From 3b3ed0179bc9672b7654374d546d6e8547ec1b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 27 Feb 2013 14:38:51 +0100 Subject: Update version numbers for R17A development --- erts/vsn.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index 4cdc20b550..8baf169d6f 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,8 +17,8 @@ # %CopyrightEnd% # -VSN = 5.10.1 -SYSTEM_VSN = R16B +VSN = 5.11 +SYSTEM_VSN = R17A # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From c69d49dce0daff8d202bac6068527a912f7072de Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Thu, 4 Apr 2013 11:22:02 +0200 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 54564 -> 54564 bytes erts/preloaded/ebin/erlang.beam | Bin 93388 -> 93388 bytes erts/preloaded/ebin/erts_internal.beam | Bin 3644 -> 3644 bytes erts/preloaded/ebin/init.beam | Bin 48672 -> 48672 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1476 -> 1476 bytes erts/preloaded/ebin/prim_file.beam | Bin 44280 -> 44280 bytes erts/preloaded/ebin/prim_inet.beam | Bin 70084 -> 70084 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23444 -> 23444 bytes erts/preloaded/ebin/zlib.beam | Bin 12812 -> 12812 bytes 9 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index aee6f631cf..2bef3b5bb7 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index ea80d47eb6..790cfcca6d 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 6b2593e427..12bc815e1d 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 7fe1685ef3..964a993817 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 57dafb9ce7..51211482ad 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 7558a713ae..95e4a5de55 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index f836473cb4..c0cde26217 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 35acb59096..b96b612df8 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index c100d1755b..68f5bb8d46 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 1f3afa8143d9aabc178b6fbcd09a693fc105065a Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 19 Apr 2013 15:29:19 +0200 Subject: Remove the "coding: utf-8" comment from all Erlang source files --- erts/emulator/internal_doc/dec.erl | 1 - erts/emulator/test/hash_SUITE.erl | 1 - 2 files changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/internal_doc/dec.erl b/erts/emulator/internal_doc/dec.erl index 255018abe0..9f7135308e 100644 --- a/erts/emulator/internal_doc/dec.erl +++ b/erts/emulator/internal_doc/dec.erl @@ -1,4 +1,3 @@ -%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl index 43c9d64af7..50667d98ed 100644 --- a/erts/emulator/test/hash_SUITE.erl +++ b/erts/emulator/test/hash_SUITE.erl @@ -1,4 +1,3 @@ -%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -- cgit v1.2.3 From afb632d2028fdc4a37e10e41e1929264ff59f52e Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Thu, 14 Mar 2013 09:53:33 +0100 Subject: Convert XML files to UTF-8, where needed --- erts/doc/src/notes.xml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 3a2c644ff7..dffc85ae36 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -1,4 +1,4 @@ - + @@ -1598,7 +1598,7 @@

Fix typo in supervisor behaviour doc (Thanks to Ricardo - Catalinas Jiménez)

+ Catalinas Jiménez)

Own Id: OTP-9924

@@ -1862,7 +1862,7 @@

Fixes module erlang doc style: option description (Thanks - to Ricardo Catalinas Jiménez)

+ to Ricardo Catalinas Jiménez)

Own Id: OTP-9697

@@ -2311,7 +2311,7 @@

Fix typos in the epmd documentation (Thanks to Holger - Weiß )

+ Weiß )

Own Id: OTP-9387

@@ -2416,7 +2416,7 @@

Fix non-existing function (erlang:disconnect/1) in - distributed reference manual (Thanks to Fabian Król)

+ distributed reference manual (Thanks to Fabian Król)

Own Id: OTP-9504

@@ -2444,7 +2444,7 @@ only separator characters (comma and space).

The same applies to epmd's -address option.(Thanks to - Holger Weiß)

+ Holger Weiß)

Own Id: OTP-9525

@@ -2588,7 +2588,7 @@

Add support for querying the number of configured and online processors on SGI systems running IRIX.(Thanks to - Holger Weiß)

+ Holger Weiß)

Own Id: OTP-9531

@@ -2698,7 +2698,7 @@ using a comma-separated list. If the loopback address is not in this list, it will be added implicitly, so that the daemon can be queried by an interactive epmd - process.(Thanks to Holger Weiß)

+ process.(Thanks to Holger Weiß)

Own Id: OTP-9213

@@ -2733,7 +2733,7 @@ value over to dbg_gen_printf(). This fixes the problem that errno had been reset to zero by the time it was used (to print the corresponding error message) in the - dbg_gen_printf() function. (Thanks to Holger Weiß)

+ dbg_gen_printf() function. (Thanks to Holger Weiß)

Own Id: OTP-9223

@@ -3119,7 +3119,7 @@ Mention that "-detached" implies "-noinput"

Clarify that specifying "-noinput" is unnecessary if the - "-detached" flag is given. (thanks to Holger Weiß)

+ "-detached" flag is given. (thanks to Holger Weiß)

Own Id: OTP-9086

@@ -4625,7 +4625,7 @@ failed to detect gcc C compilers with other command line names than gcc. `configure' now substitute GCC into the makefiles. If CC is a gcc C compiler, GCC will have the - value yes. (Thanks to Jean-Sébastien Pédron)

+ value yes. (Thanks to Jean-Sébastien Pédron)

Own Id: OTP-8373

@@ -6995,7 +6995,7 @@

IPv6 name resolving has now been fixed to use getaddrinfo() patch (thoroughly reworked) courtesy of Love - Hörnquist-Åstrand submitted by Fredrik Thulin. It also + Hörnquist-Ã…strand submitted by Fredrik Thulin. It also can use gethostname2() patch (also reworked) courtesy of Mikael Magnusson for debian submitted by Sergei Golovan.

-- cgit v1.2.3 From e5875001247e6a6ac4f474157a51a8c54f94ae49 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Thu, 14 Mar 2013 16:01:25 +0100 Subject: Convert XML files to UTF-8 --- erts/doc/src/absform.xml | 2 +- erts/doc/src/alt_dist.xml | 4 ++-- erts/doc/src/book.xml | 4 ++-- erts/doc/src/communication.xml | 2 +- erts/doc/src/crash_dump.xml | 4 ++-- erts/doc/src/driver.xml | 4 ++-- erts/doc/src/driver_entry.xml | 4 ++-- erts/doc/src/epmd.xml | 4 ++-- erts/doc/src/erl.xml | 2 +- erts/doc/src/erl_dist_protocol.xml | 2 +- erts/doc/src/erl_driver.xml | 2 +- erts/doc/src/erl_ext_dist.xml | 2 +- erts/doc/src/erl_nif.xml | 2 +- erts/doc/src/erl_prim_loader.xml | 4 ++-- erts/doc/src/erlang.xml | 2 +- erts/doc/src/erlc.xml | 4 ++-- erts/doc/src/erlsrv.xml | 4 ++-- erts/doc/src/erts_alloc.xml | 2 +- erts/doc/src/escript.xml | 2 +- erts/doc/src/fascicules.xml | 2 +- erts/doc/src/inet_cfg.xml | 4 ++-- erts/doc/src/init.xml | 4 ++-- erts/doc/src/match_spec.xml | 4 ++-- erts/doc/src/notes.xml | 2 +- erts/doc/src/notes_history.xml | 4 ++-- erts/doc/src/part.xml | 2 +- erts/doc/src/part_notes.xml | 4 ++-- erts/doc/src/part_notes_history.xml | 4 ++-- erts/doc/src/ref_man.xml | 2 +- erts/doc/src/run_erl.xml | 4 ++-- erts/doc/src/specs.xml | 2 +- erts/doc/src/start.xml | 4 ++-- erts/doc/src/start_erl.xml | 4 ++-- erts/doc/src/tty.xml | 4 ++-- erts/doc/src/werl.xml | 4 ++-- erts/doc/src/zlib.xml | 4 ++-- 36 files changed, 57 insertions(+), 57 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index e512c19e05..55aef9f8ab 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml index 038950b54d..e4912576f7 100644 --- a/erts/doc/src/alt_dist.xml +++ b/erts/doc/src/alt_dist.xml @@ -1,10 +1,10 @@ - +

- 20002011 + 20002013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/book.xml b/erts/doc/src/book.xml index 00a2888685..dc02edc5c6 100644 --- a/erts/doc/src/book.xml +++ b/erts/doc/src/book.xml @@ -1,10 +1,10 @@ - +
- 19972009 + 19972013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/communication.xml b/erts/doc/src/communication.xml index 61a9b0e716..02040c9edb 100644 --- a/erts/doc/src/communication.xml +++ b/erts/doc/src/communication.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index b3c4671c3d..7f17ef2632 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -1,10 +1,10 @@ - +
- 19992010 + 19992013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/driver.xml b/erts/doc/src/driver.xml index 52283879c7..616703fdef 100644 --- a/erts/doc/src/driver.xml +++ b/erts/doc/src/driver.xml @@ -1,10 +1,10 @@ - +
- 20012011 + 20012013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml index c37138e7b1..69a4dea466 100644 --- a/erts/doc/src/driver_entry.xml +++ b/erts/doc/src/driver_entry.xml @@ -1,10 +1,10 @@ - +
- 20012012 + 20012013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml index 3e7005410f..963d35c3c8 100644 --- a/erts/doc/src/epmd.xml +++ b/erts/doc/src/epmd.xml @@ -1,10 +1,10 @@ - +
- 19962011 + 19962013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index a68e62d051..814b904de3 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml index 84f4be208d..890293d802 100644 --- a/erts/doc/src/erl_dist_protocol.xml +++ b/erts/doc/src/erl_dist_protocol.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index efe0483b31..7c74517b2e 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml index c6849f3326..28e6f1d05e 100644 --- a/erts/doc/src/erl_ext_dist.xml +++ b/erts/doc/src/erl_ext_dist.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 864b91946a..01651aa83f 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml index 9f5b3f385b..6751deda4d 100644 --- a/erts/doc/src/erl_prim_loader.xml +++ b/erts/doc/src/erl_prim_loader.xml @@ -1,10 +1,10 @@ - +
- 19962011 + 19962013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 7dc59ea954..5befd51974 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml index 81dffe45cf..10cab344b0 100644 --- a/erts/doc/src/erlc.xml +++ b/erts/doc/src/erlc.xml @@ -1,10 +1,10 @@ - +
- 19972012 + 19972013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv.xml index 365ae21d39..71cee714a5 100644 --- a/erts/doc/src/erlsrv.xml +++ b/erts/doc/src/erlsrv.xml @@ -1,10 +1,10 @@ - +
- 19982012 + 19982013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index c73cdfd290..d0836a551d 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml index 9e2a87dde6..180447cac4 100644 --- a/erts/doc/src/escript.xml +++ b/erts/doc/src/escript.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/fascicules.xml b/erts/doc/src/fascicules.xml index cae197a516..1c371bd9c8 100644 --- a/erts/doc/src/fascicules.xml +++ b/erts/doc/src/fascicules.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/inet_cfg.xml b/erts/doc/src/inet_cfg.xml index 2a033c037c..d40bc5f9ee 100644 --- a/erts/doc/src/inet_cfg.xml +++ b/erts/doc/src/inet_cfg.xml @@ -1,10 +1,10 @@ - +
- 20042010 + 20042013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index d5c43f6e57..09b5493341 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -1,10 +1,10 @@ - +
- 19962011 + 19962013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml index bdcf9c3816..334b47d34c 100644 --- a/erts/doc/src/match_spec.xml +++ b/erts/doc/src/match_spec.xml @@ -1,10 +1,10 @@ - +
- 19992012 + 19992013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index dffc85ae36..2bea23ba88 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/notes_history.xml b/erts/doc/src/notes_history.xml index cc3b938c86..4420311912 100644 --- a/erts/doc/src/notes_history.xml +++ b/erts/doc/src/notes_history.xml @@ -1,10 +1,10 @@ - +
- 20062009 + 20062013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/part.xml b/erts/doc/src/part.xml index fa50329cad..fb720e05f3 100644 --- a/erts/doc/src/part.xml +++ b/erts/doc/src/part.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/part_notes.xml b/erts/doc/src/part_notes.xml index 4f183999e6..b5c8f0af09 100644 --- a/erts/doc/src/part_notes.xml +++ b/erts/doc/src/part_notes.xml @@ -1,10 +1,10 @@ - +
- 20042009 + 20042013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/part_notes_history.xml b/erts/doc/src/part_notes_history.xml index 1b9bcca773..a99fa4a17f 100644 --- a/erts/doc/src/part_notes_history.xml +++ b/erts/doc/src/part_notes_history.xml @@ -1,10 +1,10 @@ - +
- 20062009 + 20062013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/ref_man.xml b/erts/doc/src/ref_man.xml index e55923c344..8ed7090a61 100644 --- a/erts/doc/src/ref_man.xml +++ b/erts/doc/src/ref_man.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/run_erl.xml b/erts/doc/src/run_erl.xml index c9784299b3..684f7b1ddd 100644 --- a/erts/doc/src/run_erl.xml +++ b/erts/doc/src/run_erl.xml @@ -1,10 +1,10 @@ - +
- 19992011 + 19992013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/specs.xml b/erts/doc/src/specs.xml index e5c2f4783f..41a3984659 100644 --- a/erts/doc/src/specs.xml +++ b/erts/doc/src/specs.xml @@ -1,4 +1,4 @@ - + diff --git a/erts/doc/src/start.xml b/erts/doc/src/start.xml index 5dc33deb2a..e9a5714f93 100644 --- a/erts/doc/src/start.xml +++ b/erts/doc/src/start.xml @@ -1,10 +1,10 @@ - +
- 19992009 + 19992013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/start_erl.xml b/erts/doc/src/start_erl.xml index 92d87b095a..fe808f7737 100644 --- a/erts/doc/src/start_erl.xml +++ b/erts/doc/src/start_erl.xml @@ -1,10 +1,10 @@ - +
- 19982011 + 19982013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/tty.xml b/erts/doc/src/tty.xml index 7d662a2849..8c01365547 100644 --- a/erts/doc/src/tty.xml +++ b/erts/doc/src/tty.xml @@ -1,10 +1,10 @@ - +
- 19962010 + 19962013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/werl.xml b/erts/doc/src/werl.xml index 1494d91da8..49cc45e745 100644 --- a/erts/doc/src/werl.xml +++ b/erts/doc/src/werl.xml @@ -1,10 +1,10 @@ - +
- 19982009 + 19982013 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index 8917ab5c3a..afc597b729 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -1,10 +1,10 @@ - +
- 20052011 + 20052013 Ericsson AB. All Rights Reserved. -- cgit v1.2.3 From 40fb8d844237fdd35dff35a5d936d23ebd0191cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 19 Apr 2013 12:07:51 +0200 Subject: otp_SUITE: Add test cases to ensure that OTP conventions are obeyed Erlang source files should not have any "coding:" comment. The encoding for documentation XML files should be "utf-8" or "UTF-8". --- erts/test/otp_SUITE.erl | 64 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 2317b4f6a4..8e4a1a4b1c 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -23,7 +23,8 @@ init_per_suite/1,end_per_suite/1]). -export([undefined_functions/1,deprecated_not_in_obsolete/1, obsolete_but_not_deprecated/1,call_to_deprecated/1, - call_to_size_1/1,strong_components/1]). + call_to_size_1/1,strong_components/1, + erl_file_encoding/1,xml_file_encoding/1]). -include_lib("test_server/include/test_server.hrl"). @@ -34,7 +35,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [undefined_functions, deprecated_not_in_obsolete, obsolete_but_not_deprecated, call_to_deprecated, - call_to_size_1, strong_components]. + call_to_size_1, strong_components, + erl_file_encoding, xml_file_encoding]. groups() -> []. @@ -320,6 +322,64 @@ strong_components(Config) when is_list(Config) -> io:format("\n\nStrong components:\n\n~p\n", [Cs]), ok. +erl_file_encoding(_Config) -> + Root = code:root_dir(), + Wc = filename:join([Root,"**","*.erl"]), + ErlFiles = ordsets:subtract(ordsets:from_list(filelib:wildcard(Wc)), + release_files(Root, "*.erl")), + Fs = [F || F <- ErlFiles, + case epp:read_encoding(F) of + none -> false; + _ -> true + end], + case Fs of + [] -> + ok; + [_|_] -> + io:put_chars("Files with \"coding:\":\n"), + [io:put_chars(F) || F <- Fs], + ?t:fail() + end. + +xml_file_encoding(_Config) -> + XmlFiles = xml_files(), + Fs = [F || F <- XmlFiles, is_bad_encoding(F)], + case Fs of + [] -> + ok; + [_|_] -> + io:put_chars("Encoding should be \"utf-8\" or \"UTF-8\":\n"), + [io:put_chars(F) || F <- Fs], + ?t:fail() + end. + +xml_files() -> + Root = code:root_dir(), + AllWc = filename:join([Root,"**","*.xml"]), + AllXmlFiles = ordsets:from_list(filelib:wildcard(AllWc)), + TestsWc = filename:join([Root,"lib","*","test","**","*.xml"]), + TestXmlFiles = ordsets:from_list(filelib:wildcard(TestsWc)), + XmerlWc = filename:join([Root,"lib","xmerl","**","*.xml"]), + XmerlXmlFiles = ordsets:from_list(filelib:wildcard(XmerlWc)), + Ignore = ordsets:union([TestXmlFiles,XmerlXmlFiles, + release_files(Root, "*.xml")]), + ordsets:subtract(AllXmlFiles, Ignore). + +release_files(Root, Ext) -> + Wc = filename:join([Root,"release","**",Ext]), + filelib:wildcard(Wc). + +is_bad_encoding(File) -> + {ok,Bin} = file:read_file(File), + case Bin of + <<"> -> + false; + <<"> -> + false; + _ -> + true + end. + %%% %%% Common help functions. %%% -- cgit v1.2.3 From 26849296883df7517af7d474210c343c943b48e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 15 Mar 2013 11:59:45 +0100 Subject: erts: Use fast path for list_to_binary([Bin]) case A common case is to wrap an argument to list_to_binary in a list to ensure conversion can happen even though the argument may already be a binary. Use fast path for this case. --- erts/emulator/beam/binary.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index 33abac2f3d..c7926f18af 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -447,6 +447,7 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1) BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg) { Eterm bin; + Eterm h,t; ErlDrvSizeT size; byte* bytes; #ifdef DEBUG @@ -459,6 +460,16 @@ BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg) if (is_not_list(arg)) { goto error; } + /* check for [binary()] case */ + h = CAR(list_val(arg)); + t = CDR(list_val(arg)); + if (is_binary(h) && is_nil(t) && !( + HEADER_SUB_BIN == *(binary_val(h)) && ( + ((ErlSubBin *)binary_val(h))->bitoffs != 0 || + ((ErlSubBin *)binary_val(h))->bitsize != 0 + ))) { + return h; + } switch (erts_iolist_size(arg, &size)) { case ERTS_IOLIST_OVERFLOW: BIF_ERROR(p, SYSTEM_LIMIT); case ERTS_IOLIST_TYPE: goto error; -- cgit v1.2.3 From 21a7806986d58480367cff8d385a12f9659c7754 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 22 Apr 2013 15:28:49 +0200 Subject: Fix unmatched_returns warnings in STDLIB and Kernel --- erts/preloaded/src/init.erl | 2 +- erts/preloaded/src/prim_zip.erl | 4 ++-- erts/preloaded/src/zlib.erl | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 61d8df2428..87003d096b 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -1045,7 +1045,7 @@ start_it({eval,Bin}) -> TsR -> reverse([{dot,1} | TsR]) end, {ok,Expr} = erl_parse:parse_exprs(Ts1), - erl_eval:exprs(Expr, erl_eval:new_bindings()), + {value, _Value, _Bs} = erl_eval:exprs(Expr, erl_eval:new_bindings()), ok; start_it([_|_]=MFA) -> Ref = make_ref(), diff --git a/erts/preloaded/src/prim_zip.erl b/erts/preloaded/src/prim_zip.erl index d29f17ae56..1d5ab52a24 100644 --- a/erts/preloaded/src/prim_zip.erl +++ b/erts/preloaded/src/prim_zip.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% %% The 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,7 +89,7 @@ do_open(FilterFun, FilterAcc, F) -> {ok, PrimZip2, FilterAcc2} catch Class:Reason -> - close(PrimZip), + _ = close(PrimZip), erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace())) end. diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index 1faae1c1f4..2c9d55f50c 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -206,7 +206,7 @@ deflate(Z, Data) -> deflate(Z, Data, Flush) -> try port_command(Z, Data) of true -> - call(Z, ?DEFLATE, <<(arg_flush(Flush)):32>>), + _ = call(Z, ?DEFLATE, <<(arg_flush(Flush)):32>>), collect(Z) catch error:_Err -> @@ -252,7 +252,7 @@ inflateReset(Z) -> inflate(Z, Data) -> try port_command(Z, Data) of true -> - call(Z, ?INFLATE, <>), + _ = call(Z, ?INFLATE, <>), collect(Z) catch error:_Err -> -- cgit v1.2.3 From 88eea0e9a22099a1425553381cfb72f646cb2abf Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 25 Apr 2013 11:10:37 +0200 Subject: Fix unmatched_return warning in erl_prim_loader --- erts/preloaded/src/erl_prim_loader.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index e8ddfc4a57..81522e293a 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -1041,7 +1041,7 @@ apply_archive(PS, Fun, Acc, Archive) -> apply_archive(PS, Fun, Acc, Archive); Error -> debug(PS, {cache, {clear, Error}}), - clear_cache(Archive, {ok, PrimZip}), + ok = clear_cache(Archive, {ok, PrimZip}), apply_archive(PS, Fun, Acc, Archive) end; {Cache, FI} -> -- cgit v1.2.3 From 5bb42a3018c6d6e1bcaa382db8b873d1cef3c086 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 6 May 2013 12:26:21 +0200 Subject: Update preloaded files --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 54564 -> 54520 bytes erts/preloaded/ebin/erlang.beam | Bin 93388 -> 93348 bytes erts/preloaded/ebin/erts_internal.beam | Bin 3644 -> 3604 bytes erts/preloaded/ebin/init.beam | Bin 48672 -> 48672 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1476 -> 1436 bytes erts/preloaded/ebin/prim_file.beam | Bin 44280 -> 44232 bytes erts/preloaded/ebin/prim_inet.beam | Bin 70084 -> 70036 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23444 -> 23400 bytes erts/preloaded/ebin/zlib.beam | Bin 12812 -> 12788 bytes 9 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 059b8dd652..bfadc3c583 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index a86d1d3473..0476f75099 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index e1db170b2a..8f5a7ff6d9 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 49c041ab4c..b82047835e 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 868d51390d..4dbe6cc406 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 689d417376..a1028c5480 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 5cd013b865..fd7b20878d 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 109d2a4208..7421599da0 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 6a093c9b26..feb3c5979e 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From d9cb8383625bd06e0f2d9548b392f199e7949e24 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Mon, 11 Feb 2013 15:04:07 +0100 Subject: erts: Windows, use widechars for all paths during startup To enable unicode paths as installation dir and allow unicode in arguments --- erts/etc/common/erlexec.c | 69 +++++++------- erts/etc/win32/Install.c | 130 ++++++++++++++------------- erts/etc/win32/erl.c | 195 ++++++++++++++++++++++++---------------- erts/etc/win32/init_file.c | 32 +++---- erts/etc/win32/init_file.h | 4 +- erts/etc/win32/win_erlexec.c | 209 +++++++++++++++++++++++++++++++------------ 6 files changed, 389 insertions(+), 250 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 31d9b2e0ad..4b416adc56 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -42,7 +42,7 @@ #define DEFAULT_PROGNAME "erl" #ifdef __WIN32__ -#define INI_FILENAME "erl.ini" +#define INI_FILENAME L"erl.ini" #define INI_SECTION "erlang" #define DIRSEP "\\" #define PATHSEP ";" @@ -1373,53 +1373,49 @@ static void get_start_erl_data(char *file) } -static char *replace_filename(char *path, char *new_base) +static wchar_t *replace_filename(wchar_t *path, wchar_t *new_base) { - int plen = strlen(path); - char *res = emalloc((plen+strlen(new_base)+1)*sizeof(char)); - char *p; + int plen = wcslen(path); + wchar_t *res = (wchar_t *) emalloc((plen+wcslen(new_base)+1)*sizeof(wchar_t)); + wchar_t *p; - strcpy(res,path); - for (p = res+plen-1 ;p >= res && *p != '\\'; --p) + wcscpy(res,path); + for (p = res+plen-1 ;p >= res && *p != L'\\'; --p) ; - *(p+1) ='\0'; - strcat(res,new_base); + *(p+1) =L'\0'; + wcscat(res,new_base); return res; } -static char *path_massage(char *long_path) +static char *path_massage(wchar_t *long_path) { char *p; - - p = emalloc(MAX_PATH+1); - strcpy(p, long_path); - GetShortPathName(p, p, MAX_PATH); + int len; + len = WideCharToMultiByte(CP_UTF8, 0, long_path, -1, NULL, 0, NULL, NULL); + p = emalloc(len*sizeof(char)); + WideCharToMultiByte(CP_UTF8, 0, long_path, -1, p, len, NULL, NULL); return p; } static char *do_lookup_in_section(InitSection *inis, char *name, - char *section, char *filename, int is_path) + char *section, wchar_t *filename, int is_path) { char *p = lookup_init_entry(inis, name); if (p == NULL) { - error("Could not find key %s in section %s of file %s", + error("Could not find key %s in section %s of file %S", name,section,filename); } - if (is_path) { - return path_massage(p); - } else { - return strsave(p); - } + return strsave(p); } - +// Setup bindir, rootdir and progname as utf8 buffers static void get_parameters(int argc, char** argv) { - char *p; - char buffer[MAX_PATH]; - char *ini_filename; + wchar_t *p; + wchar_t buffer[MAX_PATH]; + wchar_t *ini_filename; HANDLE module = GetModuleHandle(NULL); /* This might look strange, but we want the erl.ini that resides in the same dir as erl.exe, not an erl.ini in our directory */ @@ -1430,34 +1426,35 @@ static void get_parameters(int argc, char** argv) error("Cannot GetModuleHandle()"); } - if (GetModuleFileName(module,buffer,MAX_PATH) == 0) { + if (GetModuleFileNameW(module,buffer,MAX_PATH) == 0) { error("Could not GetModuleFileName"); } ini_filename = replace_filename(buffer,INI_FILENAME); if ((inif = load_init_file(ini_filename)) == NULL) { + wchar_t wbindir[MAX_PATH]; + wchar_t wrootdir[MAX_PATH]; + /* Assume that the path is absolute and that it does not contain any symbolic link */ - - char buffer[MAX_PATH]; - + /* Determine bindir */ - if (GetEnvironmentVariable("ERLEXEC_DIR", buffer, MAX_PATH) == 0) { - strcpy(buffer, ini_filename); - for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '\\'; --p) + if (GetEnvironmentVariableW(L"ERLEXEC_DIR", buffer, MAX_PATH) == 0) { + wcscpy(buffer, ini_filename); + for (p = buffer+wcslen(buffer)-1; p >= buffer && *p != L'\\'; --p) ; - *p ='\0'; + *p = L'\0'; } bindir = path_massage(buffer); /* Determine rootdir */ - for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '\\'; --p) + for (p = buffer+wcslen(buffer)-1; p >= buffer && *p != L'\\'; --p) ; p--; - for (;p >= buffer && *p != '\\'; --p) + for (;p >= buffer && *p != L'\\'; --p) ; - *p ='\0'; + *p =L'\0'; rootdir = path_massage(buffer); /* Hardcoded progname */ diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c index dd02a9c111..59a5004662 100644 --- a/erts/etc/win32/Install.c +++ b/erts/etc/win32/Install.c @@ -21,58 +21,61 @@ * Dead simple installation program to set up init files etc after erlang is * copied to its destination. Also to be used after a patch is applied. */ + #include #include #include #include "init_file.h" -int main(int argc, char **argv) +int wmain(int argc, wchar_t **argv) { int silent = 0; int start_sasl = 0; - char *root = NULL; + wchar_t *root = NULL; int i; - char buffer[MAX_PATH]; - char erts_dir[MAX_PATH]; - char release_dir[MAX_PATH]; - char bin_dir[MAX_PATH]; + wchar_t buffer[MAX_PATH]; + wchar_t erts_dir[MAX_PATH]; + wchar_t release_dir[MAX_PATH]; + wchar_t bin_dir[MAX_PATH]; char *tmp; - char my_ini_filename[MAX_PATH]; + char tmp_utf8[MAX_PATH*4]; + wchar_t my_ini_filename[MAX_PATH]; InitFile *my_ini_file; InitSection *my_ini_section; - char version_string[MAX_PATH]; + char erts_version[MAX_PATH]; InitFile *ini_file; InitSection *ini_section; HANDLE module = GetModuleHandle(NULL); - char *binaries[] = { "erl.exe", "werl.exe", "erlc.exe", - "dialyzer.exe", "typer.exe", - "escript.exe", "ct_run.exe", NULL }; - char *scripts[] = { "start_clean.boot", "start_sasl.boot", NULL }; - char fromname[MAX_PATH]; - char toname[MAX_PATH]; - + wchar_t *binaries[] = { L"erl.exe", L"werl.exe", L"erlc.exe", + L"dialyzer.exe", L"typer.exe", + L"escript.exe", L"ct_run.exe", NULL }; + wchar_t *scripts[] = { L"start_clean.boot", L"start_sasl.boot", NULL }; + wchar_t fromname[MAX_PATH]; + wchar_t toname[MAX_PATH]; + size_t converted; for (i = 1; i < argc; ++i) { switch(argv[i][0]) { - case '-' : + case L'-' : switch(argv[i][1]) { - case 's' : + case L's' : silent = 1; break; default: - fprintf(stderr, "Unknown command switch %s\n", + fprintf(stderr, "Unknown command switch %S\n", argv[i]); exit(1); } break; - default: + default: { if (root != NULL) { fprintf(stderr, "Only one root directory can be specified, " - "parameter %s is illegal\n", + "parameter %S is illegal\n", argv[i]); exit(1); - } + } root = argv[i]; + } break; } } @@ -82,19 +85,19 @@ int main(int argc, char **argv) exit(1); } - if (GetModuleFileName(module,buffer,MAX_PATH) == 0) { + if (GetModuleFileNameW(module,buffer,MAX_PATH) == 0) { fprintf(stderr,"Could not GetModuleFileName()\n"); exit(1); } - i = strlen(buffer) - 1; - while ( i >= 0 && buffer[i] != '\\') { + i = wcslen(buffer) - 1; + while ( i >= 0 && buffer[i] != L'\\') { --i; } if (i < 0) { fprintf(stderr,"GetModuleFileName returned broken path\n"); exit(1); } - buffer[i] = '\0'; + buffer[i] = L'\0'; root = buffer; } @@ -122,79 +125,78 @@ int main(int argc, char **argv) start_sasl = 0; } } - sprintf(my_ini_filename,"%s\\Install.ini",root); + swprintf(my_ini_filename, MAX_PATH, L"%s\\Install.ini", root); my_ini_file = load_init_file(my_ini_filename); if (my_ini_file == NULL) { - fprintf(stderr,"Cannot open init file %s\n",my_ini_filename); + fprintf(stderr,"Cannot open init file %S\n",my_ini_filename); exit(1); } if ((my_ini_section = lookup_init_section(my_ini_file,"Install")) == NULL) { - fprintf(stderr,"No [Install] section in init file %s\n", + fprintf(stderr,"No [Install] section in init file %S\n", my_ini_filename); exit(1); } if ((tmp = lookup_init_entry(my_ini_section, "VSN")) == NULL) { - fprintf(stderr,"No key VSN in init file %s\n", + fprintf(stderr,"No key VSN in init file %S\n", my_ini_filename); exit(1); } - - strcpy(version_string,tmp); + strcpy(erts_version,tmp); - sprintf(erts_dir,"%s\\erts-%s\\bin",root,tmp); + swprintf(erts_dir,MAX_PATH,L"%s\\erts-%S\\bin",root,erts_version); if ((tmp = lookup_init_entry(my_ini_section, "SYSTEM_VSN")) == NULL) { - fprintf(stderr,"No key SYSTEM_VSN in init file %s\n", - my_ini_filename); + fprintf(stderr,"No key SYSTEM_VSN in init file %S\n", + my_ini_filename); exit(1); } - sprintf(release_dir,"%s\\releases\\%s",root,tmp); + swprintf(release_dir,MAX_PATH,L"%s\\releases\\%S",root,tmp); - sprintf(bin_dir,"%s\\bin",root); - CreateDirectory(bin_dir,NULL); + swprintf(bin_dir,MAX_PATH,L"%s\\bin",root); + CreateDirectoryW(bin_dir,NULL); free_init_file(my_ini_file); for (i = 0; binaries[i] != NULL; ++i) { - sprintf(fromname,"%s\\%s",erts_dir,binaries[i]); - sprintf(toname,"%s\\%s",bin_dir,binaries[i]); - if (GetFileAttributes(fromname) == 0xFFFFFFFF) { - fprintf(stderr,"Could not find file %s\n", + swprintf(fromname,MAX_PATH,L"%s\\%s",erts_dir,binaries[i]); + swprintf(toname,MAX_PATH,L"%s\\%s",bin_dir,binaries[i]); + if (GetFileAttributesW(fromname) == 0xFFFFFFFF) { + fprintf(stderr,"Could not find file %S\n", fromname); exit(1); } - if (!CopyFile(fromname,toname,FALSE)) { - fprintf(stderr,"Could not copy file %s to %s\n", + if (!CopyFileW(fromname,toname,FALSE)) { + fprintf(stderr,"Could not copy file %S to %S\n", fromname,toname); fprintf(stderr,"Continuing installation anyway...\n"); } } for (i = 0; scripts[i] != NULL; ++i) { - sprintf(fromname,"%s\\%s",release_dir,scripts[i]); - sprintf(toname,"%s\\%s",bin_dir,scripts[i]); - if (GetFileAttributes(fromname) == 0xFFFFFFFF) { - fprintf(stderr,"Could not find file %s\n", + swprintf(fromname,MAX_PATH,L"%s\\%s",release_dir,scripts[i]); + swprintf(toname,MAX_PATH,L"%s\\%s",bin_dir,scripts[i]); + if (GetFileAttributesW(fromname) == 0xFFFFFFFF) { + fprintf(stderr,"Could not find file %S\n", fromname); exit(1); } - if (!CopyFile(fromname,toname,FALSE)) { - fprintf(stderr,"Could not copy file %s to %s\n", + if (!CopyFileW(fromname,toname,FALSE)) { + fprintf(stderr,"Could not copy file %S to %S\n", fromname,toname); fprintf(stderr,"Cannot continue installation, bailing out.\n"); exit(1); } } if (start_sasl) { - sprintf(fromname,"%s\\start_sasl.boot",bin_dir); + swprintf(fromname,MAX_PATH,L"%s\\start_sasl.boot",bin_dir); } else { - sprintf(fromname,"%s\\start_clean.boot",bin_dir); + swprintf(fromname,MAX_PATH,L"%s\\start_clean.boot",bin_dir); } - sprintf(toname,"%s\\start.boot",bin_dir); - if (!CopyFile(fromname,toname,FALSE)) { - fprintf(stderr,"Could not copy file %s to %s\n", + swprintf(toname,MAX_PATH,L"%s\\start.boot",bin_dir); + if (!CopyFileW(fromname,toname,FALSE)) { + fprintf(stderr,"Could not copy file %S to %S\n", fromname,toname); fprintf(stderr,"Cannot continue installation, bailing out.\n"); exit(1); @@ -205,25 +207,27 @@ int main(int argc, char **argv) ini_file = create_init_file(); ini_section = create_init_section("erlang"); add_init_section(ini_file,ini_section); - add_init_entry(ini_section,"Bindir",erts_dir); + WideCharToMultiByte(CP_UTF8,0,erts_dir,-1,tmp_utf8,MAX_PATH*4,NULL,NULL); + add_init_entry(ini_section,"Bindir",tmp_utf8); add_init_entry(ini_section,"Progname","erl"); - add_init_entry(ini_section,"Rootdir",root); - sprintf(fromname,"%s\\erl.ini",erts_dir); - sprintf(toname,"%s\\erl.ini",bin_dir); + WideCharToMultiByte(CP_UTF8,0,root,-1,tmp_utf8,MAX_PATH*4,NULL,NULL); + add_init_entry(ini_section,"Rootdir",tmp_utf8); + swprintf(fromname,MAX_PATH,L"%s\\erl.ini",erts_dir); + swprintf(toname,MAX_PATH,L"%s\\erl.ini",bin_dir); if (store_init_file(ini_file,fromname) != 0) { - fprintf(stderr,"Could not create file %s\n", + fprintf(stderr,"Could not create file %S\n", fromname); fprintf(stderr,"Cannot continue installation, bailing out.\n"); exit(1); } - if (!CopyFile(fromname,toname,FALSE)) { - fprintf(stderr,"Could not copy file %s to %s\n", - fromname,toname); + if (!CopyFileW(fromname,toname,FALSE)) { + fprintf(stderr,"Could not copy file %S to %S\n", + fromname,toname); fprintf(stderr,"Cannot continue installation, bailing out.\n"); exit(1); } if (!silent) { - printf("Erlang %s installed successfully\n", version_string); + printf("Erlang %s installed successfully\n", erts_version); } return 0; } diff --git a/erts/etc/win32/erl.c b/erts/etc/win32/erl.c index d341153966..1d116bf36e 100644 --- a/erts/etc/win32/erl.c +++ b/erts/etc/win32/erl.c @@ -27,96 +27,126 @@ typedef int ErlexecFunction(int, char **, HANDLE, int); -#define INI_FILENAME "erl.ini" +#define INI_FILENAME L"erl.ini" #define INI_SECTION "erlang" -#define ERLEXEC_BASENAME "erlexec.dll" +#define ERLEXEC_BASENAME L"erlexec.dll" static void get_parameters(void); static void error(char* format, ...); -static char *erlexec_name; -static char *erlexec_dir; +static wchar_t *erlexec_name; +static wchar_t *erlexec_dir; #ifdef WIN32_WERL #define WERL 1 -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, - PSTR szCmdLine, int iCmdShow) +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + PWSTR szCmdLine, int iCmdShow) { int argc = __argc; - char **argv = __argv; + wchar_t **argv = __wargv; #else #define WERL 0 -int main(int argc, char **argv) +int wmain(int argc, wchar_t **argv) { #endif HANDLE erlexec_handle; /* Instance */ ErlexecFunction *win_erlexec; - char *path = malloc(100); - char *npath; + wchar_t *path = malloc(100*sizeof(wchar_t)); + wchar_t *npath; int pathlen; + char ** utf8argv; + int i, len; get_parameters(); - if ((pathlen = GetEnvironmentVariable("PATH",path,100)) == 0) { + if ((pathlen = GetEnvironmentVariableW(L"PATH",path,100)) == 0) { error("No PATH variable (!)"); } else if (pathlen > 100) { - path = realloc(path,pathlen); - GetEnvironmentVariable("PATH",path,pathlen); + path = realloc(path,pathlen*sizeof(wchar_t)); + GetEnvironmentVariableW(L"PATH",path,pathlen); } - npath = malloc(strlen(path) + strlen(erlexec_dir) + 2); - sprintf(npath,"%s;%s",erlexec_dir,path); - SetEnvironmentVariable("PATH",npath); + pathlen = (wcslen(path) + wcslen(erlexec_dir) + 2); + npath = (wchar_t *) malloc(pathlen*sizeof(wchar_t)); + swprintf(npath,pathlen,L"%s;%s",erlexec_dir,path); + SetEnvironmentVariableW(L"PATH",npath); - if ((erlexec_handle = LoadLibrary(erlexec_name)) == NULL) { - error("Could not load module %s.",erlexec_name); + if ((erlexec_handle = LoadLibraryW(erlexec_name)) == NULL) { + error("Could not load module %S.",erlexec_name); } if ((win_erlexec = (ErlexecFunction *) GetProcAddress(erlexec_handle,"win_erlexec")) == NULL) { - error("Could not find entry point \"win_erlexec\" in %s.", erlexec_name); + error("Could not find entry point \"win_erlexec\" in %S.", erlexec_name); } - return (*win_erlexec)(argc,argv,erlexec_handle,WERL); + /* Convert argv to utf8 */ + utf8argv = malloc((argc+1) * sizeof(char*)); + for (i=0; i= res && *p != '\\'; --p) + wcscpy(res,path); + for (p = res+plen-1 ;p >= res && *p != L'\\'; --p) ; - *(p+1) ='\0'; - strcat(res,new_base); + *(p+1) =L'\0'; + wcscat(res,new_base); return res; } static char *do_lookup_in_section(InitSection *inis, char *name, - char *section, char *filename) + char *section, wchar_t *filename) { char *p = lookup_init_entry(inis, name); if (p == NULL) { - error("Could not find key %s in section %s of file %s", + error("Could not find key %s in section %s of file %S", name,section,filename); } - return _strdup(p); + return p; } -static void copy_latest_vsn(char *latest_vsn, char *next_vsn) +static void copy_latest_vsn(wchar_t *latest_vsn, wchar_t *next_vsn) { /* Copy */ - char *lp; - char *np; + wchar_t *lp; + wchar_t *np; /* Find vsn */ - for (lp = next_vsn+strlen(next_vsn)-1 ;lp >= next_vsn && *lp != '\\'; --lp) + for (lp = next_vsn+wcslen(next_vsn)-1 ;lp >= next_vsn && *lp != L'\\'; --lp) ; /* lp =+ length("erts-"); */ - for (np = next_vsn+strlen(next_vsn)-1 ;np >= next_vsn && *np != '\\'; --np) + for (np = next_vsn+wcslen(next_vsn)-1 ;np >= next_vsn && *np != L'\\'; --np) ; /* np =+ length("erts-"); */ @@ -124,95 +154,95 @@ static void copy_latest_vsn(char *latest_vsn, char *next_vsn) if (*lp == *np) { continue; } - if (*np == '.' || *np == '\0' || *np <= *lp) { + if (*np == L'.' || *np == L'\0' || *np <= *lp) { /* */ return; } - if (*lp == '.' || *lp == '\0') { - strcpy(latest_vsn, next_vsn); + if (*lp == L'.' || *lp == L'\0') { + wcscpy(latest_vsn, next_vsn); return; } } return; } -static char *find_erlexec_dir2(char *install_dir) +static wchar_t *find_erlexec_dir2(wchar_t *install_dir) { /* List install dir and look for latest erts-vsn */ HANDLE dir_handle; /* Handle to directory. */ - char wildcard[MAX_PATH]; /* Wildcard to search for. */ - WIN32_FIND_DATA find_data; /* Data found by FindFirstFile() or FindNext(). */ - char latest_vsn[MAX_PATH]; + wchar_t wildcard[MAX_PATH]; /* Wildcard to search for. */ + WIN32_FIND_DATAW find_data; /* Data found by FindFirstFile() or FindNext(). */ + wchar_t latest_vsn[MAX_PATH]; /* Setup wildcard */ - int length = strlen(install_dir); - char *p; + int length = wcslen(install_dir); + wchar_t *p; if (length+3 >= MAX_PATH) { error("Cannot find erlexec.exe"); } - strcpy(wildcard, install_dir); + wcscpy(wildcard, install_dir); p = wildcard+length-1; - if (*p != '/' && *p != '\\') - *++p = '\\'; - strcpy(++p, "erts-*"); + if (*p != L'/' && *p != L'\\') + *++p = L'\\'; + wcscpy(++p, L"erts-*"); /* Find first dir */ - dir_handle = FindFirstFile(wildcard, &find_data); + dir_handle = FindFirstFileW(wildcard, &find_data); if (dir_handle == INVALID_HANDLE_VALUE) { /* No erts-vsn found*/ return NULL; } - strcpy(latest_vsn, find_data.cFileName); + wcscpy(latest_vsn, find_data.cFileName); /* Find the rest */ - while(FindNextFile(dir_handle, &find_data)) { + while(FindNextFileW(dir_handle, &find_data)) { copy_latest_vsn(latest_vsn, find_data.cFileName); } FindClose(dir_handle); - p = malloc((strlen(install_dir)+1+strlen(latest_vsn)+4+1)*sizeof(char)); + p = (wchar_t *) malloc((wcslen(install_dir)+1+wcslen(latest_vsn)+4+1)*sizeof(wchar_t)); - strcpy(p,install_dir); - strcat(p,"\\"); - strcat(p,latest_vsn); - strcat(p,"\\bin"); + wcscpy(p,install_dir); + wcscat(p,L"\\"); + wcscat(p,latest_vsn); + wcscat(p,L"\\bin"); return p; } -static char *find_erlexec_dir(char *erlpath) +static wchar_t *find_erlexec_dir(wchar_t *erlpath) { /* Assume that the path to erl is absolute and * that it is not a symbolic link*/ - char *dir =_strdup(erlpath); - char *p; - char *p2; + wchar_t *dir =_wcsdup(erlpath); + wchar_t *p; + wchar_t *p2; /* Chop of base name*/ - for (p = dir+strlen(dir)-1 ;p >= dir && *p != '\\'; --p) + for (p = dir+wcslen(dir)-1 ;p >= dir && *p != L'\\'; --p) ; - *p ='\0'; + *p =L'\0'; p--; /* Check if dir path is like ...\install_dir\erts-vsn\bin */ - for (;p >= dir && *p != '\\'; --p) + for (;p >= dir && *p != L'\\'; --p) ; p--; for (p2 = p;p2 >= dir && *p2 != '\\'; --p2) ; p2++; - if (strncmp(p2, "erts-", strlen("erts-")) == 0) { - p = _strdup(dir); + if (wcsncmp(p2, L"erts-", wcslen(L"erts-")) == 0) { + p = _wcsdup(dir); free(dir); return p; } /* Assume that dir path is like ...\install_dir\bin */ - *++p ='\0'; /* chop off bin dir */ + *++p =L'\0'; /* chop off bin dir */ p = find_erlexec_dir2(dir); free(dir); @@ -225,18 +255,20 @@ static char *find_erlexec_dir(char *erlpath) static void get_parameters(void) { - char buffer[MAX_PATH]; - char *ini_filename; + wchar_t buffer[MAX_PATH]; + wchar_t *ini_filename; HANDLE module = GetModuleHandle(NULL); InitFile *inif; InitSection *inis; - char *bindir; + char *utf8dir; + int len; + if (module = NULL) { error("Cannot GetModuleHandle()"); } - if (GetModuleFileName(module,buffer,MAX_PATH) == 0) { + if (GetModuleFileNameW(module,buffer,MAX_PATH) == 0) { error("Could not GetModuleFileName"); } @@ -244,21 +276,28 @@ static void get_parameters(void) if ((inif = load_init_file(ini_filename)) == NULL) { erlexec_dir = find_erlexec_dir(ini_filename); - SetEnvironmentVariable("ERLEXEC_DIR", erlexec_dir); + SetEnvironmentVariableW(L"ERLEXEC_DIR", erlexec_dir); } else { if ((inis = lookup_init_section(inif,INI_SECTION)) == NULL) { - error("Could not find section %s in init file %s", + error("Could not find section %s in init file %S", INI_SECTION, ini_filename); } - erlexec_dir = do_lookup_in_section(inis, "Bindir", INI_SECTION, ini_filename); - free_init_file(inif); + utf8dir = do_lookup_in_section(inis, "Bindir", INI_SECTION, ini_filename); + len = MultiByteToWideChar(CP_UTF8, 0, utf8dir, -1, NULL, 0); + erlexec_dir = malloc(len*sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, utf8dir, -1, erlexec_dir, len); + if(len == 0) { + error("Bindir is not a valid utf8 '%s' in init file %S", + utf8dir, ini_filename); + } + free_init_file(inif); } - erlexec_name = malloc(strlen(erlexec_dir) + strlen(ERLEXEC_BASENAME) + 2); - strcpy(erlexec_name,erlexec_dir); - strcat(erlexec_name, "\\" ERLEXEC_BASENAME); + erlexec_name = malloc((wcslen(erlexec_dir) + wcslen(ERLEXEC_BASENAME) + 2)*sizeof(wchar_t)); + wcscpy(erlexec_name,erlexec_dir); + wcscat(erlexec_name, L"\\" ERLEXEC_BASENAME); free(ini_filename); } diff --git a/erts/etc/win32/init_file.c b/erts/etc/win32/init_file.c index 52f6c41d1d..d452afa65c 100644 --- a/erts/etc/win32/init_file.c +++ b/erts/etc/win32/init_file.c @@ -173,7 +173,7 @@ static void digout_key_value(char *line, char **key, char **value) } } -InitFile *load_init_file(char *filename) +InitFile *load_init_file(wchar_t *filename) { HANDLE infile; InitFile *inif; @@ -187,13 +187,13 @@ InitFile *load_init_file(char *filename) int i; - if ( (infile = CreateFile(filename, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL)) == INVALID_HANDLE_VALUE) { + if ( (infile = CreateFileW(filename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL)) == INVALID_HANDLE_VALUE) { return NULL; } @@ -280,7 +280,7 @@ InitFile *load_init_file(char *filename) return inif; } -int store_init_file(InitFile *inif, char *filename) +int store_init_file(InitFile *inif, wchar_t *filename) { char *buff; int size = 10; @@ -297,13 +297,13 @@ int store_init_file(InitFile *inif, char *filename) buff[num++] = (Char); \ } while(0) - if ( (outfile = CreateFile(filename, - GENERIC_WRITE, - FILE_SHARE_WRITE, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL)) == INVALID_HANDLE_VALUE) { + if ( (outfile = CreateFileW(filename, + GENERIC_WRITE, + FILE_SHARE_WRITE, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL)) == INVALID_HANDLE_VALUE) { return INIT_FILE_OPEN_ERROR; } buff = ALLOC(size); diff --git a/erts/etc/win32/init_file.h b/erts/etc/win32/init_file.h index 48d2d2df62..ae40e88520 100644 --- a/erts/etc/win32/init_file.h +++ b/erts/etc/win32/init_file.h @@ -36,10 +36,10 @@ typedef struct { } InitFile; /* Load a file structure from a disk file */ -InitFile *load_init_file(char *filename); +InitFile *load_init_file(wchar_t *filename); /* Stores a file structure into a disk file */ -int store_init_file(InitFile *inif, char *filename); +int store_init_file(InitFile *inif, wchar_t *filename); /* Create an empty file structure */ InitFile *create_init_file(void); diff --git a/erts/etc/win32/win_erlexec.c b/erts/etc/win32/win_erlexec.c index 11cc6a30f7..c622e6eeee 100644 --- a/erts/etc/win32/win_erlexec.c +++ b/erts/etc/win32/win_erlexec.c @@ -62,12 +62,18 @@ static SysGetKeyFunction *sys_get_key_p; static ErlStartFunction *erl_start_p; static SysPrimitiveInitFunction *sys_primitive_init_p; -static HMODULE load_win_beam_dll(char *name) +/* + * To enable debugging of argument processing etc + * #define ARGS_HARDDEBUG 1 + * #define HARDDEBUG 1 + */ + +static HMODULE load_win_beam_dll(wchar_t *name) { HMODULE beam_module; - beam_module=LoadLibrary(name); + beam_module=LoadLibraryW(name); if (beam_module == INVALID_HANDLE_VALUE || beam_module == NULL) { - error("Unable to load emulator DLL\n(%s)",name); + error("Unable to load emulator DLL\n(%S)",name); return NULL; } sys_get_key_p = (SysGetKeyFunction *) @@ -83,9 +89,21 @@ static HMODULE load_win_beam_dll(char *name) #define DLL_ENV "ERL_EMULATOR_DLL" static void -set_env(char *key, char *value) +set_env(char *key, char *value) /* Both in UTF-8 encoding */ { - if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value)) + wchar_t *wkey=NULL; + wchar_t *wvalue=NULL; + int keylen; + int valuelen; + + + keylen = MultiByteToWideChar(CP_UTF8, 0, key, -1, NULL, 0); + valuelen = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0); + wkey = malloc(keylen*sizeof(wchar_t)); + wvalue = malloc(valuelen*sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, key, -1, wkey, keylen); + MultiByteToWideChar(CP_UTF8, 0, value, -1, wvalue, valuelen); + if (!SetEnvironmentVariableW( wkey, wvalue)) error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value); } @@ -121,55 +139,97 @@ free_env_val(char *value) int -start_win_emulator(char* emu, char *start_prog, char** argv, int start_detached) +start_win_emulator(char* utf8emu, char *utf8start_prog, char** utf8argv, int start_detached) { - int result; + int len; + int argc = 0; windowed = 1; + while (utf8argv[argc] != NULL) { + ++argc; + } + if (start_detached) { - char *buff; + wchar_t *start_prog=NULL; + int result; + int i; + wchar_t **argv; close(0); close(1); close(2); set_env("ERL_CONSOLE_MODE", "detached"); - set_env(DLL_ENV, emu); + set_env(DLL_ENV, utf8emu); + + utf8argv[0] = utf8start_prog; + utf8argv = fnuttify_argv(utf8argv); - argv[0] = start_prog; - argv = fnuttify_argv(argv); - result = spawnv(_P_DETACH, start_prog, argv); - free_fnuttified(argv); + len = MultiByteToWideChar(CP_UTF8, 0, utf8start_prog, -1, NULL, 0); + start_prog = malloc(len*sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, utf8start_prog, -1, start_prog, len); + + /* Convert utf8argv to multibyte argv */ + argv = malloc((argc+1) * sizeof(wchar_t*)); + for (i=0; i Date: Thu, 14 Feb 2013 14:34:29 +0100 Subject: erts: Fix windows widestring args and paths in tools Fix erlc, escript, dialyzer, typer, ct_run, heart and epmd should all be using widestrings on windows --- erts/etc/common/ct_run.c | 82 ++++++++++----- erts/etc/common/dialyzer.c | 104 ++++++++++++------- erts/etc/common/erlc.c | 117 ++++++++++++--------- erts/etc/common/erlexec.c | 7 +- erts/etc/common/escript.c | 247 +++++++++++++++++++++++++++++++-------------- erts/etc/common/heart.c | 117 +++++++++++---------- erts/etc/common/typer.c | 97 +++++++++++------- 7 files changed, 497 insertions(+), 274 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/ct_run.c b/erts/etc/common/ct_run.c index 853785dcd1..bb59b93998 100644 --- a/erts/etc/common/ct_run.c +++ b/erts/etc/common/ct_run.c @@ -117,9 +117,14 @@ char *strerror(int errnum) } #endif /* !HAVE_STRERROR */ -int -main(int argc, char** argv) +#ifdef __WIN32__ +int wmain(int argc, wchar_t **wcargv) +{ + char** argv; +#else +int main(int argc, char** argv) { +#endif int eargv_size; int eargc_base; /* How many arguments in the base of eargv. */ char* emulator; @@ -129,7 +134,21 @@ main(int argc, char** argv) int dist_mode; int cnt; int erl_args; - char** argv0 = argv; + char** argv0; + +#ifdef __WIN32__ + int i; + int len; + /* Convert argv to utf8 */ + argv = malloc((argc+1) * sizeof(char*)); + for (i=0; i= sbuf; s--) { if (IS_DIRSEP(*s)) { strcpy(s+1, ERL_NAME); -#ifdef __WIN32__ - if (_access(sbuf, 0) != -1) { + if(file_exists(sbuf)) return strsave(sbuf); - } -#else - if (access(sbuf, 1) != -1) { - return strsave(sbuf); - } -#endif break; } } diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c index b8a7a2bf03..09afb25182 100644 --- a/erts/etc/common/dialyzer.c +++ b/erts/etc/common/dialyzer.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2012. All Rights Reserved. + * Copyright Ericsson AB 2006-2013. All Rights Reserved. * * The 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,20 +104,31 @@ get_env(char *key) { #ifdef __WIN32__ DWORD size = 32; - char *value = NULL; + char *value=NULL; + wchar_t *wcvalue = NULL; + wchar_t wckey[256]; + int len; + + MultiByteToWideChar(CP_UTF8, 0, key, -1, wckey, 256); + while (1) { DWORD nsz; - if (value) - free(value); - value = emalloc(size); + if (wcvalue) + free(wcvalue); + wcvalue = (wchar_t*) emalloc(size*sizeof(wchar_t)); SetLastError(0); - nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size); + nsz = GetEnvironmentVariableW(wckey, wcvalue, size); if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { - free(value); + free(wcvalue); return NULL; } - if (nsz <= size) + if (nsz <= size) { + len = WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, NULL, 0, NULL, NULL); + value = emalloc(len*sizeof(char)); + WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, value, len, NULL, NULL); + free(wcvalue); return value; + } size = nsz; } #else @@ -134,9 +145,14 @@ free_env_val(char *value) #endif } -int -main(int argc, char** argv) +#ifdef __WIN32__ +int wmain(int argc, wchar_t **wcargv) +{ + char** argv; +#else +int main(int argc, char** argv) { +#endif int eargv_size; int eargc_base; /* How many arguments in the base of eargv. */ char* emulator; @@ -144,6 +160,18 @@ main(int argc, char** argv) int i; int need_shell = 0; +#ifdef __WIN32__ + int len; + /* Convert argv to utf8 */ + argv = malloc((argc+1) * sizeof(char*)); + for (i=0; i= sbuf; s--) { if (IS_DIRSEP(*s)) { strcpy(s+1, ERL_NAME); -#ifdef __WIN32__ - if (_access(sbuf, 0) != -1) { + if(file_exists(sbuf)) return strsave(sbuf); - } -#else - if (access(sbuf, 1) != -1) { - return strsave(sbuf); - } -#endif break; } } diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c index c2d7c7c76d..add65b87ca 100644 --- a/erts/etc/common/erlc.c +++ b/erts/etc/common/erlc.c @@ -114,20 +114,31 @@ get_env(char *key) { #ifdef __WIN32__ DWORD size = 32; - char *value = NULL; + char *value=NULL; + wchar_t *wcvalue = NULL; + wchar_t wckey[256]; + int len; + + MultiByteToWideChar(CP_UTF8, 0, key, -1, wckey, 256); + while (1) { DWORD nsz; - if (value) - free(value); - value = emalloc(size); + if (wcvalue) + free(wcvalue); + wcvalue = (wchar_t *) emalloc(size*sizeof(wchar_t)); SetLastError(0); - nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size); + nsz = GetEnvironmentVariableW(wckey, wcvalue, size); if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { - free(value); + free(wcvalue); return NULL; } - if (nsz <= size) + if (nsz <= size) { + len = WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, NULL, 0, NULL, NULL); + value = emalloc(len*sizeof(char)); + WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, value, len, NULL, NULL); + free(wcvalue); return value; + } size = nsz; } #else @@ -144,16 +155,32 @@ free_env_val(char *value) #endif } - -int -main(int argc, char** argv) +#ifdef __WIN32__ +int wmain(int argc, wchar_t **wcargv) +{ + char** argv; +#else +int main(int argc, char** argv) { - char cwd[MAXPATHLEN]; /* Current working directory. */ +#endif int eargv_size; int eargc_base; /* How many arguments in the base of eargv. */ char* emulator; char *env; +#ifdef __WIN32__ + int i; + int len; + /* Convert argv to utf8 */ + argv = malloc((argc+1) * sizeof(char*)); + for (i=0; i= sbuf; s--) { if (IS_DIRSEP(*s)) { strcpy(s+1, ERL_NAME); -#ifdef __WIN32__ - if (_access(sbuf, 0) != -1) { + if(file_exists(sbuf)) return strsave(sbuf); - } -#else - if (access(sbuf, 1) != -1) { - return strsave(sbuf); - } -#endif break; } } diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 4b416adc56..e4a8c4ff45 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -1175,11 +1175,14 @@ start_epmd(char *epmd) strcat(epmd, arg1); } { - STARTUPINFO start; + wchar_t wcepmd[MAXPATHLEN+100]; + STARTUPINFOW start; PROCESS_INFORMATION pi; memset(&start, 0, sizeof (start)); start.cb = sizeof (start); - if (!CreateProcess(NULL, epmd, NULL, NULL, FALSE, + MultiByteToWideChar(CP_UTF8, 0, epmd, -1, wcepmd, MAXPATHLEN+100); + + if (!CreateProcessW(NULL, wcepmd, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS, NULL, NULL, &start, &pi)) result = -1; diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c index 118bc6ef90..c92fedee4b 100644 --- a/erts/etc/common/escript.c +++ b/erts/etc/common/escript.c @@ -45,7 +45,8 @@ static int eargc; /* Number of arguments in eargv. */ # define QUOTE(s) possibly_quote(s) # define IS_DIRSEP(c) ((c) == '/' || (c) == '\\') # define DIRSEPSTR "\\" -# define PATHSEPSTR ";" +# define LDIRSEPSTR L"\\" +# define LPATHSEPSTR L";" # define PMAX MAX_PATH # define ERL_NAME "erl.exe" #else @@ -112,20 +113,31 @@ get_env(char *key) { #ifdef __WIN32__ DWORD size = 32; - char *value = NULL; + char *value=NULL; + wchar_t *wcvalue = NULL; + wchar_t wckey[256]; + int len; + + MultiByteToWideChar(CP_UTF8, 0, key, -1, wckey, 256); + while (1) { DWORD nsz; - if (value) - efree(value); - value = emalloc(size); + if (wcvalue) + efree(wcvalue); + wcvalue = (wchar_t *) emalloc(size*sizeof(wchar_t)); SetLastError(0); - nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size); + nsz = GetEnvironmentVariableW(wckey, wcvalue, size); if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { - efree(value); + efree(wcvalue); return NULL; } - if (nsz <= size) + if (nsz <= size) { + len = WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, NULL, 0, NULL, NULL); + value = emalloc(len*sizeof(char)); + WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, value, len, NULL, NULL); + efree(wcvalue); return value; + } size = nsz; } #else @@ -145,6 +157,88 @@ free_env_val(char *value) * Find absolute path to this program */ +#ifdef __WIN32__ +static char * +find_prog(char *origpath) +{ + wchar_t relpath[PMAX]; + wchar_t abspath[PMAX]; + + if (strlen(origpath) >= PMAX) + error("Path too long"); + + MultiByteToWideChar(CP_UTF8, 0, origpath, -1, relpath, PMAX); + + if (wcsstr(relpath, LDIRSEPSTR) == NULL) { + /* Just a base name */ + int sz; + wchar_t *envpath; + sz = GetEnvironmentVariableW(L"PATH", NULL, 0); + if (sz) { + /* Try to find the executable in the path */ + wchar_t dir[PMAX]; + wchar_t *beg; + wchar_t *end; + + HANDLE dir_handle; /* Handle to directory. */ + wchar_t wildcard[PMAX]; /* Wildcard to search for. */ + WIN32_FIND_DATAW find_data; /* Data found by FindFirstFile() or FindNext(). */ + + BOOL look_for_sep = TRUE; + + envpath = (wchar_t *) emalloc(sz * sizeof(wchar_t*)); + GetEnvironmentVariableW(L"PATH", envpath, sz); + beg = envpath; + + while (look_for_sep) { + end = wcsstr(beg, LPATHSEPSTR); + if (end != NULL) { + sz = end - beg; + } else { + sz = wcslen(beg); + look_for_sep = FALSE; + } + if (sz >= PMAX) { + beg = end + 1; + continue; + } + wcsncpy(dir, beg, sz); + dir[sz] = L'\0'; + beg = end + 1; + + swprintf(wildcard, PMAX, L"%s" LDIRSEPSTR L"%s", + dir, relpath /* basename */); + dir_handle = FindFirstFileW(wildcard, &find_data); + if (dir_handle == INVALID_HANDLE_VALUE) { + /* Try next directory in path */ + continue; + } else { + /* Wow we found the executable. */ + wcscpy(relpath, wildcard); + FindClose(dir_handle); + look_for_sep = FALSE; + break; + } + } + efree(envpath); + } + } + + { + DWORD size; + wchar_t *absrest; + size = GetFullPathNameW(relpath, PMAX, abspath, &absrest); + if ((size == 0) || (size > PMAX)) { + /* Cannot determine absolute path to escript. Try the origin. */ + return strsave(origpath); + } else { + char utf8abs[PMAX]; + WideCharToMultiByte(CP_UTF8, 0, abspath, -1, utf8abs, PMAX, NULL, NULL); + return strsave(utf8abs); + } + } +} +#else static char * find_prog(char *origpath) { @@ -168,14 +262,8 @@ find_prog(char *origpath) char *end; int sz; -#ifdef __WIN32__ - HANDLE dir_handle; /* Handle to directory. */ - char wildcard[PMAX]; /* Wildcard to search for. */ - WIN32_FIND_DATA find_data; /* Data found by FindFirstFile() or FindNext(). */ -#else DIR *dp; /* Pointer to directory structure. */ struct dirent* dirp; /* Pointer to directory entry. */ -#endif /* __WIN32__ */ BOOL look_for_sep = TRUE; @@ -195,21 +283,6 @@ find_prog(char *origpath) dir[sz] = '\0'; beg = end + 1; -#ifdef __WIN32__ - erts_snprintf(wildcard, sizeof(wildcard), "%s" DIRSEPSTR "%s", - dir, relpath /* basename */); - dir_handle = FindFirstFile(wildcard, &find_data); - if (dir_handle == INVALID_HANDLE_VALUE) { - /* Try next directory in path */ - continue; - } else { - /* Wow we found the executable. */ - strcpy(relpath, wildcard); - FindClose(dir_handle); - look_for_sep = FALSE; - break; - } -#else dp = opendir(dir); if (dp != NULL) { while (TRUE) { @@ -230,21 +303,12 @@ find_prog(char *origpath) } } } -#endif /* __WIN32__ */ } } } { -#ifdef __WIN32__ - DWORD size; - char *absrest; - size = GetFullPathName(relpath, PMAX, abspath, &absrest); - if ((size == 0) || (size > PMAX)) { - -#else if (!realpath(relpath, abspath)) { -#endif /* __WIN32__ */ /* Cannot determine absolute path to escript. Try the origin. */ return strsave(origpath); } else { @@ -252,12 +316,21 @@ find_prog(char *origpath) } } } +#endif static void append_shebang_args(char* scriptname) { /* Open script file */ - FILE* fd = fopen (scriptname,"r"); + FILE* fd; +#ifdef __WIN32__ + wchar_t wcscriptname[PMAX]; + + MultiByteToWideChar(CP_UTF8, 0, scriptname, -1, wcscriptname, PMAX); + fd = _wfopen(wcscriptname, L"r"); +#else + fd = fopen (scriptname,"r"); +#endif if (fd != NULL) { /* Read first line in script file */ @@ -321,9 +394,15 @@ append_shebang_args(char* scriptname) } } +#ifdef __WIN32__ +int wmain(int argc, wchar_t **wcargv) +{ + char** argv; +#else int main(int argc, char** argv) { +#endif int eargv_size; int eargc_base; /* How many arguments in the base of eargv. */ char* emulator; @@ -333,6 +412,19 @@ main(int argc, char** argv) char scriptname[PMAX]; char** last_opt; char** first_opt; + +#ifdef __WIN32__ + int i; + int len; + /* Convert argv to utf8 */ + argv = malloc((argc+1) * sizeof(char*)); + for (i=0; i= sbuf; s--) { if (IS_DIRSEP(*s)) { strcpy(s+1, ERL_NAME); -#ifdef __WIN32__ - if (_access(sbuf, 0) != -1) { + if(file_exists(sbuf)) return strsave(sbuf); - } -#else - if (access(sbuf, 1) != -1) { + strcpy(s+1, "bin" DIRSEPSTR ERL_NAME); + if(file_exists(sbuf)) return strsave(sbuf); - } -#endif break; } } diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index 81d797dc7e..2830641802 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2012. All Rights Reserved. + * Copyright Ericsson AB 1996-2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -201,7 +201,6 @@ static BOOL do_shutdown(int); static void print_last_error(void); static HANDLE start_reader_thread(void); static DWORD WINAPI reader(LPVOID); -static int test_win95(void); #define read _read #define write _write #endif @@ -239,24 +238,39 @@ get_env(char *key) { #ifdef __WIN32__ DWORD size = 32; - char *value = NULL; + char *value=NULL; + wchar_t *wcvalue = NULL; + wchar_t wckey[256]; + int len; + + MultiByteToWideChar(CP_UTF8, 0, key, -1, wckey, 256); + while (1) { DWORD nsz; - if (value) - free(value); - value = malloc(size); - if (!value) { + if (wcvalue) + free(wcvalue); + wcvalue = malloc(size*sizeof(wchar_t)); + if (!wcvalue) { print_error("Failed to allocate memory. Terminating..."); exit(1); } SetLastError(0); - nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size); + nsz = GetEnvironmentVariableW(wckey, wcvalue, size); if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { - free(value); + free(wcvalue); return NULL; } - if (nsz <= size) + if (nsz <= size) { + len = WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, NULL, 0, NULL, NULL); + value = malloc(len*sizeof(char)); + if (!value) { + print_error("Failed to allocate memory. Terminating..."); + exit(1); + } + WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, value, len, NULL, NULL); + free(wcvalue); return value; + } size = nsz; } #else @@ -564,13 +578,22 @@ void win_system(char *command) char *comspec; char * cmdbuff; char * extra = " /C "; + wchar_t *wccmdbuff; char *env; - STARTUPINFO start; + STARTUPINFOW start; SECURITY_ATTRIBUTES attr; PROCESS_INFORMATION info; + int len; - if (!debug_on || test_win95()) { - system(command); + if (!debug_on) { + len = MultiByteToWideChar(CP_UTF8, 0, command, -1, NULL, 0); + wccmdbuff = malloc(len*sizeof(wchar_t)); + if (!wccmdbuff) { + print_error("Failed to allocate memory. Terminating..."); + exit(1); + } + MultiByteToWideChar(CP_UTF8, 0, command, -1, wccmdbuff, len); + _wsystem(wccmdbuff); return; } comspec = env = get_env("COMSPEC"); @@ -602,20 +625,29 @@ void win_system(char *command) fflush(stderr); - if (!CreateProcess(NULL, - cmdbuff, - &attr, - NULL, - TRUE, - 0, - NULL, - NULL, - &start, - &info)) { + len = MultiByteToWideChar(CP_UTF8, 0, cmdbuff, -1, NULL, 0); + wccmdbuff = malloc(len*sizeof(wchar_t)); + if (!wccmdbuff) { + print_error("Failed to allocate memory. Terminating..."); + exit(1); + } + MultiByteToWideChar(CP_UTF8, 0, cmdbuff, -1, wccmdbuff, len); + + if (!CreateProcessW(NULL, + wccmdbuff, + &attr, + NULL, + TRUE, + 0, + NULL, + NULL, + &start, + &info)) { debugf("Could not create process for the command %s.\r\n", cmdbuff); } WaitForSingleObject(info.hProcess,INFINITE); free(cmdbuff); + free(wccmdbuff); } #endif /* defined(__WIN32__) */ @@ -966,16 +998,6 @@ void print_last_error() { LocalFree( lpMsgBuf ); } -static int test_win95(void) -{ - OSVERSIONINFO osinfo; - osinfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); - GetVersionEx(&osinfo); - if (osinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) - return 1; - else - return 0; -} static BOOL enable_privilege() { HANDLE ProcessHandle; @@ -993,27 +1015,18 @@ static BOOL enable_privilege() { } static BOOL do_shutdown(int really_shutdown) { - if (test_win95()) { - if (ExitWindowsEx(EWX_REBOOT,0)) { - return TRUE; - } else { - print_last_error(); - return FALSE; - } - } else { - enable_privilege(); - if (really_shutdown) { - if (InitiateSystemShutdown(NULL,"shutdown by HEART",10,TRUE,TRUE)) - return TRUE; - } else if (InitiateSystemShutdown(NULL, - "shutdown by HEART\n" - "will be interrupted", - 30,TRUE,TRUE)) { - AbortSystemShutdown(NULL); + enable_privilege(); + if (really_shutdown) { + if (InitiateSystemShutdown(NULL,"shutdown by HEART",10,TRUE,TRUE)) return TRUE; - } - return FALSE; + } else if (InitiateSystemShutdown(NULL, + "shutdown by HEART\n" + "will be interrupted", + 30,TRUE,TRUE)) { + AbortSystemShutdown(NULL); + return TRUE; } + return FALSE; } DWORD WINAPI reader(LPVOID lpvParam) { diff --git a/erts/etc/common/typer.c b/erts/etc/common/typer.c index c95959d52d..b45867f845 100644 --- a/erts/etc/common/typer.c +++ b/erts/etc/common/typer.c @@ -99,14 +99,33 @@ char *strerror(int errnum) } #endif /* !HAVE_STRERROR */ +#ifdef __WIN32__ +int wmain(int argc, wchar_t **wcargv) +{ + char** argv; +#else int main(int argc, char** argv) { +#endif int eargv_size; int eargc_base; /* How many arguments in the base of eargv. */ char* emulator; int need_shell = 0; +#ifdef __WIN32__ + int i; + int len; + /* Convert argv to utf8 */ + argv = malloc((argc+1) * sizeof(char*)); + for (i=0; i= sbuf; s--) { if (IS_DIRSEP(*s)) { strcpy(s+1, ERL_NAME); -#ifdef __WIN32__ - if (_access(sbuf, 0) != -1) { - return strsave(sbuf); - } -#else - if (access(sbuf, 1) != -1) { + if(file_exists(sbuf)) return strsave(sbuf); - } -#endif break; } } -- cgit v1.2.3 From b3152d85b1f0187d82270a607006494a04c98546 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Sat, 16 Feb 2013 18:41:39 +0100 Subject: Handle space in paths in test Makefiles test_server: Quote paths with spaces correctly Fix erl_interface makefiles Fix ic test makefiles --- erts/emulator/test/mtx_SUITE_data/Makefile.src | 4 ++-- erts/test/erl_print_SUITE_data/Makefile.src | 2 +- erts/test/ethread_SUITE_data/Makefile.src | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/mtx_SUITE_data/Makefile.src b/erts/emulator/test/mtx_SUITE_data/Makefile.src index e65d99e968..04412280c0 100644 --- a/erts/emulator/test/mtx_SUITE_data/Makefile.src +++ b/erts/emulator/test/mtx_SUITE_data/Makefile.src @@ -17,8 +17,8 @@ # %CopyrightEnd% # -include @erts_lib_include_internal_generated@@DS@ethread.mk -include @erts_lib_include_internal_generated@@DS@erts_internal.mk +include @erts_lib_make_ethread@ +include @erts_lib_make_internal@ NIF_LIBS = mtx_SUITE@dll@ diff --git a/erts/test/erl_print_SUITE_data/Makefile.src b/erts/test/erl_print_SUITE_data/Makefile.src index 3d58669c18..fdffed3b2d 100644 --- a/erts/test/erl_print_SUITE_data/Makefile.src +++ b/erts/test/erl_print_SUITE_data/Makefile.src @@ -17,7 +17,7 @@ # %CopyrightEnd% # -include @erts_lib_include_internal_generated@@DS@ethread.mk +include @erts_lib_make_ethread@ CC = @CC@ CFLAGST = @ERTS_CFLAGS@ diff --git a/erts/test/ethread_SUITE_data/Makefile.src b/erts/test/ethread_SUITE_data/Makefile.src index bad133c467..ad2556f327 100644 --- a/erts/test/ethread_SUITE_data/Makefile.src +++ b/erts/test/ethread_SUITE_data/Makefile.src @@ -17,8 +17,8 @@ # %CopyrightEnd% # -include @erts_lib_include_internal_generated@@DS@ethread.mk -include @erts_lib_include_internal_generated@@DS@erts_internal.mk +include @erts_lib_make_ethread@ +include @erts_lib_make_internal@ CC = @CC@ CFLAGS = @ERTS_CFLAGS@ -- cgit v1.2.3 From 46d99ecfdc8b5d1f6d35c415d020eca5cd6130e1 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Tue, 19 Feb 2013 16:14:49 +0100 Subject: Quote windows paths with spaces --- erts/test/nt_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/test/nt_SUITE.erl b/erts/test/nt_SUITE.erl index 7580a7b364..2e4184e7f1 100644 --- a/erts/test/nt_SUITE.erl +++ b/erts/test/nt_SUITE.erl @@ -74,7 +74,7 @@ end_per_testcase(_Func, Config) -> ok. erlsrv() -> - os:find_executable(erlsrv). + "\"" ++ os:find_executable(erlsrv) ++ "\"". recv_prog_output(Port) -> @@ -542,7 +542,7 @@ get_current_procs(Config) -> ?line erl_parse:parse_term(Tok). nt_info(Config) when is_list(Config) -> - ?line filename:join(?config(data_dir, Config), "nt_info"). + ?line "\"" ++ filename:join(?config(data_dir, Config), "nt_info") ++ "\"". logdir(Config) -> -- cgit v1.2.3 From c7cda78643561ac76e108d37a91eeb67f899696a Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Wed, 20 Feb 2013 16:27:17 +0100 Subject: erts: Window start_erl now uses widechars/unicode --- erts/etc/win32/start_erl.c | 337 ++++++++++++++++++++++++--------------------- 1 file changed, 180 insertions(+), 157 deletions(-) (limited to 'erts') diff --git a/erts/etc/win32/start_erl.c b/erts/etc/win32/start_erl.c index facf79e5ff..0ca12f09c9 100644 --- a/erts/etc/win32/start_erl.c +++ b/erts/etc/win32/start_erl.c @@ -30,7 +30,7 @@ #include #include -char *progname; +wchar_t *progname; /* * If CASE_SENSITIVE_OPTIONS is specified, options are case sensitive @@ -43,26 +43,26 @@ char *progname; #define strnicmp _strnicmp #endif -#define RELEASE_SUBDIR "\\releases" -#define ERTS_SUBDIR_PREFIX "\\erts-" -#define BIN_SUBDIR "\\bin" -#define REGISTRY_BASE "Software\\Ericsson\\Erlang\\" -#define DEFAULT_DATAFILE "start_erl.data" +#define RELEASE_SUBDIR L"\\releases" +#define ERTS_SUBDIR_PREFIX L"\\erts-" +#define BIN_SUBDIR L"\\bin" +#define REGISTRY_BASE L"Software\\Ericsson\\Erlang\\" +#define DEFAULT_DATAFILE L"start_erl.data" /* Global variables holding option values and command lines */ -char *CommandLineStart = NULL; -char *ErlCommandLine = NULL; -char *MyCommandLine = NULL; -char *DataFileName = NULL; -char *RelDir = NULL; -char *BootFlagsFile = NULL; -char *BootFlags = NULL; -char *RegistryKey = NULL; -char *BinDir = NULL; -char *RootDir = NULL; -char *VsnDir = NULL; -char *Version = NULL; -char *Release = NULL; +wchar_t *CommandLineStart = NULL; +wchar_t *ErlCommandLine = NULL; +wchar_t *MyCommandLine = NULL; +wchar_t *DataFileName = NULL; +wchar_t *RelDir = NULL; +wchar_t *BootFlagsFile = NULL; +wchar_t *BootFlags = NULL; +wchar_t *RegistryKey = NULL; +wchar_t *BinDir = NULL; +wchar_t *RootDir = NULL; +wchar_t *VsnDir = NULL; +wchar_t *Version = NULL; +wchar_t *Release = NULL; BOOL NoConfig=FALSE; PROCESS_INFORMATION ErlProcessInfo; @@ -100,7 +100,7 @@ void exit_help(char *err) ShowLastError(); fprintf(stderr, "** Error: %s\n", err); - printf("Usage:\n%s\n" + printf("Usage:\n%S\n" " [] ++\n" " [-data ]\n" " {-rootdir | \n" @@ -119,56 +119,56 @@ void exit_help(char *err) */ void split_commandline(void) { - char *cmdline = CommandLineStart; + wchar_t *cmdline = CommandLineStart; progname=cmdline; /* Remove the first (quoted) string (our program name) */ - if(*cmdline == '"') { + if(*cmdline == L'"') { cmdline++; /* Skip " */ - while( (*cmdline != '\0') && (*cmdline++) != '"' ) + while( (*cmdline != L'\0') && (*cmdline++) != L'"' ) ; } else { - while( (*cmdline != '\0') && (*cmdline++) != ' ' ) + while( (*cmdline != L'\0') && (*cmdline++) != L' ' ) ; } - while( (*cmdline) == ' ' ) + while( (*cmdline) == L' ' ) cmdline++; - if( *cmdline == '\0') { - ErlCommandLine = ""; - MyCommandLine = ""; + if( *cmdline == L'\0') { + ErlCommandLine = L""; + MyCommandLine = L""; return; } - cmdline[-1] = '\0'; + cmdline[-1] = L'\0'; /* Start from the end of the string and search for "++ " (PLUS PLUS SPACE) */ ErlCommandLine = cmdline; - if(strncmp(cmdline,"++ ",3)) - cmdline = strstr(cmdline," ++ "); + if(wcsncmp(cmdline,L"++ ",3)) + cmdline = wcsstr(cmdline,L" ++ "); if( cmdline == NULL ) { - MyCommandLine = ""; + MyCommandLine = L""; return; } /* Terminate the ErlCommandLine where MyCommandLine starts */ *cmdline++ = '\0'; /* Skip 'whitespace--whitespace' (WHITESPACE MINUS MINUS WHITESPACE) */ - while( (*cmdline) == ' ' ) + while( (*cmdline) == L' ' ) cmdline++; - while( (*cmdline) == '+' ) + while( (*cmdline) == L'+' ) cmdline++; - while( (*cmdline) == ' ' ) + while( (*cmdline) == L' ' ) cmdline++; MyCommandLine = cmdline; #ifdef _DEBUG - fprintf(stderr, "ErlCommandLine: '%s'\n", ErlCommandLine); - fprintf(stderr, "MyCommandLine: '%s'\n", MyCommandLine); + fprintf(stderr, "ErlCommandLine: '%S'\n", ErlCommandLine); + fprintf(stderr, "MyCommandLine: '%S'\n", MyCommandLine); #endif } @@ -178,30 +178,30 @@ void split_commandline(void) * Skips any leading spaces and parses up to NULL or end of quoted string. * Calls exit_help() if an unterminated quote is detected. */ -char * unquote_optionarg(char *str, char **strp) +wchar_t * unquote_optionarg(wchar_t *str, wchar_t **strp) { - char *newstr = (char *)malloc(strlen(str)+1); /* This one is - realloc:ed later */ + /* This one is realloc:ed later */ + wchar_t *newstr = (wchar_t *)malloc((wcslen(str)+1)*sizeof(wchar_t)); int i = 0, inquote = 0; assert(newstr); assert(str); /* Skip leading spaces */ - while( *str == ' ' ) + while( *str == L' ' ) str++; /* Loop while in quote or until EOS or unquoted space */ - while( (inquote==1) || ( (*str!=0) && (*str!=' ') ) ) { + while( (inquote==1) || ( (*str!=0) && (*str!=L' ') ) ) { switch( *str ) { - case '\\': + case L'\\': /* If we are inside a quoted string we should convert \c to c */ - if( inquote && str[1] == '"' ) + if( inquote && str[1] == L'"' ) str++; newstr[i++]=*str++; break; - case '"': + case L'"': inquote = 1-inquote; *str++; break; @@ -220,7 +220,7 @@ char * unquote_optionarg(char *str, char **strp) *strp = str; /* Adjust memblock of newstr */ - newstr = (char *)realloc(newstr, i); + newstr = (wchar_t *)realloc(newstr, i*sizeof(wchar_t)); assert(newstr); return(newstr); } @@ -232,34 +232,34 @@ char * unquote_optionarg(char *str, char **strp) */ void parse_commandline(void) { - char *cmdline = MyCommandLine; + wchar_t *cmdline = MyCommandLine; - while( *cmdline != '\0' ) { + while( *cmdline != L'\0' ) { switch( *cmdline ) { case '-': /* Handle both -arg and /arg */ case '/': *cmdline++; - if( strnicmp(cmdline, "data", 4) == 0) { + if( _wcsnicmp(cmdline, L"data", 4) == 0) { DataFileName = unquote_optionarg(cmdline+4, &cmdline); - } else if( strnicmp(cmdline, "rootdir", 7) == 0) { + } else if( _wcsnicmp(cmdline, L"rootdir", 7) == 0) { RootDir = unquote_optionarg(cmdline+7, &cmdline); #ifdef _DEBUG - fprintf(stderr, "RootDir: '%s'\n", RootDir); + fprintf(stderr, "RootDir: '%S'\n", RootDir); #endif - } else if( strnicmp(cmdline, "reldir", 6) == 0) { + } else if( _wcsnicmp(cmdline, L"reldir", 6) == 0) { RelDir = unquote_optionarg(cmdline+6, &cmdline); #ifdef _DEBUG - fprintf(stderr, "RelDir: '%s'\n", RelDir); + fprintf(stderr, "RelDir: '%S'\n", RelDir); #endif - } else if( strnicmp(cmdline, "bootflags", 9) == 0) { + } else if( _wcsnicmp(cmdline, L"bootflags", 9) == 0) { BootFlagsFile = unquote_optionarg(cmdline+9, &cmdline); - } else if( strnicmp(cmdline, "noconfig", 8) == 0) { + } else if( _wcsnicmp(cmdline, L"noconfig", 8) == 0) { NoConfig=TRUE; #ifdef _DEBUG fprintf(stderr, "NoConfig=TRUE\n"); #endif } else { - fprintf(stderr, "Unkown option: '%s'\n", cmdline); + fprintf(stderr, "Unkown option: '%S'\n", cmdline); exit_help("Unknown command line option"); } break; @@ -281,32 +281,35 @@ void parse_commandline(void) void read_datafile(void) { FILE *fp; - char *newname; + wchar_t *newname; long size; + char *ver; + char *rel; - if(!DataFileName){ - DataFileName = malloc(strlen(DEFAULT_DATAFILE) + 1); - strcpy(DataFileName,DEFAULT_DATAFILE); + if(!DataFileName){ + DataFileName = malloc((wcslen(DEFAULT_DATAFILE) + 1)*sizeof(wchar_t)); + wcscpy(DataFileName,DEFAULT_DATAFILE); } /* Is DataFileName relative or absolute ? */ - if( (DataFileName[0] != '\\') && (strncmp(DataFileName+1, ":\\", 2)!=0) ) { + if( (DataFileName[0] != L'\\') && (wcsncmp(DataFileName+1, L":\\", 2)!=0) ) { /* Relative name, we have to prepend RelDir to it. */ if( !RelDir ) { exit_help("Need -reldir when -data filename has relative path."); } else { - newname = (char *)malloc(strlen(DataFileName)+strlen(RelDir)+2); + size = (wcslen(DataFileName)+wcslen(RelDir)+2); + newname = (wchar_t *)malloc(size*sizeof(wchar_t)); assert(newname); - sprintf(newname, "%s\\%s", RelDir, DataFileName); + swprintf(newname, size, L"%s\\%s", RelDir, DataFileName); free(DataFileName); DataFileName=newname; } } #ifdef _DEBUG - fprintf(stderr, "DataFileName: '%s'\n", DataFileName); + fprintf(stderr, "DataFileName: '%S'\n", DataFileName); #endif - if( (fp=fopen(DataFileName, "rb")) == NULL) { + if( (fp=_wfopen(DataFileName, L"rb")) == NULL) { exit_help("Cannot find the datafile."); } @@ -314,21 +317,33 @@ void read_datafile(void) size=ftell(fp); fseek(fp, 0, SEEK_SET); - Version = (char *)malloc(size+1); - Release = (char *)malloc(size+1); - assert(Version); - assert(Release); + ver = (char *)malloc(size+1); + rel = (char *)malloc(size+1); + assert(ver); + assert(rel); - if( (fscanf(fp, "%s %s", Version, Release)) == 0) { + if( (fscanf(fp, "%s %s", ver, rel)) == 0) { fclose(fp); exit_help("Format error in datafile."); } fclose(fp); + size = MultiByteToWideChar(CP_UTF8, 0, ver, -1, NULL, 0); + Version = malloc(size*sizeof(wchar_t)); + assert(Version); + MultiByteToWideChar(CP_UTF8, 0, ver, -1, Version, size); + free(ver); + + size = MultiByteToWideChar(CP_UTF8, 0, rel, -1, NULL, 0); + Release = malloc(size*sizeof(wchar_t)); + assert(Release); + MultiByteToWideChar(CP_UTF8, 0, rel, -1, Release, size); + free(rel); + #ifdef _DEBUG - fprintf(stderr, "DataFile version: '%s'\n", Version); - fprintf(stderr, "DataFile release: '%s'\n", Release); + fprintf(stderr, "DataFile version: '%S'\n", Version); + fprintf(stderr, "DataFile release: '%S'\n", Release); #endif } @@ -340,31 +355,33 @@ void read_bootflags(void) { FILE *fp; long fsize; - char *newname; - + wchar_t *newname; + char *bootf; + if(BootFlagsFile) { /* Is BootFlagsFile relative or absolute ? */ - if( (BootFlagsFile[0] != '\\') && - (strncmp(BootFlagsFile+1, ":\\", 2)!=0) ) { + if( (BootFlagsFile[0] != L'\\') && + (wcsncmp(BootFlagsFile+1, L":\\", 2)!=0) ) { /* Relative name, we have to prepend RelDir\\Version to it. */ if( !RelDir ) { exit_help("Need -reldir when -bootflags " "filename has relative path."); } else { - newname = (char *)malloc(strlen(BootFlagsFile)+ - strlen(RelDir)+strlen(Release)+3); + int len = wcslen(BootFlagsFile)+ + wcslen(RelDir)+wcslen(Release)+3; + newname = (wchar_t *)malloc(len*sizeof(wchar_t)); assert(newname); - sprintf(newname, "%s\\%s\\%s", RelDir, Release, BootFlagsFile); + swprintf(newname, len, L"%s\\%s\\%s", RelDir, Release, BootFlagsFile); free(BootFlagsFile); BootFlagsFile=newname; } } #ifdef _DEBUG - fprintf(stderr, "BootFlagsFile: '%s'\n", BootFlagsFile); + fprintf(stderr, "BootFlagsFile: '%S'\n", BootFlagsFile); #endif - if( (fp=fopen(BootFlagsFile, "rb")) == NULL) { + if( (fp=_wfopen(BootFlagsFile, L"rb")) == NULL) { exit_help("Could not open BootFlags file."); } @@ -372,80 +389,86 @@ void read_bootflags(void) fsize=ftell(fp); fseek(fp, 0, SEEK_SET); - BootFlags = (char *)malloc(fsize+1); - assert(BootFlags); - if( (fgets(BootFlags, fsize+1, fp)) == NULL) { + bootf = (char *)malloc(fsize+1); + assert(bootf); + if( (fgets(bootf, fsize+1, fp)) == NULL) { exit_help("Error while reading BootFlags file"); } fclose(fp); /* Adjust buffer size */ - BootFlags = (char *)realloc(BootFlags, strlen(BootFlags)+1); - assert(BootFlags); + bootf = (char *)realloc(bootf, strlen(bootf)+1); + assert(bootf); /* Strip \r\n from BootFlags */ - fsize = strlen(BootFlags); + fsize = strlen(bootf); while( fsize > 0 && - ( (BootFlags[fsize-1] == '\r') || - (BootFlags[fsize-1] == '\n') ) ) { - BootFlags[--fsize]=0; + ( (bootf[fsize-1] == '\r') || + (bootf[fsize-1] == '\n') ) ) { + bootf[--fsize]=0; } - + fsize = MultiByteToWideChar(CP_UTF8, 0, bootf, -1, NULL, 0); + BootFlags = malloc(fsize*sizeof(wchar_t)); + assert(BootFlags); + MultiByteToWideChar(CP_UTF8, 0, bootf, -1, BootFlags, fsize); + free(bootf); } else { /* Set empty BootFlags */ - BootFlags = ""; + BootFlags = L""; } #ifdef _DEBUG - fprintf(stderr, "BootFlags: '%s'\n", BootFlags); + fprintf(stderr, "BootFlags: '%S'\n", BootFlags); #endif } long start_new_node(void) { - char *CommandLine; + wchar_t *CommandLine; unsigned long i; - STARTUPINFO si; - DWORD dwExitCode; + STARTUPINFOW si; + DWORD dwExitCode; - i = strlen(RelDir) + strlen(Release) + 4; - VsnDir = (char *)malloc(i); + i = wcslen(RelDir) + wcslen(Release) + 4; + VsnDir = (wchar_t *)malloc(i*sizeof(wchar_t)); assert(VsnDir); - sprintf(VsnDir, "%s\\%s", RelDir, Release); + swprintf(VsnDir, i, L"%s\\%s", RelDir, Release); if( NoConfig ) { - i = strlen(BinDir) + strlen(ErlCommandLine) + - strlen(BootFlags) + 64; - CommandLine = (char *)malloc(i); + i = wcslen(BinDir) + wcslen(ErlCommandLine) + + wcslen(BootFlags) + 64; + CommandLine = (wchar_t *)malloc(i*sizeof(wchar_t)); assert(CommandLine); - sprintf(CommandLine, - "\"%s\\erl.exe\" -boot \"%s\\start\" %s %s", - BinDir, - VsnDir, - ErlCommandLine, - BootFlags); + swprintf(CommandLine, + i, + L"\"%s\\erl.exe\" -boot \"%s\\start\" %s %s", + BinDir, + VsnDir, + ErlCommandLine, + BootFlags); } else { - i = strlen(BinDir) + strlen(ErlCommandLine) - + strlen(BootFlags) + strlen(VsnDir)*2 + 64; - CommandLine = (char *)malloc(i); + i = wcslen(BinDir) + wcslen(ErlCommandLine) + + wcslen(BootFlags) + wcslen(VsnDir)*2 + 64; + CommandLine = (wchar_t *)malloc(i*sizeof(wchar_t)); assert(CommandLine); - sprintf(CommandLine, - "\"%s\\erl.exe\" -boot \"%s\\start\" -config \"%s\\sys\" %s %s", - BinDir, - VsnDir, - VsnDir, - ErlCommandLine, - BootFlags); + swprintf(CommandLine, + i, + L"\"%s\\erl.exe\" -boot \"%s\\start\" -config \"%s\\sys\" %s %s", + BinDir, + VsnDir, + VsnDir, + ErlCommandLine, + BootFlags); } #ifdef _DEBUG - fprintf(stderr, "CommandLine: '%s'\n", CommandLine); + fprintf(stderr, "CommandLine: '%S'\n", CommandLine); #endif /* Initialize the STARTUPINFO structure. */ - memset(&si, 0, sizeof(STARTUPINFO)); - si.cb = sizeof(STARTUPINFO); + memset(&si, 0, sizeof(STARTUPINFOW)); + si.cb = sizeof(STARTUPINFOW); si.lpTitle = NULL; si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); @@ -453,19 +476,19 @@ long start_new_node(void) si.hStdError = GetStdHandle(STD_ERROR_HANDLE); /* Create the new Erlang process */ - if( (CreateProcess( - NULL, /* pointer to name of executable module */ - CommandLine, /* pointer to command line string */ - NULL, /* pointer to process security attributes */ - NULL, /* pointer to thread security attributes */ - TRUE, /* handle inheritance flag */ - GetPriorityClass(GetCurrentProcess()), - /* creation flags */ - NULL, /* pointer to new environment block */ - BinDir,/* pointer to current directory name */ - &si, /* pointer to STARTUPINFO */ - &ErlProcessInfo /* pointer to PROCESS_INFORMATION */ - )) == FALSE) { + if( (CreateProcessW( + NULL, /* pointer to name of executable module */ + CommandLine, /* pointer to command line string */ + NULL, /* pointer to process security attributes */ + NULL, /* pointer to thread security attributes */ + TRUE, /* handle inheritance flag */ + GetPriorityClass(GetCurrentProcess()), + /* creation flags */ + NULL, /* pointer to new environment block */ + BinDir,/* pointer to current directory name */ + &si, /* pointer to STARTUPINFO */ + &ErlProcessInfo /* pointer to PROCESS_INFORMATION */ + )) == FALSE) { ShowLastError(); exit_help("Failed to start new node"); } @@ -504,6 +527,7 @@ long start_new_node(void) */ void complete_options(void) { + int len; /* Try to find a descent RelDir */ if( !RelDir ) { DWORD sz = 32; @@ -511,15 +535,13 @@ void complete_options(void) DWORD nsz; if (RelDir) free(RelDir); - RelDir = malloc(sz); + RelDir = malloc(sz*sizeof(wchar_t)); if (!RelDir) { fprintf(stderr, "** Error : failed to allocate memory\n"); exit(1); } SetLastError(0); - nsz = GetEnvironmentVariable((LPCTSTR) "RELDIR", - (LPTSTR) RelDir, - sz); + nsz = GetEnvironmentVariableW(L"RELDIR", RelDir, sz); if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { free(RelDir); RelDir = NULL; @@ -536,9 +558,10 @@ void complete_options(void) exit_help("Need either Root directory nor Release directory."); } /* Ok, construct our own RelDir from RootDir */ - RelDir = (char *) malloc(strlen(RootDir)+strlen(RELEASE_SUBDIR)+1); + sz = wcslen(RootDir)+wcslen(RELEASE_SUBDIR)+1; + RelDir = (wchar_t *) malloc(sz * sizeof(wchar_t)); assert(RelDir); - sprintf(RelDir, "%s" RELEASE_SUBDIR, RootDir); + swprintf(RelDir, sz, L"%s" RELEASE_SUBDIR, RootDir); read_datafile(); } else { read_datafile(); @@ -548,32 +571,32 @@ void complete_options(void) } if( !RootDir ) { /* Try to construct RootDir from RelDir */ - char *p; - RootDir = malloc(strlen(RelDir)+1); - strcpy(RootDir,RelDir); - p = RootDir+strlen(RootDir)-1; - if (p >= RootDir && (*p == '/' || *p == '\\')) + wchar_t *p; + RootDir = malloc((wcslen(RelDir)+1)*sizeof(wchar_t)); + wcscpy(RootDir,RelDir); + p = RootDir+wcslen(RootDir)-1; + if (p >= RootDir && (*p == L'/' || *p == L'\\')) --p; - while (p >= RootDir && *p != '/' && *p != '\\') + while (p >= RootDir && *p != L'/' && *p != L'\\') --p; if (p <= RootDir) { /* Empty RootDir is also an error */ exit_help("Cannot determine Root directory from " "Release directory."); } - *p = '\0'; + *p = L'\0'; } - - BinDir = (char *) malloc(strlen(RootDir)+strlen(ERTS_SUBDIR_PREFIX)+ - strlen(Version)+strlen(BIN_SUBDIR)+1); + len = wcslen(RootDir)+wcslen(ERTS_SUBDIR_PREFIX)+ + wcslen(Version)+wcslen(BIN_SUBDIR)+1; + BinDir = (wchar_t *) malloc(len * sizeof(wchar_t)); assert(BinDir); - sprintf(BinDir, "%s" ERTS_SUBDIR_PREFIX "%s" BIN_SUBDIR, RootDir, Version); + swprintf(BinDir, len, L"%s" ERTS_SUBDIR_PREFIX L"%s" BIN_SUBDIR, RootDir, Version); read_bootflags(); #ifdef _DEBUG - fprintf(stderr, "RelDir: '%s'\n", RelDir); - fprintf(stderr, "BinDir: '%s'\n", BinDir); + fprintf(stderr, "RelDir: '%S'\n", RelDir); + fprintf(stderr, "BinDir: '%S'\n", BinDir); #endif } @@ -598,17 +621,17 @@ BOOL WINAPI LogoffHandlerRoutine( DWORD dwCtrlType ) int main(void) { DWORD dwExitCode; - char *cmdline; + wchar_t *cmdline; /* Make sure a logoff does not distrurb us. */ SetConsoleCtrlHandler(LogoffHandlerRoutine, TRUE); - cmdline = GetCommandLine(); + cmdline = GetCommandLineW(); assert(cmdline); - CommandLineStart = (char *) malloc(strlen(cmdline) + 1); + CommandLineStart = (wchar_t *) malloc((wcslen(cmdline) + 1)*sizeof(wchar_t)); assert(CommandLineStart); - strcpy(CommandLineStart,cmdline); + wcscpy(CommandLineStart,cmdline); split_commandline(); parse_commandline(); -- cgit v1.2.3 From ee23d4b549618044d4a4f800db839483b359cb9f Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Thu, 18 Apr 2013 14:55:47 +0200 Subject: erts: Windows, convert erlsrv to use widestring --- erts/etc/win32/erlsrv/erlsrv_global.h | 10 +- erts/etc/win32/erlsrv/erlsrv_interactive.c | 669 +++++++++++++++-------------- erts/etc/win32/erlsrv/erlsrv_interactive.h | 2 +- erts/etc/win32/erlsrv/erlsrv_main.c | 2 +- erts/etc/win32/erlsrv/erlsrv_registry.c | 249 ++++++----- erts/etc/win32/erlsrv/erlsrv_registry.h | 16 +- erts/etc/win32/erlsrv/erlsrv_service.c | 403 ++++++++--------- erts/etc/win32/erlsrv/erlsrv_service.h | 2 +- erts/etc/win32/erlsrv/erlsrv_util.c | 100 ++--- erts/etc/win32/erlsrv/erlsrv_util.h | 18 +- 10 files changed, 753 insertions(+), 718 deletions(-) (limited to 'erts') diff --git a/erts/etc/win32/erlsrv/erlsrv_global.h b/erts/etc/win32/erlsrv/erlsrv_global.h index d3922dc1e3..f25e09ea45 100644 --- a/erts/etc/win32/erlsrv/erlsrv_global.h +++ b/erts/etc/win32/erlsrv/erlsrv_global.h @@ -19,13 +19,13 @@ #ifndef _ERLSRV_GLOBAL_H #define _ERLSRV_GLOBAL_H -#define APP_NAME "ErlSrv" +#define APP_NAME L"ErlSrv" -#define ERLANG_MACHINE "erl.exe" +#define ERLANG_MACHINE L"erl.exe" -#define SERVICE_ENV "ERLSRV_SERVICE_NAME" -#define EXECUTABLE_ENV "ERLSRV_EXECUTABLE" -#define DEBUG_ENV "ERLSRV_DEBUG" +#define SERVICE_ENV L"ERLSRV_SERVICE_NAME" +#define EXECUTABLE_ENV L"ERLSRV_EXECUTABLE" +#define DEBUG_ENV L"ERLSRV_DEBUG" #ifdef _DEBUG #define HARDDEBUG 1 diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.c b/erts/etc/win32/erlsrv/erlsrv_interactive.c index 3f7e20b923..c370485ac1 100644 --- a/erts/etc/win32/erlsrv/erlsrv_interactive.c +++ b/erts/etc/win32/erlsrv/erlsrv_interactive.c @@ -27,54 +27,60 @@ #include "erlsrv_interactive.h" #include "erlsrv_util.h" /* service_name */ -#define DBG fprintf(stderr,"argv[0]:%s line %d\n",argv[0],__LINE__) +#define DBG fwprintf(stderr,L"argv[0]:%s line %d\n",argv[0],__LINE__) +/* #define HARDDEBUG 1 */ + +#include /* Really HAS to correcpond to the enum in erlsrv_registry.h */ -static char *arg_tab[] = { - "stopaction", "st", - "onfail", "on", - "machine", "m", - "env", "e", - "workdir", "w", - "priority", "p", - "sname", "sn", - "name", "n", - "args", "ar", - "debugtype", "d", - "internalservicename","i", - "comment","c", +static wchar_t *arg_tab[] = { + L"stopaction", L"st", + L"onfail", L"on", + L"machine", L"m", + L"env", L"e", + L"workdir", L"w", + L"priority", L"p", + L"sname", L"sn", + L"name", L"n", + L"args", L"ar", + L"debugtype", L"d", + L"internalservicename",L"i", + L"comment",L"c", NULL, NULL }; -static char *generate_real_service_name(char *display_name){ +static wchar_t *generate_real_service_name(wchar_t *display_name){ SYSTEMTIME systime; FILETIME ftime; - char *buff = malloc(strlen(display_name)+ - (8*2)+1); - char *tmp = _strdup(display_name); + int len=(wcslen(display_name)+(8*2)+1); + wchar_t *buff; + wchar_t *tmp = _wcsdup(display_name); int i; + buff = (wchar_t*) malloc(len*sizeof(wchar_t)); + /* 2 Hex chars for each byte in a DWORD */ GetSystemTime(&systime); SystemTimeToFileTime(&systime,&ftime); /* Remove trailing version info to avoid user confusion */ - for(i = (strlen(tmp)-1);i > 0; --i) - if(tmp[i] == '_'){ - tmp[i] = '\0'; + for(i = (wcslen(tmp)-1);i > 0; --i) + if(tmp[i] == L'_'){ + tmp[i] = L'\0'; break; } - sprintf(buff,"%s%08x%08x",tmp,ftime.dwHighDateTime, - ftime.dwLowDateTime); + swprintf(buff,len,L"%s%08x%08x",tmp, + ftime.dwHighDateTime, + ftime.dwLowDateTime); free(tmp); return buff; } -static int lookup_arg(char *arg){ +static int lookup_arg(wchar_t *arg){ int i; - if(*arg != '-' && *arg != '/') + if(*arg != L'-' && *arg != L'/') return -1; for(i=0; arg_tab[i] != NULL; i += 2){ - if(!_strnicmp(arg_tab[i],arg+1,strlen(arg+1)) && - !_strnicmp(arg_tab[i+1],arg+1,strlen(arg_tab[i+1]))) + if(!_wcsnicmp(arg_tab[i],arg+1,wcslen(arg+1)) && + !_wcsnicmp(arg_tab[i+1],arg+1,wcslen(arg_tab[i+1]))) return (i / 2); } return -1; @@ -82,29 +88,29 @@ static int lookup_arg(char *arg){ -char *edit_env(char *edit, char *oldenv){ - char **arg; - char *value; - char *name = strdup(edit); +wchar_t *edit_env(wchar_t *edit, wchar_t *oldenv){ + wchar_t **arg; + wchar_t *value; + wchar_t *name = wcsdup(edit); int i; - char *tmp; + wchar_t *tmp; arg = env_to_arg(oldenv); - value = strchr(name,'='); + value = wcschr(name,L'='); if(value){ - *(value++) = '\0'; - if(*value == '\0') + *(value++) = L'\0'; + if(*value == L'\0') value = NULL; } for(i=0;arg[i] != NULL; ++i){ - tmp = strchr(arg[i],'='); - if(((int) strlen(name)) == (tmp - arg[i]) && - !_strnicmp(name,arg[i], tmp - arg[i])) + tmp = wcschr(arg[i],L'='); + if(((int) wcslen(name)) == (tmp - arg[i]) && + !_wcsnicmp(name,arg[i], tmp - arg[i])) break; } if(arg[i] != NULL){ free(arg[i]); if(value){ - arg[i] = strdup(edit); + arg[i] = wcsdup(edit); } else { do { arg[i] = arg[i+1]; @@ -113,7 +119,7 @@ char *edit_env(char *edit, char *oldenv){ } } else if(value){ /* add to arg, which is always allocated to hold one extra environment variable*/ - arg[i] = strdup(edit); + arg[i] = wcsdup(edit); arg[i+1] = NULL; } free(name); @@ -132,8 +138,8 @@ void print_last_error(void){ (LPTSTR) &mes, 0, NULL ); - fprintf(stderr,"Error: %s",mes); - LocalFree(mes); + fwprintf(stderr,L"Error: %S",mes); + LocalFree(mes); } static int get_last_error(void) @@ -144,19 +150,19 @@ static int get_last_error(void) static BOOL install_service(void){ SC_HANDLE scm; SC_HANDLE service; - char filename[MAX_PATH + 3]; + wchar_t filename[MAX_PATH + 3]; DWORD fnsiz=MAX_PATH; - char dependant[] = { 'L','a','n','m','a','n', - 'W','o','r','k','s','t', - 'a','t','i','o','n','\0','\0'}; + wchar_t dependant[] = { L'L',L'a',L'n',L'm',L'a',L'n', + L'W',L'o',L'r',L'k',L's',L't', + L'a',L't',L'i',L'o',L'n',L'\0',L'\0'}; - if(!(fnsiz = GetModuleFileName(NULL, filename, fnsiz))) + if(!(fnsiz = GetModuleFileNameW(NULL, filename, fnsiz))) return FALSE; - if(strchr(filename,' ')){ - memmove(filename+1,filename,fnsiz); - filename[0] ='\"'; /* " */ - filename[fnsiz+1] = '\"'; /* " */ - filename[fnsiz+2] = '\0'; + if(wcschr(filename,L' ')){ + memmove(filename+1,filename,fnsiz*sizeof(wchar_t)); + filename[0] = L'\"'; /* " */ + filename[fnsiz+1] = L'\"'; /* " */ + filename[fnsiz+2] = L'\0'; } if((scm = OpenSCManager(NULL, NULL, @@ -166,20 +172,20 @@ static BOOL install_service(void){ last_error = GetLastError(); return FALSE; } - service = CreateService(scm, - real_service_name, - service_name, - SERVICE_ALL_ACCESS & - ~(SERVICE_PAUSE_CONTINUE), - SERVICE_WIN32_OWN_PROCESS, - SERVICE_AUTO_START, - SERVICE_ERROR_NORMAL, - filename, - NULL, - NULL, - dependant, - NULL, - NULL); + service = CreateServiceW(scm, + real_service_name, + service_name, + SERVICE_ALL_ACCESS & + ~(SERVICE_PAUSE_CONTINUE), + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + filename, + NULL, + NULL, + dependant, + NULL, + NULL); if(service == NULL){ CloseServiceHandle(scm); last_error = GetLastError(); @@ -198,9 +204,9 @@ static BOOL remove_service(void){ GENERIC_WRITE)) == NULL) return FALSE; - service = OpenService(scm, - real_service_name, - SERVICE_ALL_ACCESS); + service = OpenServiceW(scm, + real_service_name, + SERVICE_ALL_ACCESS); if(service == NULL){ CloseServiceHandle(scm); return FALSE; @@ -220,9 +226,9 @@ static BOOL open_service_control(SC_HANDLE *scm, SC_HANDLE *service){ SC_MANAGER_ALL_ACCESS)) == NULL) return FALSE; - *service = OpenService(*scm, - real_service_name, - SERVICE_ALL_ACCESS); + *service = OpenServiceW(*scm, + real_service_name, + SERVICE_ALL_ACCESS); if(service == NULL){ CloseServiceHandle(*scm); return FALSE; @@ -239,10 +245,10 @@ static BOOL open_service_config(SC_HANDLE *scm, SC_HANDLE *service){ last_error = GetLastError(); return FALSE; } - *service = OpenService(*scm, - real_service_name, - /*GENERIC_WRITE*/ - SERVICE_ALL_ACCESS); + *service = OpenServiceW(*scm, + real_service_name, + /*GENERIC_WRITE*/ + SERVICE_ALL_ACCESS); if(service == NULL){ last_error = GetLastError(); CloseServiceHandle(*scm); @@ -251,16 +257,16 @@ static BOOL open_service_config(SC_HANDLE *scm, SC_HANDLE *service){ return TRUE; } -static BOOL set_service_comment(char *comment) { +static BOOL set_service_comment(wchar_t *comment) { SC_HANDLE scm; SC_HANDLE service; - SERVICE_DESCRIPTION sd; + SERVICE_DESCRIPTIONW sd; BOOL ret = TRUE; sd.lpDescription = comment; if (!open_service_config(&scm,&service)) { return FALSE; } - if (!ChangeServiceConfig2(service,SERVICE_CONFIG_DESCRIPTION,&sd)) { + if (!ChangeServiceConfig2W(service,SERVICE_CONFIG_DESCRIPTION,&sd)) { last_error = GetLastError(); ret = FALSE; } @@ -325,7 +331,7 @@ static BOOL stop_service(void){ if(!open_service_control(&scm,&service)){ #ifdef HARDDEBUG - fprintf(stderr,"Failed to open service.\n"); + fwprintf(stderr,L"Failed to open service.\n"); #endif return FALSE; } @@ -338,7 +344,7 @@ static BOOL stop_service(void){ #ifdef HARDDEBUG if(!ret) { - fprintf(stderr,"Failed to control service.\n"); + fwprintf(stderr,L"Failed to control service.\n"); print_last_error(); } #endif @@ -466,115 +472,114 @@ void cleanup_old(){ } BOOL fill_in_defaults(RegEntry *new){ - char filename[MAX_PATH]; - char *ptr; + wchar_t filename[MAX_PATH]; + wchar_t *ptr; - if(!GetModuleFileName(NULL, filename, MAX_PATH)) + if(!GetModuleFileNameW(NULL, filename, MAX_PATH)) return FALSE; - for(ptr = filename + strlen(filename) - 1; - ptr > filename && *ptr != '\\'; + for(ptr = filename + wcslen(filename) - 1; + ptr > filename && *ptr != L'\\'; --ptr) ; - if(*ptr == '\\') + if(*ptr == L'\\') ++ptr; - *ptr = '\0'; + *ptr = L'\0'; - ptr = malloc(strlen(filename)+strlen(ERLANG_MACHINE)+1); - strcpy(ptr,filename); - strcat(ptr,ERLANG_MACHINE); + ptr = (wchar_t*) malloc((wcslen(filename)+wcslen(ERLANG_MACHINE)+1)*sizeof(wchar_t)); + wcscpy(ptr,filename); + wcscat(ptr,ERLANG_MACHINE); - new[StopAction].data.bytes = ""; + new[StopAction].data.string = L""; new[OnFail].data.value = ON_FAIL_IGNORE; - new[Machine].data.bytes = ptr; - new[Machine].data.expand.unexpanded = ptr; - new[Env].data.bytes = "\0"; - new[WorkDir].data.bytes = new[WorkDir].data.expand.unexpanded = - ""; + new[Machine].data.string = ptr; + new[Machine].data.expand.unexpanded = ptr; + new[Env].data.string = L"\0"; + new[WorkDir].data.string = new[WorkDir].data.expand.unexpanded = L""; new[Priority].data.value = NORMAL_PRIORITY_CLASS; - new[SName].data.bytes = service_name; - new[Name].data.bytes = ""; - new[Args].data.bytes = new[Args].data.expand.unexpanded = ""; + new[SName].data.string = service_name; + new[Name].data.string = L""; + new[Args].data.string = new[Args].data.expand.unexpanded = L""; new[DebugType].data.value = DEBUG_TYPE_NO_DEBUG; - new[InternalServiceName].data.bytes = real_service_name; - new[Comment].data.bytes = ""; + new[InternalServiceName].data.string = real_service_name; + new[Comment].data.string = L""; return TRUE; } -int do_usage(char *arg0){ - printf("Usage:\n"); - printf("%s {set | add} \n" - "\t[-st[opaction] []]\n" - "\t[-on[fail] [{reboot | restart | restart_always}]]\n" - "\t[-m[achine] []]\n" - "\t[-e[nv] [[=]]]\n" - "\t[-w[orkdir] []]\n" - "\t[-p[riority] [{low|high|realtime}]]\n" - "\t[{-sn[ame] | -n[ame]} []]\n" - "\t[-d[ebugtype] [{new|reuse|console}]]\n" - "\t[-ar[gs] []]\n\n" - "%s {start | start_disabled | stop | disable | enable} \n\n" - "%s remove \n\n" - "%s rename \n\n" - "%s list []\n\n" - "%s help\n\n", +int do_usage(wchar_t *arg0){ + wprintf(L"Usage:\n"); + wprintf(L"%s {set | add} \n" + L"\t[-st[opaction] []]\n" + L"\t[-on[fail] [{reboot | restart | restart_always}]]\n" + L"\t[-m[achine] []]\n" + L"\t[-e[nv] [[=]]]\n" + L"\t[-w[orkdir] []]\n" + L"\t[-p[riority] [{low|high|realtime}]]\n" + L"\t[{-sn[ame] | -n[ame]} []]\n" + L"\t[-d[ebugtype] [{new|reuse|console}]]\n" + L"\t[-ar[gs] []]\n\n" + L"%s {start | start_disabled | stop | disable | enable} \n\n" + L"%s remove \n\n" + L"%s rename \n\n" + L"%s list []\n\n" + L"%s help\n\n", arg0,arg0,arg0,arg0,arg0,arg0); - printf("Manipulates Erlang system services on Windows NT.\n\n"); - printf("When no parameter to an option is specified, the option\n" - "is reset to it's default value. To set an empty argument\n" - "list, give option -args as last option on command line " - "with\n" - "no arguments.\n\n"); - printf("See Erlang documentation for full description.\n"); + wprintf(L"Manipulates Erlang system services on Windows NT.\n\n"); + wprintf(L"When no parameter to an option is specified, the option\n" + L"is reset to it's default value. To set an empty argument\n" + L"list, give option -args as last option on command line " + L"with\n" + L"no arguments.\n\n"); + wprintf(L"See Erlang documentation for full description.\n"); return 0; } -int do_manage(int argc,char **argv){ - char *action = argv[1]; +int do_manage(int argc, wchar_t **argv){ + wchar_t *action = argv[1]; RegEntry *current = empty_reg_tab(); if(argc < 3){ - fprintf(stderr,"%s: No servicename given!\n",argv[0]); + fwprintf(stderr,L"%s: No servicename given!\n",argv[0]); do_usage(argv[0]); return 1; } service_name = argv[2]; if(!fetch_current(current)){ - fprintf(stderr,"%s: The service %s is not an erlsrv controlled service.\n", + fwprintf(stderr,L"%s: The service %s is not an erlsrv controlled service.\n", argv[0],service_name); return 1; } - real_service_name = _strdup(current[InternalServiceName].data.bytes); + real_service_name = _wcsdup(current[InternalServiceName].data.string); free_keys(current); - if(!_stricmp(action,"start")){ + if(!_wcsicmp(action,L"start")){ if(!start_service()){ - fprintf(stderr,"%s: Failed to start service %s.\n", + fwprintf(stderr,L"%s: Failed to start service %s.\n", argv[0],service_name); print_last_error(); return 1; } else { if(!wait_service_trans(SERVICE_STOPPED, SERVICE_START_PENDING, SERVICE_RUNNING, 60)){ - fprintf(stderr,"%s: Failed to start service %s.\n", + fwprintf(stderr,L"%s: Failed to start service %s.\n", argv[0],service_name); print_last_error(); return 1; } - printf("%s: Service %s started.\n", + wprintf(L"%s: Service %s started.\n", argv[0],service_name); return 0; } } - if(!_stricmp(action,"start_disabled")){ + if(!_wcsicmp(action,L"start_disabled")){ if(!enable_service()){ - fprintf(stderr,"%s: Failed to enable service %s.\n", + fwprintf(stderr,L"%s: Failed to enable service %s.\n", argv[0],service_name); print_last_error(); return 1; } if(!start_service() && get_last_error() != ERROR_SERVICE_ALREADY_RUNNING){ - fprintf(stderr,"%s: Failed to start service %s.\n", + fwprintf(stderr,L"%s: Failed to start service %s.\n", argv[0],service_name); print_last_error(); goto failure_starting; @@ -582,84 +587,84 @@ int do_manage(int argc,char **argv){ if(!wait_service_trans(SERVICE_STOPPED, SERVICE_START_PENDING, SERVICE_RUNNING, 60)){ - fprintf(stderr,"%s: Failed to start service %s.\n", + fwprintf(stderr,L"%s: Failed to start service %s.\n", argv[0],service_name); print_last_error(); goto failure_starting; } if(!disable_service()){ - fprintf(stderr,"%s: Failed to disable service %s.\n", + fwprintf(stderr,L"%s: Failed to disable service %s.\n", argv[0],service_name); print_last_error(); return 1; } - printf("%s: Service %s started.\n", + wprintf(L"%s: Service %s started.\n", argv[0],service_name); return 0; failure_starting: if(!disable_service()){ - fprintf(stderr,"%s: Failed to disable service %s.\n", + fwprintf(stderr,L"%s: Failed to disable service %s.\n", argv[0],service_name); print_last_error(); } return 1; } - if(!_stricmp(action,"stop")){ + if(!_wcsicmp(action,L"stop")){ if(!stop_service()){ - fprintf(stderr,"%s: Failed to stop service %s.\n", + fwprintf(stderr,L"%s: Failed to stop service %s.\n", argv[0],service_name); print_last_error(); return 1; } else { if(!wait_service_trans(SERVICE_RUNNING, SERVICE_STOP_PENDING, SERVICE_STOPPED, 60)){ - fprintf(stderr,"%s: Failed to stop service %s.\n", + fwprintf(stderr,L"%s: Failed to stop service %s.\n", argv[0],service_name); print_last_error(); return 1; } - printf("%s: Service %s stopped.\n", + wprintf(L"%s: Service %s stopped.\n", argv[0],service_name); return 0; } } - if(!_stricmp(action,"disable")){ + if(!_wcsicmp(action,L"disable")){ #if 0 if(stop_service()){ - printf("%s: Service %s stopped.\n", + wprintf(L"%s: Service %s stopped.\n", argv[0],service_name); } #endif if(!disable_service()){ - fprintf(stderr,"%s: Failed to disable service %s.\n", + fwprintf(stderr,L"%s: Failed to disable service %s.\n", argv[0],service_name); print_last_error(); return 1; } else { - printf("%s: Service %s disabled.\n", + wprintf(L"%s: Service %s disabled.\n", argv[0],service_name); return 0; } } - if(!_stricmp(action,"enable")){ + if(!_wcsicmp(action,L"enable")){ if(!enable_service()){ - fprintf(stderr,"%s: Failed to enable service %s.\n", + fwprintf(stderr,L"%s: Failed to enable service %s.\n", argv[0],service_name); print_last_error(); return 1; } else { - printf("%s: Service %s enabled.\n", + wprintf(L"%s: Service %s enabled.\n", argv[0],service_name); return 0; } } - fprintf(stderr,"%s: Unrecignized argument %s.\n", + fwprintf(stderr,L"%s: Unrecignized argument %s.\n", argv[0],action); return 1; } -int do_add_or_set(int argc, char **argv){ +int do_add_or_set(int argc, wchar_t **argv){ RegEntry *new_entries; RegEntry *default_entries; int add = 0; @@ -669,40 +674,40 @@ int do_add_or_set(int argc, char **argv){ new_entries = empty_reg_tab(); default_entries = empty_reg_tab(); if(argc < 3){ - fprintf(stderr,"%s: No servicename given!\n",argv[0]); + fwprintf(stderr,L"%s: No servicename given!\n",argv[0]); do_usage(argv[0]); return 1; } service_name = argv[2]; - if(!_stricmp(argv[1],"add")){ + if(!_wcsicmp(argv[1],L"add")){ if(fetch_current(default_entries)){ - fprintf(stderr,"%s: A service with the name %s already " - "exists.\n", + fwprintf(stderr,L"%s: A service with the name %s already " + L"exists.\n", argv[0],service_name); return 1; } real_service_name = generate_real_service_name(service_name); if(!fill_in_defaults(new_entries)){ - fprintf(stderr,"%s: Internal error.\n", argv[0]); + fwprintf(stderr,L"%s: Internal error.\n", argv[0]); return 1; } add = 1; } else { if(!fetch_current(new_entries)){ - fprintf(stderr,"%s: No service with the name %s exists.\n", + fwprintf(stderr,L"%s: No service with the name %s exists.\n", argv[0], service_name); return 1; } - real_service_name = new_entries[InternalServiceName].data.bytes; + real_service_name = new_entries[InternalServiceName].data.string; } if(!fill_in_defaults(default_entries)){ - fprintf(stderr,"%s: Internal error.\n", argv[0]); + fwprintf(stderr,L"%s: Internal error.\n", argv[0]); return 1; } /* make sure env is malloced... */ - new_entries[Env].data.bytes = envdup(new_entries[Env].data.bytes); + new_entries[Env].data.string = envdup(new_entries[Env].data.string); for(i = 3; i < argc; ++i){ switch((current = lookup_arg(argv[i]))){ @@ -712,43 +717,43 @@ int do_add_or_set(int argc, char **argv){ case WorkDir: case Args: if(i+1 >= argc){ - new_entries[current].data.bytes = - default_entries[current].data.bytes; + new_entries[current].data.string = + default_entries[current].data.string; new_entries[current].data.expand.unexpanded = default_entries[current].data.expand.unexpanded; } else { new_entries[current].data.expand.unexpanded = - new_entries[current].data.bytes = argv[i+1]; + new_entries[current].data.string = argv[i+1]; ++i; } break; case SName: - new_entries[Name].data.bytes = ""; + new_entries[Name].data.string = L""; case StopAction: case Name: if(i+1 >= argc || - *argv[i+1] == '-' || *argv[i+1] == '/'){ - new_entries[current].data.bytes = - default_entries[current].data.bytes; + *argv[i+1] == L'-' || *argv[i+1] == L'/'){ + new_entries[current].data.string = + default_entries[current].data.string; } else { - new_entries[current].data.bytes = argv[i+1]; + new_entries[current].data.string = argv[i+1]; ++i; } break; case OnFail: if(i+1 >= argc || - *argv[i+1] == '-' || *argv[i+1] == '/'){ + *argv[i+1] == L'-' || *argv[i+1] == L'/'){ new_entries[current].data.value = default_entries[current].data.value; } else { - if(!_stricmp(argv[i+1],"reboot")) + if(!_wcsicmp(argv[i+1],L"reboot")) new_entries[current].data.value = ON_FAIL_REBOOT; - else if(!_stricmp(argv[i+1],"restart")) + else if(!_wcsicmp(argv[i+1],L"restart")) new_entries[current].data.value = ON_FAIL_RESTART; - else if(!_stricmp(argv[i+1],"restart_always")) + else if(!_wcsicmp(argv[i+1],L"restart_always")) new_entries[current].data.value = ON_FAIL_RESTART_ALWAYS; else { - fprintf(stderr,"%s: Unrecognized keyword value %s.\n", + fwprintf(stderr,L"%s: Unrecognized keyword value %s.\n", argv[0],argv[i+1]); return 1; } @@ -757,18 +762,18 @@ int do_add_or_set(int argc, char **argv){ break; case DebugType: if(i+1 >= argc || - *argv[i+1] == '-' || *argv[i+1] == '/'){ + *argv[i+1] == L'-' || *argv[i+1] == L'/'){ new_entries[current].data.value = default_entries[current].data.value; } else { - if(!_stricmp(argv[i+1],"new")) + if(!_wcsicmp(argv[i+1],L"new")) new_entries[current].data.value = DEBUG_TYPE_NEW; - else if(!_stricmp(argv[i+1],"reuse")) + else if(!_wcsicmp(argv[i+1],L"reuse")) new_entries[current].data.value = DEBUG_TYPE_REUSE; - else if(!_stricmp(argv[i+1],"console")) + else if(!_wcsicmp(argv[i+1],L"console")) new_entries[current].data.value = DEBUG_TYPE_CONSOLE; else { - fprintf(stderr,"%s: Unrecognized keyword value %s.\n", + fwprintf(stderr,L"%s: Unrecognized keyword value %s.\n", argv[0],argv[i+1]); return 1; } @@ -777,18 +782,18 @@ int do_add_or_set(int argc, char **argv){ break; case Priority: if(i+1 >= argc || - *argv[i+1] == '-' || *argv[i+1] == '/'){ + *argv[i+1] == L'-' || *argv[i+1] == L'/'){ new_entries[current].data.value = default_entries[current].data.value; } else { - if(!_stricmp(argv[i+1],"high")) + if(!_wcsicmp(argv[i+1],L"high")) new_entries[current].data.value = HIGH_PRIORITY_CLASS; - else if(!_stricmp(argv[i+1],"low")) + else if(!_wcsicmp(argv[i+1],L"low")) new_entries[current].data.value = IDLE_PRIORITY_CLASS; - else if(!_stricmp(argv[i+1],"realtime")) + else if(!_wcsicmp(argv[i+1],L"realtime")) new_entries[current].data.value = REALTIME_PRIORITY_CLASS; else { - fprintf(stderr,"%s: Unrecognized keyword value %s.\n", + fwprintf(stderr,L"%s: Unrecognized keyword value %s.\n", argv[0],argv[i+1]); return 1; } @@ -798,64 +803,64 @@ int do_add_or_set(int argc, char **argv){ case Env: if(i+1 >= argc || - *argv[i+1] == '-' || *argv[i+1] == '/'){ - fprintf(stderr,"%s: %s requires a parameter.\n", + *argv[i+1] == L'-' || *argv[i+1] == L'/'){ + fwprintf(stderr,L"%s: %s requires a parameter.\n", argv[0],argv[i]); return 1; } - new_entries[current].data.bytes = - edit_env(argv[i+1], - new_entries[current].data.bytes); + new_entries[current].data.string = + edit_env(argv[i+1], new_entries[current].data.string); ++i; break; case InternalServiceName: if (!add) { - fprintf(stderr,"%s: %s only allowed when adding a new service.\n", + fwprintf(stderr,L"%s: %s only allowed when adding a new service.\n", argv[0],argv[i]); return 1; } if(i+1 >= argc){ - fprintf(stderr,"%s: %s requires a parameter.\n", + fwprintf(stderr,L"%s: %s requires a parameter.\n", argv[0],argv[i]); return 1; } new_entries[InternalServiceName].data.expand.unexpanded = - new_entries[InternalServiceName].data.bytes = argv[i+1]; + new_entries[InternalServiceName].data.string = argv[i+1]; ++i; /* Discard old, should maybe be fred' but we'll exit anyway */ - real_service_name = new_entries[InternalServiceName].data.bytes; + real_service_name = new_entries[InternalServiceName].data.string; break; default: - fprintf(stderr,"%s: Unrecognized option %s.\n", argv[0], + fwprintf(stderr,L"%s: Unrecognized option %s.\n", argv[0], argv[i]); return 1; } } - if(*new_entries[SName].data.bytes && - *new_entries[Name].data.bytes){ + if(*new_entries[SName].data.string && + *new_entries[Name].data.string){ #if 0 - fprintf(stderr,"%s: Both -sname and -name specified.\n", + fwprintf(stderr,L"%s: Both -sname and -name specified.\n", argv[0]); return 1; #else - new_entries[SName].data.bytes = ""; + new_entries[SName].data.string = L""; #endif } - if(add && !(*new_entries[SName].data.bytes) && - !(*new_entries[Name].data.bytes)){ - fprintf(stderr,"%s: Neither -sname nor -name specified.\n", + if(add && !(*new_entries[SName].data.string) && + !(*new_entries[Name].data.string)){ + fwprintf(stderr,L"%s: Neither -sname nor -name specified.\n", argv[0]); return 1; } + if(add && !install_service()){ - fprintf(stderr,"%s: Unable to register service with service manager.\n", + fwprintf(stderr,L"%s: Unable to register %s service with service manager.\n", argv[0], service_name); print_last_error(); return 1; } if(!set_interactive(new_entries[DebugType].data.value == DEBUG_TYPE_CONSOLE)){ - fprintf(stderr,"%s: Warning, could not set correct interactive mode.\n", + fwprintf(stderr,L"%s: Warning, could not set correct interactive mode. %s\n", argv[0], service_name); print_last_error(); /* Not severe or??? */ @@ -865,9 +870,9 @@ int do_add_or_set(int argc, char **argv){ set_keys(service_name, new_entries); /* Update service comment if needed */ if(set_comment) { - if (!set_service_comment(new_entries[Comment].data.bytes)) { - fprintf(stderr,"%s: Warning, could not set correct " - "service description (comment)", + if (!set_service_comment(new_entries[Comment].data.string)) { + fwprintf(stderr,L"%s: Warning, could not set correct " + L"service description (comment) %s", argv[0], service_name); print_last_error(); } @@ -878,64 +883,64 @@ int do_add_or_set(int argc, char **argv){ malloced, but we'll exit anyway, so... */ cleanup_old(); if(add) - printf("%s: Service %s added to system.\n", + wprintf(L"%s: Service %s added to system.\n", argv[0], service_name); else - printf("%s: Service %s updated.\n", + wprintf(L"%s: Service %s updated.\n", argv[0], service_name); return 0; } -int do_rename(int argc, char **argv){ +int do_rename(int argc, wchar_t **argv){ RegEntry *current = empty_reg_tab(); RegEntry *dummy = empty_reg_tab(); SC_HANDLE scm; SC_HANDLE service; if(argc < 3){ - fprintf(stderr,"%s: No old servicename given!\n",argv[0]); + fwprintf(stderr,L"%s: No old servicename given!\n",argv[0]); do_usage(argv[0]); return 1; } if(argc < 4){ - fprintf(stderr,"%s: No new servicename given!\n",argv[0]); + fwprintf(stderr,L"%s: No new servicename given!\n",argv[0]); do_usage(argv[0]); return 1; } service_name = argv[3]; if(fetch_current(dummy)){ - fprintf(stderr,"%s: A service with the name %s already " - "exists.\n", + fwprintf(stderr,L"%s: A service with the name %s already " + L"exists.\n", argv[0],service_name); return 1; } service_name = argv[2]; if(!fetch_current(current)){ - fprintf(stderr,"%s: Error, old service name %s does not exist.\n", + fwprintf(stderr,L"%s: Error, old service name %s does not exist.\n", argv[0],service_name); return 1; } - real_service_name = _strdup(current[InternalServiceName].data.bytes); + real_service_name = _wcsdup(current[InternalServiceName].data.string); if(!open_service_config(&scm,&service)){ - fprintf(stderr,"%s: Error, unable to communicate with service control" - " manager.\n", + fwprintf(stderr,L"%s: Error, unable to communicate with service control" + L" manager.\n", argv[0]); print_last_error(); return 1; } - if(!ChangeServiceConfig(service, - SERVICE_NO_CHANGE, - SERVICE_NO_CHANGE, - SERVICE_NO_CHANGE, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - argv[3])){ - fprintf(stderr,"%s: Error, unable to communicate with service control" - " manager.\n", + if(!ChangeServiceConfigW(service, + SERVICE_NO_CHANGE, + SERVICE_NO_CHANGE, + SERVICE_NO_CHANGE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + argv[3])){ + fwprintf(stderr,L"%s: Error, unable to communicate with service control" + L" manager.\n", argv[0]); print_last_error(); CloseServiceHandle(scm); @@ -946,154 +951,154 @@ int do_rename(int argc, char **argv){ CloseServiceHandle(service); if(remove_keys(service_name) != 0) - fprintf(stderr,"%s: Warning, old service parameter keys could not " - "be removed, continuing.\n", argv[0]); + fwprintf(stderr,L"%s: Warning, old service parameter keys could not " + L"be removed, continuing.\n", argv[0]); /* Update registry */ register_logkeys(); set_keys(argv[3], current); - printf("%s: Service %s renamed to %s.\n", + wprintf(L"%s: Service %s renamed to %s.\n", argv[0], service_name, argv[3]); return 0; } -int do_remove(int argc, char **argv){ +int do_remove(int argc, wchar_t **argv){ RegEntry *current = empty_reg_tab(); int rem_res; BOOL found; if(argc < 3){ - fprintf(stderr,"%s: No servicename given!\n",argv[0]); + fwprintf(stderr,L"%s: No servicename given!\n",argv[0]); do_usage(argv[0]); return 1; } service_name = argv[2]; found = fetch_current(current); if(found){ - real_service_name = _strdup(current[InternalServiceName].data.bytes); + real_service_name = _wcsdup(current[InternalServiceName].data.string); } else { - real_service_name = _strdup(service_name); + real_service_name = _wcsdup(service_name); } if(found) free_keys(current); if(stop_service() && !wait_service_trans(SERVICE_RUNNING, SERVICE_STOP_PENDING, SERVICE_STOPPED, 60)){ - fprintf(stderr,"%s: Failed to stop running service %s.\n", + fwprintf(stderr,L"%s: Failed to stop running service %s.\n", argv[0],service_name); print_last_error(); return 1; } if(!remove_service()){ - fprintf(stderr,"%s: Unable to remove service (not enough " - "privileges?)\n",argv[0]); + fwprintf(stderr,L"%s: Unable to remove service (not enough " + L"privileges?)\n",argv[0]); print_last_error(); return 1; } if((rem_res = remove_keys(service_name)) > 0){ - fprintf(stderr,"%s: Warning, service parameter keys belonged to old " - "erlsrv version.\n", argv[0]); + fwprintf(stderr,L"%s: Warning, service parameter keys belonged to old " + L"erlsrv version.\n", argv[0]); /* Backward compatibility... */ } else if(rem_res < 0) { - fprintf(stderr,"%s: Error, service parameter keys nonexistent.\n", + fwprintf(stderr,L"%s: Error, service parameter keys nonexistent.\n", argv[0]); return 1; } - printf("%s: Service %s removed from system.\n", + wprintf(L"%s: Service %s removed from system.\n", argv[0], service_name); return 0; } -BOOL list_one(char *servicename, RegEntry *keys, BOOL longlist){ - char *onfail; - char *prio; - char *debugtype; +BOOL list_one(wchar_t *servicename, RegEntry *keys, BOOL longlist){ + wchar_t *onfail; + wchar_t *prio; + wchar_t *debugtype; switch(keys[OnFail].data.value){ case ON_FAIL_RESTART: - onfail = "restart"; + onfail = L"restart"; break; case ON_FAIL_RESTART_ALWAYS: - onfail = "restart_always"; + onfail = L"restart_always"; break; case ON_FAIL_REBOOT: - onfail = "reboot"; + onfail = L"reboot"; break; default: - onfail = "ignore"; + onfail = L"ignore"; } switch(keys[DebugType].data.value){ case DEBUG_TYPE_NEW: - debugtype = "new"; + debugtype = L"new"; break; case DEBUG_TYPE_REUSE: - debugtype = "reuse"; + debugtype = L"reuse"; break; case DEBUG_TYPE_CONSOLE: - debugtype = "console"; + debugtype = L"console"; break; default: - debugtype = "none"; + debugtype = L"none"; } switch(keys[Priority].data.value){ case HIGH_PRIORITY_CLASS: - prio = "high"; + prio = L"high"; break; case IDLE_PRIORITY_CLASS: - prio = "low"; + prio = L"low"; break; case REALTIME_PRIORITY_CLASS: - prio = "realtime"; + prio = L"realtime"; break; case NORMAL_PRIORITY_CLASS: - prio = "default"; + prio = L"default"; break; default: - prio = "unknown/faulty"; + prio = L"unknown/faulty"; } if(longlist){ - char *env = envdup(keys[Env].data.bytes); - char **arg = env_to_arg(env); - char **pek = arg; - printf("Service name: %s\n", + wchar_t *env = envdup(keys[Env].data.string); + wchar_t **arg = env_to_arg(env); + wchar_t **pek = arg; + wprintf(L"Service name: %s\n", servicename); - printf("StopAction: %s\n", - keys[StopAction].data.bytes); - printf("OnFail: %s\n",onfail); - printf("Machine: %s\n", + wprintf(L"StopAction: %s\n", + keys[StopAction].data.string); + wprintf(L"OnFail: %s\n",onfail); + wprintf(L"Machine: %s\n", keys[Machine].data.expand.unexpanded); - printf("WorkDir: %s\n", + wprintf(L"WorkDir: %s\n", keys[WorkDir].data.expand.unexpanded); - if(*keys[SName].data.bytes) - printf("SName: %s\n", - keys[SName].data.bytes); + if(*keys[SName].data.string) + wprintf(L"SName: %s\n", + keys[SName].data.string); else - printf("Name: %s\n", - keys[Name].data.bytes); - printf("Priority: %s\n",prio); - printf("DebugType: %s\n",debugtype); - printf("Args: %s\n", + wprintf(L"Name: %s\n", + keys[Name].data.string); + wprintf(L"Priority: %s\n",prio); + wprintf(L"DebugType: %s\n",debugtype); + wprintf(L"Args: %s\n", keys[Args].data.expand.unexpanded); - printf("InternalServiceName: %s\n", - keys[InternalServiceName].data.bytes); - printf("Comment: %s\n", - keys[Comment].data.bytes); - printf("Env:\n"); + wprintf(L"InternalServiceName: %s\n", + keys[InternalServiceName].data.string); + wprintf(L"Comment: %s\n", + keys[Comment].data.string); + wprintf(L"Env:\n"); while(*pek){ - printf("\t%s\n",*pek); + wprintf(L"\t%s\n",*pek); ++pek; } /* env is easier to free...*/ env = arg_to_env(arg); free(env); } else { - printf("%s\t%s\t%s\t%s\t%s\n", + wprintf(L"%s\t%s\t%s\t%s\t%s\n", servicename, - (*keys[Name].data.bytes) ? - keys[Name].data.bytes : - keys[SName].data.bytes, + (*keys[Name].data.string) ? + keys[Name].data.string : + keys[SName].data.string, prio, onfail, keys[Args].data.expand.unexpanded); @@ -1102,15 +1107,15 @@ BOOL list_one(char *servicename, RegEntry *keys, BOOL longlist){ } -int do_list(int argc, char **argv){ +int do_list(int argc, wchar_t **argv){ if(argc < 3){ RegEntryDesc *all_keys = get_all_keys(); if(!all_keys){ - fprintf(stderr,"%s: No services found in registry.\n", + fwprintf(stderr,L"%s: No services found in registry.\n", argv[0]); return 0; } - printf("Service\t(S)Name\tPrio\tOnFail\tArgs\n"); + wprintf(L"Service\t(S)Name\tPrio\tOnFail\tArgs\n"); while(all_keys->servicename){ list_one(all_keys->servicename,all_keys->entries,FALSE); ++all_keys; @@ -1121,8 +1126,8 @@ int do_list(int argc, char **argv){ service_name = argv[2]; keys = get_keys(service_name); if(!keys){ - fprintf(stderr,"%s: Could not retrieve any " - "registered data for %s.\n",argv[0],service_name); + fwprintf(stderr,L"%s: Could not retrieve any " + L"registered data for %s.\n",argv[0],service_name); return 1; } list_one(service_name, keys, TRUE); @@ -1133,15 +1138,15 @@ int do_list(int argc, char **argv){ #define READ_CHUNK 100 #define ARGV_CHUNK 20 -char *safe_get_line(void){ +wchar_t *safe_get_line(void){ int lsize = READ_CHUNK; - char *line = malloc(READ_CHUNK); + wchar_t *line = malloc(READ_CHUNK*sizeof(wchar_t)); int pos = 0; int ch; - while((ch = getchar()) != EOF && ch != '\n'){ + while((ch = getwchar()) != EOF && ch != L'\n'){ if(pos + 1 >= lsize){ - line = realloc(line,(lsize += READ_CHUNK)); + line = realloc(line,(lsize += READ_CHUNK)*sizeof(wchar_t)); assert(line); } line[pos++] = ch; @@ -1150,22 +1155,22 @@ char *safe_get_line(void){ free(line); return NULL; } - line[pos] = '\0'; + line[pos] = L'\0'; return line; } -void read_arguments(int *pargc, char ***pargv){ +void read_arguments(int *pargc, wchar_t ***pargv){ int argc = 0; int asize = ARGV_CHUNK; - char **argv = malloc(ARGV_CHUNK*sizeof(char *)); - char *tmp; + wchar_t **argv = malloc(ARGV_CHUNK*sizeof(wchar_t *)); + wchar_t *tmp; argv[0] = (*pargv)[0]; argc = 1; while((tmp = safe_get_line()) != NULL){ if(argc + 1 >= asize){ - argv = realloc(argv,(asize += ARGV_CHUNK)*sizeof(char *)); + argv = realloc(argv,(asize += ARGV_CHUNK)*sizeof(wchar_t *)); assert(argv); } argv[argc++] = tmp; @@ -1258,40 +1263,54 @@ void release_lock(void) { -int interactive_main(int argc, char **argv){ - char *action = argv[1]; +int interactive_main(int argc, wchar_t **argv){ + wchar_t *action = argv[1]; int res; - + + _setmode(_fileno(stdin), _O_U8TEXT); /* set stdin to UTF8 */ + _setmode(_fileno(stdout), _O_U8TEXT); /* set stdout to UTF8 */ + _setmode(_fileno(stderr), _O_U8TEXT); /* set stderr to UTF8 */ + if (take_lock() != 0) { - fprintf(stderr,"%s: unable to acquire global lock (%s).\n",argv[0], + fwprintf(stderr,L"%s: unable to acquire global lock (%s).\n",argv[0], ERLSRV_INTERACTIVE_GLOBAL_SEMAPHORE); return 1; } - if(!_stricmp(action,"readargs")){ + if(!_wcsicmp(action,L"readargs")){ read_arguments(&argc,&argv); action = argv[1]; } - if(!_stricmp(action,"set") || !_stricmp(action,"add")) + +#ifdef HARDDEBUG + {int i; + for(i=0; i < argc; i++) { + fwprintf(stderr, L"%s ", argv[i]); + } + fwprintf(stderr, L"\n"); + } +#endif + + if(!_wcsicmp(action,L"set") || !_wcsicmp(action,L"add")) res = do_add_or_set(argc,argv); - else if(!_stricmp(action,"rename")) + else if(!_wcsicmp(action,L"rename")) res = do_rename(argc,argv); - else if(!_stricmp(action,"remove")) + else if(!_wcsicmp(action,L"remove")) res = do_remove(argc,argv); - else if(!_stricmp(action,"list")) + else if(!_wcsicmp(action,L"list")) res = do_list(argc,argv); - else if(!_stricmp(action,"start") || - !_stricmp(action,"start_disabled") || - !_stricmp(action,"stop") || - !_stricmp(action,"enable") || - !_stricmp(action,"disable")) + else if(!_wcsicmp(action,L"start") || + !_wcsicmp(action,L"start_disabled") || + !_wcsicmp(action,L"stop") || + !_wcsicmp(action,L"enable") || + !_wcsicmp(action,L"disable")) res = do_manage(argc,argv); - else if(_stricmp(action,"?") && - _stricmp(action,"/?") && - _stricmp(action,"-?") && - *action != 'h' && - *action != 'H') { - fprintf(stderr,"%s: action %s not implemented.\n",argv[0],action); + else if(_wcsicmp(action,L"?") && + _wcsicmp(action,L"/?") && + _wcsicmp(action,L"-?") && + *action != L'h' && + *action != L'H') { + fwprintf(stderr,L"%s: action %s not implemented.\n",argv[0],action); do_usage(argv[0]); res = 1; } else { diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.h b/erts/etc/win32/erlsrv/erlsrv_interactive.h index 23e69e508d..bc6e55fdef 100644 --- a/erts/etc/win32/erlsrv/erlsrv_interactive.h +++ b/erts/etc/win32/erlsrv/erlsrv_interactive.h @@ -21,6 +21,6 @@ #define ERLSRV_INTERACTIVE_GLOBAL_SEMAPHORE "{468d6954-e355-415f-968f-d257cb0feef4}" -int interactive_main(int argc, char **argv); +int interactive_main(int argc, wchar_t **argv); #endif /* _ERLSRV_INTERACTIVE_H */ diff --git a/erts/etc/win32/erlsrv/erlsrv_main.c b/erts/etc/win32/erlsrv/erlsrv_main.c index 920a4a1827..6d8e208fc8 100644 --- a/erts/etc/win32/erlsrv/erlsrv_main.c +++ b/erts/etc/win32/erlsrv/erlsrv_main.c @@ -25,7 +25,7 @@ #include "erlsrv_interactive.h" #include "erlsrv_service.h" -int main(int argc, char **argv){ +int wmain(int argc, wchar_t **argv){ if(argc > 1) return interactive_main(argc,argv); else diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.c b/erts/etc/win32/erlsrv/erlsrv_registry.c index c1aa9f2b67..ad50da89a4 100644 --- a/erts/etc/win32/erlsrv/erlsrv_registry.c +++ b/erts/etc/win32/erlsrv/erlsrv_registry.c @@ -24,38 +24,37 @@ #include "erlsrv_global.h" #include "erlsrv_registry.h" -#define LOG_TYPE "System" -#define LOG_ROOT \ -"SYSTEM\\CurrentControlSet\\Services\\EventLog\\" LOG_TYPE "\\" +#define LOG_TYPE L"System" +#define LOG_ROOT L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\" LOG_TYPE L"\\" #define LOG_APP_KEY APP_NAME #define BASE_KEY HKEY_LOCAL_MACHINE #define PRODUCT_NAME APP_NAME -#define OLD_PRODUCT_VERSION "1.0" -#define PRODUCT_VERSION "1.1" -#define PROG_KEY "SOFTWARE\\Ericsson\\Erlang\\" PRODUCT_NAME "\\" PRODUCT_VERSION -#define OLD_PROG_KEY "SOFTWARE\\Ericsson\\Erlang\\" PRODUCT_NAME "\\" OLD_PRODUCT_VERSION +#define OLD_PRODUCT_VERSION L"1.0" +#define PRODUCT_VERSION L"1.1" +#define PROG_KEY L"SOFTWARE\\Ericsson\\Erlang\\" PRODUCT_NAME L"\\" PRODUCT_VERSION +#define OLD_PROG_KEY L"SOFTWARE\\Ericsson\\Erlang\\" PRODUCT_NAME L"\\" OLD_PRODUCT_VERSION #define MAX_KEY_LEN MAX_PATH -static const char * const noString = "\0"; +static const wchar_t * const noString = L"\0"; #define MAX_MANDATORY_REG_ENTRY 10 /* InternalServiceName == reg_entries[10] */ static RegEntry reg_entries[] = { - {"StopAction",REG_SZ,NULL}, - {"OnFail",REG_DWORD,NULL}, - {"Machine",REG_EXPAND_SZ,NULL}, - {"Env", REG_MULTI_SZ,NULL}, - {"WorkDir", REG_EXPAND_SZ,NULL}, - {"Priority",REG_DWORD,NULL}, - {"SName",REG_SZ,NULL}, - {"Name",REG_SZ,NULL}, - {"Args",REG_EXPAND_SZ,NULL}, - {"DebugType",REG_DWORD,NULL}, - {"InternalServiceName",REG_SZ,NULL}, + {L"StopAction",REG_SZ,NULL}, + {L"OnFail",REG_DWORD,NULL}, + {L"Machine",REG_EXPAND_SZ,NULL}, + {L"Env", REG_MULTI_SZ,NULL}, + {L"WorkDir", REG_EXPAND_SZ,NULL}, + {L"Priority",REG_DWORD,NULL}, + {L"SName",REG_SZ,NULL}, + {L"Name",REG_SZ,NULL}, + {L"Args",REG_EXPAND_SZ,NULL}, + {L"DebugType",REG_DWORD,NULL}, + {L"InternalServiceName",REG_SZ,NULL}, /* Non mandatory follows */ - {"Comment",REG_SZ,NULL} + {L"Comment",REG_SZ,NULL} }; @@ -73,8 +72,8 @@ void free_keys(RegEntry *keys){ for(i=0;i MAX_KEY_LEN) + if(wcslen(PROG_KEY) + wcslen(servicename) + 2 > MAX_KEY_LEN) goto error; - sprintf(key_to_open,"%s\\%s",PROG_KEY,servicename); + swprintf(key_to_open,MAX_KEY_LEN,L"%s\\%s",PROG_KEY,servicename); - if(RegOpenKeyEx(BASE_KEY, - key_to_open, - 0, - KEY_QUERY_VALUE, - &prog_key) != ERROR_SUCCESS) + if(RegOpenKeyExW(BASE_KEY, + key_to_open, + 0, + KEY_QUERY_VALUE, + &prog_key) != ERROR_SUCCESS) goto error; key_opened = 1; @@ -128,12 +127,12 @@ RegEntry *get_keys(char *servicename){ for(i=0;i tmpbuflen){ - tmpbuf=realloc(tmpbuf,tmpbuflen=ret); + tmpbuf=realloc(tmpbuf,(tmpbuflen=ret)*sizeof(wchar_t)); } else { - copy = strdup(tmpbuf); + copy = wcsdup(tmpbuf); free(tmpbuf); break; } } - res[i].data.expand.unexpanded = strdup(val_data); + res[i].data.expand.unexpanded = wcsdup(val_data); } case REG_MULTI_SZ: case REG_SZ: if(!copy){ if(!val_datalen || - ((val_datalen == 1 && val_data[0] == '\0') || - (val_datalen == 2 && val_data[0] == '\0' && - val_data[1] == '\0'))){ - copy = (char *) noString; + ((val_datalen == 2 && val_data[0] == L'\0') || + (val_datalen == 4 && val_data[0] == L'\0' && + val_data[1] == L'\0'))){ + copy = (wchar_t *) noString; } else { - copy = malloc(val_datalen); - memcpy(copy,val_data,val_datalen); + copy = malloc(val_datalen); /* val_datalen in bytes */ + memcpy(copy,val_data,val_datalen); } } - res[i].data.bytes = copy; + res[i].data.string = copy; break; case REG_DWORD: memcpy(&res[i].data.value,val_data,sizeof(DWORD)); @@ -222,32 +221,32 @@ error: return NULL; } -int set_keys(char *servicename, RegEntry *keys){ +int set_keys(wchar_t *servicename, RegEntry *keys){ HKEY prog_key; int key_opened = 0; int i; - char key_to_open[MAX_KEY_LEN]; + wchar_t key_to_open[MAX_KEY_LEN]; DWORD disposition; - if(strlen(PROG_KEY) + strlen(servicename) + 2 > MAX_KEY_LEN) + if(wcslen(PROG_KEY) + wcslen(servicename) + 2 > MAX_KEY_LEN) goto error; - sprintf(key_to_open,"%s\\%s",PROG_KEY,servicename); + swprintf(key_to_open,MAX_KEY_LEN,L"%s\\%s",PROG_KEY,servicename); - if(RegOpenKeyEx(BASE_KEY, - key_to_open, - 0, - KEY_SET_VALUE, - &prog_key) != ERROR_SUCCESS){ - if(RegCreateKeyEx(BASE_KEY, - key_to_open, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - KEY_SET_VALUE, - NULL, - &prog_key, - &disposition) != ERROR_SUCCESS) - goto error; + if(RegOpenKeyExW(BASE_KEY, + key_to_open, + 0, + KEY_SET_VALUE, + &prog_key) != ERROR_SUCCESS){ + if(RegCreateKeyExW(BASE_KEY, + key_to_open, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, + NULL, + &prog_key, + &disposition) != ERROR_SUCCESS) + goto error; } key_opened = 1; @@ -258,19 +257,19 @@ int set_keys(char *servicename, RegEntry *keys){ int j; switch(keys[i].type){ case REG_SZ: - ptr = keys[i].data.bytes; - siz = strlen(ptr)+1; + ptr = keys[i].data.string; + siz = (wcslen(ptr)+1)*sizeof(wchar_t); break; case REG_EXPAND_SZ: ptr = keys[i].data.expand.unexpanded; - siz = strlen(ptr)+1; + siz = (wcslen(ptr)+1)*sizeof(wchar_t); break; case REG_MULTI_SZ: - ptr = keys[i].data.bytes; - for(j=0;!(((char *)ptr)[j] == '\0' && - ((char *)ptr)[j+1] == '\0');++j) + ptr = keys[i].data.string; + for(j=0;!(((wchar_t *)ptr)[j] == L'\0' && + ((wchar_t *)ptr)[j+1] == L'\0');++j) ; - siz=(DWORD)j+2; + siz=(j+2)*sizeof(wchar_t); break; case REG_DWORD: ptr = &keys[i].data.value; @@ -280,15 +279,15 @@ int set_keys(char *servicename, RegEntry *keys){ goto error; } #ifdef HARDDEBUG - fprintf(stderr,"%s %s:%d\n",keys[i].name, - (keys[i].type == REG_DWORD) ? "(dword)" : ptr,siz); + fprintf(stderr,"%S %S:%d\n",keys[i].name, + (keys[i].type == REG_DWORD) ? L"(dword)" : ptr,siz); #endif - if(RegSetValueEx(prog_key, - keys[i].name, - 0, - keys[i].type, - ptr, - siz) != ERROR_SUCCESS) + if(RegSetValueExW(prog_key, + keys[i].name, + 0, + keys[i].type, + ptr, + siz) != ERROR_SUCCESS) goto error; } RegCloseKey(prog_key); @@ -299,15 +298,15 @@ error: return 1; } -static int do_remove_keys(char *servicename, const char *prog_key_name){ +static int do_remove_keys(wchar_t *servicename, const wchar_t *prog_key_name){ HKEY prog_key; - if(RegOpenKeyEx(BASE_KEY, - prog_key_name, - 0, - KEY_ALL_ACCESS, - &prog_key) != ERROR_SUCCESS) + if(RegOpenKeyExW(BASE_KEY, + prog_key_name, + 0, + KEY_ALL_ACCESS, + &prog_key) != ERROR_SUCCESS) return -1; - if(RegDeleteKey(prog_key,servicename) != ERROR_SUCCESS){ + if(RegDeleteKeyW(prog_key,servicename) != ERROR_SUCCESS){ RegCloseKey(prog_key); return -1; } @@ -315,7 +314,7 @@ static int do_remove_keys(char *servicename, const char *prog_key_name){ return 0; } -int remove_keys(char *servicename){ +int remove_keys(wchar_t *servicename){ int ret; if((ret = do_remove_keys(servicename, PROG_KEY)) < 0){ @@ -335,33 +334,33 @@ RegEntryDesc *get_all_keys(void){ HKEY prog_key; int key_opened = 0; DWORD enum_index; - char name[MAX_KEY_LEN]; + wchar_t name[MAX_KEY_LEN]; DWORD namelen; - char class[MAX_KEY_LEN]; + wchar_t class[MAX_KEY_LEN]; DWORD classlen; FILETIME ft; res[ndx].servicename = NULL; - if(RegOpenKeyEx(BASE_KEY, PROG_KEY, 0, - KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, - &prog_key) != ERROR_SUCCESS) + if(RegOpenKeyExW(BASE_KEY, PROG_KEY, 0, + KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, + &prog_key) != ERROR_SUCCESS) goto error; key_opened = 1; for(enum_index = 0, namelen = MAX_KEY_LEN, classlen = MAX_KEY_LEN; - ERROR_SUCCESS == RegEnumKeyEx(prog_key, - enum_index, - name, - &namelen, - NULL, - class, - &classlen, - &ft); + ERROR_SUCCESS == RegEnumKeyExW(prog_key, + enum_index, + name, + &namelen, + NULL, + class, + &classlen, + &ft); ++enum_index, namelen = MAX_KEY_LEN, classlen = MAX_KEY_LEN){ if(ndx >= res_siz - 1) res = realloc(res, (res_siz += 10)*sizeof(RegEntryDesc)); if(!(res[ndx].entries = get_keys(name))) - goto error; - res[ndx].servicename = strdup(name); + goto error; + res[ndx].servicename = wcsdup(name); res[++ndx].servicename = NULL; } RegCloseKey(prog_key); @@ -380,24 +379,24 @@ int register_logkeys(void){ EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; DWORD catcount=1; - char filename[2048]; + wchar_t filename[2048]; DWORD fnsiz=2048; - if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, - LOG_ROOT LOG_APP_KEY, 0, - NULL, REG_OPTION_NON_VOLATILE, - KEY_SET_VALUE, NULL, - &key, &disposition) != ERROR_SUCCESS) + if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, + LOG_ROOT LOG_APP_KEY, 0, + NULL, REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, NULL, + &key, &disposition) != ERROR_SUCCESS) return -1; - if(!GetModuleFileName(NULL, filename, fnsiz)) + if(!GetModuleFileNameW(NULL, filename, fnsiz)) return -1; - if(RegSetValueEx(key, "EventMessageFile", - 0, REG_EXPAND_SZ, (LPBYTE) filename, - strlen(filename)+1) != ERROR_SUCCESS) + if(RegSetValueExW(key, L"EventMessageFile", + 0, REG_EXPAND_SZ, (LPBYTE) filename, + (wcslen(filename)+1)*sizeof(wchar_t)) != ERROR_SUCCESS) return -1; - if(RegSetValueEx(key, "TypesSupported", - 0, REG_DWORD, (LPBYTE) &types, - sizeof(DWORD)) != ERROR_SUCCESS) + if(RegSetValueExW(key, L"TypesSupported", + 0, REG_DWORD, (LPBYTE) &types, + sizeof(DWORD)) != ERROR_SUCCESS) return -1; return 0; } diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.h b/erts/etc/win32/erlsrv/erlsrv_registry.h index fbccc5416a..4be10e9ff2 100644 --- a/erts/etc/win32/erlsrv/erlsrv_registry.h +++ b/erts/etc/win32/erlsrv/erlsrv_registry.h @@ -20,20 +20,20 @@ #define _ERLSRV_REGISTRY_H typedef struct _reg_entry { - char *name; + wchar_t *name; DWORD type; union { - char *bytes; + wchar_t *string; DWORD value; struct { - char *bytes; - char *unexpanded; + wchar_t *string; + wchar_t *unexpanded; } expand; } data; } RegEntry; typedef struct _reg_entry_desc { - char *servicename; + wchar_t *servicename; RegEntry *entries; } RegEntryDesc; @@ -67,10 +67,10 @@ extern int num_reg_entries; RegEntry *empty_reg_tab(void); void free_keys(RegEntry *keys); void free_all_keys(RegEntryDesc *descs); -RegEntry *get_keys(char *servicename); -int set_keys(char *servicename, RegEntry *keys); +RegEntry *get_keys(wchar_t *servicename); +int set_keys(wchar_t *servicename, RegEntry *keys); RegEntryDesc *get_all_keys(void); -int remove_keys(char *servicename); +int remove_keys(wchar_t *servicename); int register_logkeys(void); #endif /* _ERLSRV_REGISTRY_H */ diff --git a/erts/etc/win32/erlsrv/erlsrv_service.c b/erts/etc/win32/erlsrv/erlsrv_service.c index 58738ee445..2e56c579a2 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-2012. All Rights Reserved. + * Copyright Ericsson AB 1998-2013. All Rights Reserved. * * The 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,8 +99,8 @@ static BOOL reset_current(){ } static VOID WINAPI handler(DWORD control){ - char buffer[1024]; - sprintf(buffer,"handler called with control = %d.",(int) control); + wchar_t buffer[1024]; + swprintf(buffer,1024,L"handler called with control = %d.",(int) control); log_debug(buffer); switch(control){ case SERVICE_CONTROL_STOP: @@ -119,7 +119,7 @@ typedef struct _server_info { RegEntry *keys; PROCESS_INFORMATION info; HANDLE erl_stdin; - char *event_name; + wchar_t *event_name; } ServerInfo; @@ -138,7 +138,7 @@ static BOOL reset_acl(SaveAclStruct *save_acl){ return FALSE; if(!OpenProcessToken(GetCurrentProcess(), TOKEN_READ|TOKEN_WRITE,&tokenh)){ - log_warning("Failed to open access token."); + log_warning(L"Failed to open access token."); return FALSE; } save_acl->initialized = FALSE; @@ -146,7 +146,7 @@ static BOOL reset_acl(SaveAclStruct *save_acl){ TokenDefaultDacl, save_acl->defdacl, sizeof(TOKEN_DEFAULT_DACL))){ - log_warning("Failed to get default ACL from token."); + log_warning(L"Failed to get default ACL from token."); CloseHandle(tokenh); LocalFree(save_acl->defdacl); LocalFree(save_acl->newacl); @@ -177,7 +177,7 @@ static BOOL new_acl(SaveAclStruct *save_acl){ save_acl->initialized = FALSE; if(!OpenProcessToken(GetCurrentProcess(), TOKEN_READ|TOKEN_WRITE,&tokenh)){ - log_warning("Failed to open access token."); + log_warning(L"Failed to open access token."); return FALSE; } save_acl->defdacl = &dummy; @@ -188,7 +188,7 @@ static BOOL new_acl(SaveAclStruct *save_acl){ sizeof(TOKEN_DEFAULT_DACL), &required); if(required == 0){ - log_warning("Failed to get any ACL info from token."); + log_warning(L"Failed to get any ACL info from token."); CloseHandle(tokenh); return FALSE; } @@ -200,7 +200,7 @@ static BOOL new_acl(SaveAclStruct *save_acl){ &required)){ #ifdef HARDDEBUG { - char *mes; + wchar_t *mes; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, @@ -213,7 +213,7 @@ static BOOL new_acl(SaveAclStruct *save_acl){ LocalFree(mes); } #endif - log_warning("Failed to get default ACL from token."); + log_warning(L"Failed to get default ACL from token."); CloseHandle(tokenh); return FALSE; } @@ -221,7 +221,7 @@ static BOOL new_acl(SaveAclStruct *save_acl){ oldacl = save_acl->defdacl->DefaultDacl; if(!GetAclInformation(oldacl, &si, sizeof(si), AclSizeInformation)){ - log_warning("Failed to get size information for ACL"); + log_warning(L"Failed to get size information for ACL"); CloseHandle(tokenh); return FALSE; } @@ -237,7 +237,7 @@ static BOOL new_acl(SaveAclStruct *save_acl){ 0, 0, &extra_sid)){ - log_warning("Failed to initialize administrator SID."); + log_warning(L"Failed to initialize administrator SID."); CloseHandle(tokenh); return FALSE; } @@ -248,7 +248,7 @@ static BOOL new_acl(SaveAclStruct *save_acl){ newacl = LocalAlloc(LPTR,newsize); if(!InitializeAcl(newacl, newsize, ACL_REVISION)){ - log_warning("Failed to initialize new ACL."); + log_warning(L"Failed to initialize new ACL."); LocalFree(newacl); FreeSid(extra_sid); CloseHandle(tokenh); @@ -258,7 +258,7 @@ static BOOL new_acl(SaveAclStruct *save_acl){ for(i=0;i<((int)si.AceCount);++i){ ACE_HEADER *ace_header; if (!GetAce (oldacl, i, &ace_header)){ - log_warning("Failed to get ACE from old ACL."); + log_warning(L"Failed to get ACE from old ACL."); LocalFree(newacl); FreeSid(extra_sid); CloseHandle(tokenh); @@ -266,7 +266,7 @@ static BOOL new_acl(SaveAclStruct *save_acl){ } if(!AddAce(newacl,ACL_REVISION,0xffffffff,ace_header, ace_header->AceSize)){ - log_warning("Failed to set ACE in new ACL."); + log_warning(L"Failed to set ACE in new ACL."); LocalFree(newacl); FreeSid(extra_sid); CloseHandle(tokenh); @@ -277,7 +277,7 @@ static BOOL new_acl(SaveAclStruct *save_acl){ ACL_REVISION2, PROCESS_ALL_ACCESS, extra_sid)){ - log_warning("Failed to add system ACE to new ACL."); + log_warning(L"Failed to add system ACE to new ACL."); LocalFree(newacl); FreeSid(extra_sid); return FALSE; @@ -288,7 +288,7 @@ static BOOL new_acl(SaveAclStruct *save_acl){ TokenDefaultDacl, &newdacl, sizeof(newdacl))){ - log_warning("Failed to set token information"); + log_warning(L"Failed to set token information"); LocalFree(newacl); FreeSid(extra_sid); CloseHandle(tokenh); @@ -302,18 +302,18 @@ static BOOL new_acl(SaveAclStruct *save_acl){ return TRUE; } -static char **find_arg(char **arg, char *str){ - char *tmp; +static wchar_t **find_arg(wchar_t **arg, wchar_t *str){ + wchar_t *tmp; int len; - str = strdup(str); - if((tmp = strchr(str,'=')) == NULL) + str = wcsdup(str); + if((tmp = wcschr(str,L'=')) == NULL) goto fail; tmp++; - *tmp = '\0'; + *tmp = L'\0'; len = tmp - str; while(*arg != NULL){ - if(!_strnicmp(*arg,str,len)){ + if(!_wcsnicmp(*arg,str,len)){ free(str); return arg; } @@ -324,11 +324,11 @@ fail: return NULL; } -static char **merge_environment(char *current, char *add){ - char **c_arg = env_to_arg(envdup(current)); - char **a_arg = env_to_arg(envdup(add)); - char **new; - char **tmp; +static wchar_t **merge_environment(wchar_t *current, wchar_t *add){ + wchar_t **c_arg = env_to_arg(envdup(current)); + wchar_t **a_arg = env_to_arg(envdup(add)); + wchar_t **new; + wchar_t **tmp; int i,j; for(i=0;c_arg[i] != NULL;++i) @@ -336,19 +336,19 @@ static char **merge_environment(char *current, char *add){ for(j=0;a_arg[j] != NULL;++j) ; - new = malloc(sizeof(char *)*(i + j + 3)); + new = malloc(sizeof(wchar_t *)*(i + j + 3)); for(i = 0; c_arg[i] != NULL; ++i) - new[i] = strdup(c_arg[i]); + new[i] = wcsdup(c_arg[i]); new[i] = NULL; for(j = 0; a_arg[j] != NULL; ++j){ if((tmp = find_arg(new,a_arg[j])) != NULL){ free(*tmp); - *tmp = strdup(a_arg[j]); + *tmp = wcsdup(a_arg[j]); } else { - new[i++] = strdup(a_arg[j]); + new[i++] = wcsdup(a_arg[j]); new[i] = NULL; } } @@ -358,12 +358,12 @@ static char **merge_environment(char *current, char *add){ } -static char *get_next_debug_file(char *prefix){ - char *buffer = malloc(strlen(prefix)+12); +static wchar_t *get_next_debug_file(wchar_t *prefix){ + wchar_t *buffer = malloc((wcslen(prefix)+12)*sizeof(wchar_t)); int i; for(i=1;i<100;++i){ - sprintf(buffer,"%s.%d",prefix,i); - if(GetFileAttributes(buffer) == 0xFFFFFFFF) + swprintf(buffer,wcslen(prefix)+12,L"%s.%d",prefix,i); + if(GetFileAttributesW(buffer) == 0xFFFFFFFF) return buffer; } return NULL; @@ -372,56 +372,66 @@ static char *get_next_debug_file(char *prefix){ static BOOL start_a_service(ServerInfo *srvi){ - STARTUPINFO start; - char execbuff[MAX_PATH*4]; /* FIXME: Can get overflow! */ - char namebuff[MAX_PATH]; - char errbuff[MAX_PATH*4]; /* hmmm.... */ + STARTUPINFOW start; + wchar_t namebuff[MAX_PATH]; + wchar_t *execbuff; + wchar_t *errbuff; HANDLE write_pipe = NULL, read_pipe = NULL; SECURITY_ATTRIBUTES pipe_security; SECURITY_ATTRIBUTES attr; HANDLE nul; SaveAclStruct save_acl; - char *my_environ; + wchar_t *my_environ; BOOL console_allocated = FALSE; + int bufflen=0; - if(!(*(srvi->keys[Env].data.bytes))){ + if(!(*(srvi->keys[Env].data.string))){ my_environ = NULL; } else { - char *tmp; - char **merged = merge_environment((tmp = GetEnvironmentStrings()), - srvi->keys[Env].data.bytes); - FreeEnvironmentStrings(tmp); + wchar_t *tmp; + wchar_t **merged = merge_environment((tmp = GetEnvironmentStringsW()), + srvi->keys[Env].data.string); + FreeEnvironmentStringsW(tmp); my_environ = arg_to_env(merged); } - if(!*(srvi->keys[Machine].data.bytes) || - (!*(srvi->keys[SName].data.bytes) && - !*(srvi->keys[Name].data.bytes))){ - log_error("Not enough parameters for erlang service."); + if(!*(srvi->keys[Machine].data.string) || + (!*(srvi->keys[SName].data.string) && + !*(srvi->keys[Name].data.string))){ + log_error(L"Not enough parameters for erlang service."); if(my_environ) free(my_environ); return FALSE; } - if(*(srvi->keys[SName].data.bytes)) - sprintf(namebuff,"-nohup -sname %s",srvi->keys[SName].data.bytes); + if(*(srvi->keys[SName].data.string)) + swprintf(namebuff,MAX_PATH,L"-nohup -sname %s",srvi->keys[SName].data.string); else - sprintf(namebuff,"-nohup -name %s",srvi->keys[Name].data.bytes); + swprintf(namebuff,MAX_PATH,L"-nohup -name %s",srvi->keys[Name].data.string); if(srvi->keys[DebugType].data.value == DEBUG_TYPE_CONSOLE) - strcat(namebuff," -keep_window"); + wcscat(namebuff,L" -keep_window"); + + bufflen = MAX_PATH + + wcslen(srvi->keys[Machine].data.string) + + wcslen(srvi->event_name) + + wcslen(namebuff) + + wcslen(srvi->keys[Args].data.string); + + execbuff = malloc(bufflen * sizeof(wchar_t)); + errbuff = malloc((MAX_PATH + bufflen) * sizeof(wchar_t)); if (srvi->event_name != NULL) { - sprintf(execbuff,"\"%s\" -service_event %s %s %s", - srvi->keys[Machine].data.bytes, - srvi->event_name, - namebuff, - srvi->keys[Args].data.bytes); + swprintf(execbuff,bufflen,L"\"%s\" -service_event %s %s %s", + srvi->keys[Machine].data.string, + srvi->event_name, + namebuff, + srvi->keys[Args].data.string); } else { - sprintf(execbuff,"\"%s\" %s %s", - srvi->keys[Machine].data.bytes, - namebuff, - srvi->keys[Args].data.bytes); + swprintf(execbuff,bufflen,L"\"%s\" %s %s", + srvi->keys[Machine].data.string, + namebuff, + srvi->keys[Args].data.string); } memset (&start, 0, sizeof (start)); @@ -435,45 +445,49 @@ static BOOL start_a_service(ServerInfo *srvi){ if(console_allocated = AllocConsole()) SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),coord); else - log_warning("Unable to allocate debugging console!"); - } else if(*(srvi->keys[StopAction].data.bytes) || + log_warning(L"Unable to allocate debugging console!"); + } else if(*(srvi->keys[StopAction].data.string) || srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG){ pipe_security.nLength = sizeof(pipe_security); pipe_security.lpSecurityDescriptor = NULL; pipe_security.bInheritHandle = TRUE; if(!CreatePipe(&read_pipe,&write_pipe,&pipe_security,0)){ - log_error("Could not create pipe for erlang service."); + log_error(L"Could not create pipe for erlang service."); if(my_environ) - free(my_environ); + free(my_environ); + free(execbuff); + free(errbuff); return FALSE; } if(srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG){ - char *filename; - if(*(srvi->keys[WorkDir].data.bytes)){ - filename = malloc(strlen(srvi->keys[WorkDir].data.bytes) + 1 + - strlen(service_name)+strlen(".debug")+1); - sprintf(filename,"%s\\%s.debug", - srvi->keys[WorkDir].data.bytes, - service_name); + wchar_t *filename; + if(*(srvi->keys[WorkDir].data.string)){ + int filenamelen = (wcslen(srvi->keys[WorkDir].data.string) + 1 + + wcslen(service_name)+wcslen(L".debug")+1); + filename = malloc(filenamelen*sizeof(wchar_t)); + swprintf(filename,filenamelen,L"%s\\%s.debug", + srvi->keys[WorkDir].data.string, + service_name); } else { - filename = malloc(strlen(service_name)+strlen(".debug")+1); - sprintf(filename,"%s.debug",service_name); + int filenamelen = wcslen(service_name)+wcslen(L".debug")+1; + filename = malloc(filenamelen*sizeof(wchar_t)); + swprintf(filename,filenamelen,L"%s.debug",service_name); } log_debug(filename); if(srvi->keys[DebugType].data.value == DEBUG_TYPE_NEW){ - char *tmpfn = get_next_debug_file(filename); + wchar_t *tmpfn = get_next_debug_file(filename); if(tmpfn){ free(filename); filename = tmpfn; } else { - log_warning("Number of debug files exceeds system defined " - "limit, reverting to DebugType: reuse. "); + log_warning(L"Number of debug files exceeds system defined " + L"limit, reverting to DebugType: reuse. "); } } - nul = CreateFile(filename, + nul = CreateFileW(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &pipe_security, @@ -492,9 +506,9 @@ static BOOL start_a_service(ServerInfo *srvi){ } if(nul == NULL){ log_error((srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG) - ? "Could not create debug file. " - "(Working directory not valid?)" - : "Cold not open NUL!"); + ? L"Could not create debug file. " + L"(Working directory not valid?)" + : L"Cold not open NUL!"); start.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); start.hStdError = GetStdHandle(STD_ERROR_HANDLE); } @@ -510,23 +524,24 @@ static BOOL start_a_service(ServerInfo *srvi){ new_acl(&save_acl); - if(!CreateProcess(NULL, - execbuff, - &attr, - NULL, - (read_pipe != NULL), - CREATE_DEFAULT_ERROR_MODE | - (srvi->keys[Priority].data.value), - my_environ, - (*(srvi->keys[WorkDir].data.bytes)) ? - srvi->keys[WorkDir].data.bytes : NULL, - &start, - &(srvi->info))){ - sprintf(errbuff,"Could not start erlang service " - "with commandline \"%s\".", - service_name, - execbuff - ); + if(!CreateProcessW(NULL, + execbuff, + &attr, + NULL, + (read_pipe != NULL), + CREATE_UNICODE_ENVIRONMENT | + CREATE_DEFAULT_ERROR_MODE | + (srvi->keys[Priority].data.value), + my_environ, + (*(srvi->keys[WorkDir].data.string)) ? + srvi->keys[WorkDir].data.string : NULL, + &start, + &(srvi->info))){ + swprintf(errbuff,bufflen+MAX_PATH,L"Could not start erlang service \"%s\"" + L"with commandline [%s].", + service_name, + execbuff + ); log_error(errbuff); if(read_pipe != NULL){ CloseHandle(read_pipe); @@ -539,14 +554,16 @@ static BOOL start_a_service(ServerInfo *srvi){ reset_acl(&save_acl); if(my_environ) free(my_environ); + free(execbuff); + free(errbuff); return FALSE; } if(console_allocated) FreeConsole(); #ifdef HARDDEBUG - sprintf(errbuff, - "Started %s with the following commandline: " - "%s",service_name,execbuff); + swprintf(errbuff,bufflen+MAX_PATH, + L"Started %s with the following commandline: %s", + service_name,execbuff); log_debug(errbuff); #endif if(read_pipe != NULL){ @@ -559,19 +576,21 @@ static BOOL start_a_service(ServerInfo *srvi){ reset_acl(&save_acl); if(my_environ) free(my_environ); + free(execbuff); + free(errbuff); return TRUE; } -static HANDLE create_erlang_event(char *event_name) +static HANDLE create_erlang_event(wchar_t *event_name) { HANDLE e; - if ((e = OpenEvent(EVENT_ALL_ACCESS,FALSE,event_name)) == NULL) { - if ((e = CreateEvent(NULL, TRUE, FALSE, event_name)) == NULL) { - log_warning("Could not create or access erlang termination event"); + if ((e = OpenEventW(EVENT_ALL_ACCESS,FALSE,event_name)) == NULL) { + if ((e = CreateEventW(NULL, TRUE, FALSE, event_name)) == NULL) { + log_warning(L"Could not create or access erlang termination event"); } } else { if (!ResetEvent(e)) { - log_warning("Could not reset erlang termination event."); + log_warning(L"Could not reset erlang termination event."); } } return e; @@ -580,17 +599,18 @@ static HANDLE create_erlang_event(char *event_name) static BOOL stop_erlang(ServerInfo *srvi, int waithint, int *checkpoint){ DWORD written = 0; - char *action = srvi->keys[StopAction].data.bytes; - DWORD towrite = strlen(action)+1; - char *toerl; + wchar_t *wc_action = srvi->keys[StopAction].data.string; + DWORD towrite = wcslen(wc_action); + char *toerl; DWORD exitcode; int i; int kill; if(towrite > 2 && srvi->erl_stdin != NULL){ - toerl = malloc(towrite+1); - strcpy(toerl,action); - strcat(toerl,"\n"); + towrite = WideCharToMultiByte(CP_UTF8, 0, wc_action, -1, NULL, 0, NULL, NULL); + toerl = malloc((1+towrite)*sizeof(char)); + WideCharToMultiByte(CP_UTF8, 0, wc_action, -1, toerl, towrite, NULL, NULL); + strcat(toerl, "\n"); WriteFile(srvi->erl_stdin, toerl, towrite, &written,0); free(toerl); /* Give it 45 seconds to terminate */ @@ -605,9 +625,9 @@ static BOOL stop_erlang(ServerInfo *srvi, int waithint, ++(*checkpoint); set_stop_pending(waithint,*checkpoint); } - log_warning("StopAction did not terminate erlang. Trying forced kill."); + log_warning(L"StopAction did not terminate erlang. Trying forced kill."); } - log_debug("Terminating erlang..."); + log_debug(L"Terminating erlang..."); kill = 1; if(eventKillErlang != NULL && SetEvent(eventKillErlang) != 0){ for(i=0;i<10;++i){ @@ -621,25 +641,25 @@ static BOOL stop_erlang(ServerInfo *srvi, int waithint, } else { #ifdef HARDDEBUG { - char *mes; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &mes, - 0, - NULL ); + wchar_t *mes; + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR) &mes, + 0, + NULL ); log_info(mes); LocalFree(mes); } #endif - log_debug("Could not send control event to Erlang process"); + log_debug(L"Could not send control event to Erlang process"); } if(kill){ - log_warning("Using TerminateProcess to kill erlang."); + log_warning(L"Using TerminateProcess to kill erlang."); if(!TerminateProcess(srvi->info.hProcess,NO_ERROR)) - log_error("TerminateProcess failed"); + log_error(L"TerminateProcess failed"); } GetExitCodeProcess(srvi->info.hProcess,&exitcode); CloseHandle(srvi->info.hProcess); @@ -668,14 +688,14 @@ static BOOL enable_privilege(void) { static BOOL pull_service_name(void){ SC_HANDLE scm; DWORD sz = 1024; - static char service_name_buff[1024]; + static wchar_t service_name_buff[1024]; if((scm = OpenSCManager(NULL, NULL, GENERIC_READ)) == NULL){ return FALSE; } - if(!GetServiceDisplayName(scm,real_service_name,service_name_buff,&sz)) + if(!GetServiceDisplayNameW(scm,real_service_name,service_name_buff,&sz)) return FALSE; CloseServiceHandle(scm); service_name = service_name_buff; @@ -683,7 +703,7 @@ static BOOL pull_service_name(void){ } -static VOID WINAPI service_main_loop(DWORD argc, char **argv){ +static VOID WINAPI service_main_loop(DWORD argc, wchar_t **argv){ int waithint = 30000; int checkpoint = 1; RegEntry *keys; @@ -692,36 +712,35 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ HANDLE harr[2]; FILETIME creationt,exitt,kernelt,usert; LONGLONG creationl,exitl,diffl; - char event_name[MAX_PATH] = "ErlSrv_"; - char executable_name[MAX_PATH]; + wchar_t event_name[MAX_PATH] = L"ErlSrv_"; + wchar_t executable_name[MAX_PATH]; #ifdef DEBUG - char errorbuff[2048]; /* FIXME... */ + wchar_t errorbuff[2048]; /* FIXME... */ #endif int success_wait = NO_SUCCESS_WAIT; real_service_name = argv[0]; if(!pull_service_name()){ - log_error("Could not get Display name of erlang service."); + log_error(L"Could not get Display name of erlang service."); set_stopped(ERROR_CANTREAD); return; } - SetEnvironmentVariable((LPCTSTR) SERVICE_ENV, (LPCTSTR) service_name); + SetEnvironmentVariableW(SERVICE_ENV, service_name); - strncat(event_name, service_name, MAX_PATH - strlen(event_name)); - event_name[MAX_PATH - 1] = '\0'; + wcsncat(event_name, service_name, MAX_PATH - wcslen(event_name)); + event_name[MAX_PATH - 1] = L'\0'; - if(!GetModuleFileName(NULL, executable_name, MAX_PATH)){ - log_error("Unable to retrieve module file name, " EXECUTABLE_ENV - " will not be set."); + if(!GetModuleFileNameW(NULL, executable_name, MAX_PATH)){ + log_error(L"Unable to retrieve module file name, " EXECUTABLE_ENV + L" will not be set."); } else { - char quoted_exe_name[MAX_PATH+4]; - sprintf(quoted_exe_name, "\"%s\"", executable_name); - SetEnvironmentVariable((LPCTSTR) EXECUTABLE_ENV, - (LPCTSTR) quoted_exe_name); + wchar_t quoted_exe_name[MAX_PATH+4]; + swprintf(quoted_exe_name, MAX_PATH+4, L"\"%s\"", executable_name); + SetEnvironmentVariableW(EXECUTABLE_ENV, quoted_exe_name); } - log_debug("Here we go, service_main_loop..."); + log_debug(L"Here we go, service_main_loop..."); currentState = SERVICE_START_PENDING; InitializeCriticalSection(&crit); eventStop = CreateEvent(NULL,FALSE,FALSE,NULL); @@ -730,13 +749,13 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ } else { srvi.event_name = NULL; } - statusHandle = RegisterServiceCtrlHandler(real_service_name, &handler); + statusHandle = RegisterServiceCtrlHandlerW(real_service_name, &handler); if(!statusHandle) return; set_start_pending(waithint,checkpoint); keys = get_keys(service_name); if(!keys){ - log_error("Could not get registry keys for erlang service."); + log_error(L"Could not get registry keys for erlang service."); set_stopped(ERROR_CANTREAD); return; } @@ -745,7 +764,7 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ ++checkpoint; if(!start_a_service(&srvi)){ - log_error("Could not start erlang machine"); + log_error(L"Could not start erlang machine"); set_stopped(ERROR_PROCESS_ABORTED); if (eventKillErlang != NULL) { CloseHandle(eventKillErlang); @@ -769,16 +788,16 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ if(ret == WAIT_TIMEOUT){ /* Just do the "success reporting" and continue */ if(success_wait == INITIAL_SUCCESS_WAIT){ - log_info("Erlang service started successfully."); + log_info(L"Erlang service started successfully."); } else { - log_warning("Erlang service restarted"); + log_warning(L"Erlang service restarted"); } success_wait = NO_SUCCESS_WAIT; continue; } if(ret == WAIT_FAILED || (int)(ret-WAIT_OBJECT_0) >= 2){ set_stopped(WAIT_FAILED); - log_error("Internal error, could not wait for objects."); + log_error(L"Internal error, could not wait for objects."); if (eventKillErlang != NULL) { CloseHandle(eventKillErlang); } @@ -791,7 +810,7 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ checkpoint = 2; /* 1 is taken by the handler */ set_stop_pending(waithint,checkpoint); if(stop_erlang(&srvi,waithint,&checkpoint)){ - log_debug("Erlang machine is stopped"); + log_debug(L"Erlang machine is stopped"); CloseHandle(eventStop); if (eventKillErlang != NULL) { CloseHandle(eventKillErlang); @@ -802,7 +821,7 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ free_keys(keys); return; } else { - log_warning("Unable to stop erlang service."); + log_warning(L"Unable to stop erlang service."); set_running(); continue; } @@ -811,12 +830,12 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ save_keys = keys; keys = get_keys(service_name); if(!keys){ - log_error("Could not reload registry keys."); + log_error(L"Could not reload registry keys."); keys = srvi.keys = save_keys; } else { #ifdef HARDDEBUG - sprintf(errorbuff,"Reloaded the registry keys because %s stopped.", - service_name); + swprintf(errorbuff,2048,L"Reloaded the registry keys because %s stopped.", + service_name); log_debug(errorbuff); #endif /* HARDDEBUG */ free_keys(save_keys); @@ -827,7 +846,7 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ if(!GetProcessTimes(srvi.info.hProcess,&creationt, &exitt,&kernelt,&usert)){ DWORD rcode = GetLastError(); - log_error("Could not get process time of terminated process."); + log_error(L"Could not get process time of terminated process."); CloseHandle(srvi.info.hProcess); CloseHandle(srvi.info.hThread); CloseHandle(eventStop); @@ -850,15 +869,14 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ diffl = exitl - creationl; diffl /= 10000000; #ifdef DEBUG - sprintf(errorbuff,"Process lived for %d seconds", (int) diffl); + swprintf(errorbuff,2048,L"Process lived for %d seconds", (int) diffl); log_debug(errorbuff); #endif if(diffl > CYCLIC_RESTART_LIMIT || srvi.keys[OnFail].data.value == ON_FAIL_RESTART_ALWAYS){ if(!start_a_service(&srvi)){ - log_error("Unable to restart failed erlang service, " - "aborting."); + log_error(L"Unable to restart failed erlang service, aborting."); CloseHandle(eventStop); set_stopped(ERROR_PROCESS_ABORTED); if (eventKillErlang != NULL) { @@ -867,19 +885,19 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ free_keys(keys); return; } - log_warning("Restarted erlang machine."); + log_warning(L"Restarted erlang machine."); if(diffl <= CYCLIC_RESTART_LIMIT) - log_warning("Possible cyclic restarting of erlang machine."); + log_warning(L"Possible cyclic restarting of erlang machine."); success_wait = RESTART_SUCCESS_WAIT; harr[0] = srvi.info.hProcess; } else { if(success_wait == INITIAL_SUCCESS_WAIT){ - log_error("Erlang machine stopped instantly " - "(distribution name conflict?). " - "The service is not restarted, ignoring OnFail option."); + log_error(L"Erlang machine stopped instantly " + L"(distribution name conflict?). " + L"The service is not restarted, ignoring OnFail option."); } else { - log_error("Erlang machine seems to die " - "continously, not restarted."); + log_error(L"Erlang machine seems to die " + L"continously, not restarted."); } CloseHandle(eventStop); set_stopped(ERROR_PROCESS_ABORTED); @@ -890,21 +908,21 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ return; } } else if(srvi.keys[OnFail].data.value == ON_FAIL_REBOOT){ - log_error("Rebooting because erlang machine stopped."); + log_error(L"Rebooting because erlang machine stopped."); enable_privilege(); if(!InitiateSystemShutdown("",NULL,0,TRUE,TRUE)){ - log_error("Failed to reboot!"); + log_error(L"Failed to reboot!"); #ifdef HARDDEBUG { - char *mes; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &mes, - 0, - NULL ); + wchar_t *mes; + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR) &mes, + 0, + NULL ); log_debug(mes); LocalFree(mes); } @@ -923,13 +941,13 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ } else { DWORD ecode = NO_ERROR; if(success_wait == NO_SUCCESS_WAIT){ - log_warning("Erlang machine voluntarily stopped. " - "The service is not restarted as OnFail " - "is set to ignore."); + log_warning(L"Erlang machine voluntarily stopped. " + L"The service is not restarted as OnFail " + L"is set to ignore."); } else { - log_error("Erlang machine stopped instantly " - "(distribution name conflict?). " - "The service is not restarted as OnFail is set to ignore."); + log_error(L"Erlang machine stopped instantly " + L"(distribution name conflict?). " + L"The service is not restarted as OnFail is set to ignore."); ecode = ERROR_PROCESS_ABORTED; } CloseHandle(srvi.info.hProcess); @@ -946,20 +964,19 @@ static VOID WINAPI service_main_loop(DWORD argc, char **argv){ } } -int service_main(int argc, char **argv){ - char dummy_name[] = ""; - SERVICE_TABLE_ENTRY serviceTable[] = +int service_main(int argc, wchar_t **argv){ + wchar_t dummy_name[] = L""; + SERVICE_TABLE_ENTRYW serviceTable[] = { { dummy_name, - (LPSERVICE_MAIN_FUNCTION) service_main_loop}, + (LPSERVICE_MAIN_FUNCTIONW) service_main_loop}, { NULL, NULL } }; BOOL success; - success = - StartServiceCtrlDispatcher(serviceTable); + success = StartServiceCtrlDispatcherW(serviceTable); if (!success) - log_error("Could not initiate service"); - log_debug("service_main done its job"); + log_error(L"Could not initiate service"); + log_debug(L"service_main done its job"); return 0; } diff --git a/erts/etc/win32/erlsrv/erlsrv_service.h b/erts/etc/win32/erlsrv/erlsrv_service.h index 3eab275836..c46689d83e 100644 --- a/erts/etc/win32/erlsrv/erlsrv_service.h +++ b/erts/etc/win32/erlsrv/erlsrv_service.h @@ -27,6 +27,6 @@ #define RESTART_SUCCESS_WAIT 2 -int service_main(int argc, char **argv); +int service_main(int argc, wchar_t **argv); #endif /* _ERLSRV_SERVICE_H */ diff --git a/erts/etc/win32/erlsrv/erlsrv_util.c b/erts/etc/win32/erlsrv/erlsrv_util.c index da3c6f5ef7..4b1ba071e8 100644 --- a/erts/etc/win32/erlsrv/erlsrv_util.c +++ b/erts/etc/win32/erlsrv/erlsrv_util.c @@ -25,76 +25,76 @@ #include "erlsrv_util.h" #include "erlsrv_logmess.h" -char *service_name = ""; -char *real_service_name = ""; +wchar_t *service_name = L""; +wchar_t *real_service_name = L""; -void log_warning(char *mess){ +void log_warning(wchar_t *mess){ HANDLE logh; - char *strings[] = {service_name, mess , NULL}; + wchar_t *strings[] = {service_name, mess , NULL}; - if(!(logh = RegisterEventSource(NULL,APP_NAME))) + if(!(logh = RegisterEventSourceW(NULL,APP_NAME))) return; - ReportEvent(logh, EVENTLOG_WARNING_TYPE, 0, MSG_WARNING, - NULL, 2, 0, strings, NULL); + ReportEventW(logh, EVENTLOG_WARNING_TYPE, 0, MSG_WARNING, + NULL, 2, 0, strings, NULL); DeregisterEventSource(logh); } -void log_error(char *mess){ +void log_error(wchar_t *mess){ HANDLE logh; - char *strings[] = {service_name, mess , NULL}; + wchar_t *strings[] = {service_name, mess , NULL}; - if(!(logh = RegisterEventSource(NULL,APP_NAME))) + if(!(logh = RegisterEventSourceW(NULL,APP_NAME))) return; - ReportEvent(logh, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR, - NULL, 2, 0, strings, NULL); + ReportEventW(logh, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR, + NULL, 2, 0, strings, NULL); DeregisterEventSource(logh); } -void log_info(char *mess){ +void log_info(wchar_t *mess){ HANDLE logh; - char *strings[] = {service_name, mess , NULL}; + wchar_t *strings[] = {service_name, mess , NULL}; - if(!(logh = RegisterEventSource(NULL,APP_NAME))) + if(!(logh = RegisterEventSourceW(NULL,APP_NAME))) return; - ReportEvent(logh, EVENTLOG_INFORMATION_TYPE, 0, MSG_INFO, - NULL, 2, 0, strings, NULL); + ReportEventW(logh, EVENTLOG_INFORMATION_TYPE, 0, MSG_INFO, + NULL, 2, 0, strings, NULL); DeregisterEventSource(logh); } #ifndef NDEBUG -void log_debug(char *mess){ - char *buff=malloc(strlen(mess)+100); - sprintf(buff,"DEBUG! %s",mess); +void log_debug(wchar_t *mess){ + wchar_t *buff=malloc((wcslen(mess)+100)*sizeof(wchar_t)); + swprintf(buff,wcslen(mess)+100,L"DEBUG! %s",mess); log_info(buff); free(buff); } #endif -char *envdup(char *env){ - char *tmp; +wchar_t *envdup(wchar_t *env){ + wchar_t *tmp; int len; - for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1) + for(tmp = env; *tmp != L'\0'; tmp += wcslen(tmp)+1) ; len = (tmp - env) + 1; if(len == 1) ++len; - tmp = malloc(len); - memcpy(tmp,env,len); + tmp = malloc(len*sizeof(wchar_t)); + memcpy(tmp,env,len*sizeof(wchar_t)); return tmp; } -char **env_to_arg(char *env){ - char **ret; - char *tmp; +wchar_t **env_to_arg(wchar_t *env){ + wchar_t **ret; + wchar_t *tmp; int i; int num_strings = 0; - for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1) + for(tmp = env; *tmp != L'\0'; tmp += wcslen(tmp)+1) ++num_strings; /* malloc enough to insert ONE string */ - ret = malloc(sizeof(char *) * (num_strings + 2)); + ret = malloc(sizeof(wchar_t *) * (num_strings + 2)); i = 0; - for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1){ - ret[i++] = strdup(tmp); + for(tmp = env; *tmp != L'\0'; tmp += wcslen(tmp)+1){ + ret[i++] = wcsdup(tmp); } ret[i] = NULL; free(env); @@ -102,52 +102,52 @@ char **env_to_arg(char *env){ } 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,'='); + wchar_t *s1 = *((wchar_t **) a); + wchar_t *s2 = *((wchar_t **) b); + wchar_t *e1 = wcschr(s1,L'='); + wchar_t *e2 = wcschr(s2,L'='); int ret; int len; if(!e1) - e1 = s1 + strlen(s1); + e1 = s1 + wcslen(s1); if(!e2) - e2 = s2 + strlen(s2); + e2 = s2 + wcslen(s2); if((e1 - s1) > (e2 - s2)) len = (e2 - s2); else len = (e1 - s1); - ret = _strnicmp(s1,s2,len); + ret = _wcsnicmp(s1,s2,len); if(ret == 0) return ((e1 - s1) - (e2 - s2)); else return ret; } -char *arg_to_env(char **arg){ - char *block; - char *pek; +wchar_t *arg_to_env(wchar_t **arg){ + wchar_t *block; + wchar_t *pek; int i; int totlen = 1; /* extra '\0' */ for(i=0;arg[i] != NULL;++i) - totlen += strlen(arg[i])+1; + totlen += wcslen(arg[i])+1; /* sort the environment vector */ - qsort(arg,i,sizeof(char *),&compare); + qsort(arg,i,sizeof(wchar_t *),&compare); if(totlen == 1){ - block = malloc(2); - block[0] = block[1] = '\0'; + block = malloc(2*sizeof(wchar_t)); + block[0] = block[1] = L'\0'; } else { - block = malloc(totlen); + block = malloc(totlen*sizeof(wchar_t)); pek = block; for(i=0; arg[i] != NULL; ++i){ - strcpy(pek, arg[i]); + wcscpy(pek, arg[i]); free(arg[i]); - pek += strlen(pek)+1; + pek += wcslen(pek)+1; } - *pek = '\0'; + *pek = L'\0'; } free(arg); return block; diff --git a/erts/etc/win32/erlsrv/erlsrv_util.h b/erts/etc/win32/erlsrv/erlsrv_util.h index b98a6cd3ef..6881906a52 100644 --- a/erts/etc/win32/erlsrv/erlsrv_util.h +++ b/erts/etc/win32/erlsrv/erlsrv_util.h @@ -19,30 +19,30 @@ #ifndef _ERLSRV_UTIL_H #define _ERLSRV_UTIL_H -extern char *service_name; -extern char *real_service_name; -void log_warning(char *mess); -void log_error(char *mess); -void log_info(char *mess); +extern wchar_t *service_name; +extern wchar_t *real_service_name; +void log_warning(wchar_t *mess); +void log_error(wchar_t *mess); +void log_info(wchar_t *mess); -char *envdup(char *env); +wchar_t *envdup(wchar_t *env); /* ** Call before env_to_arg to get a 'freeable' environment block. */ -char *arg_to_env(char **arg); +wchar_t *arg_to_env(wchar_t **arg); /* ** Frees the argument list before returning! */ -char **env_to_arg(char *env); +wchar_t **env_to_arg(wchar_t *env); /* ** Frees the environment block before returning! */ #ifndef NDEBUG -void log_debug(char *mess); +void log_debug(wchar_t *mess); #else #define log_debug(mess) /* Debug removed */ #endif -- cgit v1.2.3 From 0ab074b7dece920e8dcd34793b88ce14ca4534ed Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 28 May 2013 16:05:40 +0200 Subject: Allow unicode characters for boot and config in init:make_permanent When release_handler makes a release permanent, it calls init:make_permanent/2 in order to set the path to the config and boot file. The paths are coverterd to binaries, and this commit changes this conversion from using lists_to_binary/1 to using unicode:characters_to_binary/3 with the encoding set according to the file name encoding mode (+fnu/fna/fnl). --- erts/preloaded/src/init.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 87003d096b..ab8464956c 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -465,7 +465,10 @@ make_permanent(Boot,Config,Flags0,State) -> set_flag(_Flag,false,Flags) -> {ok,Flags}; set_flag(Flag,Value,Flags) when is_list(Value) -> - case catch list_to_binary(Value) of + %% The flag here can be -boot or -config, which means the value is + %% a file name! Thus the file name encoding is used when coverting. + Encoding = file:native_name_encoding(), + case catch unicode:characters_to_binary(Value,Encoding,Encoding) of {'EXIT',_} -> {error,badarg}; AValue -> -- cgit v1.2.3 From 9365954de6a3be7c76998ccf7caab50bf12c55c2 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 28 May 2013 16:12:29 +0200 Subject: Update preloaded init.beam --- erts/preloaded/ebin/init.beam | Bin 48672 -> 48788 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index b82047835e..089f40de5b 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ -- cgit v1.2.3 From 75763bd57338b3efe8300ece094aacc8e1a7a467 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Mon, 25 Mar 2013 12:44:20 +0100 Subject: erts: Change erlang:open_port spawn to handle unicode Previously only 'spawn_executable' handled unicode input. Also change 'cd' option to always handle unicode. Update open_port documentation and tests --- erts/doc/src/erlang.xml | 66 +++--- erts/emulator/beam/erl_bif_port.c | 69 ++----- erts/emulator/beam/erl_unicode.c | 12 +- erts/emulator/beam/global.h | 6 + erts/emulator/sys/win32/sys.c | 412 ++++++++++++-------------------------- erts/emulator/test/port_SUITE.erl | 71 +++++-- 6 files changed, 242 insertions(+), 394 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 5befd51974..81e9084e07 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2613,7 +2613,28 @@ os_prompt%

Returns a port identifier as the result of opening a new Erlang port. A port can be seen as an external Erlang - process. PortName is one of the following:

+ process. +

+

The name of the executable as well as the arguments + given in cd, env, args and arg0 is subject to + Unicode file name translation if the system is running + in Unicode file name mode. To avoid + translation or force i.e. UTF-8, supply the executable + and/or arguments as a binary in the correct + encoding. See the file module, the + + file:native_name_encoding/0 function and the + stdlib users guide + for details.

+ +

The characters in the name (if given as a list) + can only be > 255 if the Erlang VM is started in + Unicode file name translation mode, otherwise the name + of the executable is limited to the ISO-latin-1 + character set.

+ +

PortName is one of the following:

{spawn, Command} @@ -2668,25 +2689,6 @@ os_prompt% executed, the appropriate command interpreter will implicitly be invoked, but there will still be no command argument expansion or implicit PATH search.

- -

The name of the executable as well as the arguments - given in args and arg0 is subject to - Unicode file name translation if the system is running - in Unicode file name mode. To avoid - translation or force i.e. UTF-8, supply the executable - and/or arguments as a binary in the correct - encoding. See the file module, the - - file:native_name_encoding/0 function and the - stdlib users guide - for details.

- -

The characters in the name (if given as a list) - can only be > 255 if the Erlang VM is started in - Unicode file name translation mode, otherwise the name - of the executable is limited to the ISO-latin-1 - character set.

If the FileName cannot be run, an error exception, with the posix error code as the reason, is @@ -2762,11 +2764,7 @@ os_prompt% strings. The one exception is Val being the atom false (in analogy with os:getenv/1), which removes the environment variable. -

-

If Unicode filename encoding is in effect (see the erl manual - page), the strings (both Name and - Value) may contain characters with codepoints > 255.

+

{args, [ string() | binary() ]} @@ -2794,21 +2792,6 @@ os_prompt% should not be given in this list. The proper executable name will automatically be used as argv[0] where applicable.

-

When the Erlang VM is running in Unicode file name - mode, the arguments can contain any Unicode characters and - will be translated into whatever is appropriate on the - underlying OS, which means UTF-8 for all platforms except - Windows, which has other (more transparent) ways of - dealing with Unicode arguments to programs. To avoid - Unicode translation of arguments, they can be supplied as - binaries in whatever encoding is deemed appropriate.

- -

The characters in the arguments (if given as a - list of characters) can only be > 255 if the Erlang - VM is started in Unicode file name mode, - otherwise the arguments are limited to the - ISO-latin-1 character set.

-

If one, for any reason, wants to explicitly set the program name in the argument vector, the arg0 option can be used.

@@ -2824,9 +2807,6 @@ os_prompt% responds to this is highly system dependent and no specific effect is guaranteed.

-

The unicode file name translation rules of the - args option apply to this option as well.

-
exit_status diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 109c54fd7f..5b26e53c9f 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -784,43 +784,29 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) goto badarg; } - if (*tp == am_spawn || *tp == am_spawn_driver) { /* A process port */ + if (*tp == am_spawn || *tp == am_spawn_driver || *tp == am_spawn_executable) { /* A process port */ + int encoding; if (arity != make_arityval(2)) { goto badarg; } name = tp[1]; - if (is_atom(name)) { - name_buf = (char *) erts_alloc(ERTS_ALC_T_TMP, - atom_tab(atom_val(name))->len+1); - sys_memcpy((void *) name_buf, - (void *) atom_tab(atom_val(name))->name, - atom_tab(atom_val(name))->len); - name_buf[atom_tab(atom_val(name))->len] = '\0'; - } else if ((i = is_string(name))) { - name_buf = (char *) erts_alloc(ERTS_ALC_T_TMP, i + 1); - if (intlist_to_buf(name, name_buf, i) != i) - erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__); - name_buf[i] = '\0'; - } else { + encoding = erts_get_native_filename_encoding(); + /* Do not convert the command to utf-16le yet, do that in win32 specific code */ + /* since the cmd is used for comparsion with drivers names and copied to port info */ + if (encoding == ERL_FILENAME_WIN_WCHAR) { + encoding = ERL_FILENAME_UTF8; + } + if ((name_buf = erts_convert_filename_to_encoding(name, NULL, 0, ERTS_ALC_T_TMP,0,1, encoding, NULL)) + == NULL) { goto badarg; } + if (*tp == am_spawn_driver) { opts.spawn_type = ERTS_SPAWN_DRIVER; + } else if (*tp == am_spawn_executable) { + opts.spawn_type = ERTS_SPAWN_EXECUTABLE; } - driver = &spawn_driver; - } else if (*tp == am_spawn_executable) { /* A program */ - /* - * {spawn_executable,Progname} - */ - - if (arity != make_arityval(2)) { - goto badarg; - } - name = tp[1]; - 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; + driver = &spawn_driver; } else if (*tp == am_fd) { /* An fd port */ int n; @@ -861,29 +847,8 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) } if (edir != NIL) { - /* A working directory is expressed differently if spawn_executable, i.e. Unicode is handles - for spawn_executable... */ - if (opts.spawn_type != ERTS_SPAWN_EXECUTABLE) { - Eterm iolist; - DeclareTmpHeap(heap,4,p); - int r; - - UseTmpHeap(4,p); - heap[0] = edir; - heap[1] = make_list(heap+2); - heap[2] = make_small(0); - heap[3] = NIL; - iolist = make_list(heap); - r = erts_iolist_to_buf(iolist, (char*) dir, MAXPATHLEN); - UnUseTmpHeap(4,p); - if (ERTS_IOLIST_TO_BUF_FAILED(r)) { - goto badarg; - } - opts.wd = (char *) dir; - } else { - if ((opts.wd = erts_convert_filename_to_native(edir, NULL, 0, ERTS_ALC_T_TMP,0,1,NULL)) == NULL) { - goto badarg; - } + if ((opts.wd = erts_convert_filename_to_native(edir, NULL, 0, ERTS_ALC_T_TMP,0,1,NULL)) == NULL) { + goto badarg; } } @@ -961,11 +926,11 @@ static char **convert_args(Eterm l) int n; int i = 0; Eterm str; - /* We require at least one element in list (argv[0]) */ if (is_not_list(l) && is_not_nil(l)) { return NULL; } n = list_length(l); + /* We require at least one element in argv[0] + NULL at end */ pp = erts_alloc(ERTS_ALC_T_TMP, (n + 2) * sizeof(char **)); pp[i++] = erts_default_arg0; while (is_list(l)) { diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index ad6f8b993a..71794e1101 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -1981,9 +1981,19 @@ 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, char *statbuf, size_t statbuf_size, ErtsAlcType_t alloc_type, int allow_empty, int allow_atom, Sint *used) +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(); + return erts_convert_filename_to_encoding(name, statbuf, statbuf_size, alloc_type, + allow_empty, allow_atom, encoding, used); +} + +char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbuf_size, + ErtsAlcType_t alloc_type, int allow_empty, + int allow_atom, int encoding, Sint *used) +{ char* name_buf = NULL; if ((allow_atom && is_atom(name)) || diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 26ed5f82c1..f7e4c81181 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -788,6 +788,12 @@ char *erts_convert_filename_to_native(Eterm name, char *statbuf, ErtsAlcType_t alloc_type, int allow_empty, int allow_atom, Sint *used /* out */); +char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, + size_t statbuf_size, + ErtsAlcType_t alloc_type, + int allow_empty, int allow_atom, + int encoding, + Sint *used /* out */); Eterm erts_convert_native_to_filename(Process *p, byte *bytes); Eterm erts_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz, Uint left, Uint *num_built, Uint *num_eaten, Eterm tail); diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 922967c698..93bbae7dee 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -69,17 +69,14 @@ static void async_read_file(struct async_io* aio, LPVOID buf, DWORD numToRead); static int async_write_file(struct async_io* aio, LPVOID buf, DWORD numToWrite); static int get_overlapped_result(struct async_io* aio, LPDWORD pBytesRead, BOOL wait); -static BOOL create_child_process(char *, HANDLE, HANDLE, +static BOOL create_child_process(wchar_t *, HANDLE, HANDLE, HANDLE, LPHANDLE, LPDWORD, BOOL, - LPVOID, LPTSTR, unsigned, - char **, int *); + LPVOID, wchar_t*, unsigned, + wchar_t **, int *); static int create_pipe(LPHANDLE, LPHANDLE, BOOL, BOOL); -static int application_type(const char* originalName, char fullPath[MAX_PATH], +static int application_type(const wchar_t* originalName, wchar_t fullPath[MAX_PATH], BOOL search_in_path, BOOL handle_quotes, int *error_return); -static int application_type_w(const WCHAR *originalName, WCHAR fullPath[MAX_PATH], - BOOL search_in_path, BOOL handle_quotes, - int *error_return); HANDLE erts_service_event; @@ -1173,7 +1170,7 @@ spawn_init(void) } static ErlDrvData -spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) +spawn_start(ErlDrvPort port_num, char* utf8_name, SysDriverOpts* opts) { HANDLE hToChild = INVALID_HANDLE_VALUE; /* Write handle to child. */ HANDLE hFromChild = INVALID_HANDLE_VALUE; /* Read handle from child. */ @@ -1189,7 +1186,9 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE}; char* envir = opts->envir; int errno_return = -1; - + wchar_t *name; + int len; + if (opts->read_write & DO_READ) neededSelects++; if (opts->read_write & DO_WRITE) @@ -1247,26 +1246,40 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) * Spawn the port program. */ - DEBUGF(("Spawning \"%s\"\n", name)); + if ((len = MultiByteToWideChar(CP_UTF8, 0, utf8_name, -1, NULL, 0)) > 0) { + name = erts_alloc(ERTS_ALC_T_TMP, len*sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, utf8_name, -1, name, len); + } else { /* Not valid utf-8, just convert byte to wchar */ + int i; + len = strlen(utf8_name); + name = erts_alloc(ERTS_ALC_T_TMP, (1+len)*sizeof(wchar_t)); + for(i=0; iport_pid, - &pid, - opts->hide_window, - (LPVOID) envir, - (LPTSTR) opts->wd, - opts->spawn_type, - opts->argv, - &errno_return); + ok = create_child_process(name, + hChildStdin, + hChildStdout, + hChildStderr, + &dp->port_pid, + &pid, + opts->hide_window, + (LPVOID) envir, + (wchar_t *) opts->wd, + opts->spawn_type, + (wchar_t **) opts->argv, + &errno_return); CloseHandle(hChildStdin); CloseHandle(hChildStdout); if (close_child_stderr && hChildStderr != INVALID_HANDLE_VALUE && hChildStderr != 0) { CloseHandle(hChildStderr); } + erts_free(ERTS_ALC_T_TMP, name); + if (envir != NULL) { erts_free(ERTS_ALC_T_ENVIRONMENT, envir); } @@ -1344,9 +1357,9 @@ create_file_thread(AsyncIo* aio, int mode) * Example: input = "\"Program Files\"\\erl arg1 arg2" * gives 19 as result. * The length returned is equivalent with length(argv[0]) if the - * comman line should have been prepared by _setargv for the main function + * command line should have been prepared by _setargv for the main function */ -int parse_command(char* cmd){ +int parse_command(wchar_t* cmd){ #define NORMAL 2 #define STRING 1 #define STOP 0 @@ -1354,17 +1367,17 @@ int parse_command(char* cmd){ int state = NORMAL; while (cmd[i]) { switch (cmd[i]) { - case '"': + case L'"': if (state == NORMAL) state = STRING; else state = NORMAL; break; - case '\\': - if ((state == STRING) && (cmd[i+1]=='"')) + case L'\\': + if ((state == STRING) && (cmd[i+1]==L'"')) i++; break; - case ' ': + case L' ': if (state == NORMAL) state = STOP; break; @@ -1379,7 +1392,7 @@ int parse_command(char* cmd){ return i; } -static BOOL need_quotes(WCHAR *str) +static BOOL need_quotes(wchar_t *str) { int in_quote = 0; int backslashed = 0; @@ -1443,7 +1456,7 @@ static BOOL need_quotes(WCHAR *str) static BOOL create_child_process ( - char *origcmd, /* Command line for child process (including + wchar_t *origcmd, /* Command line for child process (including * name of executable). Or whole executable if st is * ERTS_SPAWN_EXECUTABLE */ @@ -1454,57 +1467,53 @@ create_child_process LPDWORD pdwID, /* Pointer to variable to received Process ID */ BOOL hide, /* Hide the window unconditionally. */ LPVOID env, /* Environment for the child */ - LPTSTR wd, /* Working dir for the child */ + wchar_t *wd, /* Working dir for the child */ unsigned st, /* Flags for spawn, tells us how to interpret origcmd */ - char **argv, /* Argument vector if given. */ + wchar_t **argv, /* Argument vector if given. */ int *errno_return /* Place to put an errno in in case of failure */ ) -{ +{ PROCESS_INFORMATION piProcInfo = {0}; BOOL ok = FALSE; int applType; /* Not to be changed for different types of executables */ int staticCreateFlags = GetPriorityClass(GetCurrentProcess()); int createFlags = DETACHED_PROCESS; - char *newcmdline = NULL; + wchar_t *newcmdline = NULL; int cmdlength; - char* thecommand; - LPTSTR appname = NULL; + wchar_t* thecommand; + wchar_t* appname = NULL; HANDLE hProcess = GetCurrentProcess(); - - *errno_return = -1; + STARTUPINFOW siStartInfo = {0}; + wchar_t execPath[MAX_PATH]; + *errno_return = -1; + siStartInfo.cb = sizeof(STARTUPINFOW); + siStartInfo.dwFlags = STARTF_USESTDHANDLES; + siStartInfo.hStdInput = hStdin; + siStartInfo.hStdOutput = hStdout; + siStartInfo.hStdError = hStderr; if (st != ERTS_SPAWN_EXECUTABLE) { - STARTUPINFO siStartInfo = {0}; - char execPath[MAX_PATH]; - - siStartInfo.cb = sizeof(STARTUPINFO); - siStartInfo.dwFlags = STARTF_USESTDHANDLES; - siStartInfo.hStdInput = hStdin; - siStartInfo.hStdOutput = hStdout; - siStartInfo.hStdError = hStderr; - /* * Parse out the program name from the command line (it can be quoted and * contain spaces). */ - newcmdline = erts_alloc(ERTS_ALC_T_TMP, 2048); + newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, 2048*sizeof(wchar_t)); cmdlength = parse_command(origcmd); - thecommand = (char *) erts_alloc(ERTS_ALC_T_TMP, cmdlength+1); - strncpy(thecommand, origcmd, cmdlength); - thecommand[cmdlength] = '\0'; - DEBUGF(("spawn command: %s\n", thecommand)); - - applType = application_type(thecommand, execPath, TRUE, - TRUE, errno_return); - DEBUGF(("application_type returned for (%s) is %d\n", thecommand, applType)); + thecommand = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (cmdlength+1)*sizeof(wchar_t)); + wcsncpy(thecommand, origcmd, cmdlength); + thecommand[cmdlength] = L'\0'; + DEBUGF(("spawn command: %S\n", thecommand)); + + applType = application_type(thecommand, execPath, TRUE, TRUE, errno_return); + DEBUGF(("application_type returned for (%S) is %d\n", thecommand, applType)); erts_free(ERTS_ALC_T_TMP, (void *) thecommand); if (applType == APPL_NONE) { erts_free(ERTS_ALC_T_TMP,newcmdline); return FALSE; } - newcmdline[0] = '\0'; + newcmdline[0] = L'\0'; if (applType == APPL_DOS) { /* @@ -1513,11 +1522,11 @@ create_child_process * a normal process inside of a hidden console application, * and then run that hidden console as a detached process. */ - + siStartInfo.wShowWindow = SW_HIDE; siStartInfo.dwFlags |= STARTF_USESHOWWINDOW; createFlags = CREATE_NEW_CONSOLE; - strcat(newcmdline, "cmd.exe /c "); + wcscat(newcmdline, L"cmd.exe /c "); } else if (hide) { DEBUGF(("hiding window\n")); siStartInfo.wShowWindow = SW_HIDE; @@ -1525,35 +1534,25 @@ create_child_process createFlags = 0; } - strcat(newcmdline, execPath); - strcat(newcmdline, origcmd+cmdlength); - DEBUGF(("Creating child process: %s, createFlags = %d\n", newcmdline, createFlags)); - ok = CreateProcessA(appname, - newcmdline, - NULL, - NULL, - TRUE, - createFlags | staticCreateFlags | - CREATE_UNICODE_ENVIRONMENT, - env, - wd, - &siStartInfo, + wcscat(newcmdline, execPath); + wcscat(newcmdline, origcmd+cmdlength); + DEBUGF(("Creating child process: %S, createFlags = %d\n", newcmdline, createFlags)); + ok = CreateProcessW(appname, + newcmdline, + NULL, + NULL, + TRUE, + createFlags | staticCreateFlags | + CREATE_UNICODE_ENVIRONMENT, + env, + wd, + &siStartInfo, &piProcInfo); } else { /* ERTS_SPAWN_EXECUTABLE, filename and args are in unicode ({utf16,little}) */ int run_cmd = 0; - STARTUPINFOW siStartInfo = {0}; - WCHAR execPath[MAX_PATH]; - - siStartInfo.cb = sizeof(STARTUPINFOW); - siStartInfo.dwFlags = STARTF_USESTDHANDLES; - siStartInfo.hStdInput = hStdin; - siStartInfo.hStdOutput = hStdout; - siStartInfo.hStdError = hStderr; - - applType = application_type_w((WCHAR *) origcmd, execPath, FALSE, FALSE, - errno_return); + applType = application_type(origcmd, execPath, FALSE, FALSE, errno_return); if (applType == APPL_NONE) { return FALSE; } @@ -1573,37 +1572,37 @@ create_child_process createFlags = 0; } if (run_cmd) { - WCHAR cmdPath[MAX_PATH]; + wchar_t cmdPath[MAX_PATH]; int cmdType; - cmdType = application_type_w(L"cmd.exe", cmdPath, TRUE, FALSE, errno_return); + cmdType = application_type(L"cmd.exe", cmdPath, TRUE, FALSE, errno_return); if (cmdType == APPL_NONE || cmdType == APPL_DOS) { return FALSE; } - appname = (char *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(cmdPath)+1)*sizeof(WCHAR)); - wcscpy((WCHAR *) appname,cmdPath); + appname = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(cmdPath)+1)*sizeof(wchar_t)); + wcscpy(appname,cmdPath); } else { - appname = (char *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(execPath)+1)*sizeof(WCHAR)); - wcscpy((WCHAR *) appname, execPath); + appname = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(execPath)+1)*sizeof(wchar_t)); + wcscpy(appname, execPath); } if (argv == NULL) { BOOL orig_need_q = need_quotes(execPath); - WCHAR *ptr; + wchar_t *ptr; int ocl = wcslen(execPath); if (run_cmd) { - newcmdline = (char *) erts_alloc(ERTS_ALC_T_TMP, - (ocl + ((orig_need_q) ? 3 : 1) - + 11)*sizeof(WCHAR)); - memcpy(newcmdline,L"cmd.exe /c ",11*sizeof(WCHAR)); - ptr = (WCHAR *) (newcmdline + (11*sizeof(WCHAR))); + newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, + (ocl + ((orig_need_q) ? 3 : 1) + + 11)*sizeof(wchar_t)); + memcpy(newcmdline,L"cmd.exe /c ",11*sizeof(wchar_t)); + ptr = newcmdline + 11; } else { - newcmdline = (char *) erts_alloc(ERTS_ALC_T_TMP, - (ocl + ((orig_need_q) ? 3 : 1))*sizeof(WCHAR)); - ptr = (WCHAR *) newcmdline; + newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, + (ocl + ((orig_need_q) ? 3 : 1))*sizeof(wchar_t)); + ptr = (wchar_t *) newcmdline; } if (orig_need_q) { *ptr++ = L'"'; } - memcpy(ptr,execPath,ocl*sizeof(WCHAR)); + memcpy(ptr,execPath,ocl*sizeof(wchar_t)); ptr += ocl; if (orig_need_q) { *ptr++ = L'"'; @@ -1611,12 +1610,12 @@ create_child_process *ptr = L'\0'; } else { int sum = 1; /* '\0' */ - WCHAR **ar = (WCHAR **) argv; - WCHAR *n; - char *save_arg0 = NULL; - if (argv[0] == erts_default_arg0 || run_cmd) { + wchar_t **ar = argv; + wchar_t *n; + wchar_t *save_arg0 = NULL; + if (argv[0] == (wchar_t *) erts_default_arg0 || run_cmd) { save_arg0 = argv[0]; - argv[0] = (char *) execPath; + argv[0] = execPath; } if (run_cmd) { sum += 11; /* cmd.exe /c */ @@ -1629,11 +1628,11 @@ create_child_process sum++; /* space */ ++ar; } - ar = (WCHAR **) argv; - newcmdline = erts_alloc(ERTS_ALC_T_TMP, sum*sizeof(WCHAR)); - n = (WCHAR *) newcmdline; + ar = argv; + newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, sum*sizeof(wchar_t)); + n = newcmdline; if (run_cmd) { - memcpy(n,L"cmd.exe /c ",11*sizeof(WCHAR)); + memcpy(n,L"cmd.exe /c ",11*sizeof(wchar_t)); n += 11; } while (*ar != NULL) { @@ -1642,7 +1641,7 @@ create_child_process if (q) { *n++ = L'"'; } - memcpy(n,*ar,sum*sizeof(WCHAR)); + memcpy(n,*ar,sum*sizeof(wchar_t)); n += sum; if (q) { *n++ = L'"'; @@ -1657,16 +1656,16 @@ create_child_process } DEBUGF(("Creating child process: %s, createFlags = %d\n", newcmdline, createFlags)); - ok = CreateProcessW((WCHAR *) appname, - (WCHAR *) newcmdline, - NULL, - NULL, - TRUE, - createFlags | staticCreateFlags | - CREATE_UNICODE_ENVIRONMENT, - env, - (WCHAR *) wd, - &siStartInfo, + ok = CreateProcessW((wchar_t *) appname, + (wchar_t *) newcmdline, + NULL, + NULL, + TRUE, + createFlags | staticCreateFlags | + CREATE_UNICODE_ENVIRONMENT, + env, + wd, + &siStartInfo, &piProcInfo); } /* end SPAWN_EXECUTABLE */ @@ -1775,180 +1774,23 @@ static int create_pipe(HANDLE *phRead, HANDLE *phWrite, BOOL inheritRead, BOOL o return TRUE; } - - - -static int application_type -( - const char *originalName, /* Name of the application to find. */ - char fullPath[MAX_PATH], /* Filled with complete path to - * application. */ - BOOL search_in_path, /* If we should search the system wide path */ - BOOL handle_quotes, /* If we should handle quotes around executable */ - int *error_return /* A place to put an error code */ - ) -{ - int applType, i; - HANDLE hFile; - char *ext, *rest; - char buf[2]; - DWORD read; - IMAGE_DOS_HEADER header; - static char extensions[][5] = {"", ".com", ".exe", ".bat"}; - int is_quoted; - int len; - - /* Look for the program as an external program. First try the name - * as it is, then try adding .com, .exe, and .bat, in that order, to - * the name, looking for an executable. - * NOTE! that we does not support execution of .com programs on Windows NT - * - * - * Using the raw SearchPath() procedure doesn't do quite what is - * necessary. If the name of the executable already contains a '.' - * character, it will not try appending the specified extension when - * searching (in other words, SearchPath will not find the program - * "a.b.exe" if the arguments specified "a.b" and ".exe"). - * So, first look for the file as it is named. Then manually append - * the extensions, looking for a match. (') - */ - - len = strlen(originalName); - is_quoted = handle_quotes && len > 0 && originalName[0] == '"' && - originalName[len-1] == '"'; - - applType = APPL_NONE; - *error_return = ENOENT; - for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) { - if(is_quoted) { - lstrcpyn(fullPath, originalName+1, MAX_PATH - 7); - len = strlen(fullPath); - if(len > 0) { - fullPath[len-1] = '\0'; - } - } else { - lstrcpyn(fullPath, originalName, MAX_PATH - 5); - } - lstrcat(fullPath, extensions[i]); - SearchPath((search_in_path) ? NULL : ".", fullPath, NULL, MAX_PATH, fullPath, &rest); - - /* - * Ignore matches on directories or data files, return if identified - * a known type. - */ - - if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) { - continue; - } - - ext = strrchr(fullPath, '.'); - if ((ext != NULL) && (strcmpi(ext, ".bat") == 0)) { - *error_return = EACCES; - applType = APPL_DOS; - break; - } - - hFile = CreateFile(fullPath, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - continue; - } - - *error_return = EACCES; /* If considered an error, - it's an access error */ - header.e_magic = 0; - ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL); - if (header.e_magic != IMAGE_DOS_SIGNATURE) { - /* - * Doesn't have the magic number for relocatable executables. If - * filename ends with .com, assume it's a DOS application anyhow. - * Note that we didn't make this assumption at first, because some - * supposed .com files are really 32-bit executables with all the - * magic numbers and everything. - */ - - CloseHandle(hFile); - if ((ext != NULL) && (strcmpi(ext, ".com") == 0)) { - applType = APPL_DOS; - break; - } - continue; - } - if (header.e_lfarlc != sizeof(header)) { - /* - * All Windows 3.X and Win32 and some DOS programs have this value - * set here. If it doesn't, assume that since it already had the - * other magic number it was a DOS application. - */ - - CloseHandle(hFile); - applType = APPL_DOS; - break; - } - - /* - * The DWORD at header.e_lfanew points to yet another magic number. - */ - - buf[0] = '\0'; - SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN); - ReadFile(hFile, (void *) buf, 2, &read, NULL); - CloseHandle(hFile); - - if ((buf[0] == 'L') && (buf[1] == 'E')) { - applType = APPL_DOS; - } else if ((buf[0] == 'N') && (buf[1] == 'E')) { - applType = APPL_WIN3X; - } else if ((buf[0] == 'P') && (buf[1] == 'E')) { - applType = APPL_WIN32; - } else { - continue; - } - break; - } - - if (applType == APPL_NONE) { - return APPL_NONE; - } - - if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) { - /* - * Replace long path name of executable with short path name for - * 16-bit applications. Otherwise the application may not be able - * to correctly parse its own command line to separate off the - * application name from the arguments. - */ - - GetShortPathName(fullPath, fullPath, MAX_PATH); - } - if (is_quoted) { - /* restore quotes on quoted program name */ - len = strlen(fullPath); - memmove(fullPath+1,fullPath,len); - fullPath[0]='"'; - fullPath[len+1]='"'; - fullPath[len+2]='\0'; - } - return applType; -} - -static int application_type_w (const WCHAR *originalName, /* Name of the application to find. */ - WCHAR wfullpath[MAX_PATH],/* Filled with complete path to +static int application_type (const wchar_t *originalName, /* Name of the application to find. */ + wchar_t wfullpath[MAX_PATH],/* Filled with complete path to * application. */ - BOOL search_in_path, /* If we should search the system wide path */ - BOOL handle_quotes, /* If we should handle quotes around executable */ - int *error_return) /* A place to put an error code */ + BOOL search_in_path, /* If we should search the system wide path */ + BOOL handle_quotes, /* If we should handle quotes around executable */ + int *error_return) /* A place to put an error code */ { int applType, i; HANDLE hFile; - WCHAR *ext, *rest; + wchar_t *ext, *rest; char buf[2]; DWORD read; IMAGE_DOS_HEADER header; - static WCHAR extensions[][5] = {L"", L".com", L".exe", L".bat"}; + static wchar_t extensions[][5] = {L"", L".com", L".exe", L".bat"}; int is_quoted; int len; - WCHAR xfullpath[MAX_PATH]; + wchar_t xfullpath[MAX_PATH]; len = wcslen(originalName); is_quoted = handle_quotes && len > 0 && originalName[0] == L'"' && @@ -2063,7 +1905,7 @@ static int application_type_w (const WCHAR *originalName, /* Name of the applica if (is_quoted) { /* restore quotes on quoted program name */ len = wcslen(wfullpath); - memmove(wfullpath+1,wfullpath,len*sizeof(WCHAR)); + memmove(wfullpath+1,wfullpath,len*sizeof(wchar_t)); wfullpath[0]=L'"'; wfullpath[len+1]=L'"'; wfullpath[len+2]=L'\0'; diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 13aa0f4c00..8f7f98f9e3 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -942,6 +942,20 @@ cd(Config) when is_list(Config) -> Other2 -> test_server:fail({env, Other2}) end, + _ = open_port({spawn, Cmd}, + [{cd, unicode:characters_to_binary(TestDir)}, + {line, 256}]), + receive + {_, {data, {eol, String2}}} -> + case filename_equal(String2, TestDir) of + true -> + ok; + false -> + test_server:fail({cd, String2}) + end; + Other3 -> + test_server:fail({env, Other3}) + end, test_server:timetrap_cancel(Dog), ok. @@ -1346,19 +1360,28 @@ spawn_executable(Config) when is_list(Config) -> EchoArgs1 = filename:join([DataDir,"echo_args"]), ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)), [ExactFile1] = run_echo_args(DataDir,[]), + [ExactFile1] = run_echo_args(DataDir,[binary]), ["echo_args"] = run_echo_args(DataDir,["echo_args"]), + ["echo_args"] = run_echo_args(DataDir,[binary, "echo_args"]), ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]), + ["echo_arguments"] = run_echo_args(DataDir,[binary, "echo_arguments"]), [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]), [ExactFile1] = run_echo_args(DataDir,[default]), + [ExactFile1] = run_echo_args(DataDir,[binary, default]), [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[switch_order,ExactFile1,"hello world", "dlrow olleh"]), [ExactFile1,"hello world","dlrow olleh"] = + run_echo_args(DataDir,[binary,switch_order,ExactFile1,"hello world", + "dlrow olleh"]), + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[default,"hello world","dlrow olleh"]), [ExactFile1,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""), + [ExactFile1,"hello world","dlrow olleh"] = + run_echo_args_2(unicode:characters_to_binary("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\"")), PrivDir = ?config(priv_dir, Config), SpaceDir =filename:join([PrivDir,"With Spaces"]), @@ -1373,6 +1396,8 @@ spawn_executable(Config) when is_list(Config) -> ["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]), [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]), + [ExactFile2,"hello world","dlrow olleh"] = + run_echo_args(SpaceDir,[binary, ExactFile2,"hello world","dlrow olleh"]), [ExactFile2] = run_echo_args(SpaceDir,[default]), [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world", @@ -1381,6 +1406,8 @@ spawn_executable(Config) when is_list(Config) -> run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]), [ExactFile2,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""), + [ExactFile2,"hello world","dlrow olleh"] = + run_echo_args_2(unicode:characters_to_binary("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\"")), ExeExt = case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of @@ -1408,9 +1435,12 @@ spawn_executable(Config) when is_list(Config) -> [default,"hello world","dlrow olleh"]), [ExactFile3,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""), + [ExactFile3,"hello world","dlrow olleh"] = + run_echo_args_2(unicode:characters_to_binary("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\"")), {'EXIT',{enoent,_}} = (catch run_echo_args(SpaceDir,"fnurflmonfi", [default,"hello world", "dlrow olleh"])), + NonExec = "kronxfrt"++ExeExt, file:write_file(filename:join([SpaceDir,NonExec]), <<"Not an executable">>), @@ -1511,25 +1541,40 @@ run_echo_args_2(FullnameAndArgs) -> run_echo_args(Where,Args) -> - run_echo_args(Where,"echo_args",Args). + run_echo_args(Where,"echo_args",Args). run_echo_args(Where,Prog,Args) -> - ArgvArg = case Args of - [] -> - []; - [default|T] -> - [{args,T}]; - [switch_order,H|T] -> - [{args,T},{arg0,H}]; - [H|T] -> - [{arg0,H},{args,T}] + {Binary, ArgvArg} = pack_argv(Args), + Command0 = filename:join([Where,Prog]), + Command = case Binary of + true -> unicode:characters_to_binary(Command0); + false -> Command0 end, - Command = filename:join([Where,Prog]), Port = open_port({spawn_executable,Command},ArgvArg++[eof]), Data = collect_data(Port), Port ! {self(), close}, receive {Port, closed} -> ok end, parse_echo_args_output(Data). - + +pack_argv([binary|Args]) -> + {true, pack_argv(Args, true)}; +pack_argv(Args) -> + {false, pack_argv(Args, false)}. + +pack_argv(Args, Binary) -> + case Args of + [] -> + []; + [default|T] -> + [{args,[make_bin(Arg,Binary) || Arg <- T]}]; + [switch_order,H|T] -> + [{args,[make_bin(Arg,Binary) || Arg <- T]},{arg0,make_bin(H,Binary)}]; + [H|T] -> + [{arg0,make_bin(H,Binary)},{args,[make_bin(Arg,Binary) || Arg <- T]}] + end. + +make_bin(Str, false) -> Str; +make_bin(Str, true) -> unicode:characters_to_binary(Str). + collect_data(Port) -> receive {Port, {data, Data}} -> -- cgit v1.2.3 From 892fb14882ef356c90eda8aa02ca43c3db8b53da Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Mon, 22 Apr 2013 13:18:29 +0200 Subject: erts: Change erlang:open_port/2 spec --- erts/preloaded/ebin/erlang.beam | Bin 93520 -> 93584 bytes erts/preloaded/src/erlang.erl | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index a5c43435df..226e4c8a1f 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 6929ca3fa5..0df0768365 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1714,15 +1714,15 @@ nodes(_Arg) -> erlang:nif_error(undefined). -spec open_port(PortName, PortSettings) -> port() when - PortName :: {spawn, Command :: string()} | - {spawn_driver, Command :: [byte()]} | + PortName :: {spawn, Command :: string() | binary()} | + {spawn_driver, Command :: string() | binary()} | {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()} + | {cd, Dir :: string() | binary()} | {env, Env :: [{Name :: string(), Val :: string() | false}]} | {args, [string() | binary()]} | {arg0, string() | binary()} -- cgit v1.2.3 From e753b347a4c88d169992ceaa87e775aebf935bf3 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 4 Jun 2013 12:04:04 +0200 Subject: erts: Use "+Muacul de" as default --- erts/doc/src/erts_alloc.xml | 21 +++++++++++---------- erts/emulator/beam/erl_alloc.c | 6 +++--- 2 files changed, 14 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index a303ad18a6..9ea2e7a6b5 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -315,16 +315,17 @@ ]]> is an integer in the range [0, 100] representing utilization in percent. When a utilization value larger than zero is used, allocator instances - are allowed to abandon multiblock carriers. Currently the default - is zero. If de (default enabled) is passed instead of a - ]]>, a recomended non zero utilization - value will be used. The actual value chosen depend on allocator - type and may be changed between ERTS versions. Carriers will be - abandoned when memory utilization in the allocator instance falls - below the utilization value used. Once a carrier has been abandoned, - no new allocations will be made in it. When an allocator instance - gets an increased multiblock carrier need, it will first try to - fetch an abandoned carrier from an allocator instances of the same + are allowed to abandon multiblock carriers. If de (default + enabled) is passed instead of a ]]>, + a recomended non zero utilization value will be used. The actual + value chosen depend on allocator type and may be changed between + ERTS versions. Currently the default equals de, but this + may be changed in the future. Carriers will be abandoned when + memory utilization in the allocator instance falls below the + utilization value used. Once a carrier has been abandoned, no new + allocations will be made in it. When an allocator instance gets an + increased multiblock carrier need, it will first try to fetch an + abandoned carrier from an allocator instances of the same allocator type. If no abandoned carrier could be fetched, it will create a new empty carrier. When an abandoned carrier has been fetched it will function as an ordinary carrier. This feature has diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index a547191d6d..65f1f64c03 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -75,9 +75,9 @@ #define ERTS_ALC_DEFAULT_ENABLED_ACUL_EHEAP_ALLOC 45 #define ERTS_ALC_DEFAULT_ENABLED_ACUL_LL_ALLOC 85 -#define ERTS_ALC_DEFAULT_ACUL 0 -#define ERTS_ALC_DEFAULT_ACUL_EHEAP_ALLOC 0 -#define ERTS_ALC_DEFAULT_ACUL_LL_ALLOC 0 +#define ERTS_ALC_DEFAULT_ACUL ERTS_ALC_DEFAULT_ENABLED_ACUL +#define ERTS_ALC_DEFAULT_ACUL_EHEAP_ALLOC ERTS_ALC_DEFAULT_ENABLED_ACUL_EHEAP_ALLOC +#define ERTS_ALC_DEFAULT_ACUL_LL_ALLOC ERTS_ALC_DEFAULT_ENABLED_ACUL_LL_ALLOC #ifndef ERTS_SMP # undef ERTS_ALC_DEFAULT_ACUL -- cgit v1.2.3 From 797721617d11cfab32dbde9eea68c7a7353c8efe Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 12 Jun 2013 10:46:26 +0200 Subject: crypto: Supress some false positives from valgrind --- erts/emulator/valgrind/suppress.standard | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/emulator/valgrind/suppress.standard b/erts/emulator/valgrind/suppress.standard index beecf1a7b5..a4da31a61d 100644 --- a/erts/emulator/valgrind/suppress.standard +++ b/erts/emulator/valgrind/suppress.standard @@ -174,6 +174,7 @@ obj:*/crypto.valgrind.* { Crypto internal... Memcheck:Cond +... obj:*/libcrypto.* } { @@ -194,6 +195,7 @@ obj:*/crypto.valgrind.* { Crypto internal... Memcheck:Value8 +... obj:*/libcrypto.* } { -- cgit v1.2.3 From 55d6274f67cafe62e4923a6369c99a45822cb767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Fri, 14 Jun 2013 15:23:40 +0200 Subject: Fix external term format BIFs on floating point middle-endian machines This complements 933e701 (OTP-10209). Simple error example: 1> <<131,70,63,240,0,0,0,0,0,0>> = term_to_binary(1.0, [{minor_version,1}]). ** exception error: no match of right hand side value <<131,70,0,0,0,0,63,240,0,0>> 2> 1.0 = binary_to_term(<<131,70,63,240,0,0,0,0,0,0>>). ** exception error: no match of right hand side value 5.299808824e-315 But roundtrip always works: 3> 1.0 = binary_to_term(term_to_binary(1.0, [{minor_version,1}])). 1.0 --- erts/emulator/beam/external.c | 4 ++-- erts/emulator/test/binary_SUITE.erl | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 45025ad631..0d56de49c9 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2316,7 +2316,7 @@ enc_term_int(Process *p,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dfla GET_DOUBLE(obj, f); if (dflags & DFLAG_NEW_FLOATS) { *ep++ = NEW_FLOAT_EXT; -#ifdef WORDS_BIGENDIAN +#if defined(WORDS_BIGENDIAN) || defined(DOUBLE_MIDDLE_ENDIAN) put_int32(f.fw[0], ep); ep += 4; put_int32(f.fw[1], ep); @@ -2795,7 +2795,7 @@ dec_term_atom_common: volatile unsigned long *fpexnp = erts_get_current_fp_exception(); #endif -#ifdef WORDS_BIGENDIAN +#if defined(WORDS_BIGENDIAN) || defined(DOUBLE_MIDDLE_ENDIAN) ff.fw[0] = get_int32(ep); ep += 4; ff.fw[1] = get_int32(ep); diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 1e924a0fee..f059d0bf96 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -47,7 +47,8 @@ copy_terms/1, conversions/1, deep_lists/1, deep_bitstr_lists/1, bad_list_to_binary/1, bad_binary_to_list/1, t_split_binary/1, bad_split/1, - terms/1, terms_float/1, external_size/1, t_iolist_size/1, + terms/1, terms_float/1, float_middle_endian/1, + external_size/1, t_iolist_size/1, t_hash/1, bad_size/1, bad_term_to_binary/1, @@ -69,7 +70,7 @@ all() -> [copy_terms, conversions, deep_lists, deep_bitstr_lists, t_split_binary, bad_split, bad_list_to_binary, bad_binary_to_list, terms, - terms_float, external_size, t_iolist_size, + terms_float, float_middle_endian, external_size, t_iolist_size, bad_binary_to_term_2, safe_binary_to_term2, bad_binary_to_term, bad_terms, t_hash, bad_size, bad_term_to_binary, more_bad_terms, otp_5484, otp_5933, @@ -486,6 +487,11 @@ terms_float(Config) when is_list(Config) -> true = Size1 < Size0 end). +float_middle_endian(Config) when is_list(Config) -> + %% Testing for roundtrip is not enough. + ?line <<131,70,63,240,0,0,0,0,0,0>> = term_to_binary(1.0, [{minor_version,1}]), + ?line 1.0 = binary_to_term(<<131,70,63,240,0,0,0,0,0,0>>). + external_size(Config) when is_list(Config) -> %% Build a term whose external size only fits in a big num (on 32-bit CPU). ?line external_size_1(16#11111111111111117777777777777777888889999, 0, 16#FFFFFFF), -- cgit v1.2.3 From 29465408c90b2271b68e9559b5482fc6c4fcdde5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Fri, 14 Jun 2013 15:23:54 +0200 Subject: Fix erlang:phash2() on floating point middle-endian machines This complements 933e701 (OTP-10209). Without this patch the test case "hash_SUITE:test_phash2/1" fails. Simple error example: 1> 77147068 = erlang:phash2(1.0). ** exception error: no match of right hand side value 50524433 --- erts/emulator/beam/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 62caa67ce1..ccc411462e 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1319,7 +1319,7 @@ make_hash2(Eterm term) { FloatDef ff; GET_DOUBLE(term, ff); -#if defined(WORDS_BIGENDIAN) +#if defined(WORDS_BIGENDIAN) || defined(DOUBLE_MIDDLE_ENDIAN) UINT32_HASH_2(ff.fw[0], ff.fw[1], HCONST_12); #else UINT32_HASH_2(ff.fw[1], ff.fw[0], HCONST_12); -- cgit v1.2.3 From 5adbd7d22bccc57e17ed00cac09fe8a336bb39c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Fri, 14 Jun 2013 15:23:59 +0200 Subject: Fix binary matching on floating point middle-endian machines This complements 933e701 (OTP-10209). Without this patch the test case "bs_match_misc_SUITE:t_float/1" fails. Simple error example: 1> <<_,_,_,_,_,_,_,_>> = <<1.25/float>>. <<63,244,0,0,0,0,0,0>> 2> <<1.25/float>> = <<63,244,0,0,0,0,0,0>>. ** exception error: no match of right hand side value <<63,244,0,0,0,0,0,0>> The additional test case is added because in a former version of this patch the ERTS_FP_ERROR_THOROUGH check for NaN/infinity was mistakenly applied on the still word-switched double. --- erts/emulator/beam/erl_bits.c | 8 ++++++++ erts/emulator/test/bs_match_misc_SUITE.erl | 12 ++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 3753b618e1..06d53efb5e 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -484,8 +484,16 @@ erts_bs_get_float_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuffer ERTS_FP_ERROR_THOROUGH(p, f32, return THE_NON_VALUE); f.fd = f32; } else { +#ifdef DOUBLE_MIDDLE_ENDIAN + FloatDef ftmp; + ftmp.fd = f64; + f.fw[0] = ftmp.fw[1]; + f.fw[1] = ftmp.fw[0]; + ERTS_FP_ERROR_THOROUGH(p, f.fd, return THE_NON_VALUE); +#else ERTS_FP_ERROR_THOROUGH(p, f64, return THE_NON_VALUE); f.fd = f64; +#endif } mb->offset += num_bits; hp = HeapOnlyAlloc(p, FLOAT_SIZE_OBJECT); diff --git a/erts/emulator/test/bs_match_misc_SUITE.erl b/erts/emulator/test/bs_match_misc_SUITE.erl index 15427661f3..b0904acbe9 100644 --- a/erts/emulator/test/bs_match_misc_SUITE.erl +++ b/erts/emulator/test/bs_match_misc_SUITE.erl @@ -23,7 +23,8 @@ bound_var/1,bound_tail/1,t_float/1,little_float/1,sean/1, kenneth/1,encode_binary/1,native/1,happi/1, size_var/1,wiger/1,x0_context/1,huge_float_field/1, - writable_binary_matched/1,otp_7198/1,unordered_bindings/1]). + writable_binary_matched/1,otp_7198/1,unordered_bindings/1, + float_middle_endian/1]). -include_lib("test_server/include/test_server.hrl"). @@ -33,7 +34,7 @@ all() -> [bound_var, bound_tail, t_float, little_float, sean, kenneth, encode_binary, native, happi, size_var, wiger, x0_context, huge_float_field, writable_binary_matched, - otp_7198, unordered_bindings]. + otp_7198, unordered_bindings, float_middle_endian]. groups() -> []. @@ -92,6 +93,13 @@ t_float(Config) when is_list(Config) -> ok. +float_middle_endian(Config) when is_list(Config) -> + F = 9007199254740990.0, % turns to -NaN when word-swapped + ?line fcmp(F, match_float(<>, 64, 0)), + ?line fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)), + ?line fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)), + ok. + fcmp(F1, F2) when (F1 - F2) / F2 < 0.0000001 -> ok. -- cgit v1.2.3 From 9e45648698862905561baa533831afd4dd23a02a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Fri, 14 Jun 2013 15:24:04 +0200 Subject: Fix binary construction on floating point middle-endian machines This complements 933e701 (OTP-10209). Without this patch the test cases "in_guard/1" and "coerce_to_float/1" in bs_construct_SUITE fail. The added lines in bs_construct_SUITE cover all branches that were not covered before (small and big numbers if BIT_OFFSET(erts_bin_offset) != 0). --- erts/emulator/beam/erl_bits.c | 26 ++++++++++++++++++++++++++ erts/emulator/test/bs_construct_SUITE.erl | 2 ++ 2 files changed, 28 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 06d53efb5e..43eb691338 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -1022,8 +1022,13 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags) #endif } else if (is_small(arg)) { u.f64 = (double) signed_val(arg); +#ifdef DOUBLE_MIDDLE_ENDIAN + a = u.i32[1]; + b = u.i32[0]; +#else a = u.i32[0]; b = u.i32[1]; +#endif } else if (is_big(arg)) { if (big_to_double(arg, &u.f64) < 0) { return 0; @@ -1126,21 +1131,42 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags) byte *bptr; double f64; float f32; +#ifdef DOUBLE_MIDDLE_ENDIAN + FloatDef fbuf, ftmp; +#endif if (num_bits == 64) { if (is_float(arg)) { +#ifdef DOUBLE_MIDDLE_ENDIAN + FloatDef *fdp = (FloatDef*)(float_val(arg) + 1); + ftmp = *fdp; +#else bptr = (byte *) (float_val(arg) + 1); +#endif } else if (is_small(arg)) { f64 = (double) signed_val(arg); +#ifdef DOUBLE_MIDDLE_ENDIAN + ftmp.fd = f64; +#else bptr = (byte *) &f64; +#endif } else if (is_big(arg)) { if (big_to_double(arg, &f64) < 0) { return 0; } +#ifdef DOUBLE_MIDDLE_ENDIAN + ftmp.fd = f64; +#else bptr = (byte *) &f64; +#endif } else { return 0; } +#ifdef DOUBLE_MIDDLE_ENDIAN + fbuf.fw[0] = ftmp.fw[1]; + fbuf.fw[1] = ftmp.fw[0]; + bptr = fbuf.fb; +#endif } else if (num_bits == 32) { if (is_float(arg)) { FloatDef f; diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 123952d01d..1a8724d63b 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -438,6 +438,8 @@ in_guard(Config) when is_list(Config) -> ?line 1 = in_guard(<<16#74ad:16>>, 16#e95, 5), ?line 2 = in_guard(<<16#3A,16#F7,"hello">>, 16#3AF7, <<"hello">>), ?line 3 = in_guard(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415), + ?line 3 = in_guard(<<16#FBCD:14,3/float,3:2>>, 16#FBCD, 3), + ?line 3 = in_guard(<<16#FBCD:14,(2 bsl 226)/float,3:2>>, 16#FBCD, 2 bsl 226), nope = in_guard(<<1>>, 42, b), nope = in_guard(<<1>>, a, b), nope = in_guard(<<1,2>>, 1, 1), -- cgit v1.2.3 From 10d93045982fc1477a4dade95d1cc400928fec14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Lid=C3=A9n?= Date: Wed, 19 Jun 2013 13:53:39 +0200 Subject: Update version numbers for R16B02 development --- erts/vsn.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index 255def22ca..e235c50f0b 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,8 +17,8 @@ # %CopyrightEnd% # -VSN = 5.10.2 -SYSTEM_VSN = R16B01 +VSN = 5.10.3 +SYSTEM_VSN = R16B02 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 5d67cc7f4c176ea6ab4d2538443d5fe264152861 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 19 Jun 2013 18:34:56 +0200 Subject: erts: Add new allocator strategy aoffcbf with better performance than aoffcaobf as we don't have to rearrange the search tree when there are blocks of equal size. --- erts/doc/src/erts_alloc.xml | 14 +- erts/emulator/beam/erl_alloc.c | 14 +- erts/emulator/beam/erl_ao_firstfit_alloc.c | 190 +++++++++++++++------ erts/emulator/beam/erl_ao_firstfit_alloc.h | 11 +- erts/emulator/beam/erl_bestfit_alloc.c | 6 +- .../test/alloc_SUITE_data/allocator_test.h | 3 +- erts/emulator/test/alloc_SUITE_data/coalesce.c | 2 +- erts/emulator/test/alloc_SUITE_data/rbtree.c | 90 +++++----- 8 files changed, 220 insertions(+), 110 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 2ffb55c6ab..5e008493b9 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -170,6 +170,15 @@ used. The time complexity is proportional to log N, where N is the number of free blocks.

+ Address order first fit carrier best fit + +

Strategy: Find the carrier with the lowest address that + can satisfy the requested block size, then find a block within + that carrier using the "best fit" strategy.

+

Implementation: Balanced binary search trees are + used. The time complexity is proportional to log N, where + N is the number of free blocks.

+
Address order first fit carrier address order best fit

Strategy: Find the carrier with the lowest address that @@ -330,7 +339,7 @@ fetched it will function as an ordinary carrier. This feature has special requirements on the allocation strategy used. Currently - only the aoff and the aoffcaobf strategies support + only the strategies aoff, aoffcbf and aoffcaobf support abandoned carriers. This feature also requires multiple thread specific instances to be enabled. When enabling this feature, multiple thread specific @@ -340,10 +349,11 @@ allocators based on the alloc_util framework with the exception of temp_alloc (which would be pointless). - as bf|aobf|aoff|aoffcaobf|gf|af]]> + as bf|aobf|aoff|aoffcbf|aoffcaobf|gf|af]]> Allocation strategy. Valid strategies are bf (best fit), aobf (address order best fit), aoff (address order first fit), + aoffcbf (address order first fit carrier best fit), aoffcaobf (address order first fit carrier address order best fit), gf (good fit), and af (a fit). See the description of allocation strategies in "the alloc_util framework" section. diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index a547191d6d..7b77bcca58 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -245,7 +245,7 @@ set_default_acul(struct au_init *ip, int acul) { ip->thr_spec = 1; ip->atype = AOFIRSTFIT; - ip->init.aoff.bf_within_carrier = 1; + ip->init.aoff.flavor = AOFF_AOBF; ip->init.util.acul = acul; } @@ -558,7 +558,7 @@ static ERTS_INLINE int strategy_support_carrier_migration(struct au_init *auip) { /* - * Currently only aoff and aoffcaobf support carrier + * Currently only aoff, aoffcbf and aoffcaobf support carrier * migration, i.e, type AOFIRSTFIT. */ return auip->atype == AOFIRSTFIT; @@ -583,7 +583,7 @@ ensure_carrier_migration_support(struct au_init *auip) if (!strategy_support_carrier_migration(auip)) { /* Default to aoffcaobf */ auip->atype = AOFIRSTFIT; - auip->init.aoff.bf_within_carrier = 1; + auip->init.aoff.flavor = AOFF_AOBF; } } @@ -1284,11 +1284,15 @@ handle_au_arg(struct au_init *auip, } else if (strcmp("aoff", alg) == 0) { auip->atype = AOFIRSTFIT; - auip->init.aoff.bf_within_carrier = 0; + auip->init.aoff.flavor = AOFF_AOFF; + } + else if (strcmp("aoffcbf", alg) == 0) { + auip->atype = AOFIRSTFIT; + auip->init.aoff.flavor = AOFF_BF; } else if (strcmp("aoffcaobf", alg) == 0) { auip->atype = AOFIRSTFIT; - auip->init.aoff.bf_within_carrier = 1; + auip->init.aoff.flavor = AOFF_AOBF; } else { bad_value(param, sub_param + 1, alg); diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c index f73ac2eb6e..4e6c8b317e 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.c +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c @@ -34,10 +34,11 @@ * sub-tree ('max_sz'). By that we can start from root and keep * left (for low addresses) while dismissing entire sub-trees with * too small blocks. - * AOFFCBF: + * Bestfit within carrier: * The only difference for "bestfit within carrier" is the tree * sorting order. Blocks within the same carrier are sorted - * wrt size instead of address. + * wrt size instead of address. The 'max_sz' field is maintained + * in order to dismiss entire carriers with too small blocks. * * Authors: Rickard Green/Sverker Eriksson */ @@ -67,6 +68,15 @@ # define LEFT_VISITED_FLG (((Uint) 1) << 2) # define RIGHT_VISITED_FLG (((Uint) 1) << 3) #endif +#ifdef DEBUG +# define IS_BF_FLG (((Uint) 1) << 4) +#endif + +#define IS_TREE_NODE(N) (((AOFF_RBTree_t *) (N))->flags & TREE_NODE_FLG) +#define IS_LIST_ELEM(N) (!IS_TREE_NODE(((AOFF_RBTree_t *) (N)))) + +#define SET_TREE_NODE(N) (((AOFF_RBTree_t *) (N))->flags |= TREE_NODE_FLG) +#define SET_LIST_ELEM(N) (((AOFF_RBTree_t *) (N))->flags &= ~TREE_NODE_FLG) #define IS_RED(N) (((AOFF_RBTree_t *) (N)) \ && ((AOFF_RBTree_t *) (N))->flags & RED_FLG) @@ -98,6 +108,16 @@ struct AOFF_RBTree_t_ { }; #define AOFF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->hdr) +/* BF block nodes keeps list of all with equal size + */ +typedef struct { + AOFF_RBTree_t t; + AOFF_RBTree_t *next; +}AOFF_RBTreeList_t; + +#define LIST_NEXT(N) (((AOFF_RBTreeList_t*) (N))->next) +#define LIST_PREV(N) (((AOFF_RBTreeList_t*) (N))->t.parent) + typedef struct AOFF_Carrier_t_ AOFF_Carrier_t; struct AOFF_Carrier_t_ { @@ -119,11 +139,11 @@ struct AOFF_Carrier_t_ { #ifdef HARD_DEBUG # define HARD_CHECK_IS_MEMBER(ROOT,NODE) rbt_assert_is_member(ROOT,NODE) -# define HARD_CHECK_TREE(CRR,BF,ROOT,SZ) check_tree(CRR, BF, ROOT, SZ) -static AOFF_RBTree_t * check_tree(Carrier_t* within_crr, int bestfit, AOFF_RBTree_t* root, Uint); +# define HARD_CHECK_TREE(CRR,FLV,ROOT,SZ) check_tree(CRR, FLV, ROOT, SZ) +static AOFF_RBTree_t * check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root, Uint); #else # define HARD_CHECK_IS_MEMBER(ROOT,NODE) -# define HARD_CHECK_TREE(CRR,BF,ROOT,SZ) +# define HARD_CHECK_TREE(CRR,FLV,ROOT,SZ) #endif @@ -161,25 +181,25 @@ static ERTS_INLINE void lower_max_size(AOFF_RBTree_t *node, else ASSERT(new_max == old_max); } -static ERTS_INLINE SWord cmp_blocks(int bestfit, +static ERTS_INLINE SWord cmp_blocks(enum AOFF_Flavor flavor, AOFF_RBTree_t* lhs, AOFF_RBTree_t* rhs) { ASSERT(lhs != rhs); - ASSERT(!bestfit || FBLK_TO_MBC(&lhs->hdr) == FBLK_TO_MBC(&rhs->hdr)); - if (bestfit) { + ASSERT(flavor == AOFF_AOFF || FBLK_TO_MBC(&lhs->hdr) == FBLK_TO_MBC(&rhs->hdr)); + if (flavor != AOFF_AOFF) { SWord diff = (SWord)AOFF_BLK_SZ(lhs) - (SWord)AOFF_BLK_SZ(rhs); - if (diff) return diff; + if (diff || flavor == AOFF_BF) return diff; } return (char*)lhs - (char*)rhs; } -static ERTS_INLINE SWord cmp_cand_blk(int bestfit, +static ERTS_INLINE SWord cmp_cand_blk(enum AOFF_Flavor flavor, Block_t* cand_blk, AOFF_RBTree_t* rhs) { - if (bestfit) { + if (flavor != AOFF_AOFF) { if (BLK_TO_MBC(cand_blk) == FBLK_TO_MBC(&rhs->hdr)) { SWord diff = (SWord)MBC_BLK_SZ(cand_blk) - (SWord)MBC_FBLK_SZ(&rhs->hdr); - if (diff) return diff; + if (diff || flavor == AOFF_BF) return diff; } } return (char*)cand_blk - (char*)rhs; @@ -198,7 +218,7 @@ static UWord aoff_largest_fblk_in_mbc(Allctr_t*, Carrier_t*); /* Generic tree functions used by both carrier and block trees. */ static void rbt_delete(AOFF_RBTree_t** root, AOFF_RBTree_t* del); -static void rbt_insert(int bestfit, AOFF_RBTree_t** root, AOFF_RBTree_t* blk); +static void rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk); static AOFF_RBTree_t* rbt_search(AOFF_RBTree_t* root, Uint size); #ifdef HARD_DEBUG static int rbt_assert_is_member(AOFF_RBTree_t* root, AOFF_RBTree_t* node); @@ -234,21 +254,21 @@ erts_aoffalc_start(AOFFAllctr_t *alc, sys_memcpy((void *) alc, (void *) &zero.allctr, sizeof(AOFFAllctr_t)); - alc->bf_within_carrier = aoffinit->bf_within_carrier; + alc->flavor = aoffinit->flavor; allctr->mbc_header_size = sizeof(AOFF_Carrier_t); allctr->min_mbc_size = MIN_MBC_SZ; allctr->min_mbc_first_free_size = MIN_MBC_FIRST_FREE_SZ; - allctr->min_block_size = sizeof(AOFF_RBTree_t); + allctr->min_block_size = (aoffinit->flavor == AOFF_BF ? + sizeof(AOFF_RBTreeList_t):sizeof(AOFF_RBTree_t)); - allctr->vsn_str = aoffinit->bf_within_carrier ? - ERTS_ALC_AOFF_CBF_ALLOC_VSN_STR : ERTS_ALC_AOFF_ALLOC_VSN_STR; + allctr->vsn_str = ERTS_ALC_AOFF_ALLOC_VSN_STR; /* Callback functions */ allctr->get_free_block = aoff_get_free_block; allctr->link_free_block = aoff_link_free_block; - allctr->unlink_free_block = aoff_unlink_free_block; + allctr->unlink_free_block = aoff_unlink_free_block; allctr->info_options = info_options; allctr->get_next_mbc_size = NULL; @@ -359,9 +379,7 @@ replace(AOFF_RBTree_t **root, AOFF_RBTree_t *x, AOFF_RBTree_t *y) y->parent = x->parent; y->right = x->right; y->left = x->left; - - y->max_sz = x->max_sz; - lower_max_size(y, NULL); + y->max_sz = x->max_sz; } static void @@ -458,23 +476,47 @@ tree_insert_fixup(AOFF_RBTree_t** root, AOFF_RBTree_t *blk) } static void -aoff_unlink_free_block(Allctr_t *allctr, Block_t *del) +aoff_unlink_free_block(Allctr_t *allctr, Block_t *blk) { -#ifdef HARD_DEBUG AOFFAllctr_t* alc = (AOFFAllctr_t*)allctr; -#endif - AOFF_Carrier_t *crr = (AOFF_Carrier_t*) FBLK_TO_MBC(del); + AOFF_RBTree_t* del = (AOFF_RBTree_t*)blk; + AOFF_Carrier_t *crr = (AOFF_Carrier_t*) FBLK_TO_MBC(&del->hdr); ASSERT(crr->rbt_node.hdr.bhdr == crr->root->max_sz); - HARD_CHECK_IS_MEMBER(alc->mbc_root, &crr->rbt_node); - HARD_CHECK_TREE(&crr->crr, alc->bf_within_carrier, crr->root, 0); + HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, 0); + + if (alc->flavor == AOFF_BF) { + ASSERT(del->flags & IS_BF_FLG); + if (IS_LIST_ELEM(del)) { + /* Remove from list */ + ASSERT(LIST_PREV(del)); + ASSERT(LIST_PREV(del)->flags & IS_BF_FLG); + LIST_NEXT(LIST_PREV(del)) = LIST_NEXT(del); + if (LIST_NEXT(del)) { + ASSERT(LIST_NEXT(del)->flags & IS_BF_FLG); + LIST_PREV(LIST_NEXT(del)) = LIST_PREV(del); + } + return; + } + else if (LIST_NEXT(del)) { + /* Replace tree node by next element in list... */ + + ASSERT(AOFF_BLK_SZ(LIST_NEXT(del)) == AOFF_BLK_SZ(del)); + ASSERT(IS_LIST_ELEM(LIST_NEXT(del))); + + replace(&crr->root, (AOFF_RBTree_t*)del, LIST_NEXT(del)); + + HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, 0); + return; + } + } rbt_delete(&crr->root, (AOFF_RBTree_t*)del); - HARD_CHECK_TREE(&crr->crr, alc->bf_within_carrier, crr->root, 0); + HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, 0); - /* Update the carrier tree with a potentially new (lower) max_sz - */ + /* Update the carrier tree with a potentially new (lower) max_sz + */ if (crr->root) { if (crr->rbt_node.hdr.bhdr == crr->root->max_sz) { return; @@ -488,6 +530,7 @@ aoff_unlink_free_block(Allctr_t *allctr, Block_t *del) lower_max_size(&crr->rbt_node, NULL); } + static void rbt_delete(AOFF_RBTree_t** root, AOFF_RBTree_t* del) { @@ -542,7 +585,9 @@ rbt_delete(AOFF_RBTree_t** root, AOFF_RBTree_t* del) } if (y != z) { /* We spliced out the successor of z; replace z by the successor */ + ASSERT(z != &null_x); replace(root, z, y); + lower_max_size(y, NULL); } if (spliced_is_black) { @@ -666,12 +711,11 @@ aoff_link_free_block(Allctr_t *allctr, Block_t *block) ASSERT(allctr == ERTS_ALC_CARRIER_TO_ALLCTR(&blk_crr->crr)); ASSERT(blk_crr->rbt_node.hdr.bhdr == (blk_crr->root ? blk_crr->root->max_sz : 0)); - HARD_CHECK_IS_MEMBER(alc->mbc_root, &blk_crr->rbt_node); - HARD_CHECK_TREE(&blk_crr->crr, alc->bf_within_carrier, blk_crr->root, 0); + HARD_CHECK_TREE(&blk_crr->crr, alc->flavor, blk_crr->root, 0); - rbt_insert(alc->bf_within_carrier, &blk_crr->root, blk); + rbt_insert(alc->flavor, &blk_crr->root, blk); - /* Update the carrier tree with a potential new (larger) max_sz + /* Update the carrier tree with a potentially new (larger) max_sz */ crr_node = &blk_crr->rbt_node; if (blk_sz > crr_node->hdr.bhdr) { @@ -683,15 +727,19 @@ aoff_link_free_block(Allctr_t *allctr, Block_t *block) if (!crr_node) break; } } - HARD_CHECK_TREE(&blk_crr->crr, alc->bf_within_carrier, blk_crr->root, 0); + HARD_CHECK_TREE(&blk_crr->crr, alc->flavor, blk_crr->root, 0); } static void -rbt_insert(int bestfit, AOFF_RBTree_t** root, AOFF_RBTree_t* blk) +rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk) { Uint blk_sz = AOFF_BLK_SZ(blk); - blk->flags = 0; +#ifdef DEBUG + blk->flags = (flavor == AOFF_BF) ? IS_BF_FLG : 0; +#else + blk->flags = 0; +#endif blk->left = NULL; blk->right = NULL; blk->max_sz = blk_sz; @@ -704,10 +752,12 @@ rbt_insert(int bestfit, AOFF_RBTree_t** root, AOFF_RBTree_t* blk) else { AOFF_RBTree_t *x = *root; while (1) { + SWord diff; if (x->max_sz < blk_sz) { x->max_sz = blk_sz; } - if (cmp_blocks(bestfit, blk, x) < 0) { + diff = cmp_blocks(flavor, blk, x); + if (diff < 0) { if (!x->left) { blk->parent = x; x->left = blk; @@ -715,7 +765,7 @@ rbt_insert(int bestfit, AOFF_RBTree_t** root, AOFF_RBTree_t* blk) } x = x->left; } - else { + else if (diff > 0) { if (!x->right) { blk->parent = x; x->right = blk; @@ -723,6 +773,18 @@ rbt_insert(int bestfit, AOFF_RBTree_t** root, AOFF_RBTree_t* blk) } x = x->right; } + else { + ASSERT(flavor == AOFF_BF); + ASSERT(blk->flags & IS_BF_FLG); + ASSERT(x->flags & IS_BF_FLG); + SET_LIST_ELEM(blk); + LIST_NEXT(blk) = LIST_NEXT(x); + LIST_PREV(blk) = x; + if (LIST_NEXT(x)) + LIST_PREV(LIST_NEXT(x)) = blk; + LIST_NEXT(x) = blk; + return; + } } /* Insert block into size tree */ @@ -732,6 +794,10 @@ rbt_insert(int bestfit, AOFF_RBTree_t** root, AOFF_RBTree_t* blk) if (IS_RED(blk->parent)) tree_insert_fixup(root, blk); } + if (flavor == AOFF_BF) { + SET_TREE_NODE(blk); + LIST_NEXT(blk) = NULL; + } } static AOFF_RBTree_t* @@ -780,7 +846,7 @@ aoff_get_free_block(Allctr_t *allctr, Uint size, /* Get block within carrier tree */ #ifdef HARD_DEBUG - dbg_blk = HARD_CHECK_TREE(&crr->crr, alc->bf_within_carrier, crr->root, size); + dbg_blk = HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, size); #endif blk = rbt_search(crr->root, size); @@ -793,7 +859,7 @@ aoff_get_free_block(Allctr_t *allctr, Uint size, if (!blk) return NULL; - if (cand_blk && cmp_cand_blk(alc->bf_within_carrier, cand_blk, blk) < 0) { + if (cand_blk && cmp_cand_blk(alc->flavor, cand_blk, blk) < 0) { return NULL; /* cand_blk was better */ } @@ -813,7 +879,7 @@ static void aoff_creating_mbc(Allctr_t *allctr, Carrier_t *carrier) /* Link carrier in address order tree */ crr->rbt_node.hdr.bhdr = 0; - rbt_insert(0, root, &crr->rbt_node); + rbt_insert(AOFF_AOFF, root, &crr->rbt_node); /* aoff_link_free_block will add free block later */ crr->root = NULL; @@ -843,7 +909,7 @@ static void aoff_add_mbc(Allctr_t *allctr, Carrier_t *carrier) /* Link carrier in address order tree */ - rbt_insert(0, root, &crr->rbt_node); + rbt_insert(AOFF_AOFF, root, &crr->rbt_node); HARD_CHECK_TREE(NULL, 0, *root, 0); } @@ -883,6 +949,7 @@ static struct { Eterm as; Eterm aoff; Eterm aoffcaobf; + Eterm aoffcbf; #ifdef DEBUG Eterm end_of_atoms; #endif @@ -912,6 +979,7 @@ init_atoms(void) AM_INIT(as); AM_INIT(aoff); AM_INIT(aoffcaobf); + AM_INIT(aoffcbf); #ifdef DEBUG for (atom = (Eterm *) &am; atom < &am.end_of_atoms; atom++) { @@ -943,13 +1011,15 @@ info_options(Allctr_t *allctr, { AOFFAllctr_t* alc = (AOFFAllctr_t*) allctr; Eterm res = THE_NON_VALUE; + const char* flavor_str[3] = {"aoff", "aoffcaobf", "aoffcbf"}; + Eterm flavor_atom[3] = {am.aoff, am.aoffcaobf, am.aoffcbf}; if (print_to_p) { erts_print(*print_to_p, print_to_arg, "%sas: %s\n", prefix, - alc->bf_within_carrier ? "aoffcaobf" : "aoff"); + flavor_str[alc->flavor]); } if (hpp || szp) { @@ -959,8 +1029,7 @@ info_options(Allctr_t *allctr, __FILE__, __LINE__);; res = NIL; - add_2tup(hpp, szp, &res, am.as, - alc->bf_within_carrier ? am.aoffcaobf : am.aoff); + add_2tup(hpp, szp, &res, am.as, flavor_atom[alc->flavor]); } return res; @@ -978,6 +1047,7 @@ UWord erts_aoffalc_test(UWord op, UWord a1, UWord a2) { switch (op) { + case 0x500: return (UWord) ((AOFFAllctr_t *) a1)->flavor == AOFF_AOBF; case 0x501: { AOFF_RBTree_t *node = ((AOFFAllctr_t *) a1)->mbc_root; Uint size = (Uint) a2; @@ -987,10 +1057,13 @@ erts_aoffalc_test(UWord op, UWord a1, UWord a2) case 0x502: return (UWord) ((AOFF_RBTree_t *) a1)->parent; case 0x503: return (UWord) ((AOFF_RBTree_t *) a1)->left; case 0x504: return (UWord) ((AOFF_RBTree_t *) a1)->right; + case 0x505: return (UWord) LIST_NEXT(a1); case 0x506: return (UWord) IS_BLACK((AOFF_RBTree_t *) a1); + case 0x507: return (UWord) IS_TREE_NODE((AOFF_RBTree_t *) a1); case 0x508: return (UWord) 0; /* IS_BF_ALGO */ case 0x509: return (UWord) ((AOFF_RBTree_t *) a1)->max_sz; - case 0x50a: return (UWord) ((AOFFAllctr_t *) a1)->bf_within_carrier; + case 0x50a: return (UWord) ((AOFFAllctr_t *) a1)->flavor == AOFF_BF; + case 0x50b: return (UWord) LIST_PREV(a1); default: ASSERT(0); return ~((UWord) 0); } } @@ -1049,7 +1122,7 @@ static void print_tree(AOFF_RBTree_t*); */ static AOFF_RBTree_t * -check_tree(Carrier_t* within_crr, int bestfit, AOFF_RBTree_t* root, Uint size) +check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root, Uint size) { AOFF_RBTree_t *res = NULL; Sint blacks; @@ -1061,6 +1134,7 @@ check_tree(Carrier_t* within_crr, int bestfit, AOFF_RBTree_t* root, Uint size) #ifdef PRINT_TREE print_tree(root); #endif + ASSERT(within_crr || flavor == AOFF_AOFF); if (!root) return res; @@ -1116,6 +1190,20 @@ check_tree(Carrier_t* within_crr, int bestfit, AOFF_RBTree_t* root, Uint size) ASSERT(crr == within_crr); ASSERT((char*)x > (char*)crr); ASSERT(((char*)x + AOFF_BLK_SZ(x)) <= ((char*)crr + CARRIER_SZ(crr))); + + } + if (flavor == AOFF_BF) { + AOFF_RBTree_t* y = x; + AOFF_RBTree_t* nxt = LIST_NEXT(y); + ASSERT(IS_TREE_NODE(x)); + while (nxt) { + ASSERT(IS_LIST_ELEM(nxt)); + ASSERT(AOFF_BLK_SZ(nxt) == AOFF_BLK_SZ(x)); + ASSERT(FBLK_TO_MBC(&nxt->hdr) == within_crr); + ASSERT(LIST_PREV(nxt) == y); + y = nxt; + nxt = LIST_NEXT(nxt); + } } if (IS_RED(x)) { @@ -1127,13 +1215,13 @@ check_tree(Carrier_t* within_crr, int bestfit, AOFF_RBTree_t* root, Uint size) if (x->left) { ASSERT(x->left->parent == x); - ASSERT(cmp_blocks(bestfit, x->left, x) < 0); + ASSERT(cmp_blocks(flavor, x->left, x) < 0); ASSERT(x->left->max_sz <= x->max_sz); } if (x->right) { ASSERT(x->right->parent == x); - ASSERT(cmp_blocks(bestfit, x->right, x) > 0); + ASSERT(cmp_blocks(flavor, x->right, x) > 0); ASSERT(x->right->max_sz <= x->max_sz); } ASSERT(x->max_sz >= AOFF_BLK_SZ(x)); @@ -1142,7 +1230,7 @@ check_tree(Carrier_t* within_crr, int bestfit, AOFF_RBTree_t* root, Uint size) || x->max_sz == (x->right ? x->right->max_sz : 0)); if (size && AOFF_BLK_SZ(x) >= size) { - if (!res || cmp_blocks(bestfit, x, res) < 0) { + if (!res || cmp_blocks(flavor, x, res) < 0) { res = x; } } diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.h b/erts/emulator/beam/erl_ao_firstfit_alloc.h index 87427e8e62..6fa2c0e5bf 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.h +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.h @@ -24,12 +24,17 @@ #include "erl_alloc_util.h" #define ERTS_ALC_AOFF_ALLOC_VSN_STR "0.9" -#define ERTS_ALC_AOFF_CBF_ALLOC_VSN_STR "0.9" typedef struct AOFFAllctr_t_ AOFFAllctr_t; +enum AOFF_Flavor { + AOFF_AOFF = 0, + AOFF_AOBF = 1, + AOFF_BF = 2 +}; + typedef struct { - int bf_within_carrier; + enum AOFF_Flavor flavor; } AOFFAllctrInit_t; #define ERTS_DEFAULT_AOFF_ALLCTR_INIT {0/*dummy*/} @@ -52,7 +57,7 @@ struct AOFFAllctr_t_ { Allctr_t allctr; /* Has to be first! */ struct AOFF_RBTree_t_* mbc_root; - int bf_within_carrier; + enum AOFF_Flavor flavor; }; UWord erts_aoffalc_test(UWord, UWord, UWord); diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c index 58e53c3d00..41f449bb28 100644 --- a/erts/emulator/beam/erl_bestfit_alloc.c +++ b/erts/emulator/beam/erl_bestfit_alloc.c @@ -966,15 +966,17 @@ UWord erts_bfalc_test(UWord op, UWord a1, UWord a2) { switch (op) { - case 0x200: return (UWord) ((BFAllctr_t *) a1)->address_order; + case 0x200: return (UWord) ((BFAllctr_t *) a1)->address_order; /* IS_AOBF */ case 0x201: return (UWord) ((BFAllctr_t *) a1)->mbc_root; case 0x202: return (UWord) ((RBTree_t *) a1)->parent; case 0x203: return (UWord) ((RBTree_t *) a1)->left; case 0x204: return (UWord) ((RBTree_t *) a1)->right; - case 0x205: return (UWord) ((RBTreeList_t *) a1)->next; + case 0x205: return (UWord) LIST_NEXT(a1); case 0x206: return (UWord) IS_BLACK((RBTree_t *) a1); case 0x207: return (UWord) IS_TREE_NODE((RBTree_t *) a1); case 0x208: return (UWord) 1; /* IS_BF_ALGO */ + case 0x20a: return (UWord) !((BFAllctr_t *) a1)->address_order; /* IS_BF */ + case 0x20b: return (UWord) LIST_PREV(a1); default: ASSERT(0); return ~((UWord) 0); } } diff --git a/erts/emulator/test/alloc_SUITE_data/allocator_test.h b/erts/emulator/test/alloc_SUITE_data/allocator_test.h index c37b074f93..2d6c5f9dc7 100644 --- a/erts/emulator/test/alloc_SUITE_data/allocator_test.h +++ b/erts/emulator/test/alloc_SUITE_data/allocator_test.h @@ -102,7 +102,8 @@ typedef void* erts_cond; #define RBT_IS_TREE(T) ((Ulong) ALC_TEST1(RBT_OP(7), (T))) #define IS_BF_ALGO(A) ((Ulong) ALC_TEST1(RBT_OP(8), (A))) #define RBT_MAX_SZ(T) ((Ulong) ALC_TEST1(RBT_OP(9), (T))) -#define IS_CBF(A) ((Ulong) ALC_TEST1(RBT_OP(0xa), (A))) +#define IS_BF(A) ((Ulong) ALC_TEST1(RBT_OP(0xa), (A))) +#define RBT_PREV(T) ((RBTL_t *) ALC_TEST1(RBT_OP(0xb), (T))) /* From erl_mseg.c */ #define HAVE_MSEG() ((int) ALC_TEST0(0x400)) diff --git a/erts/emulator/test/alloc_SUITE_data/coalesce.c b/erts/emulator/test/alloc_SUITE_data/coalesce.c index 36710bf7b5..9da49a0d14 100644 --- a/erts/emulator/test/alloc_SUITE_data/coalesce.c +++ b/erts/emulator/test/alloc_SUITE_data/coalesce.c @@ -267,7 +267,7 @@ void testcase_run(TestCaseState_t *tcs) { char *argv_org[] = {"-tsmbcs511","-tmmbcs511", "-tsbct512", "-trmbcmt100", "-tas", NULL, NULL}; - char *alg[] = {"af", "gf", "bf", "aobf", "aoff", "aoffcaobf", NULL}; + char *alg[] = {"af", "gf", "bf", "aobf", "aoff", "aoffcbf", "aoffcaobf", NULL}; int i; for (i = 0; alg[i]; i++) { diff --git a/erts/emulator/test/alloc_SUITE_data/rbtree.c b/erts/emulator/test/alloc_SUITE_data/rbtree.c index 702f075304..49df2f0245 100644 --- a/erts/emulator/test/alloc_SUITE_data/rbtree.c +++ b/erts/emulator/test/alloc_SUITE_data/rbtree.c @@ -85,23 +85,21 @@ print_tree(TestCaseState_t *tcs, RBT_t *root) static RBT_t * check_tree(TestCaseState_t *tcs, Allctr_t *alc, Ulong size) { - enum { BF, AOBF, AOFF, AOFFCAOBF }type; + enum { BF, AOBF, AOFF } type; int i, max_i; char stk[128]; RBT_t *root, *x, *y, *res; Ulong x_sz, y_sz, is_x_black; long blacks, curr_blacks; + int have_max_sz; res = NULL; - if (IS_BF_ALGO(alc)) { - if (IS_AOBF(alc)) type = AOBF; - else type = BF; - } - else { /* AOFF_ALGO */ - if (IS_CBF(alc)) type = AOFFCAOBF; - else type = AOFF; - } + if (IS_AOBF(alc)) type = AOBF; + else if (IS_BF(alc)) type = BF; + else type = AOFF; + + have_max_sz = !IS_BF_ALGO(alc); root = RBT_ROOT(alc, size); @@ -191,17 +189,10 @@ check_tree(TestCaseState_t *tcs, Allctr_t *alc, Ulong size) break; case AOFF: ASSERT(tcs, y < x); - ASSERT(tcs, RBT_MAX_SZ(y) <= RBT_MAX_SZ(x)); break; - case AOFFCAOBF: - { - void* x_crr = BLK_TO_MBC(x); - void* y_crr = BLK_TO_MBC(y); - ASSERT(tcs, (y < x && (x_crr != y_crr || x_sz == y_sz)) - || (y_sz < x_sz && x_crr == y_crr)); - ASSERT(tcs, RBT_MAX_SZ(y) <= RBT_MAX_SZ(x)); - break; - } + } + if (have_max_sz) { + ASSERT(tcs, RBT_MAX_SZ(y) <= RBT_MAX_SZ(x)); } } @@ -219,27 +210,22 @@ check_tree(TestCaseState_t *tcs, Allctr_t *alc, Ulong size) break; case AOFF: ASSERT(tcs, y > x); - ASSERT(tcs, RBT_MAX_SZ(y) <= RBT_MAX_SZ(x)); break; - case AOFFCAOBF: - { - void* x_crr = BLK_TO_MBC(x); - void* y_crr = BLK_TO_MBC(y); - ASSERT(tcs, (y > x && (x_crr != y_crr || x_sz == y_sz)) - || (y_sz > x_sz && x_crr == y_crr)); - ASSERT(tcs, RBT_MAX_SZ(y) <= RBT_MAX_SZ(x)); - break; - } + } + if (have_max_sz) { + ASSERT(tcs, RBT_MAX_SZ(y) <= RBT_MAX_SZ(x)); } } if (type == BF) { Ulong l_sz; - RBTL_t *l = RBT_NEXT(x); + RBTL_t *l, *prev=x; for (l = RBT_NEXT(x); l; l = RBT_NEXT(l)) { l_sz = BLK_SZ(l); ASSERT(tcs, l_sz == x_sz); ASSERT(tcs, !RBT_IS_TREE(l)); + ASSERT(tcs, RBT_PREV(l) == prev); + prev = l; } } @@ -262,18 +248,7 @@ check_tree(TestCaseState_t *tcs, Allctr_t *alc, Ulong size) res = x; } break; - case AOFFCAOBF: - if (BLK_TO_MBC(x) != BLK_TO_MBC(res) || x_sz == y_sz) { - if (x < res) { - res = x; - } - } - else if (x_sz < y_sz) { - res = x; - } - break; } - } } @@ -310,7 +285,7 @@ do_check(TestCaseState_t *tcs, Allctr_t *a, Ulong size, int ignore_null) tmp = ALLOC(a, sz - ABLK_HDR_SZ); ASSERT(tcs, tmp); y = UMEM2BLK(tmp); - if (!(IS_BF_ALGO(a) && !IS_AOBF(a))) { + if (!IS_BF(a)) { ASSERT(tcs, x == y); } else { @@ -488,6 +463,7 @@ testcase_run(TestCaseState_t *tcs) char *argv2[] = {"-tasaobf", NULL}; char *argv3[] = {"-tasaoff", NULL}; char *argv4[] = {"-tasaoffcaobf", NULL}; + char *argv5[] = {"-tasaoffcbf", NULL}; Allctr_t *a; rbtree_test_data *td; @@ -511,6 +487,7 @@ testcase_run(TestCaseState_t *tcs) ASSERT(tcs, a); ASSERT(tcs, IS_BF_ALGO(a)); ASSERT(tcs, !IS_AOBF(a)); + ASSERT(tcs, IS_BF(a)); test_it(tcs); @@ -529,6 +506,7 @@ testcase_run(TestCaseState_t *tcs) ASSERT(tcs, a); ASSERT(tcs, IS_BF_ALGO(a)); ASSERT(tcs, IS_AOBF(a)); + ASSERT(tcs, !IS_BF(a)); test_it(tcs); @@ -546,7 +524,8 @@ testcase_run(TestCaseState_t *tcs) ASSERT(tcs, a); ASSERT(tcs, !IS_BF_ALGO(a)); - ASSERT(tcs, !IS_CBF(a)); + ASSERT(tcs, !IS_AOBF(a)); + ASSERT(tcs, !IS_BF(a)); test_it(tcs); test_carrier_migration(tcs); @@ -556,7 +535,7 @@ testcase_run(TestCaseState_t *tcs) testcase_printf(tcs, "Address order first fit test succeeded!\n"); - /* Address order first fit, best fit within carrier */ + /* Address order first fit, aobf within carrier */ testcase_printf(tcs, "Starting test of aoffcaobf...\n"); @@ -565,7 +544,28 @@ testcase_run(TestCaseState_t *tcs) ASSERT(tcs, a); ASSERT(tcs, !IS_BF_ALGO(a)); - ASSERT(tcs, IS_CBF(a)); + ASSERT(tcs, IS_AOBF(a)); + ASSERT(tcs, !IS_BF(a)); + + test_it(tcs); + test_carrier_migration(tcs); + + STOP_ALC(a); + td->allocator = NULL; + + testcase_printf(tcs, "aoffcaobf test succeeded!\n"); + + /* Address order first fit, bf within carrier */ + + testcase_printf(tcs, "Starting test of aoffcbf...\n"); + + current_rbt_type_op_base = AO_FIRSTFIT_OP_BASE; + td->allocator = a = START_ALC("rbtree_aoffcbf_", 0, argv5); + + ASSERT(tcs, a); + ASSERT(tcs, !IS_BF_ALGO(a)); + ASSERT(tcs, !IS_AOBF(a)); + ASSERT(tcs, IS_BF(a)); test_it(tcs); test_carrier_migration(tcs); -- cgit v1.2.3 From 8212fdf19997e08a52ff9374283ff5e148561a0c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 19 Jun 2013 18:37:36 +0200 Subject: erts: Make aoffcbf default when migration is enabled --- erts/doc/src/erts_alloc.xml | 2 +- erts/emulator/beam/erl_alloc.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 5e008493b9..6ce2261430 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -344,7 +344,7 @@ multiple thread specific instances to be enabled. When enabling this feature, multiple thread specific instances will be enabled if not already enabled, and the - aoffcaobf strategy will be enabled if current strategy does not + aoffcbf strategy will be enabled if current strategy does not support abandoned carriers. This feature can be enabled on all allocators based on the alloc_util framework with the exception of temp_alloc (which would be pointless). diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 7b77bcca58..306a4f24de 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -245,7 +245,7 @@ set_default_acul(struct au_init *ip, int acul) { ip->thr_spec = 1; ip->atype = AOFIRSTFIT; - ip->init.aoff.flavor = AOFF_AOBF; + ip->init.aoff.flavor = AOFF_BF; ip->init.util.acul = acul; } @@ -581,9 +581,9 @@ ensure_carrier_migration_support(struct au_init *auip) * default to a strategy that can... */ if (!strategy_support_carrier_migration(auip)) { - /* Default to aoffcaobf */ + /* Default to aoffcbf */ auip->atype = AOFIRSTFIT; - auip->init.aoff.flavor = AOFF_AOBF; + auip->init.aoff.flavor = AOFF_BF; } } -- cgit v1.2.3 From 5e0c73fecc31c72f86663bec55819f093151a039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Fri, 5 Jul 2013 14:15:53 +0200 Subject: run_erl: Redirect standard streams to /dev/null Like in epmd and erlexec. Otherwise "run_erl" fails on some platforms when called with the "-daemon" option, printing this error: "Cannot dup" --- erts/etc/unix/run_erl.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'erts') diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index 53c779b1be..b69e31f784 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -1142,6 +1142,14 @@ static void daemon_init(void) sf_close(i); } + /* Necessary on some platforms */ + + open("/dev/null", O_RDONLY); /* Order is important! */ + open("/dev/null", O_WRONLY); + open("/dev/null", O_WRONLY); + + errno = 0; /* if set by open */ + OPEN_SYSLOG(); run_daemon = 1; } -- cgit v1.2.3 From 604d320f23c42c349f3f44710bcbc05fde7db661 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 4 Jul 2013 17:44:23 +0200 Subject: Make emulator arguments available via the system_info BIF erlang:system_info(emu_args) --- erts/emulator/beam/erl_bif_info.c | 3 ++ erts/emulator/beam/erl_init.c | 2 + erts/emulator/beam/erl_utils.h | 4 ++ erts/emulator/beam/utils.c | 91 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 74a37f374d..447c1fdf18 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2646,6 +2646,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) #endif BIF_RET(am_true); } + else if (ERTS_IS_ATOM_STR("emu_args", BIF_ARG_1)) { + BIF_RET(erts_get_emu_args(BIF_P)); + } else if (ERTS_IS_ATOM_STR("dynamic_trace", BIF_ARG_1)) { #if defined(USE_DTRACE) DECL_AM(dtrace); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 4bae3dfeb4..8d137df7ae 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -637,6 +637,8 @@ early_init(int *argc, char **argv) /* char envbuf[21]; /* enough for any 64-bit integer */ size_t envbufsz; + erts_save_emu_args(*argc, argv); + erts_sched_compact_load = 1; erts_printf_eterm_func = erts_printf_term; erts_disable_tolerant_timeofday = 0; diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index a2064bd8a3..79c5105d92 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -24,6 +24,8 @@ #include "erl_smp.h" #include "erl_printf.h" +struct process; + typedef struct { #ifdef DEBUG int smp_api; @@ -155,6 +157,8 @@ Uint32 block_hash(byte *, unsigned, Uint32); Uint32 make_hash2(Eterm); Uint32 make_hash(Eterm); +void erts_save_emu_args(int argc, char **argv); +Eterm erts_get_emu_args(struct process *c_p); Eterm erts_bld_atom(Uint **hpp, Uint *szp, char *str); Eterm erts_bld_uint(Uint **hpp, Uint *szp, Uint ui); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 62caa67ce1..eb1c8f6173 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3466,6 +3466,97 @@ erts_free_read_env(void *value) erts_free(ERTS_ALC_T_TMP, value); } + +typedef struct { + size_t sz; + char *ptr; +} ErtsEmuArg; + +typedef struct { + int argc; + ErtsEmuArg *arg; + size_t no_bytes; +} ErtsEmuArgs; + +ErtsEmuArgs saved_emu_args = {0}; + +void +erts_save_emu_args(int argc, char **argv) +{ +#ifdef DEBUG + char *end_ptr; +#endif + char *ptr; + int i; + size_t arg_sz[100]; + size_t size; + + ASSERT(!saved_emu_args.argc); + + size = sizeof(ErtsEmuArg)*argc; + for (i = 0; i < argc; i++) { + size_t sz = sys_strlen(argv[i]); + if (i < sizeof(arg_sz)/sizeof(arg_sz[0])) + arg_sz[i] = sz; + size += sz+1; + } + ptr = (char *) malloc(size); +#ifdef DEBUG + end_ptr = ptr + size; +#endif + saved_emu_args.arg = (ErtsEmuArg *) ptr; + ptr += sizeof(ErtsEmuArg)*argc; + saved_emu_args.argc = argc; + saved_emu_args.no_bytes = 0; + for (i = 0; i < argc; i++) { + size_t sz; + if (i < sizeof(arg_sz)/sizeof(arg_sz[0])) + sz = arg_sz[i]; + else + sz = sys_strlen(argv[i]); + saved_emu_args.arg[i].ptr = ptr; + saved_emu_args.arg[i].sz = sz; + saved_emu_args.no_bytes += sz; + ptr += sz+1; + sys_strcpy(saved_emu_args.arg[i].ptr, argv[i]); + } + ASSERT(ptr == end_ptr); +} + +Eterm +erts_get_emu_args(Process *c_p) +{ +#ifdef DEBUG + Eterm *end_hp; +#endif + int i; + Uint hsz; + Eterm *hp, res; + + hsz = saved_emu_args.no_bytes*2; + hsz += saved_emu_args.argc*2; + + hp = HAlloc(c_p, hsz); +#ifdef DEBUG + end_hp = hp + hsz; +#endif + res = NIL; + + for (i = saved_emu_args.argc-1; i >= 0; i--) { + Eterm arg = buf_to_intlist(&hp, + saved_emu_args.arg[i].ptr, + saved_emu_args.arg[i].sz, + NIL); + res = CONS(hp, arg, res); + hp += 2; + } + + ASSERT(hp == end_hp); + + return res; +} + + /* * To be used to silence unused result warnings, but do not abuse it. */ -- cgit v1.2.3 From dbd97f2569af24ef395fa3d60bb2b00543296ce8 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 5 Jul 2013 16:40:51 +0200 Subject: Make ethread library information available via system_info BIF erlang:system_info(ethread_info) --- erts/emulator/beam/erl_bif_info.c | 70 +------------- erts/emulator/beam/erl_utils.h | 1 + erts/emulator/beam/utils.c | 196 +++++++++++++++++++++++++++++++++++++- 3 files changed, 198 insertions(+), 69 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 447c1fdf18..b1dc62b4fd 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2577,74 +2577,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) hp = hsz ? HAlloc(BIF_P, hsz) : NULL; res = erts_bld_uint(&hp, NULL, erts_dist_buf_busy_limit); BIF_RET(res); - } else if (ERTS_IS_ATOM_STR("print_ethread_info", BIF_ARG_1)) { -#if defined(ETHR_NATIVE_ATOMIC32_IMPL) \ - || defined(ETHR_NATIVE_ATOMIC64_IMPL) \ - || defined(ETHR_NATIVE_DW_ATOMIC_IMPL) - int i; - char **str; -#endif -#ifdef ETHR_NATIVE_ATOMIC32_IMPL - erts_printf("32-bit native atomics: %s\n", - ETHR_NATIVE_ATOMIC32_IMPL); - str = ethr_native_atomic32_ops(); - for (i = 0; str[i]; i++) - erts_printf("ethr_native_atomic32_%s()\n", str[i]); -#endif -#ifdef ETHR_NATIVE_ATOMIC64_IMPL - erts_printf("64-bit native atomics: %s\n", - ETHR_NATIVE_ATOMIC64_IMPL); - str = ethr_native_atomic64_ops(); - for (i = 0; str[i]; i++) - erts_printf("ethr_native_atomic64_%s()\n", str[i]); -#endif -#ifdef ETHR_NATIVE_DW_ATOMIC_IMPL - if (ethr_have_native_dw_atomic()) { - erts_printf("Double word native atomics: %s\n", - ETHR_NATIVE_DW_ATOMIC_IMPL); - str = ethr_native_dw_atomic_ops(); - for (i = 0; str[i]; i++) - erts_printf("ethr_native_dw_atomic_%s()\n", str[i]); - str = ethr_native_su_dw_atomic_ops(); - for (i = 0; str[i]; i++) - erts_printf("ethr_native_su_dw_atomic_%s()\n", str[i]); - } -#endif -#ifdef ETHR_NATIVE_SPINLOCK_IMPL - erts_printf("Native spin-locks: %s\n", ETHR_NATIVE_SPINLOCK_IMPL); -#endif -#ifdef ETHR_NATIVE_RWSPINLOCK_IMPL - erts_printf("Native rwspin-locks: %s\n", ETHR_NATIVE_RWSPINLOCK_IMPL); -#endif -#ifdef ETHR_X86_RUNTIME_CONF_HAVE_SSE2__ - erts_printf("SSE2 support: %s\n", (ETHR_X86_RUNTIME_CONF_HAVE_SSE2__ - ? "yes" : "no")); -#endif -#ifdef ETHR_X86_OUT_OF_ORDER - erts_printf("x86" -#ifdef ARCH_64 - "_64" -#endif - " out of order\n"); -#endif -#ifdef ETHR_SPARC_TSO - erts_printf("Sparc TSO\n"); -#endif -#ifdef ETHR_SPARC_PSO - erts_printf("Sparc PSO\n"); -#endif -#ifdef ETHR_SPARC_RMO - erts_printf("Sparc RMO\n"); -#endif -#if defined(ETHR_PPC_HAVE_LWSYNC) - erts_printf("Have lwsync instruction: yes\n"); -#elif defined(ETHR_PPC_HAVE_NO_LWSYNC) - erts_printf("Have lwsync instruction: no\n"); -#elif defined(ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__) - erts_printf("Have lwsync instruction: %s (runtime test)\n", - ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__ ? "yes" : "no"); -#endif - BIF_RET(am_true); + } else if (ERTS_IS_ATOM_STR("ethread_info", BIF_ARG_1)) { + BIF_RET(erts_get_ethread_info(BIF_P)); } else if (ERTS_IS_ATOM_STR("emu_args", BIF_ARG_1)) { BIF_RET(erts_get_emu_args(BIF_P)); diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 79c5105d92..80d29d554a 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -159,6 +159,7 @@ Uint32 make_hash(Eterm); void erts_save_emu_args(int argc, char **argv); Eterm erts_get_emu_args(struct process *c_p); +Eterm erts_get_ethread_info(struct process * c_p); Eterm erts_bld_atom(Uint **hpp, Uint *szp, char *str); Eterm erts_bld_uint(Uint **hpp, Uint *szp, Uint ui); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index eb1c8f6173..a285c1d62b 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3543,7 +3543,7 @@ erts_get_emu_args(Process *c_p) res = NIL; for (i = saved_emu_args.argc-1; i >= 0; i--) { - Eterm arg = buf_to_intlist(&hp, + Eterm arg = buf_to_intlist(&hp, saved_emu_args.arg[i].ptr, saved_emu_args.arg[i].sz, NIL); @@ -3557,6 +3557,200 @@ erts_get_emu_args(Process *c_p) } +Eterm +erts_get_ethread_info(Process *c_p) +{ + Uint sz, *szp; + Eterm res, *hp, **hpp, *end_hp = NULL; + + sz = 0; + szp = &sz; + hpp = NULL; + + while (1) { + Eterm tup, list, name; +#if defined(ETHR_NATIVE_ATOMIC32_IMPL) \ + || defined(ETHR_NATIVE_ATOMIC64_IMPL) \ + || defined(ETHR_NATIVE_DW_ATOMIC_IMPL) + char buf[1024]; + int i; + char **str; +#endif + + res = NIL; + +#ifdef ETHR_X86_MEMBAR_H__ + + tup = erts_bld_tuple(hpp, szp, 2, + erts_bld_string(hpp, szp, "sse2"), +#ifdef ETHR_X86_RUNTIME_CONF_HAVE_SSE2__ + erts_bld_string(hpp, szp, + (ETHR_X86_RUNTIME_CONF_HAVE_SSE2__ + ? "yes" : "no")) +#else + erts_bld_string(hpp, szp, "yes") +#endif + ); + res = erts_bld_cons(hpp, szp, tup, res); + + tup = erts_bld_tuple(hpp, szp, 2, + erts_bld_string(hpp, szp, + "x86" +#ifdef ARCH_64 + "_64" +#endif + " OOO"), + erts_bld_string(hpp, szp, +#ifdef ETHR_X86_OUT_OF_ORDER + "yes" +#else + "no" +#endif + )); + + res = erts_bld_cons(hpp, szp, tup, res); +#endif + +#ifdef ETHR_SPARC_V9_MEMBAR_H__ + + tup = erts_bld_tuple(hpp, szp, 2, + erts_bld_string(hpp, szp, "Sparc V9"), + erts_bld_string(hpp, szp, +#if defined(ETHR_SPARC_TSO) + "TSO" +#elif defined(ETHR_SPARC_PSO) + "PSO" +#elif defined(ETHR_SPARC_RMO) + "RMO" +#else + "undefined" +#endif + )); + + res = erts_bld_cons(hpp, szp, tup, res); + +#endif + +#ifdef ETHR_PPC_MEMBAR_H__ + + tup = erts_bld_tuple(hpp, szp, 2, + erts_bld_string(hpp, szp, "lwsync"), + erts_bld_string(hpp, szp, +#if defined(ETHR_PPC_HAVE_LWSYNC) + "yes" +#elif defined(ETHR_PPC_HAVE_NO_LWSYNC) + "no" +#elif defined(ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__) + ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__ ? "yes" : "no" +#else + "undefined" +#endif + )); + + res = erts_bld_cons(hpp, szp, tup, res); + +#endif + + tup = erts_bld_tuple(hpp, szp, 2, + erts_bld_string(hpp, szp, "Native rw-spinlocks"), +#ifdef ETHR_NATIVE_RWSPINLOCK_IMPL + erts_bld_string(hpp, szp, ETHR_NATIVE_RWSPINLOCK_IMPL) +#else + erts_bld_string(hpp, szp, "no") +#endif + ); + res = erts_bld_cons(hpp, szp, tup, res); + + tup = erts_bld_tuple(hpp, szp, 2, + erts_bld_string(hpp, szp, "Native spinlocks"), +#ifdef ETHR_NATIVE_SPINLOCK_IMPL + erts_bld_string(hpp, szp, ETHR_NATIVE_SPINLOCK_IMPL) +#else + erts_bld_string(hpp, szp, "no") +#endif + ); + res = erts_bld_cons(hpp, szp, tup, res); + + + list = NIL; +#ifdef ETHR_NATIVE_DW_ATOMIC_IMPL + if (ethr_have_native_dw_atomic()) { + name = erts_bld_string(hpp, szp, ETHR_NATIVE_DW_ATOMIC_IMPL); + str = ethr_native_dw_atomic_ops(); + for (i = 0; str[i]; i++) { + erts_snprintf(buf, sizeof(buf), "ethr_native_dw_atomic_%s()", str[i]); + list = erts_bld_cons(hpp, szp, + erts_bld_string(hpp, szp, buf), + list); + } + str = ethr_native_su_dw_atomic_ops(); + for (i = 0; str[i]; i++) { + erts_snprintf(buf, sizeof(buf), "ethr_native_su_dw_atomic_%s()", str[i]); + list = erts_bld_cons(hpp, szp, + erts_bld_string(hpp, szp, buf), + list); + } + } + else +#endif + name = erts_bld_string(hpp, szp, "no"); + + tup = erts_bld_tuple(hpp, szp, 3, + erts_bld_string(hpp, szp, "Double word native atomics"), + name, + list); + res = erts_bld_cons(hpp, szp, tup, res); + + list = NIL; +#ifdef ETHR_NATIVE_ATOMIC64_IMPL + name = erts_bld_string(hpp, szp, ETHR_NATIVE_ATOMIC64_IMPL); + str = ethr_native_atomic64_ops(); + for (i = 0; str[i]; i++) { + erts_snprintf(buf, sizeof(buf), "ethr_native_atomic64_%s()", str[i]); + list = erts_bld_cons(hpp, szp, + erts_bld_string(hpp, szp, buf), + list); + } +#else + name = erts_bld_string(hpp, szp, "no"); +#endif + tup = erts_bld_tuple(hpp, szp, 3, + erts_bld_string(hpp, szp, "64-bit native atomics"), + name, + list); + res = erts_bld_cons(hpp, szp, tup, res); + + list = NIL; +#ifdef ETHR_NATIVE_ATOMIC32_IMPL + name = erts_bld_string(hpp, szp, ETHR_NATIVE_ATOMIC32_IMPL); + str = ethr_native_atomic32_ops(); + for (i = 0; str[i]; i++) { + erts_snprintf(buf, sizeof(buf), "ethr_native_atomic32_%s()", str[i]); + list = erts_bld_cons(hpp, szp, + erts_bld_string(hpp, szp, buf), + list); + } +#else + name = erts_bld_string(hpp, szp, "no"); +#endif + tup = erts_bld_tuple(hpp, szp, 3, + erts_bld_string(hpp, szp, "32-bit native atomics"), + name, + list); + res = erts_bld_cons(hpp, szp, tup, res); + + if (hpp) { + HRelease(c_p, end_hp, *hpp) + return res; + } + + hp = HAlloc(c_p, sz); + end_hp = hp + sz; + hpp = &hp; + szp = NULL; + } +} + /* * To be used to silence unused result warnings, but do not abuse it. */ -- cgit v1.2.3 From b3b0c44ca3f1b87c719f782f09dd0331dbb1a351 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 8 Jul 2013 18:04:14 +0200 Subject: Make information about use of jump table available via system_info BIF erlang:system_info(beam_jump_table) --- erts/emulator/beam/beam_emu.c | 9 +++++++++ erts/emulator/beam/erl_bif_info.c | 3 +++ erts/emulator/beam/global.h | 2 ++ 3 files changed, 14 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 5781009f58..da36c4437e 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6256,3 +6256,12 @@ erts_current_reductions(Process *current, Process *p) } } +int +erts_beam_jump_table(void) +{ +#if defined(NO_JUMP_TABLE) + return 0; +#else + return 1; +#endif +} diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b1dc62b4fd..e4fbd21c0e 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2583,6 +2583,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) else if (ERTS_IS_ATOM_STR("emu_args", BIF_ARG_1)) { BIF_RET(erts_get_emu_args(BIF_P)); } + else if (ERTS_IS_ATOM_STR("beam_jump_table", BIF_ARG_1)) { + BIF_RET(erts_beam_jump_table() ? am_true : am_false); + } else if (ERTS_IS_ATOM_STR("dynamic_trace", BIF_ARG_1)) { #if defined(USE_DTRACE) DECL_AM(dtrace); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 25aedc91c6..e5807b5ce6 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1042,6 +1042,8 @@ extern erts_driver_t vanilla_driver; extern erts_driver_t spawn_driver; extern erts_driver_t fd_driver; +int erts_beam_jump_table(void); + /* Should maybe be placed in erl_message.h, but then we get an include mess. */ ERTS_GLB_INLINE Eterm * erts_alloc_message_heap_state(Uint size, -- cgit v1.2.3 From 876d39cd86446ba0fe79ad20f1d261e5d85d659b Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 8 Jul 2013 17:20:21 +0200 Subject: Refuse to build SMP runtime by default without native atomics Build with fallback can be enabled by passing the `configure` command line argument `--disable-smp-require-native-atomics` --- erts/aclocal.m4 | 309 ++++++++++++++++++++++++++++-------------------------- erts/configure.in | 23 +++- 2 files changed, 177 insertions(+), 155 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 25f40944e7..aeb31fa58a 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -954,6 +954,40 @@ dnl AC_DEFUN(ERL_FIND_ETHR_LIB, [ +AC_ARG_ENABLE(native-ethr-impls, + AS_HELP_STRING([--disable-native-ethr-impls], + [disable native ethread implementations]), +[ case "$enableval" in + no) disable_native_ethr_impls=yes ;; + *) disable_native_ethr_impls=no ;; + esac ], disable_native_ethr_impls=no) + +test "X$disable_native_ethr_impls" = "Xyes" && + AC_DEFINE(ETHR_DISABLE_NATIVE_IMPLS, 1, [Define if you want to disable native ethread implementations]) + +AC_ARG_ENABLE(x86-out-of-order, + AS_HELP_STRING([--enable-x86-out-of-order], + [enable x86/x84_64 out of order support (default disabled)])) + +AC_ARG_ENABLE(prefer-gcc-native-ethr-impls, + AS_HELP_STRING([--enable-prefer-gcc-native-ethr-impls], + [prefer gcc native ethread implementations]), +[ case "$enableval" in + yes) enable_prefer_gcc_native_ethr_impls=yes ;; + *) enable_prefer_gcc_native_ethr_impls=no ;; + esac ], enable_prefer_gcc_native_ethr_impls=no) + +test $enable_prefer_gcc_native_ethr_impls = yes && + AC_DEFINE(ETHR_PREFER_GCC_NATIVE_IMPLS, 1, [Define if you prefer gcc native ethread implementations]) + +AC_ARG_WITH(libatomic_ops, + AS_HELP_STRING([--with-libatomic_ops=PATH], + [specify and prefer usage of libatomic_ops in the ethread library])) + +AC_ARG_WITH(with_sparc_memory_order, + AS_HELP_STRING([--with-sparc-memory-order=TSO|PSO|RMO], + [specify sparc memory order (defaults to RMO)])) + LM_CHECK_THR_LIB ERL_INTERNAL_LIBS @@ -1003,40 +1037,44 @@ case "$THR_LIB_NAME" in AC_DEFINE(ETHR_WIN32_THREADS, 1, [Define if you have win32 threads]) - ETHR_CHK_INTERLOCKED([_InterlockedDecrement], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT, 1, [Define if you have _InterlockedDecrement()])) - ETHR_CHK_INTERLOCKED([_InterlockedDecrement_rel], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT_REL, 1, [Define if you have _InterlockedDecrement_rel()])) - ETHR_CHK_INTERLOCKED([_InterlockedIncrement], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT, 1, [Define if you have _InterlockedIncrement()])) - ETHR_CHK_INTERLOCKED([_InterlockedIncrement_acq], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT_ACQ, 1, [Define if you have _InterlockedIncrement_acq()])) - ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD, 1, [Define if you have _InterlockedExchangeAdd()])) - ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd_acq], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD_ACQ, 1, [Define if you have _InterlockedExchangeAdd_acq()])) - ETHR_CHK_INTERLOCKED([_InterlockedAnd], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDAND, 1, [Define if you have _InterlockedAnd()])) - ETHR_CHK_INTERLOCKED([_InterlockedOr], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDOR, 1, [Define if you have _InterlockedOr()])) - ETHR_CHK_INTERLOCKED([_InterlockedExchange], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGE, 1, [Define if you have _InterlockedExchange()])) - ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange], [3], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE, 1, [Define if you have _InterlockedCompareExchange()])) - test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes - ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange_acq], [3], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE_ACQ, 1, [Define if you have _InterlockedCompareExchange_acq()])) - test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes - ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange_rel], [3], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE_REL, 1, [Define if you have _InterlockedCompareExchange_rel()])) - test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes - - ETHR_CHK_INTERLOCKED([_InterlockedDecrement64], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT64, 1, [Define if you have _InterlockedDecrement64()])) - ETHR_CHK_INTERLOCKED([_InterlockedDecrement64_rel], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT64_REL, 1, [Define if you have _InterlockedDecrement64_rel()])) - ETHR_CHK_INTERLOCKED([_InterlockedIncrement64], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT64, 1, [Define if you have _InterlockedIncrement64()])) - ETHR_CHK_INTERLOCKED([_InterlockedIncrement64_acq], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT64_ACQ, 1, [Define if you have _InterlockedIncrement64_acq()])) - ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD64, 1, [Define if you have _InterlockedExchangeAdd64()])) - ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd64_acq], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD64_ACQ, 1, [Define if you have _InterlockedExchangeAdd64_acq()])) - ETHR_CHK_INTERLOCKED([_InterlockedAnd64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDAND64, 1, [Define if you have _InterlockedAnd64()])) - ETHR_CHK_INTERLOCKED([_InterlockedOr64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDOR64, 1, [Define if you have _InterlockedOr64()])) - ETHR_CHK_INTERLOCKED([_InterlockedExchange64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGE64, 1, [Define if you have _InterlockedExchange64()])) - ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange64], [3], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64, 1, [Define if you have _InterlockedCompareExchange64()])) - test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes - ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange64_acq], [3], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64_ACQ, 1, [Define if you have _InterlockedCompareExchange64_acq()])) - test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes - ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange64_rel], [3], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64_REL, 1, [Define if you have _InterlockedCompareExchange64_rel()])) - test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes - - ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange128], [4], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE128, 1, [Define if you have _InterlockedCompareExchange128()])) - + if test "X$disable_native_ethr_impls" = "Xyes"; then + have_interlocked_op=no + ethr_have_native_atomics=no + else + ETHR_CHK_INTERLOCKED([_InterlockedDecrement], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT, 1, [Define if you have _InterlockedDecrement()])) + ETHR_CHK_INTERLOCKED([_InterlockedDecrement_rel], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT_REL, 1, [Define if you have _InterlockedDecrement_rel()])) + ETHR_CHK_INTERLOCKED([_InterlockedIncrement], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT, 1, [Define if you have _InterlockedIncrement()])) + ETHR_CHK_INTERLOCKED([_InterlockedIncrement_acq], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT_ACQ, 1, [Define if you have _InterlockedIncrement_acq()])) + ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD, 1, [Define if you have _InterlockedExchangeAdd()])) + ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd_acq], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD_ACQ, 1, [Define if you have _InterlockedExchangeAdd_acq()])) + ETHR_CHK_INTERLOCKED([_InterlockedAnd], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDAND, 1, [Define if you have _InterlockedAnd()])) + ETHR_CHK_INTERLOCKED([_InterlockedOr], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDOR, 1, [Define if you have _InterlockedOr()])) + ETHR_CHK_INTERLOCKED([_InterlockedExchange], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGE, 1, [Define if you have _InterlockedExchange()])) + ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange], [3], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE, 1, [Define if you have _InterlockedCompareExchange()])) + test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes + ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange_acq], [3], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE_ACQ, 1, [Define if you have _InterlockedCompareExchange_acq()])) + test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes + ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange_rel], [3], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE_REL, 1, [Define if you have _InterlockedCompareExchange_rel()])) + test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes + + ETHR_CHK_INTERLOCKED([_InterlockedDecrement64], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT64, 1, [Define if you have _InterlockedDecrement64()])) + ETHR_CHK_INTERLOCKED([_InterlockedDecrement64_rel], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT64_REL, 1, [Define if you have _InterlockedDecrement64_rel()])) + ETHR_CHK_INTERLOCKED([_InterlockedIncrement64], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT64, 1, [Define if you have _InterlockedIncrement64()])) + ETHR_CHK_INTERLOCKED([_InterlockedIncrement64_acq], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT64_ACQ, 1, [Define if you have _InterlockedIncrement64_acq()])) + ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD64, 1, [Define if you have _InterlockedExchangeAdd64()])) + ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd64_acq], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD64_ACQ, 1, [Define if you have _InterlockedExchangeAdd64_acq()])) + ETHR_CHK_INTERLOCKED([_InterlockedAnd64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDAND64, 1, [Define if you have _InterlockedAnd64()])) + ETHR_CHK_INTERLOCKED([_InterlockedOr64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDOR64, 1, [Define if you have _InterlockedOr64()])) + ETHR_CHK_INTERLOCKED([_InterlockedExchange64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGE64, 1, [Define if you have _InterlockedExchange64()])) + ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange64], [3], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64, 1, [Define if you have _InterlockedCompareExchange64()])) + test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes + ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange64_acq], [3], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64_ACQ, 1, [Define if you have _InterlockedCompareExchange64_acq()])) + test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes + ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange64_rel], [3], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64_REL, 1, [Define if you have _InterlockedCompareExchange64_rel()])) + test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes + + ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange128], [4], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE128, 1, [Define if you have _InterlockedCompareExchange128()])) + fi test "$ethr_have_native_atomics" = "yes" && ethr_have_native_spinlock=yes ;; @@ -1303,93 +1341,98 @@ case "$THR_LIB_NAME" in int128="__int128_t" fi - ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP32, 1, [Define if you have __sync_val_compare_and_swap() for 32-bit integers])) - test "$have_sync_op" = "yes" && ethr_have_native_atomics=yes - ETHR_CHK_SYNC_OP([__sync_add_and_fetch], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_ADD_AND_FETCH32, 1, [Define if you have __sync_add_and_fetch() for 32-bit integers])) - ETHR_CHK_SYNC_OP([__sync_fetch_and_and], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_AND32, 1, [Define if you have __sync_fetch_and_and() for 32-bit integers])) - ETHR_CHK_SYNC_OP([__sync_fetch_and_or], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_OR32, 1, [Define if you have __sync_fetch_and_or() for 32-bit integers])) + if test "X$disable_native_ethr_impls" = "Xyes"; then + ethr_have_native_atomics=no + else + ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP32, 1, [Define if you have __sync_val_compare_and_swap() for 32-bit integers])) + test "$have_sync_op" = "yes" && ethr_have_native_atomics=yes + ETHR_CHK_SYNC_OP([__sync_add_and_fetch], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_ADD_AND_FETCH32, 1, [Define if you have __sync_add_and_fetch() for 32-bit integers])) + ETHR_CHK_SYNC_OP([__sync_fetch_and_and], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_AND32, 1, [Define if you have __sync_fetch_and_and() for 32-bit integers])) + ETHR_CHK_SYNC_OP([__sync_fetch_and_or], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_OR32, 1, [Define if you have __sync_fetch_and_or() for 32-bit integers])) + + ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP64, 1, [Define if you have __sync_val_compare_and_swap() for 64-bit integers])) + test "$have_sync_op" = "yes" && ethr_have_native_atomics=yes + ETHR_CHK_SYNC_OP([__sync_add_and_fetch], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_ADD_AND_FETCH64, 1, [Define if you have __sync_add_and_fetch() for 64-bit integers])) + ETHR_CHK_SYNC_OP([__sync_fetch_and_and], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_AND64, 1, [Define if you have __sync_fetch_and_and() for 64-bit integers])) + ETHR_CHK_SYNC_OP([__sync_fetch_and_or], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_OR64, 1, [Define if you have __sync_fetch_and_or() for 64-bit integers])) + + if test $int128 != no; then + ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [128], [$int128], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP128, 1, [Define if you have __sync_val_compare_and_swap() for 128-bit integers])) + fi - ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP64, 1, [Define if you have __sync_val_compare_and_swap() for 64-bit integers])) - test "$have_sync_op" = "yes" && ethr_have_native_atomics=yes - ETHR_CHK_SYNC_OP([__sync_add_and_fetch], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_ADD_AND_FETCH64, 1, [Define if you have __sync_add_and_fetch() for 64-bit integers])) - ETHR_CHK_SYNC_OP([__sync_fetch_and_and], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_AND64, 1, [Define if you have __sync_fetch_and_and() for 64-bit integers])) - ETHR_CHK_SYNC_OP([__sync_fetch_and_or], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_OR64, 1, [Define if you have __sync_fetch_and_or() for 64-bit integers])) + AC_MSG_CHECKING([for a usable libatomic_ops implementation]) + case "x$with_libatomic_ops" in + xno | xyes | x) + libatomic_ops_include= + ;; + *) + if test -d "${with_libatomic_ops}/include"; then + libatomic_ops_include="-I$with_libatomic_ops/include" + CPPFLAGS="$CPPFLAGS $libatomic_ops_include" + else + AC_MSG_ERROR([libatomic_ops include directory $with_libatomic_ops/include not found]) + fi;; + esac + ethr_have_libatomic_ops=no + AC_TRY_LINK([#include "atomic_ops.h"], + [ + volatile AO_t x; + AO_t y; + int z; + + AO_nop_full(); + AO_store(&x, (AO_t) 0); + z = AO_load(&x); + z = AO_compare_and_swap_full(&x, (AO_t) 0, (AO_t) 1); + ], + [ethr_have_native_atomics=yes + ethr_have_libatomic_ops=yes]) + AC_MSG_RESULT([$ethr_have_libatomic_ops]) + if test $ethr_have_libatomic_ops = yes; then + AC_CHECK_SIZEOF(AO_t, , + [ + #include + #include "atomic_ops.h" + ]) + AC_DEFINE_UNQUOTED(ETHR_SIZEOF_AO_T, $ac_cv_sizeof_AO_t, [Define to the size of AO_t if libatomic_ops is used]) + + AC_DEFINE(ETHR_HAVE_LIBATOMIC_OPS, 1, [Define if you have libatomic_ops atomic operations]) + if test "x$with_libatomic_ops" != "xno" && test "x$with_libatomic_ops" != "x"; then + AC_DEFINE(ETHR_PREFER_LIBATOMIC_OPS_NATIVE_IMPLS, 1, [Define if you prefer libatomic_ops native ethread implementations]) + fi + ETHR_DEFS="$ETHR_DEFS $libatomic_ops_include" + elif test "x$with_libatomic_ops" != "xno" && test "x$with_libatomic_ops" != "x"; then + AC_MSG_ERROR([No usable libatomic_ops implementation found]) + fi - if test $int128 != no; then - ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [128], [$int128], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP128, 1, [Define if you have __sync_val_compare_and_swap() for 128-bit integers])) - fi + case "$host_cpu" in + sparc | sun4u | sparc64 | sun4v) + case "$with_sparc_memory_order" in + "TSO") + AC_DEFINE(ETHR_SPARC_TSO, 1, [Define if only run in Sparc TSO mode]);; + "PSO") + AC_DEFINE(ETHR_SPARC_PSO, 1, [Define if only run in Sparc PSO, or TSO mode]);; + "RMO"|"") + AC_DEFINE(ETHR_SPARC_RMO, 1, [Define if run in Sparc RMO, PSO, or TSO mode]);; + *) + AC_MSG_ERROR([Unsupported Sparc memory order: $with_sparc_memory_order]);; + esac + ethr_have_native_atomics=yes;; + i86pc | i*86 | x86_64 | amd64) + if test "$enable_x86_out_of_order" = "yes"; then + AC_DEFINE(ETHR_X86_OUT_OF_ORDER, 1, [Define if x86/x86_64 out of order instructions should be synchronized]) + fi + ethr_have_native_atomics=yes;; + macppc | ppc | "Power Macintosh") + ethr_have_native_atomics=yes;; + tile) + ethr_have_native_atomics=yes;; + *) + ;; + esac - AC_MSG_CHECKING([for a usable libatomic_ops implementation]) - case "x$with_libatomic_ops" in - xno | xyes | x) - libatomic_ops_include= - ;; - *) - if test -d "${with_libatomic_ops}/include"; then - libatomic_ops_include="-I$with_libatomic_ops/include" - CPPFLAGS="$CPPFLAGS $libatomic_ops_include" - else - AC_MSG_ERROR([libatomic_ops include directory $with_libatomic_ops/include not found]) - fi;; - esac - ethr_have_libatomic_ops=no - AC_TRY_LINK([#include "atomic_ops.h"], - [ - volatile AO_t x; - AO_t y; - int z; - - AO_nop_full(); - AO_store(&x, (AO_t) 0); - z = AO_load(&x); - z = AO_compare_and_swap_full(&x, (AO_t) 0, (AO_t) 1); - ], - [ethr_have_native_atomics=yes - ethr_have_libatomic_ops=yes]) - AC_MSG_RESULT([$ethr_have_libatomic_ops]) - if test $ethr_have_libatomic_ops = yes; then - AC_CHECK_SIZEOF(AO_t, , - [ - #include - #include "atomic_ops.h" - ]) - AC_DEFINE_UNQUOTED(ETHR_SIZEOF_AO_T, $ac_cv_sizeof_AO_t, [Define to the size of AO_t if libatomic_ops is used]) - - AC_DEFINE(ETHR_HAVE_LIBATOMIC_OPS, 1, [Define if you have libatomic_ops atomic operations]) - if test "x$with_libatomic_ops" != "xno" && test "x$with_libatomic_ops" != "x"; then - AC_DEFINE(ETHR_PREFER_LIBATOMIC_OPS_NATIVE_IMPLS, 1, [Define if you prefer libatomic_ops native ethread implementations]) - fi - ETHR_DEFS="$ETHR_DEFS $libatomic_ops_include" - elif test "x$with_libatomic_ops" != "xno" && test "x$with_libatomic_ops" != "x"; then - AC_MSG_ERROR([No usable libatomic_ops implementation found]) fi - case "$host_cpu" in - sparc | sun4u | sparc64 | sun4v) - case "$with_sparc_memory_order" in - "TSO") - AC_DEFINE(ETHR_SPARC_TSO, 1, [Define if only run in Sparc TSO mode]);; - "PSO") - AC_DEFINE(ETHR_SPARC_PSO, 1, [Define if only run in Sparc PSO, or TSO mode]);; - "RMO"|"") - AC_DEFINE(ETHR_SPARC_RMO, 1, [Define if run in Sparc RMO, PSO, or TSO mode]);; - *) - AC_MSG_ERROR([Unsupported Sparc memory order: $with_sparc_memory_order]);; - esac - ethr_have_native_atomics=yes;; - i86pc | i*86 | x86_64 | amd64) - if test "$enable_x86_out_of_order" = "yes"; then - AC_DEFINE(ETHR_X86_OUT_OF_ORDER, 1, [Define if x86/x86_64 out of order instructions should be synchronized]) - fi - ethr_have_native_atomics=yes;; - macppc | ppc | "Power Macintosh") - ethr_have_native_atomics=yes;; - tile) - ethr_have_native_atomics=yes;; - *) - ;; - esac - test ethr_have_native_atomics = "yes" && ethr_have_native_spinlock=yes dnl Restore LIBS @@ -1451,40 +1494,6 @@ esac AC_C_DOUBLE_MIDDLE_ENDIAN -AC_ARG_ENABLE(native-ethr-impls, - AS_HELP_STRING([--disable-native-ethr-impls], - [disable native ethread implementations]), -[ case "$enableval" in - no) disable_native_ethr_impls=yes ;; - *) disable_native_ethr_impls=no ;; - esac ], disable_native_ethr_impls=no) - -AC_ARG_ENABLE(x86-out-of-order, - AS_HELP_STRING([--enable-x86-out-of-order], - [enable x86/x84_64 out of order support (default disabled)])) - -test "X$disable_native_ethr_impls" = "Xyes" && - AC_DEFINE(ETHR_DISABLE_NATIVE_IMPLS, 1, [Define if you want to disable native ethread implementations]) - -AC_ARG_ENABLE(prefer-gcc-native-ethr-impls, - AS_HELP_STRING([--enable-prefer-gcc-native-ethr-impls], - [prefer gcc native ethread implementations]), -[ case "$enableval" in - yes) enable_prefer_gcc_native_ethr_impls=yes ;; - *) enable_prefer_gcc_native_ethr_impls=no ;; - esac ], enable_prefer_gcc_native_ethr_impls=no) - -test $enable_prefer_gcc_native_ethr_impls = yes && - AC_DEFINE(ETHR_PREFER_GCC_NATIVE_IMPLS, 1, [Define if you prefer gcc native ethread implementations]) - -AC_ARG_WITH(libatomic_ops, - AS_HELP_STRING([--with-libatomic_ops=PATH], - [specify and prefer usage of libatomic_ops in the ethread library])) - -AC_ARG_WITH(with_sparc_memory_order, - AS_HELP_STRING([--with-sparc-memory-order=TSO|PSO|RMO], - [specify sparc memory order (defaults to RMO)])) - ETHR_X86_SSE2_ASM=no case "$GCC-$ac_cv_sizeof_void_p-$host_cpu" in yes-4-i86pc | yes-4-i*86 | yes-4-x86_64 | yes-4-amd64) diff --git a/erts/configure.in b/erts/configure.in index b056ba44e2..2f624e5853 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -150,6 +150,14 @@ AS_HELP_STRING([--disable-smp-support], [disable smp support]), *) enable_smp_support=yes ;; esac ], enable_smp_support=unknown) +AC_ARG_ENABLE(smp-require-native-atomics, + AS_HELP_STRING([--disable-smp-require-native-atomics], + [disable the SMP requirement of a native atomic implementation]), +[ case "$enableval" in + no) smp_require_native_atomics=no ;; + *) smp_require_native_atomics=yes ;; + esac ], smp_require_native_atomics=yes) + AC_ARG_WITH(termcap, AS_HELP_STRING([--with-termcap], [use termcap (default)]) AS_HELP_STRING([--without-termcap], @@ -1043,11 +1051,15 @@ if test $ERTS_BUILD_SMP_EMU = yes; then AC_DEFINE(ERTS_HAVE_SMP_EMU, 1, [Define if the smp emulator is built]) - case "$ethr_have_native_atomics-$ethr_have_native_spinlock" in + case "$ethr_have_native_atomics-$smp_require_native_atomics-$ethr_have_native_spinlock" in yes-*) ;; - no-yes) + no-yes-*) + AC_MSG_ERROR([No native atomic implementation found. See INSTALL.md for more information.]) + ;; + + no-no-yes) test -f "$ERL_TOP/erts/CONF_INFO" || echo "" > "$ERL_TOP/erts/CONF_INFO" @@ -1056,12 +1068,13 @@ if test $ERTS_BUILD_SMP_EMU = yes; then No native atomic implementation available. Fallbacks implemented using spinlocks will be used. Note that the performance of the SMP - runtime system will suffer due to this. + runtime system will suffer immensely due to + this. EOF ;; - no-no) + no-no-no) test -f "$ERL_TOP/erts/CONF_INFO" || echo "" > "$ERL_TOP/erts/CONF_INFO" @@ -1071,7 +1084,7 @@ EOF spinlock implementation available. Fallbacks implemented using mutexes will be used. Note that the performance of the SMP runtime system - will suffer much due to this. + will suffer immensely due to this. EOF ;; -- cgit v1.2.3 From b5df977d74dfce0b1bb41ebcedb7f16d02477c5a Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 9 Jul 2013 16:13:11 +0200 Subject: Add test cases for native atomics and jump table --- erts/emulator/test/smoke_test_SUITE.erl | 39 +++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 6f5c2080c0..10b7e16a74 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -26,14 +26,14 @@ init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). --export([boot_combo/1]). +-export([boot_combo/1, native_atomics/1, jump_table/1]). -define(DEFAULT_TIMEOUT, ?t:minutes(2)). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [boot_combo]. + [boot_combo, native_atomics, jump_table]. groups() -> []. @@ -105,6 +105,41 @@ boot_combo(Config) when is_list(Config) -> end) end. +native_atomics(Config) when is_list(Config) -> + NA32Key = "32-bit native atomics", + NA64Key = "64-bit native atomics", + DWNAKey = "Double word native atomics", + EthreadInfo = erlang:system_info(ethread_info), + ?t:format("~p~n", [EthreadInfo]), + {value,{NA32Key, NA32, _}} = lists:keysearch(NA32Key, 1, EthreadInfo), + {value,{NA64Key, NA64, _}} = lists:keysearch(NA64Key, 1, EthreadInfo), + {value,{DWNAKey, DWNA, _}} = lists:keysearch(DWNAKey, 1, EthreadInfo), + case {erlang:system_info(build_type), erlang:system_info(smp_support), NA32, NA64, DWNA} of + {opt, true, "no", "no", _} -> + ?t:fail(optimized_smp_runtime_without_native_atomics); + {_, false, "no", "no", _} -> + {comment, "No native atomics"}; + _ -> + {comment, + NA32 ++ " 32-bit, " + ++ NA64 ++ " 64-bit, and " + ++ DWNA ++ " double word native atomics"} + end. + +jump_table(Config) when is_list(Config) -> + case erlang:system_info(beam_jump_table) of + true -> + ok; + false -> + case erlang:system_info(build_type) of + opt -> + ?t:fail(optimized_without_beam_jump_table); + BT -> + {comment, "No beam jump table, but build type is " ++ atom_to_list(BT)} + end + end. + + %%% %%% Aux functions -------------------------------------------------------------- %%% -- cgit v1.2.3 From db110d392706dd8065a84b55c1354cc6c004f45c Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 10 Jul 2013 17:27:55 +0200 Subject: Conditionally skip process_SUITE tests that consume large amount of memory --- erts/emulator/test/a_SUITE.erl | 3 +++ erts/emulator/test/process_SUITE.erl | 52 +++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl index b541be3df6..195c9c0a5f 100644 --- a/erts/emulator/test/a_SUITE.erl +++ b/erts/emulator/test/a_SUITE.erl @@ -68,6 +68,9 @@ pollset_size(doc) -> pollset_size(suite) -> []; pollset_size(Config) when is_list(Config) -> + %% Ensure inet_gethost_native port program started, in order to + %% allow other suites to use it... + inet_gethost_native:gethostbyname("localhost"), ?line Parent = self(), ?line Go = make_ref(), ?line spawn(fun () -> diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 72f3e8fe85..e3aae17df4 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -90,9 +90,19 @@ groups() -> otp_7738_resume]}]. init_per_suite(Config) -> - Config. + A0 = case application:start(sasl) of + ok -> [sasl]; + _ -> [] + end, + A = case application:start(os_mon) of + ok -> [os_mon|A0]; + _ -> A0 + end, + [{started_apps, A}|Config]. end_per_suite(Config) -> + As = ?config(started_apps, Config), + lists:foreach(fun (A) -> application:stop(A) end, As), catch erts_debug:set_internal_state(available_internal_state, false), Config. @@ -102,7 +112,6 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. - init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> Dog=?t:timetrap(?t:minutes(10)), [{watchdog, Dog},{testcase, Func}|Config]. @@ -1379,6 +1388,9 @@ processes_large_tab(doc) -> processes_large_tab(suite) -> []; processes_large_tab(Config) when is_list(Config) -> + sys_mem_cond_run(2048, fun () -> processes_large_tab_test(Config) end). + +processes_large_tab_test(Config) -> enable_internal_state(), MaxDbgLvl = 20, MinProcTabSize = 2*(1 bsl 15), @@ -1430,6 +1442,9 @@ processes_default_tab(doc) -> processes_default_tab(suite) -> []; processes_default_tab(Config) when is_list(Config) -> + sys_mem_cond_run(1024, fun () -> processes_default_tab_test(Config) end). + +processes_default_tab_test(Config) -> {ok, DefaultNode} = start_node(Config, ""), Res = rpc:call(DefaultNode, ?MODULE, processes_bif_test, []), stop_node(DefaultNode), @@ -1452,7 +1467,7 @@ processes_this_tab(doc) -> processes_this_tab(suite) -> []; processes_this_tab(Config) when is_list(Config) -> - chk_processes_bif_test_res(processes_bif_test()). + sys_mem_cond_run(1024, fun () -> chk_processes_bif_test_res(processes_bif_test()) end). chk_processes_bif_test_res(ok) -> ok; chk_processes_bif_test_res({comment, _} = Comment) -> Comment; @@ -2095,6 +2110,9 @@ otp_7738_resume(Config) when is_list(Config) -> otp_7738_test(resume). otp_7738_test(Type) -> + sys_mem_cond_run(3072, fun () -> do_otp_7738_test(Type) end). + +do_otp_7738_test(Type) -> T = self(), S = spawn_link(fun () -> receive @@ -2239,3 +2257,31 @@ enable_internal_state() -> true -> true; _ -> erts_debug:set_internal_state(available_internal_state, true) end. + +sys_mem_cond_run(ReqSizeMB, TestFun) when is_integer(ReqSizeMB) -> + case total_memory() of + TotMem when is_integer(TotMem), TotMem >= ReqSizeMB -> + TestFun(); + TotMem when is_integer(TotMem) -> + {skipped, "Not enough memory ("++integer_to_list(TotMem)++" MB)"}; + undefined -> + {skipped, "Could not retrieve memory information"} + end. + + +total_memory() -> + %% Totat memory in MB. + try + MemoryData = memsup:get_system_memory_data(), + case lists:keysearch(total_memory, 1, MemoryData) of + {value, {total_memory, TM}} -> + TM div (1024*1024); + false -> + {value, {system_total_memory, STM}} = + lists:keysearch(system_total_memory, 1, MemoryData), + STM div (1024*1024) + end + catch + _ : _ -> + undefined + end. -- cgit v1.2.3 From 6b16d650f99044eb2e1975f2d954aa07ed8aae35 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 11 Jul 2013 16:03:07 +0200 Subject: Fix 'no previous prototype' warning for dtrace functions --- erts/emulator/beam/global.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 25aedc91c6..c0d66d6cc1 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1217,6 +1217,13 @@ erts_alloc_message_heap(Uint size, # define UnUseTmpHeapNoproc(Size) /* Nothing */ #endif /* HEAP_ON_C_STACK */ +ERTS_GLB_INLINE void dtrace_pid_str(Eterm pid, char *process_buf); +ERTS_GLB_INLINE void dtrace_proc_str(Process *process, char *process_buf); +ERTS_GLB_INLINE void dtrace_port_str(Port *port, char *port_buf); +ERTS_GLB_INLINE void dtrace_fun_decode(Process *process, + Eterm module, Eterm function, int arity, + char *process_buf, char *mfa_buf); + #if ERTS_GLB_INLINE_INCL_FUNC_DEF #include "dtrace-wrapper.h" -- cgit v1.2.3 From 70337ed8457402aebf7087ff04718d828d0c19e3 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 11 Jul 2013 16:33:09 +0200 Subject: =?UTF-8?q?Fix=20=E2=80=98ethr=5Fnative=5Frwlock=5Fdestroy?= =?UTF-8?q?=E2=80=99=20defined=20but=20not=20used=20warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- erts/include/internal/ethread.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index ab728c65fa..407097d4b1 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -411,6 +411,7 @@ extern ethr_runtime_t ethr_runtime__; #ifdef VALGRIND /* mutex as fallback for spinlock for VALGRIND */ # undef ETHR_HAVE_NATIVE_SPINLOCKS +# undef ETHR_HAVE_NATIVE_RWSPINLOCKS #else # include "ethr_optimized_fallbacks.h" #endif @@ -693,7 +694,7 @@ static ETHR_INLINE int ETHR_INLINE_FUNC_NAME_(ethr_rwlock_destroy)(ethr_rwlock_t *lock) { #ifdef ETHR_HAVE_NATIVE_RWSPINLOCKS - return 0; + return ethr_native_rwlock_destroy(lock); #else return ethr_rwmutex_destroy((ethr_rwmutex *) lock); #endif -- cgit v1.2.3 From 54c658c89dc8edae1e420861601eec068bc74676 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 11 Jul 2013 16:33:52 +0200 Subject: =?UTF-8?q?Fix=20variable=20=E2=80=98rp=5Fhad=5Flocks=E2=80=99=20s?= =?UTF-8?q?et=20but=20not=20used=20warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- erts/emulator/beam/erl_nif.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index d4c2b5bdcc..48f8be8dd3 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -310,9 +310,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, Process* rp; Process* c_p; ErlHeapFragment* frags; -#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) - ErtsProcLocks rp_had_locks; -#endif Eterm receiver = to_pid->pid; int flush_me = 0; int scheduler = erts_get_scheduler_id() != 0; @@ -332,10 +329,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, #endif } -#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) - rp_had_locks = rp_locks; -#endif - rp = (scheduler ? erts_proc_lookup(receiver) : erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN, -- cgit v1.2.3 From 14c0a18a9970f2287bbb09c46bee0c33358e3fc9 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 4 Jul 2013 15:23:06 +0200 Subject: erts: Add cflags, ldflags and config.h into executable --- erts/emulator/Makefile.in | 10 +++- erts/emulator/beam/atom.names | 3 ++ erts/emulator/beam/erl_bif_info.c | 21 ++++++++ erts/emulator/beam/global.h | 2 +- erts/emulator/beam/utils.c | 2 +- erts/emulator/utils/make_compiler_flags | 87 +++++++++++++++++++++++++++++++++ erts/etc/unix/etp-commands | 27 ++++++++++ 7 files changed, 149 insertions(+), 3 deletions(-) create mode 100755 erts/emulator/utils/make_compiler_flags (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 58e83540e1..9751982103 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -387,6 +387,13 @@ else UNIX_ONLY_BUILDS = endif +ifeq ($(TARGET), win32) +TMPVAR := $(shell LANG=C $(PERL) utils/make_compiler_flags -o $(TTF_DIR)/erl_compile_flags.h -v CONFIG_H "N/A" -v CFLAGS "$(CFLAGS)" -v LDFLAGS "$(LDFLAGS)") +else +# We force this to be run every time this makefile is executed +TMPVAR := $(shell LANG=C $(PERL) utils/make_compiler_flags -o $(TTF_DIR)/erl_compile_flags.h -f CONFIG_H "$(ERL_TOP)/erts/$(TARGET)/config.h" -v CFLAGS "$(CFLAGS)" -v LDFLAGS "$(LDFLAGS)") +endif + .PHONY: all ifdef VOID_EMULATOR all: @@ -959,6 +966,7 @@ SED_REPL_TTF_DIR=s|$(TTF_DIR)/|$$(TTF_DIR)/|g SED_REPL_ERL_TOP=s|\([ ]\)$(ERL_TOP)/|\1$$(ERL_TOP)/|g;s|^$(ERL_TOP)/|$$(ERL_TOP)/|g SED_REPL_POLL=s|$$(OBJDIR)/erl_poll.o|$$(OBJDIR)/erl_poll.kp.o $$(OBJDIR)/erl_poll.nkp.o|g SED_REPL_CHK_IO=s|$$(OBJDIR)/erl_check_io.o|$$(OBJDIR)/erl_check_io.kp.o $$(OBJDIR)/erl_check_io.nkp.o|g +SED_REPL_TTF_COMP_FLAGS=s|\([^/]\)erl_compile_flags\.h|\1$$(TTF_DIR)/erl_compile_flags\.h|g ifeq ($(TARGET),win32) #SED_PREFIX=$(SED_REPL_WIN_DRIVE); @@ -973,7 +981,7 @@ else SED_SUFFIX= endif -SED_DEPEND=sed '$(SED_PREFIX)$(SED_REPL_O);$(SED_REPL_TTF_DIR);$(SED_REPL_ERL_TOP)$(SED_SUFFIX)' +SED_DEPEND=sed '$(SED_PREFIX)$(SED_REPL_O);$(SED_REPL_TTF_DIR);$(SED_REPL_ERL_TOP)$(SED_SUFFIX);$(SED_REPL_TTF_COMP_FLAGS)' SED_DEPEND_ZLIB=sed '$(SED_PREFIX)$(SED_REPL_O_ZLIB)' ifdef HIPE_ENABLED diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index eba1d0fa23..c2f32ba089 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -139,6 +139,7 @@ atom caseless atom catchlevel atom cd atom cdr +atom cflags atom characters_to_binary_int atom characters_to_list_int atom clear @@ -150,6 +151,7 @@ atom compact atom compat_rel atom compile atom compressed +atom config_h atom connect atom connected atom connection_closed @@ -301,6 +303,7 @@ atom label atom large_heap atom last_calls atom latin1 +atom ldflags atom Le='=<' atom lf atom line diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index e4fbd21c0e..a43eee6420 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -30,6 +30,7 @@ #include "bif.h" #include "big.h" #include "erl_version.h" +#include "erl_compile_flags.h" #include "erl_db_util.h" #include "erl_message.h" #include "erl_binary.h" @@ -2610,6 +2611,26 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(am_true); } #endif + else if (ERTS_IS_ATOM_STR("compile_info",BIF_ARG_1)) { + Eterm *hp = HAlloc(BIF_P, 3*3+3*2); /* three two tuples + + three list elems */ + Eterm res = NIL; + Eterm *lhp = HAlloc(BIF_P, 2*(strlen(erts_build_flags_CONFIG_H)+ + strlen(erts_build_flags_CFLAGS)+ + strlen(erts_build_flags_LDFLAGS))); + + res = CONS(hp+9,TUPLE2(hp, am_config_h, + buf_to_intlist(&lhp, erts_build_flags_CONFIG_H, + strlen(erts_build_flags_CONFIG_H), NIL)),res); + res = CONS(hp+11,TUPLE2(hp+3, am_cflags, + buf_to_intlist(&lhp, erts_build_flags_CFLAGS, + strlen(erts_build_flags_CFLAGS), NIL)),res); + res = CONS(hp+13,TUPLE2(hp+6, am_ldflags, + buf_to_intlist(&lhp, erts_build_flags_LDFLAGS, + strlen(erts_build_flags_LDFLAGS), NIL)),res); + + BIF_RET(res); + } BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index e5807b5ce6..56f8f2b3ea 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -937,7 +937,7 @@ char* Sint_to_buf(Sint, struct Sint_buf*); #define ERTS_IOLIST_OVERFLOW 1 #define ERTS_IOLIST_TYPE 2 -Eterm buf_to_intlist(Eterm**, char*, size_t, Eterm); /* most callers pass plain char*'s */ +Eterm buf_to_intlist(Eterm**, const char*, size_t, Eterm); /* most callers pass plain char*'s */ #define ERTS_IOLIST_TO_BUF_OVERFLOW (~((ErlDrvSizeT) 0)) #define ERTS_IOLIST_TO_BUF_TYPE_ERROR (~((ErlDrvSizeT) 1)) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 84deaecb87..bd2be7afca 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2983,7 +2983,7 @@ char* Sint_to_buf(Sint n, struct Sint_buf *buf) */ Eterm -buf_to_intlist(Eterm** hpp, char *buf, size_t len, Eterm tail) +buf_to_intlist(Eterm** hpp, const char *buf, size_t len, Eterm tail) { Eterm* hp = *hpp; size_t i = len; diff --git a/erts/emulator/utils/make_compiler_flags b/erts/emulator/utils/make_compiler_flags new file mode 100755 index 0000000000..cebe8cd0c5 --- /dev/null +++ b/erts/emulator/utils/make_compiler_flags @@ -0,0 +1,87 @@ +#!/usr/bin/env perl +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1999-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% +# +use strict; +use File::Copy; +# This program generates global constants that contains +# config.h, CFLAGS and LDFLAGS + +my $file = ""; +my %constants = (); +my $prev_file = ""; + +while (@ARGV) { + my $d = shift; + if ( $d =~ /^-o$/ ) { + $file = shift or die("-o requires argument"); + open FILE, "<$file" or next; + $prev_file = do { local $/; }; + close FILE; + next; + } + if ( $d =~ /^-f/ ) { + my $var = shift or die("-f requires two argument"); + my $value = shift or die("-f requires two argument"); + open FILE, "<$value"; + $value = do { local $/; }; + close FILE; + + $value =~ s/\n/\\n\\\n/g; + + $constants{$var} = $value; + } + if ( $d =~ /^-v/ ) { + my $var = shift or die("-v requires two argument"); + my $value = shift; + $constants{$var} = $value; + } +} + +foreach(keys %constants) { + my $value = $constants{$_}; + $value =~ s/"/\\"/g; + $constants{$_} = $value +} + +# Did we want output to a file? +open(my $oldout, ">&STDOUT") or die "Can't dup STDOUT: $!"; +if ( $file ) { + open STDOUT, ">$file.tmp" or die("can't open $file for writing"); +} + +my(@prog) = split('/', $0); +my($prog) = $prog[$#prog]; +print "/* Warning: Do not edit this file.\n"; +print " Auto-generated by '$prog'.*/\n"; + +foreach(keys %constants) { + print "const char* erts_build_flags_$_ = \"$constants{$_}\";\n" +} + +open(STDOUT, ">&", $oldout) or die "Can't dup \$oldout: $!"; + +open FILE, "<$file.tmp"; +my $new_file = do { local $/; }; +close FILE; + +if ($new_file ne $prev_file) { + move("$file.tmp","$file"); +} else { + unlink("$file.tmp"); +} diff --git a/erts/etc/unix/etp-commands b/erts/etc/unix/etp-commands index f059662271..35f75df5c1 100644 --- a/erts/etc/unix/etp-commands +++ b/erts/etc/unix/etp-commands @@ -2075,6 +2075,33 @@ document etp-system-info %--------------------------------------------------------------------------- end +define etp-compile-info + printf "--------------- Compile Information ---------------\n" + printf "CFLAGS: %s\n", erts_build_flags_CFLAGS + printf "LDFLAGS: %s\n", erts_build_flags_LDFLAGS + printf "Use etp-config-h-info to dump config.h\n" +end + +document etp-compile-info +%--------------------------------------------------------------------------- +% etp-compile-info +% +% Print information about how the system was compiled +%--------------------------------------------------------------------------- +end + +define etp-config-h-info + printf "%s", erts_build_flags_CONFIG_H +end + +document etp-config-h-info +%--------------------------------------------------------------------------- +% etp-config-h-info +% +% Dump the contents of config.h when the system was compiled +%--------------------------------------------------------------------------- +end + define etp-dictdump # Args: ProcDict* # -- cgit v1.2.3 From 31b20cac498b98d40ed2bb15bb5259cb3c33a422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 5 Jul 2013 14:53:01 +0200 Subject: Add erts app-file --- erts/preloaded/.gitignore | 2 ++ erts/preloaded/Makefile | 1 - erts/preloaded/src/Makefile | 15 ++++++++++++--- erts/preloaded/src/erts.app.src | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 erts/preloaded/.gitignore create mode 100644 erts/preloaded/src/erts.app.src (limited to 'erts') diff --git a/erts/preloaded/.gitignore b/erts/preloaded/.gitignore new file mode 100644 index 0000000000..40e4c68638 --- /dev/null +++ b/erts/preloaded/.gitignore @@ -0,0 +1,2 @@ +ebin/erts.app +src/prim_eval.abstr diff --git a/erts/preloaded/Makefile b/erts/preloaded/Makefile index 4235a7fe57..31fdeb96c5 100644 --- a/erts/preloaded/Makefile +++ b/erts/preloaded/Makefile @@ -18,7 +18,6 @@ # include $(ERL_TOP)/make/target.mk - SUB_DIRECTORIES = src include $(ERL_TOP)/make/otp_subdir.mk diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 7a7b7fb644..c1580b1495 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -55,9 +55,15 @@ ERL_FILES= $(PRE_LOADED_ERL_MODULES:%=%.erl) BEAM_FILES= $(PRE_LOADED_BEAM_MODULES:%=%.S) STUBS_FILES= $(PRE_LOADED_BEAM_MODULES:%=%.erl) -TARGET_FILES = $(PRE_LOADED_MODULES:%=$(EBIN)/%.$(EMULATOR)) +TARGET_FILES = $(PRE_LOADED_MODULES:%=$(EBIN)/%.$(EMULATOR)) \ + $(APP_TARGET) STATIC_TARGET_FILES = $(PRE_LOADED_MODULES:%=$(STATIC_EBIN)/%.$(EMULATOR)) +APP_FILE= erts.app +APP_SRC= $(APP_FILE).src +APP_TARGET= $(STATIC_EBIN)/$(APP_FILE) + + KERNEL_SRC=$(ERL_TOP)/lib/kernel/src KERNEL_INCLUDE=$(ERL_TOP)/lib/kernel/include STDLIB_INCLUDE=$(ERL_TOP)/lib/stdlib/include @@ -72,14 +78,17 @@ clean: copy: cp *.beam $(STATIC_EBIN) +$(APP_TARGET): $(APP_SRC) $(ERL_TOP)/erts/vsn.mk + $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ + include $(ERL_TOP)/make/otp_release_targets.mk -release_spec: +release_spec: $(APP_TARGET) $(INSTALL_DIR) "$(RELSYSDIR)/src" $(INSTALL_DATA) $(ERL_FILES) $(BEAM_FILES) $(STUBS_FILES) "$(RELSYSDIR)/src" $(INSTALL_DIR) "$(RELSYSDIR)/ebin" - $(INSTALL_DATA) $(STATIC_TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(STATIC_TARGET_FILES) $(APP_TARGET) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src new file mode 100644 index 0000000000..fd3e8cb692 --- /dev/null +++ b/erts/preloaded/src/erts.app.src @@ -0,0 +1,41 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The 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, erts, [ + {description, "ERTS CXC 138 10"}, + {vsn, "%VSN%"}, + {modules, [ + %% preloaded + erlang, + erl_prim_loader, + erts_internal, + init, + otp_ring0, + prim_eval, + prim_file, + prim_inet, + prim_zip, + zlib + ]}, + {registered, []}, + {applications, []}, + {env, []}, + {mod, {erts, []}} + ]}. + +%% vim: ft=erlang -- cgit v1.2.3 From 4f54774403276bc02d4a59aac172afeda8a331f4 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 12 Jul 2013 12:36:20 +0200 Subject: Fix configure detection of ethread native atomics on powerpc --- erts/aclocal.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index aeb31fa58a..46b30a16b3 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1423,7 +1423,7 @@ case "$THR_LIB_NAME" in AC_DEFINE(ETHR_X86_OUT_OF_ORDER, 1, [Define if x86/x86_64 out of order instructions should be synchronized]) fi ethr_have_native_atomics=yes;; - macppc | ppc | "Power Macintosh") + macppc | ppc | powerpc | "Power Macintosh") ethr_have_native_atomics=yes;; tile) ethr_have_native_atomics=yes;; -- cgit v1.2.3 From 5e768c8765bc70d70599dd9a6e5d27875de490e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 11 Jul 2013 18:39:04 +0200 Subject: Fix erlang:system_info(compile_info) Allocation needs to be in correct order. --- erts/emulator/beam/erl_bif_info.c | 41 ++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index a43eee6420..673dfc658c 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2612,24 +2612,29 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) } #endif else if (ERTS_IS_ATOM_STR("compile_info",BIF_ARG_1)) { - Eterm *hp = HAlloc(BIF_P, 3*3+3*2); /* three two tuples + - three list elems */ - Eterm res = NIL; - Eterm *lhp = HAlloc(BIF_P, 2*(strlen(erts_build_flags_CONFIG_H)+ - strlen(erts_build_flags_CFLAGS)+ - strlen(erts_build_flags_LDFLAGS))); - - res = CONS(hp+9,TUPLE2(hp, am_config_h, - buf_to_intlist(&lhp, erts_build_flags_CONFIG_H, - strlen(erts_build_flags_CONFIG_H), NIL)),res); - res = CONS(hp+11,TUPLE2(hp+3, am_cflags, - buf_to_intlist(&lhp, erts_build_flags_CFLAGS, - strlen(erts_build_flags_CFLAGS), NIL)),res); - res = CONS(hp+13,TUPLE2(hp+6, am_ldflags, - buf_to_intlist(&lhp, erts_build_flags_LDFLAGS, - strlen(erts_build_flags_LDFLAGS), NIL)),res); - - BIF_RET(res); + Uint sz; + Eterm res = NIL, tup, text; + Eterm *hp = HAlloc(BIF_P, 3*(2 + 3) + /* three 2-tuples and three cons */ + 2*(strlen(erts_build_flags_CONFIG_H) + + strlen(erts_build_flags_CFLAGS) + + strlen(erts_build_flags_LDFLAGS))); + + sz = strlen(erts_build_flags_CONFIG_H); + text = buf_to_intlist(&hp, erts_build_flags_CONFIG_H, sz, NIL); + tup = TUPLE2(hp, am_config_h, text); hp += 3; + res = CONS(hp, tup, res); hp += 2; + + sz = strlen(erts_build_flags_CFLAGS); + text = buf_to_intlist(&hp, erts_build_flags_CFLAGS, sz, NIL); + tup = TUPLE2(hp, am_cflags, text); hp += 3; + res = CONS(hp, tup, res); hp += 2; + + sz = strlen(erts_build_flags_LDFLAGS); + text = buf_to_intlist(&hp, erts_build_flags_LDFLAGS, sz, NIL); + tup = TUPLE2(hp, am_ldflags, text); hp += 3; + res = CONS(hp, tup, res); hp += 2; + + BIF_RET(res); } BIF_ERROR(BIF_P, BADARG); -- cgit v1.2.3 From 4e82946f3096faad3e804b31810e49e7e115b6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 21 Feb 2013 15:40:14 +0100 Subject: tests: Relax receive_SUITE time constraint --- erts/emulator/test/receive_SUITE.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl index b070e2b986..b8cf8a4abc 100644 --- a/erts/emulator/test/receive_SUITE.erl +++ b/erts/emulator/test/receive_SUITE.erl @@ -65,15 +65,18 @@ call_with_huge_message_queue(Config) when is_list(Config) -> ?line [self() ! {msg,N} || N <- lists:seq(1, 500000)], erlang:garbage_collect(), - ?line {NewTime,ok} = tc(fun() -> calls(10, Pid) end), + ?line {NewTime1,ok} = tc(fun() -> calls(10, Pid) end), + ?line {NewTime2,ok} = tc(fun() -> calls(10, Pid) end), + io:format("Time for empty message queue: ~p", [Time]), - io:format("Time for huge message queue: ~p", [NewTime]), + io:format("Time1 for huge message queue: ~p", [NewTime1]), + io:format("Time2 for huge message queue: ~p", [NewTime2]), - case (NewTime+1) / (Time+1) of + case hd(lists:sort([(NewTime1+1) / (Time+1), (NewTime2+1) / (Time+1)])) of Q when Q < 10 -> ok; Q -> - io:format("Q = ~p", [Q]), + io:format("Best Q = ~p", [Q]), ?line ?t:fail() end, ok. -- cgit v1.2.3 From 98d03e9cf84b8670afaaa450d8e9bae8e303437c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 21 Feb 2013 15:41:29 +0100 Subject: tests: Refactor away ?line macro --- erts/emulator/test/receive_SUITE.erl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl index b8cf8a4abc..2e7ac1f50c 100644 --- a/erts/emulator/test/receive_SUITE.erl +++ b/erts/emulator/test/receive_SUITE.erl @@ -59,14 +59,14 @@ end_per_testcase(_Func, Config) -> ?t:timetrap_cancel(Dog). call_with_huge_message_queue(Config) when is_list(Config) -> - ?line Pid = spawn_link(fun echo_loop/0), + Pid = spawn_link(fun echo_loop/0), - ?line {Time,ok} = tc(fun() -> calls(10, Pid) end), + {Time,ok} = tc(fun() -> calls(10, Pid) end), - ?line [self() ! {msg,N} || N <- lists:seq(1, 500000)], + [self() ! {msg,N} || N <- lists:seq(1, 500000)], erlang:garbage_collect(), - ?line {NewTime1,ok} = tc(fun() -> calls(10, Pid) end), - ?line {NewTime2,ok} = tc(fun() -> calls(10, Pid) end), + {NewTime1,ok} = tc(fun() -> calls(10, Pid) end), + {NewTime2,ok} = tc(fun() -> calls(10, Pid) end), io:format("Time for empty message queue: ~p", [Time]), io:format("Time1 for huge message queue: ~p", [NewTime1]), @@ -77,7 +77,7 @@ call_with_huge_message_queue(Config) when is_list(Config) -> ok; Q -> io:format("Best Q = ~p", [Q]), - ?line ?t:fail() + ?t:fail() end, ok. @@ -98,8 +98,8 @@ call(Pid, Msg) -> end. receive_in_between(Config) when is_list(Config) -> - ?line Pid = spawn_link(fun echo_loop/0), - ?line [{ok,{a,b}} = call2(Pid, {a,b}) || _ <- lists:seq(1, 100000)], + Pid = spawn_link(fun echo_loop/0), + [{ok,{a,b}} = call2(Pid, {a,b}) || _ <- lists:seq(1, 100000)], ok. call2(Pid, Msg) -> -- cgit v1.2.3 From 2bd279fc9196aa25446d632e6a37bc7bdb102ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 15 Jul 2013 19:11:40 +0200 Subject: tests: Relax system_profile_SUITE scheduler test --- erts/emulator/test/system_profile_SUITE.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl index ba94a371be..a387c08ef9 100644 --- a/erts/emulator/test/system_profile_SUITE.erl +++ b/erts/emulator/test/system_profile_SUITE.erl @@ -198,7 +198,9 @@ check_multi_scheduling_block(Nodes) -> Pid = start_profiler_process(), undefined = erlang:system_profile(Pid, [scheduler]), {ok, Supervisor} = start_load(Nodes), + wait(600), erlang:system_flag(multi_scheduling, block), + wait(600), erlang:system_flag(multi_scheduling, unblock), {Pid, [scheduler]} = erlang:system_profile(undefined, []), Events = get_profiler_events(), @@ -213,7 +215,6 @@ check_block_system(Nodes) -> Pid = start_profiler_process(), undefined = erlang:system_profile(Pid, [scheduler]), {ok, Supervisor} = start_load(Nodes), - % FIXME: remove wait !! wait(300), undefined = erlang:system_monitor(Dummy, [busy_port]), {Dummy, [busy_port]} = erlang:system_monitor(undefined, []), -- cgit v1.2.3 From 41989072fdfa766916489088aa7eae48a7d89961 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 12 Jul 2013 15:42:33 +0200 Subject: Implement emulator netns support for TCP and UDP --- erts/configure.in | 4 ++ erts/emulator/drivers/common/inet_drv.c | 106 ++++++++++++++++++++++++++++++-- erts/preloaded/ebin/prim_inet.beam | Bin 70520 -> 70960 bytes erts/preloaded/src/prim_inet.erl | 37 +++++++---- 4 files changed, 132 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index b056ba44e2..236c7075f2 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1663,6 +1663,10 @@ if test x"$ac_cv_header_netinet_sctp_h" = x"yes"; then ]) fi +dnl Check for setns +AC_CHECK_HEADERS(sched.h setns.h) +AC_CHECK_FUNCS([setns]) + HAVE_VALGRIND=no AC_CHECK_HEADER(valgrind/valgrind.h, HAVE_VALGRIND=yes) AC_SUBST(HAVE_VALGRIND) diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 301ce2d0e2..9ef2a1cfc0 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -282,7 +282,7 @@ static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD); static unsigned long zero_value = 0; static unsigned long one_value = 1; -#else +#else /* #ifdef __WIN32__ */ #include #ifdef NETDB_H_NEEDS_IN_H @@ -315,9 +315,17 @@ static unsigned long one_value = 1; #include +#ifdef HAVE_SCHED_H +#include +#endif + +#ifdef HAVE_SETNS_H +#include +#endif + /* SCTP support -- currently for UNIX platforms only: */ #undef HAVE_SCTP -#if (!defined(__WIN32__) && defined(HAVE_SCTP_H)) +#if defined(HAVE_SCTP_H) #include @@ -418,7 +426,7 @@ static int (*p_sctp_bindx)(int sd, struct sockaddr *addrs, static int (*p_sctp_peeloff)(int sd, sctp_assoc_t assoc_id) = NULL; #endif -#endif /* SCTP supported */ +#endif /* #if defined(HAVE_SCTP_H) */ #ifndef WANT_NONBLOCKING #define WANT_NONBLOCKING @@ -512,7 +520,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) } while(0) -#endif /* __WIN32__ */ +#endif /* #ifdef __WIN32__ #else */ #ifdef HAVE_SOCKLEN_T # define SOCKLEN_T socklen_t @@ -680,6 +688,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_LOPT_TCP_SEND_TIMEOUT_CLOSE 35 /* auto-close on send timeout or not */ #define INET_LOPT_MSGQ_HIWTRMRK 36 /* set local msgq high watermark */ #define INET_LOPT_MSGQ_LOWTRMRK 37 /* set local msgq low watermark */ +#define INET_LOPT_NETNS 38 /* Network namespace pathname */ /* SCTP options: a separate range, from 100: */ #define SCTP_OPT_RTOINFO 100 #define SCTP_OPT_ASSOCINFO 101 @@ -955,6 +964,10 @@ typedef struct { int is_ignored; /* if a fd is ignored by the inet_drv. This flag should be set to true when the fd is used outside of inet_drv. */ +#ifdef HAVE_SETNS + char *netns; /* Socket network namespace name + as full file path */ +#endif } inet_descriptor; @@ -3908,10 +3921,57 @@ static int erl_inet_close(inet_descriptor* desc) static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, char** rbuf, ErlDrvSizeT rsize) { +#ifdef HAVE_SETNS + int current_ns, new_ns, save_errno; + current_ns = new_ns = save_errno = 0; +#endif if (desc->state != INET_STATE_CLOSED) return ctl_xerror(EXBADSEQ, rbuf, rsize); +#ifdef HAVE_SETNS + if (desc->netns != NULL) { + current_ns = open("/proc/self/ns/net", O_RDONLY); + if (current_ns == INVALID_SOCKET) + return ctl_error(sock_errno(), rbuf, rsize); + new_ns = open(desc->netns, O_RDONLY); + if (new_ns == INVALID_SOCKET) { + save_errno = sock_errno(); + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + return ctl_error(save_errno, rbuf, rsize); + } + if (setns(new_ns, CLONE_NEWNET) != 0) { + save_errno = sock_errno(); + while (close(new_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + return ctl_error(save_errno, rbuf, rsize); + } + else { + while (close(new_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + } + } +#endif if ((desc->s = sock_open(domain, type, desc->sprotocol)) == INVALID_SOCKET) return ctl_error(sock_errno(), rbuf, rsize); +#ifdef HAVE_SETNS + if (desc->netns != NULL) { + if (setns(current_ns, CLONE_NEWNET) != 0) { + save_errno = sock_errno(); + while (close(desc->s) == INVALID_SOCKET && + sock_errno() == EINTR); + desc->s = INVALID_SOCKET; + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + return ctl_error(save_errno, rbuf, rsize); + } + else { + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + } + } +#endif if ((desc->event = sock_create_event(desc)) == INVALID_EVENT) return ctl_error(sock_errno(), rbuf, rsize); SET_NONBLOCKING(desc->s); @@ -5529,6 +5589,20 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) } continue; +#ifdef HAVE_SETNS + case INET_LOPT_NETNS: + /* It is annoying that ival and len are both (signed) int */ + if (ival < 0) return -1; + if (len < ival) return -1; + if (desc->netns != NULL) FREE(desc->netns); + desc->netns = ALLOC(((unsigned int) ival) + 1); + memcpy(desc->netns, ptr, ival); + desc->netns[ival] = '\0'; + ptr += ival; + len -= ival; + continue; +#endif + case INET_OPT_REUSEADDR: #ifdef __WIN32__ continue; /* Bjorn says */ @@ -6454,6 +6528,22 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, } continue; +#ifdef HAVE_SETNS + case INET_LOPT_NETNS: + if (desc->netns != NULL) { + size_t netns_len; + netns_len = strlen(desc->netns); + *ptr++ = opt; + put_int32(netns_len, ptr); + PLACE_FOR(netns_len, ptr); + memcpy(ptr, desc->netns, netns_len); + ptr += netns_len; + } else { + TRUNCATE_TO(0,ptr); + } + continue; +#endif + case INET_OPT_PRIORITY: #ifdef SO_PRIORITY type = SO_PRIORITY; @@ -7458,6 +7548,10 @@ static ErlDrvSSizeT inet_subscribe(inet_descriptor* desc, static void inet_stop(inet_descriptor* desc) { erl_inet_close(desc); +#ifdef HAVE_SETNS + if (desc->netns != NULL) + FREE(desc->netns); +#endif FREE(desc); } @@ -7537,6 +7631,10 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) desc->is_ignored = 0; +#ifdef HAVE_SETNS + desc->netns = NULL; +#endif + return (ErlDrvData)desc; } diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 8638ef677e..5b38871282 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index fb1269cf91..fa621681f3 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -25,7 +25,7 @@ %% Primitive inet_drv interface --export([open/3, fdopen/4, close/1]). +-export([open/3, open/4, fdopen/4, close/1]). -export([bind/3, listen/1, listen/2, peeloff/2]). -export([connect/3, connect/4, async_connect/4]). -export([accept/1, accept/2, async_accept/2]). @@ -64,22 +64,31 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% open(Protocol, Family, Type) -> - open(Protocol, Family, Type, ?INET_REQ_OPEN, []). + open(Protocol, Family, Type, [], ?INET_REQ_OPEN, []). + +open(Protocol, Family, Type, Opts) -> + open(Protocol, Family, Type, Opts, ?INET_REQ_OPEN, []). fdopen(Protocol, Family, Type, Fd) when is_integer(Fd) -> - open(Protocol, Family, Type, ?INET_REQ_FDOPEN, ?int32(Fd)). + open(Protocol, Family, Type, [], ?INET_REQ_FDOPEN, ?int32(Fd)). -open(Protocol, Family, Type, Req, Data) -> +open(Protocol, Family, Type, Opts, Req, Data) -> Drv = protocol2drv(Protocol), AF = enc_family(Family), T = enc_type(Type), try erlang:open_port({spawn_driver,Drv}, [binary]) of S -> - case ctl_cmd(S, Req, [AF,T,Data]) of - {ok,_} -> {ok,S}; - {error,_}=Error -> + case setopts(S, Opts) of + ok -> + case ctl_cmd(S, Req, [AF,T,Data]) of + {ok,_} -> {ok,S}; + {error,_}=E1 -> + close(S), + E1 + end; + {error,_}=E2 -> close(S), - Error + E2 end catch %% The only (?) way to get here is to try to open @@ -1108,6 +1117,7 @@ enc_opt(send_timeout_close) -> ?INET_LOPT_TCP_SEND_TIMEOUT_CLOSE; enc_opt(delay_send) -> ?INET_LOPT_TCP_DELAY_SEND; enc_opt(packet_size) -> ?INET_LOPT_PACKET_SIZE; enc_opt(read_packets) -> ?INET_LOPT_READ_PACKETS; +enc_opt(netns) -> ?INET_LOPT_NETNS; enc_opt(raw) -> ?INET_OPT_RAW; % Names of SCTP opts: enc_opt(sctp_rtoinfo) -> ?SCTP_OPT_RTOINFO; @@ -1164,6 +1174,7 @@ dec_opt(?INET_LOPT_TCP_SEND_TIMEOUT_CLOSE) -> send_timeout_close; dec_opt(?INET_LOPT_TCP_DELAY_SEND) -> delay_send; dec_opt(?INET_LOPT_PACKET_SIZE) -> packet_size; dec_opt(?INET_LOPT_READ_PACKETS) -> read_packets; +dec_opt(?INET_LOPT_NETNS) -> netns; dec_opt(?INET_OPT_RAW) -> raw; dec_opt(I) when is_integer(I) -> undefined. @@ -1261,6 +1272,7 @@ type_opt_1(send_timeout_close) -> bool; type_opt_1(delay_send) -> bool; type_opt_1(packet_size) -> uint; type_opt_1(read_packets) -> uint; +type_opt_1(netns) -> binary; %% %% SCTP options (to be set). If the type is a record type, the corresponding %% record signature is returned, otherwise, an "elementary" type tag @@ -1487,9 +1499,12 @@ type_value_2({bitenumlist,List,_}, EnumList) -> Ls when is_list(Ls) -> true; false -> false end; -type_value_2(binary,Bin) when is_binary(Bin) -> true; -type_value_2(binary_or_uint,Bin) when is_binary(Bin) -> true; -type_value_2(binary_or_uint,Int) when is_integer(Int), Int >= 0 -> true; +type_value_2(binary,Bin) + when is_binary(Bin), byte_size(Bin) < (1 bsl 32) -> true; +type_value_2(binary_or_uint,Bin) + when is_binary(Bin), byte_size(Bin) < (1 bsl 32) -> true; +type_value_2(binary_or_uint,Int) + when is_integer(Int), Int >= 0 -> true; %% Type-checking of SCTP options type_value_2(sctp_assoc_id, X) when X band 16#ffffffff =:= X -> true; -- cgit v1.2.3 From 08ff3673e25fdd184ff92d45d4609cd423fd1e34 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 16 Jul 2013 15:14:50 +0200 Subject: Implement netns for SCTP + bugfixes --- erts/emulator/drivers/common/inet_drv.c | 67 ++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 9ef2a1cfc0..60db50e80a 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1194,6 +1194,7 @@ static ErlDrvTermData am_dontroute; static ErlDrvTermData am_priority; static ErlDrvTermData am_tos; static ErlDrvTermData am_ipv6_v6only; +static ErlDrvTermData am_netns; #endif /* speical errors for bad ports and sequences */ @@ -3511,6 +3512,7 @@ static void inet_init_sctp(void) { INIT_ATOM(priority); INIT_ATOM(tos); INIT_ATOM(ipv6_v6only); + INIT_ATOM(netns); /* Option names */ INIT_ATOM(sctp_rtoinfo); @@ -3921,14 +3923,21 @@ static int erl_inet_close(inet_descriptor* desc) static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, char** rbuf, ErlDrvSizeT rsize) { + int save_errno; #ifdef HAVE_SETNS - int current_ns, new_ns, save_errno; - current_ns = new_ns = save_errno = 0; + int current_ns, new_ns; + current_ns = new_ns = 0; #endif + save_errno = 0; + if (desc->state != INET_STATE_CLOSED) return ctl_xerror(EXBADSEQ, rbuf, rsize); + #ifdef HAVE_SETNS if (desc->netns != NULL) { + /* Temporarily change network namespace for this thread + * while creating the socket + */ current_ns = open("/proc/self/ns/net", O_RDONLY); if (current_ns == INVALID_SOCKET) return ctl_error(sock_errno(), rbuf, rsize); @@ -3954,11 +3963,18 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, } #endif if ((desc->s = sock_open(domain, type, desc->sprotocol)) == INVALID_SOCKET) - return ctl_error(sock_errno(), rbuf, rsize); + save_errno = sock_errno(); #ifdef HAVE_SETNS if (desc->netns != NULL) { + /* Restore network namespace */ if (setns(current_ns, CLONE_NEWNET) != 0) { - save_errno = sock_errno(); + /* XXX Failed to restore network namespace. + * What to do? Tidy up and return an error... + * Note that the thread now might still be in the namespace. + * Can this even happen? Should the emulator be aborted? + */ + if (desc->s != INVALID_SOCKET) + save_errno = sock_errno(); while (close(desc->s) == INVALID_SOCKET && sock_errno() == EINTR); desc->s = INVALID_SOCKET; @@ -3972,8 +3988,16 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, } } #endif - if ((desc->event = sock_create_event(desc)) == INVALID_EVENT) - return ctl_error(sock_errno(), rbuf, rsize); + if (desc->s == INVALID_SOCKET) + return ctl_error(save_errno, rbuf, rsize); + + if ((desc->event = sock_create_event(desc)) == INVALID_EVENT) { + save_errno = sock_errno(); + while (close(desc->s) == INVALID_SOCKET && + sock_errno() == EINTR); + desc->s = INVALID_SOCKET; + return ctl_error(save_errno, rbuf, rsize); + } SET_NONBLOCKING(desc->s); #ifdef __WIN32__ driver_select(desc->port, desc->event, ERL_DRV_READ, 1); @@ -5932,6 +5956,21 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) res = 0; continue; +#ifdef HAVE_SETNS + case INET_LOPT_NETNS: + { + size_t ns_len; + ns_len = get_int32(curr); curr += 4; + CHKLEN(curr, ns_len); + if (desc->netns != NULL) FREE(desc->netns); + desc->netns = ALLOC(ns_len + 1); + memcpy(desc->netns, curr, ns_len); + desc->netns[ns_len] = '\0'; + curr += ns_len; + } + continue; +#endif + /* SCTP options and applicable generic INET options: */ case SCTP_OPT_RTOINFO: @@ -6827,6 +6866,22 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, break; } +#ifdef HAVE_SETNS + case INET_LOPT_NETNS: + if (desc->netns != NULL) { + PLACE_FOR + (spec, i, + LOAD_ATOM_CNT + LOAD_BUF2BINARY_CNT + LOAD_TUPLE_CNT); + i = LOAD_ATOM (spec, i, am_netns); + i = LOAD_BUF2BINARY + (spec, i, desc->netns, strlen(desc->netns)); + i = LOAD_TUPLE (spec, i, 2); + break; + } + else + continue; /* Ignore */ +#endif + /* SCTP and generic INET options: */ case SCTP_OPT_RTOINFO: -- cgit v1.2.3 From 15eba817c5da7fd91dba9b176bffe908b889e3b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Tue, 18 Jun 2013 10:14:34 +0200 Subject: Fix compile error on ARM and GCC < 4.1.0 Since b29ecbd (OTP-10418, R15B03) Erlang does not compile anymore with old versions of GCC that do not have atomic ops builtins on platforms where there is no native ethread implementation (e.g. ARM): In file included from ../include/internal/gcc/ethread.h:29, from ../include/internal/ethread.h:354, from beam/erl_threads.h:264, from beam/erl_smp.h:27, from beam/sys.h:413, from hipe/hipe_mkliterals.c:29: ../include/internal/gcc/ethr_membar.h:49:4: error: #error "No __sync_val_compare_and_swap" This patch adds a header guard in "gcc/ethread.h", as is present in "libatomic_ops/ethread.h". --- erts/include/internal/gcc/ethread.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/include/internal/gcc/ethread.h b/erts/include/internal/gcc/ethread.h index fcfdc39441..365a3535cf 100644 --- a/erts/include/internal/gcc/ethread.h +++ b/erts/include/internal/gcc/ethread.h @@ -25,6 +25,9 @@ #ifndef ETHREAD_GCC_H__ #define ETHREAD_GCC_H__ +#if defined(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP32) \ + || defined(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP64) + #ifndef ETHR_MEMBAR # include "ethr_membar.h" #endif @@ -46,3 +49,5 @@ #endif #endif + +#endif -- cgit v1.2.3 From d0898734b7ae62572579a0ecd0b03ab451b233bb Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Mon, 15 Jul 2013 11:08:18 +0200 Subject: Update to PCRE 8.33, w/o the erts_ prefix added --- erts/emulator/pcre/dftables.c | 212 + erts/emulator/pcre/local_config.h | 4 +- erts/emulator/pcre/make_latin1_table.c | 201 - erts/emulator/pcre/pcre-7.6.tar.bz2 | Bin 802829 -> 0 bytes erts/emulator/pcre/pcre-8.33.tar.bz2 | Bin 0 -> 1440869 bytes erts/emulator/pcre/pcre.h | 552 +- erts/emulator/pcre/pcre.mk | 119 +- erts/emulator/pcre/pcre_byte_order.c | 319 + erts/emulator/pcre/pcre_chartables.c | 5 +- erts/emulator/pcre/pcre_compile.c | 6076 ++++++++++----- erts/emulator/pcre/pcre_config.c | 78 +- erts/emulator/pcre/pcre_dfa_exec.c | 2108 ++++-- erts/emulator/pcre/pcre_exec.c | 6176 +++++++++++----- erts/emulator/pcre/pcre_fullinfo.c | 116 +- erts/emulator/pcre/pcre_get.c | 327 +- erts/emulator/pcre/pcre_globals.c | 39 +- erts/emulator/pcre/pcre_info.c | 94 - erts/emulator/pcre/pcre_internal.h | 2541 +++++-- erts/emulator/pcre/pcre_jit_compile.c | 9752 +++++++++++++++++++++++++ erts/emulator/pcre/pcre_latin_1_table.c | 6 +- erts/emulator/pcre/pcre_make_latin1_default.c | 367 - erts/emulator/pcre/pcre_maketables.c | 22 +- erts/emulator/pcre/pcre_newline.c | 118 +- erts/emulator/pcre/pcre_ord2utf8.c | 33 +- erts/emulator/pcre/pcre_refcount.c | 20 +- erts/emulator/pcre/pcre_string_utils.c | 211 + erts/emulator/pcre/pcre_study.c | 1293 +++- erts/emulator/pcre/pcre_tables.c | 792 +- erts/emulator/pcre/pcre_try_flipped.c | 138 - erts/emulator/pcre/pcre_ucd.c | 3298 +++++++++ erts/emulator/pcre/pcre_ucp_searchfuncs.c | 181 - erts/emulator/pcre/pcre_valid_utf8.c | 243 +- erts/emulator/pcre/pcre_version.c | 16 +- erts/emulator/pcre/pcre_xclass.c | 104 +- erts/emulator/pcre/ucp.h | 82 +- erts/emulator/pcre/ucpinternal.h | 94 - erts/emulator/pcre/ucptable.h | 3088 -------- 37 files changed, 28881 insertions(+), 9944 deletions(-) create mode 100644 erts/emulator/pcre/dftables.c delete mode 100644 erts/emulator/pcre/make_latin1_table.c delete mode 100644 erts/emulator/pcre/pcre-7.6.tar.bz2 create mode 100644 erts/emulator/pcre/pcre-8.33.tar.bz2 create mode 100644 erts/emulator/pcre/pcre_byte_order.c delete mode 100644 erts/emulator/pcre/pcre_info.c create mode 100644 erts/emulator/pcre/pcre_jit_compile.c delete mode 100644 erts/emulator/pcre/pcre_make_latin1_default.c create mode 100644 erts/emulator/pcre/pcre_string_utils.c delete mode 100644 erts/emulator/pcre/pcre_try_flipped.c create mode 100644 erts/emulator/pcre/pcre_ucd.c delete mode 100644 erts/emulator/pcre/pcre_ucp_searchfuncs.c delete mode 100644 erts/emulator/pcre/ucpinternal.h delete mode 100644 erts/emulator/pcre/ucptable.h (limited to 'erts') diff --git a/erts/emulator/pcre/dftables.c b/erts/emulator/pcre/dftables.c new file mode 100644 index 0000000000..bff2930824 --- /dev/null +++ b/erts/emulator/pcre/dftables.c @@ -0,0 +1,212 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This is a freestanding support program to generate a file containing +character tables for PCRE. The tables are built according to the current +locale. Now that pcre_maketables is a function visible to the outside world, we +make use of its code from here in order to be consistent. */ +/* %ExternalCopyright% */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "pcre_internal.h" + +#define DFTABLES /* pcre_maketables.c notices this */ +#include "pcre_maketables.c" + + +int main(int argc, char **argv) +{ +FILE *f; +int i = 1; +const unsigned char *tables; +const unsigned char *base_of_tables; + +/* By default, the default C locale is used rather than what the building user +happens to have set. However, if the -L option is given, set the locale from +the LC_xxx environment variables. */ + +if (argc > 1 && strcmp(argv[1], "-L") == 0) + { + setlocale(LC_ALL, ""); /* Set from environment variables */ + i++; + } + +if (argc < i + 1) + { + fprintf(stderr, "dftables: one filename argument is required\n"); + return 1; + } + +tables = pcre_maketables(); +base_of_tables = tables; + +f = fopen(argv[i], "wb"); +if (f == NULL) + { + fprintf(stderr, "dftables: failed to open %s for writing\n", argv[1]); + return 1; + } + +/* There are several fprintf() calls here, because gcc in pedantic mode +complains about the very long string otherwise. */ + +fprintf(f, + "/*************************************************\n" + "* Perl-Compatible Regular Expressions *\n" + "*************************************************/\n\n" + "/* This file was automatically written by the dftables auxiliary\n" + "program. It contains character tables that are used when no external\n" + "tables are passed to PCRE by the application that calls it. The tables\n" + "are used only for characters whose code values are less than 256.\n\n"); +fprintf(f, + "The following #includes are present because without them gcc 4.x may remove\n" + "the array definition from the final binary if PCRE is built into a static\n" + "library and dead code stripping is activated. This leads to link errors.\n" + "Pulling in the header ensures that the array gets flagged as \"someone\n" + "outside this compilation unit might reference this\" and so it will always\n" + "be supplied to the linker. */\n\n"); + +/* Force config.h in z/OS */ + +#if defined NATIVE_ZOS +fprintf(f, + "/* For z/OS, config.h is forced */\n" + "#ifndef HAVE_CONFIG_H\n" + "#define HAVE_CONFIG_H 1\n" + "#endif\n\n"); +#endif + +fprintf(f, + "#ifdef HAVE_CONFIG_H\n" + "#include \"config.h\"\n" + "#endif\n\n" + "#include \"pcre_internal.h\"\n\n"); + +fprintf(f, + "const pcre_uint8 PRIV(default_tables)[] = {\n\n" + "/* This table is a lower casing table. */\n\n"); + +fprintf(f, " "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) fprintf(f, "\n "); + fprintf(f, "%3d", *tables++); + if (i != 255) fprintf(f, ","); + } +fprintf(f, ",\n\n"); + +fprintf(f, "/* This table is a case flipping table. */\n\n"); + +fprintf(f, " "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) fprintf(f, "\n "); + fprintf(f, "%3d", *tables++); + if (i != 255) fprintf(f, ","); + } +fprintf(f, ",\n\n"); + +fprintf(f, + "/* This table contains bit maps for various character classes.\n" + "Each map is 32 bytes long and the bits run from the least\n" + "significant end of each byte. The classes that have their own\n" + "maps are: space, xdigit, digit, upper, lower, word, graph\n" + "print, punct, and cntrl. Other classes are built from combinations. */\n\n"); + +fprintf(f, " "); +for (i = 0; i < cbit_length; i++) + { + if ((i & 7) == 0 && i != 0) + { + if ((i & 31) == 0) fprintf(f, "\n"); + fprintf(f, "\n "); + } + fprintf(f, "0x%02x", *tables++); + if (i != cbit_length - 1) fprintf(f, ","); + } +fprintf(f, ",\n\n"); + +fprintf(f, + "/* This table identifies various classes of character by individual bits:\n" + " 0x%02x white space character\n" + " 0x%02x letter\n" + " 0x%02x decimal digit\n" + " 0x%02x hexadecimal digit\n" + " 0x%02x alphanumeric or '_'\n" + " 0x%02x regular expression metacharacter or binary zero\n*/\n\n", + ctype_space, ctype_letter, ctype_digit, ctype_xdigit, ctype_word, + ctype_meta); + +fprintf(f, " "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) + { + fprintf(f, " /* "); + if (isprint(i-8)) fprintf(f, " %c -", i-8); + else fprintf(f, "%3d-", i-8); + if (isprint(i-1)) fprintf(f, " %c ", i-1); + else fprintf(f, "%3d", i-1); + fprintf(f, " */\n "); + } + fprintf(f, "0x%02x", *tables++); + if (i != 255) fprintf(f, ","); + } + +fprintf(f, "};/* "); +if (isprint(i-8)) fprintf(f, " %c -", i-8); + else fprintf(f, "%3d-", i-8); +if (isprint(i-1)) fprintf(f, " %c ", i-1); + else fprintf(f, "%3d", i-1); +fprintf(f, " */\n\n/* End of pcre_chartables.c */\n"); + +fclose(f); +free((void *)base_of_tables); +return 0; +} + +/* End of dftables.c */ diff --git a/erts/emulator/pcre/local_config.h b/erts/emulator/pcre/local_config.h index 0c85410363..791d7f5a6b 100644 --- a/erts/emulator/pcre/local_config.h +++ b/erts/emulator/pcre/local_config.h @@ -75,7 +75,7 @@ #define SUPPORT_UCP /* Define to enable support for the UTF-8 Unicode encoding. */ -#define SUPPORT_UTF8 +#define SUPPORT_UTF /* Version number of package */ -#define VERSION "7.6" +#define VERSION "8.33" diff --git a/erts/emulator/pcre/make_latin1_table.c b/erts/emulator/pcre/make_latin1_table.c deleted file mode 100644 index cec4524d18..0000000000 --- a/erts/emulator/pcre/make_latin1_table.c +++ /dev/null @@ -1,201 +0,0 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ - -/* PCRE is a library of functions to support regular expressions whose syntax -and semantics are as close as possible to those of the Perl 5 language. - - Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge - ------------------------------------------------------------------------------ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the University of Cambridge nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------ -*/ - -/* %ExternalCopyright% */ - -/* This is a freestanding support program to generate a file containing -character tables for PCRE. The tables are built according to the current -locale. Now that pcre_maketables is a function visible to the outside world, we -make use of its code from here in order to be consistent. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include - -#include "pcre_internal.h" - -extern const unsigned char *pcre_make_latin1_tables(void); - -static int my_isprint(int x) { - if (x < 160) - return isprint(x); - else - return 1; -} - - -int main(int argc, char **argv) -{ -FILE *f; -int i = 1; -const unsigned char *tables; -const unsigned char *base_of_tables; - -/* By default, the default C locale is used rather than what the building user -happens to have set. However, if the -L option is given, set the locale from -the LC_xxx environment variables. */ -setlocale(LC_ALL, "C"); - -if (argc < i + 1) - { - fprintf(stderr, "make_latin1_table: one filename argument is required\n"); - return 1; - } - -tables = pcre_make_latin1_tables(); -base_of_tables = tables; - -f = fopen(argv[i], "wb"); -if (f == NULL) - { - fprintf(stderr, "make_latin1_table: failed to open %s for writing\n", argv[1]); - return 1; - } - -/* There are several fprintf() calls here, because gcc in pedantic mode -complains about the very long string otherwise. */ - -fprintf(f, - "/*************************************************\n" - "* Perl-Compatible Regular Expressions *\n" - "*************************************************/\n\n" - "/* This file was automatically written by the make_latin1_table auxiliary\n" - "program. It contains character tables that are used when no external\n" - "tables are passed to PCRE by the application that calls it. The tables\n" - "are used only for characters whose code values are less than 256.\n\n"); -fprintf(f, - "The following #includes are present because without them gcc 4.x may remove\n" - "the array definition from the final binary if PCRE is built into a static\n" - "library and dead code stripping is activated. This leads to link errors.\n" - "Pulling in the header ensures that the array gets flagged as \"someone\n" - "outside this compilation unit might reference this\" and so it will always\n" - "be supplied to the linker. */\n\n" - "#ifdef HAVE_CONFIG_H\n" - "#include \"config.h\"\n" - "#endif\n\n" - "#include \"pcre_internal.h\"\n\n"); -fprintf(f, - "const unsigned char _erts_pcre_default_tables[] = {\n\n" - "/* This table is a lower casing table. */\n\n"); - -fprintf(f, " "); -for (i = 0; i < 256; i++) - { - if ((i & 7) == 0 && i != 0) fprintf(f, "\n "); - fprintf(f, "%3d", *tables++); - if (i != 255) fprintf(f, ","); - } -fprintf(f, ",\n\n"); - -fprintf(f, "/* This table is a case flipping table. */\n\n"); - -fprintf(f, " "); -for (i = 0; i < 256; i++) - { - if ((i & 7) == 0 && i != 0) fprintf(f, "\n "); - fprintf(f, "%3d", *tables++); - if (i != 255) fprintf(f, ","); - } -fprintf(f, ",\n\n"); - -fprintf(f, - "/* This table contains bit maps for various character classes.\n" - "Each map is 32 bytes long and the bits run from the least\n" - "significant end of each byte. The classes that have their own\n" - "maps are: space, xdigit, digit, upper, lower, word, graph\n" - "print, punct, and cntrl. Other classes are built from combinations. */\n\n"); - -fprintf(f, " "); -for (i = 0; i < cbit_length; i++) - { - if ((i & 7) == 0 && i != 0) - { - if ((i & 31) == 0) fprintf(f, "\n"); - fprintf(f, "\n "); - } - fprintf(f, "0x%02x", *tables++); - if (i != cbit_length - 1) fprintf(f, ","); - } -fprintf(f, ",\n\n"); - -fprintf(f, - "/* This table identifies various classes of character by individual bits:\n" - " 0x%02x white space character\n" - " 0x%02x letter\n" - " 0x%02x decimal digit\n" - " 0x%02x hexadecimal digit\n" - " 0x%02x alphanumeric or '_'\n" - " 0x%02x regular expression metacharacter or binary zero\n*/\n\n", - ctype_space, ctype_letter, ctype_digit, ctype_xdigit, ctype_word, - ctype_meta); - -fprintf(f, " "); -for (i = 0; i < 256; i++) - { - if ((i & 7) == 0 && i != 0) - { - fprintf(f, " /* "); - if (my_isprint(i-8)) fprintf(f, " %c -", i-8); - else fprintf(f, "%3d-", i-8); - if (my_isprint(i-1)) fprintf(f, " %c ", i-1); - else fprintf(f, "%3d", i-1); - fprintf(f, " */\n "); - } - fprintf(f, "0x%02x", *tables++); - if (i != 255) fprintf(f, ","); - } - -fprintf(f, "};/* "); -if (my_isprint(i-8)) fprintf(f, " %c -", i-8); - else fprintf(f, "%3d-", i-8); -if (my_isprint(i-1)) fprintf(f, " %c ", i-1); - else fprintf(f, "%3d", i-1); -fprintf(f, " */\n\n/* End of pcre_chartables.c */\n"); - -fclose(f); -free((void *)base_of_tables); -return 0; -} - -/* End of make_latin1_table.c */ diff --git a/erts/emulator/pcre/pcre-7.6.tar.bz2 b/erts/emulator/pcre/pcre-7.6.tar.bz2 deleted file mode 100644 index 66b11115fc..0000000000 Binary files a/erts/emulator/pcre/pcre-7.6.tar.bz2 and /dev/null differ diff --git a/erts/emulator/pcre/pcre-8.33.tar.bz2 b/erts/emulator/pcre/pcre-8.33.tar.bz2 new file mode 100644 index 0000000000..0cea71c8b6 Binary files /dev/null and b/erts/emulator/pcre/pcre-8.33.tar.bz2 differ diff --git a/erts/emulator/pcre/pcre.h b/erts/emulator/pcre/pcre.h index 1701bd112b..9a056a15ee 100644 --- a/erts/emulator/pcre/pcre.h +++ b/erts/emulator/pcre/pcre.h @@ -5,7 +5,7 @@ /* This is the public header file for the PCRE library, to be #included by applications that call the PCRE functions. - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2013 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -42,10 +42,10 @@ POSSIBILITY OF SUCH DAMAGE. /* The current PCRE version information. */ -#define PCRE_MAJOR 7 -#define PCRE_MINOR 6 +#define PCRE_MAJOR 8 +#define PCRE_MINOR 33 #define PCRE_PRERELEASE -#define PCRE_DATE 2008-01-28 +#define PCRE_DATE 2013-05-28 /* When an application links to a PCRE DLL in Windows, the symbols that are imported have to be identified as such. When building PCRE, the appropriate @@ -96,66 +96,163 @@ it is needed here for malloc. */ extern "C" { #endif -/* Options */ - -#define PCRE_CASELESS 0x00000001 -#define PCRE_MULTILINE 0x00000002 -#define PCRE_DOTALL 0x00000004 -#define PCRE_EXTENDED 0x00000008 -#define PCRE_ANCHORED 0x00000010 -#define PCRE_DOLLAR_ENDONLY 0x00000020 -#define PCRE_EXTRA 0x00000040 -#define PCRE_NOTBOL 0x00000080 -#define PCRE_NOTEOL 0x00000100 -#define PCRE_UNGREEDY 0x00000200 -#define PCRE_NOTEMPTY 0x00000400 -#define PCRE_UTF8 0x00000800 -#define PCRE_NO_AUTO_CAPTURE 0x00001000 -#define PCRE_NO_UTF8_CHECK 0x00002000 -#define PCRE_AUTO_CALLOUT 0x00004000 -#define PCRE_PARTIAL 0x00008000 -#define PCRE_DFA_SHORTEST 0x00010000 -#define PCRE_DFA_RESTART 0x00020000 -#define PCRE_FIRSTLINE 0x00040000 -#define PCRE_DUPNAMES 0x00080000 -#define PCRE_NEWLINE_CR 0x00100000 -#define PCRE_NEWLINE_LF 0x00200000 -#define PCRE_NEWLINE_CRLF 0x00300000 -#define PCRE_NEWLINE_ANY 0x00400000 -#define PCRE_NEWLINE_ANYCRLF 0x00500000 -#define PCRE_BSR_ANYCRLF 0x00800000 -#define PCRE_BSR_UNICODE 0x01000000 +/* Public options. Some are compile-time only, some are run-time only, and some +are both. Most of the compile-time options are saved with the compiled regex so +that they can be inspected during studying (and therefore JIT compiling). Note +that pcre_study() has its own set of options. Originally, all the options +defined here used distinct bits. However, almost all the bits in a 32-bit word +are now used, so in order to conserve them, option bits that were previously +only recognized at matching time (i.e. by pcre_exec() or pcre_dfa_exec()) may +also be used for compile-time options that affect only compiling and are not +relevant for studying or JIT compiling. + +Some options for pcre_compile() change its behaviour but do not affect the +behaviour of the execution functions. Other options are passed through to the +execution functions and affect their behaviour, with or without affecting the +behaviour of pcre_compile(). + +Options that can be passed to pcre_compile() are tagged Cx below, with these +variants: + +C1 Affects compile only +C2 Does not affect compile; affects exec, dfa_exec +C3 Affects compile, exec, dfa_exec +C4 Affects compile, exec, dfa_exec, study +C5 Affects compile, exec, study + +Options that can be set for pcre_exec() and/or pcre_dfa_exec() are flagged with +E and D, respectively. They take precedence over C3, C4, and C5 settings passed +from pcre_compile(). Those that are compatible with JIT execution are flagged +with J. */ + +#define PCRE_CASELESS 0x00000001 /* C1 */ +#define PCRE_MULTILINE 0x00000002 /* C1 */ +#define PCRE_DOTALL 0x00000004 /* C1 */ +#define PCRE_EXTENDED 0x00000008 /* C1 */ +#define PCRE_ANCHORED 0x00000010 /* C4 E D */ +#define PCRE_DOLLAR_ENDONLY 0x00000020 /* C2 */ +#define PCRE_EXTRA 0x00000040 /* C1 */ +#define PCRE_NOTBOL 0x00000080 /* E D J */ +#define PCRE_NOTEOL 0x00000100 /* E D J */ +#define PCRE_UNGREEDY 0x00000200 /* C1 */ +#define PCRE_NOTEMPTY 0x00000400 /* E D J */ +#define PCRE_UTF8 0x00000800 /* C4 ) */ +#define PCRE_UTF16 0x00000800 /* C4 ) Synonyms */ +#define PCRE_UTF32 0x00000800 /* C4 ) */ +#define PCRE_NO_AUTO_CAPTURE 0x00001000 /* C1 */ +#define PCRE_NO_UTF8_CHECK 0x00002000 /* C1 E D J ) */ +#define PCRE_NO_UTF16_CHECK 0x00002000 /* C1 E D J ) Synonyms */ +#define PCRE_NO_UTF32_CHECK 0x00002000 /* C1 E D J ) */ +#define PCRE_AUTO_CALLOUT 0x00004000 /* C1 */ +#define PCRE_PARTIAL_SOFT 0x00008000 /* E D J ) Synonyms */ +#define PCRE_PARTIAL 0x00008000 /* E D J ) */ + +/* This pair use the same bit. */ +#define PCRE_NEVER_UTF 0x00010000 /* C1 ) Overlaid */ +#define PCRE_DFA_SHORTEST 0x00010000 /* D ) Overlaid */ + +#define PCRE_DFA_RESTART 0x00020000 /* D */ +#define PCRE_FIRSTLINE 0x00040000 /* C3 */ +#define PCRE_DUPNAMES 0x00080000 /* C1 */ +#define PCRE_NEWLINE_CR 0x00100000 /* C3 E D */ +#define PCRE_NEWLINE_LF 0x00200000 /* C3 E D */ +#define PCRE_NEWLINE_CRLF 0x00300000 /* C3 E D */ +#define PCRE_NEWLINE_ANY 0x00400000 /* C3 E D */ +#define PCRE_NEWLINE_ANYCRLF 0x00500000 /* C3 E D */ +#define PCRE_BSR_ANYCRLF 0x00800000 /* C3 E D */ +#define PCRE_BSR_UNICODE 0x01000000 /* C3 E D */ +#define PCRE_JAVASCRIPT_COMPAT 0x02000000 /* C5 */ +#define PCRE_NO_START_OPTIMIZE 0x04000000 /* C2 E D ) Synonyms */ +#define PCRE_NO_START_OPTIMISE 0x04000000 /* C2 E D ) */ +#define PCRE_PARTIAL_HARD 0x08000000 /* E D J */ +#define PCRE_NOTEMPTY_ATSTART 0x10000000 /* E D J */ +#define PCRE_UCP 0x20000000 /* C3 */ /* Exec-time and get/set-time error codes */ -#define PCRE_ERROR_NOMATCH (-1) -#define PCRE_ERROR_NULL (-2) -#define PCRE_ERROR_BADOPTION (-3) -#define PCRE_ERROR_BADMAGIC (-4) -#define PCRE_ERROR_UNKNOWN_OPCODE (-5) -#define PCRE_ERROR_UNKNOWN_NODE (-5) /* For backward compatibility */ -#define PCRE_ERROR_NOMEMORY (-6) -#define PCRE_ERROR_NOSUBSTRING (-7) -#define PCRE_ERROR_MATCHLIMIT (-8) -#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */ -#define PCRE_ERROR_BADUTF8 (-10) -#define PCRE_ERROR_BADUTF8_OFFSET (-11) -#define PCRE_ERROR_PARTIAL (-12) -#define PCRE_ERROR_BADPARTIAL (-13) -#define PCRE_ERROR_INTERNAL (-14) -#define PCRE_ERROR_BADCOUNT (-15) -#define PCRE_ERROR_DFA_UITEM (-16) -#define PCRE_ERROR_DFA_UCOND (-17) -#define PCRE_ERROR_DFA_UMLIMIT (-18) -#define PCRE_ERROR_DFA_WSSIZE (-19) -#define PCRE_ERROR_DFA_RECURSE (-20) -#define PCRE_ERROR_RECURSIONLIMIT (-21) -#define PCRE_ERROR_NULLWSLIMIT (-22) /* No longer actually used */ -#define PCRE_ERROR_BADNEWLINE (-23) -#ifdef ERLANG_INTEGRATION -#define PCRE_ERROR_LOOP_LIMIT (-24) +#define PCRE_ERROR_NOMATCH (-1) +#define PCRE_ERROR_NULL (-2) +#define PCRE_ERROR_BADOPTION (-3) +#define PCRE_ERROR_BADMAGIC (-4) +#define PCRE_ERROR_UNKNOWN_OPCODE (-5) +#define PCRE_ERROR_UNKNOWN_NODE (-5) /* For backward compatibility */ +#define PCRE_ERROR_NOMEMORY (-6) +#define PCRE_ERROR_NOSUBSTRING (-7) +#define PCRE_ERROR_MATCHLIMIT (-8) +#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */ +#define PCRE_ERROR_BADUTF8 (-10) /* Same for 8/16/32 */ +#define PCRE_ERROR_BADUTF16 (-10) /* Same for 8/16/32 */ +#define PCRE_ERROR_BADUTF32 (-10) /* Same for 8/16/32 */ +#define PCRE_ERROR_BADUTF8_OFFSET (-11) /* Same for 8/16 */ +#define PCRE_ERROR_BADUTF16_OFFSET (-11) /* Same for 8/16 */ +#define PCRE_ERROR_PARTIAL (-12) +#define PCRE_ERROR_BADPARTIAL (-13) +#define PCRE_ERROR_INTERNAL (-14) +#define PCRE_ERROR_BADCOUNT (-15) +#define PCRE_ERROR_DFA_UITEM (-16) +#define PCRE_ERROR_DFA_UCOND (-17) +#define PCRE_ERROR_DFA_UMLIMIT (-18) +#define PCRE_ERROR_DFA_WSSIZE (-19) +#define PCRE_ERROR_DFA_RECURSE (-20) +#define PCRE_ERROR_RECURSIONLIMIT (-21) +#define PCRE_ERROR_NULLWSLIMIT (-22) /* No longer actually used */ +#define PCRE_ERROR_BADNEWLINE (-23) +#define PCRE_ERROR_BADOFFSET (-24) +#define PCRE_ERROR_SHORTUTF8 (-25) +#define PCRE_ERROR_SHORTUTF16 (-25) /* Same for 8/16 */ +#define PCRE_ERROR_RECURSELOOP (-26) +#define PCRE_ERROR_JIT_STACKLIMIT (-27) +#define PCRE_ERROR_BADMODE (-28) +#define PCRE_ERROR_BADENDIANNESS (-29) +#define PCRE_ERROR_DFA_BADRESTART (-30) +#define PCRE_ERROR_JIT_BADOPTION (-31) +#define PCRE_ERROR_BADLENGTH (-32) +#define PCRE_ERROR_UNSET (-33) +#if defined(ERLANG_INTEGRATION) +#define PCRE_ERROR_LOOP_LIMIT (-34) #endif +/* Specific error codes for UTF-8 validity checks */ + +#define PCRE_UTF8_ERR0 0 +#define PCRE_UTF8_ERR1 1 +#define PCRE_UTF8_ERR2 2 +#define PCRE_UTF8_ERR3 3 +#define PCRE_UTF8_ERR4 4 +#define PCRE_UTF8_ERR5 5 +#define PCRE_UTF8_ERR6 6 +#define PCRE_UTF8_ERR7 7 +#define PCRE_UTF8_ERR8 8 +#define PCRE_UTF8_ERR9 9 +#define PCRE_UTF8_ERR10 10 +#define PCRE_UTF8_ERR11 11 +#define PCRE_UTF8_ERR12 12 +#define PCRE_UTF8_ERR13 13 +#define PCRE_UTF8_ERR14 14 +#define PCRE_UTF8_ERR15 15 +#define PCRE_UTF8_ERR16 16 +#define PCRE_UTF8_ERR17 17 +#define PCRE_UTF8_ERR18 18 +#define PCRE_UTF8_ERR19 19 +#define PCRE_UTF8_ERR20 20 +#define PCRE_UTF8_ERR21 21 +#define PCRE_UTF8_ERR22 22 /* Unused (was non-character) */ + +/* Specific error codes for UTF-16 validity checks */ + +#define PCRE_UTF16_ERR0 0 +#define PCRE_UTF16_ERR1 1 +#define PCRE_UTF16_ERR2 2 +#define PCRE_UTF16_ERR3 3 +#define PCRE_UTF16_ERR4 4 /* Unused (was non-character) */ + +/* Specific error codes for UTF-32 validity checks */ + +#define PCRE_UTF32_ERR0 0 +#define PCRE_UTF32_ERR1 1 +#define PCRE_UTF32_ERR2 2 /* Unused (was non-character) */ +#define PCRE_UTF32_ERR3 3 + /* Request types for pcre_fullinfo() */ #define PCRE_INFO_OPTIONS 0 @@ -174,8 +271,18 @@ extern "C" { #define PCRE_INFO_OKPARTIAL 12 #define PCRE_INFO_JCHANGED 13 #define PCRE_INFO_HASCRORLF 14 - -/* Request types for erts_pcre_config(). Do not re-arrange, in order to remain +#define PCRE_INFO_MINLENGTH 15 +#define PCRE_INFO_JIT 16 +#define PCRE_INFO_JITSIZE 17 +#define PCRE_INFO_MAXLOOKBEHIND 18 +#define PCRE_INFO_FIRSTCHARACTER 19 +#define PCRE_INFO_FIRSTCHARACTERFLAGS 20 +#define PCRE_INFO_REQUIREDCHAR 21 +#define PCRE_INFO_REQUIREDCHARFLAGS 22 +#define PCRE_INFO_MATCHLIMIT 23 +#define PCRE_INFO_RECURSIONLIMIT 24 + +/* Request types for pcre_config(). Do not re-arrange, in order to remain compatible. */ #define PCRE_CONFIG_UTF8 0 @@ -187,8 +294,20 @@ compatible. */ #define PCRE_CONFIG_UNICODE_PROPERTIES 6 #define PCRE_CONFIG_MATCH_LIMIT_RECURSION 7 #define PCRE_CONFIG_BSR 8 +#define PCRE_CONFIG_JIT 9 +#define PCRE_CONFIG_UTF16 10 +#define PCRE_CONFIG_JITTARGET 11 +#define PCRE_CONFIG_UTF32 12 + +/* Request types for pcre_study(). Do not re-arrange, in order to remain +compatible. */ -/* Bit flags for the pcre_extra structure. Do not re-arrange or redefine +#define PCRE_STUDY_JIT_COMPILE 0x0001 +#define PCRE_STUDY_JIT_PARTIAL_SOFT_COMPILE 0x0002 +#define PCRE_STUDY_JIT_PARTIAL_HARD_COMPILE 0x0004 +#define PCRE_STUDY_EXTRA_NEEDED 0x0008 + +/* Bit flags for the pcre[16|32]_extra structure. Do not re-arrange or redefine these bits, just add new ones on the end, in order to remain compatible. */ #define PCRE_EXTRA_STUDY_DATA 0x0001 @@ -196,8 +315,10 @@ these bits, just add new ones on the end, in order to remain compatible. */ #define PCRE_EXTRA_CALLOUT_DATA 0x0004 #define PCRE_EXTRA_TABLES 0x0008 #define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0x0010 -#ifdef ERLANG_INTEGRATION -#define PCRE_EXTRA_LOOP_LIMIT 0x0020 +#define PCRE_EXTRA_MARK 0x0020 +#define PCRE_EXTRA_EXECUTABLE_JIT 0x0040 +#if defined(ERLANG_INTEGRATION) +#define PCRE_EXTRA_LOOP_LIMIT 0x0080 #endif /* Types */ @@ -205,6 +326,43 @@ these bits, just add new ones on the end, in order to remain compatible. */ struct real_pcre; /* declaration; the definition is private */ typedef struct real_pcre pcre; +struct real_pcre16; /* declaration; the definition is private */ +typedef struct real_pcre16 pcre16; + +struct real_pcre32; /* declaration; the definition is private */ +typedef struct real_pcre32 pcre32; + +struct real_pcre_jit_stack; /* declaration; the definition is private */ +typedef struct real_pcre_jit_stack pcre_jit_stack; + +struct real_pcre16_jit_stack; /* declaration; the definition is private */ +typedef struct real_pcre16_jit_stack pcre16_jit_stack; + +struct real_pcre32_jit_stack; /* declaration; the definition is private */ +typedef struct real_pcre32_jit_stack pcre32_jit_stack; + +/* If PCRE is compiled with 16 bit character support, PCRE_UCHAR16 must contain +a 16 bit wide signed data type. Otherwise it can be a dummy data type since +pcre16 functions are not implemented. There is a check for this in pcre_internal.h. */ +#ifndef PCRE_UCHAR16 +#define PCRE_UCHAR16 unsigned short +#endif + +#ifndef PCRE_SPTR16 +#define PCRE_SPTR16 const PCRE_UCHAR16 * +#endif + +/* If PCRE is compiled with 32 bit character support, PCRE_UCHAR32 must contain +a 32 bit wide signed data type. Otherwise it can be a dummy data type since +pcre32 functions are not implemented. There is a check for this in pcre_internal.h. */ +#ifndef PCRE_UCHAR32 +#define PCRE_UCHAR32 unsigned int +#endif + +#ifndef PCRE_SPTR32 +#define PCRE_SPTR32 const PCRE_UCHAR32 * +#endif + /* When PCRE is compiled as a C++ library, the subject pointer type can be replaced with a custom type. For conventional use, the public interface is a const char *. */ @@ -224,7 +382,9 @@ typedef struct pcre_extra { void *callout_data; /* Data passed back in callouts */ const unsigned char *tables; /* Pointer to character tables */ unsigned long int match_limit_recursion; /* Max recursive calls to match() */ -#ifdef ERLANG_INTEGRATION + unsigned char **mark; /* For passing back a mark pointer */ + void *executable_jit; /* Contains a pointer to a compiled jit code */ +#if defined(ERLANG_INTEGRATION) unsigned long int loop_limit; unsigned long *loop_counter_return; void **restart_data; /* in/out */ @@ -232,6 +392,32 @@ typedef struct pcre_extra { #endif } pcre_extra; +/* Same structure as above, but with 16 bit char pointers. */ + +typedef struct pcre16_extra { + unsigned long int flags; /* Bits for which fields are set */ + void *study_data; /* Opaque data from pcre_study() */ + unsigned long int match_limit; /* Maximum number of calls to match() */ + void *callout_data; /* Data passed back in callouts */ + const unsigned char *tables; /* Pointer to character tables */ + unsigned long int match_limit_recursion; /* Max recursive calls to match() */ + PCRE_UCHAR16 **mark; /* For passing back a mark pointer */ + void *executable_jit; /* Contains a pointer to a compiled jit code */ +} pcre16_extra; + +/* Same structure as above, but with 32 bit char pointers. */ + +typedef struct pcre32_extra { + unsigned long int flags; /* Bits for which fields are set */ + void *study_data; /* Opaque data from pcre_study() */ + unsigned long int match_limit; /* Maximum number of calls to match() */ + void *callout_data; /* Data passed back in callouts */ + const unsigned char *tables; /* Pointer to character tables */ + unsigned long int match_limit_recursion; /* Max recursive calls to match() */ + PCRE_UCHAR32 **mark; /* For passing back a mark pointer */ + void *executable_jit; /* Contains a pointer to a compiled jit code */ +} pcre32_extra; + /* The structure for passing out data via the pcre_callout_function. We use a structure so that new fields can be added on the end in future versions, without changing the API of the function, thereby allowing old clients to work @@ -252,9 +438,55 @@ typedef struct pcre_callout_block { /* ------------------- Added for Version 1 -------------------------- */ int pattern_position; /* Offset to next item in the pattern */ int next_item_length; /* Length of next item in the pattern */ + /* ------------------- Added for Version 2 -------------------------- */ + const unsigned char *mark; /* Pointer to current mark or NULL */ /* ------------------------------------------------------------------ */ } pcre_callout_block; +/* Same structure as above, but with 16 bit char pointers. */ + +typedef struct pcre16_callout_block { + int version; /* Identifies version of block */ + /* ------------------------ Version 0 ------------------------------- */ + int callout_number; /* Number compiled into pattern */ + int *offset_vector; /* The offset vector */ + PCRE_SPTR16 subject; /* The subject being matched */ + int subject_length; /* The length of the subject */ + int start_match; /* Offset to start of this match attempt */ + int current_position; /* Where we currently are in the subject */ + int capture_top; /* Max current capture */ + int capture_last; /* Most recently closed capture */ + void *callout_data; /* Data passed in with the call */ + /* ------------------- Added for Version 1 -------------------------- */ + int pattern_position; /* Offset to next item in the pattern */ + int next_item_length; /* Length of next item in the pattern */ + /* ------------------- Added for Version 2 -------------------------- */ + const PCRE_UCHAR16 *mark; /* Pointer to current mark or NULL */ + /* ------------------------------------------------------------------ */ +} pcre16_callout_block; + +/* Same structure as above, but with 32 bit char pointers. */ + +typedef struct pcre32_callout_block { + int version; /* Identifies version of block */ + /* ------------------------ Version 0 ------------------------------- */ + int callout_number; /* Number compiled into pattern */ + int *offset_vector; /* The offset vector */ + PCRE_SPTR32 subject; /* The subject being matched */ + int subject_length; /* The length of the subject */ + int start_match; /* Offset to start of this match attempt */ + int current_position; /* Where we currently are in the subject */ + int capture_top; /* Max current capture */ + int capture_last; /* Most recently closed capture */ + void *callout_data; /* Data passed in with the call */ + /* ------------------- Added for Version 1 -------------------------- */ + int pattern_position; /* Offset to next item in the pattern */ + int next_item_length; /* Length of next item in the pattern */ + /* ------------------- Added for Version 2 -------------------------- */ + const PCRE_UCHAR32 *mark; /* Pointer to current mark or NULL */ + /* ------------------------------------------------------------------ */ +} pcre32_callout_block; + /* Indirection for store get and free functions. These can be set to alternative malloc/free functions if required. Special ones are used in the non-recursive case for "frames". There is also an optional callout function @@ -262,52 +494,180 @@ that is triggered by the (?) regex item. For Virtual Pascal, these definitions have to take another form. */ #ifndef VPCOMPAT -PCRE_EXP_DECL void *(*erts_pcre_malloc)(size_t); -PCRE_EXP_DECL void (*erts_pcre_free)(void *); -PCRE_EXP_DECL void *(*erts_pcre_stack_malloc)(size_t); -PCRE_EXP_DECL void (*erts_pcre_stack_free)(void *); -PCRE_EXP_DECL int (*erts_pcre_callout)(pcre_callout_block *); +PCRE_EXP_DECL void *(*pcre_malloc)(size_t); +PCRE_EXP_DECL void (*pcre_free)(void *); +PCRE_EXP_DECL void *(*pcre_stack_malloc)(size_t); +PCRE_EXP_DECL void (*pcre_stack_free)(void *); +PCRE_EXP_DECL int (*pcre_callout)(pcre_callout_block *); + +PCRE_EXP_DECL void *(*pcre16_malloc)(size_t); +PCRE_EXP_DECL void (*pcre16_free)(void *); +PCRE_EXP_DECL void *(*pcre16_stack_malloc)(size_t); +PCRE_EXP_DECL void (*pcre16_stack_free)(void *); +PCRE_EXP_DECL int (*pcre16_callout)(pcre16_callout_block *); + +PCRE_EXP_DECL void *(*pcre32_malloc)(size_t); +PCRE_EXP_DECL void (*pcre32_free)(void *); +PCRE_EXP_DECL void *(*pcre32_stack_malloc)(size_t); +PCRE_EXP_DECL void (*pcre32_stack_free)(void *); +PCRE_EXP_DECL int (*pcre32_callout)(pcre32_callout_block *); #else /* VPCOMPAT */ -PCRE_EXP_DECL void *erts_pcre_malloc(size_t); -PCRE_EXP_DECL void erts_pcre_free(void *); -PCRE_EXP_DECL void *erts_pcre_stack_malloc(size_t); -PCRE_EXP_DECL void erts_pcre_stack_free(void *); -PCRE_EXP_DECL int erts_pcre_callout(pcre_callout_block *); +PCRE_EXP_DECL void *pcre_malloc(size_t); +PCRE_EXP_DECL void pcre_free(void *); +PCRE_EXP_DECL void *pcre_stack_malloc(size_t); +PCRE_EXP_DECL void pcre_stack_free(void *); +PCRE_EXP_DECL int pcre_callout(pcre_callout_block *); + +PCRE_EXP_DECL void *pcre16_malloc(size_t); +PCRE_EXP_DECL void pcre16_free(void *); +PCRE_EXP_DECL void *pcre16_stack_malloc(size_t); +PCRE_EXP_DECL void pcre16_stack_free(void *); +PCRE_EXP_DECL int pcre16_callout(pcre16_callout_block *); + +PCRE_EXP_DECL void *pcre32_malloc(size_t); +PCRE_EXP_DECL void pcre32_free(void *); +PCRE_EXP_DECL void *pcre32_stack_malloc(size_t); +PCRE_EXP_DECL void pcre32_stack_free(void *); +PCRE_EXP_DECL int pcre32_callout(pcre32_callout_block *); #endif /* VPCOMPAT */ +/* User defined callback which provides a stack just before the match starts. */ + +typedef pcre_jit_stack *(*pcre_jit_callback)(void *); +typedef pcre16_jit_stack *(*pcre16_jit_callback)(void *); +typedef pcre32_jit_stack *(*pcre32_jit_callback)(void *); + /* Exported PCRE functions */ -PCRE_EXP_DECL pcre *erts_pcre_compile(const char *, int, const char **, int *, +PCRE_EXP_DECL pcre *pcre_compile(const char *, int, const char **, int *, + const unsigned char *); +PCRE_EXP_DECL pcre16 *pcre16_compile(PCRE_SPTR16, int, const char **, int *, + const unsigned char *); +PCRE_EXP_DECL pcre32 *pcre32_compile(PCRE_SPTR32, int, const char **, int *, const unsigned char *); -PCRE_EXP_DECL pcre *erts_pcre_compile2(const char *, int, int *, const char **, +PCRE_EXP_DECL pcre *pcre_compile2(const char *, int, int *, const char **, + int *, const unsigned char *); +PCRE_EXP_DECL pcre16 *pcre16_compile2(PCRE_SPTR16, int, int *, const char **, + int *, const unsigned char *); +PCRE_EXP_DECL pcre32 *pcre32_compile2(PCRE_SPTR32, int, int *, const char **, int *, const unsigned char *); -PCRE_EXP_DECL int erts_pcre_config(int, void *); -PCRE_EXP_DECL int erts_pcre_copy_named_substring(const pcre *, const char *, +PCRE_EXP_DECL int pcre_config(int, void *); +PCRE_EXP_DECL int pcre16_config(int, void *); +PCRE_EXP_DECL int pcre32_config(int, void *); +PCRE_EXP_DECL int pcre_copy_named_substring(const pcre *, const char *, int *, int, const char *, char *, int); -PCRE_EXP_DECL int erts_pcre_copy_substring(const char *, int *, int, int, char *, - int); -PCRE_EXP_DECL int erts_pcre_dfa_exec(const pcre *, const pcre_extra *, +PCRE_EXP_DECL int pcre16_copy_named_substring(const pcre16 *, PCRE_SPTR16, + int *, int, PCRE_SPTR16, PCRE_UCHAR16 *, int); +PCRE_EXP_DECL int pcre32_copy_named_substring(const pcre32 *, PCRE_SPTR32, + int *, int, PCRE_SPTR32, PCRE_UCHAR32 *, int); +PCRE_EXP_DECL int pcre_copy_substring(const char *, int *, int, int, + char *, int); +PCRE_EXP_DECL int pcre16_copy_substring(PCRE_SPTR16, int *, int, int, + PCRE_UCHAR16 *, int); +PCRE_EXP_DECL int pcre32_copy_substring(PCRE_SPTR32, int *, int, int, + PCRE_UCHAR32 *, int); +PCRE_EXP_DECL int pcre_dfa_exec(const pcre *, const pcre_extra *, const char *, int, int, int, int *, int , int *, int); -PCRE_EXP_DECL int erts_pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR, +PCRE_EXP_DECL int pcre16_dfa_exec(const pcre16 *, const pcre16_extra *, + PCRE_SPTR16, int, int, int, int *, int , int *, int); +PCRE_EXP_DECL int pcre32_dfa_exec(const pcre32 *, const pcre32_extra *, + PCRE_SPTR32, int, int, int, int *, int , int *, int); +PCRE_EXP_DECL int pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR, int, int, int, int *, int); -PCRE_EXP_DECL void erts_pcre_free_substring(const char *); -PCRE_EXP_DECL void erts_pcre_free_substring_list(const char **); -PCRE_EXP_DECL int erts_pcre_fullinfo(const pcre *, const pcre_extra *, int, +PCRE_EXP_DECL int pcre16_exec(const pcre16 *, const pcre16_extra *, + PCRE_SPTR16, int, int, int, int *, int); +PCRE_EXP_DECL int pcre32_exec(const pcre32 *, const pcre32_extra *, + PCRE_SPTR32, int, int, int, int *, int); +PCRE_EXP_DECL int pcre_jit_exec(const pcre *, const pcre_extra *, + PCRE_SPTR, int, int, int, int *, int, + pcre_jit_stack *); +PCRE_EXP_DECL int pcre16_jit_exec(const pcre16 *, const pcre16_extra *, + PCRE_SPTR16, int, int, int, int *, int, + pcre16_jit_stack *); +PCRE_EXP_DECL int pcre32_jit_exec(const pcre32 *, const pcre32_extra *, + PCRE_SPTR32, int, int, int, int *, int, + pcre32_jit_stack *); +PCRE_EXP_DECL void pcre_free_substring(const char *); +PCRE_EXP_DECL void pcre16_free_substring(PCRE_SPTR16); +PCRE_EXP_DECL void pcre32_free_substring(PCRE_SPTR32); +PCRE_EXP_DECL void pcre_free_substring_list(const char **); +PCRE_EXP_DECL void pcre16_free_substring_list(PCRE_SPTR16 *); +PCRE_EXP_DECL void pcre32_free_substring_list(PCRE_SPTR32 *); +PCRE_EXP_DECL int pcre_fullinfo(const pcre *, const pcre_extra *, int, void *); -PCRE_EXP_DECL int erts_pcre_get_named_substring(const pcre *, const char *, +PCRE_EXP_DECL int pcre16_fullinfo(const pcre16 *, const pcre16_extra *, int, + void *); +PCRE_EXP_DECL int pcre32_fullinfo(const pcre32 *, const pcre32_extra *, int, + void *); +PCRE_EXP_DECL int pcre_get_named_substring(const pcre *, const char *, int *, int, const char *, const char **); -PCRE_EXP_DECL int erts_pcre_get_stringnumber(const pcre *, const char *); -PCRE_EXP_DECL int erts_pcre_get_stringtable_entries(const pcre *, const char *, +PCRE_EXP_DECL int pcre16_get_named_substring(const pcre16 *, PCRE_SPTR16, + int *, int, PCRE_SPTR16, PCRE_SPTR16 *); +PCRE_EXP_DECL int pcre32_get_named_substring(const pcre32 *, PCRE_SPTR32, + int *, int, PCRE_SPTR32, PCRE_SPTR32 *); +PCRE_EXP_DECL int pcre_get_stringnumber(const pcre *, const char *); +PCRE_EXP_DECL int pcre16_get_stringnumber(const pcre16 *, PCRE_SPTR16); +PCRE_EXP_DECL int pcre32_get_stringnumber(const pcre32 *, PCRE_SPTR32); +PCRE_EXP_DECL int pcre_get_stringtable_entries(const pcre *, const char *, char **, char **); -PCRE_EXP_DECL int erts_pcre_get_substring(const char *, int *, int, int, +PCRE_EXP_DECL int pcre16_get_stringtable_entries(const pcre16 *, PCRE_SPTR16, + PCRE_UCHAR16 **, PCRE_UCHAR16 **); +PCRE_EXP_DECL int pcre32_get_stringtable_entries(const pcre32 *, PCRE_SPTR32, + PCRE_UCHAR32 **, PCRE_UCHAR32 **); +PCRE_EXP_DECL int pcre_get_substring(const char *, int *, int, int, const char **); -PCRE_EXP_DECL int erts_pcre_get_substring_list(const char *, int *, int, +PCRE_EXP_DECL int pcre16_get_substring(PCRE_SPTR16, int *, int, int, + PCRE_SPTR16 *); +PCRE_EXP_DECL int pcre32_get_substring(PCRE_SPTR32, int *, int, int, + PCRE_SPTR32 *); +PCRE_EXP_DECL int pcre_get_substring_list(const char *, int *, int, const char ***); -PCRE_EXP_DECL int erts_pcre_info(const pcre *, int *, int *); -PCRE_EXP_DECL const unsigned char *erts_pcre_maketables(void); -PCRE_EXP_DECL int erts_pcre_refcount(pcre *, int); -PCRE_EXP_DECL pcre_extra *erts_pcre_study(const pcre *, int, const char **); -PCRE_EXP_DECL const char *erts_pcre_version(void); +PCRE_EXP_DECL int pcre16_get_substring_list(PCRE_SPTR16, int *, int, + PCRE_SPTR16 **); +PCRE_EXP_DECL int pcre32_get_substring_list(PCRE_SPTR32, int *, int, + PCRE_SPTR32 **); +PCRE_EXP_DECL const unsigned char *pcre_maketables(void); +PCRE_EXP_DECL const unsigned char *pcre16_maketables(void); +PCRE_EXP_DECL const unsigned char *pcre32_maketables(void); +PCRE_EXP_DECL int pcre_refcount(pcre *, int); +PCRE_EXP_DECL int pcre16_refcount(pcre16 *, int); +PCRE_EXP_DECL int pcre32_refcount(pcre32 *, int); +PCRE_EXP_DECL pcre_extra *pcre_study(const pcre *, int, const char **); +PCRE_EXP_DECL pcre16_extra *pcre16_study(const pcre16 *, int, const char **); +PCRE_EXP_DECL pcre32_extra *pcre32_study(const pcre32 *, int, const char **); +PCRE_EXP_DECL void pcre_free_study(pcre_extra *); +PCRE_EXP_DECL void pcre16_free_study(pcre16_extra *); +PCRE_EXP_DECL void pcre32_free_study(pcre32_extra *); +PCRE_EXP_DECL const char *pcre_version(void); +PCRE_EXP_DECL const char *pcre16_version(void); +PCRE_EXP_DECL const char *pcre32_version(void); + +/* Utility functions for byte order swaps. */ +PCRE_EXP_DECL int pcre_pattern_to_host_byte_order(pcre *, pcre_extra *, + const unsigned char *); +PCRE_EXP_DECL int pcre16_pattern_to_host_byte_order(pcre16 *, pcre16_extra *, + const unsigned char *); +PCRE_EXP_DECL int pcre32_pattern_to_host_byte_order(pcre32 *, pcre32_extra *, + const unsigned char *); +PCRE_EXP_DECL int pcre16_utf16_to_host_byte_order(PCRE_UCHAR16 *, + PCRE_SPTR16, int, int *, int); +PCRE_EXP_DECL int pcre32_utf32_to_host_byte_order(PCRE_UCHAR32 *, + PCRE_SPTR32, int, int *, int); + +/* JIT compiler related functions. */ + +PCRE_EXP_DECL pcre_jit_stack *pcre_jit_stack_alloc(int, int); +PCRE_EXP_DECL pcre16_jit_stack *pcre16_jit_stack_alloc(int, int); +PCRE_EXP_DECL pcre32_jit_stack *pcre32_jit_stack_alloc(int, int); +PCRE_EXP_DECL void pcre_jit_stack_free(pcre_jit_stack *); +PCRE_EXP_DECL void pcre16_jit_stack_free(pcre16_jit_stack *); +PCRE_EXP_DECL void pcre32_jit_stack_free(pcre32_jit_stack *); +PCRE_EXP_DECL void pcre_assign_jit_stack(pcre_extra *, + pcre_jit_callback, void *); +PCRE_EXP_DECL void pcre16_assign_jit_stack(pcre16_extra *, + pcre16_jit_callback, void *); +PCRE_EXP_DECL void pcre32_assign_jit_stack(pcre32_extra *, + pcre32_jit_callback, void *); #ifdef ERLANG_INTEGRATION PCRE_EXP_DECL void erts_pcre_free_restart_data(void *restart_data); diff --git a/erts/emulator/pcre/pcre.mk b/erts/emulator/pcre/pcre.mk index 57bf5de2fb..54412f6b1d 100644 --- a/erts/emulator/pcre/pcre.mk +++ b/erts/emulator/pcre/pcre.mk @@ -26,17 +26,18 @@ pcre_exec.o \ pcre_fullinfo.o \ pcre_get.o \ pcre_globals.o \ -pcre_info.o \ pcre_maketables.o \ pcre_newline.o \ pcre_ord2utf8.o \ pcre_refcount.o \ pcre_study.o \ pcre_tables.o \ -pcre_try_flipped.o \ -pcre_ucp_searchfuncs.o \ pcre_valid_utf8.o \ pcre_version.o \ +pcre_byte_order.o \ +pcre_jit_compile.o \ +pcre_string_utils.o \ +pcre_ucd.o \ pcre_xclass.o PCRE_OBJS = $(PCRE_O:%=$(PCRE_OBJDIR)/%) @@ -45,6 +46,8 @@ PCRE_GENINC = $(ERL_TOP)/erts/emulator/pcre/pcre_exec_loop_break_cases.inc PCRE_OBJDIR = $(ERL_TOP)/erts/emulator/pcre/obj/$(TARGET)/$(TYPE) +PCRE_DIR = $(ERL_TOP)/erts/emulator/pcre + PCRE_CFLAGS = $(filter-out -DDEBUG,$(CFLAGS)) -DERLANG_INTEGRATION ifeq ($(TARGET), win32) @@ -56,11 +59,11 @@ $(EPCRE_LIB): $(PCRE_OBJS) -@ ($(RANLIB) $@ || true) 2>/dev/null endif -$(PCRE_OBJDIR)/%.o: pcre/%.c +$(PCRE_OBJDIR)/%.o: $(PCRE_DIR)/%.c $(V_CC) -c $(PCRE_CFLAGS) -o $@ $< -$(PCRE_GENINC): pcre/pcre_exec.c - $(gen_verbose)for x in `grep -n COST_CHK pcre/pcre_exec.c | grep -v 'COST_CHK(N)' | awk -F: '{print $$1}'`; \ +$(PCRE_GENINC): $(PCRE_DIR)/pcre_exec.c + $(gen_verbose)for x in `grep -n COST_CHK $(PCRE_DIR)/pcre_exec.c | grep -v 'COST_CHK(N)' | awk -F: '{print $$1}'`; \ do \ N=`expr $$x + 100`; \ echo "case $$N: goto L_LOOP_COUNT_$${x};"; \ @@ -68,44 +71,66 @@ $(PCRE_GENINC): pcre/pcre_exec.c # Dependencies. -$(PCRE_OBJDIR)/pcre_chartables.o: pcre/pcre_chartables.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_compile.o: pcre/pcre_compile.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_config.o: pcre/pcre_config.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_dfa_exec.o: pcre/pcre_dfa_exec.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_exec.o: pcre/pcre_exec.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h $(PCRE_GENINC) -$(PCRE_OBJDIR)/pcre_fullinfo.o: pcre/pcre_fullinfo.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_get.o: pcre/pcre_get.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_globals.o: pcre/pcre_globals.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_info.o: pcre/pcre_info.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_maketables.o: pcre/pcre_maketables.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_newline.o: pcre/pcre_newline.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_ord2utf8.o: pcre/pcre_ord2utf8.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre/pcre_refcount.o: pcre/pcre_refcount.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_study.o: pcre/pcre_study.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_tables.o: pcre/pcre_tables.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_try_flipped.o: pcre/pcre_try_flipped.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre_ucp_searchfuncs.o: pcre/pcre_ucp_searchfuncs.c \ - pcre/pcre_internal.h pcre/local_config.h pcre/pcre.h pcre/ucp.h \ - pcre/ucpinternal.h pcre/ucptable.h -$(PCRE_OBJDIR)/pcre_valid_utf8.o: pcre/pcre_valid_utf8.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h -pcre_version.o: pcre/pcre_version.c pcre/pcre_internal.h pcre/local_config.h \ - pcre/pcre.h pcre/ucp.h -$(PCRE_OBJDIR)/pcre/pcre_xclass.o: pcre/pcre_xclass.c pcre/pcre_internal.h \ - pcre/local_config.h pcre/pcre.h pcre/ucp.h +$(PCRE_OBJDIR)/pcre_byte_order.o: $(PCRE_DIR)/pcre_byte_order.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h \ + $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_compile.o: $(PCRE_DIR)/pcre_compile.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_config.o: $(PCRE_DIR)/pcre_config.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_dfa_exec.o: $(PCRE_DIR)/pcre_dfa_exec.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_exec.o: $(PCRE_DIR)/pcre_exec.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h $(PCRE_GENINC) +$(PCRE_OBJDIR)/pcre_fullinfo.o: $(PCRE_DIR)/pcre_fullinfo.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_get.o: $(PCRE_DIR)/pcre_get.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_globals.o: $(PCRE_DIR)/pcre_globals.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_jit_compile.o: $(PCRE_DIR)/pcre_jit_compile.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_latin_1_table.o: $(PCRE_DIR)/pcre_latin_1_table.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_maketables.o: $(PCRE_DIR)/pcre_maketables.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_newline.o: $(PCRE_DIR)/pcre_newline.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_ord2utf8.o: $(PCRE_DIR)/pcre_ord2utf8.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_refcount.o: $(PCRE_DIR)/pcre_refcount.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_string_utils.o: $(PCRE_DIR)/pcre_string_utils.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_study.o: $(PCRE_DIR)/pcre_study.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_tables.o: $(PCRE_DIR)/pcre_tables.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_ucd.o: $(PCRE_DIR)/pcre_ucd.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_valid_utf8.o: $(PCRE_DIR)/pcre_valid_utf8.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_version.o: $(PCRE_DIR)/pcre_version.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h +$(PCRE_OBJDIR)/pcre_xclass.o: $(PCRE_DIR)/pcre_xclass.c \ + $(PCRE_DIR)/pcre_internal.h $(PCRE_DIR)/local_config.h \ + $(PCRE_DIR)/pcre.h $(PCRE_DIR)/ucp.h diff --git a/erts/emulator/pcre/pcre_byte_order.c b/erts/emulator/pcre/pcre_byte_order.c new file mode 100644 index 0000000000..7f8457742d --- /dev/null +++ b/erts/emulator/pcre/pcre_byte_order.c @@ -0,0 +1,319 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2013 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains an internal function that tests a compiled pattern to +see if it was compiled with the opposite endianness. If so, it uses an +auxiliary local function to flip the appropriate bytes. */ +/* %ExternalCopyright% */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Swap byte functions * +*************************************************/ + +/* The following functions swap the bytes of a pcre_uint16 +and pcre_uint32 value. + +Arguments: + value any number + +Returns: the byte swapped value +*/ + +static pcre_uint32 +swap_uint32(pcre_uint32 value) +{ +return ((value & 0x000000ff) << 24) | + ((value & 0x0000ff00) << 8) | + ((value & 0x00ff0000) >> 8) | + (value >> 24); +} + +static pcre_uint16 +swap_uint16(pcre_uint16 value) +{ +return (value >> 8) | (value << 8); +} + + +/************************************************* +* Test for a byte-flipped compiled regex * +*************************************************/ + +/* This function swaps the bytes of a compiled pattern usually +loaded form the disk. It also sets the tables pointer, which +is likely an invalid pointer after reload. + +Arguments: + argument_re points to the compiled expression + extra_data points to extra data or is NULL + tables points to the character tables or NULL + +Returns: 0 if the swap is successful, negative on error +*/ + +#if defined COMPILE_PCRE8 +PCRE_EXP_DECL int pcre_pattern_to_host_byte_order(pcre *argument_re, + pcre_extra *extra_data, const unsigned char *tables) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DECL int pcre16_pattern_to_host_byte_order(pcre16 *argument_re, + pcre16_extra *extra_data, const unsigned char *tables) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DECL int pcre32_pattern_to_host_byte_order(pcre32 *argument_re, + pcre32_extra *extra_data, const unsigned char *tables) +#endif +{ +REAL_PCRE *re = (REAL_PCRE *)argument_re; +pcre_study_data *study; +#ifndef COMPILE_PCRE8 +pcre_uchar *ptr; +int length; +#if defined SUPPORT_UTF && defined COMPILE_PCRE16 +BOOL utf; +BOOL utf16_char; +#endif /* SUPPORT_UTF && COMPILE_PCRE16 */ +#endif /* !COMPILE_PCRE8 */ + +if (re == NULL) return PCRE_ERROR_NULL; +if (re->magic_number == MAGIC_NUMBER) + { + if ((re->flags & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; + re->tables = tables; + return 0; + } + +if (re->magic_number != REVERSED_MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; +if ((swap_uint32(re->flags) & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; + +re->magic_number = MAGIC_NUMBER; +re->size = swap_uint32(re->size); +re->options = swap_uint32(re->options); +re->flags = swap_uint32(re->flags); +re->limit_match = swap_uint32(re->limit_match); +re->limit_recursion = swap_uint32(re->limit_recursion); + +#if defined COMPILE_PCRE8 || defined COMPILE_PCRE16 +re->first_char = swap_uint16(re->first_char); +re->req_char = swap_uint16(re->req_char); +#elif defined COMPILE_PCRE32 +re->first_char = swap_uint32(re->first_char); +re->req_char = swap_uint32(re->req_char); +#endif + +re->max_lookbehind = swap_uint16(re->max_lookbehind); +re->top_bracket = swap_uint16(re->top_bracket); +re->top_backref = swap_uint16(re->top_backref); +re->name_table_offset = swap_uint16(re->name_table_offset); +re->name_entry_size = swap_uint16(re->name_entry_size); +re->name_count = swap_uint16(re->name_count); +re->ref_count = swap_uint16(re->ref_count); +re->tables = tables; + +if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_STUDY_DATA) != 0) + { + study = (pcre_study_data *)extra_data->study_data; + study->size = swap_uint32(study->size); + study->flags = swap_uint32(study->flags); + study->minlength = swap_uint32(study->minlength); + } + +#ifndef COMPILE_PCRE8 +ptr = (pcre_uchar *)re + re->name_table_offset; +length = re->name_count * re->name_entry_size; +#if defined SUPPORT_UTF && defined COMPILE_PCRE16 +utf = (re->options & PCRE_UTF16) != 0; +utf16_char = FALSE; +#endif /* SUPPORT_UTF && COMPILE_PCRE16 */ + +while(TRUE) + { + /* Swap previous characters. */ + while (length-- > 0) + { +#if defined COMPILE_PCRE16 + *ptr = swap_uint16(*ptr); +#elif defined COMPILE_PCRE32 + *ptr = swap_uint32(*ptr); +#endif + ptr++; + } +#if defined SUPPORT_UTF && defined COMPILE_PCRE16 + if (utf16_char) + { + if (HAS_EXTRALEN(ptr[-1])) + { + /* We know that there is only one extra character in UTF-16. */ + *ptr = swap_uint16(*ptr); + ptr++; + } + } + utf16_char = FALSE; +#endif /* SUPPORT_UTF */ + + /* Get next opcode. */ + length = 0; +#if defined COMPILE_PCRE16 + *ptr = swap_uint16(*ptr); +#elif defined COMPILE_PCRE32 + *ptr = swap_uint32(*ptr); +#endif + switch (*ptr) + { + case OP_END: + return 0; + +#if defined SUPPORT_UTF && defined COMPILE_PCRE16 + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_UPTO: + case OP_MINUPTO: + case OP_EXACT: + case OP_POSSTAR: + case OP_POSPLUS: + case OP_POSQUERY: + case OP_POSUPTO: + case OP_STARI: + case OP_MINSTARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_QUERYI: + case OP_MINQUERYI: + case OP_UPTOI: + case OP_MINUPTOI: + case OP_EXACTI: + case OP_POSSTARI: + case OP_POSPLUSI: + case OP_POSQUERYI: + case OP_POSUPTOI: + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + case OP_NOTEXACT: + case OP_NOTPOSSTAR: + case OP_NOTPOSPLUS: + case OP_NOTPOSQUERY: + case OP_NOTPOSUPTO: + case OP_NOTSTARI: + case OP_NOTMINSTARI: + case OP_NOTPLUSI: + case OP_NOTMINPLUSI: + case OP_NOTQUERYI: + case OP_NOTMINQUERYI: + case OP_NOTUPTOI: + case OP_NOTMINUPTOI: + case OP_NOTEXACTI: + case OP_NOTPOSSTARI: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERYI: + case OP_NOTPOSUPTOI: + if (utf) utf16_char = TRUE; +#endif + /* Fall through. */ + + default: + length = PRIV(OP_lengths)[*ptr] - 1; + break; + + case OP_CLASS: + case OP_NCLASS: + /* Skip the character bit map. */ + ptr += 32/sizeof(pcre_uchar); + length = 0; + break; + + case OP_XCLASS: + /* Reverse the size of the XCLASS instance. */ + ptr++; +#if defined COMPILE_PCRE16 + *ptr = swap_uint16(*ptr); +#elif defined COMPILE_PCRE32 + *ptr = swap_uint32(*ptr); +#endif +#ifndef COMPILE_PCRE32 + if (LINK_SIZE > 1) + { + /* LINK_SIZE can be 1 or 2 in 16 bit mode. */ + ptr++; + *ptr = swap_uint16(*ptr); + } +#endif + ptr++; + length = (GET(ptr, -LINK_SIZE)) - (1 + LINK_SIZE + 1); +#if defined COMPILE_PCRE16 + *ptr = swap_uint16(*ptr); +#elif defined COMPILE_PCRE32 + *ptr = swap_uint32(*ptr); +#endif + if ((*ptr & XCL_MAP) != 0) + { + /* Skip the character bit map. */ + ptr += 32/sizeof(pcre_uchar); + length -= 32/sizeof(pcre_uchar); + } + break; + } + ptr++; + } +/* Control should never reach here in 16/32 bit mode. */ +#endif /* !COMPILE_PCRE8 */ + +return 0; +} + +/* End of pcre_byte_order.c */ diff --git a/erts/emulator/pcre/pcre_chartables.c b/erts/emulator/pcre/pcre_chartables.c index f851b1b261..0d7ecd5261 100644 --- a/erts/emulator/pcre/pcre_chartables.c +++ b/erts/emulator/pcre/pcre_chartables.c @@ -14,12 +14,11 @@ example ISO-8859-1. When dftables is run, it creates these tables in the current locale. If PCRE is configured with --enable-rebuild-chartables, this happens automatically. -The following #includes are present because without the gcc 4.x may remove the +The following #includes are present because without them gcc 4.x may remove the array definition from the final binary if PCRE is built into a static library and dead code stripping is activated. This leads to link errors. Pulling in the header ensures that the array gets flagged as "someone outside this compilation unit might reference this" and so it will always be supplied to the linker. */ - /* %ExternalCopyright% */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -27,7 +26,7 @@ unit might reference this" and so it will always be supplied to the linker. */ #include "pcre_internal.h" -const unsigned char _erts_pcre_default_tables[] = { +const pcre_uint8 PRIV(default_tables)[] = { /* This table is a lower casing table. */ diff --git a/erts/emulator/pcre/pcre_compile.c b/erts/emulator/pcre/pcre_compile.c index 9508c5a697..5652bad0ce 100644 --- a/erts/emulator/pcre/pcre_compile.c +++ b/erts/emulator/pcre/pcre_compile.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2013 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. */ -/* This module contains the external function erts_pcre_compile(), along with +/* This module contains the external function pcre_compile(), along with supporting internal functions that are not used by other modules. */ /* %ExternalCopyright% */ @@ -54,17 +54,22 @@ supporting internal functions that are not used by other modules. */ #include "pcre_internal.h" -/* When DEBUG is defined, we need the pcre_printint() function, which is also -used by pcretest. DEBUG is not defined when building a production library. */ +/* When PCRE_DEBUG is defined, we need the pcre(16|32)_printint() function, which +is also used by pcretest. PCRE_DEBUG is not defined when building a production +library. We do not need to select pcre16_printint.c specially, because the +COMPILE_PCREx macro will already be appropriately set. */ -#ifdef DEBUG -#include "pcre_printint.src" +#ifdef PCRE_DEBUG +/* pcre_printint.c should not include any headers */ +#define PCRE_INCLUDED +#include "pcre_printint.c" +#undef PCRE_INCLUDED #endif /* Macro for setting individual bits in class bitmaps. */ -#define SETBIT(a,b) a[b/8] |= (1 << (b%8)) +#define SETBIT(a,b) a[(b)/8] |= (1 << ((b)&7)) /* Maximum length value to check against when making sure that the integer that holds the compiled pattern length does not overflow. We make it a bit less than @@ -73,6 +78,18 @@ to check them every time. */ #define OFLOW_MAX (INT_MAX - 20) +/* Definitions to allow mutual recursion */ + +static int + add_list_to_class(pcre_uint8 *, pcre_uchar **, int, compile_data *, + const pcre_uint32 *, unsigned int); + +static BOOL + compile_regex(int, pcre_uchar **, const pcre_uchar **, int *, BOOL, BOOL, int, int, + pcre_uint32 *, pcre_int32 *, pcre_uint32 *, pcre_int32 *, branch_chain *, + compile_data *, int *); + + /************************************************* * Code parameters and static tables * @@ -88,36 +105,89 @@ so this number is very generous. The same workspace is used during the second, actual compile phase for remembering forward references to groups so that they can be filled in at the end. Each entry in this list occupies LINK_SIZE bytes, so even when LINK_SIZE -is 4 there is plenty of room. */ +is 4 there is plenty of room for most patterns. However, the memory can get +filled up by repetitions of forward references, for example patterns like +/(?1){0,1999}(b)/, and one user did hit the limit. The code has been changed so +that the workspace is expanded using malloc() in this situation. The value +below is therefore a minimum, and we put a maximum on it for safety. The +minimum is now also defined in terms of LINK_SIZE so that the use of malloc() +kicks in at the same number of forward references in all cases. */ -#define COMPILE_WORK_SIZE (4096) +#define COMPILE_WORK_SIZE (2048*LINK_SIZE) +#define COMPILE_WORK_SIZE_MAX (100*COMPILE_WORK_SIZE) /* The overrun tests check for a slightly smaller size so that they detect the overrun before it actually does run off the end of the data block. */ -#define WORK_SIZE_CHECK (COMPILE_WORK_SIZE - 100) +#define WORK_SIZE_SAFETY_MARGIN (100) + +/* Private flags added to firstchar and reqchar. */ + +#define REQ_CASELESS (1 << 0) /* Indicates caselessness */ +#define REQ_VARY (1 << 1) /* Reqchar followed non-literal item */ +/* Negative values for the firstchar and reqchar flags */ +#define REQ_UNSET (-2) +#define REQ_NONE (-1) +/* Repeated character flags. */ + +#define UTF_LENGTH 0x10000000l /* The char contains its length. */ /* Table for handling escaped characters in the range '0'-'z'. Positive returns are simple data values; negative values are for special things like \d and so on. Zero means further processing is needed (for things like \x), or the escape is invalid. */ -#ifndef EBCDIC /* This is the "normal" table for ASCII systems */ +#ifndef EBCDIC + +/* This is the "normal" table for ASCII systems or for EBCDIC systems running +in UTF-8 mode. */ + static const short int escapes[] = { - 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ - 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */ - '@', -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E, 0, -ESC_G, /* @ - G */ --ESC_H, 0, 0, -ESC_K, 0, 0, 0, 0, /* H - O */ --ESC_P, -ESC_Q, -ESC_R, -ESC_S, 0, 0, -ESC_V, -ESC_W, /* P - W */ --ESC_X, 0, -ESC_Z, '[', '\\', ']', '^', '_', /* X - _ */ - '`', 7, -ESC_b, 0, -ESC_d, ESC_e, ESC_f, 0, /* ` - g */ --ESC_h, 0, 0, -ESC_k, 0, 0, ESC_n, 0, /* h - o */ --ESC_p, 0, ESC_r, -ESC_s, ESC_tee, 0, -ESC_v, -ESC_w, /* p - w */ - 0, 0, -ESC_z /* x - z */ + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + CHAR_COLON, CHAR_SEMICOLON, + CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN, + CHAR_GREATER_THAN_SIGN, CHAR_QUESTION_MARK, + CHAR_COMMERCIAL_AT, -ESC_A, + -ESC_B, -ESC_C, + -ESC_D, -ESC_E, + 0, -ESC_G, + -ESC_H, 0, + 0, -ESC_K, + 0, 0, + -ESC_N, 0, + -ESC_P, -ESC_Q, + -ESC_R, -ESC_S, + 0, 0, + -ESC_V, -ESC_W, + -ESC_X, 0, + -ESC_Z, CHAR_LEFT_SQUARE_BRACKET, + CHAR_BACKSLASH, CHAR_RIGHT_SQUARE_BRACKET, + CHAR_CIRCUMFLEX_ACCENT, CHAR_UNDERSCORE, + CHAR_GRAVE_ACCENT, 7, + -ESC_b, 0, + -ESC_d, ESC_e, + ESC_f, 0, + -ESC_h, 0, + 0, -ESC_k, + 0, 0, + ESC_n, 0, + -ESC_p, 0, + ESC_r, -ESC_s, + ESC_tee, 0, + -ESC_v, -ESC_w, + 0, 0, + -ESC_z }; -#else /* This is the "abnormal" table for EBCDIC systems */ +#else + +/* This is the "abnormal" table for EBCDIC systems without UTF-8 support. */ + static const short int escapes[] = { /* 48 */ 0, 0, 0, '.', '<', '(', '+', '|', /* 50 */ '&', 0, 0, 0, 0, 0, 0, 0, @@ -136,7 +206,7 @@ static const short int escapes[] = { /* B8 */ 0, 0, 0, 0, 0, ']', '=', '-', /* C0 */ '{',-ESC_A, -ESC_B, -ESC_C, -ESC_D,-ESC_E, 0, -ESC_G, /* C8 */-ESC_H, 0, 0, 0, 0, 0, 0, 0, -/* D0 */ '}', 0, -ESC_K, 0, 0, 0, 0, -ESC_P, +/* D0 */ '}', 0, -ESC_K, 0, 0,-ESC_N, 0, -ESC_P, /* D8 */-ESC_Q,-ESC_R, 0, 0, 0, 0, 0, 0, /* E0 */ '\\', 0, -ESC_S, 0, 0,-ESC_V, -ESC_W, -ESC_X, /* E8 */ 0,-ESC_Z, 0, 0, 0, 0, 0, 0, @@ -148,33 +218,40 @@ static const short int escapes[] = { /* Table of special "verbs" like (*PRUNE). This is a short table, so it is searched linearly. Put all the names into a single string, in order to reduce -the number of relocations when a shared library is dynamically linked. */ +the number of relocations when a shared library is dynamically linked. The +string is built from string macros so that it works in UTF-8 mode on EBCDIC +platforms. */ typedef struct verbitem { - int len; - int op; + int len; /* Length of verb name */ + int op; /* Op when no arg, or -1 if arg mandatory */ + int op_arg; /* Op when arg present, or -1 if not allowed */ } verbitem; static const char verbnames[] = - "ACCEPT\0" - "COMMIT\0" - "F\0" - "FAIL\0" - "PRUNE\0" - "SKIP\0" - "THEN"; - -static verbitem verbs[] = { - { 6, OP_ACCEPT }, - { 6, OP_COMMIT }, - { 1, OP_FAIL }, - { 4, OP_FAIL }, - { 5, OP_PRUNE }, - { 4, OP_SKIP }, - { 4, OP_THEN } + "\0" /* Empty name is a shorthand for MARK */ + STRING_MARK0 + STRING_ACCEPT0 + STRING_COMMIT0 + STRING_F0 + STRING_FAIL0 + STRING_PRUNE0 + STRING_SKIP0 + STRING_THEN; + +static const verbitem verbs[] = { + { 0, -1, OP_MARK }, + { 4, -1, OP_MARK }, + { 6, OP_ACCEPT, -1 }, + { 6, OP_COMMIT, -1 }, + { 1, OP_FAIL, -1 }, + { 4, OP_FAIL, -1 }, + { 5, OP_PRUNE, OP_PRUNE_ARG }, + { 4, OP_SKIP, OP_SKIP_ARG }, + { 4, OP_THEN, OP_THEN_ARG } }; -static int verbcount = sizeof(verbs)/sizeof(verbitem); +static const int verbcount = sizeof(verbs)/sizeof(verbitem); /* Tables of names of POSIX character classes and their lengths. The names are @@ -184,11 +261,12 @@ length entry. The first three must be alpha, lower, upper, as this is assumed for handling case independence. */ static const char posix_names[] = - "alpha\0" "lower\0" "upper\0" "alnum\0" "ascii\0" "blank\0" - "cntrl\0" "digit\0" "graph\0" "print\0" "punct\0" "space\0" - "word\0" "xdigit"; + STRING_alpha0 STRING_lower0 STRING_upper0 STRING_alnum0 + STRING_ascii0 STRING_blank0 STRING_cntrl0 STRING_digit0 + STRING_graph0 STRING_print0 STRING_punct0 STRING_space0 + STRING_word0 STRING_xdigit; -static const uschar posix_name_lengths[] = { +static const pcre_uint8 posix_name_lengths[] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 }; /* Table of class bit maps for each POSIX class. Each class is formed from a @@ -218,6 +296,107 @@ static const int posix_class_maps[] = { cbit_xdigit,-1, 0 /* xdigit */ }; +/* Table of substitutes for \d etc when PCRE_UCP is set. The POSIX class +substitutes must be in the order of the names, defined above, and there are +both positive and negative cases. NULL means no substitute. */ + +#ifdef SUPPORT_UCP +static const pcre_uchar string_PNd[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_N, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pNd[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_N, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PXsp[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_s, CHAR_p, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pXsp[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_s, CHAR_p, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PXwd[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_w, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pXwd[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_w, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; + +static const pcre_uchar *substitutes[] = { + string_PNd, /* \D */ + string_pNd, /* \d */ + string_PXsp, /* \S */ /* NOTE: Xsp is Perl space */ + string_pXsp, /* \s */ + string_PXwd, /* \W */ + string_pXwd /* \w */ +}; + +static const pcre_uchar string_pL[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pLl[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_l, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pLu[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_u, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pXan[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_a, CHAR_n, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_h[] = { + CHAR_BACKSLASH, CHAR_h, '\0' }; +static const pcre_uchar string_pXps[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_p, CHAR_s, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PL[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PLl[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_l, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PLu[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_u, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PXan[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_a, CHAR_n, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_H[] = { + CHAR_BACKSLASH, CHAR_H, '\0' }; +static const pcre_uchar string_PXps[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_p, CHAR_s, CHAR_RIGHT_CURLY_BRACKET, '\0' }; + +static const pcre_uchar *posix_substitutes[] = { + string_pL, /* alpha */ + string_pLl, /* lower */ + string_pLu, /* upper */ + string_pXan, /* alnum */ + NULL, /* ascii */ + string_h, /* blank */ + NULL, /* cntrl */ + string_pNd, /* digit */ + NULL, /* graph */ + NULL, /* print */ + NULL, /* punct */ + string_pXps, /* space */ /* NOTE: Xps is POSIX space */ + string_pXwd, /* word */ + NULL, /* xdigit */ + /* Negated cases */ + string_PL, /* ^alpha */ + string_PLl, /* ^lower */ + string_PLu, /* ^upper */ + string_PXan, /* ^alnum */ + NULL, /* ^ascii */ + string_H, /* ^blank */ + NULL, /* ^cntrl */ + string_PNd, /* ^digit */ + NULL, /* ^graph */ + NULL, /* ^print */ + NULL, /* ^punct */ + string_PXps, /* ^space */ /* NOTE: Xps is POSIX space */ + string_PXwd, /* ^word */ + NULL /* ^xdigit */ +}; +#define POSIX_SUBSIZE (sizeof(posix_substitutes) / sizeof(pcre_uchar *)) +#endif #define STRING(a) # a #define XSTRING(s) STRING(s) @@ -230,7 +409,11 @@ the number of relocations needed when a shared library is loaded dynamically, it is now one long string. We cannot use a table of offsets, because the lengths of inserts such as XSTRING(MAX_NAME_SIZE) are not known. Instead, we simply count through to the one we want - this isn't a performance issue -because these strings are used only when there is a compilation error. */ +because these strings are used only when there is a compilation error. + +Each substring ends with \0 to insert a null character. This includes the final +substring, so that the whole string ends with \0\0, which can be detected when +counting through. */ static const char error_texts[] = "no error\0" @@ -271,13 +454,13 @@ static const char error_texts[] = /* 30 */ "unknown POSIX class name\0" "POSIX collating elements are not supported\0" - "this version of PCRE is not compiled with PCRE_UTF8 support\0" + "this version of PCRE is compiled without UTF support\0" "spare error\0" /** DEAD **/ "character value in \\x{...} sequence is too large\0" /* 35 */ "invalid condition (?(0)\0" "\\C not allowed in lookbehind assertion\0" - "PCRE does not support \\L, \\l, \\N, \\U, or \\u\0" + "PCRE does not support \\L, \\l, \\N{name}, \\U, or \\u\0" "number after (?C is > 255\0" "closing ) for (?C expected\0" /* 40 */ @@ -294,22 +477,40 @@ static const char error_texts[] = "too many named subpatterns (maximum " XSTRING(MAX_NAME_COUNT) ")\0" /* 50 */ "repeated subpattern is too long\0" /** DEAD **/ - "octal value is greater than \\377 (not in UTF-8 mode)\0" + "octal value is greater than \\377 in 8-bit non-UTF-8 mode\0" "internal error: overran compiling workspace\0" "internal error: previously-checked referenced subpattern not found\0" "DEFINE group contains more than one branch\0" /* 55 */ - "repeating a DEFINE group is not allowed\0" + "repeating a DEFINE group is not allowed\0" /** DEAD **/ "inconsistent NEWLINE options\0" - "\\g is not followed by a braced name or an optionally braced non-zero number\0" - "(?+ or (?- or (?(+ or (?(- must be followed by a non-zero number\0" - "(*VERB) with an argument is not supported\0" + "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number\0" + "a numbered reference must not be zero\0" + "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)\0" /* 60 */ - "(*VERB) not recognized\0" + "(*VERB) not recognized or malformed\0" "number is too big\0" "subpattern name expected\0" - "digit expected after (?+"; - + "digit expected after (?+\0" + "] is an invalid data character in JavaScript compatibility mode\0" + /* 65 */ + "different names for subpatterns of the same number are not allowed\0" + "(*MARK) must have an argument\0" + "this version of PCRE is not compiled with Unicode property support\0" + "\\c must be followed by an ASCII character\0" + "\\k is not followed by a braced, angle-bracketed, or quoted name\0" + /* 70 */ + "internal error: unknown opcode in find_fixedlength()\0" + "\\N is not supported in a class\0" + "too many forward references\0" + "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)\0" + "invalid UTF-16 string\0" + /* 75 */ + "name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)\0" + "character value in \\u.... sequence is too large\0" + "invalid UTF-32 string\0" + "setting UTF is disabled by the application\0" + ; /* Table to identify digits and hex digits. This is used when compiling patterns. Note that the tables in chartables are dependent on the locale, and @@ -327,8 +528,18 @@ For convenience, we use the same bit definitions as in chartables: Then we can use ctype_digit and ctype_xdigit in the code. */ -#ifndef EBCDIC /* This is the "normal" case, for ASCII systems */ -static const unsigned char digitab[] = +/* Using a simple comparison for decimal numbers rather than a memory read +is much faster, and the resulting code is simpler (the compiler turns it +into a subtraction and unsigned comparison). */ + +#define IS_DIGIT(x) ((x) >= CHAR_0 && (x) <= CHAR_9) + +#ifndef EBCDIC + +/* This is the "normal" case, for ASCII systems, and EBCDIC systems running in +UTF-8 mode. */ + +static const pcre_uint8 digitab[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 8- 15 */ @@ -363,8 +574,11 @@ static const unsigned char digitab[] = 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ -#else /* This is the "abnormal" case, for EBCDIC systems */ -static const unsigned char digitab[] = +#else + +/* This is the "abnormal" case, for EBCDIC systems not running in UTF-8 mode. */ + +static const pcre_uint8 digitab[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 0 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 8- 15 */ @@ -399,7 +613,7 @@ static const unsigned char digitab[] = 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, /* 0 - 7 F0 */ 0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00};/* 8 -255 */ -static const unsigned char ebcdic_chartab[] = { /* chartable partial dup */ +static const pcre_uint8 ebcdic_chartab[] = { /* chartable partial dup */ 0x80,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /* 0- 7 */ 0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00, /* 8- 15 */ 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /* 16- 23 */ @@ -435,13 +649,6 @@ static const unsigned char ebcdic_chartab[] = { /* chartable partial dup */ #endif -/* Definition to allow mutual recursion */ - -static BOOL - compile_regex(int, int, uschar **, const uschar **, int *, BOOL, BOOL, int, - int *, int *, branch_chain *, compile_data *, int *); - - /************************************************* * Find an error text * @@ -460,98 +667,245 @@ static const char * find_error_text(int n) { const char *s = error_texts; -for (; n > 0; n--) while (*s++ != 0); +for (; n > 0; n--) + { + while (*s++ != CHAR_NULL) {}; + if (*s == CHAR_NULL) return "Error text not found (please report)"; + } return s; } +/************************************************* +* Expand the workspace * +*************************************************/ + +/* This function is called during the second compiling phase, if the number of +forward references fills the existing workspace, which is originally a block on +the stack. A larger block is obtained from malloc() unless the ultimate limit +has been reached or the increase will be rather small. + +Argument: pointer to the compile data block +Returns: 0 if all went well, else an error number +*/ + +static int +expand_workspace(compile_data *cd) +{ +pcre_uchar *newspace; +int newsize = cd->workspace_size * 2; + +if (newsize > COMPILE_WORK_SIZE_MAX) newsize = COMPILE_WORK_SIZE_MAX; +if (cd->workspace_size >= COMPILE_WORK_SIZE_MAX || + newsize - cd->workspace_size < WORK_SIZE_SAFETY_MARGIN) + return ERR72; + +newspace = (PUBL(malloc))(IN_UCHARS(newsize)); +if (newspace == NULL) return ERR21; +memcpy(newspace, cd->start_workspace, cd->workspace_size * sizeof(pcre_uchar)); +cd->hwm = (pcre_uchar *)newspace + (cd->hwm - cd->start_workspace); +if (cd->workspace_size > COMPILE_WORK_SIZE) + (PUBL(free))((void *)cd->start_workspace); +cd->start_workspace = newspace; +cd->workspace_size = newsize; +return 0; +} + + + +/************************************************* +* Check for counted repeat * +*************************************************/ + +/* This function is called when a '{' is encountered in a place where it might +start a quantifier. It looks ahead to see if it really is a quantifier or not. +It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd} +where the ddds are digits. + +Arguments: + p pointer to the first char after '{' + +Returns: TRUE or FALSE +*/ + +static BOOL +is_counted_repeat(const pcre_uchar *p) +{ +if (!IS_DIGIT(*p)) return FALSE; +p++; +while (IS_DIGIT(*p)) p++; +if (*p == CHAR_RIGHT_CURLY_BRACKET) return TRUE; + +if (*p++ != CHAR_COMMA) return FALSE; +if (*p == CHAR_RIGHT_CURLY_BRACKET) return TRUE; + +if (!IS_DIGIT(*p)) return FALSE; +p++; +while (IS_DIGIT(*p)) p++; + +return (*p == CHAR_RIGHT_CURLY_BRACKET); +} + + + /************************************************* * Handle escapes * *************************************************/ /* This function is called when a \ has been encountered. It either returns a -positive value for a simple escape such as \n, or a negative value which -encodes one of the more complicated things such as \d. A backreference to group -n is returned as -(ESC_REF + n); ESC_REF is the highest ESC_xxx macro. When -UTF-8 is enabled, a positive value greater than 255 may be returned. On entry, -ptr is pointing at the \. On exit, it is on the final character of the escape -sequence. +positive value for a simple escape such as \n, or 0 for a data character +which will be placed in chptr. A backreference to group n is returned as +negative n. When UTF-8 is enabled, a positive value greater than 255 may +be returned in chptr. +On entry,ptr is pointing at the \. On exit, it is on the final character of the +escape sequence. Arguments: ptrptr points to the pattern position pointer + chptr points to the data character errorcodeptr points to the errorcode variable bracount number of previous extracting brackets options the options bits isclass TRUE if inside a character class -Returns: zero or positive => a data character - negative => a special escape sequence +Returns: zero => a data character + positive => a special escape sequence + negative => a back reference on error, errorcodeptr is set */ static int -check_escape(const uschar **ptrptr, int *errorcodeptr, int bracount, - int options, BOOL isclass) +check_escape(const pcre_uchar **ptrptr, pcre_uint32 *chptr, int *errorcodeptr, + int bracount, int options, BOOL isclass) { -BOOL utf8 = (options & PCRE_UTF8) != 0; -const uschar *ptr = *ptrptr + 1; -int c, i; +/* PCRE_UTF16 has the same value as PCRE_UTF8. */ +BOOL utf = (options & PCRE_UTF8) != 0; +const pcre_uchar *ptr = *ptrptr + 1; +pcre_uint32 c; +int escape = 0; +int i; GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ ptr--; /* Set pointer back to the last byte */ /* If backslash is at the end of the pattern, it's an error. */ -if (c == 0) *errorcodeptr = ERR1; + +if (c == CHAR_NULL) *errorcodeptr = ERR1; /* Non-alphanumerics are literals. For digits or letters, do an initial lookup in a table. A non-zero result is something that can be returned immediately. Otherwise further processing may be required. */ -#ifndef EBCDIC /* ASCII coding */ -else if (c < '0' || c > 'z') {} /* Not alphanumeric */ -else if ((i = escapes[c - '0']) != 0) c = i; +#ifndef EBCDIC /* ASCII/UTF-8 coding */ +/* Not alphanumeric */ +else if (c < CHAR_0 || c > CHAR_z) {} +else if ((i = escapes[c - CHAR_0]) != 0) + { if (i > 0) c = (pcre_uint32)i; else escape = -i; } #else /* EBCDIC coding */ -else if (c < 'a' || (ebcdic_chartab[c] & 0x0E) == 0) {} /* Not alphanumeric */ -else if ((i = escapes[c - 0x48]) != 0) c = i; +/* Not alphanumeric */ +else if (c < CHAR_a || (!MAX_255(c) || (ebcdic_chartab[c] & 0x0E) == 0)) {} +else if ((i = escapes[c - 0x48]) != 0) { if (i > 0) c = (pcre_uint32)i; else escape = -i; } #endif /* Escapes that need further processing, or are illegal. */ else { - const uschar *oldptr; - BOOL braced, negated; + const pcre_uchar *oldptr; + BOOL braced, negated, overflow; + int s; switch (c) { /* A number of Perl escapes are not handled by PCRE. We give an explicit error. */ - case 'l': - case 'L': - case 'N': - case 'u': - case 'U': + case CHAR_l: + case CHAR_L: *errorcodeptr = ERR37; break; - /* \g must be followed by a number, either plain or braced. If positive, it - is an absolute backreference. If negative, it is a relative backreference. - This is a Perl 5.10 feature. Perl 5.10 also supports \g{name} as a - reference to a named group. This is part of Perl's movement towards a - unified syntax for back references. As this is synonymous with \k{name}, we - fudge it up by pretending it really was \k. */ + case CHAR_u: + if ((options & PCRE_JAVASCRIPT_COMPAT) != 0) + { + /* In JavaScript, \u must be followed by four hexadecimal numbers. + Otherwise it is a lowercase u letter. */ + if (MAX_255(ptr[1]) && (digitab[ptr[1]] & ctype_xdigit) != 0 + && MAX_255(ptr[2]) && (digitab[ptr[2]] & ctype_xdigit) != 0 + && MAX_255(ptr[3]) && (digitab[ptr[3]] & ctype_xdigit) != 0 + && MAX_255(ptr[4]) && (digitab[ptr[4]] & ctype_xdigit) != 0) + { + c = 0; + for (i = 0; i < 4; ++i) + { + register pcre_uint32 cc = *(++ptr); +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (cc >= CHAR_a) cc -= 32; /* Convert to upper case */ + c = (c << 4) + cc - ((cc < CHAR_A)? CHAR_0 : (CHAR_A - 10)); +#else /* EBCDIC coding */ + if (cc >= CHAR_a && cc <= CHAR_z) cc += 64; /* Convert to upper case */ + c = (c << 4) + cc - ((cc >= CHAR_0)? CHAR_0 : (CHAR_A - 10)); +#endif + } + +#if defined COMPILE_PCRE8 + if (c > (utf ? 0x10ffffU : 0xffU)) +#elif defined COMPILE_PCRE16 + if (c > (utf ? 0x10ffffU : 0xffffU)) +#elif defined COMPILE_PCRE32 + if (utf && c > 0x10ffffU) +#endif + { + *errorcodeptr = ERR76; + } + else if (utf && c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; + } + } + else + *errorcodeptr = ERR37; + break; + + case CHAR_U: + /* In JavaScript, \U is an uppercase U letter. */ + if ((options & PCRE_JAVASCRIPT_COMPAT) == 0) *errorcodeptr = ERR37; + break; + + /* In a character class, \g is just a literal "g". Outside a character + class, \g must be followed by one of a number of specific things: + + (1) A number, either plain or braced. If positive, it is an absolute + backreference. If negative, it is a relative backreference. This is a Perl + 5.10 feature. + + (2) Perl 5.10 also supports \g{name} as a reference to a named group. This + is part of Perl's movement towards a unified syntax for back references. As + this is synonymous with \k{name}, we fudge it up by pretending it really + was \k. + + (3) For Oniguruma compatibility we also support \g followed by a name or a + number either in angle brackets or in single quotes. However, these are + (possibly recursive) subroutine calls, _not_ backreferences. Just return + the ESC_g code (cf \k). */ + + case CHAR_g: + if (isclass) break; + if (ptr[1] == CHAR_LESS_THAN_SIGN || ptr[1] == CHAR_APOSTROPHE) + { + escape = ESC_g; + break; + } + + /* Handle the Perl-compatible cases */ - case 'g': - if (ptr[1] == '{') + if (ptr[1] == CHAR_LEFT_CURLY_BRACKET) { - const uschar *p; - for (p = ptr+2; *p != 0 && *p != '}'; p++) - if (*p != '-' && (digitab[*p] & ctype_digit) == 0) break; - if (*p != 0 && *p != '}') + const pcre_uchar *p; + for (p = ptr+2; *p != CHAR_NULL && *p != CHAR_RIGHT_CURLY_BRACKET; p++) + if (*p != CHAR_MINUS && !IS_DIGIT(*p)) break; + if (*p != CHAR_NULL && *p != CHAR_RIGHT_CURLY_BRACKET) { - c = -ESC_k; + escape = ESC_k; break; } braced = TRUE; @@ -559,40 +913,56 @@ else } else braced = FALSE; - if (ptr[1] == '-') + if (ptr[1] == CHAR_MINUS) { negated = TRUE; ptr++; } else negated = FALSE; - c = 0; - while ((digitab[ptr[1]] & ctype_digit) != 0) - c = c * 10 + *(++ptr) - '0'; - - if (c < 0) + /* The integer range is limited by the machine's int representation. */ + s = 0; + overflow = FALSE; + while (IS_DIGIT(ptr[1])) + { + if (s > INT_MAX / 10 - 1) /* Integer overflow */ + { + overflow = TRUE; + break; + } + s = s * 10 + (int)(*(++ptr) - CHAR_0); + } + if (overflow) /* Integer overflow */ { + while (IS_DIGIT(ptr[1])) + ptr++; *errorcodeptr = ERR61; break; } - if (c == 0 || (braced && *(++ptr) != '}')) + if (braced && *(++ptr) != CHAR_RIGHT_CURLY_BRACKET) { *errorcodeptr = ERR57; break; } + if (s == 0) + { + *errorcodeptr = ERR58; + break; + } + if (negated) { - if (c > bracount) + if (s > bracount) { *errorcodeptr = ERR15; break; } - c = bracount - (c - 1); + s = bracount - (s - 1); } - c = -(ESC_REF + c); + escape = -s; break; /* The handling of escape sequences consisting of a string of digits @@ -607,23 +977,34 @@ else value is greater than 377, the least significant 8 bits are taken. Inside a character class, \ followed by a digit is always an octal number. */ - case '1': case '2': case '3': case '4': case '5': - case '6': case '7': case '8': case '9': + case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: case CHAR_5: + case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9: if (!isclass) { oldptr = ptr; - c -= '0'; - while ((digitab[ptr[1]] & ctype_digit) != 0) - c = c * 10 + *(++ptr) - '0'; - if (c < 0) + /* The integer range is limited by the machine's int representation. */ + s = (int)(c -CHAR_0); + overflow = FALSE; + while (IS_DIGIT(ptr[1])) + { + if (s > INT_MAX / 10 - 1) /* Integer overflow */ + { + overflow = TRUE; + break; + } + s = s * 10 + (int)(*(++ptr) - CHAR_0); + } + if (overflow) /* Integer overflow */ { + while (IS_DIGIT(ptr[1])) + ptr++; *errorcodeptr = ERR61; break; } - if (c < 10 || c <= bracount) + if (s < 10 || s <= bracount) { - c = -(ESC_REF + c); + escape = -s; break; } ptr = oldptr; /* Put the pointer back and fall through */ @@ -633,7 +1014,7 @@ else generates a binary zero byte and treats the digit as a following literal. Thus we have to pull back the pointer by one. */ - if ((c = *ptr) >= '8') + if ((c = *ptr) >= CHAR_8) { ptr--; c = 0; @@ -643,45 +1024,87 @@ else /* \0 always starts an octal number, but we may drop through to here with a larger first octal digit. The original code used just to take the least significant 8 bits of octal numbers (I think this is what early Perls used - to do). Nowadays we allow for larger numbers in UTF-8 mode, but no more - than 3 octal digits. */ - - case '0': - c -= '0'; - while(i++ < 2 && ptr[1] >= '0' && ptr[1] <= '7') - c = c * 8 + *(++ptr) - '0'; - if (!utf8 && c > 255) *errorcodeptr = ERR51; + to do). Nowadays we allow for larger numbers in UTF-8 mode and 16-bit mode, + but no more than 3 octal digits. */ + + case CHAR_0: + c -= CHAR_0; + while(i++ < 2 && ptr[1] >= CHAR_0 && ptr[1] <= CHAR_7) + c = c * 8 + *(++ptr) - CHAR_0; +#ifdef COMPILE_PCRE8 + if (!utf && c > 0xff) *errorcodeptr = ERR51; +#endif break; /* \x is complicated. \x{ddd} is a character number which can be greater - than 0xff in utf8 mode, but only if the ddd are hex digits. If not, { is - treated as a data character. */ + than 0xff in utf or non-8bit mode, but only if the ddd are hex digits. + If not, { is treated as a data character. */ + + case CHAR_x: + if ((options & PCRE_JAVASCRIPT_COMPAT) != 0) + { + /* In JavaScript, \x must be followed by two hexadecimal numbers. + Otherwise it is a lowercase x letter. */ + if (MAX_255(ptr[1]) && (digitab[ptr[1]] & ctype_xdigit) != 0 + && MAX_255(ptr[2]) && (digitab[ptr[2]] & ctype_xdigit) != 0) + { + c = 0; + for (i = 0; i < 2; ++i) + { + register pcre_uint32 cc = *(++ptr); +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (cc >= CHAR_a) cc -= 32; /* Convert to upper case */ + c = (c << 4) + cc - ((cc < CHAR_A)? CHAR_0 : (CHAR_A - 10)); +#else /* EBCDIC coding */ + if (cc >= CHAR_a && cc <= CHAR_z) cc += 64; /* Convert to upper case */ + c = (c << 4) + cc - ((cc >= CHAR_0)? CHAR_0 : (CHAR_A - 10)); +#endif + } + } + break; + } - case 'x': - if (ptr[1] == '{') + if (ptr[1] == CHAR_LEFT_CURLY_BRACKET) { - const uschar *pt = ptr + 2; - int count = 0; + const pcre_uchar *pt = ptr + 2; c = 0; - while ((digitab[*pt] & ctype_xdigit) != 0) + overflow = FALSE; + while (MAX_255(*pt) && (digitab[*pt] & ctype_xdigit) != 0) { - register int cc = *pt++; - if (c == 0 && cc == '0') continue; /* Leading zeroes */ - count++; + register pcre_uint32 cc = *pt++; + if (c == 0 && cc == CHAR_0) continue; /* Leading zeroes */ + +#ifdef COMPILE_PCRE32 + if (c >= 0x10000000l) { overflow = TRUE; break; } +#endif -#ifndef EBCDIC /* ASCII coding */ - if (cc >= 'a') cc -= 32; /* Convert to upper case */ - c = (c << 4) + cc - ((cc < 'A')? '0' : ('A' - 10)); +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (cc >= CHAR_a) cc -= 32; /* Convert to upper case */ + c = (c << 4) + cc - ((cc < CHAR_A)? CHAR_0 : (CHAR_A - 10)); #else /* EBCDIC coding */ - if (cc >= 'a' && cc <= 'z') cc += 64; /* Convert to upper case */ - c = (c << 4) + cc - ((cc >= '0')? '0' : ('A' - 10)); + if (cc >= CHAR_a && cc <= CHAR_z) cc += 64; /* Convert to upper case */ + c = (c << 4) + cc - ((cc >= CHAR_0)? CHAR_0 : (CHAR_A - 10)); +#endif + +#if defined COMPILE_PCRE8 + if (c > (utf ? 0x10ffffU : 0xffU)) { overflow = TRUE; break; } +#elif defined COMPILE_PCRE16 + if (c > (utf ? 0x10ffffU : 0xffffU)) { overflow = TRUE; break; } +#elif defined COMPILE_PCRE32 + if (utf && c > 0x10ffffU) { overflow = TRUE; break; } #endif } - if (*pt == '}') + if (overflow) + { + while (MAX_255(*pt) && (digitab[*pt] & ctype_xdigit) != 0) pt++; + *errorcodeptr = ERR34; + } + + if (*pt == CHAR_RIGHT_CURLY_BRACKET) { - if (c < 0 || count > (utf8? 8 : 2)) *errorcodeptr = ERR34; + if (utf && c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; ptr = pt; break; } @@ -693,37 +1116,42 @@ else /* Read just a single-byte hex-defined char */ c = 0; - while (i++ < 2 && (digitab[ptr[1]] & ctype_xdigit) != 0) + while (i++ < 2 && MAX_255(ptr[1]) && (digitab[ptr[1]] & ctype_xdigit) != 0) { - int cc; /* Some compilers don't like ++ */ - cc = *(++ptr); /* in initializers */ -#ifndef EBCDIC /* ASCII coding */ - if (cc >= 'a') cc -= 32; /* Convert to upper case */ - c = c * 16 + cc - ((cc < 'A')? '0' : ('A' - 10)); + pcre_uint32 cc; /* Some compilers don't like */ + cc = *(++ptr); /* ++ in initializers */ +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (cc >= CHAR_a) cc -= 32; /* Convert to upper case */ + c = c * 16 + cc - ((cc < CHAR_A)? CHAR_0 : (CHAR_A - 10)); #else /* EBCDIC coding */ - if (cc <= 'z') cc += 64; /* Convert to upper case */ - c = c * 16 + cc - ((cc >= '0')? '0' : ('A' - 10)); + if (cc <= CHAR_z) cc += 64; /* Convert to upper case */ + c = c * 16 + cc - ((cc >= CHAR_0)? CHAR_0 : (CHAR_A - 10)); #endif } break; /* For \c, a following letter is upper-cased; then the 0x40 bit is flipped. - This coding is ASCII-specific, but then the whole concept of \cx is + An error is given if the byte following \c is not an ASCII character. This + coding is ASCII-specific, but then the whole concept of \cx is ASCII-specific. (However, an EBCDIC equivalent has now been added.) */ - case 'c': + case CHAR_c: c = *(++ptr); - if (c == 0) + if (c == CHAR_NULL) { *errorcodeptr = ERR2; break; } - -#ifndef EBCDIC /* ASCII coding */ - if (c >= 'a' && c <= 'z') c -= 32; +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (c > 127) /* Excludes all non-ASCII in either mode */ + { + *errorcodeptr = ERR68; + break; + } + if (c >= CHAR_a && c <= CHAR_z) c -= 32; c ^= 0x40; -#else /* EBCDIC coding */ - if (c >= 'a' && c <= 'z') c += 64; +#else /* EBCDIC coding */ + if (c >= CHAR_a && c <= CHAR_z) c += 64; c ^= 0xC0; #endif break; @@ -745,11 +1173,25 @@ else } } -*ptrptr = ptr; -return c; -} +/* Perl supports \N{name} for character names, as well as plain \N for "not +newline". PCRE does not support \N{name}. However, it does support +quantification such as \N{2,3}. */ + +if (escape == ESC_N && ptr[1] == CHAR_LEFT_CURLY_BRACKET && + !is_counted_repeat(ptr+2)) + *errorcodeptr = ERR37; + +/* If PCRE_UCP is set, we change the values for \d etc. */ +if ((options & PCRE_UCP) != 0 && escape >= ESC_D && escape <= ESC_w) + escape += (ESC_DU - ESC_D); +/* Set the pointer to the final character before returning. */ + +*ptrptr = ptr; +*chptr = c; +return escape; +} #ifdef SUPPORT_UCP /************************************************* @@ -764,42 +1206,45 @@ escape sequence. Argument: ptrptr points to the pattern position pointer negptr points to a boolean that is set TRUE for negation else FALSE - dptr points to an int that is set to the detailed property value + ptypeptr points to an unsigned int that is set to the type value + pdataptr points to an unsigned int that is set to the detailed property value errorcodeptr points to the error code variable -Returns: type value from ucp_type_table, or -1 for an invalid type +Returns: TRUE if the type value was found, or FALSE for an invalid type */ -static int -get_ucp(const uschar **ptrptr, BOOL *negptr, int *dptr, int *errorcodeptr) +static BOOL +get_ucp(const pcre_uchar **ptrptr, BOOL *negptr, unsigned int *ptypeptr, + unsigned int *pdataptr, int *errorcodeptr) { -int c, i, bot, top; -const uschar *ptr = *ptrptr; -char name[32]; +pcre_uchar c; +int i, bot, top; +const pcre_uchar *ptr = *ptrptr; +pcre_uchar name[32]; c = *(++ptr); -if (c == 0) goto ERROR_RETURN; +if (c == CHAR_NULL) goto ERROR_RETURN; *negptr = FALSE; /* \P or \p can be followed by a name in {}, optionally preceded by ^ for negation. */ -if (c == '{') +if (c == CHAR_LEFT_CURLY_BRACKET) { - if (ptr[1] == '^') + if (ptr[1] == CHAR_CIRCUMFLEX_ACCENT) { *negptr = TRUE; ptr++; } - for (i = 0; i < (int)sizeof(name) - 1; i++) + for (i = 0; i < (int)(sizeof(name) / sizeof(pcre_uchar)) - 1; i++) { c = *(++ptr); - if (c == 0) goto ERROR_RETURN; - if (c == '}') break; + if (c == CHAR_NULL) goto ERROR_RETURN; + if (c == CHAR_RIGHT_CURLY_BRACKET) break; name[i] = c; } - if (c !='}') goto ERROR_RETURN; + if (c != CHAR_RIGHT_CURLY_BRACKET) goto ERROR_RETURN; name[i] = 0; } @@ -816,67 +1261,36 @@ else /* Search for a recognized property name using binary chop */ bot = 0; -top = _erts_pcre_utt_size; +top = PRIV(utt_size); while (bot < top) { + int r; i = (bot + top) >> 1; - c = strcmp(name, _erts_pcre_utt_names + _erts_pcre_utt[i].name_offset); - if (c == 0) + r = STRCMP_UC_C8(name, PRIV(utt_names) + PRIV(utt)[i].name_offset); + if (r == 0) { - *dptr = _erts_pcre_utt[i].value; - return _erts_pcre_utt[i].type; + *ptypeptr = PRIV(utt)[i].type; + *pdataptr = PRIV(utt)[i].value; + return TRUE; } - if (c > 0) bot = i + 1; else top = i; + if (r > 0) bot = i + 1; else top = i; } *errorcodeptr = ERR47; *ptrptr = ptr; -return -1; +return FALSE; ERROR_RETURN: *errorcodeptr = ERR46; *ptrptr = ptr; -return -1; +return FALSE; } #endif -/************************************************* -* Check for counted repeat * -*************************************************/ - -/* This function is called when a '{' is encountered in a place where it might -start a quantifier. It looks ahead to see if it really is a quantifier or not. -It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd} -where the ddds are digits. - -Arguments: - p pointer to the first char after '{' - -Returns: TRUE or FALSE -*/ - -static BOOL -is_counted_repeat(const uschar *p) -{ -if ((digitab[*p++] & ctype_digit) == 0) return FALSE; -while ((digitab[*p] & ctype_digit) != 0) p++; -if (*p == '}') return TRUE; - -if (*p++ != ',') return FALSE; -if (*p == '}') return TRUE; - -if ((digitab[*p++] & ctype_digit) == 0) return FALSE; -while ((digitab[*p] & ctype_digit) != 0) p++; - -return (*p == '}'); -} - - - /************************************************* * Read repeat counts * *************************************************/ @@ -896,8 +1310,8 @@ Returns: pointer to '}' on success; current ptr on error, with errorcodeptr set non-zero */ -static const uschar * -read_repeat_counts(const uschar *p, int *minp, int *maxp, int *errorcodeptr) +static const pcre_uchar * +read_repeat_counts(const pcre_uchar *p, int *minp, int *maxp, int *errorcodeptr) { int min = 0; int max = -1; @@ -905,7 +1319,7 @@ int max = -1; /* Read the minimum value and do a paranoid check: a negative value indicates an integer overflow. */ -while ((digitab[*p] & ctype_digit) != 0) min = min * 10 + *p++ - '0'; +while (IS_DIGIT(*p)) min = min * 10 + (int)(*p++ - CHAR_0); if (min < 0 || min > 65535) { *errorcodeptr = ERR5; @@ -915,12 +1329,12 @@ if (min < 0 || min > 65535) /* Read the maximum value if there is one, and again do a paranoid on its size. Also, max must not be less than min. */ -if (*p == '}') max = min; else +if (*p == CHAR_RIGHT_CURLY_BRACKET) max = min; else { - if (*(++p) != '}') + if (*(++p) != CHAR_RIGHT_CURLY_BRACKET) { max = 0; - while((digitab[*p] & ctype_digit) != 0) max = max * 10 + *p++ - '0'; + while(IS_DIGIT(*p)) max = max * 10 + (int)(*p++ - CHAR_0); if (max < 0 || max > 65535) { *errorcodeptr = ERR5; @@ -945,65 +1359,201 @@ return p; /************************************************* -* Find forward referenced subpattern * +* Subroutine for finding forward reference * *************************************************/ -/* This function scans along a pattern's text looking for capturing +/* This recursive function is called only from find_parens() below. The +top-level call starts at the beginning of the pattern. All other calls must +start at a parenthesis. It scans along a pattern's text looking for capturing subpatterns, and counting them. If it finds a named pattern that matches the name it is given, it returns its number. Alternatively, if the name is NULL, it -returns when it reaches a given numbered subpattern. This is used for forward -references to subpatterns. We know that if (?P< is encountered, the name will -be terminated by '>' because that is checked in the first pass. +returns when it reaches a given numbered subpattern. Recursion is used to keep +track of subpatterns that reset the capturing group numbers - the (?| feature. + +This function was originally called only from the second pass, in which we know +that if (?< or (?' or (?P< is encountered, the name will be correctly +terminated because that is checked in the first pass. There is now one call to +this function in the first pass, to check for a recursive back reference by +name (so that we can make the whole group atomic). In this case, we need check +only up to the current position in the pattern, and that is still OK because +and previous occurrences will have been checked. To make this work, the test +for "end of pattern" is a check against cd->end_pattern in the main loop, +instead of looking for a binary zero. This means that the special first-pass +call can adjust cd->end_pattern temporarily. (Checks for binary zero while +processing items within the loop are OK, because afterwards the main loop will +terminate.) Arguments: - ptr current position in the pattern - count current count of capturing parens so far encountered + ptrptr address of the current character pointer (updated) + cd compile background data name name to seek, or NULL if seeking a numbered subpattern lorn name length, or subpattern number if name is NULL xmode TRUE if we are in /x mode + utf TRUE if we are in UTF-8 / UTF-16 / UTF-32 mode + count pointer to the current capturing subpattern number (updated) Returns: the number of the named subpattern, or -1 if not found */ static int -find_parens(const uschar *ptr, int count, const uschar *name, int lorn, - BOOL xmode) +find_parens_sub(pcre_uchar **ptrptr, compile_data *cd, const pcre_uchar *name, int lorn, + BOOL xmode, BOOL utf, int *count) { -const uschar *thisname; +pcre_uchar *ptr = *ptrptr; +int start_count = *count; +int hwm_count = start_count; +BOOL dup_parens = FALSE; + +/* If the first character is a parenthesis, check on the type of group we are +dealing with. The very first call may not start with a parenthesis. */ -for (; *ptr != 0; ptr++) +if (ptr[0] == CHAR_LEFT_PARENTHESIS) { - int term; + /* Handle specials such as (*SKIP) or (*UTF8) etc. */ + + if (ptr[1] == CHAR_ASTERISK) + { + ptr += 2; + while (ptr < cd->end_pattern && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; + } + + /* Handle a normal, unnamed capturing parenthesis. */ + + else if (ptr[1] != CHAR_QUESTION_MARK) + { + *count += 1; + if (name == NULL && *count == lorn) return *count; + ptr++; + } + + /* All cases now have (? at the start. Remember when we are in a group + where the parenthesis numbers are duplicated. */ + + else if (ptr[2] == CHAR_VERTICAL_LINE) + { + ptr += 3; + dup_parens = TRUE; + } + + /* Handle comments; all characters are allowed until a ket is reached. */ + + else if (ptr[2] == CHAR_NUMBER_SIGN) + { + for (ptr += 3; *ptr != CHAR_NULL; ptr++) + if (*ptr == CHAR_RIGHT_PARENTHESIS) break; + goto FAIL_EXIT; + } + + /* Handle a condition. If it is an assertion, just carry on so that it + is processed as normal. If not, skip to the closing parenthesis of the + condition (there can't be any nested parens). */ + + else if (ptr[2] == CHAR_LEFT_PARENTHESIS) + { + ptr += 2; + if (ptr[1] != CHAR_QUESTION_MARK) + { + while (*ptr != CHAR_NULL && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; + if (*ptr != CHAR_NULL) ptr++; + } + } + + /* Start with (? but not a condition. */ + + else + { + ptr += 2; + if (*ptr == CHAR_P) ptr++; /* Allow optional P */ + /* We have to disambiguate (? for named groups */ + + if ((*ptr == CHAR_LESS_THAN_SIGN && ptr[1] != CHAR_EXCLAMATION_MARK && + ptr[1] != CHAR_EQUALS_SIGN) || *ptr == CHAR_APOSTROPHE) + { + pcre_uchar term; + const pcre_uchar *thisname; + *count += 1; + if (name == NULL && *count == lorn) return *count; + term = *ptr++; + if (term == CHAR_LESS_THAN_SIGN) term = CHAR_GREATER_THAN_SIGN; + thisname = ptr; + while (*ptr != term) ptr++; + if (name != NULL && lorn == (int)(ptr - thisname) && + STRNCMP_UC_UC(name, thisname, (unsigned int)lorn) == 0) + return *count; + term++; + } + } + } + +/* Past any initial parenthesis handling, scan for parentheses or vertical +bars. Stop if we get to cd->end_pattern. Note that this is important for the +first-pass call when this value is temporarily adjusted to stop at the current +position. So DO NOT change this to a test for binary zero. */ + +for (; ptr < cd->end_pattern; ptr++) + { /* Skip over backslashed characters and also entire \Q...\E */ - if (*ptr == '\\') + if (*ptr == CHAR_BACKSLASH) { - if (*(++ptr) == 0) return -1; - if (*ptr == 'Q') for (;;) + if (*(++ptr) == CHAR_NULL) goto FAIL_EXIT; + if (*ptr == CHAR_Q) for (;;) { - while (*(++ptr) != 0 && *ptr != '\\'); - if (*ptr == 0) return -1; - if (*(++ptr) == 'E') break; + while (*(++ptr) != CHAR_NULL && *ptr != CHAR_BACKSLASH) {}; + if (*ptr == CHAR_NULL) goto FAIL_EXIT; + if (*(++ptr) == CHAR_E) break; } continue; } - /* Skip over character classes */ + /* Skip over character classes; this logic must be similar to the way they + are handled for real. If the first character is '^', skip it. Also, if the + first few characters (either before or after ^) are \Q\E or \E we skip them + too. This makes for compatibility with Perl. Note the use of STR macros to + encode "Q\\E" so that it works in UTF-8 on EBCDIC platforms. */ - if (*ptr == '[') + if (*ptr == CHAR_LEFT_SQUARE_BRACKET) { - while (*(++ptr) != ']') + BOOL negate_class = FALSE; + for (;;) + { + if (ptr[1] == CHAR_BACKSLASH) + { + if (ptr[2] == CHAR_E) + ptr+= 2; + else if (STRNCMP_UC_C8(ptr + 2, + STR_Q STR_BACKSLASH STR_E, 3) == 0) + ptr += 4; + else + break; + } + else if (!negate_class && ptr[1] == CHAR_CIRCUMFLEX_ACCENT) + { + negate_class = TRUE; + ptr++; + } + else break; + } + + /* If the next character is ']', it is a data character that must be + skipped, except in JavaScript compatibility mode. */ + + if (ptr[1] == CHAR_RIGHT_SQUARE_BRACKET && + (cd->external_options & PCRE_JAVASCRIPT_COMPAT) == 0) + ptr++; + + while (*(++ptr) != CHAR_RIGHT_SQUARE_BRACKET) { - if (*ptr == 0) return -1; - if (*ptr == '\\') + if (*ptr == CHAR_NULL) return -1; + if (*ptr == CHAR_BACKSLASH) { - if (*(++ptr) == 0) return -1; - if (*ptr == 'Q') for (;;) + if (*(++ptr) == CHAR_NULL) goto FAIL_EXIT; + if (*ptr == CHAR_Q) for (;;) { - while (*(++ptr) != 0 && *ptr != '\\'); - if (*ptr == 0) return -1; - if (*(++ptr) == 'E') break; + while (*(++ptr) != CHAR_NULL && *ptr != CHAR_BACKSLASH) {}; + if (*ptr == CHAR_NULL) goto FAIL_EXIT; + if (*(++ptr) == CHAR_E) break; } continue; } @@ -1013,89 +1563,131 @@ for (; *ptr != 0; ptr++) /* Skip comments in /x mode */ - if (xmode && *ptr == '#') + if (xmode && *ptr == CHAR_NUMBER_SIGN) { - while (*(++ptr) != 0 && *ptr != '\n'); - if (*ptr == 0) return -1; + ptr++; + while (*ptr != CHAR_NULL) + { + if (IS_NEWLINE(ptr)) { ptr += cd->nllen - 1; break; } + ptr++; +#ifdef SUPPORT_UTF + if (utf) FORWARDCHAR(ptr); +#endif + } + if (*ptr == CHAR_NULL) goto FAIL_EXIT; continue; } - /* An opening parens must now be a real metacharacter */ + /* Check for the special metacharacters */ - if (*ptr != '(') continue; - if (ptr[1] != '?' && ptr[1] != '*') + if (*ptr == CHAR_LEFT_PARENTHESIS) { - count++; - if (name == NULL && count == lorn) return count; - continue; + int rc = find_parens_sub(&ptr, cd, name, lorn, xmode, utf, count); + if (rc > 0) return rc; + if (*ptr == CHAR_NULL) goto FAIL_EXIT; } - ptr += 2; - if (*ptr == 'P') ptr++; /* Allow optional P */ - - /* We have to disambiguate (? */ - - if ((*ptr != '<' || ptr[1] == '!' || ptr[1] == '=') && - *ptr != '\'') - continue; - - count++; + else if (*ptr == CHAR_RIGHT_PARENTHESIS) + { + if (dup_parens && *count < hwm_count) *count = hwm_count; + goto FAIL_EXIT; + } - if (name == NULL && count == lorn) return count; - term = *ptr++; - if (term == '<') term = '>'; - thisname = ptr; - while (*ptr != term) ptr++; - if (name != NULL && lorn == ptr - thisname && - strncmp((const char *)name, (const char *)thisname, lorn) == 0) - return count; + else if (*ptr == CHAR_VERTICAL_LINE && dup_parens) + { + if (*count > hwm_count) hwm_count = *count; + *count = start_count; + } } +FAIL_EXIT: +*ptrptr = ptr; return -1; } + /************************************************* -* Find first significant op code * +* Find forward referenced subpattern * *************************************************/ -/* This is called by several functions that scan a compiled expression looking -for a fixed first character, or an anchoring op code etc. It skips over things -that do not influence this. For some calls, a change of option is important. -For some calls, it makes sense to skip negative forward and all backward -assertions, and also the \b assertion; for others it does not. +/* This function scans along a pattern's text looking for capturing +subpatterns, and counting them. If it finds a named pattern that matches the +name it is given, it returns its number. Alternatively, if the name is NULL, it +returns when it reaches a given numbered subpattern. This is used for forward +references to subpatterns. We used to be able to start this scan from the +current compiling point, using the current count value from cd->bracount, and +do it all in a single loop, but the addition of the possibility of duplicate +subpattern numbers means that we have to scan from the very start, in order to +take account of such duplicates, and to use a recursive function to keep track +of the different types of group. + +Arguments: + cd compile background data + name name to seek, or NULL if seeking a numbered subpattern + lorn name length, or subpattern number if name is NULL + xmode TRUE if we are in /x mode + utf TRUE if we are in UTF-8 / UTF-16 / UTF-32 mode + +Returns: the number of the found subpattern, or -1 if not found +*/ + +static int +find_parens(compile_data *cd, const pcre_uchar *name, int lorn, BOOL xmode, + BOOL utf) +{ +pcre_uchar *ptr = (pcre_uchar *)cd->start_pattern; +int count = 0; +int rc; + +/* If the pattern does not start with an opening parenthesis, the first call +to find_parens_sub() will scan right to the end (if necessary). However, if it +does start with a parenthesis, find_parens_sub() will return when it hits the +matching closing parens. That is why we have to have a loop. */ + +for (;;) + { + rc = find_parens_sub(&ptr, cd, name, lorn, xmode, utf, &count); + if (rc > 0 || *ptr++ == CHAR_NULL) break; + } + +return rc; +} + + + + +/************************************************* +* Find first significant op code * +*************************************************/ + +/* This is called by several functions that scan a compiled expression looking +for a fixed first character, or an anchoring op code etc. It skips over things +that do not influence this. For some calls, it makes sense to skip negative +forward and all backward assertions, and also the \b assertion; for others it +does not. Arguments: code pointer to the start of the group - options pointer to external options - optbit the option bit whose changing is significant, or - zero if none are skipassert TRUE if certain assertions are to be skipped Returns: pointer to the first significant opcode */ -static const uschar* -first_significant_code(const uschar *code, int *options, int optbit, - BOOL skipassert) +static const pcre_uchar* +first_significant_code(const pcre_uchar *code, BOOL skipassert) { for (;;) { switch ((int)*code) { - case OP_OPT: - if (optbit > 0 && ((int)code[1] & optbit) != (*options & optbit)) - *options = (int)code[1]; - code += 2; - break; - case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: if (!skipassert) return code; do code += GET(code, 1); while (*code == OP_ALT); - code += _erts_pcre_OP_lengths[*code]; + code += PRIV(OP_lengths)[*code]; break; case OP_WORD_BOUNDARY: @@ -1105,9 +1697,11 @@ for (;;) case OP_CALLOUT: case OP_CREF: + case OP_NCREF: case OP_RREF: + case OP_NRREF: case OP_DEF: - code += _erts_pcre_OP_lengths[*code]; + code += PRIV(OP_lengths)[*code]; break; default: @@ -1121,28 +1715,40 @@ for (;;) /************************************************* -* Find the fixed length of a pattern * +* Find the fixed length of a branch * *************************************************/ -/* Scan a pattern and compute the fixed length of subject that will match it, +/* Scan a branch and compute the fixed length of subject that will match it, if the length is fixed. This is needed for dealing with backward assertions. -In UTF8 mode, the result is in characters rather than bytes. +In UTF8 mode, the result is in characters rather than bytes. The branch is +temporarily terminated with OP_END when this function is called. + +This function is called when a backward assertion is encountered, so that if it +fails, the error message can point to the correct place in the pattern. +However, we cannot do this when the assertion contains subroutine calls, +because they can be forward references. We solve this by remembering this case +and doing the check at the end; a flag specifies which mode we are running in. Arguments: code points to the start of the pattern (the bracket) - options the compiling options - -Returns: the fixed length, or -1 if there is no fixed length, - or -2 if \C was encountered + utf TRUE in UTF-8 / UTF-16 / UTF-32 mode + atend TRUE if called when the pattern is complete + cd the "compile data" structure + +Returns: the fixed length, + or -1 if there is no fixed length, + or -2 if \C was encountered (in UTF-8 mode only) + or -3 if an OP_RECURSE item was encountered and atend is FALSE + or -4 if an unknown opcode was encountered (internal error) */ static int -find_fixedlength(uschar *code, int options) +find_fixedlength(pcre_uchar *code, BOOL utf, BOOL atend, compile_data *cd) { int length = -1; register int branchlength = 0; -register uschar *cc = code + 1 + LINK_SIZE; +register pcre_uchar *cc = code + 1 + LINK_SIZE; /* Scan along the opcodes for this branch. If we get to the end of the branch, check the length against that of the other branches. */ @@ -1150,29 +1756,39 @@ branch, check the length against that of the other branches. */ for (;;) { int d; - register int op = *cc; + pcre_uchar *ce, *cs; + register pcre_uchar op = *cc; + switch (op) { + /* We only need to continue for OP_CBRA (normal capturing bracket) and + OP_BRA (normal non-capturing bracket) because the other variants of these + opcodes are all concerned with unlimited repeated groups, which of course + are not of fixed length. */ + case OP_CBRA: case OP_BRA: case OP_ONCE: + case OP_ONCE_NC: case OP_COND: - d = find_fixedlength(cc + ((op == OP_CBRA)? 2:0), options); + d = find_fixedlength(cc + ((op == OP_CBRA)? IMM2_SIZE : 0), utf, atend, cd); if (d < 0) return d; branchlength += d; do cc += GET(cc, 1); while (*cc == OP_ALT); cc += 1 + LINK_SIZE; break; - /* Reached end of a branch; if it's a ket it is the end of a nested - call. If it's ALT it is an alternation in a nested call. If it is - END it's the end of the outer call. All can be handled by the same code. */ + /* Reached end of a branch; if it's a ket it is the end of a nested call. + If it's ALT it is an alternation in a nested call. An ACCEPT is effectively + an ALT. If it is END it's the end of the outer call. All can be handled by + the same code. Note that we must not include the OP_KETRxxx opcodes here, + because they all imply an unlimited repeat. */ case OP_ALT: case OP_KET: - case OP_KETRMAX: - case OP_KETRMIN: case OP_END: + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: if (length < 0) length = branchlength; else if (length != branchlength) return -1; if (*cc != OP_ALT) return length; @@ -1180,6 +1796,21 @@ for (;;) branchlength = 0; break; + /* A true recursion implies not fixed length, but a subroutine call may + be OK. If the subroutine is a forward reference, we can't deal with + it until the end of the pattern, so return -3. */ + + case OP_RECURSE: + if (!atend) return -3; + cs = ce = (pcre_uchar *)cd->start_code + GET(cc, 1); /* Start subpattern */ + do ce += GET(ce, 1); while (*ce == OP_ALT); /* End subpattern */ + if (cc > cs && cc < ce) return -1; /* Recursion */ + d = find_fixedlength(cs + IMM2_SIZE, utf, atend, cd); + if (d < 0) return d; + branchlength += d; + cc += 1 + LINK_SIZE; + break; + /* Skip over assertive subpatterns */ case OP_ASSERT: @@ -1187,39 +1818,55 @@ for (;;) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: do cc += GET(cc, 1); while (*cc == OP_ALT); - /* Fall through */ + cc += PRIV(OP_lengths)[*cc]; + break; /* Skip over things that don't match chars */ - case OP_REVERSE: + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + case OP_THEN_ARG: + cc += cc[1] + PRIV(OP_lengths)[*cc]; + break; + + case OP_CALLOUT: + case OP_CIRC: + case OP_CIRCM: + case OP_CLOSE: + case OP_COMMIT: case OP_CREF: - case OP_RREF: case OP_DEF: - case OP_OPT: - case OP_CALLOUT: - case OP_SOD: - case OP_SOM: + case OP_DOLL: + case OP_DOLLM: case OP_EOD: case OP_EODN: - case OP_CIRC: - case OP_DOLL: + case OP_FAIL: + case OP_NCREF: + case OP_NRREF: case OP_NOT_WORD_BOUNDARY: + case OP_PRUNE: + case OP_REVERSE: + case OP_RREF: + case OP_SET_SOM: + case OP_SKIP: + case OP_SOD: + case OP_SOM: + case OP_THEN: case OP_WORD_BOUNDARY: - cc += _erts_pcre_OP_lengths[*cc]; + cc += PRIV(OP_lengths)[*cc]; break; /* Handle literal characters */ case OP_CHAR: - case OP_CHARNC: + case OP_CHARI: case OP_NOT: + case OP_NOTI: branchlength++; cc += 2; -#ifdef SUPPORT_UTF8 - if ((options & PCRE_UTF8) != 0) - { - while ((*cc & 0xc0) == 0x80) cc++; - } +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; @@ -1227,20 +1874,21 @@ for (;;) need to skip over a multibyte character in UTF8 mode. */ case OP_EXACT: - branchlength += GET2(cc,1); - cc += 4; -#ifdef SUPPORT_UTF8 - if ((options & PCRE_UTF8) != 0) - { - while((*cc & 0x80) == 0x80) cc++; - } + case OP_EXACTI: + case OP_NOTEXACT: + case OP_NOTEXACTI: + branchlength += (int)GET2(cc,1); + cc += 2 + IMM2_SIZE; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; case OP_TYPEEXACT: branchlength += GET2(cc,1); - if (cc[3] == OP_PROP || cc[3] == OP_NOTPROP) cc += 2; - cc += 4; + if (cc[1 + IMM2_SIZE] == OP_PROP || cc[1 + IMM2_SIZE] == OP_NOTPROP) + cc += 2; + cc += 1 + IMM2_SIZE + 1; break; /* Handle single-char matchers */ @@ -1250,6 +1898,10 @@ for (;;) cc += 2; /* Fall through */ + case OP_HSPACE: + case OP_VSPACE: + case OP_NOT_HSPACE: + case OP_NOT_VSPACE: case OP_NOT_DIGIT: case OP_DIGIT: case OP_NOT_WHITESPACE: @@ -1257,29 +1909,37 @@ for (;;) case OP_NOT_WORDCHAR: case OP_WORDCHAR: case OP_ANY: + case OP_ALLANY: branchlength++; cc++; break; - /* The single-byte matcher isn't allowed */ + /* The single-byte matcher isn't allowed. This only happens in UTF-8 mode; + otherwise \C is coded as OP_ALLANY. */ case OP_ANYBYTE: return -2; /* Check a class for variable quantification */ -#ifdef SUPPORT_UTF8 - case OP_XCLASS: - cc += GET(cc, 1) - 33; - /* Fall through */ -#endif - case OP_CLASS: case OP_NCLASS: - cc += 33; +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + case OP_XCLASS: + /* The original code caused an unsigned overflow in 64 bit systems, + so now we use a conditional statement. */ + if (op == OP_XCLASS) + cc += GET(cc, 1); + else + cc += PRIV(OP_lengths)[OP_CLASS]; +#else + cc += PRIV(OP_lengths)[OP_CLASS]; +#endif switch (*cc) { + case OP_CRPLUS: + case OP_CRMINPLUS: case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRQUERY: @@ -1288,9 +1948,9 @@ for (;;) case OP_CRRANGE: case OP_CRMINRANGE: - if (GET2(cc,1) != GET2(cc,3)) return -1; - branchlength += GET2(cc,1); - cc += 5; + if (GET2(cc,1) != GET2(cc,1+IMM2_SIZE)) return -1; + branchlength += (int)GET2(cc,1); + cc += 1 + 2 * IMM2_SIZE; break; default: @@ -1300,8 +1960,91 @@ for (;;) /* Anything else is variable length */ - default: + case OP_ANYNL: + case OP_BRAMINZERO: + case OP_BRAPOS: + case OP_BRAPOSZERO: + case OP_BRAZERO: + case OP_CBRAPOS: + case OP_EXTUNI: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_KETRPOS: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_MINQUERY: + case OP_MINQUERYI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_MINUPTO: + case OP_MINUPTOI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_NOTUPTO: + case OP_NOTUPTOI: + case OP_PLUS: + case OP_PLUSI: + case OP_POSPLUS: + case OP_POSPLUSI: + case OP_POSQUERY: + case OP_POSQUERYI: + case OP_POSSTAR: + case OP_POSSTARI: + case OP_POSUPTO: + case OP_POSUPTOI: + case OP_QUERY: + case OP_QUERYI: + case OP_REF: + case OP_REFI: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCBRA: + case OP_SCBRAPOS: + case OP_SCOND: + case OP_SKIPZERO: + case OP_STAR: + case OP_STARI: + case OP_TYPEMINPLUS: + case OP_TYPEMINQUERY: + case OP_TYPEMINSTAR: + case OP_TYPEMINUPTO: + case OP_TYPEPLUS: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSUPTO: + case OP_TYPEQUERY: + case OP_TYPESTAR: + case OP_TYPEUPTO: + case OP_UPTO: + case OP_UPTOI: return -1; + + /* Catch unrecognized opcodes so that when new ones are added they + are not forgotten, as has happened in the past. */ + + default: + return -4; } } /* Control never gets here */ @@ -1311,26 +2054,30 @@ for (;;) /************************************************* -* Scan compiled regex for numbered bracket * +* Scan compiled regex for specific bracket * *************************************************/ /* This little function scans through a compiled pattern until it finds a -capturing bracket with the given number. +capturing bracket with the given number, or, if the number is negative, an +instance of OP_REVERSE for a lookbehind. The function is global in the C sense +so that it can be called from pcre_study() when finding the minimum matching +length. Arguments: code points to start of expression - utf8 TRUE in UTF-8 mode - number the required bracket number + utf TRUE in UTF-8 / UTF-16 / UTF-32 mode + number the required bracket number or negative to find a lookbehind Returns: pointer to the opcode for the bracket, or NULL if not found */ -static const uschar * -find_bracket(const uschar *code, BOOL utf8, int number) +const pcre_uchar * +PRIV(find_bracket)(const pcre_uchar *code, BOOL utf, int number) { for (;;) { - register int c = *code; + register pcre_uchar c = *code; + if (c == OP_END) return NULL; /* XCLASS is used for classes that cannot be represented just by a bit @@ -1339,18 +2086,28 @@ for (;;) if (c == OP_XCLASS) code += GET(code, 1); + /* Handle recursion */ + + else if (c == OP_REVERSE) + { + if (number < 0) return (pcre_uchar *)code; + code += PRIV(OP_lengths)[c]; + } + /* Handle capturing bracket */ - else if (c == OP_CBRA) + else if (c == OP_CBRA || c == OP_SCBRA || + c == OP_CBRAPOS || c == OP_SCBRAPOS) { - int n = GET2(code, 1+LINK_SIZE); - if (n == number) return (uschar *)code; - code += _erts_pcre_OP_lengths[c]; + int n = (int)GET2(code, 1+LINK_SIZE); + if (n == number) return (pcre_uchar *)code; + code += PRIV(OP_lengths)[c]; } /* Otherwise, we can get the item's length from the table, except that for repeated character types, we have to test for \p and \P, which have an extra - two bytes of parameters. */ + two bytes of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we + must add in its length. */ else { @@ -1372,39 +2129,62 @@ for (;;) case OP_TYPEMINUPTO: case OP_TYPEEXACT: case OP_TYPEPOSUPTO: - if (code[3] == OP_PROP || code[3] == OP_NOTPROP) code += 2; + if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) + code += 2; + break; + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + case OP_THEN_ARG: + code += code[1]; break; } /* Add in the fixed length from the table */ - code += _erts_pcre_OP_lengths[c]; + code += PRIV(OP_lengths)[c]; /* In UTF-8 mode, opcodes that are followed by a character may be followed by a multi-byte character. The length in the table is a minimum, so we have to arrange to skip the extra bytes. */ -#ifdef SUPPORT_UTF8 - if (utf8) switch(c) +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf) switch(c) { case OP_CHAR: - case OP_CHARNC: + case OP_CHARI: case OP_EXACT: + case OP_EXACTI: case OP_UPTO: + case OP_UPTOI: case OP_MINUPTO: + case OP_MINUPTOI: case OP_POSUPTO: + case OP_POSUPTOI: case OP_STAR: + case OP_STARI: case OP_MINSTAR: + case OP_MINSTARI: case OP_POSSTAR: + case OP_POSSTARI: case OP_PLUS: + case OP_PLUSI: case OP_MINPLUS: + case OP_MINPLUSI: case OP_POSPLUS: + case OP_POSPLUSI: case OP_QUERY: + case OP_QUERYI: case OP_MINQUERY: + case OP_MINQUERYI: case OP_POSQUERY: - if (code[-1] >= 0xc0) code += _erts_pcre_utf8_table4[code[-1] & 0x3f]; + case OP_POSQUERYI: + if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]); break; } +#else + (void)(utf); /* Keep compiler happy by referencing function argument */ #endif } } @@ -1421,17 +2201,17 @@ instance of OP_RECURSE. Arguments: code points to start of expression - utf8 TRUE in UTF-8 mode + utf TRUE in UTF-8 / UTF-16 / UTF-32 mode Returns: pointer to the opcode for OP_RECURSE, or NULL if not found */ -static const uschar * -find_recurse(const uschar *code, BOOL utf8) +static const pcre_uchar * +find_recurse(const pcre_uchar *code, BOOL utf) { for (;;) { - register int c = *code; + register pcre_uchar c = *code; if (c == OP_END) return NULL; if (c == OP_RECURSE) return code; @@ -1443,7 +2223,8 @@ for (;;) /* Otherwise, we can get the item's length from the table, except that for repeated character types, we have to test for \p and \P, which have an extra - two bytes of parameters. */ + two bytes of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we + must add in its length. */ else { @@ -1465,39 +2246,90 @@ for (;;) case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEEXACT: - if (code[3] == OP_PROP || code[3] == OP_NOTPROP) code += 2; + if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) + code += 2; + break; + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + case OP_THEN_ARG: + code += code[1]; break; } /* Add in the fixed length from the table */ - code += _erts_pcre_OP_lengths[c]; + code += PRIV(OP_lengths)[c]; /* In UTF-8 mode, opcodes that are followed by a character may be followed by a multi-byte character. The length in the table is a minimum, so we have to arrange to skip the extra bytes. */ -#ifdef SUPPORT_UTF8 - if (utf8) switch(c) +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf) switch(c) { case OP_CHAR: - case OP_CHARNC: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: case OP_EXACT: + case OP_EXACTI: + case OP_NOTEXACT: + case OP_NOTEXACTI: case OP_UPTO: + case OP_UPTOI: + case OP_NOTUPTO: + case OP_NOTUPTOI: case OP_MINUPTO: + case OP_MINUPTOI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: case OP_POSUPTO: + case OP_POSUPTOI: + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: case OP_STAR: + case OP_STARI: + case OP_NOTSTAR: + case OP_NOTSTARI: case OP_MINSTAR: + case OP_MINSTARI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: case OP_POSSTAR: + case OP_POSSTARI: + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: case OP_PLUS: + case OP_PLUSI: + case OP_NOTPLUS: + case OP_NOTPLUSI: case OP_MINPLUS: + case OP_MINPLUSI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: case OP_POSPLUS: + case OP_POSPLUSI: + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: case OP_QUERY: + case OP_QUERYI: + case OP_NOTQUERY: + case OP_NOTQUERYI: case OP_MINQUERY: + case OP_MINQUERYI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: case OP_POSQUERY: - if (code[-1] >= 0xc0) code += _erts_pcre_utf8_table4[code[-1] & 0x3f]; + case OP_POSQUERYI: + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]); break; } +#else + (void)(utf); /* Keep compiler happy by referencing function argument */ #endif } } @@ -1520,20 +2352,22 @@ bracket whose current branch will already have been scanned. Arguments: code points to start of search endcode points to where to stop - utf8 TRUE if in UTF8 mode + utf TRUE if in UTF-8 / UTF-16 / UTF-32 mode + cd contains pointers to tables etc. Returns: TRUE if what is matched could be empty */ static BOOL -could_be_empty_branch(const uschar *code, const uschar *endcode, BOOL utf8) +could_be_empty_branch(const pcre_uchar *code, const pcre_uchar *endcode, + BOOL utf, compile_data *cd) { -register int c; -for (code = first_significant_code(code + _erts_pcre_OP_lengths[*code], NULL, 0, TRUE); +register pcre_uchar c; +for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); code < endcode; - code = first_significant_code(code + _erts_pcre_OP_lengths[c], NULL, 0, TRUE)) + code = first_significant_code(code + PRIV(OP_lengths)[c], TRUE)) { - const uschar *ccode; + const pcre_uchar *ccode; c = *code; @@ -1547,11 +2381,63 @@ for (code = first_significant_code(code + _erts_pcre_OP_lengths[*code], NULL, 0, continue; } + /* For a recursion/subroutine call, if its end has been reached, which + implies a backward reference subroutine call, we can scan it. If it's a + forward reference subroutine call, we can't. To detect forward reference + we have to scan up the list that is kept in the workspace. This function is + called only when doing the real compile, not during the pre-compile that + measures the size of the compiled pattern. */ + + if (c == OP_RECURSE) + { + const pcre_uchar *scode; + BOOL empty_branch; + + /* Test for forward reference */ + + for (scode = cd->start_workspace; scode < cd->hwm; scode += LINK_SIZE) + if ((int)GET(scode, 0) == (int)(code + 1 - cd->start_code)) return TRUE; + + /* Not a forward reference, test for completed backward reference */ + + empty_branch = FALSE; + scode = cd->start_code + GET(code, 1); + if (GET(scode, 1) == 0) return TRUE; /* Unclosed */ + + /* Completed backwards reference */ + + do + { + if (could_be_empty_branch(scode, endcode, utf, cd)) + { + empty_branch = TRUE; + break; + } + scode += GET(scode, 1); + } + while (*scode == OP_ALT); + + if (!empty_branch) return FALSE; /* All branches are non-empty */ + continue; + } + /* Groups with zero repeats can of course be empty; skip them. */ - if (c == OP_BRAZERO || c == OP_BRAMINZERO) + if (c == OP_BRAZERO || c == OP_BRAMINZERO || c == OP_SKIPZERO || + c == OP_BRAPOSZERO) + { + code += PRIV(OP_lengths)[c]; + do code += GET(code, 1); while (*code == OP_ALT); + c = *code; + continue; + } + + /* A nested group that is already marked as "could be empty" can just be + skipped. */ + + if (c == OP_SBRA || c == OP_SBRAPOS || + c == OP_SCBRA || c == OP_SCBRAPOS) { - code += _erts_pcre_OP_lengths[c]; do code += GET(code, 1); while (*code == OP_ALT); c = *code; continue; @@ -1559,22 +2445,33 @@ for (code = first_significant_code(code + _erts_pcre_OP_lengths[*code], NULL, 0, /* For other groups, scan the branches. */ - if (c == OP_BRA || c == OP_CBRA || c == OP_ONCE || c == OP_COND) + if (c == OP_BRA || c == OP_BRAPOS || + c == OP_CBRA || c == OP_CBRAPOS || + c == OP_ONCE || c == OP_ONCE_NC || + c == OP_COND) { BOOL empty_branch; if (GET(code, 1) == 0) return TRUE; /* Hit unclosed bracket */ - /* Scan a closed bracket */ + /* If a conditional group has only one branch, there is a second, implied, + empty branch, so just skip over the conditional, because it could be empty. + Otherwise, scan the individual branches of the group. */ - empty_branch = FALSE; - do - { - if (!empty_branch && could_be_empty_branch(code, endcode, utf8)) - empty_branch = TRUE; + if (c == OP_COND && code[GET(code, 1)] != OP_ALT) code += GET(code, 1); + else + { + empty_branch = FALSE; + do + { + if (!empty_branch && could_be_empty_branch(code, endcode, utf, cd)) + empty_branch = TRUE; + code += GET(code, 1); + } + while (*code == OP_ALT); + if (!empty_branch) return FALSE; /* All branches are non-empty */ } - while (*code == OP_ALT); - if (!empty_branch) return FALSE; /* All branches are non-empty */ + c = *code; continue; } @@ -1585,11 +2482,11 @@ for (code = first_significant_code(code + _erts_pcre_OP_lengths[*code], NULL, 0, { /* Check for quantifiers after a class. XCLASS is used for classes that cannot be represented just by a bit map. This includes negated single - high-valued characters. The length in _erts_pcre_OP_lengths[] is zero; the + high-valued characters. The length in PRIV(OP_lengths)[] is zero; the actual length is stored in the compiled code, so we must update "code" here. */ -#ifdef SUPPORT_UTF8 +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 case OP_XCLASS: ccode = code += GET(code, 1); goto CHECK_CLASS_REPEAT; @@ -1597,9 +2494,9 @@ for (code = first_significant_code(code + _erts_pcre_OP_lengths[*code], NULL, 0, case OP_CLASS: case OP_NCLASS: - ccode = code + 33; + ccode = code + PRIV(OP_lengths)[OP_CLASS]; -#ifdef SUPPORT_UTF8 +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 CHECK_CLASS_REPEAT: #endif @@ -1635,10 +2532,12 @@ for (code = first_significant_code(code + _erts_pcre_OP_lengths[*code], NULL, 0, case OP_NOT_WORDCHAR: case OP_WORDCHAR: case OP_ANY: + case OP_ALLANY: case OP_ANYBYTE: case OP_CHAR: - case OP_CHARNC: + case OP_CHARI: case OP_NOT: + case OP_NOTI: case OP_PLUS: case OP_MINPLUS: case OP_POSPLUS: @@ -1670,7 +2569,8 @@ for (code = first_significant_code(code + _erts_pcre_OP_lengths[*code], NULL, 0, case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEPOSUPTO: - if (code[3] == OP_PROP || code[3] == OP_NOTPROP) code += 2; + if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) + code += 2; break; /* End of branch */ @@ -1678,25 +2578,53 @@ for (code = first_significant_code(code + _erts_pcre_OP_lengths[*code], NULL, 0, case OP_KET: case OP_KETRMAX: case OP_KETRMIN: + case OP_KETRPOS: case OP_ALT: return TRUE; /* In UTF-8 mode, STAR, MINSTAR, POSSTAR, QUERY, MINQUERY, POSQUERY, UPTO, MINUPTO, and POSUPTO may be followed by a multibyte character */ -#ifdef SUPPORT_UTF8 +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 case OP_STAR: + case OP_STARI: case OP_MINSTAR: + case OP_MINSTARI: case OP_POSSTAR: + case OP_POSSTARI: case OP_QUERY: + case OP_QUERYI: case OP_MINQUERY: + case OP_MINQUERYI: case OP_POSQUERY: + case OP_POSQUERYI: + if (utf && HAS_EXTRALEN(code[1])) code += GET_EXTRALEN(code[1]); + break; + case OP_UPTO: + case OP_UPTOI: case OP_MINUPTO: + case OP_MINUPTOI: case OP_POSUPTO: - if (utf8) while ((code[2] & 0xc0) == 0x80) code++; + case OP_POSUPTOI: + if (utf && HAS_EXTRALEN(code[1 + IMM2_SIZE])) code += GET_EXTRALEN(code[1 + IMM2_SIZE]); break; #endif + + /* MARK, and PRUNE/SKIP/THEN with an argument must skip over the argument + string. */ + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + case OP_THEN_ARG: + code += code[1]; + break; + + /* None of the remaining opcodes are required to match a character. */ + + default: + break; } } @@ -1713,23 +2641,27 @@ return TRUE; the current branch of the current pattern to see if it could match the empty string. If it could, we must look outwards for branches at other levels, stopping when we pass beyond the bracket which is the subject of the recursion. +This function is called only during the real compile, not during the +pre-compile. Arguments: code points to start of the recursion endcode points to where to stop (current RECURSE item) bcptr points to the chain of current (unclosed) branch starts - utf8 TRUE if in UTF-8 mode + utf TRUE if in UTF-8 / UTF-16 / UTF-32 mode + cd pointers to tables etc Returns: TRUE if what is matched could be empty */ static BOOL -could_be_empty(const uschar *code, const uschar *endcode, branch_chain *bcptr, - BOOL utf8) +could_be_empty(const pcre_uchar *code, const pcre_uchar *endcode, + branch_chain *bcptr, BOOL utf, compile_data *cd) { -while (bcptr != NULL && bcptr->current >= code) +while (bcptr != NULL && bcptr->current_branch >= code) { - if (!could_be_empty_branch(bcptr->current, endcode, utf8)) return FALSE; + if (!could_be_empty_branch(bcptr->current_branch, endcode, utf, cd)) + return FALSE; bcptr = bcptr->outer; } return TRUE; @@ -1761,6 +2693,17 @@ where Perl recognizes it as the POSIX class "lower" but PCRE does not recognize "l\ower". This is a lesser evil that not diagnosing bad classes when Perl does, I think. +A user pointed out that PCRE was rejecting [:a[:digit:]] whereas Perl was not. +It seems that the appearance of a nested POSIX class supersedes an apparent +external class. For example, [:a[:digit:]b:] matches "a", "b", ":", or +a digit. + +In Perl, unescaped square brackets may also appear as part of class names. For +example, [:a[:abc]b:] gives unknown POSIX class "[:abc]b:]". However, for +[:a[:abc]b][b:] it gives unknown POSIX class "[:abc]b][b:]", which does not +seem right at all. PCRE does not allow closing square brackets in POSIX class +names. + Arguments: ptr pointer to the initial [ endptr where to return the end pointer @@ -1769,20 +2712,27 @@ Returns: TRUE or FALSE */ static BOOL -check_posix_syntax(const uschar *ptr, const uschar **endptr) +check_posix_syntax(const pcre_uchar *ptr, const pcre_uchar **endptr) { -int terminator; /* Don't combine these lines; the Solaris cc */ +pcre_uchar terminator; /* Don't combine these lines; the Solaris cc */ terminator = *(++ptr); /* compiler warns about "non-constant" initializer. */ -for (++ptr; *ptr != 0; ptr++) +for (++ptr; *ptr != CHAR_NULL; ptr++) { - if (*ptr == '\\' && ptr[1] == ']') ptr++; else + if (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) + ptr++; + else if (*ptr == CHAR_RIGHT_SQUARE_BRACKET) return FALSE; + else { - if (*ptr == ']') return FALSE; - if (*ptr == terminator && ptr[1] == ']') + if (*ptr == terminator && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) { *endptr = ptr; return TRUE; } + if (*ptr == CHAR_LEFT_SQUARE_BRACKET && + (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || + ptr[1] == CHAR_EQUALS_SIGN) && + check_posix_syntax(ptr, endptr)) + return FALSE; } } return FALSE; @@ -1806,14 +2756,14 @@ Returns: a value representing the name, or -1 if unknown */ static int -check_posix_name(const uschar *ptr, int len) +check_posix_name(const pcre_uchar *ptr, int len) { const char *pn = posix_names; register int yield = 0; while (posix_name_lengths[yield] != 0) { if (len == posix_name_lengths[yield] && - strncmp((const char *)ptr, pn, len) == 0) return yield; + STRNCMP_UC_C8(ptr, pn, (unsigned int)len) == 0) return yield; pn += posix_name_lengths[yield] + 1; yield++; } @@ -1829,11 +2779,12 @@ return -1; that is referenced. This means that groups can be replicated for fixed repetition simply by copying (because the recursion is allowed to refer to earlier groups that are outside the current group). However, when a group is -optional (i.e. the minimum quantifier is zero), OP_BRAZERO is inserted before -it, after it has been compiled. This means that any OP_RECURSE items within it -that refer to the group itself or any contained groups have to have their -offsets adjusted. That one of the jobs of this function. Before it is called, -the partially compiled regex must be temporarily terminated with OP_END. +optional (i.e. the minimum quantifier is zero), OP_BRAZERO or OP_SKIPZERO is +inserted before it, after it has been compiled. This means that any OP_RECURSE +items within it that refer to the group itself or any contained groups have to +have their offsets adjusted. That one of the jobs of this function. Before it +is called, the partially compiled regex must be temporarily terminated with +OP_END. This function has been extended with the possibility of forward references for recursions and subroutine calls. It must also check the list of such references @@ -1844,7 +2795,7 @@ value in the reference (which is a group number). Arguments: group points to the start of the group adjust the amount by which the group is to be moved - utf8 TRUE in UTF-8 mode + utf TRUE in UTF-8 / UTF-16 / UTF-32 mode cd contains pointers to tables etc. save_hwm the hwm forward reference pointer at the start of the group @@ -1852,22 +2803,22 @@ Returns: nothing */ static void -adjust_recurse(uschar *group, int adjust, BOOL utf8, compile_data *cd, - uschar *save_hwm) +adjust_recurse(pcre_uchar *group, int adjust, BOOL utf, compile_data *cd, + pcre_uchar *save_hwm) { -uschar *ptr = group; +pcre_uchar *ptr = group; -while ((ptr = (uschar *)find_recurse(ptr, utf8)) != NULL) +while ((ptr = (pcre_uchar *)find_recurse(ptr, utf)) != NULL) { int offset; - uschar *hc; + pcre_uchar *hc; /* See if this recursion is on the forward reference list. If so, adjust the reference. */ for (hc = save_hwm; hc < cd->hwm; hc += LINK_SIZE) { - offset = GET(hc, 0); + offset = (int)GET(hc, 0); if (cd->start_code + offset == ptr + 1) { PUT(hc, 0, offset + adjust); @@ -1880,7 +2831,7 @@ while ((ptr = (uschar *)find_recurse(ptr, utf8)) != NULL) if (hc >= cd->hwm) { - offset = GET(ptr, 1); + offset = (int)GET(ptr, 1); if (cd->start_code + offset >= group) PUT(ptr, 1, offset + adjust); } @@ -1905,14 +2856,14 @@ Arguments: Returns: new code pointer */ -static uschar * -auto_callout(uschar *code, const uschar *ptr, compile_data *cd) +static pcre_uchar * +auto_callout(pcre_uchar *code, const pcre_uchar *ptr, compile_data *cd) { *code++ = OP_CALLOUT; *code++ = 255; -PUT(code, 0, ptr - cd->start_pattern); /* Pattern offset */ -PUT(code, LINK_SIZE, 0); /* Default length */ -return code + 2*LINK_SIZE; +PUT(code, 0, (int)(ptr - cd->start_pattern)); /* Pattern offset */ +PUT(code, LINK_SIZE, 0); /* Default length */ +return code + 2 * LINK_SIZE; } @@ -1934,9 +2885,9 @@ Returns: nothing */ static void -complete_callout(uschar *previous_callout, const uschar *ptr, compile_data *cd) +complete_callout(pcre_uchar *previous_callout, const pcre_uchar *ptr, compile_data *cd) { -int length = ptr - cd->start_pattern - GET(previous_callout, 2); +int length = (int)(ptr - cd->start_pattern - GET(previous_callout, 2)); PUT(previous_callout, 2 + LINK_SIZE, length); } @@ -1948,9 +2899,10 @@ PUT(previous_callout, 2 + LINK_SIZE, length); *************************************************/ /* This function is passed the start and end of a class range, in UTF-8 mode -with UCP support. It searches up the characters, looking for internal ranges of +with UCP support. It searches up the characters, looking for ranges of characters in the "other" case. Each call returns the next one, updating the -start address. +start address. A character with multiple other cases is returned on its own +with a special return value. Arguments: cptr points to starting character value; updated @@ -1958,33 +2910,127 @@ Arguments: ocptr where to put start of othercase range odptr where to put end of othercase range -Yield: TRUE when range returned; FALSE when no more +Yield: -1 when no more + 0 when a range is returned + >0 the CASESET offset for char with multiple other cases + in this case, ocptr contains the original */ -static BOOL -get_othercase_range(unsigned int *cptr, unsigned int d, unsigned int *ocptr, - unsigned int *odptr) +static int +get_othercase_range(pcre_uint32 *cptr, pcre_uint32 d, pcre_uint32 *ocptr, + pcre_uint32 *odptr) { -unsigned int c, othercase, next; +pcre_uint32 c, othercase, next; +unsigned int co; + +/* Find the first character that has an other case. If it has multiple other +cases, return its case offset value. */ for (c = *cptr; c <= d; c++) - { if ((othercase = _erts_pcre_ucp_othercase(c)) != NOTACHAR) break; } + { + if ((co = UCD_CASESET(c)) != 0) + { + *ocptr = c++; /* Character that has the set */ + *cptr = c; /* Rest of input range */ + return (int)co; + } + if ((othercase = UCD_OTHERCASE(c)) != c) break; + } -if (c > d) return FALSE; +if (c > d) return -1; /* Reached end of range */ *ocptr = othercase; next = othercase + 1; for (++c; c <= d; c++) { - if (_erts_pcre_ucp_othercase(c) != next) break; + if (UCD_OTHERCASE(c) != next) break; next++; } -*odptr = next - 1; -*cptr = c; +*odptr = next - 1; /* End of othercase range */ +*cptr = c; /* Rest of input range */ +return 0; +} + -return TRUE; + +/************************************************* +* Check a character and a property * +*************************************************/ + +/* This function is called by check_auto_possessive() when a property item +is adjacent to a fixed character. + +Arguments: + c the character + ptype the property type + pdata the data for the type + negated TRUE if it's a negated property (\P or \p{^) + +Returns: TRUE if auto-possessifying is OK +*/ + +static BOOL +check_char_prop(pcre_uint32 c, unsigned int ptype, unsigned int pdata, BOOL negated) +{ +#ifdef SUPPORT_UCP +const pcre_uint32 *p; +#endif + +const ucd_record *prop = GET_UCD(c); + +switch(ptype) + { + case PT_LAMP: + return (prop->chartype == ucp_Lu || + prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt) == negated; + + case PT_GC: + return (pdata == PRIV(ucp_gentype)[prop->chartype]) == negated; + + case PT_PC: + return (pdata == prop->chartype) == negated; + + case PT_SC: + return (pdata == prop->script) == negated; + + /* These are specials */ + + case PT_ALNUM: + return (PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N) == negated; + + case PT_SPACE: /* Perl space */ + return (PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR) + == negated; + + case PT_PXSPACE: /* POSIX space */ + return (PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR) + == negated; + + case PT_WORD: + return (PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE) == negated; + +#ifdef SUPPORT_UCP + case PT_CLIST: + p = PRIV(ucd_caseless_sets) + prop->caseset; + for (;;) + { + if (c < *p) return !negated; + if (c == *p++) return negated; + } + break; /* Control never reaches here */ +#endif + } + +return FALSE; } #endif /* SUPPORT_UCP */ @@ -1999,10 +3045,8 @@ whether the next thing could possibly match the repeated item. If not, it makes sense to automatically possessify the repeated item. Arguments: - op_code the repeated op code - this data for this item, depends on the opcode - utf8 TRUE in UTF-8 mode - utf8_char used for utf8 character bytes, NULL if not relevant + previous pointer to the repeated opcode + utf TRUE in UTF-8 / UTF-16 / UTF-32 mode ptr next character in pattern options options bits cd contains pointers to tables etc. @@ -2011,10 +3055,13 @@ Returns: TRUE if possessifying is wanted */ static BOOL -check_auto_possessive(int op_code, int item, BOOL utf8, uschar *utf8_char, - const uschar *ptr, int options, compile_data *cd) +check_auto_possessive(const pcre_uchar *previous, BOOL utf, + const pcre_uchar *ptr, int options, compile_data *cd) { -int next; +pcre_uint32 c = NOTACHAR; +pcre_uint32 next; +int escape; +pcre_uchar op_code = *previous++; /* Skip whitespace and comments in extended mode */ @@ -2022,11 +3069,18 @@ if ((options & PCRE_EXTENDED) != 0) { for (;;) { - while ((cd->ctypes[*ptr] & ctype_space) != 0) ptr++; - if (*ptr == '#') + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_space) != 0) ptr++; + if (*ptr == CHAR_NUMBER_SIGN) { - while (*(++ptr) != 0) + ptr++; + while (*ptr != CHAR_NULL) + { if (IS_NEWLINE(ptr)) { ptr += cd->nllen; break; } + ptr++; +#ifdef SUPPORT_UTF + if (utf) FORWARDCHAR(ptr); +#endif + } } else break; } @@ -2035,22 +3089,22 @@ if ((options & PCRE_EXTENDED) != 0) /* If the next item is one that we can handle, get its value. A non-negative value is a character, a negative value is an escape value. */ -if (*ptr == '\\') +if (*ptr == CHAR_BACKSLASH) { int temperrorcode = 0; - next = check_escape(&ptr, &temperrorcode, cd->bracount, options, FALSE); + escape = check_escape(&ptr, &next, &temperrorcode, cd->bracount, options, + FALSE); if (temperrorcode != 0) return FALSE; ptr++; /* Point after the escape sequence */ } - -else if ((cd->ctypes[*ptr] & ctype_meta) == 0) +else if (!MAX_255(*ptr) || (cd->ctypes[*ptr] & ctype_meta) == 0) { -#ifdef SUPPORT_UTF8 - if (utf8) { GETCHARINC(next, ptr); } else + escape = 0; +#ifdef SUPPORT_UTF + if (utf) { GETCHARINC(next, ptr); } else #endif next = *ptr++; } - else return FALSE; /* Skip whitespace and comments in extended mode */ @@ -2059,11 +3113,18 @@ if ((options & PCRE_EXTENDED) != 0) { for (;;) { - while ((cd->ctypes[*ptr] & ctype_space) != 0) ptr++; - if (*ptr == '#') + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_space) != 0) ptr++; + if (*ptr == CHAR_NUMBER_SIGN) { - while (*(++ptr) != 0) + ptr++; + while (*ptr != CHAR_NULL) + { if (IS_NEWLINE(ptr)) { ptr += cd->nllen; break; } + ptr++; +#ifdef SUPPORT_UTF + if (utf) FORWARDCHAR(ptr); +#endif + } } else break; } @@ -2071,247 +3132,286 @@ if ((options & PCRE_EXTENDED) != 0) /* If the next thing is itself optional, we have to give up. */ -if (*ptr == '*' || *ptr == '?' || strncmp((char *)ptr, "{0,", 3) == 0) - return FALSE; - -/* Now compare the next item with the previous opcode. If the previous is a -positive single character match, "item" either contains the character or, if -"item" is greater than 127 in utf8 mode, the character's bytes are in -utf8_char. */ - +if (*ptr == CHAR_ASTERISK || *ptr == CHAR_QUESTION_MARK || + STRNCMP_UC_C8(ptr, STR_LEFT_CURLY_BRACKET STR_0 STR_COMMA, 3) == 0) + return FALSE; -/* Handle cases when the next item is a character. */ +/* If the previous item is a character, get its value. */ -if (next >= 0) switch(op_code) +if (op_code == OP_CHAR || op_code == OP_CHARI || + op_code == OP_NOT || op_code == OP_NOTI) { - case OP_CHAR: -#ifdef SUPPORT_UTF8 - if (utf8 && item > 127) { GETCHAR(item, utf8_char); } +#ifdef SUPPORT_UTF + GETCHARTEST(c, previous); +#else + c = *previous; #endif - return item != next; + } - /* For CHARNC (caseless character) we must check the other case. If we have - Unicode property support, we can use it to test the other case of - high-valued characters. */ +/* Now compare the next item with the previous opcode. First, handle cases when +the next item is a character. */ + +if (escape == 0) + { + /* For a caseless UTF match, the next character may have more than one other + case, which maps to the special PT_CLIST property. Check this first. */ - case OP_CHARNC: -#ifdef SUPPORT_UTF8 - if (utf8 && item > 127) { GETCHAR(item, utf8_char); } +#ifdef SUPPORT_UCP + if (utf && c != NOTACHAR && (options & PCRE_CASELESS) != 0) + { + unsigned int ocs = UCD_CASESET(next); + if (ocs > 0) return check_char_prop(c, PT_CLIST, ocs, op_code >= OP_NOT); + } #endif - if (item == next) return FALSE; -#ifdef SUPPORT_UTF8 - if (utf8) + + switch(op_code) { - unsigned int othercase; - if (next < 128) othercase = cd->fcc[next]; else + case OP_CHAR: + return c != next; + + /* For CHARI (caseless character) we must check the other case. If we have + Unicode property support, we can use it to test the other case of + high-valued characters. We know that next can have only one other case, + because multi-other-case characters are dealt with above. */ + + case OP_CHARI: + if (c == next) return FALSE; +#ifdef SUPPORT_UTF + if (utf) + { + pcre_uint32 othercase; + if (next < 128) othercase = cd->fcc[next]; else #ifdef SUPPORT_UCP - othercase = _erts_pcre_ucp_othercase((unsigned int)next); + othercase = UCD_OTHERCASE(next); #else - othercase = NOTACHAR; + othercase = NOTACHAR; #endif - return (unsigned int)item != othercase; - } - else -#endif /* SUPPORT_UTF8 */ - return (item != cd->fcc[next]); /* Non-UTF-8 mode */ + return c != othercase; + } + else +#endif /* SUPPORT_UTF */ + return (c != TABLE_GET(next, cd->fcc, next)); /* Not UTF */ - /* For OP_NOT, "item" must be a single-byte character. */ + case OP_NOT: + return c == next; - case OP_NOT: - if (next < 0) return FALSE; /* Not a character */ - if (item == next) return TRUE; - if ((options & PCRE_CASELESS) == 0) return FALSE; -#ifdef SUPPORT_UTF8 - if (utf8) - { - unsigned int othercase; - if (next < 128) othercase = cd->fcc[next]; else + case OP_NOTI: + if (c == next) return TRUE; +#ifdef SUPPORT_UTF + if (utf) + { + pcre_uint32 othercase; + if (next < 128) othercase = cd->fcc[next]; else #ifdef SUPPORT_UCP - othercase = _erts_pcre_ucp_othercase(next); + othercase = UCD_OTHERCASE(next); #else - othercase = NOTACHAR; + othercase = NOTACHAR; #endif - return (unsigned int)item == othercase; - } - else -#endif /* SUPPORT_UTF8 */ - return (item == cd->fcc[next]); /* Non-UTF-8 mode */ + return c == othercase; + } + else +#endif /* SUPPORT_UTF */ + return (c == TABLE_GET(next, cd->fcc, next)); /* Not UTF */ - case OP_DIGIT: - return next > 127 || (cd->ctypes[next] & ctype_digit) == 0; + /* Note that OP_DIGIT etc. are generated only when PCRE_UCP is *not* set. + When it is set, \d etc. are converted into OP_(NOT_)PROP codes. */ - case OP_NOT_DIGIT: - return next <= 127 && (cd->ctypes[next] & ctype_digit) != 0; + case OP_DIGIT: + return next > 255 || (cd->ctypes[next] & ctype_digit) == 0; - case OP_WHITESPACE: - return next > 127 || (cd->ctypes[next] & ctype_space) == 0; + case OP_NOT_DIGIT: + return next <= 255 && (cd->ctypes[next] & ctype_digit) != 0; - case OP_NOT_WHITESPACE: - return next <= 127 && (cd->ctypes[next] & ctype_space) != 0; + case OP_WHITESPACE: + return next > 255 || (cd->ctypes[next] & ctype_space) == 0; - case OP_WORDCHAR: - return next > 127 || (cd->ctypes[next] & ctype_word) == 0; + case OP_NOT_WHITESPACE: + return next <= 255 && (cd->ctypes[next] & ctype_space) != 0; - case OP_NOT_WORDCHAR: - return next <= 127 && (cd->ctypes[next] & ctype_word) != 0; + case OP_WORDCHAR: + return next > 255 || (cd->ctypes[next] & ctype_word) == 0; - case OP_HSPACE: - case OP_NOT_HSPACE: - switch(next) - { - case 0x09: - case 0x20: - case 0xa0: - case 0x1680: - case 0x180e: - case 0x2000: - case 0x2001: - case 0x2002: - case 0x2003: - case 0x2004: - case 0x2005: - case 0x2006: - case 0x2007: - case 0x2008: - case 0x2009: - case 0x200A: - case 0x202f: - case 0x205f: - case 0x3000: - return op_code != OP_HSPACE; - default: - return op_code == OP_HSPACE; - } + case OP_NOT_WORDCHAR: + return next <= 255 && (cd->ctypes[next] & ctype_word) != 0; + + case OP_HSPACE: + case OP_NOT_HSPACE: + switch(next) + { + HSPACE_CASES: + return op_code == OP_NOT_HSPACE; + + default: + return op_code != OP_NOT_HSPACE; + } + + case OP_ANYNL: + case OP_VSPACE: + case OP_NOT_VSPACE: + switch(next) + { + VSPACE_CASES: + return op_code == OP_NOT_VSPACE; + + default: + return op_code != OP_NOT_VSPACE; + } + +#ifdef SUPPORT_UCP + case OP_PROP: + return check_char_prop(next, previous[0], previous[1], FALSE); + + case OP_NOTPROP: + return check_char_prop(next, previous[0], previous[1], TRUE); +#endif - case OP_VSPACE: - case OP_NOT_VSPACE: - switch(next) - { - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x85: - case 0x2028: - case 0x2029: - return op_code != OP_VSPACE; default: - return op_code == OP_VSPACE; + return FALSE; } - - default: - return FALSE; } - -/* Handle the case when the next item is \d, \s, etc. */ +/* Handle the case when the next item is \d, \s, etc. Note that when PCRE_UCP +is set, \d turns into ESC_du rather than ESC_d, etc., so ESC_d etc. are +generated only when PCRE_UCP is *not* set, that is, when only ASCII +characteristics are recognized. Similarly, the opcodes OP_DIGIT etc. are +replaced by OP_PROP codes when PCRE_UCP is set. */ switch(op_code) { case OP_CHAR: - case OP_CHARNC: -#ifdef SUPPORT_UTF8 - if (utf8 && item > 127) { GETCHAR(item, utf8_char); } -#endif - switch(-next) + case OP_CHARI: + switch(escape) { case ESC_d: - return item > 127 || (cd->ctypes[item] & ctype_digit) == 0; + return c > 255 || (cd->ctypes[c] & ctype_digit) == 0; case ESC_D: - return item <= 127 && (cd->ctypes[item] & ctype_digit) != 0; + return c <= 255 && (cd->ctypes[c] & ctype_digit) != 0; case ESC_s: - return item > 127 || (cd->ctypes[item] & ctype_space) == 0; + return c > 255 || (cd->ctypes[c] & ctype_space) == 0; case ESC_S: - return item <= 127 && (cd->ctypes[item] & ctype_space) != 0; + return c <= 255 && (cd->ctypes[c] & ctype_space) != 0; case ESC_w: - return item > 127 || (cd->ctypes[item] & ctype_word) == 0; + return c > 255 || (cd->ctypes[c] & ctype_word) == 0; case ESC_W: - return item <= 127 && (cd->ctypes[item] & ctype_word) != 0; + return c <= 255 && (cd->ctypes[c] & ctype_word) != 0; case ESC_h: case ESC_H: - switch(item) + switch(c) { - case 0x09: - case 0x20: - case 0xa0: - case 0x1680: - case 0x180e: - case 0x2000: - case 0x2001: - case 0x2002: - case 0x2003: - case 0x2004: - case 0x2005: - case 0x2006: - case 0x2007: - case 0x2008: - case 0x2009: - case 0x200A: - case 0x202f: - case 0x205f: - case 0x3000: - return -next != ESC_h; + HSPACE_CASES: + return escape != ESC_h; + default: - return -next == ESC_h; + return escape == ESC_h; } case ESC_v: case ESC_V: - switch(item) + switch(c) { - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x85: - case 0x2028: - case 0x2029: - return -next != ESC_v; + VSPACE_CASES: + return escape != ESC_v; + default: - return -next == ESC_v; + return escape == ESC_v; + } + + /* When PCRE_UCP is set, these values get generated for \d etc. Find + their substitutions and process them. The result will always be either + ESC_p or ESC_P. Then fall through to process those values. */ + +#ifdef SUPPORT_UCP + case ESC_du: + case ESC_DU: + case ESC_wu: + case ESC_WU: + case ESC_su: + case ESC_SU: + { + int temperrorcode = 0; + ptr = substitutes[escape - ESC_DU]; + escape = check_escape(&ptr, &next, &temperrorcode, 0, options, FALSE); + if (temperrorcode != 0) return FALSE; + ptr++; /* For compatibility */ } + /* Fall through */ + + case ESC_p: + case ESC_P: + { + unsigned int ptype = 0, pdata = 0; + int errorcodeptr; + BOOL negated; + + ptr--; /* Make ptr point at the p or P */ + if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcodeptr)) + return FALSE; + ptr++; /* Point past the final curly ket */ + + /* If the property item is optional, we have to give up. (When generated + from \d etc by PCRE_UCP, this test will have been applied much earlier, + to the original \d etc. At this point, ptr will point to a zero byte. */ + + if (*ptr == CHAR_ASTERISK || *ptr == CHAR_QUESTION_MARK || + STRNCMP_UC_C8(ptr, STR_LEFT_CURLY_BRACKET STR_0 STR_COMMA, 3) == 0) + return FALSE; + + /* Do the property check. */ + + return check_char_prop(c, ptype, pdata, (escape == ESC_P) != negated); + } +#endif default: return FALSE; } + /* In principle, support for Unicode properties should be integrated here as + well. It means re-organizing the above code so as to get hold of the property + values before switching on the op-code. However, I wonder how many patterns + combine ASCII \d etc with Unicode properties? (Note that if PCRE_UCP is set, + these op-codes are never generated.) */ + case OP_DIGIT: - return next == -ESC_D || next == -ESC_s || next == -ESC_W || - next == -ESC_h || next == -ESC_v; + return escape == ESC_D || escape == ESC_s || escape == ESC_W || + escape == ESC_h || escape == ESC_v || escape == ESC_R; case OP_NOT_DIGIT: - return next == -ESC_d; + return escape == ESC_d; case OP_WHITESPACE: - return next == -ESC_S || next == -ESC_d || next == -ESC_w; + return escape == ESC_S || escape == ESC_d || escape == ESC_w; case OP_NOT_WHITESPACE: - return next == -ESC_s || next == -ESC_h || next == -ESC_v; + return escape == ESC_s || escape == ESC_h || escape == ESC_v || escape == ESC_R; case OP_HSPACE: - return next == -ESC_S || next == -ESC_H || next == -ESC_d || next == -ESC_w; + return escape == ESC_S || escape == ESC_H || escape == ESC_d || + escape == ESC_w || escape == ESC_v || escape == ESC_R; case OP_NOT_HSPACE: - return next == -ESC_h; + return escape == ESC_h; /* Can't have \S in here because VT matches \S (Perl anomaly) */ + case OP_ANYNL: case OP_VSPACE: - return next == -ESC_V || next == -ESC_d || next == -ESC_w; + return escape == ESC_V || escape == ESC_d || escape == ESC_w; case OP_NOT_VSPACE: - return next == -ESC_v; + return escape == ESC_v || escape == ESC_R; case OP_WORDCHAR: - return next == -ESC_W || next == -ESC_s || next == -ESC_h || next == -ESC_v; + return escape == ESC_W || escape == ESC_s || escape == ESC_h || + escape == ESC_v || escape == ESC_R; case OP_NOT_WORDCHAR: - return next == -ESC_w || next == -ESC_d; + return escape == ESC_w || escape == ESC_d; default: return FALSE; @@ -2322,6 +3422,244 @@ switch(op_code) +/************************************************* +* Add a character or range to a class * +*************************************************/ + +/* This function packages up the logic of adding a character or range of +characters to a class. The character values in the arguments will be within the +valid values for the current mode (8-bit, 16-bit, UTF, etc). This function is +mutually recursive with the function immediately below. + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cd contains pointers to tables etc. + start start of range character + end end of range character + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static int +add_to_class(pcre_uint8 *classbits, pcre_uchar **uchardptr, int options, + compile_data *cd, pcre_uint32 start, pcre_uint32 end) +{ +pcre_uint32 c; +int n8 = 0; + +/* If caseless matching is required, scan the range and process alternate +cases. In Unicode, there are 8-bit characters that have alternate cases that +are greater than 255 and vice-versa. Sometimes we can just extend the original +range. */ + +if ((options & PCRE_CASELESS) != 0) + { +#ifdef SUPPORT_UCP + if ((options & PCRE_UTF8) != 0) + { + int rc; + pcre_uint32 oc, od; + + options &= ~PCRE_CASELESS; /* Remove for recursive calls */ + c = start; + + while ((rc = get_othercase_range(&c, end, &oc, &od)) >= 0) + { + /* Handle a single character that has more than one other case. */ + + if (rc > 0) n8 += add_list_to_class(classbits, uchardptr, options, cd, + PRIV(ucd_caseless_sets) + rc, oc); + + /* Do nothing if the other case range is within the original range. */ + + else if (oc >= start && od <= end) continue; + + /* Extend the original range if there is overlap, noting that if oc < c, we + can't have od > end because a subrange is always shorter than the basic + range. Otherwise, use a recursive call to add the additional range. */ + + else if (oc < start && od >= start - 1) start = oc; /* Extend downwards */ + else if (od > end && oc <= end + 1) end = od; /* Extend upwards */ + else n8 += add_to_class(classbits, uchardptr, options, cd, oc, od); + } + } + else +#endif /* SUPPORT_UCP */ + + /* Not UTF-mode, or no UCP */ + + for (c = start; c <= end && c < 256; c++) + { + SETBIT(classbits, cd->fcc[c]); + n8++; + } + } + +/* Now handle the original range. Adjust the final value according to the bit +length - this means that the same lists of (e.g.) horizontal spaces can be used +in all cases. */ + +#if defined COMPILE_PCRE8 +#ifdef SUPPORT_UTF + if ((options & PCRE_UTF8) == 0) +#endif + if (end > 0xff) end = 0xff; + +#elif defined COMPILE_PCRE16 +#ifdef SUPPORT_UTF + if ((options & PCRE_UTF16) == 0) +#endif + if (end > 0xffff) end = 0xffff; + +#endif /* COMPILE_PCRE[8|16] */ + +/* If all characters are less than 256, use the bit map. Otherwise use extra +data. */ + +if (end < 0x100) + { + for (c = start; c <= end; c++) + { + n8++; + SETBIT(classbits, c); + } + } + +else + { + pcre_uchar *uchardata = *uchardptr; + +#ifdef SUPPORT_UTF + if ((options & PCRE_UTF8) != 0) /* All UTFs use the same flag bit */ + { + if (start < end) + { + *uchardata++ = XCL_RANGE; + uchardata += PRIV(ord2utf)(start, uchardata); + uchardata += PRIV(ord2utf)(end, uchardata); + } + else if (start == end) + { + *uchardata++ = XCL_SINGLE; + uchardata += PRIV(ord2utf)(start, uchardata); + } + } + else +#endif /* SUPPORT_UTF */ + + /* Without UTF support, character values are constrained by the bit length, + and can only be > 256 for 16-bit and 32-bit libraries. */ + +#ifdef COMPILE_PCRE8 + {} +#else + if (start < end) + { + *uchardata++ = XCL_RANGE; + *uchardata++ = start; + *uchardata++ = end; + } + else if (start == end) + { + *uchardata++ = XCL_SINGLE; + *uchardata++ = start; + } +#endif + + *uchardptr = uchardata; /* Updata extra data pointer */ + } + +return n8; /* Number of 8-bit characters */ +} + + + + +/************************************************* +* Add a list of characters to a class * +*************************************************/ + +/* This function is used for adding a list of case-equivalent characters to a +class, and also for adding a list of horizontal or vertical whitespace. If the +list is in order (which it should be), ranges of characters are detected and +handled appropriately. This function is mutually recursive with the function +above. + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cd contains pointers to tables etc. + p points to row of 32-bit values, terminated by NOTACHAR + except character to omit; this is used when adding lists of + case-equivalent characters to avoid including the one we + already know about + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static int +add_list_to_class(pcre_uint8 *classbits, pcre_uchar **uchardptr, int options, + compile_data *cd, const pcre_uint32 *p, unsigned int except) +{ +int n8 = 0; +while (p[0] < NOTACHAR) + { + int n = 0; + if (p[0] != except) + { + while(p[n+1] == p[0] + n + 1) n++; + n8 += add_to_class(classbits, uchardptr, options, cd, p[0], p[n]); + } + p += n + 1; + } +return n8; +} + + + +/************************************************* +* Add characters not in a list to a class * +*************************************************/ + +/* This function is used for adding the complement of a list of horizontal or +vertical whitespace to a class. The list must be in order. + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cd contains pointers to tables etc. + p points to row of 32-bit values, terminated by NOTACHAR + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static int +add_not_list_to_class(pcre_uint8 *classbits, pcre_uchar **uchardptr, + int options, compile_data *cd, const pcre_uint32 *p) +{ +BOOL utf = (options & PCRE_UTF8) != 0; +int n8 = 0; +if (p[0] > 0) + n8 += add_to_class(classbits, uchardptr, options, cd, 0, p[0] - 1); +while (p[0] < NOTACHAR) + { + while (p[1] == p[0] + 1) p++; + n8 += add_to_class(classbits, uchardptr, options, cd, p[0] + 1, + (p[1] == NOTACHAR) ? (utf ? 0x10ffffu : 0xffffffffu) : p[1] - 1); + p++; + } +return n8; +} + + + /************************************************* * Compile one branch * *************************************************/ @@ -2337,9 +3675,12 @@ Arguments: codeptr points to the pointer to the current code point ptrptr points to the current pattern pointer errorcodeptr points to error code variable - firstbyteptr set to initial literal character, or < 0 (REQ_UNSET, REQ_NONE) - reqbyteptr set to the last literal character required, else < 0 + firstcharptr place to put the first required character + firstcharflagsptr place to put the first character flags, or a negative number + reqcharptr place to put the last required character + reqcharflagsptr place to put the last required character flags, or a negative number bcptr points to current branch chain + cond_depth conditional nesting depth cd contains pointers to tables etc. lengthptr NULL during the real compile phase points to length accumulator during pre-compile phase @@ -2349,46 +3690,67 @@ Returns: TRUE on success */ static BOOL -compile_branch(int *optionsptr, uschar **codeptr, const uschar **ptrptr, - int *errorcodeptr, int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr, +compile_branch(int *optionsptr, pcre_uchar **codeptr, + const pcre_uchar **ptrptr, int *errorcodeptr, + pcre_uint32 *firstcharptr, pcre_int32 *firstcharflagsptr, + pcre_uint32 *reqcharptr, pcre_int32 *reqcharflagsptr, + branch_chain *bcptr, int cond_depth, compile_data *cd, int *lengthptr) { int repeat_type, op_type; int repeat_min = 0, repeat_max = 0; /* To please picky compilers */ int bravalue = 0; int greedy_default, greedy_non_default; -int firstbyte, reqbyte; -int zeroreqbyte, zerofirstbyte; -int req_caseopt, reqvary, tempreqvary; -int options = *optionsptr; +pcre_uint32 firstchar, reqchar; +pcre_int32 firstcharflags, reqcharflags; +pcre_uint32 zeroreqchar, zerofirstchar; +pcre_int32 zeroreqcharflags, zerofirstcharflags; +pcre_int32 req_caseopt, reqvary, tempreqvary; +int options = *optionsptr; /* May change dynamically */ int after_manual_callout = 0; int length_prevgroup = 0; -register int c; -register uschar *code = *codeptr; -uschar *last_code = code; -uschar *orig_code = code; -uschar *tempcode; +register pcre_uint32 c; +int escape; +register pcre_uchar *code = *codeptr; +pcre_uchar *last_code = code; +pcre_uchar *orig_code = code; +pcre_uchar *tempcode; BOOL inescq = FALSE; -BOOL groupsetfirstbyte = FALSE; -const uschar *ptr = *ptrptr; -const uschar *tempptr; -uschar *previous = NULL; -uschar *previous_callout = NULL; -uschar *save_hwm = NULL; -uschar classbits[32]; - -#ifdef SUPPORT_UTF8 -BOOL class_utf8; -BOOL utf8 = (options & PCRE_UTF8) != 0; -uschar *class_utf8data; -uschar *class_utf8data_base; -uschar utf8_char[6]; +BOOL groupsetfirstchar = FALSE; +const pcre_uchar *ptr = *ptrptr; +const pcre_uchar *tempptr; +const pcre_uchar *nestptr = NULL; +pcre_uchar *previous = NULL; +pcre_uchar *previous_callout = NULL; +pcre_uchar *save_hwm = NULL; +pcre_uint8 classbits[32]; + +/* We can fish out the UTF-8 setting once and for all into a BOOL, but we +must not do this for other options (e.g. PCRE_EXTENDED) because they may change +dynamically as we process the pattern. */ + +#ifdef SUPPORT_UTF +/* PCRE_UTF[16|32] have the same value as PCRE_UTF8. */ +BOOL utf = (options & PCRE_UTF8) != 0; +#ifndef COMPILE_PCRE32 +pcre_uchar utf_chars[6]; +#endif #else -BOOL utf8 = FALSE; -uschar *utf8_char = NULL; +BOOL utf = FALSE; +#endif + +/* Helper variables for OP_XCLASS opcode (for characters > 255). We define +class_uchardata always so that it can be passed to add_to_class() always, +though it will not be used in non-UTF 8-bit cases. This avoids having to supply +alternative calls for the different cases. */ + +pcre_uchar *class_uchardata; +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 +BOOL xclass; +pcre_uchar *class_uchardata_base; #endif -#ifdef DEBUG +#ifdef PCRE_DEBUG if (lengthptr != NULL) DPRINTF((">> start branch\n")); #endif @@ -2399,22 +3761,24 @@ greedy_non_default = greedy_default ^ 1; /* Initialize no first byte, no required byte. REQ_UNSET means "no char matching encountered yet". It gets changed to REQ_NONE if we hit something that -matches a non-fixed char first char; reqbyte just remains unset if we never +matches a non-fixed char first char; reqchar just remains unset if we never find one. When we hit a repeat whose minimum is zero, we may have to adjust these values to take the zero repeat into account. This is implemented by setting them to -zerofirstbyte and zeroreqbyte when such a repeat is encountered. The individual +zerofirstbyte and zeroreqchar when such a repeat is encountered. The individual item types that can be repeated set these backoff variables appropriately. */ -firstbyte = reqbyte = zerofirstbyte = zeroreqbyte = REQ_UNSET; +firstchar = reqchar = zerofirstchar = zeroreqchar = 0; +firstcharflags = reqcharflags = zerofirstcharflags = zeroreqcharflags = REQ_UNSET; -/* The variable req_caseopt contains either the REQ_CASELESS value or zero, -according to the current setting of the caseless flag. REQ_CASELESS is a bit -value > 255. It is added into the firstbyte or reqbyte variables to record the -case status of the value. This is used only for ASCII characters. */ +/* The variable req_caseopt contains either the REQ_CASELESS value +or zero, according to the current setting of the caseless flag. The +REQ_CASELESS leaves the lower 28 bit empty. It is added into the +firstchar or reqchar variables to record the case status of the +value. This is used only for ASCII characters. */ -req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS : 0; +req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS:0; /* Switch on next character until the end of the branch */ @@ -2426,31 +3790,44 @@ for (;; ptr++) BOOL is_quantifier; BOOL is_recurse; BOOL reset_bracount; - int class_charcount; - int class_lastchar; + int class_has_8bitchar; + int class_one_char; int newoptions; int recno; int refsign; int skipbytes; - int subreqbyte; - int subfirstbyte; + pcre_uint32 subreqchar, subfirstchar; + pcre_int32 subreqcharflags, subfirstcharflags; int terminator; - int mclength; - uschar mcbuffer[8]; + unsigned int mclength; + unsigned int tempbracount; + pcre_uint32 ec; + pcre_uchar mcbuffer[8]; - /* Get next byte in the pattern */ + /* Get next character in the pattern */ c = *ptr; + /* If we are at the end of a nested substitution, revert to the outer level + string. Nesting only happens one level deep. */ + + if (c == CHAR_NULL && nestptr != NULL) + { + ptr = nestptr; + nestptr = NULL; + c = *ptr; + } + /* If we are in the pre-compile phase, accumulate the length used for the previous cycle of this loop. */ if (lengthptr != NULL) { -#ifdef DEBUG +#ifdef PCRE_DEBUG if (code > cd->hwm) cd->hwm = code; /* High water info */ #endif - if (code > cd->start_workspace + WORK_SIZE_CHECK) /* Check for overrun */ + if (code > cd->start_workspace + cd->workspace_size - + WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */ { *errorcodeptr = ERR52; goto FAILED; @@ -2472,8 +3849,9 @@ for (;; ptr++) goto FAILED; } - *lengthptr += code - last_code; - DPRINTF(("length=%d added %d c=%c\n", *lengthptr, code - last_code, c)); + *lengthptr += (int)(code - last_code); + DPRINTF(("length=%d added %d c=%c (0x%x)\n", *lengthptr, + (int)(code - last_code), c, c)); /* If "previous" is set and it is not at the start of the work space, move it back to there, in order to avoid filling up the work space. Otherwise, @@ -2483,7 +3861,7 @@ for (;; ptr++) { if (previous > orig_code) { - memmove(orig_code, previous, code - previous); + memmove(orig_code, previous, IN_UCHARS(code - previous)); code -= previous - orig_code; previous = orig_code; } @@ -2499,7 +3877,8 @@ for (;; ptr++) /* In the real compile phase, just check the workspace used by the forward reference list. */ - else if (cd->hwm > cd->start_workspace + WORK_SIZE_CHECK) + else if (cd->hwm > cd->start_workspace + cd->workspace_size - + WORK_SIZE_SAFETY_MARGIN) { *errorcodeptr = ERR52; goto FAILED; @@ -2507,9 +3886,9 @@ for (;; ptr++) /* If in \Q...\E, check for the end; if not, we have a literal */ - if (inescq && c != 0) + if (inescq && c != CHAR_NULL) { - if (c == '\\' && ptr[1] == 'E') + if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) { inescq = FALSE; ptr++; @@ -2535,8 +3914,9 @@ for (;; ptr++) /* Fill in length of a previous callout, except when the next thing is a quantifier. */ - is_quantifier = c == '*' || c == '+' || c == '?' || - (c == '{' && is_counted_repeat(ptr+1)); + is_quantifier = + c == CHAR_ASTERISK || c == CHAR_PLUS || c == CHAR_QUESTION_MARK || + (c == CHAR_LEFT_CURLY_BRACKET && is_counted_repeat(ptr+1)); if (!is_quantifier && previous_callout != NULL && after_manual_callout-- <= 0) @@ -2546,18 +3926,23 @@ for (;; ptr++) previous_callout = NULL; } - /* In extended mode, skip white space and comments */ + /* In extended mode, skip white space and comments. */ if ((options & PCRE_EXTENDED) != 0) { - if ((cd->ctypes[c] & ctype_space) != 0) continue; - if (c == '#') + if (MAX_255(*ptr) && (cd->ctypes[c] & ctype_space) != 0) continue; + if (c == CHAR_NUMBER_SIGN) { - while (*(++ptr) != 0) + ptr++; + while (*ptr != CHAR_NULL) { if (IS_NEWLINE(ptr)) { ptr += cd->nllen - 1; break; } + ptr++; +#ifdef SUPPORT_UTF + if (utf) FORWARDCHAR(ptr); +#endif } - if (*ptr != 0) continue; + if (*ptr != CHAR_NULL) continue; /* Else fall through to handle end of string */ c = 0; @@ -2576,10 +3961,12 @@ for (;; ptr++) { /* ===================================================================*/ case 0: /* The branch terminates at string end */ - case '|': /* or | or ) */ - case ')': - *firstbyteptr = firstbyte; - *reqbyteptr = reqbyte; + case CHAR_VERTICAL_LINE: /* or | or ) */ + case CHAR_RIGHT_PARENTHESIS: + *firstcharptr = firstchar; + *firstcharflagsptr = firstcharflags; + *reqcharptr = reqchar; + *reqcharflagsptr = reqcharflags; *codeptr = code; *ptrptr = ptr; if (lengthptr != NULL) @@ -2589,7 +3976,7 @@ for (;; ptr++) *errorcodeptr = ERR20; goto FAILED; } - *lengthptr += code - last_code; /* To include callout length */ + *lengthptr += (int)(code - last_code); /* To include callout length */ DPRINTF((">> end branch\n")); } return TRUE; @@ -2599,29 +3986,32 @@ for (;; ptr++) /* Handle single-character metacharacters. In multiline mode, ^ disables the setting of any following char as a first character. */ - case '^': + case CHAR_CIRCUMFLEX_ACCENT: + previous = NULL; if ((options & PCRE_MULTILINE) != 0) { - if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + if (firstcharflags == REQ_UNSET) firstcharflags = REQ_NONE; + *code++ = OP_CIRCM; } - previous = NULL; - *code++ = OP_CIRC; + else *code++ = OP_CIRC; break; - case '$': + case CHAR_DOLLAR_SIGN: previous = NULL; - *code++ = OP_DOLL; + *code++ = ((options & PCRE_MULTILINE) != 0)? OP_DOLLM : OP_DOLL; break; /* There can never be a first char if '.' is first, whatever happens about - repeats. The value of reqbyte doesn't change either. */ - - case '.': - if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; - zerofirstbyte = firstbyte; - zeroreqbyte = reqbyte; + repeats. The value of reqchar doesn't change either. */ + + case CHAR_DOT: + if (firstcharflags == REQ_UNSET) firstcharflags = REQ_NONE; + zerofirstchar = firstchar; + zerofirstcharflags = firstcharflags; + zeroreqchar = reqchar; + zeroreqcharflags = reqcharflags; previous = code; - *code++ = OP_ANY; + *code++ = ((options & PCRE_DOTALL) != 0)? OP_ALLANY: OP_ANY; break; @@ -2636,18 +4026,29 @@ for (;; ptr++) opcode is compiled. It may optionally have a bit map for characters < 256, but those above are are explicitly listed afterwards. A flag byte tells whether the bitmap is present, and whether this is a negated class or not. - */ - case '[': + In JavaScript compatibility mode, an isolated ']' causes an error. In + default (Perl) mode, it is treated as a data character. */ + + case CHAR_RIGHT_SQUARE_BRACKET: + if ((cd->external_options & PCRE_JAVASCRIPT_COMPAT) != 0) + { + *errorcodeptr = ERR64; + goto FAILED; + } + goto NORMAL_CHAR; + + case CHAR_LEFT_SQUARE_BRACKET: previous = code; /* PCRE supports POSIX class stuff inside a class. Perl gives an error if they are encountered at the top level, so we'll do that too. */ - if ((ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') && + if ((ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || + ptr[1] == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, &tempptr)) { - *errorcodeptr = (ptr[1] == ':')? ERR13 : ERR31; + *errorcodeptr = (ptr[1] == CHAR_COLON)? ERR13 : ERR31; goto FAILED; } @@ -2659,75 +4060,97 @@ for (;; ptr++) for (;;) { c = *(++ptr); - if (c == '\\') + if (c == CHAR_BACKSLASH) { - if (ptr[1] == 'E') ptr++; - else if (strncmp((const char *)ptr+1, "Q\\E", 3) == 0) ptr += 3; - else break; + if (ptr[1] == CHAR_E) + ptr++; + else if (STRNCMP_UC_C8(ptr + 1, STR_Q STR_BACKSLASH STR_E, 3) == 0) + ptr += 3; + else + break; } - else if (!negate_class && c == '^') + else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) negate_class = TRUE; else break; } + /* Empty classes are allowed in JavaScript compatibility mode. Otherwise, + an initial ']' is taken as a data character -- the code below handles + that. In JS mode, [] must always fail, so generate OP_FAIL, whereas + [^] must match any character, so generate OP_ALLANY. */ + + if (c == CHAR_RIGHT_SQUARE_BRACKET && + (cd->external_options & PCRE_JAVASCRIPT_COMPAT) != 0) + { + *code++ = negate_class? OP_ALLANY : OP_FAIL; + if (firstcharflags == REQ_UNSET) firstcharflags = REQ_NONE; + zerofirstchar = firstchar; + zerofirstcharflags = firstcharflags; + break; + } + /* If a class contains a negative special such as \S, we need to flip the negation flag at the end, so that support for characters > 255 works correctly (they are all included in the class). */ should_flip_negation = FALSE; - /* Keep a count of chars with values < 256 so that we can optimize the case - of just a single character (as long as it's < 256). However, For higher - valued UTF-8 characters, we don't yet do any optimization. */ + /* For optimization purposes, we track some properties of the class: + class_has_8bitchar will be non-zero if the class contains at least one < + 256 character; class_one_char will be 1 if the class contains just one + character. */ - class_charcount = 0; - class_lastchar = -1; + class_has_8bitchar = 0; + class_one_char = 0; /* Initialize the 32-char bit map to all zeros. We build the map in a - temporary bit of memory, in case the class contains only 1 character (less - than 256), because in that case the compiled code doesn't use the bit map. - */ + temporary bit of memory, in case the class contains fewer than two + 8-bit characters because in that case the compiled code doesn't use the bit + map. */ - memset(classbits, 0, 32 * sizeof(uschar)); + memset(classbits, 0, 32 * sizeof(pcre_uint8)); -#ifdef SUPPORT_UTF8 - class_utf8 = FALSE; /* No chars >= 256 */ - class_utf8data = code + LINK_SIZE + 2; /* For UTF-8 items */ - class_utf8data_base = class_utf8data; /* For resetting in pass 1 */ +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + xclass = FALSE; + class_uchardata = code + LINK_SIZE + 2; /* For XCLASS items */ + class_uchardata_base = class_uchardata; /* Save the start */ #endif /* Process characters until ] is reached. By writing this as a "do" it means that an initial ] is taken as a data character. At the start of the loop, c contains the first byte of the character. */ - if (c != 0) do + if (c != CHAR_NULL) do { - const uschar *oldptr; + const pcre_uchar *oldptr; -#ifdef SUPPORT_UTF8 - if (utf8 && c > 127) +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(c)) { /* Braces are required because the */ GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */ } +#endif - /* In the pre-compile phase, accumulate the length of any UTF-8 extra +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + /* In the pre-compile phase, accumulate the length of any extra data and reset the pointer. This is so that very large classes that - contain a zillion UTF-8 characters no longer overwrite the work space - (which is on the stack). */ + contain a zillion > 255 characters no longer overwrite the work space + (which is on the stack). We have to remember that there was XCLASS data, + however. */ - if (lengthptr != NULL) + if (lengthptr != NULL && class_uchardata > class_uchardata_base) { - *lengthptr += class_utf8data - class_utf8data_base; - class_utf8data = class_utf8data_base; + xclass = TRUE; + *lengthptr += class_uchardata - class_uchardata_base; + class_uchardata = class_uchardata_base; } - #endif /* Inside \Q...\E everything is literal except \E */ if (inescq) { - if (c == '\\' && ptr[1] == 'E') /* If we are at \E */ + if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) /* If we are at \E */ { inescq = FALSE; /* Reset literal state */ ptr++; /* Skip the 'E' */ @@ -2742,30 +4165,30 @@ for (;; ptr++) [.ch.] and [=ch=] ("collating elements") and fault them, as Perl 5.6 and 5.8 do. */ - if (c == '[' && - (ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') && - check_posix_syntax(ptr, &tempptr)) + if (c == CHAR_LEFT_SQUARE_BRACKET && + (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || + ptr[1] == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, &tempptr)) { BOOL local_negate = FALSE; int posix_class, taboffset, tabopt; - register const uschar *cbits = cd->cbits; - uschar pbits[32]; + register const pcre_uint8 *cbits = cd->cbits; + pcre_uint8 pbits[32]; - if (ptr[1] != ':') + if (ptr[1] != CHAR_COLON) { *errorcodeptr = ERR31; goto FAILED; } ptr += 2; - if (*ptr == '^') + if (*ptr == CHAR_CIRCUMFLEX_ACCENT) { local_negate = TRUE; should_flip_negation = TRUE; /* Note negative special */ ptr++; } - posix_class = check_posix_name(ptr, tempptr - ptr); + posix_class = check_posix_name(ptr, (int)(tempptr - ptr)); if (posix_class < 0) { *errorcodeptr = ERR30; @@ -2779,17 +4202,32 @@ for (;; ptr++) if ((options & PCRE_CASELESS) != 0 && posix_class <= 2) posix_class = 0; - /* We build the bit map for the POSIX class in a chunk of local store - because we may be adding and subtracting from it, and we don't want to - subtract bits that may be in the main map already. At the end we or the - result into the bit map that is being built. */ + /* When PCRE_UCP is set, some of the POSIX classes are converted to + different escape sequences that use Unicode properties. */ + +#ifdef SUPPORT_UCP + if ((options & PCRE_UCP) != 0) + { + int pc = posix_class + ((local_negate)? POSIX_SUBSIZE/2 : 0); + if (posix_substitutes[pc] != NULL) + { + nestptr = tempptr + 1; + ptr = posix_substitutes[pc] - 1; + continue; + } + } +#endif + /* In the non-UCP case, we build the bit map for the POSIX class in a + chunk of local store because we may be adding and subtracting from it, + and we don't want to subtract bits that may be in the main map already. + At the end we or the result into the bit map that is being built. */ posix_class *= 3; /* Copy in the first table (always present) */ memcpy(pbits, cbits + posix_class_maps[posix_class], - 32 * sizeof(uschar)); + 32 * sizeof(pcre_uint8)); /* If there is a second table, add or remove it as required. */ @@ -2804,7 +4242,7 @@ for (;; ptr++) for (c = 0; c < 32; c++) pbits[c] &= ~cbits[c + taboffset]; } - /* Not see if we need to remove any special characters. An option + /* Now see if we need to remove any special characters. An option value of 1 removes vertical space and 2 removes underscore. */ if (tabopt < 0) tabopt = -tabopt; @@ -2820,45 +4258,67 @@ for (;; ptr++) for (c = 0; c < 32; c++) classbits[c] |= pbits[c]; ptr = tempptr + 1; - class_charcount = 10; /* Set > 1; assumes more than 1 per class */ + /* Every class contains at least one < 256 character. */ + class_has_8bitchar = 1; + /* Every class contains at least two characters. */ + class_one_char = 2; continue; /* End of POSIX syntax handling */ } /* Backslash may introduce a single character, or it may introduce one of the specials, which just set a flag. The sequence \b is a special - case. Inside a class (and only there) it is treated as backspace. - Elsewhere it marks a word boundary. Other escapes have preset maps ready - to 'or' into the one we are building. We assume they have more than one - character in them, so set class_charcount bigger than one. */ - - if (c == '\\') + case. Inside a class (and only there) it is treated as backspace. We + assume that other escapes have more than one character in them, so + speculatively set both class_has_8bitchar and class_one_char bigger + than one. Unrecognized escapes fall through and are either treated + as literal characters (by default), or are faulted if + PCRE_EXTRA is set. */ + + if (c == CHAR_BACKSLASH) { - c = check_escape(&ptr, errorcodeptr, cd->bracount, options, TRUE); + escape = check_escape(&ptr, &ec, errorcodeptr, cd->bracount, options, + TRUE); if (*errorcodeptr != 0) goto FAILED; - - if (-c == ESC_b) c = '\b'; /* \b is backspace in a class */ - else if (-c == ESC_X) c = 'X'; /* \X is literal X in a class */ - else if (-c == ESC_R) c = 'R'; /* \R is literal R in a class */ - else if (-c == ESC_Q) /* Handle start of quoted string */ + if (escape == 0) c = ec; + else if (escape == ESC_b) c = CHAR_BS; /* \b is backspace in a class */ + else if (escape == ESC_N) /* \N is not supported in a class */ { - if (ptr[1] == '\\' && ptr[2] == 'E') + *errorcodeptr = ERR71; + goto FAILED; + } + else if (escape == ESC_Q) /* Handle start of quoted string */ + { + if (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E) { ptr += 2; /* avoid empty string */ } else inescq = TRUE; continue; } - else if (-c == ESC_E) continue; /* Ignore orphan \E */ + else if (escape == ESC_E) continue; /* Ignore orphan \E */ - if (c < 0) + else { - register const uschar *cbits = cd->cbits; - class_charcount += 2; /* Greater than 1 is what matters */ - - /* Save time by not doing this in the pre-compile phase. */ + register const pcre_uint8 *cbits = cd->cbits; + /* Every class contains at least two < 256 characters. */ + class_has_8bitchar++; + /* Every class contains at least two characters. */ + class_one_char += 2; - if (lengthptr == NULL) switch (-c) + switch (escape) { +#ifdef SUPPORT_UCP + case ESC_du: /* These are the values given for \d etc */ + case ESC_DU: /* when PCRE_UCP is set. We replace the */ + case ESC_wu: /* escape sequence with an appropriate \p */ + case ESC_WU: /* or \P to test Unicode properties instead */ + case ESC_su: /* of the default ASCII testing. */ + case ESC_SU: + nestptr = ptr; + ptr = substitutes[escape - ESC_DU] - 1; /* Just before substitute */ + class_has_8bitchar--; /* Undo! */ + continue; +#endif case ESC_d: for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_digit]; continue; @@ -2877,9 +4337,15 @@ for (;; ptr++) for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_word]; continue; + /* Perl 5.004 onwards omits VT from \s, but we must preserve it + if it was previously set by something earlier in the character + class. Luckily, the value of CHAR_VT is 0x0b in both ASCII and + EBCDIC, so we lazily just adjust the appropriate bit. */ + case ESC_s: - for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_space]; - classbits[1] &= ~0x08; /* Perl 5.004 onwards omits VT from \s */ + classbits[0] |= cbits[cbit_space]; + classbits[1] |= cbits[cbit_space+1] & ~0x08; + for (c = 2; c < 32; c++) classbits[c] |= cbits[c+cbit_space]; continue; case ESC_S: @@ -2888,224 +4354,118 @@ for (;; ptr++) classbits[1] |= 0x08; /* Perl 5.004 onwards omits VT from \s */ continue; - default: /* Not recognized; fall through */ - break; /* Need "default" setting to stop compiler warning. */ - } - - /* In the pre-compile phase, just do the recognition. */ - - else if (c == -ESC_d || c == -ESC_D || c == -ESC_w || - c == -ESC_W || c == -ESC_s || c == -ESC_S) continue; - - /* We need to deal with \H, \h, \V, and \v in both phases because - they use extra memory. */ + /* The rest apply in both UCP and non-UCP cases. */ - if (-c == ESC_h) - { - SETBIT(classbits, 0x09); /* VT */ - SETBIT(classbits, 0x20); /* SPACE */ - SETBIT(classbits, 0xa0); /* NSBP */ -#ifdef SUPPORT_UTF8 - if (utf8) - { - class_utf8 = TRUE; - *class_utf8data++ = XCL_SINGLE; - class_utf8data += _erts_pcre_ord2utf8(0x1680, class_utf8data); - *class_utf8data++ = XCL_SINGLE; - class_utf8data += _erts_pcre_ord2utf8(0x180e, class_utf8data); - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(0x2000, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(0x200A, class_utf8data); - *class_utf8data++ = XCL_SINGLE; - class_utf8data += _erts_pcre_ord2utf8(0x202f, class_utf8data); - *class_utf8data++ = XCL_SINGLE; - class_utf8data += _erts_pcre_ord2utf8(0x205f, class_utf8data); - *class_utf8data++ = XCL_SINGLE; - class_utf8data += _erts_pcre_ord2utf8(0x3000, class_utf8data); - } -#endif + case ESC_h: + (void)add_list_to_class(classbits, &class_uchardata, options, cd, + PRIV(hspace_list), NOTACHAR); continue; - } - if (-c == ESC_H) - { - for (c = 0; c < 32; c++) - { - int x = 0xff; - switch (c) - { - case 0x09/8: x ^= 1 << (0x09%8); break; - case 0x20/8: x ^= 1 << (0x20%8); break; - case 0xa0/8: x ^= 1 << (0xa0%8); break; - default: break; - } - classbits[c] |= x; - } + case ESC_H: + (void)add_not_list_to_class(classbits, &class_uchardata, options, + cd, PRIV(hspace_list)); + continue; -#ifdef SUPPORT_UTF8 - if (utf8) - { - class_utf8 = TRUE; - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(0x0100, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(0x167f, class_utf8data); - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(0x1681, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(0x180d, class_utf8data); - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(0x180f, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(0x1fff, class_utf8data); - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(0x200B, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(0x202e, class_utf8data); - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(0x2030, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(0x205e, class_utf8data); - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(0x2060, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(0x2fff, class_utf8data); - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(0x3001, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(0x7fffffff, class_utf8data); - } -#endif + case ESC_v: + (void)add_list_to_class(classbits, &class_uchardata, options, cd, + PRIV(vspace_list), NOTACHAR); continue; - } - if (-c == ESC_v) - { - SETBIT(classbits, 0x0a); /* LF */ - SETBIT(classbits, 0x0b); /* VT */ - SETBIT(classbits, 0x0c); /* FF */ - SETBIT(classbits, 0x0d); /* CR */ - SETBIT(classbits, 0x85); /* NEL */ -#ifdef SUPPORT_UTF8 - if (utf8) - { - class_utf8 = TRUE; - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(0x2028, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(0x2029, class_utf8data); - } -#endif + case ESC_V: + (void)add_not_list_to_class(classbits, &class_uchardata, options, + cd, PRIV(vspace_list)); continue; - } - if (-c == ESC_V) - { - for (c = 0; c < 32; c++) +#ifdef SUPPORT_UCP + case ESC_p: + case ESC_P: { - int x = 0xff; - switch (c) - { - case 0x0a/8: x ^= 1 << (0x0a%8); - x ^= 1 << (0x0b%8); - x ^= 1 << (0x0c%8); - x ^= 1 << (0x0d%8); - break; - case 0x85/8: x ^= 1 << (0x85%8); break; - default: break; - } - classbits[c] |= x; + BOOL negated; + unsigned int ptype = 0, pdata = 0; + if (!get_ucp(&ptr, &negated, &ptype, &pdata, errorcodeptr)) + goto FAILED; + *class_uchardata++ = ((escape == ESC_p) != negated)? + XCL_PROP : XCL_NOTPROP; + *class_uchardata++ = ptype; + *class_uchardata++ = pdata; + class_has_8bitchar--; /* Undo! */ + continue; } +#endif + /* Unrecognized escapes are faulted if PCRE is running in its + strict mode. By default, for compatibility with Perl, they are + treated as literals. */ -#ifdef SUPPORT_UTF8 - if (utf8) + default: + if ((options & PCRE_EXTRA) != 0) { - class_utf8 = TRUE; - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(0x0100, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(0x2027, class_utf8data); - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(0x2029, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(0x7fffffff, class_utf8data); + *errorcodeptr = ERR7; + goto FAILED; } -#endif - continue; - } - - /* We need to deal with \P and \p in both phases. */ - -#ifdef SUPPORT_UCP - if (-c == ESC_p || -c == ESC_P) - { - BOOL negated; - int pdata; - int ptype = get_ucp(&ptr, &negated, &pdata, errorcodeptr); - if (ptype < 0) goto FAILED; - class_utf8 = TRUE; - *class_utf8data++ = ((-c == ESC_p) != negated)? - XCL_PROP : XCL_NOTPROP; - *class_utf8data++ = ptype; - *class_utf8data++ = pdata; - class_charcount -= 2; /* Not a < 256 character */ - continue; - } -#endif - /* Unrecognized escapes are faulted if PCRE is running in its - strict mode. By default, for compatibility with Perl, they are - treated as literals. */ - - if ((options & PCRE_EXTRA) != 0) - { - *errorcodeptr = ERR7; - goto FAILED; + class_has_8bitchar--; /* Undo the speculative increase. */ + class_one_char -= 2; /* Undo the speculative increase. */ + c = *ptr; /* Get the final character and fall through */ + break; } - - class_charcount -= 2; /* Undo the default count from above */ - c = *ptr; /* Get the final character and fall through */ } - /* Fall through if we have a single character (c >= 0). This may be - greater than 256 in UTF-8 mode. */ + /* Fall through if the escape just defined a single character (c >= 0). + This may be greater than 256. */ + + escape = 0; } /* End of backslash handling */ - /* A single character may be followed by '-' to form a range. However, - Perl does not permit ']' to be the end of the range. A '-' character - at the end is treated as a literal. Perl ignores orphaned \E sequences - entirely. The code for handling \Q and \E is messy. */ + /* A character may be followed by '-' to form a range. However, Perl does + not permit ']' to be the end of the range. A '-' character at the end is + treated as a literal. Perl ignores orphaned \E sequences entirely. The + code for handling \Q and \E is messy. */ CHECK_RANGE: - while (ptr[1] == '\\' && ptr[2] == 'E') + while (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E) { inescq = FALSE; ptr += 2; } - oldptr = ptr; - /* Remember \r or \n */ + /* Remember if \r or \n were explicitly used */ - if (c == '\r' || c == '\n') cd->external_flags |= PCRE_HASCRORLF; + if (c == CHAR_CR || c == CHAR_NL) cd->external_flags |= PCRE_HASCRORLF; /* Check for range */ - if (!inescq && ptr[1] == '-') + if (!inescq && ptr[1] == CHAR_MINUS) { - int d; + pcre_uint32 d; ptr += 2; - while (*ptr == '\\' && ptr[1] == 'E') ptr += 2; + while (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_E) ptr += 2; /* If we hit \Q (not followed by \E) at this point, go into escaped mode. */ - while (*ptr == '\\' && ptr[1] == 'Q') + while (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_Q) { ptr += 2; - if (*ptr == '\\' && ptr[1] == 'E') { ptr += 2; continue; } + if (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_E) + { ptr += 2; continue; } inescq = TRUE; break; } - if (*ptr == 0 || (!inescq && *ptr == ']')) + /* Minus (hyphen) at the end of a class is treated as a literal, so put + back the pointer and jump to handle the character that preceded it. */ + + if (*ptr == CHAR_NULL || (!inescq && *ptr == CHAR_RIGHT_SQUARE_BRACKET)) { ptr = oldptr; - goto LONE_SINGLE_CHARACTER; + goto CLASS_SINGLE_CHARACTER; } -#ifdef SUPPORT_UTF8 - if (utf8) + /* Otherwise, we have a potential range; pick up the next character */ + +#ifdef SUPPORT_UTF + if (utf) { /* Braces are required because the */ GETCHARLEN(d, ptr, ptr); /* macro generates multiple statements */ } @@ -3117,324 +4477,231 @@ for (;; ptr++) not any of the other escapes. Perl 5.6 treats a hyphen as a literal in such circumstances. */ - if (!inescq && d == '\\') + if (!inescq && d == CHAR_BACKSLASH) { - d = check_escape(&ptr, errorcodeptr, cd->bracount, options, TRUE); + int descape; + descape = check_escape(&ptr, &d, errorcodeptr, cd->bracount, options, TRUE); if (*errorcodeptr != 0) goto FAILED; - /* \b is backspace; \X is literal X; \R is literal R; any other - special means the '-' was literal */ + /* \b is backspace; any other special means the '-' was literal. */ - if (d < 0) + if (descape != 0) { - if (d == -ESC_b) d = '\b'; - else if (d == -ESC_X) d = 'X'; - else if (d == -ESC_R) d = 'R'; else + if (descape == ESC_b) d = CHAR_BS; else { ptr = oldptr; - goto LONE_SINGLE_CHARACTER; /* A few lines below */ + goto CLASS_SINGLE_CHARACTER; /* A few lines below */ } } } /* Check that the two values are in the correct order. Optimize - one-character ranges */ + one-character ranges. */ if (d < c) { *errorcodeptr = ERR8; goto FAILED; - } - - if (d == c) goto LONE_SINGLE_CHARACTER; /* A few lines below */ - - /* Remember \r or \n */ - - if (d == '\r' || d == '\n') cd->external_flags |= PCRE_HASCRORLF; - - /* In UTF-8 mode, if the upper limit is > 255, or > 127 for caseless - matching, we have to use an XCLASS with extra data items. Caseless - matching for characters > 127 is available only if UCP support is - available. */ - -#ifdef SUPPORT_UTF8 - if (utf8 && (d > 255 || ((options & PCRE_CASELESS) != 0 && d > 127))) - { - class_utf8 = TRUE; - - /* With UCP support, we can find the other case equivalents of - the relevant characters. There may be several ranges. Optimize how - they fit with the basic range. */ - -#ifdef SUPPORT_UCP - if ((options & PCRE_CASELESS) != 0) - { - unsigned int occ, ocd; - unsigned int cc = c; - unsigned int origd = d; - while (get_othercase_range(&cc, origd, &occ, &ocd)) - { - if (occ >= (unsigned int)c && - ocd <= (unsigned int)d) - continue; /* Skip embedded ranges */ - - if (occ < (unsigned int)c && - ocd >= (unsigned int)c - 1) /* Extend the basic range */ - { /* if there is overlap, */ - c = occ; /* noting that if occ < c */ - continue; /* we can't have ocd > d */ - } /* because a subrange is */ - if (ocd > (unsigned int)d && - occ <= (unsigned int)d + 1) /* always shorter than */ - { /* the basic range. */ - d = ocd; - continue; - } - - if (occ == ocd) - { - *class_utf8data++ = XCL_SINGLE; - } - else - { - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(occ, class_utf8data); - } - class_utf8data += _erts_pcre_ord2utf8(ocd, class_utf8data); - } - } -#endif /* SUPPORT_UCP */ - - /* Now record the original range, possibly modified for UCP caseless - overlapping ranges. */ - - *class_utf8data++ = XCL_RANGE; - class_utf8data += _erts_pcre_ord2utf8(c, class_utf8data); - class_utf8data += _erts_pcre_ord2utf8(d, class_utf8data); - - /* With UCP support, we are done. Without UCP support, there is no - caseless matching for UTF-8 characters > 127; we can use the bit map - for the smaller ones. */ - -#ifdef SUPPORT_UCP - continue; /* With next character in the class */ -#else - if ((options & PCRE_CASELESS) == 0 || c > 127) continue; - - /* Adjust upper limit and fall through to set up the map */ - - d = 127; - -#endif /* SUPPORT_UCP */ - } -#endif /* SUPPORT_UTF8 */ - - /* We use the bit map for all cases when not in UTF-8 mode; else - ranges that lie entirely within 0-127 when there is UCP support; else - for partial ranges without UCP support. */ - - class_charcount += d - c + 1; - class_lastchar = d; - - /* We can save a bit of time by skipping this in the pre-compile. */ - - if (lengthptr == NULL) for (; c <= d; c++) - { - classbits[c/8] |= (1 << (c&7)); - if ((options & PCRE_CASELESS) != 0) - { - int uc = cd->fcc[c]; /* flip case */ - classbits[uc/8] |= (1 << (uc&7)); - } - } - - continue; /* Go get the next char in the class */ - } + } + if (d == c) goto CLASS_SINGLE_CHARACTER; /* A few lines below */ - /* Handle a lone single character - we can get here for a normal - non-escape char, or after \ that introduces a single character or for an - apparent range that isn't. */ + /* We have found a character range, so single character optimizations + cannot be done anymore. Any value greater than 1 indicates that there + is more than one character. */ - LONE_SINGLE_CHARACTER: + class_one_char = 2; - /* Handle a character that cannot go in the bit map */ + /* Remember an explicit \r or \n, and add the range to the class. */ -#ifdef SUPPORT_UTF8 - if (utf8 && (c > 255 || ((options & PCRE_CASELESS) != 0 && c > 127))) - { - class_utf8 = TRUE; - *class_utf8data++ = XCL_SINGLE; - class_utf8data += _erts_pcre_ord2utf8(c, class_utf8data); + if (d == CHAR_CR || d == CHAR_NL) cd->external_flags |= PCRE_HASCRORLF; -#ifdef SUPPORT_UCP - if ((options & PCRE_CASELESS) != 0) - { - unsigned int othercase; - if ((othercase = _erts_pcre_ucp_othercase(c)) != NOTACHAR) - { - *class_utf8data++ = XCL_SINGLE; - class_utf8data += _erts_pcre_ord2utf8(othercase, class_utf8data); - } - } -#endif /* SUPPORT_UCP */ + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cd, c, d); + continue; /* Go get the next char in the class */ } - else -#endif /* SUPPORT_UTF8 */ - /* Handle a single-byte character */ + /* Handle a single character - we can get here for a normal non-escape + char, or after \ that introduces a single character or for an apparent + range that isn't. Only the value 1 matters for class_one_char, so don't + increase it if it is already 2 or more ... just in case there's a class + with a zillion characters in it. */ + + CLASS_SINGLE_CHARACTER: + if (class_one_char < 2) class_one_char++; + + /* If class_one_char is 1, we have the first single character in the + class, and there have been no prior ranges, or XCLASS items generated by + escapes. If this is the final character in the class, we can optimize by + turning the item into a 1-character OP_CHAR[I] if it's positive, or + OP_NOT[I] if it's negative. In the positive case, it can cause firstchar + to be set. Otherwise, there can be no first char if this item is first, + whatever repeat count may follow. In the case of reqchar, save the + previous value for reinstating. */ + + if (class_one_char == 1 && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) { - classbits[c/8] |= (1 << (c&7)); - if ((options & PCRE_CASELESS) != 0) + ptr++; + zeroreqchar = reqchar; + zeroreqcharflags = reqcharflags; + + if (negate_class) { - c = cd->fcc[c]; /* flip case */ - classbits[c/8] |= (1 << (c&7)); - } - class_charcount++; - class_lastchar = c; - } - } +#ifdef SUPPORT_UCP + int d; +#endif + if (firstcharflags == REQ_UNSET) firstcharflags = REQ_NONE; + zerofirstchar = firstchar; + zerofirstcharflags = firstcharflags; - /* Loop until ']' reached. This "while" is the end of the "do" above. */ + /* For caseless UTF-8 mode when UCP support is available, check + whether this character has more than one other case. If so, generate + a special OP_NOTPROP item instead of OP_NOTI. */ - while ((c = *(++ptr)) != 0 && (c != ']' || inescq)); +#ifdef SUPPORT_UCP + if (utf && (options & PCRE_CASELESS) != 0 && + (d = UCD_CASESET(c)) != 0) + { + *code++ = OP_NOTPROP; + *code++ = PT_CLIST; + *code++ = d; + } + else +#endif + /* Char has only one other case, or UCP not available */ - if (c == 0) /* Missing terminating ']' */ - { - *errorcodeptr = ERR6; - goto FAILED; - } + { + *code++ = ((options & PCRE_CASELESS) != 0)? OP_NOTI: OP_NOT; +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf && c > MAX_VALUE_FOR_SINGLE_CHAR) + code += PRIV(ord2utf)(c, code); + else +#endif + *code++ = c; + } + /* We are finished with this character class */ -/* This code has been disabled because it would mean that \s counts as -an explicit \r or \n reference, and that's not really what is wanted. Now -we set the flag only if there is a literal "\r" or "\n" in the class. */ + goto END_CLASS; + } -#if 0 - /* Remember whether \r or \n are in this class */ + /* For a single, positive character, get the value into mcbuffer, and + then we can handle this with the normal one-character code. */ - if (negate_class) - { - if ((classbits[1] & 0x24) != 0x24) cd->external_flags |= PCRE_HASCRORLF; - } - else - { - if ((classbits[1] & 0x24) != 0) cd->external_flags |= PCRE_HASCRORLF; - } +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf && c > MAX_VALUE_FOR_SINGLE_CHAR) + mclength = PRIV(ord2utf)(c, mcbuffer); + else #endif + { + mcbuffer[0] = c; + mclength = 1; + } + goto ONE_CHAR; + } /* End of 1-char optimization */ + /* There is more than one character in the class, or an XCLASS item + has been generated. Add this character to the class. */ - /* If class_charcount is 1, we saw precisely one character whose value is - less than 256. As long as there were no characters >= 128 and there was no - use of \p or \P, in other words, no use of any XCLASS features, we can - optimize. - - In UTF-8 mode, we can optimize the negative case only if there were no - characters >= 128 because OP_NOT and the related opcodes like OP_NOTSTAR - operate on single-bytes only. This is an historical hangover. Maybe one day - we can tidy these opcodes to handle multi-byte characters. + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cd, c, c); + } - The optimization throws away the bit map. We turn the item into a - 1-character OP_CHAR[NC] if it's positive, or OP_NOT if it's negative. Note - that OP_NOT does not support multibyte characters. In the positive case, it - can cause firstbyte to be set. Otherwise, there can be no first char if - this item is first, whatever repeat count may follow. In the case of - reqbyte, save the previous value for reinstating. */ + /* Loop until ']' reached. This "while" is the end of the "do" far above. + If we are at the end of an internal nested string, revert to the outer + string. */ -#ifdef SUPPORT_UTF8 - if (class_charcount == 1 && !class_utf8 && - (!utf8 || !negate_class || class_lastchar < 128)) -#else - if (class_charcount == 1) -#endif - { - zeroreqbyte = reqbyte; + while (((c = *(++ptr)) != CHAR_NULL || + (nestptr != NULL && + (ptr = nestptr, nestptr = NULL, c = *(++ptr)) != CHAR_NULL)) && + (c != CHAR_RIGHT_SQUARE_BRACKET || inescq)); - /* The OP_NOT opcode works on one-byte characters only. */ + /* Check for missing terminating ']' */ - if (negate_class) - { - if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; - zerofirstbyte = firstbyte; - *code++ = OP_NOT; - *code++ = class_lastchar; - break; - } + if (c == CHAR_NULL) + { + *errorcodeptr = ERR6; + goto FAILED; + } - /* For a single, positive character, get the value into mcbuffer, and - then we can handle this with the normal one-character code. */ + /* We will need an XCLASS if data has been placed in class_uchardata. In + the second phase this is a sufficient test. However, in the pre-compile + phase, class_uchardata gets emptied to prevent workspace overflow, so it + only if the very last character in the class needs XCLASS will it contain + anything at this point. For this reason, xclass gets set TRUE above when + uchar_classdata is emptied, and that's why this code is the way it is here + instead of just doing a test on class_uchardata below. */ -#ifdef SUPPORT_UTF8 - if (utf8 && class_lastchar > 127) - mclength = _erts_pcre_ord2utf8(class_lastchar, mcbuffer); - else +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + if (class_uchardata > class_uchardata_base) xclass = TRUE; #endif - { - mcbuffer[0] = class_lastchar; - mclength = 1; - } - goto ONE_CHAR; - } /* End of 1-char optimization */ - /* The general case - not the one-char optimization. If this is the first - thing in the branch, there can be no first char setting, whatever the - repeat count. Any reqbyte setting must remain unchanged after any kind of - repeat. */ + /* If this is the first thing in the branch, there can be no first char + setting, whatever the repeat count. Any reqchar setting must remain + unchanged after any kind of repeat. */ - if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; - zerofirstbyte = firstbyte; - zeroreqbyte = reqbyte; + if (firstcharflags == REQ_UNSET) firstcharflags = REQ_NONE; + zerofirstchar = firstchar; + zerofirstcharflags = firstcharflags; + zeroreqchar = reqchar; + zeroreqcharflags = reqcharflags; /* If there are characters with values > 255, we have to compile an extended class, with its own opcode, unless there was a negated special - such as \S in the class, because in that case all characters > 255 are in - the class, so any that were explicitly given as well can be ignored. If - (when there are explicit characters > 255 that must be listed) there are no - characters < 256, we can omit the bitmap in the actual compiled code. */ - -#ifdef SUPPORT_UTF8 - if (class_utf8 && !should_flip_negation) + such as \S in the class, and PCRE_UCP is not set, because in that case all + characters > 255 are in the class, so any that were explicitly given as + well can be ignored. If (when there are explicit characters > 255 that must + be listed) there are no characters < 256, we can omit the bitmap in the + actual compiled code. */ + +#ifdef SUPPORT_UTF + if (xclass && (!should_flip_negation || (options & PCRE_UCP) != 0)) +#elif !defined COMPILE_PCRE8 + if (xclass && !should_flip_negation) +#endif +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 { - *class_utf8data++ = XCL_END; /* Marks the end of extra data */ + *class_uchardata++ = XCL_END; /* Marks the end of extra data */ *code++ = OP_XCLASS; code += LINK_SIZE; - *code = negate_class? XCL_NOT : 0; + *code = negate_class? XCL_NOT:0; /* If the map is required, move up the extra data to make room for it; otherwise just move the code pointer to the end of the extra data. */ - if (class_charcount > 0) + if (class_has_8bitchar > 0) { *code++ |= XCL_MAP; - memmove(code + 32, code, class_utf8data - code); + memmove(code + (32 / sizeof(pcre_uchar)), code, + IN_UCHARS(class_uchardata - code)); memcpy(code, classbits, 32); - code = class_utf8data + 32; + code = class_uchardata + (32 / sizeof(pcre_uchar)); } - else code = class_utf8data; + else code = class_uchardata; /* Now fill in the complete length of the item */ - PUT(previous, 1, code - previous); + PUT(previous, 1, (int)(code - previous)); break; /* End of class handling */ } #endif - /* If there are no characters > 255, set the opcode to OP_CLASS or - OP_NCLASS, depending on whether the whole class was negated and whether - there were negative specials such as \S in the class. Then copy the 32-byte - map into the code vector, negating it if necessary. */ + /* If there are no characters > 255, or they are all to be included or + excluded, set the opcode to OP_CLASS or OP_NCLASS, depending on whether the + whole class was negated and whether there were negative specials such as \S + (non-UCP) in the class. Then copy the 32-byte map into the code vector, + negating it if necessary. */ *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS; - if (negate_class) - { - if (lengthptr == NULL) /* Save time in the pre-compile phase */ - for (c = 0; c < 32; c++) code[c] = ~classbits[c]; - } - else + if (lengthptr == NULL) /* Save time in the pre-compile phase */ { + if (negate_class) + for (c = 0; c < 32; c++) classbits[c] = ~classbits[c]; memcpy(code, classbits, 32); } - code += 32; + code += 32 / sizeof(pcre_uchar); + + END_CLASS: break; @@ -3442,23 +4709,23 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* Various kinds of repeat; '{' is not necessarily a quantifier, but this has been tested above. */ - case '{': + case CHAR_LEFT_CURLY_BRACKET: if (!is_quantifier) goto NORMAL_CHAR; ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorcodeptr); if (*errorcodeptr != 0) goto FAILED; goto REPEAT; - case '*': + case CHAR_ASTERISK: repeat_min = 0; repeat_max = -1; goto REPEAT; - case '+': + case CHAR_PLUS: repeat_min = 1; repeat_max = -1; goto REPEAT; - case '?': + case CHAR_QUESTION_MARK: repeat_min = 0; repeat_max = 1; @@ -3471,8 +4738,10 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ if (repeat_min == 0) { - firstbyte = zerofirstbyte; /* Adjust for zero repeat */ - reqbyte = zeroreqbyte; /* Ditto */ + firstchar = zerofirstchar; /* Adjust for zero repeat */ + firstcharflags = zerofirstcharflags; + reqchar = zeroreqchar; /* Ditto */ + reqcharflags = zeroreqcharflags; } /* Remember whether this is a variable length repeat */ @@ -3482,8 +4751,8 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ op_type = 0; /* Default single-char op codes */ possessive_quantifier = FALSE; /* Default not possessive quantifier */ - /* Save start of previous item, in case we have to move it up to make space - for an inserted OP_ONCE for the additional '+' extension. */ + /* Save start of previous item, in case we have to move it up in order to + insert something before it. */ tempcode = previous; @@ -3493,50 +4762,92 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ but if PCRE_UNGREEDY is set, it works the other way round. We change the repeat type to the non-default. */ - if (ptr[1] == '+') + if (ptr[1] == CHAR_PLUS) { repeat_type = 0; /* Force greedy */ possessive_quantifier = TRUE; ptr++; } - else if (ptr[1] == '?') + else if (ptr[1] == CHAR_QUESTION_MARK) { repeat_type = greedy_non_default; ptr++; } else repeat_type = greedy_default; - /* If previous was a character match, abolish the item and generate a - repeat item instead. If a char item has a minumum of more than one, ensure - that it is set in reqbyte - it might not be if a sequence such as x{3} is - the first thing in a branch because the x will have gone into firstbyte - instead. */ + /* If previous was a recursion call, wrap it in atomic brackets so that + previous becomes the atomic group. All recursions were so wrapped in the + past, but it no longer happens for non-repeated recursions. In fact, the + repeated ones could be re-implemented independently so as not to need this, + but for the moment we rely on the code for repeating groups. */ + + if (*previous == OP_RECURSE) + { + memmove(previous + 1 + LINK_SIZE, previous, IN_UCHARS(1 + LINK_SIZE)); + *previous = OP_ONCE; + PUT(previous, 1, 2 + 2*LINK_SIZE); + previous[2 + 2*LINK_SIZE] = OP_KET; + PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE); + code += 2 + 2 * LINK_SIZE; + length_prevgroup = 3 + 3*LINK_SIZE; + + /* When actually compiling, we need to check whether this was a forward + reference, and if so, adjust the offset. */ + + if (lengthptr == NULL && cd->hwm >= cd->start_workspace + LINK_SIZE) + { + int offset = GET(cd->hwm, -LINK_SIZE); + if (offset == previous + 1 - cd->start_code) + PUT(cd->hwm, -LINK_SIZE, offset + 1 + LINK_SIZE); + } + } + + /* Now handle repetition for the different types of item. */ - if (*previous == OP_CHAR || *previous == OP_CHARNC) + /* If previous was a character or negated character match, abolish the item + and generate a repeat item instead. If a char item has a minimum of more + than one, ensure that it is set in reqchar - it might not be if a sequence + such as x{3} is the first thing in a branch because the x will have gone + into firstchar instead. */ + + if (*previous == OP_CHAR || *previous == OP_CHARI + || *previous == OP_NOT || *previous == OP_NOTI) { - /* Deal with UTF-8 characters that take up more than one byte. It's + switch (*previous) + { + default: /* Make compiler happy. */ + case OP_CHAR: op_type = OP_STAR - OP_STAR; break; + case OP_CHARI: op_type = OP_STARI - OP_STAR; break; + case OP_NOT: op_type = OP_NOTSTAR - OP_STAR; break; + case OP_NOTI: op_type = OP_NOTSTARI - OP_STAR; break; + } + + /* Deal with UTF characters that take up more than one character. It's easier to write this out separately than try to macrify it. Use c to - hold the length of the character in bytes, plus 0x80 to flag that it's a - length rather than a small character. */ + hold the length of the character in bytes, plus UTF_LENGTH to flag that + it's a length rather than a small character. */ -#ifdef SUPPORT_UTF8 - if (utf8 && (code[-1] & 0x80) != 0) +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf && NOT_FIRSTCHAR(code[-1])) { - uschar *lastchar = code - 1; - while((*lastchar & 0xc0) == 0x80) lastchar--; - c = code - lastchar; /* Length of UTF-8 character */ - memcpy(utf8_char, lastchar, c); /* Save the char */ - c |= 0x80; /* Flag c as a length */ + pcre_uchar *lastchar = code - 1; + BACKCHAR(lastchar); + c = (int)(code - lastchar); /* Length of UTF-8 character */ + memcpy(utf_chars, lastchar, IN_UCHARS(c)); /* Save the char */ + c |= UTF_LENGTH; /* Flag c as a length */ } else -#endif - - /* Handle the case of a single byte - either with no UTF8 support, or - with UTF-8 disabled, or for a UTF-8 character < 128. */ +#endif /* SUPPORT_UTF */ + /* Handle the case of a single charater - either with no UTF support, or + with UTF disabled, or for a single character UTF character. */ { c = code[-1]; - if (repeat_min > 1) reqbyte = c | req_caseopt | cd->req_varyopt; + if (*previous <= OP_CHARI && repeat_min > 1) + { + reqchar = c; + reqcharflags = req_caseopt | cd->req_varyopt; + } } /* If the repetition is unlimited, it pays to see if the next thing on @@ -3546,8 +4857,7 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ if (!possessive_quantifier && repeat_max < 0 && - check_auto_possessive(*previous, c, utf8, utf8_char, ptr + 1, - options, cd)) + check_auto_possessive(previous, utf, ptr + 1, options, cd)) { repeat_type = 0; /* Force greedy */ possessive_quantifier = TRUE; @@ -3556,26 +4866,6 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ } - /* If previous was a single negated character ([^a] or similar), we use - one of the special opcodes, replacing it. The code is shared with single- - character repeats by setting opt_type to add a suitable offset into - repeat_type. We can also test for auto-possessification. OP_NOT is - currently used only for single-byte chars. */ - - else if (*previous == OP_NOT) - { - op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */ - c = previous[1]; - if (!possessive_quantifier && - repeat_max < 0 && - check_auto_possessive(OP_NOT, c, utf8, NULL, ptr + 1, options, cd)) - { - repeat_type = 0; /* Force greedy */ - possessive_quantifier = TRUE; - } - goto OUTPUT_SINGLE_REPEAT; - } - /* If previous was a character type match (\d or similar), abolish it and create a suitable repeat item. The code is shared with single-character repeats by setting op_type to add a suitable offset into repeat_type. Note @@ -3585,14 +4875,14 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ else if (*previous < OP_EODN) { - uschar *oldcode; + pcre_uchar *oldcode; int prop_type, prop_value; op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ c = *previous; if (!possessive_quantifier && repeat_max < 0 && - check_auto_possessive(c, 0, utf8, NULL, ptr + 1, options, cd)) + check_auto_possessive(previous, utf, ptr + 1, options, cd)) { repeat_type = 0; /* Force greedy */ possessive_quantifier = TRUE; @@ -3614,11 +4904,6 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ if (repeat_max == 0) goto END_REPEAT; - /* All real repeats make it impossible to handle partial matching (maybe - one day we will be able to remove this restriction). */ - - if (repeat_max != 1) cd->external_flags |= PCRE_NOPARTIAL; - /* Combine the op_type with the repeat_type */ repeat_type += op_type; @@ -3667,14 +4952,14 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ we have to insert the character for the previous code. For a repeated Unicode property match, there are two extra bytes that define the required property. In UTF-8 mode, long characters have their length in - c, with the 0x80 bit as a flag. */ + c, with the UTF_LENGTH bit as a flag. */ if (repeat_max < 0) { -#ifdef SUPPORT_UTF8 - if (utf8 && c >= 128) +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf && (c & UTF_LENGTH) != 0) { - memcpy(code, utf8_char, c & 7); + memcpy(code, utf_chars, IN_UCHARS(c & 7)); code += c & 7; } else @@ -3696,10 +4981,10 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ else if (repeat_max != repeat_min) { -#ifdef SUPPORT_UTF8 - if (utf8 && c >= 128) +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf && (c & UTF_LENGTH) != 0) { - memcpy(code, utf8_char, c & 7); + memcpy(code, utf_chars, IN_UCHARS(c & 7)); code += c & 7; } else @@ -3726,10 +5011,10 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* The character or character type itself comes last in all cases. */ -#ifdef SUPPORT_UTF8 - if (utf8 && c >= 128) +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf && (c & UTF_LENGTH) != 0) { - memcpy(code, utf8_char, c & 7); + memcpy(code, utf_chars, IN_UCHARS(c & 7)); code += c & 7; } else @@ -3753,10 +5038,11 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ else if (*previous == OP_CLASS || *previous == OP_NCLASS || -#ifdef SUPPORT_UTF8 +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 *previous == OP_XCLASS || #endif - *previous == OP_REF) + *previous == OP_REF || + *previous == OP_REFI) { if (repeat_max == 0) { @@ -3764,11 +5050,6 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ goto END_REPEAT; } - /* All real repeats make it impossible to handle partial matching (maybe - one day we will be able to remove this restriction). */ - - if (repeat_max != 1) cd->external_flags |= PCRE_NOPARTIAL; - if (repeat_min == 0 && repeat_max == -1) *code++ = OP_CRSTAR + repeat_type; else if (repeat_min == 1 && repeat_max == -1) @@ -3785,35 +5066,35 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ } /* If previous was a bracket group, we may have to replicate it in certain - cases. */ - - else if (*previous == OP_BRA || *previous == OP_CBRA || - *previous == OP_ONCE || *previous == OP_COND) + cases. Note that at this point we can encounter only the "basic" bracket + opcodes such as BRA and CBRA, as this is the place where they get converted + into the more special varieties such as BRAPOS and SBRA. A test for >= + OP_ASSERT and <= OP_COND includes ASSERT, ASSERT_NOT, ASSERTBACK, + ASSERTBACK_NOT, ONCE, BRA, CBRA, and COND. Originally, PCRE did not allow + repetition of assertions, but now it does, for Perl compatibility. */ + + else if (*previous >= OP_ASSERT && *previous <= OP_COND) { register int i; - int ketoffset = 0; - int len = code - previous; - uschar *bralink = NULL; + int len = (int)(code - previous); + pcre_uchar *bralink = NULL; + pcre_uchar *brazeroptr = NULL; - /* Repeating a DEFINE group is pointless */ + /* Repeating a DEFINE group is pointless, but Perl allows the syntax, so + we just ignore the repeat. */ if (*previous == OP_COND && previous[LINK_SIZE+1] == OP_DEF) - { - *errorcodeptr = ERR55; - goto FAILED; - } + goto END_REPEAT; - /* If the maximum repeat count is unlimited, find the end of the bracket - by scanning through from the start, and compute the offset back to it - from the current code pointer. There may be an OP_OPT setting following - the final KET, so we can't find the end just by going back from the code - pointer. */ + /* There is no sense in actually repeating assertions. The only potential + use of repetition is in cases when the assertion is optional. Therefore, + if the minimum is greater than zero, just ignore the repeat. If the + maximum is not not zero or one, set it to 1. */ - if (repeat_max == -1) + if (*previous < OP_ONCE) /* Assertion */ { - register uschar *ket = previous; - do ket += GET(ket, 1); while (*ket != OP_KET); - ketoffset = code - ket; + if (repeat_min > 0) goto END_REPEAT; + if (repeat_max < 0 || repeat_max > 1) repeat_max = 1; } /* The case of a zero minimum is special because of the need to stick @@ -3825,28 +5106,40 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ if (repeat_min == 0) { - /* If the maximum is also zero, we just omit the group from the output - altogether. */ - - if (repeat_max == 0) - { - code = previous; - goto END_REPEAT; - } - - /* If the maximum is 1 or unlimited, we just have to stick in the - BRAZERO and do no more at this point. However, we do need to adjust - any OP_RECURSE calls inside the group that refer to the group itself or - any internal or forward referenced group, because the offset is from - the start of the whole regex. Temporarily terminate the pattern while - doing this. */ - - if (repeat_max <= 1) + /* If the maximum is also zero, we used to just omit the group from the + output altogether, like this: + + ** if (repeat_max == 0) + ** { + ** code = previous; + ** goto END_REPEAT; + ** } + + However, that fails when a group or a subgroup within it is referenced + as a subroutine from elsewhere in the pattern, so now we stick in + OP_SKIPZERO in front of it so that it is skipped on execution. As we + don't have a list of which groups are referenced, we cannot do this + selectively. + + If the maximum is 1 or unlimited, we just have to stick in the BRAZERO + and do no more at this point. However, we do need to adjust any + OP_RECURSE calls inside the group that refer to the group itself or any + internal or forward referenced group, because the offset is from the + start of the whole regex. Temporarily terminate the pattern while doing + this. */ + + if (repeat_max <= 1) /* Covers 0, 1, and unlimited */ { *code = OP_END; - adjust_recurse(previous, 1, utf8, cd, save_hwm); - memmove(previous+1, previous, len); + adjust_recurse(previous, 1, utf, cd, save_hwm); + memmove(previous + 1, previous, IN_UCHARS(len)); code++; + if (repeat_max == 0) + { + *previous++ = OP_SKIPZERO; + goto END_REPEAT; + } + brazeroptr = previous; /* Save for possessive optimizing */ *previous++ = OP_BRAZERO + repeat_type; } @@ -3862,8 +5155,8 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ { int offset; *code = OP_END; - adjust_recurse(previous, 2 + LINK_SIZE, utf8, cd, save_hwm); - memmove(previous + 2 + LINK_SIZE, previous, len); + adjust_recurse(previous, 2 + LINK_SIZE, utf, cd, save_hwm); + memmove(previous + 2 + LINK_SIZE, previous, IN_UCHARS(len)); code += 2 + LINK_SIZE; *previous++ = OP_BRAZERO + repeat_type; *previous++ = OP_BRA; @@ -3871,7 +5164,7 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* We chain together the bracket offset fields that have to be filled in later when the ends of the brackets are reached. */ - offset = (bralink == NULL)? 0 : previous - bralink; + offset = (bralink == NULL)? 0 : (int)(previous - bralink); bralink = previous; PUTINC(previous, 0, offset); } @@ -3892,13 +5185,15 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ { /* In the pre-compile phase, we don't actually do the replication. We just adjust the length as if we had. Do some paranoid checks for - potential integer overflow. */ + potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit + integer type when available, otherwise double. */ if (lengthptr != NULL) { int delta = (repeat_min - 1)*length_prevgroup; - if ((double)(repeat_min - 1)*(double)length_prevgroup > - (double)INT_MAX || + if ((INT64_OR_DOUBLE)(repeat_min - 1)* + (INT64_OR_DOUBLE)length_prevgroup > + (INT64_OR_DOUBLE)INT_MAX || OFLOW_MAX - *lengthptr < delta) { *errorcodeptr = ERR20; @@ -3907,16 +5202,36 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ *lengthptr += delta; } - /* This is compiling for real */ + /* This is compiling for real. If there is a set first byte for + the group, and we have not yet set a "required byte", set it. Make + sure there is enough workspace for copying forward references before + doing the copy. */ else { - if (groupsetfirstbyte && reqbyte < 0) reqbyte = firstbyte; + if (groupsetfirstchar && reqcharflags < 0) + { + reqchar = firstchar; + reqcharflags = firstcharflags; + } + for (i = 1; i < repeat_min; i++) { - uschar *hc; - uschar *this_hwm = cd->hwm; - memcpy(code, previous, len); + pcre_uchar *hc; + pcre_uchar *this_hwm = cd->hwm; + memcpy(code, previous, IN_UCHARS(len)); + + while (cd->hwm > cd->start_workspace + cd->workspace_size - + WORK_SIZE_SAFETY_MARGIN - (this_hwm - save_hwm)) + { + int save_offset = save_hwm - cd->start_workspace; + int this_offset = this_hwm - cd->start_workspace; + *errorcodeptr = expand_workspace(cd); + if (*errorcodeptr != 0) goto FAILED; + save_hwm = (pcre_uchar *)cd->start_workspace + save_offset; + this_hwm = (pcre_uchar *)cd->start_workspace + this_offset; + } + for (hc = save_hwm; hc < this_hwm; hc += LINK_SIZE) { PUT(cd->hwm, 0, GET(hc, 0) + len); @@ -3944,15 +5259,16 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ just adjust the length as if we had. For each repetition we must add 1 to the length for BRAZERO and for all but the last repetition we must add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some - paranoid checks to avoid integer overflow. */ + paranoid checks to avoid integer overflow. The INT64_OR_DOUBLE type is + a 64-bit integer type when available, otherwise double. */ if (lengthptr != NULL && repeat_max > 0) { int delta = repeat_max * (length_prevgroup + 1 + 2 + 2*LINK_SIZE) - 2 - 2*LINK_SIZE; /* Last one doesn't nest */ - if ((double)repeat_max * - (double)(length_prevgroup + 1 + 2 + 2*LINK_SIZE) - > (double)INT_MAX || + if ((INT64_OR_DOUBLE)repeat_max * + (INT64_OR_DOUBLE)(length_prevgroup + 1 + 2 + 2*LINK_SIZE) + > (INT64_OR_DOUBLE)INT_MAX || OFLOW_MAX - *lengthptr < delta) { *errorcodeptr = ERR20; @@ -3965,8 +5281,8 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ else for (i = repeat_max - 1; i >= 0; i--) { - uschar *hc; - uschar *this_hwm = cd->hwm; + pcre_uchar *hc; + pcre_uchar *this_hwm = cd->hwm; *code++ = OP_BRAZERO + repeat_type; @@ -3977,12 +5293,27 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ { int offset; *code++ = OP_BRA; - offset = (bralink == NULL)? 0 : code - bralink; + offset = (bralink == NULL)? 0 : (int)(code - bralink); bralink = code; PUTINC(code, 0, offset); } - memcpy(code, previous, len); + memcpy(code, previous, IN_UCHARS(len)); + + /* Ensure there is enough workspace for forward references before + copying them. */ + + while (cd->hwm > cd->start_workspace + cd->workspace_size - + WORK_SIZE_SAFETY_MARGIN - (this_hwm - save_hwm)) + { + int save_offset = save_hwm - cd->start_workspace; + int this_offset = this_hwm - cd->start_workspace; + *errorcodeptr = expand_workspace(cd); + if (*errorcodeptr != 0) goto FAILED; + save_hwm = (pcre_uchar *)cd->start_workspace + save_offset; + this_hwm = (pcre_uchar *)cd->start_workspace + this_offset; + } + for (hc = save_hwm; hc < this_hwm; hc += LINK_SIZE) { PUT(cd->hwm, 0, GET(hc, 0) + len + ((i != 0)? 2+LINK_SIZE : 1)); @@ -3998,8 +5329,8 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ while (bralink != NULL) { int oldlinkoffset; - int offset = code - bralink + 1; - uschar *bra = code - offset; + int offset = (int)(code - bralink + 1); + pcre_uchar *bra = code - offset; oldlinkoffset = GET(bra, 1); bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset; *code++ = OP_KET; @@ -4008,39 +5339,121 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ } } - /* If the maximum is unlimited, set a repeater in the final copy. We - can't just offset backwards from the current code point, because we - don't know if there's been an options resetting after the ket. The - correct offset was computed above. + /* If the maximum is unlimited, set a repeater in the final copy. For + ONCE brackets, that's all we need to do. However, possessively repeated + ONCE brackets can be converted into non-capturing brackets, as the + behaviour of (?:xx)++ is the same as (?>xx)++ and this saves having to + deal with possessive ONCEs specially. - Then, when we are doing the actual compile phase, check to see whether - this group is a non-atomic one that could match an empty string. If so, + Otherwise, when we are doing the actual compile phase, check to see + whether this group is one that could match an empty string. If so, convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so - that runtime checking can be done. [This check is also applied to - atomic groups at runtime, but in a different way.] */ + that runtime checking can be done. [This check is also applied to ONCE + groups at runtime, but in a different way.] + + Then, if the quantifier was possessive and the bracket is not a + conditional, we convert the BRA code to the POS form, and the KET code to + KETRPOS. (It turns out to be convenient at runtime to detect this kind of + subpattern at both the start and at the end.) The use of special opcodes + makes it possible to reduce greatly the stack usage in pcre_exec(). If + the group is preceded by OP_BRAZERO, convert this to OP_BRAPOSZERO. + + Then, if the minimum number of matches is 1 or 0, cancel the possessive + flag so that the default action below, of wrapping everything inside + atomic brackets, does not happen. When the minimum is greater than 1, + there will be earlier copies of the group, and so we still have to wrap + the whole thing. */ else { - uschar *ketcode = code - ketoffset; - uschar *bracode = ketcode - GET(ketcode, 1); - *ketcode = OP_KETRMAX + repeat_type; - if (lengthptr == NULL && *bracode != OP_ONCE) + pcre_uchar *ketcode = code - 1 - LINK_SIZE; + pcre_uchar *bracode = ketcode - GET(ketcode, 1); + + /* Convert possessive ONCE brackets to non-capturing */ + + if ((*bracode == OP_ONCE || *bracode == OP_ONCE_NC) && + possessive_quantifier) *bracode = OP_BRA; + + /* For non-possessive ONCE brackets, all we need to do is to + set the KET. */ + + if (*bracode == OP_ONCE || *bracode == OP_ONCE_NC) + *ketcode = OP_KETRMAX + repeat_type; + + /* Handle non-ONCE brackets and possessive ONCEs (which have been + converted to non-capturing above). */ + + else { - uschar *scode = bracode; - do + /* In the compile phase, check for empty string matching. */ + + if (lengthptr == NULL) { - if (could_be_empty_branch(scode, ketcode, utf8)) + pcre_uchar *scode = bracode; + do { - *bracode += OP_SBRA - OP_BRA; - break; + if (could_be_empty_branch(scode, ketcode, utf, cd)) + { + *bracode += OP_SBRA - OP_BRA; + break; + } + scode += GET(scode, 1); + } + while (*scode == OP_ALT); + } + + /* Handle possessive quantifiers. */ + + if (possessive_quantifier) + { + /* For COND brackets, we wrap the whole thing in a possessively + repeated non-capturing bracket, because we have not invented POS + versions of the COND opcodes. Because we are moving code along, we + must ensure that any pending recursive references are updated. */ + + if (*bracode == OP_COND || *bracode == OP_SCOND) + { + int nlen = (int)(code - bracode); + *code = OP_END; + adjust_recurse(bracode, 1 + LINK_SIZE, utf, cd, save_hwm); + memmove(bracode + 1 + LINK_SIZE, bracode, IN_UCHARS(nlen)); + code += 1 + LINK_SIZE; + nlen += 1 + LINK_SIZE; + *bracode = OP_BRAPOS; + *code++ = OP_KETRPOS; + PUTINC(code, 0, nlen); + PUT(bracode, 1, nlen); + } + + /* For non-COND brackets, we modify the BRA code and use KETRPOS. */ + + else + { + *bracode += 1; /* Switch to xxxPOS opcodes */ + *ketcode = OP_KETRPOS; } - scode += GET(scode, 1); + + /* If the minimum is zero, mark it as possessive, then unset the + possessive flag when the minimum is 0 or 1. */ + + if (brazeroptr != NULL) *brazeroptr = OP_BRAPOSZERO; + if (repeat_min < 2) possessive_quantifier = FALSE; } - while (*scode == OP_ALT); + + /* Non-possessive quantifier */ + + else *ketcode = OP_KETRMAX + repeat_type; } } } + /* If previous is OP_FAIL, it was generated by an empty class [] in + JavaScript mode. The other ways in which OP_FAIL can be generated, that is + by (*FAIL) or (?!) set previous to NULL, which gives a "nothing to repeat" + error above. We can just ignore the repeat in JS case. */ + + else if (*previous == OP_FAIL) goto END_REPEAT; + /* Else there's some kind of shambles */ else @@ -4050,13 +5463,18 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ } /* If the character following a repeat is '+', or if certain optimization - tests above succeeded, possessive_quantifier is TRUE. For some of the - simpler opcodes, there is an special alternative opcode for this. For - anything else, we wrap the entire repeated item inside OP_ONCE brackets. - The '+' notation is just syntactic sugar, taken from Sun's Java package, - but the special opcodes can optimize it a bit. The repeated item starts at - tempcode, not at previous, which might be the first part of a string whose - (former) last char we repeated. + tests above succeeded, possessive_quantifier is TRUE. For some opcodes, + there are special alternative opcodes for this case. For anything else, we + wrap the entire repeated item inside OP_ONCE brackets. Logically, the '+' + notation is just syntactic sugar, taken from Sun's Java package, but the + special opcodes can optimize it. + + Some (but not all) possessively repeated subpatterns have already been + completely handled in the code just above. For them, possessive_quantifier + is always FALSE at this stage. + + Note that the repeated item starts at tempcode, not at previous, which + might be the first part of a string whose (former) last char we repeated. Possessifying an 'exact' quantifier has no effect, so we can ignore it. But an 'upto' may follow. We skip over an 'exact' item, and then test the @@ -4065,12 +5483,22 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ if (possessive_quantifier) { int len; - if (*tempcode == OP_EXACT || *tempcode == OP_TYPEEXACT || - *tempcode == OP_NOTEXACT) - tempcode += _erts_pcre_OP_lengths[*tempcode] + - ((*tempcode == OP_TYPEEXACT && - (tempcode[3] == OP_PROP || tempcode[3] == OP_NOTPROP))? 2:0); - len = code - tempcode; + + if (*tempcode == OP_TYPEEXACT) + tempcode += PRIV(OP_lengths)[*tempcode] + + ((tempcode[1 + IMM2_SIZE] == OP_PROP + || tempcode[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0); + + else if (*tempcode == OP_EXACT || *tempcode == OP_NOTEXACT) + { + tempcode += PRIV(OP_lengths)[*tempcode]; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(tempcode[-1])) + tempcode += GET_EXTRALEN(tempcode[-1]); +#endif + } + + len = (int)(code - tempcode); if (len > 0) switch (*tempcode) { case OP_STAR: *tempcode = OP_POSSTAR; break; @@ -4078,18 +5506,33 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ case OP_QUERY: *tempcode = OP_POSQUERY; break; case OP_UPTO: *tempcode = OP_POSUPTO; break; - case OP_TYPESTAR: *tempcode = OP_TYPEPOSSTAR; break; - case OP_TYPEPLUS: *tempcode = OP_TYPEPOSPLUS; break; - case OP_TYPEQUERY: *tempcode = OP_TYPEPOSQUERY; break; - case OP_TYPEUPTO: *tempcode = OP_TYPEPOSUPTO; break; + case OP_STARI: *tempcode = OP_POSSTARI; break; + case OP_PLUSI: *tempcode = OP_POSPLUSI; break; + case OP_QUERYI: *tempcode = OP_POSQUERYI; break; + case OP_UPTOI: *tempcode = OP_POSUPTOI; break; case OP_NOTSTAR: *tempcode = OP_NOTPOSSTAR; break; case OP_NOTPLUS: *tempcode = OP_NOTPOSPLUS; break; case OP_NOTQUERY: *tempcode = OP_NOTPOSQUERY; break; case OP_NOTUPTO: *tempcode = OP_NOTPOSUPTO; break; + case OP_NOTSTARI: *tempcode = OP_NOTPOSSTARI; break; + case OP_NOTPLUSI: *tempcode = OP_NOTPOSPLUSI; break; + case OP_NOTQUERYI: *tempcode = OP_NOTPOSQUERYI; break; + case OP_NOTUPTOI: *tempcode = OP_NOTPOSUPTOI; break; + + case OP_TYPESTAR: *tempcode = OP_TYPEPOSSTAR; break; + case OP_TYPEPLUS: *tempcode = OP_TYPEPOSPLUS; break; + case OP_TYPEQUERY: *tempcode = OP_TYPEPOSQUERY; break; + case OP_TYPEUPTO: *tempcode = OP_TYPEPOSUPTO; break; + + /* Because we are moving code along, we must ensure that any + pending recursive references are updated. */ + default: - memmove(tempcode + 1+LINK_SIZE, tempcode, len); + *code = OP_END; + adjust_recurse(tempcode, 1 + LINK_SIZE, utf, cd, save_hwm); + memmove(tempcode + 1 + LINK_SIZE, tempcode, IN_UCHARS(len)); code += 1 + LINK_SIZE; len += 1 + LINK_SIZE; tempcode[0] = OP_ONCE; @@ -4101,7 +5544,7 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ } /* In all case we no longer have a previous item. We also set the - "follows varying string" flag for subsequently encountered reqbytes if + "follows varying string" flag for subsequently encountered reqchars if it isn't already set and we have just passed a varying length item. */ END_REPEAT: @@ -4115,7 +5558,7 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ lookbehind or option setting or condition or all the other extended parenthesis forms. */ - case '(': + case CHAR_LEFT_PARENTHESIS: newoptions = options; skipbytes = 0; bravalue = OP_CBRA; @@ -4124,56 +5567,143 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* First deal with various "verbs" that can be introduced by '*'. */ - if (*(++ptr) == '*' && (cd->ctypes[ptr[1]] & ctype_letter) != 0) + ptr++; + if (ptr[0] == CHAR_ASTERISK && (ptr[1] == ':' + || (MAX_255(ptr[1]) && ((cd->ctypes[ptr[1]] & ctype_letter) != 0)))) { int i, namelen; + int arglen = 0; const char *vn = verbnames; - const uschar *name = ++ptr; + const pcre_uchar *name = ptr + 1; + const pcre_uchar *arg = NULL; previous = NULL; - while ((cd->ctypes[*++ptr] & ctype_letter) != 0); - if (*ptr == ':') + ptr++; + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_letter) != 0) ptr++; + namelen = (int)(ptr - name); + + /* It appears that Perl allows any characters whatsoever, other than + a closing parenthesis, to appear in arguments, so we no longer insist on + letters, digits, and underscores. */ + + if (*ptr == CHAR_COLON) { - *errorcodeptr = ERR59; /* Not supported */ - goto FAILED; + arg = ++ptr; + while (*ptr != CHAR_NULL && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; + arglen = (int)(ptr - arg); + if ((unsigned int)arglen > MAX_MARK) + { + *errorcodeptr = ERR75; + goto FAILED; + } } - if (*ptr != ')') + + if (*ptr != CHAR_RIGHT_PARENTHESIS) { *errorcodeptr = ERR60; goto FAILED; } - namelen = ptr - name; + + /* Scan the table of verb names */ + for (i = 0; i < verbcount; i++) { if (namelen == verbs[i].len && - strncmp((char *)name, vn, namelen) == 0) + STRNCMP_UC_C8(name, vn, namelen) == 0) { - *code = verbs[i].op; - if (*code++ == OP_ACCEPT) cd->had_accept = TRUE; - break; + int setverb; + + /* Check for open captures before ACCEPT and convert it to + ASSERT_ACCEPT if in an assertion. */ + + if (verbs[i].op == OP_ACCEPT) + { + open_capitem *oc; + if (arglen != 0) + { + *errorcodeptr = ERR59; + goto FAILED; + } + cd->had_accept = TRUE; + for (oc = cd->open_caps; oc != NULL; oc = oc->next) + { + *code++ = OP_CLOSE; + PUT2INC(code, 0, oc->number); + } + setverb = *code++ = + (cd->assert_depth > 0)? OP_ASSERT_ACCEPT : OP_ACCEPT; + + /* Do not set firstchar after *ACCEPT */ + if (firstcharflags == REQ_UNSET) firstcharflags = REQ_NONE; + } + + /* Handle other cases with/without an argument */ + + else if (arglen == 0) + { + if (verbs[i].op < 0) /* Argument is mandatory */ + { + *errorcodeptr = ERR66; + goto FAILED; + } + setverb = *code++ = verbs[i].op; + } + + else + { + if (verbs[i].op_arg < 0) /* Argument is forbidden */ + { + *errorcodeptr = ERR59; + goto FAILED; + } + setverb = *code++ = verbs[i].op_arg; + *code++ = arglen; + memcpy(code, arg, IN_UCHARS(arglen)); + code += arglen; + *code++ = 0; + } + + switch (setverb) + { + case OP_THEN: + case OP_THEN_ARG: + cd->external_flags |= PCRE_HASTHEN; + break; + + case OP_PRUNE: + case OP_PRUNE_ARG: + case OP_SKIP: + case OP_SKIP_ARG: + cd->had_pruneorskip = TRUE; + break; + } + + break; /* Found verb, exit loop */ } + vn += verbs[i].len + 1; } - if (i < verbcount) continue; - *errorcodeptr = ERR60; + + if (i < verbcount) continue; /* Successfully handled a verb */ + *errorcodeptr = ERR60; /* Verb not recognized */ goto FAILED; } /* Deal with the extended parentheses; all are introduced by '?', and the appearance of any of them means that this is not a capturing group. */ - else if (*ptr == '?') + else if (*ptr == CHAR_QUESTION_MARK) { int i, set, unset, namelen; int *optset; - const uschar *name; - uschar *slot; + const pcre_uchar *name; + pcre_uchar *slot; switch (*(++ptr)) { - case '#': /* Comment; skip to ket */ + case CHAR_NUMBER_SIGN: /* Comment; skip to ket */ ptr++; - while (*ptr != 0 && *ptr != ')') ptr++; - if (*ptr == 0) + while (*ptr != CHAR_NULL && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; + if (*ptr == CHAR_NULL) { *errorcodeptr = ERR18; goto FAILED; @@ -4182,20 +5712,21 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* ------------------------------------------------------------ */ - case '|': /* Reset capture count for each branch */ + case CHAR_VERTICAL_LINE: /* Reset capture count for each branch */ reset_bracount = TRUE; /* Fall through */ /* ------------------------------------------------------------ */ - case ':': /* Non-capturing bracket */ + case CHAR_COLON: /* Non-capturing bracket */ bravalue = OP_BRA; ptr++; break; /* ------------------------------------------------------------ */ - case '(': + case CHAR_LEFT_PARENTHESIS: bravalue = OP_COND; /* Conditional group */ + tempptr = ptr; /* A condition can be an assertion, a number (referring to a numbered group), a name (referring to a named group), or 'R', referring to @@ -4208,25 +5739,40 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ be the recursive thing or the name 'R' (and similarly for 'R' followed by digits), and (b) a number could be a name that consists of digits. In both cases, we look for a name first; if not found, we try the other - cases. */ + cases. + + For compatibility with auto-callouts, we allow a callout to be + specified before a condition that is an assertion. First, check for the + syntax of a callout; if found, adjust the temporary pointer that is + used to check for an assertion condition. That's all that is needed! */ + + if (ptr[1] == CHAR_QUESTION_MARK && ptr[2] == CHAR_C) + { + for (i = 3;; i++) if (!IS_DIGIT(ptr[i])) break; + if (ptr[i] == CHAR_RIGHT_PARENTHESIS) + tempptr += i + 1; + } /* For conditions that are assertions, check the syntax, and then exit the switch. This will take control down to where bracketed groups, including assertions, are processed. */ - if (ptr[1] == '?' && (ptr[2] == '=' || ptr[2] == '!' || ptr[2] == '<')) + if (tempptr[1] == CHAR_QUESTION_MARK && + (tempptr[2] == CHAR_EQUALS_SIGN || + tempptr[2] == CHAR_EXCLAMATION_MARK || + tempptr[2] == CHAR_LESS_THAN_SIGN)) break; /* Most other conditions use OP_CREF (a couple change to OP_RREF - below), and all need to skip 3 bytes at the start of the group. */ + below), and all need to skip 1+IMM2_SIZE bytes at the start of the group. */ code[1+LINK_SIZE] = OP_CREF; - skipbytes = 3; + skipbytes = 1+IMM2_SIZE; refsign = -1; /* Check for a test for recursion in a named group. */ - if (ptr[1] == 'R' && ptr[2] == '&') + if (ptr[1] == CHAR_R && ptr[2] == CHAR_AMPERSAND) { terminator = -1; ptr += 2; @@ -4236,25 +5782,25 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* Check for a test for a named group's having been set, using the Perl syntax (?() or (?('name') */ - else if (ptr[1] == '<') + else if (ptr[1] == CHAR_LESS_THAN_SIGN) { - terminator = '>'; + terminator = CHAR_GREATER_THAN_SIGN; ptr++; } - else if (ptr[1] == '\'') + else if (ptr[1] == CHAR_APOSTROPHE) { - terminator = '\''; + terminator = CHAR_APOSTROPHE; ptr++; } else { - terminator = 0; - if (ptr[1] == '-' || ptr[1] == '+') refsign = *(++ptr); + terminator = CHAR_NULL; + if (ptr[1] == CHAR_MINUS || ptr[1] == CHAR_PLUS) refsign = *(++ptr); } /* We now expect to read a name; any thing else is an error */ - if ((cd->ctypes[ptr[1]] & ctype_word) == 0) + if (!MAX_255(ptr[1]) || (cd->ctypes[ptr[1]] & ctype_word) == 0) { ptr += 1; /* To get the right offset */ *errorcodeptr = ERR28; @@ -4265,16 +5811,16 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ recno = 0; name = ++ptr; - while ((cd->ctypes[*ptr] & ctype_word) != 0) + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_word) != 0) { if (recno >= 0) - recno = ((digitab[*ptr] & ctype_digit) != 0)? - recno * 10 + *ptr - '0' : -1; + recno = (IS_DIGIT(*ptr))? recno * 10 + (int)(*ptr - CHAR_0) : -1; ptr++; } - namelen = ptr - name; + namelen = (int)(ptr - name); - if ((terminator > 0 && *ptr++ != terminator) || *ptr++ != ')') + if ((terminator > 0 && *ptr++ != (pcre_uchar)terminator) || + *ptr++ != CHAR_RIGHT_PARENTHESIS) { ptr--; /* Error offset */ *errorcodeptr = ERR26; @@ -4296,7 +5842,7 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ *errorcodeptr = ERR58; goto FAILED; } - recno = (refsign == '-')? + recno = (refsign == CHAR_MINUS)? cd->bracount - recno + 1 : recno +cd->bracount; if (recno <= 0 || recno > cd->final_bracount) { @@ -4308,12 +5854,15 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ } /* Otherwise (did not start with "+" or "-"), start by looking for the - name. */ + name. If we find a name, add one to the opcode to change OP_CREF or + OP_RREF into OP_NCREF or OP_NRREF. These behave exactly the same, + except they record that the reference was originally to a name. The + information is used to check duplicate names. */ slot = cd->name_table; for (i = 0; i < cd->names_found; i++) { - if (strncmp((char *)name, (char *)slot+2, namelen) == 0) break; + if (STRNCMP_UC_UC(name, slot+IMM2_SIZE, namelen) == 0) break; slot += cd->name_entry_size; } @@ -4323,23 +5872,25 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ { recno = GET2(slot, 0); PUT2(code, 2+LINK_SIZE, recno); + code[1+LINK_SIZE]++; } /* Search the pattern for a forward reference */ - else if ((i = find_parens(ptr, cd->bracount, name, namelen, - (options & PCRE_EXTENDED) != 0)) > 0) + else if ((i = find_parens(cd, name, namelen, + (options & PCRE_EXTENDED) != 0, utf)) > 0) { PUT2(code, 2+LINK_SIZE, i); + code[1+LINK_SIZE]++; } - /* If terminator == 0 it means that the name followed directly after - the opening parenthesis [e.g. (?(abc)...] and in this case there are - some further alternatives to try. For the cases where terminator != 0 - [things like (?(... or (?('name')... or (?(R&name)... ] we have + /* If terminator == CHAR_NULL it means that the name followed directly + after the opening parenthesis [e.g. (?(abc)...] and in this case there + are some further alternatives to try. For the cases where terminator != + 0 [things like (?(... or (?('name')... or (?(R&name)... ] we have now checked all the possibilities, so give an error. */ - else if (terminator != 0) + else if (terminator != CHAR_NULL) { *errorcodeptr = ERR15; goto FAILED; @@ -4348,17 +5899,17 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* Check for (?(R) for recursion. Allow digits after R to specify a specific group number. */ - else if (*name == 'R') + else if (*name == CHAR_R) { recno = 0; for (i = 1; i < namelen; i++) { - if ((digitab[name[i]] & ctype_digit) == 0) + if (!IS_DIGIT(name[i])) { *errorcodeptr = ERR15; goto FAILED; } - recno = recno * 10 + name[i] - '0'; + recno = recno * 10 + name[i] - CHAR_0; } if (recno == 0) recno = RREF_ANY; code[1+LINK_SIZE] = OP_RREF; /* Change test type */ @@ -4368,7 +5919,7 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* Similarly, check for the (?(DEFINE) "condition", which is always false. */ - else if (namelen == 6 && strncmp((char *)name, "DEFINE", 6) == 0) + else if (namelen == 6 && STRNCMP_UC_C8(name, STRING_DEFINE, 6) == 0) { code[1+LINK_SIZE] = OP_DEF; skipbytes = 1; @@ -4393,41 +5944,46 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* ------------------------------------------------------------ */ - case '=': /* Positive lookahead */ + case CHAR_EQUALS_SIGN: /* Positive lookahead */ bravalue = OP_ASSERT; + cd->assert_depth += 1; ptr++; break; /* ------------------------------------------------------------ */ - case '!': /* Negative lookahead */ + case CHAR_EXCLAMATION_MARK: /* Negative lookahead */ ptr++; - if (*ptr == ')') /* Optimize (?!) */ + if (*ptr == CHAR_RIGHT_PARENTHESIS) /* Optimize (?!) */ { *code++ = OP_FAIL; previous = NULL; continue; } bravalue = OP_ASSERT_NOT; + cd->assert_depth += 1; break; /* ------------------------------------------------------------ */ - case '<': /* Lookbehind or named define */ + case CHAR_LESS_THAN_SIGN: /* Lookbehind or named define */ switch (ptr[1]) { - case '=': /* Positive lookbehind */ + case CHAR_EQUALS_SIGN: /* Positive lookbehind */ bravalue = OP_ASSERTBACK; + cd->assert_depth += 1; ptr += 2; break; - case '!': /* Negative lookbehind */ + case CHAR_EXCLAMATION_MARK: /* Negative lookbehind */ bravalue = OP_ASSERTBACK_NOT; + cd->assert_depth += 1; ptr += 2; break; default: /* Could be name define, else bad */ - if ((cd->ctypes[ptr[1]] & ctype_word) != 0) goto DEFINE_NAME; + if (MAX_255(ptr[1]) && (cd->ctypes[ptr[1]] & ctype_word) != 0) + goto DEFINE_NAME; ptr++; /* Correct offset for error */ *errorcodeptr = ERR24; goto FAILED; @@ -4436,22 +5992,23 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* ------------------------------------------------------------ */ - case '>': /* One-time brackets */ + case CHAR_GREATER_THAN_SIGN: /* One-time brackets */ bravalue = OP_ONCE; ptr++; break; /* ------------------------------------------------------------ */ - case 'C': /* Callout - may be followed by digits; */ - previous_callout = code; /* Save for later completion */ - after_manual_callout = 1; /* Skip one item before completing */ + case CHAR_C: /* Callout - may be followed by digits; */ + previous_callout = code; /* Save for later completion */ + after_manual_callout = 1; /* Skip one item before completing */ *code++ = OP_CALLOUT; { int n = 0; - while ((digitab[*(++ptr)] & ctype_digit) != 0) - n = n * 10 + *ptr - '0'; - if (*ptr != ')') + ptr++; + while(IS_DIGIT(*ptr)) + n = n * 10 + *ptr++ - CHAR_0; + if (*ptr != CHAR_RIGHT_PARENTHESIS) { *errorcodeptr = ERR39; goto FAILED; @@ -4462,8 +6019,8 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ goto FAILED; } *code++ = n; - PUT(code, 0, ptr - cd->start_pattern + 1); /* Pattern offset */ - PUT(code, LINK_SIZE, 0); /* Default length */ + PUT(code, 0, (int)(ptr - cd->start_pattern + 1)); /* Pattern offset */ + PUT(code, LINK_SIZE, 0); /* Default length */ code += 2 * LINK_SIZE; } previous = NULL; @@ -4471,14 +6028,15 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* ------------------------------------------------------------ */ - case 'P': /* Python-style named subpattern handling */ - if (*(++ptr) == '=' || *ptr == '>') /* Reference or recursion */ + case CHAR_P: /* Python-style named subpattern handling */ + if (*(++ptr) == CHAR_EQUALS_SIGN || + *ptr == CHAR_GREATER_THAN_SIGN) /* Reference or recursion */ { - is_recurse = *ptr == '>'; - terminator = ')'; + is_recurse = *ptr == CHAR_GREATER_THAN_SIGN; + terminator = CHAR_RIGHT_PARENTHESIS; goto NAMED_REF_OR_RECURSE; } - else if (*ptr != '<') /* Test for Python-style definition */ + else if (*ptr != CHAR_LESS_THAN_SIGN) /* Test for Python-style defn */ { *errorcodeptr = ERR41; goto FAILED; @@ -4488,19 +6046,20 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* ------------------------------------------------------------ */ DEFINE_NAME: /* Come here from (?< handling */ - case '\'': + case CHAR_APOSTROPHE: { - terminator = (*ptr == '<')? '>' : '\''; + terminator = (*ptr == CHAR_LESS_THAN_SIGN)? + CHAR_GREATER_THAN_SIGN : CHAR_APOSTROPHE; name = ++ptr; - while ((cd->ctypes[*ptr] & ctype_word) != 0) ptr++; - namelen = ptr - name; + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_word) != 0) ptr++; + namelen = (int)(ptr - name); /* In the pre-compile phase, just do a syntax check. */ if (lengthptr != NULL) { - if (*ptr != terminator) + if (*ptr != (pcre_uchar)terminator) { *errorcodeptr = ERR42; goto FAILED; @@ -4510,9 +6069,9 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ *errorcodeptr = ERR49; goto FAILED; } - if (namelen + 3 > cd->name_entry_size) + if (namelen + IMM2_SIZE + 1 > cd->name_entry_size) { - cd->name_entry_size = namelen + 3; + cd->name_entry_size = namelen + IMM2_SIZE + 1; if (namelen > MAX_NAME_SIZE) { *errorcodeptr = ERR48; @@ -4521,51 +6080,97 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ } } - /* In the real compile, create the entry in the table */ + /* In the real compile, create the entry in the table, maintaining + alphabetical order. Duplicate names for different numbers are + permitted only if PCRE_DUPNAMES is set. Duplicate names for the same + number are always OK. (An existing number can be re-used if (?| + appears in the pattern.) In either event, a duplicate name results in + a duplicate entry in the table, even if the number is the same. This + is because the number of names, and hence the table size, is computed + in the pre-compile, and it affects various numbers and pointers which + would all have to be modified, and the compiled code moved down, if + duplicates with the same number were omitted from the table. This + doesn't seem worth the hassle. However, *different* names for the + same number are not permitted. */ else { + BOOL dupname = FALSE; slot = cd->name_table; + for (i = 0; i < cd->names_found; i++) { - int crc = memcmp(name, slot+2, namelen); + int crc = memcmp(name, slot+IMM2_SIZE, IN_UCHARS(namelen)); if (crc == 0) { - if (slot[2+namelen] == 0) + if (slot[IMM2_SIZE+namelen] == 0) { - if ((options & PCRE_DUPNAMES) == 0) + if (GET2(slot, 0) != cd->bracount + 1 && + (options & PCRE_DUPNAMES) == 0) { *errorcodeptr = ERR43; goto FAILED; } + else dupname = TRUE; } - else crc = -1; /* Current name is substring */ + else crc = -1; /* Current name is a substring */ } + + /* Make space in the table and break the loop for an earlier + name. For a duplicate or later name, carry on. We do this for + duplicates so that in the simple case (when ?(| is not used) they + are in order of their numbers. */ + if (crc < 0) { memmove(slot + cd->name_entry_size, slot, - (cd->names_found - i) * cd->name_entry_size); + IN_UCHARS((cd->names_found - i) * cd->name_entry_size)); break; } + + /* Continue the loop for a later or duplicate name */ + slot += cd->name_entry_size; } + /* For non-duplicate names, check for a duplicate number before + adding the new name. */ + + if (!dupname) + { + pcre_uchar *cslot = cd->name_table; + for (i = 0; i < cd->names_found; i++) + { + if (cslot != slot) + { + if (GET2(cslot, 0) == cd->bracount + 1) + { + *errorcodeptr = ERR65; + goto FAILED; + } + } + else i--; + cslot += cd->name_entry_size; + } + } + PUT2(slot, 0, cd->bracount + 1); - memcpy(slot + 2, name, namelen); - slot[2+namelen] = 0; + memcpy(slot + IMM2_SIZE, name, IN_UCHARS(namelen)); + slot[IMM2_SIZE + namelen] = 0; } } - /* In both cases, count the number of names we've encountered. */ + /* In both pre-compile and compile, count the number of names we've + encountered. */ - ptr++; /* Move past > or ' */ cd->names_found++; + ptr++; /* Move past > or ' */ goto NUMBERED_GROUP; /* ------------------------------------------------------------ */ - case '&': /* Perl recursion/subroutine syntax */ - terminator = ')'; + case CHAR_AMPERSAND: /* Perl recursion/subroutine syntax */ + terminator = CHAR_RIGHT_PARENTHESIS; is_recurse = TRUE; /* Fall through */ @@ -4573,24 +6178,30 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ references (?P=name) and recursion (?P>name), as well as falling through from the Perl recursion syntax (?&name). We also come here from the Perl \k or \k'name' back reference syntax and the \k{name} - .NET syntax. */ + .NET syntax, and the Oniguruma \g<...> and \g'...' subroutine syntax. */ NAMED_REF_OR_RECURSE: name = ++ptr; - while ((cd->ctypes[*ptr] & ctype_word) != 0) ptr++; - namelen = ptr - name; + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_word) != 0) ptr++; + namelen = (int)(ptr - name); - /* In the pre-compile phase, do a syntax check and set a dummy - reference number. */ + /* In the pre-compile phase, do a syntax check. We used to just set + a dummy reference number, because it was not used in the first pass. + However, with the change of recursive back references to be atomic, + we have to look for the number so that this state can be identified, as + otherwise the incorrect length is computed. If it's not a backwards + reference, the dummy number will do. */ if (lengthptr != NULL) { + const pcre_uchar *temp; + if (namelen == 0) { *errorcodeptr = ERR62; goto FAILED; } - if (*ptr != terminator) + if (*ptr != (pcre_uchar)terminator) { *errorcodeptr = ERR42; goto FAILED; @@ -4600,7 +6211,22 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ *errorcodeptr = ERR48; goto FAILED; } - recno = 0; + + /* The name table does not exist in the first pass, so we cannot + do a simple search as in the code below. Instead, we have to scan the + pattern to find the number. It is important that we scan it only as + far as we have got because the syntax of named subpatterns has not + been checked for the rest of the pattern, and find_parens() assumes + correct syntax. In any case, it's a waste of resources to scan + further. We stop the scan at the current point by temporarily + adjusting the value of cd->endpattern. */ + + temp = cd->end_pattern; + cd->end_pattern = ptr; + recno = find_parens(cd, name, namelen, + (options & PCRE_EXTENDED) != 0, utf); + cd->end_pattern = temp; + if (recno < 0) recno = 0; /* Forward ref; set dummy number */ } /* In the real compile, seek the name in the table. We check the name @@ -4613,8 +6239,8 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ slot = cd->name_table; for (i = 0; i < cd->names_found; i++) { - if (strncmp((char *)name, (char *)slot+2, namelen) == 0 && - slot[2+namelen] == 0) + if (STRNCMP_UC_UC(name, slot+IMM2_SIZE, namelen) == 0 && + slot[IMM2_SIZE+namelen] == 0) break; slot += cd->name_entry_size; } @@ -4624,8 +6250,8 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ recno = GET2(slot, 0); } else if ((recno = /* Forward back reference */ - find_parens(ptr, cd->bracount, name, namelen, - (options & PCRE_EXTENDED) != 0)) <= 0) + find_parens(cd, name, namelen, + (options & PCRE_EXTENDED) != 0, utf)) <= 0) { *errorcodeptr = ERR15; goto FAILED; @@ -4640,45 +6266,54 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* ------------------------------------------------------------ */ - case 'R': /* Recursion */ + case CHAR_R: /* Recursion */ ptr++; /* Same as (?0) */ /* Fall through */ /* ------------------------------------------------------------ */ - case '-': case '+': - case '0': case '1': case '2': case '3': case '4': /* Recursion or */ - case '5': case '6': case '7': case '8': case '9': /* subroutine */ + case CHAR_MINUS: case CHAR_PLUS: /* Recursion or subroutine */ + case CHAR_0: case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: + case CHAR_5: case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9: { - const uschar *called; + const pcre_uchar *called; + terminator = CHAR_RIGHT_PARENTHESIS; + + /* Come here from the \g<...> and \g'...' code (Oniguruma + compatibility). However, the syntax has been checked to ensure that + the ... are a (signed) number, so that neither ERR63 nor ERR29 will + be called on this path, nor with the jump to OTHER_CHAR_AFTER_QUERY + ever be taken. */ + + HANDLE_NUMERICAL_RECURSION: - if ((refsign = *ptr) == '+') + if ((refsign = *ptr) == CHAR_PLUS) { ptr++; - if ((digitab[*ptr] & ctype_digit) == 0) + if (!IS_DIGIT(*ptr)) { *errorcodeptr = ERR63; goto FAILED; } } - else if (refsign == '-') + else if (refsign == CHAR_MINUS) { - if ((digitab[ptr[1]] & ctype_digit) == 0) + if (!IS_DIGIT(ptr[1])) goto OTHER_CHAR_AFTER_QUERY; ptr++; } recno = 0; - while((digitab[*ptr] & ctype_digit) != 0) - recno = recno * 10 + *ptr++ - '0'; + while(IS_DIGIT(*ptr)) + recno = recno * 10 + *ptr++ - CHAR_0; - if (*ptr != ')') + if (*ptr != (pcre_uchar)terminator) { *errorcodeptr = ERR29; goto FAILED; } - if (refsign == '-') + if (refsign == CHAR_MINUS) { if (recno == 0) { @@ -4692,7 +6327,7 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ goto FAILED; } } - else if (refsign == '+') + else if (refsign == CHAR_PLUS) { if (recno == 0) { @@ -4719,56 +6354,64 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ if (lengthptr == NULL) { *code = OP_END; - if (recno != 0) called = find_bracket(cd->start_code, utf8, recno); + if (recno != 0) + called = PRIV(find_bracket)(cd->start_code, utf, recno); /* Forward reference */ if (called == NULL) { - if (find_parens(ptr, cd->bracount, NULL, recno, - (options & PCRE_EXTENDED) != 0) < 0) + if (find_parens(cd, NULL, recno, + (options & PCRE_EXTENDED) != 0, utf) < 0) { *errorcodeptr = ERR15; goto FAILED; } + + /* Fudge the value of "called" so that when it is inserted as an + offset below, what it actually inserted is the reference number + of the group. Then remember the forward reference. */ + called = cd->start_code + recno; - PUTINC(cd->hwm, 0, code + 2 + LINK_SIZE - cd->start_code); + if (cd->hwm >= cd->start_workspace + cd->workspace_size - + WORK_SIZE_SAFETY_MARGIN) + { + *errorcodeptr = expand_workspace(cd); + if (*errorcodeptr != 0) goto FAILED; + } + PUTINC(cd->hwm, 0, (int)(code + 1 - cd->start_code)); } /* If not a forward reference, and the subpattern is still open, this is a recursive call. We check to see if this is a left - recursion that could loop for ever, and diagnose that case. */ - - else if (GET(called, 1) == 0 && - could_be_empty(called, code, bcptr, utf8)) + recursion that could loop for ever, and diagnose that case. We + must not, however, do this check if we are in a conditional + subpattern because the condition might be testing for recursion in + a pattern such as /(?(R)a+|(?R)b)/, which is perfectly valid. + Forever loops are also detected at runtime, so those that occur in + conditional subpatterns will be picked up then. */ + + else if (GET(called, 1) == 0 && cond_depth <= 0 && + could_be_empty(called, code, bcptr, utf, cd)) { *errorcodeptr = ERR40; goto FAILED; } } - /* Insert the recursion/subroutine item, automatically wrapped inside - "once" brackets. Set up a "previous group" length so that a - subsequent quantifier will work. */ - - *code = OP_ONCE; - PUT(code, 1, 2 + 2*LINK_SIZE); - code += 1 + LINK_SIZE; + /* Insert the recursion/subroutine item. It does not have a set first + character (relevant if it is repeated, because it will then be + wrapped with ONCE brackets). */ *code = OP_RECURSE; - PUT(code, 1, called - cd->start_code); - code += 1 + LINK_SIZE; - - *code = OP_KET; - PUT(code, 1, 2 + 2*LINK_SIZE); + PUT(code, 1, (int)(called - cd->start_code)); code += 1 + LINK_SIZE; - - length_prevgroup = 3 + 3*LINK_SIZE; + groupsetfirstchar = FALSE; } /* Can't determine a first byte now */ - if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + if (firstcharflags == REQ_UNSET) firstcharflags = REQ_NONE; continue; @@ -4778,23 +6421,23 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ set = unset = 0; optset = &set; - while (*ptr != ')' && *ptr != ':') + while (*ptr != CHAR_RIGHT_PARENTHESIS && *ptr != CHAR_COLON) { switch (*ptr++) { - case '-': optset = &unset; break; + case CHAR_MINUS: optset = &unset; break; - case 'J': /* Record that it changed in the external options */ + case CHAR_J: /* Record that it changed in the external options */ *optset |= PCRE_DUPNAMES; cd->external_flags |= PCRE_JCHANGED; break; - case 'i': *optset |= PCRE_CASELESS; break; - case 'm': *optset |= PCRE_MULTILINE; break; - case 's': *optset |= PCRE_DOTALL; break; - case 'x': *optset |= PCRE_EXTENDED; break; - case 'U': *optset |= PCRE_UNGREEDY; break; - case 'X': *optset |= PCRE_EXTRA; break; + case CHAR_i: *optset |= PCRE_CASELESS; break; + case CHAR_m: *optset |= PCRE_MULTILINE; break; + case CHAR_s: *optset |= PCRE_DOTALL; break; + case CHAR_x: *optset |= PCRE_EXTENDED; break; + case CHAR_U: *optset |= PCRE_UNGREEDY; break; + case CHAR_X: *optset |= PCRE_EXTRA; break; default: *errorcodeptr = ERR12; ptr--; /* Correct the offset */ @@ -4824,33 +6467,25 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ is necessary to ensure we correctly detect the start of the pattern in both phases. - If we are not at the pattern start, compile code to change the ims - options if this setting actually changes any of them, and reset the - greedy defaults and the case value for firstbyte and reqbyte. */ + If we are not at the pattern start, reset the greedy defaults and the + case value for firstchar and reqchar. */ - if (*ptr == ')') + if (*ptr == CHAR_RIGHT_PARENTHESIS) { if (code == cd->start_code + 1 + LINK_SIZE && (lengthptr == NULL || *lengthptr == 2 + 2*LINK_SIZE)) { cd->external_options = newoptions; } - else + else { - if ((options & PCRE_IMS) != (newoptions & PCRE_IMS)) - { - *code++ = OP_OPT; - *code++ = newoptions & PCRE_IMS; - } greedy_default = ((newoptions & PCRE_UNGREEDY) != 0); greedy_non_default = greedy_default ^ 1; - req_caseopt = ((newoptions & PCRE_CASELESS) != 0)? REQ_CASELESS : 0; + req_caseopt = ((newoptions & PCRE_CASELESS) != 0)? REQ_CASELESS:0; } /* Change options at this level, and pass them back for use - in subsequent branches. When not at the start of the pattern, this - information is also necessary so that a resetting item can be - compiled at the end of a group (if we are in a group). */ + in subsequent branches. */ *optionsptr = options = newoptions; previous = NULL; /* This item can't be repeated */ @@ -4867,8 +6502,8 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ } /* End of switch for character following (? */ } /* End of (? handling */ - /* Opening parenthesis not followed by '?'. If PCRE_NO_AUTO_CAPTURE is set, - all unadorned brackets become non-capturing and behave like (?:...) + /* Opening parenthesis not followed by '*' or '?'. If PCRE_NO_AUTO_CAPTURE + is set, all unadorned brackets become non-capturing and behave like (?:...) brackets. */ else if ((options & PCRE_NO_AUTO_CAPTURE) != 0) @@ -4883,53 +6518,64 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ NUMBERED_GROUP: cd->bracount += 1; PUT2(code, 1+LINK_SIZE, cd->bracount); - skipbytes = 2; + skipbytes = IMM2_SIZE; } - /* Process nested bracketed regex. Assertions may not be repeated, but - other kinds can be. All their opcodes are >= OP_ONCE. We copy code into a - non-register variable in order to be able to pass its address because some - compilers complain otherwise. Pass in a new setting for the ims options if - they have changed. */ + /* Process nested bracketed regex. Assertions used not to be repeatable, + but this was changed for Perl compatibility, so all kinds can now be + repeated. We copy code into a non-register variable (tempcode) in order to + be able to pass its address because some compilers complain otherwise. */ - previous = (bravalue >= OP_ONCE)? code : NULL; + previous = code; /* For handling repetition */ *code = bravalue; tempcode = code; - tempreqvary = cd->req_varyopt; /* Save value before bracket */ - length_prevgroup = 0; /* Initialize for pre-compile phase */ + tempreqvary = cd->req_varyopt; /* Save value before bracket */ + tempbracount = cd->bracount; /* Save value before bracket */ + length_prevgroup = 0; /* Initialize for pre-compile phase */ if (!compile_regex( - newoptions, /* The complete new option state */ - options & PCRE_IMS, /* The previous ims option state */ - &tempcode, /* Where to put code (updated) */ - &ptr, /* Input pointer (updated) */ - errorcodeptr, /* Where to put an error message */ + newoptions, /* The complete new option state */ + &tempcode, /* Where to put code (updated) */ + &ptr, /* Input pointer (updated) */ + errorcodeptr, /* Where to put an error message */ (bravalue == OP_ASSERTBACK || bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */ - reset_bracount, /* True if (?| group */ - skipbytes, /* Skip over bracket number */ - &subfirstbyte, /* For possible first char */ - &subreqbyte, /* For possible last char */ - bcptr, /* Current branch chain */ - cd, /* Tables block */ - (lengthptr == NULL)? NULL : /* Actual compile phase */ - &length_prevgroup /* Pre-compile phase */ + reset_bracount, /* True if (?| group */ + skipbytes, /* Skip over bracket number */ + cond_depth + + ((bravalue == OP_COND)?1:0), /* Depth of condition subpatterns */ + &subfirstchar, /* For possible first char */ + &subfirstcharflags, + &subreqchar, /* For possible last char */ + &subreqcharflags, + bcptr, /* Current branch chain */ + cd, /* Tables block */ + (lengthptr == NULL)? NULL : /* Actual compile phase */ + &length_prevgroup /* Pre-compile phase */ )) goto FAILED; + /* If this was an atomic group and there are no capturing groups within it, + generate OP_ONCE_NC instead of OP_ONCE. */ + + if (bravalue == OP_ONCE && cd->bracount <= tempbracount) + *code = OP_ONCE_NC; + + if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NOT) + cd->assert_depth -= 1; + /* At the end of compiling, code is still pointing to the start of the - group, while tempcode has been updated to point past the end of the group - and any option resetting that may follow it. The pattern pointer (ptr) - is on the bracket. */ + group, while tempcode has been updated to point past the end of the group. + The pattern pointer (ptr) is on the bracket. - /* If this is a conditional bracket, check that there are no more than + If this is a conditional bracket, check that there are no more than two branches in the group, or just one if it's a DEFINE group. We do this in the real compile phase, not in the pre-pass, where the whole group may not be available. */ if (bravalue == OP_COND && lengthptr == NULL) { - uschar *tc = code; + pcre_uchar *tc = code; int condcount = 0; do { @@ -4952,7 +6598,7 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ } /* A "normal" conditional group. If there is just one branch, we must not - make use of its firstbyte or reqbyte, because this is equivalent to an + make use of its firstchar or reqchar, because this is equivalent to an empty second branch. */ else @@ -4962,13 +6608,13 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ *errorcodeptr = ERR27; goto FAILED; } - if (condcount == 1) subfirstbyte = subreqbyte = REQ_NONE; + if (condcount == 1) subfirstcharflags = subreqcharflags = REQ_NONE; } } /* Error if hit end of pattern */ - if (*ptr != ')') + if (*ptr != CHAR_RIGHT_PARENTHESIS) { *errorcodeptr = ERR14; goto FAILED; @@ -4987,7 +6633,7 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ goto FAILED; } *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE; - *code++ = OP_BRA; + code++; /* This already contains bravalue */ PUTINC(code, 0, 1 + LINK_SIZE); *code++ = OP_KET; PUTINC(code, 0, 1 + LINK_SIZE); @@ -5006,131 +6652,233 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* Handle updating of the required and first characters for other types of group. Update for normal brackets of all kinds, and conditions with two branches (see code above). If the bracket is followed by a quantifier with - zero repeat, we have to back off. Hence the definition of zeroreqbyte and - zerofirstbyte outside the main loop so that they can be accessed for the + zero repeat, we have to back off. Hence the definition of zeroreqchar and + zerofirstchar outside the main loop so that they can be accessed for the back off. */ - zeroreqbyte = reqbyte; - zerofirstbyte = firstbyte; - groupsetfirstbyte = FALSE; + zeroreqchar = reqchar; + zeroreqcharflags = reqcharflags; + zerofirstchar = firstchar; + zerofirstcharflags = firstcharflags; + groupsetfirstchar = FALSE; if (bravalue >= OP_ONCE) { - /* If we have not yet set a firstbyte in this branch, take it from the + /* If we have not yet set a firstchar in this branch, take it from the subpattern, remembering that it was set here so that a repeat of more - than one can replicate it as reqbyte if necessary. If the subpattern has - no firstbyte, set "none" for the whole branch. In both cases, a zero - repeat forces firstbyte to "none". */ + than one can replicate it as reqchar if necessary. If the subpattern has + no firstchar, set "none" for the whole branch. In both cases, a zero + repeat forces firstchar to "none". */ - if (firstbyte == REQ_UNSET) + if (firstcharflags == REQ_UNSET) { - if (subfirstbyte >= 0) + if (subfirstcharflags >= 0) { - firstbyte = subfirstbyte; - groupsetfirstbyte = TRUE; + firstchar = subfirstchar; + firstcharflags = subfirstcharflags; + groupsetfirstchar = TRUE; } - else firstbyte = REQ_NONE; - zerofirstbyte = REQ_NONE; + else firstcharflags = REQ_NONE; + zerofirstcharflags = REQ_NONE; } - /* If firstbyte was previously set, convert the subpattern's firstbyte - into reqbyte if there wasn't one, using the vary flag that was in + /* If firstchar was previously set, convert the subpattern's firstchar + into reqchar if there wasn't one, using the vary flag that was in existence beforehand. */ - else if (subfirstbyte >= 0 && subreqbyte < 0) - subreqbyte = subfirstbyte | tempreqvary; + else if (subfirstcharflags >= 0 && subreqcharflags < 0) + { + subreqchar = subfirstchar; + subreqcharflags = subfirstcharflags | tempreqvary; + } /* If the subpattern set a required byte (or set a first byte that isn't really the first byte - see above), set it. */ - if (subreqbyte >= 0) reqbyte = subreqbyte; + if (subreqcharflags >= 0) + { + reqchar = subreqchar; + reqcharflags = subreqcharflags; + } } - /* For a forward assertion, we take the reqbyte, if set. This can be + /* For a forward assertion, we take the reqchar, if set. This can be helpful if the pattern that follows the assertion doesn't set a different - char. For example, it's useful for /(?=abcde).+/. We can't set firstbyte + char. For example, it's useful for /(?=abcde).+/. We can't set firstchar for an assertion, however because it leads to incorrect effect for patterns - such as /(?=a)a.+/ when the "real" "a" would then become a reqbyte instead - of a firstbyte. This is overcome by a scan at the end if there's no - firstbyte, looking for an asserted first char. */ + such as /(?=a)a.+/ when the "real" "a" would then become a reqchar instead + of a firstchar. This is overcome by a scan at the end if there's no + firstchar, looking for an asserted first char. */ - else if (bravalue == OP_ASSERT && subreqbyte >= 0) reqbyte = subreqbyte; + else if (bravalue == OP_ASSERT && subreqcharflags >= 0) + { + reqchar = subreqchar; + reqcharflags = subreqcharflags; + } break; /* End of processing '(' */ /* ===================================================================*/ /* Handle metasequences introduced by \. For ones like \d, the ESC_ values - are arranged to be the negation of the corresponding OP_values. For the - back references, the values are ESC_REF plus the reference number. Only - back references and those types that consume a character may be repeated. - We can test for values between ESC_b and ESC_Z for the latter; this may - have to change if any new ones are ever created. */ - - case '\\': + are arranged to be the negation of the corresponding OP_values in the + default case when PCRE_UCP is not set. For the back references, the values + are negative the reference number. Only back references and those types + that consume a character may be repeated. We can test for values between + ESC_b and ESC_Z for the latter; this may have to change if any new ones are + ever created. */ + + case CHAR_BACKSLASH: tempptr = ptr; - c = check_escape(&ptr, errorcodeptr, cd->bracount, options, FALSE); + escape = check_escape(&ptr, &ec, errorcodeptr, cd->bracount, options, FALSE); if (*errorcodeptr != 0) goto FAILED; - if (c < 0) + if (escape == 0) /* The escape coded a single character */ + c = ec; + else { - if (-c == ESC_Q) /* Handle start of quoted string */ + if (escape == ESC_Q) /* Handle start of quoted string */ { - if (ptr[1] == '\\' && ptr[2] == 'E') ptr += 2; /* avoid empty string */ - else inescq = TRUE; + if (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E) + ptr += 2; /* avoid empty string */ + else inescq = TRUE; continue; } - if (-c == ESC_E) continue; /* Perl ignores an orphan \E */ + if (escape == ESC_E) continue; /* Perl ignores an orphan \E */ /* For metasequences that actually match a character, we disable the setting of a first character if it hasn't already been set. */ - if (firstbyte == REQ_UNSET && -c > ESC_b && -c < ESC_Z) - firstbyte = REQ_NONE; + if (firstcharflags == REQ_UNSET && escape > ESC_b && escape < ESC_Z) + firstcharflags = REQ_NONE; /* Set values to reset to if this is followed by a zero repeat. */ - zerofirstbyte = firstbyte; - zeroreqbyte = reqbyte; + zerofirstchar = firstchar; + zerofirstcharflags = firstcharflags; + zeroreqchar = reqchar; + zeroreqcharflags = reqcharflags; + + /* \g or \g'name' is a subroutine call by name and \g or \g'n' + is a subroutine call by number (Oniguruma syntax). In fact, the value + ESC_g is returned only for these cases. So we don't need to check for < + or ' if the value is ESC_g. For the Perl syntax \g{n} the value is + -n, and for the Perl syntax \g{name} the result is ESC_k (as + that is a synonym for a named back reference). */ + + if (escape == ESC_g) + { + const pcre_uchar *p; + save_hwm = cd->hwm; /* Normally this is set when '(' is read */ + terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)? + CHAR_GREATER_THAN_SIGN : CHAR_APOSTROPHE; + + /* These two statements stop the compiler for warning about possibly + unset variables caused by the jump to HANDLE_NUMERICAL_RECURSION. In + fact, because we actually check for a number below, the paths that + would actually be in error are never taken. */ + + skipbytes = 0; + reset_bracount = FALSE; + + /* Test for a name */ + + if (ptr[1] != CHAR_PLUS && ptr[1] != CHAR_MINUS) + { + BOOL is_a_number = TRUE; + for (p = ptr + 1; *p != CHAR_NULL && *p != (pcre_uchar)terminator; p++) + { + if (!MAX_255(*p)) { is_a_number = FALSE; break; } + if ((cd->ctypes[*p] & ctype_digit) == 0) is_a_number = FALSE; + if ((cd->ctypes[*p] & ctype_word) == 0) break; + } + if (*p != (pcre_uchar)terminator) + { + *errorcodeptr = ERR57; + break; + } + if (is_a_number) + { + ptr++; + goto HANDLE_NUMERICAL_RECURSION; + } + is_recurse = TRUE; + goto NAMED_REF_OR_RECURSE; + } + + /* Test a signed number in angle brackets or quotes. */ + + p = ptr + 2; + while (IS_DIGIT(*p)) p++; + if (*p != (pcre_uchar)terminator) + { + *errorcodeptr = ERR57; + break; + } + ptr++; + goto HANDLE_NUMERICAL_RECURSION; + } /* \k or \k'name' is a back reference by name (Perl syntax). - We also support \k{name} (.NET syntax) */ + We also support \k{name} (.NET syntax). */ - if (-c == ESC_k && (ptr[1] == '<' || ptr[1] == '\'' || ptr[1] == '{')) + if (escape == ESC_k) { + if ((ptr[1] != CHAR_LESS_THAN_SIGN && + ptr[1] != CHAR_APOSTROPHE && ptr[1] != CHAR_LEFT_CURLY_BRACKET)) + { + *errorcodeptr = ERR69; + break; + } is_recurse = FALSE; - terminator = (*(++ptr) == '<')? '>' : (*ptr == '\'')? '\'' : '}'; + terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)? + CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)? + CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET; goto NAMED_REF_OR_RECURSE; } - /* Back references are handled specially; must disable firstbyte if + /* Back references are handled specially; must disable firstchar if not set to cope with cases like (?=(\w+))\1: which would otherwise set ':' later. */ - if (-c >= ESC_REF) + if (escape < 0) { - recno = -c - ESC_REF; + open_capitem *oc; + recno = -escape; HANDLE_REFERENCE: /* Come here from named backref handling */ - if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + if (firstcharflags == REQ_UNSET) firstcharflags = REQ_NONE; previous = code; - *code++ = OP_REF; + *code++ = ((options & PCRE_CASELESS) != 0)? OP_REFI : OP_REF; PUT2INC(code, 0, recno); cd->backref_map |= (recno < 32)? (1 << recno) : 1; if (recno > cd->top_backref) cd->top_backref = recno; + + /* Check to see if this back reference is recursive, that it, it + is inside the group that it references. A flag is set so that the + group can be made atomic. */ + + for (oc = cd->open_caps; oc != NULL; oc = oc->next) + { + if (oc->number == recno) + { + oc->flag = TRUE; + break; + } + } } /* So are Unicode property matches, if supported. */ #ifdef SUPPORT_UCP - else if (-c == ESC_P || -c == ESC_p) + else if (escape == ESC_P || escape == ESC_p) { BOOL negated; - int pdata; - int ptype = get_ucp(&ptr, &negated, &pdata, errorcodeptr); - if (ptype < 0) goto FAILED; + unsigned int ptype = 0, pdata = 0; + if (!get_ucp(&ptr, &negated, &ptype, &pdata, errorcodeptr)) + goto FAILED; previous = code; - *code++ = ((-c == ESC_p) != negated)? OP_PROP : OP_NOTPROP; + *code++ = ((escape == ESC_p) != negated)? OP_PROP : OP_NOTPROP; *code++ = ptype; *code++ = pdata; } @@ -5139,7 +6887,7 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ /* If Unicode properties are not supported, \X, \P, and \p are not allowed. */ - else if (-c == ESC_X || -c == ESC_P || -c == ESC_p) + else if (escape == ESC_X || escape == ESC_P || escape == ESC_p) { *errorcodeptr = ERR45; goto FAILED; @@ -5147,12 +6895,31 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ #endif /* For the rest (including \X when Unicode properties are supported), we - can obtain the OP value by negating the escape value. */ + can obtain the OP value by negating the escape value in the default + situation when PCRE_UCP is not set. When it *is* set, we substitute + Unicode property tests. Note that \b and \B do a one-character + lookbehind, and \A also behaves as if it does. */ else { - previous = (-c > ESC_b && -c < ESC_Z)? code : NULL; - *code++ = -c; + if ((escape == ESC_b || escape == ESC_B || escape == ESC_A) && + cd->max_lookbehind == 0) + cd->max_lookbehind = 1; +#ifdef SUPPORT_UCP + if (escape >= ESC_DU && escape <= ESC_wu) + { + nestptr = ptr + 1; /* Where to resume */ + ptr = substitutes[escape - ESC_DU] - 1; /* Just before substitute */ + } + else +#endif + /* In non-UTF-8 mode, we turn \C into OP_ALLANY instead of OP_ANYBYTE + so that it works in DFA mode and in lookbehinds. */ + + { + previous = (escape > ESC_b && escape < ESC_Z)? code : NULL; + *code++ = (!utf && escape == ESC_C)? OP_ALLANY : escape; + } } continue; } @@ -5161,9 +6928,9 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ a value > 127. We set its representation in the length/buffer, and then handle it as a data character. */ -#ifdef SUPPORT_UTF8 - if (utf8 && c > 127) - mclength = _erts_pcre_ord2utf8(c, mcbuffer); +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf && c > MAX_VALUE_FOR_SINGLE_CHAR) + mclength = PRIV(ord2utf)(c, mcbuffer); else #endif @@ -5184,12 +6951,9 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ mclength = 1; mcbuffer[0] = c; -#ifdef SUPPORT_UTF8 - if (utf8 && c >= 0xc0) - { - while ((ptr[1] & 0xc0) == 0x80) - mcbuffer[mclength++] = *(++ptr); - } +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(c)) + ACROSSCHAR(TRUE, ptr[1], mcbuffer[mclength++] = *(++ptr)); #endif /* At this point we have the character's bytes in mcbuffer, and the length @@ -5197,44 +6961,79 @@ we set the flag only if there is a literal "\r" or "\n" in the class. */ ONE_CHAR: previous = code; - *code++ = ((options & PCRE_CASELESS) != 0)? OP_CHARNC : OP_CHAR; + + /* For caseless UTF-8 mode when UCP support is available, check whether + this character has more than one other case. If so, generate a special + OP_PROP item instead of OP_CHARI. */ + +#ifdef SUPPORT_UCP + if (utf && (options & PCRE_CASELESS) != 0) + { + GETCHAR(c, mcbuffer); + if ((c = UCD_CASESET(c)) != 0) + { + *code++ = OP_PROP; + *code++ = PT_CLIST; + *code++ = c; + if (firstcharflags == REQ_UNSET) firstcharflags = zerofirstcharflags = REQ_NONE; + break; + } + } +#endif + + /* Caseful matches, or not one of the multicase characters. */ + + *code++ = ((options & PCRE_CASELESS) != 0)? OP_CHARI : OP_CHAR; for (c = 0; c < mclength; c++) *code++ = mcbuffer[c]; /* Remember if \r or \n were seen */ - if (mcbuffer[0] == '\r' || mcbuffer[0] == '\n') + if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL) cd->external_flags |= PCRE_HASCRORLF; /* Set the first and required bytes appropriately. If no previous first byte, set it from this character, but revert to none on a zero repeat. - Otherwise, leave the firstbyte value alone, and don't change it on a zero + Otherwise, leave the firstchar value alone, and don't change it on a zero repeat. */ - if (firstbyte == REQ_UNSET) + if (firstcharflags == REQ_UNSET) { - zerofirstbyte = REQ_NONE; - zeroreqbyte = reqbyte; + zerofirstcharflags = REQ_NONE; + zeroreqchar = reqchar; + zeroreqcharflags = reqcharflags; - /* If the character is more than one byte long, we can set firstbyte + /* If the character is more than one byte long, we can set firstchar only if it is not to be matched caselessly. */ if (mclength == 1 || req_caseopt == 0) { - firstbyte = mcbuffer[0] | req_caseopt; - if (mclength != 1) reqbyte = code[-1] | cd->req_varyopt; + firstchar = mcbuffer[0] | req_caseopt; + firstchar = mcbuffer[0]; + firstcharflags = req_caseopt; + + if (mclength != 1) + { + reqchar = code[-1]; + reqcharflags = cd->req_varyopt; + } } - else firstbyte = reqbyte = REQ_NONE; + else firstcharflags = reqcharflags = REQ_NONE; } - /* firstbyte was previously set; we can set reqbyte only the length is + /* firstchar was previously set; we can set reqchar only if the length is 1 or the matching is caseful. */ else { - zerofirstbyte = firstbyte; - zeroreqbyte = reqbyte; + zerofirstchar = firstchar; + zerofirstcharflags = firstcharflags; + zeroreqchar = reqchar; + zeroreqcharflags = reqcharflags; if (mclength == 1 || req_caseopt == 0) - reqbyte = code[-1] | req_caseopt | cd->req_varyopt; + { + reqchar = code[-1]; + reqcharflags = req_caseopt | cd->req_varyopt; + } } break; /* End of literal character handling */ @@ -5253,7 +7052,6 @@ return FALSE; - /************************************************* * Compile sequence of alternatives * *************************************************/ @@ -5261,26 +7059,23 @@ return FALSE; /* On entry, ptr is pointing past the bracket character, but on return it points to the closing bracket, or vertical bar, or end of string. The code variable is pointing at the byte into which the BRA operator has been stored. -If the ims options are changed at the start (for a (?ims: group) or during any -branch, we need to insert an OP_OPT item at the start of every following branch -to ensure they get set correctly at run time, and also pass the new options -into every subsequent branch compile. - This function is used during the pre-compile phase when we are trying to find out the amount of memory needed, as well as during the real compile phase. The value of lengthptr distinguishes the two phases. Arguments: options option bits, including any changes for this subpattern - oldims previous settings of ims option bits codeptr -> the address of the current code pointer ptrptr -> the address of the current pattern pointer errorcodeptr -> pointer to error code variable lookbehind TRUE if this is a lookbehind assertion reset_bracount TRUE to reset the count for each branch skipbytes skip this many bytes at start (for brackets and OP_COND) - firstbyteptr place to put the first required character, or a negative number - reqbyteptr place to put the last required character, or a negative number + cond_depth depth of nesting for conditional subpatterns + firstcharptr place to put the first required character + firstcharflagsptr place to put the first character flags, or a negative number + reqcharptr place to put the last required character + reqcharflagsptr place to put the last required character flags, or a negative number bcptr pointer to the chain of currently open branches cd points to the data block with tables pointers etc. lengthptr NULL during the real compile phase @@ -5290,27 +7085,34 @@ Returns: TRUE on success */ static BOOL -compile_regex(int options, int oldims, uschar **codeptr, const uschar **ptrptr, +compile_regex(int options, pcre_uchar **codeptr, const pcre_uchar **ptrptr, int *errorcodeptr, BOOL lookbehind, BOOL reset_bracount, int skipbytes, - int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr, compile_data *cd, - int *lengthptr) + int cond_depth, + pcre_uint32 *firstcharptr, pcre_int32 *firstcharflagsptr, + pcre_uint32 *reqcharptr, pcre_int32 *reqcharflagsptr, + branch_chain *bcptr, compile_data *cd, int *lengthptr) { -const uschar *ptr = *ptrptr; -uschar *code = *codeptr; -uschar *last_branch = code; -uschar *start_bracket = code; -uschar *reverse_count = NULL; -int firstbyte, reqbyte; -int branchfirstbyte, branchreqbyte; +const pcre_uchar *ptr = *ptrptr; +pcre_uchar *code = *codeptr; +pcre_uchar *last_branch = code; +pcre_uchar *start_bracket = code; +pcre_uchar *reverse_count = NULL; +open_capitem capitem; +int capnumber = 0; +pcre_uint32 firstchar, reqchar; +pcre_int32 firstcharflags, reqcharflags; +pcre_uint32 branchfirstchar, branchreqchar; +pcre_int32 branchfirstcharflags, branchreqcharflags; int length; -int orig_bracount; -int max_bracount; +unsigned int orig_bracount; +unsigned int max_bracount; branch_chain bc; bc.outer = bcptr; -bc.current = code; +bc.current_branch = code; -firstbyte = reqbyte = REQ_UNSET; +firstchar = reqchar = 0; +firstcharflags = reqcharflags = REQ_UNSET; /* Accumulate the length for use in the pre-compile phase. Start with the length of the BRA and KET and any extra bytes that are required at the @@ -5326,6 +7128,21 @@ the code that abstracts option settings at the start of the pattern and makes them global. It tests the value of length for (2 + 2*LINK_SIZE) in the pre-compile phase to find out whether anything has yet been compiled or not. */ +/* If this is a capturing subpattern, add to the chain of open capturing items +so that we can detect them if (*ACCEPT) is encountered. This is also used to +detect groups that contain recursive back references to themselves. Note that +only OP_CBRA need be tested here; changing this opcode to one of its variants, +e.g. OP_SCBRAPOS, happens later, after the group has been compiled. */ + +if (*code == OP_CBRA) + { + capnumber = GET2(code, 1 + LINK_SIZE); + capitem.number = capnumber; + capitem.next = cd->open_caps; + capitem.flag = FALSE; + cd->open_caps = &capitem; + } + /* Offset is set zero to mark that this bracket is still open */ PUT(code, 1, 0); @@ -5341,15 +7158,6 @@ for (;;) if (reset_bracount) cd->bracount = orig_bracount; - /* Handle a change of ims options at the start of the branch */ - - if ((options & PCRE_IMS) != oldims) - { - *code++ = OP_OPT; - *code++ = options & PCRE_IMS; - length += 2; - } - /* Set up dummy OP_REVERSE if lookbehind assertion */ if (lookbehind) @@ -5363,8 +7171,9 @@ for (;;) /* Now compile the branch; in the pre-compile phase its length gets added into the length. */ - if (!compile_branch(&options, &code, &ptr, errorcodeptr, &branchfirstbyte, - &branchreqbyte, &bc, cd, (lengthptr == NULL)? NULL : &length)) + if (!compile_branch(&options, &code, &ptr, errorcodeptr, &branchfirstchar, + &branchfirstcharflags, &branchreqchar, &branchreqcharflags, &bc, + cond_depth, cd, (lengthptr == NULL)? NULL : &length)) { *ptrptr = ptr; return FALSE; @@ -5379,62 +7188,92 @@ for (;;) if (lengthptr == NULL) { - /* If this is the first branch, the firstbyte and reqbyte values for the + /* If this is the first branch, the firstchar and reqchar values for the branch become the values for the regex. */ if (*last_branch != OP_ALT) { - firstbyte = branchfirstbyte; - reqbyte = branchreqbyte; + firstchar = branchfirstchar; + firstcharflags = branchfirstcharflags; + reqchar = branchreqchar; + reqcharflags = branchreqcharflags; } - /* If this is not the first branch, the first char and reqbyte have to + /* If this is not the first branch, the first char and reqchar have to match the values from all the previous branches, except that if the - previous value for reqbyte didn't have REQ_VARY set, it can still match, + previous value for reqchar didn't have REQ_VARY set, it can still match, and we set REQ_VARY for the regex. */ else { - /* If we previously had a firstbyte, but it doesn't match the new branch, - we have to abandon the firstbyte for the regex, but if there was - previously no reqbyte, it takes on the value of the old firstbyte. */ + /* If we previously had a firstchar, but it doesn't match the new branch, + we have to abandon the firstchar for the regex, but if there was + previously no reqchar, it takes on the value of the old firstchar. */ - if (firstbyte >= 0 && firstbyte != branchfirstbyte) + if (firstcharflags >= 0 && + (firstcharflags != branchfirstcharflags || firstchar != branchfirstchar)) { - if (reqbyte < 0) reqbyte = firstbyte; - firstbyte = REQ_NONE; + if (reqcharflags < 0) + { + reqchar = firstchar; + reqcharflags = firstcharflags; + } + firstcharflags = REQ_NONE; } - /* If we (now or from before) have no firstbyte, a firstbyte from the - branch becomes a reqbyte if there isn't a branch reqbyte. */ + /* If we (now or from before) have no firstchar, a firstchar from the + branch becomes a reqchar if there isn't a branch reqchar. */ - if (firstbyte < 0 && branchfirstbyte >= 0 && branchreqbyte < 0) - branchreqbyte = branchfirstbyte; + if (firstcharflags < 0 && branchfirstcharflags >= 0 && branchreqcharflags < 0) + { + branchreqchar = branchfirstchar; + branchreqcharflags = branchfirstcharflags; + } - /* Now ensure that the reqbytes match */ + /* Now ensure that the reqchars match */ - if ((reqbyte & ~REQ_VARY) != (branchreqbyte & ~REQ_VARY)) - reqbyte = REQ_NONE; - else reqbyte |= branchreqbyte; /* To "or" REQ_VARY */ + if (((reqcharflags & ~REQ_VARY) != (branchreqcharflags & ~REQ_VARY)) || + reqchar != branchreqchar) + reqcharflags = REQ_NONE; + else + { + reqchar = branchreqchar; + reqcharflags |= branchreqcharflags; /* To "or" REQ_VARY */ + } } /* If lookbehind, check that this branch matches a fixed-length string, and put the length into the OP_REVERSE item. Temporarily mark the end of the - branch with OP_END. */ + branch with OP_END. If the branch contains OP_RECURSE, the result is -3 + because there may be forward references that we can't check here. Set a + flag to cause another lookbehind check at the end. Why not do it all at the + end? Because common, erroneous checks are picked up here and the offset of + the problem can be shown. */ if (lookbehind) { int fixed_length; *code = OP_END; - fixed_length = find_fixedlength(last_branch, options); + fixed_length = find_fixedlength(last_branch, (options & PCRE_UTF8) != 0, + FALSE, cd); DPRINTF(("fixed length = %d\n", fixed_length)); - if (fixed_length < 0) + if (fixed_length == -3) + { + cd->check_lookbehind = TRUE; + } + else if (fixed_length < 0) { - *errorcodeptr = (fixed_length == -2)? ERR36 : ERR25; + *errorcodeptr = (fixed_length == -2)? ERR36 : + (fixed_length == -4)? ERR70: ERR25; *ptrptr = ptr; return FALSE; } - PUT(reverse_count, 0, fixed_length); + else + { + if (fixed_length > cd->max_lookbehind) + cd->max_lookbehind = fixed_length; + PUT(reverse_count, 0, fixed_length); + } } } @@ -5443,15 +7282,13 @@ for (;;) of offsets, with the field in the BRA item now becoming an offset to the first alternative. If there are no alternatives, it points to the end of the group. The length in the terminating ket is always the length of the whole - bracketed item. If any of the ims options were changed inside the group, - compile a resetting op-code following, except at the very end of the pattern. - Return leaving the pointer at the terminating char. */ + bracketed item. Return leaving the pointer at the terminating char. */ - if (*ptr != '|') + if (*ptr != CHAR_VERTICAL_LINE) { if (lengthptr == NULL) { - int branch_length = code - last_branch; + int branch_length = (int)(code - last_branch); do { int prev_length = GET(last_branch, 1); @@ -5465,16 +7302,28 @@ for (;;) /* Fill in the ket */ *code = OP_KET; - PUT(code, 1, code - start_bracket); + PUT(code, 1, (int)(code - start_bracket)); code += 1 + LINK_SIZE; - /* Resetting option if needed */ + /* If it was a capturing subpattern, check to see if it contained any + recursive back references. If so, we must wrap it in atomic brackets. + In any event, remove the block from the chain. */ - if ((options & PCRE_IMS) != oldims && *ptr == ')') + if (capnumber > 0) { - *code++ = OP_OPT; - *code++ = oldims; - length += 2; + if (cd->open_caps->flag) + { + memmove(start_bracket + 1 + LINK_SIZE, start_bracket, + IN_UCHARS(code - start_bracket)); + *start_bracket = OP_ONCE; + code += 1 + LINK_SIZE; + PUT(start_bracket, 1, (int)(code - start_bracket)); + *code = OP_KET; + PUT(code, 1, (int)(code - start_bracket)); + code += 1 + LINK_SIZE; + length += 2 + 2*LINK_SIZE; + } + cd->open_caps = cd->open_caps->next; } /* Retain the highest bracket number, in case resetting was used. */ @@ -5485,8 +7334,10 @@ for (;;) *codeptr = code; *ptrptr = ptr; - *firstbyteptr = firstbyte; - *reqbyteptr = reqbyte; + *firstcharptr = firstchar; + *firstcharflagsptr = firstcharflags; + *reqcharptr = reqchar; + *reqcharflagsptr = reqcharflags; if (lengthptr != NULL) { if (OFLOW_MAX - *lengthptr < length) @@ -5516,8 +7367,8 @@ for (;;) else { *code = OP_ALT; - PUT(code, 1, code - last_branch); - bc.current = last_branch = code; + PUT(code, 1, (int)(code - last_branch)); + bc.current_branch = last_branch = code; code += 1 + LINK_SIZE; } @@ -5536,8 +7387,8 @@ for (;;) /* Try to find out if this is an anchored regular expression. Consider each alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then -it's anchored. However, if this is a multiline pattern, then only OP_SOD -counts, since OP_CIRC can match in the middle. +it's anchored. However, if this is a multiline pattern, then only OP_SOD will +be found, because ^ generates OP_CIRCM in that mode. We can also consider a regex to be anchored if OP_SOM starts all its branches. This is the code for \G, which means "match at start of match position, taking @@ -5556,64 +7407,78 @@ and the highest back reference was greater than or equal to that level. However, by keeping a bitmap of the first 31 back references, we can catch some of the more common cases more precisely. +... A second exception is when the .* appears inside an atomic group, because +this prevents the number of characters it matches from being adjusted. + Arguments: code points to start of expression (the bracket) - options points to the options setting bracket_map a bitmap of which brackets we are inside while testing; this handles up to substring 31; after that we just have to take the less precise approach - backref_map the back reference bitmap + cd points to the compile data block + atomcount atomic group level Returns: TRUE or FALSE */ static BOOL -is_anchored(register const uschar *code, int *options, unsigned int bracket_map, - unsigned int backref_map) +is_anchored(register const pcre_uchar *code, unsigned int bracket_map, + compile_data *cd, int atomcount) { do { - const uschar *scode = first_significant_code(code + _erts_pcre_OP_lengths[*code], - options, PCRE_MULTILINE, FALSE); + const pcre_uchar *scode = first_significant_code( + code + PRIV(OP_lengths)[*code], FALSE); register int op = *scode; /* Non-capturing brackets */ - if (op == OP_BRA) + if (op == OP_BRA || op == OP_BRAPOS || + op == OP_SBRA || op == OP_SBRAPOS) { - if (!is_anchored(scode, options, bracket_map, backref_map)) return FALSE; + if (!is_anchored(scode, bracket_map, cd, atomcount)) return FALSE; } /* Capturing brackets */ - else if (op == OP_CBRA) + else if (op == OP_CBRA || op == OP_CBRAPOS || + op == OP_SCBRA || op == OP_SCBRAPOS) { int n = GET2(scode, 1+LINK_SIZE); int new_map = bracket_map | ((n < 32)? (1 << n) : 1); - if (!is_anchored(scode, options, new_map, backref_map)) return FALSE; + if (!is_anchored(scode, new_map, cd, atomcount)) return FALSE; + } + + /* Positive forward assertions and conditions */ + + else if (op == OP_ASSERT || op == OP_COND) + { + if (!is_anchored(scode, bracket_map, cd, atomcount)) return FALSE; } - /* Other brackets */ + /* Atomic groups */ - else if (op == OP_ASSERT || op == OP_ONCE || op == OP_COND) + else if (op == OP_ONCE || op == OP_ONCE_NC) { - if (!is_anchored(scode, options, bracket_map, backref_map)) return FALSE; + if (!is_anchored(scode, bracket_map, cd, atomcount + 1)) + return FALSE; } - /* .* is not anchored unless DOTALL is set and it isn't in brackets that - are or may be referenced. */ + /* .* is not anchored unless DOTALL is set (which generates OP_ALLANY) and + it isn't in brackets that are or may be referenced or inside an atomic + group. */ else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR || - op == OP_TYPEPOSSTAR) && - (*options & PCRE_DOTALL) != 0) + op == OP_TYPEPOSSTAR)) { - if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE; + if (scode[1] != OP_ALLANY || (bracket_map & cd->backref_map) != 0 || + atomcount > 0 || cd->had_pruneorskip) + return FALSE; } /* Check for explicit anchoring */ - else if (op != OP_SOD && op != OP_SOM && - ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC)) - return FALSE; + else if (op != OP_SOD && op != OP_SOM && op != OP_CIRC) return FALSE; + code += GET(code, 1); } while (*code == OP_ALT); /* Loop for each alternative */ @@ -5631,59 +7496,109 @@ return TRUE; matching and for non-DOTALL patterns that start with .* (which must start at the beginning or after \n). As in the case of is_anchored() (see above), we have to take account of back references to capturing brackets that contain .* -because in that case we can't make the assumption. +because in that case we can't make the assumption. Also, the appearance of .* +inside atomic brackets or in a pattern that contains *PRUNE or *SKIP does not +count, because once again the assumption no longer holds. Arguments: code points to start of expression (the bracket) bracket_map a bitmap of which brackets we are inside while testing; this handles up to substring 31; after that we just have to take the less precise approach - backref_map the back reference bitmap + cd points to the compile data + atomcount atomic group level Returns: TRUE or FALSE */ static BOOL -is_startline(const uschar *code, unsigned int bracket_map, - unsigned int backref_map) +is_startline(const pcre_uchar *code, unsigned int bracket_map, + compile_data *cd, int atomcount) { do { - const uschar *scode = first_significant_code(code + _erts_pcre_OP_lengths[*code], - NULL, 0, FALSE); + const pcre_uchar *scode = first_significant_code( + code + PRIV(OP_lengths)[*code], FALSE); register int op = *scode; + /* If we are at the start of a conditional assertion group, *both* the + conditional assertion *and* what follows the condition must satisfy the test + for start of line. Other kinds of condition fail. Note that there may be an + auto-callout at the start of a condition. */ + + if (op == OP_COND) + { + scode += 1 + LINK_SIZE; + if (*scode == OP_CALLOUT) scode += PRIV(OP_lengths)[OP_CALLOUT]; + switch (*scode) + { + case OP_CREF: + case OP_NCREF: + case OP_RREF: + case OP_NRREF: + case OP_DEF: + return FALSE; + + default: /* Assertion */ + if (!is_startline(scode, bracket_map, cd, atomcount)) return FALSE; + do scode += GET(scode, 1); while (*scode == OP_ALT); + scode += 1 + LINK_SIZE; + break; + } + scode = first_significant_code(scode, FALSE); + op = *scode; + } + /* Non-capturing brackets */ - if (op == OP_BRA) + if (op == OP_BRA || op == OP_BRAPOS || + op == OP_SBRA || op == OP_SBRAPOS) { - if (!is_startline(scode, bracket_map, backref_map)) return FALSE; + if (!is_startline(scode, bracket_map, cd, atomcount)) return FALSE; } /* Capturing brackets */ - else if (op == OP_CBRA) + else if (op == OP_CBRA || op == OP_CBRAPOS || + op == OP_SCBRA || op == OP_SCBRAPOS) { int n = GET2(scode, 1+LINK_SIZE); int new_map = bracket_map | ((n < 32)? (1 << n) : 1); - if (!is_startline(scode, new_map, backref_map)) return FALSE; + if (!is_startline(scode, new_map, cd, atomcount)) return FALSE; + } + + /* Positive forward assertions */ + + else if (op == OP_ASSERT) + { + if (!is_startline(scode, bracket_map, cd, atomcount)) return FALSE; } - /* Other brackets */ + /* Atomic brackets */ - else if (op == OP_ASSERT || op == OP_ONCE || op == OP_COND) - { if (!is_startline(scode, bracket_map, backref_map)) return FALSE; } + else if (op == OP_ONCE || op == OP_ONCE_NC) + { + if (!is_startline(scode, bracket_map, cd, atomcount + 1)) return FALSE; + } - /* .* means "start at start or after \n" if it isn't in brackets that - may be referenced. */ + /* .* means "start at start or after \n" if it isn't in atomic brackets or + brackets that may be referenced, as long as the pattern does not contain + *PRUNE or *SKIP, because these break the feature. Consider, for example, + /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab", i.e. not at the + start of a line. */ else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR) { - if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE; + if (scode[1] != OP_ANY || (bracket_map & cd->backref_map) != 0 || + atomcount > 0 || cd->had_pruneorskip) + return FALSE; } - /* Check for explicit circumflex */ + /* Check for explicit circumflex; anything else gives a FALSE result. Note + in particular that this includes atomic brackets OP_ONCE and OP_ONCE_NC + because the number of characters matched by .* cannot be adjusted inside + them. */ - else if (op != OP_CIRC) return FALSE; + else if (op != OP_CIRC && op != OP_CIRCM) return FALSE; /* Move on to the next alternative */ @@ -5709,58 +7624,82 @@ we return that char, otherwise -1. Arguments: code points to start of expression (the bracket) - options pointer to the options (used to check casing changes) + flags points to the first char flags, or to REQ_NONE inassert TRUE if in an assertion -Returns: -1 or the fixed first char +Returns: the fixed first char, or 0 with REQ_NONE in flags */ -static int -find_firstassertedchar(const uschar *code, int *options, BOOL inassert) +static pcre_uint32 +find_firstassertedchar(const pcre_uchar *code, pcre_int32 *flags, + BOOL inassert) { -register int c = -1; +register pcre_uint32 c = 0; +int cflags = REQ_NONE; + +*flags = REQ_NONE; do { - int d; - const uschar *scode = - first_significant_code(code + 1+LINK_SIZE, options, PCRE_CASELESS, TRUE); - register int op = *scode; + pcre_uint32 d; + int dflags; + int xl = (*code == OP_CBRA || *code == OP_SCBRA || + *code == OP_CBRAPOS || *code == OP_SCBRAPOS)? IMM2_SIZE:0; + const pcre_uchar *scode = first_significant_code(code + 1+LINK_SIZE + xl, + TRUE); + register pcre_uchar op = *scode; switch(op) { default: - return -1; + return 0; case OP_BRA: + case OP_BRAPOS: case OP_CBRA: + case OP_SCBRA: + case OP_CBRAPOS: + case OP_SCBRAPOS: case OP_ASSERT: case OP_ONCE: + case OP_ONCE_NC: case OP_COND: - if ((d = find_firstassertedchar(scode, options, op == OP_ASSERT)) < 0) - return -1; - if (c < 0) c = d; else if (c != d) return -1; + d = find_firstassertedchar(scode, &dflags, op == OP_ASSERT); + if (dflags < 0) + return 0; + if (cflags < 0) { c = d; cflags = dflags; } else if (c != d || cflags != dflags) return 0; break; - case OP_EXACT: /* Fall through */ - scode += 2; + case OP_EXACT: + scode += IMM2_SIZE; + /* Fall through */ case OP_CHAR: - case OP_CHARNC: case OP_PLUS: case OP_MINPLUS: case OP_POSPLUS: - if (!inassert) return -1; - if (c < 0) - { - c = scode[1]; - if ((*options & PCRE_CASELESS) != 0) c |= REQ_CASELESS; - } - else if (c != scode[1]) return -1; + if (!inassert) return 0; + if (cflags < 0) { c = scode[1]; cflags = 0; } + else if (c != scode[1]) return 0; + break; + + case OP_EXACTI: + scode += IMM2_SIZE; + /* Fall through */ + + case OP_CHARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_POSPLUSI: + if (!inassert) return 0; + if (cflags < 0) { c = scode[1]; cflags = REQ_CASELESS; } + else if (c != scode[1]) return 0; break; } code += GET(code, 1); } while (*code == OP_ALT); + +*flags = cflags; return c; } @@ -5778,7 +7717,7 @@ compatibility. The new function is given a new name. Arguments: pattern the regular expression options various option bits - errorcodeptr pointer to error code variable (erts_pcre_compile2() only) + errorcodeptr pointer to error code variable (pcre_compile2() only) can be NULL if you don't want a code value errorptr pointer to pointer to error text erroroffset ptr offset in pattern where error was detected @@ -5788,30 +7727,59 @@ Returns: pointer to compiled data block, or NULL on error, with errorptr and erroroffset set */ -PCRE_EXP_DEFN pcre * -erts_pcre_compile(const char *pattern, int options, const char **errorptr, +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION +pcre_compile(const char *pattern, int options, const char **errorptr, int *erroroffset, const unsigned char *tables) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN pcre16 * PCRE_CALL_CONVENTION +pcre16_compile(PCRE_SPTR16 pattern, int options, const char **errorptr, + int *erroroffset, const unsigned char *tables) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN pcre32 * PCRE_CALL_CONVENTION +pcre32_compile(PCRE_SPTR32 pattern, int options, const char **errorptr, + int *erroroffset, const unsigned char *tables) +#endif { -return erts_pcre_compile2(pattern, options, NULL, errorptr, erroroffset, tables); +#if defined COMPILE_PCRE8 +return pcre_compile2(pattern, options, NULL, errorptr, erroroffset, tables); +#elif defined COMPILE_PCRE16 +return pcre16_compile2(pattern, options, NULL, errorptr, erroroffset, tables); +#elif defined COMPILE_PCRE32 +return pcre32_compile2(pattern, options, NULL, errorptr, erroroffset, tables); +#endif } -PCRE_EXP_DEFN pcre * -erts_pcre_compile2(const char *pattern, int options, int *errorcodeptr, +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION +pcre_compile2(const char *pattern, int options, int *errorcodeptr, const char **errorptr, int *erroroffset, const unsigned char *tables) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN pcre16 * PCRE_CALL_CONVENTION +pcre16_compile2(PCRE_SPTR16 pattern, int options, int *errorcodeptr, + const char **errorptr, int *erroroffset, const unsigned char *tables) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN pcre32 * PCRE_CALL_CONVENTION +pcre32_compile2(PCRE_SPTR32 pattern, int options, int *errorcodeptr, + const char **errorptr, int *erroroffset, const unsigned char *tables) +#endif { -real_pcre *re; +REAL_PCRE *re; int length = 1; /* For final END opcode */ -int firstbyte, reqbyte, newline; +pcre_int32 firstcharflags, reqcharflags; +pcre_uint32 firstchar, reqchar; +pcre_uint32 limit_match = PCRE_UINT32_MAX; +pcre_uint32 limit_recursion = PCRE_UINT32_MAX; +int newline; int errorcode = 0; int skipatstart = 0; -#ifdef SUPPORT_UTF8 -BOOL utf8; -#endif +BOOL utf; +BOOL never_utf = FALSE; size_t size; -uschar *code; -const uschar *codestart; -const uschar *ptr; +pcre_uchar *code; +const pcre_uchar *codestart; +const pcre_uchar *ptr; compile_data compile_block; compile_data *cd = &compile_block; @@ -5819,13 +7787,14 @@ compile_data *cd = &compile_block; computing the amount of memory that is needed. Compiled items are thrown away as soon as possible, so that a fairly large buffer should be sufficient for this purpose. The same space is used in the second phase for remembering where -to fill in forward references to subpatterns. */ +to fill in forward references to subpatterns. That may overflow, in which case +new memory is obtained from malloc(). */ -uschar cworkspace[COMPILE_WORK_SIZE]; +pcre_uchar cworkspace[COMPILE_WORK_SIZE]; /* Set this early so that early errors get offset 0. */ -ptr = (const uschar *)pattern; +ptr = (const pcre_uchar *)pattern; /* We can't pass back an error message if errorptr is NULL; I guess the best we can do is just return NULL, but we can set a code value if there is a code @@ -5850,60 +7819,113 @@ if (erroroffset == NULL) *erroroffset = 0; -/* Can't support UTF8 unless PCRE has been compiled to include the code. */ +/* Set up pointers to the individual character tables */ -#ifdef SUPPORT_UTF8 -utf8 = (options & PCRE_UTF8) != 0; -if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0 && - (*erroroffset = _erts_pcre_valid_utf8((uschar *)pattern, -1)) >= 0) - { - errorcode = ERR44; - goto PCRE_EARLY_ERROR_RETURN2; - } -#else -if ((options & PCRE_UTF8) != 0) - { - errorcode = ERR32; - goto PCRE_EARLY_ERROR_RETURN; - } -#endif +if (tables == NULL) tables = PRIV(default_tables); +cd->lcc = tables + lcc_offset; +cd->fcc = tables + fcc_offset; +cd->cbits = tables + cbits_offset; +cd->ctypes = tables + ctypes_offset; + +/* Check that all undefined public option bits are zero */ -if ((options & ~PUBLIC_OPTIONS) != 0) +if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0) { errorcode = ERR17; goto PCRE_EARLY_ERROR_RETURN; } -/* Set up pointers to the individual character tables */ +/* If PCRE_NEVER_UTF is set, remember it. */ -if (tables == NULL) tables = _erts_pcre_default_tables; -cd->lcc = tables + lcc_offset; -cd->fcc = tables + fcc_offset; -cd->cbits = tables + cbits_offset; -cd->ctypes = tables + ctypes_offset; +if ((options & PCRE_NEVER_UTF) != 0) never_utf = TRUE; /* Check for global one-time settings at the start of the pattern, and remember the offset for later. */ -while (ptr[skipatstart] == '(' && ptr[skipatstart+1] == '*') +cd->external_flags = 0; /* Initialize here for LIMIT_MATCH/RECURSION */ + +while (ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && + ptr[skipatstart+1] == CHAR_ASTERISK) { int newnl = 0; int newbsr = 0; - if (strncmp((char *)(ptr+skipatstart+2), "CR)", 3) == 0) +/* For completeness and backward compatibility, (*UTFn) is supported in the +relevant libraries, but (*UTF) is generic and always supported. Note that +PCRE_UTF8 == PCRE_UTF16 == PCRE_UTF32. */ + +#ifdef COMPILE_PCRE8 + if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_UTF8_RIGHTPAR, 5) == 0) + { skipatstart += 7; options |= PCRE_UTF8; continue; } +#endif +#ifdef COMPILE_PCRE16 + if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_UTF16_RIGHTPAR, 6) == 0) + { skipatstart += 8; options |= PCRE_UTF16; continue; } +#endif +#ifdef COMPILE_PCRE32 + if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_UTF32_RIGHTPAR, 6) == 0) + { skipatstart += 8; options |= PCRE_UTF32; continue; } +#endif + + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_UTF_RIGHTPAR, 4) == 0) + { skipatstart += 6; options |= PCRE_UTF8; continue; } + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_UCP_RIGHTPAR, 4) == 0) + { skipatstart += 6; options |= PCRE_UCP; continue; } + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_NO_START_OPT_RIGHTPAR, 13) == 0) + { skipatstart += 15; options |= PCRE_NO_START_OPTIMIZE; continue; } + + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_LIMIT_MATCH_EQ, 12) == 0) + { + pcre_uint32 c = 0; + int p = skipatstart + 14; + while (isdigit(ptr[p])) + { + if (c > PCRE_UINT32_MAX / 10 - 1) break; /* Integer overflow */ + c = c*10 + ptr[p++] - CHAR_0; + } + if (ptr[p++] != CHAR_RIGHT_PARENTHESIS) break; + if (c < limit_match) + { + limit_match = c; + cd->external_flags |= PCRE_MLSET; + } + skipatstart = p; + continue; + } + + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_LIMIT_RECURSION_EQ, 16) == 0) + { + pcre_uint32 c = 0; + int p = skipatstart + 18; + while (isdigit(ptr[p])) + { + if (c > PCRE_UINT32_MAX / 10 - 1) break; /* Integer overflow check */ + c = c*10 + ptr[p++] - CHAR_0; + } + if (ptr[p++] != CHAR_RIGHT_PARENTHESIS) break; + if (c < limit_recursion) + { + limit_recursion = c; + cd->external_flags |= PCRE_RLSET; + } + skipatstart = p; + continue; + } + + if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_CR_RIGHTPAR, 3) == 0) { skipatstart += 5; newnl = PCRE_NEWLINE_CR; } - else if (strncmp((char *)(ptr+skipatstart+2), "LF)", 3) == 0) + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_LF_RIGHTPAR, 3) == 0) { skipatstart += 5; newnl = PCRE_NEWLINE_LF; } - else if (strncmp((char *)(ptr+skipatstart+2), "CRLF)", 5) == 0) + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_CRLF_RIGHTPAR, 5) == 0) { skipatstart += 7; newnl = PCRE_NEWLINE_CR + PCRE_NEWLINE_LF; } - else if (strncmp((char *)(ptr+skipatstart+2), "ANY)", 4) == 0) + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_ANY_RIGHTPAR, 4) == 0) { skipatstart += 6; newnl = PCRE_NEWLINE_ANY; } - else if (strncmp((char *)(ptr+skipatstart+2), "ANYCRLF)", 8) == 0) + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_ANYCRLF_RIGHTPAR, 8) == 0) { skipatstart += 10; newnl = PCRE_NEWLINE_ANYCRLF; } - else if (strncmp((char *)(ptr+skipatstart+2), "BSR_ANYCRLF)", 12) == 0) + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_BSR_ANYCRLF_RIGHTPAR, 12) == 0) { skipatstart += 14; newbsr = PCRE_BSR_ANYCRLF; } - else if (strncmp((char *)(ptr+skipatstart+2), "BSR_UNICODE)", 12) == 0) + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_BSR_UNICODE_RIGHTPAR, 12) == 0) { skipatstart += 14; newbsr = PCRE_BSR_UNICODE; } if (newnl != 0) @@ -5913,15 +7935,57 @@ while (ptr[skipatstart] == '(' && ptr[skipatstart+1] == '*') else break; } +/* PCRE_UTF(16|32) have the same value as PCRE_UTF8. */ +utf = (options & PCRE_UTF8) != 0; +if (utf && never_utf) + { + errorcode = ERR78; + goto PCRE_EARLY_ERROR_RETURN2; + } + +/* Can't support UTF unless PCRE has been compiled to include the code. The +return of an error code from PRIV(valid_utf)() is a new feature, introduced in +release 8.13. It is passed back from pcre_[dfa_]exec(), but at the moment is +not used here. */ + +#ifdef SUPPORT_UTF +if (utf && (options & PCRE_NO_UTF8_CHECK) == 0 && + (errorcode = PRIV(valid_utf)((PCRE_PUCHAR)pattern, -1, erroroffset)) != 0) + { +#if defined COMPILE_PCRE8 + errorcode = ERR44; +#elif defined COMPILE_PCRE16 + errorcode = ERR74; +#elif defined COMPILE_PCRE32 + errorcode = ERR77; +#endif + goto PCRE_EARLY_ERROR_RETURN2; + } +#else +if (utf) + { + errorcode = ERR32; + goto PCRE_EARLY_ERROR_RETURN; + } +#endif + +/* Can't support UCP unless PCRE has been compiled to include the code. */ + +#ifndef SUPPORT_UCP +if ((options & PCRE_UCP) != 0) + { + errorcode = ERR67; + goto PCRE_EARLY_ERROR_RETURN; + } +#endif + /* Check validity of \R options. */ -switch (options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) +if ((options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) == + (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) { - case 0: - case PCRE_BSR_ANYCRLF: - case PCRE_BSR_UNICODE: - break; - default: errorcode = ERR56; goto PCRE_EARLY_ERROR_RETURN; + errorcode = ERR56; + goto PCRE_EARLY_ERROR_RETURN; } /* Handle different types of newline. The three bits give seven cases. The @@ -5931,10 +7995,10 @@ current code allows for fixed one- or two-byte sequences, plus "any" and switch (options & PCRE_NEWLINE_BITS) { case 0: newline = NEWLINE; break; /* Build-time default */ - case PCRE_NEWLINE_CR: newline = '\r'; break; - case PCRE_NEWLINE_LF: newline = '\n'; break; + case PCRE_NEWLINE_CR: newline = CHAR_CR; break; + case PCRE_NEWLINE_LF: newline = CHAR_NL; break; case PCRE_NEWLINE_CR+ - PCRE_NEWLINE_LF: newline = ('\r' << 8) | '\n'; break; + PCRE_NEWLINE_LF: newline = (CHAR_CR << 8) | CHAR_NL; break; case PCRE_NEWLINE_ANY: newline = -1; break; case PCRE_NEWLINE_ANYCRLF: newline = -2; break; default: errorcode = ERR56; goto PCRE_EARLY_ERROR_RETURN; @@ -5974,7 +8038,10 @@ cd->backref_map = 0; /* Reflect pattern for debugging output */ DPRINTF(("------------------------------------------------------------------\n")); -DPRINTF(("%s\n", pattern)); +#ifdef PCRE_DEBUG +print_puchar(stdout, (PCRE_PUCHAR)pattern); +#endif +DPRINTF(("\n")); /* Pretend to compile the pattern while actually just accumulating the length of memory required. This behaviour is triggered by passing a non-NULL final @@ -5987,14 +8054,17 @@ cd->bracount = cd->final_bracount = 0; cd->names_found = 0; cd->name_entry_size = 0; cd->name_table = NULL; -cd->start_workspace = cworkspace; cd->start_code = cworkspace; cd->hwm = cworkspace; -cd->start_pattern = (const uschar *)pattern; -cd->end_pattern = (const uschar *)(pattern + strlen(pattern)); +cd->start_workspace = cworkspace; +cd->workspace_size = COMPILE_WORK_SIZE; +cd->start_pattern = (const pcre_uchar *)pattern; +cd->end_pattern = (const pcre_uchar *)(pattern + STRLEN_UC((const pcre_uchar *)pattern)); cd->req_varyopt = 0; +cd->assert_depth = 0; +cd->max_lookbehind = 0; cd->external_options = options; -cd->external_flags = 0; +cd->open_caps = NULL; /* Now do the pre-compile. On error, errorcode will be set non-zero, so we don't need to look at the result of the function here. The initial options have @@ -6005,13 +8075,13 @@ outside can help speed up starting point checks. */ ptr += skipatstart; code = cworkspace; *code = OP_BRA; -(void)compile_regex(cd->external_options, cd->external_options & PCRE_IMS, - &code, &ptr, &errorcode, FALSE, FALSE, 0, &firstbyte, &reqbyte, NULL, cd, - &length); +(void)compile_regex(cd->external_options, &code, &ptr, &errorcode, FALSE, + FALSE, 0, 0, &firstchar, &firstcharflags, &reqchar, &reqcharflags, NULL, + cd, &length); if (errorcode != 0) goto PCRE_EARLY_ERROR_RETURN; DPRINTF(("end pre-compile: length=%d workspace=%d\n", length, - cd->hwm - cworkspace)); + (int)(cd->hwm - cworkspace))); if (length > MAX_PATTERN_SIZE) { @@ -6024,8 +8094,8 @@ externally provided function. Integer overflow should no longer be possible because nowadays we limit the maximum value of cd->names_found and cd->name_entry_size. */ -size = length + sizeof(real_pcre) + cd->names_found * (cd->name_entry_size + 3); -re = (real_pcre *)(erts_pcre_malloc)(size); +size = sizeof(REAL_PCRE) + (length + cd->names_found * cd->name_entry_size) * sizeof(pcre_uchar); +re = (REAL_PCRE *)(PUBL(malloc))(size); if (re == NULL) { @@ -6040,18 +8110,24 @@ regex compiled on a system with 4-byte pointers is run on another with 8-byte pointers. */ re->magic_number = MAGIC_NUMBER; -re->size = size; +re->size = (int)size; re->options = cd->external_options; re->flags = cd->external_flags; -re->dummy1 = 0; -re->first_byte = 0; -re->req_byte = 0; -re->name_table_offset = sizeof(real_pcre); +re->limit_match = limit_match; +re->limit_recursion = limit_recursion; +re->first_char = 0; +re->req_char = 0; +re->name_table_offset = sizeof(REAL_PCRE) / sizeof(pcre_uchar); re->name_entry_size = cd->name_entry_size; re->name_count = cd->names_found; re->ref_count = 0; -re->tables = (tables == _erts_pcre_default_tables)? NULL : tables; +re->tables = (tables == PRIV(default_tables))? NULL : tables; re->nullpad = NULL; +#ifdef COMPILE_PCRE32 +re->dummy = 0; +#else +re->dummy1 = re->dummy2 = re->dummy3 = 0; +#endif /* The starting points of the name/number translation table and of the code are passed around in the compile data block. The start/end pattern and initial @@ -6061,69 +8137,144 @@ field; this time it's used for remembering forward references to subpatterns. */ cd->final_bracount = cd->bracount; /* Save for checking forward references */ +cd->assert_depth = 0; cd->bracount = 0; +cd->max_lookbehind = 0; cd->names_found = 0; -cd->name_table = (uschar *)re + re->name_table_offset; +cd->name_table = (pcre_uchar *)re + re->name_table_offset; codestart = cd->name_table + re->name_entry_size * re->name_count; cd->start_code = codestart; -cd->hwm = cworkspace; +cd->hwm = (pcre_uchar *)(cd->start_workspace); cd->req_varyopt = 0; cd->had_accept = FALSE; +cd->had_pruneorskip = FALSE; +cd->check_lookbehind = FALSE; +cd->open_caps = NULL; /* Set up a starting, non-extracting bracket, then compile the expression. On error, errorcode will be set non-zero, so we don't need to look at the result of the function here. */ -ptr = (const uschar *)pattern + skipatstart; -code = (uschar *)codestart; +ptr = (const pcre_uchar *)pattern + skipatstart; +code = (pcre_uchar *)codestart; *code = OP_BRA; -(void)compile_regex(re->options, re->options & PCRE_IMS, &code, &ptr, - &errorcode, FALSE, FALSE, 0, &firstbyte, &reqbyte, NULL, cd, NULL); +(void)compile_regex(re->options, &code, &ptr, &errorcode, FALSE, FALSE, 0, 0, + &firstchar, &firstcharflags, &reqchar, &reqcharflags, NULL, cd, NULL); re->top_bracket = cd->bracount; re->top_backref = cd->top_backref; -re->flags = cd->external_flags; +re->max_lookbehind = cd->max_lookbehind; +re->flags = cd->external_flags | PCRE_MODE; -if (cd->had_accept) reqbyte = -1; /* Must disable after (*ACCEPT) */ +if (cd->had_accept) + { + reqchar = 0; /* Must disable after (*ACCEPT) */ + reqcharflags = REQ_NONE; + } /* If not reached end of pattern on success, there's an excess bracket. */ -if (errorcode == 0 && *ptr != 0) errorcode = ERR22; +if (errorcode == 0 && *ptr != CHAR_NULL) errorcode = ERR22; /* Fill in the terminating state and check for disastrous overflow, but if debugging, leave the test till after things are printed out. */ *code++ = OP_END; -#ifndef DEBUG +#ifndef PCRE_DEBUG if (code - codestart > length) errorcode = ERR23; #endif -/* Fill in any forward references that are required. */ +#ifdef SUPPORT_VALGRIND +/* If the estimated length exceeds the really used length, mark the extra +allocated memory as unaddressable, so that any out-of-bound reads can be +detected. */ +VALGRIND_MAKE_MEM_NOACCESS(code, (length - (code - codestart)) * sizeof(pcre_uchar)); +#endif + +/* Fill in any forward references that are required. There may be repeated +references; optimize for them, as searching a large regex takes time. */ -while (errorcode == 0 && cd->hwm > cworkspace) +if (cd->hwm > cd->start_workspace) { - int offset, recno; - const uschar *groupptr; - cd->hwm -= LINK_SIZE; - offset = GET(cd->hwm, 0); - recno = GET(codestart, offset); - groupptr = find_bracket(codestart, (re->options & PCRE_UTF8) != 0, recno); - if (groupptr == NULL) errorcode = ERR53; - else PUT(((uschar *)codestart), offset, groupptr - codestart); + int prev_recno = -1; + const pcre_uchar *groupptr = NULL; + while (errorcode == 0 && cd->hwm > cd->start_workspace) + { + int offset, recno; + cd->hwm -= LINK_SIZE; + offset = GET(cd->hwm, 0); + recno = GET(codestart, offset); + if (recno != prev_recno) + { + groupptr = PRIV(find_bracket)(codestart, utf, recno); + prev_recno = recno; + } + if (groupptr == NULL) errorcode = ERR53; + else PUT(((pcre_uchar *)codestart), offset, (int)(groupptr - codestart)); + } } +/* If the workspace had to be expanded, free the new memory. */ + +if (cd->workspace_size > COMPILE_WORK_SIZE) + (PUBL(free))((void *)cd->start_workspace); + /* Give an error if there's back reference to a non-existent capturing subpattern. */ if (errorcode == 0 && re->top_backref > re->top_bracket) errorcode = ERR15; +/* If there were any lookbehind assertions that contained OP_RECURSE +(recursions or subroutine calls), a flag is set for them to be checked here, +because they may contain forward references. Actual recursions cannot be fixed +length, but subroutine calls can. It is done like this so that those without +OP_RECURSE that are not fixed length get a diagnosic with a useful offset. The +exceptional ones forgo this. We scan the pattern to check that they are fixed +length, and set their lengths. */ + +if (cd->check_lookbehind) + { + pcre_uchar *cc = (pcre_uchar *)codestart; + + /* Loop, searching for OP_REVERSE items, and process those that do not have + their length set. (Actually, it will also re-process any that have a length + of zero, but that is a pathological case, and it does no harm.) When we find + one, we temporarily terminate the branch it is in while we scan it. */ + + for (cc = (pcre_uchar *)PRIV(find_bracket)(codestart, utf, -1); + cc != NULL; + cc = (pcre_uchar *)PRIV(find_bracket)(cc, utf, -1)) + { + if (GET(cc, 1) == 0) + { + int fixed_length; + pcre_uchar *be = cc - 1 - LINK_SIZE + GET(cc, -LINK_SIZE); + int end_op = *be; + *be = OP_END; + fixed_length = find_fixedlength(cc, (re->options & PCRE_UTF8) != 0, TRUE, + cd); + *be = end_op; + DPRINTF(("fixed length = %d\n", fixed_length)); + if (fixed_length < 0) + { + errorcode = (fixed_length == -2)? ERR36 : + (fixed_length == -4)? ERR70 : ERR25; + break; + } + if (fixed_length > cd->max_lookbehind) cd->max_lookbehind = fixed_length; + PUT(cc, 1, fixed_length); + } + cc += 1 + LINK_SIZE; + } + } + /* Failed to compile, or error while post-processing */ if (errorcode != 0) { - (erts_pcre_free)(re); + (PUBL(free))(re); PCRE_EARLY_ERROR_RETURN: - *erroroffset = ptr - (const uschar *)pattern; + *erroroffset = (int)(ptr - (const pcre_uchar *)pattern); PCRE_EARLY_ERROR_RETURN2: *errorptr = find_error_text(errorcode); if (errorcodeptr != NULL) *errorcodeptr = errorcode; @@ -6131,33 +8282,57 @@ if (errorcode != 0) } /* If the anchored option was not passed, set the flag if we can determine that -the pattern is anchored by virtue of ^ characters or \A or anything else (such -as starting with .* when DOTALL is set). +the pattern is anchored by virtue of ^ characters or \A or anything else, such +as starting with non-atomic .* when DOTALL is set and there are no occurrences +of *PRUNE or *SKIP. Otherwise, if we know what the first byte has to be, save it, because that speeds up unanchored matches no end. If not, see if we can set the PCRE_STARTLINE flag. This is helpful for multiline matches when all branches -start with ^. and also when all branches start with .* for non-DOTALL matches. -*/ +start with ^. and also when all branches start with non-atomic .* for +non-DOTALL matches when *PRUNE and SKIP are not present. */ if ((re->options & PCRE_ANCHORED) == 0) { - int temp_options = re->options; /* May get changed during these scans */ - if (is_anchored(codestart, &temp_options, 0, cd->backref_map)) - re->options |= PCRE_ANCHORED; + if (is_anchored(codestart, 0, cd, 0)) re->options |= PCRE_ANCHORED; else { - if (firstbyte < 0) - firstbyte = find_firstassertedchar(codestart, &temp_options, FALSE); - if (firstbyte >= 0) /* Remove caseless flag for non-caseable chars */ + if (firstcharflags < 0) + firstchar = find_firstassertedchar(codestart, &firstcharflags, FALSE); + if (firstcharflags >= 0) /* Remove caseless flag for non-caseable chars */ { - int ch = firstbyte & 255; - re->first_byte = ((firstbyte & REQ_CASELESS) != 0 && - cd->fcc[ch] == ch)? ch : firstbyte; +#if defined COMPILE_PCRE8 + re->first_char = firstchar & 0xff; +#elif defined COMPILE_PCRE16 + re->first_char = firstchar & 0xffff; +#elif defined COMPILE_PCRE32 + re->first_char = firstchar; +#endif + if ((firstcharflags & REQ_CASELESS) != 0) + { +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + /* We ignore non-ASCII first chars in 8 bit mode. */ + if (utf) + { + if (re->first_char < 128) + { + if (cd->fcc[re->first_char] != re->first_char) + re->flags |= PCRE_FCH_CASELESS; + } + else if (UCD_OTHERCASE(re->first_char) != re->first_char) + re->flags |= PCRE_FCH_CASELESS; + } + else +#endif + if (MAX_255(re->first_char) + && cd->fcc[re->first_char] != re->first_char) + re->flags |= PCRE_FCH_CASELESS; + } + re->flags |= PCRE_FIRSTSET; } - else if (is_startline(codestart, 0, cd->backref_map)) - re->flags |= PCRE_STARTLINE; + + else if (is_startline(codestart, 0, cd, 0)) re->flags |= PCRE_STARTLINE; } } @@ -6165,20 +8340,43 @@ if ((re->options & PCRE_ANCHORED) == 0) variable length item in the regex. Remove the caseless flag for non-caseable bytes. */ -if (reqbyte >= 0 && - ((re->options & PCRE_ANCHORED) == 0 || (reqbyte & REQ_VARY) != 0)) +if (reqcharflags >= 0 && + ((re->options & PCRE_ANCHORED) == 0 || (reqcharflags & REQ_VARY) != 0)) { - int ch = reqbyte & 255; - re->req_byte = ((reqbyte & REQ_CASELESS) != 0 && - cd->fcc[ch] == ch)? (reqbyte & ~REQ_CASELESS) : reqbyte; +#if defined COMPILE_PCRE8 + re->req_char = reqchar & 0xff; +#elif defined COMPILE_PCRE16 + re->req_char = reqchar & 0xffff; +#elif defined COMPILE_PCRE32 + re->req_char = reqchar; +#endif + if ((reqcharflags & REQ_CASELESS) != 0) + { +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + /* We ignore non-ASCII first chars in 8 bit mode. */ + if (utf) + { + if (re->req_char < 128) + { + if (cd->fcc[re->req_char] != re->req_char) + re->flags |= PCRE_RCH_CASELESS; + } + else if (UCD_OTHERCASE(re->req_char) != re->req_char) + re->flags |= PCRE_RCH_CASELESS; + } + else +#endif + if (MAX_255(re->req_char) && cd->fcc[re->req_char] != re->req_char) + re->flags |= PCRE_RCH_CASELESS; + } + re->flags |= PCRE_REQCHSET; } /* Print out the compiled data if debugging is enabled. This is never the case when building a production library. */ -#ifdef DEBUG - +#ifdef PCRE_DEBUG printf("Length = %d top_bracket = %d top_backref = %d\n", length, re->top_bracket, re->top_backref); @@ -6186,38 +8384,50 @@ printf("Options=%08x\n", re->options); if ((re->flags & PCRE_FIRSTSET) != 0) { - int ch = re->first_byte & 255; - const char *caseless = ((re->first_byte & REQ_CASELESS) == 0)? - "" : " (caseless)"; - if (isprint(ch)) printf("First char = %c%s\n", ch, caseless); + pcre_uchar ch = re->first_char; + const char *caseless = + ((re->flags & PCRE_FCH_CASELESS) == 0)? "" : " (caseless)"; + if (PRINTABLE(ch)) printf("First char = %c%s\n", ch, caseless); else printf("First char = \\x%02x%s\n", ch, caseless); } if ((re->flags & PCRE_REQCHSET) != 0) { - int ch = re->req_byte & 255; - const char *caseless = ((re->req_byte & REQ_CASELESS) == 0)? - "" : " (caseless)"; - if (isprint(ch)) printf("Req char = %c%s\n", ch, caseless); + pcre_uchar ch = re->req_char; + const char *caseless = + ((re->flags & PCRE_RCH_CASELESS) == 0)? "" : " (caseless)"; + if (PRINTABLE(ch)) printf("Req char = %c%s\n", ch, caseless); else printf("Req char = \\x%02x%s\n", ch, caseless); } -pcre_printint(re, stdout, TRUE); +#if defined COMPILE_PCRE8 +pcre_printint((pcre *)re, stdout, TRUE); +#elif defined COMPILE_PCRE16 +pcre16_printint((pcre *)re, stdout, TRUE); +#elif defined COMPILE_PCRE32 +pcre32_printint((pcre *)re, stdout, TRUE); +#endif /* This check is done here in the debugging case so that the code that was compiled can be seen. */ if (code - codestart > length) { - (erts_pcre_free)(re); + (PUBL(free))(re); *errorptr = find_error_text(ERR23); - *erroroffset = ptr - (uschar *)pattern; + *erroroffset = ptr - (pcre_uchar *)pattern; if (errorcodeptr != NULL) *errorcodeptr = ERR23; return NULL; } -#endif /* DEBUG */ +#endif /* PCRE_DEBUG */ +#if defined COMPILE_PCRE8 return (pcre *)re; +#elif defined COMPILE_PCRE16 +return (pcre16 *)re; +#elif defined COMPILE_PCRE32 +return (pcre32 *)re; +#endif } /* End of pcre_compile.c */ diff --git a/erts/emulator/pcre/pcre_config.c b/erts/emulator/pcre/pcre_config.c index 122327d67d..a116cc7ac6 100644 --- a/erts/emulator/pcre/pcre_config.c +++ b/erts/emulator/pcre/pcre_config.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2012 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. */ -/* This module contains the external function erts_pcre_config(). */ +/* This module contains the external function pcre_config(). */ /* %ExternalCopyright% */ @@ -46,6 +46,13 @@ POSSIBILITY OF SUCH DAMAGE. #include "config.h" #endif +#ifdef ERLANG_INTEGRATION +#include "local_config.h" +#endif + +/* Keep the original link size. */ +static int real_link_size = LINK_SIZE; + #include "pcre_internal.h" @@ -63,18 +70,57 @@ Arguments: Returns: 0 if data returned, negative on error */ -PCRE_EXP_DEFN int -erts_pcre_config(int what, void *where) +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_config(int what, void *where) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_config(int what, void *where) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_config(int what, void *where) +#endif { switch (what) { case PCRE_CONFIG_UTF8: -#ifdef SUPPORT_UTF8 +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + *((int *)where) = 0; + return PCRE_ERROR_BADOPTION; +#else +#if defined SUPPORT_UTF + *((int *)where) = 1; +#else + *((int *)where) = 0; +#endif + break; +#endif + + case PCRE_CONFIG_UTF16: +#if defined COMPILE_PCRE8 || defined COMPILE_PCRE32 + *((int *)where) = 0; + return PCRE_ERROR_BADOPTION; +#else +#if defined SUPPORT_UTF *((int *)where) = 1; #else *((int *)where) = 0; #endif break; +#endif + + case PCRE_CONFIG_UTF32: +#if defined COMPILE_PCRE8 || defined COMPILE_PCRE16 + *((int *)where) = 0; + return PCRE_ERROR_BADOPTION; +#else +#if defined SUPPORT_UTF + *((int *)where) = 1; +#else + *((int *)where) = 0; +#endif + break; +#endif case PCRE_CONFIG_UNICODE_PROPERTIES: #ifdef SUPPORT_UCP @@ -84,6 +130,22 @@ switch (what) #endif break; + case PCRE_CONFIG_JIT: +#ifdef SUPPORT_JIT + *((int *)where) = 1; +#else + *((int *)where) = 0; +#endif + break; + + case PCRE_CONFIG_JITTARGET: +#ifdef SUPPORT_JIT + *((const char **)where) = PRIV(jit_get_target)(); +#else + *((const char **)where) = NULL; +#endif + break; + case PCRE_CONFIG_NEWLINE: *((int *)where) = NEWLINE; break; @@ -97,7 +159,7 @@ switch (what) break; case PCRE_CONFIG_LINK_SIZE: - *((int *)where) = LINK_SIZE; + *((int *)where) = real_link_size; break; case PCRE_CONFIG_POSIX_MALLOC_THRESHOLD: @@ -105,11 +167,11 @@ switch (what) break; case PCRE_CONFIG_MATCH_LIMIT: - *((unsigned int *)where) = MATCH_LIMIT; + *((unsigned long int *)where) = MATCH_LIMIT; break; case PCRE_CONFIG_MATCH_LIMIT_RECURSION: - *((unsigned int *)where) = MATCH_LIMIT_RECURSION; + *((unsigned long int *)where) = MATCH_LIMIT_RECURSION; break; case PCRE_CONFIG_STACKRECURSE: diff --git a/erts/emulator/pcre/pcre_dfa_exec.c b/erts/emulator/pcre/pcre_dfa_exec.c index a6e501317f..9e00552163 100644 --- a/erts/emulator/pcre/pcre_dfa_exec.c +++ b/erts/emulator/pcre/pcre_dfa_exec.c @@ -3,10 +3,11 @@ *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax -and semantics are as close as possible to those of the Perl 5 language. +and semantics are as close as possible to those of the Perl 5 language (but see +below for why this module is different). Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2013 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -37,14 +38,41 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ - -/* This module contains the external function erts_pcre_dfa_exec(), which is an +/* This module contains the external function pcre_dfa_exec(), which is an alternative matching function that uses a sort of DFA algorithm (not a true -FSM). This is NOT Perl- compatible, but it has advantages in certain +FSM). This is NOT Perl-compatible, but it has advantages in certain applications. */ /* %ExternalCopyright% */ +/* NOTE ABOUT PERFORMANCE: A user of this function sent some code that improved +the performance of his patterns greatly. I could not use it as it stood, as it +was not thread safe, and made assumptions about pattern sizes. Also, it caused +test 7 to loop, and test 9 to crash with a segfault. + +The issue is the check for duplicate states, which is done by a simple linear +search up the state list. (Grep for "duplicate" below to find the code.) For +many patterns, there will never be many states active at one time, so a simple +linear search is fine. In patterns that have many active states, it might be a +bottleneck. The suggested code used an indexing scheme to remember which states +had previously been used for each character, and avoided the linear search when +it knew there was no chance of a duplicate. This was implemented when adding +states to the state lists. + +I wrote some thread-safe, not-limited code to try something similar at the time +of checking for duplicates (instead of when adding states), using index vectors +on the stack. It did give a 13% improvement with one specially constructed +pattern for certain subject strings, but on other strings and on many of the +simpler patterns in the test suite it did worse. The major problem, I think, +was the extra time to initialize the index. This had to be done for each call +of internal_dfa_exec(). (The supplied patch used a static vector, initialized +only once - I suspect this was the cause of the problems with the tests.) + +Overall, I concluded that the gains in some cases did not outweigh the losses +in others, so I abandoned this code. */ + + + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -61,7 +89,6 @@ applications. */ #define SP " " - /************************************************* * Code parameters and static tables * *************************************************/ @@ -79,35 +106,49 @@ never stored, so we push them well clear of the normal opcodes. */ /* This table identifies those opcodes that are followed immediately by a -character that is to be tested in some way. This makes is possible to +character that is to be tested in some way. This makes it possible to centralize the loading of these characters. In the case of Type * etc, the "character" is the opcode for \D, \d, \S, \s, \W, or \w, which will always be a -small value. ***NOTE*** If the start of this table is modified, the two tables -that follow must also be modified. */ +small value. Non-zero values in the table are the offsets from the opcode where +the character is to be found. ***NOTE*** If the start of this table is +modified, the three tables that follow must also be modified. */ -static uschar coptable[] = { +static const pcre_uint8 coptable[] = { 0, /* End */ 0, 0, 0, 0, 0, /* \A, \G, \K, \B, \b */ 0, 0, 0, 0, 0, 0, /* \D, \d, \S, \s, \W, \w */ - 0, 0, /* Any, Anybyte */ - 0, 0, 0, /* NOTPROP, PROP, EXTUNI */ + 0, 0, 0, /* Any, AllAny, Anybyte */ + 0, 0, /* \P, \p */ 0, 0, 0, 0, 0, /* \R, \H, \h, \V, \v */ - 0, 0, 0, 0, 0, /* \Z, \z, Opt, ^, $ */ + 0, /* \X */ + 0, 0, 0, 0, 0, 0, /* \Z, \z, ^, ^M, $, $M */ 1, /* Char */ - 1, /* Charnc */ + 1, /* Chari */ 1, /* not */ + 1, /* noti */ /* Positive single-char repeats */ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ - 3, 3, 3, /* upto, minupto, exact */ - 1, 1, 1, 3, /* *+, ++, ?+, upto+ */ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* upto, minupto */ + 1+IMM2_SIZE, /* exact */ + 1, 1, 1, 1+IMM2_SIZE, /* *+, ++, ?+, upto+ */ + 1, 1, 1, 1, 1, 1, /* *I, *?I, +I, +?I, ?I, ??I */ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* upto I, minupto I */ + 1+IMM2_SIZE, /* exact I */ + 1, 1, 1, 1+IMM2_SIZE, /* *+I, ++I, ?+I, upto+I */ /* Negative single-char repeats - only for chars < 256 */ 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */ - 3, 3, 3, /* NOT upto, minupto, exact */ - 1, 1, 1, 3, /* NOT *+, ++, ?+, updo+ */ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* NOT upto, minupto */ + 1+IMM2_SIZE, /* NOT exact */ + 1, 1, 1, 1+IMM2_SIZE, /* NOT *+, ++, ?+, upto+ */ + 1, 1, 1, 1, 1, 1, /* NOT *I, *?I, +I, +?I, ?I, ??I */ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* NOT upto I, minupto I */ + 1+IMM2_SIZE, /* NOT exact I */ + 1, 1, 1, 1+IMM2_SIZE, /* NOT *+I, ++I, ?+I, upto+I */ /* Positive type repeats */ 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */ - 3, 3, 3, /* Type upto, minupto, exact */ - 1, 1, 1, 3, /* Type *+, ++, ?+, upto+ */ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* Type upto, minupto */ + 1+IMM2_SIZE, /* Type exact */ + 1, 1, 1, 1+IMM2_SIZE, /* Type *+, ++, ?+, upto+ */ /* Character class & ref repeats */ 0, 0, 0, 0, 0, 0, /* *, *?, +, +?, ?, ?? */ 0, 0, /* CRRANGE, CRMINRANGE */ @@ -115,44 +156,118 @@ static uschar coptable[] = { 0, /* NCLASS */ 0, /* XCLASS - variable length */ 0, /* REF */ + 0, /* REFI */ 0, /* RECURSE */ 0, /* CALLOUT */ 0, /* Alt */ 0, /* Ket */ 0, /* KetRmax */ 0, /* KetRmin */ + 0, /* KetRpos */ + 0, /* Reverse */ 0, /* Assert */ 0, /* Assert not */ 0, /* Assert behind */ 0, /* Assert behind not */ + 0, 0, /* ONCE, ONCE_NC */ + 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */ + 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */ + 0, 0, /* CREF, NCREF */ + 0, 0, /* RREF, NRREF */ + 0, /* DEF */ + 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ + 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */ + 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */ + 0, 0, 0, 0, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ + 0, 0 /* CLOSE, SKIPZERO */ +}; + +/* This table identifies those opcodes that inspect a character. It is used to +remember the fact that a character could have been inspected when the end of +the subject is reached. ***NOTE*** If the start of this table is modified, the +two tables that follow must also be modified. */ + +static const pcre_uint8 poptable[] = { + 0, /* End */ + 0, 0, 0, 1, 1, /* \A, \G, \K, \B, \b */ + 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ + 1, 1, 1, /* Any, AllAny, Anybyte */ + 1, 1, /* \P, \p */ + 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ + 1, /* \X */ + 0, 0, 0, 0, 0, 0, /* \Z, \z, ^, ^M, $, $M */ + 1, /* Char */ + 1, /* Chari */ + 1, /* not */ + 1, /* noti */ + /* Positive single-char repeats */ + 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ + 1, 1, 1, /* upto, minupto, exact */ + 1, 1, 1, 1, /* *+, ++, ?+, upto+ */ + 1, 1, 1, 1, 1, 1, /* *I, *?I, +I, +?I, ?I, ??I */ + 1, 1, 1, /* upto I, minupto I, exact I */ + 1, 1, 1, 1, /* *+I, ++I, ?+I, upto+I */ + /* Negative single-char repeats - only for chars < 256 */ + 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */ + 1, 1, 1, /* NOT upto, minupto, exact */ + 1, 1, 1, 1, /* NOT *+, ++, ?+, upto+ */ + 1, 1, 1, 1, 1, 1, /* NOT *I, *?I, +I, +?I, ?I, ??I */ + 1, 1, 1, /* NOT upto I, minupto I, exact I */ + 1, 1, 1, 1, /* NOT *+I, ++I, ?+I, upto+I */ + /* Positive type repeats */ + 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */ + 1, 1, 1, /* Type upto, minupto, exact */ + 1, 1, 1, 1, /* Type *+, ++, ?+, upto+ */ + /* Character class & ref repeats */ + 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ + 1, 1, /* CRRANGE, CRMINRANGE */ + 1, /* CLASS */ + 1, /* NCLASS */ + 1, /* XCLASS - variable length */ + 0, /* REF */ + 0, /* REFI */ + 0, /* RECURSE */ + 0, /* CALLOUT */ + 0, /* Alt */ + 0, /* Ket */ + 0, /* KetRmax */ + 0, /* KetRmin */ + 0, /* KetRpos */ 0, /* Reverse */ - 0, 0, 0, 0, /* ONCE, BRA, CBRA, COND */ - 0, 0, 0, /* SBRA, SCBRA, SCOND */ - 0, /* CREF */ - 0, /* RREF */ + 0, /* Assert */ + 0, /* Assert not */ + 0, /* Assert behind */ + 0, /* Assert behind not */ + 0, 0, /* ONCE, ONCE_NC */ + 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */ + 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */ + 0, 0, /* CREF, NCREF */ + 0, 0, /* RREF, NRREF */ 0, /* DEF */ - 0, 0, /* BRAZERO, BRAMINZERO */ - 0, 0, 0, 0, /* PRUNE, SKIP, THEN, COMMIT */ - 0, 0 /* FAIL, ACCEPT */ + 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ + 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */ + 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */ + 0, 0, 0, 0, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ + 0, 0 /* CLOSE, SKIPZERO */ }; /* These 2 tables allow for compact code for testing for \D, \d, \S, \s, \W, and \w */ -static uschar toptable1[] = { +static const pcre_uint8 toptable1[] = { 0, 0, 0, 0, 0, 0, ctype_digit, ctype_digit, ctype_space, ctype_space, ctype_word, ctype_word, - 0 /* OP_ANY */ + 0, 0 /* OP_ANY, OP_ALLANY */ }; -static uschar toptable2[] = { +static const pcre_uint8 toptable2[] = { 0, 0, 0, 0, 0, 0, ctype_digit, 0, ctype_space, 0, ctype_word, 0, - 1 /* OP_ANY */ + 1, 1 /* OP_ANY, OP_ALLANY */ }; @@ -164,14 +279,13 @@ these structures in, is a vector of ints. */ typedef struct stateblock { int offset; /* Offset to opcode */ int count; /* Count for repeats */ - int ims; /* ims flag bits */ int data; /* Some use extra data */ } stateblock; -#define INTS_PER_STATEBLOCK (sizeof(stateblock)/sizeof(int)) +#define INTS_PER_STATEBLOCK (int)(sizeof(stateblock)/sizeof(int)) -#ifdef DEBUG +#ifdef PCRE_DEBUG /************************************************* * Print character string * *************************************************/ @@ -187,15 +301,15 @@ Returns: nothing */ static void -pchars(unsigned char *p, int length, FILE *f) +pchars(const pcre_uchar *p, int length, FILE *f) { -int c; +pcre_uint32 c; while (length-- > 0) { if (isprint(c = *(p++))) fprintf(f, "%c", c); else - fprintf(f, "\\x%02x", c); + fprintf(f, "\\x{%02x}", c); } } #endif @@ -220,12 +334,10 @@ Arguments: offsetcount size of same workspace vector of workspace wscount size of same - ims the current ims flags rlevel function call recursion level - recursing regex recursive call level -Returns: > 0 => - = 0 => +Returns: > 0 => number of match offset pairs placed in offsets + = 0 => offsets overflowed; longest matches are present -1 => failed to match < -1 => some kind of unexpected problem @@ -237,7 +349,6 @@ for the current character, one for the following character). */ { \ next_active_state->offset = (x); \ next_active_state->count = (y); \ - next_active_state->ims = ims; \ next_active_state++; \ DPRINTF(("%.*sADD_ACTIVE(%d,%d)\n", rlevel*2-2, SP, (x), (y))); \ } \ @@ -248,7 +359,6 @@ for the current character, one for the following character). */ { \ next_active_state->offset = (x); \ next_active_state->count = (y); \ - next_active_state->ims = ims; \ next_active_state->data = (z); \ next_active_state++; \ DPRINTF(("%.*sADD_ACTIVE_DATA(%d,%d,%d)\n", rlevel*2-2, SP, (x), (y), (z))); \ @@ -260,7 +370,6 @@ for the current character, one for the following character). */ { \ next_new_state->offset = (x); \ next_new_state->count = (y); \ - next_new_state->ims = ims; \ next_new_state++; \ DPRINTF(("%.*sADD_NEW(%d,%d)\n", rlevel*2-2, SP, (x), (y))); \ } \ @@ -271,10 +380,10 @@ for the current character, one for the following character). */ { \ next_new_state->offset = (x); \ next_new_state->count = (y); \ - next_new_state->ims = ims; \ next_new_state->data = (z); \ next_new_state++; \ - DPRINTF(("%.*sADD_NEW_DATA(%d,%d,%d)\n", rlevel*2-2, SP, (x), (y), (z))); \ + DPRINTF(("%.*sADD_NEW_DATA(%d,%d,%d) line %d\n", rlevel*2-2, SP, \ + (x), (y), (z), __LINE__)); \ } \ else return PCRE_ERROR_DFA_WSSIZE @@ -283,39 +392,41 @@ for the current character, one for the following character). */ static int internal_dfa_exec( dfa_match_data *md, - const uschar *this_start_code, - const uschar *current_subject, + const pcre_uchar *this_start_code, + const pcre_uchar *current_subject, int start_offset, int *offsets, int offsetcount, int *workspace, int wscount, - int ims, - int rlevel, - int recursing) + int rlevel) { stateblock *active_states, *new_states, *temp_states; stateblock *next_active_state, *next_new_state; -const uschar *ctypes, *lcc, *fcc; -const uschar *ptr; -const uschar *end_code, *first_op; +const pcre_uint8 *ctypes, *lcc, *fcc; +const pcre_uchar *ptr; +const pcre_uchar *end_code, *first_op; + +dfa_recursion_info new_recursive; int active_count, new_count, match_count; /* Some fields in the md block are frequently referenced, so we load them into independent variables in the hope that this will perform better. */ -const uschar *start_subject = md->start_subject; -const uschar *end_subject = md->end_subject; -const uschar *start_code = md->start_code; +const pcre_uchar *start_subject = md->start_subject; +const pcre_uchar *end_subject = md->end_subject; +const pcre_uchar *start_code = md->start_code; -#ifdef SUPPORT_UTF8 -BOOL utf8 = (md->poptions & PCRE_UTF8) != 0; +#ifdef SUPPORT_UTF +BOOL utf = (md->poptions & PCRE_UTF8) != 0; #else -BOOL utf8 = FALSE; +BOOL utf = FALSE; #endif +BOOL reset_could_continue = FALSE; + rlevel++; offsetcount &= (-2); @@ -324,8 +435,8 @@ wscount = (wscount - (wscount % (INTS_PER_STATEBLOCK * 2))) / (2 * INTS_PER_STATEBLOCK); DPRINTF(("\n%.*s---------------------\n" - "%.*sCall to internal_dfa_exec f=%d r=%d\n", - rlevel*2-2, SP, rlevel*2-2, SP, rlevel, recursing)); + "%.*sCall to internal_dfa_exec f=%d\n", + rlevel*2-2, SP, rlevel*2-2, SP, rlevel)); ctypes = md->tables + ctypes_offset; lcc = md->tables + lcc_offset; @@ -338,7 +449,9 @@ next_new_state = new_states = active_states + wscount; new_count = 0; first_op = this_start_code + 1 + LINK_SIZE + - ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA)? 2:0); + ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA || + *this_start_code == OP_CBRAPOS || *this_start_code == OP_SCBRAPOS) + ? IMM2_SIZE:0); /* The first thing in any (sub) pattern is a bracket of some sort. Push all the alternative states onto the list, and find out where the end is. This @@ -366,18 +479,16 @@ if (*first_op == OP_REVERSE) /* If we can't go back the amount required for the longest lookbehind pattern, go back as far as we can; some alternatives may still be viable. */ -#ifdef SUPPORT_UTF8 +#ifdef SUPPORT_UTF /* In character mode we have to step back character by character */ - if (utf8) + if (utf) { for (gone_back = 0; gone_back < max_back; gone_back++) { if (current_subject <= start_subject) break; current_subject--; - while (current_subject > start_subject && - (*current_subject & 0xc0) == 0x80) - current_subject--; + ACROSSCHAR(current_subject > start_subject, *current_subject, current_subject--); } } else @@ -387,10 +498,15 @@ if (*first_op == OP_REVERSE) { gone_back = (current_subject - max_back < start_subject)? - current_subject - start_subject : max_back; + (int)(current_subject - start_subject) : max_back; current_subject -= gone_back; } + /* Save the earliest consulted character */ + + if (current_subject < md->start_used_ptr) + md->start_used_ptr = current_subject; + /* Now we can process the individual branches. */ end_code = this_start_code; @@ -399,7 +515,7 @@ if (*first_op == OP_REVERSE) int back = GET(end_code, 2+LINK_SIZE); if (back <= gone_back) { - int bstate = end_code - start_code + 2 + 2*LINK_SIZE; + int bstate = (int)(end_code - start_code + 2 + 2*LINK_SIZE); ADD_NEW_DATA(-bstate, 0, gone_back - back); } end_code += GET(end_code, 1); @@ -432,10 +548,12 @@ else else { int length = 1 + LINK_SIZE + - ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA)? 2:0); + ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA || + *this_start_code == OP_CBRAPOS || *this_start_code == OP_SCBRAPOS) + ? IMM2_SIZE:0); do { - ADD_NEW(end_code - start_code + length, 0); + ADD_NEW((int)(end_code - start_code + length), 0); end_code += GET(end_code, 1); length = 1 + LINK_SIZE; } @@ -445,7 +563,7 @@ else workspace[0] = 0; /* Bit indicating which vector is current */ -DPRINTF(("%.*sEnd state = %d\n", rlevel*2-2, SP, end_code - start_code)); +DPRINTF(("%.*sEnd state = %d\n", rlevel*2-2, SP, (int)(end_code - start_code))); /* Loop for scanning the subject */ @@ -454,7 +572,11 @@ for (;;) { int i, j; int clen, dlen; - unsigned int c, d; + pcre_uint32 c, d; + int forced_fail = 0; + BOOL partial_newline = FALSE; + BOOL could_continue = reset_could_continue; + reset_could_continue = FALSE; /* Make the new state list into the active state list and empty the new state list. */ @@ -468,9 +590,9 @@ for (;;) workspace[0] ^= 1; /* Remember for the restarting feature */ workspace[1] = active_count; -#ifdef DEBUG +#ifdef PCRE_DEBUG printf("%.*sNext character: rest of subject = \"", rlevel*2-2, SP); - pchars((uschar *)ptr, strlen((char *)ptr), stdout); + pchars(ptr, STRLEN_UC(ptr), stdout); printf("\"\n"); printf("%.*sActive states: ", rlevel*2-2, SP); @@ -490,11 +612,12 @@ for (;;) if (ptr < end_subject) { - clen = 1; /* Number of bytes in the character */ -#ifdef SUPPORT_UTF8 - if (utf8) { GETCHARLEN(c, ptr, clen); } else -#endif /* SUPPORT_UTF8 */ + clen = 1; /* Number of data items in the character */ +#ifdef SUPPORT_UTF + GETCHARLENTEST(c, ptr, clen); +#else c = *ptr; +#endif /* SUPPORT_UTF */ } else { @@ -510,27 +633,23 @@ for (;;) for (i = 0; i < active_count; i++) { stateblock *current_state = active_states + i; - const uschar *code; + BOOL caseless = FALSE; + const pcre_uchar *code; int state_offset = current_state->offset; - int count, codevalue; -#ifdef SUPPORT_UCP - int chartype, script; -#endif + int codevalue, rrc; + int count; -#ifdef DEBUG +#ifdef PCRE_DEBUG printf ("%.*sProcessing state %d c=", rlevel*2-2, SP, state_offset); if (clen == 0) printf("EOL\n"); else if (c > 32 && c < 127) printf("'%c'\n", c); else printf("0x%02x\n", c); #endif - /* This variable is referred to implicity in the ADD_xxx macros. */ - - ims = current_state->ims; - /* A negative offset is a special case meaning "hold off going to this (negated) state until the number of characters in the data field have - been skipped". */ + been skipped". If the could_continue flag was passed over from a previous + state, arrange for it to passed on. */ if (state_offset < 0) { @@ -539,6 +658,7 @@ for (;;) DPRINTF(("%.*sSkipping this character\n", rlevel*2-2, SP)); ADD_NEW_DATA(state_offset, current_state->count, current_state->data - 1); + if (could_continue) reset_could_continue = TRUE; continue; } else @@ -547,7 +667,9 @@ for (;;) } } - /* Check for a duplicate state with the same count, and skip if found. */ + /* Check for a duplicate state with the same count, and skip if found. + See the note at the head of this module about the possibility of improving + performance here. */ for (j = 0; j < i; j++) { @@ -564,23 +686,29 @@ for (;;) code = start_code + state_offset; codevalue = *code; + /* If this opcode inspects a character, but we are at the end of the + subject, remember the fact for use when testing for a partial match. */ + + if (clen == 0 && poptable[codevalue] != 0) + could_continue = TRUE; + /* If this opcode is followed by an inline character, load it. It is tempting to test for the presence of a subject character here, but that is wrong, because sometimes zero repetitions of the subject are permitted. We also use this mechanism for opcodes such as OP_TYPEPLUS that take an - argument that is not a data character - but is always one byte long. We - have to take special action to deal with \P, \p, \H, \h, \V, \v and \X in - this case. To keep the other cases fast, convert these ones to new opcodes. - */ + argument that is not a data character - but is always one byte long because + the values are small. We have to take special action to deal with \P, \p, + \H, \h, \V, \v and \X in this case. To keep the other cases fast, convert + these ones to new opcodes. */ if (coptable[codevalue] > 0) { dlen = 1; -#ifdef SUPPORT_UTF8 - if (utf8) { GETCHARLEN(d, (code + coptable[codevalue]), dlen); } else -#endif /* SUPPORT_UTF8 */ +#ifdef SUPPORT_UTF + if (utf) { GETCHARLEN(d, (code + coptable[codevalue]), dlen); } else +#endif /* SUPPORT_UTF */ d = code[coptable[codevalue]]; if (codevalue >= OP_TYPESTAR) { @@ -610,16 +738,35 @@ for (;;) switch (codevalue) { +/* ========================================================================== */ + /* These cases are never obeyed. This is a fudge that causes a compile- + time error if the vectors coptable or poptable, which are indexed by + opcode, are not the correct length. It seems to be the only way to do + such a check at compile time, as the sizeof() operator does not work + in the C preprocessor. */ + + case OP_TABLE_LENGTH: + case OP_TABLE_LENGTH + + ((sizeof(coptable) == OP_TABLE_LENGTH) && + (sizeof(poptable) == OP_TABLE_LENGTH)): + break; /* ========================================================================== */ /* Reached a closing bracket. If not at the end of the pattern, carry - on with the next opcode. Otherwise, unless we have an empty string and - PCRE_NOTEMPTY is set, save the match data, shifting up all previous + on with the next opcode. For repeating opcodes, also add the repeat + state. Note that KETRPOS will always be encountered at the end of the + subpattern, because the possessive subpattern repeats are always handled + using recursive calls. Thus, it never adds any new states. + + At the end of the (sub)pattern, unless we have an empty string and + PCRE_NOTEMPTY is set, or PCRE_NOTEMPTY_ATSTART is set and we are at the + start of the subject, save the match data, shifting up all previous matches so we always have the longest first. */ case OP_KET: case OP_KETRMIN: case OP_KETRMAX: + case OP_KETRPOS: if (code != end_code) { ADD_ACTIVE(state_offset + 1 + LINK_SIZE, 0); @@ -628,26 +775,32 @@ for (;;) ADD_ACTIVE(state_offset - GET(code, 1), 0); } } - else if (ptr > current_subject || (md->moptions & PCRE_NOTEMPTY) == 0) + else { - if (match_count < 0) match_count = (offsetcount >= 2)? 1 : 0; - else if (match_count > 0 && ++match_count * 2 >= offsetcount) - match_count = 0; - count = ((match_count == 0)? offsetcount : match_count * 2) - 2; - if (count > 0) memmove(offsets + 2, offsets, count * sizeof(int)); - if (offsetcount >= 2) - { - offsets[0] = current_subject - start_subject; - offsets[1] = ptr - start_subject; - DPRINTF(("%.*sSet matched string = \"%.*s\"\n", rlevel*2-2, SP, - offsets[1] - offsets[0], current_subject)); - } - if ((md->moptions & PCRE_DFA_SHORTEST) != 0) + if (ptr > current_subject || + ((md->moptions & PCRE_NOTEMPTY) == 0 && + ((md->moptions & PCRE_NOTEMPTY_ATSTART) == 0 || + current_subject > start_subject + md->start_offset))) { - DPRINTF(("%.*sEnd of internal_dfa_exec %d: returning %d\n" - "%.*s---------------------\n\n", rlevel*2-2, SP, rlevel, - match_count, rlevel*2-2, SP)); - return match_count; + if (match_count < 0) match_count = (offsetcount >= 2)? 1 : 0; + else if (match_count > 0 && ++match_count * 2 > offsetcount) + match_count = 0; + count = ((match_count == 0)? offsetcount : match_count * 2) - 2; + if (count > 0) memmove(offsets + 2, offsets, count * sizeof(int)); + if (offsetcount >= 2) + { + offsets[0] = (int)(current_subject - start_subject); + offsets[1] = (int)(ptr - start_subject); + DPRINTF(("%.*sSet matched string = \"%.*s\"\n", rlevel*2-2, SP, + offsets[1] - offsets[0], (char *)current_subject)); + } + if ((md->moptions & PCRE_DFA_SHORTEST) != 0) + { + DPRINTF(("%.*sEnd of internal_dfa_exec %d: returning %d\n" + "%.*s---------------------\n\n", rlevel*2-2, SP, rlevel, + match_count, rlevel*2-2, SP)); + return match_count; + } } } break; @@ -659,7 +812,7 @@ for (;;) /*-----------------------------------------------------------------*/ case OP_ALT: do { code += GET(code, 1); } while (*code == OP_ALT); - ADD_ACTIVE(code - start_code, 0); + ADD_ACTIVE((int)(code - start_code), 0); break; /*-----------------------------------------------------------------*/ @@ -667,7 +820,7 @@ for (;;) case OP_SBRA: do { - ADD_ACTIVE(code - start_code + 1 + LINK_SIZE, 0); + ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); code += GET(code, 1); } while (*code == OP_ALT); @@ -676,11 +829,11 @@ for (;;) /*-----------------------------------------------------------------*/ case OP_CBRA: case OP_SCBRA: - ADD_ACTIVE(code - start_code + 3 + LINK_SIZE, 0); + ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE + IMM2_SIZE), 0); code += GET(code, 1); while (*code == OP_ALT) { - ADD_ACTIVE(code - start_code + 1 + LINK_SIZE, 0); + ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); code += GET(code, 1); } break; @@ -691,27 +844,37 @@ for (;;) ADD_ACTIVE(state_offset + 1, 0); code += 1 + GET(code, 2); while (*code == OP_ALT) code += GET(code, 1); - ADD_ACTIVE(code - start_code + 1 + LINK_SIZE, 0); + ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); + break; + + /*-----------------------------------------------------------------*/ + case OP_SKIPZERO: + code += 1 + GET(code, 2); + while (*code == OP_ALT) code += GET(code, 1); + ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); break; /*-----------------------------------------------------------------*/ case OP_CIRC: - if ((ptr == start_subject && (md->moptions & PCRE_NOTBOL) == 0) || - ((ims & PCRE_MULTILINE) != 0 && - ptr != end_subject && - WAS_NEWLINE(ptr))) + if (ptr == start_subject && (md->moptions & PCRE_NOTBOL) == 0) { ADD_ACTIVE(state_offset + 1, 0); } break; /*-----------------------------------------------------------------*/ - case OP_EOD: - if (ptr >= end_subject) { ADD_ACTIVE(state_offset + 1, 0); } + case OP_CIRCM: + if ((ptr == start_subject && (md->moptions & PCRE_NOTBOL) == 0) || + (ptr != end_subject && WAS_NEWLINE(ptr))) + { ADD_ACTIVE(state_offset + 1, 0); } break; /*-----------------------------------------------------------------*/ - case OP_OPT: - ims = code[1]; - ADD_ACTIVE(state_offset + 2, 0); + case OP_EOD: + if (ptr >= end_subject) + { + if ((md->moptions & PCRE_PARTIAL_HARD) != 0) + could_continue = TRUE; + else { ADD_ACTIVE(state_offset + 1, 0); } + } break; /*-----------------------------------------------------------------*/ @@ -733,13 +896,34 @@ for (;;) /*-----------------------------------------------------------------*/ case OP_ANY: - if (clen > 0 && ((ims & PCRE_DOTALL) != 0 || !IS_NEWLINE(ptr))) + if (clen > 0 && !IS_NEWLINE(ptr)) + { + if (ptr + 1 >= md->end_subject && + (md->moptions & (PCRE_PARTIAL_HARD)) != 0 && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + c == NLBLOCK->nl[0]) + { + could_continue = partial_newline = TRUE; + } + else + { + ADD_NEW(state_offset + 1, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_ALLANY: + if (clen > 0) { ADD_NEW(state_offset + 1, 0); } break; /*-----------------------------------------------------------------*/ case OP_EODN: - if (clen == 0 || (IS_NEWLINE(ptr) && ptr == end_subject - md->nllen)) + if (clen == 0 && (md->moptions & PCRE_PARTIAL_HARD) != 0) + could_continue = TRUE; + else if (clen == 0 || (IS_NEWLINE(ptr) && ptr == end_subject - md->nllen)) { ADD_ACTIVE(state_offset + 1, 0); } break; @@ -747,13 +931,53 @@ for (;;) case OP_DOLL: if ((md->moptions & PCRE_NOTEOL) == 0) { - if (clen == 0 || - (IS_NEWLINE(ptr) && - ((ims & PCRE_MULTILINE) != 0 || ptr == end_subject - md->nllen) + if (clen == 0 && (md->moptions & PCRE_PARTIAL_HARD) != 0) + could_continue = TRUE; + else if (clen == 0 || + ((md->poptions & PCRE_DOLLAR_ENDONLY) == 0 && IS_NEWLINE(ptr) && + (ptr == end_subject - md->nllen) )) { ADD_ACTIVE(state_offset + 1, 0); } + else if (ptr + 1 >= md->end_subject && + (md->moptions & (PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT)) != 0 && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + c == NLBLOCK->nl[0]) + { + if ((md->moptions & PCRE_PARTIAL_HARD) != 0) + { + reset_could_continue = TRUE; + ADD_NEW_DATA(-(state_offset + 1), 0, 1); + } + else could_continue = partial_newline = TRUE; + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_DOLLM: + if ((md->moptions & PCRE_NOTEOL) == 0) + { + if (clen == 0 && (md->moptions & PCRE_PARTIAL_HARD) != 0) + could_continue = TRUE; + else if (clen == 0 || + ((md->poptions & PCRE_DOLLAR_ENDONLY) == 0 && IS_NEWLINE(ptr))) + { ADD_ACTIVE(state_offset + 1, 0); } + else if (ptr + 1 >= md->end_subject && + (md->moptions & (PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT)) != 0 && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + c == NLBLOCK->nl[0]) + { + if ((md->moptions & PCRE_PARTIAL_HARD) != 0) + { + reset_could_continue = TRUE; + ADD_NEW_DATA(-(state_offset + 1), 0, 1); + } + else could_continue = partial_newline = TRUE; + } } - else if ((ims & PCRE_MULTILINE) != 0 && IS_NEWLINE(ptr)) + else if (IS_NEWLINE(ptr)) { ADD_ACTIVE(state_offset + 1, 0); } break; @@ -784,17 +1008,43 @@ for (;;) if (ptr > start_subject) { - const uschar *temp = ptr - 1; -#ifdef SUPPORT_UTF8 - if (utf8) BACKCHAR(temp); + const pcre_uchar *temp = ptr - 1; + if (temp < md->start_used_ptr) md->start_used_ptr = temp; +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf) { BACKCHAR(temp); } #endif GETCHARTEST(d, temp); +#ifdef SUPPORT_UCP + if ((md->poptions & PCRE_UCP) != 0) + { + if (d == '_') left_word = TRUE; else + { + int cat = UCD_CATEGORY(d); + left_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif left_word = d < 256 && (ctypes[d] & ctype_word) != 0; } - else left_word = 0; + else left_word = FALSE; - if (clen > 0) right_word = c < 256 && (ctypes[c] & ctype_word) != 0; - else right_word = 0; + if (clen > 0) + { +#ifdef SUPPORT_UCP + if ((md->poptions & PCRE_UCP) != 0) + { + if (c == '_') right_word = TRUE; else + { + int cat = UCD_CATEGORY(c); + right_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif + right_word = c < 256 && (ctypes[c] & ctype_word) != 0; + } + else right_word = FALSE; if ((left_word == right_word) == (codevalue == OP_NOT_WORD_BOUNDARY)) { ADD_ACTIVE(state_offset + 1, 0); } @@ -813,7 +1063,8 @@ for (;;) if (clen > 0) { BOOL OK; - int category = _erts_pcre_ucp_findprop(c, &chartype, &script); + const pcre_uint32 *cp; + const ucd_record * prop = GET_UCD(c); switch(code[1]) { case PT_ANY: @@ -821,19 +1072,59 @@ for (;;) break; case PT_LAMP: - OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt; + OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt; break; case PT_GC: - OK = category == code[2]; + OK = PRIV(ucp_gentype)[prop->chartype] == code[2]; break; case PT_PC: - OK = chartype == code[2]; + OK = prop->chartype == code[2]; break; case PT_SC: - OK = script == code[2]; + OK = prop->script == code[2]; + break; + + /* These are specials for combination cases. */ + + case PT_ALNUM: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N; + break; + + case PT_SPACE: /* Perl space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR; + break; + + case PT_PXSPACE: /* POSIX space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR; + break; + + case PT_WORD: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE; + break; + + case PT_CLIST: + cp = PRIV(ucd_caseless_sets) + code[2]; + for (;;) + { + if (c < *cp) { OK = FALSE; break; } + if (c == *cp++) { OK = TRUE; break; } + } + break; + + case PT_UCNC: + OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || + c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || + c >= 0xe000; break; /* Should never occur, but keep compilers from grumbling. */ @@ -853,8 +1144,8 @@ for (;;) /* ========================================================================== */ /* These opcodes likewise inspect the subject character, but have an argument that is not a data character. It is one of these opcodes: - OP_ANY, OP_DIGIT, OP_NOT_DIGIT, OP_WHITESPACE, OP_NOT_SPACE, OP_WORDCHAR, - OP_NOT_WORDCHAR. The value is loaded into d. */ + OP_ANY, OP_ALLANY, OP_DIGIT, OP_NOT_DIGIT, OP_WHITESPACE, OP_NOT_SPACE, + OP_WORDCHAR, OP_NOT_WORDCHAR. The value is loaded into d. */ case OP_TYPEPLUS: case OP_TYPEMINPLUS: @@ -863,12 +1154,17 @@ for (;;) if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } if (clen > 0) { - if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + if (d == OP_ANY && ptr + 1 >= md->end_subject && + (md->moptions & (PCRE_PARTIAL_HARD)) != 0 && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + c == NLBLOCK->nl[0]) + { + could_continue = partial_newline = TRUE; + } + else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || (c < 256 && - (d != OP_ANY || - (ims & PCRE_DOTALL) != 0 || - !IS_NEWLINE(ptr) - ) && + (d != OP_ANY || !IS_NEWLINE(ptr)) && ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) { if (count > 0 && codevalue == OP_TYPEPOSPLUS) @@ -889,12 +1185,17 @@ for (;;) ADD_ACTIVE(state_offset + 2, 0); if (clen > 0) { - if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + if (d == OP_ANY && ptr + 1 >= md->end_subject && + (md->moptions & (PCRE_PARTIAL_HARD)) != 0 && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + c == NLBLOCK->nl[0]) + { + could_continue = partial_newline = TRUE; + } + else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || (c < 256 && - (d != OP_ANY || - (ims & PCRE_DOTALL) != 0 || - !IS_NEWLINE(ptr) - ) && + (d != OP_ANY || !IS_NEWLINE(ptr)) && ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) { if (codevalue == OP_TYPEPOSQUERY) @@ -914,12 +1215,17 @@ for (;;) ADD_ACTIVE(state_offset + 2, 0); if (clen > 0) { - if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + if (d == OP_ANY && ptr + 1 >= md->end_subject && + (md->moptions & (PCRE_PARTIAL_HARD)) != 0 && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + c == NLBLOCK->nl[0]) + { + could_continue = partial_newline = TRUE; + } + else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || (c < 256 && - (d != OP_ANY || - (ims & PCRE_DOTALL) != 0 || - !IS_NEWLINE(ptr) - ) && + (d != OP_ANY || !IS_NEWLINE(ptr)) && ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) { if (codevalue == OP_TYPEPOSSTAR) @@ -937,16 +1243,21 @@ for (;;) count = current_state->count; /* Number already matched */ if (clen > 0) { - if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + if (d == OP_ANY && ptr + 1 >= md->end_subject && + (md->moptions & (PCRE_PARTIAL_HARD)) != 0 && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + c == NLBLOCK->nl[0]) + { + could_continue = partial_newline = TRUE; + } + else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || (c < 256 && - (d != OP_ANY || - (ims & PCRE_DOTALL) != 0 || - !IS_NEWLINE(ptr) - ) && + (d != OP_ANY || !IS_NEWLINE(ptr)) && ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) { - if (++count >= GET2(code, 1)) - { ADD_NEW(state_offset + 4, 0); } + if (++count >= (int)GET2(code, 1)) + { ADD_NEW(state_offset + 1 + IMM2_SIZE + 1, 0); } else { ADD_NEW(state_offset, count); } } @@ -957,16 +1268,21 @@ for (;;) case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEPOSUPTO: - ADD_ACTIVE(state_offset + 4, 0); + ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); count = current_state->count; /* Number already matched */ if (clen > 0) { - if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + if (d == OP_ANY && ptr + 1 >= md->end_subject && + (md->moptions & (PCRE_PARTIAL_HARD)) != 0 && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + c == NLBLOCK->nl[0]) + { + could_continue = partial_newline = TRUE; + } + else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || (c < 256 && - (d != OP_ANY || - (ims & PCRE_DOTALL) != 0 || - !IS_NEWLINE(ptr) - ) && + (d != OP_ANY || !IS_NEWLINE(ptr)) && ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) { if (codevalue == OP_TYPEPOSUPTO) @@ -974,8 +1290,8 @@ for (;;) active_count--; /* Remove non-match possibility */ next_active_state--; } - if (++count >= GET2(code, 1)) - { ADD_NEW(state_offset + 4, 0); } + if (++count >= (int)GET2(code, 1)) + { ADD_NEW(state_offset + 2 + IMM2_SIZE, 0); } else { ADD_NEW(state_offset, count); } } @@ -997,7 +1313,8 @@ for (;;) if (clen > 0) { BOOL OK; - int category = _erts_pcre_ucp_findprop(c, &chartype, &script); + const pcre_uint32 *cp; + const ucd_record * prop = GET_UCD(c); switch(code[2]) { case PT_ANY: @@ -1005,19 +1322,59 @@ for (;;) break; case PT_LAMP: - OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt; + OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt; break; case PT_GC: - OK = category == code[3]; + OK = PRIV(ucp_gentype)[prop->chartype] == code[3]; break; case PT_PC: - OK = chartype == code[3]; + OK = prop->chartype == code[3]; break; case PT_SC: - OK = script == code[3]; + OK = prop->script == code[3]; + break; + + /* These are specials for combination cases. */ + + case PT_ALNUM: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N; + break; + + case PT_SPACE: /* Perl space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR; + break; + + case PT_PXSPACE: /* POSIX space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR; + break; + + case PT_WORD: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE; + break; + + case PT_CLIST: + cp = PRIV(ucd_caseless_sets) + code[3]; + for (;;) + { + if (c < *cp) { OK = FALSE; break; } + if (c == *cp++) { OK = TRUE; break; } + } + break; + + case PT_UCNC: + OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || + c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || + c >= 0xe000; break; /* Should never occur, but keep compilers from grumbling. */ @@ -1046,23 +1403,26 @@ for (;;) case OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS: count = current_state->count; /* Already matched */ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } - if (clen > 0 && _erts_pcre_ucp_findprop(c, &chartype, &script) != ucp_M) + if (clen > 0) { - const uschar *nptr = ptr + clen; + int lgb, rgb; + const pcre_uchar *nptr = ptr + clen; int ncount = 0; if (count > 0 && codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS) { active_count--; /* Remove non-match possibility */ next_active_state--; } + lgb = UCD_GRAPHBREAK(c); while (nptr < end_subject) { - int nd; - int ndlen = 1; - GETCHARLEN(nd, nptr, ndlen); - if (_erts_pcre_ucp_findprop(nd, &chartype, &script) != ucp_M) break; + dlen = 1; + if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } + rgb = UCD_GRAPHBREAK(d); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; ncount++; - nptr += ndlen; + lgb = rgb; + nptr += dlen; } count++; ADD_NEW_DATA(-state_offset, count, ncount); @@ -1081,20 +1441,22 @@ for (;;) int ncount = 0; switch (c) { - case 0x000b: - case 0x000c: - case 0x0085: + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#ifndef EBCDIC case 0x2028: case 0x2029: +#endif /* Not EBCDIC */ if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; goto ANYNL01; - case 0x000d: - if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1; + case CHAR_CR: + if (ptr + 1 < end_subject && RAWUCHARTEST(ptr + 1) == CHAR_LF) ncount = 1; /* Fall through */ ANYNL01: - case 0x000a: + case CHAR_LF: if (count > 0 && codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSPLUS) { active_count--; /* Remove non-match possibility */ @@ -1121,13 +1483,7 @@ for (;;) BOOL OK; switch (c) { - case 0x000a: - case 0x000b: - case 0x000c: - case 0x000d: - case 0x0085: - case 0x2028: - case 0x2029: + VSPACE_CASES: OK = TRUE; break; @@ -1160,25 +1516,7 @@ for (;;) BOOL OK; switch (c) { - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ + HSPACE_CASES: OK = TRUE; break; @@ -1219,7 +1557,8 @@ for (;;) if (clen > 0) { BOOL OK; - int category = _erts_pcre_ucp_findprop(c, &chartype, &script); + const pcre_uint32 *cp; + const ucd_record * prop = GET_UCD(c); switch(code[2]) { case PT_ANY: @@ -1227,19 +1566,59 @@ for (;;) break; case PT_LAMP: - OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt; + OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt; break; case PT_GC: - OK = category == code[3]; + OK = PRIV(ucp_gentype)[prop->chartype] == code[3]; break; case PT_PC: - OK = chartype == code[3]; + OK = prop->chartype == code[3]; break; case PT_SC: - OK = script == code[3]; + OK = prop->script == code[3]; + break; + + /* These are specials for combination cases. */ + + case PT_ALNUM: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N; + break; + + case PT_SPACE: /* Perl space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR; + break; + + case PT_PXSPACE: /* POSIX space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR; + break; + + case PT_WORD: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE; + break; + + case PT_CLIST: + cp = PRIV(ucd_caseless_sets) + code[3]; + for (;;) + { + if (c < *cp) { OK = FALSE; break; } + if (c == *cp++) { OK = TRUE; break; } + } + break; + + case PT_UCNC: + OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || + c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || + c >= 0xe000; break; /* Should never occur, but keep compilers from grumbling. */ @@ -1277,9 +1656,10 @@ for (;;) QS2: ADD_ACTIVE(state_offset + 2, 0); - if (clen > 0 && _erts_pcre_ucp_findprop(c, &chartype, &script) != ucp_M) + if (clen > 0) { - const uschar *nptr = ptr + clen; + int lgb, rgb; + const pcre_uchar *nptr = ptr + clen; int ncount = 0; if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR || codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY) @@ -1287,14 +1667,16 @@ for (;;) active_count--; /* Remove non-match possibility */ next_active_state--; } + lgb = UCD_GRAPHBREAK(c); while (nptr < end_subject) { - int nd; - int ndlen = 1; - GETCHARLEN(nd, nptr, ndlen); - if (_erts_pcre_ucp_findprop(nd, &chartype, &script) != ucp_M) break; + dlen = 1; + if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } + rgb = UCD_GRAPHBREAK(d); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; ncount++; - nptr += ndlen; + lgb = rgb; + nptr += dlen; } ADD_NEW_DATA(-(state_offset + count), 0, ncount); } @@ -1320,27 +1702,29 @@ for (;;) int ncount = 0; switch (c) { - case 0x000b: - case 0x000c: - case 0x0085: + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#ifndef EBCDIC case 0x2028: case 0x2029: +#endif /* Not EBCDIC */ if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; goto ANYNL02; - case 0x000d: - if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1; + case CHAR_CR: + if (ptr + 1 < end_subject && RAWUCHARTEST(ptr + 1) == CHAR_LF) ncount = 1; /* Fall through */ ANYNL02: - case 0x000a: + case CHAR_LF: if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSSTAR || codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSQUERY) { active_count--; /* Remove non-match possibility */ next_active_state--; } - ADD_NEW_DATA(-(state_offset + count), 0, ncount); + ADD_NEW_DATA(-(state_offset + (int)count), 0, ncount); break; default: @@ -1368,13 +1752,7 @@ for (;;) BOOL OK; switch (c) { - case 0x000a: - case 0x000b: - case 0x000c: - case 0x000d: - case 0x0085: - case 0x2028: - case 0x2029: + VSPACE_CASES: OK = TRUE; break; @@ -1390,7 +1768,7 @@ for (;;) active_count--; /* Remove non-match possibility */ next_active_state--; } - ADD_NEW_DATA(-(state_offset + count), 0, 0); + ADD_NEW_DATA(-(state_offset + (int)count), 0, 0); } } break; @@ -1414,25 +1792,7 @@ for (;;) BOOL OK; switch (c) { - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ + HSPACE_CASES: OK = TRUE; break; @@ -1449,7 +1809,7 @@ for (;;) active_count--; /* Remove non-match possibility */ next_active_state--; } - ADD_NEW_DATA(-(state_offset + count), 0, 0); + ADD_NEW_DATA(-(state_offset + (int)count), 0, 0); } } break; @@ -1461,32 +1821,73 @@ for (;;) case OP_PROP_EXTRA + OP_TYPEMINUPTO: case OP_PROP_EXTRA + OP_TYPEPOSUPTO: if (codevalue != OP_PROP_EXTRA + OP_TYPEEXACT) - { ADD_ACTIVE(state_offset + 6, 0); } + { ADD_ACTIVE(state_offset + 1 + IMM2_SIZE + 3, 0); } count = current_state->count; /* Number already matched */ if (clen > 0) { BOOL OK; - int category = _erts_pcre_ucp_findprop(c, &chartype, &script); - switch(code[4]) + const pcre_uint32 *cp; + const ucd_record * prop = GET_UCD(c); + switch(code[1 + IMM2_SIZE + 1]) { case PT_ANY: OK = TRUE; break; case PT_LAMP: - OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt; + OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt; break; case PT_GC: - OK = category == code[5]; + OK = PRIV(ucp_gentype)[prop->chartype] == code[1 + IMM2_SIZE + 2]; break; case PT_PC: - OK = chartype == code[5]; + OK = prop->chartype == code[1 + IMM2_SIZE + 2]; break; case PT_SC: - OK = script == code[5]; + OK = prop->script == code[1 + IMM2_SIZE + 2]; + break; + + /* These are specials for combination cases. */ + + case PT_ALNUM: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N; + break; + + case PT_SPACE: /* Perl space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR; + break; + + case PT_PXSPACE: /* POSIX space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR; + break; + + case PT_WORD: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE; + break; + + case PT_CLIST: + cp = PRIV(ucd_caseless_sets) + code[1 + IMM2_SIZE + 2]; + for (;;) + { + if (c < *cp) { OK = FALSE; break; } + if (c == *cp++) { OK = TRUE; break; } + } + break; + + case PT_UCNC: + OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || + c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || + c >= 0xe000; break; /* Should never occur, but keep compilers from grumbling. */ @@ -1503,8 +1904,8 @@ for (;;) active_count--; /* Remove non-match possibility */ next_active_state--; } - if (++count >= GET2(code, 1)) - { ADD_NEW(state_offset + 6, 0); } + if (++count >= (int)GET2(code, 1)) + { ADD_NEW(state_offset + 1 + IMM2_SIZE + 3, 0); } else { ADD_NEW(state_offset, count); } } @@ -1517,28 +1918,33 @@ for (;;) case OP_EXTUNI_EXTRA + OP_TYPEMINUPTO: case OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO: if (codevalue != OP_EXTUNI_EXTRA + OP_TYPEEXACT) - { ADD_ACTIVE(state_offset + 4, 0); } + { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } count = current_state->count; /* Number already matched */ - if (clen > 0 && _erts_pcre_ucp_findprop(c, &chartype, &script) != ucp_M) + if (clen > 0) { - const uschar *nptr = ptr + clen; + int lgb, rgb; + const pcre_uchar *nptr = ptr + clen; int ncount = 0; if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO) { active_count--; /* Remove non-match possibility */ next_active_state--; } + lgb = UCD_GRAPHBREAK(c); while (nptr < end_subject) { - int nd; - int ndlen = 1; - GETCHARLEN(nd, nptr, ndlen); - if (_erts_pcre_ucp_findprop(nd, &chartype, &script) != ucp_M) break; + dlen = 1; + if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } + rgb = UCD_GRAPHBREAK(d); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; ncount++; - nptr += ndlen; + lgb = rgb; + nptr += dlen; } - if (++count >= GET2(code, 1)) - { ADD_NEW_DATA(-(state_offset + 4), 0, ncount); } + if (nptr >= end_subject && (md->moptions & PCRE_PARTIAL_HARD) != 0) + reset_could_continue = TRUE; + if (++count >= (int)GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, ncount); } else { ADD_NEW_DATA(-state_offset, count, ncount); } } @@ -1551,34 +1957,36 @@ for (;;) case OP_ANYNL_EXTRA + OP_TYPEMINUPTO: case OP_ANYNL_EXTRA + OP_TYPEPOSUPTO: if (codevalue != OP_ANYNL_EXTRA + OP_TYPEEXACT) - { ADD_ACTIVE(state_offset + 4, 0); } + { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } count = current_state->count; /* Number already matched */ if (clen > 0) { int ncount = 0; switch (c) { - case 0x000b: - case 0x000c: - case 0x0085: + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#ifndef EBCDIC case 0x2028: case 0x2029: +#endif /* Not EBCDIC */ if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; goto ANYNL03; - case 0x000d: - if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1; + case CHAR_CR: + if (ptr + 1 < end_subject && RAWUCHARTEST(ptr + 1) == CHAR_LF) ncount = 1; /* Fall through */ ANYNL03: - case 0x000a: + case CHAR_LF: if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSUPTO) { active_count--; /* Remove non-match possibility */ next_active_state--; } - if (++count >= GET2(code, 1)) - { ADD_NEW_DATA(-(state_offset + 4), 0, ncount); } + if (++count >= (int)GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, ncount); } else { ADD_NEW_DATA(-state_offset, count, ncount); } break; @@ -1595,20 +2003,14 @@ for (;;) case OP_VSPACE_EXTRA + OP_TYPEMINUPTO: case OP_VSPACE_EXTRA + OP_TYPEPOSUPTO: if (codevalue != OP_VSPACE_EXTRA + OP_TYPEEXACT) - { ADD_ACTIVE(state_offset + 4, 0); } + { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } count = current_state->count; /* Number already matched */ if (clen > 0) { BOOL OK; switch (c) { - case 0x000a: - case 0x000b: - case 0x000c: - case 0x000d: - case 0x0085: - case 0x2028: - case 0x2029: + VSPACE_CASES: OK = TRUE; break; @@ -1623,8 +2025,8 @@ for (;;) active_count--; /* Remove non-match possibility */ next_active_state--; } - if (++count >= GET2(code, 1)) - { ADD_NEW_DATA(-(state_offset + 4), 0, 0); } + if (++count >= (int)GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, 0); } else { ADD_NEW_DATA(-state_offset, count, 0); } } @@ -1637,32 +2039,14 @@ for (;;) case OP_HSPACE_EXTRA + OP_TYPEMINUPTO: case OP_HSPACE_EXTRA + OP_TYPEPOSUPTO: if (codevalue != OP_HSPACE_EXTRA + OP_TYPEEXACT) - { ADD_ACTIVE(state_offset + 4, 0); } + { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } count = current_state->count; /* Number already matched */ if (clen > 0) { BOOL OK; switch (c) { - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ + HSPACE_CASES: OK = TRUE; break; @@ -1678,8 +2062,8 @@ for (;;) active_count--; /* Remove non-match possibility */ next_active_state--; } - if (++count >= GET2(code, 1)) - { ADD_NEW_DATA(-(state_offset + 4), 0, 0); } + if (++count >= (int)GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, 0); } else { ADD_NEW_DATA(-state_offset, count, 0); } } @@ -1698,35 +2082,35 @@ for (;;) break; /*-----------------------------------------------------------------*/ - case OP_CHARNC: + case OP_CHARI: if (clen == 0) break; -#ifdef SUPPORT_UTF8 - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { if (c == d) { ADD_NEW(state_offset + dlen + 1, 0); } else { unsigned int othercase; - if (c < 128) othercase = fcc[c]; else - - /* If we have Unicode property support, we can use it to test the - other case of the character. */ - + if (c < 128) + othercase = fcc[c]; + else + /* If we have Unicode property support, we can use it to test the + other case of the character. */ #ifdef SUPPORT_UCP - othercase = _erts_pcre_ucp_othercase(c); + othercase = UCD_OTHERCASE(c); #else - othercase = NOTACHAR; + othercase = NOTACHAR; #endif if (d == othercase) { ADD_NEW(state_offset + dlen + 1, 0); } } } else -#endif /* SUPPORT_UTF8 */ - - /* Non-UTF-8 mode */ +#endif /* SUPPORT_UTF */ + /* Not UTF mode */ { - if (lcc[c] == lcc[d]) { ADD_NEW(state_offset + 2, 0); } + if (TABLE_GET(c, lcc, c) == TABLE_GET(d, lcc, d)) + { ADD_NEW(state_offset + 2, 0); } } break; @@ -1738,18 +2122,24 @@ for (;;) to wait for them to pass before continuing. */ case OP_EXTUNI: - if (clen > 0 && _erts_pcre_ucp_findprop(c, &chartype, &script) != ucp_M) + if (clen > 0) { - const uschar *nptr = ptr + clen; + int lgb, rgb; + const pcre_uchar *nptr = ptr + clen; int ncount = 0; + lgb = UCD_GRAPHBREAK(c); while (nptr < end_subject) { - int nclen = 1; - GETCHARLEN(c, nptr, nclen); - if (_erts_pcre_ucp_findprop(c, &chartype, &script) != ucp_M) break; + dlen = 1; + if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } + rgb = UCD_GRAPHBREAK(d); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; ncount++; - nptr += nclen; + lgb = rgb; + nptr += dlen; } + if (nptr >= end_subject && (md->moptions & PCRE_PARTIAL_HARD) != 0) + reset_could_continue = TRUE; ADD_NEW_DATA(-(state_offset + 1), 0, ncount); } break; @@ -1763,19 +2153,27 @@ for (;;) case OP_ANYNL: if (clen > 0) switch(c) { - case 0x000b: - case 0x000c: - case 0x0085: + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#ifndef EBCDIC case 0x2028: case 0x2029: +#endif /* Not EBCDIC */ if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; - case 0x000a: + case CHAR_LF: ADD_NEW(state_offset + 1, 0); break; - case 0x000d: - if (ptr + 1 < end_subject && ptr[1] == 0x0a) + case CHAR_CR: + if (ptr + 1 >= end_subject) + { + ADD_NEW(state_offset + 1, 0); + if ((md->moptions & PCRE_PARTIAL_HARD) != 0) + reset_could_continue = TRUE; + } + else if (RAWUCHARTEST(ptr + 1) == CHAR_LF) { ADD_NEW_DATA(-(state_offset + 1), 0, 1); } @@ -1791,13 +2189,7 @@ for (;;) case OP_NOT_VSPACE: if (clen > 0) switch(c) { - case 0x000a: - case 0x000b: - case 0x000c: - case 0x000d: - case 0x0085: - case 0x2028: - case 0x2029: + VSPACE_CASES: break; default: @@ -1810,17 +2202,12 @@ for (;;) case OP_VSPACE: if (clen > 0) switch(c) { - case 0x000a: - case 0x000b: - case 0x000c: - case 0x000d: - case 0x0085: - case 0x2028: - case 0x2029: + VSPACE_CASES: ADD_NEW(state_offset + 1, 0); break; - default: break; + default: + break; } break; @@ -1828,25 +2215,7 @@ for (;;) case OP_NOT_HSPACE: if (clen > 0) switch(c) { - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ + HSPACE_CASES: break; default: @@ -1859,44 +2228,55 @@ for (;;) case OP_HSPACE: if (clen > 0) switch(c) { - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ + HSPACE_CASES: ADD_NEW(state_offset + 1, 0); break; + + default: + break; } break; /*-----------------------------------------------------------------*/ - /* Match a negated single character. This is only used for one-byte - characters, that is, we know that d < 256. The character we are - checking (c) can be multibyte. */ + /* Match a negated single character casefully. */ case OP_NOT: + if (clen > 0 && c != d) { ADD_NEW(state_offset + dlen + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + /* Match a negated single character caselessly. */ + + case OP_NOTI: if (clen > 0) { - unsigned int otherd = ((ims & PCRE_CASELESS) != 0)? fcc[d] : d; - if (c != d && c != otherd) { ADD_NEW(state_offset + dlen + 1, 0); } + unsigned int otherd; +#ifdef SUPPORT_UTF + if (utf && d >= 128) + { +#ifdef SUPPORT_UCP + otherd = UCD_OTHERCASE(d); +#endif /* SUPPORT_UCP */ + } + else +#endif /* SUPPORT_UTF */ + otherd = TABLE_GET(d, fcc, d); + if (c != d && c != otherd) + { ADD_NEW(state_offset + dlen + 1, 0); } } break; /*-----------------------------------------------------------------*/ + case OP_PLUSI: + case OP_MINPLUSI: + case OP_POSPLUSI: + case OP_NOTPLUSI: + case OP_NOTMINPLUSI: + case OP_NOTPOSPLUSI: + caseless = TRUE; + codevalue -= OP_STARI - OP_STAR; + + /* Fall through */ case OP_PLUS: case OP_MINPLUS: case OP_POSPLUS: @@ -1907,19 +2287,19 @@ for (;;) if (count > 0) { ADD_ACTIVE(state_offset + dlen + 1, 0); } if (clen > 0) { - unsigned int otherd = NOTACHAR; - if ((ims & PCRE_CASELESS) != 0) + pcre_uint32 otherd = NOTACHAR; + if (caseless) { -#ifdef SUPPORT_UTF8 - if (utf8 && d >= 128) +#ifdef SUPPORT_UTF + if (utf && d >= 128) { #ifdef SUPPORT_UCP - otherd = _erts_pcre_ucp_othercase(d); + otherd = UCD_OTHERCASE(d); #endif /* SUPPORT_UCP */ } else -#endif /* SUPPORT_UTF8 */ - otherd = fcc[d]; +#endif /* SUPPORT_UTF */ + otherd = TABLE_GET(d, fcc, d); } if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) { @@ -1936,6 +2316,15 @@ for (;;) break; /*-----------------------------------------------------------------*/ + case OP_QUERYI: + case OP_MINQUERYI: + case OP_POSQUERYI: + case OP_NOTQUERYI: + case OP_NOTMINQUERYI: + case OP_NOTPOSQUERYI: + caseless = TRUE; + codevalue -= OP_STARI - OP_STAR; + /* Fall through */ case OP_QUERY: case OP_MINQUERY: case OP_POSQUERY: @@ -1945,19 +2334,19 @@ for (;;) ADD_ACTIVE(state_offset + dlen + 1, 0); if (clen > 0) { - unsigned int otherd = NOTACHAR; - if ((ims & PCRE_CASELESS) != 0) + pcre_uint32 otherd = NOTACHAR; + if (caseless) { -#ifdef SUPPORT_UTF8 - if (utf8 && d >= 128) +#ifdef SUPPORT_UTF + if (utf && d >= 128) { #ifdef SUPPORT_UCP - otherd = _erts_pcre_ucp_othercase(d); + otherd = UCD_OTHERCASE(d); #endif /* SUPPORT_UCP */ } else -#endif /* SUPPORT_UTF8 */ - otherd = fcc[d]; +#endif /* SUPPORT_UTF */ + otherd = TABLE_GET(d, fcc, d); } if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) { @@ -1972,6 +2361,15 @@ for (;;) break; /*-----------------------------------------------------------------*/ + case OP_STARI: + case OP_MINSTARI: + case OP_POSSTARI: + case OP_NOTSTARI: + case OP_NOTMINSTARI: + case OP_NOTPOSSTARI: + caseless = TRUE; + codevalue -= OP_STARI - OP_STAR; + /* Fall through */ case OP_STAR: case OP_MINSTAR: case OP_POSSTAR: @@ -1981,19 +2379,19 @@ for (;;) ADD_ACTIVE(state_offset + dlen + 1, 0); if (clen > 0) { - unsigned int otherd = NOTACHAR; - if ((ims & PCRE_CASELESS) != 0) + pcre_uint32 otherd = NOTACHAR; + if (caseless) { -#ifdef SUPPORT_UTF8 - if (utf8 && d >= 128) +#ifdef SUPPORT_UTF + if (utf && d >= 128) { #ifdef SUPPORT_UCP - otherd = _erts_pcre_ucp_othercase(d); + otherd = UCD_OTHERCASE(d); #endif /* SUPPORT_UCP */ } else -#endif /* SUPPORT_UTF8 */ - otherd = fcc[d]; +#endif /* SUPPORT_UTF */ + otherd = TABLE_GET(d, fcc, d); } if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) { @@ -2008,29 +2406,34 @@ for (;;) break; /*-----------------------------------------------------------------*/ + case OP_EXACTI: + case OP_NOTEXACTI: + caseless = TRUE; + codevalue -= OP_STARI - OP_STAR; + /* Fall through */ case OP_EXACT: case OP_NOTEXACT: count = current_state->count; /* Number already matched */ if (clen > 0) { - unsigned int otherd = NOTACHAR; - if ((ims & PCRE_CASELESS) != 0) + pcre_uint32 otherd = NOTACHAR; + if (caseless) { -#ifdef SUPPORT_UTF8 - if (utf8 && d >= 128) +#ifdef SUPPORT_UTF + if (utf && d >= 128) { #ifdef SUPPORT_UCP - otherd = _erts_pcre_ucp_othercase(d); + otherd = UCD_OTHERCASE(d); #endif /* SUPPORT_UCP */ } else -#endif /* SUPPORT_UTF8 */ - otherd = fcc[d]; +#endif /* SUPPORT_UTF */ + otherd = TABLE_GET(d, fcc, d); } if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) { - if (++count >= GET2(code, 1)) - { ADD_NEW(state_offset + dlen + 3, 0); } + if (++count >= (int)GET2(code, 1)) + { ADD_NEW(state_offset + dlen + 1 + IMM2_SIZE, 0); } else { ADD_NEW(state_offset, count); } } @@ -2038,29 +2441,38 @@ for (;;) break; /*-----------------------------------------------------------------*/ + case OP_UPTOI: + case OP_MINUPTOI: + case OP_POSUPTOI: + case OP_NOTUPTOI: + case OP_NOTMINUPTOI: + case OP_NOTPOSUPTOI: + caseless = TRUE; + codevalue -= OP_STARI - OP_STAR; + /* Fall through */ case OP_UPTO: case OP_MINUPTO: case OP_POSUPTO: case OP_NOTUPTO: case OP_NOTMINUPTO: case OP_NOTPOSUPTO: - ADD_ACTIVE(state_offset + dlen + 3, 0); + ADD_ACTIVE(state_offset + dlen + 1 + IMM2_SIZE, 0); count = current_state->count; /* Number already matched */ if (clen > 0) { - unsigned int otherd = NOTACHAR; - if ((ims & PCRE_CASELESS) != 0) + pcre_uint32 otherd = NOTACHAR; + if (caseless) { -#ifdef SUPPORT_UTF8 - if (utf8 && d >= 128) +#ifdef SUPPORT_UTF + if (utf && d >= 128) { #ifdef SUPPORT_UCP - otherd = _erts_pcre_ucp_othercase(d); + otherd = UCD_OTHERCASE(d); #endif /* SUPPORT_UCP */ } else -#endif /* SUPPORT_UTF8 */ - otherd = fcc[d]; +#endif /* SUPPORT_UTF */ + otherd = TABLE_GET(d, fcc, d); } if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) { @@ -2069,8 +2481,8 @@ for (;;) active_count--; /* Remove non-match possibility */ next_active_state--; } - if (++count >= GET2(code, 1)) - { ADD_NEW(state_offset + dlen + 3, 0); } + if (++count >= (int)GET2(code, 1)) + { ADD_NEW(state_offset + dlen + 1 + IMM2_SIZE, 0); } else { ADD_NEW(state_offset, count); } } @@ -2087,18 +2499,18 @@ for (;;) { BOOL isinclass = FALSE; int next_state_offset; - const uschar *ecode; + const pcre_uchar *ecode; /* For a simple class, there is always just a 32-byte table, and we can set isinclass from it. */ if (codevalue != OP_XCLASS) { - ecode = code + 33; + ecode = code + 1 + (32 / sizeof(pcre_uchar)); if (clen > 0) { isinclass = (c > 255)? (codevalue == OP_NCLASS) : - ((code[1 + c/8] & (1 << (c&7))) != 0); + ((((pcre_uint8 *)(code + 1))[c/8] & (1 << (c&7))) != 0); } } @@ -2109,14 +2521,14 @@ for (;;) else { ecode = code + GET(code, 1); - if (clen > 0) isinclass = _erts_pcre_xclass(c, code + 1 + LINK_SIZE); + if (clen > 0) isinclass = PRIV(xclass)(c, code + 1 + LINK_SIZE, utf); } /* At this point, isinclass is set for all kinds of class, and ecode points to the byte after the end of the class. If there is a quantifier, this is where it will be. */ - next_state_offset = ecode - start_code; + next_state_offset = (int)(ecode - start_code); switch (*ecode) { @@ -2142,13 +2554,13 @@ for (;;) case OP_CRRANGE: case OP_CRMINRANGE: count = current_state->count; /* Already matched */ - if (count >= GET2(ecode, 1)) - { ADD_ACTIVE(next_state_offset + 5, 0); } + if (count >= (int)GET2(ecode, 1)) + { ADD_ACTIVE(next_state_offset + 1 + 2 * IMM2_SIZE, 0); } if (isinclass) { - int max = GET2(ecode, 3); + int max = (int)GET2(ecode, 1 + IMM2_SIZE); if (++count >= max && max != 0) /* Max 0 => no limit */ - { ADD_NEW(next_state_offset + 5, 0); } + { ADD_NEW(next_state_offset + 1 + 2 * IMM2_SIZE, 0); } else { ADD_NEW(state_offset, count); } } @@ -2163,7 +2575,13 @@ for (;;) /* ========================================================================== */ /* These are the opcodes for fancy brackets of various kinds. We have - to use recursion in order to handle them. */ + to use recursion in order to handle them. The "always failing" assertion + (?!) is optimised to OP_FAIL when compiling, so we have to support that, + though the other "backtracking verbs" are not supported. */ + + case OP_FAIL: + forced_fail++; /* Count FAILs for multiple states */ + break; case OP_ASSERT: case OP_ASSERT_NOT: @@ -2173,7 +2591,7 @@ for (;;) int rc; int local_offsets[2]; int local_workspace[1000]; - const uschar *endasscode = code + GET(code, 1); + const pcre_uchar *endasscode = code + GET(code, 1); while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); @@ -2181,17 +2599,16 @@ for (;;) md, /* static match data */ code, /* this subexpression's code */ ptr, /* where we currently are */ - ptr - start_subject, /* start offset */ + (int)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ sizeof(local_offsets)/sizeof(int), /* size of same */ local_workspace, /* workspace vector */ sizeof(local_workspace)/sizeof(int), /* size of same */ - ims, /* the current ims flags */ - rlevel, /* function recursion level */ - recursing); /* pass on regex recursion */ + rlevel); /* function recursion level */ + if (rc == PCRE_ERROR_DFA_UITEM) return rc; if ((rc >= 0) == (codevalue == OP_ASSERT || codevalue == OP_ASSERTBACK)) - { ADD_ACTIVE(endasscode + LINK_SIZE + 1 - start_code, 0); } + { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); } } break; @@ -2201,29 +2618,67 @@ for (;;) { int local_offsets[1000]; int local_workspace[1000]; - int condcode = code[LINK_SIZE+1]; + int codelink = GET(code, 1); + int condcode; + + /* Because of the way auto-callout works during compile, a callout item + is inserted between OP_COND and an assertion condition. This does not + happen for the other conditions. */ + + if (code[LINK_SIZE+1] == OP_CALLOUT) + { + rrc = 0; + if (PUBL(callout) != NULL) + { + PUBL(callout_block) cb; + cb.version = 1; /* Version 1 of the callout block */ + cb.callout_number = code[LINK_SIZE+2]; + cb.offset_vector = offsets; +#if defined COMPILE_PCRE8 + cb.subject = (PCRE_SPTR)start_subject; +#elif defined COMPILE_PCRE16 + cb.subject = (PCRE_SPTR16)start_subject; +#elif defined COMPILE_PCRE32 + cb.subject = (PCRE_SPTR32)start_subject; +#endif + cb.subject_length = (int)(end_subject - start_subject); + cb.start_match = (int)(current_subject - start_subject); + cb.current_position = (int)(ptr - start_subject); + cb.pattern_position = GET(code, LINK_SIZE + 3); + cb.next_item_length = GET(code, 3 + 2*LINK_SIZE); + cb.capture_top = 1; + cb.capture_last = -1; + cb.callout_data = md->callout_data; + cb.mark = NULL; /* No (*MARK) support */ + if ((rrc = (*PUBL(callout))(&cb)) < 0) return rrc; /* Abandon */ + } + if (rrc > 0) break; /* Fail this thread */ + code += PRIV(OP_lengths)[OP_CALLOUT]; /* Skip callout data */ + } + + condcode = code[LINK_SIZE+1]; /* Back reference conditions are not supported */ - if (condcode == OP_CREF) return PCRE_ERROR_DFA_UCOND; + if (condcode == OP_CREF || condcode == OP_NCREF) + return PCRE_ERROR_DFA_UCOND; /* The DEFINE condition is always false */ if (condcode == OP_DEF) - { - ADD_ACTIVE(state_offset + GET(code, 1) + LINK_SIZE + 1, 0); - } + { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); } /* The only supported version of OP_RREF is for the value RREF_ANY, which means "test if in any recursion". We can't test for specifically recursed groups. */ - else if (condcode == OP_RREF) + else if (condcode == OP_RREF || condcode == OP_NRREF) { - int value = GET2(code, LINK_SIZE+2); + int value = GET2(code, LINK_SIZE + 2); if (value != RREF_ANY) return PCRE_ERROR_DFA_UCOND; - if (recursing > 0) { ADD_ACTIVE(state_offset + LINK_SIZE + 4, 0); } - else { ADD_ACTIVE(state_offset + GET(code, 1) + LINK_SIZE + 1, 0); } + if (md->recursive != NULL) + { ADD_ACTIVE(state_offset + LINK_SIZE + 2 + IMM2_SIZE, 0); } + else { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); } } /* Otherwise, the condition is an assertion */ @@ -2231,8 +2686,8 @@ for (;;) else { int rc; - const uschar *asscode = code + LINK_SIZE + 1; - const uschar *endasscode = asscode + GET(asscode, 1); + const pcre_uchar *asscode = code + LINK_SIZE + 1; + const pcre_uchar *endasscode = asscode + GET(asscode, 1); while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); @@ -2240,20 +2695,19 @@ for (;;) md, /* fixed match data */ asscode, /* this subexpression's code */ ptr, /* where we currently are */ - ptr - start_subject, /* start offset */ + (int)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ sizeof(local_offsets)/sizeof(int), /* size of same */ local_workspace, /* workspace vector */ sizeof(local_workspace)/sizeof(int), /* size of same */ - ims, /* the current ims flags */ - rlevel, /* function recursion level */ - recursing); /* pass on regex recursion */ + rlevel); /* function recursion level */ + if (rc == PCRE_ERROR_DFA_UITEM) return rc; if ((rc >= 0) == (condcode == OP_ASSERT || condcode == OP_ASSERTBACK)) - { ADD_ACTIVE(endasscode + LINK_SIZE + 1 - start_code, 0); } + { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); } else - { ADD_ACTIVE(state_offset + GET(code, 1) + LINK_SIZE + 1, 0); } + { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); } } } break; @@ -2261,28 +2715,47 @@ for (;;) /*-----------------------------------------------------------------*/ case OP_RECURSE: { + dfa_recursion_info *ri; int local_offsets[1000]; int local_workspace[1000]; + const pcre_uchar *callpat = start_code + GET(code, 1); + int recno = (callpat == md->start_code)? 0 : + GET2(callpat, 1 + LINK_SIZE); int rc; - DPRINTF(("%.*sStarting regex recursion %d\n", rlevel*2-2, SP, - recursing + 1)); + DPRINTF(("%.*sStarting regex recursion\n", rlevel*2-2, SP)); + + /* Check for repeating a recursion without advancing the subject + pointer. This should catch convoluted mutual recursions. (Some simple + cases are caught at compile time.) */ + + for (ri = md->recursive; ri != NULL; ri = ri->prevrec) + if (recno == ri->group_num && ptr == ri->subject_position) + return PCRE_ERROR_RECURSELOOP; + + /* Remember this recursion and where we started it so as to + catch infinite loops. */ + + new_recursive.group_num = recno; + new_recursive.subject_position = ptr; + new_recursive.prevrec = md->recursive; + md->recursive = &new_recursive; rc = internal_dfa_exec( md, /* fixed match data */ - start_code + GET(code, 1), /* this subexpression's code */ + callpat, /* this subexpression's code */ ptr, /* where we currently are */ - ptr - start_subject, /* start offset */ + (int)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ sizeof(local_offsets)/sizeof(int), /* size of same */ local_workspace, /* workspace vector */ sizeof(local_workspace)/sizeof(int), /* size of same */ - ims, /* the current ims flags */ - rlevel, /* function recursion level */ - recursing + 1); /* regex recurse level */ + rlevel); /* function recursion level */ + + md->recursive = new_recursive.prevrec; /* Done this recursion */ - DPRINTF(("%.*sReturn from regex recursion %d: rc=%d\n", rlevel*2-2, SP, - recursing + 1, rc)); + DPRINTF(("%.*sReturn from regex recursion: rc=%d\n", rlevel*2-2, SP, + rc)); /* Ran out of internal offsets */ @@ -2296,10 +2769,15 @@ for (;;) { for (rc = rc*2 - 2; rc >= 0; rc -= 2) { - const uschar *p = start_subject + local_offsets[rc]; - const uschar *pp = start_subject + local_offsets[rc+1]; int charcount = local_offsets[rc+1] - local_offsets[rc]; - while (p < pp) if ((*p++ & 0xc0) == 0x80) charcount--; +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf) + { + const pcre_uchar *p = start_subject + local_offsets[rc]; + const pcre_uchar *pp = start_subject + local_offsets[rc+1]; + while (p < pp) if (NOT_FIRSTCHAR(*p++)) charcount--; + } +#endif if (charcount > 0) { ADD_NEW_DATA(-(state_offset + LINK_SIZE + 1), 0, (charcount - 1)); @@ -2314,8 +2792,100 @@ for (;;) } break; + /*-----------------------------------------------------------------*/ + case OP_BRAPOS: + case OP_SBRAPOS: + case OP_CBRAPOS: + case OP_SCBRAPOS: + case OP_BRAPOSZERO: + { + int charcount, matched_count; + const pcre_uchar *local_ptr = ptr; + BOOL allow_zero; + + if (codevalue == OP_BRAPOSZERO) + { + allow_zero = TRUE; + codevalue = *(++code); /* Codevalue will be one of above BRAs */ + } + else allow_zero = FALSE; + + /* Loop to match the subpattern as many times as possible as if it were + a complete pattern. */ + + for (matched_count = 0;; matched_count++) + { + int local_offsets[2]; + int local_workspace[1000]; + + int rc = internal_dfa_exec( + md, /* fixed match data */ + code, /* this subexpression's code */ + local_ptr, /* where we currently are */ + (int)(ptr - start_subject), /* start offset */ + local_offsets, /* offset vector */ + sizeof(local_offsets)/sizeof(int), /* size of same */ + local_workspace, /* workspace vector */ + sizeof(local_workspace)/sizeof(int), /* size of same */ + rlevel); /* function recursion level */ + + /* Failed to match */ + + if (rc < 0) + { + if (rc != PCRE_ERROR_NOMATCH) return rc; + break; + } + + /* Matched: break the loop if zero characters matched. */ + + charcount = local_offsets[1] - local_offsets[0]; + if (charcount == 0) break; + local_ptr += charcount; /* Advance temporary position ptr */ + } + + /* At this point we have matched the subpattern matched_count + times, and local_ptr is pointing to the character after the end of the + last match. */ + + if (matched_count > 0 || allow_zero) + { + const pcre_uchar *end_subpattern = code; + int next_state_offset; + + do { end_subpattern += GET(end_subpattern, 1); } + while (*end_subpattern == OP_ALT); + next_state_offset = + (int)(end_subpattern - start_code + LINK_SIZE + 1); + + /* Optimization: if there are no more active states, and there + are no new states yet set up, then skip over the subject string + right here, to save looping. Otherwise, set up the new state to swing + into action when the end of the matched substring is reached. */ + + if (i + 1 >= active_count && new_count == 0) + { + ptr = local_ptr; + clen = 0; + ADD_NEW(next_state_offset, 0); + } + else + { + const pcre_uchar *p = ptr; + const pcre_uchar *pp = local_ptr; + charcount = (int)(pp - p); +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf) while (p < pp) if (NOT_FIRSTCHAR(*p++)) charcount--; +#endif + ADD_NEW_DATA(-next_state_offset, 0, (charcount - 1)); + } + } + } + break; + /*-----------------------------------------------------------------*/ case OP_ONCE: + case OP_ONCE_NC: { int local_offsets[2]; int local_workspace[1000]; @@ -2324,24 +2894,23 @@ for (;;) md, /* fixed match data */ code, /* this subexpression's code */ ptr, /* where we currently are */ - ptr - start_subject, /* start offset */ + (int)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ sizeof(local_offsets)/sizeof(int), /* size of same */ local_workspace, /* workspace vector */ sizeof(local_workspace)/sizeof(int), /* size of same */ - ims, /* the current ims flags */ - rlevel, /* function recursion level */ - recursing); /* pass on regex recursion */ + rlevel); /* function recursion level */ if (rc >= 0) { - const uschar *end_subpattern = code; + const pcre_uchar *end_subpattern = code; int charcount = local_offsets[1] - local_offsets[0]; int next_state_offset, repeat_state_offset; do { end_subpattern += GET(end_subpattern, 1); } while (*end_subpattern == OP_ALT); - next_state_offset = end_subpattern - start_code + LINK_SIZE + 1; + next_state_offset = + (int)(end_subpattern - start_code + LINK_SIZE + 1); /* If the end of this subpattern is KETRMAX or KETRMIN, we must arrange for the repeat state also to be added to the relevant list. @@ -2349,7 +2918,7 @@ for (;;) repeat_state_offset = (*end_subpattern == OP_KETRMAX || *end_subpattern == OP_KETRMIN)? - end_subpattern - start_code - GET(end_subpattern, 1) : -1; + (int)(end_subpattern - start_code - GET(end_subpattern, 1)) : -1; /* If we have matched an empty string, add the next state at the current character pointer. This is important so that the duplicate @@ -2364,7 +2933,7 @@ for (;;) /* Optimization: if there are no more active states, and there are no new states yet set up, then skip over the subject string right here, to save looping. Otherwise, set up the new state to swing - into action when the end of the substring is reached. */ + into action when the end of the matched substring is reached. */ else if (i + 1 >= active_count && new_count == 0) { @@ -2387,14 +2956,18 @@ for (;;) } else { - const uschar *p = start_subject + local_offsets[0]; - const uschar *pp = start_subject + local_offsets[1]; - while (p < pp) if ((*p++ & 0xc0) == 0x80) charcount--; +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + if (utf) + { + const pcre_uchar *p = start_subject + local_offsets[0]; + const pcre_uchar *pp = start_subject + local_offsets[1]; + while (p < pp) if (NOT_FIRSTCHAR(*p++)) charcount--; + } +#endif ADD_NEW_DATA(-next_state_offset, 0, (charcount - 1)); if (repeat_state_offset >= 0) { ADD_NEW_DATA(-repeat_state_offset, 0, (charcount - 1)); } } - } else if (rc != PCRE_ERROR_NOMATCH) return rc; } @@ -2405,25 +2978,33 @@ for (;;) /* Handle callouts */ case OP_CALLOUT: - if (erts_pcre_callout != NULL) + rrc = 0; + if (PUBL(callout) != NULL) { - int rrc; - pcre_callout_block cb; + PUBL(callout_block) cb; cb.version = 1; /* Version 1 of the callout block */ cb.callout_number = code[1]; cb.offset_vector = offsets; +#if defined COMPILE_PCRE8 cb.subject = (PCRE_SPTR)start_subject; - cb.subject_length = end_subject - start_subject; - cb.start_match = current_subject - start_subject; - cb.current_position = ptr - start_subject; +#elif defined COMPILE_PCRE16 + cb.subject = (PCRE_SPTR16)start_subject; +#elif defined COMPILE_PCRE32 + cb.subject = (PCRE_SPTR32)start_subject; +#endif + cb.subject_length = (int)(end_subject - start_subject); + cb.start_match = (int)(current_subject - start_subject); + cb.current_position = (int)(ptr - start_subject); cb.pattern_position = GET(code, 2); cb.next_item_length = GET(code, 2 + LINK_SIZE); cb.capture_top = 1; cb.capture_last = -1; cb.callout_data = md->callout_data; - if ((rrc = (*erts_pcre_callout)(&cb)) < 0) return rrc; /* Abandon */ - if (rrc == 0) { ADD_ACTIVE(state_offset + 2 + 2*LINK_SIZE, 0); } + cb.mark = NULL; /* No (*MARK) support */ + if ((rrc = (*PUBL(callout))(&cb)) < 0) return rrc; /* Abandon */ } + if (rrc == 0) + { ADD_ACTIVE(state_offset + PRIV(OP_lengths)[OP_CALLOUT], 0); } break; @@ -2439,24 +3020,35 @@ for (;;) /* We have finished the processing at the current subject character. If no new states have been set for the next character, we have found all the matches that we are going to find. If we are at the top level and partial - matching has been requested, check for appropriate conditions. */ + matching has been requested, check for appropriate conditions. + + The "forced_ fail" variable counts the number of (*F) encountered for the + character. If it is equal to the original active_count (saved in + workspace[1]) it means that (*F) was found on every active state. In this + case we don't want to give a partial match. + + The "could_continue" variable is true if a state could have continued but + for the fact that the end of the subject was reached. */ if (new_count <= 0) { - if (match_count < 0 && /* No matches found */ - rlevel == 1 && /* Top level match function */ - (md->moptions & PCRE_PARTIAL) != 0 && /* Want partial matching */ - ptr >= end_subject && /* Reached end of subject */ - ptr > current_subject) /* Matched non-empty string */ - { - if (offsetcount >= 2) - { - offsets[0] = current_subject - start_subject; - offsets[1] = end_subject - start_subject; - } + if (rlevel == 1 && /* Top level, and */ + could_continue && /* Some could go on, and */ + forced_fail != workspace[1] && /* Not all forced fail & */ + ( /* either... */ + (md->moptions & PCRE_PARTIAL_HARD) != 0 /* Hard partial */ + || /* or... */ + ((md->moptions & PCRE_PARTIAL_SOFT) != 0 && /* Soft partial and */ + match_count < 0) /* no matches */ + ) && /* And... */ + ( + partial_newline || /* Either partial NL */ + ( /* or ... */ + ptr >= end_subject && /* End of subject and */ + ptr > md->start_used_ptr) /* Inspected non-empty string */ + ) + ) match_count = PCRE_ERROR_PARTIAL; - } - DPRINTF(("%.*sEnd of internal_dfa_exec %d: returning %d\n" "%.*s---------------------\n\n", rlevel*2-2, SP, rlevel, match_count, rlevel*2-2, SP)); @@ -2506,28 +3098,38 @@ Returns: > 0 => number of match offset pairs placed in offsets < -1 => some kind of unexpected problem */ -PCRE_EXP_DEFN int -erts_pcre_dfa_exec(const pcre *argument_re, const pcre_extra *extra_data, +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_dfa_exec(const pcre *argument_re, const pcre_extra *extra_data, const char *subject, int length, int start_offset, int options, int *offsets, int offsetcount, int *workspace, int wscount) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_dfa_exec(const pcre16 *argument_re, const pcre16_extra *extra_data, + PCRE_SPTR16 subject, int length, int start_offset, int options, int *offsets, + int offsetcount, int *workspace, int wscount) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_dfa_exec(const pcre32 *argument_re, const pcre32_extra *extra_data, + PCRE_SPTR32 subject, int length, int start_offset, int options, int *offsets, + int offsetcount, int *workspace, int wscount) +#endif { -real_pcre *re = (real_pcre *)argument_re; +REAL_PCRE *re = (REAL_PCRE *)argument_re; dfa_match_data match_block; dfa_match_data *md = &match_block; -BOOL utf8, anchored, startline, firstline; -const uschar *current_subject, *end_subject, *lcc; - -pcre_study_data internal_study; +BOOL utf, anchored, startline, firstline; +const pcre_uchar *current_subject, *end_subject; const pcre_study_data *study = NULL; -real_pcre internal_re; - -const uschar *req_byte_ptr; -const uschar *start_bits = NULL; -BOOL first_byte_caseless = FALSE; -BOOL req_byte_caseless = FALSE; -int first_byte = -1; -int req_byte = -1; -int req_byte2 = -1; + +const pcre_uchar *req_char_ptr; +const pcre_uint8 *start_bits = NULL; +BOOL has_first_char = FALSE; +BOOL has_req_char = FALSE; +pcre_uchar first_char = 0; +pcre_uchar first_char2 = 0; +pcre_uchar req_char = 0; +pcre_uchar req_char2 = 0; int newline; /* Plausibility checks */ @@ -2537,11 +3139,30 @@ if (re == NULL || subject == NULL || workspace == NULL || (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL; if (offsetcount < 0) return PCRE_ERROR_BADCOUNT; if (wscount < 20) return PCRE_ERROR_DFA_WSSIZE; +if (length < 0) return PCRE_ERROR_BADLENGTH; +if (start_offset < 0 || start_offset > length) return PCRE_ERROR_BADOFFSET; + +/* Check that the first field in the block is the magic number. If it is not, +return with PCRE_ERROR_BADMAGIC. However, if the magic number is equal to +REVERSED_MAGIC_NUMBER we return with PCRE_ERROR_BADENDIANNESS, which +means that the pattern is likely compiled with different endianness. */ + +if (re->magic_number != MAGIC_NUMBER) + return re->magic_number == REVERSED_MAGIC_NUMBER? + PCRE_ERROR_BADENDIANNESS:PCRE_ERROR_BADMAGIC; +if ((re->flags & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; + +/* If restarting after a partial match, do some sanity checks on the contents +of the workspace. */ + +if ((options & PCRE_DFA_RESTART) != 0) + { + if ((workspace[0] & (-2)) != 0 || workspace[1] < 1 || + workspace[1] > (wscount - 2)/INTS_PER_STATEBLOCK) + return PCRE_ERROR_DFA_BADRESTART; + } -/* We need to find the pointer to any study data before we test for byte -flipping, so we scan the extra_data block first. This may set two fields in the -match block, so we must initialize them beforehand. However, the other fields -in the match block must not be set until after the byte flipping. */ +/* Set up study, callout, and table data */ md->tables = re->tables; md->callout_data = NULL; @@ -2560,28 +3181,17 @@ if (extra_data != NULL) md->tables = extra_data->tables; } -/* Check that the first field in the block is the magic number. If it is not, -test for a regex that was compiled on a host of opposite endianness. If this is -the case, flipped values are put in internal_re and internal_study if there was -study data too. */ - -if (re->magic_number != MAGIC_NUMBER) - { - re = _erts_pcre_try_flipped(re, &internal_re, study, &internal_study); - if (re == NULL) return PCRE_ERROR_BADMAGIC; - if (study != NULL) study = &internal_study; - } - /* Set some local values */ -current_subject = (const unsigned char *)subject + start_offset; -end_subject = (const unsigned char *)subject + length; -req_byte_ptr = current_subject - 1; +current_subject = (const pcre_uchar *)subject + start_offset; +end_subject = (const pcre_uchar *)subject + length; +req_char_ptr = current_subject - 1; -#ifdef SUPPORT_UTF8 -utf8 = (re->options & PCRE_UTF8) != 0; +#ifdef SUPPORT_UTF +/* PCRE_UTF(16|32) have the same value as PCRE_UTF8. */ +utf = (re->options & PCRE_UTF8) != 0; #else -utf8 = FALSE; +utf = FALSE; #endif anchored = (options & (PCRE_ANCHORED|PCRE_DFA_RESTART)) != 0 || @@ -2589,10 +3199,11 @@ anchored = (options & (PCRE_ANCHORED|PCRE_DFA_RESTART)) != 0 || /* The remaining fixed data for passing around. */ -md->start_code = (const uschar *)argument_re + +md->start_code = (const pcre_uchar *)argument_re + re->name_table_offset + re->name_count * re->name_entry_size; -md->start_subject = (const unsigned char *)subject; +md->start_subject = (const pcre_uchar *)subject; md->end_subject = end_subject; +md->start_offset = start_offset; md->moptions = options; md->poptions = re->options; @@ -2615,10 +3226,10 @@ switch ((((options & PCRE_NEWLINE_BITS) == 0)? re->options : (pcre_uint32)option PCRE_NEWLINE_BITS) { case 0: newline = NEWLINE; break; /* Compile-time default */ - case PCRE_NEWLINE_CR: newline = '\r'; break; - case PCRE_NEWLINE_LF: newline = '\n'; break; + case PCRE_NEWLINE_CR: newline = CHAR_CR; break; + case PCRE_NEWLINE_LF: newline = CHAR_NL; break; case PCRE_NEWLINE_CR+ - PCRE_NEWLINE_LF: newline = ('\r' << 8) | '\n'; break; + PCRE_NEWLINE_LF: newline = (CHAR_CR << 8) | CHAR_NL; break; case PCRE_NEWLINE_ANY: newline = -1; break; case PCRE_NEWLINE_ANYCRLF: newline = -2; break; default: return PCRE_ERROR_BADNEWLINE; @@ -2651,20 +3262,33 @@ else /* Check a UTF-8 string if required. Unfortunately there's no way of passing back the character offset. */ -#ifdef SUPPORT_UTF8 -if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0) +#ifdef SUPPORT_UTF +if (utf && (options & PCRE_NO_UTF8_CHECK) == 0) { - if (_erts_pcre_valid_utf8((uschar *)subject, length) >= 0) - return PCRE_ERROR_BADUTF8; - if (start_offset > 0 && start_offset < length) + int erroroffset; + int errorcode = PRIV(valid_utf)((pcre_uchar *)subject, length, &erroroffset); + if (errorcode != 0) { - int tb = ((uschar *)subject)[start_offset]; - if (tb > 127) + if (offsetcount >= 2) { - tb &= 0xc0; - if (tb != 0 && tb != 0xc0) return PCRE_ERROR_BADUTF8_OFFSET; + offsets[0] = erroroffset; + offsets[1] = errorcode; } +#if defined COMPILE_PCRE8 + return (errorcode <= PCRE_UTF8_ERR5 && (options & PCRE_PARTIAL_HARD) != 0) ? + PCRE_ERROR_SHORTUTF8 : PCRE_ERROR_BADUTF8; +#elif defined COMPILE_PCRE16 + return (errorcode <= PCRE_UTF16_ERR1 && (options & PCRE_PARTIAL_HARD) != 0) ? + PCRE_ERROR_SHORTUTF16 : PCRE_ERROR_BADUTF16; +#elif defined COMPILE_PCRE32 + return PCRE_ERROR_BADUTF32; +#endif } +#if defined COMPILE_PCRE8 || defined COMPILE_PCRE16 + if (start_offset > 0 && start_offset < length && + NOT_FIRSTCHAR(((PCRE_PUCHAR)subject)[start_offset])) + return PCRE_ERROR_BADUTF8_OFFSET; +#endif } #endif @@ -2672,12 +3296,11 @@ if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0) is a feature that makes it possible to save compiled regex and re-use them in other programs later. */ -if (md->tables == NULL) md->tables = _erts_pcre_default_tables; +if (md->tables == NULL) md->tables = PRIV(default_tables); -/* The lower casing table and the "must be at the start of a line" flag are -used in a loop when finding where to start. */ +/* The "must be at the start of a line" flags are used in a loop when finding +where to start. */ -lcc = md->tables + lcc_offset; startline = (re->flags & PCRE_STARTLINE) != 0; firstline = (re->options & PCRE_FIRSTLINE) != 0; @@ -2691,14 +3314,21 @@ if (!anchored) { if ((re->flags & PCRE_FIRSTSET) != 0) { - first_byte = re->first_byte & 255; - if ((first_byte_caseless = ((re->first_byte & REQ_CASELESS) != 0)) == TRUE) - first_byte = lcc[first_byte]; + has_first_char = TRUE; + first_char = first_char2 = (pcre_uchar)(re->first_char); + if ((re->flags & PCRE_FCH_CASELESS) != 0) + { + first_char2 = TABLE_GET(first_char, md->tables + fcc_offset, first_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (utf && first_char > 127) + first_char2 = UCD_OTHERCASE(first_char); +#endif + } } else { - if (startline && study != NULL && - (study->options & PCRE_STUDY_MAPPED) != 0) + if (!startline && study != NULL && + (study->flags & PCRE_STUDY_MAPPED) != 0) start_bits = study->start_bits; } } @@ -2708,15 +3338,21 @@ character" set. */ if ((re->flags & PCRE_REQCHSET) != 0) { - req_byte = re->req_byte & 255; - req_byte_caseless = (re->req_byte & REQ_CASELESS) != 0; - req_byte2 = (md->tables + fcc_offset)[req_byte]; /* case flipped */ + has_req_char = TRUE; + req_char = req_char2 = (pcre_uchar)(re->req_char); + if ((re->flags & PCRE_RCH_CASELESS) != 0) + { + req_char2 = TABLE_GET(req_char, md->tables + fcc_offset, req_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (utf && req_char > 127) + req_char2 = UCD_OTHERCASE(req_char); +#endif + } } /* Call the main matching function, looping for a non-anchored regex after a -failed match. Unless restarting, optimize by moving to the first match -character if possible, when not anchored. Then unless wanting a partial match, -check for a required later character. */ +failed match. If not restarting, perform certain optimizations at the start of +a match. */ for (;;) { @@ -2724,128 +3360,194 @@ for (;;) if ((options & PCRE_DFA_RESTART) == 0) { - const uschar *save_end_subject = end_subject; + const pcre_uchar *save_end_subject = end_subject; - /* Advance to a unique first char if possible. If firstline is TRUE, the - start of the match is constrained to the first line of a multiline string. - Implement this by temporarily adjusting end_subject so that we stop - scanning at a newline. If the match fails at the newline, later code breaks - this loop. */ + /* If firstline is TRUE, the start of the match is constrained to the first + line of a multiline string. Implement this by temporarily adjusting + end_subject so that we stop scanning at a newline. If the match fails at + the newline, later code breaks this loop. */ if (firstline) { - const uschar *t = current_subject; + PCRE_PUCHAR t = current_subject; +#ifdef SUPPORT_UTF + if (utf) + { + while (t < md->end_subject && !IS_NEWLINE(t)) + { + t++; + ACROSSCHAR(t < end_subject, *t, t++); + } + } + else +#endif while (t < md->end_subject && !IS_NEWLINE(t)) t++; end_subject = t; } - if (first_byte >= 0) + /* There are some optimizations that avoid running the match if a known + starting point is not found. However, there is an option that disables + these, for testing and for ensuring that all callouts do actually occur. + The option can be set in the regex by (*NO_START_OPT) or passed in + match-time options. */ + + if (((options | re->options) & PCRE_NO_START_OPTIMIZE) == 0) { - if (first_byte_caseless) - while (current_subject < end_subject && - lcc[*current_subject] != first_byte) - current_subject++; - else - while (current_subject < end_subject && *current_subject != first_byte) - current_subject++; - } + /* Advance to a known first char. */ + + if (has_first_char) + { + if (first_char != first_char2) + { + pcre_uchar csc; + while (current_subject < end_subject && + (csc = RAWUCHARTEST(current_subject)) != first_char && csc != first_char2) + current_subject++; + } + else + while (current_subject < end_subject && + RAWUCHARTEST(current_subject) != first_char) + current_subject++; + } - /* Or to just after a linebreak for a multiline match if possible */ + /* Or to just after a linebreak for a multiline match if possible */ - else if (startline) - { - if (current_subject > md->start_subject + start_offset) + else if (startline) { - while (current_subject <= end_subject && !WAS_NEWLINE(current_subject)) - current_subject++; - - /* If we have just passed a CR and the newline option is ANY or - ANYCRLF, and we are now at a LF, advance the match position by one more - character. */ - - if (current_subject[-1] == '\r' && - (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF) && - current_subject < end_subject && - *current_subject == '\n') - current_subject++; + if (current_subject > md->start_subject + start_offset) + { +#ifdef SUPPORT_UTF + if (utf) + { + while (current_subject < end_subject && + !WAS_NEWLINE(current_subject)) + { + current_subject++; + ACROSSCHAR(current_subject < end_subject, *current_subject, + current_subject++); + } + } + else +#endif + while (current_subject < end_subject && !WAS_NEWLINE(current_subject)) + current_subject++; + + /* If we have just passed a CR and the newline option is ANY or + ANYCRLF, and we are now at a LF, advance the match position by one + more character. */ + + if (RAWUCHARTEST(current_subject - 1) == CHAR_CR && + (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF) && + current_subject < end_subject && + RAWUCHARTEST(current_subject) == CHAR_NL) + current_subject++; + } } - } - /* Or to a non-unique first char after study */ + /* Or to a non-unique first char after study */ - else if (start_bits != NULL) - { - while (current_subject < end_subject) + else if (start_bits != NULL) { - register unsigned int c = *current_subject; - if ((start_bits[c/8] & (1 << (c&7))) == 0) current_subject++; + while (current_subject < end_subject) + { + register pcre_uint32 c = RAWUCHARTEST(current_subject); +#ifndef COMPILE_PCRE8 + if (c > 255) c = 255; +#endif + if ((start_bits[c/8] & (1 << (c&7))) == 0) + { + current_subject++; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 + /* In non 8-bit mode, the iteration will stop for + characters > 255 at the beginning or not stop at all. */ + if (utf) + ACROSSCHAR(current_subject < end_subject, *current_subject, + current_subject++); +#endif + } else break; + } } } /* Restore fudged end_subject */ end_subject = save_end_subject; - } - - /* If req_byte is set, we know that that character must appear in the subject - for the match to succeed. If the first character is set, req_byte must be - later in the subject; otherwise the test starts at the match point. This - optimization can save a huge amount of work in patterns with nested unlimited - repeats that aren't going to match. Writing separate code for cased/caseless - versions makes it go faster, as does using an autoincrement and backing off - on a match. - - HOWEVER: when the subject string is very, very long, searching to its end can - take a long time, and give bad performance on quite ordinary patterns. This - showed up when somebody was matching /^C/ on a 32-megabyte string... so we - don't do this when the string is sufficiently long. - - ALSO: this processing is disabled when partial matching is requested. - */ - - if (req_byte >= 0 && - end_subject - current_subject < REQ_BYTE_MAX && - (options & PCRE_PARTIAL) == 0) - { - register const uschar *p = current_subject + ((first_byte >= 0)? 1 : 0); - /* We don't need to repeat the search if we haven't yet reached the - place we found it at last time. */ + /* The following two optimizations are disabled for partial matching or if + disabling is explicitly requested (and of course, by the test above, this + code is not obeyed when restarting after a partial match). */ - if (p > req_byte_ptr) + if (((options | re->options) & PCRE_NO_START_OPTIMIZE) == 0 && + (options & (PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT)) == 0) { - if (req_byte_caseless) - { - while (p < end_subject) - { - register int pp = *p++; - if (pp == req_byte || pp == req_byte2) { p--; break; } - } - } - else + /* If the pattern was studied, a minimum subject length may be set. This + is a lower bound; no actual string of that length may actually match the + pattern. Although the value is, strictly, in characters, we treat it as + bytes to avoid spending too much time in this optimization. */ + + if (study != NULL && (study->flags & PCRE_STUDY_MINLEN) != 0 && + (pcre_uint32)(end_subject - current_subject) < study->minlength) + return PCRE_ERROR_NOMATCH; + + /* If req_char is set, we know that that character must appear in the + subject for the match to succeed. If the first character is set, req_char + must be later in the subject; otherwise the test starts at the match + point. This optimization can save a huge amount of work in patterns with + nested unlimited repeats that aren't going to match. Writing separate + code for cased/caseless versions makes it go faster, as does using an + autoincrement and backing off on a match. + + HOWEVER: when the subject string is very, very long, searching to its end + can take a long time, and give bad performance on quite ordinary + patterns. This showed up when somebody was matching /^C/ on a 32-megabyte + string... so we don't do this when the string is sufficiently long. */ + + if (has_req_char && end_subject - current_subject < REQ_BYTE_MAX) { - while (p < end_subject) + register PCRE_PUCHAR p = current_subject + (has_first_char? 1:0); + + /* We don't need to repeat the search if we haven't yet reached the + place we found it at last time. */ + + if (p > req_char_ptr) { - if (*p++ == req_byte) { p--; break; } - } - } + if (req_char != req_char2) + { + while (p < end_subject) + { + register pcre_uint32 pp = RAWUCHARINCTEST(p); + if (pp == req_char || pp == req_char2) { p--; break; } + } + } + else + { + while (p < end_subject) + { + if (RAWUCHARINCTEST(p) == req_char) { p--; break; } + } + } - /* If we can't find the required character, break the matching loop, - which will cause a return or PCRE_ERROR_NOMATCH. */ + /* If we can't find the required character, break the matching loop, + which will cause a return or PCRE_ERROR_NOMATCH. */ - if (p >= end_subject) break; + if (p >= end_subject) break; - /* If we have found the required character, save the point where we - found it, so that we don't search again next time round the loop if - the start hasn't passed this character yet. */ + /* If we have found the required character, save the point where we + found it, so that we don't search again next time round the loop if + the start hasn't passed this character yet. */ - req_byte_ptr = p; + req_char_ptr = p; + } + } } - } + } /* End of optimizations that are done when not restarting */ /* OK, now we can do the business */ + md->start_used_ptr = current_subject; + md->recursive = NULL; + rc = internal_dfa_exec( md, /* fixed match data */ md->start_code, /* this subexpression's code */ @@ -2855,34 +3557,44 @@ for (;;) offsetcount, /* size of same */ workspace, /* workspace vector */ wscount, /* size of same */ - re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL), /* ims flags */ - 0, /* function recurse level */ - 0); /* regex recurse level */ + 0); /* function recurse level */ /* Anything other than "no match" means we are done, always; otherwise, carry on only if not anchored. */ - if (rc != PCRE_ERROR_NOMATCH || anchored) return rc; + if (rc != PCRE_ERROR_NOMATCH || anchored) + { + if (rc == PCRE_ERROR_PARTIAL && offsetcount >= 2) + { + offsets[0] = (int)(md->start_used_ptr - (PCRE_PUCHAR)subject); + offsets[1] = (int)(end_subject - (PCRE_PUCHAR)subject); + if (offsetcount > 2) + offsets[2] = (int)(current_subject - (PCRE_PUCHAR)subject); + } + return rc; + } /* Advance to the next subject character unless we are at the end of a line and firstline is set. */ if (firstline && IS_NEWLINE(current_subject)) break; current_subject++; - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { - while (current_subject < end_subject && (*current_subject & 0xc0) == 0x80) - current_subject++; + ACROSSCHAR(current_subject < end_subject, *current_subject, + current_subject++); } +#endif if (current_subject > end_subject) break; /* If we have just passed a CR and we are now at a LF, and the pattern does not contain any explicit matches for \r or \n, and the newline option is CRLF or ANY or ANYCRLF, advance the match position by one more character. */ - if (current_subject[-1] == '\r' && + if (RAWUCHARTEST(current_subject - 1) == CHAR_CR && current_subject < end_subject && - *current_subject == '\n' && + RAWUCHARTEST(current_subject) == CHAR_NL && (re->flags & PCRE_HASCRORLF) == 0 && (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF || diff --git a/erts/emulator/pcre/pcre_exec.c b/erts/emulator/pcre/pcre_exec.c index 26cb219ef1..54c0fe59a1 100644 --- a/erts/emulator/pcre/pcre_exec.c +++ b/erts/emulator/pcre/pcre_exec.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2013 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -36,9 +36,10 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ + /* #define ERLANG_DEBUG 1 */ -/* This module contains erts_pcre_exec(), the externally visible function that does +/* This module contains pcre_exec(), the externally visible function that does pattern matching using an NFA algorithm, trying to mimic Perl as closely as possible. There are also some static supporting functions. */ @@ -59,10 +60,26 @@ possible. There are also some static supporting functions. */ #undef min #undef max -/* Flag bits for the match() function */ +/* The md->capture_last field uses the lower 16 bits for the last captured +substring (which can never be greater than 65535) and a bit in the top half +to mean "capture vector overflowed". This odd way of doing things was +implemented when it was realized that preserving and restoring the overflow bit +whenever the last capture number was saved/restored made for a neater +interface, and doing it this way saved on (a) another variable, which would +have increased the stack frame size (a big NO-NO in PCRE) and (b) another +separate set of save/restore instructions. The following defines are used in +implementing this. */ + +#define CAPLMASK 0x0000ffff /* The bits used for last_capture */ +#define OVFLMASK 0xffff0000 /* The bits used for the overflow flag */ +#define OVFLBIT 0x00010000 /* The bit that is set for overflow */ -#define match_condassert 0x01 /* Called to check a condition assertion */ -#define match_cbegroup 0x02 /* Could-be-empty unlimited repeat group */ +/* Values for setting in md->match_function_type to indicate two special types +of call to match(). We do it this way to save on using another stack variable, +as stack usage is to be discouraged. */ + +#define MATCH_CONDASSERT 1 /* Called to check a condition assertion */ +#define MATCH_CBEGROUP 2 /* Could-be-empty unlimited repeat group */ /* Non-error returns from the match() function. Error returns are externally defined PCRE_ERROR_xxx codes, which are all negative. */ @@ -73,10 +90,18 @@ defined PCRE_ERROR_xxx codes, which are all negative. */ /* Special internal returns from the match() function. Make them sufficiently negative to avoid the external error codes. */ -#define MATCH_COMMIT (-999) -#define MATCH_PRUNE (-998) -#define MATCH_SKIP (-997) -#define MATCH_THEN (-996) +#define MATCH_ACCEPT (-999) +#define MATCH_KETRPOS (-998) +#define MATCH_ONCE (-997) +/* The next 5 must be kept together and in sequence so that a test that checks +for any one of them can use a range. */ +#define MATCH_COMMIT (-996) +#define MATCH_PRUNE (-995) +#define MATCH_SKIP (-994) +#define MATCH_SKIP_ARG (-993) +#define MATCH_THEN (-992) +#define MATCH_BACKTRACK_MAX MATCH_THEN +#define MATCH_BACKTRACK_MIN MATCH_COMMIT /* Maximum number of ints of offset to save on the stack for recursive calls. If the offset vector is bigger, malloc is used. This should be a multiple of 3, @@ -89,9 +114,7 @@ because the offset vector is always a multiple of 3 long. */ static const char rep_min[] = { 0, 0, 1, 1, 0, 0 }; static const char rep_max[] = { 0, 0, 0, 0, 1, 1 }; - - -#ifdef DEBUG +#ifdef PCRE_DEBUG /************************************************* * Debugging function to print chars * *************************************************/ @@ -109,12 +132,13 @@ Returns: nothing */ static void -pchars(const uschar *p, int length, BOOL is_subject, match_data *md) +pchars(const pcre_uchar *p, int length, BOOL is_subject, match_data *md) { -unsigned int c; +pcre_uint32 c; +BOOL utf = md->utf; if (is_subject && length > md->end_subject - p) length = md->end_subject - p; while (length-- > 0) - if (isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c); + if (isprint(c = RAWUCHARINCTEST(p))) printf("%c", (char)c); else printf("\\x{%02x}", c); } #endif @@ -135,30 +159,39 @@ edebug_printf(const char *format, ...) #endif #endif + /************************************************* * Match a back-reference * *************************************************/ -/* If a back reference hasn't been set, the length that is passed is greater -than the number of characters left in the string, so the match fails. +/* Normally, if a back reference hasn't been set, the length that is passed is +negative, so the match always fails. However, in JavaScript compatibility mode, +the length passed is zero. Note that in caseless UTF-8 mode, the number of +subject bytes matched may be different to the number of reference bytes. Arguments: offset index into the offset vector - eptr points into the subject - length length to be matched + eptr pointer into the subject + length length of reference to be matched (number of bytes) md points to match data block - ims the ims flags + caseless TRUE if caseless -Returns: TRUE if matched +Returns: >= 0 the number of subject bytes matched + -1 no match + -2 partial match; always given if at end subject */ -static BOOL -match_ref(int offset, register USPTR eptr, int length, match_data *md, - unsigned long int ims) +static int +match_ref(int offset, register PCRE_PUCHAR eptr, int length, match_data *md, + BOOL caseless) { -USPTR p = md->start_subject + md->offset_vector[offset]; +PCRE_PUCHAR eptr_start = eptr; +register PCRE_PUCHAR p = md->start_subject + md->offset_vector[offset]; +#ifdef SUPPORT_UTF +BOOL utf = md->utf; +#endif -#ifdef DEBUG +#ifdef PCRE_DEBUG if (eptr >= md->end_subject) printf("matching subject "); else @@ -171,21 +204,83 @@ pchars(p, length, FALSE, md); printf("\n"); #endif -/* Always fail if not enough characters left */ +/* Always fail if reference not set (and not JavaScript compatible - in that +case the length is passed as zero). */ -if (length > md->end_subject - eptr) return FALSE; +if (length < 0) return -1; -/* Separate the caselesss case for speed */ +/* Separate the caseless case for speed. In UTF-8 mode we can only do this +properly if Unicode properties are supported. Otherwise, we can check only +ASCII characters. */ -if ((ims & PCRE_CASELESS) != 0) +if (caseless) { - while (length-- > 0) - if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE; +#ifdef SUPPORT_UTF +#ifdef SUPPORT_UCP + if (utf) + { + /* Match characters up to the end of the reference. NOTE: the number of + data units matched may differ, because in UTF-8 there are some characters + whose upper and lower case versions code have different numbers of bytes. + For example, U+023A (2 bytes in UTF-8) is the upper case version of U+2C65 + (3 bytes in UTF-8); a sequence of 3 of the former uses 6 bytes, as does a + sequence of two of the latter. It is important, therefore, to check the + length along the reference, not along the subject (earlier code did this + wrong). */ + + PCRE_PUCHAR endptr = p + length; + while (p < endptr) + { + pcre_uint32 c, d; + const ucd_record *ur; + if (eptr >= md->end_subject) return -2; /* Partial match */ + GETCHARINC(c, eptr); + GETCHARINC(d, p); + ur = GET_UCD(d); + if (c != d && c != d + ur->other_case) + { + const pcre_uint32 *pp = PRIV(ucd_caseless_sets) + ur->caseset; + for (;;) + { + if (c < *pp) return -1; + if (c == *pp++) break; + } + } + } + } + else +#endif +#endif + + /* The same code works when not in UTF-8 mode and in UTF-8 mode when there + is no UCP support. */ + { + while (length-- > 0) + { + pcre_uint32 cc, cp; + if (eptr >= md->end_subject) return -2; /* Partial match */ + cc = RAWUCHARTEST(eptr); + cp = RAWUCHARTEST(p); + if (TABLE_GET(cp, md->lcc, cp) != TABLE_GET(cc, md->lcc, cc)) return -1; + p++; + eptr++; + } + } } + +/* In the caseful case, we can just compare the bytes, whether or not we +are in UTF-8 mode. */ + else - { while (length-- > 0) if (*p++ != *eptr++) return FALSE; } + { + while (length-- > 0) + { + if (eptr >= md->end_subject) return -2; /* Partial match */ + if (RAWUCHARINCTEST(p) != RAWUCHARINCTEST(eptr)) return -1; + } + } -return TRUE; +return (int)(eptr - eptr_start); } @@ -236,11 +331,13 @@ enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10, RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30, RM31, RM32, RM33, RM34, RM35, RM36, RM37, RM38, RM39, RM40, RM41, RM42, RM43, RM44, RM45, RM46, RM47, RM48, RM49, RM50, - RM51, RM52, RM53, RM54 + RM51, RM52, RM53, RM54, RM55, RM56, RM57, RM58, RM59, RM60, + RM61, RM62, RM63, RM64, RM65, RM66, RM67, RM68 }; + /* These versions of the macros use the stack, as normal. There are debugging versions and production versions. Note that the "rw" argument of RMATCH isn't -actuall used in this definition. */ +actually used in this definition. */ #ifndef NO_RECURSE #ifdef ERLANG_INTEGRATION @@ -248,21 +345,21 @@ actuall used in this definition. */ #endif #define REGISTER register -#ifdef DEBUG -#define RMATCH(ra,rb,rc,rd,re,rf,rg,rw) \ +#ifdef PCRE_DEBUG +#define RMATCH(ra,rb,rc,rd,re,rw) \ { \ printf("match() called in line %d\n", __LINE__); \ - rrc = match(ra,rb,mstart,rc,rd,re,rf,rg,rdepth+1); \ + rrc = match(ra,rb,mstart,rc,rd,re,rdepth+1); \ printf("to line %d\n", __LINE__); \ } #define RRETURN(ra) \ { \ - printf("match() returned %d from line %d ", ra, __LINE__); \ + printf("match() returned %d from line %d\n", ra, __LINE__); \ return ra; \ } #else -#define RMATCH(ra,rb,rc,rd,re,rf,rg,rw) \ - rrc = match(ra,rb,mstart,rc,rd,re,rf,rg,rdepth+1) +#define RMATCH(ra,rb,rc,rd,re,rw) \ + rrc = match(ra,rb,mstart,rc,rd,re,rdepth+1) #define RRETURN(ra) return ra #endif @@ -275,17 +372,22 @@ argument of match(), which never changes. */ #define REGISTER -#define RMATCH(ra,rb,rc,rd,re,rf,rg,rw)\ +#define RMATCH(ra,rb,rc,rd,re,rw)\ {\ - heapframe *newframe = (erts_pcre_stack_malloc)(sizeof(heapframe));\ - frame->Xwhere = rw; \ + heapframe *newframe = frame->Xnextframe;\ + if (newframe == NULL)\ + {\ + newframe = (heapframe *)(PUBL(stack_malloc))(sizeof(heapframe));\ + if (newframe == NULL) RRETURN(PCRE_ERROR_NOMEMORY);\ + newframe->Xnextframe = NULL;\ + frame->Xnextframe = newframe;\ + }\ + frame->Xwhere = rw;\ newframe->Xeptr = ra;\ newframe->Xecode = rb;\ newframe->Xmstart = mstart;\ newframe->Xoffset_top = rc;\ - newframe->Xims = re;\ - newframe->Xeptrb = rf;\ - newframe->Xflags = rg;\ + newframe->Xeptrb = re;\ newframe->Xrdepth = frame->Xrdepth + 1;\ newframe->Xprevframe = frame;\ frame = newframe;\ @@ -298,9 +400,8 @@ argument of match(), which never changes. */ #ifdef ERLANG_INTEGRATION #define RRETURN(ra)\ {\ - heapframe *newframe = frame;\ - frame = newframe->Xprevframe;\ - (erts_pcre_stack_free)(newframe);\ + heapframe *oldframe = frame;\ + frame = oldframe->Xprevframe;\ if (frame != NULL)\ {\ rrc = ra;\ @@ -315,9 +416,8 @@ argument of match(), which never changes. */ #else #define RRETURN(ra)\ {\ - heapframe *newframe = frame;\ - frame = newframe->Xprevframe;\ - (erts_pcre_stack_free)(newframe);\ + heapframe *oldframe = frame;\ + frame = oldframe->Xprevframe;\ if (frame != NULL)\ {\ rrc = ra;\ @@ -327,32 +427,32 @@ argument of match(), which never changes. */ } #endif - /* Structure for remembering the local variables in a private frame */ typedef struct heapframe { struct heapframe *Xprevframe; + struct heapframe *Xnextframe; /* Function arguments that may change */ - const uschar *Xeptr; - const uschar *Xecode; - const uschar *Xmstart; + PCRE_PUCHAR Xeptr; + const pcre_uchar *Xecode; + PCRE_PUCHAR Xmstart; int Xoffset_top; - long int Xims; eptrblock *Xeptrb; - int Xflags; unsigned int Xrdepth; /* Function local variables */ - const uschar *Xcallpat; - const uschar *Xcharptr; - const uschar *Xdata; - const uschar *Xnext; - const uschar *Xpp; - const uschar *Xprev; - const uschar *Xsaved_eptr; + PCRE_PUCHAR Xcallpat; +#ifdef SUPPORT_UTF + PCRE_PUCHAR Xcharptr; +#endif + PCRE_PUCHAR Xdata; + PCRE_PUCHAR Xnext; + PCRE_PUCHAR Xpp; + PCRE_PUCHAR Xprev; + PCRE_PUCHAR Xsaved_eptr; recursion_info Xnew_recursive; @@ -360,29 +460,25 @@ typedef struct heapframe { BOOL Xcondition; BOOL Xprev_is_word; - unsigned long int Xoriginal_ims; - #ifdef SUPPORT_UCP int Xprop_type; - int Xprop_value; + unsigned int Xprop_value; int Xprop_fail_result; - int Xprop_category; - int Xprop_chartype; - int Xprop_script; int Xoclength; - uschar Xocchars[8]; + pcre_uchar Xocchars[6]; #endif + int Xcodelink; int Xctype; unsigned int Xfc; int Xfi; int Xlength; int Xmax; int Xmin; - int Xnumber; + unsigned int Xnumber; int Xoffset; - int Xop; - int Xsave_capture_last; + unsigned int Xop; + pcre_int32 Xsave_capture_last; int Xsave_offset1, Xsave_offset2, Xsave_offset3; int Xstacksave[REC_STACK_SAVE_MAX]; @@ -391,7 +487,10 @@ typedef struct heapframe { /* Where to jump back to */ int Xwhere; - +#if defined(ERLANG_INTEGRATION) + int Xlgb; + int Xrgb; +#endif } heapframe; #endif @@ -408,10 +507,33 @@ typedef struct heapframe { /* This function is called recursively in many circumstances. Whenever it returns a negative (error) response, the outer incarnation must also return the -same response. +same response. */ + +/* These macros pack up tests that are used for partial matching, and which +appear several times in the code. We set the "hit end" flag if the pointer is +at the end of the subject and also past the start of the subject (i.e. +something has been matched). For hard partial matching, we then return +immediately. The second one is used when we already know we are past the end of +the subject. */ + +#define CHECK_PARTIAL()\ + if (md->partial != 0 && eptr >= md->end_subject && \ + eptr > md->start_used_ptr) \ + { \ + md->hitend = TRUE; \ + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); \ + } + +#define SCHECK_PARTIAL()\ + if (md->partial != 0 && eptr > md->start_used_ptr) \ + { \ + md->hitend = TRUE; \ + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); \ + } -Performance note: It might be tempting to extract commonly used fields from the -md structure (e.g. utf8, end_subject) into individual variables to improve + +/* Performance note: It might be tempting to extract commonly used fields from +the md structure (e.g. utf, end_subject) into individual variables to improve performance. Tests using gcc on a SPARC disproved this; in the first case, it made performance worse. @@ -422,25 +544,21 @@ Arguments: by encountering \K) offset_top current top pointer md pointer to "static" info for the match - ims current /i, /m, and /s options eptrb pointer to chain of blocks containing eptr at start of brackets - for testing for empty matches - flags can contain - match_condassert - this is an assertion condition - match_cbegroup - this is the start of an unlimited repeat - group that can match an empty string rdepth the recursion depth Returns: MATCH_MATCH if matched ) these values are >= 0 MATCH_NOMATCH if failed to match ) + a negative MATCH_xxx value for PRUNE, SKIP, etc a negative PCRE_ERROR_xxx value if aborted by an error condition (e.g. stopped by repeated call or recursion limit) */ static int -match(REGISTER USPTR eptr, REGISTER const uschar *ecode, const uschar *mstart, - int offset_top, match_data *md, unsigned long int ims, eptrblock *eptrb, - int flags, unsigned int rdepth) +match(REGISTER PCRE_PUCHAR eptr, REGISTER const pcre_uchar *ecode, + PCRE_PUCHAR mstart, int offset_top, match_data *md, eptrblock *eptrb, + unsigned int rdepth) { /* These variables do not need to be preserved over recursion in this function, so they can be ordinary variables in all cases. Mark some of them with @@ -448,17 +566,22 @@ so they can be ordinary variables in all cases. Mark some of them with register int rrc; /* Returns from recursive calls */ register int i; /* Used for loops not involving calls to RMATCH() */ -register unsigned int c; /* Character values not kept over RMATCH() calls */ -register BOOL utf8; /* Local copy of UTF-8 flag for speed */ +register pcre_uint32 c; /* Character values not kept over RMATCH() calls */ +register BOOL utf; /* Local copy of UTF flag for speed */ BOOL minimize, possessive; /* Quantifier options */ +BOOL caseless; +int condcode; /* When recursion is not being used, all "local" variables that have to be -preserved over calls to RMATCH() are part of a "frame" which is obtained from -heap storage. Set up the top-level frame here; others are obtained from the -heap whenever RMATCH() does a "recursion". See the macro definitions above. */ +preserved over calls to RMATCH() are part of a "frame". We set up the top-level +frame on the stack here; subsequent instantiations are obtained from the heap +whenever RMATCH() does a "recursion". See the macro definitions above. Putting +the top-level on the stack rather than malloc-ing them all gives a performance +boost in many cases where there is not much "recursion". */ #ifdef NO_RECURSE + #ifdef ERLANG_INTEGRATION #define LOOP_COUNT loop_count #define LOOP_LIMIT loop_limit @@ -489,18 +612,16 @@ register int loop_limit = md->loop_limit; heapframe *frame; if (md->state_save) { frame = md->state_save; - /* ASSERT(frame != NULL); */ EDEBUGF(("Break restore!")); goto LOOP_COUNT_RETURN; } -frame = (erts_pcre_stack_malloc)(sizeof(heapframe)); +frame = (heapframe *)md->match_frames_base; #else #define COST(N) #define COST_CHK(N) -heapframe *frame = (erts_pcre_stack_malloc)(sizeof(heapframe)); +heapframe *frame = (heapframe *)md->match_frames_base; #endif - -frame->Xprevframe = NULL; /* Marks the top level */ + /* Copy in the original argument variables */ @@ -508,9 +629,7 @@ frame->Xeptr = eptr; frame->Xecode = ecode; frame->Xmstart = mstart; frame->Xoffset_top = offset_top; -frame->Xims = ims; frame->Xeptrb = eptrb; -frame->Xflags = flags; frame->Xrdepth = rdepth; /* This is where control jumps back to to effect "recursion" */ @@ -523,17 +642,16 @@ HEAP_RECURSE: #define ecode frame->Xecode #define mstart frame->Xmstart #define offset_top frame->Xoffset_top -#define ims frame->Xims #define eptrb frame->Xeptrb -#define flags frame->Xflags #define rdepth frame->Xrdepth /* Ditto for the local variables */ -#ifdef SUPPORT_UTF8 +#ifdef SUPPORT_UTF #define charptr frame->Xcharptr #endif #define callpat frame->Xcallpat +#define codelink frame->Xcodelink #define data frame->Xdata #define next frame->Xnext #define pp frame->Xpp @@ -546,15 +664,10 @@ HEAP_RECURSE: #define condition frame->Xcondition #define prev_is_word frame->Xprev_is_word -#define original_ims frame->Xoriginal_ims - #ifdef SUPPORT_UCP #define prop_type frame->Xprop_type #define prop_value frame->Xprop_value #define prop_fail_result frame->Xprop_fail_result -#define prop_category frame->Xprop_category -#define prop_chartype frame->Xprop_chartype -#define prop_script frame->Xprop_script #define oclength frame->Xoclength #define occhars frame->Xocchars #endif @@ -573,6 +686,10 @@ HEAP_RECURSE: #define save_offset2 frame->Xsave_offset2 #define save_offset3 frame->Xsave_offset3 #define stacksave frame->Xstacksave +#if defined(ERLANG_INTEGRATION) +#define lgb frame->Xlgb +#define rgb frame->Xrgb +#endif #define newptrb frame->Xnewptrb @@ -586,50 +703,83 @@ i, and fc and c, can be the same variables. */ #define fi i #define fc c +/* Many of the following variables are used only in small blocks of the code. +My normal style of coding would have declared them within each of those blocks. +However, in order to accommodate the version of this code that uses an external +"stack" implemented on the heap, it is easier to declare them all here, so the +declarations can be cut out in a block. The only declarations within blocks +below are for variables that do not have to be preserved over a recursive call +to RMATCH(). */ + +#ifdef SUPPORT_UTF +const pcre_uchar *charptr; +#endif +const pcre_uchar *callpat; +const pcre_uchar *data; +const pcre_uchar *next; +PCRE_PUCHAR pp; +const pcre_uchar *prev; +PCRE_PUCHAR saved_eptr; + +recursion_info new_recursive; -#ifdef SUPPORT_UTF8 /* Many of these variables are used only */ -const uschar *charptr; /* in small blocks of the code. My normal */ -#endif /* style of coding would have declared */ -const uschar *callpat; /* them within each of those blocks. */ -const uschar *data; /* However, in order to accommodate the */ -const uschar *next; /* version of this code that uses an */ -USPTR pp; /* external "stack" implemented on the */ -const uschar *prev; /* heap, it is easier to declare them all */ -USPTR saved_eptr; /* here, so the declarations can be cut */ - /* out in a block. The only declarations */ -recursion_info new_recursive; /* within blocks below are for variables */ - /* that do not have to be preserved over */ -BOOL cur_is_word; /* a recursive call to RMATCH(). */ +BOOL cur_is_word; BOOL condition; BOOL prev_is_word; -unsigned long int original_ims; - #ifdef SUPPORT_UCP int prop_type; -int prop_value; +unsigned int prop_value; int prop_fail_result; -int prop_category; -int prop_chartype; -int prop_script; int oclength; -uschar occhars[8]; +pcre_uchar occhars[6]; #endif +int codelink; int ctype; int length; int max; int min; -int number; +unsigned int number; int offset; -int op; -int save_capture_last; +unsigned int op; +pcre_int32 save_capture_last; int save_offset1, save_offset2, save_offset3; int stacksave[REC_STACK_SAVE_MAX]; eptrblock newptrb; + +/* There is a special fudge for calling match() in a way that causes it to +measure the size of its basic stack frame when the stack is being used for +recursion. The second argument (ecode) being NULL triggers this behaviour. It +cannot normally ever be NULL. The return is the negated value of the frame +size. */ + +if (ecode == NULL) + { + if (rdepth == 0) + return match((PCRE_PUCHAR)&rdepth, NULL, NULL, 0, NULL, NULL, 1); + else + { + int len = (char *)&rdepth - (char *)eptr; + return (len > 0)? -len : len; + } + } #endif /* NO_RECURSE */ +/* To save space on the stack and in the heap frame, I have doubled up on some +of the local variables that are used only in localised parts of the code, but +still need to be preserved over recursive calls of match(). These macros define +the alternative names that are used. */ + +#define allow_zero cur_is_word +#define cbegroup condition +#define code_offset codelink +#define condassert condition +#define matched_once prev_is_word +#define foc number +#define save_mark data + /* These statements are here to stop the compiler complaining about unitialized variables. */ @@ -649,15 +799,15 @@ TAIL_RECURSE: /* OK, now we can get on with the real code of the function. Recursive calls are specified by the macro RMATCH and RRETURN is used to return. When NO_RECURSE is *not* defined, these just turn into a recursive call to match() -and a "return", respectively (possibly with some debugging if DEBUG is +and a "return", respectively (possibly with some debugging if PCRE_DEBUG is defined). However, RMATCH isn't like a function call because it's quite a complicated macro. It has to be used in one particular way. This shouldn't, however, impact performance when true recursion is being used. */ -#ifdef SUPPORT_UTF8 -utf8 = md->utf8; /* Local copy of the flag */ +#ifdef SUPPORT_UTF +utf = md->utf; /* Local copy of the flag */ #else -utf8 = FALSE; +utf = FALSE; #endif /* First check that we haven't called match() too many times, or that we @@ -666,22 +816,24 @@ haven't exceeded the recursive call limit. */ if (md->match_call_count++ >= md->match_limit) RRETURN(PCRE_ERROR_MATCHLIMIT); if (rdepth >= md->match_limit_recursion) RRETURN(PCRE_ERROR_RECURSIONLIMIT); -original_ims = ims; /* Save for resetting on ')' */ - /* At the start of a group with an unlimited repeat that may match an empty -string, the match_cbegroup flag is set. When this is the case, add the current -subject pointer to the chain of such remembered pointers, to be checked when we -hit the closing ket, in order to break infinite loops that match no characters. -When match() is called in other circumstances, don't add to the chain. The -match_cbegroup flag must NOT be used with tail recursion, because the memory -block that is used is on the stack, so a new one may be required for each -match(). */ - -if ((flags & match_cbegroup) != 0) +string, the variable md->match_function_type is set to MATCH_CBEGROUP. It is +done this way to save having to use another function argument, which would take +up space on the stack. See also MATCH_CONDASSERT below. + +When MATCH_CBEGROUP is set, add the current subject pointer to the chain of +such remembered pointers, to be checked when we hit the closing ket, in order +to break infinite loops that match no characters. When match() is called in +other circumstances, don't add to the chain. The MATCH_CBEGROUP feature must +NOT be used with tail recursion, because the memory block that is used is on +the stack, so a new one may be required for each match(). */ + +if (md->match_function_type == MATCH_CBEGROUP) { newptrb.epb_saved_eptr = eptr; newptrb.epb_prev = eptrb; eptrb = &newptrb; + md->match_function_type = 0; } /* Now start processing the opcodes. */ @@ -693,53 +845,203 @@ for (;;) op = *ecode; EDEBUGF(("Op = %d",op)); - /* For partial matching, remember if we ever hit the end of the subject after - matching at least one subject character. */ - - if (md->partial && - eptr >= md->end_subject && - eptr > mstart) - md->hitend = TRUE; - switch(op) { + case OP_MARK: + md->nomatch_mark = ecode + 2; + md->mark = NULL; /* In case previously set by assertion */ + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, md, + eptrb, RM55); + if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && + md->mark == NULL) md->mark = ecode + 2; + + /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an + argument, and we must check whether that argument matches this MARK's + argument. It is passed back in md->start_match_ptr (an overloading of that + variable). If it does match, we reset that variable to the current subject + position and return MATCH_SKIP. Otherwise, pass back the return code + unaltered. */ + + else if (rrc == MATCH_SKIP_ARG && + STRCMP_UC_UC_TEST(ecode + 2, md->start_match_ptr) == 0) + { + md->start_match_ptr = eptr; + RRETURN(MATCH_SKIP); + } + RRETURN(rrc); + case OP_FAIL: RRETURN(MATCH_NOMATCH); + case OP_COMMIT: + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM52); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + RRETURN(MATCH_COMMIT); + case OP_PRUNE: - RMATCH(eptr, ecode + _erts_pcre_OP_lengths[*ecode], offset_top, md, - ims, eptrb, flags, RM51); + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM51); if (rrc != MATCH_NOMATCH) RRETURN(rrc); RRETURN(MATCH_PRUNE); - case OP_COMMIT: - RMATCH(eptr, ecode + _erts_pcre_OP_lengths[*ecode], offset_top, md, - ims, eptrb, flags, RM52); + case OP_PRUNE_ARG: + md->nomatch_mark = ecode + 2; + md->mark = NULL; /* In case previously set by assertion */ + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, md, + eptrb, RM56); + if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && + md->mark == NULL) md->mark = ecode + 2; if (rrc != MATCH_NOMATCH) RRETURN(rrc); - RRETURN(MATCH_COMMIT); + RRETURN(MATCH_PRUNE); case OP_SKIP: - RMATCH(eptr, ecode + _erts_pcre_OP_lengths[*ecode], offset_top, md, - ims, eptrb, flags, RM53); + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM53); if (rrc != MATCH_NOMATCH) RRETURN(rrc); md->start_match_ptr = eptr; /* Pass back current position */ RRETURN(MATCH_SKIP); + /* Note that, for Perl compatibility, SKIP with an argument does NOT set + nomatch_mark. When a pattern match ends with a SKIP_ARG for which there was + not a matching mark, we have to re-run the match, ignoring the SKIP_ARG + that failed and any that precede it (either they also failed, or were not + triggered). To do this, we maintain a count of executed SKIP_ARGs. If a + SKIP_ARG gets to top level, the match is re-run with md->ignore_skip_arg + set to the count of the one that failed. */ + + case OP_SKIP_ARG: + md->skip_arg_count++; + if (md->skip_arg_count <= md->ignore_skip_arg) + { + ecode += PRIV(OP_lengths)[*ecode] + ecode[1]; + break; + } + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, md, + eptrb, RM57); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + + /* Pass back the current skip name by overloading md->start_match_ptr and + returning the special MATCH_SKIP_ARG return code. This will either be + caught by a matching MARK, or get to the top, where it causes a rematch + with md->ignore_skip_arg set to the value of md->skip_arg_count. */ + + md->start_match_ptr = ecode + 2; + RRETURN(MATCH_SKIP_ARG); + + /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that + the branch in which it occurs can be determined. Overload the start of + match pointer to do this. */ + case OP_THEN: - RMATCH(eptr, ecode + _erts_pcre_OP_lengths[*ecode], offset_top, md, - ims, eptrb, flags, RM54); + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM54); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->start_match_ptr = ecode; + RRETURN(MATCH_THEN); + + case OP_THEN_ARG: + md->nomatch_mark = ecode + 2; + md->mark = NULL; /* In case previously set by assertion */ + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, + md, eptrb, RM58); + if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && + md->mark == NULL) md->mark = ecode + 2; if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->start_match_ptr = ecode; RRETURN(MATCH_THEN); - /* Handle a capturing bracket. If there is space in the offset vector, save - the current subject position in the working slot at the top of the vector. - We mustn't change the current values of the data slot, because they may be - set from a previous iteration of this group, and be referred to by a - reference inside the group. + /* Handle an atomic group that does not contain any capturing parentheses. + This can be handled like an assertion. Prior to 8.13, all atomic groups + were handled this way. In 8.13, the code was changed as below for ONCE, so + that backups pass through the group and thereby reset captured values. + However, this uses a lot more stack, so in 8.20, atomic groups that do not + contain any captures generate OP_ONCE_NC, which can be handled in the old, + less stack intensive way. + + Check the alternative branches in turn - the matching won't pass the KET + for this kind of subpattern. If any one branch matches, we carry on as at + the end of a normal bracket, leaving the subject pointer, but resetting + the start-of-match value in case it was changed by \K. */ + + case OP_ONCE_NC: + prev = ecode; + saved_eptr = eptr; + save_mark = md->mark; + do /* LOOP_COUNT: Ok */ + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM64); + if (rrc == MATCH_MATCH) /* Note: _not_ MATCH_ACCEPT */ + { + mstart = md->start_match_ptr; + break; + } + if (rrc == MATCH_THEN) + { + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + rrc = MATCH_NOMATCH; + } + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode += GET(ecode,1); + md->mark = save_mark; + } + while (*ecode == OP_ALT); + + /* If hit the end of the group (which could be repeated), fail */ + + if (*ecode != OP_ONCE_NC && *ecode != OP_ALT) RRETURN(MATCH_NOMATCH); + + /* Continue as from after the group, updating the offsets high water + mark, since extracts may have been taken. */ + + do ecode += GET(ecode, 1); while (*ecode == OP_ALT); + + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. */ + + if (*ecode == OP_KET || eptr == saved_eptr) + { + ecode += 1+LINK_SIZE; + break; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. The second "call" of match() + uses tail recursion, to avoid using another stack frame. */ + + if (*ecode == OP_KETRMIN) + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM65); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode = prev; + goto TAIL_RECURSE; + } + else /* OP_KETRMAX */ + { + RMATCH(eptr, prev, offset_top, md, eptrb, RM66); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode += 1 + LINK_SIZE; + goto TAIL_RECURSE; + } + /* Control never gets here */ - If the bracket fails to match, we need to restore this value and also the - values of the final offsets, in case they were set by a previous iteration - of the same bracket. + /* Handle a capturing bracket, other than those that are possessive with an + unlimited repeat. If there is space in the offset vector, save the current + subject position in the working slot at the top of the vector. We mustn't + change the current values of the data slot, because they may be set from a + previous iteration of this group, and be referred to by a reference inside + the group. A failure to match might occur after the group has succeeded, + if something later on doesn't match. For this reason, we need to restore + the working value and also the values of the final offsets, in case they + were set by a previous iteration of the same bracket. If there isn't enough space in the offset vector, treat this as if it were a non-capturing bracket. Don't worry about setting the flag for the error @@ -750,7 +1052,7 @@ for (;;) number = GET2(ecode, 1+LINK_SIZE); offset = number << 1; -#ifdef DEBUG +#ifdef PCRE_DEBUG printf("start bracket %d\n", number); printf("subject="); pchars(eptr, 16, TRUE, md); @@ -763,28 +1065,55 @@ for (;;) save_offset2 = md->offset_vector[offset+1]; save_offset3 = md->offset_vector[md->offset_end - number]; save_capture_last = md->capture_last; + save_mark = md->mark; DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3)); - md->offset_vector[md->offset_end - number] = eptr - md->start_subject; + md->offset_vector[md->offset_end - number] = + (int)(eptr - md->start_subject); - flags = (op == OP_SCBRA)? match_cbegroup : 0; - do /* PaN: OK */ + for (;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode + _erts_pcre_OP_lengths[*ecode], offset_top, md, - ims, eptrb, flags, RM1); - if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + if (op >= OP_SBRA) md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM1); + if (rrc == MATCH_ONCE) break; /* Backing up through an atomic group */ + + /* If we backed up to a THEN, check whether it is within the current + branch by comparing the address of the THEN that is passed back with + the end of the branch. If it is within the current branch, and the + branch is one of two or more alternatives (it either starts or ends + with OP_ALT), we have reached the limit of THEN's action, so convert + the return code to NOMATCH, which will cause normal backtracking to + happen from now on. Otherwise, THEN is passed back to an outer + alternative. This implements Perl's treatment of parenthesized groups, + where a group not containing | does not affect the current alternative, + that is, (X) is NOT the same as (X|(*F)). */ + + if (rrc == MATCH_THEN) + { + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + rrc = MATCH_NOMATCH; + } + + /* Anything other than NOMATCH is passed back. */ + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); md->capture_last = save_capture_last; ecode += GET(ecode, 1); + md->mark = save_mark; + if (*ecode != OP_ALT) break; } - while (*ecode == OP_ALT); DPRINTF(("bracket %d failed\n", number)); - md->offset_vector[offset] = save_offset1; md->offset_vector[offset+1] = save_offset2; md->offset_vector[md->offset_end - number] = save_offset3; - RRETURN(MATCH_NOMATCH); + /* At this point, rrc will be one of MATCH_ONCE or MATCH_NOMATCH. */ + + RRETURN(rrc); } /* FALL THROUGH ... Insufficient room for saving captured contents. Treat @@ -798,208 +1127,709 @@ for (;;) /* VVVVVVVVVVVVVVVVVVVVVVVVV */ /* VVVVVVVVVVVVVVVVVVVVVVVVV */ - /* Non-capturing bracket. Loop for all the alternatives. When we get to the - final alternative within the brackets, we would return the result of a - recursive call to match() whatever happened. We can reduce stack usage by - turning this into a tail recursion, except in the case when match_cbegroup - is set.*/ + /* Non-capturing or atomic group, except for possessive with unlimited + repeat and ONCE group with no captures. Loop for all the alternatives. + When we get to the final alternative within the brackets, we used to return + the result of a recursive call to match() whatever happened so it was + possible to reduce stack usage by turning this into a tail recursion, + except in the case of a possibly empty group. However, now that there is + the possiblity of (*THEN) occurring in the final alternative, this + optimization is no longer always possible. + + We can optimize if we know there are no (*THEN)s in the pattern; at present + this is the best that can be done. + + MATCH_ONCE is returned when the end of an atomic group is successfully + reached, but subsequent matching fails. It passes back up the tree (causing + captured values to be reset) until the original atomic group level is + reached. This is tested by comparing md->once_target with the start of the + group. At this point, the return is converted into MATCH_NOMATCH so that + previous backup points can be taken. */ + + case OP_ONCE: case OP_BRA: case OP_SBRA: DPRINTF(("start non-capturing bracket\n")); - flags = (op >= OP_SBRA)? match_cbegroup : 0; - for (;;) /* PaN: OK */ + + for (;;) /* LOOP_COUNT: Ok */ { - if (ecode[GET(ecode, 1)] != OP_ALT) /* Final alternative */ - { - if (flags == 0) /* Not a possibly empty group */ - { - ecode += _erts_pcre_OP_lengths[*ecode]; - DPRINTF(("bracket 0 tail recursion\n")); - goto TAIL_RECURSE; - } + if (op >= OP_SBRA || op == OP_ONCE) + md->match_function_type = MATCH_CBEGROUP; - /* Possibly empty group; can't use tail recursion. */ + /* If this is not a possibly empty group, and there are no (*THEN)s in + the pattern, and this is the final alternative, optimize as described + above. */ - RMATCH(eptr, ecode + _erts_pcre_OP_lengths[*ecode], offset_top, md, ims, - eptrb, flags, RM48); - RRETURN(rrc); + else if (!md->hasthen && ecode[GET(ecode, 1)] != OP_ALT) + { + ecode += PRIV(OP_lengths)[*ecode]; + goto TAIL_RECURSE; } - /* For non-final alternatives, continue the loop for a NOMATCH result; - otherwise return. */ + /* In all other cases, we have to make another call to match(). */ + + save_mark = md->mark; + save_capture_last = md->capture_last; + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, eptrb, + RM2); + + /* See comment in the code for capturing groups above about handling + THEN. */ + + if (rrc == MATCH_THEN) + { + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + rrc = MATCH_NOMATCH; + } - RMATCH(eptr, ecode + _erts_pcre_OP_lengths[*ecode], offset_top, md, ims, - eptrb, flags, RM2); - if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + if (rrc != MATCH_NOMATCH) + { + if (rrc == MATCH_ONCE) + { + const pcre_uchar *scode = ecode; + if (*scode != OP_ONCE) /* If not at start, find it */ + { + while (*scode == OP_ALT) scode += GET(scode, 1); + scode -= GET(scode, 1); + } + if (md->once_target == scode) rrc = MATCH_NOMATCH; + } + RRETURN(rrc); + } ecode += GET(ecode, 1); + md->mark = save_mark; + if (*ecode != OP_ALT) break; + md->capture_last = save_capture_last; } - /* Control never reaches here. */ - /* Conditional group: compilation checked that there are no more than - two branches. If the condition is false, skipping the first branch takes us - past the end if there is only one branch, but that's OK because that is - exactly what going to the ket would do. As there is only one branch to be - obeyed, we can use tail recursion to avoid using another stack frame. */ + RRETURN(MATCH_NOMATCH); - case OP_COND: - case OP_SCOND: - if (ecode[LINK_SIZE+1] == OP_RREF) /* Recursion test */ - { - offset = GET2(ecode, LINK_SIZE + 2); /* Recursion group number*/ - condition = md->recursive != NULL && - (offset == RREF_ANY || offset == md->recursive->group_num); - ecode += condition? 3 : GET(ecode, 1); - } + /* Handle possessive capturing brackets with an unlimited repeat. We come + here from BRAZERO with allow_zero set TRUE. The offset_vector values are + handled similarly to the normal case above. However, the matching is + different. The end of these brackets will always be OP_KETRPOS, which + returns MATCH_KETRPOS without going further in the pattern. By this means + we can handle the group by iteration rather than recursion, thereby + reducing the amount of stack needed. */ - else if (ecode[LINK_SIZE+1] == OP_CREF) /* Group used test */ - { - offset = GET2(ecode, LINK_SIZE+2) << 1; /* Doubled ref number */ - condition = offset < offset_top && md->offset_vector[offset] >= 0; - ecode += condition? 3 : GET(ecode, 1); - } + case OP_CBRAPOS: + case OP_SCBRAPOS: + allow_zero = FALSE; - else if (ecode[LINK_SIZE+1] == OP_DEF) /* DEFINE - always false */ - { - condition = FALSE; - ecode += GET(ecode, 1); - } + POSSESSIVE_CAPTURE: + number = GET2(ecode, 1+LINK_SIZE); + offset = number << 1; - /* The condition is an assertion. Call match() to evaluate it - setting - the final argument match_condassert causes it to stop at the end of an - assertion. */ +#ifdef PCRE_DEBUG + printf("start possessive bracket %d\n", number); + printf("subject="); + pchars(eptr, 16, TRUE, md); + printf("\n"); +#endif - else + if (offset < md->offset_max) { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, - match_condassert, RM3); - if (rrc == MATCH_MATCH) + matched_once = FALSE; + code_offset = (int)(ecode - md->start_code); + + save_offset1 = md->offset_vector[offset]; + save_offset2 = md->offset_vector[offset+1]; + save_offset3 = md->offset_vector[md->offset_end - number]; + save_capture_last = md->capture_last; + + DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3)); + + /* Each time round the loop, save the current subject position for use + when the group matches. For MATCH_MATCH, the group has matched, so we + restart it with a new subject starting position, remembering that we had + at least one match. For MATCH_NOMATCH, carry on with the alternatives, as + usual. If we haven't matched any alternatives in any iteration, check to + see if a previous iteration matched. If so, the group has matched; + continue from afterwards. Otherwise it has failed; restore the previous + capture values before returning NOMATCH. */ + + for (;;) /* LOOP_COUNT: Ok */ { - condition = TRUE; - ecode += 1 + LINK_SIZE + GET(ecode, LINK_SIZE + 2); - while (*ecode == OP_ALT) ecode += GET(ecode, 1); /* PaN: Check */ + md->offset_vector[md->offset_end - number] = + (int)(eptr - md->start_subject); + if (op >= OP_SBRA) md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM63); + if (rrc == MATCH_KETRPOS) + { + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + ecode = md->start_code + code_offset; + save_capture_last = md->capture_last; + matched_once = TRUE; + continue; + } + + /* See comment in the code for capturing groups above about handling + THEN. */ + + if (rrc == MATCH_THEN) + { + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + rrc = MATCH_NOMATCH; + } + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->capture_last = save_capture_last; + ecode += GET(ecode, 1); + if (*ecode != OP_ALT) break; } - else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) + + if (!matched_once) { - RRETURN(rrc); /* Need braces because of following else */ + md->offset_vector[offset] = save_offset1; + md->offset_vector[offset+1] = save_offset2; + md->offset_vector[md->offset_end - number] = save_offset3; } - else + + if (allow_zero || matched_once) { - condition = FALSE; - ecode += GET(ecode, 1); + ecode += 1 + LINK_SIZE; + break; } + + RRETURN(MATCH_NOMATCH); } - /* We are now at the branch that is to be obeyed. As there is only one, - we can use tail recursion to avoid using another stack frame, except when - match_cbegroup is required for an unlimited repeat of a possibly empty - group. If the second alternative doesn't exist, we can just plough on. */ + /* FALL THROUGH ... Insufficient room for saving captured contents. Treat + as a non-capturing bracket. */ - if (condition || *ecode == OP_ALT) + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + + DPRINTF(("insufficient capture room: treat as non-capturing\n")); + + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + + /* Non-capturing possessive bracket with unlimited repeat. We come here + from BRAZERO with allow_zero = TRUE. The code is similar to the above, + without the capturing complication. It is written out separately for speed + and cleanliness. */ + + case OP_BRAPOS: + case OP_SBRAPOS: + allow_zero = FALSE; + + POSSESSIVE_NON_CAPTURE: + matched_once = FALSE; + code_offset = (int)(ecode - md->start_code); + save_capture_last = md->capture_last; + + for (;;) /* LOOP_COUNT: Ok */ { - ecode += 1 + LINK_SIZE; - if (op == OP_SCOND) /* Possibly empty group */ + if (op >= OP_SBRA) md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM48); + if (rrc == MATCH_KETRPOS) { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, match_cbegroup, RM49); - RRETURN(rrc); + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + ecode = md->start_code + code_offset; + matched_once = TRUE; + continue; } - else /* Group must match something */ + + /* See comment in the code for capturing groups above about handling + THEN. */ + + if (rrc == MATCH_THEN) { - flags = 0; - goto TAIL_RECURSE; + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + rrc = MATCH_NOMATCH; } + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode += GET(ecode, 1); + if (*ecode != OP_ALT) break; + md->capture_last = save_capture_last; } - else /* Condition false & no 2nd alternative */ + + if (matched_once || allow_zero) { ecode += 1 + LINK_SIZE; + break; } - break; + RRETURN(MATCH_NOMATCH); + /* Control never reaches here. */ - /* End of the pattern, either real or forced. If we are in a top-level - recursion, we should restore the offsets appropriately and continue from - after the call. */ + /* Conditional group: compilation checked that there are no more than + two branches. If the condition is false, skipping the first branch takes us + past the end if there is only one branch, but that's OK because that is + exactly what going to the ket would do. */ - case OP_ACCEPT: - case OP_END: - if (md->recursive != NULL && md->recursive->group_num == 0) + case OP_COND: + case OP_SCOND: + codelink = GET(ecode, 1); + + /* Because of the way auto-callout works during compile, a callout item is + inserted between OP_COND and an assertion condition. */ + + if (ecode[LINK_SIZE+1] == OP_CALLOUT) { - recursion_info *rec = md->recursive; - DPRINTF(("End of pattern in a (?0) recursion\n")); - md->recursive = rec->prevrec; - memmove(md->offset_vector, rec->offset_save, - rec->saved_max * sizeof(int)); - mstart = rec->save_start; - ims = original_ims; - ecode = rec->after_call; - break; + if (PUBL(callout) != NULL) + { + PUBL(callout_block) cb; + cb.version = 2; /* Version 1 of the callout block */ + cb.callout_number = ecode[LINK_SIZE+2]; + cb.offset_vector = md->offset_vector; +#if defined COMPILE_PCRE8 + cb.subject = (PCRE_SPTR)md->start_subject; +#elif defined COMPILE_PCRE16 + cb.subject = (PCRE_SPTR16)md->start_subject; +#elif defined COMPILE_PCRE32 + cb.subject = (PCRE_SPTR32)md->start_subject; +#endif + cb.subject_length = (int)(md->end_subject - md->start_subject); + cb.start_match = (int)(mstart - md->start_subject); + cb.current_position = (int)(eptr - md->start_subject); + cb.pattern_position = GET(ecode, LINK_SIZE + 3); + cb.next_item_length = GET(ecode, 3 + 2*LINK_SIZE); + cb.capture_top = offset_top/2; + cb.capture_last = md->capture_last & CAPLMASK; + /* Internal change requires this for API compatibility. */ + if (cb.capture_last == 0) cb.capture_last = -1; + cb.callout_data = md->callout_data; + cb.mark = md->nomatch_mark; + if ((rrc = (*PUBL(callout))(&cb)) > 0) RRETURN(MATCH_NOMATCH); + if (rrc < 0) RRETURN(rrc); + } + ecode += PRIV(OP_lengths)[OP_CALLOUT]; + codelink -= PRIV(OP_lengths)[OP_CALLOUT]; + } + + condcode = ecode[LINK_SIZE+1]; + + /* Now see what the actual condition is */ + + if (condcode == OP_RREF || condcode == OP_NRREF) /* Recursion test */ + { + if (md->recursive == NULL) /* Not recursing => FALSE */ + { + condition = FALSE; + ecode += GET(ecode, 1); + } + else + { /* LOOP_COUNT: Warning, No CHK in this block */ + unsigned int recno = GET2(ecode, LINK_SIZE + 2); /* Recursion group number*/ + condition = (recno == RREF_ANY || recno == md->recursive->group_num); + + /* If the test is for recursion into a specific subpattern, and it is + false, but the test was set up by name, scan the table to see if the + name refers to any other numbers, and test them. The condition is true + if any one is set. */ + + if (!condition && condcode == OP_NRREF) + { + pcre_uchar *slotA = md->name_table; + for (i = 0; i < md->name_count; i++)/* LOOP_COUNT: COST */ + { + if (GET2(slotA, 0) == recno) break; + slotA += md->name_entry_size; + COST(1); + } + + /* Found a name for the number - there can be only one; duplicate + names for different numbers are allowed, but not vice versa. First + scan down for duplicates. */ + + if (i < md->name_count) + { + pcre_uchar *slotB = slotA; + while (slotB > md->name_table) /* LOOP_COUNT: COST */ + { + slotB -= md->name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = GET2(slotB, 0) == md->recursive->group_num; + if (condition) break; + } + else break; + COST(1); + } + + /* Scan up for duplicates */ + + if (!condition) + { + slotB = slotA; + for (i++; i < md->name_count; i++)/* LOOP_COUNT: COST */ + { + slotB += md->name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = GET2(slotB, 0) == md->recursive->group_num; + if (condition) break; + } + else break; + COST(1); + } + } + } + } + + /* Chose branch according to the condition */ + + ecode += condition? 1 + IMM2_SIZE : GET(ecode, 1); + } + } + + else if (condcode == OP_CREF || condcode == OP_NCREF) /* Group used test */ + { + offset = GET2(ecode, LINK_SIZE+2) << 1; /* Doubled ref number */ + condition = offset < offset_top && md->offset_vector[offset] >= 0; + + /* If the numbered capture is unset, but the reference was by name, + scan the table to see if the name refers to any other numbers, and test + them. The condition is true if any one is set. This is tediously similar + to the code above, but not close enough to try to amalgamate. */ + + if (!condition && condcode == OP_NCREF) + { + unsigned int refno = offset >> 1; + pcre_uchar *slotA = md->name_table;/* LOOP_COUNT: Warning, no CHK in this block */ + + for (i = 0; i < md->name_count; i++) /* LOOP_COUNT: COST */ + { + if (GET2(slotA, 0) == refno) break; + slotA += md->name_entry_size; + COST(1); + } + + /* Found a name for the number - there can be only one; duplicate names + for different numbers are allowed, but not vice versa. First scan down + for duplicates. */ + + if (i < md->name_count) + { + pcre_uchar *slotB = slotA; + while (slotB > md->name_table) /* LOOP_COUNT: COST */ + { + slotB -= md->name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + offset = GET2(slotB, 0) << 1; + condition = offset < offset_top && + md->offset_vector[offset] >= 0; + if (condition) break; + } + else break; + COST(1); + } + + /* Scan up for duplicates */ + + if (!condition) + { + slotB = slotA; + for (i++; i < md->name_count; i++) /* LOOP_COUNT: COST */ + { + slotB += md->name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + offset = GET2(slotB, 0) << 1; + condition = offset < offset_top && + md->offset_vector[offset] >= 0; + if (condition) break; + } + else break; + COST(1); + } + } + } + } + + /* Chose branch according to the condition */ + + ecode += condition? 1 + IMM2_SIZE : GET(ecode, 1); + } + + else if (condcode == OP_DEF) /* DEFINE - always false */ + { + condition = FALSE; + ecode += GET(ecode, 1); + } + + /* The condition is an assertion. Call match() to evaluate it - setting + md->match_function_type to MATCH_CONDASSERT causes it to stop at the end of + an assertion. */ + + else + { + md->match_function_type = MATCH_CONDASSERT; + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, NULL, RM3); + if (rrc == MATCH_MATCH) + { + if (md->end_offset_top > offset_top) + offset_top = md->end_offset_top; /* Captures may have happened */ + condition = TRUE; + ecode += 1 + LINK_SIZE + GET(ecode, LINK_SIZE + 2); + while (*ecode == OP_ALT) ecode += GET(ecode, 1); /* LOOP_COUNT: Ok */ + } + + /* PCRE doesn't allow the effect of (*THEN) to escape beyond an + assertion; it is therefore treated as NOMATCH. */ + + else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) + { + RRETURN(rrc); /* Need braces because of following else */ + } + else + { + condition = FALSE; + ecode += codelink; + } + } + + /* We are now at the branch that is to be obeyed. As there is only one, can + use tail recursion to avoid using another stack frame, except when there is + unlimited repeat of a possibly empty group. In the latter case, a recursive + call to match() is always required, unless the second alternative doesn't + exist, in which case we can just plough on. Note that, for compatibility + with Perl, the | in a conditional group is NOT treated as creating two + alternatives. If a THEN is encountered in the branch, it propagates out to + the enclosing alternative (unless nested in a deeper set of alternatives, + of course). */ + + if (condition || *ecode == OP_ALT) + { + if (op != OP_SCOND) + { + ecode += 1 + LINK_SIZE; + goto TAIL_RECURSE; + } + + md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM49); + RRETURN(rrc); + } + + /* Condition false & no alternative; continue after the group. */ + + else + { + ecode += 1 + LINK_SIZE; } + break; - /* Otherwise, if PCRE_NOTEMPTY is set, fail if we have matched an empty - string - backtracking will then try other alternatives, if any. */ - if (md->notempty && eptr == mstart) RRETURN(MATCH_NOMATCH); + /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes, + to close any currently open capturing brackets. */ + + case OP_CLOSE: + number = GET2(ecode, 1); /* Must be less than 65536 */ + offset = number << 1; + +#ifdef PCRE_DEBUG + printf("end bracket %d at *ACCEPT", number); + printf("\n"); +#endif + + md->capture_last = (md->capture_last & OVFLMASK) | number; + if (offset >= md->offset_max) md->capture_last |= OVFLBIT; else + { + md->offset_vector[offset] = + md->offset_vector[md->offset_end - number]; + md->offset_vector[offset+1] = (int)(eptr - md->start_subject); + if (offset_top <= offset) offset_top = offset + 2; + } + ecode += 1 + IMM2_SIZE; + break; + + + /* End of the pattern, either real or forced. */ + + case OP_END: + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + + /* If we have matched an empty string, fail if not in an assertion and not + in a recursion if either PCRE_NOTEMPTY is set, or if PCRE_NOTEMPTY_ATSTART + is set and we have matched at the start of the subject. In both cases, + backtracking will then try other alternatives, if any. */ + + if (eptr == mstart && op != OP_ASSERT_ACCEPT && + md->recursive == NULL && + (md->notempty || + (md->notempty_atstart && + mstart == md->start_subject + md->start_offset))) + RRETURN(MATCH_NOMATCH); + + /* Otherwise, we have a match. */ + md->end_match_ptr = eptr; /* Record where we ended */ md->end_offset_top = offset_top; /* and how many extracts were taken */ md->start_match_ptr = mstart; /* and the start (\K can modify) */ - RRETURN(MATCH_MATCH); - /* Change option settings */ + /* For some reason, the macros don't work properly if an expression is + given as the argument to RRETURN when the heap is in use. */ - case OP_OPT: - ims = ecode[1]; - ecode += 2; - DPRINTF(("ims set to %02lx\n", ims)); - break; + rrc = (op == OP_END)? MATCH_MATCH : MATCH_ACCEPT; + RRETURN(rrc); /* Assertion brackets. Check the alternative branches in turn - the matching won't pass the KET for an assertion. If any one branch matches, the assertion is true. Lookbehind assertions have an OP_REVERSE item at the start of each branch to move the current point backwards, so the code at - this level is identical to the lookahead case. */ + this level is identical to the lookahead case. When the assertion is part + of a condition, we want to return immediately afterwards. The caller of + this incarnation of the match() function will have set MATCH_CONDASSERT in + md->match_function type, and one of these opcodes will be the first opcode + that is processed. We use a local variable that is preserved over calls to + match() to remember this case. */ case OP_ASSERT: case OP_ASSERTBACK: - do /* PaN: OK */ + save_mark = md->mark; + if (md->match_function_type == MATCH_CONDASSERT) + { + condassert = TRUE; + md->match_function_type = 0; + } + else condassert = FALSE; + + /* Loop for each branch */ + + do /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, 0, - RM4); - if (rrc == MATCH_MATCH) break; - if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, NULL, RM4); + + /* A match means that the assertion is true; break out of the loop + that matches its alternatives. */ + + if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) + { + mstart = md->start_match_ptr; /* In case \K reset it */ + break; + } + + /* If not matched, restore the previous mark setting. */ + + md->mark = save_mark; + + /* See comment in the code for capturing groups above about handling + THEN. */ + + if (rrc == MATCH_THEN) + { + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + rrc = MATCH_NOMATCH; + } + + /* Anything other than NOMATCH causes the entire assertion to fail, + passing back the return code. This includes COMMIT, SKIP, PRUNE and an + uncaptured THEN, which means they take their normal effect. This + consistent approach does not always have exactly the same effect as in + Perl. */ + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); ecode += GET(ecode, 1); } - while (*ecode == OP_ALT); + while (*ecode == OP_ALT); /* Continue for next alternative */ /* LOOP_COUNT: Ok */ + + /* If we have tried all the alternative branches, the assertion has + failed. If not, we broke out after a match. */ + if (*ecode == OP_KET) RRETURN(MATCH_NOMATCH); /* If checking an assertion for a condition, return MATCH_MATCH. */ - if ((flags & match_condassert) != 0) RRETURN(MATCH_MATCH); + if (condassert) RRETURN(MATCH_MATCH); - /* Continue from after the assertion, updating the offsets high water - mark, since extracts may have been taken during the assertion. */ + /* Continue from after a successful assertion, updating the offsets high + water mark, since extracts may have been taken during the assertion. */ - do ecode += GET(ecode,1); while (*ecode == OP_ALT); /* PaN: OK */ + do ecode += GET(ecode,1); while (*ecode == OP_ALT); /* LOOP_COUNT: Ok */ ecode += 1 + LINK_SIZE; offset_top = md->end_offset_top; continue; - /* Negative assertion: all branches must fail to match */ + /* Negative assertion: all branches must fail to match for the assertion to + succeed. */ case OP_ASSERT_NOT: case OP_ASSERTBACK_NOT: - do /* PaN: OK */ + save_mark = md->mark; + if (md->match_function_type == MATCH_CONDASSERT) { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, 0, - RM5); - if (rrc == MATCH_MATCH) RRETURN(MATCH_NOMATCH); - if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + condassert = TRUE; + md->match_function_type = 0; + } + else condassert = FALSE; + + /* Loop for each alternative branch. */ + + do /* LOOP_COUNT: Ok */ + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, NULL, RM5); + md->mark = save_mark; /* Always restore the mark setting */ + + switch(rrc) + { + case MATCH_MATCH: /* A successful match means */ + case MATCH_ACCEPT: /* the assertion has failed. */ + RRETURN(MATCH_NOMATCH); + + case MATCH_NOMATCH: /* Carry on with next branch */ + break; + + /* See comment in the code for capturing groups above about handling + THEN. */ + + case MATCH_THEN: + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + { + rrc = MATCH_NOMATCH; + break; + } + /* Otherwise fall through. */ + + /* COMMIT, SKIP, PRUNE, and an uncaptured THEN cause the whole + assertion to fail to match, without considering any more alternatives. + Failing to match means the assertion is true. This is a consistent + approach, but does not always have the same effect as in Perl. */ + + case MATCH_COMMIT: + case MATCH_SKIP: + case MATCH_SKIP_ARG: + case MATCH_PRUNE: + do ecode += GET(ecode,1); while (*ecode == OP_ALT); /* LOOP_COUNT: Ok */ + goto NEG_ASSERT_TRUE; /* Break out of alternation loop */ + + /* Anything else is an error */ + + default: + RRETURN(rrc); + } + + /* Continue with next branch */ + ecode += GET(ecode,1); } while (*ecode == OP_ALT); - if ((flags & match_condassert) != 0) RRETURN(MATCH_MATCH); + /* All branches in the assertion failed to match. */ - ecode += 1 + LINK_SIZE; + NEG_ASSERT_TRUE: + if (condassert) RRETURN(MATCH_MATCH); /* Condition assertion */ + ecode += 1 + LINK_SIZE; /* Continue with current branch */ continue; /* Move the subject pointer back. This occurs only at the start of @@ -1008,12 +1838,12 @@ for (;;) back a number of characters, not bytes. */ case OP_REVERSE: -#ifdef SUPPORT_UTF8 - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { i = GET(ecode, 1); COST(i); - while (i-- > 0) /* PaN: OK */ + while (i-- > 0) /* LOOP_COUNT: COST */ { eptr--; if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH); @@ -1030,8 +1860,9 @@ for (;;) if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH); } - /* Skip to next op code */ + /* Save the earliest consulted character, then skip to next op code */ + if (eptr < md->start_used_ptr) md->start_used_ptr = eptr; ecode += 1 + LINK_SIZE; break; @@ -1040,22 +1871,31 @@ for (;;) function is able to force a failure. */ case OP_CALLOUT: - if (erts_pcre_callout != NULL) + if (PUBL(callout) != NULL) { - pcre_callout_block cb; - cb.version = 1; /* Version 1 of the callout block */ + PUBL(callout_block) cb; + cb.version = 2; /* Version 1 of the callout block */ cb.callout_number = ecode[1]; cb.offset_vector = md->offset_vector; +#if defined COMPILE_PCRE8 cb.subject = (PCRE_SPTR)md->start_subject; - cb.subject_length = md->end_subject - md->start_subject; - cb.start_match = mstart - md->start_subject; - cb.current_position = eptr - md->start_subject; +#elif defined COMPILE_PCRE16 + cb.subject = (PCRE_SPTR16)md->start_subject; +#elif defined COMPILE_PCRE32 + cb.subject = (PCRE_SPTR32)md->start_subject; +#endif + cb.subject_length = (int)(md->end_subject - md->start_subject); + cb.start_match = (int)(mstart - md->start_subject); + cb.current_position = (int)(eptr - md->start_subject); cb.pattern_position = GET(ecode, 2); cb.next_item_length = GET(ecode, 2 + LINK_SIZE); cb.capture_top = offset_top/2; - cb.capture_last = md->capture_last; + cb.capture_last = md->capture_last & CAPLMASK; + /* Internal change requires this for API compatibility. */ + if (cb.capture_last == 0) cb.capture_last = -1; cb.callout_data = md->callout_data; - if ((rrc = (*erts_pcre_callout)(&cb)) > 0) RRETURN(MATCH_NOMATCH); + cb.mark = md->nomatch_mark; + if ((rrc = (*PUBL(callout))(&cb)) > 0) RRETURN(MATCH_NOMATCH); if (rrc < 0) RRETURN(rrc); } ecode += 2 + 2*LINK_SIZE; @@ -1065,38 +1905,52 @@ for (;;) offset data is the offset to the starting bracket from the start of the whole pattern. (This is so that it works from duplicated subpatterns.) - If there are any capturing brackets started but not finished, we have to - save their starting points and reinstate them after the recursion. However, - we don't know how many such there are (offset_top records the completed - total) so we just have to save all the potential data. There may be up to - 65535 such values, which is too large to put on the stack, but using malloc - for small numbers seems expensive. As a compromise, the stack is used when - there are no more than REC_STACK_SAVE_MAX values to store; otherwise malloc - is used. A problem is what to do if the malloc fails ... there is no way of - returning to the top level with an error. Save the top REC_STACK_SAVE_MAX - values on the stack, and accept that the rest may be wrong. + The state of the capturing groups is preserved over recursion, and + re-instated afterwards. We don't know how many are started and not yet + finished (offset_top records the completed total) so we just have to save + all the potential data. There may be up to 65535 such values, which is too + large to put on the stack, but using malloc for small numbers seems + expensive. As a compromise, the stack is used when there are no more than + REC_STACK_SAVE_MAX values to store; otherwise malloc is used. There are also other values that have to be saved. We use a chained sequence of blocks that actually live on the stack. Thanks to Robin Houston - for the original version of this logic. */ + for the original version of this logic. It has, however, been hacked around + a lot, so he is not to blame for the current way it works. */ case OP_RECURSE: { + recursion_info *ri; + unsigned int recno; /* LOOP_COUNT: Warning, no CHK until after Marker1 */ + callpat = md->start_code + GET(ecode, 1); - new_recursive.group_num = (callpat == md->start_code)? 0 : + recno = (callpat == md->start_code)? 0 : GET2(callpat, 1 + LINK_SIZE); + /* Check for repeating a recursion without advancing the subject pointer. + This should catch convoluted mutual recursions. (Some simple cases are + caught at compile time.) */ + + for (ri = md->recursive; ri != NULL; ri = ri->prevrec) /* LOOP_COUNT: COST */ + { + if (recno == ri->group_num && eptr == ri->subject_position) + RRETURN(PCRE_ERROR_RECURSELOOP); + COST(1); + } + /* Add to "recursing stack" */ + new_recursive.group_num = recno; /* LOOP_COUNT: Marker1 */ + new_recursive.saved_capture_last = md->capture_last; + new_recursive.subject_position = eptr; new_recursive.prevrec = md->recursive; md->recursive = &new_recursive; - /* Find where to continue from afterwards */ + /* Where to continue from afterwards */ ecode += 1 + LINK_SIZE; - new_recursive.after_call = ecode; - /* Now save the offset data. */ + /* Now save the offset data */ new_recursive.saved_max = md->offset_end; if (new_recursive.saved_max <= REC_STACK_SAVE_MAX) @@ -1104,41 +1958,61 @@ for (;;) else { new_recursive.offset_save = - (int *)(erts_pcre_malloc)(new_recursive.saved_max * sizeof(int)); + (int *)(PUBL(malloc))(new_recursive.saved_max * sizeof(int)); if (new_recursive.offset_save == NULL) RRETURN(PCRE_ERROR_NOMEMORY); } - memcpy(new_recursive.offset_save, md->offset_vector, new_recursive.saved_max * sizeof(int)); - new_recursive.save_start = mstart; - mstart = eptr; - /* OK, now we can do the recursion. For each top-level alternative we - restore the offset and recursion data. */ + /* OK, now we can do the recursion. After processing each alternative, + restore the offset data and the last captured value. If there were nested + recursions, md->recursive might be changed, so reset it before looping. + */ DPRINTF(("Recursing into group %d\n", new_recursive.group_num)); - flags = (*callpat >= OP_SBRA)? match_cbegroup : 0; - do /* PaN: OK */ + cbegroup = (*callpat >= OP_SBRA); + do /* LOOP_COUNT: Ok */ { - RMATCH(eptr, callpat + _erts_pcre_OP_lengths[*callpat], offset_top, - md, ims, eptrb, flags, RM6); - if (rrc == MATCH_MATCH) + if (cbegroup) md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, callpat + PRIV(OP_lengths)[*callpat], offset_top, + md, eptrb, RM6); + memcpy(md->offset_vector, new_recursive.offset_save, + new_recursive.saved_max * sizeof(int)); + md->capture_last = new_recursive.saved_capture_last; + md->recursive = new_recursive.prevrec; + if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) { DPRINTF(("Recursion matched\n")); - md->recursive = new_recursive.prevrec; if (new_recursive.offset_save != stacksave) - (erts_pcre_free)(new_recursive.offset_save); - RRETURN(MATCH_MATCH); + (PUBL(free))(new_recursive.offset_save); + + /* Set where we got to in the subject, and reset the start in case + it was changed by \K. This *is* propagated back out of a recursion, + for Perl compatibility. */ + + eptr = md->end_match_ptr; + mstart = md->start_match_ptr; + goto RECURSION_MATCHED; /* Exit loop; end processing */ } - else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) + + /* PCRE does not allow THEN, SKIP, PRUNE or COMMIT to escape beyond a + recursion; they cause a NOMATCH for the entire recursion. These codes + are defined in a range that can be tested for. */ + + if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX) + RRETURN(MATCH_NOMATCH); + + /* Any return code other than NOMATCH is an error. */ + + if (rrc != MATCH_NOMATCH) { DPRINTF(("Recursion gave error %d\n", rrc)); + if (new_recursive.offset_save != stacksave) + (PUBL(free))(new_recursive.offset_save); RRETURN(rrc); } md->recursive = &new_recursive; - memcpy(md->offset_vector, new_recursive.offset_save, - new_recursive.saved_max * sizeof(int)); callpat += GET(callpat, 1); } while (*callpat == OP_ALT); @@ -1146,223 +2020,202 @@ for (;;) DPRINTF(("Recursion didn't match\n")); md->recursive = new_recursive.prevrec; if (new_recursive.offset_save != stacksave) - (erts_pcre_free)(new_recursive.offset_save); + (PUBL(free))(new_recursive.offset_save); RRETURN(MATCH_NOMATCH); } - /* Control never reaches here */ - - /* "Once" brackets are like assertion brackets except that after a match, - the point in the subject string is not moved back. Thus there can never be - a move back into the brackets. Friedl calls these "atomic" subpatterns. - Check the alternative branches in turn - the matching won't pass the KET - for this kind of subpattern. If any one branch matches, we carry on as at - the end of a normal bracket, leaving the subject pointer. */ - - case OP_ONCE: - prev = ecode; - saved_eptr = eptr; - - do /* PaN: OK */ - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0, RM7); - if (rrc == MATCH_MATCH) break; - if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); - ecode += GET(ecode,1); - } - while (*ecode == OP_ALT); - - /* If hit the end of the group (which could be repeated), fail */ - - if (*ecode != OP_ONCE && *ecode != OP_ALT) RRETURN(MATCH_NOMATCH); - - /* Continue as from after the assertion, updating the offsets high water - mark, since extracts may have been taken. */ - - do ecode += GET(ecode, 1); while (*ecode == OP_ALT); /* PaN: OK */ - - offset_top = md->end_offset_top; - eptr = md->end_match_ptr; - /* For a non-repeating ket, just continue at this level. This also - happens for a repeating ket if no characters were matched in the group. - This is the forcible breaking of infinite loops as implemented in Perl - 5.005. If there is an options reset, it will get obeyed in the normal - course of events. */ - - if (*ecode == OP_KET || eptr == saved_eptr) - { - ecode += 1+LINK_SIZE; - break; - } - - /* The repeating kets try the rest of the pattern or restart from the - preceding bracket, in the appropriate order. The second "call" of match() - uses tail recursion, to avoid using another stack frame. We need to reset - any options that changed within the bracket before re-running it, so - check the next opcode. */ - - if (ecode[1+LINK_SIZE] == OP_OPT) - { - ims = (ims & ~PCRE_IMS) | ecode[4]; - DPRINTF(("ims set to %02lx at group repeat\n", ims)); - } - - if (*ecode == OP_KETRMIN) - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0, RM8); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode = prev; - flags = 0; - goto TAIL_RECURSE; - } - else /* OP_KETRMAX */ - { - RMATCH(eptr, prev, offset_top, md, ims, eptrb, match_cbegroup, RM9); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode += 1 + LINK_SIZE; - flags = 0; - goto TAIL_RECURSE; - } - /* Control never gets here */ + RECURSION_MATCHED: + break; /* An alternation is the end of a branch; scan along to find the end of the bracketed group and go to there. */ case OP_ALT: - do ecode += GET(ecode,1); while (*ecode == OP_ALT); /* PaN: OK */ + do ecode += GET(ecode,1); while (*ecode == OP_ALT); /* LOOP_COUNT: Ok */ break; - /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating - that it may occur zero times. It may repeat infinitely, or not at all - - i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper - repeat limits are compiled as a number of copies, with the optional ones - preceded by BRAZERO or BRAMINZERO. */ + /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a bracket group, + indicating that it may occur zero times. It may repeat infinitely, or not + at all - i.e. it could be ()* or ()? or even (){0} in the pattern. Brackets + with fixed upper repeat limits are compiled as a number of copies, with the + optional ones preceded by BRAZERO or BRAMINZERO. */ case OP_BRAZERO: - { - next = ecode+1; - RMATCH(eptr, next, offset_top, md, ims, eptrb, 0, RM10); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - do next += GET(next,1); while (*next == OP_ALT); /* PaN: OK */ - ecode = next + 1 + LINK_SIZE; - } + next = ecode + 1; + RMATCH(eptr, next, offset_top, md, eptrb, RM10); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + do next += GET(next, 1); while (*next == OP_ALT); /* LOOP_COUNT: Ok */ + ecode = next + 1 + LINK_SIZE; break; case OP_BRAMINZERO: - { - next = ecode+1; - do next += GET(next, 1); while (*next == OP_ALT); /* PaN: OK */ - RMATCH(eptr, next + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0, RM11); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode++; - } + next = ecode + 1; + do next += GET(next, 1); while (*next == OP_ALT); /* LOOP_COUNT: Ok */ + RMATCH(eptr, next + 1+LINK_SIZE, offset_top, md, eptrb, RM11); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode++; + break; + + case OP_SKIPZERO: + next = ecode+1; + do next += GET(next,1); while (*next == OP_ALT); /* LOOP_COUNT: Ok */ + ecode = next + 1 + LINK_SIZE; break; + /* BRAPOSZERO occurs before a possessive bracket group. Don't do anything + here; just jump to the group, with allow_zero set TRUE. */ + + case OP_BRAPOSZERO: + op = *(++ecode); + allow_zero = TRUE; + if (op == OP_CBRAPOS || op == OP_SCBRAPOS) goto POSSESSIVE_CAPTURE; + goto POSSESSIVE_NON_CAPTURE; + /* End of a group, repeated or non-repeating. */ case OP_KET: case OP_KETRMIN: case OP_KETRMAX: + case OP_KETRPOS: prev = ecode - GET(ecode, 1); /* If this was a group that remembered the subject start, in order to break infinite repeats of empty string matches, retrieve the subject start from the chain. Otherwise, set it NULL. */ - if (*prev >= OP_SBRA) + if (*prev >= OP_SBRA || *prev == OP_ONCE) { saved_eptr = eptrb->epb_saved_eptr; /* Value at start of group */ eptrb = eptrb->epb_prev; /* Backup to previous group */ } else saved_eptr = NULL; - /* If we are at the end of an assertion group, stop matching and return - MATCH_MATCH, but record the current high water mark for use by positive - assertions. Do this also for the "once" (atomic) groups. */ + /* If we are at the end of an assertion group or a non-capturing atomic + group, stop matching and return MATCH_MATCH, but record the current high + water mark for use by positive assertions. We also need to record the match + start in case it was changed by \K. */ - if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT || - *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT || - *prev == OP_ONCE) + if ((*prev >= OP_ASSERT && *prev <= OP_ASSERTBACK_NOT) || + *prev == OP_ONCE_NC) { - md->end_match_ptr = eptr; /* For ONCE */ + md->end_match_ptr = eptr; /* For ONCE_NC */ md->end_offset_top = offset_top; - RRETURN(MATCH_MATCH); + md->start_match_ptr = mstart; + RRETURN(MATCH_MATCH); /* Sets md->mark */ } /* For capturing groups we have to check the group number back at the start and if necessary complete handling an extraction by setting the offsets and - bumping the high water mark. Note that whole-pattern recursion is coded as - a recurse into group 0, so it won't be picked up here. Instead, we catch it - when the OP_END is reached. Other recursion is handled here. */ - - if (*prev == OP_CBRA || *prev == OP_SCBRA) + bumping the high water mark. Whole-pattern recursion is coded as a recurse + into group 0, so it won't be picked up here. Instead, we catch it when the + OP_END is reached. Other recursion is handled here. We just have to record + the current subject position and start match pointer and give a MATCH + return. */ + + if (*prev == OP_CBRA || *prev == OP_SCBRA || + *prev == OP_CBRAPOS || *prev == OP_SCBRAPOS) { number = GET2(prev, 1+LINK_SIZE); offset = number << 1; -#ifdef DEBUG +#ifdef PCRE_DEBUG printf("end bracket %d", number); printf("\n"); #endif - md->capture_last = number; - if (offset >= md->offset_max) md->offset_overflow = TRUE; else + /* Handle a recursively called group. */ + + if (md->recursive != NULL && md->recursive->group_num == number) { - md->offset_vector[offset] = - md->offset_vector[md->offset_end - number]; - md->offset_vector[offset+1] = eptr - md->start_subject; - if (offset_top <= offset) offset_top = offset + 2; + md->end_match_ptr = eptr; + md->start_match_ptr = mstart; + RRETURN(MATCH_MATCH); } - /* Handle a recursively called group. Restore the offsets - appropriately and continue from after the call. */ + /* Deal with capturing */ - if (md->recursive != NULL && md->recursive->group_num == number) + md->capture_last = (md->capture_last & OVFLMASK) | number; + if (offset >= md->offset_max) md->capture_last |= OVFLBIT; else { - recursion_info *rec = md->recursive; - DPRINTF(("Recursion (%d) succeeded - continuing\n", number)); - md->recursive = rec->prevrec; - mstart = rec->save_start; - memcpy(md->offset_vector, rec->offset_save, - rec->saved_max * sizeof(int)); - ecode = rec->after_call; - ims = original_ims; - break; + /* If offset is greater than offset_top, it means that we are + "skipping" a capturing group, and that group's offsets must be marked + unset. In earlier versions of PCRE, all the offsets were unset at the + start of matching, but this doesn't work because atomic groups and + assertions can cause a value to be set that should later be unset. + Example: matching /(?>(a))b|(a)c/ against "ac". This sets group 1 as + part of the atomic group, but this is not on the final matching path, + so must be unset when 2 is set. (If there is no group 2, there is no + problem, because offset_top will then be 2, indicating no capture.) */ + + if (offset > offset_top) + { + register int *iptr = md->offset_vector + offset_top; + register int *iend = md->offset_vector + offset; + while (iptr < iend) *iptr++ = -1; /* LOOP_COUNT: CHK */ + } + + /* Now make the extraction */ + + md->offset_vector[offset] = + md->offset_vector[md->offset_end - number]; + md->offset_vector[offset+1] = (int)(eptr - md->start_subject); + if (offset_top <= offset) offset_top = offset + 2; } } - /* For both capturing and non-capturing groups, reset the value of the ims - flags, in case they got changed during the group. */ - - ims = original_ims; - DPRINTF(("ims reset to %02lx\n", ims)); - - /* For a non-repeating ket, just continue at this level. This also - happens for a repeating ket if no characters were matched in the group. - This is the forcible breaking of infinite loops as implemented in Perl - 5.005. If there is an options reset, it will get obeyed in the normal - course of events. */ + /* For an ordinary non-repeating ket, just continue at this level. This + also happens for a repeating ket if no characters were matched in the + group. This is the forcible breaking of infinite loops as implemented in + Perl 5.005. For a non-repeating atomic group that includes captures, + establish a backup point by processing the rest of the pattern at a lower + level. If this results in a NOMATCH return, pass MATCH_ONCE back to the + original OP_ONCE level, thereby bypassing intermediate backup points, but + resetting any captures that happened along the way. */ if (*ecode == OP_KET || eptr == saved_eptr) { - ecode += 1 + LINK_SIZE; + if (*prev == OP_ONCE) + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM12); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->once_target = prev; /* Level at which to change to MATCH_NOMATCH */ + RRETURN(MATCH_ONCE); + } + ecode += 1 + LINK_SIZE; /* Carry on at this level */ break; } - /* The repeating kets try the rest of the pattern or restart from the - preceding bracket, in the appropriate order. In the second case, we can use - tail recursion to avoid using another stack frame, unless we have an - unlimited repeat of a group that can match an empty string. */ + /* OP_KETRPOS is a possessive repeating ket. Remember the current position, + and return the MATCH_KETRPOS. This makes it possible to do the repeats one + at a time from the outer level, thus saving stack. */ + + if (*ecode == OP_KETRPOS) + { + md->end_match_ptr = eptr; + md->end_offset_top = offset_top; + RRETURN(MATCH_KETRPOS); + } - flags = (*prev >= OP_SBRA)? match_cbegroup : 0; + /* The normal repeating kets try the rest of the pattern or restart from + the preceding bracket, in the appropriate order. In the second case, we can + use tail recursion to avoid using another stack frame, unless we have an + an atomic group or an unlimited repeat of a group that can match an empty + string. */ if (*ecode == OP_KETRMIN) { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0, RM12); + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM7); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (flags != 0) /* Could match an empty string */ + if (*prev == OP_ONCE) + { + RMATCH(eptr, prev, offset_top, md, eptrb, RM8); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->once_target = prev; /* Level at which to change to MATCH_NOMATCH */ + RRETURN(MATCH_ONCE); + } + if (*prev >= OP_SBRA) /* Could match an empty string */ { - RMATCH(eptr, prev, offset_top, md, ims, eptrb, flags, RM50); + RMATCH(eptr, prev, offset_top, md, eptrb, RM50); RRETURN(rrc); } ecode = prev; @@ -1370,27 +2223,25 @@ for (;;) } else /* OP_KETRMAX */ { - RMATCH(eptr, prev, offset_top, md, ims, eptrb, flags, RM13); + RMATCH(eptr, prev, offset_top, md, eptrb, RM13); + if (rrc == MATCH_ONCE && md->once_target == prev) rrc = MATCH_NOMATCH; if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (*prev == OP_ONCE) + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM9); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->once_target = prev; + RRETURN(MATCH_ONCE); + } ecode += 1 + LINK_SIZE; - flags = 0; goto TAIL_RECURSE; } /* Control never gets here */ - /* Start of subject unless notbol, or after internal newline if multiline */ + /* Not multiline mode: start of subject assertion, unless notbol. */ case OP_CIRC: if (md->notbol && eptr == md->start_subject) RRETURN(MATCH_NOMATCH); - if ((ims & PCRE_MULTILINE) != 0) - { - if (eptr != md->start_subject && - (eptr >= md->end_subject || !WAS_NEWLINE(eptr))) - RRETURN(MATCH_NOMATCH); - ecode++; - break; - } - /* ... else fall through */ /* Start of subject assertion */ @@ -1399,6 +2250,16 @@ for (;;) ecode++; break; + /* Multiline mode: start of subject unless notbol, or after any newline. */ + + case OP_CIRCM: + if (md->notbol && eptr == md->start_subject) RRETURN(MATCH_NOMATCH); + if (eptr != md->start_subject && + (eptr == md->end_subject || !WAS_NEWLINE(eptr))) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + /* Start of match assertion */ case OP_SOM: @@ -1413,46 +2274,73 @@ for (;;) ecode++; break; - /* Assert before internal newline if multiline, or before a terminating - newline unless endonly is set, else end of subject unless noteol is set. */ + /* Multiline mode: assert before any newline, or before end of subject + unless noteol is set. */ - case OP_DOLL: - if ((ims & PCRE_MULTILINE) != 0) + case OP_DOLLM: + if (eptr < md->end_subject) { - if (eptr < md->end_subject) - { if (!IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); } - else - { if (md->noteol) RRETURN(MATCH_NOMATCH); } - ecode++; - break; + if (!IS_NEWLINE(eptr)) + { + if (md->partial != 0 && + eptr + 1 >= md->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + RAWUCHARTEST(eptr) == NLBLOCK->nl[0]) + { + md->hitend = TRUE; + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); + } + RRETURN(MATCH_NOMATCH); + } } else { if (md->noteol) RRETURN(MATCH_NOMATCH); - if (!md->endonly) - { - if (eptr != md->end_subject && - (!IS_NEWLINE(eptr) || eptr != md->end_subject - md->nllen)) - RRETURN(MATCH_NOMATCH); - ecode++; - break; - } + SCHECK_PARTIAL(); } + ecode++; + break; + + /* Not multiline mode: assert before a terminating newline or before end of + subject unless noteol is set. */ + + case OP_DOLL: + if (md->noteol) RRETURN(MATCH_NOMATCH); + if (!md->endonly) goto ASSERT_NL_OR_EOS; + /* ... else fall through for endonly */ /* End of subject assertion (\z) */ case OP_EOD: if (eptr < md->end_subject) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); ecode++; break; /* End of subject or ending \n assertion (\Z) */ case OP_EODN: - if (eptr != md->end_subject && + ASSERT_NL_OR_EOS: + if (eptr < md->end_subject && (!IS_NEWLINE(eptr) || eptr != md->end_subject - md->nllen)) + { + if (md->partial != 0 && + eptr + 1 >= md->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + RAWUCHARTEST(eptr) == NLBLOCK->nl[0]) + { + md->hitend = TRUE; + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); + } RRETURN(MATCH_NOMATCH); + } + + /* Either at end of string or \n before end. */ + + SCHECK_PARTIAL(); ecode++; break; @@ -1464,34 +2352,108 @@ for (;;) /* Find out if the previous and current characters are "word" characters. It takes a bit more work in UTF-8 mode. Characters > 255 are assumed to - be "non-word" characters. */ + be "non-word" characters. Remember the earliest consulted character for + partial matching. */ -#ifdef SUPPORT_UTF8 - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { + /* Get status of previous character */ + if (eptr == md->start_subject) prev_is_word = FALSE; else { - const uschar *lastptr = eptr - 1; - while((*lastptr & 0xc0) == 0x80) lastptr--; /* PaN: OK */ + PCRE_PUCHAR lastptr = eptr - 1; + BACKCHAR(lastptr); + if (lastptr < md->start_used_ptr) md->start_used_ptr = lastptr; GETCHAR(c, lastptr); +#ifdef SUPPORT_UCP + if (md->use_ucp) + { + if (c == '_') prev_is_word = TRUE; else + { + int cat = UCD_CATEGORY(c); + prev_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif prev_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0; } - if (eptr >= md->end_subject) cur_is_word = FALSE; else + + /* Get status of next character */ + + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + cur_is_word = FALSE; + } + else { GETCHAR(c, eptr); +#ifdef SUPPORT_UCP + if (md->use_ucp) + { + if (c == '_') cur_is_word = TRUE; else + { + int cat = UCD_CATEGORY(c); + cur_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif cur_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0; } } else #endif - /* More streamlined when not in UTF-8 mode */ + /* Not in UTF-8 mode, but we may still have PCRE_UCP set, and for + consistency with the behaviour of \w we do use it in this case. */ { - prev_is_word = (eptr != md->start_subject) && - ((md->ctypes[eptr[-1]] & ctype_word) != 0); - cur_is_word = (eptr < md->end_subject) && - ((md->ctypes[*eptr] & ctype_word) != 0); + /* Get status of previous character */ + + if (eptr == md->start_subject) prev_is_word = FALSE; else + { + if (eptr <= md->start_used_ptr) md->start_used_ptr = eptr - 1; +#ifdef SUPPORT_UCP + if (md->use_ucp) + { + c = eptr[-1]; + if (c == '_') prev_is_word = TRUE; else + { + int cat = UCD_CATEGORY(c); + prev_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif + prev_is_word = MAX_255(eptr[-1]) + && ((md->ctypes[eptr[-1]] & ctype_word) != 0); + } + + /* Get status of next character */ + + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + cur_is_word = FALSE; + } + else +#ifdef SUPPORT_UCP + if (md->use_ucp) + { + c = *eptr; + if (c == '_') cur_is_word = TRUE; else + { + int cat = UCD_CATEGORY(c); + cur_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif + cur_is_word = MAX_255(*eptr) + && ((md->ctypes[*eptr] & ctype_word) != 0); } /* Now see if the situation is what we want */ @@ -1502,16 +2464,35 @@ for (;;) } break; - /* Match a single character type; inline for speed */ + /* Match any single character type except newline; have to take care with + CRLF newlines and partial matching. */ case OP_ANY: - if ((ims & PCRE_DOTALL) == 0) + if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); + if (md->partial != 0 && + eptr + 1 >= md->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + RAWUCHARTEST(eptr) == NLBLOCK->nl[0]) { - if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); + md->hitend = TRUE; + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); + } + + /* Fall through */ + + /* Match any single character whatsoever. */ + + case OP_ALLANY: + if (eptr >= md->end_subject) /* DO NOT merge the eptr++ here; it must */ + { /* not be updated before SCHECK_PARTIAL. */ + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - if (eptr++ >= md->end_subject) RRETURN(MATCH_NOMATCH); - if (utf8) - while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; /* PaN: OK */ + eptr++; +#ifdef SUPPORT_UTF + if (utf) ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); +#endif ecode++; break; @@ -1519,15 +2500,24 @@ for (;;) any byte, even newline, independent of the setting of PCRE_DOTALL. */ case OP_ANYBYTE: - if (eptr++ >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) /* DO NOT merge the eptr++ here; it must */ + { /* not be updated before SCHECK_PARTIAL. */ + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + eptr++; ecode++; break; case OP_NOT_DIGIT: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); if ( -#ifdef SUPPORT_UTF8 +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) c < 256 && #endif (md->ctypes[c] & ctype_digit) != 0 @@ -1537,11 +2527,15 @@ for (;;) break; case OP_DIGIT: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); if ( -#ifdef SUPPORT_UTF8 - c >= 256 || +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) + c > 255 || #endif (md->ctypes[c] & ctype_digit) == 0 ) @@ -1550,10 +2544,14 @@ for (;;) break; case OP_NOT_WHITESPACE: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); if ( -#ifdef SUPPORT_UTF8 +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) c < 256 && #endif (md->ctypes[c] & ctype_space) != 0 @@ -1563,11 +2561,15 @@ for (;;) break; case OP_WHITESPACE: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); if ( -#ifdef SUPPORT_UTF8 - c >= 256 || +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) + c > 255 || #endif (md->ctypes[c] & ctype_space) == 0 ) @@ -1576,10 +2578,14 @@ for (;;) break; case OP_NOT_WORDCHAR: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); if ( -#ifdef SUPPORT_UTF8 +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) c < 256 && #endif (md->ctypes[c] & ctype_word) != 0 @@ -1589,11 +2595,15 @@ for (;;) break; case OP_WORDCHAR: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); if ( -#ifdef SUPPORT_UTF8 - c >= 256 || +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) + c > 255 || #endif (md->ctypes[c] & ctype_word) == 0 ) @@ -1602,23 +2612,34 @@ for (;;) break; case OP_ANYNL: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); switch(c) { default: RRETURN(MATCH_NOMATCH); - case 0x000d: - if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + + case CHAR_CR: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + } + else if (RAWUCHARTEST(eptr) == CHAR_LF) eptr++; break; - case 0x000a: + case CHAR_LF: break; - case 0x000b: - case 0x000c: - case 0x0085: + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#ifndef EBCDIC case 0x2028: case 0x2029: +#endif /* Not EBCDIC */ if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); break; } @@ -1626,97 +2647,61 @@ for (;;) break; case OP_NOT_HSPACE: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); switch(c) { + HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */ default: break; - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ - RRETURN(MATCH_NOMATCH); } ecode++; break; case OP_HSPACE: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); switch(c) { + HSPACE_CASES: break; /* Byte and multibyte cases */ default: RRETURN(MATCH_NOMATCH); - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ - break; } ecode++; break; case OP_NOT_VSPACE: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); switch(c) { + VSPACE_CASES: RRETURN(MATCH_NOMATCH); default: break; - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ - case 0x2028: /* LINE SEPARATOR */ - case 0x2029: /* PARAGRAPH SEPARATOR */ - RRETURN(MATCH_NOMATCH); } ecode++; break; case OP_VSPACE: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); switch(c) { + VSPACE_CASES: break; default: RRETURN(MATCH_NOMATCH); - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ - case 0x2028: /* LINE SEPARATOR */ - case 0x2029: /* PARAGRAPH SEPARATOR */ - break; } ecode++; break; @@ -1727,11 +2712,15 @@ for (;;) case OP_PROP: case OP_NOTPROP: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); { - int chartype, script; - int category = _erts_pcre_ucp_findprop(c, &chartype, &script); + const pcre_uint32 *cp; + const ucd_record *prop = GET_UCD(c); /* LOOP_COUNT: Warning, no CHK in this block! */ switch(ecode[1]) { @@ -1740,27 +2729,78 @@ for (;;) break; case PT_LAMP: - if ((chartype == ucp_Lu || - chartype == ucp_Ll || - chartype == ucp_Lt) == (op == OP_NOTPROP)) + if ((prop->chartype == ucp_Lu || + prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt) == (op == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); - break; + break; case PT_GC: - if ((ecode[2] != category) == (op == OP_PROP)) + if ((ecode[2] != PRIV(ucp_gentype)[prop->chartype]) == (op == OP_PROP)) RRETURN(MATCH_NOMATCH); break; case PT_PC: - if ((ecode[2] != chartype) == (op == OP_PROP)) + if ((ecode[2] != prop->chartype) == (op == OP_PROP)) RRETURN(MATCH_NOMATCH); break; case PT_SC: - if ((ecode[2] != script) == (op == OP_PROP)) + if ((ecode[2] != prop->script) == (op == OP_PROP)) + RRETURN(MATCH_NOMATCH); + break; + + /* These are specials */ + + case PT_ALNUM: + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N) == (op == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_SPACE: /* Perl space */ + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR) + == (op == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_PXSPACE: /* POSIX space */ + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR) + == (op == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; + case PT_WORD: + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE) == (op == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_CLIST: + cp = PRIV(ucd_caseless_sets) + ecode[2]; + for (;;) /* LOOP_COUNT: COST */ + { + if (c < *cp) + { if (op == OP_PROP) { RRETURN(MATCH_NOMATCH); } else break; } + if (c == *cp++) + { if (op == OP_PROP) break; else { RRETURN(MATCH_NOMATCH); } } + COST(1); + } + break; + + case PT_UCNC: + if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || + c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || + c >= 0xe000) == (op == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + /* This should never occur */ + default: RRETURN(PCRE_ERROR_INTERNAL); } @@ -1773,28 +2813,33 @@ for (;;) is in the binary; otherwise a compile-time error occurs. */ case OP_EXTUNI: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - GETCHARINCTEST(c, eptr); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + else { - int chartype, script; - int category = _erts_pcre_ucp_findprop(c, &chartype, &script); - if (category == ucp_M) RRETURN(MATCH_NOMATCH); - while (eptr < md->end_subject) /* PaN: OK */ +#ifndef ERLANG_INTEGRATION + int lgb, rgb; +#endif + GETCHARINCTEST(c, eptr); + lgb = UCD_GRAPHBREAK(c); + while (eptr < md->end_subject) /* LOOP_COUNT: CHK */ { int len = 1; - if (!utf8) c = *eptr; else - { - GETCHARLEN(c, eptr, len); - } - category = _erts_pcre_ucp_findprop(c, &chartype, &script); - if (category != ucp_M) break; + if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } + rgb = UCD_GRAPHBREAK(c); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + lgb = rgb; eptr += len; COST_CHK(1); } } + CHECK_PARTIAL(); ecode++; break; -#endif +#endif /* SUPPORT_UCP */ /* Match a back reference, possibly repeatedly. Look past the end of the @@ -1806,111 +2851,151 @@ for (;;) loops). */ case OP_REF: - { - offset = GET2(ecode, 1) << 1; /* Doubled ref number */ - ecode += 3; /* Advance past item */ + case OP_REFI: + caseless = op == OP_REFI; + offset = GET2(ecode, 1) << 1; /* Doubled ref number */ + ecode += 1 + IMM2_SIZE; - /* If the reference is unset, set the length to be longer than the amount - of subject left; this ensures that every attempt at a match fails. We - can't just fail here, because of the possibility of quantifiers with zero - minima. */ + /* If the reference is unset, there are two possibilities: - length = (offset >= offset_top || md->offset_vector[offset] < 0)? - md->end_subject - eptr + 1 : - md->offset_vector[offset+1] - md->offset_vector[offset]; + (a) In the default, Perl-compatible state, set the length negative; + this ensures that every attempt at a match fails. We can't just fail + here, because of the possibility of quantifiers with zero minima. - /* Set up for repetition, or handle the non-repeated case */ + (b) If the JavaScript compatibility flag is set, set the length to zero + so that the back reference matches an empty string. - switch (*ecode) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - c = *ecode++ - OP_CRSTAR; - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - break; + Otherwise, set the length to the length of what was matched by the + referenced subpattern. */ - case OP_CRRANGE: - case OP_CRMINRANGE: - minimize = (*ecode == OP_CRMINRANGE); - min = GET2(ecode, 1); - max = GET2(ecode, 3); - if (max == 0) max = INT_MAX; - ecode += 5; - break; + if (offset >= offset_top || md->offset_vector[offset] < 0) + length = (md->jscript_compat)? 0 : -1; + else + length = md->offset_vector[offset+1] - md->offset_vector[offset]; - default: /* No repeat follows */ - if (!match_ref(offset, eptr, length, md, ims)) RRETURN(MATCH_NOMATCH); - eptr += length; - continue; /* With the main loop */ - } + /* Set up for repetition, or handle the non-repeated case */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = GET2(ecode, 1); + max = GET2(ecode, 1 + IMM2_SIZE); + if (max == 0) max = INT_MAX; + ecode += 1 + 2 * IMM2_SIZE; + break; - /* If the length of the reference is zero, just continue with the - main loop. */ + default: /* No repeat follows */ + if ((length = match_ref(offset, eptr, length, md, caseless)) < 0) + { + if (length == -2) eptr = md->end_subject; /* Partial match */ + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + eptr += length; + continue; /* With the main loop */ + } - if (length == 0) continue; + /* Handle repeated back references. If the length of the reference is + zero, just continue with the main loop. If the length is negative, it + means the reference is unset in non-Java-compatible mode. If the minimum is + zero, we can continue at the same level without recursion. For any other + minimum, carrying on will result in NOMATCH. */ - /* First, ensure the minimum number of matches are present. We get back - the length of the reference string explicitly rather than passing the - address of eptr, so that eptr can be a register variable. */ + if (length == 0) continue; + if (length < 0 && min == 0) continue; - COST(min); - for (i = 1; i <= min; i++) + /* First, ensure the minimum number of matches are present. We get back + the length of the reference string explicitly rather than passing the + address of eptr, so that eptr can be a register variable. */ + COST(min); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ + { + int slength; + if ((slength = match_ref(offset, eptr, length, md, caseless)) < 0) { - if (!match_ref(offset, eptr, length, md, ims)) RRETURN(MATCH_NOMATCH); - eptr += length; + if (slength == -2) eptr = md->end_subject; /* Partial match */ + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } + eptr += slength; + } - /* If min = max, continue at the same level without recursion. - They are not both allowed to be zero. */ + /* If min = max, continue at the same level without recursion. + They are not both allowed to be zero. */ - if (min == max) continue; + if (min == max) continue; - /* If minimizing, keep trying and advancing the pointer */ + /* If minimizing, keep trying and advancing the pointer */ - if (minimize) + if (minimize) + { + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - for (fi = min;; fi++) /* PaN: OK */ + int slength; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM14); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if ((slength = match_ref(offset, eptr, length, md, caseless)) < 0) { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM14); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || !match_ref(offset, eptr, length, md, ims)) - RRETURN(MATCH_NOMATCH); - eptr += length; + if (slength == -2) eptr = md->end_subject; /* Partial match */ + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - /* Control never gets here */ + eptr += slength; } + /* Control never gets here */ + } - /* If maximizing, find the longest string and work backwards */ + /* If maximizing, find the longest string and work backwards */ - else + else + { + pp = eptr; + for (i = min; i < max; i++) { - pp = eptr; - for (i = min; i < max; i++) /* PaN: OK */ - { - if (!match_ref(offset, eptr, length, md, ims)) break; - eptr += length; - COST_CHK(1); - } - while (eptr >= pp) /* PaN: OK */ + int slength; + if ((slength = match_ref(offset, eptr, length, md, caseless)) < 0) /* LOOP_COUNT: CHK */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM15); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr -= length; + /* Can't use CHECK_PARTIAL because we don't want to update eptr in + the soft partial matching case. */ + + if (slength == -2 && md->partial != 0 && + md->end_subject > md->start_used_ptr) + { + md->hitend = TRUE; + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); + } + break; } - RRETURN(MATCH_NOMATCH); + eptr += slength; + COST_CHK(1); } + + while (eptr >= pp) /* LOOP_COUNT: Ok */ + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM15); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + eptr -= length; + } + RRETURN(MATCH_NOMATCH); } /* Control never gets here */ - - /* Match a bit-mapped character class, possibly repeatedly. This op code is used when all the characters in the class have values in the range 0-255, and either the matching is caseful, or the characters are in the range @@ -1925,10 +3010,13 @@ for (;;) case OP_NCLASS: case OP_CLASS: { + /* The data variable is saved across frames, so the byte map needs to + be stored there. */ +#define BYTE_MAP ((pcre_uint8 *)data) data = ecode + 1; /* Save for matching */ - ecode += 33; /* Advance past the item */ + ecode += 1 + (32 / sizeof(pcre_uchar)); /* Advance past the item */ #ifdef ERLANG_INTEGRATION - EDEBUGF(("OP_(N)CLASS (%d)...",*ecode)); + EDEBUGF(("OP_(N)CLASS (%d)...",*ecode)); #endif switch (*ecode) @@ -1950,9 +3038,9 @@ for (;;) case OP_CRMINRANGE: minimize = (*ecode == OP_CRMINRANGE); min = GET2(ecode, 1); - max = GET2(ecode, 3); + max = GET2(ecode, 1 + IMM2_SIZE); if (max == 0) max = INT_MAX; - ecode += 5; + ecode += 1 + 2 * IMM2_SIZE; break; default: /* No repeat follows */ @@ -1962,35 +3050,47 @@ for (;;) /* First, ensure the minimum number of matches are present. */ -#ifdef SUPPORT_UTF8 - /* UTF-8 mode */ - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINC(c, eptr); if (c > 255) { if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); } - else - { - if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); - } + else + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); } } else #endif - /* Not UTF-8 mode */ + /* Not UTF mode */ { COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } c = *eptr++; - if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); +#ifndef COMPILE_PCRE8 + if (c > 255) + { + if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else +#endif + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); } } @@ -2004,37 +3104,51 @@ for (;;) if (minimize) { -#ifdef SUPPORT_UTF8 - /* UTF-8 mode */ - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM16); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM16); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINC(c, eptr); if (c > 255) { if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); } else - { - if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); - } + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); } } else #endif - /* Not UTF-8 mode */ + /* Not UTF mode */ { - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM17); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM17); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } c = *eptr++; - if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); +#ifndef COMPILE_PCRE8 + if (c > 255) + { + if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else +#endif + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); } } /* Control never gets here */ @@ -2046,29 +3160,30 @@ for (;;) { pp = eptr; -#ifdef SUPPORT_UTF8 - /* UTF-8 mode */ - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { - for (i = min; i < max; i++) /* PaN: OK */ + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(c, eptr, len); if (c > 255) { if (op == OP_CLASS) break; } else - { - if ((data[c/8] & (1 << (c&7))) == 0) break; - } + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break; eptr += len; COST_CHK(1); } - for (;;) /* PaN: OK */ + for (;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM18); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM18); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (eptr-- == pp) break; /* Stop if tried at original pos */ BACKCHAR(eptr); @@ -2076,19 +3191,30 @@ for (;;) } else #endif - /* Not UTF-8 mode */ + /* Not UTF mode */ { - for (i = min; i < max; i++) /* PaN: OK */ + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } c = *eptr; - if ((data[c/8] & (1 << (c&7))) == 0) break; - eptr++; +#ifndef COMPILE_PCRE8 + if (c > 255) + { + if (op == OP_CLASS) break; + } + else +#endif + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break; COST_CHK(1); + eptr++; } - while (eptr >= pp) /* PaN: OK */ + while (eptr >= pp) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM19); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM19); if (rrc != MATCH_NOMATCH) RRETURN(rrc); eptr--; } @@ -2096,14 +3222,16 @@ for (;;) RRETURN(MATCH_NOMATCH); } +#undef BYTE_MAP } /* Control never gets here */ /* Match an extended character class. This opcode is encountered only - in UTF-8 mode, because that's the only time it is compiled. */ + when UTF-8 mode mode is supported. Nevertheless, we may not be in UTF-8 + mode, because Unicode properties are supported in non-UTF-8 mode. */ -#ifdef SUPPORT_UTF8 +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 case OP_XCLASS: { data = ecode + 1 + LINK_SIZE; /* Save for matching */ @@ -2128,9 +3256,9 @@ for (;;) case OP_CRMINRANGE: minimize = (*ecode == OP_CRMINRANGE); min = GET2(ecode, 1); - max = GET2(ecode, 3); + max = GET2(ecode, 1 + IMM2_SIZE); if (max == 0) max = INT_MAX; - ecode += 5; + ecode += 1 + 2 * IMM2_SIZE; break; default: /* No repeat follows */ @@ -2139,13 +3267,16 @@ for (;;) } /* First, ensure the minimum number of matches are present. */ - COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - GETCHARINC(c, eptr); - if (!_erts_pcre_xclass(c, data)) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if (!PRIV(xclass)(c, data, utf)) RRETURN(MATCH_NOMATCH); } /* If max == min we can continue with the main loop without the @@ -2158,13 +3289,18 @@ for (;;) if (minimize) { - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM20); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM20); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - GETCHARINC(c, eptr); - if (!_erts_pcre_xclass(c, data)) RRETURN(MATCH_NOMATCH); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if (!PRIV(xclass)(c, data, utf)) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ } @@ -2174,21 +3310,31 @@ for (;;) else { pp = eptr; - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; - GETCHARLEN(c, eptr, len); - if (!_erts_pcre_xclass(c, data)) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } +#ifdef SUPPORT_UTF + GETCHARLENTEST(c, eptr, len); +#else + c = *eptr; +#endif + if (!PRIV(xclass)(c, data, utf)) break; eptr += len; COST_CHK(1); } - for(;;) /* PaN: OK */ + for(;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM21); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM21); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (eptr-- == pp) break; /* Stop if tried at original pos */ - if (utf8) BACKCHAR(eptr); +#ifdef SUPPORT_UTF + if (utf) BACKCHAR(eptr); +#endif } RRETURN(MATCH_NOMATCH); } @@ -2200,52 +3346,71 @@ for (;;) /* Match a single character, casefully */ case OP_CHAR: -#ifdef SUPPORT_UTF8 - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { length = 1; ecode++; GETCHARLEN(fc, ecode, length); - if (length > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); - while (length-- > 0) if (*ecode++ != *eptr++) RRETURN(MATCH_NOMATCH); /* PaN: OK */ + if (length > md->end_subject - eptr) + { + CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */ + RRETURN(MATCH_NOMATCH); + } + while (length-- > 0) if (*ecode++ != RAWUCHARINC(eptr)) RRETURN(MATCH_NOMATCH); /* LOOP_COUNT: Ok */ } else #endif - - /* Non-UTF-8 mode */ + /* Not UTF mode */ { - if (md->end_subject - eptr < 1) RRETURN(MATCH_NOMATCH); + if (md->end_subject - eptr < 1) + { + SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */ + RRETURN(MATCH_NOMATCH); + } EDEBUGF(("code to match:%d, code is:%d",ecode[1],*eptr)); if (ecode[1] != *eptr++) RRETURN(MATCH_NOMATCH); ecode += 2; } break; - /* Match a single character, caselessly */ + /* Match a single character, caselessly. If we are at the end of the + subject, give up immediately. */ + + case OP_CHARI: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } - case OP_CHARNC: -#ifdef SUPPORT_UTF8 - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { length = 1; ecode++; GETCHARLEN(fc, ecode, length); - if (length > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); - /* If the pattern character's value is < 128, we have only one byte, and - can use the fast lookup table. */ + we know that its other case must also be one byte long, so we can use the + fast lookup table. We know that there is at least one byte left in the + subject. */ if (fc < 128) { - if (md->lcc[*ecode++] != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH); + pcre_uint32 cc = RAWUCHAR(eptr); + if (md->lcc[fc] != TABLE_GET(cc, md->lcc, cc)) RRETURN(MATCH_NOMATCH); + ecode++; + eptr++; } - /* Otherwise we must pick up the subject character */ + /* Otherwise we must pick up the subject character. Note that we cannot + use the value of "length" to check for sufficient bytes left, because the + other case of the character may have more or fewer bytes. */ else { - unsigned int dc; + pcre_uint32 dc; GETCHARINC(dc, eptr); ecode += length; @@ -2255,19 +3420,20 @@ for (;;) if (fc != dc) { #ifdef SUPPORT_UCP - if (dc != _erts_pcre_ucp_othercase(fc)) + if (dc != UCD_OTHERCASE(fc)) #endif RRETURN(MATCH_NOMATCH); } } } else -#endif /* SUPPORT_UTF8 */ +#endif /* SUPPORT_UTF */ - /* Non-UTF-8 mode */ + /* Not UTF mode */ { - if (md->end_subject - eptr < 1) RRETURN(MATCH_NOMATCH); - if (md->lcc[ecode[1]] != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH); + if (TABLE_GET(ecode[1], md->lcc, ecode[1]) + != TABLE_GET(*eptr, md->lcc, *eptr)) RRETURN(MATCH_NOMATCH); + eptr++; ecode += 2; } break; @@ -2275,23 +3441,28 @@ for (;;) /* Match a single character repeatedly. */ case OP_EXACT: + case OP_EXACTI: min = max = GET2(ecode, 1); - ecode += 3; + ecode += 1 + IMM2_SIZE; goto REPEATCHAR; case OP_POSUPTO: + case OP_POSUPTOI: possessive = TRUE; /* Fall through */ case OP_UPTO: + case OP_UPTOI: case OP_MINUPTO: + case OP_MINUPTOI: min = 0; max = GET2(ecode, 1); - minimize = *ecode == OP_MINUPTO; - ecode += 3; + minimize = *ecode == OP_MINUPTO || *ecode == OP_MINUPTOI; + ecode += 1 + IMM2_SIZE; goto REPEATCHAR; case OP_POSSTAR: + case OP_POSSTARI: possessive = TRUE; min = 0; max = INT_MAX; @@ -2299,6 +3470,7 @@ for (;;) goto REPEATCHAR; case OP_POSPLUS: + case OP_POSPLUSI: possessive = TRUE; min = 1; max = INT_MAX; @@ -2306,6 +3478,7 @@ for (;;) goto REPEATCHAR; case OP_POSQUERY: + case OP_POSQUERYI: possessive = TRUE; min = 0; max = 1; @@ -2313,29 +3486,47 @@ for (;;) goto REPEATCHAR; case OP_STAR: + case OP_STARI: case OP_MINSTAR: + case OP_MINSTARI: case OP_PLUS: + case OP_PLUSI: case OP_MINPLUS: + case OP_MINPLUSI: case OP_QUERY: + case OP_QUERYI: case OP_MINQUERY: - c = *ecode++ - OP_STAR; + case OP_MINQUERYI: + c = *ecode++ - ((op < OP_STARI)? OP_STAR : OP_STARI); minimize = (c & 1) != 0; min = rep_min[c]; /* Pick up values from tables; */ max = rep_max[c]; /* zero for max => infinity */ if (max == 0) max = INT_MAX; - /* Common code for all repeated single-character matches. We can give - up quickly if there are fewer than the minimum number of characters left in - the subject. */ + /* Common code for all repeated single-character matches. We first check + for the minimum number of characters. If the minimum equals the maximum, we + are done. Otherwise, if minimizing, check the rest of the pattern for a + match; if there isn't one, advance up to the maximum, one character at a + time. + + If maximizing, advance up to the maximum number of matching characters, + until eptr is past the end of the maximum run. If possessive, we are + then done (no backing up). Otherwise, match at this position; anything + other than no match is immediately returned. For nomatch, back up one + character, unless we are matching \R and the last thing matched was + \r\n, in which case, back up two bytes. When we reach the first optional + character position, we can save stack by doing a tail recurse. + + The various UTF/non-UTF and caseful/caseless cases are handled separately, + for speed. */ REPEATCHAR: -#ifdef SUPPORT_UTF8 - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { length = 1; charptr = ecode; GETCHARLEN(fc, ecode, length); - if (min * length > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); ecode += length; /* Handle multibyte character matching specially here. There is @@ -2344,50 +3535,50 @@ for (;;) if (length > 1) { #ifdef SUPPORT_UCP - unsigned int othercase; - if ((ims & PCRE_CASELESS) != 0 && - (othercase = _erts_pcre_ucp_othercase(fc)) != NOTACHAR) - oclength = _erts_pcre_ord2utf8(othercase, occhars); + pcre_uint32 othercase; + if (op >= OP_STARI && /* Caseless */ + (othercase = UCD_OTHERCASE(fc)) != fc) + oclength = PRIV(ord2utf)(othercase, occhars); else oclength = 0; #endif /* SUPPORT_UCP */ COST(min); - for (i = 1; i <= min; i++) /* PaN: Cost min (?) */ + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (memcmp(eptr, charptr, length) == 0) eptr += length; + if (eptr <= md->end_subject - length && + memcmp(eptr, charptr, IN_UCHARS(length)) == 0) eptr += length; #ifdef SUPPORT_UCP - /* Need braces because of following else */ - else if (oclength == 0) { RRETURN(MATCH_NOMATCH); } + else if (oclength > 0 && + eptr <= md->end_subject - oclength && + memcmp(eptr, occhars, IN_UCHARS(oclength)) == 0) eptr += oclength; +#endif /* SUPPORT_UCP */ else { - if (memcmp(eptr, occhars, oclength) != 0) RRETURN(MATCH_NOMATCH); - eptr += oclength; + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } -#else /* without SUPPORT_UCP */ - else { RRETURN(MATCH_NOMATCH); } -#endif /* SUPPORT_UCP */ } if (min == max) continue; if (minimize) { - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM22); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM22); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - if (memcmp(eptr, charptr, length) == 0) eptr += length; + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr <= md->end_subject - length && + memcmp(eptr, charptr, IN_UCHARS(length)) == 0) eptr += length; #ifdef SUPPORT_UCP - /* Need braces because of following else */ - else if (oclength == 0) { RRETURN(MATCH_NOMATCH); } + else if (oclength > 0 && + eptr <= md->end_subject - oclength && + memcmp(eptr, occhars, IN_UCHARS(oclength)) == 0) eptr += oclength; +#endif /* SUPPORT_UCP */ else { - if (memcmp(eptr, occhars, oclength) != 0) RRETURN(MATCH_NOMATCH); - eptr += oclength; + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } -#else /* without SUPPORT_UCP */ - else { RRETURN (MATCH_NOMATCH); } -#endif /* SUPPORT_UCP */ } /* Control never gets here */ } @@ -2395,36 +3586,36 @@ for (;;) else /* Maximize */ { pp = eptr; - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr > md->end_subject - length) break; - if (memcmp(eptr, charptr, length) == 0) eptr += length; + if (eptr <= md->end_subject - length && + memcmp(eptr, charptr, IN_UCHARS(length)) == 0) eptr += length; #ifdef SUPPORT_UCP - else if (oclength == 0) break; + else if (oclength > 0 && + eptr <= md->end_subject - oclength && + memcmp(eptr, occhars, IN_UCHARS(oclength)) == 0) eptr += oclength; +#endif /* SUPPORT_UCP */ else { - if (memcmp(eptr, occhars, oclength) != 0) break; - eptr += oclength; + CHECK_PARTIAL(); + break; } -#else /* without SUPPORT_UCP */ - else break; -#endif /* SUPPORT_UCP */ COST_CHK(1); } - if (possessive) continue; - for(;;) /* PaN: OK */ - { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM23); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr == pp) RRETURN(MATCH_NOMATCH); + if (possessive) continue; /* No backtracking */ + for(;;) /* LOOP_COUNT: Ok */ + { + if (eptr == pp) goto TAIL_RECURSE; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM23); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); #ifdef SUPPORT_UCP - eptr--; - BACKCHAR(eptr); + eptr--; + BACKCHAR(eptr); #else /* without SUPPORT_UCP */ - eptr -= length; + eptr -= length; #endif /* SUPPORT_UCP */ - } + } } /* Control never gets here */ } @@ -2434,16 +3625,12 @@ for (;;) value of fc will always be < 128. */ } else -#endif /* SUPPORT_UTF8 */ - - /* When not in UTF-8 mode, load a single-byte character. */ - { - if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); +#endif /* SUPPORT_UTF */ + /* When not in UTF-8 mode, load a single-byte character. */ fc = *ecode++; - } - /* The value of fc at this point is always less than 256, though we may or - may not be in UTF-8 mode. The code is duplicated for the caseless and + /* The value of fc at this point is always one character, though we may + or may not be in UTF mode. The code is duplicated for the caseless and caseful cases, for speed, since matching characters is likely to be quite common. First, ensure the minimum number of matches are present. If min = max, continue at the same level without recursing. Otherwise, if @@ -2452,40 +3639,82 @@ for (;;) maximizing, find the maximum number of characters and work backwards. */ DPRINTF(("matching %c{%d,%d} against subject %.*s\n", fc, min, max, - max, eptr)); + max, (char *)eptr)); - if ((ims & PCRE_CASELESS) != 0) + if (op >= OP_STARI) /* Caseless */ { - fc = md->lcc[fc]; - COST(min); - for (i = 1; i <= min; i++) - if (fc != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH); +#ifdef COMPILE_PCRE8 + /* fc must be < 128 if UTF is enabled. */ + foc = md->fcc[fc]; +#else +#ifdef SUPPORT_UTF +#ifdef SUPPORT_UCP + if (utf && fc > 127) + foc = UCD_OTHERCASE(fc); +#else + if (utf && fc > 127) + foc = fc; +#endif /* SUPPORT_UCP */ + else +#endif /* SUPPORT_UTF */ + foc = TABLE_GET(fc, md->fcc, fc); +#endif /* COMPILE_PCRE8 */ + + for (i = 1; i <= min; i++) /* LOOP_COUNT: CHK */ + { + pcre_uint32 cc; /* Faster than pcre_uchar */ + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = RAWUCHARTEST(eptr); + if (fc != cc && foc != cc) RRETURN(MATCH_NOMATCH); + eptr++; + COST_CHK(1); + } if (min == max) continue; if (minimize) { - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM24); + pcre_uint32 cc; /* Faster than pcre_uchar */ + RMATCH(eptr, ecode, offset_top, md, eptrb, RM24); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject || - fc != md->lcc[*eptr++]) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } + cc = RAWUCHARTEST(eptr); + if (fc != cc && foc != cc) RRETURN(MATCH_NOMATCH); + eptr++; } /* Control never gets here */ } else /* Maximize */ { pp = eptr; - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject || fc != md->lcc[*eptr]) break; + pcre_uint32 cc; /* Faster than pcre_uchar */ + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + cc = RAWUCHARTEST(eptr); + if (fc != cc && foc != cc) break; eptr++; COST_CHK(1); } - if (possessive) continue; - while (eptr >= pp) /* PaN: OK */ + + if (possessive) continue; /* No backtracking */ + for (;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM25); + if (eptr == pp) goto TAIL_RECURSE; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM25); eptr--; if (rrc != MATCH_NOMATCH) RRETURN(rrc); } @@ -2499,32 +3728,53 @@ for (;;) else { COST(min); - for (i = 1; i <= min; i++) if (fc != *eptr++) RRETURN(MATCH_NOMATCH); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (fc != RAWUCHARINCTEST(eptr)) RRETURN(MATCH_NOMATCH); + } + if (min == max) continue; + if (minimize) { - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM26); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM26); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject || fc != *eptr++) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } + if (fc != RAWUCHARINCTEST(eptr)) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ } else /* Maximize */ { pp = eptr; - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject || fc != *eptr) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (fc != RAWUCHARTEST(eptr)) break; eptr++; COST_CHK(1); } - if (possessive) continue; - while (eptr >= pp) /* PaN: OK */ + if (possessive) continue; /* No backtracking */ + for (;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM27); + if (eptr == pp) goto TAIL_RECURSE; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM27); eptr--; if (rrc != MATCH_NOMATCH) RRETURN(rrc); } @@ -2537,20 +3787,47 @@ for (;;) checking can be multibyte. */ case OP_NOT: - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - ecode++; - GETCHARINCTEST(c, eptr); - if ((ims & PCRE_CASELESS) != 0) + case OP_NOTI: + if (eptr >= md->end_subject) { -#ifdef SUPPORT_UTF8 - if (c < 256) -#endif - c = md->lcc[c]; - if (md->lcc[*ecode++] == c) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } +#ifdef SUPPORT_UTF + if (utf) + { + register pcre_uint32 ch, och; + + ecode++; + GETCHARINC(ch, ecode); + GETCHARINC(c, eptr); + + if (op == OP_NOT) + { + if (ch == c) RRETURN(MATCH_NOMATCH); + } + else + { +#ifdef SUPPORT_UCP + if (ch > 127) + och = UCD_OTHERCASE(ch); +#else + if (ch > 127) + och = ch; +#endif /* SUPPORT_UCP */ + else + och = TABLE_GET(ch, md->fcc, ch); + if (ch == c || och == c) RRETURN(MATCH_NOMATCH); + } } else +#endif { - if (*ecode++ == c) RRETURN(MATCH_NOMATCH); + register pcre_uint32 ch = ecode[1]; + c = *eptr++; + if (ch == c || (op == OP_NOTI && TABLE_GET(ch, md->fcc, ch) == c)) + RRETURN(MATCH_NOMATCH); + ecode += 2; } break; @@ -2562,19 +3839,23 @@ for (;;) about... */ case OP_NOTEXACT: + case OP_NOTEXACTI: min = max = GET2(ecode, 1); - ecode += 3; + ecode += 1 + IMM2_SIZE; goto REPEATNOTCHAR; case OP_NOTUPTO: + case OP_NOTUPTOI: case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: min = 0; max = GET2(ecode, 1); - minimize = *ecode == OP_NOTMINUPTO; - ecode += 3; + minimize = *ecode == OP_NOTMINUPTO || *ecode == OP_NOTMINUPTOI; + ecode += 1 + IMM2_SIZE; goto REPEATNOTCHAR; case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: possessive = TRUE; min = 0; max = INT_MAX; @@ -2582,6 +3863,7 @@ for (;;) goto REPEATNOTCHAR; case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: possessive = TRUE; min = 1; max = INT_MAX; @@ -2589,6 +3871,7 @@ for (;;) goto REPEATNOTCHAR; case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: possessive = TRUE; min = 0; max = 1; @@ -2596,31 +3879,35 @@ for (;;) goto REPEATNOTCHAR; case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: possessive = TRUE; min = 0; max = GET2(ecode, 1); - ecode += 3; + ecode += 1 + IMM2_SIZE; goto REPEATNOTCHAR; case OP_NOTSTAR: + case OP_NOTSTARI: case OP_NOTMINSTAR: + case OP_NOTMINSTARI: case OP_NOTPLUS: + case OP_NOTPLUSI: case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: case OP_NOTQUERY: + case OP_NOTQUERYI: case OP_NOTMINQUERY: - c = *ecode++ - OP_NOTSTAR; + case OP_NOTMINQUERYI: + c = *ecode++ - ((op >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR); minimize = (c & 1) != 0; min = rep_min[c]; /* Pick up values from tables; */ max = rep_max[c]; /* zero for max => infinity */ if (max == 0) max = INT_MAX; - /* Common code for all repeated single-byte matches. We can give up quickly - if there are fewer than the minimum number of bytes left in the - subject. */ + /* Common code for all repeated single-byte matches. */ REPEATNOTCHAR: - if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); - fc = *ecode++; + GETCHARINCTEST(fc, ecode); /* The code is duplicated for the caseless and caseful cases, for speed, since matching characters is likely to be quite common. First, ensure the @@ -2631,64 +3918,93 @@ for (;;) characters and work backwards. */ DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", fc, min, max, - max, eptr)); + max, (char *)eptr)); - if ((ims & PCRE_CASELESS) != 0) + if (op >= OP_NOTSTARI) /* Caseless */ { - fc = md->lcc[fc]; +#ifdef SUPPORT_UTF +#ifdef SUPPORT_UCP + if (utf && fc > 127) + foc = UCD_OTHERCASE(fc); +#else + if (utf && fc > 127) + foc = fc; +#endif /* SUPPORT_UCP */ + else +#endif /* SUPPORT_UTF */ + foc = TABLE_GET(fc, md->fcc, fc); -#ifdef SUPPORT_UTF8 - /* UTF-8 mode */ - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { - register unsigned int d; + register pcre_uint32 d; COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINC(d, eptr); - if (d < 256) d = md->lcc[d]; - if (fc == d) RRETURN(MATCH_NOMATCH); + if (fc == d || (unsigned int)foc == d) RRETURN(MATCH_NOMATCH); } } else -#endif - - /* Not UTF-8 mode */ +#endif /* SUPPORT_UTF */ + /* Not UTF mode */ { COST(min); - for (i = 1; i <= min; i++) - if (fc == md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (fc == *eptr || foc == *eptr) RRETURN(MATCH_NOMATCH); + eptr++; + } } if (min == max) continue; if (minimize) { -#ifdef SUPPORT_UTF8 - /* UTF-8 mode */ - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { - register unsigned int d; - for (fi = min;; fi++) /* PaN: OK */ + register pcre_uint32 d; + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM28); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM28); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - GETCHARINC(d, eptr); - if (d < 256) d = md->lcc[d]; - if (fi >= max || eptr >= md->end_subject || fc == d) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, eptr); + if (fc == d || (unsigned int)foc == d) RRETURN(MATCH_NOMATCH); } } else -#endif - /* Not UTF-8 mode */ +#endif /*SUPPORT_UTF */ + /* Not UTF mode */ { - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM29); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM29); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject || fc == md->lcc[*eptr++]) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } + if (fc == *eptr || foc == *eptr) RRETURN(MATCH_NOMATCH); + eptr++; } } /* Control never gets here */ @@ -2700,44 +4016,53 @@ for (;;) { pp = eptr; -#ifdef SUPPORT_UTF8 - /* UTF-8 mode */ - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { - register unsigned int d; - for (i = min; i < max; i++) + register pcre_uint32 d; + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(d, eptr, len); - if (d < 256) d = md->lcc[d]; - if (fc == d) break; + if (fc == d || (unsigned int)foc == d) break; eptr += len; - COST_CHK(1); + COST_CHK(1); /* 'd' is not alive */ } - if (possessive) continue; - for(;;) /* PaN: OK */ + if (possessive) continue; /* No backtracking */ + for(;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM30); + if (eptr == pp) goto TAIL_RECURSE; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM30); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr-- == pp) break; /* Stop if tried at original pos */ + eptr--; BACKCHAR(eptr); } } else -#endif - /* Not UTF-8 mode */ +#endif /* SUPPORT_UTF */ + /* Not UTF mode */ { - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject || fc == md->lcc[*eptr]) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (fc == *eptr || foc == *eptr) break; eptr++; COST_CHK(1); } - if (possessive) continue; - while (eptr >= pp) /* PaN: OK */ + if (possessive) continue; /* No backtracking */ + for (;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM31); + if (eptr == pp) goto TAIL_RECURSE; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM31); if (rrc != MATCH_NOMATCH) RRETURN(rrc); eptr--; } @@ -2752,55 +4077,75 @@ for (;;) else { -#ifdef SUPPORT_UTF8 - /* UTF-8 mode */ - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { - register unsigned int d; - COST(min); - for (i = 1; i <= min; i++) + register pcre_uint32 d; + for (i = 1; i <= min; i++) /* LOOP_COUNT: CHK */ { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINC(d, eptr); if (fc == d) RRETURN(MATCH_NOMATCH); + COST_CHK(1); } } else #endif - /* Not UTF-8 mode */ + /* Not UTF mode */ { COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: Ok */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } if (fc == *eptr++) RRETURN(MATCH_NOMATCH); + } } if (min == max) continue; if (minimize) { -#ifdef SUPPORT_UTF8 - /* UTF-8 mode */ - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { - register unsigned int d; - for (fi = min;; fi++) /* PaN: OK */ + register pcre_uint32 d; + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM32); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM32); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - GETCHARINC(d, eptr); - if (fi >= max || eptr >= md->end_subject || fc == d) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, eptr); + if (fc == d) RRETURN(MATCH_NOMATCH); } } else #endif - /* Not UTF-8 mode */ + /* Not UTF mode */ { - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM33); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM33); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject || fc == *eptr++) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } + if (fc == *eptr++) RRETURN(MATCH_NOMATCH); } } /* Control never gets here */ @@ -2812,43 +4157,53 @@ for (;;) { pp = eptr; -#ifdef SUPPORT_UTF8 - /* UTF-8 mode */ - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { - register unsigned int d; - COST(min); - for (i = min; i < max; i++) + register pcre_uint32 d; + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(d, eptr, len); if (fc == d) break; eptr += len; + COST_CHK(1); } - if (possessive) continue; - for(;;) /* PaN: OK */ + if (possessive) continue; /* No backtracking */ + for(;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM34); + if (eptr == pp) goto TAIL_RECURSE; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM34); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr-- == pp) break; /* Stop if tried at original pos */ + eptr--; BACKCHAR(eptr); } } else #endif - /* Not UTF-8 mode */ + /* Not UTF mode */ { - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject || fc == *eptr) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (fc == *eptr) break; eptr++; COST_CHK(1); } - if (possessive) continue; - while (eptr >= pp) /* PaN: OK */ + if (possessive) continue; /* No backtracking */ + for (;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM35); + if (eptr == pp) goto TAIL_RECURSE; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM35); if (rrc != MATCH_NOMATCH) RRETURN(rrc); eptr--; } @@ -2866,7 +4221,7 @@ for (;;) case OP_TYPEEXACT: min = max = GET2(ecode, 1); minimize = TRUE; - ecode += 3; + ecode += 1 + IMM2_SIZE; goto REPEATTYPE; case OP_TYPEUPTO: @@ -2874,7 +4229,7 @@ for (;;) min = 0; max = GET2(ecode, 1); minimize = *ecode == OP_TYPEMINUPTO; - ecode += 3; + ecode += 1 + IMM2_SIZE; goto REPEATTYPE; case OP_TYPEPOSSTAR: @@ -2902,7 +4257,7 @@ for (;;) possessive = TRUE; min = 0; max = GET2(ecode, 1); - ecode += 3; + ecode += 1 + IMM2_SIZE; goto REPEATTYPE; case OP_TYPESTAR: @@ -2936,13 +4291,10 @@ for (;;) /* First, ensure the minimum number of matches are present. Use inline code for maximizing the speed, and do the type test once at the start - (i.e. keep it out of the loop). Also we can test that there are at least - the minimum number of bytes before we start. This isn't as effective in - UTF-8 mode, but it does no harm. Separate the UTF-8 code completely as that + (i.e. keep it out of the loop). Separate the UTF-8 code completely as that is tidier. Also separate the UCP code, which can be the same for both UTF-8 and single-bytes. */ - if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); if (min > 0) { #ifdef SUPPORT_UCP @@ -2953,59 +4305,182 @@ for (;;) { case PT_ANY: if (prop_fail_result) RRETURN(MATCH_NOMATCH); - for (i = 1; i <= min; i++) /* PaN: OK (cost above) */ + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST (above) */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + } + break; + + case PT_LAMP: + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST (above) */ + { + int chartype; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + chartype = UCD_CHARTYPE(c); + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_GC: + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST (above) */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_PC: + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST (above) */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_SC: + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST (above) */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_ALNUM: /* LOOP_COUNT: COST (above) */ + for (i = 1; i <= min; i++) + { + int category; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + category = UCD_CATEGORY(c); + if ((category == ucp_L || category == ucp_N) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_SPACE: /* Perl space */ + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST (above) */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) + RRETURN(MATCH_NOMATCH); } break; - case PT_LAMP: - for (i = 1; i <= min; i++) /* PaN: OK (cost above) */ + case PT_PXSPACE: /* POSIX space */ + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST (above) */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_chartype == ucp_Lu || - prop_chartype == ucp_Ll || - prop_chartype == ucp_Lt) == prop_fail_result) + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_VT || c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) RRETURN(MATCH_NOMATCH); } break; - case PT_GC: - for (i = 1; i <= min; i++) /* PaN: OK (cost above) */ + case PT_WORD: + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST (above) */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + int category; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_category == prop_value) == prop_fail_result) + category = UCD_CATEGORY(c); + if ((category == ucp_L || category == ucp_N || c == CHAR_UNDERSCORE) + == prop_fail_result) RRETURN(MATCH_NOMATCH); } break; - case PT_PC: - for (i = 1; i <= min; i++) /* PaN: OK (cost above) */ + case PT_CLIST: + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST (above) */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - GETCHARINCTEST(c, eptr); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_chartype == prop_value) == prop_fail_result) + const pcre_uint32 *cp; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + cp = PRIV(ucd_caseless_sets) + prop_value; + for (;;) /* LOOP_COUNT: COST */ + { + if (c < *cp) + { if (prop_fail_result) break; else { RRETURN(MATCH_NOMATCH); } } + if (c == *cp++) + { if (prop_fail_result) { RRETURN(MATCH_NOMATCH); } else break; } + COST(1); + } } break; - case PT_SC: - for (i = 1; i <= min; i++) /* PaN: OK (cost above) */ + case PT_UCNC: + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST (above) */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINCTEST(c, eptr); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_script == prop_value) == prop_fail_result) + if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || + c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || + c >= 0xe000) == prop_fail_result) RRETURN(MATCH_NOMATCH); } break; + /* This should not occur */ + default: RRETURN(PCRE_ERROR_INTERNAL); } @@ -3014,26 +4489,35 @@ for (;;) /* Match extended Unicode sequences. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ - else if (ctype == OP_EXTUNI) + else if (ctype == OP_EXTUNI) { COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - GETCHARINCTEST(c, eptr); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if (prop_category == ucp_M) RRETURN(MATCH_NOMATCH); - while (eptr < md->end_subject) + if (eptr >= md->end_subject) { - int len = 1; - if (!utf8) c = *eptr; else + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + else + { +#ifndef ERLANG_INTEGRATION + int lgb, rgb; +#endif + GETCHARINCTEST(c, eptr); + lgb = UCD_GRAPHBREAK(c); + while (eptr < md->end_subject) /* LOOP_COUNT: CHK */ { - GETCHARLEN(c, eptr, len); + int len = 1; + if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } + rgb = UCD_GRAPHBREAK(c); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + lgb = rgb; + eptr += len; + COST_CHK(1); } - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if (prop_category != ucp_M) break; - eptr += len; - COST_CHK(1); } + CHECK_PARTIAL(); } } @@ -3042,46 +4526,80 @@ for (;;) /* Handle all other cases when the coding is UTF-8 */ -#ifdef SUPPORT_UTF8 - if (utf8) switch(ctype) +#ifdef SUPPORT_UTF + if (utf) switch(ctype) { case OP_ANY: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); + if (md->partial != 0 && + eptr + 1 >= md->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + RAWUCHAR(eptr) == NLBLOCK->nl[0]) + { + md->hitend = TRUE; + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); + } + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); + } + break; + + case OP_ALLANY: + COST(min); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject || - ((ims & PCRE_DOTALL) == 0 && IS_NEWLINE(eptr))) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } eptr++; - while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); } break; case OP_ANYBYTE: + if (eptr > md->end_subject - min) RRETURN(MATCH_NOMATCH); eptr += min; break; case OP_ANYNL: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINC(c, eptr); switch(c) { default: RRETURN(MATCH_NOMATCH); - case 0x000d: - if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + + case CHAR_CR: + if (eptr < md->end_subject && RAWUCHAR(eptr) == CHAR_LF) eptr++; break; - case 0x000a: + case CHAR_LF: break; - case 0x000b: - case 0x000c: - case 0x0085: + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#ifndef EBCDIC case 0x2028: case 0x2029: +#endif /* Not EBCDIC */ if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); break; } @@ -3090,117 +4608,85 @@ for (;;) case OP_NOT_HSPACE: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINC(c, eptr); switch(c) { + HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */ default: break; - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ - RRETURN(MATCH_NOMATCH); } } break; case OP_HSPACE: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINC(c, eptr); switch(c) { + HSPACE_CASES: break; /* Byte and multibyte cases */ default: RRETURN(MATCH_NOMATCH); - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ - break; } } break; case OP_NOT_VSPACE: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINC(c, eptr); switch(c) { + VSPACE_CASES: RRETURN(MATCH_NOMATCH); default: break; - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ - case 0x2028: /* LINE SEPARATOR */ - case 0x2029: /* PARAGRAPH SEPARATOR */ - RRETURN(MATCH_NOMATCH); } } break; case OP_VSPACE: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINC(c, eptr); switch(c) { + VSPACE_CASES: break; default: RRETURN(MATCH_NOMATCH); - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ - case 0x2028: /* LINE SEPARATOR */ - case 0x2029: /* PARAGRAPH SEPARATOR */ - break; } } break; case OP_NOT_DIGIT: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } GETCHARINC(c, eptr); if (c < 128 && (md->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); @@ -3209,55 +4695,90 @@ for (;;) case OP_DIGIT: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject || - *eptr >= 128 || (md->ctypes[*eptr++] & ctype_digit) == 0) + pcre_uint32 cc; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = RAWUCHAR(eptr); + if (cc >= 128 || (md->ctypes[cc] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); + eptr++; /* No need to skip more bytes - we know it's a 1-byte character */ } break; case OP_NOT_WHITESPACE: - COST(min); - for (i = 1; i <= min; i++) + COST(min); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject || - (*eptr < 128 && (md->ctypes[*eptr] & ctype_space) != 0)) + pcre_uint32 cc; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = RAWUCHAR(eptr); + if (cc < 128 && (md->ctypes[cc] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); - while (++eptr < md->end_subject && (*eptr & 0xc0) == 0x80); /* PaN: Check */ + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); } break; case OP_WHITESPACE: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject || - *eptr >= 128 || (md->ctypes[*eptr++] & ctype_space) == 0) + pcre_uint32 cc; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = RAWUCHAR(eptr); + if (cc >= 128 || (md->ctypes[cc] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); + eptr++; /* No need to skip more bytes - we know it's a 1-byte character */ } break; - case OP_NOT_WORDCHAR: + case OP_NOT_WORDCHAR: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject || - (*eptr < 128 && (md->ctypes[*eptr] & ctype_word) != 0)) + pcre_uint32 cc; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); - while (++eptr < md->end_subject && (*eptr & 0xc0) == 0x80); /* PaN: Check */ + } + cc = RAWUCHAR(eptr); + if (cc < 128 && (md->ctypes[cc] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); } break; case OP_WORDCHAR: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject || - *eptr >= 128 || (md->ctypes[*eptr++] & ctype_word) == 0) + pcre_uint32 cc; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } + cc = RAWUCHAR(eptr); + if (cc >= 128 || (md->ctypes[cc] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + eptr++; /* No need to skip more bytes - we know it's a 1-byte character */ } break; @@ -3267,51 +4788,81 @@ for (;;) } /* End switch(ctype) */ else -#endif /* SUPPORT_UTF8 */ +#endif /* SUPPORT_UTF */ /* Code for the non-UTF-8 case for minimum matching of operators other - than OP_PROP and OP_NOTPROP. We can assume that there are the minimum - number of bytes present, as this was tested above. */ + than OP_PROP and OP_NOTPROP. */ switch(ctype) { case OP_ANY: - if ((ims & PCRE_DOTALL) == 0) + COST(min); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - COST(min); - for (i = 1; i <= min; i++) + if (eptr >= md->end_subject) { - if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); - eptr++; + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); + if (md->partial != 0 && + eptr + 1 >= md->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + *eptr == NLBLOCK->nl[0]) + { + md->hitend = TRUE; + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); } + eptr++; } - else eptr += min; break; - case OP_ANYBYTE: + case OP_ALLANY: + if (eptr > md->end_subject - min) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } eptr += min; break; - /* Because of the CRLF case, we can't assume the minimum number of - bytes are present in this case. */ + case OP_ANYBYTE: + if (eptr > md->end_subject - min) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + eptr += min; + break; case OP_ANYNL: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } switch(*eptr++) { default: RRETURN(MATCH_NOMATCH); - case 0x000d: - if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + + case CHAR_CR: + if (eptr < md->end_subject && *eptr == CHAR_LF) eptr++; break; - case 0x000a: + + case CHAR_LF: break; - case 0x000b: - case 0x000c: - case 0x0085: + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + case 0x2028: + case 0x2029: +#endif if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); break; } @@ -3320,15 +4871,20 @@ for (;;) case OP_NOT_HSPACE: COST(min); - for (i = 1; i <= min; i++) /* PaN: Check */ + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } switch(*eptr++) { default: break; - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ + HSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + HSPACE_MULTIBYTE_CASES: +#endif RRETURN(MATCH_NOMATCH); } } @@ -3336,15 +4892,20 @@ for (;;) case OP_HSPACE: COST(min); - for (i = 1; i <= min; i++) /* PaN: Check */ + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } switch(*eptr++) { default: RRETURN(MATCH_NOMATCH); - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ + HSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + HSPACE_MULTIBYTE_CASES: +#endif break; } } @@ -3352,35 +4913,41 @@ for (;;) case OP_NOT_VSPACE: COST(min); - for (i = 1; i <= min; i++) /* PaN: Check */ + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } switch(*eptr++) { - default: break; - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ + VSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + VSPACE_MULTIBYTE_CASES: +#endif RRETURN(MATCH_NOMATCH); + default: break; } } break; case OP_VSPACE: COST(min); - for (i = 1; i <= min; i++) + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ { - if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } switch(*eptr++) { default: RRETURN(MATCH_NOMATCH); - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ + VSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + VSPACE_MULTIBYTE_CASES: +#endif break; } } @@ -3388,40 +4955,92 @@ for (;;) case OP_NOT_DIGIT: COST(min); - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + eptr++; + } break; case OP_DIGIT: - COST(min); - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); + COST(min); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + eptr++; + } break; case OP_NOT_WHITESPACE: - COST(min); - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); + COST(min); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + eptr++; + } break; case OP_WHITESPACE: - COST(min); - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); + COST(min); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + eptr++; + } break; case OP_NOT_WORDCHAR: - COST(min); - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_word) != 0) + COST(min); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + eptr++; + } break; case OP_WORDCHAR: - COST(min); - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_word) == 0) + COST(min); + for (i = 1; i <= min; i++) /* LOOP_COUNT: COST */ + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); + eptr++; + } break; default: @@ -3445,70 +5064,217 @@ for (;;) switch(prop_type) { case PT_ANY: - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM36); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM36); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - GETCHARINC(c, eptr); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); if (prop_fail_result) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ case PT_LAMP: - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM37); + int chartype; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM37); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - GETCHARINC(c, eptr); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_chartype == ucp_Lu || - prop_chartype == ucp_Ll || - prop_chartype == ucp_Lt) == prop_fail_result) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + chartype = UCD_CHARTYPE(c); + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_GC: + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM38); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_PC: + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM39); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_SC: + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM40); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_ALNUM: + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ + { + int category; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM59); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + category = UCD_CATEGORY(c); + if ((category == ucp_L || category == ucp_N) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_SPACE: /* Perl space */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM60); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_PXSPACE: /* POSIX space */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM61); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_VT || c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ - case PT_GC: - for (fi = min;; fi++) /* PaN: OK */ + case PT_WORD: + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM38); + int category; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM62); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - GETCHARINC(c, eptr); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_category == prop_value) == prop_fail_result) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + category = UCD_CATEGORY(c); + if ((category == ucp_L || + category == ucp_N || + c == CHAR_UNDERSCORE) + == prop_fail_result) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ - case PT_PC: - for (fi = min;; fi++) /* PaN: OK */ + case PT_CLIST: + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM39); + const pcre_uint32 *cp; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM67); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - GETCHARINC(c, eptr); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_chartype == prop_value) == prop_fail_result) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + cp = PRIV(ucd_caseless_sets) + prop_value; + for (;;) /* LOOP_COUNT: COST */ + { + if (c < *cp) + { if (prop_fail_result) break; else { RRETURN(MATCH_NOMATCH); } } + if (c == *cp++) + { if (prop_fail_result) { RRETURN(MATCH_NOMATCH); } else break; } + COST(1); + } } /* Control never gets here */ - case PT_SC: - for (fi = min;; fi++) /* PaN: OK */ + case PT_UCNC: + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM40); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM68); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - GETCHARINC(c, eptr); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_script == prop_value) == prop_fail_result) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || + c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || + c >= 0xe000) == prop_fail_result) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ + /* This should never occur */ default: RRETURN(PCRE_ERROR_INTERNAL); } @@ -3519,50 +5285,71 @@ for (;;) else if (ctype == OP_EXTUNI) { - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM41); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM41); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); - GETCHARINCTEST(c, eptr); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if (prop_category == ucp_M) RRETURN(MATCH_NOMATCH); - while (eptr < md->end_subject) /* PaN: Check */ + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) { - int len = 1; - if (!utf8) c = *eptr; else + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + else + { +#ifndef ERLANG_INTEGRATION + int lgb, rgb; +#endif + GETCHARINCTEST(c, eptr); + lgb = UCD_GRAPHBREAK(c); + while (eptr < md->end_subject) /* LOOP_COUNT: CHK */ { - GETCHARLEN(c, eptr, len); + int len = 1; + if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } + rgb = UCD_GRAPHBREAK(c); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + lgb = rgb; + eptr += len; + COST_CHK(1); } - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if (prop_category != ucp_M) break; - eptr += len; } + CHECK_PARTIAL(); } } - else #endif /* SUPPORT_UCP */ -#ifdef SUPPORT_UTF8 - /* UTF-8 mode */ - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM42); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM42); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject || - (ctype == OP_ANY && (ims & PCRE_DOTALL) == 0 && - IS_NEWLINE(eptr))) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (ctype == OP_ANY && IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); - GETCHARINC(c, eptr); switch(ctype) { - case OP_ANY: /* This is the DOTALL case */ + case OP_ANY: /* This is the non-NL case */ + if (md->partial != 0 && /* Take care with CRLF partial */ + eptr >= md->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + c == NLBLOCK->nl[0]) + { + md->hitend = TRUE; + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); + } break; + case OP_ALLANY: case OP_ANYBYTE: break; @@ -3570,17 +5357,20 @@ for (;;) switch(c) { default: RRETURN(MATCH_NOMATCH); - case 0x000d: - if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + case CHAR_CR: + if (eptr < md->end_subject && RAWUCHAR(eptr) == CHAR_LF) eptr++; break; - case 0x000a: + + case CHAR_LF: break; - case 0x000b: - case 0x000c: - case 0x0085: + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#ifndef EBCDIC case 0x2028: case 0x2029: +#endif /* Not EBCDIC */ if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); break; } @@ -3589,84 +5379,32 @@ for (;;) case OP_NOT_HSPACE: switch(c) { + HSPACE_CASES: RRETURN(MATCH_NOMATCH); default: break; - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ - RRETURN(MATCH_NOMATCH); } break; case OP_HSPACE: switch(c) { + HSPACE_CASES: break; default: RRETURN(MATCH_NOMATCH); - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ - break; } break; case OP_NOT_VSPACE: switch(c) { + VSPACE_CASES: RRETURN(MATCH_NOMATCH); default: break; - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ - case 0x2028: /* LINE SEPARATOR */ - case 0x2029: /* PARAGRAPH SEPARATOR */ - RRETURN(MATCH_NOMATCH); } break; case OP_VSPACE: switch(c) { + VSPACE_CASES: break; default: RRETURN(MATCH_NOMATCH); - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ - case 0x2028: /* LINE SEPARATOR */ - case 0x2029: /* PARAGRAPH SEPARATOR */ - break; } break; @@ -3686,7 +5424,7 @@ for (;;) break; case OP_WHITESPACE: - if (c >= 256 || (md->ctypes[c] & ctype_space) == 0) + if (c >= 256 || (md->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); break; @@ -3707,22 +5445,36 @@ for (;;) } else #endif - /* Not UTF-8 mode */ + /* Not UTF mode */ { - for (fi = min;; fi++) /* PaN: OK */ + for (fi = min;; fi++) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM43); + RMATCH(eptr, ecode, offset_top, md, eptrb, RM43); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max || eptr >= md->end_subject || - ((ims & PCRE_DOTALL) == 0 && IS_NEWLINE(eptr))) + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (ctype == OP_ANY && IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); - c = *eptr++; switch(ctype) { - case OP_ANY: /* This is the DOTALL case */ + case OP_ANY: /* This is the non-NL case */ + if (md->partial != 0 && /* Take care with CRLF partial */ + eptr >= md->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + c == NLBLOCK->nl[0]) + { + md->hitend = TRUE; + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); + } break; + case OP_ALLANY: case OP_ANYBYTE: break; @@ -3730,16 +5482,20 @@ for (;;) switch(c) { default: RRETURN(MATCH_NOMATCH); - case 0x000d: - if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + case CHAR_CR: + if (eptr < md->end_subject && *eptr == CHAR_LF) eptr++; break; - case 0x000a: + case CHAR_LF: break; - case 0x000b: - case 0x000c: - case 0x0085: + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + case 0x2028: + case 0x2029: +#endif if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); break; } @@ -3749,9 +5505,10 @@ for (;;) switch(c) { default: break; - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ + HSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + HSPACE_MULTIBYTE_CASES: +#endif RRETURN(MATCH_NOMATCH); } break; @@ -3760,9 +5517,10 @@ for (;;) switch(c) { default: RRETURN(MATCH_NOMATCH); - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ + HSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + HSPACE_MULTIBYTE_CASES: +#endif break; } break; @@ -3771,11 +5529,10 @@ for (;;) switch(c) { default: break; - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ + VSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + VSPACE_MULTIBYTE_CASES: +#endif RRETURN(MATCH_NOMATCH); } break; @@ -3784,37 +5541,36 @@ for (;;) switch(c) { default: RRETURN(MATCH_NOMATCH); - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ + VSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + VSPACE_MULTIBYTE_CASES: +#endif break; } break; case OP_NOT_DIGIT: - if ((md->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); + if (MAX_255(c) && (md->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); break; case OP_DIGIT: - if ((md->ctypes[c] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); + if (!MAX_255(c) || (md->ctypes[c] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); break; case OP_NOT_WHITESPACE: - if ((md->ctypes[c] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); + if (MAX_255(c) && (md->ctypes[c] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); break; case OP_WHITESPACE: - if ((md->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); + if (!MAX_255(c) || (md->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); break; case OP_NOT_WORDCHAR: - if ((md->ctypes[c] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); + if (MAX_255(c) && (md->ctypes[c] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); break; case OP_WORDCHAR: - if ((md->ctypes[c] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); + if (!MAX_255(c) || (md->ctypes[c] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); break; default: @@ -3839,11 +5595,15 @@ for (;;) switch(prop_type) { case PT_ANY: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; - GETCHARLEN(c, eptr, len); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); if (prop_fail_result) break; eptr+= len; COST_CHK(1); @@ -3851,15 +5611,20 @@ for (;;) break; case PT_LAMP: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { + int chartype; int len = 1; - if (eptr >= md->end_subject) break; - GETCHARLEN(c, eptr, len); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_chartype == ucp_Lu || - prop_chartype == ucp_Ll || - prop_chartype == ucp_Lt) == prop_fail_result) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + chartype = UCD_CHARTYPE(c); + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == prop_fail_result) break; eptr+= len; COST_CHK(1); @@ -3867,57 +5632,189 @@ for (;;) break; case PT_GC: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; - GETCHARLEN(c, eptr, len); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_category == prop_value) == prop_fail_result) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); break; + } + GETCHARLENTEST(c, eptr, len); + if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) break; eptr+= len; COST_CHK(1); } break; case PT_PC: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; - GETCHARLEN(c, eptr, len); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_chartype == prop_value) == prop_fail_result) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); break; + } + GETCHARLENTEST(c, eptr, len); + if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) break; eptr+= len; COST_CHK(1); } break; case PT_SC: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; - GETCHARLEN(c, eptr, len); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if ((prop_script == prop_value) == prop_fail_result) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) break; + eptr+= len; + COST_CHK(1); + } + break; + + case PT_ALNUM: + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ + { + int category; + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + category = UCD_CATEGORY(c); + if ((category == ucp_L || category == ucp_N) == prop_fail_result) + break; + eptr+= len; + COST_CHK(1); + } + break; + + case PT_SPACE: /* Perl space */ + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) + break; + eptr+= len; + COST_CHK(1); + } + break; + + case PT_PXSPACE: /* POSIX space */ + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_VT || c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) + break; + eptr+= len; + COST_CHK(1); + } + break; + + case PT_WORD: + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ + { + int category; + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + category = UCD_CATEGORY(c); + if ((category == ucp_L || category == ucp_N || + c == CHAR_UNDERSCORE) == prop_fail_result) break; eptr+= len; COST_CHK(1); } break; + + case PT_CLIST: + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ + { + const pcre_uint32 *cp; + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + cp = PRIV(ucd_caseless_sets) + prop_value; + for (;;) /* LOOP_COUNT: COST */ + { + if (c < *cp) + { if (prop_fail_result) break; else goto GOT_MAX; } + if (c == *cp++) + { if (prop_fail_result) goto GOT_MAX; else break; } + COST(1); + } + eptr += len; + COST_CHK(1); + } + GOT_MAX: + break; + + case PT_UCNC: + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || + c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || + c >= 0xe000) == prop_fail_result) + break; + eptr += len; + COST_CHK(1); + } + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); } /* eptr is now past the end of the maximum run */ - if (possessive) continue; - for(;;) /* PaN: OK */ + if (possessive) continue; /* No backtracking */ + for(;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM44); + if (eptr == pp) goto TAIL_RECURSE; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM44); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr-- == pp) break; /* Stop if tried at original pos */ - if (utf8) BACKCHAR(eptr); + eptr--; + if (utf) BACKCHAR(eptr); } } @@ -3926,46 +5823,54 @@ for (;;) else if (ctype == OP_EXTUNI) { - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject) break; - GETCHARINCTEST(c, eptr); - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if (prop_category == ucp_M) break; - while (eptr < md->end_subject) + if (eptr >= md->end_subject) { - int len = 1; - if (!utf8) c = *eptr; else + SCHECK_PARTIAL(); + break; + } + else + { +#ifndef ERLANG_INTEGRATION + int lgb, rgb; +#endif + GETCHARINCTEST(c, eptr); + lgb = UCD_GRAPHBREAK(c); + while (eptr < md->end_subject) /* LOOP_COUNT: CHK */ { - GETCHARLEN(c, eptr, len); + int len = 1; + if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } + rgb = UCD_GRAPHBREAK(c); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + lgb = rgb; + eptr += len; + COST_CHK(1); } - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if (prop_category != ucp_M) break; - eptr += len; COST_CHK(1); } - COST_CHK(1); + CHECK_PARTIAL(); } /* eptr is now past the end of the maximum run */ - if (possessive) continue; - for(;;) /* PaN: OK */ + if (possessive) continue; /* No backtracking */ + for(;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM45); + if (eptr == pp) goto TAIL_RECURSE; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM45); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr-- == pp) break; /* Stop if tried at original pos */ - for (;;) /* Move back over one extended */ /* PaN: Check */ + eptr--; + for (;;) /* Move back over one extended */ /* LOOP_COUNT: COST */ { - int len = 1; - if (!utf8) c = *eptr; else + if (!utf) c = *eptr; else { BACKCHAR(eptr); - GETCHARLEN(c, eptr, len); + GETCHAR(c, eptr); } - prop_category = _erts_pcre_ucp_findprop(c, &prop_chartype, &prop_script); - if (prop_category != ucp_M) break; + if (UCD_CATEGORY(c) != ucp_M) break; eptr--; + COST(1); } } } @@ -3973,35 +5878,34 @@ for (;;) else #endif /* SUPPORT_UCP */ -#ifdef SUPPORT_UTF8 - /* UTF-8 mode */ - - if (utf8) +#ifdef SUPPORT_UTF + if (utf) { switch(ctype) { case OP_ANY: if (max < INT_MAX) { - if ((ims & PCRE_DOTALL) == 0) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - for (i = min; i < max; i++) + if (eptr >= md->end_subject) { - if (eptr >= md->end_subject || IS_NEWLINE(eptr)) break; - eptr++; - while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; - COST_CHK(1); + SCHECK_PARTIAL(); + break; } - } - else - { - for (i = min; i < max; i++) + if (IS_NEWLINE(eptr)) break; + if (md->partial != 0 && /* Take care with CRLF partial */ + eptr + 1 >= md->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + RAWUCHAR(eptr) == NLBLOCK->nl[0]) { - if (eptr >= md->end_subject) break; - eptr++; - while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; /* PaN: Check */ - COST_CHK(1); + md->hitend = TRUE; + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); } + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); + COST_CHK(1); } } @@ -4009,21 +5913,50 @@ for (;;) else { - if ((ims & PCRE_DOTALL) == 0) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - for (i = min; i < max; i++) + if (eptr >= md->end_subject) { - if (eptr >= md->end_subject || IS_NEWLINE(eptr)) break; - eptr++; - while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; - COST_CHK(1); + SCHECK_PARTIAL(); + break; + } + if (IS_NEWLINE(eptr)) break; + if (md->partial != 0 && /* Take care with CRLF partial */ + eptr + 1 >= md->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + RAWUCHAR(eptr) == NLBLOCK->nl[0]) + { + md->hitend = TRUE; + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); } + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); + COST_CHK(1); } - else + } + break; + + case OP_ALLANY: + if (max < INT_MAX) + { + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - eptr = md->end_subject; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); + COST_CHK(1); } } + else + { + eptr = md->end_subject; /* Unlimited UTF-8 repeat */ + SCHECK_PARTIAL(); + } break; /* The byte case is the same as non-UTF8 */ @@ -4031,27 +5964,37 @@ for (;;) case OP_ANYBYTE: c = max - min; if (c > (unsigned int)(md->end_subject - eptr)) - c = md->end_subject - eptr; - eptr += c; + { + eptr = md->end_subject; + SCHECK_PARTIAL(); + } + else eptr += c; break; case OP_ANYNL: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(c, eptr, len); - if (c == 0x000d) + if (c == CHAR_CR) { if (++eptr >= md->end_subject) break; - if (*eptr == 0x000a) eptr++; + if (RAWUCHAR(eptr) == CHAR_LF) eptr++; } else { - if (c != 0x000a && + if (c != CHAR_LF && (md->bsr_anycrlf || - (c != 0x000b && c != 0x000c && - c != 0x0085 && c != 0x2028 && c != 0x2029))) + (c != CHAR_VT && c != CHAR_FF && c != CHAR_NEL +#ifndef EBCDIC + && c != 0x2028 && c != 0x2029 +#endif /* Not EBCDIC */ + ))) break; eptr += len; } @@ -4061,36 +6004,20 @@ for (;;) case OP_NOT_HSPACE: case OP_HSPACE: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { BOOL gotspace; int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(c, eptr, len); switch(c) { + HSPACE_CASES: gotspace = TRUE; break; default: gotspace = FALSE; break; - case 0x09: /* HT */ - case 0x20: /* SPACE */ - case 0xa0: /* NBSP */ - case 0x1680: /* OGHAM SPACE MARK */ - case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ - case 0x2000: /* EN QUAD */ - case 0x2001: /* EM QUAD */ - case 0x2002: /* EN SPACE */ - case 0x2003: /* EM SPACE */ - case 0x2004: /* THREE-PER-EM SPACE */ - case 0x2005: /* FOUR-PER-EM SPACE */ - case 0x2006: /* SIX-PER-EM SPACE */ - case 0x2007: /* FIGURE SPACE */ - case 0x2008: /* PUNCTUATION SPACE */ - case 0x2009: /* THIN SPACE */ - case 0x200A: /* HAIR SPACE */ - case 0x202f: /* NARROW NO-BREAK SPACE */ - case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ - case 0x3000: /* IDEOGRAPHIC SPACE */ - gotspace = TRUE; - break; } if (gotspace == (ctype == OP_NOT_HSPACE)) break; eptr += len; @@ -4100,24 +6027,20 @@ for (;;) case OP_NOT_VSPACE: case OP_VSPACE: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { BOOL gotspace; int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(c, eptr, len); switch(c) { + VSPACE_CASES: gotspace = TRUE; break; default: gotspace = FALSE; break; - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ - case 0x2028: /* LINE SEPARATOR */ - case 0x2029: /* PARAGRAPH SEPARATOR */ - gotspace = TRUE; - break; } if (gotspace == (ctype == OP_NOT_VSPACE)) break; eptr += len; @@ -4126,10 +6049,14 @@ for (;;) break; case OP_NOT_DIGIT: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(c, eptr, len); if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) break; eptr+= len; @@ -4138,10 +6065,14 @@ for (;;) break; case OP_DIGIT: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(c, eptr, len); if (c >= 256 ||(md->ctypes[c] & ctype_digit) == 0) break; eptr+= len; @@ -4150,10 +6081,14 @@ for (;;) break; case OP_NOT_WHITESPACE: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(c, eptr, len); if (c < 256 && (md->ctypes[c] & ctype_space) != 0) break; eptr+= len; @@ -4162,10 +6097,14 @@ for (;;) break; case OP_WHITESPACE: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(c, eptr, len); if (c >= 256 ||(md->ctypes[c] & ctype_space) == 0) break; eptr+= len; @@ -4174,10 +6113,14 @@ for (;;) break; case OP_NOT_WORDCHAR: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(c, eptr, len); if (c < 256 && (md->ctypes[c] & ctype_word) != 0) break; eptr+= len; @@ -4186,10 +6129,14 @@ for (;;) break; case OP_WORDCHAR: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { int len = 1; - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } GETCHARLEN(c, eptr, len); if (c >= 256 || (md->ctypes[c] & ctype_word) == 0) break; eptr+= len; @@ -4201,60 +6148,80 @@ for (;;) RRETURN(PCRE_ERROR_INTERNAL); } - /* eptr is now past the end of the maximum run */ - - if (possessive) continue; - for(;;) /* PaN: OK */ + if (possessive) continue; /* No backtracking */ + for(;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM46); + if (eptr == pp) goto TAIL_RECURSE; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM46); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr-- == pp) break; /* Stop if tried at original pos */ + eptr--; BACKCHAR(eptr); + if (ctype == OP_ANYNL && eptr > pp && RAWUCHAR(eptr) == CHAR_NL && + RAWUCHAR(eptr - 1) == CHAR_CR) eptr--; } } else -#endif /* SUPPORT_UTF8 */ - - /* Not UTF-8 mode */ +#endif /* SUPPORT_UTF */ + /* Not UTF mode */ { switch(ctype) { case OP_ANY: - if ((ims & PCRE_DOTALL) == 0) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - for (i = min; i < max; i++) + if (eptr >= md->end_subject) { - if (eptr >= md->end_subject || IS_NEWLINE(eptr)) break; - eptr++; - COST_CHK(1); + SCHECK_PARTIAL(); + break; } - break; + if (IS_NEWLINE(eptr)) break; + if (md->partial != 0 && /* Take care with CRLF partial */ + eptr + 1 >= md->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + *eptr == NLBLOCK->nl[0]) + { + md->hitend = TRUE; + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); + } + eptr++; + COST_CHK(1); } - /* For DOTALL case, fall through and treat as \C */ + break; + case OP_ALLANY: case OP_ANYBYTE: c = max - min; if (c > (unsigned int)(md->end_subject - eptr)) - c = md->end_subject - eptr; - eptr += c; + { + eptr = md->end_subject; + SCHECK_PARTIAL(); + } + else eptr += c; break; case OP_ANYNL: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject) break; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } c = *eptr; - if (c == 0x000d) + if (c == CHAR_CR) { if (++eptr >= md->end_subject) break; - if (*eptr == 0x000a) eptr++; + if (*eptr == CHAR_LF) eptr++; } else { - if (c != 0x000a && - (md->bsr_anycrlf || - (c != 0x000b && c != 0x000c && c != 0x0085))) - break; + if (c != CHAR_LF && (md->bsr_anycrlf || + (c != CHAR_VT && c != CHAR_FF && c != CHAR_NEL +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + && c != 0x2028 && c != 0x2029 +#endif + ))) break; eptr++; } COST_CHK(1); @@ -4262,106 +6229,172 @@ for (;;) break; case OP_NOT_HSPACE: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject) break; - c = *eptr; - if (c == 0x09 || c == 0x20 || c == 0xa0) break; - eptr++; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + switch(*eptr) + { + default: eptr++; break; + HSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + HSPACE_MULTIBYTE_CASES: +#endif + goto ENDLOOP00; + } COST_CHK(1); } + ENDLOOP00: break; case OP_HSPACE: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject) break; - c = *eptr; - if (c != 0x09 && c != 0x20 && c != 0xa0) break; - eptr++; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + switch(*eptr) + { + default: goto ENDLOOP01; + HSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + HSPACE_MULTIBYTE_CASES: +#endif + eptr++; break; + } COST_CHK(1); } + ENDLOOP01: break; case OP_NOT_VSPACE: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject) break; - c = *eptr; - if (c == 0x0a || c == 0x0b || c == 0x0c || c == 0x0d || c == 0x85) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); break; - eptr++; + } + switch(*eptr) + { + default: eptr++; break; + VSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + VSPACE_MULTIBYTE_CASES: +#endif + goto ENDLOOP02; + } COST_CHK(1); } + ENDLOOP02: break; case OP_VSPACE: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject) break; - c = *eptr; - if (c != 0x0a && c != 0x0b && c != 0x0c && c != 0x0d && c != 0x85) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); break; - eptr++; + } + switch(*eptr) + { + default: goto ENDLOOP03; + VSPACE_BYTE_CASES: +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + VSPACE_MULTIBYTE_CASES: +#endif + eptr++; break; + } COST_CHK(1); } + ENDLOOP03: break; case OP_NOT_DIGIT: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); break; + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_digit) != 0) break; eptr++; COST_CHK(1); } break; case OP_DIGIT: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); break; + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_digit) == 0) break; eptr++; COST_CHK(1); } break; case OP_NOT_WHITESPACE: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); break; + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_space) != 0) break; eptr++; COST_CHK(1); } break; case OP_WHITESPACE: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); break; + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_space) == 0) break; eptr++; COST_CHK(1); } break; case OP_NOT_WORDCHAR: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); break; + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_word) != 0) break; eptr++; COST_CHK(1); } break; case OP_WORDCHAR: - for (i = min; i < max; i++) + for (i = min; i < max; i++) /* LOOP_COUNT: CHK */ { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0) + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); break; + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_word) == 0) break; eptr++; COST_CHK(1); } @@ -4371,14 +6404,15 @@ for (;;) RRETURN(PCRE_ERROR_INTERNAL); } - /* eptr is now past the end of the maximum run */ - - if (possessive) continue; - while (eptr >= pp) /* PaN: OK */ + if (possessive) continue; /* No backtracking */ + for (;;) /* LOOP_COUNT: Ok */ { - RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM47); - eptr--; + if (eptr == pp) goto TAIL_RECURSE; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM47); if (rrc != MATCH_NOMATCH) RRETURN(rrc); + eptr--; + if (ctype == OP_ANYNL && eptr > pp && *eptr == CHAR_LF && + eptr[-1] == CHAR_CR) eptr--; } } @@ -4417,21 +6451,25 @@ switch (frame->Xwhere) LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(17) LBL(19) LBL(24) LBL(25) LBL(26) LBL(27) LBL(29) LBL(31) LBL(33) LBL(35) LBL(43) LBL(47) LBL(48) LBL(49) LBL(50) LBL(51) LBL(52) - LBL(53) LBL(54) -#ifdef SUPPORT_UTF8 - LBL(16) LBL(18) LBL(20) LBL(21) LBL(22) LBL(23) LBL(28) LBL(30) + LBL(53) LBL(54) LBL(55) LBL(56) LBL(57) LBL(58) LBL(63) LBL(64) + LBL(65) LBL(66) +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + LBL(21) +#endif +#ifdef SUPPORT_UTF + LBL(16) LBL(18) LBL(20) + LBL(22) LBL(23) LBL(28) LBL(30) LBL(32) LBL(34) LBL(42) LBL(46) #ifdef SUPPORT_UCP LBL(36) LBL(37) LBL(38) LBL(39) LBL(40) LBL(41) LBL(44) LBL(45) + LBL(59) LBL(60) LBL(61) LBL(62) LBL(67) LBL(68) #endif /* SUPPORT_UCP */ -#endif /* SUPPORT_UTF8 */ +#endif /* SUPPORT_UTF */ default: DPRINTF(("jump error in pcre match: label %d non-existent\n", frame->Xwhere)); return PCRE_ERROR_INTERNAL; } #undef LBL - - #ifdef ERLANG_INTEGRATION LOOP_COUNT_RETURN: /* Restore the saved register variables in the upper dummy frame, description below */ @@ -4440,11 +6478,14 @@ LOOP_COUNT_RETURN: frame = newframe->Xprevframe; rrc = newframe->Xop; i = newframe->Xfi; - c = newframe->Xfc; - utf8 = newframe->Xcur_is_word; + c = (pcre_uint32) newframe->Xfc; + utf = newframe->Xcur_is_word; minimize = newframe->Xcondition; possessive = newframe->Xprev_is_word; - (erts_pcre_stack_free)(newframe); + caseless = (BOOL) newframe->Xcodelink; + condcode = newframe->Xctype; + /* Note, the frame is not freed until the whole match is done, + the function release_match_heapframes takes care of that */ EDEBUGF(("LOOP_COUNT_RETURN: %d",frame->Xwhere)); switch (frame->Xwhere) { @@ -4463,40 +6504,40 @@ LOOP_COUNT_BREAK: * ------------------------------ -------------- * rrc Xop * i Xfi - * c Xfc - * utf8 Xcur_is_word + * c Xfc (cast) + * utf Xcur_is_word * minimize Xcondition * possessive Xprev_is_word + * caseless Xcodelink (cast) + * condcode Xctype */ { - heapframe *newframe = (erts_pcre_stack_malloc)(sizeof(heapframe)); + heapframe *newframe = frame->Xnextframe; + if (newframe == NULL) + { + newframe = (heapframe *)(PUBL(stack_malloc))(sizeof(heapframe)); + if (newframe == NULL) RRETURN(PCRE_ERROR_NOMEMORY); + newframe->Xnextframe = NULL; + frame->Xnextframe = newframe; + } newframe->Xprevframe = frame; newframe->Xop = rrc; newframe->Xfi = i; - newframe->Xfc = c; - newframe->Xcur_is_word = utf8; + newframe->Xfc = (unsigned int) c; + newframe->Xcur_is_word = utf; newframe->Xcondition = minimize; newframe->Xprev_is_word = possessive; + newframe->Xcodelink = (int) caseless; + newframe->Xctype = condcode; md->state_save = newframe; md->loop_limit = 0; EDEBUGF(("Break loop!")); return PCRE_ERROR_LOOP_LIMIT; } #endif - #endif /* NO_RECURSE */ } -#ifdef ERLANG_INTEGRATION -static void free_saved_match_state(heapframe *top) { - while (top != NULL) { - heapframe *nxt = top->Xprevframe; - (erts_pcre_stack_free)(top); - top = nxt; - } -} -#endif - /*************************************************************************** **************************************************************************** @@ -4509,7 +6550,6 @@ Undefine all the macros that were defined above to handle this. */ #undef ecode #undef mstart #undef offset_top -#undef ims #undef eptrb #undef flags @@ -4527,8 +6567,6 @@ Undefine all the macros that were defined above to handle this. */ #undef condition #undef prev_is_word -#undef original_ims - #undef ctype #undef length #undef max @@ -4555,6 +6593,34 @@ Undefine all the macros that were defined above to handle this. */ ***************************************************************************/ +#ifdef NO_RECURSE +/************************************************* +* Release allocated heap frames * +*************************************************/ + +/* This function releases all the allocated frames. The base frame is on the +machine stack, and so must not be freed. + +Argument: the address of the base frame +Returns: nothing +*/ + +static void +release_match_heapframes (heapframe *frame_base) +{ +heapframe *nextframe = frame_base->Xnextframe; +#ifdef ERLANG_INTEGRATION +frame_base->Xnextframe = NULL; /* Protect against multiple free */ +#endif +while (nextframe != NULL) + { + heapframe *oldframe = nextframe; + nextframe = nextframe->Xnextframe; + (PUBL(stack_free))(oldframe); + } +} +#endif + /************************************************* * Execute a Regular Expression * @@ -4581,118 +6647,129 @@ Returns: > 0 => success; value is the number of elements filled in */ #ifdef ERLANG_INTEGRATION typedef struct { - int Xresetcount; - int Xfirst_byte; - BOOL Xfirst_byte_caseless; - int Xreq_byte; - int Xreq_byte2; - unsigned long int Xims; - BOOL Xreq_byte_caseless; - BOOL Xusing_temporary_offsets; - BOOL Xanchored; - BOOL Xstartline; - BOOL Xfirstline; - BOOL Xutf8; - match_data Xmatch_block; - match_data *Xmd; - const uschar *Xtables; /* may point to extra_data->tables, so the tables cannot be relocated - between restarts */ - const uschar *Xstart_bits; /* Points into study, so if studies are used, *they* - cannot be relocated between restarts */ - /* The following points into the subject, so the sublect needs to stay put too */ - USPTR Xstart_match; - USPTR Xend_subject; - USPTR Xreq_byte_ptr; - /* We'll handle internal studies and re's although this will not happen - in the erlang emulator in current implementation */ - pcre_study_data Xinternal_study; - const pcre_study_data *Xstudy; - - real_pcre Xinternal_re; - const real_pcre *Xexternal_re; - const real_pcre *Xre; - /* Original function parameters that need be saved */ - int Xstart_offset; - int Xoffsetcount; - int *Xoffsets; + int Xarg_offset_max; + BOOL Xusing_temporary_offsets; + BOOL Xanchored; + BOOL Xstartline; + BOOL Xfirstline; + BOOL Xutf; + BOOL Xhas_first_char; + BOOL Xhas_req_char; + pcre_uchar Xfirst_char; + pcre_uchar Xfirst_char2; + pcre_uchar Xreq_char; + pcre_uchar Xreq_char2; + match_data Xmatch_block; + match_data *Xmd; + const pcre_uint8 *Xtables; + const pcre_uint8 *Xstart_bits; + PCRE_PUCHAR Xstart_match; + PCRE_PUCHAR Xend_subject; + PCRE_PUCHAR Xstart_partial; + PCRE_PUCHAR Xmatch_partial; + PCRE_PUCHAR Xreq_char_ptr; + const pcre_study_data *Xstudy; + REAL_PCRE *Xre; + heapframe Xframe_zero; /* Always NO_RECURSE */ + + /* Original function parameters that need be saved */ + int Xstart_offset; + int Xoffsetcount; + int *Xoffsets; } PcreExecContext; #endif - -PCRE_EXP_DEFN int -erts_pcre_exec(const pcre *argument_re, const pcre_extra *extra_data, + + +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_exec(const pcre *argument_re, const pcre_extra *extra_data, PCRE_SPTR subject, int length, int start_offset, int options, int *offsets, int offsetcount) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_exec(const pcre16 *argument_re, const pcre16_extra *extra_data, + PCRE_SPTR16 subject, int length, int start_offset, int options, int *offsets, + int offsetcount) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_exec(const pcre32 *argument_re, const pcre32_extra *extra_data, + PCRE_SPTR32 subject, int length, int start_offset, int options, int *offsets, + int offsetcount) +#endif { #ifndef ERLANG_INTEGRATION -int rc, resetcount, ocount; -int first_byte = -1; -int req_byte = -1; -int req_byte2 = -1; +int rc, ocount, arg_offset_max; int newline; -unsigned long int ims; BOOL using_temporary_offsets = FALSE; BOOL anchored; BOOL startline; BOOL firstline; -BOOL first_byte_caseless = FALSE; -BOOL req_byte_caseless = FALSE; -BOOL utf8; +BOOL utf; +BOOL has_first_char = FALSE; +BOOL has_req_char = FALSE; +pcre_uchar first_char = 0; +pcre_uchar first_char2 = 0; +pcre_uchar req_char = 0; +pcre_uchar req_char2 = 0; match_data match_block; match_data *md = &match_block; -const uschar *tables; -const uschar *start_bits = NULL; -USPTR start_match = (USPTR)subject + start_offset; -USPTR end_subject; -USPTR req_byte_ptr = start_match - 1; +const pcre_uint8 *tables; +const pcre_uint8 *start_bits = NULL; +PCRE_PUCHAR start_match = (PCRE_PUCHAR)subject + start_offset; +PCRE_PUCHAR end_subject; +PCRE_PUCHAR start_partial = NULL; +PCRE_PUCHAR match_partial; +PCRE_PUCHAR req_char_ptr = start_match - 1; -pcre_study_data internal_study; const pcre_study_data *study; - -real_pcre internal_re; -const real_pcre *external_re = (const real_pcre *)argument_re; -const real_pcre *re = external_re; - +const REAL_PCRE *re = (const REAL_PCRE *)argument_re; +#ifdef NO_RECURSE +heapframe frame_zero; +#endif #else /* "local" variables in faked stackframe instead */ -#define resetcount (exec_context->Xresetcount) -#define req_byte2 (exec_context->Xreq_byte2) +#define arg_offset_max (exec_context->Xarg_offset_max) #define using_temporary_offsets (exec_context->Xusing_temporary_offsets) #define anchored (exec_context->Xanchored) #define startline (exec_context->Xstartline) #define firstline (exec_context->Xfirstline) -#define first_byte_caseless (exec_context->Xfirst_byte_caseless) -#define req_byte_caseless (exec_context->Xreq_byte_caseless) +#define has_first_char (exec_context->Xhas_first_char) +#define has_req_char (exec_context->Xhas_req_char) +#define first_char2 (exec_context->Xfirst_char2) +#define req_char2 (exec_context->Xreq_char2) #define match_block (exec_context->Xmatch_block) #define md (exec_context->Xmd) -#define start_match (exec_context->Xstart_match) -#define req_byte_ptr (exec_context->Xreq_byte_ptr) -#define internal_study (exec_context->Xinternal_study) +#define start_match (exec_context->Xstart_match) +#define start_partial (exec_context->Xstart_partial) +#define match_partial (exec_context->Xmatch_partial) #define study (exec_context->Xstudy) -#define internal_re (exec_context->Xinternal_re) -#define external_re (exec_context->Xexternal_re) #define re (exec_context->Xre) -#define ims (exec_context->Xims) +#define frame_zero (exec_context->Xframe_zero) #define SWAPIN() do { \ - utf8 = exec_context->Xutf8; \ - first_byte = exec_context->Xfirst_byte; \ + utf = exec_context->Xutf; \ + first_char = exec_context->Xfirst_char; \ tables = exec_context->Xtables; \ start_bits = exec_context->Xstart_bits; \ end_subject = exec_context->Xend_subject; \ - req_byte = exec_context->Xreq_byte; \ + req_char_ptr = exec_context->Xreq_char_ptr; \ + req_char = exec_context->Xreq_char; \ + /* Parameters */ \ start_offset = exec_context->Xstart_offset; \ offsetcount = exec_context->Xoffsetcount; \ offsets = exec_context->Xoffsets; \ } while (0) #define SWAPOUT() do { \ - exec_context->Xutf8 = utf8; \ - exec_context->Xfirst_byte = first_byte; \ + exec_context->Xutf = utf; \ + exec_context->Xfirst_char = first_char; \ exec_context->Xtables = tables; \ exec_context->Xstart_bits = start_bits; \ exec_context->Xend_subject = end_subject; \ - exec_context->Xreq_byte = req_byte; \ + exec_context->Xreq_char_ptr = req_char_ptr; \ + exec_context->Xreq_char = req_char; \ + /* Parameters */ \ exec_context->Xstart_offset = start_offset; \ exec_context->Xoffsetcount = offsetcount; \ exec_context->Xoffsets = offsets; \ @@ -4701,16 +6778,19 @@ const real_pcre *re = external_re; PcreExecContext *exec_context; PcreExecContext internal_context; +/* Locals that need never be saved */ int rc, ocount; int newline; -/* special variables follow, swapped in and out */ -BOOL utf8; -int first_byte; -const uschar *tables; -const uschar *start_bits; -USPTR end_subject; -int req_byte; +/* Variables that we swap in and out */ +BOOL utf; +pcre_uchar first_char; +const pcre_uint8 *tables; +const pcre_uint8 *start_bits; +PCRE_PUCHAR end_subject; +PCRE_PUCHAR req_char_ptr; +pcre_uchar req_char; + /* End special swapped variables */ if (extra_data != NULL && @@ -4719,11 +6799,12 @@ int req_byte; /* we are restarting, every initialization is skipped and we jump directly into the loop */ exec_context = (PcreExecContext *) *(extra_data->restart_data); SWAPIN(); + goto RESTART_INTERRUPTED; } else { if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_LOOP_LIMIT)) { - exec_context = (PcreExecContext *) (erts_pcre_malloc)(sizeof(PcreExecContext)); + exec_context = (PcreExecContext *) (PUBL(malloc))(sizeof(PcreExecContext)); *(extra_data->restart_data) = (void *) exec_context; /* need freeing by special routine from client */ } else { @@ -4731,32 +6812,136 @@ int req_byte; } /* OK, no restart here, initialize variables instead */ - first_byte = -1; - req_byte = -1; - req_byte2 = -1; using_temporary_offsets = FALSE; - first_byte_caseless = FALSE; - req_byte_caseless = FALSE; + has_first_char = FALSE; + has_req_char = FALSE; + first_char = 0; + first_char2 = 0; + req_char = 0; + req_char2 = 0; md = &match_block; start_bits = NULL; - start_match = (USPTR)subject + start_offset; - req_byte_ptr = start_match - 1; - external_re = (const real_pcre *)argument_re; - re = external_re; + start_match = (PCRE_PUCHAR)subject + start_offset; + start_partial = NULL; + req_char_ptr = start_match - 1; + re = (REAL_PCRE *)argument_re; md->state_save = NULL; - } #endif /* ERLANG_INTEGRATION */ +#ifdef NO_RECURSE +frame_zero.Xprevframe = NULL; /* Marks the top level */ +frame_zero.Xnextframe = NULL; /* None are allocated yet */ +md->match_frames_base = &frame_zero; +#endif + +/* Check for the special magic call that measures the size of the stack used +per recursive call of match(). Without the funny casting for sizeof, a Windows +compiler gave this error: "unary minus operator applied to unsigned type, +result still unsigned". Hopefully the cast fixes that. */ + +if (re == NULL && extra_data == NULL && subject == NULL && length == -999 && + start_offset == -999) +#ifdef NO_RECURSE + return -((int)sizeof(heapframe)); +#else + return match(NULL, NULL, NULL, 0, NULL, NULL, 0); +#endif + /* Plausibility checks */ if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION; -if (re == NULL || subject == NULL || - (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL; +if (re == NULL || subject == NULL || (offsets == NULL && offsetcount > 0)) + return PCRE_ERROR_NULL; if (offsetcount < 0) return PCRE_ERROR_BADCOUNT; +if (length < 0) return PCRE_ERROR_BADLENGTH; +if (start_offset < 0 || start_offset > length) return PCRE_ERROR_BADOFFSET; + +/* Check that the first field in the block is the magic number. If it is not, +return with PCRE_ERROR_BADMAGIC. However, if the magic number is equal to +REVERSED_MAGIC_NUMBER we return with PCRE_ERROR_BADENDIANNESS, which +means that the pattern is likely compiled with different endianness. */ + +if (re->magic_number != MAGIC_NUMBER) + return re->magic_number == REVERSED_MAGIC_NUMBER? + PCRE_ERROR_BADENDIANNESS:PCRE_ERROR_BADMAGIC; +if ((re->flags & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; + +/* These two settings are used in the code for checking a UTF-8 string that +follows immediately afterwards. Other values in the md block are used only +during "normal" pcre_exec() processing, not when the JIT support is in use, +so they are set up later. */ + +/* PCRE_UTF16 has the same value as PCRE_UTF8. */ +utf = md->utf = (re->options & PCRE_UTF8) != 0; +md->partial = ((options & PCRE_PARTIAL_HARD) != 0)? 2 : + ((options & PCRE_PARTIAL_SOFT) != 0)? 1 : 0; + +/* Check a UTF-8 string if required. Pass back the character offset and error +code for an invalid string if a results vector is available. */ + +#ifdef SUPPORT_UTF +if (utf && (options & PCRE_NO_UTF8_CHECK) == 0) + { + int erroroffset; + int errorcode = PRIV(valid_utf)((PCRE_PUCHAR)subject, length, &erroroffset); + if (errorcode != 0) + { + if (offsetcount >= 2) + { + offsets[0] = erroroffset; + offsets[1] = errorcode; + } +#if defined COMPILE_PCRE8 + return (errorcode <= PCRE_UTF8_ERR5 && md->partial > 1)? + PCRE_ERROR_SHORTUTF8 : PCRE_ERROR_BADUTF8; +#elif defined COMPILE_PCRE16 + return (errorcode <= PCRE_UTF16_ERR1 && md->partial > 1)? + PCRE_ERROR_SHORTUTF16 : PCRE_ERROR_BADUTF16; +#elif defined COMPILE_PCRE32 + return PCRE_ERROR_BADUTF32; +#endif + } +#if defined COMPILE_PCRE8 || defined COMPILE_PCRE16 + /* Check that a start_offset points to the start of a UTF character. */ + if (start_offset > 0 && start_offset < length && + NOT_FIRSTCHAR(((PCRE_PUCHAR)subject)[start_offset])) + return PCRE_ERROR_BADUTF8_OFFSET; +#endif + } +#endif + +/* If the pattern was successfully studied with JIT support, run the JIT +executable instead of the rest of this function. Most options must be set at +compile time for the JIT code to be usable. Fallback to the normal code path if +an unsupported flag is set. */ + +#ifdef SUPPORT_JIT +if (extra_data != NULL + && (extra_data->flags & (PCRE_EXTRA_EXECUTABLE_JIT | + PCRE_EXTRA_TABLES)) == PCRE_EXTRA_EXECUTABLE_JIT + && extra_data->executable_jit != NULL + && (options & ~PUBLIC_JIT_EXEC_OPTIONS) == 0) + { + rc = PRIV(jit_exec)(extra_data, (const pcre_uchar *)subject, length, + start_offset, options, offsets, offsetcount); + + /* PCRE_ERROR_NULL means that the selected normal or partial matching + mode is not compiled. In this case we simply fallback to interpreter. */ + + if (rc != PCRE_ERROR_JIT_BADOPTION) return rc; + } +#endif + +/* Carry on with non-JIT matching. This information is for finding all the +numbers associated with a given name, for condition testing. */ + +md->name_table = (pcre_uchar *)re + re->name_table_offset; +md->name_count = re->name_count; +md->name_entry_size = re->name_entry_size; /* Fish out the optional data from the extra_data structure, first setting the default values. */ @@ -4768,7 +6953,9 @@ md->callout_data = NULL; /* The table pointer is always in native byte order. */ -tables = external_re->tables; +tables = re->tables; + +/* The two limit values override the defaults, whatever their value. */ if (extra_data != NULL) { @@ -4790,23 +6977,20 @@ if (extra_data != NULL) #endif } +/* Limits in the regex override only if they are smaller. */ + +if ((re->flags & PCRE_MLSET) != 0 && re->limit_match < md->match_limit) + md->match_limit = re->limit_match; + +if ((re->flags & PCRE_RLSET) != 0 && + re->limit_recursion < md->match_limit_recursion) + md->match_limit_recursion = re->limit_recursion; + /* If the exec call supplied NULL for tables, use the inbuilt ones. This is a feature that makes it possible to save compiled regex and re-use them in other programs later. */ -if (tables == NULL) tables = _erts_pcre_default_tables; - -/* Check that the first field in the block is the magic number. If it is not, -test for a regex that was compiled on a host of opposite endianness. If this is -the case, flipped values are put in internal_re and internal_study if there was -study data too. */ - -if (re->magic_number != MAGIC_NUMBER) - { - re = _erts_pcre_try_flipped(re, &internal_re, study, &internal_study); - if (re == NULL) return PCRE_ERROR_BADMAGIC; - if (study != NULL) study = &internal_study; - } +if (tables == NULL) tables = PRIV(default_tables); /* Set up other data */ @@ -4816,26 +7000,35 @@ firstline = (re->options & PCRE_FIRSTLINE) != 0; /* The code starts after the real_pcre block and the capture name table. */ -md->start_code = (const uschar *)external_re + re->name_table_offset + +md->start_code = (const pcre_uchar *)re + re->name_table_offset + re->name_count * re->name_entry_size; -md->start_subject = (USPTR)subject; +md->start_subject = (PCRE_PUCHAR)subject; md->start_offset = start_offset; md->end_subject = md->start_subject + length; end_subject = md->end_subject; md->endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; -utf8 = md->utf8 = (re->options & PCRE_UTF8) != 0; +md->use_ucp = (re->options & PCRE_UCP) != 0; +md->jscript_compat = (re->options & PCRE_JAVASCRIPT_COMPAT) != 0; +md->ignore_skip_arg = 0; + +/* Some options are unpacked into BOOL variables in the hope that testing +them will be faster than individual option bits. */ md->notbol = (options & PCRE_NOTBOL) != 0; md->noteol = (options & PCRE_NOTEOL) != 0; md->notempty = (options & PCRE_NOTEMPTY) != 0; -md->partial = (options & PCRE_PARTIAL) != 0; +md->notempty_atstart = (options & PCRE_NOTEMPTY_ATSTART) != 0; + md->hitend = FALSE; +md->mark = md->nomatch_mark = NULL; /* In case never set */ md->recursive = NULL; /* No recursion at top level */ +md->hasthen = (re->flags & PCRE_HASTHEN) != 0; md->lcc = tables + lcc_offset; +md->fcc = tables + fcc_offset; md->ctypes = tables + ctypes_offset; /* Handle different \R options. */ @@ -4871,10 +7064,10 @@ switch ((((options & PCRE_NEWLINE_BITS) == 0)? re->options : (pcre_uint32)options) & PCRE_NEWLINE_BITS) { case 0: newline = NEWLINE; break; /* Compile-time default */ - case PCRE_NEWLINE_CR: newline = '\r'; break; - case PCRE_NEWLINE_LF: newline = '\n'; break; + case PCRE_NEWLINE_CR: newline = CHAR_CR; break; + case PCRE_NEWLINE_LF: newline = CHAR_NL; break; case PCRE_NEWLINE_CR+ - PCRE_NEWLINE_LF: newline = ('\r' << 8) | '\n'; break; + PCRE_NEWLINE_LF: newline = (CHAR_CR << 8) | CHAR_NL; break; case PCRE_NEWLINE_ANY: newline = -1; break; case PCRE_NEWLINE_ANYCRLF: newline = -2; break; default: return PCRE_ERROR_BADNEWLINE; @@ -4904,79 +7097,50 @@ else } } -/* Partial matching is supported only for a restricted set of regexes at the -moment. */ +/* Partial matching was originally supported only for a restricted set of +regexes; from release 8.00 there are no restrictions, but the bits are still +defined (though never set). So there's no harm in leaving this code. */ if (md->partial && (re->flags & PCRE_NOPARTIAL) != 0) return PCRE_ERROR_BADPARTIAL; -/* Check a UTF-8 string if required. Unfortunately there's no way of passing -back the character offset. */ - -#ifdef SUPPORT_UTF8 -if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0) - { - if (_erts_pcre_valid_utf8((uschar *)subject, length) >= 0) - return PCRE_ERROR_BADUTF8; - if (start_offset > 0 && start_offset < length) - { - int tb = ((uschar *)subject)[start_offset]; - if (tb > 127) - { - tb &= 0xc0; - if (tb != 0 && tb != 0xc0) return PCRE_ERROR_BADUTF8_OFFSET; - } - } - } -#endif - -/* The ims options can vary during the matching as a result of the presence -of (?ims) items in the pattern. They are kept in a local variable so that -restoring at the exit of a group is easy. */ - -ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL); - /* If the expression has got more back references than the offsets supplied can hold, we get a temporary chunk of working store to use during the matching. Otherwise, we can use the vector supplied, rounding down its size to a multiple of 3. */ ocount = offsetcount - (offsetcount % 3); +arg_offset_max = (2*ocount)/3; if (re->top_backref > 0 && re->top_backref >= ocount/3) { ocount = re->top_backref * 3 + 3; - md->offset_vector = (int *)(erts_pcre_malloc)(ocount * sizeof(int)); + md->offset_vector = (int *)(PUBL(malloc))(ocount * sizeof(int)); if (md->offset_vector == NULL) return PCRE_ERROR_NOMEMORY; using_temporary_offsets = TRUE; DPRINTF(("Got memory to hold back references\n")); } else md->offset_vector = offsets; - md->offset_end = ocount; md->offset_max = (2*ocount)/3; -md->offset_overflow = FALSE; -md->capture_last = -1; - -/* Compute the minimum number of offsets that we need to reset each time. Doing -this makes a huge difference to execution time when there aren't many brackets -in the pattern. */ - -resetcount = 2 + re->top_bracket * 2; -if (resetcount > offsetcount) resetcount = ocount; +md->capture_last = 0; /* Reset the working variable associated with each extraction. These should never be used unless previously set, but they get saved and restored, and so we -initialize them to avoid reading uninitialized locations. */ +initialize them to avoid reading uninitialized locations. Also, unset the +offsets for the matched string. This is really just for tidiness with callouts, +in case they inspect these fields. */ if (md->offset_vector != NULL) { register int *iptr = md->offset_vector + ocount; - register int *iend = iptr - resetcount/2 + 1; + register int *iend = iptr - re->top_bracket; + if (iend < md->offset_vector + 2) iend = md->offset_vector + 2; while (--iptr >= iend) *iptr = -1; + md->offset_vector[0] = md->offset_vector[1] = -1; } -/* Set up the first character to match, if available. The first_byte value is +/* Set up the first character to match, if available. The first_char value is never set for an anchored regular expression, but the anchoring may be forced at run time, so we have to test for anchoring. The first char may be unset for an unanchored pattern, of course. If there's no first char and the pattern was @@ -4986,13 +7150,20 @@ if (!anchored) { if ((re->flags & PCRE_FIRSTSET) != 0) { - first_byte = re->first_byte & 255; - if ((first_byte_caseless = ((re->first_byte & REQ_CASELESS) != 0)) == TRUE) - first_byte = md->lcc[first_byte]; + has_first_char = TRUE; + first_char = first_char2 = (pcre_uchar)(re->first_char); + if ((re->flags & PCRE_FCH_CASELESS) != 0) + { + first_char2 = TABLE_GET(first_char, md->fcc, first_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (utf && first_char > 127) + first_char2 = UCD_OTHERCASE(first_char); +#endif + } } else if (!startline && study != NULL && - (study->options & PCRE_STUDY_MAPPED) != 0) + (study->flags & PCRE_STUDY_MAPPED) != 0) start_bits = study->start_bits; } @@ -5001,9 +7172,16 @@ character" set. */ if ((re->flags & PCRE_REQCHSET) != 0) { - req_byte = re->req_byte & 255; - req_byte_caseless = (re->req_byte & REQ_CASELESS) != 0; - req_byte2 = (tables + fcc_offset)[req_byte]; /* case flipped */ + has_req_char = TRUE; + req_char = req_char2 = (pcre_uchar)(re->req_char); + if ((re->flags & PCRE_RCH_CASELESS) != 0) + { + req_char2 = TABLE_GET(req_char, md->fcc, req_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (utf && req_char > 127) + req_char2 = UCD_OTHERCASE(req_char); +#endif + } } @@ -5014,148 +7192,199 @@ the loop runs just once. */ for(;;) { - USPTR save_end_subject = end_subject; - USPTR new_start_match; - - /* Reset the maximum number of extractions we might see. */ - - if (md->offset_vector != NULL) - { - register int *iptr = md->offset_vector; - register int *iend = iptr + resetcount; - while (iptr < iend) *iptr++ = -1; - } + PCRE_PUCHAR save_end_subject = end_subject; + PCRE_PUCHAR new_start_match; - /* Advance to a unique first char if possible. If firstline is TRUE, the - start of the match is constrained to the first line of a multiline string. - That is, the match must be before or at the first newline. Implement this by - temporarily adjusting end_subject so that we stop scanning at a newline. If - the match fails at the newline, later code breaks this loop. */ + /* If firstline is TRUE, the start of the match is constrained to the first + line of a multiline string. That is, the match must be before or at the first + newline. Implement this by temporarily adjusting end_subject so that we stop + scanning at a newline. If the match fails at the newline, later code breaks + this loop. */ if (firstline) { - USPTR t = start_match; + PCRE_PUCHAR t = start_match; +#ifdef SUPPORT_UTF + if (utf) + { + while (t < md->end_subject && !IS_NEWLINE(t)) + { + t++; + ACROSSCHAR(t < end_subject, *t, t++); + } + } + else +#endif while (t < md->end_subject && !IS_NEWLINE(t)) t++; end_subject = t; } - /* Now test for a unique first byte */ + /* There are some optimizations that avoid running the match if a known + starting point is not found, or if a known later character is not present. + However, there is an option that disables these, for testing and for ensuring + that all callouts do actually occur. The option can be set in the regex by + (*NO_START_OPT) or passed in match-time options. */ - if (first_byte >= 0) + if (((options | re->options) & PCRE_NO_START_OPTIMIZE) == 0) { - if (first_byte_caseless) - while (start_match < end_subject && - md->lcc[*start_match] != first_byte) - { NEXTCHAR(start_match,end_subject); } - else - while (start_match < end_subject && *start_match != first_byte) - { NEXTCHAR(start_match,end_subject); } - } + /* Advance to a unique first char if there is one. */ - /* Or to just after a linebreak for a multiline match if possible */ - - else if (startline) - { - if (start_match > md->start_subject + start_offset) + if (has_first_char) { - while (start_match <= end_subject && !WAS_NEWLINE(start_match)) - { NEXTCHAR(start_match,end_subject); } + pcre_uchar smc; - /* If we have just passed a CR and the newline option is ANY or ANYCRLF, - and we are now at a LF, advance the match position by one more character. - */ + if (first_char != first_char2) + while (start_match < end_subject && + (smc = RAWUCHARTEST(start_match)) != first_char && smc != first_char2) + start_match++; + else + while (start_match < end_subject && RAWUCHARTEST(start_match) != first_char) + start_match++; + } + + /* Or to just after a linebreak for a multiline match */ - if (start_match <= end_subject && start_match[-1] == '\r' && - (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF) && - start_match < end_subject && - *start_match == '\n') - start_match++; + else if (startline) + { + if (start_match > md->start_subject + start_offset) + { +#ifdef SUPPORT_UTF + if (utf) + { + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + { + start_match++; + ACROSSCHAR(start_match < end_subject, *start_match, + start_match++); + } + } + else +#endif + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + start_match++; + + /* If we have just passed a CR and the newline option is ANY or ANYCRLF, + and we are now at a LF, advance the match position by one more character. + */ + + if (start_match[-1] == CHAR_CR && + (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF) && + start_match < end_subject && + RAWUCHARTEST(start_match) == CHAR_NL) + start_match++; + } } - } - /* Or to a non-unique first char after study */ + /* Or to a non-unique first byte after study */ - else if (start_bits != NULL) - { - while (start_match < end_subject) + else if (start_bits != NULL) { - register unsigned int c = *start_match; - if ((start_bits[c/8] & (1 << (c&7))) == 0) - { NEXTCHAR(start_match,end_subject); } + while (start_match < end_subject) + { + register pcre_uint32 c = RAWUCHARTEST(start_match); +#ifndef COMPILE_PCRE8 + if (c > 255) c = 255; +#endif + if ((start_bits[c/8] & (1 << (c&7))) == 0) + { + start_match++; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 + /* In non 8-bit mode, the iteration will stop for + characters > 255 at the beginning or not stop at all. */ + if (utf) + ACROSSCHAR(start_match < end_subject, *start_match, + start_match++); +#endif + } #ifdef ERLANG_INTEGRATION - else { + else { if ((extra_data->flags & PCRE_EXTRA_LOOP_LIMIT) != 0) { *extra_data->loop_counter_return = (extra_data->loop_limit - md->loop_limit); } break; - } + } #else - else break; + else break; #endif + } } - } + } /* Starting optimizations */ /* Restore fudged end_subject */ end_subject = save_end_subject; -#ifdef DEBUG /* Sigh. Some compilers never learn. */ - printf(">>>> Match against: "); - pchars(start_match, end_subject - start_match, TRUE, md); - printf("\n"); -#endif + /* The following two optimizations are disabled for partial matching or if + disabling is explicitly requested. */ - /* If req_byte is set, we know that that character must appear in the subject - for the match to succeed. If the first character is set, req_byte must be - later in the subject; otherwise the test starts at the match point. This - optimization can save a huge amount of backtracking in patterns with nested - unlimited repeats that aren't going to match. Writing separate code for - cased/caseless versions makes it go faster, as does using an autoincrement - and backing off on a match. - - HOWEVER: when the subject string is very, very long, searching to its end can - take a long time, and give bad performance on quite ordinary patterns. This - showed up when somebody was matching something like /^\d+C/ on a 32-megabyte - string... so we don't do this when the string is sufficiently long. - - ALSO: this processing is disabled when partial matching is requested. - */ - - if (req_byte >= 0 && - end_subject - start_match < REQ_BYTE_MAX && - !md->partial) + if (((options | re->options) & PCRE_NO_START_OPTIMIZE) == 0 && !md->partial) { - register USPTR p = start_match + ((first_byte >= 0)? 1 : 0); + /* If the pattern was studied, a minimum subject length may be set. This is + a lower bound; no actual string of that length may actually match the + pattern. Although the value is, strictly, in characters, we treat it as + bytes to avoid spending too much time in this optimization. */ - /* We don't need to repeat the search if we haven't yet reached the - place we found it at last time. */ + if (study != NULL && (study->flags & PCRE_STUDY_MINLEN) != 0 && + (pcre_uint32)(end_subject - start_match) < study->minlength) + { + rc = MATCH_NOMATCH; +#ifdef ERLANG_INTEGRATION + if ((extra_data->flags & PCRE_EXTRA_LOOP_LIMIT) != 0) + { + *extra_data->loop_counter_return = + (extra_data->loop_limit - md->loop_limit); + } +#endif + break; + } - if (p > req_byte_ptr) + /* If req_char is set, we know that that character must appear in the + subject for the match to succeed. If the first character is set, req_char + must be later in the subject; otherwise the test starts at the match point. + This optimization can save a huge amount of backtracking in patterns with + nested unlimited repeats that aren't going to match. Writing separate code + for cased/caseless versions makes it go faster, as does using an + autoincrement and backing off on a match. + + HOWEVER: when the subject string is very, very long, searching to its end + can take a long time, and give bad performance on quite ordinary patterns. + This showed up when somebody was matching something like /^\d+C/ on a + 32-megabyte string... so we don't do this when the string is sufficiently + long. */ + + if (has_req_char && end_subject - start_match < REQ_BYTE_MAX) { - if (req_byte_caseless) + register PCRE_PUCHAR p = start_match + (has_first_char? 1:0); + + /* We don't need to repeat the search if we haven't yet reached the + place we found it at last time. */ + + if (p > req_char_ptr) { - while (p < end_subject) + if (req_char != req_char2) { - register int pp = *p++; - if (pp == req_byte || pp == req_byte2) { p--; break; } + while (p < end_subject) + { + register pcre_uint32 pp = RAWUCHARINCTEST(p); + if (pp == req_char || pp == req_char2) { p--; break; } + } } - } - else - { - while (p < end_subject) + else { - if (*p++ == req_byte) { p--; break; } + while (p < end_subject) + { + if (RAWUCHARINCTEST(p) == req_char) { p--; break; } + } } - } - /* If we can't find the required character, break the matching loop, - forcing a match failure. */ + /* If we can't find the required character, break the matching loop, + forcing a match failure. */ - if (p >= end_subject) - { - rc = MATCH_NOMATCH; + if (p >= end_subject) + { + rc = MATCH_NOMATCH; #ifdef ERLANG_INTEGRATION if ((extra_data->flags & PCRE_EXTRA_LOOP_LIMIT) != 0) { @@ -5163,23 +7392,35 @@ for(;;) (extra_data->loop_limit - md->loop_limit); } #endif - break; - } + break; + } - /* If we have found the required character, save the point where we - found it, so that we don't search again next time round the loop if - the start hasn't passed this character yet. */ + /* If we have found the required character, save the point where we + found it, so that we don't search again next time round the loop if + the start hasn't passed this character yet. */ - req_byte_ptr = p; + req_char_ptr = p; + } } } - /* OK, we can now run the match. */ +#ifdef PCRE_DEBUG /* Sigh. Some compilers never learn. */ + printf(">>>> Match against: "); + pchars(start_match, end_subject - start_match, TRUE, md); + printf("\n"); +#endif + + /* OK, we can now run the match. If "hitend" is set afterwards, remember the + first starting point for which a partial match was found. */ md->start_match_ptr = start_match; + md->start_used_ptr = start_match; md->match_call_count = 0; + md->match_function_type = 0; + md->end_offset_top = 0; + md->skip_arg_count = 0; EDEBUGF(("Calling match...")); - rc = match(start_match, md->start_code, start_match, 2, md, ims, NULL, 0, 0); + rc = match(start_match, md->start_code, start_match, 2, md, NULL, 0); #ifdef ERLANG_INTEGRATION if ((extra_data->flags & PCRE_EXTRA_LOOP_LIMIT) != 0) { @@ -5192,42 +7433,65 @@ for(;;) return PCRE_ERROR_LOOP_LIMIT; RESTART_INTERRUPTED: md->loop_limit = extra_data->loop_limit; - rc = match(NULL,NULL,NULL,0,md,0,NULL,0,0); + rc = match(NULL,NULL,NULL,0,md,NULL,0); *extra_data->loop_counter_return = (extra_data->loop_limit - md->loop_limit); } md->state_save = NULL; /* So that next call to free_saved... does not crash */ #endif + if (md->hitend && start_partial == NULL) + { + start_partial = md->start_used_ptr; + match_partial = start_match; + } switch(rc) { + /* If MATCH_SKIP_ARG reaches this level it means that a MARK that matched + the SKIP's arg was not found. In this circumstance, Perl ignores the SKIP + entirely. The only way we can do that is to re-do the match at the same + point, with a flag to force SKIP with an argument to be ignored. Just + treating this case as NOMATCH does not work because it does not check other + alternatives in patterns such as A(*SKIP:A)B|AC when the subject is AC. */ + + case MATCH_SKIP_ARG: + new_start_match = start_match; + md->ignore_skip_arg = md->skip_arg_count; + break; + + /* SKIP passes back the next starting point explicitly, but if it is no + greater than the match we have just done, treat it as NOMATCH. */ + + case MATCH_SKIP: + if (md->start_match_ptr > start_match) + { + new_start_match = md->start_match_ptr; + break; + } + /* Fall through */ + /* NOMATCH and PRUNE advance by one character. THEN at this level acts - exactly like PRUNE. */ + exactly like PRUNE. Unset ignore SKIP-with-argument. */ case MATCH_NOMATCH: case MATCH_PRUNE: case MATCH_THEN: + md->ignore_skip_arg = 0; new_start_match = start_match + 1; -#ifdef SUPPORT_UTF8 - if (utf8) - while(new_start_match < end_subject && (*new_start_match & 0xc0) == 0x80) - new_start_match++; +#ifdef SUPPORT_UTF + if (utf) + ACROSSCHAR(new_start_match < end_subject, *new_start_match, + new_start_match++); #endif break; - /* SKIP passes back the next starting point explicitly. */ - - case MATCH_SKIP: - new_start_match = md->start_match_ptr; - break; - /* COMMIT disables the bumpalong, but otherwise behaves as NOMATCH. */ case MATCH_COMMIT: rc = MATCH_NOMATCH; goto ENDLOOP; - /* Any other return is some kind of error. */ + /* Any other return is either a match, or some kind of error. */ default: goto ENDLOOP; @@ -5255,18 +7519,23 @@ for(;;) /* If we have just passed a CR and we are now at a LF, and the pattern does not contain any explicit matches for \r or \n, and the newline option is CRLF - or ANY or ANYCRLF, advance the match position by one more character. */ + or ANY or ANYCRLF, advance the match position by one more character. In + normal matching start_match will aways be greater than the first position at + this stage, but a failed *SKIP can cause a return at the same point, which is + why the first test exists. */ - if (start_match[-1] == '\r' && + if (start_match > (PCRE_PUCHAR)subject + start_offset && + start_match[-1] == CHAR_CR && start_match < end_subject && - *start_match == '\n' && + *start_match == CHAR_NL && (re->flags & PCRE_HASCRORLF) == 0 && (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF || md->nllen == 2)) start_match++; - } /* End of for(;;) "bumpalong" loop */ + md->mark = NULL; /* Reset for start of next match attempt */ + } /* End of for(;;) "bumpalong" loop */ /* ==========================================================================*/ @@ -5290,32 +7559,49 @@ capturing parentheses than vector slots. */ ENDLOOP: -if (rc == MATCH_MATCH) +if (rc == MATCH_MATCH || rc == MATCH_ACCEPT) { if (using_temporary_offsets) { - if (offsetcount >= 4) + if (arg_offset_max >= 4) { memcpy(offsets + 2, md->offset_vector + 2, - (offsetcount - 2) * sizeof(int)); + (arg_offset_max - 2) * sizeof(int)); DPRINTF(("Copied offsets from temporary memory\n")); } - if (md->end_offset_top > offsetcount) md->offset_overflow = TRUE; + if (md->end_offset_top > arg_offset_max) md->capture_last |= OVFLBIT; DPRINTF(("Freeing temporary memory\n")); + (PUBL(free))(md->offset_vector); #ifdef ERLANG_INTEGRATION - if (extra_data == NULL || - !(extra_data->flags & PCRE_EXTRA_LOOP_LIMIT)) { - (erts_pcre_free)(md->offset_vector); - } -#else - (erts_pcre_free)(md->offset_vector); + md->offset_vector = NULL; #endif } - /* Set the return code to the number of captured strings, or 0 if there are + /* Set the return code to the number of captured strings, or 0 if there were too many to fit into the vector. */ - rc = md->offset_overflow? 0 : md->end_offset_top/2; + rc = ((md->capture_last & OVFLBIT) != 0 && + md->end_offset_top >= arg_offset_max)? + 0 : md->end_offset_top/2; + + /* If there is space in the offset vector, set any unused pairs at the end of + the pattern to -1 for backwards compatibility. It is documented that this + happens. In earlier versions, the whole set of potential capturing offsets + was set to -1 each time round the loop, but this is handled differently now. + "Gaps" are set to -1 dynamically instead (this fixes a bug). Thus, it is only + those at the end that need unsetting here. We can't just unset them all at + the start of the whole thing because they may get set in one branch that is + not the final matching branch. */ + + if (md->end_offset_top/2 <= re->top_bracket && offsets != NULL) + { + register int *iptr, *iend; + int resetcount = 2 + re->top_bracket * 2; + if (resetcount > offsetcount) resetcount = offsetcount; + iptr = offsets + md->end_offset_top; + iend = offsets + resetcount; + while (iptr < iend) *iptr++ = -1; + } /* If there is space, set up the whole thing as substring 0. The value of md->start_match_ptr might be modified if \K was encountered on the success @@ -5323,11 +7609,18 @@ if (rc == MATCH_MATCH) if (offsetcount < 2) rc = 0; else { - offsets[0] = md->start_match_ptr - md->start_subject; - offsets[1] = md->end_match_ptr - md->start_subject; + offsets[0] = (int)(md->start_match_ptr - md->start_subject); + offsets[1] = (int)(md->end_match_ptr - md->start_subject); } + /* Return MARK data if requested */ + + if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_MARK) != 0) + *(extra_data->mark) = (pcre_uchar *)md->mark; DPRINTF((">>>> returning %d\n", rc)); +#ifdef NO_RECURSE + release_match_heapframes(&frame_zero); +#endif return rc; } @@ -5337,44 +7630,82 @@ attempt has failed at all permitted starting positions. */ if (using_temporary_offsets) { DPRINTF(("Freeing temporary memory\n")); - (erts_pcre_free)(md->offset_vector); +#ifdef ERLANG_INTEGRATION + if (extra_data == NULL || + !(extra_data->flags & PCRE_EXTRA_LOOP_LIMIT)) + { + (PUBL(free))(md->offset_vector); + md->offset_vector = NULL; + } +#else + (PUBL(free))(md->offset_vector); +#endif } -if (rc != MATCH_NOMATCH) +/* For anything other than nomatch or partial match, just return the code. */ + +if (rc != MATCH_NOMATCH && rc != PCRE_ERROR_PARTIAL) { DPRINTF((">>>> error: returning %d\n", rc)); +#ifdef NO_RECURSE + release_match_heapframes(&frame_zero); +#endif return rc; } -else if (md->partial && md->hitend) + +/* Handle partial matches - disable any mark data */ + +if (start_partial != NULL) { DPRINTF((">>>> returning PCRE_ERROR_PARTIAL\n")); - return PCRE_ERROR_PARTIAL; + md->mark = NULL; + if (offsetcount > 1) + { + offsets[0] = (int)(start_partial - (PCRE_PUCHAR)subject); + offsets[1] = (int)(end_subject - (PCRE_PUCHAR)subject); + if (offsetcount > 2) + offsets[2] = (int)(match_partial - (PCRE_PUCHAR)subject); + } + rc = PCRE_ERROR_PARTIAL; } + +/* This is the classic nomatch case */ + else { DPRINTF((">>>> returning PCRE_ERROR_NOMATCH\n")); - return PCRE_ERROR_NOMATCH; + rc = PCRE_ERROR_NOMATCH; } + +/* Return the MARK data if it has been requested. */ + +if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_MARK) != 0) + *(extra_data->mark) = (pcre_uchar *)md->nomatch_mark; +#ifdef NO_RECURSE + release_match_heapframes(&frame_zero); +#endif +return rc; } -#ifdef ERLANG_INTEGRATION -#undef resetcount -#undef req_byte2 -#undef using_temporary_offsets -#undef anchored -#undef startline -#undef firstline -#undef first_byte_caseless -#undef req_byte_caseless -#undef match_block -#undef md -#undef start_match -#undef req_byte_ptr -#undef internal_study -#undef study -#undef internal_re -#undef external_re -#undef re -#undef ims + +#if defined(ERLANG_INTEGRATION) +#undef arg_offset_max +#undef using_temporary_offsets +#undef anchored +#undef startline +#undef firstline +#undef has_first_char +#undef has_req_char +#undef first_char2 +#undef req_char +#undef req_char2 +#undef match_block +#undef md +#undef start_match +#undef start_partial +#undef match_partial +#undef study +#undef re +#undef frame_zero void erts_pcre_free_restart_data(void *restart_data) { PcreExecContext *top = (PcreExecContext *) restart_data; @@ -5382,12 +7713,11 @@ void erts_pcre_free_restart_data(void *restart_data) { if (top != NULL) { match_data *md = top->Xmd; if (top->Xusing_temporary_offsets && md->offset_vector != NULL) { - (erts_pcre_free)(md->offset_vector); + (PUBL(free))(md->offset_vector); } - free_saved_match_state(top->Xmd->state_save); - (erts_pcre_free)(top); + release_match_heapframes(&(top->Xframe_zero)); + (PUBL(free))(top); } } #endif - /* End of pcre_exec.c */ diff --git a/erts/emulator/pcre/pcre_fullinfo.c b/erts/emulator/pcre/pcre_fullinfo.c index 559c4e27b4..80a080a758 100644 --- a/erts/emulator/pcre/pcre_fullinfo.c +++ b/erts/emulator/pcre/pcre_fullinfo.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2013 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. */ -/* This module contains the external function erts_pcre_fullinfo(), which returns +/* This module contains the external function pcre_fullinfo(), which returns information about a compiled pattern. */ /* %ExternalCopyright% */ @@ -66,13 +66,21 @@ Arguments: Returns: 0 if data returned, negative on error */ -PCRE_EXP_DEFN int -erts_pcre_fullinfo(const pcre *argument_re, const pcre_extra *extra_data, int what, - void *where) +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_fullinfo(const pcre *argument_re, const pcre_extra *extra_data, + int what, void *where) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_fullinfo(const pcre16 *argument_re, const pcre16_extra *extra_data, + int what, void *where) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_fullinfo(const pcre32 *argument_re, const pcre32_extra *extra_data, + int what, void *where) +#endif { -real_pcre internal_re; -pcre_study_data internal_study; -const real_pcre *re = (const real_pcre *)argument_re; +const REAL_PCRE *re = (const REAL_PCRE *)argument_re; const pcre_study_data *study = NULL; if (re == NULL || where == NULL) return PCRE_ERROR_NULL; @@ -80,17 +88,23 @@ if (re == NULL || where == NULL) return PCRE_ERROR_NULL; if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_STUDY_DATA) != 0) study = (const pcre_study_data *)extra_data->study_data; +/* Check that the first field in the block is the magic number. If it is not, +return with PCRE_ERROR_BADMAGIC. However, if the magic number is equal to +REVERSED_MAGIC_NUMBER we return with PCRE_ERROR_BADENDIANNESS, which +means that the pattern is likely compiled with different endianness. */ + if (re->magic_number != MAGIC_NUMBER) - { - re = _erts_pcre_try_flipped(re, &internal_re, study, &internal_study); - if (re == NULL) return PCRE_ERROR_BADMAGIC; - if (study != NULL) study = &internal_study; - } + return re->magic_number == REVERSED_MAGIC_NUMBER? + PCRE_ERROR_BADENDIANNESS:PCRE_ERROR_BADMAGIC; + +/* Check that this pattern was compiled in the correct bit mode */ + +if ((re->flags & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; switch (what) { case PCRE_INFO_OPTIONS: - *((unsigned long int *)where) = re->options & PUBLIC_OPTIONS; + *((unsigned long int *)where) = re->options & PUBLIC_COMPILE_OPTIONS; break; case PCRE_INFO_SIZE: @@ -101,6 +115,18 @@ switch (what) *((size_t *)where) = (study == NULL)? 0 : study->size; break; + case PCRE_INFO_JITSIZE: +#ifdef SUPPORT_JIT + *((size_t *)where) = + (extra_data != NULL && + (extra_data->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 && + extra_data->executable_jit != NULL)? + PRIV(jit_get_size)(extra_data->executable_jit) : 0; +#else + *((size_t *)where) = 0; +#endif + break; + case PCRE_INFO_CAPTURECOUNT: *((int *)where) = re->top_bracket; break; @@ -111,24 +137,57 @@ switch (what) case PCRE_INFO_FIRSTBYTE: *((int *)where) = - ((re->flags & PCRE_FIRSTSET) != 0)? re->first_byte : + ((re->flags & PCRE_FIRSTSET) != 0)? (int)re->first_char : ((re->flags & PCRE_STARTLINE) != 0)? -1 : -2; break; + case PCRE_INFO_FIRSTCHARACTER: + *((pcre_uint32 *)where) = + (re->flags & PCRE_FIRSTSET) != 0 ? re->first_char : 0; + break; + + case PCRE_INFO_FIRSTCHARACTERFLAGS: + *((int *)where) = + ((re->flags & PCRE_FIRSTSET) != 0) ? 1 : + ((re->flags & PCRE_STARTLINE) != 0) ? 2 : 0; + break; + /* Make sure we pass back the pointer to the bit vector in the external block, not the internal copy (with flipped integer fields). */ case PCRE_INFO_FIRSTTABLE: - *((const uschar **)where) = - (study != NULL && (study->options & PCRE_STUDY_MAPPED) != 0)? + *((const pcre_uint8 **)where) = + (study != NULL && (study->flags & PCRE_STUDY_MAPPED) != 0)? ((const pcre_study_data *)extra_data->study_data)->start_bits : NULL; break; + case PCRE_INFO_MINLENGTH: + *((int *)where) = + (study != NULL && (study->flags & PCRE_STUDY_MINLEN) != 0)? + (int)(study->minlength) : -1; + break; + + case PCRE_INFO_JIT: + *((int *)where) = extra_data != NULL && + (extra_data->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 && + extra_data->executable_jit != NULL; + break; + case PCRE_INFO_LASTLITERAL: *((int *)where) = - ((re->flags & PCRE_REQCHSET) != 0)? re->req_byte : -1; + ((re->flags & PCRE_REQCHSET) != 0)? (int)re->req_char : -1; break; + case PCRE_INFO_REQUIREDCHAR: + *((pcre_uint32 *)where) = + ((re->flags & PCRE_REQCHSET) != 0) ? re->req_char : 0; + break; + + case PCRE_INFO_REQUIREDCHARFLAGS: + *((int *)where) = + ((re->flags & PCRE_REQCHSET) != 0); + break; + case PCRE_INFO_NAMEENTRYSIZE: *((int *)where) = re->name_entry_size; break; @@ -138,13 +197,16 @@ switch (what) break; case PCRE_INFO_NAMETABLE: - *((const uschar **)where) = (const uschar *)re + re->name_table_offset; + *((const pcre_uchar **)where) = (const pcre_uchar *)re + re->name_table_offset; break; case PCRE_INFO_DEFAULT_TABLES: - *((const uschar **)where) = (const uschar *)(_erts_pcre_default_tables); + *((const pcre_uint8 **)where) = (const pcre_uint8 *)(PRIV(default_tables)); break; + /* From release 8.00 this will always return TRUE because NOPARTIAL is + no longer ever set (the restrictions have been removed). */ + case PCRE_INFO_OKPARTIAL: *((int *)where) = (re->flags & PCRE_NOPARTIAL) == 0; break; @@ -157,6 +219,20 @@ switch (what) *((int *)where) = (re->flags & PCRE_HASCRORLF) != 0; break; + case PCRE_INFO_MAXLOOKBEHIND: + *((int *)where) = re->max_lookbehind; + break; + + case PCRE_INFO_MATCHLIMIT: + if ((re->flags & PCRE_MLSET) == 0) return PCRE_ERROR_UNSET; + *((pcre_uint32 *)where) = re->limit_match; + break; + + case PCRE_INFO_RECURSIONLIMIT: + if ((re->flags & PCRE_RLSET) == 0) return PCRE_ERROR_UNSET; + *((pcre_uint32 *)where) = re->limit_recursion; + break; + default: return PCRE_ERROR_BADOPTION; } diff --git a/erts/emulator/pcre/pcre_get.c b/erts/emulator/pcre/pcre_get.c index 0bfd2e19a3..cc669ead38 100644 --- a/erts/emulator/pcre/pcre_get.c +++ b/erts/emulator/pcre/pcre_get.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2012 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -66,30 +66,61 @@ Returns: the number of the named parentheses, or a negative number (PCRE_ERROR_NOSUBSTRING) if not found */ -int -erts_pcre_get_stringnumber(const pcre *code, const char *stringname) +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_get_stringnumber(const pcre *code, const char *stringname) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_get_stringnumber(const pcre16 *code, PCRE_SPTR16 stringname) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_get_stringnumber(const pcre32 *code, PCRE_SPTR32 stringname) +#endif { int rc; int entrysize; int top, bot; -uschar *nametable; +pcre_uchar *nametable; + +#ifdef COMPILE_PCRE8 +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; + +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; +#endif +#ifdef COMPILE_PCRE16 +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; -if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; +#endif +#ifdef COMPILE_PCRE32 +if ((rc = pcre32_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) return rc; if (top <= 0) return PCRE_ERROR_NOSUBSTRING; -if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) +if ((rc = pcre32_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) return rc; -if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) +if ((rc = pcre32_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) return rc; +#endif bot = 0; while (top > bot) { int mid = (top + bot) / 2; - uschar *entry = nametable + entrysize*mid; - int c = strcmp(stringname, (char *)(entry + 2)); - if (c == 0) return (entry[0] << 8) + entry[1]; + pcre_uchar *entry = nametable + entrysize*mid; + int c = STRCMP_UC_UC((pcre_uchar *)stringname, + (pcre_uchar *)(entry + IMM2_SIZE)); + if (c == 0) return GET2(entry, 0); if (c > 0) bot = mid + 1; else top = mid; } @@ -115,47 +146,90 @@ Returns: the length of each entry, or a negative number (PCRE_ERROR_NOSUBSTRING) if not found */ -int -erts_pcre_get_stringtable_entries(const pcre *code, const char *stringname, +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_get_stringtable_entries(const pcre *code, const char *stringname, char **firstptr, char **lastptr) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_get_stringtable_entries(const pcre16 *code, PCRE_SPTR16 stringname, + PCRE_UCHAR16 **firstptr, PCRE_UCHAR16 **lastptr) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_get_stringtable_entries(const pcre32 *code, PCRE_SPTR32 stringname, + PCRE_UCHAR32 **firstptr, PCRE_UCHAR32 **lastptr) +#endif { int rc; int entrysize; int top, bot; -uschar *nametable, *lastentry; +pcre_uchar *nametable, *lastentry; + +#ifdef COMPILE_PCRE8 +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; + +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; +#endif +#ifdef COMPILE_PCRE16 +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; -if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; +#endif +#ifdef COMPILE_PCRE32 +if ((rc = pcre32_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) return rc; if (top <= 0) return PCRE_ERROR_NOSUBSTRING; -if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) +if ((rc = pcre32_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) return rc; -if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) +if ((rc = pcre32_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) return rc; +#endif lastentry = nametable + entrysize * (top - 1); bot = 0; while (top > bot) { int mid = (top + bot) / 2; - uschar *entry = nametable + entrysize*mid; - int c = strcmp(stringname, (char *)(entry + 2)); + pcre_uchar *entry = nametable + entrysize*mid; + int c = STRCMP_UC_UC((pcre_uchar *)stringname, + (pcre_uchar *)(entry + IMM2_SIZE)); if (c == 0) { - uschar *first = entry; - uschar *last = entry; + pcre_uchar *first = entry; + pcre_uchar *last = entry; while (first > nametable) { - if (strcmp(stringname, (char *)(first - entrysize + 2)) != 0) break; + if (STRCMP_UC_UC((pcre_uchar *)stringname, + (pcre_uchar *)(first - entrysize + IMM2_SIZE)) != 0) break; first -= entrysize; } while (last < lastentry) { - if (strcmp(stringname, (char *)(last + entrysize + 2)) != 0) break; + if (STRCMP_UC_UC((pcre_uchar *)stringname, + (pcre_uchar *)(last + entrysize + IMM2_SIZE)) != 0) break; last += entrysize; } +#if defined COMPILE_PCRE8 *firstptr = (char *)first; *lastptr = (char *)last; +#elif defined COMPILE_PCRE16 + *firstptr = (PCRE_UCHAR16 *)first; + *lastptr = (PCRE_UCHAR16 *)last; +#elif defined COMPILE_PCRE32 + *firstptr = (PCRE_UCHAR32 *)first; + *lastptr = (PCRE_UCHAR32 *)last; +#endif return entrysize; } if (c > 0) bot = mid + 1; else top = mid; @@ -183,23 +257,48 @@ Returns: the number of the first that is set, or a negative number on error */ +#if defined COMPILE_PCRE8 static int get_first_set(const pcre *code, const char *stringname, int *ovector) +#elif defined COMPILE_PCRE16 +static int +get_first_set(const pcre16 *code, PCRE_SPTR16 stringname, int *ovector) +#elif defined COMPILE_PCRE32 +static int +get_first_set(const pcre32 *code, PCRE_SPTR32 stringname, int *ovector) +#endif { -const real_pcre *re = (const real_pcre *)code; +const REAL_PCRE *re = (const REAL_PCRE *)code; int entrysize; +pcre_uchar *entry; +#if defined COMPILE_PCRE8 char *first, *last; -uschar *entry; +#elif defined COMPILE_PCRE16 +PCRE_UCHAR16 *first, *last; +#elif defined COMPILE_PCRE32 +PCRE_UCHAR32 *first, *last; +#endif + +#if defined COMPILE_PCRE8 +if ((re->options & PCRE_DUPNAMES) == 0 && (re->flags & PCRE_JCHANGED) == 0) + return pcre_get_stringnumber(code, stringname); +entrysize = pcre_get_stringtable_entries(code, stringname, &first, &last); +#elif defined COMPILE_PCRE16 +if ((re->options & PCRE_DUPNAMES) == 0 && (re->flags & PCRE_JCHANGED) == 0) + return pcre16_get_stringnumber(code, stringname); +entrysize = pcre16_get_stringtable_entries(code, stringname, &first, &last); +#elif defined COMPILE_PCRE32 if ((re->options & PCRE_DUPNAMES) == 0 && (re->flags & PCRE_JCHANGED) == 0) - return erts_pcre_get_stringnumber(code, stringname); -entrysize = erts_pcre_get_stringtable_entries(code, stringname, &first, &last); + return pcre32_get_stringnumber(code, stringname); +entrysize = pcre32_get_stringtable_entries(code, stringname, &first, &last); +#endif if (entrysize <= 0) return entrysize; -for (entry = (uschar *)first; entry <= (uschar *)last; entry += entrysize) +for (entry = (pcre_uchar *)first; entry <= (pcre_uchar *)last; entry += entrysize) { - int n = (entry[0] << 8) + entry[1]; + int n = GET2(entry, 0); if (ovector[n*2] >= 0) return n; } -return (first[0] << 8) + first[1]; +return GET2(entry, 0); } @@ -232,9 +331,19 @@ Returns: if successful: PCRE_ERROR_NOSUBSTRING (-7) no such captured substring */ -int -erts_pcre_copy_substring(const char *subject, int *ovector, int stringcount, +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_copy_substring(const char *subject, int *ovector, int stringcount, int stringnumber, char *buffer, int size) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_copy_substring(PCRE_SPTR16 subject, int *ovector, int stringcount, + int stringnumber, PCRE_UCHAR16 *buffer, int size) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_copy_substring(PCRE_SPTR32 subject, int *ovector, int stringcount, + int stringnumber, PCRE_UCHAR32 *buffer, int size) +#endif { int yield; if (stringnumber < 0 || stringnumber >= stringcount) @@ -242,7 +351,7 @@ if (stringnumber < 0 || stringnumber >= stringcount) stringnumber *= 2; yield = ovector[stringnumber+1] - ovector[stringnumber]; if (size < yield + 1) return PCRE_ERROR_NOMEMORY; -memcpy(buffer, subject + ovector[stringnumber], yield); +memcpy(buffer, subject + ovector[stringnumber], IN_UCHARS(yield)); buffer[yield] = 0; return yield; } @@ -277,13 +386,32 @@ Returns: if successful: PCRE_ERROR_NOSUBSTRING (-7) no such captured substring */ -int -erts_pcre_copy_named_substring(const pcre *code, const char *subject, int *ovector, - int stringcount, const char *stringname, char *buffer, int size) +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_copy_named_substring(const pcre *code, const char *subject, + int *ovector, int stringcount, const char *stringname, + char *buffer, int size) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_copy_named_substring(const pcre16 *code, PCRE_SPTR16 subject, + int *ovector, int stringcount, PCRE_SPTR16 stringname, + PCRE_UCHAR16 *buffer, int size) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_copy_named_substring(const pcre32 *code, PCRE_SPTR32 subject, + int *ovector, int stringcount, PCRE_SPTR32 stringname, + PCRE_UCHAR32 *buffer, int size) +#endif { int n = get_first_set(code, stringname, ovector); if (n <= 0) return n; -return erts_pcre_copy_substring(subject, ovector, stringcount, n, buffer, size); +#if defined COMPILE_PCRE8 +return pcre_copy_substring(subject, ovector, stringcount, n, buffer, size); +#elif defined COMPILE_PCRE16 +return pcre16_copy_substring(subject, ovector, stringcount, n, buffer, size); +#elif defined COMPILE_PCRE32 +return pcre32_copy_substring(subject, ovector, stringcount, n, buffer, size); +#endif } @@ -309,29 +437,45 @@ Returns: if successful: 0 PCRE_ERROR_NOMEMORY (-6) failed to get store */ -int -erts_pcre_get_substring_list(const char *subject, int *ovector, int stringcount, +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_get_substring_list(const char *subject, int *ovector, int stringcount, const char ***listptr) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_get_substring_list(PCRE_SPTR16 subject, int *ovector, int stringcount, + PCRE_SPTR16 **listptr) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_get_substring_list(PCRE_SPTR32 subject, int *ovector, int stringcount, + PCRE_SPTR32 **listptr) +#endif { int i; -int size = sizeof(char *); +int size = sizeof(pcre_uchar *); int double_count = stringcount * 2; -char **stringlist; -char *p; +pcre_uchar **stringlist; +pcre_uchar *p; for (i = 0; i < double_count; i += 2) - size += sizeof(char *) + ovector[i+1] - ovector[i] + 1; + size += sizeof(pcre_uchar *) + IN_UCHARS(ovector[i+1] - ovector[i] + 1); -stringlist = (char **)(erts_pcre_malloc)(size); +stringlist = (pcre_uchar **)(PUBL(malloc))(size); if (stringlist == NULL) return PCRE_ERROR_NOMEMORY; +#if defined COMPILE_PCRE8 *listptr = (const char **)stringlist; -p = (char *)(stringlist + stringcount + 1); +#elif defined COMPILE_PCRE16 +*listptr = (PCRE_SPTR16 *)stringlist; +#elif defined COMPILE_PCRE32 +*listptr = (PCRE_SPTR32 *)stringlist; +#endif +p = (pcre_uchar *)(stringlist + stringcount + 1); for (i = 0; i < double_count; i += 2) { int len = ovector[i+1] - ovector[i]; - memcpy(p, subject + ovector[i], len); + memcpy(p, subject + ovector[i], IN_UCHARS(len)); *stringlist++ = p; p += len; *p++ = 0; @@ -348,16 +492,25 @@ return 0; *************************************************/ /* This function exists for the benefit of people calling PCRE from non-C -programs that can call its functions, but not free() or (erts_pcre_free)() directly. +programs that can call its functions, but not free() or (PUBL(free))() +directly. -Argument: the result of a previous erts_pcre_get_substring_list() +Argument: the result of a previous pcre_get_substring_list() Returns: nothing */ -void -erts_pcre_free_substring_list(const char **pointer) +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +pcre_free_substring_list(const char **pointer) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +pcre16_free_substring_list(PCRE_SPTR16 *pointer) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +pcre32_free_substring_list(PCRE_SPTR32 *pointer) +#endif { -(erts_pcre_free)((void *)pointer); +(PUBL(free))((void *)pointer); } @@ -387,21 +540,37 @@ Returns: if successful: PCRE_ERROR_NOSUBSTRING (-7) substring not present */ -int -erts_pcre_get_substring(const char *subject, int *ovector, int stringcount, +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_get_substring(const char *subject, int *ovector, int stringcount, int stringnumber, const char **stringptr) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_get_substring(PCRE_SPTR16 subject, int *ovector, int stringcount, + int stringnumber, PCRE_SPTR16 *stringptr) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_get_substring(PCRE_SPTR32 subject, int *ovector, int stringcount, + int stringnumber, PCRE_SPTR32 *stringptr) +#endif { int yield; -char *substring; +pcre_uchar *substring; if (stringnumber < 0 || stringnumber >= stringcount) return PCRE_ERROR_NOSUBSTRING; stringnumber *= 2; yield = ovector[stringnumber+1] - ovector[stringnumber]; -substring = (char *)(erts_pcre_malloc)(yield + 1); +substring = (pcre_uchar *)(PUBL(malloc))(IN_UCHARS(yield + 1)); if (substring == NULL) return PCRE_ERROR_NOMEMORY; -memcpy(substring, subject + ovector[stringnumber], yield); +memcpy(substring, subject + ovector[stringnumber], IN_UCHARS(yield)); substring[yield] = 0; -*stringptr = substring; +#if defined COMPILE_PCRE8 +*stringptr = (const char *)substring; +#elif defined COMPILE_PCRE16 +*stringptr = (PCRE_SPTR16)substring; +#elif defined COMPILE_PCRE32 +*stringptr = (PCRE_SPTR32)substring; +#endif return yield; } @@ -434,13 +603,32 @@ Returns: if successful: PCRE_ERROR_NOSUBSTRING (-7) no such captured substring */ -int -erts_pcre_get_named_substring(const pcre *code, const char *subject, int *ovector, - int stringcount, const char *stringname, const char **stringptr) +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_get_named_substring(const pcre *code, const char *subject, + int *ovector, int stringcount, const char *stringname, + const char **stringptr) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_get_named_substring(const pcre16 *code, PCRE_SPTR16 subject, + int *ovector, int stringcount, PCRE_SPTR16 stringname, + PCRE_SPTR16 *stringptr) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_get_named_substring(const pcre32 *code, PCRE_SPTR32 subject, + int *ovector, int stringcount, PCRE_SPTR32 stringname, + PCRE_SPTR32 *stringptr) +#endif { int n = get_first_set(code, stringname, ovector); if (n <= 0) return n; -return erts_pcre_get_substring(subject, ovector, stringcount, n, stringptr); +#if defined COMPILE_PCRE8 +return pcre_get_substring(subject, ovector, stringcount, n, stringptr); +#elif defined COMPILE_PCRE16 +return pcre16_get_substring(subject, ovector, stringcount, n, stringptr); +#elif defined COMPILE_PCRE32 +return pcre32_get_substring(subject, ovector, stringcount, n, stringptr); +#endif } @@ -451,16 +639,25 @@ return erts_pcre_get_substring(subject, ovector, stringcount, n, stringptr); *************************************************/ /* This function exists for the benefit of people calling PCRE from non-C -programs that can call its functions, but not free() or (erts_pcre_free)() directly. +programs that can call its functions, but not free() or (PUBL(free))() +directly. -Argument: the result of a previous erts_pcre_get_substring() +Argument: the result of a previous pcre_get_substring() Returns: nothing */ -void -erts_pcre_free_substring(const char *pointer) +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +pcre_free_substring(const char *pointer) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +pcre16_free_substring(PCRE_SPTR16 pointer) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +pcre32_free_substring(PCRE_SPTR32 pointer) +#endif { -(erts_pcre_free)((void *)pointer); +(PUBL(free))((void *)pointer); } /* End of pcre_get.c */ diff --git a/erts/emulator/pcre/pcre_globals.c b/erts/emulator/pcre/pcre_globals.c index 1dd8d81714..ce143b8c21 100644 --- a/erts/emulator/pcre/pcre_globals.c +++ b/erts/emulator/pcre/pcre_globals.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2012 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -43,8 +43,14 @@ PCRE is thread-clean and doesn't use any global variables in the normal sense. However, it calls memory allocation and freeing functions via the four indirections below, and it can optionally do callouts, using the fifth indirection. These values can be changed by the caller, but are shared between -all threads. However, when compiling for Virtual Pascal, things are done -differently, and global variables are not used (see pcre.in). */ +all threads. + +For MS Visual Studio and Symbian OS, there are problems in initializing these +variables to non-local functions. In these cases, therefore, an indirection via +a local function is used. + +Also, when compiling for Virtual Pascal, things are done differently, and +global variables are not used. */ /* %ExternalCopyright% */ @@ -54,12 +60,27 @@ differently, and global variables are not used (see pcre.in). */ #include "pcre_internal.h" -#ifndef VPCOMPAT -PCRE_EXP_DATA_DEFN void *(*erts_pcre_malloc)(size_t) = malloc; -PCRE_EXP_DATA_DEFN void (*erts_pcre_free)(void *) = free; -PCRE_EXP_DATA_DEFN void *(*erts_pcre_stack_malloc)(size_t) = malloc; -PCRE_EXP_DATA_DEFN void (*erts_pcre_stack_free)(void *) = free; -PCRE_EXP_DATA_DEFN int (*erts_pcre_callout)(pcre_callout_block *) = NULL; +#if defined _MSC_VER || defined __SYMBIAN32__ +static void* LocalPcreMalloc(size_t aSize) + { + return malloc(aSize); + } +static void LocalPcreFree(void* aPtr) + { + free(aPtr); + } +PCRE_EXP_DATA_DEFN void *(*PUBL(malloc))(size_t) = LocalPcreMalloc; +PCRE_EXP_DATA_DEFN void (*PUBL(free))(void *) = LocalPcreFree; +PCRE_EXP_DATA_DEFN void *(*PUBL(stack_malloc))(size_t) = LocalPcreMalloc; +PCRE_EXP_DATA_DEFN void (*PUBL(stack_free))(void *) = LocalPcreFree; +PCRE_EXP_DATA_DEFN int (*PUBL(callout))(PUBL(callout_block) *) = NULL; + +#elif !defined VPCOMPAT +PCRE_EXP_DATA_DEFN void *(*PUBL(malloc))(size_t) = malloc; +PCRE_EXP_DATA_DEFN void (*PUBL(free))(void *) = free; +PCRE_EXP_DATA_DEFN void *(*PUBL(stack_malloc))(size_t) = malloc; +PCRE_EXP_DATA_DEFN void (*PUBL(stack_free))(void *) = free; +PCRE_EXP_DATA_DEFN int (*PUBL(callout))(PUBL(callout_block) *) = NULL; #endif /* End of pcre_globals.c */ diff --git a/erts/emulator/pcre/pcre_info.c b/erts/emulator/pcre/pcre_info.c deleted file mode 100644 index 86e957b0cc..0000000000 --- a/erts/emulator/pcre/pcre_info.c +++ /dev/null @@ -1,94 +0,0 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ - -/* PCRE is a library of functions to support regular expressions whose syntax -and semantics are as close as possible to those of the Perl 5 language. - - Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge - ------------------------------------------------------------------------------ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the University of Cambridge nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------ -*/ - - -/* This module contains the external function erts_pcre_info(), which gives some -information about a compiled pattern. However, use of this function is now -deprecated, as it has been superseded by pcre_fullinfo(). */ - -/* %ExternalCopyright% */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "pcre_internal.h" - - -/************************************************* -* (Obsolete) Return info about compiled pattern * -*************************************************/ - -/* This is the original "info" function. It picks potentially useful data out -of the private structure, but its interface was too rigid. It remains for -backwards compatibility. The public options are passed back in an int - though -the re->options field has been expanded to a long int, all the public options -at the low end of it, and so even on 16-bit systems this will still be OK. -Therefore, I haven't changed the API for erts_pcre_info(). - -Arguments: - argument_re points to compiled code - optptr where to pass back the options - first_byte where to pass back the first character, - or -1 if multiline and all branches start ^, - or -2 otherwise - -Returns: number of capturing subpatterns - or negative values on error -*/ - -PCRE_EXP_DEFN int -erts_pcre_info(const pcre *argument_re, int *optptr, int *first_byte) -{ -real_pcre internal_re; -const real_pcre *re = (const real_pcre *)argument_re; -if (re == NULL) return PCRE_ERROR_NULL; -if (re->magic_number != MAGIC_NUMBER) - { - re = _erts_pcre_try_flipped(re, &internal_re, NULL, NULL); - if (re == NULL) return PCRE_ERROR_BADMAGIC; - } -if (optptr != NULL) *optptr = (int)(re->options & PUBLIC_OPTIONS); -if (first_byte != NULL) - *first_byte = ((re->flags & PCRE_FIRSTSET) != 0)? re->first_byte : - ((re->flags & PCRE_STARTLINE) != 0)? -1 : -2; -return re->top_bracket; -} - -/* End of pcre_info.c */ diff --git a/erts/emulator/pcre/pcre_internal.h b/erts/emulator/pcre/pcre_internal.h index 6aafabb0c9..1304a13c5d 100644 --- a/erts/emulator/pcre/pcre_internal.h +++ b/erts/emulator/pcre/pcre_internal.h @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2013 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -40,7 +40,8 @@ POSSIBILITY OF SUCH DAMAGE. /* This header contains definitions that are shared between the different modules, but which are not relevant to the exported API. This includes some -functions whose names all begin with "_erts_pcre_". */ +functions whose names all begin with "_pcre_", "_pcre16_" or "_pcre32_" +depending on the PRIV macro. */ /* %ExternalCopyright% */ @@ -51,10 +52,44 @@ functions whose names all begin with "_erts_pcre_". */ #include "local_config.h" #endif -/* Define DEBUG to get debugging output on stdout. */ +/* Define PCRE_DEBUG to get debugging output on stdout. */ #if 0 -#define DEBUG +#define PCRE_DEBUG +#endif + +/* PCRE is compiled as an 8 bit library if it is not requested otherwise. */ + +#if !defined COMPILE_PCRE16 && !defined COMPILE_PCRE32 +#define COMPILE_PCRE8 +#endif + +/* If SUPPORT_UCP is defined, SUPPORT_UTF must also be defined. The +"configure" script ensures this, but not everybody uses "configure". */ + +#if defined SUPPORT_UCP && !(defined SUPPORT_UTF) +#define SUPPORT_UTF 1 +#endif + +/* We define SUPPORT_UTF if SUPPORT_UTF8 is enabled for compatibility +reasons with existing code. */ + +#if defined SUPPORT_UTF8 && !(defined SUPPORT_UTF) +#define SUPPORT_UTF 1 +#endif + +/* Fixme: SUPPORT_UTF8 should be eventually disappear from the code. +Until then we define it if SUPPORT_UTF is defined. */ + +#if defined SUPPORT_UTF && !(defined SUPPORT_UTF8) +#define SUPPORT_UTF8 1 +#endif + +/* We do not support both EBCDIC and UTF-8/16/32 at the same time. The "configure" +script prevents both being selected, but not everybody uses "configure". */ + +#if defined EBCDIC && defined SUPPORT_UTF +#error The use of both EBCDIC and SUPPORT_UTF is not supported. #endif /* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef @@ -66,7 +101,7 @@ It turns out that the Mac Debugging.h header also defines the macro DPRINTF, so be absolutely sure we get our version. */ #undef DPRINTF -#ifdef DEBUG +#ifdef PCRE_DEBUG #define DPRINTF(p) printf p #else #define DPRINTF(p) /* Nothing */ @@ -78,13 +113,17 @@ setjmp and stdarg are used is when NO_RECURSE is set. */ #include #include -#include -#include #include #include #include #include +/* Valgrind (memcheck) support */ + +#ifdef SUPPORT_VALGRIND +#include +#endif + /* When compiling a DLL for Windows, the exported symbols have to be declared using some MS magic. I found some useful information on this web page: http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the @@ -138,36 +177,125 @@ PCRE_EXP_DATA_DEFN only if they are not already set. */ # endif #endif -/* We need to have types that specify unsigned 16-bit and 32-bit integers. We +/* When compiling with the MSVC compiler, it is sometimes necessary to include +a "calling convention" before exported function names. (This is secondhand +information; I know nothing about MSVC myself). For example, something like + + void __cdecl function(....) + +might be needed. In order so make this easy, all the exported functions have +PCRE_CALL_CONVENTION just before their names. It is rarely needed; if not +set, we ensure here that it has no effect. */ + +#ifndef PCRE_CALL_CONVENTION +#define PCRE_CALL_CONVENTION +#endif + +/* We need to have types that specify unsigned 8, 16 and 32-bit integers. We cannot determine these outside the compilation (e.g. by running a program as part of "configure") because PCRE is often cross-compiled for use on other systems. Instead we make use of the maximum sizes that are available at preprocessor time in standard C environments. */ +typedef unsigned char pcre_uint8; + #if USHRT_MAX == 65535 - typedef unsigned short pcre_uint16; +typedef unsigned short pcre_uint16; +typedef short pcre_int16; +#define PCRE_UINT16_MAX USHRT_MAX +#define PCRE_INT16_MAX SHRT_MAX #elif UINT_MAX == 65535 - typedef unsigned int pcre_uint16; +typedef unsigned int pcre_uint16; +typedef int pcre_int16; +#define PCRE_UINT16_MAX UINT_MAX +#define PCRE_INT16_MAX INT_MAX +#else +#error Cannot determine a type for 16-bit integers +#endif + +#if UINT_MAX == 4294967295U +typedef unsigned int pcre_uint32; +typedef int pcre_int32; +#define PCRE_UINT32_MAX UINT_MAX +#define PCRE_INT32_MAX INT_MAX +#elif ULONG_MAX == 4294967295UL +typedef unsigned long int pcre_uint32; +typedef long int pcre_int32; +#define PCRE_UINT32_MAX ULONG_MAX +#define PCRE_INT32_MAX LONG_MAX #else - #error Cannot determine a type for 16-bit unsigned integers +#error Cannot determine a type for 32-bit integers +#endif + +/* When checking for integer overflow in pcre_compile(), we need to handle +large integers. If a 64-bit integer type is available, we can use that. +Otherwise we have to cast to double, which of course requires floating point +arithmetic. Handle this by defining a macro for the appropriate type. If +stdint.h is available, include it; it may define INT64_MAX. Systems that do not +have stdint.h (e.g. Solaris) may have inttypes.h. The macro int64_t may be set +by "configure". */ + +#if defined HAVE_STDINT_H +#include +#elif defined HAVE_INTTYPES_H +#include #endif -#if UINT_MAX == 4294967295 - typedef unsigned int pcre_uint32; -#elif ULONG_MAX == 4294967295 - typedef unsigned long int pcre_uint32; +#if defined INT64_MAX || defined int64_t +#define INT64_OR_DOUBLE int64_t #else - #error Cannot determine a type for 32-bit unsigned integers +#define INT64_OR_DOUBLE double #endif /* All character handling must be done as unsigned characters. Otherwise there are problems with top-bit-set characters and functions such as isspace(). -However, we leave the interface to the outside world as char *, because that -should make things easier for callers. We define a short type for unsigned char -to save lots of typing. I tried "uchar", but it causes problems on Digital -Unix, where it is defined in sys/types, so use "uschar" instead. */ +However, we leave the interface to the outside world as char * or short *, +because that should make things easier for callers. This character type is +called pcre_uchar. + +The IN_UCHARS macro multiply its argument with the byte size of the current +pcre_uchar type. Useful for memcpy and such operations, whose require the +byte size of their input/output buffers. + +The MAX_255 macro checks whether its pcre_uchar input is less than 256. -typedef unsigned char uschar; +The TABLE_GET macro is designed for accessing elements of tables whose contain +exactly 256 items. When the character is able to contain more than 256 +items, some check is needed before accessing these tables. +*/ + +#if defined COMPILE_PCRE8 + +typedef unsigned char pcre_uchar; +#define IN_UCHARS(x) (x) +#define MAX_255(c) 1 +#define TABLE_GET(c, table, default) ((table)[c]) + +#elif defined COMPILE_PCRE16 + +#if USHRT_MAX != 65535 +/* This is a warning message. Change PCRE_UCHAR16 to a 16 bit data type in +pcre.h(.in) and disable (comment out) this message. */ +#error Warning: PCRE_UCHAR16 is not a 16 bit data type. +#endif + +typedef pcre_uint16 pcre_uchar; +#define UCHAR_SHIFT (1) +#define IN_UCHARS(x) ((x) << UCHAR_SHIFT) +#define MAX_255(c) ((c) <= 255u) +#define TABLE_GET(c, table, default) (MAX_255(c)? ((table)[c]):(default)) + +#elif defined COMPILE_PCRE32 + +typedef pcre_uint32 pcre_uchar; +#define UCHAR_SHIFT (2) +#define IN_UCHARS(x) ((x) << UCHAR_SHIFT) +#define MAX_255(c) ((c) <= 255u) +#define TABLE_GET(c, table, default) (MAX_255(c)? ((table)[c]):(default)) + +#else +#error Unsupported compiling mode +#endif /* COMPILE_PCRE[8|16|32] */ /* This is an unsigned int value that no character can ever have. UTF-8 characters only go up to 0x7fffffff (though Unicode doesn't go beyond @@ -190,12 +318,12 @@ start/end of string field names are. */ #define IS_NEWLINE(p) \ ((NLBLOCK->nltype != NLTYPE_FIXED)? \ ((p) < NLBLOCK->PSEND && \ - _erts_pcre_is_newline((p), NLBLOCK->nltype, NLBLOCK->PSEND, &(NLBLOCK->nllen),\ - utf8)) \ + PRIV(is_newline)((p), NLBLOCK->nltype, NLBLOCK->PSEND, \ + &(NLBLOCK->nllen), utf)) \ : \ ((p) <= NLBLOCK->PSEND - NLBLOCK->nllen && \ - (p)[0] == NLBLOCK->nl[0] && \ - (NLBLOCK->nllen == 1 || (p)[1] == NLBLOCK->nl[1]) \ + RAWUCHARTEST(p) == NLBLOCK->nl[0] && \ + (NLBLOCK->nllen == 1 || RAWUCHARTEST(p+1) == NLBLOCK->nl[1]) \ ) \ ) @@ -204,12 +332,12 @@ start/end of string field names are. */ #define WAS_NEWLINE(p) \ ((NLBLOCK->nltype != NLTYPE_FIXED)? \ ((p) > NLBLOCK->PSSTART && \ - _erts_pcre_was_newline((p), NLBLOCK->nltype, NLBLOCK->PSSTART, \ - &(NLBLOCK->nllen), utf8)) \ + PRIV(was_newline)((p), NLBLOCK->nltype, NLBLOCK->PSSTART, \ + &(NLBLOCK->nllen), utf)) \ : \ ((p) >= NLBLOCK->PSSTART + NLBLOCK->nllen && \ - (p)[-NLBLOCK->nllen] == NLBLOCK->nl[0] && \ - (NLBLOCK->nllen == 1 || (p)[-NLBLOCK->nllen+1] == NLBLOCK->nl[1]) \ + RAWUCHARTEST(p - NLBLOCK->nllen) == NLBLOCK->nl[0] && \ + (NLBLOCK->nllen == 1 || RAWUCHARTEST(p - NLBLOCK->nllen + 1) == NLBLOCK->nl[1]) \ ) \ ) @@ -223,21 +351,22 @@ used for the external interface and appears in pcre.h, which is why its name must begin with PCRE_. */ #ifdef CUSTOM_SUBJECT_PTR -#define PCRE_SPTR CUSTOM_SUBJECT_PTR -#define USPTR CUSTOM_SUBJECT_PTR +#define PCRE_PUCHAR CUSTOM_SUBJECT_PTR #else -#define PCRE_SPTR const char * -#define USPTR const unsigned char * +#define PCRE_PUCHAR const pcre_uchar * #endif - - /* Include the public PCRE header and the definitions of UCP character property values. */ #include "pcre.h" #include "ucp.h" +#ifdef COMPILE_PCRE32 +/* Assert that the public PCRE_UCHAR32 is a 32-bit type */ +typedef int __assert_pcre_uchar32_size[sizeof(PCRE_UCHAR32) == 4 ? 1 : -1]; +#endif + /* When compiling for use with the Virtual Pascal compiler, these functions need to have their names changed. PCRE must be compiled with the -DVPCOMPAT option on the command line. */ @@ -299,6 +428,8 @@ The macros are controlled by the value of LINK_SIZE. This defaults to 2 in the config.h file, but can be overridden by using -D on the command line. This is automated on Unix systems via the "configure" command. */ +#if defined COMPILE_PCRE8 + #if LINK_SIZE == 2 #define PUT(a,n,d) \ @@ -335,13 +466,68 @@ is automated on Unix systems via the "configure" command. */ #define GET(a,n) \ (((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3]) -#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */ +/* Keep it positive */ +#define MAX_PATTERN_SIZE (1 << 30) + +#else +#error LINK_SIZE must be either 2, 3, or 4 +#endif + +#elif defined COMPILE_PCRE16 + +#if LINK_SIZE == 2 + +/* Redefine LINK_SIZE as a multiple of sizeof(pcre_uchar) */ +#undef LINK_SIZE +#define LINK_SIZE 1 + +#define PUT(a,n,d) \ + (a[n] = (d)) + +#define GET(a,n) \ + (a[n]) + +#define MAX_PATTERN_SIZE (1 << 16) + +#elif LINK_SIZE == 3 || LINK_SIZE == 4 + +/* Redefine LINK_SIZE as a multiple of sizeof(pcre_uchar) */ +#undef LINK_SIZE +#define LINK_SIZE 2 + +#define PUT(a,n,d) \ + (a[n] = (d) >> 16), \ + (a[(n)+1] = (d) & 65535) + +#define GET(a,n) \ + (((a)[n] << 16) | (a)[(n)+1]) +/* Keep it positive */ +#define MAX_PATTERN_SIZE (1 << 30) #else #error LINK_SIZE must be either 2, 3, or 4 #endif +#elif defined COMPILE_PCRE32 + +/* Only supported LINK_SIZE is 4 */ +/* Redefine LINK_SIZE as a multiple of sizeof(pcre_uchar) */ +#undef LINK_SIZE +#define LINK_SIZE 1 + +#define PUT(a,n,d) \ + (a[n] = (d)) + +#define GET(a,n) \ + (a[n]) + +/* Keep it positive */ +#define MAX_PATTERN_SIZE (1 << 30) + +#else +#error Unsupported compiling mode +#endif /* COMPILE_PCRE[8|16|32] */ /* Convenience macro defined in terms of the others */ @@ -352,106 +538,232 @@ is automated on Unix systems via the "configure" command. */ offsets changes. There are used for repeat counts and for other things such as capturing parenthesis numbers in back references. */ +#if defined COMPILE_PCRE8 + +#define IMM2_SIZE 2 + #define PUT2(a,n,d) \ a[n] = (d) >> 8; \ a[(n)+1] = (d) & 255 +/* For reasons that I do not understand, the expression in this GET2 macro is +treated by gcc as a signed expression, even when a is declared as unsigned. It +seems that any kind of arithmetic results in a signed value. */ + #define GET2(a,n) \ - (((a)[n] << 8) | (a)[(n)+1]) + (unsigned int)(((a)[n] << 8) | (a)[(n)+1]) -#define PUT2INC(a,n,d) PUT2(a,n,d), a += 2 +#elif defined COMPILE_PCRE16 +#define IMM2_SIZE 1 + +#define PUT2(a,n,d) \ + a[n] = d -/* When UTF-8 encoding is being used, a character is no longer just a single +#define GET2(a,n) \ + a[n] + +#elif defined COMPILE_PCRE32 + +#define IMM2_SIZE 1 + +#define PUT2(a,n,d) \ + a[n] = d + +#define GET2(a,n) \ + a[n] + +#else +#error Unsupported compiling mode +#endif /* COMPILE_PCRE[8|16|32] */ + +#define PUT2INC(a,n,d) PUT2(a,n,d), a += IMM2_SIZE + +/* The maximum length of a MARK name is currently one data unit; it may be +changed in future to be a fixed number of bytes or to depend on LINK_SIZE. */ + +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +#define MAX_MARK ((1u << 16) - 1) +#else +#define MAX_MARK ((1u << 8) - 1) +#endif + +/* When UTF encoding is being used, a character is no longer just a single byte. The macros for character handling generate simple sequences when used in -byte-mode, and more complicated ones for UTF-8 characters. BACKCHAR should -never be called in byte mode. To make sure it can never even appear when UTF-8 -support is omitted, we don't even define it. */ +character-mode, and more complicated ones for UTF characters. GETCHARLENTEST +and other macros are not used when UTF is not supported, so they are not +defined. To make sure they can never even appear when UTF support is omitted, +we don't even define them. */ -#ifndef SUPPORT_UTF8 -#define NEXTCHAR(p,end) p++; +#ifndef SUPPORT_UTF + +/* #define MAX_VALUE_FOR_SINGLE_CHAR */ +/* #define HAS_EXTRALEN(c) */ +/* #define GET_EXTRALEN(c) */ +/* #define NOT_FIRSTCHAR(c) */ #define GETCHAR(c, eptr) c = *eptr; #define GETCHARTEST(c, eptr) c = *eptr; #define GETCHARINC(c, eptr) c = *eptr++; #define GETCHARINCTEST(c, eptr) c = *eptr++; #define GETCHARLEN(c, eptr, len) c = *eptr; +#define RAWUCHAR(eptr) (*(eptr)) +#define RAWUCHARINC(eptr) (*(eptr)++) +#define RAWUCHARTEST(eptr) (*(eptr)) +#define RAWUCHARINCTEST(eptr) (*(eptr)++) +/* #define GETCHARLENTEST(c, eptr, len) */ /* #define BACKCHAR(eptr) */ +/* #define FORWARDCHAR(eptr) */ +/* #define ACROSSCHAR(condition, eptr, action) */ -#else /* SUPPORT_UTF8 */ +#else /* SUPPORT_UTF */ -/* Advance a character pointer one byte in non-UTF-8 mode and by one character -in UTF-8 mode. */ +/* Tests whether the code point needs extra characters to decode. */ -#define NEXTCHAR(p,end) \ - p++; \ - if (utf8) { while(p < end && (*p & 0xc0) == 0x80) p++; } +#define HASUTF8EXTRALEN(c) ((c) >= 0xc0) -/* Get the next UTF-8 character, not advancing the pointer. This is called when -we know we are in UTF-8 mode. */ +/* Base macro to pick up the remaining bytes of a UTF-8 character, not +advancing the pointer. */ -#define GETCHAR(c, eptr) \ - c = *eptr; \ - if (c >= 0xc0) \ +#define GETUTF8(c, eptr) \ { \ - int gcii; \ - int gcaa = _erts_pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ - int gcss = 6*gcaa; \ - c = (c & _erts_pcre_utf8_table3[gcaa]) << gcss; \ - for (gcii = 1; gcii <= gcaa; gcii++) \ + if ((c & 0x20) == 0) \ + c = ((c & 0x1f) << 6) | (eptr[1] & 0x3f); \ + else if ((c & 0x10) == 0) \ + c = ((c & 0x0f) << 12) | ((eptr[1] & 0x3f) << 6) | (eptr[2] & 0x3f); \ + else if ((c & 0x08) == 0) \ + c = ((c & 0x07) << 18) | ((eptr[1] & 0x3f) << 12) | \ + ((eptr[2] & 0x3f) << 6) | (eptr[3] & 0x3f); \ + else if ((c & 0x04) == 0) \ + c = ((c & 0x03) << 24) | ((eptr[1] & 0x3f) << 18) | \ + ((eptr[2] & 0x3f) << 12) | ((eptr[3] & 0x3f) << 6) | \ + (eptr[4] & 0x3f); \ + else \ + c = ((c & 0x01) << 30) | ((eptr[1] & 0x3f) << 24) | \ + ((eptr[2] & 0x3f) << 18) | ((eptr[3] & 0x3f) << 12) | \ + ((eptr[4] & 0x3f) << 6) | (eptr[5] & 0x3f); \ + } + +/* Base macro to pick up the remaining bytes of a UTF-8 character, advancing +the pointer. */ + +#define GETUTF8INC(c, eptr) \ + { \ + if ((c & 0x20) == 0) \ + c = ((c & 0x1f) << 6) | (*eptr++ & 0x3f); \ + else if ((c & 0x10) == 0) \ + { \ + c = ((c & 0x0f) << 12) | ((*eptr & 0x3f) << 6) | (eptr[1] & 0x3f); \ + eptr += 2; \ + } \ + else if ((c & 0x08) == 0) \ + { \ + c = ((c & 0x07) << 18) | ((*eptr & 0x3f) << 12) | \ + ((eptr[1] & 0x3f) << 6) | (eptr[2] & 0x3f); \ + eptr += 3; \ + } \ + else if ((c & 0x04) == 0) \ + { \ + c = ((c & 0x03) << 24) | ((*eptr & 0x3f) << 18) | \ + ((eptr[1] & 0x3f) << 12) | ((eptr[2] & 0x3f) << 6) | \ + (eptr[3] & 0x3f); \ + eptr += 4; \ + } \ + else \ { \ - gcss -= 6; \ - c |= (eptr[gcii] & 0x3f) << gcss; \ + c = ((c & 0x01) << 30) | ((*eptr & 0x3f) << 24) | \ + ((eptr[1] & 0x3f) << 18) | ((eptr[2] & 0x3f) << 12) | \ + ((eptr[3] & 0x3f) << 6) | (eptr[4] & 0x3f); \ + eptr += 5; \ } \ } +#if defined COMPILE_PCRE8 + +/* These macros were originally written in the form of loops that used data +from the tables whose names start with PRIV(utf8_table). They were rewritten by +a user so as not to use loops, because in some environments this gives a +significant performance advantage, and it seems never to do any harm. */ + +/* Tells the biggest code point which can be encoded as a single character. */ + +#define MAX_VALUE_FOR_SINGLE_CHAR 127 + +/* Tests whether the code point needs extra characters to decode. */ + +#define HAS_EXTRALEN(c) ((c) >= 0xc0) + +/* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE. +Otherwise it has an undefined behaviour. */ + +#define GET_EXTRALEN(c) (PRIV(utf8_table4)[(c) & 0x3f]) + +/* Returns TRUE, if the given character is not the first character +of a UTF sequence. */ + +#define NOT_FIRSTCHAR(c) (((c) & 0xc0) == 0x80) + +/* Get the next UTF-8 character, not advancing the pointer. This is called when +we know we are in UTF-8 mode. */ + +#define GETCHAR(c, eptr) \ + c = *eptr; \ + if (c >= 0xc0) GETUTF8(c, eptr); + /* Get the next UTF-8 character, testing for UTF-8 mode, and not advancing the pointer. */ #define GETCHARTEST(c, eptr) \ c = *eptr; \ - if (utf8 && c >= 0xc0) \ - { \ - int gcii; \ - int gcaa = _erts_pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ - int gcss = 6*gcaa; \ - c = (c & _erts_pcre_utf8_table3[gcaa]) << gcss; \ - for (gcii = 1; gcii <= gcaa; gcii++) \ - { \ - gcss -= 6; \ - c |= (eptr[gcii] & 0x3f) << gcss; \ - } \ - } + if (utf && c >= 0xc0) GETUTF8(c, eptr); /* Get the next UTF-8 character, advancing the pointer. This is called when we know we are in UTF-8 mode. */ #define GETCHARINC(c, eptr) \ c = *eptr++; \ - if (c >= 0xc0) \ - { \ - int gcaa = _erts_pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ - int gcss = 6*gcaa; \ - c = (c & _erts_pcre_utf8_table3[gcaa]) << gcss; \ - while (gcaa-- > 0) \ - { \ - gcss -= 6; \ - c |= (*eptr++ & 0x3f) << gcss; \ - } \ - } + if (c >= 0xc0) GETUTF8INC(c, eptr); -/* Get the next character, testing for UTF-8 mode, and advancing the pointer */ +/* Get the next character, testing for UTF-8 mode, and advancing the pointer. +This is called when we don't know if we are in UTF-8 mode. */ #define GETCHARINCTEST(c, eptr) \ c = *eptr++; \ - if (utf8 && c >= 0xc0) \ + if (utf && c >= 0xc0) GETUTF8INC(c, eptr); + +/* Base macro to pick up the remaining bytes of a UTF-8 character, not +advancing the pointer, incrementing the length. */ + +#define GETUTF8LEN(c, eptr, len) \ { \ - int gcaa = _erts_pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ - int gcss = 6*gcaa; \ - c = (c & _erts_pcre_utf8_table3[gcaa]) << gcss; \ - while (gcaa-- > 0) \ + if ((c & 0x20) == 0) \ + { \ + c = ((c & 0x1f) << 6) | (eptr[1] & 0x3f); \ + len++; \ + } \ + else if ((c & 0x10) == 0) \ + { \ + c = ((c & 0x0f) << 12) | ((eptr[1] & 0x3f) << 6) | (eptr[2] & 0x3f); \ + len += 2; \ + } \ + else if ((c & 0x08) == 0) \ + {\ + c = ((c & 0x07) << 18) | ((eptr[1] & 0x3f) << 12) | \ + ((eptr[2] & 0x3f) << 6) | (eptr[3] & 0x3f); \ + len += 3; \ + } \ + else if ((c & 0x04) == 0) \ { \ - gcss -= 6; \ - c |= (*eptr++ & 0x3f) << gcss; \ + c = ((c & 0x03) << 24) | ((eptr[1] & 0x3f) << 18) | \ + ((eptr[2] & 0x3f) << 12) | ((eptr[3] & 0x3f) << 6) | \ + (eptr[4] & 0x3f); \ + len += 4; \ + } \ + else \ + {\ + c = ((c & 0x01) << 30) | ((eptr[1] & 0x3f) << 24) | \ + ((eptr[2] & 0x3f) << 18) | ((eptr[3] & 0x3f) << 12) | \ + ((eptr[4] & 0x3f) << 6) | (eptr[5] & 0x3f); \ + len += 5; \ } \ } @@ -460,19 +772,39 @@ if there are extra bytes. This is called when we know we are in UTF-8 mode. */ #define GETCHARLEN(c, eptr, len) \ c = *eptr; \ - if (c >= 0xc0) \ - { \ - int gcii; \ - int gcaa = _erts_pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ - int gcss = 6*gcaa; \ - c = (c & _erts_pcre_utf8_table3[gcaa]) << gcss; \ - for (gcii = 1; gcii <= gcaa; gcii++) \ - { \ - gcss -= 6; \ - c |= (eptr[gcii] & 0x3f) << gcss; \ - } \ - len += gcaa; \ - } + if (c >= 0xc0) GETUTF8LEN(c, eptr, len); + +/* Get the next UTF-8 character, testing for UTF-8 mode, not advancing the +pointer, incrementing length if there are extra bytes. This is called when we +do not know if we are in UTF-8 mode. */ + +#define GETCHARLENTEST(c, eptr, len) \ + c = *eptr; \ + if (utf && c >= 0xc0) GETUTF8LEN(c, eptr, len); + +/* Returns the next uchar, not advancing the pointer. This is called when +we know we are in UTF mode. */ + +#define RAWUCHAR(eptr) \ + (*(eptr)) + +/* Returns the next uchar, advancing the pointer. This is called when +we know we are in UTF mode. */ + +#define RAWUCHARINC(eptr) \ + (*((eptr)++)) + +/* Returns the next uchar, testing for UTF mode, and not advancing the +pointer. */ + +#define RAWUCHARTEST(eptr) \ + (*(eptr)) + +/* Returns the next uchar, testing for UTF mode, advancing the +pointer. */ + +#define RAWUCHARINCTEST(eptr) \ + (*((eptr)++)) /* If the pointer is not at the start of a character, move it back until it is. This is called only in UTF-8 mode - we don't put a test within the macro @@ -480,35 +812,363 @@ because almost all calls are already within a block of UTF-8 only code. */ #define BACKCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr-- -#endif +/* Same as above, just in the other direction. */ +#define FORWARDCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr++ +/* Same as above, but it allows a fully customizable form. */ +#define ACROSSCHAR(condition, eptr, action) \ + while((condition) && ((eptr) & 0xc0) == 0x80) action -/* In case there is no definition of offsetof() provided - though any proper -Standard C system should have one. */ +#elif defined COMPILE_PCRE16 -#ifndef offsetof -#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field)) +/* Tells the biggest code point which can be encoded as a single character. */ + +#define MAX_VALUE_FOR_SINGLE_CHAR 65535 + +/* Tests whether the code point needs extra characters to decode. */ + +#define HAS_EXTRALEN(c) (((c) & 0xfc00) == 0xd800) + +/* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE. +Otherwise it has an undefined behaviour. */ + +#define GET_EXTRALEN(c) 1 + +/* Returns TRUE, if the given character is not the first character +of a UTF sequence. */ + +#define NOT_FIRSTCHAR(c) (((c) & 0xfc00) == 0xdc00) + +/* Base macro to pick up the low surrogate of a UTF-16 character, not +advancing the pointer. */ + +#define GETUTF16(c, eptr) \ + { c = (((c & 0x3ff) << 10) | (eptr[1] & 0x3ff)) + 0x10000; } + +/* Get the next UTF-16 character, not advancing the pointer. This is called when +we know we are in UTF-16 mode. */ + +#define GETCHAR(c, eptr) \ + c = *eptr; \ + if ((c & 0xfc00) == 0xd800) GETUTF16(c, eptr); + +/* Get the next UTF-16 character, testing for UTF-16 mode, and not advancing the +pointer. */ + +#define GETCHARTEST(c, eptr) \ + c = *eptr; \ + if (utf && (c & 0xfc00) == 0xd800) GETUTF16(c, eptr); + +/* Base macro to pick up the low surrogate of a UTF-16 character, advancing +the pointer. */ + +#define GETUTF16INC(c, eptr) \ + { c = (((c & 0x3ff) << 10) | (*eptr++ & 0x3ff)) + 0x10000; } + +/* Get the next UTF-16 character, advancing the pointer. This is called when we +know we are in UTF-16 mode. */ + +#define GETCHARINC(c, eptr) \ + c = *eptr++; \ + if ((c & 0xfc00) == 0xd800) GETUTF16INC(c, eptr); + +/* Get the next character, testing for UTF-16 mode, and advancing the pointer. +This is called when we don't know if we are in UTF-16 mode. */ + +#define GETCHARINCTEST(c, eptr) \ + c = *eptr++; \ + if (utf && (c & 0xfc00) == 0xd800) GETUTF16INC(c, eptr); + +/* Base macro to pick up the low surrogate of a UTF-16 character, not +advancing the pointer, incrementing the length. */ + +#define GETUTF16LEN(c, eptr, len) \ + { c = (((c & 0x3ff) << 10) | (eptr[1] & 0x3ff)) + 0x10000; len++; } + +/* Get the next UTF-16 character, not advancing the pointer, incrementing +length if there is a low surrogate. This is called when we know we are in +UTF-16 mode. */ + +#define GETCHARLEN(c, eptr, len) \ + c = *eptr; \ + if ((c & 0xfc00) == 0xd800) GETUTF16LEN(c, eptr, len); + +/* Get the next UTF-816character, testing for UTF-16 mode, not advancing the +pointer, incrementing length if there is a low surrogate. This is called when +we do not know if we are in UTF-16 mode. */ + +#define GETCHARLENTEST(c, eptr, len) \ + c = *eptr; \ + if (utf && (c & 0xfc00) == 0xd800) GETUTF16LEN(c, eptr, len); + +/* Returns the next uchar, not advancing the pointer. This is called when +we know we are in UTF mode. */ + +#define RAWUCHAR(eptr) \ + (*(eptr)) + +/* Returns the next uchar, advancing the pointer. This is called when +we know we are in UTF mode. */ + +#define RAWUCHARINC(eptr) \ + (*((eptr)++)) + +/* Returns the next uchar, testing for UTF mode, and not advancing the +pointer. */ + +#define RAWUCHARTEST(eptr) \ + (*(eptr)) + +/* Returns the next uchar, testing for UTF mode, advancing the +pointer. */ + +#define RAWUCHARINCTEST(eptr) \ + (*((eptr)++)) + +/* If the pointer is not at the start of a character, move it back until +it is. This is called only in UTF-16 mode - we don't put a test within the +macro because almost all calls are already within a block of UTF-16 only +code. */ + +#define BACKCHAR(eptr) if ((*eptr & 0xfc00) == 0xdc00) eptr-- + +/* Same as above, just in the other direction. */ +#define FORWARDCHAR(eptr) if ((*eptr & 0xfc00) == 0xdc00) eptr++ + +/* Same as above, but it allows a fully customizable form. */ +#define ACROSSCHAR(condition, eptr, action) \ + if ((condition) && ((eptr) & 0xfc00) == 0xdc00) action + +#elif defined COMPILE_PCRE32 + +/* These are trivial for the 32-bit library, since all UTF-32 characters fit +into one pcre_uchar unit. */ +#define MAX_VALUE_FOR_SINGLE_CHAR (0x10ffffu) +#define HAS_EXTRALEN(c) (0) +#define GET_EXTRALEN(c) (0) +#define NOT_FIRSTCHAR(c) (0) + +/* Get the next UTF-32 character, not advancing the pointer. This is called when +we know we are in UTF-32 mode. */ + +#define GETCHAR(c, eptr) \ + c = *(eptr); + +/* Get the next UTF-32 character, testing for UTF-32 mode, and not advancing the +pointer. */ + +#define GETCHARTEST(c, eptr) \ + c = *(eptr); + +/* Get the next UTF-32 character, advancing the pointer. This is called when we +know we are in UTF-32 mode. */ + +#define GETCHARINC(c, eptr) \ + c = *((eptr)++); + +/* Get the next character, testing for UTF-32 mode, and advancing the pointer. +This is called when we don't know if we are in UTF-32 mode. */ + +#define GETCHARINCTEST(c, eptr) \ + c = *((eptr)++); + +/* Get the next UTF-32 character, not advancing the pointer, not incrementing +length (since all UTF-32 is of length 1). This is called when we know we are in +UTF-32 mode. */ + +#define GETCHARLEN(c, eptr, len) \ + GETCHAR(c, eptr) + +/* Get the next UTF-32character, testing for UTF-32 mode, not advancing the +pointer, not incrementing the length (since all UTF-32 is of length 1). +This is called when we do not know if we are in UTF-32 mode. */ + +#define GETCHARLENTEST(c, eptr, len) \ + GETCHARTEST(c, eptr) + +/* Returns the next uchar, not advancing the pointer. This is called when +we know we are in UTF mode. */ + +#define RAWUCHAR(eptr) \ + (*(eptr)) + +/* Returns the next uchar, advancing the pointer. This is called when +we know we are in UTF mode. */ + +#define RAWUCHARINC(eptr) \ + (*((eptr)++)) + +/* Returns the next uchar, testing for UTF mode, and not advancing the +pointer. */ + +#define RAWUCHARTEST(eptr) \ + (*(eptr)) + +/* Returns the next uchar, testing for UTF mode, advancing the +pointer. */ + +#define RAWUCHARINCTEST(eptr) \ + (*((eptr)++)) + +/* If the pointer is not at the start of a character, move it back until +it is. This is called only in UTF-32 mode - we don't put a test within the +macro because almost all calls are already within a block of UTF-32 only +code. +These are all no-ops since all UTF-32 characters fit into one pcre_uchar. */ + +#define BACKCHAR(eptr) do { } while (0) + +/* Same as above, just in the other direction. */ +#define FORWARDCHAR(eptr) do { } while (0) + +/* Same as above, but it allows a fully customizable form. */ +#define ACROSSCHAR(condition, eptr, action) do { } while (0) + +#else +#error Unsupported compiling mode +#endif /* COMPILE_PCRE[8|16|32] */ + +#endif /* SUPPORT_UTF */ + +/* Tests for Unicode horizontal and vertical whitespace characters must check a +number of different values. Using a switch statement for this generates the +fastest code (no loop, no memory access), and there are several places in the +interpreter code where this happens. In order to ensure that all the case lists +remain in step, we use macros so that there is only one place where the lists +are defined. + +These values are also required as lists in pcre_compile.c when processing \h, +\H, \v and \V in a character class. The lists are defined in pcre_tables.c, but +macros that define the values are here so that all the definitions are +together. The lists must be in ascending character order, terminated by +NOTACHAR (which is 0xffffffff). + +Any changes should ensure that the various macros are kept in step with each +other. NOTE: The values also appear in pcre_jit_compile.c. */ + +/* ------ ASCII/Unicode environments ------ */ + +#ifndef EBCDIC + +#define HSPACE_LIST \ + CHAR_HT, CHAR_SPACE, 0xa0, \ + 0x1680, 0x180e, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, \ + 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202f, 0x205f, 0x3000, \ + NOTACHAR + +#define HSPACE_MULTIBYTE_CASES \ + case 0x1680: /* OGHAM SPACE MARK */ \ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ \ + case 0x2000: /* EN QUAD */ \ + case 0x2001: /* EM QUAD */ \ + case 0x2002: /* EN SPACE */ \ + case 0x2003: /* EM SPACE */ \ + case 0x2004: /* THREE-PER-EM SPACE */ \ + case 0x2005: /* FOUR-PER-EM SPACE */ \ + case 0x2006: /* SIX-PER-EM SPACE */ \ + case 0x2007: /* FIGURE SPACE */ \ + case 0x2008: /* PUNCTUATION SPACE */ \ + case 0x2009: /* THIN SPACE */ \ + case 0x200A: /* HAIR SPACE */ \ + case 0x202f: /* NARROW NO-BREAK SPACE */ \ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ \ + case 0x3000 /* IDEOGRAPHIC SPACE */ + +#define HSPACE_BYTE_CASES \ + case CHAR_HT: \ + case CHAR_SPACE: \ + case 0xa0 /* NBSP */ + +#define HSPACE_CASES \ + HSPACE_BYTE_CASES: \ + HSPACE_MULTIBYTE_CASES + +#define VSPACE_LIST \ + CHAR_LF, CHAR_VT, CHAR_FF, CHAR_CR, CHAR_NEL, 0x2028, 0x2029, NOTACHAR + +#define VSPACE_MULTIBYTE_CASES \ + case 0x2028: /* LINE SEPARATOR */ \ + case 0x2029 /* PARAGRAPH SEPARATOR */ + +#define VSPACE_BYTE_CASES \ + case CHAR_LF: \ + case CHAR_VT: \ + case CHAR_FF: \ + case CHAR_CR: \ + case CHAR_NEL + +#define VSPACE_CASES \ + VSPACE_BYTE_CASES: \ + VSPACE_MULTIBYTE_CASES + +/* ------ EBCDIC environments ------ */ + +#else +#define HSPACE_LIST CHAR_HT, CHAR_SPACE + +#define HSPACE_BYTE_CASES \ + case CHAR_HT: \ + case CHAR_SPACE + +#define HSPACE_CASES HSPACE_BYTE_CASES + +#ifdef EBCDIC_NL25 +#define VSPACE_LIST \ + CHAR_VT, CHAR_FF, CHAR_CR, CHAR_NEL, CHAR_LF, NOTACHAR +#else +#define VSPACE_LIST \ + CHAR_VT, CHAR_FF, CHAR_CR, CHAR_LF, CHAR_NEL, NOTACHAR #endif +#define VSPACE_BYTE_CASES \ + case CHAR_LF: \ + case CHAR_VT: \ + case CHAR_FF: \ + case CHAR_CR: \ + case CHAR_NEL -/* These are the public options that can change during matching. */ +#define VSPACE_CASES VSPACE_BYTE_CASES +#endif /* EBCDIC */ -#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL) +/* ------ End of whitespace macros ------ */ -/* Private flags containing information about the compiled regex. They used to -live at the top end of the options word, but that got almost full, so now they -are in a 16-bit flags word. */ -#define PCRE_NOPARTIAL 0x0001 /* can't use partial with this regex */ -#define PCRE_FIRSTSET 0x0002 /* first_byte is set */ -#define PCRE_REQCHSET 0x0004 /* req_byte is set */ -#define PCRE_STARTLINE 0x0008 /* start after \n for multiline */ -#define PCRE_JCHANGED 0x0010 /* j option used in regex */ -#define PCRE_HASCRORLF 0x0020 /* explicit \r or \n in pattern */ -/* Options for the "extra" block produced by pcre_study(). */ +/* Private flags containing information about the compiled regex. They used to +live at the top end of the options word, but that got almost full, so they were +moved to a 16-bit flags word - which got almost full, so now they are in a +32-bit flags word. From release 8.00, PCRE_NOPARTIAL is unused, as the +restrictions on partial matching have been lifted. It remains for backwards +compatibility. */ + +#define PCRE_MODE8 0x00000001 /* compiled in 8 bit mode */ +#define PCRE_MODE16 0x00000002 /* compiled in 16 bit mode */ +#define PCRE_MODE32 0x00000004 /* compiled in 32 bit mode */ +#define PCRE_FIRSTSET 0x00000010 /* first_char is set */ +#define PCRE_FCH_CASELESS 0x00000020 /* caseless first char */ +#define PCRE_REQCHSET 0x00000040 /* req_byte is set */ +#define PCRE_RCH_CASELESS 0x00000080 /* caseless requested char */ +#define PCRE_STARTLINE 0x00000100 /* start after \n for multiline */ +#define PCRE_NOPARTIAL 0x00000200 /* can't use partial with this regex */ +#define PCRE_JCHANGED 0x00000400 /* j option used in regex */ +#define PCRE_HASCRORLF 0x00000800 /* explicit \r or \n in pattern */ +#define PCRE_HASTHEN 0x00001000 /* pattern contains (*THEN) */ +#define PCRE_MLSET 0x00002000 /* match limit set by regex */ +#define PCRE_RLSET 0x00004000 /* recursion limit set by regex */ + +#if defined COMPILE_PCRE8 +#define PCRE_MODE PCRE_MODE8 +#elif defined COMPILE_PCRE16 +#define PCRE_MODE PCRE_MODE16 +#elif defined COMPILE_PCRE32 +#define PCRE_MODE PCRE_MODE32 +#endif +#define PCRE_MODE_MASK (PCRE_MODE8 | PCRE_MODE16 | PCRE_MODE32) -#define PCRE_STUDY_MAPPED 0x01 /* a map of starting chars exists */ +/* Flags for the "extra" block produced by pcre_study(). */ + +#define PCRE_STUDY_MAPPED 0x0001 /* a map of starting chars exists */ +#define PCRE_STUDY_MINLEN 0x0002 /* a minimum length field exists */ /* Masks for identifying the public options that are permitted at compile time, run time, or study time, respectively. */ @@ -516,86 +1176,690 @@ time, run time, or study time, respectively. */ #define PCRE_NEWLINE_BITS (PCRE_NEWLINE_CR|PCRE_NEWLINE_LF|PCRE_NEWLINE_ANY| \ PCRE_NEWLINE_ANYCRLF) -#define PUBLIC_OPTIONS \ +#define PUBLIC_COMPILE_OPTIONS \ (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8| \ PCRE_NO_AUTO_CAPTURE|PCRE_NO_UTF8_CHECK|PCRE_AUTO_CALLOUT|PCRE_FIRSTLINE| \ - PCRE_DUPNAMES|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE) + PCRE_DUPNAMES|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE| \ + PCRE_JAVASCRIPT_COMPAT|PCRE_UCP|PCRE_NO_START_OPTIMIZE|PCRE_NEVER_UTF) #define PUBLIC_EXEC_OPTIONS \ - (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NO_UTF8_CHECK| \ - PCRE_PARTIAL|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE) + (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NOTEMPTY_ATSTART| \ + PCRE_NO_UTF8_CHECK|PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT|PCRE_NEWLINE_BITS| \ + PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE|PCRE_NO_START_OPTIMIZE) #define PUBLIC_DFA_EXEC_OPTIONS \ - (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NO_UTF8_CHECK| \ - PCRE_PARTIAL|PCRE_DFA_SHORTEST|PCRE_DFA_RESTART|PCRE_NEWLINE_BITS| \ - PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE) + (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NOTEMPTY_ATSTART| \ + PCRE_NO_UTF8_CHECK|PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT|PCRE_DFA_SHORTEST| \ + PCRE_DFA_RESTART|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE| \ + PCRE_NO_START_OPTIMIZE) + +#define PUBLIC_STUDY_OPTIONS \ + (PCRE_STUDY_JIT_COMPILE|PCRE_STUDY_JIT_PARTIAL_SOFT_COMPILE| \ + PCRE_STUDY_JIT_PARTIAL_HARD_COMPILE|PCRE_STUDY_EXTRA_NEEDED) -#define PUBLIC_STUDY_OPTIONS 0 /* None defined */ +#define PUBLIC_JIT_EXEC_OPTIONS \ + (PCRE_NO_UTF8_CHECK|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|\ + PCRE_NOTEMPTY_ATSTART|PCRE_PARTIAL_SOFT|PCRE_PARTIAL_HARD) -/* Magic number to provide a small check against being handed junk. Also used -to detect whether a pattern was compiled on a host of different endianness. */ +/* Magic number to provide a small check against being handed junk. */ #define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ -/* Negative values for the firstchar and reqchar variables */ +/* This variable is used to detect a loaded regular expression +in different endianness. */ -#define REQ_UNSET (-2) -#define REQ_NONE (-1) +#define REVERSED_MAGIC_NUMBER 0x45524350UL /* 'ERCP' */ /* The maximum remaining length of subject we are prepared to search for a req_byte match. */ #define REQ_BYTE_MAX 1000 -/* Flags added to firstbyte or reqbyte; a "non-literal" item is either a -variable-length repeat, or a anything other than literal characters. */ - -#define REQ_CASELESS 0x0100 /* indicates caselessness */ -#define REQ_VARY 0x0200 /* reqbyte followed non-literal item */ - -/* Miscellaneous definitions */ +/* Miscellaneous definitions. The #ifndef is to pacify compiler warnings in +environments where these macros are defined elsewhere. Unfortunately, there +is no way to do the same for the typedef. */ typedef int BOOL; +#ifndef FALSE #define FALSE 0 #define TRUE 1 +#endif + +/* If PCRE is to support UTF-8 on EBCDIC platforms, we cannot use normal +character constants like '*' because the compiler would emit their EBCDIC code, +which is different from their ASCII/UTF-8 code. Instead we define macros for +the characters so that they always use the ASCII/UTF-8 code when UTF-8 support +is enabled. When UTF-8 support is not enabled, the definitions use character +literals. Both character and string versions of each character are needed, and +there are some longer strings as well. + +This means that, on EBCDIC platforms, the PCRE library can handle either +EBCDIC, or UTF-8, but not both. To support both in the same compiled library +would need different lookups depending on whether PCRE_UTF8 was set or not. +This would make it impossible to use characters in switch/case statements, +which would reduce performance. For a theoretical use (which nobody has asked +for) in a minority area (EBCDIC platforms), this is not sensible. Any +application that did need both could compile two versions of the library, using +macros to give the functions distinct names. */ + +#ifndef SUPPORT_UTF + +/* UTF-8 support is not enabled; use the platform-dependent character literals +so that PCRE works in both ASCII and EBCDIC environments, but only in non-UTF +mode. Newline characters are problematic in EBCDIC. Though it has CR and LF +characters, a common practice has been to use its NL (0x15) character as the +line terminator in C-like processing environments. However, sometimes the LF +(0x25) character is used instead, according to this Unicode document: + +http://unicode.org/standard/reports/tr13/tr13-5.html + +PCRE defaults EBCDIC NL to 0x15, but has a build-time option to select 0x25 +instead. Whichever is *not* chosen is defined as NEL. + +In both ASCII and EBCDIC environments, CHAR_NL and CHAR_LF are synonyms for the +same code point. */ + +#ifdef EBCDIC + +#ifndef EBCDIC_NL25 +#define CHAR_NL '\x15' +#define CHAR_NEL '\x25' +#define STR_NL "\x15" +#define STR_NEL "\x25" +#else +#define CHAR_NL '\x25' +#define CHAR_NEL '\x15' +#define STR_NL "\x25" +#define STR_NEL "\x15" +#endif + +#define CHAR_LF CHAR_NL +#define STR_LF STR_NL + +#define CHAR_ESC '\047' +#define CHAR_DEL '\007' +#define STR_ESC "\047" +#define STR_DEL "\007" + +#else /* Not EBCDIC */ + +/* In ASCII/Unicode, linefeed is '\n' and we equate this to NL for +compatibility. NEL is the Unicode newline character; make sure it is +a positive value. */ + +#define CHAR_LF '\n' +#define CHAR_NL CHAR_LF +#define CHAR_NEL ((unsigned char)'\x85') +#define CHAR_ESC '\033' +#define CHAR_DEL '\177' + +#define STR_LF "\n" +#define STR_NL STR_LF +#define STR_NEL "\x85" +#define STR_ESC "\033" +#define STR_DEL "\177" + +#endif /* EBCDIC */ + +/* The remaining definitions work in both environments. */ + +#define CHAR_NULL '\0' +#define CHAR_HT '\t' +#define CHAR_VT '\v' +#define CHAR_FF '\f' +#define CHAR_CR '\r' +#define CHAR_BS '\b' +#define CHAR_BEL '\a' + +#define CHAR_SPACE ' ' +#define CHAR_EXCLAMATION_MARK '!' +#define CHAR_QUOTATION_MARK '"' +#define CHAR_NUMBER_SIGN '#' +#define CHAR_DOLLAR_SIGN '$' +#define CHAR_PERCENT_SIGN '%' +#define CHAR_AMPERSAND '&' +#define CHAR_APOSTROPHE '\'' +#define CHAR_LEFT_PARENTHESIS '(' +#define CHAR_RIGHT_PARENTHESIS ')' +#define CHAR_ASTERISK '*' +#define CHAR_PLUS '+' +#define CHAR_COMMA ',' +#define CHAR_MINUS '-' +#define CHAR_DOT '.' +#define CHAR_SLASH '/' +#define CHAR_0 '0' +#define CHAR_1 '1' +#define CHAR_2 '2' +#define CHAR_3 '3' +#define CHAR_4 '4' +#define CHAR_5 '5' +#define CHAR_6 '6' +#define CHAR_7 '7' +#define CHAR_8 '8' +#define CHAR_9 '9' +#define CHAR_COLON ':' +#define CHAR_SEMICOLON ';' +#define CHAR_LESS_THAN_SIGN '<' +#define CHAR_EQUALS_SIGN '=' +#define CHAR_GREATER_THAN_SIGN '>' +#define CHAR_QUESTION_MARK '?' +#define CHAR_COMMERCIAL_AT '@' +#define CHAR_A 'A' +#define CHAR_B 'B' +#define CHAR_C 'C' +#define CHAR_D 'D' +#define CHAR_E 'E' +#define CHAR_F 'F' +#define CHAR_G 'G' +#define CHAR_H 'H' +#define CHAR_I 'I' +#define CHAR_J 'J' +#define CHAR_K 'K' +#define CHAR_L 'L' +#define CHAR_M 'M' +#define CHAR_N 'N' +#define CHAR_O 'O' +#define CHAR_P 'P' +#define CHAR_Q 'Q' +#define CHAR_R 'R' +#define CHAR_S 'S' +#define CHAR_T 'T' +#define CHAR_U 'U' +#define CHAR_V 'V' +#define CHAR_W 'W' +#define CHAR_X 'X' +#define CHAR_Y 'Y' +#define CHAR_Z 'Z' +#define CHAR_LEFT_SQUARE_BRACKET '[' +#define CHAR_BACKSLASH '\\' +#define CHAR_RIGHT_SQUARE_BRACKET ']' +#define CHAR_CIRCUMFLEX_ACCENT '^' +#define CHAR_UNDERSCORE '_' +#define CHAR_GRAVE_ACCENT '`' +#define CHAR_a 'a' +#define CHAR_b 'b' +#define CHAR_c 'c' +#define CHAR_d 'd' +#define CHAR_e 'e' +#define CHAR_f 'f' +#define CHAR_g 'g' +#define CHAR_h 'h' +#define CHAR_i 'i' +#define CHAR_j 'j' +#define CHAR_k 'k' +#define CHAR_l 'l' +#define CHAR_m 'm' +#define CHAR_n 'n' +#define CHAR_o 'o' +#define CHAR_p 'p' +#define CHAR_q 'q' +#define CHAR_r 'r' +#define CHAR_s 's' +#define CHAR_t 't' +#define CHAR_u 'u' +#define CHAR_v 'v' +#define CHAR_w 'w' +#define CHAR_x 'x' +#define CHAR_y 'y' +#define CHAR_z 'z' +#define CHAR_LEFT_CURLY_BRACKET '{' +#define CHAR_VERTICAL_LINE '|' +#define CHAR_RIGHT_CURLY_BRACKET '}' +#define CHAR_TILDE '~' + +#define STR_HT "\t" +#define STR_VT "\v" +#define STR_FF "\f" +#define STR_CR "\r" +#define STR_BS "\b" +#define STR_BEL "\a" + +#define STR_SPACE " " +#define STR_EXCLAMATION_MARK "!" +#define STR_QUOTATION_MARK "\"" +#define STR_NUMBER_SIGN "#" +#define STR_DOLLAR_SIGN "$" +#define STR_PERCENT_SIGN "%" +#define STR_AMPERSAND "&" +#define STR_APOSTROPHE "'" +#define STR_LEFT_PARENTHESIS "(" +#define STR_RIGHT_PARENTHESIS ")" +#define STR_ASTERISK "*" +#define STR_PLUS "+" +#define STR_COMMA "," +#define STR_MINUS "-" +#define STR_DOT "." +#define STR_SLASH "/" +#define STR_0 "0" +#define STR_1 "1" +#define STR_2 "2" +#define STR_3 "3" +#define STR_4 "4" +#define STR_5 "5" +#define STR_6 "6" +#define STR_7 "7" +#define STR_8 "8" +#define STR_9 "9" +#define STR_COLON ":" +#define STR_SEMICOLON ";" +#define STR_LESS_THAN_SIGN "<" +#define STR_EQUALS_SIGN "=" +#define STR_GREATER_THAN_SIGN ">" +#define STR_QUESTION_MARK "?" +#define STR_COMMERCIAL_AT "@" +#define STR_A "A" +#define STR_B "B" +#define STR_C "C" +#define STR_D "D" +#define STR_E "E" +#define STR_F "F" +#define STR_G "G" +#define STR_H "H" +#define STR_I "I" +#define STR_J "J" +#define STR_K "K" +#define STR_L "L" +#define STR_M "M" +#define STR_N "N" +#define STR_O "O" +#define STR_P "P" +#define STR_Q "Q" +#define STR_R "R" +#define STR_S "S" +#define STR_T "T" +#define STR_U "U" +#define STR_V "V" +#define STR_W "W" +#define STR_X "X" +#define STR_Y "Y" +#define STR_Z "Z" +#define STR_LEFT_SQUARE_BRACKET "[" +#define STR_BACKSLASH "\\" +#define STR_RIGHT_SQUARE_BRACKET "]" +#define STR_CIRCUMFLEX_ACCENT "^" +#define STR_UNDERSCORE "_" +#define STR_GRAVE_ACCENT "`" +#define STR_a "a" +#define STR_b "b" +#define STR_c "c" +#define STR_d "d" +#define STR_e "e" +#define STR_f "f" +#define STR_g "g" +#define STR_h "h" +#define STR_i "i" +#define STR_j "j" +#define STR_k "k" +#define STR_l "l" +#define STR_m "m" +#define STR_n "n" +#define STR_o "o" +#define STR_p "p" +#define STR_q "q" +#define STR_r "r" +#define STR_s "s" +#define STR_t "t" +#define STR_u "u" +#define STR_v "v" +#define STR_w "w" +#define STR_x "x" +#define STR_y "y" +#define STR_z "z" +#define STR_LEFT_CURLY_BRACKET "{" +#define STR_VERTICAL_LINE "|" +#define STR_RIGHT_CURLY_BRACKET "}" +#define STR_TILDE "~" + +#define STRING_ACCEPT0 "ACCEPT\0" +#define STRING_COMMIT0 "COMMIT\0" +#define STRING_F0 "F\0" +#define STRING_FAIL0 "FAIL\0" +#define STRING_MARK0 "MARK\0" +#define STRING_PRUNE0 "PRUNE\0" +#define STRING_SKIP0 "SKIP\0" +#define STRING_THEN "THEN" + +#define STRING_alpha0 "alpha\0" +#define STRING_lower0 "lower\0" +#define STRING_upper0 "upper\0" +#define STRING_alnum0 "alnum\0" +#define STRING_ascii0 "ascii\0" +#define STRING_blank0 "blank\0" +#define STRING_cntrl0 "cntrl\0" +#define STRING_digit0 "digit\0" +#define STRING_graph0 "graph\0" +#define STRING_print0 "print\0" +#define STRING_punct0 "punct\0" +#define STRING_space0 "space\0" +#define STRING_word0 "word\0" +#define STRING_xdigit "xdigit" + +#define STRING_DEFINE "DEFINE" + +#define STRING_CR_RIGHTPAR "CR)" +#define STRING_LF_RIGHTPAR "LF)" +#define STRING_CRLF_RIGHTPAR "CRLF)" +#define STRING_ANY_RIGHTPAR "ANY)" +#define STRING_ANYCRLF_RIGHTPAR "ANYCRLF)" +#define STRING_BSR_ANYCRLF_RIGHTPAR "BSR_ANYCRLF)" +#define STRING_BSR_UNICODE_RIGHTPAR "BSR_UNICODE)" +#define STRING_UTF8_RIGHTPAR "UTF8)" +#define STRING_UTF16_RIGHTPAR "UTF16)" +#define STRING_UTF32_RIGHTPAR "UTF32)" +#define STRING_UTF_RIGHTPAR "UTF)" +#define STRING_UCP_RIGHTPAR "UCP)" +#define STRING_NO_START_OPT_RIGHTPAR "NO_START_OPT)" +#define STRING_LIMIT_MATCH_EQ "LIMIT_MATCH=" +#define STRING_LIMIT_RECURSION_EQ "LIMIT_RECURSION=" + +#else /* SUPPORT_UTF */ + +/* UTF-8 support is enabled; always use UTF-8 (=ASCII) character codes. This +works in both modes non-EBCDIC platforms, and on EBCDIC platforms in UTF-8 mode +only. */ + +#define CHAR_HT '\011' +#define CHAR_VT '\013' +#define CHAR_FF '\014' +#define CHAR_CR '\015' +#define CHAR_LF '\012' +#define CHAR_NL CHAR_LF +#define CHAR_NEL ((unsigned char)'\x85') +#define CHAR_BS '\010' +#define CHAR_BEL '\007' +#define CHAR_ESC '\033' +#define CHAR_DEL '\177' + +#define CHAR_NULL '\0' +#define CHAR_SPACE '\040' +#define CHAR_EXCLAMATION_MARK '\041' +#define CHAR_QUOTATION_MARK '\042' +#define CHAR_NUMBER_SIGN '\043' +#define CHAR_DOLLAR_SIGN '\044' +#define CHAR_PERCENT_SIGN '\045' +#define CHAR_AMPERSAND '\046' +#define CHAR_APOSTROPHE '\047' +#define CHAR_LEFT_PARENTHESIS '\050' +#define CHAR_RIGHT_PARENTHESIS '\051' +#define CHAR_ASTERISK '\052' +#define CHAR_PLUS '\053' +#define CHAR_COMMA '\054' +#define CHAR_MINUS '\055' +#define CHAR_DOT '\056' +#define CHAR_SLASH '\057' +#define CHAR_0 '\060' +#define CHAR_1 '\061' +#define CHAR_2 '\062' +#define CHAR_3 '\063' +#define CHAR_4 '\064' +#define CHAR_5 '\065' +#define CHAR_6 '\066' +#define CHAR_7 '\067' +#define CHAR_8 '\070' +#define CHAR_9 '\071' +#define CHAR_COLON '\072' +#define CHAR_SEMICOLON '\073' +#define CHAR_LESS_THAN_SIGN '\074' +#define CHAR_EQUALS_SIGN '\075' +#define CHAR_GREATER_THAN_SIGN '\076' +#define CHAR_QUESTION_MARK '\077' +#define CHAR_COMMERCIAL_AT '\100' +#define CHAR_A '\101' +#define CHAR_B '\102' +#define CHAR_C '\103' +#define CHAR_D '\104' +#define CHAR_E '\105' +#define CHAR_F '\106' +#define CHAR_G '\107' +#define CHAR_H '\110' +#define CHAR_I '\111' +#define CHAR_J '\112' +#define CHAR_K '\113' +#define CHAR_L '\114' +#define CHAR_M '\115' +#define CHAR_N '\116' +#define CHAR_O '\117' +#define CHAR_P '\120' +#define CHAR_Q '\121' +#define CHAR_R '\122' +#define CHAR_S '\123' +#define CHAR_T '\124' +#define CHAR_U '\125' +#define CHAR_V '\126' +#define CHAR_W '\127' +#define CHAR_X '\130' +#define CHAR_Y '\131' +#define CHAR_Z '\132' +#define CHAR_LEFT_SQUARE_BRACKET '\133' +#define CHAR_BACKSLASH '\134' +#define CHAR_RIGHT_SQUARE_BRACKET '\135' +#define CHAR_CIRCUMFLEX_ACCENT '\136' +#define CHAR_UNDERSCORE '\137' +#define CHAR_GRAVE_ACCENT '\140' +#define CHAR_a '\141' +#define CHAR_b '\142' +#define CHAR_c '\143' +#define CHAR_d '\144' +#define CHAR_e '\145' +#define CHAR_f '\146' +#define CHAR_g '\147' +#define CHAR_h '\150' +#define CHAR_i '\151' +#define CHAR_j '\152' +#define CHAR_k '\153' +#define CHAR_l '\154' +#define CHAR_m '\155' +#define CHAR_n '\156' +#define CHAR_o '\157' +#define CHAR_p '\160' +#define CHAR_q '\161' +#define CHAR_r '\162' +#define CHAR_s '\163' +#define CHAR_t '\164' +#define CHAR_u '\165' +#define CHAR_v '\166' +#define CHAR_w '\167' +#define CHAR_x '\170' +#define CHAR_y '\171' +#define CHAR_z '\172' +#define CHAR_LEFT_CURLY_BRACKET '\173' +#define CHAR_VERTICAL_LINE '\174' +#define CHAR_RIGHT_CURLY_BRACKET '\175' +#define CHAR_TILDE '\176' + +#define STR_HT "\011" +#define STR_VT "\013" +#define STR_FF "\014" +#define STR_CR "\015" +#define STR_NL "\012" +#define STR_BS "\010" +#define STR_BEL "\007" +#define STR_ESC "\033" +#define STR_DEL "\177" + +#define STR_SPACE "\040" +#define STR_EXCLAMATION_MARK "\041" +#define STR_QUOTATION_MARK "\042" +#define STR_NUMBER_SIGN "\043" +#define STR_DOLLAR_SIGN "\044" +#define STR_PERCENT_SIGN "\045" +#define STR_AMPERSAND "\046" +#define STR_APOSTROPHE "\047" +#define STR_LEFT_PARENTHESIS "\050" +#define STR_RIGHT_PARENTHESIS "\051" +#define STR_ASTERISK "\052" +#define STR_PLUS "\053" +#define STR_COMMA "\054" +#define STR_MINUS "\055" +#define STR_DOT "\056" +#define STR_SLASH "\057" +#define STR_0 "\060" +#define STR_1 "\061" +#define STR_2 "\062" +#define STR_3 "\063" +#define STR_4 "\064" +#define STR_5 "\065" +#define STR_6 "\066" +#define STR_7 "\067" +#define STR_8 "\070" +#define STR_9 "\071" +#define STR_COLON "\072" +#define STR_SEMICOLON "\073" +#define STR_LESS_THAN_SIGN "\074" +#define STR_EQUALS_SIGN "\075" +#define STR_GREATER_THAN_SIGN "\076" +#define STR_QUESTION_MARK "\077" +#define STR_COMMERCIAL_AT "\100" +#define STR_A "\101" +#define STR_B "\102" +#define STR_C "\103" +#define STR_D "\104" +#define STR_E "\105" +#define STR_F "\106" +#define STR_G "\107" +#define STR_H "\110" +#define STR_I "\111" +#define STR_J "\112" +#define STR_K "\113" +#define STR_L "\114" +#define STR_M "\115" +#define STR_N "\116" +#define STR_O "\117" +#define STR_P "\120" +#define STR_Q "\121" +#define STR_R "\122" +#define STR_S "\123" +#define STR_T "\124" +#define STR_U "\125" +#define STR_V "\126" +#define STR_W "\127" +#define STR_X "\130" +#define STR_Y "\131" +#define STR_Z "\132" +#define STR_LEFT_SQUARE_BRACKET "\133" +#define STR_BACKSLASH "\134" +#define STR_RIGHT_SQUARE_BRACKET "\135" +#define STR_CIRCUMFLEX_ACCENT "\136" +#define STR_UNDERSCORE "\137" +#define STR_GRAVE_ACCENT "\140" +#define STR_a "\141" +#define STR_b "\142" +#define STR_c "\143" +#define STR_d "\144" +#define STR_e "\145" +#define STR_f "\146" +#define STR_g "\147" +#define STR_h "\150" +#define STR_i "\151" +#define STR_j "\152" +#define STR_k "\153" +#define STR_l "\154" +#define STR_m "\155" +#define STR_n "\156" +#define STR_o "\157" +#define STR_p "\160" +#define STR_q "\161" +#define STR_r "\162" +#define STR_s "\163" +#define STR_t "\164" +#define STR_u "\165" +#define STR_v "\166" +#define STR_w "\167" +#define STR_x "\170" +#define STR_y "\171" +#define STR_z "\172" +#define STR_LEFT_CURLY_BRACKET "\173" +#define STR_VERTICAL_LINE "\174" +#define STR_RIGHT_CURLY_BRACKET "\175" +#define STR_TILDE "\176" + +#define STRING_ACCEPT0 STR_A STR_C STR_C STR_E STR_P STR_T "\0" +#define STRING_COMMIT0 STR_C STR_O STR_M STR_M STR_I STR_T "\0" +#define STRING_F0 STR_F "\0" +#define STRING_FAIL0 STR_F STR_A STR_I STR_L "\0" +#define STRING_MARK0 STR_M STR_A STR_R STR_K "\0" +#define STRING_PRUNE0 STR_P STR_R STR_U STR_N STR_E "\0" +#define STRING_SKIP0 STR_S STR_K STR_I STR_P "\0" +#define STRING_THEN STR_T STR_H STR_E STR_N + +#define STRING_alpha0 STR_a STR_l STR_p STR_h STR_a "\0" +#define STRING_lower0 STR_l STR_o STR_w STR_e STR_r "\0" +#define STRING_upper0 STR_u STR_p STR_p STR_e STR_r "\0" +#define STRING_alnum0 STR_a STR_l STR_n STR_u STR_m "\0" +#define STRING_ascii0 STR_a STR_s STR_c STR_i STR_i "\0" +#define STRING_blank0 STR_b STR_l STR_a STR_n STR_k "\0" +#define STRING_cntrl0 STR_c STR_n STR_t STR_r STR_l "\0" +#define STRING_digit0 STR_d STR_i STR_g STR_i STR_t "\0" +#define STRING_graph0 STR_g STR_r STR_a STR_p STR_h "\0" +#define STRING_print0 STR_p STR_r STR_i STR_n STR_t "\0" +#define STRING_punct0 STR_p STR_u STR_n STR_c STR_t "\0" +#define STRING_space0 STR_s STR_p STR_a STR_c STR_e "\0" +#define STRING_word0 STR_w STR_o STR_r STR_d "\0" +#define STRING_xdigit STR_x STR_d STR_i STR_g STR_i STR_t + +#define STRING_DEFINE STR_D STR_E STR_F STR_I STR_N STR_E + +#define STRING_CR_RIGHTPAR STR_C STR_R STR_RIGHT_PARENTHESIS +#define STRING_LF_RIGHTPAR STR_L STR_F STR_RIGHT_PARENTHESIS +#define STRING_CRLF_RIGHTPAR STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS +#define STRING_ANY_RIGHTPAR STR_A STR_N STR_Y STR_RIGHT_PARENTHESIS +#define STRING_ANYCRLF_RIGHTPAR STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS +#define STRING_BSR_ANYCRLF_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS +#define STRING_BSR_UNICODE_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_U STR_N STR_I STR_C STR_O STR_D STR_E STR_RIGHT_PARENTHESIS +#define STRING_UTF8_RIGHTPAR STR_U STR_T STR_F STR_8 STR_RIGHT_PARENTHESIS +#define STRING_UTF16_RIGHTPAR STR_U STR_T STR_F STR_1 STR_6 STR_RIGHT_PARENTHESIS +#define STRING_UTF32_RIGHTPAR STR_U STR_T STR_F STR_3 STR_2 STR_RIGHT_PARENTHESIS +#define STRING_UTF_RIGHTPAR STR_U STR_T STR_F STR_RIGHT_PARENTHESIS +#define STRING_UCP_RIGHTPAR STR_U STR_C STR_P STR_RIGHT_PARENTHESIS +#define STRING_NO_START_OPT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_S STR_T STR_A STR_R STR_T STR_UNDERSCORE STR_O STR_P STR_T STR_RIGHT_PARENTHESIS +#define STRING_LIMIT_MATCH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_M STR_A STR_T STR_C STR_H STR_EQUALS_SIGN +#define STRING_LIMIT_RECURSION_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_R STR_E STR_C STR_U STR_R STR_S STR_I STR_O STR_N STR_EQUALS_SIGN + +#endif /* SUPPORT_UTF */ /* Escape items that are just an encoding of a particular data value. */ #ifndef ESC_e -#define ESC_e 27 +#define ESC_e CHAR_ESC #endif #ifndef ESC_f -#define ESC_f '\f' +#define ESC_f CHAR_FF #endif #ifndef ESC_n -#define ESC_n '\n' +#define ESC_n CHAR_LF #endif #ifndef ESC_r -#define ESC_r '\r' +#define ESC_r CHAR_CR #endif /* We can't officially use ESC_t because it is a POSIX reserved identifier (presumably because of all the others like size_t). */ #ifndef ESC_tee -#define ESC_tee '\t' +#define ESC_tee CHAR_HT #endif /* Codes for different types of Unicode property */ #define PT_ANY 0 /* Any property - matches all chars */ #define PT_LAMP 1 /* L& - the union of Lu, Ll, Lt */ -#define PT_GC 2 /* General characteristic (e.g. L) */ -#define PT_PC 3 /* Particular characteristic (e.g. Lu) */ +#define PT_GC 2 /* Specified general characteristic (e.g. L) */ +#define PT_PC 3 /* Specified particular characteristic (e.g. Lu) */ #define PT_SC 4 /* Script (e.g. Han) */ +#define PT_ALNUM 5 /* Alphanumeric - the union of L and N */ +#define PT_SPACE 6 /* Perl space - Z plus 9,10,12,13 */ +#define PT_PXSPACE 7 /* POSIX space - Z plus 9,10,11,12,13 */ +#define PT_WORD 8 /* Word - L plus N plus underscore */ +#define PT_CLIST 9 /* Pseudo-property: match character list */ +#define PT_UCNC 10 /* Universal Character nameable character */ /* Flag bits and data types for the extended class (OP_XCLASS) for classes that -contain UTF-8 characters with values greater than 255. */ +contain characters with values greater than 255. */ #define XCL_NOT 0x01 /* Flag: this is a negative class */ #define XCL_MAP 0x02 /* Flag: a 32-byte map is present */ @@ -608,26 +1872,36 @@ contain UTF-8 characters with values greater than 255. */ /* These are escaped items that aren't just an encoding of a particular data value such as \n. They must have non-zero values, as check_escape() returns -their negation. Also, they must appear in the same order as in the opcode -definitions below, up to ESC_z. There's a dummy for OP_ANY because it -corresponds to "." rather than an escape sequence. The final one must be -ESC_REF as subsequent values are used for backreferences (\1, \2, \3, etc). -There are two tests in the code for an escape greater than ESC_b and less than -ESC_Z to detect the types that may be repeated. These are the types that -consume characters. If any new escapes are put in between that don't consume a -character, that code will have to change. */ +0 for a data character. Also, they must appear in the same order as in the opcode +definitions below, up to ESC_z. There's a dummy for OP_ALLANY because it +corresponds to "." in DOTALL mode rather than an escape sequence. It is also +used for [^] in JavaScript compatibility mode, and for \C in non-utf mode. In +non-DOTALL mode, "." behaves like \N. + +The special values ESC_DU, ESC_du, etc. are used instead of ESC_D, ESC_d, etc. +when PCRE_UCP is set and replacement of \d etc by \p sequences is required. +They must be contiguous, and remain in order so that the replacements can be +looked up from a table. + +Negative numbers are used to encode a backreference (\1, \2, \3, etc.) in +check_escape(). There are two tests in the code for an escape +greater than ESC_b and less than ESC_Z to detect the types that may be +repeated. These are the types that consume characters. If any new escapes are +put in between that don't consume a character, that code will have to change. +*/ enum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, - ESC_W, ESC_w, ESC_dum1, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H, ESC_h, - ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z, ESC_E, ESC_Q, ESC_k, ESC_REF }; - + ESC_W, ESC_w, ESC_N, ESC_dum, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H, + ESC_h, ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z, + ESC_E, ESC_Q, ESC_g, ESC_k, + ESC_DU, ESC_du, ESC_SU, ESC_su, ESC_WU, ESC_wu }; /* Opcode table: Starting from 1 (i.e. after OP_END), the values up to OP_EOD must correspond in order to the list of escapes immediately above. *** NOTE NOTE NOTE *** Whenever this list is updated, the two macro definitions -that follow must also be updated to match. There is also a table called -"coptable" in pcre_dfa_exec.c that must be updated. */ +that follow must also be updated to match. There are also tables called +"coptable" and "poptable" in pcre_dfa_exec.c that must be updated. */ enum { OP_END, /* 0 End of pattern */ @@ -645,166 +1919,274 @@ enum { OP_WHITESPACE, /* 9 \s */ OP_NOT_WORDCHAR, /* 10 \W */ OP_WORDCHAR, /* 11 \w */ - OP_ANY, /* 12 Match any character */ - OP_ANYBYTE, /* 13 Match any byte (\C); different to OP_ANY for UTF-8 */ - OP_NOTPROP, /* 14 \P (not Unicode property) */ - OP_PROP, /* 15 \p (Unicode property) */ - OP_ANYNL, /* 16 \R (any newline sequence) */ - OP_NOT_HSPACE, /* 17 \H (not horizontal whitespace) */ - OP_HSPACE, /* 18 \h (horizontal whitespace) */ - OP_NOT_VSPACE, /* 19 \V (not vertical whitespace) */ - OP_VSPACE, /* 20 \v (vertical whitespace) */ - OP_EXTUNI, /* 21 \X (extended Unicode sequence */ - OP_EODN, /* 22 End of data or \n at end of data: \Z. */ - OP_EOD, /* 23 End of data: \z */ - - OP_OPT, /* 24 Set runtime options */ - OP_CIRC, /* 25 Start of line - varies with multiline switch */ - OP_DOLL, /* 26 End of line - varies with multiline switch */ - OP_CHAR, /* 27 Match one character, casefully */ - OP_CHARNC, /* 28 Match one character, caselessly */ - OP_NOT, /* 29 Match one character, not the following one */ - - OP_STAR, /* 30 The maximizing and minimizing versions of */ - OP_MINSTAR, /* 31 these six opcodes must come in pairs, with */ - OP_PLUS, /* 32 the minimizing one second. */ - OP_MINPLUS, /* 33 This first set applies to single characters.*/ - OP_QUERY, /* 34 */ - OP_MINQUERY, /* 35 */ - - OP_UPTO, /* 36 From 0 to n matches */ - OP_MINUPTO, /* 37 */ - OP_EXACT, /* 38 Exactly n matches */ - - OP_POSSTAR, /* 39 Possessified star */ - OP_POSPLUS, /* 40 Possessified plus */ - OP_POSQUERY, /* 41 Posesssified query */ - OP_POSUPTO, /* 42 Possessified upto */ - - OP_NOTSTAR, /* 43 The maximizing and minimizing versions of */ - OP_NOTMINSTAR, /* 44 these six opcodes must come in pairs, with */ - OP_NOTPLUS, /* 45 the minimizing one second. They must be in */ - OP_NOTMINPLUS, /* 46 exactly the same order as those above. */ - OP_NOTQUERY, /* 47 This set applies to "not" single characters. */ - OP_NOTMINQUERY, /* 48 */ - - OP_NOTUPTO, /* 49 From 0 to n matches */ - OP_NOTMINUPTO, /* 50 */ - OP_NOTEXACT, /* 51 Exactly n matches */ - - OP_NOTPOSSTAR, /* 52 Possessified versions */ - OP_NOTPOSPLUS, /* 53 */ - OP_NOTPOSQUERY, /* 54 */ - OP_NOTPOSUPTO, /* 55 */ - - OP_TYPESTAR, /* 56 The maximizing and minimizing versions of */ - OP_TYPEMINSTAR, /* 57 these six opcodes must come in pairs, with */ - OP_TYPEPLUS, /* 58 the minimizing one second. These codes must */ - OP_TYPEMINPLUS, /* 59 be in exactly the same order as those above. */ - OP_TYPEQUERY, /* 60 This set applies to character types such as \d */ - OP_TYPEMINQUERY, /* 61 */ - - OP_TYPEUPTO, /* 62 From 0 to n matches */ - OP_TYPEMINUPTO, /* 63 */ - OP_TYPEEXACT, /* 64 Exactly n matches */ - - OP_TYPEPOSSTAR, /* 65 Possessified versions */ - OP_TYPEPOSPLUS, /* 66 */ - OP_TYPEPOSQUERY, /* 67 */ - OP_TYPEPOSUPTO, /* 68 */ - - OP_CRSTAR, /* 69 The maximizing and minimizing versions of */ - OP_CRMINSTAR, /* 70 all these opcodes must come in pairs, with */ - OP_CRPLUS, /* 71 the minimizing one second. These codes must */ - OP_CRMINPLUS, /* 72 be in exactly the same order as those above. */ - OP_CRQUERY, /* 73 These are for character classes and back refs */ - OP_CRMINQUERY, /* 74 */ - OP_CRRANGE, /* 75 These are different to the three sets above. */ - OP_CRMINRANGE, /* 76 */ - - OP_CLASS, /* 77 Match a character class, chars < 256 only */ - OP_NCLASS, /* 78 Same, but the bitmap was created from a negative - class - the difference is relevant only when a UTF-8 - character > 255 is encountered. */ - - OP_XCLASS, /* 79 Extended class for handling UTF-8 chars within the - class. This does both positive and negative. */ - - OP_REF, /* 80 Match a back reference */ - OP_RECURSE, /* 81 Match a numbered subpattern (possibly recursive) */ - OP_CALLOUT, /* 82 Call out to external function if provided */ - - OP_ALT, /* 83 Start of alternation */ - OP_KET, /* 84 End of group that doesn't have an unbounded repeat */ - OP_KETRMAX, /* 85 These two must remain together and in this */ - OP_KETRMIN, /* 86 order. They are for groups the repeat for ever. */ - - /* The assertions must come before BRA, CBRA, ONCE, and COND.*/ - - OP_ASSERT, /* 87 Positive lookahead */ - OP_ASSERT_NOT, /* 88 Negative lookahead */ - OP_ASSERTBACK, /* 89 Positive lookbehind */ - OP_ASSERTBACK_NOT, /* 90 Negative lookbehind */ - OP_REVERSE, /* 91 Move pointer back - used in lookbehind assertions */ - - /* ONCE, BRA, CBRA, and COND must come after the assertions, with ONCE first, - as there's a test for >= ONCE for a subpattern that isn't an assertion. */ - - OP_ONCE, /* 92 Atomic group */ - OP_BRA, /* 93 Start of non-capturing bracket */ - OP_CBRA, /* 94 Start of capturing bracket */ - OP_COND, /* 95 Conditional group */ - - /* These three must follow the previous three, in the same order. There's a + + OP_ANY, /* 12 Match any character except newline (\N) */ + OP_ALLANY, /* 13 Match any character */ + OP_ANYBYTE, /* 14 Match any byte (\C); different to OP_ANY for UTF-8 */ + OP_NOTPROP, /* 15 \P (not Unicode property) */ + OP_PROP, /* 16 \p (Unicode property) */ + OP_ANYNL, /* 17 \R (any newline sequence) */ + OP_NOT_HSPACE, /* 18 \H (not horizontal whitespace) */ + OP_HSPACE, /* 19 \h (horizontal whitespace) */ + OP_NOT_VSPACE, /* 20 \V (not vertical whitespace) */ + OP_VSPACE, /* 21 \v (vertical whitespace) */ + OP_EXTUNI, /* 22 \X (extended Unicode sequence */ + OP_EODN, /* 23 End of data or \n at end of data (\Z) */ + OP_EOD, /* 24 End of data (\z) */ + + OP_CIRC, /* 25 Start of line - not multiline */ + OP_CIRCM, /* 26 Start of line - multiline */ + OP_DOLL, /* 27 End of line - not multiline */ + OP_DOLLM, /* 28 End of line - multiline */ + OP_CHAR, /* 29 Match one character, casefully */ + OP_CHARI, /* 30 Match one character, caselessly */ + OP_NOT, /* 31 Match one character, not the given one, casefully */ + OP_NOTI, /* 32 Match one character, not the given one, caselessly */ + + /* The following sets of 13 opcodes must always be kept in step because + the offset from the first one is used to generate the others. */ + + /**** Single characters, caseful, must precede the caseless ones ****/ + + OP_STAR, /* 33 The maximizing and minimizing versions of */ + OP_MINSTAR, /* 34 these six opcodes must come in pairs, with */ + OP_PLUS, /* 35 the minimizing one second. */ + OP_MINPLUS, /* 36 */ + OP_QUERY, /* 37 */ + OP_MINQUERY, /* 38 */ + + OP_UPTO, /* 39 From 0 to n matches of one character, caseful*/ + OP_MINUPTO, /* 40 */ + OP_EXACT, /* 41 Exactly n matches */ + + OP_POSSTAR, /* 42 Possessified star, caseful */ + OP_POSPLUS, /* 43 Possessified plus, caseful */ + OP_POSQUERY, /* 44 Posesssified query, caseful */ + OP_POSUPTO, /* 45 Possessified upto, caseful */ + + /**** Single characters, caseless, must follow the caseful ones */ + + OP_STARI, /* 46 */ + OP_MINSTARI, /* 47 */ + OP_PLUSI, /* 48 */ + OP_MINPLUSI, /* 49 */ + OP_QUERYI, /* 50 */ + OP_MINQUERYI, /* 51 */ + + OP_UPTOI, /* 52 From 0 to n matches of one character, caseless */ + OP_MINUPTOI, /* 53 */ + OP_EXACTI, /* 54 */ + + OP_POSSTARI, /* 55 Possessified star, caseless */ + OP_POSPLUSI, /* 56 Possessified plus, caseless */ + OP_POSQUERYI, /* 57 Posesssified query, caseless */ + OP_POSUPTOI, /* 58 Possessified upto, caseless */ + + /**** The negated ones must follow the non-negated ones, and match them ****/ + /**** Negated single character, caseful; must precede the caseless ones ****/ + + OP_NOTSTAR, /* 59 The maximizing and minimizing versions of */ + OP_NOTMINSTAR, /* 60 these six opcodes must come in pairs, with */ + OP_NOTPLUS, /* 61 the minimizing one second. They must be in */ + OP_NOTMINPLUS, /* 62 exactly the same order as those above. */ + OP_NOTQUERY, /* 63 */ + OP_NOTMINQUERY, /* 64 */ + + OP_NOTUPTO, /* 65 From 0 to n matches, caseful */ + OP_NOTMINUPTO, /* 66 */ + OP_NOTEXACT, /* 67 Exactly n matches */ + + OP_NOTPOSSTAR, /* 68 Possessified versions, caseful */ + OP_NOTPOSPLUS, /* 69 */ + OP_NOTPOSQUERY, /* 70 */ + OP_NOTPOSUPTO, /* 71 */ + + /**** Negated single character, caseless; must follow the caseful ones ****/ + + OP_NOTSTARI, /* 72 */ + OP_NOTMINSTARI, /* 73 */ + OP_NOTPLUSI, /* 74 */ + OP_NOTMINPLUSI, /* 75 */ + OP_NOTQUERYI, /* 76 */ + OP_NOTMINQUERYI, /* 77 */ + + OP_NOTUPTOI, /* 78 From 0 to n matches, caseless */ + OP_NOTMINUPTOI, /* 79 */ + OP_NOTEXACTI, /* 80 Exactly n matches */ + + OP_NOTPOSSTARI, /* 81 Possessified versions, caseless */ + OP_NOTPOSPLUSI, /* 82 */ + OP_NOTPOSQUERYI, /* 83 */ + OP_NOTPOSUPTOI, /* 84 */ + + /**** Character types ****/ + + OP_TYPESTAR, /* 85 The maximizing and minimizing versions of */ + OP_TYPEMINSTAR, /* 86 these six opcodes must come in pairs, with */ + OP_TYPEPLUS, /* 87 the minimizing one second. These codes must */ + OP_TYPEMINPLUS, /* 88 be in exactly the same order as those above. */ + OP_TYPEQUERY, /* 89 */ + OP_TYPEMINQUERY, /* 90 */ + + OP_TYPEUPTO, /* 91 From 0 to n matches */ + OP_TYPEMINUPTO, /* 92 */ + OP_TYPEEXACT, /* 93 Exactly n matches */ + + OP_TYPEPOSSTAR, /* 94 Possessified versions */ + OP_TYPEPOSPLUS, /* 95 */ + OP_TYPEPOSQUERY, /* 96 */ + OP_TYPEPOSUPTO, /* 97 */ + + /* These are used for character classes and back references; only the + first six are the same as the sets above. */ + + OP_CRSTAR, /* 98 The maximizing and minimizing versions of */ + OP_CRMINSTAR, /* 99 all these opcodes must come in pairs, with */ + OP_CRPLUS, /* 100 the minimizing one second. These codes must */ + OP_CRMINPLUS, /* 101 be in exactly the same order as those above. */ + OP_CRQUERY, /* 102 */ + OP_CRMINQUERY, /* 103 */ + + OP_CRRANGE, /* 104 These are different to the three sets above. */ + OP_CRMINRANGE, /* 105 */ + + /* End of quantifier opcodes */ + + OP_CLASS, /* 106 Match a character class, chars < 256 only */ + OP_NCLASS, /* 107 Same, but the bitmap was created from a negative + class - the difference is relevant only when a + character > 255 is encountered. */ + OP_XCLASS, /* 108 Extended class for handling > 255 chars within the + class. This does both positive and negative. */ + OP_REF, /* 109 Match a back reference, casefully */ + OP_REFI, /* 110 Match a back reference, caselessly */ + OP_RECURSE, /* 111 Match a numbered subpattern (possibly recursive) */ + OP_CALLOUT, /* 112 Call out to external function if provided */ + + OP_ALT, /* 113 Start of alternation */ + OP_KET, /* 114 End of group that doesn't have an unbounded repeat */ + OP_KETRMAX, /* 115 These two must remain together and in this */ + OP_KETRMIN, /* 116 order. They are for groups the repeat for ever. */ + OP_KETRPOS, /* 117 Possessive unlimited repeat. */ + + /* The assertions must come before BRA, CBRA, ONCE, and COND, and the four + asserts must remain in order. */ + + OP_REVERSE, /* 118 Move pointer back - used in lookbehind assertions */ + OP_ASSERT, /* 119 Positive lookahead */ + OP_ASSERT_NOT, /* 120 Negative lookahead */ + OP_ASSERTBACK, /* 121 Positive lookbehind */ + OP_ASSERTBACK_NOT, /* 122 Negative lookbehind */ + + /* ONCE, ONCE_NC, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come immediately + after the assertions, with ONCE first, as there's a test for >= ONCE for a + subpattern that isn't an assertion. The POS versions must immediately follow + the non-POS versions in each case. */ + + OP_ONCE, /* 123 Atomic group, contains captures */ + OP_ONCE_NC, /* 124 Atomic group containing no captures */ + OP_BRA, /* 125 Start of non-capturing bracket */ + OP_BRAPOS, /* 126 Ditto, with unlimited, possessive repeat */ + OP_CBRA, /* 127 Start of capturing bracket */ + OP_CBRAPOS, /* 128 Ditto, with unlimited, possessive repeat */ + OP_COND, /* 129 Conditional group */ + + /* These five must follow the previous five, in the same order. There's a check for >= SBRA to distinguish the two sets. */ - OP_SBRA, /* 96 Start of non-capturing bracket, check empty */ - OP_SCBRA, /* 97 Start of capturing bracket, check empty */ - OP_SCOND, /* 98 Conditional group, check empty */ + OP_SBRA, /* 130 Start of non-capturing bracket, check empty */ + OP_SBRAPOS, /* 131 Ditto, with unlimited, possessive repeat */ + OP_SCBRA, /* 132 Start of capturing bracket, check empty */ + OP_SCBRAPOS, /* 133 Ditto, with unlimited, possessive repeat */ + OP_SCOND, /* 134 Conditional group, check empty */ - OP_CREF, /* 99 Used to hold a capture number as condition */ - OP_RREF, /* 100 Used to hold a recursion number as condition */ - OP_DEF, /* 101 The DEFINE condition */ + /* The next two pairs must (respectively) be kept together. */ - OP_BRAZERO, /* 102 These two must remain together and in this */ - OP_BRAMINZERO, /* 103 order. */ + OP_CREF, /* 135 Used to hold a capture number as condition */ + OP_NCREF, /* 136 Same, but generated by a name reference*/ + OP_RREF, /* 137 Used to hold a recursion number as condition */ + OP_NRREF, /* 138 Same, but generated by a name reference*/ + OP_DEF, /* 139 The DEFINE condition */ + + OP_BRAZERO, /* 140 These two must remain together and in this */ + OP_BRAMINZERO, /* 141 order. */ + OP_BRAPOSZERO, /* 142 */ /* These are backtracking control verbs */ - OP_PRUNE, /* 104 */ - OP_SKIP, /* 105 */ - OP_THEN, /* 106 */ - OP_COMMIT, /* 107 */ + OP_MARK, /* 143 always has an argument */ + OP_PRUNE, /* 144 */ + OP_PRUNE_ARG, /* 145 same, but with argument */ + OP_SKIP, /* 146 */ + OP_SKIP_ARG, /* 147 same, but with argument */ + OP_THEN, /* 148 */ + OP_THEN_ARG, /* 149 same, but with argument */ + OP_COMMIT, /* 150 */ /* These are forced failure and success verbs */ - OP_FAIL, /* 108 */ - OP_ACCEPT /* 109 */ + OP_FAIL, /* 151 */ + OP_ACCEPT, /* 152 */ + OP_ASSERT_ACCEPT, /* 153 Used inside assertions */ + OP_CLOSE, /* 154 Used before OP_ACCEPT to close open captures */ + + /* This is used to skip a subpattern with a {0} quantifier */ + + OP_SKIPZERO, /* 155 */ + + /* This is not an opcode, but is used to check that tables indexed by opcode + are the correct length, in order to catch updating errors - there have been + some in the past. */ + + OP_TABLE_LENGTH }; +/* *** NOTE NOTE NOTE *** Whenever the list above is updated, the two macro +definitions that follow must also be updated to match. There are also tables +called "coptable" and "poptable" in pcre_dfa_exec.c that must be updated. */ + /* This macro defines textual names for all the opcodes. These are used only -for debugging. The macro is referenced only in pcre_printint.c. */ +for debugging, and some of them are only partial names. The macro is referenced +only in pcre_printint.c, which fills out the full names in many cases (and in +some cases doesn't actually use these names at all). */ #define OP_NAME_LIST \ "End", "\\A", "\\G", "\\K", "\\B", "\\b", "\\D", "\\d", \ - "\\S", "\\s", "\\W", "\\w", "Any", "Anybyte", \ + "\\S", "\\s", "\\W", "\\w", "Any", "AllAny", "Anybyte", \ "notprop", "prop", "\\R", "\\H", "\\h", "\\V", "\\v", \ "extuni", "\\Z", "\\z", \ - "Opt", "^", "$", "char", "charnc", "not", \ - "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "^", "^", "$", "$", "char", "chari", "not", "noti", \ + "*", "*?", "+", "+?", "?", "??", \ + "{", "{", "{", \ "*+","++", "?+", "{", \ - "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "*", "*?", "+", "+?", "?", "??", \ + "{", "{", "{", \ + "*+","++", "?+", "{", \ + "*", "*?", "+", "+?", "?", "??", \ + "{", "{", "{", \ + "*+","++", "?+", "{", \ + "*", "*?", "+", "+?", "?", "??", \ + "{", "{", "{", \ "*+","++", "?+", "{", \ "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ "*+","++", "?+", "{", \ "*", "*?", "+", "+?", "?", "??", "{", "{", \ - "class", "nclass", "xclass", "Ref", "Recurse", "Callout", \ - "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", \ - "AssertB", "AssertB not", "Reverse", \ - "Once", "Bra", "CBra", "Cond", "SBra", "SCBra", "SCond", \ - "Cond ref", "Cond rec", "Cond def", "Brazero", "Braminzero", \ - "*PRUNE", "*SKIP", "*THEN", "*COMMIT", "*FAIL", "*ACCEPT" + "class", "nclass", "xclass", "Ref", "Refi", \ + "Recurse", "Callout", \ + "Alt", "Ket", "KetRmax", "KetRmin", "KetRpos", \ + "Reverse", "Assert", "Assert not", "AssertB", "AssertB not", \ + "Once", "Once_NC", \ + "Bra", "BraPos", "CBra", "CBraPos", \ + "Cond", \ + "SBra", "SBraPos", "SCBra", "SCBraPos", \ + "SCond", \ + "Cond ref", "Cond nref", "Cond rec", "Cond nrec", "Cond def", \ + "Brazero", "Braminzero", "Braposzero", \ + "*MARK", "*PRUNE", "*PRUNE", "*SKIP", "*SKIP", \ + "*THEN", "*THEN", "*COMMIT", "*FAIL", \ + "*ACCEPT", "*ASSERT_ACCEPT", \ + "Close", "Skip zero" /* This macro defines the length of fixed length operations in the compiled @@ -820,64 +2202,88 @@ in UTF-8 mode. The code that uses this table must know about such things. */ 1, /* End */ \ 1, 1, 1, 1, 1, /* \A, \G, \K, \B, \b */ \ 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ \ - 1, 1, /* Any, Anybyte */ \ - 3, 3, 1, /* NOTPROP, PROP, EXTUNI */ \ + 1, 1, 1, /* Any, AllAny, Anybyte */ \ + 3, 3, /* \P, \p */ \ 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ \ - 1, 1, 2, 1, 1, /* \Z, \z, Opt, ^, $ */ \ + 1, /* \X */ \ + 1, 1, 1, 1, 1, 1, /* \Z, \z, ^, ^M, $, $M */ \ 2, /* Char - the minimum length */ \ - 2, /* Charnc - the minimum length */ \ + 2, /* Chari - the minimum length */ \ 2, /* not */ \ - /* Positive single-char repeats ** These are */ \ - 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** minima in */ \ - 4, 4, 4, /* upto, minupto, exact ** UTF-8 mode */ \ - 2, 2, 2, 4, /* *+, ++, ?+, upto+ */ \ + 2, /* noti */ \ + /* Positive single-char repeats ** These are */ \ + 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** minima in */ \ + 2+IMM2_SIZE, 2+IMM2_SIZE, /* upto, minupto ** mode */ \ + 2+IMM2_SIZE, /* exact */ \ + 2, 2, 2, 2+IMM2_SIZE, /* *+, ++, ?+, upto+ */ \ + 2, 2, 2, 2, 2, 2, /* *I, *?I, +I, +?I, ?I, ??I ** UTF-8 */ \ + 2+IMM2_SIZE, 2+IMM2_SIZE, /* upto I, minupto I */ \ + 2+IMM2_SIZE, /* exact I */ \ + 2, 2, 2, 2+IMM2_SIZE, /* *+I, ++I, ?+I, upto+I */ \ /* Negative single-char repeats - only for chars < 256 */ \ 2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \ - 4, 4, 4, /* NOT upto, minupto, exact */ \ - 2, 2, 2, 4, /* Possessive *, +, ?, upto */ \ + 2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto, minupto */ \ + 2+IMM2_SIZE, /* NOT exact */ \ + 2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *, +, ?, upto */ \ + 2, 2, 2, 2, 2, 2, /* NOT *I, *?I, +I, +?I, ?I, ??I */ \ + 2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto I, minupto I */ \ + 2+IMM2_SIZE, /* NOT exact I */ \ + 2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *I, +I, ?I, upto I */ \ /* Positive type repeats */ \ 2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \ - 4, 4, 4, /* Type upto, minupto, exact */ \ - 2, 2, 2, 4, /* Possessive *+, ++, ?+, upto+ */ \ + 2+IMM2_SIZE, 2+IMM2_SIZE, /* Type upto, minupto */ \ + 2+IMM2_SIZE, /* Type exact */ \ + 2, 2, 2, 2+IMM2_SIZE, /* Possessive *+, ++, ?+, upto+ */ \ /* Character class & ref repeats */ \ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \ - 5, 5, /* CRRANGE, CRMINRANGE */ \ - 33, /* CLASS */ \ - 33, /* NCLASS */ \ + 1+2*IMM2_SIZE, 1+2*IMM2_SIZE, /* CRRANGE, CRMINRANGE */ \ + 1+(32/sizeof(pcre_uchar)), /* CLASS */ \ + 1+(32/sizeof(pcre_uchar)), /* NCLASS */ \ 0, /* XCLASS - variable length */ \ - 3, /* REF */ \ + 1+IMM2_SIZE, /* REF */ \ + 1+IMM2_SIZE, /* REFI */ \ 1+LINK_SIZE, /* RECURSE */ \ 2+2*LINK_SIZE, /* CALLOUT */ \ 1+LINK_SIZE, /* Alt */ \ 1+LINK_SIZE, /* Ket */ \ 1+LINK_SIZE, /* KetRmax */ \ 1+LINK_SIZE, /* KetRmin */ \ + 1+LINK_SIZE, /* KetRpos */ \ + 1+LINK_SIZE, /* Reverse */ \ 1+LINK_SIZE, /* Assert */ \ 1+LINK_SIZE, /* Assert not */ \ 1+LINK_SIZE, /* Assert behind */ \ 1+LINK_SIZE, /* Assert behind not */ \ - 1+LINK_SIZE, /* Reverse */ \ 1+LINK_SIZE, /* ONCE */ \ + 1+LINK_SIZE, /* ONCE_NC */ \ 1+LINK_SIZE, /* BRA */ \ - 3+LINK_SIZE, /* CBRA */ \ + 1+LINK_SIZE, /* BRAPOS */ \ + 1+LINK_SIZE+IMM2_SIZE, /* CBRA */ \ + 1+LINK_SIZE+IMM2_SIZE, /* CBRAPOS */ \ 1+LINK_SIZE, /* COND */ \ 1+LINK_SIZE, /* SBRA */ \ - 3+LINK_SIZE, /* SCBRA */ \ + 1+LINK_SIZE, /* SBRAPOS */ \ + 1+LINK_SIZE+IMM2_SIZE, /* SCBRA */ \ + 1+LINK_SIZE+IMM2_SIZE, /* SCBRAPOS */ \ 1+LINK_SIZE, /* SCOND */ \ - 3, /* CREF */ \ - 3, /* RREF */ \ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* CREF, NCREF */ \ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* RREF, NRREF */ \ 1, /* DEF */ \ - 1, 1, /* BRAZERO, BRAMINZERO */ \ - 1, 1, 1, 1, /* PRUNE, SKIP, THEN, COMMIT, */ \ - 1, 1 /* FAIL, ACCEPT */ - + 1, 1, 1, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ \ + 3, 1, 3, /* MARK, PRUNE, PRUNE_ARG */ \ + 1, 3, /* SKIP, SKIP_ARG */ \ + 1, 3, /* THEN, THEN_ARG */ \ + 1, 1, 1, 1, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ \ + 1+IMM2_SIZE, 1 /* CLOSE, SKIPZERO */ -/* A magic value for OP_RREF to indicate the "any recursion" condition. */ +/* A magic value for OP_RREF and OP_NRREF to indicate the "any recursion" +condition. */ #define RREF_ANY 0xffff -/* Error code numbers. They are given names so that they can more easily be -tracked. */ +/* Compile time error code numbers. They are given names so that they can more +easily be tracked. When a new number is added, the table called eint in +pcreposix.c must be updated. */ enum { ERR0, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, ERR10, ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, @@ -885,109 +2291,198 @@ enum { ERR0, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, ERR30, ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, ERR40, ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49, ERR50, ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59, - ERR60, ERR61, ERR62, ERR63 }; + ERR60, ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69, + ERR70, ERR71, ERR72, ERR73, ERR74, ERR75, ERR76, ERR77, ERR78, ERRCOUNT }; + +/* JIT compiling modes. The function list is indexed by them. */ +enum { JIT_COMPILE, JIT_PARTIAL_SOFT_COMPILE, JIT_PARTIAL_HARD_COMPILE, + JIT_NUMBER_OF_COMPILE_MODES }; /* The real format of the start of the pcre block; the index of names and the code vector run on as long as necessary after the end. We store an explicit offset to the name table so that if a regex is compiled on one host, saved, and then run on another where the size of pointers is different, all might still -be well. For the case of compiled-on-4 and run-on-8, we include an extra -pointer that is always NULL. For future-proofing, a few dummy fields were -originally included - even though you can never get this planning right - but -there is only one left now. - -NOTE NOTE NOTE: -Because people can now save and re-use compiled patterns, any additions to this -structure should be made at the end, and something earlier (e.g. a new -flag in the options or one of the dummy fields) should indicate that the new -fields are present. Currently PCRE always sets the dummy fields to zero. -NOTE NOTE NOTE: +be well. + +The size of the structure must be a multiple of 8 bytes. For the case of +compiled-on-4 and run-on-8, we include an extra pointer that is always NULL so +that there are an even number of pointers which therefore are a multiple of 8 +bytes. + +It is necessary to fork the struct for the 32 bit library, since it needs to +use pcre_uint32 for first_char and req_char. We can't put an ifdef inside the +typedef because pcretest needs access to the struct of the 8-, 16- and 32-bit +variants. + +*** WARNING *** +When new fields are added to these structures, remember to adjust the code in +pcre_byte_order.c that is concerned with swapping the byte order of the fields +when a compiled regex is reloaded on a host with different endianness. +*** WARNING *** +There is also similar byte-flipping code in pcretest.c, which is used for +testing the byte-flipping features. It must also be kept in step. +*** WARNING *** */ -typedef struct real_pcre { +typedef struct real_pcre8_or_16 { + pcre_uint32 magic_number; + pcre_uint32 size; /* Total that was malloced */ + pcre_uint32 options; /* Public options */ + pcre_uint32 flags; /* Private flags */ + pcre_uint32 limit_match; /* Limit set from regex */ + pcre_uint32 limit_recursion; /* Limit set from regex */ + pcre_uint16 first_char; /* Starting character */ + pcre_uint16 req_char; /* This character must be seen */ + pcre_uint16 max_lookbehind; /* Longest lookbehind (characters) */ + pcre_uint16 top_bracket; /* Highest numbered group */ + pcre_uint16 top_backref; /* Highest numbered back reference */ + pcre_uint16 name_table_offset; /* Offset to name table that follows */ + pcre_uint16 name_entry_size; /* Size of any name items */ + pcre_uint16 name_count; /* Number of name items */ + pcre_uint16 ref_count; /* Reference count */ + pcre_uint16 dummy1; /* To ensure size is a multiple of 8 */ + pcre_uint16 dummy2; /* To ensure size is a multiple of 8 */ + pcre_uint16 dummy3; /* To ensure size is a multiple of 8 */ + const pcre_uint8 *tables; /* Pointer to tables or NULL for std */ + void *nullpad; /* NULL padding */ +} real_pcre8_or_16; + +typedef struct real_pcre8_or_16 real_pcre; +typedef struct real_pcre8_or_16 real_pcre16; + +typedef struct real_pcre32 { pcre_uint32 magic_number; pcre_uint32 size; /* Total that was malloced */ pcre_uint32 options; /* Public options */ - pcre_uint16 flags; /* Private flags */ - pcre_uint16 dummy1; /* For future use */ - pcre_uint16 top_bracket; - pcre_uint16 top_backref; - pcre_uint16 first_byte; - pcre_uint16 req_byte; + pcre_uint32 flags; /* Private flags */ + pcre_uint32 limit_match; /* Limit set from regex */ + pcre_uint32 limit_recursion; /* Limit set from regex */ + pcre_uint32 first_char; /* Starting character */ + pcre_uint32 req_char; /* This character must be seen */ + pcre_uint16 max_lookbehind; /* Longest lookbehind (characters) */ + pcre_uint16 top_bracket; /* Highest numbered group */ + pcre_uint16 top_backref; /* Highest numbered back reference */ pcre_uint16 name_table_offset; /* Offset to name table that follows */ pcre_uint16 name_entry_size; /* Size of any name items */ pcre_uint16 name_count; /* Number of name items */ pcre_uint16 ref_count; /* Reference count */ + pcre_uint16 dummy; /* To ensure size is a multiple of 8 */ + const pcre_uint8 *tables; /* Pointer to tables or NULL for std */ + void *nullpad; /* NULL padding */ +} real_pcre32; + +#if defined COMPILE_PCRE8 +#define REAL_PCRE real_pcre +#elif defined COMPILE_PCRE16 +#define REAL_PCRE real_pcre16 +#elif defined COMPILE_PCRE32 +#define REAL_PCRE real_pcre32 +#endif + +/* Assert that the size of REAL_PCRE is divisible by 8 */ +typedef int __assert_real_pcre_size_divisible_8[(sizeof(REAL_PCRE) % 8) == 0 ? 1 : -1]; - const unsigned char *tables; /* Pointer to tables or NULL for std */ - const unsigned char *nullpad; /* NULL padding */ -} real_pcre; +/* Needed in pcretest to access some fields in the real_pcre* structures + * directly. They're unified for 8/16/32 bits since the structs only differ + * after these fields; if that ever changes, need to fork those defines into + * 8/16 and 32 bit versions. */ +#define REAL_PCRE_MAGIC(re) (((REAL_PCRE*)re)->magic_number) +#define REAL_PCRE_SIZE(re) (((REAL_PCRE*)re)->size) +#define REAL_PCRE_OPTIONS(re) (((REAL_PCRE*)re)->options) +#define REAL_PCRE_FLAGS(re) (((REAL_PCRE*)re)->flags) /* The format of the block used to store data from pcre_study(). The same remark (see NOTE above) about extending this structure applies. */ typedef struct pcre_study_data { pcre_uint32 size; /* Total that was malloced */ - pcre_uint32 options; - uschar start_bits[32]; + pcre_uint32 flags; /* Private flags */ + pcre_uint8 start_bits[32]; /* Starting char bits */ + pcre_uint32 minlength; /* Minimum subject length */ } pcre_study_data; +/* Structure for building a chain of open capturing subpatterns during +compiling, so that instructions to close them can be compiled when (*ACCEPT) is +encountered. This is also used to identify subpatterns that contain recursive +back references to themselves, so that they can be made atomic. */ + +typedef struct open_capitem { + struct open_capitem *next; /* Chain link */ + pcre_uint16 number; /* Capture number */ + pcre_uint16 flag; /* Set TRUE if recursive back ref */ +} open_capitem; + /* Structure for passing "static" information around between the functions doing the compiling, so that they are thread-safe. */ typedef struct compile_data { - const uschar *lcc; /* Points to lower casing table */ - const uschar *fcc; /* Points to case-flipping table */ - const uschar *cbits; /* Points to character type table */ - const uschar *ctypes; /* Points to table of type maps */ - const uschar *start_workspace;/* The start of working space */ - const uschar *start_code; /* The start of the compiled code */ - const uschar *start_pattern; /* The start of the pattern */ - const uschar *end_pattern; /* The end of the pattern */ - uschar *hwm; /* High watermark of workspace */ - uschar *name_table; /* The name/number table */ - int names_found; /* Number of entries so far */ - int name_entry_size; /* Size of each entry */ - int bracount; /* Count of capturing parens as we compile */ - int final_bracount; /* Saved value after first pass */ - int top_backref; /* Maximum back reference */ - unsigned int backref_map; /* Bitmap of low back refs */ - int external_options; /* External (initial) options */ - int external_flags; /* External flag bits to be set */ - int req_varyopt; /* "After variable item" flag for reqbyte */ - BOOL had_accept; /* (*ACCEPT) encountered */ - int nltype; /* Newline type */ - int nllen; /* Newline string length */ - uschar nl[4]; /* Newline string when fixed length */ + const pcre_uint8 *lcc; /* Points to lower casing table */ + const pcre_uint8 *fcc; /* Points to case-flipping table */ + const pcre_uint8 *cbits; /* Points to character type table */ + const pcre_uint8 *ctypes; /* Points to table of type maps */ + const pcre_uchar *start_workspace;/* The start of working space */ + const pcre_uchar *start_code; /* The start of the compiled code */ + const pcre_uchar *start_pattern; /* The start of the pattern */ + const pcre_uchar *end_pattern; /* The end of the pattern */ + open_capitem *open_caps; /* Chain of open capture items */ + pcre_uchar *hwm; /* High watermark of workspace */ + pcre_uchar *name_table; /* The name/number table */ + int names_found; /* Number of entries so far */ + int name_entry_size; /* Size of each entry */ + int workspace_size; /* Size of workspace */ + unsigned int bracount; /* Count of capturing parens as we compile */ + int final_bracount; /* Saved value after first pass */ + int max_lookbehind; /* Maximum lookbehind (characters) */ + int top_backref; /* Maximum back reference */ + unsigned int backref_map; /* Bitmap of low back refs */ + int assert_depth; /* Depth of nested assertions */ + pcre_uint32 external_options; /* External (initial) options */ + pcre_uint32 external_flags; /* External flag bits to be set */ + int req_varyopt; /* "After variable item" flag for reqbyte */ + BOOL had_accept; /* (*ACCEPT) encountered */ + BOOL had_pruneorskip; /* (*PRUNE) or (*SKIP) encountered */ + BOOL check_lookbehind; /* Lookbehinds need later checking */ + int nltype; /* Newline type */ + int nllen; /* Newline string length */ + pcre_uchar nl[4]; /* Newline string when fixed length */ } compile_data; /* Structure for maintaining a chain of pointers to the currently incomplete -branches, for testing for left recursion. */ +branches, for testing for left recursion while compiling. */ typedef struct branch_chain { struct branch_chain *outer; - uschar *current; + pcre_uchar *current_branch; } branch_chain; /* Structure for items in a linked list that represents an explicit recursive -call within the pattern. */ +call within the pattern; used by pcre_exec(). */ typedef struct recursion_info { struct recursion_info *prevrec; /* Previous recursion record (or NULL) */ - int group_num; /* Number of group that was called */ - const uschar *after_call; /* "Return value": points after the call in the expr */ - USPTR save_start; /* Old value of mstart */ - int *offset_save; /* Pointer to start of saved offsets */ - int saved_max; /* Number of saved offsets */ + unsigned int group_num; /* Number of group that was called */ + int *offset_save; /* Pointer to start of saved offsets */ + int saved_max; /* Number of saved offsets */ + int saved_capture_last; /* Last capture number */ + PCRE_PUCHAR subject_position; /* Position at start of recursion */ } recursion_info; +/* A similar structure for pcre_dfa_exec(). */ + +typedef struct dfa_recursion_info { + struct dfa_recursion_info *prevrec; + int group_num; + PCRE_PUCHAR subject_position; +} dfa_recursion_info; + /* Structure for building a chain of data for holding the values of the subject pointer at the start of each subpattern, so as to detect when an empty string -has been matched by a subpattern - to break infinite loops. */ +has been matched by a subpattern - to break infinite loops; used by +pcre_exec(). */ typedef struct eptrblock { struct eptrblock *epb_prev; - USPTR epb_saved_eptr; + PCRE_PUCHAR epb_saved_eptr; } eptrblock; @@ -995,58 +2490,78 @@ typedef struct eptrblock { doing traditional NFA matching, so that they are thread-safe. */ typedef struct match_data { -#ifdef ERLANG_INTEGRATION +#if defined(ERLANG_INTEGRATION) unsigned long int loop_limit; void *state_save; #endif unsigned long int match_call_count; /* As it says */ unsigned long int match_limit; /* As it says */ unsigned long int match_limit_recursion; /* As it says */ - int *offset_vector; /* Offset vector */ - int offset_end; /* One past the end */ - int offset_max; /* The maximum usable for return data */ - int nltype; /* Newline type */ - int nllen; /* Newline string length */ - uschar nl[4]; /* Newline string when fixed */ - const uschar *lcc; /* Points to lower casing table */ - const uschar *ctypes; /* Points to table of type maps */ - BOOL offset_overflow; /* Set if too many extractions */ - BOOL notbol; /* NOTBOL flag */ - BOOL noteol; /* NOTEOL flag */ - BOOL utf8; /* UTF8 flag */ - BOOL endonly; /* Dollar not before final \n */ - BOOL notempty; /* Empty string match not wanted */ - BOOL partial; /* PARTIAL flag */ - BOOL hitend; /* Hit the end of the subject at some point */ - BOOL bsr_anycrlf; /* \R is just any CRLF, not full Unicode */ - const uschar *start_code; /* For use when recursing */ - USPTR start_subject; /* Start of the subject string */ - USPTR end_subject; /* End of the subject string */ - USPTR start_match_ptr; /* Start of matched string */ - USPTR end_match_ptr; /* Subject position at end match */ - int end_offset_top; /* Highwater mark at end of match */ - int capture_last; /* Most recent capture number */ - int start_offset; /* The start offset value */ - eptrblock *eptrchain; /* Chain of eptrblocks for tail recursions */ - int eptrn; /* Next free eptrblock */ - recursion_info *recursive; /* Linked list of recursion data */ - void *callout_data; /* To pass back to callouts */ + int *offset_vector; /* Offset vector */ + int offset_end; /* One past the end */ + int offset_max; /* The maximum usable for return data */ + int nltype; /* Newline type */ + int nllen; /* Newline string length */ + int name_count; /* Number of names in name table */ + int name_entry_size; /* Size of entry in names table */ + unsigned int skip_arg_count; /* For counting SKIP_ARGs */ + unsigned int ignore_skip_arg; /* For re-run when SKIP arg name not found */ + pcre_uchar *name_table; /* Table of names */ + pcre_uchar nl[4]; /* Newline string when fixed */ + const pcre_uint8 *lcc; /* Points to lower casing table */ + const pcre_uint8 *fcc; /* Points to case-flipping table */ + const pcre_uint8 *ctypes; /* Points to table of type maps */ + BOOL notbol; /* NOTBOL flag */ + BOOL noteol; /* NOTEOL flag */ + BOOL utf; /* UTF-8 / UTF-16 flag */ + BOOL jscript_compat; /* JAVASCRIPT_COMPAT flag */ + BOOL use_ucp; /* PCRE_UCP flag */ + BOOL endonly; /* Dollar not before final \n */ + BOOL notempty; /* Empty string match not wanted */ + BOOL notempty_atstart; /* Empty string match at start not wanted */ + BOOL hitend; /* Hit the end of the subject at some point */ + BOOL bsr_anycrlf; /* \R is just any CRLF, not full Unicode */ + BOOL hasthen; /* Pattern contains (*THEN) */ + const pcre_uchar *start_code; /* For use when recursing */ + PCRE_PUCHAR start_subject; /* Start of the subject string */ + PCRE_PUCHAR end_subject; /* End of the subject string */ + PCRE_PUCHAR start_match_ptr; /* Start of matched string */ + PCRE_PUCHAR end_match_ptr; /* Subject position at end match */ + PCRE_PUCHAR start_used_ptr; /* Earliest consulted character */ + int partial; /* PARTIAL options */ + int end_offset_top; /* Highwater mark at end of match */ + pcre_int32 capture_last; /* Most recent capture number + overflow flag */ + int start_offset; /* The start offset value */ + int match_function_type; /* Set for certain special calls of MATCH() */ + eptrblock *eptrchain; /* Chain of eptrblocks for tail recursions */ + int eptrn; /* Next free eptrblock */ + recursion_info *recursive; /* Linked list of recursion data */ + void *callout_data; /* To pass back to callouts */ + const pcre_uchar *mark; /* Mark pointer to pass back on success */ + const pcre_uchar *nomatch_mark;/* Mark pointer to pass back on failure */ + const pcre_uchar *once_target; /* Where to back up to for atomic groups */ +#ifdef NO_RECURSE + void *match_frames_base; /* For remembering malloc'd frames */ +#endif } match_data; /* A similar structure is used for the same purpose by the DFA matching functions. */ typedef struct dfa_match_data { - const uschar *start_code; /* Start of the compiled pattern */ - const uschar *start_subject; /* Start of the subject string */ - const uschar *end_subject; /* End of subject string */ - const uschar *tables; /* Character tables */ - int moptions; /* Match options */ - int poptions; /* Pattern options */ - int nltype; /* Newline type */ - int nllen; /* Newline string length */ - uschar nl[4]; /* Newline string when fixed */ - void *callout_data; /* To pass back to callouts */ + const pcre_uchar *start_code; /* Start of the compiled pattern */ + const pcre_uchar *start_subject ; /* Start of the subject string */ + const pcre_uchar *end_subject; /* End of subject string */ + const pcre_uchar *start_used_ptr; /* Earliest consulted character */ + const pcre_uint8 *tables; /* Character tables */ + int start_offset; /* The start offset value */ + int moptions; /* Match options */ + int poptions; /* Pattern options */ + int nltype; /* Newline type */ + int nllen; /* Newline string length */ + pcre_uchar nl[4]; /* Newline string when fixed */ + void *callout_data; /* To pass back to callouts */ + dfa_recursion_info *recursive; /* Linked list of recursion data */ } dfa_match_data; /* Bit definitions for entries in the pcre_ctypes table. */ @@ -1082,6 +2597,33 @@ total length. */ #define ctypes_offset (cbits_offset + cbit_length) #define tables_length (ctypes_offset + 256) +/* Internal function and data prefixes. */ + +#if defined COMPILE_PCRE8 +#ifndef PUBL +#define PUBL(name) pcre_##name +#endif +#ifndef PRIV +#define PRIV(name) _pcre_##name +#endif +#elif defined COMPILE_PCRE16 +#ifndef PUBL +#define PUBL(name) pcre16_##name +#endif +#ifndef PRIV +#define PRIV(name) _pcre16_##name +#endif +#elif defined COMPILE_PCRE32 +#ifndef PUBL +#define PUBL(name) pcre32_##name +#endif +#ifndef PRIV +#define PRIV(name) _pcre32_##name +#endif +#else +#error Unsupported compiling mode +#endif /* COMPILE_PCRE[8|16|32] */ + /* Layout of the UCP type table that translates property names into types and codes. Each entry used to point directly to a name, but to reduce the number of relocations in shared libraries, it now has an offset into a single string @@ -1099,37 +2641,140 @@ of the exported public functions. They have to be "external" in the C sense, but are not part of the PCRE public API. The data for these tables is in the pcre_tables.c module. */ -extern const int _erts_pcre_utf8_table1[]; -extern const int _erts_pcre_utf8_table2[]; -extern const int _erts_pcre_utf8_table3[]; -extern const uschar _erts_pcre_utf8_table4[]; +#ifdef COMPILE_PCRE8 +extern const int PRIV(utf8_table1)[]; +extern const int PRIV(utf8_table1_size); +extern const int PRIV(utf8_table2)[]; +extern const int PRIV(utf8_table3)[]; +extern const pcre_uint8 PRIV(utf8_table4)[]; +#endif /* COMPILE_PCRE8 */ -extern const int _erts_pcre_utf8_table1_size; +extern const char PRIV(utt_names)[]; +extern const ucp_type_table PRIV(utt)[]; +extern const int PRIV(utt_size); -extern const char _erts_pcre_utt_names[]; -extern const ucp_type_table _erts_pcre_utt[]; -extern const int _erts_pcre_utt_size; +extern const pcre_uint8 PRIV(OP_lengths)[]; +extern const pcre_uint8 PRIV(default_tables)[]; -extern const uschar _erts_pcre_default_tables[]; - -extern const uschar _erts_pcre_OP_lengths[]; +extern const pcre_uint32 PRIV(hspace_list)[]; +extern const pcre_uint32 PRIV(vspace_list)[]; /* Internal shared functions. These are functions that are used by more than one of the exported public functions. They have to be "external" in the C sense, but are not part of the PCRE public API. */ -extern BOOL _erts_pcre_is_newline(const uschar *, int, const uschar *, - int *, BOOL); -extern int _erts_pcre_ord2utf8(int, uschar *); -extern real_pcre *_erts_pcre_try_flipped(const real_pcre *, real_pcre *, - const pcre_study_data *, pcre_study_data *); -extern int _erts_pcre_ucp_findprop(const unsigned int, int *, int *); -extern unsigned int _erts_pcre_ucp_othercase(const unsigned int); -extern int _erts_pcre_valid_utf8(const uschar *, int); -extern BOOL _erts_pcre_was_newline(const uschar *, int, const uschar *, - int *, BOOL); -extern BOOL _erts_pcre_xclass(int, const uschar *); +/* String comparison functions. */ +#if defined COMPILE_PCRE8 + +#define STRCMP_UC_UC(str1, str2) \ + strcmp((char *)(str1), (char *)(str2)) +#define STRCMP_UC_C8(str1, str2) \ + strcmp((char *)(str1), (str2)) +#define STRNCMP_UC_UC(str1, str2, num) \ + strncmp((char *)(str1), (char *)(str2), (num)) +#define STRNCMP_UC_C8(str1, str2, num) \ + strncmp((char *)(str1), (str2), (num)) +#define STRLEN_UC(str) strlen((const char *)str) + +#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + +extern int PRIV(strcmp_uc_uc)(const pcre_uchar *, + const pcre_uchar *); +extern int PRIV(strcmp_uc_c8)(const pcre_uchar *, + const char *); +extern int PRIV(strncmp_uc_uc)(const pcre_uchar *, + const pcre_uchar *, unsigned int num); +extern int PRIV(strncmp_uc_c8)(const pcre_uchar *, + const char *, unsigned int num); +extern unsigned int PRIV(strlen_uc)(const pcre_uchar *str); + +#define STRCMP_UC_UC(str1, str2) \ + PRIV(strcmp_uc_uc)((str1), (str2)) +#define STRCMP_UC_C8(str1, str2) \ + PRIV(strcmp_uc_c8)((str1), (str2)) +#define STRNCMP_UC_UC(str1, str2, num) \ + PRIV(strncmp_uc_uc)((str1), (str2), (num)) +#define STRNCMP_UC_C8(str1, str2, num) \ + PRIV(strncmp_uc_c8)((str1), (str2), (num)) +#define STRLEN_UC(str) PRIV(strlen_uc)(str) + +#endif /* COMPILE_PCRE[8|16|32] */ + +#if defined COMPILE_PCRE8 || defined COMPILE_PCRE16 + +#define STRCMP_UC_UC_TEST(str1, str2) STRCMP_UC_UC(str1, str2) +#define STRCMP_UC_C8_TEST(str1, str2) STRCMP_UC_C8(str1, str2) + +#elif defined COMPILE_PCRE32 + +extern int PRIV(strcmp_uc_uc_utf)(const pcre_uchar *, + const pcre_uchar *); +extern int PRIV(strcmp_uc_c8_utf)(const pcre_uchar *, + const char *); + +#define STRCMP_UC_UC_TEST(str1, str2) \ + (utf ? PRIV(strcmp_uc_uc_utf)((str1), (str2)) : PRIV(strcmp_uc_uc)((str1), (str2))) +#define STRCMP_UC_C8_TEST(str1, str2) \ + (utf ? PRIV(strcmp_uc_c8_utf)((str1), (str2)) : PRIV(strcmp_uc_c8)((str1), (str2))) + +#endif /* COMPILE_PCRE[8|16|32] */ + +extern const pcre_uchar *PRIV(find_bracket)(const pcre_uchar *, BOOL, int); +extern BOOL PRIV(is_newline)(PCRE_PUCHAR, int, PCRE_PUCHAR, + int *, BOOL); +extern unsigned int PRIV(ord2utf)(pcre_uint32, pcre_uchar *); +extern int PRIV(valid_utf)(PCRE_PUCHAR, int, int *); +extern BOOL PRIV(was_newline)(PCRE_PUCHAR, int, PCRE_PUCHAR, + int *, BOOL); +extern BOOL PRIV(xclass)(pcre_uint32, const pcre_uchar *, BOOL); + +#ifdef SUPPORT_JIT +extern void PRIV(jit_compile)(const REAL_PCRE *, + PUBL(extra) *, int); +extern int PRIV(jit_exec)(const PUBL(extra) *, + const pcre_uchar *, int, int, int, int *, int); +extern void PRIV(jit_free)(void *); +extern int PRIV(jit_get_size)(void *); +extern const char* PRIV(jit_get_target)(void); +#endif + +/* Unicode character database (UCD) */ + +typedef struct { + pcre_uint8 script; /* ucp_Arabic, etc. */ + pcre_uint8 chartype; /* ucp_Cc, etc. (general categories) */ + pcre_uint8 gbprop; /* ucp_gbControl, etc. (grapheme break property) */ + pcre_uint8 caseset; /* offset to multichar other cases or zero */ + pcre_int32 other_case; /* offset to other case, or zero if none */ +} ucd_record; + +extern const pcre_uint32 PRIV(ucd_caseless_sets)[]; +extern const ucd_record PRIV(ucd_records)[]; +extern const pcre_uint8 PRIV(ucd_stage1)[]; +extern const pcre_uint16 PRIV(ucd_stage2)[]; +extern const pcre_uint32 PRIV(ucp_gentype)[]; +extern const pcre_uint32 PRIV(ucp_gbtable)[]; +#ifdef SUPPORT_JIT +extern const int PRIV(ucp_typerange)[]; +#endif + +#ifdef SUPPORT_UCP +/* UCD access macros */ + +#define UCD_BLOCK_SIZE 128 +#define GET_UCD(ch) (PRIV(ucd_records) + \ + PRIV(ucd_stage2)[PRIV(ucd_stage1)[(int)(ch) / UCD_BLOCK_SIZE] * \ + UCD_BLOCK_SIZE + (int)(ch) % UCD_BLOCK_SIZE]) + +#define UCD_CHARTYPE(ch) GET_UCD(ch)->chartype +#define UCD_SCRIPT(ch) GET_UCD(ch)->script +#define UCD_CATEGORY(ch) PRIV(ucp_gentype)[UCD_CHARTYPE(ch)] +#define UCD_GRAPHBREAK(ch) GET_UCD(ch)->gbprop +#define UCD_CASESET(ch) GET_UCD(ch)->caseset +#define UCD_OTHERCASE(ch) ((pcre_uint32)((int)ch + (int)(GET_UCD(ch)->other_case))) + +#endif /* SUPPORT_UCP */ #endif diff --git a/erts/emulator/pcre/pcre_jit_compile.c b/erts/emulator/pcre/pcre_jit_compile.c new file mode 100644 index 0000000000..d0090d0e14 --- /dev/null +++ b/erts/emulator/pcre/pcre_jit_compile.c @@ -0,0 +1,9752 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2013 University of Cambridge + + The machine code generator part (this module) was written by Zoltan Herczeg + Copyright (c) 2010-2013 + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ +/* %ExternalCopyright% */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +#if defined SUPPORT_JIT + +/* All-in-one: Since we use the JIT compiler only from here, +we just include it. This way we don't need to touch the build +system files. */ + +#define SLJIT_MALLOC(size) (PUBL(malloc))(size) +#define SLJIT_FREE(ptr) (PUBL(free))(ptr) +#define SLJIT_CONFIG_AUTO 1 +#define SLJIT_CONFIG_STATIC 1 +#define SLJIT_VERBOSE 0 +#define SLJIT_DEBUG 0 + +#include "sljit/sljitLir.c" + +#if defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED +#error Unsupported architecture +#endif + +/* Defines for debugging purposes. */ + +/* 1 - Use unoptimized capturing brackets. + 2 - Enable capture_last_ptr (includes option 1). */ +/* #define DEBUG_FORCE_UNOPTIMIZED_CBRAS 2 */ + +/* 1 - Always have a control head. */ +/* #define DEBUG_FORCE_CONTROL_HEAD 1 */ + +/* Allocate memory for the regex stack on the real machine stack. +Fast, but limited size. */ +#define MACHINE_STACK_SIZE 32768 + +/* Growth rate for stack allocated by the OS. Should be the multiply +of page size. */ +#define STACK_GROWTH_RATE 8192 + +/* Enable to check that the allocation could destroy temporaries. */ +#if defined SLJIT_DEBUG && SLJIT_DEBUG +#define DESTROY_REGISTERS 1 +#endif + +/* +Short summary about the backtracking mechanism empolyed by the jit code generator: + +The code generator follows the recursive nature of the PERL compatible regular +expressions. The basic blocks of regular expressions are condition checkers +whose execute different commands depending on the result of the condition check. +The relationship between the operators can be horizontal (concatenation) and +vertical (sub-expression) (See struct backtrack_common for more details). + + 'ab' - 'a' and 'b' regexps are concatenated + 'a+' - 'a' is the sub-expression of the '+' operator + +The condition checkers are boolean (true/false) checkers. Machine code is generated +for the checker itself and for the actions depending on the result of the checker. +The 'true' case is called as the matching path (expected path), and the other is called as +the 'backtrack' path. Branch instructions are expesive for all CPUs, so we avoid taken +branches on the matching path. + + Greedy star operator (*) : + Matching path: match happens. + Backtrack path: match failed. + Non-greedy star operator (*?) : + Matching path: no need to perform a match. + Backtrack path: match is required. + +The following example shows how the code generated for a capturing bracket +with two alternatives. Let A, B, C, D are arbirary regular expressions, and +we have the following regular expression: + + A(B|C)D + +The generated code will be the following: + + A matching path + '(' matching path (pushing arguments to the stack) + B matching path + ')' matching path (pushing arguments to the stack) + D matching path + return with successful match + + D backtrack path + ')' backtrack path (If we arrived from "C" jump to the backtrack of "C") + B backtrack path + C expected path + jump to D matching path + C backtrack path + A backtrack path + + Notice, that the order of backtrack code paths are the opposite of the fast + code paths. In this way the topmost value on the stack is always belong + to the current backtrack code path. The backtrack path must check + whether there is a next alternative. If so, it needs to jump back to + the matching path eventually. Otherwise it needs to clear out its own stack + frame and continue the execution on the backtrack code paths. +*/ + +/* +Saved stack frames: + +Atomic blocks and asserts require reloading the values of private data +when the backtrack mechanism performed. Because of OP_RECURSE, the data +are not necessarly known in compile time, thus we need a dynamic restore +mechanism. + +The stack frames are stored in a chain list, and have the following format: +([ capturing bracket offset ][ start value ][ end value ])+ ... [ 0 ] [ previous head ] + +Thus we can restore the private data to a particular point in the stack. +*/ + +typedef struct jit_arguments { + /* Pointers first. */ + struct sljit_stack *stack; + const pcre_uchar *str; + const pcre_uchar *begin; + const pcre_uchar *end; + int *offsets; + pcre_uchar *uchar_ptr; + pcre_uchar *mark_ptr; + void *callout_data; + /* Everything else after. */ + pcre_uint32 limit_match; + int real_offset_count; + int offset_count; + pcre_uint8 notbol; + pcre_uint8 noteol; + pcre_uint8 notempty; + pcre_uint8 notempty_atstart; +} jit_arguments; + +typedef struct executable_functions { + void *executable_funcs[JIT_NUMBER_OF_COMPILE_MODES]; + PUBL(jit_callback) callback; + void *userdata; + pcre_uint32 top_bracket; + pcre_uint32 limit_match; + sljit_uw executable_sizes[JIT_NUMBER_OF_COMPILE_MODES]; +} executable_functions; + +typedef struct jump_list { + struct sljit_jump *jump; + struct jump_list *next; +} jump_list; + +typedef struct stub_list { + struct sljit_jump *start; + struct sljit_label *quit; + struct stub_list *next; +} stub_list; + +enum frame_types { + no_frame = -1, + no_stack = -2 +}; + +enum control_types { + type_mark = 0, + type_then_trap = 1 +}; + +typedef int (SLJIT_CALL *jit_function)(jit_arguments *args); + +/* The following structure is the key data type for the recursive +code generator. It is allocated by compile_matchingpath, and contains +the arguments for compile_backtrackingpath. Must be the first member +of its descendants. */ +typedef struct backtrack_common { + /* Concatenation stack. */ + struct backtrack_common *prev; + jump_list *nextbacktracks; + /* Internal stack (for component operators). */ + struct backtrack_common *top; + jump_list *topbacktracks; + /* Opcode pointer. */ + pcre_uchar *cc; +} backtrack_common; + +typedef struct assert_backtrack { + backtrack_common common; + jump_list *condfailed; + /* Less than 0 if a frame is not needed. */ + int framesize; + /* Points to our private memory word on the stack. */ + int private_data_ptr; + /* For iterators. */ + struct sljit_label *matchingpath; +} assert_backtrack; + +typedef struct bracket_backtrack { + backtrack_common common; + /* Where to coninue if an alternative is successfully matched. */ + struct sljit_label *alternative_matchingpath; + /* For rmin and rmax iterators. */ + struct sljit_label *recursive_matchingpath; + /* For greedy ? operator. */ + struct sljit_label *zero_matchingpath; + /* Contains the branches of a failed condition. */ + union { + /* Both for OP_COND, OP_SCOND. */ + jump_list *condfailed; + assert_backtrack *assert; + /* For OP_ONCE. Less than 0 if not needed. */ + int framesize; + } u; + /* Points to our private memory word on the stack. */ + int private_data_ptr; +} bracket_backtrack; + +typedef struct bracketpos_backtrack { + backtrack_common common; + /* Points to our private memory word on the stack. */ + int private_data_ptr; + /* Reverting stack is needed. */ + int framesize; + /* Allocated stack size. */ + int stacksize; +} bracketpos_backtrack; + +typedef struct braminzero_backtrack { + backtrack_common common; + struct sljit_label *matchingpath; +} braminzero_backtrack; + +typedef struct iterator_backtrack { + backtrack_common common; + /* Next iteration. */ + struct sljit_label *matchingpath; +} iterator_backtrack; + +typedef struct recurse_entry { + struct recurse_entry *next; + /* Contains the function entry. */ + struct sljit_label *entry; + /* Collects the calls until the function is not created. */ + jump_list *calls; + /* Points to the starting opcode. */ + sljit_sw start; +} recurse_entry; + +typedef struct recurse_backtrack { + backtrack_common common; + BOOL inlined_pattern; +} recurse_backtrack; + +#define OP_THEN_TRAP OP_TABLE_LENGTH + +typedef struct then_trap_backtrack { + backtrack_common common; + /* If then_trap is not NULL, this structure contains the real + then_trap for the backtracking path. */ + struct then_trap_backtrack *then_trap; + /* Points to the starting opcode. */ + sljit_sw start; + /* Exit point for the then opcodes of this alternative. */ + jump_list *quit; + /* Frame size of the current alternative. */ + int framesize; +} then_trap_backtrack; + +#define MAX_RANGE_SIZE 6 + +typedef struct compiler_common { + /* The sljit ceneric compiler. */ + struct sljit_compiler *compiler; + /* First byte code. */ + pcre_uchar *start; + /* Maps private data offset to each opcode. */ + sljit_si *private_data_ptrs; + /* Tells whether the capturing bracket is optimized. */ + pcre_uint8 *optimized_cbracket; + /* Tells whether the starting offset is a target of then. */ + pcre_uint8 *then_offsets; + /* Current position where a THEN must jump. */ + then_trap_backtrack *then_trap; + /* Starting offset of private data for capturing brackets. */ + int cbra_ptr; + /* Output vector starting point. Must be divisible by 2. */ + int ovector_start; + /* Last known position of the requested byte. */ + int req_char_ptr; + /* Head of the last recursion. */ + int recursive_head_ptr; + /* First inspected character for partial matching. */ + int start_used_ptr; + /* Starting pointer for partial soft matches. */ + int hit_start; + /* End pointer of the first line. */ + int first_line_end; + /* Points to the marked string. */ + int mark_ptr; + /* Recursive control verb management chain. */ + int control_head_ptr; + /* Points to the last matched capture block index. */ + int capture_last_ptr; + /* Points to the starting position of the current match. */ + int start_ptr; + + /* Flipped and lower case tables. */ + const pcre_uint8 *fcc; + sljit_sw lcc; + /* Mode can be PCRE_STUDY_JIT_COMPILE and others. */ + int mode; + /* \K is found in the pattern. */ + BOOL has_set_som; + /* (*SKIP:arg) is found in the pattern. */ + BOOL has_skip_arg; + /* (*THEN) is found in the pattern. */ + BOOL has_then; + /* Needs to know the start position anytime. */ + BOOL needs_start_ptr; + /* Currently in recurse or negative assert. */ + BOOL local_exit; + /* Currently in a positive assert. */ + BOOL positive_assert; + /* Newline control. */ + int nltype; + int newline; + int bsr_nltype; + /* Dollar endonly. */ + int endonly; + /* Tables. */ + sljit_sw ctypes; + int digits[2 + MAX_RANGE_SIZE]; + /* Named capturing brackets. */ + sljit_uw name_table; + sljit_sw name_count; + sljit_sw name_entry_size; + + /* Labels and jump lists. */ + struct sljit_label *partialmatchlabel; + struct sljit_label *quit_label; + struct sljit_label *forced_quit_label; + struct sljit_label *accept_label; + stub_list *stubs; + recurse_entry *entries; + recurse_entry *currententry; + jump_list *partialmatch; + jump_list *quit; + jump_list *positive_assert_quit; + jump_list *forced_quit; + jump_list *accept; + jump_list *calllimit; + jump_list *stackalloc; + jump_list *revertframes; + jump_list *wordboundary; + jump_list *anynewline; + jump_list *hspace; + jump_list *vspace; + jump_list *casefulcmp; + jump_list *caselesscmp; + jump_list *reset_match; + BOOL jscript_compat; +#ifdef SUPPORT_UTF + BOOL utf; +#ifdef SUPPORT_UCP + BOOL use_ucp; +#endif +#ifndef COMPILE_PCRE32 + jump_list *utfreadchar; +#endif +#ifdef COMPILE_PCRE8 + jump_list *utfreadtype8; +#endif +#endif /* SUPPORT_UTF */ +#ifdef SUPPORT_UCP + jump_list *getucd; +#endif +} compiler_common; + +/* For byte_sequence_compare. */ + +typedef struct compare_context { + int length; + int sourcereg; +#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED + int ucharptr; + union { + sljit_si asint; + sljit_uh asushort; +#if defined COMPILE_PCRE8 + sljit_ub asbyte; + sljit_ub asuchars[4]; +#elif defined COMPILE_PCRE16 + sljit_uh asuchars[2]; +#elif defined COMPILE_PCRE32 + sljit_ui asuchars[1]; +#endif + } c; + union { + sljit_si asint; + sljit_uh asushort; +#if defined COMPILE_PCRE8 + sljit_ub asbyte; + sljit_ub asuchars[4]; +#elif defined COMPILE_PCRE16 + sljit_uh asuchars[2]; +#elif defined COMPILE_PCRE32 + sljit_ui asuchars[1]; +#endif + } oc; +#endif +} compare_context; + +/* Undefine sljit macros. */ +#undef CMP + +/* Used for accessing the elements of the stack. */ +#define STACK(i) ((-(i) - 1) * (int)sizeof(sljit_sw)) + +#define TMP1 SLJIT_SCRATCH_REG1 +#define TMP2 SLJIT_SCRATCH_REG3 +#define TMP3 SLJIT_TEMPORARY_EREG2 +#define STR_PTR SLJIT_SAVED_REG1 +#define STR_END SLJIT_SAVED_REG2 +#define STACK_TOP SLJIT_SCRATCH_REG2 +#define STACK_LIMIT SLJIT_SAVED_REG3 +#define ARGUMENTS SLJIT_SAVED_EREG1 +#define COUNT_MATCH SLJIT_SAVED_EREG2 +#define RETURN_ADDR SLJIT_TEMPORARY_EREG1 + +/* Local space layout. */ +/* These two locals can be used by the current opcode. */ +#define LOCALS0 (0 * sizeof(sljit_sw)) +#define LOCALS1 (1 * sizeof(sljit_sw)) +/* Two local variables for possessive quantifiers (char1 cannot use them). */ +#define POSSESSIVE0 (2 * sizeof(sljit_sw)) +#define POSSESSIVE1 (3 * sizeof(sljit_sw)) +/* Max limit of recursions. */ +#define LIMIT_MATCH (4 * sizeof(sljit_sw)) +/* The output vector is stored on the stack, and contains pointers +to characters. The vector data is divided into two groups: the first +group contains the start / end character pointers, and the second is +the start pointers when the end of the capturing group has not yet reached. */ +#define OVECTOR_START (common->ovector_start) +#define OVECTOR(i) (OVECTOR_START + (i) * sizeof(sljit_sw)) +#define OVECTOR_PRIV(i) (common->cbra_ptr + (i) * sizeof(sljit_sw)) +#define PRIVATE_DATA(cc) (common->private_data_ptrs[(cc) - common->start]) + +#if defined COMPILE_PCRE8 +#define MOV_UCHAR SLJIT_MOV_UB +#define MOVU_UCHAR SLJIT_MOVU_UB +#elif defined COMPILE_PCRE16 +#define MOV_UCHAR SLJIT_MOV_UH +#define MOVU_UCHAR SLJIT_MOVU_UH +#elif defined COMPILE_PCRE32 +#define MOV_UCHAR SLJIT_MOV_UI +#define MOVU_UCHAR SLJIT_MOVU_UI +#else +#error Unsupported compiling mode +#endif + +/* Shortcuts. */ +#define DEFINE_COMPILER \ + struct sljit_compiler *compiler = common->compiler +#define OP1(op, dst, dstw, src, srcw) \ + sljit_emit_op1(compiler, (op), (dst), (dstw), (src), (srcw)) +#define OP2(op, dst, dstw, src1, src1w, src2, src2w) \ + sljit_emit_op2(compiler, (op), (dst), (dstw), (src1), (src1w), (src2), (src2w)) +#define LABEL() \ + sljit_emit_label(compiler) +#define JUMP(type) \ + sljit_emit_jump(compiler, (type)) +#define JUMPTO(type, label) \ + sljit_set_label(sljit_emit_jump(compiler, (type)), (label)) +#define JUMPHERE(jump) \ + sljit_set_label((jump), sljit_emit_label(compiler)) +#define SET_LABEL(jump, label) \ + sljit_set_label((jump), (label)) +#define CMP(type, src1, src1w, src2, src2w) \ + sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)) +#define CMPTO(type, src1, src1w, src2, src2w, label) \ + sljit_set_label(sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)), (label)) +#define OP_FLAGS(op, dst, dstw, src, srcw, type) \ + sljit_emit_op_flags(compiler, (op), (dst), (dstw), (src), (srcw), (type)) +#define GET_LOCAL_BASE(dst, dstw, offset) \ + sljit_get_local_base(compiler, (dst), (dstw), (offset)) + +static pcre_uchar* bracketend(pcre_uchar* cc) +{ +SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NOT) || (*cc >= OP_ONCE && *cc <= OP_SCOND)); +do cc += GET(cc, 1); while (*cc == OP_ALT); +SLJIT_ASSERT(*cc >= OP_KET && *cc <= OP_KETRPOS); +cc += 1 + LINK_SIZE; +return cc; +} + +/* Functions whose might need modification for all new supported opcodes: + next_opcode + check_opcode_types + set_private_data_ptrs + get_framesize + init_frame + get_private_data_copy_length + copy_private_data + compile_matchingpath + compile_backtrackingpath +*/ + +static pcre_uchar *next_opcode(compiler_common *common, pcre_uchar *cc) +{ +SLJIT_UNUSED_ARG(common); +switch(*cc) + { + case OP_SOD: + case OP_SOM: + case OP_SET_SOM: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ALLANY: + case OP_NOTPROP: + case OP_PROP: + case OP_ANYNL: + case OP_NOT_HSPACE: + case OP_HSPACE: + case OP_NOT_VSPACE: + case OP_VSPACE: + case OP_EXTUNI: + case OP_EODN: + case OP_EOD: + case OP_CIRC: + case OP_CIRCM: + case OP_DOLL: + case OP_DOLLM: + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + case OP_CRRANGE: + case OP_CRMINRANGE: + case OP_CLASS: + case OP_NCLASS: + case OP_REF: + case OP_REFI: + case OP_RECURSE: + case OP_CALLOUT: + case OP_ALT: + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_KETRPOS: + case OP_REVERSE: + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRA: + case OP_BRAPOS: + case OP_CBRA: + case OP_CBRAPOS: + case OP_COND: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCBRA: + case OP_SCBRAPOS: + case OP_SCOND: + case OP_CREF: + case OP_NCREF: + case OP_RREF: + case OP_NRREF: + case OP_DEF: + case OP_BRAZERO: + case OP_BRAMINZERO: + case OP_BRAPOSZERO: + case OP_PRUNE: + case OP_SKIP: + case OP_THEN: + case OP_COMMIT: + case OP_FAIL: + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + case OP_CLOSE: + case OP_SKIPZERO: + return cc + PRIV(OP_lengths)[*cc]; + + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_UPTO: + case OP_MINUPTO: + case OP_EXACT: + case OP_POSSTAR: + case OP_POSPLUS: + case OP_POSQUERY: + case OP_POSUPTO: + case OP_STARI: + case OP_MINSTARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_QUERYI: + case OP_MINQUERYI: + case OP_UPTOI: + case OP_MINUPTOI: + case OP_EXACTI: + case OP_POSSTARI: + case OP_POSPLUSI: + case OP_POSQUERYI: + case OP_POSUPTOI: + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + case OP_NOTEXACT: + case OP_NOTPOSSTAR: + case OP_NOTPOSPLUS: + case OP_NOTPOSQUERY: + case OP_NOTPOSUPTO: + case OP_NOTSTARI: + case OP_NOTMINSTARI: + case OP_NOTPLUSI: + case OP_NOTMINPLUSI: + case OP_NOTQUERYI: + case OP_NOTMINQUERYI: + case OP_NOTUPTOI: + case OP_NOTMINUPTOI: + case OP_NOTEXACTI: + case OP_NOTPOSSTARI: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERYI: + case OP_NOTPOSUPTOI: + cc += PRIV(OP_lengths)[*cc]; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + return cc; + + /* Special cases. */ + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + case OP_TYPEPOSUPTO: + return cc + PRIV(OP_lengths)[*cc] - 1; + + case OP_ANYBYTE: +#ifdef SUPPORT_UTF + if (common->utf) return NULL; +#endif + return cc + 1; + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: + return cc + GET(cc, 1); +#endif + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + case OP_THEN_ARG: + return cc + 1 + 2 + cc[1]; + + default: + /* All opcodes are supported now! */ + SLJIT_ASSERT_STOP(); + return NULL; + } +} + +static BOOL check_opcode_types(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend) +{ +pcre_uchar *name; +pcre_uchar *name2; +unsigned int cbra_index; +int i; + +/* Calculate important variables (like stack size) and checks whether all opcodes are supported. */ +while (cc < ccend) + { + switch(*cc) + { + case OP_SET_SOM: + common->has_set_som = TRUE; + cc += 1; + break; + + case OP_REF: + case OP_REFI: + common->optimized_cbracket[GET2(cc, 1)] = 0; + cc += 1 + IMM2_SIZE; + break; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] = 0; + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_COND: + case OP_SCOND: + /* Only AUTO_CALLOUT can insert this opcode. We do + not intend to support this case. */ + if (cc[1 + LINK_SIZE] == OP_CALLOUT) + return FALSE; + cc += 1 + LINK_SIZE; + break; + + case OP_CREF: + i = GET2(cc, 1); + common->optimized_cbracket[i] = 0; + cc += 1 + IMM2_SIZE; + break; + + case OP_NCREF: + cbra_index = GET2(cc, 1); + name = (pcre_uchar *)common->name_table; + name2 = name; + for (i = 0; i < common->name_count; i++) + { + if (GET2(name, 0) == cbra_index) break; + name += common->name_entry_size; + } + SLJIT_ASSERT(i != common->name_count); + + for (i = 0; i < common->name_count; i++) + { + if (STRCMP_UC_UC(name2 + IMM2_SIZE, name + IMM2_SIZE) == 0) + common->optimized_cbracket[GET2(name2, 0)] = 0; + name2 += common->name_entry_size; + } + cc += 1 + IMM2_SIZE; + break; + + case OP_RECURSE: + /* Set its value only once. */ + if (common->recursive_head_ptr == 0) + { + common->recursive_head_ptr = common->ovector_start; + common->ovector_start += sizeof(sljit_sw); + } + cc += 1 + LINK_SIZE; + break; + + case OP_CALLOUT: + if (common->capture_last_ptr == 0) + { + common->capture_last_ptr = common->ovector_start; + common->ovector_start += sizeof(sljit_sw); + } + cc += 2 + 2 * LINK_SIZE; + break; + + case OP_THEN_ARG: + common->has_then = TRUE; + common->control_head_ptr = 1; + /* Fall through. */ + + case OP_PRUNE_ARG: + common->needs_start_ptr = TRUE; + /* Fall through. */ + + case OP_MARK: + if (common->mark_ptr == 0) + { + common->mark_ptr = common->ovector_start; + common->ovector_start += sizeof(sljit_sw); + } + cc += 1 + 2 + cc[1]; + break; + + case OP_THEN: + common->has_then = TRUE; + common->control_head_ptr = 1; + /* Fall through. */ + + case OP_PRUNE: + case OP_SKIP: + common->needs_start_ptr = TRUE; + cc += 1; + break; + + case OP_SKIP_ARG: + common->control_head_ptr = 1; + common->has_skip_arg = TRUE; + cc += 1 + 2 + cc[1]; + break; + + default: + cc = next_opcode(common, cc); + if (cc == NULL) + return FALSE; + break; + } + } +return TRUE; +} + +static int get_class_iterator_size(pcre_uchar *cc) +{ +switch(*cc) + { + case OP_CRSTAR: + case OP_CRPLUS: + return 2; + + case OP_CRMINSTAR: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + return 1; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (GET2(cc, 1) == GET2(cc, 1 + IMM2_SIZE)) + return 0; + return 2; + + default: + return 0; + } +} + +static BOOL detect_repeat(compiler_common *common, pcre_uchar *begin) +{ +pcre_uchar *end = bracketend(begin); +pcre_uchar *next; +pcre_uchar *next_end; +pcre_uchar *max_end; +pcre_uchar type; +sljit_sw length = end - begin; +int min, max, i; + +/* Detect fixed iterations first. */ +if (end[-(1 + LINK_SIZE)] != OP_KET) + return FALSE; + +/* Already detected repeat. */ +if (common->private_data_ptrs[end - common->start - LINK_SIZE] != 0) + return TRUE; + +next = end; +min = 1; +while (1) + { + if (*next != *begin) + break; + next_end = bracketend(next); + if (next_end - next != length || memcmp(begin, next, IN_UCHARS(length)) != 0) + break; + next = next_end; + min++; + } + +if (min == 2) + return FALSE; + +max = 0; +max_end = next; +if (*next == OP_BRAZERO || *next == OP_BRAMINZERO) + { + type = *next; + while (1) + { + if (next[0] != type || next[1] != OP_BRA || next[2 + LINK_SIZE] != *begin) + break; + next_end = bracketend(next + 2 + LINK_SIZE); + if (next_end - next != (length + 2 + LINK_SIZE) || memcmp(begin, next + 2 + LINK_SIZE, IN_UCHARS(length)) != 0) + break; + next = next_end; + max++; + } + + if (next[0] == type && next[1] == *begin && max >= 1) + { + next_end = bracketend(next + 1); + if (next_end - next == (length + 1) && memcmp(begin, next + 1, IN_UCHARS(length)) == 0) + { + for (i = 0; i < max; i++, next_end += 1 + LINK_SIZE) + if (*next_end != OP_KET) + break; + + if (i == max) + { + common->private_data_ptrs[max_end - common->start - LINK_SIZE] = next_end - max_end; + common->private_data_ptrs[max_end - common->start - LINK_SIZE + 1] = (type == OP_BRAZERO) ? OP_UPTO : OP_MINUPTO; + /* +2 the original and the last. */ + common->private_data_ptrs[max_end - common->start - LINK_SIZE + 2] = max + 2; + if (min == 1) + return TRUE; + min--; + max_end -= (1 + LINK_SIZE) + GET(max_end, -LINK_SIZE); + } + } + } + } + +if (min >= 3) + { + common->private_data_ptrs[end - common->start - LINK_SIZE] = max_end - end; + common->private_data_ptrs[end - common->start - LINK_SIZE + 1] = OP_EXACT; + common->private_data_ptrs[end - common->start - LINK_SIZE + 2] = min; + return TRUE; + } + +return FALSE; +} + +#define CASE_ITERATOR_PRIVATE_DATA_1 \ + case OP_MINSTAR: \ + case OP_MINPLUS: \ + case OP_QUERY: \ + case OP_MINQUERY: \ + case OP_MINSTARI: \ + case OP_MINPLUSI: \ + case OP_QUERYI: \ + case OP_MINQUERYI: \ + case OP_NOTMINSTAR: \ + case OP_NOTMINPLUS: \ + case OP_NOTQUERY: \ + case OP_NOTMINQUERY: \ + case OP_NOTMINSTARI: \ + case OP_NOTMINPLUSI: \ + case OP_NOTQUERYI: \ + case OP_NOTMINQUERYI: + +#define CASE_ITERATOR_PRIVATE_DATA_2A \ + case OP_STAR: \ + case OP_PLUS: \ + case OP_STARI: \ + case OP_PLUSI: \ + case OP_NOTSTAR: \ + case OP_NOTPLUS: \ + case OP_NOTSTARI: \ + case OP_NOTPLUSI: + +#define CASE_ITERATOR_PRIVATE_DATA_2B \ + case OP_UPTO: \ + case OP_MINUPTO: \ + case OP_UPTOI: \ + case OP_MINUPTOI: \ + case OP_NOTUPTO: \ + case OP_NOTMINUPTO: \ + case OP_NOTUPTOI: \ + case OP_NOTMINUPTOI: + +#define CASE_ITERATOR_TYPE_PRIVATE_DATA_1 \ + case OP_TYPEMINSTAR: \ + case OP_TYPEMINPLUS: \ + case OP_TYPEQUERY: \ + case OP_TYPEMINQUERY: + +#define CASE_ITERATOR_TYPE_PRIVATE_DATA_2A \ + case OP_TYPESTAR: \ + case OP_TYPEPLUS: + +#define CASE_ITERATOR_TYPE_PRIVATE_DATA_2B \ + case OP_TYPEUPTO: \ + case OP_TYPEMINUPTO: + +static void set_private_data_ptrs(compiler_common *common, int *private_data_start, pcre_uchar *ccend) +{ +pcre_uchar *cc = common->start; +pcre_uchar *alternative; +pcre_uchar *end = NULL; +int private_data_ptr = *private_data_start; +int space, size, bracketlen; + +while (cc < ccend) + { + space = 0; + size = 0; + bracketlen = 0; + if (private_data_ptr > SLJIT_MAX_LOCAL_SIZE) + return; + + if (*cc == OP_ONCE || *cc == OP_ONCE_NC || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND) + if (detect_repeat(common, cc)) + { + /* These brackets are converted to repeats, so no global + based single character repeat is allowed. */ + if (cc >= end) + end = bracketend(cc); + } + + switch(*cc) + { + case OP_KET: + if (common->private_data_ptrs[cc + 1 - common->start] != 0) + { + common->private_data_ptrs[cc - common->start] = private_data_ptr; + private_data_ptr += sizeof(sljit_sw); + cc += common->private_data_ptrs[cc + 1 - common->start]; + } + cc += 1 + LINK_SIZE; + break; + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRAPOS: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCOND: + common->private_data_ptrs[cc - common->start] = private_data_ptr; + private_data_ptr += sizeof(sljit_sw); + bracketlen = 1 + LINK_SIZE; + break; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + common->private_data_ptrs[cc - common->start] = private_data_ptr; + private_data_ptr += sizeof(sljit_sw); + bracketlen = 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_COND: + /* Might be a hidden SCOND. */ + alternative = cc + GET(cc, 1); + if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) + { + common->private_data_ptrs[cc - common->start] = private_data_ptr; + private_data_ptr += sizeof(sljit_sw); + } + bracketlen = 1 + LINK_SIZE; + break; + + case OP_BRA: + bracketlen = 1 + LINK_SIZE; + break; + + case OP_CBRA: + case OP_SCBRA: + bracketlen = 1 + LINK_SIZE + IMM2_SIZE; + break; + + CASE_ITERATOR_PRIVATE_DATA_1 + space = 1; + size = -2; + break; + + CASE_ITERATOR_PRIVATE_DATA_2A + space = 2; + size = -2; + break; + + CASE_ITERATOR_PRIVATE_DATA_2B + space = 2; + size = -(2 + IMM2_SIZE); + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_1 + space = 1; + size = 1; + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_2A + if (cc[1] != OP_ANYNL && cc[1] != OP_EXTUNI) + space = 2; + size = 1; + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_2B + if (cc[1 + IMM2_SIZE] != OP_ANYNL && cc[1 + IMM2_SIZE] != OP_EXTUNI) + space = 2; + size = 1 + IMM2_SIZE; + break; + + case OP_CLASS: + case OP_NCLASS: + size += 1 + 32 / sizeof(pcre_uchar); + space = get_class_iterator_size(cc + size); + break; + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: + size = GET(cc, 1); + space = get_class_iterator_size(cc + size); + break; +#endif + + default: + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); + break; + } + + /* Character iterators, which are not inside a repeated bracket, + gets a private slot instead of allocating it on the stack. */ + if (space > 0 && cc >= end) + { + common->private_data_ptrs[cc - common->start] = private_data_ptr; + private_data_ptr += sizeof(sljit_sw) * space; + } + + if (size != 0) + { + if (size < 0) + { + cc += -size; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + } + else + cc += size; + } + + if (bracketlen > 0) + { + if (cc >= end) + { + end = bracketend(cc); + if (end[-1 - LINK_SIZE] == OP_KET) + end = NULL; + } + cc += bracketlen; + } + } +*private_data_start = private_data_ptr; +} + +/* Returns with a frame_types (always < 0) if no need for frame. */ +static int get_framesize(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, BOOL recursive, BOOL* needs_control_head) +{ +int length = 0; +int possessive = 0; +BOOL stack_restore = FALSE; +BOOL setsom_found = recursive; +BOOL setmark_found = recursive; +/* The last capture is a local variable even for recursions. */ +BOOL capture_last_found = FALSE; + +#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD +SLJIT_ASSERT(common->control_head_ptr != 0); +*needs_control_head = TRUE; +#else +*needs_control_head = FALSE; +#endif + +if (ccend == NULL) + { + ccend = bracketend(cc) - (1 + LINK_SIZE); + if (!recursive && (*cc == OP_CBRAPOS || *cc == OP_SCBRAPOS)) + { + possessive = length = (common->capture_last_ptr != 0) ? 5 : 3; + /* This is correct regardless of common->capture_last_ptr. */ + capture_last_found = TRUE; + } + cc = next_opcode(common, cc); + } + +SLJIT_ASSERT(cc != NULL); +while (cc < ccend) + switch(*cc) + { + case OP_SET_SOM: + SLJIT_ASSERT(common->has_set_som); + stack_restore = TRUE; + if (!setsom_found) + { + length += 2; + setsom_found = TRUE; + } + cc += 1; + break; + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_THEN_ARG: + SLJIT_ASSERT(common->mark_ptr != 0); + stack_restore = TRUE; + if (!setmark_found) + { + length += 2; + setmark_found = TRUE; + } + if (common->control_head_ptr != 0) + *needs_control_head = TRUE; + cc += 1 + 2 + cc[1]; + break; + + case OP_RECURSE: + stack_restore = TRUE; + if (common->has_set_som && !setsom_found) + { + length += 2; + setsom_found = TRUE; + } + if (common->mark_ptr != 0 && !setmark_found) + { + length += 2; + setmark_found = TRUE; + } + if (common->capture_last_ptr != 0 && !capture_last_found) + { + length += 2; + capture_last_found = TRUE; + } + cc += 1 + LINK_SIZE; + break; + + case OP_CBRA: + case OP_CBRAPOS: + case OP_SCBRA: + case OP_SCBRAPOS: + stack_restore = TRUE; + if (common->capture_last_ptr != 0 && !capture_last_found) + { + length += 2; + capture_last_found = TRUE; + } + length += 3; + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + default: + stack_restore = TRUE; + /* Fall through. */ + + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ALLANY: + case OP_ANYBYTE: + case OP_NOTPROP: + case OP_PROP: + case OP_ANYNL: + case OP_NOT_HSPACE: + case OP_HSPACE: + case OP_NOT_VSPACE: + case OP_VSPACE: + case OP_EXTUNI: + case OP_EODN: + case OP_EOD: + case OP_CIRC: + case OP_CIRCM: + case OP_DOLL: + case OP_DOLLM: + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + + case OP_EXACT: + case OP_POSSTAR: + case OP_POSPLUS: + case OP_POSQUERY: + case OP_POSUPTO: + + case OP_EXACTI: + case OP_POSSTARI: + case OP_POSPLUSI: + case OP_POSQUERYI: + case OP_POSUPTOI: + + case OP_NOTEXACT: + case OP_NOTPOSSTAR: + case OP_NOTPOSPLUS: + case OP_NOTPOSQUERY: + case OP_NOTPOSUPTO: + + case OP_NOTEXACTI: + case OP_NOTPOSSTARI: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERYI: + case OP_NOTPOSUPTOI: + + case OP_TYPEEXACT: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + case OP_TYPEPOSUPTO: + + case OP_CLASS: + case OP_NCLASS: + case OP_XCLASS: + + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); + break; + } + +/* Possessive quantifiers can use a special case. */ +if (SLJIT_UNLIKELY(possessive == length)) + return stack_restore ? no_frame : no_stack; + +if (length > 0) + return length + 1; +return stack_restore ? no_frame : no_stack; +} + +static void init_frame(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, int stackpos, int stacktop, BOOL recursive) +{ +DEFINE_COMPILER; +BOOL setsom_found = recursive; +BOOL setmark_found = recursive; +/* The last capture is a local variable even for recursions. */ +BOOL capture_last_found = FALSE; +int offset; + +/* >= 1 + shortest item size (2) */ +SLJIT_UNUSED_ARG(stacktop); +SLJIT_ASSERT(stackpos >= stacktop + 2); + +stackpos = STACK(stackpos); +if (ccend == NULL) + { + ccend = bracketend(cc) - (1 + LINK_SIZE); + if (recursive || (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS)) + cc = next_opcode(common, cc); + } + +SLJIT_ASSERT(cc != NULL); +while (cc < ccend) + switch(*cc) + { + case OP_SET_SOM: + SLJIT_ASSERT(common->has_set_som); + if (!setsom_found) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0)); + stackpos += (int)sizeof(sljit_sw); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); + stackpos += (int)sizeof(sljit_sw); + setsom_found = TRUE; + } + cc += 1; + break; + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_THEN_ARG: + SLJIT_ASSERT(common->mark_ptr != 0); + if (!setmark_found) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr); + stackpos += (int)sizeof(sljit_sw); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); + stackpos += (int)sizeof(sljit_sw); + setmark_found = TRUE; + } + cc += 1 + 2 + cc[1]; + break; + + case OP_RECURSE: + if (common->has_set_som && !setsom_found) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0)); + stackpos += (int)sizeof(sljit_sw); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); + stackpos += (int)sizeof(sljit_sw); + setsom_found = TRUE; + } + if (common->mark_ptr != 0 && !setmark_found) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr); + stackpos += (int)sizeof(sljit_sw); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); + stackpos += (int)sizeof(sljit_sw); + setmark_found = TRUE; + } + if (common->capture_last_ptr != 0 && !capture_last_found) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->capture_last_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr); + stackpos += (int)sizeof(sljit_sw); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); + stackpos += (int)sizeof(sljit_sw); + capture_last_found = TRUE; + } + cc += 1 + LINK_SIZE; + break; + + case OP_CBRA: + case OP_CBRAPOS: + case OP_SCBRA: + case OP_SCBRAPOS: + if (common->capture_last_ptr != 0 && !capture_last_found) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->capture_last_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr); + stackpos += (int)sizeof(sljit_sw); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); + stackpos += (int)sizeof(sljit_sw); + capture_last_found = TRUE; + } + offset = (GET2(cc, 1 + LINK_SIZE)) << 1; + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, OVECTOR(offset)); + stackpos += (int)sizeof(sljit_sw); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); + stackpos += (int)sizeof(sljit_sw); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP2, 0); + stackpos += (int)sizeof(sljit_sw); + + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + default: + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); + break; + } + +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, 0); +SLJIT_ASSERT(stackpos == STACK(stacktop)); +} + +static SLJIT_INLINE int get_private_data_copy_length(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, BOOL needs_control_head) +{ +int private_data_length = needs_control_head ? 3 : 2; +int size; +pcre_uchar *alternative; +/* Calculate the sum of the private machine words. */ +while (cc < ccend) + { + size = 0; + switch(*cc) + { + case OP_KET: + if (PRIVATE_DATA(cc) != 0) + private_data_length++; + cc += 1 + LINK_SIZE; + break; + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRAPOS: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCOND: + private_data_length++; + cc += 1 + LINK_SIZE; + break; + + case OP_CBRA: + case OP_SCBRA: + if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) + private_data_length++; + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + private_data_length += 2; + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_COND: + /* Might be a hidden SCOND. */ + alternative = cc + GET(cc, 1); + if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) + private_data_length++; + cc += 1 + LINK_SIZE; + break; + + CASE_ITERATOR_PRIVATE_DATA_1 + if (PRIVATE_DATA(cc)) + private_data_length++; + cc += 2; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + CASE_ITERATOR_PRIVATE_DATA_2A + if (PRIVATE_DATA(cc)) + private_data_length += 2; + cc += 2; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + CASE_ITERATOR_PRIVATE_DATA_2B + if (PRIVATE_DATA(cc)) + private_data_length += 2; + cc += 2 + IMM2_SIZE; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_1 + if (PRIVATE_DATA(cc)) + private_data_length++; + cc += 1; + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_2A + if (PRIVATE_DATA(cc)) + private_data_length += 2; + cc += 1; + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_2B + if (PRIVATE_DATA(cc)) + private_data_length += 2; + cc += 1 + IMM2_SIZE; + break; + + case OP_CLASS: + case OP_NCLASS: +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: + size = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(pcre_uchar); +#else + size = 1 + 32 / (int)sizeof(pcre_uchar); +#endif + if (PRIVATE_DATA(cc)) + private_data_length += get_class_iterator_size(cc + size); + cc += size; + break; + + default: + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); + break; + } + } +SLJIT_ASSERT(cc == ccend); +return private_data_length; +} + +static void copy_private_data(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, + BOOL save, int stackptr, int stacktop, BOOL needs_control_head) +{ +DEFINE_COMPILER; +int srcw[2]; +int count, size; +BOOL tmp1next = TRUE; +BOOL tmp1empty = TRUE; +BOOL tmp2empty = TRUE; +pcre_uchar *alternative; +enum { + start, + loop, + end +} status; + +status = save ? start : loop; +stackptr = STACK(stackptr - 2); +stacktop = STACK(stacktop - 1); + +if (!save) + { + stackptr += (needs_control_head ? 2 : 1) * sizeof(sljit_sw); + if (stackptr < stacktop) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), stackptr); + stackptr += sizeof(sljit_sw); + tmp1empty = FALSE; + } + if (stackptr < stacktop) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), stackptr); + stackptr += sizeof(sljit_sw); + tmp2empty = FALSE; + } + /* The tmp1next must be TRUE in either way. */ + } + +do + { + count = 0; + switch(status) + { + case start: + SLJIT_ASSERT(save && common->recursive_head_ptr != 0); + count = 1; + srcw[0] = common->recursive_head_ptr; + if (needs_control_head) + { + SLJIT_ASSERT(common->control_head_ptr != 0); + count = 2; + srcw[1] = common->control_head_ptr; + } + status = loop; + break; + + case loop: + if (cc >= ccend) + { + status = end; + break; + } + + switch(*cc) + { + case OP_KET: + if (PRIVATE_DATA(cc) != 0) + { + count = 1; + srcw[0] = PRIVATE_DATA(cc); + } + cc += 1 + LINK_SIZE; + break; + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRAPOS: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCOND: + count = 1; + srcw[0] = PRIVATE_DATA(cc); + SLJIT_ASSERT(srcw[0] != 0); + cc += 1 + LINK_SIZE; + break; + + case OP_CBRA: + case OP_SCBRA: + if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) + { + count = 1; + srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); + } + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + count = 2; + srcw[0] = PRIVATE_DATA(cc); + srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); + SLJIT_ASSERT(srcw[0] != 0 && srcw[1] != 0); + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_COND: + /* Might be a hidden SCOND. */ + alternative = cc + GET(cc, 1); + if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) + { + count = 1; + srcw[0] = PRIVATE_DATA(cc); + SLJIT_ASSERT(srcw[0] != 0); + } + cc += 1 + LINK_SIZE; + break; + + CASE_ITERATOR_PRIVATE_DATA_1 + if (PRIVATE_DATA(cc)) + { + count = 1; + srcw[0] = PRIVATE_DATA(cc); + } + cc += 2; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + CASE_ITERATOR_PRIVATE_DATA_2A + if (PRIVATE_DATA(cc)) + { + count = 2; + srcw[0] = PRIVATE_DATA(cc); + srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); + } + cc += 2; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + CASE_ITERATOR_PRIVATE_DATA_2B + if (PRIVATE_DATA(cc)) + { + count = 2; + srcw[0] = PRIVATE_DATA(cc); + srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); + } + cc += 2 + IMM2_SIZE; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_1 + if (PRIVATE_DATA(cc)) + { + count = 1; + srcw[0] = PRIVATE_DATA(cc); + } + cc += 1; + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_2A + if (PRIVATE_DATA(cc)) + { + count = 2; + srcw[0] = PRIVATE_DATA(cc); + srcw[1] = srcw[0] + sizeof(sljit_sw); + } + cc += 1; + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_2B + if (PRIVATE_DATA(cc)) + { + count = 2; + srcw[0] = PRIVATE_DATA(cc); + srcw[1] = srcw[0] + sizeof(sljit_sw); + } + cc += 1 + IMM2_SIZE; + break; + + case OP_CLASS: + case OP_NCLASS: +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: + size = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(pcre_uchar); +#else + size = 1 + 32 / (int)sizeof(pcre_uchar); +#endif + if (PRIVATE_DATA(cc)) + switch(get_class_iterator_size(cc + size)) + { + case 1: + count = 1; + srcw[0] = PRIVATE_DATA(cc); + break; + + case 2: + count = 2; + srcw[0] = PRIVATE_DATA(cc); + srcw[1] = srcw[0] + sizeof(sljit_sw); + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } + cc += size; + break; + + default: + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); + break; + } + break; + + case end: + SLJIT_ASSERT_STOP(); + break; + } + + while (count > 0) + { + count--; + if (save) + { + if (tmp1next) + { + if (!tmp1empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); + stackptr += sizeof(sljit_sw); + } + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), srcw[count]); + tmp1empty = FALSE; + tmp1next = FALSE; + } + else + { + if (!tmp2empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); + stackptr += sizeof(sljit_sw); + } + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), srcw[count]); + tmp2empty = FALSE; + tmp1next = TRUE; + } + } + else + { + if (tmp1next) + { + SLJIT_ASSERT(!tmp1empty); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), srcw[count], TMP1, 0); + tmp1empty = stackptr >= stacktop; + if (!tmp1empty) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), stackptr); + stackptr += sizeof(sljit_sw); + } + tmp1next = FALSE; + } + else + { + SLJIT_ASSERT(!tmp2empty); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), srcw[count], TMP2, 0); + tmp2empty = stackptr >= stacktop; + if (!tmp2empty) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), stackptr); + stackptr += sizeof(sljit_sw); + } + tmp1next = TRUE; + } + } + } + } +while (status != end); + +if (save) + { + if (tmp1next) + { + if (!tmp1empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); + stackptr += sizeof(sljit_sw); + } + if (!tmp2empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); + stackptr += sizeof(sljit_sw); + } + } + else + { + if (!tmp2empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); + stackptr += sizeof(sljit_sw); + } + if (!tmp1empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); + stackptr += sizeof(sljit_sw); + } + } + } +SLJIT_ASSERT(cc == ccend && stackptr == stacktop && (save || (tmp1empty && tmp2empty))); +} + +static SLJIT_INLINE pcre_uchar *set_then_offsets(compiler_common *common, pcre_uchar *cc, pcre_uint8 *current_offset) +{ +pcre_uchar *end = bracketend(cc); +BOOL has_alternatives = cc[GET(cc, 1)] == OP_ALT; + +/* Assert captures then. */ +if (*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NOT) + current_offset = NULL; +/* Conditional block does not. */ +if (*cc == OP_COND || *cc == OP_SCOND) + has_alternatives = FALSE; + +cc = next_opcode(common, cc); +if (has_alternatives) + current_offset = common->then_offsets + (cc - common->start); + +while (cc < end) + { + if ((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NOT) || (*cc >= OP_ONCE && *cc <= OP_SCOND)) + cc = set_then_offsets(common, cc, current_offset); + else + { + if (*cc == OP_ALT && has_alternatives) + current_offset = common->then_offsets + (cc + 1 + LINK_SIZE - common->start); + if (*cc >= OP_THEN && *cc <= OP_THEN_ARG && current_offset != NULL) + *current_offset = 1; + cc = next_opcode(common, cc); + } + } + +return end; +} + +#undef CASE_ITERATOR_PRIVATE_DATA_1 +#undef CASE_ITERATOR_PRIVATE_DATA_2A +#undef CASE_ITERATOR_PRIVATE_DATA_2B +#undef CASE_ITERATOR_TYPE_PRIVATE_DATA_1 +#undef CASE_ITERATOR_TYPE_PRIVATE_DATA_2A +#undef CASE_ITERATOR_TYPE_PRIVATE_DATA_2B + +static SLJIT_INLINE BOOL is_powerof2(unsigned int value) +{ +return (value & (value - 1)) == 0; +} + +static SLJIT_INLINE void set_jumps(jump_list *list, struct sljit_label *label) +{ +while (list) + { + /* sljit_set_label is clever enough to do nothing + if either the jump or the label is NULL. */ + SET_LABEL(list->jump, label); + list = list->next; + } +} + +static SLJIT_INLINE void add_jump(struct sljit_compiler *compiler, jump_list **list, struct sljit_jump* jump) +{ +jump_list *list_item = sljit_alloc_memory(compiler, sizeof(jump_list)); +if (list_item) + { + list_item->next = *list; + list_item->jump = jump; + *list = list_item; + } +} + +static void add_stub(compiler_common *common, struct sljit_jump *start) +{ +DEFINE_COMPILER; +stub_list* list_item = sljit_alloc_memory(compiler, sizeof(stub_list)); + +if (list_item) + { + list_item->start = start; + list_item->quit = LABEL(); + list_item->next = common->stubs; + common->stubs = list_item; + } +} + +static void flush_stubs(compiler_common *common) +{ +DEFINE_COMPILER; +stub_list* list_item = common->stubs; + +while (list_item) + { + JUMPHERE(list_item->start); + add_jump(compiler, &common->stackalloc, JUMP(SLJIT_FAST_CALL)); + JUMPTO(SLJIT_JUMP, list_item->quit); + list_item = list_item->next; + } +common->stubs = NULL; +} + +static SLJIT_INLINE void count_match(compiler_common *common) +{ +DEFINE_COMPILER; + +OP2(SLJIT_SUB | SLJIT_SET_E, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1); +add_jump(compiler, &common->calllimit, JUMP(SLJIT_C_ZERO)); +} + +static SLJIT_INLINE void allocate_stack(compiler_common *common, int size) +{ +/* May destroy all locals and registers except TMP2. */ +DEFINE_COMPILER; + +OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); +#ifdef DESTROY_REGISTERS +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345); +OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); +OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, TMP1, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, TMP1, 0); +#endif +add_stub(common, CMP(SLJIT_C_GREATER, STACK_TOP, 0, STACK_LIMIT, 0)); +} + +static SLJIT_INLINE void free_stack(compiler_common *common, int size) +{ +DEFINE_COMPILER; +OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); +} + +static SLJIT_INLINE void reset_ovector(compiler_common *common, int length) +{ +DEFINE_COMPILER; +struct sljit_label *loop; +int i; + +/* At this point we can freely use all temporary registers. */ +SLJIT_ASSERT(length > 1); +/* TMP1 returns with begin - 1. */ +OP2(SLJIT_SUB, SLJIT_SCRATCH_REG1, 0, SLJIT_MEM1(SLJIT_SAVED_REG1), SLJIT_OFFSETOF(jit_arguments, begin), SLJIT_IMM, IN_UCHARS(1)); +if (length < 8) + { + for (i = 1; i < length; i++) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(i), SLJIT_SCRATCH_REG1, 0); + } +else + { + GET_LOCAL_BASE(SLJIT_SCRATCH_REG2, 0, OVECTOR_START); + OP1(SLJIT_MOV, SLJIT_SCRATCH_REG3, 0, SLJIT_IMM, length - 1); + loop = LABEL(); + OP1(SLJIT_MOVU, SLJIT_MEM1(SLJIT_SCRATCH_REG2), sizeof(sljit_sw), SLJIT_SCRATCH_REG1, 0); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_SCRATCH_REG3, 0, SLJIT_SCRATCH_REG3, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_C_NOT_ZERO, loop); + } +} + +static SLJIT_INLINE void do_reset_match(compiler_common *common, int length) +{ +DEFINE_COMPILER; +struct sljit_label *loop; +int i; + +SLJIT_ASSERT(length > 1); +/* OVECTOR(1) contains the "string begin - 1" constant. */ +if (length > 2) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1)); +if (length < 8) + { + for (i = 2; i < length; i++) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(i), TMP1, 0); + } +else + { + GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2); + loop = LABEL(); + OP1(SLJIT_MOVU, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0); + OP2(SLJIT_SUB | SLJIT_SET_E, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_C_NOT_ZERO, loop); + } + +OP1(SLJIT_MOV, STACK_TOP, 0, ARGUMENTS, 0); +if (common->mark_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr, SLJIT_IMM, 0); +if (common->control_head_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, SLJIT_IMM, 0); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(jit_arguments, stack)); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_ptr); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(struct sljit_stack, base)); +} + +static sljit_sw SLJIT_CALL do_search_mark(sljit_sw *current, const pcre_uchar *skip_arg) +{ +while (current != NULL) + { + switch (current[-2]) + { + case type_then_trap: + break; + + case type_mark: + if (STRCMP_UC_UC(skip_arg, (pcre_uchar *)current[-3]) == 0) + return current[-4]; + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } + current = (sljit_sw*)current[-1]; + } +return -1; +} + +static SLJIT_INLINE void copy_ovector(compiler_common *common, int topbracket) +{ +DEFINE_COMPILER; +struct sljit_label *loop; +struct sljit_jump *early_quit; + +/* At this point we can freely use all registers. */ +OP1(SLJIT_MOV, SLJIT_SAVED_REG3, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1)); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1), STR_PTR, 0); + +OP1(SLJIT_MOV, SLJIT_SCRATCH_REG1, 0, ARGUMENTS, 0); +if (common->mark_ptr != 0) + OP1(SLJIT_MOV, SLJIT_SCRATCH_REG3, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr); +OP1(SLJIT_MOV_SI, SLJIT_SCRATCH_REG2, 0, SLJIT_MEM1(SLJIT_SCRATCH_REG1), SLJIT_OFFSETOF(jit_arguments, offset_count)); +if (common->mark_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SCRATCH_REG1), SLJIT_OFFSETOF(jit_arguments, mark_ptr), SLJIT_SCRATCH_REG3, 0); +OP2(SLJIT_SUB, SLJIT_SCRATCH_REG3, 0, SLJIT_MEM1(SLJIT_SCRATCH_REG1), SLJIT_OFFSETOF(jit_arguments, offsets), SLJIT_IMM, sizeof(int)); +OP1(SLJIT_MOV, SLJIT_SCRATCH_REG1, 0, SLJIT_MEM1(SLJIT_SCRATCH_REG1), SLJIT_OFFSETOF(jit_arguments, begin)); +GET_LOCAL_BASE(SLJIT_SAVED_REG1, 0, OVECTOR_START); +/* Unlikely, but possible */ +early_quit = CMP(SLJIT_C_EQUAL, SLJIT_SCRATCH_REG2, 0, SLJIT_IMM, 0); +loop = LABEL(); +OP2(SLJIT_SUB, SLJIT_SAVED_REG2, 0, SLJIT_MEM1(SLJIT_SAVED_REG1), 0, SLJIT_SCRATCH_REG1, 0); +OP2(SLJIT_ADD, SLJIT_SAVED_REG1, 0, SLJIT_SAVED_REG1, 0, SLJIT_IMM, sizeof(sljit_sw)); +/* Copy the integer value to the output buffer */ +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +OP2(SLJIT_ASHR, SLJIT_SAVED_REG2, 0, SLJIT_SAVED_REG2, 0, SLJIT_IMM, UCHAR_SHIFT); +#endif +OP1(SLJIT_MOVU_SI, SLJIT_MEM1(SLJIT_SCRATCH_REG3), sizeof(int), SLJIT_SAVED_REG2, 0); +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_SCRATCH_REG2, 0, SLJIT_SCRATCH_REG2, 0, SLJIT_IMM, 1); +JUMPTO(SLJIT_C_NOT_ZERO, loop); +JUMPHERE(early_quit); + +/* Calculate the return value, which is the maximum ovector value. */ +if (topbracket > 1) + { + GET_LOCAL_BASE(SLJIT_SCRATCH_REG1, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_SCRATCH_REG2, 0, SLJIT_IMM, topbracket + 1); + + /* OVECTOR(0) is never equal to SLJIT_SAVED_REG3. */ + loop = LABEL(); + OP1(SLJIT_MOVU, SLJIT_SCRATCH_REG3, 0, SLJIT_MEM1(SLJIT_SCRATCH_REG1), -(2 * (sljit_sw)sizeof(sljit_sw))); + OP2(SLJIT_SUB, SLJIT_SCRATCH_REG2, 0, SLJIT_SCRATCH_REG2, 0, SLJIT_IMM, 1); + CMPTO(SLJIT_C_EQUAL, SLJIT_SCRATCH_REG3, 0, SLJIT_SAVED_REG3, 0, loop); + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_SCRATCH_REG2, 0); + } +else + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1); +} + +static SLJIT_INLINE void return_with_partial_match(compiler_common *common, struct sljit_label *quit) +{ +DEFINE_COMPILER; +struct sljit_jump *jump; + +SLJIT_COMPILE_ASSERT(STR_END == SLJIT_SAVED_REG2, str_end_must_be_saved_reg2); +SLJIT_ASSERT(common->start_used_ptr != 0 && common->start_ptr != 0 + && (common->mode == JIT_PARTIAL_SOFT_COMPILE ? common->hit_start != 0 : common->hit_start == 0)); + +OP1(SLJIT_MOV, SLJIT_SCRATCH_REG2, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_PARTIAL); +OP1(SLJIT_MOV_SI, SLJIT_SCRATCH_REG3, 0, SLJIT_MEM1(SLJIT_SCRATCH_REG2), SLJIT_OFFSETOF(jit_arguments, real_offset_count)); +CMPTO(SLJIT_C_SIG_LESS, SLJIT_SCRATCH_REG3, 0, SLJIT_IMM, 2, quit); + +/* Store match begin and end. */ +OP1(SLJIT_MOV, SLJIT_SAVED_REG1, 0, SLJIT_MEM1(SLJIT_SCRATCH_REG2), SLJIT_OFFSETOF(jit_arguments, begin)); +OP1(SLJIT_MOV, SLJIT_SCRATCH_REG2, 0, SLJIT_MEM1(SLJIT_SCRATCH_REG2), SLJIT_OFFSETOF(jit_arguments, offsets)); + +jump = CMP(SLJIT_C_SIG_LESS, SLJIT_SCRATCH_REG3, 0, SLJIT_IMM, 3); +OP2(SLJIT_SUB, SLJIT_SCRATCH_REG3, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mode == JIT_PARTIAL_HARD_COMPILE ? common->start_ptr : (common->hit_start + (int)sizeof(sljit_sw)), SLJIT_SAVED_REG1, 0); +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +OP2(SLJIT_ASHR, SLJIT_SCRATCH_REG3, 0, SLJIT_SCRATCH_REG3, 0, SLJIT_IMM, UCHAR_SHIFT); +#endif +OP1(SLJIT_MOV_SI, SLJIT_MEM1(SLJIT_SCRATCH_REG2), 2 * sizeof(int), SLJIT_SCRATCH_REG3, 0); +JUMPHERE(jump); + +OP1(SLJIT_MOV, SLJIT_SCRATCH_REG3, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mode == JIT_PARTIAL_HARD_COMPILE ? common->start_used_ptr : common->hit_start); +OP2(SLJIT_SUB, SLJIT_SAVED_REG2, 0, STR_END, 0, SLJIT_SAVED_REG1, 0); +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +OP2(SLJIT_ASHR, SLJIT_SAVED_REG2, 0, SLJIT_SAVED_REG2, 0, SLJIT_IMM, UCHAR_SHIFT); +#endif +OP1(SLJIT_MOV_SI, SLJIT_MEM1(SLJIT_SCRATCH_REG2), sizeof(int), SLJIT_SAVED_REG2, 0); + +OP2(SLJIT_SUB, SLJIT_SCRATCH_REG3, 0, SLJIT_SCRATCH_REG3, 0, SLJIT_SAVED_REG1, 0); +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +OP2(SLJIT_ASHR, SLJIT_SCRATCH_REG3, 0, SLJIT_SCRATCH_REG3, 0, SLJIT_IMM, UCHAR_SHIFT); +#endif +OP1(SLJIT_MOV_SI, SLJIT_MEM1(SLJIT_SCRATCH_REG2), 0, SLJIT_SCRATCH_REG3, 0); + +JUMPTO(SLJIT_JUMP, quit); +} + +static SLJIT_INLINE void check_start_used_ptr(compiler_common *common) +{ +/* May destroy TMP1. */ +DEFINE_COMPILER; +struct sljit_jump *jump; + +if (common->mode == JIT_PARTIAL_SOFT_COMPILE) + { + /* The value of -1 must be kept for start_used_ptr! */ + OP2(SLJIT_ADD, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, SLJIT_IMM, 1); + /* Jumps if start_used_ptr < STR_PTR, or start_used_ptr == -1. Although overwriting + is not necessary if start_used_ptr == STR_PTR, it does not hurt as well. */ + jump = CMP(SLJIT_C_LESS_EQUAL, TMP1, 0, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, STR_PTR, 0); + JUMPHERE(jump); + } +else if (common->mode == JIT_PARTIAL_HARD_COMPILE) + { + jump = CMP(SLJIT_C_LESS_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, STR_PTR, 0); + JUMPHERE(jump); + } +} + +static SLJIT_INLINE BOOL char_has_othercase(compiler_common *common, pcre_uchar* cc) +{ +/* Detects if the character has an othercase. */ +unsigned int c; + +#ifdef SUPPORT_UTF +if (common->utf) + { + GETCHAR(c, cc); + if (c > 127) + { +#ifdef SUPPORT_UCP + return c != UCD_OTHERCASE(c); +#else + return FALSE; +#endif + } +#ifndef COMPILE_PCRE8 + return common->fcc[c] != c; +#endif + } +else +#endif + c = *cc; +return MAX_255(c) ? common->fcc[c] != c : FALSE; +} + +static SLJIT_INLINE unsigned int char_othercase(compiler_common *common, unsigned int c) +{ +/* Returns with the othercase. */ +#ifdef SUPPORT_UTF +if (common->utf && c > 127) + { +#ifdef SUPPORT_UCP + return UCD_OTHERCASE(c); +#else + return c; +#endif + } +#endif +return TABLE_GET(c, common->fcc, c); +} + +static unsigned int char_get_othercase_bit(compiler_common *common, pcre_uchar* cc) +{ +/* Detects if the character and its othercase has only 1 bit difference. */ +unsigned int c, oc, bit; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +int n; +#endif + +#ifdef SUPPORT_UTF +if (common->utf) + { + GETCHAR(c, cc); + if (c <= 127) + oc = common->fcc[c]; + else + { +#ifdef SUPPORT_UCP + oc = UCD_OTHERCASE(c); +#else + oc = c; +#endif + } + } +else + { + c = *cc; + oc = TABLE_GET(c, common->fcc, c); + } +#else +c = *cc; +oc = TABLE_GET(c, common->fcc, c); +#endif + +SLJIT_ASSERT(c != oc); + +bit = c ^ oc; +/* Optimized for English alphabet. */ +if (c <= 127 && bit == 0x20) + return (0 << 8) | 0x20; + +/* Since c != oc, they must have at least 1 bit difference. */ +if (!is_powerof2(bit)) + return 0; + +#if defined COMPILE_PCRE8 + +#ifdef SUPPORT_UTF +if (common->utf && c > 127) + { + n = GET_EXTRALEN(*cc); + while ((bit & 0x3f) == 0) + { + n--; + bit >>= 6; + } + return (n << 8) | bit; + } +#endif /* SUPPORT_UTF */ +return (0 << 8) | bit; + +#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + +#ifdef SUPPORT_UTF +if (common->utf && c > 65535) + { + if (bit >= (1 << 10)) + bit >>= 10; + else + return (bit < 256) ? ((2 << 8) | bit) : ((3 << 8) | (bit >> 8)); + } +#endif /* SUPPORT_UTF */ +return (bit < 256) ? ((0 << 8) | bit) : ((1 << 8) | (bit >> 8)); + +#endif /* COMPILE_PCRE[8|16|32] */ +} + +static void check_partial(compiler_common *common, BOOL force) +{ +/* Checks whether a partial matching is occurred. Does not modify registers. */ +DEFINE_COMPILER; +struct sljit_jump *jump = NULL; + +SLJIT_ASSERT(!force || common->mode != JIT_COMPILE); + +if (common->mode == JIT_COMPILE) + return; + +if (!force) + jump = CMP(SLJIT_C_GREATER_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, STR_PTR, 0); +else if (common->mode == JIT_PARTIAL_SOFT_COMPILE) + jump = CMP(SLJIT_C_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, SLJIT_IMM, -1); + +if (common->mode == JIT_PARTIAL_SOFT_COMPILE) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->hit_start, SLJIT_IMM, 0); +else + { + if (common->partialmatchlabel != NULL) + JUMPTO(SLJIT_JUMP, common->partialmatchlabel); + else + add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP)); + } + +if (jump != NULL) + JUMPHERE(jump); +} + +static void check_str_end(compiler_common *common, jump_list **end_reached) +{ +/* Does not affect registers. Usually used in a tight spot. */ +DEFINE_COMPILER; +struct sljit_jump *jump; + +if (common->mode == JIT_COMPILE) + { + add_jump(compiler, end_reached, CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + return; + } + +jump = CMP(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0); +if (common->mode == JIT_PARTIAL_SOFT_COMPILE) + { + add_jump(compiler, end_reached, CMP(SLJIT_C_GREATER_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, STR_PTR, 0)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->hit_start, SLJIT_IMM, 0); + add_jump(compiler, end_reached, JUMP(SLJIT_JUMP)); + } +else + { + add_jump(compiler, end_reached, CMP(SLJIT_C_GREATER_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, STR_PTR, 0)); + if (common->partialmatchlabel != NULL) + JUMPTO(SLJIT_JUMP, common->partialmatchlabel); + else + add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP)); + } +JUMPHERE(jump); +} + +static void detect_partial_match(compiler_common *common, jump_list **backtracks) +{ +DEFINE_COMPILER; +struct sljit_jump *jump; + +if (common->mode == JIT_COMPILE) + { + add_jump(compiler, backtracks, CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + return; + } + +/* Partial matching mode. */ +jump = CMP(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0); +add_jump(compiler, backtracks, CMP(SLJIT_C_GREATER_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, STR_PTR, 0)); +if (common->mode == JIT_PARTIAL_SOFT_COMPILE) + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->hit_start, SLJIT_IMM, 0); + add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); + } +else + { + if (common->partialmatchlabel != NULL) + JUMPTO(SLJIT_JUMP, common->partialmatchlabel); + else + add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP)); + } +JUMPHERE(jump); +} + +static void read_char(compiler_common *common) +{ +/* Reads the character into TMP1, updates STR_PTR. +Does not check STR_END. TMP2 Destroyed. */ +DEFINE_COMPILER; +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 +struct sljit_jump *jump; +#endif + +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 +if (common->utf) + { +#if defined COMPILE_PCRE8 + jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0); +#elif defined COMPILE_PCRE16 + jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xd800); +#endif /* COMPILE_PCRE[8|16] */ + add_jump(compiler, &common->utfreadchar, JUMP(SLJIT_FAST_CALL)); + JUMPHERE(jump); + } +#endif /* SUPPORT_UTF && !COMPILE_PCRE32 */ +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +} + +static void peek_char(compiler_common *common) +{ +/* Reads the character into TMP1, keeps STR_PTR. +Does not check STR_END. TMP2 Destroyed. */ +DEFINE_COMPILER; +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 +struct sljit_jump *jump; +#endif + +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 +if (common->utf) + { +#if defined COMPILE_PCRE8 + jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0); +#elif defined COMPILE_PCRE16 + jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xd800); +#endif /* COMPILE_PCRE[8|16] */ + add_jump(compiler, &common->utfreadchar, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); + JUMPHERE(jump); + } +#endif /* SUPPORT_UTF && !COMPILE_PCRE32 */ +} + +static void read_char8_type(compiler_common *common) +{ +/* Reads the character type into TMP1, updates STR_PTR. Does not check STR_END. */ +DEFINE_COMPILER; +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +struct sljit_jump *jump; +#endif + +#ifdef SUPPORT_UTF +if (common->utf) + { + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#if defined COMPILE_PCRE8 + /* This can be an extra read in some situations, but hopefully + it is needed in most cases. */ + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); + jump = CMP(SLJIT_C_LESS, TMP2, 0, SLJIT_IMM, 0xc0); + add_jump(compiler, &common->utfreadtype8, JUMP(SLJIT_FAST_CALL)); + JUMPHERE(jump); +#elif defined COMPILE_PCRE16 + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); + jump = CMP(SLJIT_C_GREATER, TMP2, 0, SLJIT_IMM, 255); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); + JUMPHERE(jump); + /* Skip low surrogate if necessary. */ + OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xfc00); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0xd800); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +#elif defined COMPILE_PCRE32 + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); + jump = CMP(SLJIT_C_GREATER, TMP2, 0, SLJIT_IMM, 255); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); + JUMPHERE(jump); +#endif /* COMPILE_PCRE[8|16|32] */ + return; + } +#endif /* SUPPORT_UTF */ +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), 0); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +/* The ctypes array contains only 256 values. */ +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); +jump = CMP(SLJIT_C_GREATER, TMP2, 0, SLJIT_IMM, 255); +#endif +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +JUMPHERE(jump); +#endif +} + +static void skip_char_back(compiler_common *common) +{ +/* Goes one character back. Affects STR_PTR and TMP1. Does not check begin. */ +DEFINE_COMPILER; +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 +#if defined COMPILE_PCRE8 +struct sljit_label *label; + +if (common->utf) + { + label = LABEL(); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); + CMPTO(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, label); + return; + } +#elif defined COMPILE_PCRE16 +if (common->utf) + { + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + /* Skip low surrogate if necessary. */ + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xdc00); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + return; + } +#endif /* COMPILE_PCRE[8|16] */ +#endif /* SUPPORT_UTF && !COMPILE_PCRE32 */ +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +} + +static void check_newlinechar(compiler_common *common, int nltype, jump_list **backtracks, BOOL jumpiftrue) +{ +/* Character comes in TMP1. Checks if it is a newline. TMP2 may be destroyed. */ +DEFINE_COMPILER; + +if (nltype == NLTYPE_ANY) + { + add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, backtracks, JUMP(jumpiftrue ? SLJIT_C_NOT_ZERO : SLJIT_C_ZERO)); + } +else if (nltype == NLTYPE_ANYCRLF) + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_CR); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + add_jump(compiler, backtracks, JUMP(jumpiftrue ? SLJIT_C_NOT_ZERO : SLJIT_C_ZERO)); + } +else + { + SLJIT_ASSERT(nltype == NLTYPE_FIXED && common->newline < 256); + add_jump(compiler, backtracks, CMP(jumpiftrue ? SLJIT_C_EQUAL : SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline)); + } +} + +#ifdef SUPPORT_UTF + +#if defined COMPILE_PCRE8 +static void do_utfreadchar(compiler_common *common) +{ +/* Fast decoding a UTF-8 character. TMP1 contains the first byte +of the character (>= 0xc0). Return char value in TMP1, length - 1 in TMP2. */ +DEFINE_COMPILER; +struct sljit_jump *jump; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); +/* Searching for the first zero. */ +OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20); +jump = JUMP(SLJIT_C_NOT_ZERO); +/* Two byte sequence. */ +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1f); +OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +JUMPHERE(jump); + +OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10); +jump = JUMP(SLJIT_C_NOT_ZERO); +/* Three byte sequence. */ +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0f); +OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 12); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2)); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, IN_UCHARS(2)); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +JUMPHERE(jump); + +/* Four byte sequence. */ +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x07); +OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 18); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 12); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2)); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(3)); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3)); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, IN_UCHARS(3)); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +static void do_utfreadtype8(compiler_common *common) +{ +/* Fast decoding a UTF-8 character type. TMP2 contains the first byte +of the character (>= 0xc0). Return value in TMP1. */ +DEFINE_COMPILER; +struct sljit_jump *jump; +struct sljit_jump *compare; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); + +OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x20); +jump = JUMP(SLJIT_C_NOT_ZERO); +/* Two byte sequence. */ +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x1f); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_OR, TMP2, 0, TMP2, 0, TMP1, 0); +compare = CMP(SLJIT_C_GREATER, TMP2, 0, SLJIT_IMM, 255); +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); + +JUMPHERE(compare); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +JUMPHERE(jump); + +/* We only have types for characters less than 256. */ +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(utf8_table4) - 0xc0); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +#elif defined COMPILE_PCRE16 + +static void do_utfreadchar(compiler_common *common) +{ +/* Fast decoding a UTF-16 character. TMP1 contains the first 16 bit char +of the character (>= 0xd800). Return char value in TMP1, length - 1 in TMP2. */ +DEFINE_COMPILER; +struct sljit_jump *jump; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); +jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xdc00); +/* Do nothing, only return. */ +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); + +JUMPHERE(jump); +/* Combine two 16 bit characters. */ +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3ff); +OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 10); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3ff); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +#endif /* COMPILE_PCRE[8|16] */ + +#endif /* SUPPORT_UTF */ + +#ifdef SUPPORT_UCP + +/* UCD_BLOCK_SIZE must be 128 (see the assert below). */ +#define UCD_BLOCK_MASK 127 +#define UCD_BLOCK_SHIFT 7 + +static void do_getucd(compiler_common *common) +{ +/* Search the UCD record for the character comes in TMP1. +Returns chartype in TMP1 and UCD offset in TMP2. */ +DEFINE_COMPILER; + +SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 8); + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); +OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); +OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2)); +OP1(SLJIT_MOV_UH, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype)); +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM2(TMP1, TMP2), 3); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} +#endif + +static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common, BOOL hascrorlf, BOOL firstline) +{ +DEFINE_COMPILER; +struct sljit_label *mainloop; +struct sljit_label *newlinelabel = NULL; +struct sljit_jump *start; +struct sljit_jump *end = NULL; +struct sljit_jump *nl = NULL; +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 +struct sljit_jump *singlechar; +#endif +jump_list *newline = NULL; +BOOL newlinecheck = FALSE; +BOOL readuchar = FALSE; + +if (!(hascrorlf || firstline) && (common->nltype == NLTYPE_ANY || + common->nltype == NLTYPE_ANYCRLF || common->newline > 255)) + newlinecheck = TRUE; + +if (firstline) + { + /* Search for the end of the first line. */ + SLJIT_ASSERT(common->first_line_end != 0); + OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); + + if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + mainloop = LABEL(); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + end = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); + CMPTO(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, mainloop); + CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, mainloop); + JUMPHERE(end); + OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_LOCALS_REG), common->first_line_end, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + } + else + { + end = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + mainloop = LABEL(); + /* Continual stores does not cause data dependency. */ + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->first_line_end, STR_PTR, 0); + read_char(common); + check_newlinechar(common, common->nltype, &newline, TRUE); + CMPTO(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0, mainloop); + JUMPHERE(end); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->first_line_end, STR_PTR, 0); + set_jumps(newline, LABEL()); + } + + OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); + } + +start = JUMP(SLJIT_JUMP); + +if (newlinecheck) + { + newlinelabel = LABEL(); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + end = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, common->newline & 0xff); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); +#endif + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + nl = JUMP(SLJIT_JUMP); + } + +mainloop = LABEL(); + +/* Increasing the STR_PTR here requires one less jump in the most common case. */ +#ifdef SUPPORT_UTF +if (common->utf) readuchar = TRUE; +#endif +if (newlinecheck) readuchar = TRUE; + +if (readuchar) + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + +if (newlinecheck) + CMPTO(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, newlinelabel); + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#if defined SUPPORT_UTF && !defined COMPILE_PCRE32 +#if defined COMPILE_PCRE8 +if (common->utf) + { + singlechar = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + JUMPHERE(singlechar); + } +#elif defined COMPILE_PCRE16 +if (common->utf) + { + singlechar = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xd800); + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + JUMPHERE(singlechar); + } +#endif /* COMPILE_PCRE[8|16] */ +#endif /* SUPPORT_UTF && !COMPILE_PCRE32 */ +JUMPHERE(start); + +if (newlinecheck) + { + JUMPHERE(end); + JUMPHERE(nl); + } + +return mainloop; +} + +#define MAX_N_CHARS 3 + +static SLJIT_INLINE BOOL fast_forward_first_n_chars(compiler_common *common, BOOL firstline) +{ +DEFINE_COMPILER; +struct sljit_label *start; +struct sljit_jump *quit; +pcre_uint32 chars[MAX_N_CHARS * 2]; +pcre_uchar *cc = common->start + 1 + LINK_SIZE; +int location = 0; +pcre_int32 len, c, bit, caseless; +int must_stop; + +/* We do not support alternatives now. */ +if (*(common->start + GET(common->start, 1)) == OP_ALT) + return FALSE; + +while (TRUE) + { + caseless = 0; + must_stop = 1; + switch(*cc) + { + case OP_CHAR: + must_stop = 0; + cc++; + break; + + case OP_CHARI: + caseless = 1; + must_stop = 0; + cc++; + break; + + case OP_SOD: + case OP_SOM: + case OP_SET_SOM: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + case OP_EODN: + case OP_EOD: + case OP_CIRC: + case OP_CIRCM: + case OP_DOLL: + case OP_DOLLM: + /* Zero width assertions. */ + cc++; + continue; + + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + cc++; + break; + + case OP_EXACT: + cc += 1 + IMM2_SIZE; + break; + + case OP_PLUSI: + case OP_MINPLUSI: + case OP_POSPLUSI: + caseless = 1; + cc++; + break; + + case OP_EXACTI: + caseless = 1; + cc += 1 + IMM2_SIZE; + break; + + default: + must_stop = 2; + break; + } + + if (must_stop == 2) + break; + + len = 1; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[0])) len += GET_EXTRALEN(cc[0]); +#endif + + if (caseless && char_has_othercase(common, cc)) + { + caseless = char_get_othercase_bit(common, cc); + if (caseless == 0) + return FALSE; +#ifdef COMPILE_PCRE8 + caseless = ((caseless & 0xff) << 8) | (len - (caseless >> 8)); +#else + if ((caseless & 0x100) != 0) + caseless = ((caseless & 0xff) << 16) | (len - (caseless >> 9)); + else + caseless = ((caseless & 0xff) << 8) | (len - (caseless >> 9)); +#endif + } + else + caseless = 0; + + while (len > 0 && location < MAX_N_CHARS * 2) + { + c = *cc; + bit = 0; + if (len == (caseless & 0xff)) + { + bit = caseless >> 8; + c |= bit; + } + + chars[location] = c; + chars[location + 1] = bit; + + len--; + location += 2; + cc++; + } + + if (location >= MAX_N_CHARS * 2 || must_stop != 0) + break; + } + +/* At least two characters are required. */ +if (location < 2 * 2) + return FALSE; + +if (firstline) + { + SLJIT_ASSERT(common->first_line_end != 0); + OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); + OP2(SLJIT_SUB, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->first_line_end, SLJIT_IMM, IN_UCHARS((location >> 1) - 1)); + } +else + OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS((location >> 1) - 1)); + +start = LABEL(); +quit = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +if (chars[1] != 0) + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, chars[1]); +CMPTO(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[0], start); +if (location > 2 * 2) + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +if (chars[3] != 0) + OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, chars[3]); +CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, chars[2], start); +if (location > 2 * 2) + { + if (chars[5] != 0) + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, chars[5]); + CMPTO(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[4], start); + } +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +JUMPHERE(quit); + +if (firstline) + OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); +else + OP2(SLJIT_ADD, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS((location >> 1) - 1)); +return TRUE; +} + +#undef MAX_N_CHARS + +static SLJIT_INLINE void fast_forward_first_char(compiler_common *common, pcre_uchar first_char, BOOL caseless, BOOL firstline) +{ +DEFINE_COMPILER; +struct sljit_label *start; +struct sljit_jump *quit; +struct sljit_jump *found; +pcre_uchar oc, bit; + +if (firstline) + { + SLJIT_ASSERT(common->first_line_end != 0); + OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->first_line_end); + } + +start = LABEL(); +quit = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + +oc = first_char; +if (caseless) + { + oc = TABLE_GET(first_char, common->fcc, first_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (first_char > 127 && common->utf) + oc = UCD_OTHERCASE(first_char); +#endif + } +if (first_char == oc) + found = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, first_char); +else + { + bit = first_char ^ oc; + if (is_powerof2(bit)) + { + OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, bit); + found = CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, first_char | bit); + } + else + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, first_char); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, oc); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + found = JUMP(SLJIT_C_NOT_ZERO); + } + } + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +JUMPTO(SLJIT_JUMP, start); +JUMPHERE(found); +JUMPHERE(quit); + +if (firstline) + OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); +} + +static SLJIT_INLINE void fast_forward_newline(compiler_common *common, BOOL firstline) +{ +DEFINE_COMPILER; +struct sljit_label *loop; +struct sljit_jump *lastchar; +struct sljit_jump *firstchar; +struct sljit_jump *quit; +struct sljit_jump *foundcr = NULL; +struct sljit_jump *notfoundnl; +jump_list *newline = NULL; + +if (firstline) + { + SLJIT_ASSERT(common->first_line_end != 0); + OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->first_line_end); + } + +if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + lastchar = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); + firstchar = CMP(SLJIT_C_LESS_EQUAL, STR_PTR, 0, TMP2, 0); + + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(2)); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_GREATER_EQUAL); +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCHAR_SHIFT); +#endif + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); + + loop = LABEL(); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + quit = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); + CMPTO(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, loop); + CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, loop); + + JUMPHERE(quit); + JUMPHERE(firstchar); + JUMPHERE(lastchar); + + if (firstline) + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0); + return; + } + +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); +firstchar = CMP(SLJIT_C_LESS_EQUAL, STR_PTR, 0, TMP2, 0); +skip_char_back(common); + +loop = LABEL(); +read_char(common); +lastchar = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF) + foundcr = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); +check_newlinechar(common, common->nltype, &newline, FALSE); +set_jumps(newline, loop); + +if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF) + { + quit = JUMP(SLJIT_JUMP); + JUMPHERE(foundcr); + notfoundnl = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); +#endif + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + JUMPHERE(notfoundnl); + JUMPHERE(quit); + } +JUMPHERE(lastchar); +JUMPHERE(firstchar); + +if (firstline) + OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); +} + +static BOOL check_class_ranges(compiler_common *common, const pcre_uint8 *bits, BOOL nclass, jump_list **backtracks); + +static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common, sljit_uw start_bits, BOOL firstline) +{ +DEFINE_COMPILER; +struct sljit_label *start; +struct sljit_jump *quit; +struct sljit_jump *found = NULL; +jump_list *matches = NULL; +pcre_uint8 inverted_start_bits[32]; +int i; +#ifndef COMPILE_PCRE8 +struct sljit_jump *jump; +#endif + +for (i = 0; i < 32; ++i) + inverted_start_bits[i] = ~(((pcre_uint8*)start_bits)[i]); + +if (firstline) + { + SLJIT_ASSERT(common->first_line_end != 0); + OP1(SLJIT_MOV, RETURN_ADDR, 0, STR_END, 0); + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->first_line_end); + } + +start = LABEL(); +quit = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); +#ifdef SUPPORT_UTF +if (common->utf) + OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); +#endif + +if (!check_class_ranges(common, inverted_start_bits, (inverted_start_bits[31] & 0x80) != 0, &matches)) + { +#ifndef COMPILE_PCRE8 + jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 255); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 255); + JUMPHERE(jump); +#endif + OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); + OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), start_bits); + OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + found = JUMP(SLJIT_C_NOT_ZERO); + } + +#ifdef SUPPORT_UTF +if (common->utf) + OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); +#endif +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#ifdef SUPPORT_UTF +#if defined COMPILE_PCRE8 +if (common->utf) + { + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0, start); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + } +#elif defined COMPILE_PCRE16 +if (common->utf) + { + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xd800, start); + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + } +#endif /* COMPILE_PCRE[8|16] */ +#endif /* SUPPORT_UTF */ +JUMPTO(SLJIT_JUMP, start); +if (found != NULL) + JUMPHERE(found); +if (matches != NULL) + set_jumps(matches, LABEL()); +JUMPHERE(quit); + +if (firstline) + OP1(SLJIT_MOV, STR_END, 0, RETURN_ADDR, 0); +} + +static SLJIT_INLINE struct sljit_jump *search_requested_char(compiler_common *common, pcre_uchar req_char, BOOL caseless, BOOL has_firstchar) +{ +DEFINE_COMPILER; +struct sljit_label *loop; +struct sljit_jump *toolong; +struct sljit_jump *alreadyfound; +struct sljit_jump *found; +struct sljit_jump *foundoc = NULL; +struct sljit_jump *notfound; +pcre_uint32 oc, bit; + +SLJIT_ASSERT(common->req_char_ptr != 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->req_char_ptr); +OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, REQ_BYTE_MAX); +toolong = CMP(SLJIT_C_LESS, TMP1, 0, STR_END, 0); +alreadyfound = CMP(SLJIT_C_LESS, STR_PTR, 0, TMP2, 0); + +if (has_firstchar) + OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +else + OP1(SLJIT_MOV, TMP1, 0, STR_PTR, 0); + +loop = LABEL(); +notfound = CMP(SLJIT_C_GREATER_EQUAL, TMP1, 0, STR_END, 0); + +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(TMP1), 0); +oc = req_char; +if (caseless) + { + oc = TABLE_GET(req_char, common->fcc, req_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (req_char > 127 && common->utf) + oc = UCD_OTHERCASE(req_char); +#endif + } +if (req_char == oc) + found = CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, req_char); +else + { + bit = req_char ^ oc; + if (is_powerof2(bit)) + { + OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, bit); + found = CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, req_char | bit); + } + else + { + found = CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, req_char); + foundoc = CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, oc); + } + } +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); +JUMPTO(SLJIT_JUMP, loop); + +JUMPHERE(found); +if (foundoc) + JUMPHERE(foundoc); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->req_char_ptr, TMP1, 0); +JUMPHERE(alreadyfound); +JUMPHERE(toolong); +return notfound; +} + +static void do_revertframes(compiler_common *common) +{ +DEFINE_COMPILER; +struct sljit_jump *jump; +struct sljit_label *mainloop; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); +OP1(SLJIT_MOV, TMP1, 0, STACK_TOP, 0); +GET_LOCAL_BASE(TMP3, 0, 0); + +/* Drop frames until we reach STACK_TOP. */ +mainloop = LABEL(); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), 0); +OP2(SLJIT_SUB | SLJIT_SET_S, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0); +jump = JUMP(SLJIT_C_SIG_LESS_EQUAL); + +OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP3, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(TMP1), sizeof(sljit_sw)); +OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(TMP1), 2 * sizeof(sljit_sw)); +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); +JUMPTO(SLJIT_JUMP, mainloop); + +JUMPHERE(jump); +jump = JUMP(SLJIT_C_SIG_LESS); +/* End of dropping frames. */ +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); + +JUMPHERE(jump); +OP1(SLJIT_NEG, TMP2, 0, TMP2, 0); +OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP3, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(TMP1), sizeof(sljit_sw)); +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_sw)); +JUMPTO(SLJIT_JUMP, mainloop); +} + +static void check_wordboundary(compiler_common *common) +{ +DEFINE_COMPILER; +struct sljit_jump *skipread; +jump_list *skipread_list = NULL; +#if !(defined COMPILE_PCRE8) || defined SUPPORT_UTF +struct sljit_jump *jump; +#endif + +SLJIT_COMPILE_ASSERT(ctype_word == 0x10, ctype_word_must_be_16); + +sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); +/* Get type of the previous char, and put it to LOCALS1. */ +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, SLJIT_IMM, 0); +skipread = CMP(SLJIT_C_LESS_EQUAL, STR_PTR, 0, TMP1, 0); +skip_char_back(common); +check_start_used_ptr(common); +read_char(common); + +/* Testing char type. */ +#ifdef SUPPORT_UCP +if (common->use_ucp) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 1); + jump = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE); + add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_LESS_EQUAL); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_LESS_EQUAL); + JUMPHERE(jump); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, TMP2, 0); + } +else +#endif + { +#ifndef COMPILE_PCRE8 + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#elif defined SUPPORT_UTF + /* Here LOCALS1 has already been zeroed. */ + jump = NULL; + if (common->utf) + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#endif /* COMPILE_PCRE8 */ + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), common->ctypes); + OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 4 /* ctype_word */); + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, TMP1, 0); +#ifndef COMPILE_PCRE8 + JUMPHERE(jump); +#elif defined SUPPORT_UTF + if (jump != NULL) + JUMPHERE(jump); +#endif /* COMPILE_PCRE8 */ + } +JUMPHERE(skipread); + +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); +check_str_end(common, &skipread_list); +peek_char(common); + +/* Testing char type. This is a code duplication. */ +#ifdef SUPPORT_UCP +if (common->use_ucp) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 1); + jump = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE); + add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_LESS_EQUAL); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_LESS_EQUAL); + JUMPHERE(jump); + } +else +#endif + { +#ifndef COMPILE_PCRE8 + /* TMP2 may be destroyed by peek_char. */ + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#elif defined SUPPORT_UTF + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); + jump = NULL; + if (common->utf) + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#endif + OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), common->ctypes); + OP2(SLJIT_LSHR, TMP2, 0, TMP2, 0, SLJIT_IMM, 4 /* ctype_word */); + OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); +#ifndef COMPILE_PCRE8 + JUMPHERE(jump); +#elif defined SUPPORT_UTF + if (jump != NULL) + JUMPHERE(jump); +#endif /* COMPILE_PCRE8 */ + } +set_jumps(skipread_list, LABEL()); + +OP2(SLJIT_XOR | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1); +sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); +} + +/* + range format: + + ranges[0] = length of the range (max MAX_RANGE_SIZE, -1 means invalid range). + ranges[1] = first bit (0 or 1) + ranges[2-length] = position of the bit change (when the current bit is not equal to the previous) +*/ + +static BOOL check_ranges(compiler_common *common, int *ranges, jump_list **backtracks, BOOL readch) +{ +DEFINE_COMPILER; +struct sljit_jump *jump; + +if (ranges[0] < 0) + return FALSE; + +switch(ranges[0]) + { + case 1: + if (readch) + read_char(common); + add_jump(compiler, backtracks, CMP(ranges[1] == 0 ? SLJIT_C_LESS : SLJIT_C_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2])); + return TRUE; + + case 2: + if (readch) + read_char(common); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[2]); + add_jump(compiler, backtracks, CMP(ranges[1] != 0 ? SLJIT_C_LESS : SLJIT_C_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[3] - ranges[2])); + return TRUE; + + case 4: + if (ranges[2] + 1 == ranges[3] && ranges[4] + 1 == ranges[5]) + { + if (readch) + read_char(common); + if (ranges[1] != 0) + { + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2])); + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, ranges[4])); + } + else + { + jump = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2]); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[4])); + JUMPHERE(jump); + } + return TRUE; + } + if ((ranges[3] - ranges[2]) == (ranges[5] - ranges[4]) && is_powerof2(ranges[4] - ranges[2])) + { + if (readch) + read_char(common); + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[4] - ranges[2]); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[4]); + add_jump(compiler, backtracks, CMP(ranges[1] != 0 ? SLJIT_C_LESS : SLJIT_C_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[5] - ranges[4])); + return TRUE; + } + return FALSE; + + default: + return FALSE; + } +} + +static void get_ctype_ranges(compiler_common *common, int flag, int *ranges) +{ +int i, bit, length; +const pcre_uint8 *ctypes = (const pcre_uint8*)common->ctypes; + +bit = ctypes[0] & flag; +ranges[0] = -1; +ranges[1] = bit != 0 ? 1 : 0; +length = 0; + +for (i = 1; i < 256; i++) + if ((ctypes[i] & flag) != bit) + { + if (length >= MAX_RANGE_SIZE) + return; + ranges[2 + length] = i; + length++; + bit ^= flag; + } + +if (bit != 0) + { + if (length >= MAX_RANGE_SIZE) + return; + ranges[2 + length] = 256; + length++; + } +ranges[0] = length; +} + +static BOOL check_class_ranges(compiler_common *common, const pcre_uint8 *bits, BOOL nclass, jump_list **backtracks) +{ +int ranges[2 + MAX_RANGE_SIZE]; +pcre_uint8 bit, cbit, all; +int i, byte, length = 0; + +bit = bits[0] & 0x1; +ranges[1] = bit; +/* Can be 0 or 255. */ +all = -bit; + +for (i = 0; i < 256; ) + { + byte = i >> 3; + if ((i & 0x7) == 0 && bits[byte] == all) + i += 8; + else + { + cbit = (bits[byte] >> (i & 0x7)) & 0x1; + if (cbit != bit) + { + if (length >= MAX_RANGE_SIZE) + return FALSE; + ranges[2 + length] = i; + length++; + bit = cbit; + all = -cbit; + } + i++; + } + } + +if (((bit == 0) && nclass) || ((bit == 1) && !nclass)) + { + if (length >= MAX_RANGE_SIZE) + return FALSE; + ranges[2 + length] = 256; + length++; + } +ranges[0] = length; + +return check_ranges(common, ranges, backtracks, FALSE); +} + +static void check_anynewline(compiler_common *common) +{ +/* Check whether TMP1 contains a newline character. TMP2 destroyed. */ +DEFINE_COMPILER; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); + +OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a); +OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); +OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_LESS_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +#ifdef COMPILE_PCRE8 +if (common->utf) + { +#endif + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); +#ifdef COMPILE_PCRE8 + } +#endif +#endif /* SUPPORT_UTF || COMPILE_PCRE16 || COMPILE_PCRE32 */ +OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +static void check_hspace(compiler_common *common) +{ +/* Check whether TMP1 contains a newline character. TMP2 destroyed. */ +DEFINE_COMPILER; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); + +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x09); +OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20); +OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xa0); +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +#ifdef COMPILE_PCRE8 +if (common->utf) + { +#endif + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x1680); + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e); + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2000); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000); + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000); + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000); + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000); +#ifdef COMPILE_PCRE8 + } +#endif +#endif /* SUPPORT_UTF || COMPILE_PCRE16 || COMPILE_PCRE32 */ +OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +static void check_vspace(compiler_common *common) +{ +/* Check whether TMP1 contains a newline character. TMP2 destroyed. */ +DEFINE_COMPILER; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); + +OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a); +OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); +OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_LESS_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +#ifdef COMPILE_PCRE8 +if (common->utf) + { +#endif + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); +#ifdef COMPILE_PCRE8 + } +#endif +#endif /* SUPPORT_UTF || COMPILE_PCRE16 || COMPILE_PCRE32 */ +OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +#define CHAR1 STR_END +#define CHAR2 STACK_TOP + +static void do_casefulcmp(compiler_common *common) +{ +DEFINE_COMPILER; +struct sljit_jump *jump; +struct sljit_label *label; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP3, 0, CHAR1, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, CHAR2, 0); +OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +label = LABEL(); +OP1(MOVU_UCHAR, CHAR1, 0, SLJIT_MEM1(TMP1), IN_UCHARS(1)); +OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +jump = CMP(SLJIT_C_NOT_EQUAL, CHAR1, 0, CHAR2, 0); +OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); +JUMPTO(SLJIT_C_NOT_ZERO, label); + +JUMPHERE(jump); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +OP1(SLJIT_MOV, CHAR1, 0, TMP3, 0); +OP1(SLJIT_MOV, CHAR2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +#define LCC_TABLE STACK_LIMIT + +static void do_caselesscmp(compiler_common *common) +{ +DEFINE_COMPILER; +struct sljit_jump *jump; +struct sljit_label *label; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); + +OP1(SLJIT_MOV, TMP3, 0, LCC_TABLE, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, CHAR1, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, CHAR2, 0); +OP1(SLJIT_MOV, LCC_TABLE, 0, SLJIT_IMM, common->lcc); +OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +label = LABEL(); +OP1(MOVU_UCHAR, CHAR1, 0, SLJIT_MEM1(TMP1), IN_UCHARS(1)); +OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +#ifndef COMPILE_PCRE8 +jump = CMP(SLJIT_C_GREATER, CHAR1, 0, SLJIT_IMM, 255); +#endif +OP1(SLJIT_MOV_UB, CHAR1, 0, SLJIT_MEM2(LCC_TABLE, CHAR1), 0); +#ifndef COMPILE_PCRE8 +JUMPHERE(jump); +jump = CMP(SLJIT_C_GREATER, CHAR2, 0, SLJIT_IMM, 255); +#endif +OP1(SLJIT_MOV_UB, CHAR2, 0, SLJIT_MEM2(LCC_TABLE, CHAR2), 0); +#ifndef COMPILE_PCRE8 +JUMPHERE(jump); +#endif +jump = CMP(SLJIT_C_NOT_EQUAL, CHAR1, 0, CHAR2, 0); +OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); +JUMPTO(SLJIT_C_NOT_ZERO, label); + +JUMPHERE(jump); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +OP1(SLJIT_MOV, LCC_TABLE, 0, TMP3, 0); +OP1(SLJIT_MOV, CHAR1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); +OP1(SLJIT_MOV, CHAR2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +#undef LCC_TABLE +#undef CHAR1 +#undef CHAR2 + +#if defined SUPPORT_UTF && defined SUPPORT_UCP + +static const pcre_uchar * SLJIT_CALL do_utf_caselesscmp(pcre_uchar *src1, jit_arguments *args, pcre_uchar *end1) +{ +/* This function would be ineffective to do in JIT level. */ +pcre_uint32 c1, c2; +const pcre_uchar *src2 = args->uchar_ptr; +const pcre_uchar *end2 = args->end; +const ucd_record *ur; +const pcre_uint32 *pp; + +while (src1 < end1) + { + if (src2 >= end2) + return (pcre_uchar*)1; + GETCHARINC(c1, src1); + GETCHARINC(c2, src2); + ur = GET_UCD(c2); + if (c1 != c2 && c1 != c2 + ur->other_case) + { + pp = PRIV(ucd_caseless_sets) + ur->caseset; + for (;;) + { + if (c1 < *pp) return NULL; + if (c1 == *pp++) break; + } + } + } +return src2; +} + +#endif /* SUPPORT_UTF && SUPPORT_UCP */ + +static pcre_uchar *byte_sequence_compare(compiler_common *common, BOOL caseless, pcre_uchar *cc, + compare_context* context, jump_list **backtracks) +{ +DEFINE_COMPILER; +unsigned int othercasebit = 0; +pcre_uchar *othercasechar = NULL; +#ifdef SUPPORT_UTF +int utflength; +#endif + +if (caseless && char_has_othercase(common, cc)) + { + othercasebit = char_get_othercase_bit(common, cc); + SLJIT_ASSERT(othercasebit); + /* Extracting bit difference info. */ +#if defined COMPILE_PCRE8 + othercasechar = cc + (othercasebit >> 8); + othercasebit &= 0xff; +#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + /* Note that this code only handles characters in the BMP. If there + ever are characters outside the BMP whose othercase differs in only one + bit from itself (there currently are none), this code will need to be + revised for COMPILE_PCRE32. */ + othercasechar = cc + (othercasebit >> 9); + if ((othercasebit & 0x100) != 0) + othercasebit = (othercasebit & 0xff) << 8; + else + othercasebit &= 0xff; +#endif /* COMPILE_PCRE[8|16|32] */ + } + +if (context->sourcereg == -1) + { +#if defined COMPILE_PCRE8 +#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED + if (context->length >= 4) + OP1(SLJIT_MOV_SI, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); + else if (context->length >= 2) + OP1(SLJIT_MOV_UH, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); + else +#endif + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); +#elif defined COMPILE_PCRE16 +#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED + if (context->length >= 4) + OP1(SLJIT_MOV_SI, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); + else +#endif + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); +#elif defined COMPILE_PCRE32 + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); +#endif /* COMPILE_PCRE[8|16|32] */ + context->sourcereg = TMP2; + } + +#ifdef SUPPORT_UTF +utflength = 1; +if (common->utf && HAS_EXTRALEN(*cc)) + utflength += GET_EXTRALEN(*cc); + +do + { +#endif + + context->length -= IN_UCHARS(1); +#if (defined SLJIT_UNALIGNED && SLJIT_UNALIGNED) && (defined COMPILE_PCRE8 || defined COMPILE_PCRE16) + + /* Unaligned read is supported. */ + if (othercasebit != 0 && othercasechar == cc) + { + context->c.asuchars[context->ucharptr] = *cc | othercasebit; + context->oc.asuchars[context->ucharptr] = othercasebit; + } + else + { + context->c.asuchars[context->ucharptr] = *cc; + context->oc.asuchars[context->ucharptr] = 0; + } + context->ucharptr++; + +#if defined COMPILE_PCRE8 + if (context->ucharptr >= 4 || context->length == 0 || (context->ucharptr == 2 && context->length == 1)) +#else + if (context->ucharptr >= 2 || context->length == 0) +#endif + { + if (context->length >= 4) + OP1(SLJIT_MOV_SI, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); + else if (context->length >= 2) + OP1(SLJIT_MOV_UH, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); +#if defined COMPILE_PCRE8 + else if (context->length >= 1) + OP1(SLJIT_MOV_UB, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); +#endif /* COMPILE_PCRE8 */ + context->sourcereg = context->sourcereg == TMP1 ? TMP2 : TMP1; + + switch(context->ucharptr) + { + case 4 / sizeof(pcre_uchar): + if (context->oc.asint != 0) + OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asint); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asint | context->oc.asint)); + break; + + case 2 / sizeof(pcre_uchar): + if (context->oc.asushort != 0) + OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asushort); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asushort | context->oc.asushort)); + break; + +#ifdef COMPILE_PCRE8 + case 1: + if (context->oc.asbyte != 0) + OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asbyte); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asbyte | context->oc.asbyte)); + break; +#endif + + default: + SLJIT_ASSERT_STOP(); + break; + } + context->ucharptr = 0; + } + +#else + + /* Unaligned read is unsupported or in 32 bit mode. */ + if (context->length >= 1) + OP1(MOV_UCHAR, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); + + context->sourcereg = context->sourcereg == TMP1 ? TMP2 : TMP1; + + if (othercasebit != 0 && othercasechar == cc) + { + OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, othercasebit); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, *cc | othercasebit)); + } + else + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, *cc)); + +#endif + + cc++; +#ifdef SUPPORT_UTF + utflength--; + } +while (utflength > 0); +#endif + +return cc; +} + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + +#define SET_TYPE_OFFSET(value) \ + if ((value) != typeoffset) \ + { \ + if ((value) > typeoffset) \ + OP2(SLJIT_SUB, typereg, 0, typereg, 0, SLJIT_IMM, (value) - typeoffset); \ + else \ + OP2(SLJIT_ADD, typereg, 0, typereg, 0, SLJIT_IMM, typeoffset - (value)); \ + } \ + typeoffset = (value); + +#define SET_CHAR_OFFSET(value) \ + if ((value) != charoffset) \ + { \ + if ((value) > charoffset) \ + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, (value) - charoffset); \ + else \ + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, charoffset - (value)); \ + } \ + charoffset = (value); + +static void compile_xclass_matchingpath(compiler_common *common, pcre_uchar *cc, jump_list **backtracks) +{ +DEFINE_COMPILER; +jump_list *found = NULL; +jump_list **list = (*cc & XCL_NOT) == 0 ? &found : backtracks; +pcre_int32 c, charoffset; +const pcre_uint32 *other_cases; +struct sljit_jump *jump = NULL; +pcre_uchar *ccbegin; +int compares, invertcmp, numberofcmps; +#ifdef SUPPORT_UCP +BOOL needstype = FALSE, needsscript = FALSE, needschar = FALSE; +BOOL charsaved = FALSE; +int typereg = TMP1, scriptreg = TMP1; +pcre_int32 typeoffset; +#endif + +/* Although SUPPORT_UTF must be defined, we are + not necessary in utf mode even in 8 bit mode. */ +detect_partial_match(common, backtracks); +read_char(common); + +if ((*cc++ & XCL_MAP) != 0) + { + OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); +#ifndef COMPILE_PCRE8 + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#elif defined SUPPORT_UTF + if (common->utf) + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#endif + + if (!check_class_ranges(common, (const pcre_uint8 *)cc, TRUE, list)) + { + OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); + OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc); + OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + add_jump(compiler, list, JUMP(SLJIT_C_NOT_ZERO)); + } + +#ifndef COMPILE_PCRE8 + JUMPHERE(jump); +#elif defined SUPPORT_UTF + if (common->utf) + JUMPHERE(jump); +#endif + OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); +#ifdef SUPPORT_UCP + charsaved = TRUE; +#endif + cc += 32 / sizeof(pcre_uchar); + } + +/* Scanning the necessary info. */ +ccbegin = cc; +compares = 0; +while (*cc != XCL_END) + { + compares++; + if (*cc == XCL_SINGLE) + { + cc += 2; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif +#ifdef SUPPORT_UCP + needschar = TRUE; +#endif + } + else if (*cc == XCL_RANGE) + { + cc += 2; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + cc++; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif +#ifdef SUPPORT_UCP + needschar = TRUE; +#endif + } +#ifdef SUPPORT_UCP + else + { + SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP); + cc++; + switch(*cc) + { + case PT_ANY: + break; + + case PT_LAMP: + case PT_GC: + case PT_PC: + case PT_ALNUM: + needstype = TRUE; + break; + + case PT_SC: + needsscript = TRUE; + break; + + case PT_SPACE: + case PT_PXSPACE: + case PT_WORD: + needstype = TRUE; + needschar = TRUE; + break; + + case PT_CLIST: + case PT_UCNC: + needschar = TRUE; + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } + cc += 2; + } +#endif + } + +#ifdef SUPPORT_UCP +/* Simple register allocation. TMP1 is preferred if possible. */ +if (needstype || needsscript) + { + if (needschar && !charsaved) + OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); + add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); + if (needschar) + { + if (needstype) + { + OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); + typereg = RETURN_ADDR; + } + + if (needsscript) + scriptreg = TMP3; + OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); + } + else if (needstype && needsscript) + scriptreg = TMP3; + /* In all other cases only one of them was specified, and that can goes to TMP1. */ + + if (needsscript) + { + if (scriptreg == TMP1) + { + OP1(SLJIT_MOV, scriptreg, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script)); + OP1(SLJIT_MOV_UB, scriptreg, 0, SLJIT_MEM2(scriptreg, TMP2), 3); + } + else + { + OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 3); + OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script)); + OP1(SLJIT_MOV_UB, scriptreg, 0, SLJIT_MEM1(TMP2), 0); + } + } + } +#endif + +/* Generating code. */ +cc = ccbegin; +charoffset = 0; +numberofcmps = 0; +#ifdef SUPPORT_UCP +typeoffset = 0; +#endif + +while (*cc != XCL_END) + { + compares--; + invertcmp = (compares == 0 && list != backtracks); + jump = NULL; + + if (*cc == XCL_SINGLE) + { + cc ++; +#ifdef SUPPORT_UTF + if (common->utf) + { + GETCHARINC(c, cc); + } + else +#endif + c = *cc++; + + if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE)) + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, c - charoffset); + OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, numberofcmps == 0 ? SLJIT_UNUSED : TMP2, 0, SLJIT_C_EQUAL); + numberofcmps++; + } + else if (numberofcmps > 0) + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, c - charoffset); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + numberofcmps = 0; + } + else + { + jump = CMP(SLJIT_C_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, c - charoffset); + numberofcmps = 0; + } + } + else if (*cc == XCL_RANGE) + { + cc ++; +#ifdef SUPPORT_UTF + if (common->utf) + { + GETCHARINC(c, cc); + } + else +#endif + c = *cc++; + SET_CHAR_OFFSET(c); +#ifdef SUPPORT_UTF + if (common->utf) + { + GETCHARINC(c, cc); + } + else +#endif + c = *cc++; + if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE)) + { + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, c - charoffset); + OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, numberofcmps == 0 ? SLJIT_UNUSED : TMP2, 0, SLJIT_C_LESS_EQUAL); + numberofcmps++; + } + else if (numberofcmps > 0) + { + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, c - charoffset); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_LESS_EQUAL); + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + numberofcmps = 0; + } + else + { + jump = CMP(SLJIT_C_LESS_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, c - charoffset); + numberofcmps = 0; + } + } +#ifdef SUPPORT_UCP + else + { + if (*cc == XCL_NOTPROP) + invertcmp ^= 0x1; + cc++; + switch(*cc) + { + case PT_ANY: + if (list != backtracks) + { + if ((cc[-1] == XCL_NOTPROP && compares > 0) || (cc[-1] == XCL_PROP && compares == 0)) + continue; + } + else if (cc[-1] == XCL_NOTPROP) + continue; + jump = JUMP(SLJIT_JUMP); + break; + + case PT_LAMP: + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset); + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + break; + + case PT_GC: + c = PRIV(ucp_typerange)[(int)cc[1] * 2]; + SET_TYPE_OFFSET(c); + jump = CMP(SLJIT_C_LESS_EQUAL ^ invertcmp, typereg, 0, SLJIT_IMM, PRIV(ucp_typerange)[(int)cc[1] * 2 + 1] - c); + break; + + case PT_PC: + jump = CMP(SLJIT_C_EQUAL ^ invertcmp, typereg, 0, SLJIT_IMM, (int)cc[1] - typeoffset); + break; + + case PT_SC: + jump = CMP(SLJIT_C_EQUAL ^ invertcmp, scriptreg, 0, SLJIT_IMM, (int)cc[1]); + break; + + case PT_SPACE: + case PT_PXSPACE: + if (*cc == PT_SPACE) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); + jump = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 11 - charoffset); + } + SET_CHAR_OFFSET(9); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 13 - 9); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_LESS_EQUAL); + if (*cc == PT_SPACE) + JUMPHERE(jump); + + SET_TYPE_OFFSET(ucp_Zl); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_LESS_EQUAL); + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + break; + + case PT_WORD: + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE - charoffset); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + /* Fall through. */ + + case PT_ALNUM: + SET_TYPE_OFFSET(ucp_Ll); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, (*cc == PT_ALNUM) ? SLJIT_UNUSED : TMP2, 0, SLJIT_C_LESS_EQUAL); + SET_TYPE_OFFSET(ucp_Nd); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_LESS_EQUAL); + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + break; + + case PT_CLIST: + other_cases = PRIV(ucd_caseless_sets) + cc[1]; + + /* At least three characters are required. + Otherwise this case would be handled by the normal code path. */ + SLJIT_ASSERT(other_cases[0] != NOTACHAR && other_cases[1] != NOTACHAR && other_cases[2] != NOTACHAR); + SLJIT_ASSERT(other_cases[0] < other_cases[1] && other_cases[1] < other_cases[2]); + + /* Optimizing character pairs, if their difference is power of 2. */ + if (is_powerof2(other_cases[1] ^ other_cases[0])) + { + if (charoffset == 0) + OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]); + else + { + OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset); + OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]); + } + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[1]); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + other_cases += 2; + } + else if (is_powerof2(other_cases[2] ^ other_cases[1])) + { + if (charoffset == 0) + OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, other_cases[2] ^ other_cases[1]); + else + { + OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset); + OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]); + } + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[2]); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, other_cases[0] - charoffset); + OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_E : 0), TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + + other_cases += 3; + } + else + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, *other_cases++ - charoffset); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + } + + while (*other_cases != NOTACHAR) + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, *other_cases++ - charoffset); + OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_E : 0), TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + } + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + break; + + case PT_UCNC: + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_DOLLAR_SIGN - charoffset); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_COMMERCIAL_AT - charoffset); + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_GRAVE_ACCENT - charoffset); + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + + SET_CHAR_OFFSET(0xa0); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd7ff - charoffset); + OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_C_LESS_EQUAL); + SET_CHAR_OFFSET(0); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xe000 - 0); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_GREATER_EQUAL); + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + break; + } + cc += 2; + } +#endif + + if (jump != NULL) + add_jump(compiler, compares > 0 ? list : backtracks, jump); + } + +if (found != NULL) + set_jumps(found, LABEL()); +} + +#undef SET_TYPE_OFFSET +#undef SET_CHAR_OFFSET + +#endif + +static pcre_uchar *compile_char1_matchingpath(compiler_common *common, pcre_uchar type, pcre_uchar *cc, jump_list **backtracks) +{ +DEFINE_COMPILER; +int length; +unsigned int c, oc, bit; +compare_context context; +struct sljit_jump *jump[4]; +jump_list *end_list; +#ifdef SUPPORT_UTF +struct sljit_label *label; +#ifdef SUPPORT_UCP +pcre_uchar propdata[5]; +#endif +#endif + +switch(type) + { + case OP_SOD: + OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, TMP1, 0)); + return cc; + + case OP_SOM: + OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, TMP1, 0)); + return cc; + + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + add_jump(compiler, &common->wordboundary, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, backtracks, JUMP(type == OP_NOT_WORD_BOUNDARY ? SLJIT_C_NOT_ZERO : SLJIT_C_ZERO)); + return cc; + + case OP_NOT_DIGIT: + case OP_DIGIT: + /* Digits are usually 0-9, so it is worth to optimize them. */ + if (common->digits[0] == -2) + get_ctype_ranges(common, ctype_digit, common->digits); + detect_partial_match(common, backtracks); + /* Flip the starting bit in the negative case. */ + if (type == OP_NOT_DIGIT) + common->digits[1] ^= 1; + if (!check_ranges(common, common->digits, backtracks, TRUE)) + { + read_char8_type(common); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_digit); + add_jump(compiler, backtracks, JUMP(type == OP_DIGIT ? SLJIT_C_ZERO : SLJIT_C_NOT_ZERO)); + } + if (type == OP_NOT_DIGIT) + common->digits[1] ^= 1; + return cc; + + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + detect_partial_match(common, backtracks); + read_char8_type(common); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_space); + add_jump(compiler, backtracks, JUMP(type == OP_WHITESPACE ? SLJIT_C_ZERO : SLJIT_C_NOT_ZERO)); + return cc; + + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + detect_partial_match(common, backtracks); + read_char8_type(common); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_word); + add_jump(compiler, backtracks, JUMP(type == OP_WORDCHAR ? SLJIT_C_ZERO : SLJIT_C_NOT_ZERO)); + return cc; + + case OP_ANY: + detect_partial_match(common, backtracks); + read_char(common); + if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + jump[0] = CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff); + end_list = NULL; + if (common->mode != JIT_PARTIAL_HARD_COMPILE) + add_jump(compiler, &end_list, CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + else + check_str_end(common, &end_list); + + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, common->newline & 0xff)); + set_jumps(end_list, LABEL()); + JUMPHERE(jump[0]); + } + else + check_newlinechar(common, common->nltype, backtracks, TRUE); + return cc; + + case OP_ALLANY: + detect_partial_match(common, backtracks); +#ifdef SUPPORT_UTF + if (common->utf) + { + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#if defined COMPILE_PCRE8 || defined COMPILE_PCRE16 +#if defined COMPILE_PCRE8 + jump[0] = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); +#elif defined COMPILE_PCRE16 + jump[0] = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xd800); + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); +#endif + JUMPHERE(jump[0]); +#endif /* COMPILE_PCRE[8|16] */ + return cc; + } +#endif + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + return cc; + + case OP_ANYBYTE: + detect_partial_match(common, backtracks); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + return cc; + +#ifdef SUPPORT_UTF +#ifdef SUPPORT_UCP + case OP_NOTPROP: + case OP_PROP: + propdata[0] = 0; + propdata[1] = type == OP_NOTPROP ? XCL_NOTPROP : XCL_PROP; + propdata[2] = cc[0]; + propdata[3] = cc[1]; + propdata[4] = XCL_END; + compile_xclass_matchingpath(common, propdata, backtracks); + return cc + 2; +#endif +#endif + + case OP_ANYNL: + detect_partial_match(common, backtracks); + read_char(common); + jump[0] = CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); + /* We don't need to handle soft partial matching case. */ + end_list = NULL; + if (common->mode != JIT_PARTIAL_HARD_COMPILE) + add_jump(compiler, &end_list, CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + else + check_str_end(common, &end_list); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + jump[1] = CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + jump[2] = JUMP(SLJIT_JUMP); + JUMPHERE(jump[0]); + check_newlinechar(common, common->bsr_nltype, backtracks, FALSE); + set_jumps(end_list, LABEL()); + JUMPHERE(jump[1]); + JUMPHERE(jump[2]); + return cc; + + case OP_NOT_HSPACE: + case OP_HSPACE: + detect_partial_match(common, backtracks); + read_char(common); + add_jump(compiler, &common->hspace, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, backtracks, JUMP(type == OP_NOT_HSPACE ? SLJIT_C_NOT_ZERO : SLJIT_C_ZERO)); + return cc; + + case OP_NOT_VSPACE: + case OP_VSPACE: + detect_partial_match(common, backtracks); + read_char(common); + add_jump(compiler, &common->vspace, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, backtracks, JUMP(type == OP_NOT_VSPACE ? SLJIT_C_NOT_ZERO : SLJIT_C_ZERO)); + return cc; + +#ifdef SUPPORT_UCP + case OP_EXTUNI: + detect_partial_match(common, backtracks); + read_char(common); + add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, gbprop)); + /* Optimize register allocation: use a real register. */ + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, STACK_TOP, 0); + OP1(SLJIT_MOV_UB, STACK_TOP, 0, SLJIT_MEM2(TMP1, TMP2), 3); + + label = LABEL(); + jump[0] = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); + read_char(common); + add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, gbprop)); + OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM2(TMP1, TMP2), 3); + + OP2(SLJIT_SHL, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2); + OP1(SLJIT_MOV_UI, TMP1, 0, SLJIT_MEM1(STACK_TOP), (sljit_sw)PRIV(ucp_gbtable)); + OP1(SLJIT_MOV, STACK_TOP, 0, TMP2, 0); + OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + JUMPTO(SLJIT_C_NOT_ZERO, label); + + OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); + JUMPHERE(jump[0]); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); + + if (common->mode == JIT_PARTIAL_HARD_COMPILE) + { + jump[0] = CMP(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0); + /* Since we successfully read a char above, partial matching must occure. */ + check_partial(common, TRUE); + JUMPHERE(jump[0]); + } + return cc; +#endif + + case OP_EODN: + /* Requires rather complex checks. */ + jump[0] = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); + if (common->mode == JIT_COMPILE) + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_END, 0)); + else + { + jump[1] = CMP(SLJIT_C_EQUAL, TMP2, 0, STR_END, 0); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_LESS); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_NOT_EQUAL); + add_jump(compiler, backtracks, JUMP(SLJIT_C_NOT_EQUAL)); + check_partial(common, TRUE); + add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); + JUMPHERE(jump[1]); + } + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff)); + } + else if (common->nltype == NLTYPE_FIXED) + { + OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_END, 0)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline)); + } + else + { + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); + jump[1] = CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); + OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); + jump[2] = JUMP(SLJIT_C_GREATER); + add_jump(compiler, backtracks, JUMP(SLJIT_C_LESS)); + /* Equal. */ + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + jump[3] = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL); + add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); + + JUMPHERE(jump[1]); + if (common->nltype == NLTYPE_ANYCRLF) + { + OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + add_jump(compiler, backtracks, CMP(SLJIT_C_LESS, TMP2, 0, STR_END, 0)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL)); + } + else + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, STR_PTR, 0); + read_char(common); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, STR_END, 0)); + add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, backtracks, JUMP(SLJIT_C_ZERO)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1); + } + JUMPHERE(jump[2]); + JUMPHERE(jump[3]); + } + JUMPHERE(jump[0]); + check_partial(common, FALSE); + return cc; + + case OP_EOD: + add_jump(compiler, backtracks, CMP(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0)); + check_partial(common, FALSE); + return cc; + + case OP_CIRC: + OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin)); + add_jump(compiler, backtracks, CMP(SLJIT_C_GREATER, STR_PTR, 0, TMP1, 0)); + OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, notbol)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); + return cc; + + case OP_CIRCM: + OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin)); + jump[1] = CMP(SLJIT_C_GREATER, STR_PTR, 0, TMP1, 0); + OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, notbol)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); + jump[0] = JUMP(SLJIT_JUMP); + JUMPHERE(jump[1]); + + add_jump(compiler, backtracks, CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + OP2(SLJIT_SUB, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); + add_jump(compiler, backtracks, CMP(SLJIT_C_LESS, TMP2, 0, TMP1, 0)); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff)); + } + else + { + skip_char_back(common); + read_char(common); + check_newlinechar(common, common->nltype, backtracks, FALSE); + } + JUMPHERE(jump[0]); + return cc; + + case OP_DOLL: + OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, noteol)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); + + if (!common->endonly) + compile_char1_matchingpath(common, OP_EODN, cc, backtracks); + else + { + add_jump(compiler, backtracks, CMP(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0)); + check_partial(common, FALSE); + } + return cc; + + case OP_DOLLM: + jump[1] = CMP(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0); + OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, noteol)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); + check_partial(common, FALSE); + jump[0] = JUMP(SLJIT_JUMP); + JUMPHERE(jump[1]); + + if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); + if (common->mode == JIT_COMPILE) + add_jump(compiler, backtracks, CMP(SLJIT_C_GREATER, TMP2, 0, STR_END, 0)); + else + { + jump[1] = CMP(SLJIT_C_LESS_EQUAL, TMP2, 0, STR_END, 0); + /* STR_PTR = STR_END - IN_UCHARS(1) */ + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff)); + check_partial(common, TRUE); + add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); + JUMPHERE(jump[1]); + } + + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff)); + } + else + { + peek_char(common); + check_newlinechar(common, common->nltype, backtracks, FALSE); + } + JUMPHERE(jump[0]); + return cc; + + case OP_CHAR: + case OP_CHARI: + length = 1; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(*cc)) length += GET_EXTRALEN(*cc); +#endif + if (common->mode == JIT_COMPILE && (type == OP_CHAR || !char_has_othercase(common, cc) || char_get_othercase_bit(common, cc) != 0)) + { + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length)); + add_jump(compiler, backtracks, CMP(SLJIT_C_GREATER, STR_PTR, 0, STR_END, 0)); + + context.length = IN_UCHARS(length); + context.sourcereg = -1; +#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED + context.ucharptr = 0; +#endif + return byte_sequence_compare(common, type == OP_CHARI, cc, &context, backtracks); + } + detect_partial_match(common, backtracks); + read_char(common); +#ifdef SUPPORT_UTF + if (common->utf) + { + GETCHAR(c, cc); + } + else +#endif + c = *cc; + if (type == OP_CHAR || !char_has_othercase(common, cc)) + { + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, c)); + return cc + length; + } + oc = char_othercase(common, c); + bit = c ^ oc; + if (is_powerof2(bit)) + { + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, bit); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, c | bit)); + return cc + length; + } + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, c); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, oc); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + add_jump(compiler, backtracks, JUMP(SLJIT_C_ZERO)); + return cc + length; + + case OP_NOT: + case OP_NOTI: + detect_partial_match(common, backtracks); + length = 1; +#ifdef SUPPORT_UTF + if (common->utf) + { +#ifdef COMPILE_PCRE8 + c = *cc; + if (c < 128) + { + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + if (type == OP_NOT || !char_has_othercase(common, cc)) + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, c)); + else + { + /* Since UTF8 code page is fixed, we know that c is in [a-z] or [A-Z] range. */ + OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x20); + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, c | 0x20)); + } + /* Skip the variable-length character. */ + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + jump[0] = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + JUMPHERE(jump[0]); + return cc + 1; + } + else +#endif /* COMPILE_PCRE8 */ + { + GETCHARLEN(c, cc, length); + read_char(common); + } + } + else +#endif /* SUPPORT_UTF */ + { + read_char(common); + c = *cc; + } + + if (type == OP_NOT || !char_has_othercase(common, cc)) + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, c)); + else + { + oc = char_othercase(common, c); + bit = c ^ oc; + if (is_powerof2(bit)) + { + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, bit); + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, c | bit)); + } + else + { + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, c)); + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, oc)); + } + } + return cc + length; + + case OP_CLASS: + case OP_NCLASS: + detect_partial_match(common, backtracks); + read_char(common); + if (check_class_ranges(common, (const pcre_uint8 *)cc, type == OP_NCLASS, backtracks)) + return cc + 32 / sizeof(pcre_uchar); + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + jump[0] = NULL; +#ifdef COMPILE_PCRE8 + /* This check only affects 8 bit mode. In other modes, we + always need to compare the value with 255. */ + if (common->utf) +#endif /* COMPILE_PCRE8 */ + { + jump[0] = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); + if (type == OP_CLASS) + { + add_jump(compiler, backtracks, jump[0]); + jump[0] = NULL; + } + } +#endif /* SUPPORT_UTF || !COMPILE_PCRE8 */ + OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); + OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc); + OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + add_jump(compiler, backtracks, JUMP(SLJIT_C_ZERO)); +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + if (jump[0] != NULL) + JUMPHERE(jump[0]); +#endif /* SUPPORT_UTF || !COMPILE_PCRE8 */ + return cc + 32 / sizeof(pcre_uchar); + +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + case OP_XCLASS: + compile_xclass_matchingpath(common, cc + LINK_SIZE, backtracks); + return cc + GET(cc, 0) - 1; +#endif + + case OP_REVERSE: + length = GET(cc, 0); + if (length == 0) + return cc + LINK_SIZE; + OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +#ifdef SUPPORT_UTF + if (common->utf) + { + OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, length); + label = LABEL(); + add_jump(compiler, backtracks, CMP(SLJIT_C_LESS_EQUAL, STR_PTR, 0, TMP3, 0)); + skip_char_back(common); + OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_C_NOT_ZERO, label); + } + else +#endif + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length)); + add_jump(compiler, backtracks, CMP(SLJIT_C_LESS, STR_PTR, 0, TMP1, 0)); + } + check_start_used_ptr(common); + return cc + LINK_SIZE; + } +SLJIT_ASSERT_STOP(); +return cc; +} + +static SLJIT_INLINE pcre_uchar *compile_charn_matchingpath(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, jump_list **backtracks) +{ +/* This function consumes at least one input character. */ +/* To decrease the number of length checks, we try to concatenate the fixed length character sequences. */ +DEFINE_COMPILER; +pcre_uchar *ccbegin = cc; +compare_context context; +int size; + +context.length = 0; +do + { + if (cc >= ccend) + break; + + if (*cc == OP_CHAR) + { + size = 1; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[1])) + size += GET_EXTRALEN(cc[1]); +#endif + } + else if (*cc == OP_CHARI) + { + size = 1; +#ifdef SUPPORT_UTF + if (common->utf) + { + if (char_has_othercase(common, cc + 1) && char_get_othercase_bit(common, cc + 1) == 0) + size = 0; + else if (HAS_EXTRALEN(cc[1])) + size += GET_EXTRALEN(cc[1]); + } + else +#endif + if (char_has_othercase(common, cc + 1) && char_get_othercase_bit(common, cc + 1) == 0) + size = 0; + } + else + size = 0; + + cc += 1 + size; + context.length += IN_UCHARS(size); + } +while (size > 0 && context.length <= 128); + +cc = ccbegin; +if (context.length > 0) + { + /* We have a fixed-length byte sequence. */ + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, context.length); + add_jump(compiler, backtracks, CMP(SLJIT_C_GREATER, STR_PTR, 0, STR_END, 0)); + + context.sourcereg = -1; +#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED + context.ucharptr = 0; +#endif + do cc = byte_sequence_compare(common, *cc == OP_CHARI, cc + 1, &context, backtracks); while (context.length > 0); + return cc; + } + +/* A non-fixed length character will be checked if length == 0. */ +return compile_char1_matchingpath(common, *cc, cc + 1, backtracks); +} + +static struct sljit_jump *compile_ref_checks(compiler_common *common, pcre_uchar *cc, jump_list **backtracks) +{ +DEFINE_COMPILER; +int offset = GET2(cc, 1) << 1; + +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset)); +if (!common->jscript_compat) + { + if (backtracks == NULL) + { + /* OVECTOR(1) contains the "string begin - 1" constant. */ + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1)); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_C_EQUAL); + return JUMP(SLJIT_C_NOT_ZERO); + } + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1))); + } +return CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); +} + +/* Forward definitions. */ +static void compile_matchingpath(compiler_common *, pcre_uchar *, pcre_uchar *, backtrack_common *); +static void compile_backtrackingpath(compiler_common *, struct backtrack_common *); + +#define PUSH_BACKTRACK(size, ccstart, error) \ + do \ + { \ + backtrack = sljit_alloc_memory(compiler, (size)); \ + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \ + return error; \ + memset(backtrack, 0, size); \ + backtrack->prev = parent->top; \ + backtrack->cc = (ccstart); \ + parent->top = backtrack; \ + } \ + while (0) + +#define PUSH_BACKTRACK_NOVALUE(size, ccstart) \ + do \ + { \ + backtrack = sljit_alloc_memory(compiler, (size)); \ + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \ + return; \ + memset(backtrack, 0, size); \ + backtrack->prev = parent->top; \ + backtrack->cc = (ccstart); \ + parent->top = backtrack; \ + } \ + while (0) + +#define BACKTRACK_AS(type) ((type *)backtrack) + +static pcre_uchar *compile_ref_matchingpath(compiler_common *common, pcre_uchar *cc, jump_list **backtracks, BOOL withchecks, BOOL emptyfail) +{ +DEFINE_COMPILER; +int offset = GET2(cc, 1) << 1; +struct sljit_jump *jump = NULL; +struct sljit_jump *partial; +struct sljit_jump *nopartial; + +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset)); +/* OVECTOR(1) contains the "string begin - 1" constant. */ +if (withchecks && !common->jscript_compat) + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1))); + +#if defined SUPPORT_UTF && defined SUPPORT_UCP +if (common->utf && *cc == OP_REFI) + { + SLJIT_ASSERT(TMP1 == SLJIT_SCRATCH_REG1 && STACK_TOP == SLJIT_SCRATCH_REG2 && TMP2 == SLJIT_SCRATCH_REG3); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); + if (withchecks) + jump = CMP(SLJIT_C_EQUAL, TMP1, 0, TMP2, 0); + + /* Needed to save important temporary registers. */ + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, STACK_TOP, 0); + OP1(SLJIT_MOV, SLJIT_SCRATCH_REG2, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SCRATCH_REG2), SLJIT_OFFSETOF(jit_arguments, uchar_ptr), STR_PTR, 0); + sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_utf_caselesscmp)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); + if (common->mode == JIT_COMPILE) + add_jump(compiler, backtracks, CMP(SLJIT_C_LESS_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1)); + else + { + add_jump(compiler, backtracks, CMP(SLJIT_C_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0)); + nopartial = CMP(SLJIT_C_NOT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1); + check_partial(common, FALSE); + add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); + JUMPHERE(nopartial); + } + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0); + } +else +#endif /* SUPPORT_UTF && SUPPORT_UCP */ + { + OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), TMP1, 0); + if (withchecks) + jump = JUMP(SLJIT_C_ZERO); + + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); + partial = CMP(SLJIT_C_GREATER, STR_PTR, 0, STR_END, 0); + if (common->mode == JIT_COMPILE) + add_jump(compiler, backtracks, partial); + + add_jump(compiler, *cc == OP_REF ? &common->casefulcmp : &common->caselesscmp, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); + + if (common->mode != JIT_COMPILE) + { + nopartial = JUMP(SLJIT_JUMP); + JUMPHERE(partial); + /* TMP2 -= STR_END - STR_PTR */ + OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, STR_PTR, 0); + OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, STR_END, 0); + partial = CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, 0); + OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0); + add_jump(compiler, *cc == OP_REF ? &common->casefulcmp : &common->caselesscmp, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, backtracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); + JUMPHERE(partial); + check_partial(common, FALSE); + add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); + JUMPHERE(nopartial); + } + } + +if (jump != NULL) + { + if (emptyfail) + add_jump(compiler, backtracks, jump); + else + JUMPHERE(jump); + } +return cc + 1 + IMM2_SIZE; +} + +static SLJIT_INLINE pcre_uchar *compile_ref_iterator_matchingpath(compiler_common *common, pcre_uchar *cc, backtrack_common *parent) +{ +DEFINE_COMPILER; +backtrack_common *backtrack; +pcre_uchar type; +struct sljit_label *label; +struct sljit_jump *zerolength; +struct sljit_jump *jump = NULL; +pcre_uchar *ccbegin = cc; +int min = 0, max = 0; +BOOL minimize; + +PUSH_BACKTRACK(sizeof(iterator_backtrack), cc, NULL); + +type = cc[1 + IMM2_SIZE]; +minimize = (type & 0x1) != 0; +switch(type) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + min = 0; + max = 0; + cc += 1 + IMM2_SIZE + 1; + break; + case OP_CRPLUS: + case OP_CRMINPLUS: + min = 1; + max = 0; + cc += 1 + IMM2_SIZE + 1; + break; + case OP_CRQUERY: + case OP_CRMINQUERY: + min = 0; + max = 1; + cc += 1 + IMM2_SIZE + 1; + break; + case OP_CRRANGE: + case OP_CRMINRANGE: + min = GET2(cc, 1 + IMM2_SIZE + 1); + max = GET2(cc, 1 + IMM2_SIZE + 1 + IMM2_SIZE); + cc += 1 + IMM2_SIZE + 1 + 2 * IMM2_SIZE; + break; + default: + SLJIT_ASSERT_STOP(); + break; + } + +if (!minimize) + { + if (min == 0) + { + allocate_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0); + /* Temporary release of STR_PTR. */ + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + zerolength = compile_ref_checks(common, ccbegin, NULL); + /* Restore if not zero length. */ + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + } + else + { + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + zerolength = compile_ref_checks(common, ccbegin, &backtrack->topbacktracks); + } + + if (min > 1 || max > 1) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, SLJIT_IMM, 0); + + label = LABEL(); + compile_ref_matchingpath(common, ccbegin, &backtrack->topbacktracks, FALSE, FALSE); + + if (min > 1 || max > 1) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, TMP1, 0); + if (min > 1) + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, min, label); + if (max > 1) + { + jump = CMP(SLJIT_C_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, max); + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + JUMPTO(SLJIT_JUMP, label); + JUMPHERE(jump); + } + } + + if (max == 0) + { + /* Includes min > 1 case as well. */ + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + JUMPTO(SLJIT_JUMP, label); + } + + JUMPHERE(zerolength); + BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL(); + + count_match(common); + return cc; + } + +allocate_stack(common, 2); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); +if (type != OP_CRMINSTAR) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0); + +if (min == 0) + { + zerolength = compile_ref_checks(common, ccbegin, NULL); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + jump = JUMP(SLJIT_JUMP); + } +else + zerolength = compile_ref_checks(common, ccbegin, &backtrack->topbacktracks); + +BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL(); +if (max > 0) + add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_C_GREATER_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, max)); + +compile_ref_matchingpath(common, ccbegin, &backtrack->topbacktracks, TRUE, TRUE); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + +if (min > 1) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, min, BACKTRACK_AS(iterator_backtrack)->matchingpath); + } +else if (max > 0) + OP2(SLJIT_ADD, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 1); + +if (jump != NULL) + JUMPHERE(jump); +JUMPHERE(zerolength); + +count_match(common); +return cc; +} + +static SLJIT_INLINE pcre_uchar *compile_recurse_matchingpath(compiler_common *common, pcre_uchar *cc, backtrack_common *parent) +{ +DEFINE_COMPILER; +backtrack_common *backtrack; +recurse_entry *entry = common->entries; +recurse_entry *prev = NULL; +sljit_sw start = GET(cc, 1); +pcre_uchar *start_cc; +BOOL needs_control_head; + +PUSH_BACKTRACK(sizeof(recurse_backtrack), cc, NULL); + +/* Inlining simple patterns. */ +if (get_framesize(common, common->start + start, NULL, TRUE, &needs_control_head) == no_stack) + { + start_cc = common->start + start; + compile_matchingpath(common, next_opcode(common, start_cc), bracketend(start_cc) - (1 + LINK_SIZE), backtrack); + BACKTRACK_AS(recurse_backtrack)->inlined_pattern = TRUE; + return cc + 1 + LINK_SIZE; + } + +while (entry != NULL) + { + if (entry->start == start) + break; + prev = entry; + entry = entry->next; + } + +if (entry == NULL) + { + entry = sljit_alloc_memory(compiler, sizeof(recurse_entry)); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return NULL; + entry->next = NULL; + entry->entry = NULL; + entry->calls = NULL; + entry->start = start; + + if (prev != NULL) + prev->next = entry; + else + common->entries = entry; + } + +if (common->has_set_som && common->mark_ptr != 0) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); + allocate_stack(common, 2); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); + } +else if (common->has_set_som || common->mark_ptr != 0) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->has_set_som ? (int)(OVECTOR(0)) : common->mark_ptr); + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + } + +if (entry->entry == NULL) + add_jump(compiler, &entry->calls, JUMP(SLJIT_FAST_CALL)); +else + JUMPTO(SLJIT_FAST_CALL, entry->entry); +/* Leave if the match is failed. */ +add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 0)); +return cc + 1 + LINK_SIZE; +} + +static int SLJIT_CALL do_callout(struct jit_arguments* arguments, PUBL(callout_block) *callout_block, pcre_uchar **jit_ovector) +{ +const pcre_uchar *begin = arguments->begin; +int *offset_vector = arguments->offsets; +int offset_count = arguments->offset_count; +int i; + +if (PUBL(callout) == NULL) + return 0; + +callout_block->version = 2; +callout_block->callout_data = arguments->callout_data; + +/* Offsets in subject. */ +callout_block->subject_length = arguments->end - arguments->begin; +callout_block->start_match = (pcre_uchar*)callout_block->subject - arguments->begin; +callout_block->current_position = (pcre_uchar*)callout_block->offset_vector - arguments->begin; +#if defined COMPILE_PCRE8 +callout_block->subject = (PCRE_SPTR)begin; +#elif defined COMPILE_PCRE16 +callout_block->subject = (PCRE_SPTR16)begin; +#elif defined COMPILE_PCRE32 +callout_block->subject = (PCRE_SPTR32)begin; +#endif + +/* Convert and copy the JIT offset vector to the offset_vector array. */ +callout_block->capture_top = 0; +callout_block->offset_vector = offset_vector; +for (i = 2; i < offset_count; i += 2) + { + offset_vector[i] = jit_ovector[i] - begin; + offset_vector[i + 1] = jit_ovector[i + 1] - begin; + if (jit_ovector[i] >= begin) + callout_block->capture_top = i; + } + +callout_block->capture_top = (callout_block->capture_top >> 1) + 1; +if (offset_count > 0) + offset_vector[0] = -1; +if (offset_count > 1) + offset_vector[1] = -1; +return (*PUBL(callout))(callout_block); +} + +/* Aligning to 8 byte. */ +#define CALLOUT_ARG_SIZE \ + (((int)sizeof(PUBL(callout_block)) + 7) & ~7) + +#define CALLOUT_ARG_OFFSET(arg) \ + (-CALLOUT_ARG_SIZE + SLJIT_OFFSETOF(PUBL(callout_block), arg)) + +static SLJIT_INLINE pcre_uchar *compile_callout_matchingpath(compiler_common *common, pcre_uchar *cc, backtrack_common *parent) +{ +DEFINE_COMPILER; +backtrack_common *backtrack; + +PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL); + +allocate_stack(common, CALLOUT_ARG_SIZE / sizeof(sljit_sw)); + +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->capture_last_ptr); +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +SLJIT_ASSERT(common->capture_last_ptr != 0); +OP1(SLJIT_MOV_SI, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_number), SLJIT_IMM, cc[1]); +OP1(SLJIT_MOV_SI, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_last), TMP2, 0); + +/* These pointer sized fields temporarly stores internal variables. */ +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(offset_vector), STR_PTR, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(subject), TMP2, 0); + +if (common->mark_ptr != 0) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr)); +OP1(SLJIT_MOV_SI, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(pattern_position), SLJIT_IMM, GET(cc, 2)); +OP1(SLJIT_MOV_SI, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(next_item_length), SLJIT_IMM, GET(cc, 2 + LINK_SIZE)); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(mark), (common->mark_ptr != 0) ? TMP2 : SLJIT_IMM, 0); + +/* Needed to save important temporary registers. */ +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, STACK_TOP, 0); +OP2(SLJIT_SUB, SLJIT_SCRATCH_REG2, 0, STACK_TOP, 0, SLJIT_IMM, CALLOUT_ARG_SIZE); +GET_LOCAL_BASE(SLJIT_SCRATCH_REG3, 0, OVECTOR_START); +sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_callout)); +OP1(SLJIT_MOV_SI, SLJIT_RETURN_REG, 0, SLJIT_RETURN_REG, 0); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); +free_stack(common, CALLOUT_ARG_SIZE / sizeof(sljit_sw)); + +/* Check return value. */ +OP2(SLJIT_SUB | SLJIT_SET_S, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); +add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_C_SIG_GREATER)); +if (common->forced_quit_label == NULL) + add_jump(compiler, &common->forced_quit, JUMP(SLJIT_C_SIG_LESS)); +else + JUMPTO(SLJIT_C_SIG_LESS, common->forced_quit_label); +return cc + 2 + 2 * LINK_SIZE; +} + +#undef CALLOUT_ARG_SIZE +#undef CALLOUT_ARG_OFFSET + +static pcre_uchar *compile_assert_matchingpath(compiler_common *common, pcre_uchar *cc, assert_backtrack *backtrack, BOOL conditional) +{ +DEFINE_COMPILER; +int framesize; +int extrasize; +BOOL needs_control_head; +int private_data_ptr; +backtrack_common altbacktrack; +pcre_uchar *ccbegin; +pcre_uchar opcode; +pcre_uchar bra = OP_BRA; +jump_list *tmp = NULL; +jump_list **target = (conditional) ? &backtrack->condfailed : &backtrack->common.topbacktracks; +jump_list **found; +/* Saving previous accept variables. */ +BOOL save_local_exit = common->local_exit; +BOOL save_positive_assert = common->positive_assert; +then_trap_backtrack *save_then_trap = common->then_trap; +struct sljit_label *save_quit_label = common->quit_label; +struct sljit_label *save_accept_label = common->accept_label; +jump_list *save_quit = common->quit; +jump_list *save_positive_assert_quit = common->positive_assert_quit; +jump_list *save_accept = common->accept; +struct sljit_jump *jump; +struct sljit_jump *brajump = NULL; + +/* Assert captures then. */ +common->then_trap = NULL; + +if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO) + { + SLJIT_ASSERT(!conditional); + bra = *cc; + cc++; + } +private_data_ptr = PRIVATE_DATA(cc); +SLJIT_ASSERT(private_data_ptr != 0); +framesize = get_framesize(common, cc, NULL, FALSE, &needs_control_head); +backtrack->framesize = framesize; +backtrack->private_data_ptr = private_data_ptr; +opcode = *cc; +SLJIT_ASSERT(opcode >= OP_ASSERT && opcode <= OP_ASSERTBACK_NOT); +found = (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) ? &tmp : target; +ccbegin = cc; +cc += GET(cc, 1); + +if (bra == OP_BRAMINZERO) + { + /* This is a braminzero backtrack path. */ + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + brajump = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + } + +if (framesize < 0) + { + extrasize = needs_control_head ? 2 : 1; + if (framesize == no_frame) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, STACK_TOP, 0); + allocate_stack(common, extrasize); + if (needs_control_head) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + if (needs_control_head) + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, SLJIT_IMM, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); + } + } +else + { + extrasize = needs_control_head ? 3 : 2; + allocate_stack(common, framesize + extrasize); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, TMP2, 0); + if (needs_control_head) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + if (needs_control_head) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP1, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, SLJIT_IMM, 0); + } + else + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); + init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize, FALSE); + } + +memset(&altbacktrack, 0, sizeof(backtrack_common)); +if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) + { + /* Negative assert is stronger than positive assert. */ + common->local_exit = TRUE; + common->quit_label = NULL; + common->quit = NULL; + common->positive_assert = FALSE; + } +else + common->positive_assert = TRUE; +common->positive_assert_quit = NULL; + +while (1) + { + common->accept_label = NULL; + common->accept = NULL; + altbacktrack.top = NULL; + altbacktrack.topbacktracks = NULL; + + if (*ccbegin == OP_ALT) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + + altbacktrack.cc = ccbegin; + compile_matchingpath(common, ccbegin + 1 + LINK_SIZE, cc, &altbacktrack); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + { + if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) + { + common->local_exit = save_local_exit; + common->quit_label = save_quit_label; + common->quit = save_quit; + } + common->positive_assert = save_positive_assert; + common->then_trap = save_then_trap; + common->accept_label = save_accept_label; + common->positive_assert_quit = save_positive_assert_quit; + common->accept = save_accept; + return NULL; + } + common->accept_label = LABEL(); + if (common->accept != NULL) + set_jumps(common->accept, common->accept_label); + + /* Reset stack. */ + if (framesize < 0) + { + if (framesize == no_frame) + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + else + free_stack(common, extrasize); + if (needs_control_head) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), 0); + } + else + { + if ((opcode != OP_ASSERT_NOT && opcode != OP_ASSERTBACK_NOT) || conditional) + { + /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); + if (needs_control_head) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), 0); + } + else + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + if (needs_control_head) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), (framesize + 1) * sizeof(sljit_sw)); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + } + } + + if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) + { + /* We know that STR_PTR was stored on the top of the stack. */ + if (conditional) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? sizeof(sljit_sw) : 0); + else if (bra == OP_BRAZERO) + { + if (framesize < 0) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw)); + else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (framesize + extrasize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, TMP1, 0); + } + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + else if (framesize >= 0) + { + /* For OP_BRA and OP_BRAMINZERO. */ + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw)); + } + } + add_jump(compiler, found, JUMP(SLJIT_JUMP)); + + compile_backtrackingpath(common, altbacktrack.top); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + { + if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) + { + common->local_exit = save_local_exit; + common->quit_label = save_quit_label; + common->quit = save_quit; + } + common->positive_assert = save_positive_assert; + common->then_trap = save_then_trap; + common->accept_label = save_accept_label; + common->positive_assert_quit = save_positive_assert_quit; + common->accept = save_accept; + return NULL; + } + set_jumps(altbacktrack.topbacktracks, LABEL()); + + if (*cc != OP_ALT) + break; + + ccbegin = cc; + cc += GET(cc, 1); + } + +if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) + { + SLJIT_ASSERT(common->positive_assert_quit == NULL); + /* Makes the check less complicated below. */ + common->positive_assert_quit = common->quit; + } + +/* None of them matched. */ +if (common->positive_assert_quit != NULL) + { + jump = JUMP(SLJIT_JUMP); + set_jumps(common->positive_assert_quit, LABEL()); + SLJIT_ASSERT(framesize != no_stack); + if (framesize < 0) + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw)); + else + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw)); + } + JUMPHERE(jump); + } + +if (needs_control_head) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(1)); + +if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) + { + /* Assert is failed. */ + if (conditional || bra == OP_BRAZERO) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + + if (framesize < 0) + { + /* The topmost item should be 0. */ + if (bra == OP_BRAZERO) + { + if (extrasize == 2) + free_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + else + free_stack(common, extrasize); + } + else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(extrasize - 1)); + /* The topmost item should be 0. */ + if (bra == OP_BRAZERO) + { + free_stack(common, framesize + extrasize - 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + else + free_stack(common, framesize + extrasize); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, TMP1, 0); + } + jump = JUMP(SLJIT_JUMP); + if (bra != OP_BRAZERO) + add_jump(compiler, target, jump); + + /* Assert is successful. */ + set_jumps(tmp, LABEL()); + if (framesize < 0) + { + /* We know that STR_PTR was stored on the top of the stack. */ + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw)); + /* Keep the STR_PTR on the top of the stack. */ + if (bra == OP_BRAZERO) + { + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + if (extrasize == 2) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + } + else if (bra == OP_BRAMINZERO) + { + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + } + else + { + if (bra == OP_BRA) + { + /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 2) * sizeof(sljit_sw)); + } + else + { + /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw)); + if (extrasize == 2) + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + if (bra == OP_BRAMINZERO) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + else + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), bra == OP_BRAZERO ? STR_PTR : SLJIT_IMM, 0); + } + } + } + + if (bra == OP_BRAZERO) + { + backtrack->matchingpath = LABEL(); + SET_LABEL(jump, backtrack->matchingpath); + } + else if (bra == OP_BRAMINZERO) + { + JUMPTO(SLJIT_JUMP, backtrack->matchingpath); + JUMPHERE(brajump); + if (framesize >= 0) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw)); + } + set_jumps(backtrack->common.topbacktracks, LABEL()); + } + } +else + { + /* AssertNot is successful. */ + if (framesize < 0) + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + if (bra != OP_BRA) + { + if (extrasize == 2) + free_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + else + free_stack(common, extrasize); + } + else + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(extrasize - 1)); + /* The topmost item should be 0. */ + if (bra != OP_BRA) + { + free_stack(common, framesize + extrasize - 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + else + free_stack(common, framesize + extrasize); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, TMP1, 0); + } + + if (bra == OP_BRAZERO) + backtrack->matchingpath = LABEL(); + else if (bra == OP_BRAMINZERO) + { + JUMPTO(SLJIT_JUMP, backtrack->matchingpath); + JUMPHERE(brajump); + } + + if (bra != OP_BRA) + { + SLJIT_ASSERT(found == &backtrack->common.topbacktracks); + set_jumps(backtrack->common.topbacktracks, LABEL()); + backtrack->common.topbacktracks = NULL; + } + } + +if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) + { + common->local_exit = save_local_exit; + common->quit_label = save_quit_label; + common->quit = save_quit; + } +common->positive_assert = save_positive_assert; +common->then_trap = save_then_trap; +common->accept_label = save_accept_label; +common->positive_assert_quit = save_positive_assert_quit; +common->accept = save_accept; +return cc + 1 + LINK_SIZE; +} + +static sljit_sw SLJIT_CALL do_searchovector(sljit_uw refno, sljit_sw* locals, pcre_uchar *name_table) +{ +int condition = FALSE; +pcre_uchar *slotA = name_table; +pcre_uchar *slotB; +sljit_sw name_count = locals[LOCALS0 / sizeof(sljit_sw)]; +sljit_sw name_entry_size = locals[LOCALS1 / sizeof(sljit_sw)]; +sljit_sw no_capture; +int i; + +locals += refno & 0xff; +refno >>= 8; +no_capture = locals[1]; + +for (i = 0; i < name_count; i++) + { + if (GET2(slotA, 0) == refno) break; + slotA += name_entry_size; + } + +if (i < name_count) + { + /* Found a name for the number - there can be only one; duplicate names + for different numbers are allowed, but not vice versa. First scan down + for duplicates. */ + + slotB = slotA; + while (slotB > name_table) + { + slotB -= name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = locals[GET2(slotB, 0) << 1] != no_capture; + if (condition) break; + } + else break; + } + + /* Scan up for duplicates */ + if (!condition) + { + slotB = slotA; + for (i++; i < name_count; i++) + { + slotB += name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = locals[GET2(slotB, 0) << 1] != no_capture; + if (condition) break; + } + else break; + } + } + } +return condition; +} + +static sljit_sw SLJIT_CALL do_searchgroups(sljit_uw recno, sljit_uw* locals, pcre_uchar *name_table) +{ +int condition = FALSE; +pcre_uchar *slotA = name_table; +pcre_uchar *slotB; +sljit_uw name_count = locals[LOCALS0 / sizeof(sljit_sw)]; +sljit_uw name_entry_size = locals[LOCALS1 / sizeof(sljit_sw)]; +sljit_uw group_num = locals[POSSESSIVE0 / sizeof(sljit_sw)]; +sljit_uw i; + +for (i = 0; i < name_count; i++) + { + if (GET2(slotA, 0) == recno) break; + slotA += name_entry_size; + } + +if (i < name_count) + { + /* Found a name for the number - there can be only one; duplicate + names for different numbers are allowed, but not vice versa. First + scan down for duplicates. */ + + slotB = slotA; + while (slotB > name_table) + { + slotB -= name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = GET2(slotB, 0) == group_num; + if (condition) break; + } + else break; + } + + /* Scan up for duplicates */ + if (!condition) + { + slotB = slotA; + for (i++; i < name_count; i++) + { + slotB += name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = GET2(slotB, 0) == group_num; + if (condition) break; + } + else break; + } + } + } +return condition; +} + +static SLJIT_INLINE void match_once_common(compiler_common *common, pcre_uchar ket, int framesize, int private_data_ptr, BOOL has_alternatives, BOOL needs_control_head) +{ +DEFINE_COMPILER; +int stacksize; + +if (framesize < 0) + { + if (framesize == no_frame) + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + else + { + stacksize = needs_control_head ? 1 : 0; + if (ket != OP_KET || has_alternatives) + stacksize++; + free_stack(common, stacksize); + } + + if (needs_control_head) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? sizeof(sljit_sw) : 0); + + /* TMP2 which is set here used by OP_KETRMAX below. */ + if (ket == OP_KETRMAX) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), 0); + else if (ket == OP_KETRMIN) + { + /* Move the STR_PTR to the private_data_ptr. */ + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, SLJIT_MEM1(STACK_TOP), 0); + } + } +else + { + stacksize = (ket != OP_KET || has_alternatives) ? 2 : 1; + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw)); + if (needs_control_head) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), 0); + + if (ket == OP_KETRMAX) + { + /* TMP2 which is set here used by OP_KETRMAX below. */ + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + } + } +if (needs_control_head) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, TMP1, 0); +} + +static SLJIT_INLINE int match_capture_common(compiler_common *common, int stacksize, int offset, int private_data_ptr) +{ +DEFINE_COMPILER; + +if (common->capture_last_ptr != 0) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->capture_last_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->capture_last_ptr, SLJIT_IMM, offset >> 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0); + stacksize++; + } +if (common->optimized_cbracket[offset >> 1] == 0) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); + stacksize += 2; + } +return stacksize; +} + +/* + Handling bracketed expressions is probably the most complex part. + + Stack layout naming characters: + S - Push the current STR_PTR + 0 - Push a 0 (NULL) + A - Push the current STR_PTR. Needed for restoring the STR_PTR + before the next alternative. Not pushed if there are no alternatives. + M - Any values pushed by the current alternative. Can be empty, or anything. + C - Push the previous OVECTOR(i), OVECTOR(i+1) and OVECTOR_PRIV(i) to the stack. + L - Push the previous local (pointed by localptr) to the stack + () - opional values stored on the stack + ()* - optonal, can be stored multiple times + + The following list shows the regular expression templates, their PCRE byte codes + and stack layout supported by pcre-sljit. + + (?:) OP_BRA | OP_KET A M + () OP_CBRA | OP_KET C M + (?:)+ OP_BRA | OP_KETRMAX 0 A M S ( A M S )* + OP_SBRA | OP_KETRMAX 0 L M S ( L M S )* + (?:)+? OP_BRA | OP_KETRMIN 0 A M S ( A M S )* + OP_SBRA | OP_KETRMIN 0 L M S ( L M S )* + ()+ OP_CBRA | OP_KETRMAX 0 C M S ( C M S )* + OP_SCBRA | OP_KETRMAX 0 C M S ( C M S )* + ()+? OP_CBRA | OP_KETRMIN 0 C M S ( C M S )* + OP_SCBRA | OP_KETRMIN 0 C M S ( C M S )* + (?:)? OP_BRAZERO | OP_BRA | OP_KET S ( A M 0 ) + (?:)?? OP_BRAMINZERO | OP_BRA | OP_KET S ( A M 0 ) + ()? OP_BRAZERO | OP_CBRA | OP_KET S ( C M 0 ) + ()?? OP_BRAMINZERO | OP_CBRA | OP_KET S ( C M 0 ) + (?:)* OP_BRAZERO | OP_BRA | OP_KETRMAX S 0 ( A M S )* + OP_BRAZERO | OP_SBRA | OP_KETRMAX S 0 ( L M S )* + (?:)*? OP_BRAMINZERO | OP_BRA | OP_KETRMIN S 0 ( A M S )* + OP_BRAMINZERO | OP_SBRA | OP_KETRMIN S 0 ( L M S )* + ()* OP_BRAZERO | OP_CBRA | OP_KETRMAX S 0 ( C M S )* + OP_BRAZERO | OP_SCBRA | OP_KETRMAX S 0 ( C M S )* + ()*? OP_BRAMINZERO | OP_CBRA | OP_KETRMIN S 0 ( C M S )* + OP_BRAMINZERO | OP_SCBRA | OP_KETRMIN S 0 ( C M S )* + + + Stack layout naming characters: + A - Push the alternative index (starting from 0) on the stack. + Not pushed if there is no alternatives. + M - Any values pushed by the current alternative. Can be empty, or anything. + + The next list shows the possible content of a bracket: + (|) OP_*BRA | OP_ALT ... M A + (?()|) OP_*COND | OP_ALT M A + (?>|) OP_ONCE | OP_ALT ... [stack trace] M A + (?>|) OP_ONCE_NC | OP_ALT ... [stack trace] M A + Or nothing, if trace is unnecessary +*/ + +static pcre_uchar *compile_bracket_matchingpath(compiler_common *common, pcre_uchar *cc, backtrack_common *parent) +{ +DEFINE_COMPILER; +backtrack_common *backtrack; +pcre_uchar opcode; +int private_data_ptr = 0; +int offset = 0; +int stacksize; +int repeat_ptr = 0, repeat_length = 0; +int repeat_type = 0, repeat_count = 0; +pcre_uchar *ccbegin; +pcre_uchar *matchingpath; +pcre_uchar bra = OP_BRA; +pcre_uchar ket; +assert_backtrack *assert; +BOOL has_alternatives; +BOOL needs_control_head = FALSE; +struct sljit_jump *jump; +struct sljit_jump *skip; +struct sljit_label *rmax_label = NULL; +struct sljit_jump *braminzero = NULL; + +PUSH_BACKTRACK(sizeof(bracket_backtrack), cc, NULL); + +if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO) + { + bra = *cc; + cc++; + opcode = *cc; + } + +opcode = *cc; +ccbegin = cc; +matchingpath = bracketend(cc) - 1 - LINK_SIZE; +ket = *matchingpath; +if (ket == OP_KET && PRIVATE_DATA(matchingpath) != 0) + { + repeat_ptr = PRIVATE_DATA(matchingpath); + repeat_length = PRIVATE_DATA(matchingpath + 1); + repeat_type = PRIVATE_DATA(matchingpath + 2); + repeat_count = PRIVATE_DATA(matchingpath + 3); + SLJIT_ASSERT(repeat_length != 0 && repeat_type != 0 && repeat_count != 0); + if (repeat_type == OP_UPTO) + ket = OP_KETRMAX; + if (repeat_type == OP_MINUPTO) + ket = OP_KETRMIN; + } + +if ((opcode == OP_COND || opcode == OP_SCOND) && cc[1 + LINK_SIZE] == OP_DEF) + { + /* Drop this bracket_backtrack. */ + parent->top = backtrack->prev; + return matchingpath + 1 + LINK_SIZE + repeat_length; + } + +matchingpath = ccbegin + 1 + LINK_SIZE; +SLJIT_ASSERT(ket == OP_KET || ket == OP_KETRMAX || ket == OP_KETRMIN); +SLJIT_ASSERT(!((bra == OP_BRAZERO && ket == OP_KETRMIN) || (bra == OP_BRAMINZERO && ket == OP_KETRMAX))); +cc += GET(cc, 1); + +has_alternatives = *cc == OP_ALT; +if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) + { + has_alternatives = (*matchingpath == OP_RREF) ? FALSE : TRUE; + if (*matchingpath == OP_NRREF) + { + stacksize = GET2(matchingpath, 1); + if (common->currententry == NULL || stacksize == RREF_ANY) + has_alternatives = FALSE; + else if (common->currententry->start == 0) + has_alternatives = stacksize != 0; + else + has_alternatives = stacksize != (int)GET2(common->start, common->currententry->start + 1 + LINK_SIZE); + } + } + +if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN)) + opcode = OP_SCOND; +if (SLJIT_UNLIKELY(opcode == OP_ONCE_NC)) + opcode = OP_ONCE; + +if (opcode == OP_CBRA || opcode == OP_SCBRA) + { + /* Capturing brackets has a pre-allocated space. */ + offset = GET2(ccbegin, 1 + LINK_SIZE); + if (common->optimized_cbracket[offset] == 0) + { + private_data_ptr = OVECTOR_PRIV(offset); + offset <<= 1; + } + else + { + offset <<= 1; + private_data_ptr = OVECTOR(offset); + } + BACKTRACK_AS(bracket_backtrack)->private_data_ptr = private_data_ptr; + matchingpath += IMM2_SIZE; + } +else if (opcode == OP_ONCE || opcode == OP_SBRA || opcode == OP_SCOND) + { + /* Other brackets simply allocate the next entry. */ + private_data_ptr = PRIVATE_DATA(ccbegin); + SLJIT_ASSERT(private_data_ptr != 0); + BACKTRACK_AS(bracket_backtrack)->private_data_ptr = private_data_ptr; + if (opcode == OP_ONCE) + BACKTRACK_AS(bracket_backtrack)->u.framesize = get_framesize(common, ccbegin, NULL, FALSE, &needs_control_head); + } + +/* Instructions before the first alternative. */ +stacksize = 0; +if (ket == OP_KETRMAX || (ket == OP_KETRMIN && bra != OP_BRAMINZERO)) + stacksize++; +if (bra == OP_BRAZERO) + stacksize++; + +if (stacksize > 0) + allocate_stack(common, stacksize); + +stacksize = 0; +if (ket == OP_KETRMAX || (ket == OP_KETRMIN && bra != OP_BRAMINZERO)) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); + stacksize++; + } + +if (bra == OP_BRAZERO) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); + +if (bra == OP_BRAMINZERO) + { + /* This is a backtrack path! (Since the try-path of OP_BRAMINZERO matches to the empty string) */ + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + if (ket != OP_KETRMIN) + { + free_stack(common, 1); + braminzero = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + } + else + { + if (opcode == OP_ONCE || opcode >= OP_SBRA) + { + jump = CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + /* Nothing stored during the first run. */ + skip = JUMP(SLJIT_JUMP); + JUMPHERE(jump); + /* Checking zero-length iteration. */ + if (opcode != OP_ONCE || BACKTRACK_AS(bracket_backtrack)->u.framesize < 0) + { + /* When we come from outside, private_data_ptr contains the previous STR_PTR. */ + braminzero = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + } + else + { + /* Except when the whole stack frame must be saved. */ + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + braminzero = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (BACKTRACK_AS(bracket_backtrack)->u.framesize + 1) * sizeof(sljit_sw)); + } + JUMPHERE(skip); + } + else + { + jump = CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + JUMPHERE(jump); + } + } + } + +if (repeat_type != 0) + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, SLJIT_IMM, repeat_count); + if (repeat_type == OP_EXACT) + rmax_label = LABEL(); + } + +if (ket == OP_KETRMIN) + BACKTRACK_AS(bracket_backtrack)->recursive_matchingpath = LABEL(); + +if (ket == OP_KETRMAX) + { + rmax_label = LABEL(); + if (has_alternatives && opcode != OP_ONCE && opcode < OP_SBRA && repeat_type == 0) + BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = rmax_label; + } + +/* Handling capturing brackets and alternatives. */ +if (opcode == OP_ONCE) + { + stacksize = 0; + if (needs_control_head) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr); + stacksize++; + } + + if (BACKTRACK_AS(bracket_backtrack)->u.framesize < 0) + { + /* Neither capturing brackets nor recursions are found in the block. */ + if (ket == OP_KETRMIN) + { + stacksize += 2; + if (!needs_control_head) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + } + else + { + if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, STACK_TOP, 0); + if (ket == OP_KETRMAX || has_alternatives) + stacksize++; + } + + if (stacksize > 0) + allocate_stack(common, stacksize); + + stacksize = 0; + if (needs_control_head) + { + stacksize++; + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + } + + if (ket == OP_KETRMIN) + { + if (needs_control_head) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); + if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame) + OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0); + } + else if (ket == OP_KETRMAX || has_alternatives) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); + } + else + { + if (ket != OP_KET || has_alternatives) + stacksize++; + + stacksize += BACKTRACK_AS(bracket_backtrack)->u.framesize + 1; + allocate_stack(common, stacksize); + + if (needs_control_head) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); + + stacksize = needs_control_head ? 1 : 0; + if (ket != OP_KET || has_alternatives) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, TMP2, 0); + stacksize++; + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0); + } + else + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0); + } + init_frame(common, ccbegin, NULL, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1, FALSE); + } + } +else if (opcode == OP_CBRA || opcode == OP_SCBRA) + { + /* Saving the previous values. */ + if (common->optimized_cbracket[offset >> 1] != 0) + { + SLJIT_ASSERT(private_data_ptr == OVECTOR(offset)); + allocate_stack(common, 2); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr + sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0); + } + else + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + } + } +else if (opcode == OP_SBRA || opcode == OP_SCOND) + { + /* Saving the previous value. */ + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + } +else if (has_alternatives) + { + /* Pushing the starting string pointer. */ + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + } + +/* Generating code for the first alternative. */ +if (opcode == OP_COND || opcode == OP_SCOND) + { + if (*matchingpath == OP_CREF) + { + SLJIT_ASSERT(has_alternatives); + add_jump(compiler, &(BACKTRACK_AS(bracket_backtrack)->u.condfailed), + CMP(SLJIT_C_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(GET2(matchingpath, 1) << 1), SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1))); + matchingpath += 1 + IMM2_SIZE; + } + else if (*matchingpath == OP_NCREF) + { + SLJIT_ASSERT(has_alternatives); + stacksize = GET2(matchingpath, 1); + jump = CMP(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(stacksize << 1), SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1)); + + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1, STACK_TOP, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, SLJIT_IMM, common->name_count); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, SLJIT_IMM, common->name_entry_size); + OP1(SLJIT_MOV, SLJIT_SCRATCH_REG1, 0, SLJIT_IMM, (stacksize << 8) | (common->ovector_start / sizeof(sljit_sw))); + GET_LOCAL_BASE(SLJIT_SCRATCH_REG2, 0, 0); + OP1(SLJIT_MOV, SLJIT_SCRATCH_REG3, 0, SLJIT_IMM, common->name_table); + sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_searchovector)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1); + add_jump(compiler, &(BACKTRACK_AS(bracket_backtrack)->u.condfailed), CMP(SLJIT_C_EQUAL, SLJIT_SCRATCH_REG1, 0, SLJIT_IMM, 0)); + + JUMPHERE(jump); + matchingpath += 1 + IMM2_SIZE; + } + else if (*matchingpath == OP_RREF || *matchingpath == OP_NRREF) + { + /* Never has other case. */ + BACKTRACK_AS(bracket_backtrack)->u.condfailed = NULL; + + stacksize = GET2(matchingpath, 1); + if (common->currententry == NULL) + stacksize = 0; + else if (stacksize == RREF_ANY) + stacksize = 1; + else if (common->currententry->start == 0) + stacksize = stacksize == 0; + else + stacksize = stacksize == (int)GET2(common->start, common->currententry->start + 1 + LINK_SIZE); + + if (*matchingpath == OP_RREF || stacksize || common->currententry == NULL) + { + SLJIT_ASSERT(!has_alternatives); + if (stacksize != 0) + matchingpath += 1 + IMM2_SIZE; + else + { + if (*cc == OP_ALT) + { + matchingpath = cc + 1 + LINK_SIZE; + cc += GET(cc, 1); + } + else + matchingpath = cc; + } + } + else + { + SLJIT_ASSERT(has_alternatives); + + stacksize = GET2(matchingpath, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1, STACK_TOP, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, SLJIT_IMM, common->name_count); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, SLJIT_IMM, common->name_entry_size); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, SLJIT_IMM, GET2(common->start, common->currententry->start + 1 + LINK_SIZE)); + OP1(SLJIT_MOV, SLJIT_SCRATCH_REG1, 0, SLJIT_IMM, stacksize); + GET_LOCAL_BASE(SLJIT_SCRATCH_REG2, 0, 0); + OP1(SLJIT_MOV, SLJIT_SCRATCH_REG3, 0, SLJIT_IMM, common->name_table); + sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_searchgroups)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1); + add_jump(compiler, &(BACKTRACK_AS(bracket_backtrack)->u.condfailed), CMP(SLJIT_C_EQUAL, SLJIT_SCRATCH_REG1, 0, SLJIT_IMM, 0)); + matchingpath += 1 + IMM2_SIZE; + } + } + else + { + SLJIT_ASSERT(has_alternatives && *matchingpath >= OP_ASSERT && *matchingpath <= OP_ASSERTBACK_NOT); + /* Similar code as PUSH_BACKTRACK macro. */ + assert = sljit_alloc_memory(compiler, sizeof(assert_backtrack)); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return NULL; + memset(assert, 0, sizeof(assert_backtrack)); + assert->common.cc = matchingpath; + BACKTRACK_AS(bracket_backtrack)->u.assert = assert; + matchingpath = compile_assert_matchingpath(common, matchingpath, assert, TRUE); + } + } + +compile_matchingpath(common, matchingpath, cc, backtrack); +if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return NULL; + +if (opcode == OP_ONCE) + match_once_common(common, ket, BACKTRACK_AS(bracket_backtrack)->u.framesize, private_data_ptr, has_alternatives, needs_control_head); + +stacksize = 0; +if (repeat_type == OP_MINUPTO) + { + /* We need to preserve the counter. TMP2 will be used below. */ + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr); + stacksize++; + } +if (ket != OP_KET || bra != OP_BRA) + stacksize++; +if (offset != 0) + { + if (common->capture_last_ptr != 0) + stacksize++; + if (common->optimized_cbracket[offset >> 1] == 0) + stacksize += 2; + } +if (has_alternatives && opcode != OP_ONCE) + stacksize++; + +if (stacksize > 0) + allocate_stack(common, stacksize); + +stacksize = 0; +if (repeat_type == OP_MINUPTO) + { + /* TMP2 was set above. */ + OP2(SLJIT_SUB, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP2, 0, SLJIT_IMM, 1); + stacksize++; + } + +if (ket != OP_KET || bra != OP_BRA) + { + if (ket != OP_KET) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); + else + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); + stacksize++; + } + +if (offset != 0) + stacksize = match_capture_common(common, stacksize, offset, private_data_ptr); + +if (has_alternatives) + { + if (opcode != OP_ONCE) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); + if (ket != OP_KETRMAX) + BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL(); + } + +/* Must be after the matchingpath label. */ +if (offset != 0 && common->optimized_cbracket[offset >> 1] != 0) + { + SLJIT_ASSERT(private_data_ptr == OVECTOR(offset + 0)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), STR_PTR, 0); + } + +if (ket == OP_KETRMAX) + { + if (repeat_type != 0) + { + if (has_alternatives) + BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL(); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, SLJIT_IMM, 1); + JUMPTO(SLJIT_C_NOT_ZERO, rmax_label); + /* Drop STR_PTR for greedy plus quantifier. */ + if (opcode != OP_ONCE) + free_stack(common, 1); + } + else if (opcode == OP_ONCE || opcode >= OP_SBRA) + { + if (has_alternatives) + BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL(); + /* Checking zero-length iteration. */ + if (opcode != OP_ONCE) + { + CMPTO(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, STR_PTR, 0, rmax_label); + /* Drop STR_PTR for greedy plus quantifier. */ + if (bra != OP_BRAZERO) + free_stack(common, 1); + } + else + /* TMP2 must contain the starting STR_PTR. */ + CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_PTR, 0, rmax_label); + } + else + JUMPTO(SLJIT_JUMP, rmax_label); + BACKTRACK_AS(bracket_backtrack)->recursive_matchingpath = LABEL(); + } + +if (repeat_type == OP_EXACT) + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, SLJIT_IMM, 1); + JUMPTO(SLJIT_C_NOT_ZERO, rmax_label); + } +else if (repeat_type == OP_UPTO) + { + /* We need to preserve the counter. */ + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr); + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + } + +if (bra == OP_BRAZERO) + BACKTRACK_AS(bracket_backtrack)->zero_matchingpath = LABEL(); + +if (bra == OP_BRAMINZERO) + { + /* This is a backtrack path! (From the viewpoint of OP_BRAMINZERO) */ + JUMPTO(SLJIT_JUMP, ((braminzero_backtrack *)parent)->matchingpath); + if (braminzero != NULL) + { + JUMPHERE(braminzero); + /* We need to release the end pointer to perform the + backtrack for the zero-length iteration. When + framesize is < 0, OP_ONCE will do the release itself. */ + if (opcode == OP_ONCE && BACKTRACK_AS(bracket_backtrack)->u.framesize >= 0) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + } + else if (ket == OP_KETRMIN && opcode != OP_ONCE) + free_stack(common, 1); + } + /* Continue to the normal backtrack. */ + } + +if ((ket != OP_KET && bra != OP_BRAMINZERO) || bra == OP_BRAZERO) + count_match(common); + +/* Skip the other alternatives. */ +while (*cc == OP_ALT) + cc += GET(cc, 1); +cc += 1 + LINK_SIZE; + +/* Temporarily encoding the needs_control_head in framesize. */ +if (opcode == OP_ONCE) + BACKTRACK_AS(bracket_backtrack)->u.framesize = (BACKTRACK_AS(bracket_backtrack)->u.framesize << 1) | (needs_control_head ? 1 : 0); +return cc + repeat_length; +} + +static pcre_uchar *compile_bracketpos_matchingpath(compiler_common *common, pcre_uchar *cc, backtrack_common *parent) +{ +DEFINE_COMPILER; +backtrack_common *backtrack; +pcre_uchar opcode; +int private_data_ptr; +int cbraprivptr = 0; +BOOL needs_control_head; +int framesize; +int stacksize; +int offset = 0; +BOOL zero = FALSE; +pcre_uchar *ccbegin = NULL; +int stack; /* Also contains the offset of control head. */ +struct sljit_label *loop = NULL; +struct jump_list *emptymatch = NULL; + +PUSH_BACKTRACK(sizeof(bracketpos_backtrack), cc, NULL); +if (*cc == OP_BRAPOSZERO) + { + zero = TRUE; + cc++; + } + +opcode = *cc; +private_data_ptr = PRIVATE_DATA(cc); +SLJIT_ASSERT(private_data_ptr != 0); +BACKTRACK_AS(bracketpos_backtrack)->private_data_ptr = private_data_ptr; +switch(opcode) + { + case OP_BRAPOS: + case OP_SBRAPOS: + ccbegin = cc + 1 + LINK_SIZE; + break; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + offset = GET2(cc, 1 + LINK_SIZE); + /* This case cannot be optimized in the same was as + normal capturing brackets. */ + SLJIT_ASSERT(common->optimized_cbracket[offset] == 0); + cbraprivptr = OVECTOR_PRIV(offset); + offset <<= 1; + ccbegin = cc + 1 + LINK_SIZE + IMM2_SIZE; + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } + +framesize = get_framesize(common, cc, NULL, FALSE, &needs_control_head); +BACKTRACK_AS(bracketpos_backtrack)->framesize = framesize; +if (framesize < 0) + { + if (offset != 0) + { + stacksize = 2; + if (common->capture_last_ptr != 0) + stacksize++; + } + else + stacksize = 1; + + if (needs_control_head) + stacksize++; + if (!zero) + stacksize++; + + BACKTRACK_AS(bracketpos_backtrack)->stacksize = stacksize; + allocate_stack(common, stacksize); + if (framesize == no_frame) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, STACK_TOP, 0); + + stack = 0; + if (offset != 0) + { + stack = 2; + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0); + if (common->capture_last_ptr != 0) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->capture_last_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0); + if (needs_control_head) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr); + if (common->capture_last_ptr != 0) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP1, 0); + stack = 3; + } + } + else + { + if (needs_control_head) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + stack = 1; + } + + if (needs_control_head) + stack++; + if (!zero) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), SLJIT_IMM, 1); + if (needs_control_head) + { + stack--; + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP2, 0); + } + } +else + { + stacksize = framesize + 1; + if (!zero) + stacksize++; + if (needs_control_head) + stacksize++; + if (offset == 0) + stacksize++; + BACKTRACK_AS(bracketpos_backtrack)->stacksize = stacksize; + + allocate_stack(common, stacksize); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + if (needs_control_head) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr); + OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, -STACK(stacksize - 1)); + + stack = 0; + if (!zero) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 1); + stack = 1; + } + if (needs_control_head) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP2, 0); + stack++; + } + if (offset == 0) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), STR_PTR, 0); + stack++; + } + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP1, 0); + init_frame(common, cc, NULL, stacksize - 1, stacksize - framesize, FALSE); + stack -= 1 + (offset == 0); + } + +if (offset != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr, STR_PTR, 0); + +loop = LABEL(); +while (*cc != OP_KETRPOS) + { + backtrack->top = NULL; + backtrack->topbacktracks = NULL; + cc += GET(cc, 1); + + compile_matchingpath(common, ccbegin, cc, backtrack); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return NULL; + + if (framesize < 0) + { + if (framesize == no_frame) + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + + if (offset != 0) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr, STR_PTR, 0); + if (common->capture_last_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->capture_last_ptr, SLJIT_IMM, offset >> 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); + } + else + { + if (opcode == OP_SBRAPOS) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + } + + if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS) + add_jump(compiler, &emptymatch, CMP(SLJIT_C_EQUAL, TMP1, 0, STR_PTR, 0)); + + if (!zero) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0); + } + else + { + if (offset != 0) + { + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr, STR_PTR, 0); + if (common->capture_last_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->capture_last_ptr, SLJIT_IMM, offset >> 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); + } + else + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + OP2(SLJIT_ADD, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); + if (opcode == OP_SBRAPOS) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw), STR_PTR, 0); + } + + if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS) + add_jump(compiler, &emptymatch, CMP(SLJIT_C_EQUAL, TMP1, 0, STR_PTR, 0)); + + if (!zero) + { + if (framesize < 0) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0); + else + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + } + + if (needs_control_head) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(stack)); + + JUMPTO(SLJIT_JUMP, loop); + flush_stubs(common); + + compile_backtrackingpath(common, backtrack->top); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return NULL; + set_jumps(backtrack->topbacktracks, LABEL()); + + if (framesize < 0) + { + if (offset != 0) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr); + else + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + } + else + { + if (offset != 0) + { + /* Last alternative. */ + if (*cc == OP_KETRPOS) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr); + } + else + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw)); + } + } + + if (*cc == OP_KETRPOS) + break; + ccbegin = cc + 1 + LINK_SIZE; + } + +/* We don't have to restore the control head in case of a failed match. */ + +backtrack->topbacktracks = NULL; +if (!zero) + { + if (framesize < 0) + add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0)); + else /* TMP2 is set to [private_data_ptr] above. */ + add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(TMP2), (stacksize - 1) * sizeof(sljit_sw), SLJIT_IMM, 0)); + } + +/* None of them matched. */ +set_jumps(emptymatch, LABEL()); +count_match(common); +return cc + 1 + LINK_SIZE; +} + +static SLJIT_INLINE pcre_uchar *get_iterator_parameters(compiler_common *common, pcre_uchar *cc, pcre_uchar *opcode, pcre_uchar *type, int *arg1, int *arg2, pcre_uchar **end) +{ +int class_len; + +*opcode = *cc; +if (*opcode >= OP_STAR && *opcode <= OP_POSUPTO) + { + cc++; + *type = OP_CHAR; + } +else if (*opcode >= OP_STARI && *opcode <= OP_POSUPTOI) + { + cc++; + *type = OP_CHARI; + *opcode -= OP_STARI - OP_STAR; + } +else if (*opcode >= OP_NOTSTAR && *opcode <= OP_NOTPOSUPTO) + { + cc++; + *type = OP_NOT; + *opcode -= OP_NOTSTAR - OP_STAR; + } +else if (*opcode >= OP_NOTSTARI && *opcode <= OP_NOTPOSUPTOI) + { + cc++; + *type = OP_NOTI; + *opcode -= OP_NOTSTARI - OP_STAR; + } +else if (*opcode >= OP_TYPESTAR && *opcode <= OP_TYPEPOSUPTO) + { + cc++; + *opcode -= OP_TYPESTAR - OP_STAR; + *type = 0; + } +else + { + SLJIT_ASSERT(*opcode >= OP_CLASS || *opcode <= OP_XCLASS); + *type = *opcode; + cc++; + class_len = (*type < OP_XCLASS) ? (int)(1 + (32 / sizeof(pcre_uchar))) : GET(cc, 0); + *opcode = cc[class_len - 1]; + if (*opcode >= OP_CRSTAR && *opcode <= OP_CRMINQUERY) + { + *opcode -= OP_CRSTAR - OP_STAR; + if (end != NULL) + *end = cc + class_len; + } + else + { + SLJIT_ASSERT(*opcode == OP_CRRANGE || *opcode == OP_CRMINRANGE); + *arg1 = GET2(cc, (class_len + IMM2_SIZE)); + *arg2 = GET2(cc, class_len); + + if (*arg2 == 0) + { + SLJIT_ASSERT(*arg1 != 0); + *opcode = (*opcode == OP_CRRANGE) ? OP_UPTO : OP_MINUPTO; + } + if (*arg1 == *arg2) + *opcode = OP_EXACT; + + if (end != NULL) + *end = cc + class_len + 2 * IMM2_SIZE; + } + return cc; + } + +if (*opcode == OP_UPTO || *opcode == OP_MINUPTO || *opcode == OP_EXACT || *opcode == OP_POSUPTO) + { + *arg1 = GET2(cc, 0); + cc += IMM2_SIZE; + } + +if (*type == 0) + { + *type = *cc; + if (end != NULL) + *end = next_opcode(common, cc); + cc++; + return cc; + } + +if (end != NULL) + { + *end = cc + 1; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(*cc)) *end += GET_EXTRALEN(*cc); +#endif + } +return cc; +} + +static pcre_uchar *compile_iterator_matchingpath(compiler_common *common, pcre_uchar *cc, backtrack_common *parent) +{ +DEFINE_COMPILER; +backtrack_common *backtrack; +pcre_uchar opcode; +pcre_uchar type; +int arg1 = -1, arg2 = -1; +pcre_uchar* end; +jump_list *nomatch = NULL; +struct sljit_jump *jump = NULL; +struct sljit_label *label; +int private_data_ptr = PRIVATE_DATA(cc); +int base = (private_data_ptr == 0) ? SLJIT_MEM1(STACK_TOP) : SLJIT_MEM1(SLJIT_LOCALS_REG); +int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr; +int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + (int)sizeof(sljit_sw); +int tmp_base, tmp_offset; + +PUSH_BACKTRACK(sizeof(iterator_backtrack), cc, NULL); + +cc = get_iterator_parameters(common, cc, &opcode, &type, &arg1, &arg2, &end); + +switch(type) + { + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ALLANY: + case OP_ANYBYTE: + case OP_ANYNL: + case OP_NOT_HSPACE: + case OP_HSPACE: + case OP_NOT_VSPACE: + case OP_VSPACE: + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_CLASS: + case OP_NCLASS: + tmp_base = TMP3; + tmp_offset = 0; + break; + + default: + SLJIT_ASSERT_STOP(); + /* Fall through. */ + + case OP_EXTUNI: + case OP_XCLASS: + case OP_NOTPROP: + case OP_PROP: + tmp_base = SLJIT_MEM1(SLJIT_LOCALS_REG); + tmp_offset = POSSESSIVE0; + break; + } + +switch(opcode) + { + case OP_STAR: + case OP_PLUS: + case OP_UPTO: + case OP_CRRANGE: + if (type == OP_ANYNL || type == OP_EXTUNI) + { + SLJIT_ASSERT(private_data_ptr == 0); + if (opcode == OP_STAR || opcode == OP_UPTO) + { + allocate_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0); + } + else + { + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + + if (opcode == OP_UPTO || opcode == OP_CRRANGE) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, SLJIT_IMM, 0); + + label = LABEL(); + compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks); + if (opcode == OP_UPTO || opcode == OP_CRRANGE) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + if (opcode == OP_CRRANGE && arg2 > 0) + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, arg2, label); + if (opcode == OP_UPTO || (opcode == OP_CRRANGE && arg1 > 0)) + jump = CMP(SLJIT_C_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, arg1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, TMP1, 0); + } + + /* We cannot use TMP3 because of this allocate_stack. */ + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + JUMPTO(SLJIT_JUMP, label); + if (jump != NULL) + JUMPHERE(jump); + } + else + { + if (opcode == OP_PLUS) + compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks); + if (private_data_ptr == 0) + allocate_stack(common, 2); + OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); + if (opcode <= OP_PLUS) + OP1(SLJIT_MOV, base, offset1, STR_PTR, 0); + else + OP1(SLJIT_MOV, base, offset1, SLJIT_IMM, 1); + label = LABEL(); + compile_char1_matchingpath(common, type, cc, &nomatch); + OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); + if (opcode <= OP_PLUS) + JUMPTO(SLJIT_JUMP, label); + else if (opcode == OP_CRRANGE && arg1 == 0) + { + OP2(SLJIT_ADD, base, offset1, base, offset1, SLJIT_IMM, 1); + JUMPTO(SLJIT_JUMP, label); + } + else + { + OP1(SLJIT_MOV, TMP1, 0, base, offset1); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, base, offset1, TMP1, 0); + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, arg1 + 1, label); + } + set_jumps(nomatch, LABEL()); + if (opcode == OP_CRRANGE) + add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_C_LESS, base, offset1, SLJIT_IMM, arg2 + 1)); + OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); + } + BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL(); + break; + + case OP_MINSTAR: + case OP_MINPLUS: + if (opcode == OP_MINPLUS) + compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks); + if (private_data_ptr == 0) + allocate_stack(common, 1); + OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); + BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL(); + break; + + case OP_MINUPTO: + case OP_CRMINRANGE: + if (private_data_ptr == 0) + allocate_stack(common, 2); + OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); + OP1(SLJIT_MOV, base, offset1, SLJIT_IMM, 1); + if (opcode == OP_CRMINRANGE) + add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_JUMP)); + BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL(); + break; + + case OP_QUERY: + case OP_MINQUERY: + if (private_data_ptr == 0) + allocate_stack(common, 1); + OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); + if (opcode == OP_QUERY) + compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks); + BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL(); + break; + + case OP_EXACT: + OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, arg1); + label = LABEL(); + compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks); + OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + JUMPTO(SLJIT_C_NOT_ZERO, label); + break; + + case OP_POSSTAR: + case OP_POSPLUS: + case OP_POSUPTO: + if (opcode == OP_POSPLUS) + compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks); + if (opcode == OP_POSUPTO) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1, SLJIT_IMM, arg1); + OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0); + label = LABEL(); + compile_char1_matchingpath(common, type, cc, &nomatch); + OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0); + if (opcode != OP_POSUPTO) + JUMPTO(SLJIT_JUMP, label); + else + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1, SLJIT_IMM, 1); + JUMPTO(SLJIT_C_NOT_ZERO, label); + } + set_jumps(nomatch, LABEL()); + OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset); + break; + + case OP_POSQUERY: + OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0); + compile_char1_matchingpath(common, type, cc, &nomatch); + OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0); + set_jumps(nomatch, LABEL()); + OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset); + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } + +count_match(common); +return end; +} + +static SLJIT_INLINE pcre_uchar *compile_fail_accept_matchingpath(compiler_common *common, pcre_uchar *cc, backtrack_common *parent) +{ +DEFINE_COMPILER; +backtrack_common *backtrack; + +PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL); + +if (*cc == OP_FAIL) + { + add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_JUMP)); + return cc + 1; + } + +if (*cc == OP_ASSERT_ACCEPT || common->currententry != NULL) + { + /* No need to check notempty conditions. */ + if (common->accept_label == NULL) + add_jump(compiler, &common->accept, JUMP(SLJIT_JUMP)); + else + JUMPTO(SLJIT_JUMP, common->accept_label); + return cc + 1; + } + +if (common->accept_label == NULL) + add_jump(compiler, &common->accept, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0))); +else + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), common->accept_label); +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty)); +add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); +OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty_atstart)); +if (common->accept_label == NULL) + add_jump(compiler, &common->accept, CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, 0)); +else + CMPTO(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, 0, common->accept_label); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); +if (common->accept_label == NULL) + add_jump(compiler, &common->accept, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_PTR, 0)); +else + CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_PTR, 0, common->accept_label); +add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_JUMP)); +return cc + 1; +} + +static SLJIT_INLINE pcre_uchar *compile_close_matchingpath(compiler_common *common, pcre_uchar *cc) +{ +DEFINE_COMPILER; +int offset = GET2(cc, 1); +BOOL optimized_cbracket = common->optimized_cbracket[offset] != 0; + +/* Data will be discarded anyway... */ +if (common->currententry != NULL) + return cc + 1 + IMM2_SIZE; + +if (!optimized_cbracket) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR_PRIV(offset)); +offset <<= 1; +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), STR_PTR, 0); +if (!optimized_cbracket) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); +return cc + 1 + IMM2_SIZE; +} + +static SLJIT_INLINE pcre_uchar *compile_control_verb_matchingpath(compiler_common *common, pcre_uchar *cc, backtrack_common *parent) +{ +DEFINE_COMPILER; +backtrack_common *backtrack; +pcre_uchar opcode = *cc; +pcre_uchar *ccend = cc + 1; + +if (opcode == OP_PRUNE_ARG || opcode == OP_SKIP_ARG || opcode == OP_THEN_ARG) + ccend += 2 + cc[1]; + +PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL); + +if (opcode == OP_SKIP) + { + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + return ccend; + } + +if (opcode == OP_PRUNE_ARG || opcode == OP_THEN_ARG) + { + OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr, TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr), TMP2, 0); + } + +return ccend; +} + +static pcre_uchar then_trap_opcode[1] = { OP_THEN_TRAP }; + +static SLJIT_INLINE void compile_then_trap_matchingpath(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, backtrack_common *parent) +{ +DEFINE_COMPILER; +backtrack_common *backtrack; +BOOL needs_control_head; +int size; + +PUSH_BACKTRACK_NOVALUE(sizeof(then_trap_backtrack), cc); +common->then_trap = BACKTRACK_AS(then_trap_backtrack); +BACKTRACK_AS(then_trap_backtrack)->common.cc = then_trap_opcode; +BACKTRACK_AS(then_trap_backtrack)->start = (sljit_sw)(cc - common->start); +BACKTRACK_AS(then_trap_backtrack)->framesize = get_framesize(common, cc, ccend, FALSE, &needs_control_head); + +size = BACKTRACK_AS(then_trap_backtrack)->framesize; +size = 3 + (size < 0 ? 0 : size); + +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr); +allocate_stack(common, size); +if (size > 3) + OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw)); +else + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, STACK_TOP, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 1), SLJIT_IMM, BACKTRACK_AS(then_trap_backtrack)->start); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 2), SLJIT_IMM, type_then_trap); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 3), TMP2, 0); + +size = BACKTRACK_AS(then_trap_backtrack)->framesize; +if (size >= 0) + init_frame(common, cc, ccend, size - 1, 0, FALSE); +} + +static void compile_matchingpath(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, backtrack_common *parent) +{ +DEFINE_COMPILER; +backtrack_common *backtrack; +BOOL has_then_trap = FALSE; +then_trap_backtrack *save_then_trap = NULL; + +SLJIT_ASSERT(*ccend == OP_END || (*ccend >= OP_ALT && *ccend <= OP_KETRPOS)); + +if (common->has_then && common->then_offsets[cc - common->start] != 0) + { + SLJIT_ASSERT(*ccend != OP_END && common->control_head_ptr != 0); + has_then_trap = TRUE; + save_then_trap = common->then_trap; + /* Tail item on backtrack. */ + compile_then_trap_matchingpath(common, cc, ccend, parent); + } + +while (cc < ccend) + { + switch(*cc) + { + case OP_SOD: + case OP_SOM: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ALLANY: + case OP_ANYBYTE: + case OP_NOTPROP: + case OP_PROP: + case OP_ANYNL: + case OP_NOT_HSPACE: + case OP_HSPACE: + case OP_NOT_VSPACE: + case OP_VSPACE: + case OP_EXTUNI: + case OP_EODN: + case OP_EOD: + case OP_CIRC: + case OP_CIRCM: + case OP_DOLL: + case OP_DOLLM: + case OP_NOT: + case OP_NOTI: + case OP_REVERSE: + cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks); + break; + + case OP_SET_SOM: + PUSH_BACKTRACK_NOVALUE(sizeof(backtrack_common), cc); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + cc++; + break; + + case OP_CHAR: + case OP_CHARI: + if (common->mode == JIT_COMPILE) + cc = compile_charn_matchingpath(common, cc, ccend, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks); + else + cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks); + break; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_UPTO: + case OP_MINUPTO: + case OP_EXACT: + case OP_POSSTAR: + case OP_POSPLUS: + case OP_POSQUERY: + case OP_POSUPTO: + case OP_STARI: + case OP_MINSTARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_QUERYI: + case OP_MINQUERYI: + case OP_UPTOI: + case OP_MINUPTOI: + case OP_EXACTI: + case OP_POSSTARI: + case OP_POSPLUSI: + case OP_POSQUERYI: + case OP_POSUPTOI: + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + case OP_NOTEXACT: + case OP_NOTPOSSTAR: + case OP_NOTPOSPLUS: + case OP_NOTPOSQUERY: + case OP_NOTPOSUPTO: + case OP_NOTSTARI: + case OP_NOTMINSTARI: + case OP_NOTPLUSI: + case OP_NOTMINPLUSI: + case OP_NOTQUERYI: + case OP_NOTMINQUERYI: + case OP_NOTUPTOI: + case OP_NOTMINUPTOI: + case OP_NOTEXACTI: + case OP_NOTPOSSTARI: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERYI: + case OP_NOTPOSUPTOI: + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + case OP_TYPEPOSUPTO: + cc = compile_iterator_matchingpath(common, cc, parent); + break; + + case OP_CLASS: + case OP_NCLASS: + if (cc[1 + (32 / sizeof(pcre_uchar))] >= OP_CRSTAR && cc[1 + (32 / sizeof(pcre_uchar))] <= OP_CRMINRANGE) + cc = compile_iterator_matchingpath(common, cc, parent); + else + cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks); + break; + +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + case OP_XCLASS: + if (*(cc + GET(cc, 1)) >= OP_CRSTAR && *(cc + GET(cc, 1)) <= OP_CRMINRANGE) + cc = compile_iterator_matchingpath(common, cc, parent); + else + cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks); + break; +#endif + + case OP_REF: + case OP_REFI: + if (cc[1 + IMM2_SIZE] >= OP_CRSTAR && cc[1 + IMM2_SIZE] <= OP_CRMINRANGE) + cc = compile_ref_iterator_matchingpath(common, cc, parent); + else + cc = compile_ref_matchingpath(common, cc, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE, FALSE); + break; + + case OP_RECURSE: + cc = compile_recurse_matchingpath(common, cc, parent); + break; + + case OP_CALLOUT: + cc = compile_callout_matchingpath(common, cc, parent); + break; + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + PUSH_BACKTRACK_NOVALUE(sizeof(assert_backtrack), cc); + cc = compile_assert_matchingpath(common, cc, BACKTRACK_AS(assert_backtrack), FALSE); + break; + + case OP_BRAMINZERO: + PUSH_BACKTRACK_NOVALUE(sizeof(braminzero_backtrack), cc); + cc = bracketend(cc + 1); + if (*(cc - 1 - LINK_SIZE) != OP_KETRMIN) + { + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + } + else + { + allocate_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), STR_PTR, 0); + } + BACKTRACK_AS(braminzero_backtrack)->matchingpath = LABEL(); + if (cc[1] > OP_ASSERTBACK_NOT) + count_match(common); + break; + + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRA: + case OP_CBRA: + case OP_COND: + case OP_SBRA: + case OP_SCBRA: + case OP_SCOND: + cc = compile_bracket_matchingpath(common, cc, parent); + break; + + case OP_BRAZERO: + if (cc[1] > OP_ASSERTBACK_NOT) + cc = compile_bracket_matchingpath(common, cc, parent); + else + { + PUSH_BACKTRACK_NOVALUE(sizeof(assert_backtrack), cc); + cc = compile_assert_matchingpath(common, cc, BACKTRACK_AS(assert_backtrack), FALSE); + } + break; + + case OP_BRAPOS: + case OP_CBRAPOS: + case OP_SBRAPOS: + case OP_SCBRAPOS: + case OP_BRAPOSZERO: + cc = compile_bracketpos_matchingpath(common, cc, parent); + break; + + case OP_MARK: + PUSH_BACKTRACK_NOVALUE(sizeof(backtrack_common), cc); + SLJIT_ASSERT(common->mark_ptr != 0); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr); + allocate_stack(common, common->has_skip_arg ? 5 : 1); + OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(common->has_skip_arg ? 4 : 0), TMP2, 0); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr, TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr), TMP2, 0); + if (common->has_skip_arg) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, STACK_TOP, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, type_mark); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), SLJIT_IMM, (sljit_sw)(cc + 2)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(3), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0); + } + cc += 1 + 2 + cc[1]; + break; + + case OP_PRUNE: + case OP_PRUNE_ARG: + case OP_SKIP: + case OP_SKIP_ARG: + case OP_THEN: + case OP_THEN_ARG: + case OP_COMMIT: + cc = compile_control_verb_matchingpath(common, cc, parent); + break; + + case OP_FAIL: + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + cc = compile_fail_accept_matchingpath(common, cc, parent); + break; + + case OP_CLOSE: + cc = compile_close_matchingpath(common, cc); + break; + + case OP_SKIPZERO: + cc = bracketend(cc + 1); + break; + + default: + SLJIT_ASSERT_STOP(); + return; + } + if (cc == NULL) + return; + } + +if (has_then_trap) + { + /* Head item on backtrack. */ + PUSH_BACKTRACK_NOVALUE(sizeof(then_trap_backtrack), cc); + BACKTRACK_AS(then_trap_backtrack)->common.cc = then_trap_opcode; + BACKTRACK_AS(then_trap_backtrack)->then_trap = common->then_trap; + common->then_trap = save_then_trap; + } +SLJIT_ASSERT(cc == ccend); +} + +#undef PUSH_BACKTRACK +#undef PUSH_BACKTRACK_NOVALUE +#undef BACKTRACK_AS + +#define COMPILE_BACKTRACKINGPATH(current) \ + do \ + { \ + compile_backtrackingpath(common, (current)); \ + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \ + return; \ + } \ + while (0) + +#define CURRENT_AS(type) ((type *)current) + +static void compile_iterator_backtrackingpath(compiler_common *common, struct backtrack_common *current) +{ +DEFINE_COMPILER; +pcre_uchar *cc = current->cc; +pcre_uchar opcode; +pcre_uchar type; +int arg1 = -1, arg2 = -1; +struct sljit_label *label = NULL; +struct sljit_jump *jump = NULL; +jump_list *jumplist = NULL; +int private_data_ptr = PRIVATE_DATA(cc); +int base = (private_data_ptr == 0) ? SLJIT_MEM1(STACK_TOP) : SLJIT_MEM1(SLJIT_LOCALS_REG); +int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr; +int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + (int)sizeof(sljit_sw); + +cc = get_iterator_parameters(common, cc, &opcode, &type, &arg1, &arg2, NULL); + +switch(opcode) + { + case OP_STAR: + case OP_PLUS: + case OP_UPTO: + case OP_CRRANGE: + if (type == OP_ANYNL || type == OP_EXTUNI) + { + SLJIT_ASSERT(private_data_ptr == 0); + set_jumps(current->topbacktracks, LABEL()); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_backtrack)->matchingpath); + } + else + { + if (opcode == OP_UPTO) + arg2 = 0; + if (opcode <= OP_PLUS) + { + OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); + jump = CMP(SLJIT_C_LESS_EQUAL, STR_PTR, 0, base, offset1); + } + else + { + OP1(SLJIT_MOV, TMP1, 0, base, offset1); + OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); + jump = CMP(SLJIT_C_LESS_EQUAL, TMP1, 0, SLJIT_IMM, arg2 + 1); + OP2(SLJIT_SUB, base, offset1, TMP1, 0, SLJIT_IMM, 1); + } + skip_char_back(common); + OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); + JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_backtrack)->matchingpath); + if (opcode == OP_CRRANGE) + set_jumps(current->topbacktracks, LABEL()); + JUMPHERE(jump); + if (private_data_ptr == 0) + free_stack(common, 2); + if (opcode == OP_PLUS) + set_jumps(current->topbacktracks, LABEL()); + } + break; + + case OP_MINSTAR: + case OP_MINPLUS: + OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); + compile_char1_matchingpath(common, type, cc, &jumplist); + OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); + JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_backtrack)->matchingpath); + set_jumps(jumplist, LABEL()); + if (private_data_ptr == 0) + free_stack(common, 1); + if (opcode == OP_MINPLUS) + set_jumps(current->topbacktracks, LABEL()); + break; + + case OP_MINUPTO: + case OP_CRMINRANGE: + if (opcode == OP_CRMINRANGE) + { + label = LABEL(); + set_jumps(current->topbacktracks, label); + } + OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); + compile_char1_matchingpath(common, type, cc, &jumplist); + + OP1(SLJIT_MOV, TMP1, 0, base, offset1); + OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, base, offset1, TMP1, 0); + + if (opcode == OP_CRMINRANGE) + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, arg2 + 1, label); + + if (opcode == OP_CRMINRANGE && arg1 == 0) + JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_backtrack)->matchingpath); + else + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, arg1 + 2, CURRENT_AS(iterator_backtrack)->matchingpath); + + set_jumps(jumplist, LABEL()); + if (private_data_ptr == 0) + free_stack(common, 2); + break; + + case OP_QUERY: + OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); + OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_backtrack)->matchingpath); + jump = JUMP(SLJIT_JUMP); + set_jumps(current->topbacktracks, LABEL()); + OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); + OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0); + JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_backtrack)->matchingpath); + JUMPHERE(jump); + if (private_data_ptr == 0) + free_stack(common, 1); + break; + + case OP_MINQUERY: + OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); + OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0); + jump = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + compile_char1_matchingpath(common, type, cc, &jumplist); + JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_backtrack)->matchingpath); + set_jumps(jumplist, LABEL()); + JUMPHERE(jump); + if (private_data_ptr == 0) + free_stack(common, 1); + break; + + case OP_EXACT: + case OP_POSPLUS: + set_jumps(current->topbacktracks, LABEL()); + break; + + case OP_POSSTAR: + case OP_POSQUERY: + case OP_POSUPTO: + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } +} + +static SLJIT_INLINE void compile_ref_iterator_backtrackingpath(compiler_common *common, struct backtrack_common *current) +{ +DEFINE_COMPILER; +pcre_uchar *cc = current->cc; +pcre_uchar type; + +type = cc[1 + IMM2_SIZE]; +if ((type & 0x1) == 0) + { + set_jumps(current->topbacktracks, LABEL()); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_backtrack)->matchingpath); + return; + } + +OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); +CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_backtrack)->matchingpath); +set_jumps(current->topbacktracks, LABEL()); +free_stack(common, 2); +} + +static SLJIT_INLINE void compile_recurse_backtrackingpath(compiler_common *common, struct backtrack_common *current) +{ +DEFINE_COMPILER; + +if (CURRENT_AS(recurse_backtrack)->inlined_pattern) + compile_backtrackingpath(common, current->top); +set_jumps(current->topbacktracks, LABEL()); +if (CURRENT_AS(recurse_backtrack)->inlined_pattern) + return; + +if (common->has_set_som && common->mark_ptr != 0) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + free_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr, TMP1, 0); + } +else if (common->has_set_som || common->mark_ptr != 0) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->has_set_som ? (int)(OVECTOR(0)) : common->mark_ptr, TMP2, 0); + } +} + +static void compile_assert_backtrackingpath(compiler_common *common, struct backtrack_common *current) +{ +DEFINE_COMPILER; +pcre_uchar *cc = current->cc; +pcre_uchar bra = OP_BRA; +struct sljit_jump *brajump = NULL; + +SLJIT_ASSERT(*cc != OP_BRAMINZERO); +if (*cc == OP_BRAZERO) + { + bra = *cc; + cc++; + } + +if (bra == OP_BRAZERO) + { + SLJIT_ASSERT(current->topbacktracks == NULL); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + } + +if (CURRENT_AS(assert_backtrack)->framesize < 0) + { + set_jumps(current->topbacktracks, LABEL()); + + if (bra == OP_BRAZERO) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(assert_backtrack)->matchingpath); + free_stack(common, 1); + } + return; + } + +if (bra == OP_BRAZERO) + { + if (*cc == OP_ASSERT_NOT || *cc == OP_ASSERTBACK_NOT) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(assert_backtrack)->matchingpath); + free_stack(common, 1); + return; + } + free_stack(common, 1); + brajump = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + } + +if (*cc == OP_ASSERT || *cc == OP_ASSERTBACK) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), CURRENT_AS(assert_backtrack)->private_data_ptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), CURRENT_AS(assert_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(assert_backtrack)->framesize * sizeof(sljit_sw)); + + set_jumps(current->topbacktracks, LABEL()); + } +else + set_jumps(current->topbacktracks, LABEL()); + +if (bra == OP_BRAZERO) + { + /* We know there is enough place on the stack. */ + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + JUMPTO(SLJIT_JUMP, CURRENT_AS(assert_backtrack)->matchingpath); + JUMPHERE(brajump); + } +} + +static void compile_bracket_backtrackingpath(compiler_common *common, struct backtrack_common *current) +{ +DEFINE_COMPILER; +int opcode, stacksize, count; +int offset = 0; +int private_data_ptr = CURRENT_AS(bracket_backtrack)->private_data_ptr; +int repeat_ptr = 0, repeat_type = 0, repeat_count = 0; +pcre_uchar *cc = current->cc; +pcre_uchar *ccbegin; +pcre_uchar *ccprev; +jump_list *jumplist = NULL; +jump_list *jumplistitem = NULL; +pcre_uchar bra = OP_BRA; +pcre_uchar ket; +assert_backtrack *assert; +BOOL has_alternatives; +BOOL needs_control_head = FALSE; +struct sljit_jump *brazero = NULL; +struct sljit_jump *once = NULL; +struct sljit_jump *cond = NULL; +struct sljit_label *rmin_label = NULL; +struct sljit_label *exact_label = NULL; + +if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO) + { + bra = *cc; + cc++; + } + +opcode = *cc; +ccbegin = bracketend(cc) - 1 - LINK_SIZE; +ket = *ccbegin; +if (ket == OP_KET && PRIVATE_DATA(ccbegin) != 0) + { + repeat_ptr = PRIVATE_DATA(ccbegin); + repeat_type = PRIVATE_DATA(ccbegin + 2); + repeat_count = PRIVATE_DATA(ccbegin + 3); + SLJIT_ASSERT(repeat_type != 0 && repeat_count != 0); + if (repeat_type == OP_UPTO) + ket = OP_KETRMAX; + if (repeat_type == OP_MINUPTO) + ket = OP_KETRMIN; + } +ccbegin = cc; +cc += GET(cc, 1); +has_alternatives = *cc == OP_ALT; +if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) + has_alternatives = (ccbegin[1 + LINK_SIZE] >= OP_ASSERT && ccbegin[1 + LINK_SIZE] <= OP_ASSERTBACK_NOT) || CURRENT_AS(bracket_backtrack)->u.condfailed != NULL; +if (opcode == OP_CBRA || opcode == OP_SCBRA) + offset = (GET2(ccbegin, 1 + LINK_SIZE)) << 1; +if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN)) + opcode = OP_SCOND; +if (SLJIT_UNLIKELY(opcode == OP_ONCE_NC)) + opcode = OP_ONCE; + +/* Decoding the needs_control_head in framesize. */ +if (opcode == OP_ONCE) + { + needs_control_head = (CURRENT_AS(bracket_backtrack)->u.framesize & 0x1) != 0; + CURRENT_AS(bracket_backtrack)->u.framesize >>= 1; + } + +if (ket != OP_KET && repeat_type != 0) + { + /* TMP1 is used in OP_KETRMIN below. */ + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + if (repeat_type == OP_UPTO) + OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, TMP1, 0, SLJIT_IMM, 1); + else + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, TMP1, 0); + } + +if (ket == OP_KETRMAX) + { + if (bra == OP_BRAZERO) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + brazero = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 0); + } + } +else if (ket == OP_KETRMIN) + { + if (bra != OP_BRAMINZERO) + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + if (repeat_type != 0) + { + /* TMP1 was set a few lines above. */ + CMPTO(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, CURRENT_AS(bracket_backtrack)->recursive_matchingpath); + /* Drop STR_PTR for non-greedy plus quantifier. */ + if (opcode != OP_ONCE) + free_stack(common, 1); + } + else if (opcode >= OP_SBRA || opcode == OP_ONCE) + { + /* Checking zero-length iteration. */ + if (opcode != OP_ONCE || CURRENT_AS(bracket_backtrack)->u.framesize < 0) + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, CURRENT_AS(bracket_backtrack)->recursive_matchingpath); + else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (CURRENT_AS(bracket_backtrack)->u.framesize + 1) * sizeof(sljit_sw), CURRENT_AS(bracket_backtrack)->recursive_matchingpath); + } + /* Drop STR_PTR for non-greedy plus quantifier. */ + if (opcode != OP_ONCE) + free_stack(common, 1); + } + else + JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->recursive_matchingpath); + } + rmin_label = LABEL(); + if (repeat_type != 0) + OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, SLJIT_IMM, 1); + } +else if (bra == OP_BRAZERO) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + brazero = CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0); + } +else if (repeat_type == OP_EXACT) + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, SLJIT_IMM, 1); + exact_label = LABEL(); + } + +if (offset != 0) + { + if (common->capture_last_ptr != 0) + { + SLJIT_ASSERT(common->optimized_cbracket[offset >> 1] == 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->capture_last_ptr, TMP1, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(2)); + free_stack(common, 3); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), TMP1, 0); + } + else if (common->optimized_cbracket[offset >> 1] == 0) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + free_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), TMP2, 0); + } + } + +if (SLJIT_UNLIKELY(opcode == OP_ONCE)) + { + if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + } + once = JUMP(SLJIT_JUMP); + } +else if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) + { + if (has_alternatives) + { + /* Always exactly one alternative. */ + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + + jumplistitem = sljit_alloc_memory(compiler, sizeof(jump_list)); + if (SLJIT_UNLIKELY(!jumplistitem)) + return; + jumplist = jumplistitem; + jumplistitem->next = NULL; + jumplistitem->jump = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 1); + } + } +else if (*cc == OP_ALT) + { + /* Build a jump list. Get the last successfully matched branch index. */ + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + count = 1; + do + { + /* Append as the last item. */ + if (jumplist != NULL) + { + jumplistitem->next = sljit_alloc_memory(compiler, sizeof(jump_list)); + jumplistitem = jumplistitem->next; + } + else + { + jumplistitem = sljit_alloc_memory(compiler, sizeof(jump_list)); + jumplist = jumplistitem; + } + + if (SLJIT_UNLIKELY(!jumplistitem)) + return; + + jumplistitem->next = NULL; + jumplistitem->jump = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, count++); + cc += GET(cc, 1); + } + while (*cc == OP_ALT); + + cc = ccbegin + GET(ccbegin, 1); + } + +COMPILE_BACKTRACKINGPATH(current->top); +if (current->topbacktracks) + set_jumps(current->topbacktracks, LABEL()); + +if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) + { + /* Conditional block always has at most one alternative. */ + if (ccbegin[1 + LINK_SIZE] >= OP_ASSERT && ccbegin[1 + LINK_SIZE] <= OP_ASSERTBACK_NOT) + { + SLJIT_ASSERT(has_alternatives); + assert = CURRENT_AS(bracket_backtrack)->u.assert; + if (assert->framesize >= 0 && (ccbegin[1 + LINK_SIZE] == OP_ASSERT || ccbegin[1 + LINK_SIZE] == OP_ASSERTBACK)) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), assert->private_data_ptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_sw)); + } + cond = JUMP(SLJIT_JUMP); + set_jumps(CURRENT_AS(bracket_backtrack)->u.assert->condfailed, LABEL()); + } + else if (CURRENT_AS(bracket_backtrack)->u.condfailed != NULL) + { + SLJIT_ASSERT(has_alternatives); + cond = JUMP(SLJIT_JUMP); + set_jumps(CURRENT_AS(bracket_backtrack)->u.condfailed, LABEL()); + } + else + SLJIT_ASSERT(!has_alternatives); + } + +if (has_alternatives) + { + count = 1; + do + { + current->top = NULL; + current->topbacktracks = NULL; + current->nextbacktracks = NULL; + /* Conditional blocks always have an additional alternative, even if it is empty. */ + if (*cc == OP_ALT) + { + ccprev = cc + 1 + LINK_SIZE; + cc += GET(cc, 1); + if (opcode != OP_COND && opcode != OP_SCOND) + { + if (opcode != OP_ONCE) + { + if (private_data_ptr != 0) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr); + else + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + } + else + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(needs_control_head ? 1 : 0)); + } + compile_matchingpath(common, ccprev, cc, current); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return; + } + + /* Instructions after the current alternative is successfully matched. */ + /* There is a similar code in compile_bracket_matchingpath. */ + if (opcode == OP_ONCE) + match_once_common(common, ket, CURRENT_AS(bracket_backtrack)->u.framesize, private_data_ptr, has_alternatives, needs_control_head); + + stacksize = 0; + if (repeat_type == OP_MINUPTO) + { + /* We need to preserve the counter. TMP2 will be used below. */ + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr); + stacksize++; + } + if (ket != OP_KET || bra != OP_BRA) + stacksize++; + if (offset != 0) + { + if (common->capture_last_ptr != 0) + stacksize++; + if (common->optimized_cbracket[offset >> 1] == 0) + stacksize += 2; + } + if (opcode != OP_ONCE) + stacksize++; + + if (stacksize > 0) + allocate_stack(common, stacksize); + + stacksize = 0; + if (repeat_type == OP_MINUPTO) + { + /* TMP2 was set above. */ + OP2(SLJIT_SUB, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP2, 0, SLJIT_IMM, 1); + stacksize++; + } + + if (ket != OP_KET || bra != OP_BRA) + { + if (ket != OP_KET) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); + else + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); + stacksize++; + } + + if (offset != 0) + stacksize = match_capture_common(common, stacksize, offset, private_data_ptr); + + if (opcode != OP_ONCE) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, count++); + + if (offset != 0 && ket == OP_KETRMAX && common->optimized_cbracket[offset >> 1] != 0) + { + /* If ket is not OP_KETRMAX, this code path is executed after the jump to alternative_matchingpath. */ + SLJIT_ASSERT(private_data_ptr == OVECTOR(offset + 0)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), STR_PTR, 0); + } + + JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->alternative_matchingpath); + + if (opcode != OP_ONCE) + { + SLJIT_ASSERT(jumplist); + JUMPHERE(jumplist->jump); + jumplist = jumplist->next; + } + + COMPILE_BACKTRACKINGPATH(current->top); + if (current->topbacktracks) + set_jumps(current->topbacktracks, LABEL()); + SLJIT_ASSERT(!current->nextbacktracks); + } + while (*cc == OP_ALT); + SLJIT_ASSERT(!jumplist); + + if (cond != NULL) + { + SLJIT_ASSERT(opcode == OP_COND || opcode == OP_SCOND); + assert = CURRENT_AS(bracket_backtrack)->u.assert; + if ((ccbegin[1 + LINK_SIZE] == OP_ASSERT_NOT || ccbegin[1 + LINK_SIZE] == OP_ASSERTBACK_NOT) && assert->framesize >= 0) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), assert->private_data_ptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_sw)); + } + JUMPHERE(cond); + } + + /* Free the STR_PTR. */ + if (private_data_ptr == 0) + free_stack(common, 1); + } + +if (offset != 0) + { + /* Using both tmp register is better for instruction scheduling. */ + if (common->optimized_cbracket[offset >> 1] != 0) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + free_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), TMP2, 0); + } + else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, TMP1, 0); + } + } +else if (opcode == OP_SBRA || opcode == OP_SCOND) + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + } +else if (opcode == OP_ONCE) + { + cc = ccbegin + GET(ccbegin, 1); + stacksize = needs_control_head ? 1 : 0; + + if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0) + { + /* Reset head and drop saved frame. */ + stacksize += CURRENT_AS(bracket_backtrack)->u.framesize + ((ket != OP_KET || *cc == OP_ALT) ? 2 : 1); + } + else if (ket == OP_KETRMAX || (*cc == OP_ALT && ket != OP_KETRMIN)) + { + /* The STR_PTR must be released. */ + stacksize++; + } + free_stack(common, stacksize); + + JUMPHERE(once); + /* Restore previous private_data_ptr */ + if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracket_backtrack)->u.framesize * sizeof(sljit_sw)); + else if (ket == OP_KETRMIN) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + /* See the comment below. */ + free_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, TMP1, 0); + } + } + +if (repeat_type == OP_EXACT) + { + OP2(SLJIT_ADD, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), repeat_ptr, TMP1, 0); + CMPTO(SLJIT_C_LESS_EQUAL, TMP1, 0, SLJIT_IMM, repeat_count, exact_label); + } +else if (ket == OP_KETRMAX) + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + if (bra != OP_BRAZERO) + free_stack(common, 1); + + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(bracket_backtrack)->recursive_matchingpath); + if (bra == OP_BRAZERO) + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->zero_matchingpath); + JUMPHERE(brazero); + free_stack(common, 1); + } + } +else if (ket == OP_KETRMIN) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + + /* OP_ONCE removes everything in case of a backtrack, so we don't + need to explicitly release the STR_PTR. The extra release would + affect badly the free_stack(2) above. */ + if (opcode != OP_ONCE) + free_stack(common, 1); + CMPTO(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, rmin_label); + if (opcode == OP_ONCE) + free_stack(common, bra == OP_BRAMINZERO ? 2 : 1); + else if (bra == OP_BRAMINZERO) + free_stack(common, 1); + } +else if (bra == OP_BRAZERO) + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->zero_matchingpath); + JUMPHERE(brazero); + } +} + +static SLJIT_INLINE void compile_bracketpos_backtrackingpath(compiler_common *common, struct backtrack_common *current) +{ +DEFINE_COMPILER; +int offset; +struct sljit_jump *jump; + +if (CURRENT_AS(bracketpos_backtrack)->framesize < 0) + { + if (*current->cc == OP_CBRAPOS || *current->cc == OP_SCBRAPOS) + { + offset = (GET2(current->cc, 1 + LINK_SIZE)) << 1; + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); + if (common->capture_last_ptr != 0) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(2)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), TMP2, 0); + if (common->capture_last_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->capture_last_ptr, TMP1, 0); + } + set_jumps(current->topbacktracks, LABEL()); + free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize); + return; + } + +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), CURRENT_AS(bracketpos_backtrack)->private_data_ptr); +add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + +if (current->topbacktracks) + { + jump = JUMP(SLJIT_JUMP); + set_jumps(current->topbacktracks, LABEL()); + /* Drop the stack frame. */ + free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize); + JUMPHERE(jump); + } +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracketpos_backtrack)->framesize * sizeof(sljit_sw)); +} + +static SLJIT_INLINE void compile_braminzero_backtrackingpath(compiler_common *common, struct backtrack_common *current) +{ +assert_backtrack backtrack; + +current->top = NULL; +current->topbacktracks = NULL; +current->nextbacktracks = NULL; +if (current->cc[1] > OP_ASSERTBACK_NOT) + { + /* Manual call of compile_bracket_matchingpath and compile_bracket_backtrackingpath. */ + compile_bracket_matchingpath(common, current->cc, current); + compile_bracket_backtrackingpath(common, current->top); + } +else + { + memset(&backtrack, 0, sizeof(backtrack)); + backtrack.common.cc = current->cc; + backtrack.matchingpath = CURRENT_AS(braminzero_backtrack)->matchingpath; + /* Manual call of compile_assert_matchingpath. */ + compile_assert_matchingpath(common, current->cc, &backtrack, FALSE); + } +SLJIT_ASSERT(!current->nextbacktracks && !current->topbacktracks); +} + +static SLJIT_INLINE void compile_control_verb_backtrackingpath(compiler_common *common, struct backtrack_common *current) +{ +DEFINE_COMPILER; +pcre_uchar opcode = *current->cc; +struct sljit_label *loop; +struct sljit_jump *jump; + +if (opcode == OP_THEN || opcode == OP_THEN_ARG) + { + if (common->then_trap != NULL) + { + SLJIT_ASSERT(common->control_head_ptr != 0); + + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, type_then_trap); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, common->then_trap->start); + jump = JUMP(SLJIT_JUMP); + + loop = LABEL(); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), -(int)sizeof(sljit_sw)); + JUMPHERE(jump); + CMPTO(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), -(int)(2 * sizeof(sljit_sw)), TMP1, 0, loop); + CMPTO(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), -(int)(3 * sizeof(sljit_sw)), TMP2, 0, loop); + add_jump(compiler, &common->then_trap->quit, JUMP(SLJIT_JUMP)); + return; + } + else if (common->positive_assert) + { + add_jump(compiler, &common->positive_assert_quit, JUMP(SLJIT_JUMP)); + return; + } + } + +if (common->local_exit) + { + if (common->quit_label == NULL) + add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP)); + else + JUMPTO(SLJIT_JUMP, common->quit_label); + return; + } + +if (opcode == OP_SKIP_ARG) + { + SLJIT_ASSERT(common->control_head_ptr != 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, STACK_TOP, 0); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, (sljit_sw)(current->cc + 2)); + sljit_emit_ijump(compiler, SLJIT_CALL2, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_search_mark)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); + + OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0); + add_jump(compiler, &common->reset_match, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, -1)); + return; + } + +if (opcode == OP_SKIP) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); +else + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_IMM, 0); +add_jump(compiler, &common->reset_match, JUMP(SLJIT_JUMP)); +} + +static SLJIT_INLINE void compile_then_trap_backtrackingpath(compiler_common *common, struct backtrack_common *current) +{ +DEFINE_COMPILER; +struct sljit_jump *jump; +int size; + +if (CURRENT_AS(then_trap_backtrack)->then_trap) + { + common->then_trap = CURRENT_AS(then_trap_backtrack)->then_trap; + return; + } + +size = CURRENT_AS(then_trap_backtrack)->framesize; +size = 3 + (size < 0 ? 0 : size); + +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(size - 3)); +free_stack(common, size); +jump = JUMP(SLJIT_JUMP); + +set_jumps(CURRENT_AS(then_trap_backtrack)->quit, LABEL()); +/* STACK_TOP is set by THEN. */ +if (CURRENT_AS(then_trap_backtrack)->framesize >= 0) + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); +free_stack(common, 3); + +JUMPHERE(jump); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, TMP1, 0); +} + +static void compile_backtrackingpath(compiler_common *common, struct backtrack_common *current) +{ +DEFINE_COMPILER; +then_trap_backtrack *save_then_trap = common->then_trap; + +while (current) + { + if (current->nextbacktracks != NULL) + set_jumps(current->nextbacktracks, LABEL()); + switch(*current->cc) + { + case OP_SET_SOM: + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), TMP1, 0); + break; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_UPTO: + case OP_MINUPTO: + case OP_EXACT: + case OP_POSSTAR: + case OP_POSPLUS: + case OP_POSQUERY: + case OP_POSUPTO: + case OP_STARI: + case OP_MINSTARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_QUERYI: + case OP_MINQUERYI: + case OP_UPTOI: + case OP_MINUPTOI: + case OP_EXACTI: + case OP_POSSTARI: + case OP_POSPLUSI: + case OP_POSQUERYI: + case OP_POSUPTOI: + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + case OP_NOTEXACT: + case OP_NOTPOSSTAR: + case OP_NOTPOSPLUS: + case OP_NOTPOSQUERY: + case OP_NOTPOSUPTO: + case OP_NOTSTARI: + case OP_NOTMINSTARI: + case OP_NOTPLUSI: + case OP_NOTMINPLUSI: + case OP_NOTQUERYI: + case OP_NOTMINQUERYI: + case OP_NOTUPTOI: + case OP_NOTMINUPTOI: + case OP_NOTEXACTI: + case OP_NOTPOSSTARI: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERYI: + case OP_NOTPOSUPTOI: + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + case OP_TYPEPOSUPTO: + case OP_CLASS: + case OP_NCLASS: +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: +#endif + compile_iterator_backtrackingpath(common, current); + break; + + case OP_REF: + case OP_REFI: + compile_ref_iterator_backtrackingpath(common, current); + break; + + case OP_RECURSE: + compile_recurse_backtrackingpath(common, current); + break; + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + compile_assert_backtrackingpath(common, current); + break; + + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRA: + case OP_CBRA: + case OP_COND: + case OP_SBRA: + case OP_SCBRA: + case OP_SCOND: + compile_bracket_backtrackingpath(common, current); + break; + + case OP_BRAZERO: + if (current->cc[1] > OP_ASSERTBACK_NOT) + compile_bracket_backtrackingpath(common, current); + else + compile_assert_backtrackingpath(common, current); + break; + + case OP_BRAPOS: + case OP_CBRAPOS: + case OP_SBRAPOS: + case OP_SCBRAPOS: + case OP_BRAPOSZERO: + compile_bracketpos_backtrackingpath(common, current); + break; + + case OP_BRAMINZERO: + compile_braminzero_backtrackingpath(common, current); + break; + + case OP_MARK: + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(common->has_skip_arg ? 4 : 0)); + if (common->has_skip_arg) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, common->has_skip_arg ? 5 : 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr, TMP1, 0); + if (common->has_skip_arg) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, TMP2, 0); + break; + + case OP_THEN: + case OP_THEN_ARG: + case OP_PRUNE: + case OP_PRUNE_ARG: + case OP_SKIP: + case OP_SKIP_ARG: + compile_control_verb_backtrackingpath(common, current); + break; + + case OP_COMMIT: + if (!common->local_exit) + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_NOMATCH); + if (common->quit_label == NULL) + add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP)); + else + JUMPTO(SLJIT_JUMP, common->quit_label); + break; + + case OP_CALLOUT: + case OP_FAIL: + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + set_jumps(current->topbacktracks, LABEL()); + break; + + case OP_THEN_TRAP: + /* A virtual opcode for then traps. */ + compile_then_trap_backtrackingpath(common, current); + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } + current = current->prev; + } +common->then_trap = save_then_trap; +} + +static SLJIT_INLINE void compile_recurse(compiler_common *common) +{ +DEFINE_COMPILER; +pcre_uchar *cc = common->start + common->currententry->start; +pcre_uchar *ccbegin = cc + 1 + LINK_SIZE + (*cc == OP_BRA ? 0 : IMM2_SIZE); +pcre_uchar *ccend = bracketend(cc); +BOOL needs_control_head; +int framesize = get_framesize(common, cc, NULL, TRUE, &needs_control_head); +int private_data_size = get_private_data_copy_length(common, ccbegin, ccend, needs_control_head); +int alternativesize; +BOOL needs_frame; +backtrack_common altbacktrack; +struct sljit_jump *jump; + +/* Recurse captures then. */ +common->then_trap = NULL; + +SLJIT_ASSERT(*cc == OP_BRA || *cc == OP_CBRA || *cc == OP_CBRAPOS || *cc == OP_SCBRA || *cc == OP_SCBRAPOS); +needs_frame = framesize >= 0; +if (!needs_frame) + framesize = 0; +alternativesize = *(cc + GET(cc, 1)) == OP_ALT ? 1 : 0; + +SLJIT_ASSERT(common->currententry->entry == NULL && common->recursive_head_ptr != 0); +common->currententry->entry = LABEL(); +set_jumps(common->currententry->calls, common->currententry->entry); + +sljit_emit_fast_enter(compiler, TMP2, 0); +allocate_stack(common, private_data_size + framesize + alternativesize); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(private_data_size + framesize + alternativesize - 1), TMP2, 0); +copy_private_data(common, ccbegin, ccend, TRUE, private_data_size + framesize + alternativesize, framesize + alternativesize, needs_control_head); +if (needs_control_head) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, SLJIT_IMM, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->recursive_head_ptr, STACK_TOP, 0); +if (needs_frame) + init_frame(common, cc, NULL, framesize + alternativesize - 1, alternativesize, TRUE); + +if (alternativesize > 0) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + +memset(&altbacktrack, 0, sizeof(backtrack_common)); +common->quit_label = NULL; +common->accept_label = NULL; +common->quit = NULL; +common->accept = NULL; +altbacktrack.cc = ccbegin; +cc += GET(cc, 1); +while (1) + { + altbacktrack.top = NULL; + altbacktrack.topbacktracks = NULL; + + if (altbacktrack.cc != ccbegin) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + + compile_matchingpath(common, altbacktrack.cc, cc, &altbacktrack); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return; + + add_jump(compiler, &common->accept, JUMP(SLJIT_JUMP)); + + compile_backtrackingpath(common, altbacktrack.top); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return; + set_jumps(altbacktrack.topbacktracks, LABEL()); + + if (*cc != OP_ALT) + break; + + altbacktrack.cc = cc + 1 + LINK_SIZE; + cc += GET(cc, 1); + } + +/* None of them matched. */ +OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0); +jump = JUMP(SLJIT_JUMP); + +if (common->quit != NULL) + { + set_jumps(common->quit, LABEL()); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->recursive_head_ptr); + if (needs_frame) + { + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); + } + OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0); + common->quit = NULL; + add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP)); + } + +set_jumps(common->accept, LABEL()); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->recursive_head_ptr); +if (needs_frame) + { + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); + } +OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 1); + +JUMPHERE(jump); +if (common->quit != NULL) + set_jumps(common->quit, LABEL()); +copy_private_data(common, ccbegin, ccend, FALSE, private_data_size + framesize + alternativesize, framesize + alternativesize, needs_control_head); +free_stack(common, private_data_size + framesize + alternativesize); +if (needs_control_head) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->recursive_head_ptr, TMP1, 0); + OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, TMP2, 0); + } +else + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->recursive_head_ptr, TMP2, 0); + } +sljit_emit_fast_return(compiler, SLJIT_MEM1(STACK_TOP), 0); +} + +#undef COMPILE_BACKTRACKINGPATH +#undef CURRENT_AS + +void +PRIV(jit_compile)(const REAL_PCRE *re, PUBL(extra) *extra, int mode) +{ +struct sljit_compiler *compiler; +backtrack_common rootbacktrack; +compiler_common common_data; +compiler_common *common = &common_data; +const pcre_uint8 *tables = re->tables; +pcre_study_data *study; +int private_data_size; +pcre_uchar *ccend; +executable_functions *functions; +void *executable_func; +sljit_uw executable_size; +struct sljit_label *mainloop_label = NULL; +struct sljit_label *continue_match_label; +struct sljit_label *empty_match_found_label; +struct sljit_label *empty_match_backtrack_label; +struct sljit_label *reset_match_label; +struct sljit_jump *jump; +struct sljit_jump *minlength_check_failed = NULL; +struct sljit_jump *reqbyte_notfound = NULL; +struct sljit_jump *empty_match; +struct sljit_label *quit_label; + +SLJIT_ASSERT((extra->flags & PCRE_EXTRA_STUDY_DATA) != 0); +study = extra->study_data; + +if (!tables) + tables = PRIV(default_tables); + +memset(&rootbacktrack, 0, sizeof(backtrack_common)); +memset(common, 0, sizeof(compiler_common)); +rootbacktrack.cc = (pcre_uchar *)re + re->name_table_offset + re->name_count * re->name_entry_size; + +common->start = rootbacktrack.cc; +common->fcc = tables + fcc_offset; +common->lcc = (sljit_sw)(tables + lcc_offset); +common->mode = mode; +common->nltype = NLTYPE_FIXED; +switch(re->options & PCRE_NEWLINE_BITS) + { + case 0: + /* Compile-time default */ + switch(NEWLINE) + { + case -1: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANY; break; + case -2: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANYCRLF; break; + default: common->newline = NEWLINE; break; + } + break; + case PCRE_NEWLINE_CR: common->newline = CHAR_CR; break; + case PCRE_NEWLINE_LF: common->newline = CHAR_NL; break; + case PCRE_NEWLINE_CR+ + PCRE_NEWLINE_LF: common->newline = (CHAR_CR << 8) | CHAR_NL; break; + case PCRE_NEWLINE_ANY: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANY; break; + case PCRE_NEWLINE_ANYCRLF: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANYCRLF; break; + default: return; + } +if ((re->options & PCRE_BSR_ANYCRLF) != 0) + common->bsr_nltype = NLTYPE_ANYCRLF; +else if ((re->options & PCRE_BSR_UNICODE) != 0) + common->bsr_nltype = NLTYPE_ANY; +else + { +#ifdef BSR_ANYCRLF + common->bsr_nltype = NLTYPE_ANYCRLF; +#else + common->bsr_nltype = NLTYPE_ANY; +#endif + } +common->endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; +common->ctypes = (sljit_sw)(tables + ctypes_offset); +common->digits[0] = -2; +common->name_table = (sljit_sw)((pcre_uchar *)re + re->name_table_offset); +common->name_count = re->name_count; +common->name_entry_size = re->name_entry_size; +common->jscript_compat = (re->options & PCRE_JAVASCRIPT_COMPAT) != 0; +#ifdef SUPPORT_UTF +/* PCRE_UTF[16|32] have the same value as PCRE_UTF8. */ +common->utf = (re->options & PCRE_UTF8) != 0; +#ifdef SUPPORT_UCP +common->use_ucp = (re->options & PCRE_UCP) != 0; +#endif +#endif /* SUPPORT_UTF */ +ccend = bracketend(rootbacktrack.cc); + +/* Calculate the local space size on the stack. */ +common->ovector_start = LIMIT_MATCH + sizeof(sljit_sw); +common->optimized_cbracket = (pcre_uint8 *)SLJIT_MALLOC(re->top_bracket + 1); +if (!common->optimized_cbracket) + return; +#if defined DEBUG_FORCE_UNOPTIMIZED_CBRAS && DEBUG_FORCE_UNOPTIMIZED_CBRAS == 1 +memset(common->optimized_cbracket, 0, re->top_bracket + 1); +#else +memset(common->optimized_cbracket, 1, re->top_bracket + 1); +#endif + +SLJIT_ASSERT(*rootbacktrack.cc == OP_BRA && ccend[-(1 + LINK_SIZE)] == OP_KET); +#if defined DEBUG_FORCE_UNOPTIMIZED_CBRAS && DEBUG_FORCE_UNOPTIMIZED_CBRAS == 2 +common->capture_last_ptr = common->ovector_start; +common->ovector_start += sizeof(sljit_sw); +#endif +if (!check_opcode_types(common, rootbacktrack.cc, ccend)) + { + SLJIT_FREE(common->optimized_cbracket); + return; + } + +/* Checking flags and updating ovector_start. */ +if (mode == JIT_COMPILE && (re->flags & PCRE_REQCHSET) != 0 && (re->options & PCRE_NO_START_OPTIMIZE) == 0) + { + common->req_char_ptr = common->ovector_start; + common->ovector_start += sizeof(sljit_sw); + } +if (mode != JIT_COMPILE) + { + common->start_used_ptr = common->ovector_start; + common->ovector_start += sizeof(sljit_sw); + if (mode == JIT_PARTIAL_SOFT_COMPILE) + { + common->hit_start = common->ovector_start; + common->ovector_start += 2 * sizeof(sljit_sw); + } + else + { + SLJIT_ASSERT(mode == JIT_PARTIAL_HARD_COMPILE); + common->needs_start_ptr = TRUE; + } + } +if ((re->options & PCRE_FIRSTLINE) != 0) + { + common->first_line_end = common->ovector_start; + common->ovector_start += sizeof(sljit_sw); + } +#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD +common->control_head_ptr = 1; +#endif +if (common->control_head_ptr != 0) + { + common->control_head_ptr = common->ovector_start; + common->ovector_start += sizeof(sljit_sw); + } +if (common->needs_start_ptr && common->has_set_som) + { + /* Saving the real start pointer is necessary. */ + common->start_ptr = common->ovector_start; + common->ovector_start += sizeof(sljit_sw); + } +else + common->needs_start_ptr = FALSE; + +/* Aligning ovector to even number of sljit words. */ +if ((common->ovector_start & sizeof(sljit_sw)) != 0) + common->ovector_start += sizeof(sljit_sw); + +if (common->start_ptr == 0) + common->start_ptr = OVECTOR(0); + +/* Capturing brackets cannot be optimized if callouts are allowed. */ +if (common->capture_last_ptr != 0) + memset(common->optimized_cbracket, 0, re->top_bracket + 1); + +SLJIT_ASSERT(!(common->req_char_ptr != 0 && common->start_used_ptr != 0)); +common->cbra_ptr = OVECTOR_START + (re->top_bracket + 1) * 2 * sizeof(sljit_sw); + +common->private_data_ptrs = (int *)SLJIT_MALLOC((ccend - rootbacktrack.cc) * sizeof(sljit_si)); +if (!common->private_data_ptrs) + { + SLJIT_FREE(common->optimized_cbracket); + return; + } +memset(common->private_data_ptrs, 0, (ccend - rootbacktrack.cc) * sizeof(int)); + +private_data_size = common->cbra_ptr + (re->top_bracket + 1) * sizeof(sljit_sw); +set_private_data_ptrs(common, &private_data_size, ccend); +if (private_data_size > SLJIT_MAX_LOCAL_SIZE) + { + SLJIT_FREE(common->private_data_ptrs); + SLJIT_FREE(common->optimized_cbracket); + return; + } + +if (common->has_then) + { + common->then_offsets = (pcre_uint8 *)SLJIT_MALLOC(ccend - rootbacktrack.cc); + if (!common->then_offsets) + { + SLJIT_FREE(common->optimized_cbracket); + SLJIT_FREE(common->private_data_ptrs); + return; + } + memset(common->then_offsets, 0, ccend - rootbacktrack.cc); + set_then_offsets(common, rootbacktrack.cc, NULL); + } + +compiler = sljit_create_compiler(); +if (!compiler) + { + SLJIT_FREE(common->optimized_cbracket); + SLJIT_FREE(common->private_data_ptrs); + if (common->has_then) + SLJIT_FREE(common->then_offsets); + return; + } +common->compiler = compiler; + +/* Main pcre_jit_exec entry. */ +sljit_emit_enter(compiler, 1, 5, 5, private_data_size); + +/* Register init. */ +reset_ovector(common, (re->top_bracket + 1) * 2); +if (common->req_char_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->req_char_ptr, SLJIT_SCRATCH_REG1, 0); + +OP1(SLJIT_MOV, ARGUMENTS, 0, SLJIT_SAVED_REG1, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_SAVED_REG1, 0); +OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); +OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, end)); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); +OP1(SLJIT_MOV_UI, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, limit_match)); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, base)); +OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, limit)); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LIMIT_MATCH, TMP1, 0); + +if (mode == JIT_PARTIAL_SOFT_COMPILE) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->hit_start, SLJIT_IMM, -1); +if (common->mark_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr, SLJIT_IMM, 0); +if (common->control_head_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, SLJIT_IMM, 0); + +/* Main part of the matching */ +if ((re->options & PCRE_ANCHORED) == 0) + { + mainloop_label = mainloop_entry(common, (re->flags & PCRE_HASCRORLF) != 0, (re->options & PCRE_FIRSTLINE) != 0); + continue_match_label = LABEL(); + /* Forward search if possible. */ + if ((re->options & PCRE_NO_START_OPTIMIZE) == 0) + { + if (mode == JIT_COMPILE && fast_forward_first_n_chars(common, (re->options & PCRE_FIRSTLINE) != 0)) + { /* Do nothing */ } + else if ((re->flags & PCRE_FIRSTSET) != 0) + fast_forward_first_char(common, (pcre_uchar)re->first_char, (re->flags & PCRE_FCH_CASELESS) != 0, (re->options & PCRE_FIRSTLINE) != 0); + else if ((re->flags & PCRE_STARTLINE) != 0) + fast_forward_newline(common, (re->options & PCRE_FIRSTLINE) != 0); + else if ((re->flags & PCRE_STARTLINE) == 0 && study != NULL && (study->flags & PCRE_STUDY_MAPPED) != 0) + fast_forward_start_bits(common, (sljit_uw)study->start_bits, (re->options & PCRE_FIRSTLINE) != 0); + } + } +else + continue_match_label = LABEL(); + +if (mode == JIT_COMPILE && study->minlength > 0 && (re->options & PCRE_NO_START_OPTIMIZE) == 0) + { + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_NOMATCH); + OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(study->minlength)); + minlength_check_failed = CMP(SLJIT_C_GREATER, TMP2, 0, STR_END, 0); + } +if (common->req_char_ptr != 0) + reqbyte_notfound = search_requested_char(common, (pcre_uchar)re->req_char, (re->flags & PCRE_RCH_CASELESS) != 0, (re->flags & PCRE_FIRSTSET) != 0); + +/* Store the current STR_PTR in OVECTOR(0). */ +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), STR_PTR, 0); +/* Copy the limit of allowed recursions. */ +OP1(SLJIT_MOV, COUNT_MATCH, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LIMIT_MATCH); +if (common->capture_last_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->capture_last_ptr, SLJIT_IMM, -1); + +if (common->needs_start_ptr) + { + SLJIT_ASSERT(common->start_ptr != OVECTOR(0)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_ptr, STR_PTR, 0); + } +else + SLJIT_ASSERT(common->start_ptr == OVECTOR(0)); + +/* Copy the beginning of the string. */ +if (mode == JIT_PARTIAL_SOFT_COMPILE) + { + jump = CMP(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), common->hit_start, SLJIT_IMM, -1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->hit_start + sizeof(sljit_sw), STR_PTR, 0); + JUMPHERE(jump); + } +else if (mode == JIT_PARTIAL_HARD_COMPILE) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, STR_PTR, 0); + +compile_matchingpath(common, rootbacktrack.cc, ccend, &rootbacktrack); +if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + { + sljit_free_compiler(compiler); + SLJIT_FREE(common->optimized_cbracket); + SLJIT_FREE(common->private_data_ptrs); + if (common->has_then) + SLJIT_FREE(common->then_offsets); + return; + } + +empty_match = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); +empty_match_found_label = LABEL(); + +common->accept_label = LABEL(); +if (common->accept != NULL) + set_jumps(common->accept, common->accept_label); + +/* This means we have a match. Update the ovector. */ +copy_ovector(common, re->top_bracket + 1); +common->quit_label = common->forced_quit_label = LABEL(); +if (common->quit != NULL) + set_jumps(common->quit, common->quit_label); +if (common->forced_quit != NULL) + set_jumps(common->forced_quit, common->forced_quit_label); +if (minlength_check_failed != NULL) + SET_LABEL(minlength_check_failed, common->forced_quit_label); +sljit_emit_return(compiler, SLJIT_MOV, SLJIT_RETURN_REG, 0); + +if (mode != JIT_COMPILE) + { + common->partialmatchlabel = LABEL(); + set_jumps(common->partialmatch, common->partialmatchlabel); + return_with_partial_match(common, common->quit_label); + } + +empty_match_backtrack_label = LABEL(); +compile_backtrackingpath(common, rootbacktrack.top); +if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + { + sljit_free_compiler(compiler); + SLJIT_FREE(common->optimized_cbracket); + SLJIT_FREE(common->private_data_ptrs); + if (common->has_then) + SLJIT_FREE(common->then_offsets); + return; + } + +SLJIT_ASSERT(rootbacktrack.prev == NULL); +reset_match_label = LABEL(); + +if (mode == JIT_PARTIAL_SOFT_COMPILE) + { + /* Update hit_start only in the first time. */ + jump = CMP(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), common->hit_start, SLJIT_IMM, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_used_ptr, SLJIT_IMM, -1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->hit_start, TMP1, 0); + JUMPHERE(jump); + } + +/* Check we have remaining characters. */ +if ((re->options & PCRE_ANCHORED) == 0 && (re->options & PCRE_FIRSTLINE) != 0) + { + SLJIT_ASSERT(common->first_line_end != 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->first_line_end); + } + +OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->start_ptr); + +if ((re->options & PCRE_ANCHORED) == 0) + { + if ((re->options & PCRE_FIRSTLINE) == 0) + CMPTO(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0, mainloop_label); + else + CMPTO(SLJIT_C_LESS, STR_PTR, 0, TMP1, 0, mainloop_label); + } + +/* No more remaining characters. */ +if (reqbyte_notfound != NULL) + JUMPHERE(reqbyte_notfound); + +if (mode == JIT_PARTIAL_SOFT_COMPILE) + CMPTO(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), common->hit_start, SLJIT_IMM, -1, common->partialmatchlabel); + +OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_NOMATCH); +JUMPTO(SLJIT_JUMP, common->quit_label); + +flush_stubs(common); + +JUMPHERE(empty_match); +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty)); +CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0, empty_match_backtrack_label); +OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty_atstart)); +CMPTO(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, 0, empty_match_found_label); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); +CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_PTR, 0, empty_match_found_label); +JUMPTO(SLJIT_JUMP, empty_match_backtrack_label); + +common->currententry = common->entries; +common->local_exit = TRUE; +quit_label = common->quit_label; +while (common->currententry != NULL) + { + /* Might add new entries. */ + compile_recurse(common); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + { + sljit_free_compiler(compiler); + SLJIT_FREE(common->optimized_cbracket); + SLJIT_FREE(common->private_data_ptrs); + if (common->has_then) + SLJIT_FREE(common->then_offsets); + return; + } + flush_stubs(common); + common->currententry = common->currententry->next; + } +common->local_exit = FALSE; +common->quit_label = quit_label; + +/* Allocating stack, returns with PCRE_ERROR_JIT_STACKLIMIT if fails. */ +/* This is a (really) rare case. */ +set_jumps(common->stackalloc, LABEL()); +/* RETURN_ADDR is not a saved register. */ +sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, TMP2, 0); +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); +OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, top), STACK_TOP, 0); +OP2(SLJIT_ADD, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit), SLJIT_IMM, STACK_GROWTH_RATE); + +sljit_emit_ijump(compiler, SLJIT_CALL2, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_stack_resize)); +jump = CMP(SLJIT_C_NOT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, top)); +OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit)); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1); +sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); + +/* Allocation failed. */ +JUMPHERE(jump); +/* We break the return address cache here, but this is a really rare case. */ +OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_JIT_STACKLIMIT); +JUMPTO(SLJIT_JUMP, common->quit_label); + +/* Call limit reached. */ +set_jumps(common->calllimit, LABEL()); +OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_MATCHLIMIT); +JUMPTO(SLJIT_JUMP, common->quit_label); + +if (common->revertframes != NULL) + { + set_jumps(common->revertframes, LABEL()); + do_revertframes(common); + } +if (common->wordboundary != NULL) + { + set_jumps(common->wordboundary, LABEL()); + check_wordboundary(common); + } +if (common->anynewline != NULL) + { + set_jumps(common->anynewline, LABEL()); + check_anynewline(common); + } +if (common->hspace != NULL) + { + set_jumps(common->hspace, LABEL()); + check_hspace(common); + } +if (common->vspace != NULL) + { + set_jumps(common->vspace, LABEL()); + check_vspace(common); + } +if (common->casefulcmp != NULL) + { + set_jumps(common->casefulcmp, LABEL()); + do_casefulcmp(common); + } +if (common->caselesscmp != NULL) + { + set_jumps(common->caselesscmp, LABEL()); + do_caselesscmp(common); + } +if (common->reset_match != NULL) + { + set_jumps(common->reset_match, LABEL()); + do_reset_match(common, (re->top_bracket + 1) * 2); + CMPTO(SLJIT_C_GREATER, STR_PTR, 0, TMP1, 0, continue_match_label); + OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0); + JUMPTO(SLJIT_JUMP, reset_match_label); + } +#ifdef SUPPORT_UTF +#ifndef COMPILE_PCRE32 +if (common->utfreadchar != NULL) + { + set_jumps(common->utfreadchar, LABEL()); + do_utfreadchar(common); + } +#endif /* !COMPILE_PCRE32 */ +#ifdef COMPILE_PCRE8 +if (common->utfreadtype8 != NULL) + { + set_jumps(common->utfreadtype8, LABEL()); + do_utfreadtype8(common); + } +#endif /* COMPILE_PCRE8 */ +#endif /* SUPPORT_UTF */ +#ifdef SUPPORT_UCP +if (common->getucd != NULL) + { + set_jumps(common->getucd, LABEL()); + do_getucd(common); + } +#endif + +SLJIT_FREE(common->optimized_cbracket); +SLJIT_FREE(common->private_data_ptrs); +if (common->has_then) + SLJIT_FREE(common->then_offsets); + +executable_func = sljit_generate_code(compiler); +executable_size = sljit_get_generated_code_size(compiler); +sljit_free_compiler(compiler); +if (executable_func == NULL) + return; + +/* Reuse the function descriptor if possible. */ +if ((extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 && extra->executable_jit != NULL) + functions = (executable_functions *)extra->executable_jit; +else + { + /* Note: If your memory-checker has flagged the allocation below as a + * memory leak, it is probably because you either forgot to call + * pcre_free_study() (or pcre16_free_study()) on the pcre_extra (or + * pcre16_extra) object, or you called said function after having + * cleared the PCRE_EXTRA_EXECUTABLE_JIT bit from the "flags" field + * of the object. (The function will only free the JIT data if the + * bit remains set, as the bit indicates that the pointer to the data + * is valid.) + */ + functions = SLJIT_MALLOC(sizeof(executable_functions)); + if (functions == NULL) + { + /* This case is highly unlikely since we just recently + freed a lot of memory. Although not impossible. */ + sljit_free_code(executable_func); + return; + } + memset(functions, 0, sizeof(executable_functions)); + functions->top_bracket = (re->top_bracket + 1) * 2; + functions->limit_match = (re->flags & PCRE_MLSET) != 0 ? re->limit_match : 0; + extra->executable_jit = functions; + extra->flags |= PCRE_EXTRA_EXECUTABLE_JIT; + } + +functions->executable_funcs[mode] = executable_func; +functions->executable_sizes[mode] = executable_size; +} + +static int jit_machine_stack_exec(jit_arguments *arguments, void* executable_func) +{ +union { + void* executable_func; + jit_function call_executable_func; +} convert_executable_func; +pcre_uint8 local_space[MACHINE_STACK_SIZE]; +struct sljit_stack local_stack; + +local_stack.top = (sljit_sw)&local_space; +local_stack.base = local_stack.top; +local_stack.limit = local_stack.base + MACHINE_STACK_SIZE; +local_stack.max_limit = local_stack.limit; +arguments->stack = &local_stack; +convert_executable_func.executable_func = executable_func; +return convert_executable_func.call_executable_func(arguments); +} + +int +PRIV(jit_exec)(const PUBL(extra) *extra_data, const pcre_uchar *subject, + int length, int start_offset, int options, int *offsets, int offset_count) +{ +executable_functions *functions = (executable_functions *)extra_data->executable_jit; +union { + void* executable_func; + jit_function call_executable_func; +} convert_executable_func; +jit_arguments arguments; +int max_offset_count; +int retval; +int mode = JIT_COMPILE; + +if ((options & PCRE_PARTIAL_HARD) != 0) + mode = JIT_PARTIAL_HARD_COMPILE; +else if ((options & PCRE_PARTIAL_SOFT) != 0) + mode = JIT_PARTIAL_SOFT_COMPILE; + +if (functions->executable_funcs[mode] == NULL) + return PCRE_ERROR_JIT_BADOPTION; + +/* Sanity checks should be handled by pcre_exec. */ +arguments.str = subject + start_offset; +arguments.begin = subject; +arguments.end = subject + length; +arguments.mark_ptr = NULL; +/* JIT decreases this value less frequently than the interpreter. */ +arguments.limit_match = ((extra_data->flags & PCRE_EXTRA_MATCH_LIMIT) == 0) ? MATCH_LIMIT : (pcre_uint32)(extra_data->match_limit); +if (functions->limit_match != 0 && functions->limit_match < arguments.limit_match) + arguments.limit_match = functions->limit_match; +arguments.notbol = (options & PCRE_NOTBOL) != 0; +arguments.noteol = (options & PCRE_NOTEOL) != 0; +arguments.notempty = (options & PCRE_NOTEMPTY) != 0; +arguments.notempty_atstart = (options & PCRE_NOTEMPTY_ATSTART) != 0; +arguments.offsets = offsets; +arguments.callout_data = (extra_data->flags & PCRE_EXTRA_CALLOUT_DATA) != 0 ? extra_data->callout_data : NULL; +arguments.real_offset_count = offset_count; + +/* pcre_exec() rounds offset_count to a multiple of 3, and then uses only 2/3 of +the output vector for storing captured strings, with the remainder used as +workspace. We don't need the workspace here. For compatibility, we limit the +number of captured strings in the same way as pcre_exec(), so that the user +gets the same result with and without JIT. */ + +if (offset_count != 2) + offset_count = ((offset_count - (offset_count % 3)) * 2) / 3; +max_offset_count = functions->top_bracket; +if (offset_count > max_offset_count) + offset_count = max_offset_count; +arguments.offset_count = offset_count; + +if (functions->callback) + arguments.stack = (struct sljit_stack *)functions->callback(functions->userdata); +else + arguments.stack = (struct sljit_stack *)functions->userdata; + +if (arguments.stack == NULL) + retval = jit_machine_stack_exec(&arguments, functions->executable_funcs[mode]); +else + { + convert_executable_func.executable_func = functions->executable_funcs[mode]; + retval = convert_executable_func.call_executable_func(&arguments); + } + +if (retval * 2 > offset_count) + retval = 0; +if ((extra_data->flags & PCRE_EXTRA_MARK) != 0) + *(extra_data->mark) = arguments.mark_ptr; + +return retval; +} + +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_jit_exec(const pcre *argument_re, const pcre_extra *extra_data, + PCRE_SPTR subject, int length, int start_offset, int options, + int *offsets, int offset_count, pcre_jit_stack *stack) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_jit_exec(const pcre16 *argument_re, const pcre16_extra *extra_data, + PCRE_SPTR16 subject, int length, int start_offset, int options, + int *offsets, int offset_count, pcre16_jit_stack *stack) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_jit_exec(const pcre32 *argument_re, const pcre32_extra *extra_data, + PCRE_SPTR32 subject, int length, int start_offset, int options, + int *offsets, int offset_count, pcre32_jit_stack *stack) +#endif +{ +pcre_uchar *subject_ptr = (pcre_uchar *)subject; +executable_functions *functions = (executable_functions *)extra_data->executable_jit; +union { + void* executable_func; + jit_function call_executable_func; +} convert_executable_func; +jit_arguments arguments; +int max_offset_count; +int retval; +int mode = JIT_COMPILE; + +SLJIT_UNUSED_ARG(argument_re); + +/* Plausibility checks */ +if ((options & ~PUBLIC_JIT_EXEC_OPTIONS) != 0) return PCRE_ERROR_JIT_BADOPTION; + +if ((options & PCRE_PARTIAL_HARD) != 0) + mode = JIT_PARTIAL_HARD_COMPILE; +else if ((options & PCRE_PARTIAL_SOFT) != 0) + mode = JIT_PARTIAL_SOFT_COMPILE; + +if (functions->executable_funcs[mode] == NULL) + return PCRE_ERROR_JIT_BADOPTION; + +/* Sanity checks should be handled by pcre_exec. */ +arguments.stack = (struct sljit_stack *)stack; +arguments.str = subject_ptr + start_offset; +arguments.begin = subject_ptr; +arguments.end = subject_ptr + length; +arguments.mark_ptr = NULL; +/* JIT decreases this value less frequently than the interpreter. */ +arguments.limit_match = ((extra_data->flags & PCRE_EXTRA_MATCH_LIMIT) == 0) ? MATCH_LIMIT : (pcre_uint32)(extra_data->match_limit); +if (functions->limit_match != 0 && functions->limit_match < arguments.limit_match) + arguments.limit_match = functions->limit_match; +arguments.notbol = (options & PCRE_NOTBOL) != 0; +arguments.noteol = (options & PCRE_NOTEOL) != 0; +arguments.notempty = (options & PCRE_NOTEMPTY) != 0; +arguments.notempty_atstart = (options & PCRE_NOTEMPTY_ATSTART) != 0; +arguments.offsets = offsets; +arguments.callout_data = (extra_data->flags & PCRE_EXTRA_CALLOUT_DATA) != 0 ? extra_data->callout_data : NULL; +arguments.real_offset_count = offset_count; + +/* pcre_exec() rounds offset_count to a multiple of 3, and then uses only 2/3 of +the output vector for storing captured strings, with the remainder used as +workspace. We don't need the workspace here. For compatibility, we limit the +number of captured strings in the same way as pcre_exec(), so that the user +gets the same result with and without JIT. */ + +if (offset_count != 2) + offset_count = ((offset_count - (offset_count % 3)) * 2) / 3; +max_offset_count = functions->top_bracket; +if (offset_count > max_offset_count) + offset_count = max_offset_count; +arguments.offset_count = offset_count; + +convert_executable_func.executable_func = functions->executable_funcs[mode]; +retval = convert_executable_func.call_executable_func(&arguments); + +if (retval * 2 > offset_count) + retval = 0; +if ((extra_data->flags & PCRE_EXTRA_MARK) != 0) + *(extra_data->mark) = arguments.mark_ptr; + +return retval; +} + +void +PRIV(jit_free)(void *executable_funcs) +{ +int i; +executable_functions *functions = (executable_functions *)executable_funcs; +for (i = 0; i < JIT_NUMBER_OF_COMPILE_MODES; i++) + { + if (functions->executable_funcs[i] != NULL) + sljit_free_code(functions->executable_funcs[i]); + } +SLJIT_FREE(functions); +} + +int +PRIV(jit_get_size)(void *executable_funcs) +{ +int i; +sljit_uw size = 0; +sljit_uw *executable_sizes = ((executable_functions *)executable_funcs)->executable_sizes; +for (i = 0; i < JIT_NUMBER_OF_COMPILE_MODES; i++) + size += executable_sizes[i]; +return (int)size; +} + +const char* +PRIV(jit_get_target)(void) +{ +return sljit_get_platform_name(); +} + +#if defined COMPILE_PCRE8 +PCRE_EXP_DECL pcre_jit_stack * +pcre_jit_stack_alloc(int startsize, int maxsize) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DECL pcre16_jit_stack * +pcre16_jit_stack_alloc(int startsize, int maxsize) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DECL pcre32_jit_stack * +pcre32_jit_stack_alloc(int startsize, int maxsize) +#endif +{ +if (startsize < 1 || maxsize < 1) + return NULL; +if (startsize > maxsize) + startsize = maxsize; +startsize = (startsize + STACK_GROWTH_RATE - 1) & ~(STACK_GROWTH_RATE - 1); +maxsize = (maxsize + STACK_GROWTH_RATE - 1) & ~(STACK_GROWTH_RATE - 1); +return (PUBL(jit_stack)*)sljit_allocate_stack(startsize, maxsize); +} + +#if defined COMPILE_PCRE8 +PCRE_EXP_DECL void +pcre_jit_stack_free(pcre_jit_stack *stack) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DECL void +pcre16_jit_stack_free(pcre16_jit_stack *stack) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DECL void +pcre32_jit_stack_free(pcre32_jit_stack *stack) +#endif +{ +sljit_free_stack((struct sljit_stack *)stack); +} + +#if defined COMPILE_PCRE8 +PCRE_EXP_DECL void +pcre_assign_jit_stack(pcre_extra *extra, pcre_jit_callback callback, void *userdata) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DECL void +pcre16_assign_jit_stack(pcre16_extra *extra, pcre16_jit_callback callback, void *userdata) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DECL void +pcre32_assign_jit_stack(pcre32_extra *extra, pcre32_jit_callback callback, void *userdata) +#endif +{ +executable_functions *functions; +if (extra != NULL && + (extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 && + extra->executable_jit != NULL) + { + functions = (executable_functions *)extra->executable_jit; + functions->callback = callback; + functions->userdata = userdata; + } +} + +#else /* SUPPORT_JIT */ + +/* These are dummy functions to avoid linking errors when JIT support is not +being compiled. */ + +#if defined COMPILE_PCRE8 +PCRE_EXP_DECL pcre_jit_stack * +pcre_jit_stack_alloc(int startsize, int maxsize) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DECL pcre16_jit_stack * +pcre16_jit_stack_alloc(int startsize, int maxsize) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DECL pcre32_jit_stack * +pcre32_jit_stack_alloc(int startsize, int maxsize) +#endif +{ +(void)startsize; +(void)maxsize; +return NULL; +} + +#if defined COMPILE_PCRE8 +PCRE_EXP_DECL void +pcre_jit_stack_free(pcre_jit_stack *stack) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DECL void +pcre16_jit_stack_free(pcre16_jit_stack *stack) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DECL void +pcre32_jit_stack_free(pcre32_jit_stack *stack) +#endif +{ +(void)stack; +} + +#if defined COMPILE_PCRE8 +PCRE_EXP_DECL void +pcre_assign_jit_stack(pcre_extra *extra, pcre_jit_callback callback, void *userdata) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DECL void +pcre16_assign_jit_stack(pcre16_extra *extra, pcre16_jit_callback callback, void *userdata) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DECL void +pcre32_assign_jit_stack(pcre32_extra *extra, pcre32_jit_callback callback, void *userdata) +#endif +{ +(void)extra; +(void)callback; +(void)userdata; +} + +#endif + +/* End of pcre_jit_compile.c */ diff --git a/erts/emulator/pcre/pcre_latin_1_table.c b/erts/emulator/pcre/pcre_latin_1_table.c index 69d888026b..599540723b 100644 --- a/erts/emulator/pcre/pcre_latin_1_table.c +++ b/erts/emulator/pcre/pcre_latin_1_table.c @@ -2,7 +2,7 @@ * Perl-Compatible Regular Expressions * *************************************************/ -/* This file was automatically written by the make_latin1_table auxiliary +/* This file was automatically written by the dftables auxiliary program. It contains character tables that are used when no external tables are passed to PCRE by the application that calls it. The tables are used only for characters whose code values are less than 256. @@ -13,14 +13,14 @@ library and dead code stripping is activated. This leads to link errors. Pulling in the header ensures that the array gets flagged as "someone outside this compilation unit might reference this" and so it will always be supplied to the linker. */ - +/* %ExternalCopyright% */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre_internal.h" -const unsigned char _erts_pcre_default_tables[] = { +const pcre_uint8 PRIV(default_tables)[] = { /* This table is a lower casing table. */ diff --git a/erts/emulator/pcre/pcre_make_latin1_default.c b/erts/emulator/pcre/pcre_make_latin1_default.c deleted file mode 100644 index b8a8062764..0000000000 --- a/erts/emulator/pcre/pcre_make_latin1_default.c +++ /dev/null @@ -1,367 +0,0 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ -/* This is a "hacked" version of pcre_maketables that - * will generate an acceptable character table for any - * iso-latin-1 language when running in 8-bit mode. - */ - - -/* PCRE is a library of functions to support regular expressions whose syntax -and semantics are as close as possible to those of the Perl 5 language. - - Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge - ------------------------------------------------------------------------------ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the University of Cambridge nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------ -*/ - -/* %ExternalCopyright% */ - -/* This module contains the external function pcre_maketables(), which builds -character tables for PCRE in the current locale. The file is compiled on its -own as part of the PCRE library. However, it is also included in the -compilation of dftables.c, in which case the macro DFTABLES is defined. */ - - -#ifndef DFTABLES -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif -# include "pcre_internal.h" -#endif - - -/************************************************* -* Create PCRE character tables * -*************************************************/ - -/* This function builds a set of character tables for use by PCRE and returns -a pointer to them. They are build using the ctype functions, and consequently -their contents will depend upon the current locale setting. When compiled as -part of the library, the store is obtained via pcre_malloc(), but when compiled -inside dftables, use malloc(). - -Arguments: none -Returns: pointer to the contiguous block of data -*/ - -typedef struct { - int is_alpha,is_upper,is_lower,is_alnum,is_space,is_xdigit,is_graph,is_punct,is_cntrl; - int upcase; - int lowcase; -} HiCharProp; - -static HiCharProp hicharprop[] = { - {0,0,0,0,0,0,1,1,0, 0,0}, /* 160 NO-BREAK SPACE */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 161 ¡ INVERTED EXCLAMATION MARK */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 162 ¢ CENT SIGN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 163 £ POUND SIGN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 164 ¤ CURRENCY SIGN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 165 ¥ YEN SIGN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 166 ¦ BROKEN BAR */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 167 § SECTION SIGN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 168 ¨ DIAERESIS */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 169 © COPYRIGHT SIGN */ - {1,0,0,1,0,0,1,0,0, 0,0}, /* 170 ª FEMININE ORDINAL INDICATOR */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 171 « LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 172 ¬ NOT SIGN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 173 ­ SOFT HYPHEN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 174 ® REGISTERED SIGN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 175 ¯ MACRON */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 176 ° DEGREE SIGN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 177 ± PLUS-MINUS SIGN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 178 ² SUPERSCRIPT TWO */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 179 ³ SUPERSCRIPT THREE */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 180 ´ ACUTE ACCENT */ - {1,0,1,1,0,0,1,0,0, 0,0}, /* 181 µ MICRO SIGN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 182 ¶ PILCROW SIGN */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 183 · MIDDLE DOT */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 184 ¸ CEDILLA */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 185 ¹ SUPERSCRIPT ONE */ - {1,0,0,1,0,0,1,0,0, 0,0}, /* 186 º MASCULINE ORDINAL INDICATOR */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 187 » RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 188 ¼ VULGAR FRACTION ONE QUARTER */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 189 ½ VULGAR FRACTION ONE HALF */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 190 ¾ VULGAR FRACTION THREE QUARTERS */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 191 ¿ INVERTED QUESTION MARK */ - {1,1,0,1,0,0,1,0,0, 0,224}, /* 192 À LATIN CAPITAL LETTER A WITH GRAVE */ - {1,1,0,1,0,0,1,0,0, 0,225}, /* 193 Á LATIN CAPITAL LETTER A WITH ACUTE */ - {1,1,0,1,0,0,1,0,0, 0,226}, /* 194 Â LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ - {1,1,0,1,0,0,1,0,0, 0,227}, /* 195 Ã LATIN CAPITAL LETTER A WITH TILDE */ - {1,1,0,1,0,0,1,0,0, 0,228}, /* 196 Ä LATIN CAPITAL LETTER A WITH DIAERESIS */ - {1,1,0,1,0,0,1,0,0, 0,229}, /* 197 Å LATIN CAPITAL LETTER A WITH RING ABOVE */ - {1,1,0,1,0,0,1,0,0, 0,230}, /* 198 Æ LATIN CAPITAL LETTER AE */ - {1,1,0,1,0,0,1,0,0, 0,231}, /* 199 Ç LATIN CAPITAL LETTER C WITH CEDILLA */ - {1,1,0,1,0,0,1,0,0, 0,232}, /* 200 È LATIN CAPITAL LETTER E WITH GRAVE */ - {1,1,0,1,0,0,1,0,0, 0,233}, /* 201 É LATIN CAPITAL LETTER E WITH ACUTE */ - {1,1,0,1,0,0,1,0,0, 0,234}, /* 202 Ê LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ - {1,1,0,1,0,0,1,0,0, 0,235}, /* 203 Ë LATIN CAPITAL LETTER E WITH DIAERESIS */ - {1,1,0,1,0,0,1,0,0, 0,236}, /* 204 Ì LATIN CAPITAL LETTER I WITH GRAVE */ - {1,1,0,1,0,0,1,0,0, 0,237}, /* 205 Í LATIN CAPITAL LETTER I WITH ACUTE */ - {1,1,0,1,0,0,1,0,0, 0,238}, /* 206 Î LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ - {1,1,0,1,0,0,1,0,0, 0,239}, /* 207 Ï LATIN CAPITAL LETTER I WITH DIAERESIS */ - {1,1,0,1,0,0,1,0,0, 0,240}, /* 208 Ð LATIN CAPITAL LETTER ETH */ - {1,1,0,1,0,0,1,0,0, 0,241}, /* 209 Ñ LATIN CAPITAL LETTER N WITH TILDE */ - {1,1,0,1,0,0,1,0,0, 0,242}, /* 210 Ò LATIN CAPITAL LETTER O WITH GRAVE */ - {1,1,0,1,0,0,1,0,0, 0,243}, /* 211 Ó LATIN CAPITAL LETTER O WITH ACUTE */ - {1,1,0,1,0,0,1,0,0, 0,244}, /* 212 Ô LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ - {1,1,0,1,0,0,1,0,0, 0,245}, /* 213 Õ LATIN CAPITAL LETTER O WITH TILDE */ - {1,1,0,1,0,0,1,0,0, 0,246}, /* 214 Ö LATIN CAPITAL LETTER O WITH DIAERESIS */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 215 × MULTIPLICATION SIGN */ - {1,1,0,1,0,0,1,0,0, 0,248}, /* 216 Ø LATIN CAPITAL LETTER O WITH STROKE */ - {1,1,0,1,0,0,1,0,0, 0,249}, /* 217 Ù LATIN CAPITAL LETTER U WITH GRAVE */ - {1,1,0,1,0,0,1,0,0, 0,250}, /* 218 Ú LATIN CAPITAL LETTER U WITH ACUTE */ - {1,1,0,1,0,0,1,0,0, 0,251}, /* 219 Û LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ - {1,1,0,1,0,0,1,0,0, 0,252}, /* 220 Ü LATIN CAPITAL LETTER U WITH DIAERESIS */ - {1,1,0,1,0,0,1,0,0, 0,253}, /* 221 Ý LATIN CAPITAL LETTER Y WITH ACUTE */ - {1,1,0,1,0,0,1,0,0, 0,254}, /* 222 Þ LATIN CAPITAL LETTER THORN */ - {1,0,1,1,0,0,1,0,0, 223,0}, /* 223 ß LATIN SMALL LETTER SHARP S Ouch! */ - {1,0,1,1,0,0,1,0,0, 192,0}, /* 224 à LATIN SMALL LETTER A WITH GRAVE */ - {1,0,1,1,0,0,1,0,0, 193,0}, /* 225 á LATIN SMALL LETTER A WITH ACUTE */ - {1,0,1,1,0,0,1,0,0, 194,0}, /* 226 â LATIN SMALL LETTER A WITH CIRCUMFLEX */ - {1,0,1,1,0,0,1,0,0, 195,0}, /* 227 ã LATIN SMALL LETTER A WITH TILDE */ - {1,0,1,1,0,0,1,0,0, 196,0}, /* 228 ä LATIN SMALL LETTER A WITH DIAERESIS */ - {1,0,1,1,0,0,1,0,0, 197,0}, /* 229 å LATIN SMALL LETTER A WITH RING ABOVE */ - {1,0,1,1,0,0,1,0,0, 198,0}, /* 230 æ LATIN SMALL LETTER AE */ - {1,0,1,1,0,0,1,0,0, 199,0}, /* 231 ç LATIN SMALL LETTER C WITH CEDILLA */ - {1,0,1,1,0,0,1,0,0, 200,0}, /* 232 è LATIN SMALL LETTER E WITH GRAVE */ - {1,0,1,1,0,0,1,0,0, 201,0}, /* 233 é LATIN SMALL LETTER E WITH ACUTE */ - {1,0,1,1,0,0,1,0,0, 202,0}, /* 234 ê LATIN SMALL LETTER E WITH CIRCUMFLEX */ - {1,0,1,1,0,0,1,0,0, 203,0}, /* 235 ë LATIN SMALL LETTER E WITH DIAERESIS */ - {1,0,1,1,0,0,1,0,0, 204,0}, /* 236 ì LATIN SMALL LETTER I WITH GRAVE */ - {1,0,1,1,0,0,1,0,0, 205,0}, /* 237 í LATIN SMALL LETTER I WITH ACUTE */ - {1,0,1,1,0,0,1,0,0, 206,0}, /* 238 î LATIN SMALL LETTER I WITH CIRCUMFLEX */ - {1,0,1,1,0,0,1,0,0, 207,0}, /* 239 ï LATIN SMALL LETTER I WITH DIAERESIS */ - {1,0,1,1,0,0,1,0,0, 208,0}, /* 240 ð LATIN SMALL LETTER ETH */ - {1,0,1,1,0,0,1,0,0, 209,0}, /* 241 ñ LATIN SMALL LETTER N WITH TILDE */ - {1,0,1,1,0,0,1,0,0, 210,0}, /* 242 ò LATIN SMALL LETTER O WITH GRAVE */ - {1,0,1,1,0,0,1,0,0, 211,0}, /* 243 ó LATIN SMALL LETTER O WITH ACUTE */ - {1,0,1,1,0,0,1,0,0, 212,0}, /* 244 ô LATIN SMALL LETTER O WITH CIRCUMFLEX */ - {1,0,1,1,0,0,1,0,0, 213,0}, /* 245 õ LATIN SMALL LETTER O WITH TILDE */ - {1,0,1,1,0,0,1,0,0, 214,0}, /* 246 ö LATIN SMALL LETTER O WITH DIAERESIS */ - {0,0,0,0,0,0,1,1,0, 0,0}, /* 247 ÷ DIVISION SIGN */ - {1,0,1,1,0,0,1,0,0, 216,0}, /* 248 ø LATIN SMALL LETTER O WITH STROKE */ - {1,0,1,1,0,0,1,0,0, 217,0}, /* 249 ù LATIN SMALL LETTER U WITH GRAVE */ - {1,0,1,1,0,0,1,0,0, 218,0}, /* 250 ú LATIN SMALL LETTER U WITH ACUTE */ - {1,0,1,1,0,0,1,0,0, 219,0}, /* 251 û LATIN SMALL LETTER U WITH CIRCUMFLEX */ - {1,0,1,1,0,0,1,0,0, 220,0}, /* 252 ü LATIN SMALL LETTER U WITH DIAERESIS */ - {1,0,1,1,0,0,1,0,0, 221,0}, /* 253 ý LATIN SMALL LETTER Y WITH ACUTE */ - {1,0,1,1,0,0,1,0,0, 222,0}, /* 254 þ LATIN SMALL LETTER THORN */ - {1,0,1,1,0,0,1,0,0, 255,0}}; /* 255 ÿ LATIN SMALL LETTER Y WITH DIAERESIS */ - - -static int my_tolower(int x) { - if (x < 128) - return tolower(x); - else if (x < 160) - return x; - else if (hicharprop[x - 160].lowcase == 0) - return x; - else - return hicharprop[x - 160].lowcase; -} - -static int my_toupper(int x) { - if (x < 128) - return toupper(x); - else if (x < 160) - return x; - else if (hicharprop[x - 160].upcase == 0) - return x; - else - return hicharprop[x - 160].upcase; -} - -static int my_islower(int x) { - if (x < 128) - return islower(x); - else if (x < 160) - return 0; - else - return hicharprop[x - 160].is_lower; -} - -static int my_isupper(int x) { - if (x < 128) - return isupper(x); - else if (x < 160) - return 0; - else - return hicharprop[x - 160].is_upper; -} - -static int my_isdigit(int x) { - if (x < 128) - return isdigit(x); - else - return 0; -} - -static int my_isalpha(int x) { - if (x < 128) - return isalpha(x); - else if (x < 160) - return 0; - else - return hicharprop[x - 160].is_alpha; -} - -static int my_isalnum(int x) { - if (x < 128) - return isalnum(x); - else if (x < 160) - return 0; - else - return hicharprop[x - 160].is_alnum; -} - -static int my_isspace(int x) { - if (x < 128) - return isspace(x); - else if (x < 160) - return 0; - else - return hicharprop[x - 160].is_space; -} - -static int my_isxdigit(int x) { - if (x < 128) - return isxdigit(x); - else if (x < 160) - return 0; - else - return hicharprop[x - 160].is_xdigit; -} -static int my_isgraph(int x) { - if (x < 128) - return isgraph(x); - else if (x < 160) - return 0; - else - return hicharprop[x - 160].is_graph; -} -static int my_isprint(int x) { - if (x < 128) - return isprint(x); - else if (x < 160) - return 0; - else - return hicharprop[x - 160].is_graph | hicharprop[x - 160].is_space ; -} - -static int my_ispunct(int x) { - if (x < 128) - return ispunct(x); - else if (x < 160) - return 0; - else - return hicharprop[x - 160].is_punct; -} - - -static int my_iscntrl(int x) { - if (x < 128) - return iscntrl(x); - else if (x < 160) - return 1; - else - return hicharprop[x - 160].is_cntrl; -} -const unsigned char * -pcre_make_latin1_tables(void) -{ -unsigned char *yield, *p; -int i; - -yield = (unsigned char*)malloc(tables_length); - -if (yield == NULL) return NULL; -p = yield; - -/* First comes the lower casing table */ - -for (i = 0; i < 256; i++) *p++ = my_tolower(i); - -/* Next the case-flipping table */ - -for (i = 0; i < 256; i++) *p++ = my_islower(i)? my_toupper(i) : my_tolower(i); - -/* Then the character class tables. Don't try to be clever and save effort on -exclusive ones - in some locales things may be different. Note that the table -for "space" includes everything "isspace" gives, including VT in the default -locale. This makes it work for the POSIX class [:space:]. Note also that it is -possible for a character to be alnum or alpha without being lower or upper, -such as "male and female ordinals" (\xAA and \xBA) in the fr_FR locale (at -least under Debian Linux's locales as of 12/2005). So we must test for alnum -specially. */ - -memset(p, 0, cbit_length); -for (i = 0; i < 256; i++) - { - if (my_isdigit(i)) p[cbit_digit + i/8] |= 1 << (i&7); - if (my_isupper(i)) p[cbit_upper + i/8] |= 1 << (i&7); - if (my_islower(i)) p[cbit_lower + i/8] |= 1 << (i&7); - if (my_isalnum(i)) p[cbit_word + i/8] |= 1 << (i&7); - if (i == '_') p[cbit_word + i/8] |= 1 << (i&7); - if (my_isspace(i)) p[cbit_space + i/8] |= 1 << (i&7); - if (my_isxdigit(i))p[cbit_xdigit + i/8] |= 1 << (i&7); - if (my_isgraph(i)) p[cbit_graph + i/8] |= 1 << (i&7); - if (my_isprint(i)) p[cbit_print + i/8] |= 1 << (i&7); - if (my_ispunct(i)) p[cbit_punct + i/8] |= 1 << (i&7); - if (my_iscntrl(i)) p[cbit_cntrl + i/8] |= 1 << (i&7); - } -p += cbit_length; - -/* Finally, the character type table. In this, we exclude VT from the white -space chars, because Perl doesn't recognize it as such for \s and for comments -within regexes. */ - -for (i = 0; i < 256; i++) - { - int x = 0; - if (i != 0x0b && my_isspace(i)) x += ctype_space; - if (my_isalpha(i)) x += ctype_letter; - if (my_isdigit(i)) x += ctype_digit; - if (my_isxdigit(i)) x += ctype_xdigit; - if (my_isalnum(i) || i == '_') x += ctype_word; - - /* Note: strchr includes the terminating zero in the characters it considers. - In this instance, that is ok because we want binary zero to be flagged as a - meta-character, which in this sense is any character that terminates a run - of data characters. */ - - if (strchr("\\*+?{^.$|()[", i) != 0) x += ctype_meta; - *p++ = x; - } - -return yield; -} - -/* End of pcre_maketables.c */ diff --git a/erts/emulator/pcre/pcre_maketables.c b/erts/emulator/pcre/pcre_maketables.c index a695bb26ad..7c4d58d558 100644 --- a/erts/emulator/pcre/pcre_maketables.c +++ b/erts/emulator/pcre/pcre_maketables.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2012 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. */ -/* This module contains the external function erts_pcre_maketables(), which builds +/* This module contains the external function pcre_maketables(), which builds character tables for PCRE in the current locale. The file is compiled on its own as part of the PCRE library. However, it is also included in the compilation of dftables.c, in which case the macro DFTABLES is defined. */ @@ -60,21 +60,29 @@ compilation of dftables.c, in which case the macro DFTABLES is defined. */ /* This function builds a set of character tables for use by PCRE and returns a pointer to them. They are build using the ctype functions, and consequently their contents will depend upon the current locale setting. When compiled as -part of the library, the store is obtained via erts_pcre_malloc(), but when compiled -inside dftables, use malloc(). +part of the library, the store is obtained via PUBL(malloc)(), but when +compiled inside dftables, use malloc(). Arguments: none Returns: pointer to the contiguous block of data */ +#if defined COMPILE_PCRE8 const unsigned char * -erts_pcre_maketables(void) +pcre_maketables(void) +#elif defined COMPILE_PCRE16 +const unsigned char * +pcre16_maketables(void) +#elif defined COMPILE_PCRE32 +const unsigned char * +pcre32_maketables(void) +#endif { unsigned char *yield, *p; int i; #ifndef DFTABLES -yield = (unsigned char*)(erts_pcre_malloc)(tables_length); +yield = (unsigned char*)(PUBL(malloc))(tables_length); #else yield = (unsigned char*)malloc(tables_length); #endif @@ -123,7 +131,7 @@ within regexes. */ for (i = 0; i < 256; i++) { int x = 0; - if (i != 0x0b && isspace(i)) x += ctype_space; + if (i != CHAR_VT && isspace(i)) x += ctype_space; if (isalpha(i)) x += ctype_letter; if (isdigit(i)) x += ctype_digit; if (isxdigit(i)) x += ctype_xdigit; diff --git a/erts/emulator/pcre/pcre_newline.c b/erts/emulator/pcre/pcre_newline.c index 7dbda88aff..02394078d5 100644 --- a/erts/emulator/pcre/pcre_newline.c +++ b/erts/emulator/pcre/pcre_newline.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2012 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -68,23 +68,33 @@ Arguments: type the newline type endptr pointer to the end of the string lenptr where to return the length - utf8 TRUE if in utf8 mode + utf TRUE if in utf mode Returns: TRUE or FALSE */ BOOL -_erts_pcre_is_newline(const uschar *ptr, int type, const uschar *endptr, - int *lenptr, BOOL utf8) +PRIV(is_newline)(PCRE_PUCHAR ptr, int type, PCRE_PUCHAR endptr, int *lenptr, + BOOL utf) { -int c; -if (utf8) { GETCHAR(c, ptr); } else c = *ptr; +pcre_uint32 c; +(void)utf; +#ifdef SUPPORT_UTF +if (utf) + { + GETCHAR(c, ptr); + } +else +#endif /* SUPPORT_UTF */ + c = *ptr; + +/* Note that this function is called only for ANY or ANYCRLF. */ if (type == NLTYPE_ANYCRLF) switch(c) { - case 0x000a: *lenptr = 1; return TRUE; /* LF */ - case 0x000d: *lenptr = (ptr < endptr - 1 && ptr[1] == 0x0a)? 2 : 1; - return TRUE; /* CR */ + case CHAR_LF: *lenptr = 1; return TRUE; + case CHAR_CR: *lenptr = (ptr < endptr - 1 && ptr[1] == CHAR_LF)? 2 : 1; + return TRUE; default: return FALSE; } @@ -92,14 +102,29 @@ if (type == NLTYPE_ANYCRLF) switch(c) else switch(c) { - case 0x000a: /* LF */ - case 0x000b: /* VT */ - case 0x000c: *lenptr = 1; return TRUE; /* FF */ - case 0x000d: *lenptr = (ptr < endptr - 1 && ptr[1] == 0x0a)? 2 : 1; - return TRUE; /* CR */ - case 0x0085: *lenptr = utf8? 2 : 1; return TRUE; /* NEL */ +#ifdef EBCDIC + case CHAR_NEL: +#endif + case CHAR_LF: + case CHAR_VT: + case CHAR_FF: *lenptr = 1; return TRUE; + + case CHAR_CR: + *lenptr = (ptr < endptr - 1 && ptr[1] == CHAR_LF)? 2 : 1; + return TRUE; + +#ifndef EBCDIC +#ifdef COMPILE_PCRE8 + case CHAR_NEL: *lenptr = utf? 2 : 1; return TRUE; case 0x2028: /* LS */ case 0x2029: *lenptr = 3; return TRUE; /* PS */ +#else /* COMPILE_PCRE16 || COMPILE_PCRE32 */ + case CHAR_NEL: + case 0x2028: /* LS */ + case 0x2029: *lenptr = 1; return TRUE; /* PS */ +#endif /* COMPILE_PCRE8 */ +#endif /* Not EBCDIC */ + default: return FALSE; } } @@ -118,46 +143,67 @@ Arguments: type the newline type startptr pointer to the start of the string lenptr where to return the length - utf8 TRUE if in utf8 mode + utf TRUE if in utf mode Returns: TRUE or FALSE */ BOOL -_erts_pcre_was_newline(const uschar *ptr, int type, const uschar *startptr, - int *lenptr, BOOL utf8) +PRIV(was_newline)(PCRE_PUCHAR ptr, int type, PCRE_PUCHAR startptr, int *lenptr, + BOOL utf) { -int c; +pcre_uint32 c; +(void)utf; ptr--; -#ifdef SUPPORT_UTF8 -if (utf8) +#ifdef SUPPORT_UTF +if (utf) { BACKCHAR(ptr); GETCHAR(c, ptr); } -else c = *ptr; -#else /* no UTF-8 support */ -c = *ptr; -#endif /* SUPPORT_UTF8 */ +else +#endif /* SUPPORT_UTF */ + c = *ptr; + +/* Note that this function is called only for ANY or ANYCRLF. */ if (type == NLTYPE_ANYCRLF) switch(c) { - case 0x000a: *lenptr = (ptr > startptr && ptr[-1] == 0x0d)? 2 : 1; - return TRUE; /* LF */ - case 0x000d: *lenptr = 1; return TRUE; /* CR */ + case CHAR_LF: + *lenptr = (ptr > startptr && ptr[-1] == CHAR_CR)? 2 : 1; + return TRUE; + + case CHAR_CR: *lenptr = 1; return TRUE; default: return FALSE; } +/* NLTYPE_ANY */ + else switch(c) { - case 0x000a: *lenptr = (ptr > startptr && ptr[-1] == 0x0d)? 2 : 1; - return TRUE; /* LF */ - case 0x000b: /* VT */ - case 0x000c: /* FF */ - case 0x000d: *lenptr = 1; return TRUE; /* CR */ - case 0x0085: *lenptr = utf8? 2 : 1; return TRUE; /* NEL */ - case 0x2028: /* LS */ - case 0x2029: *lenptr = 3; return TRUE; /* PS */ + case CHAR_LF: + *lenptr = (ptr > startptr && ptr[-1] == CHAR_CR)? 2 : 1; + return TRUE; + +#ifdef EBCDIC + case CHAR_NEL: +#endif + case CHAR_VT: + case CHAR_FF: + case CHAR_CR: *lenptr = 1; return TRUE; + +#ifndef EBCDIC +#ifdef COMPILE_PCRE8 + case CHAR_NEL: *lenptr = utf? 2 : 1; return TRUE; + case 0x2028: /* LS */ + case 0x2029: *lenptr = 3; return TRUE; /* PS */ +#else /* COMPILE_PCRE16 || COMPILE_PCRE32 */ + case CHAR_NEL: + case 0x2028: /* LS */ + case 0x2029: *lenptr = 1; return TRUE; /* PS */ +#endif /* COMPILE_PCRE8 */ +#endif /* NotEBCDIC */ + default: return FALSE; } } diff --git a/erts/emulator/pcre/pcre_ord2utf8.c b/erts/emulator/pcre/pcre_ord2utf8.c index dd9c934e20..a134fca635 100644 --- a/erts/emulator/pcre/pcre_ord2utf8.c +++ b/erts/emulator/pcre/pcre_ord2utf8.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2012 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -47,41 +47,50 @@ character value into a UTF8 string. */ #include "config.h" #endif -#include "pcre_internal.h" +#define COMPILE_PCRE8 +#include "pcre_internal.h" /************************************************* * Convert character value to UTF-8 * *************************************************/ -/* This function takes an integer value in the range 0 - 0x7fffffff -and encodes it as a UTF-8 character in 0 to 6 bytes. +/* This function takes an integer value in the range 0 - 0x10ffff +and encodes it as a UTF-8 character in 1 to 4 pcre_uchars. Arguments: cvalue the character value - buffer pointer to buffer for result - at least 6 bytes long + buffer pointer to buffer for result - at least 6 pcre_uchars long Returns: number of characters placed in the buffer */ +unsigned int -_erts_pcre_ord2utf8(int cvalue, uschar *buffer) +PRIV(ord2utf)(pcre_uint32 cvalue, pcre_uchar *buffer) { -#ifdef SUPPORT_UTF8 +#ifdef SUPPORT_UTF + register int i, j; -for (i = 0; i < _erts_pcre_utf8_table1_size; i++) - if (cvalue <= _erts_pcre_utf8_table1[i]) break; + +for (i = 0; i < PRIV(utf8_table1_size); i++) + if ((int)cvalue <= PRIV(utf8_table1)[i]) break; buffer += i; for (j = i; j > 0; j--) { *buffer-- = 0x80 | (cvalue & 0x3f); cvalue >>= 6; } -*buffer = _erts_pcre_utf8_table2[i] | cvalue; +*buffer = PRIV(utf8_table2)[i] | cvalue; return i + 1; + #else -return 0; /* Keep compiler happy; this function won't ever be */ -#endif /* called when SUPPORT_UTF8 is not defined. */ + +(void)(cvalue); /* Keep compiler happy; this function won't ever be */ +(void)(buffer); /* called when SUPPORT_UTF is not defined. */ +return 0; + +#endif } /* End of pcre_ord2utf8.c */ diff --git a/erts/emulator/pcre/pcre_refcount.c b/erts/emulator/pcre/pcre_refcount.c index a2077b9d52..5b2af2f47b 100644 --- a/erts/emulator/pcre/pcre_refcount.c +++ b/erts/emulator/pcre/pcre_refcount.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2012 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. */ -/* This module contains the external function erts_pcre_refcount(), which is an +/* This module contains the external function pcre_refcount(), which is an auxiliary function that can be used to maintain a reference count in a compiled pattern data block. This might be helpful in applications where the block is shared by different users. */ @@ -69,11 +69,21 @@ Returns: the (possibly updated) count value (a non-negative number), or a negative error number */ -PCRE_EXP_DEFN int -erts_pcre_refcount(pcre *argument_re, int adjust) +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_refcount(pcre *argument_re, int adjust) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_refcount(pcre16 *argument_re, int adjust) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre32_refcount(pcre32 *argument_re, int adjust) +#endif { -real_pcre *re = (real_pcre *)argument_re; +REAL_PCRE *re = (REAL_PCRE *)argument_re; if (re == NULL) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; +if ((re->flags & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; re->ref_count = (-adjust > re->ref_count)? 0 : (adjust + re->ref_count > 65535)? 65535 : re->ref_count + adjust; diff --git a/erts/emulator/pcre/pcre_string_utils.c b/erts/emulator/pcre/pcre_string_utils.c new file mode 100644 index 0000000000..274070469f --- /dev/null +++ b/erts/emulator/pcre/pcre_string_utils.c @@ -0,0 +1,211 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2013 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains internal functions for comparing and finding the length +of strings for different data item sizes. */ +/* %ExternalCopyright% */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +#ifndef COMPILE_PCRE8 + +/************************************************* +* Compare string utilities * +*************************************************/ + +/* The following two functions compares two strings. Basically a strcmp +for non 8 bit characters. + +Arguments: + str1 first string + str2 second string + +Returns: 0 if both string are equal (like strcmp), 1 otherwise +*/ + +int +PRIV(strcmp_uc_uc)(const pcre_uchar *str1, const pcre_uchar *str2) +{ +pcre_uchar c1; +pcre_uchar c2; + +while (*str1 != '\0' || *str2 != '\0') + { + c1 = *str1++; + c2 = *str2++; + if (c1 != c2) + return ((c1 > c2) << 1) - 1; + } +/* Both length and characters must be equal. */ +return 0; +} + +#ifdef COMPILE_PCRE32 + +int +PRIV(strcmp_uc_uc_utf)(const pcre_uchar *str1, const pcre_uchar *str2) +{ +pcre_uchar c1; +pcre_uchar c2; + +while (*str1 != '\0' || *str2 != '\0') + { + c1 = RAWUCHARINC(str1); + c2 = RAWUCHARINC(str2); + if (c1 != c2) + return ((c1 > c2) << 1) - 1; + } +/* Both length and characters must be equal. */ +return 0; +} + +#endif /* COMPILE_PCRE32 */ + +int +PRIV(strcmp_uc_c8)(const pcre_uchar *str1, const char *str2) +{ +const pcre_uint8 *ustr2 = (pcre_uint8 *)str2; +pcre_uchar c1; +pcre_uchar c2; + +while (*str1 != '\0' || *ustr2 != '\0') + { + c1 = *str1++; + c2 = (pcre_uchar)*ustr2++; + if (c1 != c2) + return ((c1 > c2) << 1) - 1; + } +/* Both length and characters must be equal. */ +return 0; +} + +#ifdef COMPILE_PCRE32 + +int +PRIV(strcmp_uc_c8_utf)(const pcre_uchar *str1, const char *str2) +{ +const pcre_uint8 *ustr2 = (pcre_uint8 *)str2; +pcre_uchar c1; +pcre_uchar c2; + +while (*str1 != '\0' || *ustr2 != '\0') + { + c1 = RAWUCHARINC(str1); + c2 = (pcre_uchar)*ustr2++; + if (c1 != c2) + return ((c1 > c2) << 1) - 1; + } +/* Both length and characters must be equal. */ +return 0; +} + +#endif /* COMPILE_PCRE32 */ + +/* The following two functions compares two, fixed length +strings. Basically an strncmp for non 8 bit characters. + +Arguments: + str1 first string + str2 second string + num size of the string + +Returns: 0 if both string are equal (like strcmp), 1 otherwise +*/ + +int +PRIV(strncmp_uc_uc)(const pcre_uchar *str1, const pcre_uchar *str2, unsigned int num) +{ +pcre_uchar c1; +pcre_uchar c2; + +while (num-- > 0) + { + c1 = *str1++; + c2 = *str2++; + if (c1 != c2) + return ((c1 > c2) << 1) - 1; + } +/* Both length and characters must be equal. */ +return 0; +} + +int +PRIV(strncmp_uc_c8)(const pcre_uchar *str1, const char *str2, unsigned int num) +{ +const pcre_uint8 *ustr2 = (pcre_uint8 *)str2; +pcre_uchar c1; +pcre_uchar c2; + +while (num-- > 0) + { + c1 = *str1++; + c2 = (pcre_uchar)*ustr2++; + if (c1 != c2) + return ((c1 > c2) << 1) - 1; + } +/* Both length and characters must be equal. */ +return 0; +} + +/* The following function returns with the length of +a zero terminated string. Basically an strlen for non 8 bit characters. + +Arguments: + str string + +Returns: length of the string +*/ + +unsigned int +PRIV(strlen_uc)(const pcre_uchar *str) +{ +unsigned int len = 0; +while (*str++ != 0) + len++; +return len; +} + +#endif /* !COMPILE_PCRE8 */ + +/* End of pcre_string_utils.c */ diff --git a/erts/emulator/pcre/pcre_study.c b/erts/emulator/pcre/pcre_study.c index 25bd6bde07..a85163094f 100644 --- a/erts/emulator/pcre/pcre_study.c +++ b/erts/emulator/pcre/pcre_study.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2012 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. */ -/* This module contains the external function erts_pcre_study(), along with local +/* This module contains the external function pcre_study(), along with local supporting functions. */ /* %ExternalCopyright% */ @@ -49,34 +49,644 @@ supporting functions. */ #include "pcre_internal.h" +#define SET_BIT(c) start_bits[c/8] |= (1 << (c&7)) /* Returns from set_start_bits() */ -enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE }; +enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE, SSB_UNKNOWN }; + + + +/************************************************* +* Find the minimum subject length for a group * +*************************************************/ + +/* Scan a parenthesized group and compute the minimum length of subject that +is needed to match it. This is a lower bound; it does not mean there is a +string of that length that matches. In UTF8 mode, the result is in characters +rather than bytes. + +Arguments: + code pointer to start of group (the bracket) + startcode pointer to start of the whole pattern + options the compiling options + int RECURSE depth + +Returns: the minimum length + -1 if \C in UTF-8 mode or (*ACCEPT) was encountered + -2 internal error (missing capturing bracket) + -3 internal error (opcode not listed) +*/ + +static int +find_minlength(const pcre_uchar *code, const pcre_uchar *startcode, int options, + int recurse_depth) +{ +int length = -1; +/* PCRE_UTF16 has the same value as PCRE_UTF8. */ +BOOL utf = (options & PCRE_UTF8) != 0; +BOOL had_recurse = FALSE; +register int branchlength = 0; +register pcre_uchar *cc = (pcre_uchar *)code + 1 + LINK_SIZE; + +if (*code == OP_CBRA || *code == OP_SCBRA || + *code == OP_CBRAPOS || *code == OP_SCBRAPOS) cc += IMM2_SIZE; + +/* Scan along the opcodes for this branch. If we get to the end of the +branch, check the length against that of the other branches. */ + +for (;;) + { + int d, min; + pcre_uchar *cs, *ce; + register pcre_uchar op = *cc; + + switch (op) + { + case OP_COND: + case OP_SCOND: + + /* If there is only one branch in a condition, the implied branch has zero + length, so we don't add anything. This covers the DEFINE "condition" + automatically. */ + + cs = cc + GET(cc, 1); + if (*cs != OP_ALT) + { + cc = cs + 1 + LINK_SIZE; + break; + } + + /* Otherwise we can fall through and treat it the same as any other + subpattern. */ + + case OP_CBRA: + case OP_SCBRA: + case OP_BRA: + case OP_SBRA: + case OP_CBRAPOS: + case OP_SCBRAPOS: + case OP_BRAPOS: + case OP_SBRAPOS: + case OP_ONCE: + case OP_ONCE_NC: + d = find_minlength(cc, startcode, options, recurse_depth); + if (d < 0) return d; + branchlength += d; + do cc += GET(cc, 1); while (*cc == OP_ALT); + cc += 1 + LINK_SIZE; + break; + + /* ACCEPT makes things far too complicated; we have to give up. */ + + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + return -1; + + /* Reached end of a branch; if it's a ket it is the end of a nested + call. If it's ALT it is an alternation in a nested call. If it is END it's + the end of the outer call. All can be handled by the same code. If an + ACCEPT was previously encountered, use the length that was in force at that + time, and pass back the shortest ACCEPT length. */ + + case OP_ALT: + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_KETRPOS: + case OP_END: + if (length < 0 || (!had_recurse && branchlength < length)) + length = branchlength; + if (op != OP_ALT) return length; + cc += 1 + LINK_SIZE; + branchlength = 0; + had_recurse = FALSE; + break; + + /* Skip over assertive subpatterns */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do cc += GET(cc, 1); while (*cc == OP_ALT); + /* Fall through */ + + /* Skip over things that don't match chars */ + + case OP_REVERSE: + case OP_CREF: + case OP_NCREF: + case OP_RREF: + case OP_NRREF: + case OP_DEF: + case OP_CALLOUT: + case OP_SOD: + case OP_SOM: + case OP_EOD: + case OP_EODN: + case OP_CIRC: + case OP_CIRCM: + case OP_DOLL: + case OP_DOLLM: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + cc += PRIV(OP_lengths)[*cc]; + break; + + /* Skip over a subpattern that has a {0} or {0,x} quantifier */ + + case OP_BRAZERO: + case OP_BRAMINZERO: + case OP_BRAPOSZERO: + case OP_SKIPZERO: + cc += PRIV(OP_lengths)[*cc]; + do cc += GET(cc, 1); while (*cc == OP_ALT); + cc += 1 + LINK_SIZE; + break; + + /* Handle literal characters and + repetitions */ + + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_PLUS: + case OP_PLUSI: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_POSPLUS: + case OP_POSPLUSI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + branchlength++; + cc += 2; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: + branchlength++; + cc += (cc[1] == OP_PROP || cc[1] == OP_NOTPROP)? 4 : 2; + break; + + /* Handle exact repetitions. The count is already in characters, but we + need to skip over a multibyte character in UTF8 mode. */ + + case OP_EXACT: + case OP_EXACTI: + case OP_NOTEXACT: + case OP_NOTEXACTI: + branchlength += GET2(cc,1); + cc += 2 + IMM2_SIZE; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + case OP_TYPEEXACT: + branchlength += GET2(cc,1); + cc += 2 + IMM2_SIZE + ((cc[1 + IMM2_SIZE] == OP_PROP + || cc[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0); + break; + + /* Handle single-char non-literal matchers */ + + case OP_PROP: + case OP_NOTPROP: + cc += 2; + /* Fall through */ + + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ALLANY: + case OP_EXTUNI: + case OP_HSPACE: + case OP_NOT_HSPACE: + case OP_VSPACE: + case OP_NOT_VSPACE: + branchlength++; + cc++; + break; + + /* "Any newline" might match two characters, but it also might match just + one. */ + + case OP_ANYNL: + branchlength += 1; + cc++; + break; + + /* The single-byte matcher means we can't proceed in UTF-8 mode. (In + non-UTF-8 mode \C will actually be turned into OP_ALLANY, so won't ever + appear, but leave the code, just in case.) */ + + case OP_ANYBYTE: +#ifdef SUPPORT_UTF + if (utf) return -1; +#endif + branchlength++; + cc++; + break; + + /* For repeated character types, we have to test for \p and \P, which have + an extra two bytes of parameters. */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSQUERY: + if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2; + cc += PRIV(OP_lengths)[op]; + break; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEPOSUPTO: + if (cc[1 + IMM2_SIZE] == OP_PROP + || cc[1 + IMM2_SIZE] == OP_NOTPROP) cc += 2; + cc += PRIV(OP_lengths)[op]; + break; + + /* Check a class for variable quantification */ + + case OP_CLASS: + case OP_NCLASS: +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + case OP_XCLASS: + /* The original code caused an unsigned overflow in 64 bit systems, + so now we use a conditional statement. */ + if (op == OP_XCLASS) + cc += GET(cc, 1); + else + cc += PRIV(OP_lengths)[OP_CLASS]; +#else + cc += PRIV(OP_lengths)[OP_CLASS]; +#endif + + switch (*cc) + { + case OP_CRPLUS: + case OP_CRMINPLUS: + branchlength++; + /* Fall through */ + + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + cc++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + branchlength += GET2(cc,1); + cc += 1 + 2 * IMM2_SIZE; + break; + + default: + branchlength++; + break; + } + break; + + /* Backreferences and subroutine calls are treated in the same way: we find + the minimum length for the subpattern. A recursion, however, causes an + a flag to be set that causes the length of this branch to be ignored. The + logic is that a recursion can only make sense if there is another + alternation that stops the recursing. That will provide the minimum length + (when no recursion happens). A backreference within the group that it is + referencing behaves in the same way. + + If PCRE_JAVASCRIPT_COMPAT is set, a backreference to an unset bracket + matches an empty string (by default it causes a matching failure), so in + that case we must set the minimum length to zero. */ + + case OP_REF: + case OP_REFI: + if ((options & PCRE_JAVASCRIPT_COMPAT) == 0) + { + ce = cs = (pcre_uchar *)PRIV(find_bracket)(startcode, utf, GET2(cc, 1)); + if (cs == NULL) return -2; + do ce += GET(ce, 1); while (*ce == OP_ALT); + if (cc > cs && cc < ce) + { + d = 0; + had_recurse = TRUE; + } + else + { + d = find_minlength(cs, startcode, options, recurse_depth); + } + } + else d = 0; + cc += 1 + IMM2_SIZE; + + /* Handle repeated back references */ + + switch (*cc) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + min = 0; + cc++; + break; + + case OP_CRPLUS: + case OP_CRMINPLUS: + min = 1; + cc++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + min = GET2(cc, 1); + cc += 1 + 2 * IMM2_SIZE; + break; + + default: + min = 1; + break; + } + + branchlength += min * d; + break; + + /* We can easily detect direct recursion, but not mutual recursion. This is + caught by a recursion depth count. */ + + case OP_RECURSE: + cs = ce = (pcre_uchar *)startcode + GET(cc, 1); + do ce += GET(ce, 1); while (*ce == OP_ALT); + if ((cc > cs && cc < ce) || recurse_depth > 10) + had_recurse = TRUE; + else + { + branchlength += find_minlength(cs, startcode, options, recurse_depth + 1); + } + cc += 1 + LINK_SIZE; + break; + + /* Anything else does not or need not match a character. We can get the + item's length from the table, but for those that can match zero occurrences + of a character, we must take special action for UTF-8 characters. As it + happens, the "NOT" versions of these opcodes are used at present only for + ASCII characters, so they could be omitted from this list. However, in + future that may change, so we include them here so as not to leave a + gotcha for a future maintainer. */ + + case OP_UPTO: + case OP_UPTOI: + case OP_NOTUPTO: + case OP_NOTUPTOI: + case OP_MINUPTO: + case OP_MINUPTOI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + case OP_POSUPTO: + case OP_POSUPTOI: + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + + case OP_STAR: + case OP_STARI: + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_POSSTAR: + case OP_POSSTARI: + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + + case OP_QUERY: + case OP_QUERYI: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_MINQUERY: + case OP_MINQUERYI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + case OP_POSQUERY: + case OP_POSQUERYI: + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + + cc += PRIV(OP_lengths)[op]; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + /* Skip these, but we need to add in the name length. */ + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + case OP_THEN_ARG: + cc += PRIV(OP_lengths)[op] + cc[1]; + break; + + /* The remaining opcodes are just skipped over. */ + + case OP_CLOSE: + case OP_COMMIT: + case OP_FAIL: + case OP_PRUNE: + case OP_SET_SOM: + case OP_SKIP: + case OP_THEN: + cc += PRIV(OP_lengths)[op]; + break; + + /* This should not occur: we list all opcodes explicitly so that when + new ones get added they are properly considered. */ + + default: + return -3; + } + } +/* Control never gets here */ +} + /************************************************* * Set a bit and maybe its alternate case * *************************************************/ -/* Given a character, set its bit in the table, and also the bit for the other -version of a letter if we are caseless. +/* Given a character, set its first byte's bit in the table, and also the +corresponding bit for the other version of a letter if we are caseless. In +UTF-8 mode, for characters greater than 127, we can only do the caseless thing +when Unicode property support is available. Arguments: start_bits points to the bit map - c is the character + p points to the character caseless the caseless flag cd the block with char table pointers + utf TRUE for UTF-8 / UTF-16 / UTF-32 mode + +Returns: pointer after the character +*/ + +static const pcre_uchar * +set_table_bit(pcre_uint8 *start_bits, const pcre_uchar *p, BOOL caseless, + compile_data *cd, BOOL utf) +{ +pcre_uint32 c = *p; + +#ifdef COMPILE_PCRE8 +SET_BIT(c); + +#ifdef SUPPORT_UTF +if (utf && c > 127) + { + GETCHARINC(c, p); +#ifdef SUPPORT_UCP + if (caseless) + { + pcre_uchar buff[6]; + c = UCD_OTHERCASE(c); + (void)PRIV(ord2utf)(c, buff); + SET_BIT(buff[0]); + } +#endif /* Not SUPPORT_UCP */ + return p; + } +#else /* Not SUPPORT_UTF */ +(void)(utf); /* Stops warning for unused parameter */ +#endif /* SUPPORT_UTF */ + +/* Not UTF-8 mode, or character is less than 127. */ + +if (caseless && (cd->ctypes[c] & ctype_letter) != 0) SET_BIT(cd->fcc[c]); +return p + 1; +#endif /* COMPILE_PCRE8 */ + +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 +if (c > 0xff) + { + c = 0xff; + caseless = FALSE; + } +SET_BIT(c); + +#ifdef SUPPORT_UTF +if (utf && c > 127) + { + GETCHARINC(c, p); +#ifdef SUPPORT_UCP + if (caseless) + { + c = UCD_OTHERCASE(c); + if (c > 0xff) + c = 0xff; + SET_BIT(c); + } +#endif /* SUPPORT_UCP */ + return p; + } +#else /* Not SUPPORT_UTF */ +(void)(utf); /* Stops warning for unused parameter */ +#endif /* SUPPORT_UTF */ + +if (caseless && (cd->ctypes[c] & ctype_letter) != 0) SET_BIT(cd->fcc[c]); +return p + 1; +#endif +} + + + +/************************************************* +* Set bits for a positive character type * +*************************************************/ + +/* This function sets starting bits for a character type. In UTF-8 mode, we can +only do a direct setting for bytes less than 128, as otherwise there can be +confusion with bytes in the middle of UTF-8 characters. In a "traditional" +environment, the tables will only recognize ASCII characters anyway, but in at +least one Windows environment, some higher bytes bits were set in the tables. +So we deal with that case by considering the UTF-8 encoding. + +Arguments: + start_bits the starting bitmap + cbit type the type of character wanted + table_limit 32 for non-UTF-8; 16 for UTF-8 + cd the block with char table pointers + +Returns: nothing +*/ + +static void +set_type_bits(pcre_uint8 *start_bits, int cbit_type, unsigned int table_limit, + compile_data *cd) +{ +register pcre_uint32 c; +for (c = 0; c < table_limit; c++) start_bits[c] |= cd->cbits[c+cbit_type]; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +if (table_limit == 32) return; +for (c = 128; c < 256; c++) + { + if ((cd->cbits[c/8] & (1 << (c&7))) != 0) + { + pcre_uchar buff[6]; + (void)PRIV(ord2utf)(c, buff); + SET_BIT(buff[0]); + } + } +#endif +} + + +/************************************************* +* Set bits for a negative character type * +*************************************************/ + +/* This function sets starting bits for a negative character type such as \D. +In UTF-8 mode, we can only do a direct setting for bytes less than 128, as +otherwise there can be confusion with bytes in the middle of UTF-8 characters. +Unlike in the positive case, where we can set appropriate starting bits for +specific high-valued UTF-8 characters, in this case we have to set the bits for +all high-valued characters. The lowest is 0xc2, but we overkill by starting at +0xc0 (192) for simplicity. + +Arguments: + start_bits the starting bitmap + cbit type the type of character wanted + table_limit 32 for non-UTF-8; 16 for UTF-8 + cd the block with char table pointers -Returns: nothing +Returns: nothing */ static void -set_bit(uschar *start_bits, unsigned int c, BOOL caseless, compile_data *cd) +set_nottype_bits(pcre_uint8 *start_bits, int cbit_type, unsigned int table_limit, + compile_data *cd) { -start_bits[c/8] |= (1 << (c&7)); -if (caseless && (cd->ctypes[c] & ctype_letter) != 0) - start_bits[cd->fcc[c]/8] |= (1 << (cd->fcc[c]&7)); +register pcre_uint32 c; +for (c = 0; c < table_limit; c++) start_bits[c] |= ~cd->cbits[c+cbit_type]; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +if (table_limit != 32) for (c = 24; c < 32; c++) start_bits[c] = 0xff; +#endif } @@ -96,21 +706,26 @@ function fails unless the result is SSB_DONE. Arguments: code points to an expression start_bits points to a 32-byte table, initialized to 0 - caseless the current state of the caseless flag - utf8 TRUE if in UTF-8 mode + utf TRUE if in UTF-8 / UTF-16 / UTF-32 mode cd the block with char table pointers Returns: SSB_FAIL => Failed to find any starting bytes SSB_DONE => Found mandatory starting bytes SSB_CONTINUE => Found optional starting bytes + SSB_UNKNOWN => Hit an unrecognized opcode */ static int -set_start_bits(const uschar *code, uschar *start_bits, BOOL caseless, - BOOL utf8, compile_data *cd) +set_start_bits(const pcre_uchar *code, pcre_uint8 *start_bits, BOOL utf, + compile_data *cd) { -register int c; +register pcre_uint32 c; int yield = SSB_DONE; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +int table_limit = utf? 16:32; +#else +int table_limit = 32; +#endif #if 0 /* ========================================================================= */ @@ -131,19 +746,108 @@ volatile int dummy; do { - const uschar *tcode = code + (((int)*code == OP_CBRA)? 3:1) + LINK_SIZE; BOOL try_next = TRUE; + const pcre_uchar *tcode = code + 1 + LINK_SIZE; + + if (*code == OP_CBRA || *code == OP_SCBRA || + *code == OP_CBRAPOS || *code == OP_SCBRAPOS) tcode += IMM2_SIZE; while (try_next) /* Loop for items in this branch */ { int rc; + switch(*tcode) { - /* Fail if we reach something we don't understand */ + /* If we reach something we don't understand, it means a new opcode has + been created that hasn't been added to this code. Hopefully this problem + will be discovered during testing. */ default: + return SSB_UNKNOWN; + + /* Fail for a valid opcode that implies no starting bits. */ + + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + case OP_ALLANY: + case OP_ANY: + case OP_ANYBYTE: + case OP_CIRC: + case OP_CIRCM: + case OP_CLOSE: + case OP_COMMIT: + case OP_COND: + case OP_CREF: + case OP_DEF: + case OP_DOLL: + case OP_DOLLM: + case OP_END: + case OP_EOD: + case OP_EODN: + case OP_EXTUNI: + case OP_FAIL: + case OP_MARK: + case OP_NCREF: + case OP_NOT: + case OP_NOTEXACT: + case OP_NOTEXACTI: + case OP_NOTI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + case OP_NOTPROP: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_NOTUPTO: + case OP_NOTUPTOI: + case OP_NOT_HSPACE: + case OP_NOT_VSPACE: + case OP_NRREF: + case OP_PROP: + case OP_PRUNE: + case OP_PRUNE_ARG: + case OP_RECURSE: + case OP_REF: + case OP_REFI: + case OP_REVERSE: + case OP_RREF: + case OP_SCOND: + case OP_SET_SOM: + case OP_SKIP: + case OP_SKIP_ARG: + case OP_SOD: + case OP_SOM: + case OP_THEN: + case OP_THEN_ARG: +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: +#endif return SSB_FAIL; + /* We can ignore word boundary tests. */ + + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + tcode++; + break; + /* If we hit a bracket or a positive lookahead assertion, recurse to set bits from within the subpattern. If it can't find anything, we have to give up. If it finds some mandatory character(s), we are done for this @@ -153,10 +857,15 @@ do case OP_SBRA: case OP_CBRA: case OP_SCBRA: + case OP_BRAPOS: + case OP_SBRAPOS: + case OP_CBRAPOS: + case OP_SCBRAPOS: case OP_ONCE: + case OP_ONCE_NC: case OP_ASSERT: - rc = set_start_bits(tcode, start_bits, caseless, utf8, cd); - if (rc == SSB_FAIL) return SSB_FAIL; + rc = set_start_bits(tcode, start_bits, utf, cd); + if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc; if (rc == SSB_DONE) try_next = FALSE; else { do tcode += GET(tcode, 1); while (*tcode == OP_ALT); @@ -179,6 +888,7 @@ do case OP_KET: case OP_KETRMAX: case OP_KETRMIN: + case OP_KETRPOS: return SSB_CONTINUE; /* Skip over callout */ @@ -196,19 +906,13 @@ do tcode += 1 + LINK_SIZE; break; - /* Skip over an option setting, changing the caseless flag */ - - case OP_OPT: - caseless = (tcode[1] & PCRE_CASELESS) != 0; - tcode += 2; - break; - /* BRAZERO does the bracket, but carries on. */ case OP_BRAZERO: case OP_BRAMINZERO: - if (set_start_bits(++tcode, start_bits, caseless, utf8, cd) == SSB_FAIL) - return SSB_FAIL; + case OP_BRAPOSZERO: + rc = set_start_bits(++tcode, start_bits, utf, cd); + if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc; /* ========================================================================= See the comment at the head of this function concerning the next line, which was an old fudge for the benefit of OS/2. @@ -218,6 +922,14 @@ do tcode += 1 + LINK_SIZE; break; + /* SKIPZERO skips the bracket. */ + + case OP_SKIPZERO: + tcode++; + do tcode += GET(tcode,1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + break; + /* Single-char * or ? sets the bit and tries the next item */ case OP_STAR: @@ -226,12 +938,16 @@ do case OP_QUERY: case OP_MINQUERY: case OP_POSQUERY: - set_bit(start_bits, tcode[1], caseless, cd); - tcode += 2; -#ifdef SUPPORT_UTF8 - if (utf8 && tcode[-1] >= 0xc0) - tcode += _erts_pcre_utf8_table4[tcode[-1] & 0x3f]; -#endif + tcode = set_table_bit(start_bits, tcode + 1, FALSE, cd, utf); + break; + + case OP_STARI: + case OP_MINSTARI: + case OP_POSSTARI: + case OP_QUERYI: + case OP_MINQUERYI: + case OP_POSQUERYI: + tcode = set_table_bit(start_bits, tcode + 1, TRUE, cd, utf); break; /* Single-char upto sets the bit and tries the next */ @@ -239,77 +955,145 @@ do case OP_UPTO: case OP_MINUPTO: case OP_POSUPTO: - set_bit(start_bits, tcode[3], caseless, cd); - tcode += 4; -#ifdef SUPPORT_UTF8 - if (utf8 && tcode[-1] >= 0xc0) - tcode += _erts_pcre_utf8_table4[tcode[-1] & 0x3f]; -#endif + tcode = set_table_bit(start_bits, tcode + 1 + IMM2_SIZE, FALSE, cd, utf); break; - /* At least one single char sets the bit and stops */ + case OP_UPTOI: + case OP_MINUPTOI: + case OP_POSUPTOI: + tcode = set_table_bit(start_bits, tcode + 1 + IMM2_SIZE, TRUE, cd, utf); + break; - case OP_EXACT: /* Fall through */ - tcode += 2; + /* At least one single char sets the bit and stops */ + case OP_EXACT: + tcode += IMM2_SIZE; + /* Fall through */ case OP_CHAR: - case OP_CHARNC: case OP_PLUS: case OP_MINPLUS: case OP_POSPLUS: - set_bit(start_bits, tcode[1], caseless, cd); + (void)set_table_bit(start_bits, tcode + 1, FALSE, cd, utf); + try_next = FALSE; + break; + + case OP_EXACTI: + tcode += IMM2_SIZE; + /* Fall through */ + case OP_CHARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_POSPLUSI: + (void)set_table_bit(start_bits, tcode + 1, TRUE, cd, utf); try_next = FALSE; break; - /* Single character type sets the bits and stops */ + /* Special spacing and line-terminating items. These recognize specific + lists of characters. The difference between VSPACE and ANYNL is that the + latter can match the two-character CRLF sequence, but that is not + relevant for finding the first character, so their code here is + identical. */ + + case OP_HSPACE: + SET_BIT(CHAR_HT); + SET_BIT(CHAR_SPACE); +#ifdef SUPPORT_UTF + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+00A0 */ + SET_BIT(0xE1); /* For U+1680, U+180E */ + SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */ + SET_BIT(0xE3); /* For U+3000 */ +#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + SET_BIT(0xA0); + SET_BIT(0xFF); /* For characters > 255 */ +#endif /* COMPILE_PCRE[8|16|32] */ + } + else +#endif /* SUPPORT_UTF */ + { +#ifndef EBCDIC + SET_BIT(0xA0); +#endif /* Not EBCDIC */ +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + SET_BIT(0xFF); /* For characters > 255 */ +#endif /* COMPILE_PCRE[16|32] */ + } + try_next = FALSE; + break; + + case OP_ANYNL: + case OP_VSPACE: + SET_BIT(CHAR_LF); + SET_BIT(CHAR_VT); + SET_BIT(CHAR_FF); + SET_BIT(CHAR_CR); +#ifdef SUPPORT_UTF + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+0085 */ + SET_BIT(0xE2); /* For U+2028, U+2029 */ +#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + SET_BIT(CHAR_NEL); + SET_BIT(0xFF); /* For characters > 255 */ +#endif /* COMPILE_PCRE[8|16|32] */ + } + else +#endif /* SUPPORT_UTF */ + { + SET_BIT(CHAR_NEL); +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + try_next = FALSE; + break; + + /* Single character types set the bits and stop. Note that if PCRE_UCP + is set, we do not see these op codes because \d etc are converted to + properties. Therefore, these apply in the case when only characters less + than 256 are recognized to match the types. */ case OP_NOT_DIGIT: - for (c = 0; c < 32; c++) - start_bits[c] |= ~cd->cbits[c+cbit_digit]; + set_nottype_bits(start_bits, cbit_digit, table_limit, cd); try_next = FALSE; break; case OP_DIGIT: - for (c = 0; c < 32; c++) - start_bits[c] |= cd->cbits[c+cbit_digit]; + set_type_bits(start_bits, cbit_digit, table_limit, cd); try_next = FALSE; break; /* The cbit_space table has vertical tab as whitespace; we have to - discard it. */ + ensure it is set as not whitespace. Luckily, the code value is the same + (0x0b) in ASCII and EBCDIC, so we can just adjust the appropriate bit. */ case OP_NOT_WHITESPACE: - for (c = 0; c < 32; c++) - { - int d = cd->cbits[c+cbit_space]; - if (c == 1) d &= ~0x08; - start_bits[c] |= ~d; - } + set_nottype_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] |= 0x08; try_next = FALSE; break; - /* The cbit_space table has vertical tab as whitespace; we have to - discard it. */ + /* The cbit_space table has vertical tab as whitespace; we have to not + set it from the table. Luckily, the code value is the same (0x0b) in + ASCII and EBCDIC, so we can just adjust the appropriate bit. */ case OP_WHITESPACE: - for (c = 0; c < 32; c++) - { - int d = cd->cbits[c+cbit_space]; - if (c == 1) d &= ~0x08; - start_bits[c] |= d; - } + c = start_bits[1]; /* Save in case it was already set */ + set_type_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] = (start_bits[1] & ~0x08) | c; try_next = FALSE; break; case OP_NOT_WORDCHAR: - for (c = 0; c < 32; c++) - start_bits[c] |= ~cd->cbits[c+cbit_word]; + set_nottype_bits(start_bits, cbit_word, table_limit, cd); try_next = FALSE; break; case OP_WORDCHAR: - for (c = 0; c < 32; c++) - start_bits[c] |= cd->cbits[c+cbit_word]; + set_type_bits(start_bits, cbit_word, table_limit, cd); try_next = FALSE; break; @@ -318,11 +1102,12 @@ do case OP_TYPEPLUS: case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: tcode++; break; case OP_TYPEEXACT: - tcode += 3; + tcode += 1 + IMM2_SIZE; break; /* Zero or more repeats of character types set the bits and then @@ -331,7 +1116,7 @@ do case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEPOSUPTO: - tcode += 2; /* Fall through */ + tcode += IMM2_SIZE; /* Fall through */ case OP_TYPESTAR: case OP_TYPEMINSTAR: @@ -341,51 +1126,90 @@ do case OP_TYPEPOSQUERY: switch(tcode[1]) { + default: case OP_ANY: + case OP_ALLANY: return SSB_FAIL; + case OP_HSPACE: + SET_BIT(CHAR_HT); + SET_BIT(CHAR_SPACE); +#ifdef SUPPORT_UTF + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+00A0 */ + SET_BIT(0xE1); /* For U+1680, U+180E */ + SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */ + SET_BIT(0xE3); /* For U+3000 */ +#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + SET_BIT(0xA0); + SET_BIT(0xFF); /* For characters > 255 */ +#endif /* COMPILE_PCRE[8|16|32] */ + } + else +#endif /* SUPPORT_UTF */ +#ifndef EBCDIC + SET_BIT(0xA0); +#endif /* Not EBCDIC */ + break; + + case OP_ANYNL: + case OP_VSPACE: + SET_BIT(CHAR_LF); + SET_BIT(CHAR_VT); + SET_BIT(CHAR_FF); + SET_BIT(CHAR_CR); +#ifdef SUPPORT_UTF + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+0085 */ + SET_BIT(0xE2); /* For U+2028, U+2029 */ +#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + SET_BIT(CHAR_NEL); + SET_BIT(0xFF); /* For characters > 255 */ +#endif /* COMPILE_PCRE16 */ + } + else +#endif /* SUPPORT_UTF */ + SET_BIT(CHAR_NEL); + break; + case OP_NOT_DIGIT: - for (c = 0; c < 32; c++) - start_bits[c] |= ~cd->cbits[c+cbit_digit]; + set_nottype_bits(start_bits, cbit_digit, table_limit, cd); break; case OP_DIGIT: - for (c = 0; c < 32; c++) - start_bits[c] |= cd->cbits[c+cbit_digit]; + set_type_bits(start_bits, cbit_digit, table_limit, cd); break; /* The cbit_space table has vertical tab as whitespace; we have to - discard it. */ + ensure it gets set as not whitespace. Luckily, the code value is the + same (0x0b) in ASCII and EBCDIC, so we can just adjust the appropriate + bit. */ case OP_NOT_WHITESPACE: - for (c = 0; c < 32; c++) - { - int d = cd->cbits[c+cbit_space]; - if (c == 1) d &= ~0x08; - start_bits[c] |= ~d; - } + set_nottype_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] |= 0x08; break; /* The cbit_space table has vertical tab as whitespace; we have to - discard it. */ + avoid setting it. Luckily, the code value is the same (0x0b) in ASCII + and EBCDIC, so we can just adjust the appropriate bit. */ case OP_WHITESPACE: - for (c = 0; c < 32; c++) - { - int d = cd->cbits[c+cbit_space]; - if (c == 1) d &= ~0x08; - start_bits[c] |= d; - } + c = start_bits[1]; /* Save in case it was already set */ + set_type_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] = (start_bits[1] & ~0x08) | c; break; case OP_NOT_WORDCHAR: - for (c = 0; c < 32; c++) - start_bits[c] |= ~cd->cbits[c+cbit_word]; + set_nottype_bits(start_bits, cbit_word, table_limit, cd); break; case OP_WORDCHAR: - for (c = 0; c < 32; c++) - start_bits[c] |= cd->cbits[c+cbit_word]; + set_type_bits(start_bits, cbit_word, table_limit, cd); break; } @@ -399,18 +1223,23 @@ do character with a value > 255. */ case OP_NCLASS: -#ifdef SUPPORT_UTF8 - if (utf8) +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 + if (utf) { start_bits[24] |= 0xf0; /* Bits for 0xc4 - 0xc8 */ memset(start_bits+25, 0xff, 7); /* Bits for 0xc9 - 0xff */ } +#endif +#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32 + SET_BIT(0xFF); /* For characters > 255 */ #endif /* Fall through */ case OP_CLASS: { + pcre_uint8 *map; tcode++; + map = (pcre_uint8 *)tcode; /* In UTF-8 mode, the bits in a bit map correspond to character values, not to byte values. However, the bit map we are constructing is @@ -418,13 +1247,13 @@ do value is > 127. In fact, there are only two possible starting bytes for characters in the range 128 - 255. */ -#ifdef SUPPORT_UTF8 - if (utf8) +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 + if (utf) { - for (c = 0; c < 16; c++) start_bits[c] |= tcode[c]; + for (c = 0; c < 16; c++) start_bits[c] |= map[c]; for (c = 128; c < 256; c++) { - if ((tcode[c/8] && (1 << (c&7))) != 0) + if ((map[c/8] && (1 << (c&7))) != 0) { int d = (c >> 6) | 0xc0; /* Set bit for this starter */ start_bits[d/8] |= (1 << (d&7)); /* and then skip on to the */ @@ -432,18 +1261,17 @@ do } } } - - /* In non-UTF-8 mode, the two bit maps are completely compatible. */ - else #endif { - for (c = 0; c < 32; c++) start_bits[c] |= tcode[c]; + /* In non-UTF-8 mode, the two bit maps are completely compatible. */ + for (c = 0; c < 32; c++) start_bits[c] |= map[c]; } - /* Advance past the bit map, and act on what follows */ + /* Advance past the bit map, and act on what follows. For a zero + minimum repeat, continue; otherwise stop processing. */ - tcode += 32; + tcode += 32 / sizeof(pcre_uchar); switch (*tcode) { case OP_CRSTAR: @@ -455,7 +1283,7 @@ do case OP_CRRANGE: case OP_CRMINRANGE: - if (((tcode[1] << 8) + tcode[2]) == 0) tcode += 5; + if (GET2(tcode, 1) == 0) tcode += 1 + 2 * IMM2_SIZE; else try_next = FALSE; break; @@ -477,12 +1305,14 @@ return yield; + + /************************************************* * Study a compiled expression * *************************************************/ /* This function is handed a compiled expression that it must study to produce -information that will speed up the matching. It returns a pcre_extra block +information that will speed up the matching. It returns a pcre[16]_extra block which then gets handed back to pcre_exec(). Arguments: @@ -491,21 +1321,31 @@ Arguments: errorptr points to where to place error messages; set NULL unless error -Returns: pointer to a pcre_extra block, with study_data filled in and the - appropriate flag set; +Returns: pointer to a pcre[16]_extra block, with study_data filled in and + the appropriate flags set; NULL on error or if no optimization possible */ -PCRE_EXP_DEFN pcre_extra * -erts_pcre_study(const pcre *external_re, int options, const char **errorptr) +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN pcre_extra * PCRE_CALL_CONVENTION +pcre_study(const pcre *external_re, int options, const char **errorptr) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN pcre16_extra * PCRE_CALL_CONVENTION +pcre16_study(const pcre16 *external_re, int options, const char **errorptr) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN pcre32_extra * PCRE_CALL_CONVENTION +pcre32_study(const pcre32 *external_re, int options, const char **errorptr) +#endif { -uschar start_bits[32]; -pcre_extra *extra; +int min; +BOOL bits_set = FALSE; +pcre_uint8 start_bits[32]; +PUBL(extra) *extra = NULL; pcre_study_data *study; -const uschar *tables; -uschar *code; +const pcre_uint8 *tables; +pcre_uchar *code; compile_data compile_block; -const real_pcre *re = (const real_pcre *)external_re; +const REAL_PCRE *re = (const REAL_PCRE *)external_re; *errorptr = NULL; @@ -515,66 +1355,209 @@ if (re == NULL || re->magic_number != MAGIC_NUMBER) return NULL; } +if ((re->flags & PCRE_MODE) == 0) + { +#if defined COMPILE_PCRE8 + *errorptr = "argument not compiled in 8 bit mode"; +#elif defined COMPILE_PCRE16 + *errorptr = "argument not compiled in 16 bit mode"; +#elif defined COMPILE_PCRE32 + *errorptr = "argument not compiled in 32 bit mode"; +#endif + return NULL; + } + if ((options & ~PUBLIC_STUDY_OPTIONS) != 0) { *errorptr = "unknown or incorrect option bit(s) set"; return NULL; } -code = (uschar *)re + re->name_table_offset + +code = (pcre_uchar *)re + re->name_table_offset + (re->name_count * re->name_entry_size); /* For an anchored pattern, or an unanchored pattern that has a first char, or -a multiline pattern that matches only at "line starts", no further processing -at present. */ +a multiline pattern that matches only at "line starts", there is no point in +seeking a list of starting bytes. */ -if ((re->options & PCRE_ANCHORED) != 0 || - (re->flags & (PCRE_FIRSTSET|PCRE_STARTLINE)) != 0) - return NULL; +if ((re->options & PCRE_ANCHORED) == 0 && + (re->flags & (PCRE_FIRSTSET|PCRE_STARTLINE)) == 0) + { + int rc; + + /* Set the character tables in the block that is passed around */ + + tables = re->tables; + +#if defined COMPILE_PCRE8 + if (tables == NULL) + (void)pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, + (void *)(&tables)); +#elif defined COMPILE_PCRE16 + if (tables == NULL) + (void)pcre16_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, + (void *)(&tables)); +#elif defined COMPILE_PCRE32 + if (tables == NULL) + (void)pcre32_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, + (void *)(&tables)); +#endif -/* Set the character tables in the block that is passed around */ + compile_block.lcc = tables + lcc_offset; + compile_block.fcc = tables + fcc_offset; + compile_block.cbits = tables + cbits_offset; + compile_block.ctypes = tables + ctypes_offset; -tables = re->tables; -if (tables == NULL) - (void)erts_pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, - (void *)(&tables)); + /* See if we can find a fixed set of initial characters for the pattern. */ -compile_block.lcc = tables + lcc_offset; -compile_block.fcc = tables + fcc_offset; -compile_block.cbits = tables + cbits_offset; -compile_block.ctypes = tables + ctypes_offset; + memset(start_bits, 0, 32 * sizeof(pcre_uint8)); + rc = set_start_bits(code, start_bits, (re->options & PCRE_UTF8) != 0, + &compile_block); + bits_set = rc == SSB_DONE; + if (rc == SSB_UNKNOWN) + { + *errorptr = "internal error: opcode not recognized"; + return NULL; + } + } -/* See if we can find a fixed set of initial characters for the pattern. */ +/* Find the minimum length of subject string. */ -memset(start_bits, 0, 32 * sizeof(uschar)); -if (set_start_bits(code, start_bits, (re->options & PCRE_CASELESS) != 0, - (re->options & PCRE_UTF8) != 0, &compile_block) != SSB_DONE) return NULL; +switch(min = find_minlength(code, code, re->options, 0)) + { + case -2: *errorptr = "internal error: missing capturing bracket"; return NULL; + case -3: *errorptr = "internal error: opcode not recognized"; return NULL; + default: break; + } -/* Get a pcre_extra block and a pcre_study_data block. The study data is put in -the latter, which is pointed to by the former, which may also get additional -data set later by the calling program. At the moment, the size of -pcre_study_data is fixed. We nevertheless save it in a field for returning via -the erts_pcre_fullinfo() function so that if it becomes variable in the future, we -don't have to change that code. */ +/* If a set of starting bytes has been identified, or if the minimum length is +greater than zero, or if JIT optimization has been requested, or if +PCRE_STUDY_EXTRA_NEEDED is set, get a pcre[16]_extra block and a +pcre_study_data block. The study data is put in the latter, which is pointed to +by the former, which may also get additional data set later by the calling +program. At the moment, the size of pcre_study_data is fixed. We nevertheless +save it in a field for returning via the pcre_fullinfo() function so that if it +becomes variable in the future, we don't have to change that code. */ + +if (bits_set || min > 0 || (options & ( +#ifdef SUPPORT_JIT + PCRE_STUDY_JIT_COMPILE | PCRE_STUDY_JIT_PARTIAL_SOFT_COMPILE | + PCRE_STUDY_JIT_PARTIAL_HARD_COMPILE | +#endif + PCRE_STUDY_EXTRA_NEEDED)) != 0) + { + extra = (PUBL(extra) *)(PUBL(malloc)) + (sizeof(PUBL(extra)) + sizeof(pcre_study_data)); + if (extra == NULL) + { + *errorptr = "failed to get memory"; + return NULL; + } -extra = (pcre_extra *)(erts_pcre_malloc) - (sizeof(pcre_extra) + sizeof(pcre_study_data)); + study = (pcre_study_data *)((char *)extra + sizeof(PUBL(extra))); + extra->flags = PCRE_EXTRA_STUDY_DATA; + extra->study_data = study; -if (extra == NULL) - { - *errorptr = "failed to get memory"; - return NULL; - } + study->size = sizeof(pcre_study_data); + study->flags = 0; + + /* Set the start bits always, to avoid unset memory errors if the + study data is written to a file, but set the flag only if any of the bits + are set, to save time looking when none are. */ + + if (bits_set) + { + study->flags |= PCRE_STUDY_MAPPED; + memcpy(study->start_bits, start_bits, sizeof(start_bits)); + } + else memset(study->start_bits, 0, 32 * sizeof(pcre_uint8)); + +#ifdef PCRE_DEBUG + if (bits_set) + { + pcre_uint8 *ptr = start_bits; + int i; + + printf("Start bits:\n"); + for (i = 0; i < 32; i++) + printf("%3d: %02x%s", i * 8, *ptr++, ((i + 1) & 0x7) != 0? " " : "\n"); + } +#endif -study = (pcre_study_data *)((char *)extra + sizeof(pcre_extra)); -extra->flags = PCRE_EXTRA_STUDY_DATA; -extra->study_data = study; + /* Always set the minlength value in the block, because the JIT compiler + makes use of it. However, don't set the bit unless the length is greater than + zero - the interpretive pcre_exec() and pcre_dfa_exec() needn't waste time + checking the zero case. */ -study->size = sizeof(pcre_study_data); -study->options = PCRE_STUDY_MAPPED; -memcpy(study->start_bits, start_bits, sizeof(start_bits)); + if (min > 0) + { + study->flags |= PCRE_STUDY_MINLEN; + study->minlength = min; + } + else study->minlength = 0; + + /* If JIT support was compiled and requested, attempt the JIT compilation. + If no starting bytes were found, and the minimum length is zero, and JIT + compilation fails, abandon the extra block and return NULL, unless + PCRE_STUDY_EXTRA_NEEDED is set. */ + +#ifdef SUPPORT_JIT + extra->executable_jit = NULL; + if ((options & PCRE_STUDY_JIT_COMPILE) != 0) + PRIV(jit_compile)(re, extra, JIT_COMPILE); + if ((options & PCRE_STUDY_JIT_PARTIAL_SOFT_COMPILE) != 0) + PRIV(jit_compile)(re, extra, JIT_PARTIAL_SOFT_COMPILE); + if ((options & PCRE_STUDY_JIT_PARTIAL_HARD_COMPILE) != 0) + PRIV(jit_compile)(re, extra, JIT_PARTIAL_HARD_COMPILE); + + if (study->flags == 0 && (extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) == 0 && + (options & PCRE_STUDY_EXTRA_NEEDED) == 0) + { +#if defined COMPILE_PCRE8 + pcre_free_study(extra); +#elif defined COMPILE_PCRE16 + pcre16_free_study(extra); +#elif defined COMPILE_PCRE32 + pcre32_free_study(extra); +#endif + extra = NULL; + } +#endif + } return extra; } + +/************************************************* +* Free the study data * +*************************************************/ + +/* This function frees the memory that was obtained by pcre_study(). + +Argument: a pointer to the pcre[16]_extra block +Returns: nothing +*/ + +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN void +pcre_free_study(pcre_extra *extra) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN void +pcre16_free_study(pcre16_extra *extra) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN void +pcre32_free_study(pcre32_extra *extra) +#endif +{ +if (extra == NULL) + return; +#ifdef SUPPORT_JIT +if ((extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 && + extra->executable_jit != NULL) + PRIV(jit_free)(extra->executable_jit); +#endif +PUBL(free)(extra); +} + /* End of pcre_study.c */ diff --git a/erts/emulator/pcre/pcre_tables.c b/erts/emulator/pcre/pcre_tables.c index 72772de1dc..7be23babe0 100644 --- a/erts/emulator/pcre/pcre_tables.c +++ b/erts/emulator/pcre/pcre_tables.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2012 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -37,10 +37,11 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ +#ifndef PCRE_INCLUDED /* This module contains some fixed tables that are used by more than one of the PCRE code modules. The tables are also #included by the pcretest program, which -uses macros to change their names from _erts_pcre_xxx to xxxx, thereby avoiding name +uses macros to change their names from _pcre_xxx to xxxx, thereby avoiding name clashes with the library. */ /* %ExternalCopyright% */ @@ -51,11 +52,18 @@ clashes with the library. */ #include "pcre_internal.h" +#endif /* PCRE_INCLUDED */ /* Table of sizes for the fixed-length opcodes. It's defined in a macro so that the definition is next to the definition of the opcodes in pcre_internal.h. */ -const uschar _erts_pcre_OP_lengths[] = { OP_LENGTHS }; +const pcre_uint8 PRIV(OP_lengths)[] = { OP_LENGTHS }; + +/* Tables of horizontal and vertical whitespace characters, suitable for +adding to classes. */ + +const pcre_uint32 PRIV(hspace_list)[] = { HSPACE_LIST }; +const pcre_uint32 PRIV(vspace_list)[] = { VSPACE_LIST }; @@ -66,28 +74,123 @@ const uschar _erts_pcre_OP_lengths[] = { OP_LENGTHS }; /* These are the breakpoints for different numbers of bytes in a UTF-8 character. */ -#ifdef SUPPORT_UTF8 +#if (defined SUPPORT_UTF && defined COMPILE_PCRE8) \ + || (defined PCRE_INCLUDED && (defined SUPPORT_PCRE16 || defined SUPPORT_PCRE32)) -const int _erts_pcre_utf8_table1[] = +/* These tables are also required by pcretest in 16- or 32-bit mode. */ + +const int PRIV(utf8_table1)[] = { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff}; -const int _erts_pcre_utf8_table1_size = sizeof(_erts_pcre_utf8_table1)/sizeof(int); +const int PRIV(utf8_table1_size) = sizeof(PRIV(utf8_table1)) / sizeof(int); /* These are the indicator bits and the mask for the data bits to set in the first byte of a character, indexed by the number of additional bytes. */ -const int _erts_pcre_utf8_table2[] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}; -const int _erts_pcre_utf8_table3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01}; +const int PRIV(utf8_table2)[] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}; +const int PRIV(utf8_table3)[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01}; /* Table of the number of extra bytes, indexed by the first byte masked with 0x3f. The highest number for a valid UTF-8 first byte is in fact 0x3d. */ -const uschar _erts_pcre_utf8_table4[] = { +const pcre_uint8 PRIV(utf8_table4)[] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; +#endif /* (SUPPORT_UTF && COMPILE_PCRE8) || (PCRE_INCLUDED && SUPPORT_PCRE[16|32])*/ + +#ifdef SUPPORT_UTF + +/* Table to translate from particular type value to the general value. */ + +const pcre_uint32 PRIV(ucp_gentype)[] = { + ucp_C, ucp_C, ucp_C, ucp_C, ucp_C, /* Cc, Cf, Cn, Co, Cs */ + ucp_L, ucp_L, ucp_L, ucp_L, ucp_L, /* Ll, Lu, Lm, Lo, Lt */ + ucp_M, ucp_M, ucp_M, /* Mc, Me, Mn */ + ucp_N, ucp_N, ucp_N, /* Nd, Nl, No */ + ucp_P, ucp_P, ucp_P, ucp_P, ucp_P, /* Pc, Pd, Pe, Pf, Pi */ + ucp_P, ucp_P, /* Ps, Po */ + ucp_S, ucp_S, ucp_S, ucp_S, /* Sc, Sk, Sm, So */ + ucp_Z, ucp_Z, ucp_Z /* Zl, Zp, Zs */ +}; + +/* This table encodes the rules for finding the end of an extended grapheme +cluster. Every code point has a grapheme break property which is one of the +ucp_gbXX values defined in ucp.h. The 2-dimensional table is indexed by the +properties of two adjacent code points. The left property selects a word from +the table, and the right property selects a bit from that word like this: + + ucp_gbtable[left-property] & (1 << right-property) + +The value is non-zero if a grapheme break is NOT permitted between the relevant +two code points. The breaking rules are as follows: + +1. Break at the start and end of text (pretty obviously). + +2. Do not break between a CR and LF; otherwise, break before and after + controls. + +3. Do not break Hangul syllable sequences, the rules for which are: + + L may be followed by L, V, LV or LVT + LV or V may be followed by V or T + LVT or T may be followed by T + +4. Do not break before extending characters. + +The next two rules are only for extended grapheme clusters (but that's what we +are implementing). + +5. Do not break before SpacingMarks. + +6. Do not break after Prepend characters. + +7. Otherwise, break everywhere. +*/ + +const pcre_uint32 PRIV(ucp_gbtable[]) = { + (1<> 8); -return ((value & 0x000000ff) << 24) | - ((value & 0x0000ff00) << 8) | - ((value & 0x00ff0000) >> 8) | - ((value & 0xff000000) >> 24); -} - - - -/************************************************* -* Test for a byte-flipped compiled regex * -*************************************************/ - -/* This function is called from pcre_exec(), pcre_dfa_exec(), and also from -pcre_fullinfo(). Its job is to test whether the regex is byte-flipped - that -is, it was compiled on a system of opposite endianness. The function is called -only when the native MAGIC_NUMBER test fails. If the regex is indeed flipped, -we flip all the relevant values into a different data block, and return it. - -Arguments: - re points to the regex - study points to study data, or NULL - internal_re points to a new regex block - internal_study points to a new study block - -Returns: the new block if is is indeed a byte-flipped regex - NULL if it is not -*/ - -real_pcre * -_erts_pcre_try_flipped(const real_pcre *re, real_pcre *internal_re, - const pcre_study_data *study, pcre_study_data *internal_study) -{ -if (byteflip(re->magic_number, sizeof(re->magic_number)) != MAGIC_NUMBER) - return NULL; - -*internal_re = *re; /* To copy other fields */ -internal_re->size = byteflip(re->size, sizeof(re->size)); -internal_re->options = byteflip(re->options, sizeof(re->options)); -internal_re->flags = (pcre_uint16)byteflip(re->flags, sizeof(re->flags)); -internal_re->top_bracket = - (pcre_uint16)byteflip(re->top_bracket, sizeof(re->top_bracket)); -internal_re->top_backref = - (pcre_uint16)byteflip(re->top_backref, sizeof(re->top_backref)); -internal_re->first_byte = - (pcre_uint16)byteflip(re->first_byte, sizeof(re->first_byte)); -internal_re->req_byte = - (pcre_uint16)byteflip(re->req_byte, sizeof(re->req_byte)); -internal_re->name_table_offset = - (pcre_uint16)byteflip(re->name_table_offset, sizeof(re->name_table_offset)); -internal_re->name_entry_size = - (pcre_uint16)byteflip(re->name_entry_size, sizeof(re->name_entry_size)); -internal_re->name_count = - (pcre_uint16)byteflip(re->name_count, sizeof(re->name_count)); - -if (study != NULL) - { - *internal_study = *study; /* To copy other fields */ - internal_study->size = byteflip(study->size, sizeof(study->size)); - internal_study->options = byteflip(study->options, sizeof(study->options)); - } - -return internal_re; -} - -/* End of pcre_tryflipped.c */ diff --git a/erts/emulator/pcre/pcre_ucd.c b/erts/emulator/pcre/pcre_ucd.c new file mode 100644 index 0000000000..bdbc73c245 --- /dev/null +++ b/erts/emulator/pcre/pcre_ucd.c @@ -0,0 +1,3298 @@ +/* This module is generated by the maint/MultiStage2.py script. +Do not modify it by hand. Instead modify the script and run it +to regenerate this code. + +As well as being part of the PCRE library, this module is #included +by the pcretest program, which redefines the PRIV macro to change +table names from _pcre_xxx to xxxx, thereby avoiding name clashes +with the library. At present, just one of these tables is actually +needed. */ +/* %ExternalCopyright% */ +#ifndef PCRE_INCLUDED + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +#endif /* PCRE_INCLUDED */ + +/* Unicode character database. */ +/* This file was autogenerated by the MultiStage2.py script. */ +/* Total size: 65696 bytes, block size: 128. */ + +/* The tables herein are needed only when UCP support is built +into PCRE. This module should not be referenced otherwise, so +it should not matter whether it is compiled or not. However +a comment was received about space saving - maybe the guy linked +all the modules rather than using a library - so we include a +condition to cut out the tables when not needed. But don't leave +a totally empty module because some compilers barf at that. +Instead, just supply small dummy tables. */ + +#ifndef SUPPORT_UCP +const ucd_record PRIV(ucd_records)[] = {{0,0,0,0,0 }}; +const pcre_uint8 PRIV(ucd_stage1)[] = {0}; +const pcre_uint16 PRIV(ucd_stage2)[] = {0}; +const pcre_uint32 PRIV(ucd_caseless_sets)[] = {0}; +#else + +/* When recompiling tables with a new Unicode version, please check the +types in this structure definition from pcre_internal.h (the actual +field names will be different): + +typedef struct { +pcre_uint8 property_0; +pcre_uint8 property_1; +pcre_uint8 property_2; +pcre_uint8 property_3; +pcre_int32 property_4; +} ucd_record; +*/ + + +const pcre_uint32 PRIV(ucd_caseless_sets)[] = { + NOTACHAR, + 0x0053, 0x0073, 0x017f, NOTACHAR, + 0x01c4, 0x01c5, 0x01c6, NOTACHAR, + 0x01c7, 0x01c8, 0x01c9, NOTACHAR, + 0x01ca, 0x01cb, 0x01cc, NOTACHAR, + 0x01f1, 0x01f2, 0x01f3, NOTACHAR, + 0x0345, 0x0399, 0x03b9, 0x1fbe, NOTACHAR, + 0x00b5, 0x039c, 0x03bc, NOTACHAR, + 0x03a3, 0x03c2, 0x03c3, NOTACHAR, + 0x0392, 0x03b2, 0x03d0, NOTACHAR, + 0x0398, 0x03b8, 0x03d1, 0x03f4, NOTACHAR, + 0x03a6, 0x03c6, 0x03d5, NOTACHAR, + 0x03a0, 0x03c0, 0x03d6, NOTACHAR, + 0x039a, 0x03ba, 0x03f0, NOTACHAR, + 0x03a1, 0x03c1, 0x03f1, NOTACHAR, + 0x0395, 0x03b5, 0x03f5, NOTACHAR, + 0x1e60, 0x1e61, 0x1e9b, NOTACHAR, + 0x03a9, 0x03c9, 0x2126, NOTACHAR, + 0x004b, 0x006b, 0x212a, NOTACHAR, + 0x00c5, 0x00e5, 0x212b, NOTACHAR, +}; + +/* When #included in pcretest, we don't need this large table. */ + +#ifndef PCRE_INCLUDED + +const ucd_record PRIV(ucd_records)[] = { /* 5024 bytes, record size 8 */ + { 9, 0, 2, 0, 0, }, /* 0 */ + { 9, 0, 1, 0, 0, }, /* 1 */ + { 9, 0, 0, 0, 0, }, /* 2 */ + { 9, 29, 12, 0, 0, }, /* 3 */ + { 9, 21, 12, 0, 0, }, /* 4 */ + { 9, 23, 12, 0, 0, }, /* 5 */ + { 9, 22, 12, 0, 0, }, /* 6 */ + { 9, 18, 12, 0, 0, }, /* 7 */ + { 9, 25, 12, 0, 0, }, /* 8 */ + { 9, 17, 12, 0, 0, }, /* 9 */ + { 9, 13, 12, 0, 0, }, /* 10 */ + { 33, 9, 12, 0, 32, }, /* 11 */ + { 33, 9, 12, 71, 32, }, /* 12 */ + { 33, 9, 12, 1, 32, }, /* 13 */ + { 9, 24, 12, 0, 0, }, /* 14 */ + { 9, 16, 12, 0, 0, }, /* 15 */ + { 33, 5, 12, 0, -32, }, /* 16 */ + { 33, 5, 12, 71, -32, }, /* 17 */ + { 33, 5, 12, 1, -32, }, /* 18 */ + { 9, 26, 12, 0, 0, }, /* 19 */ + { 33, 7, 12, 0, 0, }, /* 20 */ + { 9, 20, 12, 0, 0, }, /* 21 */ + { 9, 1, 2, 0, 0, }, /* 22 */ + { 9, 15, 12, 0, 0, }, /* 23 */ + { 9, 5, 12, 26, 775, }, /* 24 */ + { 9, 19, 12, 0, 0, }, /* 25 */ + { 33, 9, 12, 75, 32, }, /* 26 */ + { 33, 5, 12, 0, 7615, }, /* 27 */ + { 33, 5, 12, 75, -32, }, /* 28 */ + { 33, 5, 12, 0, 121, }, /* 29 */ + { 33, 9, 12, 0, 1, }, /* 30 */ + { 33, 5, 12, 0, -1, }, /* 31 */ + { 33, 9, 12, 0, 0, }, /* 32 */ + { 33, 5, 12, 0, 0, }, /* 33 */ + { 33, 9, 12, 0, -121, }, /* 34 */ + { 33, 5, 12, 1, -268, }, /* 35 */ + { 33, 5, 12, 0, 195, }, /* 36 */ + { 33, 9, 12, 0, 210, }, /* 37 */ + { 33, 9, 12, 0, 206, }, /* 38 */ + { 33, 9, 12, 0, 205, }, /* 39 */ + { 33, 9, 12, 0, 79, }, /* 40 */ + { 33, 9, 12, 0, 202, }, /* 41 */ + { 33, 9, 12, 0, 203, }, /* 42 */ + { 33, 9, 12, 0, 207, }, /* 43 */ + { 33, 5, 12, 0, 97, }, /* 44 */ + { 33, 9, 12, 0, 211, }, /* 45 */ + { 33, 9, 12, 0, 209, }, /* 46 */ + { 33, 5, 12, 0, 163, }, /* 47 */ + { 33, 9, 12, 0, 213, }, /* 48 */ + { 33, 5, 12, 0, 130, }, /* 49 */ + { 33, 9, 12, 0, 214, }, /* 50 */ + { 33, 9, 12, 0, 218, }, /* 51 */ + { 33, 9, 12, 0, 217, }, /* 52 */ + { 33, 9, 12, 0, 219, }, /* 53 */ + { 33, 5, 12, 0, 56, }, /* 54 */ + { 33, 9, 12, 5, 2, }, /* 55 */ + { 33, 8, 12, 5, 1, }, /* 56 */ + { 33, 5, 12, 5, -2, }, /* 57 */ + { 33, 9, 12, 9, 2, }, /* 58 */ + { 33, 8, 12, 9, 1, }, /* 59 */ + { 33, 5, 12, 9, -2, }, /* 60 */ + { 33, 9, 12, 13, 2, }, /* 61 */ + { 33, 8, 12, 13, 1, }, /* 62 */ + { 33, 5, 12, 13, -2, }, /* 63 */ + { 33, 5, 12, 0, -79, }, /* 64 */ + { 33, 9, 12, 17, 2, }, /* 65 */ + { 33, 8, 12, 17, 1, }, /* 66 */ + { 33, 5, 12, 17, -2, }, /* 67 */ + { 33, 9, 12, 0, -97, }, /* 68 */ + { 33, 9, 12, 0, -56, }, /* 69 */ + { 33, 9, 12, 0, -130, }, /* 70 */ + { 33, 9, 12, 0, 10795, }, /* 71 */ + { 33, 9, 12, 0, -163, }, /* 72 */ + { 33, 9, 12, 0, 10792, }, /* 73 */ + { 33, 5, 12, 0, 10815, }, /* 74 */ + { 33, 9, 12, 0, -195, }, /* 75 */ + { 33, 9, 12, 0, 69, }, /* 76 */ + { 33, 9, 12, 0, 71, }, /* 77 */ + { 33, 5, 12, 0, 10783, }, /* 78 */ + { 33, 5, 12, 0, 10780, }, /* 79 */ + { 33, 5, 12, 0, 10782, }, /* 80 */ + { 33, 5, 12, 0, -210, }, /* 81 */ + { 33, 5, 12, 0, -206, }, /* 82 */ + { 33, 5, 12, 0, -205, }, /* 83 */ + { 33, 5, 12, 0, -202, }, /* 84 */ + { 33, 5, 12, 0, -203, }, /* 85 */ + { 33, 5, 12, 0, -207, }, /* 86 */ + { 33, 5, 12, 0, 42280, }, /* 87 */ + { 33, 5, 12, 0, 42308, }, /* 88 */ + { 33, 5, 12, 0, -209, }, /* 89 */ + { 33, 5, 12, 0, -211, }, /* 90 */ + { 33, 5, 12, 0, 10743, }, /* 91 */ + { 33, 5, 12, 0, 10749, }, /* 92 */ + { 33, 5, 12, 0, -213, }, /* 93 */ + { 33, 5, 12, 0, -214, }, /* 94 */ + { 33, 5, 12, 0, 10727, }, /* 95 */ + { 33, 5, 12, 0, -218, }, /* 96 */ + { 33, 5, 12, 0, -69, }, /* 97 */ + { 33, 5, 12, 0, -217, }, /* 98 */ + { 33, 5, 12, 0, -71, }, /* 99 */ + { 33, 5, 12, 0, -219, }, /* 100 */ + { 33, 6, 12, 0, 0, }, /* 101 */ + { 9, 6, 12, 0, 0, }, /* 102 */ + { 3, 24, 12, 0, 0, }, /* 103 */ + { 27, 12, 3, 0, 0, }, /* 104 */ + { 27, 12, 3, 21, 116, }, /* 105 */ + { 19, 9, 12, 0, 1, }, /* 106 */ + { 19, 5, 12, 0, -1, }, /* 107 */ + { 19, 24, 12, 0, 0, }, /* 108 */ + { 9, 2, 12, 0, 0, }, /* 109 */ + { 19, 6, 12, 0, 0, }, /* 110 */ + { 19, 5, 12, 0, 130, }, /* 111 */ + { 19, 9, 12, 0, 38, }, /* 112 */ + { 19, 9, 12, 0, 37, }, /* 113 */ + { 19, 9, 12, 0, 64, }, /* 114 */ + { 19, 9, 12, 0, 63, }, /* 115 */ + { 19, 5, 12, 0, 0, }, /* 116 */ + { 19, 9, 12, 0, 32, }, /* 117 */ + { 19, 9, 12, 34, 32, }, /* 118 */ + { 19, 9, 12, 59, 32, }, /* 119 */ + { 19, 9, 12, 38, 32, }, /* 120 */ + { 19, 9, 12, 21, 32, }, /* 121 */ + { 19, 9, 12, 51, 32, }, /* 122 */ + { 19, 9, 12, 26, 32, }, /* 123 */ + { 19, 9, 12, 47, 32, }, /* 124 */ + { 19, 9, 12, 55, 32, }, /* 125 */ + { 19, 9, 12, 30, 32, }, /* 126 */ + { 19, 9, 12, 43, 32, }, /* 127 */ + { 19, 9, 12, 67, 32, }, /* 128 */ + { 19, 5, 12, 0, -38, }, /* 129 */ + { 19, 5, 12, 0, -37, }, /* 130 */ + { 19, 5, 12, 0, -32, }, /* 131 */ + { 19, 5, 12, 34, -32, }, /* 132 */ + { 19, 5, 12, 59, -32, }, /* 133 */ + { 19, 5, 12, 38, -32, }, /* 134 */ + { 19, 5, 12, 21, -116, }, /* 135 */ + { 19, 5, 12, 51, -32, }, /* 136 */ + { 19, 5, 12, 26, -775, }, /* 137 */ + { 19, 5, 12, 47, -32, }, /* 138 */ + { 19, 5, 12, 55, -32, }, /* 139 */ + { 19, 5, 12, 30, 1, }, /* 140 */ + { 19, 5, 12, 30, -32, }, /* 141 */ + { 19, 5, 12, 43, -32, }, /* 142 */ + { 19, 5, 12, 67, -32, }, /* 143 */ + { 19, 5, 12, 0, -64, }, /* 144 */ + { 19, 5, 12, 0, -63, }, /* 145 */ + { 19, 9, 12, 0, 8, }, /* 146 */ + { 19, 5, 12, 34, -30, }, /* 147 */ + { 19, 5, 12, 38, -25, }, /* 148 */ + { 19, 9, 12, 0, 0, }, /* 149 */ + { 19, 5, 12, 43, -15, }, /* 150 */ + { 19, 5, 12, 47, -22, }, /* 151 */ + { 19, 5, 12, 0, -8, }, /* 152 */ + { 10, 9, 12, 0, 1, }, /* 153 */ + { 10, 5, 12, 0, -1, }, /* 154 */ + { 19, 5, 12, 51, -54, }, /* 155 */ + { 19, 5, 12, 55, -48, }, /* 156 */ + { 19, 5, 12, 0, 7, }, /* 157 */ + { 19, 9, 12, 38, -60, }, /* 158 */ + { 19, 5, 12, 59, -64, }, /* 159 */ + { 19, 25, 12, 0, 0, }, /* 160 */ + { 19, 9, 12, 0, -7, }, /* 161 */ + { 19, 9, 12, 0, -130, }, /* 162 */ + { 12, 9, 12, 0, 80, }, /* 163 */ + { 12, 9, 12, 0, 32, }, /* 164 */ + { 12, 5, 12, 0, -32, }, /* 165 */ + { 12, 5, 12, 0, -80, }, /* 166 */ + { 12, 9, 12, 0, 1, }, /* 167 */ + { 12, 5, 12, 0, -1, }, /* 168 */ + { 12, 26, 12, 0, 0, }, /* 169 */ + { 12, 12, 3, 0, 0, }, /* 170 */ + { 12, 11, 3, 0, 0, }, /* 171 */ + { 12, 9, 12, 0, 15, }, /* 172 */ + { 12, 5, 12, 0, -15, }, /* 173 */ + { 1, 9, 12, 0, 48, }, /* 174 */ + { 1, 6, 12, 0, 0, }, /* 175 */ + { 1, 21, 12, 0, 0, }, /* 176 */ + { 1, 5, 12, 0, -48, }, /* 177 */ + { 1, 5, 12, 0, 0, }, /* 178 */ + { 1, 17, 12, 0, 0, }, /* 179 */ + { 1, 23, 12, 0, 0, }, /* 180 */ + { 25, 12, 3, 0, 0, }, /* 181 */ + { 25, 17, 12, 0, 0, }, /* 182 */ + { 25, 21, 12, 0, 0, }, /* 183 */ + { 25, 7, 12, 0, 0, }, /* 184 */ + { 0, 1, 2, 0, 0, }, /* 185 */ + { 0, 25, 12, 0, 0, }, /* 186 */ + { 0, 21, 12, 0, 0, }, /* 187 */ + { 0, 23, 12, 0, 0, }, /* 188 */ + { 0, 26, 12, 0, 0, }, /* 189 */ + { 0, 12, 3, 0, 0, }, /* 190 */ + { 0, 7, 12, 0, 0, }, /* 191 */ + { 0, 6, 12, 0, 0, }, /* 192 */ + { 0, 13, 12, 0, 0, }, /* 193 */ + { 49, 21, 12, 0, 0, }, /* 194 */ + { 49, 1, 2, 0, 0, }, /* 195 */ + { 49, 7, 12, 0, 0, }, /* 196 */ + { 49, 12, 3, 0, 0, }, /* 197 */ + { 55, 7, 12, 0, 0, }, /* 198 */ + { 55, 12, 3, 0, 0, }, /* 199 */ + { 63, 13, 12, 0, 0, }, /* 200 */ + { 63, 7, 12, 0, 0, }, /* 201 */ + { 63, 12, 3, 0, 0, }, /* 202 */ + { 63, 6, 12, 0, 0, }, /* 203 */ + { 63, 26, 12, 0, 0, }, /* 204 */ + { 63, 21, 12, 0, 0, }, /* 205 */ + { 89, 7, 12, 0, 0, }, /* 206 */ + { 89, 12, 3, 0, 0, }, /* 207 */ + { 89, 6, 12, 0, 0, }, /* 208 */ + { 89, 21, 12, 0, 0, }, /* 209 */ + { 94, 7, 12, 0, 0, }, /* 210 */ + { 94, 12, 3, 0, 0, }, /* 211 */ + { 94, 21, 12, 0, 0, }, /* 212 */ + { 14, 12, 3, 0, 0, }, /* 213 */ + { 14, 10, 5, 0, 0, }, /* 214 */ + { 14, 7, 12, 0, 0, }, /* 215 */ + { 14, 13, 12, 0, 0, }, /* 216 */ + { 14, 21, 12, 0, 0, }, /* 217 */ + { 14, 6, 12, 0, 0, }, /* 218 */ + { 2, 12, 3, 0, 0, }, /* 219 */ + { 2, 10, 5, 0, 0, }, /* 220 */ + { 2, 7, 12, 0, 0, }, /* 221 */ + { 2, 10, 3, 0, 0, }, /* 222 */ + { 2, 13, 12, 0, 0, }, /* 223 */ + { 2, 23, 12, 0, 0, }, /* 224 */ + { 2, 15, 12, 0, 0, }, /* 225 */ + { 2, 26, 12, 0, 0, }, /* 226 */ + { 21, 12, 3, 0, 0, }, /* 227 */ + { 21, 10, 5, 0, 0, }, /* 228 */ + { 21, 7, 12, 0, 0, }, /* 229 */ + { 21, 13, 12, 0, 0, }, /* 230 */ + { 20, 12, 3, 0, 0, }, /* 231 */ + { 20, 10, 5, 0, 0, }, /* 232 */ + { 20, 7, 12, 0, 0, }, /* 233 */ + { 20, 13, 12, 0, 0, }, /* 234 */ + { 20, 21, 12, 0, 0, }, /* 235 */ + { 20, 23, 12, 0, 0, }, /* 236 */ + { 43, 12, 3, 0, 0, }, /* 237 */ + { 43, 10, 5, 0, 0, }, /* 238 */ + { 43, 7, 12, 0, 0, }, /* 239 */ + { 43, 10, 3, 0, 0, }, /* 240 */ + { 43, 13, 12, 0, 0, }, /* 241 */ + { 43, 26, 12, 0, 0, }, /* 242 */ + { 43, 15, 12, 0, 0, }, /* 243 */ + { 53, 12, 3, 0, 0, }, /* 244 */ + { 53, 7, 12, 0, 0, }, /* 245 */ + { 53, 10, 3, 0, 0, }, /* 246 */ + { 53, 10, 5, 0, 0, }, /* 247 */ + { 53, 13, 12, 0, 0, }, /* 248 */ + { 53, 15, 12, 0, 0, }, /* 249 */ + { 53, 26, 12, 0, 0, }, /* 250 */ + { 53, 23, 12, 0, 0, }, /* 251 */ + { 54, 10, 5, 0, 0, }, /* 252 */ + { 54, 7, 12, 0, 0, }, /* 253 */ + { 54, 12, 3, 0, 0, }, /* 254 */ + { 54, 13, 12, 0, 0, }, /* 255 */ + { 54, 15, 12, 0, 0, }, /* 256 */ + { 54, 26, 12, 0, 0, }, /* 257 */ + { 28, 10, 5, 0, 0, }, /* 258 */ + { 28, 7, 12, 0, 0, }, /* 259 */ + { 28, 12, 3, 0, 0, }, /* 260 */ + { 28, 10, 3, 0, 0, }, /* 261 */ + { 28, 13, 12, 0, 0, }, /* 262 */ + { 36, 10, 5, 0, 0, }, /* 263 */ + { 36, 7, 12, 0, 0, }, /* 264 */ + { 36, 10, 3, 0, 0, }, /* 265 */ + { 36, 12, 3, 0, 0, }, /* 266 */ + { 36, 13, 12, 0, 0, }, /* 267 */ + { 36, 15, 12, 0, 0, }, /* 268 */ + { 36, 26, 12, 0, 0, }, /* 269 */ + { 47, 10, 5, 0, 0, }, /* 270 */ + { 47, 7, 12, 0, 0, }, /* 271 */ + { 47, 12, 3, 0, 0, }, /* 272 */ + { 47, 10, 3, 0, 0, }, /* 273 */ + { 47, 21, 12, 0, 0, }, /* 274 */ + { 56, 7, 12, 0, 0, }, /* 275 */ + { 56, 12, 3, 0, 0, }, /* 276 */ + { 56, 7, 5, 0, 0, }, /* 277 */ + { 56, 6, 12, 0, 0, }, /* 278 */ + { 56, 21, 12, 0, 0, }, /* 279 */ + { 56, 13, 12, 0, 0, }, /* 280 */ + { 32, 7, 12, 0, 0, }, /* 281 */ + { 32, 12, 3, 0, 0, }, /* 282 */ + { 32, 7, 5, 0, 0, }, /* 283 */ + { 32, 6, 12, 0, 0, }, /* 284 */ + { 32, 13, 12, 0, 0, }, /* 285 */ + { 57, 7, 12, 0, 0, }, /* 286 */ + { 57, 26, 12, 0, 0, }, /* 287 */ + { 57, 21, 12, 0, 0, }, /* 288 */ + { 57, 12, 3, 0, 0, }, /* 289 */ + { 57, 13, 12, 0, 0, }, /* 290 */ + { 57, 15, 12, 0, 0, }, /* 291 */ + { 57, 22, 12, 0, 0, }, /* 292 */ + { 57, 18, 12, 0, 0, }, /* 293 */ + { 57, 10, 5, 0, 0, }, /* 294 */ + { 38, 7, 12, 0, 0, }, /* 295 */ + { 38, 10, 12, 0, 0, }, /* 296 */ + { 38, 12, 3, 0, 0, }, /* 297 */ + { 38, 10, 5, 0, 0, }, /* 298 */ + { 38, 13, 12, 0, 0, }, /* 299 */ + { 38, 21, 12, 0, 0, }, /* 300 */ + { 38, 26, 12, 0, 0, }, /* 301 */ + { 16, 9, 12, 0, 7264, }, /* 302 */ + { 16, 7, 12, 0, 0, }, /* 303 */ + { 16, 6, 12, 0, 0, }, /* 304 */ + { 23, 7, 6, 0, 0, }, /* 305 */ + { 23, 7, 7, 0, 0, }, /* 306 */ + { 23, 7, 8, 0, 0, }, /* 307 */ + { 15, 7, 12, 0, 0, }, /* 308 */ + { 15, 12, 3, 0, 0, }, /* 309 */ + { 15, 21, 12, 0, 0, }, /* 310 */ + { 15, 15, 12, 0, 0, }, /* 311 */ + { 15, 26, 12, 0, 0, }, /* 312 */ + { 8, 7, 12, 0, 0, }, /* 313 */ + { 7, 17, 12, 0, 0, }, /* 314 */ + { 7, 7, 12, 0, 0, }, /* 315 */ + { 7, 21, 12, 0, 0, }, /* 316 */ + { 40, 29, 12, 0, 0, }, /* 317 */ + { 40, 7, 12, 0, 0, }, /* 318 */ + { 40, 22, 12, 0, 0, }, /* 319 */ + { 40, 18, 12, 0, 0, }, /* 320 */ + { 45, 7, 12, 0, 0, }, /* 321 */ + { 45, 14, 12, 0, 0, }, /* 322 */ + { 50, 7, 12, 0, 0, }, /* 323 */ + { 50, 12, 3, 0, 0, }, /* 324 */ + { 24, 7, 12, 0, 0, }, /* 325 */ + { 24, 12, 3, 0, 0, }, /* 326 */ + { 6, 7, 12, 0, 0, }, /* 327 */ + { 6, 12, 3, 0, 0, }, /* 328 */ + { 51, 7, 12, 0, 0, }, /* 329 */ + { 51, 12, 3, 0, 0, }, /* 330 */ + { 31, 7, 12, 0, 0, }, /* 331 */ + { 31, 12, 3, 0, 0, }, /* 332 */ + { 31, 10, 5, 0, 0, }, /* 333 */ + { 31, 21, 12, 0, 0, }, /* 334 */ + { 31, 6, 12, 0, 0, }, /* 335 */ + { 31, 23, 12, 0, 0, }, /* 336 */ + { 31, 13, 12, 0, 0, }, /* 337 */ + { 31, 15, 12, 0, 0, }, /* 338 */ + { 37, 21, 12, 0, 0, }, /* 339 */ + { 37, 17, 12, 0, 0, }, /* 340 */ + { 37, 12, 3, 0, 0, }, /* 341 */ + { 37, 29, 12, 0, 0, }, /* 342 */ + { 37, 13, 12, 0, 0, }, /* 343 */ + { 37, 7, 12, 0, 0, }, /* 344 */ + { 37, 6, 12, 0, 0, }, /* 345 */ + { 34, 7, 12, 0, 0, }, /* 346 */ + { 34, 12, 3, 0, 0, }, /* 347 */ + { 34, 10, 5, 0, 0, }, /* 348 */ + { 34, 26, 12, 0, 0, }, /* 349 */ + { 34, 21, 12, 0, 0, }, /* 350 */ + { 34, 13, 12, 0, 0, }, /* 351 */ + { 52, 7, 12, 0, 0, }, /* 352 */ + { 39, 7, 12, 0, 0, }, /* 353 */ + { 39, 10, 12, 0, 0, }, /* 354 */ + { 39, 10, 5, 0, 0, }, /* 355 */ + { 39, 13, 12, 0, 0, }, /* 356 */ + { 39, 15, 12, 0, 0, }, /* 357 */ + { 39, 26, 12, 0, 0, }, /* 358 */ + { 31, 26, 12, 0, 0, }, /* 359 */ + { 5, 7, 12, 0, 0, }, /* 360 */ + { 5, 12, 3, 0, 0, }, /* 361 */ + { 5, 10, 5, 0, 0, }, /* 362 */ + { 5, 21, 12, 0, 0, }, /* 363 */ + { 90, 7, 12, 0, 0, }, /* 364 */ + { 90, 10, 5, 0, 0, }, /* 365 */ + { 90, 12, 3, 0, 0, }, /* 366 */ + { 90, 10, 12, 0, 0, }, /* 367 */ + { 90, 13, 12, 0, 0, }, /* 368 */ + { 90, 21, 12, 0, 0, }, /* 369 */ + { 90, 6, 12, 0, 0, }, /* 370 */ + { 61, 12, 3, 0, 0, }, /* 371 */ + { 61, 10, 5, 0, 0, }, /* 372 */ + { 61, 7, 12, 0, 0, }, /* 373 */ + { 61, 13, 12, 0, 0, }, /* 374 */ + { 61, 21, 12, 0, 0, }, /* 375 */ + { 61, 26, 12, 0, 0, }, /* 376 */ + { 75, 12, 3, 0, 0, }, /* 377 */ + { 75, 10, 5, 0, 0, }, /* 378 */ + { 75, 7, 12, 0, 0, }, /* 379 */ + { 75, 13, 12, 0, 0, }, /* 380 */ + { 92, 7, 12, 0, 0, }, /* 381 */ + { 92, 12, 3, 0, 0, }, /* 382 */ + { 92, 10, 5, 0, 0, }, /* 383 */ + { 92, 21, 12, 0, 0, }, /* 384 */ + { 69, 7, 12, 0, 0, }, /* 385 */ + { 69, 10, 5, 0, 0, }, /* 386 */ + { 69, 12, 3, 0, 0, }, /* 387 */ + { 69, 21, 12, 0, 0, }, /* 388 */ + { 69, 13, 12, 0, 0, }, /* 389 */ + { 72, 13, 12, 0, 0, }, /* 390 */ + { 72, 7, 12, 0, 0, }, /* 391 */ + { 72, 6, 12, 0, 0, }, /* 392 */ + { 72, 21, 12, 0, 0, }, /* 393 */ + { 75, 21, 12, 0, 0, }, /* 394 */ + { 9, 10, 5, 0, 0, }, /* 395 */ + { 9, 7, 12, 0, 0, }, /* 396 */ + { 12, 5, 12, 0, 0, }, /* 397 */ + { 12, 6, 12, 0, 0, }, /* 398 */ + { 33, 5, 12, 0, 35332, }, /* 399 */ + { 33, 5, 12, 0, 3814, }, /* 400 */ + { 33, 9, 12, 63, 1, }, /* 401 */ + { 33, 5, 12, 63, -1, }, /* 402 */ + { 33, 5, 12, 63, -58, }, /* 403 */ + { 33, 9, 12, 0, -7615, }, /* 404 */ + { 19, 5, 12, 0, 8, }, /* 405 */ + { 19, 9, 12, 0, -8, }, /* 406 */ + { 19, 5, 12, 0, 74, }, /* 407 */ + { 19, 5, 12, 0, 86, }, /* 408 */ + { 19, 5, 12, 0, 100, }, /* 409 */ + { 19, 5, 12, 0, 128, }, /* 410 */ + { 19, 5, 12, 0, 112, }, /* 411 */ + { 19, 5, 12, 0, 126, }, /* 412 */ + { 19, 8, 12, 0, -8, }, /* 413 */ + { 19, 5, 12, 0, 9, }, /* 414 */ + { 19, 9, 12, 0, -74, }, /* 415 */ + { 19, 8, 12, 0, -9, }, /* 416 */ + { 19, 5, 12, 21, -7173, }, /* 417 */ + { 19, 9, 12, 0, -86, }, /* 418 */ + { 19, 9, 12, 0, -100, }, /* 419 */ + { 19, 9, 12, 0, -112, }, /* 420 */ + { 19, 9, 12, 0, -128, }, /* 421 */ + { 19, 9, 12, 0, -126, }, /* 422 */ + { 27, 1, 3, 0, 0, }, /* 423 */ + { 9, 27, 2, 0, 0, }, /* 424 */ + { 9, 28, 2, 0, 0, }, /* 425 */ + { 9, 2, 2, 0, 0, }, /* 426 */ + { 27, 11, 3, 0, 0, }, /* 427 */ + { 9, 9, 12, 0, 0, }, /* 428 */ + { 9, 5, 12, 0, 0, }, /* 429 */ + { 19, 9, 12, 67, -7517, }, /* 430 */ + { 33, 9, 12, 71, -8383, }, /* 431 */ + { 33, 9, 12, 75, -8262, }, /* 432 */ + { 33, 9, 12, 0, 28, }, /* 433 */ + { 33, 5, 12, 0, -28, }, /* 434 */ + { 33, 14, 12, 0, 16, }, /* 435 */ + { 33, 14, 12, 0, -16, }, /* 436 */ + { 33, 14, 12, 0, 0, }, /* 437 */ + { 9, 26, 12, 0, 26, }, /* 438 */ + { 9, 26, 12, 0, -26, }, /* 439 */ + { 4, 26, 12, 0, 0, }, /* 440 */ + { 17, 9, 12, 0, 48, }, /* 441 */ + { 17, 5, 12, 0, -48, }, /* 442 */ + { 33, 9, 12, 0, -10743, }, /* 443 */ + { 33, 9, 12, 0, -3814, }, /* 444 */ + { 33, 9, 12, 0, -10727, }, /* 445 */ + { 33, 5, 12, 0, -10795, }, /* 446 */ + { 33, 5, 12, 0, -10792, }, /* 447 */ + { 33, 9, 12, 0, -10780, }, /* 448 */ + { 33, 9, 12, 0, -10749, }, /* 449 */ + { 33, 9, 12, 0, -10783, }, /* 450 */ + { 33, 9, 12, 0, -10782, }, /* 451 */ + { 33, 9, 12, 0, -10815, }, /* 452 */ + { 10, 5, 12, 0, 0, }, /* 453 */ + { 10, 26, 12, 0, 0, }, /* 454 */ + { 10, 12, 3, 0, 0, }, /* 455 */ + { 10, 21, 12, 0, 0, }, /* 456 */ + { 10, 15, 12, 0, 0, }, /* 457 */ + { 16, 5, 12, 0, -7264, }, /* 458 */ + { 58, 7, 12, 0, 0, }, /* 459 */ + { 58, 6, 12, 0, 0, }, /* 460 */ + { 58, 21, 12, 0, 0, }, /* 461 */ + { 58, 12, 3, 0, 0, }, /* 462 */ + { 22, 26, 12, 0, 0, }, /* 463 */ + { 22, 6, 12, 0, 0, }, /* 464 */ + { 22, 14, 12, 0, 0, }, /* 465 */ + { 23, 10, 3, 0, 0, }, /* 466 */ + { 26, 7, 12, 0, 0, }, /* 467 */ + { 26, 6, 12, 0, 0, }, /* 468 */ + { 29, 7, 12, 0, 0, }, /* 469 */ + { 29, 6, 12, 0, 0, }, /* 470 */ + { 3, 7, 12, 0, 0, }, /* 471 */ + { 23, 7, 12, 0, 0, }, /* 472 */ + { 23, 26, 12, 0, 0, }, /* 473 */ + { 29, 26, 12, 0, 0, }, /* 474 */ + { 22, 7, 12, 0, 0, }, /* 475 */ + { 60, 7, 12, 0, 0, }, /* 476 */ + { 60, 6, 12, 0, 0, }, /* 477 */ + { 60, 26, 12, 0, 0, }, /* 478 */ + { 85, 7, 12, 0, 0, }, /* 479 */ + { 85, 6, 12, 0, 0, }, /* 480 */ + { 85, 21, 12, 0, 0, }, /* 481 */ + { 76, 7, 12, 0, 0, }, /* 482 */ + { 76, 6, 12, 0, 0, }, /* 483 */ + { 76, 21, 12, 0, 0, }, /* 484 */ + { 76, 13, 12, 0, 0, }, /* 485 */ + { 12, 7, 12, 0, 0, }, /* 486 */ + { 12, 21, 12, 0, 0, }, /* 487 */ + { 78, 7, 12, 0, 0, }, /* 488 */ + { 78, 14, 12, 0, 0, }, /* 489 */ + { 78, 12, 3, 0, 0, }, /* 490 */ + { 78, 21, 12, 0, 0, }, /* 491 */ + { 33, 9, 12, 0, -35332, }, /* 492 */ + { 33, 9, 12, 0, -42280, }, /* 493 */ + { 33, 9, 12, 0, -42308, }, /* 494 */ + { 48, 7, 12, 0, 0, }, /* 495 */ + { 48, 12, 3, 0, 0, }, /* 496 */ + { 48, 10, 5, 0, 0, }, /* 497 */ + { 48, 26, 12, 0, 0, }, /* 498 */ + { 64, 7, 12, 0, 0, }, /* 499 */ + { 64, 21, 12, 0, 0, }, /* 500 */ + { 74, 10, 5, 0, 0, }, /* 501 */ + { 74, 7, 12, 0, 0, }, /* 502 */ + { 74, 12, 3, 0, 0, }, /* 503 */ + { 74, 21, 12, 0, 0, }, /* 504 */ + { 74, 13, 12, 0, 0, }, /* 505 */ + { 68, 13, 12, 0, 0, }, /* 506 */ + { 68, 7, 12, 0, 0, }, /* 507 */ + { 68, 12, 3, 0, 0, }, /* 508 */ + { 68, 21, 12, 0, 0, }, /* 509 */ + { 73, 7, 12, 0, 0, }, /* 510 */ + { 73, 12, 3, 0, 0, }, /* 511 */ + { 73, 10, 5, 0, 0, }, /* 512 */ + { 73, 21, 12, 0, 0, }, /* 513 */ + { 83, 12, 3, 0, 0, }, /* 514 */ + { 83, 10, 5, 0, 0, }, /* 515 */ + { 83, 7, 12, 0, 0, }, /* 516 */ + { 83, 21, 12, 0, 0, }, /* 517 */ + { 83, 6, 12, 0, 0, }, /* 518 */ + { 83, 13, 12, 0, 0, }, /* 519 */ + { 67, 7, 12, 0, 0, }, /* 520 */ + { 67, 12, 3, 0, 0, }, /* 521 */ + { 67, 10, 5, 0, 0, }, /* 522 */ + { 67, 13, 12, 0, 0, }, /* 523 */ + { 67, 21, 12, 0, 0, }, /* 524 */ + { 38, 6, 12, 0, 0, }, /* 525 */ + { 91, 7, 12, 0, 0, }, /* 526 */ + { 91, 12, 3, 0, 0, }, /* 527 */ + { 91, 6, 12, 0, 0, }, /* 528 */ + { 91, 21, 12, 0, 0, }, /* 529 */ + { 86, 7, 12, 0, 0, }, /* 530 */ + { 86, 10, 5, 0, 0, }, /* 531 */ + { 86, 12, 3, 0, 0, }, /* 532 */ + { 86, 21, 12, 0, 0, }, /* 533 */ + { 86, 6, 12, 0, 0, }, /* 534 */ + { 86, 13, 12, 0, 0, }, /* 535 */ + { 23, 7, 9, 0, 0, }, /* 536 */ + { 23, 7, 10, 0, 0, }, /* 537 */ + { 9, 4, 2, 0, 0, }, /* 538 */ + { 9, 3, 12, 0, 0, }, /* 539 */ + { 25, 25, 12, 0, 0, }, /* 540 */ + { 0, 24, 12, 0, 0, }, /* 541 */ + { 9, 6, 3, 0, 0, }, /* 542 */ + { 35, 7, 12, 0, 0, }, /* 543 */ + { 19, 14, 12, 0, 0, }, /* 544 */ + { 19, 15, 12, 0, 0, }, /* 545 */ + { 19, 26, 12, 0, 0, }, /* 546 */ + { 70, 7, 12, 0, 0, }, /* 547 */ + { 66, 7, 12, 0, 0, }, /* 548 */ + { 41, 7, 12, 0, 0, }, /* 549 */ + { 41, 15, 12, 0, 0, }, /* 550 */ + { 18, 7, 12, 0, 0, }, /* 551 */ + { 18, 14, 12, 0, 0, }, /* 552 */ + { 59, 7, 12, 0, 0, }, /* 553 */ + { 59, 21, 12, 0, 0, }, /* 554 */ + { 42, 7, 12, 0, 0, }, /* 555 */ + { 42, 21, 12, 0, 0, }, /* 556 */ + { 42, 14, 12, 0, 0, }, /* 557 */ + { 13, 9, 12, 0, 40, }, /* 558 */ + { 13, 5, 12, 0, -40, }, /* 559 */ + { 46, 7, 12, 0, 0, }, /* 560 */ + { 44, 7, 12, 0, 0, }, /* 561 */ + { 44, 13, 12, 0, 0, }, /* 562 */ + { 11, 7, 12, 0, 0, }, /* 563 */ + { 80, 7, 12, 0, 0, }, /* 564 */ + { 80, 21, 12, 0, 0, }, /* 565 */ + { 80, 15, 12, 0, 0, }, /* 566 */ + { 65, 7, 12, 0, 0, }, /* 567 */ + { 65, 15, 12, 0, 0, }, /* 568 */ + { 65, 21, 12, 0, 0, }, /* 569 */ + { 71, 7, 12, 0, 0, }, /* 570 */ + { 71, 21, 12, 0, 0, }, /* 571 */ + { 97, 7, 12, 0, 0, }, /* 572 */ + { 96, 7, 12, 0, 0, }, /* 573 */ + { 30, 7, 12, 0, 0, }, /* 574 */ + { 30, 12, 3, 0, 0, }, /* 575 */ + { 30, 15, 12, 0, 0, }, /* 576 */ + { 30, 21, 12, 0, 0, }, /* 577 */ + { 87, 7, 12, 0, 0, }, /* 578 */ + { 87, 15, 12, 0, 0, }, /* 579 */ + { 87, 21, 12, 0, 0, }, /* 580 */ + { 77, 7, 12, 0, 0, }, /* 581 */ + { 77, 21, 12, 0, 0, }, /* 582 */ + { 82, 7, 12, 0, 0, }, /* 583 */ + { 82, 15, 12, 0, 0, }, /* 584 */ + { 81, 7, 12, 0, 0, }, /* 585 */ + { 81, 15, 12, 0, 0, }, /* 586 */ + { 88, 7, 12, 0, 0, }, /* 587 */ + { 0, 15, 12, 0, 0, }, /* 588 */ + { 93, 10, 5, 0, 0, }, /* 589 */ + { 93, 12, 3, 0, 0, }, /* 590 */ + { 93, 7, 12, 0, 0, }, /* 591 */ + { 93, 21, 12, 0, 0, }, /* 592 */ + { 93, 15, 12, 0, 0, }, /* 593 */ + { 93, 13, 12, 0, 0, }, /* 594 */ + { 84, 12, 3, 0, 0, }, /* 595 */ + { 84, 10, 5, 0, 0, }, /* 596 */ + { 84, 7, 12, 0, 0, }, /* 597 */ + { 84, 21, 12, 0, 0, }, /* 598 */ + { 84, 1, 2, 0, 0, }, /* 599 */ + { 100, 7, 12, 0, 0, }, /* 600 */ + { 100, 13, 12, 0, 0, }, /* 601 */ + { 95, 12, 3, 0, 0, }, /* 602 */ + { 95, 7, 12, 0, 0, }, /* 603 */ + { 95, 10, 5, 0, 0, }, /* 604 */ + { 95, 13, 12, 0, 0, }, /* 605 */ + { 95, 21, 12, 0, 0, }, /* 606 */ + { 99, 12, 3, 0, 0, }, /* 607 */ + { 99, 10, 5, 0, 0, }, /* 608 */ + { 99, 7, 12, 0, 0, }, /* 609 */ + { 99, 21, 12, 0, 0, }, /* 610 */ + { 99, 13, 12, 0, 0, }, /* 611 */ + { 101, 7, 12, 0, 0, }, /* 612 */ + { 101, 12, 3, 0, 0, }, /* 613 */ + { 101, 10, 5, 0, 0, }, /* 614 */ + { 101, 13, 12, 0, 0, }, /* 615 */ + { 62, 7, 12, 0, 0, }, /* 616 */ + { 62, 14, 12, 0, 0, }, /* 617 */ + { 62, 21, 12, 0, 0, }, /* 618 */ + { 79, 7, 12, 0, 0, }, /* 619 */ + { 98, 7, 12, 0, 0, }, /* 620 */ + { 98, 10, 5, 0, 0, }, /* 621 */ + { 98, 12, 3, 0, 0, }, /* 622 */ + { 98, 6, 12, 0, 0, }, /* 623 */ + { 9, 10, 3, 0, 0, }, /* 624 */ + { 19, 12, 3, 0, 0, }, /* 625 */ + { 9, 26, 11, 0, 0, }, /* 626 */ + { 26, 26, 12, 0, 0, }, /* 627 */ +}; + +const pcre_uint8 PRIV(ucd_stage1)[] = { /* 8704 bytes */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* U+0000 */ + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* U+0800 */ + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 41, 41, 42, 43, 44, 45, /* U+1000 */ + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* U+1800 */ + 62, 63, 64, 65, 66, 66, 67, 68, 69, 70, 71, 72, 73, 71, 74, 75, /* U+2000 */ + 76, 76, 66, 77, 66, 66, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, /* U+2800 */ + 88, 89, 90, 91, 92, 93, 94, 71, 95, 95, 95, 95, 95, 95, 95, 95, /* U+3000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+3800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+4000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 96, 95, 95, 95, 95, /* U+4800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+5000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+5800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+6000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+6800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+7000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+7800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+8000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+8800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+9000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 97, /* U+9800 */ + 98, 99, 99, 99, 99, 99, 99, 99, 99,100,101,101,102,103,104,105, /* U+A000 */ +106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,114, /* U+A800 */ +115,116,117,118,119,120,114,115,116,117,118,119,120,114,115,116, /* U+B000 */ +117,118,119,120,114,115,116,117,118,119,120,114,115,116,117,118, /* U+B800 */ +119,120,114,115,116,117,118,119,120,114,115,116,117,118,119,120, /* U+C000 */ +114,115,116,117,118,119,120,114,115,116,117,118,119,120,114,115, /* U+C800 */ +116,117,118,119,120,114,115,116,117,118,119,120,114,115,116,121, /* U+D000 */ +122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122, /* U+D800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+E000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+E800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F000 */ +123,123, 95, 95,124,125,126,127,128,128,129,130,131,132,133,134, /* U+F800 */ +135,136,137,138, 79,139,140,141,142,143, 79, 79, 79, 79, 79, 79, /* U+10000 */ +144, 79,145,146,147, 79,148, 79,149, 79, 79, 79,150, 79, 79, 79, /* U+10800 */ +151,152,153,154, 79, 79, 79, 79, 79, 79, 79, 79, 79,155, 79, 79, /* U+11000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+11800 */ +156,156,156,156,156,156,157, 79,158, 79, 79, 79, 79, 79, 79, 79, /* U+12000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+12800 */ +159,159,159,159,159,159,159,159,160, 79, 79, 79, 79, 79, 79, 79, /* U+13000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+13800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+14000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+14800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+15000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+15800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+16000 */ +161,161,161,161,162, 79, 79, 79, 79, 79, 79, 79, 79, 79,163,164, /* U+16800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+17000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+17800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+18000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+18800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+19000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+19800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+1A000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+1A800 */ +165, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+1B000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+1B800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+1C000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+1C800 */ + 71,166,167,168,169, 79,170, 79,171,172,173,174,175,176,177,178, /* U+1D000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+1D800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+1E000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,179,180, 79, 79, /* U+1E800 */ +181,182,183,184,185, 79,186,187,188,189,190,191,192,193,194, 79, /* U+1F000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+1F800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+20000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+20800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+21000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+21800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+22000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+22800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+23000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+23800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+24000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+24800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+25000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+25800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+26000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+26800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+27000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+27800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+28000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+28800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+29000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+29800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,195, 95, 95, /* U+2A000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+2A800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,196, 95, /* U+2B000 */ +197, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+2B800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+2C000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+2C800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+2D000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+2D800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+2E000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+2E800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+2F000 */ + 95, 95, 95, 95,197, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+2F800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+30000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+30800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+31000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+31800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+32000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+32800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+33000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+33800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+34000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+34800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+35000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+35800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+36000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+36800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+37000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+37800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+38000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+38800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+39000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+39800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3A000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3A800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3B000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3B800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3C000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3C800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3D000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3D800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3E000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3E800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3F000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+3F800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+40000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+40800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+41000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+41800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+42000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+42800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+43000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+43800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+44000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+44800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+45000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+45800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+46000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+46800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+47000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+47800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+48000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+48800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+49000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+49800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4A000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4A800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4B000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4B800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4C000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4C800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4D000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4D800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4E000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4E800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4F000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+4F800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+50000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+50800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+51000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+51800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+52000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+52800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+53000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+53800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+54000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+54800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+55000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+55800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+56000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+56800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+57000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+57800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+58000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+58800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+59000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+59800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5A000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5A800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5B000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5B800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5C000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5C800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5D000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5D800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5E000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5E800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5F000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+5F800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+60000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+60800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+61000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+61800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+62000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+62800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+63000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+63800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+64000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+64800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+65000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+65800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+66000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+66800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+67000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+67800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+68000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+68800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+69000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+69800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6A000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6A800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6B000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6B800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6C000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6C800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6D000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6D800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6E000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6E800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6F000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+6F800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+70000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+70800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+71000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+71800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+72000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+72800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+73000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+73800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+74000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+74800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+75000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+75800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+76000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+76800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+77000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+77800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+78000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+78800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+79000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+79800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7A000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7A800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7B000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7B800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7C000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7C800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7D000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7D800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7E000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7E800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7F000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+7F800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+80000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+80800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+81000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+81800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+82000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+82800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+83000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+83800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+84000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+84800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+85000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+85800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+86000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+86800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+87000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+87800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+88000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+88800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+89000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+89800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8A000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8A800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8B000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8B800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8C000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8C800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8D000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8D800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8E000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8E800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8F000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+8F800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+90000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+90800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+91000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+91800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+92000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+92800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+93000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+93800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+94000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+94800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+95000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+95800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+96000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+96800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+97000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+97800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+98000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+98800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+99000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+99800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9A000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9A800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9B000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9B800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9C000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9C800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9D000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9D800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9E000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9E800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9F000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+9F800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A0000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A0800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A1000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A1800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A2000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A2800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A3000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A3800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A4000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A4800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A5000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A5800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A6000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A6800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A7000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A7800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A8000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A8800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A9000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+A9800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AA000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AA800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AB000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AB800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AC000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AC800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AD000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AD800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AE000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AE800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AF000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+AF800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B0000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B0800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B1000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B1800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B2000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B2800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B3000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B3800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B4000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B4800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B5000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B5800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B6000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B6800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B7000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B7800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B8000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B8800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B9000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+B9800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BA000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BA800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BB000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BB800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BC000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BC800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BD000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BD800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BE000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BE800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BF000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+BF800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C0000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C0800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C1000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C1800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C2000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C2800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C3000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C3800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C4000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C4800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C5000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C5800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C6000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C6800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C7000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C7800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C8000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C8800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C9000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+C9800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CA000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CA800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CB000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CB800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CC000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CC800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CD000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CD800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CE000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CE800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CF000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+CF800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D0000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D0800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D1000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D1800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D2000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D2800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D3000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D3800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D4000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D4800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D5000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D5800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D6000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D6800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D7000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D7800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D8000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D8800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D9000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+D9800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DA000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DA800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DB000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DB800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DC000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DC800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DD000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DD800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DE000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DE800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DF000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+DF800 */ +198,199,200,201,199,199,199,199,199,199,199,199,199,199,199,199, /* U+E0000 */ +199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, /* U+E0800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E1000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E1800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E2000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E2800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E3000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E3800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E4000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E4800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E5000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E5800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E6000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E6800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E7000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E7800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E8000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E8800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E9000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+E9800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+EA000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+EA800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+EB000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+EB800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+EC000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+EC800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+ED000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+ED800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+EE000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+EE800 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+EF000 */ + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, /* U+EF800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F0000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F0800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F1000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F1800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F2000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F2800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F3000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F3800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F4000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F4800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F5000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F5800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F6000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F6800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F7000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F7800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F8000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F8800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F9000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F9800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FA000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FA800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FB000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FB800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FC000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FC800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FD000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FD800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FE000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FE800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FF000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,202, /* U+FF800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+100000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+100800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+101000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+101800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+102000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+102800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+103000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+103800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+104000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+104800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+105000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+105800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+106000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+106800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+107000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+107800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+108000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+108800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+109000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+109800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10A000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10A800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10B000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10B800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10C000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10C800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10D000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10D800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10E000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10E800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10F000 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,202, /* U+10F800 */ +}; + +const pcre_uint16 PRIV(ucd_stage2)[] = { /* 51968 bytes, block = 128 */ +/* block 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 4, 4, 4, 5, 4, 4, 4, 6, 7, 4, 8, 4, 9, 4, 4, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 4, 8, 8, 8, 4, + 4, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 11, 11, 11, 11, + 11, 11, 11, 13, 11, 11, 11, 11, 11, 11, 11, 6, 4, 7, 14, 15, + 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 16, 16, 16, + 16, 16, 16, 18, 16, 16, 16, 16, 16, 16, 16, 6, 8, 7, 8, 0, + +/* block 1 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 4, 5, 5, 5, 5, 19, 4, 14, 19, 20, 21, 8, 22, 19, 14, + 19, 8, 23, 23, 14, 24, 4, 4, 14, 23, 20, 25, 23, 23, 23, 4, + 11, 11, 11, 11, 11, 26, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 8, 11, 11, 11, 11, 11, 11, 11, 27, + 16, 16, 16, 16, 16, 28, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 16, 16, 16, 16, 29, + +/* block 2 */ + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 32, 33, 30, 31, 30, 31, 30, 31, 33, 30, 31, 30, 31, 30, 31, 30, + 31, 30, 31, 30, 31, 30, 31, 30, 31, 33, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 34, 30, 31, 30, 31, 30, 31, 35, + +/* block 3 */ + 36, 37, 30, 31, 30, 31, 38, 30, 31, 39, 39, 30, 31, 33, 40, 41, + 42, 30, 31, 39, 43, 44, 45, 46, 30, 31, 47, 33, 45, 48, 49, 50, + 30, 31, 30, 31, 30, 31, 51, 30, 31, 51, 33, 33, 30, 31, 51, 30, + 31, 52, 52, 30, 31, 30, 31, 53, 30, 31, 33, 20, 30, 31, 33, 54, + 20, 20, 20, 20, 55, 56, 57, 58, 59, 60, 61, 62, 63, 30, 31, 30, + 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 64, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 33, 65, 66, 67, 30, 31, 68, 69, 30, 31, 30, 31, 30, 31, 30, 31, + +/* block 4 */ + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 70, 33, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 33, 33, 33, 33, 33, 33, 71, 30, 31, 72, 73, 74, + 74, 30, 31, 75, 76, 77, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 78, 79, 80, 81, 82, 33, 83, 83, 33, 84, 33, 85, 33, 33, 33, 33, + 83, 33, 33, 86, 33, 87, 88, 33, 89, 90, 33, 91, 33, 33, 33, 90, + 33, 92, 93, 33, 33, 94, 33, 33, 33, 33, 33, 33, 33, 95, 33, 33, + +/* block 5 */ + 96, 33, 33, 96, 33, 33, 33, 33, 96, 97, 98, 98, 99, 33, 33, 33, + 33, 33,100, 33, 20, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, +101,101,101,101,101,101,101,101,101,102,102,102,102,102,102,102, +102,102, 14, 14, 14, 14,102,102,102,102,102,102,102,102,102,102, +102,102, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +101,101,101,101,101, 14, 14, 14, 14, 14,103,103,102, 14,102, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + +/* block 6 */ +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,105,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +106,107,106,107,102,108,106,107,109,109,110,111,111,111, 4,109, + +/* block 7 */ +109,109,109,109,108, 14,112, 4,113,113,113,109,114,109,115,115, +116,117,118,117,117,119,117,117,120,121,122,117,123,117,117,117, +124,125,109,126,117,117,127,117,117,128,117,117,129,130,130,130, +116,131,132,131,131,133,131,131,134,135,136,131,137,131,131,131, +138,139,140,141,131,131,142,131,131,143,131,131,144,145,145,146, +147,148,149,149,149,150,151,152,106,107,106,107,106,107,106,107, +106,107,153,154,153,154,153,154,153,154,153,154,153,154,153,154, +155,156,157,116,158,159,160,106,107,161,106,107,116,162,162,162, + +/* block 8 */ +163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, +164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, +164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, +165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, +165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, +166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, + +/* block 9 */ +167,168,169,170,170,104,104,170,171,171,167,168,167,168,167,168, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, +172,167,168,167,168,167,168,167,168,167,168,167,168,167,168,173, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, + +/* block 10 */ +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, +167,168,167,168,167,168,167,168,109,109,109,109,109,109,109,109, +109,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, +174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, +174,174,174,174,174,174,174,109,109,175,176,176,176,176,176,176, +109,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, + +/* block 11 */ +177,177,177,177,177,177,177,178,109, 4,179,109,109,109,109,180, +109,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, +181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, +181,181,181,181,181,181,181,181,181,181,181,181,181,181,182,181, +183,181,181,183,181,181,183,181,109,109,109,109,109,109,109,109, +184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, +184,184,184,184,184,184,184,184,184,184,184,109,109,109,109,109, +184,184,184,183,183,109,109,109,109,109,109,109,109,109,109,109, + +/* block 12 */ +185,185,185,185,185,109,186,186,186,187,187,188, 4,187,189,189, +190,190,190,190,190,190,190,190,190,190,190, 4,109,109,187, 4, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +102,191,191,191,191,191,191,191,191,191,191,104,104,104,104,104, +104,104,104,104,104,104,190,190,190,190,190,190,190,190,190,190, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,187,187,187,187,191,191, +104,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, + +/* block 13 */ +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,187,191,190,190,190,190,190,190,190, 22,189,190, +190,190,190,190,190,192,192,190,190,189,190,190,190,190,191,191, +193,193,193,193,193,193,193,193,193,193,191,191,191,189,189,191, + +/* block 14 */ +194,194,194,194,194,194,194,194,194,194,194,194,194,194,109,195, +196,197,196,196,196,196,196,196,196,196,196,196,196,196,196,196, +196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196, +197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197, +197,197,197,197,197,197,197,197,197,197,197,109,109,196,196,196, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, + +/* block 15 */ +198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198, +198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198, +198,198,198,198,198,198,199,199,199,199,199,199,199,199,199,199, +199,198,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +200,200,200,200,200,200,200,200,200,200,201,201,201,201,201,201, +201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201, +201,201,201,201,201,201,201,201,201,201,201,202,202,202,202,202, +202,202,202,202,203,203,204,205,205,205,203,109,109,109,109,109, + +/* block 16 */ +206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206, +206,206,206,206,206,206,207,207,207,207,208,207,207,207,207,207, +207,207,207,207,208,207,207,207,208,207,207,207,207,207,109,109, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,109, +210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210, +210,210,210,210,210,210,210,210,210,211,211,211,109,109,212,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 17 */ +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +191,109,191,191,191,191,191,191,191,191,191,191,191,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,190,190,190,190,190,190,190,190,190,190,190,190, +190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,109, + +/* block 18 */ +213,213,213,214,215,215,215,215,215,215,215,215,215,215,215,215, +215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215, +215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215, +215,215,215,215,215,215,215,215,215,215,213,214,213,215,214,214, +214,213,213,213,213,213,213,213,213,214,214,214,214,213,214,214, +215,104,104,213,213,213,213,213,215,215,215,215,215,215,215,215, +215,215,213,213, 4, 4,216,216,216,216,216,216,216,216,216,216, +217,218,215,215,215,215,215,215,109,215,215,215,215,215,215,215, + +/* block 19 */ +109,219,220,220,109,221,221,221,221,221,221,221,221,109,109,221, +221,109,109,221,221,221,221,221,221,221,221,221,221,221,221,221, +221,221,221,221,221,221,221,221,221,109,221,221,221,221,221,221, +221,109,221,109,109,109,221,221,221,221,109,109,219,221,222,220, +220,219,219,219,219,109,109,220,220,109,109,220,220,219,221,109, +109,109,109,109,109,109,109,222,109,109,109,109,221,221,109,221, +221,221,219,219,109,109,223,223,223,223,223,223,223,223,223,223, +221,221,224,224,225,225,225,225,225,225,226,224,109,109,109,109, + +/* block 20 */ +109,227,227,228,109,229,229,229,229,229,229,109,109,109,109,229, +229,109,109,229,229,229,229,229,229,229,229,229,229,229,229,229, +229,229,229,229,229,229,229,229,229,109,229,229,229,229,229,229, +229,109,229,229,109,229,229,109,229,229,109,109,227,109,228,228, +228,227,227,109,109,109,109,227,227,109,109,227,227,227,109,109, +109,227,109,109,109,109,109,109,109,229,229,229,229,109,229,109, +109,109,109,109,109,109,230,230,230,230,230,230,230,230,230,230, +227,227,229,229,229,227,109,109,109,109,109,109,109,109,109,109, + +/* block 21 */ +109,231,231,232,109,233,233,233,233,233,233,233,233,233,109,233, +233,233,109,233,233,233,233,233,233,233,233,233,233,233,233,233, +233,233,233,233,233,233,233,233,233,109,233,233,233,233,233,233, +233,109,233,233,109,233,233,233,233,233,109,109,231,233,232,232, +232,231,231,231,231,231,109,231,231,232,109,232,232,231,109,109, +233,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +233,233,231,231,109,109,234,234,234,234,234,234,234,234,234,234, +235,236,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 22 */ +109,237,238,238,109,239,239,239,239,239,239,239,239,109,109,239, +239,109,109,239,239,239,239,239,239,239,239,239,239,239,239,239, +239,239,239,239,239,239,239,239,239,109,239,239,239,239,239,239, +239,109,239,239,109,239,239,239,239,239,109,109,237,239,240,237, +238,237,237,237,237,109,109,238,238,109,109,238,238,237,109,109, +109,109,109,109,109,109,237,240,109,109,109,109,239,239,109,239, +239,239,237,237,109,109,241,241,241,241,241,241,241,241,241,241, +242,239,243,243,243,243,243,243,109,109,109,109,109,109,109,109, + +/* block 23 */ +109,109,244,245,109,245,245,245,245,245,245,109,109,109,245,245, +245,109,245,245,245,245,109,109,109,245,245,109,245,109,245,245, +109,109,109,245,245,109,109,109,245,245,245,109,109,109,245,245, +245,245,245,245,245,245,245,245,245,245,109,109,109,109,246,247, +244,247,247,109,109,109,247,247,247,109,247,247,247,244,109,109, +245,109,109,109,109,109,109,246,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,248,248,248,248,248,248,248,248,248,248, +249,249,249,250,250,250,250,250,250,251,250,109,109,109,109,109, + +/* block 24 */ +109,252,252,252,109,253,253,253,253,253,253,253,253,109,253,253, +253,109,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,109,253,253,253,253,253,253, +253,253,253,253,109,253,253,253,253,253,109,109,109,253,254,254, +254,252,252,252,252,109,254,254,254,109,254,254,254,254,109,109, +109,109,109,109,109,254,254,109,253,253,109,109,109,109,109,109, +253,253,254,254,109,109,255,255,255,255,255,255,255,255,255,255, +109,109,109,109,109,109,109,109,256,256,256,256,256,256,256,257, + +/* block 25 */ +109,109,258,258,109,259,259,259,259,259,259,259,259,109,259,259, +259,109,259,259,259,259,259,259,259,259,259,259,259,259,259,259, +259,259,259,259,259,259,259,259,259,109,259,259,259,259,259,259, +259,259,259,259,109,259,259,259,259,259,109,109,260,259,258,260, +258,258,261,258,258,109,260,258,258,109,258,258,260,260,109,109, +109,109,109,109,109,261,261,109,109,109,109,109,109,109,259,109, +259,259,260,260,109,109,262,262,262,262,262,262,262,262,262,262, +109,259,259,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 26 */ +109,109,263,263,109,264,264,264,264,264,264,264,264,109,264,264, +264,109,264,264,264,264,264,264,264,264,264,264,264,264,264,264, +264,264,264,264,264,264,264,264,264,264,264,264,264,264,264,264, +264,264,264,264,264,264,264,264,264,264,264,109,109,264,265,263, +263,266,266,266,266,109,263,263,263,109,263,263,263,266,264,109, +109,109,109,109,109,109,109,265,109,109,109,109,109,109,109,109, +264,264,266,266,109,109,267,267,267,267,267,267,267,267,267,267, +268,268,268,268,268,268,109,109,109,269,264,264,264,264,264,264, + +/* block 27 */ +109,109,270,270,109,271,271,271,271,271,271,271,271,271,271,271, +271,271,271,271,271,271,271,109,109,109,271,271,271,271,271,271, +271,271,271,271,271,271,271,271,271,271,271,271,271,271,271,271, +271,271,109,271,271,271,271,271,271,271,271,271,109,271,109,109, +271,271,271,271,271,271,271,109,109,109,272,109,109,109,109,273, +270,270,272,272,272,109,272,109,270,270,270,270,270,270,270,273, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,270,270,274,109,109,109,109,109,109,109,109,109,109,109, + +/* block 28 */ +109,275,275,275,275,275,275,275,275,275,275,275,275,275,275,275, +275,275,275,275,275,275,275,275,275,275,275,275,275,275,275,275, +275,275,275,275,275,275,275,275,275,275,275,275,275,275,275,275, +275,276,275,277,276,276,276,276,276,276,276,109,109,109,109, 5, +275,275,275,275,275,275,278,276,276,276,276,276,276,276,276,279, +280,280,280,280,280,280,280,280,280,280,279,279,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 29 */ +109,281,281,109,281,109,109,281,281,109,281,109,109,281,109,109, +109,109,109,109,281,281,281,281,109,281,281,281,281,281,281,281, +109,281,281,281,109,281,109,281,109,109,281,281,109,281,281,281, +281,282,281,283,282,282,282,282,282,282,109,282,282,281,109,109, +281,281,281,281,281,109,284,109,282,282,282,282,282,282,109,109, +285,285,285,285,285,285,285,285,285,285,109,109,281,281,281,281, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 30 */ +286,287,287,287,288,288,288,288,288,288,288,288,288,288,288,288, +288,288,288,287,288,287,287,287,289,289,287,287,287,287,287,287, +290,290,290,290,290,290,290,290,290,290,291,291,291,291,291,291, +291,291,291,291,287,289,287,289,287,289,292,293,292,293,294,294, +286,286,286,286,286,286,286,286,109,286,286,286,286,286,286,286, +286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286, +286,286,286,286,286,286,286,286,286,286,286,286,286,109,109,109, +109,289,289,289,289,289,289,289,289,289,289,289,289,289,289,294, + +/* block 31 */ +289,289,289,289,289,288,289,289,286,286,286,286,286,289,289,289, +289,289,289,289,289,289,289,289,109,289,289,289,289,289,289,289, +289,289,289,289,289,289,289,289,289,289,289,289,289,289,289,289, +289,289,289,289,289,289,289,289,289,289,289,289,289,109,287,287, +287,287,287,287,287,287,289,287,287,287,287,287,287,109,287,287, +288,288,288,288,288, 19, 19, 19, 19,288,288,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 32 */ +295,295,295,295,295,295,295,295,295,295,295,295,295,295,295,295, +295,295,295,295,295,295,295,295,295,295,295,295,295,295,295,295, +295,295,295,295,295,295,295,295,295,295,295,296,296,297,297,297, +297,298,297,297,297,297,297,297,296,297,297,298,298,297,297,295, +299,299,299,299,299,299,299,299,299,299,300,300,300,300,300,300, +295,295,295,295,295,295,298,298,297,297,295,295,295,295,297,297, +297,295,296,296,296,295,295,296,296,296,296,296,296,296,295,295, +295,297,297,297,297,295,295,295,295,295,295,295,295,295,295,295, + +/* block 33 */ +295,295,297,296,298,297,297,296,296,296,296,296,296,297,295,296, +299,299,299,299,299,299,299,299,299,299,296,296,296,297,301,301, +302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302, +302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302, +302,302,302,302,302,302,109,302,109,109,109,109,109,302,109,109, +303,303,303,303,303,303,303,303,303,303,303,303,303,303,303,303, +303,303,303,303,303,303,303,303,303,303,303,303,303,303,303,303, +303,303,303,303,303,303,303,303,303,303,303, 4,304,303,303,303, + +/* block 34 */ +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, +306,306,306,306,306,306,306,306,306,306,306,306,306,306,306,306, +306,306,306,306,306,306,306,306,306,306,306,306,306,306,306,306, + +/* block 35 */ +306,306,306,306,306,306,306,306,306,306,306,306,306,306,306,306, +306,306,306,306,306,306,306,306,306,306,306,306,306,306,306,306, +306,306,306,306,306,306,306,306,307,307,307,307,307,307,307,307, +307,307,307,307,307,307,307,307,307,307,307,307,307,307,307,307, +307,307,307,307,307,307,307,307,307,307,307,307,307,307,307,307, +307,307,307,307,307,307,307,307,307,307,307,307,307,307,307,307, +307,307,307,307,307,307,307,307,307,307,307,307,307,307,307,307, +307,307,307,307,307,307,307,307,307,307,307,307,307,307,307,307, + +/* block 36 */ +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,109,308,308,308,308,109,109, +308,308,308,308,308,308,308,109,308,109,308,308,308,308,109,109, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, + +/* block 37 */ +308,308,308,308,308,308,308,308,308,109,308,308,308,308,109,109, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,109,308,308,308,308,109,109,308,308,308,308,308,308,308,109, +308,109,308,308,308,308,109,109,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,109,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, + +/* block 38 */ +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,109,308,308,308,308,109,109,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,308,308,308,308,109,109,309,309,309, +310,310,310,310,310,310,310,310,310,311,311,311,311,311,311,311, +311,311,311,311,311,311,311,311,311,311,311,311,311,109,109,109, + +/* block 39 */ +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +312,312,312,312,312,312,312,312,312,312,109,109,109,109,109,109, +313,313,313,313,313,313,313,313,313,313,313,313,313,313,313,313, +313,313,313,313,313,313,313,313,313,313,313,313,313,313,313,313, +313,313,313,313,313,313,313,313,313,313,313,313,313,313,313,313, +313,313,313,313,313,313,313,313,313,313,313,313,313,313,313,313, +313,313,313,313,313,313,313,313,313,313,313,313,313,313,313,313, +313,313,313,313,313,109,109,109,109,109,109,109,109,109,109,109, + +/* block 40 */ +314,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, + +/* block 41 */ +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, + +/* block 42 */ +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,316,316,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, + +/* block 43 */ +317,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,319,320,109,109,109, +321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321, +321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321, +321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321, +321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321, +321,321,321,321,321,321,321,321,321,321,321, 4, 4, 4,322,322, +322,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 44 */ +323,323,323,323,323,323,323,323,323,323,323,323,323,109,323,323, +323,323,324,324,324,109,109,109,109,109,109,109,109,109,109,109, +325,325,325,325,325,325,325,325,325,325,325,325,325,325,325,325, +325,325,326,326,326, 4, 4,109,109,109,109,109,109,109,109,109, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,328,328,109,109,109,109,109,109,109,109,109,109,109,109, +329,329,329,329,329,329,329,329,329,329,329,329,329,109,329,329, +329,109,330,330,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 45 */ +331,331,331,331,331,331,331,331,331,331,331,331,331,331,331,331, +331,331,331,331,331,331,331,331,331,331,331,331,331,331,331,331, +331,331,331,331,331,331,331,331,331,331,331,331,331,331,331,331, +331,331,331,331,332,332,333,332,332,332,332,332,332,332,333,333, +333,333,333,333,333,333,332,333,333,332,332,332,332,332,332,332, +332,332,332,332,334,334,334,335,334,334,334,336,331,332,109,109, +337,337,337,337,337,337,337,337,337,337,109,109,109,109,109,109, +338,338,338,338,338,338,338,338,338,338,109,109,109,109,109,109, + +/* block 46 */ +339,339, 4, 4,339, 4,340,339,339,339,339,341,341,341,342,109, +343,343,343,343,343,343,343,343,343,343,109,109,109,109,109,109, +344,344,344,344,344,344,344,344,344,344,344,344,344,344,344,344, +344,344,344,344,344,344,344,344,344,344,344,344,344,344,344,344, +344,344,344,345,344,344,344,344,344,344,344,344,344,344,344,344, +344,344,344,344,344,344,344,344,344,344,344,344,344,344,344,344, +344,344,344,344,344,344,344,344,344,344,344,344,344,344,344,344, +344,344,344,344,344,344,344,344,109,109,109,109,109,109,109,109, + +/* block 47 */ +344,344,344,344,344,344,344,344,344,344,344,344,344,344,344,344, +344,344,344,344,344,344,344,344,344,344,344,344,344,344,344,344, +344,344,344,344,344,344,344,344,344,341,344,109,109,109,109,109, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,109,109,109,109,109,109,109,109,109,109, + +/* block 48 */ +346,346,346,346,346,346,346,346,346,346,346,346,346,346,346,346, +346,346,346,346,346,346,346,346,346,346,346,346,346,109,109,109, +347,347,347,348,348,348,348,347,347,348,348,348,109,109,109,109, +348,348,347,348,348,348,348,348,348,347,347,347,109,109,109,109, +349,109,109,109,350,350,351,351,351,351,351,351,351,351,351,351, +352,352,352,352,352,352,352,352,352,352,352,352,352,352,352,352, +352,352,352,352,352,352,352,352,352,352,352,352,352,352,109,109, +352,352,352,352,352,109,109,109,109,109,109,109,109,109,109,109, + +/* block 49 */ +353,353,353,353,353,353,353,353,353,353,353,353,353,353,353,353, +353,353,353,353,353,353,353,353,353,353,353,353,353,353,353,353, +353,353,353,353,353,353,353,353,353,353,353,353,109,109,109,109, +354,354,354,354,354,355,355,355,354,354,355,354,354,354,354,354, +354,353,353,353,353,353,353,353,354,354,109,109,109,109,109,109, +356,356,356,356,356,356,356,356,356,356,357,109,109,109,358,358, +359,359,359,359,359,359,359,359,359,359,359,359,359,359,359,359, +359,359,359,359,359,359,359,359,359,359,359,359,359,359,359,359, + +/* block 50 */ +360,360,360,360,360,360,360,360,360,360,360,360,360,360,360,360, +360,360,360,360,360,360,360,361,361,362,362,362,109,109,363,363, +364,364,364,364,364,364,364,364,364,364,364,364,364,364,364,364, +364,364,364,364,364,364,364,364,364,364,364,364,364,364,364,364, +364,364,364,364,364,364,364,364,364,364,364,364,364,364,364,364, +364,364,364,364,364,365,366,365,366,366,366,366,366,366,366,109, +366,367,366,367,367,366,366,366,366,366,366,366,366,365,365,365, +365,365,365,366,366,366,366,366,366,366,366,366,366,109,109,366, + +/* block 51 */ +368,368,368,368,368,368,368,368,368,368,109,109,109,109,109,109, +368,368,368,368,368,368,368,368,368,368,109,109,109,109,109,109, +369,369,369,369,369,369,369,370,369,369,369,369,369,369,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 52 */ +371,371,371,371,372,373,373,373,373,373,373,373,373,373,373,373, +373,373,373,373,373,373,373,373,373,373,373,373,373,373,373,373, +373,373,373,373,373,373,373,373,373,373,373,373,373,373,373,373, +373,373,373,373,371,372,371,371,371,371,371,372,371,372,372,372, +372,372,371,372,372,373,373,373,373,373,373,373,109,109,109,109, +374,374,374,374,374,374,374,374,374,374,375,375,375,375,375,375, +375,376,376,376,376,376,376,376,376,376,376,371,371,371,371,371, +371,371,371,371,376,376,376,376,376,376,376,376,376,109,109,109, + +/* block 53 */ +377,377,378,379,379,379,379,379,379,379,379,379,379,379,379,379, +379,379,379,379,379,379,379,379,379,379,379,379,379,379,379,379, +379,378,377,377,377,377,378,378,377,377,378,377,378,378,379,379, +380,380,380,380,380,380,380,380,380,380,379,379,379,379,379,379, +381,381,381,381,381,381,381,381,381,381,381,381,381,381,381,381, +381,381,381,381,381,381,381,381,381,381,381,381,381,381,381,381, +381,381,381,381,381,381,382,383,382,382,383,383,383,382,383,382, +382,382,383,383,109,109,109,109,109,109,109,109,384,384,384,384, + +/* block 54 */ +385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385, +385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385, +385,385,385,385,386,386,386,386,386,386,386,386,387,387,387,387, +387,387,387,387,386,386,387,387,109,109,109,388,388,388,388,388, +389,389,389,389,389,389,389,389,389,389,109,109,109,385,385,385, +390,390,390,390,390,390,390,390,390,390,391,391,391,391,391,391, +391,391,391,391,391,391,391,391,391,391,391,391,391,391,391,391, +391,391,391,391,391,391,391,391,392,392,392,392,392,392,393,393, + +/* block 55 */ +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +394,394,394,394,394,394,394,394,109,109,109,109,109,109,109,109, +104,104,104, 4,104,104,104,104,104,104,104,104,104,104,104,104, +104,395,104,104,104,104,104,104,104,396,396,396,396,104,396,396, +396,396,395,395,104,396,396,109,109,109,109,109,109,109,109,109, + +/* block 56 */ + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33,116,116,116,116,116,397,101,101,101,101, +101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, +101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, +101,101,101,101,101,101,101,101,101,101,101,101,101,110,110,110, +110,110,101,101,101,101,110,110,110,110,110, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33,398,399, 33, 33, 33,400, 33, 33, + +/* block 57 */ + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,101,101,101,101,101, +101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, +101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,110, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,104,104,104,104, + +/* block 58 */ + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, +401,402, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + +/* block 59 */ + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 33, 33, 33, 33, 33,403, 33, 33,404, 33, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + +/* block 60 */ +405,405,405,405,405,405,405,405,406,406,406,406,406,406,406,406, +405,405,405,405,405,405,109,109,406,406,406,406,406,406,109,109, +405,405,405,405,405,405,405,405,406,406,406,406,406,406,406,406, +405,405,405,405,405,405,405,405,406,406,406,406,406,406,406,406, +405,405,405,405,405,405,109,109,406,406,406,406,406,406,109,109, +116,405,116,405,116,405,116,405,109,406,109,406,109,406,109,406, +405,405,405,405,405,405,405,405,406,406,406,406,406,406,406,406, +407,407,408,408,408,408,409,409,410,410,411,411,412,412,109,109, + +/* block 61 */ +405,405,405,405,405,405,405,405,413,413,413,413,413,413,413,413, +405,405,405,405,405,405,405,405,413,413,413,413,413,413,413,413, +405,405,405,405,405,405,405,405,413,413,413,413,413,413,413,413, +405,405,116,414,116,109,116,116,406,406,415,415,416,108,417,108, +108,108,116,414,116,109,116,116,418,418,418,418,416,108,108,108, +405,405,116,116,109,109,116,116,406,406,419,419,109,108,108,108, +405,405,116,116,116,157,116,116,406,406,420,420,161,108,108,108, +109,109,116,414,116,109,116,116,421,421,422,422,416,108,108,109, + +/* block 62 */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 22,423,423, 22, 22, + 9, 9, 9, 9, 9, 9, 4, 4, 21, 25, 6, 21, 21, 25, 6, 21, + 4, 4, 4, 4, 4, 4, 4, 4,424,425, 22, 22, 22, 22, 22, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 21, 25, 4, 4, 4, 4, 15, + 15, 4, 4, 4, 8, 6, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 8, 4, 15, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, + 22, 22, 22, 22, 22,426,426,426,426,426, 22, 22, 22, 22, 22, 22, + 23,101,109,109, 23, 23, 23, 23, 23, 23, 8, 8, 8, 6, 7,101, + +/* block 63 */ + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 8, 8, 8, 6, 7,109, +101,101,101,101,101,101,101,101,101,101,101,101,101,109,109,109, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +104,104,104,104,104,104,104,104,104,104,104,104,104,427,427,427, +427,104,427,427,427,104,104,104,104,104,104,104,104,104,104,104, +104,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 64 */ + 19, 19,428, 19, 19, 19, 19,428, 19, 19,429,428,428,428,429,429, +428,428,428,429, 19,428, 19, 19, 8,428,428,428,428,428, 19, 19, + 19, 19, 19, 19,428, 19,430, 19,428, 19,431,432,428,428, 19,429, +428,428,433,428,429,396,396,396,396,429, 19, 19,429,429,428,428, + 8, 8, 8, 8, 8,428,429,429,429,429, 19, 8, 19, 19,434, 19, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, +435,435,435,435,435,435,435,435,435,435,435,435,435,435,435,435, +436,436,436,436,436,436,436,436,436,436,436,436,436,436,436,436, + +/* block 65 */ +437,437,437, 30, 31,437,437,437,437, 23,109,109,109,109,109,109, + 8, 8, 8, 8, 8, 19, 19, 19, 19, 19, 8, 8, 19, 19, 19, 19, + 8, 19, 19, 8, 19, 19, 8, 19, 19, 19, 19, 19, 19, 19, 8, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 8, 8, + 19, 19, 8, 19, 8, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + +/* block 66 */ + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + +/* block 67 */ + 19, 19, 19, 19, 19, 19, 19, 19, 8, 8, 8, 8, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 8, 8, 19, 19, 19, 19, 19, 19, 19, 6, 7, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 8, 19, 19, 19, + +/* block 68 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 8, 8, 8, 8, + 8, 8, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 69 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + +/* block 70 */ + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + +/* block 71 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + +/* block 72 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 8, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 8, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 8, 8, 8, 8, 8, 8, 8, 8, + +/* block 73 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 8, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + +/* block 74 */ +109, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 6, 7, 6, 7, 6, 7, 6, 7, + 6, 7, 6, 7, 6, 7, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + +/* block 75 */ + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 8, 8, 8, 8, 8, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + +/* block 76 */ +440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440, +440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440, +440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440, +440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440, +440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440, +440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440, +440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440, +440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440, + +/* block 77 */ + 8, 8, 8, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, + 7, 6, 7, 6, 7, 6, 7, 6, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 6, 7, 6, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 7, 8, 8, + +/* block 78 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 19, 19, 8, 8, 8, 8, 8, 8,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 79 */ +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 80 */ +441,441,441,441,441,441,441,441,441,441,441,441,441,441,441,441, +441,441,441,441,441,441,441,441,441,441,441,441,441,441,441,441, +441,441,441,441,441,441,441,441,441,441,441,441,441,441,441,109, +442,442,442,442,442,442,442,442,442,442,442,442,442,442,442,442, +442,442,442,442,442,442,442,442,442,442,442,442,442,442,442,442, +442,442,442,442,442,442,442,442,442,442,442,442,442,442,442,109, + 30, 31,443,444,445,446,447, 30, 31, 30, 31, 30, 31,448,449,450, +451, 33, 30, 31, 33, 30, 31, 33, 33, 33, 33, 33,101,101,452,452, + +/* block 81 */ +153,154,153,154,153,154,153,154,153,154,153,154,153,154,153,154, +153,154,153,154,153,154,153,154,153,154,153,154,153,154,153,154, +153,154,153,154,153,154,153,154,153,154,153,154,153,154,153,154, +153,154,153,154,153,154,153,154,153,154,153,154,153,154,153,154, +153,154,153,154,153,154,153,154,153,154,153,154,153,154,153,154, +153,154,153,154,153,154,153,154,153,154,153,154,153,154,153,154, +153,154,153,154,453,454,454,454,454,454,454,153,154,153,154,455, +455,455,153,154,109,109,109,109,109,456,456,456,456,457,456,456, + +/* block 82 */ +458,458,458,458,458,458,458,458,458,458,458,458,458,458,458,458, +458,458,458,458,458,458,458,458,458,458,458,458,458,458,458,458, +458,458,458,458,458,458,109,458,109,109,109,109,109,458,109,109, +459,459,459,459,459,459,459,459,459,459,459,459,459,459,459,459, +459,459,459,459,459,459,459,459,459,459,459,459,459,459,459,459, +459,459,459,459,459,459,459,459,459,459,459,459,459,459,459,459, +459,459,459,459,459,459,459,459,109,109,109,109,109,109,109,460, +461,109,109,109,109,109,109,109,109,109,109,109,109,109,109,462, + +/* block 83 */ +308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308, +308,308,308,308,308,308,308,109,109,109,109,109,109,109,109,109, +308,308,308,308,308,308,308,109,308,308,308,308,308,308,308,109, +308,308,308,308,308,308,308,109,308,308,308,308,308,308,308,109, +308,308,308,308,308,308,308,109,308,308,308,308,308,308,308,109, +308,308,308,308,308,308,308,109,308,308,308,308,308,308,308,109, +170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, +170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + +/* block 84 */ + 4, 4, 21, 25, 21, 25, 4, 4, 4, 21, 25, 4, 21, 25, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 9, 4, 4, 9, 4, 21, 25, 4, 4, + 21, 25, 6, 7, 6, 7, 6, 7, 6, 7, 4, 4, 4, 4, 4,102, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9, 9,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 85 */ +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,109,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 86 */ +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, + +/* block 87 */ +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, +463,463,463,463,463,463,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109,109,109, + +/* block 88 */ + 3, 4, 4, 4, 19,464,396,465, 6, 7, 6, 7, 6, 7, 6, 7, + 6, 7, 19, 19, 6, 7, 6, 7, 6, 7, 6, 7, 9, 6, 7, 7, + 19,465,465,465,465,465,465,465,465,465,104,104,104,104,466,466, + 9,102,102,102,102,102, 19, 19,465,465,465,464,396, 4, 19, 19, +109,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467, +467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467, +467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467, +467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467, + +/* block 89 */ +467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467, +467,467,467,467,467,467,467,109,109,104,104, 14, 14,468,468,467, + 9,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469, +469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469, +469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469, +469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469, +469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469, +469,469,469,469,469,469,469,469,469,469,469, 4,102,470,470,469, + +/* block 90 */ +109,109,109,109,109,471,471,471,471,471,471,471,471,471,471,471, +471,471,471,471,471,471,471,471,471,471,471,471,471,471,471,471, +471,471,471,471,471,471,471,471,471,471,471,471,471,471,109,109, +109,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, +472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, +472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, +472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, +472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, + +/* block 91 */ +472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,109, + 19, 19, 23, 23, 23, 23, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, +471,471,471,471,471,471,471,471,471,471,471,471,471,471,471,471, +471,471,471,471,471,471,471,471,471,471,471,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19,109,109,109,109,109,109,109,109,109,109,109,109, +469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469, + +/* block 92 */ +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,109, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 23, 23, 23, 23, 23, 23, 23, 23, + 19, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, 19, + +/* block 93 */ + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, +474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474, +474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474, +474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,109, + +/* block 94 */ +474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474, +474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474, +474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474, +474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474, +474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474, +474,474,474,474,474,474,474,474, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + +/* block 95 */ +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, + +/* block 96 */ +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,109,109,109,109,109,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + +/* block 97 */ +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 98 */ +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,477,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, + +/* block 99 */ +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, + +/* block 100 */ +476,476,476,476,476,476,476,476,476,476,476,476,476,109,109,109, +478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478, +478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478, +478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478, +478,478,478,478,478,478,478,109,109,109,109,109,109,109,109,109, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,480,480,480,480,480,480,481,481, + +/* block 101 */ +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, + +/* block 102 */ +482,482,482,482,482,482,482,482,482,482,482,482,483,484,484,484, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +485,485,485,485,485,485,485,485,485,485,482,482,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, +167,168,167,168,167,168,167,168,167,168,167,168,167,168,486,170, +171,171,171,487,170,170,170,170,170,170,170,170,170,170,487,398, + +/* block 103 */ +167,168,167,168,167,168,167,168,167,168,167,168,167,168,167,168, +167,168,167,168,167,168,167,168,109,109,109,109,109,109,109,170, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,489,489,489,489,489,489,489,489,489,489, +490,490,491,491,491,491,491,491,109,109,109,109,109,109,109,109, + +/* block 104 */ + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14,102,102,102,102,102,102,102,102,102, + 14, 14, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 33, 33, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, +101, 33, 33, 33, 33, 33, 33, 33, 33, 30, 31, 30, 31,492, 30, 31, + +/* block 105 */ + 30, 31, 30, 31, 30, 31, 30, 31,102, 14, 14, 30, 31,493, 33,109, + 30, 31, 30, 31,109,109,109,109,109,109,109,109,109,109,109,109, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,494,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,101,101, 33, 20, 20, 20, 20, 20, + +/* block 106 */ +495,495,496,495,495,495,496,495,495,495,495,496,495,495,495,495, +495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495, +495,495,495,497,497,496,496,497,498,498,498,498,109,109,109,109, + 23, 23, 23, 23, 23, 23, 19, 19, 5, 19,109,109,109,109,109,109, +499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,499, +499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,499, +499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,499, +499,499,499,499,500,500,500,500,109,109,109,109,109,109,109,109, + +/* block 107 */ +501,501,502,502,502,502,502,502,502,502,502,502,502,502,502,502, +502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,502, +502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,502, +502,502,502,502,501,501,501,501,501,501,501,501,501,501,501,501, +501,501,501,501,503,109,109,109,109,109,109,109,109,109,504,504, +505,505,505,505,505,505,505,505,505,505,109,109,109,109,109,109, +213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213, +213,213,215,215,215,215,215,215,217,217,217,215,109,109,109,109, + +/* block 108 */ +506,506,506,506,506,506,506,506,506,506,507,507,507,507,507,507, +507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507, +507,507,507,507,507,507,508,508,508,508,508,508,508,508,509,509, +510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510, +510,510,510,510,510,510,510,511,511,511,511,511,511,511,511,511, +511,511,512,512,109,109,109,109,109,109,109,109,109,109,109,513, +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, +305,305,305,305,305,305,305,305,305,305,305,305,305,109,109,109, + +/* block 109 */ +514,514,514,515,516,516,516,516,516,516,516,516,516,516,516,516, +516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, +516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, +516,516,516,514,515,515,514,514,514,514,515,515,514,515,515,515, +515,517,517,517,517,517,517,517,517,517,517,517,517,517,109,518, +519,519,519,519,519,519,519,519,519,519,109,109,109,109,517,517, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 110 */ +520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520, +520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520, +520,520,520,520,520,520,520,520,520,521,521,521,521,521,521,522, +522,521,521,522,522,521,521,109,109,109,109,109,109,109,109,109, +520,520,520,521,520,520,520,520,520,520,520,520,521,522,109,109, +523,523,523,523,523,523,523,523,523,523,109,109,524,524,524,524, +295,295,295,295,295,295,295,295,295,295,295,295,295,295,295,295, +525,295,295,295,295,295,295,301,301,301,295,296,109,109,109,109, + +/* block 111 */ +526,526,526,526,526,526,526,526,526,526,526,526,526,526,526,526, +526,526,526,526,526,526,526,526,526,526,526,526,526,526,526,526, +526,526,526,526,526,526,526,526,526,526,526,526,526,526,526,526, +527,526,527,527,527,526,526,527,527,526,526,526,526,526,527,527, +526,527,526,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,526,526,528,529,529, +530,530,530,530,530,530,530,530,530,530,530,531,532,532,531,531, +533,533,530,534,534,531,532,109,109,109,109,109,109,109,109,109, + +/* block 112 */ +109,308,308,308,308,308,308,109,109,308,308,308,308,308,308,109, +109,308,308,308,308,308,308,109,109,109,109,109,109,109,109,109, +308,308,308,308,308,308,308,109,308,308,308,308,308,308,308,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 113 */ +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530, +530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530, +530,530,530,531,531,532,531,531,532,531,531,533,531,532,109,109, +535,535,535,535,535,535,535,535,535,535,109,109,109,109,109,109, + +/* block 114 */ +536,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,536,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,536,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,536,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +536,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, + +/* block 115 */ +537,537,537,537,537,537,537,537,537,537,537,537,536,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,536,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,536,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +536,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,536,537,537,537, + +/* block 116 */ +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,536,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,536,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +536,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,536,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, + +/* block 117 */ +537,537,537,537,537,537,537,537,536,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,536,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +536,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,536,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,536,537,537,537,537,537,537,537, + +/* block 118 */ +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,536,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +536,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,536,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,536,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, + +/* block 119 */ +537,537,537,537,536,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +536,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,536,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,536,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,536,537,537,537,537,537,537,537,537,537,537,537, + +/* block 120 */ +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +536,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,536,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,536,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,536,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, + +/* block 121 */ +537,537,537,537,537,537,537,537,536,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,109,109,109,109,109,109,109,109,109,109,109,109, +306,306,306,306,306,306,306,306,306,306,306,306,306,306,306,306, +306,306,306,306,306,306,306,109,109,109,109,307,307,307,307,307, +307,307,307,307,307,307,307,307,307,307,307,307,307,307,307,307, +307,307,307,307,307,307,307,307,307,307,307,307,307,307,307,307, +307,307,307,307,307,307,307,307,307,307,307,307,109,109,109,109, + +/* block 122 */ +538,538,538,538,538,538,538,538,538,538,538,538,538,538,538,538, +538,538,538,538,538,538,538,538,538,538,538,538,538,538,538,538, +538,538,538,538,538,538,538,538,538,538,538,538,538,538,538,538, +538,538,538,538,538,538,538,538,538,538,538,538,538,538,538,538, +538,538,538,538,538,538,538,538,538,538,538,538,538,538,538,538, +538,538,538,538,538,538,538,538,538,538,538,538,538,538,538,538, +538,538,538,538,538,538,538,538,538,538,538,538,538,538,538,538, +538,538,538,538,538,538,538,538,538,538,538,538,538,538,538,538, + +/* block 123 */ +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, + +/* block 124 */ +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,109,109, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, + +/* block 125 */ +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 126 */ + 33, 33, 33, 33, 33, 33, 33,109,109,109,109,109,109,109,109,109, +109,109,109,178,178,178,178,178,109,109,109,109,109,184,181,184, +184,184,184,184,184,184,184,184,184,540,184,184,184,184,184,184, +184,184,184,184,184,184,184,109,184,184,184,184,184,109,184,109, +184,184,109,184,184,109,184,184,184,184,184,184,184,184,184,184, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, + +/* block 127 */ +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,541,541,541,541,541,541,541,541,541,541,541,541,541,541, +541,541,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, + +/* block 128 */ +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, + +/* block 129 */ +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191, 6, 7, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, + +/* block 130 */ +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +109,109,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +191,191,191,191,191,191,191,191,191,191,191,191,188, 19,109,109, + +/* block 131 */ +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, + 4, 4, 4, 4, 4, 4, 4, 6, 7, 4,109,109,109,109,109,109, +104,104,104,104,104,104,104,109,109,109,109,109,109,109,109,109, + 4, 9, 9, 15, 15, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, + 7, 6, 7, 6, 7, 4, 4, 6, 7, 4, 4, 4, 4, 15, 15, 15, + 4, 4, 4,109, 4, 4, 4, 4, 9, 6, 7, 6, 7, 6, 7, 4, + 4, 4, 8, 9, 8, 8, 8,109, 4, 5, 4, 4,109,109,109,109, +191,191,191,191,191,109,191,191,191,191,191,191,191,191,191,191, + +/* block 132 */ +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,109,109, 22, + +/* block 133 */ +109, 4, 4, 4, 5, 4, 4, 4, 6, 7, 4, 8, 4, 9, 4, 4, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 4, 8, 8, 8, 4, + 4, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 6, 4, 7, 14, 15, + 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 6, 8, 7, 8, 6, + 7, 4, 6, 7, 4, 4,469,469,469,469,469,469,469,469,469,469, +102,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469, + +/* block 134 */ +469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469, +469,469,469,469,469,469,469,469,469,469,469,469,469,469,542,542, +472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, +472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,109, +109,109,472,472,472,472,472,472,109,109,472,472,472,472,472,472, +109,109,472,472,472,472,472,472,109,109,472,472,472,109,109,109, + 5, 5, 8, 14, 19, 5, 5,109, 19, 8, 8, 8, 8, 19, 19,109, +426,426,426,426,426,426,426,426,426, 22, 22, 22, 19, 19,109,109, + +/* block 135 */ +543,543,543,543,543,543,543,543,543,543,543,543,109,543,543,543, +543,543,543,543,543,543,543,543,543,543,543,543,543,543,543,543, +543,543,543,543,543,543,543,109,543,543,543,543,543,543,543,543, +543,543,543,543,543,543,543,543,543,543,543,109,543,543,109,543, +543,543,543,543,543,543,543,543,543,543,543,543,543,543,109,109, +543,543,543,543,543,543,543,543,543,543,543,543,543,543,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 136 */ +543,543,543,543,543,543,543,543,543,543,543,543,543,543,543,543, +543,543,543,543,543,543,543,543,543,543,543,543,543,543,543,543, +543,543,543,543,543,543,543,543,543,543,543,543,543,543,543,543, +543,543,543,543,543,543,543,543,543,543,543,543,543,543,543,543, +543,543,543,543,543,543,543,543,543,543,543,543,543,543,543,543, +543,543,543,543,543,543,543,543,543,543,543,543,543,543,543,543, +543,543,543,543,543,543,543,543,543,543,543,543,543,543,543,543, +543,543,543,543,543,543,543,543,543,543,543,109,109,109,109,109, + +/* block 137 */ + 4, 4, 4,109,109,109,109, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23,109,109,109, 19, 19, 19, 19, 19, 19, 19, 19, 19, +544,544,544,544,544,544,544,544,544,544,544,544,544,544,544,544, +544,544,544,544,544,544,544,544,544,544,544,544,544,544,544,544, +544,544,544,544,544,544,544,544,544,544,544,544,544,544,544,544, +544,544,544,544,544,545,545,545,545,546,546,546,546,546,546,546, + +/* block 138 */ +546,546,546,546,546,546,546,546,546,546,545,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,104,109,109, + +/* block 139 */ +547,547,547,547,547,547,547,547,547,547,547,547,547,547,547,547, +547,547,547,547,547,547,547,547,547,547,547,547,547,109,109,109, +548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548, +548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548, +548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548, +548,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 140 */ +549,549,549,549,549,549,549,549,549,549,549,549,549,549,549,549, +549,549,549,549,549,549,549,549,549,549,549,549,549,549,549,109, +550,550,550,550,109,109,109,109,109,109,109,109,109,109,109,109, +551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551, +551,552,551,551,551,551,551,551,551,551,552,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 141 */ +553,553,553,553,553,553,553,553,553,553,553,553,553,553,553,553, +553,553,553,553,553,553,553,553,553,553,553,553,553,553,109,554, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,109,109,109,109,555,555,555,555,555,555,555,555, +556,557,557,557,557,557,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 142 */ +558,558,558,558,558,558,558,558,558,558,558,558,558,558,558,558, +558,558,558,558,558,558,558,558,558,558,558,558,558,558,558,558, +558,558,558,558,558,558,558,558,559,559,559,559,559,559,559,559, +559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, +559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, +560,560,560,560,560,560,560,560,560,560,560,560,560,560,560,560, +560,560,560,560,560,560,560,560,560,560,560,560,560,560,560,560, +560,560,560,560,560,560,560,560,560,560,560,560,560,560,560,560, + +/* block 143 */ +561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, +561,561,561,561,561,561,561,561,561,561,561,561,561,561,109,109, +562,562,562,562,562,562,562,562,562,562,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 144 */ +563,563,563,563,563,563,109,109,563,109,563,563,563,563,563,563, +563,563,563,563,563,563,563,563,563,563,563,563,563,563,563,563, +563,563,563,563,563,563,563,563,563,563,563,563,563,563,563,563, +563,563,563,563,563,563,109,563,563,109,109,109,563,109,109,563, +564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,564, +564,564,564,564,564,564,109,565,566,566,566,566,566,566,566,566, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 145 */ +567,567,567,567,567,567,567,567,567,567,567,567,567,567,567,567, +567,567,567,567,567,567,568,568,568,568,568,568,109,109,109,569, +570,570,570,570,570,570,570,570,570,570,570,570,570,570,570,570, +570,570,570,570,570,570,570,570,570,570,109,109,109,109,109,571, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 146 */ +572,572,572,572,572,572,572,572,572,572,572,572,572,572,572,572, +572,572,572,572,572,572,572,572,572,572,572,572,572,572,572,572, +573,573,573,573,573,573,573,573,573,573,573,573,573,573,573,573, +573,573,573,573,573,573,573,573,109,109,109,109,109,109,573,573, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 147 */ +574,575,575,575,109,575,575,109,109,109,109,109,575,575,575,575, +574,574,574,574,109,574,574,574,109,574,574,574,574,574,574,574, +574,574,574,574,574,574,574,574,574,574,574,574,574,574,574,574, +574,574,574,574,109,109,109,109,575,575,575,109,109,109,109,575, +576,576,576,576,576,576,576,576,109,109,109,109,109,109,109,109, +577,577,577,577,577,577,577,577,577,109,109,109,109,109,109,109, +578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, +578,578,578,578,578,578,578,578,578,578,578,578,578,579,579,580, + +/* block 148 */ +581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, +581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, +581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, +581,581,581,581,581,581,109,109,109,582,582,582,582,582,582,582, +583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, +583,583,583,583,583,583,109,109,584,584,584,584,584,584,584,584, +585,585,585,585,585,585,585,585,585,585,585,585,585,585,585,585, +585,585,585,109,109,109,109,109,586,586,586,586,586,586,586,586, + +/* block 149 */ +587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587, +587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587, +587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587, +587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587, +587,587,587,587,587,587,587,587,587,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 150 */ +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588, +588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,109, + +/* block 151 */ +589,590,589,591,591,591,591,591,591,591,591,591,591,591,591,591, +591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, +591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, +591,591,591,591,591,591,591,591,590,590,590,590,590,590,590,590, +590,590,590,590,590,590,590,592,592,592,592,592,592,592,109,109, +109,109,593,593,593,593,593,593,593,593,593,593,593,593,593,593, +593,593,593,593,593,593,594,594,594,594,594,594,594,594,594,594, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 152 */ +595,595,596,597,597,597,597,597,597,597,597,597,597,597,597,597, +597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,597, +597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,597, +596,596,596,595,595,595,595,596,596,595,595,598,598,599,598,598, +598,598,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600, +600,600,600,600,600,600,600,600,600,109,109,109,109,109,109,109, +601,601,601,601,601,601,601,601,601,601,109,109,109,109,109,109, + +/* block 153 */ +602,602,602,603,603,603,603,603,603,603,603,603,603,603,603,603, +603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,603, +603,603,603,603,603,603,603,602,602,602,602,602,604,602,602,602, +602,602,602,602,602,109,605,605,605,605,605,605,605,605,605,605, +606,606,606,606,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 154 */ +607,607,608,609,609,609,609,609,609,609,609,609,609,609,609,609, +609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609, +609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609, +609,609,609,608,608,608,607,607,607,607,607,607,607,607,607,608, +608,609,609,609,609,610,610,610,610,109,109,109,109,109,109,109, +611,611,611,611,611,611,611,611,611,611,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 155 */ +612,612,612,612,612,612,612,612,612,612,612,612,612,612,612,612, +612,612,612,612,612,612,612,612,612,612,612,612,612,612,612,612, +612,612,612,612,612,612,612,612,612,612,612,613,614,613,614,614, +613,613,613,613,613,613,614,613,109,109,109,109,109,109,109,109, +615,615,615,615,615,615,615,615,615,615,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 156 */ +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, + +/* block 157 */ +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, +616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 158 */ +617,617,617,617,617,617,617,617,617,617,617,617,617,617,617,617, +617,617,617,617,617,617,617,617,617,617,617,617,617,617,617,617, +617,617,617,617,617,617,617,617,617,617,617,617,617,617,617,617, +617,617,617,617,617,617,617,617,617,617,617,617,617,617,617,617, +617,617,617,617,617,617,617,617,617,617,617,617,617,617,617,617, +617,617,617,617,617,617,617,617,617,617,617,617,617,617,617,617, +617,617,617,109,109,109,109,109,109,109,109,109,109,109,109,109, +618,618,618,618,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 159 */ +619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619, +619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619, +619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619, +619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619, +619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619, +619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619, +619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619, +619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619, + +/* block 160 */ +619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619, +619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619, +619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 161 */ +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, + +/* block 162 */ +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,488,488,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 163 */ +620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620, +620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620, +620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620, +620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620, +620,620,620,620,620,109,109,109,109,109,109,109,109,109,109,109, +620,621,621,621,621,621,621,621,621,621,621,621,621,621,621,621, +621,621,621,621,621,621,621,621,621,621,621,621,621,621,621,621, +621,621,621,621,621,621,621,621,621,621,621,621,621,621,621,109, + +/* block 164 */ +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,622, +622,622,622,623,623,623,623,623,623,623,623,623,623,623,623,623, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 165 */ +469,467,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 166 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19,109,109,109,109,109,109,109,109,109,109, + +/* block 167 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19,109,109, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19,624,395,104,104,104, 19, 19, 19,395,624,624, +624,624,624, 22, 22, 22, 22, 22, 22, 22, 22,104,104,104,104,104, + +/* block 168 */ +104,104,104, 19, 19,104,104,104,104,104,104,104, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,104,104,104,104, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 169 */ +546,546,546,546,546,546,546,546,546,546,546,546,546,546,546,546, +546,546,546,546,546,546,546,546,546,546,546,546,546,546,546,546, +546,546,546,546,546,546,546,546,546,546,546,546,546,546,546,546, +546,546,546,546,546,546,546,546,546,546,546,546,546,546,546,546, +546,546,625,625,625,546,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 170 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19,109,109,109,109,109,109,109,109,109, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 171 */ +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,429,429, +429,429,429,429,429,109,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, + +/* block 172 */ +428,428,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,428,109,428,428, +109,109,428,109,109,428,428,109,109,428,428,428,428,109,428,428, +428,428,428,428,428,428,429,429,429,429,109,429,109,429,429,429, +429,429,429,429,109,429,429,429,429,429,429,429,429,429,429,429, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, + +/* block 173 */ +429,429,429,429,428,428,109,428,428,428,428,109,109,428,428,428, +428,428,428,428,428,109,428,428,428,428,428,428,428,109,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,428,428,109,428,428,428,428,109, +428,428,428,428,428,109,428,109,109,109,428,428,428,428,428,428, +428,109,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, + +/* block 174 */ +428,428,428,428,428,428,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, + +/* block 175 */ +429,429,429,429,429,429,429,429,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, + +/* block 176 */ +428,428,428,428,428,428,428,428,428,428,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,429,429,109,109,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428, 8,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429, 8,429,429,429,429, +429,429,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428, 8,429,429,429,429, + +/* block 177 */ +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,429, 8,429,429,429,429,429,429,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428, 8,429,429,429,429,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, 8, +429,429,429,429,429,429,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, 8, +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, + +/* block 178 */ +429,429,429,429,429,429,429,429,429, 8,429,429,429,429,429,429, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428, 8,429,429,429,429,429,429, +429,429,429,429,429,429,429,429,429,429,429,429,429,429,429,429, +429,429,429, 8,429,429,429,429,429,429,428,429,109,109, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + +/* block 179 */ +191,191,191,191,109,191,191,191,191,191,191,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, +109,191,191,109,191,109,109,191,109,191,191,191,191,191,191,191, +191,191,191,109,191,191,191,191,109,191,109,191,109,109,109,109, +109,109,191,109,109,109,109,191,109,191,109,191,109,191,191,191, +109,191,191,109,191,109,109,191,109,191,109,191,109,191,109,191, +109,191,191,109,191,109,109,191,191,191,191,109,191,191,191,191, +191,191,191,109,191,191,191,191,109,191,191,191,191,109,191,109, + +/* block 180 */ +191,191,191,191,191,191,191,191,191,191,109,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,109,109,109,109, +109,191,191,191,109,191,191,191,191,191,109,191,191,191,191,191, +191,191,191,191,191,191,191,191,191,191,191,191,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +186,186,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 181 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + +/* block 182 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19,109,109,109,109,109,109,109,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109, +109, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109, +109, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, +109, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 183 */ + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + +/* block 184 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,626,626,626,626,626,626,626,626,626,626, +626,626,626,626,626,626,626,626,626,626,626,626,626,626,626,626, + +/* block 185 */ +627, 19, 19,109,109,109,109,109,109,109,109,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109,109,109,109,109,109, + 19, 19,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 186 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + 19, 19, 19, 19, 19, 19,109, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109,109, + +/* block 187 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19,109,109,109,109,109,109,109,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19,109, 19, 19, 19, 19, 19,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 188 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109, + 19,109, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + +/* block 189 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19,109, 19, 19, 19, 19,109,109,109, + +/* block 190 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109, + 19, 19, 19, 19,109,109,109,109,109,109,109,109,109,109,109,109, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 191 */ +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109, 19, 19, 19, 19, 19, + +/* block 192 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19,109,109,109,109, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 193 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 194 */ + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 195 */ +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 196 */ +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,109,109,109,109,109,109,109,109,109,109,109, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, + +/* block 197 */ +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + +/* block 198 */ +426, 22,426,426,426,426,426,426,426,426,426,426,426,426,426,426, +426,426,426,426,426,426,426,426,426,426,426,426,426,426,426,426, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + +/* block 199 */ +426,426,426,426,426,426,426,426,426,426,426,426,426,426,426,426, +426,426,426,426,426,426,426,426,426,426,426,426,426,426,426,426, +426,426,426,426,426,426,426,426,426,426,426,426,426,426,426,426, +426,426,426,426,426,426,426,426,426,426,426,426,426,426,426,426, +426,426,426,426,426,426,426,426,426,426,426,426,426,426,426,426, +426,426,426,426,426,426,426,426,426,426,426,426,426,426,426,426, +426,426,426,426,426,426,426,426,426,426,426,426,426,426,426,426, +426,426,426,426,426,426,426,426,426,426,426,426,426,426,426,426, + +/* block 200 */ +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, + +/* block 201 */ +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +426,426,426,426,426,426,426,426,426,426,426,426,426,426,426,426, + +/* block 202 */ +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, +539,539,539,539,539,539,539,539,539,539,539,539,539,539,109,109, + +}; + +#if UCD_BLOCK_SIZE != 128 +#error Please correct UCD_BLOCK_SIZE in pcre_internal.h +#endif +#endif /* SUPPORT_UCP */ + +#endif /* PCRE_INCLUDED */ diff --git a/erts/emulator/pcre/pcre_ucp_searchfuncs.c b/erts/emulator/pcre/pcre_ucp_searchfuncs.c deleted file mode 100644 index 6a20c227cf..0000000000 --- a/erts/emulator/pcre/pcre_ucp_searchfuncs.c +++ /dev/null @@ -1,181 +0,0 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ - -/* PCRE is a library of functions to support regular expressions whose syntax -and semantics are as close as possible to those of the Perl 5 language. - - Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge - ------------------------------------------------------------------------------ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the University of Cambridge nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------ -*/ - - -/* This module contains code for searching the table of Unicode character -properties. */ - -/* %ExternalCopyright% */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "pcre_internal.h" - -#include "ucp.h" /* Category definitions */ -#include "ucpinternal.h" /* Internal table details */ -#include "ucptable.h" /* The table itself */ - - -/* Table to translate from particular type value to the general value. */ - -static const int ucp_gentype[] = { - ucp_C, ucp_C, ucp_C, ucp_C, ucp_C, /* Cc, Cf, Cn, Co, Cs */ - ucp_L, ucp_L, ucp_L, ucp_L, ucp_L, /* Ll, Lu, Lm, Lo, Lt */ - ucp_M, ucp_M, ucp_M, /* Mc, Me, Mn */ - ucp_N, ucp_N, ucp_N, /* Nd, Nl, No */ - ucp_P, ucp_P, ucp_P, ucp_P, ucp_P, /* Pc, Pd, Pe, Pf, Pi */ - ucp_P, ucp_P, /* Ps, Po */ - ucp_S, ucp_S, ucp_S, ucp_S, /* Sc, Sk, Sm, So */ - ucp_Z, ucp_Z, ucp_Z /* Zl, Zp, Zs */ -}; - - - -/************************************************* -* Search table and return type * -*************************************************/ - -/* Three values are returned: the category is ucp_C, ucp_L, etc. The detailed -character type is ucp_Lu, ucp_Nd, etc. The script is ucp_Latin, etc. - -Arguments: - c the character value - type_ptr the detailed character type is returned here - script_ptr the script is returned here - -Returns: the character type category -*/ - -int -_erts_pcre_ucp_findprop(const unsigned int c, int *type_ptr, int *script_ptr) -{ -int bot = 0; -int top = sizeof(ucp_table)/sizeof(cnode); -int mid; - -/* The table is searched using a binary chop. You might think that using -intermediate variables to hold some of the common expressions would speed -things up, but tests with gcc 3.4.4 on Linux showed that, on the contrary, it -makes things a lot slower. */ - -for (;;) - { - if (top <= bot) - { - *type_ptr = ucp_Cn; - *script_ptr = ucp_Common; - return ucp_C; - } - mid = (bot + top) >> 1; - if (c == (ucp_table[mid].f0 & f0_charmask)) break; - if (c < (ucp_table[mid].f0 & f0_charmask)) top = mid; - else - { - if ((ucp_table[mid].f0 & f0_rangeflag) != 0 && - c <= (ucp_table[mid].f0 & f0_charmask) + - (ucp_table[mid].f1 & f1_rangemask)) break; - bot = mid + 1; - } - } - -/* Found an entry in the table. Set the script and detailed type values, and -return the general type. */ - -*script_ptr = (ucp_table[mid].f0 & f0_scriptmask) >> f0_scriptshift; -*type_ptr = (ucp_table[mid].f1 & f1_typemask) >> f1_typeshift; - -return ucp_gentype[*type_ptr]; -} - - - -/************************************************* -* Search table and return other case * -*************************************************/ - -/* If the given character is a letter, and there is another case for the -letter, return the other case. Otherwise, return -1. - -Arguments: - c the character value - -Returns: the other case or NOTACHAR if none -*/ - -unsigned int -_erts_pcre_ucp_othercase(const unsigned int c) -{ -int bot = 0; -int top = sizeof(ucp_table)/sizeof(cnode); -int mid, offset; - -/* The table is searched using a binary chop. You might think that using -intermediate variables to hold some of the common expressions would speed -things up, but tests with gcc 3.4.4 on Linux showed that, on the contrary, it -makes things a lot slower. */ - -for (;;) - { - if (top <= bot) return -1; - mid = (bot + top) >> 1; - if (c == (ucp_table[mid].f0 & f0_charmask)) break; - if (c < (ucp_table[mid].f0 & f0_charmask)) top = mid; - else - { - if ((ucp_table[mid].f0 & f0_rangeflag) != 0 && - c <= (ucp_table[mid].f0 & f0_charmask) + - (ucp_table[mid].f1 & f1_rangemask)) break; - bot = mid + 1; - } - } - -/* Found an entry in the table. Return NOTACHAR for a range entry. Otherwise -return the other case if there is one, else NOTACHAR. */ - -if ((ucp_table[mid].f0 & f0_rangeflag) != 0) return NOTACHAR; - -offset = ucp_table[mid].f1 & f1_casemask; -if ((offset & f1_caseneg) != 0) offset |= f1_caseneg; -return (offset == 0)? NOTACHAR : c + offset; -} - - -/* End of pcre_ucp_searchfuncs.c */ diff --git a/erts/emulator/pcre/pcre_valid_utf8.c b/erts/emulator/pcre/pcre_valid_utf8.c index 30af207ae3..516d8f4725 100644 --- a/erts/emulator/pcre/pcre_valid_utf8.c +++ b/erts/emulator/pcre/pcre_valid_utf8.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2013 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -55,109 +55,248 @@ strings. */ *************************************************/ /* This function is called (optionally) at the start of compile or match, to -validate that a supposed UTF-8 string is actually valid. The early check means +check that a supposed UTF-8 string is actually valid. The early check means that subsequent code can assume it is dealing with a valid string. The check -can be turned off for maximum performance, but the consequences of supplying -an invalid string are then undefined. +can be turned off for maximum performance, but the consequences of supplying an +invalid string are then undefined. Originally, this function checked according to RFC 2279, allowing for values in the range 0 to 0x7fffffff, up to 6 bytes long, but ensuring that they were in the canonical format. Once somebody had pointed out RFC 3629 to me (it obsoletes 2279), additional restrictions were applied. The values are now limited to be between 0 and 0x0010ffff, no more than 4 bytes long, and the -subrange 0xd000 to 0xdfff is excluded. +subrange 0xd000 to 0xdfff is excluded. However, the format of 5-byte and 6-byte +characters is still checked. + +From release 8.13 more information about the details of the error are passed +back in the returned value: + +PCRE_UTF8_ERR0 No error +PCRE_UTF8_ERR1 Missing 1 byte at the end of the string +PCRE_UTF8_ERR2 Missing 2 bytes at the end of the string +PCRE_UTF8_ERR3 Missing 3 bytes at the end of the string +PCRE_UTF8_ERR4 Missing 4 bytes at the end of the string +PCRE_UTF8_ERR5 Missing 5 bytes at the end of the string +PCRE_UTF8_ERR6 2nd-byte's two top bits are not 0x80 +PCRE_UTF8_ERR7 3rd-byte's two top bits are not 0x80 +PCRE_UTF8_ERR8 4th-byte's two top bits are not 0x80 +PCRE_UTF8_ERR9 5th-byte's two top bits are not 0x80 +PCRE_UTF8_ERR10 6th-byte's two top bits are not 0x80 +PCRE_UTF8_ERR11 5-byte character is not permitted by RFC 3629 +PCRE_UTF8_ERR12 6-byte character is not permitted by RFC 3629 +PCRE_UTF8_ERR13 4-byte character with value > 0x10ffff is not permitted +PCRE_UTF8_ERR14 3-byte character with value 0xd000-0xdfff is not permitted +PCRE_UTF8_ERR15 Overlong 2-byte sequence +PCRE_UTF8_ERR16 Overlong 3-byte sequence +PCRE_UTF8_ERR17 Overlong 4-byte sequence +PCRE_UTF8_ERR18 Overlong 5-byte sequence (won't ever occur) +PCRE_UTF8_ERR19 Overlong 6-byte sequence (won't ever occur) +PCRE_UTF8_ERR20 Isolated 0x80 byte (not within UTF-8 character) +PCRE_UTF8_ERR21 Byte with the illegal value 0xfe or 0xff +PCRE_UTF8_ERR22 Unused (was non-character) Arguments: string points to the string length length of string, or -1 if the string is zero-terminated + errp pointer to an error position offset variable -Returns: < 0 if the string is a valid UTF-8 string - >= 0 otherwise; the value is the offset of the bad byte +Returns: = 0 if the string is a valid UTF-8 string + > 0 otherwise, setting the offset of the bad character */ int -_erts_pcre_valid_utf8(const uschar *string, int length) +PRIV(valid_utf)(PCRE_PUCHAR string, int length, int *erroroffset) { -#ifdef SUPPORT_UTF8 -register const uschar *p; +#ifdef SUPPORT_UTF +register PCRE_PUCHAR p; if (length < 0) { for (p = string; *p != 0; p++); - length = p - string; + length = (int)(p - string); } for (p = string; length-- > 0; p++) { - register int ab; - register int c = *p; - if (c < 128) continue; - if (c < 0xc0) return p - string; - ab = _erts_pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ - if (length < ab || ab > 3) return p - string; - length -= ab; + register pcre_uchar ab, c, d; + + c = *p; + if (c < 128) continue; /* ASCII character */ + + if (c < 0xc0) /* Isolated 10xx xxxx byte */ + { + *erroroffset = (int)(p - string); + return PCRE_UTF8_ERR20; + } + + if (c >= 0xfe) /* Invalid 0xfe or 0xff bytes */ + { + *erroroffset = (int)(p - string); + return PCRE_UTF8_ERR21; + } + + ab = PRIV(utf8_table4)[c & 0x3f]; /* Number of additional bytes */ + if (length < ab) + { + *erroroffset = (int)(p - string); /* Missing bytes */ + return ab - length; /* Codes ERR1 to ERR5 */ + } + length -= ab; /* Length remaining */ /* Check top bits in the second byte */ - if ((*(++p) & 0xc0) != 0x80) return p - string; - /* Check for overlong sequences for each different length, and for the - excluded range 0xd000 to 0xdfff. */ + if (((d = *(++p)) & 0xc0) != 0x80) + { + *erroroffset = (int)(p - string) - 1; + return PCRE_UTF8_ERR6; + } + + /* For each length, check that the remaining bytes start with the 0x80 bit + set and not the 0x40 bit. Then check for an overlong sequence, and for the + excluded range 0xd800 to 0xdfff. */ switch (ab) { - /* Check for xx00 000x (overlong sequence) */ - - case 1: - if ((c & 0x3e) == 0) return p - string; - continue; /* We know there aren't any more bytes to check */ + /* 2-byte character. No further bytes to check for 0x80. Check first byte + for for xx00 000x (overlong sequence). */ + + case 1: if ((c & 0x3e) == 0) + { + *erroroffset = (int)(p - string) - 1; + return PCRE_UTF8_ERR15; + } + break; - /* Check for 1110 0000, xx0x xxxx (overlong sequence) or - 1110 1101, 1010 xxxx (0xd000 - 0xdfff) */ + /* 3-byte character. Check third byte for 0x80. Then check first 2 bytes + for 1110 0000, xx0x xxxx (overlong sequence) or + 1110 1101, 1010 xxxx (0xd800 - 0xdfff) */ case 2: - if ((c == 0xe0 && (*p & 0x20) == 0) || - (c == 0xed && *p >= 0xa0)) - return p - string; + if ((*(++p) & 0xc0) != 0x80) /* Third byte */ + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR7; + } + if (c == 0xe0 && (d & 0x20) == 0) + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR16; + } + if (c == 0xed && d >= 0xa0) + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR14; + } break; - /* Check for 1111 0000, xx00 xxxx (overlong sequence) or - greater than 0x0010ffff (f4 8f bf bf) */ + /* 4-byte character. Check 3rd and 4th bytes for 0x80. Then check first 2 + bytes for for 1111 0000, xx00 xxxx (overlong sequence), then check for a + character greater than 0x0010ffff (f4 8f bf bf) */ case 3: - if ((c == 0xf0 && (*p & 0x30) == 0) || - (c > 0xf4 ) || - (c == 0xf4 && *p > 0x8f)) - return p - string; + if ((*(++p) & 0xc0) != 0x80) /* Third byte */ + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR7; + } + if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */ + { + *erroroffset = (int)(p - string) - 3; + return PCRE_UTF8_ERR8; + } + if (c == 0xf0 && (d & 0x30) == 0) + { + *erroroffset = (int)(p - string) - 3; + return PCRE_UTF8_ERR17; + } + if (c > 0xf4 || (c == 0xf4 && d > 0x8f)) + { + *erroroffset = (int)(p - string) - 3; + return PCRE_UTF8_ERR13; + } break; -#if 0 - /* These cases can no longer occur, as we restrict to a maximum of four - bytes nowadays. Leave the code here in case we ever want to add an option - for longer sequences. */ + /* 5-byte and 6-byte characters are not allowed by RFC 3629, and will be + rejected by the length test below. However, we do the appropriate tests + here so that overlong sequences get diagnosed, and also in case there is + ever an option for handling these larger code points. */ + + /* 5-byte character. Check 3rd, 4th, and 5th bytes for 0x80. Then check for + 1111 1000, xx00 0xxx */ - /* Check for 1111 1000, xx00 0xxx */ case 4: - if (c == 0xf8 && (*p & 0x38) == 0) return p - string; + if ((*(++p) & 0xc0) != 0x80) /* Third byte */ + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR7; + } + if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */ + { + *erroroffset = (int)(p - string) - 3; + return PCRE_UTF8_ERR8; + } + if ((*(++p) & 0xc0) != 0x80) /* Fifth byte */ + { + *erroroffset = (int)(p - string) - 4; + return PCRE_UTF8_ERR9; + } + if (c == 0xf8 && (d & 0x38) == 0) + { + *erroroffset = (int)(p - string) - 4; + return PCRE_UTF8_ERR18; + } break; - /* Check for leading 0xfe or 0xff, and then for 1111 1100, xx00 00xx */ + /* 6-byte character. Check 3rd-6th bytes for 0x80. Then check for + 1111 1100, xx00 00xx. */ + case 5: - if (c == 0xfe || c == 0xff || - (c == 0xfc && (*p & 0x3c) == 0)) return p - string; + if ((*(++p) & 0xc0) != 0x80) /* Third byte */ + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR7; + } + if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */ + { + *erroroffset = (int)(p - string) - 3; + return PCRE_UTF8_ERR8; + } + if ((*(++p) & 0xc0) != 0x80) /* Fifth byte */ + { + *erroroffset = (int)(p - string) - 4; + return PCRE_UTF8_ERR9; + } + if ((*(++p) & 0xc0) != 0x80) /* Sixth byte */ + { + *erroroffset = (int)(p - string) - 5; + return PCRE_UTF8_ERR10; + } + if (c == 0xfc && (d & 0x3c) == 0) + { + *erroroffset = (int)(p - string) - 5; + return PCRE_UTF8_ERR19; + } break; -#endif - } - /* Check for valid bytes after the 2nd, if any; all must start 10 */ - while (--ab > 0) + /* Character is valid under RFC 2279, but 4-byte and 5-byte characters are + excluded by RFC 3629. The pointer p is currently at the last byte of the + character. */ + + if (ab > 3) { - if ((*(++p) & 0xc0) != 0x80) return p - string; + *erroroffset = (int)(p - string) - ab; + return (ab == 4)? PCRE_UTF8_ERR11 : PCRE_UTF8_ERR12; } } + +#else /* Not SUPPORT_UTF */ +(void)(string); /* Keep picky compilers happy */ +(void)(length); +(void)(erroroffset); #endif -return -1; +return PCRE_UTF8_ERR0; /* This indicates success */ } /* End of pcre_valid_utf8.c */ diff --git a/erts/emulator/pcre/pcre_version.c b/erts/emulator/pcre/pcre_version.c index b8a5b555ef..f771b3e82b 100644 --- a/erts/emulator/pcre/pcre_version.c +++ b/erts/emulator/pcre/pcre_version.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2012 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. */ -/* This module contains the external function erts_pcre_version(), which returns a +/* This module contains the external function pcre_version(), which returns a string that identifies the PCRE version that is in use. */ /* %ExternalCopyright% */ @@ -80,8 +80,16 @@ I could find no way of detecting that a macro is defined as an empty string at pre-processor time. This hack uses a standard trick for avoiding calling the STRING macro with an empty argument when doing the test. */ -PCRE_EXP_DEFN const char * -erts_pcre_version(void) +#if defined COMPILE_PCRE8 +PCRE_EXP_DEFN const char * PCRE_CALL_CONVENTION +pcre_version(void) +#elif defined COMPILE_PCRE16 +PCRE_EXP_DEFN const char * PCRE_CALL_CONVENTION +pcre16_version(void) +#elif defined COMPILE_PCRE32 +PCRE_EXP_DEFN const char * PCRE_CALL_CONVENTION +pcre32_version(void) +#endif { return (XSTRING(Z PCRE_PRERELEASE)[1] == 0)? XSTRING(PCRE_MAJOR.PCRE_MINOR PCRE_DATE) : diff --git a/erts/emulator/pcre/pcre_xclass.c b/erts/emulator/pcre/pcre_xclass.c index 1172cd17ac..cf07451a10 100644 --- a/erts/emulator/pcre/pcre_xclass.c +++ b/erts/emulator/pcre/pcre_xclass.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2008 University of Cambridge + Copyright (c) 1997-2013 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -39,8 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. /* This module contains an internal function that is used to match an extended -class (one that contains characters whose values are > 255). It is used by both -pcre_exec() and pcre_def_exec(). */ +class. It is used by both pcre_exec() and pcre_def_exec(). */ /* %ExternalCopyright% */ @@ -56,7 +55,7 @@ pcre_exec() and pcre_def_exec(). */ *************************************************/ /* This function is called to match a character against an extended class that -might contain values > 255. +might contain values > 255 and/or Unicode properties. Arguments: c the character @@ -66,47 +65,70 @@ Returns: TRUE if character matches, else FALSE */ BOOL -_erts_pcre_xclass(int c, const uschar *data) +PRIV(xclass)(pcre_uint32 c, const pcre_uchar *data, BOOL utf) { -int t; +pcre_uchar t; BOOL negated = (*data & XCL_NOT) != 0; +(void)utf; +#ifdef COMPILE_PCRE8 +/* In 8 bit mode, this must always be TRUE. Help the compiler to know that. */ +utf = TRUE; +#endif + /* Character values < 256 are matched against a bitmap, if one is present. If not, we still carry on, because there may be ranges that start below 256 in the additional data. */ if (c < 256) { - if ((*data & XCL_MAP) != 0 && (data[1 + c/8] & (1 << (c&7))) != 0) - return !negated; /* char found */ + if ((*data & XCL_MAP) != 0 && + (((pcre_uint8 *)(data + 1))[c/8] & (1 << (c&7))) != 0) + return !negated; /* char found */ } /* First skip the bit map if present. Then match against the list of Unicode properties or large chars or ranges that end with a large char. We won't ever encounter XCL_PROP or XCL_NOTPROP when UCP support is not compiled. */ -if ((*data++ & XCL_MAP) != 0) data += 32; +if ((*data++ & XCL_MAP) != 0) data += 32 / sizeof(pcre_uchar); while ((t = *data++) != XCL_END) { - int x, y; + pcre_uint32 x, y; if (t == XCL_SINGLE) { - GETCHARINC(x, data); +#ifdef SUPPORT_UTF + if (utf) + { + GETCHARINC(x, data); /* macro generates multiple statements */ + } + else +#endif + x = *data++; if (c == x) return !negated; } else if (t == XCL_RANGE) { - GETCHARINC(x, data); - GETCHARINC(y, data); +#ifdef SUPPORT_UTF + if (utf) + { + GETCHARINC(x, data); /* macro generates multiple statements */ + GETCHARINC(y, data); /* macro generates multiple statements */ + } + else +#endif + { + x = *data++; + y = *data++; + } if (c >= x && c <= y) return !negated; } #ifdef SUPPORT_UCP else /* XCL_PROP & XCL_NOTPROP */ { - int chartype, script; - int category = _erts_pcre_ucp_findprop(c, &chartype, &script); + const ucd_record *prop = GET_UCD(c); switch(*data) { @@ -115,20 +137,62 @@ while ((t = *data++) != XCL_END) break; case PT_LAMP: - if ((chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt) == - (t == XCL_PROP)) return !negated; + if ((prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt) == (t == XCL_PROP)) return !negated; break; case PT_GC: - if ((data[1] == category) == (t == XCL_PROP)) return !negated; + if ((data[1] == PRIV(ucp_gentype)[prop->chartype]) == (t == XCL_PROP)) + return !negated; break; case PT_PC: - if ((data[1] == chartype) == (t == XCL_PROP)) return !negated; + if ((data[1] == prop->chartype) == (t == XCL_PROP)) return !negated; break; case PT_SC: - if ((data[1] == script) == (t == XCL_PROP)) return !negated; + if ((data[1] == prop->script) == (t == XCL_PROP)) return !negated; + break; + + case PT_ALNUM: + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N) == (t == XCL_PROP)) + return !negated; + break; + + case PT_SPACE: /* Perl space */ + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR) + == (t == XCL_PROP)) + return !negated; + break; + + case PT_PXSPACE: /* POSIX space */ + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR) == (t == XCL_PROP)) + return !negated; + break; + + case PT_WORD: + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || c == CHAR_UNDERSCORE) + == (t == XCL_PROP)) + return !negated; + break; + + case PT_UCNC: + if (c < 0xa0) + { + if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || + c == CHAR_GRAVE_ACCENT) == (t == XCL_PROP)) + return !negated; + } + else + { + if ((c < 0xd800 || c > 0xdfff) == (t == XCL_PROP)) + return !negated; + } break; /* This should never occur, but compilers may mutter if there is no diff --git a/erts/emulator/pcre/ucp.h b/erts/emulator/pcre/ucp.h index 52f91f1a65..bbfe0f3ecb 100644 --- a/erts/emulator/pcre/ucp.h +++ b/erts/emulator/pcre/ucp.h @@ -8,9 +8,12 @@ #define _UCP_H /* This file contains definitions of the property values that are returned by -the function _erts_pcre_ucp_findprop(). New values that are added for new releases -of Unicode should always be at the end of each enum, for backwards -compatibility. */ +the UCD access macros. New values that are added for new releases of Unicode +should always be at the end of each enum, for backwards compatibility. + +IMPORTANT: Note also that the specific numeric values of the enums have to be +the same as the values that are generated by the maint/MultiStage2.py script, +where the equivalent property descriptive names are listed in vectors. */ /* These are the general character categories. */ @@ -24,7 +27,7 @@ enum { ucp_Z /* Separator */ }; -/* These are the particular character types. */ +/* These are the particular character categories. */ enum { ucp_Cc, /* Control */ @@ -59,6 +62,26 @@ enum { ucp_Zs /* Space separator */ }; +/* These are grapheme break properties. Note that the code for processing them +assumes that the values are less than 16. If more values are added that take +the number to 16 or more, the code will have to be rewritten. */ + +enum { + ucp_gbCR, /* 0 */ + ucp_gbLF, /* 1 */ + ucp_gbControl, /* 2 */ + ucp_gbExtend, /* 3 */ + ucp_gbPrepend, /* 4 */ + ucp_gbSpacingMark, /* 5 */ + ucp_gbL, /* 6 Hangul syllable type L */ + ucp_gbV, /* 7 Hangul syllable type V */ + ucp_gbT, /* 8 Hangul syllable type T */ + ucp_gbLV, /* 9 Hangul syllable type LV */ + ucp_gbLVT, /* 10 Hangul syllable type LVT */ + ucp_gbRegionalIndicator, /* 11 */ + ucp_gbOther /* 12 */ +}; + /* These are the script identifications. */ enum { @@ -123,11 +146,52 @@ enum { ucp_Tifinagh, ucp_Ugaritic, ucp_Yi, - ucp_Balinese, /* New for Unicode 5.0.0 */ - ucp_Cuneiform, /* New for Unicode 5.0.0 */ - ucp_Nko, /* New for Unicode 5.0.0 */ - ucp_Phags_Pa, /* New for Unicode 5.0.0 */ - ucp_Phoenician /* New for Unicode 5.0.0 */ + /* New for Unicode 5.0: */ + ucp_Balinese, + ucp_Cuneiform, + ucp_Nko, + ucp_Phags_Pa, + ucp_Phoenician, + /* New for Unicode 5.1: */ + ucp_Carian, + ucp_Cham, + ucp_Kayah_Li, + ucp_Lepcha, + ucp_Lycian, + ucp_Lydian, + ucp_Ol_Chiki, + ucp_Rejang, + ucp_Saurashtra, + ucp_Sundanese, + ucp_Vai, + /* New for Unicode 5.2: */ + ucp_Avestan, + ucp_Bamum, + ucp_Egyptian_Hieroglyphs, + ucp_Imperial_Aramaic, + ucp_Inscriptional_Pahlavi, + ucp_Inscriptional_Parthian, + ucp_Javanese, + ucp_Kaithi, + ucp_Lisu, + ucp_Meetei_Mayek, + ucp_Old_South_Arabian, + ucp_Old_Turkic, + ucp_Samaritan, + ucp_Tai_Tham, + ucp_Tai_Viet, + /* New for Unicode 6.0.0: */ + ucp_Batak, + ucp_Brahmi, + ucp_Mandaic, + /* New for Unicode 6.1.0: */ + ucp_Chakma, + ucp_Meroitic_Cursive, + ucp_Meroitic_Hieroglyphs, + ucp_Miao, + ucp_Sharada, + ucp_Sora_Sompeng, + ucp_Takri }; #endif diff --git a/erts/emulator/pcre/ucpinternal.h b/erts/emulator/pcre/ucpinternal.h deleted file mode 100644 index 9893d39672..0000000000 --- a/erts/emulator/pcre/ucpinternal.h +++ /dev/null @@ -1,94 +0,0 @@ -/************************************************* -* Unicode Property Table handler * -*************************************************/ - -/* %ExternalCopyright% */ - -#ifndef _UCPINTERNAL_H -#define _UCPINTERNAL_H - -/* Internal header file defining the layout of the bits in each pair of 32-bit -words that form a data item in the table. */ - -typedef struct cnode { - pcre_uint32 f0; - pcre_uint32 f1; -} cnode; - -/* Things for the f0 field */ - -#define f0_scriptmask 0xff000000 /* Mask for script field */ -#define f0_scriptshift 24 /* Shift for script value */ -#define f0_rangeflag 0x00f00000 /* Flag for a range item */ -#define f0_charmask 0x001fffff /* Mask for code point value */ - -/* Things for the f1 field */ - -#define f1_typemask 0xfc000000 /* Mask for char type field */ -#define f1_typeshift 26 /* Shift for the type field */ -#define f1_rangemask 0x0000ffff /* Mask for a range offset */ -#define f1_casemask 0x0000ffff /* Mask for a case offset */ -#define f1_caseneg 0xffff8000 /* Bits for negation */ - -/* The data consists of a vector of structures of type cnode. The two unsigned -32-bit integers are used as follows: - -(f0) (1) The most significant byte holds the script number. The numbers are - defined by the enum in ucp.h. - - (2) The 0x00800000 bit is set if this entry defines a range of characters. - It is not set if this entry defines a single character - - (3) The 0x00600000 bits are spare. - - (4) The 0x001fffff bits contain the code point. No Unicode code point will - ever be greater than 0x0010ffff, so this should be OK for ever. - -(f1) (1) The 0xfc000000 bits contain the character type number. The numbers are - defined by an enum in ucp.h. - - (2) The 0x03ff0000 bits are spare. - - (3) The 0x0000ffff bits contain EITHER the unsigned offset to the top of - range if this entry defines a range, OR the *signed* offset to the - character's "other case" partner if this entry defines a single - character. There is no partner if the value is zero. - -------------------------------------------------------------------------------- -| script (8) |.|.|.| codepoint (21) || type (6) |.|.| spare (8) | offset (16) | -------------------------------------------------------------------------------- - | | | | | - | | |-> spare | |-> spare - | | | - | |-> spare |-> spare - | - |-> range flag - -The upper/lower casing information is set only for characters that come in -pairs. The non-one-to-one mappings in the Unicode data are ignored. - -When searching the data, proceed as follows: - -(1) Set up for a binary chop search. - -(2) If the top is not greater than the bottom, the character is not in the - table. Its type must therefore be "Cn" ("Undefined"). - -(3) Find the middle vector element. - -(4) Extract the code point and compare. If equal, we are done. - -(5) If the test character is smaller, set the top to the current point, and - goto (2). - -(6) If the current entry defines a range, compute the last character by adding - the offset, and see if the test character is within the range. If it is, - we are done. - -(7) Otherwise, set the bottom to one element past the current point and goto - (2). -*/ - -#endif /* _UCPINTERNAL_H */ - -/* End of ucpinternal.h */ diff --git a/erts/emulator/pcre/ucptable.h b/erts/emulator/pcre/ucptable.h deleted file mode 100644 index a274d443ee..0000000000 --- a/erts/emulator/pcre/ucptable.h +++ /dev/null @@ -1,3088 +0,0 @@ -/* This source module is automatically generated from the Unicode -property table. See ucpinternal.h for a description of the layout. -This version was made from the Unicode 5.0.0 tables. */ - -static const cnode ucp_table[] = { - { 0x09800000, 0x0000001f }, - { 0x09000020, 0x74000000 }, - { 0x09800021, 0x54000002 }, - { 0x09000024, 0x5c000000 }, - { 0x09800025, 0x54000002 }, - { 0x09000028, 0x58000000 }, - { 0x09000029, 0x48000000 }, - { 0x0900002a, 0x54000000 }, - { 0x0900002b, 0x64000000 }, - { 0x0900002c, 0x54000000 }, - { 0x0900002d, 0x44000000 }, - { 0x0980002e, 0x54000001 }, - { 0x09800030, 0x34000009 }, - { 0x0980003a, 0x54000001 }, - { 0x0980003c, 0x64000002 }, - { 0x0980003f, 0x54000001 }, - { 0x21000041, 0x24000020 }, - { 0x21000042, 0x24000020 }, - { 0x21000043, 0x24000020 }, - { 0x21000044, 0x24000020 }, - { 0x21000045, 0x24000020 }, - { 0x21000046, 0x24000020 }, - { 0x21000047, 0x24000020 }, - { 0x21000048, 0x24000020 }, - { 0x21000049, 0x24000020 }, - { 0x2100004a, 0x24000020 }, - { 0x2100004b, 0x24000020 }, - { 0x2100004c, 0x24000020 }, - { 0x2100004d, 0x24000020 }, - { 0x2100004e, 0x24000020 }, - { 0x2100004f, 0x24000020 }, - { 0x21000050, 0x24000020 }, - { 0x21000051, 0x24000020 }, - { 0x21000052, 0x24000020 }, - { 0x21000053, 0x24000020 }, - { 0x21000054, 0x24000020 }, - { 0x21000055, 0x24000020 }, - { 0x21000056, 0x24000020 }, - { 0x21000057, 0x24000020 }, - { 0x21000058, 0x24000020 }, - { 0x21000059, 0x24000020 }, - { 0x2100005a, 0x24000020 }, - { 0x0900005b, 0x58000000 }, - { 0x0900005c, 0x54000000 }, - { 0x0900005d, 0x48000000 }, - { 0x0900005e, 0x60000000 }, - { 0x0900005f, 0x40000000 }, - { 0x09000060, 0x60000000 }, - { 0x21000061, 0x1400ffe0 }, - { 0x21000062, 0x1400ffe0 }, - { 0x21000063, 0x1400ffe0 }, - { 0x21000064, 0x1400ffe0 }, - { 0x21000065, 0x1400ffe0 }, - { 0x21000066, 0x1400ffe0 }, - { 0x21000067, 0x1400ffe0 }, - { 0x21000068, 0x1400ffe0 }, - { 0x21000069, 0x1400ffe0 }, - { 0x2100006a, 0x1400ffe0 }, - { 0x2100006b, 0x1400ffe0 }, - { 0x2100006c, 0x1400ffe0 }, - { 0x2100006d, 0x1400ffe0 }, - { 0x2100006e, 0x1400ffe0 }, - { 0x2100006f, 0x1400ffe0 }, - { 0x21000070, 0x1400ffe0 }, - { 0x21000071, 0x1400ffe0 }, - { 0x21000072, 0x1400ffe0 }, - { 0x21000073, 0x1400ffe0 }, - { 0x21000074, 0x1400ffe0 }, - { 0x21000075, 0x1400ffe0 }, - { 0x21000076, 0x1400ffe0 }, - { 0x21000077, 0x1400ffe0 }, - { 0x21000078, 0x1400ffe0 }, - { 0x21000079, 0x1400ffe0 }, - { 0x2100007a, 0x1400ffe0 }, - { 0x0900007b, 0x58000000 }, - { 0x0900007c, 0x64000000 }, - { 0x0900007d, 0x48000000 }, - { 0x0900007e, 0x64000000 }, - { 0x0980007f, 0x00000020 }, - { 0x090000a0, 0x74000000 }, - { 0x090000a1, 0x54000000 }, - { 0x098000a2, 0x5c000003 }, - { 0x098000a6, 0x68000001 }, - { 0x090000a8, 0x60000000 }, - { 0x090000a9, 0x68000000 }, - { 0x210000aa, 0x14000000 }, - { 0x090000ab, 0x50000000 }, - { 0x090000ac, 0x64000000 }, - { 0x090000ad, 0x04000000 }, - { 0x090000ae, 0x68000000 }, - { 0x090000af, 0x60000000 }, - { 0x090000b0, 0x68000000 }, - { 0x090000b1, 0x64000000 }, - { 0x098000b2, 0x3c000001 }, - { 0x090000b4, 0x60000000 }, - { 0x090000b5, 0x140002e7 }, - { 0x090000b6, 0x68000000 }, - { 0x090000b7, 0x54000000 }, - { 0x090000b8, 0x60000000 }, - { 0x090000b9, 0x3c000000 }, - { 0x210000ba, 0x14000000 }, - { 0x090000bb, 0x4c000000 }, - { 0x098000bc, 0x3c000002 }, - { 0x090000bf, 0x54000000 }, - { 0x210000c0, 0x24000020 }, - { 0x210000c1, 0x24000020 }, - { 0x210000c2, 0x24000020 }, - { 0x210000c3, 0x24000020 }, - { 0x210000c4, 0x24000020 }, - { 0x210000c5, 0x24000020 }, - { 0x210000c6, 0x24000020 }, - { 0x210000c7, 0x24000020 }, - { 0x210000c8, 0x24000020 }, - { 0x210000c9, 0x24000020 }, - { 0x210000ca, 0x24000020 }, - { 0x210000cb, 0x24000020 }, - { 0x210000cc, 0x24000020 }, - { 0x210000cd, 0x24000020 }, - { 0x210000ce, 0x24000020 }, - { 0x210000cf, 0x24000020 }, - { 0x210000d0, 0x24000020 }, - { 0x210000d1, 0x24000020 }, - { 0x210000d2, 0x24000020 }, - { 0x210000d3, 0x24000020 }, - { 0x210000d4, 0x24000020 }, - { 0x210000d5, 0x24000020 }, - { 0x210000d6, 0x24000020 }, - { 0x090000d7, 0x64000000 }, - { 0x210000d8, 0x24000020 }, - { 0x210000d9, 0x24000020 }, - { 0x210000da, 0x24000020 }, - { 0x210000db, 0x24000020 }, - { 0x210000dc, 0x24000020 }, - { 0x210000dd, 0x24000020 }, - { 0x210000de, 0x24000020 }, - { 0x210000df, 0x14000000 }, - { 0x210000e0, 0x1400ffe0 }, - { 0x210000e1, 0x1400ffe0 }, - { 0x210000e2, 0x1400ffe0 }, - { 0x210000e3, 0x1400ffe0 }, - { 0x210000e4, 0x1400ffe0 }, - { 0x210000e5, 0x1400ffe0 }, - { 0x210000e6, 0x1400ffe0 }, - { 0x210000e7, 0x1400ffe0 }, - { 0x210000e8, 0x1400ffe0 }, - { 0x210000e9, 0x1400ffe0 }, - { 0x210000ea, 0x1400ffe0 }, - { 0x210000eb, 0x1400ffe0 }, - { 0x210000ec, 0x1400ffe0 }, - { 0x210000ed, 0x1400ffe0 }, - { 0x210000ee, 0x1400ffe0 }, - { 0x210000ef, 0x1400ffe0 }, - { 0x210000f0, 0x1400ffe0 }, - { 0x210000f1, 0x1400ffe0 }, - { 0x210000f2, 0x1400ffe0 }, - { 0x210000f3, 0x1400ffe0 }, - { 0x210000f4, 0x1400ffe0 }, - { 0x210000f5, 0x1400ffe0 }, - { 0x210000f6, 0x1400ffe0 }, - { 0x090000f7, 0x64000000 }, - { 0x210000f8, 0x1400ffe0 }, - { 0x210000f9, 0x1400ffe0 }, - { 0x210000fa, 0x1400ffe0 }, - { 0x210000fb, 0x1400ffe0 }, - { 0x210000fc, 0x1400ffe0 }, - { 0x210000fd, 0x1400ffe0 }, - { 0x210000fe, 0x1400ffe0 }, - { 0x210000ff, 0x14000079 }, - { 0x21000100, 0x24000001 }, - { 0x21000101, 0x1400ffff }, - { 0x21000102, 0x24000001 }, - { 0x21000103, 0x1400ffff }, - { 0x21000104, 0x24000001 }, - { 0x21000105, 0x1400ffff }, - { 0x21000106, 0x24000001 }, - { 0x21000107, 0x1400ffff }, - { 0x21000108, 0x24000001 }, - { 0x21000109, 0x1400ffff }, - { 0x2100010a, 0x24000001 }, - { 0x2100010b, 0x1400ffff }, - { 0x2100010c, 0x24000001 }, - { 0x2100010d, 0x1400ffff }, - { 0x2100010e, 0x24000001 }, - { 0x2100010f, 0x1400ffff }, - { 0x21000110, 0x24000001 }, - { 0x21000111, 0x1400ffff }, - { 0x21000112, 0x24000001 }, - { 0x21000113, 0x1400ffff }, - { 0x21000114, 0x24000001 }, - { 0x21000115, 0x1400ffff }, - { 0x21000116, 0x24000001 }, - { 0x21000117, 0x1400ffff }, - { 0x21000118, 0x24000001 }, - { 0x21000119, 0x1400ffff }, - { 0x2100011a, 0x24000001 }, - { 0x2100011b, 0x1400ffff }, - { 0x2100011c, 0x24000001 }, - { 0x2100011d, 0x1400ffff }, - { 0x2100011e, 0x24000001 }, - { 0x2100011f, 0x1400ffff }, - { 0x21000120, 0x24000001 }, - { 0x21000121, 0x1400ffff }, - { 0x21000122, 0x24000001 }, - { 0x21000123, 0x1400ffff }, - { 0x21000124, 0x24000001 }, - { 0x21000125, 0x1400ffff }, - { 0x21000126, 0x24000001 }, - { 0x21000127, 0x1400ffff }, - { 0x21000128, 0x24000001 }, - { 0x21000129, 0x1400ffff }, - { 0x2100012a, 0x24000001 }, - { 0x2100012b, 0x1400ffff }, - { 0x2100012c, 0x24000001 }, - { 0x2100012d, 0x1400ffff }, - { 0x2100012e, 0x24000001 }, - { 0x2100012f, 0x1400ffff }, - { 0x21000130, 0x2400ff39 }, - { 0x21000131, 0x1400ff18 }, - { 0x21000132, 0x24000001 }, - { 0x21000133, 0x1400ffff }, - { 0x21000134, 0x24000001 }, - { 0x21000135, 0x1400ffff }, - { 0x21000136, 0x24000001 }, - { 0x21000137, 0x1400ffff }, - { 0x21000138, 0x14000000 }, - { 0x21000139, 0x24000001 }, - { 0x2100013a, 0x1400ffff }, - { 0x2100013b, 0x24000001 }, - { 0x2100013c, 0x1400ffff }, - { 0x2100013d, 0x24000001 }, - { 0x2100013e, 0x1400ffff }, - { 0x2100013f, 0x24000001 }, - { 0x21000140, 0x1400ffff }, - { 0x21000141, 0x24000001 }, - { 0x21000142, 0x1400ffff }, - { 0x21000143, 0x24000001 }, - { 0x21000144, 0x1400ffff }, - { 0x21000145, 0x24000001 }, - { 0x21000146, 0x1400ffff }, - { 0x21000147, 0x24000001 }, - { 0x21000148, 0x1400ffff }, - { 0x21000149, 0x14000000 }, - { 0x2100014a, 0x24000001 }, - { 0x2100014b, 0x1400ffff }, - { 0x2100014c, 0x24000001 }, - { 0x2100014d, 0x1400ffff }, - { 0x2100014e, 0x24000001 }, - { 0x2100014f, 0x1400ffff }, - { 0x21000150, 0x24000001 }, - { 0x21000151, 0x1400ffff }, - { 0x21000152, 0x24000001 }, - { 0x21000153, 0x1400ffff }, - { 0x21000154, 0x24000001 }, - { 0x21000155, 0x1400ffff }, - { 0x21000156, 0x24000001 }, - { 0x21000157, 0x1400ffff }, - { 0x21000158, 0x24000001 }, - { 0x21000159, 0x1400ffff }, - { 0x2100015a, 0x24000001 }, - { 0x2100015b, 0x1400ffff }, - { 0x2100015c, 0x24000001 }, - { 0x2100015d, 0x1400ffff }, - { 0x2100015e, 0x24000001 }, - { 0x2100015f, 0x1400ffff }, - { 0x21000160, 0x24000001 }, - { 0x21000161, 0x1400ffff }, - { 0x21000162, 0x24000001 }, - { 0x21000163, 0x1400ffff }, - { 0x21000164, 0x24000001 }, - { 0x21000165, 0x1400ffff }, - { 0x21000166, 0x24000001 }, - { 0x21000167, 0x1400ffff }, - { 0x21000168, 0x24000001 }, - { 0x21000169, 0x1400ffff }, - { 0x2100016a, 0x24000001 }, - { 0x2100016b, 0x1400ffff }, - { 0x2100016c, 0x24000001 }, - { 0x2100016d, 0x1400ffff }, - { 0x2100016e, 0x24000001 }, - { 0x2100016f, 0x1400ffff }, - { 0x21000170, 0x24000001 }, - { 0x21000171, 0x1400ffff }, - { 0x21000172, 0x24000001 }, - { 0x21000173, 0x1400ffff }, - { 0x21000174, 0x24000001 }, - { 0x21000175, 0x1400ffff }, - { 0x21000176, 0x24000001 }, - { 0x21000177, 0x1400ffff }, - { 0x21000178, 0x2400ff87 }, - { 0x21000179, 0x24000001 }, - { 0x2100017a, 0x1400ffff }, - { 0x2100017b, 0x24000001 }, - { 0x2100017c, 0x1400ffff }, - { 0x2100017d, 0x24000001 }, - { 0x2100017e, 0x1400ffff }, - { 0x2100017f, 0x1400fed4 }, - { 0x21000180, 0x140000c3 }, - { 0x21000181, 0x240000d2 }, - { 0x21000182, 0x24000001 }, - { 0x21000183, 0x1400ffff }, - { 0x21000184, 0x24000001 }, - { 0x21000185, 0x1400ffff }, - { 0x21000186, 0x240000ce }, - { 0x21000187, 0x24000001 }, - { 0x21000188, 0x1400ffff }, - { 0x21000189, 0x240000cd }, - { 0x2100018a, 0x240000cd }, - { 0x2100018b, 0x24000001 }, - { 0x2100018c, 0x1400ffff }, - { 0x2100018d, 0x14000000 }, - { 0x2100018e, 0x2400004f }, - { 0x2100018f, 0x240000ca }, - { 0x21000190, 0x240000cb }, - { 0x21000191, 0x24000001 }, - { 0x21000192, 0x1400ffff }, - { 0x21000193, 0x240000cd }, - { 0x21000194, 0x240000cf }, - { 0x21000195, 0x14000061 }, - { 0x21000196, 0x240000d3 }, - { 0x21000197, 0x240000d1 }, - { 0x21000198, 0x24000001 }, - { 0x21000199, 0x1400ffff }, - { 0x2100019a, 0x140000a3 }, - { 0x2100019b, 0x14000000 }, - { 0x2100019c, 0x240000d3 }, - { 0x2100019d, 0x240000d5 }, - { 0x2100019e, 0x14000082 }, - { 0x2100019f, 0x240000d6 }, - { 0x210001a0, 0x24000001 }, - { 0x210001a1, 0x1400ffff }, - { 0x210001a2, 0x24000001 }, - { 0x210001a3, 0x1400ffff }, - { 0x210001a4, 0x24000001 }, - { 0x210001a5, 0x1400ffff }, - { 0x210001a6, 0x240000da }, - { 0x210001a7, 0x24000001 }, - { 0x210001a8, 0x1400ffff }, - { 0x210001a9, 0x240000da }, - { 0x218001aa, 0x14000001 }, - { 0x210001ac, 0x24000001 }, - { 0x210001ad, 0x1400ffff }, - { 0x210001ae, 0x240000da }, - { 0x210001af, 0x24000001 }, - { 0x210001b0, 0x1400ffff }, - { 0x210001b1, 0x240000d9 }, - { 0x210001b2, 0x240000d9 }, - { 0x210001b3, 0x24000001 }, - { 0x210001b4, 0x1400ffff }, - { 0x210001b5, 0x24000001 }, - { 0x210001b6, 0x1400ffff }, - { 0x210001b7, 0x240000db }, - { 0x210001b8, 0x24000001 }, - { 0x210001b9, 0x1400ffff }, - { 0x210001ba, 0x14000000 }, - { 0x210001bb, 0x1c000000 }, - { 0x210001bc, 0x24000001 }, - { 0x210001bd, 0x1400ffff }, - { 0x210001be, 0x14000000 }, - { 0x210001bf, 0x14000038 }, - { 0x218001c0, 0x1c000003 }, - { 0x210001c4, 0x24000002 }, - { 0x210001c5, 0x2000ffff }, - { 0x210001c6, 0x1400fffe }, - { 0x210001c7, 0x24000002 }, - { 0x210001c8, 0x2000ffff }, - { 0x210001c9, 0x1400fffe }, - { 0x210001ca, 0x24000002 }, - { 0x210001cb, 0x2000ffff }, - { 0x210001cc, 0x1400fffe }, - { 0x210001cd, 0x24000001 }, - { 0x210001ce, 0x1400ffff }, - { 0x210001cf, 0x24000001 }, - { 0x210001d0, 0x1400ffff }, - { 0x210001d1, 0x24000001 }, - { 0x210001d2, 0x1400ffff }, - { 0x210001d3, 0x24000001 }, - { 0x210001d4, 0x1400ffff }, - { 0x210001d5, 0x24000001 }, - { 0x210001d6, 0x1400ffff }, - { 0x210001d7, 0x24000001 }, - { 0x210001d8, 0x1400ffff }, - { 0x210001d9, 0x24000001 }, - { 0x210001da, 0x1400ffff }, - { 0x210001db, 0x24000001 }, - { 0x210001dc, 0x1400ffff }, - { 0x210001dd, 0x1400ffb1 }, - { 0x210001de, 0x24000001 }, - { 0x210001df, 0x1400ffff }, - { 0x210001e0, 0x24000001 }, - { 0x210001e1, 0x1400ffff }, - { 0x210001e2, 0x24000001 }, - { 0x210001e3, 0x1400ffff }, - { 0x210001e4, 0x24000001 }, - { 0x210001e5, 0x1400ffff }, - { 0x210001e6, 0x24000001 }, - { 0x210001e7, 0x1400ffff }, - { 0x210001e8, 0x24000001 }, - { 0x210001e9, 0x1400ffff }, - { 0x210001ea, 0x24000001 }, - { 0x210001eb, 0x1400ffff }, - { 0x210001ec, 0x24000001 }, - { 0x210001ed, 0x1400ffff }, - { 0x210001ee, 0x24000001 }, - { 0x210001ef, 0x1400ffff }, - { 0x210001f0, 0x14000000 }, - { 0x210001f1, 0x24000002 }, - { 0x210001f2, 0x2000ffff }, - { 0x210001f3, 0x1400fffe }, - { 0x210001f4, 0x24000001 }, - { 0x210001f5, 0x1400ffff }, - { 0x210001f6, 0x2400ff9f }, - { 0x210001f7, 0x2400ffc8 }, - { 0x210001f8, 0x24000001 }, - { 0x210001f9, 0x1400ffff }, - { 0x210001fa, 0x24000001 }, - { 0x210001fb, 0x1400ffff }, - { 0x210001fc, 0x24000001 }, - { 0x210001fd, 0x1400ffff }, - { 0x210001fe, 0x24000001 }, - { 0x210001ff, 0x1400ffff }, - { 0x21000200, 0x24000001 }, - { 0x21000201, 0x1400ffff }, - { 0x21000202, 0x24000001 }, - { 0x21000203, 0x1400ffff }, - { 0x21000204, 0x24000001 }, - { 0x21000205, 0x1400ffff }, - { 0x21000206, 0x24000001 }, - { 0x21000207, 0x1400ffff }, - { 0x21000208, 0x24000001 }, - { 0x21000209, 0x1400ffff }, - { 0x2100020a, 0x24000001 }, - { 0x2100020b, 0x1400ffff }, - { 0x2100020c, 0x24000001 }, - { 0x2100020d, 0x1400ffff }, - { 0x2100020e, 0x24000001 }, - { 0x2100020f, 0x1400ffff }, - { 0x21000210, 0x24000001 }, - { 0x21000211, 0x1400ffff }, - { 0x21000212, 0x24000001 }, - { 0x21000213, 0x1400ffff }, - { 0x21000214, 0x24000001 }, - { 0x21000215, 0x1400ffff }, - { 0x21000216, 0x24000001 }, - { 0x21000217, 0x1400ffff }, - { 0x21000218, 0x24000001 }, - { 0x21000219, 0x1400ffff }, - { 0x2100021a, 0x24000001 }, - { 0x2100021b, 0x1400ffff }, - { 0x2100021c, 0x24000001 }, - { 0x2100021d, 0x1400ffff }, - { 0x2100021e, 0x24000001 }, - { 0x2100021f, 0x1400ffff }, - { 0x21000220, 0x2400ff7e }, - { 0x21000221, 0x14000000 }, - { 0x21000222, 0x24000001 }, - { 0x21000223, 0x1400ffff }, - { 0x21000224, 0x24000001 }, - { 0x21000225, 0x1400ffff }, - { 0x21000226, 0x24000001 }, - { 0x21000227, 0x1400ffff }, - { 0x21000228, 0x24000001 }, - { 0x21000229, 0x1400ffff }, - { 0x2100022a, 0x24000001 }, - { 0x2100022b, 0x1400ffff }, - { 0x2100022c, 0x24000001 }, - { 0x2100022d, 0x1400ffff }, - { 0x2100022e, 0x24000001 }, - { 0x2100022f, 0x1400ffff }, - { 0x21000230, 0x24000001 }, - { 0x21000231, 0x1400ffff }, - { 0x21000232, 0x24000001 }, - { 0x21000233, 0x1400ffff }, - { 0x21800234, 0x14000005 }, - { 0x2100023a, 0x24002a2b }, - { 0x2100023b, 0x24000001 }, - { 0x2100023c, 0x1400ffff }, - { 0x2100023d, 0x2400ff5d }, - { 0x2100023e, 0x24002a28 }, - { 0x2180023f, 0x14000001 }, - { 0x21000241, 0x24000001 }, - { 0x21000242, 0x1400ffff }, - { 0x21000243, 0x2400ff3d }, - { 0x21000244, 0x24000045 }, - { 0x21000245, 0x24000047 }, - { 0x21000246, 0x24000001 }, - { 0x21000247, 0x1400ffff }, - { 0x21000248, 0x24000001 }, - { 0x21000249, 0x1400ffff }, - { 0x2100024a, 0x24000001 }, - { 0x2100024b, 0x1400ffff }, - { 0x2100024c, 0x24000001 }, - { 0x2100024d, 0x1400ffff }, - { 0x2100024e, 0x24000001 }, - { 0x2100024f, 0x1400ffff }, - { 0x21800250, 0x14000002 }, - { 0x21000253, 0x1400ff2e }, - { 0x21000254, 0x1400ff32 }, - { 0x21000255, 0x14000000 }, - { 0x21000256, 0x1400ff33 }, - { 0x21000257, 0x1400ff33 }, - { 0x21000258, 0x14000000 }, - { 0x21000259, 0x1400ff36 }, - { 0x2100025a, 0x14000000 }, - { 0x2100025b, 0x1400ff35 }, - { 0x2180025c, 0x14000003 }, - { 0x21000260, 0x1400ff33 }, - { 0x21800261, 0x14000001 }, - { 0x21000263, 0x1400ff31 }, - { 0x21800264, 0x14000003 }, - { 0x21000268, 0x1400ff2f }, - { 0x21000269, 0x1400ff2d }, - { 0x2100026a, 0x14000000 }, - { 0x2100026b, 0x140029f7 }, - { 0x2180026c, 0x14000002 }, - { 0x2100026f, 0x1400ff2d }, - { 0x21800270, 0x14000001 }, - { 0x21000272, 0x1400ff2b }, - { 0x21800273, 0x14000001 }, - { 0x21000275, 0x1400ff2a }, - { 0x21800276, 0x14000006 }, - { 0x2100027d, 0x140029e7 }, - { 0x2180027e, 0x14000001 }, - { 0x21000280, 0x1400ff26 }, - { 0x21800281, 0x14000001 }, - { 0x21000283, 0x1400ff26 }, - { 0x21800284, 0x14000003 }, - { 0x21000288, 0x1400ff26 }, - { 0x21000289, 0x1400ffbb }, - { 0x2100028a, 0x1400ff27 }, - { 0x2100028b, 0x1400ff27 }, - { 0x2100028c, 0x1400ffb9 }, - { 0x2180028d, 0x14000004 }, - { 0x21000292, 0x1400ff25 }, - { 0x21000293, 0x14000000 }, - { 0x21000294, 0x1c000000 }, - { 0x21800295, 0x1400001a }, - { 0x218002b0, 0x18000008 }, - { 0x098002b9, 0x18000008 }, - { 0x098002c2, 0x60000003 }, - { 0x098002c6, 0x1800000b }, - { 0x098002d2, 0x6000000d }, - { 0x218002e0, 0x18000004 }, - { 0x098002e5, 0x60000008 }, - { 0x090002ee, 0x18000000 }, - { 0x098002ef, 0x60000010 }, - { 0x1b800300, 0x30000044 }, - { 0x1b000345, 0x30000054 }, - { 0x1b800346, 0x30000029 }, - { 0x13800374, 0x60000001 }, - { 0x1300037a, 0x18000000 }, - { 0x1300037b, 0x14000082 }, - { 0x1300037c, 0x14000082 }, - { 0x1300037d, 0x14000082 }, - { 0x0900037e, 0x54000000 }, - { 0x13800384, 0x60000001 }, - { 0x13000386, 0x24000026 }, - { 0x09000387, 0x54000000 }, - { 0x13000388, 0x24000025 }, - { 0x13000389, 0x24000025 }, - { 0x1300038a, 0x24000025 }, - { 0x1300038c, 0x24000040 }, - { 0x1300038e, 0x2400003f }, - { 0x1300038f, 0x2400003f }, - { 0x13000390, 0x14000000 }, - { 0x13000391, 0x24000020 }, - { 0x13000392, 0x24000020 }, - { 0x13000393, 0x24000020 }, - { 0x13000394, 0x24000020 }, - { 0x13000395, 0x24000020 }, - { 0x13000396, 0x24000020 }, - { 0x13000397, 0x24000020 }, - { 0x13000398, 0x24000020 }, - { 0x13000399, 0x24000020 }, - { 0x1300039a, 0x24000020 }, - { 0x1300039b, 0x24000020 }, - { 0x1300039c, 0x24000020 }, - { 0x1300039d, 0x24000020 }, - { 0x1300039e, 0x24000020 }, - { 0x1300039f, 0x24000020 }, - { 0x130003a0, 0x24000020 }, - { 0x130003a1, 0x24000020 }, - { 0x130003a3, 0x24000020 }, - { 0x130003a4, 0x24000020 }, - { 0x130003a5, 0x24000020 }, - { 0x130003a6, 0x24000020 }, - { 0x130003a7, 0x24000020 }, - { 0x130003a8, 0x24000020 }, - { 0x130003a9, 0x24000020 }, - { 0x130003aa, 0x24000020 }, - { 0x130003ab, 0x24000020 }, - { 0x130003ac, 0x1400ffda }, - { 0x130003ad, 0x1400ffdb }, - { 0x130003ae, 0x1400ffdb }, - { 0x130003af, 0x1400ffdb }, - { 0x130003b0, 0x14000000 }, - { 0x130003b1, 0x1400ffe0 }, - { 0x130003b2, 0x1400ffe0 }, - { 0x130003b3, 0x1400ffe0 }, - { 0x130003b4, 0x1400ffe0 }, - { 0x130003b5, 0x1400ffe0 }, - { 0x130003b6, 0x1400ffe0 }, - { 0x130003b7, 0x1400ffe0 }, - { 0x130003b8, 0x1400ffe0 }, - { 0x130003b9, 0x1400ffe0 }, - { 0x130003ba, 0x1400ffe0 }, - { 0x130003bb, 0x1400ffe0 }, - { 0x130003bc, 0x1400ffe0 }, - { 0x130003bd, 0x1400ffe0 }, - { 0x130003be, 0x1400ffe0 }, - { 0x130003bf, 0x1400ffe0 }, - { 0x130003c0, 0x1400ffe0 }, - { 0x130003c1, 0x1400ffe0 }, - { 0x130003c2, 0x1400ffe1 }, - { 0x130003c3, 0x1400ffe0 }, - { 0x130003c4, 0x1400ffe0 }, - { 0x130003c5, 0x1400ffe0 }, - { 0x130003c6, 0x1400ffe0 }, - { 0x130003c7, 0x1400ffe0 }, - { 0x130003c8, 0x1400ffe0 }, - { 0x130003c9, 0x1400ffe0 }, - { 0x130003ca, 0x1400ffe0 }, - { 0x130003cb, 0x1400ffe0 }, - { 0x130003cc, 0x1400ffc0 }, - { 0x130003cd, 0x1400ffc1 }, - { 0x130003ce, 0x1400ffc1 }, - { 0x130003d0, 0x1400ffc2 }, - { 0x130003d1, 0x1400ffc7 }, - { 0x138003d2, 0x24000002 }, - { 0x130003d5, 0x1400ffd1 }, - { 0x130003d6, 0x1400ffca }, - { 0x130003d7, 0x14000000 }, - { 0x130003d8, 0x24000001 }, - { 0x130003d9, 0x1400ffff }, - { 0x130003da, 0x24000001 }, - { 0x130003db, 0x1400ffff }, - { 0x130003dc, 0x24000001 }, - { 0x130003dd, 0x1400ffff }, - { 0x130003de, 0x24000001 }, - { 0x130003df, 0x1400ffff }, - { 0x130003e0, 0x24000001 }, - { 0x130003e1, 0x1400ffff }, - { 0x0a0003e2, 0x24000001 }, - { 0x0a0003e3, 0x1400ffff }, - { 0x0a0003e4, 0x24000001 }, - { 0x0a0003e5, 0x1400ffff }, - { 0x0a0003e6, 0x24000001 }, - { 0x0a0003e7, 0x1400ffff }, - { 0x0a0003e8, 0x24000001 }, - { 0x0a0003e9, 0x1400ffff }, - { 0x0a0003ea, 0x24000001 }, - { 0x0a0003eb, 0x1400ffff }, - { 0x0a0003ec, 0x24000001 }, - { 0x0a0003ed, 0x1400ffff }, - { 0x0a0003ee, 0x24000001 }, - { 0x0a0003ef, 0x1400ffff }, - { 0x130003f0, 0x1400ffaa }, - { 0x130003f1, 0x1400ffb0 }, - { 0x130003f2, 0x14000007 }, - { 0x130003f3, 0x14000000 }, - { 0x130003f4, 0x2400ffc4 }, - { 0x130003f5, 0x1400ffa0 }, - { 0x130003f6, 0x64000000 }, - { 0x130003f7, 0x24000001 }, - { 0x130003f8, 0x1400ffff }, - { 0x130003f9, 0x2400fff9 }, - { 0x130003fa, 0x24000001 }, - { 0x130003fb, 0x1400ffff }, - { 0x130003fc, 0x14000000 }, - { 0x130003fd, 0x2400ff7e }, - { 0x130003fe, 0x2400ff7e }, - { 0x130003ff, 0x2400ff7e }, - { 0x0c000400, 0x24000050 }, - { 0x0c000401, 0x24000050 }, - { 0x0c000402, 0x24000050 }, - { 0x0c000403, 0x24000050 }, - { 0x0c000404, 0x24000050 }, - { 0x0c000405, 0x24000050 }, - { 0x0c000406, 0x24000050 }, - { 0x0c000407, 0x24000050 }, - { 0x0c000408, 0x24000050 }, - { 0x0c000409, 0x24000050 }, - { 0x0c00040a, 0x24000050 }, - { 0x0c00040b, 0x24000050 }, - { 0x0c00040c, 0x24000050 }, - { 0x0c00040d, 0x24000050 }, - { 0x0c00040e, 0x24000050 }, - { 0x0c00040f, 0x24000050 }, - { 0x0c000410, 0x24000020 }, - { 0x0c000411, 0x24000020 }, - { 0x0c000412, 0x24000020 }, - { 0x0c000413, 0x24000020 }, - { 0x0c000414, 0x24000020 }, - { 0x0c000415, 0x24000020 }, - { 0x0c000416, 0x24000020 }, - { 0x0c000417, 0x24000020 }, - { 0x0c000418, 0x24000020 }, - { 0x0c000419, 0x24000020 }, - { 0x0c00041a, 0x24000020 }, - { 0x0c00041b, 0x24000020 }, - { 0x0c00041c, 0x24000020 }, - { 0x0c00041d, 0x24000020 }, - { 0x0c00041e, 0x24000020 }, - { 0x0c00041f, 0x24000020 }, - { 0x0c000420, 0x24000020 }, - { 0x0c000421, 0x24000020 }, - { 0x0c000422, 0x24000020 }, - { 0x0c000423, 0x24000020 }, - { 0x0c000424, 0x24000020 }, - { 0x0c000425, 0x24000020 }, - { 0x0c000426, 0x24000020 }, - { 0x0c000427, 0x24000020 }, - { 0x0c000428, 0x24000020 }, - { 0x0c000429, 0x24000020 }, - { 0x0c00042a, 0x24000020 }, - { 0x0c00042b, 0x24000020 }, - { 0x0c00042c, 0x24000020 }, - { 0x0c00042d, 0x24000020 }, - { 0x0c00042e, 0x24000020 }, - { 0x0c00042f, 0x24000020 }, - { 0x0c000430, 0x1400ffe0 }, - { 0x0c000431, 0x1400ffe0 }, - { 0x0c000432, 0x1400ffe0 }, - { 0x0c000433, 0x1400ffe0 }, - { 0x0c000434, 0x1400ffe0 }, - { 0x0c000435, 0x1400ffe0 }, - { 0x0c000436, 0x1400ffe0 }, - { 0x0c000437, 0x1400ffe0 }, - { 0x0c000438, 0x1400ffe0 }, - { 0x0c000439, 0x1400ffe0 }, - { 0x0c00043a, 0x1400ffe0 }, - { 0x0c00043b, 0x1400ffe0 }, - { 0x0c00043c, 0x1400ffe0 }, - { 0x0c00043d, 0x1400ffe0 }, - { 0x0c00043e, 0x1400ffe0 }, - { 0x0c00043f, 0x1400ffe0 }, - { 0x0c000440, 0x1400ffe0 }, - { 0x0c000441, 0x1400ffe0 }, - { 0x0c000442, 0x1400ffe0 }, - { 0x0c000443, 0x1400ffe0 }, - { 0x0c000444, 0x1400ffe0 }, - { 0x0c000445, 0x1400ffe0 }, - { 0x0c000446, 0x1400ffe0 }, - { 0x0c000447, 0x1400ffe0 }, - { 0x0c000448, 0x1400ffe0 }, - { 0x0c000449, 0x1400ffe0 }, - { 0x0c00044a, 0x1400ffe0 }, - { 0x0c00044b, 0x1400ffe0 }, - { 0x0c00044c, 0x1400ffe0 }, - { 0x0c00044d, 0x1400ffe0 }, - { 0x0c00044e, 0x1400ffe0 }, - { 0x0c00044f, 0x1400ffe0 }, - { 0x0c000450, 0x1400ffb0 }, - { 0x0c000451, 0x1400ffb0 }, - { 0x0c000452, 0x1400ffb0 }, - { 0x0c000453, 0x1400ffb0 }, - { 0x0c000454, 0x1400ffb0 }, - { 0x0c000455, 0x1400ffb0 }, - { 0x0c000456, 0x1400ffb0 }, - { 0x0c000457, 0x1400ffb0 }, - { 0x0c000458, 0x1400ffb0 }, - { 0x0c000459, 0x1400ffb0 }, - { 0x0c00045a, 0x1400ffb0 }, - { 0x0c00045b, 0x1400ffb0 }, - { 0x0c00045c, 0x1400ffb0 }, - { 0x0c00045d, 0x1400ffb0 }, - { 0x0c00045e, 0x1400ffb0 }, - { 0x0c00045f, 0x1400ffb0 }, - { 0x0c000460, 0x24000001 }, - { 0x0c000461, 0x1400ffff }, - { 0x0c000462, 0x24000001 }, - { 0x0c000463, 0x1400ffff }, - { 0x0c000464, 0x24000001 }, - { 0x0c000465, 0x1400ffff }, - { 0x0c000466, 0x24000001 }, - { 0x0c000467, 0x1400ffff }, - { 0x0c000468, 0x24000001 }, - { 0x0c000469, 0x1400ffff }, - { 0x0c00046a, 0x24000001 }, - { 0x0c00046b, 0x1400ffff }, - { 0x0c00046c, 0x24000001 }, - { 0x0c00046d, 0x1400ffff }, - { 0x0c00046e, 0x24000001 }, - { 0x0c00046f, 0x1400ffff }, - { 0x0c000470, 0x24000001 }, - { 0x0c000471, 0x1400ffff }, - { 0x0c000472, 0x24000001 }, - { 0x0c000473, 0x1400ffff }, - { 0x0c000474, 0x24000001 }, - { 0x0c000475, 0x1400ffff }, - { 0x0c000476, 0x24000001 }, - { 0x0c000477, 0x1400ffff }, - { 0x0c000478, 0x24000001 }, - { 0x0c000479, 0x1400ffff }, - { 0x0c00047a, 0x24000001 }, - { 0x0c00047b, 0x1400ffff }, - { 0x0c00047c, 0x24000001 }, - { 0x0c00047d, 0x1400ffff }, - { 0x0c00047e, 0x24000001 }, - { 0x0c00047f, 0x1400ffff }, - { 0x0c000480, 0x24000001 }, - { 0x0c000481, 0x1400ffff }, - { 0x0c000482, 0x68000000 }, - { 0x0c800483, 0x30000003 }, - { 0x0c800488, 0x2c000001 }, - { 0x0c00048a, 0x24000001 }, - { 0x0c00048b, 0x1400ffff }, - { 0x0c00048c, 0x24000001 }, - { 0x0c00048d, 0x1400ffff }, - { 0x0c00048e, 0x24000001 }, - { 0x0c00048f, 0x1400ffff }, - { 0x0c000490, 0x24000001 }, - { 0x0c000491, 0x1400ffff }, - { 0x0c000492, 0x24000001 }, - { 0x0c000493, 0x1400ffff }, - { 0x0c000494, 0x24000001 }, - { 0x0c000495, 0x1400ffff }, - { 0x0c000496, 0x24000001 }, - { 0x0c000497, 0x1400ffff }, - { 0x0c000498, 0x24000001 }, - { 0x0c000499, 0x1400ffff }, - { 0x0c00049a, 0x24000001 }, - { 0x0c00049b, 0x1400ffff }, - { 0x0c00049c, 0x24000001 }, - { 0x0c00049d, 0x1400ffff }, - { 0x0c00049e, 0x24000001 }, - { 0x0c00049f, 0x1400ffff }, - { 0x0c0004a0, 0x24000001 }, - { 0x0c0004a1, 0x1400ffff }, - { 0x0c0004a2, 0x24000001 }, - { 0x0c0004a3, 0x1400ffff }, - { 0x0c0004a4, 0x24000001 }, - { 0x0c0004a5, 0x1400ffff }, - { 0x0c0004a6, 0x24000001 }, - { 0x0c0004a7, 0x1400ffff }, - { 0x0c0004a8, 0x24000001 }, - { 0x0c0004a9, 0x1400ffff }, - { 0x0c0004aa, 0x24000001 }, - { 0x0c0004ab, 0x1400ffff }, - { 0x0c0004ac, 0x24000001 }, - { 0x0c0004ad, 0x1400ffff }, - { 0x0c0004ae, 0x24000001 }, - { 0x0c0004af, 0x1400ffff }, - { 0x0c0004b0, 0x24000001 }, - { 0x0c0004b1, 0x1400ffff }, - { 0x0c0004b2, 0x24000001 }, - { 0x0c0004b3, 0x1400ffff }, - { 0x0c0004b4, 0x24000001 }, - { 0x0c0004b5, 0x1400ffff }, - { 0x0c0004b6, 0x24000001 }, - { 0x0c0004b7, 0x1400ffff }, - { 0x0c0004b8, 0x24000001 }, - { 0x0c0004b9, 0x1400ffff }, - { 0x0c0004ba, 0x24000001 }, - { 0x0c0004bb, 0x1400ffff }, - { 0x0c0004bc, 0x24000001 }, - { 0x0c0004bd, 0x1400ffff }, - { 0x0c0004be, 0x24000001 }, - { 0x0c0004bf, 0x1400ffff }, - { 0x0c0004c0, 0x2400000f }, - { 0x0c0004c1, 0x24000001 }, - { 0x0c0004c2, 0x1400ffff }, - { 0x0c0004c3, 0x24000001 }, - { 0x0c0004c4, 0x1400ffff }, - { 0x0c0004c5, 0x24000001 }, - { 0x0c0004c6, 0x1400ffff }, - { 0x0c0004c7, 0x24000001 }, - { 0x0c0004c8, 0x1400ffff }, - { 0x0c0004c9, 0x24000001 }, - { 0x0c0004ca, 0x1400ffff }, - { 0x0c0004cb, 0x24000001 }, - { 0x0c0004cc, 0x1400ffff }, - { 0x0c0004cd, 0x24000001 }, - { 0x0c0004ce, 0x1400ffff }, - { 0x0c0004cf, 0x1400fff1 }, - { 0x0c0004d0, 0x24000001 }, - { 0x0c0004d1, 0x1400ffff }, - { 0x0c0004d2, 0x24000001 }, - { 0x0c0004d3, 0x1400ffff }, - { 0x0c0004d4, 0x24000001 }, - { 0x0c0004d5, 0x1400ffff }, - { 0x0c0004d6, 0x24000001 }, - { 0x0c0004d7, 0x1400ffff }, - { 0x0c0004d8, 0x24000001 }, - { 0x0c0004d9, 0x1400ffff }, - { 0x0c0004da, 0x24000001 }, - { 0x0c0004db, 0x1400ffff }, - { 0x0c0004dc, 0x24000001 }, - { 0x0c0004dd, 0x1400ffff }, - { 0x0c0004de, 0x24000001 }, - { 0x0c0004df, 0x1400ffff }, - { 0x0c0004e0, 0x24000001 }, - { 0x0c0004e1, 0x1400ffff }, - { 0x0c0004e2, 0x24000001 }, - { 0x0c0004e3, 0x1400ffff }, - { 0x0c0004e4, 0x24000001 }, - { 0x0c0004e5, 0x1400ffff }, - { 0x0c0004e6, 0x24000001 }, - { 0x0c0004e7, 0x1400ffff }, - { 0x0c0004e8, 0x24000001 }, - { 0x0c0004e9, 0x1400ffff }, - { 0x0c0004ea, 0x24000001 }, - { 0x0c0004eb, 0x1400ffff }, - { 0x0c0004ec, 0x24000001 }, - { 0x0c0004ed, 0x1400ffff }, - { 0x0c0004ee, 0x24000001 }, - { 0x0c0004ef, 0x1400ffff }, - { 0x0c0004f0, 0x24000001 }, - { 0x0c0004f1, 0x1400ffff }, - { 0x0c0004f2, 0x24000001 }, - { 0x0c0004f3, 0x1400ffff }, - { 0x0c0004f4, 0x24000001 }, - { 0x0c0004f5, 0x1400ffff }, - { 0x0c0004f6, 0x24000001 }, - { 0x0c0004f7, 0x1400ffff }, - { 0x0c0004f8, 0x24000001 }, - { 0x0c0004f9, 0x1400ffff }, - { 0x0c0004fa, 0x24000001 }, - { 0x0c0004fb, 0x1400ffff }, - { 0x0c0004fc, 0x24000001 }, - { 0x0c0004fd, 0x1400ffff }, - { 0x0c0004fe, 0x24000001 }, - { 0x0c0004ff, 0x1400ffff }, - { 0x0c000500, 0x24000001 }, - { 0x0c000501, 0x1400ffff }, - { 0x0c000502, 0x24000001 }, - { 0x0c000503, 0x1400ffff }, - { 0x0c000504, 0x24000001 }, - { 0x0c000505, 0x1400ffff }, - { 0x0c000506, 0x24000001 }, - { 0x0c000507, 0x1400ffff }, - { 0x0c000508, 0x24000001 }, - { 0x0c000509, 0x1400ffff }, - { 0x0c00050a, 0x24000001 }, - { 0x0c00050b, 0x1400ffff }, - { 0x0c00050c, 0x24000001 }, - { 0x0c00050d, 0x1400ffff }, - { 0x0c00050e, 0x24000001 }, - { 0x0c00050f, 0x1400ffff }, - { 0x0c000510, 0x24000001 }, - { 0x0c000511, 0x1400ffff }, - { 0x0c000512, 0x24000001 }, - { 0x0c000513, 0x1400ffff }, - { 0x01000531, 0x24000030 }, - { 0x01000532, 0x24000030 }, - { 0x01000533, 0x24000030 }, - { 0x01000534, 0x24000030 }, - { 0x01000535, 0x24000030 }, - { 0x01000536, 0x24000030 }, - { 0x01000537, 0x24000030 }, - { 0x01000538, 0x24000030 }, - { 0x01000539, 0x24000030 }, - { 0x0100053a, 0x24000030 }, - { 0x0100053b, 0x24000030 }, - { 0x0100053c, 0x24000030 }, - { 0x0100053d, 0x24000030 }, - { 0x0100053e, 0x24000030 }, - { 0x0100053f, 0x24000030 }, - { 0x01000540, 0x24000030 }, - { 0x01000541, 0x24000030 }, - { 0x01000542, 0x24000030 }, - { 0x01000543, 0x24000030 }, - { 0x01000544, 0x24000030 }, - { 0x01000545, 0x24000030 }, - { 0x01000546, 0x24000030 }, - { 0x01000547, 0x24000030 }, - { 0x01000548, 0x24000030 }, - { 0x01000549, 0x24000030 }, - { 0x0100054a, 0x24000030 }, - { 0x0100054b, 0x24000030 }, - { 0x0100054c, 0x24000030 }, - { 0x0100054d, 0x24000030 }, - { 0x0100054e, 0x24000030 }, - { 0x0100054f, 0x24000030 }, - { 0x01000550, 0x24000030 }, - { 0x01000551, 0x24000030 }, - { 0x01000552, 0x24000030 }, - { 0x01000553, 0x24000030 }, - { 0x01000554, 0x24000030 }, - { 0x01000555, 0x24000030 }, - { 0x01000556, 0x24000030 }, - { 0x01000559, 0x18000000 }, - { 0x0180055a, 0x54000005 }, - { 0x01000561, 0x1400ffd0 }, - { 0x01000562, 0x1400ffd0 }, - { 0x01000563, 0x1400ffd0 }, - { 0x01000564, 0x1400ffd0 }, - { 0x01000565, 0x1400ffd0 }, - { 0x01000566, 0x1400ffd0 }, - { 0x01000567, 0x1400ffd0 }, - { 0x01000568, 0x1400ffd0 }, - { 0x01000569, 0x1400ffd0 }, - { 0x0100056a, 0x1400ffd0 }, - { 0x0100056b, 0x1400ffd0 }, - { 0x0100056c, 0x1400ffd0 }, - { 0x0100056d, 0x1400ffd0 }, - { 0x0100056e, 0x1400ffd0 }, - { 0x0100056f, 0x1400ffd0 }, - { 0x01000570, 0x1400ffd0 }, - { 0x01000571, 0x1400ffd0 }, - { 0x01000572, 0x1400ffd0 }, - { 0x01000573, 0x1400ffd0 }, - { 0x01000574, 0x1400ffd0 }, - { 0x01000575, 0x1400ffd0 }, - { 0x01000576, 0x1400ffd0 }, - { 0x01000577, 0x1400ffd0 }, - { 0x01000578, 0x1400ffd0 }, - { 0x01000579, 0x1400ffd0 }, - { 0x0100057a, 0x1400ffd0 }, - { 0x0100057b, 0x1400ffd0 }, - { 0x0100057c, 0x1400ffd0 }, - { 0x0100057d, 0x1400ffd0 }, - { 0x0100057e, 0x1400ffd0 }, - { 0x0100057f, 0x1400ffd0 }, - { 0x01000580, 0x1400ffd0 }, - { 0x01000581, 0x1400ffd0 }, - { 0x01000582, 0x1400ffd0 }, - { 0x01000583, 0x1400ffd0 }, - { 0x01000584, 0x1400ffd0 }, - { 0x01000585, 0x1400ffd0 }, - { 0x01000586, 0x1400ffd0 }, - { 0x01000587, 0x14000000 }, - { 0x09000589, 0x54000000 }, - { 0x0100058a, 0x44000000 }, - { 0x19800591, 0x3000002c }, - { 0x190005be, 0x54000000 }, - { 0x190005bf, 0x30000000 }, - { 0x190005c0, 0x54000000 }, - { 0x198005c1, 0x30000001 }, - { 0x190005c3, 0x54000000 }, - { 0x198005c4, 0x30000001 }, - { 0x190005c6, 0x54000000 }, - { 0x190005c7, 0x30000000 }, - { 0x198005d0, 0x1c00001a }, - { 0x198005f0, 0x1c000002 }, - { 0x198005f3, 0x54000001 }, - { 0x09800600, 0x04000003 }, - { 0x0000060b, 0x5c000000 }, - { 0x0900060c, 0x54000000 }, - { 0x0000060d, 0x54000000 }, - { 0x0080060e, 0x68000001 }, - { 0x00800610, 0x30000005 }, - { 0x0900061b, 0x54000000 }, - { 0x0000061e, 0x54000000 }, - { 0x0900061f, 0x54000000 }, - { 0x00800621, 0x1c000019 }, - { 0x09000640, 0x18000000 }, - { 0x00800641, 0x1c000009 }, - { 0x1b80064b, 0x3000000a }, - { 0x00800656, 0x30000008 }, - { 0x09800660, 0x34000009 }, - { 0x0080066a, 0x54000003 }, - { 0x0080066e, 0x1c000001 }, - { 0x1b000670, 0x30000000 }, - { 0x00800671, 0x1c000062 }, - { 0x000006d4, 0x54000000 }, - { 0x000006d5, 0x1c000000 }, - { 0x008006d6, 0x30000006 }, - { 0x090006dd, 0x04000000 }, - { 0x000006de, 0x2c000000 }, - { 0x008006df, 0x30000005 }, - { 0x008006e5, 0x18000001 }, - { 0x008006e7, 0x30000001 }, - { 0x000006e9, 0x68000000 }, - { 0x008006ea, 0x30000003 }, - { 0x008006ee, 0x1c000001 }, - { 0x008006f0, 0x34000009 }, - { 0x008006fa, 0x1c000002 }, - { 0x008006fd, 0x68000001 }, - { 0x000006ff, 0x1c000000 }, - { 0x31800700, 0x5400000d }, - { 0x3100070f, 0x04000000 }, - { 0x31000710, 0x1c000000 }, - { 0x31000711, 0x30000000 }, - { 0x31800712, 0x1c00001d }, - { 0x31800730, 0x3000001a }, - { 0x3180074d, 0x1c000002 }, - { 0x00800750, 0x1c00001d }, - { 0x37800780, 0x1c000025 }, - { 0x378007a6, 0x3000000a }, - { 0x370007b1, 0x1c000000 }, - { 0x3f8007c0, 0x34000009 }, - { 0x3f8007ca, 0x1c000020 }, - { 0x3f8007eb, 0x30000008 }, - { 0x3f8007f4, 0x18000001 }, - { 0x3f0007f6, 0x68000000 }, - { 0x3f8007f7, 0x54000002 }, - { 0x3f0007fa, 0x18000000 }, - { 0x0e800901, 0x30000001 }, - { 0x0e000903, 0x28000000 }, - { 0x0e800904, 0x1c000035 }, - { 0x0e00093c, 0x30000000 }, - { 0x0e00093d, 0x1c000000 }, - { 0x0e80093e, 0x28000002 }, - { 0x0e800941, 0x30000007 }, - { 0x0e800949, 0x28000003 }, - { 0x0e00094d, 0x30000000 }, - { 0x0e000950, 0x1c000000 }, - { 0x0e800951, 0x30000003 }, - { 0x0e800958, 0x1c000009 }, - { 0x0e800962, 0x30000001 }, - { 0x09800964, 0x54000001 }, - { 0x0e800966, 0x34000009 }, - { 0x09000970, 0x54000000 }, - { 0x0e80097b, 0x1c000004 }, - { 0x02000981, 0x30000000 }, - { 0x02800982, 0x28000001 }, - { 0x02800985, 0x1c000007 }, - { 0x0280098f, 0x1c000001 }, - { 0x02800993, 0x1c000015 }, - { 0x028009aa, 0x1c000006 }, - { 0x020009b2, 0x1c000000 }, - { 0x028009b6, 0x1c000003 }, - { 0x020009bc, 0x30000000 }, - { 0x020009bd, 0x1c000000 }, - { 0x028009be, 0x28000002 }, - { 0x028009c1, 0x30000003 }, - { 0x028009c7, 0x28000001 }, - { 0x028009cb, 0x28000001 }, - { 0x020009cd, 0x30000000 }, - { 0x020009ce, 0x1c000000 }, - { 0x020009d7, 0x28000000 }, - { 0x028009dc, 0x1c000001 }, - { 0x028009df, 0x1c000002 }, - { 0x028009e2, 0x30000001 }, - { 0x028009e6, 0x34000009 }, - { 0x028009f0, 0x1c000001 }, - { 0x028009f2, 0x5c000001 }, - { 0x028009f4, 0x3c000005 }, - { 0x020009fa, 0x68000000 }, - { 0x15800a01, 0x30000001 }, - { 0x15000a03, 0x28000000 }, - { 0x15800a05, 0x1c000005 }, - { 0x15800a0f, 0x1c000001 }, - { 0x15800a13, 0x1c000015 }, - { 0x15800a2a, 0x1c000006 }, - { 0x15800a32, 0x1c000001 }, - { 0x15800a35, 0x1c000001 }, - { 0x15800a38, 0x1c000001 }, - { 0x15000a3c, 0x30000000 }, - { 0x15800a3e, 0x28000002 }, - { 0x15800a41, 0x30000001 }, - { 0x15800a47, 0x30000001 }, - { 0x15800a4b, 0x30000002 }, - { 0x15800a59, 0x1c000003 }, - { 0x15000a5e, 0x1c000000 }, - { 0x15800a66, 0x34000009 }, - { 0x15800a70, 0x30000001 }, - { 0x15800a72, 0x1c000002 }, - { 0x14800a81, 0x30000001 }, - { 0x14000a83, 0x28000000 }, - { 0x14800a85, 0x1c000008 }, - { 0x14800a8f, 0x1c000002 }, - { 0x14800a93, 0x1c000015 }, - { 0x14800aaa, 0x1c000006 }, - { 0x14800ab2, 0x1c000001 }, - { 0x14800ab5, 0x1c000004 }, - { 0x14000abc, 0x30000000 }, - { 0x14000abd, 0x1c000000 }, - { 0x14800abe, 0x28000002 }, - { 0x14800ac1, 0x30000004 }, - { 0x14800ac7, 0x30000001 }, - { 0x14000ac9, 0x28000000 }, - { 0x14800acb, 0x28000001 }, - { 0x14000acd, 0x30000000 }, - { 0x14000ad0, 0x1c000000 }, - { 0x14800ae0, 0x1c000001 }, - { 0x14800ae2, 0x30000001 }, - { 0x14800ae6, 0x34000009 }, - { 0x14000af1, 0x5c000000 }, - { 0x2b000b01, 0x30000000 }, - { 0x2b800b02, 0x28000001 }, - { 0x2b800b05, 0x1c000007 }, - { 0x2b800b0f, 0x1c000001 }, - { 0x2b800b13, 0x1c000015 }, - { 0x2b800b2a, 0x1c000006 }, - { 0x2b800b32, 0x1c000001 }, - { 0x2b800b35, 0x1c000004 }, - { 0x2b000b3c, 0x30000000 }, - { 0x2b000b3d, 0x1c000000 }, - { 0x2b000b3e, 0x28000000 }, - { 0x2b000b3f, 0x30000000 }, - { 0x2b000b40, 0x28000000 }, - { 0x2b800b41, 0x30000002 }, - { 0x2b800b47, 0x28000001 }, - { 0x2b800b4b, 0x28000001 }, - { 0x2b000b4d, 0x30000000 }, - { 0x2b000b56, 0x30000000 }, - { 0x2b000b57, 0x28000000 }, - { 0x2b800b5c, 0x1c000001 }, - { 0x2b800b5f, 0x1c000002 }, - { 0x2b800b66, 0x34000009 }, - { 0x2b000b70, 0x68000000 }, - { 0x2b000b71, 0x1c000000 }, - { 0x35000b82, 0x30000000 }, - { 0x35000b83, 0x1c000000 }, - { 0x35800b85, 0x1c000005 }, - { 0x35800b8e, 0x1c000002 }, - { 0x35800b92, 0x1c000003 }, - { 0x35800b99, 0x1c000001 }, - { 0x35000b9c, 0x1c000000 }, - { 0x35800b9e, 0x1c000001 }, - { 0x35800ba3, 0x1c000001 }, - { 0x35800ba8, 0x1c000002 }, - { 0x35800bae, 0x1c00000b }, - { 0x35800bbe, 0x28000001 }, - { 0x35000bc0, 0x30000000 }, - { 0x35800bc1, 0x28000001 }, - { 0x35800bc6, 0x28000002 }, - { 0x35800bca, 0x28000002 }, - { 0x35000bcd, 0x30000000 }, - { 0x35000bd7, 0x28000000 }, - { 0x35800be6, 0x34000009 }, - { 0x35800bf0, 0x3c000002 }, - { 0x35800bf3, 0x68000005 }, - { 0x35000bf9, 0x5c000000 }, - { 0x35000bfa, 0x68000000 }, - { 0x36800c01, 0x28000002 }, - { 0x36800c05, 0x1c000007 }, - { 0x36800c0e, 0x1c000002 }, - { 0x36800c12, 0x1c000016 }, - { 0x36800c2a, 0x1c000009 }, - { 0x36800c35, 0x1c000004 }, - { 0x36800c3e, 0x30000002 }, - { 0x36800c41, 0x28000003 }, - { 0x36800c46, 0x30000002 }, - { 0x36800c4a, 0x30000003 }, - { 0x36800c55, 0x30000001 }, - { 0x36800c60, 0x1c000001 }, - { 0x36800c66, 0x34000009 }, - { 0x1c800c82, 0x28000001 }, - { 0x1c800c85, 0x1c000007 }, - { 0x1c800c8e, 0x1c000002 }, - { 0x1c800c92, 0x1c000016 }, - { 0x1c800caa, 0x1c000009 }, - { 0x1c800cb5, 0x1c000004 }, - { 0x1c000cbc, 0x30000000 }, - { 0x1c000cbd, 0x1c000000 }, - { 0x1c000cbe, 0x28000000 }, - { 0x1c000cbf, 0x30000000 }, - { 0x1c800cc0, 0x28000004 }, - { 0x1c000cc6, 0x30000000 }, - { 0x1c800cc7, 0x28000001 }, - { 0x1c800cca, 0x28000001 }, - { 0x1c800ccc, 0x30000001 }, - { 0x1c800cd5, 0x28000001 }, - { 0x1c000cde, 0x1c000000 }, - { 0x1c800ce0, 0x1c000001 }, - { 0x1c800ce2, 0x30000001 }, - { 0x1c800ce6, 0x34000009 }, - { 0x1c800cf1, 0x68000001 }, - { 0x24800d02, 0x28000001 }, - { 0x24800d05, 0x1c000007 }, - { 0x24800d0e, 0x1c000002 }, - { 0x24800d12, 0x1c000016 }, - { 0x24800d2a, 0x1c00000f }, - { 0x24800d3e, 0x28000002 }, - { 0x24800d41, 0x30000002 }, - { 0x24800d46, 0x28000002 }, - { 0x24800d4a, 0x28000002 }, - { 0x24000d4d, 0x30000000 }, - { 0x24000d57, 0x28000000 }, - { 0x24800d60, 0x1c000001 }, - { 0x24800d66, 0x34000009 }, - { 0x2f800d82, 0x28000001 }, - { 0x2f800d85, 0x1c000011 }, - { 0x2f800d9a, 0x1c000017 }, - { 0x2f800db3, 0x1c000008 }, - { 0x2f000dbd, 0x1c000000 }, - { 0x2f800dc0, 0x1c000006 }, - { 0x2f000dca, 0x30000000 }, - { 0x2f800dcf, 0x28000002 }, - { 0x2f800dd2, 0x30000002 }, - { 0x2f000dd6, 0x30000000 }, - { 0x2f800dd8, 0x28000007 }, - { 0x2f800df2, 0x28000001 }, - { 0x2f000df4, 0x54000000 }, - { 0x38800e01, 0x1c00002f }, - { 0x38000e31, 0x30000000 }, - { 0x38800e32, 0x1c000001 }, - { 0x38800e34, 0x30000006 }, - { 0x09000e3f, 0x5c000000 }, - { 0x38800e40, 0x1c000005 }, - { 0x38000e46, 0x18000000 }, - { 0x38800e47, 0x30000007 }, - { 0x38000e4f, 0x54000000 }, - { 0x38800e50, 0x34000009 }, - { 0x38800e5a, 0x54000001 }, - { 0x20800e81, 0x1c000001 }, - { 0x20000e84, 0x1c000000 }, - { 0x20800e87, 0x1c000001 }, - { 0x20000e8a, 0x1c000000 }, - { 0x20000e8d, 0x1c000000 }, - { 0x20800e94, 0x1c000003 }, - { 0x20800e99, 0x1c000006 }, - { 0x20800ea1, 0x1c000002 }, - { 0x20000ea5, 0x1c000000 }, - { 0x20000ea7, 0x1c000000 }, - { 0x20800eaa, 0x1c000001 }, - { 0x20800ead, 0x1c000003 }, - { 0x20000eb1, 0x30000000 }, - { 0x20800eb2, 0x1c000001 }, - { 0x20800eb4, 0x30000005 }, - { 0x20800ebb, 0x30000001 }, - { 0x20000ebd, 0x1c000000 }, - { 0x20800ec0, 0x1c000004 }, - { 0x20000ec6, 0x18000000 }, - { 0x20800ec8, 0x30000005 }, - { 0x20800ed0, 0x34000009 }, - { 0x20800edc, 0x1c000001 }, - { 0x39000f00, 0x1c000000 }, - { 0x39800f01, 0x68000002 }, - { 0x39800f04, 0x5400000e }, - { 0x39800f13, 0x68000004 }, - { 0x39800f18, 0x30000001 }, - { 0x39800f1a, 0x68000005 }, - { 0x39800f20, 0x34000009 }, - { 0x39800f2a, 0x3c000009 }, - { 0x39000f34, 0x68000000 }, - { 0x39000f35, 0x30000000 }, - { 0x39000f36, 0x68000000 }, - { 0x39000f37, 0x30000000 }, - { 0x39000f38, 0x68000000 }, - { 0x39000f39, 0x30000000 }, - { 0x39000f3a, 0x58000000 }, - { 0x39000f3b, 0x48000000 }, - { 0x39000f3c, 0x58000000 }, - { 0x39000f3d, 0x48000000 }, - { 0x39800f3e, 0x28000001 }, - { 0x39800f40, 0x1c000007 }, - { 0x39800f49, 0x1c000021 }, - { 0x39800f71, 0x3000000d }, - { 0x39000f7f, 0x28000000 }, - { 0x39800f80, 0x30000004 }, - { 0x39000f85, 0x54000000 }, - { 0x39800f86, 0x30000001 }, - { 0x39800f88, 0x1c000003 }, - { 0x39800f90, 0x30000007 }, - { 0x39800f99, 0x30000023 }, - { 0x39800fbe, 0x68000007 }, - { 0x39000fc6, 0x30000000 }, - { 0x39800fc7, 0x68000005 }, - { 0x39000fcf, 0x68000000 }, - { 0x39800fd0, 0x54000001 }, - { 0x26801000, 0x1c000021 }, - { 0x26801023, 0x1c000004 }, - { 0x26801029, 0x1c000001 }, - { 0x2600102c, 0x28000000 }, - { 0x2680102d, 0x30000003 }, - { 0x26001031, 0x28000000 }, - { 0x26001032, 0x30000000 }, - { 0x26801036, 0x30000001 }, - { 0x26001038, 0x28000000 }, - { 0x26001039, 0x30000000 }, - { 0x26801040, 0x34000009 }, - { 0x2680104a, 0x54000005 }, - { 0x26801050, 0x1c000005 }, - { 0x26801056, 0x28000001 }, - { 0x26801058, 0x30000001 }, - { 0x100010a0, 0x24001c60 }, - { 0x100010a1, 0x24001c60 }, - { 0x100010a2, 0x24001c60 }, - { 0x100010a3, 0x24001c60 }, - { 0x100010a4, 0x24001c60 }, - { 0x100010a5, 0x24001c60 }, - { 0x100010a6, 0x24001c60 }, - { 0x100010a7, 0x24001c60 }, - { 0x100010a8, 0x24001c60 }, - { 0x100010a9, 0x24001c60 }, - { 0x100010aa, 0x24001c60 }, - { 0x100010ab, 0x24001c60 }, - { 0x100010ac, 0x24001c60 }, - { 0x100010ad, 0x24001c60 }, - { 0x100010ae, 0x24001c60 }, - { 0x100010af, 0x24001c60 }, - { 0x100010b0, 0x24001c60 }, - { 0x100010b1, 0x24001c60 }, - { 0x100010b2, 0x24001c60 }, - { 0x100010b3, 0x24001c60 }, - { 0x100010b4, 0x24001c60 }, - { 0x100010b5, 0x24001c60 }, - { 0x100010b6, 0x24001c60 }, - { 0x100010b7, 0x24001c60 }, - { 0x100010b8, 0x24001c60 }, - { 0x100010b9, 0x24001c60 }, - { 0x100010ba, 0x24001c60 }, - { 0x100010bb, 0x24001c60 }, - { 0x100010bc, 0x24001c60 }, - { 0x100010bd, 0x24001c60 }, - { 0x100010be, 0x24001c60 }, - { 0x100010bf, 0x24001c60 }, - { 0x100010c0, 0x24001c60 }, - { 0x100010c1, 0x24001c60 }, - { 0x100010c2, 0x24001c60 }, - { 0x100010c3, 0x24001c60 }, - { 0x100010c4, 0x24001c60 }, - { 0x100010c5, 0x24001c60 }, - { 0x108010d0, 0x1c00002a }, - { 0x090010fb, 0x54000000 }, - { 0x100010fc, 0x18000000 }, - { 0x17801100, 0x1c000059 }, - { 0x1780115f, 0x1c000043 }, - { 0x178011a8, 0x1c000051 }, - { 0x0f801200, 0x1c000048 }, - { 0x0f80124a, 0x1c000003 }, - { 0x0f801250, 0x1c000006 }, - { 0x0f001258, 0x1c000000 }, - { 0x0f80125a, 0x1c000003 }, - { 0x0f801260, 0x1c000028 }, - { 0x0f80128a, 0x1c000003 }, - { 0x0f801290, 0x1c000020 }, - { 0x0f8012b2, 0x1c000003 }, - { 0x0f8012b8, 0x1c000006 }, - { 0x0f0012c0, 0x1c000000 }, - { 0x0f8012c2, 0x1c000003 }, - { 0x0f8012c8, 0x1c00000e }, - { 0x0f8012d8, 0x1c000038 }, - { 0x0f801312, 0x1c000003 }, - { 0x0f801318, 0x1c000042 }, - { 0x0f00135f, 0x30000000 }, - { 0x0f001360, 0x68000000 }, - { 0x0f801361, 0x54000007 }, - { 0x0f801369, 0x3c000013 }, - { 0x0f801380, 0x1c00000f }, - { 0x0f801390, 0x68000009 }, - { 0x088013a0, 0x1c000054 }, - { 0x07801401, 0x1c00026b }, - { 0x0780166d, 0x54000001 }, - { 0x0780166f, 0x1c000007 }, - { 0x28001680, 0x74000000 }, - { 0x28801681, 0x1c000019 }, - { 0x2800169b, 0x58000000 }, - { 0x2800169c, 0x48000000 }, - { 0x2d8016a0, 0x1c00004a }, - { 0x098016eb, 0x54000002 }, - { 0x2d8016ee, 0x38000002 }, - { 0x32801700, 0x1c00000c }, - { 0x3280170e, 0x1c000003 }, - { 0x32801712, 0x30000002 }, - { 0x18801720, 0x1c000011 }, - { 0x18801732, 0x30000002 }, - { 0x09801735, 0x54000001 }, - { 0x06801740, 0x1c000011 }, - { 0x06801752, 0x30000001 }, - { 0x33801760, 0x1c00000c }, - { 0x3380176e, 0x1c000002 }, - { 0x33801772, 0x30000001 }, - { 0x1f801780, 0x1c000033 }, - { 0x1f8017b4, 0x04000001 }, - { 0x1f0017b6, 0x28000000 }, - { 0x1f8017b7, 0x30000006 }, - { 0x1f8017be, 0x28000007 }, - { 0x1f0017c6, 0x30000000 }, - { 0x1f8017c7, 0x28000001 }, - { 0x1f8017c9, 0x3000000a }, - { 0x1f8017d4, 0x54000002 }, - { 0x1f0017d7, 0x18000000 }, - { 0x1f8017d8, 0x54000002 }, - { 0x1f0017db, 0x5c000000 }, - { 0x1f0017dc, 0x1c000000 }, - { 0x1f0017dd, 0x30000000 }, - { 0x1f8017e0, 0x34000009 }, - { 0x1f8017f0, 0x3c000009 }, - { 0x25801800, 0x54000001 }, - { 0x09801802, 0x54000001 }, - { 0x25001804, 0x54000000 }, - { 0x09001805, 0x54000000 }, - { 0x25001806, 0x44000000 }, - { 0x25801807, 0x54000003 }, - { 0x2580180b, 0x30000002 }, - { 0x2500180e, 0x74000000 }, - { 0x25801810, 0x34000009 }, - { 0x25801820, 0x1c000022 }, - { 0x25001843, 0x18000000 }, - { 0x25801844, 0x1c000033 }, - { 0x25801880, 0x1c000028 }, - { 0x250018a9, 0x30000000 }, - { 0x22801900, 0x1c00001c }, - { 0x22801920, 0x30000002 }, - { 0x22801923, 0x28000003 }, - { 0x22801927, 0x30000001 }, - { 0x22801929, 0x28000002 }, - { 0x22801930, 0x28000001 }, - { 0x22001932, 0x30000000 }, - { 0x22801933, 0x28000005 }, - { 0x22801939, 0x30000002 }, - { 0x22001940, 0x68000000 }, - { 0x22801944, 0x54000001 }, - { 0x22801946, 0x34000009 }, - { 0x34801950, 0x1c00001d }, - { 0x34801970, 0x1c000004 }, - { 0x27801980, 0x1c000029 }, - { 0x278019b0, 0x28000010 }, - { 0x278019c1, 0x1c000006 }, - { 0x278019c8, 0x28000001 }, - { 0x278019d0, 0x34000009 }, - { 0x278019de, 0x54000001 }, - { 0x1f8019e0, 0x6800001f }, - { 0x05801a00, 0x1c000016 }, - { 0x05801a17, 0x30000001 }, - { 0x05801a19, 0x28000002 }, - { 0x05801a1e, 0x54000001 }, - { 0x3d801b00, 0x30000003 }, - { 0x3d001b04, 0x28000000 }, - { 0x3d801b05, 0x1c00002e }, - { 0x3d001b34, 0x30000000 }, - { 0x3d001b35, 0x28000000 }, - { 0x3d801b36, 0x30000004 }, - { 0x3d001b3b, 0x28000000 }, - { 0x3d001b3c, 0x30000000 }, - { 0x3d801b3d, 0x28000004 }, - { 0x3d001b42, 0x30000000 }, - { 0x3d801b43, 0x28000001 }, - { 0x3d801b45, 0x1c000006 }, - { 0x3d801b50, 0x34000009 }, - { 0x3d801b5a, 0x54000006 }, - { 0x3d801b61, 0x68000009 }, - { 0x3d801b6b, 0x30000008 }, - { 0x3d801b74, 0x68000008 }, - { 0x21801d00, 0x14000025 }, - { 0x13801d26, 0x14000004 }, - { 0x0c001d2b, 0x14000000 }, - { 0x21801d2c, 0x18000030 }, - { 0x13801d5d, 0x18000004 }, - { 0x21801d62, 0x14000003 }, - { 0x13801d66, 0x14000004 }, - { 0x21801d6b, 0x1400000c }, - { 0x0c001d78, 0x18000000 }, - { 0x21801d79, 0x14000003 }, - { 0x21001d7d, 0x14000ee6 }, - { 0x21801d7e, 0x1400001c }, - { 0x21801d9b, 0x18000023 }, - { 0x13001dbf, 0x18000000 }, - { 0x1b801dc0, 0x3000000a }, - { 0x1b801dfe, 0x30000001 }, - { 0x21001e00, 0x24000001 }, - { 0x21001e01, 0x1400ffff }, - { 0x21001e02, 0x24000001 }, - { 0x21001e03, 0x1400ffff }, - { 0x21001e04, 0x24000001 }, - { 0x21001e05, 0x1400ffff }, - { 0x21001e06, 0x24000001 }, - { 0x21001e07, 0x1400ffff }, - { 0x21001e08, 0x24000001 }, - { 0x21001e09, 0x1400ffff }, - { 0x21001e0a, 0x24000001 }, - { 0x21001e0b, 0x1400ffff }, - { 0x21001e0c, 0x24000001 }, - { 0x21001e0d, 0x1400ffff }, - { 0x21001e0e, 0x24000001 }, - { 0x21001e0f, 0x1400ffff }, - { 0x21001e10, 0x24000001 }, - { 0x21001e11, 0x1400ffff }, - { 0x21001e12, 0x24000001 }, - { 0x21001e13, 0x1400ffff }, - { 0x21001e14, 0x24000001 }, - { 0x21001e15, 0x1400ffff }, - { 0x21001e16, 0x24000001 }, - { 0x21001e17, 0x1400ffff }, - { 0x21001e18, 0x24000001 }, - { 0x21001e19, 0x1400ffff }, - { 0x21001e1a, 0x24000001 }, - { 0x21001e1b, 0x1400ffff }, - { 0x21001e1c, 0x24000001 }, - { 0x21001e1d, 0x1400ffff }, - { 0x21001e1e, 0x24000001 }, - { 0x21001e1f, 0x1400ffff }, - { 0x21001e20, 0x24000001 }, - { 0x21001e21, 0x1400ffff }, - { 0x21001e22, 0x24000001 }, - { 0x21001e23, 0x1400ffff }, - { 0x21001e24, 0x24000001 }, - { 0x21001e25, 0x1400ffff }, - { 0x21001e26, 0x24000001 }, - { 0x21001e27, 0x1400ffff }, - { 0x21001e28, 0x24000001 }, - { 0x21001e29, 0x1400ffff }, - { 0x21001e2a, 0x24000001 }, - { 0x21001e2b, 0x1400ffff }, - { 0x21001e2c, 0x24000001 }, - { 0x21001e2d, 0x1400ffff }, - { 0x21001e2e, 0x24000001 }, - { 0x21001e2f, 0x1400ffff }, - { 0x21001e30, 0x24000001 }, - { 0x21001e31, 0x1400ffff }, - { 0x21001e32, 0x24000001 }, - { 0x21001e33, 0x1400ffff }, - { 0x21001e34, 0x24000001 }, - { 0x21001e35, 0x1400ffff }, - { 0x21001e36, 0x24000001 }, - { 0x21001e37, 0x1400ffff }, - { 0x21001e38, 0x24000001 }, - { 0x21001e39, 0x1400ffff }, - { 0x21001e3a, 0x24000001 }, - { 0x21001e3b, 0x1400ffff }, - { 0x21001e3c, 0x24000001 }, - { 0x21001e3d, 0x1400ffff }, - { 0x21001e3e, 0x24000001 }, - { 0x21001e3f, 0x1400ffff }, - { 0x21001e40, 0x24000001 }, - { 0x21001e41, 0x1400ffff }, - { 0x21001e42, 0x24000001 }, - { 0x21001e43, 0x1400ffff }, - { 0x21001e44, 0x24000001 }, - { 0x21001e45, 0x1400ffff }, - { 0x21001e46, 0x24000001 }, - { 0x21001e47, 0x1400ffff }, - { 0x21001e48, 0x24000001 }, - { 0x21001e49, 0x1400ffff }, - { 0x21001e4a, 0x24000001 }, - { 0x21001e4b, 0x1400ffff }, - { 0x21001e4c, 0x24000001 }, - { 0x21001e4d, 0x1400ffff }, - { 0x21001e4e, 0x24000001 }, - { 0x21001e4f, 0x1400ffff }, - { 0x21001e50, 0x24000001 }, - { 0x21001e51, 0x1400ffff }, - { 0x21001e52, 0x24000001 }, - { 0x21001e53, 0x1400ffff }, - { 0x21001e54, 0x24000001 }, - { 0x21001e55, 0x1400ffff }, - { 0x21001e56, 0x24000001 }, - { 0x21001e57, 0x1400ffff }, - { 0x21001e58, 0x24000001 }, - { 0x21001e59, 0x1400ffff }, - { 0x21001e5a, 0x24000001 }, - { 0x21001e5b, 0x1400ffff }, - { 0x21001e5c, 0x24000001 }, - { 0x21001e5d, 0x1400ffff }, - { 0x21001e5e, 0x24000001 }, - { 0x21001e5f, 0x1400ffff }, - { 0x21001e60, 0x24000001 }, - { 0x21001e61, 0x1400ffff }, - { 0x21001e62, 0x24000001 }, - { 0x21001e63, 0x1400ffff }, - { 0x21001e64, 0x24000001 }, - { 0x21001e65, 0x1400ffff }, - { 0x21001e66, 0x24000001 }, - { 0x21001e67, 0x1400ffff }, - { 0x21001e68, 0x24000001 }, - { 0x21001e69, 0x1400ffff }, - { 0x21001e6a, 0x24000001 }, - { 0x21001e6b, 0x1400ffff }, - { 0x21001e6c, 0x24000001 }, - { 0x21001e6d, 0x1400ffff }, - { 0x21001e6e, 0x24000001 }, - { 0x21001e6f, 0x1400ffff }, - { 0x21001e70, 0x24000001 }, - { 0x21001e71, 0x1400ffff }, - { 0x21001e72, 0x24000001 }, - { 0x21001e73, 0x1400ffff }, - { 0x21001e74, 0x24000001 }, - { 0x21001e75, 0x1400ffff }, - { 0x21001e76, 0x24000001 }, - { 0x21001e77, 0x1400ffff }, - { 0x21001e78, 0x24000001 }, - { 0x21001e79, 0x1400ffff }, - { 0x21001e7a, 0x24000001 }, - { 0x21001e7b, 0x1400ffff }, - { 0x21001e7c, 0x24000001 }, - { 0x21001e7d, 0x1400ffff }, - { 0x21001e7e, 0x24000001 }, - { 0x21001e7f, 0x1400ffff }, - { 0x21001e80, 0x24000001 }, - { 0x21001e81, 0x1400ffff }, - { 0x21001e82, 0x24000001 }, - { 0x21001e83, 0x1400ffff }, - { 0x21001e84, 0x24000001 }, - { 0x21001e85, 0x1400ffff }, - { 0x21001e86, 0x24000001 }, - { 0x21001e87, 0x1400ffff }, - { 0x21001e88, 0x24000001 }, - { 0x21001e89, 0x1400ffff }, - { 0x21001e8a, 0x24000001 }, - { 0x21001e8b, 0x1400ffff }, - { 0x21001e8c, 0x24000001 }, - { 0x21001e8d, 0x1400ffff }, - { 0x21001e8e, 0x24000001 }, - { 0x21001e8f, 0x1400ffff }, - { 0x21001e90, 0x24000001 }, - { 0x21001e91, 0x1400ffff }, - { 0x21001e92, 0x24000001 }, - { 0x21001e93, 0x1400ffff }, - { 0x21001e94, 0x24000001 }, - { 0x21001e95, 0x1400ffff }, - { 0x21801e96, 0x14000004 }, - { 0x21001e9b, 0x1400ffc5 }, - { 0x21001ea0, 0x24000001 }, - { 0x21001ea1, 0x1400ffff }, - { 0x21001ea2, 0x24000001 }, - { 0x21001ea3, 0x1400ffff }, - { 0x21001ea4, 0x24000001 }, - { 0x21001ea5, 0x1400ffff }, - { 0x21001ea6, 0x24000001 }, - { 0x21001ea7, 0x1400ffff }, - { 0x21001ea8, 0x24000001 }, - { 0x21001ea9, 0x1400ffff }, - { 0x21001eaa, 0x24000001 }, - { 0x21001eab, 0x1400ffff }, - { 0x21001eac, 0x24000001 }, - { 0x21001ead, 0x1400ffff }, - { 0x21001eae, 0x24000001 }, - { 0x21001eaf, 0x1400ffff }, - { 0x21001eb0, 0x24000001 }, - { 0x21001eb1, 0x1400ffff }, - { 0x21001eb2, 0x24000001 }, - { 0x21001eb3, 0x1400ffff }, - { 0x21001eb4, 0x24000001 }, - { 0x21001eb5, 0x1400ffff }, - { 0x21001eb6, 0x24000001 }, - { 0x21001eb7, 0x1400ffff }, - { 0x21001eb8, 0x24000001 }, - { 0x21001eb9, 0x1400ffff }, - { 0x21001eba, 0x24000001 }, - { 0x21001ebb, 0x1400ffff }, - { 0x21001ebc, 0x24000001 }, - { 0x21001ebd, 0x1400ffff }, - { 0x21001ebe, 0x24000001 }, - { 0x21001ebf, 0x1400ffff }, - { 0x21001ec0, 0x24000001 }, - { 0x21001ec1, 0x1400ffff }, - { 0x21001ec2, 0x24000001 }, - { 0x21001ec3, 0x1400ffff }, - { 0x21001ec4, 0x24000001 }, - { 0x21001ec5, 0x1400ffff }, - { 0x21001ec6, 0x24000001 }, - { 0x21001ec7, 0x1400ffff }, - { 0x21001ec8, 0x24000001 }, - { 0x21001ec9, 0x1400ffff }, - { 0x21001eca, 0x24000001 }, - { 0x21001ecb, 0x1400ffff }, - { 0x21001ecc, 0x24000001 }, - { 0x21001ecd, 0x1400ffff }, - { 0x21001ece, 0x24000001 }, - { 0x21001ecf, 0x1400ffff }, - { 0x21001ed0, 0x24000001 }, - { 0x21001ed1, 0x1400ffff }, - { 0x21001ed2, 0x24000001 }, - { 0x21001ed3, 0x1400ffff }, - { 0x21001ed4, 0x24000001 }, - { 0x21001ed5, 0x1400ffff }, - { 0x21001ed6, 0x24000001 }, - { 0x21001ed7, 0x1400ffff }, - { 0x21001ed8, 0x24000001 }, - { 0x21001ed9, 0x1400ffff }, - { 0x21001eda, 0x24000001 }, - { 0x21001edb, 0x1400ffff }, - { 0x21001edc, 0x24000001 }, - { 0x21001edd, 0x1400ffff }, - { 0x21001ede, 0x24000001 }, - { 0x21001edf, 0x1400ffff }, - { 0x21001ee0, 0x24000001 }, - { 0x21001ee1, 0x1400ffff }, - { 0x21001ee2, 0x24000001 }, - { 0x21001ee3, 0x1400ffff }, - { 0x21001ee4, 0x24000001 }, - { 0x21001ee5, 0x1400ffff }, - { 0x21001ee6, 0x24000001 }, - { 0x21001ee7, 0x1400ffff }, - { 0x21001ee8, 0x24000001 }, - { 0x21001ee9, 0x1400ffff }, - { 0x21001eea, 0x24000001 }, - { 0x21001eeb, 0x1400ffff }, - { 0x21001eec, 0x24000001 }, - { 0x21001eed, 0x1400ffff }, - { 0x21001eee, 0x24000001 }, - { 0x21001eef, 0x1400ffff }, - { 0x21001ef0, 0x24000001 }, - { 0x21001ef1, 0x1400ffff }, - { 0x21001ef2, 0x24000001 }, - { 0x21001ef3, 0x1400ffff }, - { 0x21001ef4, 0x24000001 }, - { 0x21001ef5, 0x1400ffff }, - { 0x21001ef6, 0x24000001 }, - { 0x21001ef7, 0x1400ffff }, - { 0x21001ef8, 0x24000001 }, - { 0x21001ef9, 0x1400ffff }, - { 0x13001f00, 0x14000008 }, - { 0x13001f01, 0x14000008 }, - { 0x13001f02, 0x14000008 }, - { 0x13001f03, 0x14000008 }, - { 0x13001f04, 0x14000008 }, - { 0x13001f05, 0x14000008 }, - { 0x13001f06, 0x14000008 }, - { 0x13001f07, 0x14000008 }, - { 0x13001f08, 0x2400fff8 }, - { 0x13001f09, 0x2400fff8 }, - { 0x13001f0a, 0x2400fff8 }, - { 0x13001f0b, 0x2400fff8 }, - { 0x13001f0c, 0x2400fff8 }, - { 0x13001f0d, 0x2400fff8 }, - { 0x13001f0e, 0x2400fff8 }, - { 0x13001f0f, 0x2400fff8 }, - { 0x13001f10, 0x14000008 }, - { 0x13001f11, 0x14000008 }, - { 0x13001f12, 0x14000008 }, - { 0x13001f13, 0x14000008 }, - { 0x13001f14, 0x14000008 }, - { 0x13001f15, 0x14000008 }, - { 0x13001f18, 0x2400fff8 }, - { 0x13001f19, 0x2400fff8 }, - { 0x13001f1a, 0x2400fff8 }, - { 0x13001f1b, 0x2400fff8 }, - { 0x13001f1c, 0x2400fff8 }, - { 0x13001f1d, 0x2400fff8 }, - { 0x13001f20, 0x14000008 }, - { 0x13001f21, 0x14000008 }, - { 0x13001f22, 0x14000008 }, - { 0x13001f23, 0x14000008 }, - { 0x13001f24, 0x14000008 }, - { 0x13001f25, 0x14000008 }, - { 0x13001f26, 0x14000008 }, - { 0x13001f27, 0x14000008 }, - { 0x13001f28, 0x2400fff8 }, - { 0x13001f29, 0x2400fff8 }, - { 0x13001f2a, 0x2400fff8 }, - { 0x13001f2b, 0x2400fff8 }, - { 0x13001f2c, 0x2400fff8 }, - { 0x13001f2d, 0x2400fff8 }, - { 0x13001f2e, 0x2400fff8 }, - { 0x13001f2f, 0x2400fff8 }, - { 0x13001f30, 0x14000008 }, - { 0x13001f31, 0x14000008 }, - { 0x13001f32, 0x14000008 }, - { 0x13001f33, 0x14000008 }, - { 0x13001f34, 0x14000008 }, - { 0x13001f35, 0x14000008 }, - { 0x13001f36, 0x14000008 }, - { 0x13001f37, 0x14000008 }, - { 0x13001f38, 0x2400fff8 }, - { 0x13001f39, 0x2400fff8 }, - { 0x13001f3a, 0x2400fff8 }, - { 0x13001f3b, 0x2400fff8 }, - { 0x13001f3c, 0x2400fff8 }, - { 0x13001f3d, 0x2400fff8 }, - { 0x13001f3e, 0x2400fff8 }, - { 0x13001f3f, 0x2400fff8 }, - { 0x13001f40, 0x14000008 }, - { 0x13001f41, 0x14000008 }, - { 0x13001f42, 0x14000008 }, - { 0x13001f43, 0x14000008 }, - { 0x13001f44, 0x14000008 }, - { 0x13001f45, 0x14000008 }, - { 0x13001f48, 0x2400fff8 }, - { 0x13001f49, 0x2400fff8 }, - { 0x13001f4a, 0x2400fff8 }, - { 0x13001f4b, 0x2400fff8 }, - { 0x13001f4c, 0x2400fff8 }, - { 0x13001f4d, 0x2400fff8 }, - { 0x13001f50, 0x14000000 }, - { 0x13001f51, 0x14000008 }, - { 0x13001f52, 0x14000000 }, - { 0x13001f53, 0x14000008 }, - { 0x13001f54, 0x14000000 }, - { 0x13001f55, 0x14000008 }, - { 0x13001f56, 0x14000000 }, - { 0x13001f57, 0x14000008 }, - { 0x13001f59, 0x2400fff8 }, - { 0x13001f5b, 0x2400fff8 }, - { 0x13001f5d, 0x2400fff8 }, - { 0x13001f5f, 0x2400fff8 }, - { 0x13001f60, 0x14000008 }, - { 0x13001f61, 0x14000008 }, - { 0x13001f62, 0x14000008 }, - { 0x13001f63, 0x14000008 }, - { 0x13001f64, 0x14000008 }, - { 0x13001f65, 0x14000008 }, - { 0x13001f66, 0x14000008 }, - { 0x13001f67, 0x14000008 }, - { 0x13001f68, 0x2400fff8 }, - { 0x13001f69, 0x2400fff8 }, - { 0x13001f6a, 0x2400fff8 }, - { 0x13001f6b, 0x2400fff8 }, - { 0x13001f6c, 0x2400fff8 }, - { 0x13001f6d, 0x2400fff8 }, - { 0x13001f6e, 0x2400fff8 }, - { 0x13001f6f, 0x2400fff8 }, - { 0x13001f70, 0x1400004a }, - { 0x13001f71, 0x1400004a }, - { 0x13001f72, 0x14000056 }, - { 0x13001f73, 0x14000056 }, - { 0x13001f74, 0x14000056 }, - { 0x13001f75, 0x14000056 }, - { 0x13001f76, 0x14000064 }, - { 0x13001f77, 0x14000064 }, - { 0x13001f78, 0x14000080 }, - { 0x13001f79, 0x14000080 }, - { 0x13001f7a, 0x14000070 }, - { 0x13001f7b, 0x14000070 }, - { 0x13001f7c, 0x1400007e }, - { 0x13001f7d, 0x1400007e }, - { 0x13001f80, 0x14000008 }, - { 0x13001f81, 0x14000008 }, - { 0x13001f82, 0x14000008 }, - { 0x13001f83, 0x14000008 }, - { 0x13001f84, 0x14000008 }, - { 0x13001f85, 0x14000008 }, - { 0x13001f86, 0x14000008 }, - { 0x13001f87, 0x14000008 }, - { 0x13001f88, 0x2000fff8 }, - { 0x13001f89, 0x2000fff8 }, - { 0x13001f8a, 0x2000fff8 }, - { 0x13001f8b, 0x2000fff8 }, - { 0x13001f8c, 0x2000fff8 }, - { 0x13001f8d, 0x2000fff8 }, - { 0x13001f8e, 0x2000fff8 }, - { 0x13001f8f, 0x2000fff8 }, - { 0x13001f90, 0x14000008 }, - { 0x13001f91, 0x14000008 }, - { 0x13001f92, 0x14000008 }, - { 0x13001f93, 0x14000008 }, - { 0x13001f94, 0x14000008 }, - { 0x13001f95, 0x14000008 }, - { 0x13001f96, 0x14000008 }, - { 0x13001f97, 0x14000008 }, - { 0x13001f98, 0x2000fff8 }, - { 0x13001f99, 0x2000fff8 }, - { 0x13001f9a, 0x2000fff8 }, - { 0x13001f9b, 0x2000fff8 }, - { 0x13001f9c, 0x2000fff8 }, - { 0x13001f9d, 0x2000fff8 }, - { 0x13001f9e, 0x2000fff8 }, - { 0x13001f9f, 0x2000fff8 }, - { 0x13001fa0, 0x14000008 }, - { 0x13001fa1, 0x14000008 }, - { 0x13001fa2, 0x14000008 }, - { 0x13001fa3, 0x14000008 }, - { 0x13001fa4, 0x14000008 }, - { 0x13001fa5, 0x14000008 }, - { 0x13001fa6, 0x14000008 }, - { 0x13001fa7, 0x14000008 }, - { 0x13001fa8, 0x2000fff8 }, - { 0x13001fa9, 0x2000fff8 }, - { 0x13001faa, 0x2000fff8 }, - { 0x13001fab, 0x2000fff8 }, - { 0x13001fac, 0x2000fff8 }, - { 0x13001fad, 0x2000fff8 }, - { 0x13001fae, 0x2000fff8 }, - { 0x13001faf, 0x2000fff8 }, - { 0x13001fb0, 0x14000008 }, - { 0x13001fb1, 0x14000008 }, - { 0x13001fb2, 0x14000000 }, - { 0x13001fb3, 0x14000009 }, - { 0x13001fb4, 0x14000000 }, - { 0x13801fb6, 0x14000001 }, - { 0x13001fb8, 0x2400fff8 }, - { 0x13001fb9, 0x2400fff8 }, - { 0x13001fba, 0x2400ffb6 }, - { 0x13001fbb, 0x2400ffb6 }, - { 0x13001fbc, 0x2000fff7 }, - { 0x13001fbd, 0x60000000 }, - { 0x13001fbe, 0x1400e3db }, - { 0x13801fbf, 0x60000002 }, - { 0x13001fc2, 0x14000000 }, - { 0x13001fc3, 0x14000009 }, - { 0x13001fc4, 0x14000000 }, - { 0x13801fc6, 0x14000001 }, - { 0x13001fc8, 0x2400ffaa }, - { 0x13001fc9, 0x2400ffaa }, - { 0x13001fca, 0x2400ffaa }, - { 0x13001fcb, 0x2400ffaa }, - { 0x13001fcc, 0x2000fff7 }, - { 0x13801fcd, 0x60000002 }, - { 0x13001fd0, 0x14000008 }, - { 0x13001fd1, 0x14000008 }, - { 0x13801fd2, 0x14000001 }, - { 0x13801fd6, 0x14000001 }, - { 0x13001fd8, 0x2400fff8 }, - { 0x13001fd9, 0x2400fff8 }, - { 0x13001fda, 0x2400ff9c }, - { 0x13001fdb, 0x2400ff9c }, - { 0x13801fdd, 0x60000002 }, - { 0x13001fe0, 0x14000008 }, - { 0x13001fe1, 0x14000008 }, - { 0x13801fe2, 0x14000002 }, - { 0x13001fe5, 0x14000007 }, - { 0x13801fe6, 0x14000001 }, - { 0x13001fe8, 0x2400fff8 }, - { 0x13001fe9, 0x2400fff8 }, - { 0x13001fea, 0x2400ff90 }, - { 0x13001feb, 0x2400ff90 }, - { 0x13001fec, 0x2400fff9 }, - { 0x13801fed, 0x60000002 }, - { 0x13001ff2, 0x14000000 }, - { 0x13001ff3, 0x14000009 }, - { 0x13001ff4, 0x14000000 }, - { 0x13801ff6, 0x14000001 }, - { 0x13001ff8, 0x2400ff80 }, - { 0x13001ff9, 0x2400ff80 }, - { 0x13001ffa, 0x2400ff82 }, - { 0x13001ffb, 0x2400ff82 }, - { 0x13001ffc, 0x2000fff7 }, - { 0x13801ffd, 0x60000001 }, - { 0x09802000, 0x7400000a }, - { 0x0900200b, 0x04000000 }, - { 0x1b80200c, 0x04000001 }, - { 0x0980200e, 0x04000001 }, - { 0x09802010, 0x44000005 }, - { 0x09802016, 0x54000001 }, - { 0x09002018, 0x50000000 }, - { 0x09002019, 0x4c000000 }, - { 0x0900201a, 0x58000000 }, - { 0x0980201b, 0x50000001 }, - { 0x0900201d, 0x4c000000 }, - { 0x0900201e, 0x58000000 }, - { 0x0900201f, 0x50000000 }, - { 0x09802020, 0x54000007 }, - { 0x09002028, 0x6c000000 }, - { 0x09002029, 0x70000000 }, - { 0x0980202a, 0x04000004 }, - { 0x0900202f, 0x74000000 }, - { 0x09802030, 0x54000008 }, - { 0x09002039, 0x50000000 }, - { 0x0900203a, 0x4c000000 }, - { 0x0980203b, 0x54000003 }, - { 0x0980203f, 0x40000001 }, - { 0x09802041, 0x54000002 }, - { 0x09002044, 0x64000000 }, - { 0x09002045, 0x58000000 }, - { 0x09002046, 0x48000000 }, - { 0x09802047, 0x5400000a }, - { 0x09002052, 0x64000000 }, - { 0x09002053, 0x54000000 }, - { 0x09002054, 0x40000000 }, - { 0x09802055, 0x54000009 }, - { 0x0900205f, 0x74000000 }, - { 0x09802060, 0x04000003 }, - { 0x0980206a, 0x04000005 }, - { 0x09002070, 0x3c000000 }, - { 0x21002071, 0x14000000 }, - { 0x09802074, 0x3c000005 }, - { 0x0980207a, 0x64000002 }, - { 0x0900207d, 0x58000000 }, - { 0x0900207e, 0x48000000 }, - { 0x2100207f, 0x14000000 }, - { 0x09802080, 0x3c000009 }, - { 0x0980208a, 0x64000002 }, - { 0x0900208d, 0x58000000 }, - { 0x0900208e, 0x48000000 }, - { 0x21802090, 0x18000004 }, - { 0x098020a0, 0x5c000015 }, - { 0x1b8020d0, 0x3000000c }, - { 0x1b8020dd, 0x2c000003 }, - { 0x1b0020e1, 0x30000000 }, - { 0x1b8020e2, 0x2c000002 }, - { 0x1b8020e5, 0x3000000a }, - { 0x09802100, 0x68000001 }, - { 0x09002102, 0x24000000 }, - { 0x09802103, 0x68000003 }, - { 0x09002107, 0x24000000 }, - { 0x09802108, 0x68000001 }, - { 0x0900210a, 0x14000000 }, - { 0x0980210b, 0x24000002 }, - { 0x0980210e, 0x14000001 }, - { 0x09802110, 0x24000002 }, - { 0x09002113, 0x14000000 }, - { 0x09002114, 0x68000000 }, - { 0x09002115, 0x24000000 }, - { 0x09802116, 0x68000002 }, - { 0x09802119, 0x24000004 }, - { 0x0980211e, 0x68000005 }, - { 0x09002124, 0x24000000 }, - { 0x09002125, 0x68000000 }, - { 0x13002126, 0x2400e2a3 }, - { 0x09002127, 0x68000000 }, - { 0x09002128, 0x24000000 }, - { 0x09002129, 0x68000000 }, - { 0x2100212a, 0x2400df41 }, - { 0x2100212b, 0x2400dfba }, - { 0x0980212c, 0x24000001 }, - { 0x0900212e, 0x68000000 }, - { 0x0900212f, 0x14000000 }, - { 0x09802130, 0x24000001 }, - { 0x21002132, 0x2400001c }, - { 0x09002133, 0x24000000 }, - { 0x09002134, 0x14000000 }, - { 0x09802135, 0x1c000003 }, - { 0x09002139, 0x14000000 }, - { 0x0980213a, 0x68000001 }, - { 0x0980213c, 0x14000001 }, - { 0x0980213e, 0x24000001 }, - { 0x09802140, 0x64000004 }, - { 0x09002145, 0x24000000 }, - { 0x09802146, 0x14000003 }, - { 0x0900214a, 0x68000000 }, - { 0x0900214b, 0x64000000 }, - { 0x0980214c, 0x68000001 }, - { 0x2100214e, 0x1400ffe4 }, - { 0x09802153, 0x3c00000c }, - { 0x09002160, 0x38000010 }, - { 0x09002161, 0x38000010 }, - { 0x09002162, 0x38000010 }, - { 0x09002163, 0x38000010 }, - { 0x09002164, 0x38000010 }, - { 0x09002165, 0x38000010 }, - { 0x09002166, 0x38000010 }, - { 0x09002167, 0x38000010 }, - { 0x09002168, 0x38000010 }, - { 0x09002169, 0x38000010 }, - { 0x0900216a, 0x38000010 }, - { 0x0900216b, 0x38000010 }, - { 0x0900216c, 0x38000010 }, - { 0x0900216d, 0x38000010 }, - { 0x0900216e, 0x38000010 }, - { 0x0900216f, 0x38000010 }, - { 0x09002170, 0x3800fff0 }, - { 0x09002171, 0x3800fff0 }, - { 0x09002172, 0x3800fff0 }, - { 0x09002173, 0x3800fff0 }, - { 0x09002174, 0x3800fff0 }, - { 0x09002175, 0x3800fff0 }, - { 0x09002176, 0x3800fff0 }, - { 0x09002177, 0x3800fff0 }, - { 0x09002178, 0x3800fff0 }, - { 0x09002179, 0x3800fff0 }, - { 0x0900217a, 0x3800fff0 }, - { 0x0900217b, 0x3800fff0 }, - { 0x0900217c, 0x3800fff0 }, - { 0x0900217d, 0x3800fff0 }, - { 0x0900217e, 0x3800fff0 }, - { 0x0900217f, 0x3800fff0 }, - { 0x09802180, 0x38000002 }, - { 0x09002183, 0x24000001 }, - { 0x21002184, 0x1400ffff }, - { 0x09802190, 0x64000004 }, - { 0x09802195, 0x68000004 }, - { 0x0980219a, 0x64000001 }, - { 0x0980219c, 0x68000003 }, - { 0x090021a0, 0x64000000 }, - { 0x098021a1, 0x68000001 }, - { 0x090021a3, 0x64000000 }, - { 0x098021a4, 0x68000001 }, - { 0x090021a6, 0x64000000 }, - { 0x098021a7, 0x68000006 }, - { 0x090021ae, 0x64000000 }, - { 0x098021af, 0x6800001e }, - { 0x098021ce, 0x64000001 }, - { 0x098021d0, 0x68000001 }, - { 0x090021d2, 0x64000000 }, - { 0x090021d3, 0x68000000 }, - { 0x090021d4, 0x64000000 }, - { 0x098021d5, 0x6800001e }, - { 0x098021f4, 0x6400010b }, - { 0x09802300, 0x68000007 }, - { 0x09802308, 0x64000003 }, - { 0x0980230c, 0x68000013 }, - { 0x09802320, 0x64000001 }, - { 0x09802322, 0x68000006 }, - { 0x09002329, 0x58000000 }, - { 0x0900232a, 0x48000000 }, - { 0x0980232b, 0x68000050 }, - { 0x0900237c, 0x64000000 }, - { 0x0980237d, 0x6800001d }, - { 0x0980239b, 0x64000018 }, - { 0x098023b4, 0x68000027 }, - { 0x098023dc, 0x64000005 }, - { 0x098023e2, 0x68000005 }, - { 0x09802400, 0x68000026 }, - { 0x09802440, 0x6800000a }, - { 0x09802460, 0x3c00003b }, - { 0x0980249c, 0x68000019 }, - { 0x090024b6, 0x6800001a }, - { 0x090024b7, 0x6800001a }, - { 0x090024b8, 0x6800001a }, - { 0x090024b9, 0x6800001a }, - { 0x090024ba, 0x6800001a }, - { 0x090024bb, 0x6800001a }, - { 0x090024bc, 0x6800001a }, - { 0x090024bd, 0x6800001a }, - { 0x090024be, 0x6800001a }, - { 0x090024bf, 0x6800001a }, - { 0x090024c0, 0x6800001a }, - { 0x090024c1, 0x6800001a }, - { 0x090024c2, 0x6800001a }, - { 0x090024c3, 0x6800001a }, - { 0x090024c4, 0x6800001a }, - { 0x090024c5, 0x6800001a }, - { 0x090024c6, 0x6800001a }, - { 0x090024c7, 0x6800001a }, - { 0x090024c8, 0x6800001a }, - { 0x090024c9, 0x6800001a }, - { 0x090024ca, 0x6800001a }, - { 0x090024cb, 0x6800001a }, - { 0x090024cc, 0x6800001a }, - { 0x090024cd, 0x6800001a }, - { 0x090024ce, 0x6800001a }, - { 0x090024cf, 0x6800001a }, - { 0x090024d0, 0x6800ffe6 }, - { 0x090024d1, 0x6800ffe6 }, - { 0x090024d2, 0x6800ffe6 }, - { 0x090024d3, 0x6800ffe6 }, - { 0x090024d4, 0x6800ffe6 }, - { 0x090024d5, 0x6800ffe6 }, - { 0x090024d6, 0x6800ffe6 }, - { 0x090024d7, 0x6800ffe6 }, - { 0x090024d8, 0x6800ffe6 }, - { 0x090024d9, 0x6800ffe6 }, - { 0x090024da, 0x6800ffe6 }, - { 0x090024db, 0x6800ffe6 }, - { 0x090024dc, 0x6800ffe6 }, - { 0x090024dd, 0x6800ffe6 }, - { 0x090024de, 0x6800ffe6 }, - { 0x090024df, 0x6800ffe6 }, - { 0x090024e0, 0x6800ffe6 }, - { 0x090024e1, 0x6800ffe6 }, - { 0x090024e2, 0x6800ffe6 }, - { 0x090024e3, 0x6800ffe6 }, - { 0x090024e4, 0x6800ffe6 }, - { 0x090024e5, 0x6800ffe6 }, - { 0x090024e6, 0x6800ffe6 }, - { 0x090024e7, 0x6800ffe6 }, - { 0x090024e8, 0x6800ffe6 }, - { 0x090024e9, 0x6800ffe6 }, - { 0x098024ea, 0x3c000015 }, - { 0x09802500, 0x680000b6 }, - { 0x090025b7, 0x64000000 }, - { 0x098025b8, 0x68000008 }, - { 0x090025c1, 0x64000000 }, - { 0x098025c2, 0x68000035 }, - { 0x098025f8, 0x64000007 }, - { 0x09802600, 0x6800006e }, - { 0x0900266f, 0x64000000 }, - { 0x09802670, 0x6800002c }, - { 0x098026a0, 0x68000012 }, - { 0x09802701, 0x68000003 }, - { 0x09802706, 0x68000003 }, - { 0x0980270c, 0x6800001b }, - { 0x09802729, 0x68000022 }, - { 0x0900274d, 0x68000000 }, - { 0x0980274f, 0x68000003 }, - { 0x09002756, 0x68000000 }, - { 0x09802758, 0x68000006 }, - { 0x09802761, 0x68000006 }, - { 0x09002768, 0x58000000 }, - { 0x09002769, 0x48000000 }, - { 0x0900276a, 0x58000000 }, - { 0x0900276b, 0x48000000 }, - { 0x0900276c, 0x58000000 }, - { 0x0900276d, 0x48000000 }, - { 0x0900276e, 0x58000000 }, - { 0x0900276f, 0x48000000 }, - { 0x09002770, 0x58000000 }, - { 0x09002771, 0x48000000 }, - { 0x09002772, 0x58000000 }, - { 0x09002773, 0x48000000 }, - { 0x09002774, 0x58000000 }, - { 0x09002775, 0x48000000 }, - { 0x09802776, 0x3c00001d }, - { 0x09002794, 0x68000000 }, - { 0x09802798, 0x68000017 }, - { 0x098027b1, 0x6800000d }, - { 0x098027c0, 0x64000004 }, - { 0x090027c5, 0x58000000 }, - { 0x090027c6, 0x48000000 }, - { 0x098027c7, 0x64000003 }, - { 0x098027d0, 0x64000015 }, - { 0x090027e6, 0x58000000 }, - { 0x090027e7, 0x48000000 }, - { 0x090027e8, 0x58000000 }, - { 0x090027e9, 0x48000000 }, - { 0x090027ea, 0x58000000 }, - { 0x090027eb, 0x48000000 }, - { 0x098027f0, 0x6400000f }, - { 0x04802800, 0x680000ff }, - { 0x09802900, 0x64000082 }, - { 0x09002983, 0x58000000 }, - { 0x09002984, 0x48000000 }, - { 0x09002985, 0x58000000 }, - { 0x09002986, 0x48000000 }, - { 0x09002987, 0x58000000 }, - { 0x09002988, 0x48000000 }, - { 0x09002989, 0x58000000 }, - { 0x0900298a, 0x48000000 }, - { 0x0900298b, 0x58000000 }, - { 0x0900298c, 0x48000000 }, - { 0x0900298d, 0x58000000 }, - { 0x0900298e, 0x48000000 }, - { 0x0900298f, 0x58000000 }, - { 0x09002990, 0x48000000 }, - { 0x09002991, 0x58000000 }, - { 0x09002992, 0x48000000 }, - { 0x09002993, 0x58000000 }, - { 0x09002994, 0x48000000 }, - { 0x09002995, 0x58000000 }, - { 0x09002996, 0x48000000 }, - { 0x09002997, 0x58000000 }, - { 0x09002998, 0x48000000 }, - { 0x09802999, 0x6400003e }, - { 0x090029d8, 0x58000000 }, - { 0x090029d9, 0x48000000 }, - { 0x090029da, 0x58000000 }, - { 0x090029db, 0x48000000 }, - { 0x098029dc, 0x6400001f }, - { 0x090029fc, 0x58000000 }, - { 0x090029fd, 0x48000000 }, - { 0x098029fe, 0x64000101 }, - { 0x09802b00, 0x6800001a }, - { 0x09802b20, 0x68000003 }, - { 0x11002c00, 0x24000030 }, - { 0x11002c01, 0x24000030 }, - { 0x11002c02, 0x24000030 }, - { 0x11002c03, 0x24000030 }, - { 0x11002c04, 0x24000030 }, - { 0x11002c05, 0x24000030 }, - { 0x11002c06, 0x24000030 }, - { 0x11002c07, 0x24000030 }, - { 0x11002c08, 0x24000030 }, - { 0x11002c09, 0x24000030 }, - { 0x11002c0a, 0x24000030 }, - { 0x11002c0b, 0x24000030 }, - { 0x11002c0c, 0x24000030 }, - { 0x11002c0d, 0x24000030 }, - { 0x11002c0e, 0x24000030 }, - { 0x11002c0f, 0x24000030 }, - { 0x11002c10, 0x24000030 }, - { 0x11002c11, 0x24000030 }, - { 0x11002c12, 0x24000030 }, - { 0x11002c13, 0x24000030 }, - { 0x11002c14, 0x24000030 }, - { 0x11002c15, 0x24000030 }, - { 0x11002c16, 0x24000030 }, - { 0x11002c17, 0x24000030 }, - { 0x11002c18, 0x24000030 }, - { 0x11002c19, 0x24000030 }, - { 0x11002c1a, 0x24000030 }, - { 0x11002c1b, 0x24000030 }, - { 0x11002c1c, 0x24000030 }, - { 0x11002c1d, 0x24000030 }, - { 0x11002c1e, 0x24000030 }, - { 0x11002c1f, 0x24000030 }, - { 0x11002c20, 0x24000030 }, - { 0x11002c21, 0x24000030 }, - { 0x11002c22, 0x24000030 }, - { 0x11002c23, 0x24000030 }, - { 0x11002c24, 0x24000030 }, - { 0x11002c25, 0x24000030 }, - { 0x11002c26, 0x24000030 }, - { 0x11002c27, 0x24000030 }, - { 0x11002c28, 0x24000030 }, - { 0x11002c29, 0x24000030 }, - { 0x11002c2a, 0x24000030 }, - { 0x11002c2b, 0x24000030 }, - { 0x11002c2c, 0x24000030 }, - { 0x11002c2d, 0x24000030 }, - { 0x11002c2e, 0x24000030 }, - { 0x11002c30, 0x1400ffd0 }, - { 0x11002c31, 0x1400ffd0 }, - { 0x11002c32, 0x1400ffd0 }, - { 0x11002c33, 0x1400ffd0 }, - { 0x11002c34, 0x1400ffd0 }, - { 0x11002c35, 0x1400ffd0 }, - { 0x11002c36, 0x1400ffd0 }, - { 0x11002c37, 0x1400ffd0 }, - { 0x11002c38, 0x1400ffd0 }, - { 0x11002c39, 0x1400ffd0 }, - { 0x11002c3a, 0x1400ffd0 }, - { 0x11002c3b, 0x1400ffd0 }, - { 0x11002c3c, 0x1400ffd0 }, - { 0x11002c3d, 0x1400ffd0 }, - { 0x11002c3e, 0x1400ffd0 }, - { 0x11002c3f, 0x1400ffd0 }, - { 0x11002c40, 0x1400ffd0 }, - { 0x11002c41, 0x1400ffd0 }, - { 0x11002c42, 0x1400ffd0 }, - { 0x11002c43, 0x1400ffd0 }, - { 0x11002c44, 0x1400ffd0 }, - { 0x11002c45, 0x1400ffd0 }, - { 0x11002c46, 0x1400ffd0 }, - { 0x11002c47, 0x1400ffd0 }, - { 0x11002c48, 0x1400ffd0 }, - { 0x11002c49, 0x1400ffd0 }, - { 0x11002c4a, 0x1400ffd0 }, - { 0x11002c4b, 0x1400ffd0 }, - { 0x11002c4c, 0x1400ffd0 }, - { 0x11002c4d, 0x1400ffd0 }, - { 0x11002c4e, 0x1400ffd0 }, - { 0x11002c4f, 0x1400ffd0 }, - { 0x11002c50, 0x1400ffd0 }, - { 0x11002c51, 0x1400ffd0 }, - { 0x11002c52, 0x1400ffd0 }, - { 0x11002c53, 0x1400ffd0 }, - { 0x11002c54, 0x1400ffd0 }, - { 0x11002c55, 0x1400ffd0 }, - { 0x11002c56, 0x1400ffd0 }, - { 0x11002c57, 0x1400ffd0 }, - { 0x11002c58, 0x1400ffd0 }, - { 0x11002c59, 0x1400ffd0 }, - { 0x11002c5a, 0x1400ffd0 }, - { 0x11002c5b, 0x1400ffd0 }, - { 0x11002c5c, 0x1400ffd0 }, - { 0x11002c5d, 0x1400ffd0 }, - { 0x11002c5e, 0x1400ffd0 }, - { 0x21002c60, 0x24000001 }, - { 0x21002c61, 0x1400ffff }, - { 0x21002c62, 0x2400d609 }, - { 0x21002c63, 0x2400f11a }, - { 0x21002c64, 0x2400d619 }, - { 0x21002c65, 0x1400d5d5 }, - { 0x21002c66, 0x1400d5d8 }, - { 0x21002c67, 0x24000001 }, - { 0x21002c68, 0x1400ffff }, - { 0x21002c69, 0x24000001 }, - { 0x21002c6a, 0x1400ffff }, - { 0x21002c6b, 0x24000001 }, - { 0x21002c6c, 0x1400ffff }, - { 0x21002c74, 0x14000000 }, - { 0x21002c75, 0x24000001 }, - { 0x21002c76, 0x1400ffff }, - { 0x21002c77, 0x14000000 }, - { 0x0a002c80, 0x24000001 }, - { 0x0a002c81, 0x1400ffff }, - { 0x0a002c82, 0x24000001 }, - { 0x0a002c83, 0x1400ffff }, - { 0x0a002c84, 0x24000001 }, - { 0x0a002c85, 0x1400ffff }, - { 0x0a002c86, 0x24000001 }, - { 0x0a002c87, 0x1400ffff }, - { 0x0a002c88, 0x24000001 }, - { 0x0a002c89, 0x1400ffff }, - { 0x0a002c8a, 0x24000001 }, - { 0x0a002c8b, 0x1400ffff }, - { 0x0a002c8c, 0x24000001 }, - { 0x0a002c8d, 0x1400ffff }, - { 0x0a002c8e, 0x24000001 }, - { 0x0a002c8f, 0x1400ffff }, - { 0x0a002c90, 0x24000001 }, - { 0x0a002c91, 0x1400ffff }, - { 0x0a002c92, 0x24000001 }, - { 0x0a002c93, 0x1400ffff }, - { 0x0a002c94, 0x24000001 }, - { 0x0a002c95, 0x1400ffff }, - { 0x0a002c96, 0x24000001 }, - { 0x0a002c97, 0x1400ffff }, - { 0x0a002c98, 0x24000001 }, - { 0x0a002c99, 0x1400ffff }, - { 0x0a002c9a, 0x24000001 }, - { 0x0a002c9b, 0x1400ffff }, - { 0x0a002c9c, 0x24000001 }, - { 0x0a002c9d, 0x1400ffff }, - { 0x0a002c9e, 0x24000001 }, - { 0x0a002c9f, 0x1400ffff }, - { 0x0a002ca0, 0x24000001 }, - { 0x0a002ca1, 0x1400ffff }, - { 0x0a002ca2, 0x24000001 }, - { 0x0a002ca3, 0x1400ffff }, - { 0x0a002ca4, 0x24000001 }, - { 0x0a002ca5, 0x1400ffff }, - { 0x0a002ca6, 0x24000001 }, - { 0x0a002ca7, 0x1400ffff }, - { 0x0a002ca8, 0x24000001 }, - { 0x0a002ca9, 0x1400ffff }, - { 0x0a002caa, 0x24000001 }, - { 0x0a002cab, 0x1400ffff }, - { 0x0a002cac, 0x24000001 }, - { 0x0a002cad, 0x1400ffff }, - { 0x0a002cae, 0x24000001 }, - { 0x0a002caf, 0x1400ffff }, - { 0x0a002cb0, 0x24000001 }, - { 0x0a002cb1, 0x1400ffff }, - { 0x0a002cb2, 0x24000001 }, - { 0x0a002cb3, 0x1400ffff }, - { 0x0a002cb4, 0x24000001 }, - { 0x0a002cb5, 0x1400ffff }, - { 0x0a002cb6, 0x24000001 }, - { 0x0a002cb7, 0x1400ffff }, - { 0x0a002cb8, 0x24000001 }, - { 0x0a002cb9, 0x1400ffff }, - { 0x0a002cba, 0x24000001 }, - { 0x0a002cbb, 0x1400ffff }, - { 0x0a002cbc, 0x24000001 }, - { 0x0a002cbd, 0x1400ffff }, - { 0x0a002cbe, 0x24000001 }, - { 0x0a002cbf, 0x1400ffff }, - { 0x0a002cc0, 0x24000001 }, - { 0x0a002cc1, 0x1400ffff }, - { 0x0a002cc2, 0x24000001 }, - { 0x0a002cc3, 0x1400ffff }, - { 0x0a002cc4, 0x24000001 }, - { 0x0a002cc5, 0x1400ffff }, - { 0x0a002cc6, 0x24000001 }, - { 0x0a002cc7, 0x1400ffff }, - { 0x0a002cc8, 0x24000001 }, - { 0x0a002cc9, 0x1400ffff }, - { 0x0a002cca, 0x24000001 }, - { 0x0a002ccb, 0x1400ffff }, - { 0x0a002ccc, 0x24000001 }, - { 0x0a002ccd, 0x1400ffff }, - { 0x0a002cce, 0x24000001 }, - { 0x0a002ccf, 0x1400ffff }, - { 0x0a002cd0, 0x24000001 }, - { 0x0a002cd1, 0x1400ffff }, - { 0x0a002cd2, 0x24000001 }, - { 0x0a002cd3, 0x1400ffff }, - { 0x0a002cd4, 0x24000001 }, - { 0x0a002cd5, 0x1400ffff }, - { 0x0a002cd6, 0x24000001 }, - { 0x0a002cd7, 0x1400ffff }, - { 0x0a002cd8, 0x24000001 }, - { 0x0a002cd9, 0x1400ffff }, - { 0x0a002cda, 0x24000001 }, - { 0x0a002cdb, 0x1400ffff }, - { 0x0a002cdc, 0x24000001 }, - { 0x0a002cdd, 0x1400ffff }, - { 0x0a002cde, 0x24000001 }, - { 0x0a002cdf, 0x1400ffff }, - { 0x0a002ce0, 0x24000001 }, - { 0x0a002ce1, 0x1400ffff }, - { 0x0a002ce2, 0x24000001 }, - { 0x0a002ce3, 0x1400ffff }, - { 0x0a002ce4, 0x14000000 }, - { 0x0a802ce5, 0x68000005 }, - { 0x0a802cf9, 0x54000003 }, - { 0x0a002cfd, 0x3c000000 }, - { 0x0a802cfe, 0x54000001 }, - { 0x10002d00, 0x1400e3a0 }, - { 0x10002d01, 0x1400e3a0 }, - { 0x10002d02, 0x1400e3a0 }, - { 0x10002d03, 0x1400e3a0 }, - { 0x10002d04, 0x1400e3a0 }, - { 0x10002d05, 0x1400e3a0 }, - { 0x10002d06, 0x1400e3a0 }, - { 0x10002d07, 0x1400e3a0 }, - { 0x10002d08, 0x1400e3a0 }, - { 0x10002d09, 0x1400e3a0 }, - { 0x10002d0a, 0x1400e3a0 }, - { 0x10002d0b, 0x1400e3a0 }, - { 0x10002d0c, 0x1400e3a0 }, - { 0x10002d0d, 0x1400e3a0 }, - { 0x10002d0e, 0x1400e3a0 }, - { 0x10002d0f, 0x1400e3a0 }, - { 0x10002d10, 0x1400e3a0 }, - { 0x10002d11, 0x1400e3a0 }, - { 0x10002d12, 0x1400e3a0 }, - { 0x10002d13, 0x1400e3a0 }, - { 0x10002d14, 0x1400e3a0 }, - { 0x10002d15, 0x1400e3a0 }, - { 0x10002d16, 0x1400e3a0 }, - { 0x10002d17, 0x1400e3a0 }, - { 0x10002d18, 0x1400e3a0 }, - { 0x10002d19, 0x1400e3a0 }, - { 0x10002d1a, 0x1400e3a0 }, - { 0x10002d1b, 0x1400e3a0 }, - { 0x10002d1c, 0x1400e3a0 }, - { 0x10002d1d, 0x1400e3a0 }, - { 0x10002d1e, 0x1400e3a0 }, - { 0x10002d1f, 0x1400e3a0 }, - { 0x10002d20, 0x1400e3a0 }, - { 0x10002d21, 0x1400e3a0 }, - { 0x10002d22, 0x1400e3a0 }, - { 0x10002d23, 0x1400e3a0 }, - { 0x10002d24, 0x1400e3a0 }, - { 0x10002d25, 0x1400e3a0 }, - { 0x3a802d30, 0x1c000035 }, - { 0x3a002d6f, 0x18000000 }, - { 0x0f802d80, 0x1c000016 }, - { 0x0f802da0, 0x1c000006 }, - { 0x0f802da8, 0x1c000006 }, - { 0x0f802db0, 0x1c000006 }, - { 0x0f802db8, 0x1c000006 }, - { 0x0f802dc0, 0x1c000006 }, - { 0x0f802dc8, 0x1c000006 }, - { 0x0f802dd0, 0x1c000006 }, - { 0x0f802dd8, 0x1c000006 }, - { 0x09802e00, 0x54000001 }, - { 0x09002e02, 0x50000000 }, - { 0x09002e03, 0x4c000000 }, - { 0x09002e04, 0x50000000 }, - { 0x09002e05, 0x4c000000 }, - { 0x09802e06, 0x54000002 }, - { 0x09002e09, 0x50000000 }, - { 0x09002e0a, 0x4c000000 }, - { 0x09002e0b, 0x54000000 }, - { 0x09002e0c, 0x50000000 }, - { 0x09002e0d, 0x4c000000 }, - { 0x09802e0e, 0x54000008 }, - { 0x09002e17, 0x44000000 }, - { 0x09002e1c, 0x50000000 }, - { 0x09002e1d, 0x4c000000 }, - { 0x16802e80, 0x68000019 }, - { 0x16802e9b, 0x68000058 }, - { 0x16802f00, 0x680000d5 }, - { 0x09802ff0, 0x6800000b }, - { 0x09003000, 0x74000000 }, - { 0x09803001, 0x54000002 }, - { 0x09003004, 0x68000000 }, - { 0x16003005, 0x18000000 }, - { 0x09003006, 0x1c000000 }, - { 0x16003007, 0x38000000 }, - { 0x09003008, 0x58000000 }, - { 0x09003009, 0x48000000 }, - { 0x0900300a, 0x58000000 }, - { 0x0900300b, 0x48000000 }, - { 0x0900300c, 0x58000000 }, - { 0x0900300d, 0x48000000 }, - { 0x0900300e, 0x58000000 }, - { 0x0900300f, 0x48000000 }, - { 0x09003010, 0x58000000 }, - { 0x09003011, 0x48000000 }, - { 0x09803012, 0x68000001 }, - { 0x09003014, 0x58000000 }, - { 0x09003015, 0x48000000 }, - { 0x09003016, 0x58000000 }, - { 0x09003017, 0x48000000 }, - { 0x09003018, 0x58000000 }, - { 0x09003019, 0x48000000 }, - { 0x0900301a, 0x58000000 }, - { 0x0900301b, 0x48000000 }, - { 0x0900301c, 0x44000000 }, - { 0x0900301d, 0x58000000 }, - { 0x0980301e, 0x48000001 }, - { 0x09003020, 0x68000000 }, - { 0x16803021, 0x38000008 }, - { 0x1b80302a, 0x30000005 }, - { 0x09003030, 0x44000000 }, - { 0x09803031, 0x18000004 }, - { 0x09803036, 0x68000001 }, - { 0x16803038, 0x38000002 }, - { 0x1600303b, 0x18000000 }, - { 0x0900303c, 0x1c000000 }, - { 0x0900303d, 0x54000000 }, - { 0x0980303e, 0x68000001 }, - { 0x1a803041, 0x1c000055 }, - { 0x1b803099, 0x30000001 }, - { 0x0980309b, 0x60000001 }, - { 0x1a80309d, 0x18000001 }, - { 0x1a00309f, 0x1c000000 }, - { 0x090030a0, 0x44000000 }, - { 0x1d8030a1, 0x1c000059 }, - { 0x090030fb, 0x54000000 }, - { 0x090030fc, 0x18000000 }, - { 0x1d8030fd, 0x18000001 }, - { 0x1d0030ff, 0x1c000000 }, - { 0x03803105, 0x1c000027 }, - { 0x17803131, 0x1c00005d }, - { 0x09803190, 0x68000001 }, - { 0x09803192, 0x3c000003 }, - { 0x09803196, 0x68000009 }, - { 0x038031a0, 0x1c000017 }, - { 0x098031c0, 0x6800000f }, - { 0x1d8031f0, 0x1c00000f }, - { 0x17803200, 0x6800001e }, - { 0x09803220, 0x3c000009 }, - { 0x0980322a, 0x68000019 }, - { 0x09003250, 0x68000000 }, - { 0x09803251, 0x3c00000e }, - { 0x17803260, 0x6800001d }, - { 0x0980327e, 0x68000001 }, - { 0x09803280, 0x3c000009 }, - { 0x0980328a, 0x68000026 }, - { 0x098032b1, 0x3c00000e }, - { 0x098032c0, 0x6800003e }, - { 0x09803300, 0x680000ff }, - { 0x16803400, 0x1c0019b5 }, - { 0x09804dc0, 0x6800003f }, - { 0x16804e00, 0x1c0051bb }, - { 0x3c80a000, 0x1c000014 }, - { 0x3c00a015, 0x18000000 }, - { 0x3c80a016, 0x1c000476 }, - { 0x3c80a490, 0x68000036 }, - { 0x0980a700, 0x60000016 }, - { 0x0980a717, 0x18000003 }, - { 0x0980a720, 0x60000001 }, - { 0x3080a800, 0x1c000001 }, - { 0x3000a802, 0x28000000 }, - { 0x3080a803, 0x1c000002 }, - { 0x3000a806, 0x30000000 }, - { 0x3080a807, 0x1c000003 }, - { 0x3000a80b, 0x30000000 }, - { 0x3080a80c, 0x1c000016 }, - { 0x3080a823, 0x28000001 }, - { 0x3080a825, 0x30000001 }, - { 0x3000a827, 0x28000000 }, - { 0x3080a828, 0x68000003 }, - { 0x4080a840, 0x1c000033 }, - { 0x4080a874, 0x54000003 }, - { 0x1780ac00, 0x1c002ba3 }, - { 0x0980d800, 0x1000037f }, - { 0x0980db80, 0x1000007f }, - { 0x0980dc00, 0x100003ff }, - { 0x0980e000, 0x0c0018ff }, - { 0x1680f900, 0x1c00012d }, - { 0x1680fa30, 0x1c00003a }, - { 0x1680fa70, 0x1c000069 }, - { 0x2180fb00, 0x14000006 }, - { 0x0180fb13, 0x14000004 }, - { 0x1900fb1d, 0x1c000000 }, - { 0x1900fb1e, 0x30000000 }, - { 0x1980fb1f, 0x1c000009 }, - { 0x1900fb29, 0x64000000 }, - { 0x1980fb2a, 0x1c00000c }, - { 0x1980fb38, 0x1c000004 }, - { 0x1900fb3e, 0x1c000000 }, - { 0x1980fb40, 0x1c000001 }, - { 0x1980fb43, 0x1c000001 }, - { 0x1980fb46, 0x1c000009 }, - { 0x0080fb50, 0x1c000061 }, - { 0x0080fbd3, 0x1c00016a }, - { 0x0900fd3e, 0x58000000 }, - { 0x0900fd3f, 0x48000000 }, - { 0x0080fd50, 0x1c00003f }, - { 0x0080fd92, 0x1c000035 }, - { 0x0080fdf0, 0x1c00000b }, - { 0x0000fdfc, 0x5c000000 }, - { 0x0900fdfd, 0x68000000 }, - { 0x1b80fe00, 0x3000000f }, - { 0x0980fe10, 0x54000006 }, - { 0x0900fe17, 0x58000000 }, - { 0x0900fe18, 0x48000000 }, - { 0x0900fe19, 0x54000000 }, - { 0x1b80fe20, 0x30000003 }, - { 0x0900fe30, 0x54000000 }, - { 0x0980fe31, 0x44000001 }, - { 0x0980fe33, 0x40000001 }, - { 0x0900fe35, 0x58000000 }, - { 0x0900fe36, 0x48000000 }, - { 0x0900fe37, 0x58000000 }, - { 0x0900fe38, 0x48000000 }, - { 0x0900fe39, 0x58000000 }, - { 0x0900fe3a, 0x48000000 }, - { 0x0900fe3b, 0x58000000 }, - { 0x0900fe3c, 0x48000000 }, - { 0x0900fe3d, 0x58000000 }, - { 0x0900fe3e, 0x48000000 }, - { 0x0900fe3f, 0x58000000 }, - { 0x0900fe40, 0x48000000 }, - { 0x0900fe41, 0x58000000 }, - { 0x0900fe42, 0x48000000 }, - { 0x0900fe43, 0x58000000 }, - { 0x0900fe44, 0x48000000 }, - { 0x0980fe45, 0x54000001 }, - { 0x0900fe47, 0x58000000 }, - { 0x0900fe48, 0x48000000 }, - { 0x0980fe49, 0x54000003 }, - { 0x0980fe4d, 0x40000002 }, - { 0x0980fe50, 0x54000002 }, - { 0x0980fe54, 0x54000003 }, - { 0x0900fe58, 0x44000000 }, - { 0x0900fe59, 0x58000000 }, - { 0x0900fe5a, 0x48000000 }, - { 0x0900fe5b, 0x58000000 }, - { 0x0900fe5c, 0x48000000 }, - { 0x0900fe5d, 0x58000000 }, - { 0x0900fe5e, 0x48000000 }, - { 0x0980fe5f, 0x54000002 }, - { 0x0900fe62, 0x64000000 }, - { 0x0900fe63, 0x44000000 }, - { 0x0980fe64, 0x64000002 }, - { 0x0900fe68, 0x54000000 }, - { 0x0900fe69, 0x5c000000 }, - { 0x0980fe6a, 0x54000001 }, - { 0x0080fe70, 0x1c000004 }, - { 0x0080fe76, 0x1c000086 }, - { 0x0900feff, 0x04000000 }, - { 0x0980ff01, 0x54000002 }, - { 0x0900ff04, 0x5c000000 }, - { 0x0980ff05, 0x54000002 }, - { 0x0900ff08, 0x58000000 }, - { 0x0900ff09, 0x48000000 }, - { 0x0900ff0a, 0x54000000 }, - { 0x0900ff0b, 0x64000000 }, - { 0x0900ff0c, 0x54000000 }, - { 0x0900ff0d, 0x44000000 }, - { 0x0980ff0e, 0x54000001 }, - { 0x0980ff10, 0x34000009 }, - { 0x0980ff1a, 0x54000001 }, - { 0x0980ff1c, 0x64000002 }, - { 0x0980ff1f, 0x54000001 }, - { 0x2100ff21, 0x24000020 }, - { 0x2100ff22, 0x24000020 }, - { 0x2100ff23, 0x24000020 }, - { 0x2100ff24, 0x24000020 }, - { 0x2100ff25, 0x24000020 }, - { 0x2100ff26, 0x24000020 }, - { 0x2100ff27, 0x24000020 }, - { 0x2100ff28, 0x24000020 }, - { 0x2100ff29, 0x24000020 }, - { 0x2100ff2a, 0x24000020 }, - { 0x2100ff2b, 0x24000020 }, - { 0x2100ff2c, 0x24000020 }, - { 0x2100ff2d, 0x24000020 }, - { 0x2100ff2e, 0x24000020 }, - { 0x2100ff2f, 0x24000020 }, - { 0x2100ff30, 0x24000020 }, - { 0x2100ff31, 0x24000020 }, - { 0x2100ff32, 0x24000020 }, - { 0x2100ff33, 0x24000020 }, - { 0x2100ff34, 0x24000020 }, - { 0x2100ff35, 0x24000020 }, - { 0x2100ff36, 0x24000020 }, - { 0x2100ff37, 0x24000020 }, - { 0x2100ff38, 0x24000020 }, - { 0x2100ff39, 0x24000020 }, - { 0x2100ff3a, 0x24000020 }, - { 0x0900ff3b, 0x58000000 }, - { 0x0900ff3c, 0x54000000 }, - { 0x0900ff3d, 0x48000000 }, - { 0x0900ff3e, 0x60000000 }, - { 0x0900ff3f, 0x40000000 }, - { 0x0900ff40, 0x60000000 }, - { 0x2100ff41, 0x1400ffe0 }, - { 0x2100ff42, 0x1400ffe0 }, - { 0x2100ff43, 0x1400ffe0 }, - { 0x2100ff44, 0x1400ffe0 }, - { 0x2100ff45, 0x1400ffe0 }, - { 0x2100ff46, 0x1400ffe0 }, - { 0x2100ff47, 0x1400ffe0 }, - { 0x2100ff48, 0x1400ffe0 }, - { 0x2100ff49, 0x1400ffe0 }, - { 0x2100ff4a, 0x1400ffe0 }, - { 0x2100ff4b, 0x1400ffe0 }, - { 0x2100ff4c, 0x1400ffe0 }, - { 0x2100ff4d, 0x1400ffe0 }, - { 0x2100ff4e, 0x1400ffe0 }, - { 0x2100ff4f, 0x1400ffe0 }, - { 0x2100ff50, 0x1400ffe0 }, - { 0x2100ff51, 0x1400ffe0 }, - { 0x2100ff52, 0x1400ffe0 }, - { 0x2100ff53, 0x1400ffe0 }, - { 0x2100ff54, 0x1400ffe0 }, - { 0x2100ff55, 0x1400ffe0 }, - { 0x2100ff56, 0x1400ffe0 }, - { 0x2100ff57, 0x1400ffe0 }, - { 0x2100ff58, 0x1400ffe0 }, - { 0x2100ff59, 0x1400ffe0 }, - { 0x2100ff5a, 0x1400ffe0 }, - { 0x0900ff5b, 0x58000000 }, - { 0x0900ff5c, 0x64000000 }, - { 0x0900ff5d, 0x48000000 }, - { 0x0900ff5e, 0x64000000 }, - { 0x0900ff5f, 0x58000000 }, - { 0x0900ff60, 0x48000000 }, - { 0x0900ff61, 0x54000000 }, - { 0x0900ff62, 0x58000000 }, - { 0x0900ff63, 0x48000000 }, - { 0x0980ff64, 0x54000001 }, - { 0x1d80ff66, 0x1c000009 }, - { 0x0900ff70, 0x18000000 }, - { 0x1d80ff71, 0x1c00002c }, - { 0x0980ff9e, 0x18000001 }, - { 0x1780ffa0, 0x1c00001e }, - { 0x1780ffc2, 0x1c000005 }, - { 0x1780ffca, 0x1c000005 }, - { 0x1780ffd2, 0x1c000005 }, - { 0x1780ffda, 0x1c000002 }, - { 0x0980ffe0, 0x5c000001 }, - { 0x0900ffe2, 0x64000000 }, - { 0x0900ffe3, 0x60000000 }, - { 0x0900ffe4, 0x68000000 }, - { 0x0980ffe5, 0x5c000001 }, - { 0x0900ffe8, 0x68000000 }, - { 0x0980ffe9, 0x64000003 }, - { 0x0980ffed, 0x68000001 }, - { 0x0980fff9, 0x04000002 }, - { 0x0980fffc, 0x68000001 }, - { 0x23810000, 0x1c00000b }, - { 0x2381000d, 0x1c000019 }, - { 0x23810028, 0x1c000012 }, - { 0x2381003c, 0x1c000001 }, - { 0x2381003f, 0x1c00000e }, - { 0x23810050, 0x1c00000d }, - { 0x23810080, 0x1c00007a }, - { 0x09810100, 0x54000001 }, - { 0x09010102, 0x68000000 }, - { 0x09810107, 0x3c00002c }, - { 0x09810137, 0x68000008 }, - { 0x13810140, 0x38000034 }, - { 0x13810175, 0x3c000003 }, - { 0x13810179, 0x68000010 }, - { 0x1301018a, 0x3c000000 }, - { 0x29810300, 0x1c00001e }, - { 0x29810320, 0x3c000003 }, - { 0x12810330, 0x1c000010 }, - { 0x12010341, 0x38000000 }, - { 0x12810342, 0x1c000007 }, - { 0x1201034a, 0x38000000 }, - { 0x3b810380, 0x1c00001d }, - { 0x3b01039f, 0x54000000 }, - { 0x2a8103a0, 0x1c000023 }, - { 0x2a8103c8, 0x1c000007 }, - { 0x2a0103d0, 0x54000000 }, - { 0x2a8103d1, 0x38000004 }, - { 0x0d010400, 0x24000028 }, - { 0x0d010401, 0x24000028 }, - { 0x0d010402, 0x24000028 }, - { 0x0d010403, 0x24000028 }, - { 0x0d010404, 0x24000028 }, - { 0x0d010405, 0x24000028 }, - { 0x0d010406, 0x24000028 }, - { 0x0d010407, 0x24000028 }, - { 0x0d010408, 0x24000028 }, - { 0x0d010409, 0x24000028 }, - { 0x0d01040a, 0x24000028 }, - { 0x0d01040b, 0x24000028 }, - { 0x0d01040c, 0x24000028 }, - { 0x0d01040d, 0x24000028 }, - { 0x0d01040e, 0x24000028 }, - { 0x0d01040f, 0x24000028 }, - { 0x0d010410, 0x24000028 }, - { 0x0d010411, 0x24000028 }, - { 0x0d010412, 0x24000028 }, - { 0x0d010413, 0x24000028 }, - { 0x0d010414, 0x24000028 }, - { 0x0d010415, 0x24000028 }, - { 0x0d010416, 0x24000028 }, - { 0x0d010417, 0x24000028 }, - { 0x0d010418, 0x24000028 }, - { 0x0d010419, 0x24000028 }, - { 0x0d01041a, 0x24000028 }, - { 0x0d01041b, 0x24000028 }, - { 0x0d01041c, 0x24000028 }, - { 0x0d01041d, 0x24000028 }, - { 0x0d01041e, 0x24000028 }, - { 0x0d01041f, 0x24000028 }, - { 0x0d010420, 0x24000028 }, - { 0x0d010421, 0x24000028 }, - { 0x0d010422, 0x24000028 }, - { 0x0d010423, 0x24000028 }, - { 0x0d010424, 0x24000028 }, - { 0x0d010425, 0x24000028 }, - { 0x0d010426, 0x24000028 }, - { 0x0d010427, 0x24000028 }, - { 0x0d010428, 0x1400ffd8 }, - { 0x0d010429, 0x1400ffd8 }, - { 0x0d01042a, 0x1400ffd8 }, - { 0x0d01042b, 0x1400ffd8 }, - { 0x0d01042c, 0x1400ffd8 }, - { 0x0d01042d, 0x1400ffd8 }, - { 0x0d01042e, 0x1400ffd8 }, - { 0x0d01042f, 0x1400ffd8 }, - { 0x0d010430, 0x1400ffd8 }, - { 0x0d010431, 0x1400ffd8 }, - { 0x0d010432, 0x1400ffd8 }, - { 0x0d010433, 0x1400ffd8 }, - { 0x0d010434, 0x1400ffd8 }, - { 0x0d010435, 0x1400ffd8 }, - { 0x0d010436, 0x1400ffd8 }, - { 0x0d010437, 0x1400ffd8 }, - { 0x0d010438, 0x1400ffd8 }, - { 0x0d010439, 0x1400ffd8 }, - { 0x0d01043a, 0x1400ffd8 }, - { 0x0d01043b, 0x1400ffd8 }, - { 0x0d01043c, 0x1400ffd8 }, - { 0x0d01043d, 0x1400ffd8 }, - { 0x0d01043e, 0x1400ffd8 }, - { 0x0d01043f, 0x1400ffd8 }, - { 0x0d010440, 0x1400ffd8 }, - { 0x0d010441, 0x1400ffd8 }, - { 0x0d010442, 0x1400ffd8 }, - { 0x0d010443, 0x1400ffd8 }, - { 0x0d010444, 0x1400ffd8 }, - { 0x0d010445, 0x1400ffd8 }, - { 0x0d010446, 0x1400ffd8 }, - { 0x0d010447, 0x1400ffd8 }, - { 0x0d010448, 0x1400ffd8 }, - { 0x0d010449, 0x1400ffd8 }, - { 0x0d01044a, 0x1400ffd8 }, - { 0x0d01044b, 0x1400ffd8 }, - { 0x0d01044c, 0x1400ffd8 }, - { 0x0d01044d, 0x1400ffd8 }, - { 0x0d01044e, 0x1400ffd8 }, - { 0x0d01044f, 0x1400ffd8 }, - { 0x2e810450, 0x1c00002f }, - { 0x2c810480, 0x1c00001d }, - { 0x2c8104a0, 0x34000009 }, - { 0x0b810800, 0x1c000005 }, - { 0x0b010808, 0x1c000000 }, - { 0x0b81080a, 0x1c00002b }, - { 0x0b810837, 0x1c000001 }, - { 0x0b01083c, 0x1c000000 }, - { 0x0b01083f, 0x1c000000 }, - { 0x41810900, 0x1c000015 }, - { 0x41810916, 0x3c000003 }, - { 0x4101091f, 0x54000000 }, - { 0x1e010a00, 0x1c000000 }, - { 0x1e810a01, 0x30000002 }, - { 0x1e810a05, 0x30000001 }, - { 0x1e810a0c, 0x30000003 }, - { 0x1e810a10, 0x1c000003 }, - { 0x1e810a15, 0x1c000002 }, - { 0x1e810a19, 0x1c00001a }, - { 0x1e810a38, 0x30000002 }, - { 0x1e010a3f, 0x30000000 }, - { 0x1e810a40, 0x3c000007 }, - { 0x1e810a50, 0x54000008 }, - { 0x3e812000, 0x1c00036e }, - { 0x3e812400, 0x38000062 }, - { 0x3e812470, 0x54000003 }, - { 0x0981d000, 0x680000f5 }, - { 0x0981d100, 0x68000026 }, - { 0x0981d12a, 0x6800003a }, - { 0x0981d165, 0x28000001 }, - { 0x1b81d167, 0x30000002 }, - { 0x0981d16a, 0x68000002 }, - { 0x0981d16d, 0x28000005 }, - { 0x0981d173, 0x04000007 }, - { 0x1b81d17b, 0x30000007 }, - { 0x0981d183, 0x68000001 }, - { 0x1b81d185, 0x30000006 }, - { 0x0981d18c, 0x6800001d }, - { 0x1b81d1aa, 0x30000003 }, - { 0x0981d1ae, 0x6800002f }, - { 0x1381d200, 0x68000041 }, - { 0x1381d242, 0x30000002 }, - { 0x1301d245, 0x68000000 }, - { 0x0981d300, 0x68000056 }, - { 0x0981d360, 0x3c000011 }, - { 0x0981d400, 0x24000019 }, - { 0x0981d41a, 0x14000019 }, - { 0x0981d434, 0x24000019 }, - { 0x0981d44e, 0x14000006 }, - { 0x0981d456, 0x14000011 }, - { 0x0981d468, 0x24000019 }, - { 0x0981d482, 0x14000019 }, - { 0x0901d49c, 0x24000000 }, - { 0x0981d49e, 0x24000001 }, - { 0x0901d4a2, 0x24000000 }, - { 0x0981d4a5, 0x24000001 }, - { 0x0981d4a9, 0x24000003 }, - { 0x0981d4ae, 0x24000007 }, - { 0x0981d4b6, 0x14000003 }, - { 0x0901d4bb, 0x14000000 }, - { 0x0981d4bd, 0x14000006 }, - { 0x0981d4c5, 0x1400000a }, - { 0x0981d4d0, 0x24000019 }, - { 0x0981d4ea, 0x14000019 }, - { 0x0981d504, 0x24000001 }, - { 0x0981d507, 0x24000003 }, - { 0x0981d50d, 0x24000007 }, - { 0x0981d516, 0x24000006 }, - { 0x0981d51e, 0x14000019 }, - { 0x0981d538, 0x24000001 }, - { 0x0981d53b, 0x24000003 }, - { 0x0981d540, 0x24000004 }, - { 0x0901d546, 0x24000000 }, - { 0x0981d54a, 0x24000006 }, - { 0x0981d552, 0x14000019 }, - { 0x0981d56c, 0x24000019 }, - { 0x0981d586, 0x14000019 }, - { 0x0981d5a0, 0x24000019 }, - { 0x0981d5ba, 0x14000019 }, - { 0x0981d5d4, 0x24000019 }, - { 0x0981d5ee, 0x14000019 }, - { 0x0981d608, 0x24000019 }, - { 0x0981d622, 0x14000019 }, - { 0x0981d63c, 0x24000019 }, - { 0x0981d656, 0x14000019 }, - { 0x0981d670, 0x24000019 }, - { 0x0981d68a, 0x1400001b }, - { 0x0981d6a8, 0x24000018 }, - { 0x0901d6c1, 0x64000000 }, - { 0x0981d6c2, 0x14000018 }, - { 0x0901d6db, 0x64000000 }, - { 0x0981d6dc, 0x14000005 }, - { 0x0981d6e2, 0x24000018 }, - { 0x0901d6fb, 0x64000000 }, - { 0x0981d6fc, 0x14000018 }, - { 0x0901d715, 0x64000000 }, - { 0x0981d716, 0x14000005 }, - { 0x0981d71c, 0x24000018 }, - { 0x0901d735, 0x64000000 }, - { 0x0981d736, 0x14000018 }, - { 0x0901d74f, 0x64000000 }, - { 0x0981d750, 0x14000005 }, - { 0x0981d756, 0x24000018 }, - { 0x0901d76f, 0x64000000 }, - { 0x0981d770, 0x14000018 }, - { 0x0901d789, 0x64000000 }, - { 0x0981d78a, 0x14000005 }, - { 0x0981d790, 0x24000018 }, - { 0x0901d7a9, 0x64000000 }, - { 0x0981d7aa, 0x14000018 }, - { 0x0901d7c3, 0x64000000 }, - { 0x0981d7c4, 0x14000005 }, - { 0x0901d7ca, 0x24000000 }, - { 0x0901d7cb, 0x14000000 }, - { 0x0981d7ce, 0x34000031 }, - { 0x16820000, 0x1c00a6d6 }, - { 0x1682f800, 0x1c00021d }, - { 0x090e0001, 0x04000000 }, - { 0x098e0020, 0x0400005f }, - { 0x1b8e0100, 0x300000ef }, - { 0x098f0000, 0x0c00fffd }, - { 0x09900000, 0x0c00fffd }, -}; -- cgit v1.2.3 From 02986f8623e02b5afba41f7f593710e74b3c8a7f Mon Sep 17 00:00:00 2001 From: Stefan Zegenhagen Date: Wed, 29 May 2013 16:50:43 +0200 Subject: Fix changing terminal parameters in to_erl One of our devices does not like 'to_erl' to be run over a serial port. When to_erl is started, we see "Attaching to /tm<0xFF>" being printed and the device then refuses to accept any input. Occasionally, we have seen a linux kernel error message "serial8250: too much work for irq16" simultaneously. After some debugging we found out that cause is a call to tcsetattr() by to_erl, immediately preceeded by some printf(). The UART in our device doesn't like hardware parameters to be changed while output is concurrently active. In fact, the GNU libc manual also mentions that it might be dangerous to change UART hardware parameters when a transmission is ongoing. The patch attached to this e-mail changes the behaviour of to_erl to use TCSADRAIN instead of TCSANOW when changing terminal parameters. This makes the serial driver wait for the output queues to be empty before applying the terminal parameter change. --- erts/etc/unix/to_erl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c index 094006c5fd..5a8c9f77d4 100644 --- a/erts/etc/unix/to_erl.c +++ b/erts/etc/unix/to_erl.c @@ -339,7 +339,7 @@ int main(int argc, char **argv) tty_smode.c_cc[VTIME] =0;/* Note that VTIME is the same as VEOL! */ tty_smode.c_cc[VINTR] =3; - tcsetattr(0, TCSANOW, &tty_smode); + tcsetattr(0, TCSADRAIN, &tty_smode); #ifdef DEBUG show_terminal_settings(&tty_smode); @@ -484,7 +484,7 @@ int main(int argc, char **argv) * Reset terminal characterstics * XXX */ - tcsetattr(0, TCSANOW, &tty_rmode); + tcsetattr(0, TCSADRAIN, &tty_rmode); return 0; } -- cgit v1.2.3 From 2bf244968fb368b204e7af4b33b72d6a71a937c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 24 Jul 2013 19:55:47 +0200 Subject: erts: Extend erl_driver interface with lock names Lock and thread names are already a feature in the driver interface. This extension will let developers read these names. Eases debugging. --- erts/emulator/beam/erl_driver.h | 8 ++++--- erts/emulator/beam/erl_drv_thread.c | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index e280563de1..42b14cd7bc 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -546,6 +546,11 @@ EXTERN int erl_drv_equal_tids(ErlDrvTid tid1, ErlDrvTid tid2); EXTERN void erl_drv_thread_exit(void *resp); EXTERN int erl_drv_thread_join(ErlDrvTid, void **respp); +EXTERN char* erl_drv_mutex_name(ErlDrvMutex *mtx); +EXTERN char* erl_drv_cond_name(ErlDrvCond *cnd); +EXTERN char* erl_drv_rwlock_name(ErlDrvRWLock *rwlck); +EXTERN char* erl_drv_thread_name(ErlDrvTid tid); + /* * Misc. */ @@ -681,6 +686,3 @@ EXTERN int erl_drv_getenv(char *key, char *value, size_t *value_size); /* also in global.h, but driver's can't include global.h */ void dtrace_drvport_str(ErlDrvPort port, char *port_buf); - - - diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index a49a155701..4f1bba8657 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -188,6 +188,17 @@ erl_drv_mutex_destroy(ErlDrvMutex *dmtx) #endif } + +char * +erl_drv_mutex_name(ErlDrvMutex *dmtx) +{ +#ifdef USE_THREADS + return dmtx ? dmtx->name : NULL; +#else + return NULL; +#endif +} + int erl_drv_mutex_trylock(ErlDrvMutex *dmtx) { @@ -258,6 +269,15 @@ erl_drv_cond_destroy(ErlDrvCond *dcnd) #endif } +char * +erl_drv_cond_name(ErlDrvCond *dcnd) +{ +#ifdef USE_THREADS + return dcnd ? dcnd->name : NULL; +#else + return NULL; +#endif +} void erl_drv_cond_signal(ErlDrvCond *dcnd) @@ -331,6 +351,16 @@ erl_drv_rwlock_destroy(ErlDrvRWLock *drwlck) #endif } +char * +erl_drv_rwlock_name(ErlDrvRWLock *drwlck) +{ +#ifdef USE_THREADS + return drwlck ? drwlck->name : NULL; +#else + return NULL; +#endif +} + int erl_drv_rwlock_tryrlock(ErlDrvRWLock *drwlck) { @@ -617,6 +647,18 @@ erl_drv_thread_create(char *name, #endif } +char * +erl_drv_thread_name(ErlDrvTid tid) +{ +#ifdef USE_THREADS + struct ErlDrvTid_ *dtid = (struct ErlDrvTid_ *) tid; + return dtid ? dtid->name : NULL; +#else + return NULL; +#endif +} + + ErlDrvTid erl_drv_thread_self(void) { -- cgit v1.2.3 From 0642847e85a0517c4063e23f21dcd18052e8d94c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 29 Jul 2013 15:08:18 +0200 Subject: Bailout if no native implementations are found Some basic tests are already done in configure. This makes sure we cover all cases by bailing out when compiling as well. --- erts/configure.in | 5 ++++- erts/include/internal/ethread.h | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 2f624e5853..64436e933c 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1051,12 +1051,15 @@ if test $ERTS_BUILD_SMP_EMU = yes; then AC_DEFINE(ERTS_HAVE_SMP_EMU, 1, [Define if the smp emulator is built]) + test "X$smp_require_native_atomics" = "Xyes" && + AC_DEFINE(ETHR_SMP_REQUIRE_NATIVE_IMPLS, 1, [Define if you want to enable check for native ethread implementations]) + case "$ethr_have_native_atomics-$smp_require_native_atomics-$ethr_have_native_spinlock" in yes-*) ;; no-yes-*) - AC_MSG_ERROR([No native atomic implementation found. See INSTALL.md for more information.]) + AC_MSG_ERROR([No native atomic implementation found. See Configuring section in INSTALL.md for more information.]) ;; no-no-yes) diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 407097d4b1..38b8e9e9b6 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -361,6 +361,10 @@ extern ethr_runtime_t ethr_runtime__; # endif #endif /* !ETHR_DISABLE_NATIVE_IMPLS */ +#if !defined(ETHR_HAVE_NATIVE_ATOMIC32) && !defined(ETHR_HAVE_NATIVE_ATOMIC64) && !defined(ETHR_DISABLE_NATIVE_IMPLS) && defined(ETHR_SMP_REQUIRE_NATIVE_IMPLS) +#error "No native ethread implementation found. If you want to use fallbacks you have to disable native ethread support with configure." +#endif + #include "ethr_atomics.h" /* The atomics API */ #if defined(__GNUC__) -- cgit v1.2.3 From 397caf87b39380f5517954320f25fdf9b97d0c90 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Tue, 30 Jul 2013 11:39:35 +0200 Subject: Change default of erlang:halt/2 to the documented Bug reported by Jose Valim on the erlang-bugs mailing list: erlang:halt(0,[]) does not flush as advertised in the documentation. Should be the same as erlang:halt(0,[{flush,true}]), but is in fact the same as erlang:halt(0,[{flush,false}]). --- erts/emulator/beam/bif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index ff237b6a78..755c5e6882 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3934,7 +3934,7 @@ BIF_RETTYPE halt_2(BIF_ALIST_2) { Sint code; Eterm optlist = BIF_ARG_2; - int flush = 0; + int flush = 1; for (optlist = BIF_ARG_2; is_list(optlist); -- cgit v1.2.3 From 67b37031bbec0cc7eecc0e02670d02b8f1b9092e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 2 Aug 2013 20:23:43 +0200 Subject: erts: Create gdb pything script for thread listing --- erts/.gitignore | 1 + erts/etc/Makefile | 3 +- erts/etc/unix/Makefile | 46 + erts/etc/unix/etp-commands | 2851 ---------------------------------------- erts/etc/unix/etp-commands.in | 2855 +++++++++++++++++++++++++++++++++++++++++ erts/etc/unix/etp-thr.py | 57 + 6 files changed, 2961 insertions(+), 2852 deletions(-) create mode 100644 erts/etc/unix/Makefile delete mode 100644 erts/etc/unix/etp-commands create mode 100644 erts/etc/unix/etp-commands.in create mode 100644 erts/etc/unix/etp-thr.py (limited to 'erts') diff --git a/erts/.gitignore b/erts/.gitignore index 526d5da0b9..e515dc8811 100644 --- a/erts/.gitignore +++ b/erts/.gitignore @@ -11,6 +11,7 @@ /etc/common/Install /etc/common/erl.src +/etc/unix/etp-commands /test/Emakefile /test/*.beam diff --git a/erts/etc/Makefile b/erts/etc/Makefile index 2b32b8ae50..5b54ef9c3e 100644 --- a/erts/etc/Makefile +++ b/erts/etc/Makefile @@ -18,10 +18,11 @@ # include $(ERL_TOP)/make/target.mk - SUB_DIRECTORIES = common ifeq ($(TARGET),win32) SUB_DIRECTORIES += win32 +else +SUB_DIRECTORIES += unix endif include $(ERL_TOP)/make/otp_subdir.mk diff --git a/erts/etc/unix/Makefile b/erts/etc/unix/Makefile new file mode 100644 index 0000000000..e85d2fab0c --- /dev/null +++ b/erts/etc/unix/Makefile @@ -0,0 +1,46 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2013. All Rights Reserved. +# +# The 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% +# + +include $(ERL_TOP)/make/output.mk +include $(ERL_TOP)/make/target.mk + +include $(ERL_TOP)/make/$(TARGET)/otp.mk +include ../../vsn.mk + +opt debug: etc + +.PHONY: etc +etc: etp-commands + +etp-commands: etp-commands.in + sed 's:@ERL_TOP@:${ERL_TOP}:g' etp-commands.in > etp-commands + +.PHONY: docs +docs: + +.PHONY: clean +clean: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +.PHONY: release_spec +release_spec: etc \ No newline at end of file diff --git a/erts/etc/unix/etp-commands b/erts/etc/unix/etp-commands deleted file mode 100644 index 35f75df5c1..0000000000 --- a/erts/etc/unix/etp-commands +++ /dev/null @@ -1,2851 +0,0 @@ -# -# %CopyrightBegin% -# -# 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 -# 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% -# - -############################################################################ -# Help commands -# - -define etp-help - help etp-help -end - -document etp-help -%--------------------------------------------------------------------------- -% etp-help -% -% Same as "help etp-help" -% -% Emulator Toolbox for Pathologists -% - GDB command toolbox for analyzing core dumps from the -% Erlang emulator (BEAM). -% -% Should work for 32-bit erts-5.2/R9B, ... -% -% The commands are prefixed with: -% etp: Acronym for erts-term-print -% etpf: Acronym for erts-term-print-flat -% -% User commands (these have help themselves): -% -% Most useful: -% etp, etpf -% -% Useful for doing step-by-step traversal of lists and tuples after -% calling the toplevel command etpf: -% etpf-cons, etpf-boxed, -% -% Special commands for not really terms: -% etp-mfa, etp-cp, -% etp-msgq, etpf-msgq, -% etp-stacktrace, etp-stackdump, etpf-stackdump, etp-dictdump -% etp-offheapdump, etpf-offheapdump, -% etp-print-procs, etp-search-heaps, etp-search-alloc, -% etp-ets-tables, etp-ets-tabledump -% -% Complex commands that use the Erlang support module. -% etp-overlapped-heaps, etp-chart, etp-chart-start, etp-chart-end -% -% Erlang support module handling commands: -% etp-run -% -% Parameter handling commands: -% etp-show, etp-set-max-depth, etp-set-max-string-length -% -% Other commands you may find in this toolbox are suffixed -1, -2, ... -% and are internal; not for the console user. -% -% The Erlang support module requires `erl' and `erlc' in the path. -% The compiled "erl_commands.beam" file is stored in the current -% working directory, so it is thereby in the search path of `erl'. -% -% These are just helpful commands when analyzing core dumps, but -% you will not get away without knowing the gory details of the -% tag bits. Do not forget about the e.g p, p/x, x and x/4x commands. -% -% Execution speed of user defined gdb commands is not lightning fast. -% It may well take half a minute to dump a complex term with the default -% max depth values on our old Sparc Ultra-10's. -% -% To use the Erlang support module, the environment variable ROOTDIR -% must be set to the toplevel installation directory of Erlang/OTP, -% so the etp-commands file becomes: -% $ROOTDIR/erts/etc/unix/etp-commands -% Also, erl and erlc must be in the path. -%--------------------------------------------------------------------------- -end - -############################################################################ -# Toplevel commands -# - -define etp -# Args: Eterm -# -# Reentrant -# - etp-1 ((Eterm)($arg0)) 0 - printf ".\n" -end - -document etp -%--------------------------------------------------------------------------- -% etp Eterm -% -% Takes a toplevel Erlang term and prints the whole deep term -% very much as in Erlang itself. Up to a max depth. See etp-show. -%--------------------------------------------------------------------------- -end - -define etp-1 -# Args: Eterm, int depth -# -# Reentrant -# - if (($arg0) & 0x3) == 1 - # Cons pointer - if $etp_flat - printf "", ($arg0) - else - etp-list-1 ($arg0) ($arg1) - end - else - if (($arg0) & 0x3) == 2 - if $etp_flat - printf "", ($arg0) - else - etp-boxed-1 ($arg0) ($arg1) - end - else - if (($arg0) & 0x3) == 3 - etp-immediate-1 ($arg0) - else - # (($arg0) & 0x3) == 0 - if (($arg0) == 0x0) - printf "" - else - if (($arg0) == 0x4) - printf "" - else - etp-cp-1 ($arg0) - end - end - end - end - end -end - -define etpf -# Args: Eterm -# -# Non-reentrant - set $etp_flat = 1 - etp-1 ((Eterm)($arg0)) - set $etp_flat = 0 - printf ".\n" -end - -document etpf -%--------------------------------------------------------------------------- -% etpf Eterm -% -% Takes a toplevel Erlang term and prints it is. If it is a deep term -% print which command to use to traverse down one level. -%--------------------------------------------------------------------------- -end - -############################################################################ -# Commands for nested terms. Some are recursive. -# - -define etp-list-1 -# Args: Eterm cons_cell, int depth -# -# Reentrant -# - if (($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) - else - # Cons pointer - if $etp_chart - etp-chart-entry-1 ($arg0) ($arg1) 2 - end - etp-list-printable-1 ($arg0) ($arg1) - if !$etp_list_printable - # Print normal list - printf "[" - etp-list-2 ($arg0) (($arg1)+1) - end - end -end - -define etp-list-printable-1 -# Args: Eterm list, int depth -# -# Non-reentrant -# -# Returns: $etp_list_printable -# - if (($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) - else - # Loop to check if it is a printable string - set $etp_list_p = ($arg0) - set $etp_list_printable = ($etp_list_p != $etp_nil) - set $etp_list_i = 0 - while ($etp_list_p != $etp_nil) && \ - ($etp_list_i < $etp_max_string_length) && \ - $etp_list_printable - if ($etp_list_p & 0x3) == 0x1 - # Cons pointer - set $etp_list_n = ((Eterm*)($etp_list_p & ~0x3))[0] - if ($etp_list_n & 0xF) == 0xF - etp-ct-printable-1 ($etp_list_n>>4) - if $etp_ct_printable - # Printable - set $etp_list_p = ((Eterm*)($etp_list_p & ~0x3))[1] - set $etp_list_i++ - else - set $etp_list_printable = 0 - end - else - set $etp_list_printable = 0 - end - else - set $etp_list_printable = 0 - end - end - # - if $etp_list_printable - # Print printable string - printf "\"" - set $etp_list_p = ($arg0) - set $etp_list_i = 0 - while $etp_list_p != $etp_nil - set $etp_list_n = ((Eterm*)($etp_list_p & ~0x3))[0] - etp-char-1 ($etp_list_n>>4) '"' - set $etp_list_p = ((Eterm*)($etp_list_p & ~0x3))[1] - set $etp_list_i++ - if $etp_list_p == $etp_nil - printf "\"" - else - if $etp_list_i >= $etp_max_string_length - set $etp_list_p = $etp_nil - printf "\"++[...]" - else - if $etp_chart - etp-chart-entry-1 ($arg0) (($arg1)+$etp_list_i) 2 - end - end - end - end - end - end -end - -define etp-list-2 -# Args: Eterm cons_cell, int depth -# -# Reentrant -# - if (($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) - else - # Cons pointer - if ($arg1) >= $etp_max_depth - printf "...]" - else - etp-1 (((Eterm*)(($arg0)&~0x3))[0]) (($arg1)+1) - if ((Eterm*)(($arg0) & ~0x3))[1] == $etp_nil - # Tail is [] - printf "]" - else - if $etp_chart - etp-chart-entry-1 ($arg0) ($arg1) 2 - end - if (((Eterm*)(($arg0)&~0x3))[1]&0x3) == 0x1 - # Tail is cons cell - printf "," - etp-list-2 (((Eterm*)(($arg0)&~0x3))[1]) (($arg1)+1) - else - # Tail is other term - printf "|" - etp-1 (((Eterm*)(($arg0)&~0x3))[1]) (($arg1)+1) - printf "]" - end - end - end - end -end - -define etpf-cons -# Args: Eterm -# -# Reentrant capable -# - if ((Eterm)($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) - else - # Cons pointer - set $etp_flat = 1 - printf "[" - etp-1 (((Eterm*)((Eterm)($arg0)&~0x3))[0]) - printf "|" - etp-1 (((Eterm*)((Eterm)($arg0)&~0x3))[1]) - printf "]\n" - set $etp_flat = 0 - end -end - -document etpf-cons -%--------------------------------------------------------------------------- -% etpf-cons Eterm -% -% Takes a Cons ptr and prints the Car and Cdr cells with etpf (flat). -%--------------------------------------------------------------------------- -end - - - -define etp-boxed-1 -# Args: Eterm, int depth -# -# Reentrant -# - if (($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", ($arg0) - else - if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 - if $etp_chart - etp-chart-entry-1 (($arg0)&~0x3) ($arg1) 1 - end - printf "#BoxedError<%#x>", ($arg0) - else - if $etp_chart - etp-chart-entry-1 (($arg0)&~0x3) ($arg1) \ - ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1) - end - if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3f) == 0x0 - printf "{" - etp-array-1 ((Eterm*)(($arg0)&~0x3)) ($arg1) ($arg1) \ - 1 ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1) '}' - else - etp-boxed-immediate-1 ($arg0) - end - end - end -end - -define etp-boxed-immediate-1 -# Args: Eterm, int depth -# -# Non-reentrant -# - if (($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", ($arg0) - else - if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 - printf "#BoxedError<%#x>", ($arg0) - else - set $etp_boxed_immediate_p = (Eterm*)(($arg0) & ~0x3) - set $etp_boxed_immediate_h = ($etp_boxed_immediate_p[0] >> 2) & 0xF - if $etp_boxed_immediate_h == 0xC - etp-extpid-1 ($arg0) - else - if $etp_boxed_immediate_h == 0xD - etp-extport-1 ($arg0) - else - if ($etp_boxed_immediate_h == 0x2) || \ - ($etp_boxed_immediate_h == 0x3) - etp-bignum-1 ($arg0) - else - if ($etp_boxed_immediate_h == 0x6) - etp-float-1 ($arg0) - else - if ($etp_boxed_immediate_h == 0x4) - etp-ref-1 ($arg0) - else - if ($etp_boxed_immediate_h == 0xE) - etp-extref-1 ($arg0) - else - # Hexdump the rest - if ($etp_boxed_immediate_h == 0x5) - printf "#Fun<" - else - if ($etp_boxed_immediate_h == 0x8) - printf "#RefcBinary<" - else - if ($etp_boxed_immediate_h == 0x9) - printf "#HeapBinary<" - else - if ($etp_boxed_immediate_h == 0xA) - printf "#SubBinary<" - else - printf "#Header%X<", $etp_boxed_immediate_h - end - end - end - end - set $etp_boxed_immediate_arity = $etp_boxed_immediate_p[0]>>6 - while $etp_boxed_immediate_arity > 0 - set $etp_boxed_immediate_p++ - if $etp_boxed_immediate_arity > 1 - printf "%#x,", *$etp_boxed_immediate_p - else - printf "%#x", *$etp_boxed_immediate_p - if ($etp_boxed_immediate_h == 0xA) - set $etp_boxed_immediate_p++ - printf ":%#x", *$etp_boxed_immediate_p - end - printf ">" - end - set $etp_boxed_immediate_arity-- - end - # End of hexdump - end - end - end - end - end - end - end - end -end - -define etpf-boxed -# Args: Eterm -# -# Non-reentrant -# - set $etp_flat = 1 - etp-boxed-1 ((Eterm)($arg0)) 0 - set $etp_flat = 0 - printf ".\n" -end - -document etpf-boxed -%--------------------------------------------------------------------------- -% etpf-boxed Eterm -% -% Take a Boxed ptr and print the contents in one level using etpf (flat). -%--------------------------------------------------------------------------- -end - - - -define etp-array-1 -# Args: Eterm* p, int depth, int width, int pos, int size, int end_char -# -# Reentrant -# - if ($arg3) < ($arg4) - if (($arg1) < $etp_max_depth) && (($arg2) < $etp_max_depth) - etp-1 (($arg0)[($arg3)]) (($arg1)+1) - if (($arg3) + 1) != ($arg4) - printf "," - end - etp-array-1 ($arg0) ($arg1) (($arg2)+1) (($arg3)+1) ($arg4) ($arg5) - else - printf "...%c", ($arg5) - end - else - printf "%c", ($arg5) - end -end - - - -#define etpa-1 -## Args: Eterm, int depth, int index, int arity -## -## Reentrant -## -# if ($arg1) >= $etp_max_depth+$etp_max_string_length -# printf "%% Max depth for term %d\n", $etp_chart_id -# else -# if ($arg2) < ($arg3) -# etp-1 (((Eterm*)(($arg0)&~0x3))[$arg2]) (($arg1)+1) -# etpa-1 ($arg0) (($arg1)+1) (($arg2)+1) ($arg3) -# end -# end -#end - -############################################################################ -# Commands for non-nested terms. Recursion leaves. Some call other leaves. -# - -define etp-immediate-1 -# Args: Eterm -# -# Reentrant capable -# - if (($arg0) & 0x3) != 0x3 - printf "#NotImmediate<%#x>", ($arg0) - else - if (($arg0) & 0xF) == 0x3 - etp-pid-1 ($arg0) - else - if (($arg0) & 0xF) == 0x7 - etp-port-1 ($arg0) - else - if (($arg0) & 0xF) == 0xf - # Fixnum - printf "%ld", (long)((Sint)($arg0)>>4) - else - # Immediate2 - 0xB - if (($arg0) & 0x3f) == 0x0b - etp-atom-1 ($arg0) - else - if (($arg0) & 0x3f) == 0x1b - printf "#Catch<%d>", ($arg0)>>6 - else - if (($arg0) == $etp_nil) - printf "[]" - else - printf "#UnknownImmediate<%#x>", ($arg0) - end - end - end - end - end - end - end -end - - - -define etp-atom-1 -# Args: Eterm atom -# -# Non-reentrant -# - if ((Eterm)($arg0) & 0x3f) != 0xb - printf "#NotAtom<%#x>", ($arg0) - else - set $etp_atom_1_ap = (Atom*)erts_atom_table.seg_table[(Eterm)($arg0)>>16][((Eterm)($arg0)>>6)&0x3FF] - set $etp_atom_1_i = ($etp_atom_1_ap)->len - set $etp_atom_1_p = ($etp_atom_1_ap)->name - set $etp_atom_1_quote = 1 - # Check if atom has to be quoted - if ($etp_atom_1_i > 0) - etp-ct-atom-1 (*$etp_atom_1_p) - if $etp_ct_atom - # Atom start character - set $etp_atom_1_p++ - set $etp_atom_1_i-- - set $etp_atom_1_quote = 0 - else - set $etp_atom_1_i = 0 - end - end - while $etp_atom_1_i > 0 - etp-ct-name-1 (*$etp_atom_1_p) - if $etp_ct_name - # Name character - set $etp_atom_1_p++ - set $etp_atom_1_i-- - else - set $etp_atom_1_quote = 1 - set $etp_atom_1_i = 0 - end - end - # Print the atom - if $etp_atom_1_quote - printf "'" - end - set $etp_atom_1_i = ($etp_atom_1_ap)->len - set $etp_atom_1_p = ($etp_atom_1_ap)->name - while $etp_atom_1_i > 0 - etp-char-1 (*$etp_atom_1_p) '\'' - set $etp_atom_1_p++ - set $etp_atom_1_i-- - end - if $etp_atom_1_quote - printf "'" - end - end -end - - - -define etp-char-1 -# Args: int char, int quote_char -# -# Non-reentrant -# - if (($arg0) < 0) || (0377 < ($arg0)) - printf "#NotChar<%#x>", ($arg0) - else - if ($arg0) == ($arg1) - printf "\\%c", ($arg0) - else - etp-ct-printable-1 ($arg0) - if $etp_ct_printable - if $etp_ct_printable < 0 - printf "%c", ($arg0) - else - printf "\\%c", $etp_ct_printable - end - else - printf "\\%03o", ($arg0) - end - end - end -end - -define etp-ct-printable-1 -# Args: int -# -# Determines if integer is a printable character -# -# Non-reentrant -# Returns: $etp_ct_printable -# escape alias char, or -1 if no escape alias - if ($arg0) == 010 - set $etp_ct_printable = 'b' - else - if ($arg0) == 011 - set $etp_ct_printable = 't' - else - if ($arg0) == 012 - set $etp_ct_printable = 'n' - else - if ($arg0) == 013 - set $etp_ct_printable = 'v' - else - if ($arg0) == 014 - set $etp_ct_printable = 'f' - else - if ($arg0) == 033 - set $etp_ct_printable = 'e' - else - if ((040 <= ($arg0)) && (($arg0) <= 0176)) || \ - ((0240 <= ($arg0)) && (($arg0) <= 0377)) - # Other printable character - set $etp_ct_printable = -1 - else - set $etp_ct_printable = 0 - end - end - end - end - end - end - end -end - -define etp-ct-atom-1 -# Args: int -# -# Determines if integer is a atom first character -# -# Non-reentrant -# Returns: $etp_ct_atom - if ((0141 <= ($arg0)) && (($arg0) <= 0172)) || \ - ((0337 <= ($arg0)) && (($arg0) != 0367) && (($arg0) <= 0377)) - # Atom start character - set $etp_ct_atom = 1 - else - set $etp_ct_atom = 0 - end -end - -define etp-ct-variable-1 -# Args: int -# -# Determines if integer is a variable first character -# -# Non-reentrant -# Returns: $etp_ct_variable - if ((056 == ($arg0)) || \ - (0101 <= ($arg0)) && (($arg0) <= 0132)) || \ - (0137 == ($arg0)) || \ - ((0300 <= ($arg0)) && (($arg0) != 0327) && (($arg0) <= 0336)) - # Variable start character - set $etp_ct_variable = 1 - else - set $etp_ct_variable = 0 - end -end - -define etp-ct-name-1 -# Args: int -# -# Determines if integer is a name character, -# i.e non-first atom or variable character. -# -# Non-reentrant -# Returns: $etp_ct_variable - if (($arg0) == 0100 || \ - (060 <= ($arg0)) && (($arg0) <= 071)) - set $etp_ct_name = 1 - else - etp-ct-atom-1 ($arg0) - if $etp_ct_atom - set $etp_ct_name = 1 - else - etp-ct-variable-1 ($arg0) - set $etp_ct_name = $etp_ct_variable - end - end -end - -define etp-pid-1 -# Args: Eterm pid -# -# Non-reentrant -# - set $etp_pid_1 = (Eterm)($arg0) - if ($etp_pid_1 & 0xF) == 0x3 - if (etp_arch_bits == 64 && etp_halfword == 0) - if (etp_big_endian) - set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 36) & 0x0fffffff) - else - set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 4) & 0x0fffffff) - end - else - set $etp_pid_data = (unsigned) (((((Uint32) $etp_pid_1) >> 4) & ~erts_proc.r.o.pix_mask) | ((((Uint32) $etp_pid_1) >> (erts_proc.r.o.pix_cl_shift + 4)) & erts_proc.r.o.pix_cl_mask) | (((((Uint32) $etp_pid_1) >> 4) & erts_proc.r.o.pix_cli_mask) << erts_proc.r.o.pix_cli_shift)) - end - # Internal pid - printf "<0.%u.%u>", $etp_pid_data & 0x7fff, ($etp_pid_data >> 15) & 0x1fff - else - printf "#NotPid<%#x>", ($arg0) - end -end - -define etp-extpid-1 -# Args: Eterm extpid -# -# Non-reentrant -# - if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) - else - set $etp_extpid_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) - if ($etp_extpid_1_p->header & 0x3f) != 0x30 - printf "#NotExternalPid<%#x>", $etp_extpid_1_p->header - else - ## External pid - set $etp_extpid_1_number = $etp_extpid_1_p->data.ui[0]&0x7fff - set $etp_extpid_1_serial = ($etp_extpid_1_p->data.ui[0]>>15)&0x1fff - set $etp_extpid_1_np = $etp_extpid_1_p->node - set $etp_extpid_1_creation = $etp_extpid_1_np->creation - set $etp_extpid_1_dep = $etp_extpid_1_np->dist_entry - set $etp_extpid_1_node = $etp_extpid_1_np->sysname - if ($etp_extpid_1_node & 0x3f) != 0xb - # Should be an atom - printf "#ExternalPidError<%#x>", ($arg0) - else - if $etp_extpid_1_dep == erts_this_dist_entry - printf "<0:" - else - printf "<%u:", $etp_extpid_1_node>>6 - end - etp-atom-1 ($etp_extpid_1_node) - printf "/%u.%u.%u>", $etp_extpid_1_creation, \ - $etp_extpid_1_number, $etp_extpid_1_serial - end - end - end -end - - -define etp-port-1 -# Args: Eterm port -# -# Non-reentrant -# - set $etp_port_1 = (Eterm)($arg0) - if ($etp_port_1 & 0xF) == 0x7 - if (etp_arch_bits == 64 && etp_halfword == 0) - if (etp_big_endian) - set $etp_port_data = (unsigned) ((((Uint64) $etp_port_1) >> 36) & 0x0fffffff) - else - set $etp_port_data = (unsigned) ((((Uint64) $etp_port_1) >> 4) & 0x0fffffff) - end - else - set $etp_port_data = (unsigned) (((((Uint32) $etp_port_1) >> 4) & ~erts_port.r.o.pix_mask) | ((((Uint32) $etp_port_1) >> (erts_port.r.o.pix_cl_shift + 4)) & erts_port.r.o.pix_cl_mask) | (((((Uint32) $etp_port_1) >> 4) & erts_port.r.o.pix_cli_mask) << erts_port.r.o.pix_cli_shift)) - end - # Internal port - printf "#Port<0.%u>", $etp_port_data - else - printf "#NotPort<%#x>", ($arg0) - end -end - -define etp-extport-1 -# Args: Eterm extport -# -# Non-reentrant -# - if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) - else - set $etp_extport_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) - if ($etp_extport_1_p->header & 0x3F) != 0x34 - printf "#NotExternalPort<%#x>", $etp_extport_1->header - else - ## External port - set $etp_extport_1_number = $etp_extport_1_p->data.ui[0]&0x3ffff - set $etp_extport_1_np = $etp_extport_1_p->node - set $etp_extport_1_creation = $etp_extport_1_np->creation - set $etp_extport_1_dep = $etp_extport_1_np->dist_entry - set $etp_extport_1_node = $etp_extport_1_np->sysname - if ($etp_extport_1_node & 0x3f) != 0xb - # Should be an atom - printf "#ExternalPortError<%#x>", ($arg0) - else - if $etp_extport_1_dep == erts_this_dist_entry - printf "#Port<0:" - else - printf "#Port<%u:", $etp_extport_1_node>>6 - end - etp-atom-1 ($etp_extport_1_node) - printf "/%u.%u>", $etp_extport_1_creation, $etp_extport_1_number - end - end - end -end - - - -define etp-bignum-1 -# Args: Eterm bignum -# -# Non-reentrant -# - if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) - else - set $etp_bignum_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) - if ($etp_bignum_1_p[0] & 0x3b) != 0x08 - printf "#NotBignum<%#x>", $etp_bignum_1_p[0] - else - set $etp_bignum_1_i = ($etp_bignum_1_p[0] >> 6) - if $etp_bignum_1_i < 1 - printf "#BignumError<%#x>", (Eterm)($arg0) - else - if $etp_bignum_1_p[0] & 0x04 - printf "-" - end - set $etp_bignum_1_p = (ErtsDigit *)($etp_bignum_1_p + 1) - printf "16#" - if $etp_arch64 - while $etp_bignum_1_i > 0 - set $etp_bignum_1_i-- - printf "%016lx", $etp_bignum_1_p[$etp_bignum_1_i] - end - else - while $etp_bignum_1_i > 0 - set $etp_bignum_1_i-- - printf "%08x", $etp_bignum_1_p[$etp_bignum_1_i] - end - end - end - end - end -end - - - -define etp-float-1 -# Args: Eterm float -# -# Non-reentrant -# - if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) - else - set $etp_float_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) - if ($etp_float_1_p[0] & 0x3f) != 0x18 - printf "#NotFloat<%#x>", $etp_float_1_p[0] - else - printf "%f", *(double*)($etp_float_1_p+1) - end - end -end - - - -define etp-ref-1 -# Args: Eterm ref -# -# Non-reentrant -# - if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) - else - set $etp_ref_1_p = (RefThing *)((Eterm)($arg0) & ~0x3) - if ($etp_ref_1_p->header & 0x3b) != 0x10 - printf "#NotRef<%#x>", $etp_ref_1_p->header - else - set $etp_ref_1_nump = (Uint32 *) 0 - set $etp_ref_1_error = 0 - if ($etp_ref_1_p->header >> 6) == 0 - set $etp_ref_1_error = 1 - else - if $etp_arch64 - set $etp_ref_1_i = (int) $etp_ref_1_p->data.ui32[0] - if (($etp_ref_1_i + 1) > (2 * ($etp_ref_1_p->header >> 6))) - set $etp_ref_1_error = 1 - else - set $etp_ref_1_nump = &$etp_ref_1_p->data.ui32[1] - end - else - set $etp_ref_1_i = (int) ($etp_ref_1_p->header >> 6) - set $etp_ref_1_nump = &$etp_ref_1_p->data.ui32[0] - end - end - if $etp_ref_1_error - printf "#InternalRefError<%#x>", ($arg0) - else - printf "#Ref<0" - set $etp_ref_1_i-- - while $etp_ref_1_i >= 0 - printf ".%u", (unsigned) $etp_ref_1_nump[$etp_ref_1_i] - set $etp_ref_1_i-- - end - printf ">" - end - end - end -end - - - -define etp-extref-1 -# Args: Eterm extref -# -# Non-reentrant -# - if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) - else - set $etp_extref_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) - if ($etp_extref_1_p->header & 0x3F) != 0x38 - printf "#NotExternalRef<%#x>", $etp_extref_1->header - else - ## External ref - set $etp_extref_1_nump = (Uint32 *) 0 - set $etp_extref_1_error = 0 - set $etp_extref_1_i = (int) ($etp_extref_1_p->header >> 6) - set $etp_extref_1_np = $etp_extref_1_p->node - set $etp_extref_1_creation = $etp_extref_1_np->creation - set $etp_extref_1_dep = $etp_extref_1_np->dist_entry - set $etp_extref_1_node = $etp_extref_1_np->sysname - if ($etp_extref_1_node & 0x3f) != 0xb || $etp_extref_1_i < 3 - # Node should be an atom - set $etp_extref_1_error = 1 - else - ## $etp_extref_1_i now equals data (Uint) words - set $etp_extref_1_i -= 2 - if $etp_arch64 - if ((((int) $etp_extref_1_p->data.ui32[0]) + 1) \ - > (2 * $etp_extref_1_i)) - set $etp_extref_1_error = 1 - else - set $etp_extref_1_nump = &$etp_extref_1_p->data.ui32[1] - set $etp_extref_1_i = (int) $etp_extref_1_p->data.ui32[0] - end - else - set $etp_extref_1_nump = &$etp_extref_1_p->data.ui32[0] - end - ## $etp_extref_1_i now equals no of ref num (Uint32) words - if !$etp_extref_1_error - if $etp_extref_1_dep == erts_this_dist_entry - printf "#Ref<0:" - else - printf "#Ref<%u:", $etp_extref_1_node>>6 - end - etp-atom-1 ($etp_extref_1_node) - printf "/%u", $etp_extref_1_creation - end - end - if $etp_extref_1_error - printf "#ExternalRefError<%#x>", ($arg0) - else - set $etp_extref_1_i-- - while $etp_extref_1_i >= 0 - printf ".%u", (unsigned) $etp_extref_1_nump[$etp_extref_1_i] - set $etp_extref_1_i-- - end - printf ">" - end - end - end -end - - - -define etp-mfa-1 -# Args: Eterm*, int offset -# -# Reentrant -# - printf "<" - etp-atom-1 (((Eterm*)($arg0))[0]) - printf ":" - etp-atom-1 (((Eterm*)($arg0))[1]) - printf "/%d", ((Eterm*)($arg0))[2] - if ($arg1) > 0 - printf "+%#x>", ($arg1) - else - printf ">" - end -end - -define etp-mfa -# Args: Eterm* -# -# Reentrant capable -# - etp-mfa-1 ($arg0) 0 - printf ".\n" -end - -document etp-mfa -%--------------------------------------------------------------------------- -% etp-mfa Eterm* -% -% Take an Eterm* to an MFA function name entry and print it. -% These can be found e.g in the process structure; -% process_tab[i]->current and process_tab[i]->initial. -%--------------------------------------------------------------------------- -end - - - -define etp-cp-1 -# Args: Eterm cp -# -# Non-reentrant -# - set $etp_cp = (Eterm)($arg0) - 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 > (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 - end - end - set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2 - end - if $etp_cp_p - set $etp_cp_low = (Eterm**)($etp_cp_p->start + 8) - set $etp_cp_high = $etp_cp_low +$etp_cp_p->start[0] - set $etp_cp_p = 0 - while $etp_cp_low < $etp_cp_high - set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2 - if $etp_cp < $etp_cp_mid[0] - set $etp_cp_high = $etp_cp_mid - else - if $etp_cp < $etp_cp_mid[1] - set $etp_cp_p = $etp_cp_mid[0]+2 - set $etp_cp_low = $etp_cp_high = $etp_cp_mid - else - set $etp_cp_low = $etp_cp_mid + 1 - end - end - end - end - if $etp_cp_p - printf "#Cp" - etp-mfa-1 ($etp_cp_p) ($etp_cp-((Eterm)($etp_cp_p-2))) - else - if $etp_cp == beam_apply+1 - printf "#Cp" - else - if *(Eterm*)($etp_cp) == beam_return_trace[0] - if ($etp_cp) == beam_exception_trace - printf "#Cp" - else - printf "#Cp" - end - else - if *(Eterm*)($etp_cp) == beam_return_to_trace[0] - printf "#Cp" - else - printf "#Cp<%#x>", $etp_cp - end - end - end - end -end - -define etp-cp -# Args: Eterm cp -# -# Reentrant capable -# - etp-cp-1 ($arg0) - printf ".\n" -end - -document etp-cp -%--------------------------------------------------------------------------- -% etp-cp Eterm -% -% Take a code continuation pointer and print -% module, function, arity and offset. -% -% Code continuation pointers can be found in the process structure e.g -% process_tab[i]->cp and process_tab[i]->i, the second is the -% program counter, which is the same thing as a continuation pointer. -%--------------------------------------------------------------------------- -end - -############################################################################ -# Commands for special term bunches. -# - -define etp-msgq -# Args: ErlMessageQueue* -# -# Non-reentrant -# - set $etp_msgq = ($arg0) - set $etp_msgq_p = $etp_msgq->first - set $etp_msgq_i = $etp_msgq->len - set $etp_msgq_prev = $etp_msgq->last - printf "%% Message queue (%d):", $etp_msgq_i - if ($etp_msgq_i > 0) && $etp_msgq_p - printf "\n[" - else - printf "\n" - end - while ($etp_msgq_i > 0) && $etp_msgq_p - set $etp_msgq_i-- - set $etp_msgq_next = $etp_msgq_p->next - # Msg - etp-1 ($etp_msgq_p->m[0]) 0 - if ($etp_msgq_i > 0) && $etp_msgq_next - printf ", %% " - else - printf "]. %% " - end - # Seq_trace token - etp-1 ($etp_msgq_p->m[1]) 0 - if $etp_msgq_p == $etp_msgq->save - printf ", <=\n" - else - printf "\n" - end - if ($etp_msgq_i > 0) && $etp_msgq_next - printf " " - end - # - set $etp_msgq_prev = $etp_msgq_p - set $etp_msgq_p = $etp_msgq_next - end - if $etp_msgq_i != 0 - printf "#MsgQShort<%d>\n", $etp_msgq_i - end - if $etp_msgq_p != 0 - printf "#MsgQLong<%#lx%p>\n", (unsigned long)$etp_msgq_p - end - if $etp_msgq_prev != $etp_msgq->last - printf "#MsgQEndError<%#lx%p>\n", (unsigned long)$etp_msgq_prev - end -end - -document etp-msgq -%--------------------------------------------------------------------------- -% etp-msgq ErlMessageQueue* -% -% Take an ErlMessageQueue* and print the contents of the message queue. -% Sequential trace tokens are included in comments and -% the current match position in the queue is marked '<='. -% -% A process's message queue is process_tab[i]->msg. -%--------------------------------------------------------------------------- -end - - - -define etpf-msgq -# Args: Process* -# -# Non-reentrant -# - set $etp_flat = 1 - etp-msgq ($arg0) - set $etp_flat = 0 -end - -document etpf-msgq -%--------------------------------------------------------------------------- -% etpf-msgq ErlMessageQueue* -% -% Same as 'etp-msgq' but print the messages using etpf (flat). -%--------------------------------------------------------------------------- -end - - - -define etp-stacktrace -# Args: Process* -# -# Non-reentrant -# - set $etp_stacktrace_p = ($arg0)->stop - set $etp_stacktrace_end = ($arg0)->hend - printf "%% Stacktrace (%u): ", $etp_stacktrace_end-$etp_stacktrace_p - etp ($arg0)->cp - while $etp_stacktrace_p < $etp_stacktrace_end - if ($etp_stacktrace_p[0] & 0x3) == 0x0 - # Continuation pointer - etp $etp_stacktrace_p[0] - end - set $etp_stacktrace_p++ - end -end - -document etp-stacktrace -%--------------------------------------------------------------------------- -% etp-stacktrace Process* -% -% Take an Process* and print a stactrace for the process. -% The stacktrace consists just of the pushed code continuation -% pointers on the stack, the most recently pushed first. -%--------------------------------------------------------------------------- -end - -define etp-stackdump -# Args: Process* -# -# Non-reentrant -# - set $etp_stackdump_p = ($arg0)->stop - set $etp_stackdump_end = ($arg0)->hend - printf "%% Stackdump (%u): ", $etp_stackdump_end-$etp_stackdump_p - etp ($arg0)->cp - while $etp_stackdump_p < $etp_stackdump_end - etp $etp_stackdump_p[0] - set $etp_stackdump_p++ - end -end - -document etp-stackdump -%--------------------------------------------------------------------------- -% etp-stackdump Process* -% -% Take an Process* and print a stackdump for the process. -% The stackdump consists of all pushed values on the stack. -% All code continuation pointers are preceeded with a line -% of dashes to make the stack frames more visible. -%--------------------------------------------------------------------------- -end - -define etpf-stackdump -# Args: Process* -# -# Non-reentrant -# - set $etp_flat = 1 - etp-stackdump ($arg0) - set $etp_flat = 0 -end - -document etpf-stackdump -%--------------------------------------------------------------------------- -% etpf-stackdump Process* -% -% Same as etp-stackdump but print the values using etpf (flat). -%--------------------------------------------------------------------------- -end - -define etp-pid2pix-1 -# Args: Eterm -# - if (etp_arch_bits == 64 && etp_halfword == 0) - if (etp_big_endian) - set $etp_pix = (int) (((Uint64) $arg0) & 0x0fffffff) - else - set $etp_pix = (int) ((((Uint64) $arg0) >> 32) & 0x0fffffff) - end - else - set $etp_pix = (int) ((((Uint32) $arg0) >> 4) & erts_proc.r.o.pix_mask) - end -end - -define etp-pix2proc -# Args: Eterm -# - set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[((int) $arg0)]) - printf "(Process *) %p\n", $proc -end - -define etp-pid2proc-1 -# Args: Eterm -# - etp-pid2pix-1 $arg0 - set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$etp_pix]) -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->common.id - printf "\n State: " - etp-proc-state $arg0 - if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0 - if ($arg0->common.u.alive.reg) - printf " Registered name: " - etp-1 $arg0->common.u.alive.reg->name - printf "\n" - end - 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->msg_inq.len), $arg0->msg.len, $arg0->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.r.o.max - set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$proc_ix]) - if ($proc != ((Process *) 0) && $proc != &erts_invalid_process) - 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-id2pix-1 -# Args: Eterm -# - if (etp_arch_bits == 64 && etp_halfword == 0) - if (etp_big_endian) - set $etp_pix = (int) (((Uint64) $arg0) & 0x0fffffff) - elser - set $etp_pix = (int) ((((Uint64) $arg0) >> 32) & 0x0fffffff) - end - else - set $etp_pix = (int) ((((Uint32) $arg0) >> 4) & erts_port.r.o.pix_mask) - end -end - -define etp-pix2port -# Args: Eterm -# - set $port = (Port *) *((UWord *) &erts_port.r.o.tab[((int) $arg0)]) - printf "(Port *) %p\n", $port -end - -define etp-id2port-1 -# Args: Eterm -# - etp-port-id2pix-1 $arg0 - set $port = (Port *) *((UWord *) &erts_port.r.o.tab[((int) $etp_pix)]) -end - -define etp-id2port -# Args: Eterm -# - etp-id2port-1 $arg0 - printf "(Port *) %p\n", $port -end - -define etp-port-sched-flags-int -# Args: int -# - if ($arg0 & 0x1) - printf " in-run-queue" - end - if ($arg0 & 0x2) - printf " executing" - end - if ($arg0 & 0x4) - printf " have-tasks" - end - if ($arg0 & 0x8) - printf " exited" - end - if ($arg0 & 0x10) - printf " busy-port" - end - if ($arg0 & 0x20) - printf " busy-port-q" - end - if ($arg0 & 0x40) - printf " chk-unset-busy-port-q" - end - if ($arg0 & 0x80) - printf " have-busy-tasks" - end - if ($arg0 & 0x100) - printf " have-nosuspend-tasks" - end - if ($arg0 & 0x200) - printf " parallelism" - end - if ($arg0 & 0x400) - printf " force-sched" - end - if ($arg0 & 0xfffff800) - printf " GARBAGE" - end - printf "\n" -end - -document etp-port-sched-flags-int -%--------------------------------------------------------------------------- -% etp-proc-sched-flags-int int -% -% Print port sched-flags -%--------------------------------------------------------------------------- -end - - -define etp-port-sched-flags -# Args: Port* -# - set $sched_flags_int = *(((Uint32 *) &(((Port *) $arg0)->sched.flags))) - etp-port-sched-flags-int $sched_flags_int -end - -document etp-port-sched-flags -%--------------------------------------------------------------------------- -% etp-proc-sched-flags-int Port * -% -% Print port sched-flags -%--------------------------------------------------------------------------- -end - -define etp-port-state-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 " closing" - end - if ($arg0 & 0x40) - printf " send-closed" - end - if ($arg0 & 0x80) - printf " linebuf-io" - end - if ($arg0 & 0x100) - printf " free" - end - if ($arg0 & 0x200) - printf " initializing" - end - if ($arg0 & 0x400) - printf " port-specific-lock" - end - if ($arg0 & 0x800) - printf " invalid" - end - if ($arg0 & 0x1000) - printf " halt" - end - if (etp_debug_compiled) - if ($arg0 & 0x7fffe000) - printf " GARBAGE" - end - else - if ($arg0 & 0xffffe000) - printf " GARBAGE" - end - end - printf "\n" -end - -document etp-port-state-int -%--------------------------------------------------------------------------- -% etp-proc-state-int int -% -% Print port state -%--------------------------------------------------------------------------- -end - - -define etp-port-state -# Args: Port* -# - set $state_int = *(((Uint32 *) &(((Port *) $arg0)->state))) - etp-port-state-int $state_int -end - -document etp-port-state -%--------------------------------------------------------------------------- -% etp-proc-state-int Port * -% -% Print port state -%--------------------------------------------------------------------------- -end - -define etp-port-info -# Args: Port* -# - printf " Port: " - etp-1 $arg0->common.id - printf "\n Name: %s\n", $arg0->name - printf " State:" - etp-port-state $arg0 - printf " Scheduler flags:" - etp-port-sched-flags $arg0 - if (*(((Uint32 *) &(((Port *) $arg0)->state))) & 0x5C00) == 0 - if ($arg0->common.u.alive.reg) - printf " Registered name: " - etp-1 $arg0->common.u.alive.reg->name - printf "\n" - end - end - printf " Connected: " - set $connected = *(((Eterm *) &(((Port *) $arg0)->connected))) - etp-1 $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_port.r.o.max - set $port = (Port *) *((UWord *) &erts_port.r.o.tab[$port_ix]) - if ($port != ((Port *) 0) && $port != &erts_invalid_port) - if (*(((Uint32 *) &(((Port *) $port)->state))) & 0x100) == 0 - # I.e, not free - printf "---\n" - printf " Pix: %d\n", $port_ix - etp-port-info $port - end - 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 "Endianess: " - if (etp_big_endian) - printf "Big\n" - else - printf "Little\n" - end - printf "Word size: %d-bit\n", etp_arch_bits - printf "Halfword: " - if (etp_halfword) - printf "yes\n" - else - printf "no\n" - end - printf "HiPE support: " - if (etp_hipe) - printf "yes\n" - 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-compile-info - printf "--------------- Compile Information ---------------\n" - printf "CFLAGS: %s\n", erts_build_flags_CFLAGS - printf "LDFLAGS: %s\n", erts_build_flags_LDFLAGS - printf "Use etp-config-h-info to dump config.h\n" -end - -document etp-compile-info -%--------------------------------------------------------------------------- -% etp-compile-info -% -% Print information about how the system was compiled -%--------------------------------------------------------------------------- -end - -define etp-config-h-info - printf "%s", erts_build_flags_CONFIG_H -end - -document etp-config-h-info -%--------------------------------------------------------------------------- -% etp-config-h-info -% -% Dump the contents of config.h when the system was compiled -%--------------------------------------------------------------------------- -end - -define etp-dictdump -# Args: ProcDict* -# -# Non-reentrant -# - set $etp_dictdump = ($arg0) - if $etp_dictdump - set $etp_dictdump_n = \ - $etp_dictdump->homeSize + $etp_dictdump->splitPosition - set $etp_dictdump_i = 0 - set $etp_dictdump_written = 0 - if $etp_dictdump_n > $etp_dictdump->size - set $etp_dictdump_n = $etp_dictdump->size - end - set $etp_dictdump_cnt = $etp_dictdump->numElements - printf "%% Dictionary (%d):\n[", $etp_dictdump_cnt - while $etp_dictdump_i < $etp_dictdump_n && \ - $etp_dictdump_cnt > 0 - set $etp_dictdump_p = $etp_dictdump->data[$etp_dictdump_i] - if $etp_dictdump_p != $etp_nil - if ((Eterm)$etp_dictdump_p & 0x3) == 0x2 - # Boxed - if $etp_dictdump_written - printf ",\n " - else - set $etp_dictdump_written = 1 - end - etp-1 $etp_dictdump_p 0 - set $etp_dictdump_cnt-- - else - while ((Eterm)$etp_dictdump_p & 0x3) == 0x1 && \ - $etp_dictdump_cnt > 0 - # Cons ptr - if $etp_dictdump_written - printf ",\n " - else - set $etp_dictdump_written = 1 - end - etp-1 (((Eterm*)((Eterm)$etp_dictdump_p&~0x3))[0]) 0 - set $etp_dictdump_cnt-- - set $etp_dictdump_p = ((Eterm*)((Eterm)$etp_dictdump_p & ~0x3))[1] - end - if $etp_dictdump_p != $etp_nil - printf "#DictSlotError<%d>:", $etp_dictdump_i - set $etp_dictdump_flat = $etp_flat - set $etp_flat = 1 - etp-1 ((Eterm)$etp_dictdump_p) 0 - set $etp_flat = $etp_dictdump_flat - end - end - end - set $etp_dictdump_i++ - end - if $etp_dictdump_cnt != 0 - printf "#DictCntError<%d>, ", $etp_dictdump_cnt - end - else - printf "%% Dictionary (0):\n[" - end - printf "].\n" -end - -document etp-dictdump -%--------------------------------------------------------------------------- -% etp-dictdump ErlProcDict* -% -% Take an ErlProcDict* and print all entries in the process dictionary. -%--------------------------------------------------------------------------- -end - -define etpf-dictdump -# Args: ErlProcDict* -# -# Non-reentrant -# - set $etp_flat = 1 - etp-dictdump ($arg0) - set $etp_flat = 0 -end - -document etpf-dictdump -%--------------------------------------------------------------------------- -% etpf-dictdump ErlProcDict* -% -% Same as etp-dictdump but print the values using etpf (flat). -%--------------------------------------------------------------------------- -end - - - -define etp-offheapdump -# Args: ( ExternalThing* | ProcBin* | ErlFunThing* ) -# -# Non-reentrant -# - set $etp_offheapdump_p = ($arg0) - set $etp_offheapdump_i = 0 - set $etp_offheapdump_ - printf "%% Offheap dump:\n[" - while ($etp_offheapdump_p != 0) && ($etp_offheapdump_i < $etp_max_depth) - if ((Eterm)$etp_offheapdump_p & 0x3) == 0x0 - if $etp_offheapdump_i > 0 - printf ",\n " - end - etp-1 ((Eterm)$etp_offheapdump_p|0x2) 0 - set $etp_offheapdump_p = $etp_offheapdump_p->next - set $etp_offheapdump_i++ - else - printf "#TaggedPtr<%#x>", $etp_offheapdump_p - set $etp_offheapdump_p = 0 - end - end - printf "].\n" -end - -document etp-offheapdump -%--------------------------------------------------------------------------- -% etp-offheapdump ( ExternalThing* | ProcBin* | ErlFunThing* ) -% -% Take an pointer to a linked list and print the terms in the list -% up to the max depth. -%--------------------------------------------------------------------------- -end - -define etpf-offheapdump -# Args: ( ExternalThing* | ProcBin* | ErlFunThing* ) -# -# Non-reentrant -# - set $etp_flat = 1 - etp-offheapdump ($arg0) - set $etp_flat = 0 -end - -document etpf-offheapdump -%--------------------------------------------------------------------------- -% etpf-offheapdump ( ExternalThing* | ProcBin* | ErlFunThing* ) -% -% Same as etp-offheapdump but print the values using etpf (flat). -%--------------------------------------------------------------------------- -end - -define etp-search-heaps -# Args: Eterm -# -# Non-reentrant -# - printf "%% Search all (<%u) process heaps for ", erts_max_processes - set $etp_flat = 1 - etp-1 ($arg0) 0 - set $etp_flat = 0 - printf ":...\n" - etp-search-heaps-1 ((Eterm*)((Eterm)($arg0)&~3)) -end - -define etp-search-heaps-1 -# Args: Eterm* -# -# Non-reentrant -# - set $etp_search_heaps_q = erts_max_processes / 10 - set $etp_search_heaps_r = erts_max_processes % 10 - set $etp_search_heaps_t = 10 - set $etp_search_heaps_m = $etp_search_heaps_q - if $etp_search_heaps_r > 0 - set $etp_search_heaps_m++ - set $etp_search_heaps_r-- - end - set $etp_search_heaps_i = 0 - set $etp_search_heaps_found = 0 - while $etp_search_heaps_i < erts_proc.r.o.max - set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$proc_ix]) - if $proc - if ($proc->heap <= ($arg0)) && \ - (($arg0) < $proc->hend) - printf "process_tab[%d]->heap+%d\n", $etp_search_heaps_i, \ - ($arg0)-$proc->heap - end - if ($proc->old_heap <= ($arg0)) && \ - (($arg0) <= $proc->old_hend) - printf "process_tab[%d]->old_heap+%d\n", $etp_search_heaps_i, \ - ($arg0)-$proc->old_heap - end - set $etp_search_heaps_cnt = 0 - 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)) && \ - (($arg0) < &($etp_search_heaps_p->mem)+$etp_search_heaps_p->size) - printf "process_tab[%d]->mbuf(%d)+%d\n", \ - $etp_search_heaps_i, $etp_search_heaps_cnt, \ - ($arg0)-&($etp_search_heaps_p->mem) - end - set $etp_search_heaps_p = $etp_search_heaps_p->next - end - if $etp_search_heaps_p - printf "Process ix=%d %% Too many HeapFragments\n", \ - $etp_search_heaps_i - end - end - set $etp_search_heaps_i++ - if $etp_search_heaps_i > $etp_search_heaps_m - printf "%% %d%%...\n", $etp_search_heaps_t - set $etp_search_heaps_t += 10 - set $etp_search_heaps_m += $etp_search_heaps_q - if $etp_search_heaps_r > 0 - set $etp_search_heaps_m++ - set $etp_search_heaps_r-- - end - end - end - printf "%% 100%%.\n" -end - -document etp-search-heaps -%--------------------------------------------------------------------------- -% etp-search-heaps Eterm -% -% Search all process heaps in process_tab[], including the heap fragments -% (process_tab[]->mbuf) for the specified Eterm. -%--------------------------------------------------------------------------- -end - - - -define etp-search-alloc -# Args: Eterm -# -# Non-reentrant -# - printf "%% Search allocated memory blocks for " - set $etp_flat = 1 - etp-1 ($arg0) 0 - set $etp_flat = 0 - printf ":...\n" - set $etp_search_alloc_n = sizeof(erts_allctrs) / sizeof(*erts_allctrs) - set $etp_search_alloc_i = 0 - while $etp_search_alloc_i < $etp_search_alloc_n - if erts_allctrs[$etp_search_alloc_i].alloc - set $etp_search_alloc_f = (erts_allctrs+$etp_search_alloc_i) - while ($etp_search_alloc_f->alloc == debug_alloc) || \ - ($etp_search_alloc_f->alloc == stat_alloc) || \ - ($etp_search_alloc_f->alloc == map_stat_alloc) - set $etp_search_alloc_f = \ - (ErtsAllocatorFunctions_t*)$etp_search_alloc_f->extra - end - if ($etp_search_alloc_f->alloc != erts_sys_alloc) && \ - ($etp_search_alloc_f->alloc != erts_fix_alloc) - if ($etp_search_alloc_f->alloc == erts_alcu_alloc) || \ - ($etp_search_alloc_f->alloc == erts_alcu_alloc_ts) - # alcu alloc - set $etp_search_alloc_e = (Allctr_t*)$etp_search_alloc_f->extra - # mbc_list - set $etp_search_alloc_p = $etp_search_alloc_e->mbc_list.first - set $etp_search_alloc_cnt = 0 - while $etp_search_alloc_p && \ - ($etp_search_alloc_cnt < $etp_max_depth) - set $etp_search_alloc_cnt++ - if $etp_search_alloc_p <= ($arg0) && \ - ($arg0) < (char*)$etp_search_alloc_p + \ - ($etp_search_alloc_p->chdr & (Uint)~7) - printf "erts_allctrs[%d] %% %salloc: mbc_list: %d\n", \ - $etp_search_alloc_i, $etp_search_alloc_e->name_prefix, \ - $etp_search_alloc_cnt - end - if $etp_search_alloc_p == $etp_search_alloc_e->mbc_list.last - if $etp_search_alloc_p->next - printf \ - "erts_allctrs[%d] %% %salloc: mbc_list.last error %p\n",\ - $etp_search_alloc_i, $etp_search_alloc_e->name_prefix,\ - $etp_search_alloc_p - end - set $etp_search_alloc_p = 0 - else - set $etp_search_alloc_p = $etp_search_alloc_p->next - end - end - if $etp_search_alloc_p - printf "erts_allctrs[%d] %% %salloc: too large mbc_list %p\n", \ - $ept_search_alloc_i, $etp_search_alloc_e->name_prefix, - $ept_search_alloc_p - end - # sbc_list - set $etp_search_alloc_p = $etp_search_alloc_e->sbc_list.first - set $etp_search_alloc_cnt = 0 - while $etp_search_alloc_p && \ - ($etp_search_alloc_cnt < $etp_max_depth) - set $etp_search_alloc_cnt++ - if $etp_search_alloc_p <= ($arg0) && \ - ($arg0) < (char*)$etp_search_alloc_p + \ - ($etp_search_alloc_p->chdr & (Uint)~7) - printf "erts_allctrs[%d] %% %salloc: sbc_list: %d\n", \ - $etp_search_alloc_i, $etp_search_alloc_e->name_prefix, \ - $etp_search_alloc_cnt - end - if $etp_search_alloc_p == $etp_search_alloc_e->sbc_list.last - if $etp_search_alloc_p->next - printf \ - "erts_allctrs[%d] %% %salloc: sbc_list.last error %p",\ - $etp_search_alloc_i, $etp_search_alloc_e->name_prefix,\ - $etp_search_alloc_p - end - set $etp_search_alloc_p = 0 - else - set $etp_search_alloc_p = $etp_search_alloc_p->next - end - end - if $etp_search_alloc_p - printf "erts_allctrs[%d] %% %salloc: too large sbc_list %p\n", \ - $ept_search_alloc_i, $etp_search_alloc_e->name_prefix, - $ept_search_alloc_p - end - else - printf "erts_allctrs[%d] %% %s: unknown allocator\n", \ - $etp_search_alloc_i, erts_alc_a2ad[$etp_search_alloc_i] - end - end - end - set $etp_search_alloc_i++ - end -end - -document etp-search-alloc -%--------------------------------------------------------------------------- -% etp-search-heaps Eterm -% -% Search all internal allocator memory blocks for for the specified Eterm. -%--------------------------------------------------------------------------- -end - - - -define etp-overlapped-heaps -# Args: -# -# Non-reentrant -# - printf "%% Dumping heap addresses to \"etp-commands.bin\"\n" - set $etp_overlapped_heaps_q = erts_max_processes / 10 - set $etp_overlapped_heaps_r = erts_max_processes % 10 - set $etp_overlapped_heaps_t = 10 - set $etp_overlapped_heaps_m = $etp_overlapped_heaps_q - if $etp_overlapped_heaps_r > 0 - set $etp_overlapped_heaps_m++ - set $etp_overlapped_heaps_r-- - end - set $etp_overlapped_heaps_i = 0 - set $etp_overlapped_heaps_found = 0 - dump binary value etp-commands.bin 'o' - append binary value etp-commands.bin 'v' - append binary value etp-commands.bin 'e' - append binary value etp-commands.bin 'r' - append binary value etp-commands.bin 'l' - append binary value etp-commands.bin 'a' - append binary value etp-commands.bin 'p' - append binary value etp-commands.bin 'p' - append binary value etp-commands.bin 'e' - append binary value etp-commands.bin 'd' - append binary value etp-commands.bin '-' - append binary value etp-commands.bin 'h' - append binary value etp-commands.bin 'e' - append binary value etp-commands.bin 'a' - append binary value etp-commands.bin 'p' - append binary value etp-commands.bin 's' - append binary value etp-commands.bin '\0' - while $etp_overlapped_heaps_i < erts_max_processes - if process_tab[$etp_overlapped_heaps_i] - append binary value etp-commands.bin \ - (Eterm)$etp_overlapped_heaps_i - append binary value etp-commands.bin \ - (Eterm)process_tab[$etp_overlapped_heaps_i]->heap - append binary value etp-commands.bin \ - (Eterm)process_tab[$etp_overlapped_heaps_i]->hend - append binary value etp-commands.bin \ - (Eterm)process_tab[$etp_overlapped_heaps_i]->old_heap - append binary value etp-commands.bin \ - (Eterm)process_tab[$etp_overlapped_heaps_i]->old_hend - set $etp_overlapped_heaps_p = process_tab[$etp_overlapped_heaps_i]->mbuf - set $etp_overlapped_heaps_cnt = 0 - while $etp_overlapped_heaps_p && \ - ($etp_overlapped_heaps_cnt < $etp_max_depth) - set $etp_overlapped_heaps_cnt++ - append binary value etp-commands.bin \ - (Eterm)$etp_overlapped_heaps_p - append binary value etp-commands.bin \ -(Eterm)(&($etp_overlapped_heaps_p->mem)+$etp_overlapped_heaps_p->size) - set $etp_overlapped_heaps_p = $etp_overlapped_heaps_p->next - end - if $etp_overlapped_heaps_p - printf "process_tab[%d] %% Too many HeapFragments\n", \ - $etp_overlapped_heaps_i - end - append binary value etp-commands.bin (Eterm)0x0 - append binary value etp-commands.bin (Eterm)0x0 - end - set $etp_overlapped_heaps_i++ - if $etp_overlapped_heaps_i > $etp_overlapped_heaps_m - printf "%% %d%%...\n", $etp_overlapped_heaps_t - set $etp_overlapped_heaps_t += 10 - set $etp_overlapped_heaps_m += $etp_overlapped_heaps_q - if $etp_overlapped_heaps_r > 0 - set $etp_overlapped_heaps_m++ - set $etp_overlapped_heaps_r-- - end - end - end - etp-run -end - -document etp-overlapped-heaps -%--------------------------------------------------------------------------- -% etp-overlapped-heaps -% -% Dump all process heap addresses in process_tab[], including -% the heap fragments in binary format on the file etp-commands.bin. -% Then call etp_commands:file/1 to analyze if any heaps overlap. -% -% Requires 'erl' in the path and 'etp_commands.beam' in 'erl's search path. -%--------------------------------------------------------------------------- -end - - - -define etp-chart -# Args: Process* -# -# Non-reentrant - etp-chart-start ($arg0) - set ($arg0) = ($arg0) - etp-msgq (($arg0)->msg) - etp-stackdump ($arg0) - etp-dictdump (($arg0)->dictionary) - etp-dictdump (($arg0)->debug_dictionary) - printf "%% Dumping other process data...\n" - etp ($arg0)->seq_trace_token - etp ($arg0)->fvalue - printf "%% Dumping done.\n" - etp-chart-print -end - -document etp-chart -%--------------------------------------------------------------------------- -% etp-chart Process* -% -% Dump all process data to the file "etp-commands.bin" and then use -% the Erlang support module to print a memory chart of all terms. -%--------------------------------------------------------------------------- -end - - - -define etp-chart-start -# Args: Process* -# -# Non-reentrant - set $etp_chart = 1 - set $etp_chart_id = 0 - set $etp_chart_start_p = ($arg0) - dump binary value etp-commands.bin 'c' - append binary value etp-commands.bin 'h' - append binary value etp-commands.bin 'a' - append binary value etp-commands.bin 'r' - append binary value etp-commands.bin 't' - append binary value etp-commands.bin '\0' - append binary value etp-commands.bin (Eterm)($etp_chart_start_p->heap) - append binary value etp-commands.bin (Eterm)($etp_chart_start_p->high_water) - append binary value etp-commands.bin (Eterm)($etp_chart_start_p->hend) - append binary value etp-commands.bin (Eterm)($etp_chart_start_p->old_heap) - append binary value etp-commands.bin (Eterm)($etp_chart_start_p->old_hend) - set $etp_chart_start_cnt = 0 - set $etp_chart_start_p = $etp_chart_start_p->mbuf - while $etp_chart_start_p && ($etp_chart_start_cnt < $etp_max_depth) - set $etp_chart_start_cnt++ - append binary value etp-commands.bin (Eterm)($etp_chart_start_p->mem) - append binary value etp-commands.bin (Eterm)($etp_chart_start_p->size) - set $etp_chart_start_p = $etp_chart_start_p->next - end - append binary value etp-commands.bin (Eterm)(0) - append binary value etp-commands.bin (Eterm)(0) - if $etp_chart_start_p - printf "%% Too many HeapFragments\n" - end -end - -document etp-chart-start -%--------------------------------------------------------------------------- -% etp-chart-start Process* -% -% Dump a chart head to the file "etp-commands.bin". -%--------------------------------------------------------------------------- -end - - - -define etp-chart-entry-1 -# Args: Eterm, int depth, int words -# -# Reentrant capable - if ($arg1) == 0 - set $etp_chart_id++ - printf "#%d:", $etp_chart_id - end - append binary value etp-commands.bin ($arg0)&~0x3 - append binary value etp-commands.bin (Eterm)(($arg2)*sizeof(Eterm)) - append binary value etp-commands.bin (Eterm)$etp_chart_id - append binary value etp-commands.bin (Eterm)($arg1) -# printf "", ($arg0)&~0x3, \ -# (Eterm)(($arg2)*sizeof(Eterm)), (Eterm)$etp_chart_id, (Eterm)($arg1) -end - - - -define etp-chart-print - set $etp_chart = 0 - etp-run -end - -document etp-chart-print -%--------------------------------------------------------------------------- -% etp-chart-print Process* -% -% Print a memory chart of the dumped data in "etp-commands.bin", and stop -% chart recording. -%--------------------------------------------------------------------------- -end - -############################################################################ -# ETS table debug -# - -define etp-ets-tables -# Args: -# -# Non-reentrant - printf "%% Dumping < %lu ETS tables\n", (unsigned long)db_max_tabs - while $etp_ets_tables_i < db_max_tabs - if (meta_main_tab[$etp_ets_tables_i].u.next_free & 3) == 0 - printf "%% %d:", $etp_ets_tables_i - etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.id)) 0 - printf " " - etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.owner)) 0 - printf "\n" - end - set $etp_ets_tables_i++ - end - set $etp_ets_tables_i = 0 -end - -document etp-ets-tables -%--------------------------------------------------------------------------- -% etp-ets-tables -% -% Dump all ETS table names and their indexies. -%--------------------------------------------------------------------------- -end - -define etp-ets-obj -# Args: DbTerm* -# - set $etp_ets_obj_i = 1 - while $etp_ets_obj_i <= (($arg0)->tpl[0] >> 6) - if $etp_ets_obj_i == 1 - printf "{" - else - printf ", " - end - set $etp_ets_elem = ($arg0)->tpl[$etp_ets_obj_i] - if ($etp_ets_elem & 3) == 0 - printf "" - else - etp-1 $etp_ets_elem 0 - end - set $etp_ets_obj_i++ - end - printf "}" -end - - -define etp-ets-tabledump -# Args: int tableindex -# -# Non-reentrant - printf "%% Dumping ETS table %d:", ($arg0) - set $etp_ets_tabledump_n = 0 - set $etp_ets_tabledump_t = meta_main_tab[($arg0)].u.tb - set $etp_ets_tabledump_i = 0 - etp-1 ($etp_ets_tabledump_t->common.the_name) 0 - printf " status=%#x\n", $etp_ets_tabledump_t->common.status - if $etp_ets_tabledump_t->common.status & 0x130 - # Hash table - set $etp_ets_tabledump_h = $etp_ets_tabledump_t->hash - printf "%% nitems=%d\n", (long) $etp_ets_tabledump_t->common.nitems - while $etp_ets_tabledump_i < (long) $etp_ets_tabledump_h->nactive - set $etp_ets_tabledump_seg = ((struct segment**)$etp_ets_tabledump_h->segtab)[$etp_ets_tabledump_i>>8] - set $etp_ets_tabledump_l = $etp_ets_tabledump_seg->buckets[$etp_ets_tabledump_i&0xFF] - if $etp_ets_tabledump_l - printf "%% Slot %d:\n", $etp_ets_tabledump_i - while $etp_ets_tabledump_l - if $etp_ets_tabledump_n - printf "," - else - printf "[" - end - set $etp_ets_tabledump_n++ - etp-ets-obj &($etp_ets_tabledump_l->dbterm) - if $etp_ets_tabledump_l->hvalue == ((unsigned long)-1) - printf "% *\n" - else - printf "\n" - end - set $etp_ets_tabledump_l = $etp_ets_tabledump_l->next - if $etp_ets_tabledump_n >= $etp_max_depth - set $etp_ets_tabledump_l = 0 - end - end - end - set $etp_ets_tabledump_i++ - end - if $etp_ets_tabledump_n - printf "].\n" - end - else - printf "%% Not a hash table\n" - end -end - -document etp-ets-tabledump -%--------------------------------------------------------------------------- -% etp-ets-tabledump Slot -% -% Dump an ETS table with a specified slot index. -%--------------------------------------------------------------------------- -end - -############################################################################ -# Erlang support module handling -# - -define etp-run - shell make -f "${ROOTDIR:?}/erts/etc/unix/etp_commands.mk" \ - ROOTDIR="${ROOTDIR:?}" ETP_DATA="etp-commands.bin" -end - -document etp-run -%--------------------------------------------------------------------------- -% etp-run -% -% Make and run the Erlang support module on the input file -% "erl-commands.bin". The environment variable ROOTDIR must -% be set to find $ROOTDIR/erts/etc/unix/etp_commands.mk. -% -% Also, erl and erlc must be in the path. -%--------------------------------------------------------------------------- -end - -############################################################################ -# Toolbox parameter handling -# - -define etp-set-max-depth - if ($arg0) > 0 - set $etp_max_depth = ($arg0) - else - echo %%%Error: max-depth <= 0 %%%\n - end -end - -document etp-set-max-depth -%--------------------------------------------------------------------------- -% etp-set-max-depth Depth -% -% Set the max term depth to use for etp. The term dept limit -% works in both depth and width, so if you set the max depth to 10, -% an 11 element flat tuple will be truncated. -%--------------------------------------------------------------------------- -end - -define etp-set-max-string-length - if ($arg0) > 0 - set $etp_max_string_length = ($arg0) - else - echo %%%Error: max-string-length <= 0 %%%\n - end -end - -document etp-set-max-string-length -%--------------------------------------------------------------------------- -% etp-set-max-strint-length Length -% -% Set the max string length to use for ept when printing lists -% that can be shown as printable strings. Printable strings -% that are longer will be truncated, and not even checked if -% they really are printable all the way to the end. -%--------------------------------------------------------------------------- -end - -define etp-show - printf "etp-set-max-depth %d\n", $etp_max_depth - printf "etp-set-max-string-length %d\n", $etp_max_string_length -end - -document etp-show -%--------------------------------------------------------------------------- -% etp-show -% -% Show the commands needed to set all etp parameters -% to their current value. -%--------------------------------------------------------------------------- -end - -############################################################################ -# Init -# - -define etp-init - set $etp_arch64 = (sizeof(void *) == 8) - if $etp_arch64 - set $etp_nil = 0xfffffffffffffffb - else - set $etp_nil = 0xfffffffb - end - set $etp_flat = 0 - set $etp_chart_id = 0 - set $etp_chart = 0 - - set $etp_max_depth = 20 - set $etp_max_string_length = 100 - - set $etp_ets_tables_i = 0 -end - -document etp-init -%--------------------------------------------------------------------------- -% Use etp-help for a command overview and general help. -% -% To use the Erlang support module, the environment variable ROOTDIR -% must be set to the toplevel installation directory of Erlang/OTP, -% so the etp-commands file becomes: -% $ROOTDIR/erts/etc/unix/etp-commands -% Also, erl and erlc must be in the path. -%--------------------------------------------------------------------------- -end - -etp-init -help etp-init -etp-show -etp-system-info diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in new file mode 100644 index 0000000000..54ff7b3e3a --- /dev/null +++ b/erts/etc/unix/etp-commands.in @@ -0,0 +1,2855 @@ +# +# %CopyrightBegin% +# +# 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 +# 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% +# + +############################################################################ +# Help commands +# + +define etp-help + help etp-help +end + +document etp-help +%--------------------------------------------------------------------------- +% etp-help +% +% Same as "help etp-help" +% +% Emulator Toolbox for Pathologists +% - GDB command toolbox for analyzing core dumps from the +% Erlang emulator (BEAM). +% +% Should work for 32-bit erts-5.2/R9B, ... +% +% The commands are prefixed with: +% etp: Acronym for erts-term-print +% etpf: Acronym for erts-term-print-flat +% +% User commands (these have help themselves): +% +% Most useful: +% etp, etpf +% +% Useful for doing step-by-step traversal of lists and tuples after +% calling the toplevel command etpf: +% etpf-cons, etpf-boxed, +% +% Special commands for not really terms: +% etp-mfa, etp-cp, +% etp-msgq, etpf-msgq, +% etp-stacktrace, etp-stackdump, etpf-stackdump, etp-dictdump +% etp-offheapdump, etpf-offheapdump, +% etp-print-procs, etp-search-heaps, etp-search-alloc, +% etp-ets-tables, etp-ets-tabledump +% +% Complex commands that use the Erlang support module. +% etp-overlapped-heaps, etp-chart, etp-chart-start, etp-chart-end +% +% Erlang support module handling commands: +% etp-run +% +% Parameter handling commands: +% etp-show, etp-set-max-depth, etp-set-max-string-length +% +% Other commands you may find in this toolbox are suffixed -1, -2, ... +% and are internal; not for the console user. +% +% The Erlang support module requires `erl' and `erlc' in the path. +% The compiled "erl_commands.beam" file is stored in the current +% working directory, so it is thereby in the search path of `erl'. +% +% These are just helpful commands when analyzing core dumps, but +% you will not get away without knowing the gory details of the +% tag bits. Do not forget about the e.g p, p/x, x and x/4x commands. +% +% Execution speed of user defined gdb commands is not lightning fast. +% It may well take half a minute to dump a complex term with the default +% max depth values on our old Sparc Ultra-10's. +% +% To use the Erlang support module, the environment variable ROOTDIR +% must be set to the toplevel installation directory of Erlang/OTP, +% so the etp-commands file becomes: +% $ROOTDIR/erts/etc/unix/etp-commands +% Also, erl and erlc must be in the path. +%--------------------------------------------------------------------------- +end + +############################################################################ +# Toplevel commands +# + +define etp +# Args: Eterm +# +# Reentrant +# + etp-1 ((Eterm)($arg0)) 0 + printf ".\n" +end + +document etp +%--------------------------------------------------------------------------- +% etp Eterm +% +% Takes a toplevel Erlang term and prints the whole deep term +% very much as in Erlang itself. Up to a max depth. See etp-show. +%--------------------------------------------------------------------------- +end + +define etp-1 +# Args: Eterm, int depth +# +# Reentrant +# + if (($arg0) & 0x3) == 1 + # Cons pointer + if $etp_flat + printf "", ($arg0) + else + etp-list-1 ($arg0) ($arg1) + end + else + if (($arg0) & 0x3) == 2 + if $etp_flat + printf "", ($arg0) + else + etp-boxed-1 ($arg0) ($arg1) + end + else + if (($arg0) & 0x3) == 3 + etp-immediate-1 ($arg0) + else + # (($arg0) & 0x3) == 0 + if (($arg0) == 0x0) + printf "" + else + if (($arg0) == 0x4) + printf "" + else + etp-cp-1 ($arg0) + end + end + end + end + end +end + +define etpf +# Args: Eterm +# +# Non-reentrant + set $etp_flat = 1 + etp-1 ((Eterm)($arg0)) + set $etp_flat = 0 + printf ".\n" +end + +document etpf +%--------------------------------------------------------------------------- +% etpf Eterm +% +% Takes a toplevel Erlang term and prints it is. If it is a deep term +% print which command to use to traverse down one level. +%--------------------------------------------------------------------------- +end + +############################################################################ +# Commands for nested terms. Some are recursive. +# + +define etp-list-1 +# Args: Eterm cons_cell, int depth +# +# Reentrant +# + if (($arg0) & 0x3) != 0x1 + printf "#NotCons<%#x>", ($arg0) + else + # Cons pointer + if $etp_chart + etp-chart-entry-1 ($arg0) ($arg1) 2 + end + etp-list-printable-1 ($arg0) ($arg1) + if !$etp_list_printable + # Print normal list + printf "[" + etp-list-2 ($arg0) (($arg1)+1) + end + end +end + +define etp-list-printable-1 +# Args: Eterm list, int depth +# +# Non-reentrant +# +# Returns: $etp_list_printable +# + if (($arg0) & 0x3) != 0x1 + printf "#NotCons<%#x>", ($arg0) + else + # Loop to check if it is a printable string + set $etp_list_p = ($arg0) + set $etp_list_printable = ($etp_list_p != $etp_nil) + set $etp_list_i = 0 + while ($etp_list_p != $etp_nil) && \ + ($etp_list_i < $etp_max_string_length) && \ + $etp_list_printable + if ($etp_list_p & 0x3) == 0x1 + # Cons pointer + set $etp_list_n = ((Eterm*)($etp_list_p & ~0x3))[0] + if ($etp_list_n & 0xF) == 0xF + etp-ct-printable-1 ($etp_list_n>>4) + if $etp_ct_printable + # Printable + set $etp_list_p = ((Eterm*)($etp_list_p & ~0x3))[1] + set $etp_list_i++ + else + set $etp_list_printable = 0 + end + else + set $etp_list_printable = 0 + end + else + set $etp_list_printable = 0 + end + end + # + if $etp_list_printable + # Print printable string + printf "\"" + set $etp_list_p = ($arg0) + set $etp_list_i = 0 + while $etp_list_p != $etp_nil + set $etp_list_n = ((Eterm*)($etp_list_p & ~0x3))[0] + etp-char-1 ($etp_list_n>>4) '"' + set $etp_list_p = ((Eterm*)($etp_list_p & ~0x3))[1] + set $etp_list_i++ + if $etp_list_p == $etp_nil + printf "\"" + else + if $etp_list_i >= $etp_max_string_length + set $etp_list_p = $etp_nil + printf "\"++[...]" + else + if $etp_chart + etp-chart-entry-1 ($arg0) (($arg1)+$etp_list_i) 2 + end + end + end + end + end + end +end + +define etp-list-2 +# Args: Eterm cons_cell, int depth +# +# Reentrant +# + if (($arg0) & 0x3) != 0x1 + printf "#NotCons<%#x>", ($arg0) + else + # Cons pointer + if ($arg1) >= $etp_max_depth + printf "...]" + else + etp-1 (((Eterm*)(($arg0)&~0x3))[0]) (($arg1)+1) + if ((Eterm*)(($arg0) & ~0x3))[1] == $etp_nil + # Tail is [] + printf "]" + else + if $etp_chart + etp-chart-entry-1 ($arg0) ($arg1) 2 + end + if (((Eterm*)(($arg0)&~0x3))[1]&0x3) == 0x1 + # Tail is cons cell + printf "," + etp-list-2 (((Eterm*)(($arg0)&~0x3))[1]) (($arg1)+1) + else + # Tail is other term + printf "|" + etp-1 (((Eterm*)(($arg0)&~0x3))[1]) (($arg1)+1) + printf "]" + end + end + end + end +end + +define etpf-cons +# Args: Eterm +# +# Reentrant capable +# + if ((Eterm)($arg0) & 0x3) != 0x1 + printf "#NotCons<%#x>", ($arg0) + else + # Cons pointer + set $etp_flat = 1 + printf "[" + etp-1 (((Eterm*)((Eterm)($arg0)&~0x3))[0]) + printf "|" + etp-1 (((Eterm*)((Eterm)($arg0)&~0x3))[1]) + printf "]\n" + set $etp_flat = 0 + end +end + +document etpf-cons +%--------------------------------------------------------------------------- +% etpf-cons Eterm +% +% Takes a Cons ptr and prints the Car and Cdr cells with etpf (flat). +%--------------------------------------------------------------------------- +end + + + +define etp-boxed-1 +# Args: Eterm, int depth +# +# Reentrant +# + if (($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", ($arg0) + else + if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 + if $etp_chart + etp-chart-entry-1 (($arg0)&~0x3) ($arg1) 1 + end + printf "#BoxedError<%#x>", ($arg0) + else + if $etp_chart + etp-chart-entry-1 (($arg0)&~0x3) ($arg1) \ + ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1) + end + if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3f) == 0x0 + printf "{" + etp-array-1 ((Eterm*)(($arg0)&~0x3)) ($arg1) ($arg1) \ + 1 ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1) '}' + else + etp-boxed-immediate-1 ($arg0) + end + end + end +end + +define etp-boxed-immediate-1 +# Args: Eterm, int depth +# +# Non-reentrant +# + if (($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", ($arg0) + else + if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 + printf "#BoxedError<%#x>", ($arg0) + else + set $etp_boxed_immediate_p = (Eterm*)(($arg0) & ~0x3) + set $etp_boxed_immediate_h = ($etp_boxed_immediate_p[0] >> 2) & 0xF + if $etp_boxed_immediate_h == 0xC + etp-extpid-1 ($arg0) + else + if $etp_boxed_immediate_h == 0xD + etp-extport-1 ($arg0) + else + if ($etp_boxed_immediate_h == 0x2) || \ + ($etp_boxed_immediate_h == 0x3) + etp-bignum-1 ($arg0) + else + if ($etp_boxed_immediate_h == 0x6) + etp-float-1 ($arg0) + else + if ($etp_boxed_immediate_h == 0x4) + etp-ref-1 ($arg0) + else + if ($etp_boxed_immediate_h == 0xE) + etp-extref-1 ($arg0) + else + # Hexdump the rest + if ($etp_boxed_immediate_h == 0x5) + printf "#Fun<" + else + if ($etp_boxed_immediate_h == 0x8) + printf "#RefcBinary<" + else + if ($etp_boxed_immediate_h == 0x9) + printf "#HeapBinary<" + else + if ($etp_boxed_immediate_h == 0xA) + printf "#SubBinary<" + else + printf "#Header%X<", $etp_boxed_immediate_h + end + end + end + end + set $etp_boxed_immediate_arity = $etp_boxed_immediate_p[0]>>6 + while $etp_boxed_immediate_arity > 0 + set $etp_boxed_immediate_p++ + if $etp_boxed_immediate_arity > 1 + printf "%#x,", *$etp_boxed_immediate_p + else + printf "%#x", *$etp_boxed_immediate_p + if ($etp_boxed_immediate_h == 0xA) + set $etp_boxed_immediate_p++ + printf ":%#x", *$etp_boxed_immediate_p + end + printf ">" + end + set $etp_boxed_immediate_arity-- + end + # End of hexdump + end + end + end + end + end + end + end + end +end + +define etpf-boxed +# Args: Eterm +# +# Non-reentrant +# + set $etp_flat = 1 + etp-boxed-1 ((Eterm)($arg0)) 0 + set $etp_flat = 0 + printf ".\n" +end + +document etpf-boxed +%--------------------------------------------------------------------------- +% etpf-boxed Eterm +% +% Take a Boxed ptr and print the contents in one level using etpf (flat). +%--------------------------------------------------------------------------- +end + + + +define etp-array-1 +# Args: Eterm* p, int depth, int width, int pos, int size, int end_char +# +# Reentrant +# + if ($arg3) < ($arg4) + if (($arg1) < $etp_max_depth) && (($arg2) < $etp_max_depth) + etp-1 (($arg0)[($arg3)]) (($arg1)+1) + if (($arg3) + 1) != ($arg4) + printf "," + end + etp-array-1 ($arg0) ($arg1) (($arg2)+1) (($arg3)+1) ($arg4) ($arg5) + else + printf "...%c", ($arg5) + end + else + printf "%c", ($arg5) + end +end + + + +#define etpa-1 +## Args: Eterm, int depth, int index, int arity +## +## Reentrant +## +# if ($arg1) >= $etp_max_depth+$etp_max_string_length +# printf "%% Max depth for term %d\n", $etp_chart_id +# else +# if ($arg2) < ($arg3) +# etp-1 (((Eterm*)(($arg0)&~0x3))[$arg2]) (($arg1)+1) +# etpa-1 ($arg0) (($arg1)+1) (($arg2)+1) ($arg3) +# end +# end +#end + +############################################################################ +# Commands for non-nested terms. Recursion leaves. Some call other leaves. +# + +define etp-immediate-1 +# Args: Eterm +# +# Reentrant capable +# + if (($arg0) & 0x3) != 0x3 + printf "#NotImmediate<%#x>", ($arg0) + else + if (($arg0) & 0xF) == 0x3 + etp-pid-1 ($arg0) + else + if (($arg0) & 0xF) == 0x7 + etp-port-1 ($arg0) + else + if (($arg0) & 0xF) == 0xf + # Fixnum + printf "%ld", (long)((Sint)($arg0)>>4) + else + # Immediate2 - 0xB + if (($arg0) & 0x3f) == 0x0b + etp-atom-1 ($arg0) + else + if (($arg0) & 0x3f) == 0x1b + printf "#Catch<%d>", ($arg0)>>6 + else + if (($arg0) == $etp_nil) + printf "[]" + else + printf "#UnknownImmediate<%#x>", ($arg0) + end + end + end + end + end + end + end +end + + + +define etp-atom-1 +# Args: Eterm atom +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3f) != 0xb + printf "#NotAtom<%#x>", ($arg0) + else + set $etp_atom_1_ap = (Atom*)erts_atom_table.seg_table[(Eterm)($arg0)>>16][((Eterm)($arg0)>>6)&0x3FF] + set $etp_atom_1_i = ($etp_atom_1_ap)->len + set $etp_atom_1_p = ($etp_atom_1_ap)->name + set $etp_atom_1_quote = 1 + # Check if atom has to be quoted + if ($etp_atom_1_i > 0) + etp-ct-atom-1 (*$etp_atom_1_p) + if $etp_ct_atom + # Atom start character + set $etp_atom_1_p++ + set $etp_atom_1_i-- + set $etp_atom_1_quote = 0 + else + set $etp_atom_1_i = 0 + end + end + while $etp_atom_1_i > 0 + etp-ct-name-1 (*$etp_atom_1_p) + if $etp_ct_name + # Name character + set $etp_atom_1_p++ + set $etp_atom_1_i-- + else + set $etp_atom_1_quote = 1 + set $etp_atom_1_i = 0 + end + end + # Print the atom + if $etp_atom_1_quote + printf "'" + end + set $etp_atom_1_i = ($etp_atom_1_ap)->len + set $etp_atom_1_p = ($etp_atom_1_ap)->name + while $etp_atom_1_i > 0 + etp-char-1 (*$etp_atom_1_p) '\'' + set $etp_atom_1_p++ + set $etp_atom_1_i-- + end + if $etp_atom_1_quote + printf "'" + end + end +end + + + +define etp-char-1 +# Args: int char, int quote_char +# +# Non-reentrant +# + if (($arg0) < 0) || (0377 < ($arg0)) + printf "#NotChar<%#x>", ($arg0) + else + if ($arg0) == ($arg1) + printf "\\%c", ($arg0) + else + etp-ct-printable-1 ($arg0) + if $etp_ct_printable + if $etp_ct_printable < 0 + printf "%c", ($arg0) + else + printf "\\%c", $etp_ct_printable + end + else + printf "\\%03o", ($arg0) + end + end + end +end + +define etp-ct-printable-1 +# Args: int +# +# Determines if integer is a printable character +# +# Non-reentrant +# Returns: $etp_ct_printable +# escape alias char, or -1 if no escape alias + if ($arg0) == 010 + set $etp_ct_printable = 'b' + else + if ($arg0) == 011 + set $etp_ct_printable = 't' + else + if ($arg0) == 012 + set $etp_ct_printable = 'n' + else + if ($arg0) == 013 + set $etp_ct_printable = 'v' + else + if ($arg0) == 014 + set $etp_ct_printable = 'f' + else + if ($arg0) == 033 + set $etp_ct_printable = 'e' + else + if ((040 <= ($arg0)) && (($arg0) <= 0176)) || \ + ((0240 <= ($arg0)) && (($arg0) <= 0377)) + # Other printable character + set $etp_ct_printable = -1 + else + set $etp_ct_printable = 0 + end + end + end + end + end + end + end +end + +define etp-ct-atom-1 +# Args: int +# +# Determines if integer is a atom first character +# +# Non-reentrant +# Returns: $etp_ct_atom + if ((0141 <= ($arg0)) && (($arg0) <= 0172)) || \ + ((0337 <= ($arg0)) && (($arg0) != 0367) && (($arg0) <= 0377)) + # Atom start character + set $etp_ct_atom = 1 + else + set $etp_ct_atom = 0 + end +end + +define etp-ct-variable-1 +# Args: int +# +# Determines if integer is a variable first character +# +# Non-reentrant +# Returns: $etp_ct_variable + if ((056 == ($arg0)) || \ + (0101 <= ($arg0)) && (($arg0) <= 0132)) || \ + (0137 == ($arg0)) || \ + ((0300 <= ($arg0)) && (($arg0) != 0327) && (($arg0) <= 0336)) + # Variable start character + set $etp_ct_variable = 1 + else + set $etp_ct_variable = 0 + end +end + +define etp-ct-name-1 +# Args: int +# +# Determines if integer is a name character, +# i.e non-first atom or variable character. +# +# Non-reentrant +# Returns: $etp_ct_variable + if (($arg0) == 0100 || \ + (060 <= ($arg0)) && (($arg0) <= 071)) + set $etp_ct_name = 1 + else + etp-ct-atom-1 ($arg0) + if $etp_ct_atom + set $etp_ct_name = 1 + else + etp-ct-variable-1 ($arg0) + set $etp_ct_name = $etp_ct_variable + end + end +end + +define etp-pid-1 +# Args: Eterm pid +# +# Non-reentrant +# + set $etp_pid_1 = (Eterm)($arg0) + if ($etp_pid_1 & 0xF) == 0x3 + if (etp_arch_bits == 64 && etp_halfword == 0) + if (etp_big_endian) + set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 36) & 0x0fffffff) + else + set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 4) & 0x0fffffff) + end + else + set $etp_pid_data = (unsigned) (((((Uint32) $etp_pid_1) >> 4) & ~erts_proc.r.o.pix_mask) | ((((Uint32) $etp_pid_1) >> (erts_proc.r.o.pix_cl_shift + 4)) & erts_proc.r.o.pix_cl_mask) | (((((Uint32) $etp_pid_1) >> 4) & erts_proc.r.o.pix_cli_mask) << erts_proc.r.o.pix_cli_shift)) + end + # Internal pid + printf "<0.%u.%u>", $etp_pid_data & 0x7fff, ($etp_pid_data >> 15) & 0x1fff + else + printf "#NotPid<%#x>", ($arg0) + end +end + +define etp-extpid-1 +# Args: Eterm extpid +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_extpid_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) + if ($etp_extpid_1_p->header & 0x3f) != 0x30 + printf "#NotExternalPid<%#x>", $etp_extpid_1_p->header + else + ## External pid + set $etp_extpid_1_number = $etp_extpid_1_p->data.ui[0]&0x7fff + set $etp_extpid_1_serial = ($etp_extpid_1_p->data.ui[0]>>15)&0x1fff + set $etp_extpid_1_np = $etp_extpid_1_p->node + set $etp_extpid_1_creation = $etp_extpid_1_np->creation + set $etp_extpid_1_dep = $etp_extpid_1_np->dist_entry + set $etp_extpid_1_node = $etp_extpid_1_np->sysname + if ($etp_extpid_1_node & 0x3f) != 0xb + # Should be an atom + printf "#ExternalPidError<%#x>", ($arg0) + else + if $etp_extpid_1_dep == erts_this_dist_entry + printf "<0:" + else + printf "<%u:", $etp_extpid_1_node>>6 + end + etp-atom-1 ($etp_extpid_1_node) + printf "/%u.%u.%u>", $etp_extpid_1_creation, \ + $etp_extpid_1_number, $etp_extpid_1_serial + end + end + end +end + + +define etp-port-1 +# Args: Eterm port +# +# Non-reentrant +# + set $etp_port_1 = (Eterm)($arg0) + if ($etp_port_1 & 0xF) == 0x7 + if (etp_arch_bits == 64 && etp_halfword == 0) + if (etp_big_endian) + set $etp_port_data = (unsigned) ((((Uint64) $etp_port_1) >> 36) & 0x0fffffff) + else + set $etp_port_data = (unsigned) ((((Uint64) $etp_port_1) >> 4) & 0x0fffffff) + end + else + set $etp_port_data = (unsigned) (((((Uint32) $etp_port_1) >> 4) & ~erts_port.r.o.pix_mask) | ((((Uint32) $etp_port_1) >> (erts_port.r.o.pix_cl_shift + 4)) & erts_port.r.o.pix_cl_mask) | (((((Uint32) $etp_port_1) >> 4) & erts_port.r.o.pix_cli_mask) << erts_port.r.o.pix_cli_shift)) + end + # Internal port + printf "#Port<0.%u>", $etp_port_data + else + printf "#NotPort<%#x>", ($arg0) + end +end + +define etp-extport-1 +# Args: Eterm extport +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_extport_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) + if ($etp_extport_1_p->header & 0x3F) != 0x34 + printf "#NotExternalPort<%#x>", $etp_extport_1->header + else + ## External port + set $etp_extport_1_number = $etp_extport_1_p->data.ui[0]&0x3ffff + set $etp_extport_1_np = $etp_extport_1_p->node + set $etp_extport_1_creation = $etp_extport_1_np->creation + set $etp_extport_1_dep = $etp_extport_1_np->dist_entry + set $etp_extport_1_node = $etp_extport_1_np->sysname + if ($etp_extport_1_node & 0x3f) != 0xb + # Should be an atom + printf "#ExternalPortError<%#x>", ($arg0) + else + if $etp_extport_1_dep == erts_this_dist_entry + printf "#Port<0:" + else + printf "#Port<%u:", $etp_extport_1_node>>6 + end + etp-atom-1 ($etp_extport_1_node) + printf "/%u.%u>", $etp_extport_1_creation, $etp_extport_1_number + end + end + end +end + + + +define etp-bignum-1 +# Args: Eterm bignum +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_bignum_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) + if ($etp_bignum_1_p[0] & 0x3b) != 0x08 + printf "#NotBignum<%#x>", $etp_bignum_1_p[0] + else + set $etp_bignum_1_i = ($etp_bignum_1_p[0] >> 6) + if $etp_bignum_1_i < 1 + printf "#BignumError<%#x>", (Eterm)($arg0) + else + if $etp_bignum_1_p[0] & 0x04 + printf "-" + end + set $etp_bignum_1_p = (ErtsDigit *)($etp_bignum_1_p + 1) + printf "16#" + if $etp_arch64 + while $etp_bignum_1_i > 0 + set $etp_bignum_1_i-- + printf "%016lx", $etp_bignum_1_p[$etp_bignum_1_i] + end + else + while $etp_bignum_1_i > 0 + set $etp_bignum_1_i-- + printf "%08x", $etp_bignum_1_p[$etp_bignum_1_i] + end + end + end + end + end +end + + + +define etp-float-1 +# Args: Eterm float +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_float_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) + if ($etp_float_1_p[0] & 0x3f) != 0x18 + printf "#NotFloat<%#x>", $etp_float_1_p[0] + else + printf "%f", *(double*)($etp_float_1_p+1) + end + end +end + + + +define etp-ref-1 +# Args: Eterm ref +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_ref_1_p = (RefThing *)((Eterm)($arg0) & ~0x3) + if ($etp_ref_1_p->header & 0x3b) != 0x10 + printf "#NotRef<%#x>", $etp_ref_1_p->header + else + set $etp_ref_1_nump = (Uint32 *) 0 + set $etp_ref_1_error = 0 + if ($etp_ref_1_p->header >> 6) == 0 + set $etp_ref_1_error = 1 + else + if $etp_arch64 + set $etp_ref_1_i = (int) $etp_ref_1_p->data.ui32[0] + if (($etp_ref_1_i + 1) > (2 * ($etp_ref_1_p->header >> 6))) + set $etp_ref_1_error = 1 + else + set $etp_ref_1_nump = &$etp_ref_1_p->data.ui32[1] + end + else + set $etp_ref_1_i = (int) ($etp_ref_1_p->header >> 6) + set $etp_ref_1_nump = &$etp_ref_1_p->data.ui32[0] + end + end + if $etp_ref_1_error + printf "#InternalRefError<%#x>", ($arg0) + else + printf "#Ref<0" + set $etp_ref_1_i-- + while $etp_ref_1_i >= 0 + printf ".%u", (unsigned) $etp_ref_1_nump[$etp_ref_1_i] + set $etp_ref_1_i-- + end + printf ">" + end + end + end +end + + + +define etp-extref-1 +# Args: Eterm extref +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_extref_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) + if ($etp_extref_1_p->header & 0x3F) != 0x38 + printf "#NotExternalRef<%#x>", $etp_extref_1->header + else + ## External ref + set $etp_extref_1_nump = (Uint32 *) 0 + set $etp_extref_1_error = 0 + set $etp_extref_1_i = (int) ($etp_extref_1_p->header >> 6) + set $etp_extref_1_np = $etp_extref_1_p->node + set $etp_extref_1_creation = $etp_extref_1_np->creation + set $etp_extref_1_dep = $etp_extref_1_np->dist_entry + set $etp_extref_1_node = $etp_extref_1_np->sysname + if ($etp_extref_1_node & 0x3f) != 0xb || $etp_extref_1_i < 3 + # Node should be an atom + set $etp_extref_1_error = 1 + else + ## $etp_extref_1_i now equals data (Uint) words + set $etp_extref_1_i -= 2 + if $etp_arch64 + if ((((int) $etp_extref_1_p->data.ui32[0]) + 1) \ + > (2 * $etp_extref_1_i)) + set $etp_extref_1_error = 1 + else + set $etp_extref_1_nump = &$etp_extref_1_p->data.ui32[1] + set $etp_extref_1_i = (int) $etp_extref_1_p->data.ui32[0] + end + else + set $etp_extref_1_nump = &$etp_extref_1_p->data.ui32[0] + end + ## $etp_extref_1_i now equals no of ref num (Uint32) words + if !$etp_extref_1_error + if $etp_extref_1_dep == erts_this_dist_entry + printf "#Ref<0:" + else + printf "#Ref<%u:", $etp_extref_1_node>>6 + end + etp-atom-1 ($etp_extref_1_node) + printf "/%u", $etp_extref_1_creation + end + end + if $etp_extref_1_error + printf "#ExternalRefError<%#x>", ($arg0) + else + set $etp_extref_1_i-- + while $etp_extref_1_i >= 0 + printf ".%u", (unsigned) $etp_extref_1_nump[$etp_extref_1_i] + set $etp_extref_1_i-- + end + printf ">" + end + end + end +end + + + +define etp-mfa-1 +# Args: Eterm*, int offset +# +# Reentrant +# + printf "<" + etp-atom-1 (((Eterm*)($arg0))[0]) + printf ":" + etp-atom-1 (((Eterm*)($arg0))[1]) + printf "/%d", ((Eterm*)($arg0))[2] + if ($arg1) > 0 + printf "+%#x>", ($arg1) + else + printf ">" + end +end + +define etp-mfa +# Args: Eterm* +# +# Reentrant capable +# + etp-mfa-1 ($arg0) 0 + printf ".\n" +end + +document etp-mfa +%--------------------------------------------------------------------------- +% etp-mfa Eterm* +% +% Take an Eterm* to an MFA function name entry and print it. +% These can be found e.g in the process structure; +% process_tab[i]->current and process_tab[i]->initial. +%--------------------------------------------------------------------------- +end + + + +define etp-cp-1 +# Args: Eterm cp +# +# Non-reentrant +# + set $etp_cp = (Eterm)($arg0) + 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 > (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 + end + end + set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2 + end + if $etp_cp_p + set $etp_cp_low = (Eterm**)($etp_cp_p->start + 8) + set $etp_cp_high = $etp_cp_low +$etp_cp_p->start[0] + set $etp_cp_p = 0 + while $etp_cp_low < $etp_cp_high + set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2 + if $etp_cp < $etp_cp_mid[0] + set $etp_cp_high = $etp_cp_mid + else + if $etp_cp < $etp_cp_mid[1] + set $etp_cp_p = $etp_cp_mid[0]+2 + set $etp_cp_low = $etp_cp_high = $etp_cp_mid + else + set $etp_cp_low = $etp_cp_mid + 1 + end + end + end + end + if $etp_cp_p + printf "#Cp" + etp-mfa-1 ($etp_cp_p) ($etp_cp-((Eterm)($etp_cp_p-2))) + else + if $etp_cp == beam_apply+1 + printf "#Cp" + else + if *(Eterm*)($etp_cp) == beam_return_trace[0] + if ($etp_cp) == beam_exception_trace + printf "#Cp" + else + printf "#Cp" + end + else + if *(Eterm*)($etp_cp) == beam_return_to_trace[0] + printf "#Cp" + else + printf "#Cp<%#x>", $etp_cp + end + end + end + end +end + +define etp-cp +# Args: Eterm cp +# +# Reentrant capable +# + etp-cp-1 ($arg0) + printf ".\n" +end + +document etp-cp +%--------------------------------------------------------------------------- +% etp-cp Eterm +% +% Take a code continuation pointer and print +% module, function, arity and offset. +% +% Code continuation pointers can be found in the process structure e.g +% process_tab[i]->cp and process_tab[i]->i, the second is the +% program counter, which is the same thing as a continuation pointer. +%--------------------------------------------------------------------------- +end + +############################################################################ +# Commands for special term bunches. +# + +define etp-msgq +# Args: ErlMessageQueue* +# +# Non-reentrant +# + set $etp_msgq = ($arg0) + set $etp_msgq_p = $etp_msgq->first + set $etp_msgq_i = $etp_msgq->len + set $etp_msgq_prev = $etp_msgq->last + printf "%% Message queue (%d):", $etp_msgq_i + if ($etp_msgq_i > 0) && $etp_msgq_p + printf "\n[" + else + printf "\n" + end + while ($etp_msgq_i > 0) && $etp_msgq_p + set $etp_msgq_i-- + set $etp_msgq_next = $etp_msgq_p->next + # Msg + etp-1 ($etp_msgq_p->m[0]) 0 + if ($etp_msgq_i > 0) && $etp_msgq_next + printf ", %% " + else + printf "]. %% " + end + # Seq_trace token + etp-1 ($etp_msgq_p->m[1]) 0 + if $etp_msgq_p == $etp_msgq->save + printf ", <=\n" + else + printf "\n" + end + if ($etp_msgq_i > 0) && $etp_msgq_next + printf " " + end + # + set $etp_msgq_prev = $etp_msgq_p + set $etp_msgq_p = $etp_msgq_next + end + if $etp_msgq_i != 0 + printf "#MsgQShort<%d>\n", $etp_msgq_i + end + if $etp_msgq_p != 0 + printf "#MsgQLong<%#lx%p>\n", (unsigned long)$etp_msgq_p + end + if $etp_msgq_prev != $etp_msgq->last + printf "#MsgQEndError<%#lx%p>\n", (unsigned long)$etp_msgq_prev + end +end + +document etp-msgq +%--------------------------------------------------------------------------- +% etp-msgq ErlMessageQueue* +% +% Take an ErlMessageQueue* and print the contents of the message queue. +% Sequential trace tokens are included in comments and +% the current match position in the queue is marked '<='. +% +% A process's message queue is process_tab[i]->msg. +%--------------------------------------------------------------------------- +end + + + +define etpf-msgq +# Args: Process* +# +# Non-reentrant +# + set $etp_flat = 1 + etp-msgq ($arg0) + set $etp_flat = 0 +end + +document etpf-msgq +%--------------------------------------------------------------------------- +% etpf-msgq ErlMessageQueue* +% +% Same as 'etp-msgq' but print the messages using etpf (flat). +%--------------------------------------------------------------------------- +end + + + +define etp-stacktrace +# Args: Process* +# +# Non-reentrant +# + set $etp_stacktrace_p = ($arg0)->stop + set $etp_stacktrace_end = ($arg0)->hend + printf "%% Stacktrace (%u): ", $etp_stacktrace_end-$etp_stacktrace_p + etp ($arg0)->cp + while $etp_stacktrace_p < $etp_stacktrace_end + if ($etp_stacktrace_p[0] & 0x3) == 0x0 + # Continuation pointer + etp $etp_stacktrace_p[0] + end + set $etp_stacktrace_p++ + end +end + +document etp-stacktrace +%--------------------------------------------------------------------------- +% etp-stacktrace Process* +% +% Take an Process* and print a stactrace for the process. +% The stacktrace consists just of the pushed code continuation +% pointers on the stack, the most recently pushed first. +%--------------------------------------------------------------------------- +end + +define etp-stackdump +# Args: Process* +# +# Non-reentrant +# + set $etp_stackdump_p = ($arg0)->stop + set $etp_stackdump_end = ($arg0)->hend + printf "%% Stackdump (%u): ", $etp_stackdump_end-$etp_stackdump_p + etp ($arg0)->cp + while $etp_stackdump_p < $etp_stackdump_end + etp $etp_stackdump_p[0] + set $etp_stackdump_p++ + end +end + +document etp-stackdump +%--------------------------------------------------------------------------- +% etp-stackdump Process* +% +% Take an Process* and print a stackdump for the process. +% The stackdump consists of all pushed values on the stack. +% All code continuation pointers are preceeded with a line +% of dashes to make the stack frames more visible. +%--------------------------------------------------------------------------- +end + +define etpf-stackdump +# Args: Process* +# +# Non-reentrant +# + set $etp_flat = 1 + etp-stackdump ($arg0) + set $etp_flat = 0 +end + +document etpf-stackdump +%--------------------------------------------------------------------------- +% etpf-stackdump Process* +% +% Same as etp-stackdump but print the values using etpf (flat). +%--------------------------------------------------------------------------- +end + +define etp-pid2pix-1 +# Args: Eterm +# + if (etp_arch_bits == 64 && etp_halfword == 0) + if (etp_big_endian) + set $etp_pix = (int) (((Uint64) $arg0) & 0x0fffffff) + else + set $etp_pix = (int) ((((Uint64) $arg0) >> 32) & 0x0fffffff) + end + else + set $etp_pix = (int) ((((Uint32) $arg0) >> 4) & erts_proc.r.o.pix_mask) + end +end + +define etp-pix2proc +# Args: Eterm +# + set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[((int) $arg0)]) + printf "(Process *) %p\n", $proc +end + +define etp-pid2proc-1 +# Args: Eterm +# + etp-pid2pix-1 $arg0 + set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$etp_pix]) +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->common.id + printf "\n State: " + etp-proc-state $arg0 + if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0 + if ($arg0->common.u.alive.reg) + printf " Registered name: " + etp-1 $arg0->common.u.alive.reg->name + printf "\n" + end + 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->msg_inq.len), $arg0->msg.len, $arg0->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.r.o.max + set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$proc_ix]) + if ($proc != ((Process *) 0) && $proc != &erts_invalid_process) + 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-id2pix-1 +# Args: Eterm +# + if (etp_arch_bits == 64 && etp_halfword == 0) + if (etp_big_endian) + set $etp_pix = (int) (((Uint64) $arg0) & 0x0fffffff) + elser + set $etp_pix = (int) ((((Uint64) $arg0) >> 32) & 0x0fffffff) + end + else + set $etp_pix = (int) ((((Uint32) $arg0) >> 4) & erts_port.r.o.pix_mask) + end +end + +define etp-pix2port +# Args: Eterm +# + set $port = (Port *) *((UWord *) &erts_port.r.o.tab[((int) $arg0)]) + printf "(Port *) %p\n", $port +end + +define etp-id2port-1 +# Args: Eterm +# + etp-port-id2pix-1 $arg0 + set $port = (Port *) *((UWord *) &erts_port.r.o.tab[((int) $etp_pix)]) +end + +define etp-id2port +# Args: Eterm +# + etp-id2port-1 $arg0 + printf "(Port *) %p\n", $port +end + +define etp-port-sched-flags-int +# Args: int +# + if ($arg0 & 0x1) + printf " in-run-queue" + end + if ($arg0 & 0x2) + printf " executing" + end + if ($arg0 & 0x4) + printf " have-tasks" + end + if ($arg0 & 0x8) + printf " exited" + end + if ($arg0 & 0x10) + printf " busy-port" + end + if ($arg0 & 0x20) + printf " busy-port-q" + end + if ($arg0 & 0x40) + printf " chk-unset-busy-port-q" + end + if ($arg0 & 0x80) + printf " have-busy-tasks" + end + if ($arg0 & 0x100) + printf " have-nosuspend-tasks" + end + if ($arg0 & 0x200) + printf " parallelism" + end + if ($arg0 & 0x400) + printf " force-sched" + end + if ($arg0 & 0xfffff800) + printf " GARBAGE" + end + printf "\n" +end + +document etp-port-sched-flags-int +%--------------------------------------------------------------------------- +% etp-proc-sched-flags-int int +% +% Print port sched-flags +%--------------------------------------------------------------------------- +end + + +define etp-port-sched-flags +# Args: Port* +# + set $sched_flags_int = *(((Uint32 *) &(((Port *) $arg0)->sched.flags))) + etp-port-sched-flags-int $sched_flags_int +end + +document etp-port-sched-flags +%--------------------------------------------------------------------------- +% etp-proc-sched-flags-int Port * +% +% Print port sched-flags +%--------------------------------------------------------------------------- +end + +define etp-port-state-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 " closing" + end + if ($arg0 & 0x40) + printf " send-closed" + end + if ($arg0 & 0x80) + printf " linebuf-io" + end + if ($arg0 & 0x100) + printf " free" + end + if ($arg0 & 0x200) + printf " initializing" + end + if ($arg0 & 0x400) + printf " port-specific-lock" + end + if ($arg0 & 0x800) + printf " invalid" + end + if ($arg0 & 0x1000) + printf " halt" + end + if (etp_debug_compiled) + if ($arg0 & 0x7fffe000) + printf " GARBAGE" + end + else + if ($arg0 & 0xffffe000) + printf " GARBAGE" + end + end + printf "\n" +end + +document etp-port-state-int +%--------------------------------------------------------------------------- +% etp-proc-state-int int +% +% Print port state +%--------------------------------------------------------------------------- +end + + +define etp-port-state +# Args: Port* +# + set $state_int = *(((Uint32 *) &(((Port *) $arg0)->state))) + etp-port-state-int $state_int +end + +document etp-port-state +%--------------------------------------------------------------------------- +% etp-proc-state-int Port * +% +% Print port state +%--------------------------------------------------------------------------- +end + +define etp-port-info +# Args: Port* +# + printf " Port: " + etp-1 $arg0->common.id + printf "\n Name: %s\n", $arg0->name + printf " State:" + etp-port-state $arg0 + printf " Scheduler flags:" + etp-port-sched-flags $arg0 + if (*(((Uint32 *) &(((Port *) $arg0)->state))) & 0x5C00) == 0 + if ($arg0->common.u.alive.reg) + printf " Registered name: " + etp-1 $arg0->common.u.alive.reg->name + printf "\n" + end + end + printf " Connected: " + set $connected = *(((Eterm *) &(((Port *) $arg0)->connected))) + etp-1 $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_port.r.o.max + set $port = (Port *) *((UWord *) &erts_port.r.o.tab[$port_ix]) + if ($port != ((Port *) 0) && $port != &erts_invalid_port) + if (*(((Uint32 *) &(((Port *) $port)->state))) & 0x100) == 0 + # I.e, not free + printf "---\n" + printf " Pix: %d\n", $port_ix + etp-port-info $port + end + 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 "Endianess: " + if (etp_big_endian) + printf "Big\n" + else + printf "Little\n" + end + printf "Word size: %d-bit\n", etp_arch_bits + printf "Halfword: " + if (etp_halfword) + printf "yes\n" + else + printf "no\n" + end + printf "HiPE support: " + if (etp_hipe) + printf "yes\n" + 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-compile-info + printf "--------------- Compile Information ---------------\n" + printf "CFLAGS: %s\n", erts_build_flags_CFLAGS + printf "LDFLAGS: %s\n", erts_build_flags_LDFLAGS + printf "Use etp-config-h-info to dump config.h\n" +end + +document etp-compile-info +%--------------------------------------------------------------------------- +% etp-compile-info +% +% Print information about how the system was compiled +%--------------------------------------------------------------------------- +end + +define etp-config-h-info + printf "%s", erts_build_flags_CONFIG_H +end + +document etp-config-h-info +%--------------------------------------------------------------------------- +% etp-config-h-info +% +% Dump the contents of config.h when the system was compiled +%--------------------------------------------------------------------------- +end + +define etp-dictdump +# Args: ProcDict* +# +# Non-reentrant +# + set $etp_dictdump = ($arg0) + if $etp_dictdump + set $etp_dictdump_n = \ + $etp_dictdump->homeSize + $etp_dictdump->splitPosition + set $etp_dictdump_i = 0 + set $etp_dictdump_written = 0 + if $etp_dictdump_n > $etp_dictdump->size + set $etp_dictdump_n = $etp_dictdump->size + end + set $etp_dictdump_cnt = $etp_dictdump->numElements + printf "%% Dictionary (%d):\n[", $etp_dictdump_cnt + while $etp_dictdump_i < $etp_dictdump_n && \ + $etp_dictdump_cnt > 0 + set $etp_dictdump_p = $etp_dictdump->data[$etp_dictdump_i] + if $etp_dictdump_p != $etp_nil + if ((Eterm)$etp_dictdump_p & 0x3) == 0x2 + # Boxed + if $etp_dictdump_written + printf ",\n " + else + set $etp_dictdump_written = 1 + end + etp-1 $etp_dictdump_p 0 + set $etp_dictdump_cnt-- + else + while ((Eterm)$etp_dictdump_p & 0x3) == 0x1 && \ + $etp_dictdump_cnt > 0 + # Cons ptr + if $etp_dictdump_written + printf ",\n " + else + set $etp_dictdump_written = 1 + end + etp-1 (((Eterm*)((Eterm)$etp_dictdump_p&~0x3))[0]) 0 + set $etp_dictdump_cnt-- + set $etp_dictdump_p = ((Eterm*)((Eterm)$etp_dictdump_p & ~0x3))[1] + end + if $etp_dictdump_p != $etp_nil + printf "#DictSlotError<%d>:", $etp_dictdump_i + set $etp_dictdump_flat = $etp_flat + set $etp_flat = 1 + etp-1 ((Eterm)$etp_dictdump_p) 0 + set $etp_flat = $etp_dictdump_flat + end + end + end + set $etp_dictdump_i++ + end + if $etp_dictdump_cnt != 0 + printf "#DictCntError<%d>, ", $etp_dictdump_cnt + end + else + printf "%% Dictionary (0):\n[" + end + printf "].\n" +end + +document etp-dictdump +%--------------------------------------------------------------------------- +% etp-dictdump ErlProcDict* +% +% Take an ErlProcDict* and print all entries in the process dictionary. +%--------------------------------------------------------------------------- +end + +define etpf-dictdump +# Args: ErlProcDict* +# +# Non-reentrant +# + set $etp_flat = 1 + etp-dictdump ($arg0) + set $etp_flat = 0 +end + +document etpf-dictdump +%--------------------------------------------------------------------------- +% etpf-dictdump ErlProcDict* +% +% Same as etp-dictdump but print the values using etpf (flat). +%--------------------------------------------------------------------------- +end + + + +define etp-offheapdump +# Args: ( ExternalThing* | ProcBin* | ErlFunThing* ) +# +# Non-reentrant +# + set $etp_offheapdump_p = ($arg0) + set $etp_offheapdump_i = 0 + set $etp_offheapdump_ + printf "%% Offheap dump:\n[" + while ($etp_offheapdump_p != 0) && ($etp_offheapdump_i < $etp_max_depth) + if ((Eterm)$etp_offheapdump_p & 0x3) == 0x0 + if $etp_offheapdump_i > 0 + printf ",\n " + end + etp-1 ((Eterm)$etp_offheapdump_p|0x2) 0 + set $etp_offheapdump_p = $etp_offheapdump_p->next + set $etp_offheapdump_i++ + else + printf "#TaggedPtr<%#x>", $etp_offheapdump_p + set $etp_offheapdump_p = 0 + end + end + printf "].\n" +end + +document etp-offheapdump +%--------------------------------------------------------------------------- +% etp-offheapdump ( ExternalThing* | ProcBin* | ErlFunThing* ) +% +% Take an pointer to a linked list and print the terms in the list +% up to the max depth. +%--------------------------------------------------------------------------- +end + +define etpf-offheapdump +# Args: ( ExternalThing* | ProcBin* | ErlFunThing* ) +# +# Non-reentrant +# + set $etp_flat = 1 + etp-offheapdump ($arg0) + set $etp_flat = 0 +end + +document etpf-offheapdump +%--------------------------------------------------------------------------- +% etpf-offheapdump ( ExternalThing* | ProcBin* | ErlFunThing* ) +% +% Same as etp-offheapdump but print the values using etpf (flat). +%--------------------------------------------------------------------------- +end + +define etp-search-heaps +# Args: Eterm +# +# Non-reentrant +# + printf "%% Search all (<%u) process heaps for ", erts_max_processes + set $etp_flat = 1 + etp-1 ($arg0) 0 + set $etp_flat = 0 + printf ":...\n" + etp-search-heaps-1 ((Eterm*)((Eterm)($arg0)&~3)) +end + +define etp-search-heaps-1 +# Args: Eterm* +# +# Non-reentrant +# + set $etp_search_heaps_q = erts_max_processes / 10 + set $etp_search_heaps_r = erts_max_processes % 10 + set $etp_search_heaps_t = 10 + set $etp_search_heaps_m = $etp_search_heaps_q + if $etp_search_heaps_r > 0 + set $etp_search_heaps_m++ + set $etp_search_heaps_r-- + end + set $etp_search_heaps_i = 0 + set $etp_search_heaps_found = 0 + while $etp_search_heaps_i < erts_proc.r.o.max + set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$proc_ix]) + if $proc + if ($proc->heap <= ($arg0)) && \ + (($arg0) < $proc->hend) + printf "process_tab[%d]->heap+%d\n", $etp_search_heaps_i, \ + ($arg0)-$proc->heap + end + if ($proc->old_heap <= ($arg0)) && \ + (($arg0) <= $proc->old_hend) + printf "process_tab[%d]->old_heap+%d\n", $etp_search_heaps_i, \ + ($arg0)-$proc->old_heap + end + set $etp_search_heaps_cnt = 0 + 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)) && \ + (($arg0) < &($etp_search_heaps_p->mem)+$etp_search_heaps_p->size) + printf "process_tab[%d]->mbuf(%d)+%d\n", \ + $etp_search_heaps_i, $etp_search_heaps_cnt, \ + ($arg0)-&($etp_search_heaps_p->mem) + end + set $etp_search_heaps_p = $etp_search_heaps_p->next + end + if $etp_search_heaps_p + printf "Process ix=%d %% Too many HeapFragments\n", \ + $etp_search_heaps_i + end + end + set $etp_search_heaps_i++ + if $etp_search_heaps_i > $etp_search_heaps_m + printf "%% %d%%...\n", $etp_search_heaps_t + set $etp_search_heaps_t += 10 + set $etp_search_heaps_m += $etp_search_heaps_q + if $etp_search_heaps_r > 0 + set $etp_search_heaps_m++ + set $etp_search_heaps_r-- + end + end + end + printf "%% 100%%.\n" +end + +document etp-search-heaps +%--------------------------------------------------------------------------- +% etp-search-heaps Eterm +% +% Search all process heaps in process_tab[], including the heap fragments +% (process_tab[]->mbuf) for the specified Eterm. +%--------------------------------------------------------------------------- +end + + + +define etp-search-alloc +# Args: Eterm +# +# Non-reentrant +# + printf "%% Search allocated memory blocks for " + set $etp_flat = 1 + etp-1 ($arg0) 0 + set $etp_flat = 0 + printf ":...\n" + set $etp_search_alloc_n = sizeof(erts_allctrs) / sizeof(*erts_allctrs) + set $etp_search_alloc_i = 0 + while $etp_search_alloc_i < $etp_search_alloc_n + if erts_allctrs[$etp_search_alloc_i].alloc + set $etp_search_alloc_f = (erts_allctrs+$etp_search_alloc_i) + while ($etp_search_alloc_f->alloc == debug_alloc) || \ + ($etp_search_alloc_f->alloc == stat_alloc) || \ + ($etp_search_alloc_f->alloc == map_stat_alloc) + set $etp_search_alloc_f = \ + (ErtsAllocatorFunctions_t*)$etp_search_alloc_f->extra + end + if ($etp_search_alloc_f->alloc != erts_sys_alloc) && \ + ($etp_search_alloc_f->alloc != erts_fix_alloc) + if ($etp_search_alloc_f->alloc == erts_alcu_alloc) || \ + ($etp_search_alloc_f->alloc == erts_alcu_alloc_ts) + # alcu alloc + set $etp_search_alloc_e = (Allctr_t*)$etp_search_alloc_f->extra + # mbc_list + set $etp_search_alloc_p = $etp_search_alloc_e->mbc_list.first + set $etp_search_alloc_cnt = 0 + while $etp_search_alloc_p && \ + ($etp_search_alloc_cnt < $etp_max_depth) + set $etp_search_alloc_cnt++ + if $etp_search_alloc_p <= ($arg0) && \ + ($arg0) < (char*)$etp_search_alloc_p + \ + ($etp_search_alloc_p->chdr & (Uint)~7) + printf "erts_allctrs[%d] %% %salloc: mbc_list: %d\n", \ + $etp_search_alloc_i, $etp_search_alloc_e->name_prefix, \ + $etp_search_alloc_cnt + end + if $etp_search_alloc_p == $etp_search_alloc_e->mbc_list.last + if $etp_search_alloc_p->next + printf \ + "erts_allctrs[%d] %% %salloc: mbc_list.last error %p\n",\ + $etp_search_alloc_i, $etp_search_alloc_e->name_prefix,\ + $etp_search_alloc_p + end + set $etp_search_alloc_p = 0 + else + set $etp_search_alloc_p = $etp_search_alloc_p->next + end + end + if $etp_search_alloc_p + printf "erts_allctrs[%d] %% %salloc: too large mbc_list %p\n", \ + $ept_search_alloc_i, $etp_search_alloc_e->name_prefix, + $ept_search_alloc_p + end + # sbc_list + set $etp_search_alloc_p = $etp_search_alloc_e->sbc_list.first + set $etp_search_alloc_cnt = 0 + while $etp_search_alloc_p && \ + ($etp_search_alloc_cnt < $etp_max_depth) + set $etp_search_alloc_cnt++ + if $etp_search_alloc_p <= ($arg0) && \ + ($arg0) < (char*)$etp_search_alloc_p + \ + ($etp_search_alloc_p->chdr & (Uint)~7) + printf "erts_allctrs[%d] %% %salloc: sbc_list: %d\n", \ + $etp_search_alloc_i, $etp_search_alloc_e->name_prefix, \ + $etp_search_alloc_cnt + end + if $etp_search_alloc_p == $etp_search_alloc_e->sbc_list.last + if $etp_search_alloc_p->next + printf \ + "erts_allctrs[%d] %% %salloc: sbc_list.last error %p",\ + $etp_search_alloc_i, $etp_search_alloc_e->name_prefix,\ + $etp_search_alloc_p + end + set $etp_search_alloc_p = 0 + else + set $etp_search_alloc_p = $etp_search_alloc_p->next + end + end + if $etp_search_alloc_p + printf "erts_allctrs[%d] %% %salloc: too large sbc_list %p\n", \ + $ept_search_alloc_i, $etp_search_alloc_e->name_prefix, + $ept_search_alloc_p + end + else + printf "erts_allctrs[%d] %% %s: unknown allocator\n", \ + $etp_search_alloc_i, erts_alc_a2ad[$etp_search_alloc_i] + end + end + end + set $etp_search_alloc_i++ + end +end + +document etp-search-alloc +%--------------------------------------------------------------------------- +% etp-search-heaps Eterm +% +% Search all internal allocator memory blocks for for the specified Eterm. +%--------------------------------------------------------------------------- +end + + + +define etp-overlapped-heaps +# Args: +# +# Non-reentrant +# + printf "%% Dumping heap addresses to \"etp-commands.bin\"\n" + set $etp_overlapped_heaps_q = erts_max_processes / 10 + set $etp_overlapped_heaps_r = erts_max_processes % 10 + set $etp_overlapped_heaps_t = 10 + set $etp_overlapped_heaps_m = $etp_overlapped_heaps_q + if $etp_overlapped_heaps_r > 0 + set $etp_overlapped_heaps_m++ + set $etp_overlapped_heaps_r-- + end + set $etp_overlapped_heaps_i = 0 + set $etp_overlapped_heaps_found = 0 + dump binary value etp-commands.bin 'o' + append binary value etp-commands.bin 'v' + append binary value etp-commands.bin 'e' + append binary value etp-commands.bin 'r' + append binary value etp-commands.bin 'l' + append binary value etp-commands.bin 'a' + append binary value etp-commands.bin 'p' + append binary value etp-commands.bin 'p' + append binary value etp-commands.bin 'e' + append binary value etp-commands.bin 'd' + append binary value etp-commands.bin '-' + append binary value etp-commands.bin 'h' + append binary value etp-commands.bin 'e' + append binary value etp-commands.bin 'a' + append binary value etp-commands.bin 'p' + append binary value etp-commands.bin 's' + append binary value etp-commands.bin '\0' + while $etp_overlapped_heaps_i < erts_max_processes + if process_tab[$etp_overlapped_heaps_i] + append binary value etp-commands.bin \ + (Eterm)$etp_overlapped_heaps_i + append binary value etp-commands.bin \ + (Eterm)process_tab[$etp_overlapped_heaps_i]->heap + append binary value etp-commands.bin \ + (Eterm)process_tab[$etp_overlapped_heaps_i]->hend + append binary value etp-commands.bin \ + (Eterm)process_tab[$etp_overlapped_heaps_i]->old_heap + append binary value etp-commands.bin \ + (Eterm)process_tab[$etp_overlapped_heaps_i]->old_hend + set $etp_overlapped_heaps_p = process_tab[$etp_overlapped_heaps_i]->mbuf + set $etp_overlapped_heaps_cnt = 0 + while $etp_overlapped_heaps_p && \ + ($etp_overlapped_heaps_cnt < $etp_max_depth) + set $etp_overlapped_heaps_cnt++ + append binary value etp-commands.bin \ + (Eterm)$etp_overlapped_heaps_p + append binary value etp-commands.bin \ +(Eterm)(&($etp_overlapped_heaps_p->mem)+$etp_overlapped_heaps_p->size) + set $etp_overlapped_heaps_p = $etp_overlapped_heaps_p->next + end + if $etp_overlapped_heaps_p + printf "process_tab[%d] %% Too many HeapFragments\n", \ + $etp_overlapped_heaps_i + end + append binary value etp-commands.bin (Eterm)0x0 + append binary value etp-commands.bin (Eterm)0x0 + end + set $etp_overlapped_heaps_i++ + if $etp_overlapped_heaps_i > $etp_overlapped_heaps_m + printf "%% %d%%...\n", $etp_overlapped_heaps_t + set $etp_overlapped_heaps_t += 10 + set $etp_overlapped_heaps_m += $etp_overlapped_heaps_q + if $etp_overlapped_heaps_r > 0 + set $etp_overlapped_heaps_m++ + set $etp_overlapped_heaps_r-- + end + end + end + etp-run +end + +document etp-overlapped-heaps +%--------------------------------------------------------------------------- +% etp-overlapped-heaps +% +% Dump all process heap addresses in process_tab[], including +% the heap fragments in binary format on the file etp-commands.bin. +% Then call etp_commands:file/1 to analyze if any heaps overlap. +% +% Requires 'erl' in the path and 'etp_commands.beam' in 'erl's search path. +%--------------------------------------------------------------------------- +end + + + +define etp-chart +# Args: Process* +# +# Non-reentrant + etp-chart-start ($arg0) + set ($arg0) = ($arg0) + etp-msgq (($arg0)->msg) + etp-stackdump ($arg0) + etp-dictdump (($arg0)->dictionary) + etp-dictdump (($arg0)->debug_dictionary) + printf "%% Dumping other process data...\n" + etp ($arg0)->seq_trace_token + etp ($arg0)->fvalue + printf "%% Dumping done.\n" + etp-chart-print +end + +document etp-chart +%--------------------------------------------------------------------------- +% etp-chart Process* +% +% Dump all process data to the file "etp-commands.bin" and then use +% the Erlang support module to print a memory chart of all terms. +%--------------------------------------------------------------------------- +end + + + +define etp-chart-start +# Args: Process* +# +# Non-reentrant + set $etp_chart = 1 + set $etp_chart_id = 0 + set $etp_chart_start_p = ($arg0) + dump binary value etp-commands.bin 'c' + append binary value etp-commands.bin 'h' + append binary value etp-commands.bin 'a' + append binary value etp-commands.bin 'r' + append binary value etp-commands.bin 't' + append binary value etp-commands.bin '\0' + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->heap) + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->high_water) + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->hend) + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->old_heap) + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->old_hend) + set $etp_chart_start_cnt = 0 + set $etp_chart_start_p = $etp_chart_start_p->mbuf + while $etp_chart_start_p && ($etp_chart_start_cnt < $etp_max_depth) + set $etp_chart_start_cnt++ + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->mem) + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->size) + set $etp_chart_start_p = $etp_chart_start_p->next + end + append binary value etp-commands.bin (Eterm)(0) + append binary value etp-commands.bin (Eterm)(0) + if $etp_chart_start_p + printf "%% Too many HeapFragments\n" + end +end + +document etp-chart-start +%--------------------------------------------------------------------------- +% etp-chart-start Process* +% +% Dump a chart head to the file "etp-commands.bin". +%--------------------------------------------------------------------------- +end + + + +define etp-chart-entry-1 +# Args: Eterm, int depth, int words +# +# Reentrant capable + if ($arg1) == 0 + set $etp_chart_id++ + printf "#%d:", $etp_chart_id + end + append binary value etp-commands.bin ($arg0)&~0x3 + append binary value etp-commands.bin (Eterm)(($arg2)*sizeof(Eterm)) + append binary value etp-commands.bin (Eterm)$etp_chart_id + append binary value etp-commands.bin (Eterm)($arg1) +# printf "", ($arg0)&~0x3, \ +# (Eterm)(($arg2)*sizeof(Eterm)), (Eterm)$etp_chart_id, (Eterm)($arg1) +end + + + +define etp-chart-print + set $etp_chart = 0 + etp-run +end + +document etp-chart-print +%--------------------------------------------------------------------------- +% etp-chart-print Process* +% +% Print a memory chart of the dumped data in "etp-commands.bin", and stop +% chart recording. +%--------------------------------------------------------------------------- +end + +############################################################################ +# ETS table debug +# + +define etp-ets-tables +# Args: +# +# Non-reentrant + printf "%% Dumping < %lu ETS tables\n", (unsigned long)db_max_tabs + while $etp_ets_tables_i < db_max_tabs + if (meta_main_tab[$etp_ets_tables_i].u.next_free & 3) == 0 + printf "%% %d:", $etp_ets_tables_i + etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.id)) 0 + printf " " + etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.owner)) 0 + printf "\n" + end + set $etp_ets_tables_i++ + end + set $etp_ets_tables_i = 0 +end + +document etp-ets-tables +%--------------------------------------------------------------------------- +% etp-ets-tables +% +% Dump all ETS table names and their indexies. +%--------------------------------------------------------------------------- +end + +define etp-ets-obj +# Args: DbTerm* +# + set $etp_ets_obj_i = 1 + while $etp_ets_obj_i <= (($arg0)->tpl[0] >> 6) + if $etp_ets_obj_i == 1 + printf "{" + else + printf ", " + end + set $etp_ets_elem = ($arg0)->tpl[$etp_ets_obj_i] + if ($etp_ets_elem & 3) == 0 + printf "" + else + etp-1 $etp_ets_elem 0 + end + set $etp_ets_obj_i++ + end + printf "}" +end + + +define etp-ets-tabledump +# Args: int tableindex +# +# Non-reentrant + printf "%% Dumping ETS table %d:", ($arg0) + set $etp_ets_tabledump_n = 0 + set $etp_ets_tabledump_t = meta_main_tab[($arg0)].u.tb + set $etp_ets_tabledump_i = 0 + etp-1 ($etp_ets_tabledump_t->common.the_name) 0 + printf " status=%#x\n", $etp_ets_tabledump_t->common.status + if $etp_ets_tabledump_t->common.status & 0x130 + # Hash table + set $etp_ets_tabledump_h = $etp_ets_tabledump_t->hash + printf "%% nitems=%d\n", (long) $etp_ets_tabledump_t->common.nitems + while $etp_ets_tabledump_i < (long) $etp_ets_tabledump_h->nactive + set $etp_ets_tabledump_seg = ((struct segment**)$etp_ets_tabledump_h->segtab)[$etp_ets_tabledump_i>>8] + set $etp_ets_tabledump_l = $etp_ets_tabledump_seg->buckets[$etp_ets_tabledump_i&0xFF] + if $etp_ets_tabledump_l + printf "%% Slot %d:\n", $etp_ets_tabledump_i + while $etp_ets_tabledump_l + if $etp_ets_tabledump_n + printf "," + else + printf "[" + end + set $etp_ets_tabledump_n++ + etp-ets-obj &($etp_ets_tabledump_l->dbterm) + if $etp_ets_tabledump_l->hvalue == ((unsigned long)-1) + printf "% *\n" + else + printf "\n" + end + set $etp_ets_tabledump_l = $etp_ets_tabledump_l->next + if $etp_ets_tabledump_n >= $etp_max_depth + set $etp_ets_tabledump_l = 0 + end + end + end + set $etp_ets_tabledump_i++ + end + if $etp_ets_tabledump_n + printf "].\n" + end + else + printf "%% Not a hash table\n" + end +end + +document etp-ets-tabledump +%--------------------------------------------------------------------------- +% etp-ets-tabledump Slot +% +% Dump an ETS table with a specified slot index. +%--------------------------------------------------------------------------- +end + +############################################################################ +# Erlang support module handling +# + +define etp-run + shell make -f "${ROOTDIR:?}/erts/etc/unix/etp_commands.mk" \ + ROOTDIR="${ROOTDIR:?}" ETP_DATA="etp-commands.bin" +end + +document etp-run +%--------------------------------------------------------------------------- +% etp-run +% +% Make and run the Erlang support module on the input file +% "erl-commands.bin". The environment variable ROOTDIR must +% be set to find $ROOTDIR/erts/etc/unix/etp_commands.mk. +% +% Also, erl and erlc must be in the path. +%--------------------------------------------------------------------------- +end + +define etp-thr + source @ERL_TOP@/erts/etc/unix/etp-thr.py +end + +############################################################################ +# Toolbox parameter handling +# + +define etp-set-max-depth + if ($arg0) > 0 + set $etp_max_depth = ($arg0) + else + echo %%%Error: max-depth <= 0 %%%\n + end +end + +document etp-set-max-depth +%--------------------------------------------------------------------------- +% etp-set-max-depth Depth +% +% Set the max term depth to use for etp. The term dept limit +% works in both depth and width, so if you set the max depth to 10, +% an 11 element flat tuple will be truncated. +%--------------------------------------------------------------------------- +end + +define etp-set-max-string-length + if ($arg0) > 0 + set $etp_max_string_length = ($arg0) + else + echo %%%Error: max-string-length <= 0 %%%\n + end +end + +document etp-set-max-string-length +%--------------------------------------------------------------------------- +% etp-set-max-strint-length Length +% +% Set the max string length to use for ept when printing lists +% that can be shown as printable strings. Printable strings +% that are longer will be truncated, and not even checked if +% they really are printable all the way to the end. +%--------------------------------------------------------------------------- +end + +define etp-show + printf "etp-set-max-depth %d\n", $etp_max_depth + printf "etp-set-max-string-length %d\n", $etp_max_string_length +end + +document etp-show +%--------------------------------------------------------------------------- +% etp-show +% +% Show the commands needed to set all etp parameters +% to their current value. +%--------------------------------------------------------------------------- +end + +############################################################################ +# Init +# + +define etp-init + set $etp_arch64 = (sizeof(void *) == 8) + if $etp_arch64 + set $etp_nil = 0xfffffffffffffffb + else + set $etp_nil = 0xfffffffb + end + set $etp_flat = 0 + set $etp_chart_id = 0 + set $etp_chart = 0 + + set $etp_max_depth = 20 + set $etp_max_string_length = 100 + + set $etp_ets_tables_i = 0 +end + +document etp-init +%--------------------------------------------------------------------------- +% Use etp-help for a command overview and general help. +% +% To use the Erlang support module, the environment variable ROOTDIR +% must be set to the toplevel installation directory of Erlang/OTP, +% so the etp-commands file becomes: +% $ROOTDIR/erts/etc/unix/etp-commands +% Also, erl and erlc must be in the path. +%--------------------------------------------------------------------------- +end + +etp-init +help etp-init +etp-show +etp-system-info diff --git a/erts/etc/unix/etp-thr.py b/erts/etc/unix/etp-thr.py new file mode 100644 index 0000000000..4bfbfa16f3 --- /dev/null +++ b/erts/etc/unix/etp-thr.py @@ -0,0 +1,57 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2013. All Rights Reserved. +# +# The 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% +# + +def get_thread_name(t): + if t.name != None: + return t.name; + f = gdb.newest_frame(); + while f: + if f.name() == "async_main": + return "async"; + elif f.name() == "erts_sys_main_thread": + return "main"; + elif f.name() == "signal_dispatcher_thread_func": + return "signal_dispatcher"; + elif f.name() == "sys_msg_dispatcher_func": + return "sys_msg_dispatcher"; + elif f.name() == "child_waiter": + return "child_waiter"; + elif f.name() == "sched_thread_func": + return "scheduler"; + elif f.name() == "aux_thread": + return "aux"; + f = f.older(); + return "unknown"; + + +curr_thread = gdb.selected_thread(); + +for i in gdb.inferiors(): + gdb.write(" Id Thread Name Frame\n"); + for t in i.threads(): + t.switch(); + if curr_thread == t: + gdb.write("*"); + else: + gdb.write(" "); + gdb.write("{0:<3} {1:20} {2}\n".format( + t.num,get_thread_name(t), + gdb.newest_frame().name())); + +curr_thread.switch(); -- cgit v1.2.3 From 86f970056b55ab8c33ecb6d0ce13924e3331b79a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 30 Jul 2013 14:53:00 +0200 Subject: Add smoke tests Smoke tests are meant to verify that a build of erlang has been successfull. --- erts/emulator/test/Makefile | 3 ++- erts/emulator/test/emulator_smoke.spec | 3 +++ erts/test/Makefile | 2 +- erts/test/system_smoke.spec | 3 +++ 4 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 erts/emulator/test/emulator_smoke.spec create mode 100644 erts/test/system_smoke.spec (limited to 'erts') diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 9594ab48b1..f02ca3cb98 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -140,7 +140,8 @@ EMAKEFILE=Emakefile TEST_SPEC_FILES= emulator.spec \ emulator.spec.win \ - emulator_bench.spec + emulator_bench.spec \ + emulator_smoke.spec # ---------------------------------------------------- # Release directory specification diff --git a/erts/emulator/test/emulator_smoke.spec b/erts/emulator/test/emulator_smoke.spec new file mode 100644 index 0000000000..3219aeb823 --- /dev/null +++ b/erts/emulator/test/emulator_smoke.spec @@ -0,0 +1,3 @@ +{suites,"../emulator_test",[smoke_test_SUITE,time_SUITE]}. +{cases,"../emulator_test",crypto_SUITE,[t_md5]}. +{cases,"../emulator_test",float_SUITE,[fpe,cmp_integer]}. \ No newline at end of file diff --git a/erts/test/Makefile b/erts/test/Makefile index 6b409e2f1b..74a5bb1ccc 100644 --- a/erts/test/Makefile +++ b/erts/test/Makefile @@ -79,7 +79,7 @@ release_spec: release_tests_spec: opt $(INSTALL_DIR) "$(RELSYSDIR)" - $(INSTALL_DATA) system.spec system.dynspec \ + $(INSTALL_DATA) system.spec system.dynspec system_smoke.spec \ $(ERL_FILES) $(TARGET_FILES) "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" tar cf - *_SUITE_data utils | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/erts/test/system_smoke.spec b/erts/test/system_smoke.spec new file mode 100644 index 0000000000..933d1ba22d --- /dev/null +++ b/erts/test/system_smoke.spec @@ -0,0 +1,3 @@ +{suites,"../system_test",[ethread_SUITE]}. +{cases,"../system_test",otp_SUITE,[undefined_functions]}. +{skip_cases,"../system_test",ethread_SUITE,[max_threads],"Skip"}. -- cgit v1.2.3 From 7a0cc794dddd65ef825571056b71e3fca7f4f315 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Mon, 15 Jul 2013 11:23:38 +0200 Subject: Add erts_prefix to pcre_library and update erl_bif_re --- erts/emulator/beam/erl_bif_re.c | 2 +- erts/emulator/pcre/pcre.h | 149 +++++++++++++++++++++++++++++++++- erts/emulator/pcre/pcre_byte_order.c | 5 ++ erts/emulator/pcre/pcre_compile.c | 17 ++++ erts/emulator/pcre/pcre_config.c | 5 ++ erts/emulator/pcre/pcre_dfa_exec.c | 7 ++ erts/emulator/pcre/pcre_exec.c | 9 +- erts/emulator/pcre/pcre_fullinfo.c | 6 ++ erts/emulator/pcre/pcre_get.c | 89 ++++++++++++++++++++ erts/emulator/pcre/pcre_internal.h | 9 ++ erts/emulator/pcre/pcre_jit_compile.c | 37 +++++++++ erts/emulator/pcre/pcre_maketables.c | 5 ++ erts/emulator/pcre/pcre_refcount.c | 5 ++ erts/emulator/pcre/pcre_study.c | 20 +++++ erts/emulator/pcre/pcre_version.c | 5 ++ 15 files changed, 365 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index 3d34c2a77f..4289c79ba2 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -492,7 +492,7 @@ typedef struct _return_info { } ReturnInfo; typedef struct _restart_context { - pcre_extra extra; + erts_pcre_extra extra; void *restart_data; Uint32 flags; char *subject; /* to be able to free it when done */ diff --git a/erts/emulator/pcre/pcre.h b/erts/emulator/pcre/pcre.h index 9a056a15ee..57efdd01f5 100644 --- a/erts/emulator/pcre/pcre.h +++ b/erts/emulator/pcre/pcre.h @@ -375,7 +375,11 @@ const char *. */ such as way as to be extensible. Always add new fields at the end, in order to remain compatible. */ +#if defined(ERLANG_INTEGRATION) +typedef struct erts_pcre_extra { +#else typedef struct pcre_extra { +#endif unsigned long int flags; /* Bits for which fields are set */ void *study_data; /* Opaque data from pcre_study() */ unsigned long int match_limit; /* Maximum number of calls to match() */ @@ -389,8 +393,10 @@ typedef struct pcre_extra { unsigned long *loop_counter_return; void **restart_data; /* in/out */ int restart_flags; -#endif +} erts_pcre_extra; +#else } pcre_extra; +#endif /* Same structure as above, but with 16 bit char pointers. */ @@ -423,7 +429,11 @@ structure so that new fields can be added on the end in future versions, without changing the API of the function, thereby allowing old clients to work without modification. */ +#if defined(ERLANG_INTEGRATION) +typedef struct erts_pcre_callout_block { +#else typedef struct pcre_callout_block { +#endif int version; /* Identifies version of block */ /* ------------------------ Version 0 ------------------------------- */ int callout_number; /* Number compiled into pattern */ @@ -441,8 +451,11 @@ typedef struct pcre_callout_block { /* ------------------- Added for Version 2 -------------------------- */ const unsigned char *mark; /* Pointer to current mark or NULL */ /* ------------------------------------------------------------------ */ +#if defined(ERLANG_INTEGRATION) +} erts_pcre_callout_block; +#else } pcre_callout_block; - +#endif /* Same structure as above, but with 16 bit char pointers. */ typedef struct pcre16_callout_block { @@ -494,12 +507,19 @@ that is triggered by the (?) regex item. For Virtual Pascal, these definitions have to take another form. */ #ifndef VPCOMPAT +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL void *(*erts_pcre_malloc)(size_t); +PCRE_EXP_DECL void (*erts_pcre_free)(void *); +PCRE_EXP_DECL void *(*erts_pcre_stack_malloc)(size_t); +PCRE_EXP_DECL void (*erts_pcre_stack_free)(void *); +PCRE_EXP_DECL int (*erts_pcre_callout)(erts_pcre_callout_block *); +#else PCRE_EXP_DECL void *(*pcre_malloc)(size_t); PCRE_EXP_DECL void (*pcre_free)(void *); PCRE_EXP_DECL void *(*pcre_stack_malloc)(size_t); PCRE_EXP_DECL void (*pcre_stack_free)(void *); PCRE_EXP_DECL int (*pcre_callout)(pcre_callout_block *); - +#endif PCRE_EXP_DECL void *(*pcre16_malloc)(size_t); PCRE_EXP_DECL void (*pcre16_free)(void *); PCRE_EXP_DECL void *(*pcre16_stack_malloc)(size_t); @@ -512,11 +532,19 @@ PCRE_EXP_DECL void *(*pcre32_stack_malloc)(size_t); PCRE_EXP_DECL void (*pcre32_stack_free)(void *); PCRE_EXP_DECL int (*pcre32_callout)(pcre32_callout_block *); #else /* VPCOMPAT */ +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL void *erts_pcre_malloc(size_t); +PCRE_EXP_DECL void erts_pcre_free(void *); +PCRE_EXP_DECL void *erts_pcre_stack_malloc(size_t); +PCRE_EXP_DECL void erts_pcre_stack_free(void *); +PCRE_EXP_DECL int erts_pcre_callout(erts_pcre_callout_block *); +#else PCRE_EXP_DECL void *pcre_malloc(size_t); PCRE_EXP_DECL void pcre_free(void *); PCRE_EXP_DECL void *pcre_stack_malloc(size_t); PCRE_EXP_DECL void pcre_stack_free(void *); PCRE_EXP_DECL int pcre_callout(pcre_callout_block *); +#endif PCRE_EXP_DECL void *pcre16_malloc(size_t); PCRE_EXP_DECL void pcre16_free(void *); @@ -539,112 +567,214 @@ typedef pcre32_jit_stack *(*pcre32_jit_callback)(void *); /* Exported PCRE functions */ +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL pcre *erts_pcre_compile(const char *, int, const char **, int *, + const unsigned char *); +#else PCRE_EXP_DECL pcre *pcre_compile(const char *, int, const char **, int *, const unsigned char *); +#endif PCRE_EXP_DECL pcre16 *pcre16_compile(PCRE_SPTR16, int, const char **, int *, const unsigned char *); PCRE_EXP_DECL pcre32 *pcre32_compile(PCRE_SPTR32, int, const char **, int *, const unsigned char *); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL pcre *erts_pcre_compile2(const char *, int, int *, const char **, + int *, const unsigned char *); +#else PCRE_EXP_DECL pcre *pcre_compile2(const char *, int, int *, const char **, int *, const unsigned char *); +#endif PCRE_EXP_DECL pcre16 *pcre16_compile2(PCRE_SPTR16, int, int *, const char **, int *, const unsigned char *); PCRE_EXP_DECL pcre32 *pcre32_compile2(PCRE_SPTR32, int, int *, const char **, int *, const unsigned char *); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_config(int, void *); +#else PCRE_EXP_DECL int pcre_config(int, void *); +#endif PCRE_EXP_DECL int pcre16_config(int, void *); PCRE_EXP_DECL int pcre32_config(int, void *); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_copy_named_substring(const pcre *, const char *, + int *, int, const char *, char *, int); +#else PCRE_EXP_DECL int pcre_copy_named_substring(const pcre *, const char *, int *, int, const char *, char *, int); +#endif PCRE_EXP_DECL int pcre16_copy_named_substring(const pcre16 *, PCRE_SPTR16, int *, int, PCRE_SPTR16, PCRE_UCHAR16 *, int); PCRE_EXP_DECL int pcre32_copy_named_substring(const pcre32 *, PCRE_SPTR32, int *, int, PCRE_SPTR32, PCRE_UCHAR32 *, int); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_copy_substring(const char *, int *, int, int, + char *, int); +#else PCRE_EXP_DECL int pcre_copy_substring(const char *, int *, int, int, char *, int); +#endif PCRE_EXP_DECL int pcre16_copy_substring(PCRE_SPTR16, int *, int, int, PCRE_UCHAR16 *, int); PCRE_EXP_DECL int pcre32_copy_substring(PCRE_SPTR32, int *, int, int, PCRE_UCHAR32 *, int); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_dfa_exec(const pcre *, const erts_pcre_extra *, + const char *, int, int, int, int *, int , int *, int); +#else PCRE_EXP_DECL int pcre_dfa_exec(const pcre *, const pcre_extra *, const char *, int, int, int, int *, int , int *, int); +#endif PCRE_EXP_DECL int pcre16_dfa_exec(const pcre16 *, const pcre16_extra *, PCRE_SPTR16, int, int, int, int *, int , int *, int); PCRE_EXP_DECL int pcre32_dfa_exec(const pcre32 *, const pcre32_extra *, PCRE_SPTR32, int, int, int, int *, int , int *, int); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_exec(const pcre *, const erts_pcre_extra *, PCRE_SPTR, + int, int, int, int *, int); +#else PCRE_EXP_DECL int pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR, int, int, int, int *, int); +#endif PCRE_EXP_DECL int pcre16_exec(const pcre16 *, const pcre16_extra *, PCRE_SPTR16, int, int, int, int *, int); PCRE_EXP_DECL int pcre32_exec(const pcre32 *, const pcre32_extra *, PCRE_SPTR32, int, int, int, int *, int); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_jit_exec(const pcre *, const erts_pcre_extra *, + PCRE_SPTR, int, int, int, int *, int, + pcre_jit_stack *); +#else PCRE_EXP_DECL int pcre_jit_exec(const pcre *, const pcre_extra *, PCRE_SPTR, int, int, int, int *, int, pcre_jit_stack *); +#endif PCRE_EXP_DECL int pcre16_jit_exec(const pcre16 *, const pcre16_extra *, PCRE_SPTR16, int, int, int, int *, int, pcre16_jit_stack *); PCRE_EXP_DECL int pcre32_jit_exec(const pcre32 *, const pcre32_extra *, PCRE_SPTR32, int, int, int, int *, int, pcre32_jit_stack *); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL void erts_pcre_free_substring(const char *); +#else PCRE_EXP_DECL void pcre_free_substring(const char *); +#endif PCRE_EXP_DECL void pcre16_free_substring(PCRE_SPTR16); PCRE_EXP_DECL void pcre32_free_substring(PCRE_SPTR32); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL void erts_pcre_free_substring_list(const char **); +#else PCRE_EXP_DECL void pcre_free_substring_list(const char **); +#endif PCRE_EXP_DECL void pcre16_free_substring_list(PCRE_SPTR16 *); PCRE_EXP_DECL void pcre32_free_substring_list(PCRE_SPTR32 *); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_fullinfo(const pcre *, const erts_pcre_extra *, int, + void *); +#else PCRE_EXP_DECL int pcre_fullinfo(const pcre *, const pcre_extra *, int, void *); +#endif PCRE_EXP_DECL int pcre16_fullinfo(const pcre16 *, const pcre16_extra *, int, void *); PCRE_EXP_DECL int pcre32_fullinfo(const pcre32 *, const pcre32_extra *, int, void *); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_get_named_substring(const pcre *, const char *, + int *, int, const char *, const char **); +#else PCRE_EXP_DECL int pcre_get_named_substring(const pcre *, const char *, int *, int, const char *, const char **); +#endif PCRE_EXP_DECL int pcre16_get_named_substring(const pcre16 *, PCRE_SPTR16, int *, int, PCRE_SPTR16, PCRE_SPTR16 *); PCRE_EXP_DECL int pcre32_get_named_substring(const pcre32 *, PCRE_SPTR32, int *, int, PCRE_SPTR32, PCRE_SPTR32 *); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_get_stringnumber(const pcre *, const char *); +#else PCRE_EXP_DECL int pcre_get_stringnumber(const pcre *, const char *); +#endif PCRE_EXP_DECL int pcre16_get_stringnumber(const pcre16 *, PCRE_SPTR16); PCRE_EXP_DECL int pcre32_get_stringnumber(const pcre32 *, PCRE_SPTR32); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_get_stringtable_entries(const pcre *, const char *, + char **, char **); +#else PCRE_EXP_DECL int pcre_get_stringtable_entries(const pcre *, const char *, char **, char **); +#endif PCRE_EXP_DECL int pcre16_get_stringtable_entries(const pcre16 *, PCRE_SPTR16, PCRE_UCHAR16 **, PCRE_UCHAR16 **); PCRE_EXP_DECL int pcre32_get_stringtable_entries(const pcre32 *, PCRE_SPTR32, PCRE_UCHAR32 **, PCRE_UCHAR32 **); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_get_substring(const char *, int *, int, int, + const char **); +#else PCRE_EXP_DECL int pcre_get_substring(const char *, int *, int, int, const char **); +#endif PCRE_EXP_DECL int pcre16_get_substring(PCRE_SPTR16, int *, int, int, PCRE_SPTR16 *); PCRE_EXP_DECL int pcre32_get_substring(PCRE_SPTR32, int *, int, int, PCRE_SPTR32 *); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_get_substring_list(const char *, int *, int, + const char ***); +#else PCRE_EXP_DECL int pcre_get_substring_list(const char *, int *, int, const char ***); +#endif PCRE_EXP_DECL int pcre16_get_substring_list(PCRE_SPTR16, int *, int, PCRE_SPTR16 **); PCRE_EXP_DECL int pcre32_get_substring_list(PCRE_SPTR32, int *, int, PCRE_SPTR32 **); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL const unsigned char *erts_pcre_maketables(void); +#else PCRE_EXP_DECL const unsigned char *pcre_maketables(void); +#endif PCRE_EXP_DECL const unsigned char *pcre16_maketables(void); PCRE_EXP_DECL const unsigned char *pcre32_maketables(void); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_refcount(pcre *, int); +#else PCRE_EXP_DECL int pcre_refcount(pcre *, int); +#endif PCRE_EXP_DECL int pcre16_refcount(pcre16 *, int); PCRE_EXP_DECL int pcre32_refcount(pcre32 *, int); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL erts_pcre_extra *erts_pcre_study(const pcre *, int, const char **); +#else PCRE_EXP_DECL pcre_extra *pcre_study(const pcre *, int, const char **); +#endif PCRE_EXP_DECL pcre16_extra *pcre16_study(const pcre16 *, int, const char **); PCRE_EXP_DECL pcre32_extra *pcre32_study(const pcre32 *, int, const char **); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL void erts_pcre_free_study(erts_pcre_extra *); +#else PCRE_EXP_DECL void pcre_free_study(pcre_extra *); +#endif PCRE_EXP_DECL void pcre16_free_study(pcre16_extra *); PCRE_EXP_DECL void pcre32_free_study(pcre32_extra *); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL const char *erts_pcre_version(void); +#else PCRE_EXP_DECL const char *pcre_version(void); +#endif PCRE_EXP_DECL const char *pcre16_version(void); PCRE_EXP_DECL const char *pcre32_version(void); /* Utility functions for byte order swaps. */ +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_pattern_to_host_byte_order(pcre *, erts_pcre_extra *, + const unsigned char *); +#else PCRE_EXP_DECL int pcre_pattern_to_host_byte_order(pcre *, pcre_extra *, const unsigned char *); +#endif PCRE_EXP_DECL int pcre16_pattern_to_host_byte_order(pcre16 *, pcre16_extra *, const unsigned char *); PCRE_EXP_DECL int pcre32_pattern_to_host_byte_order(pcre32 *, pcre32_extra *, @@ -656,14 +786,27 @@ PCRE_EXP_DECL int pcre32_utf32_to_host_byte_order(PCRE_UCHAR32 *, /* JIT compiler related functions. */ +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL pcre_jit_stack *erts_pcre_jit_stack_alloc(int, int); +#else PCRE_EXP_DECL pcre_jit_stack *pcre_jit_stack_alloc(int, int); +#endif PCRE_EXP_DECL pcre16_jit_stack *pcre16_jit_stack_alloc(int, int); PCRE_EXP_DECL pcre32_jit_stack *pcre32_jit_stack_alloc(int, int); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL void erts_pcre_jit_stack_free(pcre_jit_stack *); +#else PCRE_EXP_DECL void pcre_jit_stack_free(pcre_jit_stack *); +#endif PCRE_EXP_DECL void pcre16_jit_stack_free(pcre16_jit_stack *); PCRE_EXP_DECL void pcre32_jit_stack_free(pcre32_jit_stack *); +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL void erts_pcre_assign_jit_stack(erts_pcre_extra *, + pcre_jit_callback, void *); +#else PCRE_EXP_DECL void pcre_assign_jit_stack(pcre_extra *, pcre_jit_callback, void *); +#endif PCRE_EXP_DECL void pcre16_assign_jit_stack(pcre16_extra *, pcre16_jit_callback, void *); PCRE_EXP_DECL void pcre32_assign_jit_stack(pcre32_extra *, diff --git a/erts/emulator/pcre/pcre_byte_order.c b/erts/emulator/pcre/pcre_byte_order.c index 7f8457742d..710676988f 100644 --- a/erts/emulator/pcre/pcre_byte_order.c +++ b/erts/emulator/pcre/pcre_byte_order.c @@ -96,8 +96,13 @@ Returns: 0 if the swap is successful, negative on error */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL int erts_pcre_pattern_to_host_byte_order(pcre *argument_re, + erts_pcre_extra *extra_data, const unsigned char *tables) +#else PCRE_EXP_DECL int pcre_pattern_to_host_byte_order(pcre *argument_re, pcre_extra *extra_data, const unsigned char *tables) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DECL int pcre16_pattern_to_host_byte_order(pcre16 *argument_re, pcre16_extra *extra_data, const unsigned char *tables) diff --git a/erts/emulator/pcre/pcre_compile.c b/erts/emulator/pcre/pcre_compile.c index 5652bad0ce..d48126a55d 100644 --- a/erts/emulator/pcre/pcre_compile.c +++ b/erts/emulator/pcre/pcre_compile.c @@ -7728,9 +7728,15 @@ Returns: pointer to compiled data block, or NULL on error, */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION +erts_pcre_compile(const char *pattern, int options, const char **errorptr, + int *erroroffset, const unsigned char *tables) +#else PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION pcre_compile(const char *pattern, int options, const char **errorptr, int *erroroffset, const unsigned char *tables) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN pcre16 * PCRE_CALL_CONVENTION pcre16_compile(PCRE_SPTR16 pattern, int options, const char **errorptr, @@ -7742,7 +7748,12 @@ pcre32_compile(PCRE_SPTR32 pattern, int options, const char **errorptr, #endif { #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +return erts_pcre_compile2(pattern, options, NULL, errorptr, + erroroffset, tables); +#else return pcre_compile2(pattern, options, NULL, errorptr, erroroffset, tables); +#endif #elif defined COMPILE_PCRE16 return pcre16_compile2(pattern, options, NULL, errorptr, erroroffset, tables); #elif defined COMPILE_PCRE32 @@ -7752,9 +7763,15 @@ return pcre32_compile2(pattern, options, NULL, errorptr, erroroffset, tables); #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION +erts_pcre_compile2(const char *pattern, int options, int *errorcodeptr, + const char **errorptr, int *erroroffset, const unsigned char *tables) +#else PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION pcre_compile2(const char *pattern, int options, int *errorcodeptr, const char **errorptr, int *erroroffset, const unsigned char *tables) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN pcre16 * PCRE_CALL_CONVENTION pcre16_compile2(PCRE_SPTR16 pattern, int options, int *errorcodeptr, diff --git a/erts/emulator/pcre/pcre_config.c b/erts/emulator/pcre/pcre_config.c index a116cc7ac6..06fa3d324f 100644 --- a/erts/emulator/pcre/pcre_config.c +++ b/erts/emulator/pcre/pcre_config.c @@ -71,8 +71,13 @@ Returns: 0 if data returned, negative on error */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_config(int what, void *where) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_config(int what, void *where) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_config(int what, void *where) diff --git a/erts/emulator/pcre/pcre_dfa_exec.c b/erts/emulator/pcre/pcre_dfa_exec.c index 9e00552163..f5718a3b33 100644 --- a/erts/emulator/pcre/pcre_dfa_exec.c +++ b/erts/emulator/pcre/pcre_dfa_exec.c @@ -3099,10 +3099,17 @@ Returns: > 0 => number of match offset pairs placed in offsets */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_dfa_exec(const pcre *argument_re, const erts_pcre_extra *extra_data, + const char *subject, int length, int start_offset, int options, int *offsets, + int offsetcount, int *workspace, int wscount) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_dfa_exec(const pcre *argument_re, const pcre_extra *extra_data, const char *subject, int length, int start_offset, int options, int *offsets, int offsetcount, int *workspace, int wscount) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_dfa_exec(const pcre16 *argument_re, const pcre16_extra *extra_data, diff --git a/erts/emulator/pcre/pcre_exec.c b/erts/emulator/pcre/pcre_exec.c index 54c0fe59a1..0371f9efa8 100644 --- a/erts/emulator/pcre/pcre_exec.c +++ b/erts/emulator/pcre/pcre_exec.c @@ -6681,10 +6681,17 @@ typedef struct { #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_exec(const pcre *argument_re, const erts_pcre_extra *extra_data, + PCRE_SPTR subject, int length, int start_offset, int options, int *offsets, + int offsetcount) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_exec(const pcre *argument_re, const pcre_extra *extra_data, PCRE_SPTR subject, int length, int start_offset, int options, int *offsets, int offsetcount) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_exec(const pcre16 *argument_re, const pcre16_extra *extra_data, @@ -6804,7 +6811,7 @@ pcre_uchar req_char; } else { if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_LOOP_LIMIT)) { - exec_context = (PcreExecContext *) (PUBL(malloc))(sizeof(PcreExecContext)); + exec_context = (PcreExecContext *) (erts_pcre_malloc)(sizeof(PcreExecContext)); *(extra_data->restart_data) = (void *) exec_context; /* need freeing by special routine from client */ } else { diff --git a/erts/emulator/pcre/pcre_fullinfo.c b/erts/emulator/pcre/pcre_fullinfo.c index 80a080a758..1636c5978f 100644 --- a/erts/emulator/pcre/pcre_fullinfo.c +++ b/erts/emulator/pcre/pcre_fullinfo.c @@ -67,9 +67,15 @@ Returns: 0 if data returned, negative on error */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_fullinfo(const pcre *argument_re, const erts_pcre_extra *extra_data, + int what, void *where) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_fullinfo(const pcre *argument_re, const pcre_extra *extra_data, int what, void *where) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_fullinfo(const pcre16 *argument_re, const pcre16_extra *extra_data, diff --git a/erts/emulator/pcre/pcre_get.c b/erts/emulator/pcre/pcre_get.c index cc669ead38..c0bb21bbbf 100644 --- a/erts/emulator/pcre/pcre_get.c +++ b/erts/emulator/pcre/pcre_get.c @@ -67,8 +67,13 @@ Returns: the number of the named parentheses, or a negative number */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_get_stringnumber(const pcre *code, const char *stringname) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_get_stringnumber(const pcre *code, const char *stringname) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_get_stringnumber(const pcre16 *code, PCRE_SPTR16 stringname) @@ -83,6 +88,16 @@ int top, bot; pcre_uchar *nametable; #ifdef COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; + +if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; +#else if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) return rc; if (top <= 0) return PCRE_ERROR_NOSUBSTRING; @@ -92,6 +107,7 @@ if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) return rc; #endif +#endif #ifdef COMPILE_PCRE16 if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) return rc; @@ -147,9 +163,15 @@ Returns: the length of each entry, or a negative number */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_get_stringtable_entries(const pcre *code, const char *stringname, + char **firstptr, char **lastptr) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_get_stringtable_entries(const pcre *code, const char *stringname, char **firstptr, char **lastptr) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_get_stringtable_entries(const pcre16 *code, PCRE_SPTR16 stringname, @@ -166,6 +188,16 @@ int top, bot; pcre_uchar *nametable, *lastentry; #ifdef COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; + +if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; +#else if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) return rc; if (top <= 0) return PCRE_ERROR_NOSUBSTRING; @@ -175,6 +207,7 @@ if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) return rc; #endif +#endif #ifdef COMPILE_PCRE16 if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) return rc; @@ -280,9 +313,15 @@ PCRE_UCHAR32 *first, *last; #endif #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +if ((re->options & PCRE_DUPNAMES) == 0 && (re->flags & PCRE_JCHANGED) == 0) + return erts_pcre_get_stringnumber(code, stringname); +entrysize = erts_pcre_get_stringtable_entries(code, stringname, &first, &last); +#else if ((re->options & PCRE_DUPNAMES) == 0 && (re->flags & PCRE_JCHANGED) == 0) return pcre_get_stringnumber(code, stringname); entrysize = pcre_get_stringtable_entries(code, stringname, &first, &last); +#endif #elif defined COMPILE_PCRE16 if ((re->options & PCRE_DUPNAMES) == 0 && (re->flags & PCRE_JCHANGED) == 0) return pcre16_get_stringnumber(code, stringname); @@ -332,9 +371,15 @@ Returns: if successful: */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_copy_substring(const char *subject, int *ovector, int stringcount, + int stringnumber, char *buffer, int size) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_copy_substring(const char *subject, int *ovector, int stringcount, int stringnumber, char *buffer, int size) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_copy_substring(PCRE_SPTR16 subject, int *ovector, int stringcount, @@ -387,10 +432,17 @@ Returns: if successful: */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_copy_named_substring(const pcre *code, const char *subject, + int *ovector, int stringcount, const char *stringname, + char *buffer, int size) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_copy_named_substring(const pcre *code, const char *subject, int *ovector, int stringcount, const char *stringname, char *buffer, int size) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_copy_named_substring(const pcre16 *code, PCRE_SPTR16 subject, @@ -406,7 +458,11 @@ pcre32_copy_named_substring(const pcre32 *code, PCRE_SPTR32 subject, int n = get_first_set(code, stringname, ovector); if (n <= 0) return n; #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +return erts_pcre_copy_substring(subject, ovector, stringcount, n, buffer, size); +#else return pcre_copy_substring(subject, ovector, stringcount, n, buffer, size); +#endif #elif defined COMPILE_PCRE16 return pcre16_copy_substring(subject, ovector, stringcount, n, buffer, size); #elif defined COMPILE_PCRE32 @@ -438,9 +494,15 @@ Returns: if successful: 0 */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_get_substring_list(const char *subject, int *ovector, int stringcount, + const char ***listptr) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_get_substring_list(const char *subject, int *ovector, int stringcount, const char ***listptr) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_get_substring_list(PCRE_SPTR16 subject, int *ovector, int stringcount, @@ -500,8 +562,13 @@ Returns: nothing */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +erts_pcre_free_substring_list(const char **pointer) +#else PCRE_EXP_DEFN void PCRE_CALL_CONVENTION pcre_free_substring_list(const char **pointer) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN void PCRE_CALL_CONVENTION pcre16_free_substring_list(PCRE_SPTR16 *pointer) @@ -541,9 +608,15 @@ Returns: if successful: */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_get_substring(const char *subject, int *ovector, int stringcount, + int stringnumber, const char **stringptr) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_get_substring(const char *subject, int *ovector, int stringcount, int stringnumber, const char **stringptr) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_get_substring(PCRE_SPTR16 subject, int *ovector, int stringcount, @@ -604,10 +677,17 @@ Returns: if successful: */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_get_named_substring(const pcre *code, const char *subject, + int *ovector, int stringcount, const char *stringname, + const char **stringptr) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_get_named_substring(const pcre *code, const char *subject, int *ovector, int stringcount, const char *stringname, const char **stringptr) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_get_named_substring(const pcre16 *code, PCRE_SPTR16 subject, @@ -623,7 +703,11 @@ pcre32_get_named_substring(const pcre32 *code, PCRE_SPTR32 subject, int n = get_first_set(code, stringname, ovector); if (n <= 0) return n; #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +return erts_pcre_get_substring(subject, ovector, stringcount, n, stringptr); +#else return pcre_get_substring(subject, ovector, stringcount, n, stringptr); +#endif #elif defined COMPILE_PCRE16 return pcre16_get_substring(subject, ovector, stringcount, n, stringptr); #elif defined COMPILE_PCRE32 @@ -647,8 +731,13 @@ Returns: nothing */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +erts_pcre_free_substring(const char *pointer) +#else PCRE_EXP_DEFN void PCRE_CALL_CONVENTION pcre_free_substring(const char *pointer) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN void PCRE_CALL_CONVENTION pcre16_free_substring(PCRE_SPTR16 pointer) diff --git a/erts/emulator/pcre/pcre_internal.h b/erts/emulator/pcre/pcre_internal.h index 1304a13c5d..af436bd99b 100644 --- a/erts/emulator/pcre/pcre_internal.h +++ b/erts/emulator/pcre/pcre_internal.h @@ -2600,12 +2600,21 @@ total length. */ /* Internal function and data prefixes. */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +#ifndef PUBL +#define PUBL(name) erts_pcre_##name +#endif +#ifndef PRIV +#define PRIV(name) _erts_pcre_##name +#endif +#else #ifndef PUBL #define PUBL(name) pcre_##name #endif #ifndef PRIV #define PRIV(name) _pcre_##name #endif +#endif #elif defined COMPILE_PCRE16 #ifndef PUBL #define PUBL(name) pcre16_##name diff --git a/erts/emulator/pcre/pcre_jit_compile.c b/erts/emulator/pcre/pcre_jit_compile.c index d0090d0e14..fb26c62817 100644 --- a/erts/emulator/pcre/pcre_jit_compile.c +++ b/erts/emulator/pcre/pcre_jit_compile.c @@ -9528,10 +9528,17 @@ return retval; } #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_jit_exec(const pcre *argument_re, const pcre_extra *extra_data, + PCRE_SPTR subject, int length, int start_offset, int options, + int *offsets, int offset_count, pcre_jit_stack *stack) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_jit_exec(const pcre *argument_re, const pcre_extra *extra_data, PCRE_SPTR subject, int length, int start_offset, int options, int *offsets, int offset_count, pcre_jit_stack *stack) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_jit_exec(const pcre16 *argument_re, const pcre16_extra *extra_data, @@ -9641,8 +9648,13 @@ return sljit_get_platform_name(); } #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL pcre_jit_stack * +erts_pcre_jit_stack_alloc(int startsize, int maxsize) +#else PCRE_EXP_DECL pcre_jit_stack * pcre_jit_stack_alloc(int startsize, int maxsize) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DECL pcre16_jit_stack * pcre16_jit_stack_alloc(int startsize, int maxsize) @@ -9661,8 +9673,13 @@ return (PUBL(jit_stack)*)sljit_allocate_stack(startsize, maxsize); } #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL void +erts_pcre_jit_stack_free(pcre_jit_stack *stack) +#else PCRE_EXP_DECL void pcre_jit_stack_free(pcre_jit_stack *stack) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DECL void pcre16_jit_stack_free(pcre16_jit_stack *stack) @@ -9675,8 +9692,13 @@ sljit_free_stack((struct sljit_stack *)stack); } #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL void +erts_pcre_assign_jit_stack(pcre_extra *extra, pcre_jit_callback callback, void *userdata) +#else PCRE_EXP_DECL void pcre_assign_jit_stack(pcre_extra *extra, pcre_jit_callback callback, void *userdata) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DECL void pcre16_assign_jit_stack(pcre16_extra *extra, pcre16_jit_callback callback, void *userdata) @@ -9702,8 +9724,13 @@ if (extra != NULL && being compiled. */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL pcre_jit_stack * +erts_pcre_jit_stack_alloc(int startsize, int maxsize) +#else PCRE_EXP_DECL pcre_jit_stack * pcre_jit_stack_alloc(int startsize, int maxsize) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DECL pcre16_jit_stack * pcre16_jit_stack_alloc(int startsize, int maxsize) @@ -9718,8 +9745,13 @@ return NULL; } #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL void +erts_pcre_jit_stack_free(pcre_jit_stack *stack) +#else PCRE_EXP_DECL void pcre_jit_stack_free(pcre_jit_stack *stack) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DECL void pcre16_jit_stack_free(pcre16_jit_stack *stack) @@ -9732,8 +9764,13 @@ pcre32_jit_stack_free(pcre32_jit_stack *stack) } #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DECL void +erts_pcre_assign_jit_stack(erts_pcre_extra *extra, pcre_jit_callback callback, void *userdata) +#else PCRE_EXP_DECL void pcre_assign_jit_stack(pcre_extra *extra, pcre_jit_callback callback, void *userdata) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DECL void pcre16_assign_jit_stack(pcre16_extra *extra, pcre16_jit_callback callback, void *userdata) diff --git a/erts/emulator/pcre/pcre_maketables.c b/erts/emulator/pcre/pcre_maketables.c index 7c4d58d558..9310d886fa 100644 --- a/erts/emulator/pcre/pcre_maketables.c +++ b/erts/emulator/pcre/pcre_maketables.c @@ -68,8 +68,13 @@ Returns: pointer to the contiguous block of data */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +const unsigned char * +erts_pcre_maketables(void) +#else const unsigned char * pcre_maketables(void) +#endif #elif defined COMPILE_PCRE16 const unsigned char * pcre16_maketables(void) diff --git a/erts/emulator/pcre/pcre_refcount.c b/erts/emulator/pcre/pcre_refcount.c index 5b2af2f47b..33d5a528af 100644 --- a/erts/emulator/pcre/pcre_refcount.c +++ b/erts/emulator/pcre/pcre_refcount.c @@ -70,8 +70,13 @@ Returns: the (possibly updated) count value (a non-negative number), or */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +erts_pcre_refcount(pcre *argument_re, int adjust) +#else PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre_refcount(pcre *argument_re, int adjust) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION pcre16_refcount(pcre16 *argument_re, int adjust) diff --git a/erts/emulator/pcre/pcre_study.c b/erts/emulator/pcre/pcre_study.c index a85163094f..3d8961ffb0 100644 --- a/erts/emulator/pcre/pcre_study.c +++ b/erts/emulator/pcre/pcre_study.c @@ -1327,8 +1327,13 @@ Returns: pointer to a pcre[16]_extra block, with study_data filled in and */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN erts_pcre_extra * PCRE_CALL_CONVENTION +erts_pcre_study(const pcre *external_re, int options, const char **errorptr) +#else PCRE_EXP_DEFN pcre_extra * PCRE_CALL_CONVENTION pcre_study(const pcre *external_re, int options, const char **errorptr) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN pcre16_extra * PCRE_CALL_CONVENTION pcre16_study(const pcre16 *external_re, int options, const char **errorptr) @@ -1390,9 +1395,15 @@ if ((re->options & PCRE_ANCHORED) == 0 && tables = re->tables; #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) + if (tables == NULL) + (void)erts_pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, + (void *)(&tables)); +#else if (tables == NULL) (void)pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, (void *)(&tables)); +#endif #elif defined COMPILE_PCRE16 if (tables == NULL) (void)pcre16_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, @@ -1514,7 +1525,11 @@ if (bits_set || min > 0 || (options & ( (options & PCRE_STUDY_EXTRA_NEEDED) == 0) { #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) + erts_pcre_free_study(extra); +#else pcre_free_study(extra); +#endif #elif defined COMPILE_PCRE16 pcre16_free_study(extra); #elif defined COMPILE_PCRE32 @@ -1540,8 +1555,13 @@ Returns: nothing */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN void +erts_pcre_free_study(erts_pcre_extra *extra) +#else PCRE_EXP_DEFN void pcre_free_study(pcre_extra *extra) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN void pcre16_free_study(pcre16_extra *extra) diff --git a/erts/emulator/pcre/pcre_version.c b/erts/emulator/pcre/pcre_version.c index f771b3e82b..cc4485e41f 100644 --- a/erts/emulator/pcre/pcre_version.c +++ b/erts/emulator/pcre/pcre_version.c @@ -81,8 +81,13 @@ pre-processor time. This hack uses a standard trick for avoiding calling the STRING macro with an empty argument when doing the test. */ #if defined COMPILE_PCRE8 +#if defined(ERLANG_INTEGRATION) +PCRE_EXP_DEFN const char * PCRE_CALL_CONVENTION +erts_pcre_version(void) +#else PCRE_EXP_DEFN const char * PCRE_CALL_CONVENTION pcre_version(void) +#endif #elif defined COMPILE_PCRE16 PCRE_EXP_DEFN const char * PCRE_CALL_CONVENTION pcre16_version(void) -- cgit v1.2.3 From 390ee65ba78d571fdc0a8fccb6c456d7346eb9bc Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 19 Jul 2013 17:51:11 +0200 Subject: Handle CRLF correctly in global regexp --- erts/emulator/beam/erl_bif_re.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index 4289c79ba2..12fc834685 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -379,6 +379,8 @@ build_compile_result(Process *p, Eterm error_tag, pcre *result, int errcode, con Eterm ret; size_t pattern_size; int capture_count; + int use_crlf; + unsigned long options; if (!result) { /* Return {error_tag, {Code, String, Offset}} */ int elen = sys_strlen(errstr); @@ -393,14 +395,20 @@ build_compile_result(Process *p, Eterm error_tag, pcre *result, int errcode, con } else { erts_pcre_fullinfo(result, NULL, PCRE_INFO_SIZE, &pattern_size); erts_pcre_fullinfo(result, NULL, PCRE_INFO_CAPTURECOUNT, &capture_count); + erts_pcre_fullinfo(result, NULL, PCRE_INFO_OPTIONS, &options); + options &= PCRE_NEWLINE_CR|PCRE_NEWLINE_LF | PCRE_NEWLINE_CRLF | + PCRE_NEWLINE_ANY | PCRE_NEWLINE_ANYCRLF; + use_crlf = (options == PCRE_NEWLINE_ANY || + options == PCRE_NEWLINE_CRLF || + options == PCRE_NEWLINE_ANYCRLF); /* XXX: Optimize - keep in offheap binary to allow this to be kept across traps w/o need of copying */ ret = new_binary(p, (byte *) result, pattern_size); erts_pcre_free(result); - hp = HAlloc(p, (with_ok) ? (3+5) : 5); - ret = TUPLE4(hp,am_re_pattern, make_small(capture_count), make_small(unicode),ret); + hp = HAlloc(p, (with_ok) ? (3+6) : 6); + ret = TUPLE5(hp,am_re_pattern, make_small(capture_count), make_small(unicode),make_small(use_crlf),ret); if (with_ok) { - hp += 5; + hp += 6; ret = TUPLE2(hp,am_ok,ret); } } @@ -875,7 +883,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) is_list_cap = ((pflags & PARSE_FLAG_CAPTURE_OPT) && (capture[CAPSPEC_TYPE] == am_list)); - if (is_not_tuple(arg2) || (arityval(*tuple_val(arg2)) != 4)) { + if (is_not_tuple(arg2) || (arityval(*tuple_val(arg2)) != 5)) { if (is_binary(arg2) || is_list(arg2) || is_nil(arg2)) { /* Compile from textual RE */ ErlDrvSizeT slen; @@ -947,7 +955,8 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) tp = tuple_val(arg2); if (tp[1] != am_re_pattern || is_not_small(tp[2]) || - is_not_small(tp[3]) || is_not_binary(tp[4])) { + is_not_small(tp[3]) || is_not_small(tp[4]) || + is_not_binary(tp[5])) { BIF_ERROR(p,BADARG); } @@ -967,9 +976,9 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) } ovsize = 3*(unsigned_val(tp[2])+1); - code_size = binary_size(tp[4]); + code_size = binary_size(tp[5]); if ((code_tmp = (const pcre *) - erts_get_aligned_binary_bytes(tp[4], &temp_alloc)) == NULL) { + erts_get_aligned_binary_bytes(tp[5], &temp_alloc)) == NULL) { erts_free_aligned_binary_bytes(temp_alloc); BIF_ERROR(p, BADARG); } @@ -1055,9 +1064,10 @@ handle_iolist: #ifdef DEBUG loop_count = 0xFFFFFFFF; #endif - - rc = erts_pcre_exec(restart.code, &(restart.extra), restart.subject, slength, startoffset, - options, restart.ovector, ovsize); + + rc = erts_pcre_exec(restart.code, &(restart.extra), restart.subject, + slength, startoffset, + options, restart.ovector, ovsize); ASSERT(loop_count != 0xFFFFFFFF); BUMP_REDS(p, loop_count / LOOP_FACTOR); if (rc == PCRE_ERROR_LOOP_LIMIT) { -- cgit v1.2.3 From 6138bb612cd23c0ca37faadbe3fc9944be275228 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Mon, 29 Jul 2013 10:54:20 +0200 Subject: Integrate patch for PCRE bug id 1370 --- erts/emulator/pcre/pcre-8.33_1370.diff | 60 ++++++++++++++++++++++++++++++++++ erts/emulator/pcre/pcre_exec.c | 38 ++++++++++++++++----- 2 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 erts/emulator/pcre/pcre-8.33_1370.diff (limited to 'erts') diff --git a/erts/emulator/pcre/pcre-8.33_1370.diff b/erts/emulator/pcre/pcre-8.33_1370.diff new file mode 100644 index 0000000000..d62398985d --- /dev/null +++ b/erts/emulator/pcre/pcre-8.33_1370.diff @@ -0,0 +1,60 @@ +--- code/trunk/pcre_exec.c 2013/07/02 18:37:36 1346 ++++ code/trunk/pcre_exec.c 2013/07/26 10:03:38 1350 +@@ -5637,7 +5637,7 @@ + } + } + +- /* Match extended Unicode sequences. We will get here only if the ++ /* Match extended Unicode grapheme clusters. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ + + else if (ctype == OP_EXTUNI) +@@ -5670,21 +5670,41 @@ + /* eptr is now past the end of the maximum run */ + + if (possessive) continue; /* No backtracking */ ++ + for(;;) + { +- if (eptr == pp) goto TAIL_RECURSE; ++ int lgb, rgb; ++ PCRE_PUCHAR fptr; ++ ++ if (eptr == pp) goto TAIL_RECURSE; /* At start of char run */ + RMATCH(eptr, ecode, offset_top, md, eptrb, RM45); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); ++ ++ /* Backtracking over an extended grapheme cluster involves inspecting ++ the previous two characters (if present) to see if a break is ++ permitted between them. */ ++ + eptr--; +- for (;;) /* Move back over one extended */ ++ if (!utf) c = *eptr; else ++ { ++ BACKCHAR(eptr); ++ GETCHAR(c, eptr); ++ } ++ rgb = UCD_GRAPHBREAK(c); ++ ++ for (;;) + { +- if (!utf) c = *eptr; else ++ if (eptr == pp) goto TAIL_RECURSE; /* At start of char run */ ++ fptr = eptr - 1; ++ if (!utf) c = *fptr; else + { +- BACKCHAR(eptr); +- GETCHAR(c, eptr); ++ BACKCHAR(fptr); ++ GETCHAR(c, fptr); + } +- if (UCD_CATEGORY(c) != ucp_M) break; +- eptr--; ++ lgb = UCD_GRAPHBREAK(c); ++ if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; ++ eptr = fptr; ++ rgb = lgb; + } + } + } diff --git a/erts/emulator/pcre/pcre_exec.c b/erts/emulator/pcre/pcre_exec.c index 0371f9efa8..1cab78cdd8 100644 --- a/erts/emulator/pcre/pcre_exec.c +++ b/erts/emulator/pcre/pcre_exec.c @@ -5818,7 +5818,7 @@ for (;;) } } - /* Match extended Unicode sequences. We will get here only if the + /* Match extended Unicode grapheme clusters. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ else if (ctype == OP_EXTUNI) @@ -5855,21 +5855,43 @@ for (;;) /* eptr is now past the end of the maximum run */ if (possessive) continue; /* No backtracking */ + for(;;) /* LOOP_COUNT: Ok */ { - if (eptr == pp) goto TAIL_RECURSE; +#ifndef ERLANG_INTEGRATION + int lgb, rgb; +#endif + PCRE_PUCHAR fptr; + + if (eptr == pp) goto TAIL_RECURSE; /* At start of char run */ RMATCH(eptr, ecode, offset_top, md, eptrb, RM45); if (rrc != MATCH_NOMATCH) RRETURN(rrc); + + /* Backtracking over an extended grapheme cluster involves inspecting + the previous two characters (if present) to see if a break is + permitted between them. */ + eptr--; - for (;;) /* Move back over one extended */ /* LOOP_COUNT: COST */ + if (!utf) c = *eptr; else { - if (!utf) c = *eptr; else + BACKCHAR(eptr); + GETCHAR(c, eptr); + } + rgb = UCD_GRAPHBREAK(c); + + for (;;) /* LOOP_COUNT: COST */ + { + if (eptr == pp) goto TAIL_RECURSE; /* At start of char run */ + fptr = eptr - 1; + if (!utf) c = *fptr; else { - BACKCHAR(eptr); - GETCHAR(c, eptr); + BACKCHAR(fptr); + GETCHAR(c, fptr); } - if (UCD_CATEGORY(c) != ucp_M) break; - eptr--; + lgb = UCD_GRAPHBREAK(c); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + eptr = fptr; + rgb = lgb; COST(1); } } -- cgit v1.2.3 From 02909c3eaa2b4e636e0f17b24c527879cfc1bdcd Mon Sep 17 00:00:00 2001 From: Stefan Zegenhagen Date: Mon, 15 Jul 2013 17:03:07 +0200 Subject: make edlin understand a few important control keys Hi Fredrik, > I've gotten some feedback from your review, > You need to add documentation under Erts-> "User's Guide" -> "tty - A > command line interface" > > You need to add testcase in interactive_shell_SUITE a simplified > example of how this testcase could look like; > ctrl_w_and_ctrl_u(_Conf) -> > rtnode([{putline,""}, {putline, "2."}, {getline, "2"}, > {putline,"xxx yy"++[$\^w]++"."}, {getline,"xxx"}, {putline,"xxx > yy"++[$\^u]++"z."}, {getline,"z"}],[]). Please find an updated version of the patch attached to this e-mail. I hope that you still accept it via e-mail because the former patch was sent the same way ;-). I have extended the documentation to list the new key combinations and added tests to make sure they work. Kind regards, -- Dr. Stefan Zegenhagen arcutronix GmbH Garbsener Landstr. 10 30419 Hannover Germany Tel: +49 511 277-2734 Fax: +49 511 277-2709 Email: stefan.zegenhagen@arcutronix.com Web: www.arcutronix.com *Synchronize the Ethernet* General Managers: Dipl. Ing. Juergen Schroeder, Dr. Josef Gfrerer - Legal Form: GmbH, Registered office: Hannover, HRB 202442, Amtsgericht Hannover; Ust-Id: DE257551767. Please consider the environment before printing this message. >From ce4b827c78d18f39bb1146fd2959ffd7ae2b4bb6 Mon Sep 17 00:00:00 2001 From: Stefan Zegenhagen Date: Mon, 6 May 2013 14:39:07 +0200 Subject: [PATCH] [EDLIN] support a few more control keys Add support for the following control keys that many users have become accustomed to: - +W : backward kill word - +U : backward kill line - : goto start of line - : goto end of line - + : backward word - + : forward word It seems that the + control key sequences are different between terminal emulators, therefore a few possible combinations were added (similar to how libreadline is configured). Documentation and tests are extended to reflect the new functionality. --- erts/doc/src/tty.xml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/tty.xml b/erts/doc/src/tty.xml index 7d662a2849..15b67c8c7d 100644 --- a/erts/doc/src/tty.xml +++ b/erts/doc/src/tty.xml @@ -47,13 +47,17 @@

Normal Mode

In normal mode keystrokes from the user are collected and interpreted by . Most of the emacs line editing commands are supported. The following is a complete list of the supported line editing commands.

-

Note: The notation means pressing the control key and the letter simultaneously. means pressing the key followed by the letter . +

Note: The notation means pressing the control key and the letter simultaneously. means pressing the key followed by the letter . and represent the keys with the same name on the keyboard, whereas and represent the corresponding cursor keys.

Key Sequence Function + + Home + Beginning of line + C-a Beginning of line @@ -62,6 +66,10 @@ C-b Backward character + + C-Left + Backward word + M-b Backward word @@ -74,6 +82,10 @@ M-d Delete word + + End + End of line + C-e End of line @@ -82,6 +94,10 @@ C-f Forward character + + C-Right + Forward word + M-f Forward word @@ -94,6 +110,10 @@ C-k Kill line + + C-u + Backward kill line + C-l Redraw line @@ -110,6 +130,10 @@ C-t Transpose characters + + C-w + Backward kill word + C-y Insert previously killed text -- cgit v1.2.3 From a7db1f6930505ab6a70b0cb41a4c2d52a9448a08 Mon Sep 17 00:00:00 2001 From: olgeni Date: Wed, 7 Aug 2013 12:48:09 +0200 Subject: Misc. corrections for erl_driver(3) - Remove trailing whitespaces - Spelling fixes - Replace "deferred" with "deprecated" where applicable - Remove reference to non-existing "async_ready" entry point --- erts/doc/src/erl_driver.xml | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index efe0483b31..f52d973709 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -666,7 +666,7 @@ typedef struct ErlDrvBinary {

The ErlDrvData is a handle to driver-specific data, passed to the driver call-backs. It is a pointer, and is - most often type casted to a specific pointer in the driver.

+ most often type cast to a specific pointer in the driver.

SysIOVec @@ -1014,7 +1014,7 @@ typedef struct ErlIOVec { Read a system timestamp -

This function reads a timestamp into the memory pointed to by +

This function reads a timestamp into the memory pointed to by the parameter now. See the description of ErlDrvNowData for specification of its fields.

The return value is 0 unless the now pointer is not @@ -1056,7 +1056,7 @@ typedef struct ErlIOVec { returned. Another thread may still be using the event object internally. To safely close an event object call driver_select with ERL_DRV_USE and on==0. That - will clear all events and then call + will clear all events and then call stop_select when it is safe to close the event object. ERL_DRV_USE should be set together with the first event @@ -1068,7 +1068,7 @@ typedef struct ErlIOVec {

ERL_DRV_USE was added in OTP release R13. Old drivers will still work as before. But it is recommended to update them to use ERL_DRV_USE and stop_select to make sure that event objects are closed in a safe way.

- +

The return value is 0 (failure, -1, only if the ready_input/ready_output is NULL).

@@ -1524,7 +1524,7 @@ typedef struct ErlIOVec {

This function removes a driver entry de previously added with add_driver_entry.

-

Driver entries added by the erl_ddll erlang interface can +

Driver entries added by the erl_ddll erlang interface can not be removed by using this interface.

@@ -1758,7 +1758,7 @@ typedef struct ErlIOVec {
 Term type            Argument(s)
 ===========================================
-ERL_DRV_NIL          
+ERL_DRV_NIL
 ERL_DRV_ATOM         ErlDrvTermData atom (from driver_mk_atom(char *string))
 ERL_DRV_INT          ErlDrvSInt integer
 ERL_DRV_UINT         ErlDrvUInt integer
@@ -1779,11 +1779,11 @@ ERL_DRV_EXT2TERM     char *buf, ErlDrvUInt len
 	  signed integer data type ErlDrvSInt are 64 bits wide
 	  on a 64 bit runtime system and 32 bits wide on a 32 bit
 	  runtime system. They were introduced in erts version 5.6,
-	  and replaced some of the int arguments in the list above. 
+	  and replaced some of the int arguments in the list above.
 	

The unsigned integer data type ErlDrvUInt64 and the signed integer data type ErlDrvSInt64 are always 64 bits - wide. They were introduced in erts version 5.7.4. + wide. They were introduced in erts version 5.7.4.

To build the tuple {tcp, Port, [100 | Binary]}, the @@ -1879,7 +1879,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len Send term data from driver to port owner -

driver_output_term() is deferred and will +

driver_output_term() is deprecated and will be removed in the OTP-R17 release. Use erl_drv_output_term() instead.

@@ -1937,7 +1937,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len Send term data to other process than port owner process -

driver_send_term() is deferred and will +

driver_send_term() is deprecated and will be removed in the OTP-R17 release. Use erl_drv_send_term() instead.

@@ -1998,7 +1998,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len The data should be freed in async_free, because it's called if driver_async_cancel is called.

When the async operation is done, ready_async driver - entry function is called. If async_ready is null in + entry function is called. If ready_async is null in the driver entry, the async_free function is called instead.

The return value is a handle to the asynchronous task, which @@ -2035,7 +2035,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len as of OTP-R15B driver_async_cancel() is deprecated, and scheduled for removal in OTP-R16. It will currently always fail, and return 0.

-

driver_async_cancel() is deferred and will +

driver_async_cancel() is deprecated and will be removed in the OTP-R16 release.

@@ -2048,7 +2048,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len

This function locks the driver used by the port port in memory for the rest of the emulator process' - lifetime. After this call, the driver behaves as one of Erlang's + lifetime. After this call, the driver behaves as one of Erlang's statically linked in drivers.

@@ -2076,7 +2076,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len driver_entry). drv_data The driver defined handle that will be passed in subsequent - calls to driver call-backs. Note, that the + calls to driver call-backs. Note, that the driver start call-back will not be called for this new driver instance. The driver defined handle is normally created in the @@ -2284,7 +2284,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len A thread identifier.

This function compares two thread identifiers for equality, - and returns 0 it they aren't equal, and + and returns 0 it they aren't equal, and a value not equal to 0 if they are equal.

A Thread identifier may be reused very quickly after a thread has terminated. Therefore, if a thread @@ -2469,7 +2469,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len

This function broadcasts on a condition variable. That is, if other threads are waiting on the condition variable being - broadcasted on, all of them will be woken. + broadcast on, all of them will be woken.

This function is thread-safe.

@@ -2498,7 +2498,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len the calling thread when calling this function.

erl_drv_cond_wait() might return even though - no-one has signaled or broadcasted on the condition + no-one has signaled or broadcast on the condition variable. Code calling erl_drv_cond_wait() should always be prepared for erl_drv_cond_wait() returning even though the condition that the thread was @@ -2822,7 +2822,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len A pointer to an output buffer. value_size A pointer to an integer. The integer is both used for - passing input and output sizes (see below). + passing input and output sizes (see below).

This function retrieves the value of an environment variable. @@ -2900,4 +2900,3 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len Guide Ch. 3)

- -- cgit v1.2.3 From f499b39ddc5025074c5d22f272f1dd55df79f009 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 2 Aug 2013 15:04:14 +0200 Subject: erts: Fix race in ptab that can cause PID mix-ups Since: R16B01 Symptom: A spawned process may get the same PID as an existing process. The new process will "steal" the PID and make the old process unreachable through the PID. The problem also applies to port identities but has only been seen for processes. Conditions: SMP emulator with at least two scheduler threads. Rapid spawning and termination of a large number of processes. A small number of free slots in the process table will also increase the risk for this bug. Workaround: Use command line options "+P legacy" and "+Q legacy" Cause: The race happens if a process terminates and gets stalled while releasing its process table slot. The stall has to be so long (due to OS preemptive scheduling most probably) for other schedluer threads to consume all other free slots for newly spawn processes. Fix: Write invalid-markers in the free-pid-table and do atomic exhange operations in retry-loops to make sure each thread gets a unique PID. --- erts/emulator/beam/erl_ptab.c | 139 ++++++++++++++++++++++++++---------------- erts/emulator/beam/erl_ptab.h | 2 +- 2 files changed, 89 insertions(+), 52 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c index d69619dd44..fa5482b841 100644 --- a/erts/emulator/beam/erl_ptab.c +++ b/erts/emulator/beam/erl_ptab.c @@ -433,7 +433,7 @@ erts_ptab_mem_size(ErtsPTab *ptab) { UWord size = ptab->r.o.max*sizeof(erts_smp_atomic_t); if (ptab->r.o.free_id_data) - size += ptab->r.o.max*sizeof(Uint32); + size += ptab->r.o.max*sizeof(erts_smp_atomic32_t); return size; } @@ -474,7 +474,7 @@ erts_ptab_init_table(ErtsPTab *ptab, tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(erts_smp_atomic_t)); alloc_sz = tab_sz; if (!legacy) - alloc_sz += ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(Uint32)); + alloc_sz += ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(erts_smp_atomic32_t)); ptab->r.o.tab = erts_alloc_permanent_cache_aligned(atype, alloc_sz); tab_end = ((char *) ptab->r.o.tab) + tab_sz; tab_entry = ptab->r.o.tab; @@ -497,6 +497,10 @@ erts_ptab_init_table(ErtsPTab *ptab, ASSERT(ptab->r.o.pix_cl_shift + ptab->r.o.pix_cli_shift == bits); + ptab->r.o.invalid_element = invalid_element; + ptab->r.o.invalid_data = erts_ptab_id2data(ptab, invalid_element->id); + ptab->r.o.release_element = release_element; + if (legacy) { ptab->r.o.free_id_data = NULL; ptab->r.o.dix_cl_mask = 0; @@ -506,11 +510,11 @@ erts_ptab_init_table(ErtsPTab *ptab, } else { - tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(Uint32)); - ptab->r.o.free_id_data = (Uint32 *) tab_end; + tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(erts_smp_atomic32_t)); + ptab->r.o.free_id_data = (erts_smp_atomic32_t *) tab_end; tab_cache_lines = tab_sz/ERTS_CACHE_LINE_SIZE; - ix_per_cache_line = (ERTS_CACHE_LINE_SIZE/sizeof(Uint32)); + ix_per_cache_line = (ERTS_CACHE_LINE_SIZE/sizeof(erts_smp_atomic32_t)); ptab->r.o.dix_cl_mask = tab_cache_lines-1; ptab->r.o.dix_cl_shift = erts_fit_in_bits_int32(ix_per_cache_line-1); @@ -525,7 +529,9 @@ erts_ptab_init_table(ErtsPTab *ptab, ix = 0; for (cl = 0; cl < tab_cache_lines; cl++) { for (cli = 0; cli < ix_per_cache_line; cli++) { - ptab->r.o.free_id_data[ix] = cli*tab_cache_lines+cl; + erts_smp_atomic32_init_nob(&ptab->r.o.free_id_data[ix], + cli*tab_cache_lines+cl); + ASSERT(erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[ix]) != ptab->r.o.invalid_data); ix++; } } @@ -534,9 +540,6 @@ erts_ptab_init_table(ErtsPTab *ptab, erts_smp_atomic32_init_nob(&ptab->vola.tile.fid_ix, -1); } - ptab->r.o.invalid_element = invalid_element; - ptab->r.o.invalid_data = erts_ptab_id2data(ptab, invalid_element->id); - ptab->r.o.release_element = release_element; erts_smp_interval_init(&ptab->list.data.interval); ptab->list.data.deleted.start = NULL; @@ -606,11 +609,13 @@ erts_ptab_new_element(ErtsPTab *ptab, = erts_smp_current_interval_nob(erts_ptab_interval(ptab)); if (ptab->r.o.free_id_data) { + do { + ix = (Uint32) erts_smp_atomic32_inc_read_acqb(&ptab->vola.tile.aid_ix); + ix = ix_to_free_id_data_ix(ptab, ix); - ix = (Uint32) erts_smp_atomic32_inc_read_acqb(&ptab->vola.tile.aid_ix); - ix = ix_to_free_id_data_ix(ptab, ix); - - data = ptab->r.o.free_id_data[ix]; + data = erts_smp_atomic32_xchg_nob(&ptab->r.o.free_id_data[ix], + (erts_aint32_t)ptab->r.o.invalid_data); + }while ((Eterm)data == ptab->r.o.invalid_data); init_ptab_el(init_arg, (Eterm) data); @@ -763,7 +768,7 @@ erts_ptab_delete_element(ErtsPTab *ptab, erts_smp_atomic_set_relb(&ptab->r.o.tab[pix], ERTS_AINT_NULL); if (ptab->r.o.free_id_data) { - + Uint32 prev_data; /* Next data for this slot... */ data = (Uint32) erts_ptab_id2data(ptab, ptab_el->id); data += ptab->r.o.max; @@ -772,14 +777,17 @@ erts_ptab_delete_element(ErtsPTab *ptab, data += ptab->r.o.max; data &= ~(~((Uint32) 0) << ERTS_PTAB_ID_DATA_SIZE); } - ASSERT(data != ptab->r.o.invalid_data); ASSERT(pix == erts_ptab_data2pix(ptab, data)); - ix = (Uint32) erts_smp_atomic32_inc_read_relb(&ptab->vola.tile.fid_ix); - ix = ix_to_free_id_data_ix(ptab, ix); - - ptab->r.o.free_id_data[ix] = data; + do { + ix = (Uint32) erts_smp_atomic32_inc_read_relb(&ptab->vola.tile.fid_ix); + ix = ix_to_free_id_data_ix(ptab, ix); + + prev_data = erts_smp_atomic32_cmpxchg_nob(&ptab->r.o.free_id_data[ix], + data, + ptab->r.o.invalid_data); + }while ((Eterm)prev_data != ptab->r.o.invalid_data); } ASSERT(erts_smp_atomic32_read_nob(&ptab->vola.tile.count) > 0); @@ -1392,6 +1400,31 @@ erts_ptab_init(void) * Debug stuff */ +static void assert_ptab_consistency(ErtsPTab *ptab) +{ +#ifdef DEBUG + if (ptab->r.o.free_id_data) { + Uint32 ix, pix, data; + int free_pids = 0; + int null_slots = 0; + + for (ix=0; ix < ptab->r.o.max; ix++) { + if (erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[ix]) != ptab->r.o.invalid_data) { + ++free_pids; + data = erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[ix]); + pix = erts_ptab_data2pix(ptab, (Eterm) data); + ASSERT(erts_ptab_pix2intptr_nob(ptab, pix) == ERTS_AINT_NULL); + } + if (erts_smp_atomic_read_nob(&ptab->r.o.tab[ix]) == ERTS_AINT_NULL) { + ++null_slots; + } + } + ASSERT(free_pids == null_slots); + ASSERT(free_pids == ptab->r.o.max - erts_smp_atomic32_read_nob(&ptab->vola.tile.count)); + } +#endif +} + Sint erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next) { @@ -1402,46 +1435,49 @@ erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next) erts_ptab_rwlock(ptab); + assert_ptab_consistency(ptab); + if (ptab->r.o.free_id_data) { - Uint32 aid_ix, dix; + Uint32 id_ix, dix; if (set) { - Uint32 max_ix, ser, num, start; + Uint32 i, max_ix, num, stop_id_ix; max_ix = ptab->r.o.max - 1; - ser = next & ~max_ix; - start = num = next & max_ix; - - aid_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix) + 1; - - do { - Uint32 pix = erts_ptab_data2pix(ptab, num); + num = next; + id_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix); + + for (i=0; i <= max_ix; ++i) { + Uint32 pix; + ++num; + num &= ~(~((Uint32) 0) << ERTS_PTAB_ID_DATA_SIZE); + if (num == ptab->r.o.invalid_data) { + num += ptab->r.o.max; + num &= ~(~((Uint32) 0) << ERTS_PTAB_ID_DATA_SIZE); + } + pix = erts_ptab_data2pix(ptab, num); if (ERTS_AINT_NULL == erts_ptab_pix2intptr_nob(ptab, pix)) { - dix = ix_to_free_id_data_ix(ptab, aid_ix); - ptab->r.o.free_id_data[dix] = ser + num; - ASSERT(pix == erts_ptab_data2pix(ptab, ser+num)); - if (aid_ix == max_ix) - aid_ix = 0; - else - aid_ix++; + ++id_ix; + dix = ix_to_free_id_data_ix(ptab, id_ix); + erts_smp_atomic32_set_nob(&ptab->r.o.free_id_data[dix], num); + ASSERT(pix == erts_ptab_data2pix(ptab, num)); } - if (num == max_ix) - num = 0; - else - num++; - } while (num != start); + } + erts_smp_atomic32_set_nob(&ptab->vola.tile.fid_ix, id_ix); -#ifdef DEBUG - if (aid_ix == 0) - aid_ix = max_ix; - else - aid_ix--; - ASSERT((aid_ix & max_ix) == (((Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.fid_ix)) & max_ix)); -#endif + /* Write invalid_data in rest of free_id_data[]: */ + stop_id_ix = (1 + erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix)) & max_ix; + while (1) { + id_ix = (id_ix+1) & max_ix; + if (id_ix == stop_id_ix) + break; + dix = ix_to_free_id_data_ix(ptab, id_ix); + erts_smp_atomic32_set_nob(&ptab->r.o.free_id_data[dix], + ptab->r.o.invalid_data); + } } - - aid_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix) + 1; - dix = ix_to_free_id_data_ix(ptab, aid_ix); - res = (Sint) ptab->r.o.free_id_data[dix]; + id_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix) + 1; + dix = ix_to_free_id_data_ix(ptab, id_ix); + res = (Sint) erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[dix]); } else { /* Deprecated legacy algorithm... */ @@ -1485,6 +1521,7 @@ erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next) } } + assert_ptab_consistency(ptab); erts_ptab_rwunlock(ptab); return res; diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h index c2d8bd9cad..e3e05f14af 100644 --- a/erts/emulator/beam/erl_ptab.h +++ b/erts/emulator/beam/erl_ptab.h @@ -100,7 +100,7 @@ typedef struct { typedef struct { erts_smp_atomic_t *tab; - Uint32 *free_id_data; + erts_smp_atomic32_t *free_id_data; Uint32 max; Uint32 pix_mask; Uint32 pix_cl_mask; -- cgit v1.2.3 From 60593d582bdb92631b1df0624c19827d1c5ab93e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 8 Aug 2013 10:37:06 +0200 Subject: etp: Do not use name as beam.smp is the name on Linux --- erts/etc/unix/etp-thr.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-thr.py b/erts/etc/unix/etp-thr.py index 4bfbfa16f3..64fb858d20 100644 --- a/erts/etc/unix/etp-thr.py +++ b/erts/etc/unix/etp-thr.py @@ -18,8 +18,6 @@ # def get_thread_name(t): - if t.name != None: - return t.name; f = gdb.newest_frame(); while f: if f.name() == "async_main": -- cgit v1.2.3 From a6a08b084f5abe2ab190387918e8a0f4366d454c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 6 Aug 2013 10:54:57 +0200 Subject: erts: Do not enable TRACE_SILENT when testing a ms --- erts/emulator/beam/erl_db_util.c | 5 ++++- erts/emulator/beam/global.h | 7 ++++--- erts/emulator/test/match_spec_SUITE.erl | 12 ++++++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 713ac0ba18..ef3749a2c4 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2319,6 +2319,8 @@ restart: break; case matchSilent: --esp; + if (in_flags & ERTS_PAM_IGNORE_TRACE_SILENT) + break; if (*esp == am_true) { erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); ERTS_TRACE_FLAGS(c_p) |= F_TRACE_SILENT; @@ -4971,7 +4973,8 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace) save_cp = p->cp; p->cp = NULL; res = erts_match_set_run(p, mps, arr, n, - ERTS_PAM_COPY_RESULT, &ret_flags); + ERTS_PAM_COPY_RESULT|ERTS_PAM_IGNORE_TRACE_SILENT, + &ret_flags); p->cp = save_cp; } else { n = 0; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index cecfa8a0fd..bacd5a5752 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1018,9 +1018,10 @@ Eterm erts_match_set_lint(Process *p, Eterm matchexpr); extern void erts_match_set_release_result(Process* p); enum erts_pam_run_flags { - ERTS_PAM_TMP_RESULT=0, - ERTS_PAM_COPY_RESULT=1, - ERTS_PAM_CONTIGUOUS_TUPLE=2 + ERTS_PAM_TMP_RESULT=1, + ERTS_PAM_COPY_RESULT=2, + ERTS_PAM_CONTIGUOUS_TUPLE=4, + ERTS_PAM_IGNORE_TRACE_SILENT=8 }; extern Eterm erts_match_set_run(Process *p, Binary *mpsp, Eterm *args, int num_args, diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 8dbc6b6538..b56b7ce525 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -22,7 +22,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]). -export([test_1/1, test_2/1, test_3/1, bad_match_spec_bin/1, - trace_control_word/1, silent/1, silent_no_ms/1, + trace_control_word/1, silent/1, silent_no_ms/1, silent_test/1, ms_trace2/1, ms_trace3/1, boxed_and_small/1, destructive_in_test_bif/1, guard_exceptions/1, unary_plus/1, unary_minus/1, moving_labels/1]). @@ -55,7 +55,7 @@ all() -> case test_server:is_native(match_spec_SUITE) of false -> [test_1, test_2, test_3, bad_match_spec_bin, - trace_control_word, silent, silent_no_ms, ms_trace2, + trace_control_word, silent, silent_no_ms, silent_test, ms_trace2, ms_trace3, boxed_and_small, destructive_in_test_bif, guard_exceptions, unary_plus, unary_minus, fpe, moving_labels, @@ -501,6 +501,14 @@ silent_no_ms(Config) when is_list(Config) -> {trace,Tracee,return_to,{?MODULE,f3,2}}] end). +silent_test(doc) -> + ["Test that match_spec_test does not activate silent"]; +silent_test(_Config) -> + {flags,[]} = erlang:trace_info(self(),flags), + erlang:match_spec_test([],[{'_',[],[{silent,true}]}],trace), + {flags,[]} = erlang:trace_info(self(),flags). + + ms_trace2(doc) -> ["Test the match spec functions {trace/2}"]; ms_trace2(suite) -> []; -- cgit v1.2.3 From 6146e7642d4bb9f7c9bb5f8cbca548c1d9667e5c Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Thu, 18 Jul 2013 10:18:58 +0200 Subject: Add new options to Erlang re interface and mend dupnames Add notempty_atstart, no_start_optimize, ucp and never_utf options from new PCRE version. Use the new notempty_atstart in global matching. Add inspect/2 function Correctly handle dupnames when capturing a name, as in Perl, get the leftmost matching occurence. Also added all_names, to get all the names in the pattern in alphabetical (name) order. To be able to use this in global matching, an inspect function that can dig out a namelist was added. --- erts/emulator/beam/atom.names | 6 ++ erts/emulator/beam/bif.tab | 6 ++ erts/emulator/beam/erl_bif_re.c | 233 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 240 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index eba1d0fa23..cf8f511b85 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -71,6 +71,7 @@ atom ac atom active atom all atom all_but_first +atom all_names atom alloc_info atom alloc_sizes atom allocated @@ -348,11 +349,13 @@ atom multi_scheduling atom multiline atom name atom named_table +atom namelist atom native_addresses atom Neq='=/=' atom Neqeq='/=' atom net_kernel atom net_kernel_terminated +atom never_utf atom new atom new_index atom new_uniq @@ -378,6 +381,7 @@ atom nosuspend atom no_float atom no_integer atom no_network +atom no_start_optimize atom not atom not_a_list atom not_loaded @@ -388,6 +392,7 @@ atom notalive atom notbol atom noteol atom notempty +atom notempty_atstart atom notify atom notsup atom nouse_stdio @@ -554,6 +559,7 @@ atom true atom tuple atom type atom ucompile +atom ucp atom undef atom ungreedy atom unicode diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index dc8e9101de..7c8e4b31cf 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -573,6 +573,12 @@ bif erlang:binary_to_float/1 bif io:printable_range/0 +# +# New in R17A +# + +bif re:inspect/2 + # # Obsolete # diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index 12fc834685..c74125ae41 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -288,6 +288,10 @@ parse_options(Eterm listp, /* in */ eopt |= PCRE_NOTEMPTY; fl |= PARSE_FLAG_UNIQUE_EXEC_OPT; break; + case am_notempty_atstart: + eopt |= PCRE_NOTEMPTY_ATSTART; + fl |= PARSE_FLAG_UNIQUE_EXEC_OPT; + break; case am_notbol: eopt |= PCRE_NOTBOL; fl |= PARSE_FLAG_UNIQUE_EXEC_OPT; @@ -296,6 +300,10 @@ parse_options(Eterm listp, /* in */ eopt |= PCRE_NOTEOL; fl |= PARSE_FLAG_UNIQUE_EXEC_OPT; break; + case am_no_start_optimize: + copt |= PCRE_NO_START_OPTIMIZE; + fl |= PARSE_FLAG_UNIQUE_COMPILE_OPT; + break; case am_caseless: copt |= PCRE_CASELESS; fl |= PARSE_FLAG_UNIQUE_COMPILE_OPT; @@ -332,6 +340,14 @@ parse_options(Eterm listp, /* in */ copt |= PCRE_UNGREEDY; fl |= PARSE_FLAG_UNIQUE_COMPILE_OPT; break; + case am_ucp: + copt |= PCRE_UCP; + fl |= PARSE_FLAG_UNIQUE_COMPILE_OPT; + break; + case am_never_utf: + copt |= PCRE_NEVER_UTF; + fl |= PARSE_FLAG_UNIQUE_COMPILE_OPT; + break; case am_unicode: copt |= PCRE_UTF8; fl |= (PARSE_FLAG_UNIQUE_COMPILE_OPT | PARSE_FLAG_UNICODE); @@ -359,7 +375,7 @@ parse_options(Eterm listp, /* in */ if (compile_options != NULL) { *compile_options = copt; } - if (exec_options != NULL) { + if (exec_options != NULL) { *exec_options = eopt; } if (flags != NULL) { @@ -585,6 +601,17 @@ static Eterm build_exec_return(Process *p, int rc, RestartContext *restartp, Ete ri->num_spec * 2 * sizeof(Eterm)); for (i = 0; i < ri->num_spec; ++i) { x = ri->v[i]; + if (x < -1) { + int n = i-x+1; + int j; + for (j = i+1; j < ri->num_spec && j < n; ++j) { + if (restartp->ovector[(ri->v[j])*2] >= 0) { + x = ri->v[j]; + break; + } + } + i = n-1; + } if (x < rc && x >= 0) { tmp_vect[n*2] = make_signed_integer(restartp->ovector[x*2],p); tmp_vect[n*2+1] = make_signed_integer(restartp->ovector[x*2+1]-restartp->ovector[x*2],p); @@ -666,6 +693,17 @@ static Eterm build_exec_return(Process *p, int rc, RestartContext *restartp, Ete ri->num_spec * sizeof(Eterm)); for (i = 0; i < ri->num_spec; ++i) { x = ri->v[i]; + if (x < -1) { + int n = i-x+1; + int j; + for (j = i+1; j < ri->num_spec && j < n; ++j) { + if (restartp->ovector[(ri->v[j])*2] >= 0) { + x = ri->v[j]; + break; + } + } + i = n-1; + } if (x < rc && x >= 0) { char *cp; int len; @@ -730,6 +768,49 @@ static Eterm build_exec_return(Process *p, int rc, RestartContext *restartp, Ete */ #define RINFO_SIZ(Num) (sizeof(ReturnInfo) + (sizeof(int) * (Num - 1))) +#define PICK_INDEX(NameEntry) \ + ((int) ((((unsigned) ((unsigned char *) (NameEntry))[0]) << 8) + \ + ((unsigned) ((unsigned char *) (NameEntry))[1]))) + + +static void build_one_capture(const pcre *code, ReturnInfo **ri, int *sallocated, int has_dupnames, char *name) +{ + ReturnInfo *r = (*ri); + if (has_dupnames) { + /* Build a sequence of positions, starting with -size if + more than one, otherwise just put the index there... */ + char *first,*last; + int esize = erts_pcre_get_stringtable_entries(code,name,&first,&last); + if (esize == PCRE_ERROR_NOSUBSTRING) { + r->v[r->num_spec - 1] = -1; + } else if(last == first) { + r->v[r->num_spec - 1] = PICK_INDEX(first); + } else { + int num = ((last - first) / esize) + 1; + int i; + ASSERT(num > 1); + r->v[r->num_spec - 1] = -num; /* A value less than -1 means + multiple indexes for same name */ + for (i = 0; i < num; ++i) { + ++(r->num_spec); + if(r->num_spec > (*sallocated)) { + (*sallocated) += 10; + r = erts_realloc(ERTS_ALC_T_RE_SUBJECT, r, + RINFO_SIZ((*sallocated))); + } + r->v[r->num_spec - 1] = PICK_INDEX(first); + first += esize; + } + } + } else { + /* Use the faster binary search if no duplicate names are present */ + if ((r->v[r->num_spec - 1] = erts_pcre_get_stringnumber(code,name)) == + PCRE_ERROR_NOSUBSTRING) { + r->v[r->num_spec - 1] = -1; + } + } + *ri = r; +} static ReturnInfo * build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) @@ -778,6 +859,53 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) } ri->v[ri->num_spec - 1] = 0; break; + case am_all_names: + { + int rc,i,top; + int entrysize; + char *nametable, *last = NULL; + int has_dupnames; + unsigned long options; + + if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_OPTIONS, &options) != 0) + goto error; + if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + goto error; + if (top <= 0) { + ri->num_spec = 0; + ri->type = RetNone; + break; + } + if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize) != 0) + goto error; + if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, (unsigned char **) &nametable) != 0) + goto error; + + has_dupnames = ((options & PCRE_DUPNAMES) != 0); + + for(i=0;inum_spec < 0) + ri->num_spec = 0; + ++(ri->num_spec); + if(ri->num_spec > sallocated) { + sallocated += 10; + ri = erts_realloc(ERTS_ALC_T_RE_SUBJECT, ri, RINFO_SIZ(sallocated)); + } + if (has_dupnames) { + /* This could be more effective, we actually have + the names and could fill in the vector + immediately. Now we lookup the name again. */ + build_one_capture(code,&ri,&sallocated,has_dupnames,nametable+2); + } else { + ri->v[ri->num_spec - 1] = PICK_INDEX(nametable); + } + } + last = nametable; + nametable += entrysize; + } + break; + } default: if (is_list(capture_spec[CAPSPEC_VALUES])) { for(l=capture_spec[CAPSPEC_VALUES];is_list(l);l = CDR(list_val(l))) { @@ -793,6 +921,11 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) if (term_to_int(val,&x)) { ri->v[ri->num_spec - 1] = x; } else if (is_atom(val) || is_binary(val) || is_list(val)) { + int has_dupnames; + unsigned long options; + if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_OPTIONS, &options) != 0) + goto error; + has_dupnames = ((options & PCRE_DUPNAMES) != 0); if (is_atom(val)) { Atom *ap = atom_tab(atom_val(val)); if ((ap->len + 1) > tmpbsiz) { @@ -823,10 +956,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) } tmpb[slen] = '\0'; } - if ((ri->v[ri->num_spec - 1] = erts_pcre_get_stringnumber(code,tmpb)) == - PCRE_ERROR_NOSUBSTRING) { - ri->v[ri->num_spec - 1] = -1; - } + build_one_capture(code,&ri,&sallocated,has_dupnames,tmpb); } else { goto error; } @@ -1159,6 +1289,99 @@ static BIF_RETTYPE re_exec_trap(BIF_ALIST_3) BIF_RET(res); } +BIF_RETTYPE +re_inspect_2(BIF_ALIST_2) +{ + Eterm *tp,*tmp_vec,*hp; + int rc,i,top,j; + int entrysize; + char *nametable, *last,*name; + int has_dupnames; + unsigned long options; + int num_names; + Eterm res; + const pcre *code; + byte *temp_alloc = NULL; + + if (is_not_tuple(BIF_ARG_1) || (arityval(*tuple_val(BIF_ARG_1)) != 5)) { + goto error; + } + tp = tuple_val(BIF_ARG_1); + if (tp[1] != am_re_pattern || is_not_small(tp[2]) || + is_not_small(tp[3]) || is_not_small(tp[4]) || + is_not_binary(tp[5])) { + goto error; + } + if (BIF_ARG_2 != am_namelist) { + goto error; + } + if ((code = (const pcre *) + erts_get_aligned_binary_bytes(tp[5], &temp_alloc)) == NULL) { + goto error; + } + + /* OK, so let's try to get some info */ + + if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_OPTIONS, &options) != 0) + goto error; + if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + goto error; + if (top <= 0) { + hp = HAlloc(BIF_P, 3); + res = TUPLE2(hp,am_namelist,NIL); + erts_free_aligned_binary_bytes(temp_alloc); + BIF_RET(res); + } + if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize) != 0) + goto error; + if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, (unsigned char **) &nametable) != 0) + goto error; + + has_dupnames = ((options & PCRE_DUPNAMES) != 0); + /* First, count the names */ + num_names = 0; + last = NULL; + name = nametable; + for(i=0;i= 0; --i) { + res = CONS(hp,tmp_vec[i],res); + hp += 2; + } + res = TUPLE2(hp,am_namelist,res); + erts_free_aligned_binary_bytes(temp_alloc); + erts_free(ERTS_ALC_T_RE_TMP_BUF, tmp_vec); + BIF_RET(res); + + error: + /* tmp_vec never allocated when we reach here */ + erts_free_aligned_binary_bytes(temp_alloc); + BIF_ERROR(BIF_P,BADARG); +} + + + -- cgit v1.2.3 From 00999b464b9dbbbe4d538490a8892557c1d9d6df Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 19 Jul 2013 16:55:51 +0200 Subject: Add README for updating PCRE --- erts/emulator/pcre/README.pcre_update.md | 733 +++++++++++++++++++++++++++++++ 1 file changed, 733 insertions(+) create mode 100644 erts/emulator/pcre/README.pcre_update.md (limited to 'erts') diff --git a/erts/emulator/pcre/README.pcre_update.md b/erts/emulator/pcre/README.pcre_update.md new file mode 100644 index 0000000000..5f2414f0d9 --- /dev/null +++ b/erts/emulator/pcre/README.pcre_update.md @@ -0,0 +1,733 @@ +# How to update the PCRE version used by Erlang + +## The basic changes to the PCRE library + +To work with the Erlang VM, PCRE has been changed in two important ways: + +1. The main execution machine in pcre\_exec has been modified so that +matching can be interrupted and restarted. This functionality utilizes +the code that implements recursion by allocating explicit +"stack-frames" in heap space, which basically means that all local +variables in the loop are part of a struct which is kept in "malloced" +memory on the heap and there are no real stack variables that need to +be pushed on the C stack in the case of recursive calls. This is a +technique we also use inside the VM to avoid building large C +stacks. In PCRE this is enabled by the NO\_RECURSE define, so that is a +prerequisite for the ERLANG\_INTEGRATION define which also adds labels +at restart points and counts "reductions". + +2. All visible symbols in PCRE gets the erts_ prefix, so that NIF's +and such using a "real" pcre library does not get confused (or 're' +gets confused when a "real" pcre library get's loaded into the VM +process). + +3. All irrelevant functionality has been stripped from the library, +which means for example UTF16 support, jit, DFA execution +etc. Basically the source files handling this are removed, together +with any build support from the PCRE project. We have our own +makefiles etc. + +## Setting up an environment for the work + +I work with four temporary directories when doing this (the examples +are from the updating of pcre-7.6 to pcre-8.33); + + ~/tmp/pcre> ls + epcre-7.6 epcre-8.33 pcre-7.6 pcre-8.33 + +I've unpacked the plain pcre sources in pcre-* and will work with our +patched sources in the epcre-* directories. + +Make sure your ERL_TOP contains a *built* version of Erlang (and you have made a branch) + +First unpack the pcre libraries (which will create the pcre-* +directories) and then copy our code to the old epcre directory: + + ~/tmp/pcre> tar jxf $ERL_TOP/erts/emulator/pcre/pcre-7.6.tar.bz2 + ~/tmp/pcre> tar jxf ~/Downloads/pcre-8.33.tar.bz2 + ~/tmp/pcre> mkdir epcre-7.6 epcre-8.33 + ~/tmp/pcre> cd epcre-7.6/ + ~/tmp/pcre/epcre-7.6> cp -r $ERL_TOP/erts/emulator/pcre/* . + ~/tmp/pcre/epcre-7.6> rm pcre-7.6.tar.bz2 + +Leave the obj directory, you may need the libepcre.a file... + +If you find it easier, you can revert the commit in GIT that adds the +erts_ prefix to the previous version before continuing work, but as +that is a quite small diff in newer versions of PCRE, it is probably +not worth it. Still, you will find the erts_ prefix being a separate +commit when integrating 8.33, so if you're nice, you will do the same +for the person coming after you... + +## Generating a diff for our changes to PCRE + +Before you generate a diff (that, in an ideal world, would be used to +automatically patch the newer version of pcre, which will probably +only work for minor PCRE updates), we need to configure the old pcre. + + ~/tmp/pcre/epcre-7.6> cd ../pcre-7.6 + ~/tmp/pcre/pcre-7.6> ./configure --enable-utf8 --enable-unicode-properties --disable-shared --disable-stack-for-recursion + +Note that for newer versions, the configure flag '--enable-utf8' +should be replaced with '--enable-utf' + +So we now generate a diff: + + ~/tmp/pcre/pcre-7.6> cd ../epcre-7.6 + ~/tmp/pcre/epcre-7.6> (for x in *.[ch]; do if [ -f ../pcre-7.6/$x ]; then diff -c ../pcre-7.6/$x $x; fi; done ) > ../epcre-7.6_clean.diff + +### What the diff means + +Let's now walk through the relevant parts of the diff. Some of the +differences might come from patches that probably are already in the +new version, For example in out 7.6, we had a security patch which +added the define WORK_SIZE_CHECK and used it in some places. Those can +probably safely be ignored, but to be on the safe side, check what's +already integrated in the new version. + +The interesting part is in pcre_exec.c. You will see things like + + #ifdef ERLANG_INTEGRATION + ... + #endif + +or + + #if defined(ERLANG_INTEGRATION) + ... + #endif + +and a lot of + + COST_CHK(1); + +or + + COST(min); + +and + + /* LOOP_COUNT: Ok */ + /* LOOP_COUNT: CHK */ + /* LOOP_COUNT: COST */ + + +scattered over the main loop. Those mean the following: + +* COST(int x) - consume reductions proportional to the integer + parameter, but no need for interruption here (it's like + bump_reductions without trapping). The loop they apply to also has a + 'LOOP_COUNT: COST' comment at it's head. + +* COST\_CHK(int x) - like COST(x), but also check that the reduction +counter does not reach zero. If it does, leave the execution loop to +be restarted at a later point. No real stack variables can be live +here. Note that variables like 'max' and 'min' are *not* real stack +variables, the NO\_RECURSION setting has taken care of that. 'i' is a +stack variable that's explicitly saved when trapping, so that will +also be correct when returning from a trap. So will 'c', 'rrc' and +flags like 'utf8', 'minimize' and 'posessive'. Those can also be +regarded as "non C-stack variables". The loop where they reside also +has a 'LOOP\_COUNT: CHK' comment. + +* /* LOOP_COUNT: Ok */ - means that I have checked the loop and it + only runs a deterministic set of iterations regardless of input, or + it has a call to RRECURSE in it's body, why we need not add more + cost than the normal reduction counting that will occur for each + instruction demands. + +The thing is that each loop in the function 'match' should be marked +with one of these comments. If no comment is present after you patched +the new release (if you successfully manage to do it automatically), +it may be a new regexp instruction that is added since the last +release. + +You will need to manually go through the main 'match' loop after +upgrading to verify that there are no unhandled loops in the regexp +machine loop (!). + +The COST\_CHK macro works like this: + +1. Add to the loop count. +2. If loop count > limit: + 1. Store the line (+100) in the Xwhere member of the frame structure + 2. Goto LOOP\_COUNT\_BREAK, which ultimately returns from the function +3. Insert a label, which is named L\_LOOP\_COUNT\_<line number> + +LOOP\_COUNT\_BREAK code will create an extra "stack frame" on the heap +allocated stack used if NO\_RECURSION is set, and will store the few +locals that are not already in the ordinary stack frame there (like +'c' and 'i'). + +When we continue execution (after a trap up to the main Erlang +scheduler), we will jump to LOOP\_COUNT\_RETURN, which will restore +the local variables and will jump to the labels. The jump code looks +like this in the C source: + + switch (frame->Xwhere) + { + #include "pcre_exec_loop_break_cases.inc" + default: + DPRINTF(("jump error in pcre match: label %d non-existent\n", frame->Xwhere)); + return PCRE_ERROR_INTERNAL; + } + +When building, pcre\_exec\_loop\_break\_cases.inc will be generated +during build by pcre.mk, it will look like: + + case 791: goto L_LOOP_COUNT_691; + case 1892: goto L_LOOP_COUNT_1792; + case 1999: goto L_LOOP_COUNT_1899; + +etc + +So, simply put, all C-stack variables are saved when we have consumed +our reductions, we return from the function and, as there is no real +recursion we immediately fall out into the re:run BIF, which with the +help of a magic binary keeps track of the heap allocated stack for the +regexp machine. When we return from trapping out to the scheduler, all +vital data is restored and we continue from exactly the same state as +we left. What's needed is to patch this into the new pcre_exec and +check all new instructions to determine what might need updating in +terms of COST, COST\_CHK etc. + +Well, that's *almost* everything, because there is of course more... + +The actual interface function, 'pcre\_exec', needs the same treatment +as the actual regexp machine loop, that is we need to store all local +variables between restarts. Unfortunately the NO\_RECURSE setting does +not do this, we need to do it ourselves. So there's quite a diff in +that function too, where a big struct is declared, containing every +local variable in that function, together with either local copies +that are swapped in and out, or macros that directly access the heap +allocated struct. The struct is called `PcreExecContext`. + +If a context is present, we are restarting and therefore restore +everything. If we are restarting we can also skip all initialization +code in the function and jump more or less directly to the +RESTART_INTERRUPTED label and the call to 'match', which is the actual +regexp machine loop. + +There are a few places in the pcre_exec we need to do some housekeeping, you will see code like: + + if ((extra_data->flags & PCRE_EXTRA_LOOP_LIMIT) != 0) + { + *extra_data->loop_counter_return = + (extra_data->loop_limit - md->loop_limit); + } + +Make sure, after updating, that this housekeeping is done whenever we +do not reach the call to 'match'. + +So, now we in theory know what to do, so let's do it: + +But... + +## File changes in the new version of PCRE + +First we need to go through what's changed in the new library +version. Files may have new names, functions may have moved and so on. + +Start by building the new library: + + ~/tmp/pcre> cd pcre-8.33/ + ~/tmp/pcre/pcre-8.33> ./configure --enable-utf --enable-unicode-properties --disable-shared --disable-stack-for-recursion + ~/tmp/pcre/pcre-8.33> make + +In the make process, you will probably notice most files that are +used, but you can bet that's not all not all... + +To begin with you will need a default table for Latin-1 characters, so: + + ~/tmp/pcre/pcre-8.33> cc -DHAVE_CONFIG_H -o dftables dftables.c + ~/tmp/pcre/pcre-8.33> LANG=sv_SE ./dftables -L ../epcre-8.33/pcre_latin_1_table.c + +Compare it to the pcre\_latin\_1\_table.c in the old version, they +should not differ in any significant way. + +A good starting point is then to try to find all files in the new +version of the library that have (probably) the same names as the +one's in our distribution: + + ~/tmp/pcre/pcre-8.33> cd ../epcre-7.6/ + ~/tmp/pcre/epcre-7.6> for x in *.[ch]; do if [ '!' -f ../pcre-8.33/$x ]; then echo $x; else cp ../pcre-8.33/$x ../epcre-8.33/; fi; done + +This will output a list of files not found in the new distro. Let's +look at the list from the example upgrade: + + local_config.h + make_latin1_table.c + pcre_info.c + pcre_latin_1_table.c + pcre_make_latin1_default.c + pcre_try_flipped.c + pcre_ucp_searchfuncs.c + ucpinternal.h + ucptable.h + +* local\_config.h - OK, that's our child, it contains PCRE-specific + configure-results (i.e. the #defines that are results from out + parameters to configure, like NO\_RECURSE etc). Just copy it and + edit it according to what specific settings you can find in the + generated config.h from the real library build. In our example case, + the #define SUPPORT\_UTF8 should be renamed to #define SUPPORT\_UTF + and #define VERSION "7.6" should be changed to #define VERSION + "8.33"... + +* make\_latin1\_table.c - it was renamed to dftables.c, so we copy + that instead. + +* pcre\_info.c - It was simply removed from the library. Good, because + it was useless... So just ignore. + +* pcre\_latin\_1\_table.c - No problem, we generated a new one in the + earlier stage. + +* pcre\_make\_latin1\_default.c - No longer used, a hack that's not + needed with dftables. Ignored + +* pcre\_try\_flipped.c - This functionality has been removed from + pcre\_exec, you cannot compile on one endianess and execute on + another any more :( Ignored. + +* pcre\_ucp\_searchfuncs.c, ucpinternal.h, ucptable.h - this + functionality is moved to pcre\_ucd.c, copy that one instead. + +OK, now go the other way and look at what was actually built for the new version of pcre: + + ~/tmp/pcre/epcre-7.6> cd ../pcre-8.33/ + ~/tmp/pcre/pcre-8.33> nm ./.libs/libpcre.a | egrep 'lib.*.o:' + +The output for this release was: + + libpcre_la-pcre_byte_order.o: + libpcre_la-pcre_compile.o: + libpcre_la-pcre_config.o: + libpcre_la-pcre_dfa_exec.o: + libpcre_la-pcre_exec.o: + libpcre_la-pcre_fullinfo.o: + libpcre_la-pcre_get.o: + libpcre_la-pcre_globals.o: + libpcre_la-pcre_jit_compile.o: + libpcre_la-pcre_maketables.o: + libpcre_la-pcre_newline.o: + libpcre_la-pcre_ord2utf8.o: + libpcre_la-pcre_refcount.o: + libpcre_la-pcre_string_utils.o: + libpcre_la-pcre_study.o: + libpcre_la-pcre_tables.o: + libpcre_la-pcre_ucd.o: + libpcre_la-pcre_valid_utf8.o: + libpcre_la-pcre_version.o: + libpcre_la-pcre_xclass.o: + libpcre_la-pcre_chartables.o: + +Libtool has changed the object names, but we can fix that and see what +sources we have already decided should exist: + + ~/tmp/pcre/pcre-8.33> NAMES=`nm ./.libs/libpcre.a | egrep 'lib.*.o:'| sed 's,libpcre_la-,,' | sed 's,.o:$,,'` + ~/tmp/pcre/pcre-8.33> for x in $NAMES; do if [ '!' -f ../epcre-8.33/$x.c ]; then echo $x; fi; done + +And the list contained: + + pcre_byte_order + pcre_jit_compile + pcre_string_utils + +pcre\_jit\_compile is actually needed, even though we have not enabled +jit, and the other two contain functionality needed, so just copy the +sources... + + ~/tmp/pcre/pcre-8.33> for x in $NAMES; do if [ '!' -f ../epcre-8.33/$x.c ]; then cp $x.c ../epcre-8.33/; fi; done + +## Test build of stripped down version of new PCRE + +Time to do a test build. Copy and edit the pcre.mk makefile and try to +get something that builds... + +I made a wrapper Makefile, hacked pcre.mk a little and did a few +changes to a few files, namely added: + + #ifdef ERLANG_INTEGRATION + #include "local_config.h" + #endif + +to pcre\_config.c and pcre\_internal.h. Also pcre.mk needs to get the +new files added and the old files removed, directory names need to be +changed and the wrapper can define most. My wrapper Makefile looked +like this: + + EPCRE_LIB = ./obj/libepcre.a + PCRE_GENINC = ./pcre_exec_loop_break_cases.inc + PCRE_OBJDIR = ./obj + V_AR = ar + V_CC = gcc + CFLAGS = -g -O2 -DHAVE_CONFIG_H -I/ldisk/pan/git/otp/erts/x86_64-unknown-linux-gnu + gen_verbose = + PCRE_DIR=. + include pcre.mk + +And the according variables were removed together with dependencies +from pcre.mk. Note that you will need to put things back in order in +pcre.mk after all testing is done. Once a 'make' is successful, you +can generate new dependencies: + + ~/tmp/pcre/epcre-8.33> gcc -MM -c -g -O2 -DHAVE_CONFIG_H -I/ldisk/pan/git/otp/erts/x86_64-unknown-linux-gnu -DERLANG_INTEGRATION *.c | grep -v $ERL_TOP + +Well, then you have to add $(PCRE\_OBJDIR)/ to each object and +$(PCRE\_DIR)/ to each header. I did it manually, it's just a couple of +files. Now your pcre.mk is fairly up to date and it's time to start +patching in the changes... + +## Actually patching in the changes to the C code + +### Fixing the functionality (interruptable pcre\_run etc) + +Begin with only pcre\_exec.c, that's the important part: + + ~/tmp/pcre/epcre-8.33> cd ../epcre-7.6/ + ~/tmp/pcre/epcre-7.6> diff -c ../pcre-7.6/pcre_exec.c ./pcre_exec.c > ../epcre_exec.c_7.6.diff + ~/tmp/pcre/epcre-7.6> cd ../epcre-8.33 + +Now - if you are lucky, you can patch the new pcre\_exec with the +patch command from the diff, but that may not be the case... Even if: + + ~/tmp/pcre/epcre-8.33> patch -p0 < ../epcre_exec.c_7.6.diff + +works like a charm, you still have to go through the main loop and see +that all do, while and for loops in the code contains COST\_CHK or at +least COST, or, if it's a small loop (over, say one UTF character), +mark it as OK with a comment. + +You should also check for other changes, like new local variables in +the pcre\_exec code etc. + +What will probably happen, is that the majority of chunks +fail. pcre\_exec is the main file for PCRE, one that is constantly +optimized and where every new feature ends up. You will probably see +so many failed HUNK's that you feel like giving up, but do not +despair, it's just a matter of patience and hard work: + +* First, fix the 'pcre\_exec' function. + + * Change the struct PcreExecContext to reflect the local variables + in this version of the code. + + * Add/update the defines that makes local variables in the code + actually stay in an allocated "exec\_context" and be sure to + initialize the "pseudo-stack-variables" in the same way as in + the declarations for the original version of the code. + + * The macros SWAPIN and SWAPOUT should be for variables that are + used a lot and we do not want to always access through the + struct. Also a few parameters are saved by SWAPIN and SWAPOUT. + + * What might be tricky is to get things deallocated in a proper + way, there is a function that's called from the BIF code to + clean up an exec\_context, be especially observant about how the + stack in the 'match' function is allocated! The first frame is + supposed to be on the C stack, but in our case is allocated in + the exec\_context. The rest of the frames are allocated but + never freed, not until the match is done. + + The variable 'frame' in the 'match' function is stored in our + additional field of the 'md' structure, that is the stack top, + but not necessarily the uppermost frame (due to reuse of old + frames, which is supposed to be an optimization...). + + * The housekeeping of the "reduction counter" in the extra\_data + struct needs to be added to all places where we break out of the + main loop of pcre\_exec. Look for 'break' and you will see the + places. Make sure to update + '*extra\_data->loop\_counter\_return' whenever you leave this + function. It all boils down to some code that loops over the + call for match and returns PCRE\_ERROR\_LOOP\_LIMIT and get's + jumped back to when the BIF is restarted. You will see it in + your diff and you will find a similar place in the new version + where you put basically the same code. + + * Fixing pcre\_exec takes about an hour of concentrated work, it + could be worse... + +* Next, go for the match function. It's simpler in some ways but + harder in other. The elimination of the C stack is already there, + you just need to modify it a little: + + * In the RRETURN macro for NO\_RECURSE, add updating of + md->loop\_limit before returning. You can see how it's done in + the diff. + + * RMATCH can be left as it is, at least it could in earlier + versions. Note however that you should mimic the allocation + strategies of RMATCH and RRETURN in the code at another place + later... The principle of the labels HEAP\_RECURSE and + HEAP\_RETURN are mimicked by our code in LOOP\_COUNT\_BREAK and + LOOP\_COUNT\_RETURN. You'll see later... + + * COST and COST\_CHK, together with the jump to + LOOP\_COUNT\_RETURN label are in the beginning of the function + 'match'. It's a block of macros and declaration of our local + variables loop\_count and loop\_limit. We patch in the code for + that, but may need to adopt it to new variable names etc. It's + important to handle the 'frames' variable correctly, dig it out + of the 'md' struct when we are restarting, but initialize it as + is done in normal NO\_RECURSE code otherwise. Note that the + COST\_CHK macro reuses the Xwhere field of the frame struct, it + is not needed when trapping. + + * The LOOP\_COUNT\_BREAK and the LOOP\_COUNT\_RETURN code can now + be added. Make sure to check both how a new stack frame should be + properly allocated by mimicking the code in RMATCH, and how (if) + it should be freed by mimicking RRETURN. Also check which + variables need to be saved. They are properly pointed out in + 8.33 with the comment 'These variables do not need to be + preserved over recursion' and appear in the beginning of the + function. Find variables of similar type in the frame structure + and reuse them. In 8.33 there are eight such variables. They are + placed at the end of the function 'match'. If You are reading + the diff, you need to scroll past all the COST\_CHK calls, + i.e. past the whole regexp machine loop. + + * Now take the time to add things like debug macros to the top of + the file and one single COST\_CHK (preferably the one right + after for(;;) in 'match'), and see if you can compile. You will + probably need to add some fields in the structures in pcre.h, + see from a larger diff what you need there and iterate until you + can compile. + + * So, what's left is to add all the COST and COST\_CHK macros, + plus marking all harmless loops as OK. There are a few rules + here: + + * Mark *every* loop with the comment 'LOOP\_COUNT: xxxx', + where xxxx is either 'Ok', 'COST' or 'CHK'. There are 175 + 'LOOP\_COUNT:' comments in 8.33. + + * Loops marked 'Ok' need no macro, either because they are so + short (like over an UTF character) or because they contain + an RMATCH macro, in which case they will be accounted for + anyway. + + * Loops marked 'COST' will have an associated 'COST(N)' macro, + either before, if we know the amount of iterations, or + within. Reductions are counted, but we will not + interrupt. This is typically in what is expected to be + medium long loops or at places where interruption is hard + (like where we have local variables that are alive. The + selection between 'COST' and 'COST\_CHK' is hard. 'COST' is + much cheaper and usually enough, but when in doubt about the + loop length, try to use 'COST\_CHK', while making very sure + there are no live block-local variables that need to be + saved over the trap. There are 49 'COST' macros in 8.33. + + * Loops marked 'CHK' shall contain a 'COST\_CHK(N)' + macro. This macro both counts reductions and may result in + an interrupt and a return to Erlang space. It is expensive + and it is vital to ensure that there are no unexpected local + variables that live past the macro. Most variables are in + the pseudo stack frame, but some regexp instructions declare + temporaries inside blocks. Make sure they are not expected + to be alive after a COST\_CHK if they are not in the + 'heapframe' structure. If they are, you need to + conditionally move them to the 'heapframe' #if + defined(ERLANG\_INTEGRATION). in 8.33 the variables 'lgb' + and 'rgb' are preserved in this way. There are 54 + 'COST\_CHK's in 8.33. + + * I've marked a few block-local variables with warnings, but + look thoroughly through the main loop to detect any new + ones. + + * Be careful when it comes to freeing the context from Erlang + (the function erts\_pcre\_free\_restart\_data), Whatever is + done there has to work *both* when the context is freed in + the middle of an operation (because of trapping) and when + some things have been freed by a successful + return. Specifically, make sure to set md->offset\_vector to + NULL whenever it's freed (in the rest of the code) and + construct release\_match\_heapframes so that it can be + called multiple times for the same heapframe (set the next + pointer in the "static" frame, i.e. the one allocated in the + md to NULL after freeing). + + * To add the costs to the main loop takes less than one work day, + keep calm and continue... + +OK, now you are done with the pcre\_exec (or at least, you think +so). The rest is simpler. You have probably already handled 'pcre.h' +and 'pcre\_internal.h' to add fields to the structures etc. Looking at +a diff from an earlier version, you will see what's left. In upgrading +to 8.33, the following things was left to do after pcre\_exec was +fixed, remember you could generate a diff with: + + ~/tmp/pcre/epcre-8.33> cd ../epcre-7.6/ + ~/tmp/pcre/epcre-7.6> (for x in *.[ch]; do if [ -f ../pcre-7.6/$x ]; then diff -c ../pcre-7.6/$x $x; fi; done) > ../epcre-7.6.diff + +Open the diff in your favorite editor and remove whatever changes you +have already made, like everything that has to do with pcre\_exec.c +and probably a large part of pcre.h/pcre\_internal.h. + +The expected result is a diff that either contains only the +'%ExternalCopyright%' comments or contains them and the addition of +the erts\_ prefix, depending on if you reverted the prefix change +(using 'git revert') before starting to work. With a little luck, the +patch of the remaining stuff should be possible to apply +automatically. If anything fails, just add it manually. + +### Fixing the erts\_prefix + +The erts\_ prefix is mostly implemented by adding '#if +defined(ERLANG\_INTEGRATION)' to a lot of function headers, inside the +COMPILE\_UTF8 part. If you then also change the PRIV and PUBL macros +in pcre\_internal.h. Typical diffs look like: + + #if defined COMPILE_PCRE8 + + #if defined(ERLANG_INTEGRATION) + + #ifndef PUBL + + #define PUBL(name) erts_pcre_##name + + #endif + + #ifndef PRIV + + #define PRIV(name) _erts_pcre_##name + + #endif + + #else + #ifndef PUBL + #define PUBL(name) pcre_##name + #endif + #ifndef PRIV + #define PRIV(name) _pcre_##name + #endif + + #endif + +and + + #if defined COMPILE_PCRE8 + + #if defined(ERLANG_INTEGRATION) + + PCRE_EXP_DECL int erts_pcre_pattern_to_host_byte_order(pcre *argument_re, + + erts_pcre_extra *extra_data, const unsigned char *tables) + + #else + PCRE_EXP_DECL int pcre_pattern_to_host_byte_order(pcre *argument_re, + pcre_extra *extra_data, const unsigned char *tables) + + #endif + +Note that some data types, like pcre\_extra are accessed with the PUBL +macro, so they need to explicitly get the prefix added. pcre.h is a +pig, as it declares prototypes for all functions regardless of +compilation ode, so there is quite a lot of '#if +defined(ERLANG\_INTEGRATION)' to add there. + +Anyway, now try to patch, using a diff where you have removed the +changes you made manually (probably to pcre\_exec.c) but make sure to +save your work (temporary git repository?) before, so you can revert +any disasters... + + ~/tmp/pcre/epcre-7.6> cd ../epcre-8.33/ + ~/tmp/pcre/epcre-8.33> patch -p0 < ../epcre-7.6_clean2.diff + +Some hunks may certainly still fail, read through the .rej file and fix it. + +### ExternalCopyright + +Now you should check that the 'ExternalCopyright' comment is present +in all source files: + + ~/tmp/pcre/epcre-8.33> for x in *.[ch]; do if grep ExternalCopyright $x > /dev/null; then true; else echo $x; fi; done + +In this upgrade (from 7.6 to 8.33) we certainly had some new and +renamed files: + + dftables.c + pcre_byte_order.c + pcre_chartables.c + pcre_jit_compile.c + pcre_latin_1_table.c + pcre_string_utils.c + pcre_ucd.c + +Go through them manually and add the 'ExternalCopyright' comment. + +## Integrate with Erlang + +Now you are done with most of the tedious work. It's time to move this +into your branch of the Erlang source tree, remove old files and add +new ones, plus add the tar file with the original pcre dist. Remember +to fix your hacked version of pcre.mk and then try to build +Erlang. You might need to update 'erl\_bif\_re.c' to reflect any +changes in the PCRE library. When it builds, run the test suites. + +Make sure to rename any files that has new names and remove any files +that are no longer present before copying in the new versions from +your temporary directory. In our example we remove 'pcre\_info.c', +'pcre\_make\_latin1\_default.c', 'pcre\_try\_flipped.c', +'ucpinternal.h' and 'ucptable.h'. We rename 'make\_latin1\_table.c' to +'dftables.c' and 'pcre\_ucp\_searchfuncs.c' to 'pcre\_ucd.c'. + +After copying in the sources, we can try to build. Do not forget to +fix whatever you did in pcre.mk to make it build locally. + +## Update test suites + +The next step is to integrate the updated PCRE tests into our test suites. + +Copy testoutput[1-9] from the testdata directory of your new version +of pcre, to the re\_SUITE\_data in stdlib's test suites. Run the +test suites and remove any bugs. Usually the bugs come from the fact +that the PCRE test suites get better and from our implementation of +global matching, which may have bugs outside of the PCRE library. The +test suite 'pcre' is the one that runs these tests. Also copy +testoutput11-8 to testoutput10, the testoutput10 file in pcre is +nowadays for the DFA, which we do not use. + +The next step is to regenerate re\_testoutput1\_replacement\_test. How +to do that is in a comment in the beginning of the file. The key +module is run\_pcre\_tests.erl, which both driver the pcre test and +generate re\_testoutput1\_replacement\_test.erl. Watch during the +generation that you do not get to many of the "Fishy character" +messages, if they are more than, say 20, you will probably need to +address the UTF8 issues in the Perl execution. As it is now, we skip +non latin1 characters in this test. You will need to run iconv on the +generated module to make it UTF-8 before running tests. + +The exact same procedure goes for the re\_testoutput1\_split\_test.erl. + +Also add copyright headers to the files after converting them to UTF-8. + +After ironing out the rest of the bugs, you should be done with the +code. + +## Update documentation + +Now it's time for the documentation, which is fairly +straightforward. Diff the pcrepattern man pages from the old and new +PCRE distros and update the re.xml file accordingly. It may help to +have the generated HTML file from the new version to cut and paste +from, but as you will notice, it's quite a few changes from HTML to +XML. All lists are reformatted, the <pre> tags are made into +either <code> or <quite> etc. Also the <P> tags are +converted to lowercase and all mentioned options and function calls +are converted to their Erlang counterpart. Really awesome work that +requires thorough reading of all new text. For the upgrade from 7.6 +to 8.33, the update of the pcrepattern part of our manual page took +about eight hours. + +## Add new relevant options to re + +Then, when all this is done, you should add any new relevant options +from the PCRE library to both the code (erl\_bif\_re.c), the specs and +the Erlang function 'copt/1' (re.erl) and the manual page +(re.xml). Make sure the options are really relevant to add to the +Erlang API, check if they are compile or run-time options (or both) and +add them to the 'parse\_options' function of erl\_bif\_re.c. Adding an +option that is just passed through to PCRE is pretty simple, at least +"code wise". + +Now you are done. Run all test suites on all machines and you will be happy. + +## Final notes + +To avoid the work of a major upgrade, it is probably worth it to keep +in pace with the changes to PCRE. The upgrade from 7.6 to 8.33, +including tracking down bugs etc, took me a total of two weeks. If +smaller diffs from the PCRE development were integrated in a more +incremental fashion, it will be much easier each time and you will +have the PCRE library up to date. PCRE should probably be updated for +each major release, instead of every five years... \ No newline at end of file -- cgit v1.2.3 From 1f4c016785a924b2e42fbb7858640be3d46e9625 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Tue, 30 Jul 2013 17:41:14 +0200 Subject: Add return_errors option to re:run/3 --- erts/emulator/beam/atom.names | 3 +++ erts/emulator/beam/erl_bif_re.c | 56 ++++++++++++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index cf8f511b85..8e5a079181 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -320,6 +320,8 @@ atom low atom Lt='<' atom machine atom match +atom match_limit +atom match_limit_recursion atom match_spec atom max atom maximum @@ -477,6 +479,7 @@ atom register atom registered_name atom reload atom rem +atom report_errors atom reset atom restart atom return_from diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index c74125ae41..46b5b848cf 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -180,6 +180,7 @@ static Eterm make_signed_integer(int x, Process *p) #define PARSE_FLAG_STARTOFFSET 8 #define PARSE_FLAG_CAPTURE_OPT 16 #define PARSE_FLAG_GLOBAL 32 +#define PARSE_FLAG_REPORT_ERRORS 64 #define CAPSPEC_VALUES 0 #define CAPSPEC_TYPE 1 @@ -276,7 +277,7 @@ parse_options(Eterm listp, /* in */ default: return -1; } - }else if (is_not_atom(item)) { + } else if (is_not_atom(item)) { return -1; } else { switch(item) { @@ -348,6 +349,10 @@ parse_options(Eterm listp, /* in */ copt |= PCRE_NEVER_UTF; fl |= PARSE_FLAG_UNIQUE_COMPILE_OPT; break; + case am_report_errors: + fl |= (PARSE_FLAG_UNIQUE_EXEC_OPT | + PARSE_FLAG_REPORT_ERRORS); + break; case am_unicode: copt |= PCRE_UTF8; fl |= (PARSE_FLAG_UNIQUE_COMPILE_OPT | PARSE_FLAG_UNICODE); @@ -389,7 +394,7 @@ parse_options(Eterm listp, /* in */ */ static Eterm -build_compile_result(Process *p, Eterm error_tag, pcre *result, int errcode, const char *errstr, int errofset, int unicode, int with_ok) +build_compile_result(Process *p, Eterm error_tag, pcre *result, int errcode, const char *errstr, int errofset, int unicode, int with_ok, Eterm extra_err_tag) { Eterm *hp; Eterm ret; @@ -402,11 +407,18 @@ build_compile_result(Process *p, Eterm error_tag, pcre *result, int errcode, con int elen = sys_strlen(errstr); int need = 3 /* tuple of 2 */ + 3 /* tuple of 2 */ + - (2 * elen) /* The error string list */; + (2 * elen) /* The error string list */ + + ((extra_err_tag != NIL) ? 3 : 0); hp = HAlloc(p, need); ret = buf_to_intlist(&hp, (char *) errstr, elen, NIL); ret = TUPLE2(hp, ret, make_small(errofset)); hp += 3; + if (extra_err_tag != NIL) { + /* Return {error_tag, {extra_tag, + {Code, String, Offset}}} instead */ + ret = TUPLE2(hp, extra_err_tag, ret); + hp += 3; + } ret = TUPLE2(hp, error_tag, ret); } else { erts_pcre_fullinfo(result, NULL, PCRE_INFO_SIZE, &pattern_size); @@ -478,7 +490,7 @@ re_compile(Process* p, Eterm arg1, Eterm arg2) &errstr, &errofset, default_table); ret = build_compile_result(p, am_error, result, errcode, - errstr, errofset, unicode, 1); + errstr, errofset, unicode, 1, NIL); erts_free(ERTS_ALC_T_RE_TMP_BUF, expr); BIF_RET(ret); } @@ -526,6 +538,7 @@ typedef struct _restart_context { } RestartContext; #define RESTART_FLAG_SUBJECT_IN_BINARY 0x1 +#define RESTART_FLAG_REPORT_MATCH_LIMIT 0x2 static void cleanup_restart_context(RestartContext *rc) { @@ -566,7 +579,19 @@ static Eterm build_exec_return(Process *p, int rc, RestartContext *restartp, Ete Eterm res; Eterm *hp; if (rc <= 0) { - res = am_nomatch; + if (restartp->flags & RESTART_FLAG_REPORT_MATCH_LIMIT) { + if (rc == PCRE_ERROR_MATCHLIMIT) { + hp = HAlloc(p,3); + res = TUPLE2(hp,am_error,am_match_limit); + } else if (rc == PCRE_ERROR_RECURSIONLIMIT) { + hp = HAlloc(p,3); + res = TUPLE2(hp,am_error,am_match_limit_recursion); + } else { + res = am_nomatch; + } + } else { + res = am_nomatch; + } } else { ReturnInfo *ri = restartp->ret_info; ReturnInfo defri = {RetIndex,0,{0}}; @@ -1043,10 +1068,20 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) result = erts_pcre_compile2(expr, comp_options, &errcode, &errstr, &errofset, default_table); if (!result) { - erts_free(ERTS_ALC_T_RE_TMP_BUF, expr); /* Compilation error gives badarg except in the compile - function */ - BIF_ERROR(p,BADARG); + function or if we have PARSE_FLAG_REPORT_ERRORS */ + if (pflags & PARSE_FLAG_REPORT_ERRORS) { + res = build_compile_result(p, am_error, result, errcode, + errstr, errofset, + (pflags & + PARSE_FLAG_UNICODE) ? 1 : 0, + 1, am_compile); + erts_free(ERTS_ALC_T_RE_TMP_BUF, expr); + BIF_RET(res); + } else { + erts_free(ERTS_ALC_T_RE_TMP_BUF, expr); + BIF_ERROR(p,BADARG); + } } if (pflags & PARSE_FLAG_GLOBAL) { Eterm precompiled = @@ -1055,7 +1090,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) errstr, errofset, (pflags & PARSE_FLAG_UNICODE) ? 1 : 0, - 0); + 0, NIL); Eterm *hp,r; erts_free(ERTS_ALC_T_RE_TMP_BUF, expr); hp = HAlloc(p,4); @@ -1190,6 +1225,9 @@ handle_iolist: } } + if (pflags & PARSE_FLAG_REPORT_ERRORS) { + restart.flags |= RESTART_FLAG_REPORT_MATCH_LIMIT; + } #ifdef DEBUG loop_count = 0xFFFFFFFF; -- cgit v1.2.3 From 8cbc9296944b5d1397d15e5615890b61549d5064 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 31 Jul 2013 15:17:27 +0200 Subject: Add match_limit and match_limit_recursion options Added to re:run and sets the corresponding fields in 'extra' struct for the PCRE match engine. The result can be viewed by also setting 'report_errors' when matching. Some housekeeping was also done... The offset option also did not properly check for offset's >= 0. Change nomatch to BADARG when pre-compiled mp() is faked: By constructing a 5-tuple with faked content but the right data types, you could do a re:run which returned nomatch when in fact the mp() was bad. The cheapest solution is to check the return from pcre_exec better. Remove unreachable code in erts_bif_re.c: Replaced tests for things that logically simply cannot happen with ASSERT. --- erts/emulator/beam/erl_bif_re.c | 156 ++++++++++++++++++++++++++++++---------- 1 file changed, 120 insertions(+), 36 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index 46b5b848cf..796fd301ad 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -181,6 +181,8 @@ static Eterm make_signed_integer(int x, Process *p) #define PARSE_FLAG_CAPTURE_OPT 16 #define PARSE_FLAG_GLOBAL 32 #define PARSE_FLAG_REPORT_ERRORS 64 +#define PARSE_FLAG_MATCH_LIMIT 128 +#define PARSE_FLAG_MATCH_LIMIT_RECURSION 256 #define CAPSPEC_VALUES 0 #define CAPSPEC_TYPE 1 @@ -193,7 +195,9 @@ parse_options(Eterm listp, /* in */ int *exec_options, /* out */ int *flags,/* out */ int *startoffset, /* out */ - Eterm *capture_spec) /* capture_spec[CAPSPEC_SIZE] */ /* out */ + Eterm *capture_spec, /* capture_spec[CAPSPEC_SIZE] */ /* out */ + int *match_limit, /* out */ + int *match_limit_recursion) /* out */ { int copt,eopt,fl; Eterm item; @@ -235,7 +239,7 @@ parse_options(Eterm listp, /* in */ case am_offset: { int tmp; - if (!term_to_int(tp[2],&tmp)) { + if (!term_to_int(tp[2],&tmp) || tmp < 0) { return -1; } if (startoffset != NULL) { @@ -244,6 +248,31 @@ parse_options(Eterm listp, /* in */ } fl |= (PARSE_FLAG_UNIQUE_EXEC_OPT|PARSE_FLAG_STARTOFFSET); break; + case am_match_limit: + { + int tmp; + if (!term_to_int(tp[2],&tmp) || tmp < 0) { + return -1; + } + if (match_limit != NULL) { + *match_limit = tmp; + } + } + fl |= (PARSE_FLAG_UNIQUE_EXEC_OPT|PARSE_FLAG_MATCH_LIMIT); + break; + case am_match_limit_recursion: + { + int tmp; + if (!term_to_int(tp[2],&tmp) || tmp < 0) { + return -1; + } + if (match_limit_recursion != NULL) { + *match_limit_recursion = tmp; + } + } + fl |= (PARSE_FLAG_UNIQUE_EXEC_OPT| + PARSE_FLAG_MATCH_LIMIT_RECURSION); + break; case am_newline: if (!is_atom(tp[2])) { return -1; @@ -460,9 +489,12 @@ re_compile(Process* p, Eterm arg1, Eterm arg2) int options = 0; int pflags = 0; int unicode = 0; +#ifdef DEBUG + int buffres; +#endif - if (parse_options(arg2,&options,NULL,&pflags,NULL,NULL) + if (parse_options(arg2,&options,NULL,&pflags,NULL,NULL,NULL,NULL) < 0) { BIF_ERROR(p,BADARG); } @@ -481,10 +513,13 @@ re_compile(Process* p, Eterm arg1, Eterm arg2) BIF_ERROR(p,BADARG); } expr = erts_alloc(ERTS_ALC_T_RE_TMP_BUF, slen + 1); - if (erts_iolist_to_buf(arg1, expr, slen) != 0) { - erts_free(ERTS_ALC_T_RE_TMP_BUF, expr); - BIF_ERROR(p,BADARG); - } +#ifdef DEBUG + buffres = +#endif + erts_iolist_to_buf(arg1, expr, slen); + + ASSERT(buffres >= 0); + expr[slen]='\0'; result = erts_pcre_compile2(expr, options, &errcode, &errstr, &errofset, default_table); @@ -910,8 +945,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) for(i=0;inum_spec < 0) - ri->num_spec = 0; + ASSERT(ri->num_spec >= 0); ++(ri->num_spec); if(ri->num_spec > sallocated) { sallocated += 10; @@ -936,8 +970,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) for(l=capture_spec[CAPSPEC_VALUES];is_list(l);l = CDR(list_val(l))) { int x; Eterm val = CAR(list_val(l)); - if (ri->num_spec < 0) - ri->num_spec = 0; + ASSERT(ri->num_spec >= 0); ++(ri->num_spec); if(ri->num_spec > sallocated) { sallocated += 10; @@ -965,6 +998,10 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) tmpb[ap->len] = '\0'; } else { ErlDrvSizeT slen; +#ifdef DEBUG + int buffres; +#endif + if (erts_iolist_size(val, &slen)) { goto error; } @@ -976,9 +1013,12 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) (tmpbsiz = slen + 1)); } } - if (erts_iolist_to_buf(val, tmpb, slen) != 0) { - goto error; - } + +#ifdef DEBUG + buffres = +#endif + erts_iolist_to_buf(val, tmpb, slen); + ASSERT(buffres >= 0); tmpb[slen] = '\0'; } build_one_capture(code,&ri,&sallocated,has_dupnames,tmpb); @@ -1030,8 +1070,11 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) unsigned long loop_count; Eterm capture[CAPSPEC_SIZE] = CAPSPEC_INIT; int is_list_cap; + int match_limit = 0; + int match_limit_recursion = 0; - if (parse_options(arg3,&comp_options,&options,&pflags,&startoffset,capture) + if (parse_options(arg3,&comp_options,&options,&pflags,&startoffset,capture, + &match_limit,&match_limit_recursion) < 0) { BIF_ERROR(p,BADARG); } @@ -1048,6 +1091,9 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) const char *errstr = ""; int errofset = 0; int capture_count; +#ifdef DEBUG + int buffres; +#endif if (pflags & PARSE_FLAG_UNICODE && (!is_binary(arg2) || !is_binary(arg1) || @@ -1060,10 +1106,14 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) } expr = erts_alloc(ERTS_ALC_T_RE_TMP_BUF, slen + 1); - if (erts_iolist_to_buf(arg2, expr, slen) != 0) { - erts_free(ERTS_ALC_T_RE_TMP_BUF, expr); - BIF_ERROR(p,BADARG); - } + +#ifdef DEBUG + buffres = +#endif + erts_iolist_to_buf(arg2, expr, slen); + + ASSERT(buffres >= 0); + expr[slen]='\0'; result = erts_pcre_compile2(expr, comp_options, &errcode, &errstr, &errofset, default_table); @@ -1168,6 +1218,16 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) restart.extra.restart_flags = 0; restart.extra.loop_counter_return = &loop_count; restart.ret_info = NULL; + + if (pflags & PARSE_FLAG_MATCH_LIMIT) { + restart.extra.flags |= PCRE_EXTRA_MATCH_LIMIT; + restart.extra.match_limit = match_limit; + } + + if (pflags & PARSE_FLAG_MATCH_LIMIT_RECURSION) { + restart.extra.flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; + restart.extra.match_limit_recursion = match_limit_recursion; + } if (pflags & PARSE_FLAG_CAPTURE_OPT) { if ((restart.ret_info = build_capture(capture,restart.code)) == NULL) { @@ -1203,6 +1263,9 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) restart.subject = (char *) (pb->bytes+offset); restart.flags |= RESTART_FLAG_SUBJECT_IN_BINARY; } else { +#ifdef DEBUG + int buffres; +#endif handle_iolist: if (erts_iolist_size(arg1, &slength)) { erts_free(ERTS_ALC_T_RE_SUBJECT, restart.ovector); @@ -1214,15 +1277,11 @@ handle_iolist: } restart.subject = erts_alloc(ERTS_ALC_T_RE_SUBJECT, slength); - if (erts_iolist_to_buf(arg1, restart.subject, slength) != 0) { - erts_free(ERTS_ALC_T_RE_SUBJECT, restart.ovector); - erts_free(ERTS_ALC_T_RE_SUBJECT, restart.code); - erts_free(ERTS_ALC_T_RE_SUBJECT, restart.subject); - if (restart.ret_info != NULL) { - erts_free(ERTS_ALC_T_RE_SUBJECT, restart.ret_info); - } - BIF_ERROR(p,BADARG); - } +#ifdef DEBUG + buffres = +#endif + erts_iolist_to_buf(arg1, restart.subject, slength); + ASSERT(buffres >= 0); } if (pflags & PARSE_FLAG_REPORT_ERRORS) { @@ -1236,6 +1295,12 @@ handle_iolist: rc = erts_pcre_exec(restart.code, &(restart.extra), restart.subject, slength, startoffset, options, restart.ovector, ovsize); + + if (rc == PCRE_ERROR_BADENDIANNESS || rc == PCRE_ERROR_BADMAGIC) { + cleanup_restart_context(&restart); + BIF_ERROR(p,BADARG); + } + ASSERT(loop_count != 0xFFFFFFFF); BUMP_REDS(p, loop_count / LOOP_FACTOR); if (rc == PCRE_ERROR_LOOP_LIMIT) { @@ -1255,7 +1320,7 @@ handle_iolist: arg2 /* To avoid GC of precompiled code, XXX: not utilized yet */, magic_bin); } - + res = build_exec_return(p, rc, &restart, arg1); cleanup_restart_context(&restart); @@ -1331,7 +1396,7 @@ BIF_RETTYPE re_inspect_2(BIF_ALIST_2) { Eterm *tp,*tmp_vec,*hp; - int rc,i,top,j; + int i,top,j; int entrysize; char *nametable, *last,*name; int has_dupnames; @@ -1340,6 +1405,10 @@ re_inspect_2(BIF_ALIST_2) Eterm res; const pcre *code; byte *temp_alloc = NULL; +#ifdef DEBUG + int infores; +#endif + if (is_not_tuple(BIF_ARG_1) || (arityval(*tuple_val(BIF_ARG_1)) != 5)) { goto error; @@ -1362,18 +1431,33 @@ re_inspect_2(BIF_ALIST_2) if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_OPTIONS, &options) != 0) goto error; - if ((rc = erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) - goto error; + +#ifdef DEBUG + infores = +#endif + erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top); + + ASSERT(infores == 0); + if (top <= 0) { hp = HAlloc(BIF_P, 3); res = TUPLE2(hp,am_namelist,NIL); erts_free_aligned_binary_bytes(temp_alloc); BIF_RET(res); } - if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize) != 0) - goto error; - if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, (unsigned char **) &nametable) != 0) - goto error; +#ifdef DEBUG + infores = +#endif + erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize); + + ASSERT(infores == 0); + +#ifdef DEBUG + infores = +#endif + erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, (unsigned char **) &nametable); + + ASSERT(infores == 0); has_dupnames = ((options & PCRE_DUPNAMES) != 0); /* First, count the names */ -- cgit v1.2.3 From 925d69c56d376d1e2371cd2236c981bc97ccb706 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Tue, 6 Aug 2013 15:07:47 +0200 Subject: Clarify relation between erts_iolist_{size|to_buf} Just some clarifying comments to future progremmers to keep the relation between error returns from the two functions. --- erts/emulator/beam/utils.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 84deaecb87..c8695e61d5 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3021,6 +3021,14 @@ buf_to_intlist(Eterm** hpp, char *buf, size_t len, Eterm tail) ** Return remaining bytes in buffer on success ** ERTS_IOLIST_TO_BUF_OVERFLOW on overflow ** ERTS_IOLIST_TO_BUF_TYPE_ERROR on type error (including that result would not be a whole number of bytes) +** +** Note! +** Do not detect indata errors in this fiunction that are not detected by erts_iolist_size! +** +** A caller should be able to rely on a successful return from erts_iolist_to_buf +** if erts_iolist_size is previously successfully called and erts_iolist_to_buf +** is called with a buffer at least as large as the value given by erts_iolist_size. +** */ ErlDrvSizeT erts_iolist_to_buf(Eterm obj, char* buf, ErlDrvSizeT alloced_len) @@ -3127,6 +3135,11 @@ ErlDrvSizeT erts_iolist_to_buf(Eterm obj, char* buf, ErlDrvSizeT alloced_len) /* * Return 0 if successful, and non-zero if unsuccessful. + * + * It is vital that if erts_iolist_to_buf would return an error for + * any type of term data, this function should do so as well. + * Any input term error detected in erts_iolist_to_buf should also + * be detected in this function! */ int erts_iolist_size(Eterm obj, ErlDrvSizeT* sizep) { -- cgit v1.2.3 From 9f3dc09c1a70bd485b211768bedf989bed7fbf21 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 12 Aug 2013 16:04:31 +0200 Subject: erts: Refactor non-pure macros to functions This is needed as some gcc versions seems to optimize this undefined behaviour in a way which breaks this code. --- erts/emulator/drivers/common/efile_drv.c | 100 +++++++++++++++++++------------ 1 file changed, 61 insertions(+), 39 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 595b0488a8..edcb56de1d 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -546,53 +546,75 @@ static void *ef_safe_realloc(void *op, Uint s) (((char *)(ev)->iov[(q)].iov_base) + (p)) /* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */ -#define EV_GET_CHAR(ev, p, pp, qp) \ - (*(pp)+1 <= (ev)->iov[*(qp)].iov_len \ - ? (*(p) = *EV_CHAR_P(ev, *(pp), *(qp)), \ - *(pp) = ( *(pp)+1 < (ev)->iov[*(qp)].iov_len \ - ? *(pp)+1 \ - : ((*(qp))++, 0)), \ - !0) \ - : 0) +#define EV_GET_CHAR(ev, p, pp, qp) efile_ev_get_char(ev, p ,pp, qp) +static int +efile_ev_get_char(ErlIOVec *ev, char *p, int *pp, int *qp) { + if (*(pp)+1 <= (ev)->iov[*(qp)].iov_len) { + *(p) = *EV_CHAR_P(ev, *(pp), *(qp)); + if (*(pp)+1 < (ev)->iov[*(qp)].iov_len) + *(pp) = *(pp)+1; + else + (*(qp))++; + return !0; + } + return 0; +} /* Uint32 EV_UINT32(ErlIOVec *ev, int p, int q)*/ #define EV_UINT32(ev, p, q) \ ((Uint32) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p))) /* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */ -#define EV_GET_UINT32(ev, p, pp, qp) \ - (*(pp)+4 <= (ev)->iov[*(qp)].iov_len \ - ? (*(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24) \ - | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16) \ - | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8) \ - | (EV_UINT32(ev, *(pp)+3, *(qp))), \ - *(pp) = ( *(pp)+4 < (ev)->iov[*(qp)].iov_len \ - ? *(pp)+4 \ - : ((*(qp))++, 0)), \ - !0) \ - : 0) +#define EV_GET_UINT32(ev, p, pp, qp) efile_ev_get_uint32(ev,p,pp,qp) +static int +efile_ev_get_uint32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) { + if (*(pp)+4 <= (ev)->iov[*(qp)].iov_len) { + *(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24) + | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16) + | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8) + | (EV_UINT32(ev, *(pp)+3, *(qp))); + if (*(pp)+4 < (ev)->iov[*(qp)].iov_len) + *(pp) = *(pp)+4; + else + (*(qp))++; + return !0; + } + return 0; +} /* Uint64 EV_UINT64(ErlIOVec *ev, int p, int q)*/ #define EV_UINT64(ev, p, q) \ ((Uint64) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p))) -/* int EV_GET_UINT64(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */ -#define EV_GET_UINT64(ev, p, pp, qp) \ - (*(pp)+8 <= (ev)->iov[*(qp)].iov_len \ - ? (*(p) = (EV_UINT64(ev, *(pp), *(qp)) << 56) \ - | (EV_UINT64(ev, *(pp)+1, *(qp)) << 48) \ - | (EV_UINT64(ev, *(pp)+2, *(qp)) << 40) \ - | (EV_UINT64(ev, *(pp)+3, *(qp)) << 32) \ - | (EV_UINT64(ev, *(pp)+4, *(qp)) << 24) \ - | (EV_UINT64(ev, *(pp)+5, *(qp)) << 16) \ - | (EV_UINT64(ev, *(pp)+6, *(qp)) << 8) \ - | (EV_UINT64(ev, *(pp)+7, *(qp))), \ - *(pp) = ( *(pp)+8 < (ev)->iov[*(qp)].iov_len \ - ? *(pp)+8 \ - : ((*(qp))++, 0)), \ - !0) \ - : 0) +/* int EV_GET_UINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */ +#define EV_GET_UINT64(ev, p, pp, qp) efile_ev_get_uint64(ev,p,pp,qp) +static int +efile_ev_get_uint64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) { + if (*(pp)+8 <= (ev)->iov[*(qp)].iov_len) { + *(p) = (EV_UINT64(ev, *(pp), *(qp)) << 56) + | (EV_UINT64(ev, *(pp)+1, *(qp)) << 48) + | (EV_UINT64(ev, *(pp)+2, *(qp)) << 40) + | (EV_UINT64(ev, *(pp)+3, *(qp)) << 32) + | (EV_UINT64(ev, *(pp)+4, *(qp)) << 24) + | (EV_UINT64(ev, *(pp)+5, *(qp)) << 16) + | (EV_UINT64(ev, *(pp)+6, *(qp)) << 8) + | (EV_UINT64(ev, *(pp)+7, *(qp))); + if (*(pp)+8 < (ev)->iov[*(qp)].iov_len) + *(pp) = *(pp)+8; + else + (*(qp))++; + return !0; + } + return 0; +} +/* int EV_GET_SINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */ +#define EV_GET_SINT64(ev, p, pp, qp) efile_ev_get_sint64(ev,p,pp,qp) +static int +efile_ev_get_sint64(ErlIOVec *ev, Sint64 *p, int *pp, int *qp) { + Uint64 *tmp = (Uint64*)p; + return EV_GET_UINT64(ev,tmp,pp,qp); +} #if 0 @@ -3604,7 +3626,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { for(i = 0; i < n; i++) { Uint32 sizeH, sizeL; size_t size; - if ( !EV_GET_UINT64(ev, &d->c.pwritev.specs[i].offset, &p, &q) + if ( !EV_GET_SINT64(ev, &d->c.pwritev.specs[i].offset, &p, &q) || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { /* Misalignment in buffer */ @@ -3746,7 +3768,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { for (i = 1; i < 1+n; i++) { Uint32 sizeH, sizeL; size_t size; - if ( !EV_GET_UINT64(ev, &d->c.preadv.offsets[i-1], &p, &q) + if ( !EV_GET_SINT64(ev, &d->c.preadv.offsets[i-1], &p, &q) || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { reply_posix_error(desc, EINVAL); @@ -3814,7 +3836,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { Uint32 origin; /* Origin of seek. */ if (ev->size < 1+8+4 - || !EV_GET_UINT64(ev, &offset, &p, &q) + || !EV_GET_SINT64(ev, &offset, &p, &q) || !EV_GET_UINT32(ev, &origin, &p, &q)) { /* Wrong length of buffer to contain offset and origin */ reply_posix_error(desc, EINVAL); @@ -3927,7 +3949,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { goto done; } if (ev->size < 1+1+8+4 - || !EV_GET_UINT64(ev, &hdr_offset, &p, &q) + || !EV_GET_SINT64(ev, &hdr_offset, &p, &q) || !EV_GET_UINT32(ev, &max_size, &p, &q)) { /* Buffer too short to contain * the header offset and max size spec */ -- cgit v1.2.3 From c49f2d1c169add2262a24696f85ea4b8fb8e49a2 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Wed, 14 Aug 2013 12:23:22 +0200 Subject: erts: fixed doc regarding binary_part --- erts/doc/src/erlang.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 767edc1cc0..5ee40823bc 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -235,7 +235,7 @@ 1> Bin = <<1,2,3,4,5,6,7,8,9,10>>. -2> binary_part(Bin,{byte_size(Bin), -5)). +2> binary_part(Bin,{byte_size(Bin), -5}). <<6,7,8,9,10>> -- cgit v1.2.3 From 52cb62b7930d9c7b9e04a210ff6b02946f27ae79 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 14 Aug 2013 17:09:13 +0200 Subject: Workaround TR gnu/181328, GCC 4.2.1 20070831 on FreeBSD 9.1 The bug manifests so that initialization of an automatic variable happens after a pointer to the variable is dereferenced, causing build_exec_return to take the wrong branch by default. The bug is verified to happen even outside the VM and is reported to FreeBSD as trouble ID gnu/181328. The workaround is a slight code rewrite which is optimized differently. Also removed two warnings about dereferencing type-punned pointers, which did not affect the bug. --- erts/emulator/beam/erl_bif_re.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index 796fd301ad..99c31738a5 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -628,11 +628,15 @@ static Eterm build_exec_return(Process *p, int rc, RestartContext *restartp, Ete res = am_nomatch; } } else { - ReturnInfo *ri = restartp->ret_info; + ReturnInfo *ri; ReturnInfo defri = {RetIndex,0,{0}}; - if (ri == NULL) { + + if (restartp->ret_info == NULL) { ri = &defri; + } else { + ri = restartp->ret_info; } + if (ri->type == RetNone) { res = am_match; } else if (ri->type == RetIndex){ @@ -923,7 +927,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) { int rc,i,top; int entrysize; - char *nametable, *last = NULL; + unsigned char *nametable, *last = NULL; int has_dupnames; unsigned long options; @@ -938,13 +942,13 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) } if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize) != 0) goto error; - if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, (unsigned char **) &nametable) != 0) + if (erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable) != 0) goto error; has_dupnames = ((options & PCRE_DUPNAMES) != 0); for(i=0;inum_spec >= 0); ++(ri->num_spec); if(ri->num_spec > sallocated) { @@ -955,7 +959,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) /* This could be more effective, we actually have the names and could fill in the vector immediately. Now we lookup the name again. */ - build_one_capture(code,&ri,&sallocated,has_dupnames,nametable+2); + build_one_capture(code,&ri,&sallocated,has_dupnames,(char *) nametable+2); } else { ri->v[ri->num_spec - 1] = PICK_INDEX(nametable); } @@ -1236,7 +1240,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) BIF_ERROR(p,BADARG); } } - + /* Optimized - if already in binary off heap, keep that and avoid copying, also binary returns can be sub binaries in that case */ @@ -1398,7 +1402,7 @@ re_inspect_2(BIF_ALIST_2) Eterm *tp,*tmp_vec,*hp; int i,top,j; int entrysize; - char *nametable, *last,*name; + unsigned char *nametable, *last,*name; int has_dupnames; unsigned long options; int num_names; @@ -1455,7 +1459,7 @@ re_inspect_2(BIF_ALIST_2) #ifdef DEBUG infores = #endif - erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, (unsigned char **) &nametable); + erts_pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable); ASSERT(infores == 0); @@ -1465,7 +1469,8 @@ re_inspect_2(BIF_ALIST_2) last = NULL; name = nametable; for(i=0;i Date: Tue, 20 Aug 2013 11:54:41 +0200 Subject: erts: fixed documentation regarding tty and arrow keys --- erts/doc/src/tty.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/tty.xml b/erts/doc/src/tty.xml index 15b67c8c7d..b16523e085 100644 --- a/erts/doc/src/tty.xml +++ b/erts/doc/src/tty.xml @@ -47,7 +47,7 @@
Normal Mode

In normal mode keystrokes from the user are collected and interpreted by . Most of the emacs line editing commands are supported. The following is a complete list of the supported line editing commands.

-

Note: The notation means pressing the control key and the letter simultaneously. means pressing the key followed by the letter . and represent the keys with the same name on the keyboard, whereas and represent the corresponding cursor keys. +

Note: The notation means pressing the control key and the letter simultaneously. means pressing the key followed by the letter . and represent the keys with the same name on the keyboard, whereas and represent the corresponding arrow keys.

-- cgit v1.2.3 From 68cbc7315be2db64622f2cb4a168ad60efb90259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 19 Aug 2013 14:16:34 +0200 Subject: erlc_SUITE: Test the -M* options --- erts/test/erlc_SUITE.erl | 83 +++++++++++++++++++++- .../src/erl_test_missing_header.erl | 22 ++++++ 2 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl (limited to 'erts') diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index ab774dbc4f..ed7a43c7e7 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -23,7 +23,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, compile_erl/1, compile_yecc/1, compile_script/1, - compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1]). + compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1, + make_dep_options/1]). -include_lib("test_server/include/test_server.hrl"). @@ -31,7 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [compile_erl, compile_yecc, compile_script, compile_mib, - good_citizen, deep_cwd, arg_overflow]. + good_citizen, deep_cwd, arg_overflow, make_dep_options]. groups() -> []. @@ -255,13 +256,89 @@ erlc() -> Erlc -> "\"" ++ Erlc ++ "\"" end. - + +make_dep_options(Config) -> + {SrcDir,OutDir,Cmd} = get_cmd(Config), + FileName = filename:join(SrcDir, "erl_test_ok.erl"), + + + DepRE = ["/erl_test_ok[.]beam: \\\\$", + "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", + "_OK_"], + + DepRETarget = + ["^target: \\\\$", + "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", + "_OK_"], + + DepREMP = + ["/erl_test_ok[.]beam: \\\\$", + "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", + [], + "/system_test/erlc_SUITE_data/include/erl_test.hrl:$", + "_OK_"], + + DepREMissing = + ["/erl_test_missing_header[.]beam: \\\\$", + "/system_test/erlc_SUITE_data/src/erl_test_missing_header[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl \\\\$", + "missing.hrl$", + "_OK_"], + + %% Test plain -M + run(Config, Cmd, FileName, "-M", DepRE), + + %% Test -MF File + DepFile = filename:join(OutDir, "my.deps"), + run(Config, Cmd, FileName, "-MF "++DepFile, ["_OK_"]), + {ok,MFBin} = file:read_file(DepFile), + verify_result(binary_to_list(MFBin)++["_OK_"], DepRE), + + %% Test -MD + run(Config, Cmd, FileName, "-MD", ["_OK_"]), + MDFile = filename:join(OutDir, "erl_test_ok.Pbeam"), + {ok,MFBin} = file:read_file(MDFile), + + %% Test -M -MT Target + run(Config, Cmd, FileName, "-M -MT target", DepRETarget), + + %% Test -MF File -MT Target + TargetDepFile = filename:join(OutDir, "target.deps"), + run(Config, Cmd, FileName, "-MF "++TargetDepFile++" -MT target", + ["_OK_"]), + {ok,TargetBin} = file:read_file(TargetDepFile), + verify_result(binary_to_list(TargetBin)++["_OK_"], DepRETarget), + + %% Test -MD -MT Target + run(Config, Cmd, FileName, "-MD -MT target", ["_OK_"]), + TargetMDFile = filename:join(OutDir, "erl_test_ok.Pbeam"), + {ok,TargetBin} = file:read_file(TargetMDFile), + + %% Test -M -MQ Target. (Note: Passing a $ on the command line + %% portably for Unix and Windows is tricky, so we will just test + %% that MQ works at all.) + run(Config, Cmd, FileName, "-M -MQ target", DepRETarget), + + %% Test -M -MP + run(Config, Cmd, FileName, "-M -MP", DepREMP), + + %% Test -M -MG + MissingHeader = filename:join(SrcDir, "erl_test_missing_header.erl"), + run(Config, Cmd, MissingHeader, "-M -MG", DepREMissing), + ok. + %% Runs a command. run(Config, Cmd0, Name, Options, Expect) -> Cmd = Cmd0 ++ " " ++ Options ++ " " ++ Name, io:format("~s", [Cmd]), Result = run_command(Config, Cmd), + verify_result(Result, Expect). + +verify_result(Result, Expect) -> Messages = split(Result, [], []), io:format("Result: ~p", [Messages]), io:format("Expected: ~p", [Expect]), diff --git a/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl new file mode 100644 index 0000000000..4d6c42c803 --- /dev/null +++ b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl @@ -0,0 +1,22 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The 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(erl_test_missing_header). +-include("erl_test.hrl"). +-include("missing.hrl"). -- cgit v1.2.3 From 3b1d9f46c187ecdad3930c52fa4fbe4d547277c8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 20 Aug 2013 17:19:36 +0200 Subject: erts: Fix bug in translating ev macros to functions --- erts/emulator/drivers/common/efile_drv.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index edcb56de1d..9e95325c24 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -553,8 +553,10 @@ efile_ev_get_char(ErlIOVec *ev, char *p, int *pp, int *qp) { *(p) = *EV_CHAR_P(ev, *(pp), *(qp)); if (*(pp)+1 < (ev)->iov[*(qp)].iov_len) *(pp) = *(pp)+1; - else + else { (*(qp))++; + *pp = 0; + } return !0; } return 0; @@ -575,8 +577,10 @@ efile_ev_get_uint32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) { | (EV_UINT32(ev, *(pp)+3, *(qp))); if (*(pp)+4 < (ev)->iov[*(qp)].iov_len) *(pp) = *(pp)+4; - else + else { (*(qp))++; + *pp = 0; + } return !0; } return 0; @@ -601,8 +605,10 @@ efile_ev_get_uint64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) { | (EV_UINT64(ev, *(pp)+7, *(qp))); if (*(pp)+8 < (ev)->iov[*(qp)].iov_len) *(pp) = *(pp)+8; - else + else { (*(qp))++; + *pp = 0; + } return !0; } return 0; -- cgit v1.2.3 From 20e0509d4e04fada3019639bc82d78b89f06b0fc Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 13 Aug 2013 17:14:11 +0200 Subject: erts: Add option to include nifs statically Both crypto and asn1 are supported. --- erts/configure.in | 14 ++++++++-- erts/doc/src/erl_nif.xml | 2 ++ erts/emulator/Makefile.in | 36 ++++++++++++++++++++---- erts/emulator/beam/erl_nif.c | 18 ++++++++---- erts/emulator/beam/erl_nif.h | 15 +++++++--- erts/emulator/beam/global.h | 6 ++++ erts/emulator/beam/utils.c | 21 ++++++++++++++ erts/emulator/utils/make_driver_tab | 55 +++++++++++++++++++++++++++++++++++++ 8 files changed, 150 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 64436e933c..3b4417d10e 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -419,6 +419,11 @@ else esac fi +AC_ARG_ENABLE(static-nifs, +AS_HELP_STRING([--enable-static-nifs], [link nifs statically. If yes then all nifs in all Erlang/OTP applications will be statically linked into the main binary. It is also possible to give a list of nifs that should be linked statically. The list should be a comma seperated and contain the absolute path to a .a archive for each nif that is to be statically linked. The name of the .a archive has to be the same as the name of the nif. Note that you have to link any external dependencies that the nifs have to the main binary, so for the crypto nif you want to pass LIBS=-lcrypto to configure.]), + STATIC_NIFS="$enableval", + STATIC_NIFS=no) +AC_SUBST(STATIC_NIFS) dnl ---------------------------------------------------------------------- dnl Checks for programs. @@ -457,7 +462,7 @@ dnl extra_flags="-I${ERL_TOP}/erts/$host $OTP_EXTRA_FLAGS" CFLAGS="$CFLAGS $extra_flags" -DEBUG_CFLAGS="-g $CPPFLAGS $extra_flags" +DEBUG_CFLAGS="-g $CPPFLAGS $extra_flags $DEBUG_CFLAGS" DEBUG_FLAGS=-g dnl @@ -3597,6 +3602,7 @@ fi DED_EMU_THR_DEFS=$EMU_THR_DEFS DED_CFLAGS="$CFLAGS $CPPFLAGS" if test "x$GCC" = xyes; then + DED_STATIC_CFLAGS="$DED_CFLAGS" DED_CFLAGS="$DED_CFLAGS -fPIC" fi @@ -3604,11 +3610,14 @@ DED_EXT=so case $host_os in win32) DED_EXT=dll;; darwin*) - DED_CFLAGS="$DED_CFLAGS -fno-common";; + DED_CFLAGS="$DED_CFLAGS -fno-common" + DED_STATIC_CFLAGS="$DED_STATIC_CFLAGS -fno-common";; *) ;; esac +DED_STATIC_CFLAGS="$DED_STATIC_CFLAGS -DSTATIC_ERLANG_NIF -DSTATIC_ERLANG_DRIVER" + # If DED_LD is set in environment, we expect all DED_LD* variables # to be specified (cross compiling) if test "x$DED_LD" = "x"; then @@ -3718,6 +3727,7 @@ fi AC_SUBST(DED_EXT) AC_SUBST(DED_SYS_INCLUDE) AC_SUBST(DED_CFLAGS) +AC_SUBST(DED_STATIC_CFLAGS) AC_SUBST(DED_LD) AC_SUBST(DED_LDFLAGS) AC_SUBST(DED_LD_FLAG_RUNTIME_LIBRARY_PATH) diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 01651aa83f..7ac8181d47 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -330,6 +330,8 @@ ok upgrade will be called to initialize the library. unload is called to release the library. They are all described individually below.

+

If compiling a nif for static inclusion via --enable-static-nifs you + have to define STATIC_ERLANG_NIF before the ERL_NIF_INIT declaration.

int (*load)(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 9751982103..db5642b9c3 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -136,6 +136,19 @@ endif endif endif +STATIC_NIFS=@STATIC_NIFS@ +ifneq ($(STATIC_NIFS),no) +ifeq ($(STATIC_NIFS),yes) +STATIC_NIFS=$(ERL_TOP)/lib/asn1/priv/lib/$(TARGET)/asn1rt_nif.a \ + $(ERL_TOP)/lib/crypto/priv/lib/$(TARGET)/crypto$(TYPEMARKER).a +endif +comma:=, +space:= +space+= +STATIC_NIFS:=$(subst $(comma),$(space),$(STATIC_NIFS)) +endif + + # # NOTE: When adding a new type update ERL_BUILD_TYPE_MARKER in sys/unix/sys.c # @@ -557,8 +570,8 @@ $(TARGET)/erl_version.h: ../vsn.mk GENERATE += $(TARGET)/erl_version.h # driver table -$(TTF_DIR)/driver_tab.c: Makefile.in - $(gen_verbose)LANG=C $(PERL) utils/make_driver_tab -o $@ $(DRV_OBJS) +$(TTF_DIR)/driver_tab.c: Makefile.in utils/make_driver_tab + $(gen_verbose)LANG=C $(PERL) utils/make_driver_tab -o $@ $(STATIC_NIF_LIBS) $(DRV_OBJS) GENERATE += $(TTF_DIR)/driver_tab.c @@ -797,6 +810,17 @@ endif DRV_OBJS += $(OBJDIR)/ttsl_drv.o +ifneq ($(STATIC_NIFS),no) +STATIC_NIF_LIBS = $(STATIC_NIFS) +DEPLIBS += $(STATIC_NIF_LIBS) + +$(STATIC_NIF_LIBS): + $(MAKE) BUILD_STATIC_LIBS=1 TYPE=$(TYPE) -C $(ERL_TOP)/lib/ static_lib + +else +STATIC_NIF_LIBS= +endif + ifeq ($(ERTS_ENABLE_KERNEL_POLL),yes) OS_OBJS += $(OBJDIR)/erl_poll.kp.o \ $(OBJDIR)/erl_check_io.kp.o \ @@ -935,18 +959,18 @@ $(OBJDIR)/hipe_arm.o: hipe/hipe_arm.c # ---------------------------------------------------------------------- # The emulator itself +# ifeq ($(TARGET), win32) # Only the basic erlang to begin with eh? $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(ld_verbose)$(PURIFY) $(LD) -dll -def:sys/$(ERLANG_OSTYPE)/erl.def -implib:$(BINDIR)/erl_dll.lib -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \ - $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) $(LIBS) + $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) $(STATIC_NIF_LIBS) $(LIBS) else - - $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(ld_verbose)$(PURIFY) $(LD) -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \ - $(HIPEBEAMLDFLAGS) $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) $(LIBS) + $(HIPEBEAMLDFLAGS) $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) \ + $(STATIC_NIF_LIBS) $(LIBS) endif diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 48f8be8dd3..673012a9af 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1213,7 +1213,8 @@ static void close_lib(struct erl_module_nif* lib) lib->entry->unload(&env, lib->priv_data); post_nif_noproc(&env); } - erts_sys_ddll_close(lib->handle); + if (!erts_is_static_nif(lib->handle)) + erts_sys_ddll_close(lib->handle); lib->handle = NULL; } @@ -1564,7 +1565,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) static const char upgrade[] = "upgrade"; char* lib_name = NULL; void* handle = NULL; - void* init_func; + void* init_func = NULL; ErlNifEntry* entry = NULL; ErlNifEnv env; int len, i, err; @@ -1577,6 +1578,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) int veto; struct erl_module_nif* lib = NULL; int reload_warning = 0; + char tmp_buf[255]; len = list_length(BIF_ARG_1); if (len < 0) { @@ -1613,13 +1615,18 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) mod=erts_get_module(mod_atom, erts_active_code_ix()); ASSERT(mod != NULL); + init_func = erts_static_nif_get_nif_init(erts_basename(lib_name,tmp_buf)); + if (init_func != NULL) + handle = init_func; + if (!in_area(caller, mod->curr.code, mod->curr.code_length)) { ASSERT(in_area(caller, mod->old.code, mod->old.code_length)); ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old " "module '%T' not allowed", mod_atom); } - else if ((err=erts_sys_ddll_open2(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) { + else if (init_func == NULL && + (err=erts_sys_ddll_open2(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) { const char slogan[] = "Failed to load NIF library"; if (strstr(errdesc.str, lib_name) != NULL) { ret = load_nif_error(BIF_P, "load_failed", "%s: '%s'", slogan, errdesc.str); @@ -1628,7 +1635,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ret = load_nif_error(BIF_P, "load_failed", "%s %s: '%s'", slogan, lib_name, errdesc.str); } } - else if (erts_sys_ddll_load_nif_init(handle, &init_func, &errdesc) != ERL_DE_NO_ERROR) { + else if (init_func == NULL && + erts_sys_ddll_load_nif_init(handle, &init_func, &errdesc) != ERL_DE_NO_ERROR) { ret = load_nif_error(BIF_P, bad_lib, "Failed to find library init" " function: '%s'", errdesc.str); @@ -1784,7 +1792,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) if (lib != NULL) { erts_free(ERTS_ALC_T_NIF, lib); } - if (handle != NULL) { + if (handle != NULL && !erts_is_static_nif(handle)) { erts_sys_ddll_close(handle); } erts_sys_ddll_free_error(&errdesc); diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 8006741a63..5f4dc21d5c 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -168,7 +168,7 @@ extern TWinDynNifCallbacks WinDynNifCallbacks; # undef ERL_NIF_API_FUNC_DECL #endif -#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) && !defined(STATIC_ERLANG_DRIVER) +#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) && !defined(STATIC_ERLANG_DRIVER) && !defined(STATIC_ERLANG_NIF) # define ERL_NIF_API_FUNC_MACRO(NAME) (WinDynNifCallbacks.NAME) # include "erl_nif_api_funcs.h" /* note that we have to keep ERL_NIF_API_FUNC_MACRO defined */ @@ -180,15 +180,22 @@ extern TWinDynNifCallbacks WinDynNifCallbacks; # undef ERL_NIF_API_FUNC_DECL #endif - #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_INIT_GLOB TWinDynNifCallbacks WinDynNifCallbacks; -# define ERL_NIF_INIT_DECL(MODNAME) __declspec(dllexport) ErlNifEntry* nif_init(TWinDynNifCallbacks* callbacks) +# ifdef STATIC_ERLANG_NIF +# define ERL_NIF_INIT_DECL(MODNAME) __declspec(dllexport) ErlNifEntry* MODNAME ## _nif_init(TWinDynNifCallbacks* callbacks) +# else +# define ERL_NIF_INIT_DECL(MODNAME) __declspec(dllexport) ErlNifEntry* nif_init(TWinDynNifCallbacks* callbacks) +# endif # define ERL_NIF_INIT_BODY memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks)) #else # define ERL_NIF_INIT_GLOB # define ERL_NIF_INIT_BODY -# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void) +# ifdef STATIC_ERLANG_NIF +# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _nif_init(void) +# else +# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void) +# endif #endif diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 7f7b6f5d1e..2acc86cf42 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -852,11 +852,17 @@ Port *erts_get_heart_port(void); void erts_lcnt_enable_io_lock_count(int enable); #endif +/* driver_tab.c */ +typedef void *(*ErtsStaticNifInitFPtr)(void); +ErtsStaticNifInitFPtr erts_static_nif_get_nif_init(const char *name); +int erts_is_static_nif(void *handle); + /* erl_drv_thread.c */ void erl_drv_thr_init(void); /* utils.c */ void erts_cleanup_offheap(ErlOffHeap *offheap); +const char *erts_basename(const char* path, char* buff); Uint64 erts_timestamp_millis(void); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index c71f8f0712..6293286c75 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -4019,6 +4019,27 @@ erts_smp_ensure_later_interval_acqb(erts_interval_t *icp, Uint64 ic) #endif } +const char *erts_basename(const char* path, char* buff) { + /* This function is not compliant with bash basename. Edge cases like "//" + and "/path//" do not work properly. + */ + int i; + int len = strlen(path); + const char *basename = path; + for (i = 0; path[i] != '\0'; i++) { + if (path[i] == '/') { + if (path[i+1] == '\0') { + memcpy(buff,basename,len - (basename-path)); + buff[len - (basename-path)-1] = '\0'; + basename = buff; + break; + } else { basename = path+i;} + } + } + if (basename == path) + return path; + return basename+1; +} /* * A millisecond timestamp without time correction where there's no hrtime diff --git a/erts/emulator/utils/make_driver_tab b/erts/emulator/utils/make_driver_tab index fbbfa3e49e..06928a3a4a 100755 --- a/erts/emulator/utils/make_driver_tab +++ b/erts/emulator/utils/make_driver_tab @@ -27,7 +27,9 @@ use File::Basename; # usage: make_driver_tab [-o filename] drivers... my $file = ""; +my $nif = ""; my @drivers = (); +my @nifs = (); while (@ARGV) { my $d = shift; @@ -35,6 +37,12 @@ while (@ARGV) { $file = shift or die("-o requires argument"); next; } + if ( $d =~ /^.*\.a$/ ) { + $d = basename $d; + $d =~ s/\.a$//; # strip .a + push(@nifs, $d); + next; + } $d = basename $d; $d =~ s/drv(\..*|)$//; # strip drv.* or just drv push(@drivers, $d); @@ -52,6 +60,7 @@ print < #include "global.h" + EOF # "extern" declarations @@ -68,4 +77,50 @@ foreach (@drivers) { print " NULL\n};\n"; +print < Date: Thu, 15 Aug 2013 17:04:52 +0200 Subject: erts: Add support for static linked-in drivers None of the OTP linked-in driver are supported --- erts/configure.in | 6 ++++++ erts/doc/src/driver_entry.xml | 2 ++ erts/emulator/Makefile.in | 42 ++++++++++++++++++++++++++++--------- erts/emulator/beam/erl_driver.h | 14 +++++++++---- erts/emulator/beam/global.h | 1 + erts/emulator/beam/io.c | 1 + erts/emulator/utils/make_driver_tab | 42 ++++++++++++++++++++++++++++++++----- 7 files changed, 89 insertions(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 3b4417d10e..4e60b27ee9 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -425,6 +425,12 @@ AS_HELP_STRING([--enable-static-nifs], [link nifs statically. If yes then all ni STATIC_NIFS=no) AC_SUBST(STATIC_NIFS) +AC_ARG_ENABLE(static-drivers, +AS_HELP_STRING([--enable-static-drivers], [comma seperated list of linked-in drivers to link statically with the main binary. The list should contain the absolute path to a .a archive for each driver that is to be statically linked. The name of the .a archive has to be the same as the name of the driver.]), + STATIC_DRIVERS="$enableval", + STATIC_DRIVERS=no) +AC_SUBST(STATIC_DRIVERS) + dnl ---------------------------------------------------------------------- dnl Checks for programs. dnl ---------------------------------------------------------------------- diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml index 69a4dea466..48dfcb09b1 100644 --- a/erts/doc/src/driver_entry.xml +++ b/erts/doc/src/driver_entry.xml @@ -110,6 +110,8 @@

When the driver has passed the driver_entry over to the emulator, the driver is not allowed to modify the driver_entry.

+

If compiling a driver for static inclusion via --enable-static-drivers you + have to define STATIC_ERLANG_DRIVER before the DRIVER_INIT declaration.

Do not declare the driver_entry const. This since the emulator needs to modify the handle, and the handle2 diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index db5642b9c3..048e92baad 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -136,18 +136,27 @@ endif endif endif +comma:=, +space:= +space+= + STATIC_NIFS=@STATIC_NIFS@ ifneq ($(STATIC_NIFS),no) ifeq ($(STATIC_NIFS),yes) STATIC_NIFS=$(ERL_TOP)/lib/asn1/priv/lib/$(TARGET)/asn1rt_nif.a \ $(ERL_TOP)/lib/crypto/priv/lib/$(TARGET)/crypto$(TYPEMARKER).a endif -comma:=, -space:= -space+= STATIC_NIFS:=$(subst $(comma),$(space),$(STATIC_NIFS)) endif +STATIC_DRIVERS=@STATIC_DRIVERS@ +ifneq ($(STATIC_DRIVERS),no) +ifeq ($(STATIC_DRIVERS),yes) +STATIC_DRIVERS= +endif +STATIC_DRIVERS:=$(subst $(comma),$(space),$(STATIC_DRIVERS)) +endif + # # NOTE: When adding a new type update ERL_BUILD_TYPE_MARKER in sys/unix/sys.c @@ -571,7 +580,7 @@ GENERATE += $(TARGET)/erl_version.h # driver table $(TTF_DIR)/driver_tab.c: Makefile.in utils/make_driver_tab - $(gen_verbose)LANG=C $(PERL) utils/make_driver_tab -o $@ $(STATIC_NIF_LIBS) $(DRV_OBJS) + $(gen_verbose)LANG=C $(PERL) utils/make_driver_tab -o $@ -nifs $(STATIC_NIF_LIBS) -drivers $(DRV_OBJS) $(STATIC_DRIVER_LIBS) GENERATE += $(TTF_DIR)/driver_tab.c @@ -813,14 +822,25 @@ endif ifneq ($(STATIC_NIFS),no) STATIC_NIF_LIBS = $(STATIC_NIFS) DEPLIBS += $(STATIC_NIF_LIBS) - -$(STATIC_NIF_LIBS): - $(MAKE) BUILD_STATIC_LIBS=1 TYPE=$(TYPE) -C $(ERL_TOP)/lib/ static_lib - +COMPILE_STATIC_LIBS = yes else STATIC_NIF_LIBS= endif +ifneq ($(STATIC_DRIVERS),no) +STATIC_DRIVER_LIBS = $(STATIC_DRIVERS) +DEPLIBS += $(STATIC_DRIVER_LIBS) +COMPILE_STATIC_LIBS = yes +else +STATIC_DRIVER_LIBS= +endif + +ifeq ($(COMPILE_STATIC_LIBS),yes) + +$(STATIC_NIF_LIBS) $(STATIC_DRIVER_LIBS): + $(MAKE) BUILD_STATIC_LIBS=1 TYPE=$(TYPE) -C $(ERL_TOP)/lib/ static_lib +endif + ifeq ($(ERTS_ENABLE_KERNEL_POLL),yes) OS_OBJS += $(OBJDIR)/erl_poll.kp.o \ $(OBJDIR)/erl_check_io.kp.o \ @@ -965,12 +985,14 @@ ifeq ($(TARGET), win32) # Only the basic erlang to begin with eh? $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(ld_verbose)$(PURIFY) $(LD) -dll -def:sys/$(ERLANG_OSTYPE)/erl.def -implib:$(BINDIR)/erl_dll.lib -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \ - $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) $(STATIC_NIF_LIBS) $(LIBS) + $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) $(STATIC_NIF_LIBS) \ + $(STATIC_DRIVER_LIBS) $(LIBS) else $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) + echo $(DEPLIBS) $(ld_verbose)$(PURIFY) $(LD) -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \ $(HIPEBEAMLDFLAGS) $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) \ - $(STATIC_NIF_LIBS) $(LIBS) + $(STATIC_NIF_LIBS) $(STATIC_DRIVER_LIBS) $(LIBS) endif diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index e280563de1..f43f5a766e 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -365,17 +365,23 @@ typedef struct erl_drv_entry { * It must initialize a ErlDrvEntry structure and return a pointer to it. */ +#ifdef STATIC_ERLANG_DRIVER +# define ERLANG_DRIVER_NAME(NAME) NAME ## _driver_init +#else +# define ERLANG_DRIVER_NAME(NAME) driver_init +#endif + /* For windows dynamic drivers */ #ifndef ERL_DRIVER_TYPES_ONLY #if defined(__WIN32__) # define DRIVER_INIT(DRIVER_NAME) \ - __declspec(dllexport) ErlDrvEntry* driver_init(void); \ - __declspec(dllexport) ErlDrvEntry* driver_init(void) + __declspec(dllexport) ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void); \ + __declspec(dllexport) ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void) #else # define DRIVER_INIT(DRIVER_NAME) \ - ErlDrvEntry* driver_init(void); \ - ErlDrvEntry* driver_init(void) + ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void); \ + ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void) #endif #define ERL_DRV_BUSY_MSGQ_DISABLED (~((ErlDrvSizeT) 0)) diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 2acc86cf42..ef34f5b221 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -856,6 +856,7 @@ void erts_lcnt_enable_io_lock_count(int enable); typedef void *(*ErtsStaticNifInitFPtr)(void); ErtsStaticNifInitFPtr erts_static_nif_get_nif_init(const char *name); int erts_is_static_nif(void *handle); +void erts_init_static_drivers(void); /* erl_drv_thread.c */ void erl_drv_thr_init(void); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index c1e66b59af..fb39c18d9d 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -2765,6 +2765,7 @@ void erts_init_io(int port_tab_size, init_driver(&fd_driver, &fd_driver_entry, NULL); init_driver(&vanilla_driver, &vanilla_driver_entry, NULL); init_driver(&spawn_driver, &spawn_driver_entry, NULL); + erts_init_static_drivers(); for (dp = driver_tab; *dp != NULL; dp++) erts_add_driver_entry(*dp, NULL, 1); diff --git a/erts/emulator/utils/make_driver_tab b/erts/emulator/utils/make_driver_tab index 06928a3a4a..3eedef21a7 100755 --- a/erts/emulator/utils/make_driver_tab +++ b/erts/emulator/utils/make_driver_tab @@ -28,8 +28,10 @@ use File::Basename; my $file = ""; my $nif = ""; -my @drivers = (); +my @emu_drivers = (); +my @static_drivers = (); my @nifs = (); +my $mode = 1; while (@ARGV) { my $d = shift; @@ -37,15 +39,28 @@ while (@ARGV) { $file = shift or die("-o requires argument"); next; } + if ( $d =~ /^-nifs$/ ) { + $mode = 2; + next; + } + if ( $d =~ /^-drivers$/ ) { + $mode = 1; + next; + } if ( $d =~ /^.*\.a$/ ) { $d = basename $d; $d =~ s/\.a$//; # strip .a - push(@nifs, $d); + if ($mode == 1) { + push(@static_drivers, $d); + } + if ($mode == 2) { + push(@nifs, $d); + } next; } $d = basename $d; $d =~ s/drv(\..*|)$//; # strip drv.* or just drv - push(@drivers, $d); + push(@emu_drivers, $d); } # Did we want output to a file? @@ -64,19 +79,36 @@ print < Date: Thu, 22 Aug 2013 10:35:16 +0200 Subject: Export type zlib:zstream/0 Terms of this type are returned and sometimes sit in states. Exporting it allows us to properly track the types there. --- erts/preloaded/src/zlib.erl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index 1faae1c1f4..54391bd945 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -30,6 +30,8 @@ compress/1,uncompress/1,zip/1,unzip/1, gzip/1,gunzip/1]). +-export_type([zstream/0]). + %% flush argument encoding -define(Z_NO_FLUSH, 0). -define(Z_SYNC_FLUSH, 2). -- cgit v1.2.3 From e897846b560002f58edaae543cf28f36e3edfaf4 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 23 Aug 2013 12:06:27 +0200 Subject: erts: Fix print out of acul option in crash dump --- erts/emulator/beam/erl_alloc_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index bf8a37c71b..e6d9f83aed 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -4369,7 +4369,7 @@ info_options(Allctr_t *allctr, #endif "option lmbcs: %beu\n" "option smbcs: %beu\n" - "option mbcgs: %beu\n", + "option mbcgs: %beu\n" "option acul: %d\n", topt, allctr->ramv ? "true" : "false", -- cgit v1.2.3 From f47f8e3813a6dda5db94c9704c74e8a809dd2313 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 23 Aug 2013 16:21:17 +0200 Subject: erts: Speed up valgrind with asynch threads by only letting it run on one core. Valgrind only let one thread at a time execute anyway. --- erts/etc/unix/cerl.src | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 0d45917e4b..41baa323ed 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -283,6 +283,19 @@ if [ "x$GDB" = "x" ]; then else valgrind_misc_flags="$VALGRIND_MISC_FLAGS" fi + if which taskset > /dev/null && test -e /proc/cpuinfo; then + # We only let valgrind utilize one core with "taskset 1" as it can be very slow + # on multiple cores (especially with async threads). Valgrind only run one pthread + # at a time anyway so there is no point letting it utilize more than one core. + # Use $sched_arg to force all schedulers online to emulate multicore. + taskset1="taskset 1" + ncpu=`cat /proc/cpuinfo | grep -w processor | wc -l` + sched_arg="-S$ncpu:$ncpu" + else + taskset1= + sched_arg= + fi + beam_args=`$EXEC -emu_args_exit ${1+"$@"}` # Time for some argument passing voodoo: @@ -293,7 +306,7 @@ if [ "x$GDB" = "x" ]; then ' set -- $beam_args IFS="$SAVE_IFS" - exec valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $emu_xargs "$@" -pz $PRELOADED + exec $taskset1 valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@" -pz $PRELOADED else exec $EXEC $eeargs $xargs ${1+"$@"} fi -- cgit v1.2.3 From c82784a8fc24ede2760cc96b5f5d6596684ba7e2 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 16 Aug 2013 14:42:53 +0200 Subject: Add debug functionality to retrieve async key --- erts/emulator/drivers/common/efile_drv.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 9e95325c24..779ecd1bd2 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -3133,25 +3133,25 @@ file_flush(ErlDrvData e) { /********************************************************************* * Driver entry point -> control + * Only debug functionality... */ static ErlDrvSSizeT file_control(ErlDrvData e, unsigned int command, char* buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) { - /* - * warning: variable ‘desc’ set but not used - * [-Wunused-but-set-variable] - * ... no kidding ... - * - * file_descriptor *desc = (file_descriptor *)e; switch (command) { + case 'K' : + if (rlen < 4) { + *rbuf = EF_ALLOC(4); + } + (*rbuf)[0] = ((desc->key) >> 24) & 0xFF; + (*rbuf)[1] = ((desc->key) >> 16) & 0xFF; + (*rbuf)[2] = ((desc->key) >> 8) & 0xFF; + (*rbuf)[3] = (desc->key) & 0xFF; + return 4; default: return 0; - } - ASSERT(0); - desc = NULL; - */ - return 0; + } } /********************************************************************* -- cgit v1.2.3 From 7204e78d8d16e41769cfd4b7b4051545052c335e Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Thu, 22 Aug 2013 17:17:03 +0200 Subject: Initialize errno properly in win32 efile_may_openfile --- erts/emulator/drivers/win32/win_efile.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index be3d86a1d2..b36a103f8e 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -772,6 +772,7 @@ efile_may_openfile(Efile_error* errInfo, char *name) { DWORD attr; if ((attr = GetFileAttributesW(wname)) == INVALID_FILE_ATTRIBUTES) { + errno = ENOENT; return check_error(-1, errInfo); } -- cgit v1.2.3 From 6d1f1d43aa0a9e0a2cb275ef760339c49518fc69 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 16 Aug 2013 17:14:25 +0200 Subject: Create better distribution of files over async threads The actual port id is used to create a key from the pointer value which is the ErlDrvPort. To do this a new driver api function driver_async_port_key is added and the driver API minor version is updated. The documentation is updated and the faulty description of how to spread ports over async threads is updated to use the new API. Testcase also added. --- erts/doc/src/erl_driver.xml | 24 +++++++- erts/emulator/beam/erl_async.c | 14 +++++ erts/emulator/beam/erl_driver.h | 4 +- erts/emulator/drivers/common/efile_drv.c | 3 +- erts/emulator/sys/win32/erl_win_dyn_driver.h | 4 ++ erts/emulator/test/efile_SUITE.erl | 88 ++++++++++++++++++++++++++-- 6 files changed, 127 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index f52d973709..540390e1b1 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -1981,7 +1981,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len thread, the following call can be used:

@@ -2021,6 +2021,24 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
+ + unsigned intdriver_async_port_key (ErlDrvPort port) + Calculate an async key from an ErlDrvPort + + +

This function calculates a key for later use in driver_async(). The keys are + evenly distributed so that a fair mapping between port id's + and async thread id's is achieved.

+ +

Before OTP-R16, the actual port id could be used as a key + with proper casting, but after the rewrite of the port + subsystem, this is no longer the case. With this function, you + can achieve the same distribution based on port id's as before + OTP-R16.

+
+
+
intdriver_async_cancel(long id) Cancel an asynchronous call @@ -2033,10 +2051,10 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len The user had to implement synchronization of cancellation anyway. It also unnecessarily complicated the implementation. Therefore, as of OTP-R15B driver_async_cancel() is deprecated, and - scheduled for removal in OTP-R16. It will currently always fail, + scheduled for removal in OTP-R17. It will currently always fail, and return 0.

driver_async_cancel() is deprecated and will - be removed in the OTP-R16 release.

+ be removed in the OTP-R17 release.

diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index 054d1a48f6..e6d72f569b 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -582,6 +582,20 @@ int erts_async_ready_clean(void *varq, void *val) #endif +/* +** Generate a fair async key prom an ErlDrvPort +** The port data gives a fair distribution grom port pointer +** to unsigned integer - to be used in key for driver_async below. +*/ +unsigned int driver_async_port_key(ErlDrvPort port) +{ + ErlDrvTermData td = driver_mk_port(port); + if (td == (ErlDrvTermData) NIL) { + return 0; + } + return (unsigned int) (UWord) internal_port_data(td); +} + /* ** Schedule async_invoke on a worker thread ** NOTE will be syncrounous when threads are unsupported diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index e280563de1..1ab6e17f56 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -133,7 +133,7 @@ typedef struct { #define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed) #define ERL_DRV_EXTENDED_MAJOR_VERSION 2 -#define ERL_DRV_EXTENDED_MINOR_VERSION 1 +#define ERL_DRV_EXTENDED_MINOR_VERSION 2 /* * The emulator will refuse to load a driver with different major @@ -638,6 +638,8 @@ EXTERN int erl_drv_send_term(ErlDrvTermData port, int len); /* Async IO functions */ +EXTERN unsigned int driver_async_port_key(ErlDrvPort port); + EXTERN long driver_async(ErlDrvPort ix, unsigned int* key, void (*async_invoke)(void*), diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 779ecd1bd2..c997fe1bf9 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -772,6 +772,7 @@ file_init(void) return 0; } + /********************************************************************* * Driver entry point -> start */ @@ -788,7 +789,7 @@ file_start(ErlDrvPort port, char* command) } desc->fd = FILE_FD_INVALID; desc->port = port; - desc->key = (unsigned int) (UWord) port; + desc->key = driver_async_port_key(port); desc->flags = 0; desc->invoke = NULL; desc->d = NULL; diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h index a7c53c904d..b9a9838a36 100644 --- a/erts/emulator/sys/win32/erl_win_dyn_driver.h +++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h @@ -80,6 +80,7 @@ WDD_TYPEDEF(int, erl_drv_output_term, (ErlDrvTermData, ErlDrvTermData*, int)); WDD_TYPEDEF(int, driver_output_term, (ErlDrvPort, ErlDrvTermData*, int)); WDD_TYPEDEF(int, erl_drv_send_term, (ErlDrvTermData, ErlDrvTermData, ErlDrvTermData*, int)); WDD_TYPEDEF(int, driver_send_term, (ErlDrvPort, ErlDrvTermData, ErlDrvTermData*, int)); +WDD_TYPEDEF(unsigned int, driver_async_port_key, (ErlDrvPort)); WDD_TYPEDEF(long, driver_async, (ErlDrvPort,unsigned int*,void (*)(void*),void*,void (*)(void*))); WDD_TYPEDEF(int, driver_async_cancel, (unsigned int)); WDD_TYPEDEF(int, driver_lock_driver, (ErlDrvPort)); @@ -197,6 +198,7 @@ typedef struct { WDD_FTYPE(driver_output_term) *driver_output_term; WDD_FTYPE(erl_drv_send_term) *erl_drv_send_term; WDD_FTYPE(driver_send_term) *driver_send_term; + WDD_FTYPE(driver_async_port_key) *driver_async_port_key; WDD_FTYPE(driver_async) *driver_async; WDD_FTYPE(driver_async_cancel) *driver_async_cancel; WDD_FTYPE(driver_lock_driver) *driver_lock_driver; @@ -308,6 +310,7 @@ extern TWinDynDriverCallbacks WinDynDriverCallbacks; #define driver_output_term (WinDynDriverCallbacks.driver_output_term) #define erl_drv_send_term (WinDynDriverCallbacks.erl_drv_send_term) #define driver_send_term (WinDynDriverCallbacks.driver_send_term) +#define driver_async_port_key (WinDynDriverCallbacks.driver_async_port_key) #define driver_async (WinDynDriverCallbacks.driver_async) #define driver_async_cancel (WinDynDriverCallbacks.driver_async_cancel) #define driver_lock_driver (WinDynDriverCallbacks.driver_lock_driver) @@ -443,6 +446,7 @@ do { \ ((W).driver_output_term) = driver_output_term; \ ((W).erl_drv_send_term) = erl_drv_send_term; \ ((W).driver_send_term) = driver_send_term; \ +((W).driver_async_port_key) = driver_async_port_key; \ ((W).driver_async) = driver_async; \ ((W).driver_async_cancel) = driver_async_cancel; \ ((W).driver_lock_driver) = driver_lock_driver; \ diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index 65367eab98..f79bb761d1 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -19,16 +19,16 @@ -module(efile_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). --export([iter_max_files/1]). +-export([iter_max_files/1, async_dist/1]). --export([do_iter_max_files/2]). +-export([do_iter_max_files/2, do_async_dist/1]). -include_lib("test_server/include/test_server.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [iter_max_files]. + [iter_max_files, async_dist]. groups() -> []. @@ -45,6 +45,84 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +do_async_dist(Dir) -> + X = 100, + AT = erlang:system_info(thread_pool_size), + Keys = file_keys(Dir,AT*X,[],[]), + Tab = ets:new(x,[ordered_set]), + [ ets:insert(Tab,{N,0}) || N <- lists:seq(0,AT-1) ], + [ ets:update_counter(Tab,(N rem AT),1) || N <- Keys ], + Res = [ V || {_,V} <- ets:tab2list(Tab) ], + ets:delete(Tab), + {Res, sdev(Res)/X}. + +sdev(List) -> + Len = length(List), + Mean = lists:sum(List)/Len, + math:sqrt(lists:sum([ (X - Mean) * (X - Mean) || X <- List ]) / Len). + +file_keys(_,0,FdList,FnList) -> + [ file:close(FD) || FD <- FdList ], + [ file:delete(FN) || FN <- FnList ], + []; +file_keys(Dir,Num,FdList,FnList) -> + Name = "dummy"++integer_to_list(Num), + FN = filename:join([Dir,Name]), + case file:open(FN,[write,raw]) of + {ok,FD} -> + {file_descriptor,prim_file,{Port,_}} = FD, + <> = + iolist_to_binary(erlang:port_control(Port,$K,[])), + [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])]; + {error,_} -> + % Try freeing up FD's if there are any + case FdList of + [] -> + exit({cannot_open_file,FN}); + _ -> + [ file:close(FD) || FD <- FdList ], + [ file:delete(F) || F <- FnList ], + file_keys(Dir,Num,[],[]) + end + end. + +async_dist(doc) -> + "Check that the distribution of files over async threads is fair"; +async_dist(Config) when is_list(Config) -> + DataDir = ?config(data_dir,Config), + TestFile = filename:join(DataDir, "existing_file"), + Dir = filename:dirname(code:which(?MODULE)), + AsyncSizes = [7,10,100,255,256,64,63,65], + Max = 0.5, + + lists:foreach(fun(Size) -> + {ok,Node} = + test_server:start_node + (test_iter_max_files,slave, + [{args, + "+A "++integer_to_list(Size)++ + " -pa " ++ Dir}]), + {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist, + [DataDir]), + test_server:stop_node(Node), + if + SD > Max -> + io:format("Bad async queue distribution for " + "~p async threads:~n" + " Standard deviation is ~p~n" + " Key distribution:~n ~lp~n", + [Size,SD,Distr]), + exit({bad_async_dist,Size,SD,Distr}); + true -> + io:format("OK async queue distribution for " + "~p async threads:~n" + " Standard deviation is ~p~n" + " Key distribution:~n ~lp~n", + [Size,SD,Distr]), + ok + end + end, AsyncSizes), + ok. %% %% Open as many files as possible. Do this several times and check @@ -98,7 +176,7 @@ open_files(Name) -> ?line case file:open(Name, [read,raw]) of {ok, Fd} -> [Fd| open_files(Name)]; - {error, Reason} -> -% io:format("Error reason: ~p", [Reason]), + {error, _Reason} -> +% io:format("Error reason: ~p", [_Reason]), [] end. -- cgit v1.2.3 From b368fb879676fa6dd2d251ba6583d0a86d04aeb0 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 27 Aug 2013 12:24:30 +0200 Subject: erts: Fix segfault when scheduling release_port later_op begin_port_cleanup can be called from a non-scheduler thread, this means that there is no esdp available and thus erts_schedule_thr_prgr_later_op segfaults. --- erts/emulator/beam/erl_port_task.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 7d53ce7152..547a42beb2 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -1838,6 +1838,16 @@ release_port(void *vport) { erts_port_dec_refc((Port *) vport); } + +static void +schedule_release_port(void *vport) { + Port *pp = (Port*)vport; + /* This is only used when a port release was ordered from a non-scheduler */ + erts_schedule_thr_prgr_later_op(release_port, + (void *) pp, + &pp->common.u.release); +} + #endif static void @@ -2033,10 +2043,15 @@ begin_port_cleanup(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p) * Schedule cleanup of port structure... */ #ifdef ERTS_SMP - /* Has to be more or less immediate to release any driver */ - erts_schedule_thr_prgr_later_op(release_port, - (void *) pp, - &pp->common.u.release); + /* We might not be a scheduler, eg. traceing to port we are sys_msg_dispatcher */ + if (!erts_get_scheduler_data()) { + erts_schedule_misc_aux_work(1, schedule_release_port, (void*)pp); + } else { + /* Has to be more or less immediate to release any driver */ + erts_schedule_thr_prgr_later_op(release_port, + (void *) pp, + &pp->common.u.release); + } #else pp->cleanup = 1; #endif -- cgit v1.2.3 From a6df426d6daff0ab20c0a81ac834befd69322452 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 19 Apr 2013 11:00:32 +0200 Subject: Add time correction doc in users guide --- erts/doc/src/Makefile | 1 + erts/doc/src/part.xml | 1 + erts/doc/src/time_correction.xml | 274 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 erts/doc/src/time_correction.xml (limited to 'erts') diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile index 89d7c85a86..d4c6fe67d2 100644 --- a/erts/doc/src/Makefile +++ b/erts/doc/src/Makefile @@ -78,6 +78,7 @@ XML_CHAPTER_FILES = \ erl_ext_dist.xml \ erl_dist_protocol.xml \ communication.xml \ + time_correction.xml \ notes.xml \ notes_history.xml diff --git a/erts/doc/src/part.xml b/erts/doc/src/part.xml index fb720e05f3..7b17b5b551 100644 --- a/erts/doc/src/part.xml +++ b/erts/doc/src/part.xml @@ -32,6 +32,7 @@

The Erlang Runtime System Application ERTS.

+ diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml new file mode 100644 index 0000000000..d52cc7f3e2 --- /dev/null +++ b/erts/doc/src/time_correction.xml @@ -0,0 +1,274 @@ + + + + +
+ + 19992013 + Ericsson AB. All Rights Reserved. + + + The 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. + + + + Time and time correction in Erlang + Patrik Nyblom + + + + + 2013-08-28 + PA1 + time_correction.xml +
+

Time is vital to an Erlang program and, more importantly, correct + time is vital to an Erlang program. As Erlang is a language with + soft real time properties and we have the possibility to express + time in our programs, the Virtual Machine and the language has to be + very careful about what is considered a correct point in time and in + how time functions behave.

+ +

In the beginning, Erlang was constructed assuming that the wall + clock time in the system showed a monotonic time moving forward at + exactly the same pace as the definition of time. That more or less + meant that an atomic clock (or better) was expected to be attached + to your hardware and that the hardware was then expected to be + locked away from any human (or unearthly) tinkering for all + eternity. While this might be a compelling thought, it's simply + never the case.

+ +

A "normal" modern computer can not keep time. Not on itself and + not unless you actually have a chip level atomic clock wired to + it. Time, as perceived by your computer, will normally need to be + corrected. Hence the NTP protocol that together with the ntpd + process will do it's best to keep your computers time in sync with + the "real" time in the universe. Between NTP corrections, usually a + less potent time-keeper than an atomic clock is used.

+ +

But NTP is not fail safe. The NTP server can be unavailable, the + ntp.conf can be wrongly configured or your computer may from time to + time be disconnected from the internet. Furthermore you can have a + user (or even system administrator) on your system that thinks the + right way to handle daylight saving time is to adjust the clock one + hour two times a year (a tip, that is not the right way to do + it...). To further complicate things, this user fetched your + software from the internet and has never ever thought about what's + the correct time as perceived by a computer. The user simply does + not care about keeping the wall clock in sync with the rest of the + universe. The user expects your program to have omnipotent knowledge + about the time.

+ +

Most programmers also expect time to be reliable, at least until + they realize that the wall clock time on their workstation is of by + a minute. Then they simply set it to the correct time, maybe or + maybe not in a smooth way. Most probably not in a smooth way.

+ +

The amount of problems that arise when you expect the wall clock + time on the system to always be correct may be immense. Therefore Erlang + introduced the "corrected estimate of time", or the "time + correction" many years ago. The time correction relies on the fact + that most operating systems have some kind of monotonic clock, + either a real time extension or some built in "tick counter" that is + independent of the wall clock settings. This counter may have + microsecond resolution or much less, but generally it has a drift + that is not to be ignored.

+ +

So we have this monotonic ticking and we have the wall clock + time. Two unreliable times that together can give us an estimate of + an actual wall clock time that does not jump around and that + monotonically moves forward. If the tick counter has a high + resolution, this is fairly easy to do, if the counter has a low + resolution, it's more expensive, but still doable down to + frequencies of 50-60 Hz (of the tick counter).

+ +

So the corrected time is the nearest approximation of an atomic + clock that is available on the computer. We want it to have the + following properties:

+ + Monotonic + The clock should not move backwards + Intervals should be near the truth + We want the actual time (as measured by an atomic clock or + an astronomer) that passes between two time stamps, T1 and T2, to be as + near to T2 - T1 as possible. + Tight coupling to the wall clock + We want a timer that is to be fired when the wall clock + reaches a time in the future, to fire as near to that point in + time as possible + +

To meet all the criteria, we have to utilize both times in such a + way that Erlangs "corrected time" moves slightly slower or slightly + faster than the wall clock to get in sync with it. The word + "slightly" means a maximum of 1% difference to the wall clock time, + meaning that a sudden change in the wall clock of one minute, takes + 100 minutes to fix, by letting all "corrected time" move 1% slower + or faster.

+ +

Needless to say, correcting for a faulty handling of daylight + saving time may be disturbing to a user comparing wall clock + time to for example calendar:now_to_local_time(erlang:now()). But + calendar:now_to_local_time/1 is not supposed to be used for presenting wall + clock time to the user.

+ +

Time correction is not perfect, but it saves you from the havoc + of clocks jumping around, which would make timers in your program + fire far to late or far to early and could bring your whole system + to it's knees (or worse) just because someone detected a small error + in the wall clock time of the server where your program runs. So + while it might be confusing, it is still a really good feature of + Erlang and you should not throw it away using time functions which + may give you higher benchmark results, not unless you really know + what you're doing.

+ +
+ What does time correction mean in my system? +

Time correction means that Erlang estimates a time from current + and previous settings of the wall clock, and it uses a fairly + exact tick counter to detect when the wall clock time has jumped + for some reason, slowly adjusting to the new value.

+ +

In practice, this means that the difference between two calls + to time corrected functions, like erlang:now(), might differ up to + one percent from the corresponding calls to non time corrected + functions (like os:timestamp()). Furthermore, if comparing + calendar:local_time/0 to calendar:now_to_local_time(erlang:now()), + you might temporarily see a difference, depending on how well kept your + system is.

+ +

It is important to understand that it is (to the program) + always unknown if it is the wall clock time that moves in the + wrong pace or the Erlang corrected time. The only way to determine + that, is to have an external source of universally correct time. If + some such source is available, the wall clock time can be kept + nearly perfect at all times, and no significant difference will be + detected between erlang:now/0's pace and the wall clock's.

+ +

Still, the time correction will mean that your system keeps + it's real time characteristics very well, even when the wall clock + is unreliable.

+
+
+ Where does Erlang use corrected time? +

For all functionality where real time characteristics are + desirable, time correction is used. This basically means:

+ + erlang:now/0 + The infamous erlang:now/0 function uses time correction so + that differences between two "now-timestamps" will correspond to + other timeouts in the system. erlang:now/0 also holds other + properties, discussed later. + receive ... after + Timeouts on receive uses time correction to determine a + stable timeout interval. + The timer module + As the timer module uses other built in functions which + deliver corrected time, the timer module itself works with + corrected time. + erlang:start_timer/3 and erlang:send_after/3 + The timer BIF's work with corrected time, so that they + will not fire prematurely or too late due to changes in the wall + clock time. + + +

All other functionality in the system where erlang:now/0 or any + other time corrected functionality is used, will of course + automatically benefit from it, as long as it's not "optimized" to + use some other time stamp function (like os:timestamp/0).

+ +

Modules like calendar and functions like erlang:localtime/0 use + the wall clock time as it is currently set on the system. They + will not use corrected time. However, if you use a now-value and + convert it to local time, you will get a corrected local time + value, which may or may not be what you want. Typically older code + tend to use erlang:now/0 as a wall clock time, which is usually + correct (at least when testing), but might surprise you when + compared to other times in the system.

+
+
+ What is erlang:now/0 really? +

erlang:now/0 is a function designed to serve multiple purposes + (or a multi-headed beast if you're a VM designer). It is expected + to hold the following properties:

+ + Monotonic + erlang:now() never jumps backwards - it always moves + forward + Interval correct + The interval between two erlang:now() calls is expected to + correspond to the correct time in real life (as defined by an + atomic clock, or better) + Absolute correctness + The erlang:now/0 value should be possible to convert to an + absolute and correct date-time, corresponding to the real world + date and time (the wall clock) + System correspondence + The erlang:now/0 value converted to a date-time is + expected to correspond to times given by other programs on the + system (or by functions like os:timestamp/0) + Unique + No two calls to erlang:now on one Erlang node should + return the same value + +

All these requirements are possible to uphold at the same + time if (and only if):

+ + The wall clock time of the system is perfect + The system (Operating System) time needs to be perfectly + in sync with the actual time as defined by an atomic clock or + a better time source. A good installation using NTP, and that is + up to date before Erlang starts, will have properties that for + most users and programs will be near indistinguishable from the + perfect time. Note that any larger corrections to the time done + by hand, or after Erlang has started, will partly (or + temporarily) invalidate some of the properties, as the time is + no longer perfect. + Less than one call per microsecond to erlang:now/0 is + done + This means that at any microsecond interval in + time, there can be no more than one call to erlang:now/0 in the + system. However, for the system not to loose it's properties + completely, it's enough that it on average is no more than one + call per microsecond (in one Erlang node). + +

The uniqueness property of erlang:now/0 is the most limiting + property. It means that erlang:now() maintains a global state and + that there is a hard-to-check property of the system that needs to + be maintained. For most applications this is still not a problem, + but a future system might very well manage to violate the + frequency limit on the calls globally. The uniqueness property is + also quite useless, as there are globally unique references that + provide a much better unique value to programs. However the + property will need to be maintained unless a really subtle + backward compatibility issue is to be introduced.

+
+
+ Should I use erlang:now/0 or os:timestamp/0 +

The simple answer is to use erlang:now/0 for everything where + you want to keep real time characteristics, but use os:timestamp + for things like logs, user communication and debugging (typically + timer:ts uses os:timestamp, as it is a test tool, not a real world + application API). The benefit of using os:timestamp/0 is that it's + faster and does not involve any global state (unless the operating + system has one). The downside is that it will be vulnerable to wall + clock time changes.

+
+
+ Turning off time correction +

If, for some reason, time correction causes trouble and you are + absolutely confident that the wall clock on the system is nearly + perfect, you can turn off time correction completely by giving the + +c option to erl. The probability for this being a + good idea, is very low.

+
+
+ -- cgit v1.2.3 From 43b1f1755b1ca9003d88655bfebe5ca9e46cfbf6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 28 Aug 2013 20:55:16 +0200 Subject: erts: Remove unused constant DRIVER_TAB_SIZE --- erts/emulator/beam/global.h | 5 ----- erts/emulator/utils/make_driver_tab | 6 +++--- 2 files changed, 3 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index ef34f5b221..e3a0487aeb 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -186,11 +186,6 @@ extern void erts_ddll_remove_monitor(Process *p, extern Eterm erts_ddll_monitor_driver(Process *p, Eterm description, ErtsProcLocks plocks); -/* - * Max no. of drivers (linked in and dynamically loaded). Each table - * entry uses 4 bytes. - */ -#define DRIVER_TAB_SIZE 32 /* ** Just like the driver binary but with initial flags diff --git a/erts/emulator/utils/make_driver_tab b/erts/emulator/utils/make_driver_tab index 3eedef21a7..0e6793d2af 100755 --- a/erts/emulator/utils/make_driver_tab +++ b/erts/emulator/utils/make_driver_tab @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# Copyright Ericsson AB 1999-2013. All Rights Reserved. # # The 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,7 @@ foreach (@static_drivers) { } # The array itself -print "\nErlDrvEntry *driver_tab[DRIVER_TAB_SIZE] =\n{\n"; +print "\nErlDrvEntry *driver_tab[] =\n{\n"; foreach (@emu_drivers) { print " &${_}driver_entry,\n"; @@ -126,7 +126,7 @@ foreach (@nifs) { } # The array itself -print "static ErtsStaticNifEntry static_nif_tab[DRIVER_TAB_SIZE] =\n{\n"; +print "static ErtsStaticNifEntry static_nif_tab[] =\n{\n"; foreach (@nifs) { my $d = ${_}; -- cgit v1.2.3 From e0f448d5dcbfed90023203ef9cd96768460eec60 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Thu, 29 Aug 2013 10:15:16 +0200 Subject: Updated preloaded --- erts/preloaded/ebin/zlib.beam | Bin 12784 -> 12812 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 35e4d963fd..75af019783 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 69e01c0f5d332d34b95575203ae9d7829b9a45fa Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Tue, 20 Aug 2013 21:09:47 -0400 Subject: add erl option to set schedulers by percentages For applications where measurements show enhanced performance from the use of a non-default number of emulator scheduler threads, having to accurately set the right number of scheduler threads across multiple hosts each with different numbers of logical processors is difficult because the erl +S option requires absolute numbers of scheduler threads and scheduler threads online to be specified. To address this issue, add a +SP option to erl, similar to the existing +S option but allowing the number of scheduler threads and scheduler threads online to be set as percentages of logical processors configured and logical processors available, respectively. For example, "+SP 50:25" sets the number of scheduler threads to 50% of the logical processors configured, and the number of scheduler threads online to 25% of the logical processors available. The +SP option also interacts with any settings specified with the +S option, such that the combination of options "+S 4:4 +SP 50:50" (in either order) results in 2 scheduler threads and 2 scheduler threads online. Add documentation for the +SP option. Add tests for the +SP option to scheduler_SUITE. Add tests and documentation for two existing features of the +S option: +S 0:0 resets the scheduler thread count and scheduler threads online count to their defaults, and specifying negative numbers for +S results in those values being subtracted from the default values for the host. --- erts/doc/src/erl.xml | 50 +++++++--- erts/emulator/beam/erl_init.c | 169 +++++++++++++++++++++++---------- erts/emulator/test/scheduler_SUITE.erl | 64 ++++++++++++- erts/etc/common/erlexec.c | 18 +++- 4 files changed, 238 insertions(+), 63 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 70569b1c6c..c16b45856d 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -748,19 +748,47 @@ -

Sets the amount of scheduler threads to create and scheduler - threads to set online when SMP support has been enabled. - Valid range for both values are 1-1024. If the - Erlang runtime system is able to determine the amount - of logical processors configured and logical processors available, - Schedulers will default to logical processors configured, - and SchedulersOnline will default to logical processors - available; otherwise, the default values will be 1. Schedulers - may be omitted if :SchedulerOnline is not and vice versa. The - amount of schedulers online can be changed at run time via +

Sets the number of scheduler threads to create and scheduler + threads to set online when SMP support has been enabled. The maximum for + both values is 1024. If the Erlang runtime system is able to determine the + amount of logical processors configured and logical processors available, + Schedulers will default to logical processors configured, and + SchedulersOnline will default to logical processors available; + otherwise, the default values will be 1. Schedulers may be omitted + if :SchedulerOnline is not and vice versa. The number of schedulers + online can be changed at run time via erlang:system_flag(schedulers_online, SchedulersOnline).

-

This flag will be ignored if the emulator doesn't have +

If Schedulers or SchedulersOnline is specified as a + negative number, the value is subtracted from the default number of + logical processors configured or logical processors available, respectively. +

+

Specifying the value 0 for Schedulers or SchedulersOnline + resets the number of scheduler threads or scheduler threads online respectively + to its default value. +

+

This option is ignored if the emulator doesn't have + SMP support enabled (see the -smp + flag).

+
+ + +

Similar to +S but uses percentages to set the + number of scheduler threads to create, based on logical processors configured, + and scheduler threads to set online, based on logical processors available, when + SMP support has been enabled. Specified values must be greater than 0. For example, + +SP 50:25 sets the number of scheduler threads to 50% of the logical processors + configured and the number of scheduler threads online to 25% of the logical processors available. + SchedulersPercentage may be omitted if :SchedulersOnlinePercentage is + not and vice versa. The number of schedulers online can be changed at run time via + erlang:system_flag(schedulers_online, SchedulersOnline). +

+

This option interacts with +S settings. + For example, on a system with 8 logical cores configured and 8 logical cores + available, the combination of the options +S 4:4 +SP 50:25 (in either order) + results in 2 scheduler threads (50% of 4) and 1 scheduler thread online (25% of 4). +

+

This option is ignored if the emulator doesn't have SMP support enabled (see the -smp flag).

diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 8d137df7ae..f7bfa91b54 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -549,9 +549,12 @@ void erts_usage(void) ERTS_SCHED_THREAD_MAX_STACK_SIZE); erts_fprintf(stderr, "-spp Bool set port parallelism scheduling hint\n"); erts_fprintf(stderr, "-S n1:n2 set number of schedulers (n1), and number of\n"); - erts_fprintf(stderr, " schedulers online (n2), valid range for both\n"); - erts_fprintf(stderr, " numbers are [1-%d]\n", + erts_fprintf(stderr, " schedulers online (n2), maximum for both\n"); + erts_fprintf(stderr, " numbers is %d\n", ERTS_MAX_NO_OF_SCHEDULERS); + erts_fprintf(stderr, "-SP p1:p2 specify schedulers (p1) and schedulers online (p2)\n"); + erts_fprintf(stderr, " as percentages of logical processors configured and logical\n"); + erts_fprintf(stderr, " processors available, respectively\n"); erts_fprintf(stderr, "-t size set the maximum number of atoms the " "emulator can handle\n"); erts_fprintf(stderr, " valid range is [%d-%d]\n", @@ -631,6 +634,8 @@ early_init(int *argc, char **argv) /* int ncpuavail; int schdlrs; int schdlrs_onln; + int schdlrs_percentage = 100; + int schdlrs_onln_percentage = 100; int max_main_threads; int max_reader_groups; int reader_groups; @@ -758,63 +763,128 @@ early_init(int *argc, char **argv) /* } break; } - case 'S' : { - int tot, onln; - char *arg = get_arg(argv[i]+2, argv[i+1], &i); - switch (sscanf(arg, "%d:%d", &tot, &onln)) { - case 0: - switch (sscanf(arg, ":%d", &onln)) { + case 'S' : + if (argv[i][2] == 'P') { + int ptot, ponln; + char *arg = get_arg(argv[i]+3, argv[i+1], &i); + switch (sscanf(arg, "%d:%d", &ptot, &ponln)) { + case 0: + switch (sscanf(arg, ":%d", &ponln)) { + case 1: + if (ponln < 0) + goto bad_SP; + ptot = 100; + goto chk_SP; + default: + goto bad_SP; + } case 1: - tot = no_schedulers; - goto chk_S; + if (ptot < 0) + goto bad_SP; + ponln = ptot < 100 ? ptot : 100; + goto chk_SP; + case 2: + if (ptot < 0 || ponln < 0) + goto bad_SP; + chk_SP: + schdlrs_percentage = ptot; + schdlrs_onln_percentage = ponln; + break; default: - goto bad_S; - } - case 1: - onln = tot < schdlrs_onln ? tot : schdlrs_onln; - case 2: - chk_S: - if (tot > 0) - schdlrs = tot; - else - schdlrs = no_schedulers + tot; - if (onln > 0) - schdlrs_onln = onln; - else - schdlrs_onln = no_schedulers_online + onln; - if (schdlrs < 1 || ERTS_MAX_NO_OF_SCHEDULERS < schdlrs) { - erts_fprintf(stderr, - "bad amount of schedulers %d\n", - tot); - erts_usage(); - } - if (schdlrs_onln < 1 || schdlrs < schdlrs_onln) { + bad_SP: + erts_fprintf(stderr, + "bad schedulers percentage specifier %s\n", + arg); + erts_usage(); + break; + } + + VERBOSE(DEBUG_SYSTEM, + ("using %d:%d scheduler percentages\n", + schdlrs_percentage, schdlrs_onln_percentage)); + } else { + int tot, onln; + char *arg = get_arg(argv[i]+2, argv[i+1], &i); + switch (sscanf(arg, "%d:%d", &tot, &onln)) { + case 0: + switch (sscanf(arg, ":%d", &onln)) { + case 1: + tot = no_schedulers; + goto chk_S; + default: + goto bad_S; + } + case 1: + onln = tot < schdlrs_onln ? tot : schdlrs_onln; + case 2: + chk_S: + if (tot > 0) + schdlrs = tot; + else + schdlrs = no_schedulers + tot; + if (onln > 0) + schdlrs_onln = onln; + else + schdlrs_onln = no_schedulers_online + onln; + if (schdlrs < 1 || ERTS_MAX_NO_OF_SCHEDULERS < schdlrs) { + erts_fprintf(stderr, + "bad amount of schedulers %d\n", + tot); + erts_usage(); + } + if (schdlrs_onln < 1 || schdlrs < schdlrs_onln) { + erts_fprintf(stderr, + "bad amount of schedulers online %d " + "(total amount of schedulers %d)\n", + schdlrs_onln, schdlrs); + erts_usage(); + } + break; + default: + bad_S: erts_fprintf(stderr, - "bad amount of schedulers online %d " - "(total amount of schedulers %d)\n", - schdlrs_onln, schdlrs); + "bad amount of schedulers %s\n", + arg); erts_usage(); + break; } - break; - default: - bad_S: - erts_fprintf(stderr, - "bad amount of schedulers %s\n", - arg); - erts_usage(); - break; - } - VERBOSE(DEBUG_SYSTEM, - ("using %d:%d scheduler(s)\n", tot, onln)); - break; - } + VERBOSE(DEBUG_SYSTEM, + ("using %d:%d scheduler(s)\n", tot, onln)); + } + break; default: break; } } i++; } + +#ifdef ERTS_SMP + /* apply any scheduler percentages */ + if (schdlrs_percentage != 100 || schdlrs_onln_percentage != 100) { + schdlrs = schdlrs * schdlrs_percentage / 100; + schdlrs_onln = schdlrs_onln * schdlrs_onln_percentage / 100; + if (schdlrs < 1) + schdlrs = 1; + if (ERTS_MAX_NO_OF_SCHEDULERS < schdlrs) { + erts_fprintf(stderr, + "bad schedulers percentage %d " + "(total amount of schedulers %d)\n", + schdlrs_percentage, schdlrs); + erts_usage(); + } + if (schdlrs_onln < 1) + schdlrs_onln = 1; + if (schdlrs < schdlrs_onln) { + erts_fprintf(stderr, + "bad schedulers online percentage %d " + "(total amount of schedulers %d, online %d)\n", + schdlrs_onln_percentage, schdlrs, schdlrs_onln); + erts_usage(); + } + } +#endif } #ifndef USE_THREADS @@ -1312,7 +1382,10 @@ erl_start(int argc, char **argv) break; case 'S' : /* Was handled in early_init() just read past it */ - (void) get_arg(argv[i]+2, argv[i+1], &i); + if (argv[i][2] == 'P') + (void) get_arg(argv[i]+3, argv[i+1], &i); + else + (void) get_arg(argv[i]+2, argv[i+1], &i); break; case 's' : { diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 8931562828..81539faa09 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -52,6 +52,7 @@ update_cpu_info/1, sct_cmd/1, sbt_cmd/1, + scheduler_threads/1, scheduler_suspend/1, reader_groups/1]). @@ -66,7 +67,7 @@ all() -> equal_with_part_time_max, equal_and_high_with_part_time_max, equal_with_high, equal_with_high_max, bound_process, - {group, scheduler_bind}, scheduler_suspend, + {group, scheduler_bind}, scheduler_threads, scheduler_suspend, reader_groups]. groups() -> @@ -1039,7 +1040,66 @@ sbt_test(Config, CpuTCmd, ClBt, Bt, LP) -> tuple_to_list(SB)), ?line stop_node(Node), ?line ok. - + +scheduler_threads(Config) when is_list(Config) -> + SmpSupport = erlang:system_info(smp_support), + {Sched, SchedOnln, _} = get_sstate(Config, ""), + %% Configure half the number of both the scheduler threads and + %% the scheduler threads online. + {HalfSched, HalfSchedOnln} = case SmpSupport of + false -> {1,1}; + true -> + {Sched div 2, + SchedOnln div 2} + end, + {HalfSched, HalfSchedOnln, _} = get_sstate(Config, "+SP 50:50"), + %% Use +S to configure 4x the number of scheduler threads and + %% 4x the number of scheduler threads online, but alter that + %% setting using +SP to 50% scheduler threads and 25% scheduler + %% threads online. The result should be 2x scheduler threads and + %% 1x scheduler threads online. + TwiceSched = case SmpSupport of + false -> 1; + true -> Sched*2 + end, + FourSched = integer_to_list(Sched*4), + FourSchedOnln = integer_to_list(SchedOnln*4), + CombinedCmd1 = "+S "++FourSched++":"++FourSchedOnln++" +SP50:25", + {TwiceSched, SchedOnln, _} = get_sstate(Config, CombinedCmd1), + %% Now do the same test but with the +S and +SP options in the + %% opposite order, since order shouldn't matter. + CombinedCmd2 = "+SP50:25 +S "++FourSched++":"++FourSchedOnln, + {TwiceSched, SchedOnln, _} = get_sstate(Config, CombinedCmd2), + %% Apply two +SP options to make sure the second overrides the first + TwoCmd = "+SP 25:25 +SP 100:100", + {Sched, SchedOnln, _} = get_sstate(Config, TwoCmd), + %% Configure 50% of scheduler threads online only + {Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"), + %% Configure 2x scheduler threads only + {TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"), + %% Test resetting the scheduler counts + ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0", + {Sched, SchedOnln, _} = get_sstate(Config, ResetCmd), + %% Test negative +S settings, but only for SMP-enabled emulators + case SmpSupport of + false -> ok; + true -> + SchedMinus1 = Sched-1, + SchedOnlnMinus1 = SchedOnln-1, + {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"), + {Sched, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"), + {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1") + end, + ok. + +get_sstate(Config, Cmd) -> + {ok, Node} = start_node(Config, Cmd), + [SState] = mcall(Node, [fun () -> + erlang:system_info(schedulers_state) + end]), + stop_node(Node), + SState. + scheduler_suspend(Config) when is_list(Config) -> ?line Dog = ?t:timetrap(?t:minutes(5)), ?line lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end, diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index e61ebe15f5..552afe295d 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -803,7 +803,6 @@ int main(int argc, char **argv) case 'n': case 'P': case 'Q': - case 'S': case 't': case 'T': case 'R': @@ -818,6 +817,19 @@ int main(int argc, char **argv) add_Eargs(argv[i+1]); i++; break; + case 'S': + if (argv[i][2] == 'P') { + if (argv[i][3] != '\0') + goto the_default; + } else if (argv[i][2] != '\0') + goto the_default; + if (i+1 >= argc) + usage(argv[i]); + argv[i][0] = '-'; + add_Eargs(argv[i]); + add_Eargs(argv[i+1]); + i++; + break; case 'B': argv[i][0] = '-'; if (argv[i][2] != '\0') { @@ -1119,7 +1131,9 @@ usage_aux(void) "[+l] [+M ] [+P MAX_PROCS] [+Q MAX_PORTS] " "[+R COMPAT_REL] " "[+r] [+rg READER_GROUPS_LIMIT] [+s SCHEDULER_OPTION] " - "[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] [+T LEVEL] [+V] [+v] " + "[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] " + "[+SP PERCENTAGE_SCHEDULERS:PERCENTAGE_SCHEDULERS_ONLINE] " + "[+T LEVEL] [+V] [+v] " "[+W] [+z MISC_OPTION] [args ...]\n"); exit(1); } -- cgit v1.2.3 From c4da6eed78d30670d3602f746030e03c46137ead Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Mon, 2 Sep 2013 15:10:18 +0200 Subject: Fix EV_* macros and functions signedness flaws --- erts/emulator/drivers/common/efile_drv.c | 119 ++++++++++++++++--------------- 1 file changed, 60 insertions(+), 59 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index c997fe1bf9..8de578d8b7 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -542,84 +542,84 @@ static void *ef_safe_realloc(void *op, Uint s) */ /* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */ -#define EV_CHAR_P(ev, p, q) \ - (((char *)(ev)->iov[(q)].iov_base) + (p)) +#define EV_CHAR_P(ev, p, q) \ + (((char *)(ev)->iov[q].iov_base) + (p)) /* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */ #define EV_GET_CHAR(ev, p, pp, qp) efile_ev_get_char(ev, p ,pp, qp) static int -efile_ev_get_char(ErlIOVec *ev, char *p, int *pp, int *qp) { - if (*(pp)+1 <= (ev)->iov[*(qp)].iov_len) { - *(p) = *EV_CHAR_P(ev, *(pp), *(qp)); - if (*(pp)+1 < (ev)->iov[*(qp)].iov_len) - *(pp) = *(pp)+1; - else { - (*(qp))++; - *pp = 0; +efile_ev_get_char(ErlIOVec *ev, char *p, size_t *pp, size_t *qp) { + if (*pp + 1 <= ev->iov[*qp].iov_len) { + *p = *EV_CHAR_P(ev, *pp, *qp); + if (*pp + 1 < ev->iov[*qp].iov_len) + *pp += 1; + else { + *qp += 1; + *pp = 0; + } + return !0; } - return !0; - } - return 0; + return 0; } /* Uint32 EV_UINT32(ErlIOVec *ev, int p, int q)*/ -#define EV_UINT32(ev, p, q) \ - ((Uint32) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p))) +#define EV_UINT32(ev, p, q) \ + ((Uint32) ((unsigned char *)(ev)->iov[q].iov_base)[p]) /* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */ -#define EV_GET_UINT32(ev, p, pp, qp) efile_ev_get_uint32(ev,p,pp,qp) +#define EV_GET_UINT32(ev, p, pp, qp) efile_ev_get_uint32(ev, p, pp, qp) static int -efile_ev_get_uint32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) { - if (*(pp)+4 <= (ev)->iov[*(qp)].iov_len) { - *(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24) - | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16) - | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8) - | (EV_UINT32(ev, *(pp)+3, *(qp))); - if (*(pp)+4 < (ev)->iov[*(qp)].iov_len) - *(pp) = *(pp)+4; - else { - (*(qp))++; - *pp = 0; +efile_ev_get_uint32(ErlIOVec *ev, Uint32 *p, size_t *pp, size_t *qp) { + if (*pp + 4 <= ev->iov[*qp].iov_len) { + *p = (EV_UINT32(ev, *pp, *qp) << 24) + | (EV_UINT32(ev, *pp + 1, *qp) << 16) + | (EV_UINT32(ev, *pp + 2, *qp) << 8) + | (EV_UINT32(ev, *pp + 3, *qp)); + if (*pp + 4 < ev->iov[*qp].iov_len) + *pp += 4; + else { + *qp += 1; + *pp = 0; + } + return !0; } - return !0; - } - return 0; + return 0; } /* Uint64 EV_UINT64(ErlIOVec *ev, int p, int q)*/ -#define EV_UINT64(ev, p, q) \ - ((Uint64) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p))) +#define EV_UINT64(ev, p, q) \ + ((Uint64) ((unsigned char *)(ev)->iov[q].iov_base)[p]) /* int EV_GET_UINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */ -#define EV_GET_UINT64(ev, p, pp, qp) efile_ev_get_uint64(ev,p,pp,qp) +#define EV_GET_UINT64(ev, p, pp, qp) efile_ev_get_uint64(ev, p, pp, qp) static int -efile_ev_get_uint64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) { - if (*(pp)+8 <= (ev)->iov[*(qp)].iov_len) { - *(p) = (EV_UINT64(ev, *(pp), *(qp)) << 56) - | (EV_UINT64(ev, *(pp)+1, *(qp)) << 48) - | (EV_UINT64(ev, *(pp)+2, *(qp)) << 40) - | (EV_UINT64(ev, *(pp)+3, *(qp)) << 32) - | (EV_UINT64(ev, *(pp)+4, *(qp)) << 24) - | (EV_UINT64(ev, *(pp)+5, *(qp)) << 16) - | (EV_UINT64(ev, *(pp)+6, *(qp)) << 8) - | (EV_UINT64(ev, *(pp)+7, *(qp))); - if (*(pp)+8 < (ev)->iov[*(qp)].iov_len) - *(pp) = *(pp)+8; - else { - (*(qp))++; - *pp = 0; +efile_ev_get_uint64(ErlIOVec *ev, Uint64 *p, size_t *pp, size_t *qp) { + if (*pp + 8 <= ev->iov[*qp].iov_len) { + *p = (EV_UINT64(ev, *pp, *qp) << 56) + | (EV_UINT64(ev, *pp + 1, *qp) << 48) + | (EV_UINT64(ev, *pp + 2, *qp) << 40) + | (EV_UINT64(ev, *pp + 3, *qp) << 32) + | (EV_UINT64(ev, *pp + 4, *qp) << 24) + | (EV_UINT64(ev, *pp + 5, *qp) << 16) + | (EV_UINT64(ev, *pp + 6, *qp) << 8) + | (EV_UINT64(ev, *pp + 7, *qp)); + if (*pp + 8 < ev->iov[*qp].iov_len) + *pp += 8; + else { + *qp += 1; + *pp = 0; + } + return !0; } - return !0; - } - return 0; + return 0; } /* int EV_GET_SINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */ -#define EV_GET_SINT64(ev, p, pp, qp) efile_ev_get_sint64(ev,p,pp,qp) +#define EV_GET_SINT64(ev, p, pp, qp) efile_ev_get_sint64(ev, p, pp, qp) static int -efile_ev_get_sint64(ErlIOVec *ev, Sint64 *p, int *pp, int *qp) { - Uint64 *tmp = (Uint64*)p; - return EV_GET_UINT64(ev,tmp,pp,qp); +efile_ev_get_sint64(ErlIOVec *ev, Sint64 *p, size_t *pp, size_t *qp) { + Uint64 *tmp = (Uint64*)p; + return EV_GET_UINT64(ev, tmp, pp, qp); } #if 0 @@ -1139,7 +1139,7 @@ static void invoke_read(void *data) read_size = erts_gzread((gzFile)d->fd, d->c.read.binp->orig_bytes + d->c.read.bin_offset, size); - status = (read_size != -1); + status = (read_size != (size_t) -1); if (!status) { d->errInfo.posix_errno = EIO; } @@ -1213,7 +1213,7 @@ static void invoke_read_line(void *data) d->c.read_line.binp->orig_bytes + d->c.read_line.read_offset + d->c.read_line.read_size, size); - status = (read_size != -1); + status = (read_size != (size_t) -1); if (!status) { d->errInfo.posix_errno = EIO; } @@ -1707,8 +1707,9 @@ static void invoke_pwritev(void *data) { ASSERT(written == size); d->again = 0; } - } else + } else { ASSERT(written >= FILE_SEGMENT_WRITE); + } MUTEX_LOCK(d->c.writev.q_mtx); driver_deq(d->c.pwritev.port, written); @@ -3205,7 +3206,7 @@ static void file_outputv(ErlDrvData e, ErlIOVec *ev) { file_descriptor* desc = (file_descriptor*)e; char command; - int p, q; + size_t p, q; int err; struct t_data *d = NULL; #ifdef USE_VM_PROBES -- cgit v1.2.3 From 06ab9f03fef5883a39591edf9406274a63606724 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 2 Sep 2013 16:27:26 +0200 Subject: Silence gcc warnings in non-smp build --- erts/emulator/beam/erl_init.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index f7bfa91b54..8c4fffa75b 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -884,6 +884,10 @@ early_init(int *argc, char **argv) /* erts_usage(); } } +#else + /* Silence gcc warnings */ + (void)schdlrs_percentage; + (void)schdlrs_onln_percentage; #endif } -- cgit v1.2.3 From a209817dd8467a04be869541e7c31216dc4b0a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 3 Sep 2013 17:11:24 +0200 Subject: erts: Document erl_driver interface lock names --- erts/doc/src/erl_driver.xml | 78 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index efe0483b31..5cf0d2f47f 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -2889,8 +2889,84 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len beginning of this document.

- + + char *erl_drv_cond_name(ErlDrvCond *cnd) + Get name of driver mutex. + + +

Arguments:

+ + cnd + A pointer to an initialized condition. + +

+ Returns a pointer to the name of the condition. +

+ +

This function is intended for debugging purposes only.

+
+
+
+ + + char *erl_drv_mutex_name(ErlDrvMutex *mtx) + Get name of driver mutex. + + +

Arguments:

+ + mtx + A pointer to an initialized mutex. + +

+ Returns a pointer to the name of the mutex. +

+ +

This function is intended for debugging purposes only.

+
+
+
+ + + char *erl_drv_rwlock_name(ErlDrvRWLock *rwlck) + Get name of driver mutex. + + +

Arguments:

+ + rwlck + A pointer to an initialized r/w-lock. + +

+ Returns a pointer to the name of the r/w-lock. +

+ +

This function is intended for debugging purposes only.

+
+
+
+ + + char *erl_drv_thread_name(ErlDrvTid tid) + Get name of driver mutex. + + +

Arguments:

+ + tid + A thread identifier. + +

+ Returns a pointer to the name of the thread. +

+ +

This function is intended for debugging purposes only.

+
+
+
+ +
SEE ALSO

driver_entry(3), -- cgit v1.2.3 From f2dc85b778e7d3b0a3fbd0c523f1c4d7bcb2a94f Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 4 Sep 2013 16:44:54 +0200 Subject: +e should be passed through erlexec --- erts/etc/common/erlexec.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 552afe295d..30560f5a2f 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -799,6 +799,7 @@ int main(int argc, char **argv) case 'a': case 'A': case 'b': + case 'e': case 'i': case 'n': case 'P': -- cgit v1.2.3 From b00a53c0d1dc0dea0231f7aae31ccc6f96577e77 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 4 Sep 2013 18:12:02 +0200 Subject: erts: Fix faulty assert in "unlimited select" for mac --- erts/emulator/sys/common/erl_poll.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 5861b30315..7676d8872a 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -123,8 +123,8 @@ static ERTS_INLINE int ERTS_SELECT(int nfds, ERTS_fd_set *readfds, ERTS_fd_set *writefds, ERTS_fd_set *exceptfds, struct timeval *timeout) { - ASSERT(!readfds || readfds->sz >= nfds); - ASSERT(!writefds || writefds->sz >= nfds); + ASSERT(!readfds || readfds->sz >= ERTS_FD_SIZE(nfds)); + ASSERT(!writefds || writefds->sz >= ERTS_FD_SIZE(nfds)); ASSERT(!exceptfds); return select(nfds, (readfds ? readfds->ptr : NULL ), -- cgit v1.2.3 From d7ce2d9b68ba83e46100a97aaf56c0ca09899525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 6 Sep 2013 10:03:56 +0200 Subject: Convert some notes.xml files from latin-1 to utf-8 In the master branch, the encoding for most xml files have been changed from latin-1 to utf-8. The problem is, that the corresponding files in the maint branch still are encoded in latin-1, and that a merge from maint to master may bring in characters encoded in latin-1 into a notes.xml file declared to be in utf-8. To fix the problem once and for all (for the files involved), we'll need to re-encode the files files utf-8 in maint, and then merge to master. Noticed-by: Magnus Henoch --- erts/doc/src/notes.xml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index f94d71ee3d..822aceff08 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -1,4 +1,4 @@ - + @@ -321,7 +321,7 @@

Support wide characters in the shell through wcwidth(). - Thanks to Anthony Ramine. Reported by Loïc Hoguin.

+ Thanks to Anthony Ramine. Reported by Loïc Hoguin.

Own Id: OTP-11088

@@ -342,7 +342,7 @@

Remove 'query' from the list of reserved words in docs. - Thanks to Matthias Endler and Loïc Hoguin.

+ Thanks to Matthias Endler and Loïc Hoguin.

Own Id: OTP-11158

@@ -1961,7 +1961,7 @@

Fix typo in supervisor behaviour doc (Thanks to Ricardo - Catalinas Jiménez)

+ Catalinas Jiménez)

Own Id: OTP-9924

@@ -2225,7 +2225,7 @@

Fixes module erlang doc style: option description (Thanks - to Ricardo Catalinas Jiménez)

+ to Ricardo Catalinas Jiménez)

Own Id: OTP-9697

@@ -2674,7 +2674,7 @@

Fix typos in the epmd documentation (Thanks to Holger - Weiß )

+ Weiß )

Own Id: OTP-9387

@@ -2779,7 +2779,7 @@

Fix non-existing function (erlang:disconnect/1) in - distributed reference manual (Thanks to Fabian Król)

+ distributed reference manual (Thanks to Fabian Król)

Own Id: OTP-9504

@@ -2807,7 +2807,7 @@ only separator characters (comma and space).

The same applies to epmd's -address option.(Thanks to - Holger Weiß)

+ Holger Weiß)

Own Id: OTP-9525

@@ -2951,7 +2951,7 @@

Add support for querying the number of configured and online processors on SGI systems running IRIX.(Thanks to - Holger Weiß)

+ Holger Weiß)

Own Id: OTP-9531

@@ -3061,7 +3061,7 @@ using a comma-separated list. If the loopback address is not in this list, it will be added implicitly, so that the daemon can be queried by an interactive epmd - process.(Thanks to Holger Weiß)

+ process.(Thanks to Holger Weiß)

Own Id: OTP-9213

@@ -3096,7 +3096,7 @@ value over to dbg_gen_printf(). This fixes the problem that errno had been reset to zero by the time it was used (to print the corresponding error message) in the - dbg_gen_printf() function. (Thanks to Holger Weiß)

+ dbg_gen_printf() function. (Thanks to Holger Weiß)

Own Id: OTP-9223

@@ -3482,7 +3482,7 @@ Mention that "-detached" implies "-noinput"

Clarify that specifying "-noinput" is unnecessary if the - "-detached" flag is given. (thanks to Holger Weiß)

+ "-detached" flag is given. (thanks to Holger Weiß)

Own Id: OTP-9086

@@ -4988,7 +4988,7 @@ failed to detect gcc C compilers with other command line names than gcc. `configure' now substitute GCC into the makefiles. If CC is a gcc C compiler, GCC will have the - value yes. (Thanks to Jean-Sébastien Pédron)

+ value yes. (Thanks to Jean-Sébastien Pédron)

Own Id: OTP-8373

@@ -7358,7 +7358,7 @@

IPv6 name resolving has now been fixed to use getaddrinfo() patch (thoroughly reworked) courtesy of Love - Hörnquist-Åstrand submitted by Fredrik Thulin. It also + Hörnquist-Ã…strand submitted by Fredrik Thulin. It also can use gethostname2() patch (also reworked) courtesy of Mikael Magnusson for debian submitted by Sergei Golovan.

-- cgit v1.2.3 From 49059dbd211987987a7e9d9ef40cc0b2484f829a Mon Sep 17 00:00:00 2001 From: Juan Jose Comellas Date: Mon, 9 Sep 2013 14:19:41 -0300 Subject: Fix incorrect values returned by integer_to_binary/2 When integer_to_binary/2 receives 0 or a negative number as an argument with a base that is different from 10, it will return incorrect values (<<>> in the case of 0) or it will crash (with negative numbers). This commit fixes these problems and adds tests to cover these cases. --- erts/emulator/test/num_bif_SUITE.erl | 22 ++++++++++++++++++++++ erts/preloaded/src/erlang.erl | 15 ++++++++------- 2 files changed, 30 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index b92a0e2059..ff8d18eef8 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -350,12 +350,34 @@ t_integer_to_string(Config) when is_list(Config) -> (catch erlang:integer_to_list(Value)) end,[atom,1.2,0.0,[$1,[$2]]]), + %% Base-2 integers + test_its("0", 0, 2), + test_its("1", 1, 2), + test_its("110110", 54, 2), + test_its("-1000000", -64, 2), + %% Base-16 integers + test_its("0", 0, 16), + test_its("A", 10, 16), + test_its("D4BE", 54462, 16), + test_its("-D4BE", -54462, 16), + + lists:foreach(fun(Value) -> + {'EXIT', {badarg, _}} = + (catch erlang:integer_to_binary(Value, 8)), + {'EXIT', {badarg, _}} = + (catch erlang:integer_to_list(Value, 8)) + end,[atom,1.2,0.0,[$1,[$2]]]), + ok. test_its(List,Int) -> Int = list_to_integer(List), Int = binary_to_integer(list_to_binary(List)). +test_its(List,Int,Base) -> + Int = list_to_integer(List, Base), + Int = binary_to_integer(list_to_binary(List), Base). + %% Tests binary_to_integer/1. t_string_to_integer(Config) when is_list(Config) -> diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index e016a50c4c..a969ef91dc 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2891,22 +2891,23 @@ integer_to_binary(I, Base) when erlang:is_integer(I), erlang:is_integer(Base), Base >= 2, Base =< 1+$Z-$A+10 -> if I < 0 -> - <<"$-",(integer_to_binary(-I, Base, []))/binary>>; + <<$-,(integer_to_binary(-I, Base, <<>>))/binary>>; true -> integer_to_binary(I, Base, <<>>) end; integer_to_binary(I, Base) -> erlang:error(badarg, [I, Base]). -integer_to_binary(0, _Base, R0) -> - R0; integer_to_binary(I0, Base, R0) -> D = I0 rem Base, I1 = I0 div Base, - if D >= 10 -> - integer_to_binary(I1,Base,<<(D-10+$A),R0/binary>>); - true -> - integer_to_binary(I1,Base,<<(D+$0),R0/binary>>) + R1 = if + D >= 10 -> <<(D-10+$A),R0/binary>>; + true -> <<(D+$0),R0/binary>> + end, + if + I1 =:= 0 -> R1; + true -> integer_to_binary(I1, Base, R1) end. %% erlang:flush_monitor_message/2 is for internal use only! -- cgit v1.2.3 From b6af3b245383767a6ab92a38b7a27732cce3d2bf Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Tue, 10 Sep 2013 08:50:39 +0200 Subject: Update preloaded --- erts/preloaded/ebin/erlang.beam | Bin 94136 -> 94176 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 09eafbcc29..83cafe197e 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ -- cgit v1.2.3 From 3e71527e639101c9482b98ebde1c3d98a0419ec5 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Tue, 10 Sep 2013 09:06:13 -0400 Subject: fix system_flag(scheduling_statistics,disable) Clear the "enabled" flag for scheduling statistics when disable is specified. --- erts/emulator/beam/erl_process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 2439a46ab6..434d5ca147 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -7237,7 +7237,7 @@ erts_sched_stat_modify(int what) break; case ERTS_SCHED_STAT_MODIFY_DISABLE: erts_smp_thr_progress_block(); - erts_sched_stat.enabled = 1; + erts_sched_stat.enabled = 0; erts_smp_thr_progress_unblock(); break; case ERTS_SCHED_STAT_MODIFY_CLEAR: -- cgit v1.2.3 From 814d38ee1a111caf81e066d290fec455d13dcc9b Mon Sep 17 00:00:00 2001 From: Matt Lewandowsky Date: Wed, 11 Sep 2013 05:12:32 -0700 Subject: Fix syslog defines config.h defines HAVE_SYSLOG_H whereas the sources are looking for NO_SYSLOG to be undefined. As the logic of "if feature is available" makes more sense than "if feature is not unavailable", I opted for the config.h define. --- erts/emulator/sys/win32/erl_win_sys.h | 1 - erts/epmd/src/epmd.c | 6 +++--- erts/epmd/src/epmd_int.h | 4 +--- erts/etc/unix/run_erl.c | 8 ++++---- 4 files changed, 8 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 5ce1a61303..66ba6c002b 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -82,7 +82,6 @@ #define NO_ERF #define NO_ERFC -#define NO_SYSLOG #define NO_SYSCONF #define NO_DAEMON #define NO_PWD diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 94bb74c876..2d55b37ff3 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -286,7 +286,7 @@ static void run_daemon(EpmdVars *g) /* fork to make sure first child is not a process group leader */ if (( child_pid = fork()) < 0) { -#ifndef NO_SYSLOG +#ifdef HAVE_SYSLOG_H syslog(LOG_ERR,"erlang mapper daemon cant fork %m"); #endif epmd_cleanup_exit(g,1); @@ -312,7 +312,7 @@ static void run_daemon(EpmdVars *g) if ((child_pid = fork()) < 0) { -#ifndef NO_SYSLOG +#ifdef HAVE_SYSLOG_H syslog(LOG_ERR,"erlang mapper daemon cant fork 2'nd time %m"); #endif epmd_cleanup_exit(g,1); @@ -483,7 +483,7 @@ static void dbg_gen_printf(int onsyslog,int perr,int from_level, if (g->is_daemon) { -#ifndef NO_SYSLOG +#ifdef HAVE_SYSLOG_H if (onsyslog) { erts_vsnprintf(buf, DEBUG_BUFFER_SIZE, format, args); diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index ac354dcc78..656dbd1f45 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -25,13 +25,11 @@ definitions ourselves */ #ifdef __WIN32__ -#define NO_SYSLOG #define NO_SYSCONF #define NO_DAEMON #endif #ifdef VXWORKS -#define NO_SYSLOG #define NO_SYSCONF #define NO_DAEMON #define NO_FCNTL @@ -98,7 +96,7 @@ #include -#ifndef NO_SYSLOG +#ifdef HAVE_SYSLOG_H # include #endif diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index b69e31f784..c9b2321ab0 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -60,7 +60,7 @@ #include #include #include -#ifndef NO_SYSLOG +#ifdef HAVE_SYSLOG_H # include #endif #ifdef HAVE_PTY_H @@ -197,7 +197,7 @@ static char* outbuf_in; #endif -#ifdef NO_SYSLOG +#ifndef HAVE_SYSLOG_H # define OPEN_SYSLOG() ((void) 0) #else # define OPEN_SYSLOG() openlog(simple_basename(program_name), \ @@ -415,7 +415,7 @@ int main(int argc, char **argv) } #endif -#ifndef NO_SYSLOG +#ifdef HAVE_SYSLOG_H /* Before fiddling with file descriptors we make sure syslog is turned off or "closed". In the single case where we might want it again, we will open it again instead. Would not want syslog to @@ -1163,7 +1163,7 @@ static void error_logf(int priority, int line, const char *format, ...) va_list args; va_start(args, format); -#ifndef NO_SYSLOG +#ifdef HAVE_SYSLOG_H if (run_daemon) { vsyslog(priority,format,args); } -- cgit v1.2.3 From a65f66a4ab0e1b6409a5de5fcb0c14248c292949 Mon Sep 17 00:00:00 2001 From: Matt Lewandowsky Date: Wed, 11 Sep 2013 06:59:00 -0700 Subject: Define LOG_ERR for systems without syslog.h --- erts/etc/unix/run_erl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index c9b2321ab0..2018bc007c 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -199,6 +199,7 @@ static char* outbuf_in; #ifndef HAVE_SYSLOG_H # define OPEN_SYSLOG() ((void) 0) +# define LOG_ERR NULL #else # define OPEN_SYSLOG() openlog(simple_basename(program_name), \ LOG_PID|LOG_CONS|LOG_NOWAIT,LOG_USER) -- cgit v1.2.3 From 21095e6830f37676dd29c33a590851ba2c76499b Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Tue, 10 Sep 2013 15:48:29 +0100 Subject: Remove ^L characters hidden randomly in the code. Not those used in text files as delimiters. While working on a tool that processes Erlang code and testing it against this repo, I found out about those little sneaky 0xff. I thought it may be of help to other people build such tools to remove non-conforming-to-standard characters. --- erts/emulator/beam/beam_emu.c | 3 --- erts/emulator/beam/beam_load.c | 16 ---------------- erts/emulator/drivers/unix/ttsl_drv.c | 8 ++++---- erts/emulator/drivers/win32/win_efile.c | 2 +- erts/emulator/test/call_trace_SUITE.erl | 2 +- erts/emulator/test/driver_SUITE.erl | 6 +++--- erts/emulator/test/statistics_SUITE.erl | 12 ++++++------ erts/emulator/test/trace_SUITE.erl | 2 +- erts/emulator/test/trace_port_SUITE.erl | 2 +- 9 files changed, 17 insertions(+), 36 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index da36c4437e..78ab6fa30f 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5654,7 +5654,6 @@ build_stacktrace(Process* c_p, Eterm exc) { return res; } - static BeamInstr* call_error_handler(Process* p, BeamInstr* fi, Eterm* reg, Eterm func) { @@ -5702,7 +5701,6 @@ call_error_handler(Process* p, BeamInstr* fi, Eterm* reg, Eterm func) return ep->addressv[erts_active_code_ix()]; } - static Export* apply_setup_error_handler(Process* p, Eterm module, Eterm function, Uint arity, Eterm* reg) { @@ -6208,7 +6206,6 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) return make_fun(funp); } - int catchlevel(Process *p) { diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 4193eb4f3f..938fd8f2c9 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -535,7 +535,6 @@ static int must_swap_floats; Uint erts_total_code_size; /**********************************************************************/ - void init_load(void) { FloatDef f; @@ -1209,7 +1208,6 @@ verify_chunks(LoaderState* stp) return 0; } - static int load_atom_table(LoaderState* stp) { @@ -1255,7 +1253,6 @@ load_atom_table(LoaderState* stp) return 0; } - static int load_import_table(LoaderState* stp) { @@ -1308,7 +1305,6 @@ load_import_table(LoaderState* stp) return 0; } - static int read_export_table(LoaderState* stp) { @@ -1641,7 +1637,6 @@ read_line_table(LoaderState* stp) return 0; } - static int read_code_header(LoaderState* stp) { @@ -1711,7 +1706,6 @@ read_code_header(LoaderState* stp) return 0; } - #define VerifyTag(Stp, Actual, Expected) \ if (Actual != Expected) { \ LoadError2(Stp, "bad tag %d; expected %d", Actual, Expected); \ @@ -1730,7 +1724,6 @@ read_code_header(LoaderState* stp) #define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm)))) - static int load_code(LoaderState* stp) { @@ -2512,7 +2505,6 @@ load_code(LoaderState* stp) return retval; } - #define succ(St, X, Y) ((X).type == (Y).type && (X).val + 1 == (Y).val) #define succ2(St, X, Y) ((X).type == (Y).type && (X).val + 2 == (Y).val) #define succ3(St, X, Y) ((X).type == (Y).type && (X).val + 3 == (Y).val) @@ -3958,7 +3950,6 @@ tuple_append_put(LoaderState* stp, GenOpArg Arity, GenOpArg Dst, } - /* * Freeze the code in memory, move the string table into place, * resolve all labels. @@ -4276,7 +4267,6 @@ freeze_code(LoaderState* stp) return 0; } - static void final_touch(LoaderState* stp) { @@ -4378,7 +4368,6 @@ final_touch(LoaderState* stp) } } - static int transform_engine(LoaderState* st) { @@ -4716,7 +4705,6 @@ transform_engine(LoaderState* st) return rval; } - static void short_file(int line, LoaderState* stp, unsigned needed) { @@ -4724,7 +4712,6 @@ short_file(int line, LoaderState* stp, unsigned needed) stp->file_name, needed); } - static void load_printf(int line, LoaderState* context, char *fmt,...) { @@ -5190,7 +5177,6 @@ native_addresses(Process* p, Eterm mod) return result; } - /* * Builds a list of all exported functions in the given module: * [{Name, Arity},...] @@ -5240,7 +5226,6 @@ exported_from_module(Process* p, /* Process whose heap to use. */ return result; } - /* * Returns a list of all attributes for the module. * @@ -5281,7 +5266,6 @@ attributes_for_module(Process* p, /* Process whose heap to use. */ return result; } - /* * Returns a list containing compilation information. * diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index 1e436830e7..491e0a090e 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -745,7 +745,7 @@ static Sint16 get_sint16(char *s) { return ((*s << 8) | ((byte*)s)[1]); } - + static int start_lbuf(void) { if (!lbuf && !(lbuf = ( Uint32*) driver_alloc(lbuf_size * sizeof(Uint32)))) @@ -1091,7 +1091,7 @@ static int move_cursor(int from, int to) move_left(-dc); return TRUE; } - + static int start_termcap(void) { int eres; @@ -1187,7 +1187,7 @@ static int move_down(int n) tputs(down, 1, outc); return TRUE; } - + /* * Updates cols if terminal has resized (SIGWINCH). Should be called @@ -1209,7 +1209,7 @@ static void update_cols(void) cols = width; } } - + /* * Put a terminal device into non-canonical mode with ECHO off. diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index b36a103f8e..319065f57b 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -1216,7 +1216,7 @@ int flags; return 1; } - + /* * is_root_unc_name - returns TRUE if the argument is a UNC name specifying * a root share. That is, if it is of the form \\server\share\. diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index eaecd32f95..ef1f2aa04c 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -1193,7 +1193,7 @@ bs_sum_b(Acc, <<>>) -> Acc. - + %%% Help functions. expect() -> diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 104bdf8aec..7087542899 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -367,7 +367,7 @@ compare(Got, Expected) -> ?t:fail(got_bad_data) end. - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Driver timer test suites %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -515,7 +515,7 @@ try_change_timer(Port, Timeout) -> ?line test_server:fail("driver failed to timeout") end. - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Queue test suites %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -719,7 +719,7 @@ deq(Port, Size) -> read_head(Port, Size) -> erlang:port_control(Port, ?READ_HEAD, <>). - + driver_unloaded(doc) -> []; driver_unloaded(suite) -> diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index a93dd309c1..c428be6c5a 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -75,7 +75,7 @@ end_per_group(_GroupName, Config) -> Config. - + %%% Testing statistics(wall_clock). @@ -121,7 +121,7 @@ wall_clock_update1(N) when N > 0 -> wall_clock_update1(0) -> ok. - + %%% Test statistics(runtime). @@ -199,7 +199,7 @@ do_much(N) -> _ = 4784728478274827 * 72874284728472, do_much(N-1). - + reductions(doc) -> "Test that statistics(reductions) is callable, and that " "Total_Reductions and Reductions_Since_Last_Call make sense. " @@ -246,7 +246,7 @@ reductions_big_loop() -> reductions_big_loop() end. - + %%% Tests of statistics(run_queue). @@ -295,7 +295,7 @@ hog_iter(N, Mon) when N > 0 -> end; hog_iter(0, Mon) -> ?line hog_iter(10000, Mon). - + %%% Tests of statistics(scheduler_wall_time). scheduler_wall_time(doc) -> @@ -363,7 +363,7 @@ load_percentage([{Id, WN, TN}|Ss], [{Id, WP, TP}|Ps]) -> [100*(WN-WP) div (TN-TP)|load_percentage(Ss, Ps)]; load_percentage([], []) -> []. - + garbage_collection(doc) -> "Tests that statistics(garbage_collection) is callable. " "It is not clear how to test anything more."; diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 0f513f0dcb..2251575e5a 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -1427,7 +1427,7 @@ receive_nothing() -> ok end. - + %%% Models for various kinds of processes. process(Dest) -> diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index cc2eadafbc..99df8da107 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -648,7 +648,7 @@ fun_spawn(Fun, Opts) -> % [] % end. - + %%% Models for various kinds of processes. %% Sends messages when ordered to. -- cgit v1.2.3 From 43ac962df419c2e027f3fe8c226e693eb8da7805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Lid=C3=A9n?= Date: Fri, 13 Sep 2013 15:32:18 +0200 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 54532 -> 54536 bytes erts/preloaded/ebin/erlang.beam | Bin 94176 -> 94152 bytes erts/preloaded/ebin/erts_internal.beam | Bin 3256 -> 3260 bytes erts/preloaded/ebin/init.beam | Bin 48636 -> 48640 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1448 -> 1452 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1324 -> 1328 bytes erts/preloaded/ebin/prim_file.beam | Bin 44248 -> 44252 bytes erts/preloaded/ebin/prim_inet.beam | Bin 70960 -> 70856 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23408 -> 23416 bytes erts/preloaded/ebin/zlib.beam | Bin 12812 -> 12788 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index a8c9961d87..f51ccfe8ce 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 83cafe197e..9da4f3cd00 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 78a45c4325..3ab52615ab 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index f95a09c003..89d3cd6b83 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 25c620bdd7..733d3fb587 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 12e6471159..80bd1b3331 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 0b9562b8c6..a73a2f0db1 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 5b38871282..b0bef84e68 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 58da9ce5ea..562003df0d 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 75af019783..e4a22ff2c7 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 439d7e1b81b77e3930d81a6ea9bbadb3cff30fa4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 13 Sep 2013 18:48:18 +0200 Subject: erts: Fix loading of NIF library with unicode in path --- erts/emulator/beam/erl_nif.c | 20 +++++++------------- erts/emulator/beam/global.h | 3 +-- erts/emulator/beam/utils.c | 22 ---------------------- erts/emulator/utils/make_driver_tab | 18 +++++++++--------- 4 files changed, 17 insertions(+), 46 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 673012a9af..ee480fb661 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1568,9 +1568,10 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) void* init_func = NULL; ErlNifEntry* entry = NULL; ErlNifEnv env; - int len, i, err; + int i, err; Module* mod; Eterm mod_atom; + const Atom* mod_atomp; Eterm f_atom; BeamInstr* caller; ErtsSysDdllError errdesc = ERTS_SYS_DDLL_ERROR_INIT; @@ -1578,21 +1579,13 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) int veto; struct erl_module_nif* lib = NULL; int reload_warning = 0; - char tmp_buf[255]; - len = list_length(BIF_ARG_1); - if (len < 0) { + lib_name = erts_convert_filename_to_native(BIF_ARG_1, NULL, 0, + ERTS_ALC_T_TMP, 1, 0, NULL); + if (!lib_name) { 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) { - erts_free(ERTS_ALC_T_TMP, lib_name); - BIF_ERROR(BIF_P, BADARG); - } - 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], @@ -1615,7 +1608,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) mod=erts_get_module(mod_atom, erts_active_code_ix()); ASSERT(mod != NULL); - init_func = erts_static_nif_get_nif_init(erts_basename(lib_name,tmp_buf)); + mod_atomp = atom_tab(atom_val(mod_atom)); + init_func = erts_static_nif_get_nif_init(mod_atomp->name, mod_atomp->len); if (init_func != NULL) handle = init_func; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index e3a0487aeb..11e464b639 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -849,7 +849,7 @@ void erts_lcnt_enable_io_lock_count(int enable); /* driver_tab.c */ typedef void *(*ErtsStaticNifInitFPtr)(void); -ErtsStaticNifInitFPtr erts_static_nif_get_nif_init(const char *name); +ErtsStaticNifInitFPtr erts_static_nif_get_nif_init(const char *name, int len); int erts_is_static_nif(void *handle); void erts_init_static_drivers(void); @@ -858,7 +858,6 @@ void erl_drv_thr_init(void); /* utils.c */ void erts_cleanup_offheap(ErlOffHeap *offheap); -const char *erts_basename(const char* path, char* buff); Uint64 erts_timestamp_millis(void); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 6293286c75..43720084d1 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -4019,28 +4019,6 @@ erts_smp_ensure_later_interval_acqb(erts_interval_t *icp, Uint64 ic) #endif } -const char *erts_basename(const char* path, char* buff) { - /* This function is not compliant with bash basename. Edge cases like "//" - and "/path//" do not work properly. - */ - int i; - int len = strlen(path); - const char *basename = path; - for (i = 0; path[i] != '\0'; i++) { - if (path[i] == '/') { - if (path[i+1] == '\0') { - memcpy(buff,basename,len - (basename-path)); - buff[len - (basename-path)-1] = '\0'; - basename = buff; - break; - } else { basename = path+i;} - } - } - if (basename == path) - return path; - return basename+1; -} - /* * A millisecond timestamp without time correction where there's no hrtime * - for tracing on "long" things... diff --git a/erts/emulator/utils/make_driver_tab b/erts/emulator/utils/make_driver_tab index 0e6793d2af..5c68143d58 100755 --- a/erts/emulator/utils/make_driver_tab +++ b/erts/emulator/utils/make_driver_tab @@ -137,19 +137,19 @@ foreach (@nifs) { print " {NULL,NULL}\n};\n"; print <nif_name != NULL; p++) + if (strncmp(p->nif_name, name, len) == 0 && p->nif_name[len] == 0) + return p->nif_init; return NULL; } int erts_is_static_nif(void *handle) { - int i; - for (i = 0; static_nif_tab[i].nif_name != NULL; i++) - if (((void*)static_nif_tab[i].nif_init) == handle) - return 1; + ErtsStaticNifEntry* p; + for (p = static_nif_tab; p->nif_name != NULL; p++) + if (((void*)p->nif_init) == handle) + return 1; return 0; } -- cgit v1.2.3 From 51a15e3a712c2a28234be3a267d80af9e839c31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Sep 2013 13:39:05 +0200 Subject: erts: Remove unit in 'Time left' printout The unit 'ms' is removed due to ease of parsing. --- erts/emulator/beam/erl_bif_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c index d67695e533..03ac97283c 100644 --- a/erts/emulator/beam/erl_bif_timer.c +++ b/erts/emulator/beam/erl_bif_timer.c @@ -616,7 +616,7 @@ erts_print_bif_timer_info(int to, void *to_arg) : btm->receiver.proc.ess->common.id); erts_print(to, to_arg, "=timer:%T\n", receiver); erts_print(to, to_arg, "Message: %T\n", btm->message); - erts_print(to, to_arg, "Time left: %u ms\n", + erts_print(to, to_arg, "Time left: %u\n", erts_time_left(&btm->tm)); } } -- cgit v1.2.3 From 683281e2362ab950810141647fc7ce3654ffc4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Sep 2013 13:42:03 +0200 Subject: erts: Remove space in 'Buckets' printout Fix to ease parsing of information. --- erts/emulator/beam/erl_db_hash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 2fea4671e1..06dac8f161 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -2106,7 +2106,7 @@ static void db_print_hash(int to, void *to_arg, int show, DbTable *tbl) DbTableHash *tb = &tbl->hash; int i; - erts_print(to, to_arg, "Buckets: %d \n", NACTIVE(tb)); + erts_print(to, to_arg, "Buckets: %d\n", NACTIVE(tb)); if (show) { for (i = 0; i < NACTIVE(tb); i++) { -- cgit v1.2.3 From 20641fe0f2ea745873fc7557448d3a7deb1bd639 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 16 Sep 2013 20:11:53 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 226 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 822aceff08..77ffeefd04 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,232 @@

This document describes the changes made to the ERTS application.

+
Erts 5.10.3 + +
Fixed Bugs and Malfunctions + + +

The documentation of predefined types and built-in + types has been corrected.

+

+ Own Id: OTP-11090

+
+ +

+ Fix changing terminal parameters in to_erl

+

+ Change the behaviour of to_erl to use TCSADRAIN instead + of TCSANOW when changing terminal parameters. This makes + the serial driver wait for the output queues to be empty + before applying the terminal parameter change. Thanks to + Stefan Zegenhagen.

+

+ Own Id: OTP-11206

+
+ +

+ The default value of {flush, boolean()} in erlang:halt/2 + is documented to be 'true' if the status is an integer. + The implementation behaviour was reversed. The + Implementation is now corrected to adhere to the + documentation. Thanks to Jose Valim for reporting the + error.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11218

+
+ +

+ Fix serious race bug in R16B01 that could cause PID + mix-ups when a lot of processes were spawned and + terminated in a very rapid pace on an SMP emulator with + at least two scheduler threads.

+

+ Own Id: OTP-11225

+
+ +

+ Validating a trace pattern with the option silent no + longer incorrectly enables/disables the silent option of + the calling process.

+

+ Own Id: OTP-11232

+
+ +

+ Fixed a bug where GCC 4.8 and later use a more aggressive + loop optimization algorithm that broke some previously + working code in the efile driver. Thanks to Tomas + Abrahamsson for reporting this issue.

+

+ Own Id: OTP-11246

+
+ +

+ Fixed bug when printing memory allocator acul option in + crash dump.

+

+ Own Id: OTP-11264

+
+ +

+ Opening a new compressed file on Windows could in rare + (random) cases result in {error,eisdir} or other error + codes although it should have succeeded. This is now + corrected.

+

+ Own Id: OTP-11265

+
+ +

+ Fixed a race condition when closing a trace port that + would cause the emulator to crash.

+

+ Own Id: OTP-11290

+
+
+
+ + +
Improvements and New Features + + +

+ There is a new somewhat experimental socket option + 'netns' that can set the network namespace for a socket + on Linux:es where it is supported. See the documentation.

+

+ Own Id: OTP-11157

+
+ +

+ New allocator strategy aoffcbf (address order + first fit carrier best fit). Supports carrier migration + but with better CPU performance than aoffcaobf.

+

+ Own Id: OTP-11174

+
+ +

+ Introduced functionality for inspection of system and + build configuration.

+

+ Own Id: OTP-11196

+
+ +

+ Fix matching of floating point middle-endian machines. + Thanks to Johannes Weissl.

+

+ Own Id: OTP-11201

+
+ +

+ Fix compile error on ARM and GCC versions greater than + 4.1.0. Thanks to Johannes Weissl.

+

+ Own Id: OTP-11214

+
+ +

+ run_erl: Redirect standard streams to /dev/null. Thanks + to Johannes Weissl.

+

+ Own Id: OTP-11215

+
+ +

+ Misc. corrections in documentation for erl_driver. Thanks + to Giacomo Olgeni.

+

+ Own Id: OTP-11227

+
+ +

+ Fix documentation regarding binary_part.

+

+ Own Id: OTP-11239

+
+ +

+ Make edlin understand a few important control keys. + Thanks to Stefan Zegenhagen.

+

+ Own Id: OTP-11251

+
+ +

+ Export type zlib:zstream/0. Thanks to Loic Hoguin.

+

+ Own Id: OTP-11278

+
+ +

+ Add erl option to set schedulers by percentages.

+

+ For applications where measurements show enhanced + performance from the use of a non-default number of + emulator scheduler threads, having to accurately set the + right number of scheduler threads across multiple hosts + each with different numbers of logical processors is + difficult because the erl +S option requires absolute + numbers of scheduler threads and scheduler threads online + to be specified.

+

+ To address this issue, add a +SP option to erl, similar + to the existing +S option but allowing the number of + scheduler threads and scheduler threads online to be set + as percentages of logical processors configured and + logical processors available, respectively. For example, + "+SP 50:25" sets the number of scheduler threads to 50% + of the logical processors configured, and the number of + scheduler threads online to 25% of the logical processors + available. The +SP option also interacts with any + settings specified with the +S option, such that the + combination of options "+S 4:4 +SP 50:50" (in either + order) results in 2 scheduler threads and 2 scheduler + threads online.

+

+ Thanks to Steve Vinoski

+

+ Own Id: OTP-11282

+
+ +

+ Extend erl_driver interface with lock names

+

+ Lock and thread names are already a feature in the driver + interface. This extension will let developers read these + names which eases debugging.

+

+ Own Id: OTP-11303

+
+ +

+ Fix incorrect values returned by integer_to_binary/2. + Thanks to Juan Jose Comellas.

+

+ Own Id: OTP-11311

+
+ +

+ Fix system_flag scheduling_statistics - disable . Thanks + to Steve Vinoski.

+

+ Own Id: OTP-11317

+
+ +

The documentation of predefined types has been + corrected Thanks to Kostis Sagonas.

+

+ Own Id: OTP-11321

+
+
+
+ +
+
Erts 5.10.2
Fixed Bugs and Malfunctions -- cgit v1.2.3 From de853c60333c6e95379b69ee81bd5fff1dca95de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 16 Aug 2013 14:04:45 +0200 Subject: Teach erlc to handle UTF-8 file names The 'erlc' program passes options to the 'erl' program using the '-s' option. The '-s' option causes all options to be converted to atoms, which implies that UTF-8 file names may not be given on the command line. We could solve just the UTF-8 problem by using '-run' and change the erl_compile module to expect strings instead of atoms, but since that is an incompatible change, we should take the opportunity to make more incompatible changes while we are at it. Specifically, when 'erlc' was first written, there was no way to pass command line arguments starting with '-' to Erlang, so 'erlc' did all parsing of arguments and translated options to atoms starting with a '@' character (for example, -I was translated to @i). Since then, the '-extra' option has been introduced which allows us to pass anything to Erlang at the end of the command line. Therefore, while at it, do the minimum of necessary command line parsing in the 'erlc' program (e.g. the '-smp' option), passing the command line essentially unchanged to 'erl' using the '-extra' option, and rewrite the option parsing in Erlang. --- erts/etc/common/erlc.c | 267 ++++--------------------------------------------- 1 file changed, 18 insertions(+), 249 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c index add65b87ca..055064abc4 100644 --- a/erts/etc/common/erlc.c +++ b/erts/etc/common/erlc.c @@ -60,7 +60,6 @@ static int eargc; /* Number of arguments in eargv. */ #define PUSH2(s, t) PUSH(s); PUSH(t) #define PUSH3(s, t, u) PUSH2(s, t); PUSH(u) -static char* output_type = NULL; /* Type of output file. */ #ifdef __WIN32__ static int pause_after_execution = 0; #endif @@ -71,7 +70,6 @@ static int pause_after_execution = 0; static char* process_opt(int* pArgc, char*** pArgv, int offset); static void error(char* format, ...); -static void usage(void); static char* emalloc(size_t size); static char* strsave(char* string); static void push_words(char* src); @@ -218,6 +216,7 @@ int main(int argc, char** argv) PUSH2("-mode", "minimal"); PUSH2("-boot", "start_clean"); PUSH3("-s", "erl_compile", "compile_cmdline"); + PUSH("-extra"); /* * Push standard arguments to Erlang. @@ -227,154 +226,31 @@ int main(int argc, char** argv) * Parse all command line switches. */ - while (argc > 1 && (argv[1][0] == '-' || argv[1][0] == '+')) { + while (argc > 1) { /* * Options starting with '+' are passed on to Erlang. */ - if (argv[1][0] == '+') { - PUSH2("@option", argv[1]+1); - } else { - /* - * Interpret options starting with '-'. - */ - + switch (argv[1][0]) { + case '+': + PUSH(argv[1]); + break; + case '-': switch (argv[1][1]) { - case 'b': - output_type = process_opt(&argc, &argv, 0); - PUSH2("@output_type", output_type); - break; - case 'c': /* Allowed for compatibility with 'erl'. */ - if (strcmp(argv[1], "-compile") != 0) - goto error; - break; case 'd': - debug = 1; - break; - case 'D': - { - char* def = process_opt(&argc, &argv, 0); - char* equals; - - def = strsave(def); /* Do not clobber original. */ - if ((equals = strchr(def, '=')) == NULL) { - PUSH2("@d", def); - } else { - *equals = '\0'; - equals++; - PUSH3("@dv", def, equals); - } - } - break; - case 'I': - PUSH2("@i", process_opt(&argc, &argv, 0)); - break; - case 'M': - { - char *buf, *key, *val; - size_t buf_len; - - if (argv[1][2] == '\0') { /* -M */ - /* Push the following options: - * o 'makedep' - * o {makedep_output, standard_io} - */ - buf = strsave("makedep"); - PUSH2("@option", buf); - - key = "makedep_output"; - val = "standard_io"; - buf_len = 1 + strlen(key) + 1 + strlen(val) + 1 + 1; - buf = emalloc(buf_len); - snprintf(buf, buf_len, "{%s,%s}", key, val); - PUSH2("@option", buf); - } else if (argv[1][3] == '\0') { - switch(argv[1][2]) { - case 'D': /* -MD */ - /* Push the following options: - * o 'makedep' - */ - buf = strsave("makedep"); - PUSH2("@option", buf); - break; - case 'F': /* -MF */ - /* Push the following options: - * o 'makedep' - * o {makedep_output, } - */ - buf = strsave("makedep"); - PUSH2("@option", buf); - - key = "makedep_output"; - val = process_opt(&argc, &argv, 1); - buf_len = 1 + strlen(key) + 2 + strlen(val) + 2 + 1; - buf = emalloc(buf_len); - snprintf(buf, buf_len, "{%s,\"%s\"}", key, val); - PUSH2("@option", buf); - break; - case 'T': /* -MT */ - /* Push the following options: - * o {makedep_target, } - */ - key = "makedep_target"; - val = process_opt(&argc, &argv, 1); - buf_len = 1 + strlen(key) + 2 + strlen(val) + 2 + 1; - buf = emalloc(buf_len); - snprintf(buf, buf_len, "{%s,\"%s\"}", key, val); - PUSH2("@option", buf); - break; - case 'Q': /* -MQ */ - /* Push the following options: - * o {makedep_target, } - * o makedep_quote_target - */ - key = "makedep_target"; - val = process_opt(&argc, &argv, 1); - buf_len = 1 + strlen(key) + 2 + strlen(val) + 2 + 1; - buf = emalloc(buf_len); - snprintf(buf, buf_len, "{%s,\"%s\"}", key, val); - PUSH2("@option", buf); - - buf = strsave("makedep_quote_target"); - PUSH2("@option", buf); - break; - case 'G': /* -MG */ - /* Push the following options: - * o makedep_add_missing - */ - buf = strsave("makedep_add_missing"); - PUSH2("@option", buf); - break; - case 'P': /* -MP */ - /* Push the following options: - * o makedep_phony - */ - buf = strsave("makedep_phony"); - PUSH2("@option", buf); - break; - default: - goto error; - } - } + if (argv[1][2] == '\0') { + debug = 1; + } else { + PUSH(argv[1]); } break; - case 'o': - PUSH2("@outdir", process_opt(&argc, &argv, 0)); - break; - case 'O': - PUSH("@optimize"); - if (argv[1][2] == '\0') - PUSH("1"); - else - PUSH(argv[1]+2); - break; case 'p': { int c = argv[1][2]; if (c != 'a' && c != 'z') { - goto error; + PUSH(argv[1]); #ifdef __WIN32__ } else if (strcmp(argv[1], "-pause") == 0) { pause_after_execution = 1; @@ -395,81 +271,21 @@ int main(int argc, char** argv) if (strcmp(argv[1], "-smp") == 0) { UNSHIFT(argv[1]); } else { - goto error; - } - break; - case 'v': /* Verbose. */ - PUSH2("@verbose", "true"); - break; - case 'V': - /** XXX Version perhaps, but of what? **/ - break; - case 'W': /* Enable warnings. */ - if (strcmp(argv[1]+2, "all") == 0) { - PUSH2("@warn", "999"); - } else if (strcmp(argv[1]+2, "error") == 0) { - PUSH2("@option", "warnings_as_errors"); - } else if (isdigit((int)argv[1][2])) { - PUSH2("@warn", argv[1]+2); - } else { - PUSH2("@warn", "1"); - } - break; - case 'E': - case 'S': - case 'P': - { - char* buf; - - /* - * From the given upper-case letter, construct - * a quoted atom. This is a convenience for the - * Erlang compiler, to avoid fighting with the shell's - * quoting. - */ - - buf = emalloc(4); - buf[0] = '\''; - buf[1] = argv[1][1]; - buf[2] = '\''; - buf[3] = '\0'; - - PUSH2("@option", buf); + PUSH(argv[1]); } break; - - case '-': - goto no_more_options; - default: - error: - usage(); + PUSH(argv[1]); break; } + break; + default: + PUSH(argv[1]); + break; } argc--, argv++; } - no_more_options: - - if (argc <= 1) { - /* - * To avoid starting an Erlang system unless absolutely needed - * exit if no files were specified on the command line. - */ - exit(0); - } - - /* - * The rest of the command line must be filenames. Simply push them. - */ - - PUSH("@files"); - while (argc > 1) { - PUSH(argv[1]); - argc--, argv++; - } - /* * Move up the commands for invoking the emulator and adjust eargv * accordingly. @@ -648,53 +464,6 @@ run_erlang(char* progname, char** argv) #endif } -static void -usage(void) -{ - static struct { - char* name; - char* desc; - } options[] = { - {"-b type", "type of output file (e.g. jam or beam)"}, - {"-d", "turn on debugging of erlc itself"}, - {"-Dname", "define name"}, - {"-Dname=value", "define name to have value"}, - {"-help", "shows this help text"}, - {"-I path", "where to search for include files"}, - {"-M", "generate a rule for make(1) describing the dependencies"}, - {"-MF file", "write the dependencies to 'file'"}, - {"-MT target", "change the target of the rule emitted by dependency " - "generation"}, - {"-MQ target", "same as -MT but quote characters special to make(1)"}, - {"-MG", "consider missing headers as generated files and add them to " - "the dependencies"}, - {"-MP", "add a phony target for each dependency"}, - {"-MD", "same as -M -MT file (with default 'file')"}, - {"-o name", "name output directory or file"}, - {"-pa path", "add path to the front of Erlang's code path"}, - {"-pz path", "add path to the end of Erlang's code path"}, - {"-smp", "compile using SMP emulator"}, - {"-v", "verbose compiler output"}, - {"-Werror", "make all warnings into errors"}, - {"-W0", "disable warnings"}, - {"-Wnumber", "set warning level to number"}, - {"-Wall", "enable all warnings"}, - {"-W", "enable warnings (default; same as -W1)"}, - {"-E", "generate listing of expanded code (Erlang compiler)"}, - {"-S", "generate assembly listing (Erlang compiler)"}, - {"-P", "generate listing of preprocessed code (Erlang compiler)"}, - {"+term", "pass the Erlang term unchanged to the compiler"}, - }; - int i; - - fprintf(stderr, "Usage:\terlc [options] file.ext ...\n"); - fprintf(stderr, "Options:\n"); - for (i = 0; i < sizeof(options)/sizeof(options[0]); i++) { - fprintf(stderr, "%-14s %s\n", options[i].name, options[i].desc); - } - exit(1); -} - static void error(char* format, ...) { -- cgit v1.2.3 From 138737feb3acd4dd5be310bc8f39712adcafc175 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 19 Sep 2013 17:50:39 +0200 Subject: erts: Fix compiler warning --- erts/emulator/beam/erl_nif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index ee480fb661..9e2e588161 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1609,7 +1609,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ASSERT(mod != NULL); mod_atomp = atom_tab(atom_val(mod_atom)); - init_func = erts_static_nif_get_nif_init(mod_atomp->name, mod_atomp->len); + init_func = erts_static_nif_get_nif_init((char*)mod_atomp->name, mod_atomp->len); if (init_func != NULL) handle = init_func; -- cgit v1.2.3 From f7c9b020f21d57bceddc8596faa275be20625557 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 19 Sep 2013 18:00:01 +0200 Subject: erts: Factor out erts_convert_filename_to_wchar() from erts_convert_filename_to_encoding() --- erts/emulator/beam/erl_unicode.c | 83 ++++++++++++++++++++++++---------------- erts/emulator/beam/global.h | 4 ++ 2 files changed, 55 insertions(+), 32 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index 8f1b22ff92..a448b600ca 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -2023,11 +2023,11 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu } else if (is_binary(name)) { byte *temp_alloc = NULL; byte *bytes; - byte *err_pos; - Uint size,num_chars; + Uint size; size = binary_size(name); bytes = erts_get_aligned_binary_bytes(name, &temp_alloc); + if (encoding != ERL_FILENAME_WIN_WCHAR) { /*Add 0 termination only*/ if (used) @@ -2039,36 +2039,11 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu } 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 */ - 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++; - *p++ = 0; - } - *p++ = 0; - *p++ = 0; - } else { /* WIN_WCHAR and valid UTF8 */ - 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; - } + } else { + name_buf = erts_convert_filename_to_wchar(bytes, size, + statbuf, statbuf_size, + alloc_type, used, 0); + } erts_free_aligned_binary_bytes(temp_alloc); } else { return NULL; @@ -2076,6 +2051,50 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu return name_buf; } +char* erts_convert_filename_to_wchar(byte* bytes, Uint size, + char *statbuf, size_t statbuf_size, + ErtsAlcType_t alloc_type, Sint* used, + Uint extra_wchars) +{ + byte *err_pos; + Uint num_chars; + char* name_buf = NULL; + Sint need; + char *p; + + if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK || + erts_get_user_requested_filename_encoding() == ERL_FILENAME_LATIN1) { + + /* What to do now? Maybe latin1, so just take byte for byte instead */ + need = (Sint) (size + extra_wchars + 1) * 2; + if (need > statbuf_size) { + name_buf = (char *) erts_alloc(alloc_type, need); + } else { + name_buf = statbuf; + } + p = name_buf; + while (size--) { + *p++ = *bytes++; + *p++ = 0; + } + } else { /* WIN_WCHAR and valid UTF8 */ + need = (Sint) (num_chars + extra_wchars + 1) * 2; + if (need > statbuf_size) { + name_buf = (char *) erts_alloc(alloc_type, need); + } else { + name_buf = statbuf; + } + erts_copy_utf8_to_utf16_little((byte *) name_buf, bytes, num_chars); + p = name_buf + num_chars*2; + } + *p++ = 0; + *p++ = 0; + if (used) + *used = p - name_buf; + return name_buf; +} + + static int filename_len_16bit(byte *str) { byte *p = str; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 11e464b639..c040b259a0 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -918,6 +918,10 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, int allow_empty, int allow_atom, int encoding, Sint *used /* out */); +char* erts_convert_filename_to_wchar(byte* bytes, Uint size, + char *statbuf, size_t statbuf_size, + ErtsAlcType_t alloc_type, Sint* used, + Uint extra_wchars); Eterm erts_convert_native_to_filename(Process *p, byte *bytes); Eterm erts_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz, Uint left, Uint *num_built, Uint *num_eaten, Eterm tail); -- cgit v1.2.3 From 6fea2c436a6f4a501be632e9bb7453570c09fb8e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 19 Sep 2013 19:13:13 +0200 Subject: erts, crypto: Support NIF library with unicode filename on windows --- erts/emulator/beam/erl_nif.c | 12 +++++++++--- erts/emulator/sys/win32/erl_win32_sys_ddll.c | 24 ++++++++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 9e2e588161..e87959f0ab 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1568,7 +1568,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) void* init_func = NULL; ErlNifEntry* entry = NULL; ErlNifEnv env; - int i, err; + int i, err, encoding; Module* mod; Eterm mod_atom; const Atom* mod_atomp; @@ -1580,8 +1580,14 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) struct erl_module_nif* lib = NULL; int reload_warning = 0; - lib_name = erts_convert_filename_to_native(BIF_ARG_1, NULL, 0, - ERTS_ALC_T_TMP, 1, 0, NULL); + encoding = erts_get_native_filename_encoding(); + if (encoding == ERL_FILENAME_WIN_WCHAR) { + /* Do not convert the lib name to utf-16le yet, do that in win32 specific code */ + /* since lib_name is used in error messages */ + encoding = ERL_FILENAME_UTF8; + } + lib_name = erts_convert_filename_to_encoding(BIF_ARG_1, NULL, 0, + ERTS_ALC_T_TMP, 1, 0, encoding, NULL); if (!lib_name) { BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/sys/win32/erl_win32_sys_ddll.c b/erts/emulator/sys/win32/erl_win32_sys_ddll.c index 4c8d83ab16..553d3b0983 100644 --- a/erts/emulator/sys/win32/erl_win32_sys_ddll.c +++ b/erts/emulator/sys/win32/erl_win32_sys_ddll.c @@ -38,7 +38,7 @@ #include "erl_nif.h" #define EXT_LEN 4 -#define FILE_EXT ".dll" +#define FILE_EXT_WCHAR L".dll" static DWORD tls_index = 0; static TWinDynDriverCallbacks wddc; @@ -57,11 +57,14 @@ void erl_sys_ddll_init(void) { /* * Open a shared object + * Expecting 'full_name' as an UTF-8 string. */ int erts_sys_ddll_open2(const char *full_name, void **handle, ErtsSysDdllError* err) { + HINSTANCE hinstance; int len; char dlname[MAXPATHLEN + 1]; + char* wcp; if ((len = sys_strlen(full_name)) >= MAXPATHLEN - EXT_LEN) { if (err != NULL) { @@ -69,10 +72,23 @@ int erts_sys_ddll_open2(const char *full_name, void **handle, ErtsSysDdllError* } return ERL_DE_LOAD_ERROR_NAME_TO_LONG; } - sys_strcpy(dlname, full_name); - sys_strcpy(dlname+len, FILE_EXT); - return erts_sys_ddll_open_noext(dlname, handle, err); + + wcp = erts_convert_filename_to_wchar(full_name, len, dlname, sizeof(dlname), + ERTS_ALC_T_TMP, &used, EXT_LEN); + wcscpy(&wcp[used], FILE_EXT_WCHAR); + + if ((hinstance = LoadLibraryW(wcp)) == NULL) { + int code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError(); + if (err != NULL) { + err->str = erts_sys_ddll_error(code); + } + return code; + } + + *handle = (void *) hinstance; + return ERL_DE_NO_ERROR; } + int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err) { HINSTANCE hinstance; -- cgit v1.2.3 From 17f6fd613e8b7039526fbb063c6751fc0c2008c3 Mon Sep 17 00:00:00 2001 From: Mike Sassak Date: Fri, 13 Sep 2013 16:37:50 -0700 Subject: Check all pattern arguments passed to binary:matches/2 Including an empty binary as one of multiple patterns passed to binary:matches/2 would crash BEAM with: "Cannot reallocate 1342177280 bytes of memory (of type "tmp")". This ensures each pattern is valid before trying to match. --- erts/emulator/beam/erl_bif_binary.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 0db19a1ee6..ff775691b3 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -927,6 +927,9 @@ static int do_binary_match_compile(Eterm argument, Eterm *tag, Binary **binp) if (binary_bitsize(b) != 0) { goto badarg; } + if (binary_size(b) == 0) { + goto badarg; + } ++words; characters += binary_size(b); } -- cgit v1.2.3 From b404991ffb133e7deb4f9f85816841d5f4a33240 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 21 Sep 2013 12:39:00 +0200 Subject: Fix two small silent rules omissions --- erts/etc/unix/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/unix/Makefile b/erts/etc/unix/Makefile index e85d2fab0c..c137a31ec2 100644 --- a/erts/etc/unix/Makefile +++ b/erts/etc/unix/Makefile @@ -29,7 +29,7 @@ opt debug: etc etc: etp-commands etp-commands: etp-commands.in - sed 's:@ERL_TOP@:${ERL_TOP}:g' etp-commands.in > etp-commands + $(gen_verbose)sed 's:@ERL_TOP@:${ERL_TOP}:g' etp-commands.in > etp-commands .PHONY: docs docs: -- cgit v1.2.3 From 7fbf2c26ac063988818230a0e18a9df48c2fbf2d Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Tue, 27 Aug 2013 11:42:00 -0400 Subject: add {active,N} socket option for TCP, UDP, and SCTP Add the {active,N} socket option, where N is an integer in the range -32768..32767, to allow a caller to specify the number of data messages to be delivered to the controlling process. Once the socket's delivered message count either reaches 0 or is explicitly set to 0 with inet:setopts/2 or by including {active,0} as an option when the socket is created, the socket transitions to passive ({active, false}) mode and the socket's controlling process receives a message to inform it of the transition. TCP sockets receive {tcp_passive,Socket}, UDP sockets receive {udp_passive,Socket} and SCTP sockets receive {sctp_passive,Socket}. The socket's delivered message counter defaults to 0, but it can be set using {active,N} via any gen_tcp, gen_udp, or gen_sctp function that takes socket options as arguments, or via inet:setopts/2. New N values are added to the socket's current counter value, and negative numbers can be used to reduce the counter value. Specifying a number that would cause the socket's counter value to go above 32767 causes an einval error. If a negative number is specified such that the counter value would become negative, the socket's counter value is set to 0 and the socket transitions to passive mode. If the counter value is already 0 and inet:setopts(Socket, [{active,0}]) is specified, the counter value remains at 0 but the appropriate passive mode transition message is generated for the socket. This commit contains a modified preloaded prim_inet.beam due to changes in prim_inet.erl. Add tests for {active,N} mode for TCP, UDP, and SCTP sockets. Add documentation for {active,N} mode for inet, gen_tcp, gen_udp, and gen_sctp. --- erts/emulator/drivers/common/inet_drv.c | 119 +++++++++++++++++++++++++++++--- erts/preloaded/src/prim_inet.erl | 20 ++++-- 2 files changed, 125 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 60db50e80a..7dcb191a83 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -86,6 +86,13 @@ #endif typedef unsigned long long llu_t; +#ifndef INT16_MIN +#define INT16_MIN (-32768) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif + #ifdef __WIN32__ #define STRNCASECMP strncasecmp @@ -581,6 +588,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_PASSIVE 0 /* false */ #define INET_ACTIVE 1 /* true */ #define INET_ONCE 2 /* true; active once then passive */ +#define INET_MULTI 3 /* true; active N then passive */ /* INET_REQ_GETSTATUS enumeration */ #define INET_F_OPEN 0x0001 @@ -925,6 +933,7 @@ typedef struct { inet_async_op op_queue[INET_MAX_ASYNC]; /* call queue */ int active; /* 0 = passive, 1 = active, 2 = active once */ + Sint16 active_count; /* counter for {active,N} */ int stype; /* socket type: SOCK_STREAM/SOCK_DGRAM/SOCK_SEQPACKET */ int sprotocol; /* socket protocol: @@ -1160,22 +1169,36 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event); static int async_ref = 0; /* async reference id generator */ #define NEW_ASYNC_ID() ((async_ref++) & 0xffff) +/* check for transition from active to passive */ +#define INET_CHECK_ACTIVE_TO_PASSIVE(inet) \ + do { \ + if ((inet)->active == INET_ONCE) \ + (inet)->active = INET_PASSIVE; \ + else if ((inet)->active == INET_MULTI && --((inet)->active_count) == 0) { \ + (inet)->active = INET_PASSIVE; \ + packet_passive_message(inet); \ + } \ + } while (0) static ErlDrvTermData am_ok; static ErlDrvTermData am_tcp; static ErlDrvTermData am_udp; static ErlDrvTermData am_error; +static ErlDrvTermData am_einval; static ErlDrvTermData am_inet_async; static ErlDrvTermData am_inet_reply; static ErlDrvTermData am_timeout; static ErlDrvTermData am_closed; +static ErlDrvTermData am_tcp_passive; static ErlDrvTermData am_tcp_closed; static ErlDrvTermData am_tcp_error; +static ErlDrvTermData am_udp_passive; static ErlDrvTermData am_udp_error; static ErlDrvTermData am_empty_out_q; static ErlDrvTermData am_ssl_tls; #ifdef HAVE_SCTP static ErlDrvTermData am_sctp; +static ErlDrvTermData am_sctp_passive; static ErlDrvTermData am_sctp_error; static ErlDrvTermData am_true; static ErlDrvTermData am_false; @@ -1185,6 +1208,7 @@ static ErlDrvTermData am_list; static ErlDrvTermData am_binary; static ErlDrvTermData am_active; static ErlDrvTermData am_once; +static ErlDrvTermData am_multi; static ErlDrvTermData am_buffer; static ErlDrvTermData am_linger; static ErlDrvTermData am_recbuf; @@ -3336,6 +3360,34 @@ static int packet_binary_message return erl_drv_output_term(desc->dport, spec, i); } +/* +** active mode message: send active-to-passive transition message +** {tcp_passive, S} or +** {udp_passive, S} or +** {sctp_passive, S} +*/ + static int packet_passive_message(inet_descriptor* desc) + { + ErlDrvTermData spec[6]; + int i = 0; + + DEBUGF(("packet_passive_message(%ld):\r\n", (long)desc->port)); + + if (desc->sprotocol == IPPROTO_TCP) + i = LOAD_ATOM(spec, i, am_tcp_passive); + else { +#ifdef HAVE_SCTP + i = LOAD_ATOM(spec, i, IS_SCTP(desc) ? am_sctp_passive : am_udp_passive); +#else + i = LOAD_ATOM(spec, i, am_udp_passive); +#endif + } + i = LOAD_PORT(spec, i, desc->dport); + i = LOAD_TUPLE(spec, i, 2); + ASSERT(i <= 6); + return erl_drv_output_term(desc->dport, spec, i); + } + /* ** send active message {udp_error|sctp_error, S, Error} */ @@ -3378,7 +3430,7 @@ static int tcp_reply_data(tcp_descriptor* desc, char* buf, int len) int code; const char* body = buf; int bodylen = len; - + packet_get_body(desc->inet.htype, &body, &bodylen); if (desc->inet.deliver == INET_DELIVER_PORT) { @@ -3396,8 +3448,7 @@ static int tcp_reply_data(tcp_descriptor* desc, char* buf, int len) if (code < 0) return code; - if (desc->inet.active == INET_ONCE) - desc->inet.active = INET_PASSIVE; + INET_CHECK_ACTIVE_TO_PASSIVE(INETP(desc)); return code; } @@ -3424,8 +3475,7 @@ tcp_reply_binary_data(tcp_descriptor* desc, ErlDrvBinary* bin, int offs, int len } if (code < 0) return code; - if (desc->inet.active == INET_ONCE) - desc->inet.active = INET_PASSIVE; + INET_CHECK_ACTIVE_TO_PASSIVE(INETP(desc)); return code; } @@ -3448,8 +3498,7 @@ packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz, code = packet_binary_message(desc, bin, offs, len, extra); if (code < 0) return code; - if (desc->active == INET_ONCE) - desc->active = INET_PASSIVE; + INET_CHECK_ACTIVE_TO_PASSIVE(desc); return code; } } @@ -3494,6 +3543,7 @@ sock_init(void) /* May be called multiple times. */ #ifdef HAVE_SCTP static void inet_init_sctp(void) { INIT_ATOM(sctp); + INIT_ATOM(sctp_passive); INIT_ATOM(sctp_error); INIT_ATOM(true); INIT_ATOM(false); @@ -3503,6 +3553,7 @@ static void inet_init_sctp(void) { INIT_ATOM(binary); INIT_ATOM(active); INIT_ATOM(once); + INIT_ATOM(multi); INIT_ATOM(buffer); INIT_ATOM(linger); INIT_ATOM(recbuf); @@ -3628,12 +3679,15 @@ static int inet_init() INIT_ATOM(tcp); INIT_ATOM(udp); INIT_ATOM(error); + INIT_ATOM(einval); INIT_ATOM(inet_async); INIT_ATOM(inet_reply); INIT_ATOM(timeout); INIT_ATOM(closed); + INIT_ATOM(tcp_passive); INIT_ATOM(tcp_closed); INIT_ATOM(tcp_error); + INIT_ATOM(udp_passive); INIT_ATOM(udp_error); INIT_ATOM(empty_out_q); INIT_ATOM(ssl_tls); @@ -5508,8 +5562,25 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) case INET_LOPT_ACTIVE: DEBUGF(("inet_set_opts(%ld): s=%d, ACTIVE=%d\r\n", - (long)desc->port, desc->s,ival)); + (long)desc->port, desc->s, ival)); desc->active = ival; + if (desc->active == INET_MULTI) { + long ac = desc->active_count; + Sint16 nval = get_int16(ptr); + ptr += 2; + len -= 2; + ac += nval; + if (ac > INT16_MAX || ac < INT16_MIN) + return -1; + desc->active_count += nval; + if (desc->active_count < 0) + desc->active_count = 0; + if (desc->active_count == 0) { + desc->active = INET_PASSIVE; + packet_passive_message(desc); + } + } else + desc->active_count = 0; if ((desc->stype == SOCK_STREAM) && (desc->active != INET_PASSIVE) && (desc->state == INET_STATE_CLOSED)) { tcp_closed_message((tcp_descriptor *) desc); @@ -5820,7 +5891,8 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) /* XXX fprintf(stderr,"desc->htype == %d, old_htype == %d, desc->active == %d, old_active == %d\r\n",(int)desc->htype, (int) old_htype, (int) desc->active, (int) old_active );*/ - return 1+(desc->htype == old_htype && desc->active == INET_ONCE); + return 1+(desc->htype == old_htype && + (desc->active == INET_ONCE || desc->active == INET_MULTI)); } return 0; } @@ -5953,6 +6025,21 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) case INET_LOPT_ACTIVE: desc->active = get_int32(curr); curr += 4; + if (desc->active == INET_MULTI) { + long ac = desc->active_count; + Sint16 nval = get_int16(curr); curr += 2; + ac += nval; + if (ac > INT16_MAX || ac < INT16_MIN) + return -1; + desc->active_count += nval; + if (desc->active_count < 0) + desc->active_count = 0; + if (desc->active_count == 0) { + desc->active = INET_PASSIVE; + packet_passive_message(desc); + } + } else + desc->active_count = 0; res = 0; continue; @@ -6475,6 +6562,11 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, case INET_LOPT_ACTIVE: *ptr++ = opt; put_int32(desc->active, ptr); + if (desc->active == INET_MULTI) { + PLACE_FOR(2,ptr); + put_int16(desc->active_count, ptr); + ptr += 2; + } continue; case INET_LOPT_PACKET: *ptr++ = opt; @@ -6847,7 +6939,10 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, } case INET_LOPT_ACTIVE: { - PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_TUPLE_CNT); + if (desc->active == INET_MULTI) + PLACE_FOR(spec, i, LOAD_ATOM_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT); + else + PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_TUPLE_CNT); i = LOAD_ATOM (spec, i, am_active); switch (desc->active) { @@ -6860,6 +6955,9 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, case INET_ONCE : { i = LOAD_ATOM (spec, i, am_once); break; } + case INET_MULTI : + { i = LOAD_INT(spec, i, desc->active_count); break; } + default: ASSERT (0); } i = LOAD_TUPLE (spec, i, 2); @@ -7656,6 +7754,7 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) socket */ desc->deliver = INET_DELIVER_TERM; /* standard term format */ desc->active = INET_PASSIVE; /* start passive */ + desc->active_count = 0; desc->oph = NULL; desc->opt = NULL; diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index fa621681f3..69eed716ff 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -1237,7 +1237,8 @@ type_opt_1(buffer) -> int; type_opt_1(active) -> {enum,[{false, ?INET_PASSIVE}, {true, ?INET_ACTIVE}, - {once, ?INET_ONCE}]}; + {once, ?INET_ONCE}, + {multi, ?INET_MULTI}]}; type_opt_1(packet) -> {enum,[{0, ?TCP_PB_RAW}, {1, ?TCP_PB_1}, @@ -1716,11 +1717,14 @@ encode_opt_val(Opts) -> Reason -> {error,Reason} end. +%% {active, once} and {active, N} are specially optimized because they will +%% be used for every packet or every N packets, not only once when +%% initializing the socket. Measurements show that this optimization is +%% worthwhile. enc_opt_val([{active,once}|Opts], Acc) -> - %% Specially optimized because {active,once} will be used for - %% every packet, not only once when initializing the socket. - %% Measurements show that this optimization is worthwhile. enc_opt_val(Opts, [<>|Acc]); +enc_opt_val([{active,N}|Opts], Acc) when is_integer(N), N < 32768, N >= -32768 -> + enc_opt_val(Opts, [<>|Acc]); enc_opt_val([{raw,P,O,B}|Opts], Acc) -> enc_opt_val(Opts, Acc, raw, {P,O,B}); enc_opt_val([{Opt,Val}|Opts], Acc) -> @@ -1810,6 +1814,14 @@ dec_opt_val([]) -> []. dec_opt_val(Buf, raw, Type) -> {{P,O,B},T} = dec_value(Type, Buf), [{raw,P,O,B}|dec_opt_val(T)]; +dec_opt_val(Buf, active, Type) -> + case dec_value(Type, Buf) of + {multi,[M0,M1|T]} -> + <> = list_to_binary([M0,M1]), + [{active,N}|dec_opt_val(T)]; + {Val,T} -> + [{active,Val}|dec_opt_val(T)] + end; dec_opt_val(Buf, Opt, Type) -> {Val,T} = dec_value(Type, Buf), [{Opt,Val}|dec_opt_val(T)]. -- cgit v1.2.3 From 1e6c600111fac2cf564bcdfab0230ffd70cb30c3 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 23 Sep 2013 17:52:48 +0200 Subject: Update preloaded --- erts/preloaded/ebin/prim_inet.beam | Bin 70908 -> 71452 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 2be5ae1d96..deed86816e 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ -- cgit v1.2.3 From 75a79e6547fd66c2194d6f488c30ad888a715f4b Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Wed, 4 Sep 2013 10:21:47 -0400 Subject: add system_info(ets_limit) Add system_info(ets_limit) to provide a way to retrieve the runtime's maximum number of ETS tables. Add tests and documentation for it too. Also repair the alphabetical order of system_info/1 argument descriptions in the documentation and in the erlang.erl clauses. Add new preloaded erlang.erl due to that change. Also ensure all system_info/1 clauses are represented in the erlang.xml source documentation -- a couple had been inadvertently dropped in previous commits when other clauses were added. --- erts/doc/src/erl.xml | 2 +- erts/doc/src/erlang.xml | 22 ++++++++++--- erts/emulator/beam/erl_bif_info.c | 3 ++ erts/emulator/beam/erl_db.c | 7 +++++ erts/emulator/beam/erl_db.h | 2 ++ erts/emulator/test/system_info_SUITE.erl | 54 ++++++++++++++++++++++++++++++-- erts/preloaded/src/erlang.erl | 3 +- 7 files changed, 84 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index c16b45856d..528a2d95aa 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -524,7 +524,7 @@

Calling erlang:halt/1 with a string argument will still produce a crash dump.

- +

Set max number of ETS tables.

diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 5ee40823bc..d3b21de8cf 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5500,6 +5500,9 @@ ok + + + Information about the system

Returns various information about the current system @@ -5576,6 +5579,13 @@ ok information see the "How to interpret the Erlang crash dumps" chapter in the ERTS User's Guide.

+ dist_buf_busy_limit + +

Returns the value of the distribution buffer busy limit + in bytes. This limit can be set on startup by passing the + +zdbbl command line + flag to erl.

+
dist_ctrl

Returns a list of tuples @@ -5622,12 +5632,14 @@ ok The return value will always be false since the elib_malloc allocator has been removed.

- dist_buf_busy_limit + ets_limit -

Returns the value of the distribution buffer busy limit - in bytes. This limit can be set on startup by passing the - +zdbbl command line - flag to erl.

+

Returns the maximum number of ETS tables allowed. This limit + can be increased on startup by passing the +e command line flag to + erl or by setting the environment variable + ERL_MAX_ETS_TABLES before starting the Erlang runtime + system.

fullsweep_after diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 673dfc658c..3b25efd9af 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2636,6 +2636,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(res); } + else if (ERTS_IS_ATOM_STR("ets_limit",BIF_ARG_1)) { + BIF_RET(make_small(erts_db_get_max_tabs())); + } BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 98c2988323..40b8eaf8fb 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -3811,6 +3811,13 @@ erts_db_foreach_offheap(DbTable *tb, tb->common.meth->db_foreach_offheap(tb, func, arg); } +/* retrieve max number of ets tables */ +Uint +erts_db_get_max_tabs() +{ + return db_max_tabs; +} + /* * For testing of meta tables only. * diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h index 6b62e10eb7..5b4681fc90 100644 --- a/erts/emulator/beam/erl_db.h +++ b/erts/emulator/beam/erl_db.h @@ -79,6 +79,8 @@ extern erts_smp_atomic_t erts_ets_misc_mem_size; Eterm erts_ets_colliding_names(Process*, Eterm name, Uint cnt); +Uint erts_db_get_max_tabs(void); + #endif #if defined(ERTS_WANT_DB_INTERNAL__) && !defined(ERTS_HAVE_DB_INTERNAL__) diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index 0350eb671d..ceb4afb5cf 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -37,7 +37,8 @@ init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). --export([process_count/1, system_version/1, misc_smoke_tests/1, heap_size/1, wordsize/1, memory/1]). +-export([process_count/1, system_version/1, misc_smoke_tests/1, heap_size/1, wordsize/1, memory/1, + ets_limit/1]). -define(DEFAULT_TIMEOUT, ?t:minutes(2)). @@ -45,7 +46,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [process_count, system_version, misc_smoke_tests, - heap_size, wordsize, memory]. + heap_size, wordsize, memory, ets_limit]. groups() -> []. @@ -496,3 +497,52 @@ mapn(_Fun, 0) -> []; mapn(Fun, N) -> [Fun(N) | mapn(Fun, N-1)]. + +ets_limit(doc) -> + "Verify system_info(ets_limit) reflects max ETS table settings."; +ets_limit(suite) -> []; +ets_limit(Config0) when is_list(Config0) -> + Config = [{testcase,ets_limit}|Config0], + true = is_integer(get_ets_limit(Config)), + 12345 = get_ets_limit(Config, 12345), + ok. + +get_ets_limit(Config) -> + get_ets_limit(Config, 0). +get_ets_limit(Config, EtsMax) -> + Envs = case EtsMax of + 0 -> []; + _ -> [{"ERL_MAX_ETS_TABLES", integer_to_list(EtsMax)}] + end, + {ok, Node} = start_node(Config, Envs), + Me = self(), + Ref = make_ref(), + spawn_link(Node, + fun() -> + Res = erlang:system_info(ets_limit), + unlink(Me), + Me ! {Ref, Res} + end), + receive + {Ref, Res} -> + Res + end, + stop_node(Node), + Res. + +start_node(Config, Envs) when is_list(Config) -> + Pa = filename:dirname(code:which(?MODULE)), + {A, B, C} = now(), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(?config(testcase, Config)) + ++ "-" + ++ integer_to_list(A) + ++ "-" + ++ integer_to_list(B) + ++ "-" + ++ integer_to_list(C)), + ?t:start_node(Name, peer, [{args, "-pa "++Pa}, {env, Envs}]). + +stop_node(Node) -> + ?t:stop_node(Node). diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index a969ef91dc..d40ee7c59a 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2099,13 +2099,14 @@ tuple_to_list(_Tuple) -> (creation) -> integer(); (debug_compiled) -> boolean(); (dist) -> binary(); + (dist_buf_busy_limit) -> non_neg_integer(); (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(); + (ets_limit) -> pos_integer(); (fullsweep_after) -> {fullsweep_after, non_neg_integer()}; (garbage_collection) -> [{atom(), integer()}]; (heap_sizes) -> [non_neg_integer()]; -- cgit v1.2.3 From 0d18c06a55bcde2af8cff12fe49093fffd32466c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 24 Sep 2013 09:58:06 +0200 Subject: Update preloaded --- erts/preloaded/ebin/erlang.beam | Bin 94152 -> 94156 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 9da4f3cd00..f1791200e0 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ -- cgit v1.2.3 From 02726f24bbe4bcc8da252d572c555c041c3599e6 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Tue, 24 Sep 2013 10:56:22 +0200 Subject: Update version numbers for R16B03 development --- erts/vsn.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index e235c50f0b..9afd46b961 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,8 +17,8 @@ # %CopyrightEnd% # -VSN = 5.10.3 -SYSTEM_VSN = R16B02 +VSN = 5.10.4 +SYSTEM_VSN = R16B03 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 3a0402e1157a89fac210f2276d9461aab30a9968 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Tue, 24 Sep 2013 16:19:44 +0200 Subject: Fix open_ddll for win --- erts/emulator/sys/win32/erl_win32_sys_ddll.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/win32/erl_win32_sys_ddll.c b/erts/emulator/sys/win32/erl_win32_sys_ddll.c index 553d3b0983..2d3f073cc2 100644 --- a/erts/emulator/sys/win32/erl_win32_sys_ddll.c +++ b/erts/emulator/sys/win32/erl_win32_sys_ddll.c @@ -63,8 +63,9 @@ int erts_sys_ddll_open2(const char *full_name, void **handle, ErtsSysDdllError* { HINSTANCE hinstance; int len; - char dlname[MAXPATHLEN + 1]; - char* wcp; + wchar_t* wcp; + Sint used; + int code; if ((len = sys_strlen(full_name)) >= MAXPATHLEN - EXT_LEN) { if (err != NULL) { @@ -73,20 +74,23 @@ int erts_sys_ddll_open2(const char *full_name, void **handle, ErtsSysDdllError* return ERL_DE_LOAD_ERROR_NAME_TO_LONG; } - wcp = erts_convert_filename_to_wchar(full_name, len, dlname, sizeof(dlname), - ERTS_ALC_T_TMP, &used, EXT_LEN); - wcscpy(&wcp[used], FILE_EXT_WCHAR); + wcp = (wchar_t*)erts_convert_filename_to_wchar((byte*)full_name, len, + NULL, 0, + ERTS_ALC_T_TMP, &used, EXT_LEN); + wcscpy(&wcp[used/2 - 1], FILE_EXT_WCHAR); if ((hinstance = LoadLibraryW(wcp)) == NULL) { - int code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError(); + code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError(); if (err != NULL) { err->str = erts_sys_ddll_error(code); } - return code; } - - *handle = (void *) hinstance; - return ERL_DE_NO_ERROR; + else { + code = ERL_DE_NO_ERROR; + *handle = (void *) hinstance; + } + erts_free(ERTS_ALC_T_TMP, wcp); + return code; } int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err) -- cgit v1.2.3 From b6b0b73ecec7facefb3b9c5a7ef663599cfee4aa Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 20 Sep 2013 20:13:40 +0200 Subject: erts: Fix bug in atom to filename conversions Buggy old code assumed latin1 atoms. --- erts/emulator/beam/erl_unicode.c | 59 ++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index a448b600ca..ec8ea5f044 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -2174,16 +2174,31 @@ Sint erts_native_filename_need(Eterm ioterm, int encoding) ap = atom_tab(atom_val(ioterm)); switch (encoding) { case ERL_FILENAME_LATIN1: - need = ap->len; + need = ap->latin1_chars; /* May be -1 */ break; case ERL_FILENAME_UTF8_MAC: case ERL_FILENAME_UTF8: - for (i = 0; i < ap->len; i++) { - need += (ap->name[i] >= 0x80) ? 2 : 1; - } + need = ap->len; break; case ERL_FILENAME_WIN_WCHAR: - need = 2*(ap->len); + if (ap->latin1_chars >= 0) { + need = 2* ap->latin1_chars; + } + else { + for (i = 0; i < ap->len; ) { + if (ap->name[i] < 0x80) { + i++; + } else if (ap->name[i] < 0xE0) { + i += 2; + } else if (ap->name[i] < 0xF0) { + i += 3; + } else { + need = -1; + break; + } + need += 2; + } + } break; default: need = -1; @@ -2313,26 +2328,36 @@ void erts_native_filename_put(Eterm ioterm, int encoding, byte *p) switch (encoding) { case ERL_FILENAME_LATIN1: for (i = 0; i < ap->len; i++) { - *p++ = ap->name[i]; - } - break; - case ERL_FILENAME_UTF8_MAC: - case ERL_FILENAME_UTF8: - for (i = 0; i < ap->len; i++) { - if(ap->name[i] < 0x80) { + if (ap->name[i] < 0x80) { *p++ = ap->name[i]; } else { - *p++ = (((ap->name[i]) >> 6) | ((byte) 0xC0)); - *p++ = (((ap->name[i]) & 0x3F) | ((byte) 0x80)); + ASSERT(ap->name[i] < 0xC4); + *p++ = ((ap->name[i] & 3) << 6) | (ap->name[i+1] & 0x3F); + i++; } } break; + case ERL_FILENAME_UTF8_MAC: + case ERL_FILENAME_UTF8: + sys_memcpy(p, ap->name, ap->len); + break; case ERL_FILENAME_WIN_WCHAR: for (i = 0; i < ap->len; i++) { /* Little endian */ - *p++ = ap->name[i]; - *p++ = 0; - } + if (ap->name[i] < 0x80) { + *p++ = ap->name[i]; + *p++ = 0; + } else if (ap->name[i] < 0xE0) { + *p++ = ((ap->name[i] & 3) << 6) | (ap->name[i+1] & 0x3F); + *p++ = ((ap->name[i] & 0x1C) >> 2); + i++; + } else { + ASSERT(ap->name[i] < 0xF0); + *p++ = ((ap->name[i+1] & 3) << 6) | (ap->name[i+2] & 0x3C); + *p++ = ((ap->name[i] & 0xF) << 4) | ((ap->name[i+1] & 0x3C) >> 2); + i += 2; + } + } break; default: ASSERT(0); -- cgit v1.2.3 From cabfe2cdc0b8d879431f81233addd2a43ff1f742 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 11 Sep 2013 21:49:32 +0200 Subject: Implement platform specific aligned sys_alloc and use when supported erts_sys_aligned_alloc() is currently implemented using posix_memalign if it exist, or using _aligned_malloc on Windows. If erts_sys_aligned_alloc() exist allocators will create sys_alloc carriers similar to how this was done pre-R16. --- erts/configure.in | 4 +- erts/emulator/beam/erl_alloc.c | 21 ++----- erts/emulator/beam/erl_alloc.h | 10 ++++ erts/emulator/beam/erl_alloc_util.c | 103 ++++++++++++++++++++-------------- erts/emulator/beam/erl_alloc_util.h | 27 ++++++--- erts/emulator/beam/sys.h | 3 + erts/emulator/sys/common/erl_mseg.h | 6 +- erts/emulator/sys/unix/erl_unix_sys.h | 4 ++ erts/emulator/sys/unix/sys.c | 46 +++++++++++++++ erts/emulator/sys/win32/erl_win_sys.h | 2 + erts/emulator/sys/win32/sys.c | 26 ++++++++- 11 files changed, 181 insertions(+), 71 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 7257751068..c391aece14 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1924,8 +1924,8 @@ AC_CHECK_FUNCS([getipnodebyname getipnodebyaddr gethostbyname2]) AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlopen \ pread pwrite memmove strerror strerror_r strncasecmp \ - gethrtime localtime_r gmtime_r inet_pton mmap mremap memcpy mallopt \ - sbrk _sbrk __sbrk brk _brk __brk \ + gethrtime localtime_r gmtime_r inet_pton posix_memalign \ + mmap mremap memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \ flockfile fstat strlcpy strlcat setsid posix2time time2posix \ setlocale nl_langinfo poll]) diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index d748f86d75..13692edbfc 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -235,7 +235,6 @@ set_default_sbmbc_alloc_opts(struct au_init *ip) ip->init.bf.ao = 1; ip->init.util.ramv = 0; ip->init.util.mmsbc = 0; - ip->init.util.mmmbc = 500; ip->init.util.sbct = ~((UWord) 0); ip->init.util.name_prefix = "sbmbc_"; ip->init.util.alloc_no = ERTS_ALC_A_SBMBC; @@ -261,7 +260,6 @@ set_default_sl_alloc_opts(struct au_init *ip) ip->thr_spec = 1; ip->atype = GOODFIT; ip->init.util.name_prefix = "sl_"; - ip->init.util.mmmbc = 5; ip->init.util.alloc_no = ERTS_ALC_A_SHORT_LIVED; #ifndef SMALL_MEMORY ip->init.util.mmbcs = 128*1024; /* Main carrier size */ @@ -285,7 +283,6 @@ set_default_std_alloc_opts(struct au_init *ip) ip->thr_spec = 1; ip->atype = BESTFIT; ip->init.util.name_prefix = "std_"; - ip->init.util.mmmbc = 5; ip->init.util.alloc_no = ERTS_ALC_A_STANDARD; #ifndef SMALL_MEMORY ip->init.util.mmbcs = 128*1024; /* Main carrier size */ @@ -305,7 +302,6 @@ set_default_ll_alloc_opts(struct au_init *ip) ip->init.bf.ao = 1; ip->init.util.ramv = 0; ip->init.util.mmsbc = 0; - ip->init.util.mmmbc = 0; ip->init.util.sbct = ~((UWord) 0); ip->init.util.name_prefix = "ll_"; ip->init.util.alloc_no = ERTS_ALC_A_LONG_LIVED; @@ -353,7 +349,6 @@ set_default_eheap_alloc_opts(struct au_init *ip) ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); ip->thr_spec = 1; ip->atype = GOODFIT; - ip->init.util.mmmbc = 100; ip->init.util.name_prefix = "eheap_"; ip->init.util.alloc_no = ERTS_ALC_A_EHEAP; #ifndef SMALL_MEMORY @@ -376,7 +371,6 @@ set_default_binary_alloc_opts(struct au_init *ip) ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); ip->thr_spec = 1; ip->atype = BESTFIT; - ip->init.util.mmmbc = 50; ip->init.util.name_prefix = "binary_"; ip->init.util.alloc_no = ERTS_ALC_A_BINARY; #ifndef SMALL_MEMORY @@ -394,7 +388,6 @@ set_default_ets_alloc_opts(struct au_init *ip) ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); ip->thr_spec = 1; ip->atype = BESTFIT; - ip->init.util.mmmbc = 100; ip->init.util.name_prefix = "ets_"; ip->init.util.alloc_no = ERTS_ALC_A_ETS; #ifndef SMALL_MEMORY @@ -462,13 +455,6 @@ adjust_tpref(struct au_init *ip, int no_sched) /* ... shrink smallest multi-block carrier size */ if (ip->default_.smbcs) ip->init.util.smbcs /= ERTS_MIN(4, no_sched); - /* ... and more than three allocators shrink - max mseg multi-block carriers */ - if (ip->default_.mmmbc && no_sched > 2) { - ip->init.util.mmmbc /= ERTS_MIN(4, no_sched - 1); - if (ip->init.util.mmmbc < 3) - ip->init.util.mmmbc = 3; - } } } @@ -2609,8 +2595,8 @@ erts_allocator_options(void *proc) #endif Uint sz, *szp, *hp, **hpp; Eterm res, features, settings; - Eterm atoms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+5]; - Uint terms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+5]; + Eterm atoms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+6]; + Uint terms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+6]; int a, length; SysAllocStat sas; Uint *endp = NULL; @@ -2723,6 +2709,9 @@ erts_allocator_options(void *proc) if (use_mseg) terms[length++] = am_atom_put("mseg_alloc", 10); #endif +#if ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC + terms[length++] = am_atom_put("sys_aligned_alloc", 17); +#endif features = length ? erts_bld_list(hpp, szp, length, terms) : NIL; diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index ba5ec9c367..7168241f76 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -54,6 +54,16 @@ void erts_sys_alloc_init(void); void *erts_sys_alloc(ErtsAlcType_t, void *, Uint); void *erts_sys_realloc(ErtsAlcType_t, void *, void *, Uint); void erts_sys_free(ErtsAlcType_t, void *, void *); +#if ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC +/* + * Note 'alignment' must remain the same in calls to + * 'erts_sys_aligned_realloc()' and 'erts_sys_aligned_free()' + * as in the initial call to 'erts_sys_aligned_alloc()'. + */ +void *erts_sys_aligned_alloc(UWord alignment, UWord size); +void *erts_sys_aligned_realloc(UWord alignment, void *ptr, UWord size, UWord old_size); +void erts_sys_aligned_free(UWord alignment, void *ptr); +#endif Eterm erts_memory(int *, void *, void *, Eterm); Eterm erts_allocated_areas(int *, void *, void *); diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index ac7f420708..24f297dc58 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -76,14 +76,14 @@ static int initialized = 0; int erts_have_sbmbc_alloc; -#if HAVE_ERTS_MSEG +#if ERTS_SA_MB_CARRIERS -#define MSEG_UNIT_SHIFT MSEG_ALIGN_BITS -#define MSEG_UNIT_SZ (1 << MSEG_UNIT_SHIFT) -#define MSEG_UNIT_MASK ((~(UWord)0) << MSEG_UNIT_SHIFT) +#define ERTS_SACRR_UNIT_SHIFT ERTS_SUPER_ALIGN_BITS +#define ERTS_SACRR_UNIT_SZ (1 << ERTS_SACRR_UNIT_SHIFT) +#define ERTS_SACRR_UNIT_MASK ((~(UWord)0) << ERTS_SACRR_UNIT_SHIFT) -#define MSEG_UNIT_FLOOR(X) ((X) & MSEG_UNIT_MASK) -#define MSEG_UNIT_CEILING(X) MSEG_UNIT_FLOOR((X) + ~MSEG_UNIT_MASK) +#define ERTS_SACRR_UNIT_FLOOR(X) ((X) & ERTS_SACRR_UNIT_MASK) +#define ERTS_SACRR_UNIT_CEILING(X) ERTS_SACRR_UNIT_FLOOR((X) + ~ERTS_SACRR_UNIT_MASK) #endif @@ -208,9 +208,9 @@ MBC after deallocating first block: #if MBC_ABLK_OFFSET_BITS -# define MBC_SZ_MAX_LIMIT ((((UWord)1 << MBC_ABLK_OFFSET_BITS) - 1) << MSEG_ALIGN_BITS) +# define MBC_SZ_MAX_LIMIT ((((UWord)1 << MBC_ABLK_OFFSET_BITS) - 1) << ERTS_SUPER_ALIGN_BITS) -# define BLK_CARRIER_OFFSET(B, C) (((char*)(B) - (char*)(C)) >> MSEG_UNIT_SHIFT) +# define BLK_CARRIER_OFFSET(B, C) (((char*)(B) - (char*)(C)) >> ERTS_SACRR_UNIT_SHIFT) # define SET_MBC_ABLK_HDR(B, Sz, F, C) \ (ASSERT(((Sz) & ~MBC_ABLK_SZ_MASK) == 0), \ @@ -225,8 +225,8 @@ MBC after deallocating first block: # define ABLK_TO_MBC(B) \ (ASSERT(IS_MBC_BLK(B) && IS_ALLOCED_BLK(B)), \ - (Carrier_t*)((MSEG_UNIT_FLOOR((UWord)(B)) - \ - (((B)->bhdr >> MBC_ABLK_OFFSET_SHIFT) << MSEG_UNIT_SHIFT)))) + (Carrier_t*)((ERTS_SACRR_UNIT_FLOOR((UWord)(B)) - \ + (((B)->bhdr >> MBC_ABLK_OFFSET_SHIFT) << ERTS_SACRR_UNIT_SHIFT)))) # define FBLK_TO_MBC(B) \ (ASSERT(IS_MBC_BLK(B) && IS_FREE_BLK(B)), \ @@ -235,7 +235,7 @@ MBC after deallocating first block: # define BLK_TO_MBC(B) (IS_FREE_BLK(B) ? FBLK_TO_MBC(B) : ABLK_TO_MBC(B)) # define IS_MBC_FIRST_ABLK(AP,B) \ - ((((UWord)(B) & ~MSEG_UNIT_MASK) == MBC_HEADER_SIZE(AP)) \ + ((((UWord)(B) & ~ERTS_SACRR_UNIT_MASK) == MBC_HEADER_SIZE(AP)) \ && ((B)->bhdr & MBC_ABLK_OFFSET_MASK) == 0) # define IS_MBC_FIRST_FBLK(AP,B) \ @@ -652,11 +652,15 @@ alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags) #endif static ERTS_INLINE void * -alcu_sys_alloc(Allctr_t *allctr, Uint size) +alcu_sys_alloc(Allctr_t *allctr, Uint size, int superalign) { void *res; - - res = erts_sys_alloc(0, NULL, size); +#if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC + if (superalign) + res = erts_sys_aligned_alloc(ERTS_SACRR_UNIT_SZ, size); + else +#endif + res = erts_sys_alloc(0, NULL, size); INC_CC(allctr->calls.sys_alloc); if (erts_mtrace_enabled) erts_mtrace_crr_alloc(res, allctr->alloc_no, ERTS_ALC_A_SYSTEM, size); @@ -664,11 +668,16 @@ alcu_sys_alloc(Allctr_t *allctr, Uint size) } static ERTS_INLINE void * -alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint size) +alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign) { void *res; - res = erts_sys_realloc(0, NULL, ptr, size); +#if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC + if (superalign) + res = erts_sys_aligned_realloc(ERTS_SACRR_UNIT_SZ, ptr, size, old_size); + else +#endif + res = erts_sys_realloc(0, NULL, ptr, size); INC_CC(allctr->calls.sys_realloc); if (erts_mtrace_enabled) erts_mtrace_crr_realloc(res, @@ -680,9 +689,14 @@ alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint size) } static ERTS_INLINE void -alcu_sys_free(Allctr_t *allctr, void *ptr) +alcu_sys_free(Allctr_t *allctr, void *ptr, int superalign) { - erts_sys_free(0, NULL, ptr); +#if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC + if (superalign) + erts_sys_aligned_free(ERTS_SACRR_UNIT_SZ, ptr); + else +#endif + erts_sys_free(0, NULL, ptr); INC_CC(allctr->calls.sys_free); if (erts_mtrace_enabled) erts_mtrace_crr_free(allctr->alloc_no, ERTS_ALC_A_SYSTEM, ptr); @@ -1321,7 +1335,7 @@ mbc_alloc_block(Allctr_t *allctr, Uint size, Uint *blk_szp, Uint32 *alcu_flgsp) blk = create_sbmbc(allctr, get_blk_sz); else { blk = create_carrier(allctr, get_blk_sz, CFLG_MBC); -#if !HALFWORD_HEAP && !HAVE_SUPER_ALIGNED_MB_CARRIERS +#if !HALFWORD_HEAP && !ERTS_SUPER_ALIGNED_MSEG_ONLY if (!blk) { /* Emergency! We couldn't create the carrier as we wanted. Try to place it in a sys_alloced sbc. */ @@ -1927,10 +1941,10 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs) #ifdef DEBUG -#if HAVE_ERTS_MSEG -#define ASSERT_MSEG_UNIT_SIZE_MULTIPLE(CSZ) ASSERT((CSZ) % MSEG_UNIT_SZ == 0) +#if ERTS_SA_MB_CARRIERS +#define ASSERT_ERTS_SACRR_UNIT_SIZE_MULTIPLE(CSZ) ASSERT((CSZ) % ERTS_SACRR_UNIT_SZ == 0) #else -#define ASSERT_MSEG_UNIT_SIZE_MULTIPLE(CSZ) +#define ASSERT_ERTS_SACRR_UNIT_SIZE_MULTIPLE(CSZ) #endif static void CHECK_1BLK_CARRIER(Allctr_t* A, int SBC, int MSEGED, Carrier_t* C, @@ -1955,7 +1969,7 @@ static void CHECK_1BLK_CARRIER(Allctr_t* A, int SBC, int MSEGED, Carrier_t* C, } if ((MSEGED)) { ASSERT(IS_MSEG_CARRIER((C))); - ASSERT_MSEG_UNIT_SIZE_MULTIPLE((CSZ)); + ASSERT_ERTS_SACRR_UNIT_SIZE_MULTIPLE((CSZ)); } else { ASSERT(IS_SYS_ALLOC_CARRIER((C))); @@ -2058,7 +2072,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) #if HALFWORD_HEAP flags |= CFLG_FORCE_MSEG; -#elif HAVE_SUPER_ALIGNED_MB_CARRIERS +#elif ERTS_SUPER_ALIGNED_MSEG_ONLY if (flags & CFLG_MBC) { flags |= CFLG_FORCE_MSEG; } @@ -2083,7 +2097,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) if (allctr->sbcs.curr.norm.mseg.no >= allctr->max_mseg_sbcs) goto try_sys_alloc; } -#if !HAVE_SUPER_ALIGNED_MB_CARRIERS +#if !ERTS_SUPER_ALIGNED_MSEG_ONLY else { if (allctr->mbcs.curr.norm.mseg.no >= allctr->max_mseg_mbcs) goto try_sys_alloc; @@ -2146,12 +2160,12 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) ? UNIT_CEILING(bcrr_sz) : SYS_ALLOC_CARRIER_CEILING(bcrr_sz)); - crr = (Carrier_t *) alcu_sys_alloc(allctr, crr_sz); + crr = (Carrier_t *) alcu_sys_alloc(allctr, crr_sz, flags & CFLG_MBC); if (!crr) { if (crr_sz > UNIT_CEILING(bcrr_sz)) { crr_sz = UNIT_CEILING(bcrr_sz); - crr = (Carrier_t *) alcu_sys_alloc(allctr, crr_sz); + crr = (Carrier_t *) alcu_sys_alloc(allctr, crr_sz, flags & CFLG_MBC); } if (!crr) { #if HAVE_ERTS_MSEG @@ -2245,7 +2259,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) if (!(flags & CFLG_FORCE_SYS_ALLOC)) { new_crr_sz = new_blk_sz + SBC_HEADER_SIZE; - new_crr_sz = MSEG_UNIT_CEILING(new_crr_sz); + new_crr_sz = ERTS_SACRR_UNIT_CEILING(new_crr_sz); new_crr = (Carrier_t *) alcu_mseg_realloc(allctr, old_crr, old_crr_sz, @@ -2295,7 +2309,9 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) new_crr = (Carrier_t *) alcu_sys_realloc(allctr, (void *) old_crr, - new_crr_sz); + new_crr_sz, + old_crr_sz, + 0); if (new_crr) { sys_realloc_success: SET_CARRIER_SZ(new_crr, new_crr_sz); @@ -2314,7 +2330,9 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) new_crr_sz = UNIT_CEILING(new_crr_sz); new_crr = (Carrier_t *) alcu_sys_realloc(allctr, (void *) old_crr, - new_crr_sz); + new_crr_sz, + old_crr_sz, + 0); if (new_crr) goto sys_realloc_success; } @@ -2333,7 +2351,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) (void *) BLK2UMEM(old_blk), MIN(new_blk_sz, old_blk_sz) - ABLK_HDR_SZ); unlink_carrier(&allctr->sbc_list, old_crr); - alcu_sys_free(allctr, old_crr); + alcu_sys_free(allctr, old_crr, 0); } else { /* Old carrier unchanged; restore... */ @@ -2350,6 +2368,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk) { Uint crr_sz; Carrier_t *crr; + int is_mbc; #if HAVE_ERTS_MSEG Uint is_mseg = 0; Uint mseg_flags = ERTS_MSEG_FLG_NONE; @@ -2357,6 +2376,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk) if (IS_SBC_BLK(blk)) { Uint blk_sz = SBC_BLK_SZ(blk); + is_mbc = 0; crr = BLK_TO_SBC(blk); crr_sz = CARRIER_SZ(crr); @@ -2367,7 +2387,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk) #if HAVE_ERTS_MSEG if (IS_MSEG_CARRIER(crr)) { is_mseg++; - ASSERT(crr_sz % MSEG_UNIT_SZ == 0); + ASSERT(crr_sz % ERTS_SACRR_UNIT_SZ == 0); STAT_MSEG_SBC_FREE(allctr, crr_sz, blk_sz); } else @@ -2378,6 +2398,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk) } else { + is_mbc = 1; ASSERT(IS_MBC_FIRST_FBLK(allctr, blk)); crr = FIRST_BLK_TO_MBC(allctr, blk); crr_sz = CARRIER_SZ(crr); @@ -2397,7 +2418,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk) #if HAVE_ERTS_MSEG if (IS_MSEG_CARRIER(crr)) { is_mseg++; - ASSERT(crr_sz % MSEG_UNIT_SZ == 0); + ASSERT(crr_sz % ERTS_SACRR_UNIT_SZ == 0); STAT_MSEG_MBC_FREE(allctr, crr_sz); mseg_flags = ERTS_MSEG_FLG_2POW; } @@ -2417,7 +2438,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk) } else #endif - alcu_sys_free(allctr, crr); + alcu_sys_free(allctr, crr, is_mbc); } @@ -3833,7 +3854,7 @@ do_erts_alcu_realloc(ErtsAlcType_t type, crr_sz = SYS_ALLOC_CARRIER_CEILING(used_sz); #if HAVE_ERTS_MSEG else - crr_sz = MSEG_UNIT_CEILING(used_sz); + crr_sz = ERTS_SACRR_UNIT_CEILING(used_sz); #endif diff_sz_val = crr_sz - used_sz; if (diff_sz_val < (~((Uint) 0) / 100)) @@ -4175,7 +4196,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) allctr->mbc_move_threshold = init->rmbcmt; #if HAVE_ERTS_MSEG allctr->max_mseg_sbcs = init->mmsbc; -# if HAVE_SUPER_ALIGNED_MB_CARRIERS +# if ERTS_SUPER_ALIGNED_MSEG_ONLY allctr->max_mseg_mbcs = ~(Uint)0; # else allctr->max_mseg_mbcs = init->mmmbc; @@ -4300,7 +4321,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) allctr->main_carrier_size, CFLG_MBC | CFLG_FORCE_SIZE -#if !HALFWORD_HEAP && !HAVE_SUPER_ALIGNED_MB_CARRIERS +#if !HALFWORD_HEAP && !ERTS_SUPER_ALIGNED_MSEG_ONLY | CFLG_FORCE_SYS_ALLOC #endif | CFLG_MAIN_CARRIER); @@ -4369,9 +4390,9 @@ erts_alcu_init(AlcUInit_t *init) { ASSERT(SBC_BLK_SZ_MASK == MBC_FBLK_SZ_MASK); /* see BLK_SZ */ #if HAVE_ERTS_MSEG - ASSERT(erts_mseg_unit_size() == MSEG_UNIT_SZ); + ASSERT(erts_mseg_unit_size() == ERTS_SACRR_UNIT_SZ); max_mseg_carriers = init->mmc; - sys_alloc_carrier_size = MSEG_UNIT_CEILING(init->ycs); + sys_alloc_carrier_size = ERTS_SACRR_UNIT_CEILING(init->ycs); #else /* #if HAVE_ERTS_MSEG */ sys_alloc_carrier_size = ((init->ycs + 4095) / 4096) * 4096; #endif @@ -4493,7 +4514,7 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk) ASSERT(CARRIER_SZ(sbc) - SBC_HEADER_SIZE >= SBC_BLK_SZ(iblk)); #if HAVE_ERTS_MSEG if (IS_MSEG_CARRIER(sbc)) { - ASSERT(CARRIER_SZ(sbc) % MSEG_UNIT_SZ == 0); + ASSERT(CARRIER_SZ(sbc) % ERTS_SACRR_UNIT_SZ == 0); } #endif crr = sbc; @@ -4578,7 +4599,7 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk) #if HAVE_ERTS_MSEG if (IS_MSEG_CARRIER(crr)) { - ASSERT(CARRIER_SZ(crr) % MSEG_UNIT_SZ == 0); + ASSERT(CARRIER_SZ(crr) % ERTS_SACRR_UNIT_SZ == 0); } #endif cl = &allctr->mbc_list; diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index e0754e7f69..e975dc34a7 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -76,7 +76,7 @@ typedef struct { #define ERTS_DEFAULT_ALCU_INIT { \ 1024*1024, /* (bytes) ycs: sys_alloc carrier size */\ - 1024 /* (amount) mmc: max mseg carriers */\ + ~((UWord) 0) /* (amount) mmc: max mseg carriers */ \ } #define ERTS_DEFAULT_ALLCTR_INIT { \ @@ -96,7 +96,7 @@ typedef struct { 50, /* (%) rmbcmt: rel mbc move threshold */\ 1024*1024, /* (bytes) mmbcs: main multiblock carrier size */\ 256, /* (amount) mmsbc: max mseg sbcs */\ - 10, /* (amount) mmmbc: max mseg mbcs */\ + ~((UWord) 0), /* (amount) mmmbc: max mseg mbcs */ \ 10*1024*1024, /* (bytes) lmbcs: largest mbc size */\ 1024*1024, /* (bytes) smbcs: smallest mbc size */\ 10, /* (amount) mbcgs: mbc growth stages */\ @@ -130,7 +130,7 @@ typedef struct { 80, /* (%) rsbcmt: rel sbc move threshold */\ 128*1024, /* (bytes) mmbcs: main multiblock carrier size */\ 256, /* (amount) mmsbc: max mseg sbcs */\ - 10, /* (amount) mmmbc: max mseg mbcs */\ + ~((UWord) 0), /* (amount) mmmbc: max mseg mbcs */ \ 1024*1024, /* (bytes) lmbcs: largest mbc size */\ 128*1024, /* (bytes) smbcs: smallest mbc size */\ 10, /* (amount) mbcgs: mbc growth stages */\ @@ -221,18 +221,29 @@ erts_aint32_t erts_alcu_fix_alloc_shrink(Allctr_t *, erts_aint32_t); #define MBC_FBLK_SZ_MASK UNIT_MASK #define CARRIER_SZ_MASK UNIT_MASK - -#if HAVE_ERTS_MSEG +#if ERTS_HAVE_MSEG_SUPER_ALIGNED \ + || (!HAVE_ERTS_MSEG && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC) +# ifndef MSEG_ALIGN_BITS +# define ERTS_SUPER_ALIGN_BITS MSEG_ALIGN_BITS +# else +# define ERTS_SUPER_ALIGN_BITS 18 +# endif # ifdef ARCH_64 # define MBC_ABLK_OFFSET_BITS 24 -# elif HAVE_SUPER_ALIGNED_MB_CARRIERS +# else # define MBC_ABLK_OFFSET_BITS 9 /* Affects hard limits for sbct and lmbcs documented in erts_alloc.xml */ # endif -#endif -#ifndef MBC_ABLK_OFFSET_BITS +# define ERTS_SA_MB_CARRIERS 1 +#else +# define ERTS_SA_MB_CARRIERS 0 # define MBC_ABLK_OFFSET_BITS 0 /* no carrier offset in block header */ #endif +#if ERTS_HAVE_MSEG_SUPER_ALIGNED && !ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC +# define ERTS_SUPER_ALIGNED_MSEG_ONLY 1 +#else +# define ERTS_SUPER_ALIGNED_MSEG_ONLY 0 +#endif #if MBC_ABLK_OFFSET_BITS # define MBC_ABLK_OFFSET_SHIFT (sizeof(UWord)*8 - MBC_ABLK_OFFSET_BITS) diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 05bff430e3..096394b878 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -1012,6 +1012,9 @@ void erl_bin_write(unsigned char *, int, int); #define ERTS_SMALL_ABS(Small) labs(Small) #endif +#ifndef ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC +# define ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC 0 +#endif #ifdef __WIN32__ void call_break_handler(void); diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index 3cab9e18da..127576b850 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -32,13 +32,13 @@ #if HAVE_MMAP # define HAVE_ERTS_MSEG 1 -# define HAVE_SUPER_ALIGNED_MB_CARRIERS 1 +# define ERTS_HAVE_MSEG_SUPER_ALIGNED 1 #else # define HAVE_ERTS_MSEG 0 -# define HAVE_SUPER_ALIGNED_MB_CARRIERS 0 +# define ERTS_HAVE_MSEG_SUPER_ALIGNED 0 #endif -#if HAVE_SUPER_ALIGNED_MB_CARRIERS +#if ERTS_HAVE_MSEG_SUPER_ALIGNED # define MSEG_ALIGN_BITS (18) /* Affects hard limits for sbct and lmbcs documented in erts_alloc.xml */ #else diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index c8fcec8547..2c47aa06c2 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -107,6 +107,10 @@ #endif #include +#ifdef HAVE_POSIX_MEMALIGN +# define ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC 1 +#endif + /* * Make sure that MAXPATHLEN is defined. */ diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index a7ea4b2490..fdc3167c62 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -2524,6 +2524,52 @@ void erts_sys_alloc_init(void) { } +#if ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC +void *erts_sys_aligned_alloc(UWord alignment, UWord size) +{ +#ifdef HAVE_POSIX_MEMALIGN + void *ptr = NULL; + int error; + ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + error = posix_memalign(&ptr, (size_t) alignment, (size_t) size); +#if HAVE_ERTS_MSEG + if (error || !ptr) { + erts_mseg_clear_cache(); + error = posix_memalign(&ptr, (size_t) alignment, (size_t) size); + } +#endif + if (error) { + errno = error; + return NULL; + } + if (!ptr) + errno = ENOMEM; + ASSERT(!ptr || (((UWord) ptr) & (alignment - 1)) == 0); + return ptr; +#else +# error "Missing erts_sys_aligned_alloc() implementation" +#endif +} + +void erts_sys_aligned_free(UWord alignment, void *ptr) +{ + ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + free(ptr); +} + +void *erts_sys_aligned_realloc(UWord alignment, void *ptr, UWord size, UWord old_size) +{ + void *new_ptr = erts_sys_aligned_alloc(alignment, size); + if (new_ptr) { + UWord copy_size = old_size < size ? old_size : size; + sys_memcpy(new_ptr, ptr, (size_t) copy_size); + erts_sys_aligned_free(alignment, ptr); + } + return new_ptr; +} + +#endif + void *erts_sys_alloc(ErtsAlcType_t t, void *x, Uint sz) { void *res = malloc((size_t) sz); diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 5ce1a61303..c44c5d9492 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -95,6 +95,8 @@ # define ERTS_I64_LITERAL(X) X##i64 #endif +#define ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC 1 + /* * Practial Windows specific macros. */ diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index e0422de026..8244a43583 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -32,7 +32,7 @@ #include "erl_threads.h" #include "../../drivers/win32/win_con.h" #include "erl_cpu_topology.h" - +#include void erts_sys_init_float(void); @@ -2906,6 +2906,30 @@ void erts_sys_free(ErtsAlcType_t t, void *x, void *p) free(p); } +void *erts_sys_aligned_alloc(UWord alignment, UWord size) +{ + void *ptr; + ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + ptr = _aligned_malloc((size_t) size, (size_t) alignment); + ASSERT(!ptr || (((UWord) ptr) & (alignment - 1)) == 0); + return ptr; +} + +void erts_sys_aligned_free(UWord alignment, void *ptr) +{ + ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + _aligned_free(ptr); +} + +void *erts_sys_aligned_realloc(UWord alignment, void *ptr, UWord size, UWord old_size) +{ + void *new_ptr; + ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + new_ptr = _aligned_realloc(ptr, (size_t) size, (size_t) alignment); + ASSERT(!new_ptr || (((UWord) new_ptr) & (alignment - 1)) == 0); + return new_ptr; +} + static Preload* preloaded = NULL; static unsigned* res_name = NULL; static int num_preloaded = 0; -- cgit v1.2.3 From abac2eda110a33d8310c0f9cc152d91de37f731d Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 27 Sep 2013 17:16:02 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 24 ++++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 77ffeefd04..b25e4ccbec 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,30 @@

This document describes the changes made to the ERTS application.

+
Erts 5.10.3.1 + +
Improvements and New Features + + +

+ Memory allocators will be able to create sys_alloc + carriers as fallback, if mseg_alloc cannot create + more carriers, on systems with posix_memalign() + support. This is similar to how it worked in pre-R16 + releases.

+

+ Windows systems will create carriers using + _aligned_malloc() and can by this use the new + optimized allocator header scheme introduced in R16 on + other platforms.

+

+ Own Id: OTP-11318

+
+
+
+ +
+
Erts 5.10.3
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index e235c50f0b..6ebdcdbc85 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 5.10.3 +VSN = 5.10.3.1 SYSTEM_VSN = R16B02 # Port number 4365 in 4.2 -- cgit v1.2.3 From 156b011958a3b80e507039ddc916db039874ada1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 27 Aug 2013 17:23:09 +0200 Subject: erts: Refactor the ASSERT macro Introduce unconditional ERTS_ASSERT and use that for both ASSERT and ASSERT_EXPR. --- erts/emulator/beam/dist.c | 4 ++-- erts/emulator/beam/erl_alloc_util.c | 3 --- erts/emulator/beam/erl_ao_firstfit_alloc.c | 3 --- erts/emulator/beam/erl_bestfit_alloc.c | 3 --- erts/emulator/beam/erl_bif_info.c | 2 +- erts/emulator/beam/erl_binary.h | 4 ++-- erts/emulator/beam/erl_db.c | 4 ++-- erts/emulator/beam/io.c | 2 +- erts/emulator/beam/sys.h | 16 ++++++---------- erts/emulator/drivers/common/inet_drv.c | 2 +- erts/emulator/sys/unix/sys.c | 4 ++-- 11 files changed, 17 insertions(+), 30 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 44f4eb9d43..aabccac822 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -353,7 +353,7 @@ static void doit_link_net_exits_sub(ErtsLink *sublnk, void *vlnecp) static void doit_link_net_exits(ErtsLink *lnk, void *vnecp) { LinkNetExitsContext lnec = {(NetExitsContext *) vnecp, lnk}; - ASSERT(lnk->type == LINK_PID) + ASSERT(lnk->type == LINK_PID); erts_sweep_links(ERTS_LINK_ROOT(lnk), &doit_link_net_exits_sub, (void *) &lnec); #ifdef DEBUG ERTS_LINK_ROOT(lnk) = NULL; @@ -369,7 +369,7 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp) Process *rp; ErtsLink *rlnk; Uint i,n; - ASSERT(lnk->type == LINK_NODE) + ASSERT(lnk->type == LINK_NODE); if (is_internal_pid(lnk->pid)) { ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK; rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks); diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 825b68bb85..3ea74a12f9 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -87,9 +87,6 @@ static int initialized = 0; #define SYS_ALLOC_CARRIER_CEILING(X) \ SYS_ALLOC_CARRIER_FLOOR((X) + INV_SYS_ALLOC_CARRIER_MASK) -#undef ASSERT -#define ASSERT ASSERT_EXPR - #if 0 /* Can be useful for debugging */ #define MBC_REALLOC_ALWAYS_MOVES diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c index 4e6c8b317e..396aa88e0b 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.c +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c @@ -85,9 +85,6 @@ #define SET_RED(N) (((AOFF_RBTree_t *) (N))->flags |= RED_FLG) #define SET_BLACK(N) (((AOFF_RBTree_t *) (N))->flags &= ~RED_FLG) -#undef ASSERT -#define ASSERT ASSERT_EXPR - #if 1 #define RBT_ASSERT ASSERT #else diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c index 41f449bb28..59c14899a2 100644 --- a/erts/emulator/beam/erl_bestfit_alloc.c +++ b/erts/emulator/beam/erl_bestfit_alloc.c @@ -75,9 +75,6 @@ #define BF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->hdr) -#undef ASSERT -#define ASSERT ASSERT_EXPR - #if 1 #define RBT_ASSERT ASSERT #else diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 3b25efd9af..a4f9f787cd 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2091,7 +2091,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(res); } else if (BIF_ARG_1 == am_sequential_tracer) { val = erts_get_system_seq_tracer(); - ASSERT(is_internal_pid(val) || is_internal_port(val) || val==am_false) + ASSERT(is_internal_pid(val) || is_internal_port(val) || val==am_false); hp = HAlloc(BIF_P, 3); res = TUPLE2(hp, am_sequential_tracer, val); BIF_RET(res); diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index 506c4813fa..331a12dc1c 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2011. All Rights Reserved. + * Copyright Ericsson AB 2000-2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -183,7 +183,7 @@ BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen); #endif #define ERTS_CHK_BIN_ALIGNMENT(B) \ - do { ASSERT(!(B) || (((UWord) &((Binary *)(B))->orig_bytes[0]) & ERTS_BIN_ALIGNMENT_MASK) == ((UWord) 0)) } while(0) + do { ASSERT(!(B) || (((UWord) &((Binary *)(B))->orig_bytes[0]) & ERTS_BIN_ALIGNMENT_MASK) == ((UWord) 0)); } while(0) ERTS_GLB_INLINE byte* erts_get_aligned_binary_bytes(Eterm bin, byte** base_ptr); ERTS_GLB_INLINE void erts_free_aligned_binary_bytes(byte* buf); diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 40b8eaf8fb..41e64fcd4f 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -2236,7 +2236,7 @@ static BIF_RETTYPE ets_select_trap_1(BIF_ALIST_1) CHECK_TABLES(); tptr = tuple_val(a1); - ASSERT(arityval(*tptr) >= 1) + ASSERT(arityval(*tptr) >= 1); if ((tb = db_get_table(p, tptr[1], DB_READ, kind)) == NULL) { BIF_ERROR(p, BADARG); @@ -2403,7 +2403,7 @@ static BIF_RETTYPE ets_select_count_1(BIF_ALIST_1) CHECK_TABLES(); tptr = tuple_val(a1); - ASSERT(arityval(*tptr) >= 1) + ASSERT(arityval(*tptr) >= 1); if ((tb = db_get_table(p, tptr[1], DB_READ, kind)) == NULL) { BIF_ERROR(p, BADARG); } diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index c1e66b59af..db19f6c142 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1330,7 +1330,7 @@ force_imm_drv_call(ErtsTryImmDrvCallState *sp) erts_aint32_t invalid_state; Port *prt = sp->port; - ASSERT(ERTS_IS_CRASH_DUMPING) + ASSERT(ERTS_IS_CRASH_DUMPING); ASSERT(is_atom(sp->port_op)); invalid_state = sp->state; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 096394b878..a20106749c 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -149,20 +149,16 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType; # define ERTS_EXIT_AFTER_DUMP exit #endif -#ifdef DEBUG -# define ASSERT(e) \ - if (e) { \ - ; \ - } else { \ - erl_assert_error(#e, __FILE__, __LINE__); \ - } -# define ASSERT_EXPR(e) \ +#define ERTS_ASSERT(e) \ ((void) ((e) ? 1 : (erl_assert_error(#e, __FILE__, __LINE__), 0))) void erl_assert_error(char* expr, char* file, int line); + +#ifdef DEBUG +# define ASSERT(e) ERTS_ASSERT(e) #else -# define ASSERT(e) -# define ASSERT_EXPR(e) ((void) 1) +# define ASSERT(e) ((void) 1) #endif +#define ASSERT_EXPR ASSERT /* * Microsoft C/C++: We certainly want to use stdarg.h and prototypes. diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 60db50e80a..12f45245b5 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -4433,7 +4433,7 @@ static ErlDrvSSizeT inet_ctl_getiflist(inet_descriptor* desc, case AF_INET6: #endif case AF_INET: - ASSERT(sp+IFNAMSIZ+1 < sbuf+ifc.ifc_len+1) + ASSERT(sp+IFNAMSIZ+1 < sbuf+ifc.ifc_len+1); strncpy(sp, ifrp->ifr_name, IFNAMSIZ); sp[IFNAMSIZ] = '\0'; sp += strlen(sp), ++sp; diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index fdc3167c62..47991756df 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -2638,8 +2638,6 @@ int fd; } -#ifdef DEBUG - extern int erts_initialized; void erl_assert_error(char* expr, char* file, int line) @@ -2661,6 +2659,8 @@ erl_assert_error(char* expr, char* file, int line) abort(); } +#ifdef DEBUG + void erl_debug(char* fmt, ...) { -- cgit v1.2.3 From ca1dc60a852c7827c2934ffeacefdd0119e2d776 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 27 Aug 2013 17:36:58 +0200 Subject: erts: Remove ASSERT_EXPR macro --- erts/emulator/beam/erl_alloc_util.h | 6 +++--- erts/emulator/beam/erl_binary.h | 2 +- erts/emulator/beam/erl_db_util.h | 2 +- erts/emulator/beam/erl_node_container_utils.h | 10 +++++----- erts/emulator/beam/erl_process.c | 2 +- erts/emulator/beam/erl_process.h | 4 ++-- erts/emulator/beam/erl_vm.h | 12 ++++++------ erts/emulator/beam/external.h | 4 ++-- erts/emulator/beam/global.h | 6 +++--- erts/emulator/beam/sys.h | 1 - 10 files changed, 24 insertions(+), 25 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index 70ecf28172..222f137024 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -254,9 +254,9 @@ erts_aint32_t erts_alcu_fix_alloc_shrink(Allctr_t *, erts_aint32_t); # define MBC_ABLK_SZ_MASK (~FLG_MASK) #endif -#define MBC_ABLK_SZ(B) (ASSERT_EXPR(!is_sbc_blk(B)), (B)->bhdr & MBC_ABLK_SZ_MASK) -#define MBC_FBLK_SZ(B) (ASSERT_EXPR(!is_sbc_blk(B)), (B)->bhdr & MBC_FBLK_SZ_MASK) -#define SBC_BLK_SZ(B) (ASSERT_EXPR(is_sbc_blk(B)), (B)->bhdr & SBC_BLK_SZ_MASK) +#define MBC_ABLK_SZ(B) (ASSERT(!is_sbc_blk(B)), (B)->bhdr & MBC_ABLK_SZ_MASK) +#define MBC_FBLK_SZ(B) (ASSERT(!is_sbc_blk(B)), (B)->bhdr & MBC_FBLK_SZ_MASK) +#define SBC_BLK_SZ(B) (ASSERT(is_sbc_blk(B)), (B)->bhdr & SBC_BLK_SZ_MASK) #define CARRIER_SZ(C) \ ((C)->chdr & CARRIER_SZ_MASK) diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index 331a12dc1c..f7dc20f5e6 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -153,7 +153,7 @@ do { \ #define binary_bytes(Bin) \ (*binary_val(Bin) == HEADER_PROC_BIN ? \ ((ProcBin *) binary_val(Bin))->bytes : \ - (ASSERT_EXPR(thing_subtag(*binary_val(Bin)) == HEAP_BINARY_SUBTAG), \ + (ASSERT(thing_subtag(*binary_val(Bin)) == HEAP_BINARY_SUBTAG), \ (byte *)(&(((ErlHeapBin *) binary_val(Bin))->data)))) void erts_init_binary(void); diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 90b79e6044..328b19dfc9 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -457,7 +457,7 @@ int erts_db_is_compiled_ms(Eterm term); && ERTS_MAGIC_BIN_DESTRUCTOR((BP)) == erts_db_match_prog_destructor) #define Binary2MatchProg(BP) \ - (ASSERT_EXPR(IsMatchProgBinary((BP))), \ + (ASSERT(IsMatchProgBinary((BP))), \ ((MatchProg *) ERTS_MAGIC_BIN_DATA((BP)))) /* ** Debugging diff --git a/erts/emulator/beam/erl_node_container_utils.h b/erts/emulator/beam/erl_node_container_utils.h index 0f93a3a9f0..17f6b32bb1 100644 --- a/erts/emulator/beam/erl_node_container_utils.h +++ b/erts/emulator/beam/erl_node_container_utils.h @@ -106,7 +106,7 @@ #define dist_entry_channel_no(x) \ ((x) == erts_this_dist_entry \ ? ((Uint) 0) \ - : (ASSERT_EXPR(is_atom((x)->sysname)), \ + : (ASSERT(is_atom((x)->sysname)), \ (Uint) atom_val((x)->sysname))) #define internal_channel_no(x) ((Uint) ERST_INTERNAL_CHANNEL_NO) #define external_channel_no(x) \ @@ -122,10 +122,10 @@ extern ErtsPTab erts_proc; (D), \ _TAG_IMMED1_PID) -#define internal_pid_index(PID) (ASSERT_EXPR(is_internal_pid((PID))), \ +#define internal_pid_index(PID) (ASSERT(is_internal_pid((PID))), \ erts_ptab_id2pix(&erts_proc, (PID))) -#define internal_pid_data(PID) (ASSERT_EXPR(is_internal_pid((PID))), \ +#define internal_pid_data(PID) (ASSERT(is_internal_pid((PID))), \ erts_ptab_id2data(&erts_proc, (PID))) #define internal_pid_number(x) _GET_PID_NUM(internal_pid_data((x))) @@ -193,10 +193,10 @@ extern ErtsPTab erts_port; (D), \ _TAG_IMMED1_PORT) -#define internal_port_index(PRT) (ASSERT_EXPR(is_internal_port((PRT))), \ +#define internal_port_index(PRT) (ASSERT(is_internal_port((PRT))), \ erts_ptab_id2pix(&erts_port, (PRT))) -#define internal_port_data(PRT) (ASSERT_EXPR(is_internal_port((PRT))), \ +#define internal_port_data(PRT) (ASSERT(is_internal_port((PRT))), \ erts_ptab_id2data(&erts_port, (PRT))) #define internal_port_number(x) _GET_PORT_NUM(internal_port_data((x))) diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 434d5ca147..79f382674a 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -294,7 +294,7 @@ ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(proclist, ERTS_ALC_T_PROC_LIST) #define ERTS_SCHED_SLEEP_INFO_IX(IX) \ - (ASSERT_EXPR(-1 <= ((int) (IX)) \ + (ASSERT(-1 <= ((int) (IX)) \ && ((int) (IX)) < ((int) erts_no_schedulers)), \ &aligned_sched_sleep_info[(IX)].ssi) diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 8e5467f196..8d136f6e8b 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1135,10 +1135,10 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; } while (0) #define ERTS_RUNQ_IX(IX) \ - (ASSERT_EXPR(0 <= (IX) && (IX) < erts_no_run_queues), \ + (ASSERT(0 <= (IX) && (IX) < erts_no_run_queues), \ &erts_aligned_run_queues[(IX)].runq) #define ERTS_SCHEDULER_IX(IX) \ - (ASSERT_EXPR(0 <= (IX) && (IX) < erts_no_schedulers), \ + (ASSERT(0 <= (IX) && (IX) < erts_no_schedulers), \ &erts_aligned_scheduler_data[(IX)].esd) void erts_pre_init_process(void); diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index c962955de9..8026243555 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2012. All Rights Reserved. + * Copyright Ericsson AB 1996-2013. All Rights Reserved. * * The 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,7 +98,7 @@ * failing that, in a heap fragment. */ #define HAllocX(p, sz, xtra) \ - (ASSERT_EXPR((sz) >= 0), \ + (ASSERT((sz) >= 0), \ ErtsHAllocLockCheck(p), \ (IS_FORCE_HEAP_FRAGS || (((HEAP_LIMIT(p) - HEAP_TOP(p)) < (sz))) \ ? erts_heap_alloc((p),(sz),(xtra)) \ @@ -135,14 +135,14 @@ */ #ifdef CHECK_FOR_HOLES # define HeapOnlyAlloc(p, sz) \ - (ASSERT_EXPR((sz) >= 0), \ - (ASSERT_EXPR(((HEAP_LIMIT(p) - HEAP_TOP(p)) >= (sz))), \ + (ASSERT((sz) >= 0), \ + (ASSERT(((HEAP_LIMIT(p) - HEAP_TOP(p)) >= (sz))), \ (erts_set_hole_marker(HEAP_TOP(p), (sz)), \ (HEAP_TOP(p) = HEAP_TOP(p) + (sz), HEAP_TOP(p) - (sz))))) #else # define HeapOnlyAlloc(p, sz) \ - (ASSERT_EXPR((sz) >= 0), \ - (ASSERT_EXPR(((HEAP_LIMIT(p) - HEAP_TOP(p)) >= (sz))), \ + (ASSERT((sz) >= 0), \ + (ASSERT(((HEAP_LIMIT(p) - HEAP_TOP(p)) >= (sz))), \ (HEAP_TOP(p) = HEAP_TOP(p) + (sz), HEAP_TOP(p) - (sz)))) #endif diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index e37d47919e..ff29e84972 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -138,8 +138,8 @@ typedef struct { #define ERTS_DIST_EXT_SIZE(EDEP) \ (sizeof(ErtsDistExternal) \ - (((EDEP)->flags & ERTS_DIST_EXT_ATOM_TRANS_TAB) \ - ? (ASSERT_EXPR(0 <= (EDEP)->attab.size \ - && (EDEP)->attab.size <= ERTS_ATOM_CACHE_SIZE), \ + ? (ASSERT(0 <= (EDEP)->attab.size \ + && (EDEP)->attab.size <= ERTS_ATOM_CACHE_SIZE), \ sizeof(Eterm)*(ERTS_ATOM_CACHE_SIZE - (EDEP)->attab.size)) \ : sizeof(ErtsAtomTranslationTable))) diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index bacd5a5752..063d16c0c7 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -866,13 +866,13 @@ Eterm store_external_or_ref_in_proc_(Process *, Eterm); Eterm store_external_or_ref_(Uint **, ErlOffHeap*, Eterm); #define NC_HEAP_SIZE(NC) \ - (ASSERT_EXPR(is_node_container((NC))), \ + (ASSERT(is_node_container((NC))), \ IS_CONST((NC)) ? 0 : (thing_arityval(*boxed_val((NC))) + 1)) #define STORE_NC(Hpp, ETpp, NC) \ - (ASSERT_EXPR(is_node_container((NC))), \ + (ASSERT(is_node_container((NC))), \ IS_CONST((NC)) ? (NC) : store_external_or_ref_((Hpp), (ETpp), (NC))) #define STORE_NC_IN_PROC(Pp, NC) \ - (ASSERT_EXPR(is_node_container((NC))), \ + (ASSERT(is_node_container((NC))), \ IS_CONST((NC)) ? (NC) : store_external_or_ref_in_proc_((Pp), (NC))) /* duplicates from big.h */ diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index a20106749c..97e6ed8410 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -158,7 +158,6 @@ void erl_assert_error(char* expr, char* file, int line); #else # define ASSERT(e) ((void) 1) #endif -#define ASSERT_EXPR ASSERT /* * Microsoft C/C++: We certainly want to use stdarg.h and prototypes. -- cgit v1.2.3 From c2dbcb69929ac18e7687f1df1de6613b34e2897b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 30 Aug 2013 11:59:49 +0200 Subject: erts: Prepare erl_mmap with tree structures for free seg storage --- erts/emulator/Makefile.in | 1 + erts/emulator/beam/erl_alloc.c | 37 ++ erts/emulator/beam/sys.h | 6 + erts/emulator/sys/common/erl_mmap.c | 972 ++++++++++++++++++++++++++++++++++++ erts/emulator/sys/common/erl_mmap.h | 26 + erts/emulator/sys/common/erl_mseg.c | 6 +- erts/emulator/sys/common/erl_mseg.h | 2 + erts/etc/common/erlexec.c | 3 + 8 files changed, 1052 insertions(+), 1 deletion(-) create mode 100644 erts/emulator/sys/common/erl_mmap.c create mode 100644 erts/emulator/sys/common/erl_mmap.h (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 9751982103..f442540f49 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -808,6 +808,7 @@ OS_OBJS += $(OBJDIR)/erl_poll.o \ endif OS_OBJS += $(OBJDIR)/erl_mseg.o \ + $(OBJDIR)/erl_mmap.o \ $(OBJDIR)/erl_$(ERLANG_OSTYPE)_sys_ddll.o \ $(OBJDIR)/erl_mtrace_sys_wrap.o \ $(OBJDIR)/erl_sys_common_misc.o diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 6dec383cee..2babe2f416 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1174,6 +1174,25 @@ get_kb_value(char *param_end, char** argv, int* ip) return ((Uint) tmp)*1024; } +static UWord +get_mb_value(char *param_end, char** argv, int* ip) +{ + SWord tmp; + UWord max = ((~((Uint) 0))/(1024*1024)) + 1; + char *rest; + char *param = argv[*ip]+1; + char *value = get_value(param_end, argv, ip); + errno = 0; + tmp = (SWord) ErtsStrToSint(value, &rest, 10); + if (errno != 0 || rest == value || tmp < 0 || max < ((UWord) tmp)) + bad_value(param, param_end, value); + if (max == (UWord) tmp) + return ~((UWord) 0); + else + return ((UWord) tmp)*1024*1024; +} + + #if 0 static Uint get_byte_value(char *param_end, char** argv, int* ip) @@ -1448,6 +1467,24 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) #endif get_amount_value(argv[i]+6, argv, &i); } + else if (has_prefix("scs", argv[i]+3)) { +#if HAVE_ERTS_MSEG + init->mseg.mmap.scs = +#endif + get_mb_value(argv[i]+6, argv, &i); + } + else if (has_prefix("sco", argv[i]+3)) { +#if HAVE_ERTS_MSEG + init->mseg.mmap.sco = +#endif + get_bool_value(argv[i]+6, argv, &i); + } + else if (has_prefix("scmgc", argv[i]+3)) { +#if HAVE_ERTS_MSEG + init->mseg.mmap.scmgc = +#endif + get_amount_value(argv[i]+8, argv, &i); + } else { bad_param(param, param+2); } diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 97e6ed8410..dfe60d8ea0 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -277,16 +277,19 @@ typedef unsigned long UWord; typedef long SWord; #define SWORD_CONSTANT(Const) Const##L #define UWORD_CONSTANT(Const) Const##UL +#define ERTS_SWORD_MAX LONG_MAX #elif SIZEOF_VOID_P == SIZEOF_INT typedef unsigned int UWord; typedef int SWord; #define SWORD_CONSTANT(Const) Const #define UWORD_CONSTANT(Const) Const##U +#define ERTS_SWORD_MAX INT_MAX #elif SIZEOF_VOID_P == SIZEOF_LONG_LONG typedef unsigned long long UWord; typedef long long SWord; #define SWORD_CONSTANT(Const) Const##LL #define UWORD_CONSTANT(Const) Const##ULL +#define ERTS_SWORD_MAX LLONG_MAX #else #error Found no appropriate type to use for 'Eterm', 'Uint' and 'Sint' #endif @@ -299,6 +302,7 @@ typedef unsigned long Uint; typedef long Sint; #define SWORD_CONSTANT(Const) Const##L #define UWORD_CONSTANT(Const) Const##UL +#define ERTS_SWORD_MAX LONG_MAX #define ERTS_SIZEOF_ETERM SIZEOF_LONG #define ErtsStrToSint strtol #elif SIZEOF_VOID_P == SIZEOF_INT @@ -307,6 +311,7 @@ typedef unsigned int Uint; typedef int Sint; #define SWORD_CONSTANT(Const) Const #define UWORD_CONSTANT(Const) Const##U +#define ERTS_SWORD_MAX INT_MAX #define ERTS_SIZEOF_ETERM SIZEOF_INT #define ErtsStrToSint strtol #elif SIZEOF_VOID_P == SIZEOF_LONG_LONG @@ -315,6 +320,7 @@ typedef unsigned long long Uint; typedef long long Sint; #define SWORD_CONSTANT(Const) Const##LL #define UWORD_CONSTANT(Const) Const##ULL +#define ERTS_SWORD_MAX LLONG_MAX #define ERTS_SIZEOF_ETERM SIZEOF_LONG_LONG #if defined(__WIN32__) #define ErtsStrToSint _strtoi64 diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c new file mode 100644 index 0000000000..a16ee7ae39 --- /dev/null +++ b/erts/emulator/sys/common/erl_mmap.c @@ -0,0 +1,972 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2002-2013. All Rights Reserved. + * + * The 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 +#include +#include + +#ifdef DEBUG +# define RBT_DEBUG +#endif +#ifdef RBT_DEBUG +# define RBT_ASSERT ERTS_ASSERT +# define IF_RBT_DEBUG(C) C +#else +# define RBT_ASSERT(x) +# define IF_RBT_DEBUG(C) +#endif + +typedef struct RBTNode_ RBTNode; +struct RBTNode_ { + RBTNode *parent; + RBTNode *left; + RBTNode *right; + int flags; +}; + +enum SortOrder { + ADDR_ORDER, + SZ_ADDR_ORDER, + SZ_REVERSE_ADDR_ORDER +}; + +typedef struct { + RBTNode* root; + enum SortOrder order; +}RBTree; + +#define RED_FLG (1) +#define IS_RED(N) ((N) && ((N)->flags & RED_FLG)) +#define IS_BLACK(N) (!IS_RED(N)) +#define SET_RED(N) ((N)->flags |= RED_FLG) +#define SET_BLACK(N) ((N)->flags &= ~RED_FLG) + +#define HARD_DEBUG /*SVERK*/ +#ifdef HARD_DEBUG +# define HARD_CHECK_IS_MEMBER(ROOT,NODE) rbt_assert_is_member(ROOT,NODE) +# define HARD_CHECK_TREE(TREE,SZ) check_tree(TREE, SZ) +static int rbt_assert_is_member(RBTNode* root, RBTNode* node); +static RBTNode* check_tree(RBTree* tree, Uint); +#else +# define HARD_CHECK_IS_MEMBER(ROOT,NODE) +# define HARD_CHECK_TREE(TREE,SZ) +#endif + + +typedef struct { + RBTNode snode; + RBTNode anode; + char* start; + char* end; +}ErtsFreeSegDesc; + +static ERTS_INLINE ErtsFreeSegDesc* anode_to_desc(RBTNode* anode) +{ + return (ErtsFreeSegDesc*) ((char*)anode - offsetof(ErtsFreeSegDesc, anode)); +} + +static ERTS_INLINE ErtsFreeSegDesc* snode_to_desc(RBTNode* snode) +{ + return (ErtsFreeSegDesc*) ((char*)snode - offsetof(ErtsFreeSegDesc, snode)); +} + +static ERTS_INLINE ErtsFreeSegDesc* node_to_desc(enum SortOrder order, RBTNode* node) +{ + return order==ADDR_ORDER ? anode_to_desc(node) : snode_to_desc(node); +} + +typedef struct { + RBTree stree; + RBTree atree; +}ErtsFreeSegMap; + + +#ifdef HARD_DEBUG +static ERTS_INLINE SWord cmp_blocks(enum SortOrder order, + RBTNode* lhs, RBTNode* rhs) +{ + ErtsFreeSegDesc* ldesc = node_to_desc(order, lhs); + ErtsFreeSegDesc* rdesc = node_to_desc(order, rhs); + RBT_ASSERT(lhs != rhs); + if (order != ADDR_ORDER) { + SWord lsz = ldesc->end - ldesc->start; + SWord rsz = rdesc->end - rdesc->start; + SWord diff = lsz - rsz; + if (diff) return diff; + } + if (order != SZ_REVERSE_ADDR_ORDER) { + return (char*)ldesc->start - (char*)rdesc->start; + } + else { + return (char*)rdesc->start - (char*)ldesc->start; + } +} +#endif + +static ERTS_INLINE SWord cmp_with_block(enum SortOrder order, + SWord sz, char* addr, RBTNode* rhs) +{ + ErtsFreeSegDesc* rdesc; + if (order != ADDR_ORDER) { + rdesc = snode_to_desc(rhs); + { + SWord rhs_sz = rdesc->end - rdesc->start; + SWord diff = sz - rhs_sz; + if (diff) return diff; + } + } + else + rdesc = anode_to_desc(rhs); + + if (order != SZ_REVERSE_ADDR_ORDER) + return addr - (char*)rdesc->start; + else + return (char*)rdesc->start - addr; +} + + +static ERTS_INLINE void +left_rotate(RBTNode **root, RBTNode *x) +{ + RBTNode *y = x->right; + x->right = y->left; + if (y->left) + y->left->parent = x; + y->parent = x->parent; + if (!y->parent) { + RBT_ASSERT(*root == x); + *root = y; + } + else if (x == x->parent->left) + x->parent->left = y; + else { + RBT_ASSERT(x == x->parent->right); + x->parent->right = y; + } + y->left = x; + x->parent = y; + + /*SVERK y->max_sz = x->max_sz; + x->max_sz = node_max_size(x); + ASSERT(y->max_sz >= x->max_sz);*/ +} + +static ERTS_INLINE void +right_rotate(RBTNode **root, RBTNode *x) +{ + RBTNode *y = x->left; + x->left = y->right; + if (y->right) + y->right->parent = x; + y->parent = x->parent; + if (!y->parent) { + RBT_ASSERT(*root == x); + *root = y; + } + else if (x == x->parent->right) + x->parent->right = y; + else { + RBT_ASSERT(x == x->parent->left); + x->parent->left = y; + } + y->right = x; + x->parent = y; + /*SVERK y->max_sz = x->max_sz; + x->max_sz = node_max_size(x); + ASSERT(y->max_sz >= x->max_sz);*/ +} + +/* + * Replace node x with node y + * NOTE: block header of y is not changed + */ +static ERTS_INLINE void +replace(RBTNode **root, RBTNode *x, RBTNode *y) +{ + + if (!x->parent) { + RBT_ASSERT(*root == x); + *root = y; + } + else if (x == x->parent->left) + x->parent->left = y; + else { + RBT_ASSERT(x == x->parent->right); + x->parent->right = y; + } + if (x->left) { + RBT_ASSERT(x->left->parent == x); + x->left->parent = y; + } + if (x->right) { + RBT_ASSERT(x->right->parent == x); + x->right->parent = y; + } + + y->flags = x->flags; + y->parent = x->parent; + y->right = x->right; + y->left = x->left; + /*SVERK y->max_sz = x->max_sz;*/ +} + +static void +tree_insert_fixup(RBTNode** root, RBTNode *blk) +{ + RBTNode *x = blk, *y; + + /* + * Rearrange the tree so that it satisfies the Red-Black Tree properties + */ + + RBT_ASSERT(x != *root && IS_RED(x->parent)); + do { + + /* + * x and its parent are both red. Move the red pair up the tree + * until we get to the root or until we can separate them. + */ + + RBT_ASSERT(IS_RED(x)); + RBT_ASSERT(IS_BLACK(x->parent->parent)); + RBT_ASSERT(x->parent->parent); + + if (x->parent == x->parent->parent->left) { + y = x->parent->parent->right; + if (IS_RED(y)) { + SET_BLACK(y); + x = x->parent; + SET_BLACK(x); + x = x->parent; + SET_RED(x); + } + else { + + if (x == x->parent->right) { + x = x->parent; + left_rotate(root, x); + } + + RBT_ASSERT(x == x->parent->parent->left->left); + RBT_ASSERT(IS_RED(x)); + RBT_ASSERT(IS_RED(x->parent)); + RBT_ASSERT(IS_BLACK(x->parent->parent)); + RBT_ASSERT(IS_BLACK(y)); + + SET_BLACK(x->parent); + SET_RED(x->parent->parent); + right_rotate(root, x->parent->parent); + + RBT_ASSERT(x == x->parent->left); + RBT_ASSERT(IS_RED(x)); + RBT_ASSERT(IS_RED(x->parent->right)); + RBT_ASSERT(IS_BLACK(x->parent)); + break; + } + } + else { + RBT_ASSERT(x->parent == x->parent->parent->right); + y = x->parent->parent->left; + if (IS_RED(y)) { + SET_BLACK(y); + x = x->parent; + SET_BLACK(x); + x = x->parent; + SET_RED(x); + } + else { + + if (x == x->parent->left) { + x = x->parent; + right_rotate(root, x); + } + + RBT_ASSERT(x == x->parent->parent->right->right); + RBT_ASSERT(IS_RED(x)); + RBT_ASSERT(IS_RED(x->parent)); + RBT_ASSERT(IS_BLACK(x->parent->parent)); + RBT_ASSERT(IS_BLACK(y)); + + SET_BLACK(x->parent); + SET_RED(x->parent->parent); + left_rotate(root, x->parent->parent); + + RBT_ASSERT(x == x->parent->right); + RBT_ASSERT(IS_RED(x)); + RBT_ASSERT(IS_RED(x->parent->left)); + RBT_ASSERT(IS_BLACK(x->parent)); + break; + } + } + } while (x != *root && IS_RED(x->parent)); + + SET_BLACK(*root); +} + +static void +rbt_delete(RBTNode** root, RBTNode* del) +{ + Uint spliced_is_black; + RBTNode *x, *y, *z = del; + RBTNode null_x; /* null_x is used to get the fixup started when we + splice out a node without children. */ + + HARD_CHECK_IS_MEMBER(*root, del); + + null_x.parent = NULL; + + /* Remove node from tree... */ + + /* Find node to splice out */ + if (!z->left || !z->right) + y = z; + else + /* Set y to z:s successor */ + for(y = z->right; y->left; y = y->left); + /* splice out y */ + x = y->left ? y->left : y->right; + spliced_is_black = IS_BLACK(y); + if (x) { + x->parent = y->parent; + } + else if (spliced_is_black) { + x = &null_x; + x->flags = 0; + SET_BLACK(x); + x->right = x->left = NULL; + /*SVERK x->max_sz = 0;*/ + x->parent = y->parent; + y->left = x; + } + + if (!y->parent) { + RBT_ASSERT(*root == y); + *root = x; + } + else { + if (y == y->parent->left) { + y->parent->left = x; + } + else { + RBT_ASSERT(y == y->parent->right); + y->parent->right = x; + } + /*SVERK if (y->parent != z) { + lower_max_size(y->parent, (y==z ? NULL : z)); + }*/ + } + if (y != z) { + /* We spliced out the successor of z; replace z by the successor */ + RBT_ASSERT(z != &null_x); + replace(root, z, y); + /*SVERK lower_max_size(y, NULL);*/ + } + + if (spliced_is_black) { + /* We removed a black node which makes the resulting tree + violate the Red-Black Tree properties. Fixup tree... */ + + while (IS_BLACK(x) && x->parent) { + + /* + * x has an "extra black" which we move up the tree + * until we reach the root or until we can get rid of it. + * + * y is the sibbling of x + */ + + if (x == x->parent->left) { + y = x->parent->right; + RBT_ASSERT(y); + if (IS_RED(y)) { + RBT_ASSERT(y->right); + RBT_ASSERT(y->left); + SET_BLACK(y); + RBT_ASSERT(IS_BLACK(x->parent)); + SET_RED(x->parent); + left_rotate(root, x->parent); + y = x->parent->right; + } + RBT_ASSERT(y); + RBT_ASSERT(IS_BLACK(y)); + if (IS_BLACK(y->left) && IS_BLACK(y->right)) { + SET_RED(y); + x = x->parent; + } + else { + if (IS_BLACK(y->right)) { + SET_BLACK(y->left); + SET_RED(y); + right_rotate(root, y); + y = x->parent->right; + } + RBT_ASSERT(y); + if (IS_RED(x->parent)) { + + SET_BLACK(x->parent); + SET_RED(y); + } + RBT_ASSERT(y->right); + SET_BLACK(y->right); + left_rotate(root, x->parent); + x = *root; + break; + } + } + else { + RBT_ASSERT(x == x->parent->right); + y = x->parent->left; + RBT_ASSERT(y); + if (IS_RED(y)) { + RBT_ASSERT(y->right); + RBT_ASSERT(y->left); + SET_BLACK(y); + RBT_ASSERT(IS_BLACK(x->parent)); + SET_RED(x->parent); + right_rotate(root, x->parent); + y = x->parent->left; + } + RBT_ASSERT(y); + RBT_ASSERT(IS_BLACK(y)); + if (IS_BLACK(y->right) && IS_BLACK(y->left)) { + SET_RED(y); + x = x->parent; + } + else { + if (IS_BLACK(y->left)) { + SET_BLACK(y->right); + SET_RED(y); + left_rotate(root, y); + y = x->parent->left; + } + RBT_ASSERT(y); + if (IS_RED(x->parent)) { + SET_BLACK(x->parent); + SET_RED(y); + } + RBT_ASSERT(y->left); + SET_BLACK(y->left); + right_rotate(root, x->parent); + x = *root; + break; + } + } + } + SET_BLACK(x); + + if (null_x.parent) { + if (null_x.parent->left == &null_x) + null_x.parent->left = NULL; + else { + RBT_ASSERT(null_x.parent->right == &null_x); + null_x.parent->right = NULL; + } + RBT_ASSERT(!null_x.left); + RBT_ASSERT(!null_x.right); + } + else if (*root == &null_x) { + *root = NULL; + RBT_ASSERT(!null_x.left); + RBT_ASSERT(!null_x.right); + } + } +} + + +static void +rbt_insert(enum SortOrder order, RBTNode** root, RBTNode* blk) +{ +#ifdef RBT_DEBUG + ErtsFreeSegDesc *dbg_under=NULL, *dbg_over=NULL; +#endif + ErtsFreeSegDesc* desc = node_to_desc(order, blk); + char* blk_addr = desc->start; + SWord blk_sz = desc->end - desc->start; + /*SVERK Uint blk_sz = AOFF_BLK_SZ(blk);*/ + + blk->flags = 0; + blk->left = NULL; + blk->right = NULL; + /*SVERK blk->max_sz = blk_sz;*/ + + if (!*root) { + blk->parent = NULL; + SET_BLACK(blk); + *root = blk; + } + else { + RBTNode *x = *root; + while (1) { + SWord diff; + /*SVERK if (x->max_sz < blk_sz) { + x->max_sz = blk_sz; + }*/ + diff = cmp_with_block(order, blk_sz, blk_addr, x); + if (diff < 0) { + IF_RBT_DEBUG(dbg_over = node_to_desc(order, x)); + if (!x->left) { + blk->parent = x; + x->left = blk; + break; + } + x = x->left; + } + else { + RBT_ASSERT(diff > 0); + IF_RBT_DEBUG(dbg_under = node_to_desc(order, x)); + if (!x->right) { + blk->parent = x; + x->right = blk; + break; + } + x = x->right; + } + /*SVERK else { + ASSERT(flavor == AOFF_BF); + ASSERT(blk->flags & IS_BF_FLG); + ASSERT(x->flags & IS_BF_FLG); + SET_LIST_ELEM(blk); + LIST_NEXT(blk) = LIST_NEXT(x); + LIST_PREV(blk) = x; + if (LIST_NEXT(x)) + LIST_PREV(LIST_NEXT(x)) = blk; + LIST_NEXT(x) = blk; + return; + }*/ + } + + /* Insert block into size tree */ + RBT_ASSERT(blk->parent); +#ifdef RBT_DEBUG + if (!order) { + RBT_ASSERT(!dbg_under || dbg_under->end < desc->start); + RBT_ASSERT(!dbg_over || dbg_over->start > desc->end); + } +#endif + SET_RED(blk); + if (IS_RED(blk->parent)) + tree_insert_fixup(root, blk); + } + /*SVERK if (flavor == AOFF_BF) { + SET_TREE_NODE(blk); + LIST_NEXT(blk) = NULL; + }*/ +} + + +/* The API to keep track of a bunch of separated free segments + (non-overlapping and non-adjacent). + */ +static void init_free_seg_map(ErtsFreeSegMap*, int reverse_ao); +static void adjacent_free_seg(ErtsFreeSegMap*, char* start, char* end, + ErtsFreeSegDesc** under, ErtsFreeSegDesc** over); +static void insert_free_seg(ErtsFreeSegMap*, ErtsFreeSegDesc*, char* start, char* end); +static void resize_free_seg(ErtsFreeSegMap*, ErtsFreeSegDesc*, char* start, char* end); +static void delete_free_seg(ErtsFreeSegMap*, ErtsFreeSegDesc*); +static ErtsFreeSegDesc* lookup_free_seg(ErtsFreeSegMap*, SWord sz); + + +static void init_free_seg_map(ErtsFreeSegMap* map, int reverse_ao) +{ + map->atree.root = NULL; + map->atree.order = ADDR_ORDER; + map->stree.root = NULL; + map->stree.order = reverse_ao ? SZ_REVERSE_ADDR_ORDER : SZ_ADDR_ORDER; +} + +static void adjacent_free_seg(ErtsFreeSegMap* map, char* start, char* end, + ErtsFreeSegDesc** under, ErtsFreeSegDesc** over) +{ + RBTNode* x = map->atree.root; + + *under = NULL; + *over = NULL; + while (x) { + if (start < anode_to_desc(x)->start) { + RBT_ASSERT(end <= anode_to_desc(x)->start); + if (end == anode_to_desc(x)->start) { + RBT_ASSERT(!*over); + *over = anode_to_desc(x); + } + x = x->left; + } + else { + RBT_ASSERT(start >= anode_to_desc(x)->end); + if (start == anode_to_desc(x)->end) { + RBT_ASSERT(!*under); + *under = anode_to_desc(x); + } + x = x->right; + } + } +} + +static void insert_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc, + char* start, char* end) +{ + desc->start = start; + desc->end = end; + rbt_insert(map->atree.order, &map->atree.root, &desc->anode); + rbt_insert(map->stree.order, &map->stree.root, &desc->snode); +} + +static void resize_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc, + char* start, char* end) +{ +#ifdef DEBUG + ErtsFreeSegDesc *dbg_under, *dbg_over; + rbt_delete(&map->atree.root, &desc->anode); + adjacent_free_seg(map, start, end, &dbg_under, &dbg_over); + RBT_ASSERT(dbg_under == NULL && dbg_over == NULL); + rbt_insert(map->atree.order, &map->atree.root, &desc->anode); +#endif + rbt_delete(&map->stree.root, &desc->snode); + desc->start = start; + desc->end = end; + rbt_insert(map->stree.order, &map->stree.root, &desc->snode); +} + +static void delete_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc) +{ + rbt_delete(&map->atree.root, &desc->anode); + rbt_delete(&map->stree.root, &desc->snode); +} + +static ErtsFreeSegDesc* lookup_free_seg(ErtsFreeSegMap* map, SWord need_sz) +{ + RBTNode* x = map->stree.root; + ErtsFreeSegDesc* best_desc = NULL; + + while (x) { + ErtsFreeSegDesc* desc = snode_to_desc(x); + SWord seg_sz = desc->end - desc->start; + + if (seg_sz < need_sz) { + x = x->right; + } + else { + best_desc = desc; + x = x->left; + } + } + return best_desc; +} + + +void erts_mmap_init(ErtsMMapInit* init) +{ +#ifdef HARD_DEBUG + erts_fprintf(stderr, "SVERK: scs = %bpu\n", init->scs); + erts_fprintf(stderr, "SVERK: sco = %i\n", init->sco); + erts_fprintf(stderr, "SVERK: scmgc = %i\n", init->scmgc); + + { + void test_it(void); + test_it(); + } +#endif +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Debug functions * +\* */ + + +#ifdef HARD_DEBUG + +static int rbt_assert_is_member(RBTNode* root, RBTNode* node) +{ + while (node != root) { + RBT_ASSERT(node->parent); + RBT_ASSERT(node->parent->left == node || node->parent->right == node); + node = node->parent; + } + return 1; +} + +#define LEFT_VISITED_FLG 0x1000 +#define THIS_VISITED_FLG 0x100 +#define RIGHT_VISITED_FLG 0x10 +#define IS_LEFT_VISITED(FB) ((FB)->flags & LEFT_VISITED_FLG) +#define IS_THIS_VISITED(FB) ((FB)->flags & THIS_VISITED_FLG) +#define IS_RIGHT_VISITED(FB) ((FB)->flags & RIGHT_VISITED_FLG) + +#define SET_LEFT_VISITED(FB) ((FB)->flags |= LEFT_VISITED_FLG) +#define SET_THIS_VISITED(FB) ((FB)->flags |= THIS_VISITED_FLG) +#define SET_RIGHT_VISITED(FB) ((FB)->flags |= RIGHT_VISITED_FLG) + +#define UNSET_LEFT_VISITED(FB) ((FB)->flags &= ~LEFT_VISITED_FLG) +#define UNSET_THIS_VISITED(FB) ((FB)->flags &= ~THIS_VISITED_FLG) +#define UNSET_RIGHT_VISITED(FB) ((FB)->flags &= ~RIGHT_VISITED_FLG) + + + +#if 1 /*SVERK*/ +# define PRINT_TREE +#else +# undef PRINT_TREE +#endif + +#ifdef PRINT_TREE +static void print_tree(enum SortOrder order, RBTNode*); +#endif + +/* + * Checks that the order between parent and children are correct, + * and that the Red-Black Tree properies are satisfied. if size > 0, + * check_tree() returns the node that satisfies "address order first fit" + * + * The Red-Black Tree properies are: + * 1. Every node is either red or black. + * 2. Every leaf (NIL) is black. + * 3. If a node is red, then both its children are black. + * 4. Every simple path from a node to a descendant leaf + * contains the same number of black nodes. + * + */ + +static RBTNode * +check_tree(RBTree* tree, Uint size) +{ + RBTNode *res = NULL; + Sint blacks; + Sint curr_blacks; + RBTNode *x; + Uint depth, max_depth, node_cnt; + ErtsFreeSegDesc* seg = NULL; + ErtsFreeSegDesc* prev_seg = NULL; + +#ifdef PRINT_TREE + print_tree(tree->order, tree->root); +#endif + + if (!tree->root) + return res; + + x = tree->root; + RBT_ASSERT(IS_BLACK(x)); + RBT_ASSERT(!x->parent); + curr_blacks = 1; + blacks = -1; + depth = 1; + max_depth = 0; + node_cnt = 0; + + /* Traverse tree in sorting order */ + while (x) { + if (!IS_LEFT_VISITED(x)) { + SET_LEFT_VISITED(x); + if (x->left) { + x = x->left; + ++depth; + if (IS_BLACK(x)) + curr_blacks++; + continue; + } + else { + if (blacks < 0) + blacks = curr_blacks; + RBT_ASSERT(blacks == curr_blacks); + } + } + + if (!IS_THIS_VISITED(x)) { + SET_THIS_VISITED(x); + ++node_cnt; + if (depth > max_depth) + max_depth = depth; + + if (IS_RED(x)) { + RBT_ASSERT(IS_BLACK(x->right)); + RBT_ASSERT(IS_BLACK(x->left)); + } + + RBT_ASSERT(x->parent || x == tree->root); + + if (x->left) { + RBT_ASSERT(x->left->parent == x); + RBT_ASSERT(cmp_blocks(tree->order, x->left, x) < 0); + } + + if (x->right) { + RBT_ASSERT(x->right->parent == x); + RBT_ASSERT(cmp_blocks(tree->order, x->right, x) > 0); + } + + seg = node_to_desc(tree->order, x); + RBT_ASSERT(seg->start < seg->end); + if (size && (seg->end - seg->start) >= size) { + if (!res || cmp_blocks(tree->order, x, res) < 0) { + res = x; + } + } + if (tree->order == ADDR_ORDER) { + RBT_ASSERT(!prev_seg || prev_seg->end < seg->start); + prev_seg = seg; + } + + } + if (!IS_RIGHT_VISITED(x)) { + SET_RIGHT_VISITED(x); + if (x->right) { + x = x->right; + ++depth; + if (IS_BLACK(x)) + curr_blacks++; + continue; + } + else { + if (blacks < 0) + blacks = curr_blacks; + RBT_ASSERT(blacks == curr_blacks); + } + } + + UNSET_LEFT_VISITED(x); + UNSET_THIS_VISITED(x); + UNSET_RIGHT_VISITED(x); + if (IS_BLACK(x)) + curr_blacks--; + x = x->parent; + --depth; + } + RBT_ASSERT(depth == 0 || (!tree->root && depth==1)); + RBT_ASSERT(curr_blacks == 0); + RBT_ASSERT((1 << (max_depth/2)) <= node_cnt); + + UNSET_LEFT_VISITED(tree->root); + UNSET_THIS_VISITED(tree->root); + UNSET_RIGHT_VISITED(tree->root); + + return res; +} + + +#ifdef PRINT_TREE +#define INDENT_STEP 2 + +#include + +static void +print_tree_aux(enum SortOrder order, RBTNode *x, int indent) +{ + int i; + + if (x) { + ErtsFreeSegDesc* desc = node_to_desc(order, x); + print_tree_aux(order, x->right, indent + INDENT_STEP); + for (i = 0; i < indent; i++) { + putc(' ', stderr); + } + fprintf(stderr, "%s: sz=%lx [%p - %p] desc=%p\r\n", + IS_BLACK(x) ? "BLACK" : "RED", + desc->end - desc->start, desc->start, desc->end, desc); + print_tree_aux(order, x->left, indent + INDENT_STEP); + } +} + + +static void +print_tree(enum SortOrder order, RBTNode* root) +{ + static const char* type[] = {"Address","Size-Address","Size-RevAddress"}; + fprintf(stderr, " --- %s ordered tree begin ---\r\n", type[order]); + print_tree_aux(order, root, 0); + fprintf(stderr, " --- %s ordered tree end ---\r\n", type[order]); +} + +#endif /* PRINT_TREE */ + + +static ErtsFreeSegDesc* new_desc(void) +{ + return (ErtsFreeSegDesc*) malloc(sizeof(ErtsFreeSegDesc)); +} + +void test_it(void) +{ + ErtsFreeSegMap map; + ErtsFreeSegDesc *desc, *under, *over, *d1, *d2; + int i; + + for (i=0; i<2; i++) { + init_free_seg_map(&map, i); + + insert_free_seg(&map, new_desc(), (char*)0x11000, (char*)0x12000); + check_tree(&map.atree, 0); check_tree(&map.stree, 0); + insert_free_seg(&map, new_desc(), (char*)0x13000, (char*)0x14000); + check_tree(&map.atree, 0); check_tree(&map.stree, 0); + insert_free_seg(&map, new_desc(), (char*)0x15000, (char*)0x17000); + check_tree(&map.atree, 0); check_tree(&map.stree, 0); + insert_free_seg(&map, new_desc(), (char*)0x8000, (char*)0x10000); + check_tree(&map.atree, 0); check_tree(&map.stree, 0); + + desc = lookup_free_seg(&map, 0x500); + ERTS_ASSERT(desc->start == (char*)(i?0x13000L:0x11000L)); + + desc = lookup_free_seg(&map, 0x1500); + ERTS_ASSERT(desc->start == (char*)0x15000); + + adjacent_free_seg(&map, (char*)0x6666, (char*)0x7777, &under, &over); + ERTS_ASSERT(!under && !over); + + adjacent_free_seg(&map, (char*)0x6666, (char*)0x8000, &under, &over); + ERTS_ASSERT(!under && over->start == (char*)0x8000); + + adjacent_free_seg(&map, (char*)0x10000, (char*)0x10500, &under, &over); + ERTS_ASSERT(under->end == (char*)0x10000 && !over); + + adjacent_free_seg(&map, (char*)0x10100, (char*)0x10500, &under, &over); + ERTS_ASSERT(!under && !over); + + adjacent_free_seg(&map, (char*)0x10100, (char*)0x11000, &under, &over); + ERTS_ASSERT(!under && over && over->start == (char*)0x11000); + + adjacent_free_seg(&map, (char*)0x12000, (char*)0x12500, &under, &over); + ERTS_ASSERT(under && under->end == (char*)0x12000 && !over); + + adjacent_free_seg(&map, (char*)0x12000, (char*)0x13000, &under, &over); + ERTS_ASSERT(under && under->end == (char*)0x12000 && + over && over->start == (char*)0x13000); + + adjacent_free_seg(&map, (char*)0x12500, (char*)0x13000, &under, &over); + ERTS_ASSERT(!under && over && over->start == (char*)0x13000); + + d1 = lookup_free_seg(&map, 0x500); + ERTS_ASSERT(d1->start == (char*)(i?0x13000L:0x11000L)); + + resize_free_seg(&map, d1, d1->start - 0x800, (char*)d1->end); + check_tree(&map.atree, 0); check_tree(&map.stree, 0); + + d2 = lookup_free_seg(&map, 0x1200); + ERTS_ASSERT(d2 == d1); + + delete_free_seg(&map, d1); + check_tree(&map.atree, 0); check_tree(&map.stree, 0); + + d1 = lookup_free_seg(&map, 0x1200); + ERTS_ASSERT(d1->start == (char*)0x15000); + } +} + +#endif /* HARD_DEBUG */ diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h new file mode 100644 index 0000000000..64baa6c493 --- /dev/null +++ b/erts/emulator/sys/common/erl_mmap.h @@ -0,0 +1,26 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2002-2013. All Rights Reserved. + * + * The 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% + */ + +typedef struct { + SWord scs; /* super carrier size */ + int sco; /* super carrier only? */ + Uint scmgc; /* super carrier: max guaranteed (number of) carriers */ +}ErtsMMapInit; + +void erts_mmap_init(ErtsMMapInit*); diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 2748edba02..64fcb6bb40 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -1668,8 +1668,12 @@ erts_mseg_init(ErtsMsegInit_t *init) erl_exit(ERTS_ABORT_EXIT, "erts_mseg: unable to open /dev/zero\n"); #endif -#if HAVE_MMAP && HALFWORD_HEAP +#if HAVE_MMAP +# if HALFWORD_HEAP initialize_pmmap(); +# else + erts_mmap_init(&init->mmap); +# endif #endif if (!IS_2POW(GET_PAGE_SIZE)) diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index a1b000f51c..7454e5c473 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -22,6 +22,7 @@ #include "sys.h" #include "erl_alloc_types.h" +#include "erl_mmap.h" #ifndef HAVE_MMAP # define HAVE_MMAP 0 @@ -68,6 +69,7 @@ typedef struct { Uint rmcbf; Uint mcs; Uint nos; + ErtsMMapInit mmap; } ErtsMsegInit_t; #define ERTS_MSEG_INIT_DEFAULT_INITIALIZER \ diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 30560f5a2f..1e01c5bc20 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -109,6 +109,9 @@ static char *plusM_other_switches[] = { "Mamcbf", "Mrmcbf", "Mmcs", + "Mscs", + "Mscmgc", + "Msco", "Ye", "Ym", "Ytp", -- cgit v1.2.3 From ef3da907bd566b43a4022f1cbb1ae3d103b9ec3e Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 23 Aug 2013 17:29:58 +0200 Subject: erts: erts_mmap supercarrier management and erts_mseg usage * Coalescing and trimming of free segments in supercarrier * Management of super aligned and super unaligned areas in supercarrier * Management of reservation of physical memory * erts_mseg usage of erts_mmap --- erts/emulator/beam/erl_alloc.c | 9 +- erts/emulator/beam/erl_alloc_util.c | 17 +- erts/emulator/beam/erl_lock_check.c | 3 +- erts/emulator/beam/erl_unicode.c | 3 + erts/emulator/beam/erl_vm.h | 2 +- erts/emulator/sys/common/erl_mmap.c | 1120 ++++++++++++++++++++++++++++++++++- erts/emulator/sys/common/erl_mmap.h | 89 ++- erts/emulator/sys/common/erl_mseg.c | 860 +++++---------------------- erts/emulator/sys/common/erl_mseg.h | 34 +- erts/etc/common/erlexec.c | 1 + 10 files changed, 1379 insertions(+), 759 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 2babe2f416..e30b3e7b51 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -718,6 +718,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) init.mseg.nos = erts_no_schedulers; erts_mseg_init(&init.mseg); #endif + erts_alcu_init(&init.alloc_util); erts_afalc_init(); erts_bfalc_init(); @@ -1178,7 +1179,7 @@ static UWord get_mb_value(char *param_end, char** argv, int* ip) { SWord tmp; - UWord max = ((~((Uint) 0))/(1024*1024)) + 1; + UWord max = ((~((UWord) 0))/(1024*1024)) + 1; char *rest; char *param = argv[*ip]+1; char *value = get_value(param_end, argv, ip); @@ -1479,6 +1480,12 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) #endif get_bool_value(argv[i]+6, argv, &i); } + else if (has_prefix("scrpm", argv[i]+3)) { +#if HAVE_ERTS_MSEG + init->mseg.mmap.scrpm = +#endif + get_bool_value(argv[i]+8, argv, &i); + } else if (has_prefix("scmgc", argv[i]+3)) { #if HAVE_ERTS_MSEG init->mseg.mmap.scmgc = diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 3ea74a12f9..1fdee4db2c 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -756,8 +756,9 @@ static ERTS_INLINE void * alcu_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) { void *res; - - res = erts_mseg_alloc_opt(allctr->alloc_no, size_p, flags, &allctr->mseg_opt); + UWord size = (UWord) *size_p; + res = erts_mseg_alloc_opt(allctr->alloc_no, &size, flags, &allctr->mseg_opt); + *size_p = (Uint) size; INC_CC(allctr->calls.mseg_alloc); return res; } @@ -766,9 +767,10 @@ static ERTS_INLINE void * alcu_mseg_realloc(Allctr_t *allctr, void *seg, Uint old_size, Uint *new_size_p) { void *res; - - res = erts_mseg_realloc_opt(allctr->alloc_no, seg, old_size, new_size_p, + UWord new_size = (UWord) *new_size_p; + res = erts_mseg_realloc_opt(allctr->alloc_no, seg, (UWord) old_size, &new_size, ERTS_MSEG_FLG_NONE, &allctr->mseg_opt); + *new_size_p = (Uint) new_size; INC_CC(allctr->calls.mseg_realloc); return res; } @@ -776,7 +778,7 @@ alcu_mseg_realloc(Allctr_t *allctr, void *seg, Uint old_size, Uint *new_size_p) static ERTS_INLINE void alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags) { - erts_mseg_dealloc_opt(allctr->alloc_no, seg, size, flags, &allctr->mseg_opt); + erts_mseg_dealloc_opt(allctr->alloc_no, seg, (UWord) size, flags, &allctr->mseg_opt); INC_CC(allctr->calls.mseg_dealloc); } @@ -3223,10 +3225,12 @@ static void CHECK_1BLK_CARRIER(Allctr_t* A, int SBC, int MSEGED, Carrier_t* C, ASSERT(IS_MBC_BLK((B))); ASSERT(IS_MB_CARRIER((C))); ASSERT(FBLK_TO_MBC(B) == (C)); + if ((MSEGED)) { + ASSERT_ERTS_SACRR_UNIT_SIZE_MULTIPLE((CSZ)); + } } if ((MSEGED)) { ASSERT(IS_MSEG_CARRIER((C))); - ASSERT_ERTS_SACRR_UNIT_SIZE_MULTIPLE((CSZ)); } else { ASSERT(IS_SYS_ALLOC_CARRIER((C))); @@ -3598,7 +3602,6 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk, Carrier_t **busy_pcrr_pp) #if HAVE_ERTS_MSEG if (IS_MSEG_CARRIER(crr)) { - ASSERT(crr_sz % ERTS_SACRR_UNIT_SZ == 0); STAT_MSEG_SBC_FREE(allctr, crr_sz, blk_sz); } else diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 2114d0c001..1e9cef3759 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -185,7 +185,8 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "sys_gethrtime", NULL }, #endif #endif - { "erts_alloc_hard_debug", NULL } + { "erts_alloc_hard_debug", NULL }, + { "erts_mmap", NULL } }; #define ERTS_LOCK_ORDER_SIZE \ diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index e00440b905..569c0a7d31 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -1476,6 +1476,9 @@ static Eterm do_utf8_to_list_normalize(Process *p, Uint num, byte *bytes, Uint s Uint16 savepoints[4]; int numpoints = 0; + if (num == 0) + return NIL; + ASSERT(num > 0); hp = HAlloc(p,num * 2); /* May be to much */ diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 8026243555..337422eead 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -80,7 +80,7 @@ # ifdef CHECK_FOR_HOLES # define INIT_HEAP_MEM(p,sz) erts_set_hole_marker(HEAP_TOP(p), (sz)) # else -# define INIT_HEAP_MEM(p,sz) memset(HEAP_TOP(p),DEBUG_BAD_BYTE,(sz)*sizeof(Eterm*)) +# define INIT_HEAP_MEM(p,sz) memset(HEAP_TOP(p),0x01,(sz)*sizeof(Eterm*)) # endif #else # define INIT_HEAP_MEM(p,sz) ((void)0) diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index a16ee7ae39..aac01bf93c 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -20,11 +20,57 @@ # include "config.h" #endif -#include -#include +#include "sys.h" #include +#include "erl_smp.h" +#include "erl_mmap.h" -#ifdef DEBUG +#if defined(DEBUG) || 0 +# undef ERTS_MMAP_DEBUG +# define ERTS_MMAP_DEBUG +#endif + +/* #define ERTS_MMAP_DEBUG_FILL_AREAS */ + +#ifdef ERTS_MMAP_DEBUG +# define ERTS_MMAP_ASSERT(A) \ + ((void) (!(A) \ + ? erts_mmap_assert_failed(#A, __func__, __FILE__, __LINE__)\ + : 1)) +static int +erts_mmap_assert_failed(const char *a, const char *func, const char *file, int line) +{ + erts_fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", + (char *) file, line, (char *) func, (char *) a); + abort(); + return 0; +} +#else +# define ERTS_MMAP_ASSERT(A) ((void) 1) +#endif + +/* + * `mmap_state.sa.bot` and `mmap_state.sua.top` are read only after + * initialization, but the other pointers are not; i.e., only + * ERTS_MMAP_IN_SUPERCARRIER() is allowed without the mutex held. + */ +#define ERTS_MMAP_IN_SUPERCARRIER(PTR) \ + (((UWord) (PTR)) - ((UWord) mmap_state.sa.bot) \ + < ((UWord) mmap_state.sua.top) - ((UWord) mmap_state.sa.bot)) +#define ERTS_MMAP_IN_SUPERALIGNED_AREA(PTR) \ + (ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&mmap_state.mtx)), \ + (((UWord) (PTR)) - ((UWord) mmap_state.sa.bot) \ + < ((UWord) mmap_state.sa.top) - ((UWord) mmap_state.sa.bot))) +#define ERTS_MMAP_IN_SUPERUNALIGNED_AREA(PTR) \ + (ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&mmap_state.mtx)), \ + (((UWord) (PTR)) - ((UWord) mmap_state.sua.bot) \ + < ((UWord) mmap_state.sua.top) - ((UWord) mmap_state.sua.bot))) + +int erts_have_erts_mmap; +UWord erts_page_inv_mask; + +#if defined(DEBUG) || defined(ERTS_MMAP_DEBUG) +# undef RBT_DEBUG # define RBT_DEBUG #endif #ifdef RBT_DEBUG @@ -60,7 +106,7 @@ typedef struct { #define SET_RED(N) ((N)->flags |= RED_FLG) #define SET_BLACK(N) ((N)->flags &= ~RED_FLG) -#define HARD_DEBUG /*SVERK*/ +/* #define HARD_DEBUG */ #ifdef HARD_DEBUG # define HARD_CHECK_IS_MEMBER(ROOT,NODE) rbt_assert_is_member(ROOT,NODE) # define HARD_CHECK_TREE(TREE,SZ) check_tree(TREE, SZ) @@ -79,6 +125,134 @@ typedef struct { char* end; }ErtsFreeSegDesc; +typedef struct { + RBTree stree; + RBTree atree; +}ErtsFreeSegMap; + +static struct { + int (*reserve_physical)(char *, UWord); + void (*unreserve_physical)(char *, UWord); + int supercarrier; + int no_os_mmap; + /* + * Super unaligend area is located above super aligned + * area. That is, `sa.bot` is beginning of the super + * carrier, `sua.top` is the end of the super carrier, + * and sa.top and sua.bot moves towards eachother. + */ + struct { + char *top; + char *bot; + ErtsFreeSegMap map; + } sua; + struct { + char *top; + char *bot; + ErtsFreeSegMap map; + } sa; +#if HAVE_MMAP && (!defined(MAP_ANON) && !defined(MAP_ANONYMOUS)) + int mmap_fd; +#endif + erts_smp_mtx_t mtx; + char *desc_free_list; + struct { + struct { + UWord total; + struct { + UWord total; + UWord sa; + UWord sua; + } used; + } supercarrier; + struct { + UWord used; + } os; + } size; +} mmap_state; + +#define ERTS_MMAP_SIZE_SC_SA_INC(SZ) \ + do { \ + mmap_state.size.supercarrier.used.total += (SZ); \ + mmap_state.size.supercarrier.used.sa += (SZ); \ + ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total \ + <= mmap_state.size.supercarrier.total); \ + ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sa \ + <= mmap_state.size.supercarrier.used.total); \ + } while (0) +#define ERTS_MMAP_SIZE_SC_SA_DEC(SZ) \ + do { \ + ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total >= (SZ)); \ + mmap_state.size.supercarrier.used.total -= (SZ); \ + ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sa >= (SZ)); \ + mmap_state.size.supercarrier.used.sa -= (SZ); \ + } while (0) +#define ERTS_MMAP_SIZE_SC_SUA_INC(SZ) \ + do { \ + mmap_state.size.supercarrier.used.total += (SZ); \ + mmap_state.size.supercarrier.used.sua += (SZ); \ + ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total \ + <= mmap_state.size.supercarrier.total); \ + ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sua \ + <= mmap_state.size.supercarrier.used.total); \ + } while (0) +#define ERTS_MMAP_SIZE_SC_SUA_DEC(SZ) \ + do { \ + ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total >= (SZ)); \ + mmap_state.size.supercarrier.used.total -= (SZ); \ + ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sua >= (SZ)); \ + mmap_state.size.supercarrier.used.sua -= (SZ); \ + } while (0) +#define ERTS_MMAP_SIZE_OS_INC(SZ) \ + do { \ + ERTS_MMAP_ASSERT(mmap_state.size.os.used + (SZ) >= (SZ)); \ + mmap_state.size.os.used += (SZ); \ + } while (0) +#define ERTS_MMAP_SIZE_OS_DEC(SZ) \ + do { \ + ERTS_MMAP_ASSERT(mmap_state.size.os.used >= (SZ)); \ + mmap_state.size.os.used -= (SZ); \ + } while (0) + +static void +add_free_desc_area(char *start, char *end) +{ + if (end > start && sizeof(ErtsFreeSegDesc) <= end - start) { + ErtsFreeSegDesc *prev_desc, *desc; + char *desc_end; + + prev_desc = (ErtsFreeSegDesc *) start; + prev_desc->start = mmap_state.desc_free_list; + desc = (ErtsFreeSegDesc *) (start + sizeof(ErtsFreeSegDesc)); + desc_end = start + 2*sizeof(ErtsFreeSegDesc); + + while (desc_end <= end) { + desc->start = (char *) prev_desc; + prev_desc = desc; + desc = (ErtsFreeSegDesc *) desc_end; + desc_end += sizeof(ErtsFreeSegDesc); + } + mmap_state.desc_free_list = (char *) prev_desc; + } +} + +static ERTS_INLINE ErtsFreeSegDesc * +alloc_desc(void) +{ + ErtsFreeSegDesc *res; + res = (ErtsFreeSegDesc *) mmap_state.desc_free_list; + if (res) + mmap_state.desc_free_list = res->start; + return res; +} + +static ERTS_INLINE void +free_desc(ErtsFreeSegDesc *desc) +{ + desc->start = mmap_state.desc_free_list; + mmap_state.desc_free_list = (char *) desc; +} + static ERTS_INLINE ErtsFreeSegDesc* anode_to_desc(RBTNode* anode) { return (ErtsFreeSegDesc*) ((char*)anode - offsetof(ErtsFreeSegDesc, anode)); @@ -94,12 +268,6 @@ static ERTS_INLINE ErtsFreeSegDesc* node_to_desc(enum SortOrder order, RBTNode* return order==ADDR_ORDER ? anode_to_desc(node) : snode_to_desc(node); } -typedef struct { - RBTree stree; - RBTree atree; -}ErtsFreeSegMap; - - #ifdef HARD_DEBUG static ERTS_INLINE SWord cmp_blocks(enum SortOrder order, RBTNode* lhs, RBTNode* rhs) @@ -671,19 +839,927 @@ static ErtsFreeSegDesc* lookup_free_seg(ErtsFreeSegMap* map, SWord need_sz) return best_desc; } +#if ERTS_HAVE_OS_MMAP +/* Implementation of os_mmap()/os_munmap()/os_mremap()... */ + +#if HAVE_MMAP +# define ERTS_MMAP_PROT (PROT_READ|PROT_WRITE) +# if defined(MAP_ANONYMOUS) +# define ERTS_MMAP_FLAGS (MAP_ANON|MAP_PRIVATE) +# define ERTS_MMAP_FD (-1) +# elif defined(MAP_ANON) +# define ERTS_MMAP_FLAGS (MAP_ANON|MAP_PRIVATE) +# define ERTS_MMAP_FD (-1) +# else +# define ERTS_MMAP_FLAGS (MAP_PRIVATE) +# define ERTS_MMAP_FD mmap_state.mmap_fd +# endif +#endif -void erts_mmap_init(ErtsMMapInit* init) +static ERTS_INLINE void * +os_mmap(UWord size, int try_superalign) { -#ifdef HARD_DEBUG - erts_fprintf(stderr, "SVERK: scs = %bpu\n", init->scs); - erts_fprintf(stderr, "SVERK: sco = %i\n", init->sco); - erts_fprintf(stderr, "SVERK: scmgc = %i\n", init->scmgc); +#if HAVE_MMAP + void *res; +#ifdef MAP_ALIGN + if (try_superalign) + res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, ERTS_MMAP_PROT, + ERTS_MMAP_FLAGS|MAP_ALIGN, ERTS_MMAP_FD, 0); + else +#endif + res = mmap((void *) 0, size, ERTS_MMAP_PROT, + ERTS_MMAP_FLAGS, ERTS_MMAP_FD, 0); + if (res == MAP_FAILED) + return NULL; + return res; +#elif HAVE_VIRTUALALLOC + return (void *) VirtualAlloc(NULL, (SIZE_T) size, + MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); +#else +# error "missing mmap() or similar" +#endif +} + +static ERTS_INLINE void +os_munmap(void *ptr, UWord size) +{ +#if HAVE_MMAP +#ifdef ERTS_MMAP_DEBUG + int res = +#endif + munmap(ptr, size); + ERTS_MMAP_ASSERT(res == 0); +#elif HAVE_VIRTUALALLOC +#ifdef DEBUG + BOOL res = +#endif + VirtualFree((LPVOID) ptr, (SIZE_T) 0, MEM_RELEASE); + ERTS_MMAP_ASSERT(res != 0); +#else +# error "missing munmap() or similar" +#endif +} + +#ifdef ERTS_HAVE_OS_MREMAP +# if HAVE_MREMAP +# if defined(__NetBSD__) +# define ERTS_MREMAP_FLAGS (0) +# else +# define ERTS_MREMAP_FLAGS (MREMAP_MAYMOVE) +# endif +# endif +static ERTS_INLINE void * +os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign) +{ + void *new_seg; +#if HAVE_MREMAP + new_seg = mremap(ptr, (size_t) old_size, +# if defined(__NetBSD__) + NULL, +# endif + (size_t) new_size, ERTS_MREMAP_FLAGS); + if (new_seg == (void *) MAP_FAILED) + return NULL; + return new_seg; +#else +# error "missing mremap() or similar" +#endif +} +#endif + +#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION +#if HAVE_MMAP + +#define ERTS_MMAP_RESERVE_PROT (ERTS_MMAP_PROT) +#define ERTS_MMAP_RESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED) +#define ERTS_MMAP_UNRESERVE_PROT (PROT_NONE) +#define ERTS_MMAP_UNRESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_NORESERVE|MAP_FIXED) +#define ERTS_MMAP_VIRTUAL_PROT (PROT_NONE) +#define ERTS_MMAP_VIRTUAL_FLAGS (ERTS_MMAP_FLAGS|MAP_NORESERVE) + +static int +os_reserve_physical(char *ptr, UWord size) +{ + void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_RESERVE_PROT, + ERTS_MMAP_RESERVE_FLAGS, ERTS_MMAP_FD, 0); + if (res == (void *) MAP_FAILED) + return 0; + return 1; +} + +static void +os_unreserve_physical(char *ptr, UWord size) +{ + void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_UNRESERVE_PROT, + ERTS_MMAP_UNRESERVE_FLAGS, ERTS_MMAP_FD, 0); + if (res == (void *) MAP_FAILED) + erl_exit(ERTS_ABORT_EXIT, "Failed to unreserve memory"); +} + +static void * +os_mmap_virtual(char *ptr, UWord size) +{ + void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_VIRTUAL_PROT, + ERTS_MMAP_VIRTUAL_FLAGS, ERTS_MMAP_FD, 0); + if (res == (void *) MAP_FAILED) + return NULL; + return res; +} + +#else +#error "Missing reserve/unreserve physical memory implementation" +#endif +#endif /* ERTS_HAVE_OS_RESERVE_PHYSICAL_MEMORY */ + +#endif /* ERTS_HAVE_OS_MMAP */ + +static int reserve_noop(char *ptr, UWord size) +{ +#ifdef ERTS_MMAP_DEBUG_FILL_AREAS + Uint32 *uip, *end = (Uint32 *) (ptr + size); + + for (uip = (Uint32 *) ptr; uip < end; uip++) + ERTS_MMAP_ASSERT(*uip == (Uint32) 0xdeadbeef); + for (uip = (Uint32 *) ptr; uip < end; uip++) + *uip = (Uint32) 0xfeedfeed; +#endif + return 1; +} + +static void unreserve_noop(char *ptr, UWord size) +{ +#ifdef ERTS_MMAP_DEBUG_FILL_AREAS + Uint32 *uip, *end = (Uint32 *) (ptr + size); + + for (uip = (Uint32 *) ptr; uip < end; uip++) + *uip = (Uint32) 0xdeadbeef; +#endif +} + +void * +erts_mmap(Uint32 flags, UWord *sizep) +{ + char *seg; + UWord asize = ERTS_PAGEALIGNED_CEILING(*sizep); + + /* Map in premapped supercarrier */ + if (mmap_state.supercarrier && !(ERTS_MMAPFLG_OS_ONLY & flags)) { + char *end; + ErtsFreeSegDesc *desc; + Uint32 superaligned = (ERTS_MMAPFLG_SUPERALIGNED & flags); + + erts_smp_mtx_lock(&mmap_state.mtx); + + if (!superaligned) { + desc = lookup_free_seg(&mmap_state.sua.map, asize); + if (desc) { + seg = desc->start; + end = seg+asize; + if (!mmap_state.reserve_physical(seg, asize)) + goto supercarrier_reserve_failure; + if (desc->end == end) { + delete_free_seg(&mmap_state.sua.map, desc); + free_desc(desc); + } + else { + ERTS_MMAP_ASSERT(end < desc->end); + resize_free_seg(&mmap_state.sua.map, desc, end, desc->end); + } + ERTS_MMAP_SIZE_SC_SUA_INC(asize); + goto supercarrier_success; + } + + if (asize <= mmap_state.sua.bot - mmap_state.sa.top) { + if (!mmap_state.reserve_physical(mmap_state.sua.bot - asize, + asize)) + goto supercarrier_reserve_failure; + mmap_state.sua.bot -= asize; + seg = mmap_state.sua.bot; + ERTS_MMAP_SIZE_SC_SUA_INC(asize); + goto supercarrier_success; + } + } + + asize = ERTS_SUPERALIGNED_CEILING(asize); + + desc = lookup_free_seg(&mmap_state.sa.map, asize); + if (desc) { + seg = desc->start; + end = seg+asize; + if (!mmap_state.reserve_physical(seg, asize)) + goto supercarrier_reserve_failure; + if (desc->end == end) { + delete_free_seg(&mmap_state.sa.map, desc); + free_desc(desc); + } + else { + ERTS_MMAP_ASSERT(end < desc->end); + resize_free_seg(&mmap_state.sa.map, desc, end, desc->end); + } + ERTS_MMAP_SIZE_SC_SA_INC(asize); + goto supercarrier_success; + } + + if (superaligned) { + + if (asize <= mmap_state.sua.bot - mmap_state.sa.top) { + seg = (void *) mmap_state.sa.top; + if (!mmap_state.reserve_physical(seg, asize)) + goto supercarrier_reserve_failure; + mmap_state.sa.top += asize; + ERTS_MMAP_SIZE_SC_SA_INC(asize); + goto supercarrier_success; + } + + desc = lookup_free_seg(&mmap_state.sua.map, asize + ERTS_SUPERALIGNED_SIZE); + if (desc) { + char *org_start = desc->start; + char *org_end = desc->end; + + seg = (char *) ERTS_SUPERALIGNED_CEILING(org_start); + end = seg + asize; + if (!mmap_state.reserve_physical(seg, asize)) + goto supercarrier_reserve_failure; + if (org_start != seg) { + ERTS_MMAP_ASSERT(org_start < seg); + resize_free_seg(&mmap_state.sua.map, desc, org_start, seg); + desc = NULL; + } + if (end != org_end) { + ERTS_MMAP_ASSERT(end < org_end); + if (desc) + resize_free_seg(&mmap_state.sua.map, desc, end, org_end); + else { + desc = alloc_desc(); + if (!desc) + add_free_desc_area(end, org_end); + else + insert_free_seg(&mmap_state.sua.map, desc, end, org_end); + } + } + ERTS_MMAP_SIZE_SC_SA_INC(asize); + goto supercarrier_success; + } + } + + erts_smp_mtx_unlock(&mmap_state.mtx); + } + +#if ERTS_HAVE_OS_MMAP + /* Map using OS primitives */ + if (!(ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) && !mmap_state.no_os_mmap) { + if (!(ERTS_MMAPFLG_SUPERALIGNED & flags)) { + seg = os_mmap(asize, 0); + if (!seg) + return NULL; + } + else { + asize = ERTS_SUPERALIGNED_CEILING(*sizep); + seg = os_mmap(asize, 1); + if (!seg) + return NULL; + + if (!ERTS_IS_SUPERALIGNED(seg)) { + char *ptr; + UWord sz; + + os_munmap(seg, asize); + + ptr = os_mmap(asize + ERTS_SUPERALIGNED_SIZE, 1); + if (!ptr) + return NULL; + + seg = (char *) ERTS_SUPERALIGNED_CEILING(ptr); + sz = (UWord) (seg - ptr); + ERTS_MMAP_ASSERT(sz <= ERTS_SUPERALIGNED_SIZE); + if (sz) + os_munmap(ptr, sz); + sz = ERTS_SUPERALIGNED_SIZE - sz; + if (sz) + os_munmap(seg+asize, sz); + } + } + + ERTS_MMAP_SIZE_OS_INC(asize); + *sizep = asize; + return (void *) seg; + } +#endif + *sizep = 0; + return NULL; + +supercarrier_success: + +#ifdef ERTS_MMAP_DEBUG + if ((ERTS_MMAPFLG_SUPERALIGNED & flags) + || ERTS_MMAP_IN_SUPERALIGNED_AREA(seg)) { + ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(seg)); + ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(asize)); + } + else { + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(seg)); + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(asize)); + } +#endif + + erts_smp_mtx_unlock(&mmap_state.mtx); + + *sizep = asize; + return (void *) seg; + +supercarrier_reserve_failure: + erts_smp_mtx_unlock(&mmap_state.mtx); + + *sizep = 0; + return NULL; + +} + +void +erts_munmap(Uint32 flags, void **ptrp, UWord *sizep) +{ + void *ptr = *ptrp; + UWord size = *sizep; + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(ptr)); + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(size)); + if (!ERTS_MMAP_IN_SUPERCARRIER(ptr)) { + ERTS_MMAP_ASSERT(!mmap_state.no_os_mmap); +#if ERTS_HAVE_OS_MMAP + ERTS_MMAP_SIZE_OS_DEC(size); + os_munmap(ptr, size); +#endif + } + else { + char *start, *end; + ErtsFreeSegMap *map; + ErtsFreeSegDesc *prev, *next, *desc; + + ERTS_MMAP_ASSERT(mmap_state.supercarrier); + + start = (char *) ptr; + end = start + size; + + erts_smp_mtx_lock(&mmap_state.mtx); + + if (ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)) { + + start = (char *) ERTS_SUPERALIGNED_CEILING(start); + end = (char *) ERTS_SUPERALIGNED_FLOOR(end); + + size = (UWord) (end - start); + *ptrp = start; + *sizep = size; + + map = &mmap_state.sa.map; + adjacent_free_seg(map, start, end, &prev, &next); + + ERTS_MMAP_SIZE_SC_SA_DEC(size); + if (end == mmap_state.sa.top) { + ERTS_MMAP_ASSERT(!next); + if (prev) { + start = prev->start; + delete_free_seg(map, prev); + free_desc(prev); + } + mmap_state.sa.top = start; + goto supercarrier_success; + } + } + else { + ERTS_MMAP_ASSERT(ERTS_MMAP_IN_SUPERUNALIGNED_AREA(ptr)); + + map = &mmap_state.sua.map; + adjacent_free_seg(map, start, end, &prev, &next); + + ERTS_MMAP_SIZE_SC_SUA_DEC(size); + if (start == mmap_state.sua.bot) { + ERTS_MMAP_ASSERT(!prev); + if (next) { + end = next->end; + delete_free_seg(map, next); + free_desc(next); + } + mmap_state.sua.bot = end; + goto supercarrier_success; + } + } + + desc = NULL; + + if (next) { + ERTS_MMAP_ASSERT(end < next->end); + end = next->end; + if (prev) { + delete_free_seg(map, next); + free_desc(next); + goto save_prev; + } + desc = next; + } else if (prev) { + save_prev: + ERTS_MMAP_ASSERT(prev->start < start); + start = prev->start; + desc = prev; + } + + if (desc) + resize_free_seg(map, desc, start, end); + else { + desc = alloc_desc(); + if (desc) + insert_free_seg(map, desc, start, end); + else { + if (map == &mmap_state.sa.map) + ERTS_MMAP_SIZE_SC_SA_INC(size); + else + ERTS_MMAP_SIZE_SC_SUA_INC(size); + add_free_desc_area(start, end); + } + } + + supercarrier_success: + erts_smp_mtx_unlock(&mmap_state.mtx); + + mmap_state.unreserve_physical((char *) ptr, size); + } +} + +static void * +remap_move(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) +{ + UWord size = *sizep; + UWord um_size = old_size; + void *um_ptr = ptr; + void *new_ptr = erts_mmap(flags, &size); + if (!new_ptr) + return NULL; + *sizep = size; + if (old_size < size) + size = old_size; + sys_memcpy(new_ptr, ptr, (size_t) size); + erts_munmap(flags, &um_ptr, &um_size); + ERTS_MMAP_ASSERT(um_ptr == ptr); + ERTS_MMAP_ASSERT(um_size == old_size); + return new_ptr; +} + +void * +erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) +{ + void *new_ptr; + Uint32 superaligned; + UWord asize; + + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(ptr)); + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(old_size)); + ERTS_MMAP_ASSERT(sizep && ERTS_IS_PAGEALIGNED(*sizep)); + + if (!ERTS_MMAP_IN_SUPERCARRIER(ptr)) { + + ERTS_MMAP_ASSERT(!mmap_state.no_os_mmap); + + if (!(ERTS_MMAPFLG_OS_ONLY & flags) && mmap_state.supercarrier) { + new_ptr = remap_move(ERTS_MMAPFLG_SUPERCARRIER_ONLY|flags, ptr, + old_size, sizep); + if (new_ptr) + return new_ptr; + } + + if (ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) + return NULL; + +#if ERTS_HAVE_OS_MREMAP || ERTS_HAVE_GENUINE_OS_MMAP + superaligned = (ERTS_MMAPFLG_SUPERALIGNED & flags); + + if (superaligned) { + asize = ERTS_SUPERALIGNED_CEILING(*sizep); + if (asize == old_size && ERTS_IS_SUPERALIGNED(ptr)) { + *sizep = asize; + return ptr; + } + } + else { + asize = ERTS_PAGEALIGNED_CEILING(*sizep); + if (asize == old_size) { + *sizep = asize; + return ptr; + } + } + +#if ERTS_HAVE_GENUINE_OS_MMAP + if (asize < old_size + && (!superaligned + || ERTS_IS_SUPERALIGNED(ptr))) { + UWord um_sz; + new_ptr = ((char *) ptr) + asize; + ERTS_MMAP_ASSERT((((char *)ptr) + old_size) > (char *) new_ptr); + um_sz = (UWord) ((((char *) ptr) + old_size) - (char *) new_ptr); + ERTS_MMAP_SIZE_OS_DEC(um_sz); + os_munmap(new_ptr, um_sz); + *sizep = asize; + return ptr; + } +#endif +#if ERTS_HAVE_OS_MREMAP + if (superaligned) + return remap_move(flags, new_ptr, old_size, sizep); + else { + new_ptr = os_mremap(ptr, old_size, asize, 0); + if (!new_ptr) + return NULL; + if (asize > old_size) + ERTS_MMAP_SIZE_OS_INC(asize - old_size); + else + ERTS_MMAP_SIZE_OS_DEC(old_size - asize); + *sizep = asize; + return new_ptr; + } +#endif +#endif + } + else { /* In super carrier */ + char *start, *end, *new_end; + ErtsFreeSegMap *map; + ErtsFreeSegDesc *prev, *next, *desc; + + ERTS_MMAP_ASSERT(mmap_state.supercarrier); + + if (ERTS_MMAPFLG_OS_ONLY & flags) + return remap_move(flags, ptr, old_size, sizep); + + superaligned = (ERTS_MMAPFLG_SUPERALIGNED & flags); + + asize = (superaligned + ? ERTS_SUPERALIGNED_CEILING(*sizep) + : ERTS_PAGEALIGNED_CEILING(*sizep)); + + erts_smp_mtx_lock(&mmap_state.mtx); + + if (ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr) + ? (!superaligned && lookup_free_seg(&mmap_state.sua.map, asize)) + : (superaligned && lookup_free_seg(&mmap_state.sa.map, asize))) { + erts_smp_mtx_unlock(&mmap_state.mtx); + /* + * Segment currently in wrong area (due to a previous memory + * shortage), move it to the right area. + * (remap_move() will succeed) + */ + return remap_move(ERTS_MMAPFLG_SUPERCARRIER_ONLY|flags, ptr, + old_size, sizep); + } + + if (asize == old_size) { + new_ptr = ptr; + goto supercarrier_resize_success; + } + + start = (char *) ptr; + end = start + old_size; + new_end = start+asize; + + if (asize < old_size) { + new_ptr = ptr; + if (!ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)) { + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(ptr)); + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(old_size)); + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(asize)); + map = &mmap_state.sua.map; + ERTS_MMAP_SIZE_SC_SUA_DEC(old_size - asize); + } + else { + ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(ptr)); + ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(old_size)); + if (!superaligned) { + /* must be a superaligned size in this area */ + asize = ERTS_SUPERALIGNED_CEILING(asize); + ERTS_MMAP_ASSERT(asize <= old_size); + if (asize == old_size) + goto supercarrier_resize_success; + new_end = start+asize; + } + ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(asize)); + if (end == mmap_state.sa.top) { + mmap_state.sa.top = new_end; + mmap_state.unreserve_physical(((char *) ptr) + asize, + old_size - asize); + goto supercarrier_resize_success; + } + ERTS_MMAP_SIZE_SC_SA_DEC(old_size - asize); + map = &mmap_state.sa.map; + } + + adjacent_free_seg(map, start, end, &prev, &next); + + if (next) + resize_free_seg(map, next, new_end, next->end); + else { + desc = alloc_desc(); + if (desc) + insert_free_seg(map, desc, new_end, end); + else { + if (map == &mmap_state.sa.map) + ERTS_MMAP_SIZE_SC_SA_INC(old_size - asize); + else + ERTS_MMAP_SIZE_SC_SUA_INC(old_size - asize); + add_free_desc_area(new_end, end); + goto supercarrier_resize_success; + } + } + mmap_state.unreserve_physical(((char *) ptr) + asize, + old_size - asize); + goto supercarrier_resize_success; + } + + if (!ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)) { + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(ptr)); + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(old_size)); + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(asize)); + + adjacent_free_seg(&mmap_state.sua.map, start, end, &prev, &next); + + if (next && new_end <= next->end) { + if (!mmap_state.reserve_physical(((char *) ptr) + old_size, + asize - old_size)) + goto supercarrier_reserve_failure; + if (new_end < next->end) + resize_free_seg(&mmap_state.sua.map, next, new_end, next->end); + else { + delete_free_seg(&mmap_state.sua.map, next); + free_desc(next); + } + new_ptr = ptr; + ERTS_MMAP_SIZE_SC_SUA_INC(asize - old_size); + goto supercarrier_resize_success; + } + } + else { /* Superaligned area */ + ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(ptr)); + ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(old_size)); + + if (!superaligned) { + /* must be a superaligned size in this area */ + asize = ERTS_PAGEALIGNED_CEILING(asize); + new_end = start+asize; + } + + ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(asize)); + + if (end == mmap_state.sa.top) { + if (new_end <= mmap_state.sua.bot) { + if (!mmap_state.reserve_physical(((char *) ptr) + old_size, + asize - old_size)) + goto supercarrier_reserve_failure; + mmap_state.sa.top = new_end; + new_ptr = ptr; + ERTS_MMAP_SIZE_SC_SA_INC(asize - old_size); + goto supercarrier_resize_success; + } + } + else { + adjacent_free_seg(&mmap_state.sa.map, start, end, &prev, &next); + if (next && new_end <= next->end) { + if (!mmap_state.reserve_physical(((char *) ptr) + old_size, + asize - old_size)) + goto supercarrier_reserve_failure; + if (new_end < next->end) + resize_free_seg(&mmap_state.sa.map, next, new_end, next->end); + else { + delete_free_seg(&mmap_state.sa.map, next); + free_desc(next); + } + new_ptr = ptr; + ERTS_MMAP_SIZE_SC_SA_INC(asize - old_size); + goto supercarrier_resize_success; + } + } + } + erts_smp_mtx_unlock(&mmap_state.mtx); + + /* Failed to resize... */ + } + + return remap_move(flags, ptr, old_size, sizep); + +supercarrier_resize_success: + +#ifdef ERTS_MMAP_DEBUG + if ((ERTS_MMAPFLG_SUPERALIGNED & flags) + || ERTS_MMAP_IN_SUPERALIGNED_AREA(new_ptr)) { + ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(new_ptr)); + ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(asize)); + } + else { + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(new_ptr)); + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(asize)); + } +#endif + + erts_smp_mtx_unlock(&mmap_state.mtx); + + *sizep = asize; + return new_ptr; + +supercarrier_reserve_failure: + + erts_smp_mtx_unlock(&mmap_state.mtx); + *sizep = 0; + return NULL; + +} + +int erts_mmap_in_supercarrier(void *ptr) +{ + return ERTS_MMAP_IN_SUPERCARRIER(ptr); +} + +void +erts_mmap_init(ErtsMMapInit *init) +{ + int virtual_map = 0; + char *start = NULL, *end = NULL; + UWord pagesize; +#if defined(__WIN32__) + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + pagesize = (UWord) sysinfo.dwPageSize; +#elif defined(_SC_PAGESIZE) + pagesize = (UWord) sysconf(_SC_PAGESIZE); +#elif defined(HAVE_GETPAGESIZE) + pagesize = (UWord) getpagesize(); +#else +# error "Do not know how to get page size" +#endif +#if defined(HARD_DEBUG) || 0 + erts_fprintf(stderr, "erts_mmap: scs = %bpu\n", init->scs); + erts_fprintf(stderr, "erts_mmap: sco = %i\n", init->sco); + erts_fprintf(stderr, "erts_mmap: scmgc = %i\n", init->scmgc); +#endif + erts_page_inv_mask = pagesize - 1; + if (pagesize & erts_page_inv_mask) + erl_exit(-1, "erts_mmap: Invalid pagesize: %bpu\n", + pagesize); + + erts_have_erts_mmap = 0; + + mmap_state.reserve_physical = reserve_noop; + mmap_state.unreserve_physical = unreserve_noop; + +#if HAVE_MMAP && !defined(MAP_ANON) + mmap_state.mmap_fd = open("/dev/zero", O_RDWR); + if (mmap_state.mmap_fd < 0) + erl_exit(-1, "erts_mmap: Failed to open /dev/zero\n"); +#endif + + erts_smp_mtx_init(&mmap_state.mtx, "erts_mmap"); + +#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION + if (init->virtual_range.start) { + char *ptr; + UWord sz; + ptr = (char *) ERTS_PAGEALIGNED_CEILING(init->virtual_range.start); + end = (char *) ERTS_PAGEALIGNED_FLOOR(init->virtual_range.end); + sz = end - ptr; + start = os_mmap_virtual(ptr, sz); + if (!start || start > ptr || start >= end) + erl_exit(-1, + "erts_mmap: Failed to create virtual range for super carrier\n"); + sz = start - ptr; + if (sz) + os_munmap(end, sz); + mmap_state.reserve_physical = os_reserve_physical; + mmap_state.unreserve_physical = os_unreserve_physical; + virtual_map = 1; + } + else +#endif + if (init->predefined_area.start) { + start = init->predefined_area.start; + end = init->predefined_area.end; + if (end != (void *) 0 && end < start) + end = start; + } +#if ERTS_HAVE_OS_MMAP + else if (init->scs) { + UWord sz; + sz = ERTS_PAGEALIGNED_CEILING(init->scs); +#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION + if (!init->scrpm) { + start = os_mmap_virtual(NULL, sz); + mmap_state.reserve_physical = os_reserve_physical; + mmap_state.unreserve_physical = os_unreserve_physical; + virtual_map = 1; + } + else +#endif + { + /* + * The whole supercarrier will by physically + * reserved all the time. + */ + start = os_mmap(sz, 1); + } + if (!start) + erl_exit(-1, + "erts_mmap: Failed to create super carrier of size %bpu MB\n", + init->scs/1024/1024); + end = start + sz; +#ifdef ERTS_MMAP_DEBUG_FILL_AREAS + if (!virtual_map) { + Uint32 *uip; + + for (uip = (Uint32 *) start; uip < (Uint32 *) end; uip++) + *uip = (Uint32) 0xdeadbeef; + } +#endif + } + if (!mmap_state.no_os_mmap) + erts_have_erts_mmap |= ERTS_HAVE_ERTS_OS_MMAP; +#endif + + mmap_state.size.supercarrier.total = 0; + mmap_state.size.supercarrier.used.total = 0; + mmap_state.size.supercarrier.used.sa = 0; + mmap_state.size.supercarrier.used.sua = 0; + mmap_state.size.os.used = 0; + + if (!start) { + mmap_state.sa.bot = NULL; + mmap_state.sua.top = NULL; + mmap_state.sa.bot = NULL; + mmap_state.sua.top = NULL; + mmap_state.no_os_mmap = 0; + } + else { + size_t desc_size; + + mmap_state.no_os_mmap = init->sco; + + desc_size = init->scmgc; + if (desc_size < 100) + desc_size = 100; + desc_size *= sizeof(ErtsFreeSegDesc); + if ((desc_size + + ERTS_SUPERALIGNED_SIZE + + ERTS_PAGEALIGNED_SIZE) > end - start) + erl_exit(-1, "erts_mmap: No space for segments in super carrier\n"); + + mmap_state.sa.bot = start; + mmap_state.sa.bot += desc_size; + mmap_state.sa.bot = (char *) ERTS_SUPERALIGNED_CEILING(mmap_state.sa.bot); + mmap_state.sa.top = mmap_state.sa.bot; + mmap_state.sua.top = (char *) ERTS_SUPERALIGNED_FLOOR(end); + mmap_state.sua.bot = mmap_state.sua.top; + + mmap_state.size.os.used += (UWord) (mmap_state.sa.bot - start); + + if (end == (void *) 0) { + /* + * Very unlikely, but we need a guarantee + * that `mmap_state.sua.top` always will + * compare as larger than all segment pointers + * into the super carrier... + */ + mmap_state.sua.top -= ERTS_PAGEALIGNED_SIZE; + mmap_state.size.os.used += ERTS_PAGEALIGNED_SIZE; + } + + mmap_state.size.supercarrier.total = (UWord) (mmap_state.sua.top - mmap_state.sa.bot); + + /* + * Area before (and after) super carrier + * will be used for free segment descritors. + */ + mmap_state.desc_free_list = NULL; +#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION + if (virtual_map && mmap_state.sa.bot - start > 0) + os_reserve_physical(start, mmap_state.sa.bot - start); +#endif + add_free_desc_area(start, mmap_state.sa.bot); +#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION + if (virtual_map && end - mmap_state.sua.top > 0) + os_reserve_physical(mmap_state.sua.top, end - mmap_state.sua.top); +#endif + add_free_desc_area(mmap_state.sua.top, end); + + init_free_seg_map(&mmap_state.sa.map, 0); + init_free_seg_map(&mmap_state.sua.map, 1); + + mmap_state.supercarrier = 1; + erts_have_erts_mmap |= ERTS_HAVE_ERTS_SUPERCARRIER_MMAP; + +#ifdef HARD_DEBUG { void test_it(void); test_it(); } #endif + + } +#if !ERTS_HAVE_OS_MMAP + mmap_state.no_os_mmap = 1; +#endif + } @@ -897,12 +1973,6 @@ print_tree(enum SortOrder order, RBTNode* root) #endif /* PRINT_TREE */ - -static ErtsFreeSegDesc* new_desc(void) -{ - return (ErtsFreeSegDesc*) malloc(sizeof(ErtsFreeSegDesc)); -} - void test_it(void) { ErtsFreeSegMap map; @@ -912,13 +1982,13 @@ void test_it(void) for (i=0; i<2; i++) { init_free_seg_map(&map, i); - insert_free_seg(&map, new_desc(), (char*)0x11000, (char*)0x12000); + insert_free_seg(&map, alloc_desc(), (char*)0x11000, (char*)0x12000); check_tree(&map.atree, 0); check_tree(&map.stree, 0); - insert_free_seg(&map, new_desc(), (char*)0x13000, (char*)0x14000); + insert_free_seg(&map, alloc_desc(), (char*)0x13000, (char*)0x14000); check_tree(&map.atree, 0); check_tree(&map.stree, 0); - insert_free_seg(&map, new_desc(), (char*)0x15000, (char*)0x17000); + insert_free_seg(&map, alloc_desc(), (char*)0x15000, (char*)0x17000); check_tree(&map.atree, 0); check_tree(&map.stree, 0); - insert_free_seg(&map, new_desc(), (char*)0x8000, (char*)0x10000); + insert_free_seg(&map, alloc_desc(), (char*)0x8000, (char*)0x10000); check_tree(&map.atree, 0); check_tree(&map.stree, 0); desc = lookup_free_seg(&map, 0x500); diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 64baa6c493..143f1aff3e 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2013. All Rights Reserved. + * Copyright Ericsson AB 2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -17,10 +17,95 @@ * %CopyrightEnd% */ +#ifndef ERL_MMAP_H__ +#define ERL_MMAP_H__ + +#include "sys.h" + +#define ERTS_MMAP_SUPERALIGNED_BITS (18) +/* Affects hard limits for sbct and lmbcs documented in erts_alloc.xml */ + +#define ERTS_MMAPFLG_OS_ONLY (((Uint32) 1) << 0) +#define ERTS_MMAPFLG_SUPERCARRIER_ONLY (((Uint32) 1) << 1) +#define ERTS_MMAPFLG_SUPERALIGNED (((Uint32) 1) << 2) + +#define ERTS_HAVE_ERTS_OS_MMAP (1 << 0) +#define ERTS_HAVE_ERTS_SUPERCARRIER_MMAP (1 << 1) +extern int erts_have_erts_mmap; +extern UWord erts_page_inv_mask; + typedef struct { - SWord scs; /* super carrier size */ + struct { + char *start; + char *end; + } virtual_range; + struct { + char *start; + char *end; + } predefined_area; + UWord scs; /* super carrier size */ int sco; /* super carrier only? */ Uint scmgc; /* super carrier: max guaranteed (number of) carriers */ + int scrpm; }ErtsMMapInit; +#define ERTS_MMAP_INIT_DEFAULT_INITER \ + {{NULL, NULL}, {NULL, NULL}, 0, 1, (1 << 16), 1} + +void *erts_mmap(Uint32 flags, UWord *sizep); +void erts_munmap(Uint32 flags, void **ptrp, UWord *sizep); +void *erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep); +int erts_mmap_in_supercarrier(void *ptr); void erts_mmap_init(ErtsMMapInit*); + +#define ERTS_SUPERALIGNED_SIZE \ + (1 << ERTS_MMAP_SUPERALIGNED_BITS) +#define ERTS_INV_SUPERALIGNED_MASK \ + ((UWord) (ERTS_SUPERALIGNED_SIZE - 1)) +#define ERTS_SUPERALIGNED_MASK \ + (~ERTS_INV_SUPERALIGNED_MASK) +#define ERTS_SUPERALIGNED_FLOOR(X) \ + (((UWord) (X)) & ERTS_SUPERALIGNED_MASK) +#define ERTS_SUPERALIGNED_CEILING(X) \ + ERTS_SUPERALIGNED_FLOOR((X) + ERTS_INV_SUPERALIGNED_MASK) +#define ERTS_IS_SUPERALIGNED(X) \ + (((UWord) (X) & ERTS_INV_SUPERALIGNED_MASK) == 0) + +#define ERTS_INV_PAGEALIGNED_MASK \ + (erts_page_inv_mask) +#define ERTS_PAGEALIGNED_MASK \ + (~ERTS_INV_PAGEALIGNED_MASK) +#define ERTS_PAGEALIGNED_FLOOR(X) \ + (((UWord) (X)) & ERTS_PAGEALIGNED_MASK) +#define ERTS_PAGEALIGNED_CEILING(X) \ + ERTS_PAGEALIGNED_FLOOR((X) + ERTS_INV_PAGEALIGNED_MASK) +#define ERTS_IS_PAGEALIGNED(X) \ + (((UWord) (X) & ERTS_INV_PAGEALIGNED_MASK) == 0) +#define ERTS_PAGEALIGNED_SIZE \ + (ERTS_INV_PAGEALIGNED_MASK + 1) + +#ifndef HAVE_MMAP +# define HAVE_MMAP 0 +#endif +#ifndef HAVE_MREMAP +# define HAVE_MREMAP 0 +#endif +#if HAVE_MMAP +# define ERTS_HAVE_OS_MMAP 1 +# define ERTS_HAVE_GENUINE_OS_MMAP 1 +# if HAVE_MREMAP +# define ERTS_HAVE_OS_MREMAP 1 +# endif +# if defined(MAP_FIXED) && defined(MAP_NORESERVE) +# define ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION 1 +# endif +#endif + +#ifndef HAVE_VIRTUALALLOC +# define HAVE_VIRTUALALLOC 0 +#endif +#if HAVE_VIRTUALALLOC +# define ERTS_HAVE_OS_MMAP 1 +#endif + +#endif /* ERL_MMAP_H__ */ diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 64fcb6bb40..b21d6ca393 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -100,45 +100,6 @@ static int atoms_initialized; typedef struct mem_kind_t MemKind; -#if HALFWORD_HEAP -static int initialize_pmmap(void); -static void *pmmap(size_t size); -static int pmunmap(void *p, size_t size); -static void *pmremap(void *old_address, size_t old_size, - size_t new_size); -#endif - -#if HAVE_MMAP -/* Mmap ... */ - -#define MMAP_PROT (PROT_READ|PROT_WRITE) - - -#ifdef MAP_ANON -# define MMAP_FLAGS (MAP_ANON|MAP_PRIVATE) -# define MMAP_FD (-1) -#else -# define MMAP_FLAGS (MAP_PRIVATE) -# define MMAP_FD mmap_fd -static int mmap_fd; -#endif - -#if HAVE_MREMAP -# define HAVE_MSEG_RECREATE 1 -#else -# define HAVE_MSEG_RECREATE 0 -#endif - -#if HALFWORD_HEAP -#define CAN_PARTLY_DESTROY 0 -#else -#define CAN_PARTLY_DESTROY 1 -#endif -#else /* #if HAVE_MMAP */ -#define CAN_PARTLY_DESTROY 0 -#error "Not supported" -#endif /* #if HAVE_MMAP */ - const ErtsMsegOpt_t erts_mseg_default_opt = { 1, /* Use cache */ 1, /* Preserv data */ @@ -163,9 +124,7 @@ typedef struct { CallCounter create; CallCounter create_resize; CallCounter destroy; -#if HAVE_MSEG_RECREATE CallCounter recreate; -#endif CallCounter clear_cache; CallCounter check_cache; } ErtsMsegCalls; @@ -236,11 +195,6 @@ struct ErtsMsegAllctr_t_ { Uint rel_max_cache_bad_fit; ErtsMsegCalls calls; - -#if CAN_PARTLY_DESTROY - Uint min_seg_size; -#endif - }; typedef union { @@ -344,69 +298,31 @@ schedule_cache_check(ErtsMsegAllctr_t *ma) { } } -/* remove ErtsMsegAllctr_t from arguments? - * only used for statistics - */ -static ERTS_INLINE void * -mmap_align(ErtsMsegAllctr_t *ma, void *addr, size_t length, int prot, int flags, int fd, off_t offset) { - - char *p, *q; - UWord d; - - p = mmap(addr, length, prot, flags, fd, offset); - - if (MAP_IS_ALIGNED(p) || p == MAP_FAILED) - return p; - - if (ma) - INC_CC(ma, create_resize); - - munmap(p, length); - - if ((p = mmap(addr, length + MSEG_ALIGNED_SIZE, prot, flags, fd, offset)) == MAP_FAILED) - return MAP_FAILED; - - q = (void *)ALIGNED_CEILING((char *)p); - d = (UWord)(q - p); - - if (d > 0) - munmap(p, d); - - if (MSEG_ALIGNED_SIZE - d > 0) - munmap((void *)(q + length), MSEG_ALIGNED_SIZE - d); - - return q; -} +/* #define ERTS_PRINT_ERTS_MMAP */ static ERTS_INLINE void * -mseg_create(ErtsMsegAllctr_t *ma, MemKind* mk, Uint size) +mseg_create(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, UWord *sizep) { +#ifdef ERTS_PRINT_ERTS_MMAP + UWord req_size = *sizep; +#endif void *seg; - ASSERT(size % MSEG_ALIGNED_SIZE == 0); - + Uint32 mmap_flags = 0; #if HALFWORD_HEAP - if (mk == &ma->low_mem) { - seg = pmmap(size); - if ((unsigned long) seg & CHECK_POINTER_MASK) { - erts_fprintf(stderr,"Pointer mask failure (0x%08lx)\n",(unsigned long) seg); - return NULL; - } - } else + mmap_flags |= ((mk == &ma->low_mem) + ? ERTS_MMAPFLG_SUPERCARRIER_ONLY + : ERTS_MMAPFLG_OS_ONLY); #endif - { -#if HAVE_MMAP - { - seg = (void *) mmap_align(ma, (void *) 0, (size_t) size, - MMAP_PROT, MMAP_FLAGS, MMAP_FD, 0); - if (seg == (void *) MAP_FAILED) - seg = NULL; - - ASSERT(MAP_IS_ALIGNED(seg) || !seg); - } -#else -# error "Missing mseg_create() implementation" + if (MSEG_FLG_IS_2POW(flags)) + mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; + + seg = erts_mmap(mmap_flags, sizep); + +#ifdef ERTS_PRINT_ERTS_MMAP + erts_fprintf(stderr, "%p = erts_mmap(%s, {%bpu, %bpu});\n", seg, + (mmap_flags & ERTS_MMAPFLG_SUPERALIGNED) ? "sa" : "sua", + req_size, *sizep); #endif - } INC_CC(ma, create); @@ -414,91 +330,55 @@ mseg_create(ErtsMsegAllctr_t *ma, MemKind* mk, Uint size) } static ERTS_INLINE void -mseg_destroy(ErtsMsegAllctr_t *ma, MemKind* mk, void *seg, Uint size) { - ERTS_DECLARE_DUMMY(int res); - +mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void **seg_pp, UWord *size_p) { + + Uint32 mmap_flags = 0; #if HALFWORD_HEAP - if (mk == &ma->low_mem) { - res = pmunmap((void *) seg, size); - } - else + mmap_flags |= ((mk == &ma->low_mem) + ? ERTS_MMAPFLG_SUPERCARRIER_ONLY + : ERTS_MMAPFLG_OS_ONLY); #endif - { -#ifdef HAVE_MMAP - res = munmap((void *) seg, size); -#else -# error "Missing mseg_destroy() implementation" + if (MSEG_FLG_IS_2POW(flags)) + mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; + + erts_munmap(mmap_flags, seg_pp, size_p); +#ifdef ERTS_PRINT_ERTS_MMAP + erts_fprintf(stderr, "erts_munmap(%s, %p, %bpu);\n", + (mmap_flags & ERTS_MMAPFLG_SUPERALIGNED) ? "sa" : "sua", + *seg_pp, *size_p); #endif - } - - ASSERT(size % MSEG_ALIGNED_SIZE == 0); - ASSERT(res == 0); - INC_CC(ma, destroy); } -#if HAVE_MSEG_RECREATE -#if defined(__NetBSD__) -#define MREMAP_FLAGS (0) -#else -#define MREMAP_FLAGS (MREMAP_MAYMOVE) -#endif - - -/* mseg_recreate - * May return *unaligned* segments as in address not aligned to MSEG_ALIGNMENT - * it is still page aligned - * - * This is fine for single block carriers as long as we don't cache misaligned - * segments (since multiblock carriers may use them) - * - * For multiblock carriers we *need* MSEG_ALIGNMENT but mbc's will never be - * reallocated. - * - * This should probably be fixed the following way: - * 1) Use an option to segment allocation - NEED_ALIGNMENT - * 2) Add mremap_align which takes care of aligning a new a mremaped area - * 3) Fix the cache to handle of aligned and unaligned segments - */ - static ERTS_INLINE void * -mseg_recreate(ErtsMsegAllctr_t *ma, MemKind* mk, void *old_seg, Uint old_size, Uint new_size) +mseg_recreate(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void *old_seg, UWord old_size, UWord *sizep) { +#ifdef ERTS_PRINT_ERTS_MMAP + UWord req_size = *sizep; +#endif void *new_seg; - - ASSERT(old_size % MSEG_ALIGNED_SIZE == 0); - ASSERT(new_size % MSEG_ALIGNED_SIZE == 0); - + Uint32 mmap_flags = 0; #if HALFWORD_HEAP - if (mk == &ma->low_mem) { - new_seg = (void *) pmremap((void *) old_seg, - (size_t) old_size, - (size_t) new_size); - } - else -#endif - { -#if HAVE_MREMAP -#if defined(__NetBSD__) - new_seg = mremap(old_seg, (size_t)old_size, NULL, new_size, MREMAP_FLAGS); -#else - new_seg = mremap(old_seg, (size_t)old_size, (size_t)new_size, MREMAP_FLAGS); + mmap_flags |= ((mk == &ma->low_mem) + ? ERTS_MMAPFLG_SUPERCARRIER_ONLY + : ERTS_MMAPFLG_OS_ONLY); #endif - if (new_seg == (void *) MAP_FAILED) - new_seg = NULL; -#else -#error "Missing mseg_recreate() implementation" -#endif - } + if (MSEG_FLG_IS_2POW(flags)) + mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; + + new_seg = erts_mremap(mmap_flags, old_seg, old_size, sizep); +#ifdef ERTS_PRINT_ERTS_MMAP + erts_fprintf(stderr, "%p = erts_mremap(%s, %p, %bpu, {%bpu, %bpu});\n", + new_seg, (mmap_flags & ERTS_MMAPFLG_SUPERALIGNED) ? "sa" : "sua", + old_seg, old_size, req_size, *sizep); +#endif INC_CC(ma, recreate); return new_seg; } -#endif /* #if HAVE_MSEG_RECREATE */ - #ifdef DEBUG #define ERTS_DBG_MA_CHK_THR_ACCESS(MA) \ do { \ @@ -566,14 +446,19 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size, Ui return 1; } else if (!MSEG_FLG_IS_2POW(flags) && !erts_circleq_is_empty(&(mk->cache_unpowered_node))) { - + void *destr_seg; + UWord destr_size; /* No free slots. * Evict oldest slot from unpowered cache so we can cache an unpowered (sbc) segment */ c = erts_circleq_tail(&(mk->cache_unpowered_node)); erts_circleq_remove(c); - mseg_destroy(mk->ma, mk, c->seg, c->size); + destr_seg = c->seg; + destr_size = c->size; + mseg_destroy(mk->ma, ERTS_MSEG_FLG_NONE, mk, &destr_seg, &destr_size); + ASSERT(destr_seg == c->seg); + ASSERT(destr_size == c->size); mseg_cache_clear_node(c); c->seg = seg; @@ -593,13 +478,20 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size, Ui int i; for( i = 0; i < CACHE_AREAS; i++) { + void *destr_seg; + UWord destr_size; if (erts_circleq_is_empty(&(mk->cache_powered_node[i]))) continue; c = erts_circleq_tail(&(mk->cache_powered_node[i])); erts_circleq_remove(c); - mseg_destroy(mk->ma, mk, c->seg, c->size); + destr_seg = seg; + destr_size = c->size; + mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, &destr_seg, &destr_size); + ASSERT(destr_seg == c->seg); + ASSERT(destr_size == c->size); + mseg_cache_clear_node(c); c->seg = seg; @@ -614,9 +506,9 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size, Ui return 0; } -static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p, Uint flags) { +static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flags) { - Uint size = *size_p; + Uint size = (UWord) *size_p; ERTS_DBG_MK_CHK_THR_ACCESS(mk); @@ -653,7 +545,11 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p, Uint flags ASSERT(!(mk->cache_size < 0)); if (csize != size) { - mseg_destroy(mk->ma, mk, (char *)seg + size, csize - size); + void *destr_seg = ((char *) seg) + size; + UWord destr_size = csize - size; + mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, &destr_seg, &destr_size); + *size_p = (UWord) (destr_seg - seg); + ASSERT(c->seg + c->size == destr_seg + destr_size); } return seg; @@ -683,7 +579,7 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p, Uint flags mseg_cache_clear_node(c); erts_circleq_push_head(&(mk->cache_free), c); - *size_p = csize; + *size_p = (UWord) csize; return seg; @@ -714,7 +610,7 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p, Uint flags ASSERT((size % GET_PAGE_SIZE) == 0); ASSERT((best->size % GET_PAGE_SIZE) == 0); - *size_p = size; + *size_p = (UWord) size; return seg; @@ -728,7 +624,9 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p, Uint flags * using callbacks from aux-work in the scheduler. */ -static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, cache_t *head) { +static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, Uint flags, cache_t *head) { + void *destr_seg; + UWord destr_size; cache_t *c = NULL; c = erts_circleq_tail(head); @@ -737,7 +635,11 @@ static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, cache_t *h if (erts_mtrace_enabled) erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg); - mseg_destroy(mk->ma, mk, c->seg, c->size); + destr_seg = c->seg; + destr_size = c->size; + mseg_destroy(mk->ma, flags, mk, &destr_seg, &destr_size); + ASSERT(destr_seg == c->seg); + ASSERT(destr_size == c->size); mseg_cache_clear_node(c); erts_circleq_push_head(&(mk->cache_free), c); @@ -749,10 +651,12 @@ static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, cache_t *h return mk->cache_size; } -static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, cache_t *head) { +static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, Uint flags, cache_t *head) { cache_t *c = NULL; while (!erts_circleq_is_empty(head)) { + void *destr_seg; + UWord destr_size; c = erts_circleq_tail(head); erts_circleq_remove(c); @@ -760,7 +664,11 @@ static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, cache_t *head) if (erts_mtrace_enabled) erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg); - mseg_destroy(mk->ma, mk, c->seg, c->size); + destr_seg = c->seg; + destr_size = c->size; + mseg_destroy(mk->ma, flags, mk, &destr_seg, &destr_size); + ASSERT(destr_seg == c->seg); + ASSERT(destr_size == c->size); mseg_cache_clear_node(c); erts_circleq_push_head(&(mk->cache_free), c); @@ -788,11 +696,11 @@ static Uint mseg_check_memkind_cache(MemKind *mk) { for (i = 0; i < CACHE_AREAS; i++) { if (!erts_circleq_is_empty(&(mk->cache_powered_node[i]))) - return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_powered_node[i])); + return mseg_drop_one_memkind_cache_size(mk, ERTS_MSEG_FLG_2POW, &(mk->cache_powered_node[i])); } if (!erts_circleq_is_empty(&(mk->cache_unpowered_node))) - return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_unpowered_node)); + return mseg_drop_one_memkind_cache_size(mk, ERTS_MSEG_FLG_NONE, &(mk->cache_unpowered_node)); return 0; } @@ -851,12 +759,12 @@ static void mseg_clear_memkind_cache(MemKind *mk) { if (erts_circleq_is_empty(&(mk->cache_powered_node[i]))) continue; - mseg_drop_memkind_cache_size(mk, &(mk->cache_powered_node[i])); + mseg_drop_memkind_cache_size(mk, ERTS_MSEG_FLG_2POW, &(mk->cache_powered_node[i])); ASSERT(erts_circleq_is_empty(&(mk->cache_powered_node[i]))); } /* drop varied caches */ if (!erts_circleq_is_empty(&(mk->cache_unpowered_node))) - mseg_drop_memkind_cache_size(mk, &(mk->cache_unpowered_node)); + mseg_drop_memkind_cache_size(mk, ERTS_MSEG_FLG_NONE, &(mk->cache_unpowered_node)); ASSERT(erts_circleq_is_empty(&(mk->cache_unpowered_node))); ASSERT(mk->cache_size == 0); @@ -896,36 +804,33 @@ static ERTS_INLINE MemKind* memkind(ErtsMsegAllctr_t *ma, } static void * -mseg_alloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, Uint *size_p, +mseg_alloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, UWord *size_p, Uint flags, const ErtsMsegOpt_t *opt) { - Uint size; + UWord size; void *seg; MemKind* mk = memkind(ma, opt); INC_CC(ma, alloc); - /* Carrier align */ - size = ALIGNED_CEILING(*size_p); - - /* Cache optim (if applicable) */ - if (MSEG_FLG_IS_2POW(flags) && !IS_2POW(size)) + if (!MSEG_FLG_IS_2POW(flags) && !IS_2POW(size)) + size = ERTS_PAGEALIGNED_CEILING(*size_p); + else { + size = ALIGNED_CEILING(*size_p); + /* Cache optim (if applicable) */ size = ceil_2pow(size); + } -#if CAN_PARTLY_DESTROY - if (size < ma->min_seg_size) - ma->min_seg_size = size; -#endif - if (opt->cache && mk->cache_size > 0 && (seg = cache_get_segment(mk, &size, flags)) != NULL) goto done; - if ((seg = mseg_create(ma, mk, size)) == NULL) - size = 0; + seg = mseg_create(ma, flags, mk, &size); + if (!seg) + *size_p = 0; + else { done: - *size_p = size; - if (seg) { + *size_p = size; if (erts_mtrace_enabled) erts_mtrace_crr_alloc(seg, atype, ERTS_MTRACE_SEGMENT_ID, size); @@ -937,14 +842,16 @@ done: static void -mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, Uint size, +mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, UWord size, Uint flags, const ErtsMsegOpt_t *opt) { + void *destr_seg; + UWord destr_size; MemKind* mk = memkind(ma, opt); ERTS_MSEG_DEALLOC_STAT(mk,size); - if (opt->cache && cache_bless_segment(mk, seg, size, flags)) { + if (opt->cache && cache_bless_segment(mk, seg, (Uint) size, flags)) { schedule_cache_check(ma); goto done; } @@ -952,7 +859,11 @@ mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, Uint size, if (erts_mtrace_enabled) erts_mtrace_crr_free(atype, SEGTYPE, seg); - mseg_destroy(ma, mk, seg, size); + destr_seg = seg; + destr_size = size; + mseg_destroy(ma, flags, mk, &destr_seg, &destr_size); + ASSERT(destr_seg == seg); + ASSERT(destr_size == size); done: @@ -961,11 +872,11 @@ done: static void * mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, - Uint old_size, Uint *new_size_p, Uint flags, const ErtsMsegOpt_t *opt) + UWord old_size, UWord *new_size_p, Uint flags, const ErtsMsegOpt_t *opt) { MemKind* mk; void *new_seg; - Uint new_size; + UWord new_size; /* Just allocate a new segment if we didn't have one before */ if (!seg || !old_size) { @@ -985,90 +896,46 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, mk = memkind(ma, opt); new_seg = seg; - /* Carrier align */ - new_size = ALIGNED_CEILING(*new_size_p); - /* Cache optim (if applicable) */ - if (MSEG_FLG_IS_2POW(flags) && !IS_2POW(new_size)) + if (!MSEG_FLG_IS_2POW(flags)) + new_size = ERTS_PAGEALIGNED_CEILING(*new_size_p); + else { + new_size = ALIGNED_CEILING(*new_size_p); + /* Cache optim (if applicable) */ new_size = ceil_2pow(new_size); + } - if (new_size == old_size) - ; - else if (new_size < old_size) { - Uint shrink_sz = old_size - new_size; - -#if CAN_PARTLY_DESTROY - if (new_size < ma->min_seg_size) - ma->min_seg_size = new_size; -#endif - /* +Mrsbcst */ - if (shrink_sz < opt->abs_shrink_th - && 100*shrink_sz < opt->rel_shrink_th*old_size) { - new_size = old_size; + if (new_size > old_size) { + if (opt->preserv) { + new_seg = mseg_recreate(ma, flags, mk, (void *) seg, old_size, &new_size); + if (!new_seg) + new_size = old_size; } else { - -#if CAN_PARTLY_DESTROY - - if (erts_mtrace_enabled) - erts_mtrace_crr_realloc(new_seg, atype, SEGTYPE, seg, new_size); - - mseg_destroy(ma, mk, ((char *) seg) + new_size, shrink_sz); - -#elif HAVE_MSEG_RECREATE - goto do_recreate; -#else + mseg_dealloc(ma, atype, seg, old_size, flags, opt); new_seg = mseg_alloc(ma, atype, &new_size, flags, opt); - - ASSERT(MAP_IS_ALIGNED(new_seg) || !new_seg); - if (!new_seg) - new_size = old_size; - else { - sys_memcpy(((char *) new_seg), - ((char *) seg), - MIN(new_size, old_size)); - mseg_dealloc(ma, atype, seg, old_size, flags, opt); - } -#endif + new_size = 0; } } - else { + else if (new_size < old_size) { + UWord shrink_sz = old_size - new_size; - if (!opt->preserv) { - mseg_dealloc(ma, atype, seg, old_size, flags, opt); - new_seg = mseg_alloc(ma, atype, &new_size, flags, opt); - ASSERT(MAP_IS_ALIGNED(new_seg) || !new_seg); + /* +Mrsbcst */ + if (shrink_sz < opt->abs_shrink_th + && 100*shrink_sz < opt->rel_shrink_th*old_size) { + new_size = old_size; } else { -#if HAVE_MSEG_RECREATE -#if !CAN_PARTLY_DESTROY - do_recreate: -#endif - new_seg = mseg_recreate(ma, mk, (void *) seg, old_size, new_size); - /* ASSERT(MAP_IS_ALIGNED(new_seg) || !new_seg); - * will not always be aligned and it ok for now - */ - - if (erts_mtrace_enabled) - erts_mtrace_crr_realloc(new_seg, atype, SEGTYPE, seg, new_size); + new_seg = mseg_recreate(ma, flags, mk, (void *) seg, old_size, &new_size); if (!new_seg) new_size = old_size; -#else - new_seg = mseg_alloc(ma, atype, &new_size, flags, opt); - - ASSERT(MAP_IS_ALIGNED(new_seg) || !new_seg); - - if (!new_seg) - new_size = old_size; - else { - sys_memcpy(((char *) new_seg), ((char *) seg), MIN(new_size, old_size)); - mseg_dealloc(ma, atype, seg, old_size, flags, opt); - } -#endif } } + if (erts_mtrace_enabled) + erts_mtrace_crr_realloc(new_seg, atype, SEGTYPE, seg, new_size); + INC_CC(ma, realloc); ASSERT(!MSEG_FLG_IS_2POW(flags) || IS_2POW(new_size)); @@ -1106,9 +973,7 @@ static struct { Eterm mseg_create; Eterm mseg_create_resize; Eterm mseg_destroy; -#if HAVE_MSEG_RECREATE Eterm mseg_recreate; -#endif Eterm mseg_clear_cache; Eterm mseg_check_cache; @@ -1163,9 +1028,7 @@ init_atoms(ErtsMsegAllctr_t *ma) AM_INIT(mseg_create); AM_INIT(mseg_create_resize); AM_INIT(mseg_destroy); -#if HAVE_MSEG_RECREATE AM_INIT(mseg_recreate); -#endif AM_INIT(mseg_clear_cache); AM_INIT(mseg_check_cache); @@ -1293,9 +1156,7 @@ info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp PRINT_CC(to, arg, create); PRINT_CC(to, arg, create_resize); PRINT_CC(to, arg, destroy); -#if HAVE_MSEG_RECREATE PRINT_CC(to, arg, recreate); -#endif PRINT_CC(to, arg, clear_cache); PRINT_CC(to, arg, check_cache); @@ -1316,12 +1177,10 @@ info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp bld_unstable_uint(hpp, szp, ma->calls.clear_cache.giga_no), bld_unstable_uint(hpp, szp, ma->calls.clear_cache.no)); -#if HAVE_MSEG_RECREATE add_3tup(hpp, szp, &res, am.mseg_recreate, bld_unstable_uint(hpp, szp, ma->calls.recreate.giga_no), bld_unstable_uint(hpp, szp, ma->calls.recreate.no)); -#endif add_3tup(hpp, szp, &res, am.mseg_destroy, bld_unstable_uint(hpp, szp, ma->calls.destroy.giga_no), @@ -1521,7 +1380,7 @@ erts_mseg_info(int ix, } void * -erts_mseg_alloc_opt(ErtsAlcType_t atype, Uint *size_p, Uint flags, const ErtsMsegOpt_t *opt) +erts_mseg_alloc_opt(ErtsAlcType_t atype, UWord *size_p, Uint flags, const ErtsMsegOpt_t *opt) { ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_OPT(opt); void *seg; @@ -1533,14 +1392,14 @@ erts_mseg_alloc_opt(ErtsAlcType_t atype, Uint *size_p, Uint flags, const ErtsMse } void * -erts_mseg_alloc(ErtsAlcType_t atype, Uint *size_p, Uint flags) +erts_mseg_alloc(ErtsAlcType_t atype, UWord *size_p, Uint flags) { return erts_mseg_alloc_opt(atype, size_p, flags, &erts_mseg_default_opt); } void erts_mseg_dealloc_opt(ErtsAlcType_t atype, void *seg, - Uint size, Uint flags, const ErtsMsegOpt_t *opt) + UWord size, Uint flags, const ErtsMsegOpt_t *opt) { ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_OPT(opt); ERTS_MSEG_LOCK(ma); @@ -1550,14 +1409,14 @@ erts_mseg_dealloc_opt(ErtsAlcType_t atype, void *seg, } void -erts_mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size, Uint flags) +erts_mseg_dealloc(ErtsAlcType_t atype, void *seg, UWord size, Uint flags) { erts_mseg_dealloc_opt(atype, seg, size, flags, &erts_mseg_default_opt); } void * erts_mseg_realloc_opt(ErtsAlcType_t atype, void *seg, - Uint old_size, Uint *new_size_p, + UWord old_size, UWord *new_size_p, Uint flags, const ErtsMsegOpt_t *opt) { @@ -1572,7 +1431,7 @@ erts_mseg_realloc_opt(ErtsAlcType_t atype, void *seg, void * erts_mseg_realloc(ErtsAlcType_t atype, void *seg, - Uint old_size, Uint *new_size_p, Uint flags) + UWord old_size, UWord *new_size_p, Uint flags) { return erts_mseg_realloc_opt(atype, seg, old_size, new_size_p, flags, &erts_mseg_default_opt); @@ -1662,19 +1521,16 @@ erts_mseg_init(ErtsMsegInit_t *init) erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms"); -#if HAVE_MMAP && !defined(MAP_ANON) - mmap_fd = open("/dev/zero", O_RDWR); - if (mmap_fd < 0) - erl_exit(ERTS_ABORT_EXIT, "erts_mseg: unable to open /dev/zero\n"); +#if HALFWORD_HEAP + if (sizeof(void *) != 8) + erl_exit(-1,"Halfword emulator cannot be run in 32bit mode"); + + init->mmap.virtual_range.start = (char *) sbrk(0); + init->mmap.virtual_range.end = (char *) 0x100000000UL; + init->mmap.sco = 0; #endif -#if HAVE_MMAP -# if HALFWORD_HEAP - initialize_pmmap(); -# else erts_mmap_init(&init->mmap); -# endif -#endif if (!IS_2POW(GET_PAGE_SIZE)) erl_exit(ERTS_ABORT_EXIT, "erts_mseg: Unexpected page_size %beu\n", GET_PAGE_SIZE); @@ -1716,10 +1572,6 @@ erts_mseg_init(ErtsMsegInit_t *init) #endif sys_memzero((void *) &ma->calls, sizeof(ErtsMsegCalls)); - -#if CAN_PARTLY_DESTROY - ma->min_seg_size = ~((Uint) 0); -#endif } } @@ -1761,7 +1613,7 @@ erts_mseg_test(UWord op, UWord a1, UWord a2, UWord a3) case 0x400: /* Have erts_mseg */ return (UWord) 1; case 0x401: - return (UWord) erts_mseg_alloc(ERTS_ALC_A_INVALID, (Uint *) a1, (Uint) 0); + return (UWord) erts_mseg_alloc(ERTS_ALC_A_INVALID, (UWord *) a1, (Uint) 0); case 0x402: erts_mseg_dealloc(ERTS_ALC_A_INVALID, (void *) a1, (Uint) a2, (Uint) 0); return (UWord) 0; @@ -1769,7 +1621,7 @@ erts_mseg_test(UWord op, UWord a1, UWord a2, UWord a3) return (UWord) erts_mseg_realloc(ERTS_ALC_A_INVALID, (void *) a1, (Uint) a2, - (Uint *) a3, + (UWord *) a3, (Uint) 0); case 0x404: erts_mseg_clear_cache(); @@ -1792,405 +1644,3 @@ erts_mseg_test(UWord op, UWord a1, UWord a2, UWord a3) } } - - -#if HALFWORD_HEAP -/* - * Very simple page oriented mmap replacer. Works in the lower - * 32 bit address range of a 64bit program. - * Implements anonymous mmap mremap and munmap with address order first fit. - * The free list is expected to be very short... - * To be used for compressed pointers in Erlang halfword emulator - * implementation. The MacOS X version is more of a toy, it's not really - * for production as the halfword erlang VM relies on Linux specific memory - * mapping tricks. - */ - -/* #define HARDDEBUG 1 */ - -#ifdef HARDDEBUG -static void dump_freelist(void) -{ - FreeBlock *p = first; - - while (p) { - fprintf(stderr, "p = %p\r\np->num = %ld\r\np->next = %p\r\n\r\n", - (void *) p, (unsigned long) p->num, (void *) p->next); - p = p->next; - } -} - -#define HARDDEBUG_HW_INCOMPLETE_ALIGNMENT(PTR, SZ) \ - fprintf(stderr,"Mapping of address %p with size %ld " \ - "does not map complete pages (%s:%d)\r\n", \ - (void *) (PTR), (unsigned long) (SZ),__FILE__, __LINE__) - -#define HARDDEBUG_HW_UNALIGNED_ALIGNMENT(PTR, SZ) \ - fprintf(stderr,"Mapping of address %p with size %ld " \ - "is not page aligned (%s:%d)\r\n", \ - (void *) (PTR), (unsigned long) (SZ),__FILE__, __LINE__) - -#define HARDDEBUG_MAP_FAILED(PTR, SZ) \ - fprintf(stderr, "Could not actually map memory " \ - "at address %p with size %ld (%s:%d) ..\r\n", \ - (void *) (PTR), (unsigned long) (SZ),__FILE__, __LINE__) -#else -#define HARDDEBUG_HW_INCOMPLETE_ALIGNMENT(PTR, SZ) do{}while(0) -#define HARDDEBUG_HW_UNALIGNED_ALIGNMENT(PTR, SZ) do{}while(0) -#define HARDDEBUG_MAP_FAILED(PTR, SZ) do{}while(0) -#endif - - -#ifdef __APPLE__ -#define MAP_ANONYMOUS MAP_ANON -#endif - -#define INIT_LOCK() do {erts_mtx_init(&pmmap_mutex, "pmmap");} while(0) - -#define TAKE_LOCK() do {erts_mtx_lock(&pmmap_mutex);} while(0) - -#define RELEASE_LOCK() do {erts_mtx_unlock(&pmmap_mutex);} while(0) - -static erts_mtx_t pmmap_mutex; /* Also needed when !USE_THREADS */ - -typedef struct _free_block { - unsigned long num; /*pages*/ - struct _free_block *next; -} FreeBlock; - -/* Protect with lock */ -static FreeBlock *first; - -static void *do_map(void *ptr, size_t sz) -{ - void *res; - - if (ALIGNED_CEILING(sz) != sz) { - HARDDEBUG_HW_INCOMPLETE_ALIGNMENT(ptr, sz); - return NULL; - } - - if (((unsigned long) ptr) % MSEG_ALIGNED_SIZE) { - HARDDEBUG_HW_UNALIGNED_ALIGNMENT(ptr, sz); - return NULL; - } - -#if HAVE_MMAP - res = mmap(ptr, sz, - PROT_READ | PROT_WRITE, MAP_PRIVATE | - MAP_ANONYMOUS | MAP_FIXED, - -1 , 0); -#else -# error "Missing mmap support" -#endif - - if (res == MAP_FAILED) { - HARDDEBUG_MAP_FAILED(ptr, sz); - return NULL; - } - - return res; -} - -static int do_unmap(void *ptr, size_t sz) -{ - void *res; - - if (ALIGNED_CEILING(sz) != sz) { - HARDDEBUG_HW_INCOMPLETE_ALIGNMENT(ptr, sz); - return 1; - } - - if (((unsigned long) ptr) % MSEG_ALIGNED_SIZE) { - HARDDEBUG_HW_UNALIGNED_ALIGNMENT(ptr, sz); - return 1; - } - - res = mmap(ptr, sz, - PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED, - -1 , 0); - - if (res == MAP_FAILED) { - HARDDEBUG_MAP_FAILED(ptr, sz); - return 1; - } - - return 0; -} - -#ifdef __APPLE__ -/* - * The first 4 gig's are protected on Macos X for 64bit processes :( - * The range 0x1000000000 - 0x10FFFFFFFF is selected as an arbitrary - * value of a normally unused range... Real MMAP's will avoid - * it and all 32bit compressed pointers can be in that range... - * More expensive than on Linux where expansion of compressed - * poiters involves no masking (as they are in the first 4 gig's). - * It's also very uncertain if the MAP_NORESERVE flag really has - * any effect in MacOS X. Swap space may always be allocated... - */ -#define SET_RANGE_MIN() /* nothing */ -#define RANGE_MIN 0x1000000000UL -#define RANGE_MAX 0x1100000000UL -#define RANGE_MASK (RANGE_MIN) -#define EXTRA_MAP_FLAGS (MAP_FIXED) -#else -static size_t range_min; -#define SET_RANGE_MIN() do { range_min = (size_t) sbrk(0); } while (0) -#define RANGE_MIN range_min -#define RANGE_MAX 0x100000000UL -#define RANGE_MASK 0UL -#define EXTRA_MAP_FLAGS (0) -#endif - -static int initialize_pmmap(void) -{ - char *p,*q,*rptr; - size_t rsz; - FreeBlock *initial; - - SET_RANGE_MIN(); - if (sizeof(void *) != 8) { - erl_exit(1,"Halfword emulator cannot be run in 32bit mode"); - } - - p = (char *) RANGE_MIN; - q = (char *) RANGE_MAX; - - rsz = ALIGNED_FLOOR(q - p); - - rptr = mmap_align(NULL, (void *) p, rsz, - PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | - MAP_NORESERVE | EXTRA_MAP_FLAGS, - -1 , 0); -#ifdef HARDDEBUG - printf("p=%p, rsz = %ld, pages = %ld, got range = %p -> %p\r\n", - p, (unsigned long) rsz, (unsigned long) (rsz / MSEG_ALIGNED_SIZE), - (void *) rptr, (void*)(rptr + rsz)); -#endif - if ((UWord)(rptr + rsz) > RANGE_MAX) { - size_t rsz_trunc = RANGE_MAX - (UWord)rptr; -#ifdef HARDDEBUG - printf("Reducing mmap'ed memory from %lu to %lu Mb, reduced range = %p -> %p\r\n", - rsz/(1024*1024), rsz_trunc/(1024*1024), rptr, rptr+rsz_trunc); -#endif - munmap((void*)RANGE_MAX, rsz - rsz_trunc); - rsz = rsz_trunc; - } - if (!do_map(rptr, MSEG_ALIGNED_SIZE)) { - erl_exit(1,"Could not actually mmap first page for halfword emulator...\n"); - } - initial = (FreeBlock *) rptr; - initial->num = (rsz / MSEG_ALIGNED_SIZE); - initial->next = NULL; - first = initial; - INIT_LOCK(); - return 0; -} - -static void *pmmap(size_t size) -{ - size_t real_size = ALIGNED_CEILING(size); - size_t num_pages = real_size / MSEG_ALIGNED_SIZE; - FreeBlock **block; - FreeBlock *tail; - FreeBlock *res; - - TAKE_LOCK(); - - for (block = &first; - *block != NULL && (*block)->num < num_pages; - block = &((*block)->next)) - ; - if (!(*block)) { - RELEASE_LOCK(); - return NULL; - } - if ((*block)->num == num_pages) { - /* nice, perfect fit */ - res = *block; - *block = (*block)->next; - } else { - tail = (FreeBlock *) (((char *) ((void *) (*block))) + real_size); - if (!do_map(tail, MSEG_ALIGNED_SIZE)) { - HARDDEBUG_MAP_FAILED(tail, MSEG_ALIGNED_SIZE); - RELEASE_LOCK(); - return NULL; - } - tail->num = (*block)->num - num_pages; - tail->next = (*block)->next; - res = *block; - *block = tail; - } - - RELEASE_LOCK(); - - if (!do_map(res, real_size)) { - HARDDEBUG_MAP_FAILED(res, real_size); - return NULL; - } - - return (void *) res; -} - -static int pmunmap(void *p, size_t size) -{ - size_t real_size = ALIGNED_CEILING(size); - size_t num_pages = real_size / MSEG_ALIGNED_SIZE; - - FreeBlock *block; - FreeBlock *last; - FreeBlock *nb = (FreeBlock *) p; - - ASSERT(((unsigned long)p & CHECK_POINTER_MASK)==0); - - if (real_size > MSEG_ALIGNED_SIZE) { - if (do_unmap(((char *) p) + MSEG_ALIGNED_SIZE, real_size - MSEG_ALIGNED_SIZE)) { - return 1; - } - } - - TAKE_LOCK(); - - last = NULL; - block = first; - while(block != NULL && ((void *) block) < p) { - last = block; - block = block->next; - } - - if (block != NULL && - ((void *) block) == ((void *) (((char *) p) + real_size))) { - /* Merge new free block with following */ - nb->num = block->num + num_pages; - nb->next = block->next; - if (do_unmap(block, MSEG_ALIGNED_SIZE)) { - RELEASE_LOCK(); - return 1; - } - } else { - /* just link in */ - nb->num = num_pages; - nb->next = block; - } - if (last != NULL) { - if (p == ((void *) (((char *) last) + (last->num * MSEG_ALIGNED_SIZE)))) { - /* Merge with previous */ - last->num += nb->num; - last->next = nb->next; - if (do_unmap(nb, MSEG_ALIGNED_SIZE)) { - RELEASE_LOCK(); - return 1; - } - } else { - last->next = nb; - } - } else { - first = nb; - } - RELEASE_LOCK(); - return 0; -} - -static void *pmremap(void *old_address, size_t old_size, - size_t new_size) -{ - size_t new_real_size = ALIGNED_CEILING(new_size); - size_t new_num_pages = new_real_size / MSEG_ALIGNED_SIZE; - size_t old_real_size = ALIGNED_CEILING(old_size); - size_t old_num_pages = old_real_size / MSEG_ALIGNED_SIZE; - if (new_num_pages == old_num_pages) { - return old_address; - } else if (new_num_pages < old_num_pages) { /* Shrink */ - size_t nfb_pages = old_num_pages - new_num_pages; - size_t nfb_real_size = old_real_size - new_real_size; - void *vnfb = (void *) (((char *)old_address) + new_real_size); - FreeBlock *nfb = (FreeBlock *) vnfb; - FreeBlock **block; - TAKE_LOCK(); - for (block = &first; - *block != NULL && (*block) < nfb; - block = &((*block)->next)) - ; - if (!(*block) || - (*block) > ((FreeBlock *)(((char *) vnfb) + nfb_real_size))) { - /* Normal link in */ - if (nfb_pages > 1) { - if (do_unmap((void *)(((char *) vnfb) + MSEG_ALIGNED_SIZE), - (nfb_pages - 1)*MSEG_ALIGNED_SIZE)) { - return NULL; - } - } - nfb->next = (*block); - nfb->num = nfb_pages; - (*block) = nfb; - } else { /* block merge */ - nfb->next = (*block)->next; - nfb->num = nfb_pages + (*block)->num; - /* unmap also the first page of the next freeblock */ - (*block) = nfb; - if (do_unmap((void *)(((char *) vnfb) + MSEG_ALIGNED_SIZE), - nfb_pages*MSEG_ALIGNED_SIZE)) { - return NULL; - } - } - RELEASE_LOCK(); - return old_address; - } else { /* Enlarge */ - FreeBlock **block; - void *old_end = (void *) (((char *)old_address) + old_real_size); - TAKE_LOCK(); - for (block = &first; - *block != NULL && (*block) < (FreeBlock *) old_address; - block = &((*block)->next)) - ; - if ((*block) == NULL || old_end > ((void *) RANGE_MAX) || - (*block) != old_end || - (*block)->num < (new_num_pages - old_num_pages)) { - /* cannot extend */ - void *result; - RELEASE_LOCK(); - result = pmmap(new_size); - if (result == NULL) { - return NULL; - } - memcpy(result,old_address,old_size); - if (pmunmap(old_address,old_size)) { - /* Oups... */ - pmunmap(result,new_size); - return NULL; - } - return result; - } else { /* extend */ - size_t remaining_pages = (*block)->num - - (new_num_pages - old_num_pages); - if (!remaining_pages) { - void *p = (void *) (((char *) (*block)) + MSEG_ALIGNED_SIZE); - void *n = (*block)->next; - size_t x = ((*block)->num - 1) * MSEG_ALIGNED_SIZE; - if (x > 0) { - if (do_map(p,x) == NULL) { - RELEASE_LOCK(); - return NULL; - } - } - (*block) = n; - } else { - FreeBlock *nfb = (FreeBlock *) ((void *) - (((char *) old_address) + - new_real_size)); - void *p = (void *) (((char *) (*block)) + MSEG_ALIGNED_SIZE); - if (do_map(p,new_real_size - old_real_size) == NULL) { - RELEASE_LOCK(); - return NULL; - } - nfb->num = remaining_pages; - nfb->next = (*block)->next; - (*block) = nfb; - } - RELEASE_LOCK(); - return old_address; - } - } -} -#endif /* HALFWORD_HEAP */ diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index 7454e5c473..2284b3f8f1 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -24,14 +24,14 @@ #include "erl_alloc_types.h" #include "erl_mmap.h" -#ifndef HAVE_MMAP -# define HAVE_MMAP 0 -#endif -#ifndef HAVE_MREMAP -# define HAVE_MREMAP 0 -#endif - -#if HAVE_MMAP +/* + * We currently only enable mseg_alloc if we got + * a genuine mmap()/munmap() primitive. It is possible + * to utilize erts_mmap() withiout a mmap support but + * alloc_util needs to be prepared before we can do + * that. + */ +#ifdef ERTS_HAVE_GENUINE_OS_MMAP # define HAVE_ERTS_MSEG 1 # define ERTS_HAVE_MSEG_SUPER_ALIGNED 1 #else @@ -40,8 +40,7 @@ #endif #if ERTS_HAVE_MSEG_SUPER_ALIGNED -# define MSEG_ALIGN_BITS (18) - /* Affects hard limits for sbct and lmbcs documented in erts_alloc.xml */ +# define MSEG_ALIGN_BITS ERTS_MMAP_SUPERALIGNED_BITS #else /* If we don't use super aligned multiblock carriers * we will mmap with page size alignment (and thus use corresponding @@ -77,7 +76,8 @@ typedef struct { 4*1024*1024, /* amcbf: Absolute max cache bad fit */ \ 20, /* rmcbf: Relative max cache bad fit */ \ 10, /* mcs: Max cache size */ \ - 1000 /* cci: Cache check interval */ \ + 1000, /* cci: Cache check interval */ \ + ERTS_MMAP_INIT_DEFAULT_INITER \ } typedef struct { @@ -93,12 +93,12 @@ typedef struct { extern const ErtsMsegOpt_t erts_mseg_default_opt; -void *erts_mseg_alloc(ErtsAlcType_t, Uint *, Uint); -void *erts_mseg_alloc_opt(ErtsAlcType_t, Uint *, Uint, const ErtsMsegOpt_t *); -void erts_mseg_dealloc(ErtsAlcType_t, void *, Uint, Uint); -void erts_mseg_dealloc_opt(ErtsAlcType_t, void *, Uint, Uint, const ErtsMsegOpt_t *); -void *erts_mseg_realloc(ErtsAlcType_t, void *, Uint, Uint *, Uint); -void *erts_mseg_realloc_opt(ErtsAlcType_t, void *, Uint, Uint *, Uint, const ErtsMsegOpt_t *); +void *erts_mseg_alloc(ErtsAlcType_t, UWord *, Uint); +void *erts_mseg_alloc_opt(ErtsAlcType_t, UWord *, Uint, const ErtsMsegOpt_t *); +void erts_mseg_dealloc(ErtsAlcType_t, void *, UWord, Uint); +void erts_mseg_dealloc_opt(ErtsAlcType_t, void *, UWord, Uint, const ErtsMsegOpt_t *); +void *erts_mseg_realloc(ErtsAlcType_t, void *, UWord, UWord *, Uint); +void *erts_mseg_realloc_opt(ErtsAlcType_t, void *, UWord, UWord *, Uint, const ErtsMsegOpt_t *); void erts_mseg_clear_cache(void); void erts_mseg_cache_check(void); Uint erts_mseg_no( const ErtsMsegOpt_t *); diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 1e01c5bc20..c9908caf20 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -112,6 +112,7 @@ static char *plusM_other_switches[] = { "Mscs", "Mscmgc", "Msco", + "Mscrpm", "Ye", "Ym", "Ytp", -- cgit v1.2.3 From 5e4a2c0cd69736ee1b1f54f8bb63a68688bc84e8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 2 Sep 2013 15:30:02 +0200 Subject: erts: Save one word in ErtsFreeSegDesc by putting red/black color bit in 'parent' pointer --- erts/emulator/sys/common/erl_mmap.c | 298 +++++++++++++++++++----------------- 1 file changed, 155 insertions(+), 143 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index aac01bf93c..cd71127ce5 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -83,16 +83,43 @@ UWord erts_page_inv_mask; typedef struct RBTNode_ RBTNode; struct RBTNode_ { - RBTNode *parent; + UWord parent_and_color; /* color in bit 0 of parent ptr */ RBTNode *left; RBTNode *right; +#ifdef HARD_DEBUG int flags; +#endif }; +#define RED_FLG (1) +#define IS_RED(N) ((N) && ((N)->parent_and_color & RED_FLG)) +#define IS_BLACK(N) (!IS_RED(N)) +#define SET_RED(N) ((N)->parent_and_color |= RED_FLG) +#define SET_BLACK(N) ((N)->parent_and_color &= ~RED_FLG) + +static ERTS_INLINE RBTNode* parent(RBTNode* node) +{ + return (RBTNode*) (node->parent_and_color & ~RED_FLG); +} + +static ERTS_INLINE void set_parent(RBTNode* node, RBTNode* parent) +{ + RBT_ASSERT(!((UWord)parent & RED_FLG)); + node->parent_and_color = ((UWord)parent) | (node->parent_and_color & RED_FLG); +} + +static ERTS_INLINE UWord parent_and_color(RBTNode* parent, int color) +{ + RBT_ASSERT(!((UWord)parent & RED_FLG)); + RBT_ASSERT(!(color & ~RED_FLG)); + return ((UWord)parent) | color; +} + + enum SortOrder { - ADDR_ORDER, - SZ_ADDR_ORDER, - SZ_REVERSE_ADDR_ORDER + ADDR_ORDER, /* only address order */ + SZ_ADDR_ORDER, /* first size then address order as tiebreaker */ + SZ_REVERSE_ADDR_ORDER /* first size then reverse address order */ }; typedef struct { @@ -100,13 +127,6 @@ typedef struct { enum SortOrder order; }RBTree; -#define RED_FLG (1) -#define IS_RED(N) ((N) && ((N)->flags & RED_FLG)) -#define IS_BLACK(N) (!IS_RED(N)) -#define SET_RED(N) ((N)->flags |= RED_FLG) -#define SET_BLACK(N) ((N)->flags &= ~RED_FLG) - -/* #define HARD_DEBUG */ #ifdef HARD_DEBUG # define HARD_CHECK_IS_MEMBER(ROOT,NODE) rbt_assert_is_member(ROOT,NODE) # define HARD_CHECK_TREE(TREE,SZ) check_tree(TREE, SZ) @@ -318,20 +338,20 @@ left_rotate(RBTNode **root, RBTNode *x) RBTNode *y = x->right; x->right = y->left; if (y->left) - y->left->parent = x; - y->parent = x->parent; - if (!y->parent) { + set_parent(y->left, x); + set_parent(y, parent(x)); + if (!parent(y)) { RBT_ASSERT(*root == x); *root = y; } - else if (x == x->parent->left) - x->parent->left = y; + else if (x == parent(x)->left) + parent(x)->left = y; else { - RBT_ASSERT(x == x->parent->right); - x->parent->right = y; + RBT_ASSERT(x == parent(x)->right); + parent(x)->right = y; } y->left = x; - x->parent = y; + set_parent(x, y); /*SVERK y->max_sz = x->max_sz; x->max_sz = node_max_size(x); @@ -344,20 +364,20 @@ right_rotate(RBTNode **root, RBTNode *x) RBTNode *y = x->left; x->left = y->right; if (y->right) - y->right->parent = x; - y->parent = x->parent; - if (!y->parent) { + set_parent(y->right, x); + set_parent(y, parent(x)); + if (!parent(y)) { RBT_ASSERT(*root == x); *root = y; } - else if (x == x->parent->right) - x->parent->right = y; + else if (x == parent(x)->right) + parent(x)->right = y; else { - RBT_ASSERT(x == x->parent->left); - x->parent->left = y; + RBT_ASSERT(x == parent(x)->left); + parent(x)->left = y; } y->right = x; - x->parent = y; + set_parent(x, y); /*SVERK y->max_sz = x->max_sz; x->max_sz = node_max_size(x); ASSERT(y->max_sz >= x->max_sz);*/ @@ -371,27 +391,27 @@ static ERTS_INLINE void replace(RBTNode **root, RBTNode *x, RBTNode *y) { - if (!x->parent) { + if (!parent(x)) { RBT_ASSERT(*root == x); *root = y; } - else if (x == x->parent->left) - x->parent->left = y; + else if (x == parent(x)->left) + parent(x)->left = y; else { - RBT_ASSERT(x == x->parent->right); - x->parent->right = y; + RBT_ASSERT(x == parent(x)->right); + parent(x)->right = y; } if (x->left) { - RBT_ASSERT(x->left->parent == x); - x->left->parent = y; + RBT_ASSERT(parent(x->left) == x); + set_parent(x->left, y); } if (x->right) { - RBT_ASSERT(x->right->parent == x); - x->right->parent = y; + RBT_ASSERT(parent(x->right) == x); + set_parent(x->right, y); } - y->flags = x->flags; - y->parent = x->parent; + /*y->flags = x->flags;*/ + y->parent_and_color = x->parent_and_color; y->right = x->right; y->left = x->left; /*SVERK y->max_sz = x->max_sz;*/ @@ -406,7 +426,7 @@ tree_insert_fixup(RBTNode** root, RBTNode *blk) * Rearrange the tree so that it satisfies the Red-Black Tree properties */ - RBT_ASSERT(x != *root && IS_RED(x->parent)); + RBT_ASSERT(x != *root && IS_RED(parent(x))); do { /* @@ -415,77 +435,77 @@ tree_insert_fixup(RBTNode** root, RBTNode *blk) */ RBT_ASSERT(IS_RED(x)); - RBT_ASSERT(IS_BLACK(x->parent->parent)); - RBT_ASSERT(x->parent->parent); + RBT_ASSERT(IS_BLACK(parent(parent(x)))); + RBT_ASSERT(parent(parent(x))); - if (x->parent == x->parent->parent->left) { - y = x->parent->parent->right; + if (parent(x) == parent(parent(x))->left) { + y = parent(parent(x))->right; if (IS_RED(y)) { SET_BLACK(y); - x = x->parent; + x = parent(x); SET_BLACK(x); - x = x->parent; + x = parent(x); SET_RED(x); } else { - if (x == x->parent->right) { - x = x->parent; + if (x == parent(x)->right) { + x = parent(x); left_rotate(root, x); } - RBT_ASSERT(x == x->parent->parent->left->left); + RBT_ASSERT(x == parent(parent(x))->left->left); RBT_ASSERT(IS_RED(x)); - RBT_ASSERT(IS_RED(x->parent)); - RBT_ASSERT(IS_BLACK(x->parent->parent)); + RBT_ASSERT(IS_RED(parent(x))); + RBT_ASSERT(IS_BLACK(parent(parent(x)))); RBT_ASSERT(IS_BLACK(y)); - SET_BLACK(x->parent); - SET_RED(x->parent->parent); - right_rotate(root, x->parent->parent); + SET_BLACK(parent(x)); + SET_RED(parent(parent(x))); + right_rotate(root, parent(parent(x))); - RBT_ASSERT(x == x->parent->left); + RBT_ASSERT(x == parent(x)->left); RBT_ASSERT(IS_RED(x)); - RBT_ASSERT(IS_RED(x->parent->right)); - RBT_ASSERT(IS_BLACK(x->parent)); + RBT_ASSERT(IS_RED(parent(x)->right)); + RBT_ASSERT(IS_BLACK(parent(x))); break; } } else { - RBT_ASSERT(x->parent == x->parent->parent->right); - y = x->parent->parent->left; + RBT_ASSERT(parent(x) == parent(parent(x))->right); + y = parent(parent(x))->left; if (IS_RED(y)) { SET_BLACK(y); - x = x->parent; + x = parent(x); SET_BLACK(x); - x = x->parent; + x = parent(x); SET_RED(x); } else { - if (x == x->parent->left) { - x = x->parent; + if (x == parent(x)->left) { + x = parent(x); right_rotate(root, x); } - RBT_ASSERT(x == x->parent->parent->right->right); + RBT_ASSERT(x == parent(parent(x))->right->right); RBT_ASSERT(IS_RED(x)); - RBT_ASSERT(IS_RED(x->parent)); - RBT_ASSERT(IS_BLACK(x->parent->parent)); + RBT_ASSERT(IS_RED(parent(x))); + RBT_ASSERT(IS_BLACK(parent(parent(x)))); RBT_ASSERT(IS_BLACK(y)); - SET_BLACK(x->parent); - SET_RED(x->parent->parent); - left_rotate(root, x->parent->parent); + SET_BLACK(parent(x)); + SET_RED(parent(parent(x))); + left_rotate(root, parent(parent(x))); - RBT_ASSERT(x == x->parent->right); + RBT_ASSERT(x == parent(x)->right); RBT_ASSERT(IS_RED(x)); - RBT_ASSERT(IS_RED(x->parent->left)); - RBT_ASSERT(IS_BLACK(x->parent)); + RBT_ASSERT(IS_RED(parent(x)->left)); + RBT_ASSERT(IS_BLACK(parent(x))); break; } } - } while (x != *root && IS_RED(x->parent)); + } while (x != *root && IS_RED(parent(x))); SET_BLACK(*root); } @@ -500,7 +520,7 @@ rbt_delete(RBTNode** root, RBTNode* del) HARD_CHECK_IS_MEMBER(*root, del); - null_x.parent = NULL; + null_x.parent_and_color = parent_and_color(NULL, !RED_FLG); /* Remove node from tree... */ @@ -514,29 +534,29 @@ rbt_delete(RBTNode** root, RBTNode* del) x = y->left ? y->left : y->right; spliced_is_black = IS_BLACK(y); if (x) { - x->parent = y->parent; + set_parent(x, parent(y)); } else if (spliced_is_black) { x = &null_x; - x->flags = 0; - SET_BLACK(x); + /*x->flags = 0; + SET_BLACK(x);*/ x->right = x->left = NULL; /*SVERK x->max_sz = 0;*/ - x->parent = y->parent; + x->parent_and_color = parent_and_color(parent(y), !RED_FLG); y->left = x; } - if (!y->parent) { + if (!parent(y)) { RBT_ASSERT(*root == y); *root = x; } else { - if (y == y->parent->left) { - y->parent->left = x; + if (y == parent(y)->left) { + parent(y)->left = x; } else { - RBT_ASSERT(y == y->parent->right); - y->parent->right = x; + RBT_ASSERT(y == parent(y)->right); + parent(y)->right = x; } /*SVERK if (y->parent != z) { lower_max_size(y->parent, (y==z ? NULL : z)); @@ -553,7 +573,7 @@ rbt_delete(RBTNode** root, RBTNode* del) /* We removed a black node which makes the resulting tree violate the Red-Black Tree properties. Fixup tree... */ - while (IS_BLACK(x) && x->parent) { + while (IS_BLACK(x) && parent(x)) { /* * x has an "extra black" which we move up the tree @@ -562,78 +582,78 @@ rbt_delete(RBTNode** root, RBTNode* del) * y is the sibbling of x */ - if (x == x->parent->left) { - y = x->parent->right; + if (x == parent(x)->left) { + y = parent(x)->right; RBT_ASSERT(y); if (IS_RED(y)) { RBT_ASSERT(y->right); RBT_ASSERT(y->left); SET_BLACK(y); - RBT_ASSERT(IS_BLACK(x->parent)); - SET_RED(x->parent); - left_rotate(root, x->parent); - y = x->parent->right; + RBT_ASSERT(IS_BLACK(parent(x))); + SET_RED(parent(x)); + left_rotate(root, parent(x)); + y = parent(x)->right; } RBT_ASSERT(y); RBT_ASSERT(IS_BLACK(y)); if (IS_BLACK(y->left) && IS_BLACK(y->right)) { SET_RED(y); - x = x->parent; + x = parent(x); } else { if (IS_BLACK(y->right)) { SET_BLACK(y->left); SET_RED(y); right_rotate(root, y); - y = x->parent->right; + y = parent(x)->right; } RBT_ASSERT(y); - if (IS_RED(x->parent)) { + if (IS_RED(parent(x))) { - SET_BLACK(x->parent); + SET_BLACK(parent(x)); SET_RED(y); } RBT_ASSERT(y->right); SET_BLACK(y->right); - left_rotate(root, x->parent); + left_rotate(root, parent(x)); x = *root; break; } } else { - RBT_ASSERT(x == x->parent->right); - y = x->parent->left; + RBT_ASSERT(x == parent(x)->right); + y = parent(x)->left; RBT_ASSERT(y); if (IS_RED(y)) { RBT_ASSERT(y->right); RBT_ASSERT(y->left); SET_BLACK(y); - RBT_ASSERT(IS_BLACK(x->parent)); - SET_RED(x->parent); - right_rotate(root, x->parent); - y = x->parent->left; + RBT_ASSERT(IS_BLACK(parent(x))); + SET_RED(parent(x)); + right_rotate(root, parent(x)); + y = parent(x)->left; } RBT_ASSERT(y); RBT_ASSERT(IS_BLACK(y)); if (IS_BLACK(y->right) && IS_BLACK(y->left)) { SET_RED(y); - x = x->parent; + x = parent(x); } else { if (IS_BLACK(y->left)) { SET_BLACK(y->right); SET_RED(y); left_rotate(root, y); - y = x->parent->left; + y = parent(x)->left; } RBT_ASSERT(y); - if (IS_RED(x->parent)) { - SET_BLACK(x->parent); + if (IS_RED(parent(x))) { + SET_BLACK(parent(x)); SET_RED(y); } RBT_ASSERT(y->left); SET_BLACK(y->left); - right_rotate(root, x->parent); + right_rotate(root, parent(x)); x = *root; break; } @@ -641,12 +661,12 @@ rbt_delete(RBTNode** root, RBTNode* del) } SET_BLACK(x); - if (null_x.parent) { - if (null_x.parent->left == &null_x) - null_x.parent->left = NULL; + if (parent(&null_x)) { + if (parent(&null_x)->left == &null_x) + parent(&null_x)->left = NULL; else { - RBT_ASSERT(null_x.parent->right == &null_x); - null_x.parent->right = NULL; + RBT_ASSERT(parent(&null_x)->right == &null_x); + parent(&null_x)->right = NULL; } RBT_ASSERT(!null_x.left); RBT_ASSERT(!null_x.right); @@ -671,14 +691,14 @@ rbt_insert(enum SortOrder order, RBTNode** root, RBTNode* blk) SWord blk_sz = desc->end - desc->start; /*SVERK Uint blk_sz = AOFF_BLK_SZ(blk);*/ - blk->flags = 0; + /*blk->flags = 0;*/ blk->left = NULL; blk->right = NULL; /*SVERK blk->max_sz = blk_sz;*/ if (!*root) { - blk->parent = NULL; - SET_BLACK(blk); + blk->parent_and_color = parent_and_color(NULL, !RED_FLG); + /*SET_BLACK(blk);*/ *root = blk; } else { @@ -692,7 +712,7 @@ rbt_insert(enum SortOrder order, RBTNode** root, RBTNode* blk) if (diff < 0) { IF_RBT_DEBUG(dbg_over = node_to_desc(order, x)); if (!x->left) { - blk->parent = x; + blk->parent_and_color = parent_and_color(x, RED_FLG); x->left = blk; break; } @@ -702,7 +722,7 @@ rbt_insert(enum SortOrder order, RBTNode** root, RBTNode* blk) RBT_ASSERT(diff > 0); IF_RBT_DEBUG(dbg_under = node_to_desc(order, x)); if (!x->right) { - blk->parent = x; + blk->parent_and_color = parent_and_color(x, RED_FLG); x->right = blk; break; } @@ -723,15 +743,16 @@ rbt_insert(enum SortOrder order, RBTNode** root, RBTNode* blk) } /* Insert block into size tree */ - RBT_ASSERT(blk->parent); + RBT_ASSERT(parent(blk)); #ifdef RBT_DEBUG if (!order) { RBT_ASSERT(!dbg_under || dbg_under->end < desc->start); RBT_ASSERT(!dbg_over || dbg_over->start > desc->end); } #endif - SET_RED(blk); - if (IS_RED(blk->parent)) + /*SET_RED(blk);*/ + RBT_ASSERT(IS_RED(blk)); + if (IS_RED(parent(blk))) tree_insert_fixup(root, blk); } /*SVERK if (flavor == AOFF_BF) { @@ -1746,20 +1767,11 @@ erts_mmap_init(ErtsMMapInit *init) mmap_state.supercarrier = 1; erts_have_erts_mmap |= ERTS_HAVE_ERTS_SUPERCARRIER_MMAP; - - -#ifdef HARD_DEBUG - { - void test_it(void); - test_it(); } -#endif - } #if !ERTS_HAVE_OS_MMAP mmap_state.no_os_mmap = 1; #endif - } @@ -1773,9 +1785,9 @@ erts_mmap_init(ErtsMMapInit *init) static int rbt_assert_is_member(RBTNode* root, RBTNode* node) { while (node != root) { - RBT_ASSERT(node->parent); - RBT_ASSERT(node->parent->left == node || node->parent->right == node); - node = node->parent; + RBT_ASSERT(parent(node)); + RBT_ASSERT(parent(node)->left == node || parent(node)->right == node); + node = parent(node); } return 1; } @@ -1841,7 +1853,7 @@ check_tree(RBTree* tree, Uint size) x = tree->root; RBT_ASSERT(IS_BLACK(x)); - RBT_ASSERT(!x->parent); + RBT_ASSERT(!parent(x)); curr_blacks = 1; blacks = -1; depth = 1; @@ -1877,15 +1889,15 @@ check_tree(RBTree* tree, Uint size) RBT_ASSERT(IS_BLACK(x->left)); } - RBT_ASSERT(x->parent || x == tree->root); + RBT_ASSERT(parent(x) || x == tree->root); if (x->left) { - RBT_ASSERT(x->left->parent == x); + RBT_ASSERT(parent(x->left) == x); RBT_ASSERT(cmp_blocks(tree->order, x->left, x) < 0); } if (x->right) { - RBT_ASSERT(x->right->parent == x); + RBT_ASSERT(parent(x->right) == x); RBT_ASSERT(cmp_blocks(tree->order, x->right, x) > 0); } @@ -1923,7 +1935,7 @@ check_tree(RBTree* tree, Uint size) UNSET_RIGHT_VISITED(x); if (IS_BLACK(x)) curr_blacks--; - x = x->parent; + x = parent(x); --depth; } RBT_ASSERT(depth == 0 || (!tree->root && depth==1)); @@ -1937,6 +1949,8 @@ check_tree(RBTree* tree, Uint size) return res; } +#endif /* HARD_DEBUG */ + #ifdef PRINT_TREE #define INDENT_STEP 2 @@ -1983,13 +1997,13 @@ void test_it(void) init_free_seg_map(&map, i); insert_free_seg(&map, alloc_desc(), (char*)0x11000, (char*)0x12000); - check_tree(&map.atree, 0); check_tree(&map.stree, 0); + HARD_CHECK_TREE(&map.atree, 0); HARD_CHECK_TREE(&map.stree, 0); insert_free_seg(&map, alloc_desc(), (char*)0x13000, (char*)0x14000); - check_tree(&map.atree, 0); check_tree(&map.stree, 0); + HARD_CHECK_TREE(&map.atree, 0); HARD_CHECK_TREE(&map.stree, 0); insert_free_seg(&map, alloc_desc(), (char*)0x15000, (char*)0x17000); - check_tree(&map.atree, 0); check_tree(&map.stree, 0); + HARD_CHECK_TREE(&map.atree, 0); HARD_CHECK_TREE(&map.stree, 0); insert_free_seg(&map, alloc_desc(), (char*)0x8000, (char*)0x10000); - check_tree(&map.atree, 0); check_tree(&map.stree, 0); + HARD_CHECK_TREE(&map.atree, 0); HARD_CHECK_TREE(&map.stree, 0); desc = lookup_free_seg(&map, 0x500); ERTS_ASSERT(desc->start == (char*)(i?0x13000L:0x11000L)); @@ -2026,17 +2040,15 @@ void test_it(void) ERTS_ASSERT(d1->start == (char*)(i?0x13000L:0x11000L)); resize_free_seg(&map, d1, d1->start - 0x800, (char*)d1->end); - check_tree(&map.atree, 0); check_tree(&map.stree, 0); + HARD_CHECK_TREE(&map.atree, 0); HARD_CHECK_TREE(&map.stree, 0); d2 = lookup_free_seg(&map, 0x1200); ERTS_ASSERT(d2 == d1); delete_free_seg(&map, d1); - check_tree(&map.atree, 0); check_tree(&map.stree, 0); + HARD_CHECK_TREE(&map.atree, 0); HARD_CHECK_TREE(&map.stree, 0); d1 = lookup_free_seg(&map, 0x1200); ERTS_ASSERT(d1->start == (char*)0x15000); } } - -#endif /* HARD_DEBUG */ -- cgit v1.2.3 From 97f11582fba9e8b141764273b94bd1ccee3d9b08 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 2 Sep 2013 16:22:32 +0200 Subject: erts: Remove HARD_DEBUG flags for tree traversal --- erts/emulator/sys/common/erl_mmap.c | 120 ++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 66 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index cd71127ce5..77c6abadf9 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -86,9 +86,6 @@ struct RBTNode_ { UWord parent_and_color; /* color in bit 0 of parent ptr */ RBTNode *left; RBTNode *right; -#ifdef HARD_DEBUG - int flags; -#endif }; #define RED_FLG (1) @@ -1792,22 +1789,6 @@ static int rbt_assert_is_member(RBTNode* root, RBTNode* node) return 1; } -#define LEFT_VISITED_FLG 0x1000 -#define THIS_VISITED_FLG 0x100 -#define RIGHT_VISITED_FLG 0x10 -#define IS_LEFT_VISITED(FB) ((FB)->flags & LEFT_VISITED_FLG) -#define IS_THIS_VISITED(FB) ((FB)->flags & THIS_VISITED_FLG) -#define IS_RIGHT_VISITED(FB) ((FB)->flags & RIGHT_VISITED_FLG) - -#define SET_LEFT_VISITED(FB) ((FB)->flags |= LEFT_VISITED_FLG) -#define SET_THIS_VISITED(FB) ((FB)->flags |= THIS_VISITED_FLG) -#define SET_RIGHT_VISITED(FB) ((FB)->flags |= RIGHT_VISITED_FLG) - -#define UNSET_LEFT_VISITED(FB) ((FB)->flags &= ~LEFT_VISITED_FLG) -#define UNSET_THIS_VISITED(FB) ((FB)->flags &= ~THIS_VISITED_FLG) -#define UNSET_RIGHT_VISITED(FB) ((FB)->flags &= ~RIGHT_VISITED_FLG) - - #if 1 /*SVERK*/ # define PRINT_TREE @@ -1843,6 +1824,7 @@ check_tree(RBTree* tree, Uint size) Uint depth, max_depth, node_cnt; ErtsFreeSegDesc* seg = NULL; ErtsFreeSegDesc* prev_seg = NULL; + enum { RECURSE_LEFT, CHECK_NODE, RECURSE_RIGHT, RETURN_TO_PARENT }state; #ifdef PRINT_TREE print_tree(tree->order, tree->root); @@ -1859,27 +1841,29 @@ check_tree(RBTree* tree, Uint size) depth = 1; max_depth = 0; node_cnt = 0; + state = RECURSE_LEFT; /* Traverse tree in sorting order */ while (x) { - if (!IS_LEFT_VISITED(x)) { - SET_LEFT_VISITED(x); - if (x->left) { - x = x->left; - ++depth; - if (IS_BLACK(x)) - curr_blacks++; - continue; - } - else { - if (blacks < 0) - blacks = curr_blacks; - RBT_ASSERT(blacks == curr_blacks); - } - } + switch (state) { + case RECURSE_LEFT: + if (x->left) { + RBT_ASSERT(parent(x->left) == x); + x = x->left; + ++depth; + if (IS_BLACK(x)) + curr_blacks++; + state = RECURSE_LEFT; + } + else { + if (blacks < 0) + blacks = curr_blacks; + RBT_ASSERT(blacks == curr_blacks); + state = CHECK_NODE; + } + break; - if (!IS_THIS_VISITED(x)) { - SET_THIS_VISITED(x); + case CHECK_NODE: ++node_cnt; if (depth > max_depth) max_depth = depth; @@ -1892,12 +1876,9 @@ check_tree(RBTree* tree, Uint size) RBT_ASSERT(parent(x) || x == tree->root); if (x->left) { - RBT_ASSERT(parent(x->left) == x); RBT_ASSERT(cmp_blocks(tree->order, x->left, x) < 0); } - if (x->right) { - RBT_ASSERT(parent(x->right) == x); RBT_ASSERT(cmp_blocks(tree->order, x->right, x) > 0); } @@ -1912,40 +1893,47 @@ check_tree(RBTree* tree, Uint size) RBT_ASSERT(!prev_seg || prev_seg->end < seg->start); prev_seg = seg; } + state = RECURSE_RIGHT; + break; - } - if (!IS_RIGHT_VISITED(x)) { - SET_RIGHT_VISITED(x); - if (x->right) { - x = x->right; - ++depth; - if (IS_BLACK(x)) - curr_blacks++; - continue; - } - else { - if (blacks < 0) - blacks = curr_blacks; - RBT_ASSERT(blacks == curr_blacks); - } - } + case RECURSE_RIGHT: + if (x->right) { + RBT_ASSERT(parent(x->right) == x); + x = x->right; + ++depth; + if (IS_BLACK(x)) + curr_blacks++; + state = RECURSE_LEFT; + } + else { + if (blacks < 0) + blacks = curr_blacks; + RBT_ASSERT(blacks == curr_blacks); + state = RETURN_TO_PARENT; + } + break; - UNSET_LEFT_VISITED(x); - UNSET_THIS_VISITED(x); - UNSET_RIGHT_VISITED(x); - if (IS_BLACK(x)) - curr_blacks--; - x = parent(x); - --depth; + case RETURN_TO_PARENT: + if (parent(x)) { + if (x == parent(x)->left) { + state = CHECK_NODE; + } + else { + RBT_ASSERT(x == parent(x)->right); + state = RETURN_TO_PARENT; + } + } + if (IS_BLACK(x)) + curr_blacks--; + x = parent(x); + --depth; + break; + } } RBT_ASSERT(depth == 0 || (!tree->root && depth==1)); RBT_ASSERT(curr_blacks == 0); RBT_ASSERT((1 << (max_depth/2)) <= node_cnt); - UNSET_LEFT_VISITED(tree->root); - UNSET_THIS_VISITED(tree->root); - UNSET_RIGHT_VISITED(tree->root); - return res; } -- cgit v1.2.3 From 9fe8915784a23622577a8bc8604e809d34623c94 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 3 Sep 2013 11:25:36 +0200 Subject: erts: Add build_free_seg_list --- erts/emulator/sys/common/erl_mmap.c | 136 +++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 77c6abadf9..5624ec9813 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -21,9 +21,10 @@ #endif #include "sys.h" -#include +#include "erl_process.h" #include "erl_smp.h" #include "erl_mmap.h" +#include #if defined(DEBUG) || 0 # undef ERTS_MMAP_DEBUG @@ -145,6 +146,7 @@ typedef struct { typedef struct { RBTree stree; RBTree atree; + Uint nseg; }ErtsFreeSegMap; static struct { @@ -758,6 +760,96 @@ rbt_insert(enum SortOrder order, RBTNode** root, RBTNode* blk) }*/ } +/* + * Traverse tree in (reverse) sorting order + */ +static void +rbt_foreach_node(RBTree* tree, + void (*fn)(RBTNode*,void*), + void* arg, int reverse) +{ +#ifdef HARD_DEBUG + Sint blacks = -1; + Sint curr_blacks = 1; + Uint depth = 1; +#endif + enum { RECURSE_LEFT, DO_NODE, RECURSE_RIGHT, RETURN_TO_PARENT }state; + RBTNode *x = tree->root; + + RBT_ASSERT(!parent(x)); + + state = reverse ? RECURSE_RIGHT : RECURSE_LEFT; + while (x) { + switch (state) { + case RECURSE_LEFT: + if (x->left) { + RBT_ASSERT(parent(x->left) == x); + #ifdef HARD_DEBUG + ++depth; + if (IS_BLACK(x->left)) + curr_blacks++; + #endif + x = x->left; + state = reverse ? RECURSE_RIGHT : RECURSE_LEFT; + } + else { + #ifdef HARD_DEBUG + if (blacks < 0) + blacks = curr_blacks; + RBT_ASSERT(blacks == curr_blacks); + #endif + state = reverse ? RETURN_TO_PARENT : DO_NODE; + } + break; + + case DO_NODE: + (*fn) (x, arg); + state = reverse ? RECURSE_LEFT : RECURSE_RIGHT; + break; + + case RECURSE_RIGHT: + if (x->right) { + RBT_ASSERT(parent(x->right) == x); + #ifdef HARD_DEBUG + ++depth; + if (IS_BLACK(x->right)) + curr_blacks++; + #endif + x = x->right; + state = reverse ? RECURSE_RIGHT : RECURSE_LEFT; + } + else { + #ifdef HARD_DEBUG + if (blacks < 0) + blacks = curr_blacks; + RBT_ASSERT(blacks == curr_blacks); + #endif + state = reverse ? DO_NODE : RETURN_TO_PARENT; + } + break; + + case RETURN_TO_PARENT: + #ifdef HARD_DEBUG + if (IS_BLACK(x)) + curr_blacks--; + --depth; + #endif + if (parent(x)) { + if (x == parent(x)->left) { + state = reverse ? RETURN_TO_PARENT : DO_NODE; + } + else { + RBT_ASSERT(x == parent(x)->right); + state = reverse ? DO_NODE : RETURN_TO_PARENT; + } + } + x = parent(x); + break; + } + } +} + + /* The API to keep track of a bunch of separated free segments (non-overlapping and non-adjacent). @@ -777,6 +869,7 @@ static void init_free_seg_map(ErtsFreeSegMap* map, int reverse_ao) map->atree.order = ADDR_ORDER; map->stree.root = NULL; map->stree.order = reverse_ao ? SZ_REVERSE_ADDR_ORDER : SZ_ADDR_ORDER; + map->nseg = 0; } static void adjacent_free_seg(ErtsFreeSegMap* map, char* start, char* end, @@ -813,6 +906,7 @@ static void insert_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc, desc->end = end; rbt_insert(map->atree.order, &map->atree.root, &desc->anode); rbt_insert(map->stree.order, &map->stree.root, &desc->snode); + map->nseg++; } static void resize_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc, @@ -835,6 +929,7 @@ static void delete_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc) { rbt_delete(&map->atree.root, &desc->anode); rbt_delete(&map->stree.root, &desc->snode); + map->nseg--; } static ErtsFreeSegDesc* lookup_free_seg(ErtsFreeSegMap* map, SWord need_sz) @@ -857,6 +952,44 @@ static ErtsFreeSegDesc* lookup_free_seg(ErtsFreeSegMap* map, SWord need_sz) return best_desc; } +struct build_arg_t +{ + Process* p; + Eterm* hp; + Eterm acc; +}; + +static void build_free_seg_tuple(RBTNode* node, void* arg) +{ + struct build_arg_t* a = (struct build_arg_t*)arg; + ErtsFreeSegDesc* desc = anode_to_desc(node); + Eterm start= erts_bld_uword(&a->hp, NULL, (UWord)desc->start); + Eterm end = erts_bld_uword(&a->hp, NULL, (UWord)desc->end); + Eterm tpl = TUPLE2(a->hp, start, end); + + a->hp += 3; + a->acc = CONS(a->hp, tpl, a->acc); + a->hp += 2; +} + +static +Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map) +{ + struct build_arg_t barg; + Eterm* hp_end; + const Uint may_need = map->nseg * (2 + 3 + 2*2); /* cons + tuple + bigs */ + + barg.p = p; + barg.hp = HAlloc(p, may_need); + hp_end = barg.hp + may_need; + barg.acc = NIL; + rbt_foreach_node(&map->atree, build_free_seg_tuple, &barg, 1); + + RBT_ASSERT(barg.hp <= hp_end); + HRelease(p, hp_end, barg.hp); + return barg.acc; +} + #if ERTS_HAVE_OS_MMAP /* Implementation of os_mmap()/os_munmap()/os_mremap()... */ @@ -1975,6 +2108,7 @@ print_tree(enum SortOrder order, RBTNode* root) #endif /* PRINT_TREE */ + void test_it(void) { ErtsFreeSegMap map; -- cgit v1.2.3 From 0d9c5c44a46810f0d9f45a533ca8a3754c11c643 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 3 Sep 2013 17:18:34 +0200 Subject: erts: Use rbt_foreach_node to check_tree --- erts/emulator/sys/common/erl_mmap.c | 161 +++++++++++++----------------------- 1 file changed, 58 insertions(+), 103 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 5624ec9813..2d749b2402 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -772,6 +772,8 @@ rbt_foreach_node(RBTree* tree, Sint blacks = -1; Sint curr_blacks = 1; Uint depth = 1; + Uint max_depth = 0; + Uint node_cnt = 0; #endif enum { RECURSE_LEFT, DO_NODE, RECURSE_RIGHT, RETURN_TO_PARENT }state; RBTNode *x = tree->root; @@ -803,7 +805,12 @@ rbt_foreach_node(RBTree* tree, break; case DO_NODE: - (*fn) (x, arg); + #ifdef HARD_DEBUG + ++node_cnt; + if (depth > max_depth) + max_depth = depth; + #endif + (*fn) (x, arg); /* Do it! */ state = reverse ? RECURSE_LEFT : RECURSE_RIGHT; break; @@ -847,6 +854,11 @@ rbt_foreach_node(RBTree* tree, break; } } +#ifdef HARD_DEBUG + RBT_ASSERT(depth == 0 || (!tree->root && depth==1)); + RBT_ASSERT(curr_blacks == 0); + RBT_ASSERT((1 << (max_depth/2)) <= node_cnt); +#endif } @@ -1947,127 +1959,70 @@ static void print_tree(enum SortOrder order, RBTNode*); * */ +struct check_arg_t { + RBTree* tree; + ErtsFreeSegDesc* prev_seg; + Uint size; + RBTNode *res; +}; +static void check_node(RBTNode* x, void* arg); + + static RBTNode * check_tree(RBTree* tree, Uint size) { - RBTNode *res = NULL; - Sint blacks; - Sint curr_blacks; - RBTNode *x; - Uint depth, max_depth, node_cnt; - ErtsFreeSegDesc* seg = NULL; - ErtsFreeSegDesc* prev_seg = NULL; - enum { RECURSE_LEFT, CHECK_NODE, RECURSE_RIGHT, RETURN_TO_PARENT }state; + struct check_arg_t carg; + carg.tree = tree; + carg.prev_seg = NULL; + carg.size = size; + carg.res = NULL; #ifdef PRINT_TREE print_tree(tree->order, tree->root); #endif if (!tree->root) - return res; + return NULL; - x = tree->root; - RBT_ASSERT(IS_BLACK(x)); - RBT_ASSERT(!parent(x)); - curr_blacks = 1; - blacks = -1; - depth = 1; - max_depth = 0; - node_cnt = 0; - state = RECURSE_LEFT; - - /* Traverse tree in sorting order */ - while (x) { - switch (state) { - case RECURSE_LEFT: - if (x->left) { - RBT_ASSERT(parent(x->left) == x); - x = x->left; - ++depth; - if (IS_BLACK(x)) - curr_blacks++; - state = RECURSE_LEFT; - } - else { - if (blacks < 0) - blacks = curr_blacks; - RBT_ASSERT(blacks == curr_blacks); - state = CHECK_NODE; - } - break; + RBT_ASSERT(IS_BLACK(tree->root)); + RBT_ASSERT(!parent(tree->root)); - case CHECK_NODE: - ++node_cnt; - if (depth > max_depth) - max_depth = depth; + rbt_foreach_node(tree, check_node, &carg, 0); - if (IS_RED(x)) { - RBT_ASSERT(IS_BLACK(x->right)); - RBT_ASSERT(IS_BLACK(x->left)); - } + return carg.res; +} - RBT_ASSERT(parent(x) || x == tree->root); +/* callback */ +static void check_node(RBTNode* x, void* arg) +{ + struct check_arg_t* a = (struct check_arg_t*) arg; + ErtsFreeSegDesc* seg; - if (x->left) { - RBT_ASSERT(cmp_blocks(tree->order, x->left, x) < 0); - } - if (x->right) { - RBT_ASSERT(cmp_blocks(tree->order, x->right, x) > 0); - } + if (IS_RED(x)) { + RBT_ASSERT(IS_BLACK(x->right)); + RBT_ASSERT(IS_BLACK(x->left)); + } - seg = node_to_desc(tree->order, x); - RBT_ASSERT(seg->start < seg->end); - if (size && (seg->end - seg->start) >= size) { - if (!res || cmp_blocks(tree->order, x, res) < 0) { - res = x; - } - } - if (tree->order == ADDR_ORDER) { - RBT_ASSERT(!prev_seg || prev_seg->end < seg->start); - prev_seg = seg; - } - state = RECURSE_RIGHT; - break; + RBT_ASSERT(parent(x) || x == a->tree->root); - case RECURSE_RIGHT: - if (x->right) { - RBT_ASSERT(parent(x->right) == x); - x = x->right; - ++depth; - if (IS_BLACK(x)) - curr_blacks++; - state = RECURSE_LEFT; - } - else { - if (blacks < 0) - blacks = curr_blacks; - RBT_ASSERT(blacks == curr_blacks); - state = RETURN_TO_PARENT; - } - break; + if (x->left) { + RBT_ASSERT(cmp_blocks(a->tree->order, x->left, x) < 0); + } + if (x->right) { + RBT_ASSERT(cmp_blocks(a->tree->order, x->right, x) > 0); + } - case RETURN_TO_PARENT: - if (parent(x)) { - if (x == parent(x)->left) { - state = CHECK_NODE; - } - else { - RBT_ASSERT(x == parent(x)->right); - state = RETURN_TO_PARENT; - } - } - if (IS_BLACK(x)) - curr_blacks--; - x = parent(x); - --depth; - break; + seg = node_to_desc(a->tree->order, x); + RBT_ASSERT(seg->start < seg->end); + if (a->size && (seg->end - seg->start) >= a->size) { + if (!a->res || cmp_blocks(a->tree->order, x, a->res) < 0) { + a->res = x; } } - RBT_ASSERT(depth == 0 || (!tree->root && depth==1)); - RBT_ASSERT(curr_blacks == 0); - RBT_ASSERT((1 << (max_depth/2)) <= node_cnt); - - return res; + if (a->tree->order == ADDR_ORDER) { + RBT_ASSERT(!a->prev_seg || a->prev_seg->end < seg->start); + a->prev_seg = seg; + } } #endif /* HARD_DEBUG */ -- cgit v1.2.3 From 984f0e42665e9fa09467145976183bf88f8f3da8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 3 Sep 2013 19:54:38 +0200 Subject: erts: Optimize rb-tree operations by "caching" parent ptr --- erts/emulator/sys/common/erl_mmap.c | 152 +++++++++++++++++++----------------- 1 file changed, 81 insertions(+), 71 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 2d749b2402..ede7c66fdc 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -419,13 +419,14 @@ replace(RBTNode **root, RBTNode *x, RBTNode *y) static void tree_insert_fixup(RBTNode** root, RBTNode *blk) { - RBTNode *x = blk, *y; + RBTNode *x = blk, *y, *papa_x, *granpa_x; /* * Rearrange the tree so that it satisfies the Red-Black Tree properties */ - RBT_ASSERT(x != *root && IS_RED(parent(x))); + papa_x = parent(x); + RBT_ASSERT(x != *root && IS_RED(papa_x)); do { /* @@ -433,35 +434,36 @@ tree_insert_fixup(RBTNode** root, RBTNode *blk) * until we get to the root or until we can separate them. */ + granpa_x = parent(papa_x); RBT_ASSERT(IS_RED(x)); - RBT_ASSERT(IS_BLACK(parent(parent(x)))); - RBT_ASSERT(parent(parent(x))); + RBT_ASSERT(IS_BLACK(granpa_x)); + RBT_ASSERT(granpa_x); - if (parent(x) == parent(parent(x))->left) { - y = parent(parent(x))->right; + if (papa_x == granpa_x->left) { + y = granpa_x->right; if (IS_RED(y)) { SET_BLACK(y); - x = parent(x); - SET_BLACK(x); - x = parent(x); - SET_RED(x); + SET_BLACK(papa_x); + SET_RED(granpa_x); + x = granpa_x; } else { - if (x == parent(x)->right) { - x = parent(x); - left_rotate(root, x); + if (x == papa_x->right) { + left_rotate(root, papa_x); + papa_x = x; + x = papa_x->left; } - RBT_ASSERT(x == parent(parent(x))->left->left); + RBT_ASSERT(x == granpa_x->left->left); RBT_ASSERT(IS_RED(x)); - RBT_ASSERT(IS_RED(parent(x))); - RBT_ASSERT(IS_BLACK(parent(parent(x)))); + RBT_ASSERT(IS_RED(papa_x)); + RBT_ASSERT(IS_BLACK(granpa_x)); RBT_ASSERT(IS_BLACK(y)); - SET_BLACK(parent(x)); - SET_RED(parent(parent(x))); - right_rotate(root, parent(parent(x))); + SET_BLACK(papa_x); + SET_RED(granpa_x); + right_rotate(root, granpa_x); RBT_ASSERT(x == parent(x)->left); RBT_ASSERT(IS_RED(x)); @@ -471,31 +473,31 @@ tree_insert_fixup(RBTNode** root, RBTNode *blk) } } else { - RBT_ASSERT(parent(x) == parent(parent(x))->right); - y = parent(parent(x))->left; + RBT_ASSERT(papa_x == granpa_x->right); + y = granpa_x->left; if (IS_RED(y)) { SET_BLACK(y); - x = parent(x); - SET_BLACK(x); - x = parent(x); - SET_RED(x); + SET_BLACK(papa_x); + SET_RED(granpa_x); + x = granpa_x; } else { - if (x == parent(x)->left) { - x = parent(x); - right_rotate(root, x); + if (x == papa_x->left) { + right_rotate(root, papa_x); + papa_x = x; + x = papa_x->right; } - RBT_ASSERT(x == parent(parent(x))->right->right); + RBT_ASSERT(x == granpa_x->right->right); RBT_ASSERT(IS_RED(x)); - RBT_ASSERT(IS_RED(parent(x))); - RBT_ASSERT(IS_BLACK(parent(parent(x)))); + RBT_ASSERT(IS_RED(papa_x)); + RBT_ASSERT(IS_BLACK(granpa_x)); RBT_ASSERT(IS_BLACK(y)); - SET_BLACK(parent(x)); - SET_RED(parent(parent(x))); - left_rotate(root, parent(parent(x))); + SET_BLACK(papa_x); + SET_RED(granpa_x); + left_rotate(root, granpa_x); RBT_ASSERT(x == parent(x)->right); RBT_ASSERT(IS_RED(x)); @@ -504,7 +506,7 @@ tree_insert_fixup(RBTNode** root, RBTNode *blk) break; } } - } while (x != *root && IS_RED(parent(x))); + } while (x != *root && (papa_x=parent(x), IS_RED(papa_x))); SET_BLACK(*root); } @@ -513,7 +515,7 @@ static void rbt_delete(RBTNode** root, RBTNode* del) { Uint spliced_is_black; - RBTNode *x, *y, *z = del; + RBTNode *x, *y, *z = del, *papa_y; RBTNode null_x; /* null_x is used to get the fixup started when we splice out a node without children. */ @@ -532,8 +534,9 @@ rbt_delete(RBTNode** root, RBTNode* del) /* splice out y */ x = y->left ? y->left : y->right; spliced_is_black = IS_BLACK(y); + papa_y = parent(y); if (x) { - set_parent(x, parent(y)); + set_parent(x, papa_y); } else if (spliced_is_black) { x = &null_x; @@ -541,21 +544,21 @@ rbt_delete(RBTNode** root, RBTNode* del) SET_BLACK(x);*/ x->right = x->left = NULL; /*SVERK x->max_sz = 0;*/ - x->parent_and_color = parent_and_color(parent(y), !RED_FLG); + x->parent_and_color = parent_and_color(papa_y, !RED_FLG); y->left = x; } - if (!parent(y)) { + if (!papa_y) { RBT_ASSERT(*root == y); *root = x; } else { - if (y == parent(y)->left) { - parent(y)->left = x; + if (y == papa_y->left) { + papa_y->left = x; } else { - RBT_ASSERT(y == parent(y)->right); - parent(y)->right = x; + RBT_ASSERT(y == papa_y->right); + papa_y->right = x; } /*SVERK if (y->parent != z) { lower_max_size(y->parent, (y==z ? NULL : z)); @@ -569,10 +572,12 @@ rbt_delete(RBTNode** root, RBTNode* del) } if (spliced_is_black) { + RBTNode* papa_x; /* We removed a black node which makes the resulting tree violate the Red-Black Tree properties. Fixup tree... */ - while (IS_BLACK(x) && parent(x)) { + papa_x = parent(x); + while (IS_BLACK(x) && papa_x) { /* * x has an "extra black" which we move up the tree @@ -581,91 +586,96 @@ rbt_delete(RBTNode** root, RBTNode* del) * y is the sibbling of x */ - if (x == parent(x)->left) { - y = parent(x)->right; + if (x == papa_x->left) { + y = papa_x->right; RBT_ASSERT(y); if (IS_RED(y)) { RBT_ASSERT(y->right); RBT_ASSERT(y->left); SET_BLACK(y); - RBT_ASSERT(IS_BLACK(parent(x))); - SET_RED(parent(x)); - left_rotate(root, parent(x)); - y = parent(x)->right; + RBT_ASSERT(IS_BLACK(papa_x)); + SET_RED(papa_x); + left_rotate(root, papa_x); + RBT_ASSERT(papa_x == parent(x)); + y = papa_x->right; } RBT_ASSERT(y); RBT_ASSERT(IS_BLACK(y)); if (IS_BLACK(y->left) && IS_BLACK(y->right)) { SET_RED(y); - x = parent(x); } else { if (IS_BLACK(y->right)) { SET_BLACK(y->left); SET_RED(y); right_rotate(root, y); - y = parent(x)->right; + RBT_ASSERT(papa_x == parent(x)); + y = papa_x->right; } RBT_ASSERT(y); - if (IS_RED(parent(x))) { + if (IS_RED(papa_x)) { - SET_BLACK(parent(x)); + SET_BLACK(papa_x); SET_RED(y); } RBT_ASSERT(y->right); SET_BLACK(y->right); - left_rotate(root, parent(x)); + left_rotate(root, papa_x); x = *root; break; } } else { - RBT_ASSERT(x == parent(x)->right); - y = parent(x)->left; + RBT_ASSERT(x == papa_x->right); + y = papa_x->left; RBT_ASSERT(y); if (IS_RED(y)) { RBT_ASSERT(y->right); RBT_ASSERT(y->left); SET_BLACK(y); - RBT_ASSERT(IS_BLACK(parent(x))); - SET_RED(parent(x)); - right_rotate(root, parent(x)); - y = parent(x)->left; + RBT_ASSERT(IS_BLACK(papa_x)); + SET_RED(papa_x); + right_rotate(root, papa_x); + RBT_ASSERT(papa_x == parent(x)); + y = papa_x->left; } RBT_ASSERT(y); RBT_ASSERT(IS_BLACK(y)); if (IS_BLACK(y->right) && IS_BLACK(y->left)) { SET_RED(y); - x = parent(x); } else { if (IS_BLACK(y->left)) { SET_BLACK(y->right); SET_RED(y); left_rotate(root, y); - y = parent(x)->left; + RBT_ASSERT(papa_x == parent(x)); + y = papa_x->left; } RBT_ASSERT(y); - if (IS_RED(parent(x))) { - SET_BLACK(parent(x)); + if (IS_RED(papa_x)) { + SET_BLACK(papa_x); SET_RED(y); } RBT_ASSERT(y->left); SET_BLACK(y->left); - right_rotate(root, parent(x)); + right_rotate(root, papa_x); x = *root; break; } } + x = papa_x; + papa_x = parent(x); } SET_BLACK(x); - if (parent(&null_x)) { - if (parent(&null_x)->left == &null_x) - parent(&null_x)->left = NULL; + papa_x = parent(&null_x); + if (papa_x) { + if (papa_x->left == &null_x) + papa_x->left = NULL; else { - RBT_ASSERT(parent(&null_x)->right == &null_x); - parent(&null_x)->right = NULL; + RBT_ASSERT(papa_x->right == &null_x); + papa_x->right = NULL; } RBT_ASSERT(!null_x.left); RBT_ASSERT(!null_x.right); -- cgit v1.2.3 From e0e17136506f9c8363b46a991012422980925dd1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 4 Sep 2013 14:56:49 +0200 Subject: erts: Add __func__ to ERTS_ASSERT macro --- erts/emulator/beam/sys.h | 4 ++-- erts/emulator/sys/common/erl_mmap.c | 13 +------------ erts/emulator/sys/unix/sys.c | 6 +++--- erts/emulator/sys/win32/sys.c | 2 +- 4 files changed, 7 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index dfe60d8ea0..9561c0be96 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -150,8 +150,8 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType; #endif #define ERTS_ASSERT(e) \ - ((void) ((e) ? 1 : (erl_assert_error(#e, __FILE__, __LINE__), 0))) -void erl_assert_error(char* expr, char* file, int line); + ((void) ((e) ? 1 : (erl_assert_error(#e, __func__, __FILE__, __LINE__), 0))) +void erl_assert_error(const char* expr, const char *func, const char* file, int line); #ifdef DEBUG # define ASSERT(e) ERTS_ASSERT(e) diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index ede7c66fdc..58795bd3a6 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -34,18 +34,7 @@ /* #define ERTS_MMAP_DEBUG_FILL_AREAS */ #ifdef ERTS_MMAP_DEBUG -# define ERTS_MMAP_ASSERT(A) \ - ((void) (!(A) \ - ? erts_mmap_assert_failed(#A, __func__, __FILE__, __LINE__)\ - : 1)) -static int -erts_mmap_assert_failed(const char *a, const char *func, const char *file, int line) -{ - erts_fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", - (char *) file, line, (char *) func, (char *) a); - abort(); - return 0; -} +# define ERTS_MMAP_ASSERT ERTS_ASSERT #else # define ERTS_MMAP_ASSERT(A) ((void) 1) #endif diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 47991756df..401b37b9d2 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -2640,11 +2640,11 @@ int fd; extern int erts_initialized; void -erl_assert_error(char* expr, char* file, int line) +erl_assert_error(const char* expr, const char* func, const char* file, int line) { fflush(stdout); - fprintf(stderr, "Assertion failed: %s in %s, line %d\n", - expr, file, line); + fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", + file, line, func, expr); fflush(stderr); #if !defined(ERTS_SMP) && 0 /* Writing a crashdump from a failed assertion when smp support diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 6a1d6b08f4..99951bb45e 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -3224,7 +3224,7 @@ erl_bin_write(buf, sz, max) } void -erl_assert_error(char* expr, char* file, int line) +erl_assert_error(const char* expr, const char* func, const char* file, int line) { char message[1024]; -- cgit v1.2.3 From 5295a138518df6c9f4907a2fb9afb1282b96ffb7 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 4 Sep 2013 15:00:30 +0200 Subject: erts: Cleanup erl_mmap --- erts/emulator/sys/common/erl_mmap.c | 233 +++++++++++++++++++----------------- 1 file changed, 121 insertions(+), 112 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 58795bd3a6..723efa772f 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -126,15 +126,15 @@ static RBTNode* check_tree(RBTree* tree, Uint); typedef struct { - RBTNode snode; - RBTNode anode; + RBTNode snode; /* node in 'stree' */ + RBTNode anode; /* node in 'atree' */ char* start; char* end; }ErtsFreeSegDesc; typedef struct { - RBTree stree; - RBTree atree; + RBTree stree; /* size ordered tree */ + RBTree atree; /* address ordered tree */ Uint nseg; }ErtsFreeSegMap; @@ -277,8 +277,8 @@ static ERTS_INLINE ErtsFreeSegDesc* node_to_desc(enum SortOrder order, RBTNode* } #ifdef HARD_DEBUG -static ERTS_INLINE SWord cmp_blocks(enum SortOrder order, - RBTNode* lhs, RBTNode* rhs) +static ERTS_INLINE SWord cmp_nodes(enum SortOrder order, + RBTNode* lhs, RBTNode* rhs) { ErtsFreeSegDesc* ldesc = node_to_desc(order, lhs); ErtsFreeSegDesc* rdesc = node_to_desc(order, rhs); @@ -296,10 +296,10 @@ static ERTS_INLINE SWord cmp_blocks(enum SortOrder order, return (char*)rdesc->start - (char*)ldesc->start; } } -#endif +#endif /* HARD_DEBUG */ -static ERTS_INLINE SWord cmp_with_block(enum SortOrder order, - SWord sz, char* addr, RBTNode* rhs) +static ERTS_INLINE SWord cmp_with_node(enum SortOrder order, + SWord sz, char* addr, RBTNode* rhs) { ErtsFreeSegDesc* rdesc; if (order != ADDR_ORDER) { @@ -340,10 +340,6 @@ left_rotate(RBTNode **root, RBTNode *x) } y->left = x; set_parent(x, y); - - /*SVERK y->max_sz = x->max_sz; - x->max_sz = node_max_size(x); - ASSERT(y->max_sz >= x->max_sz);*/ } static ERTS_INLINE void @@ -366,14 +362,11 @@ right_rotate(RBTNode **root, RBTNode *x) } y->right = x; set_parent(x, y); - /*SVERK y->max_sz = x->max_sz; - x->max_sz = node_max_size(x); - ASSERT(y->max_sz >= x->max_sz);*/ } /* * Replace node x with node y - * NOTE: block header of y is not changed + * NOTE: segment descriptor of y is not changed */ static ERTS_INLINE void replace(RBTNode **root, RBTNode *x, RBTNode *y) @@ -398,17 +391,15 @@ replace(RBTNode **root, RBTNode *x, RBTNode *y) set_parent(x->right, y); } - /*y->flags = x->flags;*/ y->parent_and_color = x->parent_and_color; y->right = x->right; y->left = x->left; - /*SVERK y->max_sz = x->max_sz;*/ } static void -tree_insert_fixup(RBTNode** root, RBTNode *blk) +tree_insert_fixup(RBTNode** root, RBTNode *node) { - RBTNode *x = blk, *y, *papa_x, *granpa_x; + RBTNode *x = node, *y, *papa_x, *granpa_x; /* * Rearrange the tree so that it satisfies the Red-Black Tree properties @@ -501,14 +492,15 @@ tree_insert_fixup(RBTNode** root, RBTNode *blk) } static void -rbt_delete(RBTNode** root, RBTNode* del) +rbt_delete(RBTree* tree, RBTNode* del) { Uint spliced_is_black; RBTNode *x, *y, *z = del, *papa_y; RBTNode null_x; /* null_x is used to get the fixup started when we splice out a node without children. */ - HARD_CHECK_IS_MEMBER(*root, del); + HARD_CHECK_IS_MEMBER(tree->root, del); + HARD_CHECK_TREE(tree, 0); null_x.parent_and_color = parent_and_color(NULL, !RED_FLG); @@ -529,17 +521,14 @@ rbt_delete(RBTNode** root, RBTNode* del) } else if (spliced_is_black) { x = &null_x; - /*x->flags = 0; - SET_BLACK(x);*/ x->right = x->left = NULL; - /*SVERK x->max_sz = 0;*/ x->parent_and_color = parent_and_color(papa_y, !RED_FLG); y->left = x; } if (!papa_y) { - RBT_ASSERT(*root == y); - *root = x; + RBT_ASSERT(tree->root == y); + tree->root = x; } else { if (y == papa_y->left) { @@ -549,15 +538,11 @@ rbt_delete(RBTNode** root, RBTNode* del) RBT_ASSERT(y == papa_y->right); papa_y->right = x; } - /*SVERK if (y->parent != z) { - lower_max_size(y->parent, (y==z ? NULL : z)); - }*/ } if (y != z) { /* We spliced out the successor of z; replace z by the successor */ RBT_ASSERT(z != &null_x); - replace(root, z, y); - /*SVERK lower_max_size(y, NULL);*/ + replace(&tree->root, z, y); } if (spliced_is_black) { @@ -584,7 +569,7 @@ rbt_delete(RBTNode** root, RBTNode* del) SET_BLACK(y); RBT_ASSERT(IS_BLACK(papa_x)); SET_RED(papa_x); - left_rotate(root, papa_x); + left_rotate(&tree->root, papa_x); RBT_ASSERT(papa_x == parent(x)); y = papa_x->right; } @@ -597,7 +582,7 @@ rbt_delete(RBTNode** root, RBTNode* del) if (IS_BLACK(y->right)) { SET_BLACK(y->left); SET_RED(y); - right_rotate(root, y); + right_rotate(&tree->root, y); RBT_ASSERT(papa_x == parent(x)); y = papa_x->right; } @@ -609,8 +594,8 @@ rbt_delete(RBTNode** root, RBTNode* del) } RBT_ASSERT(y->right); SET_BLACK(y->right); - left_rotate(root, papa_x); - x = *root; + left_rotate(&tree->root, papa_x); + x = tree->root; break; } } @@ -624,7 +609,7 @@ rbt_delete(RBTNode** root, RBTNode* del) SET_BLACK(y); RBT_ASSERT(IS_BLACK(papa_x)); SET_RED(papa_x); - right_rotate(root, papa_x); + right_rotate(&tree->root, papa_x); RBT_ASSERT(papa_x == parent(x)); y = papa_x->left; } @@ -637,7 +622,7 @@ rbt_delete(RBTNode** root, RBTNode* del) if (IS_BLACK(y->left)) { SET_BLACK(y->right); SET_RED(y); - left_rotate(root, y); + left_rotate(&tree->root, y); RBT_ASSERT(papa_x == parent(x)); y = papa_x->left; } @@ -648,8 +633,8 @@ rbt_delete(RBTNode** root, RBTNode* del) } RBT_ASSERT(y->left); SET_BLACK(y->left); - right_rotate(root, papa_x); - x = *root; + right_rotate(&tree->root, papa_x); + x = tree->root; break; } } @@ -669,49 +654,44 @@ rbt_delete(RBTNode** root, RBTNode* del) RBT_ASSERT(!null_x.left); RBT_ASSERT(!null_x.right); } - else if (*root == &null_x) { - *root = NULL; + else if (tree->root == &null_x) { + tree->root = NULL; RBT_ASSERT(!null_x.left); RBT_ASSERT(!null_x.right); } } + HARD_CHECK_TREE(tree, 0); } static void -rbt_insert(enum SortOrder order, RBTNode** root, RBTNode* blk) +rbt_insert(enum SortOrder order, RBTree* tree, RBTNode* node) { #ifdef RBT_DEBUG ErtsFreeSegDesc *dbg_under=NULL, *dbg_over=NULL; #endif - ErtsFreeSegDesc* desc = node_to_desc(order, blk); - char* blk_addr = desc->start; - SWord blk_sz = desc->end - desc->start; - /*SVERK Uint blk_sz = AOFF_BLK_SZ(blk);*/ - - /*blk->flags = 0;*/ - blk->left = NULL; - blk->right = NULL; - /*SVERK blk->max_sz = blk_sz;*/ - - if (!*root) { - blk->parent_and_color = parent_and_color(NULL, !RED_FLG); - /*SET_BLACK(blk);*/ - *root = blk; + ErtsFreeSegDesc* desc = node_to_desc(order, node); + char* seg_addr = desc->start; + SWord seg_sz = desc->end - desc->start; + + HARD_CHECK_TREE(tree, 0); + + node->left = NULL; + node->right = NULL; + + if (!tree->root) { + node->parent_and_color = parent_and_color(NULL, !RED_FLG); + tree->root = node; } else { - RBTNode *x = *root; + RBTNode *x = tree->root; while (1) { - SWord diff; - /*SVERK if (x->max_sz < blk_sz) { - x->max_sz = blk_sz; - }*/ - diff = cmp_with_block(order, blk_sz, blk_addr, x); + SWord diff = cmp_with_node(order, seg_sz, seg_addr, x); if (diff < 0) { IF_RBT_DEBUG(dbg_over = node_to_desc(order, x)); if (!x->left) { - blk->parent_and_color = parent_and_color(x, RED_FLG); - x->left = blk; + node->parent_and_color = parent_and_color(x, RED_FLG); + x->left = node; break; } x = x->left; @@ -720,43 +700,26 @@ rbt_insert(enum SortOrder order, RBTNode** root, RBTNode* blk) RBT_ASSERT(diff > 0); IF_RBT_DEBUG(dbg_under = node_to_desc(order, x)); if (!x->right) { - blk->parent_and_color = parent_and_color(x, RED_FLG); - x->right = blk; + node->parent_and_color = parent_and_color(x, RED_FLG); + x->right = node; break; } x = x->right; } - /*SVERK else { - ASSERT(flavor == AOFF_BF); - ASSERT(blk->flags & IS_BF_FLG); - ASSERT(x->flags & IS_BF_FLG); - SET_LIST_ELEM(blk); - LIST_NEXT(blk) = LIST_NEXT(x); - LIST_PREV(blk) = x; - if (LIST_NEXT(x)) - LIST_PREV(LIST_NEXT(x)) = blk; - LIST_NEXT(x) = blk; - return; - }*/ } - /* Insert block into size tree */ - RBT_ASSERT(parent(blk)); + RBT_ASSERT(parent(node)); #ifdef RBT_DEBUG - if (!order) { + if (order == ADDR_ORDER) { RBT_ASSERT(!dbg_under || dbg_under->end < desc->start); RBT_ASSERT(!dbg_over || dbg_over->start > desc->end); } #endif - /*SET_RED(blk);*/ - RBT_ASSERT(IS_RED(blk)); - if (IS_RED(parent(blk))) - tree_insert_fixup(root, blk); + RBT_ASSERT(IS_RED(node)); + if (IS_RED(parent(node))) + tree_insert_fixup(&tree->root, node); } - /*SVERK if (flavor == AOFF_BF) { - SET_TREE_NODE(blk); - LIST_NEXT(blk) = NULL; - }*/ + HARD_CHECK_TREE(tree, 0); } /* @@ -860,9 +823,39 @@ rbt_foreach_node(RBTree* tree, #endif } +#ifdef RBT_DEBUG +static RBTNode* rbt_prev_node(RBTNode* node) +{ + RBTNode* x; + if (node->left) { + for (x=node->left; x->right; x=x->right) + ; + return x; + } + for (x=node; parent(x); x=parent(x)) { + if (parent(x)->right == x) + return parent(x); + } + return NULL; +} +static RBTNode* rbt_next_node(RBTNode* node) +{ + RBTNode* x; + if (node->right) { + for (x=node->right; x->left; x=x->left) + ; + return x; + } + for (x=node; parent(x); x=parent(x)) { + if (parent(x)->left == x) + return parent(x); + } + return NULL; +} +#endif /* RBT_DEBUG */ -/* The API to keep track of a bunch of separated free segments +/* The API to keep track of a bunch of separated (free) segments (non-overlapping and non-adjacent). */ static void init_free_seg_map(ErtsFreeSegMap*, int reverse_ao); @@ -883,6 +876,9 @@ static void init_free_seg_map(ErtsFreeSegMap* map, int reverse_ao) map->nseg = 0; } +/* Lookup directly adjacent free segments to the given area [start->end]. + * The given area must not contain any free segments. + */ static void adjacent_free_seg(ErtsFreeSegMap* map, char* start, char* end, ErtsFreeSegDesc** under, ErtsFreeSegDesc** over) { @@ -910,39 +906,49 @@ static void adjacent_free_seg(ErtsFreeSegMap* map, char* start, char* end, } } +/* Initialize 'desc' and insert as new free segment [start->end]. + * The new segment must not contain or be adjacent to any free segment in 'map'. + */ static void insert_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc, char* start, char* end) { desc->start = start; desc->end = end; - rbt_insert(map->atree.order, &map->atree.root, &desc->anode); - rbt_insert(map->stree.order, &map->stree.root, &desc->snode); + rbt_insert(map->atree.order, &map->atree, &desc->anode); + rbt_insert(map->stree.order, &map->stree, &desc->snode); map->nseg++; } +/* Resize existing free segment 'desc' to [start->end]. + * The new segment location must overlap the old location and + * it must not contain or be adjacent to any other free segment in 'map'. + */ static void resize_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc, char* start, char* end) { -#ifdef DEBUG - ErtsFreeSegDesc *dbg_under, *dbg_over; - rbt_delete(&map->atree.root, &desc->anode); - adjacent_free_seg(map, start, end, &dbg_under, &dbg_over); - RBT_ASSERT(dbg_under == NULL && dbg_over == NULL); - rbt_insert(map->atree.order, &map->atree.root, &desc->anode); +#ifdef RBT_DEBUG + RBTNode* prev = rbt_prev_node(&desc->anode); + RBTNode* next = rbt_next_node(&desc->anode); + RBT_ASSERT(!prev || anode_to_desc(prev)->end < start); + RBT_ASSERT(!next || anode_to_desc(next)->start > end); #endif - rbt_delete(&map->stree.root, &desc->snode); + rbt_delete(&map->stree, &desc->snode); desc->start = start; desc->end = end; - rbt_insert(map->stree.order, &map->stree.root, &desc->snode); + rbt_insert(map->stree.order, &map->stree, &desc->snode); } +/* Delete existing free segment 'desc' from 'map'. + */ static void delete_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc) { - rbt_delete(&map->atree.root, &desc->anode); - rbt_delete(&map->stree.root, &desc->snode); + rbt_delete(&map->atree, &desc->anode); + rbt_delete(&map->stree, &desc->snode); map->nseg--; } +/* Lookup a free segment in 'map' with a size of at least 'need_sz' bytes. + */ static ErtsFreeSegDesc* lookup_free_seg(ErtsFreeSegMap* map, SWord need_sz) { RBTNode* x = map->stree.root; @@ -1934,7 +1940,7 @@ static int rbt_assert_is_member(RBTNode* root, RBTNode* node) } -#if 1 /*SVERK*/ +#if 0 # define PRINT_TREE #else # undef PRINT_TREE @@ -1964,7 +1970,7 @@ struct check_arg_t { Uint size; RBTNode *res; }; -static void check_node(RBTNode* x, void* arg); +static void check_node_callback(RBTNode* x, void* arg); static RBTNode * @@ -1986,13 +1992,12 @@ check_tree(RBTree* tree, Uint size) RBT_ASSERT(IS_BLACK(tree->root)); RBT_ASSERT(!parent(tree->root)); - rbt_foreach_node(tree, check_node, &carg, 0); + rbt_foreach_node(tree, check_node_callback, &carg, 0); return carg.res; } -/* callback */ -static void check_node(RBTNode* x, void* arg) +static void check_node_callback(RBTNode* x, void* arg) { struct check_arg_t* a = (struct check_arg_t*) arg; ErtsFreeSegDesc* seg; @@ -2005,16 +2010,16 @@ static void check_node(RBTNode* x, void* arg) RBT_ASSERT(parent(x) || x == a->tree->root); if (x->left) { - RBT_ASSERT(cmp_blocks(a->tree->order, x->left, x) < 0); + RBT_ASSERT(cmp_nodes(a->tree->order, x->left, x) < 0); } if (x->right) { - RBT_ASSERT(cmp_blocks(a->tree->order, x->right, x) > 0); + RBT_ASSERT(cmp_nodes(a->tree->order, x->right, x) > 0); } seg = node_to_desc(a->tree->order, x); RBT_ASSERT(seg->start < seg->end); if (a->size && (seg->end - seg->start) >= a->size) { - if (!a->res || cmp_blocks(a->tree->order, x, a->res) < 0) { + if (!a->res || cmp_nodes(a->tree->order, x, a->res) < 0) { a->res = x; } } @@ -2063,6 +2068,8 @@ print_tree(enum SortOrder order, RBTNode* root) #endif /* PRINT_TREE */ +#ifdef FREE_SEG_API_SMOKE_TEST + void test_it(void) { ErtsFreeSegMap map; @@ -2128,3 +2135,5 @@ void test_it(void) ERTS_ASSERT(d1->start == (char*)0x15000); } } + +#endif /* FREE_SEG_API_SMOKE_TEST */ -- cgit v1.2.3 From d04e3d43f3a96030db0c9b8da8f88cb78f8ec8dc Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 5 Sep 2013 14:45:26 +0200 Subject: erts: Improve erts_mmap out of free descriptor management --- erts/emulator/sys/common/erl_mmap.c | 100 +++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 40 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 723efa772f..0ac08a0004 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -222,6 +222,7 @@ static struct { mmap_state.size.os.used -= (SZ); \ } while (0) + static void add_free_desc_area(char *start, char *end) { @@ -1164,6 +1165,39 @@ static void unreserve_noop(char *ptr, UWord size) #endif } +static ERTS_INLINE UWord +alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end) +{ + UWord ad_sz; + char *new_end; + ErtsFreeSegDesc *desc = alloc_desc(); + if (desc) { + insert_free_seg(map, desc, start, end); + return 0; + } + + /* Use part of the free segment for descriptors */ + + if (map == &mmap_state.sa.map) { + ERTS_MMAP_SIZE_SC_SA_INC(ERTS_SUPERALIGNED_SIZE); + ad_sz = ERTS_SUPERALIGNED_SIZE; + } + else { + ERTS_MMAP_SIZE_SC_SUA_INC(ERTS_PAGEALIGNED_SIZE); + ad_sz = ERTS_PAGEALIGNED_SIZE; + } + + new_end = end - ad_sz; + ERTS_MMAP_ASSERT(start <= new_end); + if (start != new_end) { + desc = alloc_desc(); + ERTS_MMAP_ASSERT(desc); + insert_free_seg(map, desc, start, new_end); + } + + return ad_sz; +} + void * erts_mmap(Uint32 flags, UWord *sizep) { @@ -1248,6 +1282,7 @@ erts_mmap(Uint32 flags, UWord *sizep) end = seg + asize; if (!mmap_state.reserve_physical(seg, asize)) goto supercarrier_reserve_failure; + ERTS_MMAP_SIZE_SC_SUA_INC(asize); if (org_start != seg) { ERTS_MMAP_ASSERT(org_start < seg); resize_free_seg(&mmap_state.sua.map, desc, org_start, seg); @@ -1257,15 +1292,10 @@ erts_mmap(Uint32 flags, UWord *sizep) ERTS_MMAP_ASSERT(end < org_end); if (desc) resize_free_seg(&mmap_state.sua.map, desc, end, org_end); - else { - desc = alloc_desc(); - if (!desc) - add_free_desc_area(end, org_end); - else - insert_free_seg(&mmap_state.sua.map, desc, end, org_end); - } + else + alloc_desc_insert_free_seg(&mmap_state.sua.map, + end, org_end); } - ERTS_MMAP_SIZE_SC_SA_INC(asize); goto supercarrier_success; } } @@ -1361,6 +1391,7 @@ erts_munmap(Uint32 flags, void **ptrp, UWord *sizep) char *start, *end; ErtsFreeSegMap *map; ErtsFreeSegDesc *prev, *next, *desc; + UWord ad_sz = 0; ERTS_MMAP_ASSERT(mmap_state.supercarrier); @@ -1432,23 +1463,19 @@ erts_munmap(Uint32 flags, void **ptrp, UWord *sizep) if (desc) resize_free_seg(map, desc, start, end); - else { - desc = alloc_desc(); - if (desc) - insert_free_seg(map, desc, start, end); - else { - if (map == &mmap_state.sa.map) - ERTS_MMAP_SIZE_SC_SA_INC(size); - else - ERTS_MMAP_SIZE_SC_SUA_INC(size); - add_free_desc_area(start, end); - } - } + else + ad_sz = alloc_desc_insert_free_seg(map, start, end); + + supercarrier_success: { + UWord unres_sz; - supercarrier_success: - erts_smp_mtx_unlock(&mmap_state.mtx); + erts_smp_mtx_unlock(&mmap_state.mtx); - mmap_state.unreserve_physical((char *) ptr, size); + ERTS_MMAP_ASSERT(size >= ad_sz); + unres_sz = size - ad_sz; + if (unres_sz) + mmap_state.unreserve_physical((char *) ptr, unres_sz); + } } } @@ -1548,7 +1575,8 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) else { /* In super carrier */ char *start, *end, *new_end; ErtsFreeSegMap *map; - ErtsFreeSegDesc *prev, *next, *desc; + ErtsFreeSegDesc *prev, *next; + UWord ad_sz = 0; ERTS_MMAP_ASSERT(mmap_state.supercarrier); @@ -1586,6 +1614,7 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) new_end = start+asize; if (asize < old_size) { + UWord unres_sz; new_ptr = ptr; if (!ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)) { ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(ptr)); @@ -1620,21 +1649,13 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) if (next) resize_free_seg(map, next, new_end, next->end); - else { - desc = alloc_desc(); - if (desc) - insert_free_seg(map, desc, new_end, end); - else { - if (map == &mmap_state.sa.map) - ERTS_MMAP_SIZE_SC_SA_INC(old_size - asize); - else - ERTS_MMAP_SIZE_SC_SUA_INC(old_size - asize); - add_free_desc_area(new_end, end); - goto supercarrier_resize_success; - } - } - mmap_state.unreserve_physical(((char *) ptr) + asize, - old_size - asize); + else + ad_sz = alloc_desc_insert_free_seg(map, new_end, end); + ERTS_MMAP_ASSERT(old_size - asize >= ad_sz); + unres_sz = old_size - asize - ad_sz; + if (unres_sz) + mmap_state.unreserve_physical(((char *) ptr) + asize, + unres_sz); goto supercarrier_resize_success; } @@ -1921,7 +1942,6 @@ erts_mmap_init(ErtsMMapInit *init) #endif } - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * Debug functions * \* */ -- cgit v1.2.3 From 0820017c421bfab27d23aff4da474974f988006c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 5 Sep 2013 19:23:07 +0200 Subject: erts: Add mmap argument to erts_debug:get_internal_state --- erts/emulator/beam/erl_bif_info.c | 3 +++ erts/emulator/sys/common/erl_mmap.c | 48 ++++++++++++++++++++++++++++++++++++- erts/emulator/sys/common/erl_mmap.h | 2 ++ 3 files changed, 52 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index a4f9f787cd..7aa439f2e6 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3289,6 +3289,9 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) erts_smp_thr_progress_unblock(); BIF_RET(res); } + else if (ERTS_IS_ATOM_STR("mmap", BIF_ARG_1)) { + BIF_RET(erts_mmap_info(BIF_P)); + } } else if (is_tuple(BIF_ARG_1)) { Eterm* tp = tuple_val(BIF_ARG_1); diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 0ac08a0004..1d18c1fcc9 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -23,6 +23,7 @@ #include "sys.h" #include "erl_process.h" #include "erl_smp.h" +#include "atom.h" #include "erl_mmap.h" #include @@ -741,7 +742,7 @@ rbt_foreach_node(RBTree* tree, enum { RECURSE_LEFT, DO_NODE, RECURSE_RIGHT, RETURN_TO_PARENT }state; RBTNode *x = tree->root; - RBT_ASSERT(!parent(x)); + RBT_ASSERT(!x || !parent(x)); state = reverse ? RECURSE_RIGHT : RECURSE_LEFT; while (x) { @@ -1942,6 +1943,51 @@ erts_mmap_init(ErtsMMapInit *init) #endif } +Eterm erts_mmap_info(Process* p) +{ + if (mmap_state.supercarrier) { + ERTS_DECL_AM(sabot); + ERTS_DECL_AM(satop); + ERTS_DECL_AM(suabot); + ERTS_DECL_AM(suatop); + Eterm sa_list, sua_list, list; + Eterm tags[] = { AM_sabot, AM_satop, AM_suabot, AM_suatop }; + UWord values[4]; + Eterm *hp, *hp_end; + Uint may_need; + const Uint PTR_BIG_SZ = HALFWORD_HEAP ? 3 : 2; + + erts_smp_mtx_lock(&mmap_state.mtx); + values[0] = (UWord)mmap_state.sa.bot; + values[1] = (UWord)mmap_state.sa.top; + values[2] = (UWord)mmap_state.sua.bot; + values[3] = (UWord)mmap_state.sua.top; + sa_list = build_free_seg_list(p, &mmap_state.sa.map); + sua_list = build_free_seg_list(p, &mmap_state.sua.map); + erts_smp_mtx_unlock(&mmap_state.mtx); + + may_need = 4*(2+3+PTR_BIG_SZ) + 2*(2+3); + hp = HAlloc(p, may_need); + hp_end = hp + may_need; + + list = erts_bld_atom_uint_2tup_list(&hp, NULL, + sizeof(values)/sizeof(*values), + tags, values); + + sa_list = TUPLE2(hp, am_atom_put("sa_free_segs",12), sa_list); hp+=3; + sua_list = TUPLE2(hp, am_atom_put("sua_free_segs",13), sua_list); hp+=3; + list = CONS(hp, sua_list, list); hp+=2; + list = CONS(hp, sa_list, list); hp+=2; + + ASSERT(hp <= hp_end); + HRelease(p, hp_end, hp); + return list; + } + else { + return am_undefined; + } +} + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * Debug functions * \* */ diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 143f1aff3e..b75200f4e9 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -57,6 +57,8 @@ void erts_munmap(Uint32 flags, void **ptrp, UWord *sizep); void *erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep); int erts_mmap_in_supercarrier(void *ptr); void erts_mmap_init(ErtsMMapInit*); +struct process; +Eterm erts_mmap_info(struct process*); #define ERTS_SUPERALIGNED_SIZE \ (1 << ERTS_MMAP_SUPERALIGNED_BITS) -- cgit v1.2.3 From 2d64c6e31966d9e63d1aa1835d41ded22f799175 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 6 Sep 2013 19:34:02 +0200 Subject: erts: Fix ASSERT bug and void* arithmetics --- erts/emulator/sys/common/erl_mseg.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index b21d6ca393..09035ca73e 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -515,7 +515,7 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag if (MSEG_FLG_IS_2POW(flags)) { int i, ix = SIZE_TO_CACHE_AREA_IDX(size); - void *seg; + char *seg; cache_t *c; Uint csize; @@ -533,7 +533,7 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag ASSERT(MAP_IS_ALIGNED(c->seg)); csize = c->size; - seg = c->seg; + seg = (char*) c->seg; mk->cache_size--; mk->cache_hits++; @@ -545,11 +545,11 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag ASSERT(!(mk->cache_size < 0)); if (csize != size) { - void *destr_seg = ((char *) seg) + size; + char *destr_seg = seg + size; UWord destr_size = csize - size; - mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, &destr_seg, &destr_size); + mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, (void**)&destr_seg, &destr_size); *size_p = (UWord) (destr_seg - seg); - ASSERT(c->seg + c->size == destr_seg + destr_size); + ASSERT(seg + csize == destr_seg + destr_size); } return seg; -- cgit v1.2.3 From ac32bccf21354d7e6896d8b83b6e9a45bb1bccd7 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 9 Sep 2013 15:49:21 +0200 Subject: erts: Sort tree in super aligned sizes (SA_SZ_ADDR_ORDER) --- erts/emulator/sys/common/erl_mmap.c | 52 +++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 1d18c1fcc9..eae1a1a410 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -106,9 +106,12 @@ static ERTS_INLINE UWord parent_and_color(RBTNode* parent, int color) enum SortOrder { ADDR_ORDER, /* only address order */ - SZ_ADDR_ORDER, /* first size then address order as tiebreaker */ + SA_SZ_ADDR_ORDER, /* first super-aligned size then address order */ SZ_REVERSE_ADDR_ORDER /* first size then reverse address order */ }; +#ifdef HARD_DEBUG +static const char* sort_order_names[] = {"Address","SuperAlignedSize-Address","Size-RevAddress"}; +#endif typedef struct { RBTNode* root; @@ -286,9 +289,17 @@ static ERTS_INLINE SWord cmp_nodes(enum SortOrder order, ErtsFreeSegDesc* rdesc = node_to_desc(order, rhs); RBT_ASSERT(lhs != rhs); if (order != ADDR_ORDER) { - SWord lsz = ldesc->end - ldesc->start; - SWord rsz = rdesc->end - rdesc->start; - SWord diff = lsz - rsz; + SWord lsz, rsz, diff; + if (order == SA_SZ_ADDR_ORDER) { + lsz = ERTS_SUPERALIGNED_FLOOR(ldesc->end) - ERTS_SUPERALIGNED_CEILING(ldesc->start); + rsz = ERTS_SUPERALIGNED_FLOOR(rdesc->end) - ERTS_SUPERALIGNED_CEILING(rdesc->start); + } + else { + RBT_ASSERT(order == SZ_REVERSE_ADDR_ORDER); + lsz = ldesc->end - ldesc->start; + rsz = rdesc->end - rdesc->start; + } + diff = lsz - rsz; if (diff) return diff; } if (order != SZ_REVERSE_ADDR_ORDER) { @@ -305,12 +316,14 @@ static ERTS_INLINE SWord cmp_with_node(enum SortOrder order, { ErtsFreeSegDesc* rdesc; if (order != ADDR_ORDER) { + SWord rhs_sz, diff; rdesc = snode_to_desc(rhs); - { - SWord rhs_sz = rdesc->end - rdesc->start; - SWord diff = sz - rhs_sz; - if (diff) return diff; - } + if (order == SA_SZ_ADDR_ORDER) + rhs_sz = ERTS_SUPERALIGNED_FLOOR(rdesc->end) - ERTS_SUPERALIGNED_CEILING(rdesc->start); + else + rhs_sz = rdesc->end - rdesc->start; + diff = sz - rhs_sz; + if (diff) return diff; } else rdesc = anode_to_desc(rhs); @@ -860,7 +873,7 @@ static RBTNode* rbt_next_node(RBTNode* node) /* The API to keep track of a bunch of separated (free) segments (non-overlapping and non-adjacent). */ -static void init_free_seg_map(ErtsFreeSegMap*, int reverse_ao); +static void init_free_seg_map(ErtsFreeSegMap*, enum SortOrder); static void adjacent_free_seg(ErtsFreeSegMap*, char* start, char* end, ErtsFreeSegDesc** under, ErtsFreeSegDesc** over); static void insert_free_seg(ErtsFreeSegMap*, ErtsFreeSegDesc*, char* start, char* end); @@ -869,12 +882,12 @@ static void delete_free_seg(ErtsFreeSegMap*, ErtsFreeSegDesc*); static ErtsFreeSegDesc* lookup_free_seg(ErtsFreeSegMap*, SWord sz); -static void init_free_seg_map(ErtsFreeSegMap* map, int reverse_ao) +static void init_free_seg_map(ErtsFreeSegMap* map, enum SortOrder order) { map->atree.root = NULL; map->atree.order = ADDR_ORDER; map->stree.root = NULL; - map->stree.order = reverse_ao ? SZ_REVERSE_ADDR_ORDER : SZ_ADDR_ORDER; + map->stree.order = order; map->nseg = 0; } @@ -1931,8 +1944,8 @@ erts_mmap_init(ErtsMMapInit *init) #endif add_free_desc_area(mmap_state.sua.top, end); - init_free_seg_map(&mmap_state.sa.map, 0); - init_free_seg_map(&mmap_state.sua.map, 1); + init_free_seg_map(&mmap_state.sa.map, SA_SZ_ADDR_ORDER); + init_free_seg_map(&mmap_state.sua.map, SZ_REVERSE_ADDR_ORDER); mmap_state.supercarrier = 1; erts_have_erts_mmap |= ERTS_HAVE_ERTS_SUPERCARRIER_MMAP; @@ -2125,10 +2138,9 @@ print_tree_aux(enum SortOrder order, RBTNode *x, int indent) static void print_tree(enum SortOrder order, RBTNode* root) { - static const char* type[] = {"Address","Size-Address","Size-RevAddress"}; - fprintf(stderr, " --- %s ordered tree begin ---\r\n", type[order]); + fprintf(stderr, " --- %s ordered tree begin ---\r\n", sort_order_names[order]); print_tree_aux(order, root, 0); - fprintf(stderr, " --- %s ordered tree end ---\r\n", type[order]); + fprintf(stderr, " --- %s ordered tree end ---\r\n", sort_order_names[order]); } #endif /* PRINT_TREE */ @@ -2140,10 +2152,10 @@ void test_it(void) { ErtsFreeSegMap map; ErtsFreeSegDesc *desc, *under, *over, *d1, *d2; - int i; + const int i = 1; /* reverse addr order */ - for (i=0; i<2; i++) { - init_free_seg_map(&map, i); + { + init_free_seg_map(&map, SZ_REVERSE_ADDR_ORDER); insert_free_seg(&map, alloc_desc(), (char*)0x11000, (char*)0x12000); HARD_CHECK_TREE(&map.atree, 0); HARD_CHECK_TREE(&map.stree, 0); -- cgit v1.2.3 From c8f87b4ec91b67c9d3373c8466a07db638e32cc2 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 9 Sep 2013 16:07:29 +0200 Subject: erts: Allow page aligned erts_munmap() --- erts/emulator/sys/common/erl_mmap.c | 102 +++++++++++++++++------------------- erts/emulator/sys/common/erl_mmap.h | 2 +- erts/emulator/sys/common/erl_mseg.c | 70 +++++++------------------ 3 files changed, 67 insertions(+), 107 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index eae1a1a410..73874759f5 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -1260,30 +1260,53 @@ erts_mmap(Uint32 flags, UWord *sizep) desc = lookup_free_seg(&mmap_state.sa.map, asize); if (desc) { - seg = desc->start; + char *start = seg = desc->start; + seg = (char *) ERTS_SUPERALIGNED_CEILING(seg); end = seg+asize; - if (!mmap_state.reserve_physical(seg, asize)) + if (!mmap_state.reserve_physical(start, (UWord) (end - start))) goto supercarrier_reserve_failure; + ERTS_MMAP_SIZE_SC_SA_INC(asize); if (desc->end == end) { - delete_free_seg(&mmap_state.sa.map, desc); - free_desc(desc); + if (start != seg) + resize_free_seg(&mmap_state.sa.map, desc, start, seg); + else { + delete_free_seg(&mmap_state.sa.map, desc); + free_desc(desc); + } } else { ERTS_MMAP_ASSERT(end < desc->end); resize_free_seg(&mmap_state.sa.map, desc, end, desc->end); + if (start != seg) { + UWord ad_sz; + ad_sz = alloc_desc_insert_free_seg(&mmap_state.sua.map, + start, seg); + start += ad_sz; + if (start != seg) + mmap_state.unreserve_physical(start, (UWord) (seg - start)); + } } - ERTS_MMAP_SIZE_SC_SA_INC(asize); goto supercarrier_success; } if (superaligned) { + seg = (char *) ERTS_SUPERALIGNED_CEILING(mmap_state.sa.top); - if (asize <= mmap_state.sua.bot - mmap_state.sa.top) { - seg = (void *) mmap_state.sa.top; - if (!mmap_state.reserve_physical(seg, asize)) + if (asize <= mmap_state.sua.bot - seg) { + char *start = mmap_state.sa.top; + end = seg + asize; + if (!mmap_state.reserve_physical(start, (UWord) (end - start))) goto supercarrier_reserve_failure; - mmap_state.sa.top += asize; + mmap_state.sa.top = end; ERTS_MMAP_SIZE_SC_SA_INC(asize); + if (start != seg) { + UWord ad_sz; + ad_sz = alloc_desc_insert_free_seg(&mmap_state.sua.map, + start, seg); + start += ad_sz; + if (start != seg) + mmap_state.unreserve_physical(start, (UWord) (seg - start)); + } goto supercarrier_success; } @@ -1294,7 +1317,7 @@ erts_mmap(Uint32 flags, UWord *sizep) seg = (char *) ERTS_SUPERALIGNED_CEILING(org_start); end = seg + asize; - if (!mmap_state.reserve_physical(seg, asize)) + if (!mmap_state.reserve_physical(seg, (UWord) (org_end - seg))) goto supercarrier_reserve_failure; ERTS_MMAP_SIZE_SC_SUA_INC(asize); if (org_start != seg) { @@ -1303,12 +1326,17 @@ erts_mmap(Uint32 flags, UWord *sizep) desc = NULL; } if (end != org_end) { + UWord ad_sz = 0; ERTS_MMAP_ASSERT(end < org_end); if (desc) resize_free_seg(&mmap_state.sua.map, desc, end, org_end); else - alloc_desc_insert_free_seg(&mmap_state.sua.map, - end, org_end); + ad_sz = alloc_desc_insert_free_seg(&mmap_state.sua.map, + end, org_end); + end += ad_sz; + if (end != org_end) + mmap_state.unreserve_physical(end, + (UWord) (org_end - end)); } goto supercarrier_success; } @@ -1363,8 +1391,7 @@ erts_mmap(Uint32 flags, UWord *sizep) supercarrier_success: #ifdef ERTS_MMAP_DEBUG - if ((ERTS_MMAPFLG_SUPERALIGNED & flags) - || ERTS_MMAP_IN_SUPERALIGNED_AREA(seg)) { + if (ERTS_MMAPFLG_SUPERALIGNED & flags) { ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(seg)); ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(asize)); } @@ -1388,10 +1415,8 @@ supercarrier_reserve_failure: } void -erts_munmap(Uint32 flags, void **ptrp, UWord *sizep) +erts_munmap(Uint32 flags, void *ptr, UWord size) { - void *ptr = *ptrp; - UWord size = *sizep; ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(ptr)); ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(size)); if (!ERTS_MMAP_IN_SUPERCARRIER(ptr)) { @@ -1416,13 +1441,6 @@ erts_munmap(Uint32 flags, void **ptrp, UWord *sizep) if (ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)) { - start = (char *) ERTS_SUPERALIGNED_CEILING(start); - end = (char *) ERTS_SUPERALIGNED_FLOOR(end); - - size = (UWord) (end - start); - *ptrp = start; - *sizep = size; - map = &mmap_state.sa.map; adjacent_free_seg(map, start, end, &prev, &next); @@ -1439,8 +1457,6 @@ erts_munmap(Uint32 flags, void **ptrp, UWord *sizep) } } else { - ERTS_MMAP_ASSERT(ERTS_MMAP_IN_SUPERUNALIGNED_AREA(ptr)); - map = &mmap_state.sua.map; adjacent_free_seg(map, start, end, &prev, &next); @@ -1497,8 +1513,6 @@ static void * remap_move(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) { UWord size = *sizep; - UWord um_size = old_size; - void *um_ptr = ptr; void *new_ptr = erts_mmap(flags, &size); if (!new_ptr) return NULL; @@ -1506,9 +1520,7 @@ remap_move(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) if (old_size < size) size = old_size; sys_memcpy(new_ptr, ptr, (size_t) size); - erts_munmap(flags, &um_ptr, &um_size); - ERTS_MMAP_ASSERT(um_ptr == ptr); - ERTS_MMAP_ASSERT(um_size == old_size); + erts_munmap(flags, ptr, old_size); return new_ptr; } @@ -1627,28 +1639,18 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) end = start + old_size; new_end = start+asize; + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(ptr)); + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(old_size)); + ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(asize)); + if (asize < old_size) { UWord unres_sz; new_ptr = ptr; if (!ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)) { - ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(ptr)); - ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(old_size)); - ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(asize)); map = &mmap_state.sua.map; ERTS_MMAP_SIZE_SC_SUA_DEC(old_size - asize); } else { - ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(ptr)); - ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(old_size)); - if (!superaligned) { - /* must be a superaligned size in this area */ - asize = ERTS_SUPERALIGNED_CEILING(asize); - ERTS_MMAP_ASSERT(asize <= old_size); - if (asize == old_size) - goto supercarrier_resize_success; - new_end = start+asize; - } - ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(asize)); if (end == mmap_state.sa.top) { mmap_state.sa.top = new_end; mmap_state.unreserve_physical(((char *) ptr) + asize, @@ -1696,16 +1698,6 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) } } else { /* Superaligned area */ - ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(ptr)); - ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(old_size)); - - if (!superaligned) { - /* must be a superaligned size in this area */ - asize = ERTS_PAGEALIGNED_CEILING(asize); - new_end = start+asize; - } - - ERTS_MMAP_ASSERT(ERTS_IS_SUPERALIGNED(asize)); if (end == mmap_state.sa.top) { if (new_end <= mmap_state.sua.bot) { diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index b75200f4e9..6cb51fb0b4 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -53,7 +53,7 @@ typedef struct { {{NULL, NULL}, {NULL, NULL}, 0, 1, (1 << 16), 1} void *erts_mmap(Uint32 flags, UWord *sizep); -void erts_munmap(Uint32 flags, void **ptrp, UWord *sizep); +void erts_munmap(Uint32 flags, void *ptr, UWord size); void *erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep); int erts_mmap_in_supercarrier(void *ptr); void erts_mmap_init(ErtsMMapInit*); diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 09035ca73e..2474015bcb 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -330,7 +330,7 @@ mseg_create(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, UWord *sizep) } static ERTS_INLINE void -mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void **seg_pp, UWord *size_p) { +mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void *seg_p, UWord size) { Uint32 mmap_flags = 0; #if HALFWORD_HEAP @@ -341,11 +341,11 @@ mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void **seg_pp, UWord if (MSEG_FLG_IS_2POW(flags)) mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; - erts_munmap(mmap_flags, seg_pp, size_p); + erts_munmap(mmap_flags, seg_p, size); #ifdef ERTS_PRINT_ERTS_MMAP erts_fprintf(stderr, "erts_munmap(%s, %p, %bpu);\n", (mmap_flags & ERTS_MMAPFLG_SUPERALIGNED) ? "sa" : "sua", - *seg_pp, *size_p); + seg_p, *size); #endif INC_CC(ma, destroy); @@ -446,19 +446,13 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size, Ui return 1; } else if (!MSEG_FLG_IS_2POW(flags) && !erts_circleq_is_empty(&(mk->cache_unpowered_node))) { - void *destr_seg; - UWord destr_size; /* No free slots. * Evict oldest slot from unpowered cache so we can cache an unpowered (sbc) segment */ c = erts_circleq_tail(&(mk->cache_unpowered_node)); erts_circleq_remove(c); - destr_seg = c->seg; - destr_size = c->size; - mseg_destroy(mk->ma, ERTS_MSEG_FLG_NONE, mk, &destr_seg, &destr_size); - ASSERT(destr_seg == c->seg); - ASSERT(destr_size == c->size); + mseg_destroy(mk->ma, ERTS_MSEG_FLG_NONE, mk, c->seg, c->size); mseg_cache_clear_node(c); c->seg = seg; @@ -478,19 +472,13 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size, Ui int i; for( i = 0; i < CACHE_AREAS; i++) { - void *destr_seg; - UWord destr_size; if (erts_circleq_is_empty(&(mk->cache_powered_node[i]))) continue; c = erts_circleq_tail(&(mk->cache_powered_node[i])); erts_circleq_remove(c); - destr_seg = seg; - destr_size = c->size; - mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, &destr_seg, &destr_size); - ASSERT(destr_seg == c->seg); - ASSERT(destr_size == c->size); + mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, c->seg, c->size); mseg_cache_clear_node(c); @@ -544,13 +532,8 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag ASSERT(!(mk->cache_size < 0)); - if (csize != size) { - char *destr_seg = seg + size; - UWord destr_size = csize - size; - mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, (void**)&destr_seg, &destr_size); - *size_p = (UWord) (destr_seg - seg); - ASSERT(seg + csize == destr_seg + destr_size); - } + if (csize != size) + mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, seg + size, csize - size); return seg; } @@ -625,8 +608,6 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag */ static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, Uint flags, cache_t *head) { - void *destr_seg; - UWord destr_size; cache_t *c = NULL; c = erts_circleq_tail(head); @@ -635,11 +616,7 @@ static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, Uint flags if (erts_mtrace_enabled) erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg); - destr_seg = c->seg; - destr_size = c->size; - mseg_destroy(mk->ma, flags, mk, &destr_seg, &destr_size); - ASSERT(destr_seg == c->seg); - ASSERT(destr_size == c->size); + mseg_destroy(mk->ma, flags, mk, c->seg, c->size); mseg_cache_clear_node(c); erts_circleq_push_head(&(mk->cache_free), c); @@ -655,8 +632,6 @@ static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, Uint flags, ca cache_t *c = NULL; while (!erts_circleq_is_empty(head)) { - void *destr_seg; - UWord destr_size; c = erts_circleq_tail(head); erts_circleq_remove(c); @@ -664,11 +639,7 @@ static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, Uint flags, ca if (erts_mtrace_enabled) erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg); - destr_seg = c->seg; - destr_size = c->size; - mseg_destroy(mk->ma, flags, mk, &destr_seg, &destr_size); - ASSERT(destr_seg == c->seg); - ASSERT(destr_size == c->size); + mseg_destroy(mk->ma, flags, mk, c->seg, c->size); mseg_cache_clear_node(c); erts_circleq_push_head(&(mk->cache_free), c); @@ -813,12 +784,14 @@ mseg_alloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, UWord *size_p, INC_CC(ma, alloc); - if (!MSEG_FLG_IS_2POW(flags) && !IS_2POW(size)) + if (!MSEG_FLG_IS_2POW(flags)) size = ERTS_PAGEALIGNED_CEILING(*size_p); else { size = ALIGNED_CEILING(*size_p); - /* Cache optim (if applicable) */ - size = ceil_2pow(size); + if (!IS_2POW(size)) { + /* Cache optim (if applicable) */ + size = ceil_2pow(size); + } } if (opt->cache && mk->cache_size > 0 && (seg = cache_get_segment(mk, &size, flags)) != NULL) @@ -845,8 +818,6 @@ static void mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, UWord size, Uint flags, const ErtsMsegOpt_t *opt) { - void *destr_seg; - UWord destr_size; MemKind* mk = memkind(ma, opt); ERTS_MSEG_DEALLOC_STAT(mk,size); @@ -859,11 +830,7 @@ mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, UWord size, if (erts_mtrace_enabled) erts_mtrace_crr_free(atype, SEGTYPE, seg); - destr_seg = seg; - destr_size = size; - mseg_destroy(ma, flags, mk, &destr_seg, &destr_size); - ASSERT(destr_seg == seg); - ASSERT(destr_size == size); + mseg_destroy(ma, flags, mk, seg, size); done: @@ -896,13 +863,14 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, mk = memkind(ma, opt); new_seg = seg; - if (!MSEG_FLG_IS_2POW(flags)) new_size = ERTS_PAGEALIGNED_CEILING(*new_size_p); else { new_size = ALIGNED_CEILING(*new_size_p); - /* Cache optim (if applicable) */ - new_size = ceil_2pow(new_size); + if (!IS_2POW(new_size)) { + /* Cache optim (if applicable) */ + new_size = ceil_2pow(new_size); + } } if (new_size > old_size) { -- cgit v1.2.3 From ffbd1fe3fe4fdd1657f98d650eb3b40139e4b115 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 Sep 2013 15:33:33 +0200 Subject: erts: Add documentation for +MMsc* system flags --- erts/doc/src/erts_alloc.xml | 59 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 6ce2261430..9dab5b3876 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -271,6 +271,65 @@ memory segment cache is not reused if its size exceeds the requested size with more than relative max cache bad fit percent of the requested size. Default value is 20. + ]]> + + Set super carrier max guaranteed + no of carriers. This parameter defaults to 65536. This + parameter determines an amount of pre-allocated structures that is + needed in order to keep track of different areas in the super carrier. + When the system runs out of such structures it may crash due to an + out of memory condition. + + + + Set super carrier only flag. This + flag defaults to true. When a super carrier is used and this + flag is true, the system will crash when a carrier request + cannot be satisfied by the super carrier. When the flag is false + the system will try to create requested carrier by other means. +

+ NOTE: Setting this flag to false may not be supported + on all systems. This flag will in that case be ignored. +

+ NOTE: The super carrier cannot be enabled nor + disabled on halfword heap systems. This flag will be + ignored on halfword heap systems. +
+ + + Set super carrier reserve physical + memory flag. This flag defaults to true. When this flag is + true, physical memory will be reserved for the whole super + carrier at once when it is created. The reservation will after that + be left unchanged. When this flag is set to false only virtual + address space will be reserved for the super carrier upon creation. + The system will attempt to reserve physical memory upon carrier + creations in the super carrier, and attempt to unreserve physical + memory upon carrier destructions in the super carrier. +

+ NOTE: What reservation of physical memory actually means + highly depends on the operating system, and how it is configured. For + example, different memory overcommit settings on Linux drastically + change the behaviour. Also note, setting this flag to false + may not be supported on all systems. This flag will in that case + be ignored. +

+ NOTE: The super carrier cannot be enabled nor + disabled on halfword heap systems. This flag will be + ignored on halfword heap systems. +
+ ]]> + + Set super carrier size (in MB). The super carrier size defaults to + zero; i.e, the super carrier is by default disabled. The super + carrier is a large continuous area in the virtual address space. + The system will always try to create new carriers in the super + carrier. +

+ NOTE: The super carrier cannot be enabled nor + disabled on halfword heap systems. This flag will be + ignored on halfword heap systems. +
]]> Max cached segments. The maximum number of memory segments -- cgit v1.2.3 From e107965576ccd0cfd4f235e463cd6cc8da11a259 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 13 Sep 2013 02:07:03 +0200 Subject: erts: erts_mmap improved free seg desc management --- erts/emulator/sys/common/erl_mmap.c | 389 +++++++++++++++++++++++++++++++----- erts/emulator/sys/common/erl_mseg.c | 22 +- 2 files changed, 349 insertions(+), 62 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 73874759f5..1f9168df28 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -27,9 +27,18 @@ #include "erl_mmap.h" #include +/* #define ERTS_MMAP_OP_RINGBUF_SZ 100 */ + #if defined(DEBUG) || 0 # undef ERTS_MMAP_DEBUG # define ERTS_MMAP_DEBUG +# ifndef ERTS_MMAP_OP_RINGBUF_SZ +# define ERTS_MMAP_OP_RINGBUF_SZ 100 +# endif +#endif + +#ifndef ERTS_MMAP_OP_RINGBUF_SZ +# define ERTS_MMAP_OP_RINGBUF_SZ 0 #endif /* #define ERTS_MMAP_DEBUG_FILL_AREAS */ @@ -128,6 +137,148 @@ static RBTNode* check_tree(RBTree* tree, Uint); # define HARD_CHECK_TREE(TREE,SZ) #endif +#if ERTS_MMAP_OP_RINGBUF_SZ + +static int mmap_op_ix; + +typedef enum { + ERTS_OP_TYPE_NONE, + ERTS_OP_TYPE_MMAP, + ERTS_OP_TYPE_MUNMAP, + ERTS_OP_TYPE_MREMAP +} ErtsMMapOpType; + +typedef struct { + ErtsMMapOpType type; + void *result; + UWord in_size; + UWord out_size; + void *old_ptr; + UWord old_size; +} ErtsMMapOp; + +static ErtsMMapOp mmap_ops[ERTS_MMAP_OP_RINGBUF_SZ]; + +#define ERTS_MMAP_OP_RINGBUF_INIT() \ + do { \ + int ix__; \ + for (ix__ = 0; ix__ < ERTS_MMAP_OP_RINGBUF_SZ; ix__++) {\ + mmap_ops[ix__].type = ERTS_OP_TYPE_NONE; \ + mmap_ops[ix__].result = NULL; \ + mmap_ops[ix__].in_size = 0; \ + mmap_ops[ix__].out_size = 0; \ + mmap_ops[ix__].old_ptr = NULL; \ + mmap_ops[ix__].old_size = 0; \ + } \ + mmap_op_ix = ERTS_MMAP_OP_RINGBUF_SZ-1; \ + } while (0) + +#define ERTS_MMAP_OP_START(SZ) \ + do { \ + int ix__; \ + if (++mmap_op_ix >= ERTS_MMAP_OP_RINGBUF_SZ) \ + mmap_op_ix = 0; \ + ix__ = mmap_op_ix; \ + mmap_ops[ix__].type = ERTS_OP_TYPE_MMAP; \ + mmap_ops[ix__].result = NULL; \ + mmap_ops[ix__].in_size = (SZ); \ + mmap_ops[ix__].out_size = 0; \ + mmap_ops[ix__].old_ptr = NULL; \ + mmap_ops[ix__].old_size = 0; \ + } while (0) + +#define ERTS_MMAP_OP_END(PTR, SZ) \ + do { \ + int ix__ = mmap_op_ix; \ + mmap_ops[ix__].result = (PTR); \ + mmap_ops[ix__].out_size = (SZ); \ + } while (0) + +#define ERTS_MMAP_OP_LCK(RES, IN_SZ, OUT_SZ) \ + do { \ + erts_smp_mtx_lock(&mmap_state.mtx); \ + ERTS_MMAP_OP_START((IN_SZ)); \ + ERTS_MMAP_OP_END((RES), (OUT_SZ)); \ + erts_smp_mtx_unlock(&mmap_state.mtx); \ + } while (0) + +#define ERTS_MUNMAP_OP(PTR, SZ) \ + do { \ + int ix__; \ + if (++mmap_op_ix >= ERTS_MMAP_OP_RINGBUF_SZ) \ + mmap_op_ix = 0; \ + ix__ = mmap_op_ix; \ + mmap_ops[ix__].type = ERTS_OP_TYPE_MUNMAP; \ + mmap_ops[ix__].result = NULL; \ + mmap_ops[ix__].in_size = 0; \ + mmap_ops[ix__].out_size = 0; \ + mmap_ops[ix__].old_ptr = (PTR); \ + mmap_ops[ix__].old_size = (SZ); \ + } while (0) + +#define ERTS_MUNMAP_OP_LCK(PTR, SZ) \ + do { \ + erts_smp_mtx_lock(&mmap_state.mtx); \ + ERTS_MUNMAP_OP((PTR), (SZ)); \ + erts_smp_mtx_unlock(&mmap_state.mtx); \ + } while (0) + +#define ERTS_MREMAP_OP_START(OLD_PTR, OLD_SZ, IN_SZ) \ + do { \ + int ix__; \ + if (++mmap_op_ix >= ERTS_MMAP_OP_RINGBUF_SZ) \ + mmap_op_ix = 0; \ + ix__ = mmap_op_ix; \ + mmap_ops[ix__].type = ERTS_OP_TYPE_MREMAP; \ + mmap_ops[ix__].result = NULL; \ + mmap_ops[ix__].in_size = (IN_SZ); \ + mmap_ops[ix__].out_size = (OLD_SZ); \ + mmap_ops[ix__].old_ptr = (OLD_PTR); \ + mmap_ops[ix__].old_size = (OLD_SZ); \ + } while (0) + +#define ERTS_MREMAP_OP_END(PTR, SZ) \ + do { \ + int ix__ = mmap_op_ix; \ + mmap_ops[ix__].result = (PTR); \ + mmap_ops[mmap_op_ix].out_size = (SZ); \ + } while (0) + +#define ERTS_MREMAP_OP_LCK(RES, OLD_PTR, OLD_SZ, IN_SZ, OUT_SZ) \ + do { \ + erts_smp_mtx_lock(&mmap_state.mtx); \ + ERTS_MREMAP_OP_START((OLD_PTR), (OLD_SZ), (IN_SZ)); \ + ERTS_MREMAP_OP_END((RES), (OUT_SZ)); \ + erts_smp_mtx_unlock(&mmap_state.mtx); \ + } while (0) + +#define ERTS_MMAP_OP_ABORT() \ + do { \ + int ix__ = mmap_op_ix; \ + mmap_ops[ix__].type = ERTS_OP_TYPE_NONE; \ + mmap_ops[ix__].result = NULL; \ + mmap_ops[ix__].in_size = 0; \ + mmap_ops[ix__].out_size = 0; \ + mmap_ops[ix__].old_ptr = NULL; \ + mmap_ops[ix__].old_size = 0; \ + if (--mmap_op_ix < 0) \ + mmap_op_ix = ERTS_MMAP_OP_RINGBUF_SZ-1; \ + } while (0) + +#else + +#define ERTS_MMAP_OP_RINGBUF_INIT() +#define ERTS_MMAP_OP_START(SZ) +#define ERTS_MMAP_OP_END(PTR, SZ) +#define ERTS_MMAP_OP_LCK(RES, IN_SZ, OUT_SZ) +#define ERTS_MUNMAP_OP(PTR, SZ) +#define ERTS_MUNMAP_OP_LCK(PTR, SZ) +#define ERTS_MREMAP_OP_START(OLD_PTR, OLD_SZ, IN_SZ) +#define ERTS_MREMAP_OP_END(PTR, SZ) +#define ERTS_MREMAP_OP_LCK(RES, OLD_PTR, OLD_SZ, IN_SZ, OUT_SZ) +#define ERTS_MMAP_OP_ABORT() + +#endif typedef struct { RBTNode snode; /* node in 'stree' */ @@ -167,7 +318,19 @@ static struct { int mmap_fd; #endif erts_smp_mtx_t mtx; - char *desc_free_list; + struct { + char *free_list; + char *unused_start; + char *unused_end; + char *new_area_hint; + } desc; + struct { + UWord free_seg_descs; + struct { + UWord curr; + UWord max; + } free_segs; + } no; struct { struct { UWord total; @@ -230,12 +393,15 @@ static struct { static void add_free_desc_area(char *start, char *end) { - if (end > start && sizeof(ErtsFreeSegDesc) <= end - start) { + ERTS_MMAP_ASSERT(end == (void *) 0 || end > start); + if (sizeof(ErtsFreeSegDesc) <= ((UWord) end) - ((UWord) start)) { + UWord no; ErtsFreeSegDesc *prev_desc, *desc; char *desc_end; + no = 1; prev_desc = (ErtsFreeSegDesc *) start; - prev_desc->start = mmap_state.desc_free_list; + prev_desc->start = mmap_state.desc.free_list; desc = (ErtsFreeSegDesc *) (start + sizeof(ErtsFreeSegDesc)); desc_end = start + 2*sizeof(ErtsFreeSegDesc); @@ -244,26 +410,61 @@ add_free_desc_area(char *start, char *end) prev_desc = desc; desc = (ErtsFreeSegDesc *) desc_end; desc_end += sizeof(ErtsFreeSegDesc); + no++; } - mmap_state.desc_free_list = (char *) prev_desc; + mmap_state.desc.free_list = (char *) prev_desc; + mmap_state.no.free_seg_descs += no; } } +static ErtsFreeSegDesc * +add_unused_free_desc_area(void) +{ + char *ptr; + if (!mmap_state.desc.unused_start) + return NULL; + + ERTS_MMAP_ASSERT(mmap_state.desc.unused_end); + ERTS_MMAP_ASSERT(ERTS_PAGEALIGNED_SIZE + <= mmap_state.desc.unused_end - mmap_state.desc.unused_start); + + ptr = mmap_state.desc.unused_start + ERTS_PAGEALIGNED_SIZE; + add_free_desc_area(mmap_state.desc.unused_start, ptr); + + if ((mmap_state.desc.unused_end - ptr) >= ERTS_PAGEALIGNED_SIZE) + mmap_state.desc.unused_start = ptr; + else + mmap_state.desc.unused_end = mmap_state.desc.unused_start = NULL; + + ERTS_MMAP_ASSERT(mmap_state.desc.free_list); + return (ErtsFreeSegDesc *) mmap_state.desc.free_list; +} + static ERTS_INLINE ErtsFreeSegDesc * alloc_desc(void) { ErtsFreeSegDesc *res; - res = (ErtsFreeSegDesc *) mmap_state.desc_free_list; - if (res) - mmap_state.desc_free_list = res->start; + res = (ErtsFreeSegDesc *) mmap_state.desc.free_list; + if (!res) { + res = add_unused_free_desc_area(); + if (!res) + return NULL; + } + mmap_state.desc.free_list = res->start; + ASSERT(mmap_state.no.free_segs.curr < mmap_state.no.free_seg_descs); + mmap_state.no.free_segs.curr++; + if (mmap_state.no.free_segs.max < mmap_state.no.free_segs.curr) + mmap_state.no.free_segs.max = mmap_state.no.free_segs.curr; return res; } static ERTS_INLINE void free_desc(ErtsFreeSegDesc *desc) { - desc->start = mmap_state.desc_free_list; - mmap_state.desc_free_list = (char *) desc; + desc->start = mmap_state.desc.free_list; + mmap_state.desc.free_list = (char *) desc; + ERTS_MMAP_ASSERT(mmap_state.no.free_segs.curr > 0); + mmap_state.no.free_segs.curr--; } static ERTS_INLINE ErtsFreeSegDesc* anode_to_desc(RBTNode* anode) @@ -1040,7 +1241,7 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map) #endif static ERTS_INLINE void * -os_mmap(UWord size, int try_superalign) +os_mmap(void *hint_ptr, UWord size, int try_superalign) { #if HAVE_MMAP void *res; @@ -1050,7 +1251,7 @@ os_mmap(UWord size, int try_superalign) ERTS_MMAP_FLAGS|MAP_ALIGN, ERTS_MMAP_FD, 0); else #endif - res = mmap((void *) 0, size, ERTS_MMAP_PROT, + res = mmap((void *) hint_ptr, size, ERTS_MMAP_PROT, ERTS_MMAP_FLAGS, ERTS_MMAP_FD, 0); if (res == MAP_FAILED) return NULL; @@ -1179,37 +1380,90 @@ static void unreserve_noop(char *ptr, UWord size) #endif } -static ERTS_INLINE UWord +static UWord alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end) { - UWord ad_sz; - char *new_end; + char *ptr; + ErtsFreeSegMap *da_map; ErtsFreeSegDesc *desc = alloc_desc(); if (desc) { insert_free_seg(map, desc, start, end); return 0; } - /* Use part of the free segment for descriptors */ + /* + * Ahh; ran out of free segment descriptors. + * + * First try to map a new page... + */ - if (map == &mmap_state.sa.map) { - ERTS_MMAP_SIZE_SC_SA_INC(ERTS_SUPERALIGNED_SIZE); - ad_sz = ERTS_SUPERALIGNED_SIZE; +#if ERTS_HAVE_OS_MMAP + ptr = os_mmap(mmap_state.desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0); + if (ptr) { + mmap_state.desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE; + ERTS_MMAP_SIZE_OS_INC(ERTS_PAGEALIGNED_SIZE); + add_free_desc_area(ptr, ptr+ERTS_PAGEALIGNED_SIZE); + desc = alloc_desc(); + ERTS_MMAP_ASSERT(desc); + insert_free_seg(map, desc, start, end); + return 0; + } +#endif + + /* + * ...then try to find a good place in the supercarrier... + */ + da_map = &mmap_state.sua.map; + desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE); + if (desc) { + if (mmap_state.reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE)) + ERTS_MMAP_SIZE_SC_SUA_INC(ERTS_PAGEALIGNED_SIZE); + else + desc = NULL; + } else { - ERTS_MMAP_SIZE_SC_SUA_INC(ERTS_PAGEALIGNED_SIZE); - ad_sz = ERTS_PAGEALIGNED_SIZE; + da_map = &mmap_state.sa.map; + desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE); + if (desc) { + if (mmap_state.reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE)) + ERTS_MMAP_SIZE_SC_SA_INC(ERTS_PAGEALIGNED_SIZE); + else + desc = NULL; + } } + if (desc) { + char *da_end = desc->start + ERTS_PAGEALIGNED_SIZE; + add_free_desc_area(desc->start, da_end); + if (da_end != desc->end) + resize_free_seg(da_map, desc, da_end, desc->end); + else { + delete_free_seg(da_map, desc); + free_desc(desc); + } - new_end = end - ad_sz; - ERTS_MMAP_ASSERT(start <= new_end); - if (start != new_end) { desc = alloc_desc(); ERTS_MMAP_ASSERT(desc); - insert_free_seg(map, desc, start, new_end); + insert_free_seg(map, desc, start, end); + return 0; } - return ad_sz; + /* + * ... and then as last resort use the first page of the + * free segment we are trying to insert for free descriptors. + */ + ptr = start + ERTS_PAGEALIGNED_SIZE; + ERTS_MMAP_ASSERT(ptr <= end); + + add_free_desc_area(start, ptr); + + if (ptr != end) { + desc = alloc_desc(); + ERTS_MMAP_ASSERT(desc); + insert_free_seg(map, desc, ptr, end); + } + + return ERTS_PAGEALIGNED_SIZE; } void * @@ -1226,6 +1480,8 @@ erts_mmap(Uint32 flags, UWord *sizep) erts_smp_mtx_lock(&mmap_state.mtx); + ERTS_MMAP_OP_START(*sizep); + if (!superaligned) { desc = lookup_free_seg(&mmap_state.sua.map, asize); if (desc) { @@ -1290,10 +1546,10 @@ erts_mmap(Uint32 flags, UWord *sizep) } if (superaligned) { - seg = (char *) ERTS_SUPERALIGNED_CEILING(mmap_state.sa.top); + char *start = mmap_state.sa.top; + seg = (char *) ERTS_SUPERALIGNED_CEILING(start); - if (asize <= mmap_state.sua.bot - seg) { - char *start = mmap_state.sa.top; + if (asize + (seg - start) <= mmap_state.sua.bot - start) { end = seg + asize; if (!mmap_state.reserve_physical(start, (UWord) (end - start))) goto supercarrier_reserve_failure; @@ -1342,6 +1598,7 @@ erts_mmap(Uint32 flags, UWord *sizep) } } + ERTS_MMAP_OP_ABORT(); erts_smp_mtx_unlock(&mmap_state.mtx); } @@ -1349,15 +1606,15 @@ erts_mmap(Uint32 flags, UWord *sizep) /* Map using OS primitives */ if (!(ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) && !mmap_state.no_os_mmap) { if (!(ERTS_MMAPFLG_SUPERALIGNED & flags)) { - seg = os_mmap(asize, 0); + seg = os_mmap(NULL, asize, 0); if (!seg) - return NULL; + goto failure; } else { asize = ERTS_SUPERALIGNED_CEILING(*sizep); - seg = os_mmap(asize, 1); + seg = os_mmap(NULL, asize, 1); if (!seg) - return NULL; + goto failure; if (!ERTS_IS_SUPERALIGNED(seg)) { char *ptr; @@ -1365,9 +1622,9 @@ erts_mmap(Uint32 flags, UWord *sizep) os_munmap(seg, asize); - ptr = os_mmap(asize + ERTS_SUPERALIGNED_SIZE, 1); + ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1); if (!ptr) - return NULL; + goto failure; seg = (char *) ERTS_SUPERALIGNED_CEILING(ptr); sz = (UWord) (seg - ptr); @@ -1380,11 +1637,14 @@ erts_mmap(Uint32 flags, UWord *sizep) } } + ERTS_MMAP_OP_LCK(seg, *sizep, asize); ERTS_MMAP_SIZE_OS_INC(asize); *sizep = asize; return (void *) seg; } +failure: #endif + ERTS_MMAP_OP_LCK(NULL, *sizep, 0); *sizep = 0; return NULL; @@ -1401,6 +1661,7 @@ supercarrier_success: } #endif + ERTS_MMAP_OP_END(seg, asize); erts_smp_mtx_unlock(&mmap_state.mtx); *sizep = asize; @@ -1408,10 +1669,8 @@ supercarrier_success: supercarrier_reserve_failure: erts_smp_mtx_unlock(&mmap_state.mtx); - *sizep = 0; return NULL; - } void @@ -1419,9 +1678,11 @@ erts_munmap(Uint32 flags, void *ptr, UWord size) { ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(ptr)); ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(size)); + if (!ERTS_MMAP_IN_SUPERCARRIER(ptr)) { ERTS_MMAP_ASSERT(!mmap_state.no_os_mmap); #if ERTS_HAVE_OS_MMAP + ERTS_MUNMAP_OP_LCK(ptr, size); ERTS_MMAP_SIZE_OS_DEC(size); os_munmap(ptr, size); #endif @@ -1439,6 +1700,8 @@ erts_munmap(Uint32 flags, void *ptr, UWord size) erts_smp_mtx_lock(&mmap_state.mtx); + ERTS_MUNMAP_OP(ptr, size); + if (ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)) { map = &mmap_state.sa.map; @@ -1504,7 +1767,7 @@ erts_munmap(Uint32 flags, void *ptr, UWord size) ERTS_MMAP_ASSERT(size >= ad_sz); unres_sz = size - ad_sz; if (unres_sz) - mmap_state.unreserve_physical((char *) ptr, unres_sz); + mmap_state.unreserve_physical(((char *) ptr) + ad_sz, unres_sz); } } } @@ -1546,8 +1809,10 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) return new_ptr; } - if (ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) + if (ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) { + ERTS_MREMAP_OP_LCK(NULL, ptr, old_size, *sizep, old_size); return NULL; + } #if ERTS_HAVE_OS_MREMAP || ERTS_HAVE_GENUINE_OS_MMAP superaligned = (ERTS_MMAPFLG_SUPERALIGNED & flags); @@ -1555,6 +1820,7 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) if (superaligned) { asize = ERTS_SUPERALIGNED_CEILING(*sizep); if (asize == old_size && ERTS_IS_SUPERALIGNED(ptr)) { + ERTS_MREMAP_OP_LCK(ptr, ptr, old_size, *sizep, asize); *sizep = asize; return ptr; } @@ -1562,6 +1828,7 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) else { asize = ERTS_PAGEALIGNED_CEILING(*sizep); if (asize == old_size) { + ERTS_MREMAP_OP_LCK(ptr, ptr, old_size, *sizep, asize); *sizep = asize; return ptr; } @@ -1577,6 +1844,7 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) um_sz = (UWord) ((((char *) ptr) + old_size) - (char *) new_ptr); ERTS_MMAP_SIZE_OS_DEC(um_sz); os_munmap(new_ptr, um_sz); + ERTS_MREMAP_OP_LCK(ptr, ptr, old_size, *sizep, asize); *sizep = asize; return ptr; } @@ -1592,6 +1860,7 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) ERTS_MMAP_SIZE_OS_INC(asize - old_size); else ERTS_MMAP_SIZE_OS_DEC(old_size - asize); + ERTS_MREMAP_OP_LCK(new_ptr, ptr, old_size, *sizep, asize); *sizep = asize; return new_ptr; } @@ -1630,6 +1899,8 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) old_size, sizep); } + ERTS_MREMAP_OP_START(ptr, old_size, *sizep); + if (asize == old_size) { new_ptr = ptr; goto supercarrier_resize_success; @@ -1670,7 +1941,7 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) ERTS_MMAP_ASSERT(old_size - asize >= ad_sz); unres_sz = old_size - asize - ad_sz; if (unres_sz) - mmap_state.unreserve_physical(((char *) ptr) + asize, + mmap_state.unreserve_physical(((char *) ptr) + asize + ad_sz, unres_sz); goto supercarrier_resize_success; } @@ -1728,6 +1999,8 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) } } } + + ERTS_MMAP_OP_ABORT(); erts_smp_mtx_unlock(&mmap_state.mtx); /* Failed to resize... */ @@ -1749,15 +2022,16 @@ supercarrier_resize_success: } #endif + ERTS_MREMAP_OP_END(new_ptr, asize); erts_smp_mtx_unlock(&mmap_state.mtx); *sizep = asize; return new_ptr; supercarrier_reserve_failure: - + ERTS_MREMAP_OP_END(NULL, old_size); erts_smp_mtx_unlock(&mmap_state.mtx); - *sizep = 0; + *sizep = old_size; return NULL; } @@ -1794,8 +2068,11 @@ erts_mmap_init(ErtsMMapInit *init) erl_exit(-1, "erts_mmap: Invalid pagesize: %bpu\n", pagesize); + ERTS_MMAP_OP_RINGBUF_INIT(); + erts_have_erts_mmap = 0; + mmap_state.supercarrier = 0; mmap_state.reserve_physical = reserve_noop; mmap_state.unreserve_physical = unreserve_noop; @@ -1851,7 +2128,7 @@ erts_mmap_init(ErtsMMapInit *init) * The whole supercarrier will by physically * reserved all the time. */ - start = os_mmap(sz, 1); + start = os_mmap(NULL, sz, 1); } if (!start) erl_exit(-1, @@ -1871,12 +2148,18 @@ erts_mmap_init(ErtsMMapInit *init) erts_have_erts_mmap |= ERTS_HAVE_ERTS_OS_MMAP; #endif + mmap_state.no.free_seg_descs = 0; + mmap_state.no.free_segs.curr = 0; + mmap_state.no.free_segs.max = 0; + mmap_state.size.supercarrier.total = 0; mmap_state.size.supercarrier.used.total = 0; mmap_state.size.supercarrier.used.sa = 0; mmap_state.size.supercarrier.used.sua = 0; mmap_state.size.os.used = 0; + mmap_state.desc.new_area_hint = NULL; + if (!start) { mmap_state.sa.bot = NULL; mmap_state.sua.top = NULL; @@ -1907,6 +2190,8 @@ erts_mmap_init(ErtsMMapInit *init) mmap_state.size.os.used += (UWord) (mmap_state.sa.bot - start); + mmap_state.desc.free_list = NULL; + if (end == (void *) 0) { /* * Very unlikely, but we need a guarantee @@ -1916,6 +2201,10 @@ erts_mmap_init(ErtsMMapInit *init) */ mmap_state.sua.top -= ERTS_PAGEALIGNED_SIZE; mmap_state.size.os.used += ERTS_PAGEALIGNED_SIZE; +#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION + if (!virtual_map || os_reserve_physical(mmap_state.sua.top, ERTS_PAGEALIGNED_SIZE)) +#endif + add_free_desc_area(mmap_state.sua.top, end); } mmap_state.size.supercarrier.total = (UWord) (mmap_state.sua.top - mmap_state.sa.bot); @@ -1924,23 +2213,21 @@ erts_mmap_init(ErtsMMapInit *init) * Area before (and after) super carrier * will be used for free segment descritors. */ - mmap_state.desc_free_list = NULL; -#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION - if (virtual_map && mmap_state.sa.bot - start > 0) - os_reserve_physical(start, mmap_state.sa.bot - start); -#endif - add_free_desc_area(start, mmap_state.sa.bot); #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION - if (virtual_map && end - mmap_state.sua.top > 0) - os_reserve_physical(mmap_state.sua.top, end - mmap_state.sua.top); + if (virtual_map && !os_reserve_physical(start, mmap_state.sa.bot - start)) + erl_exit(-1, "erts_mmap: Failed to reserve physical memory for descriptors\n"); #endif - add_free_desc_area(mmap_state.sua.top, end); + mmap_state.desc.unused_start = start; + mmap_state.desc.unused_end = mmap_state.sa.bot; init_free_seg_map(&mmap_state.sa.map, SA_SZ_ADDR_ORDER); init_free_seg_map(&mmap_state.sua.map, SZ_REVERSE_ADDR_ORDER); mmap_state.supercarrier = 1; erts_have_erts_mmap |= ERTS_HAVE_ERTS_SUPERCARRIER_MMAP; + + mmap_state.desc.new_area_hint = end; + } #if !ERTS_HAVE_OS_MMAP diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 2474015bcb..c65973b3be 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -132,7 +132,7 @@ typedef struct { typedef struct cache_t_ cache_t; struct cache_t_ { - Uint size; + UWord size; void *seg; cache_t *next; cache_t *prev; @@ -408,7 +408,7 @@ static ERTS_INLINE void mseg_cache_clear_node(cache_t *c) { c->prev = c; } -static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size, Uint flags) { +static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, UWord size, Uint flags) { cache_t *c; ERTS_DBG_MK_CHK_THR_ACCESS(mk); @@ -496,7 +496,7 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size, Ui static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flags) { - Uint size = (UWord) *size_p; + UWord size = *size_p; ERTS_DBG_MK_CHK_THR_ACCESS(mk); @@ -505,7 +505,7 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag int i, ix = SIZE_TO_CACHE_AREA_IDX(size); char *seg; cache_t *c; - Uint csize; + UWord csize; ASSERT(IS_2POW(size)); @@ -542,10 +542,10 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag void *seg; cache_t *c; cache_t *best = NULL; - Uint bdiff = 0; - Uint csize; - Uint bad_max_abs = mk->ma->abs_max_cache_bad_fit; - Uint bad_max_rel = mk->ma->rel_max_cache_bad_fit; + UWord bdiff = 0; + UWord csize; + UWord bad_max_abs = mk->ma->abs_max_cache_bad_fit; + UWord bad_max_rel = mk->ma->rel_max_cache_bad_fit; erts_circleq_foreach(c, &(mk->cache_unpowered_node)) { csize = c->size; @@ -562,7 +562,7 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag mseg_cache_clear_node(c); erts_circleq_push_head(&(mk->cache_free), c); - *size_p = (UWord) csize; + *size_p = csize; return seg; @@ -593,7 +593,7 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag ASSERT((size % GET_PAGE_SIZE) == 0); ASSERT((best->size % GET_PAGE_SIZE) == 0); - *size_p = (UWord) size; + *size_p = size; return seg; @@ -822,7 +822,7 @@ mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, UWord size, ERTS_MSEG_DEALLOC_STAT(mk,size); - if (opt->cache && cache_bless_segment(mk, seg, (Uint) size, flags)) { + if (opt->cache && cache_bless_segment(mk, seg, size, flags)) { schedule_cache_check(ma); goto done; } -- cgit v1.2.3 From 19e47f24c1fe3dc996e836da0e5f99cea86acdbd Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 12 Sep 2013 17:52:33 +0200 Subject: erts: Refactor rbt_insert in erl_mmap --- erts/emulator/sys/common/erl_mmap.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 1f9168df28..1876398ec4 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -881,12 +881,12 @@ rbt_delete(RBTree* tree, RBTNode* del) static void -rbt_insert(enum SortOrder order, RBTree* tree, RBTNode* node) +rbt_insert(RBTree* tree, RBTNode* node) { #ifdef RBT_DEBUG ErtsFreeSegDesc *dbg_under=NULL, *dbg_over=NULL; #endif - ErtsFreeSegDesc* desc = node_to_desc(order, node); + ErtsFreeSegDesc* desc = node_to_desc(tree->order, node); char* seg_addr = desc->start; SWord seg_sz = desc->end - desc->start; @@ -902,9 +902,9 @@ rbt_insert(enum SortOrder order, RBTree* tree, RBTNode* node) else { RBTNode *x = tree->root; while (1) { - SWord diff = cmp_with_node(order, seg_sz, seg_addr, x); + SWord diff = cmp_with_node(tree->order, seg_sz, seg_addr, x); if (diff < 0) { - IF_RBT_DEBUG(dbg_over = node_to_desc(order, x)); + IF_RBT_DEBUG(dbg_over = node_to_desc(tree->order, x)); if (!x->left) { node->parent_and_color = parent_and_color(x, RED_FLG); x->left = node; @@ -914,7 +914,7 @@ rbt_insert(enum SortOrder order, RBTree* tree, RBTNode* node) } else { RBT_ASSERT(diff > 0); - IF_RBT_DEBUG(dbg_under = node_to_desc(order, x)); + IF_RBT_DEBUG(dbg_under = node_to_desc(tree->order, x)); if (!x->right) { node->parent_and_color = parent_and_color(x, RED_FLG); x->right = node; @@ -926,7 +926,7 @@ rbt_insert(enum SortOrder order, RBTree* tree, RBTNode* node) RBT_ASSERT(parent(node)); #ifdef RBT_DEBUG - if (order == ADDR_ORDER) { + if (tree->order == ADDR_ORDER) { RBT_ASSERT(!dbg_under || dbg_under->end < desc->start); RBT_ASSERT(!dbg_over || dbg_over->start > desc->end); } @@ -1130,8 +1130,8 @@ static void insert_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc, { desc->start = start; desc->end = end; - rbt_insert(map->atree.order, &map->atree, &desc->anode); - rbt_insert(map->stree.order, &map->stree, &desc->snode); + rbt_insert(&map->atree, &desc->anode); + rbt_insert(&map->stree, &desc->snode); map->nseg++; } @@ -1151,7 +1151,7 @@ static void resize_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc, rbt_delete(&map->stree, &desc->snode); desc->start = start; desc->end = end; - rbt_insert(map->stree.order, &map->stree, &desc->snode); + rbt_insert(&map->stree, &desc->snode); } /* Delete existing free segment 'desc' from 'map'. -- cgit v1.2.3 From b9e82ba0be1364c64e90274d5e9bf37f78b676ee Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 13 Sep 2013 13:31:05 +0200 Subject: erts: Add HARD_DBG_MSEG --- erts/emulator/beam/erl_lock_check.c | 1 + erts/emulator/sys/common/erl_mmap.c | 142 +++++++++++++++++++++++++++++++++++- erts/emulator/sys/common/erl_mmap.h | 11 +++ erts/emulator/sys/common/erl_mseg.c | 6 ++ 4 files changed, 158 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 1e9cef3759..87efbdbc3e 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -186,6 +186,7 @@ static erts_lc_lock_order_t erts_lock_order[] = { #endif #endif { "erts_alloc_hard_debug", NULL }, + { "hard_dbg_mseg", NULL }, { "erts_mmap", NULL } }; diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 1876398ec4..93a95e5eef 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -1039,7 +1039,7 @@ rbt_foreach_node(RBTree* tree, #endif } -#ifdef RBT_DEBUG +#if defined(RBT_DEBUG) || defined(HARD_DEBUG_MSEG) static RBTNode* rbt_prev_node(RBTNode* node) { RBTNode* x; @@ -1068,7 +1068,7 @@ static RBTNode* rbt_next_node(RBTNode* node) } return NULL; } -#endif /* RBT_DEBUG */ +#endif /* RBT_DEBUG || HARD_DEBUG_MSEG */ /* The API to keep track of a bunch of separated (free) segments @@ -2041,6 +2041,10 @@ int erts_mmap_in_supercarrier(void *ptr) return ERTS_MMAP_IN_SUPERCARRIER(ptr); } +#ifdef HARD_DEBUG_MSEG +static void hard_dbg_mseg_init(void); +#endif + void erts_mmap_init(ErtsMMapInit *init) { @@ -2233,6 +2237,10 @@ erts_mmap_init(ErtsMMapInit *init) #if !ERTS_HAVE_OS_MMAP mmap_state.no_os_mmap = 1; #endif + +#ifdef HARD_DEBUG_MSEG + hard_dbg_mseg_init(); +#endif } Eterm erts_mmap_info(Process* p) @@ -2494,3 +2502,133 @@ void test_it(void) } #endif /* FREE_SEG_API_SMOKE_TEST */ + + +#ifdef HARD_DEBUG_MSEG + +/* + * Debug stuff used by erl_mseg to check that it does the right thing. + * The reason for keeping it here is that we (ab)use the rb-tree code + * for keeping track of *allocated* segments. + */ + +typedef struct ErtsFreeSegDesc_fake_ { + /*RBTNode snode; Save memory by skipping unused size tree node */ + RBTNode anode; /* node in 'atree' */ + union { + char* start; + struct ErtsFreeSegDesc_fake_* next_free; + }u; + char* end; +}ErtsFreeSegDesc_fake; + +static ErtsFreeSegDesc_fake hard_dbg_mseg_desc_pool[10000]; +static ErtsFreeSegDesc_fake* hard_dbg_mseg_desc_first; +RBTree hard_dbg_mseg_tree; + +static erts_mtx_t hard_dbg_mseg_mtx; + +static void hard_dbg_mseg_init(void) +{ + ErtsFreeSegDesc_fake* p; + + erts_mtx_init(&hard_dbg_mseg_mtx, "hard_dbg_mseg"); + hard_dbg_mseg_tree.root = NULL; + hard_dbg_mseg_tree.order = ADDR_ORDER; + + p = &hard_dbg_mseg_desc_pool[(sizeof(hard_dbg_mseg_desc_pool) / + sizeof(*hard_dbg_mseg_desc_pool)) - 1]; + p->u.next_free = NULL; + while (--p >= hard_dbg_mseg_desc_pool) { + p->u.next_free = (p+1); + } + hard_dbg_mseg_desc_first = &hard_dbg_mseg_desc_pool[0]; +} + +static ErtsFreeSegDesc* hard_dbg_alloc_desc(void) +{ + ErtsFreeSegDesc_fake* p = hard_dbg_mseg_desc_first; + ERTS_ASSERT(p || !"HARD_DEBUG_MSEG: Out of mseg descriptors"); + hard_dbg_mseg_desc_first = p->u.next_free; + + /* Creative pointer arithmetic to return something that looks like + * a ErtsFreeSegDesc as long as we don't use the absent 'snode'. + */ + return (ErtsFreeSegDesc*) ((char*)p - offsetof(ErtsFreeSegDesc,anode)); +} + +static void hard_dbg_free_desc(ErtsFreeSegDesc* desc) +{ + ErtsFreeSegDesc_fake* p = (ErtsFreeSegDesc_fake*) &desc->anode; + memset(p, 0xfe, sizeof(*p)); + p->u.next_free = hard_dbg_mseg_desc_first; + hard_dbg_mseg_desc_first = p; +} + +static void check_seg_writable(void* seg, UWord sz) +{ + UWord* seg_end = (UWord*)((char*)seg + sz); + volatile UWord* p; + ERTS_ASSERT(ERTS_IS_PAGEALIGNED(seg)); + ERTS_ASSERT(ERTS_IS_PAGEALIGNED(sz)); + for (p=(UWord*)seg; pstart = (char*)seg; + desc->end = desc->start + sz - 1; /* -1 to allow adjacent segments in tree */ + rbt_insert(&hard_dbg_mseg_tree, &desc->anode); + prev = rbt_prev_node(&desc->anode); + next = rbt_next_node(&desc->anode); + ERTS_ASSERT(!prev || anode_to_desc(prev)->end < desc->start); + ERTS_ASSERT(!next || anode_to_desc(next)->start > desc->end); + } + erts_mtx_unlock(&hard_dbg_mseg_mtx); +} + +static ErtsFreeSegDesc* hard_dbg_lookup_seg_at(RBTree* tree, char* start) +{ + RBTNode* x = tree->root; + + while (x) { + ErtsFreeSegDesc* desc = anode_to_desc(x); + if (start < desc->start) { + x = x->left; + } + else if (start > desc->start) { + ERTS_ASSERT(start > desc->end); + x = x->right; + } + else + return desc; + } + return NULL; +} + +void hard_dbg_remove_mseg(void* seg, UWord sz) +{ + check_seg_writable(seg, sz); + erts_mtx_lock(&hard_dbg_mseg_mtx); + { + ErtsFreeSegDesc* desc = hard_dbg_lookup_seg_at(&hard_dbg_mseg_tree, (char*)seg); + ERTS_ASSERT(desc); + ERTS_ASSERT(desc->start == (char*)seg); + ERTS_ASSERT(desc->end == (char*)seg + sz - 1); + + rbt_delete(&hard_dbg_mseg_tree, &desc->anode); + hard_dbg_free_desc(desc); + } + erts_mtx_unlock(&hard_dbg_mseg_mtx); +} + +#endif /* HARD_DEBUG_MSEG */ diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 6cb51fb0b4..106459f872 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -110,4 +110,15 @@ Eterm erts_mmap_info(struct process*); # define ERTS_HAVE_OS_MMAP 1 #endif +/*#define HARD_DEBUG_MSEG*/ +#ifdef HARD_DEBUG_MSEG +# define HARD_DBG_INSERT_MSEG hard_dbg_insert_mseg +# define HARD_DBG_REMOVE_MSEG hard_dbg_remove_mseg +void hard_dbg_insert_mseg(void* seg, UWord sz); +void hard_dbg_remove_mseg(void* seg, UWord sz); +#else +# define HARD_DBG_INSERT_MSEG(SEG,SZ) +# define HARD_DBG_REMOVE_MSEG(SEG,SZ) +#endif + #endif /* ERL_MMAP_H__ */ diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index c65973b3be..13f8069cf5 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -1356,6 +1356,7 @@ erts_mseg_alloc_opt(ErtsAlcType_t atype, UWord *size_p, Uint flags, const ErtsMs ERTS_DBG_MA_CHK_THR_ACCESS(ma); seg = mseg_alloc(ma, atype, size_p, flags, opt); ERTS_MSEG_UNLOCK(ma); + HARD_DBG_INSERT_MSEG(seg, *size_p); return seg; } @@ -1370,6 +1371,8 @@ erts_mseg_dealloc_opt(ErtsAlcType_t atype, void *seg, UWord size, Uint flags, const ErtsMsegOpt_t *opt) { ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_OPT(opt); + + HARD_DBG_REMOVE_MSEG(seg, size); ERTS_MSEG_LOCK(ma); ERTS_DBG_MA_CHK_THR_ACCESS(ma); mseg_dealloc(ma, atype, seg, size, flags, opt); @@ -1390,10 +1393,13 @@ erts_mseg_realloc_opt(ErtsAlcType_t atype, void *seg, { ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_OPT(opt); void *new_seg; + + HARD_DBG_REMOVE_MSEG(seg, old_size); ERTS_MSEG_LOCK(ma); ERTS_DBG_MA_CHK_THR_ACCESS(ma); new_seg = mseg_realloc(ma, atype, seg, old_size, new_size_p, flags, opt); ERTS_MSEG_UNLOCK(ma); + HARD_DBG_INSERT_MSEG(new_seg, *new_size_p); return new_seg; } -- cgit v1.2.3 From 4ba6824a90943e74e8fdd02f3cb695931093bcca Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 13 Sep 2013 17:37:44 +0200 Subject: erts: Fix race bug in erts_munmap Must keep mutex to serialize (un)reserve ops. --- erts/emulator/sys/common/erl_mmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 93a95e5eef..317e3ec391 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -1762,12 +1762,12 @@ erts_munmap(Uint32 flags, void *ptr, UWord size) supercarrier_success: { UWord unres_sz; - erts_smp_mtx_unlock(&mmap_state.mtx); - ERTS_MMAP_ASSERT(size >= ad_sz); unres_sz = size - ad_sz; if (unres_sz) mmap_state.unreserve_physical(((char *) ptr) + ad_sz, unres_sz); + + erts_smp_mtx_unlock(&mmap_state.mtx); } } } -- cgit v1.2.3 From 98c01224ff2c0dacc97613c080cc0f7268c6b2f1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 24 Sep 2013 22:26:31 +0200 Subject: erts: Fix bug in lookup_free_seg that could return segments that are too small after being super aligned. --- erts/emulator/sys/common/erl_mmap.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 317e3ec391..237820d3ec 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -482,6 +482,14 @@ static ERTS_INLINE ErtsFreeSegDesc* node_to_desc(enum SortOrder order, RBTNode* return order==ADDR_ORDER ? anode_to_desc(node) : snode_to_desc(node); } +static ERTS_INLINE SWord usable_size(enum SortOrder order, + ErtsFreeSegDesc* desc) +{ + return ((order == SA_SZ_ADDR_ORDER) ? + ERTS_SUPERALIGNED_FLOOR(desc->end) - ERTS_SUPERALIGNED_CEILING(desc->start) + : desc->end - desc->start); +} + #ifdef HARD_DEBUG static ERTS_INLINE SWord cmp_nodes(enum SortOrder order, RBTNode* lhs, RBTNode* rhs) @@ -490,17 +498,7 @@ static ERTS_INLINE SWord cmp_nodes(enum SortOrder order, ErtsFreeSegDesc* rdesc = node_to_desc(order, rhs); RBT_ASSERT(lhs != rhs); if (order != ADDR_ORDER) { - SWord lsz, rsz, diff; - if (order == SA_SZ_ADDR_ORDER) { - lsz = ERTS_SUPERALIGNED_FLOOR(ldesc->end) - ERTS_SUPERALIGNED_CEILING(ldesc->start); - rsz = ERTS_SUPERALIGNED_FLOOR(rdesc->end) - ERTS_SUPERALIGNED_CEILING(rdesc->start); - } - else { - RBT_ASSERT(order == SZ_REVERSE_ADDR_ORDER); - lsz = ldesc->end - ldesc->start; - rsz = rdesc->end - rdesc->start; - } - diff = lsz - rsz; + SWord diff = usable_size(order, ldesc) - usable_size(order, rdesc); if (diff) return diff; } if (order != SZ_REVERSE_ADDR_ORDER) { @@ -517,13 +515,9 @@ static ERTS_INLINE SWord cmp_with_node(enum SortOrder order, { ErtsFreeSegDesc* rdesc; if (order != ADDR_ORDER) { - SWord rhs_sz, diff; + SWord diff; rdesc = snode_to_desc(rhs); - if (order == SA_SZ_ADDR_ORDER) - rhs_sz = ERTS_SUPERALIGNED_FLOOR(rdesc->end) - ERTS_SUPERALIGNED_CEILING(rdesc->start); - else - rhs_sz = rdesc->end - rdesc->start; - diff = sz - rhs_sz; + diff = sz - usable_size(order, rdesc); if (diff) return diff; } else @@ -1163,16 +1157,17 @@ static void delete_free_seg(ErtsFreeSegMap* map, ErtsFreeSegDesc* desc) map->nseg--; } -/* Lookup a free segment in 'map' with a size of at least 'need_sz' bytes. +/* Lookup a free segment in 'map' with a size of at least 'need_sz' usable bytes. */ static ErtsFreeSegDesc* lookup_free_seg(ErtsFreeSegMap* map, SWord need_sz) { RBTNode* x = map->stree.root; ErtsFreeSegDesc* best_desc = NULL; + const enum SortOrder order = map->stree.order; while (x) { ErtsFreeSegDesc* desc = snode_to_desc(x); - SWord seg_sz = desc->end - desc->start; + SWord seg_sz = usable_size(order, desc); if (seg_sz < need_sz) { x = x->right; -- cgit v1.2.3 From d6d531012957f8e3315d44c2bcb10938ab5d6e72 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 25 Sep 2013 17:23:55 +0200 Subject: erts: Rename erts_bld_atom_uint_2tup_list to *_uword_* and change from Uint to UWord values --- erts/emulator/beam/erl_trace.c | 38 ++++++++++++++++++------------------- erts/emulator/beam/erl_utils.h | 6 +++--- erts/emulator/beam/utils.c | 4 ++-- erts/emulator/sys/common/erl_mmap.c | 2 +- 4 files changed, 25 insertions(+), 25 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index fa015ee4b9..ff7fdfcfca 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -2184,7 +2184,7 @@ trace_gc(Process *p, Eterm what) AM_bin_old_vheap_block_size }; - Uint values[] = { + UWord values[] = { OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) : 0, HEAP_SIZE(p), MBUF_SIZE(p), @@ -2198,7 +2198,7 @@ trace_gc(Process *p, Eterm what) BIN_OLD_VHEAP_SZ(p) }; #define LOCAL_HEAP_SIZE \ - (sizeof(values)/sizeof(Eterm)) * \ + (sizeof(values)/sizeof(*values)) * \ (2/*cons*/ + 3/*2-tuple*/ + BIG_UINT_HEAP_SIZE) + \ 5/*4-tuple */ + TS_HEAP_WORDS DeclareTmpHeap(local_heap,LOCAL_HEAP_SIZE,p); @@ -2206,7 +2206,7 @@ trace_gc(Process *p, Eterm what) Eterm* limit; #endif - ASSERT(sizeof(values)/sizeof(Uint) == sizeof(tags)/sizeof(Eterm)); + ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(Eterm)); UseTmpHeap(LOCAL_HEAP_SIZE,p); @@ -2214,9 +2214,9 @@ trace_gc(Process *p, Eterm what) hp = local_heap; #ifdef DEBUG size = 0; - (void) erts_bld_atom_uint_2tup_list(NULL, + (void) erts_bld_atom_uword_2tup_list(NULL, &size, - sizeof(values)/sizeof(Uint), + sizeof(values)/sizeof(*values), tags, values); size += 5/*4-tuple*/ + TS_SIZE(p); @@ -2229,9 +2229,9 @@ trace_gc(Process *p, Eterm what) ERTS_TRACE_FLAGS(p)); size = 0; - (void) erts_bld_atom_uint_2tup_list(NULL, + (void) erts_bld_atom_uword_2tup_list(NULL, &size, - sizeof(values)/sizeof(Uint), + sizeof(values)/sizeof(*values), tags, values); size += 5/*4-tuple*/ + TS_SIZE(p); @@ -2244,9 +2244,9 @@ trace_gc(Process *p, Eterm what) ASSERT(size <= LOCAL_HEAP_SIZE); #endif - msg = erts_bld_atom_uint_2tup_list(&hp, + msg = erts_bld_atom_uword_2tup_list(&hp, NULL, - sizeof(values)/sizeof(Uint), + sizeof(values)/sizeof(*values), tags, values); @@ -2415,7 +2415,7 @@ monitor_long_gc(Process *p, Uint time) { am_old_heap_size, am_heap_size }; - Eterm values[] = { + UWord values[] = { time, OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) : 0, HEAP_SIZE(p), @@ -2436,9 +2436,9 @@ monitor_long_gc(Process *p, Uint time) { #endif hsz = 0; - (void) erts_bld_atom_uint_2tup_list(NULL, + (void) erts_bld_atom_uword_2tup_list(NULL, &hsz, - sizeof(values)/sizeof(Uint), + sizeof(values)/sizeof(*values), tags, values); hsz += 5 /* 4-tuple */; @@ -2449,9 +2449,9 @@ monitor_long_gc(Process *p, Uint time) { hp_end = hp + hsz; #endif - list = erts_bld_atom_uint_2tup_list(&hp, + list = erts_bld_atom_uword_2tup_list(&hp, NULL, - sizeof(values)/sizeof(Uint), + sizeof(values)/sizeof(*values), tags, values); msg = TUPLE4(hp, am_monitor, p->common.id, am_long_gc, list); @@ -2489,7 +2489,7 @@ monitor_large_heap(Process *p) { am_old_heap_size, am_heap_size }; - Uint values[] = { + UWord values[] = { OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) : 0, HEAP_SIZE(p), MBUF_SIZE(p), @@ -2511,9 +2511,9 @@ monitor_large_heap(Process *p) { #endif hsz = 0; - (void) erts_bld_atom_uint_2tup_list(NULL, + (void) erts_bld_atom_uword_2tup_list(NULL, &hsz, - sizeof(values)/sizeof(Uint), + sizeof(values)/sizeof(*values), tags, values); hsz += 5 /* 4-tuple */; @@ -2524,9 +2524,9 @@ monitor_large_heap(Process *p) { hp_end = hp + hsz; #endif - list = erts_bld_atom_uint_2tup_list(&hp, + list = erts_bld_atom_uword_2tup_list(&hp, NULL, - sizeof(values)/sizeof(Uint), + sizeof(values)/sizeof(*values), tags, values); msg = TUPLE4(hp, am_monitor, p->common.id, am_large_heap, list); diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 80d29d554a..f85c7410c4 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012. All Rights Reserved. + * Copyright Ericsson AB 2012-2013. All Rights Reserved. * * The 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,8 +175,8 @@ Eterm erts_bld_list(Uint **hpp, Uint *szp, Sint length, Eterm terms[]); Eterm erts_bld_2tup_list(Uint **hpp, Uint *szp, Sint length, Eterm terms1[], Uint terms2[]); Eterm -erts_bld_atom_uint_2tup_list(Uint **hpp, Uint *szp, - Sint length, Eterm atoms[], Uint uints[]); +erts_bld_atom_uword_2tup_list(Uint **hpp, Uint *szp, + Sint length, Eterm atoms[], UWord uints[]); Eterm erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length, Eterm atoms[], Uint uints1[], Uint uints2[]); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index bd2be7afca..0d75bbcc77 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -576,8 +576,8 @@ erts_bld_2tup_list(Uint **hpp, Uint *szp, } Eterm -erts_bld_atom_uint_2tup_list(Uint **hpp, Uint *szp, - Sint length, Eterm atoms[], Uint uints[]) +erts_bld_atom_uword_2tup_list(Uint **hpp, Uint *szp, + Sint length, Eterm atoms[], UWord uints[]) { Sint i; Eterm res = THE_NON_VALUE; diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 237820d3ec..4392ec4f9c 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -2265,7 +2265,7 @@ Eterm erts_mmap_info(Process* p) hp = HAlloc(p, may_need); hp_end = hp + may_need; - list = erts_bld_atom_uint_2tup_list(&hp, NULL, + list = erts_bld_atom_uword_2tup_list(&hp, NULL, sizeof(values)/sizeof(*values), tags, values); -- cgit v1.2.3 From 8d5b9a53a1fd5e2264d705911af23cd484ccead0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 26 Sep 2013 22:30:26 +0200 Subject: erts: Add erts_bld_tupleX macros for compile time argument checking --- erts/emulator/beam/erl_alloc.c | 12 ++++++------ erts/emulator/beam/erl_utils.h | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index e30b3e7b51..ad1020d7d6 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3060,13 +3060,13 @@ reply_alloc_info(void *vair) ? NIL : erts_mseg_info(0, NULL, NULL, hpp != NULL, hpp, szp)); - ainfo = erts_bld_tuple(hpp, szp, 3, - alloc_atom, - make_small(0), - ainfo); + ainfo = erts_bld_tuple3(hpp, szp, + alloc_atom, + make_small(0), + ainfo); #else - ainfo = erts_bld_tuple(hpp, szp, 2, alloc_atom, - am_false); + ainfo = erts_bld_tuple2(hpp, szp, alloc_atom, + am_false); #endif break; default: diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index f85c7410c4..292d135946 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -168,6 +168,10 @@ Eterm erts_bld_uint64(Uint **hpp, Uint *szp, Uint64 ui64); Eterm erts_bld_sint64(Uint **hpp, Uint *szp, Sint64 si64); Eterm erts_bld_cons(Uint **hpp, Uint *szp, Eterm car, Eterm cdr); Eterm erts_bld_tuple(Uint **hpp, Uint *szp, Uint arity, ...); +#define erts_bld_tuple2(H,S,E1,E2) erts_bld_tuple(H,S,2,E1,E2) +#define erts_bld_tuple3(H,S,E1,E2,E3) erts_bld_tuple(H,S,3,E1,E2,E3) +#define erts_bld_tuple4(H,S,E1,E2,E3,E4) erts_bld_tuple(H,S,4,E1,E2,E3,E4) +#define erts_bld_tuple5(H,S,E1,E2,E3,E4,E5) erts_bld_tuple(H,S,5,E1,E2,E3,E4,E5) Eterm erts_bld_tuplev(Uint **hpp, Uint *szp, Uint arity, Eterm terms[]); Eterm erts_bld_string_n(Uint **hpp, Uint *szp, const char *str, Sint len); #define erts_bld_string(hpp,szp,str) erts_bld_string_n(hpp,szp,str,strlen(str)) -- cgit v1.2.3 From 059d8b76011f960cc5938501a33002b051b0bca2 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 26 Sep 2013 22:36:34 +0200 Subject: erts: Add erts_mmap stats As part of erlang:system_info({allocator,mseg_alloc}) and erl_crash.dump --- erts/emulator/beam/erl_alloc.c | 14 ++- erts/emulator/beam/erl_bif_info.c | 2 +- erts/emulator/sys/common/erl_mmap.c | 193 +++++++++++++++++++++++++++++++++++- erts/emulator/sys/common/erl_mmap.h | 12 ++- erts/emulator/sys/common/erl_mseg.c | 5 +- erts/preloaded/ebin/erlang.beam | Bin 94156 -> 94244 bytes erts/preloaded/src/erlang.erl | 2 + 7 files changed, 222 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index ad1020d7d6..d8da616d05 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -2712,6 +2712,7 @@ erts_allocator_info(int to, void *arg) #if HAVE_ERTS_MSEG { + struct erts_mmap_info_struct emis; #ifdef ERTS_SMP int max = (int) erts_no_schedulers; #else @@ -2722,6 +2723,8 @@ erts_allocator_info(int to, void *arg) erts_print(to, arg, "=allocator:mseg_alloc[%d]\n", i); erts_mseg_info(i, &to, arg, 0, NULL, NULL); } + erts_print(to, arg, "=allocator:mseg_alloc.erts_mmap\n"); + erts_mmap_info(&to, arg, NULL, NULL, &emis); } #endif @@ -2948,6 +2951,7 @@ reply_alloc_info(void *vair) Uint sz, *szp; ErlOffHeap *ohp = NULL; ErlHeapFragment *bp = NULL; + struct erts_mmap_info_struct emis; int i; Eterm (*info_func)(Allctr_t *, int, @@ -3064,11 +3068,19 @@ reply_alloc_info(void *vair) alloc_atom, make_small(0), ainfo); + + ai_list = erts_bld_cons(hpp, szp, + ainfo, ai_list); + ainfo = (air->only_sz ? NIL : erts_mmap_info(NULL, NULL, hpp, szp, &emis)); + ainfo = erts_bld_tuple3(hpp, szp, + alloc_atom, + erts_bld_atom(hpp,szp,"erts_mmap"), + ainfo); #else ainfo = erts_bld_tuple2(hpp, szp, alloc_atom, am_false); #endif - break; + break; default: alloc_atom = erts_bld_atom(hpp, szp, (char *) ERTS_ALC_A2AD(ai)); diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 7aa439f2e6..5fbcbbe250 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3290,7 +3290,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) BIF_RET(res); } else if (ERTS_IS_ATOM_STR("mmap", BIF_ARG_1)) { - BIF_RET(erts_mmap_info(BIF_P)); + BIF_RET(erts_mmap_debug_info(BIF_P)); } } else if (is_tuple(BIF_ARG_1)) { diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 4392ec4f9c..60b32e8115 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -323,6 +323,7 @@ static struct { char *unused_start; char *unused_end; char *new_area_hint; + Uint reserved; } desc; struct { UWord free_seg_descs; @@ -2190,6 +2191,7 @@ erts_mmap_init(ErtsMMapInit *init) mmap_state.size.os.used += (UWord) (mmap_state.sa.bot - start); mmap_state.desc.free_list = NULL; + mmap_state.desc.reserved = 0; if (end == (void *) 0) { /* @@ -2204,6 +2206,7 @@ erts_mmap_init(ErtsMMapInit *init) if (!virtual_map || os_reserve_physical(mmap_state.sua.top, ERTS_PAGEALIGNED_SIZE)) #endif add_free_desc_area(mmap_state.sua.top, end); + mmap_state.desc.reserved += (end - mmap_state.sua.top) / sizeof(ErtsFreeSegDesc); } mmap_state.size.supercarrier.total = (UWord) (mmap_state.sua.top - mmap_state.sa.bot); @@ -2218,6 +2221,8 @@ erts_mmap_init(ErtsMMapInit *init) #endif mmap_state.desc.unused_start = start; mmap_state.desc.unused_end = mmap_state.sa.bot; + mmap_state.desc.reserved += ((mmap_state.desc.unused_end - start) + / sizeof(ErtsFreeSegDesc)); init_free_seg_map(&mmap_state.sa.map, SA_SZ_ADDR_ORDER); init_free_seg_map(&mmap_state.sua.map, SZ_REVERSE_ADDR_ORDER); @@ -2238,7 +2243,192 @@ erts_mmap_init(ErtsMMapInit *init) #endif } -Eterm erts_mmap_info(Process* p) +static struct { + Eterm total; + Eterm total_sa; + Eterm total_sua; + Eterm used; + Eterm used_sa; + Eterm used_sua; + Eterm max; + Eterm allocated; + Eterm reserved; + Eterm sizes; + Eterm free_segs; + Eterm supercarrier; + Eterm os; + Eterm scs; + Eterm sco; + Eterm scrpm; + Eterm scmgc; + + int is_initialized; +}am; + +static void ERTS_INLINE atom_init(Eterm *atom, char *name) +{ + *atom = am_atom_put(name, strlen(name)); +} +#define AM_INIT(AM) atom_init(&am.AM, #AM) + +static void init_atoms(void) +{ + AM_INIT(total); + AM_INIT(total_sa); + AM_INIT(total_sua); + AM_INIT(used); + AM_INIT(used_sa); + AM_INIT(used_sua); + AM_INIT(max); + AM_INIT(allocated); + AM_INIT(reserved); + AM_INIT(sizes); + AM_INIT(free_segs); + AM_INIT(supercarrier); + AM_INIT(os); + AM_INIT(scs); + AM_INIT(sco); + AM_INIT(scrpm); + AM_INIT(scmgc); + am.is_initialized = 1; +}; + + +static ERTS_INLINE void +add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2) +{ + *lp = erts_bld_cons(hpp, szp, erts_bld_tuple(hpp, szp, 2, el1, el2), *lp); +} + +Eterm erts_mmap_info(int *print_to_p, + void *print_to_arg, + Eterm** hpp, Uint* szp, + struct erts_mmap_info_struct* emis) +{ + Eterm size_tags[] = { am.total, am.total_sa, am.total_sua, am.used, am.used_sa, am.used_sua }; + Eterm seg_tags[] = { am.used, am.max, am.allocated, am.reserved, am.used_sa, am.used_sua }; + Eterm group[2]; + Eterm group_tags[] = { am.sizes, am.free_segs }; + Eterm list[2]; + Eterm list_tags[2]; /* { am.supercarrier, am.os } */ + int lix; + Eterm res = THE_NON_VALUE; + + if (!hpp) { + erts_smp_mtx_lock(&mmap_state.mtx); + emis->sizes[0] = mmap_state.size.supercarrier.total; + emis->sizes[1] = mmap_state.sa.top - mmap_state.sa.bot; + emis->sizes[2] = mmap_state.sua.top - mmap_state.sua.bot; + emis->sizes[3] = mmap_state.size.supercarrier.used.total; + emis->sizes[4] = mmap_state.size.supercarrier.used.sa; + emis->sizes[5] = mmap_state.size.supercarrier.used.sua; + + emis->segs[0] = mmap_state.no.free_segs.curr; + emis->segs[1] = mmap_state.no.free_segs.max; + emis->segs[2] = mmap_state.no.free_seg_descs; + emis->segs[3] = mmap_state.desc.reserved; + emis->segs[4] = mmap_state.sa.map.nseg; + emis->segs[5] = mmap_state.sua.map.nseg; + + emis->os_used = mmap_state.size.os.used; + erts_smp_mtx_unlock(&mmap_state.mtx); + } + + if (print_to_p) { + int to = *print_to_p; + void *arg = print_to_arg; + if (mmap_state.supercarrier) { + const char* prefix = "supercarrier "; + erts_print(to, arg, "%stotal size: %bpu\n", prefix, emis->sizes[0]); + erts_print(to, arg, "%stotal sa size: %bpu\n", prefix, emis->sizes[1]); + erts_print(to, arg, "%stotal sua size: %bpu\n", prefix, emis->sizes[2]); + erts_print(to, arg, "%sused size: %bpu\n", prefix, emis->sizes[3]); + erts_print(to, arg, "%sused sa size: %bpu\n", prefix, emis->sizes[4]); + erts_print(to, arg, "%sused sua size: %bpu\n", prefix, emis->sizes[5]); + erts_print(to, arg, "%sused free segs: %bpu\n", prefix, emis->segs[0]); + erts_print(to, arg, "%smax free segs: %bpu\n", prefix, emis->segs[1]); + erts_print(to, arg, "%sallocated free segs: %bpu\n", prefix, emis->segs[2]); + erts_print(to, arg, "%sreserved free segs: %bpu\n", prefix, emis->segs[3]); + erts_print(to, arg, "%ssa free segs: %bpu\n", prefix, emis->segs[4]); + erts_print(to, arg, "%ssua free segs: %bpu\n", prefix, emis->segs[5]); + } + if (!mmap_state.no_os_mmap) { + erts_print(to, arg, "os mmap size used: %bpu\n", emis->os_used); + } + } + + + if (hpp || szp) { + if (!am.is_initialized) { + init_atoms(); + } + + lix = 0; + if (mmap_state.supercarrier) { + group[0] = erts_bld_atom_uword_2tup_list(hpp, szp, + sizeof(size_tags)/sizeof(Eterm), + size_tags, emis->sizes); + group[1] = erts_bld_atom_uword_2tup_list(hpp, szp, + sizeof(seg_tags)/sizeof(Eterm), + seg_tags, emis->segs); + list[lix] = erts_bld_2tup_list(hpp, szp, 2, group_tags, group); + list_tags[lix] = am.supercarrier; + lix++; + } + + if (!mmap_state.no_os_mmap) { + group[0] = erts_bld_atom_uword_2tup_list(hpp, szp, + 1, &am.used, &emis->os_used); + list[lix] = erts_bld_2tup_list(hpp, szp, 1, group_tags, group); + list_tags[lix] = am.os; + lix++; + } + res = erts_bld_2tup_list(hpp, szp, lix, list_tags, list); + } + return res; +} + +Eterm erts_mmap_info_options(char *prefix, + int *print_to_p, + void *print_to_arg, + Uint **hpp, + Uint *szp) +{ + const UWord scs = mmap_state.sua.top - mmap_state.sa.bot; + const Eterm sco = mmap_state.no_os_mmap ? am_true : am_false; + const Eterm scrpm = (mmap_state.reserve_physical == reserve_noop) ? am_true : am_false; + Eterm res = THE_NON_VALUE; + + if (print_to_p) { + int to = *print_to_p; + void *arg = print_to_arg; + erts_print(to, arg, "%sscs: %bpu\n", prefix, scs); + if (mmap_state.supercarrier) { + erts_print(to, arg, "%ssco: %T\n", prefix, sco); + erts_print(to, arg, "%sscrpm: %T\n", prefix, scrpm); + erts_print(to, arg, "%sscmgc: %beu\n", prefix, mmap_state.desc.reserved); + } + } + + if (hpp || szp) { + if (!am.is_initialized) { + init_atoms(); + } + + res = NIL; + if (mmap_state.supercarrier) { + add_2tup(hpp, szp, &res, am.scmgc, + erts_bld_uint(hpp,szp, mmap_state.desc.reserved)); + add_2tup(hpp, szp, &res, am.scrpm, scrpm); + add_2tup(hpp, szp, &res, am.sco, sco); + } + add_2tup(hpp, szp, &res, am.scs, erts_bld_uword(hpp, szp, scs)); + } + return res; +} + + +Eterm erts_mmap_debug_info(Process* p) { if (mmap_state.supercarrier) { ERTS_DECL_AM(sabot); @@ -2283,6 +2473,7 @@ Eterm erts_mmap_info(Process* p) } } + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * Debug functions * \* */ diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 106459f872..e6934dbb26 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -57,8 +57,18 @@ void erts_munmap(Uint32 flags, void *ptr, UWord size); void *erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep); int erts_mmap_in_supercarrier(void *ptr); void erts_mmap_init(ErtsMMapInit*); +struct erts_mmap_info_struct +{ + UWord sizes[6]; + UWord segs[6]; + UWord os_used; +}; +Eterm erts_mmap_info(int *print_to_p, void *print_to_arg, + Eterm** hpp, Uint* szp, struct erts_mmap_info_struct*); +Eterm erts_mmap_info_options(char *prefix, int *print_to_p, void *print_to_arg, + Uint **hpp, Uint *szp); struct process; -Eterm erts_mmap_info(struct process*); +Eterm erts_mmap_debug_info(struct process*); #define ERTS_SUPERALIGNED_SIZE \ (1 << ERTS_MMAP_SUPERALIGNED_BITS) diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 13f8069cf5..b165f76c96 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -1070,7 +1070,9 @@ info_options(ErtsMsegAllctr_t *ma, Uint **hpp, Uint *szp) { - Eterm res = THE_NON_VALUE; + Eterm res; + + res = erts_mmap_info_options(prefix, print_to_p, print_to_arg, hpp, szp); if (print_to_p) { int to = *print_to_p; @@ -1085,7 +1087,6 @@ info_options(ErtsMsegAllctr_t *ma, if (!atoms_initialized) init_atoms(ma); - res = NIL; add_2tup(hpp, szp, &res, am.mcs, bld_uint(hpp, szp, ma->max_cache_size)); diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index f1791200e0..5a9ffa89d1 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index d40ee7c59a..2b6760c675 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -3499,6 +3499,8 @@ mk_res_list([]) -> mk_res_list([Alloc | Rest]) -> [{Alloc, []} | mk_res_list(Rest)]. +insert_instance(I, N, Rest) when erlang:is_atom(N) -> + [{N, I} | Rest]; insert_instance(I, N, []) -> [{instance, N, I}]; insert_instance(I, N, [{instance, M, _}|_] = Rest) when N < M -> -- cgit v1.2.3 From f2a7538022e6d094bfbb003718fec688d3ba189e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 1 Oct 2013 15:33:28 +0200 Subject: erts: Fix time_SUITE:consistency to work over turn of the month --- erts/emulator/test/time_SUITE.erl | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index 4d12e3449c..a0a8a9c42c 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -241,14 +241,26 @@ compare(Utc0, Local) -> %% Two linear times can be subtracted to give their difference %% in seconds. %% -%% XXX Limitations: The length of months and leap years are not -%% taken into account; thus a comparision of dates is only -%% valid if they are in the SAME month. +%% XXX Limitations: Simplified leap year calc will fail for 2100 :-) linear_time({{Year, Mon, Day}, {Hour, Min, Sec}}) -> - 86400*(366*Year + 31*(Mon-1) + (Day-1)) + + 86400*(year_to_days(Year) + month_to_days(Year,Mon) + (Day-1)) + 3600*Hour + 60*Min + Sec. +year_to_days(Year) -> + Year * 365 + (Year-1) div 4. + +month_to_days(Year, Mon) -> + DoM = [31,days_in_february(Year),31,30,31,30,31,31,30,31,30,31], + {PastMonths,_} = lists:split(Mon-1, DoM), + lists:sum(PastMonths). + +days_in_february(Year) -> + case (Year rem 4) of + 0 -> 29; + _ -> 28 + end. + %% This functions returns either the normal timezone or the %% the DST timezone, depending on the given UTC time. %% -- cgit v1.2.3 From ee39709b4ae375277c6380033c9c9f04b0242b43 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 26 Sep 2013 22:37:43 +0200 Subject: erts: Fix misc minor bugs in supercarrier initialization --- erts/emulator/sys/common/erl_mmap.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 60b32e8115..52041fd03e 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -1394,15 +1394,17 @@ alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end) */ #if ERTS_HAVE_OS_MMAP - ptr = os_mmap(mmap_state.desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0); - if (ptr) { - mmap_state.desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE; - ERTS_MMAP_SIZE_OS_INC(ERTS_PAGEALIGNED_SIZE); - add_free_desc_area(ptr, ptr+ERTS_PAGEALIGNED_SIZE); - desc = alloc_desc(); - ERTS_MMAP_ASSERT(desc); - insert_free_seg(map, desc, start, end); - return 0; + if (!mmap_state.no_os_mmap) { + ptr = os_mmap(mmap_state.desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0); + if (ptr) { + mmap_state.desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE; + ERTS_MMAP_SIZE_OS_INC(ERTS_PAGEALIGNED_SIZE); + add_free_desc_area(ptr, ptr+ERTS_PAGEALIGNED_SIZE); + desc = alloc_desc(); + ERTS_MMAP_ASSERT(desc); + insert_free_seg(map, desc, start, end); + return 0; + } } #endif @@ -2166,6 +2168,7 @@ erts_mmap_init(ErtsMMapInit *init) mmap_state.sa.bot = NULL; mmap_state.sua.top = NULL; mmap_state.no_os_mmap = 0; + mmap_state.supercarrier = 0; } else { size_t desc_size; @@ -2185,10 +2188,10 @@ erts_mmap_init(ErtsMMapInit *init) mmap_state.sa.bot += desc_size; mmap_state.sa.bot = (char *) ERTS_SUPERALIGNED_CEILING(mmap_state.sa.bot); mmap_state.sa.top = mmap_state.sa.bot; - mmap_state.sua.top = (char *) ERTS_SUPERALIGNED_FLOOR(end); + mmap_state.sua.top = end; mmap_state.sua.bot = mmap_state.sua.top; - mmap_state.size.os.used += (UWord) (mmap_state.sa.bot - start); + mmap_state.size.supercarrier.used.total += (UWord) (mmap_state.sa.bot - start); mmap_state.desc.free_list = NULL; mmap_state.desc.reserved = 0; @@ -2201,7 +2204,7 @@ erts_mmap_init(ErtsMMapInit *init) * into the super carrier... */ mmap_state.sua.top -= ERTS_PAGEALIGNED_SIZE; - mmap_state.size.os.used += ERTS_PAGEALIGNED_SIZE; + mmap_state.size.supercarrier.used.total += ERTS_PAGEALIGNED_SIZE; #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION if (!virtual_map || os_reserve_physical(mmap_state.sua.top, ERTS_PAGEALIGNED_SIZE)) #endif @@ -2209,7 +2212,7 @@ erts_mmap_init(ErtsMMapInit *init) mmap_state.desc.reserved += (end - mmap_state.sua.top) / sizeof(ErtsFreeSegDesc); } - mmap_state.size.supercarrier.total = (UWord) (mmap_state.sua.top - mmap_state.sa.bot); + mmap_state.size.supercarrier.total = (UWord) (mmap_state.sua.top - start); /* * Area before (and after) super carrier -- cgit v1.2.3 From e9f670e542dd2ea2dc29dff66d3516a3fd0d2d21 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 1 Oct 2013 17:11:27 +0200 Subject: erts: Fix lock violation for init_atoms in erl_mmap.c by not holding the mseg lock while reading version and option info which is unnecessary anyway. --- erts/emulator/sys/common/erl_mseg.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index b165f76c96..94a381e168 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -962,8 +962,6 @@ init_atoms(ErtsMsegAllctr_t *ma) #ifdef DEBUG Eterm *atom; #endif - - ERTS_MSEG_UNLOCK(ma); erts_mtx_lock(&init_atoms_mutex); if (!atoms_initialized) { @@ -1007,7 +1005,6 @@ init_atoms(ErtsMsegAllctr_t *ma) #endif } - ERTS_MSEG_LOCK(ma); atoms_initialized = 1; erts_mtx_unlock(&init_atoms_mutex); } @@ -1293,14 +1290,8 @@ erts_mseg_info_options(int ix, ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_IX(ix); Eterm res; - ERTS_MSEG_LOCK(ma); - - ERTS_DBG_MA_CHK_THR_ACCESS(ma); - res = info_options(ma, "option ", print_to_p, print_to_arg, hpp, szp); - ERTS_MSEG_UNLOCK(ma); - return res; } @@ -1318,10 +1309,6 @@ erts_mseg_info(int ix, Eterm values[4]; Uint n = 0; - ERTS_MSEG_LOCK(ma); - - ERTS_DBG_MA_CHK_THR_ACCESS(ma); - if (hpp || szp) { if (!atoms_initialized) @@ -1334,6 +1321,10 @@ erts_mseg_info(int ix, } values[n++] = info_version(ma, print_to_p, print_to_arg, hpp, szp); values[n++] = info_options(ma, "option ", print_to_p, print_to_arg, hpp, szp); + + ERTS_MSEG_LOCK(ma); + ERTS_DBG_MA_CHK_THR_ACCESS(ma); + #if HALFWORD_HEAP values[n++] = info_memkind(ma, &ma->low_mem, print_to_p, print_to_arg, begin_max_per, hpp, szp); values[n++] = info_memkind(ma, &ma->hi_mem, print_to_p, print_to_arg, begin_max_per, hpp, szp); -- cgit v1.2.3 From ff95e85937007a7952477c7acc7619791405ab1c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 1 Oct 2013 18:12:43 +0200 Subject: erts: Add mutex to init_atoms in erts_mmap.c --- erts/emulator/beam/erl_lock_check.c | 1 + erts/emulator/sys/common/erl_mmap.c | 109 +++++++++++++++++++----------------- 2 files changed, 60 insertions(+), 50 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 87efbdbc3e..0dd83fa6ed 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -132,6 +132,7 @@ static erts_lc_lock_order_t erts_lock_order[] = { #endif /* __WIN32__ */ { "alcu_init_atoms", NULL }, { "mseg_init_atoms", NULL }, + { "mmap_init_atoms", NULL }, { "drv_tsd", NULL }, { "async_enq_mtx", NULL }, #ifdef ERTS_SMP diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 52041fd03e..a9da7430fb 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -2039,6 +2039,64 @@ int erts_mmap_in_supercarrier(void *ptr) return ERTS_MMAP_IN_SUPERCARRIER(ptr); } + +static struct { + Eterm total; + Eterm total_sa; + Eterm total_sua; + Eterm used; + Eterm used_sa; + Eterm used_sua; + Eterm max; + Eterm allocated; + Eterm reserved; + Eterm sizes; + Eterm free_segs; + Eterm supercarrier; + Eterm os; + Eterm scs; + Eterm sco; + Eterm scrpm; + Eterm scmgc; + + int is_initialized; + erts_mtx_t init_mutex; +}am; + +static void ERTS_INLINE atom_init(Eterm *atom, char *name) +{ + *atom = am_atom_put(name, strlen(name)); +} +#define AM_INIT(AM) atom_init(&am.AM, #AM) + +static void init_atoms(void) +{ + erts_mtx_lock(&am.init_mutex); + + if (!am.is_initialized) { + AM_INIT(total); + AM_INIT(total_sa); + AM_INIT(total_sua); + AM_INIT(used); + AM_INIT(used_sa); + AM_INIT(used_sua); + AM_INIT(max); + AM_INIT(allocated); + AM_INIT(reserved); + AM_INIT(sizes); + AM_INIT(free_segs); + AM_INIT(supercarrier); + AM_INIT(os); + AM_INIT(scs); + AM_INIT(sco); + AM_INIT(scrpm); + AM_INIT(scmgc); + am.is_initialized = 1; + } + erts_mtx_unlock(&am.init_mutex); +}; + + #ifdef HARD_DEBUG_MSEG static void hard_dbg_mseg_init(void); #endif @@ -2085,6 +2143,7 @@ erts_mmap_init(ErtsMMapInit *init) #endif erts_smp_mtx_init(&mmap_state.mtx, "erts_mmap"); + erts_mtx_init(&am.init_mutex, "mmap_init_atoms"); #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION if (init->virtual_range.start) { @@ -2246,56 +2305,6 @@ erts_mmap_init(ErtsMMapInit *init) #endif } -static struct { - Eterm total; - Eterm total_sa; - Eterm total_sua; - Eterm used; - Eterm used_sa; - Eterm used_sua; - Eterm max; - Eterm allocated; - Eterm reserved; - Eterm sizes; - Eterm free_segs; - Eterm supercarrier; - Eterm os; - Eterm scs; - Eterm sco; - Eterm scrpm; - Eterm scmgc; - - int is_initialized; -}am; - -static void ERTS_INLINE atom_init(Eterm *atom, char *name) -{ - *atom = am_atom_put(name, strlen(name)); -} -#define AM_INIT(AM) atom_init(&am.AM, #AM) - -static void init_atoms(void) -{ - AM_INIT(total); - AM_INIT(total_sa); - AM_INIT(total_sua); - AM_INIT(used); - AM_INIT(used_sa); - AM_INIT(used_sua); - AM_INIT(max); - AM_INIT(allocated); - AM_INIT(reserved); - AM_INIT(sizes); - AM_INIT(free_segs); - AM_INIT(supercarrier); - AM_INIT(os); - AM_INIT(scs); - AM_INIT(sco); - AM_INIT(scrpm); - AM_INIT(scmgc); - am.is_initialized = 1; -}; - static ERTS_INLINE void add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2) -- cgit v1.2.3 From bc966c32bfb52467f4fd527995c110c0a980527c Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Tue, 1 Oct 2013 14:16:21 +0200 Subject: sasl: Add no_dot_erlang start script Sometimes it is wanted to start erlang without loading the user dependent .erlang file, for example in scripts and configure tests. --- erts/Makefile.in | 3 ++- erts/etc/unix/Install.src | 2 +- erts/etc/win32/Install.c | 2 +- erts/start_scripts/Makefile | 33 +++++++++++++++++++++++++++++--- erts/start_scripts/no_dot_erlang.rel.src | 21 ++++++++++++++++++++ 5 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 erts/start_scripts/no_dot_erlang.rel.src (limited to 'erts') diff --git a/erts/Makefile.in b/erts/Makefile.in index 92fdc7a862..e3db37d3fd 100644 --- a/erts/Makefile.in +++ b/erts/Makefile.in @@ -107,7 +107,8 @@ local_setup: fi @cd start_scripts && $(MAKE) $(ERL_TOP)/bin/start.script \ $(ERL_TOP)/bin/start_sasl.script \ - $(ERL_TOP)/bin/start_clean.script + $(ERL_TOP)/bin/start_clean.script \ + $(ERL_TOP)/bin/no_dot_erlang.script # Run the configure script .PHONY: configure diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src index 0f33258a28..8eb1db75bd 100644 --- a/erts/etc/unix/Install.src +++ b/erts/etc/unix/Install.src @@ -137,9 +137,9 @@ case $start_option in esac cp -p ../releases/%I_SYSTEM_VSN%/start_*.boot . +cp -p ../releases/%I_SYSTEM_VSN%/no_dot_erlang.boot . cp -p $Name.boot start.boot cp -p ../releases/%I_SYSTEM_VSN%/$Name.script start.script - # # Fixing the man pages # diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c index dd02a9c111..c46bb89f7c 100644 --- a/erts/etc/win32/Install.c +++ b/erts/etc/win32/Install.c @@ -47,7 +47,7 @@ int main(int argc, char **argv) char *binaries[] = { "erl.exe", "werl.exe", "erlc.exe", "dialyzer.exe", "typer.exe", "escript.exe", "ct_run.exe", NULL }; - char *scripts[] = { "start_clean.boot", "start_sasl.boot", NULL }; + char *scripts[] = { "start_clean.boot", "start_sasl.boot", "no_dot_erlang.boot", NULL }; char fromname[MAX_PATH]; char toname[MAX_PATH]; diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile index 3bf233cbb6..32e65a227f 100644 --- a/erts/start_scripts/Makefile +++ b/erts/start_scripts/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2012. All Rights Reserved. +# Copyright Ericsson AB 1997-2013. All Rights Reserved. # # The 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,12 +34,16 @@ INSTALL_SCRIPTS = \ $(SS_ROOT)/start_clean.script \ $(SS_ROOT)/start_clean.boot \ $(SS_ROOT)/start_sasl.boot \ - $(SS_ROOT)/start_sasl.script + $(SS_ROOT)/start_sasl.script \ + $(SS_ROOT)/no_dot_erlang.boot \ + $(SS_ROOT)/no_dot_erlang.script + REL_SCRIPTS = \ $(SS_ROOT)/start_clean.rel \ $(SS_ROOT)/start_sasl.rel \ - $(SS_ROOT)/start_all_example.rel + $(SS_ROOT)/start_all_example.rel \ + $(SS_ROOT)/no_dot_erlang.rel \ ifneq ($(findstring win32,$(TARGET)),win32) RELEASES_SRC = RELEASES.src @@ -82,6 +86,13 @@ $(SS_ROOT)/start_sasl.boot: $(SS_ROOT)/start_sasl.rel $(V_at)( cd $(SS_TMP) && \ $(ERLC) $(SASL_FLAGS) $(SCRIPT_PATH) -o $(SS_ROOT) $< ) +$(SS_ROOT)/no_dot_erlang.script \ +$(SS_ROOT)/no_dot_erlang.boot: $(SS_ROOT)/no_dot_erlang.rel + $(gen_verbose)$(INSTALL_DIR) $(SS_TMP) + $(V_at)( cd $(SS_TMP) && \ + $(ERLC) $(SASL_FLAGS) $(SCRIPT_PATH) +no_warn_sasl +no_dot_erlang -o $(SS_ROOT) $< ) + + $(SS_ROOT)/start_clean.rel: $(SS_ROOT)/start_clean.rel.src \ ../vsn.mk \ $(LIBPATH)/kernel/vsn.mk \ @@ -104,6 +115,16 @@ $(SS_ROOT)/start_sasl.rel: $(SS_ROOT)/start_sasl.rel.src \ -e 's;%SASL_VSN%;$(SASL_VSN);' \ $(SS_ROOT)/start_sasl.rel.src > $(SS_ROOT)/start_sasl.rel +$(SS_ROOT)/no_dot_erlang.rel: $(SS_ROOT)/no_dot_erlang.rel.src \ + ../vsn.mk \ + $(LIBPATH)/kernel/vsn.mk \ + $(LIBPATH)/stdlib/vsn.mk + $(gen_verbose)sed -e 's;%SYS_VSN%;$(SYSTEM_VSN);' \ + -e 's;%ERTS_VSN%;$(VSN);' \ + -e 's;%KERNEL_VSN%;$(KERNEL_VSN);' \ + -e 's;%STDLIB_VSN%;$(STDLIB_VSN);' \ + $(SS_ROOT)/no_dot_erlang.rel.src > $(SS_ROOT)/no_dot_erlang.rel + $(SS_ROOT)/start_all_example.rel: $(SS_ROOT)/start_all_example.rel.src \ ../vsn.mk \ $(LIBPATH)/kernel/vsn.mk \ @@ -140,7 +161,13 @@ $(ERL_TOP)/bin/start_clean.script: $(V_at)( cd $(SS_TMP) && \ $(ERLC) $(SCRIPT_PATH) +no_warn_sasl +otp_build -o $@ $(SS_ROOT)/start_clean.rel ) +$(ERL_TOP)/bin/no_dot_erlang.script: + $(gen_verbose)$(INSTALL_DIR) $(SS_TMP) + $(V_at)( cd $(SS_TMP) && \ + $(ERLC) $(SCRIPT_PATH) +no_warn_sasl +otp_build +no_dot_erlang -o $@ $(SS_ROOT)/no_dot_erlang.rel ) + ## Special target used from system/build/Makefile for source code release bootstrap. +## Add no_dot_erlang after next release bootstrap_scripts: $(SS_ROOT)/start_clean.rel $(V_at)$(INSTALL_DIR) $(TESTROOT)/bin $(V_at)$(INSTALL_DIR) $(SS_TMP) diff --git a/erts/start_scripts/no_dot_erlang.rel.src b/erts/start_scripts/no_dot_erlang.rel.src new file mode 100644 index 0000000000..03b64ebf1a --- /dev/null +++ b/erts/start_scripts/no_dot_erlang.rel.src @@ -0,0 +1,21 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The 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% +%% +{release, {"OTP APN 181 01","%SYS_VSN%"}, {erts, "%ERTS_VSN%"}, + [{kernel,"%KERNEL_VSN%"}, + {stdlib,"%STDLIB_VSN%"}]}. -- cgit v1.2.3 From 5db2c05af6efaf636b2e41e3a1f305c592a71f34 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 3 Oct 2013 15:46:59 +0200 Subject: erts: Add test case for erts_mmap --- erts/emulator/test/alloc_SUITE.erl | 69 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 801ed0f85a..f6ff6bb813 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -29,6 +29,7 @@ bucket_mask/1, rbtree/1, mseg_clear_cache/1, + erts_mmap/1, cpool/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -41,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [basic, coalesce, threads, realloc_copy, bucket_index, - bucket_mask, rbtree, mseg_clear_cache, cpool]. + bucket_mask, rbtree, mseg_clear_cache, erts_mmap, cpool]. groups() -> []. @@ -110,6 +111,59 @@ cpool(suite) -> []; cpool(doc) -> []; cpool(Cfg) -> ?line drv_case(Cfg). +erts_mmap(Config) when is_list(Config) -> + case {?t:os_type(), is_halfword_vm()} of + {{unix, _}, false} -> + [erts_mmap_do(Config, SCO, SCRPM, SCMGC) + || SCO <-[true,false], SCMGC <-[1234,0], SCRPM <- [true,false]]; + + {_,true} -> + {skipped, "No supercarrier support on halfword vm"}; + {SkipOs,_} -> + ?line {skipped, + lists:flatten(["Not run on " + | io_lib:format("~p",[SkipOs])])} + end. + + +erts_mmap_do(Config, SCO, SCRPM, SCMGC) -> + SCS = 100, % Mb + O1 = "+MMscs" ++ integer_to_list(SCS) + ++ " +MMsco" ++ atom_to_list(SCO) + ++ " +MMscrpm" ++ atom_to_list(SCRPM), + Opts = case SCMGC of + 0 -> O1; + _ -> O1 ++ " +MMscmgc"++integer_to_list(SCMGC) + end, + {ok, Node} = start_node(Config, Opts), + Self = self(), + Ref = make_ref(), + F = fun () -> + SI = erlang:system_info({allocator,mseg_alloc}), + {erts_mmap,EM} = lists:keyfind(erts_mmap, 1, SI), + {supercarrier,SC} = lists:keyfind(supercarrier, 1, EM), + {sizes,Sizes} = lists:keyfind(sizes, 1, SC), + {free_segs,Segs} = lists:keyfind(free_segs,1,SC), + {total,Total} = lists:keyfind(total,1,Sizes), + Total = SCS*1024*1024, + + {reserved,Reserved} = lists:keyfind(reserved,1,Segs), + true = (Reserved >= SCMGC), + + case {SCO,lists:keyfind(os,1,EM)} of + {true, false} -> ok; + {false, {os,_}} -> ok + end, + + Self ! {Ref, ok} + end, + + spawn_link(Node, F), + Result = receive {Ref, Rslt} -> Rslt end, + stop_node(Node), + Result. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% Internal functions %% @@ -179,7 +233,9 @@ receive_drv_result(Port, CaseName) -> ?line {comment, Comment} end. -start_node(Config) when is_list(Config) -> +start_node(Config) -> + start_node(Config, []). +start_node(Config, Opts) when is_list(Config), is_list(Opts) -> ?line Pa = filename:dirname(code:which(?MODULE)), ?line {A, B, C} = now(), ?line Name = list_to_atom(atom_to_list(?MODULE) @@ -191,7 +247,14 @@ start_node(Config) when is_list(Config) -> ++ integer_to_list(B) ++ "-" ++ integer_to_list(C)), - ?line ?t:start_node(Name, slave, [{args, "-pa "++Pa}]). + ?line ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). stop_node(Node) -> ?t:stop_node(Node). + +is_halfword_vm() -> + case {erlang:system_info({wordsize, internal}), + erlang:system_info({wordsize, external})} of + {4, 8} -> true; + {WS, WS} -> false + end. -- cgit v1.2.3 From 4364de3cc6c6212b291a5c240f25a00e90b2e852 Mon Sep 17 00:00:00 2001 From: Lars Hesel Christensen Date: Tue, 24 Sep 2013 11:09:42 +0200 Subject: Add bsr test data showing bug when shifting large numbers Add test data demonstrating that bsr is broken when shifting a large number a huge number of bits to the right. --- erts/emulator/test/big_SUITE_data/eq_big.dat | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/test/big_SUITE_data/eq_big.dat b/erts/emulator/test/big_SUITE_data/eq_big.dat index 5511d1bf10..4ccb33d182 100644 --- a/erts/emulator/test/big_SUITE_data/eq_big.dat +++ b/erts/emulator/test/big_SUITE_data/eq_big.dat @@ -13001,4 +13001,5 @@ 0 = 7153697524993 bsr 475833444444444444444444444444444444444444444444. -1 = -83987348 bsr 475833444444444444444444444444444444444444444444. +0 = 1183140560213014108063589658350 bsr 146783911423364576743092537299333564210980159306769991919205685720763064069663027716481187399048043939495935. -- cgit v1.2.3 From 54aecc2a73a1398d141c7f8e9a96c24a5a5731cf Mon Sep 17 00:00:00 2001 From: Lars Hesel Christensen Date: Tue, 24 Sep 2013 11:22:48 +0200 Subject: Fix bsr bug Fix bsr bug occurring when shifting a huge number a huge number of bits to the right. The bug can occur if Sint is 64 bits and int is 32 bits, causing a truncation in the big.c:I_lshift function. --- erts/emulator/beam/big.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 6b43c53985..2b27b111d8 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1325,9 +1325,9 @@ static dsize_t I_lshift(ErtsDigit* x, dsize_t xl, Sint y, return 1; } else { - SWord ay = (y < 0) ? -y : y; - int bw = ay / D_EXP; - int sw = ay % D_EXP; + Uint ay = (y < 0) ? -y : y; + Uint bw = ay / D_EXP; + Uint sw = ay % D_EXP; dsize_t rl; ErtsDigit a1=0; ErtsDigit a0=0; @@ -1368,7 +1368,7 @@ static dsize_t I_lshift(ErtsDigit* x, dsize_t xl, Sint y, } if (sign) { - int zl = bw; + Uint zl = bw; ErtsDigit* z = x; while(zl--) { -- cgit v1.2.3 From 41e340fb37d0e660fca70486dc715383df9ba026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20H=C3=A4ssler?= Date: Thu, 2 May 2013 20:52:30 +0200 Subject: Fix erts erlang.xml doc typo badargif -> badarg if --- erts/doc/src/erlang.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index d3b21de8cf..bc38055b62 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2467,7 +2467,7 @@ os_prompt% fails, a nodedown message is delivered.

Nodes connected through hidden connections can be monitored as any other node.

-

Failure: badargif the local node is not alive.

+

Failure: badarg if the local node is not alive.

-- cgit v1.2.3 From cf2159e5e43bb989f5a3f5f1b038bfa5ce33057d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 17 Oct 2013 15:10:48 +0200 Subject: erts: Fix memory leak for distributed monitors --- erts/emulator/beam/dist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 44f4eb9d43..95d2c5b362 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -1509,12 +1509,12 @@ int erts_net_message(Port *prt, break; } rp = erts_pid2proc(NULL, 0, mon->pid, rp_locks); + + erts_destroy_monitor(mon); if (rp == NULL) { break; } - erts_destroy_monitor(mon); - mon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); if (mon == NULL) { -- cgit v1.2.3 From e25f74afd0705f686d0fc949e4362c73d6da15fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 18 Oct 2013 16:47:19 +0200 Subject: erts: Fix segfaulting crashdump writing Crashdumps initiated by out-of-memory on spawn could cause the beam to segfault during crashdump writing due to invalid pointers. The pointers are invalid since the process creation never finished. This commit remedies this problem by removing the process from crashdump printout. --- erts/emulator/beam/break.c | 5 ++++- erts/emulator/beam/erl_process.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index ad9a89b642..99604fa3bc 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -76,7 +76,10 @@ process_info(int to, void *to_arg) for (i = 0; i < max; i++) { Process *p = erts_pix2proc(i); if (p && p->i != ENULL) { - if (!ERTS_PROC_IS_EXITING(p)) + /* Do not include processes with no heap, + * they are most likely just created and has invalid data + */ + if (!ERTS_PROC_IS_EXITING(p) && p->heap != NULL) print_process_info(to, to_arg, p); } } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 434d5ca147..5cfaf1b5ee 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -7471,6 +7471,7 @@ alloc_process(ErtsRunQueue *rq, erts_aint32_t state) p->approx_started = erts_get_approx_time(); p->rcount = 0; + p->heap = NULL; ASSERT(p == (Process *) (erts_ptab_pix2intptr_nob( @@ -7583,7 +7584,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). hipe_init_process_smp(&p->hipe_smp); #endif #endif - p->heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*sz); p->old_hend = p->old_htop = p->old_heap = NULL; p->high_water = p->heap; -- cgit v1.2.3 From a51730d1f0ef5a9825998a9e4c2cd0556de9a18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 30 Oct 2013 13:20:42 +0100 Subject: Fix truncated pointers in erl_crash.dump When dumping the heaps and stacks in the erl_crash.dump file, only the lower 32 bits of pointers would be printed. Ensure that all bits are printed. While at it, make sure that printing sub binaries with a debug compiled run-time system will work. --- erts/emulator/beam/break.c | 2 +- erts/emulator/beam/erl_process_dump.c | 46 ++++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 99604fa3bc..b7e1092907 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -757,7 +757,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) return; /* Can't create the crash dump, skip it */ time(&now); - erts_fdprintf(fd, "=erl_crash_dump:0.2\n%s", ctime(&now)); + erts_fdprintf(fd, "=erl_crash_dump:0.3\n%s", ctime(&now)); if (file != NULL) erts_fdprintf(fd, "The error occurred in file %s, line %d\n", file, line); diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index 6cd0d23b97..2f3cf23b00 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -34,8 +34,8 @@ #define ERTS_WANT_EXTERNAL_TAGS #include "external.h" -#define WORD_FMT "%X" -#define ADDR_FMT "%X" +#define PTR_FMT "%bpX" +#define ETERM_FMT "%beX" #define OUR_NIL _make_header(0,_TAG_HEADER_FLOAT) @@ -210,9 +210,9 @@ static void dump_element(int to, void *to_arg, Eterm x) { if (is_list(x)) { - erts_print(to, to_arg, "H" WORD_FMT, list_val(x)); + erts_print(to, to_arg, "H" PTR_FMT, list_val(x)); } else if (is_boxed(x)) { - erts_print(to, to_arg, "H" WORD_FMT, boxed_val(x)); + erts_print(to, to_arg, "H" PTR_FMT, boxed_val(x)); } else if (is_immed(x)) { if (is_atom(x)) { unsigned char* s = atom_tab(atom_val(x))->name; @@ -311,7 +311,7 @@ heap_dump(int to, void *to_arg, Eterm x) } else if (is_list(x)) { ptr = list_val(x); if (ptr[0] != OUR_NIL) { - erts_print(to, to_arg, ADDR_FMT ":l", ptr); + erts_print(to, to_arg, PTR_FMT ":l", ptr); dump_element(to, to_arg, ptr[0]); erts_putc(to, to_arg, '|'); dump_element(to, to_arg, ptr[1]); @@ -330,12 +330,12 @@ heap_dump(int to, void *to_arg, Eterm x) ptr = boxed_val(x); hdr = *ptr; if (hdr != OUR_NIL) { /* If not visited */ - erts_print(to, to_arg, ADDR_FMT ":", ptr); + erts_print(to, to_arg, PTR_FMT ":", ptr); if (is_arity_value(hdr)) { Uint i; Uint arity = arityval(hdr); - erts_print(to, to_arg, "t" WORD_FMT ":", arity); + erts_print(to, to_arg, "t" ETERM_FMT ":", arity); for (i = 1; i <= arity; i++) { dump_element(to, to_arg, ptr[i]); if (is_immed(ptr[i])) { @@ -388,21 +388,43 @@ heap_dump(int to, void *to_arg, Eterm x) val->flags = (UWord) all_binaries; all_binaries = val; } - erts_print(to, to_arg, "Yc%X:%X:%X", val, + erts_print(to, to_arg, + "Yc" PTR_FMT ":" PTR_FMT ":" PTR_FMT, + val, pb->bytes - (byte *)val->orig_bytes, size); } else if (tag == SUB_BINARY_SUBTAG) { ErlSubBin* Sb = (ErlSubBin *) binary_val(x); - Eterm* real_bin = binary_val(Sb->orig); + Eterm* real_bin; void* val; + /* + * Must use boxed_val() here, because the original + * binary may have been visited and have had its + * header word changed to OUR_NIL (in which case + * binary_val() will cause an assertion failure in + * the DEBUG emulator). + */ + + real_bin = boxed_val(Sb->orig); + if (thing_subtag(*real_bin) == REFC_BINARY_SUBTAG) { + /* + * Unvisited REFC_BINARY: Point directly to + * the binary. + */ ProcBin* pb = (ProcBin *) real_bin; val = pb->val; - } else { /* Heap binary */ + } else { + /* + * Heap binary or visited REFC binary: Point + * to heap binary or ProcBin on the heap. + */ val = real_bin; } - erts_print(to, to_arg, "Ys%X:%X:%X", val, Sb->offs, size); + erts_print(to, to_arg, + "Ys" PTR_FMT ":" PTR_FMT ":" PTR_FMT, + val, Sb->offs, size); } erts_putc(to, to_arg, '\n'); *ptr = OUR_NIL; @@ -438,7 +460,7 @@ dump_binaries(int to, void *to_arg, Binary* current) long size = current->orig_size; byte* bytes = (byte*) current->orig_bytes; - erts_print(to, to_arg, "=binary:%X\n", current); + erts_print(to, to_arg, "=binary:" PTR_FMT "\n", current); erts_print(to, to_arg, "%X:", size); for (i = 0; i < size; i++) { erts_print(to, to_arg, "%02X", bytes[i]); -- cgit v1.2.3 From 70ebf76f1cef4a6de6be3ea96b36fb81fe245921 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 28 Oct 2013 18:26:40 +0100 Subject: erts: Add max alignment posix_memalign configure check On some OSs posix_memalign exists, but it does not allow for alignment greater than the current page size. So we have to do a runtime check for alignment size and also add cross compile options. --- erts/configure.in | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index f17f4cb5c8..8288a1aab1 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1974,11 +1974,39 @@ AC_CHECK_FUNCS([getipnodebyname getipnodebyaddr gethostbyname2]) AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlopen \ pread pwrite memmove strerror strerror_r strncasecmp \ - gethrtime localtime_r gmtime_r inet_pton posix_memalign \ + gethrtime localtime_r gmtime_r inet_pton \ mmap mremap memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \ flockfile fstat strlcpy strlcat setsid posix2time time2posix \ setlocale nl_langinfo poll]) + +case X$erl_xcomp_posix_memalign in + Xno) ;; + Xyes) AC_DEFINE(HAVE_POSIX_MEMALIGN,[1], + [Define to 1 if you have the `posix_memalign' function.]) ;; + *) + AC_CHECK_FUNC( + [posix_memalign], + [if test "$cross_compiling" != yes; then +AC_TRY_RUN([ +#include +int main(void) { + void *ptr = NULL; + int error; + size_t alignment = 0x40000, size = 0x20028; + if ((error = posix_memalign(&ptr, alignment, size)) != 0 || ptr == NULL) + return error; + return 0; +} +],AC_DEFINE(HAVE_POSIX_MEMALIGN,[1], + [Define to 1 if you have the `posix_memalign' function.]) +) + else + AC_DEFINE(HAVE_POSIX_MEMALIGN,[1], + [Define to 1 if you have the `posix_memalign' function.]) + fi]);; +esac + dnl writev on OS X snow leopard is broken for files > 4GB case $host_os in darwin10.8.0) -- cgit v1.2.3 From fb3aaa41a046e317609c39c9b8281263a250ef2f Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 1 Nov 2013 14:32:00 +0100 Subject: Ensure carrier pool only accessed by schedulers Disable carrier pool for the thread safe allocator instance 0. This since non-managed threads allocates and deallocates memory in this instance, and only managed threads are allowed to access the carrier pool. --- erts/emulator/beam/erl_alloc_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 1fdee4db2c..a6f256a15e 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -5512,7 +5512,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) erts_atomic_init_nob(&allctr->cpool.stat.carriers_size, 0); erts_atomic_init_nob(&allctr->cpool.stat.no_carriers, 0); allctr->cpool.check_limit_count = ERTS_ALC_CPOOL_CHECK_LIMIT_COUNT; - allctr->cpool.util_limit = init->acul; + allctr->cpool.util_limit = init->ts ? 0 : init->acul; #endif allctr->sbc_threshold = init->sbct; -- cgit v1.2.3 From 98232ea62b2a971c802ef38488eaab2a396fefef Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 1 Nov 2013 17:45:13 +0100 Subject: erts: Move compiler flags generation Move the call to utils/make_compiler_flags to after _create_dirs to make sure that the dirs are created before we try to generate files in there. This seems to work with parallel make as well, but I cannot find any documentation that says that it should or should not work. --- erts/emulator/Makefile.in | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index f442540f49..68bb9c0bfb 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -387,13 +387,6 @@ else UNIX_ONLY_BUILDS = endif -ifeq ($(TARGET), win32) -TMPVAR := $(shell LANG=C $(PERL) utils/make_compiler_flags -o $(TTF_DIR)/erl_compile_flags.h -v CONFIG_H "N/A" -v CFLAGS "$(CFLAGS)" -v LDFLAGS "$(LDFLAGS)") -else -# We force this to be run every time this makefile is executed -TMPVAR := $(shell LANG=C $(PERL) utils/make_compiler_flags -o $(TTF_DIR)/erl_compile_flags.h -f CONFIG_H "$(ERL_TOP)/erts/$(TARGET)/config.h" -v CFLAGS "$(CFLAGS)" -v LDFLAGS "$(LDFLAGS)") -endif - .PHONY: all ifdef VOID_EMULATOR all: @@ -472,6 +465,15 @@ release_docs_spec: _create_dirs := $(shell mkdir -p $(CREATE_DIRS)) + +# has to be run after _create_dirs +ifeq ($(TARGET), win32) +TMPVAR := $(shell LANG=C $(PERL) utils/make_compiler_flags -o $(TTF_DIR)/erl_compile_flags.h -v CONFIG_H "N/A" -v CFLAGS "$(CFLAGS)" -v LDFLAGS "$(LDFLAGS)") +else +# We force this to be run every time this makefile is executed +TMPVAR := $(shell LANG=C $(PERL) utils/make_compiler_flags -o $(TTF_DIR)/erl_compile_flags.h -f CONFIG_H "$(ERL_TOP)/erts/$(TARGET)/config.h" -v CFLAGS "$(CFLAGS)" -v LDFLAGS "$(LDFLAGS)") +endif + GENERATE = HIPE_ASM = -- cgit v1.2.3 From 4685ec726844b3b92d90c13bae0f4c96e86e3665 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 1 Nov 2013 17:45:37 +0100 Subject: erts: Fix resolve of generated files for depend --- erts/emulator/Makefile.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 68bb9c0bfb..574909ac7c 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -997,7 +997,9 @@ BEAM_SRC=$(wildcard beam/*.c) DRV_COMMON_SRC=$(wildcard drivers/common/*.c) DRV_OSTYPE_SRC=$(wildcard drivers/$(ERLANG_OSTYPE)/*.c) ALL_SYS_SRC=$(wildcard sys/$(ERLANG_OSTYPE)/*.c) $(wildcard sys/common/*.c) -TARGET_SRC=$(wildcard $(TARGET)/*.c) $(wildcard $(TTF_DIR)/*.c) +# We use $(shell ls) here instead of wildcard as $(wildcard ) resolved at +# loadtime of the makefile and at that time these files are not generated yet. +TARGET_SRC=$(shell ls $(TARGET)/*.c) $(shell ls $(TTF_DIR)/*.c) # I do not want the -MG flag on windows, it does not work properly for a # windows build. -- cgit v1.2.3 From e94f730bdda9137ce5f436256ac7fdbdcd0be14a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 17 Oct 2013 15:25:52 +0200 Subject: erts: Prevent valgrind from repeating same memory leaks reports by using the macro VALGRIND_DO_ADDED_LEAK_CHECK if it exists for system_info({error_checker,memory}) --- erts/emulator/beam/erl_bif_info.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 673dfc658c..6479320a83 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1771,7 +1771,11 @@ info_1_tuple(Process* BIF_P, /* Pointer to current process. */ #if defined(PURIFY) BIF_RET(erts_make_integer(purify_new_leaks(), BIF_P)); #elif defined(VALGRIND) +# ifdef VALGRIND_DO_ADDED_LEAK_CHECK + VALGRIND_DO_ADDED_LEAK_CHECK; +# else VALGRIND_DO_LEAK_CHECK; +# endif BIF_RET(make_small(0)); #endif } else if (*tp == am_fd) { -- cgit v1.2.3 From 3d4078722e5baa1bc64c1e6a808f98c802faa6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 5 Nov 2013 06:43:47 +0100 Subject: erts: Clarify documentation for erlang:statistics(run_queue) --- erts/doc/src/erlang.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index bc38055b62..cbb25c2cf2 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4727,8 +4727,8 @@ true Information about the run-queue -

Returns the length of the run queue, that is, the number - of processes that are ready to run.

+

Returns the total length of the run queues, that is, the number + of processes that are ready to run on all available run queues.

-- cgit v1.2.3 From f17532112c6b723a7b025c7d74565e7ac2588cbb Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 4 Nov 2013 17:18:52 +0100 Subject: Add support for locking mappings to physical memory Using "+Mlpm all" switch all mappings made by the emulator will be locked into physical memory. --- erts/configure.in | 2 +- erts/doc/src/erts_alloc.xml | 10 ++++++++++ erts/emulator/beam/erl_alloc.c | 40 ++++++++++++++++++++++++++++++++++++++-- erts/etc/common/erlexec.c | 1 + 4 files changed, 50 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 8288a1aab1..b47a6ff094 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1977,7 +1977,7 @@ AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlop gethrtime localtime_r gmtime_r inet_pton \ mmap mremap memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \ flockfile fstat strlcpy strlcat setsid posix2time time2posix \ - setlocale nl_langinfo poll]) + setlocale nl_langinfo poll mlockall]) case X$erl_xcomp_posix_memalign in diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 9dab5b3876..90bc35cdc9 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -604,6 +604,16 @@
+ +Mlpm all|no + Lock physical memory. The default value is no, i.e., + no physical memory will be locked. If set to all, all + memory mappings made by the runtime system, will be locked into + physical memory. If set to all, the runtime system will fail + to start if this feature is not supported, the user has not got enough + privileges, or the user is not allowed to lock enough physical memory. + The runtime system will also fail with an out of memory condition + if the user limit on the amount of locked memory is reached. +

Only some default values have been presented here. diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index d8da616d05..8e20d58161 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -100,6 +100,8 @@ static Uint install_debug_functions(void); #endif #endif +static int lock_all_physical_memory = 0; + ErtsAllocatorFunctions_t erts_allctrs[ERTS_ALC_A_MAX+1]; ErtsAllocatorInfo_t erts_allctrs_info[ERTS_ALC_A_MAX+1]; ErtsAllocatorThrSpec_t erts_allctr_thr_spec[ERTS_ALC_A_MAX+1]; @@ -618,6 +620,8 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) hdbg_init(); #endif + lock_all_physical_memory = 0; + ncpu = eaiop->ncpu; if (ncpu < 1) ncpu = 1; @@ -641,6 +645,20 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) if (argc && argv) handle_args(argc, argv, &init); + if (lock_all_physical_memory) { +#ifdef HAVE_MLOCKALL + errno = 0; + if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0) { + int err = errno; + char *errstr = err ? strerror(err) : "unknown"; + erl_exit(-1, "Failed to lock physical memory: %s (%d)\n", + errstr, err); + } +#else + erl_exit(-1, "Failed to lock physical memory: Not supported\n"); +#endif + } + #ifndef ERTS_SMP init.sl_alloc.thr_spec = 0; init.std_alloc.thr_spec = 0; @@ -1630,6 +1648,19 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) bad_param(param, param+2); } break; + case 'l': + if (has_prefix("pm", param+2)) { + arg = get_value(argv[i]+5, argv, &i); + if (strcmp("all", arg) == 0) + lock_all_physical_memory = 1; + else if (strcmp("no", arg) == 0) + lock_all_physical_memory = 0; + else + bad_value(param, param+4, arg); + break; + } + bad_param(param, param+2); + break; case 'u': if (has_prefix("ycs", argv[i]+3)) { init->alloc_util.ycs @@ -2749,8 +2780,8 @@ erts_allocator_options(void *proc) #endif Uint sz, *szp, *hp, **hpp; Eterm res, features, settings; - Eterm atoms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+6]; - Uint terms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+6]; + Eterm atoms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+7]; + Uint terms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+7]; int a, length; SysAllocStat sas; Uint *endp = NULL; @@ -2848,6 +2879,11 @@ erts_allocator_options(void *proc) terms[length++] = erts_bld_2tup_list(hpp, szp, 3, o, v); } + atoms[length] = am_atom_put("lock_physical_memory", 20); + terms[length++] = (lock_all_physical_memory + ? am_atom_put("all", 3) + : am_atom_put("no", 2)); + settings = erts_bld_2tup_list(hpp, szp, length, atoms, terms); length = 0; diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index c9908caf20..ffbefce545 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -106,6 +106,7 @@ static char *plusM_other_switches[] = { "im", "is", "it", + "lpm", "Mamcbf", "Mrmcbf", "Mmcs", -- cgit v1.2.3 From bb59a8fcf1f6cf4162a2a97c13087843d1b9dac4 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 5 Nov 2013 15:37:49 +0100 Subject: Add switch for disabling sys_alloc carriers The switch "+Musac " controls if sys_alloc carriers are allowed. --- erts/doc/src/erts_alloc.xml | 5 +++++ erts/emulator/beam/erl_alloc.c | 4 ++++ erts/emulator/beam/erl_alloc_util.c | 25 ++++++++++++++++++------- erts/emulator/beam/erl_alloc_util.h | 7 +++++-- erts/etc/common/erlexec.c | 1 + 5 files changed, 33 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 90bc35cdc9..70029dbeab 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -549,6 +549,11 @@ placed in separate memory segments. When this limit has been reached, new carriers will be placed in memory retrieved from sys_alloc. + ]]> + + Allow sys_alloc carriers. By default true. If + set to false, sys_alloc carriers will never be + created by allocators using the alloc_util framework.

Instrumentation flags:

diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 8e20d58161..fb0f47d1b7 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1670,6 +1670,10 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) init->alloc_util.mmc = get_amount_value(argv[i]+6, argv, &i); } + else if (has_prefix("sac", argv[i]+3)) { + init->alloc_util.sac + = get_bool_value(argv[i]+6, argv, &i); + } else { int a; int start = i; diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 1fdee4db2c..b0fc653f32 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -98,6 +98,7 @@ static Uint sys_alloc_carrier_size; #if HAVE_ERTS_MSEG static Uint max_mseg_carriers; #endif +static int allow_sys_alloc_carriers; #define ONE_GIGA (1000000000) @@ -3256,13 +3257,15 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) int is_mseg = 0; #endif -#if HALFWORD_HEAP - flags |= CFLG_FORCE_MSEG; -#elif ERTS_SUPER_ALIGNED_MSEG_ONLY - if (flags & CFLG_MBC) { + if (HALFWORD_HEAP + || (ERTS_SUPER_ALIGNED_MSEG_ONLY && (flags & CFLG_MBC)) + || !allow_sys_alloc_carriers) { flags |= CFLG_FORCE_MSEG; - } + flags &= ~CFLG_FORCE_SYS_ALLOC; +#if !HAVE_ERTS_MSEG + return NULL; #endif + } ASSERT((flags & CFLG_SBC && !(flags & CFLG_MBC)) || (flags & CFLG_MBC && !(flags & CFLG_SBC))); @@ -3697,6 +3700,7 @@ static struct { Eterm mmc; #endif Eterm ycs; + Eterm sac; Eterm fix_types; @@ -3789,6 +3793,7 @@ init_atoms(Allctr_t *allctr) AM_INIT(mmc); #endif AM_INIT(ycs); + AM_INIT(sac); AM_INIT(fix_types); @@ -4509,16 +4514,21 @@ erts_alcu_au_info_options(int *print_to_p, void *print_to_arg, #if HAVE_ERTS_MSEG "option mmc: %beu\n" #endif - "option ycs: %beu\n", + "option ycs: %beu\n" + "option sac: %s\n", #if HAVE_ERTS_MSEG max_mseg_carriers, #endif - sys_alloc_carrier_size); + sys_alloc_carrier_size, + allow_sys_alloc_carriers ? "true" : "false"); } if (hpp || szp) { res = NIL; ensure_atoms_initialized(NULL); + add_2tup(hpp, szp, &res, + am.sac, + allow_sys_alloc_carriers ? am_true : am_false); add_2tup(hpp, szp, &res, am.ycs, bld_uint(hpp, szp, sys_alloc_carrier_size)); @@ -5681,6 +5691,7 @@ erts_alcu_init(AlcUInit_t *init) #else /* #if HAVE_ERTS_MSEG */ sys_alloc_carrier_size = ((init->ycs + 4095) / 4096) * 4096; #endif + allow_sys_alloc_carriers = init->sac; #ifdef DEBUG carrier_alignment = sizeof(Unit_t); diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index 222f137024..7be6b1ed9d 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -32,6 +32,7 @@ typedef struct Allctr_t_ Allctr_t; typedef struct { UWord ycs; UWord mmc; + int sac; } AlcUInit_t; typedef struct { @@ -75,7 +76,8 @@ typedef struct { #define ERTS_DEFAULT_ALCU_INIT { \ 1024*1024, /* (bytes) ycs: sys_alloc carrier size */\ - ~((UWord) 0) /* (amount) mmc: max mseg carriers */ \ + ~((UWord) 0), /* (amount) mmc: max mseg carriers */\ + 1 /* (bool) sac: sys_alloc carriers */\ } #define ERTS_DEFAULT_ALLCTR_INIT { \ @@ -109,7 +111,8 @@ typedef struct { #define ERTS_DEFAULT_ALCU_INIT { \ 128*1024, /* (bytes) ycs: sys_alloc carrier size */\ - 1024 /* (amount) mmc: max mseg carriers */\ + 1024, /* (amount) mmc: max mseg carriers */\ + 1 /* (bool) sac: sys_alloc carriers */\ } #define ERTS_DEFAULT_ALLCTR_INIT { \ diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index ffbefce545..6fd3dd83e2 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -103,6 +103,7 @@ static char *plusM_other_switches[] = { "ea", "ummc", "uycs", + "usac", "im", "is", "it", -- cgit v1.2.3 From e526cb32336afc4feb4b92665d31ccc2af3e741c Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 5 Nov 2013 17:00:38 +0100 Subject: Replace the +MMscmgc switch with +MMscrfsd Replaced the +MMscmgc switch with the +MMscrfsd switch. The old switch didn't reflect what it controlled. --- erts/doc/src/erts_alloc.xml | 22 +++++++++++++--------- erts/emulator/beam/erl_alloc.c | 6 +++--- erts/emulator/sys/common/erl_mmap.c | 12 ++++++------ erts/emulator/sys/common/erl_mmap.h | 4 ++-- erts/emulator/test/alloc_SUITE.erl | 12 ++++++------ erts/etc/common/erlexec.c | 2 +- 6 files changed, 31 insertions(+), 27 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 70029dbeab..8f5e8cd10b 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -271,15 +271,6 @@ memory segment cache is not reused if its size exceeds the requested size with more than relative max cache bad fit percent of the requested size. Default value is 20. - ]]> - - Set super carrier max guaranteed - no of carriers. This parameter defaults to 65536. This - parameter determines an amount of pre-allocated structures that is - needed in order to keep track of different areas in the super carrier. - When the system runs out of such structures it may crash due to an - out of memory condition. - Set super carrier only flag. This @@ -295,6 +286,19 @@ disabled on halfword heap systems. This flag will be ignored on halfword heap systems. + ]]> + + Set super carrier reserved + free segment descriptors. This parameter defaults to 65536. + This parameter determines the amount of memory to reserve for + free segment descriptors used by the super carrier. If the system + runs out of reserved memory for free segment descriptors, other + memory will be used. This may however cause fragmentation issues, + so you want to ensure that this never happens. The maximum amount + of free segment descriptors used can be retrieved from the + erts_mmap tuple part of the result from calling + erlang:system_info({allocator, mseg_alloc}). + Set super carrier reserve physical diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index fb0f47d1b7..b5ba9bb94a 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1504,11 +1504,11 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) #endif get_bool_value(argv[i]+8, argv, &i); } - else if (has_prefix("scmgc", argv[i]+3)) { + else if (has_prefix("scrfsd", argv[i]+3)) { #if HAVE_ERTS_MSEG - init->mseg.mmap.scmgc = + init->mseg.mmap.scrfsd = #endif - get_amount_value(argv[i]+8, argv, &i); + get_amount_value(argv[i]+9, argv, &i); } else { bad_param(param, param+2); diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index a9da7430fb..3f6813e1a5 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -2057,7 +2057,7 @@ static struct { Eterm scs; Eterm sco; Eterm scrpm; - Eterm scmgc; + Eterm scrfsd; int is_initialized; erts_mtx_t init_mutex; @@ -2090,7 +2090,7 @@ static void init_atoms(void) AM_INIT(scs); AM_INIT(sco); AM_INIT(scrpm); - AM_INIT(scmgc); + AM_INIT(scrfsd); am.is_initialized = 1; } erts_mtx_unlock(&am.init_mutex); @@ -2121,7 +2121,7 @@ erts_mmap_init(ErtsMMapInit *init) #if defined(HARD_DEBUG) || 0 erts_fprintf(stderr, "erts_mmap: scs = %bpu\n", init->scs); erts_fprintf(stderr, "erts_mmap: sco = %i\n", init->sco); - erts_fprintf(stderr, "erts_mmap: scmgc = %i\n", init->scmgc); + erts_fprintf(stderr, "erts_mmap: scrfsd = %i\n", init->scrfsd); #endif erts_page_inv_mask = pagesize - 1; if (pagesize & erts_page_inv_mask) @@ -2234,7 +2234,7 @@ erts_mmap_init(ErtsMMapInit *init) mmap_state.no_os_mmap = init->sco; - desc_size = init->scmgc; + desc_size = init->scrfsd; if (desc_size < 100) desc_size = 100; desc_size *= sizeof(ErtsFreeSegDesc); @@ -2418,7 +2418,7 @@ Eterm erts_mmap_info_options(char *prefix, if (mmap_state.supercarrier) { erts_print(to, arg, "%ssco: %T\n", prefix, sco); erts_print(to, arg, "%sscrpm: %T\n", prefix, scrpm); - erts_print(to, arg, "%sscmgc: %beu\n", prefix, mmap_state.desc.reserved); + erts_print(to, arg, "%sscrfsd: %beu\n", prefix, mmap_state.desc.reserved); } } @@ -2429,7 +2429,7 @@ Eterm erts_mmap_info_options(char *prefix, res = NIL; if (mmap_state.supercarrier) { - add_2tup(hpp, szp, &res, am.scmgc, + add_2tup(hpp, szp, &res, am.scrfsd, erts_bld_uint(hpp,szp, mmap_state.desc.reserved)); add_2tup(hpp, szp, &res, am.scrpm, scrpm); add_2tup(hpp, szp, &res, am.sco, sco); diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index e6934dbb26..778a8e0e80 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -45,8 +45,8 @@ typedef struct { } predefined_area; UWord scs; /* super carrier size */ int sco; /* super carrier only? */ - Uint scmgc; /* super carrier: max guaranteed (number of) carriers */ - int scrpm; + UWord scrfsd; /* super carrier reserved free segment descriptors */ + int scrpm; /* super carrier reserve physical memory */ }ErtsMMapInit; #define ERTS_MMAP_INIT_DEFAULT_INITER \ diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index f6ff6bb813..cd2d043f7e 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -114,8 +114,8 @@ cpool(Cfg) -> ?line drv_case(Cfg). erts_mmap(Config) when is_list(Config) -> case {?t:os_type(), is_halfword_vm()} of {{unix, _}, false} -> - [erts_mmap_do(Config, SCO, SCRPM, SCMGC) - || SCO <-[true,false], SCMGC <-[1234,0], SCRPM <- [true,false]]; + [erts_mmap_do(Config, SCO, SCRPM, SCRFSD) + || SCO <-[true,false], SCRFSD <-[1234,0], SCRPM <- [true,false]]; {_,true} -> {skipped, "No supercarrier support on halfword vm"}; @@ -126,14 +126,14 @@ erts_mmap(Config) when is_list(Config) -> end. -erts_mmap_do(Config, SCO, SCRPM, SCMGC) -> +erts_mmap_do(Config, SCO, SCRPM, SCRFSD) -> SCS = 100, % Mb O1 = "+MMscs" ++ integer_to_list(SCS) ++ " +MMsco" ++ atom_to_list(SCO) ++ " +MMscrpm" ++ atom_to_list(SCRPM), - Opts = case SCMGC of + Opts = case SCRFSD of 0 -> O1; - _ -> O1 ++ " +MMscmgc"++integer_to_list(SCMGC) + _ -> O1 ++ " +MMscrfsd"++integer_to_list(SCRFSD) end, {ok, Node} = start_node(Config, Opts), Self = self(), @@ -148,7 +148,7 @@ erts_mmap_do(Config, SCO, SCRPM, SCMGC) -> Total = SCS*1024*1024, {reserved,Reserved} = lists:keyfind(reserved,1,Segs), - true = (Reserved >= SCMGC), + true = (Reserved >= SCRFSD), case {SCO,lists:keyfind(os,1,EM)} of {true, false} -> ok; diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 6fd3dd83e2..f098e56a2e 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -112,7 +112,7 @@ static char *plusM_other_switches[] = { "Mrmcbf", "Mmcs", "Mscs", - "Mscmgc", + "Mscrfsd", "Msco", "Mscrpm", "Ye", -- cgit v1.2.3 From b82549dfb985e4b55a6531d634e46446f7a8dbee Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 5 Nov 2013 17:13:35 +0100 Subject: Fix documentation of the +MMsco switch --- erts/doc/src/erts_alloc.xml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 8f5e8cd10b..a0ec89f398 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -275,9 +275,14 @@ Set super carrier only flag. This flag defaults to true. When a super carrier is used and this - flag is true, the system will crash when a carrier request - cannot be satisfied by the super carrier. When the flag is false - the system will try to create requested carrier by other means. + flag is true, mseg_alloc will only create carriers + in the super carrier. Note that the alloc_util framework may + create sys_alloc carriers, so if you want all carriers to + be created in the super carrier, you therefore want to disable use + of sys_alloc carriers by also passing + +Musac false. When the flag + is false, mseg_alloc will try to create carriers outside + of the super carrier when the super carrier is full.

NOTE: Setting this flag to false may not be supported on all systems. This flag will in that case be ignored. -- cgit v1.2.3 From f207cee9b667914dd93b5f4349fb86dd14bff7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Nov 2013 11:04:48 +0100 Subject: preloaded: Add debug_info to preloaded Needed by Dialyzer. --- erts/preloaded/src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index c1580b1495..4ea2d41075 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -68,7 +68,7 @@ KERNEL_SRC=$(ERL_TOP)/lib/kernel/src KERNEL_INCLUDE=$(ERL_TOP)/lib/kernel/include STDLIB_INCLUDE=$(ERL_TOP)/lib/stdlib/include -ERL_COMPILE_FLAGS += +warn_obsolete_guard -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE) +ERL_COMPILE_FLAGS += +warn_obsolete_guard +debug_info -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE) debug opt: $(TARGET_FILES) -- cgit v1.2.3 From 5bddfc9a002f27df1228182ac170e7e4a7023fc0 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 31 Oct 2013 17:38:38 +0100 Subject: erts: Add cerl -dump and dumping in z_SUITE --- erts/etc/unix/cerl.src | 28 +++++++++++++++++++++++++++- erts/test/z_SUITE.erl | 31 ++++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 41baa323ed..38c6dd037f 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -33,6 +33,7 @@ # You have to start beam in gdb using "run". # -rgdb Run the debug compiled emulator in gdb. # You have to start beam in gdb using "run". +# -dump Dump the bt of all threads in a core. # -break F Run the debug compiled emulator in emacs and gdb and set break. # The session is started, i.e. "run" is already don for you. # -xxgdb FIXME currently disabled @@ -178,6 +179,12 @@ while [ $# -gt 0 ]; do cargs="$cargs -frmptr" TYPE=.frmptr ;; + "-dump") + shift + GDB=dump + core="$1" + shift + ;; "-gdb") shift GDB=egdb @@ -331,7 +338,7 @@ elif [ "x$GDB" = "xgdb" ]; then echo "source $ROOTDIR/erts/etc/unix/etp-commands" > $cmdfile # Fire up gdb in emacs... exec gdb $GDBBP -x $cmdfile $gdbcmd -else +elif [ "x$GDB" = "xegdb" ]; then if [ "x$EMACS" = "x" ]; then EMACS=emacs fi @@ -372,4 +379,23 @@ else (comint-send-input)" # Fire up gdb in emacs... exec $EMACS --eval "(progn (gdb \"gdb $GDBARGS$EMU_NAME\") $gdbcmd)" +elif [ "x$GDB" = "xdump" ]; then + cmdfile="/tmp/.cerlgdb.$$" + case "x$core" in + x/*) + gdbcmd="$EMU_NAME ${core}" + ;; + *) + dir=`pwd` + gdbcmd="$EMU_NAME ${dir}/${core}" + ;; + esac + echo "set width 0 +set height 0 +set verbose off + +source $ROOTDIR/erts/etc/unix/etp-commands +thread apply all bt +" > $cmdfile + exec gdb --batch --command=$cmdfile $gdbcmd fi diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl index ccf22a9b6b..da72b18f05 100644 --- a/erts/test/z_SUITE.erl +++ b/erts/test/z_SUITE.erl @@ -231,6 +231,20 @@ mod_time_list(F) -> str_strip(S) -> string:strip(string:strip(string:strip(S), both, $\n), both, $\r). +dump_core(#core_search_conf{ cerl = false }, _) -> + ok; +dump_core(_, {ignore, _Core}) -> + ok; +dump_core(#core_search_conf{ cerl = Cerl }, Core) -> + Dump = case test_server:is_debug() of + true -> + os:cmd(Cerl ++ " -debug -dump " ++ Core); + _ -> + os:cmd(Cerl ++ " -dump " ++ Core) + end, + ct:log("~s~n~n~s",[Core,Dump]). + + format_core(Conf, {ignore, Core}) -> format_core(Conf, Core, "[ignored] "); format_core(Conf, Core) -> @@ -254,11 +268,16 @@ core_file_search(#core_search_conf{search_dir = Base, extra_search_dir = XBase, cerl = Cerl, run_by_ts = RunByTS} = Conf) -> - case Cerl of - false -> ok; - _ -> catch io:format("A cerl script that probably can be used for " - "inspection of emulator cores:~n ~s~n", - [Cerl]) + case {Cerl,test_server:is_debug()} of + {false,_} -> ok; + {_,true} -> + catch io:format("A cerl script that probably can be used for " + "inspection of emulator cores:~n ~s -debug~n", + [Cerl]); + _ -> + catch io:format("A cerl script that probably can be used for " + "inspection of emulator cores:~n ~s~n", + [Cerl]) end, io:format("Searching for core-files in: ~s~s~n", [case XBase of @@ -329,6 +348,8 @@ core_file_search(#core_search_conf{search_dir = Base, ["Ignored core-files found:", lists:reverse(ICores)] end]), + + lists:foreach(fun(C) -> dump_core(Conf,C) end, Cores), case {RunByTS, ICores, FCores} of {true, [], []} -> ok; {true, _, []} -> {comment, Res}; -- cgit v1.2.3 From bb52546e9a671ed0fd55d2e5274f299a853bed51 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 5 Nov 2013 15:52:56 +0100 Subject: Implement prim_inet:socknames/1,2 and prim_inet:peernames/1,2 --- erts/configure.in | 2 +- erts/emulator/beam/io.c | 2 +- erts/emulator/drivers/common/inet_drv.c | 225 ++++++++++++++++++++++++++++++-- erts/preloaded/ebin/prim_inet.beam | Bin 70856 -> 72632 bytes erts/preloaded/src/prim_inet.erl | 70 +++++++++- 5 files changed, 287 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index f17f4cb5c8..4f94f0d156 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1647,7 +1647,7 @@ if test "x$enable_sctp" != "xno" ; then fi if test x"$ac_cv_header_netinet_sctp_h" = x"yes"; then - AC_CHECK_FUNCS([sctp_bindx sctp_peeloff]) + AC_CHECK_FUNCS([sctp_bindx sctp_peeloff sctp_getladdrs sctp_freeladdrs sctp_getpaddrs sctp_freepaddrs]) AC_CHECK_DECLS([SCTP_UNORDERED, SCTP_ADDR_OVER, SCTP_ABORT, SCTP_EOF, SCTP_SENDALL, SCTP_ADDR_CONFIRMED, SCTP_DELAYED_ACK_TIME, diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index c1e66b59af..aeecea1ff5 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4078,7 +4078,7 @@ erts_port_control(Process* c_p, copy = 1; else { binp = ((ProcBin *) ebinp)->val; - ASSERT(bufp < bufp + size); + ASSERT(bufp <= bufp + size); ASSERT(binp->orig_bytes <= bufp && bufp + size <= binp->orig_bytes + binp->orig_size); erts_refc_inc(&binp->refc, 1); diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 60db50e80a..8d26adfa63 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -417,13 +417,44 @@ static unsigned long one_value = 1; # define sctp_adaptation_layer_event sctp_adaption_layer_event #endif -#ifdef __GNUC__ +#if defined(__GNUC__) && defined(HAVE_SCTP_BINDX) static typeof(sctp_bindx) *p_sctp_bindx = NULL; +#else +static int (*p_sctp_bindx) + (int sd, struct sockaddr *addrs, int addrcnt, int flags) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_PEELOFF) static typeof(sctp_peeloff) *p_sctp_peeloff = NULL; #else -static int (*p_sctp_bindx)(int sd, struct sockaddr *addrs, - int addrcnt, int flags) = NULL; -static int (*p_sctp_peeloff)(int sd, sctp_assoc_t assoc_id) = NULL; +static int (*p_sctp_peeloff) + (int sd, sctp_assoc_t assoc_id) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_GETLADDRS) +static typeof(sctp_getladdrs) *p_sctp_getladdrs = NULL; +#else +static int (*p_sctp_getladdrs) + (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_FREELADDRS) +static typeof(sctp_freeladdrs) *p_sctp_freeladdrs = NULL; +#else +static void (*p_sctp_freeladdrs)(struct sockaddr *addrs) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_GETPADDRS) +static typeof(sctp_getpaddrs) *p_sctp_getpaddrs = NULL; +#else +static int (*p_sctp_getpaddrs) + (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_FREEPADDRS) +static typeof(sctp_freepaddrs) *p_sctp_freepaddrs = NULL; +#else +static void (*p_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #endif #endif /* #if defined(HAVE_SCTP_H) */ @@ -593,7 +624,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_F_BUSY 0x0080 #define INET_F_MULTI_CLIENT 0x0100 /* Multiple clients for one descriptor, i.e. multi-accept */ -/* One numberspace for *_REC_* so if an e.g UDP request is issued +/* One numberspace for *_REQ_* so if an e.g UDP request is issued ** for a TCP socket, the driver can protest. */ #define INET_REQ_OPEN 1 @@ -624,6 +655,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_REQ_ACCEPT 26 #define INET_REQ_LISTEN 27 #define INET_REQ_IGNOREFD 28 +#define INET_REQ_GETLADDRS 29 +#define INET_REQ_GETPADDRS 30 /* TCP requests */ /* #define TCP_REQ_ACCEPT 40 MOVED */ @@ -3658,9 +3691,27 @@ static int inet_init() /* Check the size of SCTP AssocID -- currently both this driver and the Erlang part require 32 bit: */ ASSERT(sizeof(sctp_assoc_t)==ASSOC_ID_LEN); -# if defined(HAVE_SCTP_BINDX) && defined (HAVE_SCTP_PEELOFF) +# if defined(HAVE_SCTP_BINDX) p_sctp_bindx = sctp_bindx; +# if defined(HAVE_SCTP_PEELOFF) p_sctp_peeloff = sctp_peeloff; +# else + p_sctp_peeloff = NULL; +# endif +# if defined(HAVE_SCTP_GETLADDRS) && defined(HAVE_SCTP_FREELADDRS) + p_sctp_getladdrs = sctp_getladdrs; + p_sctp_freeladdrs = sctp_freeladdrs; +# else + p_sctp_getladdrs = NULL; + p_sctp_freeladdrs = NULL; +# endif +# if defined(HAVE_SCTP_GETPADDRS) && defined(HAVE_SCTP_FREEPADDRS) + p_sctp_getpaddrs = sctp_getpaddrs; + p_sctp_freepaddrs = sctp_freepaddrs; +# else + p_sctp_getpaddrs = NULL; + p_sctp_freepaddrs = NULL; +# endif inet_init_sctp(); add_driver_entry(&sctp_inet_driver_entry); # else @@ -3675,12 +3726,36 @@ static int inet_init() void *ptr; if (erts_sys_ddll_sym(h_libsctp, "sctp_bindx", &ptr) == 0) { p_sctp_bindx = ptr; - inet_init_sctp(); - add_driver_entry(&sctp_inet_driver_entry); if (erts_sys_ddll_sym(h_libsctp, "sctp_peeloff", &ptr) == 0) { p_sctp_peeloff = ptr; } + else p_sctp_peeloff = NULL; + if (erts_sys_ddll_sym(h_libsctp, "sctp_getladdrs", &ptr) == 0) { + p_sctp_getladdrs = ptr; + } + else p_sctp_getladdrs = NULL; + if (erts_sys_ddll_sym(h_libsctp, "sctp_freeladdrs", &ptr) == 0) { + p_sctp_freeladdrs = ptr; + } + else { + p_sctp_freeladdrs = NULL; + p_sctp_getladdrs = NULL; + } + if (erts_sys_ddll_sym(h_libsctp, "sctp_getpaddrs", &ptr) == 0) { + p_sctp_getpaddrs = ptr; + } + else p_sctp_getpaddrs = NULL; + if (erts_sys_ddll_sym(h_libsctp, "sctp_freepaddrs", &ptr) == 0) { + p_sctp_freepaddrs = ptr; + } + else { + p_sctp_freepaddrs = NULL; + p_sctp_getpaddrs = NULL; + } + inet_init_sctp(); + add_driver_entry(&sctp_inet_driver_entry); } + else p_sctp_bindx = NULL; } } # endif @@ -3860,6 +3935,74 @@ static int inet_get_address(int family, char* dst, inet_address* src, unsigned i return -1; } +/* Same as the above, but take family from the address structure, +** and advance the address pointer to the next address +** according to the size of the current, +** and return the resulting encoded size +*/ +static int inet_address_to_erlang(char *dst, inet_address **src) { + short port; + + switch ((*src)->sa.sa_family) { + case AF_INET: + if (dst) { + dst[0] = INET_AF_INET; + port = sock_ntohs((*src)->sai.sin_port); + put_int16(port, dst+1); + sys_memcpy(dst+1+2, (char *) &(*src)->sai.sin_addr, 4); + } + (*src) = (inet_address *) (&(*src)->sai + 1); + return 1 + 2 + 4; +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + if (dst) { + dst[0] = INET_AF_INET6; + port = sock_ntohs((*src)->sai6.sin6_port); + put_int16(port, dst+1); + sys_memcpy(dst+1+2, (char *) &(*src)->sai6.sin6_addr, 16); + } + (*src) = (inet_address *) (&(*src)->sai6 + 1); + return 1 + 2 + 16; +#endif + default: + return -1; + } +} + +/* Encode n encoded addresses from addrs in the result buffer +*/ +static ErlDrvSizeT reply_inet_addrs +(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize) { + inet_address *ia; + int i, s; + ErlDrvSizeT rlen; + + if (IS_SOCKET_ERROR(n)) return ctl_error(sock_errno(), rbuf, rsize); + if (n == 0) return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); + + /* Calculate result length */ + rlen = 1; + ia = addrs; + for (i = 0; i < n; i++) { + s = inet_address_to_erlang(NULL, &ia); + if (s < 0) break; + rlen += s; + } + + if (rlen > rsize) (*rbuf) = ALLOC(rlen); + + (*rbuf)[0] = INET_REP_OK; + rlen = 1; + ia = addrs; + for (i = 0; i < n; i++) { + s = inet_address_to_erlang((*rbuf)+rlen, &ia); + if (s < 0) break; + rlen += s; + } + + return rlen; +} + static void desc_close(inet_descriptor* desc) { if (desc->s != INVALID_SOCKET) { @@ -7879,6 +8022,39 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, return ctl_reply(INET_REP_OK, tbuf, strlen(tbuf), rbuf, rsize); } + case INET_REQ_GETPADDRS: { + DEBUGF(("inet_ctl(%ld): INET_GETPADDRS\r\n", (long)desc->port)); + + if (len != 4) return ctl_error(EINVAL, rbuf, rsize); + + if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); + if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); + +#ifdef HAVE_SCTP + if (IS_SCTP(desc) && p_sctp_getpaddrs) { + struct sockaddr *sa; + Uint32 assoc_id; + int n; + ErlDrvSizeT rlen; + + assoc_id = get_int32(buf); + n = p_sctp_getpaddrs(desc->s, assoc_id, &sa); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + if (n > 0) p_sctp_freepaddrs(sa); + return rlen; + } +#endif + { /* Fallback to sock_peer */ + inet_address addr; + unsigned int sz; + int i; + + sz = sizeof(addr); + i = sock_peer(desc->s, (struct sockaddr *) &addr, &sz); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + } + } + case INET_REQ_PEER: { /* get peername */ char tbuf[sizeof(inet_address)]; inet_address peer; @@ -7915,6 +8091,39 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } } + case INET_REQ_GETLADDRS: { + DEBUGF(("inet_ctl(%ld): INET_GETLADDRS\r\n", (long)desc->port)); + + if (len != 4) return ctl_error(EINVAL, rbuf, rsize); + + if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); + if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); + +#ifdef HAVE_SCTP + if (IS_SCTP(desc) && p_sctp_getladdrs) { + struct sockaddr *sa; + Uint32 assoc_id; + int n; + ErlDrvSizeT rlen; + + assoc_id = get_int32(buf); + n = p_sctp_getladdrs(desc->s, assoc_id, &sa); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + if (n > 0) p_sctp_freeladdrs(sa); + return rlen; + } +#endif + { /* Fallback to sock_name */ + inet_address addr; + unsigned int sz; + int i; + + sz = sizeof(addr); + i = sock_name(desc->s, (struct sockaddr *) &addr, &sz); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + } + } + case INET_REQ_NAME: { /* get sockname */ char tbuf[sizeof(inet_address)]; inet_address name; diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index b0bef84e68..5d4be0b684 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index fa621681f3..aa700c5194 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -41,8 +41,8 @@ getifaddrs/1, getiflist/1, ifget/3, ifset/3, gethostname/1]). -export([getservbyname/3, getservbyport/3]). --export([peername/1, setpeername/2]). --export([sockname/1, setsockname/2]). +-export([peername/1, setpeername/2, peernames/1, peernames/2]). +-export([sockname/1, setsockname/2, socknames/1, socknames/2]). -export([attach/1, detach/1]). -include("inet_sctp.hrl"). @@ -574,6 +574,36 @@ setpeername(S, undefined) when is_port(S) -> {error,_}=Error -> Error end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% PEERNAMES(insock()) -> {ok, [{IP, Port}, ...]} | {error, Reason} +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +peernames(S) when is_port(S) -> + peernames(S, undefined). + +peernames(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) -> + peernames(S, AssocId); +peernames(S, AssocId) + when is_port(S), is_integer(AssocId); + is_port(S), AssocId =:= undefined -> + Q = get, + Type = [[sctp_assoc_id,0]], + case type_value(Q, Type, AssocId) of + true -> + case ctl_cmd + (S, ?INET_REQ_GETPADDRS, + enc_value(Q, Type, AssocId)) of + {ok,Addrs} -> + {ok,get_addrs(Addrs)}; + Error -> + Error + end; + false -> + {error,einval} + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% SOCKNAME(insock()) -> {ok, {IP, Port}} | {error, Reason} @@ -599,6 +629,36 @@ setsockname(S, undefined) when is_port(S) -> {error,_}=Error -> Error end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% SOCKNAMES(insock()) -> {ok, [{IP, Port}, ...]} | {error, Reason} +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +socknames(S) when is_port(S) -> + socknames(S, undefined). + +socknames(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) -> + socknames(S, AssocId); +socknames(S, AssocId) + when is_port(S), is_integer(AssocId); + is_port(S), AssocId =:= undefined -> + Q = get, + Type = [[sctp_assoc_id,0]], + case type_value(Q, Type, AssocId) of + true -> + case ctl_cmd + (S, ?INET_REQ_GETLADDRS, + enc_value(Q, Type, AssocId)) of + {ok,Addrs} -> + {ok,get_addrs(Addrs)}; + Error -> + Error + end; + false -> + {error,einval} + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% SETOPT(insock(), Opt, Value) -> ok | {error, Reason} @@ -2213,6 +2273,12 @@ ip6_to_bytes({A,B,C,D,E,F,G,H}) -> [?int16(A), ?int16(B), ?int16(C), ?int16(D), ?int16(E), ?int16(F), ?int16(G), ?int16(H)]. +get_addrs([]) -> + []; +get_addrs([F,P1,P0|Addr]) -> + {IP,Addrs} = get_ip(F, Addr), + [{IP,?u16(P1, P0)}|get_addrs(Addrs)]. + get_ip(?INET_AF_INET, Addr) -> get_ip4(Addr); get_ip(?INET_AF_INET6, Addr) -> get_ip6(Addr). -- cgit v1.2.3 From 7b739330bb459401f9c11f0f84912aedc7ee22cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20H=C3=A4ssler?= Date: Sat, 9 Nov 2013 21:04:06 +0100 Subject: Add os:unsetenv/1 New BIF os:unsetenv/1 which deletes an environment variable and returns 'true'. Does not change any old functionality. Calls the libc function unsetenv(3) on UNIX and SetEnvironmentVariableW(key, NULL) on Windows. The unicode support is the same as for os:getenv and os:putenv. --- erts/emulator/beam/bif.tab | 1 + erts/emulator/beam/erl_bif_os.c | 22 ++++++++++++++++++++++ erts/emulator/beam/sys.h | 2 ++ erts/emulator/sys/unix/sys.c | 10 ++++++++++ erts/emulator/sys/win32/sys_env.c | 18 ++++++++++++++++++ erts/emulator/test/bif_SUITE.erl | 10 +++++++--- 6 files changed, 60 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index dc8e9101de..6037c08dd8 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -572,6 +572,7 @@ bif erlang:float_to_binary/2 bif erlang:binary_to_float/1 bif io:printable_range/0 +bif os:unsetenv/1 # # Obsolete diff --git a/erts/emulator/beam/erl_bif_os.c b/erts/emulator/beam/erl_bif_os.c index 1062d4379b..e07c622928 100644 --- a/erts/emulator/beam/erl_bif_os.c +++ b/erts/emulator/beam/erl_bif_os.c @@ -180,3 +180,25 @@ BIF_RETTYPE os_putenv_2(BIF_ALIST_2) BIF_RET(am_true); } +BIF_RETTYPE os_unsetenv_1(BIF_ALIST_1) +{ + char *key_buf; + char buf[STATIC_BUF_SIZE]; + + key_buf = erts_convert_filename_to_native(BIF_ARG_1,buf,STATIC_BUF_SIZE, + ERTS_ALC_T_TMP,0,0,NULL); + if (!key_buf) { + BIF_ERROR(BIF_P, BADARG); + } + + if (erts_sys_unsetenv(key_buf)) { + if (key_buf != buf) { + erts_free(ERTS_ALC_T_TMP, key_buf); + } + BIF_ERROR(BIF_P, BADARG); + } + if (key_buf != buf) { + erts_free(ERTS_ALC_T_TMP, key_buf); + } + BIF_RET(am_true); +} diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 9561c0be96..d22f125945 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -750,6 +750,8 @@ int erts_sys_getenv(char *key, char *value, size_t *size); 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); +/* erst_sys_unsetenv() returns 0 on success and a value != 0 on failure. */ +int erts_sys_unsetenv(char *key); /* Easier to use, but not as efficient, environment functions */ char *erts_read_env(char *key); diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 401b37b9d2..61f9f6a59a 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -2489,6 +2489,16 @@ erts_sys_getenv(char *key, char *value, size_t *size) return res; } +int +erts_sys_unsetenv(char *key) +{ + int res; + erts_smp_rwmtx_rwlock(&environ_rwmtx); + res = unsetenv(key); + erts_smp_rwmtx_rwunlock(&environ_rwmtx); + return res; +} + void sys_init_io(void) { diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c index 754f4c6e4c..9f977ad6c8 100644 --- a/erts/emulator/sys/win32/sys_env.c +++ b/erts/emulator/sys/win32/sys_env.c @@ -141,6 +141,24 @@ void fini_getenv_state(GETENV_STATE *state) erts_smp_rwmtx_runlock(&environ_rwmtx); } +int erts_sys_unsetenv(char *key) +{ + int res = 0; + WCHAR *wkey = (WCHAR *) key; + + SetLastError(0); + erts_smp_rwmtx_rlock(&environ_rwmtx); + GetEnvironmentVariableW(wkey, + NULL, + 0); + if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) { + res = (SetEnvironmentVariableW(wkey, + NULL) ? 0 : 1); + } + erts_smp_rwmtx_runlock(&environ_rwmtx); + return res; +} + char* win_build_environment(char* new_env) { diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 02c6de8cb1..8b612a145c 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -388,8 +388,12 @@ os_env(Config) when is_list(Config) -> false -> ?line ok; BadVal -> ?line ?t:fail(BadVal) end, - %% os:putenv and os:getenv currently uses a temp buf of size 1024 - %% for storing key+value + ?line true = os:putenv(EnvVar1, "mors"), + ?line true = os:unsetenv(EnvVar1), + ?line false = os:getenv(EnvVar1), + ?line true = os:unsetenv(EnvVar1), % unset unset variable + %% os:putenv, os:getenv and os:unsetenv currently use a temp + %% buffer of size 1024 for storing key+value ?line os_env_long(1010, 1030, "hej hopp"). os_env_long(Min, Max, _Value) when Min > Max -> @@ -398,7 +402,7 @@ os_env_long(Min, Max, Value) -> ?line EnvVar = lists:duplicate(Min, $X), ?line true = os:putenv(EnvVar, Value), ?line Value = os:getenv(EnvVar), - ?line true = os:putenv(EnvVar, ""), + ?line true = os:unsetenv(EnvVar), ?line os_env_long(Min+1, Max, Value). otp_7526(doc) -> -- cgit v1.2.3 From 22c39699f35142063f69c45bdb919bcba468fede Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Mon, 11 Nov 2013 10:53:57 +0100 Subject: erts: remove ?line macro in testcases --- erts/emulator/test/bif_SUITE.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 8b612a145c..fbc229bc53 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -388,10 +388,10 @@ os_env(Config) when is_list(Config) -> false -> ?line ok; BadVal -> ?line ?t:fail(BadVal) end, - ?line true = os:putenv(EnvVar1, "mors"), - ?line true = os:unsetenv(EnvVar1), - ?line false = os:getenv(EnvVar1), - ?line true = os:unsetenv(EnvVar1), % unset unset variable + true = os:putenv(EnvVar1, "mors"), + true = os:unsetenv(EnvVar1), + false = os:getenv(EnvVar1), + true = os:unsetenv(EnvVar1), % unset unset variable %% os:putenv, os:getenv and os:unsetenv currently use a temp %% buffer of size 1024 for storing key+value ?line os_env_long(1010, 1030, "hej hopp"). @@ -402,7 +402,7 @@ os_env_long(Min, Max, Value) -> ?line EnvVar = lists:duplicate(Min, $X), ?line true = os:putenv(EnvVar, Value), ?line Value = os:getenv(EnvVar), - ?line true = os:unsetenv(EnvVar), + true = os:unsetenv(EnvVar), ?line os_env_long(Min+1, Max, Value). otp_7526(doc) -> -- cgit v1.2.3 From 2e96b554bd0374224aed1c28e48d139fad42c38e Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Mon, 11 Nov 2013 15:07:14 +0100 Subject: Clean up address family handling towards Erlang --- erts/emulator/drivers/common/inet_drv.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 8d26adfa63..de18656273 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1450,8 +1450,7 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) #ifdef HAVE_SCTP /* For SCTP, we often need to return {IP, Port} tuples: */ -static int inet_get_address - (int family, char* dst, inet_address* src, unsigned int* len); +static int inet_get_address(char* dst, inet_address* src, unsigned int* len); #define LOAD_IP_AND_PORT_CNT \ (8*LOAD_INT_CNT + LOAD_TUPLE_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT) @@ -1466,8 +1465,7 @@ static int load_ip_and_port unsigned int len = sizeof(struct sockaddr_storage); unsigned int alen = len; char abuf [len]; - int res = - inet_get_address(desc->sfamily, abuf, (inet_address*) addr, &alen); + int res = inet_get_address(abuf, (inet_address*) addr, &alen); ASSERT(res==0); res = 0; /* Now "abuf" contains: Family(1b), Port(2b), IP(4|16b) */ @@ -3910,10 +3908,12 @@ static char *inet_set_faddress(int family, inet_address* dst, ** and *len is the length of dst on return ** (suitable to deliver to erlang) */ -static int inet_get_address(int family, char* dst, inet_address* src, unsigned int* len) +static int inet_get_address(char* dst, inet_address* src, unsigned int* len) { + int family; short port; + family = src->sa.sa_family; if ((family == AF_INET) && (*len >= sizeof(struct sockaddr_in))) { dst[0] = INET_AF_INET; port = sock_ntohs(src->sai.sin_port); @@ -8070,7 +8070,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (IS_SOCKET_ERROR(sock_peer(desc->s, (struct sockaddr*)ptr,&sz))) return ctl_error(sock_errno(), rbuf, rsize); } - if (inet_get_address(desc->sfamily, tbuf, ptr, &sz) < 0) + if (inet_get_address(tbuf, ptr, &sz) < 0) return ctl_error(EINVAL, rbuf, rsize); return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize); } @@ -8140,7 +8140,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (IS_SOCKET_ERROR(sock_name(desc->s, (struct sockaddr*)ptr, &sz))) return ctl_error(sock_errno(), rbuf, rsize); } - if (inet_get_address(desc->sfamily, tbuf, ptr, &sz) < 0) + if (inet_get_address(tbuf, ptr, &sz) < 0) return ctl_error(EINVAL, rbuf, rsize); return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize); } @@ -10979,7 +10979,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) inet_input_count(desc, n); udesc->i_ptr += n; - inet_get_address(desc->sfamily, abuf, &other, &len); + inet_get_address(abuf, &other, &len); /* Copy formatted address to the buffer allocated; "len" is the actual length which must be <= than the original reserved. This means that the addr + data in the buffer are contiguous, -- cgit v1.2.3 From 6a47cdc863ebb1bcdaf6d183cf40da67911f3fd0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 8 Oct 2013 19:26:20 +0200 Subject: erts: Fix cerl -gdb by replacing all newlines in $beam_args with space --- erts/etc/unix/cerl.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 41baa323ed..2d18b9f310 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -339,7 +339,7 @@ else case "x$core" in x) # Get emu args to use from erlexec... - beam_args=`$EXEC -emu_args_exit ${1+"$@"}` + beam_args=`$EXEC -emu_args_exit ${1+"$@"} | tr '\n' ' '` gdbcmd="(insert-string \"set args $beam_args\") \ (comint-send-input)" ;; -- cgit v1.2.3 From 9315ebe52626e7320e1de14d704ec5e3c7d72d1c Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 15 Nov 2013 10:52:14 +0100 Subject: Fix bug with backslash in erl_prim_loader:normalize/1 This function normalizes a path: * convert atoms to strings * flatten strings * convert backslash to a forward slash The bugfix is to only convert backslashes to forward slashes on windows and not on any other platforms. --- erts/preloaded/src/erl_prim_loader.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index e8ddfc4a57..fc43d1d4fa 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -1439,7 +1439,12 @@ normalize(Name, Acc) -> [Atom | Rest] when is_atom(Atom) -> normalize(atom_to_list(Atom) ++ Rest, Acc); [$\\ | Chars] -> - normalize(Chars, [$/ | Acc]); + case erlang:system_info(os_type) of + {win32, _} -> + normalize(Chars, [$/ | Acc]); + _ -> + normalize(Chars, [$\\ | Acc]) + end; [Char | Chars] -> normalize(Chars, [Char | Acc]); [] -> -- cgit v1.2.3 From c8f93bbcf1af2e41683f1b23acc2f51e637870b8 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 15 Nov 2013 10:56:57 +0100 Subject: Update preloaded --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 54536 -> 54672 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index f51ccfe8ce..ece0e6d5e8 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ -- cgit v1.2.3 From f47c818746c1df4055b1de8aabf47364f502274c Mon Sep 17 00:00:00 2001 From: Joseph Blomstedt Date: Thu, 29 Nov 2012 14:50:42 -0800 Subject: Add sync option to file:open/2 The sync option adds the POSIX O_SYNC flag to the open system call on platforms that support the flag or its equivalent, e.g., FILE_FLAG_WRITE_THROUGH on Windows. For platforms that don't support it, file:open/2 returns {error, enotsup} if the sync option is passed in. The semantics of O_SYNC are platform-specific. For example, not all platforms guarantee that all file metadata are written to the disk along with the file data when the flag is in effect. This issue is noted in the documentation this commit adds for the sync option. Add a test for the sync option. Note however that the underlying OS semantics for O_SYNC can't be tested automatically in any practical way, so the test assumes the OS does the right thing with the flag when present. For manual verification, dtruss on OS X and strace on Linux were both run against beam processes to watch calls to open(), and file:open/2 was called in Erlang shells to open files for writing, both with and without the sync option. Both the dtruss output and the strace output showed that the O_SYNC flag was present in the open() calls when sync was specified and was clear when sync was not specified. --- erts/emulator/drivers/common/erl_efile.h | 1 + erts/emulator/drivers/unix/unix_efile.c | 9 +++++++++ erts/emulator/drivers/win32/win_efile.c | 7 ++++++- erts/preloaded/ebin/prim_file.beam | Bin 44252 -> 44340 bytes erts/preloaded/src/prim_file.erl | 6 +++++- 5 files changed, 21 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h index 5387f75efc..95c036db8f 100644 --- a/erts/emulator/drivers/common/erl_efile.h +++ b/erts/emulator/drivers/common/erl_efile.h @@ -34,6 +34,7 @@ #define EFILE_COMPRESSED 8 #define EFILE_MODE_EXCL 16 #define EFILE_NO_TRUNCATE 32 /* Special for reopening on VxWorks */ +#define EFILE_MODE_SYNC 64 /* * Seek modes for efile_seek(). diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index 55539b44dd..8ffc05da99 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -405,6 +405,15 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ mode |= O_EXCL; } + if (flags & EFILE_MODE_SYNC) { +#ifdef O_SYNC + mode |= O_SYNC; +#else + errno = ENOTSUP; + return check_error(-1, errInfo); +#endif + } + fd = open(name, mode, FILE_MODE); if (!check_error(fd, errInfo)) diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index 319065f57b..d693d7d593 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -698,6 +698,7 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ HANDLE fd; /* Handle to open file. */ DWORD access; /* Access mode: GENERIC_READ, GENERIC_WRITE. */ DWORD crFlags; + DWORD flagsAndAttrs = FILE_ATTRIBUTE_NORMAL; WCHAR *wname = (WCHAR *) name; switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) { @@ -719,6 +720,10 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ return 0; } + if (flags & EFILE_MODE_SYNC) { + flagsAndAttrs = FILE_FLAG_WRITE_THROUGH; + } + if (flags & EFILE_MODE_APPEND) { crFlags = OPEN_ALWAYS; } @@ -727,7 +732,7 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ } fd = CreateFileW(wname, access, FILE_SHARE_FLAGS, - NULL, crFlags, FILE_ATTRIBUTE_NORMAL, NULL); + NULL, crFlags, flagsAndAttrs, NULL); /* * Check for errors. diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index a73a2f0db1..c2a1be6d3b 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 489e8ca4ea..5999e98340 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -123,9 +123,11 @@ -define(EFILE_MODE_APPEND, 4). -define(EFILE_COMPRESSED, 8). -define(EFILE_MODE_EXCL, 16). +%% Note: bit 5 (32) is used internally for VxWorks +-define(EFILE_MODE_SYNC, 64). %% Use this mask to get just the mode bits to be passed to the driver. --define(EFILE_MODE_MASK, 31). +-define(EFILE_MODE_MASK, 127). %% Seek modes for the driver's seek function. -define(EFILE_SEEK_SET, 0). @@ -1197,6 +1199,8 @@ open_mode([append|Rest], Mode, Portopts, Setopts) -> Portopts, Setopts); open_mode([exclusive|Rest], Mode, Portopts, Setopts) -> open_mode(Rest, Mode bor ?EFILE_MODE_EXCL, Portopts, Setopts); +open_mode([sync|Rest], Mode, Portopts, Setopts) -> + open_mode(Rest, Mode bor ?EFILE_MODE_SYNC, Portopts, Setopts); open_mode([delayed_write|Rest], Mode, Portopts, Setopts) -> open_mode([{delayed_write, 64*1024, 2000}|Rest], Mode, Portopts, Setopts); -- cgit v1.2.3 From 1a6347e868d375bb4bd9cb4e6651c82ea57bc220 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 18 Nov 2013 09:54:12 +0100 Subject: erts: Fix naming in erlang:monitor spec --- erts/preloaded/src/erlang.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 2b6760c675..291926578e 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1073,8 +1073,8 @@ module_loaded(_Module) -> %% monitor/2 -spec monitor(Type, Item) -> MonitorRef when Type :: process, - Item :: pid() | Module | {Module, Node}, - Module :: module(), + Item :: pid() | RegName | {RegName, Node}, + RegName :: module(), Node :: node(), MonitorRef :: reference(). monitor(_Type, _Item) -> -- cgit v1.2.3 From ee0ca14382e76d97285e64b3396fbb87f33e23da Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 18 Nov 2013 16:56:40 +0100 Subject: erts: Fix bugs in binary_to_term for invalid bitstrings <<131, 77, Len:32, Bits:8, Data/binary>> badarg if Bits > 8 Used to return internally inconsistent bitstring badarg if Len==0 and Bits > 0 Used to return invalid *huge* binary (size = (Uint)-1) badarg if Bits==0 and Len > 0 Used to return valid binary as if Bits was 8 --- erts/doc/src/erl_ext_dist.xml | 6 +++--- erts/emulator/beam/erl_binary.h | 2 +- erts/emulator/beam/external.c | 8 +++++--- erts/emulator/test/binary_SUITE.erl | 14 +++++++------- 4 files changed, 16 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml index c6849f3326..64a201cc8f 100644 --- a/erts/doc/src/erl_ext_dist.xml +++ b/erts/doc/src/erl_ext_dist.xml @@ -1014,10 +1014,10 @@

- This term represents a bitstring whose length in bits is not a - multiple of 8 (created using the bit syntax in R12B and later). + This term represents a bitstring whose length in bits does + not have to be a multiple of 8. The Len field is an unsigned 4 byte integer (big endian). - The Bits field is the number of bits that are used + The Bits field is the number of bits (1-8) that are used in the last byte in the data field, counting from the most significant bit towards the least significant. diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index 506c4813fa..24b10cffef 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -225,7 +225,7 @@ erts_free_aligned_binary_bytes(byte* buf) ** These extra bytes where earlier (< R13B04) added by an alignment-bug ** in this code. Do we dare remove this in some major release (R14?) maybe? */ -#ifdef DEBUG +#if defined(DEBUG) || defined(VALGRIND) # define CHICKEN_PAD 0 #else # define CHICKEN_PAD (sizeof(void*) - 1) diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 1c88765381..cfdd38df73 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3007,7 +3007,9 @@ dec_term_atom_common: n = get_int32(ep); bitsize = ep[4]; - ep += 5; + if (((bitsize==0) != (n==0)) || bitsize > 8) + goto error; + ep += 5; if (n <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hb = (ErlHeapBin *) hp; @@ -3035,10 +3037,10 @@ dec_term_atom_common: hp += PROC_BIN_SIZE; } ep += n; - if (bitsize == 0) { + if (bitsize == 8 || n == 0) { *objp = bin; } else { - sb = (ErlSubBin *) hp; + sb = (ErlSubBin *)hp; sb->thing_word = HEADER_SUB_BIN; sb->orig = bin; sb->size = n - 1; diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 08ab094019..d2c4a8ff3c 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -631,7 +631,12 @@ safe_binary_to_term2(Config) when is_list(Config) -> bad_terms(suite) -> []; bad_terms(Config) when is_list(Config) -> - ?line test_terms(fun corrupter/1). + ?line test_terms(fun corrupter/1), + {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,3:32,0,11,22,33>>)), + {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,3:32,9,11,22,33>>)), + {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,0:32,1,11,22,33>>)), + ok. + corrupter(Term) when is_function(Term); is_function(hd(Term)); @@ -1221,14 +1226,9 @@ gc() -> gc1() -> ok. bit_sized_binary_sizes(Config) when is_list(Config) -> - ?line [bsbs_1(A) || A <- lists:seq(0, 7)], + ?line [bsbs_1(A) || A <- lists:seq(1, 8)], ok. -bsbs_1(0) -> - BinSize = 32+8, - io:format("A: ~p BinSize: ~p", [0,BinSize]), - Bin = binary_to_term(<<131,$M,5:32,0,0,0,0,0,0>>), - BinSize = bit_size(Bin); bsbs_1(A) -> BinSize = 32+A, io:format("A: ~p BinSize: ~p", [A,BinSize]), -- cgit v1.2.3 From f32368c4b34c86aa772a372cdb3c306a79127185 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 18 Nov 2013 16:58:43 +0100 Subject: erts: Fix bug in binary_to_term for binaries larger than 2^31 --- erts/emulator/beam/external.c | 4 ++-- erts/emulator/test/binary_SUITE.erl | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index cfdd38df73..22b0a02937 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2970,7 +2970,7 @@ dec_term_atom_common: n = get_int32(ep); ep += 4; - if (n <= ERL_ONHEAP_BIN_LIMIT) { + if ((unsigned)n <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hb = (ErlHeapBin *) hp; hb->thing_word = header_heap_bin(n); @@ -3010,7 +3010,7 @@ dec_term_atom_common: if (((bitsize==0) != (n==0)) || bitsize > 8) goto error; ep += 5; - if (n <= ERL_ONHEAP_BIN_LIMIT) { + if ((unsigned)n <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hb = (ErlHeapBin *) hp; hb->thing_word = header_heap_bin(n); diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index d2c4a8ff3c..a340a805b5 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -635,6 +635,7 @@ bad_terms(Config) when is_list(Config) -> {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,3:32,0,11,22,33>>)), {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,3:32,9,11,22,33>>)), {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,0:32,1,11,22,33>>)), + {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,-1:32,1,11,22,33>>)), ok. -- cgit v1.2.3 From ca0425c6ff85262bc15367f5fd9cbc51cde52b20 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 2 Oct 2013 10:07:27 +0200 Subject: Execution of system tasks in context of another process A process requesting a system task to be executed in the context of another process will be notified by a message when the task has executed. This message will be on the form: {RequestType, RequestId, Pid, Result}. A process requesting a system task to be executed can set priority on the system task. The requester typically set the same priority on the task as its own process priority, and by this avoiding priority inversion. A request for execution of a system task is made by calling the statically linked in NIF erts_internal:request_system_task(Pid, Prio, Request). This is an undocumented ERTS internal function that should remain so. It should *only* be called from BIF implementations. Currently defined system tasks are: * garbage_collect * check_process_code Further system tasks can and will be implemented in the future. The erlang:garbage_collect/[1,2] and erlang:check_process_code/[2,3] BIFs are now implemented using system tasks. Both the 'garbage_collect' and the 'check_process_code' operations perform or may perform garbage_collections. By doing these via the system task functionality all garbage collect operations in the system will be performed solely in the context of the process being garbage collected. This makes it possible to later implement functionality for disabling garbage collection of a process over context switches. Newly introduced BIFs: * erlang:garbage_collect/2 - The new second argument is an option list. Introduced option: * {async, RequestId} - making it possible for users to issue asynchronous garbage collect requests. * erlang:check_process_code/3 - The new third argument is an option list. Introduced options: * {async, RequestId} - making it possible for users to issue asynchronous check process code requests. * {allow_gc, boolean()} - making it possible to issue requests that aren't allowed to garbage collect (operation will abort if gc should be needed). These options have been introduced as a preparation for parallelization of check_process_code operations when the code_server is about to purge a module. --- erts/doc/src/erlang.xml | 161 ++++- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/beam_bif_load.c | 133 ++-- erts/emulator/beam/bif.c | 39 - erts/emulator/beam/bif.tab | 6 +- erts/emulator/beam/break.c | 10 +- erts/emulator/beam/erl_alloc.types | 2 + erts/emulator/beam/erl_message.h | 5 + erts/emulator/beam/erl_process.c | 1224 ++++++++++++++++++++++++++++---- erts/emulator/beam/erl_process.h | 74 +- erts/emulator/beam/global.h | 4 + erts/emulator/beam/ops.tab | 14 +- erts/emulator/beam/utils.c | 2 +- erts/emulator/hipe/hipe_bif_list.m4 | 3 +- erts/emulator/hipe/hipe_native_bif.c | 17 +- erts/emulator/test/process_SUITE.erl | 162 ++++- erts/etc/unix/etp-commands.in | 93 ++- erts/preloaded/ebin/erlang.beam | Bin 94152 -> 97500 bytes erts/preloaded/ebin/erts_internal.beam | Bin 3260 -> 3780 bytes erts/preloaded/src/erlang.erl | 132 +++- erts/preloaded/src/erts_internal.erl | 21 + 21 files changed, 1787 insertions(+), 316 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 5ee40823bc..a9642be6fa 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -501,16 +501,87 @@ Check if a process is executing old code for a module -

Returns true if the process Pid is executing - old code for Module. 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 - module. Otherwise, it returns false.

-
-> check_process_code(Pid, lists).
-false
+

The same as + erlang:check_process_code(Pid, + Module, []).

+ + + + + Check if a process is executing old code for a module + +

Check if the node local process identified by Pid + is executing old code for Module.

+

Currently available Options:

+ + {allow_gc, boolean()} + + Determines if garbage collection is allowed when performing + the operation. If {allow_gc, false} is passed, and + a garbage collection is needed in order to determine the + result of the operation, the operation will be aborted + (see information on CheckResult below). + The default is to allow garbage collection, i.e., + {allow_gc, true}. + + {async, RequestId} + + The check_process_code/3 function will return + the value async immediately after the request + has been sent. When the request has been processed, the + process that called this function will be passed a + message on the form:
+ {check_process_code, RequestId, CheckResult}. +
+
+

If Pid equals self(), and + no async option has been passed, the operation will + be performed at once. In all other cases a request for + the operation will be sent to the process identified by + Pid, and will be handled when + appropriate. If no async option has been passed, + the caller will block until CheckResult + is available and can be returned.

+

CheckResult informs about the result of + the request:

+ + true + + The process identified by Pid is + executing old code for Module. + That is, the current call of the process executes old + code for this module, or the process has references + to old code for this module, or the process contains + funs that references old code for this module. + + false + + The process identified by Pid is + not executing old code for Module. + + aborted + + The operation was aborted since the process needed to + be garbage collected in order to determine the result + of the operation, and the operation was requested + by passing the {allow_gc, false} option. +

See also code(3).

+

Failures:

+ + badarg + + If Pid is not a node local process identifier. + + badarg + + If Module is not an atom. + + badarg + + If OptionList is not a valid list of options. + +
@@ -1197,20 +1268,74 @@ true that the spontaneous garbage collection will occur too late or not at all. Improper use may seriously degrade system performance.

-

Compatibility note: In versions of OTP prior to R7, - the garbage collection took place at the next context switch, - not immediately. To force a context switch after a call to - erlang:garbage_collect(), it was sufficient to make - any function call.

- Force an immediate garbage collection of a process + Garbage collect a process + +

The same as + garbage_collect(Pid, []).

+
+
+ + + Garbage collect a process -

Works like erlang:garbage_collect() but on any - process. The same caveats apply. Returns false if - Pid refers to a dead process; true otherwise.

+

Garbage collect the node local process identified by + Pid.

+

Currently available Options:

+ + {async, RequestId} + + The garbage_collect/2 function will return + the value async immediately after the request + has been sent. When the request has been processed, the + process that called this function will be passed a + message on the form:
+ {garbage_collect, RequestId, GCResult}. +
+
+

If Pid equals self(), and + no async option has been passed, the garbage + collection will be performed at once, i.e. the same as + calling + garbage_collect/0. + In all other cases a request for garbage collection will + be sent to the process identified by Pid, + and will be handled when appropriate. If no async + option has been passed, the caller will block until + GCResult is available and can be + returned.

+

GCResult informs about the result of + the garbage collection request:

+ + true + + The process identified by Pid has + been garbage collected. + + false + + No garbage collection was performed. This since the + the process identified by Pid + terminated before the request could be satisfied. + + +

Note that the same caveats as for + garbage_collect/0 + apply.

+

Failures:

+ + badarg + + If Pid is not a node local process identifier. + + badarg + + If OptionList is not a valid list of options. + +
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index c2f32ba089..8433fd826a 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -78,6 +78,7 @@ atom allocated_areas atom allocator atom allocator_sizes atom alloc_util_allocators +atom allow_gc atom allow_passive_connect atom already_loaded atom amd64 diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 649594a334..5239940a50 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -36,7 +36,7 @@ #include "erl_thr_progress.h" static void set_default_trace_pattern(Eterm module); -static Eterm check_process_code(Process* rp, Module* modp); +static Eterm check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp); static void delete_code(Module* modp); static void decrement_refc(BeamInstr* code); static int is_native(BeamInstr* code); @@ -427,69 +427,80 @@ check_old_code_1(BIF_ALIST_1) } Eterm -check_process_code_2(BIF_ALIST_2) +erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp) { - Process* rp; Module* modp; + Eterm res; + ErtsCodeIndex code_ix; - if (is_not_atom(BIF_ARG_2)) { - goto error; - } - if (is_internal_pid(BIF_ARG_1)) { - Eterm res; - ErtsCodeIndex code_ix; - - code_ix = erts_active_code_ix(); - modp = erts_get_module(BIF_ARG_2, code_ix); - if (modp == NULL) { /* Doesn't exist. */ - return am_false; - } - 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, - BIF_ARG_1, ERTS_PROC_LOCK_MAIN); -#else - rp = erts_pid2proc(BIF_P, 0, BIF_ARG_1, 0); -#endif - if (!rp) { - BIF_RET(am_false); - } - if (rp == ERTS_PROC_LOCK_BUSY) { - ERTS_BIF_YIELD2(bif_export[BIF_check_process_code_2], BIF_P, - BIF_ARG_1, BIF_ARG_2); - } - 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); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); + (*redsp)++; + + ASSERT(is_atom(module)); + + code_ix = erts_active_code_ix(); + modp = erts_get_module(module, code_ix); + if (!modp) + return am_false; + erts_rlock_old_code(code_ix); + res = modp->old.code ? check_process_code(c_p, modp, allow_gc, redsp) : am_false; + erts_runlock_old_code(code_ix); + + return res; +} + +BIF_RETTYPE erts_internal_check_process_code_2(BIF_ALIST_2) +{ + int reds = 0; + Eterm res; + Eterm olist = BIF_ARG_2; + int allow_gc = 1; + + if (is_not_atom(BIF_ARG_1)) + goto badarg; + + while (is_list(olist)) { + Eterm *lp = list_val(olist); + Eterm opt = CAR(lp); + if (is_tuple(opt)) { + Eterm* tp = tuple_val(opt); + switch (arityval(tp[0])) { + case 2: + switch (tp[1]) { + case am_allow_gc: + switch (tp[2]) { + case am_false: + allow_gc = 0; + break; + case am_true: + allow_gc = 1; + break; + default: + goto badarg; + } + break; + default: + goto badarg; + } + break; + default: + goto badarg; + } } -#endif - BIF_RET(res); - } - else if (is_external_pid(BIF_ARG_1) - && external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry) { - BIF_RET(am_false); + else + goto badarg; + olist = CDR(lp); } + if (is_not_nil(olist)) + goto badarg; - error: + res = erts_check_process_code(BIF_P, BIF_ARG_1, allow_gc, &reds); + + BIF_RET2(res, reds); + +badarg: BIF_ERROR(BIF_P, BADARG); } - BIF_RETTYPE delete_module_1(BIF_ALIST_1) { ErtsCodeIndex code_ix; @@ -710,7 +721,7 @@ set_default_trace_pattern(Eterm module) } static Eterm -check_process_code(Process* rp, Module* modp) +check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) { BeamInstr* start; char* mod_start; @@ -786,6 +797,8 @@ check_process_code(Process* rp, Module* modp) if (done_gc) { return am_true; } else { + if (!allow_gc) + return am_aborted; /* * Try to get rid of this fun by garbage collecting. * Clear both fvalue and ftrace to make sure they @@ -796,7 +809,7 @@ check_process_code(Process* rp, Module* modp) rp->ftrace = NIL; done_gc = 1; FLAGS(rp) |= F_NEED_FULLSWEEP; - (void) erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); + *redsp += erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); goto rescan; } } @@ -850,6 +863,9 @@ check_process_code(Process* rp, Module* modp) Uint lit_size; struct erl_off_heap_header* oh; + if (!allow_gc) + return am_aborted; + /* * Try to get rid of constants by by garbage collecting. * Clear both fvalue and ftrace. @@ -859,11 +875,12 @@ check_process_code(Process* rp, Module* modp) rp->ftrace = NIL; done_gc = 1; FLAGS(rp) |= F_NEED_FULLSWEEP; - (void) erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); + *redsp += erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); literals = (Eterm *) modp->old.code[MI_LITERALS_START]; lit_size = (Eterm *) modp->old.code[MI_LITERALS_END] - literals; oh = (struct erl_off_heap_header *) modp->old.code[MI_LITERALS_OFF_HEAP]; + *redsp += lit_size / 10; /* Need, better value... */ erts_garbage_collect_literals(rp, literals, lit_size, oh); } } diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 755c5e6882..58bd6aac0b 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3741,45 +3741,6 @@ BIF_RETTYPE now_0(BIF_ALIST_0) /**********************************************************************/ -BIF_RETTYPE garbage_collect_1(BIF_ALIST_1) -{ - int reds; - Process *rp; - - if (is_not_pid(BIF_ARG_1)) { - BIF_ERROR(BIF_P, BADARG); - } - - if (BIF_P->common.id == BIF_ARG_1) - rp = BIF_P; - else { -#ifdef ERTS_SMP - rp = erts_pid2proc_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, - BIF_ARG_1, ERTS_PROC_LOCK_MAIN); - if (rp == ERTS_PROC_LOCK_BUSY) - ERTS_BIF_YIELD1(bif_export[BIF_garbage_collect_1], BIF_P, BIF_ARG_1); -#else - rp = erts_proc_lookup(BIF_ARG_1); -#endif - if (!rp) - BIF_RET(am_false); - } - - /* The GC cost is taken for the process executing this BIF. */ - - FLAGS(rp) |= F_NEED_FULLSWEEP; - reds = erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); - -#ifdef ERTS_SMP - if (BIF_P != rp) { - erts_resume(rp, ERTS_PROC_LOCK_MAIN); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); - } -#endif - - BIF_RET2(am_true, reds); -} - BIF_RETTYPE garbage_collect_0(BIF_ALIST_0) { int reds; diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index dc8e9101de..3b7bb56885 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -46,7 +46,6 @@ bif erlang:atom_to_list/1 bif erlang:binary_to_list/1 bif erlang:binary_to_list/3 bif erlang:binary_to_term/1 -bif erlang:check_process_code/2 bif erlang:crc32/1 bif erlang:crc32/2 bif erlang:crc32_combine/3 @@ -67,7 +66,6 @@ bif erlang:float_to_list/1 bif erlang:float_to_list/2 bif erlang:fun_info/2 bif erlang:garbage_collect/0 -bif erlang:garbage_collect/1 bif erlang:get/0 bif erlang:get/1 bif erlang:get_keys/1 @@ -155,6 +153,10 @@ bif erts_internal:port_control/3 bif erts_internal:port_close/1 bif erts_internal:port_connect/2 +bif erts_internal:request_system_task/3 +bif erts_internal:check_process_code/2 + + # inet_db support bif erlang:port_set_data/2 bif erlang:port_get_data/1 diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index ad9a89b642..acbd700cc4 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -109,10 +109,12 @@ process_killer(void) 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_PSFLG_EXITING + | ERTS_PSFLG_ACTIVE + | ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_IN_RUNQ + | ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS)) { erts_printf("Can only kill WAITING processes this way\n"); } else { diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index bb5eba80be..32308fae9b 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -268,6 +268,8 @@ type PROC_INTERVAL LONG_LIVED SYSTEM process_interval type BUSY_CALLER_TAB SHORT_LIVED SYSTEM busy_caller_table type BUSY_CALLER SHORT_LIVED SYSTEM busy_caller type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap +type PROC_SYS_TSK SHORT_LIVED PROCESSES proc_sys_task +type PROC_SYS_TSK_QS SHORT_LIVED PROCESSES proc_sys_task_queues +if threads_no_smp # Need thread safe allocs, but std_alloc and fix_alloc are not; diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 771eba431f..0f3bb8d281 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -46,6 +46,11 @@ typedef struct erl_off_heap { Uint64 overhead; /* Administrative overhead (used to force GC). */ } ErlOffHeap; +#define ERTS_INIT_OFF_HEAP(OHP) \ + do { \ + (OHP)->first = NULL; \ + (OHP)->overhead = 0; \ + } while (0) #include "external.h" #include "erl_process.h" diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 434d5ca147..ad806da660 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -283,6 +283,40 @@ struct erts_system_profile_flags_t erts_system_profile_flags; #error "Need to store process_count in another type" #endif +typedef enum { + ERTS_PSTT_GC, /* Garbage Collect */ + ERTS_PSTT_CPC /* Check Process Code */ +} ErtsProcSysTaskType; + +#define ERTS_MAX_PROC_SYS_TASK_ARGS 2 + +struct ErtsProcSysTask_ { + ErtsProcSysTask *next; + ErtsProcSysTask *prev; + ErtsProcSysTaskType type; + Eterm requester; + Eterm reply_tag; + Eterm req_id; + Uint req_id_sz; + Eterm arg[ERTS_MAX_PROC_SYS_TASK_ARGS]; + ErlOffHeap off_heap; + Eterm heap[1]; +}; + +#define ERTS_PROC_SYS_TASK_SIZE(HSz) \ + (sizeof(ErtsProcSysTask) - sizeof(Eterm) + sizeof(Eterm)*(HSz)) + +struct ErtsProcSysTaskQs_ { + int qmask; + int ncount; + ErtsProcSysTask *q[ERTS_NO_PROC_PRIO_LEVELS]; +}; + +ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(proc_sys_task_queues, + ErtsProcSysTaskQs, + 50, + ERTS_ALC_T_PROC_SYS_TSK_QS) + ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(misc_op_list, ErtsMiscOpList, 10, @@ -353,6 +387,14 @@ static void aux_work_timeout_early_init(int no_schedulers); static void aux_work_timeout_late_init(void); static void setup_aux_work_timer(void); +static int execute_sys_tasks(Process *c_p, + erts_aint32_t *statep, + int in_reds); +static int cleanup_sys_tasks(Process *c_p, + erts_aint32_t in_state, + int in_reds); + + #if defined(DEBUG) || 0 #define ERTS_DBG_CHK_AUX_WORK_VAL(V) dbg_chk_aux_work_val((V)) static void @@ -2848,7 +2890,7 @@ dequeue_process(ErtsRunQueue *runq, int prio_q, erts_aint32_t *statep) if (statep) *statep = state; - prio = (int) (ERTS_PSFLG_PRIO_MASK & state); + prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state); rqi = &runq->procs.prio_info[prio]; @@ -2997,7 +3039,7 @@ immigrate(ErtsRunQueue *c_rq, ErtsMigrationPath *mp) erts_aint32_t state; state = erts_smp_atomic32_read_acqb(&proc->state); if (!(ERTS_PSFLG_BOUND & state) - && (prio == (int) (ERTS_PSFLG_PRIO_MASK & state))) { + && (prio == (int) ERTS_PSFLGS_GET_PRQ_PRIO(state))) { ErtsRunQueueInfo *rqi = &rq->procs.prio_info[prio]; unqueue_process(rq, rpq, rqi, prio, prev_proc, proc); erts_smp_runq_unlock(rq); @@ -3079,7 +3121,7 @@ schedule_bound_processes(ErtsRunQueue *rq, 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); + enqueue_process(rq, (int) ERTS_PSFLGS_GET_PRQ_PRIO(state), proc); proc = next; } } @@ -3184,7 +3226,7 @@ evacuate_run_queue(ErtsRunQueue *rq, sbpp->last = proc; } else { - int prio = (int) (ERTS_PSFLG_PRIO_MASK & state); + int prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state); erts_smp_runq_unlock(rq); to_rq = mp->prio[prio].runq; @@ -3257,7 +3299,7 @@ try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq, 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); + int prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state); ErtsRunQueueInfo *rqi = &vrq->procs.prio_info[prio]; unqueue_process(vrq, rpq, rqi, prio, prev_proc, proc); erts_smp_runq_unlock(vrq); @@ -4575,6 +4617,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) #endif init_misc_op_list_alloc(); + init_proc_sys_task_queues_alloc(); #ifdef ERTS_SMP set_wakeup_other_data(); @@ -4858,46 +4901,166 @@ erts_get_scheduler_data(void) #endif +static Process * +make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio) +{ + erts_aint32_t state; + Process *proxy; +#ifdef ERTS_SMP + ErtsRunQueue *rq = RUNQ_READ_RQ(&proc->run_queue); +#endif + + state = (ERTS_PSFLG_PROXY + | ERTS_PSFLG_IN_RUNQ + | (((erts_aint32_t) 1) << (prio + ERTS_PSFLGS_IN_PRQ_MASK_OFFSET)) + | (prio << ERTS_PSFLGS_PRQ_PRIO_OFFSET) + | (prio << ERTS_PSFLGS_USR_PRIO_OFFSET) + | (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET)); + + if (prev_proxy) { + proxy = prev_proxy; + ASSERT(erts_smp_atomic32_read_nob(&proxy->state) & ERTS_PSFLG_PROXY); + erts_smp_atomic32_set_nob(&proxy->state, state); +#ifdef ERTS_SMP + RUNQ_SET_RQ(&proc->run_queue, rq); +#endif + } + else { + proxy = erts_alloc(ERTS_ALC_T_PROC, sizeof(Process)); +#ifdef DEBUG + { + int i; + Uint32 *ui32 = (Uint32 *) (char *) proxy; + for (i = 0; i < sizeof(Process)/sizeof(Uint32); i++) + ui32[i] = (Uint32) 0xdeadbeef; + } +#endif + erts_smp_atomic32_init_nob(&proxy->state, state); +#ifdef ERTS_SMP + erts_smp_atomic_init_nob(&proxy->run_queue, + erts_smp_atomic_read_nob(&proc->run_queue)); +#endif + } + + proxy->common.id = proc->common.id; + + return proxy; +} + +static ERTS_INLINE void +free_proxy_proc(Process *proxy) +{ + ASSERT(erts_smp_atomic32_read_nob(&proxy->state) & ERTS_PSFLG_PROXY); + erts_free(ERTS_ALC_T_PROC, proxy); +} + + +static ERTS_INLINE int +check_enqueue_in_prio_queue(erts_aint32_t *prq_prio_p, + erts_aint32_t *newp, + erts_aint32_t actual) +{ + erts_aint32_t aprio, qbit, max_qbit; + + aprio = (actual >> ERTS_PSFLGS_ACT_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK; + qbit = 1 << aprio; + + *prq_prio_p = aprio; + + max_qbit = (actual >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET) & ERTS_PSFLGS_QMASK; + max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS; + max_qbit &= -max_qbit; + /* + * max_qbit now either contain bit set for highest prio queue or a bit + * out of range (which will have a value larger than valid range). + */ + + if (qbit >= max_qbit) + return 0; /* Already queued in higher or equal prio */ + + /* Need to enqueue (if already enqueued, it is in lower prio) */ + *newp |= qbit << ERTS_PSFLGS_IN_PRQ_MASK_OFFSET; + + if ((actual & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLGS_USR_PRIO_MASK)) + != (aprio << ERTS_PSFLGS_USR_PRIO_OFFSET)) { + /* + * Process struct already enqueued, or actual prio not + * equal to user prio, i.e., enqueue using proxy. + */ + return -1; + } + + /* + * Enqueue using process struct. + */ + *newp &= ~ERTS_PSFLGS_PRQ_PRIO_MASK; + *newp |= ERTS_PSFLG_IN_RUNQ | (aprio << ERTS_PSFLGS_PRQ_PRIO_OFFSET); + return 1; +} + /* * scheduler_out_process() return with c_rq locked. */ static ERTS_INLINE int -schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p) +schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Process *proxy) { - erts_aint32_t a, e, n; + erts_aint32_t a, e, n, enq_prio = -1; int res = 0; + int enqueue; /* < 0 -> use proxy */ a = state; while (1) { n = e = a; - ASSERT(a & ERTS_PSFLG_RUNNING); - ASSERT(!(a & ERTS_PSFLG_IN_RUNQ)); + ASSERT(a & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)); - n &= ~ERTS_PSFLG_RUNNING; - if ((a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) - n |= ERTS_PSFLG_IN_RUNQ; + enqueue = 0; + + n &= ~(ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS); + if (a & ERTS_PSFLG_ACTIVE_SYS + || (a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { + enqueue = check_enqueue_in_prio_queue(&enq_prio, &n, a); + } 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); + if (!enqueue) { + + if (erts_system_profile_flags.runnable_procs) { + + if (!(a & ERTS_PSFLG_ACTIVE_SYS) + && (!(a & ERTS_PSFLG_ACTIVE) + || (a & ERTS_PSFLG_SUSPENDED))) { + /* Process inactive */ + profile_runnable_proc(p, am_inactive); + } + } + + if (proxy) + free_proxy_proc(proxy); } else { - int prio = (int) (ERTS_PSFLG_PRIO_MASK & n); + Process *sched_p; ErtsRunQueue *runq = erts_get_runq_proc(p); - ASSERT(!(n & ERTS_PSFLG_SUSPENDED)); + ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & ERTS_PSFLG_ACTIVE_SYS)); + + if (enqueue < 0) + sched_p = make_proxy_proc(proxy, p, enq_prio); + else { + sched_p = p; + if (proxy) + free_proxy_proc(proxy); + } #ifdef ERTS_SMP if (!(ERTS_PSFLG_BOUND & n)) { - ErtsRunQueue *new_runq = erts_check_emigration_need(runq, prio); + ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio); if (new_runq) { - RUNQ_SET_RQ(&p->run_queue, new_runq); + RUNQ_SET_RQ(&sched_p->run_queue, new_runq); runq = new_runq; } } @@ -4908,7 +5071,7 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p) erts_smp_runq_lock(runq); /* Enqueue the process */ - enqueue_process(runq, prio, p); + enqueue_process(runq, (int) enq_prio, sched_p); if (runq == c_rq) return res; @@ -4920,14 +5083,13 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p) } static ERTS_INLINE void -add2runq(Process *p, erts_aint32_t state) +add2runq(Process *p, erts_aint32_t state, erts_aint32_t prio) { - int prio = (int) (ERTS_PSFLG_PRIO_MASK & state); ErtsRunQueue *runq = erts_get_runq_proc(p); #ifdef ERTS_SMP if (!(ERTS_PSFLG_BOUND & state)) { - ErtsRunQueue *new_runq = erts_check_emigration_need(runq, prio); + ErtsRunQueue *new_runq = erts_check_emigration_need(runq, (int) prio); if (new_runq) { RUNQ_SET_RQ(&p->run_queue, new_runq); runq = new_runq; @@ -4939,101 +5101,236 @@ add2runq(Process *p, erts_aint32_t state) erts_smp_runq_lock(runq); /* Enqueue the process */ - enqueue_process(runq, prio, p); + enqueue_process(runq, (int) 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) +static ERTS_INLINE int +change_proc_schedule_state(Process *p, + erts_aint32_t clear_state_flags, + erts_aint32_t set_state_flags, + erts_aint32_t *statep, + erts_aint32_t *enq_prio_p) { - erts_aint32_t a = state, n; + /* + * NOTE: ERTS_PSFLG_RUNNING, ERTS_PSFLG_RUNNING_SYS and + * ERTS_PSFLG_ACTIVE_SYS are not allowed to be + * altered by this function! + */ + erts_aint32_t a = *statep, n; + int enqueue; /* < 0 -> use proxy */ + + ASSERT(!(a & ERTS_PSFLG_PROXY)); + ASSERT((clear_state_flags & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_ACTIVE_SYS)) == 0); + ASSERT((set_state_flags & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_ACTIVE_SYS)) == 0); while (1) { erts_aint32_t e; n = e = a; + enqueue = 0; + 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; + break; /* We don't want to schedule free processes... */ + + if (clear_state_flags) + n &= ~clear_state_flags; + + if (set_state_flags) + n |= set_state_flags; + + if ((n & (ERTS_PSFLG_SUSPENDED + | ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_IN_RUNQ + | ERTS_PSFLG_ACTIVE)) == ERTS_PSFLG_ACTIVE) { + /* + * Active and seemingly need to be enqueued, but + * process may be in a run queue via proxy, need + * further inspection... + */ + enqueue = check_enqueue_in_prio_queue(enq_prio_p, &n, a); + } + 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 (enqueue == 0 && n == a) + break; } - if (erts_system_profile_flags.runnable_procs - && !(a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED))) { - profile_runnable_proc(p, am_active); + if (erts_system_profile_flags.runnable_procs) { + + if (((n & (ERTS_PSFLG_SUSPENDED + | ERTS_PSFLG_ACTIVE)) == ERTS_PSFLG_ACTIVE) + && (!(a & (ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS) + && (!(a & ERTS_PSFLG_ACTIVE) + || (a & ERTS_PSFLG_SUSPENDED))))) { + /* We activated a prevously inactive process */ + profile_runnable_proc(p, am_active); + } + } - if ((n & ERTS_PSFLG_IN_RUNQ) && !(a & ERTS_PSFLG_IN_RUNQ)) - add2runq(p, n); + *statep = a; + + return enqueue; +} + +static ERTS_INLINE void +schedule_process(Process *p, erts_aint32_t in_state) +{ + erts_aint32_t enq_prio = -1; + erts_aint32_t state = in_state; + int enqueue = change_proc_schedule_state(p, + 0, + ERTS_PSFLG_ACTIVE, + &state, + &enq_prio); + if (enqueue) + add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), + state, + enq_prio); } void erts_schedule_process(Process *p, erts_aint32_t state) { - schedule_process(p, state, 0); + schedule_process(p, state); +} + +static void +schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) +{ + erts_aint32_t a = state, n, enq_prio = -1; + int enqueue; /* < 0 -> use proxy */ + + ASSERT(!(state & ERTS_PSFLG_PROXY)); + + while (1) { + erts_aint32_t e; + n = e = a; + + if (a & ERTS_PSFLG_FREE) + return; /* We don't want to schedule free processes... */ + + enqueue = 0; + n |= ERTS_PSFLG_ACTIVE_SYS; + if (!(a & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))) + enqueue = check_enqueue_in_prio_queue(&enq_prio, &n, a); + a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); + if (a == e) + break; + if (a == n && !enqueue) + goto cleanup; + } + + if (erts_system_profile_flags.runnable_procs) { + + if (!(a & (ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS)) + && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) { + /* We activated a prevously inactive process */ + profile_runnable_proc(p, am_active); + } + + } + + if (enqueue) { + Process *sched_p; + if (enqueue > 0) + sched_p = p; + else { + sched_p = make_proxy_proc(proxy, p, enq_prio); + proxy = NULL; + } + add2runq(sched_p, n, enq_prio); + } + +cleanup: + if (proxy) + free_proxy_proc(proxy); } static ERTS_INLINE int suspend_process(Process *c_p, Process *p) { - erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state); + erts_aint32_t state; int suspended = 0; ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); + state = erts_smp_atomic32_read_acqb(&p->state); + 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; + suspended = (state & ERTS_PSFLG_SUSPENDED) ? -1: 1; } else { while (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_EXITING))) { - erts_aint32_t e, n; + erts_aint32_t n, e; + 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) { + suspended = -1; + break; + } } } } - if (state & ERTS_PSFLG_SUSPENDED) { + if (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); + if (suspended > 0 && erts_system_profile_flags.runnable_procs) { + + /* 'state' is before our change... */ + + if ((state & (ERTS_PSFLG_ACTIVE + | ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { + /* We made process inactive */ + 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_aint32_t state, enq_prio = -1; + int enqueue; + ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); ASSERT(p->rcount > 0); @@ -5041,14 +5338,16 @@ resume_process(Process *p) if (--p->rcount > 0) /* multiple suspend */ return; - 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); - } + state = erts_smp_atomic32_read_nob(&p->state); + enqueue = change_proc_schedule_state(p, + ERTS_PSFLG_SUSPENDED, + 0, + &state, + &enq_prio); + if (enqueue) + add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), + state, + enq_prio); } int @@ -6032,7 +6331,8 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, goto done; } else { - if (!(ERTS_PSFLG_RUNNING & erts_smp_atomic32_read_acqb(&rp->state))) + if (!((ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS) + & erts_smp_atomic32_read_acqb(&rp->state))) goto done; } @@ -6695,7 +6995,7 @@ Eterm erts_get_process_priority(Process *p) { erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state); - switch (state & ERTS_PSFLG_PRIO_MASK) { + switch (ERTS_PSFLGS_GET_USR_PRIO(state)) { case PRIORITY_MAX: return am_max; case PRIORITY_HIGH: return am_high; case PRIORITY_NORMAL: return am_normal; @@ -6718,18 +7018,60 @@ erts_set_process_priority(Process *p, Eterm value) } a = erts_smp_atomic32_read_nob(&p->state); - if (nprio == (a & ERTS_PSFLG_PRIO_MASK)) + if (nprio == ERTS_PSFLGS_GET_USR_PRIO(a)) oprio = nprio; else { - erts_aint32_t e, n; + int slocked = 0; + erts_aint32_t e, n, aprio; + + if (a & ERTS_PSFLG_ACTIVE_SYS) { + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + slocked = 1; + } + do { - oprio = a & ERTS_PSFLG_PRIO_MASK; + oprio = ERTS_PSFLGS_GET_USR_PRIO(a); n = e = a; - ASSERT(!(a & ERTS_PSFLG_IN_RUNQ)); + if (!(a & ERTS_PSFLG_ACTIVE_SYS)) + aprio = nprio; + else { + int max_qbit; + + if (!slocked) { + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + slocked = 1; + } + + max_qbit = p->sys_task_qs->qmask; + max_qbit &= -max_qbit; + switch (max_qbit) { + case MAX_BIT: + aprio = PRIORITY_MAX; + break; + case HIGH_BIT: + aprio = PRIORITY_HIGH; + break; + case NORMAL_BIT: + aprio = PRIORITY_NORMAL; + break; + case LOW_BIT: + aprio = PRIORITY_LOW; + break; + default: + ERTS_INTERNAL_ERROR("Invalid qmask"); + aprio = -1; + } + + if (aprio > nprio) /* low value -> high prio */ + aprio = nprio; + } + + n &= ~(ERTS_PSFLGS_USR_PRIO_MASK + | ERTS_PSFLGS_ACT_PRIO_MASK); + n |= ((nprio << ERTS_PSFLGS_USR_PRIO_OFFSET) + | (aprio << ERTS_PSFLGS_ACT_PRIO_OFFSET)); - n &= ~ERTS_PSFLG_PRIO_MASK; - n |= nprio; a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); } while (a != e); } @@ -6763,6 +7105,7 @@ erts_set_process_priority(Process *p, Eterm value) Process *schedule(Process *p, int calls) { + Process *proxy_p = NULL; ErtsRunQueue *rq; erts_aint_t dt; ErtsSchedulerData *esdp; @@ -6805,6 +7148,8 @@ Process *schedule(Process *p, int calls) actual_reds = reds = 0; erts_smp_runq_lock(rq); } else { + sched_out_proc: + #ifdef ERTS_SMP ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); esdp = p->scheduler_data; @@ -6858,10 +7203,11 @@ Process *schedule(Process *p, int calls) esdp->reductions += reds; - schedule_out_process(rq, state, p); /* Returns with rq locked! */ + schedule_out_process(rq, state, p, proxy_p); /* Returns with rq locked! */ + proxy_p = NULL; ERTS_PROC_REDUCTIONS_EXECUTED(rq, - (int) (state & ERTS_PSFLG_PRIO_MASK), + (int) ERTS_PSFLGS_GET_USR_PRIO(state), reds, actual_reds); @@ -6870,18 +7216,19 @@ Process *schedule(Process *p, int calls) p->scheduler_data = NULL; #endif + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS); if (state & ERTS_PSFLG_FREE) { #ifdef ERTS_SMP ASSERT(esdp->free_process == p); esdp->free_process = NULL; -#else - erts_free_proc(p); +#else + state = erts_smp_atomic32_read_nob(&p->state); + if (!(state & ERTS_PSFLG_IN_RUNQ)) + erts_free_proc(p); #endif } - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS); - #ifdef ERTS_SMP ASSERT(!esdp->free_process); #endif @@ -7088,6 +7435,8 @@ Process *schedule(Process *p, int calls) * Find a new process to run. */ pick_next_process: { + erts_aint32_t psflg_band_mask; + erts_aint32_t running_flag; int prio_q; int qmask; @@ -7121,18 +7470,62 @@ Process *schedule(Process *p, int calls) ASSERT(p); /* Wrong qmask in rq->flags? */ + psflg_band_mask = ~(((erts_aint32_t) 1) << (ERTS_PSFLGS_GET_PRQ_PRIO(state) + + ERTS_PSFLGS_IN_PRQ_MASK_OFFSET)); + + if (!(state & ERTS_PSFLG_PROXY)) + psflg_band_mask &= ~ERTS_PSFLG_IN_RUNQ; + else { + proxy_p = p; + p = erts_proc_lookup_raw(proxy_p->common.id); + if (!p) { + free_proxy_proc(proxy_p); + proxy_p = NULL; + goto pick_next_process; + } + state = erts_smp_atomic32_read_nob(&p->state); + } + + + if (state & ERTS_PSFLG_ACTIVE_SYS) + running_flag = ERTS_PSFLG_RUNNING_SYS; + else + running_flag = ERTS_PSFLG_RUNNING; + 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; + new &= psflg_band_mask; + if (!(state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS))) { + tmp = state & (ERTS_PSFLG_SUSPENDED + | ERTS_PSFLG_PENDING_EXIT + | ERTS_PSFLG_ACTIVE_SYS); + if (tmp != ERTS_PSFLG_SUSPENDED) + new |= running_flag; + } 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) + if ((state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_FREE)) + || ((state & (ERTS_PSFLG_SUSPENDED + | ERTS_PSFLG_PENDING_EXIT + | ERTS_PSFLG_ACTIVE_SYS)) + == ERTS_PSFLG_SUSPENDED)) { + if (state & ERTS_PSFLG_FREE) { +#ifdef ERTS_SMP + erts_smp_proc_dec_refc(p); +#else + erts_free_proc(p); +#endif + } + if (proxy_p) { + free_proxy_proc(proxy_p); + proxy_p = NULL; + } goto pick_next_process; + } state = new; break; } @@ -7162,7 +7555,7 @@ Process *schedule(Process *p, int calls) (UWord) esdp->no); int migrated = old && old != esdp->no; - prio = (int) (state & ERTS_PSFLG_PRIO_MASK); + prio = (int) ERTS_PSFLGS_GET_USR_PRIO(state); erts_smp_spin_lock(&erts_sched_stat.lock); erts_sched_stat.prio[prio].total_executed++; @@ -7182,9 +7575,6 @@ Process *schedule(Process *p, int calls) ASSERT(!p->scheduler_data); p->scheduler_data = esdp; #endif - /* Never run a suspended process */ - ASSERT(!(ERTS_PSFLG_SUSPENDED & erts_smp_atomic32_read_nob(&p->state))); - reds = context_reds; if (IS_TRACED(p)) { @@ -7210,21 +7600,574 @@ Process *schedule(Process *p, int calls) erts_check_my_tracer_proc(p); #endif + if (state & ERTS_PSFLG_RUNNING_SYS) { + reds -= execute_sys_tasks(p, &state, reds); + if (reds <= 0) { + p->fcalls = reds; + goto sched_out_proc; + } + + ASSERT(state & ERTS_PSFLG_RUNNING_SYS); + ASSERT(!(state & ERTS_PSFLG_RUNNING)); + + while (1) { + erts_aint32_t n, e; + + if (((state & (ERTS_PSFLG_SUSPENDED + | ERTS_PSFLG_ACTIVE)) != ERTS_PSFLG_ACTIVE) + && !(state & ERTS_PSFLG_EXITING)) + goto sched_out_proc; + + n = e = state; + n &= ~ERTS_PSFLG_RUNNING_SYS; + n |= ERTS_PSFLG_RUNNING; + + state = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); + if (state == e) { + state = n; + break; + } + + ASSERT(state & ERTS_PSFLG_RUNNING_SYS); + ASSERT(!(state & ERTS_PSFLG_RUNNING)); + } + } + if (!(state & ERTS_PSFLG_EXITING) && ((FLAGS(p) & F_FORCE_GC) || (MSO(p).overhead > BIN_VHEAP_SZ(p)))) { reds -= erts_garbage_collect(p, 0, p->arg_reg, p->arity); - if (reds < 0) { - reds = 1; + if (reds <= 0) { + p->fcalls = reds; + goto sched_out_proc; } } + + if (proxy_p) { + free_proxy_proc(proxy_p); + proxy_p = NULL; + } p->fcalls = reds; ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); + + /* Never run a suspended process */ + ASSERT(!(ERTS_PSFLG_SUSPENDED & erts_smp_atomic32_read_nob(&p->state))); + return p; } } +static int +notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result) +{ + Process *rp = erts_proc_lookup(st->requester); + if (rp) { + ErtsProcLocks rp_locks; + ErlOffHeap *ohp; + ErlHeapFragment* bp; + Eterm *hp, msg, req_id, result; + Uint st_result_sz, hsz; +#ifdef DEBUG + Eterm *hp_start; +#endif + + rp_locks = (c_p == rp) ? ERTS_PROC_LOCK_MAIN : 0; + + st_result_sz = is_immed(st_result) ? 0 : size_object(st_result); + hsz = st->req_id_sz + st_result_sz + 4 /* 3-tuple */; + + hp = erts_alloc_message_heap(hsz, + &bp, + &ohp, + rp, + &rp_locks); + +#ifdef DEBUG + hp_start = hp; +#endif + + req_id = st->req_id_sz == 0 ? st->req_id : copy_struct(st->req_id, + st->req_id_sz, + &hp, + ohp); + + result = st_result_sz == 0 ? st_result : copy_struct(st_result, + st_result_sz, + &hp, + ohp); + + ASSERT(is_immed(st->reply_tag)); + + msg = TUPLE3(hp, st->reply_tag, req_id, result); + +#ifdef DEBUG + hp += 4; + ASSERT(hp_start + hsz == hp); +#endif + + erts_queue_message(rp, + &rp_locks, + bp, + msg, + NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); + + if (c_p == rp) + rp_locks &= ~ERTS_PROC_LOCK_MAIN; + + if (rp_locks) + erts_smp_proc_unlock(rp, rp_locks); + } + + erts_cleanup_offheap(&st->off_heap); + + erts_free(ERTS_ALC_T_PROC_SYS_TSK, st); + + return rp ? 1 : 0; +} + +static ERTS_INLINE ErtsProcSysTask * +fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp) +{ + ErtsProcSysTaskQs *unused_qs = NULL; + int qbit, qmask; + ErtsProcSysTask *st, **qp; + + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS); + + if (!c_p->sys_task_qs) { + qmask = 0; + st = NULL; + goto update_state; + } + + qmask = c_p->sys_task_qs->qmask; + + if ((state & (ERTS_PSFLG_ACTIVE + | ERTS_PSFLG_EXITING + | ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { + /* No sys tasks if we got exclusively higher prio user work to do */ + st = NULL; + switch (ERTS_PSFLGS_GET_USR_PRIO(state)) { + case PRIORITY_MAX: + if (!(qmask & MAX_BIT)) + goto done; + break; + case PRIORITY_HIGH: + if (!(qmask & (MAX_BIT|HIGH_BIT))) + goto done; + break; + default: + break; + } + } + + qbit = qmask & -qmask; + switch (qbit) { + case MAX_BIT: + qp = &c_p->sys_task_qs->q[PRIORITY_MAX]; + break; + case HIGH_BIT: + qp = &c_p->sys_task_qs->q[PRIORITY_HIGH]; + break; + case NORMAL_BIT: + if (!(qmask & PRIORITY_LOW) + || ++c_p->sys_task_qs->ncount <= RESCHEDULE_LOW) { + qp = &c_p->sys_task_qs->q[PRIORITY_NORMAL]; + break; + } + c_p->sys_task_qs->ncount = 0; + /* Fall through */ + case LOW_BIT: + qp = &c_p->sys_task_qs->q[PRIORITY_LOW]; + break; + default: + ERTS_INTERNAL_ERROR("Invalid qmask"); + } + + st = *qp; + ASSERT(st); + if (st->next != st) { + *qp = st->next; + st->next->prev = st->prev; + st->prev->next = st->next; + } + else { + erts_aint32_t a, e, n, st_prio, qmask2; + + *qp = NULL; + qmask &= ~qbit; + c_p->sys_task_qs->qmask = qmask; + + update_state: + + qmask2 = qmask; + + switch (qmask2 & -qmask2) { + case MAX_BIT: + st_prio = PRIORITY_MAX; + break; + case HIGH_BIT: + st_prio = PRIORITY_HIGH; + break; + case NORMAL_BIT: + st_prio = PRIORITY_NORMAL; + break; + case LOW_BIT: + st_prio = PRIORITY_LOW; + break; + case 0: + st_prio = PRIORITY_LOW; + unused_qs = c_p->sys_task_qs; + c_p->sys_task_qs = NULL; + break; + default: + ERTS_INTERNAL_ERROR("Invalid qmask"); + } + + a = state; + do { + erts_aint32_t prio = ERTS_PSFLGS_GET_USR_PRIO(a); + + if (prio > st_prio) + prio = st_prio; + + n = e = a; + + n &= ~ERTS_PSFLGS_ACT_PRIO_MASK; + n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET); + + if (!qmask) + n &= ~ERTS_PSFLG_ACTIVE_SYS; + + if (a == n) + break; + a = erts_smp_atomic32_cmpxchg_nob(&c_p->state, n, e); + } while (a != e); + } + +done: + + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS); + + if (unused_qs) + proc_sys_task_queues_free(unused_qs); + + *qmaskp = qmask; + + return st; +} + +static int +execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) +{ + int garbage_collected = 0; + erts_aint32_t state = *statep; + int max_reds = in_reds; + int reds = 0; + int qmask = 0; + + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN); + + do { + ErtsProcSysTask *st; + Eterm st_res; + + if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) { +#ifdef ERTS_SMP + if (state & ERTS_PSFLG_PENDING_EXIT) + erts_handle_pending_exit(c_p, ERTS_PROC_LOCK_MAIN); +#endif + ASSERT(ERTS_PROC_IS_EXITING(c_p)); + break; + } + + st = fetch_sys_task(c_p, state, &qmask); + if (!st) + break; + + switch (st->type) { + case ERTS_PSTT_GC: + if (!garbage_collected) { + FLAGS(c_p) |= F_NEED_FULLSWEEP; + reds += erts_garbage_collect(c_p, 0, c_p->arg_reg, c_p->arity); + garbage_collected = 1; + } + st_res = am_true; + break; + case ERTS_PSTT_CPC: + st_res = erts_check_process_code(c_p, + st->arg[0], + st->arg[1] == am_true, + &reds); + break; + default: + ERTS_INTERNAL_ERROR("Invalid process sys task type"); + st_res = am_false; + } + + if (st) + reds += notify_sys_task_executed(c_p, st, st_res); + + state = erts_smp_atomic32_read_acqb(&c_p->state); + } while (qmask && reds < max_reds); + + *statep = state; + + return reds; +} + +static int +cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds) +{ + erts_aint32_t state = in_state; + int max_reds = in_reds; + int reds = 0; + int qmask = 0; + + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN); + + do { + ErtsProcSysTask *st; + Eterm st_res; + + st = fetch_sys_task(c_p, state, &qmask); + if (!st) + break; + + switch (st->type) { + case ERTS_PSTT_GC: + st_res = am_false; + break; + case ERTS_PSTT_CPC: + st_res = am_false; + break; + default: + ERTS_INTERNAL_ERROR("Invalid process sys task type"); + st_res = am_false; + break; + } + + reds += notify_sys_task_executed(c_p, st, st_res); + + state = erts_smp_atomic32_read_acqb(&c_p->state); + } while (qmask && reds < max_reds); + + return reds; +} + +BIF_RETTYPE +erts_internal_request_system_task_3(BIF_ALIST_3) +{ + Process *rp = erts_proc_lookup(BIF_ARG_1); + ErtsProcSysTaskQs *stqs, *free_stqs = NULL; + ErtsProcSysTask *st = NULL; + erts_aint32_t prio, rp_state; + int rp_locked; + Eterm noproc_res, req_type; + + if (!rp && !is_internal_pid(BIF_ARG_1)) { + if (!is_external_pid(BIF_ARG_1)) + goto badarg; + if (external_pid_dist_entry(BIF_ARG_1) != erts_this_dist_entry) + goto badarg; + } + + switch (BIF_ARG_2) { + case am_max: prio = PRIORITY_MAX; break; + case am_high: prio = PRIORITY_HIGH; break; + case am_normal: prio = PRIORITY_NORMAL; break; + case am_low: prio = PRIORITY_LOW; break; + default: goto badarg; + } + + if (is_not_tuple(BIF_ARG_3)) + goto badarg; + else { + int i; + Eterm *tp = tuple_val(BIF_ARG_3); + Uint arity = arityval(*tp); + Eterm req_id; + Uint req_id_sz; + Eterm arg[ERTS_MAX_PROC_SYS_TASK_ARGS]; + Uint arg_sz[ERTS_MAX_PROC_SYS_TASK_ARGS]; + Uint tot_sz; + Eterm *hp; + + if (arity < 2) + goto badarg; + if (arity > 2 + ERTS_MAX_PROC_SYS_TASK_ARGS) + goto badarg; + req_type = tp[1]; + req_id = tp[2]; + req_id_sz = is_immed(req_id) ? req_id : size_object(req_id); + tot_sz = req_id_sz; + for (i = 0; i < ERTS_MAX_PROC_SYS_TASK_ARGS; i++) { + int tix = 3 + i; + if (tix > arity) { + arg[i] = THE_NON_VALUE; + arg_sz[i] = 0; + } + else { + arg[i] = tp[tix]; + if (is_immed(arg[i])) + arg_sz[i] = 0; + else { + arg_sz[i] = size_object(arg[i]); + tot_sz += arg_sz[i]; + } + } + } + st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK, + ERTS_PROC_SYS_TASK_SIZE(tot_sz)); + st->next = st->prev = st; /* Prep for empty prio queue */ + ERTS_INIT_OFF_HEAP(&st->off_heap); + hp = &st->heap[0]; + + st->requester = BIF_P->common.id; + st->reply_tag = req_type; + st->req_id_sz = req_id_sz; + st->req_id = req_id_sz == 0 ? req_id : copy_struct(req_id, + req_id_sz, + &hp, + &st->off_heap); + + for (i = 0; i < ERTS_MAX_PROC_SYS_TASK_ARGS; i++) + st->arg[i] = arg_sz[i] == 0 ? arg[i] : copy_struct(arg[i], + arg_sz[i], + &hp, + &st->off_heap); + ASSERT(&st->heap[0] + tot_sz == hp); + } + + switch (req_type) { + + case am_garbage_collect: + st->type = ERTS_PSTT_GC; + noproc_res = am_false; + if (!rp) + goto noproc; + break; + + case am_check_process_code: + if (is_not_atom(st->arg[0])) + goto badarg; + if (st->arg[1] != am_true && st->arg[1] != am_false) + goto badarg; + noproc_res = am_false; + st->type = ERTS_PSTT_CPC; + if (!rp) + goto noproc; + break; + + default: + goto badarg; + } + + rp_state = erts_smp_atomic32_read_nob(&rp->state); + + rp_locked = 0; + + free_stqs = NULL; + if (rp_state & ERTS_PSFLG_ACTIVE_SYS) + stqs = NULL; + else { + alloc_qs: + stqs = proc_sys_task_queues_alloc(); + stqs->qmask = 1 << prio; + stqs->ncount = 0; + stqs->q[PRIORITY_MAX] = NULL; + stqs->q[PRIORITY_HIGH] = NULL; + stqs->q[PRIORITY_NORMAL] = NULL; + stqs->q[PRIORITY_LOW] = NULL; + stqs->q[prio] = st; + } + + if (!rp_locked) { + rp_locked = 1; + erts_smp_proc_lock(rp, ERTS_PROC_LOCK_STATUS); + + rp_state = erts_smp_atomic32_read_nob(&rp->state); + if (rp_state & ERTS_PSFLG_EXITING) { + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); + rp = NULL; + free_stqs = stqs; + goto noproc; + } + } + + if (!rp->sys_task_qs) { + if (stqs) + rp->sys_task_qs = stqs; + else + goto alloc_qs; + } + else { + if (stqs) + free_stqs = stqs; + stqs = rp->sys_task_qs; + if (!stqs->q[prio]) { + stqs->q[prio] = st; + stqs->qmask |= 1 << prio; + } + else { + st->next = stqs->q[prio]; + st->prev = stqs->q[prio]->prev; + st->next->prev = st; + st->prev->next = st; + ASSERT(stqs->qmask & (1 << prio)); + } + } + + if (ERTS_PSFLGS_GET_ACT_PRIO(rp_state) > prio) { + erts_aint32_t n, a, e; + /* Need to elevate actual prio */ + + a = rp_state; + do { + if (ERTS_PSFLGS_GET_ACT_PRIO(a) <= prio) { + n = a; + break; + } + n = e = a; + n &= ~ERTS_PSFLGS_ACT_PRIO_MASK; + n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET); + a = erts_smp_atomic32_cmpxchg_nob(&rp->state, n, e); + } while (a != e); + rp_state = n; + } + + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); + + schedule_process_sys_task(rp, rp_state, NULL); + + if (free_stqs) + proc_sys_task_queues_free(free_stqs); + + BIF_RET(am_ok); + +noproc: + + notify_sys_task_executed(BIF_P, st, noproc_res); + if (free_stqs) + proc_sys_task_queues_free(free_stqs); + BIF_RET(am_ok); + +badarg: + + if (st) { + erts_cleanup_offheap(&st->off_heap); + erts_free(ERTS_ALC_T_PROC_SYS_TSK, st); + } + if (free_stqs) + proc_sys_task_queues_free(free_stqs); + BIF_ERROR(BIF_P, BADARG); +} + void erts_sched_stat_modify(int what) { @@ -7520,7 +8463,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). prio = (erts_aint32_t) so->priority; } - state |= (prio & ERTS_PSFLG_PRIO_MASK); + state |= (((prio & ERTS_PSFLGS_PRIO_MASK) << ERTS_PSFLGS_ACT_PRIO_OFFSET) + | ((prio & ERTS_PSFLGS_PRIO_MASK) << ERTS_PSFLGS_USR_PRIO_OFFSET)); if (!rq) rq = erts_get_runq_proc(parent); @@ -7599,6 +8543,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->bin_old_vheap = 0; p->bin_vheap_mature = 0; + p->sys_task_qs = NULL; + /* No need to initialize p->fcalls. */ p->current = p->initial+INITIAL_MOD; @@ -7764,7 +8710,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). * Schedule process for execution. */ - schedule_process(p, state, 0); + schedule_process(p, state); VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->common.id)); @@ -7815,6 +8761,7 @@ void erts_init_empty_process(Process *p) p->bin_vheap_sz = BIN_VH_MIN_SIZE; p->bin_old_vheap_sz = BIN_VH_MIN_SIZE; p->bin_old_vheap = 0; + p->sys_task_qs = NULL; p->bin_vheap_mature = 0; #ifdef ERTS_SMP p->common.u.alive.ptimer = NULL; @@ -8070,33 +9017,21 @@ 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, - erts_aint32_t state, + erts_aint32_t in_state, Eterm reason, ErlHeapFragment *bp) { + erts_aint32_t state = in_state, enq_prio = -1; + int enqueue; ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) == ERTS_PROC_LOCKS_ALL); - state = set_proc_exiting_state(p, state); + enqueue = change_proc_schedule_state(p, + ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT, + ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE, + &state, + &enq_prio); p->fvalue = reason; if (bp) @@ -8111,15 +9046,37 @@ set_proc_exiting(Process *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); + if (enqueue) + add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), + state, + enq_prio); } +static ERTS_INLINE erts_aint32_t +set_proc_self_exiting(Process *c_p) +{ +#ifdef DEBUG + int enqueue; +#endif + erts_aint32_t state, enq_prio = -1; + + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCKS_ALL); + + state = erts_smp_atomic32_read_nob(&c_p->state); + ASSERT(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)); + +#ifdef DEBUG + enqueue = +#endif + change_proc_schedule_state(c_p, + ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT, + ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE, + &state, + &enq_prio); + + ASSERT(!enqueue); + return state; +} #ifdef ERTS_SMP @@ -8167,7 +9124,7 @@ handle_pending_exiters(ErtsProcList *pnd_xtrs) if (p) { if (erts_proclist_same(plp, p)) { erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state); - if (!(state & ERTS_PSFLG_RUNNING)) { + if (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))) { ASSERT(state & ERTS_PSFLG_PENDING_EXIT); erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL); } @@ -8388,7 +9345,7 @@ send_exit_signal(Process *c_p, /* current process if and only } set_proc_exiting(c_p, state, rsn, NULL); } - else if (!(state & ERTS_PSFLG_RUNNING)) { + else if (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))) { /* Process not running ... */ ErtsProcLocks need_locks = ~(*rp_locks) & ERTS_PROC_LOCKS_ALL; if (need_locks @@ -8765,9 +9722,6 @@ resume_suspend_monitor(ErtsSuspendMonitor *smon, void *vc_p) void erts_do_exit_process(Process* p, Eterm reason) { -#ifdef ERTS_SMP - erts_aint32_t state; -#endif p->arity = 0; /* No live registers */ p->fvalue = reason; @@ -8792,10 +9746,9 @@ erts_do_exit_process(Process* p, Eterm reason) #endif #ifndef ERTS_SMP - set_proc_exiting_state(p, erts_smp_atomic32_read_nob(&p->state)); + set_proc_self_exiting(p); #else - state = set_proc_exiting_state(p, erts_smp_atomic32_read_nob(&p->state)); - if (state & ERTS_PSFLG_PENDING_EXIT) { + if (ERTS_PSFLG_PENDING_EXIT & set_proc_self_exiting(p)) { /* Process exited before pending exit was received... */ p->pending_exit.reason = THE_NON_VALUE; if (p->pending_exit.bp) { @@ -8849,6 +9802,7 @@ erts_continue_exit_process(Process *p) DistEntry *dep; struct saved_calls *scb; process_breakpoint_time_t *pbt; + erts_aint32_t state; #ifdef DEBUG int yield_allowed = 1; @@ -8885,6 +9839,12 @@ erts_continue_exit_process(Process *p) p->flags &= ~F_USING_DB; } + state = erts_smp_atomic32_read_acqb(&p->state); + if (state & ERTS_PSFLG_ACTIVE_SYS) { + if (cleanup_sys_tasks(p, state, CONTEXT_REDS) >= CONTEXT_REDS/2) + goto yield; + } + if (p->flags & F_USING_DDLL) { erts_ddll_proc_dead(p, ERTS_PROC_LOCK_MAIN); p->flags &= ~F_USING_DDLL; @@ -8962,17 +9922,31 @@ erts_continue_exit_process(Process *p) { /* Inactivate and notify free */ erts_aint32_t n, e, a = erts_smp_atomic32_read_nob(&p->state); +#ifdef ERTS_SMP + int refc_inced = 0; +#endif while (1) { n = e = a; ASSERT(a & ERTS_PSFLG_EXITING); n |= ERTS_PSFLG_FREE; n &= ~ERTS_PSFLG_ACTIVE; +#ifdef ERTS_SMP + if ((n & ERTS_PSFLG_IN_RUNQ) && !refc_inced) { + erts_smp_proc_inc_refc(p); + refc_inced = 1; + } +#endif a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); if (a == e) break; } - } +#ifdef ERTS_SMP + if (refc_inced && !(n & ERTS_PSFLG_IN_RUNQ)) + erts_smp_proc_dec_refc(p); +#endif + } + dep = ((p->flags & F_DISTRIBUTION) ? ERTS_PROC_SET_DIST_ENTRY(p, ERTS_PROC_LOCKS_ALL, NULL) : NULL); @@ -9075,7 +10049,7 @@ timeout_proc(Process* p) state = erts_smp_atomic32_read_acqb(&p->state); if (!(state & ERTS_PSFLG_ACTIVE)) - schedule_process(p, state, 0); + schedule_process(p, state); } @@ -9153,7 +10127,9 @@ erts_program_counter_info(int to, void *to_arg, Process *p) print_function_from_pc(to, to_arg, p->cp); erts_print(to, to_arg, ")\n"); state = erts_smp_atomic32_read_acqb(&p->state); - if (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_GC))) { + if (!(state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_GC))) { erts_print(to, to_arg, "arity = %d\n",p->arity); if (!ERTS_IS_CRASH_DUMPING) { /* diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 8e5467f196..814e3d34df 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -688,6 +688,9 @@ typedef struct { ErlHeapFragment *bp; } ErtsPendExit; +typedef struct ErtsProcSysTask_ ErtsProcSysTask; +typedef struct ErtsProcSysTaskQs_ ErtsProcSysTaskQs; + #ifdef ERTS_SMP typedef struct ErtsPendingSuspend_ ErtsPendingSuspend; @@ -855,6 +858,7 @@ struct process { Uint64 bin_old_vheap_sz; /* Virtual old heap block size for binaries */ Uint64 bin_old_vheap; /* Virtual old heap size for binaries */ + ErtsProcSysTaskQs *sys_task_qs; erts_smp_atomic32_t state; /* Process state flags (see ERTS_PSFLG_*) */ #ifdef ERTS_SMP @@ -924,24 +928,66 @@ void erts_check_for_holes(Process* p); # error "Need to increase ERTS_PSFLG_PRIO_SHIFT" #endif -#define ERTS_PSFLG_PRIO_SHIFT 2 +#define ERTS_PSFLGS_PRIO_BITS 2 +#define ERTS_PSFLGS_PRIO_MASK \ + ((((erts_aint32_t) 1) << ERTS_PSFLGS_PRIO_BITS) - 1) -#define ERTS_PSFLG_BIT(N) \ - (((erts_aint32_t) 1) << (ERTS_PSFLG_PRIO_SHIFT + (N))) +#define ERTS_PSFLGS_ACT_PRIO_OFFSET (0*ERTS_PSFLGS_PRIO_BITS) +#define ERTS_PSFLGS_USR_PRIO_OFFSET (1*ERTS_PSFLGS_PRIO_BITS) +#define ERTS_PSFLGS_PRQ_PRIO_OFFSET (2*ERTS_PSFLGS_PRIO_BITS) +#define ERTS_PSFLGS_ZERO_BIT_OFFSET (3*ERTS_PSFLGS_PRIO_BITS) -#define ERTS_PSFLG_PRIO_MASK (ERTS_PSFLG_BIT(0) - 1) +#define ERTS_PSFLGS_QMASK_BITS 4 +#define ERTS_PSFLGS_QMASK \ + ((((erts_aint32_t) 1) << ERTS_PSFLGS_QMASK_BITS) - 1) +#define ERTS_PSFLGS_IN_PRQ_MASK_OFFSET \ + ERTS_PSFLGS_ZERO_BIT_OFFSET -#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) +#define ERTS_PSFLG_BIT(N) \ + (((erts_aint32_t) 1) << (ERTS_PSFLGS_ZERO_BIT_OFFSET + (N))) +/* + * ACT_PRIO -> Active prio, i.e., currently active prio. This + * prio may be higher than user prio. + * USR_PRIO -> User prio. i.e., prio the user has set. + * PRQ_PRIO -> Prio queue prio, i.e., prio queue currently + * enqueued in. + */ +#define ERTS_PSFLGS_ACT_PRIO_MASK \ + (ERTS_PSFLGS_PRIO_MASK << ERTS_PSFLGS_ACT_PRIO_OFFSET) +#define ERTS_PSFLGS_USR_PRIO_MASK \ + (ERTS_PSFLGS_PRIO_MASK << ERTS_PSFLGS_USR_PRIO_OFFSET) +#define ERTS_PSFLGS_PRQ_PRIO_MASK \ + (ERTS_PSFLGS_PRIO_MASK << ERTS_PSFLGS_PRQ_PRIO_OFFSET) +#define ERTS_PSFLG_IN_PRQ_MAX ERTS_PSFLG_BIT(0) +#define ERTS_PSFLG_IN_PRQ_HIGH ERTS_PSFLG_BIT(1) +#define ERTS_PSFLG_IN_PRQ_NORMAL ERTS_PSFLG_BIT(2) +#define ERTS_PSFLG_IN_PRQ_LOW ERTS_PSFLG_BIT(3) +#define ERTS_PSFLG_FREE ERTS_PSFLG_BIT(4) +#define ERTS_PSFLG_EXITING ERTS_PSFLG_BIT(5) +#define ERTS_PSFLG_PENDING_EXIT ERTS_PSFLG_BIT(6) +#define ERTS_PSFLG_ACTIVE ERTS_PSFLG_BIT(7) +#define ERTS_PSFLG_IN_RUNQ ERTS_PSFLG_BIT(8) +#define ERTS_PSFLG_RUNNING ERTS_PSFLG_BIT(9) +#define ERTS_PSFLG_SUSPENDED ERTS_PSFLG_BIT(10) +#define ERTS_PSFLG_GC ERTS_PSFLG_BIT(11) +#define ERTS_PSFLG_BOUND ERTS_PSFLG_BIT(12) +#define ERTS_PSFLG_TRAP_EXIT ERTS_PSFLG_BIT(13) +#define ERTS_PSFLG_ACTIVE_SYS ERTS_PSFLG_BIT(14) +#define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15) +#define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16) + +#define ERTS_PSFLGS_IN_PRQ_MASK (ERTS_PSFLG_IN_PRQ_MAX \ + | ERTS_PSFLG_IN_PRQ_HIGH \ + | ERTS_PSFLG_IN_PRQ_NORMAL \ + | ERTS_PSFLG_IN_PRQ_LOW) + +#define ERTS_PSFLGS_GET_ACT_PRIO(PSFLGS) \ + (((PSFLGS) >> ERTS_PSFLGS_ACT_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK) +#define ERTS_PSFLGS_GET_USR_PRIO(PSFLGS) \ + (((PSFLGS) >> ERTS_PSFLGS_USR_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK) +#define ERTS_PSFLGS_GET_PRQ_PRIO(PSFLGS) \ + (((PSFLGS) >> ERTS_PSFLGS_USR_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK) /* The sequential tracing token is a tuple of size 5: * diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index bacd5a5752..885f092aca 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -655,6 +655,10 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg); Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2); +/* beam_bif_load.c */ +Eterm erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp); + + /* beam_load.c */ typedef struct { BeamInstr* current; /* Pointer to: Mod, Name, Arity */ diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 1e5ae46bfa..c29f3f9b1b 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -763,17 +763,17 @@ allocate_init t I y ################################################################# # -# The BIFs erlang:check_process_code/2 must be called like a function, +# The BIFs erts_internal:check_process_code/2 must be called like a function, # to ensure that c_p->i (program counter) is set correctly (an ordinary # BIF call doesn't set it). # -call_ext u==2 Bif=u$bif:erlang:check_process_code/2 => i_call_ext Bif -call_ext_last u==2 Bif=u$bif:erlang:check_process_code/2 D => i_call_ext_last Bif D -call_ext_only u==2 Bif=u$bif:erlang:check_process_code/2 => i_call_ext_only Bif +call_ext u==2 Bif=u$bif:erts_internal:check_process_code/2 => i_call_ext Bif +call_ext_last u==2 Bif=u$bif:erts_internal:check_process_code/2 D => i_call_ext_last Bif D +call_ext_only u==2 Bif=u$bif:erts_internal:check_process_code/2 => i_call_ext_only Bif # -# The BIFs erlang:garbage_collect/0,1 must be called like functions, +# The BIFs erlang:garbage_collect/0 must be called like a function, # to allow them to invoke the garbage collector. (The stack pointer must # be saved and p->arity must be zeroed, which is not done on ordinary BIF calls.) # @@ -782,10 +782,6 @@ call_ext u==0 Bif=u$bif:erlang:garbage_collect/0 => i_call_ext Bif call_ext_last u==0 Bif=u$bif:erlang:garbage_collect/0 D => i_call_ext_last Bif D call_ext_only u==0 Bif=u$bif:erlang:garbage_collect/0 => i_call_ext_only Bif -call_ext u==1 Bif=u$bif:erlang:garbage_collect/1 => i_call_ext Bif -call_ext_last u==1 Bif=u$bif:erlang:garbage_collect/1 D => i_call_ext_last Bif D -call_ext_only u==1 Bif=u$bif:erlang:garbage_collect/1 => i_call_ext_only Bif - # # put/2 and erase/1 must be able to do garbage collection, so we must call # them like functions. diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index bd2be7afca..a5b2cc589c 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1675,7 +1675,7 @@ static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len) 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) + if (state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)) p = NULL; } } diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 764b8d180c..b1fedf4838 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -151,10 +151,9 @@ standard_bif_interface_0(nbif_ports_0, ports_0) * BIFs and primops that may do a GC (change heap limit and walk the native stack). * XXX: erase/1 and put/2 cannot fail */ -gc_bif_interface_2(nbif_check_process_code_2, hipe_check_process_code_2) +gc_bif_interface_2(nbif_erts_internal_check_process_code_2, hipe_erts_internal_check_process_code_2) gc_bif_interface_1(nbif_erase_1, erase_1) gc_bif_interface_0(nbif_garbage_collect_0, garbage_collect_0) -gc_bif_interface_1(nbif_garbage_collect_1, hipe_garbage_collect_1) gc_nofail_primop_interface_1(nbif_gc_1, hipe_gc) gc_bif_interface_2(nbif_put_2, put_2) diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index 1f76268934..7d343dd91e 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -41,8 +41,7 @@ */ /* for -Wmissing-prototypes :-( */ -extern Eterm hipe_check_process_code_2(BIF_ALIST_2); -extern Eterm hipe_garbage_collect_1(BIF_ALIST_1); +extern Eterm hipe_erts_internal_check_process_code_2(BIF_ALIST_2); extern Eterm hipe_show_nstack_1(BIF_ALIST_1); /* Used when a BIF can trigger a stack walk. */ @@ -51,22 +50,12 @@ static __inline__ void hipe_set_narity(Process *p, unsigned int arity) p->hipe.narity = arity; } -Eterm hipe_check_process_code_2(BIF_ALIST_2) +Eterm hipe_erts_internal_check_process_code_2(BIF_ALIST_2) { Eterm ret; hipe_set_narity(BIF_P, 2); - ret = check_process_code_2(BIF_P, BIF__ARGS); - hipe_set_narity(BIF_P, 0); - return ret; -} - -Eterm hipe_garbage_collect_1(BIF_ALIST_1) -{ - Eterm ret; - - hipe_set_narity(BIF_P, 1); - ret = garbage_collect_1(BIF_P, BIF__ARGS); + ret = erts_internal_check_process_code_2(BIF_P, BIF__ARGS); hipe_set_narity(BIF_P, 0); return ret; } diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index e3aae17df4..e66c6f09b6 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -51,7 +51,11 @@ processes_term_proc_list/1, otp_7738_waiting/1, otp_7738_suspended/1, otp_7738_resume/1, - garb_other_running/1]). + garb_other_running/1, + no_priority_inversion/1, + no_priority_inversion2/1, + system_task_blast/1, + system_task_on_suspended/1]). -export([prio_server/2, prio_client/2]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -73,7 +77,8 @@ all() -> bad_register, garbage_collect, process_info_messages, process_flag_badarg, process_flag_heap_size, spawn_opt_heap_size, otp_6237, {group, processes_bif}, - {group, otp_7738}, garb_other_running]. + {group, otp_7738}, garb_other_running, + {group, system_task}]. groups() -> [{t_exit_2, [], @@ -87,7 +92,10 @@ groups() -> processes_gc_trap, processes_term_proc_list]}, {otp_7738, [], [otp_7738_waiting, otp_7738_suspended, - otp_7738_resume]}]. + otp_7738_resume]}, + {system_task, [], + [no_priority_inversion, no_priority_inversion2, + system_task_blast, system_task_on_suspended]}]. init_per_suite(Config) -> A0 = case application:start(sasl) of @@ -2214,6 +2222,154 @@ garb_other_running(Config) when is_list(Config) -> receive {'DOWN', Mon, process, Pid, normal} -> ok end, ok. +no_priority_inversion(Config) when is_list(Config) -> + Prio = process_flag(priority, max), + HTLs = lists:map(fun (_) -> + spawn_opt(fun () -> + tok_loop() + end, + [{priority, high}, monitor, link]) + end, + lists:seq(1, 2*erlang:system_info(schedulers))), + receive after 500 -> ok end, + LTL = spawn_opt(fun () -> + tok_loop() + end, + [{priority, low}, monitor, link]), + false = erlang:check_process_code(element(1, LTL), nonexisting_module), + true = erlang:garbage_collect(element(1, LTL)), + lists:foreach(fun ({P, _}) -> + unlink(P), + exit(P, kill) + end, [LTL | HTLs]), + lists:foreach(fun ({P, M}) -> + receive + {'DOWN', M, process, P, killed} -> + ok + end + end, [LTL | HTLs]), + process_flag(priority, Prio), + ok. + +no_priority_inversion2(Config) when is_list(Config) -> + Prio = process_flag(priority, max), + MTLs = lists:map(fun (_) -> + spawn_opt(fun () -> + tok_loop() + end, + [{priority, max}, monitor, link]) + end, + lists:seq(1, 2*erlang:system_info(schedulers))), + receive after 500 -> ok end, + {PL, ML} = spawn_opt(fun () -> + tok_loop() + end, + [{priority, low}, monitor, link]), + RL = request_gc(PL, low), + RN = request_gc(PL, normal), + RH = request_gc(PL, high), + receive + {garbage_collect, _, _} -> + ?t:fail(unexpected_gc) + after 1000 -> + ok + end, + RM = request_gc(PL, max), + receive + {garbage_collect, RM, true} -> + ok + end, + lists:foreach(fun ({P, _}) -> + unlink(P), + exit(P, kill) + end, MTLs), + lists:foreach(fun ({P, M}) -> + receive + {'DOWN', M, process, P, killed} -> + ok + end + end, MTLs), + receive + {garbage_collect, RH, true} -> + ok + end, + receive + {garbage_collect, RN, true} -> + ok + end, + receive + {garbage_collect, RL, true} -> + ok + end, + unlink(PL), + exit(PL, kill), + receive + {'DOWN', ML, process, PL, killed} -> + ok + end, + process_flag(priority, Prio), + ok. + +request_gc(Pid, Prio) -> + Ref = make_ref(), + erts_internal:request_system_task(Pid, Prio, {garbage_collect, Ref}), + Ref. + +system_task_blast(Config) when is_list(Config) -> + Me = self(), + GCReq = fun () -> + RL = gc_req(Me, 100), + lists:foreach(fun (R) -> + receive + {garbage_collect, R, true} -> + ok + end + end, RL), + exit(it_worked) + end, + HTLs = lists:map(fun (_) -> spawn_monitor(GCReq) end, lists:seq(1, 1000)), + lists:foreach(fun ({P, M}) -> + receive + {'DOWN', M, process, P, it_worked} -> + ok + end + end, HTLs), + ok. + +gc_req(_Pid, 0) -> + []; +gc_req(Pid, N) -> + R0 = request_gc(Pid, low), + R1 = request_gc(Pid, normal), + R2 = request_gc(Pid, high), + R3 = request_gc(Pid, max), + [R0, R1, R2, R3 | gc_req(Pid, N-1)]. + +system_task_on_suspended(Config) when is_list(Config) -> + {P, M} = spawn_monitor(fun () -> + tok_loop() + end), + true = erlang:suspend_process(P), + {status, suspended} = process_info(P, status), + true = erlang:garbage_collect(P), + {status, suspended} = process_info(P, status), + true = erlang:resume_process(P), + false = ({status, suspended} == process_info(P, status)), + exit(P, kill), + receive + {'DOWN', M, process, P, killed} -> + ok + end. + +gc_req(_Pid, 0) -> + []; +gc_req(Pid, N) -> + R0 = erts_internal:request_system_task(Pid, low, garbage_collect), + R1 = erts_internal:request_system_task(Pid, normal, garbage_collect), + R2 = erts_internal:request_system_task(Pid, high, garbage_collect), + R3 = erts_internal:request_system_task(Pid, max, garbage_collect), + [R0, R1, R2, R3 | gc_req(Pid, N-1)]. + %% Internal functions wait_until(Fun) -> diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 54ff7b3e3a..6e3466b8c0 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -1316,49 +1316,99 @@ end define etp-proc-state-int # Args: int # - if ($arg0 & 0xfffff000) + if ($arg0 & 0xff800000) printf "GARBAGE | " end - if ($arg0 & 0x800) + if ($arg0 & 0x400000) + printf "proxy | " + set $proxy_process = 1 + else + set $proxy_process = 0 + end + if ($arg0 & 0x200000) + printf "running-sys | " + end + if ($arg0 & 0x100000) + printf "active-sys | " + end + if ($arg0 & 0x80000) printf "trapping-exit | " end - if ($arg0 & 0x400) + if ($arg0 & 0x40000) printf "bound | " end - if ($arg0 & 0x200) + if ($arg0 & 0x20000) printf "garbage-collecting | " end - if ($arg0 & 0x100) + if ($arg0 & 0x10000) printf "suspended | " end - if ($arg0 & 0x80) + if ($arg0 & 0x8000) printf "running | " end - if ($arg0 & 0x40) + if ($arg0 & 0x4000) printf "in-run-queue | " end - if ($arg0 & 0x20) + if ($arg0 & 0x2000) printf "active | " end - if ($arg0 & 0x10) + if ($arg0 & 0x1000) printf "pending-exit | " end - if ($arg0 & 0x8) + if ($arg0 & 0x800) printf "exiting | " end - if ($arg0 & 0x4) + if ($arg0 & 0x400) printf "free | " end - if ($arg0 & 0x3) == 0 - printf "prio-max\n" + if ($arg0 & 0x200) + printf "in-prq-low | " + end + if ($arg0 & 0x100) + printf "in-prq-normal | " + end + if ($arg0 & 0x80) + printf "in-prq-high | " + end + if ($arg0 & 0x40) + printf "in-prq-max | " + end + if ($arg0 & 0x30) == 0x0 + printf "prq-prio-max | " else - if ($arg0 & 0x3) == 1 - printf "prio-high\n" + if ($arg0 & 0x30) == 0x10 + printf "prq-prio-high | " else - if ($arg0 & 0x3) == 2 - printf "prio-normal\n" + if ($arg0 & 0x30) == 0x20 + printf "prq-prio-normal | " else - printf "prio-low\n" + printf "prq-prio-low | " + end + end + end + if ($arg0 & 0xc) == 0x0 + printf "usr-prio-max | " + else + if ($arg0 & 0xc) == 0x4 + printf "usr-prio-high | " + else + if ($arg0 & 0xc) == 0x8 + printf "usr-prio-normal | " + else + printf "usr-prio-low | " + end + end + end + if ($arg0 & 0x3) == 0x0 + printf "act-prio-max\n" + else + if ($arg0 & 0x3) == 0x1 + printf "act-prio-high\n" + else + if ($arg0 & 0x3) == 0x2 + printf "act-prio-normal\n" + else + printf "act-prio-low\n" end end end @@ -1395,6 +1445,12 @@ define etp-process-info etp-1 $arg0->common.id printf "\n State: " etp-proc-state $arg0 + if $proxy_process != 0 + printf " Pointer: (Process *) %p\n", $arg0 + printf " *** PROXY process struct *** refer to: \n" + etp-pid2proc-1 $arg0->common.id + etp-process-info $proc + else if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0 if ($arg0->common.u.alive.reg) printf " Registered name: " @@ -1432,6 +1488,7 @@ define etp-process-info printf " Parent: " etp-1 $arg0->parent printf "\n Pointer: (Process *) %p\n", $arg0 + end end document etp-process-info diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 9da4f3cd00..159c91e37c 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 3ab52615ab..9d8a4cfd00 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index a969ef91dc..e521c6fc3d 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -81,7 +81,8 @@ -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([cancel_timer/1, check_old_code/1, check_process_code/2, + check_process_code/3, crc32/1]). -export([crc32/2, crc32_combine/3, date/0, decode_packet/3]). -export([delete_element/2]). -export([delete_module/1, demonitor/1, demonitor/2, display/1]). @@ -91,7 +92,7 @@ -export([float_to_binary/1, float_to_binary/2, float_to_list/1, float_to_list/2]). -export([fun_info/2, fun_to_list/1, function_exported/3]). --export([garbage_collect/0, garbage_collect/1]). +-export([garbage_collect/0, garbage_collect/1, garbage_collect/2]). -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]). @@ -429,11 +430,71 @@ check_old_code(_Module) -> erlang:nif_error(undefined). %% check_process_code/2 --spec check_process_code(Pid, Module) -> boolean() when +-spec check_process_code(Pid, Module) -> CheckResult when Pid :: pid(), - Module :: module(). -check_process_code(_Pid, _Module) -> - erlang:nif_error(undefined). + Module :: module(), + CheckResult :: boolean(). +check_process_code(Pid, Module) -> + try + erlang:check_process_code(Pid, Module, [{allow_gc, true}]) + catch + error:Error -> erlang:error(Error, [Pid, Module]) + end. + +%% check_process_code/3 +-spec check_process_code(Pid, Module, OptionList) -> CheckResult | async when + Pid :: pid(), + Module :: module(), + RequestId :: term(), + Option :: {async, RequestId} | {allow_gc, boolean()}, + OptionList :: [Option], + CheckResult :: boolean() | aborted. +check_process_code(Pid, Module, OptionList) -> + try + {Async, AllowGC} = get_cpc_opts(OptionList, sync, true), + case Async of + {async, ReqId} -> + {priority, Prio} = erlang:process_info(erlang:self(), + priority), + erts_internal:request_system_task(Pid, + Prio, + {check_process_code, + ReqId, + Module, + AllowGC}), + async; + sync -> + case Pid == erlang:self() of + true -> + erts_internal:check_process_code(Module, + [{allow_gc, AllowGC}]); + false -> + {priority, Prio} = erlang:process_info(erlang:self(), + priority), + ReqId = erlang:make_ref(), + erts_internal:request_system_task(Pid, + Prio, + {check_process_code, + ReqId, + Module, + AllowGC}), + receive + {check_process_code, ReqId, CheckResult} -> + CheckResult + end + end + end + catch + error:Error -> erlang:error(Error, [Pid, Module, OptionList]) + end. + +% gets async and allow_gc opts and verify valid option list +get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, AllowGC) -> + get_cpc_opts(Options, AsyncTuple, AllowGC); +get_cpc_opts([{allow_gc, AllowGC} | Options], Async, _OldAllowGC) -> + get_cpc_opts(Options, Async, AllowGC); +get_cpc_opts([], Async, AllowGC) -> + {Async, AllowGC}. %% crc32/1 -spec erlang:crc32(Data) -> non_neg_integer() when @@ -793,10 +854,61 @@ garbage_collect() -> erlang:nif_error(undefined). %% garbage_collect/1 --spec garbage_collect(Pid) -> boolean() when - Pid :: pid(). -garbage_collect(_Pid) -> - erlang:nif_error(undefined). +-spec garbage_collect(Pid) -> GCResult when + Pid :: pid(), + GCResult :: boolean(). +garbage_collect(Pid) -> + try + erlang:garbage_collect(Pid, []) + catch + error:Error -> erlang:error(Error, [Pid]) + end. + +%% garbage_collect/2 +-spec garbage_collect(Pid, OptionList) -> GCResult | async when + Pid :: pid(), + RequestId :: term(), + Option :: {async, RequestId}, + OptionList :: [Option], + GCResult :: boolean(). +garbage_collect(Pid, OptionList) -> + try + Async = get_gc_opts(OptionList, sync), + case Async of + {async, ReqId} -> + {priority, Prio} = erlang:process_info(erlang:self(), + priority), + erts_internal:request_system_task(Pid, + Prio, + {garbage_collect, ReqId}), + async; + sync -> + case Pid == erlang:self() of + true -> + erlang:garbage_collect(); + false -> + {priority, Prio} = erlang:process_info(erlang:self(), + priority), + ReqId = erlang:make_ref(), + erts_internal:request_system_task(Pid, + Prio, + {garbage_collect, + ReqId}), + receive + {garbage_collect, ReqId, GCResult} -> + GCResult + end + end + end + catch + error:Error -> erlang:error(Error, [Pid, OptionList]) + end. + +% gets async opt and verify valid option list +get_gc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync) -> + get_gc_opts(Options, AsyncTuple); +get_gc_opts([], Async) -> + Async. %% garbage_collect_message_area/0 -spec erlang:garbage_collect_message_area() -> boolean(). diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 8a8cd52d64..c8e8e7e069 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -33,6 +33,10 @@ -export([port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). +-export([request_system_task/3]). + +-export([check_process_code/2]). + %% %% Await result of send to port %% @@ -139,3 +143,20 @@ port_info(_Result) -> port_info(_Result, _Item) -> erlang:nif_error(undefined). + +-spec request_system_task(Pid, Prio, Request) -> 'ok' when + Prio :: 'max' | 'high' | 'normal' | 'low', + Request :: {'garbage_collect', term()} + | {'check_process_code', term(), module(), boolean()}, + Pid :: pid(). + +request_system_task(_Pid, _Prio, _Request) -> + erlang:nif_error(undefined). + +-spec check_process_code(Module, OptionList) -> boolean() when + Module :: module(), + Option :: {allow_gc, boolean()}, + OptionList :: [Option]. +check_process_code(_Module, _OptionList) -> + erlang:nif_error(undefined). + -- cgit v1.2.3 From 406fd5c773b5ae73dbfc6b305a502ffbe236a9bb Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 8 Nov 2013 17:41:20 +0100 Subject: Use asynchronous check_process_code in code_parallel_SUITE --- erts/emulator/test/code_parallel_load_SUITE.erl | 36 ++++++++++++++++--------- 1 file changed, 24 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl index 1cfe015ea6..428f1242ab 100644 --- a/erts/emulator/test/code_parallel_load_SUITE.erl +++ b/erts/emulator/test/code_parallel_load_SUITE.erl @@ -159,22 +159,34 @@ 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, []) -> + Tag = make_ref(), + N = request_cpc(Pids, M, Tag), + ok = handle_cpc_responses(N, Tag, M), erlang:purge_module(M), + ok. + +request_cpc(Pid, M, Tag) when is_pid(Pid) -> + erlang:check_process_code(Pid, M, [{async, {Tag, Pid}}]), + 1; +request_cpc(Pids, M, Tag) when is_list(Pids) -> + request_cpc(Pids, M, Tag, 0). + +request_cpc([], _M, _Tag, N) -> + N; +request_cpc([Pid|Pids], M, Tag, N) -> + request_cpc(Pids, M, Tag, N + request_cpc(Pid, M, Tag)). + +handle_cpc_responses(0, _Tag, _Module) -> 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]) +handle_cpc_responses(N, Tag, Module) -> + receive + {check_process_code, {Tag, _Pid}, false} -> + handle_cpc_responses(N-1, Tag, Module); + {check_process_code, {Tag, Pid}, true} -> + 1 = request_cpc(Pid, Module, Tag), + handle_cpc_responses(N, Tag, Module) end. - generate(Module, Attributes, FunStrings) -> FunForms = function_forms(FunStrings), Forms = [ -- cgit v1.2.3 From c6cb0293ba33e1671f8ed670d5add082e5ee674a Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 30 Oct 2013 17:29:18 +0100 Subject: Functionality for disabling garbage collection Being able to disable garbage collection over context switches vastly simplifies implementation of yielding native code that builds large or complex data structures on the heap. This since the heap can be left in an inconsistent state over the context switch. --- erts/emulator/beam/beam_bif_load.c | 12 ++ erts/emulator/beam/erl_bif_info.c | 13 ++ erts/emulator/beam/erl_debug.c | 3 + erts/emulator/beam/erl_gc.c | 13 +- erts/emulator/beam/erl_process.c | 251 +++++++++++++++++++++++++++++++++-- erts/emulator/beam/erl_process.h | 15 ++- erts/emulator/beam/global.h | 5 + erts/emulator/test/process_SUITE.erl | 79 +++++++++-- erts/etc/unix/etp-commands.in | 5 +- 9 files changed, 368 insertions(+), 28 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 5239940a50..3f92c5b025 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -495,6 +495,8 @@ BIF_RETTYPE erts_internal_check_process_code_2(BIF_ALIST_2) res = erts_check_process_code(BIF_P, BIF_ARG_1, allow_gc, &reds); + ASSERT(is_value(res)); + BIF_RET2(res, reds); badarg: @@ -784,6 +786,16 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) } } + if (rp->flags & F_DISABLE_GC) { + /* + * Cannot proceed. Process has disabled gc in order to + * safely leave inconsistent data on the heap and/or + * off heap lists. Need to wait for gc to be enabled + * again. + */ + return THE_NON_VALUE; + } + /* * See if there are funs that refer to the old version of the module. */ diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 673dfc658c..f5893f9291 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3593,6 +3593,19 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) BIF_RET(am_true); } } + else if (ERTS_IS_ATOM_STR("gc_state", BIF_ARG_1)) { + /* Used by process_SUITE (emulator) */ + int res, enable; + + switch (BIF_ARG_2) { + case am_true: enable = 1; break; + case am_false: enable = 0; break; + default: BIF_ERROR(BIF_P, BADARG); break; + } + + res = erts_set_gc_state(BIF_P, enable); + BIF_RET(res ? am_true : am_false); + } else if (ERTS_IS_ATOM_STR("send_fake_exit_signal", BIF_ARG_1)) { /* Used by signal_SUITE (emulator) */ diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index b90d00f236..dc79d45be7 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -299,6 +299,9 @@ void erts_check_for_holes(Process* p) ErlHeapFragment* hf; Eterm* start; + if (p->flags & F_DISABLE_GC) + return; + start = p->last_htop ? p->last_htop : HEAP_START(p); check_memory(start, HEAP_TOP(p)); p->last_htop = HEAP_TOP(p); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 8ba94d89e9..da254286c4 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -400,10 +400,16 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) Uint reclaimed_now = 0; int done = 0; Uint ms1, s1, us1; - ErtsSchedulerData *esdp = erts_get_scheduler_data(); + ErtsSchedulerData *esdp; #ifdef USE_VM_PROBES DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); #endif + + if (p->flags & F_DISABLE_GC) + return 1; + + esdp = erts_get_scheduler_data(); + if (IS_TRACED_FL(p, F_TRACE_GC)) { trace_gc(p, am_gc_start); } @@ -532,6 +538,9 @@ erts_garbage_collect_hibernate(Process* p) Uint area_size; Sint offs; + if (p->flags & F_DISABLE_GC) + ERTS_INTERNAL_ERROR("GC disabled"); + /* * Preliminaries. */ @@ -667,6 +676,8 @@ erts_garbage_collect_literals(Process* p, Eterm* literals, Uint n; struct erl_off_heap_header** prev; + if (p->flags & F_DISABLE_GC) + return; /* * Set GC state. */ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index ad806da660..13b18e9e0e 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -513,6 +513,11 @@ erts_pre_init_process(void) erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].set_locks = ERTS_PSD_CALL_TIME_BP_SET_LOCKS; + erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].get_locks + = ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS; + erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks + = ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS; + /* Check that we have locks for all entries */ for (ix = 0; ix < ERTS_PSD_SIZE; ix++) { ERTS_SMP_LC_ASSERT(erts_psd_required_locks[ix].get_locks); @@ -7033,7 +7038,7 @@ erts_set_process_priority(Process *p, Eterm value) oprio = ERTS_PSFLGS_GET_USR_PRIO(a); n = e = a; - if (!(a & ERTS_PSFLG_ACTIVE_SYS)) + if (!(a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DELAYED_SYS))) aprio = nprio; else { int max_qbit; @@ -7043,7 +7048,15 @@ erts_set_process_priority(Process *p, Eterm value) slocked = 1; } - max_qbit = p->sys_task_qs->qmask; + max_qbit = 0; + if (a & ERTS_PSFLG_ACTIVE_SYS) + max_qbit |= p->sys_task_qs->qmask; + if (a & ERTS_PSFLG_DELAYED_SYS) { + ErtsProcSysTaskQs *qs; + qs = ERTS_PROC_GET_DELAYED_GC_TASK_QS(p); + ASSERT(qs); + max_qbit |= qs->qmask; + } max_qbit &= -max_qbit; switch (max_qbit) { case MAX_BIT: @@ -7731,12 +7744,14 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result) } static ERTS_INLINE ErtsProcSysTask * -fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp) +fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp, int *priop) { ErtsProcSysTaskQs *unused_qs = NULL; int qbit, qmask; ErtsProcSysTask *st, **qp; + *priop = -1; /* Shut up annoying erroneous warning */ + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS); if (!c_p->sys_task_qs) { @@ -7770,20 +7785,24 @@ fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp) switch (qbit) { case MAX_BIT: qp = &c_p->sys_task_qs->q[PRIORITY_MAX]; + *priop = PRIORITY_MAX; break; case HIGH_BIT: qp = &c_p->sys_task_qs->q[PRIORITY_HIGH]; + *priop = PRIORITY_HIGH; break; case NORMAL_BIT: if (!(qmask & PRIORITY_LOW) || ++c_p->sys_task_qs->ncount <= RESCHEDULE_LOW) { qp = &c_p->sys_task_qs->q[PRIORITY_NORMAL]; + *priop = PRIORITY_NORMAL; break; } c_p->sys_task_qs->ncount = 0; /* Fall through */ case LOW_BIT: qp = &c_p->sys_task_qs->q[PRIORITY_LOW]; + *priop = PRIORITY_LOW; break; default: ERTS_INTERNAL_ERROR("Invalid qmask"); @@ -7807,6 +7826,12 @@ fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp) qmask2 = qmask; + if (state & ERTS_PSFLG_DELAYED_SYS) { + ErtsProcSysTaskQs *qs = ERTS_PROC_GET_DELAYED_GC_TASK_QS(c_p); + ASSERT(qs); + qmask2 |= qs->qmask; + } + switch (qmask2 & -qmask2) { case MAX_BIT: st_prio = PRIORITY_MAX; @@ -7818,17 +7843,18 @@ fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp) st_prio = PRIORITY_NORMAL; break; case LOW_BIT: - st_prio = PRIORITY_LOW; - break; case 0: st_prio = PRIORITY_LOW; - unused_qs = c_p->sys_task_qs; - c_p->sys_task_qs = NULL; break; default: ERTS_INTERNAL_ERROR("Invalid qmask"); } + if (!qmask) { + unused_qs = c_p->sys_task_qs; + c_p->sys_task_qs = NULL; + } + a = state; do { erts_aint32_t prio = ERTS_PSFLGS_GET_USR_PRIO(a); @@ -7862,6 +7888,8 @@ done: return st; } +static void save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio); + static int execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) { @@ -7875,6 +7903,7 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) do { ErtsProcSysTask *st; + int st_prio; Eterm st_res; if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) { @@ -7886,24 +7915,39 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) break; } - st = fetch_sys_task(c_p, state, &qmask); + st = fetch_sys_task(c_p, state, &qmask, &st_prio); if (!st) break; switch (st->type) { case ERTS_PSTT_GC: - if (!garbage_collected) { - FLAGS(c_p) |= F_NEED_FULLSWEEP; - reds += erts_garbage_collect(c_p, 0, c_p->arg_reg, c_p->arity); - garbage_collected = 1; + if (c_p->flags & F_DISABLE_GC) { + save_gc_task(c_p, st, st_prio); + st = NULL; + reds++; + } + else { + if (!garbage_collected) { + FLAGS(c_p) |= F_NEED_FULLSWEEP; + reds += erts_garbage_collect(c_p, + 0, + c_p->arg_reg, + c_p->arity); + garbage_collected = 1; + } + st_res = am_true; } - st_res = am_true; break; case ERTS_PSTT_CPC: st_res = erts_check_process_code(c_p, st->arg[0], st->arg[1] == am_true, &reds); + if (is_non_value(st_res)) { + /* Needed gc, but gc was disabled */ + save_gc_task(c_p, st, st_prio); + st = NULL; + } break; default: ERTS_INTERNAL_ERROR("Invalid process sys task type"); @@ -7934,8 +7978,9 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds) do { ErtsProcSysTask *st; Eterm st_res; + int st_prio; - st = fetch_sys_task(c_p, state, &qmask); + st = fetch_sys_task(c_p, state, &qmask, &st_prio); if (!st) break; @@ -8168,6 +8213,183 @@ badarg: BIF_ERROR(BIF_P, BADARG); } +static void +save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio) +{ + erts_aint32_t state; + ErtsProcSysTaskQs *qs; + + ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p)); + + qs = ERTS_PROC_GET_DELAYED_GC_TASK_QS(c_p); + if (!qs) { + st->next = st->prev = st; + qs = proc_sys_task_queues_alloc(); + qs->qmask = 1 << prio; + qs->ncount = 0; + qs->q[PRIORITY_MAX] = NULL; + qs->q[PRIORITY_HIGH] = NULL; + qs->q[PRIORITY_NORMAL] = NULL; + qs->q[PRIORITY_LOW] = NULL; + qs->q[prio] = st; + (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, ERTS_PROC_LOCK_MAIN, qs); + } + else { + if (!qs->q[prio]) { + st->next = st->prev = st; + qs->q[prio] = st; + qs->qmask |= 1 << prio; + } + else { + st->next = qs->q[prio]; + st->prev = qs->q[prio]->prev; + st->next->prev = st; + st->prev->next = st; + ASSERT(qs->qmask & (1 << prio)); + } + } + + state = erts_smp_atomic32_read_nob(&c_p->state); + ASSERT((ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS) & state); + + while (!(state & ERTS_PSFLG_DELAYED_SYS) + || prio < ERTS_PSFLGS_GET_ACT_PRIO(state)) { + erts_aint32_t n, e; + + n = e = state; + n |= ERTS_PSFLG_DELAYED_SYS; + if (prio < ERTS_PSFLGS_GET_ACT_PRIO(state)) { + n &= ~ERTS_PSFLGS_ACT_PRIO_MASK; + n |= prio << ERTS_PSFLGS_ACT_PRIO_OFFSET; + } + state = erts_smp_atomic32_cmpxchg_relb(&c_p->state, n, e); + if (state == e) + break; + } +} + +int +erts_set_gc_state(Process *c_p, int enable) +{ + int res; + ErtsProcSysTaskQs *dgc_tsk_qs; + ASSERT(c_p == erts_get_current_process()); + ASSERT((ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS) + & erts_smp_atomic32_read_nob(&c_p->state)); + ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p)); + + res = !(c_p->flags & F_DISABLE_GC); + + if (!enable) { + c_p->flags |= F_DISABLE_GC; + return res; + } + + c_p->flags &= ~F_DISABLE_GC; + + dgc_tsk_qs = ERTS_PROC_GET_DELAYED_GC_TASK_QS(c_p); + if (!dgc_tsk_qs) + return res; + + /* Move delayed gc tasks into sys tasks queues. */ + + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS); + + if (!c_p->sys_task_qs) { + c_p->sys_task_qs = dgc_tsk_qs; + dgc_tsk_qs = NULL; + } + else { + ErtsProcSysTaskQs *stsk_qs; + int prio; + + /* + * We push delayed tasks to the front of the queue + * since they have already made it to the front + * once and then been delayed after that. + */ + + stsk_qs = c_p->sys_task_qs; + + while (dgc_tsk_qs->qmask) { + int qbit = dgc_tsk_qs->qmask & -dgc_tsk_qs->qmask; + dgc_tsk_qs->qmask &= ~qbit; + switch (qbit) { + case MAX_BIT: + prio = PRIORITY_MAX; + break; + case HIGH_BIT: + prio = PRIORITY_HIGH; + break; + case NORMAL_BIT: + prio = PRIORITY_NORMAL; + break; + case LOW_BIT: + prio = PRIORITY_LOW; + break; + default: + ERTS_INTERNAL_ERROR("Invalid qmask"); + prio = -1; + break; + } + + ASSERT(dgc_tsk_qs->q[prio]); + + if (!stsk_qs->q[prio]) { + stsk_qs->q[prio] = dgc_tsk_qs->q[prio]; + stsk_qs->qmask |= 1 << prio; + } + else { + ErtsProcSysTask *first1, *last1, *first2, *last2; + + ASSERT(stsk_qs->qmask & (1 << prio)); + first1 = dgc_tsk_qs->q[prio]; + last1 = first1->prev; + first2 = stsk_qs->q[prio]; + last2 = first1->prev; + + last1->next = first2; + first2->prev = last1; + + first1->prev = last2; + last2->next = first1; + + stsk_qs->q[prio] = first1; + } + + } + } + +#ifdef DEBUG + { + int qmask; + erts_aint32_t aprio, state = +#endif + + erts_smp_atomic32_read_bset_nob(&c_p->state, + (ERTS_PSFLG_DELAYED_SYS + | ERTS_PSFLG_ACTIVE_SYS), + ERTS_PSFLG_ACTIVE_SYS); + +#ifdef DEBUG + ASSERT(state & ERTS_PSFLG_DELAYED_SYS); + qmask = c_p->sys_task_qs->qmask; + aprio = ERTS_PSFLGS_GET_ACT_PRIO(state); + ASSERT(ERTS_PSFLGS_GET_USR_PRIO(state) >= aprio); + ASSERT((qmask & -qmask) >= (1 << aprio)); + } +#endif + + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS); + + (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, ERTS_PROC_LOCK_MAIN, NULL); + + if (dgc_tsk_qs) + proc_sys_task_queues_free(dgc_tsk_qs); + + return res; +} + void erts_sched_stat_modify(int what) { @@ -9839,6 +10061,7 @@ erts_continue_exit_process(Process *p) p->flags &= ~F_USING_DB; } + erts_set_gc_state(p, 1); state = erts_smp_atomic32_read_acqb(&p->state); if (state & ERTS_PSFLG_ACTIVE_SYS) { if (cleanup_sys_tasks(p, state, CONTEXT_REDS) >= CONTEXT_REDS/2) diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 814e3d34df..0b8d2976ed 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -631,8 +631,9 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi) #define ERTS_PSD_SCHED_ID 2 #define ERTS_PSD_DIST_ENTRY 3 #define ERTS_PSD_CALL_TIME_BP 4 +#define ERTS_PSD_DELAYED_GC_TASK_QS 5 -#define ERTS_PSD_SIZE 5 +#define ERTS_PSD_SIZE 6 typedef struct { void *data[ERTS_PSD_SIZE]; @@ -656,6 +657,9 @@ typedef struct { #define ERTS_PSD_CALL_TIME_BP_GET_LOCKS ERTS_PROC_LOCK_MAIN #define ERTS_PSD_CALL_TIME_BP_SET_LOCKS ERTS_PROC_LOCK_MAIN +#define ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS ERTS_PROC_LOCK_MAIN +#define ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS ERTS_PROC_LOCK_MAIN + typedef struct { ErtsProcLocks get_locks; ErtsProcLocks set_locks; @@ -859,6 +863,7 @@ struct process { Uint64 bin_old_vheap; /* Virtual old heap size for binaries */ ErtsProcSysTaskQs *sys_task_qs; + erts_smp_atomic32_t state; /* Process state flags (see ERTS_PSFLG_*) */ #ifdef ERTS_SMP @@ -976,6 +981,7 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLG_ACTIVE_SYS ERTS_PSFLG_BIT(14) #define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15) #define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16) +#define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17) #define ERTS_PSFLGS_IN_PRQ_MASK (ERTS_PSFLG_IN_PRQ_MAX \ | ERTS_PSFLG_IN_PRQ_HIGH \ @@ -1102,6 +1108,7 @@ 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_DISABLE_GC (1 << 11) /* Disable GC */ /* process trace_flags */ #define F_SENSITIVE (1 << 0) @@ -1192,6 +1199,7 @@ void erts_late_init_process(void); void erts_early_init_scheduling(int); void erts_init_scheduling(int, int); +int erts_set_gc_state(Process *c_p, int enable); Eterm erts_sched_wall_time_request(Process *c_p, int set, int enable); Eterm erts_gc_info_request(Process *c_p); Uint64 erts_get_proc_interval(void); @@ -1637,6 +1645,11 @@ erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *data) #define ERTS_PROC_SET_CALL_TIME(P, L, PBT) \ ((process_breakpoint_time_t *) erts_psd_set((P), (L), ERTS_PSD_CALL_TIME_BP, (void *) (PBT))) +#define ERTS_PROC_GET_DELAYED_GC_TASK_QS(P) \ + ((ErtsProcSysTaskQs *) erts_psd_get((P), ERTS_PSD_DELAYED_GC_TASK_QS)) +#define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, L, PBT) \ + ((ErtsProcSysTaskQs *) erts_psd_set((P), (L), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT))) + ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p); ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p, diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 885f092aca..6b720b53d8 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1114,7 +1114,12 @@ erts_alloc_message_heap_state(Uint size, if (statep) *statep = state; if ((state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) + || (receiver->flags & F_DISABLE_GC) || HEAP_LIMIT(receiver) - HEAP_TOP(receiver) <= size) { + /* + * The heap is either potentially in an inconsistent + * state, or not large enough. + */ #ifdef ERTS_SMP if (locked_main) { *receiver_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index e66c6f09b6..bf31655066 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -55,7 +55,9 @@ no_priority_inversion/1, no_priority_inversion2/1, system_task_blast/1, - system_task_on_suspended/1]). + system_task_on_suspended/1, + gc_request_when_gc_disabled/1, + gc_request_blast_when_gc_disabled/1]). -export([prio_server/2, prio_client/2]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -95,7 +97,8 @@ groups() -> otp_7738_resume]}, {system_task, [], [no_priority_inversion, no_priority_inversion2, - system_task_blast, system_task_on_suspended]}]. + system_task_blast, system_task_on_suspended, + gc_request_when_gc_disabled, gc_request_blast_when_gc_disabled]}]. init_per_suite(Config) -> A0 = case application:start(sasl) of @@ -2361,15 +2364,69 @@ system_task_on_suspended(Config) when is_list(Config) -> ok end. -gc_req(_Pid, 0) -> - []; -gc_req(Pid, N) -> - R0 = erts_internal:request_system_task(Pid, low, garbage_collect), - R1 = erts_internal:request_system_task(Pid, normal, garbage_collect), - R2 = erts_internal:request_system_task(Pid, high, garbage_collect), - R3 = erts_internal:request_system_task(Pid, max, garbage_collect), - [R0, R1, R2, R3 | gc_req(Pid, N-1)]. - +gc_request_when_gc_disabled(Config) when is_list(Config) -> + Master = self(), + AIS = erts_debug:set_internal_state(available_internal_state, true), + {P, M} = spawn_opt(fun () -> + true = erts_debug:set_internal_state(gc_state, + false), + Master ! {self(), gc_state, false}, + receive after 1000 -> ok end, + Master ! {self(), gc_state, true}, + false = erts_debug:set_internal_state(gc_state, + true), + receive after 100 -> ok end + end, [monitor, link]), + receive {P, gc_state, false} -> ok end, + ReqId = make_ref(), + async = garbage_collect(P, [{async, ReqId}]), + receive + {garbage_collect, ReqId, Result} -> + ?t:fail({unexpected_gc, Result}); + {P, gc_state, true} -> + ok + end, + receive {garbage_collect, ReqId, true} -> ok end, + erts_debug:set_internal_state(available_internal_state, AIS), + receive {'DOWN', M, process, P, _Reason} -> ok end, + ok. + +gc_request_blast_when_gc_disabled(Config) when is_list(Config) -> + Master = self(), + AIS = erts_debug:set_internal_state(available_internal_state, true), + {P, M} = spawn_opt(fun () -> + true = erts_debug:set_internal_state(gc_state, + false), + Master ! {self(), gc_state, false}, + receive after 1000 -> ok end, + false = erts_debug:set_internal_state(gc_state, + true), + receive after 100 -> ok end + end, [monitor, link]), + receive {P, gc_state, false} -> ok end, + PMs = lists:map(fun (N) -> + Prio = case N rem 4 of + 0 -> max; + 1 -> high; + 2 -> normal; + 3 -> low + end, + spawn_opt(fun () -> + erlang:garbage_collect(P) + end, [monitor, link, {priority, Prio}]) + end, lists:seq(1, 10000)), + lists:foreach(fun ({Proc, Mon}) -> + receive + {'DOWN', Mon, process, Proc, normal} -> + ok + end + end, + PMs), + erts_debug:set_internal_state(available_internal_state, AIS), + receive {'DOWN', M, process, P, _Reason} -> ok end, + ok. + + %% Internal functions wait_until(Fun) -> diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 6e3466b8c0..73887931cc 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -1316,9 +1316,12 @@ end define etp-proc-state-int # Args: int # - if ($arg0 & 0xff800000) + if ($arg0 & 0xff000000) printf "GARBAGE | " end + if ($arg0 & 0x800000) + printf "delayed-sys | " + end if ($arg0 & 0x400000) printf "proxy | " set $proxy_process = 1 -- cgit v1.2.3 From 7f17d16335231b4bedd9d2cdd7507ab88a5bf021 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 8 Oct 2013 12:18:46 +0200 Subject: trapping binary_to_term passing binary_SUITE --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/erl_debug.c | 2 +- erts/emulator/beam/external.c | 299 ++++++++++++++++++++++++++++++++++------- erts/emulator/beam/external.h | 1 + erts/emulator/beam/sys.h | 6 + 5 files changed, 256 insertions(+), 53 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 8433fd826a..71addedfdc 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -115,6 +115,7 @@ atom binary_longest_prefix_trap atom binary_longest_suffix_trap atom binary_match_trap atom binary_matches_trap +atom binary_to_term_trap atom block atom blocked atom bm diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index dc79d45be7..873a9860da 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2012. All Rights Reserved. + * Copyright Ericsson AB 1998-2013. All Rights Reserved. * * The 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/external.c b/erts/emulator/beam/external.c index 1c88765381..b1cdad1343 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -61,6 +61,9 @@ */ # define ERTS_DEBUG_USE_DIST_SEP # endif +# define IF_DEBUG(X) X +#else +# define IF_DEBUG(X) #endif /* Does Sint fit in Sint32? @@ -89,7 +92,8 @@ static int enc_term_int(Process *p,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, static Uint is_external_string(Eterm obj, int* p_is_string); static byte* enc_atom(ErtsAtomCacheMap *, Eterm, byte*, Uint32); static byte* enc_pid(ErtsAtomCacheMap *, Eterm, byte*, Uint32); -static byte* dec_term(ErtsDistExternal *, Eterm**, byte*, ErlOffHeap*, Eterm*); +struct B2TContext_t; +static byte* dec_term(ErtsDistExternal *, Eterm**, byte*, ErlOffHeap*, Eterm*, struct B2TContext_t*); static byte* dec_atom(ErtsDistExternal *, byte*, Eterm*); static byte* dec_pid(ErtsDistExternal *, Eterm**, byte*, ErlOffHeap*, Eterm*); static Sint decoded_size(byte *ep, byte* endp, int internal_tags); @@ -102,11 +106,19 @@ static Uint encode_size_struct2(ErtsAtomCacheMap *, Eterm, unsigned); static int encode_size_struct_int(Process *p, ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags, Sint *reds, Uint *res); +static Export binary_to_term_trap_export; +static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1); +static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b); + void erts_init_external(void) { #if 1 /* In R16 */ erts_init_trap_export(&term_to_binary_trap_export, am_erlang, am_term_to_binary_trap, 1, &term_to_binary_trap_1); + + erts_init_trap_export(&binary_to_term_trap_export, + am_erlang, am_binary_to_term_trap, 1, + &binary_to_term_trap_1); #else sys_memset((void *) &term_to_binary_trap_export, 0, sizeof(Export)); term_to_binary_trap_export.address = &term_to_binary_trap_export.code[3]; @@ -927,7 +939,7 @@ erts_decode_dist_ext(Eterm** hpp, goto error; ep++; } - ep = dec_term(edep, hpp, ep, off_heap, &obj); + ep = dec_term(edep, hpp, ep, off_heap, &obj, NULL); if (!ep) goto error; @@ -948,7 +960,7 @@ Eterm erts_decode_ext(Eterm **hpp, ErlOffHeap *off_heap, byte **ext) byte *ep = *ext; if (*ep++ != VERSION_MAGIC) return THE_NON_VALUE; - ep = dec_term(NULL, hpp, ep, off_heap, &obj); + ep = dec_term(NULL, hpp, ep, off_heap, &obj, NULL); if (!ep) { #ifdef DEBUG bin_write(ERTS_PRINT_STDERR,NULL,*ext,500); @@ -962,7 +974,7 @@ Eterm erts_decode_ext(Eterm **hpp, ErlOffHeap *off_heap, byte **ext) Eterm erts_decode_ext_ets(Eterm **hpp, ErlOffHeap *off_heap, byte *ext) { Eterm obj; - ext = dec_term(NULL, hpp, ext, off_heap, &obj); + ext = dec_term(NULL, hpp, ext, off_heap, &obj, NULL); ASSERT(ext); return obj; } @@ -1112,6 +1124,46 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2) } } + +enum B2TState { /*B2TUncompress,*/ B2TSize, B2TDecodeInit, B2TDecode, B2TDecodeFail, B2TBadArg, B2TDone }; + +typedef struct { +} B2TSizeContext; + +typedef struct { + byte* ep; + Eterm res; + Eterm* next; + Eterm* hp_start; + Eterm* hp; + Eterm* hp_end; +} B2TDecodeContext; + +typedef struct { + /*Uint real_size; + Uint dest_len; + byte *dbytes; + Binary *result_bin; + Binary *destination_bin; + z_stream stream;*/ +} B2TUncompressContext; + +typedef struct B2TContext_t { + Sint heap_size; + byte* aligned_alloc; + ErtsBinary2TermState b2ts; + //Uint ext_size; + Uint reds; + Eterm trap_bin; + enum B2TState state; + union { + B2TSizeContext sc; + B2TDecodeContext dc; + B2TUncompressContext uc; + } u; +} B2TContext; + + static uLongf binary2term_uncomp_size(byte* data, Sint size) { z_stream stream; @@ -1153,7 +1205,7 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size) if (size < 1 || *bytes != VERSION_MAGIC) { error: if (state->exttmp) - erts_free(ERTS_ALC_T_TMP, state->extp); + erts_free(ERTS_ALC_T_EXT_TERM_DATA, state->extp); state->extp = NULL; state->exttmp = 0; return -1; @@ -1168,17 +1220,18 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size) bytes += 5; size -= 5; if (dest_len > 32*1024*1024 - || (state->extp = erts_alloc_fnf(ERTS_ALC_T_TMP, dest_len)) == NULL) { + || (state->extp = erts_alloc_fnf(ERTS_ALC_T_EXT_TERM_DATA, dest_len)) == NULL) { if (dest_len != binary2term_uncomp_size(bytes, size)) { goto error; } - state->extp = erts_alloc(ERTS_ALC_T_TMP, dest_len); + state->extp = erts_alloc(ERTS_ALC_T_EXT_TERM_DATA, dest_len); } state->exttmp = 1; if (erl_zlib_uncompress(state->extp, &dest_len, bytes, size) != Z_OK) goto error; size = (Sint) dest_len; } + state->extsize = size; res = decoded_size(state->extp, state->extp + size, 0); if (res < 0) goto error; @@ -1190,7 +1243,7 @@ binary2term_abort(ErtsBinary2TermState *state) { if (state->exttmp) { state->exttmp = 0; - erts_free(ERTS_ALC_T_TMP, state->extp); + erts_free(ERTS_ALC_T_EXT_TERM_DATA, state->extp); } } @@ -1198,11 +1251,11 @@ static ERTS_INLINE Eterm binary2term_create(ErtsDistExternal *edep, ErtsBinary2TermState *state, Eterm **hpp, ErlOffHeap *ohp) { Eterm res; - if (!dec_term(edep, hpp, state->extp, ohp, &res)) + if (!dec_term(edep, hpp, state->extp, ohp, &res, NULL)) res = THE_NON_VALUE; if (state->exttmp) { state->exttmp = 0; - erts_free(ERTS_ALC_T_TMP, state->extp); + erts_free(ERTS_ALC_T_EXT_TERM_DATA, state->extp); } return res; } @@ -1225,46 +1278,155 @@ erts_binary2term_create(ErtsBinary2TermState *state, Eterm **hpp, ErlOffHeap *oh return binary2term_create(NULL,state, hpp, ohp); } +static void b2t_destroy_context(B2TContext* context) +{ + erts_free_aligned_binary_bytes_extra(context->aligned_alloc, + ERTS_ALC_T_EXT_TERM_DATA); + context->aligned_alloc = NULL; + binary2term_abort(&context->b2ts); +} + +static void b2t_context_destructor(Binary *context_bin) +{ + B2TContext* ctx = (B2TContext*) ERTS_MAGIC_BIN_DATA(context_bin); + ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(context_bin) == b2t_context_destructor); + + b2t_destroy_context(ctx); +} + +static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1) +{ + Binary *context_bin = ((ProcBin *) binary_val(BIF_ARG_1))->val; + ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(context_bin) == b2t_context_destructor); + + return binary_to_term_int(BIF_P, THE_NON_VALUE, context_bin); +} + BIF_RETTYPE binary_to_term_1(BIF_ALIST_1) { - Sint heap_size; - Eterm res; - Eterm* hp; - Eterm* endp; - Sint size; + return binary_to_term_int(BIF_P, BIF_ARG_1, NULL); +} + +#define B2T_BYTES_PER_REDUCTION 100 + + +static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) +{ byte* bytes; - byte* temp_alloc = NULL; - ErtsBinary2TermState b2ts; + B2TContext c_buff; + B2TContext *ctx; + Eterm* magic_space = NULL; - if ((bytes = erts_get_aligned_binary_bytes(BIF_ARG_1, &temp_alloc)) == NULL) { - error: - erts_free_aligned_binary_bytes(temp_alloc); - BIF_ERROR(BIF_P, BADARG); - } - size = binary_size(BIF_ARG_1); + if (context_b == NULL) { + /* Setup enough to get started */ + ctx = &c_buff; + ctx->state = B2TSize; + ctx->aligned_alloc = NULL; + IF_DEBUG(ctx->trap_bin = THE_NON_VALUE;) + } else { + ctx = ERTS_MAGIC_BIN_DATA(context_b); + } + ctx->reds = 1; /*(Uint)(ERTS_BIF_REDS_LEFT(p) * B2T_BYTES_PER_REDUCTION);*/ + + do { + switch (ctx->state) { + case B2TSize: + bytes = erts_get_aligned_binary_bytes_extra(bin, + &ctx->aligned_alloc, + ERTS_ALC_T_EXT_TERM_DATA, + 0); + if (bytes == NULL) { + ctx->b2ts.exttmp = 0; + ctx->state = B2TBadArg; + break; + } - heap_size = binary2term_prepare(&b2ts, bytes, size); - if (heap_size < 0) - goto error; + ctx->heap_size = binary2term_prepare(&ctx->b2ts, bytes, + binary_size(bin)); + if (ctx->heap_size < 0) { + ctx->state = B2TBadArg; + break; + } - hp = HAlloc(BIF_P, heap_size); - endp = hp + heap_size; - res = binary2term_create(NULL, &b2ts, &hp, &MSO(BIF_P)); - erts_free_aligned_binary_bytes(temp_alloc); + ctx->state = B2TDecodeInit; - if (hp > endp) { - erl_exit(1, ":%s, line %d: heap overrun by %d words(s)\n", - __FILE__, __LINE__, hp-endp); - } + if (ctx->b2ts.extsize >= ctx->reds) { + ctx->reds = 0; + break; + } + ctx->reds -= ctx->b2ts.extsize; + /*fall through*/ + case B2TDecodeInit: + if (context_b == NULL && ctx->b2ts.extsize > ctx->reds) { + /* dec_term will probably trap, allocate space for magic bin + before result term to make it easy to trim with HRelease. + */ + magic_space = HAlloc(p, PROC_BIN_SIZE); + *magic_space = make_pos_bignum_header(PROC_BIN_SIZE-1); + } + ctx->u.dc.ep = ctx->b2ts.extp; + ctx->u.dc.res = (Eterm) (UWord) NULL; + ctx->u.dc.next = &ctx->u.dc.res; + ctx->u.dc.hp_start = HAlloc(p, ctx->heap_size); + ctx->u.dc.hp = ctx->u.dc.hp_start; + ctx->u.dc.hp_end = ctx->u.dc.hp_start + ctx->heap_size; + ctx->state = B2TDecode; + /*fall through*/ + case B2TDecode: + dec_term(NULL, NULL, NULL, &MSO(p), NULL, ctx); + break; + + case B2TDecodeFail: + HRelease(p, ctx->u.dc.hp_end, ctx->u.dc.hp_start); + /*fall through*/ + case B2TBadArg: + b2t_destroy_context(ctx); + if (context_b) { + erts_set_gc_state(p, 1); + } + BIF_ERROR(p, BADARG); - HRelease(BIF_P, endp, hp); + case B2TDone: + b2t_destroy_context(ctx); - if (res == THE_NON_VALUE) - goto error; + if (ctx->u.dc.hp > ctx->u.dc.hp_end) { + erl_exit(1, ":%s, line %d: heap overrun by %d words(s)\n", + __FILE__, __LINE__, ctx->u.dc.hp - ctx->u.dc.hp_end); + } + HRelease(p, ctx->u.dc.hp_end, ctx->u.dc.hp); - return res; + if (context_b) { + erts_set_gc_state(p, 1); + } + + return ctx->u.dc.res; + + } + }while (ctx->reds); + + if (context_b == NULL) { + ASSERT(ctx->trap_bin == THE_NON_VALUE); + + context_b = erts_create_magic_binary(sizeof(B2TContext), + b2t_context_destructor); + ctx = ERTS_MAGIC_BIN_DATA(context_b); + sys_memcpy(ctx, &c_buff, sizeof(B2TContext)); + + if (!magic_space) { + ASSERT(ctx->state != B2TDecode); + magic_space = HAlloc(p, PROC_BIN_SIZE); + } + ctx->trap_bin = erts_mk_magic_binary_term(&magic_space, &MSO(p), context_b); + + erts_set_gc_state(p, 0); + } + ASSERT(context_b); + ASSERT(ctx->trap_bin != THE_NON_VALUE); + + BUMP_ALL_REDS(p); + BIF_TRAP1(&binary_to_term_trap_export, p, ctx->trap_bin); } BIF_RETTYPE binary_to_term_2(BIF_ALIST_2) @@ -1514,7 +1676,7 @@ typedef struct { } s; } TTBContext; -static void context_destructor(Binary *context_bin) +static void ttb_context_destructor(Binary *context_bin) { TTBContext *context = ERTS_MAGIC_BIN_DATA(context_bin); if (context->alive) { @@ -1567,7 +1729,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla do { \ if (context_b == NULL) { \ context_b = erts_create_magic_binary(sizeof(TTBContext), \ - context_destructor); \ + ttb_context_destructor); \ context = ERTS_MAGIC_BIN_DATA(context_b); \ memcpy(context,&c_buff,sizeof(TTBContext)); \ } \ @@ -2608,15 +2770,34 @@ undo_offheap_in_area(ErlOffHeap* off_heap, Eterm* start, Eterm* end) ** On failure return NULL and (R13B04) *hpp will be unchanged. */ static byte* -dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, Eterm* objp) +dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, + Eterm* objp, B2TContext* ctx) { - Eterm* hp_saved = *hpp; + Eterm* hp_saved; int n; ErtsAtomEncoding char_enc; - register Eterm* hp = *hpp; /* Please don't take the address of hp */ - Eterm* next = objp; - - *next = (Eterm) (UWord) NULL; + register Eterm* hp; /* Please don't take the address of hp */ + Eterm* next; + byte* ep_trap_limit; + + if (ctx) { + hp_saved = ctx->u.dc.hp_start; + ep = ctx->u.dc.ep; + ep_trap_limit = ep + ctx->reds; + if (ep_trap_limit < ep) { + ep_trap_limit = (byte*)ERTS_UWORD_MAX; /*SVERK Is there a safe way to create a "largest ptr" */ + } + next = ctx->u.dc.next; + hpp = &ctx->u.dc.hp; + } + else { + hp_saved = *hpp; + ep_trap_limit = (byte*)ERTS_UWORD_MAX; /*SVERK - " - */ + next = objp; + *next = (Eterm) (UWord) NULL; + } + ASSERT(ep < ep_trap_limit); + hp = *hpp; while (next != NULL) { objp = next; @@ -3065,7 +3246,7 @@ dec_term_atom_common: goto error; } *hpp = hp; - ep = dec_term(edep, hpp, ep, off_heap, &temp); + ep = dec_term(edep, hpp, ep, off_heap, &temp, NULL); hp = *hpp; if (ep == NULL) { goto error; @@ -3125,7 +3306,7 @@ dec_term_atom_common: } *hpp = hp; /* Index */ - if ((ep = dec_term(edep, hpp, ep, off_heap, &temp)) == NULL) { + if ((ep = dec_term(edep, hpp, ep, off_heap, &temp, NULL)) == NULL) { goto error; } if (!is_small(temp)) { @@ -3134,7 +3315,7 @@ dec_term_atom_common: old_index = unsigned_val(temp); /* Uniq */ - if ((ep = dec_term(edep, hpp, ep, off_heap, &temp)) == NULL) { + if ((ep = dec_term(edep, hpp, ep, off_heap, &temp, NULL)) == NULL) { goto error; } if (!is_small(temp)) { @@ -3202,7 +3383,7 @@ dec_term_atom_common: } /* Index */ - if ((ep = dec_term(edep, hpp, ep, off_heap, &temp)) == NULL) { + if ((ep = dec_term(edep, hpp, ep, off_heap, &temp, NULL)) == NULL) { goto error; } if (!is_small(temp)) { @@ -3211,7 +3392,7 @@ dec_term_atom_common: old_index = unsigned_val(temp); /* Uniq */ - if ((ep = dec_term(edep, hpp, ep, off_heap, &temp)) == NULL) { + if ((ep = dec_term(edep, hpp, ep, off_heap, &temp, NULL)) == NULL) { goto error; } if (!is_small(temp)) { @@ -3311,8 +3492,22 @@ dec_term_atom_common: } undo_offheap_in_area(off_heap, hp_saved, hp); *hpp = hp_saved; - return NULL; + if (ctx) { + ctx->state = B2TDecodeFail; + } + return NULL; } + if (ep > ep_trap_limit) { + ASSERT(ctx); + ctx->u.dc.ep = ep; + ctx->u.dc.next = next; + ctx->u.dc.hp = hp; + ctx->reds = 0; + return NULL; + } + } + if (ctx) { + ctx->state = B2TDone; } *hpp = hp; return ep; diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index e37d47919e..9afdb52329 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -146,6 +146,7 @@ typedef struct { typedef struct { byte *extp; int exttmp; + Uint extsize; } ErtsBinary2TermState; /* -------------------------------------------------------------------------- */ diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 05bff430e3..11ba554a3d 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -282,16 +282,19 @@ typedef unsigned long UWord; typedef long SWord; #define SWORD_CONSTANT(Const) Const##L #define UWORD_CONSTANT(Const) Const##UL +#define ERTS_UWORD_MAX ULONG_MAX #elif SIZEOF_VOID_P == SIZEOF_INT typedef unsigned int UWord; typedef int SWord; #define SWORD_CONSTANT(Const) Const #define UWORD_CONSTANT(Const) Const##U +#define ERTS_UWORD_MAX UINT_MAX #elif SIZEOF_VOID_P == SIZEOF_LONG_LONG typedef unsigned long long UWord; typedef long long SWord; #define SWORD_CONSTANT(Const) Const##LL #define UWORD_CONSTANT(Const) Const##ULL +#define ERTS_UWORD_MAX ULLONG_MAX #else #error Found no appropriate type to use for 'Eterm', 'Uint' and 'Sint' #endif @@ -304,6 +307,7 @@ typedef unsigned long Uint; typedef long Sint; #define SWORD_CONSTANT(Const) Const##L #define UWORD_CONSTANT(Const) Const##UL +#define ERTS_UWORD_MAX ULONG_MAX #define ERTS_SIZEOF_ETERM SIZEOF_LONG #define ErtsStrToSint strtol #elif SIZEOF_VOID_P == SIZEOF_INT @@ -312,6 +316,7 @@ typedef unsigned int Uint; typedef int Sint; #define SWORD_CONSTANT(Const) Const #define UWORD_CONSTANT(Const) Const##U +#define ERTS_UWORD_MAX UINT_MAX #define ERTS_SIZEOF_ETERM SIZEOF_INT #define ErtsStrToSint strtol #elif SIZEOF_VOID_P == SIZEOF_LONG_LONG @@ -320,6 +325,7 @@ typedef unsigned long long Uint; typedef long long Sint; #define SWORD_CONSTANT(Const) Const##LL #define UWORD_CONSTANT(Const) Const##ULL +#define ERTS_UWORD_MAX ULLONG_MAX #define ERTS_SIZEOF_ETERM SIZEOF_LONG_LONG #if defined(__WIN32__) #define ErtsStrToSint _strtoi64 -- cgit v1.2.3 From 78a5dd69c9ed1749f2ad4fd24151e4aa784b1cba Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 10 Oct 2013 12:11:29 +0200 Subject: trapping lists and tuples --- erts/emulator/beam/external.c | 153 +++++++++++++++++++++++++++++++++++------- erts/emulator/beam/sys.h | 6 ++ 2 files changed, 133 insertions(+), 26 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index b1cdad1343..48536d610b 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1125,7 +1125,18 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2) } -enum B2TState { /*B2TUncompress,*/ B2TSize, B2TDecodeInit, B2TDecode, B2TDecodeFail, B2TBadArg, B2TDone }; +enum B2TState { + /*B2TUncompress,*/ + B2TSize, + B2TDecodeInit, + B2TDecode, + B2TDecodeList, + B2TDecodeTuple, + + B2TDone, + B2TDecodeFail, + B2TBadArg +}; typedef struct { } B2TSizeContext; @@ -1137,6 +1148,10 @@ typedef struct { Eterm* hp_start; Eterm* hp; Eterm* hp_end; + int remaining_n; +#ifdef DEBUG + Eterm* container_start; +#endif } B2TDecodeContext; typedef struct { @@ -1153,7 +1168,7 @@ typedef struct B2TContext_t { byte* aligned_alloc; ErtsBinary2TermState b2ts; //Uint ext_size; - Uint reds; + SWord reds; Eterm trap_bin; enum B2TState state; union { @@ -1309,6 +1324,12 @@ BIF_RETTYPE binary_to_term_1(BIF_ALIST_1) #define B2T_BYTES_PER_REDUCTION 100 +static unsigned sverk_rand(void) +{ + static unsigned prev = 17; + prev = (prev * 214013 + 2531011); + return prev; +} static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) { @@ -1326,7 +1347,7 @@ static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) } else { ctx = ERTS_MAGIC_BIN_DATA(context_b); } - ctx->reds = 1; /*(Uint)(ERTS_BIF_REDS_LEFT(p) * B2T_BYTES_PER_REDUCTION);*/ + ctx->reds = 1 + sverk_rand() % 4; /*(Uint)(ERTS_BIF_REDS_LEFT(p) * B2T_BYTES_PER_REDUCTION);*/ do { switch (ctx->state) { @@ -1360,6 +1381,10 @@ static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) /*fall through*/ case B2TDecodeInit: if (context_b == NULL && ctx->b2ts.extsize > ctx->reds) { + /*SVERK Can we do a better prediction that is still safe + OR is there a way to do HAlloc after result some how... + ... support for mutiple HReleases maybe? + */ /* dec_term will probably trap, allocate space for magic bin before result term to make it easy to trim with HRelease. */ @@ -1374,7 +1399,9 @@ static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) ctx->u.dc.hp_end = ctx->u.dc.hp_start + ctx->heap_size; ctx->state = B2TDecode; /*fall through*/ - case B2TDecode: + case B2TDecode: + case B2TDecodeList: + case B2TDecodeTuple: dec_term(NULL, NULL, NULL, &MSO(p), NULL, ctx); break; @@ -1404,7 +1431,7 @@ static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) return ctx->u.dc.res; } - }while (ctx->reds); + }while (ctx->reds || ctx->state >= B2TDone); if (context_b == NULL) { ASSERT(ctx->trap_bin == THE_NON_VALUE); @@ -1415,7 +1442,7 @@ static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) sys_memcpy(ctx, &c_buff, sizeof(B2TContext)); if (!magic_space) { - ASSERT(ctx->state != B2TDecode); + ASSERT(ctx->state < B2TDecode); magic_space = HAlloc(p, PROC_BIN_SIZE); } ctx->trap_bin = erts_mk_magic_binary_term(&magic_space, &MSO(p), context_b); @@ -2778,28 +2805,81 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, ErtsAtomEncoding char_enc; register Eterm* hp; /* Please don't take the address of hp */ Eterm* next; - byte* ep_trap_limit; + SWord reds; if (ctx) { + reds = ctx->reds; + next = ctx->u.dc.next; + + if (ctx->state != B2TDecode) { + n = ctx->u.dc.remaining_n; + if (reds < n) { + ctx->u.dc.remaining_n -= reds; + n = reds; + } + else { + ctx->u.dc.remaining_n = 0; + } + reds -= n; + + switch (ctx->state) { + case B2TDecodeList: + objp = next - 2; + while (n > 0) { + objp[0] = (Eterm) COMPRESS_POINTER(next); + objp[1] = make_list(next); + next = objp; + objp -= 2; + n--; + } + break; + + case B2TDecodeTuple: + objp = next - 1; + while (n-- > 0) { + objp[0] = (Eterm) COMPRESS_POINTER(next); + next = objp; + objp--; + } + break; + + default: + ASSERT(!"Unknown state"); + } + if (ctx->u.dc.remaining_n) { + ctx->u.dc.next = next; + ctx->reds = 0; + return NULL; + } + ASSERT(next == ctx->u.dc.container_start); + ctx->state = B2TDecode; + } + hp_saved = ctx->u.dc.hp_start; ep = ctx->u.dc.ep; - ep_trap_limit = ep + ctx->reds; - if (ep_trap_limit < ep) { - ep_trap_limit = (byte*)ERTS_UWORD_MAX; /*SVERK Is there a safe way to create a "largest ptr" */ - } - next = ctx->u.dc.next; hpp = &ctx->u.dc.hp; } else { hp_saved = *hpp; - ep_trap_limit = (byte*)ERTS_UWORD_MAX; /*SVERK - " - */ + reds = ERTS_SWORD_MAX; next = objp; *next = (Eterm) (UWord) NULL; } - ASSERT(ep < ep_trap_limit); hp = *hpp; while (next != NULL) { + + if (reds <= 0) { + if (ctx) { + ctx->u.dc.ep = ep; + ctx->u.dc.next = next; + ctx->u.dc.hp = hp; + ctx->reds = 0; + return NULL; + } + reds = ERTS_SWORD_MAX; + } + objp = next; next = (Eterm *) EXPAND_POINTER(*objp); @@ -2918,8 +2998,20 @@ dec_term_atom_common: tuple_loop: *objp = make_tuple(hp); *hp++ = make_arityval(n); + #ifdef DEBUG + if (ctx) ctx->u.dc.container_start = hp; + #endif hp += n; - objp = hp - 1; + objp = hp - 1; + if (ctx) { + if (reds < n) { + ASSERT(reds > 0); + ctx->state = B2TDecodeTuple; + ctx->u.dc.remaining_n = n - reds; + n = reds; + } + reds -= n; + } while (n-- > 0) { objp[0] = (Eterm) COMPRESS_POINTER(next); next = objp; @@ -2937,17 +3029,30 @@ dec_term_atom_common: break; } *objp = make_list(hp); - hp += 2*n; + #ifdef DEBUG + if (ctx) ctx->u.dc.container_start = hp; + #endif + hp += 2 * n; objp = hp - 2; objp[0] = (Eterm) COMPRESS_POINTER((objp+1)); objp[1] = (Eterm) COMPRESS_POINTER(next); next = objp; objp -= 2; - while (--n > 0) { + n--; + if (ctx) { + if (reds < n) { + ctx->state = B2TDecodeList; + ctx->u.dc.remaining_n = n - reds; + n = reds; + } + reds -= n; + } + while (n > 0) { objp[0] = (Eterm) COMPRESS_POINTER(next); - objp[1] = make_list(objp + 2); + objp[1] = make_list(next); next = objp; objp -= 2; + n--; } break; case STRING_EXT: @@ -3494,20 +3599,16 @@ dec_term_atom_common: *hpp = hp_saved; if (ctx) { ctx->state = B2TDecodeFail; + ctx->reds = reds; } return NULL; } - if (ep > ep_trap_limit) { - ASSERT(ctx); - ctx->u.dc.ep = ep; - ctx->u.dc.next = next; - ctx->u.dc.hp = hp; - ctx->reds = 0; - return NULL; - } + + --reds; } if (ctx) { ctx->state = B2TDone; + ctx->reds = reds; } *hpp = hp; return ep; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 11ba554a3d..0f4169c81d 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -283,18 +283,21 @@ typedef long SWord; #define SWORD_CONSTANT(Const) Const##L #define UWORD_CONSTANT(Const) Const##UL #define ERTS_UWORD_MAX ULONG_MAX +#define ERTS_SWORD_MAX LONG_MAX #elif SIZEOF_VOID_P == SIZEOF_INT typedef unsigned int UWord; typedef int SWord; #define SWORD_CONSTANT(Const) Const #define UWORD_CONSTANT(Const) Const##U #define ERTS_UWORD_MAX UINT_MAX +#define ERTS_SWORD_MAX INT_MAX #elif SIZEOF_VOID_P == SIZEOF_LONG_LONG typedef unsigned long long UWord; typedef long long SWord; #define SWORD_CONSTANT(Const) Const##LL #define UWORD_CONSTANT(Const) Const##ULL #define ERTS_UWORD_MAX ULLONG_MAX +#define ERTS_SWORD_MAX LLONG_MAX #else #error Found no appropriate type to use for 'Eterm', 'Uint' and 'Sint' #endif @@ -308,6 +311,7 @@ typedef long Sint; #define SWORD_CONSTANT(Const) Const##L #define UWORD_CONSTANT(Const) Const##UL #define ERTS_UWORD_MAX ULONG_MAX +#define ERTS_SWORD_MAX LONG_MAX #define ERTS_SIZEOF_ETERM SIZEOF_LONG #define ErtsStrToSint strtol #elif SIZEOF_VOID_P == SIZEOF_INT @@ -317,6 +321,7 @@ typedef int Sint; #define SWORD_CONSTANT(Const) Const #define UWORD_CONSTANT(Const) Const##U #define ERTS_UWORD_MAX UINT_MAX +#define ERTS_SWORD_MAX INT_MAX #define ERTS_SIZEOF_ETERM SIZEOF_INT #define ErtsStrToSint strtol #elif SIZEOF_VOID_P == SIZEOF_LONG_LONG @@ -326,6 +331,7 @@ typedef long long Sint; #define SWORD_CONSTANT(Const) Const##LL #define UWORD_CONSTANT(Const) Const##ULL #define ERTS_UWORD_MAX ULLONG_MAX +#define ERTS_SWORD_MAX LLONG_MAX #define ERTS_SIZEOF_ETERM SIZEOF_LONG_LONG #if defined(__WIN32__) #define ErtsStrToSint _strtoi64 -- cgit v1.2.3 From d42bd3b37d3b741d546d94fd7611afda81bba419 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 10 Oct 2013 12:11:59 +0200 Subject: trapping STRING_EXT --- erts/emulator/beam/external.c | 84 ++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 29 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 48536d610b..2116c25cd2 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1125,13 +1125,15 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2) } -enum B2TState { +enum B2TState { /* order is somewhat significant */ /*B2TUncompress,*/ B2TSize, B2TDecodeInit, + B2TDecode, B2TDecodeList, B2TDecodeTuple, + B2TDecodeString, B2TDone, B2TDecodeFail, @@ -1150,7 +1152,7 @@ typedef struct { Eterm* hp_end; int remaining_n; #ifdef DEBUG - Eterm* container_start; + Eterm* container_end; #endif } B2TDecodeContext; @@ -1402,6 +1404,7 @@ static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) case B2TDecode: case B2TDecodeList: case B2TDecodeTuple: + case B2TDecodeString: dec_term(NULL, NULL, NULL, &MSO(p), NULL, ctx); break; @@ -2808,8 +2811,11 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, SWord reds; if (ctx) { - reds = ctx->reds; - next = ctx->u.dc.next; + hp_saved = ctx->u.dc.hp_start; + reds = ctx->reds; + next = ctx->u.dc.next; + ep = ctx->u.dc.ep; + hpp = &ctx->u.dc.hp; if (ctx->state != B2TDecode) { n = ctx->u.dc.remaining_n; @@ -2832,6 +2838,7 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, objp -= 2; n--; } + ASSERT(ctx->u.dc.remaining_n || next == ctx->u.dc.container_end); break; case B2TDecodeTuple: @@ -2841,23 +2848,35 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, next = objp; objp--; } + ASSERT(ctx->u.dc.remaining_n || next == ctx->u.dc.container_end); + break; + + case B2TDecodeString: + hp = *hpp; + hp[-1] = make_list(hp); /* overwrite the premature NIL */ + while (n-- > 0) { + hp[0] = make_small(*ep++); + hp[1] = make_list(hp+2); + hp += 2; + } + hp[-1] = NIL; + *hpp = hp; + ASSERT(ctx->u.dc.remaining_n || hp == ctx->u.dc.container_end); break; default: ASSERT(!"Unknown state"); } - if (ctx->u.dc.remaining_n) { + if (!ctx->u.dc.remaining_n) { + ctx->state = B2TDecode; + } + if (reds <= 0) { ctx->u.dc.next = next; + ctx->u.dc.ep = ep; ctx->reds = 0; return NULL; } - ASSERT(next == ctx->u.dc.container_start); - ctx->state = B2TDecode; } - - hp_saved = ctx->u.dc.hp_start; - ep = ctx->u.dc.ep; - hpp = &ctx->u.dc.hp; } else { hp_saved = *hpp; @@ -2869,17 +2888,6 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, while (next != NULL) { - if (reds <= 0) { - if (ctx) { - ctx->u.dc.ep = ep; - ctx->u.dc.next = next; - ctx->u.dc.hp = hp; - ctx->reds = 0; - return NULL; - } - reds = ERTS_SWORD_MAX; - } - objp = next; next = (Eterm *) EXPAND_POINTER(*objp); @@ -2998,13 +3006,11 @@ dec_term_atom_common: tuple_loop: *objp = make_tuple(hp); *hp++ = make_arityval(n); - #ifdef DEBUG - if (ctx) ctx->u.dc.container_start = hp; - #endif hp += n; objp = hp - 1; if (ctx) { if (reds < n) { + IF_DEBUG(ctx->u.dc.container_end = hp - n;) ASSERT(reds > 0); ctx->state = B2TDecodeTuple; ctx->u.dc.remaining_n = n - reds; @@ -3029,9 +3035,6 @@ dec_term_atom_common: break; } *objp = make_list(hp); - #ifdef DEBUG - if (ctx) ctx->u.dc.container_start = hp; - #endif hp += 2 * n; objp = hp - 2; objp[0] = (Eterm) COMPRESS_POINTER((objp+1)); @@ -3041,6 +3044,7 @@ dec_term_atom_common: n--; if (ctx) { if (reds < n) { + IF_DEBUG(ctx->u.dc.container_end = hp - 2*(n+1);) ctx->state = B2TDecodeList; ctx->u.dc.remaining_n = n - reds; n = reds; @@ -3063,6 +3067,15 @@ dec_term_atom_common: break; } *objp = make_list(hp); + if (ctx) { + if (reds < n) { + IF_DEBUG(ctx->u.dc.container_end = hp + n*2;) + ctx->state = B2TDecodeString; + ctx->u.dc.remaining_n = n - reds; + n = reds; + } + reds -= n; + } while (n-- > 0) { hp[0] = make_small(*ep++); hp[1] = make_list(hp+2); @@ -3604,7 +3617,20 @@ dec_term_atom_common: return NULL; } - --reds; + if (--reds <= 0) { + if (ctx) { + if (next || ctx->state != B2TDecode) { + ctx->u.dc.ep = ep; + ctx->u.dc.next = next; + ctx->u.dc.hp = hp; + ctx->reds = 0; + return NULL; + } + } + else { + reds = ERTS_SWORD_MAX; + } + } } if (ctx) { ctx->state = B2TDone; -- cgit v1.2.3 From 74c5c0314d4cf85365d89c1fdf41a5d286a7191a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 10 Oct 2013 19:59:09 +0200 Subject: trapping binary_to_term/2 --- erts/emulator/beam/external.c | 73 +++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 47 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 2116c25cd2..38280b7a99 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -108,7 +108,7 @@ static int encode_size_struct_int(Process *p, ErtsAtomCacheMap *acmp, Eterm obj, static Export binary_to_term_trap_export; static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1); -static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b); +static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b); void erts_init_external(void) { #if 1 /* In R16 */ @@ -1169,7 +1169,7 @@ typedef struct B2TContext_t { Sint heap_size; byte* aligned_alloc; ErtsBinary2TermState b2ts; - //Uint ext_size; + Uint32 flags; SWord reds; Eterm trap_bin; enum B2TState state; @@ -1316,15 +1316,22 @@ static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1) Binary *context_bin = ((ProcBin *) binary_val(BIF_ARG_1))->val; ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(context_bin) == b2t_context_destructor); - return binary_to_term_int(BIF_P, THE_NON_VALUE, context_bin); + return binary_to_term_int(BIF_P, 0, THE_NON_VALUE, context_bin); } BIF_RETTYPE binary_to_term_1(BIF_ALIST_1) { - return binary_to_term_int(BIF_P, BIF_ARG_1, NULL); +/*SVERK if (++sverk_cnt % 1000 == 0) { + erts_fprintf(stderr, "Call #%u to binary_to_term_int()\n", sverk_cnt); + } + if (sverk_cnt == 1767) { + sverk_break(); + } +*/ + return binary_to_term_int(BIF_P, 0, BIF_ARG_1, NULL); } -#define B2T_BYTES_PER_REDUCTION 100 +#define B2T_BYTES_PER_REDUCTION 128 static unsigned sverk_rand(void) { @@ -1333,11 +1340,13 @@ static unsigned sverk_rand(void) return prev; } -static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) +static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b) { + SWord initial_reds = 1 + sverk_rand() % 4; /*(Uint)(ERTS_BIF_REDS_LEFT(p) * B2T_BYTES_PER_REDUCTION);*/ byte* bytes; B2TContext c_buff; B2TContext *ctx; + ErtsDistExternal fakedep; Eterm* magic_space = NULL; if (context_b == NULL) { @@ -1345,11 +1354,12 @@ static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) ctx = &c_buff; ctx->state = B2TSize; ctx->aligned_alloc = NULL; + ctx->flags = flags; IF_DEBUG(ctx->trap_bin = THE_NON_VALUE;) } else { ctx = ERTS_MAGIC_BIN_DATA(context_b); } - ctx->reds = 1 + sverk_rand() % 4; /*(Uint)(ERTS_BIF_REDS_LEFT(p) * B2T_BYTES_PER_REDUCTION);*/ + ctx->reds = initial_reds; do { switch (ctx->state) { @@ -1405,7 +1415,8 @@ static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) case B2TDecodeList: case B2TDecodeTuple: case B2TDecodeString: - dec_term(NULL, NULL, NULL, &MSO(p), NULL, ctx); + fakedep.flags = ctx->flags; + dec_term(&fakedep, NULL, NULL, &MSO(p), NULL, ctx); break; case B2TDecodeFail: @@ -1416,6 +1427,7 @@ static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) if (context_b) { erts_set_gc_state(p, 1); } + BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION); BIF_ERROR(p, BADARG); case B2TDone: @@ -1431,6 +1443,7 @@ static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) erts_set_gc_state(p, 1); } + BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION); return ctx->u.dc.res; } @@ -1461,24 +1474,15 @@ static Eterm binary_to_term_int(Process* p, Eterm bin, Binary* context_b) BIF_RETTYPE binary_to_term_2(BIF_ALIST_2) { - Sint heap_size; - Eterm res; Eterm opts; Eterm opt; - Eterm* hp; - Eterm* endp; - Sint size; - byte* bytes; - byte* temp_alloc = NULL; - ErtsBinary2TermState b2ts; - ErtsDistExternal fakedep; + Uint32 flags = 0; - fakedep.flags = 0; opts = BIF_ARG_2; while (is_list(opts)) { opt = CAR(list_val(opts)); if (opt == am_safe) { - fakedep.flags |= ERTS_DIST_EXT_BTT_SAFE; + flags |= ERTS_DIST_EXT_BTT_SAFE; } else { goto error; @@ -1489,35 +1493,10 @@ BIF_RETTYPE binary_to_term_2(BIF_ALIST_2) if (is_not_nil(opts)) goto error; - if ((bytes = erts_get_aligned_binary_bytes(BIF_ARG_1, &temp_alloc)) == NULL) { - error: - erts_free_aligned_binary_bytes(temp_alloc); - BIF_ERROR(BIF_P, BADARG); - } - size = binary_size(BIF_ARG_1); - - heap_size = binary2term_prepare(&b2ts, bytes, size); - if (heap_size < 0) - goto error; - - hp = HAlloc(BIF_P, heap_size); - endp = hp + heap_size; - - res = binary2term_create(&fakedep, &b2ts, &hp, &MSO(BIF_P)); - - erts_free_aligned_binary_bytes(temp_alloc); - - if (hp > endp) { - erl_exit(1, ":%s, line %d: heap overrun by %d words(s)\n", - __FILE__, __LINE__, hp-endp); - } - - HRelease(BIF_P, endp, hp); - - if (res == THE_NON_VALUE) - goto error; + return binary_to_term_int(BIF_P, flags, BIF_ARG_1, NULL); - return res; +error: + BIF_ERROR(BIF_P, BADARG); } Eterm -- cgit v1.2.3 From 38f1140d197e14f6cb4d0040859b0b19876d2a14 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 11 Oct 2013 16:28:59 +0200 Subject: trapping size calculation --- erts/emulator/beam/external.c | 175 +++++++++++++++++++++++++++--------------- 1 file changed, 114 insertions(+), 61 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 38280b7a99..d4ffc23536 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -96,7 +96,7 @@ struct B2TContext_t; static byte* dec_term(ErtsDistExternal *, Eterm**, byte*, ErlOffHeap*, Eterm*, struct B2TContext_t*); static byte* dec_atom(ErtsDistExternal *, byte*, Eterm*); static byte* dec_pid(ErtsDistExternal *, Eterm**, byte*, ErlOffHeap*, Eterm*); -static Sint decoded_size(byte *ep, byte* endp, int internal_tags); +static Sint decoded_size(byte *ep, byte* endp, int internal_tags, struct B2TContext_t*); static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1); static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint flags, @@ -889,7 +889,7 @@ erts_decode_dist_ext_size(ErtsDistExternal *edep) goto fail; ep = edep->extp+1; } - res = decoded_size(ep, edep->ext_endp, 0); + res = decoded_size(ep, edep->ext_endp, 0, NULL); if (res >= 0) return res; fail: @@ -901,12 +901,12 @@ Sint erts_decode_ext_size(byte *ext, Uint size) { if (size == 0 || *ext != VERSION_MAGIC) return -1; - return decoded_size(ext+1, ext+size, 0); + return decoded_size(ext+1, ext+size, 0, NULL); } Sint erts_decode_ext_size_ets(byte *ext, Uint size) { - Sint sz = decoded_size(ext, ext+size, 1); + Sint sz = decoded_size(ext, ext+size, 1, NULL); ASSERT(sz >= 0); return sz; } @@ -1126,7 +1126,7 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2) enum B2TState { /* order is somewhat significant */ - /*B2TUncompress,*/ + B2TPrepare, B2TSize, B2TDecodeInit, @@ -1141,6 +1141,10 @@ enum B2TState { /* order is somewhat significant */ }; typedef struct { + int heap_size; + int terms; + byte* ep; + int atom_extra_skip; } B2TSizeContext; typedef struct { @@ -1210,21 +1214,15 @@ static uLongf binary2term_uncomp_size(byte* data, Sint size) return err == Z_STREAM_END ? uncomp_size : 0; } -static ERTS_INLINE Sint +static ERTS_INLINE int binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size) { - Sint res; byte *bytes = data; Sint size = data_size; state->exttmp = 0; if (size < 1 || *bytes != VERSION_MAGIC) { - error: - if (state->exttmp) - erts_free(ERTS_ALC_T_EXT_TERM_DATA, state->extp); - state->extp = NULL; - state->exttmp = 0; return -1; } bytes++; @@ -1239,20 +1237,17 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size) if (dest_len > 32*1024*1024 || (state->extp = erts_alloc_fnf(ERTS_ALC_T_EXT_TERM_DATA, dest_len)) == NULL) { if (dest_len != binary2term_uncomp_size(bytes, size)) { - goto error; + return -1; } state->extp = erts_alloc(ERTS_ALC_T_EXT_TERM_DATA, dest_len); } state->exttmp = 1; if (erl_zlib_uncompress(state->extp, &dest_len, bytes, size) != Z_OK) - goto error; + return -1; size = (Sint) dest_len; } state->extsize = size; - res = decoded_size(state->extp, state->extp + size, 0); - if (res < 0) - goto error; - return res; + return 0; } static ERTS_INLINE void @@ -1280,7 +1275,18 @@ binary2term_create(ErtsDistExternal *edep, ErtsBinary2TermState *state, Eterm ** Sint erts_binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size) { - return binary2term_prepare(state, data, data_size); + Sint res; + + if (binary2term_prepare(state, data, data_size) < 0 || + (res=decoded_size(state->extp, state->extp + state->extsize, 0, NULL)) < 0) { + + if (state->exttmp) + erts_free(ERTS_ALC_T_EXT_TERM_DATA, state->extp); + state->extp = NULL; + state->exttmp = 0; + return -1; + } + return res; } void @@ -1319,17 +1325,6 @@ static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1) return binary_to_term_int(BIF_P, 0, THE_NON_VALUE, context_bin); } -BIF_RETTYPE binary_to_term_1(BIF_ALIST_1) -{ -/*SVERK if (++sverk_cnt % 1000 == 0) { - erts_fprintf(stderr, "Call #%u to binary_to_term_int()\n", sverk_cnt); - } - if (sverk_cnt == 1767) { - sverk_break(); - } -*/ - return binary_to_term_int(BIF_P, 0, BIF_ARG_1, NULL); -} #define B2T_BYTES_PER_REDUCTION 128 @@ -1352,18 +1347,19 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con if (context_b == NULL) { /* Setup enough to get started */ ctx = &c_buff; - ctx->state = B2TSize; + ctx->state = B2TPrepare; ctx->aligned_alloc = NULL; ctx->flags = flags; IF_DEBUG(ctx->trap_bin = THE_NON_VALUE;) } else { ctx = ERTS_MAGIC_BIN_DATA(context_b); + ASSERT(ctx->state != B2TPrepare); } ctx->reds = initial_reds; do { switch (ctx->state) { - case B2TSize: + case B2TPrepare: bytes = erts_get_aligned_binary_bytes_extra(bin, &ctx->aligned_alloc, ERTS_ALC_T_EXT_TERM_DATA, @@ -1381,16 +1377,19 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con break; } + ctx->reds = 0; /*SVERK*/ + ctx->u.sc.heap_size = 0; + ctx->u.sc.terms = 1; + ctx->u.sc.ep = ctx->b2ts.extp; + ctx->u.sc.atom_extra_skip = 0; + ctx->state = B2TSize; + break; + case B2TSize: + ctx->heap_size = decoded_size(NULL, ctx->b2ts.extp + ctx->b2ts.extsize, + 0, ctx); + break; - ctx->state = B2TDecodeInit; - - if (ctx->b2ts.extsize >= ctx->reds) { - ctx->reds = 0; - break; - } - ctx->reds -= ctx->b2ts.extsize; - /*fall through*/ case B2TDecodeInit: if (context_b == NULL && ctx->b2ts.extsize > ctx->reds) { /*SVERK Can we do a better prediction that is still safe @@ -1472,6 +1471,18 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con BIF_TRAP1(&binary_to_term_trap_export, p, ctx->trap_bin); } +BIF_RETTYPE binary_to_term_1(BIF_ALIST_1) +{ +/*SVERK if (++sverk_cnt % 1000 == 0) { + erts_fprintf(stderr, "Call #%u to binary_to_term_int()\n", sverk_cnt); + } + if (sverk_cnt == 1767) { + sverk_break(); + } +*/ + return binary_to_term_int(BIF_P, 0, BIF_ARG_1, NULL); +} + BIF_RETTYPE binary_to_term_2(BIF_ALIST_2) { Eterm opts; @@ -3901,18 +3912,32 @@ encode_size_struct_int(Process *p, ErtsAtomCacheMap *acmp, Eterm obj, } static Sint -decoded_size(byte *ep, byte* endp, int internal_tags) +decoded_size(byte *ep, byte* endp, int internal_tags, B2TContext* ctx) { int heap_size = 0; int terms; int atom_extra_skip = 0; Uint n; + SWord reds; + if (ctx) { + heap_size = ctx->u.sc.heap_size; + terms = ctx->u.sc.terms; + ep = ctx->u.sc.ep; + atom_extra_skip = ctx->u.sc.atom_extra_skip; + reds = ctx->reds; + } + else { + heap_size = 0; + terms = 1; + atom_extra_skip = 0; + reds = ERTS_SWORD_MAX; + } #define SKIP(sz) \ do { \ if ((sz) <= endp-ep) { \ ep += (sz); \ - } else { return -1; }; \ + } else { goto error; }; \ } while (0) #define SKIP2(sz1, sz2) \ @@ -3920,25 +3945,27 @@ decoded_size(byte *ep, byte* endp, int internal_tags) Uint sz = (sz1) + (sz2); \ if (sz1 < sz && (sz) <= endp-ep) { \ ep += (sz); \ - } else { return -1; } \ + } else { goto error; } \ } while (0) #define CHKSIZE(sz) \ do { \ - if ((sz) > endp-ep) { return -1; } \ + if ((sz) > endp-ep) { goto error; } \ } while (0) #define ADDTERMS(n) \ do { \ int before = terms; \ terms += (n); \ - if (terms < before) return -1; \ + if (terms < before) goto error; \ } while (0) - - for (terms=1; terms > 0; terms--) { - int tag; - + ASSERT(terms > 0); + do { + int tag; + /*SVERK + erts_fprintf(stderr, "SVERK tag=%d ep=%p terms=%d heap_size=%d endp=%p\n", + ep[0], ep, terms, heap_size, endp); */ CHKSIZE(1); tag = ep++[0]; switch (tag) { @@ -3959,7 +3986,7 @@ decoded_size(byte *ep, byte* endp, int internal_tags) CHKSIZE(4); n = get_int32(ep); if (n > BIG_ARITY_MAX*sizeof(ErtsDigit)) { - return -1; + goto error; } SKIP2(n,4+1); /* skip, size,sign,digits */ heap_size += 1+1+(n+sizeof(Eterm)-1)/sizeof(Eterm); /* XXX: 1 too much? */ @@ -3968,7 +3995,7 @@ decoded_size(byte *ep, byte* endp, int internal_tags) CHKSIZE(2); n = get_int16(ep); if (n > MAX_ATOM_CHARACTERS) { - return -1; + goto error; } SKIP(n+2+atom_extra_skip); atom_extra_skip = 0; @@ -3978,7 +4005,7 @@ decoded_size(byte *ep, byte* endp, int internal_tags) n = get_int16(ep); ep += 2; if (n > MAX_ATOM_SZ_LIMIT) { - return -1; + goto error; } SKIP(n+atom_extra_skip); atom_extra_skip = 0; @@ -3987,7 +4014,7 @@ decoded_size(byte *ep, byte* endp, int internal_tags) CHKSIZE(1); n = get_int8(ep); if (n > MAX_ATOM_CHARACTERS) { - return -1; + goto error; } SKIP(n+1+atom_extra_skip); atom_extra_skip = 0; @@ -3997,7 +4024,7 @@ decoded_size(byte *ep, byte* endp, int internal_tags) n = get_int8(ep); ep++; if (n > MAX_ATOM_SZ_LIMIT) { - return -1; + goto error; } SKIP(n+atom_extra_skip); atom_extra_skip = 0; @@ -4026,7 +4053,7 @@ decoded_size(byte *ep, byte* endp, int internal_tags) id_words = get_int16(ep); if (id_words > ERTS_MAX_REF_NUMBERS) - return -1; + goto error; ep += 2; atom_extra_skip = 1 + 4*id_words; @@ -4128,7 +4155,7 @@ decoded_size(byte *ep, byte* endp, int internal_tags) num_free = get_int32(ep); ep += 4; if (num_free > MAX_ARG) { - return -1; + goto error; } terms += 4 + num_free; heap_size += ERL_FUN_SIZE + num_free; @@ -4145,24 +4172,50 @@ decoded_size(byte *ep, byte* endp, int internal_tags) case BINARY_INTERNAL_REF: if (!internal_tags) { - return -1; + goto error; } SKIP(sizeof(ProcBin)); heap_size += PROC_BIN_SIZE; break; case BIT_BINARY_INTERNAL_REF: if (!internal_tags) { - return -1; + goto error; } SKIP(2+sizeof(ProcBin)); heap_size += PROC_BIN_SIZE + ERL_SUB_BIN_SIZE; break; default: - return -1; + goto error; } - } + terms--; + + if (--reds <= 0) { + if (ctx && terms > 0) { + ctx->u.sc.heap_size = heap_size; + ctx->u.sc.terms = terms; + ctx->u.sc.ep = ep; + ctx->u.sc.atom_extra_skip = atom_extra_skip; + ctx->reds = 0; + return 0; + } + reds = ERTS_SWORD_MAX; + } + }while (terms > 0); + /* 'terms' may be non-zero if it has wrapped around */ - return terms==0 ? heap_size : -1; + if (terms == 0) { + if (ctx) { + ctx->state = B2TDecodeInit; + ctx->reds = reds; + } + return heap_size; + } + +error: + if (ctx) { + ctx->state = B2TBadArg; + } + return -1; #undef SKIP #undef SKIP2 #undef CHKSIZE -- cgit v1.2.3 From f10ea68ce28e9b93ce614b5f829b1ca7f4cc753f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 14 Oct 2013 17:46:28 +0200 Subject: trapping uncompress --- erts/emulator/beam/erl_zlib.c | 40 ++++++++++++ erts/emulator/beam/erl_zlib.h | 6 ++ erts/emulator/beam/external.c | 146 ++++++++++++++++++++++++++++-------------- 3 files changed, 144 insertions(+), 48 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_zlib.c b/erts/emulator/beam/erl_zlib.c index 47fd92988e..8e33144f96 100644 --- a/erts/emulator/beam/erl_zlib.c +++ b/erts/emulator/beam/erl_zlib.c @@ -87,6 +87,46 @@ int ZEXPORT erl_zlib_deflate_finish(z_stream *streamp) return deflateEnd(streamp); } +int ZEXPORT erl_zlib_inflate_start(z_stream *streamp, const Bytef* source, + uLong sourceLen) +{ + streamp->next_in = (Bytef*)source; + streamp->avail_in = (uInt)sourceLen; + streamp->total_out = streamp->avail_out = 0; + streamp->next_out = NULL; + erl_zlib_alloc_init(streamp); + return inflateInit(streamp); +} +/* + * Inflate a chunk, The destination length is the limit. + * Returns Z_OK if more to process, Z_STREAM_END if we are done. + */ +int ZEXPORT erl_zlib_inflate_chunk(z_stream *streamp, Bytef* dest, uLongf* destLen) +{ + int err; + uLongf last_tot = streamp->total_out; + + streamp->next_out = dest; + streamp->avail_out = (uInt)*destLen; + + if ((uLong)streamp->avail_out != *destLen) return Z_BUF_ERROR; + + err = inflate(streamp, Z_NO_FLUSH); + ASSERT(err != Z_STREAM_ERROR); + *destLen = streamp->total_out - last_tot; + return err; +} + +/* + * When we are done, free up the inflate structure + * Retyurns Z_OK or Error + */ +int ZEXPORT erl_zlib_inflate_finish(z_stream *streamp) +{ + return inflateEnd(streamp); +} + + int ZEXPORT erl_zlib_compress2 (Bytef* dest, uLongf* destLen, const Bytef* source, uLong sourceLen, int level) diff --git a/erts/emulator/beam/erl_zlib.h b/erts/emulator/beam/erl_zlib.h index 5ac849d21c..160166c66b 100644 --- a/erts/emulator/beam/erl_zlib.h +++ b/erts/emulator/beam/erl_zlib.h @@ -39,6 +39,12 @@ int ZEXPORT erl_zlib_deflate_start(z_stream *streamp, const Bytef* source, int ZEXPORT erl_zlib_deflate_chunk(z_stream *streamp, Bytef* dest, uLongf* destLen); int ZEXPORT erl_zlib_deflate_finish(z_stream *streamp); +int ZEXPORT erl_zlib_inflate_start(z_stream *streamp, const Bytef* source, + uLong sourceLen); +int ZEXPORT erl_zlib_inflate_chunk(z_stream *streamp, Bytef* dest, uLongf* destLen); +int ZEXPORT erl_zlib_inflate_finish(z_stream *streamp); + + /* Use instead of compress */ #define erl_zlib_compress(dest,destLen,source,sourceLen) \ diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index d4ffc23536..0d2de1b199 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1127,6 +1127,8 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2) enum B2TState { /* order is somewhat significant */ B2TPrepare, + B2TUncompressChunk, + B2TSizeInit, B2TSize, B2TDecodeInit, @@ -1161,12 +1163,9 @@ typedef struct { } B2TDecodeContext; typedef struct { - /*Uint real_size; - Uint dest_len; - byte *dbytes; - Binary *result_bin; - Binary *destination_bin; - z_stream stream;*/ + z_stream stream; + byte* dbytes; + Uint dleft; } B2TUncompressContext; typedef struct B2TContext_t { @@ -1215,7 +1214,8 @@ static uLongf binary2term_uncomp_size(byte* data, Sint size) } static ERTS_INLINE int -binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size) +binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size, + B2TContext* ctx) { byte *bytes = data; Sint size = data_size; @@ -1229,6 +1229,8 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size) size--; if (size < 5 || *bytes != COMPRESSED) { state->extp = bytes; + if (ctx) + ctx->state = B2TSizeInit; } else { uLongf dest_len = (Uint32) get_int32(bytes+1); @@ -1236,14 +1238,33 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size) size -= 5; if (dest_len > 32*1024*1024 || (state->extp = erts_alloc_fnf(ERTS_ALC_T_EXT_TERM_DATA, dest_len)) == NULL) { + /* + * Try avoid out-of-memory crash due to corrupted 'dest_len' + * by checking the actual length of the uncompressed data. + * The only way to do that is to uncompress it. Sad but true. + */ if (dest_len != binary2term_uncomp_size(bytes, size)) { return -1; } state->extp = erts_alloc(ERTS_ALC_T_EXT_TERM_DATA, dest_len); + ctx->reds -= dest_len; } state->exttmp = 1; - if (erl_zlib_uncompress(state->extp, &dest_len, bytes, size) != Z_OK) - return -1; + if (ctx) { + if (erl_zlib_inflate_start(&ctx->u.uc.stream, bytes, size) != Z_OK) + return -1; + + ctx->u.uc.dbytes = state->extp; + ctx->u.uc.dleft = dest_len; + ctx->state = B2TUncompressChunk; + } + else { + uLongf dlen = dest_len; + if (erl_zlib_uncompress(state->extp, &dlen, bytes, size) != Z_OK + || dlen != dest_len) { + return -1; + } + } size = (Sint) dest_len; } state->extsize = size; @@ -1277,7 +1298,7 @@ erts_binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size { Sint res; - if (binary2term_prepare(state, data, data_size) < 0 || + if (binary2term_prepare(state, data, data_size, NULL) < 0 || (res=decoded_size(state->extp, state->extp + state->extsize, 0, NULL)) < 0) { if (state->exttmp) @@ -1307,6 +1328,9 @@ static void b2t_destroy_context(B2TContext* context) ERTS_ALC_T_EXT_TERM_DATA); context->aligned_alloc = NULL; binary2term_abort(&context->b2ts); + if (context->state == B2TUncompressChunk) { + erl_zlib_inflate_finish(&context->u.uc.stream); + } } static void b2t_context_destructor(Binary *context_bin) @@ -1359,7 +1383,8 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con do { switch (ctx->state) { - case B2TPrepare: + case B2TPrepare: { + Uint bin_size; bytes = erts_get_aligned_binary_bytes_extra(bin, &ctx->aligned_alloc, ERTS_ALC_T_EXT_TERM_DATA, @@ -1369,24 +1394,48 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con ctx->state = B2TBadArg; break; } - - ctx->heap_size = binary2term_prepare(&ctx->b2ts, bytes, - binary_size(bin)); - if (ctx->heap_size < 0) { - ctx->state = B2TBadArg; - break; + bin_size = binary_size(bin); + if (ctx->aligned_alloc) { + ctx->reds -= bin_size / 8; } - - ctx->reds = 0; /*SVERK*/ - ctx->u.sc.heap_size = 0; - ctx->u.sc.terms = 1; - ctx->u.sc.ep = ctx->b2ts.extp; - ctx->u.sc.atom_extra_skip = 0; - ctx->state = B2TSize; + if (binary2term_prepare(&ctx->b2ts, bytes, bin_size, ctx) < 0) { + ctx->state = B2TBadArg; + } break; + } + case B2TUncompressChunk: + { + Uint chunk = ctx->reds; + int zret; + if (chunk > ctx->u.uc.dleft) + chunk = ctx->u.uc.dleft; + + zret = erl_zlib_inflate_chunk(&ctx->u.uc.stream, + ctx->u.uc.dbytes, &chunk); + ctx->u.uc.dbytes += chunk; + ctx->u.uc.dleft -= chunk; + if (zret == Z_OK && ctx->u.uc.dleft > 0) { + ctx->reds = 0; + } + else if (erl_zlib_inflate_finish(&ctx->u.uc.stream) == Z_OK + && zret == Z_STREAM_END + && ctx->u.uc.dleft == 0) { + ctx->reds -= chunk; + ctx->state = B2TSizeInit; + } + else { + ctx->state = B2TBadArg; + } + break; + } + case B2TSizeInit: + ctx->u.sc.ep = NULL; + ctx->state = B2TSize; + /*fall through*/ case B2TSize: - ctx->heap_size = decoded_size(NULL, ctx->b2ts.extp + ctx->b2ts.extsize, + ctx->heap_size = decoded_size(ctx->b2ts.extp, + ctx->b2ts.extp + ctx->b2ts.extsize, 0, ctx); break; @@ -1455,7 +1504,9 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con b2t_context_destructor); ctx = ERTS_MAGIC_BIN_DATA(context_b); sys_memcpy(ctx, &c_buff, sizeof(B2TContext)); - + if (ctx->state >= B2TDecode && ctx->u.dc.next == &c_buff.u.dc.res) { + ctx->u.dc.next = &ctx->u.dc.res; + } if (!magic_space) { ASSERT(ctx->state < B2TDecode); magic_space = HAlloc(p, PROC_BIN_SIZE); @@ -3914,25 +3965,27 @@ encode_size_struct_int(Process *p, ErtsAtomCacheMap *acmp, Eterm obj, static Sint decoded_size(byte *ep, byte* endp, int internal_tags, B2TContext* ctx) { - int heap_size = 0; + int heap_size; int terms; - int atom_extra_skip = 0; + int atom_extra_skip; Uint n; SWord reds; if (ctx) { - heap_size = ctx->u.sc.heap_size; - terms = ctx->u.sc.terms; - ep = ctx->u.sc.ep; - atom_extra_skip = ctx->u.sc.atom_extra_skip; reds = ctx->reds; + if (ctx->u.sc.ep) { + heap_size = ctx->u.sc.heap_size; + terms = ctx->u.sc.terms; + ep = ctx->u.sc.ep; + atom_extra_skip = ctx->u.sc.atom_extra_skip; + goto init_done; + } } - else { - heap_size = 0; - terms = 1; - atom_extra_skip = 0; - reds = ERTS_SWORD_MAX; - } + heap_size = 0; + terms = 1; + atom_extra_skip = 0; +init_done: + #define SKIP(sz) \ do { \ if ((sz) <= endp-ep) { \ @@ -4189,16 +4242,13 @@ decoded_size(byte *ep, byte* endp, int internal_tags, B2TContext* ctx) } terms--; - if (--reds <= 0) { - if (ctx && terms > 0) { - ctx->u.sc.heap_size = heap_size; - ctx->u.sc.terms = terms; - ctx->u.sc.ep = ep; - ctx->u.sc.atom_extra_skip = atom_extra_skip; - ctx->reds = 0; - return 0; - } - reds = ERTS_SWORD_MAX; + if (ctx && --reds <= 0 && terms > 0) { + ctx->u.sc.heap_size = heap_size; + ctx->u.sc.terms = terms; + ctx->u.sc.ep = ep; + ctx->u.sc.atom_extra_skip = atom_extra_skip; + ctx->reds = 0; + return 0; } }while (terms > 0); -- cgit v1.2.3 From d5b6c6f0bd96108d788cdfb9be15059125b3d87f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 16 Oct 2013 16:28:21 +0200 Subject: erts: Add erlang wrappers to binary_to_term to not expose the trapping BIF in the stacktrace when it throws badarg. --- erts/emulator/beam/bif.tab | 8 ++------ erts/emulator/beam/external.c | 6 +++--- erts/preloaded/ebin/erlang.beam | Bin 97500 -> 97740 bytes erts/preloaded/ebin/erts_internal.beam | Bin 3780 -> 4084 bytes erts/preloaded/src/erlang.erl | 18 ++++++++++++++---- erts/preloaded/src/erts_internal.erl | 12 +++++++++++- 6 files changed, 30 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 3b7bb56885..9f3de5f780 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -45,7 +45,6 @@ bif erlang:apply/3 bif erlang:atom_to_list/1 bif erlang:binary_to_list/1 bif erlang:binary_to_list/3 -bif erlang:binary_to_term/1 bif erlang:crc32/1 bif erlang:crc32/2 bif erlang:crc32_combine/3 @@ -152,6 +151,8 @@ bif erts_internal:port_command/3 bif erts_internal:port_control/3 bif erts_internal:port_close/1 bif erts_internal:port_connect/2 +bif erts_internal:binary_to_term/1 +bif erts_internal:binary_to_term/2 bif erts_internal:request_system_task/3 bif erts_internal:check_process_code/2 @@ -478,11 +479,6 @@ bif erlang:load_nif/2 bif erlang:call_on_load_function/1 bif erlang:finish_after_on_load/2 -# -# New Bifs in R13B4 -# -bif erlang:binary_to_term/2 - # # The binary match bifs (New in R14A - EEP9) # diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 0d2de1b199..e3e199b198 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1476,7 +1476,7 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con erts_set_gc_state(p, 1); } BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION); - BIF_ERROR(p, BADARG); + BIF_ERROR(p, BADARG & ~EXF_SAVETRACE); case B2TDone: b2t_destroy_context(ctx); @@ -1522,7 +1522,7 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con BIF_TRAP1(&binary_to_term_trap_export, p, ctx->trap_bin); } -BIF_RETTYPE binary_to_term_1(BIF_ALIST_1) +BIF_RETTYPE erts_internal_binary_to_term_1(BIF_ALIST_1) { /*SVERK if (++sverk_cnt % 1000 == 0) { erts_fprintf(stderr, "Call #%u to binary_to_term_int()\n", sverk_cnt); @@ -1534,7 +1534,7 @@ BIF_RETTYPE binary_to_term_1(BIF_ALIST_1) return binary_to_term_int(BIF_P, 0, BIF_ARG_1, NULL); } -BIF_RETTYPE binary_to_term_2(BIF_ALIST_2) +BIF_RETTYPE erts_internal_binary_to_term_2(BIF_ALIST_2) { Eterm opts; Eterm opt; diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 159c91e37c..63fa535427 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 9d8a4cfd00..6c7dcff420 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index e521c6fc3d..7b59f6c8b4 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -362,15 +362,25 @@ binary_to_list(_Binary, _Start, _Stop) -> %% 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(Binary) -> + %% This BIF may throw badarg while trapping + try + erts_internal:binary_to_term(Binary) + catch + error:Reason -> erlang:error(Reason,[Binary]) + end. %% 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). +binary_to_term(Binary, Opts) -> + %% This BIF may throw badarg while trapping + try + erts_internal:binary_to_term(Binary,Opts) + catch + error:Reason -> erlang:error(Reason,[Binary,Opts]) + end. %% bit_size/1 %% Shadowed by erl_bif_types: erlang:bit_size/1 diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index c8e8e7e069..d6a185482e 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -29,7 +29,7 @@ -module(erts_internal). -export([await_port_send_result/3]). - +-export([binary_to_term/1, binary_to_term/2]). -export([port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). @@ -160,3 +160,13 @@ request_system_task(_Pid, _Prio, _Request) -> check_process_code(_Module, _OptionList) -> erlang:nif_error(undefined). +-spec binary_to_term(Binary) -> term() when + Binary :: binary(). +binary_to_term(_Binary) -> + erlang:nif_error(undefined). + +-spec binary_to_term(Binary, Opts) -> term() when + Binary :: binary(), + Opts :: [safe]. +binary_to_term(_Binary, _Opts) -> + erlang:nif_error(undefined). -- cgit v1.2.3 From abbc9c2755396d4050db8f15e02c21032a528049 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 16 Oct 2013 16:28:40 +0200 Subject: erts: Cleanup code for trapping binary_to_term --- erts/emulator/beam/external.c | 103 ++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 58 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index e3e199b198..651aac03a2 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1131,7 +1131,6 @@ enum B2TState { /* order is somewhat significant */ B2TSizeInit, B2TSize, B2TDecodeInit, - B2TDecode, B2TDecodeList, B2TDecodeTuple, @@ -1157,9 +1156,6 @@ typedef struct { Eterm* hp; Eterm* hp_end; int remaining_n; -#ifdef DEBUG - Eterm* container_end; -#endif } B2TDecodeContext; typedef struct { @@ -1352,20 +1348,28 @@ static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1) #define B2T_BYTES_PER_REDUCTION 128 -static unsigned sverk_rand(void) +/* Define for testing */ +/*#define EXTREME_B2T_TRAPPING 1*/ + +#ifdef EXTREME_B2T_TRAPPING +static unsigned b2t_rand(void) { static unsigned prev = 17; prev = (prev * 214013 + 2531011); return prev; } +#endif + static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b) { - SWord initial_reds = 1 + sverk_rand() % 4; /*(Uint)(ERTS_BIF_REDS_LEFT(p) * B2T_BYTES_PER_REDUCTION);*/ - byte* bytes; +#ifdef EXTREME_B2T_TRAPPING + SWord initial_reds = 1 + b2t_rand() % 4; +#else + SWord initial_reds = (Uint)(ERTS_BIF_REDS_LEFT(p) * B2T_BYTES_PER_REDUCTION); +#endif B2TContext c_buff; B2TContext *ctx; - ErtsDistExternal fakedep; Eterm* magic_space = NULL; if (context_b == NULL) { @@ -1384,6 +1388,7 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con do { switch (ctx->state) { case B2TPrepare: { + byte* bytes; Uint bin_size; bytes = erts_get_aligned_binary_bytes_extra(bin, &ctx->aligned_alloc, @@ -1403,32 +1408,30 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con } break; } - case B2TUncompressChunk: - { - Uint chunk = ctx->reds; - int zret; - if (chunk > ctx->u.uc.dleft) - chunk = ctx->u.uc.dleft; - - zret = erl_zlib_inflate_chunk(&ctx->u.uc.stream, - ctx->u.uc.dbytes, &chunk); - ctx->u.uc.dbytes += chunk; - ctx->u.uc.dleft -= chunk; - if (zret == Z_OK && ctx->u.uc.dleft > 0) { - ctx->reds = 0; - } - else if (erl_zlib_inflate_finish(&ctx->u.uc.stream) == Z_OK - && zret == Z_STREAM_END - && ctx->u.uc.dleft == 0) { - ctx->reds -= chunk; - ctx->state = B2TSizeInit; - } - else { - ctx->state = B2TBadArg; - } - break; - } - + case B2TUncompressChunk: { + Uint chunk = ctx->reds; + int zret; + + if (chunk > ctx->u.uc.dleft) + chunk = ctx->u.uc.dleft; + zret = erl_zlib_inflate_chunk(&ctx->u.uc.stream, + ctx->u.uc.dbytes, &chunk); + ctx->u.uc.dbytes += chunk; + ctx->u.uc.dleft -= chunk; + if (zret == Z_OK && ctx->u.uc.dleft > 0) { + ctx->reds = 0; + } + else if (erl_zlib_inflate_finish(&ctx->u.uc.stream) == Z_OK + && zret == Z_STREAM_END + && ctx->u.uc.dleft == 0) { + ctx->reds -= chunk; + ctx->state = B2TSizeInit; + } + else { + ctx->state = B2TBadArg; + } + break; + } case B2TSizeInit: ctx->u.sc.ep = NULL; ctx->state = B2TSize; @@ -1441,11 +1444,7 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con case B2TDecodeInit: if (context_b == NULL && ctx->b2ts.extsize > ctx->reds) { - /*SVERK Can we do a better prediction that is still safe - OR is there a way to do HAlloc after result some how... - ... support for mutiple HReleases maybe? - */ - /* dec_term will probably trap, allocate space for magic bin + /* dec_term will maybe trap, allocate space for magic bin before result term to make it easy to trim with HRelease. */ magic_space = HAlloc(p, PROC_BIN_SIZE); @@ -1462,11 +1461,12 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con case B2TDecode: case B2TDecodeList: case B2TDecodeTuple: - case B2TDecodeString: + case B2TDecodeString: { + ErtsDistExternal fakedep; fakedep.flags = ctx->flags; dec_term(&fakedep, NULL, NULL, &MSO(p), NULL, ctx); break; - + } case B2TDecodeFail: HRelease(p, ctx->u.dc.hp_end, ctx->u.dc.hp_start); /*fall through*/ @@ -1495,7 +1495,7 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con return ctx->u.dc.res; } - }while (ctx->reds || ctx->state >= B2TDone); + }while (ctx->reds > 0 || ctx->state >= B2TDone); if (context_b == NULL) { ASSERT(ctx->trap_bin == THE_NON_VALUE); @@ -1524,13 +1524,6 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con BIF_RETTYPE erts_internal_binary_to_term_1(BIF_ALIST_1) { -/*SVERK if (++sverk_cnt % 1000 == 0) { - erts_fprintf(stderr, "Call #%u to binary_to_term_int()\n", sverk_cnt); - } - if (sverk_cnt == 1767) { - sverk_break(); - } -*/ return binary_to_term_int(BIF_P, 0, BIF_ARG_1, NULL); } @@ -2879,7 +2872,6 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, objp -= 2; n--; } - ASSERT(ctx->u.dc.remaining_n || next == ctx->u.dc.container_end); break; case B2TDecodeTuple: @@ -2889,7 +2881,6 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, next = objp; objp--; } - ASSERT(ctx->u.dc.remaining_n || next == ctx->u.dc.container_end); break; case B2TDecodeString: @@ -2902,7 +2893,6 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, } hp[-1] = NIL; *hpp = hp; - ASSERT(ctx->u.dc.remaining_n || hp == ctx->u.dc.container_end); break; default: @@ -3051,7 +3041,6 @@ dec_term_atom_common: objp = hp - 1; if (ctx) { if (reds < n) { - IF_DEBUG(ctx->u.dc.container_end = hp - n;) ASSERT(reds > 0); ctx->state = B2TDecodeTuple; ctx->u.dc.remaining_n = n - reds; @@ -3085,7 +3074,6 @@ dec_term_atom_common: n--; if (ctx) { if (reds < n) { - IF_DEBUG(ctx->u.dc.container_end = hp - 2*(n+1);) ctx->state = B2TDecodeList; ctx->u.dc.remaining_n = n - reds; n = reds; @@ -3110,7 +3098,6 @@ dec_term_atom_common: *objp = make_list(hp); if (ctx) { if (reds < n) { - IF_DEBUG(ctx->u.dc.container_end = hp + n*2;) ctx->state = B2TDecodeString; ctx->u.dc.remaining_n = n - reds; n = reds; @@ -3981,6 +3968,9 @@ decoded_size(byte *ep, byte* endp, int internal_tags, B2TContext* ctx) goto init_done; } } + else + reds = 0; /* not used but compiler warns anyway */ + heap_size = 0; terms = 1; atom_extra_skip = 0; @@ -4016,9 +4006,6 @@ init_done: ASSERT(terms > 0); do { int tag; - /*SVERK - erts_fprintf(stderr, "SVERK tag=%d ep=%p terms=%d heap_size=%d endp=%p\n", - ep[0], ep, terms, heap_size, endp); */ CHKSIZE(1); tag = ep++[0]; switch (tag) { -- cgit v1.2.3 From bcb227d1f467439fa5f901233e8ad382e75373cb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 4 Nov 2013 17:53:43 +0100 Subject: erts: Trapping memcpy in binary_to_term --- erts/emulator/beam/external.c | 73 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 651aac03a2..a73734d487 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1135,6 +1135,7 @@ enum B2TState { /* order is somewhat significant */ B2TDecodeList, B2TDecodeTuple, B2TDecodeString, + B2TDecodeBinary, B2TDone, B2TDecodeFail, @@ -1156,6 +1157,7 @@ typedef struct { Eterm* hp; Eterm* hp_end; int remaining_n; + char* remaining_bytes; } B2TDecodeContext; typedef struct { @@ -1347,6 +1349,7 @@ static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1) #define B2T_BYTES_PER_REDUCTION 128 +#define B2T_MEMCPY_FACTOR 8 /* Define for testing */ /*#define EXTREME_B2T_TRAPPING 1*/ @@ -1461,7 +1464,8 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con case B2TDecode: case B2TDecodeList: case B2TDecodeTuple: - case B2TDecodeString: { + case B2TDecodeString: + case B2TDecodeBinary: { ErtsDistExternal fakedep; fakedep.flags = ctx->flags; dec_term(&fakedep, NULL, NULL, &MSO(p), NULL, ctx); @@ -1494,6 +1498,8 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION); return ctx->u.dc.res; + default: + ASSERT(!"Unknown state in binary_to_term"); } }while (ctx->reds > 0 || ctx->state >= B2TDone); @@ -2830,6 +2836,7 @@ undo_offheap_in_area(ErlOffHeap* off_heap, Eterm* start, Eterm* end) #endif /* DEBUG */ } + /* Decode term from external format into *objp. ** On failure return NULL and (R13B04) *hpp will be unchanged. */ @@ -2852,15 +2859,25 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, hpp = &ctx->u.dc.hp; if (ctx->state != B2TDecode) { - n = ctx->u.dc.remaining_n; - if (reds < n) { - ctx->u.dc.remaining_n -= reds; - n = reds; + int n_limit = reds; + + n = ctx->u.dc.remaining_n; + if (ctx->state == B2TDecodeBinary) { + n_limit *= B2T_MEMCPY_FACTOR; + ASSERT(n_limit >= reds); + reds -= n / B2T_MEMCPY_FACTOR; + } + else + reds -= n; + + if (n > n_limit) { + ctx->u.dc.remaining_n -= n_limit; + n = n_limit; + reds = 0; } else { ctx->u.dc.remaining_n = 0; } - reds -= n; switch (ctx->state) { case B2TDecodeList: @@ -2895,6 +2912,12 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, *hpp = hp; break; + case B2TDecodeBinary: + sys_memcpy(ctx->u.dc.remaining_bytes, ep, n); + ctx->u.dc.remaining_bytes += n; + ep += n; + break; + default: ASSERT(!"Unknown state"); } @@ -3311,7 +3334,6 @@ dec_term_atom_common: dbin->flags = 0; dbin->orig_size = n; erts_refc_init(&dbin->refc, 1); - sys_memcpy(dbin->orig_bytes, ep, n); pb = (ProcBin *) hp; hp += PROC_BIN_SIZE; pb->thing_word = HEADER_PROC_BIN; @@ -3322,7 +3344,20 @@ dec_term_atom_common: pb->bytes = (byte*) dbin->orig_bytes; pb->flags = 0; *objp = make_binary(pb); - } + if (ctx) { + int n_limit = reds * B2T_MEMCPY_FACTOR; + if (n > n_limit) { + ctx->state = B2TDecodeBinary; + ctx->u.dc.remaining_n = n - n_limit; + ctx->u.dc.remaining_bytes = dbin->orig_bytes + n_limit; + n = n_limit; + reds = 0; + } + else + reds -= n / B2T_MEMCPY_FACTOR; + } + sys_memcpy(dbin->orig_bytes, ep, n); + } ep += n; break; } @@ -3343,13 +3378,14 @@ dec_term_atom_common: sys_memcpy(hb->data, ep, n); bin = make_binary(hb); hp += heap_bin_size(n); + ep += n; } else { Binary* dbin = erts_bin_nrml_alloc(n); ProcBin* pb; + dbin->flags = 0; dbin->orig_size = n; erts_refc_init(&dbin->refc, 1); - sys_memcpy(dbin->orig_bytes, ep, n); pb = (ProcBin *) hp; pb->thing_word = HEADER_PROC_BIN; pb->size = n; @@ -3360,8 +3396,23 @@ dec_term_atom_common: pb->flags = 0; bin = make_binary(pb); hp += PROC_BIN_SIZE; - } - ep += n; + if (ctx) { + int n_limit = reds * B2T_MEMCPY_FACTOR; + if (n > n_limit) { + ctx->state = B2TDecodeBinary; + ctx->u.dc.remaining_n = n - n_limit; + ctx->u.dc.remaining_bytes = dbin->orig_bytes + n_limit; + n = n_limit; + reds = 0; + } + else + reds -= n / B2T_MEMCPY_FACTOR; + } + sys_memcpy(dbin->orig_bytes, ep, n); + ep += n; + n = pb->size; + } + if (bitsize == 0) { *objp = bin; } else { -- cgit v1.2.3 From adcdcb2c7985ad6076a2bedebdfce08d14465b43 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 5 Nov 2013 18:44:39 +0100 Subject: erts: Fix crash when binary_to_term throws badarg after it has built off_heap data and then done at least one trap call. The undo mechanism in dec_term does not work if we build the magic binary after any other off_heap data. --- erts/emulator/beam/external.c | 51 +++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index a73734d487..6a7d15cc88 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1364,6 +1364,21 @@ static unsigned b2t_rand(void) #endif +static B2TContext* b2t_export_context(Process* p, B2TContext* src) +{ + Binary* context_b = erts_create_magic_binary(sizeof(B2TContext), + b2t_context_destructor); + B2TContext* ctx = ERTS_MAGIC_BIN_DATA(context_b); + Eterm* hp; + sys_memcpy(ctx, src, sizeof(B2TContext)); + if (ctx->state >= B2TDecode && ctx->u.dc.next == &src->u.dc.res) { + ctx->u.dc.next = &ctx->u.dc.res; + } + hp = HAlloc(p, PROC_BIN_SIZE); + ctx->trap_bin = erts_mk_magic_binary_term(&hp, &MSO(p), context_b); + return ctx; +} + static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b) { #ifdef EXTREME_B2T_TRAPPING @@ -1373,16 +1388,18 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con #endif B2TContext c_buff; B2TContext *ctx; - Eterm* magic_space = NULL; + int is_first_call; if (context_b == NULL) { /* Setup enough to get started */ + is_first_call = 1; ctx = &c_buff; ctx->state = B2TPrepare; ctx->aligned_alloc = NULL; ctx->flags = flags; IF_DEBUG(ctx->trap_bin = THE_NON_VALUE;) } else { + is_first_call = 0; ctx = ERTS_MAGIC_BIN_DATA(context_b); ASSERT(ctx->state != B2TPrepare); } @@ -1446,12 +1463,11 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con break; case B2TDecodeInit: - if (context_b == NULL && ctx->b2ts.extsize > ctx->reds) { + if (ctx == &c_buff && ctx->b2ts.extsize > ctx->reds) { /* dec_term will maybe trap, allocate space for magic bin before result term to make it easy to trim with HRelease. */ - magic_space = HAlloc(p, PROC_BIN_SIZE); - *magic_space = make_pos_bignum_header(PROC_BIN_SIZE-1); + ctx = b2t_export_context(p, &c_buff); } ctx->u.dc.ep = ctx->b2ts.extp; ctx->u.dc.res = (Eterm) (UWord) NULL; @@ -1476,7 +1492,7 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con /*fall through*/ case B2TBadArg: b2t_destroy_context(ctx); - if (context_b) { + if (!is_first_call) { erts_set_gc_state(p, 1); } BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION); @@ -1491,10 +1507,9 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con } HRelease(p, ctx->u.dc.hp_end, ctx->u.dc.hp); - if (context_b) { + if (!is_first_call) { erts_set_gc_state(p, 1); } - BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION); return ctx->u.dc.res; @@ -1503,27 +1518,15 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con } }while (ctx->reds > 0 || ctx->state >= B2TDone); - if (context_b == NULL) { + if (ctx == &c_buff) { ASSERT(ctx->trap_bin == THE_NON_VALUE); - - context_b = erts_create_magic_binary(sizeof(B2TContext), - b2t_context_destructor); - ctx = ERTS_MAGIC_BIN_DATA(context_b); - sys_memcpy(ctx, &c_buff, sizeof(B2TContext)); - if (ctx->state >= B2TDecode && ctx->u.dc.next == &c_buff.u.dc.res) { - ctx->u.dc.next = &ctx->u.dc.res; - } - if (!magic_space) { - ASSERT(ctx->state < B2TDecode); - magic_space = HAlloc(p, PROC_BIN_SIZE); - } - ctx->trap_bin = erts_mk_magic_binary_term(&magic_space, &MSO(p), context_b); - - erts_set_gc_state(p, 0); + ctx = b2t_export_context(p, &c_buff); } - ASSERT(context_b); ASSERT(ctx->trap_bin != THE_NON_VALUE); + if (is_first_call) { + erts_set_gc_state(p, 0); + } BUMP_ALL_REDS(p); BIF_TRAP1(&binary_to_term_trap_export, p, ctx->trap_bin); } -- cgit v1.2.3 From 53c26db17f6bf89783e0dd48f7777cfeff18f756 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 5 Nov 2013 21:59:53 +0100 Subject: erts: Fix bug in binary_to_term for compressed on halfword --- erts/emulator/beam/external.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 6a7d15cc88..28c6caf5fe 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1429,7 +1429,7 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con break; } case B2TUncompressChunk: { - Uint chunk = ctx->reds; + uLongf chunk = ctx->reds; int zret; if (chunk > ctx->u.uc.dleft) -- cgit v1.2.3 From 6817539f36b0f5b4fcc3164d812e9535ee4be9ff Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 8 Nov 2013 17:51:56 +0100 Subject: erts: Improve stress of binary_to_term in binary_SUITE by doing repeated calls with different reduction count. --- erts/emulator/test/binary_SUITE.erl | 143 ++++++++++++++++++++++++------------ 1 file changed, 96 insertions(+), 47 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 08ab094019..2e5f8ce64d 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -447,26 +447,26 @@ terms(Config) when is_list(Config) -> Sz1 when is_integer(Sz1), size(Bin1) =< Sz1 -> ok end, - Term = binary_to_term(Bin), - Term = binary_to_term(Bin, [safe]), + Term = binary_to_term_stress(Bin), + Term = binary_to_term_stress(Bin, [safe]), Unaligned = make_unaligned_sub_binary(Bin), - Term = binary_to_term(Unaligned), - Term = binary_to_term(Unaligned, []), - Term = binary_to_term(Bin, [safe]), + Term = binary_to_term_stress(Unaligned), + Term = binary_to_term_stress(Unaligned, []), + Term = binary_to_term_stress(Bin, [safe]), BinC = erlang:term_to_binary(Term, [compressed]), - Term = binary_to_term(BinC), + Term = binary_to_term_stress(BinC), true = size(BinC) =< size(Bin), Bin = term_to_binary(Term, [{compressed,0}]), terms_compression_levels(Term, size(Bin), 1), UnalignedC = make_unaligned_sub_binary(BinC), - Term = binary_to_term(UnalignedC) + Term = binary_to_term_stress(UnalignedC) end, ?line test_terms(TestFun), ok. terms_compression_levels(Term, UncompressedSz, Level) when Level < 10 -> BinC = erlang:term_to_binary(Term, [{compressed,Level}]), - Term = binary_to_term(BinC), + Term = binary_to_term_stress(BinC), Sz = byte_size(BinC), true = Sz =< UncompressedSz, terms_compression_levels(Term, UncompressedSz, Level+1); @@ -476,9 +476,9 @@ terms_float(Config) when is_list(Config) -> ?line test_floats(fun(Term) -> Bin0 = term_to_binary(Term), Bin0 = term_to_binary(Term, [{minor_version,0}]), - Term = binary_to_term(Bin0), + Term = binary_to_term_stress(Bin0), Bin1 = term_to_binary(Term, [{minor_version,1}]), - Term = binary_to_term(Bin1), + Term = binary_to_term_stress(Bin1), true = size(Bin1) < size(Bin0), Size0 = erlang:external_size(Term), Size00 = erlang:external_size(Term, [{minor_version, 0}]), @@ -490,7 +490,7 @@ terms_float(Config) when is_list(Config) -> float_middle_endian(Config) when is_list(Config) -> %% Testing for roundtrip is not enough. ?line <<131,70,63,240,0,0,0,0,0,0>> = term_to_binary(1.0, [{minor_version,1}]), - ?line 1.0 = binary_to_term(<<131,70,63,240,0,0,0,0,0,0>>). + ?line 1.0 = binary_to_term_stress(<<131,70,63,240,0,0,0,0,0,0>>). external_size(Config) when is_list(Config) -> %% Build a term whose external size only fits in a big num (on 32-bit CPU). @@ -608,10 +608,10 @@ bad_binary_to_term(Config) when is_list(Config) -> ok. bad_bin_to_term(BadBin) -> - {'EXIT',{badarg,_}} = (catch binary_to_term(BadBin)). + {'EXIT',{badarg,_}} = (catch binary_to_term_stress(BadBin)). bad_bin_to_term(BadBin,Opts) -> - {'EXIT',{badarg,_}} = (catch binary_to_term(BadBin,Opts)). + {'EXIT',{badarg,_}} = (catch binary_to_term_stress(BadBin,Opts)). safe_binary_to_term2(doc) -> "Test safety options for binary_to_term/2"; safe_binary_to_term2(Config) when is_list(Config) -> @@ -622,7 +622,7 @@ safe_binary_to_term2(Config) when is_list(Config) -> BadRef = <<131,114,0,3,BadHostAtom/binary,0,<<0,0,0,255>>/binary, Empty/binary,Empty/binary>>, ?line bad_bin_to_term(BadRef, [safe]), % good ref, with a bad atom - ?line fullsweep_after = binary_to_term(<<131,100,0,15,"fullsweep_after">>, [safe]), % should be a good atom + ?line fullsweep_after = binary_to_term_stress(<<131,100,0,15,"fullsweep_after">>, [safe]), % should be a good atom BadExtFun = <<131,113,100,0,4,98,108,117,101,100,0,4,109,111,111,110,97,3>>, ?line bad_bin_to_term(BadExtFun, [safe]), ok. @@ -673,14 +673,14 @@ corrupter0(Term) -> corrupter(Bin, Pos) when Pos >= 0 -> ?line {ShorterBin, Rest} = split_binary(Bin, Pos), - ?line catch binary_to_term(ShorterBin), %% emulator shouldn't crash + ?line catch binary_to_term_stress(ShorterBin), %% emulator shouldn't crash ?line MovedBin = list_to_binary([ShorterBin]), - ?line catch binary_to_term(MovedBin), %% emulator shouldn't crash + ?line catch binary_to_term_stress(MovedBin), %% emulator shouldn't crash %% Bit faults, shouldn't crash <> = Rest, Fun = fun(M) -> FaultyByte = Byte bxor M, - catch binary_to_term(<>) end, ?line lists:foreach(Fun,[1,2,4,8,16,32,64,128,255]), ?line corrupter(Bin, Pos-1); @@ -694,7 +694,7 @@ more_bad_terms(Config) when is_list(Config) -> ?line ok = io:format("File: ~s\n", [BadFile]), ?line case file:read_file(BadFile) of {ok,Bin} -> - ?line {'EXIT',{badarg,_}} = (catch binary_to_term(Bin)), + ?line {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)), ok; Other -> ?line ?t:fail(Other) @@ -703,7 +703,7 @@ more_bad_terms(Config) when is_list(Config) -> otp_5484(Config) when is_list(Config) -> ?line {'EXIT',_} = (catch - binary_to_term( + binary_to_term_stress( <<131, 104,2, %Tuple, 2 elements 103, %Pid @@ -716,7 +716,7 @@ otp_5484(Config) when is_list(Config) -> ?line {'EXIT',_} = (catch - binary_to_term( + binary_to_term_stress( <<131, 104,2, %Tuple, 2 elements 103, %Pid @@ -728,13 +728,13 @@ otp_5484(Config) when is_list(Config) -> ?line {'EXIT',_} = (catch - binary_to_term( + binary_to_term_stress( %% A old-type fun in a list containing a bad creator pid. <<131,108,0,0,0,1,117,0,0,0,0,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,255,255,0,25,255,0,0,0,0,100,0,1,116,97,0,98,6,142,121,72,106>>)), ?line {'EXIT',_} = (catch - binary_to_term( + binary_to_term_stress( %% A new-type fun in a list containing a bad creator pid. %% <<131, @@ -746,7 +746,7 @@ otp_5484(Config) when is_list(Config) -> ?line {'EXIT',_} = (catch - binary_to_term( + binary_to_term_stress( %% A new-type fun in a list containing a bad module. <<131, 108,0,0,0,1, %List, 1 element @@ -757,7 +757,7 @@ otp_5484(Config) when is_list(Config) -> ?line {'EXIT',_} = (catch - binary_to_term( + binary_to_term_stress( %% A new-type fun in a list containing a bad index. <<131, 108,0,0,0,1, %List, 1 element @@ -769,7 +769,7 @@ otp_5484(Config) when is_list(Config) -> ?line {'EXIT',_} = (catch - binary_to_term( + binary_to_term_stress( %% A new-type fun in a list containing a bad unique value. <<131, 108,0,0,0,1, %List, 1 element @@ -782,46 +782,46 @@ otp_5484(Config) when is_list(Config) -> %% An absurdly large atom. ?line {'EXIT',_} = - (catch binary_to_term(iolist_to_binary([<<131,100,65000:16>>| + (catch binary_to_term_stress(iolist_to_binary([<<131,100,65000:16>>| lists:duplicate(65000, 42)]))), %% Longer than 255 characters. ?line {'EXIT',_} = - (catch binary_to_term(iolist_to_binary([<<131,100,256:16>>| + (catch binary_to_term_stress(iolist_to_binary([<<131,100,256:16>>| lists:duplicate(256, 42)]))), %% OTP-7218. Thanks to Matthew Dempsky. Also make sure that we %% cover the other error cases for external funs (EXPORT_EXT). ?line {'EXIT',_} = - (catch binary_to_term( + (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 97,13, %Integer: 13 97,13, %Integer: 13 97,13>>)), %Integer: 13 ?line {'EXIT',_} = - (catch binary_to_term( + (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 100,0,1,64, %Atom: '@' 97,13, %Integer: 13 97,13>>)), %Integer: 13 ?line {'EXIT',_} = - (catch binary_to_term( + (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 100,0,1,64, %Atom: '@' 100,0,1,64, %Atom: '@' 106>>)), %NIL ?line {'EXIT',_} = - (catch binary_to_term( + (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 100,0,1,64, %Atom: '@' 100,0,1,64, %Atom: '@' 98,255,255,255,255>>)), %Integer: -1 ?line {'EXIT',_} = - (catch binary_to_term( + (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 100,0,1,64, %Atom: '@' @@ -829,7 +829,7 @@ otp_5484(Config) when is_list(Config) -> 113,97,13,97,13,97,13>>)), %fun 13:13/13 %% Bad funs. - ?line {'EXIT',_} = (catch binary_to_term(fake_fun(0, lists:seq(0, 256)))), + ?line {'EXIT',_} = (catch binary_to_term_stress(fake_fun(0, lists:seq(0, 256)))), ok. fake_fun(Arity, Env0) -> @@ -863,7 +863,7 @@ try_bad_lengths(B) -> try_bad_lengths(B, L) when L > 16#FFFFFFF0 -> Bin = <>, io:format("~p\n", [Bin]), - {'EXIT',_} = (catch binary_to_term(Bin)), + {'EXIT',_} = (catch binary_to_term_stress(Bin)), try_bad_lengths(B, L-1); try_bad_lengths(_, _) -> ok. @@ -917,7 +917,7 @@ otp_6817_try_bin(Bin) -> %% If the bug is present, the heap pointer will moved when the invalid term %% is found and we will have a linked list passing through the limbo area %% between the heap top and the stack pointer. - catch binary_to_term(Bin), + catch binary_to_term_stress(Bin), %% If the bug is present, we will overwrite the pointers in the limbo area. Filler = erlang:make_tuple(1024, 16#3FA), @@ -1227,12 +1227,12 @@ bit_sized_binary_sizes(Config) when is_list(Config) -> bsbs_1(0) -> BinSize = 32+8, io:format("A: ~p BinSize: ~p", [0,BinSize]), - Bin = binary_to_term(<<131,$M,5:32,0,0,0,0,0,0>>), + Bin = binary_to_term_stress(<<131,$M,5:32,0,0,0,0,0,0>>), BinSize = bit_size(Bin); bsbs_1(A) -> BinSize = 32+A, io:format("A: ~p BinSize: ~p", [A,BinSize]), - Bin = binary_to_term(<<131,$M,5:32,A,0,0,0,0,0>>), + Bin = binary_to_term_stress(<<131,$M,5:32,A,0,0,0,0,0>>), BinSize = bit_size(Bin). deep(Config) when is_list(Config) -> @@ -1249,7 +1249,7 @@ deep(Config) when is_list(Config) -> deep_roundtrip(T) -> B = term_to_binary(T), - T = binary_to_term(B). + T = binary_to_term_stress(B). obsolete_funs(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), @@ -1284,29 +1284,29 @@ obsolete_fun(Fun) -> Tuple = no_fun_roundtrip(Fun). no_fun_roundtrip(Term) -> - binary_to_term(erts_debug:get_internal_state({term_to_binary_no_funs,Term})). + binary_to_term_stress(erts_debug:get_internal_state({term_to_binary_no_funs,Term})). %% Test non-standard encodings never generated by term_to_binary/1 %% but recognized by binary_to_term/1. robustness(Config) when is_list(Config) -> - ?line [] = binary_to_term(<<131,107,0,0>>), %Empty string. - ?line [] = binary_to_term(<<131,108,0,0,0,0,106>>), %Zero-length list. + ?line [] = binary_to_term_stress(<<131,107,0,0>>), %Empty string. + ?line [] = binary_to_term_stress(<<131,108,0,0,0,0,106>>), %Zero-length list. %% {[],a} where [] is a zero-length list. - ?line {[],a} = binary_to_term(<<131,104,2,108,0,0,0,0,106,100,0,1,97>>), + ?line {[],a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,106,100,0,1,97>>), %% {42,a} where 42 is a zero-length list with 42 in the tail. - ?line {42,a} = binary_to_term(<<131,104,2,108,0,0,0,0,97,42,100,0,1,97>>), + ?line {42,a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,97,42,100,0,1,97>>), %% {{x,y},a} where {x,y} is a zero-length list with {x,y} in the tail. - ?line {{x,y},a} = binary_to_term(<<131,104,2,108,0,0,0,0, + ?line {{x,y},a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0, 104,2,100,0,1,120,100,0,1, 121,100,0,1,97>>), %% Bignums fitting in 32 bits. - ?line 16#7FFFFFFF = binary_to_term(<<131,98,127,255,255,255>>), - ?line -1 = binary_to_term(<<131,98,255,255,255,255>>), + ?line 16#7FFFFFFF = binary_to_term_stress(<<131,98,127,255,255,255>>), + ?line -1 = binary_to_term_stress(<<131,98,255,255,255,255>>), ok. @@ -1324,7 +1324,7 @@ run_otp_8180(Name) -> ?line {ok,Bins} = file:consult(Name), [begin io:format("~p\n", [Bin]), - ?line {'EXIT',{badarg,_}} = (catch binary_to_term(Bin)) + ?line {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)) end || Bin <- Bins], ok. @@ -1393,3 +1393,52 @@ unaligned_sub_bin(Bin0, Offs) -> Bin. id(I) -> I. + + +%% Stress binary_to_term with different initial reductions +binary_to_term_stress(Bin) -> + binary_to_term_stress(Bin, no_opts). + +binary_to_term_stress(Bin, Opts) -> + Reds = get_reds(), + T = b2t(erlang:system_info(context_reductions), + Bin, Opts, catch_binary_to_term(Bin, Opts)), + set_reds(Reds), + T = case Opts of + no_opts -> binary_to_term(Bin); + _ -> binary_to_term(Bin,Opts) + end. + +catch_binary_to_term(Bin, no_opts) -> + try binary_to_term(Bin) + catch + error:badarg -> binary_to_term_throws_badarg + end; +catch_binary_to_term(Bin, Opts) -> + try binary_to_term(Bin, Opts) + catch + error:badarg -> binary_to_term_throws_badarg + end. + +b2t(0, _Bin, _Opts, Term) -> + Term; +b2t(Reds, Bin, Opts, Term) -> + set_reds(Reds), + Term = catch_binary_to_term(Bin,Opts), + b2t(Reds div 3, Bin, Opts, Term). + +set_reds(Reds) -> + try erts_debug:set_internal_state(reds_left, Reds) + catch + error:undef -> + erts_debug:set_internal_state(available_internal_state, true), + set_reds(Reds) + end. + +get_reds() -> + try erts_debug:get_internal_state(reds_left) + catch + error:undef -> + erts_debug:set_internal_state(available_internal_state, true), + get_reds() + end. -- cgit v1.2.3 From 08521e1438038aab694170d110e638d69ecee0df Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 19 Nov 2013 14:31:23 +0100 Subject: erts: Fix test case hash_SUITE:bit_level_binaries Commontest doesn't like exported functions with same name as test case. --- erts/emulator/test/hash_SUITE.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl index 43c9d64af7..738c9c8b16 100644 --- a/erts/emulator/test/hash_SUITE.erl +++ b/erts/emulator/test/hash_SUITE.erl @@ -32,7 +32,7 @@ %% -module(hash_SUITE). -export([basic_test/0,cmp_test/1,range_test/0,spread_test/1, - phash2_test/0, otp_5292_test/0, bit_level_binaries/0, + phash2_test/0, otp_5292_test/0, otp_7127_test/0]). -compile({nowarn_deprecated_function, {erlang,hash,2}}). @@ -152,7 +152,7 @@ otp_5292(Config) when is_list(Config) -> %% Test hashing bit-level binaries. bit_level_binaries(Config) when is_list(Config) -> - bit_level_binaries(). + bit_level_binaries_do(). otp_7127(suite) -> []; @@ -537,7 +537,7 @@ hash_int(Start, End, F) -> md5(T) -> erlang:md5(term_to_binary(T)). -bit_level_binaries() -> +bit_level_binaries_do() -> [3511317,7022633,14044578,28087749,56173436,112344123,90467083|_] = bit_level_all_different(fun erlang:hash/2), [3511317,7022633,14044578,28087749,56173436,112344123,90467083|_] = -- cgit v1.2.3 From cd5f69d2148b77c2700d216939938e250477b5f7 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 19 Nov 2013 19:19:06 +0100 Subject: Suppress false valgrind warnings caused by sctp_getpaddrs --- erts/emulator/drivers/common/inet_drv.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index de18656273..f55e6efacc 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -3959,6 +3959,7 @@ static int inet_address_to_erlang(char *dst, inet_address **src) { dst[0] = INET_AF_INET6; port = sock_ntohs((*src)->sai6.sin6_port); put_int16(port, dst+1); + VALGRIND_MAKE_MEM_DEFINED(&(*src)->sai6.sin6_addr,16); /* false undefs from syscall sctp_get[lp]addrs */ sys_memcpy(dst+1+2, (char *) &(*src)->sai6.sin6_addr, 16); } (*src) = (inet_address *) (&(*src)->sai6 + 1); -- cgit v1.2.3 From 5a00e724a58ee29d4012cca79c8aa33979e74eb6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 20 Nov 2013 21:33:20 +0100 Subject: erts: Fix alignment bug in allocator start code Bug never released. --- erts/emulator/beam/erl_alloc_util.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 1fdee4db2c..00b87aca8b 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -372,6 +372,8 @@ do { \ #define ERTS_CRR_ALCTR_FLG_IN_POOL (((erts_aint_t) 1) << 0) #define ERTS_CRR_ALCTR_FLG_BUSY (((erts_aint_t) 1) << 1) +#define ERTS_CRR_ALCTR_FLG_MASK (ERTS_CRR_ALCTR_FLG_IN_POOL | \ + ERTS_CRR_ALCTR_FLG_BUSY) #ifdef ERTS_SMP #define SBC_HEADER_SIZE \ @@ -1404,14 +1406,14 @@ get_used_allctr(Allctr_t *pref_allctr, int pref_lock, void *p, UWord *sizep, if (ERTS_ALC_TS_PREF_LOCK_IF_USED == pref_lock && pref_allctr->thread_safe) { - used_allctr = (Allctr_t *) (iallctr & ~FLG_MASK); + used_allctr = (Allctr_t *) (iallctr & ~ERTS_CRR_ALCTR_FLG_MASK); if (pref_allctr == used_allctr) { erts_mtx_lock(&pref_allctr->mutex); locked_pref_allctr = 1; } } - while ((iallctr & ((~FLG_MASK)|ERTS_CRR_ALCTR_FLG_IN_POOL)) + while ((iallctr & ((~ERTS_CRR_ALCTR_FLG_MASK)|ERTS_CRR_ALCTR_FLG_IN_POOL)) == (((erts_aint_t) pref_allctr)|ERTS_CRR_ALCTR_FLG_IN_POOL)) { erts_aint_t act; @@ -1426,7 +1428,7 @@ get_used_allctr(Allctr_t *pref_allctr, int pref_lock, void *p, UWord *sizep, iallctr = act; } - used_allctr = (Allctr_t *) (iallctr & ~FLG_MASK); + used_allctr = (Allctr_t *) (iallctr & ~ERTS_CRR_ALCTR_FLG_MASK); if (ERTS_ALC_TS_PREF_LOCK_IF_USED == pref_lock) { if (locked_pref_allctr && used_allctr != pref_allctr) { @@ -1436,16 +1438,16 @@ get_used_allctr(Allctr_t *pref_allctr, int pref_lock, void *p, UWord *sizep, } ERTS_ALC_CPOOL_ASSERT( - (((iallctr & ~FLG_MASK) == (erts_aint_t) pref_allctr) - ? (((iallctr & FLG_MASK) == ERTS_CRR_ALCTR_FLG_IN_POOL) - || ((iallctr & FLG_MASK) == 0)) + (((iallctr & ~ERTS_CRR_ALCTR_FLG_MASK) == (erts_aint_t) pref_allctr) + ? (((iallctr & ERTS_CRR_ALCTR_FLG_MASK) == ERTS_CRR_ALCTR_FLG_IN_POOL) + || ((iallctr & ERTS_CRR_ALCTR_FLG_MASK) == 0)) : 1)); return used_allctr; } } - used_allctr = (Allctr_t *) (iallctr & ~FLG_MASK); + used_allctr = (Allctr_t *) (iallctr & ~ERTS_CRR_ALCTR_FLG_MASK); if (ERTS_ALC_TS_PREF_LOCK_IF_USED == pref_lock && used_allctr == pref_allctr @@ -1776,7 +1778,7 @@ handle_delayed_dealloc(Allctr_t *allctr, ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr); ERTS_ALC_CPOOL_ASSERT(((erts_aint_t) allctr) != (erts_smp_atomic_read_nob(&crr->allctr) - & ~FLG_MASK)); + & ~ERTS_CRR_ALCTR_FLG_MASK)); erts_smp_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr)); @@ -2919,7 +2921,7 @@ cpool_fetch(Allctr_t *allctr, UWord size) #ifdef ERTS_ALC_CPOOL_DEBUG ERTS_ALC_CPOOL_ASSERT(erts_smp_atomic_xchg_nob(&crr->allctr, ((erts_aint_t) allctr)) - == (((erts_aint_t) allctr) & ~FLG_MASK)); + == (((erts_aint_t) allctr) & ~ERTS_CRR_ALCTR_FLG_MASK)); #else erts_smp_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr)); #endif @@ -2961,7 +2963,7 @@ cpool_fetch(Allctr_t *allctr, UWord size) (erts_aint_t) allctr, exp); if (act == exp) { - cpool_delete(allctr, ((Allctr_t *) (act & ~FLG_MASK)), crr); + cpool_delete(allctr, ((Allctr_t *) (act & ~ERTS_CRR_ALCTR_FLG_MASK)), crr); return crr; } } @@ -3056,7 +3058,7 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr) ERTS_ALC_CPOOL_ASSERT(crr == FIRST_BLK_TO_MBC(allctr, blk)); ERTS_ALC_CPOOL_ASSERT(((erts_aint_t) allctr) == (erts_smp_atomic_read_nob(&crr->allctr) - & ~FLG_MASK)); + & ~ERTS_CRR_ALCTR_FLG_MASK)); if (ddq_enqueue(&orig_allctr->dd.q, BLK2UMEM(blk), cinit)) erts_alloc_notify_delayed_dealloc(orig_allctr->ix); @@ -5422,6 +5424,11 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) { /* erts_alcu_start assumes that allctr has been zeroed */ + if (((UWord)allctr & ERTS_CRR_ALCTR_FLG_MASK) != 0) { + erl_exit(ERTS_ABORT_EXIT, "%s:%d:erts_alcu_start: Alignment error\n", + __FILE__, __LINE__); + } + if (!initialized) goto error; -- cgit v1.2.3 From 522a29666088d5d96956d2752ffb1596d778cffd Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 13 Nov 2013 18:07:33 +0100 Subject: erts: Let term_to_binary disable gc while trapping as an attempt to improve performance --- erts/emulator/beam/external.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 28c6caf5fe..d5f3b19b82 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1055,8 +1055,10 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1) Binary *bin = ((ProcBin *) binary_val(bt))->val; Eterm res = erts_term_to_binary_int(BIF_P, Term, 0, 0,bin); if (is_tuple(res)) { + ASSERT(BIF_P->flags & F_DISABLE_GC); BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res); } else { + erts_set_gc_state(BIF_P, 1); BIF_RET(res); } } @@ -1065,8 +1067,10 @@ BIF_RETTYPE term_to_binary_1(BIF_ALIST_1) { Eterm res = erts_term_to_binary_int(BIF_P, BIF_ARG_1, 0, TERM_TO_BINARY_DFLAGS, NULL); if (is_tuple(res)) { + erts_set_gc_state(BIF_P, 0); BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res); } else { + ASSERT(!(BIF_P->flags & F_DISABLE_GC)); BIF_RET(res); } } @@ -1118,8 +1122,10 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2) res = erts_term_to_binary_int(p, Term, level, flags, bin); if (is_tuple(res)) { + erts_set_gc_state(p, 0); BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res); } else { + ASSERT(!(BIF_P->flags & F_DISABLE_GC)); BIF_RET(res); } } -- cgit v1.2.3 From 1f09936f34f5daee534bbfde4f16e5bbb434b6c4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 13 Nov 2013 18:46:17 +0100 Subject: erts: Yield after trapping term_to_binary if gc has been ordered or if "too much" offheap binaries has been built --- erts/emulator/beam/erl_bif_info.c | 5 +++-- erts/emulator/beam/erl_process.c | 9 +++------ erts/emulator/beam/external.c | 7 +++++-- 3 files changed, 11 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index f5893f9291..39bbd6b182 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3603,8 +3603,9 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) default: BIF_ERROR(BIF_P, BADARG); break; } - res = erts_set_gc_state(BIF_P, enable); - BIF_RET(res ? am_true : am_false); + res = (BIF_P->flags & F_DISABLE_GC) ? am_false : am_true; + erts_set_gc_state(BIF_P, enable); + BIF_RET(res); } else if (ERTS_IS_ATOM_STR("send_fake_exit_signal", BIF_ARG_1)) { /* Used by signal_SUITE (emulator) */ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 13b18e9e0e..b88c871ffc 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -8271,25 +8271,22 @@ save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio) int erts_set_gc_state(Process *c_p, int enable) { - int res; ErtsProcSysTaskQs *dgc_tsk_qs; ASSERT(c_p == erts_get_current_process()); ASSERT((ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS) & erts_smp_atomic32_read_nob(&c_p->state)); ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p)); - res = !(c_p->flags & F_DISABLE_GC); - if (!enable) { c_p->flags |= F_DISABLE_GC; - return res; + return 0; } c_p->flags &= ~F_DISABLE_GC; dgc_tsk_qs = ERTS_PROC_GET_DELAYED_GC_TASK_QS(c_p); if (!dgc_tsk_qs) - return res; + return 0; /* Move delayed gc tasks into sys tasks queues. */ @@ -8387,7 +8384,7 @@ erts_set_gc_state(Process *c_p, int enable) if (dgc_tsk_qs) proc_sys_task_queues_free(dgc_tsk_qs); - return res; + return 1; } void diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index d5f3b19b82..7dc7ba6f98 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1058,8 +1058,11 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1) ASSERT(BIF_P->flags & F_DISABLE_GC); BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res); } else { - erts_set_gc_state(BIF_P, 1); - BIF_RET(res); + if (erts_set_gc_state(BIF_P, 1) + || MSO(BIF_P).overhead > BIN_VHEAP_SZ(BIF_P)) + ERTS_BIF_YIELD_RETURN(BIF_P, res); + else + BIF_RET(res); } } -- cgit v1.2.3 From 6cff38512b753172a7dfa2bedd60e8987156736d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 10 Oct 2013 20:54:12 +0200 Subject: erts: Adjust term_to_binary reduction factors Made them powers of 2 for faster calculations. 500 encoded terms per reductions seemed a bit much, lowered to 32. TERM_TO_BINARY_SIZE_FACTOR was not used in practice as it was only applied to small binaries. Lowered from 500kb to 256kb compressed bytes per trap call. --- erts/emulator/beam/external.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 7dc7ba6f98..4600d691c7 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1717,12 +1717,10 @@ erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags) { /* #define EXTREME_TTB_TRAPPING 1 */ #ifndef EXTREME_TTB_TRAPPING -#define TERM_TO_BINARY_LOOP_FACTOR 500 -#define TERM_TO_BINARY_SIZE_FACTOR 500000 -#define TERM_TO_BINARY_COMPRESS_CHUNK 500000 +#define TERM_TO_BINARY_LOOP_FACTOR 32 +#define TERM_TO_BINARY_COMPRESS_CHUNK (1 << 18) #else #define TERM_TO_BINARY_LOOP_FACTOR 1 -#define TERM_TO_BINARY_SIZE_FACTOR 10 #define TERM_TO_BINARY_COMPRESS_CHUNK 10 #endif @@ -1859,7 +1857,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla /* Finish in one go */ res = erts_term_to_binary_simple(p, Term, size, level, flags); - BUMP_REDS(p, size / TERM_TO_BINARY_SIZE_FACTOR); + BUMP_REDS(p, 1); return res; } -- cgit v1.2.3 From e9c04e92199deb274e1bb74e4caecd296443ca5f Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 20 Nov 2013 14:34:06 +0100 Subject: Improve error info when main carrier creation fails Also fix testcase that failed due to main carrier creation failure. --- erts/emulator/beam/erl_alloc_util.c | 11 +++++++++-- erts/emulator/test/alloc_SUITE.erl | 7 ++++++- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 1fdee4db2c..b59fe0bf15 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -5593,8 +5593,15 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) | CFLG_FORCE_SYS_ALLOC #endif | CFLG_MAIN_CARRIER); - if (!blk) - goto error; + if (!blk) { +#ifdef USE_THREADS + if (allctr->thread_safe) + erts_mtx_destroy(&allctr->mutex); +#endif + erl_exit(ERTS_ABORT_EXIT, + "Failed to create main carrier for %salloc\n", + init->name_prefix); + } (*allctr->link_free_block)(allctr, blk); diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index f6ff6bb813..bc83c94c41 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -127,7 +127,12 @@ erts_mmap(Config) when is_list(Config) -> erts_mmap_do(Config, SCO, SCRPM, SCMGC) -> - SCS = 100, % Mb + %% We use the number of schedulers + 1 * approx main carriers size + %% to calculate how large the super carrier has to be + %% and then use a minimum of 100 for systems with a low amount of + %% schedulers + Schldr = erlang:system_info(schedulers_online)+1, + SCS = max(round((262144 * 6 + 3 * 1048576) * Schldr / 1024 / 1024),100), O1 = "+MMscs" ++ integer_to_list(SCS) ++ " +MMsco" ++ atom_to_list(SCO) ++ " +MMscrpm" ++ atom_to_list(SCRPM), -- cgit v1.2.3 From dc9ff931423b57e64abde0f1de7133f334abb780 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 25 Nov 2013 15:34:03 +0100 Subject: Ensure exit signal due to link precede port BIF return --- erts/doc/src/erlang.xml | 162 ++++++++++++++++++++++++++++++------- erts/emulator/beam/bif.c | 37 +++++++-- erts/emulator/beam/erl_bif_port.c | 34 +++++--- erts/emulator/beam/erl_port_task.h | 11 ++- erts/emulator/beam/io.c | 2 + 5 files changed, 198 insertions(+), 48 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index cbb25c2cf2..062caadad3 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -3032,7 +3032,10 @@ os_prompt% (see below), being synchronous, and that the port does not reply with {Port, closed}. Any process may close a port with port_close/1, not only the port owner - (the connected process).

+ (the connected process). If the calling process is linked to + port identified by Port, an exit signal due + to that link will be received by the process prior to the return + from port_close/1.

For comparison: Port ! {self(), close} fails with badarg if Port cannot be sent to (i.e., Port refers neither to a port nor to a process). If @@ -3041,6 +3044,7 @@ os_prompt% the port replies with {Port, closed} when all buffers have been flushed and the port really closes, but if the calling process is not the port owner the port owner fails with badsig.

+

Note that any process can close a port using Port ! {PortOwner, close} just as if it itself was the port owner, but the reply always goes to the port owner.

@@ -3050,8 +3054,17 @@ os_prompt% implementation has been synchronous. port_close/1 is however still fully synchronous. This due to its error behavior.

-

Failure: badarg if Port is not an open port or - the registered name of an open port.

+

Failure:

+ + badarg + + If Port is not an identifier of an open + port, or the registered name of an open port. If the calling + process was linked to the previously open port identified by + Port, an exit signal due to this link + was received by the process prior to this exception. + +
@@ -3086,8 +3099,11 @@ os_prompt% badarg - If Port is not an open port or the registered name - of an open port. + If Port is not an identifier of an open + port, or the registered name of an open port. If the calling + process was linked to the previously open port identified by + Port, an exit signal due to this link + was received by the process prior to this exception. badarg @@ -3130,8 +3146,11 @@ os_prompt% badarg - If Port is not an open port or the registered name - of an open port. + If Port is not an identifier of an open + port, or the registered name of an open port. If the calling + process was linked to the previously open port identified by + Port, an exit signal due to this link + was received by the process prior to this exception. badarg @@ -3198,9 +3217,20 @@ os_prompt% implementation has been synchronous. port_connect/2 is however still fully synchronous. This due to its error behavior.

-

Failure: badarg if Port is not an open port - or the registered name of an open port, or if Pid is - not an existing local pid.

+

Failures:

+ + badarg + + If Port is not an identifier of an open + port, or the registered name of an open port. If the calling + process was linked to the previously open port identified by + Port, an exit signal due to this link + was received by the process prior to this exception. + + badarg + If process identified by Pid is not an existing + local process. +
@@ -3236,12 +3266,33 @@ os_prompt% binary term format and sent to the port.

Returns: a term from the driver. The meaning of the returned data also depends on the port driver.

-

Failure: badarg if Port is not an open port or - the registered name of an open port, if Operation - 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 - Operation or Data).

+

Failures:

+ + badarg + + If Port is not an identifier of an open + port, or the registered name of an open port. If the calling + process was linked to the previously open port identified by + Port, an exit signal due to this link + was received by the process prior to this exception. + + badarg + + If Operation does not fit in a + 32-bit integer. + + badarg + + If the port driver does not support synchronous control + operations. + + badarg + + If the port driver so decides for any reason (probably + something wrong with Operation, or + Data). + +
@@ -3251,7 +3302,12 @@ os_prompt%

Returns a list containing tuples with information about the Port, or undefined if the port is not open. The order of the tuples is not defined, nor are all the - tuples mandatory.

+ tuples mandatory. + If undefined is returned and the calling process + was linked to a previously open port identified by + Port, an exit signal due to this link + was received by the process prior to the return from + port_info/1.

Currently the result will containt information about the following Items: registered_name (if the port has a registered name), id, connected, links, @@ -3269,7 +3325,11 @@ os_prompt%

Pid is the process identifier of the process connected to the port.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3281,7 +3341,11 @@ os_prompt%

Index is the internal index of the port. This index may be used to separate ports.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3293,7 +3357,11 @@ os_prompt%

Bytes is the total number of bytes read from the port.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3305,7 +3373,11 @@ os_prompt%

Pids is a list of the process identifiers of the processes that the port is linked to.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3320,7 +3392,11 @@ os_prompt% that these results are highly implementation specific and might change in the future.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3334,7 +3410,11 @@ os_prompt% that the port itself might have allocated memory which is not included in Bytes.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3346,7 +3426,11 @@ os_prompt%

Monitors represent processes that this port is monitoring.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3358,7 +3442,11 @@ os_prompt%

Name is the command name set by open_port/2.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3373,7 +3461,11 @@ os_prompt% Command}, Options). If the port is not the result of spawning an OS process, the value is undefined.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3389,7 +3481,11 @@ os_prompt% or Port ! {Owner, {command, Data}.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3412,7 +3508,11 @@ os_prompt% in bytes, queued by the port using the ERTS driver queue implementation.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3424,7 +3524,11 @@ os_prompt%

RegisteredName is the registered name of the port. If the port has no registered name, [] is returned.

If the port identified by Port is not open, - undefined is returned.

+ undefined is returned. If undefined is returned and + the calling process was linked to a previously open port identified + by Port, an exit signal due to this link + was received by the process prior to the return from + port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 755c5e6882..13d31285b2 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -160,7 +160,10 @@ BIF_RETTYPE link_1(BIF_ALIST_1) if (is_internal_port(BIF_ARG_1)) { int send_link_signal = 0; - Port *prt = erts_port_lookup(BIF_ARG_1, ERTS_PORT_SFLGS_INVALID_LOOKUP); + Port *prt = erts_port_lookup(BIF_ARG_1, + (erts_port_synchronous_ops + ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + : ERTS_PORT_SFLGS_INVALID_LOOKUP)); if (!prt) { goto res_no_proc; } @@ -1363,11 +1366,22 @@ BIF_RETTYPE exit_2(BIF_ALIST_2) */ if (is_internal_port(BIF_ARG_1)) { - Port *prt = erts_port_lookup(BIF_ARG_1, ERTS_PORT_SFLGS_INVALID_LOOKUP); + Eterm ref, *refp; + Uint32 invalid_flags; + Port *prt; + + if (erts_port_synchronous_ops) { + refp = &ref; + invalid_flags = ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP; + } + else { + refp = NULL; + invalid_flags = ERTS_PORT_SFLGS_INVALID_LOOKUP; + } + + prt = erts_port_lookup(BIF_ARG_1, invalid_flags); if (prt) { - Eterm ref; - Eterm *refp = erts_port_synchronous_ops ? &ref : NULL; ErtsPortOpResult res; #ifdef DEBUG @@ -1875,7 +1889,10 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { if (rp) goto send_message; - pt = erts_port_lookup(id, ERTS_PORT_SFLGS_INVALID_LOOKUP); + pt = erts_port_lookup(id, + (erts_port_synchronous_ops + ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + : ERTS_PORT_SFLGS_INVALID_LOOKUP)); if (pt) { portid = id; goto port_common; @@ -1905,7 +1922,10 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { int ret_val; portid = to; - pt = erts_port_lookup(portid, ERTS_PORT_SFLGS_INVALID_LOOKUP); + pt = erts_port_lookup(portid, + (erts_port_synchronous_ops + ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + : ERTS_PORT_SFLGS_INVALID_LOOKUP)); port_common: ret_val = 0; @@ -1994,7 +2014,10 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { rp = erts_proc_lookup_raw(id); if (rp) goto send_message; - pt = erts_port_lookup(id, ERTS_PORT_SFLGS_INVALID_LOOKUP); + pt = erts_port_lookup(id, + (erts_port_synchronous_ops + ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + : ERTS_PORT_SFLGS_INVALID_LOOKUP)); if (pt) { portid = id; goto port_common; diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 109c54fd7f..3cd53ef65d 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -84,7 +84,7 @@ BIF_RETTYPE open_port_2(BIF_ALIST_2) } static ERTS_INLINE Port * -lookup_port(Process *c_p, Eterm id_or_name) +lookup_port(Process *c_p, Eterm id_or_name, Uint32 invalid_flags) { /* TODO: Implement nicer lookup in register... */ Eterm id; @@ -92,7 +92,19 @@ lookup_port(Process *c_p, Eterm id_or_name) id = erts_whereis_name_to_id(c_p, id_or_name); else id = id_or_name; - return erts_port_lookup(id, ERTS_PORT_SFLGS_INVALID_LOOKUP); + return erts_port_lookup(id, invalid_flags); +} + +static ERTS_INLINE Port * +sig_lookup_port(Process *c_p, Eterm id_or_name) +{ + return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); +} + +static ERTS_INLINE Port * +data_lookup_port(Process *c_p, Eterm id_or_name) +{ + return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_LOOKUP); } /* @@ -125,7 +137,7 @@ BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3) BIF_RET(am_badarg); } - prt = lookup_port(BIF_P, BIF_ARG_1); + prt = sig_lookup_port(BIF_P, BIF_ARG_1); if (!prt) BIF_RET(am_badarg); @@ -185,7 +197,7 @@ BIF_RETTYPE erts_internal_port_call_3(BIF_ALIST_3) unsigned int op; erts_aint32_t state; - prt = lookup_port(BIF_P, BIF_ARG_1); + prt = sig_lookup_port(BIF_P, BIF_ARG_1); if (!prt) BIF_RET(am_badarg); @@ -235,7 +247,7 @@ BIF_RETTYPE erts_internal_port_control_3(BIF_ALIST_3) unsigned int op; erts_aint32_t state; - prt = lookup_port(BIF_P, BIF_ARG_1); + prt = sig_lookup_port(BIF_P, BIF_ARG_1); if (!prt) BIF_RET(am_badarg); @@ -290,7 +302,7 @@ BIF_RETTYPE erts_internal_port_close_1(BIF_ALIST_1) ref = NIL; #endif - prt = lookup_port(BIF_P, BIF_ARG_1); + prt = sig_lookup_port(BIF_P, BIF_ARG_1); if (!prt) BIF_RET(am_badarg); @@ -320,7 +332,7 @@ BIF_RETTYPE erts_internal_port_connect_2(BIF_ALIST_2) Eterm ref; Port* prt; - prt = lookup_port(BIF_P, BIF_ARG_1); + prt = sig_lookup_port(BIF_P, BIF_ARG_1); if (!prt) BIF_RET(am_badarg); @@ -352,7 +364,7 @@ BIF_RETTYPE erts_internal_port_info_1(BIF_ALIST_1) Port* prt; if (is_internal_port(BIF_ARG_1) || is_atom(BIF_ARG_1)) { - prt = lookup_port(BIF_P, BIF_ARG_1); + prt = sig_lookup_port(BIF_P, BIF_ARG_1); if (!prt) BIF_RET(am_undefined); } @@ -391,7 +403,7 @@ BIF_RETTYPE erts_internal_port_info_2(BIF_ALIST_2) Port* prt; if (is_internal_port(BIF_ARG_1) || is_atom(BIF_ARG_1)) { - prt = lookup_port(BIF_P, BIF_ARG_1); + prt = sig_lookup_port(BIF_P, BIF_ARG_1); if (!prt) BIF_RET(am_undefined); } @@ -523,7 +535,7 @@ BIF_RETTYPE port_set_data_2(BIF_ALIST_2) erts_aint_t data; Port* prt; - prt = lookup_port(BIF_P, BIF_ARG_1); + prt = data_lookup_port(BIF_P, BIF_ARG_1); if (!prt) BIF_ERROR(BIF_P, BADARG); @@ -564,7 +576,7 @@ BIF_RETTYPE port_get_data_1(BIF_ALIST_1) erts_aint_t data; Port* prt; - prt = lookup_port(BIF_P, BIF_ARG_1); + prt = data_lookup_port(BIF_P, BIF_ARG_1); if (!prt) BIF_ERROR(BIF_P, BADARG); diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h index e4d964146e..123253a057 100644 --- a/erts/emulator/beam/erl_port_task.h +++ b/erts/emulator/beam/erl_port_task.h @@ -77,6 +77,7 @@ extern erts_smp_atomic_t erts_port_task_outstanding_io_tasks; #define ERTS_PTS_FLG_HAVE_NS_TASKS (((erts_aint32_t) 1) << 8) #define ERTS_PTS_FLG_PARALLELISM (((erts_aint32_t) 1) << 9) #define ERTS_PTS_FLG_FORCE_SCHED (((erts_aint32_t) 1) << 10) +#define ERTS_PTS_FLG_EXITING (((erts_aint32_t) 1) << 11) #define ERTS_PTS_FLGS_BUSY \ (ERTS_PTS_FLG_BUSY_PORT | ERTS_PTS_FLG_BUSY_PORT_Q) @@ -86,7 +87,8 @@ extern erts_smp_atomic_t erts_port_task_outstanding_io_tasks; | ERTS_PTS_FLG_HAVE_BUSY_TASKS \ | ERTS_PTS_FLG_HAVE_TASKS \ | ERTS_PTS_FLG_EXEC \ - | ERTS_PTS_FLG_FORCE_SCHED) + | ERTS_PTS_FLG_FORCE_SCHED \ + | ERTS_PTS_FLG_EXITING) #define ERTS_PORT_TASK_DEFAULT_BUSY_PORT_Q_HIGH 8192 #define ERTS_PORT_TASK_DEFAULT_BUSY_PORT_Q_LOW 4096 @@ -135,6 +137,7 @@ ERTS_GLB_INLINE void erts_port_task_fini_sched(ErtsPortTaskSched *ptsp); ERTS_GLB_INLINE void erts_port_task_sched_lock(ErtsPortTaskSched *ptsp); ERTS_GLB_INLINE void erts_port_task_sched_unlock(ErtsPortTaskSched *ptsp); ERTS_GLB_INLINE int erts_port_task_sched_lock_is_locked(ErtsPortTaskSched *ptsp); +ERTS_GLB_INLINE void erts_port_task_sched_enter_exiting_state(ErtsPortTaskSched *ptsp); #ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS ERTS_GLB_INLINE int erts_port_task_have_outstanding_io_tasks(void); @@ -225,6 +228,12 @@ erts_port_task_fini_sched(ErtsPortTaskSched *ptsp) #endif } +ERTS_GLB_INLINE void +erts_port_task_sched_enter_exiting_state(ErtsPortTaskSched *ptsp) +{ + erts_smp_atomic32_read_bor_nob(&ptsp->flags, ERTS_PTS_FLG_EXITING); +} + #ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS ERTS_GLB_INLINE int diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 6911fd8241..9076bbe73c 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -3601,6 +3601,8 @@ erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed) if (send_closed) set_state_flags |= ERTS_PORT_SFLG_SEND_CLOSED; + erts_port_task_sched_enter_exiting_state(&p->sched); + state = erts_atomic32_read_bor_mb(&p->state, set_state_flags); state |= set_state_flags; -- cgit v1.2.3 From e16f564fe0d64017d5cfcf56255f0a6965a6a471 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 25 Nov 2013 15:34:27 +0100 Subject: Fix prim_inet:close/1 --- erts/preloaded/ebin/prim_inet.beam | Bin 72644 -> 72316 bytes erts/preloaded/src/prim_inet.erl | 28 ++-------------------------- 2 files changed, 2 insertions(+), 26 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 3fd0b9e106..0bb10486ea 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index aa700c5194..a9df75327c 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -186,32 +186,8 @@ close_pend_loop(S, N) -> end. close_port(S) -> - case erlang:process_info(self(), trap_exit) of - {trap_exit,true} -> - %% Ensure exit message and consume it - link(S), - %% This is still not a perfect solution. - %% - %% The problem is to close the port and consume any exit - %% message while not knowing if this process traps exit - %% nor if this process has a link to the port. Here we - %% just knows that this process traps exit. - %% - %% If we right here get killed for some reason that exit - %% signal will propagate to the port and onwards to anyone - %% that is linked to the port. E.g when we close a socket - %% that is not ours. - %% - %% The problem can be solved with lists:member on our link - %% list but we deem that as potentially too expensive. We - %% need an is_linked/1 function or guard, or we need - %% a port_close function that can atomically unlink... - catch erlang:port_close(S), - receive {'EXIT',S,_} -> ok end; - {trap_exit,false} -> - catch erlang:port_close(S), - ok - end. + catch erlang:port_close(S), + receive {'EXIT',S,_} -> ok after 0 -> ok end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% -- cgit v1.2.3 From 8f25405251c694f0660d5d7b2b24de1218888439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 25 Nov 2013 17:55:35 +0100 Subject: erts: Let cc determine default wordsize for darwin build --- erts/configure.in | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index ba80fdbbbe..bad748d8fe 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -393,30 +393,17 @@ if test X${enable_darwin_64bit} = Xyes -o X${enable_m64_build} = Xyes; then ;; esac else - case $host_os in - darwin*) - case $CFLAGS in - *-m32*) - ;; - *) - CFLAGS="-m32 $CFLAGS" - ;; - esac - ;; - *) - if test X${enable_m32_build} = Xyes; - then - enable_hipe=no; - case $CFLAGS in - *-m32*) - ;; - *) - CFLAGS="-m32 $CFLAGS" - ;; - esac ; - fi - ;; - esac + if test X${enable_m32_build} = Xyes; + then + enable_hipe=no; + case $CFLAGS in + *-m32*) + ;; + *) + CFLAGS="-m32 $CFLAGS" + ;; + esac ; + fi fi -- cgit v1.2.3 From a1ce4da11ca0710a166e5048251aa1e128a83695 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 26 Nov 2013 20:25:53 +0100 Subject: erts: Fix invalid read in bitstring comparison Valgrind complained that erts_cmp_bits sometimes read one byte too much from bitstrings. The byte was never used and the invalid read is safe in opt-VM due to padding in binary allocation (CHICKED_PAD). --- erts/emulator/beam/erl_bits.c | 79 +++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 30 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 43eb691338..d5b0e341a1 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2012. All Rights Reserved. + * Copyright Ericsson AB 1999-2013. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -1810,6 +1810,11 @@ erts_cmp_bits(byte* a_ptr, size_t a_offs, byte* b_ptr, size_t b_offs, size_t siz Uint rshift; int cmp; + ASSERT(a_offs < 8 && b_offs < 8); + + if (size == 0) + return 0; + if (((a_offs | b_offs | size) & 7) == 0) { int byte_size = size >> 3; return sys_memcmp(a_ptr, b_ptr, byte_size); @@ -1818,13 +1823,15 @@ erts_cmp_bits(byte* a_ptr, size_t a_offs, byte* b_ptr, size_t b_offs, size_t siz /* Compare bit by bit until a_ptr is aligned on byte boundary */ a = *a_ptr++; b = *b_ptr++; - while (size > 0) { + for (;;) { a_bit = get_bit(a, a_offs); b_bit = get_bit(b, b_offs); if ((cmp = (a_bit-b_bit)) != 0) { return cmp; } - size--; + if (--size == 0) + return 0; + b_offs++; if (b_offs == 8) { b_offs = 0; @@ -1839,37 +1846,49 @@ erts_cmp_bits(byte* a_ptr, size_t a_offs, byte* b_ptr, size_t b_offs, size_t siz } /* Compare byte by byte as long as at least 8 bits remain */ - lshift = b_offs; - rshift = 8 - lshift; - while (size >= 8) { - byte b_cmp = (b << lshift); - b = *b_ptr++; - b_cmp |= b >> rshift; - if ((cmp = (a - b_cmp)) != 0) { - return cmp; - } + if (size >= 8) { + lshift = b_offs; + rshift = 8 - lshift; + for (;;) { + byte b_cmp = (b << lshift); + b = *b_ptr++; + b_cmp |= b >> rshift; + if ((cmp = (a - b_cmp)) != 0) { + return cmp; + } + size -= 8; + if (size < 8) + break; + a = *a_ptr++; + } + + if (size == 0) + return 0; a = *a_ptr++; - size -= 8; } /* Compare the remaining bits bit by bit */ - while (size > 0) { - a_bit = get_bit(a, a_offs); - b_bit = get_bit(b, b_offs); - if ((cmp = (a_bit-b_bit)) != 0) { - return cmp; - } - a_offs++; - if (a_offs == 8) { - a_offs = 0; - a = *a_ptr++; - } - b_offs++; - if (b_offs == 8) { - b_offs = 0; - b = *b_ptr++; - } - size--; + if (size > 0) { + for (;;) { + a_bit = get_bit(a, a_offs); + b_bit = get_bit(b, b_offs); + if ((cmp = (a_bit-b_bit)) != 0) { + return cmp; + } + if (--size == 0) + return 0; + + a_offs++; + if (a_offs == 8) { + a_offs = 0; + a = *a_ptr++; + } + b_offs++; + if (b_offs == 8) { + b_offs = 0; + b = *b_ptr++; + } + } } return 0; -- cgit v1.2.3 From f3589a79de29f1a4e6a2d03518739af214ae9600 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 26 Nov 2013 20:31:43 +0100 Subject: erts: Optimize comparison for bitstrings with byte aligned start --- erts/emulator/beam/erl_bits.c | 44 +++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index d5b0e341a1..872ded4be7 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -1823,25 +1823,27 @@ erts_cmp_bits(byte* a_ptr, size_t a_offs, byte* b_ptr, size_t b_offs, size_t siz /* Compare bit by bit until a_ptr is aligned on byte boundary */ a = *a_ptr++; b = *b_ptr++; - for (;;) { - a_bit = get_bit(a, a_offs); - b_bit = get_bit(b, b_offs); - if ((cmp = (a_bit-b_bit)) != 0) { - return cmp; - } - if (--size == 0) - return 0; + if (a_offs) { + for (;;) { + a_bit = get_bit(a, a_offs); + b_bit = get_bit(b, b_offs); + if ((cmp = (a_bit-b_bit)) != 0) { + return cmp; + } + if (--size == 0) + return 0; - b_offs++; - if (b_offs == 8) { - b_offs = 0; - b = *b_ptr++; - } - a_offs++; - if (a_offs == 8) { - a_offs = 0; - a = *a_ptr++; - break; + b_offs++; + if (b_offs == 8) { + b_offs = 0; + b = *b_ptr++; + } + a_offs++; + if (a_offs == 8) { + a_offs = 0; + a = *a_ptr++; + break; + } } } @@ -1879,10 +1881,8 @@ erts_cmp_bits(byte* a_ptr, size_t a_offs, byte* b_ptr, size_t b_offs, size_t siz return 0; a_offs++; - if (a_offs == 8) { - a_offs = 0; - a = *a_ptr++; - } + ASSERT(a_offs < 8); + b_offs++; if (b_offs == 8) { b_offs = 0; -- cgit v1.2.3 From 35b4fd5d91cbeb1b9c8540996624856134e091e9 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 27 Nov 2013 18:58:14 +0100 Subject: erts: Fix invalid read when appending binaries during call trace Found by valgrind. Probably safe on opt-VM due to CHICKEN_PAD. --- erts/emulator/beam/erl_bits.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 872ded4be7..73765772c8 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -1491,7 +1491,7 @@ erts_bs_private_append(Process* p, Eterm bin, Eterm build_size_term, Uint unit) bptr->flags = 0; bptr->orig_size = new_size; erts_refc_init(&bptr->refc, 1); - sys_memcpy(bptr->orig_bytes, binp->orig_bytes, pb->size); + sys_memcpy(bptr->orig_bytes, binp->orig_bytes, binp->orig_size); pb->flags |= PB_IS_WRITABLE | PB_ACTIVE_WRITER; pb->val = bptr; pb->bytes = (byte *) bptr->orig_bytes; -- cgit v1.2.3 From ce785835f3fcbab8b6c07e2415b5f636d181c28d Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 14 Oct 2013 23:26:56 +0200 Subject: Remove uninitialized use of new_crr in erl_alloc_util When the offending code is reached, new_crr is either uninitalized or have been set to NULL. This patch removes the following warning: beam/erl_alloc_util.c:3510:6: warning: variable 'new_crr' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized] if (!(flags & CFLG_FORCE_MSEG)) { ^~~~~~~~~~~~~~~~~~~~~~~~~~ beam/erl_alloc_util.c:3567:23: note: uninitialized use occurs here DEBUG_SAVE_ALIGNMENT(new_crr); ^~~~~~~ beam/erl_alloc_util.c:674:51: note: expanded from macro 'DEBUG_SAVE_ALIGNMENT' UWord algnmnt__ = sizeof(Unit_t) - (((UWord) (C)) % sizeof(Unit_t));\ ^ beam/erl_alloc_util.c:3510:2: note: remove the 'if' if its condition is always true if (!(flags & CFLG_FORCE_MSEG)) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ beam/erl_alloc_util.c:3438:23: note: initialize the variable 'new_crr' to silence this warning Carrier_t *new_crr, *old_crr; ^ = NULL --- erts/emulator/beam/erl_alloc_util.c | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 3914537d0d..c6cea0185f 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -3570,7 +3570,6 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) /* Old carrier unchanged; restore... */ STAT_SYS_ALLOC_SBC_ALLOC(allctr, old_crr_sz, old_blk_sz); } - DEBUG_SAVE_ALIGNMENT(new_crr); return new_blk; } #endif -- cgit v1.2.3 From 2567dfefa9426c61cdbea3c984e6c7e7d6dad1ea Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 14 Oct 2013 23:38:27 +0200 Subject: Properly mark erl_assert_error as non-returning --- erts/emulator/beam/sys.h | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index d22f125945..31252ed78f 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -149,9 +149,28 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType; # define ERTS_EXIT_AFTER_DUMP exit #endif +/* In VC++, noreturn is a declspec that has to be before the types, + * but in GNUC it is an att ribute to be placed between return type + * and function name, hence __decl_noreturn __noreturn + */ +#if __GNUC__ +# define __decl_noreturn +# define __noreturn __attribute__((noreturn)) +#else +# if defined(__WIN32__) && defined(_MSC_VER) +# define __noreturn +# define __decl_noreturn __declspec(noreturn) +# else +# define __noreturn +# define __decl_noreturn +# endif +#endif + #define ERTS_ASSERT(e) \ ((void) ((e) ? 1 : (erl_assert_error(#e, __func__, __FILE__, __LINE__), 0))) -void erl_assert_error(const char* expr, const char *func, const char* file, int line); + +__decl_noreturn void __noreturn erl_assert_error(const char* expr, const char *func, + const char* file, int line); #ifdef DEBUG # define ASSERT(e) ERTS_ASSERT(e) @@ -192,23 +211,6 @@ void erl_assert_error(const char* expr, const char *func, const char* file, int # define erts_align_attribute(SZ) #endif -/* In VC++, noreturn is a declspec that has to be before the types, - * but in GNUC it is an att ribute to be placed between return type - * and function name, hence __decl_noreturn __noreturn - */ -#if __GNUC__ -# define __decl_noreturn -# define __noreturn __attribute__((noreturn)) -#else -# if defined(__WIN32__) && defined(_MSC_VER) -# define __noreturn -# define __decl_noreturn __declspec(noreturn) -# else -# define __noreturn -# define __decl_noreturn -# endif -#endif - /* ** Data types: ** -- cgit v1.2.3 From 2b8016ebb9d487be590c01390928d1cc83c8225c Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 14 Oct 2013 23:39:26 +0200 Subject: Fix erts_check_off_heap2 assertion The test needs to be false to fail, not true. This was noticed with the following warning, where marking erl_assert_error as non-returning didn't help: beam/erl_gc.c:2772:2: warning: variable 'refc' is used uninitialized whenever switch default is taken [-Wsometimes-uninitialized] default: ^~~~~~~ beam/erl_gc.c:2775:26: note: uninitialized use occurs here ERTS_CHK_OFFHEAP_ASSERT(refc >= 1); ^~~~ beam/erl_gc.c:2738:11: note: expanded from macro 'ERTS_CHK_OFFHEAP_ASSERT' if (!(EXP)) \ ^ beam/erl_gc.c:2759:18: note: initialize the variable 'refc' to silence this warning erts_aint_t refc; ^ = 0 --- erts/emulator/beam/erl_gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 8ba94d89e9..e89725c190 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2770,7 +2770,7 @@ erts_check_off_heap2(Process *p, Eterm *htop) refc = erts_refc_read(&u.ext->node->refc, 1); break; default: - ASSERT(!!"erts_check_off_heap2: Invalid thing_word"); + ASSERT(!"erts_check_off_heap2: Invalid thing_word"); } ERTS_CHK_OFFHEAP_ASSERT(refc >= 1); #ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_LIST -- cgit v1.2.3 From 04e0926b73b23eea6894314a8a99bb429c689a59 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 14 Oct 2013 23:42:06 +0200 Subject: Silence a warning in erl_poll about an empty loop body The warning is: sys/common/erl_poll.c:2513:72: warning: for loop has empty body [-Wempty-body] for (prev_ps = pollsets; ps != prev_ps->next; prev_ps = prev_ps->next); --- erts/emulator/sys/common/erl_poll.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 7676d8872a..0a58a625b2 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -2510,7 +2510,8 @@ ERTS_POLL_EXPORT(erts_poll_destroy_pollset)(ErtsPollSet ps) pollsets = pollsets->next; else { ErtsPollSet prev_ps; - for (prev_ps = pollsets; ps != prev_ps->next; prev_ps = prev_ps->next); + for (prev_ps = pollsets; ps != prev_ps->next; prev_ps = prev_ps->next) + ; ASSERT(ps == prev_ps->next); prev_ps->next = ps->next; } -- cgit v1.2.3 From bcb310808d548ece9a7c6b78ab0846d2cd06e81f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 21 Nov 2013 18:19:12 +0100 Subject: erts: Remove unused file winsock_func.h --- erts/emulator/drivers/win32/winsock_func.h | 102 ----------------------------- 1 file changed, 102 deletions(-) delete mode 100644 erts/emulator/drivers/win32/winsock_func.h (limited to 'erts') diff --git a/erts/emulator/drivers/win32/winsock_func.h b/erts/emulator/drivers/win32/winsock_func.h deleted file mode 100644 index 9d2c099c4d..0000000000 --- a/erts/emulator/drivers/win32/winsock_func.h +++ /dev/null @@ -1,102 +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% - */ - -typedef struct _WinSockFuncs { - int (WSAAPI *WSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData); - int (WSAAPI *WSACleanup)(void); - int (WSAAPI *WSAGetLastError)(void); - DWORD (WSAAPI *WSAWaitForMultipleEvents) (DWORD cEvents, - const WSAEVENT FAR * lphEvents, - BOOL fWaitAll, - DWORD dwTimeout, - BOOL fAlertable); - WSAEVENT (WSAAPI *WSACreateEvent)(void); - BOOL (WSAAPI *WSACloseEvent)(WSAEVENT hEvent); - - BOOL (WSAAPI *WSASetEvent)(WSAEVENT hEvent); - BOOL (WSAAPI *WSAResetEvent)(WSAEVENT hEvent); - int (WSAAPI *WSAEventSelect)(SOCKET s, WSAEVENT hEventObject, - long lNetworkEvents); - int (WSAAPI *WSAEnumNetworkEvents)(SOCKET s, - WSAEVENT hEventObject, - LPWSANETWORKEVENTS lpNetworkEvents); - int (WSAAPI *WSAIoctl)(SOCKET s, - DWORD dwIoControlCode, - LPVOID lpvInBuffer, - DWORD cbInBuffer, - LPVOID lpvOUTBuffer, - DWORD cbOUTBuffer, - LPDWORD lpcbBytesReturned, - LPWSAOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE - ); - SOCKET (WSAAPI *accept)(SOCKET s, struct sockaddr FAR *addr, - int FAR *addrlen); - int (WSAAPI *bind)(SOCKET s, const struct sockaddr FAR *addr, - int namelen); - int (WSAAPI *closesocket)(SOCKET s); - int (WSAAPI *connect)(SOCKET s, const struct sockaddr FAR *name, - int namelen); - int (WSAAPI *ioctlsocket)(SOCKET s, long cmd, u_long FAR *argp); - int (WSAAPI *getsockopt)(SOCKET s, int level, int optname, - char FAR * optval, int FAR *optlen); - u_long (WSAAPI *htonl)(u_long hostlong); - u_short (WSAAPI *htons)(u_short hostshort); - unsigned long (WSAAPI *inet_addr)(const char FAR * cp); - char FAR * (WSAAPI *inet_ntoa)(struct in_addr in); - int (WSAAPI *listen)(SOCKET s, int backlog); - u_short (WSAAPI *ntohs)(u_short netshort); - int (WSAAPI *recv)(SOCKET s, char FAR * buf, int len, int flags); - int (WSAAPI *send)(SOCKET s, const char FAR * buf, int len, int flags); - int (WSAAPI *setsockopt)(SOCKET s, int level, int optname, - const char FAR * optval, int optlen); - int (WSAAPI *shutdown)(SOCKET s, int how); - SOCKET (WSAAPI *socket)(int af, int type, int protocol); - struct hostent FAR * (WSAAPI *gethostbyname)(const char FAR * name); - struct hostent FAR * (WSAAPI *gethostbyaddr)(const char FAR *addr, - int addrlen, int addrtype); - int (WSAAPI *gethostname)(char FAR * name, int namelen); - struct servent FAR * (WSAAPI *getservbyname)(const char FAR * name, - const char FAR * proto); - struct servent FAR * (WSAAPI *getservbyport)(int port, - const char FAR * proto); - int (WSAAPI *getsockname)(SOCKET sock, struct sockaddr FAR *name, - int FAR *namelen); - - /* - * New, added for inet_drv. - */ - - int (WSAAPI *getpeername)(SOCKET s, struct sockaddr FAR * name, - int FAR * namelen); - u_long (WSAAPI *ntohl)(u_long netlong); - int (WSAAPI *WSASend)(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, - LPDWORD lpNumberOfBytesSent, DWORD dwFlags, - LPWSAOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); - int (WSAAPI *sendto)(SOCKET s, const char FAR * buf, int len, - int flags, const struct sockaddr FAR * to, int tolen); - int (WSAAPI *recvfrom)(SOCKET s, char FAR * buf, int len, int flags, - struct sockaddr FAR * from, int FAR * fromlen); -} WinSockFuncs; - - -extern WinSockFuncs winSock; - -extern int tcp_lookup_functions(void); -- cgit v1.2.3 From f8c1256fc624ef79aa4f25574278c0cd2f9f1106 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Tue, 15 Oct 2013 13:06:20 +0200 Subject: Compile in_heapfrag() only in debug mode --- erts/emulator/beam/erl_message.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 325d77e911..6a9030fd99 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -46,10 +46,12 @@ ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message, +#ifdef DEBUG static ERTS_INLINE int in_heapfrag(const Eterm* ptr, const ErlHeapFragment *bp) { return ((unsigned)(ptr - bp->mem) < bp->used_size); } +#endif void -- cgit v1.2.3 From 30e97f13e85efdc2d68282996cc6f907fc1cf9d6 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 2 Dec 2013 18:21:16 +0100 Subject: Documentation fix --- erts/doc/src/erlang.xml | 6 +++++- erts/doc/src/erts_alloc.xml | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index bc38055b62..287346340c 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5316,7 +5316,11 @@ ok As of erts version 5.6.1 the return value is a list of {instance, InstanceNo, InstanceInfo} tuples where InstanceInfo contains information about - a specific instance of the allocator. + a specific instance of the allocator. As of erts version + 5.10.4 the returned list when calling + erlang:system_info({allocator, mseg_alloc}) also + include an {erts_mmap, _} tuple as one element + in the list. If Alloc is not a recognized allocator, undefined is returned. If Alloc is disabled, false is returned.

diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index a0ec89f398..49ee740a73 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -332,8 +332,11 @@ Set super carrier size (in MB). The super carrier size defaults to zero; i.e, the super carrier is by default disabled. The super carrier is a large continuous area in the virtual address space. - The system will always try to create new carriers in the super - carrier. + mseg_alloc will always try to create new carriers in the super + carrier if it exists. Note that the alloc_util framework may + create sys_alloc carriers. For more information on this, see the + documentation of the +MMsco + flag.

NOTE: The super carrier cannot be enabled nor disabled on halfword heap systems. This flag will be -- cgit v1.2.3 From 200886ab7d03572cfc3e5ff2d686eff2010363c5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 2 Dec 2013 18:49:55 +0100 Subject: erts: Reduce wasted execution time for match_spec_SUITE:otp_9422 --- erts/emulator/test/match_spec_SUITE.erl | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index b56b7ce525..bcc46d78ba 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -213,7 +213,7 @@ test_3(Config) when is_list(Config) -> otp_9422(doc) -> []; otp_9422(Config) when is_list(Config) -> - Laps = 1000, + Laps = 10000, ?line Fun1 = fun() -> otp_9422_tracee() end, ?line P1 = spawn_link(?MODULE, loop_runner, [self(), Fun1, Laps]), io:format("spawned ~p as tracee\n", [P1]), @@ -230,7 +230,7 @@ otp_9422(Config) when is_list(Config) -> %%receive after 10*1000 -> ok end, stop_collect(P1), - stop_collect(P2), + stop_collect(P2, abort), ok. otp_9422_tracee() -> @@ -975,7 +975,9 @@ start_collect(P) -> P ! {go, self()}. stop_collect(P) -> - P ! {done, self()}, + stop_collect(P, done). +stop_collect(P, Order) -> + P ! {Order, self()}, receive {gone, P} -> ok @@ -1008,7 +1010,13 @@ loop_runner_cont(_Collector, _Fun, Laps, Laps) -> end; loop_runner_cont(Collector, Fun, N, Laps) -> Fun(), - loop_runner_cont(Collector, Fun, N+1, Laps). + receive + {abort, Collector} -> + io:format("loop_runner ~p aborted after ~p of ~p laps\n", [self(), N+1, Laps]), + Collector ! {gone, self()} + after 0 -> + loop_runner_cont(Collector, Fun, N+1, Laps) + end. f1(X) -> -- cgit v1.2.3 From 3c00452a81dfde57f85c882029186cfa3c0d348d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Lid=C3=A9n?= Date: Fri, 6 Dec 2013 12:51:29 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 54676 -> 54696 bytes erts/preloaded/ebin/erlang.beam | Bin 94264 -> 94280 bytes erts/preloaded/ebin/erts_internal.beam | Bin 3276 -> 3272 bytes erts/preloaded/ebin/init.beam | Bin 48788 -> 48804 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1436 -> 1464 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1352 -> 1340 bytes erts/preloaded/ebin/prim_file.beam | Bin 44592 -> 44612 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72932 -> 72916 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23400 -> 23436 bytes erts/preloaded/ebin/zlib.beam | Bin 12828 -> 13148 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index ae0ee5c4b5..cedfbdac46 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index bc0690f97f..ecb65af214 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 9f92349803..881fea4665 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 089f40de5b..93bc5fd77a 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 4dbe6cc406..45e53c281b 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 6c7b7e5262..ab51515d78 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index de88b33fea..eba78fd22a 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index aa0371e6b2..8c136ceef4 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 7421599da0..c952a89413 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 690b90adb5..4bcf5d582f 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 635553bfcfdb33c337ece558240de3692920ca95 Mon Sep 17 00:00:00 2001 From: Ryan Zezeski Date: Sat, 7 Dec 2013 13:58:46 -0500 Subject: Fix DTrace build on Illumos DTrace was recently patched in Illumos to fail to create an object file if no probes are found. * https://www.illumos.org/issues/4248 * https://github.com/illumos/illumos-gate/commit/54a20ab41aadcb81c53e72fc65886e964e9add59 This patch fixes two issues: * Modify the configure script to pass an object file to `dtrace -G` that actually invokes a probe. * Remove creation of `dtrace_user.o` from the dyntrace Makefile. In a previous commit [1] Scott Fritchie relocated all the user probes into the VM proper due to difficulties with DTrace probes in shared libraries. The `dtrace_user.d` file is now empty and generates a header file with nothing in it. There is no longer any reason to generate `dtrace_user.o` because all the probes are in the VM. Thus all the steps for building `dtrace_user.o` have been removed. [1]: https://github.com/erlang/otp/commit/75552bd3bb4e7f3cf4dab81a5c81cf73b1d3fb99 --- erts/configure.in | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index ba80fdbbbe..4edf1883cd 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3804,10 +3804,16 @@ if test "$enable_dtrace_test" = "yes" ; then if ! dtrace -h $DTRACE_CPP -Iemulator/beam -o ./foo-dtrace.h -s emulator/beam/erlang_dtrace.d; then AC_MSG_ERROR([Could not precompile erlang_dtrace.d: dtrace -h failed]) fi - rm -f foo-dtrace.h + + $RM -f dtest.{o,c} + cat > dtest.c <<_DTEST + #include "foo-dtrace.h" + int main(void) { ERLANG_DIST_PORT_BUSY_ENABLED(); return 0; } +_DTEST + $CC $CFLAGS -c -o dtest.o dtest.c $RM -f $DTRACE_2STEP_TEST - if dtrace -G $DTRACE_CPP $DTRACE_BITS_FLAG -Iemulator/beam -o $DTRACE_2STEP_TEST -s emulator/beam/erlang_dtrace.d 2> /dev/null && \ + if dtrace -G $DTRACE_CPP $DTRACE_BITS_FLAG -Iemulator/beam -o $DTRACE_2STEP_TEST -s emulator/beam/erlang_dtrace.d dtest.o && \ test -f $DTRACE_2STEP_TEST ; then rm $DTRACE_2STEP_TEST DTRACE_ENABLED_2STEP=yes @@ -3815,6 +3821,8 @@ if test "$enable_dtrace_test" = "yes" ; then else AC_MSG_NOTICE([dtrace precompilation for 1-stage DTrace successful]) fi + $RM -f dtest.{o,c} foo-dtrace.h + DTRACE_ENABLED=yes case $OPSYS in linux) -- cgit v1.2.3 From 94b1def454ebd058c3beebe364cce22b712d0506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Lid=C3=A9n?= Date: Mon, 9 Dec 2013 14:38:00 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 54672 -> 54696 bytes erts/preloaded/ebin/erlang.beam | Bin 94248 -> 94264 bytes erts/preloaded/ebin/erts_internal.beam | Bin 3260 -> 3272 bytes erts/preloaded/ebin/init.beam | Bin 48640 -> 48652 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1452 -> 1464 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1328 -> 1340 bytes erts/preloaded/ebin/prim_file.beam | Bin 44252 -> 44264 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72316 -> 72280 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23416 -> 23428 bytes erts/preloaded/ebin/zlib.beam | Bin 12788 -> 12800 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index ece0e6d5e8..564dfbcf2b 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index c53f6407f0..9dd826fa26 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 3ab52615ab..1ef9b86a38 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 89d3cd6b83..83b5b8f7dc 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 733d3fb587..c8cc31503f 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 80bd1b3331..4f9a963f60 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index a73a2f0db1..2a187c23cb 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 0bb10486ea..656fbc6627 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 562003df0d..e1f4a06b40 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index e4a22ff2c7..cc45a8e6f1 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From f265dc315485734adda7e7a8a88f6b4c02211477 Mon Sep 17 00:00:00 2001 From: "Brian L. Troutwine" Date: Mon, 9 Dec 2013 09:31:38 -0800 Subject: Clean up some awkward wording around the +spp flag. This issue was pointed out by lpgauth in #erlounge. While the intention is clear, the original wording was a bit dodgy. I'm no grammarian--though I am a native speaker--so it's quite possible the new wording I've introduced is not impeachable. Should be idiomatic, though. Signed-off-by: Brian L. Troutwine --- erts/doc/src/erl.xml | 20 ++++++++++---------- erts/doc/src/erlang.xml | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index bf0d132955..34c9d8ea61 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1126,18 +1126,18 @@ +spp Bool

Set default scheduler hint for port parallelism. If set to - true, the VM will schedule port tasks when it by this can - improve the parallelism in the system. If set to false, - the VM will try to perform port tasks immediately and by this - improve latency at the expense of parallelism. If this - flag has not been passed, the default scheduler hint for port - parallelism is currently false. The default used can be - inspected in runtime by calling - erlang:system_info(port_parallelism). + true, the VM will schedule port tasks when doing so will + improve parallelism in the system. If set to false, the VM + will try to perform port tasks immediately, improving latency at the + expense of parallelism. If this flag has not been passed, the + default scheduler hint for port parallelism is currently + false. The default used can be inspected in runtime by + calling erlang:system_info(port_parallelism). The default can be overriden on port creation by passing the parallelism - option to - open_port/2

. + option to open_port/2

.
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index a498fc24df..13665f3e7c 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2886,11 +2886,11 @@ os_prompt% {parallelism, Boolean}

Set scheduler hint for port parallelism. If set to true, - the VM will schedule port tasks when it by this can improve the + the VM will schedule port tasks when doing so will improve parallelism in the system. If set to false, the VM will - try to perform port tasks immediately and by this improving the - latency at the expense of parallelism. The default can be set on - system startup by passing the + try to perform port tasks immediately, improving latency at the + expense of parallelism. The default can be set on system startup + by passing the +spp command line argument to erl(1).

-- cgit v1.2.3 From 25237481ccccd3ddfa74582dc267632ad618ba30 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 9 Dec 2013 20:12:33 +0100 Subject: Prepare release --- erts/doc/src/notes.xml | 259 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index b25e4ccbec..8c008c493e 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,265 @@

This document describes the changes made to the ERTS application.

+
Erts 5.10.4 + +
Fixed Bugs and Malfunctions + + +

+ When normalizing paths, erl_prim_loader would always + convert backslash to forward slash. This is correct on + Windows, but not on other operating systems. + erl_prim_loader now checks which OS is running before + performing this conversion.

+

+ Own Id: OTP-11170

+
+ +

+ Fixed syslog defines and defined LOG_ERR for systems + without syslog.h. Thanks to Matt Lewandowsky.

+

+ Own Id: OTP-11349

+
+ +

+ Check all pattern arguments passed to binary:matches/2. + Thanks to Mike Sassak.

+

+ Own Id: OTP-11350

+
+ +

+ Fix two small silent rules omissions. Thanks to Anthony + Ramine.

+

+ Own Id: OTP-11351

+
+ +

+ Teach configure to detect if posix_memalign cannot align + to more than the system page size.

+

+ For cross-compiled systems a new environment variable + called erl_xcomp_posix_memalign has been introduced to + indicate whether posix_memalign should be used.

+

+ Own Id: OTP-11371

+
+ +

+ Fix bsr bug occurring when shifting a huge number a huge + number of bits to the right. Thanks to Lars Hesel + Christensen.

+

+ Own Id: OTP-11381

+
+ +

+ Fix memory leak for distributed monitors

+

+ Own Id: OTP-11410

+
+ +

+ Fix various typos in erts, kernel and ssh. Thanks to + Martin Hässler.

+

+ Own Id: OTP-11414

+
+ +

+ Crashdumps initiated by out-of-memory on process spawn + could cause the beam to segfault during crashdump writing + due to invalid pointers.

+

+ The pointers are invalid since the process creation never + finished. This fix removes these processes from the + printouts. Reported by Richard Carlsson.

+

+ Own Id: OTP-11420

+
+ +

+ Crash dumps from 64-bit Erlang machines would have all + memory addresses truncated to 32 bits, which could cause + trouble inspecting processes message queues and stacks in + the crashdump viewer.

+

+ Own Id: OTP-11450

+
+ +

+ Threads other than schedulers threads could make thread + unsafe accesses when support for migration of memory + carriers had been enabled, i.e., when the +M<S>acul + command line flag had been passed to erl. This could cause + corruption of the VMs internal state.

+

+ This bug was introduced in erts-5.10.2 when the support + for migration of memory carriers was introduced.

+

+ Own Id: OTP-11456 Aux Id: OTP-10279

+
+ +

+ Fix bug in binary_to_term for invalid bitstrings + and very large binaries (>2Gb).

+

+ Own Id: OTP-11479

+
+ +

+ Under rare circumstances a process calling inet:close/1, + gen_tcp:close/1, + gen_udp:close/1, + or gen_sctp:close/1 + could hang in the call indefinitely.

+

+ Own Id: OTP-11491

+
+ +

+ Fix bug that could cause a 32-bit emulator to always + crash at start (since R16B01) depending on the alignment + of static data in the beam executable.

+

+ Own Id: OTP-11496

+
+ +

+ Fix benign bugs regarding bitstring compare. Only a + nuisance for debug and valgrind VM.

+

+ Own Id: OTP-11501

+
+ +

+ Silence warnings (Thanks to Anthony Ramine)

+

+ Own Id: OTP-11517

+
+ +

+ The default wordsize of the emulator (beam) is now + determined by compiler default on Mac OSX (Darwin). This + was previously forced to 32bits by the configure script + unless otherwise specified.

+

+ Own Id: OTP-11521

+
+
+
+ + +
Improvements and New Features + + +

+ A new memory allocation feature called "super carrier" + has been introduced. The super carrier feature can be + used in different ways. It can for example be used for + pre-allocation of all memory that the runtime system + should be able to use.

+

+ By default the super carrier is disabled. It is enabled + by passing the +MMscs <size in + MB> command line argument. For more + information see the documentation of the +MMsco, + +MMscrfsd, + +MMscrpm, + +MMscs, + +MMusac, + and, +Mlpm + command line arguments in the erts_alloc(3) + documentation.

+

+ Since it is disabled by default there should be no impact + on system characteristics if not used.

+

+ This change has been marked as a potential + incompatibility since the returned list when calling + erlang:system_info({allocator, + mseg_alloc}) now also include an + {erts_mmap, _} tuple as one element in the list.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11149

+
+ +

+ Added erlang:system_info(ets_limit) to provide a way to + retrieve the runtime's maximum number of ETS tables. + Thanks to Steve Vinoski

+

+ Own Id: OTP-11362

+
+ +

+ Add new BIF os:unsetenv/1 which deletes an environment + variable. Thanks to Martin Hässler.

+

+ Own Id: OTP-11446

+
+ +

Introduced a new guarantee regarding exit signals + from ports:

If the process calling one of the + synchronous port BIFs listed below is linked to the port + identified by the first argument, and the port exits + before sending the result of the port operation, the exit + signal issued due to this link will be received by the + processes before the BIF returns, or fail with an + exception due to the port not being open.

The + synchronous port BIFs are:

port_close/1 + port_command/2 + port_command/3 + port_connect/2 + port_control/3 + erlang:port_call/3 + erlang:port_info/1 + erlang:port_info/2 +

Note that some ports under certain + circumstances unlink themselves from the calling process + before exiting, i.e. even though the process linked + itself to the port there might be no link triggering an + exit signal.

Characteristics impact: The return + or exception from the synchronous port BIF will be + delayed if the port simultaneously exit due to some issue + unrelated to the outstanding synchronous port BIF call. + In all other cases characteristics are unchanged.

+

+ Own Id: OTP-11489

+
+
+
+ +
+
Erts 5.10.3.1
Improvements and New Features -- cgit v1.2.3 From 51918f4322b3d30c89ba9b6ca2fbfe5ad6c6b51b Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 Dec 2013 09:52:50 +0100 Subject: Update versions of OTP, erts, kernel, and stdlib Update versions of OTP, erts, kernel, and stdlib to comply with the new version scheme decided by the OTP technical board. --- erts/emulator/beam/erl_bif_info.c | 4 ++-- erts/etc/common/erlexec.c | 30 +++++++++++++++++++++++++++++- erts/vsn.mk | 4 ++-- 3 files changed, 33 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 8fa3aa29eb..2b40f9272d 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -65,8 +65,8 @@ static Export *gather_gc_info_res_trap; #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) /* Keep erts_system_version as a global variable for easy access from a core */ -static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE - " (erts-" ERLANG_VERSION ")" +static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE + " [erts-" ERLANG_VERSION "]" #if !HEAP_ON_C_STACK && !HALFWORD_HEAP " [no-c-stack-objects]" #endif diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 00540662fe..1d7811d570 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -1971,9 +1971,36 @@ get_file_args(char *filename, argv_buf *abp, argv_buf *xabp) } } +static void +write_erl_otp_flags(char *bufp) +{ + /* ERL_OTP_FLAGS */ + int ix = 0; + char *otp_p; + char otp[] = OTP_SYSTEM_VERSION; + + bufp[ix++] = 'E'; + bufp[ix++] = 'R'; + bufp[ix++] = 'L'; + bufp[ix++] = '_'; + bufp[ix++] = 'O'; + bufp[ix++] = 'T'; + bufp[ix++] = 'P'; + for (otp_p = &otp[0]; '0' <= *otp_p && *otp_p <= '9'; otp_p++) + bufp[ix++] = *otp_p; + bufp[ix++] = '_'; + bufp[ix++] = 'F'; + bufp[ix++] = 'L'; + bufp[ix++] = 'A'; + bufp[ix++] = 'G'; + bufp[ix++] = 'S'; + bufp[ix] = '\0'; +} + static void initial_argv_massage(int *argc, char ***argv) { + char erl_otp_flags_buf[] = "ERL_OTP" OTP_SYSTEM_VERSION "_FLAGS"; argv_buf ab = {0}, xab = {0}; int ix, vix, ac; char **av; @@ -1989,7 +2016,8 @@ initial_argv_massage(int *argc, char ***argv) vix = 0; - av = build_args_from_env("ERL_" OTP_SYSTEM_VERSION "_FLAGS"); + write_erl_otp_flags(erl_otp_flags_buf); + av = build_args_from_env(erl_otp_flags_buf); if (av) avv[vix++].argv = av; diff --git a/erts/vsn.mk b/erts/vsn.mk index 8baf169d6f..30aa870144 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,8 +17,8 @@ # %CopyrightEnd% # -VSN = 5.11 -SYSTEM_VSN = R17A +VSN = 6.0 +SYSTEM_VSN = 17.0-rc0 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 289f77ff40833b8b95331efddeaaf29d1af21bae Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 11 Dec 2013 19:43:19 +0100 Subject: erts: Fix valgrind warning for re_SUITE:error_handling by making sure we at least can read the first four bytes of a compiled regexp where the 'magic_number' is located. --- erts/emulator/beam/erl_bif_re.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index 99c31738a5..448c6f6f6d 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -1196,8 +1196,8 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) ovsize = 3*(unsigned_val(tp[2])+1); code_size = binary_size(tp[5]); - if ((code_tmp = (const pcre *) - erts_get_aligned_binary_bytes(tp[5], &temp_alloc)) == NULL) { + code_tmp = (const pcre *) erts_get_aligned_binary_bytes(tp[5], &temp_alloc); + if (code_tmp == NULL || code_size < 4) { erts_free_aligned_binary_bytes(temp_alloc); BIF_ERROR(p, BADARG); } -- cgit v1.2.3 From a0988a638a92211ae0af6ec35ca99edfc05110aa Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 10 Nov 2012 21:32:31 +0100 Subject: Test named funs --- erts/emulator/test/binary_SUITE.erl | 7 ++++++- erts/emulator/test/fun_SUITE.erl | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index a340a805b5..44dbb2c588 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -935,7 +935,7 @@ otp_6817_try_bin(Bin) -> otp_8117(doc) -> "Some bugs in binary_to_term when 32-bit integers are negative."; otp_8117(suite) -> []; otp_8117(Config) when is_list(Config) -> - [otp_8117_do(Op,-(1 bsl N)) || Op <- ['fun',list,tuple], + [otp_8117_do(Op,-(1 bsl N)) || Op <- ['fun',named_fun,list,tuple], N <- lists:seq(0,31)], ok. @@ -944,6 +944,11 @@ otp_8117_do('fun',Neg) -> FunBin = term_to_binary(fun() -> ok end), ?line <> = FunBin, ?line bad_bin_to_term(<>); +otp_8117_do(named_fun,Neg) -> + % Named fun with negative num_free + FunBin = term_to_binary(fun F() -> F end), + ?line <> = FunBin, + ?line bad_bin_to_term(<>); otp_8117_do(list,Neg) -> %% List with negative length ?line bad_bin_to_term(<<131,104,2,108,Neg:32,97,11,104,1,97,12,97,13,106,97,14>>); diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 36ba4e0f48..8ad5f290ed 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -262,6 +262,16 @@ equality(Config) when is_list(Config) -> ?line false = eq(FF2, FF4), ?line false = eq(FF3, FF4), + %% EEP37 + H1 = fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end, + H2 = fun Pow(N, M) when M > 0 -> N * Pow(N, M - 1); Pow(_, 0) -> 1 end, + H1_copy = copy_term(H1), + + true = eq(H1, H1), + true = eq(H1, H1_copy), + true = eq(H2, H2), + false = eq(H1, H2), + ok. eq(X, X) -> true; -- cgit v1.2.3 From a929df291877df45c93303d22995bbbebf6a2c45 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 12 Nov 2012 10:07:37 +0100 Subject: Document named fun expressions --- erts/doc/src/absform.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 55aef9f8ab..4acc03b133 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -290,6 +290,18 @@ If E is where each is a function clause then Rep(E) = . + If E is + where is a variable and each + is a function clause then Rep(E) = + . + + If E is , + where each is a generator or a filter, then + Rep(E) = . + For Rep(W), see below. + If E is , a Mnesia record access + inside a query, then + Rep(E) = . If E is , then Rep(E) = , i.e., parenthesized expressions cannot be distinguished from their bodies. -- cgit v1.2.3 From 4b3462665c591dffa05294ce5ea94c6259446a04 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 12 Dec 2013 13:21:30 +0100 Subject: Increase versions for OTP_R16B03_yielding_binary_to_term --- erts/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index 9afd46b961..0eaad80407 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 5.10.4 +VSN = 5.10.4.0.1 SYSTEM_VSN = R16B03 # Port number 4365 in 4.2 -- cgit v1.2.3 From 9635f505739dd920d2b9297eb5e2d7f3b76518fa Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 13 Dec 2013 11:17:18 +0100 Subject: erts: Update windows erlang icon --- erts/etc/win32/erlang.ico | Bin 1398 -> 99678 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/etc/win32/erlang.ico b/erts/etc/win32/erlang.ico index cee8b58af9..7b62d31aa9 100644 Binary files a/erts/etc/win32/erlang.ico and b/erts/etc/win32/erlang.ico differ -- cgit v1.2.3 From 02f75aa7d187f1e2462f7b8793446b1f9be7c612 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 13 Dec 2013 16:10:03 +0100 Subject: erts: Fix compiler warning --- erts/emulator/drivers/common/inet_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 978a766de9..80937dfcc8 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1490,8 +1490,8 @@ static int load_ip_and_port unsigned int alen = len; char abuf [len]; int res = inet_get_address(abuf, (inet_address*) addr, &alen); - ASSERT(res==0); - res = 0; + ASSERT(res==0); (void)res; + /* Now "abuf" contains: Family(1b), Port(2b), IP(4|16b) */ /* NB: the following functions are safe to use, as they create tuples -- cgit v1.2.3 From d7b9addd8637f67ba670312033acf1c0695f5af6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 13 Dec 2013 16:17:30 +0100 Subject: erts: Refactor remove erts_sys_dll_open2 --- erts/emulator/beam/erl_bif_ddll.c | 2 +- erts/emulator/beam/erl_nif.c | 4 ++-- erts/emulator/beam/io.c | 2 +- erts/emulator/beam/sys.h | 3 +-- erts/emulator/sys/unix/erl_unix_sys_ddll.c | 2 +- erts/emulator/sys/win32/erl_win32_sys_ddll.c | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index 1c3e955f47..caee07e491 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -1524,7 +1524,7 @@ static int do_load_driver_entry(DE_Handle *dh, char *path, char *name) assert_drv_list_rwlocked(); - if ((res = erts_sys_ddll_open(path, &(dh->handle))) != ERL_DE_NO_ERROR) { + if ((res = erts_sys_ddll_open(path, &(dh->handle), NULL)) != ERL_DE_NO_ERROR) { return res; } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index e87959f0ab..de626ca4e1 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1407,7 +1407,7 @@ void* enif_dlopen(const char* lib, ErtsSysDdllError errdesc = ERTS_SYS_DDLL_ERROR_INIT; void* handle; void* init_func; - if (erts_sys_ddll_open2(lib, &handle, &errdesc) == ERL_DE_NO_ERROR) { + if (erts_sys_ddll_open(lib, &handle, &errdesc) == ERL_DE_NO_ERROR) { if (erts_sys_ddll_load_nif_init(handle, &init_func, &errdesc) == ERL_DE_NO_ERROR) { erts_sys_ddll_call_nif_init(init_func); } @@ -1626,7 +1626,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) "module '%T' not allowed", mod_atom); } else if (init_func == NULL && - (err=erts_sys_ddll_open2(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) { + (err=erts_sys_ddll_open(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) { const char slogan[] = "Failed to load NIF library"; if (strstr(errdesc.str, lib_name) != NULL) { ret = load_nif_error(BIF_P, "load_failed", "%s: '%s'", slogan, errdesc.str); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index d4623c0450..49af86b36a 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -7061,7 +7061,7 @@ void *driver_dl_open(char * path) int res; int *last_error_p = erts_smp_tsd_get(driver_list_last_error_key); int locked = maybe_lock_driver_list(); - if ((res = erts_sys_ddll_open(path, &ptr)) == 0) { + if ((res = erts_sys_ddll_open(path, &ptr, NULL)) == 0) { maybe_unlock_driver_list(locked); return ptr; } else { diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 31252ed78f..4a2a87a89a 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -661,8 +661,7 @@ typedef struct { #define ERTS_SYS_DDLL_ERROR_INIT {NULL} extern void erts_sys_ddll_free_error(ErtsSysDdllError*); extern void erl_sys_ddll_init(void); /* to initialize mutexes etc */ -extern int erts_sys_ddll_open2(const char *path, void **handle, ErtsSysDdllError*); -#define erts_sys_ddll_open(P,H) erts_sys_ddll_open2(P,H,NULL) +extern int erts_sys_ddll_open(const char *path, void **handle, ErtsSysDdllError*); extern int erts_sys_ddll_open_noext(char *path, void **handle, ErtsSysDdllError*); extern int erts_sys_ddll_load_driver_init(void *handle, void **function); extern int erts_sys_ddll_load_nif_init(void *handle, void **function,ErtsSysDdllError*); diff --git a/erts/emulator/sys/unix/erl_unix_sys_ddll.c b/erts/emulator/sys/unix/erl_unix_sys_ddll.c index 12c47d0088..8760b58839 100644 --- a/erts/emulator/sys/unix/erl_unix_sys_ddll.c +++ b/erts/emulator/sys/unix/erl_unix_sys_ddll.c @@ -101,7 +101,7 @@ void erl_sys_ddll_init(void) { /* * Open a shared object */ -int erts_sys_ddll_open2(const char *full_name, void **handle, ErtsSysDdllError* err) +int erts_sys_ddll_open(const char *full_name, void **handle, ErtsSysDdllError* err) { #if defined(HAVE_DLOPEN) char* dlname; diff --git a/erts/emulator/sys/win32/erl_win32_sys_ddll.c b/erts/emulator/sys/win32/erl_win32_sys_ddll.c index 2d3f073cc2..338f0d7386 100644 --- a/erts/emulator/sys/win32/erl_win32_sys_ddll.c +++ b/erts/emulator/sys/win32/erl_win32_sys_ddll.c @@ -59,7 +59,7 @@ void erl_sys_ddll_init(void) { * Open a shared object * Expecting 'full_name' as an UTF-8 string. */ -int erts_sys_ddll_open2(const char *full_name, void **handle, ErtsSysDdllError* err) +int erts_sys_ddll_open(const char *full_name, void **handle, ErtsSysDdllError* err) { HINSTANCE hinstance; int len; -- cgit v1.2.3 From 16c4d1edacb1feeb394ebb6c49e2c04fcbd4d1b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Sun, 15 Dec 2013 20:04:57 +0100 Subject: Cross-compilation fix for TileraMDE-3.0.1.125620 -OPT:Olimit=0 -WOPT:lpre=off:spre=off:epre=off tile-gcc 4.4.3 does not accept these options: cc1: error: unrecognized command line option "-WOPT:lpre=off:spre=off:epre=off" --- erts/emulator/Makefile.in | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index f442540f49..ef1e578e34 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -643,7 +643,6 @@ endif ifneq ($(filter tile-%,$(TARGET)),) $(OBJDIR)/beam_emu.o: beam/beam_emu.c $(V_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) \ - -OPT:Olimit=0 -WOPT:lpre=off:spre=off:epre=off \ $(INCLUDES) -c $< -o $@ else # Usually the same as the default rule, but certain platforms (e.g. win32) mix -- cgit v1.2.3 From f5de34c3f54f1798c0932d9cb41929134649aa08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Sun, 15 Dec 2013 20:08:50 +0100 Subject: Undefined MALLOC_USE_HASH macro on TileMDE3 MALLOC_USE_HASH is not defined in TileraMDE-3, but exists in TileraMDE-2. So use macro conditionally. --- erts/emulator/sys/unix/sys.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 61f9f6a59a..a71ae4e864 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -409,8 +409,10 @@ void sys_tty_reset(int exit_code) #ifdef __tile__ /* Direct malloc to spread memory around the caches of multiple tiles. */ #include +#if defined(MALLOC_USE_HASH) MALLOC_USE_HASH(1); #endif +#endif #ifdef USE_THREADS -- cgit v1.2.3 From 3c5baf40ff8967d0b3190c5fabe15fb1487f6c52 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 16 Dec 2013 11:55:49 +0100 Subject: erts: Add 'extra' argument to erts_convert_filename_to_encoding --- erts/emulator/beam/erl_bif_port.c | 2 +- erts/emulator/beam/erl_nif.c | 3 ++- erts/emulator/beam/erl_unicode.c | 17 ++++++++++------- erts/emulator/beam/global.h | 3 ++- 4 files changed, 15 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 864349491a..f298422267 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -808,7 +808,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) if (encoding == ERL_FILENAME_WIN_WCHAR) { encoding = ERL_FILENAME_UTF8; } - if ((name_buf = erts_convert_filename_to_encoding(name, NULL, 0, ERTS_ALC_T_TMP,0,1, encoding, NULL)) + if ((name_buf = erts_convert_filename_to_encoding(name, NULL, 0, ERTS_ALC_T_TMP,0,1, encoding, NULL, 0)) == NULL) { goto badarg; } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index de626ca4e1..dc285b3cf7 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1587,7 +1587,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) encoding = ERL_FILENAME_UTF8; } lib_name = erts_convert_filename_to_encoding(BIF_ARG_1, NULL, 0, - ERTS_ALC_T_TMP, 1, 0, encoding, NULL); + ERTS_ALC_T_TMP, 1, 0, encoding, + NULL, 0); if (!lib_name) { BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index 7e3c6681d9..3a968594f3 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -1990,12 +1990,14 @@ char *erts_convert_filename_to_native(Eterm name, char *statbuf, size_t statbuf_ { int encoding = erts_get_native_filename_encoding(); return erts_convert_filename_to_encoding(name, statbuf, statbuf_size, alloc_type, - allow_empty, allow_atom, encoding, used); + allow_empty, allow_atom, encoding, + used, 0); } char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbuf_size, ErtsAlcType_t alloc_type, int allow_empty, - int allow_atom, int encoding, Sint *used) + int allow_atom, int encoding, Sint *used, + Uint extra) { char* name_buf = NULL; @@ -2008,13 +2010,14 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu } if (encoding == ERL_FILENAME_WIN_WCHAR) { need += 2; + extra *= 2; } else { ++need; } if (used) *used = (Sint) need; - if (need > statbuf_size) { - name_buf = (char *) erts_alloc(alloc_type, need); + if (need+extra > statbuf_size) { + name_buf = (char *) erts_alloc(alloc_type, need+extra); } else { name_buf = statbuf; } @@ -2035,8 +2038,8 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu /*Add 0 termination only*/ if (used) *used = (Sint) size+1; - if (size+1 > statbuf_size) { - name_buf = (char *) erts_alloc(alloc_type, size+1); + if (size+1+extra > statbuf_size) { + name_buf = (char *) erts_alloc(alloc_type, size+1+extra); } else { name_buf = statbuf; } @@ -2045,7 +2048,7 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu } else { name_buf = erts_convert_filename_to_wchar(bytes, size, statbuf, statbuf_size, - alloc_type, used, 0); + alloc_type, used, extra); } erts_free_aligned_binary_bytes(temp_alloc); } else { diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 94bc1b172a..6e5d352e5b 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -921,7 +921,8 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, ErtsAlcType_t alloc_type, int allow_empty, int allow_atom, int encoding, - Sint *used /* out */); + Sint *used /* out */, + Uint extra); char* erts_convert_filename_to_wchar(byte* bytes, Uint size, char *statbuf, size_t statbuf_size, ErtsAlcType_t alloc_type, Sint* used, -- cgit v1.2.3 From 21408235bd6915001cbc5051a1b7c38a3fef9d05 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 16 Dec 2013 19:39:17 +0100 Subject: erts: Support loading of drivers with unicode paths --- erts/emulator/beam/erl_bif_ddll.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index caee07e491..1728b200f7 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -182,7 +182,7 @@ BIF_RETTYPE erl_ddll_try_load_3(BIF_ALIST_3) Eterm name_term = BIF_ARG_2; Eterm options = BIF_ARG_3; char *path = NULL; - ErlDrvSizeT path_len; + Sint path_len; char *name = NULL; DE_Handle *dh; erts_driver_t *drv; @@ -198,6 +198,7 @@ BIF_RETTYPE erl_ddll_try_load_3(BIF_ALIST_3) int kill_ports = 0; int do_build_load_error = 0; int build_this_load_error = 0; + int encoding; for(l = options; is_list(l); l = CDR(list_val(l))) { Eterm opt = CAR(list_val(l)); @@ -257,18 +258,23 @@ BIF_RETTYPE erl_ddll_try_load_3(BIF_ALIST_3) goto error; } - if (erts_iolist_size(path_term, &path_len)) { - goto error; + encoding = erts_get_native_filename_encoding(); + if (encoding == ERL_FILENAME_WIN_WCHAR) { + /* Do not convert the lib name to utf-16le yet, do that in win32 specific code */ + /* since lib_name is used in error messages */ + encoding = ERL_FILENAME_UTF8; } - path = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, path_len + 1 /* might need path separator */ + sys_strlen(name) + 1); - if (erts_iolist_to_buf(path_term, path, path_len) != 0) { + path = erts_convert_filename_to_encoding(path_term, NULL, 0, + ERTS_ALC_T_DDLL_TMP_BUF, 1, 0, + encoding, &path_len, + sys_strlen(name) + 2); /* might need path separator */ + if (!path) { goto error; } - while (path_len > 0 && (path[path_len-1] == '\\' || path[path_len-1] == '/')) { - --path_len; - } + ASSERT(path_len > 0 && path[path_len-1] == 0); + while (--path_len > 0 && (path[path_len-1] == '\\' || path[path_len-1] == '/')) + ; path[path_len++] = '/'; - /*path[path_len] = '\0';*/ sys_strcpy(path+path_len,name); #if DDLL_SMP -- cgit v1.2.3 From 18315c1675d965e5d0210a560e4742c483fcfd7f Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Tue, 3 Dec 2013 18:43:30 +0100 Subject: Officially support building assembler files erlc is wired to treat *.S files as assembler and build them as compile:file(File, [from_asm]), but this is not documented. There's also a documented compile:file/2 option called 'asm' (mapping to 'from_asm'), but the wording discourages its use. All of this has been in place and in use for a long time. Therefore, it should be supported officially. To fix that, make the following changes: * document erlc handling of *.core files * un-document 'asm' and document 'from_asm' instead * deprecate 'asm' While at it, fix a minor typo in the test suite. --- erts/doc/src/erlc.xml | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml index 10cab344b0..2df026aef5 100644 --- a/erts/doc/src/erlc.xml +++ b/erts/doc/src/erlc.xml @@ -234,6 +234,11 @@ erlc +export_all file.erl from the shell.

Supported options: -I, -o, -D, -v, -W, -b.

+ .S + +

Erlang assembler source code. It generates a file.

+

Supported options: same as for .erl.

+
.yrl

Yecc source code. It generates an file.

-- cgit v1.2.3 From 455c8238535d2754234cd68bbf7caba9960607d6 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Tue, 3 Dec 2013 19:16:16 +0100 Subject: Officially support building core files erlc is wired to treat *.core files as core and build them as compile:file(File, [from_core]), but this is not documented. There's also an udocumented compile:file/2 option called 'from_core'. This has been in place and in use for a long time. Therefore, it should be supported officially. To fix that, make the following changes: * document erlc handling of *.core files * document 'from_core' --- erts/doc/src/erlc.xml | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml index 2df026aef5..c3fc3b1686 100644 --- a/erts/doc/src/erlc.xml +++ b/erts/doc/src/erlc.xml @@ -239,6 +239,11 @@ erlc +export_all file.erl

Erlang assembler source code. It generates a file.

Supported options: same as for .erl.

+ .core + +

Erlang core source code. It generates a file.

+

Supported options: same as for .erl.

+
.yrl

Yecc source code. It generates an file.

-- cgit v1.2.3 From d61aff41862a017b948aeac58a8340ff41e69135 Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Sun, 15 Dec 2013 19:32:33 +0000 Subject: Add a chmod call in the CLI example add missing './' --- erts/doc/src/escript.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml index 180447cac4..d2b09d4515 100644 --- a/erts/doc/src/escript.xml +++ b/erts/doc/src/escript.xml @@ -44,6 +44,7 @@

escript runs a script written in Erlang.

Here follows an example.

+$ chmod u+x factorial
 $ cat factorial
 #!/usr/bin/env escript
 %% -*- erlang -*-
@@ -66,12 +67,13 @@ usage() ->
 
 fac(0) -> 1;
 fac(N) -> N * fac(N-1).
-$ factorial 5
+$ ./factorial 5
 factorial 5 = 120
-$ factorial
+$ ./factorial
 usage: factorial integer
-$ factorial five
-usage: factorial integer        
+$ ./factorial five +usage: factorial integer +

The header of the Erlang script in the example differs from a normal Erlang module. The first line is intended to be the interpreter line, which invokes escript. However if you -- cgit v1.2.3 From 71e6fe5bcbd7b2b98dfa159db34ee1fe14823a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 7 Jan 2014 18:40:43 +0100 Subject: erts: Fix bs_get_integer instruction The instruction bs_get_integer could unnecessarily trigger a garbage collection in failure cases which is unwanted or outright dangerous. Ex: <> = <<"some binary">> Previously, if Sz induced X to a bignum it would reserved memory size this on the heap via a garbage collection before checking if the size could actually match. It will now check the binary size before triggering a collection. --- erts/emulator/beam/beam_emu.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 78ab6fa30f..592cfe273f 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4326,7 +4326,19 @@ void process_main(void) flags = Arg(2); BsGetFieldSize(tmp_arg2, (flags >> 3), ClauseFail(), size); if (size >= SMALL_BITS) { - Uint wordsneeded = 1+WSIZE(NBYTES((Uint) size)); + Uint wordsneeded; + /* check bits size before potential gc. + * We do not want a gc and then realize we don't need + * the allocated space (i.e. if the op fails) + * + * remember to reacquire the matchbuffer after gc. + */ + + mb = ms_matchbuffer(tmp_arg1); + if (mb->size - mb->offset < size) { + ClauseFail(); + } + wordsneeded = 1+WSIZE(NBYTES((Uint) size)); TestHeapPreserve(wordsneeded, Arg(1), tmp_arg1); } mb = ms_matchbuffer(tmp_arg1); -- cgit v1.2.3 From c1c6fbcb1ef741801edeef3b17bb38e52fcaea2e Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 5 Mar 2013 22:02:17 +0100 Subject: Add misc internal documentation --- erts/emulator/internal_doc/CarrierMigration.md | 201 ++++++++++++ erts/emulator/internal_doc/DelayedDealloc.md | 175 ++++++++++ erts/emulator/internal_doc/PTables.md | 356 +++++++++++++++++++++ erts/emulator/internal_doc/PortSignals.md | 267 ++++++++++++++++ .../internal_doc/ProcessManagementOptimizations.md | 172 ++++++++++ erts/emulator/internal_doc/ThreadProgress.md | 308 ++++++++++++++++++ 6 files changed, 1479 insertions(+) create mode 100644 erts/emulator/internal_doc/CarrierMigration.md create mode 100644 erts/emulator/internal_doc/DelayedDealloc.md create mode 100644 erts/emulator/internal_doc/PTables.md create mode 100644 erts/emulator/internal_doc/PortSignals.md create mode 100644 erts/emulator/internal_doc/ProcessManagementOptimizations.md create mode 100644 erts/emulator/internal_doc/ThreadProgress.md (limited to 'erts') diff --git a/erts/emulator/internal_doc/CarrierMigration.md b/erts/emulator/internal_doc/CarrierMigration.md new file mode 100644 index 0000000000..b93c11c6ec --- /dev/null +++ b/erts/emulator/internal_doc/CarrierMigration.md @@ -0,0 +1,201 @@ +Carrier Migration +================= + +The ERTS memory allocators manage memory blocks in two types of raw +memory chunks. We call these chunks of raw memory +*carriers*. Singleblock carriers which only contain one large block, +and multiblock carriers which contain multiple blocks. A carrier is +typically created using `mmap()` on unix systems. However, how a +carrier is created is of minor importance. An allocator instance +typically manages a mixture of single- and multiblock carriers. + +Problem +------- + +When a carrier is empty, i.e. contains only one large free block, it +is deallocated. Since multiblock carriers can contain both allocated +blocks and free blocks at the same time, an allocator instance might +be stuck with a large amount of poorly utilized carriers if the memory +load decrease. After a peak in memory usage it is expected that not +all memory can be returned since the blocks still allocated is likely +to be dispersed over multiple carriers. Such poorly utilized carriers +can usually be reused if the memory load increase again. However, +since each scheduler thread manages its own set of allocator +instances, and memory load is not necessarily connected to CPU load we +might get into a situation where there are lots of poorly utilized +multiblock carriers on some allocator instances while we need to +allocate new multiblock carriers on other allocator instances. In +scenarios like this, the demand for multiblock carriers in the system +might increase at the same time as the actual memory demand in the +system has decreased which is both unwanted and quite unexpected for +the end user. + +Solution +-------- + +In order to prevent scenarios like this we've implemented support for +migration of multiblock carriers between allocator instances of the +same type. + +### Management of Free Blocks ### + +In order to be able to remove a carrier from one allocator instance +and add it to another we need to be able to move references to the +free blocks of the carrier between the allocator instances. The +allocator instance specific data structure referring to the free +blocks it manages often refers to the same carrier from multiple +places. For example, when the address order bestfit strategy is used +this data structure is a binary search tree spanning all carriers that +the allocator instance manages. Free blocks in one specific carrier +can be referred to from potentially every other carrier that is +managed, and the amount of such references can be huge. That is, the +work of removing the free blocks of such a carrier from the search +tree will be huge. One way of solving this could be to not migrate +carriers that contain lots of free blocks, but this would prevent us +from migrating carriers that potentially needs to be migrated in order +to solve the problem we set out to solve. + +By using one data structure of free blocks in each carrier and an +allocator instance wide data structure of carriers managed by the +allocator instance, the work needed in order to remove and add +carriers can be kept to a minimum. When migration of carriers is +enabled on a specific allocator type, we require that an allocation +strategy with such an implementation is used. Currently we've +implemented this for three different allocation strategies. All of +these strategies use a search tree of carriers sorted so that we can +find the carrier with the lowest address that can satisfy the +request. Internally in carriers we use yet another search tree that +either implement address order first fit, address order best fit, +or best fit. The abbreviations used for these different allocation +strategies are `aoff`, and `aoffcaobf`, `aoffcbf`. + +### Carrier Pool ### + +In order to migrate carriers between allocator instances we move them +through a pool of carriers. In order for a carrier migration to +complete, one scheduler needs to move the carrier into the pool, and +another scheduler needs to take the carrier out of the pool. + +The pool is implemented as a lock free, circular, double linked, +list. The list contains a sentinel which is used as the starting point +when inserting to, or fetching from the pool. Carriers in the pool are +elements in this list. + +The list can be modified by all scheduler threads +simultaneously. During modifications the double linked list is allowed +to get a bit "out of shape". For example, following the `next` pointer +to the next element and then following the `prev` pointer does not +always take you back to were you started. The following is however +always true: + +* Repeatedly following `next` pointers will eventually take you to the + sentinel. +* Repeatedly following `prev` pointers will eventually take you to the + sentinel. +* Following a `next` or a `prev` pointer will take you to either an + element in the pool, or an element that used to be in the pool. + +When inserting a new element we search for a place to insert the +element by only following `next` pointers, and we always begin by +skipping the first element encountered. When trying to fetch an +element we do the same thing, but instead only follow `prev` pointers. + +By going different directions when inserting and fetching, we avoid +contention between threads inserting and threads fetching as much as +possible. By skipping one element when we begin searching, we preserve +the sentinel unmodified as much as possible. This is beneficial since +all search operations need to read the content of the sentinel. If we +were to modify the sentinel, the cache line containing the sentinel +would unnecessarily be bounced between processors. + +The `prev`, and `next` fields in the elements of the list contains the +value of the pointer, a modification marker, and a deleted +marker. Memory operations on these fields are done using atomic memory +operations. When a thread has set the modification marker in a field, +no-one except the thread that set the marker is allowed to modify the +field. If multiple modification markers needs to be set, we always +begin with `next` fields followed by `prev` fields in the order +following the actual pointers. This guarantees that no deadlocks will +occur. + +When a carrier is being removed from a pool, we mark it with a thread +progress value that needs to be reached before we are allowed to +modify the `next`, and `prev` fields. That is, until we reach this +thread progress we are not allowed to insert the carrier into the pool +again, and we are not allowed to deallocate the carrier. This ensures +that threads inspecting the pool always will be able to traverse the +pool and reach valid elements. Once we have reached the thread +progress value that the carrier was tagged with, we know that no +threads may have references to it via the pool. + +### Migration ### + +There exist one pool for each allocator type enabling migration of +carriers between scheduler specific allocator instances of the same +allocator type. + +Each allocator instance keeps track of the current utilization of its +multiblock carriers. When the utilization falls below the "abandon +carrier utilization limit" it starts to inspect the utilization of the +current carrier when deallocations are made. If also the utilization +of the carrier falls below the "abandon carrier utilization limit" it +unlinks the carrier from its data structure of available free blocks +and inserts the carrier into the pool. + +Since the carrier has been unlinked from the data structure of +available free blocks, no more allocations will be made in the +carrier. The allocator instance putting the carrier into the pool, +however, still has the responsibility of performing deallocations in +it while it remains in the pool. + +Each carrier has a flag field containing information about allocator +instance owning the carrier, a flag indicating if the carrier is in +the pool or not, and a flag indicating if it is busy or not. When the +carrier is in the pool, the owning allocator instance needs to mark it +as busy while operating on it. If another thread inspects it in order +to try to fetch it from the pool, it will abort the fetch if it is +busy. When fetching the carrier from the pool, ownership will changed +and further deallocations in the carrier will be redirected to the new +owner using the delayed dealloc functionality. + +If a carrier in the pool becomes empty, it will be withdrawn from the +pool. All carriers that become empty are also always passed to its +originating allocator instance for deallocation using the delayed +dealloc functionality. Since carriers this way always will be +deallocated by the allocator instance that allocated the carrier the +underlying functionality of allocating and deallocating carriers can +remain simple and doesn't have to bother about multiple threads. In a +NUMA system we will also not mix carriers originating from multiple +NUMA nodes. + +When an allocator instance needs more carrier space, it always begins +by inspecting its own carriers that are waiting for thread progress +before they can be deallocated. If no such carrier could be found, it +then inspects the pool. If no carrier could be fetched from the pool, +it will allocate a new carrier. Regardless of where the allocator +instance gets the carrier from it the just links in the carrier into +its data structure of free blocks. + +### Result ### + +The use of this strategy of abandoning carriers with poor utilization +and reusing these in allocator instances with an increased carrier +demand is extremely effective and completely eliminates the problems +that otherwise sometimes occurred when CPU load dropped while memory +load did not. + +When using the `aoffcaobf` or `aoff` strategies compared to `gf` or +`bf`, we loose some performance since we get more modifications in the +data structure of free blocks. This performance penalty is however +reduced using the `aoffcbf` strategy. A tradeoff between memory +consumption and performance is however inevitable, and it is up to +the user to decide what is most important. + +Further work +------------ + +It would be quite easy to extend this to allow migration of multiblock +carriers between all allocator types. More or less the only obstacle +is maintenance of the statistics information. + + diff --git a/erts/emulator/internal_doc/DelayedDealloc.md b/erts/emulator/internal_doc/DelayedDealloc.md new file mode 100644 index 0000000000..b7d87b839f --- /dev/null +++ b/erts/emulator/internal_doc/DelayedDealloc.md @@ -0,0 +1,175 @@ +Delayed Dealloc +=============== + +Problem +------- + +An easy way to handle memory allocation in a multi-threaded +environment is to protect the memory allocator with a global lock +which threads performing memory allocations or deallocations have to +have locked during the whole operation. This solution of course scales +very poorly, due to heavy lock contention. An improved solution of +this scheme is to use multiple thread specific instances of such an +allocator. That is, each thread allocates in its own allocator +instance which is protected by a lock. In the general case references +to memory need to be passed between threads. In the case where a +thread that needs to deallocate memory that originates from another +threads allocator instance a lock conflict is possible. In a system as +the Erlang VM where memory allocation/deallocation is frequent and +references to memory also are passed around between threads this +solution will also scale poorly due to lock contention. + +Functionality Used to Adress This problem +----------------------------------------- + +In order to reduce contention due to locking of allocator instances we +introduced completely lock free instances tied to each scheduler +thread, and an extra locked instance for other threads. The scheduler +threads in the system is expected to do the major part of the +work. Other threads may still be needed but should not perform any +major and/or time critical work. The limited amount of contention that +appears on the locked allocator instance can more or less be +disregarded. + +Since we still need to be able to pass references to memory between +scheduler threads we need some way to manage this. An allocator +instance belonging to one scheduler thread is only allowed to be +manipulated by that scheduler thread. When other threads need to +deallocate memory originating from a foreign allocator instance, they +only pass the memory block to a "message box" containing deallocation +jobs attached to the originating allocator instance. When a scheduler +thread detects such deallocation job it performs the actual +deallocation. + +The "message box" is implemented using a lock free single linked list +through the memory blocks to deallocate. The order of the elements in +this list is not important. Insertion of new free blocks will be made +somewhere near the end of this list. Requirering that the new blocks +need to be inserted at the end would cause unnecessary contention when +large amount of memory blocks are inserted simultaneous by multiple +threads. + +The data structure refering to this single linked list cover two cache +lines. One cache line containing information about the head of the +list, and one cache line containing information about the tail of the +list. This in order to reduce cache line ping ponging of this data +structure. The head of the list will only be manipulated by the thread +owning the allocator instance, and the tail will be manipulated by +other threads inserting deallocation jobs. + +### Tail ### + +In the tail part of the data structure we find a pointer to the last +element of the list, or at least something that is near the end of the +list. In the uncontended case it will point to the end of the list, +but when simultaneous insert operations are performed it will point to +something near the end of the list. + +When insterting an element one will try to write a pointer to the new +element in the next pointer of the element pointed to by the last +pointer. This is done using an atomic compare and swap that expects +the next pointer to be `NULL`. If this succeds the thread performing +this operation moves the last pointer to point to the newly inserted +element. + +If the atomic compare and swap described above failed, the last +pointer didn't point to the last element. In this case we need to +insert the new element somewhere inbetween the element that the last +pointer pointed to and the actual last element. If we do it this way +the last pointer will eventually end up at the last element when +threads stop adding new elements. When trying to insert somewhere near +the end and failing to do so, the inserting thread sometimes moves to +the next element and somtimes tries with the same element again. This +in order to spread the inserted elements during heavy contention. That +is, we try to spread the modifications of memory to different +locations instead of letting all threads continue to try to modify the +same location in memory. + +### Head ### + +The head contains pointers to begining of the list (`head.first`), and +to the first block which other threads may refer to +(`head.unref_end`). Blocks between these pointers are only refered to +by the head part of the data structure which is only used by the +thread owning the allocator instance. When these two pointers are not +equal the thread owning the allocator instance deallocate block after +block until `head.first` reach `head.unref_end`. + +We of course periodically need to move the `head.unref_end` closer to +the end in order to be able to continue deallocating memory +blocks. Since all threads inserting new elements in the linked list +will enter the list using the last pointer we can use this +knowledge. If we call `erts_thr_progress_later()` and wait until we +have reached that thread progress we know that no managed threads can +refer the elements up to the element pointed to by the last pointer at +the time when we called `erts_thr_progress_later()`. This since, all +managed threads must have left the code implementing this at least +once, and they always enters into the list via the last pointer. The +`tail.next` field contains information about next `head.unref_end` +pointer and thread progress that needs to be reached before we can +move `head.unref_end`. + +Unfortunately not only threads managed by the thread progress +functionality may insert memory blocks. Other threads also needs to be +taken care of. Other threads will not be as frequent users of this +functionality as managed threads, so using a less efficient scheme for +them is not that big of a problem. In order to handle unmanaged +threads we use two reference counters. When an unmanaged thread enters +this implementation it increments the reference counter currently +used, and when it leaves this implementation it decrements the same +reference counter. When the consumer thread calls +`erts_thr_progress_later()` in order to determine when it is safe to +move `head.unref_end`, it also swaps reference counters for unmanaged +threads. The previous current represents outstanding references from +the time up to this point. The new current represents future reference +following this point. When the consumer thread detects that we have +both reached the desired thread progress and when the previous current +reference counter reach zero it is safe to move the `head.unref_end`. + +The reason for using two reference counters is that we need to know +that the reference counter eventually will reach zero. If we only used +one reference counter it would potentially be held above zero for ever +by different unmanaged threads. + +### Empty List ### + +If no new memory blocks are inserted into the list, it should +eventually be emptied. All pointers to the list however expect to +always point to something. This is solved by inserting an empty +"marker" element, which only has to purpose of being there in the +absense of other elements. That is when the list is empty it only +contains this "marker" element. + +### Contention ### + +When elements are continously inserted by threads not owning the +allocator instance, the thread owning the allocator instance will be +able to work more or less undisturbed by other threads at the head end +of the list. At the tail end large amounts of simultaneous inserts may +cause contention, but we reduce such contention by spreading inserts +of new elements near the end instead of requiring all new elements to +be inserted at the end. + +### Schedulers and The Locked Allocator Instance ### + +Also the locked allocator instance for use by non-scheduler threads +have a message box for deallocation jobs just as all the other +allocator instances. The reason for this is that other threads may +allocate memory pass it to a scheduler that then needs to deallocate +it. We do not want the scheduler to have to wait for the lock on this +locked instance. Since also locked instances has message boxes for +deallocation jobs, the scheduler can just insert the job and avoid the +locking. + + +### A Benchmark Result ### + +When running the ehb benchmark, large amount of messages are passed +around between schedulers. All message passing will in some way or the +other cause memory allocation and deallocation. Since messages are +passed between different schedulers we will get contention on the +allocator instances where messages were allocated. By the introduction +of the delayed dealloc feature, we got a speedup of between 25-45%, +depending on configuration of the benchmark, when running on a +relatively new machine with an Intel i7 quad core processor with +hyper-threading using 8 schedulers. \ No newline at end of file diff --git a/erts/emulator/internal_doc/PTables.md b/erts/emulator/internal_doc/PTables.md new file mode 100644 index 0000000000..6fe0e7665d --- /dev/null +++ b/erts/emulator/internal_doc/PTables.md @@ -0,0 +1,356 @@ +Process and Port Tables +======================= + +Problems +-------- + +The process table is a mapping from process identifiers to process +structure pointers. The process structure contains miscellaneous +information about a process, as for example pointers to its heap, +message queue, etc. When the runtime system needs to operate on a +process, it looks up the process structure in the process table using +the process identifier. An example of this is when passing a message +to a process. + +The process table has for a very long time just been an array of +pointers to process structures. Since process identifiers internally +in the runtime system are 28-bit integers it is quite easy to map a +process identifier to index into the array. The 28-bits were divided +into two sets. The least significant set of bits was used as index +into the array. The most significant set of bits was only used to be +able to distinguish between a number of identifiers with which map to +the same index in the array. As long as process table sizes of a power +of two was used we had 2^28 unique process identifiers. + +When the first SMP support was implemented, the table still was kept +more or less the same way, but protected by two types of locks. One +lock that protected the whole table against modifications and an array +of locks protecting different parts of the table. The exact locking +strategy previously used isn't interesting. What is interesting is +that it suffered from heavy lock contention especially when lots of +modifications was being made, but also when only performing lookups. + +In order to be able to detect when it is safe to deallocate a +previously used process structure, reference counting of the structure +was used. Also this was problematic, since simultaneous lookups needed +to modify the reference counter which also caused contention on the +cache line where the reference counter was located. This since all +modifications needs to be communicated between all involved +processors. + +The port table is very similar to the process table. The major +difference, at least in concept, is that it is a mapping from port +identifiers to port structures. It had a similar implementation, but +with some differences. Instead of being an array of pointers it was an +array of structures, and instead of being protected by two types of +locks it was only protected by one global lock. This table also +suffered from lock contention in various situations. + +Solution +-------- + +The process table was the major problem to address since processes are +much more frequently used than ports. The first implementation only +implemented this for processes, but since the port table is very +similar and very similar problems occur on the port table, the process +table implementation was later generalized so that it could also be +used implementing the port table. For simplicity I will only talk +about the process table in the following text, but the same will apply +to the port table unless otherwise stated. + +If we disregard the locking issues, the original solution is very +appealing. The mapping from process identifier to index into the array +is very fast, and this property is something we would like to +keep. The vast majority of operations on these tables are lookups so +optimizing for lookups is what we want to do. + +### Lookup ### + +Using a set of bits in the process identifier as index into an array +seems hard to beat. By replacing the array of pointers with an array +of our pointer sized atomic data type, a lookup will consist of the +following: + +1. Mapping the 28-bit integer to an index into the array. + + More about this mapping later. + +2. Read the pointer using an atomic memory operation at determined + index in array. + + On all platforms that we provide atomic memory operations, this is + just a `volatile` read, preventing the compiler to use values in + registers, forcing the a read from memory. + +3. Depending on use, issue appropriate memory barrier. + + A common barrier used is a barrier with acquire semantics. On + x86/x86_64 this maps to a compiler barrier preventing the compiler + to reorder instructions, but on other hardware often some kind of + light weight hardware memory barrier is also needed. + + When comparing with a locked approach, at least one heavy weight + memory barrier will be issued when locking the lock on most, if + not all, hardware architectures (including x86/x86_64), and often + some kind of light weight memory barrier will be issued when + unlocking the lock. + +When looking at this very simple solution with very little overhead +you might wonder why we didn't implement it this way from the +beginning. It all boils down to the read operation of the pointer. We +need some way to know that it is safe to access the memory pointed +to. One way of doing this is to place a reference counter in the +process structure. Increment of the reference counter at lookup needs +to be done atomically with the lookup. A lock can typically provide +this service for us, which was the approach we previously +used. Another approach could be to co-locate the reference counter +with the pointer in the table. The major problem with this approach is +the modifications of the reference counter. This since these +modification would have to be communicated between all involved +processor cause contention on the cache line containing the reference +counter. The new lookup approach above is possible since we can use +the "thread progress" functionality in order to determine when it is +safe to deallocate the process structure. We'll get back to this when +describing deletion in the table. + +Using this new lookup approach we wont modify any memory at all which +is important. A lookup conceptually only read memory, now this is true +in the implementation also which is important from a scalability +perspective. The previous implementation modified the cache line +containing the reference counter two times, and the cache line +containing the corresponding lock two times at each lookup. + +### Modifications of the Table ### + +A lightweight lookup in the table was the most important feature, but +we also wanted to improve modifications of the table. The process +table is modified when a new process is spawned, i.e. a new pointer is +inserted into the table, and when a process terminates, i.e. a pointer +is deleted in the table. + +Assuming that we spawn fewer processes than the maximum amount of +unique process identifiers in the system, one has always been able to +determine the order of process creation just by comparing process +identifiers. If PidX is larger than PidY, then PidX was created after +PidY assuming both identifiers originates from the same node. However, +since we have a quite limited amount of unique identifiers today +(2^28), this property cannot be relied upon if we create large amount +of processes. But never the less, this is a property the system always +have had. + +If we would have had a huge amount of unique identifiers available, it +would have tempting to drop or modify this ordering property as +described above. The ordering property could for example be based on +the scheduler performing the spawn operation. It would have been +possible to reserve large ranges of identifiers exclusive for each +scheduler thread which could be used minimizing the need for +communication when allocating identifiers. The amount of identifiers +we got to work with today is, however, not even close to be enough for +such an approach. + +Since we have a limited amount of unique identifiers, we need to be +careful not to waste them. If previously used identifiers are reused +too quick, identifiers originating from terminated processes will +refer to newly created processes, and mixups will occur. The +previously used approach was quite good at not wasting +identifiers. Using a modified version of the same approach also lets +us keep the ordering property that we have always had. + +#### Insert #### + +The original approach is more or less to search for next free index or +slot in the array. The search starts from the last slot allocated. If +we reach the end of the array we increase a "wrapped counter" and then +continue the search. The process identifier is constructed by writing +the index to the least significant set of bits, and the "wrapped +counter" to the most significant set of bits. The amount of bits in +each set of bits is decided at boot time, so that maximum index will +just fit into the least significant set of bits. + +In the modified lock free version of this approach we more or less do +it the same way, but with some important modifications trying to avoid +unnecessary contention when multiple schedulers create processes +simultaneously. Since multiple threads might be trying to search for +the next free slot at the same time from the same starting point we +want subsequent slots to be located in different cache lines. Multiple +schedulers simultaneously writing new pointers into the table are +therefore very likely to write into adjacent slots. If adjacent slots +are located in the same cache line all modification of this cache line +needs to be communicated between all involved processors which will be +very expensive and scale very poor. By locating adjacent slots in +different cache lines only true conflicts will trigger communication +between involved processors, i.e., avoiding false sharing. + +A cache line is larger than a pointer, typically 8 or 16 times larger, +so using one cache line for each slot only containing one pointer +would be a waste of space. Each cache line will be able to hold a +fixed amount of slots. The first slot of the table will be the first +slot of the first cache line, the second slot of the table will be the +first slot of the second cache line until we reach the end of the +array. The next slot after that will be the second slot of the first +cache line, etc, moving forward one cache line internal slot each time +we wrap. This way we will be able to fit the same amount of pointers +into an array of the same size while always keeping adjacent slots in +different cache lines. + +The mapping from identifier to slot or index into the array gets a bit +more complicated than before. Instead of a `shift` and a bitwise +`and`, we get two `shift`s, two bitwise `and`s, and an `add` (see +implementation of `erts_ptab_data2pix()` in `erl_ptab.h`). However, by +storing this information optimized for lookup we only need a `shift` +and a bitwise `and` on 32-bit platforms. On 64-bit platforms we got +enough room for the 28-bit identifier in the least significant +halfword, and the index in the most significant halfword, in other +words, we just need to read the most significant halfword to get the +index. That is, this operation is as fast, or faster than before. The +downside is that on 32-bit platforms we need to convert this +information into the 28-bit identifier number when printing, or when +ordering identifiers from the same node. These operations are, +however, extremely infrequent compared to lookups. + +When we insert a new element in the table we do the following: + +1. We begin by reserving space in the table by atomically + incrementing a counter of processes in the table. If our increment + brings the counter above the maximum size of the table, the + operation fail and a `system_limit` exception is raised. + +2. The table contains a 64-bit atomic variable of the last identifier + used. Only the least significant bits will be used when actually + creating the identifier. This identifier is where the search + begin. + +3. We increment last identifier value used. In order determine the + slot that corresponds to this identifier we call + `erts_ptab_data2pix()` that maps identifier to slot. We read the + content of the slot. If the slot is free we try to write a + reservation marker using an atomic compare and swap. If this fails + we repeat this step until it succeeds. + +4. Change the table variable of last identifier used. Since multiple + writes might occur at the same time this value may already have + been changed by to an identifier larger that the one we got. In + this case we can continue; otherwise, we need to change it to the + identifier we got. + +5. We now do some initializations of the process structure that + cannot be done before we know the process identifier, and have to + be done before we publish the structure in the table. This, for + example, includes storing the identifier in the process structure. + +6. Now we can publish the structure in the table by writing the the + pointer to the process structure in the slot previously reserved + in 3. + +Using this approach we keep the properties like identifier ordering, +and identifier reuse while improving performance and scalability. It +has one flaw, though. There is no guarantee that the operation will +terminate. This can quite easily be fixed though, and will be fixed in +the next release. We will get back to this below. + +#### Delete #### + +When a process terminates, we mark the process as terminated in the +process structure, the counter of number of processes in the table is +decreased, and the reference to the process structure is removed by +writing a `NULL` pointer into the corresponding slot. The scheduler +thread performing this then schedule a thread progress later job which +will do the final cleanup and deallocate the process structure. The +thread progress functionality will make sure that this job will not +execute until it is certain that all managed threads have dropped all +references to the process structure. + +### BIF Iterating Over the Table ### + +The `erlang:processes/1` and `erlang:port/1` BIFs iterate over the +tables and return corresponding identifiers. These BIF should return a +consistent snapshot of the table content during some time when the BIF +is executing. In order to implement this we use locking in a strange +way. We use an "inverted rwlock". + +When performing lookups in the table we do not need to bother about +the locking at all, but when modifying the table we read lock the +rwlock protecting the table which allows for multiple writers during +normal operation. When the BIF that iterates over the table need +access to the table it write locks the rwlock and reads content of the +table. The BIF do not read the whole table in one go but instead read +small chunks at time only write locking while reading. The actual +implementation of the BIFs is out of the scope of this document. + +An out of the box rwlock will typically suffer from contention on the +single cache line containing the state of the rwlock even in the case +we are only read locking. Instead of using such an rwlock, we have our +own implementation of reader optimized rwlocks which keeps track of +reader threads in separate thread specific cache lines. This in order +to avoid contention on a singe cache line. As long as we only do read +lock operations, threads only need to read a global cache line and +modify its own cache line, and by this minimize communication between +involved processors. The iterating BIFs are normally very infrequently +used, so in the normal case we will only do read lock operations on +the table global rwlock. + +### Future Improvements ### + +The first improvement is to fix the guarantee so that insert +operations will be guaranteed to terminate. When the operation starts +we verify that there actually exist a free slot that we can use. The +problem is that we might not find it since it may move when multiple +threads modify the table at the same time as we are trying to find the +slot. The easy fix is to abort the operation if an empty slot could +not be found in a finite number operation, and then restart the +operation under a write lock. This will be implemented in next +release, but furter work should be made trying to find a better +solution. + +This and also previous implementation do not work well when the table +is nearly full. We will both get long search times for free slots, and +we will reuse identifiers more frequently since we more frequently +wrap during the search. These tables works best when the table is much +larger than the amount of simultaneous existing processes. One easy +improvement is to always have room for more processes than we allow in +the table. This will also be implemented in the next release, but this +should probably also be worked more on trying to find an even better +solution. + +It would also be nice to get rid of the rwlock all together. The use +of a reader optimized rwlock makes sure we do not any contention on +the lock, but unnecessary memory barriers will be issued due to the +lock. The main issue here is to modify iterating BIFs so that they do +not require exclusive access to the table while reading a sequence of +slots. In principle this should be rather easy, the code can handle +sequences of variable sizes, so shrinking the sequence size of slots +to one would solv the problem. This will, however, need some tweeks +and modifications of not trival code, but is something that should be +looked at in the future. + +By increasing the size of identifiers, at least on 64-bit machines +(which isn't as easy as it first might seem) we get further room for +improvement. Besides the obvious improvement of not reusing +identifiers as fast as we currently do, it makes it possible to +further avoid contention when inserting elements in the table. At +least if we drop this ordering property, which isn't that useful +anyway. + +### Some Benchmark Results ### + +In order to test modifications of the process table we ran a couple of +benchmarks where lots of processes are spawned and terminated +simultaneously, and got a speedup of between 150-200%. Running a +similar benchmark but with ports we got a speedup of about 130%. + +The BIF `erlang:is_process_alive/1` is the closest you can get to a +process table lookup only. The BIF looks up the process corresponding +to the process identifier passed as argument, and then checks if it is +alive. By running multiple processes looping over this BIF checking +the same process, we get a speedup between 20000-23000%. Conceptually +this operation only involve read operations. In the implementation +used in R16B also only read operation are performed, while the +previous implementation need to lock structures in order to read the +data, suffering from both lock contention and contention due to +modifications of cache lines used by lock internal data structures and +the reference counter on the process being looked up. + +The benchmarks were run on a relatively new machine with an Intel i7 +quad core processor with hyper-threading using 8 schedulers. On a +machine with more communication overhead and/or larger amount of +logical processors the speedups are expected to be even larger. diff --git a/erts/emulator/internal_doc/PortSignals.md b/erts/emulator/internal_doc/PortSignals.md new file mode 100644 index 0000000000..b1afb7c5cb --- /dev/null +++ b/erts/emulator/internal_doc/PortSignals.md @@ -0,0 +1,267 @@ +Port Signals +============ + +Problems +-------- + +Erlang ports conceptually are very similar to Erlang processes. Erlang +processes execute Erlang code in the virtual machine, while an Erlang +port execute native code typically used for communication with the +outside world. For example, when an Erlang process wants to +communicate using TCP over the network, it communicates via an Erlang +port implementing the TCP socket interface in native code. Both Erlang +Processes and Ports communicate using asynchronous signaling. The +native code executed by an Erlang port is a collection of callback +functions, called a driver. Each callback more or less implements the +code of a signal to, or from the port. + +Even though processes and ports conceptually always have been very +similar, the implementations have been very different. Originally, +more or less all port signals were handled synchronously at the time +they occurred. Very early in the development of the SMP support for +the runtime system we recognized that this was a huge problem for +signals between ports and the outside world. That is, I/O events to +and from the outside world, or I/O signals. This was one of the first +things that had to be rewritten in order to be able to do I/O in +parallel at all. The solution was to implement scheduling of these +signals. I/O signals corresponding to different ports could then be +executed in parallel on different scheduler threads. Signals from +processes to ports was not as big of a problem as the I/O signals, and +the implementation of those was left as they were. + +Each port is protected by its own lock to protect against simultaneous +execution in multiple threads. Previously when a process, executing on +a scheduler thread, sent a port a signal, it locked the port lock and +synchronously executed the code corresponding to the signal. If the +lock was busy, the scheduler thread blocked waiting until it could +lock the lock. If multiple processes executing simultaneously on +different scheduler threads, sent signals to the same port, schedulers +suffered from heavy lock contention. Such contention could also occur +between I/O signals for the port executing on one scheduler thread, +and a signal from a process to the port executing on another scheduler +thread. Beside the contention issues, we also loose potential work to +execute in parallel on different scheduler threads. This since the +process sending the *asynchronous* signal is blocked while the code +implementing the signal is executed synchronously. + +Solution +-------- + +In order to prevent multiple schedulers from trying to execute signals +to/from the same port simultaneously, we need to be able to ensure +that all signals to/from a port are executed in sequence on one +scheduler. More or less, the only way to do this is to schedule all +types of signals. Signals corresponding to a port can then be executed +in sequence by one single scheduler thread. If only one thread tries +to execute the port, no contention will appear on the port +lock. Besides getting rid of the contention, processes sending signals +to the port can also continue execution of their own Erlang code on +other schedulers at the same time as the signaling code is executing +on another scheduler. + +When implementing this there are a couple of important properties that +we either need, or want to preserve: + +* Signal ordering guarantee. Signals from process `X` to port `Y`, + *must* be delivered to `Y` in the same order as sent from `X`. + +* Signal latency. Due to the previous synchronous implementation, + latency of signals sent from processes to ports have usually been + very low. During contention the latency has of course + increased. Users expect latency of these signals to be low, a + sudden increase in latency would not be appreciated by our users. + +* Compatible flow control. Ports have for a very long time had the + possibility to use the busy port functionality when implementing + flow control. One may argue that this functionality fits very bad + with the conceptually completely asynchronous signaling, but the + functionality has been there for ages and is expected to be + there. When a port sets itself into a busy state, `command` + signals should not be delivered, and senders of such signals + should suspend until the port sets itself in a not busy state. + +### Scheduling of Port Signals ### + +A run queue has four queues for processes of different priority and +one queue for ports. The scheduler thread associated with the run +queue switch evenly between execution of processes and execution of +ports while both processes and ports exist in the queue. This is not +completely true, but not important for this discussion. A port that is +in a run queue also has a queue of tasks to execute. Each task +corresponds to an in- or outgoing signal. When the port is selected +for execution each task will be executed in sequence. The run queue +locks not only protected the queues of ports, but also the queues of +port tasks. + +Since we go from a state where I/O signals are the only port related +signals scheduled, to a state where potentially all port related +signals may be scheduled we may drastically increase the load on the +run queue lock. The amount of scheduled port tasks very much depend on +the Erlang application executing, which we do not control, and we do +not want to get increased contention on the run queue locks. We +therefore need another approach of protecting the port task queue. + +#### Task Queue #### + +We chose a "semi locked" approach, with one public locked task queue, +and a private, lock free, queue like, task data structure. This "semi +locked" approach is similar to how the message boxes of processes are +managed. The lock is port specific and only used for protection of +port tasks, so the run queue lock is now needed in more or less the +same way for ports as for processes. This ensures that we wont see an +increased lock contention on run queue locks due to this rewrite of +the port functionality. + +When an executing port runs out of work to execute in the private task +data structure, it moves the public task queue into the private task +data structure while holding the lock. Once tasks has been moved to +the private data structure no lock protects them. This way the port +can continue working on tasks in the private data structure without +having to fight for the lock. + +I/O signals may however be aborted. This could be solved by letting +the port specific scheduling lock also protect the private task data +structure, but then the port very frequently would have to fight with +others enqueueing new tasks. In order to handle this while keeping the +private task data structure lock free, we use a similar "non +aggressive" approach as we use when handling processes that gets +suspended while in the run queue. Instead of removing the aborted port +task, we just mark it as aborted using an atomic memory +operation. When a task is selected for execution, we first verify that +it has not been aborted. If aborted we, just drop the task. + +A task that can be aborted is referred via another data structure from +other parts of the system, so that a thread that needs to abort the +task can reach it. In order to be sure to safely deallocate a task +that is no longer used, we first clear this reference and then use the +thread progress functionality in order to make sure no references can +exist to the task. Unfortunately, also unmanaged threads might abort +tasks. This is very infrequent, but might occur. This could be handled +locally for each port, but would require extra information in each +port structure which very infrequently would be used. Instead of +implementing this in each port, we implemented general functionality +that can be used from unmanaged threads to delay thread progress. + +The private "queue like" task data structure could have been an +ordinary queue if it wasn't for the busy port functionality. When the +port has flagged itself as busy, `command` signals are not allowed to +be delivered and need to be blocked. Other signals sent from the same +sender following a `command` signal that has been blocked also have to +be blocked; otherwise, we would violate the ordering guarantee. At the +same time, other signals that have no dependencies to blocked +`command` signals are expected to be delivered. + +The above requirements makes the private task data structure a rather +complex data structure. It has a queue of unprocessed tasks, and a +busy queue. The busy queue contains blocked tasks corresponding to +`command` signals, and tasks with dependencies to such tasks. The busy +queue is accompanied by a table over blocked tasks based on sender +with a references into last task in the busy queue from a specific +sender. This since we need check for dependencies when new tasks are +processed in the queue of unprocessed tasks. When a new task is +processed that needs to be blocked it isn't enqueued at the end of the +busy queue, but instead directly after the last task with the same +sender. This in order to easily be able to detect when we have tasks +that no longer have any dependencies to tasks corresponding to +`command` signals which should be moved out of the busy queue. When +the port executes, it switches between processing tasks from the busy +queue, and processing directly from the unprocessed queue based on its +busy state. When processing directly from the unprocessed queue it +might, of course, have to move a task into the busy queue instead of +executing it. + +#### Busy Port Queue #### + +Since it is the port itself which decides when it is time to enter a +busy state, it needs to be executing in order to enter the busy +state. As a result of `command` signals being scheduled, we may get +into a situation where the port gets flooded by a huge amount of +`command` signals before it even gets a chance to set itself into a +busy state. This since it has not been scheduled for execution +yet. That is, under these circumstances the busy port functionality +loose the flow control properties it was intended to provide. + +In order to solve this, we introduced a new busy feature, namely "busy +port queue". The port has a limit of `command` data that is allowed to +be enqueued in the task queue. When this limit is reached, the port +will automatically enter a busy port queue state. When in this state, +senders of `command` signals will be suspended, but `command` signals +will still be delivered to the port unless it is also in a busy port +state. This limit is known as the high limit. + +There is also a low limit. When the amount of queued `command` data +falls below this limit and the port is in a busy port queue state, the +busy port queue state is automatically disabled. The low limit should +typically be significantly lower than the high limit in order to +prevent frequent oscillation around the busy port queue state. + +By introduction of this new busy state we still can provide the flow +control. Old driver do not even have to be changed. The limits can, +however, be configured and even disabled by the port. By default the +high limit is 8 KB and the low limit is 4 KB. + +### Preparation of Signal Send ### + +Previously all operations sending signals to ports began by acquiring +the port lock, then performed preparations for sending the signal, and +then finaly sent the signal. The preparations typically included +inspecting the state of the port, and preparing the data to pass along +with the signal. The preparation of data is frequently quite time +consuming, and did not really depend on the port. That is we would +like to do this without having the port lock locked. + +In order to improve this, state information was re-organized in the +port structer, so that we can access it using atomic memory +operations. This together with the new port table implementation, +enabled us to lookup the port and inspect the state before acquiring +the port lock, which in turn made it possible to perform preparations +of signal data before acquiring the port lock. + +### Preserving Low Latency ### + +If we disregard the contended cases, we will inevitably get a higher +latency when scheduling signals for execution at a later time than by +executing the signal immediately. In order to preserve the low latency +we now first check if this is a contended case or not. If it is, we +schedule the signal for later execution; otherwise, we execute the +signal immediately. It is a contended case if other signals already +are scheduled on the port, or if we fail to acquire the port +lock. That is we will not block waiting for the lock. + +Doing it this way we will preserve the low latency at the expense of +lost potential parallel execution of the signal and other code in the +process sending the signal. This default behaviour can however be +changed on port basis or system wide, forcing scheduling of all +signals from processes to ports that are not part of a synchronous +communication. That is, an unconditional request/response pair of +asynchronous signals. In this case it is no potential for parallelism, +and by that no point forcing scheduling of the request signal. + +The immediate execution of signals may also cause a scheduler that is +about to execute scheduled tasks to block waiting for the port +lock. This is however more or less the only scenario where a scheduler +needs to wait for the port lock. The maximum time it has to wait is +the time it takes to execute one signal, since we always schedule +signals when contention occurs. + +### Signal Operations ### + +Besides implementing the functionality enabling the scheduling, +preparation of signal data without port lock, etc, each operation +sending signals to ports had to be quite extensively re-written. This +in order to move all sub-operations that can be done without the lock +to a place before we have acquired the lock, and also since signals +now sometimes are executed immediately and sometimes scheduled for +execution at a later time which put different requirements on the data +to pass along with the signal. + +### Some Benchmark Results ### + +When running some simple benchmarks where contention only occur due to +I/O signals contending with signals from one single process we got a +speedup of 5-15%. When multiple processes send signals to one single +port the improvements can be much larger, but the scenario with one +process contending with I/O is the most common one. + +The benchmarks were run on a relatively new machine with an Intel i7 +quad core processor with hyper-threading using 8 schedulers. \ No newline at end of file diff --git a/erts/emulator/internal_doc/ProcessManagementOptimizations.md b/erts/emulator/internal_doc/ProcessManagementOptimizations.md new file mode 100644 index 0000000000..9e83633bef --- /dev/null +++ b/erts/emulator/internal_doc/ProcessManagementOptimizations.md @@ -0,0 +1,172 @@ +Process Management Optimizations +================================ + +Problems +-------- + +Early versions of the SMP support for the runtime system completely +relied on locking in order to protect data accesses from multiple +threads. In some cases this isn't that problematic, but in some cases +it really is. It complicates the code, ensuring all locks needed are +actually held, and ensuring that all locks are acquired in such an +order that no deadlock occur. Acquiring locks in the right order often +also involve releasing locks held, forcing threads to reread data +already read. A good recipe for creation of bugs. Trying to use more +fine-grained locking in order to increase possible parallelism in the +system makes the complexity situation even worse. Having to acquire a +bunch of locks when doing operations also often cause heavy lock +contention which cause poor scalability. + +Management of processes internally in the runtime system suffered from +these problems. When changing state on a process, for example from +`waiting` to `runnable`, a lock on the process needed to be +locked. When inserting a process into a run queue also a lock +protecting the run queue had to be locked. When migrating a process +from one run queue to another run queue, locks on both run queues and +on the process had to be locked. + +This last example is a quite common case in during normal +operation. For example, when a scheduler thread runs out of work it +tries to steal work from another scheduler threads run queue. When +searching for a victim to steal from there was a lot of juggling of +run queue locks involved, and during the actual theft finalized by +having to lock both run queues and the process. When one scheduler +runs out of work, often others also do, causing lots of lock +contention. + +Solution +-------- + +### Process ### + +In order to avoid these situations we wanted to be able to do most of +the fundamental operations on a process without having to acquire a +lock on the process. Some examples of such fundamental operations are, +moving a process between run queues, detecting if we need to insert it +into a run queue or not, detecting if it is alive or not. + +All of this information in the process structure that was needed by +these operations was protected by the process `status` lock, but the +information was spread across a number of fields. The fields used was +typically state fields that could contain a small number of different +states. By reordering this information a bit we could *easily* fit +this information into a 32-bit wide field of bit flags (only 12-flags +were needed). By moving this information we could remove five 32-bit +wide fields and one pointer field from the process structure! The move +also enabled us to easily read and change the state using atomic +memory operations. + +### Run Queue ### + +As with processes we wanted to be able to do the most fundamental +operations without having to acquire a lock on it. The most important +being able to determine if we should enqueue a process in a specific +run queue or not. This involves being able to read actual load, and +load balancing information. + +The load balancing functionality is triggered at repeated fixed +intervals. The load balancing more or less strives to even out run +queue lengths over the system. When balancing is triggered, +information about every run queue is gathered, migrations paths and +run queue length limits are set up. Migration paths and limits are +fixed until the next balancing has been done. The most important +information about each run queue is the maximum run queue length since +last balancing. All of this information were previously stored in the +run queues themselves. + +When a process has become runnable, for example due to reception of a +message, we need to determine which run queue to enqueue it +in. Previously this at least involved locking the run queue that the +process currently was assigned to while holding the status lock on the +process. Depending on load we sometimes also had to acquire a lock on +another run queue in order to be able to determine if it should be +migrated to that run queue or not. + +In order to be able to decide which run queue to use without having to +lock any run queues, we moved all fixed balancing information out of +the run queues into a global memory block. That is, migration paths +and run queue limits. Information that need to be frequently updated, +like for example maximum run queue length, were kept in the run queue, +but instead of operating on this information under locks we now use +atomic memory operations when accessing this information. This made it +possible to first determine which run queue to use, without locking +any run queues, and when decided, lock the chosen run queue and insert +the process. + +#### Fixed Balancing Information #### + +When determining which run queue to choose we need to read the fixed +balancing information that we moved out of the run queues. This +information is global, read only between load balancing operations, +but will be changed during a load balancing. We do not want to +introduce a global lock that needs to be acquired when accessing this +information. A reader optimized rwlock could avoid some of the +overhead since the data is most frequently read, but it would +unavoidably cause disruption during load balancing, since this +information is very frequently read. The likelihood of a large +disruption due to this also increase as number of schedulers grows. + +Instead of using a global lock protecting modifications of this +information, we write a completely new version of it at each load +balancing. The new version is written in another memory block than the +previous one, and published by issuing a write memory barrier and then +storing a pointer to the new memory block in a global variable using +an atomic write operation. + +When schedulers need to read this information, they read the pointer +to currently used information using an atomic read operation, and then +issue a data dependency read barrier, which on most architectures is a +no-op. That is, it is very little overhead getting access to this +information. + +Instead of allocating and deallocating memory blocks for the different +versions of the balancing information we keep old memory blocks and +reuse them when it is safe to do so. In order to be able to determine +when it is safe to reuse a block we use the thread progress +functionality, ensuring that no threads have any references to the +memory block when we reuse it. + +#### Be Less Aggressive #### + +We implemented a test version using lock free run queues. This +implementation did however not perform as good as the version using +one lock per run queue. The reason for this was not investigated +enough to say why this was. Since the locked version performed better +we kept it, at least for now. The lock free version, however, forced +us to use other solutions, some of them we kept. + +Previously when a process that was in a run queue got suspended, we +removed it from the queue straight away. This involved locking the +process, locking the run queue, and then unlinking it from the double +linked list implementing the queue. Removing a process from a lock +free queue gets really complicated. Instead, of removing it from the +queue, we just leave it in the queue and mark it as suspended. When +later selected for execution we check if the process is suspended, if +so just dropped it. During its time in the queue, it might also get +resumed again, if so execute it when it get selected for execution. + +By keeping this part when reverting back to a locked implementation, +we could remove a pointer field in each process structure, and avoid +unnecessary operations on the process and the queue which might cause +contention. + +### Combined Modifications ### + +By combining the modifications of the process state management and the +run queue management, we can do large parts of the work involved when +managing processes with regards to scheduling and migration without +having any locks locked at all. In these situations we previously had +to have multiple locks locked. This of course caused a lot of rewrites +across large parts of the runtime system, but the rewrite both +simplified code and eliminated locking at a number of places. The +major benefit is, of course, reduced contention. + +### A Benchmark Result ### + +When running the chameneosredux benchmark, schedulers frequently run +out of work trying to steal work from each other. That is, either +succeeding in migrating, or trying to migrate processes which is a +scenario which we wanted to optimize. By the introduction of these +improvements, we got a speedup of 25-35% when running this benchmark +on a relatively new machine with an Intel i7 quad core processor with +hyper-threading using 8 schedulers. \ No newline at end of file diff --git a/erts/emulator/internal_doc/ThreadProgress.md b/erts/emulator/internal_doc/ThreadProgress.md new file mode 100644 index 0000000000..6118bcf0f6 --- /dev/null +++ b/erts/emulator/internal_doc/ThreadProgress.md @@ -0,0 +1,308 @@ +Thread Progress +=============== + +Problems +-------- + +### Knowing When Threads Have Completed Accesses to a Data Structure ### + +When multiple threads access the same data structure you often need to +know when all threads have completed their accesses. For example, in +order to know when it is safe to deallocate the data structure. One +simple way to accomplish this is to reference count all accesses to +the data structure. The problem with this approach is that the cache +line where the reference counter is located needs to be communicated +between all involved processors. Such communication can become +extremely expensive and will scale poorly if the reference counter is +frequently accessed. That is, we want to use some other approach of +keeping track of threads than reference counting. + +### Knowing That Modifications of Memory is Consistently Observed ### + +Different hardware architectures have different memory models. Some +architectures allows very aggressive reordering of memory accesses +while other architectures only reorder a few specific cases. Common to +all modern hardware is, however, that some type of reordering will +occur. When using locks to protect all memory accesses made from +multiple threads such reorderings will not be visible. The locking +primitives will ensure that the memory accesses will be ordered. When +using lock free algorithms one do however have to take this reordering +made by the hardware into account. + +Hardware memory barriers or memory fences are instructions that can be +used to enforce order between memory accesses. Different hardware +architectures provide different memory barriers. Lock free algorithms +need to use memory barriers in order to ensure that memory accesses +are not reordered in such ways that the algorithm breaks down. Memory +barriers are also expensive instructions, so you typically want to +minimize the use of these instructions. + +Functionality Used to Address These Problems +------------------------------------------- + +The "thread progress" functionality in the Erlang VM is used to +address these problems. The name "thread progress" was chosen since we +want to use it to determine when all threads in a set of threads have +made such progress so that two specific events have taken place for +all them. + +The set of threads that we are interested in we call managed +threads. The managed threads are the only threads that we get any +information about. These threads *have* to frequently report +progress. Not all threads in the system are able to frequently report +progress. Such threads cannot be allowed in the set of managed threads +and are called unmanaged threads. An example of unmanaged threads are +threads in the async thread pool. Async threads can be blocked for +very long times and by this be prevented from frequently reporting +progress. Currently only scheduler threads and a couple of other +threads are managed threads. + +### Thread Progress Events ### + +Any thread in the system may use the thread progress functionality in +order to determine when the following events have occured at least +once in all managed threads: + +1. The thread has returned from other code to a known state in the + thread progress functionality, which is independent of any other + code. +2. The thread has executed a full memory barrier. + +These events, of course, need to occur ordered to other memory +operations. The operation of determining this begins by initiating the +thread progress operation. The thread that initiated the thread +progress operation after this poll for the completion of the +operation. Both of these events must occur at least once *after* the +thread progress operation has been initiated, and at least once +*before* the operation has completed in each managed thread. This is +ordered using communication via memory which makes it possible to draw +conclusion about the memory state after the thread progress operation +has completed. Lets call the progress made from initiation to +comletion for "thread progress". + +Assuming that the thread progress functionality is efficient, a lot of +algorithms can both be simplified and made more efficient than using +the first approach that comes to mind. A couple of examples follows. + +By being able to determine when the first event above has occurred we +can easily know when all managed threads have completed accesses to a +data structure. This can be determined the following way. We have an +implementation of some functionality `F` using a data structure +`D`. The reference to `D` is always looked up before `D` is being +accessed, and the references to `D` is always dropped before we leave +the code implementing `F`. If we remove the possibility to look up `D` +and then wait until the first event has occurred in all managed +threads, no managed threads can have any references to the data +structure `D`. This could for example have been achieved by using +reference counting, but the cache line containing the reference +counter would in this case be ping ponged between all processors +accessing `D` at every access. + +By being able to determine when the second event has occurred it is +quite easy to do complex modifications of memory that needs to be seen +consistently by other threads without having to resort to locking. By +doing the modifications, then issuing a full memory barrier, then wait +until the second event has occurred in all managed threads, and then +publish the modifications, we know that all managed threads reading +this memory will get a consistent view of the modifications. Managed +threads reading this will not have to issue any extra memory barriers +at all. + +Implementation of the Thread Progress Functionality +--------------------------------------------------- + +### Requirement on the Implementation ### + +In order to be able to determine when all managed threads have reached +the states that we are interested in we need to communicate between +all involved threads. We of course want to minimize this +communication. + +We also want threads to be able to determine when thread progress has +been made relatively fast. That is we need to have some balance +between comunication overhead and time to complete the operation. + +### API ### + +I will only present the most important functions in the API here. + +* `ErtsThrPrgrVal erts_thr_progress_later(void)` - Initiation of the + operation. The thread progress value returned can be used testing + for completion of the operation. +* `int erts_thr_progress_has_reached(ErtsThrPrgrVal val)` - Returns + a non zero value when we have reached the thread progress value + passed as argument. That is, when a non zero value is returned the + operation has completed. + +When a thread calls `my_val = erts_thr_progress_later()` and waits for +`erts_thr_progress_has_reached(my_val)` to return a non zero value it +knows that thread progress has been made. + +While waiting for `erts_thr_progress_has_reached()` to return a non +zero value we typically do not want to block waiting, but instead want +to continue working with other stuff. If we run out of other stuff to +work on we typically do want to block waiting until we have reached +the thread progress value that we are waiting for. In order to be able +to do this we provide functionality for waking up a thread when a +certain thread progress value has been reached: + +* `void erts_thr_progress_wakeup(ErtsSchedulerData *esdp, + ErtsThrPrgrVal val)` - Request wake up. The calling thread will be + woken when thread progress has reached val. + +Managed threads frequently need to update their thread progress by +calling the following functions: + +* `int erts_thr_progress_update(ErtsSchedulerData *esdp)` - Update + thread progress. If a non zero value is returned + `erts_thr_progress_leader_update()` has to be called without any + locks held. +* `int erts_thr_progress_leader_update(ErtsSchedulerData *esdp)` - + Leader update thread progress. + +Unmanaged threads can delay thread progress beeing made: + +* `ErtsThrPrgrDelayHandle erts_thr_progress_unmanaged_delay(void)` - + Delay thread progress. +* `void erts_thr_progress_unmanaged_continue(ErtsThrPrgrDelayHandle + handle)` - Let thread progress continue. + +Scheduler threads can schedule an operation to be executed by the +scheduler itself when thread progress has been made: + +* `void erts_schedule_thr_prgr_later_op(void (*funcp)(void *), void + *argp, ErtsThrPrgrLaterOp *memp)` - Schedule a call to `funcp`. The + call `(*funcp)(argp)` will be executed when thread progress has been + made since the call to `erts_schedule_thr_prgr_later_op()` was + made. + +### Implementation ### + +In order to determine when the events has happened we use a global +counter that is incremented when all managed threads have called +`erts_thr_progress_update()` (or `erts_thr_progress_leader_update()`). +This could naively be implemented using a "thread confirmed" counter. +This would however cause an explosion of communication where all +involved processors would need to communicate with each other at each +update. + +Instead of confirming at a global location each thread confirms that +it accepts in increment of the global counter in its own cache +line. These confirmation cache lines are located in sequence in an +array, and each confirmation cache line will only be written by one +and only one thread. One of the managed threads always have the leader +responsibility. This responsibility may jump between threads, but as +long as there are some activity in the system always one of them will +have the leader responsibility. The thread with the leader +responsibility will call `erts_thr_progress_leader_update()` which +will check that all other threads have confirmed an increment of the +global counter before doing the increment of the global counter. The +leader thread is the only thread reading the confirmation cache +lines. + +Doing it this way we will get a communication pattern of information +going from the leader thread out to all other managed threads and then +back from the other threads to the leader thread. This since only the +leader thread will write to the global counter and all other threads +will only read it, and since each confirmation cache lines will only +be written by one specific thread and only read by the leader +thread. When each managed thread is distributed over different +processors, the communication between processors will be a reflection +of this communication pattern between threads. + +The value returned from `erts_thr_progress_later()` equals the, by +this thread, latest confirmed value plus two. The global value may be +latest confirmed value or latest confirmed value minus one. In order +to be certain that all other managed threads actually will call +`erts_thr_progress_update()` at least once before we reach the value +returned from `erts_thr_progress_later()`, the global counter plus one +is not enough. This since all other threads may already have confirmed +current global value plus one at the time when we call +`erts_thr_progress_later()`. They are however guaranteed not to have +confirmed global value plus two at this time. + +The above described implementation more or less minimizes the +comunication needed before we can increment the global counter. The +amount of communication in the system due to the thread progress +functionality however also depend on the frequency with which managed +threads call `erts_thr_progress_update()`. Today each scheduler thread +calls `erts_thr_progress_update()` more or less each time an Erlang +process is scheduled out. One way of further reducing communication +due to the thread progress functionality is to only call +`erts_thr_progress_update()` every second, or third time an Erlang +process is scheduled out, or even less frequently than that. However, +by doing updates of thread progress less frequently all operations +depending on the thread progress functionality will also take a longer +time. + +#### Delay of Thread Progress by Unmanaged Threads #### + +In order to implement delay of thread progress from unmanaged threads +we use two reference counters. One being `current` and one being +`waiting`. When an unmanaged thread wants to delay thread progress it +increments `current` and gets a handle back to the reference counter +it incremented. When it later wants to enable continuation of thread +progress it uses the handle to decrement the reference counter it +previously incremented. + +When the leader threads is about to increment the global thread +progress counter it verifies that the `waiting` counter is zero before +doing so. If not zero, the leader isn't allowed to increment the +global counter, and needs to wait before it can do this. When it is +zero, it swaps the `waiting` and `current` counters before increasing +the global counter. From now on the new `waiting` counter will +decrease, so that it eventualy will reach zero, making it possible to +increment the global counter the next time. If we only used one +reference counter it would potentially be held above zero for ever by +different unmanaged threads. + +When an unmanaged thread increment the `current` counter it will not +prevent the next increment of the global counter, but instead the +increment after that. This is sufficient since the global counter +needs to be incremented two times before thread progress has been +made. It is also desirable not to prevent the first increment, since +the likelyhood increases that the delay is withdrawn before any +increment of the global counter is delayed. That is, the operation +will cause as little disruption as possible. + +However, this feature of delaying thread progress from unmanaged +threads should preferably be used as little as possible, since heavy +use of it will cause contention on the reference counter cache +lines. The functionality is however very useful in code which normally +only executes in managed threads, but which may under some infrequent +circumstances be executed in other threads. + +#### Overhead #### + +The overhead caused by the thread progress functionality is more or +less fixed using the same amount of schedulers regardless of the +number of uses of the functionality. Already today quite a lot of +functionality use it, and we plan to use it even more. When rewriting +old implementations of ERTS internal functionality to use the thread +progress functionality, this implies removing communication in the old +implementation. Otherwise it is simply no point rewriting the old +implementation to use the thread progress functionality. Since the +thread progress overhead is more or less fixed, the rewrite will cause +a reduction of the total communication in the system. + +##### An Example ##### + +The main structure of an ETS table was originally managed using +reference counting. Already a long time ago we replaced this strategy +since the reference counter caused contention on each access of the +table. The solution used was to schedule "confirm deletion" jobs on +each scheduler in order to know when it was safe to deallocate the +table structure of a removed table. These confirm deletion jobs needed +to be allocated. That is, we had to allocate and deallocate as many +blocks as schedulers in order to deallocate one block. This of course +was a quite an expensive operation, but we only needed to do this once +when removing a table. It was more important to get rid of the +contention on the reference counter which was present on every +operation on the table. + +When the thread progress functionality had been introduced, we could +remove the code implementing the "confirm deletion" jobs, and then +just schedule a thread progress later operation which deallocates the +structure. Besides simplifying the code a lot, we got an increase of +more than 10% of the number of transactions per second handled on a +mnesia tpcb benchmark executing on a quad core machine. -- cgit v1.2.3 From e086297a8638a9f57983fd707f6c75e3d4b6edf3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 8 Jan 2014 15:56:29 +0100 Subject: erts: Add internal docs for code loading and tracing --- erts/emulator/internal_doc/CodeLoading.md | 186 +++++++++++++++++++++++++ erts/emulator/internal_doc/Tracing.md | 220 ++++++++++++++++++++++++++++++ 2 files changed, 406 insertions(+) create mode 100644 erts/emulator/internal_doc/CodeLoading.md create mode 100644 erts/emulator/internal_doc/Tracing.md (limited to 'erts') diff --git a/erts/emulator/internal_doc/CodeLoading.md b/erts/emulator/internal_doc/CodeLoading.md new file mode 100644 index 0000000000..151b9cd57c --- /dev/null +++ b/erts/emulator/internal_doc/CodeLoading.md @@ -0,0 +1,186 @@ +Non-Blocking Code Loading +========================= + +Introduction +------------ + +Before OTP R16 when an Erlang code module was loaded, all other +execution in the VM were halted while the load operation was carried +out in single threaded mode. This might not be a big problem for +initial loading of modules during VM boot, but it can be a severe +problem for availability when upgrading modules or adding new code on +a VM with running payload. This problem grows with the number of cores +as both the time it takes to wait for all schedulers to stop increases +as well as the potential amount of halted ongoing work. + +In OTP R16, modules are loaded without blocking the VM. +Erlang processes may continue executing undisturbed in parallel during +the entire load operation. The code loading is carried out by a normal +Erlang process that is scheduled like all the others. The load +operation is completed by making the loaded code visible to all +processes in a consistent way with one single atomic +instruction. Non-blocking code loading will improve real-time +characteristics when modules are loaded/upgraded on a running SMP +system. + + +The Load Phases +--------------- + +The loading of a module is divided into two phases; a *prepare phase* +and a *finishing phase*. The prepare phase contains reading the BEAM +file format and all the preparations of the loaded code that can +easily be done without interference with the running code. The +finishing phase will make the loaded (and prepared) code accessible +from the running code. Old module versions (replaced or deleted) will +also be made inaccessible by the finishing phase. + +The prepare phase is designed to allow several "loader" processes to +prepare separate modules in parallel while the finishing phase can +only be done by one loader process at a time. A second loader process +trying to enter finishing phase will be suspended until the first +loader is done. This will only block the process, the scheduler is +free to schedule other work while the second loader is waiting. (See +`erts_try_seize_code_write_permission` and +`erts_release_code_write_permission`). + +The ability to prepare several modules in parallel is not currently +used as almost all code loading is serialized by the code_server +process. The BIF interface is however prepared for this. + + erlang:prepare_loading(Module, Code) -> LoaderState + erlang:finish_loading([LoaderState]) + +The idea is that `prepare_loading` could be called in parallel for +different modules and returns a "magic binary" containing the internal +state of each prepared module. Function `finish_loading` could take a +list of such states and do the finishing of all of them in one go. + +Currenlty we use the legacy BIF `erlang:load_module` which is now +implemented in Erlang by calling the above two functions in +sequence. Function `finish_loading` is limited to only accepts a list +with one module state as we do not yet use the multi module loading +feature. + + +The Finishing Sequence +---------------------- + +During VM execution, code is accessed through a number of data +structures. These *code access structures* are + +* Export table. One entry for every exported function. +* Module table. One entry for each loaded module. +* "beam_catches". Identifies jump destinations for catch instructions. +* "beam_ranges". Map code address to function and line in source file. + +The most frequently used of these structures is the export table that +is accessed in run time for every executed external function call to +get the address of the callee. For performance reasons, we want to +access all these structures without any overhead from thread +synchronization. Earlier this was solved with an emergency break. Stop +the entire VM to mutate these code access structures, otherwise treat +them as read-only. + +The solution in R16 is instead to *replicate* the code access +structures. We have one set of active structures read by the running +code. When new code is loaded the active structures are copied, the +copy is updated to include the newly loaded module and then a switch +is made to make the updated copy the new active set. The active set is +identified by a single global atomic variable +`the_active_code_index`. The switch can thus be made by a single +atomic write operation. The running code have to read this atomic +variable when using the active access structures, which means one +atomic read operation per external function call for example. The +performance penalty from this extra atomic read is however very small +as it can be done without any memory barriers at all (as described +below). With this solution we also preserve the transactional feature +of a load operation. Running code will never see the intermediate +result of a half loaded module. + +The finishing phase is carried out in the following sequence by the +BIF `erlang:finish_loading`: + +1. Seize exclusive code write permission (suspend process if needed + until we get it). + +2. Make a full copy of all the active access structures. This copy is + called the staging area and is identified by the global atomic + variable `the_staging_code_index`. + +3. Update all access structures in the staging area to include the + newly prepared module. + +4. Schedule a thread progress event. That is a time in the future when + all schedulers have yielded and executed a full memory barrier. + +5. Suspend the loader process. + +6. After thread progress, commit the staging area by assigning + `the_staging_code_index` to `the_active_code_index`. + +7. Release the code write permission allowing other processes to stage + new code. + +8. Resume the loader process allowing it to return from + `erlang:finish_loading`. + + +### Thread Progress + +The waiting for thread progress in 4-6 is necessary in order for +processes to read `the_active_code_index` atomic during normal +execution without any expensive memory barriers. When we write a new +value into `the_active_code_index` in step 6, we know that all +schedulers will see an updated and consistent view of all the new +active access structures once they become reachable through +`the_active_code_index`. + +The total lack of memory barrier when reading `the_active_code_index` +has one interesting consequence however. Different processes may see +the new code at different point in time depending on when different +cores happen to refresh their hardware caches. This may sound unsafe +but it actually does not matter. The only property we must guarantee +is that the ability to see the new code must spread with process +communication. After receiving a message that was triggered by new +code, the receiver must be guaranteed to also see the new code. This +will be guaranteed as all types of process communication involves +memory barriers in order for the receiver to be sure to read what the +sender has written. This implicit memory barrier will then also make +sure that the receiver reads the new value of `the_active_code_index` +and thereby also sees the new code. This is true for all kinds of +inter process communication (TCP, ETS, process name registering, +tracing, drivers, NIFs, etc) not just Erlang messages. + +### Code Index Reuse + +To optimize the copy operation in step 2, code access structures are +reused. In current solution we have three sets of code access +structures, identified by a code index of 0, 1 and 2. These indexes +are used in a round robin fashion. Instead of having to initialize a +completely new copy of all access structures for every load operation +we just have to update with the changes that have happened since the +last two code load operations. We could get by with only two code +indexes (0 and 1), but that would require yet another round of waiting +for thread progress before step 2 in the `finish_loading` sequence. We +cannot start reusing a code index as staging area until we know that +no lingering scheduler thread is still using it as the active code +index. With three generations of code indexes, the waiting for thread +progress in step 4-6 will give this guarantee for us. Thread progress +will wait for all running schedulers to reschedule at least one +time. No ongoing execution reading code access structures reached from +an old value of `the_active_code_index` can exist after a second round +of thread progress. + +The design choice between two or three generations of code access +structures is a trade-off between memory consumption and code loading +latency. + +### A Consistent Code View + +Some native BIFs may need to get a consistent snapshot view of the +active code. To do this it is important to only read +`the_active_code_index` one time and then use that index value for all +code accessing during the BIF. If a load operation is executed in +parallel, reading `the_active_code_index` a second time might result +in a different value, and thereby a different view of the code. diff --git a/erts/emulator/internal_doc/Tracing.md b/erts/emulator/internal_doc/Tracing.md new file mode 100644 index 0000000000..30bc5327a7 --- /dev/null +++ b/erts/emulator/internal_doc/Tracing.md @@ -0,0 +1,220 @@ +Non-blocking trace setting +========================== + +Introduction +------------ + +Before OTP R16 when trace settings were changed by `erlang:trace_pattern`, +all other execution in the VM were halted while the trace operation +was carried out in single threaded mode. Similar to code loading, this +can impose a severe problem for availability that grows with the +number of cores. + +In OTP R16, trace breakpoints are set in the code without blocking the +VM. Erlang processes may continue executing undisturbed in parallel +during the entire operation. The same base technique is used as for +code loading. A staging area of breakpoints is prepared and then made +active with a single atomic operation. + + +Redesign of Breakpoint Wheel +---------------------------- + +To make it easier to manage breakpoints without single threaded mode a +redesign of the breakpoint mechanism has been made. The old +"breakpoint wheel" data structure was a circular double-linked list of +breakpoints for each instrumented function. It was invented before the +SMP emulator. To support it in the SMP emulator, is was essentially +expanded to one breakpoint wheel per scheduler. As more breakpoint +types have been added, the implementation have become messy and hard +to understand and maintain. + +In the new design the old wheel was dropped and instead replaced by +one struct (`GenericBp`) to hold the data for all types of breakpoints +for each instrumented function. A bit-flag field is used to indicate +what different type of break actions that are enabled. + + +Same Same but Different +----------------------- +Even though `trace_pattern` use the same technique as the non-blocking +code loading with replicated generations of data structures and an +atomic switch, the implementations are quite separate from each +other. One initial idea was to use the existing mechanism of code +loading to do a dummy load operation that would make a copy of the +affected modules. That copy could then be instrumented with +breakpoints before making it reachable with the same atomic switch as +done for code loading. This approach seems straight forward but has a +number of shortcomings, one being the large memory footprint when many +modules are instrumented. Another problem is how execution will reach +the new instrumented code. Normally loaded code can only be reached +through external functions calls. Trace settings must be activated +instantaneously without the need of external function calls. + +The choosen solution is instead for tracing to use the technique of +replication applied on the data structures for breakpoints. Two +generations of breakpoints are kept and indentified by index of 0 and +1. The global atomic variables `erts_active_bp_index` will determine +which generation of breakpoints running code will use. + +### Atomicy Without Atomic Operations + +Not using the code loading generations (or any other code duplication) +means that `trace_pattern` must at some point write to the active beam +code in order for running processes to reach the staged breakpoints +structures. This can be done with one single atomic write operation +per instrumented function. The beam instruction words are however read +with normal memory loads and not through the atomic API. The only +guarantee we need is that the written instruction word is seen as +atomic. Either fully written or not at all. This is true for word +aligned write operation on all hardware architectures we use. + + +Adding a new Breakpoint +----------------------- +This is a simplified sequence describing what `trace_pattern` goes +through when adding a new breakpoint. + +1. Seize exclusive code write permission (suspend process until we get it). + +2. Allocate breakpoint structure `GenericBp` including both generations. + Set the active part as disabled with a zeroed flagfield. Save the original + instruction word in the breakpoint. + +3. Write a pointer to the breakpoint at offset -4 from the first + instruction "func_info" header. + +4. Set the staging part of the breakpoint as enabled with specified + breakpoint data. + +5. Wait for thread progress. + +6. Write a `op_i_generic_breakpoint` as the first instruction for the function. + This instruction will execute the breakpoint that it finds at offset -4. + +7. Wait for thread progress. + +8. Commit the breadpoint by switching `erts_active_bp_index`. + +9. Wait for thread progress. + +10. Prepare for next call to `trace_pattern` by updating the new staging part + (the old active) of the breakpoint to be identic to the the new active part. + +11. Release code write permission and return from `trace_pattern`. + + +The code write permission "lock" seized in step 1 is the same as used +by code loading. This will ensure that only one process at a time can +stage new trace settings but it will also prevent concurrent code +loading and make sure we see a consistent view of the beam code during +the entire sequence. + +Between step 6 and 8, runninng processes might execute the written +`op_i_generic_breakpoint` instruction. They will get the breakpoint +structure written in step 3, read `erts_active_bp_index` and execute +the corresponding part of the breakpoint. Before the switch in step 8 +becomes visible they will however execute the disabled part of the +breakpoint structure and do nothing other than executing the saved +original instruction. + + +To Updating and Remove Breakpoints +---------------------------------- + +The above sequence did only describe adding a new breakpoint. We do +basically the same sequence to update the settings of an existing +breakpoint except step 2,3 and 6 can be skipped as it has already been +done. + +To remove a breakpoint some more steps are needed. The idea is to +first stage the breakpoint as disabled, do the switch, wait for thread +progress and then remove the disabled breakpoint by restoring the +original beam instruction. + +Here is a more complete sequence that contains both adding, updating +and removing breakpoints. + +1. Seize exclusive code write permission (suspend process until we get it). + +2. Allocate new breakpoint structures with a disabled active part and + the original beam instruction. Write a pointer to the breakpoint in + "func_info" header at offset -4. + +3. Update the staging part of all affected breakpoints. Disable + breakpoints that are to be removed. + +4. Wait for thread progress. + +5. Write a `op_i_generic_breakpoint` as the first instruction for all + functions with new breakpoints. + +6. Wait for thread progress. + +7. Commit all staged breadpoints by switching `erts_active_bp_index`. + +8. Wait for thread progress. + + +9. Restore original beam instruction for disabled breakpoints. + +10. Wait for thread progress. + +11. Prepare for next call to `trace_pattern` by updating the new + staging area (the old active) for all enabled breakpoints. + +12. Deallocate disabled breakpoint structures. + +13. Release code write permission and return from `trace_pattern`. + + +### All that Waiting for Thread Progress + +There are four rounds of waiting for thread progress in the above +sequence. In the code loading sequence we sacrificed memory overhead +of three generations to avoid a second round of thread progress. The +latency of `trace_pattern` should not be such a big problem for +however, as it is normally not called in a rapid sequence. + +The waiting in step 4 is to make sure all threads will see an updated +view of the breakpoint structures once they become reachable through +the `op_i_generic_breakpoint` instruction written in step 5. + +The waiting in step 6 is to make the activation of the new trace +settings "as atomic as possible". Different cores might see the new +value of `erts_active_bp_index` at different times as it is read +without any memory barrier. But this is the best we can do without +more expensive thread synchronization. + +The waiting in step 8 is to make sure we dont't restore the original +bream instructions for disabled breakpoints until we know that no +thread is still accessing the old enabled part of a disabled +breakpoint. + +The waiting in step 10 is to make sure no lingering thread is still +accessing disabled breakpoint structures to be deallocated in step +12. + + +Global Tracing +-------------- + +Call tracing with `global` option only affects external function +calls. This was earlier handled by inserting a special trace +instruction in export entries without the use of breakpoints. With the +new non-blocking tracing we want to avoid special handling for global +tracing and make use of the staging and atomic switching within the +breakpoint mechanism. The solution was to create the same type of +breakpoint structure for a global call trace. The difference to local +tracing is that we insert the `op_i_generic_breakpoint` instruction +(with its pointer at offset -4) in the export entry rather than in the +code. + + +Future work +----------- + +We still go to single threaded mode when new code is loaded for a +module that is traced, or when loading code when there is a default +trace pattern set. That is not impossible to fix, but that requires +much closer cooperation between tracing BIFs and the loader BIFs. -- cgit v1.2.3 From 99fbb7cffd40562907487278ae5f88b2e76d4923 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 8 Jan 2014 19:19:33 +0100 Subject: erts: Remove overestimation of heap space in binary_to_term for 32-bit integers (INTEGER_EXT) on 64-bit architectures. --- erts/emulator/beam/external.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 2cb44a5b64..bccbedc7bd 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -4074,7 +4074,9 @@ init_done: switch (tag) { case INTEGER_EXT: SKIP(4); +#if !defined(ARCH_64) || HALFWORD_HEAP heap_size += BIG_UINT_HEAP_SIZE; +#endif break; case SMALL_INTEGER_EXT: SKIP(1); -- cgit v1.2.3 From 2947f831d65b5bd807fa6e6f78cd2e96a7c26b35 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 8 Jan 2014 20:14:18 +0100 Subject: erts: Reduce heap usage for binary_SUITE:deep --- erts/emulator/test/binary_SUITE.erl | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index bce4278337..53cafb3fbc 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1241,16 +1241,27 @@ bsbs_1(A) -> Bin = binary_to_term_stress(<<131,$M,5:32,A,0,0,0,0,0>>), BinSize = bit_size(Bin). +%% lists:foldl(_,_,lists:seq(_,_)) with less heap consumption +lists_foldl_seq(Fun, Acc0, N, To) when N =< To -> + Acc1 = Fun(N, Acc0), + lists_foldl_seq(Fun, Acc1, N+1, To); + +lists_foldl_seq(_, Acc, _, _) -> + Acc. + deep(Config) when is_list(Config) -> - ?line deep_roundtrip(lists:foldl(fun(E, A) -> - [E,A] - end, [], lists:seq(1, 1000000))), - ?line deep_roundtrip(lists:foldl(fun(E, A) -> - {E,A} - end, [], lists:seq(1, 1000000))), - ?line deep_roundtrip(lists:foldl(fun(E, A) -> - fun() -> {E,A} end - end, [], lists:seq(1, 1000000))), + deep_roundtrip(lists_foldl_seq(fun(E, A) -> + [E,A] + end, [], 1, 1000000)), + erlang:garbage_collect(), + deep_roundtrip(lists_foldl_seq(fun(E, A) -> + {E,A} + end, [], 1, 1000000)), + erlang:garbage_collect(), + deep_roundtrip(lists_foldl_seq(fun(E, A) -> + fun() -> {E,A} end + end, [], 1, 1000000)), + erlang:garbage_collect(), ok. deep_roundtrip(T) -> -- cgit v1.2.3 From 11533683c568402ec11afc9e1823debac74414d7 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 8 Jan 2014 20:15:37 +0100 Subject: erts: Fix useless comparisons in binary_SUITE:external_size --- erts/emulator/test/binary_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 53cafb3fbc..eee299db56 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -506,8 +506,8 @@ external_size(Config) when is_list(Config) -> io:format("Unaligned size: ~p\n", [Sz2]), ?line ?t:fail() end, - ?line erlang:external_size(Bin) =:= erlang:external_size(Bin, [{minor_version, 1}]), - ?line erlang:external_size(Unaligned) =:= erlang:external_size(Unaligned, [{minor_version, 1}]). + true = (erlang:external_size(Bin) =:= erlang:external_size(Bin, [{minor_version, 1}])), + true = (erlang:external_size(Unaligned) =:= erlang:external_size(Unaligned, [{minor_version, 1}])). external_size_1(Term, Size0, Limit) when Size0 < Limit -> case erlang:external_size(Term) of -- cgit v1.2.3 From 069f9ec3dfd7a4dc17973e22dc271d40ebe11467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 9 Jan 2014 12:25:57 +0100 Subject: erts/zlib: Remove unused file example.c --- erts/emulator/zlib/example.c | 570 ------------------------------------------- 1 file changed, 570 deletions(-) delete mode 100644 erts/emulator/zlib/example.c (limited to 'erts') diff --git a/erts/emulator/zlib/example.c b/erts/emulator/zlib/example.c deleted file mode 100644 index ebe828f72d..0000000000 --- a/erts/emulator/zlib/example.c +++ /dev/null @@ -1,570 +0,0 @@ -/* example.c -- usage example of the zlib compression library - * Copyright (C) 1995-2004 Jean-loup Gailly. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* %ExternalCopyright% */ - -/* @(#) $Id$ */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include -#include "zlib.h" - -#ifdef STDC -# include -# include -#endif - -#if defined(VMS) || defined(RISCOS) -# define TESTFILE "foo-gz" -#else -# define TESTFILE "foo.gz" -#endif - -#define CHECK_ERR(err, msg) { \ - if (err != Z_OK) { \ - fprintf(stderr, "%s error: %d\n", msg, err); \ - exit(1); \ - } \ -} - -const char hello[] = "hello, hello!"; -/* "hello world" would be more standard, but the repeated "hello" - * stresses the compression code better, sorry... - */ - -const char dictionary[] = "hello"; -uLong dictId; /* Adler32 value of the dictionary */ - -void test_compress OF((Byte *compr, uLong comprLen, - Byte *uncompr, uLong uncomprLen)); -void test_gzio OF((const char *fname, - Byte *uncompr, uLong uncomprLen)); -void test_deflate OF((Byte *compr, uLong comprLen)); -void test_inflate OF((Byte *compr, uLong comprLen, - Byte *uncompr, uLong uncomprLen)); -void test_large_deflate OF((Byte *compr, uLong comprLen, - Byte *uncompr, uLong uncomprLen)); -void test_large_inflate OF((Byte *compr, uLong comprLen, - Byte *uncompr, uLong uncomprLen)); -void test_flush OF((Byte *compr, uLong *comprLen)); -void test_sync OF((Byte *compr, uLong comprLen, - Byte *uncompr, uLong uncomprLen)); -void test_dict_deflate OF((Byte *compr, uLong comprLen)); -void test_dict_inflate OF((Byte *compr, uLong comprLen, - Byte *uncompr, uLong uncomprLen)); -int main OF((int argc, char *argv[])); - -/* =========================================================================== - * Test compress() and uncompress() - */ -void test_compress(compr, comprLen, uncompr, uncomprLen) - Byte *compr, *uncompr; - uLong comprLen, uncomprLen; -{ - int err; - uLong len = (uLong)strlen(hello)+1; - - err = compress(compr, &comprLen, (const Bytef*)hello, len); - CHECK_ERR(err, "compress"); - - strcpy((char*)uncompr, "garbage"); - - err = uncompress(uncompr, &uncomprLen, compr, comprLen); - CHECK_ERR(err, "uncompress"); - - if (strcmp((char*)uncompr, hello)) { - fprintf(stderr, "bad uncompress\n"); - exit(1); - } else { - printf("uncompress(): %s\n", (char *)uncompr); - } -} - -/* =========================================================================== - * Test read/write of .gz files - */ -void test_gzio(fname, uncompr, uncomprLen) - const char *fname; /* compressed file name */ - Byte *uncompr; - uLong uncomprLen; -{ -#ifdef NO_GZCOMPRESS - fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n"); -#else - int err; - int len = (int)strlen(hello)+1; - gzFile file; - z_off_t pos; - - file = gzopen(fname, "wb"); - if (file == NULL) { - fprintf(stderr, "gzopen error\n"); - exit(1); - } - gzputc(file, 'h'); - if (gzputs(file, "ello") != 4) { - fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err)); - exit(1); - } - if (gzprintf(file, ", %s!", "hello") != 8) { - fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err)); - exit(1); - } - gzseek(file, 1L, SEEK_CUR); /* add one zero byte */ - gzclose(file); - - file = gzopen(fname, "rb"); - if (file == NULL) { - fprintf(stderr, "gzopen error\n"); - exit(1); - } - strcpy((char*)uncompr, "garbage"); - - if (gzread(file, uncompr, (unsigned)uncomprLen) != len) { - fprintf(stderr, "gzread err: %s\n", gzerror(file, &err)); - exit(1); - } - if (strcmp((char*)uncompr, hello)) { - fprintf(stderr, "bad gzread: %s\n", (char*)uncompr); - exit(1); - } else { - printf("gzread(): %s\n", (char*)uncompr); - } - - pos = gzseek(file, -8L, SEEK_CUR); - if (pos != 6 || gztell(file) != pos) { - fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n", - (long)pos, (long)gztell(file)); - exit(1); - } - - if (gzgetc(file) != ' ') { - fprintf(stderr, "gzgetc error\n"); - exit(1); - } - - if (gzungetc(' ', file) != ' ') { - fprintf(stderr, "gzungetc error\n"); - exit(1); - } - - gzgets(file, (char*)uncompr, (int)uncomprLen); - if (strlen((char*)uncompr) != 7) { /* " hello!" */ - fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err)); - exit(1); - } - if (strcmp((char*)uncompr, hello + 6)) { - fprintf(stderr, "bad gzgets after gzseek\n"); - exit(1); - } else { - printf("gzgets() after gzseek: %s\n", (char*)uncompr); - } - - gzclose(file); -#endif -} - -/* =========================================================================== - * Test deflate() with small buffers - */ -void test_deflate(compr, comprLen) - Byte *compr; - uLong comprLen; -{ - z_stream c_stream; /* compression stream */ - int err; - uLong len = (uLong)strlen(hello)+1; - - c_stream.zalloc = (alloc_func)0; - c_stream.zfree = (free_func)0; - c_stream.opaque = (voidpf)0; - - err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); - CHECK_ERR(err, "deflateInit"); - - c_stream.next_in = (Bytef*)hello; - c_stream.next_out = compr; - - while (c_stream.total_in != len && c_stream.total_out < comprLen) { - c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */ - err = deflate(&c_stream, Z_NO_FLUSH); - CHECK_ERR(err, "deflate"); - } - /* Finish the stream, still forcing small buffers: */ - for (;;) { - c_stream.avail_out = 1; - err = deflate(&c_stream, Z_FINISH); - if (err == Z_STREAM_END) break; - CHECK_ERR(err, "deflate"); - } - - err = deflateEnd(&c_stream); - CHECK_ERR(err, "deflateEnd"); -} - -/* =========================================================================== - * Test inflate() with small buffers - */ -void test_inflate(compr, comprLen, uncompr, uncomprLen) - Byte *compr, *uncompr; - uLong comprLen, uncomprLen; -{ - int err; - z_stream d_stream; /* decompression stream */ - - strcpy((char*)uncompr, "garbage"); - - d_stream.zalloc = (alloc_func)0; - d_stream.zfree = (free_func)0; - d_stream.opaque = (voidpf)0; - - d_stream.next_in = compr; - d_stream.avail_in = 0; - d_stream.next_out = uncompr; - - err = inflateInit(&d_stream); - CHECK_ERR(err, "inflateInit"); - - while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) { - d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */ - err = inflate(&d_stream, Z_NO_FLUSH); - if (err == Z_STREAM_END) break; - CHECK_ERR(err, "inflate"); - } - - err = inflateEnd(&d_stream); - CHECK_ERR(err, "inflateEnd"); - - if (strcmp((char*)uncompr, hello)) { - fprintf(stderr, "bad inflate\n"); - exit(1); - } else { - printf("inflate(): %s\n", (char *)uncompr); - } -} - -/* =========================================================================== - * Test deflate() with large buffers and dynamic change of compression level - */ -void test_large_deflate(compr, comprLen, uncompr, uncomprLen) - Byte *compr, *uncompr; - uLong comprLen, uncomprLen; -{ - z_stream c_stream; /* compression stream */ - int err; - - c_stream.zalloc = (alloc_func)0; - c_stream.zfree = (free_func)0; - c_stream.opaque = (voidpf)0; - - err = deflateInit(&c_stream, Z_BEST_SPEED); - CHECK_ERR(err, "deflateInit"); - - c_stream.next_out = compr; - c_stream.avail_out = (uInt)comprLen; - - /* At this point, uncompr is still mostly zeroes, so it should compress - * very well: - */ - c_stream.next_in = uncompr; - c_stream.avail_in = (uInt)uncomprLen; - err = deflate(&c_stream, Z_NO_FLUSH); - CHECK_ERR(err, "deflate"); - if (c_stream.avail_in != 0) { - fprintf(stderr, "deflate not greedy\n"); - exit(1); - } - - /* Feed in already compressed data and switch to no compression: */ - deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); - c_stream.next_in = compr; - c_stream.avail_in = (uInt)comprLen/2; - err = deflate(&c_stream, Z_NO_FLUSH); - CHECK_ERR(err, "deflate"); - - /* Switch back to compressing mode: */ - deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED); - c_stream.next_in = uncompr; - c_stream.avail_in = (uInt)uncomprLen; - err = deflate(&c_stream, Z_NO_FLUSH); - CHECK_ERR(err, "deflate"); - - err = deflate(&c_stream, Z_FINISH); - if (err != Z_STREAM_END) { - fprintf(stderr, "deflate should report Z_STREAM_END\n"); - exit(1); - } - err = deflateEnd(&c_stream); - CHECK_ERR(err, "deflateEnd"); -} - -/* =========================================================================== - * Test inflate() with large buffers - */ -void test_large_inflate(compr, comprLen, uncompr, uncomprLen) - Byte *compr, *uncompr; - uLong comprLen, uncomprLen; -{ - int err; - z_stream d_stream; /* decompression stream */ - - strcpy((char*)uncompr, "garbage"); - - d_stream.zalloc = (alloc_func)0; - d_stream.zfree = (free_func)0; - d_stream.opaque = (voidpf)0; - - d_stream.next_in = compr; - d_stream.avail_in = (uInt)comprLen; - - err = inflateInit(&d_stream); - CHECK_ERR(err, "inflateInit"); - - for (;;) { - d_stream.next_out = uncompr; /* discard the output */ - d_stream.avail_out = (uInt)uncomprLen; - err = inflate(&d_stream, Z_NO_FLUSH); - if (err == Z_STREAM_END) break; - CHECK_ERR(err, "large inflate"); - } - - err = inflateEnd(&d_stream); - CHECK_ERR(err, "inflateEnd"); - - if (d_stream.total_out != 2*uncomprLen + comprLen/2) { - fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out); - exit(1); - } else { - printf("large_inflate(): OK\n"); - } -} - -/* =========================================================================== - * Test deflate() with full flush - */ -void test_flush(compr, comprLen) - Byte *compr; - uLong *comprLen; -{ - z_stream c_stream; /* compression stream */ - int err; - uInt len = (uInt)strlen(hello)+1; - - c_stream.zalloc = (alloc_func)0; - c_stream.zfree = (free_func)0; - c_stream.opaque = (voidpf)0; - - err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); - CHECK_ERR(err, "deflateInit"); - - c_stream.next_in = (Bytef*)hello; - c_stream.next_out = compr; - c_stream.avail_in = 3; - c_stream.avail_out = (uInt)*comprLen; - err = deflate(&c_stream, Z_FULL_FLUSH); - CHECK_ERR(err, "deflate"); - - compr[3]++; /* force an error in first compressed block */ - c_stream.avail_in = len - 3; - - err = deflate(&c_stream, Z_FINISH); - if (err != Z_STREAM_END) { - CHECK_ERR(err, "deflate"); - } - err = deflateEnd(&c_stream); - CHECK_ERR(err, "deflateEnd"); - - *comprLen = c_stream.total_out; -} - -/* =========================================================================== - * Test inflateSync() - */ -void test_sync(compr, comprLen, uncompr, uncomprLen) - Byte *compr, *uncompr; - uLong comprLen, uncomprLen; -{ - int err; - z_stream d_stream; /* decompression stream */ - - strcpy((char*)uncompr, "garbage"); - - d_stream.zalloc = (alloc_func)0; - d_stream.zfree = (free_func)0; - d_stream.opaque = (voidpf)0; - - d_stream.next_in = compr; - d_stream.avail_in = 2; /* just read the zlib header */ - - err = inflateInit(&d_stream); - CHECK_ERR(err, "inflateInit"); - - d_stream.next_out = uncompr; - d_stream.avail_out = (uInt)uncomprLen; - - inflate(&d_stream, Z_NO_FLUSH); - CHECK_ERR(err, "inflate"); - - d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */ - err = inflateSync(&d_stream); /* but skip the damaged part */ - CHECK_ERR(err, "inflateSync"); - - err = inflate(&d_stream, Z_FINISH); - if (err != Z_DATA_ERROR) { - fprintf(stderr, "inflate should report DATA_ERROR\n"); - /* Because of incorrect adler32 */ - exit(1); - } - err = inflateEnd(&d_stream); - CHECK_ERR(err, "inflateEnd"); - - printf("after inflateSync(): hel%s\n", (char *)uncompr); -} - -/* =========================================================================== - * Test deflate() with preset dictionary - */ -void test_dict_deflate(compr, comprLen) - Byte *compr; - uLong comprLen; -{ - z_stream c_stream; /* compression stream */ - int err; - - c_stream.zalloc = (alloc_func)0; - c_stream.zfree = (free_func)0; - c_stream.opaque = (voidpf)0; - - err = deflateInit(&c_stream, Z_BEST_COMPRESSION); - CHECK_ERR(err, "deflateInit"); - - err = deflateSetDictionary(&c_stream, - (const Bytef*)dictionary, sizeof(dictionary)); - CHECK_ERR(err, "deflateSetDictionary"); - - dictId = c_stream.adler; - c_stream.next_out = compr; - c_stream.avail_out = (uInt)comprLen; - - c_stream.next_in = (Bytef*)hello; - c_stream.avail_in = (uInt)strlen(hello)+1; - - err = deflate(&c_stream, Z_FINISH); - if (err != Z_STREAM_END) { - fprintf(stderr, "deflate should report Z_STREAM_END\n"); - exit(1); - } - err = deflateEnd(&c_stream); - CHECK_ERR(err, "deflateEnd"); -} - -/* =========================================================================== - * Test inflate() with a preset dictionary - */ -void test_dict_inflate(compr, comprLen, uncompr, uncomprLen) - Byte *compr, *uncompr; - uLong comprLen, uncomprLen; -{ - int err; - z_stream d_stream; /* decompression stream */ - - strcpy((char*)uncompr, "garbage"); - - d_stream.zalloc = (alloc_func)0; - d_stream.zfree = (free_func)0; - d_stream.opaque = (voidpf)0; - - d_stream.next_in = compr; - d_stream.avail_in = (uInt)comprLen; - - err = inflateInit(&d_stream); - CHECK_ERR(err, "inflateInit"); - - d_stream.next_out = uncompr; - d_stream.avail_out = (uInt)uncomprLen; - - for (;;) { - err = inflate(&d_stream, Z_NO_FLUSH); - if (err == Z_STREAM_END) break; - if (err == Z_NEED_DICT) { - if (d_stream.adler != dictId) { - fprintf(stderr, "unexpected dictionary"); - exit(1); - } - err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary, - sizeof(dictionary)); - } - CHECK_ERR(err, "inflate with dict"); - } - - err = inflateEnd(&d_stream); - CHECK_ERR(err, "inflateEnd"); - - if (strcmp((char*)uncompr, hello)) { - fprintf(stderr, "bad inflate with dict\n"); - exit(1); - } else { - printf("inflate with dictionary: %s\n", (char *)uncompr); - } -} - -/* =========================================================================== - * Usage: example [output.gz [input.gz]] - */ - -int main(argc, argv) - int argc; - char *argv[]; -{ - Byte *compr, *uncompr; - uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */ - uLong uncomprLen = comprLen; - static const char* myVersion = ZLIB_VERSION; - - if (zlibVersion()[0] != myVersion[0]) { - fprintf(stderr, "incompatible zlib version\n"); - exit(1); - - } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) { - fprintf(stderr, "warning: different zlib version\n"); - } - - printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n", - ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags()); - - compr = (Byte*)calloc((uInt)comprLen, 1); - uncompr = (Byte*)calloc((uInt)uncomprLen, 1); - /* compr and uncompr are cleared to avoid reading uninitialized - * data and to ensure that uncompr compresses well. - */ - if (compr == Z_NULL || uncompr == Z_NULL) { - printf("out of memory\n"); - exit(1); - } - test_compress(compr, comprLen, uncompr, uncomprLen); - - test_gzio((argc > 1 ? argv[1] : TESTFILE), - uncompr, uncomprLen); - - test_deflate(compr, comprLen); - test_inflate(compr, comprLen, uncompr, uncomprLen); - - test_large_deflate(compr, comprLen, uncompr, uncomprLen); - test_large_inflate(compr, comprLen, uncompr, uncomprLen); - - test_flush(compr, &comprLen); - test_sync(compr, comprLen, uncompr, uncomprLen); - comprLen = uncomprLen; - - test_dict_deflate(compr, comprLen); - test_dict_inflate(compr, comprLen, uncompr, uncomprLen); - - free(compr); - free(uncompr); - - return 0; -} -- cgit v1.2.3 From 47979206defa9429458e419b691138ab1b519833 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 9 Jan 2014 12:50:28 +0100 Subject: Fix issues with new versioning --- erts/doc/src/erlang.xml | 7 +++++++ erts/emulator/Makefile.in | 2 +- erts/emulator/beam/erl_bif_info.c | 25 ++++++++++++++++++++++++- erts/emulator/test/scheduler_SUITE.erl | 2 +- erts/emulator/utils/make_version | 4 ++++ erts/etc/common/erlexec.c | 30 +----------------------------- erts/preloaded/ebin/erlang.beam | Bin 97872 -> 97916 bytes erts/preloaded/src/erlang.erl | 1 + erts/vsn.mk | 6 +++++- 9 files changed, 44 insertions(+), 33 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 711473afd2..124302a2cb 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6008,6 +6008,13 @@ ok erlang:system_info(multi_scheduling), and erlang:system_info(schedulers).

+ otp_correction_package + +

Returns a string containing the OTP correction package version + number that currenly executing VM is part of. Note that other + OTP applications in the system may be part of other OTP correction + packages.

+
otp_release

Returns a string containing the OTP release number.

diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 5638683f88..b270099566 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -575,7 +575,7 @@ GENERATE += $(TTF_DIR)/erl_alloc_types.h # version include file $(TARGET)/erl_version.h: ../vsn.mk - $(gen_verbose)LANG=C $(PERL) utils/make_version -o $@ $(SYSTEM_VSN) $(VSN)$(SERIALNO) $(TARGET) + $(gen_verbose)LANG=C $(PERL) utils/make_version -o $@ $(SYSTEM_VSN) $(SYSTEM_CP_VSN) $(VSN)$(SERIALNO) $(TARGET) GENERATE += $(TARGET)/erl_version.h # driver table diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 414ae2f046..e0b654cb22 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -64,8 +64,10 @@ static Export *gather_gc_info_res_trap; #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) +static char otp_correction_package[] = ERLANG_OTP_CORRECTION_PACKAGE; /* Keep erts_system_version as a global variable for easy access from a core */ static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE + "%s" " [erts-" ERLANG_VERSION "]" #if !HEAP_ON_C_STACK && !HALFWORD_HEAP " [no-c-stack-objects]" @@ -304,11 +306,28 @@ make_link_list(Process *p, ErtsLink *root, Eterm tail) int erts_print_system_version(int to, void *arg, Process *c_p) { + int i, rc = -1; + char *rc_str = ""; + char rc_buf[100]; + char *ocp = otp_correction_package; #ifdef ERTS_SMP Uint total, online, active; (void) erts_schedulers_state(&total, &online, &active, 0); #endif - return erts_print(to, arg, erts_system_version + for (i = 0; i < sizeof(otp_correction_package)-4; i++) { + if (ocp[i] == '-' && ocp[i+1] == 'r' && ocp[i+2] == 'c') + rc = atoi(&ocp[i+3]); + } + if (rc >= 0) { + if (rc == 0) + rc_str = " [DEVELOPMENT]"; + else { + erts_snprintf(rc_buf, sizeof(rc_buf), " [RELEASE CANDIDATE %d]", rc); + rc_str = rc_buf; + } + } + return erts_print(to, arg, erts_system_version, + rc_str #ifdef ERTS_SMP , total, online #endif @@ -2417,6 +2436,10 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) DECL_AM(unknown); BIF_RET(AM_unknown); } + } else if (ERTS_IS_ATOM_STR("otp_correction_package", BIF_ARG_1)) { + int n = sizeof(ERLANG_OTP_CORRECTION_PACKAGE)-1; + hp = HAlloc(BIF_P, 2*n); + BIF_RET(buf_to_intlist(&hp, ERLANG_OTP_CORRECTION_PACKAGE, n, NIL)); } else if (ERTS_IS_ATOM_STR("otp_release", BIF_ARG_1)) { int n = sizeof(ERLANG_OTP_RELEASE)-1; hp = HAlloc(BIF_P, 2*n); diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 81539faa09..6a43e2b0e7 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -1495,7 +1495,7 @@ mcall(Node, Funs) -> end, Refs). erl_rel_flag_var() -> - "ERL_"++erlang:system_info(otp_release)++"_FLAGS". + "ERL_OTP"++erlang:system_info(otp_release)++"_FLAGS". clear_erl_rel_flags() -> EnvVar = erl_rel_flag_var(), diff --git a/erts/emulator/utils/make_version b/erts/emulator/utils/make_version index 7757fa8138..02b68f2b39 100755 --- a/erts/emulator/utils/make_version +++ b/erts/emulator/utils/make_version @@ -41,6 +41,9 @@ if ($ARGV[0] eq '-o') { my $release = shift; defined $release or die "No release specified"; +my $correction_package = shift; +defined $correction_package or die "No correction package specified"; + my $version = shift; defined $version or die "No version name specified"; @@ -53,6 +56,7 @@ open(FILE, ">$outputfile") or die "Can't create $outputfile: $!"; print FILE <_FLAGS */ - int ix = 0; - char *otp_p; - char otp[] = OTP_SYSTEM_VERSION; - - bufp[ix++] = 'E'; - bufp[ix++] = 'R'; - bufp[ix++] = 'L'; - bufp[ix++] = '_'; - bufp[ix++] = 'O'; - bufp[ix++] = 'T'; - bufp[ix++] = 'P'; - for (otp_p = &otp[0]; '0' <= *otp_p && *otp_p <= '9'; otp_p++) - bufp[ix++] = *otp_p; - bufp[ix++] = '_'; - bufp[ix++] = 'F'; - bufp[ix++] = 'L'; - bufp[ix++] = 'A'; - bufp[ix++] = 'G'; - bufp[ix++] = 'S'; - bufp[ix] = '\0'; -} - static void initial_argv_massage(int *argc, char ***argv) { - char erl_otp_flags_buf[] = "ERL_OTP" OTP_SYSTEM_VERSION "_FLAGS"; argv_buf ab = {0}, xab = {0}; int ix, vix, ac; char **av; @@ -2016,8 +1989,7 @@ initial_argv_massage(int *argc, char ***argv) vix = 0; - write_erl_otp_flags(erl_otp_flags_buf); - av = build_args_from_env(erl_otp_flags_buf); + av = build_args_from_env("ERL_OTP" OTP_SYSTEM_VERSION "_FLAGS"); if (av) avv[vix++].argv = av; diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 73fac27161..3c77d6ae0f 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 0ed677c3d8..f99d5bfdd0 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2246,6 +2246,7 @@ tuple_to_list(_Tuple) -> (modified_timing_level) -> integer() | undefined; (multi_scheduling) -> disabled | blocked | enabled; (multi_scheduling_blockers) -> [PID :: pid()]; + (otp_correction_package) -> string(); (otp_release) -> string(); (port_count) -> non_neg_integer(); (port_limit) -> pos_integer(); diff --git a/erts/vsn.mk b/erts/vsn.mk index 30aa870144..8e77a9a26e 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,11 @@ # VSN = 6.0 -SYSTEM_VSN = 17.0-rc0 + +# OTP major version +SYSTEM_VSN = 17 +# OTP correction package version +SYSTEM_CP_VSN = 17.0-rc0 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From cdf4475d519ee146785a9a1f02744b0229965769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 9 Jan 2014 14:53:16 +0100 Subject: erts: Update etp-commands with heap-dump --- erts/etc/unix/etp-commands.in | 343 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 340 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 73887931cc..8520d58f47 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -652,7 +652,7 @@ end define etp-ct-atom-1 # Args: int # -# Determines if integer is a atom first character +# Determines if integer is an atom first character # # Non-reentrant # Returns: $etp_ct_atom @@ -1278,6 +1278,250 @@ document etpf-stackdump %--------------------------------------------------------------------------- end +define etp-heapdump +# Args: Process* +# +# Non-reentrant + etp-heapdump-1 ($arg0)->heap ($arg0)->htop +end + +document etp-heapdump +%--------------------------------------------------------------------------- +% etp-heapdump Process* +% +% Take an Process* and print a heapdump for the process heap. +%--------------------------------------------------------------------------- +end + +define etp-heapdump-old +# Args: Process* +# +# Non-reentrant + etp-heapdump-1 ($arg0)->old_heap ($arg0)->old_htop +end + +document etp-heapdump +%--------------------------------------------------------------------------- +% etp-heapdump-old Process* +% +% Take an Process* and print a heapdump for the process old heap (gen-heap). +%--------------------------------------------------------------------------- +end + + +define etp-heapdump-1 +# Args: Eterm* heap, Eterm* htop +# +# Non-reentrant + set $etp_heapdump_heap = (Eterm*)($arg0) + set $etp_heapdump_p = (Eterm*)($arg0) + set $etp_heapdump_end = (Eterm*)($arg1) + set $etp_heapdump_skips = 0 + printf "%% heapdump (%u):\n", $etp_heapdump_end-$etp_heapdump_p + while $etp_heapdump_p < $etp_heapdump_end + set $etp_heapdump_ix = 0 + printf " %p: ", $etp_heapdump_p + while $etp_heapdump_p < $etp_heapdump_end && $etp_heapdump_ix < 8 + if ($etp_heapdump_skips > 0) + printf "| 0x%08x ", ($etp_heapdump_p) + set $etp_heapdump_skips-- + else + etp-term-dump $etp_heapdump_p[0] + end + set $etp_heapdump_p++ + set $etp_heapdump_ix++ + end + printf "\n" + end +end + + +define etp-term-dump +# Args: Eterm + if (($arg0) & 0x3) == 0 + etp-term-dump-header ($arg0) + else + if (($arg0) & 0x3) == 1 + # Cons pointer + set $etp_term_dump_cons_p = ((Eterm*)(($arg0) & ~0x3)) + if $etp_term_dump_cons_p > $etp_heapdump_heap && $etp_term_dump_cons_p < $etp_heapdump_end + printf "| C:0x%08x ", $etp_term_dump_cons_p + #printf "| C: --> %5d ", $etp_heapdump_p - $etp_term_dump_cons_p - 1 + else + printf "| C:0x%08x ", $etp_term_dump_cons_p + end + else + if (($arg0) & 0x3) == 2 + # Box pointer + printf "| B:0x%08x ", ($arg0) + else + if (($arg0) & 0x3) == 3 + # immediate + etp-term-dump-immediate ($arg0) + else + printf "| U:0x%08x ", ($arg0) + end + end + end + end +end + +define etp-term-dump-immediate +# Args: immediate term + if (($arg0) & 0xF) == 0xf + # Fixnum + etp-ct-printable-1 ((long)((Sint)($arg0)>>4)) + if $etp_ct_printable + if $etp_ct_printable < 0 + printf "| I: %c (%3ld) ", (long)((Sint)($arg0)>>4), (long)((Sint)($arg0)>>4) + else + printf "| I: \\%c (%3ld) ", (long)((Sint)($arg0)>>4), (long)((Sint)($arg0)>>4) + end + else + printf "| I:%10ld ", (long)((Sint)($arg0)>>4) + end + else + if (($arg0) & 0xF) == 0x3 + etp-term-dump-pid ($arg0) + else + if (($arg0) & 0xF) == 0x7 + printf "| port:0x%05x ", ($arg0) + else + # Immediate2 - 0xB + if (($arg0) & 0x3f) == 0x0b + etp-term-dump-atom ($arg0) + else + if (($arg0) & 0x3f) == 0x1b + printf "| #Catch<%06d> ", ($arg0)>>6 + else + if (($arg0) == $etp_nil) + printf "| [] (NIL) " + else + printf "| I:0x%08x ", ($arg0) + end + end + end + end + end + end +end + +define etp-term-dump-atom +# Args: atom term + set $etp_atom_1_ap = (Atom*)erts_atom_table.seg_table[(Eterm)($arg0)>>16][((Eterm)($arg0)>>6)&0x3FF] + set $etp_atom_1_i = ($etp_atom_1_ap)->len + set $etp_atom_1_p = ($etp_atom_1_ap)->name + set $etp_atom_1_quote = 1 + set $etp_atom_indent = 13 + + if ($etp_atom_1_i < 11) + if ($etp_atom_1_i > 0) + etp-ct-atom-1 (*$etp_atom_1_p) + if $etp_ct_atom + set $etp_atom_indent = 13 + else + set $etp_atom_indent = 11 + end + end + # perform indentation + printf "|" + while ($etp_atom_1_i < $etp_atom_indent) + printf " " + set $etp_atom_1_i++ + end + set $etp_atom_1_i = ($etp_atom_1_ap)->len + # Check if atom has to be quoted + if ($etp_atom_1_i > 0) + etp-ct-atom-1 (*$etp_atom_1_p) + if $etp_ct_atom + # Atom start character + set $etp_atom_1_p++ + set $etp_atom_1_i-- + set $etp_atom_1_quote = 0 + else + set $etp_atom_1_i = 0 + end + end + while $etp_atom_1_i > 0 + etp-ct-name-1 (*$etp_atom_1_p) + if $etp_ct_name + # Name character + set $etp_atom_1_p++ + set $etp_atom_1_i-- + else + set $etp_atom_1_quote = 1 + set $etp_atom_1_i = 0 + end + end + # Print the atom + if $etp_atom_1_quote + printf "'" + end + set $etp_atom_1_i = ($etp_atom_1_ap)->len + set $etp_atom_1_p = ($etp_atom_1_ap)->name + while $etp_atom_1_i > 0 + etp-char-1 (*$etp_atom_1_p) '\'' + set $etp_atom_1_p++ + set $etp_atom_1_i-- + end + if $etp_atom_1_quote + printf "'" + end + printf " " + else + printf "| A:0x%08x ", ($arg0) + end +end + +define etp-term-dump-pid +# Args: Eterm pid +# +# Non-reentrant +# + set $etp_pid_1 = (Eterm)($arg0) + if ($etp_pid_1 & 0xF) == 0x3 + if (etp_arch_bits == 64 && etp_halfword == 0) + if (etp_big_endian) + set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 36) & 0x0fffffff) + else + set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 4) & 0x0fffffff) + end + else + set $etp_pid_data = (unsigned) (((((Uint32) $etp_pid_1) >> 4) & ~erts_proc.r.o.pix_mask) | ((((Uint32) $etp_pid_1) >> (erts_proc.r.o.pix_cl_shift + 4)) & erts_proc.r.o.pix_cl_mask) | (((((Uint32) $etp_pid_1) >> 4) & erts_proc.r.o.pix_cli_mask) << erts_proc.r.o.pix_cli_shift)) + end + # Internal pid + printf "| <0.%04u.%03u> ", $etp_pid_data & 0x7fff, ($etp_pid_data >> 15) & 0x1fff + else + printf "| #NotPid<%#x> ", ($arg0) + end +end + +define etp-term-dump-header +# Args: Header term + if (($arg0) & 0x3f) == 0 + printf "| H:%4d-tuple ", ($arg0) >> 6 + else + set $etp_heapdump_skips = ($arg0) >> 6 + if ((($arg0) & 0x3f) == 0x18) + printf "| H: float %3d ", ($arg0) >> 6 + else + if ((($arg0) & 0x3f) == 0x28) + # sub-binary + printf "| H: sub-bin " + else + if ((($arg0) & 0x3f) == 0x8) + # pos-bignum + printf "| H:bignum %3u ", ($arg0) >> 6 + else + printf "| header %5d ", ($arg0) >> 6 + end + end + end + end +end + + + define etp-pid2pix-1 # Args: Eterm # @@ -1445,7 +1689,7 @@ define etp-process-info # Args: Process* # printf " Pid: " - etp-1 $arg0->common.id + etp-1 ($arg0)->common.id printf "\n State: " etp-proc-state $arg0 if $proxy_process != 0 @@ -1523,11 +1767,104 @@ end document etp-processes %--------------------------------------------------------------------------- % etp-processes -% +% % Print misc info about all processes %--------------------------------------------------------------------------- end +define etp-processes-memory + if (!erts_initialized) + printf "No processes, since system isn't initialized!\n" + else + set $proc_ix = 0 + printf "--- (%ld processes in wheel)\n", erts_proc.r.o.max + while $proc_ix < erts_proc.r.o.max + set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$proc_ix]) + if ($proc != ((Process *) 0) && $proc != &erts_invalid_process) + etp-process-memory-info $proc + end + set $proc_ix++ + end + printf "---\n", + end +end + +document etp-processes-memory +%--------------------------------------------------------------------------- +% etp-processes-memory +% +% Print memory info about all processes +%--------------------------------------------------------------------------- +end + +define etp-process-memory-info +# Args: Process* +# + if ((*(((Uint32 *) &(((Process *) $arg0)->state)))) & 0x400000) + set $proxy_process = 1 + else + set $proxy_process = 0 + end + printf " " + etp-1 $arg0->common.id + printf ": (Process *) %p ", $arg0 + if $proxy_process != 0 + printf "(Process *) %p ", $arg0 + printf " *** PROXY process struct *** refer to next: \n" + etp-pid2proc-1 $arg0->common.id + printf " -" + etp-process-memory-info $proc + else + printf " [Heap: %5ld", $arg0->heap_sz + if ($arg0->old_heap) + printf " | %5ld", $arg0->old_hend - $arg0->old_heap + else + printf " | none " + end + printf "] [Mbuf: %5ld", $arg0->mbuf_sz + if (etp_smp_compiled) + printf " | %3ld (%3ld | %3ld)", ($arg0->msg.len + $arg0->msg_inq.len), $arg0->msg.len, $arg0->msg_inq.len + else + printf " | %3ld", $arg0->msg.len + end + printf "] " + if ($arg0->i) + printf " I: " + etp-cp-1 $arg0->i + printf " " + end + + if ($arg0->current) + etp-1 $arg0->current[0] + printf ":" + etp-1 $arg0->current[1] + printf "/%d ", $arg0->current[2] + end + + if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0 + if ($arg0->common.u.alive.reg) + etp-1 $arg0->common.u.alive.reg->name + printf " " + end + end + + if ($arg0->cp) + printf " CP: " + etp-cp-1 $arg0->cp + printf " " + end + printf "\n" + end +end + +document etp-process-memory-info +%--------------------------------------------------------------------------- +% etp-process-memory-info Process* +% +% Print memory info about process +%--------------------------------------------------------------------------- +end + define etp-port-id2pix-1 # Args: Eterm # -- cgit v1.2.3 From 79355961135bb107a01b0a7e4668818f58327d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 9 Jan 2014 18:46:36 +0100 Subject: erts: Increase gc tenure rate The garbage collector tries to maintain the previous heap block size during a minor gc, i.e. 'need' is not utilized in determining the size of the new heap, instead it relies on tenure and garbage to be sufficiently large. In instances during intense growing with exlusively live data on the heap coupled with delayed tenure, fullsweeps would be triggered directly after a minor gc to make room for 'need' since the new heap would be full. To remedy this, the tenure of terms on the minor heap will always happen (if it is below the high watermark) instead of every other minor gc. --- erts/emulator/beam/erl_gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index c5585d39e8..fd86c658d6 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1157,7 +1157,7 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) old_htop = sweep_one_area(OLD_HTOP(p), old_htop, heap, heap_size); } OLD_HTOP(p) = old_htop; - HIGH_WATER(p) = (HEAP_START(p) != HIGH_WATER(p)) ? n_heap : n_htop; + HIGH_WATER(p) = n_htop; if (MSO(p).first) { sweep_off_heap(p, 0); -- cgit v1.2.3 From 8c10604c7d278c28b32feb67c2c8cf207f5bd305 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Mon, 4 Nov 2013 15:07:30 +0100 Subject: Add support for the separate tinfo library from ncurses Recent versions of ncurses can be built with terminfo symbols moved into the separate tinfo library. This patch prevents erts configure from dying by adding the tinfo library to list of termcap lib candidates. --- erts/configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 3b4c46d4a5..cf21d0cbfc 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1336,7 +1336,7 @@ TERMCAP_LIB= if test "x$with_termcap" != "xno" && test "X$host" != "Xwin32"; then # try these libs - termcap_libs="ncurses curses termcap termlib" + termcap_libs="tinfo ncurses curses termcap termlib" for termcap_lib in $termcap_libs; do AC_CHECK_LIB($termcap_lib, tgetent, TERMCAP_LIB="-l$termcap_lib") -- cgit v1.2.3 From 9bb39214db2de93142976de301427969aa2f9206 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 10 Jan 2014 15:23:06 +0100 Subject: erts: Replace tab with space for proper alignment --- erts/emulator/beam/erl_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 8c4fffa75b..1af80dd04b 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -553,8 +553,8 @@ void erts_usage(void) erts_fprintf(stderr, " numbers is %d\n", ERTS_MAX_NO_OF_SCHEDULERS); erts_fprintf(stderr, "-SP p1:p2 specify schedulers (p1) and schedulers online (p2)\n"); - erts_fprintf(stderr, " as percentages of logical processors configured and logical\n"); - erts_fprintf(stderr, " processors available, respectively\n"); + erts_fprintf(stderr, " as percentages of logical processors configured and logical\n"); + erts_fprintf(stderr, " processors available, respectively\n"); erts_fprintf(stderr, "-t size set the maximum number of atoms the " "emulator can handle\n"); erts_fprintf(stderr, " valid range is [%d-%d]\n", -- cgit v1.2.3 From b49efb826e191a0ef18da7ebdb3dd9e56fe654da Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 10 Jan 2014 15:41:48 +0100 Subject: erts: Simplify term_to_binary by removing saved ESTACK from root set We disabled GC (in 522a29666088d5) during trapping and don't need to include the saved ESTACK as part of root set. --- erts/emulator/beam/erl_alloc.types | 4 +- erts/emulator/beam/external.c | 219 ++++++++++++++++++------------------- 2 files changed, 106 insertions(+), 117 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 32308fae9b..b4e52770e3 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2013. All Rights Reserved. +# Copyright Ericsson AB 2003-2014. All Rights Reserved. # # The 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 @@ type LINK_LH STANDARD PROCESSES link_lh type SUSPEND_MON STANDARD PROCESSES suspend_monitor type PEND_SUSPEND SHORT_LIVED PROCESSES pending_suspend type PROC_LIST SHORT_LIVED PROCESSES proc_list -type EXTRA_ROOT SHORT_LIVED PROCESSES extra_root +type SAVED_ESTACK SHORT_LIVED PROCESSES saved_estack type FUN_ENTRY LONG_LIVED CODE fun_entry type ATOM_TXT LONG_LIVED ATOM atom_text type BEAM_REGISTER EHEAP PROCESSES beam_register diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 2cb44a5b64..50831e848e 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The 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,7 +87,8 @@ static Export term_to_binary_trap_export; static byte* enc_term(ErtsAtomCacheMap *, Eterm, byte*, Uint32, struct erl_off_heap_header** off_heap); -static int enc_term_int(Process *p,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags, +struct TTBEncodeContext_; +static int enc_term_int(struct TTBEncodeContext_*,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags, struct erl_off_heap_header** off_heap, Sint *reds, byte **res); static Uint is_external_string(Eterm obj, int* p_is_string); static byte* enc_atom(ErtsAtomCacheMap *, Eterm, byte*, Uint32); @@ -103,7 +104,8 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla Binary *context_b); static Uint encode_size_struct2(ErtsAtomCacheMap *, Eterm, unsigned); -static int encode_size_struct_int(Process *p, ErtsAtomCacheMap *acmp, Eterm obj, +struct TTBSizeContext_; +static int encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags, Sint *reds, Uint *res); static Export binary_to_term_trap_export; @@ -1086,7 +1088,6 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2) int level = 0; Uint flags = TERM_TO_BINARY_DFLAGS; Eterm res; - Binary *bin = NULL; while (is_list(Flags)) { Eterm arg = CAR(list_val(Flags)); @@ -1123,7 +1124,7 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2) goto error; } - res = erts_term_to_binary_int(p, Term, level, flags, bin); + res = erts_term_to_binary_int(p, Term, level, flags, NULL); if (is_tuple(res)) { erts_set_gc_state(p, 0); BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res); @@ -1726,14 +1727,22 @@ erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags) { typedef enum { TTBSize, TTBEncode, TTBCompress } TTBState; -typedef struct { +typedef struct TTBSizeContext_ { Uint flags; int level; + Uint result; + Eterm obj; + UWord* stack; + Uint stack_sz; } TTBSizeContext; -typedef struct { +typedef struct TTBEncodeContext_ { Uint flags; int level; + byte* ep; + Eterm obj; + UWord* stack; + Uint stack_sz; Binary *result_bin; } TTBEncodeContext; @@ -1763,8 +1772,16 @@ static void ttb_context_destructor(Binary *context_bin) context->alive = 0; switch (context->state) { case TTBSize: + if (context->s.sc.stack) { + erts_free(ERTS_ALC_T_SAVED_ESTACK, context->s.sc.stack); + context->s.sc.stack = NULL; + } break; case TTBEncode: + if (context->s.ec.stack) { + erts_free(ERTS_ALC_T_SAVED_ESTACK, context->s.ec.stack); + context->s.ec.stack = NULL; + } if (context->s.ec.result_bin != NULL) { /* Set to NULL if ever made alive! */ ASSERT(erts_refc_read(&(context->s.ec.result_bin->refc),0) == 0); erts_bin_free(context->s.ec.result_bin); @@ -1829,6 +1846,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla /* Setup enough to get started */ context->state = TTBSize; context->alive = 1; + context->s.sc.stack = NULL; context->s.sc.flags = flags; context->s.sc.level = level; } else { @@ -1844,7 +1862,8 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla int level; Uint flags; /* Try for fast path */ - if (encode_size_struct_int(p, NULL, Term, context->s.sc.flags, &reds, &size) < 0) { + if (encode_size_struct_int(&context->s.sc, NULL, Term, + context->s.sc.flags, &reds, &size) < 0) { EXPORT_CONTEXT(); /* Same state */ RETURN_STATE(); @@ -1870,6 +1889,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla context->state = TTBEncode; context->s.ec.flags = flags; context->s.ec.level = level; + context->s.ec.stack = NULL; context->s.ec.result_bin = result_bin; break; } @@ -1881,7 +1901,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla Binary *result_bin; flags = context->s.ec.flags; - if (enc_term_int(p,NULL,Term, bytes+1, flags, NULL, &reds, &endp) < 0) { + if (enc_term_int(&context->s.ec, NULL,Term, bytes+1, flags, NULL, &reds, &endp) < 0) { EXPORT_CONTEXT(); RETURN_STATE(); } @@ -2289,27 +2309,6 @@ dec_pid(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, Ete #define ENC_PATCH_FUN_SIZE ((Eterm) 2) #define ENC_LAST_ARRAY_ELEMENT ((Eterm) 3) -/* Free extra rootset (used when trapping) */ -static void cleanup_ttb_extra_root(ErlExtraRootSet *rs) -{ - if (rs->objv != NULL) { - erts_free(ERTS_ALC_T_EXTRA_ROOT, rs->objv); - } - erts_free(ERTS_ALC_T_EXTRA_ROOT, rs); -} - -/* Same as above, but we have an extra "stack" beyond GC reach, i.e. an array of two extra roots */ -static void cleanup_ttb_extra_root_2(ErlExtraRootSet *rs) -{ - if (rs->objv != NULL) { - erts_free(ERTS_ALC_T_EXTRA_ROOT, rs->objv); - } - if (rs[1].objv != NULL) { - erts_free(ERTS_ALC_T_EXTRA_ROOT, rs[1].objv); - } - - erts_free(ERTS_ALC_T_EXTRA_ROOT, rs); -} static byte* enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags, @@ -2321,39 +2320,43 @@ enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags, } static int -enc_term_int(Process *p,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags, +enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags, struct erl_off_heap_header** off_heap, Sint *reds, byte **res) { - DECLARE_ESTACK(s); - DECLARE_WSTACK(com); + DECLARE_WSTACK(s); Uint n; Uint i; Uint j; Uint* ptr; Eterm val; FloatDef f; - int count_reds = (p != NULL && reds != NULL); Sint r = 0; +#if HALFWORD_HEAP + UWord wobj; +#endif - if (count_reds) { - ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_EXTRA_ROOT); - WSTACK_CHANGE_ALLOCATOR(com, ERTS_ALC_T_EXTRA_ROOT); + + if (ctx) { + WSTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); r = *reds; - } - if (p && p->extra_root) { /* restore saved stacks and byte pointer */ - ESTACK_RESTORE(s,p->extra_root[0].objv, p->extra_root[0].sz); - obj = ESTACK_POP(s); - WSTACK_RESTORE(com, p->extra_root[1].objv, p->extra_root[1].sz); - ep = (byte *) WSTACK_POP(com); + if (ctx->stack) { /* restore saved stacks and byte pointer */ + WSTACK_RESTORE(s, ctx->stack, ctx->stack_sz); + ep = ctx->ep; + obj = ctx->obj; + } } goto L_jump_start; outer_loop: - while (!ESTACK_ISEMPTY(s)) { - obj = ESTACK_POP(s); - switch (val = WSTACK_POP(com)) { + while (!WSTACK_ISEMPTY(s)) { +#if HALFWORD_HEAP + obj = (Eterm) (wobj = WSTACK_POP(s)); +#else + obj = WSTACK_POP(s); +#endif + switch (val = WSTACK_POP(s)) { case ENC_TERM: break; case ENC_ONE_CONS: @@ -2364,55 +2367,52 @@ enc_term_int(Process *p,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dfla obj = CAR(cons); tl = CDR(cons); - WSTACK_PUSH(com, is_list(tl) ? ENC_ONE_CONS : ENC_TERM); - ESTACK_PUSH(s, tl); + WSTACK_PUSH(s, is_list(tl) ? ENC_ONE_CONS : ENC_TERM); + WSTACK_PUSH(s, tl); } break; case ENC_PATCH_FUN_SIZE: - /* obj will be discarded, it was NIL */ { - byte* size_p = (byte *) WSTACK_POP(com); +#if HALFWORD_HEAP + byte* size_p = (byte *) wobj; +#else + byte* size_p = (byte *) obj; +#endif put_int32(ep - size_p, size_p); } goto outer_loop; case ENC_LAST_ARRAY_ELEMENT: /* obj is the tuple */ { - Eterm* ptr = tuple_val(obj); - i = arityval(*ptr); - obj = ptr[i]; +#if HALFWORD_HEAP + Eterm* ptr = (Eterm *) wobj; +#else + Eterm* ptr = (Eterm *) obj; +#endif + obj = *ptr; } break; default: /* ENC_LAST_ARRAY_ELEMENT+1 and upwards */ { - Eterm* ptr = tuple_val(obj); - i = arityval(*ptr); - ESTACK_PUSH(s, obj); /* put back tuple and next element index */ - WSTACK_PUSH(com, val-1); - obj = ptr[i - (val - ENC_LAST_ARRAY_ELEMENT)]; /* the index is counting down */ +#if HALFWORD_HEAP + Eterm* ptr = (Eterm *) wobj; +#else + Eterm* ptr = (Eterm *) obj; +#endif + WSTACK_PUSH(s, val-1); + obj = *ptr++; + WSTACK_PUSH(s, (UWord)ptr); } break; } L_jump_start: - if (count_reds && --r == 0) { + if (ctx && --r == 0) { *reds = r; - ESTACK_PUSH(s,obj); /* push back current object, to be popped on restore */ - WSTACK_PUSH(com,((UWord) ep)); - if (p->extra_root == NULL) { - /* NB. Allocate an array of two "extra-roots", of which only the first element - is seen and handled by the GC. Index 1 holds the Wstack. */ - p->extra_root = erts_alloc(ERTS_ALC_T_EXTRA_ROOT, sizeof(ErlExtraRootSet)*2); - p->extra_root->objv = NULL; - p->extra_root->sz = 0; - p->extra_root->cleanup = cleanup_ttb_extra_root_2; - p->extra_root[1].objv = NULL; - p->extra_root[1].sz = 0; - p->extra_root[1].cleanup = NULL; /* Never used */ - } - ESTACK_SAVE(s, p->extra_root[0].objv, p->extra_root[0].sz); - WSTACK_SAVE(com, p->extra_root[1].objv, (p->extra_root[1].sz)); + ctx->obj = obj; + ctx->ep = ep; + WSTACK_SAVE(s, ctx->stack, ctx->stack_sz); return -1; } switch(tag_val_def(obj)) { @@ -2558,8 +2558,8 @@ enc_term_int(Process *p,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dfla ep += 4; } if (i > 0) { - WSTACK_PUSH(com, ENC_LAST_ARRAY_ELEMENT+i-1); - ESTACK_PUSH(s, obj); + WSTACK_PUSH(s, ENC_LAST_ARRAY_ELEMENT+i-1); + WSTACK_PUSH(s, (UWord)ptr); } break; @@ -2703,9 +2703,8 @@ enc_term_int(Process *p,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dfla int ei; *ep++ = NEW_FUN_EXT; - WSTACK_PUSH(com, (UWord) ep); /* Position for patching in size */ - WSTACK_PUSH(com, ENC_PATCH_FUN_SIZE); - ESTACK_PUSH(s,NIL); /* Will be thrown away */ + WSTACK_PUSH(s, ENC_PATCH_FUN_SIZE); + WSTACK_PUSH(s, (UWord) ep); /* Position for patching in size */ ep += 4; *ep = funp->arity; ep += 1; @@ -2722,8 +2721,8 @@ enc_term_int(Process *p,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dfla fun_env: for (ei = funp->num_free-1; ei > 0; ei--) { - WSTACK_PUSH(com, ENC_TERM); - ESTACK_PUSH(s, (UWord) funp->env[ei]); + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) funp->env[ei]); } if (funp->num_free != 0) { obj = funp->env[0]; @@ -2766,13 +2765,12 @@ enc_term_int(Process *p,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dfla break; } } - DESTROY_ESTACK(s); - DESTROY_WSTACK(com); - if (p && p->extra_root) { - cleanup_ttb_extra_root_2(p->extra_root); - p->extra_root = NULL; - } - if (count_reds) { + DESTROY_WSTACK(s); + if (ctx) { + if (ctx->stack) { + erts_free(ERTS_ALC_T_SAVED_ESTACK, ctx->stack); + ctx->stack = NULL; + } *reds = r; } *res = ep; @@ -3742,26 +3740,24 @@ static Uint encode_size_struct2(ErtsAtomCacheMap *acmp, Eterm obj, unsigned dfla } static int -encode_size_struct_int(Process *p, ErtsAtomCacheMap *acmp, Eterm obj, +encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags, Sint *reds, Uint *res) { DECLARE_ESTACK(s); Uint m, i, arity; Uint result = 0; - int count_reds = (p != NULL && reds != 0); Sint r = 0; - if (count_reds) { - ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_EXTRA_ROOT); + if (ctx) { + ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); r = *reds; - } - - if (p && p->extra_root) { /* restore saved stack */ - ESTACK_RESTORE(s,p->extra_root->objv, p->extra_root->sz + 1); - result = ESTACK_POP(s); /*Untagged, beyond p->extra_root->sz */ - obj = ESTACK_POP(s); - } + if (ctx->stack) { /* restore saved stack */ + ESTACK_RESTORE(s, ctx->stack, ctx->stack_sz); + result = ctx->result; + obj = ctx->obj; + } + } goto L_jump_start; @@ -3787,18 +3783,11 @@ encode_size_struct_int(Process *p, ErtsAtomCacheMap *acmp, Eterm obj, } L_jump_start: - if (count_reds && --r == 0) { + if (ctx && --r == 0) { *reds = r; - ESTACK_PUSH(s,obj); /* push back current object */ - ESTACK_PUSH(s,result); /* Untagged, will be out of GC reach */ - if (p->extra_root == NULL) { - p->extra_root = erts_alloc(ERTS_ALC_T_EXTRA_ROOT, sizeof(ErlExtraRootSet)); - p->extra_root->objv = NULL; - p->extra_root->sz = 0; - p->extra_root->cleanup = cleanup_ttb_extra_root; - } - ESTACK_SAVE(s, p->extra_root->objv, p->extra_root->sz); - --p->extra_root->sz; /* Hide result from GC */ + ctx->obj = obj; + ctx->result = result; + ESTACK_SAVE(s, ctx->stack, ctx->stack_sz); return -1; } switch (tag_val_def(obj)) { @@ -4001,11 +3990,11 @@ encode_size_struct_int(Process *p, ErtsAtomCacheMap *acmp, Eterm obj, } DESTROY_ESTACK(s); - if (p && p->extra_root) { - cleanup_ttb_extra_root(p->extra_root); - p->extra_root = NULL; - } - if (count_reds) { + if (ctx) { + if (ctx->stack) { + erts_free(ERTS_ALC_T_SAVED_ESTACK, ctx->stack); + ctx->stack = NULL; + } *reds = r; } *res = result; -- cgit v1.2.3 From f7f08e93cc1e976cb2b37d450996cde32be8aae2 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 10 Jan 2014 15:47:16 +0100 Subject: erts: Remove the extra_root feature from the process structure as we don't use it and instead have the feature to disable GC during trapping BIFs. --- erts/emulator/beam/erl_gc.c | 18 +----------------- erts/emulator/beam/erl_process.c | 9 +-------- erts/emulator/beam/erl_process.h | 11 +---------- 3 files changed, 3 insertions(+), 35 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index c5585d39e8..8ff6f9a3b9 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2013. All Rights Reserved. + * Copyright Ericsson AB 2002-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -1975,17 +1975,6 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) ++n; } - /* - * A trapping BIF can add to rootset by setting the extra_root - * in the process_structure. - */ - if (p->extra_root != NULL) { - roots[n].v = p->extra_root->objv; - roots[n].sz = p->extra_root->sz; - ++n; - } - - ASSERT((is_nil(p->seq_trace_token) || is_tuple(follow_moved(p->seq_trace_token)) || is_atom(p->seq_trace_token))); @@ -2563,11 +2552,6 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size, p->dictionary->used, offs, area, area_size); } - if (p->extra_root != NULL) { - offset_heap_ptr(p->extra_root->objv, - p->extra_root->sz, - offs, area, area_size); - } offset_heap_ptr(&p->fvalue, 1, offs, area, area_size); offset_heap_ptr(&p->ftrace, 1, offs, area, area_size); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 21fd8dd50a..9983a26688 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -8755,7 +8755,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->htop = p->heap; p->heap_sz = sz; p->catches = 0; - p->extra_root = NULL; p->bin_vheap_sz = p->min_vheap_size; p->bin_old_vheap_sz = p->min_vheap_size; @@ -10219,12 +10218,6 @@ erts_continue_exit_process(Process *p) if (pbt) erts_free(ERTS_ALC_T_BPD, (void *) pbt); - if (p->extra_root != NULL) { - (p->extra_root->cleanup)(p->extra_root); /* Should deallocate - whole structure */ - p->extra_root = NULL; - } - delete_process(p); #ifdef ERTS_SMP diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 043621125c..e35d1c785c 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -711,13 +711,6 @@ struct ErtsPendingSuspend_ { #endif -typedef struct ErlExtraRootSet_ ErlExtraRootSet; -struct ErlExtraRootSet_ { - Eterm *objv; - Uint sz; - void (*cleanup)(ErlExtraRootSet *); -}; - /* Defines to ease the change of memory architecture */ # define HEAP_START(p) (p)->heap # define HEAP_TOP(p) (p)->htop @@ -811,8 +804,6 @@ struct process { ErlMessageQueue msg; /* Message queue */ - ErlExtraRootSet *extra_root; /* Used by trapping BIF's */ - union { ErtsBifTimer *bif_timers; /* Bif timers aiming at this process */ void *terminate; -- cgit v1.2.3 From c97dd5af565567d0de3c5df33aa5dc1c3146d650 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 10 Jan 2014 15:58:30 +0100 Subject: erts: Extend binary_SUITE:ttb_trap to also cover binary_to_term and rename it 'trapping'. --- erts/emulator/test/binary_SUITE.erl | 53 +++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index bce4278337..8b5a6d08a2 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% %% The 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,10 +58,10 @@ ordering/1,unaligned_order/1,gc_test/1, bit_sized_binary_sizes/1, otp_6817/1,deep/1,obsolete_funs/1,robustness/1,otp_8117/1, - otp_8180/1, ttb_trap/1]). + otp_8180/1, trapping/1]). %% Internal exports. --export([sleeper/0,ttb_loop/2]). +-export([sleeper/0,trapping_loop/4]). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,2}}]. @@ -76,7 +76,7 @@ all() -> bad_term_to_binary, more_bad_terms, otp_5484, otp_5933, ordering, unaligned_order, gc_test, bit_sized_binary_sizes, otp_6817, otp_8117, deep, - obsolete_funs, robustness, otp_8180, ttb_trap]. + obsolete_funs, robustness, otp_8180, trapping]. groups() -> []. @@ -1334,36 +1334,49 @@ run_otp_8180(Name) -> end || Bin <- Bins], ok. -%% Test that exit and GC during term_to_binary trap does not crash. -ttb_trap(Config) when is_list(Config)-> +%% Test that exit and GC during trapping term_to_binary and binary_to_term +%% does not crash. +trapping(Config) when is_list(Config)-> case erlang:system_info(wordsize) of N when N < 8 -> {skipped, "Only on 64bit machines"}; _ -> - do_ttb_trap(5) + do_trapping(5, term_to_binary, + fun() -> [lists:duplicate(2000000,2000000)] end), + do_trapping(5, binary_to_term, + fun() -> [term_to_binary(lists:duplicate(2000000,2000000))] end) end. -do_ttb_trap(0) -> +do_trapping(0, _, _) -> ok; -do_ttb_trap(N) -> - Pid = spawn(?MODULE,ttb_loop,[1000,self()]), +do_trapping(N, Bif, ArgFun) -> + io:format("N=~p: Do ~p ~s gc.\n", [N, Bif, case N rem 2 of 0 -> "with"; 1 -> "without" end]), + Pid = spawn(?MODULE,trapping_loop,[Bif, ArgFun, 1000, self()]), receive ok -> ok end, receive after 100 -> ok end, - erlang:garbage_collect(Pid), - receive after 100 -> ok end, + Ref = make_ref(), + case N rem 2 of + 0 -> erlang:garbage_collect(Pid, [{async,Ref}]), + receive after 100 -> ok end; + 1 -> void + end, exit(Pid,kill), + case N rem 2 of + 0 -> receive {garbage_collect, Ref, _} -> ok end; + 1 -> void + end, receive after 1 -> ok end, - do_ttb_trap(N-1). + do_trapping(N-1, Bif, ArgFun). -ttb_loop(N,Pid) -> - Term = lists:duplicate(2000000,2000000), +trapping_loop(Bif, ArgFun, N, Pid) -> + Args = ArgFun(), Pid ! ok, - ttb_loop2(N,Term). -ttb_loop2(0,_T) -> + trapping_loop2(Bif,Args,N). +trapping_loop2(_,_,0) -> ok; -ttb_loop2(N,T) -> - apply(erlang,term_to_binary,[T]), - ttb_loop2(N-1,T). +trapping_loop2(Bif,Args,N) -> + apply(erlang,Bif,Args), + trapping_loop2(Bif, Args, N-1). %% Utilities. -- cgit v1.2.3 From 5737a9a1d0d2fdafe9fc34dd35e07233c70b53c4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 10 Jan 2014 16:04:06 +0100 Subject: erts: Run binary_SUITE:trapping even for 32bit Not sure why it was disabled for 32bit in the first place? --- erts/emulator/test/binary_SUITE.erl | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 8b5a6d08a2..f493f8c1ac 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1337,15 +1337,10 @@ run_otp_8180(Name) -> %% Test that exit and GC during trapping term_to_binary and binary_to_term %% does not crash. trapping(Config) when is_list(Config)-> - case erlang:system_info(wordsize) of - N when N < 8 -> - {skipped, "Only on 64bit machines"}; - _ -> - do_trapping(5, term_to_binary, - fun() -> [lists:duplicate(2000000,2000000)] end), - do_trapping(5, binary_to_term, - fun() -> [term_to_binary(lists:duplicate(2000000,2000000))] end) - end. + do_trapping(5, term_to_binary, + fun() -> [lists:duplicate(2000000,2000000)] end), + do_trapping(5, binary_to_term, + fun() -> [term_to_binary(lists:duplicate(2000000,2000000))] end). do_trapping(0, _, _) -> ok; -- cgit v1.2.3 From b9eb22b7cdb7c52b87848c486214012aabf8cf96 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 19 Dec 2013 14:10:28 +0100 Subject: erts: Make sure fds 0,1 and 2 are open If they are not open a really nasty race where the io pipe in erl_poll got fd 0 and the fd_driver setting fd 0 to blocking could occur. This caused the emulator to hang during shutdown. --- erts/emulator/sys/unix/sys.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index a7ea4b2490..77d7154313 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -547,6 +547,25 @@ erts_sys_pre_init(void) #endif #endif /* USE_THREADS */ erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0); + + { + /* + * Unfortunately we depend on fd 0,1,2 in the old shell code. + * So if for some reason we do not have those open when we start + * we have to open them here. Not doing this can cause the emulator + * to deadlock when reaping the fd_driver ports :( + */ + int fd; + /* Make sure fd 0 is open */ + if ((fd = open("/dev/null", O_RDONLY)) != 0) + close(fd); + /* Make sure fds 1 and 2 are open */ + while (fd < 3) { + fd = open("/dev/null", O_WRONLY); + } + close(fd); + } + } void -- cgit v1.2.3 From 9205b6671892e7516e8571e4ecf19bfa2ade1130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 9 Jan 2014 12:26:18 +0100 Subject: Update zlib to 1.2.8 --- erts/emulator/zlib/adler32.c | 104 ++-- erts/emulator/zlib/compress.c | 9 +- erts/emulator/zlib/crc32.c | 121 ++-- erts/emulator/zlib/crc32.h | 4 +- erts/emulator/zlib/deflate.c | 521 +++++++++++----- erts/emulator/zlib/deflate.h | 47 +- erts/emulator/zlib/gzguts.h | 209 +++++++ erts/emulator/zlib/inffast.c | 86 ++- erts/emulator/zlib/inffast.h | 6 +- erts/emulator/zlib/inffixed.h | 6 +- erts/emulator/zlib/inflate.c | 418 ++++++++----- erts/emulator/zlib/inflate.h | 33 +- erts/emulator/zlib/inftrees.c | 95 ++- erts/emulator/zlib/inftrees.h | 29 +- erts/emulator/zlib/trees.c | 147 ++--- erts/emulator/zlib/trees.h | 4 +- erts/emulator/zlib/uncompr.c | 8 +- erts/emulator/zlib/zconf.h | 291 +++++++-- erts/emulator/zlib/zlib.h | 1379 ++++++++++++++++++++++++++--------------- erts/emulator/zlib/zutil.c | 68 +- erts/emulator/zlib/zutil.h | 145 ++--- 21 files changed, 2460 insertions(+), 1270 deletions(-) create mode 100644 erts/emulator/zlib/gzguts.h (limited to 'erts') diff --git a/erts/emulator/zlib/adler32.c b/erts/emulator/zlib/adler32.c index 4368c31d70..c693a42b7c 100644 --- a/erts/emulator/zlib/adler32.c +++ b/erts/emulator/zlib/adler32.c @@ -1,19 +1,20 @@ /* adler32.c -- compute the Adler-32 checksum of a data stream - * Copyright (C) 1995-2004 Mark Adler + * Copyright (C) 1995-2011 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* @(#) $Id$ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#define ZLIB_INTERNAL -#include "zlib.h" +#include "zutil.h" + +#define local static + +local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); -#define BASE 65521UL /* largest prime smaller than 65536 */ +#define BASE 65521 /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ @@ -23,39 +24,44 @@ #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); -/* use NO_DIVIDE if your processor does not do division in hardware */ +/* use NO_DIVIDE if your processor does not do division in hardware -- + try it both ways to see which is faster */ #ifdef NO_DIVIDE -# define MOD(a) \ +/* note that this assumes BASE is 65521, where 65536 % 65521 == 15 + (thank you to John Reiser for pointing this out) */ +# define CHOP(a) \ + do { \ + unsigned long tmp = a >> 16; \ + a &= 0xffffUL; \ + a += (tmp << 4) - tmp; \ + } while (0) +# define MOD28(a) \ do { \ - if (a >= (BASE << 16)) a -= (BASE << 16); \ - if (a >= (BASE << 15)) a -= (BASE << 15); \ - if (a >= (BASE << 14)) a -= (BASE << 14); \ - if (a >= (BASE << 13)) a -= (BASE << 13); \ - if (a >= (BASE << 12)) a -= (BASE << 12); \ - if (a >= (BASE << 11)) a -= (BASE << 11); \ - if (a >= (BASE << 10)) a -= (BASE << 10); \ - if (a >= (BASE << 9)) a -= (BASE << 9); \ - if (a >= (BASE << 8)) a -= (BASE << 8); \ - if (a >= (BASE << 7)) a -= (BASE << 7); \ - if (a >= (BASE << 6)) a -= (BASE << 6); \ - if (a >= (BASE << 5)) a -= (BASE << 5); \ - if (a >= (BASE << 4)) a -= (BASE << 4); \ - if (a >= (BASE << 3)) a -= (BASE << 3); \ - if (a >= (BASE << 2)) a -= (BASE << 2); \ - if (a >= (BASE << 1)) a -= (BASE << 1); \ + CHOP(a); \ if (a >= BASE) a -= BASE; \ } while (0) -# define MOD4(a) \ +# define MOD(a) \ do { \ - if (a >= (BASE << 4)) a -= (BASE << 4); \ - if (a >= (BASE << 3)) a -= (BASE << 3); \ - if (a >= (BASE << 2)) a -= (BASE << 2); \ - if (a >= (BASE << 1)) a -= (BASE << 1); \ + CHOP(a); \ + MOD28(a); \ + } while (0) +# define MOD63(a) \ + do { /* this assumes a is not negative */ \ + z_off64_t tmp = a >> 32; \ + a &= 0xffffffffL; \ + a += (tmp << 8) - (tmp << 5) + tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ if (a >= BASE) a -= BASE; \ } while (0) #else # define MOD(a) a %= BASE -# define MOD4(a) a %= BASE +# define MOD28(a) a %= BASE +# define MOD63(a) a %= BASE #endif /* ========================================================================= */ @@ -94,7 +100,7 @@ uLong ZEXPORT adler32(adler, buf, len) } if (adler >= BASE) adler -= BASE; - MOD4(sum2); /* only added so many BASE's */ + MOD28(sum2); /* only added so many BASE's */ return adler | (sum2 << 16); } @@ -130,25 +136,47 @@ uLong ZEXPORT adler32(adler, buf, len) } /* ========================================================================= */ -uLong ZEXPORT adler32_combine(adler1, adler2, len2) +local uLong adler32_combine_(adler1, adler2, len2) uLong adler1; uLong adler2; - z_off_t len2; + z_off64_t len2; { unsigned long sum1; unsigned long sum2; unsigned rem; + /* for negative len, return invalid adler32 as a clue for debugging */ + if (len2 < 0) + return 0xffffffffUL; + /* the derivation of this formula is left as an exercise for the reader */ - rem = (unsigned)(len2 % BASE); + MOD63(len2); /* assumes len2 >= 0 */ + rem = (unsigned)len2; sum1 = adler1 & 0xffff; sum2 = rem * sum1; MOD(sum2); sum1 += (adler2 & 0xffff) + BASE - 1; sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; - if (sum1 > BASE) sum1 -= BASE; - if (sum1 > BASE) sum1 -= BASE; - if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); - if (sum2 > BASE) sum2 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; return sum1 | (sum2 << 16); } + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} + +uLong ZEXPORT adler32_combine64(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} diff --git a/erts/emulator/zlib/compress.c b/erts/emulator/zlib/compress.c index 28bceb15f8..8ecef0f790 100644 --- a/erts/emulator/zlib/compress.c +++ b/erts/emulator/zlib/compress.c @@ -1,10 +1,8 @@ /* compress.c -- compress a memory buffer - * Copyright (C) 1995-2003 Jean-loup Gailly. + * Copyright (C) 1995-2005 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* @(#) $Id$ */ #ifdef HAVE_CONFIG_H @@ -34,7 +32,7 @@ int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) z_stream stream; int err; - stream.next_in = (Bytef*)source; + stream.next_in = (z_const Bytef *)source; stream.avail_in = (uInt)sourceLen; #ifdef MAXSEG_64K /* Check for source > 64K on 16-bit machine: */ @@ -80,5 +78,6 @@ int ZEXPORT compress (dest, destLen, source, sourceLen) uLong ZEXPORT compressBound (sourceLen) uLong sourceLen; { - return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13; } diff --git a/erts/emulator/zlib/crc32.c b/erts/emulator/zlib/crc32.c index b9c10bb9b3..ba506d8dd3 100644 --- a/erts/emulator/zlib/crc32.c +++ b/erts/emulator/zlib/crc32.c @@ -1,19 +1,14 @@ /* crc32.c -- compute the CRC-32 of a data stream - * Copyright (C) 1995-2005 Mark Adler + * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * * Thanks to Rodney Brown for his contribution of faster * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing * tables for updating the shift register in one step with three exclusive-ors -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif * instead of four steps with four exclusive-ors. This results in about a * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. */ -/* %ExternalCopyright% */ - /* @(#) $Id$ */ /* @@ -22,6 +17,8 @@ of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should first call get_crc_table() to initialize the tables before allowing more than one thread to use crc32(). + + DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. */ #ifdef MAKECRCH @@ -31,35 +28,19 @@ # endif /* !DYNAMIC_CRC_TABLE */ #endif /* MAKECRCH */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include "zutil.h" /* for STDC and FAR definitions */ #define local static -/* Find a four-byte integer type for crc32_little() and crc32_big(). */ -#ifndef NOBYFOUR -# ifdef STDC /* need ANSI C limits.h to determine sizes */ -# include -# define BYFOUR -# if (UINT_MAX == 0xffffffffUL) - typedef unsigned int u4; -# else -# if (ULONG_MAX == 0xffffffffUL) - typedef unsigned long u4; -# else -# if (USHRT_MAX == 0xffffffffUL) - typedef unsigned short u4; -# else -# undef BYFOUR /* can't find a four-byte integer type! */ -# endif -# endif -# endif -# endif /* STDC */ -#endif /* !NOBYFOUR */ - /* Definitions for doing the crc four data bytes at a time. */ +#if !defined(NOBYFOUR) && defined(Z_U4) +# define BYFOUR +#endif #ifdef BYFOUR -# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ - (((w)&0xff00)<<8)+(((w)&0xff)<<24)) local unsigned long crc32_little OF((unsigned long, const unsigned char FAR *, unsigned)); local unsigned long crc32_big OF((unsigned long, @@ -73,14 +54,16 @@ local unsigned long gf2_matrix_times OF((unsigned long *mat, unsigned long vec)); local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); +local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); + #ifdef DYNAMIC_CRC_TABLE local volatile int crc_table_empty = 1; -local unsigned long FAR crc_table[TBLS][256]; +local z_crc_t FAR crc_table[TBLS][256]; local void make_crc_table OF((void)); #ifdef MAKECRCH - local void write_table OF((FILE *, const unsigned long FAR *)); + local void write_table OF((FILE *, const z_crc_t FAR *)); #endif /* MAKECRCH */ /* Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: @@ -110,9 +93,9 @@ local void make_crc_table OF((void)); */ local void make_crc_table() { - unsigned long c; + z_crc_t c; int n, k; - unsigned long poly; /* polynomial exclusive-or pattern */ + z_crc_t poly; /* polynomial exclusive-or pattern */ /* terms of polynomial defining this crc (except x^32): */ static volatile int first = 1; /* flag to limit concurrent making */ static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; @@ -124,13 +107,13 @@ local void make_crc_table() first = 0; /* make exclusive-or pattern from polynomial (0xedb88320UL) */ - poly = 0UL; - for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) - poly |= 1UL << (31 - p[n]); + poly = 0; + for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) + poly |= (z_crc_t)1 << (31 - p[n]); /* generate a crc for every 8-bit value */ for (n = 0; n < 256; n++) { - c = (unsigned long)n; + c = (z_crc_t)n; for (k = 0; k < 8; k++) c = c & 1 ? poly ^ (c >> 1) : c >> 1; crc_table[0][n] = c; @@ -141,11 +124,11 @@ local void make_crc_table() and then the byte reversal of those as well as the first table */ for (n = 0; n < 256; n++) { c = crc_table[0][n]; - crc_table[4][n] = REV(c); + crc_table[4][n] = ZSWAP32(c); for (k = 1; k < 4; k++) { c = crc_table[0][c & 0xff] ^ (c >> 8); crc_table[k][n] = c; - crc_table[k + 4][n] = REV(c); + crc_table[k + 4][n] = ZSWAP32(c); } } #endif /* BYFOUR */ @@ -167,7 +150,7 @@ local void make_crc_table() if (out == NULL) return; fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); - fprintf(out, "local const unsigned long FAR "); + fprintf(out, "local const z_crc_t FAR "); fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); write_table(out, crc_table[0]); # ifdef BYFOUR @@ -187,12 +170,13 @@ local void make_crc_table() #ifdef MAKECRCH local void write_table(out, table) FILE *out; - const unsigned long FAR *table; + const z_crc_t FAR *table; { int n; for (n = 0; n < 256; n++) - fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", + (unsigned long)(table[n]), n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); } #endif /* MAKECRCH */ @@ -207,13 +191,13 @@ local void write_table(out, table) /* ========================================================================= * This function can be used by asm versions of crc32() */ -const unsigned long FAR * ZEXPORT get_crc_table() +const z_crc_t FAR * ZEXPORT get_crc_table() { #ifdef DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); #endif /* DYNAMIC_CRC_TABLE */ - return (const unsigned long FAR *)crc_table; + return (const z_crc_t FAR *)crc_table; } /* ========================================================================= */ @@ -224,7 +208,7 @@ const unsigned long FAR * ZEXPORT get_crc_table() unsigned long ZEXPORT crc32(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; - unsigned len; + uInt len; { if (buf == Z_NULL) return 0UL; @@ -235,7 +219,7 @@ unsigned long ZEXPORT crc32(crc, buf, len) #ifdef BYFOUR if (sizeof(void *) == sizeof(ptrdiff_t)) { - u4 endian; + z_crc_t endian; endian = 1; if (*((unsigned char *)(&endian))) @@ -269,17 +253,17 @@ local unsigned long crc32_little(crc, buf, len) const unsigned char FAR *buf; unsigned len; { - register u4 c; - register const u4 FAR *buf4; + register z_crc_t c; + register const z_crc_t FAR *buf4; - c = (u4)crc; + c = (z_crc_t)crc; c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); len--; } - buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; while (len >= 32) { DOLIT32; len -= 32; @@ -309,17 +293,17 @@ local unsigned long crc32_big(crc, buf, len) const unsigned char FAR *buf; unsigned len; { - register u4 c; - register const u4 FAR *buf4; + register z_crc_t c; + register const z_crc_t FAR *buf4; - c = REV((u4)crc); + c = ZSWAP32((z_crc_t)crc); c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); len--; } - buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; buf4--; while (len >= 32) { DOBIG32; @@ -336,7 +320,7 @@ local unsigned long crc32_big(crc, buf, len) c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); } while (--len); c = ~c; - return (unsigned long)(REV(c)); + return (unsigned long)(ZSWAP32(c)); } #endif /* BYFOUR */ @@ -372,22 +356,22 @@ local void gf2_matrix_square(square, mat) } /* ========================================================================= */ -uLong ZEXPORT crc32_combine(crc1, crc2, len2) +local uLong crc32_combine_(crc1, crc2, len2) uLong crc1; uLong crc2; - z_off_t len2; + z_off64_t len2; { int n; unsigned long row; unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ - /* degenerate case */ - if (len2 == 0) + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) return crc1; /* put operator for one zero bit in odd */ - odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ row = 1; for (n = 1; n < GF2_DIM; n++) { odd[n] = row; @@ -426,3 +410,20 @@ uLong ZEXPORT crc32_combine(crc1, crc2, len2) crc1 ^= crc2; return crc1; } + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} + +uLong ZEXPORT crc32_combine64(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} diff --git a/erts/emulator/zlib/crc32.h b/erts/emulator/zlib/crc32.h index 49cd69a4c2..9e0c778102 100644 --- a/erts/emulator/zlib/crc32.h +++ b/erts/emulator/zlib/crc32.h @@ -2,9 +2,7 @@ * Generated automatically by crc32.c */ -/* %ExternalCopyright% */ - -local const unsigned long FAR crc_table[TBLS][256] = +local const z_crc_t FAR crc_table[TBLS][256] = { { 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, diff --git a/erts/emulator/zlib/deflate.c b/erts/emulator/zlib/deflate.c index 92f4be57c5..943c26dfb2 100644 --- a/erts/emulator/zlib/deflate.c +++ b/erts/emulator/zlib/deflate.c @@ -1,10 +1,8 @@ /* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2005 Jean-loup Gailly. + * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* * ALGORITHM * @@ -39,7 +37,7 @@ * REFERENCES * * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". - * Available in http://www.ietf.org/rfc/rfc1951.txt + * Available in http://tools.ietf.org/html/rfc1951 * * A description of the Rabin and Karp algorithm is given in the book * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. @@ -57,7 +55,7 @@ #include "deflate.h" const char deflate_copyright[] = - " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; + " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -84,19 +82,18 @@ local block_state deflate_fast OF((deflate_state *s, int flush)); #ifndef FASTEST local block_state deflate_slow OF((deflate_state *s, int flush)); #endif +local block_state deflate_rle OF((deflate_state *s, int flush)); +local block_state deflate_huff OF((deflate_state *s, int flush)); local void lm_init OF((deflate_state *s)); local void putShortMSB OF((deflate_state *s, uInt b)); local void flush_pending OF((z_streamp strm)); local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); -#ifndef FASTEST #ifdef ASMV void match_init OF((void)); /* asm code initialization */ uInt longest_match OF((deflate_state *s, IPos cur_match)); #else local uInt longest_match OF((deflate_state *s, IPos cur_match)); #endif -#endif -local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); #ifdef DEBUG local void check_match OF((deflate_state *s, IPos start, IPos match, @@ -115,11 +112,6 @@ local void check_match OF((deflate_state *s, IPos start, IPos match, #endif /* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ -#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) -/* Minimum amount of lookahead, except at the end of the input file. - * See deflate.c for comments about the MIN_MATCH+1. - */ - /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be @@ -166,6 +158,9 @@ local const config configuration_table[10] = { struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ #endif +/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ +#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0)) + /* =========================================================================== * Update a hash value with the given input byte * IN assertion: all calls to to UPDATE_HASH are made with consecutive @@ -246,10 +241,19 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, strm->msg = Z_NULL; if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; +#endif } - if (strm->zfree == (free_func)0) strm->zfree = zcfree; + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif #ifdef FASTEST if (level != 0) level = 1; @@ -293,6 +297,8 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + s->high_water = 0; /* nothing written to s->window yet */ + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); @@ -302,7 +308,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || s->pending_buf == Z_NULL) { s->status = FINISH_STATE; - strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + strm->msg = ERR_MSG(Z_MEM_ERROR); deflateEnd (strm); return Z_MEM_ERROR; } @@ -323,43 +329,70 @@ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) uInt dictLength; { deflate_state *s; - uInt length = dictLength; - uInt n; - IPos hash_head = 0; + uInt str, n; + int wrap; + unsigned avail; + z_const unsigned char *next; - if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || - strm->state->wrap == 2 || - (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) return Z_STREAM_ERROR; - s = strm->state; - if (s->wrap) - strm->adler = adler32(strm->adler, dictionary, dictLength); + wrap = s->wrap; + if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) + return Z_STREAM_ERROR; - if (length < MIN_MATCH) return Z_OK; - if (length > MAX_DIST(s)) { - length = MAX_DIST(s); - dictionary += dictLength - length; /* use the tail of the dictionary */ + /* when using zlib wrappers, compute Adler-32 for provided dictionary */ + if (wrap == 1) + strm->adler = adler32(strm->adler, dictionary, dictLength); + s->wrap = 0; /* avoid computing Adler-32 in read_buf */ + + /* if dictionary would fill window, just replace the history */ + if (dictLength >= s->w_size) { + if (wrap == 0) { /* already empty otherwise */ + CLEAR_HASH(s); + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + dictionary += dictLength - s->w_size; /* use the tail */ + dictLength = s->w_size; } - zmemcpy(s->window, dictionary, length); - s->strstart = length; - s->block_start = (long)length; - /* Insert all strings in the hash table (except for the last two bytes). - * s->lookahead stays null, so s->ins_h will be recomputed at the next - * call of fill_window. - */ - s->ins_h = s->window[0]; - UPDATE_HASH(s, s->ins_h, s->window[1]); - for (n = 0; n <= length - MIN_MATCH; n++) { - INSERT_STRING(s, n, hash_head); + /* insert dictionary into window and hash */ + avail = strm->avail_in; + next = strm->next_in; + strm->avail_in = dictLength; + strm->next_in = (z_const Bytef *)dictionary; + fill_window(s); + while (s->lookahead >= MIN_MATCH) { + str = s->strstart; + n = s->lookahead - (MIN_MATCH-1); + do { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + } while (--n); + s->strstart = str; + s->lookahead = MIN_MATCH-1; + fill_window(s); } - if (hash_head) hash_head = 0; /* to make compiler happy */ + s->strstart += s->lookahead; + s->block_start = (long)s->strstart; + s->insert = s->lookahead; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + strm->next_in = next; + strm->avail_in = avail; + s->wrap = wrap; return Z_OK; } /* ========================================================================= */ -int ZEXPORT deflateReset (strm) +int ZEXPORT deflateResetKeep (strm) z_streamp strm; { deflate_state *s; @@ -389,11 +422,22 @@ int ZEXPORT deflateReset (strm) s->last_flush = Z_NO_FLUSH; _tr_init(s); - lm_init(s); return Z_OK; } +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + int ret; + + ret = deflateResetKeep(strm); + if (ret == Z_OK) + lm_init(strm->state); + return ret; +} + /* ========================================================================= */ int ZEXPORT deflateSetHeader (strm, head) z_streamp strm; @@ -405,15 +449,43 @@ int ZEXPORT deflateSetHeader (strm, head) return Z_OK; } +/* ========================================================================= */ +int ZEXPORT deflatePending (strm, pending, bits) + unsigned *pending; + int *bits; + z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (pending != Z_NULL) + *pending = strm->state->pending; + if (bits != Z_NULL) + *bits = strm->state->bi_valid; + return Z_OK; +} + /* ========================================================================= */ int ZEXPORT deflatePrime (strm, bits, value) z_streamp strm; int bits; int value; { + deflate_state *s; + int put; + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - strm->state->bi_valid = bits; - strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + s = strm->state; + if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) + return Z_BUF_ERROR; + do { + put = Buf_size - s->bi_valid; + if (put > bits) + put = bits; + s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); + s->bi_valid += put; + _tr_flush_bits(s); + value >>= put; + bits -= put; + } while (bits); return Z_OK; } @@ -440,9 +512,12 @@ int ZEXPORT deflateParams(strm, level, strategy) } func = configuration_table[s->level].func; - if (func != configuration_table[level].func && strm->total_in != 0) { + if ((strategy != s->strategy || func != configuration_table[level].func) && + strm->total_in != 0) { /* Flush the last buffer: */ - err = deflate(strm, Z_PARTIAL_FLUSH); + err = deflate(strm, Z_BLOCK); + if (err == Z_BUF_ERROR && s->pending == 0) + err = Z_OK; } if (s->level != level) { s->level = level; @@ -486,33 +561,66 @@ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) * resulting from using fixed blocks instead of stored blocks, which deflate * can emit on compressed data for some combinations of the parameters. * - * This function could be more sophisticated to provide closer upper bounds - * for every combination of windowBits and memLevel, as well as wrap. - * But even the conservative upper bound of about 14% expansion does not - * seem onerous for output buffer allocation. + * This function could be more sophisticated to provide closer upper bounds for + * every combination of windowBits and memLevel. But even the conservative + * upper bound of about 14% expansion does not seem onerous for output buffer + * allocation. */ uLong ZEXPORT deflateBound(strm, sourceLen) z_streamp strm; uLong sourceLen; { deflate_state *s; - uLong destLen; + uLong complen, wraplen; + Bytef *str; - /* conservative upper bound */ - destLen = sourceLen + - ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + /* conservative upper bound for compressed data */ + complen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; - /* if can't get parameters, return conservative bound */ + /* if can't get parameters, return conservative bound plus zlib wrapper */ if (strm == Z_NULL || strm->state == Z_NULL) - return destLen; + return complen + 6; - /* if not default parameters, return conservative bound */ + /* compute wrapper length */ s = strm->state; + switch (s->wrap) { + case 0: /* raw deflate */ + wraplen = 0; + break; + case 1: /* zlib wrapper */ + wraplen = 6 + (s->strstart ? 4 : 0); + break; + case 2: /* gzip wrapper */ + wraplen = 18; + if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + if (s->gzhead->extra != Z_NULL) + wraplen += 2 + s->gzhead->extra_len; + str = s->gzhead->name; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + str = s->gzhead->comment; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + if (s->gzhead->hcrc) + wraplen += 2; + } + break; + default: /* for compiler happiness */ + wraplen = 6; + } + + /* if not default parameters, return conservative bound */ if (s->w_bits != 15 || s->hash_bits != 8 + 7) - return destLen; + return complen + wraplen; /* default settings: return tight bound for that case */ - return compressBound(sourceLen); + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13 - 6 + wraplen; } /* ========================================================================= @@ -537,19 +645,22 @@ local void putShortMSB (s, b) local void flush_pending(strm) z_streamp strm; { - unsigned len = strm->state->pending; + unsigned len; + deflate_state *s = strm->state; + _tr_flush_bits(s); + len = s->pending; if (len > strm->avail_out) len = strm->avail_out; if (len == 0) return; - zmemcpy(strm->next_out, strm->state->pending_out, len); + zmemcpy(strm->next_out, s->pending_out, len); strm->next_out += len; - strm->state->pending_out += len; + s->pending_out += len; strm->total_out += len; strm->avail_out -= len; - strm->state->pending -= len; - if (strm->state->pending == 0) { - strm->state->pending_out = strm->state->pending_buf; + s->pending -= len; + if (s->pending == 0) { + s->pending_out = s->pending_buf; } } @@ -562,7 +673,7 @@ int ZEXPORT deflate (strm, flush) deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL || - flush > Z_FINISH || flush < 0) { + flush > Z_BLOCK || flush < 0) { return Z_STREAM_ERROR; } s = strm->state; @@ -586,7 +697,7 @@ int ZEXPORT deflate (strm, flush) put_byte(s, 31); put_byte(s, 139); put_byte(s, 8); - if (s->gzhead == NULL) { + if (s->gzhead == Z_NULL) { put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); @@ -613,7 +724,7 @@ int ZEXPORT deflate (strm, flush) (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, s->gzhead->os & 0xff); - if (s->gzhead->extra != NULL) { + if (s->gzhead->extra != Z_NULL) { put_byte(s, s->gzhead->extra_len & 0xff); put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); } @@ -655,7 +766,7 @@ int ZEXPORT deflate (strm, flush) } #ifdef GZIP if (s->status == EXTRA_STATE) { - if (s->gzhead->extra != NULL) { + if (s->gzhead->extra != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { @@ -683,7 +794,7 @@ int ZEXPORT deflate (strm, flush) s->status = NAME_STATE; } if (s->status == NAME_STATE) { - if (s->gzhead->name != NULL) { + if (s->gzhead->name != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ int val; @@ -714,7 +825,7 @@ int ZEXPORT deflate (strm, flush) s->status = COMMENT_STATE; } if (s->status == COMMENT_STATE) { - if (s->gzhead->comment != NULL) { + if (s->gzhead->comment != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ int val; @@ -776,7 +887,7 @@ int ZEXPORT deflate (strm, flush) * flushes. For repeated and useless calls with Z_FINISH, we keep * returning Z_STREAM_END instead of Z_BUF_ERROR. */ - } else if (strm->avail_in == 0 && flush <= old_flush && + } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && flush != Z_FINISH) { ERR_RETURN(strm, Z_BUF_ERROR); } @@ -792,7 +903,9 @@ int ZEXPORT deflate (strm, flush) (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { block_state bstate; - bstate = (*(configuration_table[s->level].func))(s, flush); + bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + (s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush)); if (bstate == finish_started || bstate == finish_done) { s->status = FINISH_STATE; @@ -813,13 +926,18 @@ int ZEXPORT deflate (strm, flush) if (bstate == block_done) { if (flush == Z_PARTIAL_FLUSH) { _tr_align(s); - } else { /* FULL_FLUSH or SYNC_FLUSH */ + } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ _tr_stored_block(s, (char*)0, 0L, 0); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush == Z_FULL_FLUSH) { CLEAR_HASH(s); /* forget history */ + if (s->lookahead == 0) { + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } } } flush_pending(strm); @@ -914,12 +1032,12 @@ int ZEXPORT deflateCopy (dest, source) ss = source->state; - zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); if (ds == Z_NULL) return Z_MEM_ERROR; dest->state = (struct internal_state FAR *) ds; - zmemcpy(ds, ss, sizeof(deflate_state)); + zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); ds->strm = dest; ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); @@ -935,8 +1053,8 @@ int ZEXPORT deflateCopy (dest, source) } /* following zmemcpy do not work for 16-bit MSDOS */ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); - zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); - zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); @@ -970,15 +1088,15 @@ local int read_buf(strm, buf, size) strm->avail_in -= len; + zmemcpy(buf, strm->next_in, len); if (strm->state->wrap == 1) { - strm->adler = adler32(strm->adler, strm->next_in, len); + strm->adler = adler32(strm->adler, buf, len); } #ifdef GZIP else if (strm->state->wrap == 2) { - strm->adler = crc32(strm->adler, strm->next_in, len); + strm->adler = crc32(strm->adler, buf, len); } #endif - zmemcpy(buf, strm->next_in, len); strm->next_in += len; strm->total_in += len; @@ -1005,6 +1123,7 @@ local void lm_init (s) s->strstart = 0; s->block_start = 0L; s->lookahead = 0; + s->insert = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; s->ins_h = 0; @@ -1172,12 +1291,13 @@ local uInt longest_match(s, cur_match) return s->lookahead; } #endif /* ASMV */ -#endif /* FASTEST */ + +#else /* FASTEST */ /* --------------------------------------------------------------------------- - * Optimized version for level == 1 or strategy == Z_RLE only + * Optimized version for FASTEST only */ -local uInt longest_match_fast(s, cur_match) +local uInt longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ { @@ -1230,6 +1350,8 @@ local uInt longest_match_fast(s, cur_match) return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; } +#endif /* FASTEST */ + #ifdef DEBUG /* =========================================================================== * Check that the match at match_start is indeed a match. @@ -1276,6 +1398,8 @@ local void fill_window(s) unsigned more; /* Amount of free space at the end of the window. */ uInt wsize = s->w_size; + Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + do { more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); @@ -1308,7 +1432,6 @@ local void fill_window(s) later. (Using level 0 permanently is not an optimal usage of zlib, so we don't care about this pathological case.) */ - /* %%% avoid this when Z_RLE */ n = s->hash_size; p = &s->head[n]; do { @@ -1329,7 +1452,7 @@ local void fill_window(s) #endif more += wsize; } - if (s->strm->avail_in == 0) return; + if (s->strm->avail_in == 0) break; /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && @@ -1348,39 +1471,88 @@ local void fill_window(s) s->lookahead += n; /* Initialize the hash value now that we have some input: */ - if (s->lookahead >= MIN_MATCH) { - s->ins_h = s->window[s->strstart]; - UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); + if (s->lookahead + s->insert >= MIN_MATCH) { + uInt str = s->strstart - s->insert; + s->ins_h = s->window[str]; + UPDATE_HASH(s, s->ins_h, s->window[str + 1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif + while (s->insert) { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + s->insert--; + if (s->lookahead + s->insert < MIN_MATCH) + break; + } } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. */ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } + + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "not enough room for search"); } /* =========================================================================== * Flush the current block, with given end-of-file flag. * IN assertion: strstart is set to the end of the current match. */ -#define FLUSH_BLOCK_ONLY(s, eof) { \ +#define FLUSH_BLOCK_ONLY(s, last) { \ _tr_flush_block(s, (s->block_start >= 0L ? \ (charf *)&s->window[(unsigned)s->block_start] : \ (charf *)Z_NULL), \ (ulg)((long)s->strstart - s->block_start), \ - (eof)); \ + (last)); \ s->block_start = s->strstart; \ flush_pending(s->strm); \ Tracev((stderr,"[FLUSH]")); \ } /* Same but force premature exit if necessary. */ -#define FLUSH_BLOCK(s, eof) { \ - FLUSH_BLOCK_ONLY(s, eof); \ - if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +#define FLUSH_BLOCK(s, last) { \ + FLUSH_BLOCK_ONLY(s, last); \ + if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ } /* =========================================================================== @@ -1439,8 +1611,14 @@ local block_state deflate_stored(s, flush) FLUSH_BLOCK(s, 0); } } - FLUSH_BLOCK(s, flush == Z_FINISH); - return flush == Z_FINISH ? finish_done : block_done; + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if ((long)s->strstart > s->block_start) + FLUSH_BLOCK(s, 0); + return block_done; } /* =========================================================================== @@ -1454,7 +1632,7 @@ local block_state deflate_fast(s, flush) deflate_state *s; int flush; { - IPos hash_head = NIL; /* head of the hash chain */ + IPos hash_head; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ for (;;) { @@ -1474,6 +1652,7 @@ local block_state deflate_fast(s, flush) /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ + hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } @@ -1486,19 +1665,8 @@ local block_state deflate_fast(s, flush) * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ -#ifdef FASTEST - if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || - (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { - s->match_length = longest_match_fast (s, hash_head); - } -#else - if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { - s->match_length = longest_match (s, hash_head); - } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { - s->match_length = longest_match_fast (s, hash_head); - } -#endif - /* longest_match() or longest_match_fast() sets match_start */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ } if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->match_start, s->match_length); @@ -1546,8 +1714,14 @@ local block_state deflate_fast(s, flush) } if (bflush) FLUSH_BLOCK(s, 0); } - FLUSH_BLOCK(s, flush == Z_FINISH); - return flush == Z_FINISH ? finish_done : block_done; + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; } #ifndef FASTEST @@ -1560,7 +1734,7 @@ local block_state deflate_slow(s, flush) deflate_state *s; int flush; { - IPos hash_head = NIL; /* head of hash chain */ + IPos hash_head; /* head of hash chain */ int bflush; /* set if current block must be flushed */ /* Process the input block. */ @@ -1581,6 +1755,7 @@ local block_state deflate_slow(s, flush) /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ + hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } @@ -1596,12 +1771,8 @@ local block_state deflate_slow(s, flush) * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ - if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { - s->match_length = longest_match (s, hash_head); - } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { - s->match_length = longest_match_fast (s, hash_head); - } - /* longest_match() or longest_match_fast() sets match_start */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ if (s->match_length <= 5 && (s->strategy == Z_FILTERED #if TOO_FAR <= 32767 @@ -1674,12 +1845,17 @@ local block_state deflate_slow(s, flush) _tr_tally_lit(s, s->window[s->strstart-1], bflush); s->match_available = 0; } - FLUSH_BLOCK(s, flush == Z_FINISH); - return flush == Z_FINISH ? finish_done : block_done; + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; } #endif /* FASTEST */ -#if 0 /* =========================================================================== * For Z_RLE, simply look for runs of bytes, generate matches only of distance * one. Do not maintain a hash table. (It will be regenerated if this run of @@ -1689,43 +1865,52 @@ local block_state deflate_rle(s, flush) deflate_state *s; int flush; { - int bflush; /* set if current block must be flushed */ - uInt run; /* length of run */ - uInt max; /* maximum length of run */ - uInt prev; /* byte at distance one to match */ - Bytef *scan; /* scan for end of run */ + int bflush; /* set if current block must be flushed */ + uInt prev; /* byte at distance one to match */ + Bytef *scan, *strend; /* scan goes up to strend for length of run */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes - * for the longest encodable run. + * for the longest run, plus one for the unrolled loop. */ - if (s->lookahead < MAX_MATCH) { + if (s->lookahead <= MAX_MATCH) { fill_window(s); - if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* See how many times the previous byte repeats */ - run = 0; - if (s->strstart > 0) { /* if there is a previous byte, that is */ - max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + s->match_length = 0; + if (s->lookahead >= MIN_MATCH && s->strstart > 0) { scan = s->window + s->strstart - 1; - prev = *scan++; - do { - if (*scan++ != prev) - break; - } while (++run < max); + prev = *scan; + if (prev == *++scan && prev == *++scan && prev == *++scan) { + strend = s->window + s->strstart + MAX_MATCH; + do { + } while (prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + scan < strend); + s->match_length = MAX_MATCH - (int)(strend - scan); + if (s->match_length > s->lookahead) + s->match_length = s->lookahead; + } + Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ - if (run >= MIN_MATCH) { - check_match(s, s->strstart, s->strstart - 1, run); - _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); - s->lookahead -= run; - s->strstart += run; + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, s->match_length); + + _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + s->strstart += s->match_length; + s->match_length = 0; } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); @@ -1735,7 +1920,51 @@ local block_state deflate_rle(s, flush) } if (bflush) FLUSH_BLOCK(s, 0); } - FLUSH_BLOCK(s, flush == Z_FINISH); - return flush == Z_FINISH ? finish_done : block_done; + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +local block_state deflate_huff(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s->lookahead == 0) { + fill_window(s); + if (s->lookahead == 0) { + if (flush == Z_NO_FLUSH) + return need_more; + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s->match_length = 0; + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; } -#endif diff --git a/erts/emulator/zlib/deflate.h b/erts/emulator/zlib/deflate.h index 92b037c9d2..ce0299edd1 100644 --- a/erts/emulator/zlib/deflate.h +++ b/erts/emulator/zlib/deflate.h @@ -1,10 +1,8 @@ /* deflate.h -- internal compression state - * Copyright (C) 1995-2004 Jean-loup Gailly + * Copyright (C) 1995-2012 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. @@ -50,6 +48,9 @@ #define MAX_BITS 15 /* All codes must not exceed MAX_BITS bits */ +#define Buf_size 16 +/* size of bit buffer in bi_buf */ + #define INIT_STATE 42 #define EXTRA_STATE 69 #define NAME_STATE 73 @@ -103,7 +104,7 @@ typedef struct internal_state { int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ gz_headerp gzhead; /* gzip header information to write */ uInt gzindex; /* where in extra, name, or comment */ - Byte method; /* STORED (for zip only) or DEFLATED */ + Byte method; /* can only be DEFLATED */ int last_flush; /* value of flush param for previous deflate call */ /* used by deflate.c: */ @@ -190,7 +191,7 @@ typedef struct internal_state { int nice_match; /* Stop searching when current match exceeds this */ /* used by trees.c: */ - /* Didn't use ct_data typedef below to supress compiler warning */ + /* Didn't use ct_data typedef below to suppress compiler warning */ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ @@ -246,7 +247,7 @@ typedef struct internal_state { ulg opt_len; /* bit length of current block with optimal trees */ ulg static_len; /* bit length of current block with static trees */ uInt matches; /* number of string matches in current block */ - int last_eob_len; /* bit length of EOB code for last block */ + uInt insert; /* bytes at end of window left to insert */ #ifdef DEBUG ulg compressed_len; /* total bit length of compressed file mod 2^32 */ @@ -262,6 +263,13 @@ typedef struct internal_state { * are always zero. */ + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + } FAR deflate_state; /* Output a byte on the stream. @@ -280,14 +288,19 @@ typedef struct internal_state { * distances are limited to MAX_DIST instead of WSIZE. */ +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + /* in trees.c */ -void _tr_init OF((deflate_state *s)); -int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); -void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, - int eof)); -void _tr_align OF((deflate_state *s)); -void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, - int eof)); +void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); #define d_code(dist) \ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) @@ -300,11 +313,11 @@ void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, /* Inline versions of _tr_tally for speed: */ #if defined(GEN_TREES_H) || !defined(STDC) - extern uch _length_code[]; - extern uch _dist_code[]; + extern uch ZLIB_INTERNAL _length_code[]; + extern uch ZLIB_INTERNAL _dist_code[]; #else - extern const uch _length_code[]; - extern const uch _dist_code[]; + extern const uch ZLIB_INTERNAL _length_code[]; + extern const uch ZLIB_INTERNAL _dist_code[]; #endif # define _tr_tally_lit(s, c, flush) \ diff --git a/erts/emulator/zlib/gzguts.h b/erts/emulator/zlib/gzguts.h new file mode 100644 index 0000000000..d87659d031 --- /dev/null +++ b/erts/emulator/zlib/gzguts.h @@ -0,0 +1,209 @@ +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif +#include + +#ifdef _WIN32 +# include +#endif + +#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) +# include +#endif + +#ifdef WINAPI_FAMILY +# define open _open +# define read _read +# define write _write +# define close _close +#endif + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS +/* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 +/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +# ifdef VMS +# define NO_vsnprintf +# endif +# ifdef __OS400__ +# define NO_vsnprintf +# endif +# ifdef __MVS__ +# define NO_vsnprintf +# endif +#endif + +/* unlike snprintf (which is required in C99, yet still not supported by + Microsoft more than a decade later!), _snprintf does not guarantee null + termination of the result -- however this is only used in gzlib.c where + the result is assured to fit in the space provided */ +#ifdef _MSC_VER +# define snprintf _snprintf +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE +# include +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifndef NO_STRERROR +# include +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + +/* default i/o buffer size -- double this for output when reading (this and + twice this must be able to fit in an unsigned type) */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* exposed contents for gzgetc() macro */ + struct gzFile_s x; /* "x" for exposed */ + /* x.have: number of bytes available at x.next */ + /* x.next: next output data to deliver or write */ + /* x.pos: current position in uncompressed data */ + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer */ + unsigned char *out; /* output buffer (double-sized when reading) */ + int direct; /* 0 if processing gzip, 1 if transparent */ + /* just for reading */ + int how; /* 0: get header, 1: copy, 2: decompress */ + z_off64_t start; /* where the gzip data started, for rewinding */ + int eof; /* true if end of input file reached */ + int past; /* true if read requested past end */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/erts/emulator/zlib/inffast.c b/erts/emulator/zlib/inffast.c index eb81884888..5187743fde 100644 --- a/erts/emulator/zlib/inffast.c +++ b/erts/emulator/zlib/inffast.c @@ -1,10 +1,8 @@ /* inffast.c -- fast decoding - * Copyright (C) 1995-2004 Mark Adler + * Copyright (C) 1995-2008, 2010, 2013 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -69,13 +67,13 @@ requires strm->avail_out >= 258 for each loop to avoid checking for output space. */ -void inflate_fast(strm, start) +void ZLIB_INTERNAL inflate_fast(strm, start) z_streamp strm; unsigned start; /* inflate()'s starting value for strm->avail_out */ { struct inflate_state FAR *state; - unsigned char FAR *in; /* local strm->next_in */ - unsigned char FAR *last; /* while in < last, enough input available */ + z_const unsigned char FAR *in; /* local strm->next_in */ + z_const unsigned char FAR *last; /* have enough input while in < last */ unsigned char FAR *out; /* local strm->next_out */ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ unsigned char FAR *end; /* while out < end, enough space available */ @@ -84,7 +82,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ #endif unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ - unsigned write; /* window write index */ + unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ unsigned long hold; /* local strm->hold */ unsigned bits; /* local strm->bits */ @@ -92,7 +90,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ code const FAR *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ - code this; /* retrieved table entry */ + code here; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ @@ -111,7 +109,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ #endif wsize = state->wsize; whave = state->whave; - write = state->write; + wnext = state->wnext; window = state->window; hold = state->hold; bits = state->bits; @@ -129,20 +127,20 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ hold += (unsigned long)(PUP(in)) << bits; bits += 8; } - this = lcode[hold & lmask]; + here = lcode[hold & lmask]; dolen: - op = (unsigned)(this.bits); + op = (unsigned)(here.bits); hold >>= op; bits -= op; - op = (unsigned)(this.op); + op = (unsigned)(here.op); if (op == 0) { /* literal */ - Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", this.val)); - PUP(out) = (unsigned char)(this.val); + "inflate: literal 0x%02x\n", here.val)); + PUP(out) = (unsigned char)(here.val); } else if (op & 16) { /* length base */ - len = (unsigned)(this.val); + len = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { @@ -160,14 +158,14 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ hold += (unsigned long)(PUP(in)) << bits; bits += 8; } - this = dcode[hold & dmask]; + here = dcode[hold & dmask]; dodist: - op = (unsigned)(this.bits); + op = (unsigned)(here.bits); hold >>= op; bits -= op; - op = (unsigned)(this.op); + op = (unsigned)(here.op); if (op & 16) { /* distance base */ - dist = (unsigned)(this.val); + dist = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (bits < op) { hold += (unsigned long)(PUP(in)) << bits; @@ -192,12 +190,34 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ if (dist > op) { /* see if copy from window */ op = dist - op; /* distance back in window */ if (op > whave) { - strm->msg = (char *)"invalid distance too far back"; - state->mode = BAD; - break; + if (state->sane) { + strm->msg = + (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (len <= op - whave) { + do { + PUP(out) = 0; + } while (--len); + continue; + } + len -= op - whave; + do { + PUP(out) = 0; + } while (--op > whave); + if (op == 0) { + from = out - dist; + do { + PUP(out) = PUP(from); + } while (--len); + continue; + } +#endif } from = window - OFF; - if (write == 0) { /* very common case */ + if (wnext == 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; @@ -207,17 +227,17 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ from = out - dist; /* rest from output */ } } - else if (write < op) { /* wrap around window */ - from += wsize + write - op; - op -= write; + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; if (op < len) { /* some from end of window */ len -= op; do { PUP(out) = PUP(from); } while (--op); from = window - OFF; - if (write < len) { /* some from start of window */ - op = write; + if (wnext < len) { /* some from start of window */ + op = wnext; len -= op; do { PUP(out) = PUP(from); @@ -227,7 +247,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else { /* contiguous in window */ - from += write - op; + from += wnext - op; if (op < len) { /* some from window */ len -= op; do { @@ -264,7 +284,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else if ((op & 64) == 0) { /* 2nd level distance code */ - this = dcode[this.val + (hold & ((1U << op) - 1))]; + here = dcode[here.val + (hold & ((1U << op) - 1))]; goto dodist; } else { @@ -274,7 +294,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else if ((op & 64) == 0) { /* 2nd level length code */ - this = lcode[this.val + (hold & ((1U << op) - 1))]; + here = lcode[here.val + (hold & ((1U << op) - 1))]; goto dolen; } else if (op & 32) { /* end-of-block */ @@ -310,7 +330,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): - Using bit fields for code structure - Different op definition to avoid & for extra bits (do & for table bits) - - Three separate decoding do-loops for direct, window, and write == 0 + - Three separate decoding do-loops for direct, window, and wnext == 0 - Special case for distance > 1 copies to do overlapped load and store copy - Explicit branch predictions (based on measured branch probabilities) - Deferring match copy and interspersed it with decoding subsequent codes diff --git a/erts/emulator/zlib/inffast.h b/erts/emulator/zlib/inffast.h index 623ed83c08..e5c1aa4ca8 100644 --- a/erts/emulator/zlib/inffast.h +++ b/erts/emulator/zlib/inffast.h @@ -1,13 +1,11 @@ /* inffast.h -- header to use inffast.c - * Copyright (C) 1995-2003 Mark Adler + * Copyright (C) 1995-2003, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ -void inflate_fast OF((z_streamp strm, unsigned start)); +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/erts/emulator/zlib/inffixed.h b/erts/emulator/zlib/inffixed.h index 75ed4b5978..d628327769 100644 --- a/erts/emulator/zlib/inffixed.h +++ b/erts/emulator/zlib/inffixed.h @@ -2,9 +2,9 @@ * Generated automatically by makefixed(). */ - /* WARNING: this file should *not* be used by applications. It - is part of the implementation of the compression library and - is subject to change. Applications should only use zlib.h. + /* WARNING: this file should *not* be used by applications. + It is part of the implementation of this library and is + subject to change. Applications should only use zlib.h. */ static const code lenfix[512] = { diff --git a/erts/emulator/zlib/inflate.c b/erts/emulator/zlib/inflate.c index 1764447c66..532330b06b 100644 --- a/erts/emulator/zlib/inflate.c +++ b/erts/emulator/zlib/inflate.c @@ -1,13 +1,8 @@ /* inflate.c -- zlib decompression - * Copyright (C) 1995-2005 Mark Adler + * Copyright (C) 1995-2012 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif /* * Change history: * @@ -50,7 +45,7 @@ * - Rearrange window copies in inflate_fast() for speed and simplification * - Unroll last copy for window match in inflate_fast() * - Use local copies of window variables in inflate_fast() for speed - * - Pull out common write == 0 case for speed in inflate_fast() + * - Pull out common wnext == 0 case for speed in inflate_fast() * - Make op and len in inflate_fast() unsigned for consistency * - Add FAR to lcode and dcode declarations in inflate_fast() * - Simplified bad distance check in inflate_fast() @@ -85,6 +80,9 @@ * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include "zutil.h" #include "inftrees.h" #include "inflate.h" @@ -98,14 +96,15 @@ /* function prototypes */ local void fixedtables OF((struct inflate_state FAR *state)); -local int updatewindow OF((z_streamp strm, unsigned out)); +local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, + unsigned copy)); #ifdef BUILDFIXED void makefixed OF((void)); #endif -local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, +local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, unsigned len)); -int ZEXPORT inflateReset(strm) +int ZEXPORT inflateResetKeep(strm) z_streamp strm; { struct inflate_state FAR *state; @@ -114,36 +113,71 @@ z_streamp strm; state = (struct inflate_state FAR *)strm->state; strm->total_in = strm->total_out = state->total = 0; strm->msg = Z_NULL; - strm->adler = 1; /* to support ill-conceived Java test suite */ + if (state->wrap) /* to support ill-conceived Java test suite */ + strm->adler = state->wrap & 1; state->mode = HEAD; state->last = 0; state->havedict = 0; state->dmax = 32768U; state->head = Z_NULL; - state->wsize = 0; - state->whave = 0; - state->write = 0; state->hold = 0; state->bits = 0; state->lencode = state->distcode = state->next = state->codes; + state->sane = 1; + state->back = -1; Tracev((stderr, "inflate: reset\n")); return Z_OK; } -int ZEXPORT inflatePrime(strm, bits, value) +int ZEXPORT inflateReset(strm) z_streamp strm; -int bits; -int value; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; - if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; - value &= (1L << bits) - 1; - state->hold += value << state->bits; - state->bits += bits; - return Z_OK; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + return inflateResetKeep(strm); +} + +int ZEXPORT inflateReset2(strm, windowBits) +z_streamp strm; +int windowBits; +{ + int wrap; + struct inflate_state FAR *state; + + /* get the state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) + return Z_STREAM_ERROR; + if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); } int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) @@ -152,6 +186,7 @@ int windowBits; const char *version; int stream_size; { + int ret; struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || @@ -160,33 +195,31 @@ int stream_size; if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* in case we return an error */ if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; +#endif } - if (strm->zfree == (free_func)0) strm->zfree = zcfree; + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif state = (struct inflate_state FAR *) ZALLOC(strm, 1, sizeof(struct inflate_state)); if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; - if (windowBits < 0) { - state->wrap = 0; - windowBits = -windowBits; - } - else { - state->wrap = (windowBits >> 4) + 1; -#ifdef GUNZIP - if (windowBits < 48) windowBits &= 15; -#endif - } - if (windowBits < 8 || windowBits > 15) { + state->window = Z_NULL; + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { ZFREE(strm, state); strm->state = Z_NULL; - return Z_STREAM_ERROR; } - state->wbits = (unsigned)windowBits; - state->window = Z_NULL; - return inflateReset(strm); + return ret; } int ZEXPORT inflateInit_(strm, version, stream_size) @@ -197,6 +230,27 @@ int stream_size; return inflateInit2_(strm, DEF_WBITS, version, stream_size); } +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. @@ -291,8 +345,8 @@ void makefixed() low = 0; for (;;) { if ((low % 7) == 0) printf("\n "); - printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, - state.lencode[low].val); + printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, + state.lencode[low].bits, state.lencode[low].val); if (++low == size) break; putchar(','); } @@ -325,12 +379,13 @@ void makefixed() output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ -local int updatewindow(strm, out) +local int updatewindow(strm, end, copy) z_streamp strm; -unsigned out; +const Bytef *end; +unsigned copy; { struct inflate_state FAR *state; - unsigned copy, dist; + unsigned dist; state = (struct inflate_state FAR *)strm->state; @@ -345,30 +400,29 @@ unsigned out; /* if window not in use yet, initialize */ if (state->wsize == 0) { state->wsize = 1U << state->wbits; - state->write = 0; + state->wnext = 0; state->whave = 0; } /* copy state->wsize or less output bytes into the circular window */ - copy = out - strm->avail_out; if (copy >= state->wsize) { - zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); - state->write = 0; + zmemcpy(state->window, end - state->wsize, state->wsize); + state->wnext = 0; state->whave = state->wsize; } else { - dist = state->wsize - state->write; + dist = state->wsize - state->wnext; if (dist > copy) dist = copy; - zmemcpy(state->window + state->write, strm->next_out - copy, dist); + zmemcpy(state->window + state->wnext, end - copy, dist); copy -= dist; if (copy) { - zmemcpy(state->window, strm->next_out - copy, copy); - state->write = copy; + zmemcpy(state->window, end - copy, copy); + state->wnext = copy; state->whave = state->wsize; } else { - state->write += dist; - if (state->write == state->wsize) state->write = 0; + state->wnext += dist; + if (state->wnext == state->wsize) state->wnext = 0; if (state->whave < state->wsize) state->whave += dist; } } @@ -469,11 +523,6 @@ unsigned out; bits -= bits & 7; \ } while (0) -/* Reverse the bytes in a 32-bit value */ -#define REVERSE(q) \ - ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ - (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) - /* inflate() uses a state machine to process as much input data and generate as much output data as possible before returning. The state machine is @@ -561,7 +610,7 @@ z_streamp strm; int flush; { struct inflate_state FAR *state; - unsigned char FAR *next; /* next input */ + z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ unsigned have, left; /* available input and output */ unsigned long hold; /* bit buffer */ @@ -569,7 +618,7 @@ int flush; unsigned in, out; /* save starting available input and output */ unsigned copy; /* number of stored or match bytes to copy */ unsigned char FAR *from; /* where to copy match bytes from */ - code this; /* current decoding table entry */ + code here; /* current decoding table entry */ code last; /* parent table entry */ unsigned len; /* length to copy for repeats, bits to drop */ int ret; /* return code */ @@ -624,7 +673,9 @@ int flush; } DROPBITS(4); len = BITS(4) + 8; - if (len > state->wbits) { + if (state->wbits == 0) + state->wbits = len; + else if (len > state->wbits) { strm->msg = (char *)"invalid window size"; state->mode = BAD; break; @@ -765,7 +816,7 @@ int flush; #endif case DICTID: NEEDBITS(32); - strm->adler = state->check = REVERSE(hold); + strm->adler = state->check = ZSWAP32(hold); INITBITS(); state->mode = DICT; case DICT: @@ -776,7 +827,7 @@ int flush; strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = TYPE; case TYPE: - if (flush == Z_BLOCK) goto inf_leave; + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; case TYPEDO: if (state->last) { BYTEBITS(); @@ -796,7 +847,11 @@ int flush; fixedtables(state); Tracev((stderr, "inflate: fixed codes block%s\n", state->last ? " (last)" : "")); - state->mode = LEN; /* decode codes */ + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } break; case 2: /* dynamic block */ Tracev((stderr, "inflate: dynamic codes block%s\n", @@ -821,6 +876,9 @@ int flush; Tracev((stderr, "inflate: stored length %u\n", state->length)); INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: state->mode = COPY; case COPY: copy = state->length; @@ -866,7 +924,7 @@ int flush; while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; - state->lencode = (code const FAR *)(state->next); + state->lencode = (const code FAR *)(state->next); state->lenbits = 7; ret = inflate_table(CODES, state->lens, 19, &(state->next), &(state->lenbits), state->work); @@ -881,19 +939,18 @@ int flush; case CODELENS: while (state->have < state->nlen + state->ndist) { for (;;) { - this = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(this.bits) <= bits) break; + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } - if (this.val < 16) { - NEEDBITS(this.bits); - DROPBITS(this.bits); - state->lens[state->have++] = this.val; + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; } else { - if (this.val == 16) { - NEEDBITS(this.bits + 2); - DROPBITS(this.bits); + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); if (state->have == 0) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; @@ -903,16 +960,16 @@ int flush; copy = 3 + BITS(2); DROPBITS(2); } - else if (this.val == 17) { - NEEDBITS(this.bits + 3); - DROPBITS(this.bits); + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); len = 0; copy = 3 + BITS(3); DROPBITS(3); } else { - NEEDBITS(this.bits + 7); - DROPBITS(this.bits); + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); len = 0; copy = 11 + BITS(7); DROPBITS(7); @@ -930,9 +987,18 @@ int flush; /* handle error breaks in while */ if (state->mode == BAD) break; - /* build code tables */ + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; - state->lencode = (code const FAR *)(state->next); + state->lencode = (const code FAR *)(state->next); state->lenbits = 9; ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), &(state->lenbits), state->work); @@ -941,7 +1007,7 @@ int flush; state->mode = BAD; break; } - state->distcode = (code const FAR *)(state->next); + state->distcode = (const code FAR *)(state->next); state->distbits = 6; ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(state->distbits), state->work); @@ -951,88 +1017,102 @@ int flush; break; } Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: state->mode = LEN; case LEN: if (have >= 6 && left >= 258) { RESTORE(); inflate_fast(strm, out); LOAD(); + if (state->mode == TYPE) + state->back = -1; break; } + state->back = 0; for (;;) { - this = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(this.bits) <= bits) break; + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } - if (this.op && (this.op & 0xf0) == 0) { - last = this; + if (here.op && (here.op & 0xf0) == 0) { + last = here; for (;;) { - this = state->lencode[last.val + + here = state->lencode[last.val + (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + this.bits) <= bits) break; + if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); + state->back += last.bits; } - DROPBITS(this.bits); - state->length = (unsigned)this.val; - if ((int)(this.op) == 0) { - Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + DROPBITS(here.bits); + state->back += here.bits; + state->length = (unsigned)here.val; + if ((int)(here.op) == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", this.val)); + "inflate: literal 0x%02x\n", here.val)); state->mode = LIT; break; } - if (this.op & 32) { + if (here.op & 32) { Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; state->mode = TYPE; break; } - if (this.op & 64) { + if (here.op & 64) { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } - state->extra = (unsigned)(this.op) & 15; + state->extra = (unsigned)(here.op) & 15; state->mode = LENEXT; case LENEXT: if (state->extra) { NEEDBITS(state->extra); state->length += BITS(state->extra); DROPBITS(state->extra); + state->back += state->extra; } Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; state->mode = DIST; case DIST: for (;;) { - this = state->distcode[BITS(state->distbits)]; - if ((unsigned)(this.bits) <= bits) break; + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } - if ((this.op & 0xf0) == 0) { - last = this; + if ((here.op & 0xf0) == 0) { + last = here; for (;;) { - this = state->distcode[last.val + + here = state->distcode[last.val + (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + this.bits) <= bits) break; + if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); + state->back += last.bits; } - DROPBITS(this.bits); - if (this.op & 64) { + DROPBITS(here.bits); + state->back += here.bits; + if (here.op & 64) { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } - state->offset = (unsigned)this.val; - state->extra = (unsigned)(this.op) & 15; + state->offset = (unsigned)here.val; + state->extra = (unsigned)(here.op) & 15; state->mode = DISTEXT; case DISTEXT: if (state->extra) { NEEDBITS(state->extra); state->offset += BITS(state->extra); DROPBITS(state->extra); + state->back += state->extra; } #ifdef INFLATE_STRICT if (state->offset > state->dmax) { @@ -1041,11 +1121,6 @@ int flush; break; } #endif - if (state->offset > state->whave + out - left) { - strm->msg = (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } Tracevv((stderr, "inflate: distance %u\n", state->offset)); state->mode = MATCH; case MATCH: @@ -1053,12 +1128,32 @@ int flush; copy = out - left; if (state->offset > copy) { /* copy from window */ copy = state->offset - copy; - if (copy > state->write) { - copy -= state->write; + if (copy > state->whave) { + if (state->sane) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + Trace((stderr, "inflate.c too far\n")); + copy -= state->whave; + if (copy > state->length) copy = state->length; + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = 0; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; +#endif + } + if (copy > state->wnext) { + copy -= state->wnext; from = state->window + (state->wsize - copy); } else - from = state->window + (state->write - copy); + from = state->window + (state->wnext - copy); if (copy > state->length) copy = state->length; } else { /* copy from output */ @@ -1093,7 +1188,7 @@ int flush; #ifdef GUNZIP state->flags ? hold : #endif - REVERSE(hold)) != state->check) { + ZSWAP32(hold)) != state->check) { strm->msg = (char *)"incorrect data check"; state->mode = BAD; break; @@ -1137,8 +1232,9 @@ int flush; */ inf_leave: RESTORE(); - if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) - if (updatewindow(strm, out)) { + if (state->wsize || (out != strm->avail_out && state->mode < BAD && + (state->mode < CHECK || flush != Z_FINISH))) + if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { state->mode = MEM; return Z_MEM_ERROR; } @@ -1151,7 +1247,8 @@ int flush; strm->adler = state->check = UPDATE(state->check, strm->next_out - out, out); strm->data_type = state->bits + (state->last ? 64 : 0) + - (state->mode == TYPE ? 128 : 0); + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) ret = Z_BUF_ERROR; return ret; @@ -1171,13 +1268,37 @@ z_streamp strm; return Z_OK; } +int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) +z_streamp strm; +Bytef *dictionary; +uInt *dictLength; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* copy dictionary */ + if (state->whave && dictionary != Z_NULL) { + zmemcpy(dictionary, state->window + state->wnext, + state->whave - state->wnext); + zmemcpy(dictionary + state->whave - state->wnext, + state->window, state->wnext); + } + if (dictLength != Z_NULL) + *dictLength = state->whave; + return Z_OK; +} + int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; { struct inflate_state FAR *state; - unsigned long id; + unsigned long dictid; + int ret; /* check state */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; @@ -1185,29 +1306,21 @@ uInt dictLength; if (state->wrap != 0 && state->mode != DICT) return Z_STREAM_ERROR; - /* check for correct dictionary id */ + /* check for correct dictionary identifier */ if (state->mode == DICT) { - id = adler32(0L, Z_NULL, 0); - id = adler32(id, dictionary, dictLength); - if (id != state->check) + dictid = adler32(0L, Z_NULL, 0); + dictid = adler32(dictid, dictionary, dictLength); + if (dictid != state->check) return Z_DATA_ERROR; } - /* copy dictionary to window */ - if (updatewindow(strm, strm->avail_out)) { + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary + dictLength, dictLength); + if (ret) { state->mode = MEM; return Z_MEM_ERROR; } - if (dictLength > state->wsize) { - zmemcpy(state->window, dictionary + dictLength - state->wsize, - state->wsize); - state->whave = state->wsize; - } - else { - zmemcpy(state->window + state->wsize - dictLength, dictionary, - dictLength); - state->whave = dictLength; - } state->havedict = 1; Tracev((stderr, "inflate: dictionary set\n")); return Z_OK; @@ -1243,7 +1356,7 @@ gz_headerp head; */ local unsigned syncsearch(have, buf, len) unsigned FAR *have; -unsigned char FAR *buf; +const unsigned char FAR *buf; unsigned len; { unsigned got; @@ -1355,8 +1468,8 @@ z_streamp source; } /* copy state */ - zmemcpy(dest, source, sizeof(z_stream)); - zmemcpy(copy, state, sizeof(struct inflate_state)); + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); if (state->lencode >= state->codes && state->lencode <= state->codes + ENOUGH - 1) { copy->lencode = copy->codes + (state->lencode - state->codes); @@ -1371,3 +1484,32 @@ z_streamp source; dest->state = (struct internal_state FAR *)copy; return Z_OK; } + +int ZEXPORT inflateUndermine(strm, subvert) +z_streamp strm; +int subvert; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->sane = !subvert; +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + return Z_OK; +#else + state->sane = 1; + return Z_DATA_ERROR; +#endif +} + +long ZEXPORT inflateMark(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + state = (struct inflate_state FAR *)strm->state; + return ((long)(state->back) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} diff --git a/erts/emulator/zlib/inflate.h b/erts/emulator/zlib/inflate.h index 59164091c5..95f4986d40 100644 --- a/erts/emulator/zlib/inflate.h +++ b/erts/emulator/zlib/inflate.h @@ -1,10 +1,8 @@ /* inflate.h -- internal inflate state definition - * Copyright (C) 1995-2004 Mark Adler + * Copyright (C) 1995-2009 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. @@ -34,11 +32,13 @@ typedef enum { TYPE, /* i: waiting for type bits, including last-flag bit */ TYPEDO, /* i: same, but skip check to exit inflate on new block */ STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ COPY, /* i/o: waiting for input or output to copy stored block */ TABLE, /* i: waiting for dynamic block table lengths */ LENLENS, /* i: waiting for code length code lengths */ CODELENS, /* i: waiting for length/lit and distance code lengths */ - LEN, /* i: waiting for length/lit code */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ LENEXT, /* i: waiting for length extra bits */ DIST, /* i: waiting for distance code */ DISTEXT, /* i: waiting for distance extra bits */ @@ -55,19 +55,21 @@ typedef enum { /* State transitions between above modes - - (most modes can go to the BAD or MEM mode -- not shown for clarity) + (most modes can go to BAD or MEM on error -- not shown for clarity) Process header: - HEAD -> (gzip) or (zlib) - (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME - NAME -> COMMENT -> HCRC -> TYPE + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE (zlib) -> DICTID or TYPE DICTID -> DICT -> TYPE + (raw) -> TYPEDO Read deflate blocks: - TYPE -> STORED or TABLE or LEN or CHECK - STORED -> COPY -> TYPE - TABLE -> LENLENS -> CODELENS -> LEN - Read deflate codes: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: LEN -> LENEXT or LIT or TYPE LENEXT -> DIST -> DISTEXT -> MATCH -> LEN LIT -> LEN @@ -75,7 +77,7 @@ typedef enum { CHECK -> LENGTH -> DONE */ -/* state maintained between inflate() calls. Approximately 7K bytes. */ +/* state maintained between inflate() calls. Approximately 10K bytes. */ struct inflate_state { inflate_mode mode; /* current inflate mode */ int last; /* true if processing last block */ @@ -90,7 +92,7 @@ struct inflate_state { unsigned wbits; /* log base 2 of requested window size */ unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ - unsigned write; /* window write index */ + unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if needed */ /* bit accumulator */ unsigned long hold; /* input bit accumulator */ @@ -114,4 +116,7 @@ struct inflate_state { unsigned short lens[320]; /* temporary storage for code lengths */ unsigned short work[288]; /* work area for code table building */ code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ }; diff --git a/erts/emulator/zlib/inftrees.c b/erts/emulator/zlib/inftrees.c index 832fe28668..3766fa2646 100644 --- a/erts/emulator/zlib/inftrees.c +++ b/erts/emulator/zlib/inftrees.c @@ -1,10 +1,8 @@ /* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2005 Mark Adler + * Copyright (C) 1995-2013 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -14,7 +12,7 @@ #define MAXBITS 15 const char inflate_copyright[] = - " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; + " inflate 1.2.8 Copyright 1995-2013 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -34,7 +32,7 @@ const char inflate_copyright[] = table index bits. It will differ if the request is greater than the longest code or if it is less than the shortest code. */ -int inflate_table(type, lens, codes, table, bits, work) +int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) codetype type; unsigned short FAR *lens; unsigned codes; @@ -55,7 +53,7 @@ unsigned short FAR *work; unsigned fill; /* index for replicating entries */ unsigned low; /* low bits for current root entry */ unsigned mask; /* mask for low root bits */ - code this; /* table entry for duplication */ + code here; /* table entry for duplication */ code FAR *next; /* next available space in table */ const unsigned short FAR *base; /* base value table to use */ const unsigned short FAR *extra; /* extra bits table to use */ @@ -67,7 +65,7 @@ unsigned short FAR *work; 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, @@ -120,15 +118,15 @@ unsigned short FAR *work; if (count[max] != 0) break; if (root > max) root = max; if (max == 0) { /* no symbols to code at all */ - this.op = (unsigned char)64; /* invalid code marker */ - this.bits = (unsigned char)1; - this.val = (unsigned short)0; - *(*table)++ = this; /* make a table to force an error */ - *(*table)++ = this; + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)1; + here.val = (unsigned short)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; *bits = 1; return 0; /* no symbols, but wait for decoding to report error */ } - for (min = 1; min <= MAXBITS; min++) + for (min = 1; min < max; min++) if (count[min] != 0) break; if (root < min) root = min; @@ -171,11 +169,10 @@ unsigned short FAR *work; entered in the tables. used keeps track of how many table entries have been allocated from the - provided *table space. It is checked when a LENS table is being made - against the space in *table, ENOUGH, minus the maximum space needed by - the worst case distance code, MAXD. This should never happen, but the - sufficiency of ENOUGH has not been proven exhaustively, hence the check. - This assumes that when type == LENS, bits == 9. + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This @@ -214,24 +211,25 @@ unsigned short FAR *work; mask = used - 1; /* mask for comparing low */ /* check available table space */ - if (type == LENS && used >= ENOUGH - MAXD) + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) return 1; /* process all codes and make table entries */ for (;;) { /* create table entry */ - this.bits = (unsigned char)(len - drop); + here.bits = (unsigned char)(len - drop); if ((int)(work[sym]) < end) { - this.op = (unsigned char)0; - this.val = work[sym]; + here.op = (unsigned char)0; + here.val = work[sym]; } else if ((int)(work[sym]) > end) { - this.op = (unsigned char)(extra[work[sym]]); - this.val = base[work[sym]]; + here.op = (unsigned char)(extra[work[sym]]); + here.val = base[work[sym]]; } else { - this.op = (unsigned char)(32 + 64); /* end of block */ - this.val = 0; + here.op = (unsigned char)(32 + 64); /* end of block */ + here.val = 0; } /* replicate for those indices with low len bits equal to huff */ @@ -240,7 +238,7 @@ unsigned short FAR *work; min = fill; /* save offset to next table */ do { fill -= incr; - next[(huff >> drop) + fill] = this; + next[(huff >> drop) + fill] = here; } while (fill != 0); /* backwards increment the len-bit code huff */ @@ -282,7 +280,8 @@ unsigned short FAR *work; /* check for enough space */ used += 1U << curr; - if (type == LENS && used >= ENOUGH - MAXD) + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) return 1; /* point entry in root table to sub-table */ @@ -293,38 +292,14 @@ unsigned short FAR *work; } } - /* - Fill in rest of table for incomplete codes. This loop is similar to the - loop above in incrementing huff for table indices. It is assumed that - len is equal to curr + drop, so there is no loop needed to increment - through high index bits. When the current sub-table is filled, the loop - drops back to the root table to fill in any remaining entries there. - */ - this.op = (unsigned char)64; /* invalid code marker */ - this.bits = (unsigned char)(len - drop); - this.val = (unsigned short)0; - while (huff != 0) { - /* when done with sub-table, drop back to root table */ - if (drop != 0 && (huff & mask) != low) { - drop = 0; - len = root; - next = *table; - this.bits = (unsigned char)len; - } - - /* put invalid code marker in table */ - next[huff >> drop] = this; - - /* backwards increment the len-bit code huff */ - incr = 1U << (len - 1); - while (huff & incr) - incr >>= 1; - if (incr != 0) { - huff &= incr - 1; - huff += incr; - } - else - huff = 0; + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff != 0) { + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + next[huff] = here; } /* set return parameters */ diff --git a/erts/emulator/zlib/inftrees.h b/erts/emulator/zlib/inftrees.h index 808100f70a..baa53a0b1a 100644 --- a/erts/emulator/zlib/inftrees.h +++ b/erts/emulator/zlib/inftrees.h @@ -1,10 +1,8 @@ /* inftrees.h -- header to use inftrees.c - * Copyright (C) 1995-2005 Mark Adler + * Copyright (C) 1995-2005, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. @@ -37,21 +35,28 @@ typedef struct { 01000000 - invalid code */ -/* Maximum size of dynamic tree. The maximum found in a long but non- - exhaustive search was 1444 code structures (852 for length/literals - and 592 for distances, the latter actually the result of an - exhaustive search). The true maximum is not known, but the value - below is more than safe. */ -#define ENOUGH 2048 -#define MAXD 592 +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) -/* Type of code to build for inftable() */ +/* Type of code to build for inflate_table() */ typedef enum { CODES, LENS, DISTS } codetype; -extern int inflate_table OF((codetype type, unsigned short FAR *lens, +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, unsigned codes, code FAR * FAR *table, unsigned FAR *bits, unsigned short FAR *work)); diff --git a/erts/emulator/zlib/trees.c b/erts/emulator/zlib/trees.c index 7d9f77f451..465e944e5b 100644 --- a/erts/emulator/zlib/trees.c +++ b/erts/emulator/zlib/trees.c @@ -1,10 +1,9 @@ /* trees.c -- output deflated data using Huffman coding - * Copyright (C) 1995-2005 Jean-loup Gailly + * Copyright (C) 1995-2012 Jean-loup Gailly + * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* * ALGORITHM * @@ -78,11 +77,6 @@ local const uch bl_order[BL_CODES] * probability, to avoid transmitting the lengths for unused bit length codes. */ -#define Buf_size (8 * 2*sizeof(char)) -/* Number of bits used within bi_buf. (bi_buf might be implemented on - * more than 16 bits on some systems.) - */ - /* =========================================================================== * Local data. These are initialized only once. */ @@ -155,9 +149,9 @@ local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); local int build_bl_tree OF((deflate_state *s)); local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, int blcodes)); -local void compress_block OF((deflate_state *s, ct_data *ltree, - ct_data *dtree)); -local void set_data_type OF((deflate_state *s)); +local void compress_block OF((deflate_state *s, const ct_data *ltree, + const ct_data *dtree)); +local int detect_data_type OF((deflate_state *s)); local unsigned bi_reverse OF((unsigned value, int length)); local void bi_windup OF((deflate_state *s)); local void bi_flush OF((deflate_state *s)); @@ -208,12 +202,12 @@ local void send_bits(s, value, length) * unused bits in value. */ if (s->bi_valid > (int)Buf_size - length) { - s->bi_buf |= (value << s->bi_valid); + s->bi_buf |= (ush)value << s->bi_valid; put_short(s, s->bi_buf); s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); s->bi_valid += length - Buf_size; } else { - s->bi_buf |= value << s->bi_valid; + s->bi_buf |= (ush)value << s->bi_valid; s->bi_valid += length; } } @@ -223,12 +217,12 @@ local void send_bits(s, value, length) { int len = length;\ if (s->bi_valid > (int)Buf_size - len) {\ int val = value;\ - s->bi_buf |= (val << s->bi_valid);\ + s->bi_buf |= (ush)val << s->bi_valid;\ put_short(s, s->bi_buf);\ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ s->bi_valid += len - Buf_size;\ } else {\ - s->bi_buf |= (value) << s->bi_valid;\ + s->bi_buf |= (ush)(value) << s->bi_valid;\ s->bi_valid += len;\ }\ } @@ -255,11 +249,13 @@ local void tr_static_init() if (static_init_done) return; /* For some embedded targets, global variables are not initialized: */ +#ifdef NO_INIT_GLOBAL_POINTERS static_l_desc.static_tree = static_ltree; static_l_desc.extra_bits = extra_lbits; static_d_desc.static_tree = static_dtree; static_d_desc.extra_bits = extra_dbits; static_bl_desc.extra_bits = extra_blbits; +#endif /* Initialize the mapping length (0..255) -> length code (0..28) */ length = 0; @@ -353,13 +349,14 @@ void gen_trees_header() static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); } - fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); for (i = 0; i < DIST_CODE_LEN; i++) { fprintf(header, "%2u%s", _dist_code[i], SEPARATOR(i, DIST_CODE_LEN-1, 20)); } - fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + fprintf(header, + "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { fprintf(header, "%2u%s", _length_code[i], SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); @@ -384,7 +381,7 @@ void gen_trees_header() /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ -void _tr_init(s) +void ZLIB_INTERNAL _tr_init(s) deflate_state *s; { tr_static_init(); @@ -400,7 +397,6 @@ void _tr_init(s) s->bi_buf = 0; s->bi_valid = 0; - s->last_eob_len = 8; /* enough lookahead for inflate */ #ifdef DEBUG s->compressed_len = 0L; s->bits_sent = 0L; @@ -869,13 +865,13 @@ local void send_all_trees(s, lcodes, dcodes, blcodes) /* =========================================================================== * Send a stored block */ -void _tr_stored_block(s, buf, stored_len, eof) +void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) deflate_state *s; charf *buf; /* input block */ ulg stored_len; /* length of input block */ - int eof; /* true if this is the last block for a file */ + int last; /* one if this is the last block for a file */ { - send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ + send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ #ifdef DEBUG s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; s->compressed_len += (stored_len + 4) << 3; @@ -883,18 +879,20 @@ void _tr_stored_block(s, buf, stored_len, eof) copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ } +/* =========================================================================== + * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) + */ +void ZLIB_INTERNAL _tr_flush_bits(s) + deflate_state *s; +{ + bi_flush(s); +} + /* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. - * The current inflate code requires 9 bits of lookahead. If the - * last two codes for the previous block (real code plus EOB) were coded - * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode - * the last real code. In this case we send two empty static blocks instead - * of one. (There are no problems if the previous block is stored or fixed.) - * To simplify the code, we assume the worst case of last real code encoded - * on one bit only. */ -void _tr_align(s) +void ZLIB_INTERNAL _tr_align(s) deflate_state *s; { send_bits(s, STATIC_TREES<<1, 3); @@ -903,31 +901,17 @@ void _tr_align(s) s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ #endif bi_flush(s); - /* Of the 10 bits for the empty block, we have already sent - * (10 - bi_valid) bits. The lookahead for the last real code (before - * the EOB of the previous block) was thus at least one plus the length - * of the EOB plus what we have just sent of the empty static block. - */ - if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { - send_bits(s, STATIC_TREES<<1, 3); - send_code(s, END_BLOCK, static_ltree); -#ifdef DEBUG - s->compressed_len += 10L; -#endif - bi_flush(s); - } - s->last_eob_len = 7; } /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and output the encoded block to the zip file. */ -void _tr_flush_block(s, buf, stored_len, eof) +void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) deflate_state *s; charf *buf; /* input block, or NULL if too old */ ulg stored_len; /* length of input block */ - int eof; /* true if this is the last block for a file */ + int last; /* one if this is the last block for a file */ { ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ int max_blindex = 0; /* index of last bit length code of non zero freq */ @@ -936,8 +920,8 @@ void _tr_flush_block(s, buf, stored_len, eof) if (s->level > 0) { /* Check if the file is binary or text */ - if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) - set_data_type(s); + if (s->strm->data_type == Z_UNKNOWN) + s->strm->data_type = detect_data_type(s); /* Construct the literal and distance trees */ build_tree(s, (tree_desc *)(&(s->l_desc))); @@ -983,23 +967,25 @@ void _tr_flush_block(s, buf, stored_len, eof) * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ - _tr_stored_block(s, buf, stored_len, eof); + _tr_stored_block(s, buf, stored_len, last); #ifdef FORCE_STATIC } else if (static_lenb >= 0) { /* force static trees */ #else } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { #endif - send_bits(s, (STATIC_TREES<<1)+eof, 3); - compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); + send_bits(s, (STATIC_TREES<<1)+last, 3); + compress_block(s, (const ct_data *)static_ltree, + (const ct_data *)static_dtree); #ifdef DEBUG s->compressed_len += 3 + s->static_len; #endif } else { - send_bits(s, (DYN_TREES<<1)+eof, 3); + send_bits(s, (DYN_TREES<<1)+last, 3); send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, max_blindex+1); - compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); + compress_block(s, (const ct_data *)s->dyn_ltree, + (const ct_data *)s->dyn_dtree); #ifdef DEBUG s->compressed_len += 3 + s->opt_len; #endif @@ -1010,21 +996,21 @@ void _tr_flush_block(s, buf, stored_len, eof) */ init_block(s); - if (eof) { + if (last) { bi_windup(s); #ifdef DEBUG s->compressed_len += 7; /* align on byte boundary */ #endif } Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, - s->compressed_len-7*eof)); + s->compressed_len-7*last)); } /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ -int _tr_tally (s, dist, lc) +int ZLIB_INTERNAL _tr_tally (s, dist, lc) deflate_state *s; unsigned dist; /* distance of matched string */ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ @@ -1076,8 +1062,8 @@ int _tr_tally (s, dist, lc) */ local void compress_block(s, ltree, dtree) deflate_state *s; - ct_data *ltree; /* literal tree */ - ct_data *dtree; /* distance tree */ + const ct_data *ltree; /* literal tree */ + const ct_data *dtree; /* distance tree */ { unsigned dist; /* distance of matched string */ int lc; /* match length or unmatched char (if dist == 0) */ @@ -1119,28 +1105,48 @@ local void compress_block(s, ltree, dtree) } while (lx < s->last_lit); send_code(s, END_BLOCK, ltree); - s->last_eob_len = ltree[END_BLOCK].Len; } /* =========================================================================== - * Set the data type to BINARY or TEXT, using a crude approximation: - * set it to Z_TEXT if all symbols are either printable characters (33 to 255) - * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). * IN assertion: the fields Freq of dyn_ltree are set. */ -local void set_data_type(s) +local int detect_data_type(s) deflate_state *s; { + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long black_mask = 0xf3ffc07fUL; int n; - for (n = 0; n < 9; n++) + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>= 1) + if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("white-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) if (s->dyn_ltree[n].Freq != 0) - break; - if (n == 9) - for (n = 14; n < 32; n++) - if (s->dyn_ltree[n].Freq != 0) - break; - s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; + return Z_TEXT; + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; } /* =========================================================================== @@ -1206,7 +1212,6 @@ local void copy_block(s, buf, len, header) int header; /* true if block header must be written */ { bi_windup(s); /* align on byte boundary */ - s->last_eob_len = 8; /* enough lookahead for inflate */ if (header) { put_short(s, (ush)len); diff --git a/erts/emulator/zlib/trees.h b/erts/emulator/zlib/trees.h index 72facf900f..d35639d82a 100644 --- a/erts/emulator/zlib/trees.h +++ b/erts/emulator/zlib/trees.h @@ -70,7 +70,7 @@ local const ct_data static_dtree[D_CODES] = { {{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} }; -const uch _dist_code[DIST_CODE_LEN] = { +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, @@ -99,7 +99,7 @@ const uch _dist_code[DIST_CODE_LEN] = { 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 }; -const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, diff --git a/erts/emulator/zlib/uncompr.c b/erts/emulator/zlib/uncompr.c index cbc93cb1eb..864d571719 100644 --- a/erts/emulator/zlib/uncompr.c +++ b/erts/emulator/zlib/uncompr.c @@ -1,10 +1,8 @@ /* uncompr.c -- decompress a memory buffer - * Copyright (C) 1995-2003 Jean-loup Gailly. + * Copyright (C) 1995-2003, 2010 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* @(#) $Id$ */ #ifdef HAVE_CONFIG_H @@ -21,8 +19,6 @@ been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen is the actual size of the compressed buffer. - This function can be used to decompress a whole file at once if the - input file is mmap'ed. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output @@ -37,7 +33,7 @@ int ZEXPORT uncompress (dest, destLen, source, sourceLen) z_stream stream; int err; - stream.next_in = (Bytef*)source; + stream.next_in = (z_const Bytef *)source; stream.avail_in = (uInt)sourceLen; /* Check for source > 64K on 16-bit machine: */ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; diff --git a/erts/emulator/zlib/zconf.h b/erts/emulator/zlib/zconf.h index b7979d48d3..9987a77553 100644 --- a/erts/emulator/zlib/zconf.h +++ b/erts/emulator/zlib/zconf.h @@ -1,10 +1,8 @@ /* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2005 Jean-loup Gailly. + * Copyright (C) 1995-2013 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* @(#) $Id$ */ #ifndef ZCONF_H @@ -13,52 +11,145 @@ /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". */ -#ifdef Z_PREFIX -# define deflateInit_ z_deflateInit_ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 # define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd -# define inflateInit_ z_inflateInit_ -# define inflate z_inflate -# define inflateEnd z_inflateEnd # define deflateInit2_ z_deflateInit2_ -# define deflateSetDictionary z_deflateSetDictionary -# define deflateCopy z_deflateCopy -# define deflateReset z_deflateReset +# define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams -# define deflateBound z_deflateBound +# define deflatePending z_deflatePending # define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzvprintf z_gzvprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader # define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 # define inflateSetDictionary z_inflateSetDictionary +# define inflateGetDictionary z_inflateGetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint -# define inflateCopy z_inflateCopy -# define inflateReset z_inflateReset -# define inflateBack z_inflateBack -# define inflateBackEnd z_inflateBackEnd -# define compress z_compress -# define compress2 z_compress2 -# define compressBound z_compressBound -# define uncompress z_uncompress -# define adler32 z_adler32 -# define crc32 z_crc32 -# define get_crc_table z_get_crc_table +# define inflateUndermine z_inflateUndermine +# define inflateResetKeep z_inflateResetKeep +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# endif # define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef # define alloc_func z_alloc_func +# define charf z_charf # define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp # define in_func z_in_func +# define intf z_intf # define out_func z_out_func -# define Byte z_Byte # define uInt z_uInt -# define uLong z_uLong -# define Bytef z_Bytef -# define charf z_charf -# define intf z_intf # define uIntf z_uIntf +# define uLong z_uLong # define uLongf z_uLongf -# define voidpf z_voidpf # define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + #endif #if defined(__MSDOS__) && !defined(MSDOS) @@ -127,6 +218,12 @@ # endif #endif +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + /* Some Mac compilers merge all .h files incorrectly: */ #if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) # define NO_DUMMY_DECL @@ -173,6 +270,14 @@ # endif #endif +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have @@ -286,49 +391,121 @@ typedef uLong FAR uLongf; typedef Byte *voidp; #endif -#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ -# include /* for off_t */ -# include /* for SEEK_* and off_t */ -# ifdef VMS -# include /* for off_t */ +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ # endif -# define z_off_t off_t #endif -#ifndef SEEK_SET + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif + #ifndef z_off_t # define z_off_t long #endif -#if defined(__OS400__) -# define NO_vsnprintf -#endif - -#if defined(__MVS__) -# define NO_vsnprintf -# ifdef FAR -# undef FAR +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t # endif #endif /* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) -# pragma map(deflateInit_,"DEIN") -# pragma map(deflateInit2_,"DEIN2") -# pragma map(deflateEnd,"DEEND") -# pragma map(deflateBound,"DEBND") -# pragma map(inflateInit_,"ININ") -# pragma map(inflateInit2_,"ININ2") -# pragma map(inflateEnd,"INEND") -# pragma map(inflateSync,"INSY") -# pragma map(inflateSetDictionary,"INSEDI") -# pragma map(compressBound,"CMBND") -# pragma map(inflate_table,"INTABL") -# pragma map(inflate_fast,"INFA") -# pragma map(inflate_copyright,"INCOPY") + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") #endif #endif /* ZCONF_H */ diff --git a/erts/emulator/zlib/zlib.h b/erts/emulator/zlib/zlib.h index 9209774383..3e0c7672ac 100644 --- a/erts/emulator/zlib/zlib.h +++ b/erts/emulator/zlib/zlib.h @@ -1,7 +1,7 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.3, July 18th, 2005 + version 1.2.8, April 28th, 2013 - Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -24,12 +24,10 @@ The data format used by the zlib library is described by RFCs (Request for - Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt - (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ -/* %ExternalCopyright% */ - #ifndef ZLIB_H #define ZLIB_H @@ -39,41 +37,44 @@ extern "C" { #endif -#define ZLIB_VERSION "1.2.3" -#define ZLIB_VERNUM 0x1230 +#define ZLIB_VERSION "1.2.8" +#define ZLIB_VERNUM 0x1280 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 8 +#define ZLIB_VER_SUBREVISION 0 /* - The 'zlib' compression library provides in-memory compression and - decompression functions, including integrity checks of the uncompressed - data. This version of the library supports only one compression method - (deflation) but other algorithms will be added later and will have the same - stream interface. - - Compression can be done in a single step if the buffers are large - enough (for example if an input file is mmap'ed), or can be done by - repeated calls of the compression function. In the latter case, the - application must provide more input and/or consume the output + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output (providing more output space) before each call. - The compressed data format used by default by the in-memory functions is + The compressed data format used by default by the in-memory functions is the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped around a deflate stream, which is itself documented in RFC 1951. - The library also supports reading and writing files in gzip (.gz) format + The library also supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. - This library can optionally read and write gzip streams in memory as well. + This library can optionally read and write gzip streams in memory as well. - The zlib format was designed to be compact and fast for use in memory + The zlib format was designed to be compact and fast for use in memory and on communications channels. The gzip format was designed for single- file compression on file systems, has a larger header than zlib to maintain directory information, and uses a different, slower check method than zlib. - The library does not install any signal handler. The decoder checks - the consistency of the compressed data, so the library should never - crash even in case of corrupted input. + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. */ typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); @@ -82,15 +83,15 @@ typedef void (*free_func) OF((voidpf opaque, voidpf address)); struct internal_state; typedef struct z_stream_s { - Bytef *next_in; /* next input byte */ + z_const Bytef *next_in; /* next input byte */ uInt avail_in; /* number of bytes available at next_in */ - uLong total_in; /* total nb of input bytes read so far */ + uLong total_in; /* total number of input bytes read so far */ Bytef *next_out; /* next output byte should be put there */ uInt avail_out; /* remaining free space at next_out */ - uLong total_out; /* total nb of bytes output so far */ + uLong total_out; /* total number of bytes output so far */ - char *msg; /* last error message, NULL if no error */ + z_const char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ @@ -128,45 +129,45 @@ typedef struct gz_header_s { typedef gz_header FAR *gz_headerp; /* - The application must update next_in and avail_in when avail_in has - dropped to zero. It must update next_out and avail_out when avail_out - has dropped to zero. The application must initialize zalloc, zfree and - opaque before calling the init function. All other fields are set by the - compression library and must not be updated by the application. - - The opaque value provided by the application will be passed as the first - parameter for calls of zalloc and zfree. This can be useful for custom - memory management. The compression library attaches no meaning to the + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the opaque value. - zalloc must return Z_NULL if there is not enough memory for the object. + zalloc must return Z_NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be thread safe. - On 16-bit systems, the functions zalloc and zfree must be able to allocate - exactly 65536 bytes, but will not be required to allocate more than this - if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, - pointers returned by zalloc for objects of exactly 65536 bytes *must* - have their offset normalized to zero. The default allocation function - provided by this library ensures this (see zutil.c). To reduce memory - requirements and avoid any allocation of 64K objects, at the expense of - compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). - - The fields total_in and total_out can be used for statistics or - progress reports. After compression, total_in holds the total size of - the uncompressed data and may be saved for use in the decompressor - (particularly if the decompressor wants to decompress everything in - a single step). + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). */ /* constants */ #define Z_NO_FLUSH 0 -#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_PARTIAL_FLUSH 1 #define Z_SYNC_FLUSH 2 #define Z_FULL_FLUSH 3 #define Z_FINISH 4 #define Z_BLOCK 5 +#define Z_TREES 6 /* Allowed flush values; see deflate() and inflate() below for details */ #define Z_OK 0 @@ -178,8 +179,8 @@ typedef gz_header FAR *gz_headerp; #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) -/* Return codes for the compression/decompression functions. Negative - * values are errors, positive values are used for special but normal events. +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. */ #define Z_NO_COMPRESSION 0 @@ -209,119 +210,141 @@ typedef gz_header FAR *gz_headerp; #define zlib_version zlibVersion() /* for compatibility with versions < 1.0.2 */ + /* basic functions */ ZEXTERN const char * ZEXPORT zlibVersion OF((void)); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. - If the first character differs, the library code actually used is - not compatible with the zlib.h header file used by the application. - This check is automatically made by deflateInit and inflateInit. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. */ /* ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); - Initializes the internal stream state for compression. The fields - zalloc, zfree and opaque must be initialized before by the caller. - If zalloc and zfree are set to Z_NULL, deflateInit updates them to - use default allocation functions. + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: - 1 gives best speed, 9 gives best compression, 0 gives no compression at - all (the input data is simply copied a block at a time). - Z_DEFAULT_COMPRESSION requests a default compromise between speed and - compression (currently equivalent to level 6). + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). - deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if level is not a valid compression level, + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible - with the version assumed by the caller (ZLIB_VERSION). - msg is set to null if there is no error message. deflateInit does not - perform any compression: this will be done by deflate(). + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). */ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); /* deflate compresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce some - output latency (reading input without producing any output) except when + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when forced to flush. - The detailed semantics are as follows. deflate performs one or both of the + The detailed semantics are as follows. deflate performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not + accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - Provide more output starting at next_out and update next_out and avail_out - accordingly. This action is forced if the parameter flush is non zero. + accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter - should be set only when necessary (in interactive applications). - Some output may be provided even if flush is not set. - - Before the call of deflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming - more output, and updating avail_in or avail_out accordingly; avail_out - should never be zero before the call. The application can consume the - compressed output when it wants, for example when the output buffer is full - (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK - and with zero avail_out, it must be called again after making room in the - output buffer because there might be more output pending. + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to - decide how much data to accumualte before producing output, in order to + decide how much data to accumulate before producing output, in order to maximize compression. If the parameter flush is set to Z_SYNC_FLUSH, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so - that the decompressor can get all input data available so far. (In particular - avail_in is zero after the call if enough output space has been provided - before the call.) Flushing may degrade compression for some compression - algorithms and so it should be used only when necessary. + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. If flush is set to Z_FULL_FLUSH, all output is flushed as with Z_SYNC_FLUSH, and the compression state is reset so that decompression can restart from this point if previous compressed data has been damaged or if - random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero - avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out is greater than six to avoid repeated flush markers due to avail_out == 0 on return. If the parameter flush is set to Z_FINISH, pending input is processed, - pending output is flushed and deflate returns with Z_STREAM_END if there - was enough output space; if deflate returns with Z_OK, this function must be + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be called again with Z_FINISH and more output space (updated avail_out) but no - more input data, until it returns with Z_STREAM_END or an error. After - deflate has returned Z_STREAM_END, the only possible operations on the - stream are deflateReset or deflateEnd. + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. Z_FINISH can be used immediately after deflateInit if all the compression - is to be done in a single step. In this case, avail_out must be at least - the value returned by deflateBound (see below). If deflate does not return - Z_STREAM_END, then it must be called again as described above. + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). Then deflate is guaranteed to + return Z_STREAM_END. If not enough output space is provided, deflate will + not return Z_STREAM_END, and it must be called again as described above. deflate() sets strm->adler to the adler32 checksum of all input read so far (that is, total_in bytes). deflate() may update strm->data_type if it can make a good guess about - the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered - binary. This field is only for information purposes and does not affect - the compression algorithm in any manner. + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible - (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and deflate() can be called again with more input and more output space to continue compressing. */ @@ -330,13 +353,13 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. + This function discards any unprocessed input and does not flush any pending + output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent, Z_DATA_ERROR if the stream was freed - prematurely (some input or output was discarded). In the error case, - msg may be set but then points to a static string (which must not be + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be deallocated). */ @@ -344,10 +367,10 @@ ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); /* ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); - Initializes the internal stream state for decompression. The fields + Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by - the caller. If next_in is not Z_NULL and avail_in is large enough (the exact - value depends on the compression method), inflateInit determines the + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the compression method from the zlib header and allocates all data structures accordingly; otherwise the allocation will be deferred to the first call of inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to @@ -355,95 +378,116 @@ ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller. msg is set to null if there is no error - message. inflateInit does not perform any decompression apart from reading - the zlib header if present: this will be done by inflate(). (So next_in and - avail_in may be modified, but next_out and avail_out are unchanged.) + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. */ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); /* inflate decompresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce + buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. - The detailed semantics are as follows. inflate performs one or both of the + The detailed semantics are as follows. inflate performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in is updated and processing - will resume at this point for the next call of inflate(). + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). - Provide more output starting at next_out and update next_out and avail_out - accordingly. inflate() provides as much output as possible, until there - is no more input data or no more space in the output buffer (see below - about the flush parameter). - - Before the call of inflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming - more output, and updating the next_* and avail_* values accordingly. - The application can consume the uncompressed output when it wants, for - example when the output buffer is full (avail_out == 0), or after each - call of inflate(). If inflate returns Z_OK and with zero avail_out, it - must be called again after making room in the output buffer because there - might be more output pending. - - The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, - Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much - output as possible to the output buffer. Z_BLOCK requests that inflate() stop - if and when it gets to the next deflate block boundary. When decoding the - zlib or gzip format, this will cause inflate() to return immediately after - the header and before the first block. When doing a raw inflate, inflate() - will go ahead and process the first block, and will return when it gets to - the end of that block, or when it runs out of data. + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. The Z_BLOCK option assists in appending to or combining deflate streams. Also to assist in this, on return inflate() will set strm->data_type to the - number of unused bits in the last byte taken from strm->next_in, plus 64 - if inflate() is currently decoding the last block in the deflate stream, - plus 128 if inflate() returned immediately after decoding an end-of-block - code or decoding the complete header up to just before the first byte of the - deflate stream. The end-of-block will not be indicated until all of the - uncompressed data from that block has been written to strm->next_out. The - number of unused bits may in general be greater than seven, except when - bit 7 of data_type is set, in which case the number of unused bits will be - less than eight. + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. inflate() should normally be called until it returns Z_STREAM_END or an - error. However if all decompression is to be performed in a single step - (a single call of inflate), the parameter flush should be set to - Z_FINISH. In this case all pending input is processed and all pending - output is flushed; avail_out must be large enough to hold all the - uncompressed data. (The size of the uncompressed data may have been saved - by the compressor for this purpose.) The next operation on this stream must - be inflateEnd to deallocate the decompression state. The use of Z_FINISH - is never required, but can be used to inform inflate that a faster approach - may be used for the single inflate() call. + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. In this implementation, inflate() always flushes as much output as possible to the output buffer, and always uses the faster approach on the - first call. So the only effect of the flush parameter in this implementation - is on the return value of inflate(), as noted below, or when it returns early - because Z_BLOCK is used. + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. If a preset dictionary is needed after this call (see inflateSetDictionary - below), inflate sets strm->adler to the adler32 checksum of the dictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise it sets - strm->adler to the adler32 checksum of all output produced so far (that is, + strm->adler to the Adler-32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described - below. At the end of the stream, inflate() checks that its computed adler32 + below. At the end of the stream, inflate() checks that its computed adler32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. - inflate() will decompress and check either zlib-wrapped or gzip-wrapped - deflate data. The header type is detected automatically. Any information - contained in the gzip header is not retained, so applications that need that - information should instead use raw inflate, see inflateInit2() below, or - inflateBack() and perform their own processing of the gzip header and - trailer. + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + producted so far. The CRC-32 is checked against the gzip trailer. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has @@ -451,27 +495,28 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check value), Z_STREAM_ERROR if the stream structure was inconsistent (for example - if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress is possible or if there was not enough room in the - output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to - continue decompressing. If Z_DATA_ERROR is returned, the application may then - call inflateSync() to look for a good compression block if a partial recovery - of the data is desired. + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. */ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. + This function discards any unprocessed input and does not flush any pending + output. inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state - was inconsistent. In the error case, msg may be set but then points to a + was inconsistent. In the error case, msg may be set but then points to a static string (which must not be deallocated). */ + /* Advanced functions */ /* @@ -486,55 +531,57 @@ ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, int memLevel, int strategy)); - This is another version of deflateInit with more compression options. The - fields next_in, zalloc, zfree and opaque must be initialized before by - the caller. + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. - The method parameter is the compression method. It must be Z_DEFLATED in + The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. The windowBits parameter is the base two logarithm of the window size - (the size of the history buffer). It should be in the range 8..15 for this - version of the library. Larger values of this parameter result in better - compression at the expense of memory usage. The default value is 15 if + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. - windowBits can also be -8..-15 for raw deflate. In this case, -windowBits - determines the window size. deflate() will then generate raw deflate data + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data with no zlib header or trailer, and will not compute an adler32 check value. - windowBits can also be greater than 15 for optional gzip encoding. Add + windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits to write a simple gzip header and trailer around the - compressed data instead of a zlib wrapper. The gzip header will have no - file name, no extra data, no comment, no modification time (set to zero), - no header crc, and the operating system will be set to 255 (unknown). If a + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a gzip stream is being written, strm->adler is a crc32 instead of an adler32. The memLevel parameter specifies how much memory should be allocated - for the internal compression state. memLevel=1 uses minimum memory but - is slow and reduces compression ratio; memLevel=9 uses maximum memory - for optimal speed. The default value is 8. See zconf.h for total memory - usage as a function of windowBits and memLevel. + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. - The strategy parameter is used to tune the compression algorithm. Use the + The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no string match), or Z_RLE to limit match distances to one (run-length - encoding). Filtered data consists mostly of small values with a somewhat - random distribution. In this case, the compression algorithm is tuned to - compress them better. The effect of Z_FILTERED is to force more Huffman + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between - Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as - Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy - parameter only affects the compression ratio but not the correctness of the - compressed output even if it is not set appropriately. Z_FIXED prevents the - use of dynamic Huffman codes, allowing for a simpler decoder for special - applications. - - deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid - method). msg is set to null if there is no error message. deflateInit2 does - not perform any compression: this will be done by deflate(). + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, @@ -542,38 +589,43 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, uInt dictLength)); /* Initializes the compression dictionary from the given byte sequence - without producing any compressed output. This function must be called - immediately after deflateInit, deflateInit2 or deflateReset, before any - call of deflate. The compressor and decompressor must use exactly the same - dictionary (see inflateSetDictionary). + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly - used strings preferably put towards the end of the dictionary. Using a + used strings preferably put towards the end of the dictionary. Using a dictionary is most useful when the data to be compressed is short and can be predicted with good accuracy; the data can then be compressed better than with the default empty dictionary. Depending on the size of the compression data structures selected by deflateInit or deflateInit2, a part of the dictionary may in effect be - discarded, for example if the dictionary is larger than the window size in - deflate or deflate2. Thus the strings most likely to be useful should be - put at the end of the dictionary, not at the front. In addition, the - current implementation of deflate will use at most the window size minus - 262 bytes of the provided dictionary. + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. Upon return of this function, strm->adler is set to the adler32 value of the dictionary; the decompressor may later use this value to determine - which dictionary has been used by the compressor. (The adler32 value + which dictionary has been used by the compressor. (The adler32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) If a raw deflate was requested, then the adler32 value is not computed and strm->adler is not set. deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent (for example if deflate has already been called for this stream - or if the compression method is bsort). deflateSetDictionary does not - perform any compression: this will be done by deflate(). + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, @@ -583,26 +635,26 @@ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, This function can be useful when several compression strategies will be tried, for example when there are several ways of pre-processing the input - data with a filter. The streams that will be discarded should then be freed + data with a filter. The streams that will be discarded should then be freed by calling deflateEnd. Note that deflateCopy duplicates the internal - compression state which can be quite large, so this strategy is slow and - can consume lots of memory. + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and + (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); /* This function is equivalent to deflateEnd followed by deflateInit, - but does not free and reallocate all the internal compression state. - The stream will keep the same compression level and any other attributes - that may have been set by deflateInit2. + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. - deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, @@ -612,18 +664,18 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2. This can be used to switch between compression and straight copy of the input data, or - to switch to a different kind of input data requiring a different - strategy. If the compression level is changed, the input available so far - is compressed with the old level (and may be flushed); the new level will - take effect only at the next call of deflate(). + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). Before the call of deflateParams, the stream state must be set as for - a call of deflate(), since the currently available input may have to - be compressed and flushed. In particular, strm->avail_out must be non-zero. + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source - stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR - if strm->avail_out was zero. + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. */ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, @@ -647,31 +699,53 @@ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, uLong sourceLen)); /* deflateBound() returns an upper bound on the compressed size after - deflation of sourceLen bytes. It must be called after deflateInit() - or deflateInit2(). This would be used to allocate an output buffer - for deflation in a single pass, and so would be called before deflate(). + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. */ +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, int bits, int value)); /* deflatePrime() inserts bits in the deflate output stream. The intent - is that this function is used to start off the deflate output with the - bits leftover from a previous deflate stream when appending to it. As such, - this function can only be used for raw deflate, and must be used before the - first deflate() call after a deflateInit2() or deflateReset(). bits must be - less than or equal to 16, and that many of the least significant bits of - value will be inserted in the output. - - deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, gz_headerp head)); /* - deflateSetHeader() provides gzip header information for when a gzip + deflateSetHeader() provides gzip header information for when a gzip stream is requested by deflateInit2(). deflateSetHeader() may be called after deflateInit2() or deflateReset() and before the first call of deflate(). The text, time, os, extra field, name, and comment information @@ -684,11 +758,11 @@ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, 1.3.x) do not support header crc's, and will report that it is a "multi-part gzip file" and give up. - If deflateSetHeader is not used, the default gzip header has text false, + If deflateSetHeader is not used, the default gzip header has text false, the time set to zero, and os set to 255, with no extra, name, or comment fields. The gzip header is returned to the default state by deflateReset(). - deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ @@ -696,43 +770,50 @@ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, int windowBits)); - This is another version of inflateInit with an extra parameter. The + This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for - this version of the library. The default value is 15 if inflateInit is used - instead. windowBits must be greater than or equal to the windowBits value + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing, or it must be equal to 15 if - deflateInit2() was not used. If a compressed stream with a larger window + deflateInit2() was not used. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. - windowBits can also be -8..-15 for raw inflate. In this case, -windowBits - determines the window size. inflate() will then process raw deflate data, + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, not looking for a zlib or gzip header, not generating a check value, and not - looking for any check values for comparison at the end of the stream. This + looking for any check values for comparison at the end of the stream. This is for use with other formats that use the deflate compressed data format - such as zip. Those formats provide their own check values. If a custom + such as zip. Those formats provide their own check values. If a custom format is developed using the raw deflate format for compressed data, it is recommended that a check value such as an adler32 or a crc32 be applied to the uncompressed data as is done in the zlib, gzip, and zip formats. For - most applications, the zlib format should be used as is. Note that comments + most applications, the zlib format should be used as is. Note that comments above on the use in deflateInit2() applies to the magnitude of windowBits. - windowBits can also be greater than 15 for optional gzip decoding. Add + windowBits can also be greater than 15 for optional gzip decoding. Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will - return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is - a crc32 instead of an adler32. + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg - is set to null if there is no error message. inflateInit2 does not perform - any decompression apart from reading the zlib header if present: this will - be done by inflate(). (So next_in and avail_in may be modified, but next_out - and avail_out are unchanged.) + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, @@ -740,36 +821,56 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, uInt dictLength)); /* Initializes the decompression dictionary from the given uncompressed byte - sequence. This function must be called immediately after a call of inflate, - if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor can be determined from the adler32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see - deflateSetDictionary). For raw inflate, this function can be called - immediately after inflateInit2() or inflateReset() and before any call of - inflate() to set the dictionary. The application must insure that the - dictionary that was used for compression is provided. + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect adler32 value). inflateSetDictionary does not + expected one (incorrect adler32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ -ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); /* - Skips invalid compressed data until a full flush point (see above the - description of deflate with Z_FULL_FLUSH) can be found, or until all - available input is skipped. No output is provided. + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ - inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR - if no more input was provided, Z_DATA_ERROR if no flush point has been found, - or Z_STREAM_ERROR if the stream structure was inconsistent. In the success - case, the application may save the current current value of total_in which - indicates where valid compressed data was found. In the error case, the - application may repeatedly call inflateSync, providing more input each time, - until success or end of the input data. +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. */ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, @@ -784,18 +885,30 @@ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and + (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); /* This function is equivalent to inflateEnd followed by inflateInit, - but does not free and reallocate all the internal decompression state. - The stream will keep attributes that may have been set by inflateInit2. + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. - inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. */ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, @@ -803,54 +916,87 @@ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, int value)); /* This function inserts bits in the inflate input stream. The intent is - that this function is used to start inflating at a bit position in the - middle of a byte. The provided bits will be used before any bytes are used - from next_in. This function should only be used with raw inflate, and - should be used before the first inflate() call after inflateInit2() or - inflateReset(). bits must be less than or equal to 16, and that many of the - least significant bits of value will be inserted in the input. - - inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, gz_headerp head)); /* - inflateGetHeader() requests that gzip header information be stored in the + inflateGetHeader() requests that gzip header information be stored in the provided gz_header structure. inflateGetHeader() may be called after inflateInit2() or inflateReset(), and before the first call of inflate(). As inflate() processes the gzip stream, head->done is zero until the header is completed, at which time head->done is set to one. If a zlib stream is being decoded, then head->done is set to -1 to indicate that there will be - no gzip header information forthcoming. Note that Z_BLOCK can be used to - force inflate() to return immediately after header processing is complete - and before any actual data is decompressed. + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. - The text, time, xflags, and os fields are filled in with the gzip header + The text, time, xflags, and os fields are filled in with the gzip header contents. hcrc is set to true if there is a header CRC. (The header CRC - was valid if done is set to one.) If extra is not Z_NULL, then extra_max + was valid if done is set to one.) If extra is not Z_NULL, then extra_max contains the maximum number of bytes to write to extra. Once done is true, extra_len contains the actual extra field length, and extra contains the extra field, or that field truncated if extra_max is less than extra_len. If name is not Z_NULL, then up to name_max characters are written there, terminated with a zero unless the length is greater than name_max. If comment is not Z_NULL, then up to comm_max characters are written there, - terminated with a zero unless the length is greater than comm_max. When - any of extra, name, or comment are not Z_NULL and the respective field is - not present in the header, then that field is set to Z_NULL to signal its + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its absence. This allows the use of deflateSetHeader() with the returned structure to duplicate the header. However if those fields are set to allocated memory, then the application will need to save those pointers elsewhere so that they can be eventually freed. - If inflateGetHeader is not used, then the header information is simply + If inflateGetHeader is not used, then the header information is simply discarded. The header is always checked for validity, including the header CRC if present. inflateReset() will reset the process to discard the header information. The application would need to call inflateGetHeader() again to retrieve the header from the next gzip stream. - inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ @@ -871,12 +1017,13 @@ ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, See inflateBack() for the usage of these routines. inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of - the paramaters are invalid, Z_MEM_ERROR if the internal state could not - be allocated, or Z_VERSION_ERROR if the version of the library does not - match the version of the header file. + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. */ -typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, @@ -884,24 +1031,25 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, out_func out, void FAR *out_desc)); /* inflateBack() does a raw inflate with a single call using a call-back - interface for input and output. This is more efficient than inflate() for - file i/o applications in that it avoids copying between the output and the - sliding window by simply making the window itself the output buffer. This - function trusts the application to not change the output buffer passed by - the output function, at least until inflateBack() returns. + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. inflateBackInit() must be called first to allocate the internal state and to initialize the state with the user-provided window buffer. inflateBack() may then be used multiple times to inflate a complete, raw - deflate stream with each call. inflateBackEnd() is then called to free - the allocated state. + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. A raw deflate stream is one with no zlib or gzip header or trailer. This routine would normally be used in a utility that reads zip or gzip files and writes out uncompressed files. The utility would decode the - header and process the trailer on its own, hence this routine expects - only the raw deflate stream to decompress. This is different from the - normal behavior of inflate(), which expects either a zlib or gzip header and + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and trailer around the deflate stream. inflateBack() uses two subroutines supplied by the caller that are then @@ -927,7 +1075,7 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in must also be initialized, and then if strm->avail_in is not zero, input will - initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. The in_desc and out_desc parameters of inflateBack() is passed as the first parameter of in() and out() respectively when they are called. These @@ -937,15 +1085,15 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, On return, inflateBack() will set strm->next_in and strm->avail_in to pass back any unused input that was provided by the last in() call. The return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR - if in() or out() returned an error, Z_DATA_ERROR if there was a format - error in the deflate stream (in which case strm->msg is set to indicate the - nature of the error), or Z_STREAM_ERROR if the stream was not properly - initialized. In the case of Z_BUF_ERROR, an input or output error can be - distinguished using strm->next_in which will be Z_NULL only if in() returned - an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to - out() returning non-zero. (in() will always be called before out(), so - strm->next_in is assured to be defined if out() returns non-zero.) Note - that inflateBack() cannot return Z_OK. + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. */ ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); @@ -997,27 +1145,27 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); 27-31: 0 (reserved) */ +#ifndef Z_SOLO /* utility functions */ /* - The following utility functions are implemented on top of the - basic stream-oriented functions. To simplify the interface, some - default options are assumed (compression level and memory usage, - standard memory allocation functions). The source code of these - utility functions can easily be modified if you need special options. + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. */ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Compresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be at least the value returned - by compressBound(sourceLen). Upon exit, destLen is the actual size of the + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed buffer. - This function can be used to compress a whole file at once if the - input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer. @@ -1027,11 +1175,11 @@ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level)); /* - Compresses the source buffer into the destination buffer. The level + Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte - length of the source buffer. Upon entry, destLen is the total size of the + length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by - compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed buffer. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough @@ -1042,159 +1190,255 @@ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); /* compressBound() returns an upper bound on the compressed size after - compress() or compress2() on sourceLen bytes. It would be used before - a compress() or compress2() call to allocate the destination buffer. + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. */ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be large enough to hold the - entire uncompressed data. (The size of the uncompressed data must have - been saved previously by the compressor and transmitted to the decompressor - by some mechanism outside the scope of this compression library.) - Upon exit, destLen is the actual size of the compressed buffer. - This function can be used to decompress a whole file at once if the - input file is mmap'ed. + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. */ + /* gzip file access functions */ -typedef voidp gzFile; +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ -ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); /* - Opens a gzip (.gz) file for reading or writing. The mode parameter - is as in fopen ("rb" or "wb") but can also include a compression level - ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for - Huffman only compression as in "wb1h", or 'R' for run-length encoding - as in "wb1R". (See the description of deflateInit2 for more information - about the strategy parameter.) +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. gzopen can be used to read a file which is not in gzip format; in this - case gzread will directly read from the file without decompression. + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ - gzopen returns NULL if the file could not be opened or if there was - insufficient memory to allocate the (de)compression state; errno - can be checked to distinguish the two cases (if errno is zero, the - zlib error is Z_MEM_ERROR). */ +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ -ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); /* - gzdopen() associates a gzFile with the file descriptor fd. File - descriptors are obtained from calls like open, dup, creat, pipe or - fileno (in the file has been previously opened with fopen). - The mode parameter is as in gzopen. - The next call of gzclose on the returned gzFile will also close the - file descriptor fd, just like fclose(fdopen(fd), mode) closes the file - descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). - gzdopen returns NULL if there was insufficient memory to allocate - the (de)compression state. + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. */ ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); /* - Dynamically update the compression level or strategy. See the description + Dynamically update the compression level or strategy. See the description of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not opened for writing. */ -ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); /* - Reads the given number of uncompressed bytes from the compressed file. - If the input file was not in gzip format, gzread copies the given number - of bytes into the buffer. - gzread returns the number of uncompressed bytes actually read (0 for - end of file, -1 for error). */ + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ -ZEXTERN int ZEXPORT gzwrite OF((gzFile file, - voidpc buf, unsigned len)); +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); /* Writes the given number of uncompressed bytes into the compressed file. - gzwrite returns the number of uncompressed bytes actually written - (0 in case of error). + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. */ -ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); /* - Converts, formats, and writes the args to the compressed file under - control of the format string, as in fprintf. gzprintf returns the number of - uncompressed bytes actually written (0 in case of error). The number of - uncompressed bytes written is limited to 4095. The caller should assure that - this limit is not exceeded. If it is exceeded, then gzprintf() will return - return an error (0) with nothing written. In this case, there may also be a - buffer overflow with unpredictable consequences, which is possible only if - zlib was compiled with the insecure functions sprintf() or vsprintf() - because the secure snprintf() or vsnprintf() functions were not available. + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). */ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); /* - Writes the given null-terminated string to the compressed file, excluding + Writes the given null-terminated string to the compressed file, excluding the terminating null character. - gzputs returns the number of characters written, or -1 in case of error. + + gzputs returns the number of characters written, or -1 in case of error. */ ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); /* - Reads bytes from the compressed file until len-1 characters are read, or - a newline character is read and transferred to buf, or an end-of-file - condition is encountered. The string is then terminated with a null - character. - gzgets returns buf, or Z_NULL in case of error. + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. */ -ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); /* - Writes c, converted to an unsigned char, into the compressed file. - gzputc returns the value that was written, or -1 in case of error. + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. */ -ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); /* - Reads one byte from the compressed file. gzgetc returns this byte - or -1 in case of end of file or error. + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. */ -ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); /* - Push one character back onto the stream to be read again later. - Only one character of push-back is allowed. gzungetc() returns the - character pushed, or -1 on failure. gzungetc() will fail if a - character has been pushed but not read yet, or if c is -1. The pushed - character will be discarded if the stream is repositioned with gzseek() - or gzrewind(). + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). */ -ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); /* - Flushes all pending output into the compressed file. The parameter - flush is as in the deflate() function. The return value is the zlib - error number (see function gzerror below). gzflush returns Z_OK if - the flush parameter is Z_FINISH and all output could be flushed. - gzflush should be called only when strictly necessary because it can - degrade compression. + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. */ -ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, - z_off_t offset, int whence)); /* - Sets the starting position for the next gzread or gzwrite on the - given compressed file. The offset represents a number of bytes in the - uncompressed data stream. The whence parameter is defined as in lseek(2); +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be - extremely slow. If the file is opened for writing, only forward seeks are + extremely slow. If the file is opened for writing, only forward seeks are supported; gzseek then compresses a sequence of zeroes up to the new starting position. - gzseek returns the resulting offset location as measured in bytes from + gzseek returns the resulting offset location as measured in bytes from the beginning of the uncompressed stream, or -1 in case of error, in particular if the file is opened for writing and the new starting position would be before the current position. @@ -1204,68 +1448,134 @@ ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); /* Rewinds the given file. This function is supported only for reading. - gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) */ +/* ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + /* - Returns the starting position for the next gzread or gzwrite on the - given compressed file. This position represents a number of bytes in the - uncompressed data stream. +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); - gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. */ ZEXTERN int ZEXPORT gzeof OF((gzFile file)); /* - Returns 1 when EOF has previously been detected reading the given - input stream, otherwise zero. + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. */ ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); /* - Returns 1 if file is being read directly without decompression, otherwise - zero. + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) */ ZEXTERN int ZEXPORT gzclose OF((gzFile file)); /* - Flushes all pending output if necessary, closes the compressed file - and deallocates all the (de)compression state. The return value is the zlib - error number (see function gzerror below). + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. */ ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); /* - Returns the error message for the last error which occurred on the - given compressed file. errnum is set to zlib error number. If an - error occurred in the file system and not in the compression library, - errnum is set to Z_ERRNO and the application may consult errno - to get the exact error code. + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. */ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); /* - Clears the error and end-of-file flags for file. This is analogous to the - clearerr() function in stdio. This is useful for continuing to read a gzip + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip file that is being written concurrently. */ +#endif /* !Z_SOLO */ + /* checksum functions */ /* These functions are not related to compression but are exported - anyway because they might be useful in applications using the - compression library. + anyway because they might be useful in applications using the compression + library. */ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and - return the updated checksum. If buf is NULL, this function returns - the required initial value for the checksum. - An Adler-32 checksum is almost as reliable as a CRC32 but can be computed - much faster. Usage example: + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: uLong adler = adler32(0L, Z_NULL, 0); @@ -1275,21 +1585,25 @@ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); if (adler != original_adler) error(); */ +/* ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, z_off_t len2)); -/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of - seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. */ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the - updated CRC-32. If buf is NULL, this function returns the required initial - value for the for the crc. Pre- and post-conditioning (one's complement) is + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is performed within this function so it shouldn't be done by the application. + Usage example: uLong crc = crc32(0L, Z_NULL, 0); @@ -1300,9 +1614,9 @@ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); if (crc != original_crc) error(); */ +/* ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); -/* Combine two CRC-32 check values into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, CRC-32 check values were calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 @@ -1331,26 +1645,121 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, const char *version, int stream_size)); #define deflateInit(strm, level) \ - deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) #define inflateInit(strm) \ - inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) #define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, sizeof(z_stream)) + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) #define inflateInit2(strm, windowBits) \ - inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) #define inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ - ZLIB_VERSION, sizeof(z_stream)) + ZLIB_VERSION, (int)sizeof(z_stream)) + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#endif +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* hack for buggy compilers */ #if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) - struct internal_state {int dummy;}; /* hack for buggy compilers */ + struct internal_state {int dummy;}; #endif +/* undocumented functions */ ZEXTERN const char * ZEXPORT zError OF((int)); -ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); -ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if defined(_WIN32) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif #ifdef __cplusplus } diff --git a/erts/emulator/zlib/zutil.c b/erts/emulator/zlib/zutil.c index fa5b43126a..27a8af4a2b 100644 --- a/erts/emulator/zlib/zutil.c +++ b/erts/emulator/zlib/zutil.c @@ -1,22 +1,23 @@ /* zutil.c -- target dependent utility functions for the compression library - * Copyright (C) 1995-2005 Jean-loup Gailly. + * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* @(#) $Id$ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "zutil.h" +#ifndef Z_SOLO +# include "gzguts.h" +#endif #ifndef NO_DUMMY_DECL struct internal_state {int dummy;}; /* for buggy compilers */ #endif -const char * const z_errmsg[10] = { +z_const char * const z_errmsg[10] = { "need dictionary", /* Z_NEED_DICT 2 */ "stream end", /* Z_STREAM_END 1 */ "", /* Z_OK 0 */ @@ -39,25 +40,25 @@ uLong ZEXPORT zlibCompileFlags() uLong flags; flags = 0; - switch (sizeof(uInt)) { + switch ((int)(sizeof(uInt))) { case 2: break; case 4: flags += 1; break; case 8: flags += 2; break; default: flags += 3; } - switch (sizeof(uLong)) { + switch ((int)(sizeof(uLong))) { case 2: break; case 4: flags += 1 << 2; break; case 8: flags += 2 << 2; break; default: flags += 3 << 2; } - switch (sizeof(voidpf)) { + switch ((int)(sizeof(voidpf))) { case 2: break; case 4: flags += 1 << 4; break; case 8: flags += 2 << 4; break; default: flags += 3 << 4; } - switch (sizeof(z_off_t)) { + switch ((int)(sizeof(z_off_t))) { case 2: break; case 4: flags += 1 << 6; break; case 8: flags += 2 << 6; break; @@ -90,27 +91,27 @@ uLong ZEXPORT zlibCompileFlags() #ifdef FASTEST flags += 1L << 21; #endif -#ifdef STDC +#if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifdef NO_vsnprintf - flags += 1L << 25; + flags += 1L << 25; # ifdef HAS_vsprintf_void - flags += 1L << 26; + flags += 1L << 26; # endif # else # ifdef HAS_vsnprintf_void - flags += 1L << 26; + flags += 1L << 26; # endif # endif #else - flags += 1L << 24; + flags += 1L << 24; # ifdef NO_snprintf - flags += 1L << 25; + flags += 1L << 25; # ifdef HAS_sprintf_void - flags += 1L << 26; + flags += 1L << 26; # endif # else # ifdef HAS_snprintf_void - flags += 1L << 26; + flags += 1L << 26; # endif # endif #endif @@ -122,9 +123,9 @@ uLong ZEXPORT zlibCompileFlags() # ifndef verbose # define verbose 0 # endif -int z_verbose = verbose; +int ZLIB_INTERNAL z_verbose = verbose; -void z_error (m) +void ZLIB_INTERNAL z_error (m) char *m; { fprintf(stderr, "%s\n", m); @@ -151,7 +152,7 @@ const char * ZEXPORT zError(err) #ifndef HAVE_MEMCPY -void zmemcpy(dest, source, len) +void ZLIB_INTERNAL zmemcpy(dest, source, len) Bytef* dest; const Bytef* source; uInt len; @@ -162,7 +163,7 @@ void zmemcpy(dest, source, len) } while (--len != 0); } -int zmemcmp(s1, s2, len) +int ZLIB_INTERNAL zmemcmp(s1, s2, len) const Bytef* s1; const Bytef* s2; uInt len; @@ -175,7 +176,7 @@ int zmemcmp(s1, s2, len) return 0; } -void zmemzero(dest, len) +void ZLIB_INTERNAL zmemzero(dest, len) Bytef* dest; uInt len; { @@ -186,6 +187,7 @@ void zmemzero(dest, len) } #endif +#ifndef Z_SOLO #ifdef SYS16BIT @@ -218,7 +220,7 @@ local ptr_table table[MAX_PTR]; * a protected system like OS/2. Use Microsoft C instead. */ -voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) { voidpf buf = opaque; /* just to make some compilers happy */ ulg bsize = (ulg)items*size; @@ -242,7 +244,7 @@ voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) return buf; } -void zcfree (voidpf opaque, voidpf ptr) +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { int n; if (*(ush*)&ptr != 0) { /* object < 64K */ @@ -277,13 +279,13 @@ void zcfree (voidpf opaque, voidpf ptr) # define _hfree hfree #endif -voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) { if (opaque) opaque = 0; /* to make compiler happy */ return _halloc((long)items, size); } -void zcfree (voidpf opaque, voidpf ptr) +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { if (opaque) opaque = 0; /* to make compiler happy */ _hfree(ptr); @@ -302,26 +304,24 @@ extern voidp calloc OF((uInt items, uInt size)); extern void free OF((voidpf ptr)); #endif -extern void* sys_alloc(unsigned); -extern void* sys_free(void *); - -voidpf zcalloc (opaque, items, size) +voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) voidpf opaque; unsigned items; unsigned size; { - unsigned sz = items * size; - voidpf* ptr = (voidpf) sys_alloc(sz); if (opaque) items += size - size; /* make compiler happy */ - return ptr; + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); } -void zcfree (opaque, ptr) +void ZLIB_INTERNAL zcfree (opaque, ptr) voidpf opaque; voidpf ptr; { - sys_free(ptr); + free(ptr); if (opaque) return; /* make compiler happy */ } #endif /* MY_ZCALLOC */ + +#endif /* !Z_SOLO */ diff --git a/erts/emulator/zlib/zutil.h b/erts/emulator/zlib/zutil.h index a8872e1c88..24ab06b1cf 100644 --- a/erts/emulator/zlib/zutil.h +++ b/erts/emulator/zlib/zutil.h @@ -1,10 +1,8 @@ /* zutil.h -- internal interface and configuration of the compression library - * Copyright (C) 1995-2005 Jean-loup Gailly. + * Copyright (C) 1995-2013 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ -/* %ExternalCopyright% */ - /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. @@ -15,30 +13,24 @@ #ifndef ZUTIL_H #define ZUTIL_H -#define ZLIB_INTERNAL +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + #include "zlib.h" -#ifdef STDC -# ifndef _WIN32_WCE +#if defined(STDC) && !defined(Z_SOLO) +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) # include # endif # include # include #endif -#ifdef NO_ERRNO_H -# ifdef _WIN32_WCE - /* The Microsoft C Run-Time Library for Windows CE doesn't have - * errno. We define it as a global variable to simplify porting. - * Its value is always 0 and should not be used. We rename it to - * avoid conflict with other libraries that use the same workaround. - */ -# define errno z_errno -# endif - extern int errno; -#else -# ifndef _WIN32_WCE -# include -# endif + +#ifdef Z_SOLO + typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ #endif #ifndef local @@ -52,13 +44,13 @@ typedef unsigned short ush; typedef ush FAR ushf; typedef unsigned long ulg; -extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ #define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] #define ERR_RETURN(strm,err) \ - return (strm->msg = (char*)ERR_MSG(err), (err)) + return (strm->msg = ERR_MSG(err), (err)) /* To be used only when the state is known to be valid */ /* common constants */ @@ -90,16 +82,18 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) # define OS_CODE 0x00 -# if defined(__TURBOC__) || defined(__BORLANDC__) -# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) - /* Allow compilation with ANSI keywords only enabled */ - void _Cdecl farfree( void *block ); - void *_Cdecl farmalloc( unsigned long nbytes ); -# else -# include +# ifndef Z_SOLO +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include # endif -# else /* MSC or DJGPP */ -# include # endif #endif @@ -119,18 +113,20 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #ifdef OS2 # define OS_CODE 0x06 -# ifdef M_I86 - #include +# if defined(M_I86) && !defined(Z_SOLO) +# include # endif #endif #if defined(MACOS) || defined(TARGET_OS_MAC) # define OS_CODE 0x07 -# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os -# include /* for fdopen */ -# else -# ifndef fdopen -# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef Z_SOLO +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif # endif # endif #endif @@ -142,7 +138,6 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #ifdef WIN32 # ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ # define OS_CODE 0x0b -# define F_OPEN(name, mode) _wfopen((WCHAR *)(name), (WCHAR *)(mode)) /* Unicode */ # endif #endif @@ -154,7 +149,7 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # define fdopen(fd,mode) NULL /* No fdopen() */ #endif -#if (defined(_MSC_VER) && (_MSC_VER > 600)) +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX # if defined(_WIN32_WCE) # define fdopen(fd,mode) NULL /* No fdopen() */ # ifndef _PTRDIFF_T_DEFINED @@ -166,6 +161,19 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # endif #endif +#if defined(__BORLANDC__) && !defined(MSDOS) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_WIN32) && \ + (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + /* common defaults */ #ifndef OS_CODE @@ -178,40 +186,7 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* functions */ -#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) -# ifndef HAVE_VSNPRINTF -# define HAVE_VSNPRINTF -# endif -#endif -#if defined(__CYGWIN__) -# ifndef HAVE_VSNPRINTF -# define HAVE_VSNPRINTF -# endif -#endif -#ifndef HAVE_VSNPRINTF -# ifdef MSDOS - /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), - but for now we just assume it doesn't. */ -# define NO_vsnprintf -# endif -# ifdef __TURBOC__ -# define NO_vsnprintf -# endif -# ifdef WIN32 - /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ -# if !defined(vsnprintf) && !defined(NO_vsnprintf) -# define vsnprintf _vsnprintf -# endif -# endif -# ifdef __SASC -# define NO_vsnprintf -# endif -#endif -#ifdef VMS -# define NO_vsnprintf -#endif - -#if defined(pyr) +#if defined(pyr) || defined(Z_SOLO) # define NO_MEMCPY #endif #if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) @@ -235,16 +210,16 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # define zmemzero(dest, len) memset(dest, 0, len) # endif #else - extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); - extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); - extern void zmemzero OF((Bytef* dest, uInt len)); + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); #endif /* Diagnostic functions */ #ifdef DEBUG # include - extern int z_verbose; - extern void z_error OF((char *m)); + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); # define Assert(cond,msg) {if(!(cond)) z_error(msg);} # define Trace(x) {if (z_verbose>=0) fprintf x ;} # define Tracev(x) {if (z_verbose>0) fprintf x ;} @@ -260,13 +235,19 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # define Tracecv(c,x) #endif - -voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); -void zcfree OF((voidpf opaque, voidpf ptr)); +#ifndef Z_SOLO + voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); + void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); +#endif #define ZALLOC(strm, items, size) \ (*((strm)->zalloc))((strm)->opaque, (items), (size)) #define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) #define TRY_FREE(s, p) {if (p) ZFREE(s, p);} +/* Reverse the bytes in a 32-bit value */ +#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + #endif /* ZUTIL_H */ -- cgit v1.2.3 From 8a147a73651713efebb9ac973f618a6d66685eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 10 Jan 2014 12:52:20 +0100 Subject: Don't make gzio.c dependent on the zutil.h header file gzio.c is our own replacement for zlib's gzopen() etc (based on a version of gzio.c that was included in an old version of zlib). Unfortunately, gzio.c still depends on the *internal* zlib header file zutil.h which is not supposed to be used outside of the zlib source code. The dependencies are the use of the gzFile typedef and the F_OPEN() macro. Instead of gzFile, define and use our own ErtsGzFile. To get rid of the F_OPEN() macro, call open() of _wfopen() directly. --- erts/emulator/drivers/common/efile_drv.c | 15 ++++----- erts/emulator/drivers/common/gzio.c | 53 +++++++++++++++---------------- erts/emulator/drivers/common/gzio.h | 16 ++++++---- erts/emulator/drivers/common/gzio_zutil.h | 9 ------ 4 files changed, 43 insertions(+), 50 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 8de578d8b7..dca979c13a 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -111,7 +111,6 @@ #include "erl_driver.h" #include "erl_efile.h" #include "erl_threads.h" -#include "zlib.h" #include "gzio.h" #include "dtrace-wrapper.h" #include @@ -818,7 +817,7 @@ file_start(ErlDrvPort port, char* command) static void do_close(int flags, SWord fd) { if (flags & EFILE_COMPRESSED) { - erts_gzclose((gzFile)(fd)); + erts_gzclose((ErtsGzFile)(fd)); } else { efile_closefile((int) fd); } @@ -1136,7 +1135,7 @@ static void invoke_read(void *data) } read_size = size; if (d->flags & EFILE_COMPRESSED) { - read_size = erts_gzread((gzFile)d->fd, + read_size = erts_gzread((ErtsGzFile)d->fd, d->c.read.binp->orig_bytes + d->c.read.bin_offset, size); status = (read_size != (size_t) -1); @@ -1209,7 +1208,7 @@ static void invoke_read_line(void *data) size = need - d->c.read_line.read_size; } if (d->flags & EFILE_COMPRESSED) { - read_size = erts_gzread((gzFile)d->fd, + read_size = erts_gzread((ErtsGzFile)d->fd, d->c.read_line.binp->orig_bytes + d->c.read_line.read_offset + d->c.read_line.read_size, size); @@ -1250,7 +1249,7 @@ static void invoke_read_line(void *data) d->c.read_line.read_size -= too_much; ASSERT(d->c.read_line.read_size >= 0); if (d->flags & EFILE_COMPRESSED) { - Sint64 location = erts_gzseek((gzFile)d->fd, + Sint64 location = erts_gzseek((ErtsGzFile)d->fd, -((Sint64) too_much), EFILE_SEEK_CUR); if (location == -1) { d->result_ok = 0; @@ -1535,7 +1534,7 @@ static void invoke_writev(void *data) { */ errno = EINVAL; if (! (status = - erts_gzwrite((gzFile)d->fd, + erts_gzwrite((ErtsGzFile)d->fd, iov[i].iov_base, iov[i].iov_len)) == iov[i].iov_len) { d->errInfo.posix_errno = @@ -1797,7 +1796,7 @@ static void invoke_lseek(void *data) d->errInfo.posix_errno = EINVAL; status = 0; } else { - d->c.lseek.location = erts_gzseek((gzFile)d->fd, + d->c.lseek.location = erts_gzseek((ErtsGzFile)d->fd, offset, d->c.lseek.origin); if (d->c.lseek.location == -1) { d->errInfo.posix_errno = errno; @@ -1885,7 +1884,7 @@ static void invoke_open(void *data) if (status || (d->errInfo.posix_errno != EISDIR)) { mode = (d->flags & EFILE_MODE_READ) ? "rb" : "wb"; d->fd = (SWord) erts_gzopen(d->b, mode); - if ((gzFile)d->fd) { + if ((ErtsGzFile)d->fd) { status = 1; } else { if (errno == 0) { diff --git a/erts/emulator/drivers/common/gzio.c b/erts/emulator/drivers/common/gzio.c index e085c262b0..653f3954b1 100644 --- a/erts/emulator/drivers/common/gzio.c +++ b/erts/emulator/drivers/common/gzio.c @@ -77,7 +77,7 @@ typedef struct gz_stream { * this structure. */ } gz_stream; -local gzFile gz_open OF((const char *path, const char *mode)); +local ErtsGzFile gz_open OF((const char *path, const char *mode)); local int get_byte OF((gz_stream *s)); local void check_header OF((gz_stream *s)); local int destroy OF((gz_stream *s)); @@ -144,7 +144,7 @@ local uLong getLong OF((gz_stream *s)); can be checked to distinguish the two cases (if errno is zero, the zlib error is Z_MEM_ERROR). */ -local gzFile gz_open (path, mode) +local ErtsGzFile gz_open (path, mode) const char *path; const char *mode; { @@ -179,7 +179,7 @@ local gzFile gz_open (path, mode) s->path = (char*)ALLOC(FILENAME_BYTELEN(path)+FILENAME_CHARSIZE); if (s->path == NULL) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } FILENAME_COPY(s->path, path); /* do this early for debugging */ @@ -197,7 +197,7 @@ local gzFile gz_open (path, mode) } while (*p++ && m < fmode + sizeof(fmode) - 1); *m = '\0'; if (s->mode == '\0') - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; if (s->mode == 'w') { err = deflateInit2(&(s->stream), level, @@ -207,7 +207,7 @@ local gzFile gz_open (path, mode) s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); if (err != Z_OK || s->outbuf == Z_NULL) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } } else { /* @@ -221,7 +221,7 @@ local gzFile gz_open (path, mode) s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); if (err != Z_OK || s->inbuf == Z_NULL) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } } s->stream.avail_out = Z_BUFSIZE; @@ -229,17 +229,16 @@ local gzFile gz_open (path, mode) errno = 0; #if defined(FILENAMES_16BIT) { - char wfmode[160]; - int i=0,j; - for(j=0;fmode[j] != '\0';++j) { - wfmode[i++]=fmode[j]; - wfmode[i++]='\0'; + WCHAR wfmode[80]; + int i = 0; + int j; + for(j = 0; fmode[j] != '\0'; ++j) { + wfmode[i++] = (WCHAR) fmode[j]; } - wfmode[i++] = '\0'; - wfmode[i++] = '\0'; - s->file = F_OPEN(path, wfmode); + wfmode[i++] = L'\0'; + s->file = _wfopen((WCHAR *)path, wfmode); if (s->file == NULL) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } } #elif defined(UNIX) @@ -249,18 +248,18 @@ local gzFile gz_open (path, mode) s->file = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666); } if (s->file == -1) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } #else - s->file = F_OPEN(path, fmode); + s->file = fopen(path, fmode); if (s->file == NULL) { - return s->destroy(s), (gzFile)Z_NULL; + return s->destroy(s), (ErtsGzFile)Z_NULL; } #endif if (s->mode == 'r') { check_header(s); /* skip the .gz header */ } - return (gzFile)s; + return (ErtsGzFile)s; } /* =========================================================================== @@ -296,7 +295,7 @@ local int gz_rewind (gz_stream *s) /* =========================================================================== Opens a gzip (.gz) file for reading or writing. */ -gzFile erts_gzopen (path, mode) +ErtsGzFile erts_gzopen (path, mode) const char *path; const char *mode; { @@ -447,7 +446,7 @@ local int destroy (s) gzread returns the number of bytes actually read (0 for end of file). */ int -erts_gzread(gzFile file, voidp buf, unsigned len) +erts_gzread(ErtsGzFile file, voidp buf, unsigned len) { gz_stream *s = (gz_stream*)file; Bytef *start = buf; /* starting point for crc computation */ @@ -557,7 +556,7 @@ erts_gzread(gzFile file, voidp buf, unsigned len) gzwrite returns the number of bytes actually written (0 in case of error). */ int -erts_gzwrite(gzFile file, voidp buf, unsigned len) +erts_gzwrite(ErtsGzFile file, voidp buf, unsigned len) { gz_stream *s = (gz_stream*)file; @@ -593,7 +592,7 @@ erts_gzwrite(gzFile file, voidp buf, unsigned len) */ int -erts_gzseek(gzFile file, int offset, int whence) +erts_gzseek(ErtsGzFile file, int offset, int whence) { int pos; gz_stream* s = (gz_stream *) file; @@ -655,7 +654,7 @@ erts_gzseek(gzFile file, int offset, int whence) degrade compression. */ int -erts_gzflush(gzFile file, int flush) +erts_gzflush(ErtsGzFile file, int flush) { uInt len; int done = 0; @@ -714,7 +713,7 @@ local uLong getLong (s) and deallocates all the (de)compression state. */ int -erts_gzclose(gzFile file) +erts_gzclose(ErtsGzFile file) { int err; gz_stream *s = (gz_stream*)file; @@ -723,9 +722,9 @@ erts_gzclose(gzFile file) if (s->mode == 'w') { err = erts_gzflush (file, Z_FINISH); - if (err != Z_OK) return s->destroy(file); + if (err != Z_OK) return s->destroy(s); } - return s->destroy(file); + return s->destroy(s); } diff --git a/erts/emulator/drivers/common/gzio.h b/erts/emulator/drivers/common/gzio.h index 3f1e546140..ea50d922ec 100644 --- a/erts/emulator/drivers/common/gzio.h +++ b/erts/emulator/drivers/common/gzio.h @@ -17,11 +17,15 @@ * %CopyrightEnd% */ -gzFile erts_gzopen (const char *path, const char *mode); -int erts_gzread(gzFile file, voidp buf, unsigned len); -int erts_gzwrite(gzFile file, voidp buf, unsigned len); -int erts_gzseek(gzFile, int, int); -int erts_gzflush(gzFile file, int flush); -int erts_gzclose(gzFile file); +#include "zlib.h" + +typedef struct erts_gzFile* ErtsGzFile; + +ErtsGzFile erts_gzopen (const char *path, const char *mode); +int erts_gzread(ErtsGzFile file, voidp buf, unsigned len); +int erts_gzwrite(ErtsGzFile file, voidp buf, unsigned len); +int erts_gzseek(ErtsGzFile, int, int); +int erts_gzflush(ErtsGzFile file, int flush); +int erts_gzclose(ErtsGzFile file); ErlDrvBinary* erts_gzinflate_buffer(char*, uLong); ErlDrvBinary* erts_gzdeflate_buffer(char*, uLong); diff --git a/erts/emulator/drivers/common/gzio_zutil.h b/erts/emulator/drivers/common/gzio_zutil.h index 00eccc80fc..854205cc2c 100644 --- a/erts/emulator/drivers/common/gzio_zutil.h +++ b/erts/emulator/drivers/common/gzio_zutil.h @@ -23,12 +23,6 @@ * that may change or not exist at all. */ -#ifndef HAVE_LIBZ -/* Use our "real" copy of zutil.h if we don't use shared zlib */ -#include "zutil.h" - -#else /* HAVE_LIBZ: Shared zlib is used */ - #define local static #define DEF_MEM_LEVEL 8 #define zmemcpy sys_memcpy @@ -77,6 +71,3 @@ # define OS_CODE 0x03 /* assume Unix */ #endif - -#endif /* HAVE_LIBZ */ - -- cgit v1.2.3 From 615f45929df6ed4689904c3c463d11640cda2cde Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 13 Jan 2014 11:44:20 +0100 Subject: New version number does not start with an R --- erts/test/z_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl index da72b18f05..d9892f21c3 100644 --- a/erts/test/z_SUITE.erl +++ b/erts/test/z_SUITE.erl @@ -116,7 +116,7 @@ find_cerl(false) -> end; find_cerl(DBTop) -> case catch filelib:wildcard(filename:join([DBTop, - "otp_src_R*", + "otp_src_*", "bin", "cerl"])) of [Cerl | _ ] -> -- cgit v1.2.3 From 3645c345ac88305140b3474fbbbca5c47236933c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Tue, 12 Nov 2013 16:58:02 +0100 Subject: epmd: Fix -names option on Windows Since 3aa60cc `epmd -names` does not produce any output on Windows anymore. This patch uses fwrite() instead of write() which adds the necessary carriage returns to the output so that it is suitable for the Windows cmd.exe. A test case is added (fails on Windows without the patch). --- erts/epmd/src/epmd_cli.c | 4 ++-- erts/epmd/test/epmd_SUITE.erl | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c index 8817bde8d7..bd30bc35d9 100644 --- a/erts/epmd/src/epmd_cli.c +++ b/erts/epmd/src/epmd_cli.c @@ -118,7 +118,7 @@ void epmd_call(EpmdVars *g,int what) if (!g->silent) { rval = erts_snprintf(buf, OUTBUF_SIZE, "epmd: up and running on port %d with data:\n", j); - write(1, buf, rval); + fwrite(buf, 1, rval, stdout); } while(1) { if ((rval = read(fd,buf,OUTBUF_SIZE)) <= 0) { @@ -126,7 +126,7 @@ void epmd_call(EpmdVars *g,int what) epmd_cleanup_exit(g,0); } if (!g->silent) - write(1, buf, rval); /* Potentially UTF-8 encoded */ + fwrite(buf, 1, rval, stdout); /* Potentially UTF-8 encoded */ } } diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index cc24a556a3..a752abf33b 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -69,6 +69,8 @@ returns_valid_empty_extra/1, returns_valid_populated_extra_with_nulls/1, + names_stdout/1, + buffer_overrun_1/1, buffer_overrun_2/1, no_nonlocal_register/1, @@ -118,6 +120,7 @@ all() -> too_large, alive_req_too_small_1, alive_req_too_small_2, alive_req_too_large, returns_valid_empty_extra, returns_valid_populated_extra_with_nulls, + names_stdout, {group, buffer_overrun}, no_nonlocal_register, no_nonlocal_kill, no_live_killing]. @@ -759,6 +762,24 @@ returns_valid_populated_extra_with_nulls(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +names_stdout(doc) -> + ["Test that epmd -names prints registered nodes to stdout"]; +names_stdout(suite) -> + []; +names_stdout(Config) when is_list(Config) -> + ?line ok = epmdrun(), + ?line {ok,Sock} = register_node("foobar"), + ?line ok = epmdrun("-names"), + ?line {ok, Data} = receive {_Port, {data, D}} -> {ok, D} + after 10000 -> {error, timeout} + end, + ?line {match,_} = re:run(Data, "^epmd: up and running", [multiline]), + ?line {match,_} = re:run(Data, "^name foobar at port", [multiline]), + ?line ok = close(Sock), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + buffer_overrun_1(suite) -> []; buffer_overrun_1(doc) -> @@ -968,7 +989,7 @@ epmdrun(Epmd,Args0) -> O -> " "++O end, - osrun("\"" ++ Epmd ++ "\"" ++ Args ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT)). + osrun("\"" ++ Epmd ++ "\"" ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT) ++ Args). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From 1a8fda28b3fdb48fb080a49a001ca167ba8834c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 14 Jan 2014 14:37:15 +0100 Subject: Update system version --- erts/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index 9afd46b961..0355901877 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # VSN = 5.10.4 -SYSTEM_VSN = R16B03 +SYSTEM_VSN = R16B03-1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 139a226d7a927dc46dea312d60b4beed75516bcd Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 14 Jan 2014 18:25:44 +0100 Subject: cerl: Fix target detection on freebsd --- erts/etc/unix/cerl.src | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'erts') diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index be8343e87e..78fefbea55 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -86,6 +86,7 @@ run_valgrind=no # Default rootdir ROOTDIR=%SRC_ROOTDIR% BINDIR="$ROOTDIR/bin/`$ROOTDIR/erts/autoconf/config.guess`" +TARGET=%TARGET% #BINDIR="$ROOTDIR/bin/%TARGET%" PROGNAME=$ROOTDIR/bin/cerl EMU=beam @@ -248,6 +249,12 @@ while [ $# -gt 0 ]; do done +if [ ! -f $BINDIR/erlexec -a -f $ROOTDIR/bin/$TARGET/erlexec ]; then + # We are in a strange target (I'm looking at you openbsd) where + # TARGET != config.guess + BINDIR=$ROOTDIR/bin/$TARGET +fi + PATH=$BINDIR:$ROOTDIR/bin:$PATH EXEC=$BINDIR/erlexec -- cgit v1.2.3 From 8ff33cff02d01b2b4f20769cbd77c5ef23b01631 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Wed, 15 Jan 2014 10:13:11 -0500 Subject: remove deprecated driver_async_cancel function Some time ago the driver_async_cancel function was deprecated and slated for removal in R17. This commit removes the function along with its associated tests and documentation, sets the ERL_DRV_EXTENDED_MAJOR_VERSION to 3 and ERL_DRV_EXTENDED_MINOR_VERSION to 0, and modifies the sys_info_base_drv and sys_info_prev_drv tests in the driver test suite to check version 3.0 instead of 2.0. --- erts/doc/src/erl_driver.xml | 28 +++------------------- erts/emulator/beam/erl_async.c | 22 +---------------- erts/emulator/beam/erl_driver.h | 10 ++------ erts/emulator/sys/win32/erl_win_dyn_driver.h | 4 ---- .../emulator/test/driver_SUITE_data/ioq_exit_drv.c | 4 ---- .../emulator/test/driver_SUITE_data/otp_9302_drv.c | 2 -- .../test/driver_SUITE_data/sys_info_base_drv.c | 4 ++-- .../test/driver_SUITE_data/sys_info_prev_drv.c | 4 ++-- 8 files changed, 10 insertions(+), 68 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index b453a4861e..c2f7fa4588 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -745,7 +745,7 @@ typedef struct ErlIOVec { created and decrement it once when the port associated with the lock terminates. The emulator will also increment the reference count when an async job is enqueued and decrement - it after an async job has been invoked, or canceled. Besides + it after an async job has been invoked. Besides this, it is the responsibility of the driver to ensure that the reference count does not reach zero before the last use of the lock by the driver has been made. The reference count @@ -1995,14 +1995,12 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len async_invoke and async_free. It's typically a pointer to a structure that contains a pipe or event that can be used to signal that the async operation completed. - The data should be freed in async_free, because it's - called if driver_async_cancel is called.

+ The data should be freed in async_free.

When the async operation is done, ready_async driver entry function is called. If ready_async is null in the driver entry, the async_free function is called instead.

-

The return value is a handle to the asynchronous task, which - can be used as argument to driver_async_cancel.

+

The return value is a handle to the asynchronous task.

As of erts version 5.5.4.3 the default stack size for threads in the async-thread pool is 16 kilowords, @@ -2039,26 +2037,6 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len - - intdriver_async_cancel(long id) - Cancel an asynchronous call - - -

This function used to cancel a scheduled asynchronous operation, - if it was still in the queue. It returned 1 if it succeeded, and - 0 if it failed.

-

Since it could not guarantee success, it was more or less useless. - The user had to implement synchronization of cancellation anyway. - It also unnecessarily complicated the implementation. Therefore, - as of OTP-R15B driver_async_cancel() is deprecated, and - scheduled for removal in OTP-R17. It will currently always fail, - and return 0.

-

driver_async_cancel() is deprecated and will - be removed in the OTP-R17 release.

-
- - - intdriver_lock_driver(ErlDrvPort port) Make sure the driver is never unloaded diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index e6d72f569b..f0cec1c53c 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -602,7 +602,7 @@ unsigned int driver_async_port_key(ErlDrvPort port) ** return values: ** 0 completed ** -1 error -** N handle value (used with async_cancel) +** N handle value ** arguments: ** ix driver index ** key pointer to secedule queue (NULL means round robin) @@ -687,23 +687,3 @@ long driver_async(ErlDrvPort ix, unsigned int* key, return id; } - -int driver_async_cancel(unsigned int id) -{ - /* - * Not supported anymore. Always fail (which is backward - * compatible). - * - * This functionality could be implemented again. However, - * it is (and always has been) completely useless since - * it doesn't give you any guarantees whatsoever. The user - * needs to (and always have had to) synchronize in his/her - * own code in order to get any guarantees. - */ - return 0; -} - - - - - diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 5cffae92be..2bd3181bdc 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -132,8 +132,8 @@ typedef struct { #define DO_WRITE ERL_DRV_WRITE #define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed) -#define ERL_DRV_EXTENDED_MAJOR_VERSION 2 -#define ERL_DRV_EXTENDED_MINOR_VERSION 2 +#define ERL_DRV_EXTENDED_MAJOR_VERSION 3 +#define ERL_DRV_EXTENDED_MINOR_VERSION 0 /* * The emulator will refuse to load a driver with different major @@ -657,12 +657,6 @@ EXTERN long driver_async(ErlDrvPort ix, void* async_data, void (*async_free)(void*)); -/* - * driver_async_cancel() is deprecated. It is scheduled for removal - * in OTP-R16. For more information see the erl_driver(3) documentation. - */ -EXTERN int driver_async_cancel(unsigned int key) ERL_DRV_DEPRECATED_FUNC; - /* Locks the driver in the machine "forever", there is no unlock function. Note that this is almost never useful, as an open port towards the driver locks it until the port is closed, why unexpected diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h index b9a9838a36..4010d939e5 100644 --- a/erts/emulator/sys/win32/erl_win_dyn_driver.h +++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h @@ -82,7 +82,6 @@ WDD_TYPEDEF(int, erl_drv_send_term, (ErlDrvTermData, ErlDrvTermData, ErlDrvTermD WDD_TYPEDEF(int, driver_send_term, (ErlDrvPort, ErlDrvTermData, ErlDrvTermData*, int)); WDD_TYPEDEF(unsigned int, driver_async_port_key, (ErlDrvPort)); WDD_TYPEDEF(long, driver_async, (ErlDrvPort,unsigned int*,void (*)(void*),void*,void (*)(void*))); -WDD_TYPEDEF(int, driver_async_cancel, (unsigned int)); WDD_TYPEDEF(int, driver_lock_driver, (ErlDrvPort)); WDD_TYPEDEF(void *, driver_dl_open, (char *)); WDD_TYPEDEF(void *, driver_dl_sym, (void *, char *)); @@ -200,7 +199,6 @@ typedef struct { WDD_FTYPE(driver_send_term) *driver_send_term; WDD_FTYPE(driver_async_port_key) *driver_async_port_key; WDD_FTYPE(driver_async) *driver_async; - WDD_FTYPE(driver_async_cancel) *driver_async_cancel; WDD_FTYPE(driver_lock_driver) *driver_lock_driver; WDD_FTYPE(driver_dl_open) *driver_dl_open; WDD_FTYPE(driver_dl_sym) *driver_dl_sym; @@ -312,7 +310,6 @@ extern TWinDynDriverCallbacks WinDynDriverCallbacks; #define driver_send_term (WinDynDriverCallbacks.driver_send_term) #define driver_async_port_key (WinDynDriverCallbacks.driver_async_port_key) #define driver_async (WinDynDriverCallbacks.driver_async) -#define driver_async_cancel (WinDynDriverCallbacks.driver_async_cancel) #define driver_lock_driver (WinDynDriverCallbacks.driver_lock_driver) #define driver_dl_open (WinDynDriverCallbacks.driver_dl_open) #define driver_dl_sym (WinDynDriverCallbacks.driver_dl_sym) @@ -448,7 +445,6 @@ do { \ ((W).driver_send_term) = driver_send_term; \ ((W).driver_async_port_key) = driver_async_port_key; \ ((W).driver_async) = driver_async; \ -((W).driver_async_cancel) = driver_async_cancel; \ ((W).driver_lock_driver) = driver_lock_driver; \ ((W).driver_dl_open) = driver_dl_open; \ ((W).driver_dl_sym) = driver_dl_sym; \ 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 9d8bbac231..e2b338f801 100644 --- a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c +++ b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c @@ -277,10 +277,6 @@ static void stop(ErlDrvData drv_data) case IOQ_EXIT_TIMEOUT_ASYNC: driver_cancel_timer(ddp->port); break; - case IOQ_EXIT_READY_ASYNC: - if (ddp->outstanding_async_task) - driver_async_cancel(ddp->async_task); - break; default: break; } diff --git a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c index 88df73f696..7c144d20cf 100644 --- a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c +++ b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c @@ -227,6 +227,4 @@ static void output(ErlDrvData drv_data, ad[4]->term_data.msg = driver_mk_atom("end_of_jobs"); for (i = 0; i < sizeof(id)/sizeof(id[0]); i++) id[i] = driver_async(data->port, &key, async_invoke, ad[i], driver_free); - if (id[2] > 0) - driver_async_cancel(id[2]); } diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c index c22a415c59..e44c7dbd5e 100644 --- a/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c +++ b/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c @@ -19,14 +19,14 @@ /* * Author: Rickard Green * - * Description: Driver that fakes driver version 2.0 and tests + * Description: Driver that fakes driver version 3.0 and tests * driver_system_info(). * */ #include "sys_info_drv_impl.h" -#define SYS_INFO_DRV_MAJOR_VSN 2 +#define SYS_INFO_DRV_MAJOR_VSN 3 #define SYS_INFO_DRV_MINOR_VSN 0 #define SYS_INFO_DRV_NAME_STR "sys_info_base_drv" #define SYS_INFO_DRV_NAME sys_info_base_drv diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c index 815d96cc97..63c69f751c 100644 --- a/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c +++ b/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c @@ -19,14 +19,14 @@ /* * Author: Rickard Green * - * Description: Driver that fakes driver version 2.0 and tests + * Description: Driver that fakes driver version 3.0 and tests * driver_system_info(). * */ #include "sys_info_drv_impl.h" -#define SYS_INFO_DRV_MAJOR_VSN 2 +#define SYS_INFO_DRV_MAJOR_VSN 3 #define SYS_INFO_DRV_MINOR_VSN 0 #define SYS_INFO_DRV_NAME_STR "sys_info_prev_drv" #define SYS_INFO_DRV_NAME sys_info_prev_drv -- cgit v1.2.3 From ee1886e457302347414787e74ce5017604d2e52a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 15 Jan 2014 21:16:37 +0100 Subject: erts: Fix compiler warnings for NO_JUMP_TABLE --- erts/emulator/beam/beam_emu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 78ab6fa30f..0609e86a39 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The 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 @@ # define OpCase(OpCode) case op_##OpCode # define CountCase(OpCode) case op_count_##OpCode # define OpCode(OpCode) ((Uint*)op_##OpCode) -# define Goto(Rel) {Go = (int)(Rel); goto emulator_loop;} +# define Goto(Rel) {Go = (int)(UWord)(Rel); goto emulator_loop;} # define LabelAddr(Addr) &&##Addr #else # define OpCase(OpCode) lb_##OpCode @@ -133,7 +133,7 @@ do { \ /* We don't check the range if an ordinary switch is used */ #ifdef NO_JUMP_TABLE -#define VALID_INSTR(IP) (0 <= (int)(IP) && ((int)(IP) < (NUMBER_OF_OPCODES*2+10))) +#define VALID_INSTR(IP) ((UWord)(IP) < (NUMBER_OF_OPCODES*2+10)) #else #define VALID_INSTR(IP) \ ((SWord)LabelAddr(emulator_loop) <= (SWord)(IP) && \ -- cgit v1.2.3 From 03493394e8d20591020ce6c8152cb607cb21e967 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 22:31:36 +0100 Subject: erts: Fix benign ESTACK/WSTACK typo --- erts/emulator/beam/utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 297c4bf439..216952e2ac 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -2846,7 +2846,7 @@ pop_next: return 0; not_equal: - DESTROY_ESTACK(stack); + DESTROY_WSTACK(stack); return j; #undef CMP_NODES -- cgit v1.2.3 From 66b16c23552418c17a29caf7cecf8e11f587b29d Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Wed, 15 Jan 2014 23:43:03 -0500 Subject: fix system_flag deprecation warnings Passing cpu_topology or scheduler_bind_type to erlang:system_flag/2 results in an error report warning that the argument is deprecated and is slated for removal in erts-5.10/OTP-R16. Since we're already past that version and no substitute approach has yet been decided for these features, keep the deprecation warning but bump the removal version it mentions up to Erlang 18. --- erts/emulator/beam/bif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 96666d98ed..61c1abedb5 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4488,7 +4488,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) BIF_P->group_leader, "A call to erlang:system_flag(cpu_topology, _) was made.\n" "The cpu_topology argument is deprecated and scheduled\n" - "for removal in erts-5.10/OTP-R16. For more information\n" + "for removal in Erlang/OTP 18. For more information\n" "see the erlang:system_flag/2 documentation.\n"); BIF_TRAP1(set_cpu_topology_trap, BIF_P, BIF_ARG_2); } else if (ERTS_IS_ATOM_STR("scheduler_bind_type", BIF_ARG_1)) { @@ -4496,7 +4496,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) BIF_P->group_leader, "A call to erlang:system_flag(scheduler_bind_type, _) was\n" "made. The scheduler_bind_type argument is deprecated and\n" - "scheduled for removal in erts-5.10/OTP-R16. For more\n" + "scheduled for removal in Erlang/OTP 18. For more\n" "information see the erlang:system_flag/2 documentation.\n"); return erts_bind_schedulers(BIF_P, BIF_ARG_2); } -- cgit v1.2.3 From 8c39400c7fb03f3dd7d9dbfb01c43e5e91d7086c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 16 Jan 2014 09:50:42 +0100 Subject: compiler: Correct line number in exception from binary construction Reported-by: Stanislav Seletskiy --- erts/emulator/test/exception_SUITE.erl | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index 109cec25cb..09a7a87a9a 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -589,6 +589,13 @@ line_numbers(Config) when is_list(Config) -> [{file,ModFile},{line,_}]}|_]}} = (catch build_binary2(8, bad_binary)), + <<"abc",357:16>> = build_binary3(<<"abc">>), + {'EXIT',{badarg,[{?MODULE,build_binary3,1, + [{file,"bit_syntax.erl"},{line,72511}]}, + {?MODULE,line_numbers,1, + [{file,ModFile},{line,_}]}|_]}} = + (catch build_binary3(no_binary)), + {'EXIT',{function_clause, [{?MODULE,do_call_abs,[y,y], [{file,"gc_bif.erl"},{line,18}]}, @@ -691,6 +698,10 @@ build_binary2(Size, Bin) -> %Line 72505 id(0), %Line 72506 <<7:Size,Bin/binary>>. %Line 72507 +build_binary3(Bin) -> %Line 72509 + id(0), %Line 72510 + <>. %Line 72511 + -file("gc_bif.erl", 17). do_call_abs(x, Arg) -> %Line 18 abs(Arg). %Line 19 -- cgit v1.2.3 From 1101fcb3e61634f1be92e1b9ba9fad5a11b8554a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 16 Jan 2014 12:16:39 +0100 Subject: Add the 'rle' zstrategy --- erts/doc/src/zlib.xml | 30 ++++++++++++++++-------------- erts/preloaded/src/zlib.erl | 4 +++- 2 files changed, 19 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index afc597b729..11a7437f5a 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -161,20 +161,22 @@ list_to_binary([Compressed|Last]) state. MemLevel=1 uses minimum memory but is slow and reduces compression ratio; MemLevel=9 uses maximum memory for optimal speed. The default value is 8.

-

The Strategy parameter is used to tune the - compression algorithm. Use the value default for - normal data, filtered for data produced by a filter - (or predictor), or huffman_only to force Huffman - encoding only (no string match). Filtered data consists - mostly of small values with a somewhat random - distribution. In this case, the compression algorithm is - tuned to compress them better. The effect of - filteredis to force more Huffman coding and less - string matching; it is somewhat intermediate between - default and huffman_only. The Strategy - parameter only affects the compression ratio but not the - correctness of the compressed output even if it is not set - appropriately.

+

The Strategy parameter is used to tune + the compression algorithm. Use the value default for + normal data, filtered for data produced by a filter (or + predictor), huffman_only to force Huffman encoding + only (no string match), or rle to limit match + distances to one (run-length encoding). Filtered data + consists mostly of small values with a somewhat random + distribution. In this case, the compression algorithm is tuned + to compress them better. The effect of filteredis to + force more Huffman coding and less string matching; it is + somewhat intermediate between default and + huffman_only. rle is designed to be almost as + fast as huffman_only, but give better compression for PNG + image data. The Strategy parameter only + affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately.

diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index 3d85533b80..df7b2e6198 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -47,6 +47,7 @@ %% compresssion strategy -define(Z_FILTERED, 1). -define(Z_HUFFMAN_ONLY, 2). +-define(Z_RLE, 3). -define(Z_DEFAULT_STRATEGY, 0). %% deflate compression method @@ -125,7 +126,7 @@ -type zmethod() :: 'deflated'. -type zwindowbits() :: -15..-9 | 9..47. -type zmemlevel() :: 1..9. --type zstrategy() :: 'default' | 'filtered' | 'huffman_only'. +-type zstrategy() :: 'default' | 'filtered' | 'huffman_only' | 'rle'. %%------------------------------------------------------------------------ @@ -486,6 +487,7 @@ arg_level(_) -> erlang:error(badarg). arg_strategy(filtered) -> ?Z_FILTERED; arg_strategy(huffman_only) -> ?Z_HUFFMAN_ONLY; +arg_strategy(rle) -> ?Z_RLE; arg_strategy(default) -> ?Z_DEFAULT_STRATEGY; arg_strategy(_) -> erlang:error(badarg). -- cgit v1.2.3 From 3f53b27f9c4af8f91c3a66e8ebe81efe5ea02b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 16 Jan 2014 12:22:09 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/zlib.beam | Bin 13148 -> 13164 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 5b51280838..ffd80f51ba 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 172ebf11dc455e22b87f742c06fa6344995d7db5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 11:10:34 +0100 Subject: erts: Refactor ESTACK & WSTACK to use a struct easy to "export" This is not a clean refactor. It changes the behaviour slightly of E/WSTACK_RESTORE. The allocated stack from E/WSTACK_SAVE is used as-is and not copied into default_stack. This will hopefully fix an illusive memory leak that valgrind is reporting. --- erts/emulator/beam/external.c | 42 ++--- erts/emulator/beam/global.h | 346 +++++++++++++++++++++--------------------- erts/emulator/beam/utils.c | 44 +++--- 3 files changed, 211 insertions(+), 221 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 50831e848e..94acceaaf1 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1732,8 +1732,7 @@ typedef struct TTBSizeContext_ { int level; Uint result; Eterm obj; - UWord* stack; - Uint stack_sz; + ErtsEStack estack; } TTBSizeContext; typedef struct TTBEncodeContext_ { @@ -1741,8 +1740,7 @@ typedef struct TTBEncodeContext_ { int level; byte* ep; Eterm obj; - UWord* stack; - Uint stack_sz; + ErtsWStack wstack; Binary *result_bin; } TTBEncodeContext; @@ -1772,16 +1770,10 @@ static void ttb_context_destructor(Binary *context_bin) context->alive = 0; switch (context->state) { case TTBSize: - if (context->s.sc.stack) { - erts_free(ERTS_ALC_T_SAVED_ESTACK, context->s.sc.stack); - context->s.sc.stack = NULL; - } + DESTROY_SAVED_ESTACK(&context->s.sc.estack); break; case TTBEncode: - if (context->s.ec.stack) { - erts_free(ERTS_ALC_T_SAVED_ESTACK, context->s.ec.stack); - context->s.ec.stack = NULL; - } + DESTROY_SAVED_WSTACK(&context->s.ec.wstack); if (context->s.ec.result_bin != NULL) { /* Set to NULL if ever made alive! */ ASSERT(erts_refc_read(&(context->s.ec.result_bin->refc),0) == 0); erts_bin_free(context->s.ec.result_bin); @@ -1846,7 +1838,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla /* Setup enough to get started */ context->state = TTBSize; context->alive = 1; - context->s.sc.stack = NULL; + context->s.sc.estack.start = NULL; context->s.sc.flags = flags; context->s.sc.level = level; } else { @@ -1889,7 +1881,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla context->state = TTBEncode; context->s.ec.flags = flags; context->s.ec.level = level; - context->s.ec.stack = NULL; + context->s.ec.wstack.wstart = NULL; context->s.ec.result_bin = result_bin; break; } @@ -2340,8 +2332,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, WSTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); r = *reds; - if (ctx->stack) { /* restore saved stacks and byte pointer */ - WSTACK_RESTORE(s, ctx->stack, ctx->stack_sz); + if (ctx->wstack.wstart) { /* restore saved stacks and byte pointer */ + WSTACK_RESTORE(s, &ctx->wstack); ep = ctx->ep; obj = ctx->obj; } @@ -2412,7 +2404,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, *reds = r; ctx->obj = obj; ctx->ep = ep; - WSTACK_SAVE(s, ctx->stack, ctx->stack_sz); + WSTACK_SAVE(s, &ctx->wstack); return -1; } switch(tag_val_def(obj)) { @@ -2767,10 +2759,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, } DESTROY_WSTACK(s); if (ctx) { - if (ctx->stack) { - erts_free(ERTS_ALC_T_SAVED_ESTACK, ctx->stack); - ctx->stack = NULL; - } + ASSERT(ctx->wstack.wstart == NULL); *reds = r; } *res = ep; @@ -3752,8 +3741,8 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); r = *reds; - if (ctx->stack) { /* restore saved stack */ - ESTACK_RESTORE(s, ctx->stack, ctx->stack_sz); + if (ctx->estack.start) { /* restore saved stack */ + ESTACK_RESTORE(s, &ctx->estack); result = ctx->result; obj = ctx->obj; } @@ -3787,7 +3776,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, *reds = r; ctx->obj = obj; ctx->result = result; - ESTACK_SAVE(s, ctx->stack, ctx->stack_sz); + ESTACK_SAVE(s, &ctx->estack); return -1; } switch (tag_val_def(obj)) { @@ -3991,10 +3980,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, DESTROY_ESTACK(s); if (ctx) { - if (ctx->stack) { - erts_free(ERTS_ALC_T_SAVED_ESTACK, ctx->stack); - ctx->stack = NULL; - } + ASSERT(ctx->estack.start == NULL); *reds = r; } *res = result; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 94bc1b172a..c44f532366 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -370,231 +370,233 @@ extern int stackdump_on_exit; * DESTROY_ESTACK(Stack) */ +typedef struct { + UWord* start; + UWord* sp; + UWord* end; + ErtsAlcType_t alloc_type; +}ErtsEStack; -void erl_grow_stack(ErtsAlcType_t a_type, Eterm** start, Eterm** sp, Eterm** end); -#define ESTK_CONCAT(a,b) a##b -#define ESTK_SUBSCRIPT(s,i) *((Eterm *)((byte *)ESTK_CONCAT(s,_start) + (i))) #define DEF_ESTACK_SIZE (16) -#define DECLARE_ESTACK(s) \ - Eterm ESTK_CONCAT(s,_default_stack)[DEF_ESTACK_SIZE]; \ - Eterm* ESTK_CONCAT(s,_start) = ESTK_CONCAT(s,_default_stack); \ - Eterm* ESTK_CONCAT(s,_sp) = ESTK_CONCAT(s,_start); \ - Eterm* ESTK_CONCAT(s,_end) = ESTK_CONCAT(s,_start) + DEF_ESTACK_SIZE;\ - ErtsAlcType_t ESTK_CONCAT(s,_alloc_type) = ERTS_ALC_T_ESTACK +void erl_grow_estack(ErtsEStack*, Eterm* def_stack); +#define ESTK_CONCAT(a,b) a##b +#define ESTK_DEF_STACK(s) ESTK_CONCAT(s,_default_estack) + +#define DECLARE_ESTACK(s) \ + UWord ESTK_DEF_STACK(s)[DEF_ESTACK_SIZE]; \ + ErtsEStack s = { \ + ESTK_DEF_STACK(s), /* start */ \ + ESTK_DEF_STACK(s), /* sp */ \ + ESTK_DEF_STACK(s) + DEF_ESTACK_SIZE, /* end */ \ + ERTS_ALC_T_ESTACK /* alloc_type */ \ + } #define ESTACK_CHANGE_ALLOCATOR(s,t) \ do { \ - if (ESTK_CONCAT(s,_start) != ESTK_CONCAT(s,_default_stack)) { \ + if (s.start != ESTK_DEF_STACK(s)) { \ erl_exit(1, "Internal error - trying to change allocator " \ "type of active estack\n"); \ } \ - ESTK_CONCAT(s,_alloc_type) = (t); \ + s.alloc_type = (t); \ } while (0) +#define DESTROY_ESTACK(s) \ +do { \ + if (s.start != ESTK_DEF_STACK(s)) { \ + erts_free(s.alloc_type, s.start); \ + } \ +} while(0) + + /* - * Do not free the stack after this, it may have pointers into what - * was saved in 'v'. 'v' and 'vsize' are changed by this macro. If - * 'v' points to anything, it should have been allocated by a previous - * call to this macro. Be careful to set a correct allocator prior to - * saving. - * 'v' can be any lvalue pointer, it will point to an array of UWord - * after calling this macro. + * Do not free the stack after this, it may have pointers into what + * was saved in 'dst'. */ -#define ESTACK_SAVE(s,v,vsize) /* v and vsize are "name parameters" */ \ -do { \ - Uint _esz = ESTACK_COUNT(s); \ - if (ESTK_CONCAT(s,_start) == ESTK_CONCAT(s,_default_stack)) { \ - if ((v) == NULL) { \ - (v) = erts_alloc(ESTK_CONCAT(s,_alloc_type), \ - DEF_ESTACK_SIZE * sizeof(Eterm)); \ - } \ - memcpy((v),ESTK_CONCAT(s,_start),_esz*sizeof(Eterm)); \ - } else { \ - (v) = (void *) ESTK_CONCAT(s,_start); \ - } \ - (vsize) = _esz; \ +#define ESTACK_SAVE(s,dst)\ +do {\ + if (s.start == ESTK_DEF_STACK(s)) {\ + UWord _wsz = ESTACK_COUNT(s);\ + (dst)->start = erts_alloc(s.alloc_type,\ + DEF_ESTACK_SIZE * sizeof(UWord));\ + memcpy((dst)->start, s.start,_wsz*sizeof(UWord));\ + (dst)->sp = (dst)->start + _wsz;\ + (dst)->end = (dst)->start + DEF_ESTACK_SIZE;\ + (dst)->alloc_type = s.alloc_type;\ + } else\ + *(dst) = s;\ } while (0) -/* - * Use on empty stack, only the allocator can be changed before this - * The vector parameter is reset to NULL if the vector is moved to stack, - * otherwise it's kept for reuse, so a saved and restored vector might - * need freeing using the correct allocator parameter. - * 'v' can be any lvalue pointer, it's cast to an (Eterm *). +#define DESTROY_SAVED_ESTACK(estack)\ +do {\ + if ((estack)->start) {\ + erts_free((estack)->alloc_type, (estack)->start);\ + (estack)->start = NULL;\ + }\ +} while(0) + +/* + * Use on empty stack, only the allocator can be changed before this. + * The src stack is reset to NULL. */ -#define ESTACK_RESTORE(s, v, vsize) /*v is a "name parameter"*/ \ -do { \ - if ((vsize) > DEF_ESTACK_SIZE) { \ - Uint _ca = DEF_ESTACK_SIZE; \ - while (_ca < (vsize)) \ - _ca = _ca * 2; \ - ESTK_CONCAT(s,_start) = (Eterm *) (v); \ - ESTK_CONCAT(s,_end) = ((Eterm *)(v)) + _ca; \ - ESTK_CONCAT(s,_sp) = ESTK_CONCAT(s,_start) + (vsize); \ - (v) = NULL; \ - } else { \ - memcpy(ESTK_CONCAT(s,_start),(v),(vsize)*sizeof(Eterm));\ - ESTK_CONCAT(s,_sp) = ESTK_CONCAT(s,_start) + (vsize); \ - } \ - } while (0) +#define ESTACK_RESTORE(s, src) \ +do { \ + ASSERT(s.start == ESTK_DEF_STACK(s)); \ + s = *(src); /* struct copy */ \ + (src)->start = NULL; \ + ASSERT(s.sp >= s.start); \ + ASSERT(s.sp <= s.end); \ +} while (0) -#define ESTACK_IS_STATIC(s) (ESTK_CONCAT(s,_start) == ESTK_CONCAT(s,_default_stack)) +#define ESTACK_IS_STATIC(s) (s.start == ESTK_DEF_STACK(s))) -#define DESTROY_ESTACK(s) \ -do { \ - if (ESTK_CONCAT(s,_start) != ESTK_CONCAT(s,_default_stack)) { \ - erts_free(ESTK_CONCAT(s,_alloc_type), ESTK_CONCAT(s,_start)); \ - } \ +#define ESTACK_PUSH(s, x) \ +do { \ + if (s.sp == s.end) { \ + erl_grow_estack(&s, ESTK_DEF_STACK(s)); \ + } \ + *s.sp++ = (x); \ } while(0) -#define ESTACK_PUSH(s, x) \ -do { \ - if (ESTK_CONCAT(s,_sp) == ESTK_CONCAT(s,_end)) { \ - erl_grow_stack(ESTK_CONCAT(s,_alloc_type),&ESTK_CONCAT(s,_start), \ - &ESTK_CONCAT(s,_sp), &ESTK_CONCAT(s,_end)); \ - } \ - *ESTK_CONCAT(s,_sp)++ = (x); \ +#define ESTACK_PUSH2(s, x, y) \ +do { \ + if (s.sp > s.end - 2) { \ + erl_grow_estack(&s, ESTK_DEF_STACK(s)); \ + } \ + *s.sp++ = (x); \ + *s.sp++ = (y); \ } while(0) -#define ESTACK_PUSH2(s, x, y) \ -do { \ - if (ESTK_CONCAT(s,_sp) > ESTK_CONCAT(s,_end) - 2) { \ - erl_grow_stack(ESTK_CONCAT(s,_alloc_type),&ESTK_CONCAT(s,_start), \ - &ESTK_CONCAT(s,_sp), &ESTK_CONCAT(s,_end)); \ - } \ - *ESTK_CONCAT(s,_sp)++ = (x); \ - *ESTK_CONCAT(s,_sp)++ = (y); \ +#define ESTACK_PUSH3(s, x, y, z) \ +do { \ + if (s.sp > s.end - 3) { \ + erl_grow_estack(&s, ESTK_DEF_STACK(s)); \ + } \ + *s.sp++ = (x); \ + *s.sp++ = (y); \ + *s.sp++ = (z); \ } while(0) -#define ESTACK_PUSH3(s, x, y, z) \ -do { \ - if (ESTK_CONCAT(s,_sp) > ESTK_CONCAT(s,_end) - 3) { \ - erl_grow_stack(&ESTK_CONCAT(s,_start), &ESTK_CONCAT(s,_sp), \ - &ESTK_CONCAT(s,_end)); \ - } \ - *ESTK_CONCAT(s,_sp)++ = (x); \ - *ESTK_CONCAT(s,_sp)++ = (y); \ - *ESTK_CONCAT(s,_sp)++ = (z); \ -} while(0) +#define ESTACK_COUNT(s) (s.sp - s.start) +#define ESTACK_ISEMPTY(s) (s.sp == s.start) +#define ESTACK_POP(s) (*(--s.sp)) -#define ESTACK_COUNT(s) (ESTK_CONCAT(s,_sp) - ESTK_CONCAT(s,_start)) -#define ESTACK_ISEMPTY(s) (ESTK_CONCAT(s,_sp) == ESTK_CONCAT(s,_start)) -#define ESTACK_POP(s) (*(--ESTK_CONCAT(s,_sp))) +/* + * WSTACK: same as ESTACK but with UWord instead of Eterm + */ +typedef struct { + UWord* wstart; + UWord* wsp; + UWord* wend; + ErtsAlcType_t alloc_type; +}ErtsWStack; -void erl_grow_wstack(ErtsAlcType_t a_type, UWord** start, UWord** sp, UWord** end); -#define WSTK_CONCAT(a,b) a##b -#define WSTK_SUBSCRIPT(s,i) *((UWord *)((byte *)WSTK_CONCAT(s,_start) + (i))) #define DEF_WSTACK_SIZE (16) -#define DECLARE_WSTACK(s) \ - UWord WSTK_CONCAT(s,_default_stack)[DEF_WSTACK_SIZE]; \ - UWord* WSTK_CONCAT(s,_start) = WSTK_CONCAT(s,_default_stack); \ - UWord* WSTK_CONCAT(s,_sp) = WSTK_CONCAT(s,_start); \ - UWord* WSTK_CONCAT(s,_end) = WSTK_CONCAT(s,_start) + DEF_WSTACK_SIZE; \ - ErtsAlcType_t WSTK_CONCAT(s,_alloc_type) = ERTS_ALC_T_ESTACK +void erl_grow_wstack(ErtsWStack*, Eterm* def_stack); +#define WSTK_CONCAT(a,b) a##b +#define WSTK_DEF_STACK(s) WSTK_CONCAT(s,_default_wstack) + +#define DECLARE_WSTACK(s) \ + UWord WSTK_DEF_STACK(s)[DEF_WSTACK_SIZE]; \ + ErtsWStack s = { \ + WSTK_DEF_STACK(s), /* wstart */ \ + WSTK_DEF_STACK(s), /* wsp */ \ + WSTK_DEF_STACK(s) + DEF_WSTACK_SIZE, /* wend */ \ + ERTS_ALC_T_ESTACK /* alloc_type */ \ + } #define WSTACK_CHANGE_ALLOCATOR(s,t) \ do { \ - if (WSTK_CONCAT(s,_start) != WSTK_CONCAT(s,_default_stack)) { \ + if (s.wstart != WSTK_DEF_STACK(s)) { \ erl_exit(1, "Internal error - trying to change allocator " \ "type of active wstack\n"); \ } \ - WSTK_CONCAT(s,_alloc_type) = (t); \ + s.alloc_type = (t); \ } while (0) -#define DESTROY_WSTACK(s) \ -do { \ - if (WSTK_CONCAT(s,_start) != WSTK_CONCAT(s,_default_stack)) { \ - erts_free(WSTK_CONCAT(s,_alloc_type), WSTK_CONCAT(s,_start)); \ - } \ +#define DESTROY_WSTACK(s) \ +do { \ + if (s.wstart != WSTK_DEF_STACK(s)) { \ + erts_free(s.alloc_type, s.wstart); \ + } \ } while(0) + /* - * Do not free the stack after this, it may have pointers into what - * was saved in 'v'. 'v' and 'vsize' are changed by this macro. If - * 'v' points to anything, it should have been allocated by a previous - * call to this macro. Be careful to set a correct allocator prior to - * saving. - * 'v' can be any lvalue pointer, it will point to an array of UWord - * after calling this macro. + * Do not free the stack after this, it may have pointers into what + * was saved in 'dst'. */ -#define WSTACK_SAVE(s,v,vsize) /* v and vsize are "name parameters" */ \ -do { \ - Uint _wsz = WSTACK_COUNT(s); \ - if (WSTK_CONCAT(s,_start) == WSTK_CONCAT(s,_default_stack)) { \ - if ((v) == NULL) { \ - (v) = erts_alloc(WSTK_CONCAT(s,_alloc_type), \ - DEF_WSTACK_SIZE * sizeof(UWord)); \ - } \ - memcpy((v),WSTK_CONCAT(s,_start),_wsz*sizeof(UWord)); \ - } else { \ - (v) = (void *) WSTK_CONCAT(s,_start); \ - } \ - (vsize) = _wsz; \ +#define WSTACK_SAVE(s,dst)\ +do {\ + if (s.wstart == WSTK_DEF_STACK(s)) {\ + UWord _wsz = WSTACK_COUNT(s);\ + (dst)->wstart = erts_alloc(s.alloc_type,\ + DEF_WSTACK_SIZE * sizeof(UWord));\ + memcpy((dst)->wstart, s.wstart,_wsz*sizeof(UWord));\ + (dst)->wsp = (dst)->wstart + _wsz;\ + (dst)->wend = (dst)->wstart + DEF_WSTACK_SIZE;\ + (dst)->alloc_type = s.alloc_type;\ + } else\ + *(dst) = s;\ } while (0) -/* - * Use on empty stack, only the allocator can be changed before this - * The vector parameter is reset to NULL if the vector is moved to stack, - * otherwise it's kept for reuse, so a saved and restored vector might - * need freeing using the correct allocator parameter. - * 'v' can be any lvalue pointer, it's cast to an (UWord *). +#define DESTROY_SAVED_WSTACK(wstack)\ +do {\ + if ((wstack)->wstart) {\ + erts_free((wstack)->alloc_type, (wstack)->wstart);\ + (wstack)->wstart = NULL;\ + }\ +} while(0) + +/* + * Use on empty stack, only the allocator can be changed before this. + * The src stack is reset to NULL. */ -#define WSTACK_RESTORE(s, v, vsize) /*v is a "name parameter"*/ \ -do { \ - if ((vsize) > DEF_WSTACK_SIZE) { \ - Uint _ca = DEF_WSTACK_SIZE; \ - while (_ca < (vsize)) \ - _ca = _ca * 2; \ - WSTK_CONCAT(s,_start) = (UWord *) (v); \ - WSTK_CONCAT(s,_end) = ((UWord *)(v)) + _ca; \ - WSTK_CONCAT(s,_sp) = WSTK_CONCAT(s,_start) + (vsize); \ - (v) = NULL; \ - } else { \ - memcpy(WSTK_CONCAT(s,_start),(v),(vsize)*sizeof(UWord));\ - WSTK_CONCAT(s,_sp) = WSTK_CONCAT(s,_start) + (vsize); \ - } \ - } while (0) +#define WSTACK_RESTORE(s, src) \ +do { \ + ASSERT(s.wstart == WSTK_DEF_STACK(s)); \ + s = *(src); /* struct copy */ \ + (src)->wstart = NULL; \ + ASSERT(s.wsp >= s.wstart); \ + ASSERT(s.wsp <= s.wend); \ +} while (0) -#define WSTACK_IS_STATIC(s) (WSTK_CONCAT(s,_start) == WSTK_CONCAT(s,_default_stack)) +#define WSTACK_IS_STATIC(s) (s.wstart == WSTK_DEF_STACK(s))) -#define WSTACK_PUSH(s, x) \ -do { \ - if (WSTK_CONCAT(s,_sp) == WSTK_CONCAT(s,_end)) { \ - erl_grow_wstack(WSTK_CONCAT(s,_alloc_type), &WSTK_CONCAT(s,_start), \ - &WSTK_CONCAT(s,_sp), &WSTK_CONCAT(s,_end)); \ - } \ - *WSTK_CONCAT(s,_sp)++ = (x); \ +#define WSTACK_PUSH(s, x) \ +do { \ + if (s.wsp == s.wend) { \ + erl_grow_wstack(&s, WSTK_DEF_STACK(s)); \ + } \ + *s.wsp++ = (x); \ } while(0) -#define WSTACK_PUSH2(s, x, y) \ -do { \ - if (WSTK_CONCAT(s,_sp) > WSTK_CONCAT(s,_end) - 2) { \ - erl_grow_wstack(WSTK_CONCAT(s,_alloc_type), &WSTK_CONCAT(s,_start), \ - &WSTK_CONCAT(s,_sp), &WSTK_CONCAT(s,_end)); \ - } \ - *WSTK_CONCAT(s,_sp)++ = (x); \ - *WSTK_CONCAT(s,_sp)++ = (y); \ +#define WSTACK_PUSH2(s, x, y) \ +do { \ + if (s.wsp > s.wend - 2) { \ + erl_grow_wstack(&s, WSTK_DEF_STACK(s)); \ + } \ + *s.wsp++ = (x); \ + *s.wsp++ = (y); \ } while(0) -#define WSTACK_PUSH3(s, x, y, z) \ -do { \ - if (WSTK_CONCAT(s,_sp) > WSTK_CONCAT(s,_end) - 3) { \ - erl_grow_wstack(WSTK_CONCAT(s,_alloc_type), &WSTK_CONCAT(s,_start), \ - &WSTK_CONCAT(s,_sp), &WSTK_CONCAT(s,_end)); \ - } \ - *WSTK_CONCAT(s,_sp)++ = (x); \ - *WSTK_CONCAT(s,_sp)++ = (y); \ - *WSTK_CONCAT(s,_sp)++ = (z); \ +#define WSTACK_PUSH3(s, x, y, z) \ +do { \ + if (s.wsp > s.wend - 3) { \ + erl_grow_wstack(&s, WSTK_DEF_STACK(s)); \ + } \ + *s.wsp++ = (x); \ + *s.wsp++ = (y); \ + *s.wsp++ = (z); \ } while(0) -#define WSTACK_COUNT(s) (WSTK_CONCAT(s,_sp) - WSTK_CONCAT(s,_start)) +#define WSTACK_COUNT(s) (s.wsp - s.wstart) +#define WSTACK_ISEMPTY(s) (s.wsp == s.wstart) +#define WSTACK_POP(s) (*(--s.wsp)) -#define WSTACK_ISEMPTY(s) (WSTK_CONCAT(s,_sp) == WSTK_CONCAT(s,_start)) -#define WSTACK_POP(s) (*(--WSTK_CONCAT(s,_sp))) /* binary.c */ diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 216952e2ac..7f8bdcb2ca 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -185,39 +185,41 @@ erts_set_hole_marker(Eterm* ptr, Uint sz) * Helper function for the ESTACK macros defined in global.h. */ void -erl_grow_stack(ErtsAlcType_t a_type, Eterm** start, Eterm** sp, Eterm** end) +erl_grow_estack(ErtsEStack* s, Eterm* default_estack) { - Uint old_size = (*end - *start); + Uint old_size = (s->end - s->start); Uint new_size = old_size * 2; - Uint sp_offs = *sp - *start; - if (new_size > 2 * DEF_ESTACK_SIZE) { - *start = erts_realloc(a_type, (void *) *start, new_size*sizeof(Eterm)); + Uint sp_offs = s->sp - s->start; + if (s->start != default_estack) { + s->start = erts_realloc(s->alloc_type, s->start, + new_size*sizeof(Eterm)); } else { - Eterm* new_ptr = erts_alloc(a_type, new_size*sizeof(Eterm)); - sys_memcpy(new_ptr, *start, old_size*sizeof(Eterm)); - *start = new_ptr; + Eterm* new_ptr = erts_alloc(s->alloc_type, new_size*sizeof(Eterm)); + sys_memcpy(new_ptr, s->start, old_size*sizeof(Eterm)); + s->start = new_ptr; } - *end = *start + new_size; - *sp = *start + sp_offs; + s->end = s->start + new_size; + s->sp = s->start + sp_offs; } /* - * Helper function for the ESTACK macros defined in global.h. + * Helper function for the WSTACK macros defined in global.h. */ void -erl_grow_wstack(ErtsAlcType_t a_type, UWord** start, UWord** sp, UWord** end) +erl_grow_wstack(ErtsWStack* s, UWord* default_wstack) { - Uint old_size = (*end - *start); + Uint old_size = (s->wend - s->wstart); Uint new_size = old_size * 2; - Uint sp_offs = *sp - *start; - if (new_size > 2 * DEF_ESTACK_SIZE) { - *start = erts_realloc(a_type, (void *) *start, new_size*sizeof(UWord)); + Uint sp_offs = s->wsp - s->wstart; + if (s->wstart != default_wstack) { + s->wstart = erts_realloc(s->alloc_type, s->wstart, + new_size*sizeof(UWord)); } else { - UWord* new_ptr = erts_alloc(a_type, new_size*sizeof(UWord)); - sys_memcpy(new_ptr, *start, old_size*sizeof(UWord)); - *start = new_ptr; + UWord* new_ptr = erts_alloc(s->alloc_type, new_size*sizeof(UWord)); + sys_memcpy(new_ptr, s->wstart, old_size*sizeof(UWord)); + s->wstart = new_ptr; } - *end = *start + new_size; - *sp = *start + sp_offs; + s->wend = s->wstart + new_size; + s->wsp = s->wstart + sp_offs; } /* CTYPE macros */ -- cgit v1.2.3 From 9f57d8c4f86323cf7481c9a7e512486087bb5542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 16 Jan 2014 14:37:34 +0100 Subject: configure: Prefer the system's zlib over own our zlib source WxWidgets (used by the wx application) also uses zlib. To ensure that the run-time system and WxWidgets use the same version of zlib, use the system's zlib if present. Also, the system's zlib may be specially optimized and thus faster than our own generic source code. We only use zlib versions that are "good enough". For now, that means 1.2.5 or higher. Remove the option --enable-shared-zlib and add the option --enable-builtin-zlib to force the use of the built-in zlib. --- erts/configure.in | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 3b4c46d4a5..f03296a89f 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1372,24 +1372,44 @@ dnl ------------- dnl zlib dnl ------------- -AC_ARG_ENABLE(shared-zlib, -AS_HELP_STRING([--enable-shared-zlib], [enable using shared zlib library]), -[ case "$enableval" in - no) enable_shared_zlib=no ;; - *) enable_shared_zlib=yes ;; - esac ], enable_shared_zlib=no) +AC_ARG_ENABLE(builtin-zlib, + AS_HELP_STRING([--enable-builtin-zlib], + [force use of our own built-in zlib]), + [ case "$enableval" in + no) enable_builtin_zlib=no ;; + *) enable_builtin_zlib=yes ;; + esac ], enable_builtin_zlib=no) Z_LIB= -if test "x$enable_shared_zlib" = "xyes" ; then - AC_CHECK_LIB(z, adler32_combine, - [Z_LIB="-lz" - AC_DEFINE(HAVE_LIBZ, 1, [Define to 1 if you have the `z' library (-lz).])], - [AC_MSG_ERROR([cannot find any shared zlib])]) +if test "x$enable_builtin_zlib" = "xyes"; then + AC_MSG_NOTICE([Using our own built-in zlib source]) else - AC_MSG_NOTICE([Using own zlib source]) +AC_MSG_CHECKING(for zlib 1.2.5 or higher) +zlib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ +#include "zlib.h" +]],[[ +#if ZLIB_VERNUM >= 0x1250 + Bytef s[1]; + s[0] = 0; + (void) adler32((uLong)0, s, 1); +#else +#error "No zlib 1.2.5 or higher found" +error +#endif +]])], +[ + Z_LIB="-lz" + AC_DEFINE(HAVE_LIBZ, 1, [Define to 1 if you have the `z' library (-lz).]) + AC_MSG_RESULT(yes) +],[ + AC_MSG_RESULT(no) +]) +LIBS=$zlib_save_LIBS fi - AC_SUBST(Z_LIB) dnl -- cgit v1.2.3 From 1747c3fb3dded18b9abfcb72ba867b43393b446c Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 13 Dec 2013 12:44:42 +0100 Subject: Fix testcase driver_SUITE:consume_timeslice --- erts/emulator/test/driver_SUITE.erl | 68 +++++++++++++++---------------------- 1 file changed, 28 insertions(+), 40 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 7087542899..06211406b4 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -2075,6 +2075,21 @@ thr_msg_blast(Config) when is_list(Config) -> Res end. +-define(IN_RANGE(LoW_, VaLuE_, HiGh_), + case in_range(LoW_, VaLuE_, HiGh_) of + true -> ok; + false -> + case erlang:system_info(lock_checking) of + true -> + ?t:format("~p:~p: Ignore bad sched count due to " + "lock checking~n", + [?MODULE,?LINE]); + false -> + ?t:fail({unexpected_sched_counts, VaLuE_}) + end + end). + + consume_timeslice(Config) when is_list(Config) -> %% %% Verify that erl_drv_consume_timeslice() works. @@ -2131,15 +2146,8 @@ consume_timeslice(Config) when is_list(Config) -> Proc1 ! Go, wait_command_msgs(Port, 10), [{Port, Sprt1}, {Proc1, Sproc1}] = count_pp_sched_stop([Port, Proc1]), - case Sprt1 of - 10 -> - true = in_range(5, Sproc1-10, 7); - _ -> - case erlang:system_info(lock_checking) of - true -> ?t:format("Ignore bad sched count due to lock checking", []); - false -> ?t:fail({unexpected_sched_counts, Sprt1, Sproc1}) - end - end, + ?IN_RANGE(10, Sprt1, 10), + ?IN_RANGE(5, Sproc1-10, 7), "disabled" = port_control(Port, $D, ""), Proc2 = spawn_link(fun () -> @@ -2160,15 +2168,8 @@ consume_timeslice(Config) when is_list(Config) -> Proc2 ! Go, wait_command_msgs(Port, 10), [{Port, Sprt2}, {Proc2, Sproc2}] = count_pp_sched_stop([Port, Proc2]), - case Sprt2 of - 10 -> - true = in_range(1, Sproc2-10, 2); - _ -> - case erlang:system_info(lock_checking) of - true -> ?t:format("Ignore bad sched count due to lock checking", []); - false -> ?t:fail({unexpected_sched_counts, Sprt2, Sproc2}) - end - end, + ?IN_RANGE(10, Sprt2, 10), + ?IN_RANGE(1, Sproc2-10, 2), "enabled" = port_control(Port, $E, ""), Proc3 = spawn_link(fun () -> @@ -2188,15 +2189,8 @@ consume_timeslice(Config) when is_list(Config) -> Proc3 ! Go, wait_command_msgs(Port, 10), [{Port, Sprt3}, {Proc3, Sproc3}] = count_pp_sched_stop([Port, Proc3]), - case Sprt3 of - 10 -> - true = in_range(5, Sproc3-10, 7); - _ -> - case erlang:system_info(lock_checking) of - true -> ?t:format("Ignore bad sched count due to lock checking", []); - false -> ?t:fail({unexpected_sched_counts, Sprt3, Sproc3}) - end - end, + ?IN_RANGE(10, Sprt3, 10), + ?IN_RANGE(5, Sproc3-10, 7), "disabled" = port_control(Port, $D, ""), Proc4 = spawn_link(fun () -> @@ -2216,15 +2210,8 @@ consume_timeslice(Config) when is_list(Config) -> Proc4 ! Go, wait_command_msgs(Port, 10), [{Port, Sprt4}, {Proc4, Sproc4}] = count_pp_sched_stop([Port, Proc4]), - case Sprt4 of - 10 -> - true = in_range(1, Sproc4-10, 2); - _ -> - case erlang:system_info(lock_checking) of - true -> ?t:format("Ignore bad sched count due to lock checking", []); - false -> ?t:fail({unexpected_sched_counts, Sprt4, Sproc4}) - end - end, + ?IN_RANGE(10, Sprt4, 10), + ?IN_RANGE(1, Sproc4-10, 2), SOnl = erlang:system_info(schedulers_online), %% If only one scheduler use port with parallelism set to true, @@ -2272,8 +2259,8 @@ consume_timeslice(Config) when is_list(Config) -> wait_procs_exit([W5, Proc5]), wait_command_msgs(Port2, 10), [{Port2, Sprt5}, {Proc5, Sproc5}] = count_pp_sched_stop([Port2, Proc5]), - true = in_range(2, Sproc5, 3), - true = in_range(7, Sprt5, 20), + ?IN_RANGE(2, Sproc5, 3), + ?IN_RANGE(6, Sprt5, 20), count_pp_sched_start(), "disabled" = port_control(Port2, $D, ""), @@ -2307,8 +2294,8 @@ consume_timeslice(Config) when is_list(Config) -> wait_procs_exit([W6, Proc6]), wait_command_msgs(Port2, 10), [{Port2, Sprt6}, {Proc6, Sproc6}] = count_pp_sched_stop([Port2, Proc6]), - true = in_range(2, Sproc6, 3), - true = in_range(3, Sprt6, 6), + ?IN_RANGE(2, Sproc6, 3), + ?IN_RANGE(2, Sprt6, 6), process_flag(scheduler, 0), @@ -2316,6 +2303,7 @@ consume_timeslice(Config) when is_list(Config) -> receive {Port2, closed} -> ok end, ok. + wait_command_msgs(_, 0) -> ok; wait_command_msgs(Port, N) -> -- cgit v1.2.3 From 048400b08872d95ce7a8b48e346b49971acb6506 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Wed, 28 Aug 2013 14:05:08 +0200 Subject: Fix testing with unicode paths re needs unicode option --- erts/test/erlc_SUITE.erl | 8 ++++---- erts/test/erlexec_SUITE.erl | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index ed7a43c7e7..5002836954 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -334,7 +334,7 @@ make_dep_options(Config) -> run(Config, Cmd0, Name, Options, Expect) -> Cmd = Cmd0 ++ " " ++ Options ++ " " ++ Name, - io:format("~s", [Cmd]), + io:format("~ts", [Cmd]), Result = run_command(Config, Cmd), verify_result(Result, Expect). @@ -356,7 +356,7 @@ split([], Current, Lines) -> split([], [], [lists:reverse(Current)|Lines]). match_messages([Msg|Rest1], [Regexp|Rest2]) -> - case re:run(Msg, Regexp, [{capture,none}]) of + case re:run(Msg, Regexp, [{capture,none}, unicode]) of match -> ok; nomatch -> @@ -398,7 +398,7 @@ run_command(Config, Cmd) -> TmpDir = filename:join(?config(priv_dir, Config), "tmp"), file:make_dir(TmpDir), {RunFile, Run, Script} = run_command(TmpDir, os:type(), Cmd), - ok = file:write_file(filename:join(TmpDir, RunFile), Script), + ok = file:write_file(filename:join(TmpDir, RunFile), unicode:characters_to_binary(Script)), os:cmd(Run). run_command(Dir, {win32, _}, Cmd) -> diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index 0dfe6c2e5f..f5ea8f160a 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -433,10 +433,10 @@ verify_not_args(Xs, Ys) -> Xs). emu_args(CmdLineArgs) -> - io:format("CmdLineArgs = ~s~n", [CmdLineArgs]), + io:format("CmdLineArgs = ~ts~n", [CmdLineArgs]), {ok,[[Erl]]} = init:get_argument(progname), EmuCL = os:cmd(Erl ++ " -emu_args_exit " ++ CmdLineArgs), - io:format("EmuCL = ~s", [EmuCL]), + io:format("EmuCL = ~ts", [EmuCL]), split_emu_clt(string:tokens(EmuCL, [$ ,$\t,$\n,$\r])). split_emu_clt(EmuCLT) -> -- cgit v1.2.3 From d8565ab63de970f01ec1bf80fcbf0b3c692c7bd0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 22 Jan 2014 11:12:59 +0100 Subject: erts: Fix halfword compile errors in ESTACK Errors introduced in 172ebf11dc455e2 --- erts/emulator/beam/global.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index c183c519ff..83a8911a36 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -371,9 +371,9 @@ extern int stackdump_on_exit; */ typedef struct { - UWord* start; - UWord* sp; - UWord* end; + Eterm* start; + Eterm* sp; + Eterm* end; ErtsAlcType_t alloc_type; }ErtsEStack; @@ -384,7 +384,7 @@ void erl_grow_estack(ErtsEStack*, Eterm* def_stack); #define ESTK_DEF_STACK(s) ESTK_CONCAT(s,_default_estack) #define DECLARE_ESTACK(s) \ - UWord ESTK_DEF_STACK(s)[DEF_ESTACK_SIZE]; \ + Eterm ESTK_DEF_STACK(s)[DEF_ESTACK_SIZE]; \ ErtsEStack s = { \ ESTK_DEF_STACK(s), /* start */ \ ESTK_DEF_STACK(s), /* sp */ \ @@ -418,8 +418,8 @@ do {\ if (s.start == ESTK_DEF_STACK(s)) {\ UWord _wsz = ESTACK_COUNT(s);\ (dst)->start = erts_alloc(s.alloc_type,\ - DEF_ESTACK_SIZE * sizeof(UWord));\ - memcpy((dst)->start, s.start,_wsz*sizeof(UWord));\ + DEF_ESTACK_SIZE * sizeof(Eterm));\ + memcpy((dst)->start, s.start,_wsz*sizeof(Eterm));\ (dst)->sp = (dst)->start + _wsz;\ (dst)->end = (dst)->start + DEF_ESTACK_SIZE;\ (dst)->alloc_type = s.alloc_type;\ @@ -495,7 +495,7 @@ typedef struct { #define DEF_WSTACK_SIZE (16) -void erl_grow_wstack(ErtsWStack*, Eterm* def_stack); +void erl_grow_wstack(ErtsWStack*, UWord* def_stack); #define WSTK_CONCAT(a,b) a##b #define WSTK_DEF_STACK(s) WSTK_CONCAT(s,_default_wstack) -- cgit v1.2.3 From 1b904fd1fcec000efb33446859e75872dc00ef2b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 22 Jan 2014 16:24:00 +0100 Subject: erts: Refactor big-float compare on HALFWORD to use C-stack for the temporary conversion from float to big. Preparation for coming bugfix of 'big_buf' array size. --- erts/emulator/beam/big.c | 6 ++++-- erts/emulator/beam/erl_process.h | 1 - erts/emulator/beam/erl_term.h | 3 ++- erts/emulator/beam/erl_vm.h | 3 +-- erts/emulator/beam/utils.c | 18 ++++++++---------- 5 files changed, 15 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 2b27b111d8..4343c6cb4c 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -1603,6 +1603,8 @@ big_to_double(Wterm x, double* resp) /* * Logic has been copied from erl_bif_guard.c and slightly * modified to use a static instead of dynamic heap + * + * HALFWORD: Return relative term with 'heap' as base. */ Eterm double_to_big(double x, Eterm *heap) @@ -1633,7 +1635,7 @@ double_to_big(double x, Eterm *heap) sz = BIG_NEED_SIZE(ds); /* number of words including arity */ hp = heap; - res = make_big(hp); + res = make_big_rel(hp, heap); xp = (ErtsDigit*) (hp + 1); for (i = ds - 1; i >= 0; i--) { diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index e35d1c785c..9c8cce3cb5 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -499,7 +499,6 @@ struct ErtsSchedulerData_ { Eterm tmp_heap[TMP_HEAP_SIZE]; int num_tmp_heap_used; Eterm beam_emu_tmp_heap[BEAM_EMU_TMP_HEAP_SIZE]; - Eterm cmp_tmp_heap[CMP_TMP_HEAP_SIZE]; Eterm erl_arith_tmp_heap[ERL_ARITH_TMP_HEAP_SIZE]; #endif ErtsSchedulerSleepInfo *ssi; diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 953edf79ea..50d3e63c58 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2013. All Rights Reserved. + * Copyright Ericsson AB 2000-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -1126,6 +1126,7 @@ extern unsigned tag_val_def(Wterm); #define make_tuple_rel make_boxed_rel #define make_external_rel make_boxed_rel #define make_internal_ref_rel make_boxed_rel +#define make_big_rel make_boxed_rel #define binary_val_rel(RTERM, BASE) binary_val(rterm2wterm(RTERM, BASE)) #define list_val_rel(RTERM, BASE) list_val(rterm2wterm(RTERM, BASE)) diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 337422eead..b7de8208ad 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The 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,6 @@ heap data on the C stack or if we use the buffers in the scheduler data. */ #define TMP_HEAP_SIZE 128 /* Number of Eterm in the schedulers small heap for transient heap data */ -#define CMP_TMP_HEAP_SIZE 32 /* cmp wants its own tmp-heap... */ #define ERL_ARITH_TMP_HEAP_SIZE 4 /* as does erl_arith... */ #define BEAM_EMU_TMP_HEAP_SIZE 2 /* and beam_emu... */ diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 7f8bdcb2ca..86bb5fd3ad 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2688,11 +2688,7 @@ tailrecur_ne: { FloatDef f1, f2; Eterm big; -#if HEAP_ON_C_STACK - Eterm big_buf[CMP_TMP_HEAP_SIZE]; /* If HEAP_ON_C_STACK */ -#else - Eterm *big_buf = erts_get_scheduler_data()->cmp_tmp_heap; -#endif + Eterm big_buf[32]; #if HALFWORD_HEAP Wterm aw = is_immed(a) ? a : rterm2wterm(a,a_base); Wterm bw = is_immed(b) ? b : rterm2wterm(b,b_base); @@ -2718,8 +2714,9 @@ tailrecur_ne: /* Float is within the no loss limit */ f1.fd = signed_val(aw); j = float_comp(f1.fd, f2.fd); + } #if ERTS_SIZEOF_ETERM == 8 - } else if (f2.fd > (double) (MAX_SMALL + 1)) { + else if (f2.fd > (double) (MAX_SMALL + 1)) { /* Float is a positive bignum, i.e. bigger */ j = -1; } else if (f2.fd < (double) (MIN_SMALL - 1)) { @@ -2730,7 +2727,7 @@ tailrecur_ne: j = signed_val(aw) - (Sint) f2.fd; } #else - } else { + else { /* If float is positive it is bigger than small */ j = (f2.fd > 0.0) ? -1 : 1; } @@ -2765,7 +2762,7 @@ tailrecur_ne: } } else { big = double_to_big(f2.fd, big_buf); - j = big_comp(aw, big); + j = big_comp(aw, rterm2wterm(big,big_buf)); } if (_NUMBER_CODE(a_tag, b_tag) == FLOAT_BIG) { j = -j; @@ -2777,8 +2774,9 @@ tailrecur_ne: /* Float is within the no loss limit */ f2.fd = signed_val(bw); j = float_comp(f1.fd, f2.fd); + } #if ERTS_SIZEOF_ETERM == 8 - } else if (f1.fd > (double) (MAX_SMALL + 1)) { + else if (f1.fd > (double) (MAX_SMALL + 1)) { /* Float is a positive bignum, i.e. bigger */ j = 1; } else if (f1.fd < (double) (MIN_SMALL - 1)) { @@ -2789,7 +2787,7 @@ tailrecur_ne: j = (Sint) f1.fd - signed_val(bw); } #else - } else { + else { /* If float is positive it is bigger than small */ j = (f1.fd > 0.0) ? 1 : -1; } -- cgit v1.2.3 From af0227d7591bde8927ea95c93cbadee6b812b1d9 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 22 Jan 2014 16:24:48 +0100 Subject: erts: Fix crash when comparing very large floats with integers big_buf was one word too short on 32-bit emulators causing memory corruption. Seems like this did not cause a problem before the ESTACK memory layout was changed in 172ebf11dc455e22b87f. --- erts/emulator/beam/big.c | 3 ++- erts/emulator/beam/big.h | 4 ++-- erts/emulator/beam/utils.c | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 4343c6cb4c..41a041eba6 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1607,7 +1607,7 @@ big_to_double(Wterm x, double* resp) * HALFWORD: Return relative term with 'heap' as base. */ Eterm -double_to_big(double x, Eterm *heap) +double_to_big(double x, Eterm *heap, Uint hsz) { int is_negative; int ds; @@ -1638,6 +1638,7 @@ double_to_big(double x, Eterm *heap) res = make_big_rel(hp, heap); xp = (ErtsDigit*) (hp + 1); + ASSERT(ds < hsz); for (i = ds - 1; i >= 0; i--) { ErtsDigit d; diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 1a7b14170f..d80111822e 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The 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 @@ Eterm big_lshift(Eterm, Sint, Eterm*); int big_comp (Wterm, Wterm); int big_ucomp (Eterm, Eterm); int big_to_double(Wterm x, double* resp); -Eterm double_to_big(double, Eterm*); +Eterm double_to_big(double, Eterm*, Uint hsz); Eterm small_to_big(Sint, Eterm*); Eterm uint_to_big(Uint, Eterm*); Eterm uword_to_big(UWord, Eterm*); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 86bb5fd3ad..e0776cf67d 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2688,7 +2688,6 @@ tailrecur_ne: { FloatDef f1, f2; Eterm big; - Eterm big_buf[32]; #if HALFWORD_HEAP Wterm aw = is_immed(a) ? a : rterm2wterm(a,a_base); Wterm bw = is_immed(b) ? b : rterm2wterm(b,b_base); @@ -2699,6 +2698,8 @@ tailrecur_ne: #define MAX_LOSSLESS_FLOAT ((double)((1LL << 53) - 2)) #define MIN_LOSSLESS_FLOAT ((double)(((1LL << 53) - 2)*-1)) #define BIG_ARITY_FLOAT_MAX (1024 / D_EXP) /* arity of max float as a bignum */ + Eterm big_buf[BIG_NEED_SIZE(BIG_ARITY_FLOAT_MAX)]; + b_tag = tag_val_def(bw); switch(_NUMBER_CODE(a_tag, b_tag)) { @@ -2761,7 +2762,7 @@ tailrecur_ne: j = float_comp(f1.fd, f2.fd); } } else { - big = double_to_big(f2.fd, big_buf); + big = double_to_big(f2.fd, big_buf, sizeof(big_buf)/sizeof(Eterm)); j = big_comp(aw, rterm2wterm(big,big_buf)); } if (_NUMBER_CODE(a_tag, b_tag) == FLOAT_BIG) { -- cgit v1.2.3 From 4343225d9d26a5c2476bc083c686e2a3816df506 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 23 Jan 2014 10:24:45 +0100 Subject: erts: fix unicode printing of gdb printouts --- erts/test/z_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl index d9892f21c3..056561d3db 100644 --- a/erts/test/z_SUITE.erl +++ b/erts/test/z_SUITE.erl @@ -242,7 +242,7 @@ dump_core(#core_search_conf{ cerl = Cerl }, Core) -> _ -> os:cmd(Cerl ++ " -dump " ++ Core) end, - ct:log("~s~n~n~s",[Core,Dump]). + ct:log("~ts~n~n~ts",[Core,Dump]). format_core(Conf, {ignore, Core}) -> -- cgit v1.2.3 From 29196a3528e034d3d46ec4589749074f8d766b97 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 23 Jan 2014 12:28:13 +0100 Subject: erts: Fix faulty assert in match spec engine. --- erts/emulator/beam/erl_db_util.c | 4 ++-- erts/emulator/test/match_spec_SUITE.erl | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index ef3749a2c4..a358ecf326 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -1838,7 +1838,7 @@ restart: ep = termp; break; case matchArrayBind: /* When the array size is unknown. */ - ASSERT(termp); + ASSERT(termp || arity==0); n = *pc++; variables[n].term = dpm_array_to_list(psp, termp, arity); break; diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index bcc46d78ba..330bef7104 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2014. All Rights Reserved. %% %% The 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,7 @@ trace_control_word/1, silent/1, silent_no_ms/1, silent_test/1, ms_trace2/1, ms_trace3/1, boxed_and_small/1, destructive_in_test_bif/1, guard_exceptions/1, + empty_list/1, unary_plus/1, unary_minus/1, moving_labels/1]). -export([fpe/1]). -export([otp_9422/1]). @@ -60,6 +61,7 @@ all() -> guard_exceptions, unary_plus, unary_minus, fpe, moving_labels, faulty_seq_trace, + empty_list, otp_9422]; true -> [not_run] end. @@ -897,6 +899,11 @@ fpe(Config) when is_list(Config) -> _ -> ok end. +empty_list(Config) when is_list(Config) -> + Val=[{'$1',[], [{message,'$1'},{message,{caller}},{return_trace}]}], + %% Did crash debug VM in faulty assert: + erlang:match_spec_test([],Val,trace). + moving_labels(Config) when is_list(Config) -> %% Force an andalso/orelse construction to be moved by placing it %% in a tuple followed by a constant term. Labels should still -- cgit v1.2.3 From e7ea832a4a3a8ba2f94ce02a47ca34b60277cb0a Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 16 Jan 2014 23:41:47 +0100 Subject: Add support for scheduler utilization balancing For more information see documentation of the new command line argument +sub --- erts/doc/src/erl.xml | 27 +++ erts/emulator/beam/erl_init.c | 26 +++ erts/emulator/beam/erl_process.c | 424 +++++++++++++++++++++++++++++++++++---- erts/emulator/beam/erl_process.h | 104 ++++++++-- erts/etc/common/erlexec.c | 1 + 5 files changed, 525 insertions(+), 57 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index bf0d132955..2aa247e293 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -941,6 +941,10 @@ when schedulers frequently run out of work. When disabled, the frequency with which schedulers run out of work will not be taken into account by the load balancing logic. +
  +scl false is similar to + +sub true with the difference + that +sub true also will balance scheduler utilization + between schedulers.

+sct CpuTopology @@ -1087,6 +1091,29 @@ documentation of the +sbt flag.

+ +sub true|false + +

Enable or disable + scheduler + utilization balancing of load. By default scheduler + utilization balancing is disabled and instead scheduler + compaction of load is enabled which will strive for a load + distribution which causes as many scheduler threads as possible + to be fully loaded (i.e., not run out of work). When scheduler + utilization balancing is enabled the system will instead try to + balance scheduler utilization between schedulers. That is, + strive for equal scheduler utilization on all schedulers. +
   +sub true is only supported on + systems where the runtime system detects and use a monotonically + increasing high resolution clock. On other systems, the runtime + system will fail to start. +
   +sub true implies + +scl false. The difference + between +sub true and +scl false is that + +scl false will not try to balance the scheduler + utilization. +

+
+sws very_eager|eager|medium|lazy|very_lazy

diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 1af80dd04b..1d4f617746 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -537,6 +537,12 @@ void erts_usage(void) erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); erts_fprintf(stderr, "-sct cput set cpu topology,\n"); erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT + erts_fprintf(stderr, "-sub bool enable/disable scheduler utilization balancing,\n"); +#else + erts_fprintf(stderr, "-sub false disable scheduler utilization balancing,\n"); +#endif + erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); erts_fprintf(stderr, "-sws val set scheduler wakeup strategy, valid values are:\n"); erts_fprintf(stderr, " default|legacy.\n"); erts_fprintf(stderr, "-swct val set scheduler wake cleanup threshold, valid values are:\n"); @@ -1512,6 +1518,26 @@ erl_start(int argc, char **argv) erts_usage(); } } + else if (has_prefix("ub", sub_param)) { + arg = get_arg(sub_param+2, argv[i+1], &i); + if (sys_strcmp("true", arg) == 0) { +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT + erts_sched_balance_util = 1; +#else + erts_fprintf(stderr, + "scheduler utilization balancing not " + "supported on this system\n"); + erts_usage(); +#endif + } + else if (sys_strcmp("false", arg) == 0) + erts_sched_balance_util = 0; + else { + erts_fprintf(stderr, "bad scheduler utilization balancing " + " value '%s'\n", arg); + erts_usage(); + } + } else if (has_prefix("wct", sub_param)) { arg = get_arg(sub_param+3, argv[i+1], &i); if (erts_sched_set_wake_cleanup_threshold(arg) != 0) { diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 21fd8dd50a..6143c5fa3a 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -144,6 +144,7 @@ extern BeamInstr beam_exit[]; extern BeamInstr beam_continue_exit[]; int erts_sched_compact_load; +int erts_sched_balance_util = 0; Uint erts_no_schedulers; #define ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_VERY_LAZY (4*1024*1024) @@ -608,6 +609,7 @@ erts_late_init_process(void) static void init_sched_wall_time(ErtsSchedWallTime *swtp) { + swtp->need = erts_sched_balance_util; swtp->enabled = 0; swtp->start = 0; swtp->working.total = 0; @@ -630,27 +632,253 @@ sched_wall_time_ts(void) #endif } +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + +#ifdef ARCH_64 + +static ERTS_INLINE Uint64 +aschedtime_read(ErtsAtomicSchedTime *var) +{ + return (Uint64) erts_atomic_read_nob((erts_atomic_t *) var); +} + +static ERTS_INLINE void +aschedtime_set(ErtsAtomicSchedTime *var, Uint64 val) +{ + erts_atomic_set_nob((erts_atomic_t *) var, (erts_aint_t) val); +} + +static ERTS_INLINE void +aschedtime_init(ErtsAtomicSchedTime *var) +{ + erts_atomic_init_nob((erts_atomic_t *) var, (erts_aint_t) 0); +} + +#elif defined(ARCH_32) + +static ERTS_INLINE Uint64 +aschedtime_read(ErtsAtomicSchedTime *var) +{ + erts_dw_aint_t dw; + erts_dw_atomic_read_nob((erts_dw_atomic_t *) var, &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 ERTS_INLINE void +aschedtime_set(ErtsAtomicSchedTime *var, Uint64 val) +{ + erts_dw_aint_t dw; +#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 + erts_dw_atomic_set_nob((erts_dw_atomic_t *) var, &dw); +} + +static ERTS_INLINE void +aschedtime_init(ErtsAtomicSchedTime *var) +{ + erts_dw_aint_t dw; + dw.sint[ERTS_DW_AINT_LOW_WORD] = (erts_aint_t) 0; + dw.sint[ERTS_DW_AINT_HIGH_WORD] = (erts_aint_t) 0; + erts_dw_atomic_init_nob((erts_dw_atomic_t *) var, &dw); +} + +#else +# error :-/ +#endif + +#define ERTS_GET_AVG_MAX_UNLOCKED_TRY 50 +#define ERTS_SCHED_AVG_UTIL_WRITE_MARKER (~((Uint64) 0)) + +/* Intervals in nanoseconds */ +#define ERTS_SCHED_UTIL_SHORT_INTERVAL ((Uint64) 1*1000*1000*1000) +#define ERTS_SCHED_UTIL_LONG_INTERVAL ((Uint64) 10*1000*1000*1000) + + +#define ERTS_SCHED_UTIL_IGNORE_IMBALANCE_DIFF 5000 /* ppm */ + +static ERTS_INLINE Uint64 +calc_sched_worktime(int is_working, Uint64 now, Uint64 last, + Uint64 interval, Uint64 old_worktime) +{ + Uint64 worktime; + Uint64 new; + + if (now <= last) + return old_worktime; + + new = now - last; + + if (new >= interval) + return is_working ? interval : (Uint64) 0; + + + /* + * Division by 1000 in order to avoid + * overflow. If changed update assertions + * in init_runq_sched_util(). + */ + worktime = old_worktime; + worktime *= (interval - new)/1000; + worktime /= (interval/1000); + if (is_working) + worktime += new; + + ASSERT(0 <= worktime && worktime <= interval); + + return worktime; +} + +static ERTS_INLINE void +update_avg_sched_util(ErtsSchedulerData *esdp, Uint64 now, int is_working) +{ + ErtsRunQueue *rq; + int worked; + Uint64 swt, lwt, last; + + rq = esdp->run_queue; + last = aschedtime_read(&rq->sched_util.last); + + if (now <= last) { + ASSERT(last == ERTS_SCHED_AVG_UTIL_WRITE_MARKER); + return; + } + + ASSERT(now >= last); + + worked = rq->sched_util.is_working; + + swt = calc_sched_worktime(worked, now, last, ERTS_SCHED_UTIL_SHORT_INTERVAL, + rq->sched_util.worktime.short_interval); + lwt = calc_sched_worktime(worked, now, last, ERTS_SCHED_UTIL_LONG_INTERVAL, + rq->sched_util.worktime.long_interval); + + aschedtime_set(&rq->sched_util.last, ERTS_SCHED_AVG_UTIL_WRITE_MARKER); + ERTS_THR_WRITE_MEMORY_BARRIER; + rq->sched_util.is_working = is_working; + rq->sched_util.worktime.short_interval = swt; + rq->sched_util.worktime.long_interval = lwt; + ERTS_THR_WRITE_MEMORY_BARRIER; + aschedtime_set(&rq->sched_util.last, now); +} + +int +erts_get_sched_util(ErtsRunQueue *rq, int initially_locked, int short_interval) +{ + /* Average scheduler utilization in ppm */ + int util, is_working, try = 0, locked = initially_locked; + Uint64 worktime, old_worktime, now, last, interval, *old_worktimep; + + if (short_interval) { + old_worktimep = &rq->sched_util.worktime.short_interval; + interval = ERTS_SCHED_UTIL_SHORT_INTERVAL; + } + else { + old_worktimep = &rq->sched_util.worktime.long_interval; + interval = ERTS_SCHED_UTIL_LONG_INTERVAL; + } + + while (1) { + Uint64 chk_last; + last = aschedtime_read(&rq->sched_util.last); + ERTS_THR_READ_MEMORY_BARRIER; + is_working = rq->sched_util.is_working; + old_worktime = *old_worktimep; + ERTS_THR_READ_MEMORY_BARRIER; + chk_last = aschedtime_read(&rq->sched_util.last); + if (chk_last == last) + break; + if (!locked) { + if (++try >= ERTS_GET_AVG_MAX_UNLOCKED_TRY) { + /* Writer will eventually block on runq-lock */ + erts_smp_runq_lock(rq); + locked = 1; + } + } + } + + if (!initially_locked && locked) + erts_smp_runq_unlock(rq); + + now = sched_wall_time_ts(); + worktime = calc_sched_worktime(is_working, now, last, interval, old_worktime); + + util = (int) ((worktime * 1000000)/interval); + + ASSERT(0 <= util && util <= 1000000); + + return util; +} + +static void +init_runq_sched_util(ErtsRunQueueSchedUtil *rqsu, int enabled) +{ + aschedtime_init(&rqsu->last); + if (!enabled) + aschedtime_set(&rqsu->last, ERTS_SCHED_AVG_UTIL_WRITE_MARKER); + rqsu->is_working = 0; + rqsu->worktime.short_interval = (Uint64) 0; + rqsu->worktime.long_interval = (Uint64) 0; + +#ifdef DEBUG + { + Uint64 intrvl; + /* + * If one of these asserts fail we may have + * overflow in calc_sched_worktime(). Which + * have to be fixed either by shrinking + * interval size, or fix calculation of + * worktime in calc_sched_worktime(). + */ + intrvl = ERTS_SCHED_UTIL_SHORT_INTERVAL; + ASSERT(intrvl*(intrvl/1000) > intrvl); + intrvl = ERTS_SCHED_UTIL_LONG_INTERVAL; + ASSERT(intrvl*(intrvl/1000) > intrvl); + } +#endif +} + +#endif /* ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT */ + static ERTS_INLINE void sched_wall_time_change(ErtsSchedulerData *esdp, int working) { - if (esdp->sched_wall_time.enabled) { + if (esdp->sched_wall_time.need) { Uint64 ts = sched_wall_time_ts(); - if (working) { +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + update_avg_sched_util(esdp, ts, working); +#endif + if (esdp->sched_wall_time.enabled) { + if (working) { #ifdef DEBUG - ASSERT(!esdp->sched_wall_time.working.currently); - esdp->sched_wall_time.working.currently = 1; + ASSERT(!esdp->sched_wall_time.working.currently); + esdp->sched_wall_time.working.currently = 1; #endif - ts -= esdp->sched_wall_time.start; - esdp->sched_wall_time.working.start = ts; - } - else { + ts -= esdp->sched_wall_time.start; + esdp->sched_wall_time.working.start = ts; + } + else { #ifdef DEBUG - ASSERT(esdp->sched_wall_time.working.currently); - esdp->sched_wall_time.working.currently = 0; + ASSERT(esdp->sched_wall_time.working.currently); + esdp->sched_wall_time.working.currently = 0; #endif - ts -= esdp->sched_wall_time.start; - ts -= esdp->sched_wall_time.working.start; - esdp->sched_wall_time.working.total += ts; + ts -= esdp->sched_wall_time.start; + ts -= esdp->sched_wall_time.working.start; + esdp->sched_wall_time.working.total += ts; + } } } } @@ -705,10 +933,13 @@ reply_sched_wall_time(void *vswtrp) ASSERT(esdp); if (swtrp->set) { - if (!swtrp->enable && esdp->sched_wall_time.enabled) + if (!swtrp->enable && esdp->sched_wall_time.enabled) { + esdp->sched_wall_time.need = erts_sched_balance_util; esdp->sched_wall_time.enabled = 0; + } else if (swtrp->enable && !esdp->sched_wall_time.enabled) { Uint64 ts = sched_wall_time_ts(); + esdp->sched_wall_time.need = 1; esdp->sched_wall_time.enabled = 1; esdp->sched_wall_time.start = ts; esdp->sched_wall_time.working.total = 0; @@ -2084,9 +2315,8 @@ ongoing_multi_scheduling_block(void) } static ERTS_INLINE void -empty_runq(ErtsRunQueue *rq) +empty_runq_aux(ErtsRunQueue *rq, Uint32 old_flags) { - 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); @@ -2106,6 +2336,23 @@ empty_runq(ErtsRunQueue *rq) } } +static ERTS_INLINE void +empty_runq(ErtsRunQueue *rq) +{ + Uint32 old_flags = ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_NONEMPTY|ERTS_RUNQ_FLG_PROTECTED); + empty_runq_aux(rq, old_flags); +} + +static ERTS_INLINE Uint32 +empty_protected_runq(ErtsRunQueue *rq) +{ + Uint32 old_flags = ERTS_RUNQ_FLGS_BSET(rq, + ERTS_RUNQ_FLG_NONEMPTY|ERTS_RUNQ_FLG_PROTECTED, + ERTS_RUNQ_FLG_PROTECTED); + empty_runq_aux(rq, old_flags); + return old_flags; +} + static ERTS_INLINE void non_empty_runq(ErtsRunQueue *rq) { @@ -2130,6 +2377,18 @@ non_empty_runq(ErtsRunQueue *rq) } } +void +erts_empty_runq(ErtsRunQueue *rq) +{ + empty_runq(rq); +} + +void +erts_non_empty_runq(ErtsRunQueue *rq) +{ + non_empty_runq(rq); +} + static erts_aint32_t sched_prep_spin_wait(ErtsSchedulerSleepInfo *ssi) { @@ -2632,7 +2891,7 @@ ssi_flags_set_wake(ErtsSchedulerSleepInfo *ssi) } static void -wake_scheduler(ErtsRunQueue *rq, int incq) +wake_scheduler(ErtsRunQueue *rq) { ErtsSchedulerSleepInfo *ssi; erts_aint32_t flgs; @@ -2651,9 +2910,6 @@ wake_scheduler(ErtsRunQueue *rq, int incq) flgs = ssi_flags_set_wake(ssi); erts_sched_finish_poke(ssi, flgs); - - if (incq && (flgs & ERTS_SSI_FLG_WAITING)) - non_empty_runq(rq); } #define ERTS_NO_USED_RUNQS_SHIFT 16 @@ -2744,7 +3000,7 @@ chk_wake_sched(ErtsRunQueue *crq, int ix, int activate) if (try_inc_no_active_runqs(ix+1)) (void) ERTS_RUNQ_FLGS_UNSET(wrq, ERTS_RUNQ_FLG_INACTIVE); } - wake_scheduler(wrq, 0); + wake_scheduler(wrq); return 1; } return 0; @@ -2792,7 +3048,7 @@ smp_notify_inc_runq(ErtsRunQueue *runq) { #ifdef ERTS_SMP if (runq) - wake_scheduler(runq, 1); + wake_scheduler(runq); #endif } @@ -2810,7 +3066,7 @@ erts_sched_notify_check_cpu_bind(void) for (ix = 0; ix < erts_no_run_queues; ix++) { ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND); - wake_scheduler(rq, 0); + wake_scheduler(rq); } #else erts_sched_check_cpu_bind(erts_get_scheduler_data()); @@ -2938,6 +3194,11 @@ check_immigration_need(ErtsRunQueue *c_rq, ErtsMigrationPath *mp, int prio) if (!f_rq) return NULL; +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + if (mp->sched_util) + return NULL; +#endif + f_rq_flags = ERTS_RUNQ_FLGS_GET(f_rq); if (f_rq_flags & ERTS_RUNQ_FLG_PROTECTED) return NULL; @@ -3077,7 +3338,7 @@ suspend_run_queue(ErtsRunQueue *rq) ERTS_SSI_FLG_SUSPENDED); (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_SUSPENDED); - wake_scheduler(rq, 0); + wake_scheduler(rq); } static void scheduler_ix_resume_wake(Uint ix); @@ -3169,6 +3430,9 @@ evacuate_run_queue(ErtsRunQueue *rq, to_rq->misc.start = start; to_rq->misc.end = end; + + non_empty_runq(to_rq); + erts_smp_runq_unlock(to_rq); smp_notify_inc_runq(to_rq); erts_smp_runq_lock(to_rq); @@ -3381,7 +3645,7 @@ try_steal_task(ErtsRunQueue *rq) Uint32 flags; /* Protect jobs we steal from getting stolen from us... */ - flags = ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_PROTECTED); + flags = empty_protected_runq(rq); if (flags & ERTS_RUNQ_FLG_SUSPENDED) return 0; /* go suspend instead... */ @@ -3460,6 +3724,9 @@ typedef struct { int full_reds_history_change; int oowc; int max_len; +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + int sched_util; +#endif } ErtsRunQueueBalance; static ErtsRunQueueBalance *run_queue_info; @@ -3623,6 +3890,9 @@ check_balance(ErtsRunQueue *c_rq) Sint64 scheds_reds, full_scheds_reds; int forced, active, current_active, oowc, half_full_scheds, full_scheds, mmax_len, blnc_no_rqs, qix, pix, freds_hist_ix; +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + int sched_util_balancing; +#endif if (erts_smp_atomic32_xchg_nob(&balance_info.checking_balance, 1)) { c_rq->check_balance_reds = INT_MAX; @@ -3678,6 +3948,10 @@ check_balance(ErtsRunQueue *c_rq) return; } +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + sched_util_balancing = 0; +#endif + freds_hist_ix = balance_info.full_reds_history_index; balance_info.full_reds_history_index++; if (balance_info.full_reds_history_index >= ERTS_FULL_REDS_HISTORY_SIZE) @@ -3708,7 +3982,12 @@ check_balance(ErtsRunQueue *c_rq) run_queue_info[qix].oowc = rq->out_of_work_count; run_queue_info[qix].max_len = rq->max_len; rq->check_balance_reds = INT_MAX; - + +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + if (erts_sched_balance_util) + run_queue_info[qix].sched_util = erts_get_sched_util(rq, 1, 0); +#endif + erts_smp_runq_unlock(rq); } @@ -3778,8 +4057,38 @@ check_balance(ErtsRunQueue *c_rq) mmax_len = run_queue_info[qix].max_len; } - if (!erts_sched_compact_load) + if (!erts_sched_compact_load) { +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + if (erts_sched_balance_util && full_scheds < blnc_no_rqs) { + int avg_util = 0; + + for (qix = 0; qix < blnc_no_rqs; qix++) + avg_util += run_queue_info[qix].sched_util; + + avg_util /= blnc_no_rqs; /* in ppm */ + + sched_util_balancing = 1; + /* + * In order to avoid renaming a large amount of fields + * we write utilization values instead of lenght values + * in the 'max_len' and 'migration_limit' fields... + */ + for (qix = 0; qix < blnc_no_rqs; qix++) { + run_queue_info[qix].flags = 0; /* Reset for later use... */ + for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) { + run_queue_info[qix].prio[pix].emigrate_to = -1; + run_queue_info[qix].prio[pix].immigrate_from = -1; + run_queue_info[qix].prio[pix].avail = 100; + run_queue_info[qix].prio[pix].max_len = run_queue_info[qix].sched_util; + run_queue_info[qix].prio[pix].migration_limit = avg_util; + } + } + active = blnc_no_rqs; + goto setup_migration_paths; + } +#endif goto all_active; + } if (!forced && half_full_scheds != blnc_no_rqs) { int min = 1; @@ -3896,15 +4205,30 @@ check_balance(ErtsRunQueue *c_rq) } } +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + setup_migration_paths: +#endif + /* Setup migration paths for all priorities */ for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) { int low = 0, high = 0; for (qix = 0; qix < blnc_no_rqs; qix++) { int len_diff = run_queue_info[qix].prio[pix].max_len; len_diff -= run_queue_info[qix].prio[pix].migration_limit; + #ifdef DBG_PRINT if (pix == 2) erts_fprintf(stderr, "%d ", len_diff); #endif + +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + if (sched_util_balancing + && -ERTS_SCHED_UTIL_IGNORE_IMBALANCE_DIFF <= len_diff + && len_diff <= ERTS_SCHED_UTIL_IGNORE_IMBALANCE_DIFF) { + /* ignore minor imbalance */ + len_diff = 0; + } +#endif + run_queue_compare[qix].qix = qix; run_queue_compare[qix].len = len_diff; if (len_diff != 0) { @@ -4031,6 +4355,9 @@ erts_fprintf(stderr, "--------------------------------\n"); Uint32 flags = run_queue_info[qix].flags; ErtsMigrationPath *mp = &new_mpaths->mpath[qix]; +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + mp->sched_util = sched_util_balancing; +#endif mp->flags = flags; mp->misc_evac_runq = NULL; @@ -4628,6 +4955,11 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) set_wakeup_other_data(); #endif +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + if (erts_sched_balance_util) + erts_sched_compact_load = 0; +#endif + ASSERT(no_schedulers_online <= no_schedulers); ASSERT(no_schedulers_online >= 1); ASSERT(no_schedulers >= 1); @@ -4696,6 +5028,11 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) rq->ports.info.reds = 0; rq->ports.start = NULL; rq->ports.end = NULL; + +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + init_runq_sched_util(&rq->sched_util, erts_sched_balance_util); +#endif + } #ifdef ERTS_SMP @@ -4794,6 +5131,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) esdp->reductions = 0; init_sched_wall_time(&esdp->sched_wall_time); + erts_port_task_handle_init(&esdp->nosuspend_port_task_handle); } @@ -5761,7 +6099,7 @@ erts_set_schedulers_online(Process *p, for (ix = no; ix < online; ix++) { ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); - wake_scheduler(rq, 0); + wake_scheduler(rq); } } } @@ -5860,7 +6198,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) for (ix = 1; ix < online; ix++) { ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); - wake_scheduler(rq, 0); + wake_scheduler(rq); } if (erts_smp_atomic32_read_nob(&schdlr_sspnd.active) @@ -7294,7 +7632,7 @@ Process *schedule(Process *p, int calls) continue_check_activities_to_run: flags = ERTS_RUNQ_FLGS_GET_NOB(rq); continue_check_activities_to_run_known_flags: - + ASSERT(flags & ERTS_RUNQ_FLG_NONEMPTY); if (flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND|ERTS_RUNQ_FLG_SUSPENDED)) { @@ -7346,20 +7684,16 @@ Process *schedule(Process *p, int calls) rq->wakeup_other = 0; rq->wakeup_other_reds = 0; - empty_runq(rq); - flags = ERTS_RUNQ_FLGS_GET_NOB(rq); - if (flags & ERTS_RUNQ_FLG_SUSPENDED) { - non_empty_runq(rq); + if (flags & ERTS_RUNQ_FLG_SUSPENDED) goto continue_check_activities_to_run_known_flags; - } - else if (!(flags & ERTS_RUNQ_FLG_INACTIVE)) { - if (try_steal_task(rq)) { - non_empty_runq(rq); + if (flags & ERTS_RUNQ_FLG_INACTIVE) + empty_runq(rq); + else { + if (try_steal_task(rq)) goto continue_check_activities_to_run; - } - (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); + empty_runq(rq); /* * Check for ERTS_RUNQ_FLG_SUSPENDED has to be done @@ -7371,7 +7705,6 @@ Process *schedule(Process *p, int calls) goto continue_check_activities_to_run_known_flags; } } - #endif scheduler_wait(&fcalls, esdp, rq); @@ -8486,6 +8819,10 @@ erts_schedule_misc_op(void (*func)(void *), void *arg) rq->misc.start = molp; rq->misc.end = molp; +#ifdef ERTS_SMP + non_empty_runq(rq); +#endif + erts_smp_runq_unlock(rq); smp_notify_inc_runq(rq); @@ -9372,8 +9709,11 @@ save_pending_exiter(Process *p) erts_proclist_store_last(&rq->procs.pending_exiters, plp); + non_empty_runq(rq); + erts_smp_runq_unlock(rq); - wake_scheduler(rq, 1); + + wake_scheduler(rq); } #endif diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 043621125c..c2ef55f2d5 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -70,6 +70,9 @@ typedef struct process Process; struct ErtsNodesMonitor_; +#define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT 0 +#define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT 0 + #define ERTS_MAX_NO_OF_SCHEDULERS 1024 #define ERTS_DEFAULT_MAX_PROCESSES (1 << 18) @@ -98,6 +101,7 @@ struct saved_calls { extern Export exp_send, exp_receive, exp_timeout; extern int erts_sched_compact_load; +extern int erts_sched_balance_util; extern Uint erts_no_schedulers; extern Uint erts_no_run_queues; extern int erts_sched_thread_suggested_stack_size; @@ -198,6 +202,10 @@ extern int erts_sched_thread_suggested_stack_size; #define ERTS_RUNQ_FLGS_SET(RQ, FLGS) \ ((Uint32) erts_smp_atomic32_read_bor_relb(&(RQ)->flags, \ (erts_aint32_t) (FLGS))) +#define ERTS_RUNQ_FLGS_BSET(RQ, MSK, FLGS) \ + ((Uint32) erts_smp_atomic32_read_bset_relb(&(RQ)->flags, \ + (erts_aint32_t) (MSK), \ + (erts_aint32_t) (FLGS))) #define ERTS_RUNQ_FLGS_UNSET(RQ, FLGS) \ ((Uint32) erts_smp_atomic32_read_band_relb(&(RQ)->flags, \ (erts_aint32_t) ~(FLGS))) @@ -316,9 +324,40 @@ typedef struct { int reds; } ErtsRunQueueInfo; + +#ifdef HAVE_GETHRTIME +# undef ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT +# define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT 1 +#endif + #ifdef ERTS_SMP +#undef ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT +#define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT + +#ifdef ARCH_64 +typedef erts_atomic_t ErtsAtomicSchedTime; +#elif defined(ARCH_32) +typedef erts_dw_atomic_t ErtsAtomicSchedTime; +#else +# error :-/ +#endif + +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT +typedef struct { + ErtsAtomicSchedTime last; + struct { + Uint64 short_interval; + Uint64 long_interval; + } worktime; + int is_working; +} ErtsRunQueueSchedUtil; +#endif + typedef struct { +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + int sched_util; +#endif Uint32 flags; ErtsRunQueue *misc_evac_runq; struct { @@ -385,6 +424,9 @@ struct ErtsRunQueue_ { Port *start; Port *end; } ports; +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + ErtsRunQueueSchedUtil sched_util; +#endif }; #ifdef ERTS_SMP @@ -414,6 +456,7 @@ do { \ } while (0) typedef struct { + int need; /* "+sbu true" or scheduler_wall_time enabled */ int enabled; Uint64 start; struct { @@ -542,6 +585,12 @@ int erts_smp_lc_runq_is_locked(ErtsRunQueue *); #ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS +#ifdef ERTS_SMP +void erts_empty_runq(ErtsRunQueue *rq); +void erts_non_empty_runq(ErtsRunQueue *rq); +#endif + + /* * Run queue locked during modifications. We use atomic ops since * other threads peek at values without run queue lock. @@ -574,6 +623,10 @@ erts_smp_inc_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio) erts_smp_atomic32_set_relb(&rqi->len, len); +#ifdef ERTS_SMP + if (rq->len == 0) + erts_non_empty_runq(rq); +#endif rq->len++; if (rq->max_len < rq->len) rq->max_len = len; @@ -1695,6 +1748,13 @@ erts_proc_set_error_handler(Process *p, ErtsProcLocks plocks, Eterm handler) extern erts_atomic_t erts_migration_paths; +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT +int erts_get_sched_util(ErtsRunQueue *rq, + int initially_locked, + int short_interval); +#endif + + 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, @@ -1746,22 +1806,36 @@ erts_check_emigration_need(ErtsRunQueue *c_rq, int prio) return mp->prio[prio].runq; } - - if (prio == ERTS_PORT_PRIO_LEVEL) - len = RUNQ_READ_LEN(&c_rq->ports.info.len); +#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT + if (mp->sched_util) { + ErtsRunQueue *rq = mp->prio[prio].runq; + /* No migration if other is non-empty */ + if (!(ERTS_RUNQ_FLGS_GET(rq) & ERTS_RUNQ_FLG_NONEMPTY) + && erts_get_sched_util(rq, 0, 1) < mp->prio[prio].limit.other + && erts_get_sched_util(c_rq, 0, 1) > mp->prio[prio].limit.this) { + return rq; + } + } 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; +#endif + { + + 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; + } } } } diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index c30203c632..78a50744ef 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -135,6 +135,7 @@ static char *pluss_val_switches[] = { "ws", "ss", "pp", + "ub", NULL }; /* +h arguments with values */ -- cgit v1.2.3 From 3ff735cc034dea43e593b5e58b91be95268cbf85 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 24 Jan 2014 12:45:27 +0100 Subject: Disable scheduler utilization balancing if +scl true is passed --- erts/emulator/beam/erl_init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 1d4f617746..19088fd913 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -1439,8 +1439,10 @@ erl_start(int argc, char **argv) } else if (has_prefix("cl", sub_param)) { arg = get_arg(sub_param+2, argv[i+1], &i); - if (sys_strcmp("true", arg) == 0) + if (sys_strcmp("true", arg) == 0) { erts_sched_compact_load = 1; + erts_sched_balance_util = 0; + } else if (sys_strcmp("false", arg) == 0) erts_sched_compact_load = 0; else { -- cgit v1.2.3 From 0b36ce01e81744f4c0b41bfe1f62b4bf5d0ece97 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 27 Jan 2014 12:01:45 +0100 Subject: erts/kernel: sendfile no longer uses async threads This has been done because a slow client attack is possible if the async thread pool is used. The scenario is: Client does a request for a file and then slowly receives the file one byte at a time. This will eventually fill the async thread pool with blocking sendfile operations and thus starving the vm of all file operations. If you still want to use the async threads pool for sendfile an option to enable it has been introduced. --- erts/emulator/drivers/common/efile_drv.c | 33 ++++++++++++++++++++----------- erts/preloaded/ebin/prim_file.beam | Bin 44612 -> 44880 bytes erts/preloaded/src/prim_file.erl | 17 +++++++++++++--- 3 files changed, 36 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 8de578d8b7..23c5cde7ed 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -168,7 +168,7 @@ dt_private *get_dt_private(int); #ifdef USE_THREADS -#define IF_THRDS if (sys_info.async_threads > 0) +#define THRDS_AVAILABLE (sys_info.async_threads > 0) #ifdef HARDDEBUG /* HARDDEBUG in io.c is expected too */ #define TRACE_DRIVER fprintf(stderr, "Efile: ") #else @@ -178,24 +178,26 @@ dt_private *get_dt_private(int); #define MUTEX_LOCK(m) do { IF_THRDS { TRACE_DRIVER; driver_pdl_lock(m); } } while (0) #define MUTEX_UNLOCK(m) do { IF_THRDS { TRACE_DRIVER; driver_pdl_unlock(m); } } while (0) #else -#define IF_THRDS if (0) +#define THRDS_AVAILABLE (0) #define MUTEX_INIT(m, p) #define MUTEX_LOCK(m) #define MUTEX_UNLOCK(m) #endif +#define IF_THRDS if (THRDS_AVAILABLE) +#define SENDFILE_FLGS_USE_THREADS (1 << 0) /** * On DARWIN sendfile can deadlock with close if called in * different threads. So until Apple fixes so that sendfile * is not buggy we disable usage of the async pool for * DARWIN. The testcase t_sendfile_crashduring reproduces - * this error when using +A 10. + * this error when using +A 10 and enabling SENDFILE_FLGS_USE_THREADS. */ #if defined(__APPLE__) && defined(__MACH__) -#define USE_THRDS_FOR_SENDFILE 0 +#define USE_THRDS_FOR_SENDFILE(DATA) 0 #else -#define USE_THRDS_FOR_SENDFILE (sys_info.async_threads > 0) +#define USE_THRDS_FOR_SENDFILE(DATA) (DATA->flags & SENDFILE_FLGS_USE_THREADS) #endif /* defined(__APPLE__) && defined(__MACH__) */ @@ -301,7 +303,7 @@ static void file_stop_select(ErlDrvEvent event, void* _); enum e_timer {timer_idle, timer_again, timer_write}; #ifdef HAVE_SENDFILE enum e_sendfile {sending, not_sending}; -static void free_sendfile(void *data); +#define SENDFILE_USE_THREADS (1 << 0) #endif /* HAVE_SENDFILE */ struct t_data; @@ -1933,7 +1935,7 @@ static void invoke_sendfile(void *data) d->c.sendfile.written += nbytes; - if (result == 1 || (result == 0 && USE_THRDS_FOR_SENDFILE)) { + if (result == 1 || (result == 0 && USE_THRDS_FOR_SENDFILE(d))) { d->result_ok = 0; } else if (result == 0 && (d->errInfo.posix_errno == EAGAIN || d->errInfo.posix_errno == EINTR)) { @@ -1950,7 +1952,7 @@ static void invoke_sendfile(void *data) static void free_sendfile(void *data) { struct t_data *d = (struct t_data *)data; - if (USE_THRDS_FOR_SENDFILE) { + if (USE_THRDS_FOR_SENDFILE(d)) { SET_NONBLOCKING(d->c.sendfile.out_fd); } else { MUTEX_LOCK(d->c.sendfile.q_mtx); @@ -4123,8 +4125,16 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { goto done; } - if (hd_len != 0 || tl_len != 0 || flags != 0) { - /* We do not allow header, trailers and/or flags right now */ + if (hd_len != 0 || tl_len != 0) { + /* We do not allow header, trailers */ + reply_posix_error(desc, EINVAL); + goto done; + } + + + if (flags & SENDFILE_FLGS_USE_THREADS && !THRDS_AVAILABLE) { + /* We do not allow use_threads flag on a system where + no threads are available. */ reply_posix_error(desc, EINVAL); goto done; } @@ -4134,6 +4144,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->command = command; d->invoke = invoke_sendfile; d->free = free_sendfile; + d->flags = flags; d->level = 2; d->c.sendfile.out_fd = (int) out_fd; @@ -4153,7 +4164,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->c.sendfile.nbytes = nbytes; - if (USE_THRDS_FOR_SENDFILE) { + if (USE_THRDS_FOR_SENDFILE(d)) { SET_BLOCKING(d->c.sendfile.out_fd); } else { /** diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index d5a1ec766d..56446a2005 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 5999e98340..34679404a2 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -27,7 +27,7 @@ %% Generic file contents operations -export([open/2, close/1, datasync/1, sync/1, advise/4, position/2, truncate/1, write/2, pwrite/2, pwrite/3, read/2, read_line/1, pread/2, pread/3, - copy/3, sendfile/10, allocate/3]). + copy/3, sendfile/8, allocate/3]). %% Specialized file operations -export([open/1, open/3]). @@ -149,6 +149,9 @@ -define(POSIX_FADV_DONTNEED, 4). -define(POSIX_FADV_NOREUSE, 5). +%% Sendfile flags +-define(EFILE_SENDFILE_USE_THREADS, 1). + %%% BIFs @@ -582,13 +585,14 @@ write_file(_, _) -> % {error, enotsup}; sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}}, Dest, Offset, Bytes, _ChunkSize, Headers, Trailers, - _Nodiskio, _MNowait, _Sync) -> + Flags) -> case erlang:port_get_data(Dest) of Data when Data == inet_tcp; Data == inet6_tcp -> ok = inet:lock_socket(Dest,true), {ok, DestFD} = prim_inet:getfd(Dest), + IntFlags = translate_sendfile_flags(Flags), try drv_command(Port, [< + ?EFILE_SENDFILE_USE_THREADS bor translate_sendfile_flags(T); +translate_sendfile_flags([_|T]) -> + translate_sendfile_flags(T); +translate_sendfile_flags([]) -> + 0. + %%%----------------------------------------------------------------- %%% Functions operating on files without handle to the file. ?DRV. -- cgit v1.2.3 From 8f8a03ba3556f2c2c70797bedf5d44b360659425 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 17 Jan 2014 14:37:39 +0100 Subject: erts: fix bug when using passive mode and sendfile The bug incorrectly issued driver_select when un-ignoring an fd for a socket in passive mode, which caused an incorrect error tuple to be returned when the remote end closed the connection. --- erts/emulator/drivers/common/inet_drv.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 45fac69303..fedab62866 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -846,9 +846,10 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_IFNAMSIZ 16 /* INET Ignore states */ -#define INET_IGNORE_NONE 0 -#define INET_IGNORE_READ 1 -#define INET_IGNORE_WRITE 1 << 1 +#define INET_IGNORE_NONE 0 +#define INET_IGNORE_READ (1 << 0) +#define INET_IGNORE_WRITE (1 << 1) +#define INET_IGNORE_PASSIVE (1 << 2) /* Max length of Erlang Term Buffer (for outputting structured terms): */ #ifdef HAVE_SCTP @@ -8208,11 +8209,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (*buf == 1 && !desc->is_ignored) { sock_select(desc, (FD_READ|FD_WRITE|FD_CLOSE|ERL_DRV_USE_NO_CALLBACK), 0); - desc->is_ignored = INET_IGNORE_READ; + if (desc->active) + desc->is_ignored = INET_IGNORE_READ; + else + desc->is_ignored = INET_IGNORE_PASSIVE; } else if (*buf == 0 && desc->is_ignored) { - int flags = (FD_READ|FD_CLOSE|((desc->is_ignored & INET_IGNORE_WRITE)?FD_WRITE:0)); + int flags = FD_CLOSE; + if (desc->is_ignored & INET_IGNORE_READ) + flags |= FD_READ; + if (desc->is_ignored & INET_IGNORE_WRITE) + flags |= FD_WRITE; desc->is_ignored = INET_IGNORE_NONE; - sock_select(desc, flags, 1); + if (flags != FD_CLOSE) + sock_select(desc, flags, 1); } else return ctl_error(EINVAL, rbuf, rsize); @@ -8889,6 +8898,8 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, driver_set_timer(desc->inet.port, timeout); if (!INETP(desc)->is_ignored) sock_select(INETP(desc),(FD_READ|FD_CLOSE),1); + else + INETP(desc)->is_ignored |= INET_IGNORE_READ; } } return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); -- cgit v1.2.3 From 302954c0641ba679fb33a003c0a665b7b4a79a0e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 27 Jan 2014 16:14:10 +0100 Subject: 17.0 anchor and broken links fixes --- erts/doc/src/notes.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 8c008c493e..b4ebef72f4 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -257,7 +257,7 @@ processes before the BIF returns, or fail with an exception due to the port not being open.

The synchronous port BIFs are:

port_close/1 + marker="erlang#port_close/1">port_close/1
port_command/2 Date: Mon, 27 Jan 2014 16:28:21 +0100 Subject: Fix usage of non-empty run-queue flag --- erts/emulator/beam/erl_port_task.c | 5 +++++ erts/emulator/beam/erl_process.c | 19 +++++++------------ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 547a42beb2..d4108067d0 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -877,6 +877,11 @@ enqueue_port(ErtsRunQueue *runq, Port *pp) ASSERT(runq->ports.start && runq->ports.end); erts_smp_inc_runq_len(runq, &runq->ports.info, ERTS_PORT_PRIO_LEVEL); + +#ifdef ERTS_SMP + if (runq->halt_in_progress) + erts_non_empty_runq(runq); +#endif } static ERTS_INLINE Port * diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 6143c5fa3a..f88d44ca03 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -7603,19 +7603,13 @@ Process *schedule(Process *p, int calls) #ifdef ERTS_SMP ErtsMigrationPaths *mps; ErtsMigrationPath *mp; - -#ifdef ERTS_SMP - { - ErtsProcList *pnd_xtrs = rq->procs.pending_exiters; - if (erts_proclist_fetch(&pnd_xtrs, NULL)) { - rq->procs.pending_exiters = NULL; - erts_smp_runq_unlock(rq); - handle_pending_exiters(pnd_xtrs); - erts_smp_runq_lock(rq); - } - + ErtsProcList *pnd_xtrs = rq->procs.pending_exiters; + if (erts_proclist_fetch(&pnd_xtrs, NULL)) { + rq->procs.pending_exiters = NULL; + erts_smp_runq_unlock(rq); + handle_pending_exiters(pnd_xtrs); + erts_smp_runq_lock(rq); } -#endif if (rq->check_balance_reds <= 0) check_balance(rq); @@ -7702,6 +7696,7 @@ Process *schedule(Process *p, int calls) flags = ERTS_RUNQ_FLGS_GET_NOB(rq); if (flags & ERTS_RUNQ_FLG_SUSPENDED) { non_empty_runq(rq); + flags |= ERTS_RUNQ_FLG_NONEMPTY; goto continue_check_activities_to_run_known_flags; } } -- cgit v1.2.3 From c1c03ae4ee50e58b7669ea88ec4d29c6b2b67c7b Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Thu, 9 Jan 2014 21:22:45 -0500 Subject: initial support for dirty schedulers and dirty NIFs Add initial support for dirty schedulers. There are two types of dirty schedulers: CPU schedulers and I/O schedulers. By default, there are as many dirty CPU schedulers as there are normal schedulers and as many dirty CPU schedulers online as normal schedulers online. There are 10 dirty I/O schedulers (similar to the choice of 10 as the default for async threads). By default, dirty schedulers are disabled and conditionally compiled out. To enable them, you must pass --enable-dirty-schedulers to the top-level configure script when building Erlang/OTP. Current dirty scheduler support requires the emulator to be built with SMP support. This restriction will be lifted in the future. You can specify the number of dirty schedulers with the command-line options +SDcpu (for dirty CPU schedulers) and +SDio (for dirty I/O schedulers). The +SDcpu option is similar to the +S option in that it takes two numbers separated by a colon: C1:C2, where C1 specifies the number of dirty schedulers available and C2 specifies the number of dirty schedulers online. The +SDPcpu option allows numbers of dirty CPU schedulers available and dirty CPU schedulers online to be specified as percentages, similar to the existing +SP option for normal schedulers. The number of dirty CPU schedulers created and dirty CPU schedulers online may not exceed the number of normal schedulers created and normal schedulers online, respectively. The +SDio option takes only a single number specifying the number of dirty I/O schedulers available and online. There is no support yet for programmatically changing at run time the number of dirty CPU schedulers online via erlang:system_flag/2. Also, changing the number of normal schedulers online via erlang:system_flag(schedulers_online, NewSchedulersOnline) should ensure that there are no more dirty CPU schedulers than normal schedulers, but this is not yet implemented. You can retrieve the number of dirty schedulers by passing dirty_cpu_schedulers, dirty_cpu_schedulers_online, or dirty_io_schedulers to erlang:system_info/1. Currently only NIFs are able to access dirty scheduler functionality. Neither drivers nor BIFs currently support dirty schedulers. This restriction will be addressed in the future. If dirty scheduler support is present in the runtime, the initial status line Erlang prints before presenting its interactive prompt will include the indicator "[ds:C1:C2:I]" where "ds" indicates "dirty schedulers", "C1" indicates the number of dirty CPU schedulers available, "C2" indicates the number of dirty CPU schedulers online, and "I" indicates the number of dirty I/O schedulers. Document The dirty NIF API in the erl_nif man page. The API closely follows Rickard Green's presentation slides from his talk "Future Extensions to the Native Interface", presented at the 2011 Erlang Factory held in the San Francisco Bay Area. Rickard's slides are available online at http://bit.ly/1m34UHB . Document the new erl command-line options, the additions to erlang:system_info/1, and also add the erlang:system_flag/2 dirty scheduler documentation even though it's not yet implemented. To determine whether the dirty NIF API is available, native code can check to see whether the C preprocessor macro ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined. To check if dirty schedulers are available at run time, native code can call the boolean enif_have_dirty_schedulers() function, and Erlang code can call erlang:system_info(dirty_cpu_schedulers), which raises badarg if no dirty scheduler support is available. Add a simple dirty NIF test to the emulator NIF suite. --- erts/configure.in | 29 +- erts/doc/src/erl.xml | 48 ++ erts/doc/src/erl_nif.xml | 122 +++++- erts/doc/src/erlang.xml | 100 ++++- erts/emulator/beam/beam_emu.c | 9 + erts/emulator/beam/beam_load.h | 1 + erts/emulator/beam/erl_alloc.c | 3 + erts/emulator/beam/erl_bif_info.c | 35 +- erts/emulator/beam/erl_init.c | 304 ++++++++++--- erts/emulator/beam/erl_lock_check.c | 3 + erts/emulator/beam/erl_nif.c | 151 +++++++ erts/emulator/beam/erl_nif.h | 17 + erts/emulator/beam/erl_nif_api_funcs.h | 14 + erts/emulator/beam/erl_process.c | 607 +++++++++++++++++++++----- erts/emulator/beam/erl_process.h | 103 ++++- erts/emulator/test/nif_SUITE.erl | 19 +- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 47 +- erts/etc/common/erlexec.c | 12 +- erts/include/erl_native_features_config.h.in | 21 + erts/lib_src/Makefile.in | 1 + erts/preloaded/ebin/erlang.beam | Bin 97916 -> 98008 bytes erts/preloaded/src/erlang.erl | 7 + 22 files changed, 1451 insertions(+), 202 deletions(-) create mode 100644 erts/include/erl_native_features_config.h.in (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index c992fb5bd9..26bc6d1240 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -101,7 +101,7 @@ ERL_XCOMP_SYSROOT_INIT AC_ISC_POSIX -AC_CONFIG_HEADER($host/config.h:config.h.in include/internal/$host/ethread_header_config.h:include/internal/ethread_header_config.h.in include/$host/erl_int_sizes_config.h:include/erl_int_sizes_config.h.in) +AC_CONFIG_HEADER($host/config.h:config.h.in include/internal/$host/ethread_header_config.h:include/internal/ethread_header_config.h.in include/$host/erl_int_sizes_config.h:include/erl_int_sizes_config.h.in include/$host/erl_native_features_config.h:include/erl_native_features_config.h.in) dnl ---------------------------------------------------------------------- dnl Optional features. dnl ---------------------------------------------------------------------- @@ -123,6 +123,7 @@ AS_HELP_STRING([--enable-bootstrap-only], with_ssl_zlib=no enable_hipe=no enable_sctp=no + enable_dirty_schedulers=no fi ]) @@ -134,6 +135,13 @@ AS_HELP_STRING([--disable-threads], [disable async thread support]), *) enable_threads=yes ;; esac ], enable_threads=unknown) +AC_ARG_ENABLE(dirty-schedulers, +AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]), +[ case "$enableval" in + no) enable_dirty_schedulers=no ;; + *) enable_dirty_schedulers=yes ;; + esac ], enable_dirty_schedulers=no) + AC_ARG_ENABLE(halfword-emulator, AS_HELP_STRING([--enable-halfword-emulator], [enable halfword emulator (only for 64bit builds)]), @@ -1222,6 +1230,13 @@ esac if test $emu_threads != yes; then enable_lock_check=no enable_lock_count=no + AC_MSG_CHECKING(whether dirty schedulers should be enabled) + if test "x$enable_dirty_schedulers" != "xno"; then + AC_DEFINE(ERL_NIF_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi else # Threads enabled for emulator EMU_THR_LIB_NAME=$ETHR_LIB_NAME @@ -1240,7 +1255,17 @@ else if test "x$enable_lock_count" != "xno"; then EMU_THR_DEFS="$EMU_THR_DEFS -DERTS_ENABLE_LOCK_COUNT" fi - + + AC_MSG_CHECKING(whether dirty schedulers should be enabled) + if test "x$enable_dirty_schedulers" != "xno"; then + EMU_THR_DEFS="$EMU_THR_DEFS -DERTS_DIRTY_SCHEDULERS" + AC_DEFINE(ERTS_DIRTY_SCHEDULERS, 1, [Define if the emulator supports dirty schedulers]) + AC_DEFINE(ERL_NIF_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi + disable_child_waiter_thread=no case $host_os in solaris*) diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index e737727941..27a23174d5 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -792,6 +792,54 @@ SMP support enabled (see the -smp flag).

+ + +

Sets the number of dirty CPU scheduler threads to create and dirty + CPU scheduler threads to set online when threading support has been + enabled. The maximum for both values is 1024, and each value is further + limited by the settings for normal schedulers: the number of dirty CPU + scheduler threads created cannot exceed the number of normal scheduler + threads created, and the number of dirty CPU scheduler threads online + cannot exceed the number of normal scheduler threads online (see the + +S and +SP + flags for more details). By default, the number of dirty CPU scheduler + threads created equals the number of normal scheduler threads created, + and the number of dirty CPU scheduler threads online equals the number + of normal scheduler threads online. DirtyCPUSchedulers may be + omitted if :DirtyCPUSchedulersOnline is not and vice versa. The + number of dirty CPU schedulers online can be changed at run time via + erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline). +

+

This option is ignored if the emulator doesn't have threading support + enabled. Currently, this option is experimental and is supported only + if the emulator was configured and built with support for dirty schedulers + enabled (it's disabled by default). +

+
+ + +

Similar to +SDcpu but uses percentages to set the + number of dirty CPU scheduler threads to create and number of dirty CPU scheduler threads + to set online when threading support has been enabled. Specified values must be greater + than 0. For example, +SDPcpu 50:25 sets the number of dirty CPU scheduler threads + to 50% of the logical processors configured and the number of dirty CPU scheduler threads + online to 25% of the logical processors available. DirtyCPUSchedulersPercentage may + be omitted if :DirtyCPUSchedulersOnlinePercentage is not and vice versa. The + number of dirty CPU schedulers online can be changed at run time via + erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline). +

+

This option interacts with +SDcpu settings. + For example, on a system with 8 logical cores configured and 8 logical cores available, + the combination of the options +SDcpu 4:4 +SDPcpu 50:25 (in either order) results + in 2 dirty CPU scheduler threads (50% of 4) and 1 dirty CPU scheduler thread online (25% of 4). +

+

This option is ignored if the emulator doesn't have threading support + enabled. Currently, this option is experimental and is supported only + if the emulator was configured and built with support for dirty schedulers + enabled (it's disabled by default). +

+
+

Scheduling specific flags.

diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 7ac8181d47..8b19725c02 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -181,7 +181,11 @@ ok to dispatch the work to another thread, return from the native function, and wait for the result. The thread can send the result back to the calling thread using message passing. Information - about thread primitives can be found below.

+ about thread primitives can be found below. If you have built your system + with the currently experimental support for dirty schedulers, + you may want to try out this functionality by dispatching the work to a + dirty NIF, + which does not have the same duration restriction as a normal NIF.

FUNCTIONALITY @@ -312,6 +316,38 @@ ok

The library initialization callbacks load, reload and upgrade are all thread-safe even for shared state data.

+ Dirty NIFs +

Note that the dirty NIF functionality + is experimental and that you have to enable support for dirty + schedulers when building OTP in order to try the functionality out. Native functions + + must normally run quickly, as explained earlier in this document. They + generally should execute for no more than a millisecond. But not all native functions + can execute so quickly; for example, functions that encrypt large blocks of data or + perform lengthy file system operations can often run for tens of seconds or more.

+

A NIF that cannot execute in a millisecond or less is called a "dirty NIF" since + it performs work that the Erlang runtime cannot handle cleanly. Applications + that make use of such functions must indicate to the runtime that the functions are + dirty so they can be handled specially. To schedule a dirty NIF for execution, the + application calls enif_schedule_dirty_nif, + passing to it a pointer to the dirty NIF to be executed and indicating with a flag + argument whether it expects the operation to be CPU-bound or I/O-bound.

+

All dirty NIFs must ultimately invoke the + enif_schedule_dirty_nif_finalizer as their final action, passing to it the + result they wish to return to the original caller. A finalizer function can either + receive the result and return it directly, or it can return a different value instead. + For convenience, the NIF API provides the + enif_dirty_nif_finalizer function that applications can use as a finalizer; + it simply returns its result argument.

+

Dirty NIF support is available only when the emulator is configured with dirty + schedulers enabled. This feature is currently disabled by default. To determine whether + the dirty NIF API is available, native code can check to see if the C preprocessor macro + ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined. Also, if the Erlang runtime was built + without threading support, dirty schedulers are disabled. To check at runtime for the presence + of dirty scheduler threads, code can call the + enif_have_dirty_schedulers() API function, which returns true if dirty + scheduler threads are present, false otherwise.

+
@@ -610,6 +646,18 @@ typedef enum { See also the warning text at the beginning of this document.

+ ERL_NIF_TERMenif_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result) + Simple dirty NIF result finalizer + +

A convenience function that a dirty NIF can use as a finalizer that simply + return its result argument as its return value. This function is provided + for dirty NIFs with results that should be returned directly to the original caller.

+

This function is available only when the emulator is configured with dirty + schedulers enabled. This feature is currently disabled by default. To determine whether + the dirty NIF API is available, native code can check to see if the C preprocessor macro + ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined.

+
+
intenif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)

Same as erl_drv_equal_tids. @@ -730,6 +778,22 @@ typedef enum { and return true, or return false if term is not an unsigned integer or is outside the bounds of type unsigned long.

+ intenif_have_dirty_schedulers() + Runtime check for the presence of dirty scheduler threads + +

Check at runtime for the presence of dirty scheduler threads. If the emulator is + built with threading support, dirty scheduler threads are available and + enif_have_dirty_schedulers() returns true. If the emulator was built without + threading support, enif_have_dirty_schedulers() returns false.

+

If dirty scheduler threads are not available in the emulator, calls to + enif_schedule_dirty_nif and enif_schedule_dirty_nif_finalizer result in + the NIF and finalizer functions being called directly within the calling thread.

+

This function is available only when the emulator is configured with dirty + schedulers enabled. This feature is currently disabled by default. To determine whether + the dirty NIF API is available, native code can check to see if the C preprocessor macro + ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined.

+
+
intenif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin) Inspect the content of a binary

Initialize the structure pointed to by bin with @@ -777,6 +841,20 @@ typedef enum { Erlang operators =:= and =/=.

+ intenif_is_on_dirty_scheduler(ErlNifEnv* env) + Check to see if executing on a dirty scheduler thread + +

Check to see if the current NIF is executing on a dirty scheduler thread. If the + emulator is built with threading support, calling enif_is_on_dirty_scheduler + from within a dirty NIF returns true. It returns false when the calling NIF is a regular + NIF or a NIF finalizer, both of which run on normal scheduler threads, or when the emulator + is built without threading support.

+

This function is available only when the emulator is configured with dirty + schedulers enabled. This feature is currently disabled by default. To determine whether + the dirty NIF API is available, native code can check to see if the C preprocessor macro + ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined.

+
+
intenif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term) Determine if a term is a pid

Return true if term is a pid.

@@ -1141,6 +1219,48 @@ typedef enum {

Same as erl_drv_rwlock_tryrwlock.

+ ERL_NIF_TERMenif_schedule_dirty_nif(ErlNifEnv* env, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM argv[]) + Schedule a dirty NIF for execution + +

Schedule dirty NIF fp to execute a long-running operation. The flags + argument must be set to either ERL_NIF_DIRTY_JOB_CPU_BOUND if the job is expected to + be primarily CPU-bound, or ERL_NIF_DIRTY_JOB_IO_BOUND for jobs that will be + I/O-bound. The argc and argv arguments can either be the originals passed + into the calling NIF, or they can be values created by the calling NIF. The calling + NIF must use the return value of enif_schedule_dirty_nif as its own return value.

+

Be aware that enif_schedule_dirty_nif, as its name implies, only schedules the + dirty NIF for future execution. The calling NIF does not block waiting for the dirty NIF to + execute and return, which means that the calling NIF can't expect to receive the dirty NIF + return value and use it for further operations.

+

A dirty NIF may not invoke the enif_make_badarg + to raise an exception. If it wishes to return an exception, the dirty NIF should pass a + regular result indicating the exception details to its finalizer, and allow the finalizer + to raise the exception on its behalf.

+

This function is available only when the emulator is configured with dirty schedulers + enabled. This feature is currently disabled by default. To determine whether the dirty NIF API + is available, native code can check to see if the C preprocessor macro + ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined.

+
+
+ ERL_NIF_TERMenif_schedule_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result, ERL_NIF_TERM (*fp)(ErlNifEnv* env, ERL_NIF_TERM result)) + Schedule a dirty NIF finalizer + +

When a dirty NIF finishes executing, it must schedule a finalizer function to return + its result to the original NIF caller. The dirty NIF passes result as the value it + wants the finalizer to use as the return value. The fp argument is a pointer to the + finalizer function. The NIF API provides the + enif_dirty_nif_finalizer function that can be used as a finalizer that simply + returns its result argument. You are also free to write your own custom finalizer + that uses result to derive a different return value, or ignores result + entirely and returns a completely different value.

+

Without exception, all dirty NIFs must invoke enif_schedule_dirty_nif_finalizer + to complete their execution.

+

This function is available only when the emulator is configured with dirty + schedulers enabled. This feature is currently disabled by default. To determine whether + the dirty NIF API is available, native code can check to see if the C preprocessor macro + ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined.

+
+
ErlNifPid *enif_self(ErlNifEnv* caller_env, ErlNifPid* pid) Get the pid of the calling process.

Initialize the pid variable *pid to represent the diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index ea753cfaaf..4cf5631727 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5194,6 +5194,27 @@ ok + Set system flag dirty CPU schedulers online + +

+ Sets the amount of dirty CPU schedulers online. Valid range is + where N is the + lesser of the return values of erlang:system_info(dirty_cpu_schedulers) and + erlang:system_info(schedulers_online). +

+

Returns the old value of the flag.

+

Note that the dirty schedulers functionality is experimental, and + that you have to enable support for dirty schedulers when building OTP in + order to try the functionality out.

+

For more information see + erlang:system_info(dirty_cpu_schedulers) + and + erlang:system_info(dirty_cpu_schedulers_online). +

+
+
+ + Set system flag fullsweep_after

Number is a non-negative integer which indicates @@ -5211,7 +5232,7 @@ ok - + Set system flag min_heap_size

Sets the default minimum heap size for processes. The @@ -5226,7 +5247,7 @@ ok - + Set system flag min_bin_vheap_size

Sets the default minimum binary virtual heap size for processes. The @@ -5241,7 +5262,7 @@ ok - + Set system flag multi_scheduling

@@ -5279,7 +5300,7 @@ ok - + Set system flag scheduler_bind_type @@ -5399,7 +5420,7 @@ ok - + Set system flag scheduler_wall_time

Turns on/off scheduler wall time measurements.

@@ -5409,7 +5430,7 @@ ok
- + Set system flag schedulers_online

@@ -5425,7 +5446,7 @@ ok - + Set system flag trace_control_word

Sets the value of the node's trace control word to @@ -5785,6 +5806,71 @@ ok compiled; otherwise, false.

+ dirty_cpu_schedulers + +

Returns the number of dirty CPU scheduler threads used by + the emulator. Dirty CPU schedulers execute CPU-bound + native functions such as NIFs, linked-in driver code, and BIFs + that cannot be managed cleanly by the emulator's normal schedulers. +

+

The number of dirty CPU scheduler threads is determined at emulator + boot time and cannot be changed after that. The number of dirty CPU + scheduler threads online can however be changed at any time. The number of + dirty CPU schedulers can be set on startup by passing + the +SDcpu command line flag, see + erl(1). +

+

Note that the dirty schedulers functionality is experimental, and + that you have to enable support for dirty schedulers when building OTP in + order to try the functionality out.

+

See also erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline), + erlang:system_info(dirty_cpu_schedulers_online), + erlang:system_info(dirty_io_schedulers), + erlang:system_info(schedulers), + erlang:system_info(schedulers_online), and + erlang:system_flag(schedulers_online, SchedulersOnline).

+
+ dirty_cpu_schedulers_online + +

Returns the number of dirty CPU schedulers online. The return value + satisfies the following relationship: + , where N is + the lesser of the return values of erlang:system_info(dirty_cpu_schedulers) and + erlang:system_info(schedulers_online). +

+

The number of dirty CPU schedulers online can be set on startup by passing + the +SDcpu command line flag, see + erl(1). +

+

Note that the dirty schedulers functionality is experimental, and + that you have to enable support for dirty schedulers when building OTP in + order to try the functionality out.

+

For more information, see + erlang:system_info(dirty_cpu_schedulers), + erlang:system_info(dirty_io_schedulers), + erlang:system_info(schedulers_online), and + erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline). +

+
+ dirty_io_schedulers + +

Returns the number of dirty I/O schedulers as an integer. Dirty I/O schedulers + execute I/O-bound native functions such as NIFs and linked-in driver code that + cannot be managed cleanly by the emulator's normal schedulers. +

+

This value can be set on startup by passing + the +SDio command line flag, see + erl(1). +

+

Note that the dirty schedulers functionality is experimental, and + that you have to enable support for dirty schedulers when building OTP in + order to try the functionality out.

+

For more information, see + erlang:system_info(dirty_cpu_schedulers), + erlang:system_info(dirty_cpu_schedulers_online), and + erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline). +

+
dist

Returns a binary containing a string of distribution diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index b413f0e859..7fecdd5c5f 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -217,6 +217,7 @@ BeamInstr beam_continue_exit[1]; BeamInstr* em_call_error_handler; BeamInstr* em_apply_bif; +BeamInstr* em_call_nif; /* NOTE These should be the only variables containing trace instructions. @@ -3323,6 +3324,13 @@ void process_main(void) reg[0] = r(0); nif_bif_result = (*fp)(&env, bif_nif_arity, reg); erts_post_nif(&env); +#ifdef ERTS_DIRTY_SCHEDULERS + if (is_non_value(nif_bif_result) && c_p->freason == TRAP) { + Export* ep = (Export*) c_p->psd->data[ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT]; + ep->code[0] = I[-3]; + ep->code[1] = I[-2]; + } +#endif } ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result)); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -4964,6 +4972,7 @@ void process_main(void) em_call_error_handler = OpCode(call_error_handler); em_apply_bif = OpCode(apply_bif); + em_call_nif = OpCode(call_nif); beam_apply[0] = (BeamInstr) OpCode(i_apply); beam_apply[1] = (BeamInstr) OpCode(normal_exit); diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index 65a8f26d7c..bd22b0c4de 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -49,6 +49,7 @@ extern void** beam_ops; extern BeamInstr beam_debug_apply[]; extern BeamInstr* em_call_error_handler; extern BeamInstr* em_apply_bif; +extern BeamInstr* em_call_nif; /* * The following variables keep a sorted list of address ranges for diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 8094c6ee2e..c6b324dc15 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1754,6 +1754,9 @@ erts_alloc_register_scheduler(void *vesdp) int ix = (int) esdp->no; int aix; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); +#endif for (aix = ERTS_ALC_A_MIN; aix <= ERTS_ALC_A_MAX; aix++) { ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[aix]; esdp->alloc_data.deallctr[aix] = NULL; diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index e0b654cb22..f25b4dbae5 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -90,6 +90,9 @@ static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE " [smp:%beu:%beu]" #endif #ifdef USE_THREADS +#ifdef ERTS_DIRTY_SCHEDULERS + " [ds:%beu:%beu:%beu]" +#endif " [async-threads:%d]" #endif #ifdef HIPE @@ -312,7 +315,13 @@ erts_print_system_version(int to, void *arg, Process *c_p) char *ocp = otp_correction_package; #ifdef ERTS_SMP Uint total, online, active; - (void) erts_schedulers_state(&total, &online, &active, 0); +#ifdef ERTS_DIRTY_SCHEDULERS + Uint dirty_cpu, dirty_cpu_onln, dirty_io; + + (void) erts_schedulers_state(&total, &online, &active, &dirty_cpu, &dirty_cpu_onln, &dirty_io, 0); +#else + (void) erts_schedulers_state(&total, &online, &active, NULL, NULL, NULL, 0); +#endif #endif for (i = 0; i < sizeof(otp_correction_package)-4; i++) { if (ocp[i] == '-' && ocp[i+1] == 'r' && ocp[i+2] == 'c') @@ -330,6 +339,9 @@ erts_print_system_version(int to, void *arg, Process *c_p) rc_str #ifdef ERTS_SMP , total, online +#ifdef ERTS_DIRTY_SCHEDULERS + , dirty_cpu, dirty_cpu_onln, dirty_io +#endif #endif #ifdef USE_THREADS , erts_async_max_threads @@ -2477,6 +2489,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) switch (erts_schedulers_state(&total, &online, &active, + NULL, + NULL, + NULL, 1)) { case ERTS_SCHDLR_SSPND_DONE: { Eterm *hp = HAlloc(BIF_P, 4); @@ -2500,7 +2515,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(make_small(1)); #else Uint total, online, active; - switch (erts_schedulers_state(&total, &online, &active, 1)) { + switch (erts_schedulers_state(&total, &online, &active, NULL, NULL, NULL, 1)) { case ERTS_SCHDLR_SSPND_DONE: BIF_RET(make_small(online)); case ERTS_SCHDLR_SSPND_YIELD_RESTART: @@ -2517,7 +2532,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(make_small(1)); #else Uint total, online, active; - switch (erts_schedulers_state(&total, &online, &active, 1)) { + switch (erts_schedulers_state(&total, &online, &active, NULL, NULL, NULL, 1)) { case ERTS_SCHDLR_SSPND_DONE: BIF_RET(make_small(active)); case ERTS_SCHDLR_SSPND_YIELD_RESTART: @@ -2528,6 +2543,20 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) ASSERT(0); BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); } +#endif +#if defined(ERTS_SMP) && defined(ERTS_DIRTY_SCHEDULERS) + } else if (ERTS_IS_ATOM_STR("dirty_cpu_schedulers", BIF_ARG_1)) { + Uint dirty_cpu; + erts_schedulers_state(NULL, NULL, NULL, &dirty_cpu, NULL, NULL, 1); + BIF_RET(make_small(dirty_cpu)); + } else if (ERTS_IS_ATOM_STR("dirty_cpu_schedulers_online", BIF_ARG_1)) { + Uint dirty_cpu_onln; + erts_schedulers_state(NULL, NULL, NULL, NULL, &dirty_cpu_onln, NULL, 1); + BIF_RET(make_small(dirty_cpu_onln)); + } else if (ERTS_IS_ATOM_STR("dirty_io_schedulers", BIF_ARG_1)) { + Uint dirty_io; + erts_schedulers_state(NULL, NULL, NULL, NULL, NULL, &dirty_io, 1); + BIF_RET(make_small(dirty_io)); #endif } else if (ERTS_IS_ATOM_STR("run_queues", BIF_ARG_1)) { res = make_small(erts_no_run_queues); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 19088fd913..c17256f466 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -484,98 +484,107 @@ void erts_usage(void) /* erts_fprintf(stderr, "-# number set the number of items to be used in traces etc\n"); */ - erts_fprintf(stderr, "-a size suggested stack size in kilo words for threads\n"); - erts_fprintf(stderr, " in the async-thread pool, valid range is [%d-%d]\n", + erts_fprintf(stderr, "-a size suggested stack size in kilo words for threads\n"); + erts_fprintf(stderr, " in the async-thread pool, valid range is [%d-%d]\n", ERTS_ASYNC_THREAD_MIN_STACK_SIZE, ERTS_ASYNC_THREAD_MAX_STACK_SIZE); - erts_fprintf(stderr, "-A number set number of threads in async thread pool,\n"); - erts_fprintf(stderr, " valid range is [0-%d]\n", + erts_fprintf(stderr, "-A number set number of threads in async thread pool,\n"); + erts_fprintf(stderr, " valid range is [0-%d]\n", ERTS_MAX_NO_OF_ASYNC_THREADS); - erts_fprintf(stderr, "-B[c|d|i] c to have Ctrl-c interrupt the Erlang shell,\n"); - erts_fprintf(stderr, " d (or no extra option) to disable the break\n"); - erts_fprintf(stderr, " handler, i to ignore break signals\n"); + erts_fprintf(stderr, "-B[c|d|i] c to have Ctrl-c interrupt the Erlang shell,\n"); + erts_fprintf(stderr, " d (or no extra option) to disable the break\n"); + erts_fprintf(stderr, " handler, i to ignore break signals\n"); /* erts_fprintf(stderr, "-b func set the boot function (default boot)\n"); */ - erts_fprintf(stderr, "-c disable continuous date/time correction with\n"); - erts_fprintf(stderr, " respect to uptime\n"); + erts_fprintf(stderr, "-c disable continuous date/time correction with\n"); + erts_fprintf(stderr, " respect to uptime\n"); - erts_fprintf(stderr, "-d don't write a crash dump for internally detected errors\n"); - erts_fprintf(stderr, " (halt(String) will still produce a crash dump)\n"); - erts_fprintf(stderr, "-fn[u|a|l] Control how filenames are interpreted\n"); - erts_fprintf(stderr, "-hms size set minimum heap size in words (default %d)\n", + erts_fprintf(stderr, "-d don't write a crash dump for internally detected errors\n"); + erts_fprintf(stderr, " (halt(String) will still produce a crash dump)\n"); + erts_fprintf(stderr, "-fn[u|a|l] Control how filenames are interpreted\n"); + erts_fprintf(stderr, "-hms size set minimum heap size in words (default %d)\n", H_DEFAULT_SIZE); - erts_fprintf(stderr, "-hmbs size set minimum binary virtual heap size in words (default %d)\n", + erts_fprintf(stderr, "-hmbs size set minimum binary virtual heap size in words (default %d)\n", VH_DEFAULT_SIZE); /* erts_fprintf(stderr, "-i module set the boot module (default init)\n"); */ - erts_fprintf(stderr, "-K boolean enable or disable kernel poll\n"); - erts_fprintf(stderr, "-n[s|a|d] Control behavior of signals to ports\n"); - erts_fprintf(stderr, " Note that this flag is deprecated!\n"); - erts_fprintf(stderr, "-M memory allocator switches,\n"); - erts_fprintf(stderr, " see the erts_alloc(3) documentation for more info.\n"); - erts_fprintf(stderr, "-pc Control what characters are considered printable (default latin1)\n"); - erts_fprintf(stderr, "-P number set maximum number of processes on this node,\n"); - erts_fprintf(stderr, " valid range is [%d-%d]\n", + erts_fprintf(stderr, "-K boolean enable or disable kernel poll\n"); + erts_fprintf(stderr, "-n[s|a|d] Control behavior of signals to ports\n"); + erts_fprintf(stderr, " Note that this flag is deprecated!\n"); + erts_fprintf(stderr, "-M memory allocator switches,\n"); + erts_fprintf(stderr, " see the erts_alloc(3) documentation for more info.\n"); + erts_fprintf(stderr, "-pc Control what characters are considered printable (default latin1)\n"); + erts_fprintf(stderr, "-P number set maximum number of processes on this node,\n"); + erts_fprintf(stderr, " valid range is [%d-%d]\n", ERTS_MIN_PROCESSES, ERTS_MAX_PROCESSES); - erts_fprintf(stderr, "-Q number set maximum number of ports on this node,\n"); - erts_fprintf(stderr, " valid range is [%d-%d]\n", + erts_fprintf(stderr, "-Q number set maximum number of ports on this node,\n"); + erts_fprintf(stderr, " valid range is [%d-%d]\n", ERTS_MIN_PORTS, ERTS_MAX_PORTS); - erts_fprintf(stderr, "-R number set compatibility release number,\n"); - erts_fprintf(stderr, " valid range [%d-%d]\n", + erts_fprintf(stderr, "-R number set compatibility release number,\n"); + erts_fprintf(stderr, " valid range [%d-%d]\n", this_rel-2, this_rel); - erts_fprintf(stderr, "-r force ets memory block to be moved on realloc\n"); - erts_fprintf(stderr, "-rg amount set reader groups limit\n"); - erts_fprintf(stderr, "-sbt type set scheduler bind type, valid types are:\n"); - erts_fprintf(stderr, "-stbt type u|ns|ts|ps|s|nnts|nnps|tnnps|db\n"); - erts_fprintf(stderr, "-sbwt val set scheduler busy wait threshold, valid values are:\n"); - erts_fprintf(stderr, " none|very_short|short|medium|long|very_long.\n"); - erts_fprintf(stderr, "-scl bool enable/disable compaction of scheduler load,\n"); - erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); - erts_fprintf(stderr, "-sct cput set cpu topology,\n"); - erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); + erts_fprintf(stderr, "-r force ets memory block to be moved on realloc\n"); + erts_fprintf(stderr, "-rg amount set reader groups limit\n"); + erts_fprintf(stderr, "-sbt type set scheduler bind type, valid types are:\n"); + erts_fprintf(stderr, "-stbt type u|ns|ts|ps|s|nnts|nnps|tnnps|db\n"); + erts_fprintf(stderr, "-sbwt val set scheduler busy wait threshold, valid values are:\n"); + erts_fprintf(stderr, " none|very_short|short|medium|long|very_long.\n"); + erts_fprintf(stderr, "-scl bool enable/disable compaction of scheduler load,\n"); + erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); + erts_fprintf(stderr, "-sct cput set cpu topology,\n"); + erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); #if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT - erts_fprintf(stderr, "-sub bool enable/disable scheduler utilization balancing,\n"); + erts_fprintf(stderr, "-sub bool enable/disable scheduler utilization balancing,\n"); #else - erts_fprintf(stderr, "-sub false disable scheduler utilization balancing,\n"); + erts_fprintf(stderr, "-sub false disable scheduler utilization balancing,\n"); #endif - erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); - erts_fprintf(stderr, "-sws val set scheduler wakeup strategy, valid values are:\n"); - erts_fprintf(stderr, " default|legacy.\n"); - erts_fprintf(stderr, "-swct val set scheduler wake cleanup threshold, valid values are:\n"); - erts_fprintf(stderr, " very_lazy|lazy|medium|eager|very_eager.\n"); - erts_fprintf(stderr, "-swt val set scheduler wakeup threshold, valid values are:\n"); - erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n"); - erts_fprintf(stderr, "-sss size suggested stack size in kilo words for scheduler threads,\n"); - erts_fprintf(stderr, " valid range is [%d-%d]\n", + erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); + erts_fprintf(stderr, "-sws val set scheduler wakeup strategy, valid values are:\n"); + erts_fprintf(stderr, " default|legacy.\n"); + erts_fprintf(stderr, "-swct val set scheduler wake cleanup threshold, valid values are:\n"); + erts_fprintf(stderr, " very_lazy|lazy|medium|eager|very_eager.\n"); + erts_fprintf(stderr, "-swt val set scheduler wakeup threshold, valid values are:\n"); + erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n"); + erts_fprintf(stderr, "-sss size suggested stack size in kilo words for scheduler threads,\n"); + erts_fprintf(stderr, " valid range is [%d-%d]\n", ERTS_SCHED_THREAD_MIN_STACK_SIZE, ERTS_SCHED_THREAD_MAX_STACK_SIZE); - erts_fprintf(stderr, "-spp Bool set port parallelism scheduling hint\n"); - erts_fprintf(stderr, "-S n1:n2 set number of schedulers (n1), and number of\n"); - erts_fprintf(stderr, " schedulers online (n2), maximum for both\n"); - erts_fprintf(stderr, " numbers is %d\n", + erts_fprintf(stderr, "-spp Bool set port parallelism scheduling hint\n"); + erts_fprintf(stderr, "-S n1:n2 set number of schedulers (n1), and number of\n"); + erts_fprintf(stderr, " schedulers online (n2), maximum for both\n"); + erts_fprintf(stderr, " numbers is %d\n", ERTS_MAX_NO_OF_SCHEDULERS); - erts_fprintf(stderr, "-SP p1:p2 specify schedulers (p1) and schedulers online (p2)\n"); - erts_fprintf(stderr, " as percentages of logical processors configured and logical\n"); - erts_fprintf(stderr, " processors available, respectively\n"); - erts_fprintf(stderr, "-t size set the maximum number of atoms the " - "emulator can handle\n"); - erts_fprintf(stderr, " valid range is [%d-%d]\n", + erts_fprintf(stderr, "-SP p1:p2 specify schedulers (p1) and schedulers online (p2)\n"); + erts_fprintf(stderr, " as percentages of logical processors configured and logical\n"); + erts_fprintf(stderr, " processors available, respectively\n"); +#ifdef ERTS_DIRTY_SCHEDULERS + erts_fprintf(stderr, "-SDcpu n1:n2 set number of dirty CPU schedulers (n1), and number of\n"); + erts_fprintf(stderr, " dirty CPU schedulers online (n2), valid range for both\n"); + erts_fprintf(stderr, " numbers is [1-%d], and n2 must be less than or equal to n1\n", + ERTS_MAX_NO_OF_DIRTY_CPU_SCHEDULERS); + erts_fprintf(stderr, "-SDPcpu p1:p2 specify dirty CPU schedulers (p1) and dirty CPU schedulers\n"); + erts_fprintf(stderr, " online (p2) as percentages of logical processors configured\n"); + erts_fprintf(stderr, " and logical processors available, respectively\n"); + erts_fprintf(stderr, "-SDio n set number of dirty I/O schedulers, valid range is [0-%d]\n", + ERTS_MAX_NO_OF_DIRTY_IO_SCHEDULERS); +#endif + erts_fprintf(stderr, "-t size set the maximum number of atoms the emulator can handle\n"); + erts_fprintf(stderr, " valid range is [%d-%d]\n", MIN_ATOM_TABLE_SIZE, MAX_ATOM_TABLE_SIZE); - erts_fprintf(stderr, "-T number set modified timing level,\n"); - erts_fprintf(stderr, " valid range is [0-%d]\n", + erts_fprintf(stderr, "-T number set modified timing level, valid range is [0-%d]\n", ERTS_MODIFIED_TIMING_LEVELS-1); - erts_fprintf(stderr, "-V print Erlang version\n"); + erts_fprintf(stderr, "-V print Erlang version\n"); - erts_fprintf(stderr, "-v turn on chatty mode (GCs will be reported etc)\n"); + erts_fprintf(stderr, "-v turn on chatty mode (GCs will be reported etc)\n"); - erts_fprintf(stderr, "-W set error logger warnings mapping,\n"); - erts_fprintf(stderr, " see error_logger documentation for details\n"); - erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n"); - erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024); + erts_fprintf(stderr, "-W set error logger warnings mapping,\n"); + erts_fprintf(stderr, " see error_logger documentation for details\n"); + erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n"); + erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024); erts_fprintf(stderr, "\n"); erts_fprintf(stderr, "Note that if the emulator is started with erlexec (typically\n"); erts_fprintf(stderr, "from the erl script), these flags should be specified with +.\n"); @@ -643,6 +652,13 @@ early_init(int *argc, char **argv) /* int schdlrs_percentage = 100; int schdlrs_onln_percentage = 100; int max_main_threads; +#ifdef ERTS_DIRTY_SCHEDULERS + int dirty_cpu_scheds; + int dirty_cpu_scheds_online; + int dirty_cpu_scheds_pctg = 100; + int dirty_cpu_scheds_onln_pctg = 100; + int dirty_io_scheds; +#endif int max_reader_groups; int reader_groups; char envbuf[21]; /* enough for any 64-bit integer */ @@ -718,6 +734,12 @@ early_init(int *argc, char **argv) /* schdlrs = no_schedulers; schdlrs_onln = no_schedulers_online; +#ifdef ERTS_DIRTY_SCHEDULERS + dirty_cpu_scheds = no_schedulers; + dirty_cpu_scheds_online = no_schedulers_online; + dirty_io_scheds = 10; +#endif + envbufsz = sizeof(envbuf); /* erts_sys_getenv(_raw)() not initialized yet; need erts_sys_getenv__() */ @@ -808,7 +830,121 @@ early_init(int *argc, char **argv) /* VERBOSE(DEBUG_SYSTEM, ("using %d:%d scheduler percentages\n", schdlrs_percentage, schdlrs_onln_percentage)); - } else { + } +#ifdef ERTS_DIRTY_SCHEDULERS + else if (argv[i][2] == 'D') { + char *arg; + char *type = argv[i]+3; + if (strcmp(type, "Pcpu") == 0) { + int ptot, ponln; + arg = get_arg(argv[i]+7, argv[i+1], &i); + switch (sscanf(arg, "%d:%d", &ptot, &ponln)) { + case 0: + switch (sscanf(arg, ":%d", &ponln)) { + case 1: + if (ponln < 0) + goto bad_SDPcpu; + ptot = 100; + goto chk_SDPcpu; + default: + goto bad_SDPcpu; + } + case 1: + if (ptot < 0) + goto bad_SDPcpu; + ponln = ptot < 100 ? ptot : 100; + goto chk_SDPcpu; + case 2: + if (ptot < 0 || ponln < 0) + goto bad_SDPcpu; + chk_SDPcpu: + dirty_cpu_scheds_pctg = ptot; + dirty_cpu_scheds_onln_pctg = ponln; + break; + default: + bad_SDPcpu: + erts_fprintf(stderr, + "bad dirty CPU schedulers percentage specifier %s\n", + arg); + erts_usage(); + break; + } + VERBOSE(DEBUG_SYSTEM, + ("using %d:%d dirty CPU scheduler percentages\n", + dirty_cpu_scheds_pctg, dirty_cpu_scheds_onln_pctg)); + } else if (strcmp(type, "cpu") == 0) { + int tot, onln; + arg = get_arg(argv[i]+6, argv[i+1], &i); + switch (sscanf(arg, "%d:%d", &tot, &onln)) { + case 0: + switch (sscanf(arg, ":%d", &onln)) { + case 1: + tot = no_schedulers; + goto chk_SDcpu; + default: + goto bad_SDcpu; + } + case 1: + onln = tot < dirty_cpu_scheds_online ? + tot : dirty_cpu_scheds_online; + case 2: + chk_SDcpu: + if (tot > 0) + dirty_cpu_scheds = tot; + else + dirty_cpu_scheds = no_schedulers + tot; + if (onln > 0) + dirty_cpu_scheds_online = onln; + else + dirty_cpu_scheds_online = no_schedulers_online + onln; + if (dirty_cpu_scheds < 1 || + ERTS_MAX_NO_OF_DIRTY_CPU_SCHEDULERS < dirty_cpu_scheds) { + erts_fprintf(stderr, + "bad amount of dirty CPU schedulers %d\n", + tot); + erts_usage(); + } + if (dirty_cpu_scheds_online < 1 || + dirty_cpu_scheds < dirty_cpu_scheds_online) { + erts_fprintf(stderr, + "bad amount of dirty CPU schedulers online %d " + "(total amount of dirty CPU schedulers %d)\n", + dirty_cpu_scheds_online, dirty_cpu_scheds); + erts_usage(); + } + break; + default: + bad_SDcpu: + erts_fprintf(stderr, + "bad amount of dirty CPU schedulers %s\n", + arg); + erts_usage(); + break; + } + VERBOSE(DEBUG_SYSTEM, + ("using %d:%d dirty CPU scheduler(s)\n", tot, onln)); + } else if (strcmp(type, "io") == 0) { + arg = get_arg(argv[i]+5, argv[i+1], &i); + dirty_io_scheds = atoi(arg); + if (dirty_io_scheds < 0 || + dirty_io_scheds > ERTS_MAX_NO_OF_DIRTY_IO_SCHEDULERS) { + erts_fprintf(stderr, + "bad number of dirty I/O schedulers %s\n", + arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, + ("using %d dirty I/O scheduler(s)\n", dirty_io_scheds)); + } else { + erts_fprintf(stderr, + "bad or missing dirty scheduler specifier: %s\n", + argv[i]); + erts_usage(); + break; + } + } +#endif + else { int tot, onln; char *arg = get_arg(argv[i]+2, argv[i+1], &i); switch (sscanf(arg, "%d:%d", &tot, &onln)) { @@ -894,6 +1030,17 @@ early_init(int *argc, char **argv) /* /* Silence gcc warnings */ (void)schdlrs_percentage; (void)schdlrs_onln_percentage; +#endif +#ifdef ERTS_DIRTY_SCHEDULERS + /* apply any dirty scheduler precentages */ + if (dirty_cpu_scheds_pctg != 100 || dirty_cpu_scheds_onln_pctg != 100) { + dirty_cpu_scheds = dirty_cpu_scheds * dirty_cpu_scheds_pctg / 100; + dirty_cpu_scheds_online = dirty_cpu_scheds_online * dirty_cpu_scheds_onln_pctg / 100; + } + if (dirty_cpu_scheds > schdlrs) + dirty_cpu_scheds = schdlrs; + if (dirty_cpu_scheds_online > schdlrs_onln) + dirty_cpu_scheds_online = schdlrs_onln; #endif } @@ -906,6 +1053,11 @@ early_init(int *argc, char **argv) /* no_schedulers_online = schdlrs_onln; erts_no_schedulers = (Uint) no_schedulers; +#endif +#ifdef ERTS_DIRTY_SCHEDULERS + erts_no_dirty_cpu_schedulers = dirty_cpu_scheds; + erts_no_dirty_cpu_schedulers_online = dirty_cpu_scheds_online; + erts_no_dirty_io_schedulers = dirty_io_scheds; #endif erts_early_init_scheduling(no_schedulers); @@ -924,10 +1076,18 @@ early_init(int *argc, char **argv) /* * * * Unmanaged threads that need to register: * ** Async threads (see erl_async.c) + * ** Dirty scheduler threads */ erts_thr_progress_init(no_schedulers, no_schedulers+2, - erts_async_max_threads); +#ifndef ERTS_DIRTY_SCHEDULERS + erts_async_max_threads +#else + erts_async_max_threads + + erts_no_dirty_cpu_schedulers + + erts_no_dirty_io_schedulers +#endif + ); #endif erts_thr_q_init(); erts_init_utils(); @@ -1392,7 +1552,15 @@ erl_start(int argc, char **argv) break; case 'S' : /* Was handled in early_init() just read past it */ - if (argv[i][2] == 'P') + if (argv[i][2] == 'D') { + char* type = argv[i]+3; + if (strcmp(type, "Pcpu") == 0) + (void) get_arg(argv[i]+7, argv[i+1], &i); + if (strcmp(type, "cpu") == 0) + (void) get_arg(argv[i]+6, argv[i+1], &i); + else if (strcmp(type, "io") == 0) + (void) get_arg(argv[i]+5, argv[i+1], &i); + } else if (argv[i][2] == 'P') (void) get_arg(argv[i]+3, argv[i+1], &i); else (void) get_arg(argv[i]+2, argv[i+1], &i); diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 0dd83fa6ed..a8ff94ac89 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -123,6 +123,9 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "schdlr_sspnd", NULL }, { "migration_info_update", NULL }, { "run_queue", "address" }, +#ifdef ERTS_DIRTY_SCHEDULERS + { "dirty_run_queue_sleep_list", "address" }, +#endif { "process_table", NULL }, { "cpu_info", NULL }, { "pollset", "address" }, diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index dc285b3cf7..e1e213c4eb 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -34,6 +34,7 @@ #include "beam_bp.h" #include "erl_thr_progress.h" #include "dtrace-wrapper.h" +#include "erl_process.h" #if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP)) #define HAVE_USE_DTRACE 1 #endif @@ -1451,6 +1452,156 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent) return ERTS_BIF_REDS_LEFT(env->proc) == 0; } +#ifdef ERTS_DIRTY_SCHEDULERS + +static void +alloc_proc_psd(Process* proc, Export **ep) +{ + int i; + if (!*ep) { + *ep = erts_alloc(ERTS_ALC_T_PSD, sizeof(Export)); + sys_memset((void*) *ep, 0, sizeof(Export)); + for (i=0; iaddressv[i] = &(*ep)->code[3]; + } + (*ep)->code[3] = (BeamInstr) em_call_nif; + } + (void) ERTS_PROC_SET_DIRTY_SCHED_TRAP_EXPORT(proc, ERTS_PROC_LOCK_MAIN, *ep); +} + +static ERL_NIF_TERM +execute_dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + Eterm* reg = ERTS_PROC_GET_SCHDATA(env->proc)->x_reg_array; + ERL_NIF_TERM result = (ERL_NIF_TERM) reg[0]; + typedef ERL_NIF_TERM (*FinalizerFP)(ErlNifEnv*, ERL_NIF_TERM); + FinalizerFP fp; +#if HAVE_INT64 && SIZEOF_LONG != 8 + ASSERT(sizeof(fp) <= sizeof(ErlNifUInt64)); + enif_get_uint64(env, reg[1], (ErlNifUInt64 *) &fp); +#else + ASSERT(sizeof(fp) <= sizeof(unsigned long)); + enif_get_ulong(env, reg[1], (unsigned long *) &fp); +#endif + return (*fp)(env, result); +} + +#endif /* ERTS_DIRTY_SCHEDULERS */ + +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT + +ERL_NIF_TERM +enif_schedule_dirty_nif(ErlNifEnv* env, int flags, + ERL_NIF_TERM (*fp)(ErlNifEnv*, int, const ERL_NIF_TERM[]), + int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef USE_THREADS + erts_aint32_t state, n, a; + Process* proc = env->proc; + Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; + Export* ep = NULL; + int i; + + int chkflgs = (flags & (ERL_NIF_DIRTY_JOB_IO_BOUND|ERL_NIF_DIRTY_JOB_CPU_BOUND)); + if (chkflgs != ERL_NIF_DIRTY_JOB_IO_BOUND && chkflgs != ERL_NIF_DIRTY_JOB_CPU_BOUND) + return enif_make_badarg(env); + + a = erts_smp_atomic32_read_acqb(&proc->state); + while (1) { + n = state = a; + if (chkflgs == ERL_NIF_DIRTY_JOB_CPU_BOUND) + n |= ERTS_PSFLG_DIRTY_CPU_PROC; + else + n |= ERTS_PSFLG_DIRTY_IO_PROC; + a = erts_smp_atomic32_cmpxchg_mb(&proc->state, n, state); + if (a == state) + break; + } + if (!(ep = ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc))) + alloc_proc_psd(proc, &ep); + ERTS_VBUMP_ALL_REDS(proc); + ep->code[2] = argc; + for (i = 0; i < argc; i++) { + reg[i] = (Eterm) argv[i]; + } + proc->i = (BeamInstr*) ep->addressv[0]; + ep->code[4] = (BeamInstr) fp; + proc->freason = TRAP; + + return THE_NON_VALUE; +#else + return (*fp)(env, argc, argv); +#endif +} + +ERL_NIF_TERM +enif_schedule_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result, + ERL_NIF_TERM (*fp)(ErlNifEnv*, ERL_NIF_TERM)) +{ +#ifdef USE_THREADS + erts_aint32_t state, n, a; + Process* proc = env->proc; + Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; + Export* ep; + + a = erts_smp_atomic32_read_acqb(&proc->state); + while (1) { + n = state = a; + if (!(n & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q))) + break; + n &= ~(ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC + |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q); + a = erts_smp_atomic32_cmpxchg_mb(&proc->state, n, state); + if (a == state) + break; + } + if (!(ep = ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc))) + alloc_proc_psd(proc, &ep); + ERTS_VBUMP_ALL_REDS(proc); + ep->code[2] = 2; + reg[0] = (Eterm) result; +#if HAVE_INT64 && SIZEOF_LONG != 8 + ASSERT(sizeof(fp) <= sizeof(ErlNifUInt64)); + reg[1] = (Eterm) enif_make_uint64(env, (ErlNifUInt64) fp); +#else + ASSERT(sizeof(fp) <= sizeof(unsigned long)); + reg[1] = (Eterm) enif_make_ulong(env, (unsigned long) fp); +#endif + proc->i = (BeamInstr*) ep->addressv[0]; + ep->code[4] = (BeamInstr) execute_dirty_nif_finalizer; + proc->freason = TRAP; + + return THE_NON_VALUE; +#else + return (*fp)(env, result); +#endif +} + +/* A simple finalizer that just returns its result argument */ +ERL_NIF_TERM +enif_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result) +{ + return result; +} + +int +enif_is_on_dirty_scheduler(ErlNifEnv* env) +{ + return ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data); +} + +int +enif_have_dirty_schedulers() +{ +#ifdef USE_THREADS + return 1; +#else + return 0; +#endif +} + +#endif /* ERL_NIF_DIRTY_SCHEDULER_SUPPORT */ + /*************************************************************************** ** load_nif/2 ** ***************************************************************************/ diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 5f4dc21d5c..fb3c359ec9 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -23,7 +23,11 @@ #ifndef __ERL_NIF_H__ #define __ERL_NIF_H__ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "erl_native_features_config.h" #include "erl_drv_nif.h" /* Version history: @@ -34,9 +38,14 @@ ** 2.2: R14B03 enif_is_exception ** 2.3: R15 enif_make_reverse_list, enif_is_number ** 2.4: R16 enif_consume_timeslice +** 2.5: R17 dirty schedulers */ #define ERL_NIF_MAJOR_VERSION 2 +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT +#define ERL_NIF_MINOR_VERSION 5 +#else #define ERL_NIF_MINOR_VERSION 4 +#endif #include @@ -159,6 +168,14 @@ typedef int ErlNifTSDKey; typedef ErlDrvThreadOpts ErlNifThreadOpts; +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT +typedef enum +{ + ERL_NIF_DIRTY_JOB_CPU_BOUND = 1, + ERL_NIF_DIRTY_JOB_IO_BOUND = 2 +}ErlNifDirtyTaskFlags; +#endif + #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct { diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 2f841645e1..f5b27dfdfa 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -141,6 +141,13 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_number,(ErlNifEnv*, ERL_NIF_TERM term)); ERL_NIF_API_FUNC_DECL(void*,enif_dlopen,(const char* lib, void (*err_handler)(void*,const char*), void* err_arg)); ERL_NIF_API_FUNC_DECL(void*,enif_dlsym,(void* handle, const char* symbol, void (*err_handler)(void*,const char*), void* err_arg)); ERL_NIF_API_FUNC_DECL(int,enif_consume_timeslice,(ErlNifEnv*, int percent)); +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_dirty_nif,(ErlNifEnv*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[])); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_dirty_nif_finalizer,(ErlNifEnv*,ERL_NIF_TERM,ERL_NIF_TERM (*)(ErlNifEnv*,ERL_NIF_TERM))); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_dirty_nif_finalizer,(ErlNifEnv*,ERL_NIF_TERM)); +ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); +ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); +#endif /* ** Add new entries here to keep compatibility on Windows!!! @@ -266,6 +273,13 @@ ERL_NIF_API_FUNC_DECL(int,enif_consume_timeslice,(ErlNifEnv*, int percent)); # define enif_dlopen ERL_NIF_API_FUNC_MACRO(enif_dlopen) # define enif_dlsym ERL_NIF_API_FUNC_MACRO(enif_dlsym) # define enif_consume_timeslice ERL_NIF_API_FUNC_MACRO(enif_consume_timeslice) +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT +# define enif_schedule_dirty_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_dirty_nif) +# define enif_schedule_dirty_nif_finalizer ERL_NIF_API_FUNC_MACRO(enif_schedule_dirty_nif_finalizer) +# define enif_dirty_nif_finalizer ERL_NIF_API_FUNC_MACRO(enif_dirty_nif_finalizer) +# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler) +# define enif_have_dirty_schedulers ERL_NIF_API_FUNC_MACRO(enif_have_dirty_schedulers) +#endif /* ** Add new entries here diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 74cd84a998..937881212a 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -146,6 +146,11 @@ extern BeamInstr beam_continue_exit[]; int erts_sched_compact_load; int erts_sched_balance_util = 0; Uint erts_no_schedulers; +#ifdef ERTS_DIRTY_SCHEDULERS +Uint erts_no_dirty_cpu_schedulers; +Uint erts_no_dirty_cpu_schedulers_online; +Uint erts_no_dirty_io_schedulers; +#endif #define ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_VERY_LAZY (4*1024*1024) #define ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_LAZY (512*1024) @@ -259,6 +264,10 @@ ErtsAlignedRunQueue *erts_aligned_run_queues; Uint erts_no_run_queues; ErtsAlignedSchedulerData *erts_aligned_scheduler_data; +#ifdef ERTS_DIRTY_SCHEDULERS +ErtsAlignedSchedulerData *erts_aligned_dirty_cpu_scheduler_data; +ErtsAlignedSchedulerData *erts_aligned_dirty_io_scheduler_data; +#endif typedef union { ErtsSchedulerSleepInfo ssi; @@ -266,6 +275,12 @@ typedef union { } ErtsAlignedSchedulerSleepInfo; static ErtsAlignedSchedulerSleepInfo *aligned_sched_sleep_info; +#ifdef ERTS_DIRTY_SCHEDULERS +#ifdef ERTS_SMP +static ErtsAlignedSchedulerSleepInfo *aligned_dirty_cpu_sched_sleep_info; +static ErtsAlignedSchedulerSleepInfo *aligned_dirty_io_sched_sleep_info; +#endif +#endif static Uint last_reductions; static Uint last_exact_reductions; @@ -332,6 +347,16 @@ ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(proclist, (ASSERT(-1 <= ((int) (IX)) \ && ((int) (IX)) < ((int) erts_no_schedulers)), \ &aligned_sched_sleep_info[(IX)].ssi) +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(IX) \ + (ASSERT(0 <= ((int) (IX)) \ + && ((int) (IX)) < ((int) erts_no_dirty_cpu_schedulers)), \ + &aligned_dirty_cpu_sched_sleep_info[(IX)].ssi) +#define ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(IX) \ + (ASSERT(0 <= ((int) (IX)) \ + && ((int) (IX)) < ((int) erts_no_dirty_io_schedulers)), \ + &aligned_dirty_io_sched_sleep_info[(IX)].ssi) +#endif #define ERTS_FOREACH_RUNQ(RQVAR, DO) \ do { \ @@ -519,6 +544,13 @@ erts_pre_init_process(void) erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks = ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS; +#ifdef ERTS_DIRTY_SCHEDULERS + erts_psd_required_locks[ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT].get_locks + = ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT_GET_LOCKS; + erts_psd_required_locks[ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT].set_locks + = ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT_SET_LOCKS; +#endif + /* Check that we have locks for all entries */ for (ix = 0; ix < ERTS_PSD_SIZE; ix++) { ERTS_SMP_LC_ASSERT(erts_psd_required_locks[ix].get_locks); @@ -931,7 +963,9 @@ reply_sched_wall_time(void *vswtrp) ErlHeapFragment *bp = NULL; ASSERT(esdp); - +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); +#endif if (swtrp->set) { if (!swtrp->enable && esdp->sched_wall_time.enabled) { esdp->sched_wall_time.need = erts_sched_balance_util; @@ -1015,6 +1049,9 @@ erts_sched_wall_time_request(Process *c_p, int set, int enable) if (!set && !esdp->sched_wall_time.enabled) return THE_NON_VALUE; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); +#endif swtrp = swtreq_alloc(); ref = erts_make_ref(c_p); @@ -1492,6 +1529,9 @@ erts_schedule_multi_misc_aux_work(int ignore_self, if (ignore_self) { ErtsSchedulerData *esdp = erts_get_scheduler_data(); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); +#endif if (esdp) self = (int) esdp->no; } @@ -1601,7 +1641,7 @@ void erts_alloc_notify_delayed_dealloc(int ix) { ErtsSchedulerData *esdp = erts_get_scheduler_data(); - if (esdp) + if (esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp)) schedule_aux_work_wakeup(&esdp->aux_work_data, ix, ERTS_SSI_AUX_WORK_DD); @@ -1615,6 +1655,10 @@ erts_alloc_ensure_handle_delayed_dealloc_call(int ix) { #ifdef DEBUG ErtsSchedulerData *esdp = erts_get_scheduler_data(); +#ifdef ERTS_DIRTY_SCHEDULERS + if (esdp && ERTS_SCHEDULER_IS_DIRTY(esdp)) + return; +#endif ASSERT(!esdp || ix == (int) esdp->no); #endif set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(ix-1), @@ -2220,6 +2264,9 @@ static ERTS_INLINE void sched_active_sys(Uint no, ErtsRunQueue *rq) { ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); +#endif ASSERT(rq->waiting < 0); rq->waiting *= -1; rq->waiting--; @@ -2276,6 +2323,9 @@ static ERTS_INLINE void sched_change_waiting_sys_to_waiting(Uint no, ErtsRunQueue *rq) { ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); +#endif ASSERT(rq->waiting < 0); rq->waiting *= -1; } @@ -2291,7 +2341,7 @@ sched_waiting(Uint no, ErtsRunQueue *rq) else rq->waiting++; rq->woken = 0; - if (erts_system_profile_flags.scheduler) + if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && erts_system_profile_flags.scheduler) profile_scheduler(make_small(no), am_inactive); } @@ -2303,7 +2353,7 @@ sched_active(Uint no, ErtsRunQueue *rq) rq->waiting++; else rq->waiting--; - if (erts_system_profile_flags.scheduler) + if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && erts_system_profile_flags.scheduler) profile_scheduler(make_small(no), am_active); } @@ -2317,7 +2367,7 @@ ongoing_multi_scheduling_block(void) static ERTS_INLINE void empty_runq_aux(ErtsRunQueue *rq, Uint32 old_flags) { - if (old_flags & ERTS_RUNQ_FLG_NONEMPTY) { + if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && old_flags & ERTS_RUNQ_FLG_NONEMPTY) { #ifdef DEBUG erts_aint32_t empty = erts_smp_atomic32_read_nob(&no_empty_run_queues); /* @@ -2357,7 +2407,7 @@ static ERTS_INLINE void non_empty_runq(ErtsRunQueue *rq) { Uint32 old_flags = ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_NONEMPTY); - if (!(old_flags & ERTS_RUNQ_FLG_NONEMPTY)) { + if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && (!(old_flags & ERTS_RUNQ_FLG_NONEMPTY))) { #ifdef DEBUG erts_aint32_t empty = erts_smp_atomic32_read_nob(&no_empty_run_queues); /* @@ -2602,18 +2652,37 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) + erts_smp_spin_lock(&rq->sleepers.lock); +#endif flgs = sched_prep_spin_wait(ssi); if (flgs & ERTS_SSI_FLG_SUSPENDED) { /* Go suspend instead... */ +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) + erts_smp_spin_unlock(&rq->sleepers.lock); +#endif return; } +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) { + ssi->prev = NULL; + ssi->next = rq->sleepers.list; + if (rq->sleepers.list) + rq->sleepers.list->prev = ssi; + rq->sleepers.list = ssi; + erts_smp_spin_unlock(&rq->sleepers.lock); + } +#endif + /* * If all schedulers are waiting, one of them *should* * be waiting in erl_sys_schedule() */ - if (!prepare_for_sys_schedule()) { + if (ERTS_SCHEDULER_IS_DIRTY(esdp) || !prepare_for_sys_schedule()) { sched_waiting(esdp->no, rq); @@ -2623,12 +2692,13 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) tse_wait: - if (thr_prgr_active != working) + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && thr_prgr_active != working) sched_wall_time_change(esdp, thr_prgr_active); while (1) { - aux_work = erts_atomic32_read_acqb(&ssi->aux_work); + aux_work = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : + erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work) { if (!thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); @@ -2642,11 +2712,13 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) if (aux_work) flgs = erts_smp_atomic32_read_acqb(&ssi->flags); else { - if (thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 0); - sched_wall_time_change(esdp, 0); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 0); + sched_wall_time_change(esdp, 0); + } + erts_thr_progress_prepare_wait(esdp); } - erts_thr_progress_prepare_wait(esdp); flgs = sched_spin_wait(ssi, spincount); if (flgs & ERTS_SSI_FLG_SLEEPING) { @@ -2661,7 +2733,8 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) } while (res == EINTR); } } - erts_thr_progress_finalize_wait(esdp); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) + erts_thr_progress_finalize_wait(esdp); } if (!(flgs & ERTS_SSI_FLG_WAITING)) { @@ -2682,7 +2755,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) if (flgs & ~ERTS_SSI_FLG_SUSPENDED) erts_smp_atomic32_read_band_nob(&ssi->flags, ERTS_SSI_FLG_SUSPENDED); - if (!thr_prgr_active) { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } @@ -2699,6 +2772,9 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erts_smp_atomic32_set_relb(&function_calls, 0); *fcalls = 0; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); +#endif sched_waiting_sys(esdp->no, rq); @@ -2912,6 +2988,34 @@ wake_scheduler(ErtsRunQueue *rq) erts_sched_finish_poke(ssi, flgs); } +#ifdef ERTS_DIRTY_SCHEDULERS +static void +wake_dirty_scheduler(ErtsRunQueue *rq) +{ + ErtsSchedulerSleepInfo *ssi; + ErtsSchedulerSleepList *sl; + + ASSERT(ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); + + sl = &rq->sleepers; + erts_smp_spin_lock(&sl->lock); + ssi = sl->list; + if (!ssi) + erts_smp_spin_unlock(&sl->lock); + else { + sl->list = NULL; + erts_smp_spin_unlock(&sl->lock); + + ERTS_THR_MEMORY_BARRIER; + do { + ErtsSchedulerSleepInfo *wake_ssi = ssi; + ssi = ssi->next; + erts_sched_finish_poke(wake_ssi, ssi_flags_set_wake(wake_ssi)); + } while (ssi); + } +} +#endif + #define ERTS_NO_USED_RUNQS_SHIFT 16 #define ERTS_NO_RUNQS_MASK 0xffff @@ -3047,8 +3151,14 @@ static ERTS_INLINE void smp_notify_inc_runq(ErtsRunQueue *runq) { #ifdef ERTS_SMP - if (runq) - wake_scheduler(runq); + if (runq) { +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(runq->ix)) + wake_dirty_scheduler(runq); + else +#endif + wake_scheduler(runq); + } #endif } @@ -4899,7 +5009,10 @@ erts_sched_set_wake_cleanup_threshold(char *str) static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp) { - awdp->sched_id = esdp ? (int) esdp->no : 0; + if (!esdp || ERTS_SCHEDULER_IS_DIRTY(esdp)) + awdp->sched_id = 0; + else + awdp->sched_id = (int) esdp->no; awdp->esdp = esdp; awdp->ssi = esdp ? esdp->ssi : NULL; #ifdef ERTS_SMP @@ -4939,14 +5052,71 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp) #endif } +static void +init_scheduler_data(ErtsSchedulerData* esdp, int num, + ErtsSchedulerSleepInfo* ssi, + ErtsRunQueue* runq, + char** daww_ptr, size_t daww_sz) +{ +#ifdef ERTS_SMP + erts_bits_init_state(&esdp->erl_bits_state); + esdp->match_pseudo_process = NULL; + esdp->free_process = NULL; +#endif + esdp->x_reg_array = + erts_alloc_permanent_cache_aligned(ERTS_ALC_T_BEAM_REGISTER, + ERTS_X_REGS_ALLOCATED * + sizeof(Eterm)); + esdp->f_reg_array = + erts_alloc_permanent_cache_aligned(ERTS_ALC_T_BEAM_REGISTER, + MAX_REG * sizeof(FloatDef)); +#if !HEAP_ON_C_STACK + esdp->num_tmp_heap_used = 0; +#endif +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(runq->ix)) { + esdp->no = 0; + esdp->dirty_no = (Uint) num; + } + else { + esdp->no = (Uint) num; + esdp->dirty_no = 0; + } +#else + esdp->no = (Uint) num; +#endif + esdp->ssi = ssi; + esdp->current_process = NULL; + esdp->current_port = NULL; + + esdp->virtual_reds = 0; + esdp->cpu_id = -1; + + erts_init_atom_cache_map(&esdp->atom_cache_map); + + esdp->run_queue = runq; + esdp->run_queue->scheduler = esdp; + + if (daww_ptr) { + init_aux_work_data(&esdp->aux_work_data, esdp, *daww_ptr); +#ifdef ERTS_SMP + *daww_ptr += daww_sz; +#endif + } + + esdp->reductions = 0; + + init_sched_wall_time(&esdp->sched_wall_time); + erts_port_task_handle_init(&esdp->nosuspend_port_task_handle); +} + 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 + size_t size_runqs; init_misc_op_list_alloc(); init_proc_sys_task_queues_alloc(); @@ -4967,19 +5137,26 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) /* Create and initialize run queues */ n = no_schedulers; - - erts_aligned_run_queues = - erts_alloc_permanent_cache_aligned(ERTS_ALC_T_RUNQS, - sizeof(ErtsAlignedRunQueue) * n); + size_runqs = sizeof(ErtsAlignedRunQueue) * (n + ERTS_NUM_DIRTY_RUNQS); + erts_aligned_run_queues = + erts_alloc_permanent_cache_aligned(ERTS_ALC_T_RUNQS, size_runqs); #ifdef ERTS_SMP +#ifdef ERTS_DIRTY_SCHEDULERS + erts_aligned_run_queues += ERTS_NUM_DIRTY_RUNQS; +#endif erts_smp_atomic32_init_nob(&no_empty_run_queues, 0); #endif erts_no_run_queues = n; - for (ix = 0; ix < n; ix++) { + for (ix = -(ERTS_NUM_DIRTY_RUNQS); ix < n; ix++) { int pix, rix; +#ifdef ERTS_DIRTY_SCHEDULERS + ErtsRunQueue *rq = ERTS_RUNQ_IX_IS_DIRTY(ix) ? + ERTS_DIRTY_RUNQ_IX(ix) : ERTS_RUNQ_IX(ix); +#else ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); +#endif rq->ix = ix; @@ -4990,6 +5167,15 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) erts_smp_mtx_init_x(&rq->mtx, "run_queue", make_small(ix + 1)); erts_smp_cnd_init(&rq->cnd); +#ifdef ERTS_DIRTY_SCHEDULERS +#ifdef ERTS_SMP + if (ERTS_RUNQ_IX_IS_DIRTY(ix)) { + erts_smp_spinlock_init(&rq->sleepers.lock, "dirty_run_queue_sleep_list"); + rq->sleepers.list = NULL; + } +#endif +#endif + rq->waiting = 0; rq->woken = 0; ERTS_RUNQ_FLGS_INIT(rq, ERTS_RUNQ_FLG_NONEMPTY); @@ -5076,6 +5262,29 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) #ifdef ERTS_SMP aligned_sched_sleep_info++; + +#ifdef ERTS_DIRTY_SCHEDULERS + aligned_dirty_cpu_sched_sleep_info = + erts_alloc_permanent_cache_aligned( + ERTS_ALC_T_SCHDLR_SLP_INFO, + erts_no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerSleepInfo)); + for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { + ErtsSchedulerSleepInfo *ssi = &aligned_dirty_cpu_sched_sleep_info[ix].ssi; + erts_smp_atomic32_init_nob(&ssi->flags, 0); + ssi->event = NULL; /* initialized in sched_thread_func */ + erts_atomic32_init_nob(&ssi->aux_work, 0); + } + aligned_dirty_io_sched_sleep_info = + erts_alloc_permanent_cache_aligned( + ERTS_ALC_T_SCHDLR_SLP_INFO, + erts_no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerSleepInfo)); + for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + ErtsSchedulerSleepInfo *ssi = &aligned_dirty_io_sched_sleep_info[ix].ssi; + erts_smp_atomic32_init_nob(&ssi->flags, 0); + ssi->event = NULL; /* initialized in sched_thread_func */ + erts_atomic32_init_nob(&ssi->aux_work, 0); + } +#endif #endif /* Create and initialize scheduler specific data */ @@ -5086,6 +5295,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) daww_ptr = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA, daww_sz*n); #else + daww_sz = 0; daww_ptr = NULL; #endif @@ -5095,46 +5305,32 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) for (ix = 0; ix < n; ix++) { ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(ix); -#ifdef ERTS_SMP - erts_bits_init_state(&esdp->erl_bits_state); - esdp->match_pseudo_process = NULL; - esdp->free_process = NULL; -#endif - esdp->x_reg_array = - erts_alloc_permanent_cache_aligned(ERTS_ALC_T_BEAM_REGISTER, - ERTS_X_REGS_ALLOCATED * - sizeof(Eterm)); - esdp->f_reg_array = - erts_alloc_permanent_cache_aligned(ERTS_ALC_T_BEAM_REGISTER, - MAX_REG * sizeof(FloatDef)); -#if !HEAP_ON_C_STACK - esdp->num_tmp_heap_used = 0; -#endif - esdp->no = (Uint) ix+1; - esdp->ssi = ERTS_SCHED_SLEEP_INFO_IX(ix); - esdp->current_process = NULL; - esdp->current_port = NULL; - - esdp->virtual_reds = 0; - esdp->cpu_id = -1; - - erts_init_atom_cache_map(&esdp->atom_cache_map); - - esdp->run_queue = ERTS_RUNQ_IX(ix); - esdp->run_queue->scheduler = esdp; + init_scheduler_data(esdp, ix+1, ERTS_SCHED_SLEEP_INFO_IX(ix), + ERTS_RUNQ_IX(ix), &daww_ptr, daww_sz); + } - init_aux_work_data(&esdp->aux_work_data, esdp, daww_ptr); +#ifdef ERTS_DIRTY_SCHEDULERS #ifdef ERTS_SMP - daww_ptr += daww_sz; -#endif - - esdp->reductions = 0; - - init_sched_wall_time(&esdp->sched_wall_time); - - erts_port_task_handle_init(&esdp->nosuspend_port_task_handle); - + erts_aligned_dirty_cpu_scheduler_data = + erts_alloc_permanent_cache_aligned( + ERTS_ALC_T_SCHDLR_DATA, + erts_no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerData)); + for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { + ErtsSchedulerData *esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); + init_scheduler_data(esdp, ix+1, ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix), + ERTS_DIRTY_CPU_RUNQ, NULL, 0); + } + erts_aligned_dirty_io_scheduler_data = + erts_alloc_permanent_cache_aligned( + ERTS_ALC_T_SCHDLR_DATA, + erts_no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerData)); + for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix); + init_scheduler_data(esdp, ix+1, ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix), + ERTS_DIRTY_IO_RUNQ, NULL, 0); } +#endif +#endif init_misc_aux_work(); #if !HALFWORD_HEAP @@ -5198,6 +5394,10 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) #endif } erts_no_schedulers = 1; +#ifdef ERTS_DIRTY_SCHEDULERS + erts_no_dirty_cpu_schedulers = 0; + erts_no_dirty_io_schedulers = 0; +#endif #endif erts_smp_atomic32_init_nob(&function_calls, 0); @@ -5310,28 +5510,39 @@ check_enqueue_in_prio_queue(erts_aint32_t *prq_prio_p, *prq_prio_p = aprio; - max_qbit = (actual >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET) & ERTS_PSFLGS_QMASK; - max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS; - max_qbit &= -max_qbit; - /* - * max_qbit now either contain bit set for highest prio queue or a bit - * out of range (which will have a value larger than valid range). - */ +#ifdef ERTS_DIRTY_SCHEDULERS + if (!(actual & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC))) { +#endif + max_qbit = (actual >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET) & ERTS_PSFLGS_QMASK; + max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS; + max_qbit &= -max_qbit; + /* + * max_qbit now either contain bit set for highest prio queue or a bit + * out of range (which will have a value larger than valid range). + */ - if (qbit >= max_qbit) - return 0; /* Already queued in higher or equal prio */ + if (qbit >= max_qbit) + return 0; /* Already queued in higher or equal prio */ - /* Need to enqueue (if already enqueued, it is in lower prio) */ - *newp |= qbit << ERTS_PSFLGS_IN_PRQ_MASK_OFFSET; + /* Need to enqueue (if already enqueued, it is in lower prio) */ + *newp |= qbit << ERTS_PSFLGS_IN_PRQ_MASK_OFFSET; - if ((actual & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLGS_USR_PRIO_MASK)) - != (aprio << ERTS_PSFLGS_USR_PRIO_OFFSET)) { - /* - * Process struct already enqueued, or actual prio not - * equal to user prio, i.e., enqueue using proxy. - */ - return -1; + if ((actual & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLGS_USR_PRIO_MASK)) + != (aprio << ERTS_PSFLGS_USR_PRIO_OFFSET)) { + /* + * Process struct already enqueued, or actual prio not + * equal to user prio, i.e., enqueue using proxy. + */ + return -1; + } +#ifdef ERTS_DIRTY_SCHEDULERS + } else { + if (actual & ERTS_PSFLG_DIRTY_CPU_PROC) + *newp |= ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q; + else + *newp |= ERTS_PSFLG_DIRTY_IO_PROC_IN_Q; } +#endif /* * Enqueue using process struct. @@ -5387,10 +5598,21 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces } else { Process *sched_p; - ErtsRunQueue *runq = erts_get_runq_proc(p); + ErtsRunQueue *runq; ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & ERTS_PSFLG_ACTIVE_SYS)); +#ifdef ERTS_DIRTY_SCHEDULERS +#ifdef ERTS_SMP + if (ERTS_PSFLG_DIRTY_CPU_PROC & a) + runq = ERTS_DIRTY_CPU_RUNQ; + else if (ERTS_PSFLG_DIRTY_IO_PROC & a) + runq = ERTS_DIRTY_IO_RUNQ; + else +#endif +#endif + runq = erts_get_runq_proc(p); + if (enqueue < 0) sched_p = make_proxy_proc(proxy, p, enq_prio); else { @@ -5400,7 +5622,11 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces } #ifdef ERTS_SMP - if (!(ERTS_PSFLG_BOUND & n)) { + if (!(ERTS_PSFLG_BOUND & n) +#ifdef ERTS_DIRTY_SCHEDULERS + && !(n & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)) +#endif + ) { ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio); if (new_runq) { RUNQ_SET_RQ(&sched_p->run_queue, new_runq); @@ -5827,6 +6053,9 @@ suspend_scheduler(ErtsSchedulerData *esdp) * Regardless of why a scheduler is suspended, it ends up here. */ +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); +#endif ASSERT(no != 1); evacuate_run_queue(esdp->run_queue, &sbp); @@ -5994,22 +6223,44 @@ ErtsSchedSuspendResult erts_schedulers_state(Uint *total, Uint *online, Uint *active, + Uint *dirty_cpu, + Uint *dirty_cpu_online, + Uint *dirty_io, int yield_allowed) { - int res; + int res = ERTS_SCHDLR_SSPND_EINVAL; erts_aint32_t changing; - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); - if (yield_allowed && (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER)) - res = ERTS_SCHDLR_SSPND_YIELD_RESTART; - else { - *active = *online = schdlr_sspnd.online; - if (ongoing_multi_scheduling_block()) - *active = 1; - res = ERTS_SCHDLR_SSPND_DONE; + if (total) { + ASSERT(online); + ASSERT(active); + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); + if (yield_allowed && (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER)) + res = ERTS_SCHDLR_SSPND_YIELD_RESTART; + else { + *active = *online = schdlr_sspnd.online; + if (ongoing_multi_scheduling_block()) + *active = 1; + res = ERTS_SCHDLR_SSPND_DONE; + } + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + *total = erts_no_schedulers; } - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - *total = erts_no_schedulers; +#ifdef ERTS_DIRTY_SCHEDULERS + if (dirty_cpu) + *dirty_cpu = erts_no_dirty_cpu_schedulers; + if (dirty_cpu_online) + *dirty_cpu_online = erts_no_dirty_cpu_schedulers_online; + if (dirty_io) + *dirty_io = erts_no_dirty_io_schedulers; +#else + if (dirty_cpu) + *dirty_cpu = 0; + if (dirty_cpu_online) + *dirty_cpu_online = 0; + if (dirty_io) + *dirty_io = 0; +#endif return res; } @@ -6101,6 +6352,10 @@ erts_set_schedulers_online(Process *p, ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); wake_scheduler(rq); } +#ifdef ERTS_DIRTY_SCHEDULERS + wake_dirty_scheduler(ERTS_DIRTY_CPU_RUNQ); + wake_dirty_scheduler(ERTS_DIRTY_IO_RUNQ); +#endif } } @@ -6448,6 +6703,92 @@ sched_thread_func(void *vesdp) return NULL; } +#ifdef ERTS_DIRTY_SCHEDULERS +#ifdef ERTS_SMP +static void* +sched_dirty_cpu_thread_func(void *vesdp) +{ + ErtsThrPrgrCallbacks callbacks; + ErtsSchedulerData *esdp = vesdp; + Uint no = esdp->dirty_no; + ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(no-1)->event = erts_tse_fetch(); + callbacks.arg = (void *) esdp->ssi; + callbacks.wakeup = thr_prgr_wakeup; + callbacks.prepare_wait = NULL; + callbacks.wait = NULL; + callbacks.finalize_wait = NULL; + + erts_thr_progress_register_unmanaged_thread(&callbacks); +#ifdef ERTS_ENABLE_LOCK_CHECK + { + char buf[31]; + erts_snprintf(&buf[0], 31, "dirty cpu scheduler %beu", no); + erts_lc_set_thread_name(&buf[0]); + } +#endif + erts_tsd_set(sched_data_key, vesdp); +#if ERTS_USE_ASYNC_READY_Q + esdp->aux_work_data.async_ready.queue = NULL; +#endif + + erts_proc_lock_prepare_proc_lock_waiter(); + +#ifdef HIPE + hipe_thread_signal_init(); +#endif + erts_thread_init_float(); + + process_main(); + /* No schedulers should *ever* terminate */ + erl_exit(ERTS_ABORT_EXIT, + "Dirty CPU scheduler thread number %beu terminated\n", + no); + return NULL; +} + +static void* +sched_dirty_io_thread_func(void *vesdp) +{ + ErtsThrPrgrCallbacks callbacks; + ErtsSchedulerData *esdp = vesdp; + Uint no = esdp->dirty_no; + ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(no-1)->event = erts_tse_fetch(); + callbacks.arg = (void *) esdp->ssi; + callbacks.wakeup = thr_prgr_wakeup; + callbacks.prepare_wait = NULL; + callbacks.wait = NULL; + callbacks.finalize_wait = NULL; + + erts_thr_progress_register_unmanaged_thread(&callbacks); +#ifdef ERTS_ENABLE_LOCK_CHECK + { + char buf[31]; + erts_snprintf(&buf[0], 31, "dirty io scheduler %beu", no); + erts_lc_set_thread_name(&buf[0]); + } +#endif + erts_tsd_set(sched_data_key, vesdp); +#if ERTS_USE_ASYNC_READY_Q + esdp->aux_work_data.async_ready.queue = NULL; +#endif + + erts_proc_lock_prepare_proc_lock_waiter(); + +#ifdef HIPE + hipe_thread_signal_init(); +#endif + erts_thread_init_float(); + + process_main(); + /* No schedulers should *ever* terminate */ + erl_exit(ERTS_ABORT_EXIT, + "Dirty I/O scheduler thread number %beu terminated\n", + no); + return NULL; +} +#endif +#endif + static ethr_tid aux_tid; void @@ -6498,6 +6839,26 @@ erts_start_schedulers(void) erts_no_schedulers = actual; +#ifdef ERTS_DIRTY_SCHEDULERS +#ifdef ERTS_SMP + { + int ix; + for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { + ErtsSchedulerData *esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); + res = ethr_thr_create(&esdp->tid,sched_dirty_cpu_thread_func,(void*)esdp,&opts); + if (res != 0) + erl_exit(1, "Failed to create dirty cpu scheduler thread %d\n", ix); + } + for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix); + res = ethr_thr_create(&esdp->tid,sched_dirty_io_thread_func,(void*)esdp,&opts); + if (res != 0) + erl_exit(1, "Failed to create dirty io scheduler thread %d\n", ix); + } + } +#endif +#endif + ERTS_THR_MEMORY_BARRIER; res = ethr_thr_create(&aux_tid, aux_thread, NULL, &opts); @@ -7486,7 +7847,8 @@ Process *schedule(Process *p, int calls) input_reductions = INPUT_REDUCTIONS; } - ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); + ERTS_SMP_LC_ASSERT(ERTS_SCHEDULER_IS_DIRTY(erts_get_scheduler_data()) + || !erts_thr_progress_is_blocking()); /* * Clean up after the process being scheduled out. @@ -7597,7 +7959,8 @@ Process *schedule(Process *p, int calls) } - ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); + ERTS_SMP_LC_ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp) + || !erts_thr_progress_is_blocking()); check_activities_to_run: { #ifdef ERTS_SMP @@ -7611,22 +7974,25 @@ Process *schedule(Process *p, int calls) erts_smp_runq_lock(rq); } - if (rq->check_balance_reds <= 0) - check_balance(rq); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (rq->check_balance_reds <= 0) + check_balance(rq); - ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); - ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); + ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); - mps = erts_get_migration_paths_managed(); - mp = &mps->mpath[rq->ix]; + mps = erts_get_migration_paths_managed(); + mp = &mps->mpath[rq->ix]; - if (mp->flags & ERTS_RUNQ_FLGS_IMMIGRATE_QMASK) - immigrate(rq, mp); + if (mp->flags & ERTS_RUNQ_FLGS_IMMIGRATE_QMASK) + immigrate(rq, mp); + } + ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); continue_check_activities_to_run: flags = ERTS_RUNQ_FLGS_GET_NOB(rq); continue_check_activities_to_run_known_flags: - ASSERT(flags & ERTS_RUNQ_FLG_NONEMPTY); + ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp) + || flags & ERTS_RUNQ_FLG_NONEMPTY); if (flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND|ERTS_RUNQ_FLG_SUSPENDED)) { @@ -7641,7 +8007,7 @@ Process *schedule(Process *p, int calls) } } - { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { erts_aint32_t aux_work; int leader_update = erts_thr_progress_update(esdp); aux_work = erts_atomic32_read_acqb(&esdp->ssi->aux_work); @@ -7653,9 +8019,9 @@ Process *schedule(Process *p, int calls) handle_aux_work(&esdp->aux_work_data, aux_work, 0); erts_smp_runq_lock(rq); } - } - ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); + ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); + } ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); #else /* ERTS_SMP */ @@ -7684,7 +8050,7 @@ Process *schedule(Process *p, int calls) if (flags & ERTS_RUNQ_FLG_INACTIVE) empty_runq(rq); else { - if (try_steal_task(rq)) + if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && try_steal_task(rq)) goto continue_check_activities_to_run; empty_runq(rq); @@ -7710,7 +8076,8 @@ Process *schedule(Process *p, int calls) goto check_activities_to_run; } - else if (fcalls > input_reductions && prepare_for_sys_schedule()) { + else if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && + (fcalls > input_reductions && prepare_for_sys_schedule())) { /* * Schedule system-level activities. */ @@ -7814,6 +8181,14 @@ Process *schedule(Process *p, int calls) psflg_band_mask = ~(((erts_aint32_t) 1) << (ERTS_PSFLGS_GET_PRQ_PRIO(state) + ERTS_PSFLGS_IN_PRQ_MASK_OFFSET)); +#ifdef ERTS_DIRTY_SCHEDULERS + /* if a non-dirty scheduler picks up a process marked as already being + in a dirty run queue, just drop it and go get another process */ + if (state & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q) && + !ERTS_SCHEDULER_IS_DIRTY(esdp)) + goto pick_next_process; +#endif + if (!(state & ERTS_PSFLG_PROXY)) psflg_band_mask &= ~ERTS_PSFLG_IN_RUNQ; else { @@ -7896,6 +8271,10 @@ Process *schedule(Process *p, int calls) (UWord) esdp->no); int migrated = old && old != esdp->no; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); +#endif + prio = (int) ERTS_PSFLGS_GET_USR_PRIO(state); erts_smp_spin_lock(&erts_sched_stat.lock); @@ -9706,8 +10085,12 @@ save_pending_exiter(Process *p) non_empty_runq(rq); erts_smp_runq_unlock(rq); - - wake_scheduler(rq); +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) + wake_dirty_scheduler(rq); + else +#endif + wake_scheduler(rq); } #endif diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 6155f99b85..a0519dc0d9 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -74,6 +74,10 @@ struct ErtsNodesMonitor_; #define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT 0 #define ERTS_MAX_NO_OF_SCHEDULERS 1024 +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_MAX_NO_OF_DIRTY_CPU_SCHEDULERS ERTS_MAX_NO_OF_SCHEDULERS +#define ERTS_MAX_NO_OF_DIRTY_IO_SCHEDULERS ERTS_MAX_NO_OF_SCHEDULERS +#endif #define ERTS_DEFAULT_MAX_PROCESSES (1 << 18) @@ -103,6 +107,11 @@ extern Export exp_send, exp_receive, exp_timeout; extern int erts_sched_compact_load; extern int erts_sched_balance_util; extern Uint erts_no_schedulers; +#ifdef ERTS_DIRTY_SCHEDULERS +extern Uint erts_no_dirty_cpu_schedulers; +extern Uint erts_no_dirty_cpu_schedulers_online; +extern Uint erts_no_dirty_io_schedulers; +#endif extern Uint erts_no_run_queues; extern int erts_sched_thread_suggested_stack_size; #define ERTS_SCHED_THREAD_MIN_STACK_SIZE 4 /* Kilo words */ @@ -275,6 +284,13 @@ typedef enum { typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo; +#ifdef ERTS_DIRTY_SCHEDULERS +typedef struct { + erts_smp_spinlock_t lock; + ErtsSchedulerSleepInfo *list; +} ErtsSchedulerSleepList; +#endif + struct ErtsSchedulerSleepInfo_ { #ifdef ERTS_SMP ErtsSchedulerSleepInfo *next; @@ -387,6 +403,12 @@ struct ErtsRunQueue_ { erts_smp_mtx_t mtx; erts_smp_cnd_t cnd; +#ifdef ERTS_DIRTY_SCHEDULERS +#ifdef ERTS_SMP + ErtsSchedulerSleepList sleepers; +#endif +#endif + ErtsSchedulerData *scheduler; int waiting; /* < 0 in sys schedule; > 0 on cnd variable */ int woken; @@ -547,7 +569,10 @@ struct ErtsSchedulerData_ { #endif ErtsSchedulerSleepInfo *ssi; Process *current_process; - Uint no; /* Scheduler number */ + Uint no; /* Scheduler number for normal schedulers */ +#ifdef ERTS_DIRTY_SCHEDULERS + Uint dirty_no; /* Scheduler number for dirty schedulers */ +#endif Port *current_port; ErtsRunQueue *run_queue; int virtual_reds; @@ -574,6 +599,10 @@ typedef union { } ErtsAlignedSchedulerData; extern ErtsAlignedSchedulerData *erts_aligned_scheduler_data; +#ifdef ERTS_DIRTY_SCHEDULERS +extern ErtsAlignedSchedulerData *erts_aligned_dirty_cpu_scheduler_data; +extern ErtsAlignedSchedulerData *erts_aligned_dirty_io_scheduler_data; +#endif #ifndef ERTS_SMP extern ErtsSchedulerData *erts_scheduler_data; @@ -685,8 +714,13 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi) #define ERTS_PSD_DIST_ENTRY 3 #define ERTS_PSD_CALL_TIME_BP 4 #define ERTS_PSD_DELAYED_GC_TASK_QS 5 +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT 6 +#define ERTS_PSD_SIZE 7 +#else #define ERTS_PSD_SIZE 6 +#endif typedef struct { void *data[ERTS_PSD_SIZE]; @@ -713,6 +747,11 @@ typedef struct { #define ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS ERTS_PROC_LOCK_MAIN #define ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS ERTS_PROC_LOCK_MAIN +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT_GET_LOCKS ERTS_PROC_LOCK_MAIN +#define ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT_SET_LOCKS ERTS_PROC_LOCK_MAIN +#endif + typedef struct { ErtsProcLocks get_locks; ErtsProcLocks set_locks; @@ -1026,6 +1065,12 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15) #define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16) #define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17) +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(18) +#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(19) +#define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q ERTS_PSFLG_BIT(20) +#define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q ERTS_PSFLG_BIT(21) +#endif #define ERTS_PSFLGS_IN_PRQ_MASK (ERTS_PSFLG_IN_PRQ_MAX \ | ERTS_PSFLG_IN_PRQ_HIGH \ @@ -1231,12 +1276,46 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; (p)->flags &= ~F_TIMO; \ } while (0) +#if defined(ERTS_DIRTY_SCHEDULERS) && defined(ERTS_SMP) +#define ERTS_NUM_DIRTY_RUNQS 2 +#else +#define ERTS_NUM_DIRTY_RUNQS 0 +#endif + #define ERTS_RUNQ_IX(IX) \ (ASSERT(0 <= (IX) && (IX) < erts_no_run_queues), \ &erts_aligned_run_queues[(IX)].runq) +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_RUNQ_IX_IS_DIRTY(IX) \ + (-(ERTS_NUM_DIRTY_RUNQS) <= (IX) && (IX) < 0) +#define ERTS_DIRTY_RUNQ_IX(IX) \ + (ASSERT(ERTS_RUNQ_IX_IS_DIRTY(IX)), \ + &erts_aligned_run_queues[(IX)].runq) +#define ERTS_DIRTY_CPU_RUNQ (&erts_aligned_run_queues[-1].runq) +#define ERTS_DIRTY_IO_RUNQ (&erts_aligned_run_queues[-2].runq) +#else +#define ERTS_RUNQ_IX_IS_DIRTY(IX) 0 +#endif #define ERTS_SCHEDULER_IX(IX) \ (ASSERT(0 <= (IX) && (IX) < erts_no_schedulers), \ &erts_aligned_scheduler_data[(IX)].esd) +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_DIRTY_CPU_SCHEDULER_IX(IX) \ + (ASSERT(0 <= (IX) && (IX) < erts_no_dirty_cpu_schedulers), \ + &erts_aligned_dirty_cpu_scheduler_data[(IX)].esd) +#define ERTS_DIRTY_IO_SCHEDULER_IX(IX) \ + (ASSERT(0 <= (IX) && (IX) < erts_no_dirty_io_schedulers), \ + &erts_aligned_dirty_io_scheduler_data[(IX)].esd) +#ifdef ERTS_SMP +#define ERTS_SCHEDULER_IS_DIRTY(ESDP) \ + ((ESDP)->dirty_no != 0) +#else +#define ERTS_SCHEDULER_IS_DIRTY(ESDP) 0 +#endif +#else +#define ERTS_RUNQ_IX_IS_DIRTY(IX) 0 +#define ERTS_SCHEDULER_IS_DIRTY(ESDP) 0 +#endif void erts_pre_init_process(void); void erts_late_init_process(void); @@ -1439,9 +1518,11 @@ int erts_dbg_check_halloc_lock(Process *p); void erts_dbg_multi_scheduling_return_trap(Process *, Eterm); #endif int erts_get_max_no_executing_schedulers(void); -#ifdef ERTS_SMP +#if defined(ERTS_SMP) || defined(ERTS_DIRTY_SCHEDULERS) ErtsSchedSuspendResult -erts_schedulers_state(Uint *, Uint *, Uint *, int); +erts_schedulers_state(Uint *, Uint *, Uint *, Uint *, Uint *, Uint *, int); +#endif +#ifdef ERTS_SMP ErtsSchedSuspendResult erts_set_schedulers_online(Process *p, ErtsProcLocks plocks, @@ -1559,7 +1640,7 @@ do { \ ErtsSchedulerData *esdp__ = ((P) \ ? ERTS_PROC_GET_SCHDATA((Process *) (P)) \ : erts_get_scheduler_data()); \ - if (esdp__) \ + if (esdp__ && !ERTS_SCHEDULER_IS_DIRTY(esdp__)) \ esdp__->verify_unused_temp_alloc( \ esdp__->verify_unused_temp_alloc_data); \ } while (0) @@ -1694,6 +1775,13 @@ erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *data) #define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, L, PBT) \ ((ErtsProcSysTaskQs *) erts_psd_set((P), (L), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT))) +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(P) \ + ((Export *) erts_psd_get((P), ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT)) +#define ERTS_PROC_SET_DIRTY_SCHED_TRAP_EXPORT(P, L, DSTE) \ + ((Export *) erts_psd_set((P), (L), ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT, (void *) (DSTE))) +#endif + ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p); ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p, @@ -1887,7 +1975,12 @@ Uint erts_get_scheduler_id(void) { #ifdef ERTS_SMP ErtsSchedulerData *esdp = erts_get_scheduler_data(); - return esdp ? esdp->no : (Uint) 0; +#ifdef ERTS_DIRTY_SCHEDULERS + if (esdp && ERTS_SCHEDULER_IS_DIRTY(esdp)) + return 0; + else +#endif + return esdp ? esdp->no : (Uint) 0; #else return erts_get_scheduler_data() ? (Uint) 1 : (Uint) 0; #endif diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 9a70e8646a..affb66289b 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -36,7 +36,7 @@ threading/1, send/1, send2/1, send3/1, send_threaded/1, neg/1, is_checks/1, get_length/1, make_atom/1, make_string/1, reverse_list_test/1, - otp_9668/1, consume_timeslice/1 + otp_9668/1, consume_timeslice/1, dirty_nif/1 ]). -export([many_args_100/100]). @@ -63,7 +63,7 @@ all() -> resource_takeover, threading, send, send2, send3, send_threaded, neg, is_checks, get_length, make_atom, make_string,reverse_list_test, - otp_9668, consume_timeslice + otp_9668, consume_timeslice, dirty_nif ]. groups() -> @@ -1343,6 +1343,20 @@ consume_timeslice(Config) when is_list(Config) -> ok. +dirty_nif(Config) when is_list(Config) -> + try erlang:system_info(dirty_cpu_schedulers) of + N when is_integer(N) -> + ensure_lib_loaded(Config), + Val1 = 42, + Val2 = "Erlang", + Val3 = list_to_binary([Val2, 0]), + {Val1, Val2, Val3} = call_dirty_nif(Val1, Val2, Val3), + ok + catch + error:badarg -> + {skipped,"No dirty scheduler support"} + end. + next_msg(Pid) -> receive M -> M @@ -1472,6 +1486,7 @@ echo_int(_) -> ?nif_stub. type_sizes() -> ?nif_stub. otp_9668_nif(_) -> ?nif_stub. consume_timeslice_nif(_,_) -> ?nif_stub. +call_dirty_nif(_,_,_) -> ?nif_stub. nif_stub_error(Line) -> exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 0c4a9f7e5c..6f902e186d 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1494,6 +1494,48 @@ static ERL_NIF_TERM consume_timeslice_nif(ErlNifEnv* env, int argc, const ERL_NI } } +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT +static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int n; + char s[10]; + ErlNifBinary b; + ERL_NIF_TERM result; + if (enif_have_dirty_schedulers()) { + assert(enif_is_on_dirty_scheduler(env)); + } + assert(argc == 3); + enif_get_int(env, argv[0], &n); + enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1); + enif_inspect_binary(env, argv[2], &b); + result = enif_make_tuple3(env, + enif_make_int(env, n), + enif_make_string(env, s, ERL_NIF_LATIN1), + enif_make_binary(env, &b)); + return enif_schedule_dirty_nif_finalizer(env, result, enif_dirty_nif_finalizer); +} + +static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int n; + char s[10]; + ErlNifBinary b; + assert(!enif_is_on_dirty_scheduler(env)); + if (argc != 3) + return enif_make_badarg(env); + if (enif_have_dirty_schedulers()) { + if (enif_get_int(env, argv[0], &n) && + enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1) && + enif_inspect_binary(env, argv[2], &b)) + return enif_schedule_dirty_nif(env, ERL_NIF_DIRTY_JOB_CPU_BOUND, dirty_nif, argc, argv); + else + return enif_make_badarg(env); + } else { + return dirty_nif(env, argc, argv); + } +} +#endif + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -1543,7 +1585,10 @@ static ErlNifFunc nif_funcs[] = {"echo_int", 1, echo_int}, {"type_sizes", 0, type_sizes}, {"otp_9668_nif", 1, otp_9668_nif}, - {"consume_timeslice_nif", 2, consume_timeslice_nif} + {"consume_timeslice_nif", 2, consume_timeslice_nif}, +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT + {"call_dirty_nif", 3, call_dirty_nif}, +#endif }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 78a50744ef..2cf7280ebc 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -829,7 +829,17 @@ int main(int argc, char **argv) if (argv[i][2] == 'P') { if (argv[i][3] != '\0') goto the_default; - } else if (argv[i][2] != '\0') + } +#ifdef ERTS_DIRTY_SCHEDULERS + else if (argv[i][2] == 'D') { + char* type = argv[i]+3; + if (strcmp(type, "cpu") != 0 && + strcmp(type, "Pcpu") != 0 && + strcmp(type, "io") != 0) + goto the_default; + } +#endif + else if (argv[i][2] != '\0') goto the_default; if (i+1 >= argc) usage(argv[i]); diff --git a/erts/include/erl_native_features_config.h.in b/erts/include/erl_native_features_config.h.in new file mode 100644 index 0000000000..d1674cb256 --- /dev/null +++ b/erts/include/erl_native_features_config.h.in @@ -0,0 +1,21 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2004-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% + */ + +/* Dirty scheduler support */ +#undef ERL_NIF_DIRTY_SCHEDULER_SUPPORT diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in index 4f0a5e5202..cf1aef518a 100644 --- a/erts/lib_src/Makefile.in +++ b/erts/lib_src/Makefile.in @@ -458,6 +458,7 @@ RELSYSDIR = $(RELEASE_PATH)/erts-$(VSN) RELEASE_INCLUDES= \ $(ERTS_INCL)/erl_memory_trace_parser.h \ $(ERTS_INCL)/$(TARGET)/erl_int_sizes_config.h \ + $(ERTS_INCL)/$(TARGET)/erl_native_features_config.h \ $(ERTS_INCL)/erl_fixed_size_int_types.h RELEASE_LIBS=$(ERTS_LIBS) diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 3c77d6ae0f..f38377647c 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index f99d5bfdd0..ee5bd3e515 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2090,6 +2090,10 @@ subtract(_,_) -> (cpu_topology, CpuTopology) -> OldCpuTopology when CpuTopology :: cpu_topology(), OldCpuTopology :: cpu_topology(); + (dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline) -> + OldDirtyCPUSchedulersOnline when + DirtyCPUSchedulersOnline :: pos_integer(), + OldDirtyCPUSchedulersOnline :: pos_integer(); (fullsweep_after, Number) -> OldNumber when Number :: non_neg_integer(), OldNumber :: non_neg_integer(); @@ -2220,6 +2224,9 @@ tuple_to_list(_Tuple) -> CpuTopology :: cpu_topology(); (creation) -> integer(); (debug_compiled) -> boolean(); + (dirty_cpu_schedulers) -> non_neg_integer(); + (dirty_cpu_schedulers_online) -> non_neg_integer(); + (dirty_io_schedulers) -> non_neg_integer(); (dist) -> binary(); (dist_buf_busy_limit) -> non_neg_integer(); (dist_ctrl) -> {Node :: node(), -- cgit v1.2.3 From 331bb6cab54e6697e12cc9c5a4ca0ae618c37dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 23 May 2012 16:08:59 +0200 Subject: BEAM loader: Support preservation of extra operand in transforms It was not possible to preserve extra arguments in transformations. The following (hypothetical) example will now work: some_op Lit=c SizeArg Rest=* => move Lit x | some_op x SizeArg Rest --- erts/emulator/beam/beam_load.c | 18 ++++++++++++++++-- erts/emulator/utils/beam_makeops | 23 ++++++++++++++++------- 2 files changed, 32 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 938fd8f2c9..58207ec75b 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4376,6 +4376,7 @@ transform_engine(LoaderState* st) Uint* restart; /* Where to restart if current match fails. */ GenOpArg def_vars[TE_MAX_VARS]; /* Default buffer for variables. */ GenOpArg* var = def_vars; + int num_vars = 0; int i; /* General index. */ Uint mask; GenOp* instr; @@ -4578,9 +4579,9 @@ transform_engine(LoaderState* st) { int n = *pc++; int formal_arity = gen_opc[instr->op].arity; - int num_vars = n + (instr->arity - formal_arity); int j = formal_arity; + num_vars = n + (instr->arity - formal_arity); var = erts_alloc(ERTS_ALC_T_LOADER_TMP, num_vars * sizeof(GenOpArg)); for (i = 0; i < n; i++) { @@ -4592,7 +4593,6 @@ transform_engine(LoaderState* st) } break; #endif - case TOP_next_arg: ap++; break; @@ -4680,6 +4680,20 @@ transform_engine(LoaderState* st) instr->a[ap].val = var[i].val; ap++; break; +#if defined(TOP_store_rest_args) + case TOP_store_rest_args: + { + int n = *pc++; + int num_extra = num_vars - n; + + ASSERT(n <= num_vars); + GENOP_ARITY(instr, instr->arity+num_extra); + memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg)); + memcpy(instr->a+ap, var+n, num_extra*sizeof(GenOpArg)); + ap += num_extra; + } + break; +#endif case TOP_try_me_else: restart = pc + 1; restart += *pc++; diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 16a949c2a6..0b7c16f606 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -1202,6 +1202,7 @@ sub parse_transformation { my($from, $to) = split(/\s*=>\s*/); my(@op); + my $rest_var; # The source instructions. @@ -1212,7 +1213,7 @@ sub parse_transformation { $_ = (&compile_transform_function($name, split(/\s*,\s*/, $arglist))); } else { (@op) = split; - $_ = &compile_transform(1, @op); + ($rest_var,$_) = compile_transform(1, $rest_var, @op); } } @@ -1230,7 +1231,7 @@ sub parse_transformation { @to = split(/\s*\|\s*/, $to); foreach (@to) { (@op) = split; - $_ = &compile_transform(0, @op); + (undef,$_) = compile_transform(0, $rest_var, @op); } } push(@transformations, [$., $orig, [@from], [reverse @to]]); @@ -1243,12 +1244,18 @@ sub compile_transform_function { } sub compile_transform { - my($src, $name, @ops) = @_; + my($src, $rest_var, $name, @ops) = @_; my $arity = 0; - + foreach (@ops) { my(@list) = &tr_parse_op($src, $_); - $arity++ unless $list[1] eq '*'; + if ($list[1] eq '*') { + $rest_var = $list[0]; + } elsif (defined $rest_var and $list[0] eq $rest_var) { + $list[1] = '*'; + } else { + $arity++; + } $_ = [ @list ]; } @@ -1260,7 +1267,7 @@ sub compile_transform { $is_transformed{$name,$arity} = 1; } - [$name,$arity,@ops]; + ($rest_var,[$name,$arity,@ops]); } sub tr_parse_op { @@ -1681,7 +1688,9 @@ sub tr_gen_to { foreach $op (@ops) { my($var, $type, $type_val) = @$op; - if ($var ne '') { + if ($type eq '*') { + push(@code, make_op($var, 'store_rest_args', $var{$var})); + } elsif ($var ne '') { &error($where, "variable '$var' unbound") unless defined $var{$var}; push(@code, &make_op($var, 'store_var_next_arg', $var{$var})); -- cgit v1.2.3 From 64ee859fc4c17259ab95192abf7493fed8f2b0ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 May 2012 10:50:30 +0200 Subject: Implement support for maps in the compiler To make it possible to build the entire OTP system, also define dummys for the instructions in ops.tab. --- erts/emulator/beam/ops.tab | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index c29f3f9b1b..b52ecfaefc 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1465,6 +1465,15 @@ fclearerror apply I apply_last I P +# +# Map instructions in R16. +# + +put_map Fail Src Dst Live Size Rest=* => jump Fail +is_map Fail Src => jump Fail +has_map_field Fail Src Key => jump Fail +get_map_element Fail Src Key Dst => jump Fail + # # Optimize addition and subtraction of small literals using # the i_increment/4 instruction (in bodies, not in guards). -- cgit v1.2.3 From 345bf9d4634f81ad0343e8c60442bdd293e41835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 21 May 2013 17:00:11 +0200 Subject: erts: Maps beam-instruction definitions --- erts/emulator/beam/ops.tab | 67 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index b52ecfaefc..b89bdb2e3a 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1466,14 +1466,69 @@ apply I apply_last I P # -# Map instructions in R16. -# - -put_map Fail Src Dst Live Size Rest=* => jump Fail -is_map Fail Src => jump Fail -has_map_field Fail Src Key => jump Fail +# Map instructions in R17. +# + +# put_map Fail Src Dst Live Size Rest=* => jump Fail +# is_map Fail Src => jump Fail +# has_map_field Fail Src Key => jump Fail +# get_map_element Fail Src Key Dst => jump Fail + +put_map F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map F Src Dst Live Size Rest=* => update_map F Src Dst Live Size Rest + +new_map j d I I +update_map j d d I I + +is_map Fail cq => jump Fail + +%macro: is_map IsMap -fail_action +is_map f r +is_map f x +is_map f y + +has_map_field Fail Src=rxy Key=arxy => i_has_map_field Fail Src Key +has_map_field Fail Src Key => move Key x | i_has_map_field Fail Src x + +%macro: i_has_map_field HasMapField -fail_action +i_has_map_field f r a +i_has_map_field f x a +i_has_map_field f y a +i_has_map_field f r r +i_has_map_field f x r +i_has_map_field f y r +i_has_map_field f r x +i_has_map_field f x x +i_has_map_field f y x +i_has_map_field f r y +i_has_map_field f x y +i_has_map_field f y y + +get_map_element Fail Src=rxy Key=ax Dst => i_get_map_element Fail Src Key Dst +get_map_element Fail Src=rxy Key=rycq Dst => \ + move Key x | i_get_map_element Fail Src x Dst get_map_element Fail Src Key Dst => jump Fail +%macro: i_get_map_element GetMapElement -fail_action +i_get_map_element f r a r +i_get_map_element f x a r +i_get_map_element f y a r +i_get_map_element f r a x +i_get_map_element f x a x +i_get_map_element f y a x +i_get_map_element f r a y +i_get_map_element f x a y +i_get_map_element f y a y +i_get_map_element f r x r +i_get_map_element f x x r +i_get_map_element f y x r +i_get_map_element f r x x +i_get_map_element f x x x +i_get_map_element f y x x +i_get_map_element f r x y +i_get_map_element f x x y +i_get_map_element f y x y + # # Optimize addition and subtraction of small literals using # the i_increment/4 instruction (in bodies, not in guards). -- cgit v1.2.3 From 03d5bb0d1bd5d7c4e09ee793a680f2d538fe0122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 21 May 2013 18:09:55 +0200 Subject: erts: Initial Map instructions, type and structure --- erts/emulator/Makefile.in | 2 +- erts/emulator/beam/beam_emu.c | 341 ++++++++++++++++++++++++++++++++++- erts/emulator/beam/bif.tab | 6 + erts/emulator/beam/copy.c | 32 ++++ erts/emulator/beam/erl_bif_op.c | 12 +- erts/emulator/beam/erl_db_util.c | 1 + erts/emulator/beam/erl_debug.c | 1 + erts/emulator/beam/erl_gc.c | 1 + erts/emulator/beam/erl_gc.h | 37 ++-- erts/emulator/beam/erl_map.c | 225 +++++++++++++++++++++++ erts/emulator/beam/erl_map.h | 64 +++++++ erts/emulator/beam/erl_printf_term.c | 52 +++++- erts/emulator/beam/erl_term.c | 6 +- erts/emulator/beam/erl_term.h | 44 +++-- erts/emulator/beam/utils.c | 41 ++++- erts/emulator/hipe/hipe_bif2.c | 1 + erts/emulator/hipe/hipe_debug.c | 1 + 17 files changed, 811 insertions(+), 56 deletions(-) create mode 100644 erts/emulator/beam/erl_map.c create mode 100644 erts/emulator/beam/erl_map.h (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index b270099566..54365f279c 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -780,7 +780,7 @@ RUN_OBJS = \ $(OBJDIR)/erl_zlib.o $(OBJDIR)/erl_nif.o \ $(OBJDIR)/erl_bif_binary.o $(OBJDIR)/erl_ao_firstfit_alloc.o \ $(OBJDIR)/erl_thr_queue.o $(OBJDIR)/erl_sched_spec_pre_alloc.o \ - $(OBJDIR)/erl_ptab.o + $(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o ifeq ($(TARGET),win32) DRV_OBJS = \ diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 7fecdd5c5f..5c955b9566 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -31,6 +31,7 @@ #include "big.h" #include "beam_load.h" #include "erl_binary.h" +#include "erl_map.h" #include "erl_bits.h" #include "dist.h" #include "beam_bp.h" @@ -701,6 +702,19 @@ extern int count_instructions; Fail; \ } +#define IsMap(Src, Fail) if (is_not_map(Src)) { Fail; } + +#define HasMapField(Src, Key, Fail) if (has_not_map_field(Src, Key)) { Fail; } + +#define GetMapElement(Src, Key, Dst, Fail) \ + do { \ + Eterm _res = get_map_element(Src, Key); \ + if (is_non_value(_res)) { \ + Fail; \ + } \ + Dst = _res; \ + } while (0) + #define IsFunction(X, Action) \ do { \ if ( !(is_any_fun(X)) ) { \ @@ -944,7 +958,11 @@ static BeamInstr* apply_fun(Process* p, Eterm fun, Eterm args, Eterm* reg) NOINLINE; static Eterm new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) NOINLINE; - +static Eterm new_map(Process* p, Eterm* reg, BeamInstr* I) NOINLINE; +static Eterm update_map(Process* p, Eterm* reg, + Eterm map, BeamInstr* I) NOINLINE; +static int has_not_map_field(Eterm map, Eterm key); +static Eterm get_map_element(Eterm map, Eterm key); /* * Functions not directly called by process_main(). OK to inline. @@ -2323,6 +2341,37 @@ void process_main(void) Goto(*I); } + OpCase(new_map_jdII): { + Eterm res; + + x(0) = r(0); + SWAPOUT; + res = new_map(c_p, reg, I); + SWAPIN; + r(0) = x(0); + StoreResult(res, Arg(1)); + Next(4+Arg(3)); + } + + OpCase(update_map_jddII): { + Eterm res; + Eterm map; + + GetArg1(1, map); + x(0) = r(0); + SWAPOUT; + res = update_map(c_p, reg, map, I); + SWAPIN; + if (res) { + r(0) = x(0); + StoreResult(res, Arg(2)); + Next(5+Arg(4)); + } else { + goto lb_Cl_error; + } + } + + /* * All guards with zero arguments have special instructions: * self/0 @@ -6227,6 +6276,296 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) return make_fun(funp); } +static int has_not_map_field(Eterm map, Eterm key) +{ + map_t* mp; + Eterm* keys; + Uint i; + Uint n; + + mp = (map_t *)map_val(map); + keys = map_get_keys(mp); + n = map_get_size(mp); + if (is_immed(key)) { + for (i = 0; i < n; i++) { + if (keys[i] == key) { + return 0; + } + } + } else { + for (i = 0; i < n; i++) { + if (eq(keys[i], key)) { + return 0; + } + } + } + return 1; +} + +static Eterm get_map_element(Eterm map, Eterm key) +{ + map_t *mp; + Eterm* ks, *vs; + Uint i; + Uint n; + + mp = (map_t *)map_val(map); + ks = map_get_keys(mp); + vs = map_get_values(mp); + n = map_get_size(mp); + if (is_immed(key)) { + for (i = 0; i < n; i++) { + if (ks[i] == key) { + return vs[i]; + } + } + } else { + for (i = 0; i < n; i++) { + if (eq(ks[i], key)) { + return vs[i]; + } + } + } + return THE_NON_VALUE; +} + +#define GET_TERM(term, dest) \ +do { \ + Eterm src = term; \ + switch (src & _TAG_IMMED1_MASK) { \ + case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + dest = x(0); \ + break; \ + case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + dest = x(src >> _TAG_IMMED1_SIZE); \ + break; \ + case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + dest = y(src >> _TAG_IMMED1_SIZE); \ + break; \ + default: \ + dest = src; \ + break; \ + } \ +} while(0) + + +static Eterm +new_map(Process* p, Eterm* reg, BeamInstr* I) +{ + Uint n = Arg(3); + Uint i; + Uint need = n + 1 /* hdr */ + 1 /*size*/ + 1 /* ptr */ + 1 /* arity */; + Eterm keys; + Eterm *mhp,*thp; + Eterm *E; + Eterm *tp; + map_t *mp; + + if (HeapWordsLeft(p) < need) { + erts_garbage_collect(p, need, reg, Arg(2)); + } + + thp = p->htop; + mhp = thp + 1 + n/2; + E = p->stop; + tp = &Arg(4); + keys = make_tuple(thp); + *thp++ = make_arityval(n/2); + + mp = (map_t *)mhp; mhp += 3; + mp->thing_word = MAP_HEADER; + mp->size = n/2; + mp->keys = keys; + + for (i = 0; i < n/2; i++) { + GET_TERM(*tp++, *thp++); + GET_TERM(*tp++, *mhp++); + } + p->htop = mhp; + return make_map(mp); +} + + +/* This entire instruction will be split into two. + * 1) update_map_exact (literals) <- this can be much more optimized + * 2) update_map_assoc (literals) + * Also update_map is pretty bad code as it stands now. + */ + +static Eterm +update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) +{ + Uint n; + Uint i; + Uint num_old; + Uint num_updates; + Uint need; + map_t *old_mp, *mp; + Eterm res; + Eterm* hp; + Eterm* E; + Eterm* old_keys; + Eterm* old_vals; + Eterm* new_p; + Eterm new_key; + Eterm* kp; + + if (is_not_map(map)) { + return 0; + } + + old_mp = (map_t *) map_val(map); + num_old = map_get_size(old_mp); + + /* + * If the old map is empty, create a new map. + */ + + if (num_old == 0) { + return new_map(p, reg, I+1); + } + + /* + * Allocate heap space for the worst case (i.e. all keys are new). + */ + + num_updates = Arg(4) / 2; + need = 2*(num_old+num_updates) + 4; + if (HeapWordsLeft(p) < need) { + Uint live = Arg(3); + reg[live] = map; + erts_garbage_collect(p, need, reg, live+1); + map = reg[live]; + old_mp = (map_t *)map_val(map); + } + + /* + * Update map, optimistically assuming that there are no + * new keys, allowing us to keep the old key tuple. + */ + + hp = p->htop; + E = p->stop; + + old_vals = map_get_values(old_mp); + old_keys = map_get_keys(old_mp); + + res = make_map(hp); + mp = (map_t *)hp; hp += 3; + mp->thing_word = MAP_HEADER; + mp->size = num_old; + mp->keys = old_mp->keys; + + ASSERT(num_updates > 0); + + /* Get array of key/value pairs to be updated */ + new_p = &Arg(5); + GET_TERM(*new_p, new_key); + + n = num_updates; + + for (i = 0; i < num_old; i++) { + if (new_key == THE_NON_VALUE || !eq(*old_keys, new_key)) { + /* not same keys */ + *hp++ = *old_vals; + } else { + GET_TERM(new_p[1], *hp); + hp++; + n--; + if (n == 0) { + new_key = THE_NON_VALUE; + } else { + new_p += 2; + GET_TERM(*new_p, new_key); + } + } + old_vals++, old_keys++; + } + + /* + * If we have exhausted the update list we are done. + */ + + if (n == 0) { + p->htop = hp; + return res; + } + + /* + * There were some new keys. We'll have to start over and rebuild + * the key tuple too. + */ + + kp = p->htop; + *kp++ = make_arityval(0); + + res = make_map(hp); + mp = (map_t *)hp; hp += 3; + mp->keys = make_tuple(kp-1); + + old_vals = map_get_values(old_mp); + old_keys = map_get_keys(old_mp); + + new_p = &Arg(5); + GET_TERM(*new_p, new_key); + n = num_updates; + + for (;;) { + Eterm key; + Sint c; + + ASSERT(kp < (Eterm *)mp); + key = *old_keys; + if ((c = cmp(key, new_key)) < 0) { + *kp++ = key; + *hp++ = *old_vals; + old_keys++, old_vals++, num_old--; + } else { /* Replace or insert new */ + *kp++ = new_key; + GET_TERM(new_p[1], *hp++); + if (c == 0) { /* If replacement */ + old_keys++, old_vals++, num_old--; + } + n--; + if (n == 0) { + break; + } else { + new_p += 2; + GET_TERM(*new_p, new_key); + } + } + if (num_old == 0) { + break; + } + } + + while (n-- > 0) { + GET_TERM(new_p[0], *kp++); + GET_TERM(new_p[1], *hp++); + new_p += 2; + } + + /* + * All updates done. Now copy the remaining part of the frame's + * keys and values. + */ + + while (num_old-- > 0) { + ASSERT(kp < (Eterm *)mp); + *kp++ = *old_keys++; + *hp++ = *old_vals++; + } + if ((n = (Eterm *)mp - kp) > 0) { + *kp = make_pos_bignum_header(n-1); + } + n = kp - p->htop - 1; /* Actual number of keys/values */ + *p->htop = make_arityval(n); + mp->thing_word = MAP_HEADER; + mp->size = n; + p->htop = hp; + return res; +} +#undef GET_TERM int catchlevel(Process *p) { diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 3ec534f0bc..42515d0494 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -578,6 +578,12 @@ bif os:unsetenv/1 bif re:inspect/2 +ubif erlang:is_map/1 +bif map:to_list/1 +bif map:new/0 +bif map:get/2 +bif map:put/3 + # # Obsolete # diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 23c0fca6aa..3a987e213b 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -27,6 +27,7 @@ #include "erl_process.h" #include "erl_gc.h" #include "big.h" +#include "erl_map.h" #include "erl_binary.h" #include "erl_bits.h" #include "dtrace-wrapper.h" @@ -150,6 +151,24 @@ Uint size_object(Eterm obj) goto pop_next; } break; + case MAP_SUBTAG: + { + Uint n; + map_t *mp; + mp = (map_t*)map_val_rel(obj,base); + ptr = (Eterm *)mp; + n = map_get_size(mp) + 1; + sum += n + 2; + ptr += 2; /* hdr + size words */ + while (n--) { + obj = *ptr++; + if (!IS_CONST(obj)) { + ESTACK_PUSH(s, obj); + } + } + goto pop_next; + } + break; case BIN_MATCHSTATE_SUBTAG: erl_exit(ERTS_ABORT_EXIT, "size_object: matchstate term not allowed"); @@ -318,6 +337,15 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } } break; + case MAP_SUBTAG: + { + i = map_get_size(objp) + 3; + *argp = make_map_rel(htop, dst_base); + while (i--) { + *htop++ = *objp++; + } + } + break; case REFC_BINARY_SUBTAG: { ProcBin* pb; @@ -537,6 +565,10 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } goto off_heap_common; + case MAP_SUBTAG: + *hp++ = *tp++; + sz--; + break; case EXTERNAL_PID_SUBTAG: case EXTERNAL_PORT_SUBTAG: case EXTERNAL_REF_SUBTAG: diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c index adac0052d6..37dd6457db 100644 --- a/erts/emulator/beam/erl_bif_op.c +++ b/erts/emulator/beam/erl_bif_op.c @@ -36,6 +36,7 @@ #include "dist.h" #include "erl_version.h" #include "erl_binary.h" +#include "erl_map.h" BIF_RETTYPE and_2(BIF_ALIST_2) { @@ -321,7 +322,10 @@ BIF_RETTYPE is_record_3(BIF_ALIST_3) BIF_RET(am_false); } - - - - +BIF_RETTYPE is_map_1(BIF_ALIST_1) +{ + if (is_map(BIF_ARG_1)) { + BIF_RET(am_true); + } + BIF_RET(am_false); +} diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index a358ecf326..d903053aa4 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -35,6 +35,7 @@ #include "bif.h" #include "big.h" #include "erl_binary.h" +#include "erl_map.h" #include "erl_thr_progress.h" #include "erl_db_util.h" diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index 873a9860da..50bdc79506 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -29,6 +29,7 @@ #include "bif.h" #include "beam_catches.h" #include "erl_debug.h" +#include "erl_map.h" #define WITHIN(ptr, x, y) ((x) <= (ptr) && (ptr) < (y)) diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index ab8448e8a1..2022f70cbb 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -28,6 +28,7 @@ #include "beam_catches.h" #include "erl_binary.h" #include "erl_bits.h" +#include "erl_map.h" #include "error.h" #include "big.h" #include "erl_gc.h" diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index 1801df359a..5203dda263 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -20,6 +20,8 @@ #ifndef __ERL_GC_H__ #define __ERL_GC_H__ +#include "erl_map.h" + /* GC declarations shared by beam/erl_gc.c and hipe/hipe_gc.c */ #if defined(DEBUG) && !ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -42,23 +44,24 @@ do { \ HTOP += 2; /* update tospace htop */ \ } while(0) -#define MOVE_BOXED(PTR,HDR,HTOP,ORIG) \ -do { \ - Eterm gval; \ - Sint nelts; \ - \ - ASSERT(is_header(HDR)); \ - gval = make_boxed(HTOP); \ - *ORIG = gval; \ - *HTOP++ = HDR; \ - *PTR++ = gval; \ - nelts = header_arity(HDR); \ - switch ((HDR) & _HEADER_SUBTAG_MASK) { \ - case SUB_BINARY_SUBTAG: nelts++; break; \ - case FUN_SUBTAG: nelts+=((ErlFunThing*)(PTR-1))->num_free+1; break; \ - } \ - while (nelts--) \ - *HTOP++ = *PTR++; \ +#define MOVE_BOXED(PTR,HDR,HTOP,ORIG) \ +do { \ + Eterm gval; \ + Sint nelts; \ + \ + ASSERT(is_header(HDR)); \ + nelts = header_arity(HDR); \ + switch ((HDR) & _HEADER_SUBTAG_MASK) { \ + case SUB_BINARY_SUBTAG: nelts++; break; \ + case MAP_SUBTAG: nelts+=map_get_size(PTR) + 1; break; \ + case FUN_SUBTAG: nelts+=((ErlFunThing*)(PTR))->num_free+1; break; \ + } \ + gval = make_boxed(HTOP); \ + *ORIG = gval; \ + *HTOP++ = HDR; \ + *PTR++ = gval; \ + while (nelts--) *HTOP++ = *PTR++; \ + \ } while(0) #define in_area(ptr,start,nbytes) \ diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c new file mode 100644 index 0000000000..1fd7943c30 --- /dev/null +++ b/erts/emulator/beam/erl_map.c @@ -0,0 +1,225 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013. All Rights Reserved. + * + * The 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include "erl_vm.h" +#include "global.h" +#include "erl_process.h" +#include "error.h" +#include "bif.h" + + +#include "erl_map.h" + + +BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Uint n; + Eterm* hp; + Eterm *ks,*vs, res, tup; + map_t *mp = (map_t*)map_val(BIF_ARG_1); + + ks = map_get_keys(mp); + vs = map_get_values(mp); + n = map_get_size(mp); + hp = HAlloc(BIF_P, (2 + 3) * n); + res = NIL; + + while(n--) { + tup = TUPLE2(hp, ks[n], vs[n]); hp += 3; + res = CONS(hp, tup, res); hp += 2; + } + + BIF_RET(res); + } + + BIF_ERROR(BIF_P, BADARG); +} + +BIF_RETTYPE map_new_0(BIF_ALIST_0) { + Eterm* hp; + Eterm tup; + map_t *mp; + + hp = HAlloc(BIF_P, (3 + 1)); + tup = make_tuple(hp); + *hp++ = make_arityval(0); + + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = 0; + mp->keys = tup; + + BIF_RET(make_map(mp)); +} + +BIF_RETTYPE map_get_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp, *ks,*vs, key, error; + map_t *mp; + Uint n,i; + char *s_error; + + mp = (map_t*)map_val(BIF_ARG_2); + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) + goto error; + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + if (is_immed(key)) { + for( i = 0; i < n; i++) { + if (ks[i] == key) { + BIF_RET(vs[i]); + } + } + } + + for( i = 0; i < n; i++) { + if (eq(ks[i], key)) { + BIF_RET(vs[i]); + } + } +error: + + s_error = "bad_key"; + error = am_atom_put(s_error, sys_strlen(s_error)); + + hp = HAlloc(BIF_P, 3); + BIF_P->fvalue = TUPLE2(hp, error, key); + BIF_ERROR(BIF_P, EXC_ERROR_2); + } + BIF_ERROR(BIF_P, BADARG); +} + +BIF_RETTYPE map_put_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + Sint n,i; + Sint c = 0; + Eterm* hp, *shp; + Eterm *ks,*vs, res, key, tup; + map_t *mp = (map_t*)map_val(BIF_ARG_3); + + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) { + hp = HAlloc(BIF_P, 4 + 2); + tup = make_tuple(hp); + *hp++ = make_arityval(1); + *hp++ = key; + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = 1; + *hp++ = tup; + *hp++ = BIF_ARG_2; + + BIF_RET(res); + } + + ks = map_get_keys(mp); + vs = map_get_values(mp); + /* only allocate for values, + * assume key-tuple will be intact + */ + + hp = HAlloc(BIF_P, 3 + n); + shp = hp; /* save hp, used if optimistic update fails */ + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n; + *hp++ = mp->keys; + + if (is_immed(key)) { + for( i = 0; i < n; i ++) { + if (ks[i] == key) { + *hp++ = BIF_ARG_2; + vs++; + c = 1; + } else { + *hp++ = *vs++; + } + } + } else { + for( i = 0; i < n; i ++) { + if (eq(ks[i], key)) { + *hp++ = BIF_ARG_2; + vs++; + c = 1; + } else { + *hp++ = *vs++; + } + } + } + + if (c) + BIF_RET(res); + + /* need to make a new tuple, + * use old hp since it needs to be recreated anyway. + */ + tup = make_tuple(shp); + *shp++ = make_arityval(n+1); + + hp = HAlloc(BIF_P, 3 + n + 1); + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n + 1; + *hp++ = tup; + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + ASSERT(n >= 0); + + /* copy map in order */ + while (n && ((c = CMP(*ks, key)) < 0)) { + *shp++ = *ks++; + *hp++ = *vs++; + n--; + } + + *shp++ = key; + *hp++ = BIF_ARG_2; + + ASSERT(n >= 0); + + while(n--) { + *shp++ = *ks++; + *hp++ = *vs++; + } + /* we have one word remaining + * this will work out fine once we get the size word + * in the header. + */ + *shp = make_pos_bignum_header(0); + BIF_RET(res); + } + + BIF_ERROR(BIF_P, BADARG); +} diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h new file mode 100644 index 0000000000..ca8ce0f974 --- /dev/null +++ b/erts/emulator/beam/erl_map.h @@ -0,0 +1,64 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013. All Rights Reserved. + * + * The 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_MAP_H__ +#define __ERL_MAP_H__ + +#include "sys.h" +/* MAP */ + +typedef struct map_s { + Eterm thing_word; + Uint size; + Eterm keys; /* tuple */ +} map_t; +/* map node + * + * ----------- + * Eterm THING + * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = arity + * ---- + * Eterm V1 + * ... + * Eterm Vn, where n = arity + * ----------- + */ + + + +/* erl_term.h stuff */ +#define make_map(x) make_boxed((Eterm*)(x)) +#define make_map_rel(x, BASE) make_boxed_rel((Eterm*)(x),(BASE)) +#define is_map(x) (is_boxed((x)) && is_map_header(*boxed_val((x)))) +#define is_map_rel(RTERM,BASE) is_map(rterm2wterm(RTERM,BASE)) +#define is_not_map(x) (!is_map((x))) +#define is_map_header(x) (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_MAP) +#define header_is_map(x) ((((x) & (_HEADER_SUBTAG_MASK)) == MAP_SUBTAG)) +#define map_val(x) (_unchecked_boxed_val((x))) +#define map_val_rel(RTERM, BASE) map_val(rterm2wterm(RTERM, BASE)) + +#define map_get_values(x) (((Eterm *)(x)) + 3) +#define map_get_keys(x) (((Eterm *)tuple_val(((map_t *)(x))->keys)) + 1) +#define map_get_size(x) (((map_t*)(x))->size) + +#define MAP_HEADER _make_header(1,_TAG_HEADER_MAP) + +#endif + diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 436147749e..d18760dc43 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -24,6 +24,7 @@ #include "erl_printf_term.h" #include "sys.h" #include "big.h" +#include "erl_map.h" #define PRINT_CHAR(CNT, FN, ARG, C) \ do { \ @@ -216,14 +217,15 @@ static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount) } -#define PRT_BAR ((Eterm) 0) -#define PRT_COMMA ((Eterm) 1) -#define PRT_CLOSE_LIST ((Eterm) 2) -#define PRT_CLOSE_TUPLE ((Eterm) 3) -#define PRT_TERM ((Eterm) 4) -#define PRT_ONE_CONS ((Eterm) 5) -#define PRT_PATCH_FUN_SIZE ((Eterm) 6) -#define PRT_LAST_ARRAY_ELEMENT ((Eterm) 7) /* Note! Must be last... */ +#define PRT_BAR ((Eterm) 0) +#define PRT_COMMA ((Eterm) 1) +#define PRT_CLOSE_LIST ((Eterm) 2) +#define PRT_CLOSE_TUPLE ((Eterm) 3) +#define PRT_ASSOC ((Eterm) 4) +#define PRT_TERM ((Eterm) 5) +#define PRT_ONE_CONS ((Eterm) 6) +#define PRT_PATCH_FUN_SIZE ((Eterm) 7) +#define PRT_LAST_ARRAY_ELEMENT ((Eterm) 8) /* Note! Must be last... */ static int print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, @@ -260,6 +262,9 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, case PRT_CLOSE_TUPLE: PRINT_CHAR(res, fn, arg, '}'); goto L_outer_loop; + case PRT_ASSOC: + PRINT_STRING(res, fn, arg, "=>"); + goto L_outer_loop; default: popped.word = WSTACK_POP(s); @@ -483,6 +488,37 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, PRINT_CHAR(res, fn, arg, '>'); } break; + case MAP_DEF: + { + Uint n; + Eterm *ks, *vs; + map_t *mp = (map_t *)map_val(wobj); + n = map_get_size(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); + + PRINT_CHAR(res, fn, arg, '#'); + PRINT_CHAR(res, fn, arg, '{'); + WSTACK_PUSH(s, PRT_CLOSE_TUPLE); + if (n > 0) { + n--; + WSTACK_PUSH(s, vs[n]); + WSTACK_PUSH(s, PRT_TERM); + WSTACK_PUSH(s, PRT_ASSOC); + WSTACK_PUSH(s, ks[n]); + WSTACK_PUSH(s, PRT_TERM); + + while (n--) { + WSTACK_PUSH(s, PRT_COMMA); + WSTACK_PUSH(s, vs[n]); + WSTACK_PUSH(s, PRT_TERM); + WSTACK_PUSH(s, PRT_ASSOC); + WSTACK_PUSH(s, ks[n]); + WSTACK_PUSH(s, PRT_TERM); + } + } + } + break; default: PRINT_STRING(res, fn, arg, " #include @@ -85,7 +86,10 @@ unsigned tag_val_def(Wterm x) case (_TAG_HEADER_EXTERNAL_PID >> _TAG_PRIMARY_SIZE): return EXTERNAL_PID_DEF; case (_TAG_HEADER_EXTERNAL_PORT >> _TAG_PRIMARY_SIZE): return EXTERNAL_PORT_DEF; case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE): return EXTERNAL_REF_DEF; - default: return BINARY_DEF; + case (_TAG_HEADER_REFC_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_HEAP_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_SUB_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): return MAP_DEF; } break; } diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 50d3e63c58..f10a3a9d38 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -135,11 +135,12 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #define REF_SUBTAG (0x4 << _TAG_PRIMARY_SIZE) /* REF */ #define FUN_SUBTAG (0x5 << _TAG_PRIMARY_SIZE) /* FUN */ #define FLOAT_SUBTAG (0x6 << _TAG_PRIMARY_SIZE) /* FLOAT */ -#define EXPORT_SUBTAG (0x7 << _TAG_PRIMARY_SIZE) /* FLOAT */ +#define EXPORT_SUBTAG (0x7 << _TAG_PRIMARY_SIZE) /* FLOAT */ #define _BINARY_XXX_MASK (0x3 << _TAG_PRIMARY_SIZE) #define REFC_BINARY_SUBTAG (0x8 << _TAG_PRIMARY_SIZE) /* BINARY */ #define HEAP_BINARY_SUBTAG (0x9 << _TAG_PRIMARY_SIZE) /* BINARY */ #define SUB_BINARY_SUBTAG (0xA << _TAG_PRIMARY_SIZE) /* BINARY */ +#define MAP_SUBTAG (0xB << _TAG_PRIMARY_SIZE) /* MAP */ #define EXTERNAL_PID_SUBTAG (0xC << _TAG_PRIMARY_SIZE) /* EXTERNAL_PID */ #define EXTERNAL_PORT_SUBTAG (0xD << _TAG_PRIMARY_SIZE) /* EXTERNAL_PORT */ #define EXTERNAL_REF_SUBTAG (0xE << _TAG_PRIMARY_SIZE) /* EXTERNAL_REF */ @@ -155,6 +156,7 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #define _TAG_HEADER_REFC_BIN (TAG_PRIMARY_HEADER|REFC_BINARY_SUBTAG) #define _TAG_HEADER_HEAP_BIN (TAG_PRIMARY_HEADER|HEAP_BINARY_SUBTAG) #define _TAG_HEADER_SUB_BIN (TAG_PRIMARY_HEADER|SUB_BINARY_SUBTAG) +#define _TAG_HEADER_MAP (TAG_PRIMARY_HEADER|MAP_SUBTAG) #define _TAG_HEADER_EXTERNAL_PID (TAG_PRIMARY_HEADER|EXTERNAL_PID_SUBTAG) #define _TAG_HEADER_EXTERNAL_PORT (TAG_PRIMARY_HEADER|EXTERNAL_PORT_SUBTAG) #define _TAG_HEADER_EXTERNAL_REF (TAG_PRIMARY_HEADER|EXTERNAL_REF_SUBTAG) @@ -354,7 +356,10 @@ _ET_DECLARE_CHECKED(Uint,thing_subtag,Eterm) #define is_value(x) ((x) != THE_NON_VALUE) /* binary object access methods */ -#define is_binary_header(x) (((x) & (_TAG_HEADER_MASK-_BINARY_XXX_MASK)) == _TAG_HEADER_REFC_BIN) +#define is_binary_header(x) \ + ((((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_REFC_BIN) || \ + (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_HEAP_BIN) || \ + (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_SUB_BIN)) #define make_binary(x) make_boxed((Eterm*)(x)) #define is_binary(x) (is_boxed((x)) && is_binary_header(*boxed_val((x)))) #define is_not_binary(x) (!is_binary((x))) @@ -1064,8 +1069,8 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint) /* * Backwards compatibility definitions: - * - #define virtal *_DEF constants with values that fit term order: - * number < atom < ref < fun < port < pid < tuple < nil < cons < binary + * - #define virtual *_DEF constants with values that fit term order: + * number < atom < ref < fun < port < pid < tuple < map < nil < cons < binary * - tag_val_def() function generates virtual _DEF tag * - not_eq_tags() and NUMBER_CODE() defined in terms * of the tag_val_def() function @@ -1074,19 +1079,20 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint) #define BINARY_DEF 0x0 #define LIST_DEF 0x1 #define NIL_DEF 0x2 -#define TUPLE_DEF 0x3 -#define PID_DEF 0x4 -#define EXTERNAL_PID_DEF 0x5 -#define PORT_DEF 0x6 -#define EXTERNAL_PORT_DEF 0x7 -#define EXPORT_DEF 0x8 -#define FUN_DEF 0x9 -#define REF_DEF 0xa -#define EXTERNAL_REF_DEF 0xb -#define ATOM_DEF 0xc -#define FLOAT_DEF 0xd -#define BIG_DEF 0xe -#define SMALL_DEF 0xf +#define MAP_DEF 0x3 +#define TUPLE_DEF 0x4 +#define PID_DEF 0x5 +#define EXTERNAL_PID_DEF 0x6 +#define PORT_DEF 0x7 +#define EXTERNAL_PORT_DEF 0x8 +#define EXPORT_DEF 0x9 +#define FUN_DEF 0xa +#define REF_DEF 0xb +#define EXTERNAL_REF_DEF 0xc +#define ATOM_DEF 0xd +#define FLOAT_DEF 0xe +#define BIG_DEF 0xf +#define SMALL_DEF 0x10 #if ET_DEBUG extern unsigned tag_val_def_debug(Wterm, const char*, unsigned); @@ -1096,8 +1102,8 @@ extern unsigned tag_val_def(Wterm); #endif #define not_eq_tags(X,Y) (tag_val_def((X)) ^ tag_val_def((Y))) -#define NUMBER_CODE(x,y) ((tag_val_def(x) << 4) | tag_val_def(y)) -#define _NUMBER_CODE(TX,TY) ((TX << 4) | TY) +#define NUMBER_CODE(x,y) ((tag_val_def(x) << 5) | tag_val_def(y)) +#define _NUMBER_CODE(TX,TY) ((TX << 5) | TY) #define SMALL_SMALL _NUMBER_CODE(SMALL_DEF,SMALL_DEF) #define SMALL_BIG _NUMBER_CODE(SMALL_DEF,BIG_DEF) #define SMALL_FLOAT _NUMBER_CODE(SMALL_DEF,FLOAT_DEF) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index e0776cf67d..6cdfd1c989 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -31,6 +31,7 @@ #include "bif.h" #include "erl_binary.h" #include "erl_bits.h" +#include "erl_map.h" #include "packet_parser.h" #include "erl_gc.h" #define ERTS_WANT_DB_INTERNAL__ @@ -785,10 +786,10 @@ Uint32 make_hash(Eterm term_arg) unsigned op; /* Must not collide with the real tag_val_def's: */ -#define MAKE_HASH_TUPLE_OP 0x10 -#define MAKE_HASH_FUN_OP 0x11 -#define MAKE_HASH_CDR_PRE_OP 0x12 -#define MAKE_HASH_CDR_POST_OP 0x13 +#define MAKE_HASH_TUPLE_OP 0x11 +#define MAKE_HASH_FUN_OP 0x12 +#define MAKE_HASH_CDR_PRE_OP 0x13 +#define MAKE_HASH_CDR_POST_OP 0x14 /* ** Convenience macro for calculating a bytewise hash on an unsigned 32 bit @@ -2007,6 +2008,18 @@ tailrecur_ne: ++bb; goto term_array; } + case MAP_SUBTAG: + { + aa = map_val_rel(a, a_base); + if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa) + goto not_equal; + bb = map_val_rel(b,b_base); + if ((sz = map_get_size((map_t*)aa)) == 0) goto pop_next; + aa += 2; + bb += 2; + sz += 1; /* increment for tuple-keys */ + goto term_array; + } case REFC_BINARY_SUBTAG: case HEAP_BINARY_SUBTAG: case SUB_BINARY_SUBTAG: @@ -2281,7 +2294,7 @@ static int cmpbytes(byte *s1, int l1, byte *s2, int l2) * * According to the Erlang Standard, types are orderered as follows: * numbers < (characters) < atoms < refs < funs < ports < pids < - * tuples < [] < conses < binaries. + * tuples < maps < [] < conses < binaries. * * Note that characters are currently not implemented. * @@ -2464,7 +2477,25 @@ tailrecur_ne: ++aa; ++bb; goto term_array; + case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE) : + if (!is_map_rel(b,b_base)) { + a_tag = MAP_DEF; + goto mixed_types; + } + aa = (Eterm *)map_val_rel(a,a_base); + bb = (Eterm *)map_val_rel(b,b_base); + i = map_get_size((map_t*)aa); + if (i != map_get_size((map_t*)bb)) { + RETURN_NEQ((int)(i - map_get_size((map_t*)bb))); + } + if (i == 0) { + goto pop_next; + } + aa += 2; + bb += 2; + i += 1; /* increment for tuple-keys */ + goto term_array; case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): if (!is_float_rel(b,b_base)) { a_tag = FLOAT_DEF; diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c index e09988e2c5..c3687681cf 100644 --- a/erts/emulator/hipe/hipe_bif2.c +++ b/erts/emulator/hipe/hipe_bif2.c @@ -31,6 +31,7 @@ #include "erl_process.h" #include "bif.h" #include "big.h" +#include "erl_map.h" #include "hipe_debug.h" #include "hipe_mode_switch.h" #include "hipe_arch.h" diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c index bf25ba82af..32694a8f97 100644 --- a/erts/emulator/hipe/hipe_debug.c +++ b/erts/emulator/hipe/hipe_debug.c @@ -36,6 +36,7 @@ #include "beam_load.h" #include "hipe_mode_switch.h" #include "hipe_debug.h" +#include "erl_map.h" static const char dashes[2*sizeof(long)+5] = { [0 ... 2*sizeof(long)+3] = '-' -- cgit v1.2.3 From 92303a2e1abdf74aa3bc3af095131a59a601c45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Oct 2013 19:27:54 +0200 Subject: erts: Add phash2 Map functionality --- erts/emulator/beam/utils.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 6cdfd1c989..bc7e91295d 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1088,6 +1088,7 @@ make_hash2(Eterm term) #define HCONST_13 0x08d12e65UL #define HCONST_14 0xa708a81eUL #define HCONST_15 0x454021d7UL +#define HCONST_16 0xe3779b90UL #define UINT32_HASH_2(Expr1, Expr2, AConst) \ do { \ @@ -1190,6 +1191,28 @@ make_hash2(Eterm term) term = elem[1]; } break; + case MAP_SUBTAG: + { + map_t *mp = (map_t *)map_val(term); + int i; + int size = map_get_size(mp); + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + UINT32_HASH(size, HCONST_16); + if (size == 0) /* Empty Map */ + goto hash2_common; + + for (i = size - 1; i >= 0; i--) { + tmp = vs[i]; + ESTACK_PUSH(s, tmp); + } + for (i = size - 1; i >= 1; i--) { + tmp = ks[i]; + ESTACK_PUSH(s, tmp); + } + term = ks[0]; + } + break; case EXPORT_SUBTAG: { Export* ep = *((Export **) (export_val(term) + 1)); -- cgit v1.2.3 From 63ef0bbfdfb70673fe7f3ce2fc6fa4f0f801747d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 19 Sep 2013 17:48:28 +0200 Subject: erts: Add the size-testing guard BIF map_size/1 --- erts/emulator/beam/beam_emu.c | 2 ++ erts/emulator/beam/beam_load.c | 2 ++ erts/emulator/beam/bif.tab | 1 + erts/emulator/beam/erl_bif_guard.c | 23 +++++++++++++++++++++++ erts/emulator/beam/erl_map.c | 23 +++++++++++++++++++++-- erts/emulator/beam/global.h | 1 + 6 files changed, 50 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 5c955b9566..1cad31be2c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5080,6 +5080,8 @@ translate_gc_bif(void* gcf) return bit_size_1; } else if (gcf == erts_gc_byte_size_1) { return byte_size_1; + } else if (gcf == erts_gc_map_size_1) { + return map_size_1; } else if (gcf == erts_gc_abs_1) { return abs_1; } else if (gcf == erts_gc_float_1) { diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 58207ec75b..b589d1c930 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -3783,6 +3783,8 @@ gen_guard_bif1(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, op->a[1].val = (BeamInstr) (void *) erts_gc_bit_size_1; } else if (bf == byte_size_1) { op->a[1].val = (BeamInstr) (void *) erts_gc_byte_size_1; + } else if (bf == map_size_1) { + op->a[1].val = (BeamInstr) (void *) erts_gc_map_size_1; } else if (bf == abs_1) { op->a[1].val = (BeamInstr) (void *) erts_gc_abs_1; } else if (bf == float_1) { diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 42515d0494..a8623fcf61 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -579,6 +579,7 @@ bif os:unsetenv/1 bif re:inspect/2 ubif erlang:is_map/1 +ubif erlang:map_size/1 bif map:to_list/1 bif map:new/0 bif map:get/2 diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c index a715756c15..bbd8aa31d9 100644 --- a/erts/emulator/beam/erl_bif_guard.c +++ b/erts/emulator/beam/erl_bif_guard.c @@ -33,6 +33,7 @@ #include "bif.h" #include "big.h" #include "erl_binary.h" +#include "erl_map.h" static Eterm gc_double_to_integer(Process* p, double x, Eterm* reg, Uint live); @@ -455,6 +456,28 @@ Eterm erts_gc_byte_size_1(Process* p, Eterm* reg, Uint live) } } +Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live) +{ + Eterm arg = reg[live]; + if (is_map(arg)) { + map_t *mp = (map_t*)map_val(arg); + Uint size = map_get_size(mp); + if (IS_USMALL(0, size)) { + return make_small(size); + } else { + Eterm* hp; + if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) { + erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live); + } + hp = p->htop; + p->htop += BIG_UINT_HEAP_SIZE; + return uint_to_big(size, hp); + } + } else { + BIF_ERROR(p, BADARG); + } +} + Eterm erts_gc_abs_1(Process* p, Eterm* reg, Uint live) { Eterm arg; diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 1fd7943c30..6a59fa29b8 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -30,10 +30,8 @@ #include "error.h" #include "bif.h" - #include "erl_map.h" - BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Uint n; @@ -58,6 +56,27 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } + +/* erlang:map_size/1 + * the corresponding instruction is implemented in: + * beam/erl_bif_guard.c + */ + +BIF_RETTYPE map_size_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp; + Uint hsz = 0; + map_t *mp = (map_t*)map_val(BIF_ARG_1); + Uint n = map_get_size(mp); + + erts_bld_uint(NULL, &hsz, n); + hp = HAlloc(BIF_P, hsz); + BIF_RET(erts_bld_uint(&hp, NULL, n)); + } + + BIF_ERROR(BIF_P, BADARG); +} + BIF_RETTYPE map_new_0(BIF_ALIST_0) { Eterm* hp; Eterm tup; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 83a8911a36..8fcb95d0e2 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -998,6 +998,7 @@ Eterm erts_gc_length_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_size_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_bit_size_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_byte_size_1(Process* p, Eterm* reg, Uint live); +Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_abs_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_float_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_round_1(Process* p, Eterm* reg, Uint live); -- cgit v1.2.3 From 43ed0cc716039c3b2f65a5395f00424169639309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 21 May 2013 18:11:39 +0200 Subject: erts: Add the type-testing guard BIF is_map/1 To add a type-testing guard BIF, the following steps are needed: * The BIF itself is added to bif.tab (note that it should be declared using "ubif", not "bif"), and its implementation to erl_bif_op.c. * erl_internal must be modified in 3 places: The type test must be recognized as guard BIF, as a type test, and it must be auto-imported. * There must be an instruction that implements the same type test as the BIF (it will be used in guards). beam_utils:bif_to_test/3 must be updated to recognize the new guard BIF. --- erts/emulator/beam/bif.tab | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index a8623fcf61..8f85f931c9 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -572,6 +572,7 @@ bif erlang:binary_to_float/1 bif io:printable_range/0 bif os:unsetenv/1 + # # New in R17A # -- cgit v1.2.3 From bc0960c095e9482ba0f452c9df3623f5c9c54e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 30 Sep 2013 20:13:07 +0200 Subject: erts: Introduce more Maps BIFs * map:remove/2 * map:keys/1 * map:values/1 * map:is_key/2 * map:update/3 - Equivalent to ':=' operator in #{ K := V } maps. * map:from_list/1 - map:from_list/1 takes any unsorted key/value list, [{K,V}], and produces a map. Duplicate keys are removed. The latest key is kept. * map:find/2 - Searches for a pair that *equals* input key. * map:merge/2 - Merge two maps to one map. --- erts/emulator/beam/bif.tab | 11 +- erts/emulator/beam/erl_map.c | 565 ++++++++++++++++++++++++++++++++++++++++--- erts/emulator/beam/erl_map.h | 6 +- 3 files changed, 547 insertions(+), 35 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 8f85f931c9..306861a4c5 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -572,7 +572,6 @@ bif erlang:binary_to_float/1 bif io:printable_range/0 bif os:unsetenv/1 - # # New in R17A # @@ -582,9 +581,17 @@ bif re:inspect/2 ubif erlang:is_map/1 ubif erlang:map_size/1 bif map:to_list/1 -bif map:new/0 +bif map:find/2 bif map:get/2 +bif map:from_list/1 +bif map:is_key/2 +bif map:keys/1 +bif map:merge/2 +bif map:new/0 bif map:put/3 +bif map:remove/2 +bif map:update/3 +bif map:values/1 # # Obsolete diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 6a59fa29b8..6fd60f0689 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -32,6 +32,57 @@ #include "erl_map.h" +/* BIFs + * + * DONE: + * - erlang:is_map/1 + * - erlang:map_size/1 + * + * - map:find/2 + * - map:from_list/1 + * - map:get/2 + * - map:is_key/2 + * - map:keys/1 + * - map:merge/2 + * - map:new/0 + * - map:put/3 + * - map:remove/2 + * - map:to_list/1 + * - map:update/3 + * - map:values/1 + * + * TODO: + * - map:foldl/3 + * - map:foldr/3 + * - map:map/3 + * - map:size/1 + * - map:without/2 + * + */ + +/* erlang:map_size/1 + * the corresponding instruction is implemented in: + * beam/erl_bif_guard.c + */ + +BIF_RETTYPE map_size_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp; + Uint hsz = 0; + map_t *mp = (map_t*)map_val(BIF_ARG_1); + Uint n = map_get_size(mp); + + erts_bld_uint(NULL, &hsz, n); + hp = HAlloc(BIF_P, hsz); + BIF_RET(erts_bld_uint(&hp, NULL, n)); + } + + BIF_ERROR(BIF_P, BADARG); +} + +/* map:to_list/1 + */ + BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Uint n; @@ -56,43 +107,40 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } - -/* erlang:map_size/1 - * the corresponding instruction is implemented in: - * beam/erl_bif_guard.c +/* map:find/2 + * return value if key *equals* a key in the map */ -BIF_RETTYPE map_size_1(BIF_ALIST_1) { - if (is_map(BIF_ARG_1)) { - Eterm *hp; - Uint hsz = 0; - map_t *mp = (map_t*)map_val(BIF_ARG_1); - Uint n = map_get_size(mp); +BIF_RETTYPE map_find_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp, *ks,*vs, key, res; + map_t *mp; + Uint n,i; - erts_bld_uint(NULL, &hsz, n); - hp = HAlloc(BIF_P, hsz); - BIF_RET(erts_bld_uint(&hp, NULL, n)); - } + mp = (map_t*)map_val(BIF_ARG_2); + key = BIF_ARG_1; + n = map_get_size(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); + for( i = 0; i < n; i++) { + if (CMP(ks[i], key)==0) { + hp = HAlloc(BIF_P, 3); + res = make_tuple(hp); + *hp++ = make_arityval(2); + *hp++ = am_ok; + *hp++ = vs[i]; + BIF_RET(res); + } + } + BIF_RET(am_error); + } BIF_ERROR(BIF_P, BADARG); } - -BIF_RETTYPE map_new_0(BIF_ALIST_0) { - Eterm* hp; - Eterm tup; - map_t *mp; - - hp = HAlloc(BIF_P, (3 + 1)); - tup = make_tuple(hp); - *hp++ = make_arityval(0); - - mp = (map_t*)hp; - mp->thing_word = MAP_HEADER; - mp->size = 0; - mp->keys = tup; - - BIF_RET(make_map(mp)); -} +/* map:get/2 + * return value if key *matches* a key in the map + * exception bad_key if none matches + */ BIF_RETTYPE map_get_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { @@ -136,6 +184,297 @@ error: BIF_ERROR(BIF_P, BADARG); } +/* map:from_list/1 + * List may be unsorted [{K,V}] + */ + +BIF_RETTYPE map_from_list_1(BIF_ALIST_1) { + Eterm *kv, item = BIF_ARG_1; + Eterm *hp, *thp,*vs, *ks, keys, res; + map_t *mp; + Uint size = 0, unused_size = 0; + Sint c = 0; + Sint idx = 0; + + if (is_list(item) || is_nil(item)) { + + /* Calculate size and check validity */ + + while(is_list(item)) { + res = CAR(list_val(item)); + if (is_not_tuple(res)) + goto error; + + kv = tuple_val(res); + if (*kv != make_arityval(2)) + goto error; + + size++; + item = CDR(list_val(item)); + } + + if (is_not_nil(item)) + goto error; + + hp = HAlloc(BIF_P, 3 + 1 + (2 * size)); + thp = hp; + keys = make_tuple(hp); + *hp++ = make_arityval(size); + ks = hp; + hp += size; + mp = (map_t*)hp; + res = make_map(mp); + hp += MAP_HEADER_SIZE; + vs = hp; + + mp->thing_word = MAP_HEADER; + mp->size = size; /* set later, might shrink*/ + mp->keys = keys; + + if (size == 0) + BIF_RET(res); + + item = BIF_ARG_1; + + /* first entry */ + kv = tuple_val(CAR(list_val(item))); + ks[0] = kv[1]; + vs[0] = kv[2]; + size = 1; + item = CDR(list_val(item)); + + /* insert sort key/value pairs */ + while(is_list(item)) { + + kv = tuple_val(CAR(list_val(item))); + + /* compare ks backwards + * idx represent word index to be written (hole position). + * We cannot copy the elements when searching since we might + * have an equal key. So we search for just the index first =( + * + * It is perhaps faster to move the values in the first pass. + * Check for uniqueness during insert phase and then have a + * second phace compacting the map if duplicates are found + * during insert. .. or do someother sort .. shell-sort perhaps. + */ + + idx = size; + + while(idx > 0 && (c = CMP(kv[1],ks[idx-1])) < 0) { idx--; } + + if (c == 0) { + /* last compare was equal, + * i.e. we have to release memory + * and overwrite that key/value + */ + ks[idx-1] = kv[1]; + vs[idx-1] = kv[2]; + unused_size++; + } else { + Uint i = size; + while(i > idx) { + ks[i] = ks[i-1]; + vs[i] = vs[i-1]; + i--; + } + ks[idx] = kv[1]; + vs[idx] = kv[2]; + size++; + } + item = CDR(list_val(item)); + } + + if (unused_size) { + /* the key tuple is embedded in the heap + * write a bignum to clear it. + */ + /* release values as normal since they are on the top of the heap */ + + ks[size] = make_pos_bignum_header(unused_size - 1); + HRelease(BIF_P, vs + size + unused_size, vs + size); + } + + *thp = make_arityval(size); + mp->size = size; + BIF_RET(res); + } + +error: + + BIF_ERROR(BIF_P, BADARG); +} + +/* map:is_key/2 + */ + +BIF_RETTYPE map_is_key_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *ks, key; + map_t *mp; + Uint n,i; + + mp = (map_t*)map_val(BIF_ARG_2); + key = BIF_ARG_1; + n = map_get_size(mp); + ks = map_get_keys(mp); + + if (n == 0) + BIF_RET(am_false); + + if (is_immed(key)) { + for( i = 0; i < n; i++) { + if (ks[i] == key) { + BIF_RET(am_true); + } + } + } + + for( i = 0; i < n; i++) { + if (eq(ks[i], key)) { + BIF_RET(am_true); + } + } + BIF_RET(am_false); + } + BIF_ERROR(BIF_P, BADARG); +} + +/* map:keys/1 + */ + +BIF_RETTYPE map_keys_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp, *ks, res = NIL; + map_t *mp; + Uint n; + + mp = (map_t*)map_val(BIF_ARG_1); + n = map_get_size(mp); + + if (n == 0) + BIF_RET(res); + + hp = HAlloc(BIF_P, (2 * n)); + ks = map_get_keys(mp); + + while(n--) { + res = CONS(hp, ks[n], res); hp += 2; + } + + BIF_RET(res); + } + BIF_ERROR(BIF_P, BADARG); +} +/* map:merge/2 + */ + +BIF_RETTYPE map_merge_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_1) && is_map(BIF_ARG_2)) { + Eterm *hp,*thp; + Eterm tup; + Eterm *ks,*vs,*ks1,*vs1,*ks2,*vs2; + map_t *mp1,*mp2,*mp_new; + Uint n1,n2,i1,i2,need,unused_size=0; + int c = 0; + + mp1 = (map_t*)map_val(BIF_ARG_1); + mp2 = (map_t*)map_val(BIF_ARG_2); + n1 = map_get_size(mp1); + n2 = map_get_size(mp2); + + need = MAP_HEADER_SIZE + 1 + 2*(n1 + n2); + + hp = HAlloc(BIF_P, need); + thp = hp; + tup = make_tuple(thp); + ks = hp + 1; hp += 1 + n1 + n2; + mp_new = (map_t*)hp; hp += MAP_HEADER_SIZE; + vs = hp; hp += n1 + n2; + + mp_new->thing_word = MAP_HEADER; + mp_new->size = 0; + mp_new->keys = tup; + + i1 = 0; i2 = 0; + ks1 = map_get_keys(mp1); + vs1 = map_get_values(mp1); + ks2 = map_get_keys(mp2); + vs2 = map_get_values(mp2); + + while(i1 < n1 && i2 < n2) { + c = CMP(ks1[i1],ks2[i2]); + if ( c == 0) { + /* use righthand side arguments map value, + * but advance both maps */ + *ks++ = ks2[i2]; + *vs++ = vs2[i2]; + i1++, i2++, unused_size++; + } else if ( c < 0) { + *ks++ = ks1[i1]; + *vs++ = vs1[i1]; + i1++; + } else { + *ks++ = ks2[i2]; + *vs++ = vs2[i2]; + i2++; + } + } + + /* copy remaining */ + while (i1 < n1) { + *ks++ = ks1[i1]; + *vs++ = vs1[i1]; + i1++; + } + + while (i2 < n2) { + *ks++ = ks2[i2]; + *vs++ = vs2[i2]; + i2++; + } + + if (unused_size) { + /* the key tuple is embedded in the heap, write a bignum to clear it. + * + * release values as normal since they are on the top of the heap + * size = n1 + n1 - unused_size + */ + + *ks = make_pos_bignum_header(unused_size - 1); + HRelease(BIF_P, vs + unused_size, vs); + } + + mp_new->size = n1 + n2 - unused_size; + *thp = make_arityval(n1 + n2 - unused_size); + + BIF_RET(make_map(mp_new)); + } + BIF_ERROR(BIF_P, BADARG); +} +/* map:new/2 + */ + +BIF_RETTYPE map_new_0(BIF_ALIST_0) { + Eterm* hp; + Eterm tup; + map_t *mp; + + hp = HAlloc(BIF_P, (3 + 1)); + tup = make_tuple(hp); + *hp++ = make_arityval(0); + + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = 0; + mp->keys = tup; + + BIF_RET(make_map(mp)); +} + +/* map:put/3 + */ + BIF_RETTYPE map_put_3(BIF_ALIST_3) { if (is_map(BIF_ARG_3)) { Sint n,i; @@ -242,3 +581,167 @@ BIF_RETTYPE map_put_3(BIF_ALIST_3) { BIF_ERROR(BIF_P, BADARG); } + +/* map:remove/3 + */ + +BIF_RETTYPE map_remove_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Sint n; + Sint found = 0; + Uint need; + Eterm *thp, *mhp; + Eterm *ks, *vs, res, key,tup; + map_t *mp = (map_t*)map_val(BIF_ARG_2); + + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) + BIF_RET(BIF_ARG_2); + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + /* Assume key exists. + * Release allocated if it didn't. + * Allocate key tuple first. + */ + + need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ + thp = HAlloc(BIF_P, need); + mhp = thp + n; /* offset with tuple heap size */ + + tup = make_tuple(thp); + *thp++ = make_arityval(n - 1); + + res = make_map(mhp); + *mhp++ = MAP_HEADER; + *mhp++ = n - 1; + *mhp++ = tup; + + if (is_immed(key)) { + while(n--) { + if (*ks == key) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; + } + } + } else { + while(n--) { + if (eq(*ks, key)) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; + } + } + } + + if (found) + BIF_RET(res); + + /* Not found, remove allocated memory + * and return previous map. + */ + HRelease(BIF_P, thp + need, thp); + BIF_RET(BIF_ARG_2); + } + BIF_ERROR(BIF_P, BADARG); +} + +/* map:update/3 + */ + +BIF_RETTYPE map_update_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + Sint n,i; + Sint found = 0; + Eterm* hp,*shp; + Eterm *ks,*vs, res, key; + map_t *mp = (map_t*)map_val(BIF_ARG_3); + + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) { + BIF_ERROR(BIF_P, BADARG); + } + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + /* only allocate for values, + * assume key-tuple will be intact + */ + + hp = HAlloc(BIF_P, 3 + n); + shp = hp; + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n; + *hp++ = mp->keys; + + if (is_immed(key)) { + for( i = 0; i < n; i ++) { + if (ks[i] == key) { + *hp++ = BIF_ARG_2; + vs++; + found = 1; + } else { + *hp++ = *vs++; + } + } + } else { + for( i = 0; i < n; i ++) { + if (eq(ks[i], key)) { + *hp++ = BIF_ARG_2; + vs++; + found = 1; + } else { + *hp++ = *vs++; + } + } + } + + if (found) + BIF_RET(res); + + HRelease(BIF_P, shp + 3 + n, shp); + } + BIF_ERROR(BIF_P, BADARG); +} + + +/* map:values/1 + */ + +BIF_RETTYPE map_values_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp, *vs, res = NIL; + map_t *mp; + Uint n; + + mp = (map_t*)map_val(BIF_ARG_1); + n = map_get_size(mp); + + if (n == 0) + BIF_RET(res); + + hp = HAlloc(BIF_P, (2 * n)); + vs = map_get_values(mp); + + while(n--) { + res = CONS(hp, vs[n], res); hp += 2; + } + + BIF_RET(res); + } + BIF_ERROR(BIF_P, BADARG); +} diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index ca8ce0f974..4f0d26e100 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -33,11 +33,12 @@ typedef struct map_s { * * ----------- * Eterm THING - * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = arity + * Uint size + * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = size * ---- * Eterm V1 * ... - * Eterm Vn, where n = arity + * Eterm Vn, where n = size * ----------- */ @@ -59,6 +60,7 @@ typedef struct map_s { #define map_get_size(x) (((map_t*)(x))->size) #define MAP_HEADER _make_header(1,_TAG_HEADER_MAP) +#define MAP_HEADER_SIZE (sizeof(map_t) / sizeof(Eterm)) #endif -- cgit v1.2.3 From 60e7d2695dcf8ac2484db87c649ca69f1d0b763c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 May 2013 17:35:16 +0200 Subject: erts: Add testsuite for Maps --- erts/emulator/test/Makefile | 1 + erts/emulator/test/map_SUITE.erl | 532 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 533 insertions(+) create mode 100644 erts/emulator/test/map_SUITE.erl (limited to 'erts') diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index f02ca3cb98..0b0568c31a 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -68,6 +68,7 @@ MODULES= \ hash_SUITE \ hibernate_SUITE \ list_bif_SUITE \ + map_SUITE \ match_spec_SUITE \ module_info_SUITE \ monitor_SUITE \ diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl new file mode 100644 index 0000000000..bbc5aa07b4 --- /dev/null +++ b/erts/emulator/test/map_SUITE.erl @@ -0,0 +1,532 @@ +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The 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(map_SUITE). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2 + ]). + +-export([ + t_build_and_match_literals/1, + t_update_literals/1,t_match_and_update_literals/1, + t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, + t_guard_receive/1, t_guard_fun/1, + t_list_comprehension/1, + t_map_sort_literals/1, + t_size/1, t_map_size/1, + + %% BIFs + t_bif_map_get/1, + t_bif_map_find/1, + t_bif_map_is_key/1, + t_bif_map_keys/1, + t_bif_map_new/1, + t_bif_map_put/1, + t_bif_map_remove/1, + t_bif_map_values/1 + ]). + +-include_lib("test_server/include/test_server.hrl"). + + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [ + t_build_and_match_literals, + t_update_literals, t_match_and_update_literals, + t_guard_bifs, t_guard_sequence, t_guard_update, + t_guard_receive,t_guard_fun, t_list_comprehension, + t_map_sort_literals, + + %% Specific Map BIFs + t_bif_map_get,t_bif_map_find,t_bif_map_is_key, + t_bif_map_keys,t_bif_map_new,t_bif_map_put, + t_bif_map_remove,t_bif_map_values + ]. + +groups() -> []. + +init_per_suite(Config) -> Config. +end_per_suite(_Config) -> ok. + +init_per_group(_GroupName, Config) -> Config. +end_per_group(_GroupName, Config) -> Config. + +%% tests + +t_build_and_match_literals(Config) when is_list(Config) -> + #{} = id(#{}), + #{1=>a} = id(#{1=>a}), + #{1=>a,2=>b} = id(#{1=>a,2=>b}), + #{1=>a,2=>b,3=>"c"} = id(#{1=>a,2=>b,3=>"c"}), + #{1=>a,2=>b,3=>"c","4"=>"d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}), + #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>} = + id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}), + #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"} = + id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}), + #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g} = + id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}), + + #{<<"hi all">> => 1} = id(#{<<"hi",32,"all">> => 1}), + + #{a=>X,a=>X=3,b=>4} = id(#{a=>3,b=>4}), % weird but ok =) + + #{ a=>#{ b=>#{c => third, b=>second}}, b=>first} = + id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}), + + M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}, + M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first} = + id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}), + + %% error case + %V = 32, + %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3,x=>2} = id(#{x=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x=>2} = id(#{x=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id({a,b,c}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{y=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{x=>"three"}))), + ok. + +%% Tests size(Map). + +t_size(Config) when is_list(Config) -> +% 0 = size(#{}), +% 1 = size(#{a=>1}), +% 1 = size(#{a=>#{a=>1}}), +% 2 = size(#{a=>1, b=>2}), +% 3 = size(#{a=>1, b=>2, b=>"3"}), + ok. + +t_map_size(Config) when is_list(Config) -> + 0 = map_size(id(#{})), + 1 = map_size(id(#{a=>1})), + 1 = map_size(id(#{a=>"wat"})), + 2 = map_size(id(#{a=>1, b=>2})), + 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})), + + true = map_is_size(#{a=>1}, 1), + true = map_is_size(#{a=>1, a=>2}, 2), + M = #{ "a" => 1, "b" => 2}, + true = map_is_size(M#{ "a" => 2}, 2), + false = map_is_size(M#{ "a" => 2}, 3), + + %% Error cases. + {'EXIT',{badarg,_}} = (catch map_size([])), + {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), + {'EXIT',{badarg,_}} = (catch map_size(1)), + ok. + +map_is_size(M,N) when map_size(M) =:= N -> true; +map_is_size(_,_) -> false. + +% test map updates without matching +t_update_literals(Config) when is_list(Config) -> + Map = #{x=>1,y=>2,z=>3,q=>4}, + #{x=>"d",q=>"4"} = loop_update_literals_x_q(Map, [ + {"a","1"},{"b","2"},{"c","3"},{"d","4"} + ]), + ok. + +loop_update_literals_x_q(Map, []) -> Map; +loop_update_literals_x_q(Map, [{X,Q}|Vs]) -> + loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs). + +% test map updates with matching +t_match_and_update_literals(Config) when is_list(Config) -> + Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1}, + #{x=>16,q=>21,y=>"untouched",z=>"also untouched"} = loop_match_and_update_literals_x_q(Map, [ + {1,2},{3,4},{5,6},{7,8} + ]), + M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}), + M1 = id(#{}), + M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + M0 = M2, + + #{ 4 => another_number, int => 3 } = M2#{ 4 => another_number }, + ok. + +loop_match_and_update_literals_x_q(Map, []) -> Map; +loop_match_and_update_literals_x_q(#{q=>Q0,x=>X0} = Map, [{X,Q}|Vs]) -> + loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). + + +t_guard_bifs(Config) when is_list(Config) -> + true = map_guard_head(#{a=>1}), + false = map_guard_head([]), + true = map_guard_body(#{a=>1}), + false = map_guard_body({}), + true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }), + false = map_guard_pattern("list"), + ok. + +map_guard_head(M) when is_map(M) -> true; +map_guard_head(_) -> false. + +map_guard_body(M) -> is_map(M). + +map_guard_pattern(#{}) -> true; +map_guard_pattern(_) -> false. + +t_guard_sequence(Config) when is_list(Config) -> + {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}), + {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}), + {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}), + {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}), + {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}), + + {1,M1} = map_guard_sequence_2(M1 = id(#{a=>3})), + {2,M2} = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})), + {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})), + {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})), + {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})), + + %% error case + {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})), + {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})), + ok. + +map_guard_sequence_1(#{seq=>1=Seq, val=>Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq=>2=Seq, val=>Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq=>3=Seq, val=>Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq=>4=Seq, val=>Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq=>5=Seq, val=>Val}) -> {Seq,Val}. + +map_guard_sequence_2(#{ a=>3 }=M) -> {1, M}; +map_guard_sequence_2(#{ a=>4 }=M) -> {2, M}; +map_guard_sequence_2(#{ a=>X, a=>X, b=>4 }=M) -> {3,X,M}; +map_guard_sequence_2(#{ a=>X, a=>Y, b=>3 }=M) when X =:= Y -> {4,X,Y,M}; +map_guard_sequence_2(#{ a=>X, a=>Y }=M) when X =:= Y -> {5,X,Y,M}. + + +t_guard_update(Config) when is_list(Config) -> + error = map_guard_update(#{},#{}), + first = map_guard_update(#{}, #{x=>first}), + second = map_guard_update(#{y=>old}, #{x=>second,y=>old}), + ok. + +map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first; +map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second; +map_guard_update(_, _) -> error. + +t_guard_receive(Config) when is_list(Config) -> + M0 = #{ id => 0 }, + Pid = spawn_link(fun() -> guard_receive_loop() end), + Big = 36893488147419103229, + B1 = <<"some text">>, + B2 = <<"was appended">>, + B3 = <>, + + #{id=>1, res=>Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}), + #{id=>2, res=>26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}), + #{id=>3, res=>832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}), + #{id=>4, res=>4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}), + #{id=>5, res=>Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}), + #{id=>6, res=>B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}), + #{id=>7, res=>4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}), + + + %% update old maps and check id update + #{id=>2, res=>B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}), + #{id=>5, res=>99} = call(Pid, M4#{op=>add,in=>{33, 66}}), + + %% cleanup + done = call(Pid, done), + ok. + +call(Pid, M) -> + Pid ! {self(), M}, receive {Pid, Res} -> Res end. + +guard_receive_loop() -> + receive + {Pid, #{ id=>Id, op=>"append", in=>{X,Y}}=M} when is_binary(X), is_binary(Y) -> + Pid ! {self(), M#{ id=>Id+1, res=><>}}, + guard_receive_loop(); + {Pid, #{ id=>Id, op=>add, in=>{X,Y}}} -> + Pid ! {self(), #{ id=>Id+1, res=>X+Y}}, + guard_receive_loop(); + {Pid, #{ id=>Id, op=>sub, in=>{X,Y}}=M} -> + Pid ! {self(), M#{ id=>Id+1, res=>X-Y}}, + guard_receive_loop(); + {Pid, #{ id=>Id, op=>idiv, in=>{X,Y}}=M} -> + Pid ! {self(), M#{ id=>Id+1, res=>X div Y}}, + guard_receive_loop(); + {Pid, #{ id=>Id, op=>imul, in=>{X,Y}}=M} -> + Pid ! {self(), M#{ id=>Id+1, res=>X * Y}}, + guard_receive_loop(); + {Pid, done} -> + Pid ! {self(), done}; + {Pid, Other} -> + Pid ! {error, Other}, + guard_receive_loop() + end. + + +t_list_comprehension(Config) when is_list(Config) -> + [#{k=>1},#{k=>2},#{k=>3}] = [#{k=>I} || I <- [1,2,3]], + ok. + +t_guard_fun(Config) when is_list(Config) -> + F1 = fun + (#{s=>v,v=>V}) -> {v,V}; + (#{s=>t,v=>{V,V}}) -> {t,V}; + (#{s=>l,v=>[V,V]}) -> {l,V} + end, + + F2 = fun + (#{s=>T,v=>{V,V}}) -> {T,V}; + (#{s=>T,v=>[V,V]}) -> {T,V}; + (#{s=>T,v=>V}) -> {T,V} + end, + V = <<"hi">>, + + {v,V} = F1(#{s=>v,v=>V}), + {t,V} = F1(#{s=>t,v=>{V,V}}), + {l,V} = F1(#{s=>l,v=>[V,V]}), + + {v,V} = F2(#{s=>v,v=>V}), + {t,V} = F2(#{s=>t,v=>{V,V}}), + {l,V} = F2(#{s=>l,v=>[V,V]}), + + %% error case + {'EXIT', {function_clause,[{?MODULE,_,[#{s=>none,v=>none}],_}|_]}} = (catch F1(#{s=>none,v=>none})), + ok. + + +t_map_sort_literals(Config) when is_list(Config) -> + % test relation + + %% size order + true = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}), + true = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}), + false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}), + + %% key order + true = #{ a => 1 } < id(#{ b => 1}), + false = #{ b => 1 } < id(#{ a => 1}), + true = #{ a => 1, b => 1, c => 1 } < id(#{ b => 1, c => 1, d => 1}), + true = #{ b => 1, c => 1, d => 1 } > id(#{ a => 1, b => 1, c => 1}), + true = #{ c => 1, b => 1, a => 1 } < id(#{ b => 1, c => 1, d => 1}), + true = #{ "a" => 1 } < id(#{ <<"a">> => 1}), + false = #{ <<"a">> => 1 } < id(#{ "a" => 1}), + false = #{ 1 => 1 } < id(#{ 1.0 => 1}), + false = #{ 1.0 => 1 } < id(#{ 1 => 1}), + + %% value order + true = #{ a => 1 } < id(#{ a => 2}), + false = #{ a => 2 } < id(#{ a => 1}), + false = #{ a => 2, b => 1 } < id(#{ a => 1, b => 3}), + true = #{ a => 1, b => 1 } < id(#{ a => 1, b => 3}), + + true = #{ "a" => "hi", b => 134 } == id(#{ b => 134,"a" => "hi"}), + + %% lists:sort + + SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}], + [#{1=>ok},#{a=>ok},#{"a"=>ok},#{<<"a">>=>ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]), + [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(SortVs), + [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(lists:reverse(SortVs)), + + ok. + +%% BIFs +t_bif_map_get(Config) when is_list(Config) -> + + 1 = map:get(a, #{ a=> 1}), + 2 = map:get(b, #{ a=> 1, b => 2}), + "hi" = map:get("hello", #{ a=>1, "hello" => "hi"}), + "tuple hi" = map:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), + + M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), + "v4" = map:get(<<"k2">>, M#{ <<"k2">> => "v4" }), + + %% error case + {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,[])), + {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,<<>>)), + {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get({1,1}, #{{1,1.0} => "tuple"})), + {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{})), + {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{ b=>1, c=>2})), + ok. + +t_bif_map_find(Config) when is_list(Config) -> + + {ok, 1} = map:find(a, #{ a=> 1}), + {ok, 2} = map:find(b, #{ a=> 1, b => 2}), + {ok, "int"} = map:find(1, #{ 1 => "int"}), + {ok, "int"} = map:find(1.0, #{ 1 => "int"}), + {ok, "float"} = map:find(1, #{ 1.0 => "float"}), + {ok, "float"} = map:find(1.0, #{ 1.0=> "float"}), + + {ok, "hi"} = map:find("hello", #{ a=>1, "hello" => "hi"}), + {ok, "tuple hi"} = map:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + + M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), + {ok, "v4"} = map:find(<<"k2">>, M#{ <<"k2">> => "v4" }), + + %% error case + error = map:find(a,#{}), + error = map:find(a,#{b=>1, c=>2}), + + {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,[])), + {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,<<>>)), + ok. + + +t_bif_map_is_key(Config) when is_list(Config) -> + M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, + + true = map:is_key("hi", M1), + true = map:is_key(int, M1), + true = map:is_key(<<"key">>, M1), + true = map:is_key(4, M1), + + false = map:is_key(5, M1), + false = map:is_key(<<"key2">>, M1), + false = map:is_key("h", M1), + false = map:is_key("hello", M1), + false = map:is_key(atom, M1), + + false = map:is_key("hi", map:remove("hi", M1)), + true = map:is_key("hi", M1), + true = map:is_key(1, map:put(1, "number", M1)), + false = map:is_key(1.0, map:put(1, "number", M1)), + ok. + +t_bif_map_keys(Config) when is_list(Config) -> + [] = map:keys(#{}), + + [1,2,3,4,5] = map:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [1,2,3,4,5] = map:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + + % values in key order: [4,int,"hi",<<"key">>] + M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, + [4,int,"hi",<<"key">>] = map:keys(M1), + + %% error case + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(154)), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(atom)), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys([])), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(<<>>)), + ok. + +t_bif_map_new(Config) when is_list(Config) -> + #{} = map:new(), + 0 = erlang:map_size(map:new()), + ok. + +t_bif_map_put(Config) when is_list(Config) -> + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + M1 = #{ "hi" => "hello"} = map:put("hi", "hello", #{}), + + ["hi"] = map:keys(M1), + ["hello"] = map:values(M1), + + M2 = #{ int => 3 } = map:put(int, 3, M1), + + [int,"hi"] = map:keys(M2), + [3,"hello"] = map:values(M2), + + M3 = #{ <<"key">> => <<"value">> } = map:put(<<"key">>, <<"value">>, M2), + + [int,"hi",<<"key">>] = map:keys(M3), + [3,"hello",<<"value">>] = map:values(M3), + + M4 = #{ 18446744073709551629 => wat } = map:put(18446744073709551629, wat, M3), + + [18446744073709551629,int,"hi",<<"key">>] = map:keys(M4), + [wat,3,"hello",<<"value">>] = map:values(M4), + + M0 = #{ 4 => number } = M5 = map:put(4, number, M4), + + [4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5), + [number,wat,3,"hello",<<"value">>] = map:values(M5), + + %% error case + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,154)), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,atom)), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,[])), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,<<>>)), + ok. + +t_bif_map_remove(Config) when is_list(Config) -> + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + M1 = map:remove("hi", M0), + [4,18446744073709551629,int,<<"key">>] = map:keys(M1), + [number,wat,3,<<"value">>] = map:values(M1), + + M2 = map:remove(int, M1), + [4,18446744073709551629,<<"key">>] = map:keys(M2), + [number,wat,<<"value">>] = map:values(M2), + + M3 = map:remove(<<"key">>, M2), + [4,18446744073709551629] = map:keys(M3), + [number,wat] = map:values(M3), + + M4 = map:remove(18446744073709551629, M3), + [4] = map:keys(M4), + [number] = map:values(M4), + + M5 = map:remove(4, M4), + [] = map:keys(M5), + [] = map:values(M5), + + M0 = map:remove(5,M0), + M0 = map:remove("hi there",M0), + + #{ "hi" => "hello", int => 3, 4 => number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), + + %% error case + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,154)), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,atom)), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,[])), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,<<>>)), + ok. + + +t_bif_map_values(Config) when is_list(Config) -> + + [] = map:values(#{}), + + [a,b,c,d,e] = map:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [a,b,c,d,e] = map:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + + % values in key order: [4,int,"hi",<<"key">>] + M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, + M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> }, + [number,3,"hello2",<<"value2">>] = map:values(M2), + [number,3,"hello",<<"value">>] = map:values(M1), + + %% error case + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)), + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])), + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)), + + ok. + + +%% Use this function to avoid compile-time evaluation of an expression. +id(I) -> I. -- cgit v1.2.3 From aee6128a1bfb2356440b27a9c7db44650336e4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Oct 2013 00:09:37 +0200 Subject: erts: Conform map_SUITE to extended syntax --- erts/emulator/test/map_SUITE.erl | 122 +++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 61 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index bbc5aa07b4..6d82a2cb59 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -70,36 +70,36 @@ end_per_group(_GroupName, Config) -> Config. t_build_and_match_literals(Config) when is_list(Config) -> #{} = id(#{}), - #{1=>a} = id(#{1=>a}), - #{1=>a,2=>b} = id(#{1=>a,2=>b}), - #{1=>a,2=>b,3=>"c"} = id(#{1=>a,2=>b,3=>"c"}), - #{1=>a,2=>b,3=>"c","4"=>"d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}), - #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>} = + #{1:=a} = id(#{1=>a}), + #{1:=a,2:=b} = id(#{1=>a,2=>b}), + #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}), + #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}), + #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} = id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}), - #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"} = + #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} = id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}), - #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g} = + #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} = id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}), - #{<<"hi all">> => 1} = id(#{<<"hi",32,"all">> => 1}), + #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}), - #{a=>X,a=>X=3,b=>4} = id(#{a=>3,b=>4}), % weird but ok =) + #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =) - #{ a=>#{ b=>#{c => third, b=>second}}, b=>first} = + #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} = id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}), M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}, - M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first} = + M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} = id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}), %% error case %V = 32, %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))), - {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3,x=>2} = id(#{x=>3}))), - {'EXIT',{{badmatch,_},_}} = (catch (#{x=>2} = id(#{x=>3}))), - {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id({a,b,c}))), - {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{y=>3}))), - {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{x=>"three"}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), ok. %% Tests size(Map). @@ -137,7 +137,7 @@ map_is_size(_,_) -> false. % test map updates without matching t_update_literals(Config) when is_list(Config) -> Map = #{x=>1,y=>2,z=>3,q=>4}, - #{x=>"d",q=>"4"} = loop_update_literals_x_q(Map, [ + #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [ {"a","1"},{"b","2"},{"c","3"},{"d","4"} ]), ok. @@ -149,7 +149,7 @@ loop_update_literals_x_q(Map, [{X,Q}|Vs]) -> % test map updates with matching t_match_and_update_literals(Config) when is_list(Config) -> Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1}, - #{x=>16,q=>21,y=>"untouched",z=>"also untouched"} = loop_match_and_update_literals_x_q(Map, [ + #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [ {1,2},{3,4},{5,6},{7,8} ]), M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, @@ -159,11 +159,11 @@ t_match_and_update_literals(Config) when is_list(Config) -> 4 => number, 18446744073709551629 => wat}, M0 = M2, - #{ 4 => another_number, int => 3 } = M2#{ 4 => another_number }, + #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number }, ok. loop_match_and_update_literals_x_q(Map, []) -> Map; -loop_match_and_update_literals_x_q(#{q=>Q0,x=>X0} = Map, [{X,Q}|Vs]) -> +loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). @@ -202,17 +202,17 @@ t_guard_sequence(Config) when is_list(Config) -> {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})), ok. -map_guard_sequence_1(#{seq=>1=Seq, val=>Val}) -> {Seq,Val}; -map_guard_sequence_1(#{seq=>2=Seq, val=>Val}) -> {Seq,Val}; -map_guard_sequence_1(#{seq=>3=Seq, val=>Val}) -> {Seq,Val}; -map_guard_sequence_1(#{seq=>4=Seq, val=>Val}) -> {Seq,Val}; -map_guard_sequence_1(#{seq=>5=Seq, val=>Val}) -> {Seq,Val}. +map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}. -map_guard_sequence_2(#{ a=>3 }=M) -> {1, M}; -map_guard_sequence_2(#{ a=>4 }=M) -> {2, M}; -map_guard_sequence_2(#{ a=>X, a=>X, b=>4 }=M) -> {3,X,M}; -map_guard_sequence_2(#{ a=>X, a=>Y, b=>3 }=M) when X =:= Y -> {4,X,Y,M}; -map_guard_sequence_2(#{ a=>X, a=>Y }=M) when X =:= Y -> {5,X,Y,M}. +map_guard_sequence_2(#{ a:=3 }=M) -> {1, M}; +map_guard_sequence_2(#{ a:=4 }=M) -> {2, M}; +map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M}; +map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M}; +map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}. t_guard_update(Config) when is_list(Config) -> @@ -233,18 +233,18 @@ t_guard_receive(Config) when is_list(Config) -> B2 = <<"was appended">>, B3 = <>, - #{id=>1, res=>Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}), - #{id=>2, res=>26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}), - #{id=>3, res=>832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}), - #{id=>4, res=>4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}), - #{id=>5, res=>Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}), - #{id=>6, res=>B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}), - #{id=>7, res=>4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}), + #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}), + #{id:=2, res:=26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}), + #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}), + #{id:=4, res:=4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}), + #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}), + #{id:=6, res:=B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}), + #{id:=7, res:=4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}), %% update old maps and check id update - #{id=>2, res=>B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}), - #{id=>5, res=>99} = call(Pid, M4#{op=>add,in=>{33, 66}}), + #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}), + #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}), %% cleanup done = call(Pid, done), @@ -255,19 +255,19 @@ call(Pid, M) -> guard_receive_loop() -> receive - {Pid, #{ id=>Id, op=>"append", in=>{X,Y}}=M} when is_binary(X), is_binary(Y) -> + {Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) -> Pid ! {self(), M#{ id=>Id+1, res=><>}}, guard_receive_loop(); - {Pid, #{ id=>Id, op=>add, in=>{X,Y}}} -> + {Pid, #{ id:=Id, op:=add, in:={X,Y}}} -> Pid ! {self(), #{ id=>Id+1, res=>X+Y}}, guard_receive_loop(); - {Pid, #{ id=>Id, op=>sub, in=>{X,Y}}=M} -> + {Pid, #{ id:=Id, op:=sub, in:={X,Y}}=M} -> Pid ! {self(), M#{ id=>Id+1, res=>X-Y}}, guard_receive_loop(); - {Pid, #{ id=>Id, op=>idiv, in=>{X,Y}}=M} -> + {Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} -> Pid ! {self(), M#{ id=>Id+1, res=>X div Y}}, guard_receive_loop(); - {Pid, #{ id=>Id, op=>imul, in=>{X,Y}}=M} -> + {Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} -> Pid ! {self(), M#{ id=>Id+1, res=>X * Y}}, guard_receive_loop(); {Pid, done} -> @@ -279,20 +279,20 @@ guard_receive_loop() -> t_list_comprehension(Config) when is_list(Config) -> - [#{k=>1},#{k=>2},#{k=>3}] = [#{k=>I} || I <- [1,2,3]], + [#{k:=1},#{k:=2},#{k:=3}] = [#{k:=I} || I <- [1,2,3]], ok. t_guard_fun(Config) when is_list(Config) -> F1 = fun - (#{s=>v,v=>V}) -> {v,V}; - (#{s=>t,v=>{V,V}}) -> {t,V}; - (#{s=>l,v=>[V,V]}) -> {l,V} + (#{s:=v,v:=V}) -> {v,V}; + (#{s:=t,v:={V,V}}) -> {t,V}; + (#{s:=l,v:=[V,V]}) -> {l,V} end, F2 = fun - (#{s=>T,v=>{V,V}}) -> {T,V}; - (#{s=>T,v=>[V,V]}) -> {T,V}; - (#{s=>T,v=>V}) -> {T,V} + (#{s:=T,v:={V,V}}) -> {T,V}; + (#{s:=T,v:=[V,V]}) -> {T,V}; + (#{s:=T,v:=V}) -> {T,V} end, V = <<"hi">>, @@ -305,7 +305,7 @@ t_guard_fun(Config) when is_list(Config) -> {l,V} = F2(#{s=>l,v=>[V,V]}), %% error case - {'EXIT', {function_clause,[{?MODULE,_,[#{s=>none,v=>none}],_}|_]}} = (catch F1(#{s=>none,v=>none})), + {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})), ok. @@ -339,9 +339,9 @@ t_map_sort_literals(Config) when is_list(Config) -> %% lists:sort SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}], - [#{1=>ok},#{a=>ok},#{"a"=>ok},#{<<"a">>=>ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]), - [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(SortVs), - [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(lists:reverse(SortVs)), + [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]), + [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs), + [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)), ok. @@ -435,27 +435,27 @@ t_bif_map_put(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, - M1 = #{ "hi" => "hello"} = map:put("hi", "hello", #{}), + M1 = #{ "hi" := "hello"} = map:put("hi", "hello", #{}), ["hi"] = map:keys(M1), ["hello"] = map:values(M1), - M2 = #{ int => 3 } = map:put(int, 3, M1), + M2 = #{ int := 3 } = map:put(int, 3, M1), [int,"hi"] = map:keys(M2), [3,"hello"] = map:values(M2), - M3 = #{ <<"key">> => <<"value">> } = map:put(<<"key">>, <<"value">>, M2), + M3 = #{ <<"key">> := <<"value">> } = map:put(<<"key">>, <<"value">>, M2), [int,"hi",<<"key">>] = map:keys(M3), [3,"hello",<<"value">>] = map:values(M3), - M4 = #{ 18446744073709551629 => wat } = map:put(18446744073709551629, wat, M3), + M4 = #{ 18446744073709551629 := wat } = map:put(18446744073709551629, wat, M3), [18446744073709551629,int,"hi",<<"key">>] = map:keys(M4), [wat,3,"hello",<<"value">>] = map:values(M4), - M0 = #{ 4 => number } = M5 = map:put(4, number, M4), + M0 = #{ 4 := number } = M5 = map:put(4, number, M4), [4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5), [number,wat,3,"hello",<<"value">>] = map:values(M5), @@ -495,7 +495,7 @@ t_bif_map_remove(Config) when is_list(Config) -> M0 = map:remove(5,M0), M0 = map:remove("hi there",M0), - #{ "hi" => "hello", int => 3, 4 => number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), + #{ "hi" := "hello", int := 3, 4 := number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), %% error case {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)), -- cgit v1.2.3 From c2702eb35db00ad67f922708eeea48616d908306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 3 Oct 2013 14:06:57 +0200 Subject: compiler: Implement different instructions for => and := --- erts/emulator/beam/beam_emu.c | 301 +++++++++++++++++++++++++++------------ erts/emulator/beam/ops.tab | 11 +- erts/emulator/test/map_SUITE.erl | 38 +++++ 3 files changed, 257 insertions(+), 93 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 1cad31be2c..b666b7c3f7 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -959,8 +959,10 @@ static BeamInstr* apply_fun(Process* p, Eterm fun, static Eterm new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) NOINLINE; static Eterm new_map(Process* p, Eterm* reg, BeamInstr* I) NOINLINE; -static Eterm update_map(Process* p, Eterm* reg, - Eterm map, BeamInstr* I) NOINLINE; +static Eterm update_map_assoc(Process* p, Eterm* reg, + Eterm map, BeamInstr* I) NOINLINE; +static Eterm update_map_exact(Process* p, Eterm* reg, + Eterm map, BeamInstr* I) NOINLINE; static int has_not_map_field(Eterm map, Eterm key); static Eterm get_map_element(Eterm map, Eterm key); @@ -2353,21 +2355,39 @@ void process_main(void) Next(4+Arg(3)); } - OpCase(update_map_jddII): { + OpCase(update_map_assoc_jddII): { Eterm res; Eterm map; GetArg1(1, map); x(0) = r(0); SWAPOUT; - res = update_map(c_p, reg, map, I); + res = update_map_assoc(c_p, reg, map, I); SWAPIN; - if (res) { + if (is_value(res)) { r(0) = x(0); StoreResult(res, Arg(2)); Next(5+Arg(4)); } else { - goto lb_Cl_error; + goto badarg; + } + } + + OpCase(update_map_exact_jddII): { + Eterm res; + Eterm map; + + GetArg1(1, map); + x(0) = r(0); + SWAPOUT; + res = update_map_exact(c_p, reg, map, I); + SWAPIN; + if (is_value(res)) { + r(0) = x(0); + StoreResult(res, Arg(2)); + Next(5+Arg(4)); + } else { + goto badarg; } } @@ -6387,18 +6407,10 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) return make_map(mp); } - -/* This entire instruction will be split into two. - * 1) update_map_exact (literals) <- this can be much more optimized - * 2) update_map_assoc (literals) - * Also update_map is pretty bad code as it stands now. - */ - static Eterm -update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) +update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) { Uint n; - Uint i; Uint num_old; Uint num_updates; Uint need; @@ -6413,7 +6425,7 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) Eterm* kp; if (is_not_map(map)) { - return 0; + return THE_NON_VALUE; } old_mp = (map_t *) map_val(map); @@ -6428,11 +6440,12 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) } /* - * Allocate heap space for the worst case (i.e. all keys are new). + * Allocate heap space for the worst case (i.e. all keys in the + * update list are new). */ num_updates = Arg(4) / 2; - need = 2*(num_old+num_updates) + 4; + need = 2*(num_old+num_updates) + 1 + sizeof(map_t) / sizeof(Eterm); if (HeapWordsLeft(p) < need) { Uint live = Arg(3); reg[live] = map; @@ -6442,67 +6455,37 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) } /* - * Update map, optimistically assuming that there are no - * new keys, allowing us to keep the old key tuple. + * Build the skeleton for the map, ready to be filled in. + * + * +-----------------------------------+ + * | (Space for aritvyal for keys) | <-----------+ + * +-----------------------------------+ | + * | (Space for key 1) | | <-- kp + * +-----------------------------------+ | + * . | + * . | + * . | + * +-----------------------------------+ | + * | (Space for last key) | | + * +-----------------------------------+ | + * | MAP_HEADER | | + * +-----------------------------------+ | + * | (Space for number of keys/values) | | + * +-----------------------------------+ | + * | Boxed tuple pointer >----------------+ + * +-----------------------------------+ + * | (Space for value 1) | <-- hp + * +-----------------------------------+ */ - hp = p->htop; - E = p->stop; - - old_vals = map_get_values(old_mp); - old_keys = map_get_keys(old_mp); + E = p->stop; + kp = p->htop + 1; /* Point to first key */ + hp = kp + num_old + num_updates; res = make_map(hp); - mp = (map_t *)hp; hp += 3; + mp = (map_t *)hp; + hp += sizeof(map_t) / sizeof(Eterm); mp->thing_word = MAP_HEADER; - mp->size = num_old; - mp->keys = old_mp->keys; - - ASSERT(num_updates > 0); - - /* Get array of key/value pairs to be updated */ - new_p = &Arg(5); - GET_TERM(*new_p, new_key); - - n = num_updates; - - for (i = 0; i < num_old; i++) { - if (new_key == THE_NON_VALUE || !eq(*old_keys, new_key)) { - /* not same keys */ - *hp++ = *old_vals; - } else { - GET_TERM(new_p[1], *hp); - hp++; - n--; - if (n == 0) { - new_key = THE_NON_VALUE; - } else { - new_p += 2; - GET_TERM(*new_p, new_key); - } - } - old_vals++, old_keys++; - } - - /* - * If we have exhausted the update list we are done. - */ - - if (n == 0) { - p->htop = hp; - return res; - } - - /* - * There were some new keys. We'll have to start over and rebuild - * the key tuple too. - */ - - kp = p->htop; - *kp++ = make_arityval(0); - - res = make_map(hp); - mp = (map_t *)hp; hp += 3; mp->keys = make_tuple(kp-1); old_vals = map_get_values(old_mp); @@ -6512,20 +6495,28 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) GET_TERM(*new_p, new_key); n = num_updates; + /* + * Fill in keys and values, until we run out of either updates + * or old values and keys. + */ + for (;;) { Eterm key; Sint c; ASSERT(kp < (Eterm *)mp); key = *old_keys; - if ((c = cmp(key, new_key)) < 0) { + if ((c = CMP(key, new_key)) < 0) { + /* Copy old key and value */ *kp++ = key; *hp++ = *old_vals; old_keys++, old_vals++, num_old--; } else { /* Replace or insert new */ - *kp++ = new_key; GET_TERM(new_p[1], *hp++); - if (c == 0) { /* If replacement */ + if (c > 0) { /* If new new key */ + *kp++ = new_key; + } else { /* If replacement */ + *kp++ = key; old_keys++, old_vals++, num_old--; } n--; @@ -6541,32 +6532,162 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) } } - while (n-- > 0) { - GET_TERM(new_p[0], *kp++); - GET_TERM(new_p[1], *hp++); - new_p += 2; - } - /* - * All updates done. Now copy the remaining part of the frame's - * keys and values. + * At this point, we have run out of either old keys and values, + * or the update list. In other words, at least of one n and + * num_old must be zero. */ - while (num_old-- > 0) { - ASSERT(kp < (Eterm *)mp); - *kp++ = *old_keys++; - *hp++ = *old_vals++; + if (n > 0) { + /* + * All old keys and values have been copied, but there + * are still new keys and values in the update list that + * must be copied. + */ + ASSERT(num_old == 0); + while (n-- > 0) { + GET_TERM(new_p[0], *kp++); + GET_TERM(new_p[1], *hp++); + new_p += 2; + } + } else { + /* + * All updates are now done. We may still have old + * keys and values that we must copy. + */ + ASSERT(n == 0); + while (num_old-- > 0) { + ASSERT(kp < (Eterm *)mp); + *kp++ = *old_keys++; + *hp++ = *old_vals++; + } } + + /* + * Calculate how many values that are unused at the end of the + * key tuple and fill it out with a bignum header. + */ if ((n = (Eterm *)mp - kp) > 0) { *kp = make_pos_bignum_header(n-1); } + + /* + * Fill in the size of the map in both the key tuple and in the map. + */ + n = kp - p->htop - 1; /* Actual number of keys/values */ *p->htop = make_arityval(n); - mp->thing_word = MAP_HEADER; mp->size = n; p->htop = hp; return res; } + +/* + * Update values for keys that already exist in the map. + */ + +static Eterm +update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) +{ + Uint n; + Uint i; + Uint num_old; + Uint need; + map_t *old_mp, *mp; + Eterm res; + Eterm* hp; + Eterm* E; + Eterm* old_keys; + Eterm* old_vals; + Eterm* new_p; + Eterm new_key; + + if (is_not_map(map)) { + return THE_NON_VALUE; + } + + old_mp = (map_t *) map_val(map); + num_old = map_get_size(old_mp); + + /* + * If the old map is empty, create a new map. + */ + + if (num_old == 0) { + return new_map(p, reg, I+1); + } + + /* + * Allocate the exact heap space needed. + */ + + need = num_old + sizeof(map_t) / sizeof(Eterm); + if (HeapWordsLeft(p) < need) { + Uint live = Arg(3); + reg[live] = map; + erts_garbage_collect(p, need, reg, live+1); + map = reg[live]; + old_mp = (map_t *)map_val(map); + } + + /* + * Update map, keeping the old key tuple. + */ + + hp = p->htop; + E = p->stop; + + old_vals = map_get_values(old_mp); + old_keys = map_get_keys(old_mp); + + res = make_map(hp); + mp = (map_t *)hp; + hp += sizeof(map_t) / sizeof(Eterm); + mp->thing_word = MAP_HEADER; + mp->size = num_old; + mp->keys = old_mp->keys; + + /* Get array of key/value pairs to be updated */ + new_p = &Arg(5); + GET_TERM(*new_p, new_key); + + /* Update all values */ + n = Arg(4) / 2; /* Number of values to be updated */ + ASSERT(n > 0); + for (i = 0; i < num_old; i++) { + if (!EQ(*old_keys, new_key)) { + /* Not same keys */ + *hp++ = *old_vals; + } else { + GET_TERM(new_p[1], *hp); + hp++; + n--; + if (n == 0) { + /* + * All updates done. Copy remaining values + * and return the result. + */ + for (i++, old_vals++; i < num_old; i++) { + *hp++ = *old_vals++; + } + ASSERT(hp == p->htop + need); + p->htop = hp; + return res; + } else { + new_p += 2; + GET_TERM(*new_p, new_key); + } + } + old_vals++, old_keys++; + } + + /* + * Updates left. That means that at least one the keys in the + * update list did not previously exist. + */ + ASSERT(hp == p->htop + need); + return THE_NON_VALUE; +} #undef GET_TERM int catchlevel(Process *p) diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index b89bdb2e3a..f35997efee 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1474,11 +1474,16 @@ apply_last I P # has_map_field Fail Src Key => jump Fail # get_map_element Fail Src Key Dst => jump Fail -put_map F n Dst Live Size Rest=* => new_map F Dst Live Size Rest -put_map F Src Dst Live Size Rest=* => update_map F Src Dst Live Size Rest +put_map_assoc F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map_exact F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map_assoc F Src Dst Live Size Rest=* => \ + update_map_assoc F Src Dst Live Size Rest +put_map_exact F Src Dst Live Size Rest=* => \ + update_map_exact F Src Dst Live Size Rest new_map j d I I -update_map j d d I I +update_map_assoc j d d I I +update_map_exact j d d I I is_map Fail cq => jump Fail diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 6d82a2cb59..81d39fc97a 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -23,6 +23,7 @@ -export([ t_build_and_match_literals/1, t_update_literals/1,t_match_and_update_literals/1, + update_assoc/1,update_exact/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, @@ -48,6 +49,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ t_build_and_match_literals, t_update_literals, t_match_and_update_literals, + update_assoc,update_exact, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, t_map_sort_literals, @@ -166,6 +168,42 @@ loop_match_and_update_literals_x_q(Map, []) -> Map; loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). +update_assoc(Config) when is_list(Config) -> + M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), + + M1 = M0#{1=>42,2=>100,4=>[a,b,c]}, + #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1, + M1 = M0#{1.0=>wrong,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, + + M2 = M0#{3.0=>new}, + #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, + M2 = M0#{3.0:=wrong,3.0=>new}, + + %% Errors cases. + BadMap = id(badmap), + {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}), + + ok. + +update_exact(Config) when is_list(Config) -> + M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), + + M1 = M0#{1:=42,2:=100,4:=[a,b,c]}, + #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1, + M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]}, + + M2 = M0#{3.0:=new}, + #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, + M2 = M0#{3.0=>wrong,3.0:=new}, + M2 = M0#{3=>wrong,3.0:=new}, + + %% Errors cases. + {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + + ok. t_guard_bifs(Config) when is_list(Config) -> true = map_guard_head(#{a=>1}), -- cgit v1.2.3 From 813f61de8e7872481a0369de3297596c1b11881d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 9 Oct 2013 15:46:03 +0200 Subject: erts: Fixup map instructions for halfword --- erts/emulator/beam/beam_emu.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index b666b7c3f7..fe2e196785 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6353,7 +6353,7 @@ static Eterm get_map_element(Eterm map, Eterm key) #define GET_TERM(term, dest) \ do { \ - Eterm src = term; \ + Eterm src = (Eterm)(term); \ switch (src & _TAG_IMMED1_MASK) { \ case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ dest = x(0); \ @@ -6380,7 +6380,7 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) Eterm keys; Eterm *mhp,*thp; Eterm *E; - Eterm *tp; + BeamInstr *ptr; map_t *mp; if (HeapWordsLeft(p) < need) { @@ -6390,18 +6390,18 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) thp = p->htop; mhp = thp + 1 + n/2; E = p->stop; - tp = &Arg(4); + ptr = &Arg(4); keys = make_tuple(thp); *thp++ = make_arityval(n/2); - mp = (map_t *)mhp; mhp += 3; + mp = (map_t *)mhp; mhp += MAP_HEADER_SIZE; mp->thing_word = MAP_HEADER; mp->size = n/2; mp->keys = keys; for (i = 0; i < n/2; i++) { - GET_TERM(*tp++, *thp++); - GET_TERM(*tp++, *mhp++); + GET_TERM(*ptr++, *thp++); + GET_TERM(*ptr++, *mhp++); } p->htop = mhp; return make_map(mp); @@ -6420,7 +6420,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) Eterm* E; Eterm* old_keys; Eterm* old_vals; - Eterm* new_p; + BeamInstr* new_p; Eterm new_key; Eterm* kp; @@ -6445,7 +6445,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) */ num_updates = Arg(4) / 2; - need = 2*(num_old+num_updates) + 1 + sizeof(map_t) / sizeof(Eterm); + need = 2*(num_old+num_updates) + 1 + MAP_HEADER_SIZE; if (HeapWordsLeft(p) < need) { Uint live = Arg(3); reg[live] = map; @@ -6484,7 +6484,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) res = make_map(hp); mp = (map_t *)hp; - hp += sizeof(map_t) / sizeof(Eterm); + hp += MAP_HEADER_SIZE; mp->thing_word = MAP_HEADER; mp->keys = make_tuple(kp-1); @@ -6599,7 +6599,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) Eterm* E; Eterm* old_keys; Eterm* old_vals; - Eterm* new_p; + BeamInstr* new_p; Eterm new_key; if (is_not_map(map)) { @@ -6621,7 +6621,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) * Allocate the exact heap space needed. */ - need = num_old + sizeof(map_t) / sizeof(Eterm); + need = num_old + MAP_HEADER_SIZE; if (HeapWordsLeft(p) < need) { Uint live = Arg(3); reg[live] = map; @@ -6642,7 +6642,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) res = make_map(hp); mp = (map_t *)hp; - hp += sizeof(map_t) / sizeof(Eterm); + hp += MAP_HEADER_SIZE; mp->thing_word = MAP_HEADER; mp->size = num_old; mp->keys = old_mp->keys; -- cgit v1.2.3 From eb218eb375d353bcd78ae895805d1d45f927b3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Oct 2013 16:19:22 +0200 Subject: erts: Teach term_to_binary/1 and binary_to_term/1 Map encoding --- erts/emulator/beam/external.c | 197 ++++++++++++++++++++++++++++++++++++++---- erts/emulator/beam/external.h | 1 + 2 files changed, 183 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 5e7a5cab6e..a31b2731be 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -42,6 +42,7 @@ #include "erl_binary.h" #include "erl_bits.h" #include "erl_zlib.h" +#include "erl_map.h" #ifdef HIPE #include "hipe_mode_switch.h" @@ -2555,6 +2556,38 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, } break; + case MAP_DEF: + { + map_t *mp = (map_t*)map_val(obj); + Uint size = map_get_size(mp); + Eterm *mptr; + + *ep++ = MAP_EXT; + put_int32(size, ep); ep += 4; + + /* Push values first */ + if (size > 0) { + mptr = map_get_values(mp); + for (i = size-1; i >= 1; i--) { + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) mptr[i]); + } + + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) mptr[0]); + + mptr = map_get_keys(mp); + for (i = size-1; i >= 1; i--) { + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) mptr[i]); + } + + obj = mptr[0]; + goto L_jump_start; + } + } + break; + case FLOAT_DEF: GET_DOUBLE(obj, f); if (dflags & DFLAG_NEW_FLOATS) { @@ -2845,6 +2878,7 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, int n; ErtsAtomEncoding char_enc; register Eterm* hp; /* Please don't take the address of hp */ + Eterm *maps_head = NULL; /* for validation of maps */ Eterm* next; SWord reds; @@ -3469,6 +3503,65 @@ dec_term_atom_common: break; } break; + case MAP_EXT: + { + map_t *mp; + Uint32 size,n; + Eterm *kptr,*vptr; + Eterm keys; + + size = get_int32(ep); ep += 4; + + keys = make_tuple(hp); + *hp++ = make_arityval(size); + kptr = hp; + hp += size; + + mp = (map_t*)hp; + hp += MAP_HEADER_SIZE; + vptr = hp; + hp += size; + + /* kptr, first word for keys + * vptr, first word for values + */ + + /* + * Use thing_word to link through decoded maps. + * The list of maps is for later validation. + */ + + mp->thing_word = (Eterm) COMPRESS_POINTER(maps_head); + maps_head = (Eterm *) mp; + + mp->size = size; + mp->keys = keys; + *objp = make_map(mp); + + /* We assume the map is wellformed, meaning: + * - ascending key order + * - unique keys + */ + + objp = vptr + size - 1; + n = size; + + while (n-- > 0) { + *objp = (Eterm) COMPRESS_POINTER(next); + next = objp; + objp--; + } + + objp = kptr + size - 1; + n = size; + + while (n-- > 0) { + *objp = (Eterm) COMPRESS_POINTER(next); + next = objp; + objp--; + } + } + break; case NEW_FUN_EXT: { ErlFunThing* funp = (ErlFunThing *) hp; @@ -3678,21 +3771,7 @@ dec_term_atom_common: } default: - error: - /* UNDO: - * Must unlink all off-heap objects that may have been - * linked into the process. - */ - if (hp < *hpp) { /* Sometimes we used hp and sometimes *hpp */ - hp = *hpp; /* the largest must be the freshest */ - } - undo_offheap_in_area(off_heap, hp_saved, hp); - *hpp = hp_saved; - if (ctx) { - ctx->state = B2TDecodeFail; - ctx->reds = reds; - } - return NULL; + goto error; } if (--reds <= 0) { @@ -3710,12 +3789,53 @@ dec_term_atom_common: } } } + + /* Iterate through all the maps and check for validity + * - done here for when we know it is complete. + */ + + while (maps_head) { + Eterm *keys; + Sint arity; + + next = (Eterm *)(EXPAND_POINTER(*maps_head)); + keys = tuple_val(*(maps_head + 2)); + arity = arityval(*keys++); + + while(arity-- > 1) { + if (CMP(keys[arity-1],keys[arity]) >= 0) { + goto error; + } + } + + *maps_head = MAP_HEADER; + maps_head = next; + } + if (ctx) { ctx->state = B2TDone; ctx->reds = reds; } + *hpp = hp; return ep; + +error: + /* UNDO: + * Must unlink all off-heap objects that may have been + * linked into the process. + */ + if (hp < *hpp) { /* Sometimes we used hp and sometimes *hpp */ + hp = *hpp; /* the largest must be the freshest */ + } + undo_offheap_in_area(off_heap, hp_saved, hp); + *hpp = hp_saved; + if (ctx) { + ctx->state = B2TDecodeFail; + ctx->reds = reds; + } + + return NULL; } /* returns the number of bytes needed to encode an object @@ -3885,6 +4005,46 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, goto outer_loop; } break; + case MAP_DEF: + { + map_t *mp = (map_t*)map_val(obj); + Uint size = map_get_size(mp); + Uint i; + Eterm *ptr; + + result += 1 + 4; /* tag + 4 bytes size */ + + /* push values first */ + ptr = map_get_values(mp); + i = size; + while(i--) { + if (is_list(*ptr)) { + if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) { + result += m + 2 + 1; + } else { + result += 5; + } + } + ESTACK_PUSH(s,*ptr); + ++ptr; + } + + ptr = map_get_keys(mp); + i = size; + while(i--) { + if (is_list(*ptr)) { + if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) { + result += m + 2 + 1; + } else { + result += 5; + } + } + ESTACK_PUSH(s,*ptr); + ++ptr; + } + goto outer_loop; + } + break; case FLOAT_DEF: if (dflags & DFLAG_NEW_FLOATS) { result += 9; @@ -4175,6 +4335,13 @@ init_done: ADDTERMS(n); heap_size += n + 1; break; + case MAP_EXT: + CHKSIZE(4); + n = get_int32(ep); + ep += 4; + ADDTERMS(2*n); + heap_size += 3 + n + 1 + n; + break; case STRING_EXT: CHKSIZE(2); n = get_int16(ep); diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index 83001b2c7e..bf00958eb1 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -50,6 +50,7 @@ #define LARGE_BIG_EXT 'o' #define NEW_FUN_EXT 'p' #define EXPORT_EXT 'q' +#define MAP_EXT 't' #define FUN_EXT 'u' #define ATOM_UTF8_EXT 'v' #define SMALL_ATOM_UTF8_EXT 'w' -- cgit v1.2.3 From 97d087263e1836f3efb4267e3990219fe3e3c045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 3 Oct 2013 00:35:45 +0200 Subject: erts: Add Map tests for hashing and external format - Update map_SUITE with hash and encode tests - Update map_SUITE with external format decode tests - Update map_SUITE with map:to_list/1 and map:from_list/1 tests --- erts/emulator/test/map_SUITE.erl | 143 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 81d39fc97a..27f35ccd29 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -30,7 +30,7 @@ t_map_sort_literals/1, t_size/1, t_map_size/1, - %% BIFs + %% Specific Map BIFs t_bif_map_get/1, t_bif_map_find/1, t_bif_map_is_key/1, @@ -38,7 +38,13 @@ t_bif_map_new/1, t_bif_map_put/1, t_bif_map_remove/1, - t_bif_map_values/1 + t_bif_map_values/1, + t_bif_map_to_list/1, + t_bif_map_from_list/1, + + %% erlang + t_erlang_hash/1, + t_map_encode_decode/1 ]). -include_lib("test_server/include/test_server.hrl"). @@ -57,7 +63,11 @@ all() -> [ %% Specific Map BIFs t_bif_map_get,t_bif_map_find,t_bif_map_is_key, t_bif_map_keys,t_bif_map_new,t_bif_map_put, - t_bif_map_remove,t_bif_map_values + t_bif_map_remove,t_bif_map_values, + t_bif_map_to_list, t_bif_map_from_list, + + %% erlang + t_erlang_hash, t_map_encode_decode ]. groups() -> []. @@ -562,7 +572,134 @@ t_bif_map_values(Config) when is_list(Config) -> {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)), {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])), {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)), + ok. + +t_erlang_hash(Config) when is_list(Config) -> + + 39679005 = erlang:phash2(#{}), + 106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), + 119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), + 87559846 = erlang:phash2(#{ 1 => a }), + 76038729 = erlang:phash2(#{ a => 1 }), + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = map:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 67968757 = erlang:phash2(M0), + 76038729 = erlang:phash2(M1), + 67968757 = erlang:phash2(M2), + ok. + +t_map_encode_decode(Config) when is_list(Config) -> + <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}), + Pairs = [ + {a,b},{"key","values"},{<<"key">>,<<"value">>}, + {1,b},{[atom,1],{<<"wat">>,1,2,3}}, + {aa,"values"}, + {1 bsl 64 + (1 bsl 50 - 1), sc1}, + {99, sc2}, + {1 bsl 65 + (1 bsl 51 - 1), sc3}, + {88, sc4}, + {1 bsl 66 + (1 bsl 52 - 1), sc5}, + {77, sc6}, + {1 bsl 67 + (1 bsl 53 - 1), sc3}, + {75, sc6}, {-10,sc8}, + {<<>>, sc9}, {3.14158, sc10}, + {[3.14158], sc11}, {more_atoms, sc12}, + {{more_tuples}, sc13}, {self(), sc14}, + {{},{}},{[],[]} + ], + ok = map_encode_decode_and_match(Pairs,[],#{}), + + %% error cases + %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>> + %% which is: #{ a=>1, b=>1 } + + %% order violation + %% literally #{ b=>1, a=>1 } in the internal order (bad) + {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch + erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,1,97,1>>)), + + %% uniqueness violation + %% literally #{ a=>1, a=>1 } + {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch + erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,97,100,0,1,97,97,1,97,1>>)), + + %% bad size (too large) + {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch + erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,100,0,1,98,97,1,97,1>>)), + + %% bad size (too small) .. should fail just truncate it .. weird. + %% possibly change external format so truncated will be #{a:=1} + #{ a:=b } = + erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>), + + ok. + +map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> + M1 = map:put(K,V,M0), + B0 = erlang:term_to_binary(M1), + Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), + %% sort Ks and Vs according to term spec, then match it + ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]), + %% decode and match it + M1 = erlang:binary_to_term(B0), + map_encode_decode_and_match(Pairs,Ls,M1); +map_encode_decode_and_match([],_,_) -> ok. + +match_encoded_map(<<131,116,Size:32,Encoded/binary>>,Size,Items) -> + match_encoded_map(Encoded,Items); +match_encoded_map(_,_,_) -> no_match_size. + +match_encoded_map(<<>>,[]) -> ok; +match_encoded_map(Bin,[<<131,Item/binary>>|Items]) -> + Size = erlang:byte_size(Item), + <> = Bin, + EncodedTerm = Item, %% Asssert + match_encoded_map(Bin1,Items). + + +t_bif_map_to_list(Config) when is_list(Config) -> + [] = map:to_list(#{}), + [{a,1},{b,2}] = map:to_list(#{a=>1,b=>2}), + [{a,1},{b,2},{c,3}] = map:to_list(#{c=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3}] = map:to_list(#{g=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3},{"c",4}] = map:to_list(#{g=>3,a=>1,b=>2,"c"=>4}), + [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = map:to_list(#{ + <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}), + + [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = map:to_list(#{ + <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5, + <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10}), + + %% error cases + {'EXIT', {badarg,_}} = (catch map:to_list(id(a))), + {'EXIT', {badarg,_}} = (catch map:to_list(id(42))), + ok. + + +t_bif_map_from_list(Config) when is_list(Config) -> + #{} = map:from_list([]), + A = map:from_list([]), + 0 = erlang:map_size(A), + + #{a:=1,b:=2} = map:from_list([{a,1},{b,2}]), + #{c:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{c,3}]), + #{g:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{g,3}]), + + #{a:=2} = map:from_list([{a,1},{a,3},{a,2}]), + + #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} = + map:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]), + + #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} = + map:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2}, + {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]), + %% error cases + {'EXIT', {badarg,_}} = (catch map:from_list(id(a))), + {'EXIT', {badarg,_}} = (catch map:from_list(id(42))), ok. -- cgit v1.2.3 From ac2954982d6089b0970798581b2230bdd1a065d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Oct 2013 17:21:30 +0200 Subject: erts: Add Maps to erlang:phash/2 and erlang:hash/2 The hashing a map in these functions uses the same strategy as the other terms. The exception being a prime number with size so we do not get erlang:phash(#{}) -> 1 which would be the same as erlang:phash({}) and erlang:phash(<<>>). Same argument for erlang:hash/1. --- erts/emulator/beam/utils.c | 53 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index bc7e91295d..3774f379cc 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -735,6 +735,8 @@ erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length, #define FUNNY_NUMBER10 268440479 #define FUNNY_NUMBER11 268440577 #define FUNNY_NUMBER12 268440581 +#define FUNNY_NUMBER13 268440593 +#define FUNNY_NUMBER14 268440611 static Uint32 hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash) @@ -787,7 +789,7 @@ Uint32 make_hash(Eterm term_arg) /* Must not collide with the real tag_val_def's: */ #define MAKE_HASH_TUPLE_OP 0x11 -#define MAKE_HASH_FUN_OP 0x12 +#define MAKE_HASH_TERM_ARRAY_OP 0x12 #define MAKE_HASH_CDR_PRE_OP 0x13 #define MAKE_HASH_CDR_POST_OP 0x14 @@ -878,7 +880,7 @@ tail_recur: hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq; if (num_free > 0) { if (num_free > 1) { - WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_FUN_OP); + WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_TERM_ARRAY_OP); } term = funp->env[0]; goto tail_recur; @@ -968,6 +970,24 @@ tail_recur: hash *= is_neg ? FUNNY_NUMBER4 : FUNNY_NUMBER3; break; } + case MAP_DEF: + { + map_t *mp = (map_t *)map_val(term); + int size = map_get_size(mp); + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + + /* Use a prime with size to remedy some of + * the {} and <<>> hash problems */ + hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + size; + if (size == 0) + break; + + /* push values first */ + WSTACK_PUSH3(stack, (UWord)vs, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + WSTACK_PUSH3(stack, (UWord)ks, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + break; + } case TUPLE_DEF: { Eterm* ptr = tuple_val(term); @@ -977,7 +997,7 @@ tail_recur: op = MAKE_HASH_TUPLE_OP; }/*fall through*/ case MAKE_HASH_TUPLE_OP: - case MAKE_HASH_FUN_OP: + case MAKE_HASH_TERM_ARRAY_OP: { Uint i = (Uint) WSTACK_POP(stack); Eterm* ptr = (Eterm*) WSTACK_POP(stack); @@ -1206,6 +1226,9 @@ make_hash2(Eterm term) tmp = vs[i]; ESTACK_PUSH(s, tmp); } + /* We do not want to expose the tuple representation. + * Do not push the keys as a tuple. + */ for (i = size - 1; i >= 1; i--) { tmp = ks[i]; ESTACK_PUSH(s, tmp); @@ -1514,7 +1537,7 @@ tail_recur: hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq; if (num_free > 0) { if (num_free > 1) { - WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_FUN_OP); + WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_TERM_ARRAY_OP); } term = funp->env[0]; goto tail_recur; @@ -1627,6 +1650,24 @@ tail_recur: } break; + case MAP_DEF: + { + map_t *mp = (map_t *)map_val(term); + int size = map_get_size(mp); + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + + /* Use a prime with size to remedy some of + * the {} and <<>> hash problems */ + hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + size; + if (size == 0) + break; + + /* push values first */ + WSTACK_PUSH3(stack, (UWord)vs, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + WSTACK_PUSH3(stack, (UWord)ks, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + break; + } case TUPLE_DEF: { Eterm* ptr = tuple_val(term); @@ -1636,7 +1677,7 @@ tail_recur: op = MAKE_HASH_TUPLE_OP; }/*fall through*/ case MAKE_HASH_TUPLE_OP: - case MAKE_HASH_FUN_OP: + case MAKE_HASH_TERM_ARRAY_OP: { Uint i = (Uint) WSTACK_POP(stack); Eterm* ptr = (Eterm*) WSTACK_POP(stack); @@ -1664,7 +1705,7 @@ tail_recur: return hash; #undef MAKE_HASH_TUPLE_OP -#undef MAKE_HASH_FUN_OP +#undef MAKE_HASH_TERM_ARRAY_OP #undef MAKE_HASH_CDR_PRE_OP #undef MAKE_HASH_CDR_POST_OP } -- cgit v1.2.3 From 912f0f2aae82ee4316a2ee1863775bcf1d8a3ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Oct 2013 18:06:34 +0200 Subject: erts: Add Map tests for erlang:phash/2 --- erts/emulator/test/map_SUITE.erl | 60 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 27f35ccd29..70b5c33fb2 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -23,7 +23,7 @@ -export([ t_build_and_match_literals/1, t_update_literals/1,t_match_and_update_literals/1, - update_assoc/1,update_exact/1, + t_update_assoc/1,t_update_exact/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, @@ -55,7 +55,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ t_build_and_match_literals, t_update_literals, t_match_and_update_literals, - update_assoc,update_exact, + t_update_assoc,t_update_exact, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, t_map_sort_literals, @@ -178,7 +178,7 @@ loop_match_and_update_literals_x_q(Map, []) -> Map; loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). -update_assoc(Config) when is_list(Config) -> +t_update_assoc(Config) when is_list(Config) -> M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), M1 = M0#{1=>42,2=>100,4=>[a,b,c]}, @@ -195,7 +195,7 @@ update_assoc(Config) when is_list(Config) -> ok. -update_exact(Config) when is_list(Config) -> +t_update_exact(Config) when is_list(Config) -> M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), M1 = M0#{1:=42,2:=100,4:=[a,b,c]}, @@ -576,12 +576,23 @@ t_bif_map_values(Config) when is_list(Config) -> t_erlang_hash(Config) when is_list(Config) -> + ok = t_bif_erlang_phash2(), + ok = t_bif_erlang_phash(), + ok = t_bif_erlang_hash(), + + ok. + +t_bif_erlang_phash2() -> + 39679005 = erlang:phash2(#{}), 106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), 119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), 87559846 = erlang:phash2(#{ 1 => a }), 76038729 = erlang:phash2(#{ a => 1 }), + 79950245 = erlang:phash2(#{{} => <<>>}), + 11244490 = erlang:phash2(#{<<>> => {}}), + M0 = #{ a => 1, "key" => <<"value">> }, M1 = map:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, @@ -591,6 +602,47 @@ t_erlang_hash(Config) when is_list(Config) -> 67968757 = erlang:phash2(M2), ok. +t_bif_erlang_phash() -> + Sz = 1 bsl 32, + 268440612 = erlang:phash(#{},Sz), + 1196461908 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), + 3944426064 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), + 1394238263 = erlang:phash(#{ 1 => a },Sz), + 4066388227 = erlang:phash(#{ a => 1 },Sz), + + 1578050717 = erlang:phash(#{{} => <<>>},Sz), + 1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = map:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 3590546636 = erlang:phash(M0,Sz), + 4066388227 = erlang:phash(M1,Sz), + 3590546636 = erlang:phash(M2,Sz), + ok. + +t_bif_erlang_hash() -> + Sz = 1 bsl 27 - 1, + 5158 = erlang:hash(#{},Sz), + 71555838 = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), + 5497225 = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), + 126071654 = erlang:hash(#{ 1 => a },Sz), + 126426236 = erlang:hash(#{ a => 1 },Sz), + + 101655720 = erlang:hash(#{{} => <<>>},Sz), + 101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = map:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 38260486 = erlang:hash(M0,Sz), + 126426236 = erlang:hash(M1,Sz), + 38260486 = erlang:hash(M2,Sz), + ok. + + t_map_encode_decode(Config) when is_list(Config) -> <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}), Pairs = [ -- cgit v1.2.3 From ab54731c41f28a21bce526249e582b56106d4965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Dec 2013 17:07:54 +0100 Subject: erts: erlang:phash2 should hash Maps independent of order --- erts/emulator/beam/utils.c | 63 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 3774f379cc..8958d334ae 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1091,9 +1091,11 @@ Uint32 make_hash2(Eterm term) { Uint32 hash; + Uint32 hash_xor_keys = 0; + Uint32 hash_xor_values = 0; DeclareTmpHeapNoproc(tmp_big,2); -/* (HCONST * {2, ..., 14}) mod 2^32 */ +/* (HCONST * {2, ..., 16}) mod 2^32 */ #define HCONST_2 0x3c6ef372UL #define HCONST_3 0xdaa66d2bUL #define HCONST_4 0x78dde6e4UL @@ -1110,6 +1112,10 @@ make_hash2(Eterm term) #define HCONST_15 0x454021d7UL #define HCONST_16 0xe3779b90UL +#define HASH_MAP_TAIL (_make_header(1,_TAG_HEADER_REF)) +#define HASH_MAP_KEY (_make_header(2,_TAG_HEADER_REF)) +#define HASH_MAP_VAL (_make_header(3,_TAG_HEADER_REF)) + #define UINT32_HASH_2(Expr1, Expr2, AConst) \ do { \ Uint32 a,b; \ @@ -1204,36 +1210,45 @@ make_hash2(Eterm term) UINT32_HASH(arity, HCONST_9); if (arity == 0) /* Empty tuple */ goto hash2_common; - for (i = arity; i >= 2; i--) { + for (i = arity; i >= 1; i--) { tmp = elem[i]; ESTACK_PUSH(s, tmp); } - term = elem[1]; + goto hash2_common; } break; case MAP_SUBTAG: { map_t *mp = (map_t *)map_val(term); int i; - int size = map_get_size(mp); + int size = map_get_size(mp); Eterm *ks = map_get_keys(mp); Eterm *vs = map_get_values(mp); UINT32_HASH(size, HCONST_16); - if (size == 0) /* Empty Map */ + if (size == 0) { goto hash2_common; - + } + ESTACK_PUSH(s, hash_xor_values); + ESTACK_PUSH(s, hash_xor_keys); + ESTACK_PUSH(s, hash); + ESTACK_PUSH(s, HASH_MAP_TAIL); + hash = 0; + hash_xor_keys = 0; + hash_xor_values = 0; for (i = size - 1; i >= 0; i--) { tmp = vs[i]; + ESTACK_PUSH(s, HASH_MAP_VAL); ESTACK_PUSH(s, tmp); } /* We do not want to expose the tuple representation. * Do not push the keys as a tuple. */ - for (i = size - 1; i >= 1; i--) { + for (i = size - 1; i >= 0; i--) { tmp = ks[i]; + ESTACK_PUSH(s, HASH_MAP_KEY); ESTACK_PUSH(s, tmp); } - term = ks[0]; + goto hash2_common; } break; case EXPORT_SUBTAG: @@ -1427,15 +1442,47 @@ make_hash2(Eterm term) default: erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term); hash2_common: + + /* Uint32 hash always has the hash value of the previous term, + * compounded or otherwise. + */ + if (ESTACK_ISEMPTY(s)) { DESTROY_ESTACK(s); UnUseTmpHeapNoproc(2); return hash; } + term = ESTACK_POP(s); + + switch (term) { + case HASH_MAP_TAIL: { + hash = (Uint32) ESTACK_POP(s); + UINT32_HASH(hash_xor_keys, HCONST_16); + UINT32_HASH(hash_xor_values, HCONST_16); + hash_xor_keys = (Uint32) ESTACK_POP(s); + hash_xor_values = (Uint32) ESTACK_POP(s); + goto hash2_common; + } + case HASH_MAP_KEY: + hash_xor_keys ^= hash; + hash = 0; + goto hash2_common; + case HASH_MAP_VAL: + hash_xor_values ^= hash; + hash = 0; + goto hash2_common; + default: + break; + } } } } + +#undef HASH_MAP_TAIL +#undef HASH_MAP_KEY +#undef HASH_MAP_VAL + #undef UINT32_HASH_2 #undef UINT32_HASH #undef SINT32_HASH -- cgit v1.2.3 From 9344e8ccf846af62576657244e3d526f88fdeb0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Dec 2013 18:01:58 +0100 Subject: erts: Update Maps erlang:phash2/1 tests --- erts/emulator/test/map_SUITE.erl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 70b5c33fb2..1f4476defa 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -584,22 +584,22 @@ t_erlang_hash(Config) when is_list(Config) -> t_bif_erlang_phash2() -> - 39679005 = erlang:phash2(#{}), - 106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), - 119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), - 87559846 = erlang:phash2(#{ 1 => a }), - 76038729 = erlang:phash2(#{ a => 1 }), + 39679005 = erlang:phash2(#{}), + 78942764 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), + 37338230 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), + 14363616 = erlang:phash2(#{ 1 => a }), + 51612236 = erlang:phash2(#{ a => 1 }), - 79950245 = erlang:phash2(#{{} => <<>>}), - 11244490 = erlang:phash2(#{<<>> => {}}), + 37468437 = erlang:phash2(#{{} => <<>>}), + 44049159 = erlang:phash2(#{<<>> => {}}), M0 = #{ a => 1, "key" => <<"value">> }, M1 = map:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, - 67968757 = erlang:phash2(M0), - 76038729 = erlang:phash2(M1), - 67968757 = erlang:phash2(M2), + 118679416 = erlang:phash2(M0), + 51612236 = erlang:phash2(M1), + 118679416 = erlang:phash2(M2), ok. t_bif_erlang_phash() -> -- cgit v1.2.3 From f00675d3c682f53824e23f1599cbb09ff2d57daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 9 Oct 2013 16:37:58 +0200 Subject: stdlib: Deny variables as keys and disallow ':=' in map construction In the current iteration of Maps we should deny *any* variables in Map keys. --- erts/emulator/test/map_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 1f4476defa..327e9b8060 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -327,7 +327,7 @@ guard_receive_loop() -> t_list_comprehension(Config) when is_list(Config) -> - [#{k:=1},#{k:=2},#{k:=3}] = [#{k:=I} || I <- [1,2,3]], + [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]], ok. t_guard_fun(Config) when is_list(Config) -> -- cgit v1.2.3 From bbdec77e0749a81f5e1e94a00631249cbb420fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Oct 2013 19:40:25 +0200 Subject: erts: Remove erlang:size/1 test from map_SUITE --- erts/emulator/test/map_SUITE.erl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 327e9b8060..3b52295039 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -28,7 +28,8 @@ t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, t_map_sort_literals/1, - t_size/1, t_map_size/1, + %t_size/1, + t_map_size/1, %% Specific Map BIFs t_bif_map_get/1, @@ -67,7 +68,9 @@ all() -> [ t_bif_map_to_list, t_bif_map_from_list, %% erlang - t_erlang_hash, t_map_encode_decode + t_erlang_hash, t_map_encode_decode, + %t_size, + t_map_size ]. groups() -> []. @@ -115,14 +118,15 @@ t_build_and_match_literals(Config) when is_list(Config) -> ok. %% Tests size(Map). +%% not implemented, perhaps it shouldn't be either -t_size(Config) when is_list(Config) -> +%t_size(Config) when is_list(Config) -> % 0 = size(#{}), % 1 = size(#{a=>1}), % 1 = size(#{a=>#{a=>1}}), % 2 = size(#{a=>1, b=>2}), % 3 = size(#{a=>1, b=>2, b=>"3"}), - ok. +% ok. t_map_size(Config) when is_list(Config) -> 0 = map_size(id(#{})), @@ -132,10 +136,12 @@ t_map_size(Config) when is_list(Config) -> 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})), true = map_is_size(#{a=>1}, 1), - true = map_is_size(#{a=>1, a=>2}, 2), + true = map_is_size(#{a=>1, a=>2}, 1), M = #{ "a" => 1, "b" => 2}, + true = map_is_size(M, 2), + false = map_is_size(M, 3), true = map_is_size(M#{ "a" => 2}, 2), - false = map_is_size(M#{ "a" => 2}, 3), + false = map_is_size(M#{ "c" => 2}, 2), %% Error cases. {'EXIT',{badarg,_}} = (catch map_size([])), -- cgit v1.2.3 From 72146c6675aaff02b2452c2fd2026c111e641f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 11 Oct 2013 18:11:12 +0200 Subject: erts: Specs for Map BIFs --- erts/preloaded/src/erlang.erl | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index ee5bd3e515..ff0f9d0ead 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -60,6 +60,7 @@ -export_type([timestamp/0]). -type ext_binary() :: binary(). +-type map() :: term(). %% FIXME: remove when handled internally. -type timestamp() :: {MegaSecs :: non_neg_integer(), Secs :: non_neg_integer(), MicroSecs :: non_neg_integer()}. @@ -104,10 +105,9 @@ -export([list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1]). -export([list_to_integer/1, list_to_integer/2]). -export([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([localtime/0, make_ref/0, map_size/1, 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([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]). @@ -128,7 +128,7 @@ -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_list/1, is_map/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, @@ -1149,6 +1149,12 @@ localtime() -> make_ref() -> erlang:nif_error(undefined). +%% Shadowed by erl_bif_types: erlang:map_size/1 +-spec map_size(Map) -> non_neg_integer() when + Map :: map(). +map_size(_Map) -> + erlang:nif_error(undefined). + %% match_spec_test/3 -spec erlang:match_spec_test(P1, P2, P3) -> TestResult when P1 :: [term()] | tuple(), @@ -1739,6 +1745,12 @@ is_number(_Term) -> is_pid(_Term) -> erlang:nif_error(undefined). +%% Shadowed by erl_bif_types: erlang:is_map/1 +-spec is_map(Map) -> boolean() when + Map :: map(). +is_map(_Map) -> + erlang:nif_error(undefined). + %% Shadowed by erl_bif_types: erlang:is_port/1 -spec is_port(Term) -> boolean() when Term :: term(). -- cgit v1.2.3 From 0cc9753f7f873bbcf8a528e05ab0adbd5c8fc79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 27 Jan 2014 13:10:54 +0100 Subject: Change the default file name encoding mode to +fnaw --- erts/doc/src/erl.xml | 14 +++++++++++--- erts/emulator/sys/common/erl_sys_common_misc.c | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index e737727941..6428a24209 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -535,12 +535,15 @@ -

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.

+

The VM works with file names as if they are encoded using the ISO-latin-1 encoding, disallowing Unicode characters with codepoints beyond 255.

See STDLIB User's Guide for more infomation about unicode file names.

-

The VM works with file names as if they are encoded using UTF-8 (or some other system specific Unicode encoding). This is the default on operating systems that enforce Unicode encoding, i.e. Windows and MacOSX.

+

The VM works with file names as if they are encoded using + UTF-8 (or some other system specific Unicode encoding). This + is the default on operating systems that enforce Unicode + encoding, i.e. Windows and MacOS X.

The +fnu switch can be followed by w, i, or e to control the way wrongly encoded file names are to be reported. w means that a warning is @@ -556,7 +559,12 @@ -

Selection between +fnl and +fnu is done based on the current locale settings in the OS, meaning that if you have set your terminal for UTF-8 encoding, the filesystem is expected to use the same encoding for file names (use with care).

+

Selection between +fnl and +fnu is done based + on the current locale settings in the OS, meaning that if you + have set your terminal for UTF-8 encoding, the filesystem is + expected to use the same encoding for file names. This is + default on all operating systems except MacOS X and + Windows.

The +fna switch can be followed by w, i, or e. This will have effect if the locale settings cause the behavior of +fnu to be selected. diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c index 31ad3b82d5..e3ba741058 100644 --- a/erts/emulator/sys/common/erl_sys_common_misc.c +++ b/erts/emulator/sys/common/erl_sys_common_misc.c @@ -52,7 +52,7 @@ static int filename_warning = ERL_FILENAME_WARNING_WARNING; /* Default unicode on windows and MacOS X */ static int user_filename_encoding = ERL_FILENAME_UTF8; #else -static int user_filename_encoding = ERL_FILENAME_LATIN1; +static int user_filename_encoding = ERL_FILENAME_UNKNOWN; #endif /* This controls the heuristic in printing characters in shell and w/ io:format("~tp", ...) etc. */ -- cgit v1.2.3 From 457b29c9ddd2b338e80916a5e7bad8dfe4b36ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 25 Oct 2013 16:28:50 +0200 Subject: erts,stdlib: Teach matchspec compiler map guards --- erts/emulator/beam/erl_db_util.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index d903053aa4..3986ccd4d3 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -565,6 +565,12 @@ static DMCGuardBif guard_tab[] = 1, DBIF_ALL }, + { + am_is_map, + &is_map_1, + 1, + DBIF_ALL + }, { am_is_binary, &is_binary_1, @@ -631,6 +637,12 @@ static DMCGuardBif guard_tab[] = 1, DBIF_ALL }, + { + am_map_size, + &map_size_1, + 1, + DBIF_ALL + }, { am_bit_size, &bit_size_1, -- cgit v1.2.3 From e1095860776d76294c1577079b2f24e0688c8e04 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 25 Oct 2013 14:38:50 +0200 Subject: Add tests for pditc, ets and tracing --- erts/emulator/test/map_SUITE.erl | 143 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 3b52295039..ea3483d09e 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -45,13 +45,18 @@ %% erlang t_erlang_hash/1, - t_map_encode_decode/1 - ]). + t_map_encode_decode/1, --include_lib("test_server/include/test_server.hrl"). + %% misc + t_pdict/1, + t_ets/1, + t_dets/1, + t_tracing/1 + ]). +-include_lib("stdlib/include/ms_transform.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> []. all() -> [ t_build_and_match_literals, @@ -70,7 +75,12 @@ all() -> [ %% erlang t_erlang_hash, t_map_encode_decode, %t_size, - t_map_size + t_map_size, + + %% Other functions + t_pdict, + t_ets, + t_tracing ]. groups() -> []. @@ -760,6 +770,129 @@ t_bif_map_from_list(Config) when is_list(Config) -> {'EXIT', {badarg,_}} = (catch map:from_list(id(42))), ok. +%% MISC +t_pdict(_Config) -> + + put(#{ a => b, b => a},#{ c => d}), + put(get(#{ a => b, b => a}),1), + 1 = get(#{ c => d}), + #{ c := d } = get(#{ a => b, b => a}). + +t_ets(_Config) -> + + Tid = ets:new(map_table,[]), + + [ets:insert(Tid,{maps:from_list([{I,-I}]),I}) || I <- lists:seq(1,100)], + + + [{#{ 2 := -2},2}] = ets:lookup(Tid,#{ 2 => -2 }), + + %% Test equal + [3,4] = lists:sort( + ets:select(Tid,[{{'$1','$2'}, + [{'or',{'==','$1',#{ 3 => -3 }}, + {'==','$1',#{ 4 => -4 }}}], + ['$2']}])), + %% Test match + [30,50] = lists:sort( + ets:select(Tid, + [{{#{ 30 => -30}, '$1'},[],['$1']}, + {{#{ 50 => -50}, '$1'},[],['$1']}] + )), + + ets:insert(Tid,{#{ a => b, b => c, c => a},transitivity}), + + %% Test equal with map of different size + [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]), + + %% Test match with map of different size + [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]), + + %% Test match with don't care value + [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]), + + %% Test is_map bif + ets:insert(Tid,{not_a_map,2}), + 100 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$_']}])), + + %% Test map_size bif + [3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}], + [{map_size,'$1'}]}]), + + true = ets:delete(Tid,#{50 => -50}), + [] = ets:lookup(Tid,#{50 => -50}), + + ets:delete(Tid), + ok. + +t_dets(_Config) -> + ok. + +t_tracing(_Config) -> + + dbg:stop_clear(), + {ok,Tracer} = dbg:tracer(process,{fun trace_collector/2, self()}), + dbg:p(self(),c), + + %% Test basic map call + {ok,_} = dbg:tpl(?MODULE,id,x), + id(#{ a => b }), + {trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer), + {trace,_,return_from,{?MODULE,id,1},#{ a := b }} = getmsg(Tracer), + dbg:ctpl(), + + %% Test equals in argument list + {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==','$1',#{ b => c}}], + [{return_trace}]}]), + id(#{ a => b }), + id(#{ b => c }), + {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer), + {trace,_,return_from,{?MODULE,id,1},#{ b := c }} = getmsg(Tracer), + dbg:ctpl(), + + %% Test match in head + {ok,_} = dbg:tpl(?MODULE,id,[{[#{b => c}],[],[]}]), + id(#{ a => b }), + id(#{ b => c }), + {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer), + dbg:ctpl(), + + %% Test map guard bifs + {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'or',{is_map,{element,1,'$1'}}, + {'==',{map_size,'$1'},2}}],[]}]), + id(#{ a => b }), + id({1,2}), + id({#{ a => b},2}), + id(#{ a => b, b => c}), + {trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer), + {trace,_,call,{?MODULE,id,[#{ a := b, b := c }]}} = getmsg(Tracer), + dbg:ctpl(), + + %% Test fun2ms, DOES NOT COMPILE!! + %MS = dbg:fun2ms(fun([A]) when A == #{ a => b} -> ok end), + %dbg:tpl(?MODULE,id,MS), + %id(#{ a => b }), + %id(#{ b => c }), + %{trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer), + %dbg:ctpl(), + + %% Check to extra messages + timeout = getmsg(Tracer), + + dbg:stop_clear(), + ok. + +getmsg(_Tracer) -> + receive + V -> V + after 50 -> + timeout + end. + +trace_collector(Msg,Parent) -> + io:format("~p~n",[Msg]), + Parent ! Msg, + Parent. %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. -- cgit v1.2.3 From f8345dc86a1787c3c8deb06befbb697f37db1c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 25 Oct 2013 17:34:19 +0200 Subject: erts: Fix map_SUITE match spec tests --- erts/emulator/test/map_SUITE.erl | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index ea3483d09e..69b68c4b67 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -806,14 +806,17 @@ t_ets(_Config) -> [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]), %% Test match with map of different size - [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]), + %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]), - %% Test match with don't care value - [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]), + %%% Test match with don't care value + %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]), %% Test is_map bif + 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), ets:insert(Tid,{not_a_map,2}), - 100 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$_']}])), + 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), + ets:insert(Tid,{{nope,a,tuple},2}), + 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), %% Test map_size bif [3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}], @@ -857,18 +860,22 @@ t_tracing(_Config) -> {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer), dbg:ctpl(), - %% Test map guard bifs - {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'or',{is_map,{element,1,'$1'}}, - {'==',{map_size,'$1'},2}}],[]}]), + % Test map guard bifs + {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{is_map,{element,1,'$1'}}],[]}]), id(#{ a => b }), id({1,2}), id({#{ a => b},2}), - id(#{ a => b, b => c}), {trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer), - {trace,_,call,{?MODULE,id,[#{ a := b, b := c }]}} = getmsg(Tracer), dbg:ctpl(), - %% Test fun2ms, DOES NOT COMPILE!! + {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==',{map_size,{element,1,'$1'}},2}],[]}]), + id(#{ a => b }), + id({1,2}), + id({#{ a => b},2}), + id({#{ a => b, b => c},atom}), + {trace,_,call,{?MODULE,id,[{#{ a := b, b := c },atom}]}} = getmsg(Tracer), + dbg:ctpl(), + %MS = dbg:fun2ms(fun([A]) when A == #{ a => b} -> ok end), %dbg:tpl(?MODULE,id,MS), %id(#{ a => b }), @@ -883,11 +890,7 @@ t_tracing(_Config) -> ok. getmsg(_Tracer) -> - receive - V -> V - after 50 -> - timeout - end. + receive V -> V after 100 -> timeout end. trace_collector(Msg,Parent) -> io:format("~p~n",[Msg]), -- cgit v1.2.3 From 6fdad74f41803089a0f9026c98f319daecda9a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 24 Oct 2013 19:06:34 +0200 Subject: erts,stdlib: Change map module name to maps Name conforms to EEP. --- erts/emulator/beam/bif.tab | 24 ++-- erts/emulator/beam/erl_map.c | 82 ++++++------- erts/emulator/test/map_SUITE.erl | 248 +++++++++++++++++++-------------------- 3 files changed, 177 insertions(+), 177 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 306861a4c5..b623e47b9a 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -580,18 +580,18 @@ bif re:inspect/2 ubif erlang:is_map/1 ubif erlang:map_size/1 -bif map:to_list/1 -bif map:find/2 -bif map:get/2 -bif map:from_list/1 -bif map:is_key/2 -bif map:keys/1 -bif map:merge/2 -bif map:new/0 -bif map:put/3 -bif map:remove/2 -bif map:update/3 -bif map:values/1 +bif maps:to_list/1 +bif maps:find/2 +bif maps:get/2 +bif maps:from_list/1 +bif maps:is_key/2 +bif maps:keys/1 +bif maps:merge/2 +bif maps:new/0 +bif maps:put/3 +bif maps:remove/2 +bif maps:update/3 +bif maps:values/1 # # Obsolete diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 6fd60f0689..27734276f9 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -38,25 +38,25 @@ * - erlang:is_map/1 * - erlang:map_size/1 * - * - map:find/2 - * - map:from_list/1 - * - map:get/2 - * - map:is_key/2 - * - map:keys/1 - * - map:merge/2 - * - map:new/0 - * - map:put/3 - * - map:remove/2 - * - map:to_list/1 - * - map:update/3 - * - map:values/1 + * - maps:find/2 + * - maps:from_list/1 + * - maps:get/2 + * - maps:is_key/2 + * - maps:keys/1 + * - maps:merge/2 + * - maps:new/0 + * - maps:put/3 + * - maps:remove/2 + * - maps:to_list/1 + * - maps:update/3 + * - maps:values/1 * * TODO: - * - map:foldl/3 - * - map:foldr/3 - * - map:map/3 - * - map:size/1 - * - map:without/2 + * - maps:foldl/3 + * - maps:foldr/3 + * - maps:map/3 + * - maps:size/1 + * - maps:without/2 * */ @@ -80,10 +80,10 @@ BIF_RETTYPE map_size_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } -/* map:to_list/1 +/* maps:to_list/1 */ -BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { +BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Uint n; Eterm* hp; @@ -107,11 +107,11 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } -/* map:find/2 +/* maps:find/2 * return value if key *equals* a key in the map */ -BIF_RETTYPE map_find_2(BIF_ALIST_2) { +BIF_RETTYPE maps_find_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Eterm *hp, *ks,*vs, key, res; map_t *mp; @@ -137,12 +137,12 @@ BIF_RETTYPE map_find_2(BIF_ALIST_2) { } BIF_ERROR(BIF_P, BADARG); } -/* map:get/2 +/* maps:get/2 * return value if key *matches* a key in the map * exception bad_key if none matches */ -BIF_RETTYPE map_get_2(BIF_ALIST_2) { +BIF_RETTYPE maps_get_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Eterm *hp, *ks,*vs, key, error; map_t *mp; @@ -184,11 +184,11 @@ error: BIF_ERROR(BIF_P, BADARG); } -/* map:from_list/1 +/* maps:from_list/1 * List may be unsorted [{K,V}] */ -BIF_RETTYPE map_from_list_1(BIF_ALIST_1) { +BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) { Eterm *kv, item = BIF_ARG_1; Eterm *hp, *thp,*vs, *ks, keys, res; map_t *mp; @@ -305,10 +305,10 @@ error: BIF_ERROR(BIF_P, BADARG); } -/* map:is_key/2 +/* maps:is_key/2 */ -BIF_RETTYPE map_is_key_2(BIF_ALIST_2) { +BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Eterm *ks, key; map_t *mp; @@ -340,10 +340,10 @@ BIF_RETTYPE map_is_key_2(BIF_ALIST_2) { BIF_ERROR(BIF_P, BADARG); } -/* map:keys/1 +/* maps:keys/1 */ -BIF_RETTYPE map_keys_1(BIF_ALIST_1) { +BIF_RETTYPE maps_keys_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Eterm *hp, *ks, res = NIL; map_t *mp; @@ -366,10 +366,10 @@ BIF_RETTYPE map_keys_1(BIF_ALIST_1) { } BIF_ERROR(BIF_P, BADARG); } -/* map:merge/2 +/* maps:merge/2 */ -BIF_RETTYPE map_merge_2(BIF_ALIST_2) { +BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { if (is_map(BIF_ARG_1) && is_map(BIF_ARG_2)) { Eterm *hp,*thp; Eterm tup; @@ -452,10 +452,10 @@ BIF_RETTYPE map_merge_2(BIF_ALIST_2) { } BIF_ERROR(BIF_P, BADARG); } -/* map:new/2 +/* maps:new/2 */ -BIF_RETTYPE map_new_0(BIF_ALIST_0) { +BIF_RETTYPE maps_new_0(BIF_ALIST_0) { Eterm* hp; Eterm tup; map_t *mp; @@ -472,10 +472,10 @@ BIF_RETTYPE map_new_0(BIF_ALIST_0) { BIF_RET(make_map(mp)); } -/* map:put/3 +/* maps:put/3 */ -BIF_RETTYPE map_put_3(BIF_ALIST_3) { +BIF_RETTYPE maps_put_3(BIF_ALIST_3) { if (is_map(BIF_ARG_3)) { Sint n,i; Sint c = 0; @@ -582,10 +582,10 @@ BIF_RETTYPE map_put_3(BIF_ALIST_3) { BIF_ERROR(BIF_P, BADARG); } -/* map:remove/3 +/* maps:remove/3 */ -BIF_RETTYPE map_remove_2(BIF_ALIST_2) { +BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Sint n; Sint found = 0; @@ -656,10 +656,10 @@ BIF_RETTYPE map_remove_2(BIF_ALIST_2) { BIF_ERROR(BIF_P, BADARG); } -/* map:update/3 +/* maps:update/3 */ -BIF_RETTYPE map_update_3(BIF_ALIST_3) { +BIF_RETTYPE maps_update_3(BIF_ALIST_3) { if (is_map(BIF_ARG_3)) { Sint n,i; Sint found = 0; @@ -719,10 +719,10 @@ BIF_RETTYPE map_update_3(BIF_ALIST_3) { } -/* map:values/1 +/* maps:values/1 */ -BIF_RETTYPE map_values_1(BIF_ALIST_1) { +BIF_RETTYPE maps_values_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Eterm *hp, *vs, res = NIL; map_t *mp; diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 69b68c4b67..59fdb82f50 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -412,182 +412,182 @@ t_map_sort_literals(Config) when is_list(Config) -> %% BIFs t_bif_map_get(Config) when is_list(Config) -> - 1 = map:get(a, #{ a=> 1}), - 2 = map:get(b, #{ a=> 1, b => 2}), - "hi" = map:get("hello", #{ a=>1, "hello" => "hi"}), - "tuple hi" = map:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), + 1 = maps:get(a, #{ a=> 1}), + 2 = maps:get(b, #{ a=> 1, b => 2}), + "hi" = maps:get("hello", #{ a=>1, "hello" => "hi"}), + "tuple hi" = maps:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), - "v4" = map:get(<<"k2">>, M#{ <<"k2">> => "v4" }), + "v4" = maps:get(<<"k2">>, M#{ <<"k2">> => "v4" }), %% error case - {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,[])), - {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,<<>>)), - {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get({1,1}, #{{1,1.0} => "tuple"})), - {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{})), - {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{ b=>1, c=>2})), + {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,[])), + {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,<<>>)), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{})), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{ b=>1, c=>2})), ok. t_bif_map_find(Config) when is_list(Config) -> - {ok, 1} = map:find(a, #{ a=> 1}), - {ok, 2} = map:find(b, #{ a=> 1, b => 2}), - {ok, "int"} = map:find(1, #{ 1 => "int"}), - {ok, "int"} = map:find(1.0, #{ 1 => "int"}), - {ok, "float"} = map:find(1, #{ 1.0 => "float"}), - {ok, "float"} = map:find(1.0, #{ 1.0=> "float"}), + {ok, 1} = maps:find(a, #{ a=> 1}), + {ok, 2} = maps:find(b, #{ a=> 1, b => 2}), + {ok, "int"} = maps:find(1, #{ 1 => "int"}), + {ok, "int"} = maps:find(1.0, #{ 1 => "int"}), + {ok, "float"} = maps:find(1, #{ 1.0 => "float"}), + {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}), - {ok, "hi"} = map:find("hello", #{ a=>1, "hello" => "hi"}), - {ok, "tuple hi"} = map:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}), + {ok, "tuple hi"} = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), - {ok, "v4"} = map:find(<<"k2">>, M#{ <<"k2">> => "v4" }), + {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }), %% error case - error = map:find(a,#{}), - error = map:find(a,#{b=>1, c=>2}), + error = maps:find(a,#{}), + error = maps:find(a,#{b=>1, c=>2}), - {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,[])), - {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,<<>>)), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)), ok. t_bif_map_is_key(Config) when is_list(Config) -> M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, - true = map:is_key("hi", M1), - true = map:is_key(int, M1), - true = map:is_key(<<"key">>, M1), - true = map:is_key(4, M1), - - false = map:is_key(5, M1), - false = map:is_key(<<"key2">>, M1), - false = map:is_key("h", M1), - false = map:is_key("hello", M1), - false = map:is_key(atom, M1), - - false = map:is_key("hi", map:remove("hi", M1)), - true = map:is_key("hi", M1), - true = map:is_key(1, map:put(1, "number", M1)), - false = map:is_key(1.0, map:put(1, "number", M1)), + true = maps:is_key("hi", M1), + true = maps:is_key(int, M1), + true = maps:is_key(<<"key">>, M1), + true = maps:is_key(4, M1), + + false = maps:is_key(5, M1), + false = maps:is_key(<<"key2">>, M1), + false = maps:is_key("h", M1), + false = maps:is_key("hello", M1), + false = maps:is_key(atom, M1), + + false = maps:is_key("hi", maps:remove("hi", M1)), + true = maps:is_key("hi", M1), + true = maps:is_key(1, maps:put(1, "number", M1)), + false = maps:is_key(1.0, maps:put(1, "number", M1)), ok. t_bif_map_keys(Config) when is_list(Config) -> - [] = map:keys(#{}), + [] = maps:keys(#{}), - [1,2,3,4,5] = map:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), - [1,2,3,4,5] = map:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + [1,2,3,4,5] = maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [1,2,3,4,5] = maps:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), % values in key order: [4,int,"hi",<<"key">>] M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, - [4,int,"hi",<<"key">>] = map:keys(M1), + [4,int,"hi",<<"key">>] = maps:keys(M1), %% error case - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(154)), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(atom)), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys([])), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(<<>>)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(154)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(atom)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys([])), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(<<>>)), ok. t_bif_map_new(Config) when is_list(Config) -> - #{} = map:new(), - 0 = erlang:map_size(map:new()), + #{} = maps:new(), + 0 = erlang:map_size(maps:new()), ok. t_bif_map_put(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, - M1 = #{ "hi" := "hello"} = map:put("hi", "hello", #{}), + M1 = #{ "hi" := "hello"} = maps:put("hi", "hello", #{}), - ["hi"] = map:keys(M1), - ["hello"] = map:values(M1), + ["hi"] = maps:keys(M1), + ["hello"] = maps:values(M1), - M2 = #{ int := 3 } = map:put(int, 3, M1), + M2 = #{ int := 3 } = maps:put(int, 3, M1), - [int,"hi"] = map:keys(M2), - [3,"hello"] = map:values(M2), + [int,"hi"] = maps:keys(M2), + [3,"hello"] = maps:values(M2), - M3 = #{ <<"key">> := <<"value">> } = map:put(<<"key">>, <<"value">>, M2), + M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2), - [int,"hi",<<"key">>] = map:keys(M3), - [3,"hello",<<"value">>] = map:values(M3), + [int,"hi",<<"key">>] = maps:keys(M3), + [3,"hello",<<"value">>] = maps:values(M3), - M4 = #{ 18446744073709551629 := wat } = map:put(18446744073709551629, wat, M3), + M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3), - [18446744073709551629,int,"hi",<<"key">>] = map:keys(M4), - [wat,3,"hello",<<"value">>] = map:values(M4), + [18446744073709551629,int,"hi",<<"key">>] = maps:keys(M4), + [wat,3,"hello",<<"value">>] = maps:values(M4), - M0 = #{ 4 := number } = M5 = map:put(4, number, M4), + M0 = #{ 4 := number } = M5 = maps:put(4, number, M4), - [4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5), - [number,wat,3,"hello",<<"value">>] = map:values(M5), + [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5), + [number,wat,3,"hello",<<"value">>] = maps:values(M5), %% error case - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,154)), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,atom)), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,[])), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,<<>>)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,atom)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,[])), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)), ok. t_bif_map_remove(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, - M1 = map:remove("hi", M0), - [4,18446744073709551629,int,<<"key">>] = map:keys(M1), - [number,wat,3,<<"value">>] = map:values(M1), + M1 = maps:remove("hi", M0), + [4,18446744073709551629,int,<<"key">>] = maps:keys(M1), + [number,wat,3,<<"value">>] = maps:values(M1), - M2 = map:remove(int, M1), - [4,18446744073709551629,<<"key">>] = map:keys(M2), - [number,wat,<<"value">>] = map:values(M2), + M2 = maps:remove(int, M1), + [4,18446744073709551629,<<"key">>] = maps:keys(M2), + [number,wat,<<"value">>] = maps:values(M2), - M3 = map:remove(<<"key">>, M2), - [4,18446744073709551629] = map:keys(M3), - [number,wat] = map:values(M3), + M3 = maps:remove(<<"key">>, M2), + [4,18446744073709551629] = maps:keys(M3), + [number,wat] = maps:values(M3), - M4 = map:remove(18446744073709551629, M3), - [4] = map:keys(M4), - [number] = map:values(M4), + M4 = maps:remove(18446744073709551629, M3), + [4] = maps:keys(M4), + [number] = maps:values(M4), - M5 = map:remove(4, M4), - [] = map:keys(M5), - [] = map:values(M5), + M5 = maps:remove(4, M4), + [] = maps:keys(M5), + [] = maps:values(M5), - M0 = map:remove(5,M0), - M0 = map:remove("hi there",M0), + M0 = maps:remove(5,M0), + M0 = maps:remove("hi there",M0), - #{ "hi" := "hello", int := 3, 4 := number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), + #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)), %% error case - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,154)), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,atom)), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,[])), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,<<>>)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,154)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,atom)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,[])), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)), ok. t_bif_map_values(Config) when is_list(Config) -> - [] = map:values(#{}), + [] = maps:values(#{}), - [a,b,c,d,e] = map:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), - [a,b,c,d,e] = map:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + [a,b,c,d,e] = maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [a,b,c,d,e] = maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), % values in key order: [4,int,"hi",<<"key">>] M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> }, - [number,3,"hello2",<<"value2">>] = map:values(M2), - [number,3,"hello",<<"value">>] = map:values(M1), + [number,3,"hello2",<<"value2">>] = maps:values(M2), + [number,3,"hello",<<"value">>] = maps:values(M1), %% error case - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)), - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])), - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(atom)), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values([])), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(<<>>)), ok. t_erlang_hash(Config) when is_list(Config) -> @@ -610,7 +610,7 @@ t_bif_erlang_phash2() -> 44049159 = erlang:phash2(#{<<>> => {}}), M0 = #{ a => 1, "key" => <<"value">> }, - M1 = map:remove("key",M0), + M1 = maps:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, 118679416 = erlang:phash2(M0), @@ -630,7 +630,7 @@ t_bif_erlang_phash() -> 1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken M0 = #{ a => 1, "key" => <<"value">> }, - M1 = map:remove("key",M0), + M1 = maps:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, 3590546636 = erlang:phash(M0,Sz), @@ -650,7 +650,7 @@ t_bif_erlang_hash() -> 101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken M0 = #{ a => 1, "key" => <<"value">> }, - M1 = map:remove("key",M0), + M1 = maps:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, 38260486 = erlang:hash(M0,Sz), @@ -706,7 +706,7 @@ t_map_encode_decode(Config) when is_list(Config) -> ok. map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> - M1 = map:put(K,V,M0), + M1 = maps:put(K,V,M0), B0 = erlang:term_to_binary(M1), Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), %% sort Ks and Vs according to term spec, then match it @@ -729,45 +729,45 @@ match_encoded_map(Bin,[<<131,Item/binary>>|Items]) -> t_bif_map_to_list(Config) when is_list(Config) -> - [] = map:to_list(#{}), - [{a,1},{b,2}] = map:to_list(#{a=>1,b=>2}), - [{a,1},{b,2},{c,3}] = map:to_list(#{c=>3,a=>1,b=>2}), - [{a,1},{b,2},{g,3}] = map:to_list(#{g=>3,a=>1,b=>2}), - [{a,1},{b,2},{g,3},{"c",4}] = map:to_list(#{g=>3,a=>1,b=>2,"c"=>4}), - [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = map:to_list(#{ + [] = maps:to_list(#{}), + [{a,1},{b,2}] = maps:to_list(#{a=>1,b=>2}), + [{a,1},{b,2},{c,3}] = maps:to_list(#{c=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3}] = maps:to_list(#{g=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3},{"c",4}] = maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4}), + [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = maps:to_list(#{ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}), - [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = map:to_list(#{ + [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = maps:to_list(#{ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5, <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10}), %% error cases - {'EXIT', {badarg,_}} = (catch map:to_list(id(a))), - {'EXIT', {badarg,_}} = (catch map:to_list(id(42))), + {'EXIT', {badarg,_}} = (catch maps:to_list(id(a))), + {'EXIT', {badarg,_}} = (catch maps:to_list(id(42))), ok. t_bif_map_from_list(Config) when is_list(Config) -> - #{} = map:from_list([]), - A = map:from_list([]), + #{} = maps:from_list([]), + A = maps:from_list([]), 0 = erlang:map_size(A), - #{a:=1,b:=2} = map:from_list([{a,1},{b,2}]), - #{c:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{c,3}]), - #{g:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{g,3}]), + #{a:=1,b:=2} = maps:from_list([{a,1},{b,2}]), + #{c:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{c,3}]), + #{g:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{g,3}]), - #{a:=2} = map:from_list([{a,1},{a,3},{a,2}]), + #{a:=2} = maps:from_list([{a,1},{a,3},{a,2}]), #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} = - map:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]), + maps:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]), #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} = - map:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2}, + maps:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2}, {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]), %% error cases - {'EXIT', {badarg,_}} = (catch map:from_list(id(a))), - {'EXIT', {badarg,_}} = (catch map:from_list(id(42))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), ok. %% MISC -- cgit v1.2.3 From 7e42aefdecae6cc91bdaaef21924b4e7cb75861c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 30 Oct 2013 16:09:29 +0100 Subject: erts: Add tests for non BIFs in Maps module * maps:without/2 * maps:foldl/3 * maps:foldr/3 * maps:map/2 * maps:size/1 --- erts/emulator/test/map_SUITE.erl | 55 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 59fdb82f50..b6cfd668f1 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -47,6 +47,12 @@ t_erlang_hash/1, t_map_encode_decode/1, + %% maps module not bifs + t_maps_fold/1, + t_maps_map/1, + t_maps_size/1, + t_maps_without/1, + %% misc t_pdict/1, t_ets/1, @@ -74,9 +80,13 @@ all() -> [ %% erlang t_erlang_hash, t_map_encode_decode, - %t_size, t_map_size, + %% maps module + t_maps_fold, t_maps_map, + t_maps_size, t_maps_without, + + %% Other functions t_pdict, t_ets, @@ -770,6 +780,49 @@ t_bif_map_from_list(Config) when is_list(Config) -> {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), ok. +%% Maps module, not BIFs +t_maps_fold(_Config) -> + + Vs = lists:seq(1,100), + Rs = lists:reverse(Vs), + M = maps:from_list([{{k,I},{v,I}}||I<-Vs]), + + %% foldl + 5050 = maps:foldl(fun({k,_},{v,V},A) -> V + A end, 0, M), + Rs = maps:foldl(fun({k,_},{v,V},A) -> [V|A] end, [], M), + + %% foldr + 5050 = maps:foldr(fun({k,_},{v,V},A) -> V + A end, 0, M), + Vs = maps:foldr(fun({k,_},{v,V},A) -> [V|A] end, [], M), + + ok. + +t_maps_map(_Config) -> + Vs = lists:seq(1,100), + M1 = maps:from_list([{I,I}||I<-Vs]), + M2 = maps:from_list([{I,{token,I}}||I<-Vs]), + + M2 = maps:map(fun(_K,V) -> {token,V} end, M1), + ok. + +t_maps_size(_Config) -> + Vs = lists:seq(1,100), + lists:foldl(fun(I,M) -> + M1 = maps:put(I,I,M), + I = maps:size(M1), + M1 + end, #{}, Vs), + ok. + + +t_maps_without(_Config) -> + Ki = [11,22,33,44,55,66,77,88,99], + M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]), + M1 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100) -- Ki]), + M1 = maps:without([{k,I}||I <- Ki],M0), + ok. + + %% MISC t_pdict(_Config) -> -- cgit v1.2.3 From 105dcb3eb7c6d5fd07cd76cd495230b0a1d1e7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 30 Oct 2013 17:05:43 +0100 Subject: erts: Add tests for Map update on expressions (foo())#{ k1 := V1, k2 => V2 } --- erts/emulator/test/map_SUITE.erl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index b6cfd668f1..a06cb43ac7 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -23,6 +23,7 @@ -export([ t_build_and_match_literals/1, t_update_literals/1,t_match_and_update_literals/1, + t_update_map_expressions/1, t_update_assoc/1,t_update_exact/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, t_guard_receive/1, t_guard_fun/1, @@ -67,6 +68,7 @@ suite() -> []. all() -> [ t_build_and_match_literals, t_update_literals, t_match_and_update_literals, + t_update_map_expressions, t_update_assoc,t_update_exact, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, @@ -137,6 +139,7 @@ t_build_and_match_literals(Config) when is_list(Config) -> {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), ok. + %% Tests size(Map). %% not implemented, perhaps it shouldn't be either @@ -163,7 +166,7 @@ t_map_size(Config) when is_list(Config) -> true = map_is_size(M#{ "a" => 2}, 2), false = map_is_size(M#{ "c" => 2}, 2), - %% Error cases. + %% Error cases. {'EXIT',{badarg,_}} = (catch map_size([])), {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), {'EXIT',{badarg,_}} = (catch map_size(1)), @@ -204,6 +207,22 @@ loop_match_and_update_literals_x_q(Map, []) -> Map; loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). + +t_update_map_expressions(Config) when is_list(Config) -> + M = maps:new(), + #{ a := 1 } = M#{a => 1}, + + #{ b := 2 } = (maps:new())#{ b => 2 }, + + #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 }, + #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 }, + + %% Error cases, FIXME: should be 'badmap'? + {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), + {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }), + ok. + + t_update_assoc(Config) when is_list(Config) -> M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), -- cgit v1.2.3 From b313f455be0def48b85c905efd98308aefb5bbc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 24 Oct 2013 14:40:12 +0200 Subject: preloaded: Remove bogus map type --- erts/preloaded/src/erlang.erl | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index ff0f9d0ead..fbc37bd955 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -60,7 +60,6 @@ -export_type([timestamp/0]). -type ext_binary() :: binary(). --type map() :: term(). %% FIXME: remove when handled internally. -type timestamp() :: {MegaSecs :: non_neg_integer(), Secs :: non_neg_integer(), MicroSecs :: non_neg_integer()}. -- cgit v1.2.3 From 38047efbbb91b02bd8894654458350b5a9813798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 Nov 2013 16:44:55 +0100 Subject: Update preloaded erlang.beam --- erts/preloaded/ebin/erlang.beam | Bin 98008 -> 98256 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index f38377647c..eb696bb32f 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ -- cgit v1.2.3 From ae2c2f1d838b523939b586014ceb66c2970b8cf2 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 28 Jan 2014 18:14:51 +0100 Subject: Update OTP version --- erts/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index 8e77a9a26e..88a393f3d5 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -22,7 +22,7 @@ VSN = 6.0 # OTP major version SYSTEM_VSN = 17 # OTP correction package version -SYSTEM_CP_VSN = 17.0-rc0 +SYSTEM_CP_VSN = 17.0-rc1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From e0eb6d5bafcebc1c24b0a538e50a1d55a3724f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 8 Nov 2013 15:34:05 +0100 Subject: erts: Add NIFs for Maps - int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM map) - int enif_get_map_size(ErlNifEnv *env, ERL_NIF_TERM, int*) - ERL_NIF_TERM enif_make_new_map(ErlNifEnv *env) - int enif_make_map_put(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out) - int enif_get_map_value(ErlNifEnv *env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value) - int enif_find_map_value(ErlNifEnv *env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value) - int enif_make_map_update(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out) - int enif_make_map_remove(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out) - int enif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter) - void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter) - int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) - int enif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value) --- erts/emulator/beam/erl_map.c | 443 ++++++++++++++++++--------------- erts/emulator/beam/erl_map.h | 6 + erts/emulator/beam/erl_nif.c | 209 ++++++++++++++++ erts/emulator/beam/erl_nif.h | 26 +- erts/emulator/beam/erl_nif_api_funcs.h | 33 +++ 5 files changed, 511 insertions(+), 206 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 27734276f9..98aeee634b 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -111,28 +111,39 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { * return value if key *equals* a key in the map */ -BIF_RETTYPE maps_find_2(BIF_ALIST_2) { - if (is_map(BIF_ARG_2)) { - Eterm *hp, *ks,*vs, key, res; - map_t *mp; - Uint n,i; +int erts_maps_find(Eterm key, Eterm map, Eterm *value) { - mp = (map_t*)map_val(BIF_ARG_2); - key = BIF_ARG_1; - n = map_get_size(mp); - ks = map_get_keys(mp); - vs = map_get_values(mp); + Eterm *ks,*vs; + map_t *mp; + Uint n,i; - for( i = 0; i < n; i++) { - if (CMP(ks[i], key)==0) { - hp = HAlloc(BIF_P, 3); - res = make_tuple(hp); - *hp++ = make_arityval(2); - *hp++ = am_ok; - *hp++ = vs[i]; - BIF_RET(res); - } + mp = (map_t*)map_val(map); + n = map_get_size(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); + + for( i = 0; i < n; i++) { + if (CMP(ks[i], key)==0) { + *value = vs[i]; + return 1; + } + } + return 0; +} + +BIF_RETTYPE maps_find_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp, value,res; + + if (erts_maps_find(BIF_ARG_1, BIF_ARG_2, &value)) { + hp = HAlloc(BIF_P, 3); + res = make_tuple(hp); + *hp++ = make_arityval(2); + *hp++ = am_ok; + *hp++ = value; + BIF_RET(res); } + BIF_RET(am_error); } BIF_ERROR(BIF_P, BADARG); @@ -142,43 +153,54 @@ BIF_RETTYPE maps_find_2(BIF_ALIST_2) { * exception bad_key if none matches */ -BIF_RETTYPE maps_get_2(BIF_ALIST_2) { - if (is_map(BIF_ARG_2)) { - Eterm *hp, *ks,*vs, key, error; - map_t *mp; - Uint n,i; - char *s_error; - mp = (map_t*)map_val(BIF_ARG_2); - key = BIF_ARG_1; - n = map_get_size(mp); - - if (n == 0) - goto error; +int erts_maps_get(Eterm key, Eterm map, Eterm *value) { + Eterm *ks,*vs; + map_t *mp; + Uint n,i; - ks = map_get_keys(mp); - vs = map_get_values(mp); + mp = (map_t*)map_val(map); + n = map_get_size(mp); - if (is_immed(key)) { - for( i = 0; i < n; i++) { - if (ks[i] == key) { - BIF_RET(vs[i]); - } - } - } + if (n == 0) + return 0; + + ks = map_get_keys(mp); + vs = map_get_values(mp); + if (is_immed(key)) { for( i = 0; i < n; i++) { - if (eq(ks[i], key)) { - BIF_RET(vs[i]); + if (ks[i] == key) { + *value = vs[i]; + return 1; } } -error: + } + + for( i = 0; i < n; i++) { + if (eq(ks[i], key)) { + *value = vs[i]; + return 1; + } + } + return 0; +} + +BIF_RETTYPE maps_get_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp; + Eterm value, error; + char *s_error; + + if (erts_maps_get(BIF_ARG_1, BIF_ARG_2, &value)) { + BIF_RET(value); + } s_error = "bad_key"; error = am_atom_put(s_error, sys_strlen(s_error)); hp = HAlloc(BIF_P, 3); - BIF_P->fvalue = TUPLE2(hp, error, key); + BIF_P->fvalue = TUPLE2(hp, error, BIF_ARG_1); BIF_ERROR(BIF_P, EXC_ERROR_2); } BIF_ERROR(BIF_P, BADARG); @@ -460,7 +482,7 @@ BIF_RETTYPE maps_new_0(BIF_ALIST_0) { Eterm tup; map_t *mp; - hp = HAlloc(BIF_P, (3 + 1)); + hp = HAlloc(BIF_P, (MAP_HEADER_SIZE + 1)); tup = make_tuple(hp); *hp++ = make_arityval(0); @@ -475,183 +497,197 @@ BIF_RETTYPE maps_new_0(BIF_ALIST_0) { /* maps:put/3 */ -BIF_RETTYPE maps_put_3(BIF_ALIST_3) { - if (is_map(BIF_ARG_3)) { - Sint n,i; - Sint c = 0; - Eterm* hp, *shp; - Eterm *ks,*vs, res, key, tup; - map_t *mp = (map_t*)map_val(BIF_ARG_3); - - key = BIF_ARG_1; - n = map_get_size(mp); - - if (n == 0) { - hp = HAlloc(BIF_P, 4 + 2); - tup = make_tuple(hp); - *hp++ = make_arityval(1); - *hp++ = key; - res = make_map(hp); - *hp++ = MAP_HEADER; - *hp++ = 1; - *hp++ = tup; - *hp++ = BIF_ARG_2; +Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { + Sint n,i; + Sint c = 0; + Eterm* hp, *shp; + Eterm *ks,*vs, res, tup; + map_t *mp = (map_t*)map_val(map); - BIF_RET(res); - } - - ks = map_get_keys(mp); - vs = map_get_values(mp); - /* only allocate for values, - * assume key-tuple will be intact - */ + n = map_get_size(mp); - hp = HAlloc(BIF_P, 3 + n); - shp = hp; /* save hp, used if optimistic update fails */ - res = make_map(hp); + if (n == 0) { + hp = HAlloc(p, MAP_HEADER_SIZE + 1 + 2); + tup = make_tuple(hp); + *hp++ = make_arityval(1); + *hp++ = key; + res = make_map(hp); *hp++ = MAP_HEADER; - *hp++ = n; - *hp++ = mp->keys; + *hp++ = 1; + *hp++ = tup; + *hp++ = value; - if (is_immed(key)) { - for( i = 0; i < n; i ++) { - if (ks[i] == key) { - *hp++ = BIF_ARG_2; - vs++; - c = 1; - } else { - *hp++ = *vs++; - } + return res; + } + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + /* only allocate for values, + * assume key-tuple will be intact + */ + + hp = HAlloc(p, MAP_HEADER_SIZE + n); + shp = hp; /* save hp, used if optimistic update fails */ + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n; + *hp++ = mp->keys; + + if (is_immed(key)) { + for( i = 0; i < n; i ++) { + if (ks[i] == key) { + *hp++ = value; + vs++; + c = 1; + } else { + *hp++ = *vs++; } - } else { - for( i = 0; i < n; i ++) { - if (eq(ks[i], key)) { - *hp++ = BIF_ARG_2; - vs++; - c = 1; - } else { - *hp++ = *vs++; - } + } + } else { + for( i = 0; i < n; i ++) { + if (eq(ks[i], key)) { + *hp++ = value; + vs++; + c = 1; + } else { + *hp++ = *vs++; } } + } - if (c) - BIF_RET(res); + if (c) + return res; - /* need to make a new tuple, - * use old hp since it needs to be recreated anyway. - */ - tup = make_tuple(shp); - *shp++ = make_arityval(n+1); + /* need to make a new tuple, + * use old hp since it needs to be recreated anyway. + */ + tup = make_tuple(shp); + *shp++ = make_arityval(n+1); - hp = HAlloc(BIF_P, 3 + n + 1); - res = make_map(hp); - *hp++ = MAP_HEADER; - *hp++ = n + 1; - *hp++ = tup; + hp = HAlloc(p, 3 + n + 1); + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n + 1; + *hp++ = tup; - ks = map_get_keys(mp); - vs = map_get_values(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); - ASSERT(n >= 0); + ASSERT(n >= 0); - /* copy map in order */ - while (n && ((c = CMP(*ks, key)) < 0)) { - *shp++ = *ks++; - *hp++ = *vs++; - n--; - } + /* copy map in order */ + while (n && ((c = CMP(*ks, key)) < 0)) { + *shp++ = *ks++; + *hp++ = *vs++; + n--; + } - *shp++ = key; - *hp++ = BIF_ARG_2; + *shp++ = key; + *hp++ = value; - ASSERT(n >= 0); + ASSERT(n >= 0); - while(n--) { - *shp++ = *ks++; - *hp++ = *vs++; - } - /* we have one word remaining - * this will work out fine once we get the size word - * in the header. - */ - *shp = make_pos_bignum_header(0); - BIF_RET(res); + while(n--) { + *shp++ = *ks++; + *hp++ = *vs++; } + /* we have one word remaining + * this will work out fine once we get the size word + * in the header. + */ + *shp = make_pos_bignum_header(0); + return res; +} +BIF_RETTYPE maps_put_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + BIF_RET(erts_maps_put(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3)); + } BIF_ERROR(BIF_P, BADARG); } /* maps:remove/3 */ -BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { - if (is_map(BIF_ARG_2)) { - Sint n; - Sint found = 0; - Uint need; - Eterm *thp, *mhp; - Eterm *ks, *vs, res, key,tup; - map_t *mp = (map_t*)map_val(BIF_ARG_2); +int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { + Sint n; + Sint found = 0; + Uint need; + Eterm *thp, *mhp; + Eterm *ks, *vs, tup; + map_t *mp = (map_t*)map_val(map); - key = BIF_ARG_1; - n = map_get_size(mp); + n = map_get_size(mp); - if (n == 0) - BIF_RET(BIF_ARG_2); + if (n == 0) { + *res = map; + return 1; + } - ks = map_get_keys(mp); - vs = map_get_values(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); - /* Assume key exists. - * Release allocated if it didn't. - * Allocate key tuple first. - */ + /* Assume key exists. + * Release allocated if it didn't. + * Allocate key tuple first. + */ - need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ - thp = HAlloc(BIF_P, need); - mhp = thp + n; /* offset with tuple heap size */ + need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ + thp = HAlloc(p, need); + mhp = thp + n; /* offset with tuple heap size */ - tup = make_tuple(thp); - *thp++ = make_arityval(n - 1); + tup = make_tuple(thp); + *thp++ = make_arityval(n - 1); - res = make_map(mhp); - *mhp++ = MAP_HEADER; - *mhp++ = n - 1; - *mhp++ = tup; + *res = make_map(mhp); + *mhp++ = MAP_HEADER; + *mhp++ = n - 1; + *mhp++ = tup; - if (is_immed(key)) { - while(n--) { - if (*ks == key) { - ks++; - vs++; - found = 1; - } else { - *mhp++ = *vs++; - *thp++ = *ks++; - } + if (is_immed(key)) { + while(n--) { + if (*ks == key) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; } - } else { - while(n--) { - if (eq(*ks, key)) { - ks++; - vs++; - found = 1; - } else { - *mhp++ = *vs++; - *thp++ = *ks++; - } + } + } else { + while(n--) { + if (eq(*ks, key)) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; } } + } - if (found) - BIF_RET(res); + if (found) { + return 1; + } - /* Not found, remove allocated memory - * and return previous map. - */ - HRelease(BIF_P, thp + need, thp); - BIF_RET(BIF_ARG_2); + /* Not found, remove allocated memory + * and return previous map. + */ + HRelease(p, thp + need, thp); + + *res = map; + return 1; +} + +BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm res; + if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) { + BIF_RET(res); + } } BIF_ERROR(BIF_P, BADARG); } @@ -659,19 +695,15 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { /* maps:update/3 */ -BIF_RETTYPE maps_update_3(BIF_ALIST_3) { - if (is_map(BIF_ARG_3)) { +int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) { Sint n,i; Sint found = 0; Eterm* hp,*shp; - Eterm *ks,*vs, res, key; - map_t *mp = (map_t*)map_val(BIF_ARG_3); + Eterm *ks,*vs; + map_t *mp = (map_t*)map_val(map); - key = BIF_ARG_1; - n = map_get_size(mp); - - if (n == 0) { - BIF_ERROR(BIF_P, BADARG); + if ((n = map_get_size(mp)) == 0) { + return 0; } ks = map_get_keys(mp); @@ -681,9 +713,9 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { * assume key-tuple will be intact */ - hp = HAlloc(BIF_P, 3 + n); + hp = HAlloc(p, MAP_HEADER_SIZE + n); shp = hp; - res = make_map(hp); + *res = make_map(hp); *hp++ = MAP_HEADER; *hp++ = n; *hp++ = mp->keys; @@ -691,7 +723,7 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { if (is_immed(key)) { for( i = 0; i < n; i ++) { if (ks[i] == key) { - *hp++ = BIF_ARG_2; + *hp++ = value; vs++; found = 1; } else { @@ -701,7 +733,7 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { } else { for( i = 0; i < n; i ++) { if (eq(ks[i], key)) { - *hp++ = BIF_ARG_2; + *hp++ = value; vs++; found = 1; } else { @@ -710,10 +742,19 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { } } - if (found) - BIF_RET(res); + if (found) { + return 1; + } + HRelease(p, shp + MAP_HEADER_SIZE + n, shp); + return 0; +} - HRelease(BIF_P, shp + 3 + n, shp); +BIF_RETTYPE maps_update_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + Eterm res; + if (erts_maps_update(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &res)) { + BIF_RET(res); + } } BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 4f0d26e100..616ecd24ce 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -62,5 +62,11 @@ typedef struct map_s { #define MAP_HEADER _make_header(1,_TAG_HEADER_MAP) #define MAP_HEADER_SIZE (sizeof(map_t) / sizeof(Eterm)) +Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map); +int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res); +int erts_maps_find(Eterm key, Eterm map, Eterm *value); +int erts_maps_get(Eterm key, Eterm map, Eterm *value); +int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res); + #endif diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index e1e213c4eb..c683847aaa 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -31,6 +31,7 @@ #include "bif.h" #include "error.h" #include "big.h" +#include "erl_map.h" #include "beam_bp.h" #include "erl_thr_progress.h" #include "dtrace-wrapper.h" @@ -1602,6 +1603,214 @@ enif_have_dirty_schedulers() #endif /* ERL_NIF_DIRTY_SCHEDULER_SUPPORT */ +/* Maps */ + +int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term) +{ + return is_map(term); +} + +int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, int *size) +{ + if (is_map(term)) { + map_t *mp; + mp = (map_t*)map_val(term); + *size = map_get_size(mp); + return 1; + } + return 0; +} + +ERL_NIF_TERM enif_make_new_map(ErlNifEnv* env) +{ + Eterm* hp = alloc_heap(env,MAP_HEADER_SIZE+1); + Eterm tup; + map_t *mp; + + tup = make_tuple(hp); + *hp++ = make_arityval(0); + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = 0; + mp->keys = tup; + + return make_map(mp); +} + +int enif_make_map_put(ErlNifEnv* env, + Eterm map_in, + Eterm key, + Eterm value, + Eterm *map_out) +{ + if (is_not_map(map_in)) { + return 0; + } + flush_env(env); + *map_out = erts_maps_put(env->proc, key, value, map_in); + cache_env(env); + return 1; +} + +int enif_get_map_value(ErlNifEnv* env, + Eterm map, + Eterm key, + Eterm *value) +{ + if (is_not_map(map)) { + return 0; + } + return erts_maps_get(key, map, value); +} + +int enif_find_map_value(ErlNifEnv* env, + Eterm map, + Eterm key, + Eterm *value) +{ + if (is_not_map(map)) { + return 0; + } + return erts_maps_get(key, map, value); +} + +int enif_make_map_update(ErlNifEnv* env, + Eterm map_in, + Eterm key, + Eterm value, + Eterm *map_out) +{ + int res; + if (is_not_map(map_in)) { + return 0; + } + + flush_env(env); + res = erts_maps_update(env->proc, key, value, map_in, map_out); + cache_env(env); + return res; +} + +int enif_make_map_remove(ErlNifEnv* env, + Eterm map_in, + Eterm key, + Eterm *map_out) +{ + int res; + if (is_not_map(map_in)) { + return 0; + } + flush_env(env); + res = erts_maps_remove(env->proc, key, map_in, map_out); + cache_env(env); + return res; +} + +int enif_map_iterator_create(ErlNifEnv *env, + Eterm map, + ErlNifMapIterator *iter, + ErlNifMapIteratorEntry entry) +{ + if (is_map(map)) { + map_t *mp = (map_t*)map_val(map); + size_t offset; + + switch (entry) { + case ERL_NIF_MAP_ITERATOR_HEAD: offset = 0; break; + case ERL_NIF_MAP_ITERATOR_TAIL: offset = map_get_size(mp) - 1; break; + default: goto error; + } + + /* empty maps are ok but will leave the iterator + * in bad shape. + */ + + iter->map = map; + iter->ks = ((Eterm *)map_get_keys(mp)) + offset; + iter->vs = ((Eterm *)map_get_values(mp)) + offset; + iter->t_limit = map_get_size(mp) + 1; + iter->h_limit = 0; + iter->idx = offset + 1; + + return 1; + } + +error: + iter->map = THE_NON_VALUE; + return 0; +} + +void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + /* not used */ +} + +int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); + if (is_map(iter->map) && ( + (iter->t_limit - iter->h_limit) == 1 || + iter->idx == iter->t_limit)) { + return 1; + } + return 0; +} + +int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); + if (is_map(iter->map) && ( + (iter->t_limit - iter->h_limit) == 1 || + iter->idx == iter->h_limit)) { + return 1; + } + return 0; +} + + +int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + if (is_map(iter->map) && iter->idx < iter->t_limit) { + iter->idx++; + if (iter->idx != iter->t_limit) { + iter->ks++; + iter->vs++; + } + return 1; + } + return 0; +} + +int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + if (is_map(iter->map) && iter->idx > iter->h_limit ) { + iter->idx--; + if (iter->idx != iter->h_limit ) { + iter->ks--; + iter->vs--; + } + return 1; + } + return 0; +} + +int enif_map_iterator_get_pair(ErlNifEnv *env, + ErlNifMapIterator *iter, + Eterm *key, + Eterm *value) +{ + if (is_map(iter->map) && iter->idx > iter->h_limit && iter->idx < iter->t_limit) { + ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) && + iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map)))); + ASSERT(iter->vs >= map_get_values(map_val(iter->map)) && + iter->vs < (map_get_values(map_val(iter->map)) + map_get_size(map_val(iter->map)))); + *key = *(iter->ks); + *value = *(iter->vs); + return 1; + } + return 0; +} + /*************************************************************************** ** load_nif/2 ** ***************************************************************************/ diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index fb3c359ec9..3c1e13f8a4 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -38,14 +38,11 @@ ** 2.2: R14B03 enif_is_exception ** 2.3: R15 enif_make_reverse_list, enif_is_number ** 2.4: R16 enif_consume_timeslice -** 2.5: R17 dirty schedulers +** 2.5: R17 Maps API additions +** R17 dirty schedulers */ #define ERL_NIF_MAJOR_VERSION 2 -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT #define ERL_NIF_MINOR_VERSION 5 -#else -#define ERL_NIF_MINOR_VERSION 4 -#endif #include @@ -168,6 +165,7 @@ typedef int ErlNifTSDKey; typedef ErlDrvThreadOpts ErlNifThreadOpts; +<<<<<<< HEAD #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT typedef enum { @@ -175,6 +173,24 @@ typedef enum ERL_NIF_DIRTY_JOB_IO_BOUND = 2 }ErlNifDirtyTaskFlags; #endif +======= +typedef struct +{ + /* use a lot of memory, structure may change */ + ERL_NIF_TERM map; + ErlNifUInt64 h_limit; + ErlNifUInt64 t_limit; + ErlNifUInt64 idx; + ERL_NIF_TERM *ks; + ERL_NIF_TERM *vs; +} ErlNifMapIterator; + +typedef enum { + ERL_NIF_MAP_ITERATOR_HEAD = 1, + ERL_NIF_MAP_ITERATOR_TAIL = 2 +} ErlNifMapIteratorEntry; + +>>>>>>> erts: Add NIFs for Maps #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index f5b27dfdfa..2cabfd4ce1 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -149,6 +149,23 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); #endif +ERL_NIF_API_FUNC_DECL(int, enif_is_map, (ErlNifEnv* env, ERL_NIF_TERM term)); +ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, int *size)); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); +ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); +ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); +ERL_NIF_API_FUNC_DECL(int, enif_find_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); +ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); +ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)); +ERL_NIF_API_FUNC_DECL(void, enif_map_iterator_destroy, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_is_head, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_is_tail, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)); + + /* ** Add new entries here to keep compatibility on Windows!!! */ @@ -281,6 +298,22 @@ ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); # define enif_have_dirty_schedulers ERL_NIF_API_FUNC_MACRO(enif_have_dirty_schedulers) #endif +# define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map) +# define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size) +# define enif_make_new_map ERL_NIF_FUNC_MACRO(enif_make_new_map) +# define enif_make_map_put ERL_NIF_FUNC_MACRO(enif_map_map_put) +# define enif_get_map_value ERL_NIF_FUNC_MACRO(enif_get_map_value) +# define enif_find_map_value ERL_NIF_FUNC_MACRO(enif_find_map_value) +# define enif_make_map_update ERL_NIF_FUNC_MACRO(enif_make_map_update) +# define enif_make_map_remove ERL_NIF_FUNC_MACRO(enif_make_map_remove) +# define enif_map_iterator_create ERL_NIF_FUNC_MACRO(enif_map_iterator_create) +# define enif_map_iterator_destroy ERL_NIF_FUNC_MACRO(enif_map_iterator_destroy) +# define enif_map_iterator_is_head ERL_NIF_FUNC_MACRO(enif_map_iterator_is_head) +# define enif_map_iterator_is_tail ERL_NIF_FUNC_MACRO(enif_map_iterator_is_tail) +# define enif_map_iterator_next ERL_NIF_FUNC_MACRO(enif_map_iterator_next) +# define enif_map_iterator_prev ERL_NIF_FUNC_MACRO(enif_map_iterator_prev) +# define enif_map_iterator_get_pair NIF_FUNC_MACRO(enif_map_iterator_get_pair) + /* ** Add new entries here */ -- cgit v1.2.3 From c71408f7184090fee077e35d0220fc021d2817fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 19 Nov 2013 11:38:36 +0100 Subject: erts: Test Maps with NIFs --- erts/emulator/test/nif_SUITE.erl | 25 +++++++++++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 59 +++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index affb66289b..5b7c310aa4 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -29,7 +29,8 @@ init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2, basic/1, reload/1, upgrade/1, heap_frag/1, - types/1, many_args/1, binaries/1, get_string/1, get_atom/1, + types/1, many_args/1, binaries/1, get_string/1, get_atom/1, + maps/1, api_macros/1, from_array/1, iolist_as_binary/1, resource/1, resource_binary/1, resource_takeover/1, @@ -58,7 +59,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [basic, reload, upgrade, heap_frag, types, many_args, - binaries, get_string, get_atom, api_macros, from_array, + binaries, get_string, get_atom, maps, api_macros, from_array, iolist_as_binary, resource, resource_binary, resource_takeover, threading, send, send2, send3, send_threaded, neg, is_checks, get_length, make_atom, @@ -435,6 +436,21 @@ get_atom(Config) when is_list(Config) -> ?line {0, <<>>} = atom_to_bin('',0), ok. +maps(doc) -> ["Test NIF maps handling."]; +maps(suite) -> []; +maps(Config) when is_list(Config) -> + TmpMem = tmpmem(), + Pairs = [{adam, "bert"}] ++ + [{I,I}||I <- lists:seq(1,10)] ++ + [{a,value},{"a","value"},{<<"a">>,<<"value">>}], + ok = ensure_lib_loaded(Config, 1), + M = maps_from_list(Pairs), + R = {RIs,Is} = sorted_list_from_maps(M), + io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]), + Is = lists:sort(Pairs), + Is = lists:reverse(RIs), + ok. + api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"]; api_macros(suite) -> []; api_macros(Config) when is_list(Config) -> @@ -1488,5 +1504,10 @@ otp_9668_nif(_) -> ?nif_stub. consume_timeslice_nif(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. +%% maps +maps_from_list(_) -> ?nif_stub. +sorted_list_from_maps(_) -> ?nif_stub. + + nif_stub_error(Line) -> exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 6f902e186d..a0316a7861 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1536,6 +1536,63 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM } #endif +/* maps */ +static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM cell = argv[0]; + ERL_NIF_TERM map = enif_make_new_map(env); + ERL_NIF_TERM tuple; + const ERL_NIF_TERM *pair; + int arity = -1; + + if (argc != 1 && !enif_is_list(env, cell)) return enif_make_badarg(env); + + /* assume sorted keys */ + + while (!enif_is_empty_list(env,cell)) { + if (!enif_get_list_cell(env, cell, &tuple, &cell)) return enif_make_badarg(env); + if (enif_get_tuple(env,tuple,&arity,&pair)) { + enif_make_map_put(env, map, pair[0], pair[1], &map); + } + } + + return map; +} + +static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + + ERL_NIF_TERM map = argv[0]; + ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */ + ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */ + ERL_NIF_TERM key, value; + ErlNifMapIterator iter; + + if (argc != 1 && !enif_is_map(env, map)) + return enif_make_badarg(env); + + if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_HEAD)) + return enif_make_badarg(env); + + while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { + list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f); + enif_map_iterator_next(env,&iter); + } + + enif_map_iterator_destroy(env, &iter); + + if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_TAIL)) + return enif_make_badarg(env); + + while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { + list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b); + enif_map_iterator_prev(env,&iter); + } + + enif_map_iterator_destroy(env, &iter); + + return enif_make_tuple2(env, list_f, list_b); +} + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -1589,6 +1646,8 @@ static ErlNifFunc nif_funcs[] = #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT {"call_dirty_nif", 3, call_dirty_nif}, #endif + {"maps_from_list", 1, maps_from_list}, + {"sorted_list_from_maps", 1, sorted_list_from_maps} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) -- cgit v1.2.3 From faf92c5174c7b86d2b5829c928b187753b3918e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 29 Nov 2013 12:12:53 +0100 Subject: erts: NIFs Map API fixup --- erts/emulator/beam/erl_nif_api_funcs.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 2cabfd4ce1..4a5aacad48 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -300,19 +300,19 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map) # define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size) -# define enif_make_new_map ERL_NIF_FUNC_MACRO(enif_make_new_map) -# define enif_make_map_put ERL_NIF_FUNC_MACRO(enif_map_map_put) -# define enif_get_map_value ERL_NIF_FUNC_MACRO(enif_get_map_value) -# define enif_find_map_value ERL_NIF_FUNC_MACRO(enif_find_map_value) -# define enif_make_map_update ERL_NIF_FUNC_MACRO(enif_make_map_update) -# define enif_make_map_remove ERL_NIF_FUNC_MACRO(enif_make_map_remove) -# define enif_map_iterator_create ERL_NIF_FUNC_MACRO(enif_map_iterator_create) -# define enif_map_iterator_destroy ERL_NIF_FUNC_MACRO(enif_map_iterator_destroy) -# define enif_map_iterator_is_head ERL_NIF_FUNC_MACRO(enif_map_iterator_is_head) -# define enif_map_iterator_is_tail ERL_NIF_FUNC_MACRO(enif_map_iterator_is_tail) -# define enif_map_iterator_next ERL_NIF_FUNC_MACRO(enif_map_iterator_next) -# define enif_map_iterator_prev ERL_NIF_FUNC_MACRO(enif_map_iterator_prev) -# define enif_map_iterator_get_pair NIF_FUNC_MACRO(enif_map_iterator_get_pair) +# define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map) +# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put) +# define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value) +# define enif_find_map_value ERL_NIF_API_FUNC_MACRO(enif_find_map_value) +# define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update) +# define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove) +# define enif_map_iterator_create ERL_NIF_API_FUNC_MACRO(enif_map_iterator_create) +# define enif_map_iterator_destroy ERL_NIF_API_FUNC_MACRO(enif_map_iterator_destroy) +# define enif_map_iterator_is_head ERL_NIF_API_FUNC_MACRO(enif_map_iterator_is_head) +# define enif_map_iterator_is_tail ERL_NIF_API_FUNC_MACRO(enif_map_iterator_is_tail) +# define enif_map_iterator_next ERL_NIF_API_FUNC_MACRO(enif_map_iterator_next) +# define enif_map_iterator_prev ERL_NIF_API_FUNC_MACRO(enif_map_iterator_prev) +# define enif_map_iterator_get_pair ERL_NIF_API_FUNC_MACRO(enif_map_iterator_get_pair) /* ** Add new entries here -- cgit v1.2.3 From d5c238473b9cec819d93faaef4ccc00ddb60465f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sat, 26 Oct 2013 21:01:41 +0200 Subject: erts: Add cmp_term to compare Uses total order of types meaning int < float --- erts/emulator/beam/erl_utils.h | 46 +++++++++++++++++++++++++++--------------- erts/emulator/beam/utils.c | 13 ++++++++++-- 2 files changed, 41 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 292d135946..c6247c7246 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -202,23 +202,37 @@ int eq(Eterm, Eterm); #define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y)))) #if HALFWORD_HEAP -Sint cmp_rel(Eterm, Eterm*, Eterm, Eterm*); -#define CMP(A,B) cmp_rel(A,NULL,B,NULL) +Sint cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int); +#define cmp_rel(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,0) +#define cmp_rel_exact(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1) +#define CMP(A,B) cmp_rel(A,NULL,B,NULL,0) +#define CMP_TERM(A,B) cmp_rel(A,NULL,B,NULL,1) #else -Sint cmp(Eterm, Eterm); -#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B) -#define CMP(A,B) cmp(A,B) +Sint cmp(Eterm, Eterm, int); +#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B,0) +#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp(A,B,1) +#define CMP(A,B) cmp(A,B,0) +#define CMP_TERM(A,B) cmp(A,B,1) #endif -#define cmp_lt(a,b) (CMP((a),(b)) < 0) -#define cmp_le(a,b) (CMP((a),(b)) <= 0) -#define cmp_eq(a,b) (CMP((a),(b)) == 0) -#define cmp_ne(a,b) (CMP((a),(b)) != 0) -#define cmp_ge(a,b) (CMP((a),(b)) >= 0) -#define cmp_gt(a,b) (CMP((a),(b)) > 0) - -#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b))) -#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b))) -#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b))) -#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b))) + +#define cmp_lt(a,b) (CMP((a),(b)) < 0) +#define cmp_le(a,b) (CMP((a),(b)) <= 0) +#define cmp_eq(a,b) (CMP((a),(b)) == 0) +#define cmp_ne(a,b) (CMP((a),(b)) != 0) +#define cmp_ge(a,b) (CMP((a),(b)) >= 0) +#define cmp_gt(a,b) (CMP((a),(b)) > 0) + +#define cmp_lt_term(a,b) (CMP_TERM((a),(b)) < 0) +#define cmp_le_term(a,b) (CMP_TERM((a),(b)) <= 0) +#define cmp_ge_term(a,b) (CMP_TERM((a),(b)) >= 0) +#define cmp_gt_term(a,b) (CMP_TERM((a),(b)) > 0) + +#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b))) +#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b))) +#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b))) +#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b))) + +#define CMP_LT_TERM(a,b) ((a) != (b) && cmp_lt_term((a),(b))) +#define CMP_GE_TERM(a,b) ((a) == (b) || cmp_ge_term((a),(b))) #endif diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 8958d334ae..bc4a05d385 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2425,10 +2425,14 @@ static int cmp_atoms(Eterm a, Eterm b) bb->name+3, bb->len-3); } +/* cmp(Eterm a, Eterm b, int exact) + * exact = 1 -> term-based compare + * exact = 0 -> arith-based compare + */ #if HALFWORD_HEAP -Sint cmp_rel(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base) +Sint cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, int exact) #else -Sint cmp(Eterm a, Eterm b) +Sint cmp(Eterm a, Eterm b, int exact) #endif { DECLARE_WSTACK(stack); @@ -2852,6 +2856,7 @@ tailrecur_ne: j = big_sign(aw) ? -1 : 1; break; case SMALL_FLOAT: + if (exact) goto exact_fall_through; GET_DOUBLE(bw, f2); if (f2.fd < MAX_LOSSLESS_FLOAT && f2.fd > MIN_LOSSLESS_FLOAT) { /* Float is within the no loss limit */ @@ -2877,12 +2882,14 @@ tailrecur_ne: #endif /* ERTS_SIZEOF_ETERM == 8 */ break; case FLOAT_BIG: + if (exact) goto exact_fall_through; { Wterm tmp = aw; aw = bw; bw = tmp; }/* fall through */ case BIG_FLOAT: + if (exact) goto exact_fall_through; GET_DOUBLE(bw, f2); if ((f2.fd < (double) (MAX_SMALL + 1)) && (f2.fd > (double) (MIN_SMALL - 1))) { @@ -2912,6 +2919,7 @@ tailrecur_ne: } break; case FLOAT_SMALL: + if (exact) goto exact_fall_through; GET_DOUBLE(aw, f1); if (f1.fd < MAX_LOSSLESS_FLOAT && f1.fd > MIN_LOSSLESS_FLOAT) { /* Float is within the no loss limit */ @@ -2936,6 +2944,7 @@ tailrecur_ne: } #endif /* ERTS_SIZEOF_ETERM == 8 */ break; +exact_fall_through: default: j = b_tag - a_tag; } -- cgit v1.2.3 From 76b8ea8ab1eb4ce099f88ccb8d1721c438d0ada4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 10:58:59 +0100 Subject: erts: Add BIF erts_internal:cmp_term/2 Compares terms where integer() < float(). --- erts/emulator/beam/bif.c | 11 +++++++++++ erts/emulator/beam/bif.tab | 2 ++ erts/preloaded/src/erts_internal.erl | 10 ++++++++++ 3 files changed, 23 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 61c1abedb5..9c4801041f 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4615,6 +4615,17 @@ BIF_RETTYPE bump_reductions_1(BIF_ALIST_1) BIF_RET2(am_true, reds); } +BIF_RETTYPE erts_internal_cmp_term_2(BIF_ALIST_2) { + int res = CMP_TERM(BIF_ARG_1,BIF_ARG_2); + + /* ensure -1, 0, 1 result */ + if (res < 0) { + BIF_RET(make_small(-1)); + } else if (res > 0) { + BIF_RET(make_small(1)); + } + BIF_RET(make_small(0)); +} /* * Processes doing yield on return in a bif ends up in bif_return_trap(). */ diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index b623e47b9a..2d888862bf 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -593,6 +593,8 @@ bif maps:remove/2 bif maps:update/3 bif maps:values/1 +bif erts_internal:cmp_term/2 + # # Obsolete # diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index d6a185482e..88eb317f1d 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -170,3 +170,13 @@ binary_to_term(_Binary) -> Opts :: [safe]. binary_to_term(_Binary, _Opts) -> erlang:nif_error(undefined). + +%% term compare where integer() < float() = true + +-spec cmp_term(A,B) -> Result when + A :: term(), + B :: term(), + Result :: -1 | 0 | 1. + +cmp_term(_A,_B) -> + erlang:nif_error(undefined). -- cgit v1.2.3 From 862728a458729f4a71630f4a8fa93f1f26744c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 11:15:20 +0100 Subject: erts: Update maps BIFs to use term order Maps internally uses term order to store keys in an ordered fashion. --- erts/emulator/beam/erl_map.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 98aeee634b..455ba25e11 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -108,7 +108,7 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { } /* maps:find/2 - * return value if key *equals* a key in the map + * return value if key *matches* a key in the map */ int erts_maps_find(Eterm key, Eterm map, Eterm *value) { @@ -123,7 +123,7 @@ int erts_maps_find(Eterm key, Eterm map, Eterm *value) { vs = map_get_values(mp); for( i = 0; i < n; i++) { - if (CMP(ks[i], key)==0) { + if (EQ(ks[i], key)) { *value = vs[i]; return 1; } @@ -178,7 +178,7 @@ int erts_maps_get(Eterm key, Eterm map, Eterm *value) { } for( i = 0; i < n; i++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { *value = vs[i]; return 1; } @@ -283,7 +283,7 @@ BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) { idx = size; - while(idx > 0 && (c = CMP(kv[1],ks[idx-1])) < 0) { idx--; } + while(idx > 0 && (c = CMP_TERM(kv[1],ks[idx-1])) < 0) { idx--; } if (c == 0) { /* last compare was equal, @@ -353,7 +353,7 @@ BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) { } for( i = 0; i < n; i++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { BIF_RET(am_true); } } @@ -425,7 +425,7 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { vs2 = map_get_values(mp2); while(i1 < n1 && i2 < n2) { - c = CMP(ks1[i1],ks2[i2]); + c = CMP_TERM(ks1[i1],ks2[i2]); if ( c == 0) { /* use righthand side arguments map value, * but advance both maps */ @@ -546,7 +546,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { } } else { for( i = 0; i < n; i ++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { *hp++ = value; vs++; c = 1; @@ -577,7 +577,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { ASSERT(n >= 0); /* copy map in order */ - while (n && ((c = CMP(*ks, key)) < 0)) { + while (n && ((c = CMP_TERM(*ks, key)) < 0)) { *shp++ = *ks++; *hp++ = *vs++; n--; @@ -658,7 +658,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { } } else { while(n--) { - if (eq(*ks, key)) { + if (EQ(*ks, key)) { ks++; vs++; found = 1; @@ -732,7 +732,7 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) } } else { for( i = 0; i < n; i ++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { *hp++ = value; vs++; found = 1; -- cgit v1.2.3 From f3821597383fa20d1093dab70fa75b4a1018a6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 11:20:04 +0100 Subject: erts: Update maps instructions to use term order Maps internally uses term order to store keys in an ordered fashion. --- erts/emulator/beam/beam_emu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index fe2e196785..89d9442526 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6316,7 +6316,7 @@ static int has_not_map_field(Eterm map, Eterm key) } } else { for (i = 0; i < n; i++) { - if (eq(keys[i], key)) { + if (EQ(keys[i], key)) { return 0; } } @@ -6343,7 +6343,7 @@ static Eterm get_map_element(Eterm map, Eterm key) } } else { for (i = 0; i < n; i++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { return vs[i]; } } @@ -6506,7 +6506,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) ASSERT(kp < (Eterm *)mp); key = *old_keys; - if ((c = CMP(key, new_key)) < 0) { + if ((c = CMP_TERM(key, new_key)) < 0) { /* Copy old key and value */ *kp++ = key; *hp++ = *old_vals; -- cgit v1.2.3 From c334d4841d9d600237184233518ee13f7421f91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 10 Jan 2014 18:04:00 +0100 Subject: erts: Update maps serializing to use term order * erlang:term_to_binary/1 * erlang:binary_to_term/1 --- erts/emulator/beam/external.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index a31b2731be..e8b77b9f37 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3803,7 +3803,7 @@ dec_term_atom_common: arity = arityval(*keys++); while(arity-- > 1) { - if (CMP(keys[arity-1],keys[arity]) >= 0) { + if (CMP_TERM(keys[arity-1],keys[arity]) >= 0) { goto error; } } -- cgit v1.2.3 From 0422247a9d8259ef1a28704500f07caf827f42f6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 22:00:04 +0100 Subject: erts: Fix bug in erts_maps_remove HRelease was called with wrong arguments and left garbage on heap when key was not found. --- erts/emulator/beam/erl_map.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 455ba25e11..e68482be58 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2013. All Rights Reserved. + * Copyright Ericsson AB 2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -614,6 +614,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { Sint n; Sint found = 0; Uint need; + Eterm *hp_start; Eterm *thp, *mhp; Eterm *ks, *vs, tup; map_t *mp = (map_t*)map_val(map); @@ -634,7 +635,8 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { */ need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ - thp = HAlloc(p, need); + hp_start = HAlloc(p, need); + thp = hp_start; mhp = thp + n; /* offset with tuple heap size */ tup = make_tuple(thp); @@ -676,7 +678,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { /* Not found, remove allocated memory * and return previous map. */ - HRelease(p, thp + need, thp); + HRelease(p, hp_start + need, hp_start); *res = map; return 1; -- cgit v1.2.3 From f1003b26524c2321b1eef36fb2d3997aaf0b9e10 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 23:03:33 +0100 Subject: erts: Optimize erts_map_update/remove to not continue comparing keys once it has been found. --- erts/emulator/beam/erl_map.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index e68482be58..57f156509c 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -612,7 +612,6 @@ BIF_RETTYPE maps_put_3(BIF_ALIST_3) { int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { Sint n; - Sint found = 0; Uint need; Eterm *hp_start; Eterm *thp, *mhp; @@ -650,9 +649,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { if (is_immed(key)) { while(n--) { if (*ks == key) { - ks++; - vs++; - found = 1; + goto found_key; } else { *mhp++ = *vs++; *thp++ = *ks++; @@ -661,9 +658,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { } else { while(n--) { if (EQ(*ks, key)) { - ks++; - vs++; - found = 1; + goto found_key; } else { *mhp++ = *vs++; *thp++ = *ks++; @@ -671,10 +666,6 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { } } - if (found) { - return 1; - } - /* Not found, remove allocated memory * and return previous map. */ @@ -682,6 +673,14 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { *res = map; return 1; + +found_key: + /* Copy rest of keys and values */ + if (n) { + sys_memcpy(mhp, vs+1, n*sizeof(Eterm)); + sys_memcpy(thp, ks+1, n*sizeof(Eterm)); + } + return 1; } BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { @@ -699,7 +698,6 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) { Sint n,i; - Sint found = 0; Eterm* hp,*shp; Eterm *ks,*vs; map_t *mp = (map_t*)map_val(map); @@ -717,7 +715,6 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) hp = HAlloc(p, MAP_HEADER_SIZE + n); shp = hp; - *res = make_map(hp); *hp++ = MAP_HEADER; *hp++ = n; *hp++ = mp->keys; @@ -725,9 +722,7 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) if (is_immed(key)) { for( i = 0; i < n; i ++) { if (ks[i] == key) { - *hp++ = value; - vs++; - found = 1; + goto found_key; } else { *hp++ = *vs++; } @@ -735,20 +730,23 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) } else { for( i = 0; i < n; i ++) { if (EQ(ks[i], key)) { - *hp++ = value; - vs++; - found = 1; + goto found_key; } else { *hp++ = *vs++; } } } - if (found) { - return 1; - } HRelease(p, shp + MAP_HEADER_SIZE + n, shp); return 0; + +found_key: + *hp++ = value; + vs++; + if (++i < n) + sys_memcpy(hp, vs, (n - i)*sizeof(Eterm)); + *res = make_map(shp); + return 1; } BIF_RETTYPE maps_update_3(BIF_ALIST_3) { -- cgit v1.2.3 From 4db2458bba34884448d3b3752dce74c17d92c698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 13:24:20 +0100 Subject: Update map_SUITE to respect term order --- erts/emulator/test/map_SUITE.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index a06cb43ac7..081e1b852e 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -228,7 +228,7 @@ t_update_assoc(Config) when is_list(Config) -> M1 = M0#{1=>42,2=>100,4=>[a,b,c]}, #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1, - M1 = M0#{1.0=>wrong,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, + #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, M2 = M0#{3.0=>new}, #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, @@ -462,12 +462,10 @@ t_bif_map_find(Config) when is_list(Config) -> {ok, 1} = maps:find(a, #{ a=> 1}), {ok, 2} = maps:find(b, #{ a=> 1, b => 2}), {ok, "int"} = maps:find(1, #{ 1 => "int"}), - {ok, "int"} = maps:find(1.0, #{ 1 => "int"}), - {ok, "float"} = maps:find(1, #{ 1.0 => "float"}), {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}), {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}), - {ok, "tuple hi"} = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + {ok, "tuple hi"} = maps:find({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }), @@ -475,6 +473,10 @@ t_bif_map_find(Config) when is_list(Config) -> %% error case error = maps:find(a,#{}), error = maps:find(a,#{b=>1, c=>2}), + error = maps:find(1.0, #{ 1 => "int"}), + error = maps:find(1, #{ 1.0 => "float"}), + error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])), {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)), @@ -737,7 +739,7 @@ t_map_encode_decode(Config) when is_list(Config) -> map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> M1 = maps:put(K,V,M0), B0 = erlang:term_to_binary(M1), - Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), + Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), %% sort Ks and Vs according to term spec, then match it ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]), %% decode and match it -- cgit v1.2.3 From 39c35199f5118a59f337b695a934c6bfcbf0813b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 13 Jan 2014 16:23:56 +0100 Subject: erts: Let erlang:binary_to_term/1 handle unsorted Maps Maps may be encoded with keys in arbitrary order. This is fine, as long as keys are unique. --- erts/emulator/beam/external.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index e8b77b9f37..57251286c8 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3790,24 +3790,41 @@ dec_term_atom_common: } } - /* Iterate through all the maps and check for validity + /* Iterate through all the maps and check for validity and sort keys * - done here for when we know it is complete. */ while (maps_head) { - Eterm *keys; - Sint arity; + map_t *mp = (map_t*)maps_head; + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + Uint sz = map_get_size(mp); + Uint ix,jx; + Eterm tmp; + int c; next = (Eterm *)(EXPAND_POINTER(*maps_head)); - keys = tuple_val(*(maps_head + 2)); - arity = arityval(*keys++); - while(arity-- > 1) { - if (CMP_TERM(keys[arity-1],keys[arity]) >= 0) { - goto error; + /* sort */ + + for ( ix = 1; ix < sz; ix++) { + jx = ix; + while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { + /* identical key -> error */ + if (c == 0) goto error; + + tmp = ks[jx]; + ks[jx] = ks[jx - 1]; + ks[jx - 1] = tmp; + + tmp = vs[jx]; + vs[jx] = vs[jx - 1]; + vs[jx - 1] = tmp; + + jx--; } - } + } *maps_head = MAP_HEADER; maps_head = next; } -- cgit v1.2.3 From 228ee23186639c8b66be8ed26def7c8e9cb09059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 13 Jan 2014 16:26:23 +0100 Subject: Update map_SUITE to test "unsorted" encoded maps --- erts/emulator/test/map_SUITE.erl | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 081e1b852e..75381de556 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -711,19 +711,32 @@ t_map_encode_decode(Config) when is_list(Config) -> ], ok = map_encode_decode_and_match(Pairs,[],#{}), + %% check sorting + + %% literally #{ b=>2, a=>1 } in the internal order + #{ a:=1, b:=2 } = + erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,2,97,1>>), + + + %% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order + #{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3, + 107,0,2,104,105, % "hi" :: list() + 100,0,1,97, % a :: atom() + 100,0,1,98, % b :: atom() + 107,0,5,118,97,108,117,101, % "value" :: list() + 97,33, % 33 :: integer() + 97,55 % 55 :: integer() + >>), + + %% error cases %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>> %% which is: #{ a=>1, b=>1 } - %% order violation - %% literally #{ b=>1, a=>1 } in the internal order (bad) - {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch - erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,1,97,1>>)), - %% uniqueness violation - %% literally #{ a=>1, a=>1 } + %% literally #{ a=>1, "hi"=>"value", a=>2 } {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch - erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,97,100,0,1,97,97,1,97,1>>)), + erlang:binary_to_term(<<131,116,0,0,0,3,100,0,1,97,107,0,2,104,105,100,0,1,97,97,1,107,0,5,118,97,108,117,101,97,2>>)), %% bad size (too large) {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch -- cgit v1.2.3 From c8ecb6962a923ceb0b7e599d22adef773d042e4a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 13 Jan 2014 22:15:13 +0100 Subject: erts: Remove enif_find_map_value as it does the same thing as enif_get_map_value. Replace with placeholder to be ABI backward compatible on Windows as long as enif_find_map_value is not called. --- erts/emulator/beam/erl_nif.c | 13 +------------ erts/emulator/beam/erl_nif_api_funcs.h | 5 ++--- 2 files changed, 3 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c683847aaa..1a539c730f 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -1663,17 +1663,6 @@ int enif_get_map_value(ErlNifEnv* env, return erts_maps_get(key, map, value); } -int enif_find_map_value(ErlNifEnv* env, - Eterm map, - Eterm key, - Eterm *value) -{ - if (is_not_map(map)) { - return 0; - } - return erts_maps_get(key, map, value); -} - int enif_make_map_update(ErlNifEnv* env, Eterm map_in, Eterm key, diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 4a5aacad48..71abb5dc8b 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -154,7 +154,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); -ERL_NIF_API_FUNC_DECL(int, enif_find_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); +ERL_NIF_API_FUNC_DECL(void, __enif_PLACEHOLDER__, (void)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)); @@ -303,7 +303,6 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map) # define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put) # define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value) -# define enif_find_map_value ERL_NIF_API_FUNC_MACRO(enif_find_map_value) # define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update) # define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove) # define enif_map_iterator_create ERL_NIF_API_FUNC_MACRO(enif_map_iterator_create) -- cgit v1.2.3 From b8a7203f04a1ed6d2756e6b782cd4c95a8c7c491 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 18:22:34 +0100 Subject: erts: Increase version for NIF API and reject experimental v2.5 --- erts/emulator/beam/erl_nif.c | 3 ++- erts/emulator/beam/erl_nif.h | 9 ++++----- erts/emulator/beam/erl_nif_api_funcs.h | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 1a539c730f..1eca7822eb 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1996,7 +1996,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful"); } else if (entry->major != ERL_NIF_MAJOR_VERSION - || entry->minor > ERL_NIF_MINOR_VERSION) { + || entry->minor > ERL_NIF_MINOR_VERSION + || (entry->major==2 && entry->minor == 5)) { /* experimental maps */ ret = load_nif_error(BIF_P, bad_lib, "Library version (%d.%d) not compatible (with %d.%d).", entry->major, entry->minor, ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION); diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 3c1e13f8a4..ae7f63f923 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -38,11 +38,13 @@ ** 2.2: R14B03 enif_is_exception ** 2.3: R15 enif_make_reverse_list, enif_is_number ** 2.4: R16 enif_consume_timeslice +** 2.5: First experimental maps API additions (libs of this version is not compatible with any other VM) ** 2.5: R17 Maps API additions +** 2.6: R17 with maps ** R17 dirty schedulers */ #define ERL_NIF_MAJOR_VERSION 2 -#define ERL_NIF_MINOR_VERSION 5 +#define ERL_NIF_MINOR_VERSION 6 #include @@ -165,7 +167,6 @@ typedef int ErlNifTSDKey; typedef ErlDrvThreadOpts ErlNifThreadOpts; -<<<<<<< HEAD #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT typedef enum { @@ -173,7 +174,7 @@ typedef enum ERL_NIF_DIRTY_JOB_IO_BOUND = 2 }ErlNifDirtyTaskFlags; #endif -======= + typedef struct { /* use a lot of memory, structure may change */ @@ -190,8 +191,6 @@ typedef enum { ERL_NIF_MAP_ITERATOR_TAIL = 2 } ErlNifMapIteratorEntry; ->>>>>>> erts: Add NIFs for Maps - #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct { diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 71abb5dc8b..9c386a4c41 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -154,7 +154,6 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); -ERL_NIF_API_FUNC_DECL(void, __enif_PLACEHOLDER__, (void)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)); -- cgit v1.2.3 From f321ea89fecdb343e58e2485c057326d08fed69c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 13 Jan 2014 22:16:40 +0100 Subject: erts: Do not allow map iterator created without map --- erts/emulator/beam/erl_nif.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 1eca7822eb..ca82590a78 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1725,21 +1725,27 @@ int enif_map_iterator_create(ErlNifEnv *env, } error: +#ifdef DEBUG iter->map = THE_NON_VALUE; +#endif return 0; } void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter) { /* not used */ +#ifdef DEBUG + iter->map = THE_NON_VALUE; +#endif + } int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) { + ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (is_map(iter->map) && ( - (iter->t_limit - iter->h_limit) == 1 || - iter->idx == iter->t_limit)) { + if ((iter->t_limit - iter->h_limit) == 1 + || iter->idx == iter->t_limit) { return 1; } return 0; @@ -1747,10 +1753,10 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) { + ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (is_map(iter->map) && ( - (iter->t_limit - iter->h_limit) == 1 || - iter->idx == iter->h_limit)) { + if ((iter->t_limit - iter->h_limit) == 1 + || iter->idx == iter->h_limit) { return 1; } return 0; @@ -1759,7 +1765,8 @@ int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) { - if (is_map(iter->map) && iter->idx < iter->t_limit) { + ASSERT(iter && is_map(iter->map)); + if (iter->idx < iter->t_limit) { iter->idx++; if (iter->idx != iter->t_limit) { iter->ks++; @@ -1772,7 +1779,8 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) { - if (is_map(iter->map) && iter->idx > iter->h_limit ) { + ASSERT(iter && is_map(iter->map)); + if (iter->idx > iter->h_limit ) { iter->idx--; if (iter->idx != iter->h_limit ) { iter->ks--; @@ -1788,7 +1796,8 @@ int enif_map_iterator_get_pair(ErlNifEnv *env, Eterm *key, Eterm *value) { - if (is_map(iter->map) && iter->idx > iter->h_limit && iter->idx < iter->t_limit) { + ASSERT(iter && is_map(iter->map)); + if (iter->idx > iter->h_limit && iter->idx < iter->t_limit) { ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) && iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map)))); ASSERT(iter->vs >= map_get_values(map_val(iter->map)) && -- cgit v1.2.3 From 609d835ce04a1f6320377780a11fb32382e1e419 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 11:25:34 +0100 Subject: erts: Let enif_map_iterator_next/prev return 0 to signal end of map. --- erts/emulator/beam/erl_nif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index ca82590a78..def9a7efe1 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1771,8 +1771,8 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) if (iter->idx != iter->t_limit) { iter->ks++; iter->vs++; + return 1; } - return 1; } return 0; } @@ -1785,8 +1785,8 @@ int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) if (iter->idx != iter->h_limit ) { iter->ks--; iter->vs--; + return 1; } - return 1; } return 0; } -- cgit v1.2.3 From d39affeac223a7e2891b509395e7b19c931b5018 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 20:32:46 +0100 Subject: erts: Remove use of h_limit which is always zero. --- erts/emulator/beam/erl_nif.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index def9a7efe1..94c2512650 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1718,7 +1718,6 @@ int enif_map_iterator_create(ErlNifEnv *env, iter->ks = ((Eterm *)map_get_keys(mp)) + offset; iter->vs = ((Eterm *)map_get_values(mp)) + offset; iter->t_limit = map_get_size(mp) + 1; - iter->h_limit = 0; iter->idx = offset + 1; return 1; @@ -1744,8 +1743,7 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if ((iter->t_limit - iter->h_limit) == 1 - || iter->idx == iter->t_limit) { + if (iter->t_limit == 1 || iter->idx == iter->t_limit) { return 1; } return 0; @@ -1755,8 +1753,7 @@ int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if ((iter->t_limit - iter->h_limit) == 1 - || iter->idx == iter->h_limit) { + if (iter->t_limit == 1 || iter->idx == 0) { return 1; } return 0; @@ -1780,9 +1777,9 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); - if (iter->idx > iter->h_limit ) { + if (iter->idx > 0) { iter->idx--; - if (iter->idx != iter->h_limit ) { + if (iter->idx != 0) { iter->ks--; iter->vs--; return 1; @@ -1797,7 +1794,7 @@ int enif_map_iterator_get_pair(ErlNifEnv *env, Eterm *value) { ASSERT(iter && is_map(iter->map)); - if (iter->idx > iter->h_limit && iter->idx < iter->t_limit) { + if (iter->idx > 0 && iter->idx < iter->t_limit) { ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) && iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map)))); ASSERT(iter->vs >= map_get_values(map_val(iter->map)) && -- cgit v1.2.3 From ada36d9024b4ceda974f32a78b5fe39b64f59319 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 20:55:10 +0100 Subject: erts: Simplify some map iterator code --- erts/emulator/beam/erl_nif.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 94c2512650..c6f7c8adb5 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1743,20 +1743,14 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (iter->t_limit == 1 || iter->idx == iter->t_limit) { - return 1; - } - return 0; + return (iter->t_limit == 1 || iter->idx == iter->t_limit); } int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (iter->t_limit == 1 || iter->idx == 0) { - return 1; - } - return 0; + return (iter->t_limit == 1 || iter->idx == 0); } -- cgit v1.2.3 From 00f9be42e43913bce9b110382e55bfbdaa9406d0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 18:13:45 +0100 Subject: erts: Fix map iterator bug when reverting from end of map position and simplify code by ignoring h_limit which is always zero. --- erts/emulator/beam/erl_nif.c | 18 +++------ erts/emulator/test/nif_SUITE.erl | 5 ++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 57 ++++++++++++++++++++------- 3 files changed, 53 insertions(+), 27 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c6f7c8adb5..6fceb8a0ba 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1759,13 +1759,10 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) ASSERT(iter && is_map(iter->map)); if (iter->idx < iter->t_limit) { iter->idx++; - if (iter->idx != iter->t_limit) { - iter->ks++; - iter->vs++; - return 1; - } + iter->ks++; + iter->vs++; } - return 0; + return (iter->idx != iter->t_limit); } int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) @@ -1773,13 +1770,10 @@ int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) ASSERT(iter && is_map(iter->map)); if (iter->idx > 0) { iter->idx--; - if (iter->idx != 0) { - iter->ks--; - iter->vs--; - return 1; - } + iter->ks--; + iter->vs--; } - return 0; + return (iter->idx > 0); } int enif_map_iterator_get_pair(ErlNifEnv *env, diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 5b7c310aa4..a34f70c618 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -449,6 +449,9 @@ maps(Config) when is_list(Config) -> io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]), Is = lists:sort(Pairs), Is = lists:reverse(RIs), + + #{} = maps_from_list([]), + {[],[]} = sorted_list_from_maps(#{}), ok. api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"]; diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index a0316a7861..8549d277de 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -1564,31 +1564,60 @@ static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NI ERL_NIF_TERM map = argv[0]; ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */ ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */ - ERL_NIF_TERM key, value; - ErlNifMapIterator iter; + ERL_NIF_TERM key, value, k2, v2; + ErlNifMapIterator iter_f; + ErlNifMapIterator iter_b; + int cnt, next_ret, prev_ret; if (argc != 1 && !enif_is_map(env, map)) - return enif_make_badarg(env); + return enif_make_int(env, __LINE__); - if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_HEAD)) - return enif_make_badarg(env); + if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_HEAD)) + return enif_make_int(env, __LINE__); - while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { + cnt = 0; + while(enif_map_iterator_get_pair(env,&iter_f,&key,&value)) { + if (cnt && !next_ret) + return enif_make_int(env, __LINE__); list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f); - enif_map_iterator_next(env,&iter); + next_ret = enif_map_iterator_next(env,&iter_f); + cnt++; } + if (cnt && next_ret) + return enif_make_int(env, __LINE__); - enif_map_iterator_destroy(env, &iter); + if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_TAIL)) + return enif_make_int(env, __LINE__); - if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_TAIL)) - return enif_make_badarg(env); + cnt = 0; + while(enif_map_iterator_get_pair(env,&iter_b,&key,&value)) { + if (cnt && !prev_ret) + return enif_make_int(env, __LINE__); + + /* Test that iter_f can step "backwards" */ + if (!enif_map_iterator_prev(env,&iter_f) + || !enif_map_iterator_get_pair(env,&iter_f,&k2,&v2) + || k2 != key || v2 != value) { + return enif_make_int(env, __LINE__); + } - while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b); - enif_map_iterator_prev(env,&iter); + prev_ret = enif_map_iterator_prev(env,&iter_b); + } + + if (cnt) { + if (prev_ret || enif_map_iterator_prev(env,&iter_f)) + return enif_make_int(env, __LINE__); + + /* Test that iter_b can step "backwards" one step */ + if (!enif_map_iterator_next(env, &iter_b) + || !enif_map_iterator_get_pair(env,&iter_b,&k2,&v2) + || k2 != key || v2 != value) + return enif_make_int(env, __LINE__); } - enif_map_iterator_destroy(env, &iter); + enif_map_iterator_destroy(env, &iter_f); + enif_map_iterator_destroy(env, &iter_b); return enif_make_tuple2(env, list_f, list_b); } -- cgit v1.2.3 From a18853e6814ab42188e56343e6363ff94c794bb8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 18:21:26 +0100 Subject: erts: Optimize struct ErlNifMapIterator No need to use 64bit integers on 32bit machines. --- erts/emulator/beam/erl_nif.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index ae7f63f923..7613446f64 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The 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,6 +103,8 @@ typedef unsigned long long ERL_NIF_TERM; # endif #endif +typedef ERL_NIF_TERM ERL_NIF_UINT; + struct enif_environment_t; typedef struct enif_environment_t ErlNifEnv; @@ -175,15 +177,14 @@ typedef enum }ErlNifDirtyTaskFlags; #endif -typedef struct +typedef struct /* All fields all internal and may change */ { - /* use a lot of memory, structure may change */ ERL_NIF_TERM map; - ErlNifUInt64 h_limit; - ErlNifUInt64 t_limit; - ErlNifUInt64 idx; + ERL_NIF_UINT t_limit; + ERL_NIF_UINT idx; ERL_NIF_TERM *ks; ERL_NIF_TERM *vs; + void* __spare__[2]; /* for future additions to be ABI compatible (same struct size) */ } ErlNifMapIterator; typedef enum { -- cgit v1.2.3 From e21989fdc834c9c9e75454bd06cb6ef29218bf9f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 23:07:01 +0100 Subject: erts: Add more tests for the NIF map API --- erts/emulator/test/nif_SUITE.erl | 49 +++++++++++++++++++++--- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 55 +++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index a34f70c618..bcc1f9e5af 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -444,14 +444,44 @@ maps(Config) when is_list(Config) -> [{I,I}||I <- lists:seq(1,10)] ++ [{a,value},{"a","value"},{<<"a">>,<<"value">>}], ok = ensure_lib_loaded(Config, 1), - M = maps_from_list(Pairs), - R = {RIs,Is} = sorted_list_from_maps(M), + M = maps_from_list_nif(Pairs), + R = {RIs,Is} = sorted_list_from_maps_nif(M), io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]), Is = lists:sort(Pairs), Is = lists:reverse(RIs), - #{} = maps_from_list([]), - {[],[]} = sorted_list_from_maps(#{}), + #{} = maps_from_list_nif([]), + {[],[]} = sorted_list_from_maps_nif(#{}), + + 1 = is_map_nif(M), + 0 = is_map_nif("no map"), + + Msz = map_size(M), + {1,Msz} = get_map_size_nif(M), + {1,0} = get_map_size_nif(#{}), + {0,-123} = get_map_size_nif({#{}}), + + #{} = M0 = make_new_map_nif(), + + {1, #{key := value}=M1} = make_map_put_nif(M0, key, value), + {1, #{key := value, "key2" := "value2"}=M2} = make_map_put_nif(M1, "key2", "value2"), + {1, #{key := "value", "key2" := "value2"}=M3} = make_map_put_nif(M2, key, "value"), + {0, undefined} = make_map_put_nif(666, key, value), + + {1, "value2"} = get_map_value_nif(M3,"key2"), + {0, undefined} = get_map_value_nif(M3,"key3"), + {0, undefined} = get_map_value_nif(false,key), + + {0, undefined} = make_map_update_nif(M0, key, value), + {0, undefined} = make_map_update_nif(M1, "key2", "value2"), + {1, #{key := "value", "key2" := "value2"}} = make_map_update_nif(M2, key, "value"), + {0, undefined} = make_map_update_nif(666, key, value), + + {1, #{}} = make_map_remove_nif(M1, key), + {1, M1} = make_map_remove_nif(M2, "key2"), + {1, M2} = make_map_remove_nif(M2, "key3"), + {0, undefined} = make_map_remove_nif(self(), key), + ok. api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"]; @@ -1508,8 +1538,15 @@ consume_timeslice_nif(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. %% maps -maps_from_list(_) -> ?nif_stub. -sorted_list_from_maps(_) -> ?nif_stub. +is_map_nif(_) -> ?nif_stub. +get_map_size_nif(_) -> ?nif_stub. +make_new_map_nif() -> ?nif_stub. +make_map_put_nif(_,_,_) -> ?nif_stub. +get_map_value_nif(_,_) -> ?nif_stub. +make_map_update_nif(_,_,_) -> ?nif_stub. +make_map_remove_nif(_,_) -> ?nif_stub. +maps_from_list_nif(_) -> ?nif_stub. +sorted_list_from_maps_nif(_) -> ?nif_stub. nif_stub_error(Line) -> diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 8549d277de..9ee89a49a4 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1536,8 +1536,48 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM } #endif +static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return enif_make_int(env, enif_is_map(env,argv[0])); +} +static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int size = -123; + int ret = enif_get_map_size(env, argv[0], &size); + return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, size)); +} +static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return enif_make_new_map(env); +} +static ERL_NIF_TERM make_map_put_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM map_out = enif_make_atom(env, "undefined"); + int ret = enif_make_map_put(env, argv[0], argv[1], argv[2], &map_out); + return enif_make_tuple2(env, enif_make_int(env,ret), map_out); +} +static ERL_NIF_TERM get_map_value_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM value = enif_make_atom(env, "undefined"); + int ret = enif_get_map_value(env, argv[0], argv[1], &value); + return enif_make_tuple2(env, enif_make_int(env,ret), value); + +} +static ERL_NIF_TERM make_map_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM map_out = enif_make_atom(env, "undefined"); + int ret = enif_make_map_update(env, argv[0], argv[1], argv[2], &map_out); + return enif_make_tuple2(env, enif_make_int(env,ret), map_out); +} +static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM map_out = enif_make_atom(env, "undefined"); + int ret = enif_make_map_remove(env, argv[0], argv[1], &map_out); + return enif_make_tuple2(env, enif_make_int(env,ret), map_out); +} + /* maps */ -static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM cell = argv[0]; ERL_NIF_TERM map = enif_make_new_map(env); @@ -1559,7 +1599,7 @@ static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM return map; } -static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM map = argv[0]; ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */ @@ -1675,8 +1715,15 @@ static ErlNifFunc nif_funcs[] = #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT {"call_dirty_nif", 3, call_dirty_nif}, #endif - {"maps_from_list", 1, maps_from_list}, - {"sorted_list_from_maps", 1, sorted_list_from_maps} + {"is_map_nif", 1, is_map_nif}, + {"get_map_size_nif", 1, get_map_size_nif}, + {"make_new_map_nif", 0, make_new_map_nif}, + {"make_map_put_nif", 3, make_map_put_nif}, + {"get_map_value_nif", 2, get_map_value_nif}, + {"make_map_update_nif", 3, make_map_update_nif}, + {"make_map_remove_nif", 2, make_map_remove_nif}, + {"maps_from_list_nif", 1, maps_from_list_nif}, + {"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) -- cgit v1.2.3 From 9eddc8eb4c7f4ef45b51650750334a787c5f9ef7 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 23:45:42 +0100 Subject: erts: Fix compile error for halfword emulator --- erts/emulator/beam/erl_utils.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index c6247c7246..5b81d814c6 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2013. All Rights Reserved. + * Copyright Ericsson AB 2012-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -204,9 +204,9 @@ int eq(Eterm, Eterm); #if HALFWORD_HEAP Sint cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int); #define cmp_rel(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,0) -#define cmp_rel_exact(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1) -#define CMP(A,B) cmp_rel(A,NULL,B,NULL,0) -#define CMP_TERM(A,B) cmp_rel(A,NULL,B,NULL,1) +#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1) +#define CMP(A,B) cmp_rel_opt(A,NULL,B,NULL,0) +#define CMP_TERM(A,B) cmp_rel_opt(A,NULL,B,NULL,1) #else Sint cmp(Eterm, Eterm, int); #define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B,0) -- cgit v1.2.3 From f2b9a5623ec5c9a314560add82ef20a03ba91235 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 17 Jan 2014 12:32:42 +0100 Subject: erts: Change 'size' argument of enif_get_map_size from int* to size_t* --- erts/emulator/beam/erl_nif.c | 2 +- erts/emulator/beam/erl_nif_api_funcs.h | 2 +- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 6fceb8a0ba..c35f1fc2c6 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1610,7 +1610,7 @@ int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term) return is_map(term); } -int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, int *size) +int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size) { if (is_map(term)) { map_t *mp; diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 9c386a4c41..4121378e1a 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -150,7 +150,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); #endif ERL_NIF_API_FUNC_DECL(int, enif_is_map, (ErlNifEnv* env, ERL_NIF_TERM term)); -ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, int *size)); +ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 9ee89a49a4..b550d1f16d 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1542,9 +1542,9 @@ static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv } static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - int size = -123; + size_t size = (size_t)-123; int ret = enif_get_map_size(env, argv[0], &size); - return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, size)); + return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, (int)size)); } static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -- cgit v1.2.3 From 3645dc6b15d4fdd79768b92bb598c264005a8689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 14 Jan 2014 12:30:51 +0100 Subject: preloaded: Fixup export cmp_term in erts_internal --- erts/preloaded/src/erts_internal.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 88eb317f1d..edcd50c77e 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -30,6 +30,7 @@ -export([await_port_send_result/3]). -export([binary_to_term/1, binary_to_term/2]). +-export([cmp_term/2]). -export([port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). -- cgit v1.2.3 From bc34b5dafaebf07d7185900310246f5752c0a6c4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 21 Jan 2014 12:21:11 +0100 Subject: erts: Add map construction to driver API erl_drv_output_term() and erl_drv_send_term() can send messages containing maps with the use of the new ERL_DRV_MAP. The driver API minor version is updated as new functionality is added. --- erts/doc/src/erl_driver.xml | 29 ++++++++- erts/emulator/beam/erl_driver.h | 4 +- erts/emulator/beam/erl_map.c | 31 ++++++++++ erts/emulator/beam/erl_map.h | 4 +- erts/emulator/beam/external.c | 31 +--------- erts/emulator/beam/io.c | 44 ++++++++++++- erts/emulator/test/send_term_SUITE.erl | 19 +++++- .../test/send_term_SUITE_data/send_term_drv.c | 72 ++++++++++++++++++++-- 8 files changed, 191 insertions(+), 43 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index c2f7fa4588..710c9b19cf 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -4,7 +4,7 @@

- 20012013 + 20012014 Ericsson AB. All Rights Reserved. @@ -1742,15 +1742,19 @@ typedef struct ErlIOVec { term consists of one to four elements in the array. The term first has a term type, and then arguments. The port parameter specifies the sending port.

-

Tuple and lists (with the exception of strings, see below), +

Tuples, maps and lists (with the exception of strings, see below), are built in reverse polish notation, so that to build a tuple, the elements are given first, and then the tuple - term, with a count. Likewise for lists.

+ term, with a count. Likewise for lists and maps.

A tuple must be specified with the number of elements. (The elements precede the ERL_DRV_TUPLE term.)

A list must be specified with the number of elements, including the tail, which is the last term preceding ERL_DRV_LIST.

+

A map must be specified with the number of key-value pairs N. + The key-value pairs must precede the ERL_DRV_MAP in this order: + key1,value1,key2,value2,...,keyN,valueN. + Duplicate keys are not allowed.

The special term ERL_DRV_STRING_CONS is used to "splice" in a string in a list, a string given this way is not a list per se, but the elements are elements of the @@ -1774,6 +1778,7 @@ ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port) ERL_DRV_STRING_CONS char *str, int len ERL_DRV_FLOAT double *dbl ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len +ERL_DRV_MAP int sz

The unsigned integer data type ErlDrvUInt and the signed integer data type ErlDrvSInt are 64 bits wide @@ -1856,6 +1861,24 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len }; erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]> + +

To build the map #{key1 => 100, key2 => {200, 300}}, the + following call could be made.

+ + +

If you want to pass a binary and don't already have the content of the binary in an ErlDrvBinary, you can benefit from using ERL_DRV_BUF2BINARY instead of creating an ErlDrvBinary diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 2bd3181bdc..ab9ee63104 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2013. All Rights Reserved. + * Copyright Ericsson AB 1999-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -605,6 +605,8 @@ EXTERN int null_func(void); #define ERL_DRV_INT64 ((ErlDrvTermData) 15) /* ErlDrvSInt64 * */ #define ERL_DRV_UINT64 ((ErlDrvTermData) 16) /* ErlDrvUInt64 * */ +#define ERL_DRV_MAP ((ErlDrvTermData) 17) /* ErlDrvUInt */ + #ifndef ERL_DRIVER_TYPES_ONLY /* make terms for driver_output_term and driver_send_term */ diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 57f156509c..2fff7f9390 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -786,3 +786,34 @@ BIF_RETTYPE maps_values_1(BIF_ALIST_1) { } BIF_ERROR(BIF_P, BADARG); } + +int erts_validate_and_sort_map(map_t* mp) +{ + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + Uint sz = map_get_size(mp); + Uint ix,jx; + Eterm tmp; + int c; + + /* sort */ + + for (ix = 1; ix < sz; ix++) { + jx = ix; + while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { + /* identical key -> error */ + if (c == 0) return 0; + + tmp = ks[jx]; + ks[jx] = ks[jx - 1]; + ks[jx - 1] = tmp; + + tmp = vs[jx]; + vs[jx] = vs[jx - 1]; + vs[jx - 1] = tmp; + + jx--; + } + } + return 1; +} diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 616ecd24ce..cfacb2ec28 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2013. All Rights Reserved. + * Copyright Ericsson AB 2014. All Rights Reserved. * * The 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,6 +67,6 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res int erts_maps_find(Eterm key, Eterm map, Eterm *value); int erts_maps_get(Eterm key, Eterm map, Eterm *value); int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res); - +int erts_validate_and_sort_map(map_t* map); #endif diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 57251286c8..a4cc3435c3 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3795,37 +3795,10 @@ dec_term_atom_common: */ while (maps_head) { - map_t *mp = (map_t*)maps_head; - Eterm *ks = map_get_keys(mp); - Eterm *vs = map_get_values(mp); - Uint sz = map_get_size(mp); - Uint ix,jx; - Eterm tmp; - int c; - next = (Eterm *)(EXPAND_POINTER(*maps_head)); - - /* sort */ - - for ( ix = 1; ix < sz; ix++) { - jx = ix; - while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { - /* identical key -> error */ - if (c == 0) goto error; - - tmp = ks[jx]; - ks[jx] = ks[jx - 1]; - ks[jx - 1] = tmp; - - tmp = vs[jx]; - vs[jx] = vs[jx - 1]; - vs[jx - 1] = tmp; - - jx--; - } - - } *maps_head = MAP_HEADER; + if (!erts_validate_and_sort_map((map_t*)maps_head)) + goto error; maps_head = next; } diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 49af86b36a..3b16cdeb4a 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The 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,6 +46,7 @@ #define ERTS_WANT_EXTERNAL_TAGS #include "external.h" #include "dtrace-wrapper.h" +#include "erl_map.h" extern ErlDrvEntry fd_driver_entry; extern ErlDrvEntry vanilla_driver_entry; @@ -5293,6 +5294,17 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) depth++; break; } + case ERL_DRV_MAP: { /* int */ + ERTS_DDT_CHK_ENOUGH_ARGS(1); + if ((int) ptr[0] < 0) ERTS_DDT_FAIL; + need += MAP_HEADER_SIZE + 1 + 2*ptr[0]; + depth -= 2*ptr[0]; + if (depth < 0) ERTS_DDT_FAIL; + ptr++; + depth++; + break; + } + default: ERTS_DDT_FAIL; } @@ -5529,6 +5541,36 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) ptr += 2; break; + case ERL_DRV_MAP: { /* int */ + int size = (int)ptr[0]; + Eterm* tp = hp; + Eterm* vp; + map_t *mp; + + *tp = make_arityval(size); + + hp += 1 + size; + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = size; + mp->keys = make_tuple(tp); + mess = make_map(mp); + + hp += MAP_HEADER_SIZE + size; /* advance "heap" pointer */ + + tp += size; /* point at last key */ + vp = hp - 1; /* point at last value */ + + while(size--) { + *vp-- = ESTACK_POP(stack); + *tp-- = ESTACK_POP(stack); + } + if (!erts_validate_and_sort_map(mp)) + ERTS_DDT_FAIL; + ptr++; + break; + } + } ESTACK_PUSH(stack, mess); } diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index b631f55a03..8e1f8df43a 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2013. All Rights Reserved. +%% Copyright Ericsson AB 2005-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -62,7 +62,19 @@ basic(Config) when is_list(Config) -> ?line [] = term(P, 0), ?line Self = self(), - ?line {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self} = term(P, 1), + {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self,#{}} = term(P, 1), + + Map41 = maps:from_list([{blurf, 42}, + {[], [-42,{}|"abc"++P]}, + {"kalle", 3.1416}, + {Self, #{}}]), + Map41 = term(P, 41), + + Map42 = maps:from_list([{42, []}, + {[-42,{}|"abc"++P], "kalle"}, + {3.1416, Self}, + {#{}, blurf}]), + Map42 = term(P, 42), ?line Deep = lists:seq(0, 199), ?line Deep = term(P, 2), ?line {B1,B2} = term(P, 3), @@ -125,7 +137,8 @@ basic(Config) when is_list(Config) -> {-1, 36}, % ERL_DRV_INT64 {-4711, 37}, % ERL_DRV_INT64 {-20233590931456, 38}, % ERL_DRV_INT64 - {-9223372036854775808, 39}], % ERL_DRV_INT64 + {-9223372036854775808, 39}, + {#{}, 40}], % ERL_DRV_MAP ?line {Terms, Ops} = lists:unzip(Singles), ?line Terms = term(P,Ops), diff --git a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c index f8613487b0..381a4f20d5 100644 --- a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c +++ b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c @@ -104,7 +104,7 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) double f = 3.1416; msg[0] = ERL_DRV_ATOM; - msg[1] = driver_mk_atom("blurf"), + msg[1] = driver_mk_atom("blurf"); msg[2] = ERL_DRV_INT; msg[3] = (ErlDrvTermData) 42; msg[4] = ERL_DRV_NIL; @@ -126,9 +126,11 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) msg[20] = (ErlDrvTermData) &f; msg[21] = ERL_DRV_PID; msg[22] = driver_connected(erlang_port); - msg[23] = ERL_DRV_TUPLE; - msg[24] = (ErlDrvTermData) 7; - msg += 25; + msg[23] = ERL_DRV_MAP; + msg[24] = (ErlDrvTermData) 0; + msg[25] = ERL_DRV_TUPLE; + msg[26] = (ErlDrvTermData) 8; + msg += 27; } break; @@ -481,6 +483,52 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) break; } + case 40: { + msg[0] = ERL_DRV_MAP; + msg[1] = (ErlDrvTermData) 0; + msg += 2; + break; + } + + case 41: /* Most term types inside a map */ + case 42: { + double f = 3.1416; + + if (buf[i] == 41) { + *msg++ = ERL_DRV_ATOM; + *msg++ = driver_mk_atom("blurf"); + } + *msg++ = ERL_DRV_INT; + *msg++ = (ErlDrvTermData)42; + *msg++ = ERL_DRV_NIL; + *msg++ = ERL_DRV_INT; + *msg++ = (ErlDrvTermData)-42; + *msg++ = ERL_DRV_TUPLE; + *msg++ = (ErlDrvTermData)0; + *msg++ = ERL_DRV_PORT; + *msg++ = driver_mk_port(erlang_port); + *msg++ = ERL_DRV_STRING_CONS; + *msg++ = (ErlDrvTermData)"abc"; + *msg++ = (ErlDrvTermData)3; + *msg++ = ERL_DRV_LIST; + *msg++ = (ErlDrvTermData)3; + *msg++ = ERL_DRV_STRING; + *msg++ = (ErlDrvTermData)"kalle"; + *msg++ = (ErlDrvTermData)5; + *msg++ = ERL_DRV_FLOAT; + *msg++ = (ErlDrvTermData)&f; + *msg++ = ERL_DRV_PID; + *msg++ = driver_connected(erlang_port); + *msg++ = ERL_DRV_MAP; + *msg++ = (ErlDrvTermData)0; + if (buf[i] == 42) { + *msg++ = ERL_DRV_ATOM; + *msg++ = driver_mk_atom("blurf"); + } + *msg++ = ERL_DRV_MAP; + *msg++ = (ErlDrvTermData)4; + break; + } case 127: /* Error cases */ { @@ -662,6 +710,22 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) FAIL_TERM(msg, 2); } + msg[0] = ERL_DRV_MAP; + msg[1] = (ErlDrvTermData) 0; + FAIL_TERM(msg, 1); + + /* map with duplicate key */ + msg[0] = ERL_DRV_ATOM; + msg[1] = driver_mk_atom("key"); + msg[2] = ERL_DRV_NIL; + msg[3] = ERL_DRV_ATOM; + msg[4] = driver_mk_atom("key"); + msg[5] = ERL_DRV_INT; + msg[6] = (ErlDrvTermData) -4711; + msg[7] = ERL_DRV_MAP; + msg[8] = 2; + FAIL_TERM(msg, 9); + /* Signal end of test case */ msg[0] = ERL_DRV_NIL; erl_drv_output_term(driver_mk_port(erlang_port), msg, 1); -- cgit v1.2.3 From dfce3c3ea8ed4064856c29a678c83fc277a05bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 14:18:14 +0100 Subject: erts: Update preloaded erts_internal.beam --- erts/preloaded/ebin/erts_internal.beam | Bin 4096 -> 4276 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 12b36913a9..4a84b3945a 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ -- cgit v1.2.3 From 543074e7aab966a597e405bc52c94e57358663a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 14:47:42 +0100 Subject: erts: Fixup enif_make_map_put on windows --- erts/emulator/beam/erl_nif_api_funcs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 4121378e1a..d7c554e60b 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -300,7 +300,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map) # define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size) # define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map) -# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put) +# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_make_map_put) # define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value) # define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update) # define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove) -- cgit v1.2.3 From a7ff048b6b5d8649d1709a90f7514fccbb5e9235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 16:04:49 +0100 Subject: erts: Update maps_fold test to respect maps:fold/3 --- erts/emulator/test/map_SUITE.erl | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 75381de556..faaafb33bc 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -816,18 +816,11 @@ t_bif_map_from_list(Config) when is_list(Config) -> %% Maps module, not BIFs t_maps_fold(_Config) -> - Vs = lists:seq(1,100), - Rs = lists:reverse(Vs), M = maps:from_list([{{k,I},{v,I}}||I<-Vs]), - %% foldl - 5050 = maps:foldl(fun({k,_},{v,V},A) -> V + A end, 0, M), - Rs = maps:foldl(fun({k,_},{v,V},A) -> [V|A] end, [], M), - - %% foldr - 5050 = maps:foldr(fun({k,_},{v,V},A) -> V + A end, 0, M), - Vs = maps:foldr(fun({k,_},{v,V},A) -> [V|A] end, [], M), + %% fold + 5050 = maps:fold(fun({k,_},{v,V},A) -> V + A end, 0, M), ok. -- cgit v1.2.3 From 7849ea4d398717bd48bad76c551dcc5322fe39cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 17:39:37 +0100 Subject: erts: Strengthen map_SUITE tests * Add tests for maps:merge/2 * Add tests for maps:update/3 * Test more corner cases --- erts/emulator/test/map_SUITE.erl | 83 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index faaafb33bc..31c1486f1c 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -37,9 +37,11 @@ t_bif_map_find/1, t_bif_map_is_key/1, t_bif_map_keys/1, + t_bif_map_merge/1, t_bif_map_new/1, t_bif_map_put/1, t_bif_map_remove/1, + t_bif_map_update/1, t_bif_map_values/1, t_bif_map_to_list/1, t_bif_map_from_list/1, @@ -76,8 +78,10 @@ all() -> [ %% Specific Map BIFs t_bif_map_get,t_bif_map_find,t_bif_map_is_key, - t_bif_map_keys,t_bif_map_new,t_bif_map_put, - t_bif_map_remove,t_bif_map_values, + t_bif_map_keys, t_bif_map_merge, t_bif_map_new, + t_bif_map_put, + t_bif_map_remove, t_bif_map_update, + t_bif_map_values, t_bif_map_to_list, t_bif_map_from_list, %% erlang @@ -478,8 +482,8 @@ t_bif_map_find(Config) when is_list(Config) -> error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key - {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])), - {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id([]))), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id(<<>>))), ok. @@ -496,11 +500,16 @@ t_bif_map_is_key(Config) when is_list(Config) -> false = maps:is_key("h", M1), false = maps:is_key("hello", M1), false = maps:is_key(atom, M1), + false = maps:is_key(any, id(#{})), false = maps:is_key("hi", maps:remove("hi", M1)), true = maps:is_key("hi", M1), true = maps:is_key(1, maps:put(1, "number", M1)), false = maps:is_key(1.0, maps:put(1, "number", M1)), + + %% error case + {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id([]))), + {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id(<<>>))), ok. t_bif_map_keys(Config) when is_list(Config) -> @@ -526,6 +535,34 @@ t_bif_map_new(Config) when is_list(Config) -> 0 = erlang:map_size(maps:new()), ok. +t_bif_map_merge(Config) when is_list(Config) -> + 0 = erlang:map_size(maps:merge(#{},#{})), + + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:merge(#{}, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:merge(M0, #{}), + + M1 = #{ "hi" => "hello again", float => 3.3, {1,2} => "tuple", 4 => integer }, + + #{4 := number, 18446744073709551629 := wat, float := 3.3, int := 3, + {1,2} := "tuple", "hi" := "hello", <<"key">> := <<"value">>} = maps:merge(M1,M0), + + #{4 := integer, 18446744073709551629 := wat, float := 3.3, int := 3, + {1,2} := "tuple", "hi" := "hello again", <<"key">> := <<"value">>} = maps:merge(M0,M1), + + %% error case + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)), + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))), + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )), + + ok. + + t_bif_map_put(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, @@ -555,6 +592,11 @@ t_bif_map_put(Config) when is_list(Config) -> [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5), [number,wat,3,"hello",<<"value">>] = maps:values(M5), + M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5), + + [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M6), + [number,wat,3,"hello",<<"other value">>] = maps:values(M6), + %% error case {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)), {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)), @@ -564,6 +606,8 @@ t_bif_map_put(Config) when is_list(Config) -> ok. t_bif_map_remove(Config) when is_list(Config) -> + 0 = erlang:map_size(maps:remove(some_key, #{})), + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, @@ -600,6 +644,33 @@ t_bif_map_remove(Config) when is_list(Config) -> {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)), ok. +t_bif_map_update(Config) when is_list(Config) -> + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + #{ "hi" := "hello again", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:update("hi", "hello again", M0), + + #{ "hi" := "hello", int := 1337, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:update(int, 1337, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"new value">>, + 4 := number, 18446744073709551629 := wat} = maps:update(<<"key">>, <<"new value">>, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := integer, 18446744073709551629 := wat} = maps:update(4, integer, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wazzup} = maps:update(18446744073709551629, wazzup, M0), + + %% error case + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,{})), + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,<<"value">>)), + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(5,none,M0)), + + ok. + + t_bif_map_values(Config) when is_list(Config) -> @@ -810,6 +881,10 @@ t_bif_map_from_list(Config) when is_list(Config) -> {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]), %% error cases + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},b]))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},{b,b,3}]))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},<<>>]))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b}|{b,a}]))), {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))), {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), ok. -- cgit v1.2.3 From b7c7cdb8ac922f571d0ace3a3cd0cd5149606480 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 29 Jan 2014 16:51:06 +0100 Subject: Make it easier to revert +Macul default --- erts/emulator/beam/erl_alloc.c | 117 +++++++++++++---------------------------- 1 file changed, 38 insertions(+), 79 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index c6b324dc15..ca7e39041b 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -242,32 +242,13 @@ do { \ sys_memcpy((void *) (IP), (void *) &aui__, sizeof(struct au_init)); \ } while (0) -#if ERTS_ALC_DEFAULT_ACUL \ - || ERTS_ALC_DEFAULT_ACUL_LL_ALLOC \ - || ERTS_ALC_DEFAULT_ACUL_EHEAP_ALLOC - -static ERTS_INLINE void -set_default_acul(struct au_init *ip, int acul) -{ - ip->thr_spec = 1; - ip->atype = AOFIRSTFIT; - ip->init.aoff.flavor = AOFF_BF; - ip->init.util.acul = acul; -} - -#endif - static void set_default_sl_alloc_opts(struct au_init *ip) { SET_DEFAULT_ALLOC_OPTS(ip); ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); -#if ERTS_ALC_DEFAULT_ACUL - set_default_acul(ip, ERTS_ALC_DEFAULT_ACUL); -#else ip->thr_spec = 1; ip->atype = GOODFIT; -#endif ip->init.util.name_prefix = "sl_"; ip->init.util.alloc_no = ERTS_ALC_A_SHORT_LIVED; #ifndef SMALL_MEMORY @@ -281,7 +262,7 @@ set_default_sl_alloc_opts(struct au_init *ip) ip->init.util.force = 1; ip->init.util.low_mem = 1; #endif - + ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL; } static void @@ -289,12 +270,8 @@ set_default_std_alloc_opts(struct au_init *ip) { SET_DEFAULT_ALLOC_OPTS(ip); ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); -#if ERTS_ALC_DEFAULT_ACUL - set_default_acul(ip, ERTS_ALC_DEFAULT_ACUL); -#else ip->thr_spec = 1; ip->atype = BESTFIT; -#endif ip->init.util.name_prefix = "std_"; ip->init.util.alloc_no = ERTS_ALC_A_STANDARD; #ifndef SMALL_MEMORY @@ -303,6 +280,7 @@ set_default_std_alloc_opts(struct au_init *ip) ip->init.util.mmbcs = 32*1024; /* Main carrier size */ #endif ip->init.util.ts = ERTS_ALC_MTA_STANDARD; + ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL; } static void @@ -310,13 +288,9 @@ set_default_ll_alloc_opts(struct au_init *ip) { SET_DEFAULT_ALLOC_OPTS(ip); ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); -#if ERTS_ALC_DEFAULT_ACUL_LL_ALLOC - set_default_acul(ip, ERTS_ALC_DEFAULT_ACUL_LL_ALLOC); -#else ip->thr_spec = 0; ip->atype = BESTFIT; ip->init.bf.ao = 1; -#endif ip->init.util.ramv = 0; ip->init.util.mmsbc = 0; ip->init.util.sbct = ~((UWord) 0); @@ -332,6 +306,7 @@ set_default_ll_alloc_opts(struct au_init *ip) ip->init.util.rsbcst = 0; ip->init.util.rsbcmt = 0; ip->init.util.rmbcmt = 0; + ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL_LL_ALLOC; } static void @@ -363,12 +338,8 @@ set_default_eheap_alloc_opts(struct au_init *ip) { SET_DEFAULT_ALLOC_OPTS(ip); ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); -#if ERTS_ALC_DEFAULT_ACUL_EHEAP_ALLOC - set_default_acul(ip, ERTS_ALC_DEFAULT_ACUL_EHEAP_ALLOC); -#else ip->thr_spec = 1; ip->atype = GOODFIT; -#endif ip->init.util.name_prefix = "eheap_"; ip->init.util.alloc_no = ERTS_ALC_A_EHEAP; #ifndef SMALL_MEMORY @@ -382,6 +353,7 @@ set_default_eheap_alloc_opts(struct au_init *ip) ip->init.util.force = 1; ip->init.util.low_mem = 1; #endif + ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL_EHEAP_ALLOC; } static void @@ -389,12 +361,8 @@ set_default_binary_alloc_opts(struct au_init *ip) { SET_DEFAULT_ALLOC_OPTS(ip); ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); -#if ERTS_ALC_DEFAULT_ACUL - set_default_acul(ip, ERTS_ALC_DEFAULT_ACUL); -#else ip->thr_spec = 1; ip->atype = BESTFIT; -#endif ip->init.util.name_prefix = "binary_"; ip->init.util.alloc_no = ERTS_ALC_A_BINARY; #ifndef SMALL_MEMORY @@ -403,6 +371,7 @@ set_default_binary_alloc_opts(struct au_init *ip) ip->init.util.mmbcs = 32*1024; /* Main carrier size */ #endif ip->init.util.ts = ERTS_ALC_MTA_BINARY; + ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL; } static void @@ -410,12 +379,8 @@ set_default_ets_alloc_opts(struct au_init *ip) { SET_DEFAULT_ALLOC_OPTS(ip); ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); -#if ERTS_ALC_DEFAULT_ACUL - set_default_acul(ip, ERTS_ALC_DEFAULT_ACUL); -#else ip->thr_spec = 1; ip->atype = BESTFIT; -#endif ip->init.util.name_prefix = "ets_"; ip->init.util.alloc_no = ERTS_ALC_A_ETS; #ifndef SMALL_MEMORY @@ -424,6 +389,7 @@ set_default_ets_alloc_opts(struct au_init *ip) ip->init.util.mmbcs = 32*1024; /* Main carrier size */ #endif ip->init.util.ts = ERTS_ALC_MTA_ETS; + ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL; } static void @@ -431,12 +397,8 @@ set_default_driver_alloc_opts(struct au_init *ip) { SET_DEFAULT_ALLOC_OPTS(ip); ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); -#if ERTS_ALC_DEFAULT_ACUL - set_default_acul(ip, ERTS_ALC_DEFAULT_ACUL); -#else ip->thr_spec = 1; ip->atype = BESTFIT; -#endif ip->init.util.name_prefix = "driver_"; ip->init.util.alloc_no = ERTS_ALC_A_DRIVER; #ifndef SMALL_MEMORY @@ -445,6 +407,7 @@ set_default_driver_alloc_opts(struct au_init *ip) ip->init.util.mmbcs = 32*1024; /* Main carrier size */ #endif ip->init.util.ts = ERTS_ALC_MTA_DRIVER; + ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL; } static void @@ -453,12 +416,8 @@ set_default_fix_alloc_opts(struct au_init *ip, { SET_DEFAULT_ALLOC_OPTS(ip); ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); -#if ERTS_ALC_DEFAULT_ACUL - set_default_acul(ip, ERTS_ALC_DEFAULT_ACUL); -#else ip->thr_spec = 1; ip->atype = BESTFIT; -#endif ip->init.bf.ao = 1; ip->init.util.name_prefix = "fix_"; ip->init.util.fix_type_size = fix_type_sizes; @@ -469,6 +428,7 @@ set_default_fix_alloc_opts(struct au_init *ip, ip->init.util.mmbcs = 128*1024; /* Main carrier size */ #endif ip->init.util.ts = ERTS_ALC_MTA_FIXED_SIZE; + ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL; } #ifdef ERTS_SMP @@ -560,26 +520,25 @@ strategy_support_carrier_migration(struct au_init *auip) } static ERTS_INLINE void -check_disable_carrier_migration(struct au_init *auip) -{ - if (!strategy_support_carrier_migration(auip) || !auip->thr_spec) - auip->init.util.acul = 0; -} - -static ERTS_INLINE void -ensure_carrier_migration_support(struct au_init *auip) +adjust_carrier_migration_support(struct au_init *auip) { - auip->thr_spec = 1; /* Need thread preferred */ +#ifdef ERTS_SMP + if (auip->init.util.acul) { + auip->thr_spec = -1; /* Need thread preferred */ - /* - * If strategy cannot handle carrier migration, - * default to a strategy that can... - */ - if (!strategy_support_carrier_migration(auip)) { - /* Default to aoffcbf */ - auip->atype = AOFIRSTFIT; - auip->init.aoff.flavor = AOFF_BF; + /* + * If strategy cannot handle carrier migration, + * default to a strategy that can... + */ + if (!strategy_support_carrier_migration(auip)) { + /* Default to aoffcbf */ + auip->atype = AOFIRSTFIT; + auip->init.aoff.flavor = AOFF_BF; + } } +#else + auip->init.util.acul = 0; +#endif } void @@ -683,15 +642,16 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) init.fix_alloc.thr_spec = 0; } - check_disable_carrier_migration(&init.sl_alloc); - check_disable_carrier_migration(&init.std_alloc); - check_disable_carrier_migration(&init.ll_alloc); - check_disable_carrier_migration(&init.eheap_alloc); - check_disable_carrier_migration(&init.binary_alloc); - check_disable_carrier_migration(&init.ets_alloc); - check_disable_carrier_migration(&init.driver_alloc); - check_disable_carrier_migration(&init.fix_alloc); - + /* Make adjustments for carrier migration support */ + init.temp_alloc.init.util.acul = 0; + adjust_carrier_migration_support(&init.sl_alloc); + adjust_carrier_migration_support(&init.std_alloc); + adjust_carrier_migration_support(&init.ll_alloc); + adjust_carrier_migration_support(&init.eheap_alloc); + adjust_carrier_migration_support(&init.binary_alloc); + adjust_carrier_migration_support(&init.ets_alloc); + adjust_carrier_migration_support(&init.driver_alloc); + adjust_carrier_migration_support(&init.fix_alloc); #ifdef ERTS_SMP /* Only temp_alloc can use thread specific interface */ @@ -1290,8 +1250,6 @@ handle_au_arg(struct au_init *auip, break; } } - ensure_carrier_migration_support(auip); - auip->init.util.acul = get_acul_value(auip, sub_param + 4, argv, ip); } else if(has_prefix("asbcst", sub_param)) { @@ -1328,7 +1286,8 @@ handle_au_arg(struct au_init *auip, else { bad_value(param, sub_param + 1, alg); } - check_disable_carrier_migration(auip); + if (!strategy_support_carrier_migration(auip)) + auip->init.util.acul = 0; } else goto bad_switch; @@ -1409,7 +1368,7 @@ handle_au_arg(struct au_init *auip, } else if (res == 0) { auip->thr_spec = 0; - check_disable_carrier_migration(auip); + auip->init.util.acul = 0; break; } goto bad_switch; @@ -1607,7 +1566,7 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) for (a = 0; a < aui_sz; a++) { aui[a]->thr_spec = 0; - check_disable_carrier_migration(aui[a]); + aui[a]->init.util.acul = 0; aui[a]->init.util.ramv = 0; aui[a]->init.util.lmbcs = 5*1024*1024; } -- cgit v1.2.3 From 4a0aa563b9776e33bca19459bfefc81b7ac900c9 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 31 Jan 2014 16:52:51 +0100 Subject: Fix +Mea config --- erts/emulator/beam/erl_alloc.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index ca7e39041b..64b3e01ed4 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -629,8 +629,21 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) init.fix_alloc.thr_spec = 0; #endif + /* Make adjustments for carrier migration support */ + init.temp_alloc.init.util.acul = 0; + adjust_carrier_migration_support(&init.sl_alloc); + adjust_carrier_migration_support(&init.std_alloc); + adjust_carrier_migration_support(&init.ll_alloc); + adjust_carrier_migration_support(&init.eheap_alloc); + adjust_carrier_migration_support(&init.binary_alloc); + adjust_carrier_migration_support(&init.ets_alloc); + adjust_carrier_migration_support(&init.driver_alloc); + adjust_carrier_migration_support(&init.fix_alloc); + if (init.erts_alloc_config) { /* Adjust flags that erts_alloc_config won't like */ + + /* No thread specific instances */ init.temp_alloc.thr_spec = 0; init.sl_alloc.thr_spec = 0; init.std_alloc.thr_spec = 0; @@ -639,20 +652,20 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) init.binary_alloc.thr_spec = 0; init.ets_alloc.thr_spec = 0; init.driver_alloc.thr_spec = 0; - init.fix_alloc.thr_spec = 0; + init.fix_alloc.thr_spec = 0; + + /* No carrier migration */ + init.temp_alloc.init.util.acul = 0; + init.sl_alloc.init.util.acul = 0; + init.std_alloc.init.util.acul = 0; + init.ll_alloc.init.util.acul = 0; + init.eheap_alloc.init.util.acul = 0; + init.binary_alloc.init.util.acul = 0; + init.ets_alloc.init.util.acul = 0; + init.driver_alloc.init.util.acul = 0; + init.fix_alloc.init.util.acul = 0; } - /* Make adjustments for carrier migration support */ - init.temp_alloc.init.util.acul = 0; - adjust_carrier_migration_support(&init.sl_alloc); - adjust_carrier_migration_support(&init.std_alloc); - adjust_carrier_migration_support(&init.ll_alloc); - adjust_carrier_migration_support(&init.eheap_alloc); - adjust_carrier_migration_support(&init.binary_alloc); - adjust_carrier_migration_support(&init.ets_alloc); - adjust_carrier_migration_support(&init.driver_alloc); - adjust_carrier_migration_support(&init.fix_alloc); - #ifdef ERTS_SMP /* Only temp_alloc can use thread specific interface */ if (init.temp_alloc.thr_spec) -- cgit v1.2.3 From e5ccb57c0e45535d4b2b34cc85b177056b67b2da Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 31 Jan 2014 20:09:44 +0100 Subject: Document maps-related abstract syntax trees {map,Loc,Fields} {map,Loc,Argument,Fields} {map_field_assoc,Loc,Name,Value} {map_field_exact,Loc,Name,Value} --- erts/doc/src/absform.xml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 4acc03b133..835a4fc692 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -217,6 +217,14 @@ Rep(E) = . If E is , then Rep(E) = . + If E is where each + is a map assoc or exact field, then Rep(E) = + . For Rep(W), see + below. + If E is where + is a map assoc or exact field, then Rep(E) = + . For + Rep(W), see below. If E is , then Rep(E) = . If E is , then @@ -334,6 +342,21 @@ is an integer, Rep(TS) = .

+ +
+ Map assoc and exact fields +

When W is an assoc or exact field (in the body of a map), then:

+ + If W is an assoc field V]]>, where + and are both expressions, + then Rep(W) = . + + If W is an exact field , where + and are both expressions, + then Rep(W) = . + + +
-- cgit v1.2.3 From 9ae4a522ea841a880d94e2b1e683e470cf2c5037 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 3 Feb 2014 17:58:24 +0100 Subject: erts: Fix bug in binary_to_term for maps 'maps_head' was not restored when yielding. Risk for crash increases with size and number of maps in term. --- erts/emulator/beam/external.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index a4cc3435c3..9fb2dbd8bf 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1169,6 +1169,7 @@ typedef struct { Eterm* hp_end; int remaining_n; char* remaining_bytes; + Eterm* maps_head; } B2TDecodeContext; typedef struct { @@ -1486,6 +1487,7 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con ctx->u.dc.hp_start = HAlloc(p, ctx->heap_size); ctx->u.dc.hp = ctx->u.dc.hp_start; ctx->u.dc.hp_end = ctx->u.dc.hp_start + ctx->heap_size; + ctx->u.dc.maps_head = NULL; ctx->state = B2TDecode; /*fall through*/ case B2TDecode: @@ -2878,7 +2880,7 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, int n; ErtsAtomEncoding char_enc; register Eterm* hp; /* Please don't take the address of hp */ - Eterm *maps_head = NULL; /* for validation of maps */ + Eterm *maps_head; /* for validation of maps */ Eterm* next; SWord reds; @@ -2888,6 +2890,7 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, next = ctx->u.dc.next; ep = ctx->u.dc.ep; hpp = &ctx->u.dc.hp; + maps_head = ctx->u.dc.maps_head; if (ctx->state != B2TDecode) { int n_limit = reds; @@ -2968,6 +2971,7 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, reds = ERTS_SWORD_MAX; next = objp; *next = (Eterm) (UWord) NULL; + maps_head = NULL; } hp = *hpp; @@ -3780,6 +3784,7 @@ dec_term_atom_common: ctx->u.dc.ep = ep; ctx->u.dc.next = next; ctx->u.dc.hp = hp; + ctx->u.dc.maps_head = maps_head; ctx->reds = 0; return NULL; } -- cgit v1.2.3 From 21f2d57e5f7919d468fe89e38c52f978e15c1dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 4 Feb 2014 15:04:24 +0100 Subject: gzio.c: Remove unnecessary usage of the OF() macro Daniel Goertzen reported that commit 8a147a7365 broke building of Erlang/OTP on Gentoo Linux because the macro OF() was missing. Apparently, on Gentoo the OF() macro in zconf.h has been renamed to _Z_OF() (to avoid polluting the global namespace). Don't use the OF() macro in gzio.c since it no longer serves any useful purpose (it provided compatibility with pre-ANSI/ISO C compilers, but the rest of Erlang/OTP requires an ANSI/ISO C compiler anyway). --- erts/emulator/drivers/common/gzio.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/gzio.c b/erts/emulator/drivers/common/gzio.c index 653f3954b1..8ec2c3f762 100644 --- a/erts/emulator/drivers/common/gzio.c +++ b/erts/emulator/drivers/common/gzio.c @@ -73,15 +73,15 @@ typedef struct gz_stream { int transparent; /* 1 if input file is not a .gz file */ char mode; /* 'w' or 'r' */ int position; /* Position (for seek) */ - int (*destroy)OF((struct gz_stream*)); /* Function to destroy + int (*destroy)(struct gz_stream*); /* Function to destroy * this structure. */ } gz_stream; -local ErtsGzFile gz_open OF((const char *path, const char *mode)); -local int get_byte OF((gz_stream *s)); -local void check_header OF((gz_stream *s)); -local int destroy OF((gz_stream *s)); -local uLong getLong OF((gz_stream *s)); +local ErtsGzFile gz_open (const char *path, const char *mode); +local int get_byte (gz_stream *s); +local void check_header (gz_stream *s); +local int destroy (gz_stream *s); +local uLong getLong (gz_stream *s); #ifdef UNIX /* -- cgit v1.2.3 From bf011d8116e0102080db8b2586acb8ecb60152d6 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 5 Feb 2014 11:39:45 +0100 Subject: erts: Rewrite dtrace configure test The previous version printed error messages from the dtrace command to stderr. This output is now printed to config.log instead. --- erts/configure.in | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 8d245252b5..c20bb032f1 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3848,28 +3848,26 @@ esac if test "$enable_dtrace_test" = "yes" ; then if test "$DTRACE" = "dtrace" ; then AC_CHECK_HEADERS(sys/sdt.h) + AC_MSG_CHECKING([for 1-stage DTrace precompilation]) # The OS X version of dtrace prints a spurious line here. if ! dtrace -h $DTRACE_CPP -Iemulator/beam -o ./foo-dtrace.h -s emulator/beam/erlang_dtrace.d; then AC_MSG_ERROR([Could not precompile erlang_dtrace.d: dtrace -h failed]) fi + AC_MSG_RESULT([yes]) - $RM -f dtest.{o,c} - cat > dtest.c <<_DTEST - #include "foo-dtrace.h" - int main(void) { ERLANG_DIST_PORT_BUSY_ENABLED(); return 0; } -_DTEST - $CC $CFLAGS -c -o dtest.o dtest.c - - $RM -f $DTRACE_2STEP_TEST - if dtrace -G $DTRACE_CPP $DTRACE_BITS_FLAG -Iemulator/beam -o $DTRACE_2STEP_TEST -s emulator/beam/erlang_dtrace.d dtest.o && \ - test -f $DTRACE_2STEP_TEST ; then - rm $DTRACE_2STEP_TEST + AC_MSG_CHECKING([for 2-stage DTrace precompilation]) + AC_TRY_COMPILE([ #include "foo-dtrace.h" ], + [ERLANG_DIST_PORT_BUSY_ENABLED();], + [$RM -f $DTRACE_2STEP_TEST + dtrace -G $DTRACE_CPP $DTRACE_BITS_FLAG -Iemulator/beam -o $DTRACE_2STEP_TEST -s emulator/beam/erlang_dtrace.d conftest.$OBJEXT 2>&AS_MESSAGE_LOG_FD + if test -f $DTRACE_2STEP_TEST; then + rm $DTRACE_2STEP_TEST DTRACE_ENABLED_2STEP=yes - AC_MSG_NOTICE([dtrace precompilation for 2-stage DTrace successful]) - else - AC_MSG_NOTICE([dtrace precompilation for 1-stage DTrace successful]) - fi - $RM -f dtest.{o,c} foo-dtrace.h + fi], + []) + AS_IF([test "x$DTRACE_ENABLED_2STEP" = "xyes"], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])]) DTRACE_ENABLED=yes case $OPSYS in -- cgit v1.2.3 From eb3aa83b0e93c766309a99a5c7e6c251ee90275a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 5 Feb 2014 16:59:58 +0100 Subject: erts: Fix driver_SUITE:otp_9302 for VM without threads --- .../emulator/test/driver_SUITE_data/otp_9302_drv.c | 27 ++++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c index 7c144d20cf..ad29d17f06 100644 --- a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c +++ b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2013. All Rights Reserved. + * Copyright Ericsson AB 2011-2014. All Rights Reserved. * * The 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 @@ DRIVER_INIT(otp_9302_drv) static void stop(ErlDrvData drv_data) { Otp9302Data *data = (Otp9302Data *) drv_data; - if (!data->smp) + if (data->msgq.mtx) erl_drv_mutex_destroy(data->msgq.mtx); driver_free(data); } @@ -114,13 +114,16 @@ static ErlDrvData start(ErlDrvPort port, driver_system_info(&sys_info, sizeof(ErlDrvSysInfo)); data->smp = sys_info.smp_support; + data->msgq.mtx = NULL; if (!data->smp) { data->msgq.start = NULL; data->msgq.end = NULL; - data->msgq.mtx = erl_drv_mutex_create(""); - if (!data->msgq.mtx) { - driver_free(data); - return ERL_DRV_ERROR_GENERAL; + if (sys_info.thread_support) { + data->msgq.mtx = erl_drv_mutex_create(""); + if (!data->msgq.mtx) { + driver_free(data); + return ERL_DRV_ERROR_GENERAL; + } } } @@ -143,19 +146,22 @@ static void enqueue_reply(Otp9302AsyncData *adata) Otp9302MsgQ *msgq = adata->msgq; adata->next = NULL; adata->refc++; - erl_drv_mutex_lock(msgq->mtx); + if (msgq->mtx) + erl_drv_mutex_lock(msgq->mtx); if (msgq->end) msgq->end->next = adata; else msgq->end = msgq->start = adata; msgq->end = adata; - erl_drv_mutex_unlock(msgq->mtx); + if (msgq->mtx) + erl_drv_mutex_unlock(msgq->mtx); } static void dequeue_replies(Otp9302AsyncData *adata) { Otp9302MsgQ *msgq = adata->msgq; - erl_drv_mutex_lock(msgq->mtx); + if (msgq->mtx) + erl_drv_mutex_lock(msgq->mtx); if (--adata->refc == 0) driver_free(adata); while (msgq->start) { @@ -166,7 +172,8 @@ static void dequeue_replies(Otp9302AsyncData *adata) driver_free(adata); } msgq->start = msgq->end = NULL; - erl_drv_mutex_unlock(msgq->mtx); + if (msgq->mtx) + erl_drv_mutex_unlock(msgq->mtx); } static void async_invoke(void *data) -- cgit v1.2.3 From c3fb1d51b73867a9045171e812cc0acb82126edd Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 5 Feb 2014 18:04:37 +0100 Subject: erts: Skip driver_SUITE:thr_free_drv for VM without threads --- erts/emulator/test/driver_SUITE.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 06211406b4..c62bc0c454 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1945,6 +1945,14 @@ otp_9302(Config) when is_list(Config) -> end. thr_free_drv(Config) when is_list(Config) -> + case erlang:system_info(threads) of + false -> + {skipped, "No thread support"}; + true -> + thr_free_drv_do(Config) + end. + +thr_free_drv_do(Config) -> ?line Path = ?config(data_dir, Config), ?line erl_ddll:start(), ?line ok = load_driver(Path, thr_free_drv), -- cgit v1.2.3 From 1f7324d0fd72b4137fb1f21b0b759ffebdf7ea90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 5 Feb 2014 19:07:22 +0100 Subject: erts: Fix Maps for beam_load Map source may be anything, not only registers. --- erts/emulator/beam/beam_emu.c | 4 ++-- erts/emulator/beam/ops.tab | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 89d9442526..4d8ea354ce 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2355,7 +2355,7 @@ void process_main(void) Next(4+Arg(3)); } - OpCase(update_map_assoc_jddII): { + OpCase(update_map_assoc_jsdII): { Eterm res; Eterm map; @@ -2373,7 +2373,7 @@ void process_main(void) } } - OpCase(update_map_exact_jddII): { + OpCase(update_map_exact_jsdII): { Eterm res; Eterm map; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index f35997efee..9ea3a3a8ea 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1482,8 +1482,8 @@ put_map_exact F Src Dst Live Size Rest=* => \ update_map_exact F Src Dst Live Size Rest new_map j d I I -update_map_assoc j d d I I -update_map_exact j d d I I +update_map_assoc j s d I I +update_map_exact j s d I I is_map Fail cq => jump Fail -- cgit v1.2.3 From 4082c3d1ad3d0e8bb85706c5f3b2bde97bb43fcb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 5 Feb 2014 19:41:28 +0100 Subject: erts: Fix NIF bug when load/upgrade fails after enif_open_resource_type ..has been successfully called. Opened resource types (created or taken-over) were left "hanging" leading both to memory leakage and other more strange and serious behavior. Now a proper rollback is done. --- erts/emulator/beam/erl_nif.c | 113 ++++++++++++++++----- erts/emulator/test/nif_SUITE.erl | 136 +++++++++++++++++++++++++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 3 +- erts/emulator/test/nif_SUITE_data/nif_mod.c | 47 ++++++--- 4 files changed, 257 insertions(+), 42 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index dc285b3cf7..b2fd3dcd24 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -1235,6 +1235,19 @@ static void steal_resource_type(ErlNifResourceType* type) } } +/* The opened_rt_list is used by enif_open_resource_type() + * in order to rollback "creates" and "take-overs" in case the load fails. + */ +struct opened_resource_type +{ + struct opened_resource_type* next; + + ErlNifResourceFlags op; + ErlNifResourceType* type; + ErlNifResourceDtor* new_dtor; +}; +static struct opened_resource_type* opened_rt_list = NULL; + ErlNifResourceType* enif_open_resource_type(ErlNifEnv* env, const char* module_str, @@ -1256,22 +1269,21 @@ enif_open_resource_type(ErlNifEnv* env, if (type == NULL) { if (flags & ERL_NIF_RT_CREATE) { type = erts_alloc(ERTS_ALC_T_NIF, - sizeof(struct enif_resource_type_t)); - type->dtor = dtor; + sizeof(struct enif_resource_type_t)); type->module = module_am; type->name = name_am; erts_refc_init(&type->refc, 1); - type->owner = env->mod_nif; - type->prev = &resource_type_list; - type->next = resource_type_list.next; - type->next->prev = type; - type->prev->next = type; op = ERL_NIF_RT_CREATE; + #ifdef DEBUG + type->dtor = (void*)1; + type->owner = (void*)2; + type->prev = (void*)3; + type->next = (void*)4; + #endif } } else { - if (flags & ERL_NIF_RT_TAKEOVER) { - steal_resource_type(type); + if (flags & ERL_NIF_RT_TAKEOVER) { op = ERL_NIF_RT_TAKEOVER; } else { @@ -1279,12 +1291,13 @@ enif_open_resource_type(ErlNifEnv* env, } } if (type != NULL) { - type->owner = env->mod_nif; - type->dtor = dtor; - if (type->dtor != NULL) { - erts_refc_inc(&type->owner->rt_dtor_cnt, 1); - } - erts_refc_inc(&type->owner->rt_cnt, 1); + struct opened_resource_type* ort = erts_alloc(ERTS_ALC_T_TMP, + sizeof(struct opened_resource_type)); + ort->op = op; + ort->type = type; + ort->new_dtor = dtor; + ort->next = opened_rt_list; + opened_rt_list = ort; } if (tried != NULL) { *tried = op; @@ -1292,6 +1305,51 @@ enif_open_resource_type(ErlNifEnv* env, return type; } +static void commit_opened_resource_types(struct erl_module_nif* lib) +{ + while (opened_rt_list) { + struct opened_resource_type* ort = opened_rt_list; + + ErlNifResourceType* type = ort->type; + + if (ort->op == ERL_NIF_RT_CREATE) { + type->prev = &resource_type_list; + type->next = resource_type_list.next; + type->next->prev = type; + type->prev->next = type; + } + else { /* ERL_NIF_RT_TAKEOVER */ + steal_resource_type(type); + } + + type->owner = lib; + type->dtor = ort->new_dtor; + + if (type->dtor != NULL) { + erts_refc_inc(&lib->rt_dtor_cnt, 1); + } + erts_refc_inc(&lib->rt_cnt, 1); + + opened_rt_list = ort->next; + erts_free(ERTS_ALC_T_TMP, ort); + } +} + +static void rollback_opened_resource_types(void) +{ + while (opened_rt_list) { + struct opened_resource_type* ort = opened_rt_list; + + if (ort->op == ERL_NIF_RT_CREATE) { + erts_free(ERTS_ALC_T_NIF, ort->type); + } + + opened_rt_list = ort->next; + erts_free(ERTS_ALC_T_TMP, ort); + } +} + + static void nif_resource_dtor(Binary* bin) { ErlNifResource* resource = (ErlNifResource*) ERTS_MAGIC_BIN_DATA(bin); @@ -1317,6 +1375,8 @@ void* enif_alloc_resource(ErlNifResourceType* type, size_t size) { Binary* bin = erts_create_magic_binary(SIZEOF_ErlNifResource(size), &nif_resource_dtor); ErlNifResource* resource = ERTS_MAGIC_BIN_DATA(bin); + + ASSERT(type->owner && type->next && type->prev); /* not allowed in load/upgrade */ resource->type = type; erts_refc_inc(&bin->refc, 1); #ifdef DEBUG @@ -1696,9 +1756,15 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) lib->entry = entry; erts_refc_init(&lib->rt_cnt, 0); erts_refc_init(&lib->rt_dtor_cnt, 0); + ASSERT(opened_rt_list == NULL); lib->mod = mod; env.mod_nif = lib; - if (mod->curr.nif != NULL) { /* Reload */ + if (mod->curr.nif != NULL) { /*************** Reload ******************/ + /* + * Repeated load_nif calls from same Erlang module instance ("reload") + * is deprecated and was only ment as a development feature not to + * be used in production systems. (See warning below) + */ int k; lib->priv_data = mod->curr.nif->priv_data; @@ -1730,6 +1796,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ret = load_nif_error(BIF_P, reload, "Library reload-call unsuccessful."); } else { + commit_opened_resource_types(lib); mod->curr.nif->entry = NULL; /* to prevent 'unload' callback */ erts_unload_nif(mod->curr.nif); reload_warning = 1; @@ -1737,7 +1804,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) } else { lib->priv_data = NULL; - if (mod->old.nif != NULL) { /* Upgrade */ + 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."); @@ -1750,17 +1817,18 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) 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) { - refresh_cached_nif_data(mod->old_code, mod->old_nif); - }*/ + else + commit_opened_resource_types(lib); } - else if (entry->load != NULL) { /* Initial load */ + else if (entry->load != NULL) { /********* Initial load ***********/ erts_pre_nif(&env, BIF_P, lib); veto = entry->load(&env, &lib->priv_data, BIF_ARG_2); erts_post_nif(&env); if (veto) { ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful."); } + else + commit_opened_resource_types(lib); } } if (ret == am_ok) { @@ -1789,6 +1857,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) } else { error: + rollback_opened_resource_types(); ASSERT(ret != am_ok); if (lib != NULL) { erts_free(ERTS_ALC_T_NIF, lib); diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 9a70e8646a..c88fa4b228 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -801,6 +801,138 @@ resource_takeover(Config) when is_list(Config) -> ?line ok = forget_resource(AN4), ?line [] = nif_mod_call_history(), + + %% + %% Test rollback after failed upgrade of same lib-version + %% + + {A5,BinA5} = make_resource(2, Holder, "A5"), + {NA5,BinNA5} = make_resource(0, Holder, "NA5"), + {AN5,_BinAN5} = make_resource(1, Holder, "AN5"), + + {A6,BinA6} = make_resource(2, Holder, "A6"), + {NA6,BinNA6} = make_resource(0, Holder, "NA6"), + {AN6,_BinAN6} = make_resource(1, Holder, "AN6"), + + {module,nif_mod} = erlang:load_module(nif_mod,ModBin), + undefined = nif_mod:lib_version(), + {error,{upgrade,_}} = + nif_mod:load_nif_lib(Config, 1, + [{resource_type, 4, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_CREATE, "Mr Pink", resource_dtor_A, + ?RT_CREATE}, + + {return, 1} % FAIL + ]), + + undefined = nif_mod:lib_version(), + [{upgrade,1,5,105}] = nif_mod_call_history(), + + %% Make sure dtor was not changed (from A to B) + ok = forget_resource(A5), + [{{resource_dtor_A_v1,BinA5},1,6,106}] = nif_mod_call_history(), + + %% Make sure dtor was not nullified (from A to null) + ok = forget_resource(NA5), + [{{resource_dtor_A_v1,BinNA5},1,7,107}] = nif_mod_call_history(), + + %% Make sure dtor was not added (from null to A) + ok = forget_resource(AN5), + [] = nif_mod_call_history(), + + %% + %% Test rollback after failed upgrade of other lib-version + %% + + {error,{upgrade,_}} = + nif_mod:load_nif_lib(Config, 2, + [{resource_type, 4, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, null, ?RT_TAKEOVER, "Mr Pink", resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_CREATE, "Mr Pink", resource_dtor_A, + ?RT_CREATE}, + + {return, 1} % FAIL + ]), + + undefined = nif_mod:lib_version(), + [{upgrade,2,_,_}] = nif_mod_call_history(), + + %% Make sure dtor was not changed (from A to B) + ok = forget_resource(A6), + [{{resource_dtor_A_v1,BinA6},1,_,_}] = nif_mod_call_history(), + + %% Make sure dtor was not nullified (from A to null) + ok = forget_resource(NA6), + [{{resource_dtor_A_v1,BinNA6},1,_,_}] = nif_mod_call_history(), + + %% Make sure dtor was not added (from null to A) + ok = forget_resource(AN6), + [] = nif_mod_call_history(), + + %% + %% Test rolback after failed initial load + %% + false = code:purge(nif_mod), + [{unload,1,_,_}] = nif_mod_call_history(), + true = code:delete(nif_mod), + false = code:purge(nif_mod), + [] = nif_mod_call_history(), + + + {module,nif_mod} = erlang:load_module(nif_mod,ModBin), + undefined = nif_mod:lib_version(), + {error,{load,_}} = + nif_mod:load_nif_lib(Config, 1, + [{resource_type, null, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, + ?RT_CREATE}, + {resource_type, 4, ?RT_CREATE, "resource_type_A_null",resource_dtor_A, + ?RT_CREATE}, + {resource_type, 4, ?RT_CREATE, "Mr Pink", resource_dtor_A, + ?RT_CREATE}, + + {return, 1} % FAIL + ]), + + undefined = nif_mod:lib_version(), + ok = nif_mod:load_nif_lib(Config, 1, + [{resource_type, null, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A", + resource_dtor_A, ?RT_CREATE}, + + {resource_type, 1, ?RT_CREATE, "resource_type_A_null", null, + ?RT_CREATE}, + {resource_type, null, ?RT_TAKEOVER, "Mr Pink", resource_dtor_A, + ?RT_TAKEOVER}, + + {return, 0} % SUCCESS + ]), + + ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), + ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), + + {NA7,BinNA7} = make_resource(0, Holder, "NA7"), + {AN7,BinAN7} = make_resource(1, Holder, "AN7"), + + ok = forget_resource(NA7), + [{{resource_dtor_A_v1,BinNA7},1,_,_}] = nif_mod_call_history(), + + ok = forget_resource(AN7), + [] = nif_mod_call_history(), + ?line true = lists:member(?MODULE, erlang:system_info(taints)), ?line true = lists:member(nif_mod, erlang:system_info(taints)), ?line verify_tmpmem(TmpMem), @@ -1343,7 +1475,7 @@ consume_timeslice(Config) when is_list(Config) -> ok. -next_msg(Pid) -> +next_msg(_Pid) -> receive M -> M after 100 -> diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 0c4a9f7e5c..cdc64b89b2 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -1477,7 +1477,6 @@ static ERL_NIF_TERM consume_timeslice_nif(ErlNifEnv* env, int argc, const ERL_NI { int percent; char atom[10]; - int do_repeat; if (!enif_get_int(env, argv[0], &percent) || !enif_get_atom(env, argv[1], atom, sizeof(atom), ERL_NIF_LATIN1)) { diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c index e32d10057c..aed8524635 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.c +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2010. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -41,6 +41,7 @@ static ERL_NIF_TERM am_null; static ERL_NIF_TERM am_resource_type; static ERL_NIF_TERM am_resource_dtor_A; static ERL_NIF_TERM am_resource_dtor_B; +static ERL_NIF_TERM am_return; static NifModPrivData* priv_data(ErlNifEnv* env) { @@ -54,6 +55,7 @@ static void init(ErlNifEnv* env) am_resource_type = enif_make_atom(env, "resource_type"); am_resource_dtor_A = enif_make_atom(env, "resource_dtor_A"); am_resource_dtor_B = enif_make_atom(env, "resource_dtor_B"); + am_return = enif_make_atom(env, "return"); } static void add_call_with_arg(ErlNifEnv* env, NifModPrivData* data, const char* func_name, @@ -105,19 +107,15 @@ static void resource_dtor_B(ErlNifEnv* env, void* a) } /* {resource_type, Ix|null, ErlNifResourceFlags in, "TypeName", dtor(A|B|null), ErlNifResourceFlags out}*/ -static void open_resource_type(ErlNifEnv* env, ERL_NIF_TERM op_tpl) +static void open_resource_type(ErlNifEnv* env, const ERL_NIF_TERM* arr) { NifModPrivData* data = priv_data(env); - const ERL_NIF_TERM* arr; - int arity; char rt_name[30]; union { ErlNifResourceFlags e; int i; } flags, exp_res, got_res; unsigned ix; ErlNifResourceDtor* dtor; ErlNifResourceType* got_ptr; - CHECK(enif_get_tuple(env, op_tpl, &arity, &arr)); - CHECK(arity == 6); CHECK(enif_is_identical(arr[0], am_resource_type)); CHECK(enif_get_int(env, arr[2], &flags.i)); CHECK(enif_get_string(env, arr[3], rt_name, sizeof(rt_name), ERL_NIF_LATIN1) > 0); @@ -147,18 +145,32 @@ static void open_resource_type(ErlNifEnv* env, ERL_NIF_TERM op_tpl) CHECK(got_res.e == exp_res.e); } -static void do_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info) +static void do_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info, int* retvalp) { NifModPrivData* data = priv_data(env); ERL_NIF_TERM head, tail; unsigned ix; + for (ix=0; ixrt_arr[ix] = NULL; } for (head = load_info; enif_get_list_cell(env, head, &head, &tail); head = tail) { - - open_resource_type(env, head); + const ERL_NIF_TERM* arr; + int arity; + + CHECK(enif_get_tuple(env, head, &arity, &arr)); + switch (arity) { + case 6: + open_resource_type(env, arr); + break; + case 2: + CHECK(arr[0] == am_return); + CHECK(enif_get_int(env, arr[1], retvalp)); + break; + default: + CHECK(0); + } } CHECK(enif_is_empty_list(env, head)); } @@ -166,6 +178,7 @@ static void do_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info) static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) { NifModPrivData* data; + int retval = 0; init(env); data = (NifModPrivData*) enif_alloc(sizeof(NifModPrivData)); @@ -177,38 +190,40 @@ static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) add_call(env, data, "load"); - do_load_info(env, load_info); + do_load_info(env, load_info, &retval); data->calls = 0; - return 0; + return retval; } static int reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) { NifModPrivData* data = (NifModPrivData*) *priv; + int retval = 0; init(env); add_call(env, data, "reload"); - do_load_info(env, load_info); - return 0; + do_load_info(env, load_info, &retval); + return retval; } static int upgrade(ErlNifEnv* env, void** priv, void** old_priv_data, ERL_NIF_TERM load_info) { NifModPrivData* data = (NifModPrivData*) *old_priv_data; + int retval = 0; init(env); add_call(env, data, "upgrade"); data->ref_cnt++; *priv = *old_priv_data; - do_load_info(env, load_info); + do_load_info(env, load_info, &retval); - return 0; + return retval; } static void unload(ErlNifEnv* env, void* priv) { NifModPrivData* data = (NifModPrivData*) priv; - int is_last; + add_call(env, data, "unload"); NifModPrivData_release(data); } -- cgit v1.2.3 From 5c90e67d6581cb7a94ec127a4dbe3ade6685f2fc Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 6 Feb 2014 11:39:59 +0100 Subject: erts: Fix dtrace generation on FreeBSD --- erts/emulator/Makefile.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index f88a4ccc24..74e0d26cff 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -513,10 +513,9 @@ ifdef DTRACE_ENABLED # global.h causes problems by including dtrace-wrapper.h which includes # the autogenerated erlang_dtrace.h ... so make erlang_dtrace.h very early. DTRACE_HEADERS = $(TARGET)/erlang_dtrace.h -generate: $(DTRACE_HEADERS) $(GENERATE) +GENERATE+= $(DTRACE_HEADERS) else DTRACE_HEADERS = -generate: $(GENERATE) endif ifdef HIPE_ENABLED -- cgit v1.2.3 From 963c06a5b72c2ac8d88a6910ee60248d6dca747d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 5 Feb 2014 19:11:07 +0100 Subject: erts: Maps must fail on exact updates of empty Maps Exact updates on empty Maps must fail directly. --- erts/emulator/beam/beam_emu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 4d8ea354ce..1ae413d46e 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6614,7 +6614,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) */ if (num_old == 0) { - return new_map(p, reg, I+1); + return THE_NON_VALUE; } /* -- cgit v1.2.3 From d2c8aaef5917101c29f055cd235e604a5d9d728b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 4 Feb 2014 17:57:55 +0100 Subject: erts,compiler: Correct and amend tests for Maps Faulty test for maps update --- erts/emulator/test/map_SUITE.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 31c1486f1c..8cc5621181 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -254,7 +254,15 @@ t_update_exact(Config) when is_list(Config) -> M2 = M0#{3.0:=new}, #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, M2 = M0#{3.0=>wrong,3.0:=new}, - M2 = M0#{3=>wrong,3.0:=new}, + true = M2 =/= M0#{3=>right,3.0:=new}, + #{ 3 := right, 3.0 := new } = M0#{3=>right,3.0:=new}, + + M3 = id(#{ 1 => val}), + #{1 := update2,1.0 := new_val4} = M3#{ + 1.0 => new_val1, 1 := update, 1=> update3, + 1 := update2, 1.0 := new_val2, 1.0 => new_val3, + 1.0 => new_val4 }, + %% Errors cases. {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), -- cgit v1.2.3 From 57573ab98e88ede4b6bca07574e537b6e4f82027 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Tue, 28 Jan 2014 22:26:14 -0500 Subject: allow optional whitespace in dirty scheduler erl options The +SDcpu, +SDPcpu, and +SDio options did not properly handle having their arguments immediately following them without intervening whitespace, e.g. +SDio20 was treated as an error. Fix all the dirty scheduler command line options so they handle optional whitespace between them and their associated arguments. --- erts/emulator/beam/erl_init.c | 6 +++--- erts/etc/common/erlexec.c | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index c17256f466..e6c05adec1 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -835,7 +835,7 @@ early_init(int *argc, char **argv) /* else if (argv[i][2] == 'D') { char *arg; char *type = argv[i]+3; - if (strcmp(type, "Pcpu") == 0) { + if (strncmp(type, "Pcpu", 4) == 0) { int ptot, ponln; arg = get_arg(argv[i]+7, argv[i+1], &i); switch (sscanf(arg, "%d:%d", &ptot, &ponln)) { @@ -872,7 +872,7 @@ early_init(int *argc, char **argv) /* VERBOSE(DEBUG_SYSTEM, ("using %d:%d dirty CPU scheduler percentages\n", dirty_cpu_scheds_pctg, dirty_cpu_scheds_onln_pctg)); - } else if (strcmp(type, "cpu") == 0) { + } else if (strncmp(type, "cpu", 3) == 0) { int tot, onln; arg = get_arg(argv[i]+6, argv[i+1], &i); switch (sscanf(arg, "%d:%d", &tot, &onln)) { @@ -923,7 +923,7 @@ early_init(int *argc, char **argv) /* } VERBOSE(DEBUG_SYSTEM, ("using %d:%d dirty CPU scheduler(s)\n", tot, onln)); - } else if (strcmp(type, "io") == 0) { + } else if (strncmp(type, "io", 2) == 0) { arg = get_arg(argv[i]+5, argv[i+1], &i); dirty_io_scheds = atoi(arg); if (dirty_io_scheds < 0 || diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 2cf7280ebc..709c6f02d1 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -833,9 +833,13 @@ int main(int argc, char **argv) #ifdef ERTS_DIRTY_SCHEDULERS else if (argv[i][2] == 'D') { char* type = argv[i]+3; - if (strcmp(type, "cpu") != 0 && - strcmp(type, "Pcpu") != 0 && - strcmp(type, "io") != 0) + if (strncmp(type, "cpu", 3) != 0 && + strncmp(type, "Pcpu", 4) != 0 && + strncmp(type, "io", 2) != 0) + usage(argv[i]); + if ((argv[i][3] == 'c' && argv[i][6] != '\0') || + (argv[i][3] == 'P' && argv[i][7] != '\0') || + (argv[i][3] == 'i' && argv[i][5] != '\0')) goto the_default; } #endif -- cgit v1.2.3 From b7c95eabf6017ddb352fb8ce2b3749af108ebf29 Mon Sep 17 00:00:00 2001 From: "Matwey V. Kornilov" Date: Tue, 17 Dec 2013 18:16:56 +0400 Subject: Add systemd socket activation for epmd. Check for systemd libraries and headers in confgiure. Add -systemd option enabling waiting for sockets from systemd. --- erts/configure.in | 4 ++++ erts/epmd/src/epmd.c | 16 ++++++++++++++-- erts/epmd/src/epmd_int.h | 7 +++++++ erts/epmd/src/epmd_srv.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index bad748d8fe..3ed1238319 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -967,6 +967,8 @@ AC_CHECK_LIB(dl, dlopen) AC_CHECK_LIB(inet, main) AC_CHECK_LIB(util, openpty) +AC_CHECK_LIB(systemd-daemon, sd_listen_fds) + dnl Try to find a thread library. dnl dnl ETHR_LIB_NAME, ETHR_LIBS, ETHR_X_LIBS, ETHR_THR_LIB_BASE and ETHR_DEFS @@ -1563,6 +1565,8 @@ AC_CHECK_MEMBERS([struct ifreq.ifr_enaddr], [], [], #endif ]) +AC_CHECK_HEADERS(systemd/sd-daemon.h) + dnl ---------------------------------------------------------------------- dnl Check the availability for libdlpi dnl ---------------------------------------------------------------------- diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 2d55b37ff3..fc58882907 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -175,6 +175,9 @@ int main(int argc, char** argv) g->nodes.reg = g->nodes.unreg = g->nodes.unreg_tail = NULL; g->nodes.unreg_count = 0; g->active_conn = 0; +#ifdef HAVE_SYSTEMD_SD_DAEMON_H + g->is_systemd = 0; +#endif for (i = 0; i < MAX_LISTEN_SOCKETS; i++) g->listenfd[i] = -1; @@ -248,8 +251,12 @@ int main(int argc, char** argv) else usage(g); epmd_cleanup_exit(g,0); - } - else +#ifdef HAVE_SYSTEMD_SD_DAEMON_H + } else if (strcmp(argv[0], "-systemd") == 0) { + g->is_systemd = 1; + argv++; argc--; +#endif + } else usage(g); } dbg_printf(g,1,"epmd running - daemon = %d",g->is_daemon); @@ -454,6 +461,11 @@ static void usage(EpmdVars *g) fprintf(stderr, " Forcibly unregisters a name with epmd\n"); fprintf(stderr, " (only allowed if -relaxed_command_check was given when \n"); fprintf(stderr, " epmd was started).\n"); +#ifdef HAVE_SYSTEMD_SD_DAEMON_H + fprintf(stderr, " -systemd\n"); + fprintf(stderr, " Wait for socket from systemd. The option makes sense\n"); + fprintf(stderr, " when started from .socket unit.\n"); +#endif epmd_cleanup_exit(g,1); } diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index 656dbd1f45..363923eaa9 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -110,6 +110,10 @@ #include +#ifdef HAVE_SYSTEMD_SD_DAEMON_H +# include +#endif + /* ************************************************************************ */ /* Replace some functions by others by making the function name a macro */ @@ -321,6 +325,9 @@ typedef struct { int listenfd[MAX_LISTEN_SOCKETS]; char *addresses; char **argv; +#ifdef HAVE_SYSTEMD_SD_DAEMON_H + int is_systemd; +#endif } EpmdVars; void dbg_printf(EpmdVars*,int,const char*,...); diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 90df7cc25a..cb8ca960de 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -208,6 +208,39 @@ void run(EpmdVars *g) node_init(g); g->conn = conn_init(g); +#ifdef HAVE_SYSTEMD_SD_DAEMON_H + if (g->is_systemd) + { + int n; + + dbg_printf(g,2,"try to obtain sockets from systemd"); + + n = sd_listen_fds(0); + if (n < 0) + { + dbg_perror(g,"cannot obtain sockets from systemd"); + epmd_cleanup_exit(g,1); + } + else if (n == 0) + { + dbg_tty_printf(g,0,"systemd provides no sockets"); + epmd_cleanup_exit(g,1); + } + else if (n > MAX_LISTEN_SOCKETS) + { + dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses", MAX_LISTEN_SOCKETS); + epmd_cleanup_exit(g,1); + } + num_sockets = n; + for (i = 0; i < num_sockets; i++) + { + g->listenfd[i] = listensock[i] = SD_LISTEN_FDS_START + i; + } + } + else + { +#endif + dbg_printf(g,2,"try to initiate listening port %d", g->port); if (g->addresses != NULL && /* String contains non-separator characters if: */ @@ -272,6 +305,9 @@ void run(EpmdVars *g) SET_ADDR(iserv_addr[0],EPMD_ADDR_ANY,sport); num_sockets = 1; } +#ifdef HAVE_SYSTEMD_SD_DAEMON_H + } +#endif #if !defined(__WIN32__) /* We ignore the SIGPIPE signal that is raised when we call write @@ -289,6 +325,13 @@ void run(EpmdVars *g) FD_ZERO(&g->orig_read_mask); g->select_fd_top = 0; +#ifdef HAVE_SYSTEMD_SD_DAEMON_H + if (g->is_systemd) + for (i = 0; i < num_sockets; i++) + select_fd_set(g, listensock[i]); + else + { +#endif for (i = 0; i < num_sockets; i++) { if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0) @@ -351,6 +394,9 @@ void run(EpmdVars *g) } select_fd_set(g, listensock[i]); } +#ifdef HAVE_SYSTEMD_SD_DAEMON_H + } +#endif dbg_tty_printf(g,2,"entering the main select() loop"); -- cgit v1.2.3 From 04f4d9656724f465352df9582773393a165c8803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 19 Feb 2014 14:16:06 +0100 Subject: beam/utils.c: Change back cmp() to only take two arguments Commit d5c238473b9cec819d93faaef4ccc00ddb60465f added a third argument to the cmp() function, but did not change the code generation of native code that called cmp(). Name the new functions with an "erts_" prefix, and reinstate the old cmp() function with two arguments. We don't have to keep a cmp() function in the halfword emulator, since HiPE does not support the halfword emulator. --- erts/emulator/beam/erl_utils.h | 21 +++++++++++---------- erts/emulator/beam/utils.c | 16 +++++++++++++--- 2 files changed, 24 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 5b81d814c6..43a19e97f1 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -202,17 +202,18 @@ int eq(Eterm, Eterm); #define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y)))) #if HALFWORD_HEAP -Sint cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int); -#define cmp_rel(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,0) -#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1) -#define CMP(A,B) cmp_rel_opt(A,NULL,B,NULL,0) -#define CMP_TERM(A,B) cmp_rel_opt(A,NULL,B,NULL,1) +Sint erts_cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int); +#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,0) +#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,1) +#define CMP(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,0) +#define CMP_TERM(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,1) #else -Sint cmp(Eterm, Eterm, int); -#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B,0) -#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp(A,B,1) -#define CMP(A,B) cmp(A,B,0) -#define CMP_TERM(A,B) cmp(A,B,1) +Sint cmp(Eterm, Eterm); +Sint erts_cmp(Eterm, Eterm, int); +#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp(A,B,0) +#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp(A,B,1) +#define CMP(A,B) erts_cmp(A,B,0) +#define CMP_TERM(A,B) erts_cmp(A,B,1) #endif #define cmp_lt(a,b) (CMP((a),(b)) < 0) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index bc4a05d385..7da555b18d 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2425,14 +2425,24 @@ static int cmp_atoms(Eterm a, Eterm b) bb->name+3, bb->len-3); } -/* cmp(Eterm a, Eterm b, int exact) +#if !HALFWORD_HEAP +/* cmp(Eterm a, Eterm b) + * For compatibility with HiPE - arith-based compare. + */ +Sint cmp(Eterm a, Eterm b) +{ + return erts_cmp(a, b, 0); +} +#endif + +/* erts_cmp(Eterm a, Eterm b, int exact) * exact = 1 -> term-based compare * exact = 0 -> arith-based compare */ #if HALFWORD_HEAP -Sint cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, int exact) +Sint erts_cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, int exact) #else -Sint cmp(Eterm a, Eterm b, int exact) +Sint erts_cmp(Eterm a, Eterm b, int exact) #endif { DECLARE_WSTACK(stack); -- cgit v1.2.3 From 3fd4e3e770fbed95fd6fa2d547945ac71256339c Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Sun, 16 Feb 2014 00:54:08 +0100 Subject: Misc adjustments of OTP version --- erts/configure.in | 10 ++++++++++ erts/doc/src/Makefile | 2 +- erts/doc/src/erlang.xml | 19 +++++++++++-------- erts/emulator/Makefile.in | 4 ++-- erts/emulator/beam/erl_bif_info.c | 14 +++++--------- erts/emulator/utils/make_version | 8 ++++---- erts/etc/win32/nsis/erlang.nsi | 1 + erts/etc/win32/nsis/erlang20.nsi | 1 + erts/start_scripts/Makefile | 2 +- erts/vsn.mk | 5 ----- 10 files changed, 36 insertions(+), 30 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 8d245252b5..0784be001c 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -357,6 +357,16 @@ else fi AC_SUBST(OTP_RELEASE) +AC_MSG_CHECKING([OTP release]) +[SYSTEM_VSN=`cat $ERL_TOP/OTP_VERSION | sed "s|\([0-9]*\).*|\1|"`] +AC_MSG_RESULT([$SYSTEM_VSN]) +AC_SUBST(SYSTEM_VSN) + +AC_MSG_CHECKING([OTP version]) +[OTP_VERSION=`cat $ERL_TOP/OTP_VERSION`] +AC_MSG_RESULT([$OTP_VERSION]) +AC_SUBST(OTP_VERSION) + dnl OK, we might have darwin switches off different kinds, lets dnl check it all before continuing. TMPSYS=`uname -s`-`uname -m` diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile index d4c6fe67d2..e8b856c3ff 100644 --- a/erts/doc/src/Makefile +++ b/erts/doc/src/Makefile @@ -139,7 +139,7 @@ man: $(MAN1_FILES) $(MAN3_FILES) gifs: $(GIF_FILES:%=$(HTMLDIR)/%) -$(INFO_FILE): $(INFO_FILE_SRC) ../../vsn.mk +$(INFO_FILE): $(INFO_FILE_SRC) $(ERL_TOP)/make/$(TARGET)/otp.mk sed -e 's;%RELEASE%;$(SYSTEM_VSN);' $(INFO_FILE_SRC) > $(INFO_FILE) diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 4cf5631727..0f312fb430 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6094,16 +6094,19 @@ ok erlang:system_info(multi_scheduling), and erlang:system_info(schedulers).

- otp_correction_package - -

Returns a string containing the OTP correction package version - number that currenly executing VM is part of. Note that other - OTP applications in the system may be part of other OTP correction - packages.

-
otp_release -

Returns a string containing the OTP release number.

+

Returns a string containing the OTP release number of the + OTP release that the currently executing ERTS application is + part of.

+

As of OTP release 17, the OTP release number corresponds to + the major OTP version number. There is no + erlang:system_info() argument giving the exact OTP + version. This since the exact OTP version in the general case + is hard to determine. For more information see + the + documentation of the OTP version in the installation + guide.

port_parallelism

Returns the default port parallelism scheduling hint used. diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index f88a4ccc24..da4c33e457 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -574,8 +574,8 @@ $(TTF_DIR)/erl_alloc_types.h: beam/erl_alloc.types utils/make_alloc_types GENERATE += $(TTF_DIR)/erl_alloc_types.h # version include file -$(TARGET)/erl_version.h: ../vsn.mk - $(gen_verbose)LANG=C $(PERL) utils/make_version -o $@ $(SYSTEM_VSN) $(SYSTEM_CP_VSN) $(VSN)$(SERIALNO) $(TARGET) +$(TARGET)/erl_version.h: ../vsn.mk $(ERL_TOP)/make/$(TARGET)/otp.mk + $(gen_verbose)LANG=C $(PERL) utils/make_version -o $@ $(SYSTEM_VSN) $(OTP_VERSION) $(VSN)$(SERIALNO) $(TARGET) GENERATE += $(TARGET)/erl_version.h # driver table diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index f25b4dbae5..2adba9b240 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -64,7 +64,7 @@ static Export *gather_gc_info_res_trap; #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) -static char otp_correction_package[] = ERLANG_OTP_CORRECTION_PACKAGE; +static char otp_version[] = ERLANG_OTP_VERSION; /* Keep erts_system_version as a global variable for easy access from a core */ static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE "%s" @@ -312,7 +312,7 @@ erts_print_system_version(int to, void *arg, Process *c_p) int i, rc = -1; char *rc_str = ""; char rc_buf[100]; - char *ocp = otp_correction_package; + char *ov = otp_version; #ifdef ERTS_SMP Uint total, online, active; #ifdef ERTS_DIRTY_SCHEDULERS @@ -323,9 +323,9 @@ erts_print_system_version(int to, void *arg, Process *c_p) (void) erts_schedulers_state(&total, &online, &active, NULL, NULL, NULL, 0); #endif #endif - for (i = 0; i < sizeof(otp_correction_package)-4; i++) { - if (ocp[i] == '-' && ocp[i+1] == 'r' && ocp[i+2] == 'c') - rc = atoi(&ocp[i+3]); + for (i = 0; i < sizeof(otp_version)-4; i++) { + if (ov[i] == '-' && ov[i+1] == 'r' && ov[i+2] == 'c') + rc = atoi(&ov[i+3]); } if (rc >= 0) { if (rc == 0) @@ -2448,10 +2448,6 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) DECL_AM(unknown); BIF_RET(AM_unknown); } - } else if (ERTS_IS_ATOM_STR("otp_correction_package", BIF_ARG_1)) { - int n = sizeof(ERLANG_OTP_CORRECTION_PACKAGE)-1; - hp = HAlloc(BIF_P, 2*n); - BIF_RET(buf_to_intlist(&hp, ERLANG_OTP_CORRECTION_PACKAGE, n, NIL)); } else if (ERTS_IS_ATOM_STR("otp_release", BIF_ARG_1)) { int n = sizeof(ERLANG_OTP_RELEASE)-1; hp = HAlloc(BIF_P, 2*n); diff --git a/erts/emulator/utils/make_version b/erts/emulator/utils/make_version index 02b68f2b39..0ba1c77930 100755 --- a/erts/emulator/utils/make_version +++ b/erts/emulator/utils/make_version @@ -39,10 +39,10 @@ if ($ARGV[0] eq '-o') { } my $release = shift; -defined $release or die "No release specified"; +defined $release or die "No otp release specified"; -my $correction_package = shift; -defined $correction_package or die "No correction package specified"; +my $otp_version = shift; +defined $otp_version or die "No otp version specified"; my $version = shift; defined $version or die "No version name specified"; @@ -56,7 +56,7 @@ open(FILE, ">$outputfile") or die "Can't create $outputfile: $!"; print FILE < Date: Wed, 12 Feb 2014 17:04:26 +0100 Subject: erts: Introduce new instructions for combined key fetches --- erts/emulator/beam/beam_emu.c | 120 +++++++++++++++++++++++++++++++++++++++++ erts/emulator/beam/beam_load.c | 46 ++++++++++++++++ erts/emulator/beam/ops.tab | 23 ++++++-- 3 files changed, 184 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 1ae413d46e..6f295530b9 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2355,6 +2355,126 @@ void process_main(void) Next(4+Arg(3)); } + OpCase(i_has_map_fields_fsI): { + map_t* mp; + Eterm map; + Eterm field; + Eterm *ks; + BeamInstr* fs; + Uint sz,n; + + GetArg1(1, map); + + /* this instruction assumes Arg1 is a map, + * i.e. that it follows a test is_map if needed. + */ + + mp = (map_t *)map_val(map); + sz = map_get_size(mp); + + if (sz == 0) { + SET_I((BeamInstr *) Arg(0)); + goto has_map_fields_fail; + } + + ks = map_get_keys(mp); + n = (Uint)Arg(2); + fs = &Arg(3); /* pattern fields */ + + ASSERT(n>0); + + while(sz) { + field = (Eterm)*fs; + if (EQ(field,*ks)) { + n--; + fs++; + if (n == 0) break; + } + ks++; sz--; + } + + if (n) { + SET_I((BeamInstr *) Arg(0)); + goto has_map_fields_fail; + } + + I += 4 + Arg(2); +has_map_fields_fail: + ASSERT(VALID_INSTR(*I)); + Goto(*I); + } + +#define PUT_TERM_REG(term, desc) \ +do { \ + switch ((desc) & _TAG_IMMED1_MASK) { \ + case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + r(0) = (term); \ + break; \ + case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + x((desc) >> _TAG_IMMED1_SIZE) = (term); \ + break; \ + case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + y((desc) >> _TAG_IMMED1_SIZE) = (term); \ + break; \ + default: \ + ASSERT(0); \ + break; \ + } \ +} while(0) + + OpCase(i_get_map_elements_fsI): { + Eterm map; + map_t *mp; + Eterm field; + Eterm *ks; + Eterm *vs; + BeamInstr *fs; + Uint sz,n; + + GetArg1(1, map); + + /* this instruction assumes Arg1 is a map, + * i.e. that it follows a test is_map if needed. + */ + + mp = (map_t *)map_val(map); + sz = map_get_size(mp); + + if (sz == 0) { + SET_I((BeamInstr *) Arg(0)); + goto get_map_elements_fail; + } + + n = (Uint)Arg(2) / 2; + fs = &Arg(3); /* pattern fields and target registers */ + ks = map_get_keys(mp); + vs = map_get_values(mp); + + while(sz) { + field = (Eterm)*fs; + if (EQ(field,*ks)) { + PUT_TERM_REG(*vs, fs[1]); + n--; + fs += 2; + /* no more values to fetch, we are done */ + if (n == 0) break; + } + ks++; sz--; + vs++; + } + + if (n) { + SET_I((BeamInstr *) Arg(0)); + goto get_map_elements_fail; + } + + I += 4 + Arg(2); +get_map_elements_fail: + ASSERT(VALID_INSTR(*I)); + Goto(*I); + } +#undef PUT_TERM_REG + OpCase(update_map_assoc_jsdII): { Eterm res; Eterm map; diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index b589d1c930..5430ebb29d 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -506,6 +506,9 @@ static GenOp* gen_select_literals(LoaderState* stp, GenOpArg S, static GenOp* const_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail, GenOpArg Size, GenOpArg* Rest); +static GenOp* gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src, + GenOpArg Size, GenOpArg* Rest); + static int freeze_code(LoaderState* stp); static void final_touch(LoaderState* stp); @@ -3951,6 +3954,49 @@ tuple_append_put(LoaderState* stp, GenOpArg Arity, GenOpArg Dst, return op; } +/* + * Replace a get_map_elements with one key to an instruction with one + * element + */ + +static GenOp* +gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src, + GenOpArg Size, GenOpArg* Rest) +{ + GenOp* op; + + ASSERT(Size.type == TAG_u); + + NEW_GENOP(stp, op); + op->next = NULL; + op->op = genop_get_map_element_4; + op->arity = 4; + + op->a[0] = Fail; + op->a[1] = Src; + op->a[2] = Rest[0]; + op->a[3] = Rest[1]; + return op; +} + +static GenOp* +gen_has_map_field(LoaderState* stp, GenOpArg Fail, GenOpArg Src, + GenOpArg Size, GenOpArg* Rest) +{ + GenOp* op; + + ASSERT(Size.type == TAG_u); + + NEW_GENOP(stp, op); + op->next = NULL; + op->op = genop_has_map_field_3; + op->arity = 4; + + op->a[0] = Fail; + op->a[1] = Src; + op->a[2] = Rest[0]; + return op; +} /* * Freeze the code in memory, move the string table into place, diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 9ea3a3a8ea..8406f5344a 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1469,11 +1469,6 @@ apply_last I P # Map instructions in R17. # -# put_map Fail Src Dst Live Size Rest=* => jump Fail -# is_map Fail Src => jump Fail -# has_map_field Fail Src Key => jump Fail -# get_map_element Fail Src Key Dst => jump Fail - put_map_assoc F n Dst Live Size Rest=* => new_map F Dst Live Size Rest put_map_exact F n Dst Live Size Rest=* => new_map F Dst Live Size Rest put_map_assoc F Src Dst Live Size Rest=* => \ @@ -1492,6 +1487,15 @@ is_map f r is_map f x is_map f y +## Transform has_map_field(s) #{ K1 := _, K2 := _ } + +has_map_field/3 + +has_map_fields Fail Src Size=u==1 Rest=* => gen_has_map_field(Fail,Src,Size,Rest) +has_map_fields Fail Src Size Rest=* => i_has_map_fields Fail Src Size Rest + +i_has_map_fields f s I + has_map_field Fail Src=rxy Key=arxy => i_has_map_field Fail Src Key has_map_field Fail Src Key => move Key x | i_has_map_field Fail Src x @@ -1509,6 +1513,15 @@ i_has_map_field f r y i_has_map_field f x y i_has_map_field f y y +## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 } + +get_map_element/4 + +get_map_elements Fail Src=rxy Size=u==2 Rest=* => gen_get_map_element(Fail,Src,Size,Rest) +get_map_elements Fail Src Size Rest=* => i_get_map_elements Fail Src Size Rest + +i_get_map_elements f s I + get_map_element Fail Src=rxy Key=ax Dst => i_get_map_element Fail Src Key Dst get_map_element Fail Src=rxy Key=rycq Dst => \ move Key x | i_get_map_element Fail Src x Dst -- cgit v1.2.3 From 5006a54b032a82ad55f33e532e4068c34fe143a9 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 6 Feb 2014 17:15:05 +0100 Subject: erts: Fix race bug in ets:all/0 causing recently created/deleted tables to not be included/excluded. --- erts/emulator/beam/erl_db.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 41e64fcd4f..a5d67571e2 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -125,6 +125,7 @@ get_meta_main_tab_lock(unsigned slot) static erts_smp_spinlock_t meta_main_tab_main_lock; static Uint meta_main_tab_first_free; /* Index of first free slot */ static int meta_main_tab_cnt; /* Number of active tables */ +static int meta_main_tab_top; /* Highest ever used slot + 1 */ static Uint meta_main_tab_slot_mask; /* The slot index part of an unnamed table id */ static Uint meta_main_tab_seq_incr; static Uint meta_main_tab_seq_cnt = 0; /* To give unique(-ish) table identifiers */ @@ -1469,6 +1470,10 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2) ASSERT(slot>=0 && slot= meta_main_tab_top) { + ASSERT(slot == meta_main_tab_top); + meta_main_tab_top = slot + 1; + } if (is_named) { ret = BIF_ARG_1; @@ -2058,27 +2063,31 @@ BIF_RETTYPE ets_all_0(BIF_ALIST_0) { DbTable* tb; Eterm previous; - int i, j; + int i; Eterm* hp; Eterm* hendp; int t_tabs_cnt; - int t_max_tabs; + int t_top; erts_smp_spin_lock(&meta_main_tab_main_lock); t_tabs_cnt = meta_main_tab_cnt; - t_max_tabs = db_max_tabs; + t_top = meta_main_tab_top; erts_smp_spin_unlock(&meta_main_tab_main_lock); hp = HAlloc(BIF_P, 2*t_tabs_cnt); hendp = hp + 2*t_tabs_cnt; previous = NIL; - j = 0; - for(i = 0; (i < t_max_tabs && j < t_tabs_cnt); i++) { + for(i = 0; i < t_top; i++) { erts_smp_rwmtx_t *mmtl = get_meta_main_tab_lock(i); erts_smp_rwmtx_rlock(mmtl); if (IS_SLOT_ALIVE(i)) { - j++; + if (hp == hendp) { + /* Racing table creator, grab some more heap space */ + t_tabs_cnt = 10; + hp = HAlloc(BIF_P, 2*t_tabs_cnt); + hendp = hp + 2*t_tabs_cnt; + } tb = meta_main_tab[i].u.tb; previous = CONS(hp, tb->common.id, previous); hp += 2; @@ -2849,6 +2858,7 @@ void init_db(void) ERTS_ETS_MISC_MEM_ADD(size); meta_main_tab_cnt = 0; + meta_main_tab_top = 0; for (i=1; i Date: Mon, 10 Feb 2014 17:00:34 +0100 Subject: erts: Fix harmless (?) typo in beam_load.c --- erts/emulator/beam/beam_load.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index b589d1c930..915af7fbb1 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -409,7 +409,7 @@ typedef struct LoaderState { __result = __result << 8 | *Stp->file_p++; \ } \ Dest = __result; \ - } while (0) + } #define GetByte(Stp, Dest) \ if ((Stp)->file_left < 1) { \ -- cgit v1.2.3 From f8693cf61f0555fb0108956f2d21d2234e01251d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 10 Feb 2014 17:07:01 +0100 Subject: erts: Fix faulty asserts in erts_sys_aligned_alloc/free --- erts/emulator/sys/unix/sys.c | 6 +++--- erts/emulator/sys/win32/sys.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index a5294ad84e..865cb50a56 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -2561,7 +2561,7 @@ void *erts_sys_aligned_alloc(UWord alignment, UWord size) #ifdef HAVE_POSIX_MEMALIGN void *ptr = NULL; int error; - ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */ error = posix_memalign(&ptr, (size_t) alignment, (size_t) size); #if HAVE_ERTS_MSEG if (error || !ptr) { @@ -2584,7 +2584,7 @@ void *erts_sys_aligned_alloc(UWord alignment, UWord size) void erts_sys_aligned_free(UWord alignment, void *ptr) { - ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */ free(ptr); } diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 5ea4703a7a..a11ac73ebc 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -2757,7 +2757,7 @@ void erts_sys_free(ErtsAlcType_t t, void *x, void *p) void *erts_sys_aligned_alloc(UWord alignment, UWord size) { void *ptr; - ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */ ptr = _aligned_malloc((size_t) size, (size_t) alignment); ASSERT(!ptr || (((UWord) ptr) & (alignment - 1)) == 0); return ptr; @@ -2765,14 +2765,14 @@ void *erts_sys_aligned_alloc(UWord alignment, UWord size) void erts_sys_aligned_free(UWord alignment, void *ptr) { - ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */ _aligned_free(ptr); } void *erts_sys_aligned_realloc(UWord alignment, void *ptr, UWord size, UWord old_size) { void *new_ptr; - ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */ new_ptr = _aligned_realloc(ptr, (size_t) size, (size_t) alignment); ASSERT(!new_ptr || (((UWord) new_ptr) & (alignment - 1)) == 0); return new_ptr; -- cgit v1.2.3 From 8899c393163f9c95a81971e013b57788b33db09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 19 Feb 2014 18:30:35 +0100 Subject: erts: Fix erts_debug:disassemble/1 Now handles map instructions correctly. --- erts/emulator/beam/beam_debug.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index e36ec2a93e..a3cd08834f 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -635,6 +635,11 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) case op_i_put_tuple_rI: case op_i_put_tuple_xI: case op_i_put_tuple_yI: + case op_new_map_jdII: + case op_update_map_assoc_jsdII: + case op_update_map_exact_jsdII: + case op_i_has_map_fields_fsI: + case op_i_get_map_elements_fsI: { int n = unpacked[-1]; -- cgit v1.2.3 From a1c31504a7a75f74ec90f3aab8aa0e6fbfc20c33 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 20 Feb 2014 11:59:58 +0100 Subject: erts: Fix memory leak in nif_SUITE:resource_takeover --- erts/emulator/test/nif_SUITE_data/nif_mod.c | 3 ++- erts/emulator/test/nif_SUITE_data/nif_mod.h | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c index aed8524635..55a0d2ac4f 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.c +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c @@ -191,7 +191,8 @@ static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) add_call(env, data, "load"); do_load_info(env, load_info, &retval); - data->calls = 0; + if (retval) + NifModPrivData_release(data); return retval; } diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.h b/erts/emulator/test/nif_SUITE_data/nif_mod.h index cd0ecf4b54..fb14fee815 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.h +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.h @@ -14,7 +14,6 @@ typedef struct call_info_t typedef struct { ErlNifMutex* mtx; - int calls; int ref_cnt; CallInfo* call_history; ErlNifResourceType* rt_arr[RT_MAX]; @@ -28,6 +27,11 @@ typedef struct enif_mutex_unlock((NMPD)->mtx); \ if (is_last) { \ enif_mutex_destroy((NMPD)->mtx); \ + while ((NMPD)->call_history) { \ + CallInfo* next = (NMPD)->call_history->next; \ + enif_free((NMPD)->call_history); \ + (NMPD)->call_history = next; \ + } \ enif_free((NMPD)); \ } \ }while (0) -- cgit v1.2.3 From 880239f529bbdefecc39cc179a24d9ea89c3736a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 20 Feb 2014 11:14:42 +0100 Subject: ASSERT that GC is not tried with "need" when GC is disabled --- erts/emulator/beam/erl_gc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 2022f70cbb..aa15d2cc57 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -406,8 +406,10 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); #endif - if (p->flags & F_DISABLE_GC) + if (p->flags & F_DISABLE_GC) { + ASSERT(need == 0); return 1; + } esdp = erts_get_scheduler_data(); -- cgit v1.2.3 From 0dd1ae604a093a475c3af716958092427820a64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 21 Feb 2014 17:14:43 +0100 Subject: erts: Maps src instructions can't be literals Move src to a register if it is a literal. --- erts/emulator/beam/ops.tab | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 8406f5344a..73630fda8e 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1470,11 +1470,15 @@ apply_last I P # put_map_assoc F n Dst Live Size Rest=* => new_map F Dst Live Size Rest -put_map_exact F n Dst Live Size Rest=* => new_map F Dst Live Size Rest -put_map_assoc F Src Dst Live Size Rest=* => \ +put_map_assoc F Src=s Dst Live Size Rest=* => \ update_map_assoc F Src Dst Live Size Rest -put_map_exact F Src Dst Live Size Rest=* => \ +put_map_assoc F Src Dst Live Size Rest=* => \ + move Src x | update_map_assoc F x Dst Live Size Rest +put_map_exact F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map_exact F Src=s Dst Live Size Rest=* => \ update_map_exact F Src Dst Live Size Rest +put_map_exact F Src Dst Live Size Rest=* => \ + move Src x | update_map_exact F x Dst Live Size Rest new_map j d I I update_map_assoc j s d I I -- cgit v1.2.3 From b2017b91a5572f7bc2caf15082b4b105f3a3e21d Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Sun, 23 Feb 2014 20:04:30 +0100 Subject: Introduce configure option --with-assumed-cache-line-size=SIZE --- erts/configure.in | 19 +++++++++++++++++++ erts/emulator/beam/erl_alloc.h | 4 ++-- erts/include/internal/ethread.h | 2 +- erts/include/internal/ethread_header_config.h.in | 2 ++ 4 files changed, 24 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 02e5d12918..d71a264066 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -347,6 +347,25 @@ AS_HELP_STRING([--enable-clock-gettime], *) clock_gettime_correction=yes ;; esac ], clock_gettime_correction=unknown) +AC_ARG_WITH(assumed-cache-line-size, +AS_HELP_STRING([--with-assumed-cache-line-size=SIZE], + [specify assumed cache line size in bytes (valid values are powers of two between and including 16 and 8192; default is 64)])) + +dnl Require the assumed cache-line size to be a power of two between 16 and 8192 +case "$with_assumed_cache_line_size" in + ""|no|yes) + with_assumed_cache_line_size=64;; + 16|32|64|128|256|512|1024|2048|4096|8192) + ;; + *) + AC_MSG_ERROR([Invalid assumed cache-line size of $with_assumed_cache_line_size bytes]) + ;; +esac + +AC_DEFINE_UNQUOTED(ASSUMED_CACHE_LINE_SIZE, + $with_assumed_cache_line_size, + [Assumed cache-line size (in bytes)]) + dnl Magic test for clearcase. OTP_RELEASE= if test "${ERLANG_COMMERCIAL_BUILD}" != ""; then diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index f83f6b39cf..942eaa47d0 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -209,8 +209,8 @@ int erts_is_allctr_wrapper_prelocked(void); void *erts_alloc_permanent_cache_aligned(ErtsAlcType_t type, Uint size); #ifndef ERTS_CACHE_LINE_SIZE -/* Assume a cache line size of 64 bytes */ -# define ERTS_CACHE_LINE_SIZE ((UWord) 64) +/* Assumed cache line size */ +# define ERTS_CACHE_LINE_SIZE ((UWord) ASSUMED_CACHE_LINE_SIZE) # define ERTS_CACHE_LINE_MASK (ERTS_CACHE_LINE_SIZE - 1) #endif diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 38b8e9e9b6..3a676e2a36 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -60,7 +60,7 @@ #endif /* Assume 64-byte cache line size */ -#define ETHR_CACHE_LINE_SIZE 64 +#define ETHR_CACHE_LINE_SIZE ASSUMED_CACHE_LINE_SIZE #define ETHR_CACHE_LINE_MASK (ETHR_CACHE_LINE_SIZE - 1) #define ETHR_CACHE_LINE_ALIGN_SIZE(SZ) \ diff --git a/erts/include/internal/ethread_header_config.h.in b/erts/include/internal/ethread_header_config.h.in index dd3599f86d..b36322490a 100644 --- a/erts/include/internal/ethread_header_config.h.in +++ b/erts/include/internal/ethread_header_config.h.in @@ -235,3 +235,5 @@ /* Define if you want to turn on extra sanity checking in the ethread library */ #undef ETHR_XCHK +/* Assumed cache-line size (in bytes) */ +#undef ASSUMED_CACHE_LINE_SIZE -- cgit v1.2.3 From a3af5f4a5c4568225ef91ee4493da6bf659f7161 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 2 Sep 2013 17:27:58 +0200 Subject: erts: Set default external enc to use new float scheme This change was triggered by the OSE float printing function not working exactly the same way as linux/win32. But it is also a good one in general as it cuts size in more than half for floats. --- erts/doc/src/erlang.xml | 7 ++++--- erts/emulator/beam/dist.h | 1 + erts/emulator/beam/external.c | 8 ++++---- erts/emulator/test/binary_SUITE.erl | 10 +++++----- 4 files changed, 14 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 0f312fb430..accd7f0b08 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6572,11 +6572,12 @@ ok some details of the encoding. This option was introduced in R11B-4. Currently, the allowed values for Version are 0 and 1.

-

{minor_version, 1} forces any floats in the term to be encoded +

{minor_version, 1} is since 17.0 the default, it forces any floats in + the term to be encoded in a more space-efficient and exact way (namely in the 64-bit IEEE format, rather than converted to a textual representation). binary_to_term/1 - in R11B-4 and later is able decode the new representation.

-

{minor_version, 0} is currently the default, meaning that floats + in R11B-4 and later is able decode this representation.

+

{minor_version, 0} 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.

diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index ff8f5e106f..0519a9225e 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -44,6 +44,7 @@ /* All flags that should be enabled when term_to_binary/1 is used. */ #define TERM_TO_BINARY_DFLAGS (DFLAG_EXTENDED_REFERENCES \ | DFLAG_NEW_FUN_TAGS \ + | DFLAG_NEW_FLOATS \ | DFLAG_EXTENDED_PIDS_PORTS \ | DFLAG_EXPORT_PTR_TAG \ | DFLAG_BIT_BINARIES) diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 9fb2dbd8bf..b8e6b3b072 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -529,7 +529,7 @@ Uint erts_encode_ext_size(Eterm term) Uint erts_encode_ext_size_2(Eterm term, unsigned dflags) { - return encode_size_struct2(NULL, term, TERM_TO_BINARY_DFLAGS|dflags) + return encode_size_struct2(NULL, term, dflags) + 1 /* VERSION_MAGIC */; } @@ -1099,10 +1099,10 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2) if (tp[1] == am_minor_version && is_small(tp[2])) { switch (signed_val(tp[2])) { case 0: - flags = TERM_TO_BINARY_DFLAGS; + flags = TERM_TO_BINARY_DFLAGS & ~DFLAG_NEW_FLOATS; break; case 1: - flags = TERM_TO_BINARY_DFLAGS|DFLAG_NEW_FLOATS; + flags = TERM_TO_BINARY_DFLAGS; break; default: goto error; @@ -1605,9 +1605,9 @@ external_size_2(BIF_ALIST_2) if (tp[1] == am_minor_version && is_small(tp[2])) { switch (signed_val(tp[2])) { case 0: + flags &= ~DFLAG_NEW_FLOATS; break; case 1: - flags |= DFLAG_NEW_FLOATS; break; default: goto error; diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index a390c536bb..938aac6a0e 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -474,16 +474,16 @@ terms_compression_levels(_, _, _) -> ok. terms_float(Config) when is_list(Config) -> ?line test_floats(fun(Term) -> - Bin0 = term_to_binary(Term), Bin0 = term_to_binary(Term, [{minor_version,0}]), Term = binary_to_term_stress(Bin0), + Bin1 = term_to_binary(Term), Bin1 = term_to_binary(Term, [{minor_version,1}]), Term = binary_to_term_stress(Bin1), true = size(Bin1) < size(Bin0), - Size0 = erlang:external_size(Term), - Size00 = erlang:external_size(Term, [{minor_version, 0}]), - Size1 = erlang:external_size(Term, [{minor_version, 1}]), - true = (Size0 =:= Size00), + Size0 = erlang:external_size(Term, [{minor_version, 0}]), + Size1 = erlang:external_size(Term), + Size11 = erlang:external_size(Term, [{minor_version, 1}]), + true = (Size1 =:= Size11), true = Size1 < Size0 end). -- cgit v1.2.3 From c68f8f0006f54ccca37455b8a3e6f531c5ab9b46 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 22 Jan 2014 16:08:20 +0100 Subject: Add upgrade_SUITE to test upgrade of OTP release This test creates an OTP package of the previous major release and performs an upgrade to the current release. --- erts/test/Makefile | 5 +- erts/test/upgrade_SUITE.erl | 411 +++++++++++++++++++++++++++++++++ erts/test/upgrade_SUITE_data/start.src | 36 +++ 3 files changed, 450 insertions(+), 2 deletions(-) create mode 100644 erts/test/upgrade_SUITE.erl create mode 100644 erts/test/upgrade_SUITE_data/start.src (limited to 'erts') diff --git a/erts/test/Makefile b/erts/test/Makefile index 74a5bb1ccc..6fbc19fcae 100644 --- a/erts/test/Makefile +++ b/erts/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2012. All Rights Reserved. +# Copyright Ericsson AB 1997-2014. All Rights Reserved. # # The 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,8 @@ MODULES= \ erl_print_SUITE \ run_erl_SUITE \ erlexec_SUITE \ - z_SUITE + z_SUITE \ + upgrade_SUITE ERL_FILES= $(MODULES:%=%.erl) diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl new file mode 100644 index 0000000000..f6799467f6 --- /dev/null +++ b/erts/test/upgrade_SUITE.erl @@ -0,0 +1,411 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The 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(upgrade_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/file.hrl"). + +-define(upgr_sname,otp_upgrade). + +%% Applications that are excluded from this test because they can not +%% just be started in a new node with out specific configuration. +-define(start_exclude, + [cosEvent,cosEventDomain,cosFileTransfer,cosNotification, + cosProperty,cosTime,cosTransactions,erts,ic,netconf,orber, + safe]). + +%% Applications that are excluded from this test because they don't +%% have useful appup files. +-define(appup_exclude, + [asn1,common_test,compiler,crypto,dialyzer,edoc,eldap,eunit, + hipe,inets,observer,odbc,os_mon,public_key,runtime_tools, + ssh,ssl,syntax_tools,test_server,tools,typer,wx,xmerl]). + +init_per_suite(Config) -> + rm_rf(filename:join([?config(data_dir,Config),priv_dir])), + Config. + +init_per_testcase(Case,Config) -> + PrivDir = filename:join([?config(data_dir,Config),priv_dir,Case]), + CreateDir = filename:join([PrivDir,create]), + InstallDir = filename:join([PrivDir,install]), + ok = filelib:ensure_dir(filename:join(CreateDir,"*")), + ok = filelib:ensure_dir(filename:join(InstallDir,"*")), + Config1 = lists:keyreplace(priv_dir,1,Config,{priv_dir,PrivDir}), + [{create_dir,CreateDir},{install_dir,InstallDir}|Config1]. + +end_per_testcase(_Case,Config) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes], + case ?config(tc_status,Config) of + ok -> + %% Note that priv_dir here is per test case! + rm_rf(?config(priv_dir,Config)); + _fail -> + %% Test case data can be found under DataDir/priv_dir/Case + ok + end, + ok. + +all() -> + [minor,major]. + +%% If this is major release X, then this test performs an upgrade from +%% major release X-1 to the current release. +major(Config) -> + Current = erlang:system_info(otp_release), + PreviousMajor = previous_major(Current), + upgrade_test(PreviousMajor,Current,Config). + +%% If this is a patched version of major release X, then this test +%% performs an upgrade from major release X to the current release. +minor(Config) -> + CurrentMajor = erlang:system_info(otp_release), + Current = list_to_atom(CurrentMajor++"_patched"), + upgrade_test(CurrentMajor,Current,Config). + +%%%----------------------------------------------------------------- +upgrade_test(FromVsn,ToVsn,Config) -> + case test_server:is_release_available(FromVsn) of + true -> + upgrade_test1(FromVsn,ToVsn,Config); + false -> + %% Note that priv_dir here is per test case! + rm_rf(?config(priv_dir,Config)), + {skip, "no previous release available"} + end. + +upgrade_test1(FromVsn,ToVsn,Config) -> + CreateDir = ?config(create_dir,Config), + InstallDir = ?config(install_dir,Config), + FromRelName = "otp-"++FromVsn, + ToRelName = "otp-"++ToVsn, + + {FromRel,FromApps} = target_system(FromRelName, FromVsn, + CreateDir, InstallDir,Config), + {ToRel,ToApps} = upgrade_system(FromRel, ToRelName, ToVsn, + CreateDir, InstallDir), + do_upgrade(FromVsn, FromApps, ToRel, ToApps, InstallDir). + +%%%----------------------------------------------------------------- +%%% This is similar to sasl/examples/src/target_system.erl, but with +%%% the following adjustments: +%%% - add a log directory +%%% - use an own 'start' script +%%% - chmod 'start' and 'start_erl' +target_system(RelName0,RelVsn,CreateDir,InstallDir,Config) -> + {ok,Node} = test_server:start_node(list_to_atom(RelName0),peer, + [{erl,[{release,RelVsn}]}]), + + {RelName,Apps,ErtsVsn} = create_relfile(Node,CreateDir,RelName0,RelVsn), + + %% Create .script and .boot + ok = rpc:call(Node,systools,make_script,[RelName]), + + %% Create base tar file - i.e. erts and all apps + ok = rpc:call(Node,systools,make_tar, + [RelName,[{erts,rpc:call(Node,code,root_dir,[])}]]), + + %% Unpack the tar to complete the installation + erl_tar:extract(RelName ++ ".tar.gz", [{cwd, InstallDir}, compressed]), + + %% Add bin and log dirs + BinDir = filename:join([InstallDir, "bin"]), + file:make_dir(BinDir), + file:make_dir(filename:join(InstallDir,"log")), + + %% Delete start scripts - they will be added later + ErtsBinDir = filename:join([InstallDir, "erts-" ++ ErtsVsn, "bin"]), + file:delete(filename:join([ErtsBinDir, "erl"])), + file:delete(filename:join([ErtsBinDir, "start"])), + file:delete(filename:join([ErtsBinDir, "start_erl"])), + + %% Copy .boot to bin/start.boot + copy_file(RelName++".boot",filename:join([BinDir, "start.boot"])), + + %% Copy scripts from erts-xxx/bin to bin + copy_file(filename:join([ErtsBinDir, "epmd"]), + filename:join([BinDir, "epmd"]), [preserve]), + copy_file(filename:join([ErtsBinDir, "run_erl"]), + filename:join([BinDir, "run_erl"]), [preserve]), + copy_file(filename:join([ErtsBinDir, "to_erl"]), + filename:join([BinDir, "to_erl"]), [preserve]), + + %% create start_erl.data and sys.config + StartErlData = filename:join([InstallDir, "releases", "start_erl.data"]), + write_file(StartErlData, io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn])), + SysConfig = filename:join([InstallDir, "releases", RelVsn, "sys.config"]), + write_file(SysConfig, "[]."), + + %% Insert 'start' script from data_dir - modified to add sname and heart + copy_file(filename:join(?config(data_dir,Config),"start.src"), + filename:join(ErtsBinDir,"start.src")), + ok = file:change_mode(filename:join(ErtsBinDir,"start.src"),8#0755), + + %% Make start_erl executable + %% (this has been fixed in OTP 17 - is is now installed with + %% $INSTALL_SCRIPT instead of $INSTALL_DATA and should therefore + %% be executable from the start) + ok = file:change_mode(filename:join(ErtsBinDir,"start_erl.src"),8#0755), + + %% Substitute variables in erl.src, start.src and start_erl.src + %% (.src found in erts-xxx/bin - result stored in bin) + subst_src_scripts(["erl", "start", "start_erl"], ErtsBinDir, BinDir, + [{"FINAL_ROOTDIR", InstallDir}, {"EMU", "beam"}], + [preserve]), + + %% Create RELEASES + RelFile = filename:join([InstallDir, "releases", + filename:basename(RelName) ++ ".rel"]), + release_handler:create_RELEASES(InstallDir, RelFile), + + true = test_server:stop_node(Node), + + {RelName,Apps}. + +%%%----------------------------------------------------------------- +%%% Create a release containing the current (the test node) OTP +%%% release, including relup to allow upgrade from an earlier OTP +%%% release. +upgrade_system(FromRel, ToRelName0, ToVsn, + CreateDir, InstallDir) -> + + {RelName,Apps,_} = create_relfile(node(),CreateDir,ToRelName0,ToVsn), + FromPath = filename:join([InstallDir,lib,"*",ebin]), + + ok = systools:make_script(RelName), + ok = systools:make_relup(RelName,[FromRel],[FromRel], + [{path,[FromPath]}, + {outdir,CreateDir}]), + SysConfig = filename:join([CreateDir, "sys.config"]), + write_file(SysConfig, "[]."), + + ok = systools:make_tar(RelName,[{erts,code:root_dir()}]), + + {RelName,Apps}. + +%%%----------------------------------------------------------------- +%%% Start a new node running the release from target_system/5 +%%% above. Then upgrade to the system from upgrade_system/5. +do_upgrade(FromVsn,FromApps,ToRel,ToApps,InstallDir) -> + Start = filename:join([InstallDir,bin,start]), + {ok,Node} = start_node(Start,permanent,FromVsn,FromApps), + + [{"OTP upgrade test",FromVsn,_,permanent}] = + rpc:call(Node,release_handler,which_releases,[]), + {ok,ToVsn} = rpc:call(Node,release_handler,unpack_release,[ToRel]), + [{"OTP upgrade test",ToVsn,_,unpacked}, + {"OTP upgrade test",FromVsn,_,permanent}] = + rpc:call(Node,release_handler,which_releases,[]), + case rpc:call(Node,release_handler,install_release,[ToVsn]) of + {ok,FromVsn,_} -> + ok; + {continue_after_restart,FromVsn,_} -> + wait_node_up(current,ToVsn,ToApps) + end, + [{"OTP upgrade test",ToVsn,_,current}, + {"OTP upgrade test",FromVsn,_,permanent}] = + rpc:call(Node,release_handler,which_releases,[]), + ok = rpc:call(Node,release_handler,make_permanent,[ToVsn]), + [{"OTP upgrade test",ToVsn,_,permanent}, + {"OTP upgrade test",FromVsn,_,old}] = + rpc:call(Node,release_handler,which_releases,[]), + + true = test_server:stop_node(Node), + ok. + +%%%----------------------------------------------------------------- +%%% Library functions +previous_major("17") -> + "r16"; +previous_major(Rel) -> + integer_to_list(list_to_integer(Rel)-1). + +create_relfile(Node,CreateDir,RelName0,RelVsn) -> + LibDir = rpc:call(Node,code,lib_dir,[]), + SplitLibDir = filename:split(LibDir), + Paths = rpc:call(Node,code,get_path,[]), + Exclude = ?start_exclude ++ ?appup_exclude, + Apps = lists:flatmap( + fun(Path) -> + case lists:prefix(LibDir,Path) of + true -> + case filename:split(Path) -- SplitLibDir of + [AppVsn,"ebin"] -> + case string:tokens(AppVsn,"-") of + [AppStr,Vsn] -> + App = list_to_atom(AppStr), + case lists:member(App,Exclude) of + true -> + []; + false -> + [{App,Vsn}] + end; + _ -> + [] + end; + _ -> + [] + end; + false -> + [] + end + end, + Paths), + + ErtsVsn = rpc:call(Node, erlang, system_info, [version]), + + %% Create the .rel file + RelContent = {release, {"OTP upgrade test", RelVsn}, {erts, ErtsVsn}, Apps}, + RelName = filename:join(CreateDir,RelName0), + RelFile = RelName++".rel", + {ok,Fd} = file:open(RelFile,[write,{encoding,utf8}]), + io:format(Fd,"~tp.~n",[RelContent]), + ok = file:close(Fd), + {RelName,Apps,ErtsVsn}. + +copy_file(Src, Dest) -> + copy_file(Src, Dest, []). + +copy_file(Src, Dest, Opts) -> + {ok,_} = file:copy(Src, Dest), + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + file:write_file_info(Dest, FileInfo); + false -> + ok + end. + + +write_file(FName, Conts) -> + Enc = file:native_name_encoding(), + {ok, Fd} = file:open(FName, [write]), + file:write(Fd, unicode:characters_to_binary(Conts,Enc,Enc)), + file:close(Fd). + + +subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) -> + lists:foreach(fun(Script) -> + subst_src_script(Script, SrcDir, DestDir, + Vars, Opts) + end, Scripts). + +subst_src_script(Script, SrcDir, DestDir, Vars, Opts) -> + subst_file(filename:join([SrcDir, Script ++ ".src"]), + filename:join([DestDir, Script]), + Vars, Opts). + +subst_file(Src, Dest, Vars, Opts) -> + {ok, Bin} = file:read_file(Src), + Conts = binary_to_list(Bin), + NConts = subst(Conts, Vars), + write_file(Dest, NConts), + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +%% subst(Str, Vars) +%% Vars = [{Var, Val}] +%% Var = Val = string() +%% Substitute all occurrences of %Var% for Val in Str, using the list +%% of variables in Vars. +%% +subst(Str, Vars) -> + subst(Str, Vars, []). + +subst([$%, C| Rest], Vars, Result) when $A =< C, C =< $Z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when $a =< C, C =< $z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when C == $_ -> + subst_var([C| Rest], Vars, Result, []); +subst([C| Rest], Vars, Result) -> + subst(Rest, Vars, [C| Result]); +subst([], _Vars, Result) -> + lists:reverse(Result). + +subst_var([$%| Rest], Vars, Result, VarAcc) -> + Key = lists:reverse(VarAcc), + case lists:keysearch(Key, 1, Vars) of + {value, {Key, Value}} -> + subst(Rest, Vars, lists:reverse(Value, Result)); + false -> + subst(Rest, Vars, [$%| VarAcc ++ [$%| Result]]) + end; +subst_var([C| Rest], Vars, Result, VarAcc) -> + subst_var(Rest, Vars, Result, [C| VarAcc]); +subst_var([], Vars, Result, VarAcc) -> + subst([], Vars, [VarAcc ++ [$%| Result]]). + + +%%%----------------------------------------------------------------- +%%% +start_node(Start,ExpStatus,ExpVsn,ExpApps) -> + case open_port({spawn_executable, Start}, []) of + Port when is_port(Port) -> + unlink(Port), + erlang:port_close(Port), + wait_node_up(ExpStatus,ExpVsn,ExpApps); + Error -> + Error + end. + +wait_node_up(ExpStatus,ExpVsn,ExpApps) -> + Node = node_name(?upgr_sname), + wait_node_up(Node,ExpStatus,ExpVsn,lists:keysort(1,ExpApps),60). + +wait_node_up(Node,ExpStatus,ExpVsn,ExpApps,0) -> + ct:fail({app_check_failed,ExpVsn,ExpApps, + rpc:call(Node,release_handler,which_releases,[ExpStatus]), + rpc:call(Node,application,which_applications,[])}); +wait_node_up(Node,ExpStatus,ExpVsn,ExpApps,N) -> + timer:sleep(2000), + case {rpc:call(Node,release_handler,which_releases,[ExpStatus]), + rpc:call(Node, application, which_applications, [])} of + {[{_,ExpVsn,_,_}],Apps} when is_list(Apps) -> + case [{A,V} || {A,_,V} <- lists:keysort(1,Apps)] of + ExpApps -> {ok,Node}; + _ -> wait_node_up(Node,ExpStatus,ExpVsn,ExpApps,N-1) + end; + _ -> + wait_node_up(Node,ExpStatus,ExpVsn,ExpApps,N-1) + end. + +node_name(Sname) -> + {ok,Host} = inet:gethostname(), + list_to_atom(atom_to_list(Sname) ++ "@" ++ Host). + +rm_rf(Dir) -> + case file:read_file_info(Dir) of + {ok, #file_info{type = directory}} -> + {ok, Content} = file:list_dir_all(Dir), + [rm_rf(filename:join(Dir,C)) || C <- Content], + ok=file:del_dir(Dir), + ok; + {ok, #file_info{}} -> + ok=file:delete(Dir); + _ -> + ok + end. diff --git a/erts/test/upgrade_SUITE_data/start.src b/erts/test/upgrade_SUITE_data/start.src new file mode 100644 index 0000000000..70d1a322c9 --- /dev/null +++ b/erts/test/upgrade_SUITE_data/start.src @@ -0,0 +1,36 @@ +#!/bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2014. All Rights Reserved. +# +# The 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 program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# +ROOTDIR=%FINAL_ROOTDIR% + +if [ -z "$RELDIR" ] +then + RELDIR=$ROOTDIR/releases +fi + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +$ROOTDIR/bin/run_erl -daemon /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -sname otp_upgrade -heart" -- cgit v1.2.3 From 00623ae5c4718e001653a66a6d1cdca3615d0a77 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 22 Jan 2014 16:40:10 +0100 Subject: Use $INSTALL_SCRIPT instead of $INSTALL_DATA for start[_erl].src To make the result executable as is the case for erl.src. --- erts/etc/common/Makefile.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index 5c1ce51644..83560735bc 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2012. All Rights Reserved. +# Copyright Ericsson AB 1996-2014. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -488,7 +488,7 @@ ifneq ($(INSTALL_SRC),) $(INSTALL_DATA) $(INSTALL_SRC) "$(RELEASE_PATH)/erts-$(VSN)/src" endif ifneq ($(INSTALL_EMBEDDED_DATA),) - $(INSTALL_DATA) $(INSTALL_EMBEDDED_DATA) "$(RELEASE_PATH)/erts-$(VSN)/bin" + $(INSTALL_SCRIPT) $(INSTALL_EMBEDDED_DATA) "$(RELEASE_PATH)/erts-$(VSN)/bin" endif ifneq ($(INSTALL_LIBS),) $(INSTALL_DATA) $(INSTALL_LIBS) "$(RELEASE_PATH)/erts-$(VSN)/bin" -- cgit v1.2.3 From aeb2f71edb6ecba094dcfeddf8f8255229f90b9b Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 6 Feb 2014 12:25:54 +0100 Subject: Skip upgrade_SUITE when running cerl --- erts/test/upgrade_SUITE.erl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index f6799467f6..16a805db52 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -39,8 +39,16 @@ ssh,ssl,syntax_tools,test_server,tools,typer,wx,xmerl]). init_per_suite(Config) -> - rm_rf(filename:join([?config(data_dir,Config),priv_dir])), - Config. + %% Check that a real release is running, not e.g. cerl + ok = application:ensure_started(sasl), + case release_handler:which_releases() of + [{_,_,[],_}] -> + %% Fake release, no applications + {skip, "Need a real release running to create other releases"}; + _ -> + rm_rf(filename:join([?config(data_dir,Config),priv_dir])), + Config + end. init_per_testcase(Case,Config) -> PrivDir = filename:join([?config(data_dir,Config),priv_dir,Case]), -- cgit v1.2.3 From 0256f0e352021e56c3d130caed14a50d573939cb Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 7 Feb 2014 16:18:32 +0100 Subject: In upgrade_SUITE stop node with init:stop instead of test_server:stop_node This is only for nodes not started with test_server:start_node. --- erts/test/upgrade_SUITE.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index 16a805db52..9f15437772 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -236,7 +236,10 @@ do_upgrade(FromVsn,FromApps,ToRel,ToApps,InstallDir) -> {"OTP upgrade test",FromVsn,_,old}] = rpc:call(Node,release_handler,which_releases,[]), - true = test_server:stop_node(Node), + erlang:monitor_node(Node,true), + _ = rpc:call(Node,init,stop,[]), + receive {nodedown,Node} -> ok end, + ok. %%%----------------------------------------------------------------- -- cgit v1.2.3 From 41b4ec74fcbe8fedcc7b832ff7c6501e441292ac Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 11 Feb 2014 13:52:34 +0100 Subject: Allow config to point out old releases in upgrade_SUITE This suite was earlier only possible to run on linux hosts in the OTP test lab. Now a ct config file can be used to point out the two last major releases, e.g. like this: {otp_releases,[{r16,"/path/to/r16/bin/erl"}, {'17',"/path/to/17/bin/erl"}]}. Until OTP 17.0 is really released, the entry named '17' may well be left out, causing the test case upgrade_SUITE:minor to be skipped. --- erts/test/upgrade_SUITE.erl | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index 9f15437772..690ee0bad0 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -86,18 +86,35 @@ major(Config) -> %% performs an upgrade from major release X to the current release. minor(Config) -> CurrentMajor = erlang:system_info(otp_release), - Current = list_to_atom(CurrentMajor++"_patched"), + Current = CurrentMajor++"_patched", upgrade_test(CurrentMajor,Current,Config). %%%----------------------------------------------------------------- upgrade_test(FromVsn,ToVsn,Config) -> - case test_server:is_release_available(FromVsn) of - true -> - upgrade_test1(FromVsn,ToVsn,Config); + OldRel = + case test_server:is_release_available(FromVsn) of + true -> + {release,FromVsn}; + false -> + case ct:get_config({otp_releases,list_to_atom(FromVsn)}) of + undefined -> + false; + Prog0 -> + case os:find_executable(Prog0) of + false -> + false; + Prog -> + {prog,Prog} + end + end + end, + case OldRel of false -> %% Note that priv_dir here is per test case! rm_rf(?config(priv_dir,Config)), - {skip, "no previous release available"} + {skip, "no previous release available"}; + _ -> + upgrade_test1(FromVsn,ToVsn,[{old_rel,OldRel}|Config]) end. upgrade_test1(FromVsn,ToVsn,Config) -> @@ -120,7 +137,7 @@ upgrade_test1(FromVsn,ToVsn,Config) -> %%% - chmod 'start' and 'start_erl' target_system(RelName0,RelVsn,CreateDir,InstallDir,Config) -> {ok,Node} = test_server:start_node(list_to_atom(RelName0),peer, - [{erl,[{release,RelVsn}]}]), + [{erl,[?config(old_rel,Config)]}]), {RelName,Apps,ErtsVsn} = create_relfile(Node,CreateDir,RelName0,RelVsn), -- cgit v1.2.3 From d9ebb210d5299306945c68e43ce6642f9a2d1617 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 18 Feb 2014 13:30:24 +0100 Subject: Start all non-core applications as temporary in upgrade_SUITE --- erts/test/upgrade_SUITE.erl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index 690ee0bad0..bcdac2a66e 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -284,7 +284,7 @@ create_relfile(Node,CreateDir,RelName0,RelVsn) -> true -> []; false -> - [{App,Vsn}] + [{App,Vsn,restart_type(App)}] end; _ -> [] @@ -309,6 +309,11 @@ create_relfile(Node,CreateDir,RelName0,RelVsn) -> ok = file:close(Fd), {RelName,Apps,ErtsVsn}. +restart_type(App) when App==kernel; App==stdlib; App==sasl -> + permanent; +restart_type(_) -> + temporary. + copy_file(Src, Dest) -> copy_file(Src, Dest, []). @@ -400,7 +405,8 @@ start_node(Start,ExpStatus,ExpVsn,ExpApps) -> Error end. -wait_node_up(ExpStatus,ExpVsn,ExpApps) -> +wait_node_up(ExpStatus,ExpVsn,ExpApps0) -> + ExpApps = [{A,V} || {A,V,_T} <- ExpApps0], Node = node_name(?upgr_sname), wait_node_up(Node,ExpStatus,ExpVsn,lists:keysort(1,ExpApps),60). -- cgit v1.2.3 From 1ca1d9d83295e5bd53cc29997ece8e34b19cdf22 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 24 Feb 2014 12:18:27 +0100 Subject: Include more applications in upgrade_SUITE Many appup files are now corrected and the applications can be included in the upgrade suite. --- erts/test/upgrade_SUITE.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index bcdac2a66e..ee84a5dfd7 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -31,12 +31,14 @@ cosProperty,cosTime,cosTransactions,erts,ic,netconf,orber, safe]). -%% Applications that are excluded from this test because they don't -%% have useful appup files. +%% Applications that are excluded from this test because their appup +%% file don't support the upgrade. +%% In specific: +%% - hipe does not support any upgrade at all +%% - dialyzer requires hipe (in the .app file) +%% - typer requires hipe (in the .app file) -define(appup_exclude, - [asn1,common_test,compiler,crypto,dialyzer,edoc,eldap,eunit, - hipe,inets,observer,odbc,os_mon,public_key,runtime_tools, - ssh,ssl,syntax_tools,test_server,tools,typer,wx,xmerl]). + [dialyzer,hipe,typer]). init_per_suite(Config) -> %% Check that a real release is running, not e.g. cerl -- cgit v1.2.3 From 3e8b423a2cb11f819f3cede7ef817f4012f18944 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Sun, 26 Jan 2014 20:34:12 -0500 Subject: further enhancements for dirty schedulers Add support for setting the number of dirty CPU schedulers online via erlang:system_flag/2. Assuming the emulator is built with dirty schedulers enabled, the number of dirty CPU schedulers online may not be less than 1, nor greater than the number of dirty CPU schedulers available, nor greater than the number of normal schedulers online. Dirty CPU scheduler threads that are taken offline via system_flag/2 are suspended. The number of dirty CPU schedulers online may be adjusted independently of the number of normal schedulers online, but if system_flag/2 is used to set the number of normal schedulers online to a value less than the current number of normal schedulers online, the number of dirty CPU schedulers online is decreased proportionally. Likewise, if the number of normal schedulers online is increased, the number of dirty CPU schedulers online is increased proportionally. For example, if 8 normal schedulers and 4 dirty CPU schedulers are online, and system_flag/2 is called to set the number of normal schedulers online to 4, the number of dirty CPU schedulers online is also decreased by half, to 2. Subsequently setting the number of normal schedulers online back to 8 also sets the number of dirty CPU schedulers online back to 4. Augment the system_flag/2 documentation in the erlang man page to explain this relationship between schedulers_online and dirty_cpu_schedulers_online. Also ensure that all dirty CPU and I/O schedulers are suspended when multi-scheduling is blocked via system_flag/2, and brought back online when multi-scheduling is unblocked. Add Rickard Green's rewritten check_enqueue_in_prio_queue() function that inspects process state more thoroughly to determine if to enqueue it and if so on what queue, including dirty queues when appropriate. Make sure dirty NIF jobs do not trigger erlang:system_monitor long_schedule messages. Add more dirty scheduler testing to the scheduler test suite. Remove the erts_no_dirty_cpu_schedulers_online global variable, since it's no longer needed. Execute dirty NIFs on a normal scheduler thread while multi-scheduling blocking is in effect. Evacuate any dirty jobs residing in the dirty run queues over to a normal run queue when multi-scheduling is blocked. Allow dirty schedulers to execute aux work. Set the dirty run queues halt_in_progress flag when halting the normal schedulers. Change dirty scheduler numbers to a structure including both scheduler number and type, either dirty CPU or dirty I/O. Add some assertions to ensure that dirty CPU schedulers operate only on dirty CPU scheduler process flags, and the same for dirty I/O schedulers. --- erts/configure.in | 2 + erts/doc/src/erlang.xml | 33 +- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/beam_emu.c | 6 +- erts/emulator/beam/bif.c | 33 +- erts/emulator/beam/erl_drv_nif.h | 7 + erts/emulator/beam/erl_init.c | 19 +- erts/emulator/beam/erl_nif.c | 24 +- erts/emulator/beam/erl_nif.h | 4 +- erts/emulator/beam/erl_process.c | 1433 +++++++++++++++++++++++++++----- erts/emulator/beam/erl_process.h | 46 +- erts/emulator/test/scheduler_SUITE.erl | 95 ++- 12 files changed, 1455 insertions(+), 248 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 8d245252b5..6445fec954 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1233,6 +1233,7 @@ if test $emu_threads != yes; then AC_MSG_CHECKING(whether dirty schedulers should be enabled) if test "x$enable_dirty_schedulers" != "xno"; then AC_DEFINE(ERL_NIF_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) + AC_DEFINE(ERL_DRV_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) @@ -1261,6 +1262,7 @@ else EMU_THR_DEFS="$EMU_THR_DEFS -DERTS_DIRTY_SCHEDULERS" AC_DEFINE(ERTS_DIRTY_SCHEDULERS, 1, [Define if the emulator supports dirty schedulers]) AC_DEFINE(ERL_NIF_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) + AC_DEFINE(ERL_DRV_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 4cf5631727..3004acf124 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5203,9 +5203,16 @@ ok erlang:system_info(schedulers_online).

Returns the old value of the flag.

-

Note that the dirty schedulers functionality is experimental, and - that you have to enable support for dirty schedulers when building OTP in - order to try the functionality out.

+

Note that the number of dirty CPU schedulers online may change if the number of + schedulers online changes. For example, if there are 12 schedulers and all are + online, and 6 dirty CPU schedulers, all online as well, and system_flag/2 + is used to set the number of schedulers online to 6, then the number of dirty + CPU schedulers online is automatically decreased by half as well, down to 3. + Similarly, the number of dirty CPU schedulers online increases proportionally + to increases in the number of schedulers online.

+

Note that the dirty schedulers functionality is experimental, and + that you have to enable support for dirty schedulers when building OTP in order + to try out the functionality.

For more information see erlang:system_info(dirty_cpu_schedulers) and @@ -5438,6 +5445,15 @@ ok .

Returns the old value of the flag.

+

Note that if the emulator was built with support for dirty schedulers, + changing the number of schedulers online can also change the number of dirty + CPU schedulers online. For example, if there are 12 schedulers and all are + online, and 6 dirty CPU schedulers, all online as well, and system_flag/2 + is used to set the number of schedulers online to 6, then the number of dirty + CPU schedulers online is automatically decreased by half as well, down to 3. + Similarly, the number of dirty CPU schedulers online increases proportionally + to increases in the number of schedulers online.

For more information see, erlang:system_info(schedulers), and @@ -5817,12 +5833,13 @@ ok boot time and cannot be changed after that. The number of dirty CPU scheduler threads online can however be changed at any time. The number of dirty CPU schedulers can be set on startup by passing - the +SDcpu command line flag, see - erl(1). + the +SDcpu or + +SDPcpu command line flags, + see erl(1).

Note that the dirty schedulers functionality is experimental, and that you have to enable support for dirty schedulers when building OTP in - order to try the functionality out.

+ order to try out the functionality.

See also erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline), erlang:system_info(dirty_cpu_schedulers_online), erlang:system_info(dirty_io_schedulers), @@ -5844,7 +5861,7 @@ ok

Note that the dirty schedulers functionality is experimental, and that you have to enable support for dirty schedulers when building OTP in - order to try the functionality out.

+ order to try out the functionality.

For more information, see erlang:system_info(dirty_cpu_schedulers), erlang:system_info(dirty_io_schedulers), @@ -5864,7 +5881,7 @@ ok

Note that the dirty schedulers functionality is experimental, and that you have to enable support for dirty schedulers when building OTP in - order to try the functionality out.

+ order to try out the functionality.

For more information, see erlang:system_info(dirty_cpu_schedulers), erlang:system_info(dirty_cpu_schedulers_online), and diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 96547ba743..d28e519ae1 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -179,6 +179,7 @@ atom dexit atom depth atom dgroup_leader atom dictionary +atom dirty_cpu_schedulers_online atom disable_trace atom disabled atom display_items diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 1ae413d46e..1dfff7cb6a 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1203,7 +1203,11 @@ void process_main(void) if (start_time != 0) { Sint64 diff = erts_timestamp_millis() - start_time; - if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule) { + if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule +#ifdef ERTS_DIRTY_SCHEDULERS + && !ERTS_SCHEDULER_IS_DIRTY(c_p->scheduler_data) +#endif + ) { BeamInstr *inptr = find_function_from_pc(start_time_i); BeamInstr *outptr = find_function_from_pc(c_p->i); monitor_long_schedule_proc(c_p,inptr,outptr,(Uint) diff); diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 9c4801041f..f34045a589 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4333,7 +4333,11 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) switch (erts_set_schedulers_online(BIF_P, ERTS_PROC_LOCK_MAIN, signed_val(BIF_ARG_2), - &old_no)) { + &old_no +#ifdef ERTS_DIRTY_SCHEDULERS + , 0 +#endif + )) { case ERTS_SCHDLR_SSPND_DONE: BIF_RET(make_small(old_no)); case ERTS_SCHDLR_SSPND_YIELD_RESTART: @@ -4465,6 +4469,33 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) ref, old ? am_true : am_false); } +#if defined(ERTS_SMP) && defined(ERTS_DIRTY_SCHEDULERS) + } else if (BIF_ARG_1 == am_dirty_cpu_schedulers_online) { + Sint old_no; + if (!is_small(BIF_ARG_2)) + goto error; + switch (erts_set_schedulers_online(BIF_P, + ERTS_PROC_LOCK_MAIN, + signed_val(BIF_ARG_2), + &old_no, + 1)) { + case ERTS_SCHDLR_SSPND_DONE: + BIF_RET(make_small(old_no)); + case ERTS_SCHDLR_SSPND_YIELD_RESTART: + ERTS_VBUMP_ALL_REDS(BIF_P); + BIF_TRAP2(bif_export[BIF_system_flag_2], + BIF_P, BIF_ARG_1, BIF_ARG_2); + case ERTS_SCHDLR_SSPND_YIELD_DONE: + ERTS_BIF_YIELD_RETURN_X(BIF_P, make_small(old_no), + am_dirty_cpu_schedulers_online); + case ERTS_SCHDLR_SSPND_EINVAL: + goto error; + default: + ASSERT(0); + BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); + break; + } +#endif } else if (ERTS_IS_ATOM_STR("scheduling_statistics", BIF_ARG_1)) { int what; if (ERTS_IS_ATOM_STR("disable", BIF_ARG_2)) diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h index ea013a49a3..3f829ea7ea 100644 --- a/erts/emulator/beam/erl_drv_nif.h +++ b/erts/emulator/beam/erl_drv_nif.h @@ -41,6 +41,13 @@ typedef struct { int suggested_stack_size; } ErlDrvThreadOpts; +#if defined(ERL_DRV_DIRTY_SCHEDULER_SUPPORT) || defined(ERL_NIF_DIRTY_SCHEDULER_SUPPORT) +typedef enum { + ERL_DRV_DIRTY_JOB_CPU_BOUND = 1, + ERL_DRV_DIRTY_JOB_IO_BOUND = 2 +} ErlDrvDirtyJobFlags; +#endif + #endif /* __ERL_DRV_NIF_H__ */ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index e6c05adec1..5bdebc493c 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -178,6 +178,11 @@ int erts_compat_rel; static int no_schedulers; static int no_schedulers_online; +#ifdef ERTS_DIRTY_SCHEDULERS +static int no_dirty_cpu_schedulers; +static int no_dirty_cpu_schedulers_online; +static int no_dirty_io_schedulers; +#endif #ifdef DEBUG Uint32 verbose; /* See erl_debug.h for information about verbose */ @@ -304,7 +309,13 @@ erl_init(int ncpu, erts_init_sys_common_misc(); erts_init_process(ncpu, proc_tab_sz, legacy_proc_tab); erts_init_scheduling(no_schedulers, - no_schedulers_online); + no_schedulers_online +#ifdef ERTS_DIRTY_SCHEDULERS + , no_dirty_cpu_schedulers, + no_dirty_cpu_schedulers_online, + no_dirty_io_schedulers +#endif + ); erts_init_cpu_topology(); /* Must be after init_scheduling */ erts_init_gc(); /* Must be after init_scheduling */ erts_alloc_late_init(); @@ -1055,9 +1066,9 @@ early_init(int *argc, char **argv) /* erts_no_schedulers = (Uint) no_schedulers; #endif #ifdef ERTS_DIRTY_SCHEDULERS - erts_no_dirty_cpu_schedulers = dirty_cpu_scheds; - erts_no_dirty_cpu_schedulers_online = dirty_cpu_scheds_online; - erts_no_dirty_io_schedulers = dirty_io_scheds; + erts_no_dirty_cpu_schedulers = no_dirty_cpu_schedulers = dirty_cpu_scheds; + no_dirty_cpu_schedulers_online = dirty_cpu_scheds_online; + erts_no_dirty_io_schedulers = no_dirty_io_schedulers = dirty_io_scheds; #endif erts_early_init_scheduling(no_schedulers); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c35f1fc2c6..1ddfb2f7bd 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1510,6 +1510,13 @@ enif_schedule_dirty_nif(ErlNifEnv* env, int flags, a = erts_smp_atomic32_read_acqb(&proc->state); while (1) { n = state = a; + /* + * clear any current dirty flags and dirty queue indicators, + * in case the application is shifting a job from one type + * of dirty scheduler to the other + */ + n &= ~(ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC + |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q); if (chkflgs == ERL_NIF_DIRTY_JOB_CPU_BOUND) n |= ERTS_PSFLG_DIRTY_CPU_PROC; else @@ -1540,22 +1547,15 @@ enif_schedule_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result, ERL_NIF_TERM (*fp)(ErlNifEnv*, ERL_NIF_TERM)) { #ifdef USE_THREADS - erts_aint32_t state, n, a; Process* proc = env->proc; Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; Export* ep; - a = erts_smp_atomic32_read_acqb(&proc->state); - while (1) { - n = state = a; - if (!(n & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q))) - break; - n &= ~(ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC - |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q); - a = erts_smp_atomic32_cmpxchg_mb(&proc->state, n, state); - if (a == state) - break; - } + erts_smp_atomic32_read_band_mb(&proc->state, + ~(ERTS_PSFLG_DIRTY_CPU_PROC + |ERTS_PSFLG_DIRTY_IO_PROC + |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q + |ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)); if (!(ep = ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc))) alloc_proc_psd(proc, &ep); ERTS_VBUMP_ALL_REDS(proc); diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 7613446f64..c12ba4d554 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -172,8 +172,8 @@ typedef ErlDrvThreadOpts ErlNifThreadOpts; #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT typedef enum { - ERL_NIF_DIRTY_JOB_CPU_BOUND = 1, - ERL_NIF_DIRTY_JOB_IO_BOUND = 2 + ERL_NIF_DIRTY_JOB_CPU_BOUND = ERL_DRV_DIRTY_JOB_CPU_BOUND, + ERL_NIF_DIRTY_JOB_IO_BOUND = ERL_DRV_DIRTY_JOB_IO_BOUND }ErlNifDirtyTaskFlags; #endif diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 937881212a..1d0275c524 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -148,7 +148,6 @@ int erts_sched_balance_util = 0; Uint erts_no_schedulers; #ifdef ERTS_DIRTY_SCHEDULERS Uint erts_no_dirty_cpu_schedulers; -Uint erts_no_dirty_cpu_schedulers_online; Uint erts_no_dirty_io_schedulers; #endif @@ -188,6 +187,13 @@ static ErtsAuxWorkData *aux_thread_aux_work_data; #define ERTS_SCHDLR_SSPND_CHNG_SET(VAL, OLD_VAL) \ erts_smp_atomic32_set_nob(&schdlr_sspnd.changing, (VAL)) +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(VAL, OLD_VAL) \ + erts_smp_atomic32_set_nob(&schdlr_sspnd.dirty_cpu_changing, (VAL)) +#define ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(VAL, OLD_VAL) \ + erts_smp_atomic32_set_nob(&schdlr_sspnd.dirty_io_changing, (VAL)) +#endif + #else #define ERTS_SCHDLR_SSPND_CHNG_SET(VAL, OLD_VAL) \ @@ -198,6 +204,23 @@ do { \ ASSERT(old_val__ == (OLD_VAL)); \ } while (0) +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(VAL, OLD_VAL) \ +do { \ + erts_aint32_t old_val__; \ + old_val__ = erts_smp_atomic32_xchg_nob(&schdlr_sspnd.dirty_cpu_changing, \ + (VAL)); \ + ASSERT(old_val__ == (OLD_VAL)); \ +} while (0) +#define ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(VAL, OLD_VAL) \ +do { \ + erts_aint32_t old_val__; \ + old_val__ = erts_smp_atomic32_xchg_nob(&schdlr_sspnd.dirty_io_changing, \ + (VAL)); \ + ASSERT(old_val__ == (OLD_VAL)); \ +} while (0) +#endif + #endif @@ -207,11 +230,29 @@ static struct { int online; int curr_online; int wait_curr_online; +#ifdef ERTS_DIRTY_SCHEDULERS + int dirty_cpu_online; + int dirty_cpu_curr_online; + int dirty_cpu_wait_curr_online; + int dirty_io_online; + int dirty_io_curr_online; + int dirty_io_wait_curr_online; +#endif erts_smp_atomic32_t changing; erts_smp_atomic32_t active; +#ifdef ERTS_DIRTY_SCHEDULERS + erts_smp_atomic32_t dirty_cpu_changing; + erts_smp_atomic32_t dirty_cpu_active; + erts_smp_atomic32_t dirty_io_changing; + erts_smp_atomic32_t dirty_io_active; +#endif struct { int ongoing; long wait_active; +#ifdef ERTS_DIRTY_SCHEDULERS + long dirty_cpu_wait_active; + long dirty_io_wait_active; +#endif ErtsProcList *procs; } msb; /* Multi Scheduling Block */ } schdlr_sspnd; @@ -1306,6 +1347,9 @@ static ERTS_INLINE void haw_thr_prgr_current_check_progress(ErtsAuxWorkData *awdp) { ErtsThrPrgrVal current = awdp->current_thr_prgr; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif if (current != ERTS_THR_PRGR_INVALID && !erts_thr_progress_equal(current, erts_thr_progress_current())) { /* @@ -1322,6 +1366,10 @@ handle_delayed_aux_work_wakeup(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, in { int jix, max_jix; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif + ASSERT(awdp->delayed_wakeup.next != ERTS_DELAYED_WAKEUP_INFINITY); if (!waiting && awdp->delayed_wakeup.next > awdp->esdp->reductions) @@ -1477,6 +1525,9 @@ handle_misc_aux_work_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) { +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif if (!erts_thr_progress_has_reached_this(haw_thr_prgr_current(awdp), awdp->misc.thr_prgr)) return aux_work & ~ERTS_SSI_AUX_WORK_MISC_THR_PRGR; @@ -1561,6 +1612,9 @@ handle_async_ready(ErtsAuxWorkData *awdp, int waiting) { ErtsSchedulerSleepInfo *ssi = awdp->ssi; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_ASYNC_READY); if (erts_check_async_ready(awdp->async_ready.queue)) { if (set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_ASYNC_READY) @@ -1585,6 +1639,9 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp, { void *thr_prgr_p; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif #ifdef ERTS_SMP if (awdp->async_ready.need_thr_prgr && !erts_thr_progress_has_reached_this(haw_thr_prgr_current(awdp), @@ -1622,6 +1679,9 @@ handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) ErtsSchedulerSleepInfo *ssi = awdp->ssi; erts_aint32_t res; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif unset_aux_work_flags(ssi, (ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM | ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC)); aux_work &= ~(ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM @@ -1655,11 +1715,7 @@ erts_alloc_ensure_handle_delayed_dealloc_call(int ix) { #ifdef DEBUG ErtsSchedulerData *esdp = erts_get_scheduler_data(); -#ifdef ERTS_DIRTY_SCHEDULERS - if (esdp && ERTS_SCHEDULER_IS_DIRTY(esdp)) - return; -#endif - ASSERT(!esdp || ix == (int) esdp->no); + ASSERT(!esdp || (ERTS_SCHEDULER_IS_DIRTY(esdp) || ix == (int) esdp->no)); #endif set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(ix-1), ERTS_SSI_AUX_WORK_DD); @@ -1673,6 +1729,9 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waitin ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID; int more_work = 0; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD); erts_alloc_scheduler_handle_delayed_dealloc((void *) awdp->esdp, &need_thr_progress, @@ -1712,6 +1771,9 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, i ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID; ErtsThrPrgrVal current = haw_thr_prgr_current(awdp); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif if (!erts_thr_progress_has_reached_this(current, awdp->dd.thr_prgr)) return aux_work & ~ERTS_SSI_AUX_WORK_DD_THR_PRGR; @@ -1759,6 +1821,9 @@ handle_thr_prgr_later_op(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int wait int lops; ErtsThrPrgrVal current = haw_thr_prgr_current(awdp); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif for (lops = 0; lops < ERTS_MAX_THR_PRGR_LATER_OPS; lops++) { ErtsThrPrgrLaterOp *lop = awdp->later_op.first; if (!erts_thr_progress_has_reached_this(current, lop->later)) @@ -1917,6 +1982,14 @@ erts_smp_notify_check_children_needed(void) for (i = 0; i < erts_no_schedulers; i++) set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(i), ERTS_SSI_AUX_WORK_CHECK_CHILDREN); +#ifdef ERTS_DIRTY_SCHEDULERS + for (i = 0; i < erts_no_dirty_cpu_schedulers; i++) + set_aux_work_flags_wakeup_nob(ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(i), + ERTS_SSI_AUX_WORK_CHECK_CHILDREN); + for (i = 0; i < erts_no_dirty_io_schedulers; i++) + set_aux_work_flags_wakeup_nob(ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(i), + ERTS_SSI_AUX_WORK_CHECK_CHILDREN); +#endif } static ERTS_INLINE erts_aint32_t @@ -2697,15 +2770,15 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) while (1) { - aux_work = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : - erts_atomic32_read_acqb(&ssi->aux_work); + aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work) { - if (!thr_prgr_active) { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !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 && erts_thr_progress_update(esdp)) + if (aux_work && !ERTS_SCHEDULER_IS_DIRTY(esdp) + && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); } @@ -2990,7 +3063,7 @@ wake_scheduler(ErtsRunQueue *rq) #ifdef ERTS_DIRTY_SCHEDULERS static void -wake_dirty_scheduler(ErtsRunQueue *rq) +wake_dirty_schedulers(ErtsRunQueue *rq, int one) { ErtsSchedulerSleepInfo *ssi; ErtsSchedulerSleepList *sl; @@ -3000,9 +3073,27 @@ wake_dirty_scheduler(ErtsRunQueue *rq) sl = &rq->sleepers; erts_smp_spin_lock(&sl->lock); ssi = sl->list; - if (!ssi) + if (!ssi) { erts_smp_spin_unlock(&sl->lock); - else { + if (one) + wake_scheduler(rq); + } else if (one) { + erts_aint32_t flgs; + if (ssi->prev) + ssi->prev->next = ssi->next; + else { + ASSERT(sl->list == ssi); + sl->list = ssi->next; + } + if (ssi->next) + ssi->next->prev = ssi->prev; + + erts_smp_spin_unlock(&sl->lock); + + ERTS_THR_MEMORY_BARRIER; + flgs = ssi_flags_set_wake(ssi); + erts_sched_finish_poke(ssi, flgs); + } else { sl->list = NULL; erts_smp_spin_unlock(&sl->lock); @@ -3154,7 +3245,7 @@ smp_notify_inc_runq(ErtsRunQueue *runq) if (runq) { #ifdef ERTS_DIRTY_SCHEDULERS if (ERTS_RUNQ_IX_IS_DIRTY(runq->ix)) - wake_dirty_scheduler(runq); + wake_dirty_schedulers(runq, 1); else #endif wake_scheduler(runq); @@ -3452,6 +3543,7 @@ suspend_run_queue(ErtsRunQueue *rq) } static void scheduler_ix_resume_wake(Uint ix); +static void scheduler_ssi_resume_wake(ErtsSchedulerSleepInfo *ssi); static ERTS_INLINE void resume_run_queue(ErtsRunQueue *rq) @@ -3478,7 +3570,10 @@ resume_run_queue(ErtsRunQueue *rq) erts_smp_runq_unlock(rq); - scheduler_ix_resume_wake(rq->ix); +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) +#endif + scheduler_ix_resume_wake(rq->ix); } typedef struct { @@ -3509,20 +3604,28 @@ evacuate_run_queue(ErtsRunQueue *rq, int prio_q; ErtsRunQueue *to_rq; ErtsMigrationPaths *mps; - ErtsMigrationPath *mp; + ErtsMigrationPath *mp = NULL; ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); - mps = erts_get_migration_paths_managed(); - mp = &mps->mpath[rq->ix]; +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) +#endif + { + mps = erts_get_migration_paths_managed(); + mp = &mps->mpath[rq->ix]; + } /* Evacuate scheduled misc ops */ if (rq->misc.start) { ErtsMiscOpList *start, *end; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); +#endif to_rq = mp->misc_evac_runq; if (!to_rq) return; @@ -3551,6 +3654,9 @@ evacuate_run_queue(ErtsRunQueue *rq, if (rq->ports.start) { Port *prt; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); +#endif to_rq = mp->prio[ERTS_PORT_PRIO_LEVEL].runq; if (!to_rq) return; @@ -3586,15 +3692,26 @@ evacuate_run_queue(ErtsRunQueue *rq, erts_aint32_t state; Process *proc; int notify = 0; +#ifdef ERTS_DIRTY_SCHEDULERS + int requeue; +#endif to_rq = NULL; - if (!mp->prio[prio_q].runq) - return; - if (prio_q == PRIORITY_NORMAL && !mp->prio[PRIORITY_LOW].runq) - return; +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) +#endif + { + if (!mp->prio[prio_q].runq) + return; + if (prio_q == PRIORITY_NORMAL && !mp->prio[PRIORITY_LOW].runq) + return; + } proc = dequeue_process(rq, prio_q, &state); while (proc) { +#ifdef ERTS_DIRTY_SCHEDULERS + requeue = 1; +#endif if (ERTS_PSFLG_BOUND & state) { /* Bound processes get stuck here... */ proc->next = NULL; @@ -3603,12 +3720,43 @@ evacuate_run_queue(ErtsRunQueue *rq, else sbpp->first = proc; sbpp->last = proc; +#ifdef ERTS_DIRTY_SCHEDULERS + requeue = 0; +#endif + } +#ifdef ERTS_DIRTY_SCHEDULERS + else if (state & ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q) { + erts_aint32_t old; + old = erts_smp_atomic32_read_band_nob(&proc->state, + ~(ERTS_PSFLG_DIRTY_CPU_PROC + | ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q)); + /* assert that no other dirty flags are set */ + ASSERT(!(old & (ERTS_PSFLG_DIRTY_IO_PROC|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q))); + } else if (state & ERTS_PSFLG_DIRTY_IO_PROC_IN_Q) { + erts_aint32_t old; + old = erts_smp_atomic32_read_band_nob(&proc->state, + ~(ERTS_PSFLG_DIRTY_IO_PROC + | ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)); + /* assert that no other dirty flags are set */ + ASSERT(!(old & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q))); } + if (requeue) { +#else else { +#endif int prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state); erts_smp_runq_unlock(rq); - to_rq = mp->prio[prio].runq; +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) + /* + * dirty run queues evacuate only to run + * queue 0 during multi-scheduling blocking + */ + to_rq = ERTS_RUNQ_IX(0); + else +#endif + to_rq = mp->prio[prio].runq; RUNQ_SET_RQ(&proc->run_queue, to_rq); erts_smp_runq_lock(to_rq); @@ -4722,13 +4870,20 @@ wakeup_other_check(ErtsRunQueue *rq, Uint32 flags) rq->wakeup_other += (left_len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC); 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; +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && rq->waiting) + wake_dirty_schedulers(rq, 1); + else +#endif + { + 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; + } } } rq->wakeup_other_reds = 0; @@ -5009,8 +5164,12 @@ erts_sched_set_wake_cleanup_threshold(char *str) static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp) { - if (!esdp || ERTS_SCHEDULER_IS_DIRTY(esdp)) + if (!esdp) awdp->sched_id = 0; +#ifdef ERTS_DIRTY_SCHEDULERS + else if (ERTS_SCHEDULER_IS_DIRTY(esdp)) + awdp->sched_id = (int) ERTS_DIRTY_SCHEDULER_NO(esdp); +#endif else awdp->sched_id = (int) esdp->no; awdp->esdp = esdp; @@ -5076,11 +5235,11 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num, #ifdef ERTS_DIRTY_SCHEDULERS if (ERTS_RUNQ_IX_IS_DIRTY(runq->ix)) { esdp->no = 0; - esdp->dirty_no = (Uint) num; + ERTS_DIRTY_SCHEDULER_NO(esdp) = (Uint) num; } else { esdp->no = (Uint) num; - esdp->dirty_no = 0; + ERTS_DIRTY_SCHEDULER_NO(esdp) = 0; } #else esdp->no = (Uint) num; @@ -5111,7 +5270,12 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num, } void -erts_init_scheduling(int no_schedulers, int no_schedulers_online) +erts_init_scheduling(int no_schedulers, int no_schedulers_online +#ifdef ERTS_DIRTY_SCHEDULERS + , int no_dirty_cpu_schedulers, int no_dirty_cpu_schedulers_online, + int no_dirty_io_schedulers +#endif + ) { int ix, n, no_ssi; char *daww_ptr; @@ -5133,6 +5297,12 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) ASSERT(no_schedulers_online <= no_schedulers); ASSERT(no_schedulers_online >= 1); ASSERT(no_schedulers >= 1); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(no_dirty_cpu_schedulers <= no_schedulers); + ASSERT(no_dirty_cpu_schedulers >= 1); + ASSERT(no_dirty_cpu_schedulers_online <= no_schedulers_online); + ASSERT(no_dirty_cpu_schedulers_online >= 1); +#endif /* Create and initialize run queues */ @@ -5169,10 +5339,9 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) #ifdef ERTS_DIRTY_SCHEDULERS #ifdef ERTS_SMP - if (ERTS_RUNQ_IX_IS_DIRTY(ix)) { + if (ERTS_RUNQ_IX_IS_DIRTY(ix)) erts_smp_spinlock_init(&rq->sleepers.lock, "dirty_run_queue_sleep_list"); - rq->sleepers.list = NULL; - } + rq->sleepers.list = NULL; #endif #endif @@ -5236,6 +5405,10 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) n = (int) no_schedulers; erts_no_schedulers = n; +#ifdef ERTS_DIRTY_SCHEDULERS + erts_no_dirty_cpu_schedulers = no_dirty_cpu_schedulers; + erts_no_dirty_io_schedulers = no_dirty_io_schedulers; +#endif /* Create and initialize scheduler sleep info */ #ifdef ERTS_SMP @@ -5267,21 +5440,21 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) aligned_dirty_cpu_sched_sleep_info = erts_alloc_permanent_cache_aligned( ERTS_ALC_T_SCHDLR_SLP_INFO, - erts_no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerSleepInfo)); - for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { + no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerSleepInfo)); + for (ix = 0; ix < no_dirty_cpu_schedulers; ix++) { ErtsSchedulerSleepInfo *ssi = &aligned_dirty_cpu_sched_sleep_info[ix].ssi; erts_smp_atomic32_init_nob(&ssi->flags, 0); - ssi->event = NULL; /* initialized in sched_thread_func */ + ssi->event = NULL; /* initialized in sched_dirty_cpu_thread_func */ erts_atomic32_init_nob(&ssi->aux_work, 0); } aligned_dirty_io_sched_sleep_info = erts_alloc_permanent_cache_aligned( ERTS_ALC_T_SCHDLR_SLP_INFO, - erts_no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerSleepInfo)); - for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerSleepInfo)); + for (ix = 0; ix < no_dirty_io_schedulers; ix++) { ErtsSchedulerSleepInfo *ssi = &aligned_dirty_io_sched_sleep_info[ix].ssi; erts_smp_atomic32_init_nob(&ssi->flags, 0); - ssi->event = NULL; /* initialized in sched_thread_func */ + ssi->event = NULL; /* initialized in sched_dirty_io_thread_func */ erts_atomic32_init_nob(&ssi->aux_work, 0); } #endif @@ -5314,8 +5487,8 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) erts_aligned_dirty_cpu_scheduler_data = erts_alloc_permanent_cache_aligned( ERTS_ALC_T_SCHDLR_DATA, - erts_no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerData)); - for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { + no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerData)); + for (ix = 0; ix < no_dirty_cpu_schedulers; ix++) { ErtsSchedulerData *esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); init_scheduler_data(esdp, ix+1, ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix), ERTS_DIRTY_CPU_RUNQ, NULL, 0); @@ -5323,8 +5496,8 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) erts_aligned_dirty_io_scheduler_data = erts_alloc_permanent_cache_aligned( ERTS_ALC_T_SCHDLR_DATA, - erts_no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerData)); - for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerData)); + for (ix = 0; ix < no_dirty_io_schedulers; ix++) { ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix); init_scheduler_data(esdp, ix+1, ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix), ERTS_DIRTY_IO_RUNQ, NULL, 0); @@ -5354,6 +5527,16 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) schdlr_sspnd.curr_online = no_schedulers; schdlr_sspnd.msb.ongoing = 0; erts_smp_atomic32_init_nob(&schdlr_sspnd.active, no_schedulers); +#ifdef ERTS_DIRTY_SCHEDULERS + erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_cpu_changing, 0); + schdlr_sspnd.dirty_cpu_online = no_dirty_cpu_schedulers_online; + schdlr_sspnd.dirty_cpu_curr_online = no_dirty_cpu_schedulers; + erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_cpu_active, no_dirty_cpu_schedulers); + erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_io_changing, 0); + schdlr_sspnd.dirty_io_online = no_dirty_io_schedulers; + schdlr_sspnd.dirty_io_curr_online = no_dirty_io_schedulers; + erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_io_active, no_dirty_io_schedulers); +#endif schdlr_sspnd.msb.procs = NULL; init_no_runqs(no_schedulers_online, no_schedulers_online); balance_info.last_active_runqs = no_schedulers; @@ -5379,6 +5562,21 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) schdlr_sspnd.curr_online *= 2; /* Boot strapping... */ ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); +#ifdef ERTS_DIRTY_SCHEDULERS + schdlr_sspnd.dirty_cpu_wait_curr_online = no_dirty_cpu_schedulers_online; + schdlr_sspnd.dirty_cpu_curr_online *= 2; + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + for (ix = no_dirty_cpu_schedulers_online; ix < no_dirty_cpu_schedulers; ix++) { + ErtsSchedulerData* esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); + erts_smp_atomic32_read_bor_nob(&esdp->ssi->flags, ERTS_SSI_FLG_SUSPENDED); + } + + schdlr_sspnd.dirty_io_wait_curr_online = no_dirty_io_schedulers; + schdlr_sspnd.dirty_io_curr_online *= 2; + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); +#endif erts_smp_atomic32_init_nob(&doing_sys_schedule, 0); @@ -5497,9 +5695,16 @@ free_proxy_proc(Process *proxy) erts_free(ERTS_ALC_T_PROC, proxy); } +#define ERTS_ENQUEUE_NOT 0 +#define ERTS_ENQUEUE_NORMAL_QUEUE 1 +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_ENQUEUE_DIRTY_CPU_QUEUE 2 +#define ERTS_ENQUEUE_DIRTY_IO_QUEUE 3 +#endif static ERTS_INLINE int -check_enqueue_in_prio_queue(erts_aint32_t *prq_prio_p, +check_enqueue_in_prio_queue(Process *c_p, + erts_aint32_t *prq_prio_p, erts_aint32_t *newp, erts_aint32_t actual) { @@ -5511,56 +5716,105 @@ check_enqueue_in_prio_queue(erts_aint32_t *prq_prio_p, *prq_prio_p = aprio; #ifdef ERTS_DIRTY_SCHEDULERS - if (!(actual & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC))) { + if (actual & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) { + /* + * If we have system tasks of a priority higher + * or equal to the user priority, we enqueue + * on ordinary run-queue and take care of + * those system tasks first. + */ + if (actual & ERTS_PSFLG_ACTIVE_SYS) { + erts_aint32_t uprio, stprio, qmask; + uprio = (actual >> ERTS_PSFLGS_USR_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK; + if (aprio < uprio) + goto enqueue_normal_runq; /* system tasks with higher prio */ + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS); + qmask = c_p->sys_task_qs->qmask; + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS); + switch (qmask & -qmask) { + case MAX_BIT: + stprio = PRIORITY_MAX; + break; + case HIGH_BIT: + stprio = PRIORITY_HIGH; + break; + case NORMAL_BIT: + stprio = PRIORITY_NORMAL; + break; + case LOW_BIT: + stprio = PRIORITY_LOW; + break; + default: + stprio = PRIORITY_LOW+1; + break; + } + if (stprio <= uprio) + goto enqueue_normal_runq; /* system tasks with higher prio */ + } + + /* Enqueue in dirty run queue if not already enqueued */ + if (actual & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)) + return ERTS_ENQUEUE_NOT; /* already in queue */ + if (actual & ERTS_PSFLG_DIRTY_CPU_PROC) { + *newp |= ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q; + if (actual & ERTS_PSFLG_IN_RUNQ) + return -ERTS_ENQUEUE_DIRTY_CPU_QUEUE; /* use proxy */ + *newp |= ERTS_PSFLG_IN_RUNQ; + return ERTS_ENQUEUE_DIRTY_CPU_QUEUE; + } + *newp |= ERTS_PSFLG_DIRTY_IO_PROC_IN_Q; + if (actual & ERTS_PSFLG_IN_RUNQ) + return -ERTS_ENQUEUE_DIRTY_IO_QUEUE; /* use proxy */ + *newp |= ERTS_PSFLG_IN_RUNQ; + return ERTS_ENQUEUE_DIRTY_IO_QUEUE; + } + + enqueue_normal_runq: #endif - max_qbit = (actual >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET) & ERTS_PSFLGS_QMASK; - max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS; - max_qbit &= -max_qbit; - /* - * max_qbit now either contain bit set for highest prio queue or a bit - * out of range (which will have a value larger than valid range). - */ + max_qbit = (actual >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET) & ERTS_PSFLGS_QMASK; + max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS; + max_qbit &= -max_qbit; + /* + * max_qbit now either contain bit set for highest prio queue or a bit + * out of range (which will have a value larger than valid range). + */ - if (qbit >= max_qbit) - return 0; /* Already queued in higher or equal prio */ + if (qbit >= max_qbit) + return ERTS_ENQUEUE_NOT; /* Already queued in higher or equal prio */ - /* Need to enqueue (if already enqueued, it is in lower prio) */ - *newp |= qbit << ERTS_PSFLGS_IN_PRQ_MASK_OFFSET; + /* Need to enqueue (if already enqueued, it is in lower prio) */ + *newp |= qbit << ERTS_PSFLGS_IN_PRQ_MASK_OFFSET; - if ((actual & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLGS_USR_PRIO_MASK)) - != (aprio << ERTS_PSFLGS_USR_PRIO_OFFSET)) { - /* - * Process struct already enqueued, or actual prio not - * equal to user prio, i.e., enqueue using proxy. - */ - return -1; - } -#ifdef ERTS_DIRTY_SCHEDULERS - } else { - if (actual & ERTS_PSFLG_DIRTY_CPU_PROC) - *newp |= ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q; - else - *newp |= ERTS_PSFLG_DIRTY_IO_PROC_IN_Q; + if ((actual & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLGS_USR_PRIO_MASK)) + != (aprio << ERTS_PSFLGS_USR_PRIO_OFFSET)) { + /* + * Process struct already enqueued, or actual prio not + * equal to user prio, i.e., enqueue using proxy. + */ + return -ERTS_ENQUEUE_NORMAL_QUEUE; } -#endif /* * Enqueue using process struct. */ *newp &= ~ERTS_PSFLGS_PRQ_PRIO_MASK; *newp |= ERTS_PSFLG_IN_RUNQ | (aprio << ERTS_PSFLGS_PRQ_PRIO_OFFSET); - return 1; + return ERTS_ENQUEUE_NORMAL_QUEUE; } /* - * scheduler_out_process() return with c_rq locked. + * schedule_out_process() return with c_rq locked. */ static ERTS_INLINE int schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Process *proxy) { erts_aint32_t a, e, n, enq_prio = -1; - int res = 0; int enqueue; /* < 0 -> use proxy */ + Process* sched_p; + ErtsRunQueue* runq; +#ifdef ERTS_SMP + int check_emigration_need; +#endif a = state; @@ -5569,20 +5823,20 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces ASSERT(a & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)); - enqueue = 0; + enqueue = ERTS_ENQUEUE_NOT; n &= ~(ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS); if (a & ERTS_PSFLG_ACTIVE_SYS || (a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { - enqueue = check_enqueue_in_prio_queue(&enq_prio, &n, a); + enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); } a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); if (a == e) break; } - if (!enqueue) { - + switch (enqueue) { + case ERTS_ENQUEUE_NOT: if (erts_system_profile_flags.runnable_procs) { if (!(a & ERTS_PSFLG_ACTIVE_SYS) @@ -5595,60 +5849,76 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces if (proxy) free_proxy_proc(proxy); - } - else { - Process *sched_p; - ErtsRunQueue *runq; - ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & ERTS_PSFLG_ACTIVE_SYS)); + erts_smp_runq_lock(c_rq); + return 0; #ifdef ERTS_DIRTY_SCHEDULERS #ifdef ERTS_SMP - if (ERTS_PSFLG_DIRTY_CPU_PROC & a) - runq = ERTS_DIRTY_CPU_RUNQ; - else if (ERTS_PSFLG_DIRTY_IO_PROC & a) - runq = ERTS_DIRTY_IO_RUNQ; - else + case ERTS_ENQUEUE_DIRTY_CPU_QUEUE: + case -ERTS_ENQUEUE_DIRTY_CPU_QUEUE: + runq = ERTS_DIRTY_CPU_RUNQ; + ASSERT(ERTS_SCHEDULER_IS_DIRTY_CPU(runq->scheduler)); +#ifdef ERTS_SMP + check_emigration_need = 0; +#endif + break; + + case ERTS_ENQUEUE_DIRTY_IO_QUEUE: + case -ERTS_ENQUEUE_DIRTY_IO_QUEUE: + runq = ERTS_DIRTY_IO_RUNQ; + ASSERT(ERTS_SCHEDULER_IS_DIRTY_IO(runq->scheduler)); +#ifdef ERTS_SMP + check_emigration_need = 0; +#endif + break; #endif #endif - runq = erts_get_runq_proc(p); - if (enqueue < 0) - sched_p = make_proxy_proc(proxy, p, enq_prio); - else { - sched_p = p; - if (proxy) - free_proxy_proc(proxy); - } + default: + ASSERT(enqueue == ERTS_ENQUEUE_NORMAL_QUEUE + || enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE); + runq = erts_get_runq_proc(p); #ifdef ERTS_SMP - if (!(ERTS_PSFLG_BOUND & n) -#ifdef ERTS_DIRTY_SCHEDULERS - && !(n & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)) + check_emigration_need = !(ERTS_PSFLG_BOUND & n); #endif - ) { - ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio); - if (new_runq) { - RUNQ_SET_RQ(&sched_p->run_queue, new_runq); - runq = new_runq; - } + break; + } + + ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & ERTS_PSFLG_ACTIVE_SYS)); + + if (enqueue < 0) + sched_p = make_proxy_proc(proxy, p, enq_prio); + else { + sched_p = p; + if (proxy) + free_proxy_proc(proxy); + } + +#ifdef ERTS_SMP + if (check_emigration_need) { + ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio); + if (new_runq) { + RUNQ_SET_RQ(&sched_p->run_queue, new_runq); + runq = new_runq; } + } #endif - ASSERT(runq); - res = 1; - erts_smp_runq_lock(runq); + ASSERT(runq); - /* Enqueue the process */ - enqueue_process(runq, (int) enq_prio, sched_p); + erts_smp_runq_lock(runq); - if (runq == c_rq) - return res; - erts_smp_runq_unlock(runq); - smp_notify_inc_runq(runq); - } + /* Enqueue the process */ + enqueue_process(runq, (int) enq_prio, sched_p); + + if (runq == c_rq) + return 1; + erts_smp_runq_unlock(runq); + smp_notify_inc_runq(runq); erts_smp_runq_lock(c_rq); - return res; + return 1; } static ERTS_INLINE void @@ -5704,7 +5974,7 @@ change_proc_schedule_state(Process *p, erts_aint32_t e; n = e = a; - enqueue = 0; + enqueue = ERTS_ENQUEUE_NOT; if (a & ERTS_PSFLG_FREE) break; /* We don't want to schedule free processes... */ @@ -5725,13 +5995,13 @@ change_proc_schedule_state(Process *p, * process may be in a run queue via proxy, need * further inspection... */ - enqueue = check_enqueue_in_prio_queue(enq_prio_p, &n, a); + enqueue = check_enqueue_in_prio_queue(p, enq_prio_p, &n, a); } a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); if (a == e) break; - if (enqueue == 0 && n == a) + if (enqueue == ERTS_ENQUEUE_NOT && n == a) break; } @@ -5765,7 +6035,7 @@ schedule_process(Process *p, erts_aint32_t in_state) ERTS_PSFLG_ACTIVE, &state, &enq_prio); - if (enqueue) + if (enqueue != ERTS_ENQUEUE_NOT) add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), state, enq_prio); @@ -5792,14 +6062,14 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) if (a & ERTS_PSFLG_FREE) return; /* We don't want to schedule free processes... */ - enqueue = 0; + enqueue = ERTS_ENQUEUE_NOT; n |= ERTS_PSFLG_ACTIVE_SYS; if (!(a & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))) - enqueue = check_enqueue_in_prio_queue(&enq_prio, &n, a); + enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); if (a == e) break; - if (a == n && !enqueue) + if (a == n && enqueue == ERTS_ENQUEUE_NOT) goto cleanup; } @@ -5815,7 +6085,7 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) } - if (enqueue) { + if (enqueue != ERTS_ENQUEUE_NOT) { Process *sched_p; if (enqueue > 0) sched_p = p; @@ -5938,6 +6208,12 @@ static void scheduler_ix_resume_wake(Uint ix) { ErtsSchedulerSleepInfo *ssi = ERTS_SCHED_SLEEP_INFO_IX(ix); + scheduler_ssi_resume_wake(ssi); +} + +static void +scheduler_ssi_resume_wake(ErtsSchedulerSleepInfo *ssi) +{ erts_aint32_t xflgs = (ERTS_SSI_FLG_SLEEPING | ERTS_SSI_FLG_TSE_SLEEPING | ERTS_SSI_FLG_WAITING @@ -6028,12 +6304,20 @@ sched_set_suspended_sleeptype(ErtsSchedulerSleepInfo *ssi) } } +#ifdef ERTS_DIRTY_SCHEDULERS + static void suspend_scheduler(ErtsSchedulerData *esdp) { erts_aint32_t flgs; erts_aint32_t changing; +#ifdef ERTS_DIRTY_SCHEDULERS + long no = (long) (ERTS_SCHEDULER_IS_DIRTY(esdp) + ? ERTS_DIRTY_SCHEDULER_NO(esdp) + : esdp->no); +#else long no = (long) esdp->no; +#endif ErtsSchedulerSleepInfo *ssi = esdp->ssi; long active_schedulers; int curr_online = 1; @@ -6041,47 +6325,106 @@ suspend_scheduler(ErtsSchedulerData *esdp) erts_aint32_t aux_work; int thr_prgr_active = 1; ErtsStuckBoundProcesses sbp = {NULL, NULL}; + int* ss_onlinep; + int* ss_curr_onlinep; + int* ss_wait_curr_onlinep; + long* ss_wait_activep; + long ss_wait_active_target; + erts_smp_atomic32_t* ss_changingp; + erts_smp_atomic32_t* ss_activep; /* * Schedulers may be suspended in two different ways: * - A scheduler may be suspended since it is not online. * All schedulers with scheduler ids greater than - * schdlr_sspnd.online are suspended. + * schdlr_sspnd.online are suspended; same for dirty + * schedulers and schdlr_sspnd.dirty_cpu_online and + * schdlr_sspnd.dirty_io_online. * - Multi scheduling is blocked. All schedulers except the - * scheduler with scheduler id 1 are suspended. + * scheduler with scheduler id 1 are suspended, and all + * dirty CPU and dirty I/O schedulers are suspended. * * Regardless of why a scheduler is suspended, it ends up here. */ + ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp) || no != 1); + #ifdef ERTS_DIRTY_SCHEDULERS - ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); + if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (erts_smp_mtx_trylock(&schdlr_sspnd.mtx) == EBUSY) { + erts_smp_runq_unlock(esdp->run_queue); + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + erts_smp_runq_lock(esdp->run_queue); + } + if (ongoing_multi_scheduling_block()) + evacuate_run_queue(esdp->run_queue, &sbp); + } else #endif - ASSERT(no != 1); - - evacuate_run_queue(esdp->run_queue, &sbp); + evacuate_run_queue(esdp->run_queue, &sbp); erts_smp_runq_unlock(esdp->run_queue); - erts_sched_check_cpu_bind_prep_suspend(esdp); +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) +#endif + { + erts_sched_check_cpu_bind_prep_suspend(esdp); - if (erts_system_profile_flags.scheduler) - profile_scheduler(make_small(esdp->no), am_inactive); + if (erts_system_profile_flags.scheduler) + profile_scheduler(make_small(esdp->no), am_inactive); - sched_wall_time_change(esdp, 0); + sched_wall_time_change(esdp, 0); - erts_smp_mtx_lock(&schdlr_sspnd.mtx); + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + } flgs = sched_prep_spin_suspended(ssi, ERTS_SSI_FLG_SUSPENDED); if (flgs & ERTS_SSI_FLG_SUSPENDED) { - active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.active); - ASSERT(active_schedulers >= 1); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(esdp->run_queue)) { + active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.dirty_cpu_active); + ASSERT(active_schedulers >= 0); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing); + ss_onlinep = &schdlr_sspnd.dirty_cpu_online; + ss_curr_onlinep = &schdlr_sspnd.dirty_cpu_curr_online; + ss_wait_curr_onlinep = &schdlr_sspnd.dirty_cpu_wait_curr_online; + ss_changingp = &schdlr_sspnd.dirty_cpu_changing; + ss_wait_activep = &schdlr_sspnd.msb.dirty_cpu_wait_active; + ss_activep = &schdlr_sspnd.dirty_cpu_active; + } else { + active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.dirty_io_active); + ASSERT(active_schedulers >= 0); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing); + ss_onlinep = &schdlr_sspnd.dirty_io_online; + ss_curr_onlinep = &schdlr_sspnd.dirty_io_curr_online; + ss_wait_curr_onlinep = &schdlr_sspnd.dirty_io_wait_curr_online; + ss_changingp = &schdlr_sspnd.dirty_io_changing; + ss_wait_activep = &schdlr_sspnd.msb.dirty_io_wait_active; + ss_activep = &schdlr_sspnd.dirty_io_active; + } + ss_wait_active_target = 0; + } + else +#endif + { + active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.active); + ASSERT(active_schedulers >= 1); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); + ss_onlinep = &schdlr_sspnd.online; + ss_curr_onlinep = &schdlr_sspnd.curr_online; + ss_wait_curr_onlinep = &schdlr_sspnd.wait_curr_online; + ss_changingp = &schdlr_sspnd.changing; + ss_wait_activep = &schdlr_sspnd.msb.wait_active; + ss_activep = &schdlr_sspnd.active; + ss_wait_active_target = 1; + } if (changing & ERTS_SCHDLR_SSPND_CHNG_MSB) { - if (active_schedulers == schdlr_sspnd.msb.wait_active) + if (active_schedulers == *ss_wait_activep) wake = 1; - if (active_schedulers == 1) { - changing = erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, + if (active_schedulers == ss_wait_active_target) { + changing = erts_smp_atomic32_read_band_nob(ss_changingp, ~ERTS_SCHDLR_SSPND_CHNG_MSB); changing &= ~ERTS_SCHDLR_SSPND_CHNG_MSB; } @@ -6090,21 +6433,21 @@ suspend_scheduler(ErtsSchedulerData *esdp) while (1) { if (changing & ERTS_SCHDLR_SSPND_CHNG_ONLN) { int changed = 0; - if (no > schdlr_sspnd.online && curr_online) { - schdlr_sspnd.curr_online--; + if (no > *ss_onlinep && curr_online) { + (*ss_curr_onlinep)--; curr_online = 0; changed = 1; } - else if (no <= schdlr_sspnd.online && !curr_online) { - schdlr_sspnd.curr_online++; + else if (no <= *ss_onlinep && !curr_online) { + (*ss_curr_onlinep)++; curr_online = 1; changed = 1; } if (changed - && schdlr_sspnd.curr_online == schdlr_sspnd.wait_curr_online) + && *ss_curr_onlinep == *ss_wait_curr_onlinep) wake = 1; - if (schdlr_sspnd.online == schdlr_sspnd.curr_online) { - changing = erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, + if (*ss_onlinep == *ss_curr_onlinep) { + changing = erts_smp_atomic32_read_band_nob(ss_changingp, ~ERTS_SCHDLR_SSPND_CHNG_ONLN); changing &= ~ERTS_SCHDLR_SSPND_CHNG_ONLN; } @@ -6130,7 +6473,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) & ERTS_RUNQ_FLGS_QMASK); aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work|qmask) { - if (!thr_prgr_active) { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } @@ -6138,21 +6481,40 @@ suspend_scheduler(ErtsSchedulerData *esdp) aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); - if (aux_work && erts_thr_progress_update(esdp)) + + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && + (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); +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + erts_smp_runq_lock(esdp->run_queue); + if (ongoing_multi_scheduling_block()) + evacuate_run_queue(esdp->run_queue, &sbp); + erts_smp_runq_unlock(esdp->run_queue); + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + } else +#endif + { + erts_smp_runq_lock(esdp->run_queue); + evacuate_run_queue(esdp->run_queue, &sbp); + erts_smp_runq_unlock(esdp->run_queue); + } } } if (!aux_work) { - if (thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 0); - sched_wall_time_change(esdp, 0); +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) +#endif + { + if (thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 0); + sched_wall_time_change(esdp, 0); + } + erts_thr_progress_prepare_wait(esdp); } - erts_thr_progress_prepare_wait(esdp); flgs = sched_spin_suspended(ssi, ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT); if (flgs == (ERTS_SSI_FLG_SLEEPING @@ -6170,24 +6532,230 @@ suspend_scheduler(ErtsSchedulerData *esdp) } while (res == EINTR); } } - erts_thr_progress_finalize_wait(esdp); +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) +#endif + erts_thr_progress_finalize_wait(esdp); } flgs = sched_prep_spin_suspended(ssi, (ERTS_SSI_FLG_WAITING | ERTS_SSI_FLG_SUSPENDED)); if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) break; - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); + changing = erts_smp_atomic32_read_nob(ss_changingp); if (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER) break; } erts_smp_mtx_lock(&schdlr_sspnd.mtx); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); + changing = erts_smp_atomic32_read_nob(ss_changingp); } - active_schedulers = erts_smp_atomic32_inc_read_nob(&schdlr_sspnd.active); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); + active_schedulers = erts_smp_atomic32_inc_read_nob(ss_activep); + changing = erts_smp_atomic32_read_nob(ss_changingp); + if ((changing & ERTS_SCHDLR_SSPND_CHNG_MSB) + && *ss_onlinep == active_schedulers) { + erts_smp_atomic32_read_band_nob(ss_changingp, + ~ERTS_SCHDLR_SSPND_CHNG_MSB); + } + +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) +#endif + ASSERT(no <= *ss_onlinep); + ASSERT(!ongoing_multi_scheduling_block()); + + } + + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + + ASSERT(curr_online); + +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) +#endif + { + if (erts_system_profile_flags.scheduler) + profile_scheduler(make_small(esdp->no), am_active); + + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + } + + erts_smp_runq_lock(esdp->run_queue); + non_empty_runq(esdp->run_queue); + +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) +#endif + { + schedule_bound_processes(esdp->run_queue, &sbp); + + erts_sched_check_cpu_bind_post_suspend(esdp); + } +} + +#else /* !ERTS_DIRTY_SCHEDULERS */ + +static void +suspend_scheduler(ErtsSchedulerData *esdp) +{ + erts_aint32_t flgs; + erts_aint32_t changing; + long no = (long) esdp->no; + ErtsSchedulerSleepInfo *ssi = esdp->ssi; + long active_schedulers; + int curr_online = 1; + int wake = 0; + erts_aint32_t aux_work; + int thr_prgr_active = 1; + ErtsStuckBoundProcesses sbp = {NULL, NULL}; + + /* + * Schedulers may be suspended in two different ways: + * - A scheduler may be suspended since it is not online. + * All schedulers with scheduler ids greater than + * schdlr_sspnd.online are suspended. + * - Multi scheduling is blocked. All schedulers except the + * scheduler with scheduler id 1 are suspended. + * + * Regardless of why a scheduler is suspended, it ends up here. + */ + + ASSERT(no != 1); + + evacuate_run_queue(esdp->run_queue, &sbp); + + erts_smp_runq_unlock(esdp->run_queue); + + erts_sched_check_cpu_bind_prep_suspend(esdp); + + if (erts_system_profile_flags.scheduler) + profile_scheduler(make_small(esdp->no), am_inactive); + + sched_wall_time_change(esdp, 0); + + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + + flgs = sched_prep_spin_suspended(ssi, ERTS_SSI_FLG_SUSPENDED); + if (flgs & ERTS_SSI_FLG_SUSPENDED) { + + active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.active); + ASSERT(active_schedulers >= 1); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); + if (changing & ERTS_SCHDLR_SSPND_CHNG_MSB) { + if (active_schedulers == schdlr_sspnd.msb.wait_active) + wake = 1; + if (active_schedulers == 1) { + changing = erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, + ~ERTS_SCHDLR_SSPND_CHNG_MSB); + changing &= ~ERTS_SCHDLR_SSPND_CHNG_MSB; + } + } + + while (1) { + if (changing & ERTS_SCHDLR_SSPND_CHNG_ONLN) { + int changed = 0; + if (no > schdlr_sspnd.online && curr_online) { + schdlr_sspnd.curr_online--; + curr_online = 0; + changed = 1; + } + else if (no <= schdlr_sspnd.online && !curr_online) { + schdlr_sspnd.curr_online++; + curr_online = 1; + changed = 1; + } + if (changed + && schdlr_sspnd.curr_online == schdlr_sspnd.wait_curr_online) + wake = 1; + if (schdlr_sspnd.online == schdlr_sspnd.curr_online) { + changing = erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, + ~ERTS_SCHDLR_SSPND_CHNG_ONLN); + changing &= ~ERTS_SCHDLR_SSPND_CHNG_ONLN; + } + } + + if (wake) { + erts_smp_cnd_signal(&schdlr_sspnd.cnd); + wake = 0; + } + + if (curr_online && !ongoing_multi_scheduling_block()) { + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); + if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) + break; + } + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + + 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|qmask) { + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + if (aux_work) + aux_work = handle_aux_work(&esdp->aux_work_data, + aux_work, + 1); + 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) { + if (thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 0); + sched_wall_time_change(esdp, 0); + } + erts_thr_progress_prepare_wait(esdp); + flgs = sched_spin_suspended(ssi, + ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT); + if (flgs == (ERTS_SSI_FLG_SLEEPING + | ERTS_SSI_FLG_WAITING + | ERTS_SSI_FLG_SUSPENDED)) { + flgs = sched_set_suspended_sleeptype(ssi); + if (flgs == (ERTS_SSI_FLG_SLEEPING + | ERTS_SSI_FLG_TSE_SLEEPING + | ERTS_SSI_FLG_WAITING + | ERTS_SSI_FLG_SUSPENDED)) { + int res; + + do { + res = erts_tse_wait(ssi->event); + } while (res == EINTR); + } + } + erts_thr_progress_finalize_wait(esdp); + } + + flgs = sched_prep_spin_suspended(ssi, (ERTS_SSI_FLG_WAITING + | ERTS_SSI_FLG_SUSPENDED)); + if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) + break; + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); + if (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER) + break; + } + + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); + } + + active_schedulers = erts_smp_atomic32_inc_read_nob(&schdlr_sspnd.active); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); if ((changing & ERTS_SCHDLR_SSPND_CHNG_MSB) && schdlr_sspnd.online == active_schedulers) { erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, @@ -6219,6 +6787,8 @@ suspend_scheduler(ErtsSchedulerData *esdp) erts_sched_check_cpu_bind_post_suspend(esdp); } +#endif + ErtsSchedSuspendResult erts_schedulers_state(Uint *total, Uint *online, @@ -6230,40 +6800,315 @@ erts_schedulers_state(Uint *total, { int res = ERTS_SCHDLR_SSPND_EINVAL; erts_aint32_t changing; - if (total) { - ASSERT(online); - ASSERT(active); - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); - if (yield_allowed && (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER)) - res = ERTS_SCHDLR_SSPND_YIELD_RESTART; - else { - *active = *online = schdlr_sspnd.online; - if (ongoing_multi_scheduling_block()) - *active = 1; - res = ERTS_SCHDLR_SSPND_DONE; - } - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - *total = erts_no_schedulers; + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); +#ifdef ERTS_DIRTY_SCHEDULERS + changing |= (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing) + | erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing)); +#endif + if (yield_allowed && (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER)) + res = ERTS_SCHDLR_SSPND_YIELD_RESTART; + else { + if (active) + *active = schdlr_sspnd.online; + if (online) + *online = schdlr_sspnd.online; + if (ongoing_multi_scheduling_block() && active) + *active = 1; +#ifdef ERTS_DIRTY_SCHEDULERS + if (dirty_cpu_online) + *dirty_cpu_online = schdlr_sspnd.dirty_cpu_online; +#endif + res = ERTS_SCHDLR_SSPND_DONE; } + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + if (total) + *total = erts_no_schedulers; #ifdef ERTS_DIRTY_SCHEDULERS if (dirty_cpu) *dirty_cpu = erts_no_dirty_cpu_schedulers; - if (dirty_cpu_online) - *dirty_cpu_online = erts_no_dirty_cpu_schedulers_online; if (dirty_io) *dirty_io = erts_no_dirty_io_schedulers; -#else - if (dirty_cpu) - *dirty_cpu = 0; - if (dirty_cpu_online) - *dirty_cpu_online = 0; - if (dirty_io) - *dirty_io = 0; #endif return res; } +#ifdef ERTS_DIRTY_SCHEDULERS + +ErtsSchedSuspendResult +erts_set_schedulers_online(Process *p, + ErtsProcLocks plocks, + Sint new_no, + Sint *old_no +#ifdef ERTS_DIRTY_SCHEDULERS + , int dirty_only +#endif + ) +{ + ErtsSchedulerData *esdp; + int ix, res = -1, no, have_unlocked_plocks, end_wait; + erts_aint32_t changing = 0; +#ifdef ERTS_DIRTY_SCHEDULERS + ErtsSchedulerSleepInfo* ssi; + int dirty_no, change_dirty; +#endif + + if (new_no < 1) + return ERTS_SCHDLR_SSPND_EINVAL; +#ifdef ERTS_DIRTY_SCHEDULERS + else if (dirty_only && erts_no_dirty_cpu_schedulers < new_no) + return ERTS_SCHDLR_SSPND_EINVAL; +#endif + else if (erts_no_schedulers < new_no) + return ERTS_SCHDLR_SSPND_EINVAL; + + esdp = ERTS_PROC_GET_SCHDATA(p); + end_wait = 0; + + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + + have_unlocked_plocks = 0; + no = (int) new_no; + +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(schdlr_sspnd.dirty_cpu_online <= erts_no_dirty_cpu_schedulers); + if (dirty_only) { + if (no > schdlr_sspnd.online) { + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + return ERTS_SCHDLR_SSPND_EINVAL; + } + dirty_no = no; + } else { + /* + * Adjust the number of dirty CPU schedulers online relative to the + * adjustment made to the number of normal schedulers online. + */ + int total_pct = erts_no_dirty_cpu_schedulers*100/erts_no_schedulers; + int onln_pct = no*total_pct/schdlr_sspnd.online; + dirty_no = schdlr_sspnd.dirty_cpu_online*onln_pct/100; + if (dirty_no == 0) + dirty_no = 1; + ASSERT(dirty_no <= erts_no_dirty_cpu_schedulers); + } +#endif + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); +#ifdef ERTS_DIRTY_SCHEDULERS + changing |= erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing); +#endif + if (changing) { + res = ERTS_SCHDLR_SSPND_YIELD_RESTART; + } + else { + int online = *old_no = schdlr_sspnd.online; +#ifdef ERTS_DIRTY_SCHEDULERS + int dirty_online = schdlr_sspnd.dirty_cpu_online; + + if (dirty_only) { + *old_no = schdlr_sspnd.dirty_cpu_online; + if (dirty_no == schdlr_sspnd.dirty_cpu_online) { + res = ERTS_SCHDLR_SSPND_DONE; + } + change_dirty = 1; + } else { +#endif + if (no == schdlr_sspnd.online) { +#ifdef ERTS_DIRTY_SCHEDULERS + dirty_only = 1; + if (dirty_no == schdlr_sspnd.dirty_cpu_online) +#endif + res = ERTS_SCHDLR_SSPND_DONE; +#ifdef ERTS_DIRTY_SCHEDULERS + else + change_dirty = 1; +#endif + } +#ifdef ERTS_DIRTY_SCHEDULERS + else + change_dirty = (dirty_no != schdlr_sspnd.dirty_cpu_online); + } +#endif + if (res == -1) + { + int increase = (no > online); +#ifdef ERTS_DIRTY_SCHEDULERS + if (!dirty_only) { +#endif + ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + schdlr_sspnd.online = no; +#ifdef ERTS_DIRTY_SCHEDULERS + } else + increase = (dirty_no > dirty_online); + if (change_dirty) { + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + schdlr_sspnd.dirty_cpu_online = dirty_no; + } +#endif + if (increase) { + int ix; +#ifdef ERTS_DIRTY_SCHEDULERS + if (!dirty_only) { +#endif + schdlr_sspnd.wait_curr_online = no; + if (ongoing_multi_scheduling_block()) { + for (ix = online; ix < no; ix++) + erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix)); + } + else { + if (plocks) { + have_unlocked_plocks = 1; + erts_smp_proc_unlock(p, plocks); + } + change_no_used_runqs(no); + + 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)); + } +#ifdef ERTS_DIRTY_SCHEDULERS + } + if (change_dirty) { + schdlr_sspnd.dirty_cpu_wait_curr_online = dirty_no; + ASSERT(schdlr_sspnd.dirty_cpu_curr_online != + schdlr_sspnd.dirty_cpu_wait_curr_online); + if (ongoing_multi_scheduling_block()) { + for (ix = dirty_online; ix < dirty_no; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_sched_poke(ssi); + } + } else { + for (ix = dirty_online; ix < dirty_no; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + scheduler_ssi_resume_wake(ssi); + erts_smp_atomic32_read_band_nob(&ssi->flags, + ~ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); + } + } +#endif + res = ERTS_SCHDLR_SSPND_DONE; + } + else /* if (no < online) */ { +#ifdef ERTS_DIRTY_SCHEDULERS + if (change_dirty) { + schdlr_sspnd.dirty_cpu_wait_curr_online = dirty_no; + ASSERT(schdlr_sspnd.dirty_cpu_curr_online != + schdlr_sspnd.dirty_cpu_wait_curr_online); + if (ongoing_multi_scheduling_block()) { + for (ix = dirty_no; ix < dirty_online; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_sched_poke(ssi); + } + } else { + for (ix = dirty_no; ix < dirty_online; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_smp_atomic32_read_bor_nob(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); + } + } + if (dirty_only) { + res = ERTS_SCHDLR_SSPND_DONE; + } + else +#endif + { + if (p->scheduler_data->no <= no) { + res = ERTS_SCHDLR_SSPND_DONE; + schdlr_sspnd.wait_curr_online = no; + } + else { + /* + * Yield! Current process needs to migrate + * before bif returns. + */ + res = ERTS_SCHDLR_SSPND_YIELD_DONE; + schdlr_sspnd.wait_curr_online = no+1; + } + + if (ongoing_multi_scheduling_block()) { + for (ix = no; ix < online; ix++) + erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix)); + } + else { + if (plocks) { + have_unlocked_plocks = 1; + erts_smp_proc_unlock(p, plocks); + } + + change_no_used_runqs(no); + for (ix = no; ix < erts_no_run_queues; ix++) + suspend_run_queue(ERTS_RUNQ_IX(ix)); + + for (ix = no; ix < online; ix++) { + ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); + wake_scheduler(rq); + } + } + } + } + +#ifdef ERTS_DIRTY_SCHEDULERS + if (change_dirty) { + while (schdlr_sspnd.dirty_cpu_curr_online != schdlr_sspnd.dirty_cpu_wait_curr_online) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_cpu_changing, + ~ERTS_SCHDLR_SSPND_CHNG_WAITER); + } + if (!dirty_only) +#endif + { + if (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online) { + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + if (plocks && !have_unlocked_plocks) { + have_unlocked_plocks = 1; + erts_smp_proc_unlock(p, plocks); + } + erts_thr_progress_active(esdp, 0); + erts_thr_progress_prepare_wait(esdp); + end_wait = 1; + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + } + + while (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + + ASSERT(res != ERTS_SCHDLR_SSPND_DONE + ? (ERTS_SCHDLR_SSPND_CHNG_WAITER + & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)) + : (ERTS_SCHDLR_SSPND_CHNG_WAITER + == erts_smp_atomic32_read_nob(&schdlr_sspnd.changing))); + erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, + ~ERTS_SCHDLR_SSPND_CHNG_WAITER); + } + } + } + + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(schdlr_sspnd.dirty_cpu_online <= schdlr_sspnd.online); + if (!dirty_only) +#endif + { + if (end_wait) { + erts_thr_progress_finalize_wait(esdp); + erts_thr_progress_active(esdp, 1); + } + if (have_unlocked_plocks) + erts_smp_proc_lock(p, plocks); + } + + return res; +} + +#else /* !ERTS_DIRTY_SCHEDULERS */ + ErtsSchedSuspendResult erts_set_schedulers_online(Process *p, ErtsProcLocks plocks, @@ -6352,10 +7197,6 @@ erts_set_schedulers_online(Process *p, ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); wake_scheduler(rq); } -#ifdef ERTS_DIRTY_SCHEDULERS - wake_dirty_scheduler(ERTS_DIRTY_CPU_RUNQ); - wake_dirty_scheduler(ERTS_DIRTY_IO_RUNQ); -#endif } } @@ -6396,15 +7237,24 @@ erts_set_schedulers_online(Process *p, return res; } +#endif + ErtsSchedSuspendResult erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) { - int ix, res, have_unlocked_plocks = 0; + int ix, res, have_unlocked_plocks = 0, online; erts_aint32_t changing; ErtsProcList *plp; +#ifdef ERTS_DIRTY_SCHEDULERS + ErtsSchedulerSleepInfo* ssi; +#endif erts_smp_mtx_lock(&schdlr_sspnd.mtx); changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); +#ifdef ERTS_DIRTY_SCHEDULERS + changing |= (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing) + | erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing)); +#endif if (changing) { res = ERTS_SCHDLR_SSPND_YIELD_RESTART; /* Yield */ } @@ -6414,10 +7264,13 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) erts_proclist_store_last(&schdlr_sspnd.msb.procs, plp); p->flags |= F_HAVE_BLCKD_MSCHED; ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) == 0); + ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active) == 0); +#endif ASSERT(p->scheduler_data->no == 1); res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; - } - else { + } else { int online = schdlr_sspnd.online; p->flags |= F_HAVE_BLCKD_MSCHED; if (plocks) { @@ -6429,6 +7282,35 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) if (online == 1) { res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) == 1); + ASSERT(!(erts_smp_atomic32_read_nob(&ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(0)->flags) + & ERTS_SSI_FLG_SUSPENDED)); + schdlr_sspnd.msb.dirty_cpu_wait_active = 0; + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(0); + erts_smp_atomic32_read_bor_nob(&ssi->flags, ERTS_SSI_FLG_SUSPENDED); + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); + while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) + != schdlr_sspnd.msb.dirty_cpu_wait_active) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + + schdlr_sspnd.msb.dirty_io_wait_active = 0; + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix); + erts_smp_atomic32_read_bor_nob(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0); + while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active) + != schdlr_sspnd.msb.dirty_io_wait_active) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); +#endif ASSERT(p->scheduler_data->no == 1); } else { @@ -6447,6 +7329,37 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) schdlr_sspnd.msb.wait_active = 2; } +#ifdef ERTS_DIRTY_SCHEDULERS + schdlr_sspnd.msb.dirty_cpu_wait_active = 0; + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_smp_atomic32_read_bor_nob(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); + while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) + != schdlr_sspnd.msb.dirty_cpu_wait_active) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + ASSERT(schdlr_sspnd.dirty_cpu_curr_online == schdlr_sspnd.dirty_cpu_online); + + schdlr_sspnd.msb.dirty_io_wait_active = 0; + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix); + erts_smp_atomic32_read_bor_nob(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0); + while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active) + != schdlr_sspnd.msb.dirty_io_wait_active) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + ASSERT(schdlr_sspnd.dirty_io_curr_online == schdlr_sspnd.dirty_io_online); +#endif change_no_used_runqs(1); for (ix = 1; ix < erts_no_run_queues; ix++) suspend_run_queue(ERTS_RUNQ_IX(ix)); @@ -6487,6 +7400,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) erts_smp_mtx_lock(&schdlr_sspnd.mtx); } + ASSERT(res != ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED ? (ERTS_SCHDLR_SSPND_CHNG_WAITER & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)) @@ -6527,12 +7441,12 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) p->flags &= ~F_HAVE_BLCKD_MSCHED; schdlr_sspnd.msb.ongoing = 0; if (schdlr_sspnd.online == 1) { - /* No schedulers to resume */ + /* No normal schedulers to resume */ ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1); ERTS_SCHDLR_SSPND_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_MSB); } else { - int online = schdlr_sspnd.online; + online = schdlr_sspnd.online; if (plocks) { have_unlocked_plocks = 1; erts_smp_proc_unlock(p, plocks); @@ -6547,6 +7461,27 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) for (ix = online; ix < erts_no_run_queues; ix++) suspend_run_queue(ERTS_RUNQ_IX(ix)); } +#ifdef ERTS_DIRTY_SCHEDULERS + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0); + schdlr_sspnd.msb.dirty_cpu_wait_active = schdlr_sspnd.dirty_cpu_online; + for (ix = 0; ix < schdlr_sspnd.dirty_cpu_online; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + scheduler_ssi_resume_wake(ssi); + erts_smp_atomic32_read_band_nob(&ssi->flags, + ~ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); + + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0); + schdlr_sspnd.msb.dirty_io_wait_active = erts_no_dirty_io_schedulers; + for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix); + scheduler_ssi_resume_wake(ssi); + erts_smp_atomic32_read_band_nob(&ssi->flags, + ~ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0); +#endif res = ERTS_SCHDLR_SSPND_DONE; } } @@ -6673,7 +7608,11 @@ sched_thread_func(void *vesdp) erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, ~ERTS_SCHDLR_SSPND_CHNG_ONLN); if (no != 1) +#ifdef ERTS_DIRTY_SCHEDULERS + erts_smp_cnd_broadcast(&schdlr_sspnd.cnd); +#else erts_smp_cnd_signal(&schdlr_sspnd.cnd); +#endif } if (no == 1) { @@ -6710,7 +7649,9 @@ sched_dirty_cpu_thread_func(void *vesdp) { ErtsThrPrgrCallbacks callbacks; ErtsSchedulerData *esdp = vesdp; - Uint no = esdp->dirty_no; + Uint no = ERTS_DIRTY_SCHEDULER_NO(esdp); + ERTS_DIRTY_SCHEDULER_TYPE(esdp) = ERTS_DIRTY_CPU_SCHEDULER; + ASSERT(no != 0); ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(no-1)->event = erts_tse_fetch(); callbacks.arg = (void *) esdp->ssi; callbacks.wakeup = thr_prgr_wakeup; @@ -6738,6 +7679,24 @@ sched_dirty_cpu_thread_func(void *vesdp) #endif erts_thread_init_float(); + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing) + & ERTS_SCHDLR_SSPND_CHNG_ONLN); + + if (--schdlr_sspnd.dirty_cpu_curr_online == schdlr_sspnd.dirty_cpu_wait_curr_online) { + erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_cpu_changing, + ~ERTS_SCHDLR_SSPND_CHNG_ONLN); + if (no != 1) + erts_smp_cnd_broadcast(&schdlr_sspnd.cnd); + } + + if (no == 1) { + while (schdlr_sspnd.dirty_cpu_curr_online != schdlr_sspnd.dirty_cpu_wait_curr_online) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + } + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + process_main(); /* No schedulers should *ever* terminate */ erl_exit(ERTS_ABORT_EXIT, @@ -6751,7 +7710,9 @@ sched_dirty_io_thread_func(void *vesdp) { ErtsThrPrgrCallbacks callbacks; ErtsSchedulerData *esdp = vesdp; - Uint no = esdp->dirty_no; + Uint no = ERTS_DIRTY_SCHEDULER_NO(esdp); + ERTS_DIRTY_SCHEDULER_TYPE(esdp) = ERTS_DIRTY_IO_SCHEDULER; + ASSERT(no != 0); ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(no-1)->event = erts_tse_fetch(); callbacks.arg = (void *) esdp->ssi; callbacks.wakeup = thr_prgr_wakeup; @@ -6779,6 +7740,24 @@ sched_dirty_io_thread_func(void *vesdp) #endif erts_thread_init_float(); + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing) + & ERTS_SCHDLR_SSPND_CHNG_ONLN); + + if (--schdlr_sspnd.dirty_io_curr_online == schdlr_sspnd.dirty_io_wait_curr_online) { + erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_io_changing, + ~ERTS_SCHDLR_SSPND_CHNG_ONLN); + if (no != 1) + erts_smp_cnd_broadcast(&schdlr_sspnd.cnd); + } + + if (no == 1) { + while (schdlr_sspnd.dirty_io_curr_online != schdlr_sspnd.dirty_io_wait_curr_online) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + } + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + process_main(); /* No schedulers should *ever* terminate */ erl_exit(ERTS_ABORT_EXIT, @@ -7995,7 +8974,6 @@ Process *schedule(Process *p, int calls) || flags & ERTS_RUNQ_FLG_NONEMPTY); 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); @@ -8006,10 +8984,17 @@ Process *schedule(Process *p, int calls) erts_sched_check_cpu_bind(esdp); } } +#ifdef ERTS_DIRTY_SCHEDULERS + else if (ERTS_SCHEDULER_IS_DIRTY(esdp) + && (erts_smp_atomic32_read_acqb(&esdp->ssi->flags) + & ERTS_SSI_FLG_SUSPENDED)) + suspend_scheduler(esdp); +#endif - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + { erts_aint32_t aux_work; - int leader_update = erts_thr_progress_update(esdp); + int leader_update = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 + : erts_thr_progress_update(esdp); aux_work = erts_atomic32_read_acqb(&esdp->ssi->aux_work); if (aux_work | leader_update) { erts_smp_runq_unlock(rq); @@ -8020,7 +9005,8 @@ Process *schedule(Process *p, int calls) erts_smp_runq_lock(rq); } - ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); + ERTS_SMP_LC_ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp) + || !erts_thr_progress_is_blocking()); } ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); @@ -8035,6 +9021,17 @@ Process *schedule(Process *p, int calls) flags = ERTS_RUNQ_FLGS_GET_NOB(rq); +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && rq->halt_in_progress) { + /* + * TODO: if halt in progress, need to put the dirty scheduler + * to sleep somewhere around here to prevent it from picking up + * new work + */ + } + else +#endif + if ((!(flags & ERTS_RUNQ_FLGS_QMASK) && !rq->misc.start) || (rq->halt_in_progress && ERTS_EMPTY_RUNQ_PORTS(rq))) { /* Prepare for scheduler wait */ @@ -8182,11 +9179,15 @@ Process *schedule(Process *p, int calls) + ERTS_PSFLGS_IN_PRQ_MASK_OFFSET)); #ifdef ERTS_DIRTY_SCHEDULERS - /* if a non-dirty scheduler picks up a process marked as already being - in a dirty run queue, just drop it and go get another process */ - if (state & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q) && - !ERTS_SCHEDULER_IS_DIRTY(esdp)) - goto pick_next_process; + ASSERT((state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) != + (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)); + if (state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) { + ASSERT((ERTS_SCHEDULER_IS_DIRTY_CPU(esdp) && (state & ERTS_PSFLG_DIRTY_CPU_PROC)) || + (ERTS_SCHEDULER_IS_DIRTY_IO(esdp) && (state & ERTS_PSFLG_DIRTY_IO_PROC))); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !(state & ERTS_PSFLG_ACTIVE_SYS)) + goto pick_next_process; + state &= ~(ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q); + } #endif if (!(state & ERTS_PSFLG_PROXY)) @@ -8322,7 +9323,11 @@ Process *schedule(Process *p, int calls) if (state & ERTS_PSFLG_RUNNING_SYS) { reds -= execute_sys_tasks(p, &state, reds); - if (reds <= 0) { + if (reds <= 0 +#ifdef ERTS_DIRTY_SCHEDULERS + || (state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) +#endif + ) { p->fcalls = reds; goto sched_out_proc; } @@ -10087,7 +11092,7 @@ save_pending_exiter(Process *p) erts_smp_runq_unlock(rq); #ifdef ERTS_DIRTY_SCHEDULERS if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) - wake_dirty_scheduler(rq); + wake_dirty_schedulers(rq, 0); else #endif wake_scheduler(rq); @@ -11148,6 +12153,10 @@ void erl_halt(int code) if (-1 == erts_smp_atomic32_cmpxchg_acqb(&erts_halt_progress, erts_no_schedulers, -1)) { +#ifdef ERTS_DIRTY_SCHEDULERS + ERTS_DIRTY_CPU_RUNQ->halt_in_progress = 1; + ERTS_DIRTY_IO_RUNQ->halt_in_progress = 1; +#endif erts_halt_code = code; notify_reap_ports_relb(); } diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index dcb9251d0d..f6b185c0ad 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -109,7 +109,6 @@ extern int erts_sched_balance_util; extern Uint erts_no_schedulers; #ifdef ERTS_DIRTY_SCHEDULERS extern Uint erts_no_dirty_cpu_schedulers; -extern Uint erts_no_dirty_cpu_schedulers_online; extern Uint erts_no_dirty_io_schedulers; #endif extern Uint erts_no_run_queues; @@ -544,6 +543,21 @@ typedef struct { #endif } ErtsAuxWorkData; +#ifdef ERTS_DIRTY_SCHEDULERS +typedef enum { + ERTS_DIRTY_CPU_SCHEDULER, + ERTS_DIRTY_IO_SCHEDULER +} ErtsDirtySchedulerType; + +typedef union { + struct { + ErtsDirtySchedulerType type: 1; + unsigned num: 31; + } s; + Uint no; +} ErtsDirtySchedId; +#endif + struct ErtsSchedulerData_ { /* * Keep X registers first (so we get as many low @@ -570,7 +584,7 @@ struct ErtsSchedulerData_ { Process *current_process; Uint no; /* Scheduler number for normal schedulers */ #ifdef ERTS_DIRTY_SCHEDULERS - Uint dirty_no; /* Scheduler number for dirty schedulers */ + ErtsDirtySchedId dirty_no; /* Scheduler number for dirty schedulers */ #endif Port *current_port; ErtsRunQueue *run_queue; @@ -1292,6 +1306,8 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; &erts_aligned_run_queues[(IX)].runq) #define ERTS_DIRTY_CPU_RUNQ (&erts_aligned_run_queues[-1].runq) #define ERTS_DIRTY_IO_RUNQ (&erts_aligned_run_queues[-2].runq) +#define ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(RQ) ((RQ)->ix == -1) +#define ERTS_RUNQ_IS_DIRTY_IO_RUNQ(RQ) ((RQ)->ix == -2) #else #define ERTS_RUNQ_IX_IS_DIRTY(IX) 0 #endif @@ -1305,21 +1321,37 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; #define ERTS_DIRTY_IO_SCHEDULER_IX(IX) \ (ASSERT(0 <= (IX) && (IX) < erts_no_dirty_io_schedulers), \ &erts_aligned_dirty_io_scheduler_data[(IX)].esd) +#define ERTS_DIRTY_SCHEDULER_NO(ESDP) \ + ((ESDP)->dirty_no.s.num) +#define ERTS_DIRTY_SCHEDULER_TYPE(ESDP) \ + ((ESDP)->dirty_no.s.type) #ifdef ERTS_SMP #define ERTS_SCHEDULER_IS_DIRTY(ESDP) \ - ((ESDP)->dirty_no != 0) + ((ESDP)->dirty_no.s.num != 0) +#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) \ + ((ESDP)->dirty_no.s.type == 0) +#define ERTS_SCHEDULER_IS_DIRTY_IO(ESDP) \ + ((ESDP)->dirty_no.s.type == 1) #else #define ERTS_SCHEDULER_IS_DIRTY(ESDP) 0 +#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) 0 +#define ERTS_SCHEDULER_IS_DIRTY_IO(ESDP) 0 #endif #else #define ERTS_RUNQ_IX_IS_DIRTY(IX) 0 #define ERTS_SCHEDULER_IS_DIRTY(ESDP) 0 +#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) 0 +#define ERTS_SCHEDULER_IS_DIRTY_IO(ESDP) 0 #endif void erts_pre_init_process(void); void erts_late_init_process(void); void erts_early_init_scheduling(int); -void erts_init_scheduling(int, int); +void erts_init_scheduling(int, int +#ifdef ERTS_DIRTY_SCHEDULERS + , int, int, int +#endif + ); int erts_set_gc_state(Process *c_p, int enable); Eterm erts_sched_wall_time_request(Process *c_p, int set, int enable); @@ -1526,7 +1558,11 @@ ErtsSchedSuspendResult erts_set_schedulers_online(Process *p, ErtsProcLocks plocks, Sint new_no, - Sint *old_no); + Sint *old_no +#ifdef ERTS_DIRTY_SCHEDULERS + , int dirty_only +#endif + ); ErtsSchedSuspendResult erts_block_multi_scheduling(Process *, ErtsProcLocks, int, int); int erts_is_multi_scheduling_blocked(void); diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 6a43e2b0e7..3906471f87 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -54,6 +54,7 @@ sbt_cmd/1, scheduler_threads/1, scheduler_suspend/1, + dirty_scheduler_threads/1, reader_groups/1]). -define(DEFAULT_TIMEOUT, ?t:minutes(15)). @@ -68,6 +69,7 @@ all() -> equal_and_high_with_part_time_max, equal_with_high, equal_with_high_max, bound_process, {group, scheduler_bind}, scheduler_threads, scheduler_suspend, + dirty_scheduler_threads, reader_groups]. groups() -> @@ -1092,6 +1094,55 @@ scheduler_threads(Config) when is_list(Config) -> end, ok. +dirty_scheduler_threads(Config) when is_list(Config) -> + SmpSupport = erlang:system_info(smp_support), + try + erlang:system_info(dirty_cpu_schedulers), + dirty_scheduler_threads_test(Config, SmpSupport) + catch + error:badarg -> + {skipped, "No dirty scheduler support"} + end. + +dirty_scheduler_threads_test(Config, SmpSupport) -> + {Sched, SchedOnln, _} = get_dsstate(Config, ""), + {HalfSched, HalfSchedOnln} = case SmpSupport of + false -> {1,1}; + true -> + {Sched div 2, + SchedOnln div 2} + end, + Cmd1 = "+SDcpu "++integer_to_list(HalfSched)++":"++ + integer_to_list(HalfSchedOnln), + {HalfSched, HalfSchedOnln, _} = get_dsstate(Config, Cmd1), + {HalfSched, HalfSchedOnln, _} = get_dsstate(Config, "+SDPcpu 50:50"), + IOSched = 20, + {_, _, IOSched} = get_dsstate(Config, "+SDio "++integer_to_list(IOSched)), + {ok, Node} = start_node(Config, ""), + [ok] = mcall(Node, [fun() -> dirty_schedulers_online_test() end]), + ok. + +dirty_schedulers_online_test() -> + dirty_schedulers_online_test(erlang:system_info(smp_support)). +dirty_schedulers_online_test(false) -> ok; +dirty_schedulers_online_test(true) -> + dirty_schedulers_online_smp_test(erlang:system_info(schedulers_online)). +dirty_schedulers_online_smp_test(SchedOnln) when SchedOnln < 4 -> ok; +dirty_schedulers_online_smp_test(SchedOnln) -> + DirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), + SchedOnln = DirtyCPUSchedOnln, + HalfSchedOnln = SchedOnln div 2, + SchedOnln = erlang:system_flag(schedulers_online, HalfSchedOnln), + HalfDirtyCPUSchedOnln = DirtyCPUSchedOnln div 2, + HalfDirtyCPUSchedOnln = erlang:system_flag(schedulers_online, SchedOnln), + DirtyCPUSchedOnln = erlang:system_flag(dirty_cpu_schedulers_online, + HalfDirtyCPUSchedOnln), + HalfDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), + QrtrDirtyCPUSchedOnln = HalfDirtyCPUSchedOnln div 2, + SchedOnln = erlang:system_flag(schedulers_online, HalfSchedOnln), + QrtrDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), + ok. + get_sstate(Config, Cmd) -> {ok, Node} = start_node(Config, Cmd), [SState] = mcall(Node, [fun () -> @@ -1100,6 +1151,20 @@ get_sstate(Config, Cmd) -> stop_node(Node), SState. +get_dsstate(Config, Cmd) -> + {ok, Node} = start_node(Config, Cmd), + [DSCPU] = mcall(Node, [fun () -> + erlang:system_info(dirty_cpu_schedulers) + end]), + [DSCPUOnln] = mcall(Node, [fun () -> + erlang:system_info(dirty_cpu_schedulers_online) + end]), + [DSIO] = mcall(Node, [fun () -> + erlang:system_info(dirty_io_schedulers) + end]), + stop_node(Node), + {DSCPU, DSCPUOnln, DSIO}. + scheduler_suspend(Config) when is_list(Config) -> ?line Dog = ?t:timetrap(?t:minutes(5)), ?line lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end, @@ -1171,16 +1236,40 @@ sst2_loop(N) -> erlang:system_flag(multi_scheduling, unblock), sst2_loop(N-1). -sst3_loop(_S, 0) -> - ok; sst3_loop(S, N) -> + try erlang:system_info(dirty_cpu_schedulers) of + DS -> + sst3_loop_with_dirty_schedulers(S, DS, N) + catch + error:badarg -> + sst3_loop_normal_schedulers_only(S, N) + end. + +sst3_loop_normal_schedulers_only(_S, 0) -> + ok; +sst3_loop_normal_schedulers_only(S, N) -> + erlang:system_flag(schedulers_online, (S div 2)+1), + erlang:system_flag(schedulers_online, 1), + erlang:system_flag(schedulers_online, (S div 2)+1), + erlang:system_flag(schedulers_online, S), + erlang:system_flag(schedulers_online, 1), + erlang:system_flag(schedulers_online, S), + sst3_loop_normal_schedulers_only(S, N-1). + +sst3_loop_with_dirty_schedulers(_S, _DS, 0) -> + ok; +sst3_loop_with_dirty_schedulers(S, DS, N) -> erlang:system_flag(schedulers_online, (S div 2)+1), + erlang:system_flag(dirty_cpu_schedulers_online, (DS div 2)+1), erlang:system_flag(schedulers_online, 1), erlang:system_flag(schedulers_online, (S div 2)+1), + erlang:system_flag(dirty_cpu_schedulers_online, 1), erlang:system_flag(schedulers_online, S), + erlang:system_flag(dirty_cpu_schedulers_online, DS), erlang:system_flag(schedulers_online, 1), erlang:system_flag(schedulers_online, S), - sst3_loop(S, N-1). + erlang:system_flag(dirty_cpu_schedulers_online, DS), + sst3_loop_with_dirty_schedulers(S, DS, N-1). reader_groups(Config) when is_list(Config) -> %% White box testing. These results are correct, but other results -- cgit v1.2.3 From c7ddafbe6dbcc805a9758c4313f2d6b902983343 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 21 Feb 2014 09:29:40 +0100 Subject: Always default to disabled floating point exceptions on Linux This since there exist unresolved stability issues in the implementation for Linux. --- erts/configure.in | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 8d245252b5..0933b124df 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2767,6 +2767,11 @@ esac if test X${enable_fp_exceptions} = Xauto ; then case $host_os in + *linux*) + enable_fp_exceptions=no + AC_MSG_NOTICE([Floating point exceptions disabled by default on Linux]) ;; + *) + ;; darwin*) enable_fp_exceptions=no AC_MSG_NOTICE([Floating point exceptions disabled by default on MacOS X]) ;; -- cgit v1.2.3 From 200fbe924466720bd2a8c5eb05b05d67b0a2414c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 14 Mar 2013 15:42:19 +0100 Subject: Added support for ENEA OSE This port has support for both non-smp and smp. It contains a new way to do io checking in which erts_poll_wait receives the payload of the polled entity. This has implications for all linked-in drivers. --- erts/aclocal.m4 | 41 +- erts/configure.in | 38 +- erts/emulator/Makefile.in | 44 +- erts/emulator/beam/beam_bif_load.c | 2 +- erts/emulator/beam/beam_emu.c | 5 + erts/emulator/beam/beam_load.c | 2 +- erts/emulator/beam/bif.c | 8 +- erts/emulator/beam/erl_alloc.types | 15 + erts/emulator/beam/erl_async.c | 7 + erts/emulator/beam/erl_bif_lists.c | 6 +- erts/emulator/beam/erl_bif_port.c | 5 +- erts/emulator/beam/erl_cpu_topology.c | 2 +- erts/emulator/beam/erl_driver.h | 18 + erts/emulator/beam/erl_drv_thread.c | 3 +- erts/emulator/beam/erl_nif.c | 2 +- erts/emulator/beam/erl_port_task.c | 3 +- erts/emulator/beam/erl_process.c | 82 +- erts/emulator/beam/erl_process_dict.c | 2 +- erts/emulator/beam/erl_process_lock.c | 2 +- erts/emulator/beam/erl_trace.c | 9 + erts/emulator/beam/erl_utils.h | 2 +- erts/emulator/beam/global.h | 3 + erts/emulator/beam/io.c | 7 + erts/emulator/beam/sys.h | 2 + erts/emulator/beam/utils.c | 2 +- erts/emulator/drivers/common/efile_drv.c | 3 + erts/emulator/drivers/common/inet_drv.c | 110 +- erts/emulator/drivers/ose/ose_efile.c | 1090 +++++++++++++++++ erts/emulator/sys/common/erl_check_io.c | 115 +- erts/emulator/sys/common/erl_poll.h | 10 +- erts/emulator/sys/ose/default.lmconf | 10 + erts/emulator/sys/ose/erl_main.c | 70 ++ erts/emulator/sys/ose/erl_ose_sys.h | 238 ++++ erts/emulator/sys/ose/erl_ose_sys_ddll.c | 197 +++ erts/emulator/sys/ose/erl_poll.c | 748 ++++++++++++ erts/emulator/sys/ose/gcc_lm.lcf | 146 +++ erts/emulator/sys/ose/gcc_lm_ppc.lcf | 219 ++++ erts/emulator/sys/ose/gcc_lm_x86_4.4.3.lcf | 277 +++++ erts/emulator/sys/ose/gcc_lm_x86_4.6.3.lcf | 252 ++++ erts/emulator/sys/ose/sys.c | 1820 ++++++++++++++++++++++++++++ erts/emulator/sys/ose/sys_float.c | 844 +++++++++++++ erts/emulator/sys/ose/sys_time.c | 56 + erts/epmd/src/Makefile.in | 24 + erts/epmd/src/epmd.c | 2 +- erts/epmd/src/epmd_int.h | 18 +- erts/epmd/src/epmd_srv.c | 7 +- erts/etc/common/Makefile.in | 42 +- erts/etc/common/inet_gethost.c | 2 +- erts/etc/ose/run_erl.c | 34 + erts/etc/ose/to_erl.c | 34 + erts/etc/unix/etp-commands.in | 18 + erts/include/internal/ethr_mutex.h | 22 + erts/include/internal/ethread.h | 97 +- erts/include/internal/ose/ethr_event.h | 104 ++ erts/lib_src/common/erl_misc_utils.c | 6 + erts/lib_src/common/ethr_aux.c | 15 + erts/lib_src/ose/ethr_event.c | 213 ++++ erts/lib_src/ose/ethread.c | 654 ++++++++++ erts/preloaded/ebin/erl_prim_loader.beam | Bin 54696 -> 54844 bytes erts/preloaded/src/erl_prim_loader.erl | 4 + 60 files changed, 7736 insertions(+), 77 deletions(-) create mode 100644 erts/emulator/drivers/ose/ose_efile.c create mode 100644 erts/emulator/sys/ose/default.lmconf create mode 100644 erts/emulator/sys/ose/erl_main.c create mode 100644 erts/emulator/sys/ose/erl_ose_sys.h create mode 100644 erts/emulator/sys/ose/erl_ose_sys_ddll.c create mode 100644 erts/emulator/sys/ose/erl_poll.c create mode 100644 erts/emulator/sys/ose/gcc_lm.lcf create mode 100644 erts/emulator/sys/ose/gcc_lm_ppc.lcf create mode 100644 erts/emulator/sys/ose/gcc_lm_x86_4.4.3.lcf create mode 100644 erts/emulator/sys/ose/gcc_lm_x86_4.6.3.lcf create mode 100644 erts/emulator/sys/ose/sys.c create mode 100644 erts/emulator/sys/ose/sys_float.c create mode 100644 erts/emulator/sys/ose/sys_time.c create mode 100644 erts/etc/ose/run_erl.c create mode 100644 erts/etc/ose/to_erl.c create mode 100644 erts/include/internal/ose/ethr_event.h create mode 100644 erts/lib_src/ose/ethr_event.c create mode 100644 erts/lib_src/ose/ethread.c (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 46b30a16b3..4a3407e0eb 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -74,6 +74,17 @@ AC_ARG_VAR(erl_xcomp_clock_gettime_cpu_time, [clock_gettime() can be used for re AC_ARG_VAR(erl_xcomp_after_morecore_hook, [__after_morecore_hook can track malloc()s core memory usage: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_dlsym_brk_wrappers, [dlsym(RTLD_NEXT, _) brk wrappers can track malloc()s core memory usage: yes|no (only used when cross compiling)]) +dnl Cross compilation variables for OSE +AC_ARG_VAR(erl_xcomp_ose_ldflags_pass1, [Linker flags for the OSE module (pass 1) (only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_ldflags_pass2, [Linker flags for the OSE module (pass 2) (only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_OSEROOT, [OSE installation root directory (only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_STRIP, [Strip utility shipped with the OSE distribution(only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_LM_CONF, [OSE load module default configuration file (only used when cross compiling for OSE)]) + ]) AC_DEFUN(ERL_XCOMP_SYSROOT_INIT, @@ -488,6 +499,8 @@ AC_CACHE_VAL(ac_cv_sys_ipv6_support, #ifdef __WIN32__ #include #include +#elif __OSE__ +#error "no ipv6" #else #include #endif], @@ -500,6 +513,8 @@ else #ifdef __WIN32__ #include #include +#elif __OSE__ +#error "no ipv6" #else #include #endif], @@ -728,6 +743,12 @@ if test "X$host_os" = "Xwin32"; then THR_LIBS= THR_LIB_NAME=win32_threads THR_LIB_TYPE=win32_threads +elif test "X$host_os" = "Xose"; then + AC_MSG_RESULT(yes) + THR_DEFS="-DOSE_THREADS" + THR_LIBS= + THR_LIB_NAME=ose_threads + THR_LIB_TYPE=ose_threads else AC_MSG_RESULT(no) THR_DEFS= @@ -1078,9 +1099,17 @@ case "$THR_LIB_NAME" in test "$ethr_have_native_atomics" = "yes" && ethr_have_native_spinlock=yes ;; - pthread) - ETHR_THR_LIB_BASE_DIR=pthread - AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads]) + pthread|ose_threads) + case "$THR_LIB_NAME" in + pthread) + ETHR_THR_LIB_BASE_DIR=pthread + AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads]) + ;; + ose_threads) + AC_DEFINE(ETHR_OSE_THREADS, 1, [Define if you have OSE style threads]) ETHR_THR_LIB_BASE_DIR=ose + ;; + esac + if test "x$THR_LIB_NAME" == "xpthread"; then case $host_os in openbsd*) # The default stack size is insufficient for our needs @@ -1139,6 +1168,7 @@ case "$THR_LIB_NAME" in *) ;; esac + fi dnl We sometimes need ETHR_DEFS in order to find certain headers dnl (at least for pthread.h on osf1). saved_cppflags="$CPPFLAGS" @@ -1151,7 +1181,6 @@ case "$THR_LIB_NAME" in dnl dnl Check for headers dnl - AC_CHECK_HEADER(pthread.h, \ AC_DEFINE(ETHR_HAVE_PTHREAD_H, 1, \ [Define if you have the header file.])) @@ -1184,7 +1213,7 @@ case "$THR_LIB_NAME" in dnl dnl Check for functions dnl - + if test "x$THR_LIB_NAME" == "xpthread"; then AC_CHECK_FUNC(pthread_spin_lock, \ [ethr_have_native_spinlock=yes \ AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \ @@ -1311,6 +1340,8 @@ case "$THR_LIB_NAME" in AC_MSG_RESULT([$linux_futex]) test $linux_futex = yes && AC_DEFINE(ETHR_HAVE_LINUX_FUTEX, 1, [Define if you have a linux futex implementation.]) + fi + AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) diff --git a/erts/configure.in b/erts/configure.in index 8d245252b5..7ad0a6caf0 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -905,7 +905,10 @@ dnl what the user say. This might not be the right way to do it, but dnl for now that is the way we do it. USER_LD=$LD USER_LDFLAGS="$LDFLAGS" -LD='$(CC)' +case $host in + *ose) ;; + *) LD='$(CC)' ;; +esac AC_SUBST(LD) LDFLAG_RUNTIME_LIBRARY_PATH="$CFLAG_RUNTIME_LIBRARY_PATH" @@ -916,12 +919,15 @@ dnl AC_CYGWIN is deprecated AC_EXEEXT AC_OBJEXT -dnl This is the os flavour, should be unix, vxworks or win32 -if test "X$host" = "Xwin32"; then - ERLANG_OSTYPE=win32 -else - ERLANG_OSTYPE=unix -fi +dnl This is the os flavour, should be unix, ose, vxworks or win32 +case $host in + win32) + ERLANG_OSTYPE=win32 ;; + *ose) + ERLANG_OSTYPE=ose ;; + *) + ERLANG_OSTYPE=unix ;; +esac AC_SUBST(ERLANG_OSTYPE) @@ -1206,7 +1212,7 @@ case "$enable_threads"-"$found_threads" in AC_MSG_RESULT(yes; enabled by user) ;; unknown-yes) case $host_os in - solaris*|linux*|darwin*|win32) + solaris*|linux*|darwin*|win32|ose) emu_threads=yes AC_MSG_RESULT(yes; default on this platform) ;; @@ -1286,7 +1292,7 @@ else enable_child_waiter_thread=no fi ;; - win32) + win32|ose) # Child waiter thread cannot be enabled disable_child_waiter_thread=yes enable_child_waiter_thread=no @@ -2018,7 +2024,7 @@ AC_CHECK_FUNCS([getipnodebyname getipnodebyaddr gethostbyname2]) AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlopen \ pread pwrite memmove strerror strerror_r strncasecmp \ gethrtime localtime_r gmtime_r inet_pton \ - mmap mremap memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \ + memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \ flockfile fstat strlcpy strlcat setsid posix2time time2posix \ setlocale nl_langinfo poll mlockall]) @@ -2059,6 +2065,17 @@ case $host_os in AC_CHECK_FUNCS([writev]) ;; esac +case $host_os in + *ose) + AC_MSG_CHECKING([for mmap]) + AC_MSG_RESULT(no) + AC_MSG_CHECKING([for mremap]) + AC_MSG_RESULT(no) ;; + *) + AC_CHECK_FUNCS([mmap mremap]) ;; +esac + + AC_CHECK_DECLS([posix2time, time2posix],,,[#include ]) disable_vfork=false @@ -4728,6 +4745,7 @@ AC_OUTPUT( Makefile:Makefile.in ../make/$host/otp.mk:../make/otp.mk.in ../make/$host/otp_ded.mk:../make/otp_ded.mk.in + ../make/$host/ose_lm.mk:../make/ose_lm.mk.in dnl dnl The ones below should be moved to their respective lib dnl diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index f88a4ccc24..299d524bd3 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -22,6 +22,9 @@ include ../vsn.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk -include $(TARGET)/gen_git_version.mk +ifeq ($(findstring ose,$(TARGET)),ose) +include $(ERL_TOP)/make/$(TARGET)/ose_lm.mk +endif ENABLE_ALLOC_TYPE_VARS = @ENABLE_ALLOC_TYPE_VARS@ HIPE_ENABLED=@HIPE_ENABLED@ @@ -234,7 +237,9 @@ HCC = @HCC@ LD = @LD@ DEXPORT = @DEXPORT@ RANLIB = @RANLIB@ +ifneq ($(findstring ose,$(TARGET)),ose) STRIP = strip +endif PERL = @PERL@ RM = @RM@ MKDIR = @MKDIR@ @@ -787,7 +792,8 @@ DRV_OBJS = \ $(OBJDIR)/efile_drv.o \ $(OBJDIR)/inet_drv.o \ $(OBJDIR)/zlib_drv.o \ - $(OBJDIR)/ram_file_drv.o + $(OBJDIR)/ram_file_drv.o \ + $(OBJDIR)/ttsl_drv.o OS_OBJS = \ $(OBJDIR)/win_efile.o \ $(OBJDIR)/win_con.o \ @@ -799,6 +805,28 @@ OS_OBJS = \ $(OBJDIR)/sys_interrupt.o \ $(OBJDIR)/sys_env.o \ $(OBJDIR)/dosmap.o + +else +ifeq ($(findstring ose,$(TARGET)),ose) +OS_OBJS = \ + $(OBJDIR)/sys.o \ + $(OBJDIR)/driver_tab.o \ + $(OBJDIR)/ose_efile.o \ + $(OBJDIR)/gzio.o \ + $(OBJDIR)/elib_memmove.o + +OS_OBJS += $(OSEROOT)/src/ose_confd.o \ + $(OSEROOT)/src/crt0_lm.o + +OS_OBJS += $(OBJDIR)/sys_float.o \ + $(OBJDIR)/sys_time.o + +DRV_OBJS = \ + $(OBJDIR)/efile_drv.o \ + $(OBJDIR)/inet_drv.o \ + $(OBJDIR)/zlib_drv.o \ + $(OBJDIR)/ram_file_drv.o + else OS_OBJS = \ $(OBJDIR)/sys.o \ @@ -813,10 +841,10 @@ DRV_OBJS = \ $(OBJDIR)/efile_drv.o \ $(OBJDIR)/inet_drv.o \ $(OBJDIR)/zlib_drv.o \ - $(OBJDIR)/ram_file_drv.o + $(OBJDIR)/ram_file_drv.o \ + $(OBJDIR)/ttsl_drv.o +endif endif - - DRV_OBJS += $(OBJDIR)/ttsl_drv.o ifneq ($(STATIC_NIFS),no) STATIC_NIF_LIBS = $(STATIC_NIFS) @@ -987,6 +1015,13 @@ $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(ld_verbose)$(PURIFY) $(LD) -dll -def:sys/$(ERLANG_OSTYPE)/erl.def -implib:$(BINDIR)/erl_dll.lib -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \ $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) $(STATIC_NIF_LIBS) \ $(STATIC_DRIVER_LIBS) $(LIBS) + +else +ifeq ($(findstring ose,$(TARGET)),ose) +$(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(LCF) + $(call build-ose-load-module, $@, $(INIT_OBJS) $(OBJS), $(STATIC_NIF_LIBS) \ + $(STATIC_DRIVER_LIBS) $(LIBS), $(LMCONF)) + else $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) echo $(DEPLIBS) @@ -995,6 +1030,7 @@ $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(STATIC_NIF_LIBS) $(STATIC_DRIVER_LIBS) $(LIBS) endif +endif # ---------------------------------------------------------------------- # Dependencies diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 3f92c5b025..df1983a83d 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -201,7 +201,7 @@ finish_loading_1(BIF_ALIST_1) * to keep the elements in. */ - n = list_length(BIF_ARG_1); + n = erts_list_length(BIF_ARG_1); if (n == -1) { ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG); goto done; diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 89d9442526..ceff43b24f 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1190,11 +1190,16 @@ void process_main(void) * c_p->arg_reg before calling the scheduler. */ if (!init_done) { + /* This should only be reached during the init phase when only the main + * process is running. I.e. there is no race for init_done. + */ init_done = 1; goto init_emulator; } + c_p = NULL; reds_used = 0; + goto do_schedule1; do_schedule: diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index b589d1c930..c983615827 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -5824,7 +5824,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) Funcs = tp[1]; Patchlist = tp[2]; - if ((n = list_length(Funcs)) < 0) { + if ((n = erts_list_length(Funcs)) < 0) { goto error; } if ((bytes = erts_get_aligned_binary_bytes(Beam, &temp_alloc)) == NULL) { diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 9c4801041f..1f568726a6 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2675,7 +2675,7 @@ BIF_RETTYPE list_to_atom_1(BIF_ALIST_1) if (i < 0) { erts_free(ERTS_ALC_T_TMP, (void *) buf); - i = list_length(BIF_ARG_1); + i = erts_list_length(BIF_ARG_1); if (i > MAX_ATOM_CHARACTERS) { BIF_ERROR(BIF_P, SYSTEM_LIMIT); } @@ -2953,7 +2953,7 @@ BIF_RETTYPE list_to_integer_2(BIF_ALIST_2) char *buf = NULL; int base; - i = list_length(BIF_ARG_1); + i = erts_list_length(BIF_ARG_1); if (i < 0) BIF_ERROR(BIF_P, BADARG); @@ -3292,7 +3292,7 @@ BIF_RETTYPE list_to_float_1(BIF_ALIST_1) Eterm res; char *buf = NULL; - i = list_length(BIF_ARG_1); + i = erts_list_length(BIF_ARG_1); if (i < 0) BIF_ERROR(BIF_P, BADARG); @@ -3407,7 +3407,7 @@ BIF_RETTYPE list_to_tuple_1(BIF_ALIST_1) Eterm* hp; int len; - if ((len = list_length(list)) < 0 || len > ERTS_MAX_TUPLE_SIZE) { + if ((len = erts_list_length(list)) < 0 || len > ERTS_MAX_TUPLE_SIZE) { BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index b4e52770e3..17ac6316b7 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -414,6 +414,21 @@ type PRT_REP_EXIT STANDARD SYSTEM port_report_exit +endif ++if ose + +type SYS_READ_BUF TEMPORARY SYSTEM sys_read_buf +type FD_TAB LONG_LIVED SYSTEM fd_tab +type FD_ENTRY_BUF STANDARD SYSTEM fd_entry_buf +type FD_SIG_LIST SHORT_LIVED SYSTEM fd_sig_list +type DRV_EV STANDARD SYSTEM driver_event +type CS_PROG_PATH LONG_LIVED SYSTEM cs_prog_path +type ENVIRONMENT TEMPORARY SYSTEM environment +type PUTENV_STR SYSTEM SYSTEM putenv_string +type PRT_REP_EXIT STANDARD SYSTEM port_report_exit + ++endif + + +if win32 type DRV_DATA_BUF SYSTEM SYSTEM drv_data_buf diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index f0cec1c53c..afb5bc302f 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -226,6 +226,13 @@ erts_init_async(void) thr_opts.suggested_stack_size = erts_async_thread_suggested_stack_size; +#ifdef ETHR_HAVE_THREAD_NICENESS + thr_opts.prio += 2; +#endif +#ifdef ETHR_HAVE_THREAD_NAMES + thr_opts.name = "async_thread"; +#endif + for (i = 0; i < erts_async_max_threads; i++) { ErtsAsyncQ *aq = async_q(i); erts_thr_create(&aq->thr_id, async_main, (void*) aq, &thr_opts); diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c index 1805366cfe..820ed2385d 100644 --- a/erts/emulator/beam/erl_bif_lists.c +++ b/erts/emulator/beam/erl_bif_lists.c @@ -43,7 +43,7 @@ static BIF_RETTYPE append(Process* p, Eterm A, Eterm B) Eterm* hp; int i; - if ((i = list_length(A)) < 0) { + if ((i = erts_list_length(A)) < 0) { BIF_ERROR(p, BADARG); } if (i == 0) { @@ -102,10 +102,10 @@ static Eterm subtract(Process* p, Eterm A, Eterm B) int n; int m; - if ((n = list_length(A)) < 0) { + if ((n = erts_list_length(A)) < 0) { BIF_ERROR(p, BADARG); } - if ((m = list_length(B)) < 0) { + if ((m = erts_list_length(B)) < 0) { BIF_ERROR(p, BADARG); } diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index f298422267..77627a6897 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -941,7 +941,8 @@ static char **convert_args(Eterm l) if (is_not_list(l) && is_not_nil(l)) { return NULL; } - n = list_length(l); + + n = erts_list_length(l); /* We require at least one element in argv[0] + NULL at end */ pp = erts_alloc(ERTS_ALC_T_TMP, (n + 2) * sizeof(char **)); pp[i++] = erts_default_arg0; @@ -986,7 +987,7 @@ static byte* convert_environment(Process* p, Eterm env) byte* bytes; int encoding = erts_get_native_filename_encoding(); - if ((n = list_length(env)) < 0) { + if ((n = erts_list_length(env)) < 0) { return NULL; } heap_size = 2*(5*n+1); diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c index 88c6c34881..f594cb9392 100644 --- a/erts/emulator/beam/erl_cpu_topology.c +++ b/erts/emulator/beam/erl_cpu_topology.c @@ -1699,7 +1699,7 @@ erts_early_init_cpu_topology(int no_schedulers, } max_main_threads = erts_get_cpu_configured(cpuinfo); - if (max_main_threads > no_schedulers) + if (max_main_threads > no_schedulers || max_main_threads < 0) max_main_threads = no_schedulers; *max_main_threads_p = max_main_threads; diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index ab9ee63104..9ede2982de 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -271,6 +271,13 @@ typedef struct ErlDrvCond_ ErlDrvCond; typedef struct ErlDrvRWLock_ ErlDrvRWLock; typedef int ErlDrvTSDKey; +/* + * Potential OSE signals + */ +#ifdef __OSE__ +typedef union SIGNAL OseSignal; +#endif + /* * @@ -346,6 +353,9 @@ typedef struct erl_drv_entry { /* Called on behalf of driver_select when it is safe to release 'event'. A typical unix driver would call close(event) */ +#ifdef __OSE__ + int (*resolve_signal)(OseSignal* sig, int* mode); +#endif /* When adding entries here, dont forget to pad in obsolete/driver.h */ } ErlDrvEntry; @@ -680,6 +690,14 @@ EXTERN char *driver_dl_error(void); EXTERN int erl_drv_putenv(char *key, char *value); EXTERN int erl_drv_getenv(char *key, char *value, size_t *value_size); +#ifdef __OSE__ +EXTERN OseSignal *erl_drv_ose_get_output_signal(ErlDrvEvent ev); +EXTERN OseSignal *erl_drv_ose_get_input_signal(ErlDrvEvent ev); +EXTERN ErlDrvEvent erl_drv_ose_event_alloc(SIGSELECT sig,int id); +EXTERN void erl_drv_ose_event_free(ErlDrvEvent ev); +EXTERN void erl_drv_ose_event_fetch(ErlDrvEvent ev, SIGSELECT *sig,int *id); +#endif + #endif /* !ERL_DRIVER_TYPES_ONLY */ #ifdef WIN32_DYNAMIC_ERL_DRIVER diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index 4f1bba8657..beaeed03a2 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -78,8 +78,6 @@ struct ErlDrvTid_ { static ethr_tsd_key tid_key; -static ethr_thr_opts def_ethr_opts = ETHR_THR_OPTS_DEFAULT_INITER; - #else /* USE_THREADS */ static Uint tsd_len; static void **tsd; @@ -605,6 +603,7 @@ erl_drv_thread_create(char *name, struct ErlDrvTid_ *dtid; ethr_thr_opts ethr_opts; ethr_thr_opts *use_opts; + ethr_thr_opts def_ethr_opts = ETHR_THR_OPTS_DEFAULT_INITER; if (!opts) use_opts = NULL; diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c35f1fc2c6..d467d129e8 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -876,7 +876,7 @@ int enif_get_list_cell(ErlNifEnv* env, Eterm term, Eterm* head, Eterm* tail) int enif_get_list_length(ErlNifEnv* env, Eterm term, unsigned* len) { if (is_not_list(term) && is_not_nil(term)) return 0; - *len = list_length(term); + *len = erts_list_length(term); return 1; } diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index d4108067d0..fb6048b41f 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -1681,7 +1681,8 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) reds = ERTS_PORT_REDS_INPUT; ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0); DTRACE_DRIVER(driver_ready_input, pp); - /* NOTE some windows drivers use ->ready_input for input and output */ + /* NOTE some windows/ose drivers use ->ready_input + for input and output */ (*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data, ptp->u.alive.td.io.event); io_tasks_executed++; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 937881212a..7e5d88cb24 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -44,6 +44,7 @@ #include "dtrace-wrapper.h" #include "erl_ptab.h" + #define ERTS_DELAYED_WAKEUP_INFINITY (~(Uint64) 0) #define ERTS_DELAYED_WAKEUP_REDUCTIONS ((Uint64) CONTEXT_REDS/2) @@ -53,7 +54,11 @@ #define ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST (CONTEXT_REDS/10) +#ifndef ERTS_SCHED_MIN_SPIN #define ERTS_SCHED_SPIN_UNTIL_YIELD 100 +#else +#define ERTS_SCHED_SPIN_UNTIL_YIELD 1 +#endif #define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_LONG 40 #define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_VERY_LONG 1000 @@ -2682,7 +2687,11 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * be waiting in erl_sys_schedule() */ +#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 + if (esdp->no != 1) { +#else if (ERTS_SCHEDULER_IS_DIRTY(esdp) || !prepare_for_sys_schedule()) { +#endif sched_waiting(esdp->no, rq); @@ -2690,7 +2699,11 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) spincount = sched_busy_wait.tse; +#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 + ASSERT(esdp->no != 1); +#else tse_wait: +#endif if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && thr_prgr_active != working) sched_wall_time_change(esdp, thr_prgr_active); @@ -2782,6 +2795,12 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ASSERT(working); sched_wall_time_change(esdp, working = 0); +#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 + sys_poll_try: + while(!prepare_for_sys_schedule()) { + delay(1); + } +#endif spincount = sched_busy_wait.sys_schedule; if (spincount == 0) @@ -2795,7 +2814,6 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) sched_wall_time_change(esdp, working = 0); ASSERT(!erts_port_task_have_outstanding_io_tasks()); - erl_sys_schedule(1); /* Might give us something to do */ dt = erts_do_time_read_and_reset(); @@ -2843,7 +2861,11 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) */ if (!prepare_for_sys_schedule()) { spincount *= ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT; +#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 + goto sys_poll_try; +#else goto tse_wait; +#endif } } #endif @@ -2871,7 +2893,11 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) sched_change_waiting_sys_to_waiting(esdp->no, rq); erts_smp_runq_unlock(rq); spincount = 0; +#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 + goto sys_poll_try; +#else goto tse_wait; +#endif } } #endif @@ -4889,11 +4915,17 @@ erts_early_init_scheduling(int no_schedulers) wakeup_other.threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM; wakeup_other.type = ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT; #endif +#ifndef ERTS_SCHED_MIN_SPIN sched_busy_wait.sys_schedule = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM; sched_busy_wait.tse = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM * ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT); sched_busy_wait.aux_work = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM * ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_MEDIUM); +#else + sched_busy_wait.sys_schedule = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE; + sched_busy_wait.tse = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE; + sched_busy_wait.aux_work = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE; +#endif } int @@ -6795,7 +6827,7 @@ void erts_start_schedulers(void) { int res = 0; - Uint actual = 0; + Uint actual; Uint wanted = erts_no_schedulers; Uint wanted_no_schedulers = erts_no_schedulers; ethr_thr_opts opts = ETHR_THR_OPTS_DEFAULT_INITER; @@ -6826,15 +6858,31 @@ erts_start_schedulers(void) res = ENOTSUP; } - while (actual < wanted) { +#ifdef ETHR_HAVE_THREAD_NAMES + opts.name = malloc(sizeof(char)*(strlen("scheduler_XXXX")+1)); +#endif + + for (actual = 0; actual < wanted; actual++) { ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(actual); - actual++; - ASSERT(actual == esdp->no); - res = ethr_thr_create(&esdp->tid,sched_thread_func,(void*)esdp,&opts); + + ASSERT(actual == esdp->no - 1); + +#ifdef ETHR_HAVE_THREAD_NAMES + sprintf(opts.name,"scheduler_%d", actual+1); +#endif + +#ifdef __OSE__ + /* This should be done in the bind strategy */ + opts.coreNo = (actual+1) % ose_num_cpus(); +#endif + + res = ethr_thr_create(&esdp->tid, sched_thread_func,(void*)esdp,&opts); + if (res != 0) { - actual--; - break; + //actual--; + break; } + } erts_no_schedulers = actual; @@ -6860,6 +6908,16 @@ erts_start_schedulers(void) #endif ERTS_THR_MEMORY_BARRIER; +#ifdef ETHR_HAVE_THREAD_NAMES + free(opts.name); + opts.name = "aux_thread"; +#endif +#ifdef __OSE__ + opts.coreNo = 0; +#endif +#ifdef ETHR_HAVE_THREAD_NICENESS + opts.prio++; +#endif res = ethr_thr_create(&aux_tid, aux_thread, NULL, &opts); if (res != 0) @@ -8076,7 +8134,11 @@ Process *schedule(Process *p, int calls) goto check_activities_to_run; } - else if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && + else if ( +#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 + esdp->no == 1 && +#endif + !ERTS_SCHEDULER_IS_DIRTY(esdp) && (fcalls > input_reductions && prepare_for_sys_schedule())) { /* * Schedule system-level activities. @@ -9379,7 +9441,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). * Check for errors. */ - if (is_not_atom(mod) || is_not_atom(func) || ((arity = list_length(args)) < 0)) { + if (is_not_atom(mod) || is_not_atom(func) || ((arity = erts_list_length(args)) < 0)) { so->error_code = BADARG; goto error; } diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index a611b52af2..23e5bf737f 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -659,7 +659,7 @@ static void shrink(Process *p, Eterm* ret) } else { int needed = 4; if (is_list(hi) && is_list(lo)) { - needed = 2*list_length(hi); + needed = 2*erts_list_length(hi); } if (HeapWordsLeft(p) < needed) { BUMP_REDS(p, erts_garbage_collect(p, needed, ret, 1)); diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index 2db5df06b4..bb17593297 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -1001,8 +1001,8 @@ erts_pid2proc_opt(Process *c_p, void erts_proc_lock_init(Process *p) { -#if ERTS_PROC_LOCK_OWN_IMPL int i; +#if ERTS_PROC_LOCK_OWN_IMPL /* We always start with all locks locked */ #if ERTS_PROC_LOCK_ATOMIC_IMPL erts_smp_atomic32_init_nob(&p->lock.flags, diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index ff7fdfcfca..62a0b8ccea 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -3467,12 +3467,21 @@ static void init_sys_msg_dispatcher(void) { erts_smp_thr_opts_t thr_opts = ERTS_SMP_THR_OPTS_DEFAULT_INITER; +#ifdef __OSE__ + thr_opts.coreNo = 0; +#endif thr_opts.detached = 1; init_smq_element_alloc(); sys_message_queue = NULL; sys_message_queue_end = NULL; erts_smp_cnd_init(&smq_cnd); erts_smp_mtx_init(&smq_mtx, "sys_msg_q"); +#ifdef ETHR_HAVE_THREAD_NAMES + thr_opts.name = "sys_msg_dispatcher"; +#endif +#ifdef ETHR_HAVE_THREAD_NICENESS + thr_opts.prio++; +#endif erts_smp_thr_create(&sys_msg_dispatcher_tid, sys_msg_dispatcher_func, NULL, diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 5b81d814c6..4e61429cd1 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -150,7 +150,7 @@ void erts_silence_warn_unused_result(long unused); int erts_fit_in_bits_int64(Sint64); int erts_fit_in_bits_int32(Sint32); -int list_length(Eterm); +int erts_list_length(Eterm); int erts_is_builtin(Eterm, Eterm, int); Uint32 make_broken_hash(Eterm); Uint32 block_hash(byte *, unsigned, Uint32); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 8fcb95d0e2..5a97ac5892 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -160,6 +160,9 @@ struct erts_driver_t_ { void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data); /* Might be NULL */ void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor); void (*stop_select)(ErlDrvEvent event, void*); /* Might be NULL */ +#ifdef __OSE__ + int (*resolve_signal)(OseSignal* sig, int* mode); +#endif }; extern erts_driver_t *driver_list; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 3b16cdeb4a..f4d905552d 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -49,7 +49,9 @@ #include "erl_map.h" extern ErlDrvEntry fd_driver_entry; +#ifndef __OSE__ extern ErlDrvEntry vanilla_driver_entry; +#endif extern ErlDrvEntry spawn_driver_entry; extern ErlDrvEntry *driver_tab[]; /* table of static drivers, only used during initialization */ @@ -2764,7 +2766,9 @@ void erts_init_io(int port_tab_size, erts_smp_rwmtx_rwlock(&erts_driver_list_lock); init_driver(&fd_driver, &fd_driver_entry, NULL); +#ifndef __OSE__ init_driver(&vanilla_driver, &vanilla_driver_entry, NULL); +#endif init_driver(&spawn_driver, &spawn_driver_entry, NULL); erts_init_static_drivers(); for (dp = driver_tab; *dp != NULL; dp++) @@ -7333,6 +7337,9 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle) drv->stop_select = de->stop_select; else drv->stop_select = no_stop_select_callback; +#ifdef __OSE__ + drv->resolve_signal = de->resolve_signal; +#endif if (!de->init) return 0; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 189d9ebac8..e273056a2b 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -38,6 +38,8 @@ #if defined (__WIN32__) # include "erl_win_sys.h" +#elif defined (__OSE__) +# include "erl_ose_sys.h" #else # include "erl_unix_sys.h" #ifndef UNIX diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index bc4a05d385..d92909c673 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -258,7 +258,7 @@ erl_grow_wstack(ErtsWStack* s, UWord* default_wstack) * Returns -1 if not a proper list (i.e. not terminated with NIL) */ int -list_length(Eterm list) +erts_list_length(Eterm list) { int i = 0; diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index e040864d24..fbd72c6c1b 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -373,6 +373,9 @@ struct erl_drv_entry efile_driver_entry = { #else NULL #endif /* HAVE_SENDFILE */ +#ifdef __OSE__ + ,NULL +#endif }; diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 4a861b121c..357a4b7bcb 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -121,6 +121,10 @@ typedef unsigned long long llu_t; #undef WANT_NONBLOCKING #include "sys.h" +#ifdef __OSE__ +#include "inet.h" +#endif + #undef EWOULDBLOCK #undef ETIMEDOUT @@ -289,7 +293,111 @@ static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD); static unsigned long zero_value = 0; static unsigned long one_value = 1; -#else /* #ifdef __WIN32__ */ +#elif defined (__OSE__) +#include "sys/socket.h" +#include "sys/uio.h" +#include "sfk/sys/sfk_uio.h" +#include "netinet/in.h" +#include "netinet/tcp.h" +#include "netdb.h" + +ssize_t writev(int fd, const struct iovec *iov, int iovcnt) +{ + return 0; +} + +#define INVALID_SOCKET -1 +#define INVALID_EVENT -1 +#define SOCKET_ERROR -1 + +#define SOCKET int +#define HANDLE long int +#define FD_READ ERL_DRV_READ +#define FD_WRITE ERL_DRV_WRITE +#define FD_CLOSE 0 +#define FD_CONNECT ERL_DRV_WRITE +#define FD_ACCEPT ERL_DRV_READ + +#define sock_connect(s, addr, len) connect((s), (addr), (len)) +#define sock_listen(s, b) listen((s), (b)) +#define sock_bind(s, addr, len) bind((s), (addr), (len)) +#define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) +#define sock_setopt(s,t,n,v,l) setsockopt((s),(t),(n),(v),(l)) +#define sock_name(s, addr, len) getsockname((s), (addr), (len)) +#define sock_peer(s, addr, len) getpeername((s), (addr), (len)) +#define sock_ntohs(x) ntohs((x)) +#define sock_ntohl(x) ntohl((x)) +#define sock_htons(x) htons((x)) +#define sock_htonl(x) htonl((x)) + +#define sock_accept(s, addr, len) accept((s), (addr), (len)) +#define sock_send(s,buf,len,flag) inet_send((s),(buf),(len),(flag)) +#define sock_sendto(s,buf,blen,flag,addr,alen) \ + sendto((s),(buf),(blen),(flag),(addr),(alen)) +#define sock_sendv(s, vec, size, np, flag) \ + (*(np) = writev((s), (struct iovec*)(vec), (size))) +#define sock_sendmsg(s,msghdr,flag) sendmsg((s),(msghdr),(flag)) + +#define sock_open(af, type, proto) socket((af), (type), (proto)) +#define sock_close(s) close((s)) +#define sock_shutdown(s, how) shutdown((s), (how)) + +#define sock_hostname(buf, len) gethostname((buf), (len)) +#define sock_getservbyname(name,proto) getservbyname((name), (proto)) +#define sock_getservbyport(port,proto) getservbyport((port), (proto)) + +#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag)) +#define sock_recvfrom(s,buf,blen,flag,addr,alen) \ + recvfrom((s),(buf),(blen),(flag),(addr),(alen)) +#define sock_recvmsg(s,msghdr,flag) recvmsg((s),(msghdr),(flag)) + +#define sock_errno() errno +#define sock_create_event(d) ((d)->s) /* return file descriptor */ +#define sock_close_event(e) /* do nothing */ + +#define inet_driver_select(port, e, mode, on) \ + driver_select(port, e, mode | (on?ERL_DRV_USE:0), on) + +#define sock_select(d, flags, onoff) do { \ + ASSERT(!(d)->is_ignored); \ + (d)->event_mask = (onoff) ? \ + ((d)->event_mask | (flags)) : \ + ((d)->event_mask & ~(flags)); \ + DEBUGF(("sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX\r\n", \ + (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask)); \ + inet_driver_select((d)->port, (ErlDrvEvent)(long)(d)->event, (flags), (onoff)); \ + } while(0) + +#ifndef WANT_NONBLOCKING +#define WANT_NONBLOCKING +#endif +#include "sys.h" + +typedef unsigned long u_long; +#define IN_CLASSA(a) ((((in_addr_t)(a)) & 0x80000000) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) +#define IN_CLASSA_MAX 128 + +#define IN_CLASSB(a) ((((in_addr_t)(a)) & 0xc0000000) == 0x80000000) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) +#define IN_CLASSB_MAX 65536 + +#define IN_CLASSC(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xc0000000) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) + +#define IN_CLASSD(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xe0000000) +#define IN_MULTICAST(a) IN_CLASSD(a) + +#define IN_EXPERIMENTAL(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xe0000000) +#define IN_BADCLASS(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xf0000000) + +#else /* !__OSE__ && !__WIN32__ */ #include #ifdef NETDB_H_NEEDS_IN_H diff --git a/erts/emulator/drivers/ose/ose_efile.c b/erts/emulator/drivers/ose/ose_efile.c new file mode 100644 index 0000000000..8cd34a7bb0 --- /dev/null +++ b/erts/emulator/drivers/ose/ose_efile.c @@ -0,0 +1,1090 @@ +/* + * %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% + */ +/* + * Purpose: Provides file and directory operations for OSE. + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if defined(HAVE_POSIX_FALLOCATE) && !defined(__sun) && !defined(__sun__) +#define _XOPEN_SOURCE 600 +#endif +#if !defined(_GNU_SOURCE) && defined(HAVE_LINUX_FALLOC_H) +#define _GNU_SOURCE +#endif +#include "sys.h" +#include "erl_driver.h" +#include "erl_efile.h" +/*#include */ +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_UIO_H +#include +#include +#endif +#if defined(HAVE_SENDFILE) && (defined(__linux__) || (defined(__sun) && defined(__SVR4))) +#include +#endif + +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +#define DARWIN 1 +#endif + +#if defined(DARWIN) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE) +#include +#endif + +#ifdef HAVE_LINUX_FALLOC_H +#include +#endif + +#ifdef SUNOS4 +# define getcwd(buf, size) getwd(buf) +#endif + +#ifdef __OSE__ +#include "sys/stat.h" +#include "dirent.h" +#endif + +/* Find a definition of MAXIOV, that is used in the code later. */ +#if defined IOV_MAX +#define MAXIOV IOV_MAX +#elif defined UIO_MAXIOV +#define MAXIOV UIO_MAXIOV +#else +#define MAXIOV 16 +#endif + + +/* + * Macros for testing file types. + */ + +#define ISDIR(st) (((st).st_mode & S_IFMT) == S_IFDIR) +#define ISREG(st) (((st).st_mode & S_IFMT) == S_IFREG) +#define ISDEV(st) \ + (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) +#define ISLNK(st) (((st).st_mode & S_IFLNK) == S_IFLNK) +#ifdef NO_UMASK +#define FILE_MODE 0644 +#define DIR_MODE 0755 +#else +#define FILE_MODE 0666 +#define DIR_MODE 0777 +#endif + +#define IS_DOT_OR_DOTDOT(s) \ + (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0'))) + +static int check_error(int result, Efile_error* errInfo); + +static int +check_error(int result, Efile_error *errInfo) +{ + if (result < 0) { + errInfo->posix_errno = errInfo->os_errno = errno; + return 0; + } + return 1; +} + +int +efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of directory to create. */ +{ +#ifdef NO_MKDIR_MODE + return check_error(mkdir(name), errInfo); +#else + return check_error(mkdir(name, DIR_MODE), errInfo); +#endif +} + +int +efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of directory to delete. */ +{ + if (rmdir(name) == 0) { + return 1; + } + if (errno == ENOTEMPTY) { + errno = EEXIST; + } + if (errno == EEXIST) { + int saved_errno = errno; + struct stat file_stat; + struct stat cwd_stat; + + /* + * The error code might be wrong if this is the current directory. + */ + + if (stat(name, &file_stat) == 0 && stat(".", &cwd_stat) == 0 && + file_stat.st_ino == cwd_stat.st_ino && + file_stat.st_dev == cwd_stat.st_dev) { + saved_errno = EINVAL; + } + errno = saved_errno; + } + return check_error(-1, errInfo); +} + +int +efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of file to delete. */ +{ + if (unlink(name) == 0) { + return 1; + } + if (errno == EISDIR) { /* Linux sets the wrong error code. */ + errno = EPERM; + } + return check_error(-1, errInfo); +} + +/* + *--------------------------------------------------------------------------- + * + * Changes the name of an existing file or directory, from src to dst. + * If src and dst refer to the same file or directory, does nothing + * and returns success. Otherwise if dst already exists, it will be + * deleted and replaced by src subject to the following conditions: + * If src is a directory, dst may be an empty directory. + * If src is a file, dst may be a file. + * In any other situation where dst already exists, the rename will + * fail. + * + * Results: + * If the directory was successfully created, returns 1. + * Otherwise the return value is 0 and errno is set to + * indicate the error. Some possible values for errno are: + * + * EACCES: src or dst parent directory can't be read and/or written. + * EEXIST: dst is a non-empty directory. + * EINVAL: src is a root directory or dst is a subdirectory of src. + * EISDIR: dst is a directory, but src is not. + * ENOENT: src doesn't exist, or src or dst is "". + * ENOTDIR: src is a directory, but dst is not. + * EXDEV: src and dst are on different filesystems. + * + * Side effects: + * The implementation of rename may allow cross-filesystem renames, + * but the caller should be prepared to emulate it with copy and + * delete if errno is EXDEV. + * + *--------------------------------------------------------------------------- + */ + +int +efile_rename(Efile_error* errInfo, /* Where to return error codes. */ + char* src, /* Original name. */ + char* dst) /* New name. */ +{ + if (rename(src, dst) == 0) { + return 1; + } + if (errno == ENOTEMPTY) { + errno = EEXIST; + } +#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 + * EINVAL result code for moving a directory into itself). Must be + * conditionally compiled because realpath() is only defined on SunOS. + */ + + if (errno == EINVAL) { + char srcPath[MAXPATHLEN], dstPath[MAXPATHLEN]; + DIR *dirPtr; + struct dirent *dirEntPtr; + +#ifdef PURIFY + memset(srcPath, '\0', sizeof(srcPath)); + memset(dstPath, '\0', sizeof(dstPath)); +#endif + + if ((realpath(src, srcPath) != NULL) + && (realpath(dst, dstPath) != NULL) + && (strncmp(srcPath, dstPath, strlen(srcPath)) != 0)) { + dirPtr = opendir(dst); + if (dirPtr != NULL) { + while ((dirEntPtr = readdir(dirPtr)) != NULL) { + if ((strcmp(dirEntPtr->d_name, ".") != 0) && + (strcmp(dirEntPtr->d_name, "..") != 0)) { + errno = EEXIST; + closedir(dirPtr); + return check_error(-1, errInfo); + } + } + closedir(dirPtr); + } + } + errno = EINVAL; + } +#endif /* sparc */ + + if (strcmp(src, "/") == 0) { + /* + * Alpha reports renaming / as EBUSY and Linux reports it as EACCES, + * instead of EINVAL. + */ + + errno = EINVAL; + } + + /* + * DEC Alpha OSF1 V3.0 returns EACCES when attempting to move a + * file across filesystems and the parent directory of that file is + * not writable. Most other systems return EXDEV. Does nothing to + * correct this behavior. + */ + + return check_error(-1, errInfo); +} + +int +efile_chdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of directory to make current. */ +{ + return check_error(chdir(name), errInfo); +} + + +int +efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */ + int drive, /* 0 - current, 1 - A, 2 - B etc. */ + char* buffer, /* Where to return the current + directory. */ + size_t size) /* Size of buffer. */ +{ + if (drive == 0) { + if (getcwd(buffer, size) == NULL) + return check_error(-1, errInfo); + +#ifdef SIMSPARCSOLARIS + /* We get "host:" prepended to the dirname - remove!. */ + { + int i = 0; + int j = 0; + while ((buffer[i] != ':') && (buffer[i] != '\0')) i++; + if (buffer[i] == ':') { + i++; + while ((buffer[j++] = buffer[i++]) != '\0'); + } + } +#endif + return 1; + } + + /* + * Drives other than 0 is not supported on Unix. + */ + + errno = ENOTSUP; + return check_error(-1, errInfo); +} + +int +efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name, /* Name of directory to open. */ + EFILE_DIR_HANDLE* p_dir_handle, /* Pointer to directory + handle of + open directory.*/ + char* buffer, /* Pointer to buffer for + one filename. */ + size_t *size) /* in-out Size of buffer, length + of name. */ +{ + DIR *dp; /* Pointer to directory structure. */ + struct dirent* dirp; /* Pointer to directory entry. */ + + /* + * If this is the first call, we must open the directory. + */ + + if (*p_dir_handle == NULL) { + dp = opendir(name); + if (dp == NULL) + return check_error(-1, errInfo); + *p_dir_handle = (EFILE_DIR_HANDLE) dp; + } + + /* + * Retrieve the name of the next file using the directory handle. + */ + + dp = *((DIR **)((void *)p_dir_handle)); + for (;;) { + dirp = readdir(dp); + if (dirp == NULL) { + closedir(dp); + return 0; + } + if (IS_DOT_OR_DOTDOT(dirp->d_name)) + continue; + buffer[0] = '\0'; + strncat(buffer, dirp->d_name, (*size)-1); + *size = strlen(dirp->d_name); + return 1; + } +} + +int +efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ + char* name, /* Name of directory to open. */ + int flags, /* Flags to user for opening. */ + int* pfd, /* Where to store the file + descriptor. */ + Sint64 *pSize) /* Where to store the size of the + file. */ +{ + struct stat statbuf; + int fd; + int mode; /* Open mode. */ + + if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) { + /* + * For UNIX only, here is some ugly code to allow + * /dev/null to be opened as a file. + * + * Assumption: The i-node number for /dev/null cannot be zero. + */ + static ino_t dev_null_ino = 0; + + if (dev_null_ino == 0) { + struct stat nullstatbuf; + + if (stat("/dev/null", &nullstatbuf) >= 0) { + dev_null_ino = nullstatbuf.st_ino; + } + } + if (!(dev_null_ino && statbuf.st_ino == dev_null_ino)) { + errno = EISDIR; + return check_error(-1, errInfo); + } + } + + switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) { + case EFILE_MODE_READ: + mode = O_RDONLY; + break; + case EFILE_MODE_WRITE: + if (flags & EFILE_NO_TRUNCATE) + mode = O_WRONLY | O_CREAT; + else + mode = O_WRONLY | O_CREAT | O_TRUNC; + break; + case EFILE_MODE_READ_WRITE: + mode = O_RDWR | O_CREAT; + break; + default: + errno = EINVAL; + return check_error(-1, errInfo); + } + + + if (flags & EFILE_MODE_APPEND) { + mode &= ~O_TRUNC; + mode |= O_APPEND; + } + + if (flags & EFILE_MODE_EXCL) { + mode |= O_EXCL; + } + + fd = open(name, mode, FILE_MODE); + + if (!check_error(fd, errInfo)) + return 0; + + *pfd = fd; + if (pSize) { + *pSize = statbuf.st_size; + } + return 1; +} + +int +efile_may_openfile(Efile_error* errInfo, char *name) { + struct stat statbuf; /* Information about the file */ + int result; + + result = stat(name, &statbuf); + if (!check_error(result, errInfo)) + return 0; + if (!ISREG(statbuf)) { + errno = EISDIR; + return check_error(-1, errInfo); + } + return 1; +} + +void +efile_closefile(int fd) +{ + close(fd); +} + +int +efile_fdatasync(Efile_error *errInfo, /* Where to return error codes. */ + int fd) /* File descriptor for file to sync data. */ +{ +#ifdef HAVE_FDATASYNC + return check_error(fdatasync(fd), errInfo); +#else + return efile_fsync(errInfo, fd); +#endif +} + +int +efile_fsync(Efile_error *errInfo, /* Where to return error codes. */ + int fd) /* File descriptor for file to sync. */ +{ +#ifdef NO_FSYNC + undefined fsync /* XXX: Really? */ +#else +#if defined(DARWIN) && defined(F_FULLFSYNC) + return check_error(fcntl(fd, F_FULLFSYNC), errInfo); +#else + return check_error(fsync(fd), errInfo); +#endif /* DARWIN */ +#endif /* NO_FSYNC */ +} + +int +efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, + char* name, int info_for_link) +{ + struct stat statbuf; /* Information about the file */ + int result; + + if (info_for_link) { +#ifndef __OSE__ + result = lstat(name, &statbuf); +#else + result = stat(name, &statbuf); +#endif + } else { + result = stat(name, &statbuf); + } + if (!check_error(result, errInfo)) { + return 0; + } + +#if SIZEOF_OFF_T == 4 + pInfo->size_high = 0; +#else + pInfo->size_high = (Uint32)(statbuf.st_size >> 32); +#endif + pInfo->size_low = (Uint32)statbuf.st_size; + +#ifdef NO_ACCESS + /* Just look at read/write access for owner. */ + + pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1; + +#else + pInfo->access = FA_NONE; + if (access(name, R_OK) == 0) + pInfo->access |= FA_READ; + if (access(name, W_OK) == 0) + pInfo->access |= FA_WRITE; + +#endif + + if (ISDEV(statbuf)) + pInfo->type = FT_DEVICE; + else if (ISDIR(statbuf)) + pInfo->type = FT_DIRECTORY; + else if (ISREG(statbuf)) + pInfo->type = FT_REGULAR; + else if (ISLNK(statbuf)) + pInfo->type = FT_SYMLINK; + else + pInfo->type = FT_OTHER; + + pInfo->accessTime = statbuf.st_atime; + pInfo->modifyTime = statbuf.st_mtime; + pInfo->cTime = statbuf.st_ctime; + + pInfo->mode = statbuf.st_mode; + pInfo->links = statbuf.st_nlink; + pInfo->major_device = statbuf.st_dev; +#ifndef __OSE__ + pInfo->minor_device = statbuf.st_rdev; +#endif + pInfo->inode = statbuf.st_ino; + pInfo->uid = statbuf.st_uid; + pInfo->gid = statbuf.st_gid; + + return 1; +} + +int +efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) +{ +#ifndef __OSE__ + struct utimbuf tval; +#endif + + /* + * On some systems chown will always fail for a non-root user unless + * POSIX_CHOWN_RESTRICTED is not set. Others will succeed as long as + * you don't try to chown a file to someone besides youself. + */ +#ifndef __OSE__ + if (chown(name, pInfo->uid, pInfo->gid) && errno != EPERM) { + return check_error(-1, errInfo); + } +#endif + + if (pInfo->mode != -1) { + mode_t newMode = pInfo->mode & (S_ISUID | S_ISGID | + S_IRWXU | S_IRWXG | S_IRWXO); + if (chmod(name, newMode)) { + newMode &= ~(S_ISUID | S_ISGID); + if (chmod(name, newMode)) { + return check_error(-1, errInfo); + } + } + } + +#ifndef __OSE__ + tval.actime = pInfo->accessTime; + tval.modtime = pInfo->modifyTime; + + return check_error(utime(name, &tval), errInfo); +#else + return 1; +#endif +} + + +int +efile_write(Efile_error* errInfo, /* Where to return error codes. */ + int flags, /* Flags given when file was + opened. */ + int fd, /* File descriptor to write to. */ + char* buf, /* Buffer to write. */ + size_t count) /* Number of bytes to write. */ +{ + ssize_t written; /* Bytes written in last operation. */ + + while (count > 0) { + if ((written = write(fd, buf, count)) < 0) { + if (errno != EINTR) + return check_error(-1, errInfo); + else + written = 0; + } + ASSERT(written <= count); + buf += written; + count -= written; + } + return 1; +} + +int +efile_writev(Efile_error* errInfo, /* Where to return error codes */ + int flags, /* Flags given when file was + * opened */ + int fd, /* File descriptor to write to */ + SysIOVec* iov, /* Vector of buffer structs. + * The structs may be changed i.e. + * due to incomplete writes */ + int iovcnt) /* Number of structs in vector */ +{ + int cnt = 0; /* Buffers so far written */ + + ASSERT(iovcnt >= 0); + + while (cnt < iovcnt) { + if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) { + /* Empty buffer - skip */ + cnt++; + } else { /* Non-empty buffer */ + ssize_t w; /* Bytes written in this call */ +#ifdef HAVE_WRITEV + int b = iovcnt - cnt; /* Buffers to write */ + /* Use as many buffers as MAXIOV allows */ + if (b > MAXIOV) + b = MAXIOV; + if (b > 1) { + do { + w = writev(fd, &iov[cnt], b); + } while (w < 0 && errno == EINTR); + } else + /* Degenerated io vector - use regular write */ +#endif + { + do { + w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len); + } while (w < 0 && errno == EINTR); + ASSERT(w <= iov[cnt].iov_len || w == -1); + } + if (w < 0) return check_error(-1, errInfo); + /* Move forward to next buffer to write */ + for (; cnt < iovcnt && w > 0; cnt++) { + if (iov[cnt].iov_base && iov[cnt].iov_len > 0) { + if (w < iov[cnt].iov_len) { + /* Adjust the buffer for next write */ + iov[cnt].iov_len -= w; + iov[cnt].iov_base += w; + w = 0; + break; + } else { + w -= iov[cnt].iov_len; + } + } + } + ASSERT(w == 0); + } /* else Non-empty buffer */ + } /* while (cnt< iovcnt) */ + return 1; +} + +int +efile_read(Efile_error* errInfo, /* Where to return error codes. */ + int flags, /* Flags given when file was opened. */ + int fd, /* File descriptor to read from. */ + char* buf, /* Buffer to read into. */ + size_t count, /* Number of bytes to read. */ + size_t *pBytesRead) /* Where to return number of + bytes read. */ +{ + ssize_t n; + + for (;;) { + if ((n = read(fd, buf, count)) >= 0) + break; + else if (errno != EINTR) + return check_error(-1, errInfo); + } + *pBytesRead = (size_t) n; + return 1; +} + + +/* pread() and pwrite() */ +/* Some unix systems, notably Solaris has these syscalls */ +/* It is especially nice for i.e. the dets module to have support */ +/* for this, even if the underlying OS dosn't support it, it is */ +/* reasonably easy to work around by first calling seek, and then */ +/* calling read(). */ +/* This later strategy however changes the file pointer, which pread() */ +/* does not do. We choose to ignore this and say that the location */ +/* of the file pointer is undefined after a call to any of the p functions*/ + + +int +efile_pread(Efile_error* errInfo, /* Where to return error codes. */ + int fd, /* File descriptor to read from. */ + Sint64 offset, /* Offset in bytes from BOF. */ + char* buf, /* Buffer to read into. */ + size_t count, /* Number of bytes to read. */ + size_t *pBytesRead) /* Where to return + number of bytes read. */ +{ +#if defined(HAVE_PREAD) && defined(HAVE_PWRITE) + ssize_t n; + off_t off = (off_t) offset; + if (off != offset) { + errno = EINVAL; + return check_error(-1, errInfo); + } + for (;;) { + if ((n = pread(fd, buf, count, offset)) >= 0) + break; + else if (errno != EINTR) + return check_error(-1, errInfo); + } + *pBytesRead = (size_t) n; + return 1; +#else + { + int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + if (res) { + return efile_read(errInfo, 0, fd, buf, count, pBytesRead); + } else { + return res; + } + } +#endif +} + + + +int +efile_pwrite(Efile_error* errInfo, /* Where to return error codes. */ + int fd, /* File descriptor to write to. */ + char* buf, /* Buffer to write. */ + size_t count, /* Number of bytes to write. */ + Sint64 offset) /* where to write it */ +{ +#if defined(HAVE_PREAD) && defined(HAVE_PWRITE) + ssize_t written; /* Bytes written in last operation. */ + off_t off = (off_t) offset; + if (off != offset) { + errno = EINVAL; + return check_error(-1, errInfo); + } + + while (count > 0) { + if ((written = pwrite(fd, buf, count, offset)) < 0) { + if (errno != EINTR) + return check_error(-1, errInfo); + else + written = 0; + } + ASSERT(written <= count); + buf += written; + count -= written; + offset += written; + } + return 1; +#else /* For unix systems that don't support pread() and pwrite() */ + { + int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + + if (res) { + return efile_write(errInfo, 0, fd, buf, count); + } else { + return res; + } + } +#endif +} + + +int +efile_seek(Efile_error* errInfo, /* Where to return error codes. */ + int fd, /* File descriptor to do the seek on. */ + Sint64 offset, /* Offset in bytes from the given + origin. */ + int origin, /* Origin of seek (SEEK_SET, SEEK_CUR, + SEEK_END). */ + Sint64 *new_location) /* Resulting new location in file. */ +{ + off_t off, result; + + switch (origin) { + case EFILE_SEEK_SET: origin = SEEK_SET; break; + case EFILE_SEEK_CUR: origin = SEEK_CUR; break; + case EFILE_SEEK_END: origin = SEEK_END; break; + default: + errno = EINVAL; + return check_error(-1, errInfo); + } + off = (off_t) offset; + if (off != offset) { + errno = EINVAL; + return check_error(-1, errInfo); + } + + errno = 0; + result = lseek(fd, off, origin); + + /* + * Note that the man page for lseek (on SunOs 5) says: + * + * "if fildes is a remote file descriptor and offset is + * negative, lseek() returns the file pointer even if it is + * negative." + */ + + if (result < 0 && errno == 0) + errno = EINVAL; + if (result < 0) + return check_error(-1, errInfo); + if (new_location) { + *new_location = result; + } + return 1; +} + + +int +efile_truncate_file(Efile_error* errInfo, int *fd, int flags) +{ +#ifndef NO_FTRUNCATE + off_t offset; + + return check_error((offset = lseek(*fd, 0, 1)) >= 0 && + ftruncate(*fd, offset) == 0 ? 1 : -1, + errInfo); +#else + return 1; +#endif +} + +int +efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) +{ +#ifndef __OSE__ + int len; + ASSERT(size > 0); + len = readlink(name, buffer, size-1); + if (len == -1) { + return check_error(-1, errInfo); + } + buffer[len] = '\0'; + return 1; +#else + errno = ENOTSUP; + return check_error(-1, errInfo); +#endif +} + +int +efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size) +{ + errno = ENOTSUP; + return check_error(-1, errInfo); +} + +int +efile_link(Efile_error* errInfo, char* old, char* new) +{ +#ifndef __OSE__ + return check_error(link(old, new), errInfo); +#else + errno = ENOTSUP; + return check_error(-1, errInfo); +#endif +} + +int +efile_symlink(Efile_error* errInfo, char* old, char* new) +{ +#ifndef __OSE__ + return check_error(symlink(old, new), errInfo); +#else + errno = ENOTSUP; + return check_error(-1, errInfo); +#endif +} + +int +efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, + Sint64 length, int advise) +{ +#ifdef HAVE_POSIX_FADVISE + return check_error(posix_fadvise(fd, offset, length, advise), errInfo); +#else + return check_error(0, errInfo); +#endif +} + +#ifdef HAVE_SENDFILE +/* For some reason the maximum size_t cannot be used as the max size + 3GB seems to work on all platforms */ +#define SENDFILE_CHUNK_SIZE ((1UL << 30) -1) + +/* + * sendfile: The implementation of the sendfile system call varies + * a lot on different *nix platforms so to make the api similar in all + * we have to emulate some things in linux and play with variables on + * bsd/darwin. + * + * All of the calls will split a command which tries to send more than + * SENDFILE_CHUNK_SIZE of data at once. + * + * On platforms where *nbytes of 0 does not mean the entire file, this is + * simulated. + * + * It could be possible to implement header/trailer in sendfile. Though + * you would have to emulate it in linux and on BSD/Darwin some complex + * calculations have to be made when using a non blocking socket to figure + * out how much of the header/file/trailer was sent in each command. + * + * The semantics of the API is this: + * Return value: 1 if all data was sent and the function does not need to + * be called again. 0 if an error occures OR if there is more data which + * has to be sent (EAGAIN or EINTR will be set appropriately) + * + * The amount of data written in a call is returned through nbytes. + * + */ + +int +efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd, + off_t *offset, Uint64 *nbytes, struct t_sendfile_hdtl* hdtl) +{ + Uint64 written = 0; +#if defined(__linux__) + ssize_t retval; + do { + /* check if *nbytes is 0 or greater than chunk size */ + if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE) + retval = sendfile(out_fd, in_fd, offset, SENDFILE_CHUNK_SIZE); + else + retval = sendfile(out_fd, in_fd, offset, *nbytes); + if (retval > 0) { + written += retval; + *nbytes -= retval; + } + } while (retval == SENDFILE_CHUNK_SIZE); + if (written != 0) { + /* -1 is not returned by the linux API so we have to simulate it */ + retval = -1; + errno = EAGAIN; + } +#elif defined(__sun) && defined(__SVR4) && defined(HAVE_SENDFILEV) + ssize_t retval; + size_t len; + sendfilevec_t fdrec; + fdrec.sfv_fd = in_fd; + fdrec.sfv_flag = 0; + do { + fdrec.sfv_off = *offset; + len = 0; + /* check if *nbytes is 0 or greater than chunk size */ + if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE) + fdrec.sfv_len = SENDFILE_CHUNK_SIZE; + else + fdrec.sfv_len = *nbytes; + retval = sendfilev(out_fd, &fdrec, 1, &len); + + /* Sometimes sendfilev can return -1 and still send data. + When that happens we just pretend that no error happend. */ + if (retval != -1 || errno == EAGAIN || errno == EINTR || + len != 0) { + *offset += len; + *nbytes -= len; + written += len; + if (errno != EAGAIN && errno != EINTR && len != 0) + retval = len; + } + } while (len == SENDFILE_CHUNK_SIZE); +#elif defined(DARWIN) + int retval; + off_t len; + do { + /* check if *nbytes is 0 or greater than chunk size */ + if(*nbytes > SENDFILE_CHUNK_SIZE) + len = SENDFILE_CHUNK_SIZE; + else + len = *nbytes; + retval = sendfile(in_fd, out_fd, *offset, &len, NULL, 0); + if (retval != -1 || errno == EAGAIN || errno == EINTR) { + *offset += len; + *nbytes -= len; + written += len; + } + } while (len == SENDFILE_CHUNK_SIZE); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + off_t len; + int retval; + do { + if (*nbytes > SENDFILE_CHUNK_SIZE) + retval = sendfile(in_fd, out_fd, *offset, SENDFILE_CHUNK_SIZE, + NULL, &len, 0); + else + retval = sendfile(in_fd, out_fd, *offset, *nbytes, NULL, &len, 0); + if (retval != -1 || errno == EAGAIN || errno == EINTR) { + *offset += len; + *nbytes -= len; + written += len; + } + } while(len == SENDFILE_CHUNK_SIZE); +#endif + *nbytes = written; + return check_error(retval, errInfo); +} +#endif /* HAVE_SENDFILE */ + +#ifdef HAVE_POSIX_FALLOCATE +static int +call_posix_fallocate(int fd, Sint64 offset, Sint64 length) +{ + int ret; + + /* + * On Linux and Solaris for example, posix_fallocate() returns + * a positive error number on error and it does not set errno. + * On FreeBSD however (9.0 at least), it returns -1 on error + * and it sets errno. + */ + do { + ret = posix_fallocate(fd, (off_t) offset, (off_t) length); + if (ret > 0) { + errno = ret; + ret = -1; + } + } while (ret != 0 && errno == EINTR); + + return ret; +} +#endif /* HAVE_POSIX_FALLOCATE */ + +int +efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length) +{ +#if defined HAVE_FALLOCATE + /* Linux specific, more efficient than posix_fallocate. */ + int ret; + + do { + ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, (off_t) offset, (off_t) length); + } while (ret != 0 && errno == EINTR); + +#if defined HAVE_POSIX_FALLOCATE + /* Fallback to posix_fallocate if available. */ + if (ret != 0) { + ret = call_posix_fallocate(fd, offset, length); + } +#endif + + return check_error(ret, errInfo); +#elif defined F_PREALLOCATE + /* Mac OS X specific, equivalent to posix_fallocate. */ + int ret; + fstore_t fs; + + memset(&fs, 0, sizeof(fs)); + fs.fst_flags = F_ALLOCATECONTIG; + fs.fst_posmode = F_VOLPOSMODE; + fs.fst_offset = (off_t) offset; + fs.fst_length = (off_t) length; + + ret = fcntl(fd, F_PREALLOCATE, &fs); + + if (-1 == ret) { + fs.fst_flags = F_ALLOCATEALL; + ret = fcntl(fd, F_PREALLOCATE, &fs); + +#if defined HAVE_POSIX_FALLOCATE + /* Fallback to posix_fallocate if available. */ + if (-1 == ret) { + ret = call_posix_fallocate(fd, offset, length); + } +#endif + } + + return check_error(ret, errInfo); +#elif defined HAVE_POSIX_FALLOCATE + /* Other Unixes, use posix_fallocate if available. */ + return check_error(call_posix_fallocate(fd, offset, length), errInfo); +#else + errno = ENOTSUP; + return check_error(-1, errInfo); +#endif +} diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 7035dc77df..dab056e2df 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -34,6 +34,7 @@ #endif #include "sys.h" #include "global.h" +#include "erl_port.h" #include "erl_check_io.h" #include "erl_thr_progress.h" #include "dtrace-wrapper.h" @@ -78,6 +79,12 @@ typedef char EventStateFlags; #define ERTS_CIO_POLL_INIT ERTS_POLL_EXPORT(erts_poll_init) #define ERTS_CIO_POLL_INFO ERTS_POLL_EXPORT(erts_poll_info) +#ifdef __OSE__ +#define GET_FD(fd) fd->id +#else +#define GET_FD(fd) fd +#endif + static struct pollset_info { ErtsPollSet ps; @@ -435,7 +442,11 @@ deselect(ErtsDrvEventState *state, int mode) } } - state->events = ERTS_CIO_POLL_CTL(pollset.ps, state->fd, rm_events, 0, &do_wake); + state->events = ERTS_CIO_POLL_CTL(pollset.ps, state->fd, rm_events, 0, &do_wake +#ifdef __OSE__ + ,NULL +#endif + ); if (!(state->events)) { switch (state->type) { @@ -584,7 +595,11 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, wake_poller = 1; } - new_events = ERTS_CIO_POLL_CTL(pollset.ps, state->fd, ctl_events, on, &wake_poller); + new_events = ERTS_CIO_POLL_CTL(pollset.ps, state->fd, ctl_events, on, &wake_poller +#ifdef __OSE__ + ,prt->drv_ptr->resolve_signal +#endif + ); if (new_events & (ERTS_POLL_EV_ERR|ERTS_POLL_EV_NVAL)) { if (state->type == ERTS_EV_TYPE_DRV_SEL && !state->events) { @@ -894,7 +909,7 @@ print_driver_name(erts_dsprintf_buf_t *dsbufp, Eterm id) static void steal(erts_dsprintf_buf_t *dsbufp, ErtsDrvEventState *state, int mode) { - erts_dsprintf(dsbufp, "stealing control of fd=%d from ", (int) state->fd); + erts_dsprintf(dsbufp, "stealing control of fd=%d from ", (int) GET_FD(state->fd)); switch (state->type) { case ERTS_EV_TYPE_DRV_SEL: { int deselect_mode = 0; @@ -918,7 +933,7 @@ steal(erts_dsprintf_buf_t *dsbufp, ErtsDrvEventState *state, int mode) if (deselect_mode) deselect(state, deselect_mode); else { - erts_dsprintf(dsbufp, "no one", (int) state->fd); + erts_dsprintf(dsbufp, "no one", (int) GET_FD(state->fd)); ASSERT(0); } erts_dsprintf(dsbufp, "\n"); @@ -946,7 +961,7 @@ steal(erts_dsprintf_buf_t *dsbufp, ErtsDrvEventState *state, int mode) break; } default: - erts_dsprintf(dsbufp, "no one\n", (int) state->fd); + erts_dsprintf(dsbufp, "no one\n", (int) GET_FD(state->fd)); ASSERT(0); } } @@ -957,14 +972,21 @@ print_select_op(erts_dsprintf_buf_t *dsbufp, { Port *pp = erts_drvport2port(ix); erts_dsprintf(dsbufp, +#ifdef __OSE__ + "driver_select(%p, %d,%s%s%s%s | %d, %d) " +#else "driver_select(%p, %d,%s%s%s%s, %d) " +#endif "by ", ix, - (int) fd, + (int) GET_FD(fd), mode & ERL_DRV_READ ? " ERL_DRV_READ" : "", mode & ERL_DRV_WRITE ? " ERL_DRV_WRITE" : "", mode & ERL_DRV_USE ? " ERL_DRV_USE" : "", mode & (ERL_DRV_USE_NO_CALLBACK & ~ERL_DRV_USE) ? "_NO_CALLBACK" : "", +#ifdef __OSE__ + fd->signo, +#endif on); print_driver_name(dsbufp, pp != ERTS_INVALID_ERL_DRV_PORT ? pp->common.id : NIL); erts_dsprintf(dsbufp, "driver %T ", pp != ERTS_INVALID_ERL_DRV_PORT ? pp->common.id : NIL); @@ -1010,7 +1032,7 @@ steal_pending_stop_select(erts_dsprintf_buf_t *dsbufp, ErlDrvPort ix, ASSERT(state->type == ERTS_EV_TYPE_STOP_USE); erts_dsprintf(dsbufp, "failed: fd=%d (re)selected before stop_select " "was called for driver %s\n", - (int) state->fd, state->driver.drv_ptr->name); + (int) GET_FD(state->fd), state->driver.drv_ptr->name); erts_send_error_to_logger_nogl(dsbufp); if (on) { @@ -1395,6 +1417,20 @@ stale_drv_select(Eterm id, ErtsDrvEventState *state, int mode) } #ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS +#ifdef __OSE__ +static SafeHashValue drv_ev_state_hash(void *des) +{ + SafeHashValue val = (SafeHashValue) ((ErtsDrvEventState *) des)->fd; + return val ^ (val >> 8); /* Good enough for aligned pointer values? */ +} + +static int drv_ev_state_cmp(void *des1, void *des2) +{ + return ( ((((ErtsDrvEventState *) des1)->fd->id == ((ErtsDrvEventState *) des2)->fd->id) + && (((ErtsDrvEventState *) des1)->fd->signo == ((ErtsDrvEventState *) des2)->fd->signo)) + ? 0 : 1); +} +#else static SafeHashValue drv_ev_state_hash(void *des) { SafeHashValue val = (SafeHashValue) ((ErtsDrvEventState *) des)->fd; @@ -1406,6 +1442,7 @@ static int drv_ev_state_cmp(void *des1, void *des2) return ( ((ErtsDrvEventState *) des1)->fd == ((ErtsDrvEventState *) des2)->fd ? 0 : 1); } +#endif static void *drv_ev_state_alloc(void *des_tmpl) { @@ -1436,7 +1473,69 @@ static void drv_ev_state_free(void *des) erts_smp_spin_unlock(&state_prealloc_lock); } #endif +#ifdef __OSE__ +OseSignal *erl_drv_ose_get_input_signal(ErlDrvEvent drv_ev) { + struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; + ethr_mutex_lock(&ev->mtx); + if (ev->imsgs == NULL) { + ethr_mutex_unlock(&ev->mtx); + return NULL; + } else { + ErtsPollOseMsgList *msg = ev->imsgs; + OseSignal *sig = (OseSignal*)msg->data; + ASSERT(msg->data); + ev->imsgs = msg->next; + ethr_mutex_unlock(&ev->mtx); + erts_free(ERTS_ALC_T_FD_SIG_LIST,msg); + restore(sig); + return sig; + } +} + +OseSignal *erl_drv_ose_get_output_signal(ErlDrvEvent drv_ev) { + struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; + ethr_mutex_lock(&ev->mtx); + if (ev->omsgs == NULL) { + ethr_mutex_unlock(&ev->mtx); + return NULL; + } else { + ErtsPollOseMsgList *msg = ev->omsgs; + OseSignal *sig = (OseSignal*)msg->data; + ASSERT(msg->data); + ev->omsgs = msg->next; + ethr_mutex_unlock(&ev->mtx); + erts_free(ERTS_ALC_T_FD_SIG_LIST,msg); + restore(sig); + return sig; + } +} +ErlDrvEvent erl_drv_ose_event_alloc(SIGSELECT signo, int id) { + struct erts_sys_fd_type *ev = erts_alloc(ERTS_ALC_T_DRV_EV, + sizeof(struct erts_sys_fd_type)); + ev->signo = signo; + ev->id = id; + ev->imsgs = NULL; + ev->omsgs = NULL; + ethr_mutex_init(&ev->mtx); + return (ErlDrvEvent)ev; +} + +void erl_drv_ose_event_free(ErlDrvEvent drv_ev) { + struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; + ethr_mutex_destroy(&ev->mtx); + erts_free(ERTS_ALC_T_DRV_EV,ev); +} + +void erl_drv_ose_event_fetch(ErlDrvEvent drv_ev, SIGSELECT *signo, int *id) { + struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; + if (signo) + *signo = ev->signo; + if (id) + *id = ev->id; +} + +#endif void ERTS_CIO_EXPORT(erts_init_check_io)(void) { @@ -1882,12 +1981,14 @@ ERTS_CIO_EXPORT(erts_check_io_debug)(void) int fd, len; #endif IterDebugCounters counters; +#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS ErtsDrvEventState null_des; null_des.driver.select = NULL; null_des.events = 0; null_des.remove_cnt = 0; null_des.type = ERTS_EV_TYPE_NONE; +#endif erts_printf("--- fds in pollset --------------------------------------\n"); diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index 09ed9f41af..e1ea8fb207 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -90,7 +90,7 @@ # if defined(ERTS_USE_POLL) # undef ERTS_POLL_USE_POLL # define ERTS_POLL_USE_POLL 1 -# elif !defined(__WIN32__) +# elif !defined(__WIN32__) && !defined(__OSE__) # undef ERTS_POLL_USE_SELECT # define ERTS_POLL_USE_SELECT 1 # endif @@ -99,7 +99,7 @@ typedef Uint32 ErtsPollEvents; #undef ERTS_POLL_EV_E2N -#if defined(__WIN32__) /* --- win32 ------------------------------- */ +#if defined(__WIN32__) || defined(__OSE__) /* --- win32 or ose -------- */ #define ERTS_POLL_EV_IN 1 #define ERTS_POLL_EV_OUT 2 @@ -228,7 +228,11 @@ ErtsPollEvents ERTS_POLL_EXPORT(erts_poll_control)(ErtsPollSet, ErtsSysFdType, ErtsPollEvents, int on, - int* wake_poller); + int* wake_poller +#ifdef __OSE__ + ,int (*decode)(OseSignal* sig, int* mode) +#endif + ); void ERTS_POLL_EXPORT(erts_poll_controlv)(ErtsPollSet, ErtsPollControlEntry [], int on); diff --git a/erts/emulator/sys/ose/default.lmconf b/erts/emulator/sys/ose/default.lmconf new file mode 100644 index 0000000000..f897872fa2 --- /dev/null +++ b/erts/emulator/sys/ose/default.lmconf @@ -0,0 +1,10 @@ +OSE_LM_STACK_SIZES=256,512,1024,2048,4096,8192,16384,65536 +OSE_LM_SIGNAL_SIZES=31,63,127,255,1023,4095,16383,65535 +OSE_LM_POOL_SIZE=0x200000 +OSE_LM_MAIN_NAME=main +OSE_LM_MAIN_STACK_SIZE=0x400 +OSE_LM_MAIN_PRIORITY=20 +OSE_LM_PROGRAM_TYPE=SYS_RAM +OSE_LM_DATA_INIT=YES +OSE_LM_BSS_INIT=YES +OSE_LM_EXEC_MODEL=SHARED diff --git a/erts/emulator/sys/ose/erl_main.c b/erts/emulator/sys/ose/erl_main.c new file mode 100644 index 0000000000..21cfce9463 --- /dev/null +++ b/erts/emulator/sys/ose/erl_main.c @@ -0,0 +1,70 @@ +/* + * %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" +#include "global.h" + +#include "shell.h" +#include "ramlog.h" +#include "ose_err/ose_err.h" + +static PROCESS mainPid; + +#ifdef DEBUG +static OSADDRESS err_handler(OSBOOLEAN user_called, OSERRCODE ecode, OSERRCODE extra) { + fprintf(stderr,"err_handler: %p %p\n",ecode,extra); + return 1; +} +#endif + +static int +cmd_ek(int argc, char **argv) { + kill_proc(mainPid); + return 0; +} + +static int +cmd_erl_start(int argc, char **argv) { + ramlog_printf("\n"); + ramlog_printf("================================================================\n"); + ramlog_printf("\n"); +#ifdef DEBUG + create_error_handler(get_bid(current_process()),err_handler,0x100); +#endif + erl_start(argc, argv); + return 0; +} + +int +main(int argc, char **argv) { + mainPid = current_process(); + + shell_add_cmd_attrs("start_beam", "start_beam [params]", "Start the Erlang VM", + cmd_erl_start, OS_PRI_PROC, 20, 0xF000); + + shell_add_cmd_attrs("ek", "ek", "Kills the Erlang VM", + cmd_ek, OS_PRI_PROC, 20, 0x100); + + stop(current_process()); + + return 0; +} diff --git a/erts/emulator/sys/ose/erl_ose_sys.h b/erts/emulator/sys/ose/erl_ose_sys.h new file mode 100644 index 0000000000..a3308e9ba4 --- /dev/null +++ b/erts/emulator/sys/ose/erl_ose_sys.h @@ -0,0 +1,238 @@ +/* + * %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% + * + * This file handles differences between different Unix systems. + * This should be the only place with conditional compilation + * depending on the type of OS. + */ + +#ifndef _ERL_OSE_SYS_H +#define _ERL_OSE_SYS_H + +#include "ose.h" +#undef NIL +#include "ramlog.h" + +#include "fcntl.h" +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "sys/param.h" +#include "sys/time.h" +#include "time.h" +#include "dirent.h" +#include "ethread.h" + +/* FIXME: configuration options */ +#define ERTS_SCHED_MIN_SPIN +#define ERTS_SCHED_ONLY_POLL_SCHED_1 +#define NO_SYSCONF 1 +#define OPEN_MAX FOPEN_MAX + +#define MAP_ANON MAP_ANONYMOUS + +#ifndef HAVE_MMAP +# define HAVE_MMAP 0 +#endif + +#if HAVE_MMAP +# include "sys/mman.h" +#endif + +typedef struct ErtsPollOseMsgList_ { + struct ErtsPollOseMsgList_ *next; + void *data; +} ErtsPollOseMsgList; + +struct erts_sys_fd_type { + SIGSELECT signo; + int id; + ErtsPollOseMsgList *imsgs; + ErtsPollOseMsgList *omsgs; + ethr_mutex mtx; +}; + + +/* + * Our own type of "FD's" + */ +#define ERTS_SYS_FD_TYPE struct erts_sys_fd_type* +#define NO_FSTAT_ON_SYS_FD_TYPE 1 /* They are signals, not files */ + +#include "sys/stat.h" + +/* FIXME mremap is not defined in OSE - POSIX issue */ +extern void *mremap (void *__addr, size_t __old_len, size_t __new_len, + int __flags, ...); + +/* FIXME: mremap constants */ +#define MREMAP_MAYMOVE 1 +#define MREMAP_FIXED 2 + +typedef void *GETENV_STATE; + +/* +** For the erl_timer_sup module. +*/ +#define HAVE_GETHRTIME + +typedef long long SysHrTime; +extern SysHrTime sys_gethrtime(void); + +void sys_init_hrtime(void); + +typedef time_t erts_time_t; + +typedef struct timeval SysTimeval; + +#define sys_gettimeofday(Arg) ((void) gettimeofday((Arg), NULL)) + +typedef struct { + clock_t tms_utime; + clock_t tms_stime; + clock_t tms_cutime; + clock_t tms_cstime; +} SysTimes; + +extern int erts_ticks_per_sec; + +#define SYS_CLK_TCK (erts_ticks_per_sec) + +extern clock_t sys_times(SysTimes *buffer); + +/* No use in having other resolutions than 1 Ms. */ +#define SYS_CLOCK_RESOLUTION 1 + +#ifdef NO_FPE_SIGNALS + +#define erts_get_current_fp_exception() NULL +#ifdef ERTS_SMP +#define erts_thread_init_fp_exception() do{}while(0) +#endif +# define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0) +# define __ERTS_FP_ERROR(fpexnp, f, Action) if (!finite(f)) { Action; } else {} +# define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) __ERTS_FP_ERROR(fpexnp, f, Action) +# define __ERTS_SAVE_FP_EXCEPTION(fpexnp) +# define __ERTS_RESTORE_FP_EXCEPTION(fpexnp) + +#define erts_sys_block_fpe() 0 +#define erts_sys_unblock_fpe(x) do{}while(0) + +#else /* !NO_FPE_SIGNALS */ + +extern volatile unsigned long *erts_get_current_fp_exception(void); +#ifdef ERTS_SMP +extern void erts_thread_init_fp_exception(void); +#endif +# if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__) +# define erts_fwait(fpexnp,f) \ + __asm__ __volatile__("fwait" : "=m"(*(fpexnp)) : "m"(f)) +# elif (defined(__powerpc__) || defined(__ppc__)) && defined(__GNUC__) +# define erts_fwait(fpexnp,f) \ + __asm__ __volatile__("" : "=m"(*(fpexnp)) : "fm"(f)) +# elif defined(__sparc__) && defined(__linux__) && defined(__GNUC__) +# define erts_fwait(fpexnp,f) \ + __asm__ __volatile__("" : "=m"(*(fpexnp)) : "em"(f)) +# else +# define erts_fwait(fpexnp,f) \ + __asm__ __volatile__("" : "=m"(*(fpexnp)) : "g"(f)) +# endif +# if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__) + extern void erts_restore_fpu(void); +# else +# define erts_restore_fpu() /*empty*/ +# endif +# if (!defined(__GNUC__) || \ + (__GNUC__ < 2) || \ + (__GNUC__ == 2 && __GNUC_MINOR < 96)) && \ + !defined(__builtin_expect) +# define __builtin_expect(x, expected_value) (x) +# endif +static __inline__ int erts_check_fpe(volatile unsigned long *fp_exception, double f) +{ + erts_fwait(fp_exception, f); + if (__builtin_expect(*fp_exception == 0, 1)) + return 0; + *fp_exception = 0; + erts_restore_fpu(); + return 1; +} +# undef erts_fwait +# undef erts_restore_fpu +extern void erts_fp_check_init_error(volatile unsigned long *fp_exception); +static __inline__ void __ERTS_FP_CHECK_INIT(volatile unsigned long *fp_exception) +{ + if (__builtin_expect(*fp_exception == 0, 1)) + return; + erts_fp_check_init_error(fp_exception); +} +# define __ERTS_FP_ERROR(fpexnp, f, Action) do { if (erts_check_fpe((fpexnp),(f))) { Action; } } while (0) +# define __ERTS_SAVE_FP_EXCEPTION(fpexnp) unsigned long old_erl_fp_exception = *(fpexnp) +# define __ERTS_RESTORE_FP_EXCEPTION(fpexnp) \ + do { *(fpexnp) = old_erl_fp_exception; } while (0) + /* This is for library calls where we don't trust the external + code to always throw floating-point exceptions on errors. */ +static __inline__ int erts_check_fpe_thorough(volatile unsigned long *fp_exception, double f) +{ + return erts_check_fpe(fp_exception, f) || !finite(f); +} +# define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) \ + do { if (erts_check_fpe_thorough((fpexnp),(f))) { Action; } } while (0) + +int erts_sys_block_fpe(void); +void erts_sys_unblock_fpe(int); + +#endif /* !NO_FPE_SIGNALS */ + +#define ERTS_FP_CHECK_INIT(p) __ERTS_FP_CHECK_INIT(&(p)->fp_exception) +#define ERTS_FP_ERROR(p, f, A) __ERTS_FP_ERROR(&(p)->fp_exception, f, A) +#define ERTS_FP_ERROR_THOROUGH(p, f, A) __ERTS_FP_ERROR_THOROUGH(&(p)->fp_exception, f, A) + +/* FIXME: force HAVE_GETPAGESIZE and stub getpagesize */ +#ifndef HAVE_GETPAGESIZE +#define HAVE_GETPAGESIZE 1 +#endif + +extern int getpagesize(void); + +#ifndef HZ +#define HZ 60 +#endif + +/* OSE5 doesn't provide limits.h so a number of macros should be + * added manually */ + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +/* Minimum and maximum values a `signed int' can hold. */ +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif + +#ifndef INT_MIN +#define INT_MIN (-INT_MAX - 1) +#endif + +#ifndef UINT_MAX +# define UINT_MAX 4294967295U +#endif + +#endif /* _ERL_OSE_SYS_H */ diff --git a/erts/emulator/sys/ose/erl_ose_sys_ddll.c b/erts/emulator/sys/ose/erl_ose_sys_ddll.c new file mode 100644 index 0000000000..4121199096 --- /dev/null +++ b/erts/emulator/sys/ose/erl_ose_sys_ddll.c @@ -0,0 +1,197 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * + * The 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 "sys.h" +#include "erl_vm.h" +#include "global.h" +#ifdef HAVE_DLFCN_H +#include +#endif + + +/* some systems do not have RTLD_NOW defined, and require the "mode" + * argument to dload() always be 1. + */ +#ifndef RTLD_NOW +# define RTLD_NOW 1 +#endif + +#define MAX_NAME_LEN 255 /* XXX should we get the system path size? */ +#define EXT_LEN 3 +#define FILE_EXT ".so" /* extension appended to the filename */ + +static char **errcodes = NULL; +static int num_errcodes = 0; +static int num_errcodes_allocated = 0; + +#define my_strdup(WHAT) my_strdup_in(ERTS_ALC_T_DDLL_ERRCODES, WHAT); + +static char *my_strdup_in(ErtsAlcType_t type, char *what) +{ + char *res = erts_alloc(type, strlen(what) + 1); + strcpy(res, what); + return res; +} + + +static int find_errcode(char *string, ErtsSysDdllError* err) +{ + int i; + + if (err != NULL) { + erts_sys_ddll_free_error(err); /* in case we ignored an earlier error */ + err->str = my_strdup_in(ERTS_ALC_T_DDLL_TMP_BUF, string); + return 0; + } + for(i=0;i ERL_DE_DYNAMIC_ERROR_OFFSET) { + return "Unspecified error"; + } + actual_code = -1*(code - ERL_DE_DYNAMIC_ERROR_OFFSET); +#if defined(HAVE_DLOPEN) + { + char *msg; + + if (actual_code >= num_errcodes) { + msg = "Unknown dlload error"; + } else { + msg = errcodes[actual_code]; + } + return msg; + } +#endif + return "no error"; +} + +void erts_sys_ddll_free_error(ErtsSysDdllError* err) +{ + if (err->str != NULL) { + erts_free(ERTS_ALC_T_DDLL_TMP_BUF, err->str); + } +} diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c new file mode 100644 index 0000000000..878bd362e4 --- /dev/null +++ b/erts/emulator/sys/ose/erl_poll.c @@ -0,0 +1,748 @@ +/* + * %CopyrightBegin% + * + * 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 + * 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: Poll interface suitable for ERTS on OSE with or without + * SMP support. + * + * The interface is currently implemented using: + * - receive + receive_fsem + * + * Author: Lukas Larsson + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "erl_thr_progress.h" +#include "erl_driver.h" +#include "erl_alloc.h" +#include "erl_poll.h" + +#define NOFILE 4096 + +/* + * Some debug macros + */ + +/* #define HARDDEBUG +#define HARDTRACE*/ +#ifdef HARDDEBUG +#ifdef HARDTRACE +#define HARDTRACEF(X, ...) { fprintf(stderr, X, __VA_ARGS__); fprintf(stderr,"\r\n"); } +#else +#define HARDTRACEF(...) +#endif + +#else +#define HARDTRACEF(X,...) +#define HARDDEBUGF(...) +#endif + +#if 0 +#define ERTS_POLL_DEBUG_PRINT +#endif + +#if defined(DEBUG) && 0 +#define HARD_DEBUG +#endif + +# define SEL_ALLOC erts_alloc +# define SEL_REALLOC realloc_wrap +# define SEL_FREE erts_free + +#define ERTS_POLL_INVALID_SIGNO 12345 + +#ifdef ERTS_SMP + +#define ERTS_POLLSET_LOCK(PS) \ + erts_smp_mtx_lock(&(PS)->mtx) +#define ERTS_POLLSET_UNLOCK(PS) \ + erts_smp_mtx_unlock(&(PS)->mtx) + +#else + +#define ERTS_POLLSET_LOCK(PS) +#define ERTS_POLLSET_UNLOCK(PS) + +#endif + +/* + * --- Data types ------------------------------------------------------------ + */ + +union SIGNAL { + SIGSELECT sig_no; +}; + +typedef struct erts_sigsel_item_ ErtsSigSelItem; + +struct erts_sigsel_item_ { + ErtsSigSelItem *next; + ErtsSysFdType fd; + ErtsPollEvents events; +}; + +typedef struct erts_sigsel_info_ ErtsSigSelInfo; + +struct erts_sigsel_info_ { + ErtsSigSelInfo *next; + SIGSELECT signo; + int (*decode)(OseSignal* sig, int* mode); + ErtsSigSelItem *fds; +}; + +struct ErtsPollSet_ { + SIGSELECT *sigs; + ErtsSigSelInfo *info; + Uint sig_count; + Uint item_count; + PROCESS interrupt; + erts_atomic32_t wakeup_state; + erts_smp_atomic32_t timeout; +#ifdef ERTS_SMP + erts_smp_mtx_t mtx; +#endif +}; + +static int max_fds = -1; + +#define ERTS_POLL_NOT_WOKEN ((erts_aint32_t) (1 << 0)) +#define ERTS_POLL_WOKEN_INTR ((erts_aint32_t) (1 << 1)) +#define ERTS_POLL_WOKEN_TIMEDOUT ((erts_aint32_t) (1 << 2)) +#define ERTS_POLL_WOKEN_IO_READY ((erts_aint32_t) (1 << 3)) +#define ERTS_POLL_SLEEPING ((erts_aint32_t) (1 << 4)) + +/* signal list prototypes */ +static ErtsSigSelInfo *get_sigsel_info(ErtsPollSet ps, SIGSELECT signo); +static ErtsSigSelItem *get_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd); +static ErtsSigSelInfo *add_sigsel_info(ErtsPollSet ps, ErtsSysFdType fd, int (*decode)(OseSignal* sig, int* mode)); +static ErtsSigSelItem *add_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd, int (*decode)(OseSignal* sig, int* mode)); +static int del_sigsel_info(ErtsPollSet ps, ErtsSigSelInfo *info); +static int del_sigsel_item(ErtsPollSet ps, ErtsSigSelItem *item); +static int update_sigsel(ErtsPollSet ps); + +static ErtsSigSelInfo * +get_sigsel_info(ErtsPollSet ps, SIGSELECT signo) { + ErtsSigSelInfo *curr = ps->info; + while (curr != NULL) { + if (curr->signo == signo) + return curr; + curr = curr->next; + } + return NULL; +} + +static ErtsSigSelItem * +get_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd) { + ErtsSigSelInfo *info = get_sigsel_info(ps,fd->signo); + ErtsSigSelItem *curr; + + if (info == NULL) + return NULL; + + curr = info->fds; + + while (curr != NULL) { + if (curr->fd->id == fd->id) { + ASSERT(curr->fd->signo == fd->signo); + return curr; + } + curr = curr->next; + } + return NULL; +} + +static ErtsSigSelInfo * +add_sigsel_info(ErtsPollSet ps, ErtsSysFdType fd, + int (*decode)(OseSignal* sig, int* mode)) { + ErtsSigSelInfo *info = SEL_ALLOC(ERTS_ALC_T_POLLSET, + sizeof(ErtsSigSelInfo)); + info->next = ps->info; + info->fds = NULL; + info->signo = fd->signo; + info->decode = decode; + ps->info = info; + ps->sig_count++; + return info; +} + +static ErtsSigSelItem * +add_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd, + int (*decode)(OseSignal* sig, int* mode)) { + ErtsSigSelInfo *info = get_sigsel_info(ps,fd->signo); + ErtsSigSelItem *item = SEL_ALLOC(ERTS_ALC_T_POLLSET, + sizeof(ErtsSigSelItem)); + if (info == NULL) + info = add_sigsel_info(ps, fd, decode); + if (info->decode != decode) { + erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); + erts_dsprintf(dsbufp, "erts_poll_control() inconsistency: multiple resolve_signal functions for same signal (%d)\n", + fd->signo); + erts_send_error_to_logger_nogl(dsbufp); + } + ASSERT(info->decode == decode); + item->next = info->fds; + item->fd = fd; + item->events = 0; + info->fds = item; + ps->item_count++; + return item; +} + +static int del_sigsel_info(ErtsPollSet ps, ErtsSigSelInfo *info) { + ErtsSigSelInfo *curr, *prev; + + if (ps->info == info) { + ps->info = ps->info->next; + } else { + curr = ps->info->next; + prev = ps->info; + + while (curr != info) { + if (curr == NULL) + return 1; + prev = curr; + curr = curr->next; + } + prev->next = curr->next; + } + + ps->sig_count--; + SEL_FREE(ERTS_ALC_T_POLLSET, info); + return 0; +} + +static int del_sigsel_item(ErtsPollSet ps, ErtsSigSelItem *item) { + ErtsSigSelInfo *info = get_sigsel_info(ps,item->fd->signo); + ErtsSigSelItem *curr, *prev; + + ps->item_count--; + ASSERT(ps->item_count >= 0); + + if (info->fds == item) { + info->fds = info->fds->next; + SEL_FREE(ERTS_ALC_T_POLLSET,item); + if (info->fds == NULL) + return del_sigsel_info(ps,info); + return 0; + } + + curr = info->fds->next; + prev = info->fds; + + while (curr != item) { + if (curr == NULL) { + /* We did not find an item to delete so we have to + * increment item count again. + */ + ps->item_count++; + return 1; + } + prev = curr; + curr = curr->next; + } + prev->next = curr->next; + SEL_FREE(ERTS_ALC_T_POLLSET,item); + return 0; +} + +#ifdef ERTS_SMP + +static void update_redir_tables(ErtsPollSet ps) { + struct OS_redir_entry *redir_table; + PROCESS sched_1 = ERTS_SCHEDULER_IX(0)->tid.id; + int i; + redir_table = SEL_ALLOC(ERTS_ALC_T_POLLSET, + sizeof(struct OS_redir_entry)*(ps->sig_count+1)); + + redir_table[0].sig = ps->sig_count+1; + redir_table[0].pid = 0; + + for (i = 1; i < ps->sig_count+1; i++) { + ramlog_printf("Adding 0x%p -> 0x%p to redir table\n",ps->sigs[i],sched_1); + redir_table[i].sig = ps->sigs[i]; + redir_table[i].pid = sched_1; + } + + for (i = 1; i < erts_no_schedulers; i++) { + ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(i); + set_redirection(esdp->tid.id,redir_table); + ramlog_printf("Setting redir table to 0x%p\n",esdp->tid.id); + } + + SEL_FREE(ERTS_ALC_T_POLLSET,redir_table); +} + +#endif + +static int update_sigsel(ErtsPollSet ps) { + ErtsSigSelInfo *info = ps->info; + + int i; + + if (ps->sigs != NULL) + SEL_FREE(ERTS_ALC_T_POLLSET,ps->sigs); + + if (ps->sig_count == 0) { + /* If there are no signals we place a non-valid signal to make sure that + * we do not trigger on a any unrelated signals which are sent to the + * process. + */ + ps->sigs = SEL_ALLOC(ERTS_ALC_T_POLLSET,sizeof(SIGSELECT)*(2)); + ps->sigs[0] = 1; + ps->sigs[1] = ERTS_POLL_INVALID_SIGNO; + return 0; + } + + ps->sigs = SEL_ALLOC(ERTS_ALC_T_POLLSET,sizeof(SIGSELECT)*(ps->sig_count+1)); + ps->sigs[0] = ps->sig_count; + + for (i = 1; info != NULL; i++, info = info->next) + ps->sigs[i] = info->signo; + +#ifdef ERTS_SMP + update_redir_tables(ps); +#endif + + return 0; +} + +static ERTS_INLINE void +wake_poller(ErtsPollSet ps) +{ + erts_aint32_t wakeup_state; + + ERTS_THR_MEMORY_BARRIER; + wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state); + while (wakeup_state != ERTS_POLL_WOKEN_IO_READY + && wakeup_state != ERTS_POLL_WOKEN_INTR) { + erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, + ERTS_POLL_WOKEN_INTR, + wakeup_state); + if (act == wakeup_state) { + wakeup_state = act; + break; + } + wakeup_state = act; + } + if (wakeup_state == ERTS_POLL_SLEEPING) { + /* + * Since we don't know the internals of signal_fsem() we issue + * a memory barrier as a safety precaution ensuring that + * the store we just made to wakeup_state wont be reordered + * with loads in signal_fsem(). + */ + ERTS_THR_MEMORY_BARRIER; + signal_fsem(ps->interrupt); + } +} + +static ERTS_INLINE void +reset_interrupt(ErtsPollSet ps) +{ + /* We need to keep io-ready if set */ + erts_aint32_t wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state); + while (wakeup_state != ERTS_POLL_NOT_WOKEN && + wakeup_state != ERTS_POLL_SLEEPING) { + erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, + ERTS_POLL_NOT_WOKEN, + wakeup_state); + if (wakeup_state == act) + break; + wakeup_state = act; + } + ERTS_THR_MEMORY_BARRIER; +} + +static ERTS_INLINE void +set_interrupt(ErtsPollSet ps) +{ + wake_poller(ps); +} + +void erts_poll_interrupt(ErtsPollSet ps,int set) { + HARDTRACEF("erts_poll_interrupt called!\n"); + + if (!set) + reset_interrupt(ps); + else + set_interrupt(ps); + +} + +void erts_poll_interrupt_timed(ErtsPollSet ps,int set,erts_short_time_t msec) { + HARDTRACEF("erts_poll_interrupt_timed called!\n"); + + if (!set) + reset_interrupt(ps); + else if (erts_smp_atomic32_read_acqb(&ps->timeout) > (erts_aint32_t) msec) + set_interrupt(ps); +} + +ErtsPollEvents erts_poll_control(ErtsPollSet ps, ErtsSysFdType fd, + ErtsPollEvents pe, int on, int* do_wake, + int(*decode)(OseSignal* sig, int* mode)) { + ErtsSigSelItem *curr; + ErtsPollEvents new_events; + int old_sig_count; + + HARDTRACEF( + "%ux: In erts_poll_control, fd = %d, pe = %d, on = %d, *do_wake = %d, curr = 0x%xu", + ps, fd, pe, on, do_wake, curr); + + ERTS_POLLSET_LOCK(ps); + + curr = get_sigsel_item(ps, fd); + old_sig_count = ps->sig_count; + + if (curr == NULL && on) { + curr = add_sigsel_item(ps, fd, decode); + } else if (curr == NULL && !on) { + new_events = ERTS_POLL_EV_NVAL; + goto done; + } + + new_events = curr->events; + + if (pe == 0) { + *do_wake = 0; + goto done; + } + + if (on) { + new_events |= pe; + curr->events = new_events; + } else { + new_events &= ~pe; + curr->events = new_events; + if (new_events == 0 && del_sigsel_item(ps, curr)) { + new_events = ERTS_POLL_EV_NVAL; + goto done; + } + } + + if (ps->sig_count != old_sig_count) { + if (update_sigsel(ps)) + new_events = ERTS_POLL_EV_NVAL; + } +done: + ERTS_POLLSET_UNLOCK(ps); + HARDTRACEF("%ux: Out erts_poll_control", ps); + return new_events; +} + +int erts_poll_wait(ErtsPollSet ps, + ErtsPollResFd pr[], + int *len, + SysTimeval *utvp) { + int res = ETIMEDOUT, no_fds, currid = 0; + OSTIME timeout; + OseSignal *sig; + // HARDTRACEF("%ux: In erts_poll_wait",ps); + if (ps->interrupt == (PROCESS)0) + ps->interrupt = current_process(); + + ASSERT(current_process() == ps->interrupt); + ASSERT(get_fsem(current_process()) == 0); + ASSERT(erts_atomic32_read_nob(&ps->wakeup_state) & + (ERTS_POLL_NOT_WOKEN | ERTS_POLL_WOKEN_INTR)); + /* Max no of spots avable in pr */ + no_fds = *len; + + *len = 0; + + ASSERT(utvp); + + /* erts_printf("Entering erts_poll_wait(), timeout=%d\n", + (int) utvp->tv_sec*1000 + utvp->tv_usec/1000); */ + + timeout = utvp->tv_sec*1000 + utvp->tv_usec/1000; + + if (timeout > ((time_t) ERTS_AINT32_T_MAX)) + timeout = ERTS_AINT32_T_MAX; + erts_smp_atomic32_set_relb(&ps->timeout, (erts_aint32_t) timeout); + + while (currid < no_fds) { + if (timeout > 0) { + erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, + ERTS_POLL_SLEEPING, + ERTS_POLL_NOT_WOKEN); + if (act == ERTS_POLL_NOT_WOKEN) { +#ifdef ERTS_SMP + erts_thr_progress_prepare_wait(NULL); +#endif + sig = receive_fsem(timeout, ps->sigs, 1); +#ifdef ERTS_SMP + erts_thr_progress_finalize_wait(NULL); +#endif + } else { + ASSERT(act == ERTS_POLL_WOKEN_INTR); + sig = OS_RCV_FSEM; + } + } else + sig = receive_w_tmo(0, ps->sigs); + + if (sig == NULL) { + if (timeout > 0) { + erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, + ERTS_POLL_WOKEN_TIMEDOUT, + ERTS_POLL_SLEEPING); + if (act == ERTS_POLL_WOKEN_INTR) + /* Restore fsem as it was signaled but we got a timeout */ + wait_fsem(1); + } else + erts_atomic32_cmpxchg_nob(&ps->wakeup_state, + ERTS_POLL_WOKEN_TIMEDOUT, + ERTS_POLL_NOT_WOKEN); + break; + } else if (sig == OS_RCV_FSEM) { + ASSERT(erts_atomic32_read_nob(&ps->wakeup_state) == ERTS_POLL_WOKEN_INTR); + break; + } + { + ErtsSigSelInfo *info = get_sigsel_info(ps, sig->sig_no); + int mode = -1; + struct erts_sys_fd_type fd = { sig->sig_no, info->decode(sig, &mode) }; + ErtsSigSelItem *item = get_sigsel_item(ps, &fd); + + ASSERT(sig); + if (currid == 0 && timeout > 0) { + erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, + ERTS_POLL_WOKEN_IO_READY, + ERTS_POLL_SLEEPING); + if (act == ERTS_POLL_WOKEN_INTR) { + /* Restore fsem as it was signaled but we got a msg */ + wait_fsem(1); + act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, + ERTS_POLL_WOKEN_IO_READY, + ERTS_POLL_WOKEN_INTR); + } + } else if (currid == 0) { + erts_atomic32_set_nob(&ps->wakeup_state, + ERTS_POLL_WOKEN_IO_READY); + } + + if (item == NULL) { + erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); + erts_dsprintf( + dsbufp, + "erts_poll_wait() failed: found unkown signal id %d (signo %u) " + "(curr_proc 0x%x /sender 0x%x)\n", + fd.id, fd.signo, current_process(), sender(&sig)); + erts_send_error_to_logger_nogl(dsbufp); + timeout = 0; + ASSERT(0); + } else if (mode == -1 && item->events == (ERTS_POLL_EV_IN|ERTS_POLL_EV_OUT)) { + erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); + erts_dsprintf( + dsbufp, + "erts_poll_wait() failed: found ambigous signal id %d (signo %u) " + "(curr_proc 0x%x /sender 0x%x)\n You have to give a specify a mode " + "in the resolve_signal callback for this signal.\n", + fd.id, fd.signo, current_process(), sender(&sig)); + erts_send_error_to_logger_nogl(dsbufp); + timeout = 0; + ASSERT(0); + } else { + int i; + struct erts_sys_fd_type *fd = NULL; + ErtsPollOseMsgList *tl,*new; + + /* Figure out which mode to set and which queue to store + the signal in */ + if (mode == -1) + mode = item->events; + else if (mode == 0) + mode = ERTS_POLL_EV_IN; + else if (mode == 1) + mode = ERTS_POLL_EV_OUT; + else + abort(); + + /* Check if this fd has already been triggered by a previous signal */ + for (i = 0; i < currid;i++) { + if (pr[i].fd == item->fd) { + fd = pr[i].fd; + pr[i].events |= mode; + break; + } + } + + /* First time this fd is triggered */ + if (fd == NULL) { + pr[currid].fd = item->fd; + pr[currid].events = mode; + fd = item->fd; + timeout = 0; + currid++; + } + + /* Insert new signal in approriate list */ + new = erts_alloc(ERTS_ALC_T_FD_SIG_LIST,sizeof(ErtsPollOseMsgList)); + new->next = NULL; + new->data = sig; + + ethr_mutex_lock(&fd->mtx); + if (mode & ERTS_POLL_EV_IN) + tl = fd->imsgs; + else if (mode & ERTS_POLL_EV_OUT) + tl = fd->omsgs; + + if (tl == NULL) { + if (mode & ERTS_POLL_EV_IN) + fd->imsgs = new; + else if (mode & ERTS_POLL_EV_OUT) + fd->omsgs = new; + } else { + while (tl->next != NULL) + tl = tl->next; + tl->next = new; + } + ethr_mutex_unlock(&fd->mtx); + } + + } + } + + { + erts_aint32_t wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state); + + switch (wakeup_state) { + case ERTS_POLL_WOKEN_IO_READY: + res = 0; + break; + case ERTS_POLL_WOKEN_INTR: + res = EINTR; + break; + case ERTS_POLL_WOKEN_TIMEDOUT: + res = ETIMEDOUT; + break; + case ERTS_POLL_NOT_WOKEN: + /* This happens when we get an invalid signal only */ + res = EINVAL; + break; + default: + res = 0; + erl_exit(ERTS_ABORT_EXIT, + "%s:%d: Internal error: Invalid wakeup_state=%d\n", + __FILE__, __LINE__, (int) wakeup_state); + } + } + + erts_atomic32_set_nob(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN); + erts_smp_atomic32_set_nob(&ps->timeout, ERTS_AINT32_T_MAX); + + *len = currid; + + // HARDTRACEF("%ux: Out erts_poll_wait",ps); + return res; +} + +int erts_poll_max_fds(void) +{ + + HARDTRACEF("In/Out erts_poll_max_fds -> %d",max_fds); + return max_fds; +} + +void erts_poll_info(ErtsPollSet ps, + ErtsPollInfo *pip) +{ + Uint size = 0; + Uint num_events = 0; + + size += sizeof(struct ErtsPollSet_); + size += sizeof(ErtsSigSelInfo)*ps->sig_count; + size += sizeof(ErtsSigSelItem)*ps->item_count; + size += sizeof(SIGSELECT)*(ps->sig_count+1); + + pip->primary = "receive_fsem"; + + pip->fallback = NULL; + + pip->kernel_poll = NULL; + + pip->memory_size = size; + + pip->poll_set_size = num_events; + + pip->fallback_poll_set_size = 0; + + pip->lazy_updates = 0; + + pip->pending_updates = 0; + + pip->batch_updates = 0; + + pip->concurrent_updates = 0; + + + pip->max_fds = erts_poll_max_fds(); + HARDTRACEF("%ux: Out erts_poll_info",ps); + +} + +ErtsPollSet erts_poll_create_pollset(void) +{ + ErtsPollSet ps = SEL_ALLOC(ERTS_ALC_T_POLLSET, + sizeof(struct ErtsPollSet_)); + + ps->sigs = NULL; + ps->sig_count = 0; + ps->item_count = 0; + ps->info = NULL; + ps->interrupt = (PROCESS)0; + erts_atomic32_init_nob(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN); + erts_smp_atomic32_init_nob(&ps->timeout, ERTS_AINT32_T_MAX); +#ifdef ERTS_SMP + erts_smp_mtx_init(&ps->mtx, "pollset"); +#endif + update_sigsel(ps); + HARDTRACEF("%ux: Out erts_poll_create_pollset",ps); + return ps; +} + +void erts_poll_destroy_pollset(ErtsPollSet ps) +{ + ErtsSigSelInfo *info; + for (info = ps->info; ps->info != NULL; info = ps->info, ps->info = ps->info->next) { + ErtsSigSelItem *item; + for (item = info->fds; info->fds != NULL; item = info->fds, info->fds = info->fds->next) + SEL_FREE(ERTS_ALC_T_POLLSET, item); + SEL_FREE(ERTS_ALC_T_POLLSET, info); + } + + SEL_FREE(ERTS_ALC_T_POLLSET,ps->sigs); + +#ifdef ERTS_SMP + erts_smp_mtx_destroy(&ps->mtx); +#endif + + SEL_FREE(ERTS_ALC_T_POLLSET,ps); +} + +void erts_poll_init(void) +{ + HARDTRACEF("In %s", __FUNCTION__); + max_fds = 256; + + HARDTRACEF("Out %s", __FUNCTION__); +} diff --git a/erts/emulator/sys/ose/gcc_lm.lcf b/erts/emulator/sys/ose/gcc_lm.lcf new file mode 100644 index 0000000000..42b6f89851 --- /dev/null +++ b/erts/emulator/sys/ose/gcc_lm.lcf @@ -0,0 +1,146 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH("i386") +ENTRY("crt0_lm") +MEMORY +{ + rom : ORIGIN = 0x01000000, LENGTH = 0x01000000 + ram : ORIGIN = 0x02000000, LENGTH = 0x01000000 +} +PHDRS +{ + ph_conf PT_LOAD ; + ph_rom PT_LOAD ; + ph_ram PT_LOAD ; +} +SECTIONS +{ + .text : + { + *(.text_first) + *(.text) + *(.text.*) + *(.stub) + *(oscode) + *(.init*) + *(.fini*) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + *(.glue_7t) + *(.glue_7) + } > rom :ph_rom = 0 + .ose_sfk_biosentry : + { + *(.ose_sfk_biosentry) + } > rom :ph_rom + .ctors : + { + __CTOR_LIST__ = .; + *(.ctors) + *(SORT(.ctors.*)) + __CTOR_END__ = .; + } > rom :ph_rom + .dtors : + { + __DTOR_LIST__ = .; + *(.dtors) + *(SORT(.dtors.*)) + __DTOR_END__ = .; + } > rom :ph_rom + OSESYMS : + { + *(.osesyms) + } > rom :ph_rom + .rodata : + { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + } > rom :ph_rom + .eh_frame : + { + __EH_FRAME_BEGIN__ = .; + *(.eh_frame) + LONG(0) + __EH_FRAME_END__ = .; + } > rom :ph_rom + .gcc_except_table : + { + *(.gcc_except_table) + } > rom :ph_rom + .sdata2 : + { + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + } > rom :ph_rom + .sbss2 : + { + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + } > rom :ph_rom + LMCONF : + { + obj/?*?/ose_confd.o(.rodata) + *(LMCONF) + } > rom :ph_conf + .data : + { + LONG(0xDEADBABE) + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + . = ALIGN(0x10); + } > ram :ph_ram = 0 + .sdata2 : + { + _SDA2_BASE_ = .; + *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) + }> ram :ph_ram + .sdata : + { + _SDA_BASE_ = .; + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + } > ram :ph_ram + .sbss : + { + *(.sbss) + *(.sbss.*) + *(.scommon) + *(.gnu.linkonce.sb.*) + } > ram :ph_ram + .bss (NOLOAD) : + { + *(.bss) + *(.bss.*) + *(COMMON) + *(.gnu.linkonce.b.*) + *(.osvars) + } > ram :ph_ram + .ignore (NOLOAD) : + { + *(.rel.dyn) + } > ram :ph_ram + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} +__OSESYMS_START = ADDR(OSESYMS); +__OSESYMS_END = ADDR(OSESYMS) + SIZEOF(OSESYMS); diff --git a/erts/emulator/sys/ose/gcc_lm_ppc.lcf b/erts/emulator/sys/ose/gcc_lm_ppc.lcf new file mode 100644 index 0000000000..a2399c93da --- /dev/null +++ b/erts/emulator/sys/ose/gcc_lm_ppc.lcf @@ -0,0 +1,219 @@ +/******************************************************************************* + * gcc_lm_ppc.lcf: GCC linker control file for PowerPC load modules + * @(#) $FilePath: /vobs/ose5/system/refsys/compilers/gcc_lm_ppc.lcf $ + * @(#) $FileRevision: /main/tb_current_ose5/10 $ + * $Author: joka $$Date: 01/21/13 16:35:41 $ + * $Copyright: (C) 2006 by Enea AB. All rights reserved. $ + ******************************************************************************/ + +OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc") +OUTPUT_ARCH("powerpc") + +ENTRY("crt0_lm") + +/* Note: + * You may have to increase the length of the "rom" memory region and the + * origin and length of the "ram" memory region below depending on the size + * of the code and data in your load module. + */ + +MEMORY +{ + conf : ORIGIN = 0x00100000, LENGTH = 0x00030000 + rom : ORIGIN = 0x01000000, LENGTH = 0x01000000 + ram : ORIGIN = 0x03000000, LENGTH = 0x01000000 +} + +PHDRS +{ + ph_conf PT_LOAD ; + ph_rom PT_LOAD ; + ph_ram PT_LOAD ; +} + +SECTIONS +{ +/*--------------------------------------------------------------------------- + * Load module configuration area + *-------------------------------------------------------------------------*/ + + /* Load module configuration section. */ + LMCONF : + { + obj/?*?/ose_confd.o(.rodata) + *(LMCONF) + } > conf :ph_conf + +/*--------------------------------------------------------------------------- + * Read-only area + *-------------------------------------------------------------------------*/ + + /* Code section. */ + .text : + { + *(.text) + *(.text.*) + *(.stub) + *(oscode) + *(.init*) + *(.fini*) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + } > rom :ph_rom = 0 + + /* OSE symbols section. */ + OSESYMS : + { + *(.osesyms) + } > rom :ph_rom + + /* Read-only data section. */ + .rodata : + { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + } > rom :ph_rom + + /* C++ exception handling section. */ + .eh_frame : + { + __EH_FRAME_BEGIN__ = .; + *(.eh_frame) + LONG(0) + __EH_FRAME_END__ = .; + } > rom :ph_rom + + /* C++ exception handling section. */ + .gcc_except_table : + { + *(.gcc_except_table .gcc_except_table.*) + } > rom :ph_rom + + /* PowerPC EABI initialized read-only data section. */ + .sdata2 : + { + PROVIDE (_SDA2_BASE_ = .); + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + } > rom :ph_rom + + /* PowerPC EABI uninitialized read-only data section. */ + .sbss2 : + { + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + } > rom :ph_rom + +/*--------------------------------------------------------------------------- + * Read-write area + *-------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------- + * Initialized data (copied by PM) + *-----------------------------------------------------------------*/ + + /* Data section. */ + .data : + { + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } > ram :ph_ram + + /* C++ constructor section. */ + .ctors : + { + __CTOR_LIST__ = .; + *(.ctors) + *(SORT(.ctors.*)) + __CTOR_END__ = .; + } > ram :ph_ram + + /* C++ destructor section. */ + .dtors : + { + __DTOR_LIST__ = .; + *(.dtors) + *(SORT(.dtors.*)) + __DTOR_END__ = .; + } > ram :ph_ram + + + /* Small data section. */ + .sdata ALIGN(0x10) : + { + PROVIDE (_SDA_BASE_ = .); + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + } > ram :ph_ram + + /*------------------------------------------------------------------- + * Uninitialized data (cleared by PM) + *-----------------------------------------------------------------*/ + + /* Small bss section. */ + .sbss : + { + *(.sbss) + *(.sbss.*) + *(.scommon) + *(.gnu.linkonce.sb.*) + } > ram :ph_ram + + /* Bss section. */ + .bss : + { + *(.bss) + *(.bss.*) + *(COMMON) + *(.gnu.linkonce.b.*) + } > ram :ph_ram + +/*--------------------------------------------------------------------------- + * Debug information + *-------------------------------------------------------------------------*/ + + /* + * Stabs debug sections. + */ + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + + /* + * DWARF debug sections. + */ + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} diff --git a/erts/emulator/sys/ose/gcc_lm_x86_4.4.3.lcf b/erts/emulator/sys/ose/gcc_lm_x86_4.4.3.lcf new file mode 100644 index 0000000000..d64fd91604 --- /dev/null +++ b/erts/emulator/sys/ose/gcc_lm_x86_4.4.3.lcf @@ -0,0 +1,277 @@ +/* COPYRIGHT-ENEA-EXAMPLE-R2 * +************************************************************************** +* Copyright (C) 2010 by Enea Software AB. +* All rights reserved. +* +* This Example is furnished under a Software License Agreement and +* may be used only in accordance with the terms of such agreement. +* No title to and ownership of the Example is hereby transferred. +* +* The information in this Example is subject to change +* without notice and should not be construed as a commitment +* by Enea Software AB. +* +* DISCLAIMER +* This Example is delivered "AS IS", consequently +* Enea Software AB makes no representations or warranties, +* expressed or implied, for the Example. +************************************************************************** +* COPYRIGHT-END */ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH("i386") + +ENTRY("crt0_lm") + +MEMORY +{ + rom : ORIGIN = 0x01000000, LENGTH = 0x01000000 + ram : ORIGIN = 0x02000000, LENGTH = 0x01000000 +} + +PHDRS +{ + ph_conf PT_LOAD ; + ph_rom PT_LOAD ; + ph_ram PT_LOAD ; +} + +SECTIONS +{ +/*--------------------------------------------------------------------------- + * Read-only area + *-------------------------------------------------------------------------*/ + + /* Code section. */ + .text : + { + *(.text_first) + *(.text) + *(.text.*) + *(.stub) + *(oscode) + *(.init*) + *(.fini*) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + *(.glue_7t) + *(.glue_7) + } > rom :ph_rom = 0 + + + .ose_sfk_biosentry : + { + *(.ose_sfk_biosentry) + } > rom :ph_rom + + /* C++ constructor section. */ + .ctors : + { + __CTOR_LIST__ = .; + *(.ctors) + *(SORT(.ctors.*)) + __CTOR_END__ = .; + } > rom :ph_rom + + /* C++ destructor section. */ + .dtors : + { + __DTOR_LIST__ = .; + *(.dtors) + *(SORT(.dtors.*)) + __DTOR_END__ = .; + } > rom :ph_rom + + /* OSE symbols section. */ + OSESYMS : + { + *(.osesyms) + } > rom :ph_rom + + .plt : + { + *(.plt) + *(.iplt) + } > rom :ph_rom + + + /* Read-only data section. */ + .rodata : + { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + } > rom :ph_rom + + /* C++ exception handling section. */ + .eh_frame : + { + __EH_FRAME_BEGIN__ = .; + *(.eh_frame) + LONG(0) + __EH_FRAME_END__ = .; + } > rom :ph_rom + + /* C++ exception handling section. */ + .gcc_except_table : + { + *(.gcc_except_table) + } > rom :ph_rom + + /* PowerPC EABI initialized read-only data section. */ + .sdata2 : + { + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + } > rom :ph_rom + + /* PowerPC EABI uninitialized read-only data section. */ + .sbss2 : + { + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + } > rom :ph_rom + + /* Dynamic relocations */ + .rel.dyn : + { + *(.rel.init) + *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) + *(.rel.fini) + *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) + *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) + *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) + *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) + *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) + *(.rel.ctors) + *(.rel.dtors) + *(.rel.got) + *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) + *(.rel.ifunc) + } > rom :ph_rom + +/*--------------------------------------------------------------------------- + * Load module configuration area + *-------------------------------------------------------------------------*/ + + /* Load module configuration section. */ + LMCONF : + { + obj/?*?/ose_confd.o(.rodata) + *(LMCONF) + } > rom :ph_conf + + +/*--------------------------------------------------------------------------- + * Read-write area + *-------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------- + * Initialized data (copied by PM ) + *-----------------------------------------------------------------*/ + + /* Data section. */ + .data : + { + LONG(0xDEADBABE) + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + . = ALIGN(0x10); + } > ram :ph_ram = 0 + + .got : + { + *(.got.plt) + *(.got) + } > rom :ph_ram + + /* Global offset table for dynamically linked procedures. */ + .got.plt : + { + *(.got.plt) + *(.igot.plt) + } > rom :ph_ram + + /* Small data section. */ + .sdata2 : + { + _SDA2_BASE_ = .; + *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) + }> ram :ph_ram + .sdata : + { + _SDA_BASE_ = .; + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + } > ram :ph_ram + + /*------------------------------------------------------------------- + * Uninitialized data (cleared by PM ) + *-----------------------------------------------------------------*/ + + /* Small bss section. */ + .sbss : + { + *(.sbss) + *(.sbss.*) + *(.scommon) + *(.gnu.linkonce.sb.*) + } > ram :ph_ram + + /* Bss section. */ + .bss (NOLOAD) : + { + *(.bss) + *(.bss.*) + *(COMMON) + *(.gnu.linkonce.b.*) + *(.osvars) + } > ram :ph_ram + + /* Ignore unwanted sections that are not used in OSE. */ + .ignore (NOLOAD) : + { + *(.rel.dyn) + } > ram :ph_ram + +/*--------------------------------------------------------------------------- + * Debug information + *-------------------------------------------------------------------------*/ + + /* + * DWARF debug sections. + */ + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + +} + +/* Symbols used by DDA. */ +__OSESYMS_START = ADDR(OSESYMS); +__OSESYMS_END = ADDR(OSESYMS) + SIZEOF(OSESYMS); diff --git a/erts/emulator/sys/ose/gcc_lm_x86_4.6.3.lcf b/erts/emulator/sys/ose/gcc_lm_x86_4.6.3.lcf new file mode 100644 index 0000000000..bb357afcb1 --- /dev/null +++ b/erts/emulator/sys/ose/gcc_lm_x86_4.6.3.lcf @@ -0,0 +1,252 @@ +/******************************************************************************* + * gcc_lm_x86.lcf: GCC linker control file for x86 load modules + * @(#) $FilePath: /vobs/ose5/system/refsys/compilers/gcc_lm_x86.lcf $ + * @(#) $FileRevision: /main/tb_current_ose5/9 $ + * $Author: rand $$Date: 10/29/12 17:45:04 $ + * $Copyright: (C) 2006 by Enea AB. All rights reserved. $ + ******************************************************************************/ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH("i386") + +ENTRY("crt0_lm") + +/* Note: + * You may have to increase the length of the "rom" memory region below + * depending on the size of the code and data in your load module. + */ + +MEMORY +{ + conf : ORIGIN = 0x00300000, LENGTH = 0x00001000 + rom : ORIGIN = 0x00000000, LENGTH = 0x00300000 +} + +PHDRS +{ + ph_conf PT_LOAD ; + ph_rom PT_LOAD ; + ph_ram PT_LOAD ; +} + +SECTIONS +{ +/*--------------------------------------------------------------------------- + * Load module configuration area + *-------------------------------------------------------------------------*/ + + /* Load module configuration section. */ + LMCONF : + { + dummy = ALIGN(0x10000); + */?*?/*ose_confd.o(LMCONF*) + *(LMCONF*) + *ose_confd.o(.rodata) + } > conf :ph_conf + +/*--------------------------------------------------------------------------- + * Read-only area + *-------------------------------------------------------------------------*/ + + /* Init section. */ + .init : + { + KEEP(*(.init)) + } > rom :ph_rom = 0x90909090 + + /* Code section. */ + .text : + { + *(.text) + *(.text.*) + *(.stub) + *(oscode) + *(.init*) + *(.fini*) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + } > rom :ph_rom = 0 + + /* Read-only data section. */ + .rodata : + { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + } > rom :ph_rom + + /* OSE symbols section. */ + OSESYMS : + { + *(.osesyms) + } > rom :ph_rom + + /* Procedure linkage table section. */ + .plt : + { + *(.plt) + *(.iplt) + } > rom :ph_rom + + /* Dynamic relocations */ + .rel.dyn : + { + *(.rel.init) + *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) + *(.rel.fini) + *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) + *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) + *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) + *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) + *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) + *(.rel.ctors) + *(.rel.dtors) + *(.rel.got) + *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) + *(.rel.ifunc) + } > rom :ph_rom + +/*--------------------------------------------------------------------------- + * Read-write area + *-------------------------------------------------------------------------*/ + + /* C++ constructor section. */ + .ctors : + { + __CTOR_LIST__ = .; + *(.ctors) + *(SORT(.ctors.*)) + __CTOR_END__ = .; + } > rom :ph_rom + + /* C++ destructor section. */ + .dtors : + { + __DTOR_LIST__ = .; + *(.dtors) + *(SORT(.dtors.*)) + __DTOR_END__ = .; + } > rom :ph_rom + + /* C++ exception handling section. */ + .eh_frame : + { + __EH_FRAME_BEGIN__ = .; + *(.eh_frame) + LONG(0) + __EH_FRAME_END__ = .; + } > rom :ph_rom + + /*------------------------------------------------------------------- + * Initialized data (copied by PM) + *-----------------------------------------------------------------*/ + + /* Data section. */ + .data : + { + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } > rom :ph_ram + + /* Global offset table section. */ + .got : + { + *(.got.plt) + *(.got) + } > rom :ph_ram + + /* Global offset table for dynamically linked procedures. */ + .got.plt : + { + *(.got.plt) + *(.igot.plt) + } > rom :ph_ram + + /* SFK BIOS entry section. */ + .ose_sfk_biosentry : + { + *(.ose_sfk_biosentry) + } > rom :ph_ram + + /* C++ exception handling section. */ + .gcc_except_table : + { + *(.gcc_except_table .gcc_except_table.*) + } > rom :ph_ram + + /* Small data section. */ + .sdata ALIGN(0x10) : + { + PROVIDE (_SDA_BASE_ = .); + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + } > rom :ph_ram + + /*------------------------------------------------------------------- + * Uninitialized data (cleared by PM) + *-----------------------------------------------------------------*/ + + /* Bss section. */ + .bss : + { + *(.bss) + *(.bss.*) + *(COMMON) + *(.gnu.linkonce.b.*) + } > rom :ph_ram + + /* Small bss section. */ + .sbss ALIGN(0x10) : + { + *(.sbss) + *(.sbss.*) + *(.scommon) + *(.gnu.linkonce.sb.*) + } > rom :ph_ram + +/*--------------------------------------------------------------------------- + * Debug information + *-------------------------------------------------------------------------*/ + + /* + * Stabs debug sections. + */ + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + + /* + * DWARF debug sections. + */ + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c new file mode 100644 index 0000000000..c475aafe38 --- /dev/null +++ b/erts/emulator/sys/ose/sys.c @@ -0,0 +1,1820 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * + * The 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 + +#ifdef ISC32 +#define _POSIX_SOURCE +#define _XOPEN_SOURCE +#endif + +#include /* ose*/ +#include +/* ose +#include +#include +#include +*/ + +#include +#include +#include + +#ifdef ISC32 +#include +#endif + +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#define ERTS_WANT_BREAK_HANDLING +#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */ +#include "sys.h" +#include "erl_thr_progress.h" + +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +#define __DARWIN__ 1 +#endif + +#ifdef USE_THREADS +#include "erl_threads.h" +#endif + +#include "erl_mseg.h" + +/*ose*/ +#include "unistd.h" +#include "efs.h" +#include "erl_printf.h" +#if 0 +#define TRACE do { \ + erts_fprintf(stderr, " %s / %d / pid 0x%x\n", __FUNCTION__, __LINE__, current_process()); \ + } while(0) +#else +#define TRACE do {} while(0) +#endif +/*ose*/ + +extern char **environ; +static erts_smp_rwmtx_t environ_rwmtx; + +#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O + * vector sock_sendv(). + */ +/* + * Don't need global.h, but bif_table.h (included by bif.h), + * won't compile otherwise + */ +#include "global.h" +#include "bif.h" + +#include "erl_sys_driver.h" +#include "erl_check_io.h" +#include "erl_cpu_topology.h" + +#ifndef DISABLE_VFORK +#define DISABLE_VFORK 0 +#endif + +/* The priority for reader/writer processes */ +#define FD_PROC_PRI 20 + +/* + * [OTP-3906] + * Solaris signal management gets confused when threads are used and a + * lot of child processes dies. The confusion results in that SIGCHLD + * signals aren't delivered to the emulator which in turn results in + * a lot of defunct processes in the system. + * + * The problem seems to appear when a signal is frequently + * blocked/unblocked at the same time as the signal is frequently + * propagated. The child waiter thread is a workaround for this problem. + * The SIGCHLD signal is always blocked (in all threads), and the child + * waiter thread fetches the signal by a call to sigwait(). See + * child_waiter(). + */ + +typedef struct ErtsSysReportExit_ ErtsSysReportExit; +struct ErtsSysReportExit_ { + ErtsSysReportExit *next; + Eterm port; + int pid; + int ifd; + int ofd; + ErlDrvEvent in_sig_descr; + ErlDrvEvent out_sig_descr; +}; + +/* This data is shared by these drivers - initialized by spawn_init() */ +static struct driver_data { + ErlDrvPort port_num; + int ofd, packet_bytes; + ErtsSysReportExit *report_exit; + int pid; + int alive; + int status; + ErlDrvEvent in_sig_descr; + ErlDrvEvent out_sig_descr; + PROCESS in_proc; + PROCESS out_proc; + ErlDrvPDL pdl; +} *driver_data; /* indexed by fd */ + +static ErtsSysReportExit *report_exit_list; + +extern int driver_interrupt(int, int); +extern void do_break(void); + +extern void erl_sys_args(int*, char**); + +/* The following two defs should probably be moved somewhere else */ + +extern void erts_sys_init_float(void); + +extern void erl_crash_dump(char* file, int line, char* fmt, ...); + +#define DIR_SEPARATOR_CHAR '/' + +#if defined(DEBUG) +#define ERL_BUILD_TYPE_MARKER ".debug" +#else /* opt */ +#define ERL_BUILD_TYPE_MARKER +#endif + +#define CHILD_SETUP_PROG_NAME "child_setup" ERL_BUILD_TYPE_MARKER + +#ifdef DEBUG +static int debug_log = 0; +#endif + +#ifdef ERTS_SMP +static erts_smp_atomic32_t have_prepared_crash_dump; +#define ERTS_PREPARED_CRASH_DUMP \ + ((int) erts_smp_atomic32_xchg_nob(&have_prepared_crash_dump, 1)) +#else +static volatile int have_prepared_crash_dump; +#define ERTS_PREPARED_CRASH_DUMP \ + (have_prepared_crash_dump++) +#endif + +static erts_smp_atomic_t sys_misc_mem_sz; + +#if defined(ERTS_SMP) +erts_mtx_t chld_stat_mtx; +#endif + +#if defined(ERTS_SMP) /* ------------------------------------------------- */ +#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx) +#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx) + +#else /* ------------------------------------------------------------------- */ +#define CHLD_STAT_LOCK +#define CHLD_STAT_UNLOCK +static volatile int children_died; +#endif + + +static struct fd_data { + char pbuf[4]; /* hold partial packet bytes */ + int psz; /* size of pbuf */ + char *buf; + char *cpos; + int sz; + int remain; /* for input on fd */ +} *fd_data; /* indexed by fd */ + +/********************* General functions ****************************/ + +/* This is used by both the drivers and general I/O, must be set early */ +static int max_files = -1; + +/* + * a few variables used by the break handler + */ +#ifdef ERTS_SMP +erts_smp_atomic32_t erts_break_requested; +#define ERTS_SET_BREAK_REQUESTED \ + erts_smp_atomic32_set_nob(&erts_break_requested, (erts_aint32_t) 1) +#define ERTS_UNSET_BREAK_REQUESTED \ + erts_smp_atomic32_set_nob(&erts_break_requested, (erts_aint32_t) 0) +#else +volatile int erts_break_requested = 0; +#define ERTS_SET_BREAK_REQUESTED (erts_break_requested = 1) +#define ERTS_UNSET_BREAK_REQUESTED (erts_break_requested = 0) +#endif +/* set early so the break handler has access to initial mode */ +static struct termios initial_tty_mode; +static int replace_intr = 0; +/* assume yes initially, ttsl_init will clear it */ +int using_oldshell = 1; + +static void +init_check_io(void) +{ + erts_init_check_io(); + max_files = erts_check_io_max_files(); +} + +#ifdef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT +#define ERTS_CHK_IO_AS_INTR() erts_check_io_async_sig_interrupt() +#else +#define ERTS_CHK_IO_AS_INTR() erts_check_io_interrupt(1) +#endif +#define ERTS_CHK_IO_INTR erts_check_io_interrupt +#define ERTS_CHK_IO_INTR_TMD erts_check_io_interrupt_timed +#define ERTS_CHK_IO erts_check_io +#define ERTS_CHK_IO_SZ erts_check_io_size + + +void +erts_sys_schedule_interrupt(int set) +{ + ERTS_CHK_IO_INTR(set); +} + +#ifdef ERTS_SMP +void +erts_sys_schedule_interrupt_timed(int set, erts_short_time_t msec) +{ + ERTS_CHK_IO_INTR_TMD(set, msec); +} +#endif + +Uint +erts_sys_misc_mem_sz(void) +{ + Uint res = ERTS_CHK_IO_SZ(); + res += erts_smp_atomic_read_mb(&sys_misc_mem_sz); + return res; +} + +/* + * reset the terminal to the original settings on exit + */ +void sys_tty_reset(int exit_code) +{ + if (using_oldshell && !replace_intr) { + SET_BLOCKING(0); + } + else if (isatty(0)) { + tcsetattr(0,TCSANOW,&initial_tty_mode); + } +} + +#ifdef USE_THREADS + +typedef struct { + int sched_bind_data; +} erts_thr_create_data_t; + +/* + * thr_create_prepare() is called in parent thread before thread creation. + * Returned value is passed as argument to thr_create_cleanup(). + */ +static void * +thr_create_prepare(void) +{ + erts_thr_create_data_t *tcdp; + + tcdp = erts_alloc(ERTS_ALC_T_TMP, sizeof(erts_thr_create_data_t)); + + tcdp->sched_bind_data = erts_sched_bind_atthrcreate_prepare(); + + return (void *) tcdp; +} + + +/* thr_create_cleanup() is called in parent thread after thread creation. */ +static void +thr_create_cleanup(void *vtcdp) +{ + erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp; + + erts_sched_bind_atthrcreate_parent(tcdp->sched_bind_data); + + erts_free(ERTS_ALC_T_TMP, tcdp); +} + +static void +thr_create_prepare_child(void *vtcdp) +{ + erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp; + +#ifdef ERTS_ENABLE_LOCK_COUNT + erts_lcnt_thread_setup(); +#endif + + erts_sched_bind_atthrcreate_child(tcdp->sched_bind_data); +} + +#endif /* #ifdef USE_THREADS */ + +void +erts_sys_pre_init(void) +{ + erts_printf_add_cr_to_stdout = 1; + erts_printf_add_cr_to_stderr = 1; +#ifdef USE_THREADS + { + erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER; + + eid.thread_create_child_func = thr_create_prepare_child; + /* Before creation in parent */ + eid.thread_create_prepare_func = thr_create_prepare; + /* After creation in parent */ + eid.thread_create_parent_func = thr_create_cleanup, + + erts_thr_init(&eid); + + report_exit_list = NULL; + +#ifdef ERTS_ENABLE_LOCK_COUNT + erts_lcnt_init(); +#endif + +#if defined(ERTS_SMP) + erts_mtx_init(&chld_stat_mtx, "child_status"); +#endif + } +#ifdef ERTS_SMP + erts_smp_atomic32_init_nob(&erts_break_requested, 0); + erts_smp_atomic32_init_nob(&have_prepared_crash_dump, 0); +#else + erts_break_requested = 0; + have_prepared_crash_dump = 0; +#endif +#if !defined(ERTS_SMP) + children_died = 0; +#endif +#endif /* USE_THREADS */ + erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0); +} + +void +erl_sys_init(void) +{ + +#ifdef USE_SETLINEBUF + setlinebuf(stdout); +#else + setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ); +#endif + + erts_sys_init_float(); + + /* we save this so the break handler can set and reset it properly */ + /* also so that we can reset on exit (break handler or not) */ + if (isatty(0)) { + tcgetattr(0,&initial_tty_mode); + } + tzset(); /* Required at least for NetBSD with localtime_r() */ +} + +static ERTS_INLINE int +prepare_crash_dump(int secs) +{ +#define NUFBUF (3) + int i, max; + char env[21]; /* enough to hold any 64-bit integer */ + size_t envsz; + /*DeclareTmpHeapNoproc(heap,NUFBUF);*/ + /*Eterm *hp = heap;*/ + /*Eterm list = NIL;*/ + int has_heart = 0; + + UseTmpHeapNoproc(NUFBUF); + + if (ERTS_PREPARED_CRASH_DUMP) + return 0; /* We have already been called */ + + + /* Positive secs means an alarm must be set + * 0 or negative means no alarm + * + * Set alarm before we try to write to a port + * we don't want to hang on a port write with + * no alarm. + * + */ + +#if 0 /*ose TBD!!!*/ + if (secs >= 0) { + alarm((unsigned int)secs); + } +#endif + + /* Make sure we unregister at epmd (unknown fd) and get at least + one free filedescriptor (for erl_crash.dump) */ + + max = max_files; + if (max < 1024) + max = 1024; + for (i = 3; i < max; i++) { + close(i); + } + + envsz = sizeof(env); + i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz); + if (i >= 0) { + int nice_val; + nice_val = i != 0 ? 0 : atoi(env); + if (nice_val > 39) { + nice_val = 39; + } + set_pri(nice_val); + } + + UnUseTmpHeapNoproc(NUFBUF); +#undef NUFBUF + return has_heart; +} + +int erts_sys_prepare_crash_dump(int secs) +{ + return prepare_crash_dump(secs); +} + +static ERTS_INLINE void +break_requested(void) +{ + /* + * just set a flag - checked for and handled by + * scheduler threads erts_check_io() (not signal handler). + */ +#ifdef DEBUG + fprintf(stderr,"break!\n"); +#endif + if (ERTS_BREAK_REQUESTED) + erl_exit(ERTS_INTR_EXIT, ""); + + ERTS_SET_BREAK_REQUESTED; + ERTS_CHK_IO_AS_INTR(); /* Make sure we don't sleep in poll */ +} + +/* Disable break */ +void erts_set_ignore_break(void) { + +} + +/* Don't use ctrl-c for break handler but let it be + used by the shell instead (see user_drv.erl) */ +void erts_replace_intr(void) { + struct termios mode; + + if (isatty(0)) { + tcgetattr(0, &mode); + + /* here's an example of how to replace ctrl-c with ctrl-u */ + /* mode.c_cc[VKILL] = 0; + mode.c_cc[VINTR] = CKILL; */ + + mode.c_cc[VINTR] = 0; /* disable ctrl-c */ + tcsetattr(0, TCSANOW, &mode); + replace_intr = 1; + } +} + +void init_break_handler(void) +{ + +} + +int sys_max_files(void) +{ + return(max_files); +} + + +/************************** OS info *******************************/ + +/* Used by erlang:info/1. */ +/* (This code was formerly in drv.XXX/XXX_os_drv.c) */ + +char os_type[] = "ose"; + +void +os_flavor(char* namebuf, /* Where to return the name. */ + unsigned size) /* Size of name buffer. */ +{ +#if 0 + struct utsname uts; /* Information about the system. */ + char* s; + + (void) uname(&uts); + for (s = uts.sysname; *s; s++) { + if (isupper((int) *s)) { + *s = tolower((int) *s); + } + } + strcpy(namebuf, uts.sysname); +#else + strncpy(namebuf, "release", size); +#endif +} + +void +os_version(pMajor, pMinor, pBuild) +int* pMajor; /* Pointer to major version. */ +int* pMinor; /* Pointer to minor version. */ +int* pBuild; /* Pointer to build number. */ +{ + *pMajor = 5; + *pMinor = 7; + *pBuild = 0; +} + +void init_getenv_state(GETENV_STATE *state) +{ + erts_smp_rwmtx_rlock(&environ_rwmtx); + *state = NULL; +} + +char **environ; /*ose - needs replacement*/ + +char *getenv_string(GETENV_STATE *state0) +{ + char **state = (char **) *state0; + char *cp; + + ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx)); + + if (state == NULL) + state = environ; + + cp = *state++; + *state0 = (GETENV_STATE) state; + + return cp; +} + +void fini_getenv_state(GETENV_STATE *state) +{ + *state = NULL; + erts_smp_rwmtx_runlock(&environ_rwmtx); +} + + +/************************** Port I/O *******************************/ + + + +/* I. Common stuff */ + +/* + * Decreasing the size of it below 16384 is not allowed. + */ +#define SYSDRIVERASYNCSIG 1000 +#define SYSDRIVERCONFSIG 1001 + +typedef struct SysDriverAsyncSignal_ { + SIGSELECT sig_no; + int type; + byte *buff; + ssize_t res; + int errno_copy; +} SysDriverAsyncSignal; + +typedef struct SysDriverConfSignal_ { + SIGSELECT sig_no; + int fd; + PROCESS parent; +} SysDriverConfSignal; + +union SIGNAL { + SIGSELECT sig_no; + SysDriverAsyncSignal sys_async; + SysDriverConfSignal conf_async; +}; + +/* II. The spawn/fd drivers */ + +#define ERTS_SYS_READ_BUF_SZ (64*1024) + +/* Driver interfaces */ +static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*); +static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*); +static ErlDrvSSizeT fd_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT, + char **, ErlDrvSizeT); +static int spawn_init(void); +static void fd_stop(ErlDrvData); +static void erl_stop(ErlDrvData); +static void ready_input(ErlDrvData, ErlDrvEvent); +static void ready_output(ErlDrvData, ErlDrvEvent); +static void output(ErlDrvData, char*, ErlDrvSizeT); +static void outputv(ErlDrvData, ErlIOVec*); +static void stop_select(ErlDrvEvent, void*); +static int resolve_signal(OseSignal* sig, int *mode) { + return sig->sig_no == SYSDRIVERASYNCSIG ? sig->sys_async.type : -1; +} + +OS_PROCESS(fd_writer_process); +OS_PROCESS(fd_reader_process); + +struct erl_drv_entry spawn_driver_entry = { + spawn_init, + spawn_start, + erl_stop, + output, + ready_input, + ready_output, + "spawn", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL, NULL, + stop_select, + resolve_signal +}; +struct erl_drv_entry fd_driver_entry = { + NULL, + fd_start, + fd_stop, + output, + ready_input, + ready_output, + "fd", + NULL, + NULL, + fd_control, + 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, + resolve_signal +}; + +static int set_driver_data(ErlDrvPort port_num, + int ifd, + int ofd, + int packet_bytes, + int read_write, + int exit_status, + int pid) +{ + Port *prt; + ErtsSysReportExit *report_exit; + OseSignal *sig; + + /*erts_fprintf(stderr, " %s / pid %x / ofd %d / ifd %d\n", __FUNCTION__, current_process(), ofd, ifd);*/ + + + if (!exit_status) + report_exit = NULL; + else { + report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT, + sizeof(ErtsSysReportExit)); + report_exit->next = report_exit_list; + report_exit->port = erts_drvport2id(port_num); + report_exit->pid = pid; + report_exit->ifd = read_write & DO_READ ? ifd : -1; + report_exit->ofd = read_write & DO_WRITE ? ofd : -1; + + if (read_write & DO_READ) + report_exit->in_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG, ifd); + if (read_write & DO_WRITE) + report_exit->out_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG, ofd); + + report_exit_list = report_exit; + } + + prt = erts_drvport2port(port_num); + if (prt != ERTS_INVALID_ERL_DRV_PORT) + prt->os_pid = pid; + + if (read_write & DO_READ) { + driver_data[ifd].packet_bytes = packet_bytes; + driver_data[ifd].port_num = port_num; + driver_data[ifd].report_exit = report_exit; + driver_data[ifd].pid = pid; + driver_data[ifd].alive = 1; + driver_data[ifd].status = 0; + driver_data[ifd].in_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG,ifd); + + driver_data[ifd].in_proc = create_process(OS_PRI_PROC,"beam_fd_reader", + fd_reader_process, 0x800, + FD_PROC_PRI, 0, 0, NULL, 0, 0); + efs_clone(driver_data[ifd].in_proc); + sig = alloc(sizeof(SysDriverConfSignal), SYSDRIVERCONFSIG); + sig->conf_async.fd = ifd; + sig->conf_async.parent = current_process(); + send(&sig, driver_data[ifd].in_proc); + start(driver_data[ifd].in_proc); + + if (read_write & DO_WRITE) { + driver_data[ifd].ofd = ofd; + driver_data[ifd].out_sig_descr = + erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG,ofd); + driver_data[ifd].pdl = driver_pdl_create(port_num); + driver_data[ifd].out_proc = create_process(OS_PRI_PROC, "beam_fd_writer", + fd_writer_process, 0x800, + FD_PROC_PRI, 0, 0, NULL, 0, 0); + sig = alloc(sizeof(SysDriverConfSignal), SYSDRIVERCONFSIG); + sig->conf_async.fd = ofd; + sig->conf_async.parent = current_process(); + send(&sig, driver_data[ifd].out_proc); + // efs_clone(driver_data[ifd].out_proc); + start(driver_data[ifd].out_proc); + if (ifd != ofd) + driver_data[ofd] = driver_data[ifd]; /* structure copy */ + } else { /* DO_READ only */ + driver_data[ifd].ofd = -1; + } + (void) driver_select(port_num, driver_data[ifd].in_sig_descr, (ERL_DRV_READ | ERL_DRV_USE), 1); + return(ifd); + } else { /* DO_WRITE only */ + driver_data[ofd].packet_bytes = packet_bytes; + driver_data[ofd].port_num = port_num; + driver_data[ofd].report_exit = report_exit; + driver_data[ofd].ofd = ofd; + driver_data[ofd].pid = pid; + driver_data[ofd].alive = 1; + driver_data[ofd].status = 0; + driver_data[ofd].in_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG, + ofd); + driver_data[ofd].out_sig_descr = driver_data[ofd].in_sig_descr; + driver_data[ofd].out_proc = create_process(OS_PRI_PROC, "beam_fd_writer", + fd_writer_process, 0x800, + FD_PROC_PRI, 0, 0, NULL, 0, 0); + sig = alloc(sizeof(SysDriverConfSignal), SYSDRIVERCONFSIG); + sig->conf_async.fd = ofd; + sig->conf_async.parent = current_process(); + send(&sig, driver_data[ofd].out_proc); + start(driver_data[ofd].out_proc); + //efs_clone(driver_data[ifd].out_proc); + driver_data[ofd].pdl = driver_pdl_create(port_num); + return(ofd); + } +} + +static int spawn_init() +{ + int i; + TRACE; + + driver_data = (struct driver_data *) + erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data)); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, max_files * sizeof(struct driver_data)); + + for (i = 0; i < max_files; i++) + driver_data[i].pid = -1; + + return 1; +} + +static void init_fd_data(int fd, ErlDrvPort port_num) +{ + TRACE; + + fd_data[fd].buf = NULL; + fd_data[fd].cpos = NULL; + fd_data[fd].remain = 0; + fd_data[fd].sz = 0; + fd_data[fd].psz = 0; +} + +static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) +{ + + long res = 0; + + TRACE; + /* Have to implement for OSE */ + return (ErlDrvData)res; +} + +OS_PROCESS(fd_reader_process) { + OseSignal *sig; + PROCESS parent; + int fd; + byte *read_buf; + + SIGSELECT sigsel[] = {1,SYSDRIVERCONFSIG}; + +#ifdef ERTS_ENABLE_LOCK_COUNT + erts_lcnt_init(); +#endif + + TRACE; + + sig = receive(sigsel); + + TRACE; + + fd = sig->conf_async.fd; + + parent = sig->conf_async.parent; + free_buf(&sig); + +#ifdef ERTS_ENABLE_LOCK_CHECK + { + char buf[31]; + erts_snprintf(&buf[0], 31, "fd_reader %beu", fd); + erts_lc_set_thread_name(&buf[0]); + } +#endif + + sigsel[1] = SYSDRIVERASYNCSIG; + + read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, + ERTS_SYS_READ_BUF_SZ); + while (1) { + int errno_copy = errno; + ssize_t res; + res = read(fd, read_buf, ERTS_SYS_READ_BUF_SZ); + sig = alloc(sizeof(SysDriverAsyncSignal), SYSDRIVERASYNCSIG); + sig->sys_async.buff = read_buf; + sig->sys_async.res = res; + if (res <= 0 && errno == EBADF) { + fprintf(stderr,"Could not read from input fd (fd %d/ errno %d/ res %d)\n", + fd, errno, res); + break; + } + if (errno != errno_copy) + sig->sys_async.errno_copy = errno; + else + sig->sys_async.errno_copy = -1; + sig->sys_async.type = fd; + send(&sig,parent); + /* Wait for acc from async_read */ + sig = receive(sigsel); + free_buf(&sig); + } + erts_free(ERTS_ALC_T_SYS_READ_BUF, read_buf); +} + +OS_PROCESS(fd_writer_process) { + OseSignal *sig; + PROCESS parent; + int fd; + SIGSELECT sigsel[] = { 1, SYSDRIVERCONFSIG, SYSDRIVERASYNCSIG }; + + TRACE; + /* Only wait for config event with the fd which we are printing to */ + sig = receive(sigsel); + + TRACE; + + fd = sig->conf_async.fd; + parent = sig->conf_async.parent; + free_buf(&sig); + +#ifdef ERTS_ENABLE_LOCK_COUNT + { + char buf[31]; + erts_snprintf(&buf[0], 31, "fd_writer %beu", fd); + erts_lc_set_thread_name(&buf[0]); + } +#endif + + sigsel[0] = 2; + /* Why do I need these?!? */ + if (fd == 1) { + FILE* ffd = stdout; + } else if (fd == 2) { + FILE* ffd = stderr; + } + + while (1) { + int errno_copy = errno; + int res; + SysIOVec *iov0; + SysIOVec *iov; + int iovlen; + int iovcnt; + int n = 0, i; + size_t p; + /* fprintf(stderr,"0x%x: fd_writer, receive\n", current_process()); */ + sig = receive(sigsel); + /* size = sig->sys_async.res;*/ + if (sig->sig_no == SYSDRIVERCONFSIG) + return; + driver_pdl_lock(driver_data[fd].pdl); + + iov0 = driver_peekq(driver_data[fd].port_num, &iovlen); + + /* Calculate iovcnt */ + for (p = 0, iovcnt = 0; iovcnt < iovlen; + p += iov0[iovcnt++].iov_len) + ; + iov = driver_alloc(sizeof(SysIOVec) * iovcnt); + memcpy(iov, iov0, iovcnt * sizeof(SysIOVec)); + driver_pdl_unlock(driver_data[fd].pdl); + /* Let go of lock until we deque from original vector */ + + if (iovlen > 0) { + for (i = 0; i < iovcnt; i++) { + res = write(fd, iov[i].iov_base, iov[i].iov_len > 256 ? 256 : iov[i].iov_len); + if (res < 0) + break; + n += res; + } + if (res > 0) + res = n; + } else if (iovlen == 0) { + res = 0; + } else { /* Port has terminated */ + res = -1; + } + driver_free(iov); + + sig->sys_async.buff = NULL; + sig->sys_async.res = res; + if (errno != errno_copy) + sig->sys_async.errno_copy = errno; + else + sig->sys_async.errno_copy = -1; + sig->sys_async.type = fd; + send(&sig, parent); + } +} + +#define FD_DEF_HEIGHT 24 +#define FD_DEF_WIDTH 80 +/* Control op */ +#define FD_CTRL_OP_GET_WINSIZE 100 + +static int fd_get_window_size(int fd, Uint32 *width, Uint32 *height) +{ +#ifdef TIOCGWINSZ + struct winsize ws; + if (ioctl(fd,TIOCGWINSZ,&ws) == 0) { + *width = (Uint32) ws.ws_col; + *height = (Uint32) ws.ws_row; + return 0; + } +#endif + return -1; +} + +static ErlDrvSSizeT fd_control(ErlDrvData drv_data, + unsigned int command, + char *buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen) +{ + int fd = (int)(long)drv_data; + char resbuff[2*sizeof(Uint32)]; + switch (command) { + case FD_CTRL_OP_GET_WINSIZE: + { + Uint32 w,h; + if (fd_get_window_size(fd,&w,&h)) + return 0; + memcpy(resbuff,&w,sizeof(Uint32)); + memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32)); + } + break; + default: + return 0; + } + if (rlen < 2*sizeof(Uint32)) { + *rbuf = driver_alloc(2*sizeof(Uint32)); + } + memcpy(*rbuf,resbuff,2*sizeof(Uint32)); + return 2*sizeof(Uint32); +} + +static ErlDrvData fd_start(ErlDrvPort port_num, char* name, + SysDriverOpts* opts) +{ + ErlDrvData res; + + TRACE; + + CHLD_STAT_LOCK; + if (opts->read_write & DO_READ) { + init_fd_data(opts->ifd, port_num); + } + if (opts->read_write & DO_WRITE) { + init_fd_data(opts->ofd, port_num); + } + res = (ErlDrvData)(long)set_driver_data(port_num, opts->ifd, opts->ofd, + opts->packet_bytes, + opts->read_write, 0, -1); + CHLD_STAT_UNLOCK; + return res; +} + +static void clear_fd_data(int fd) +{ + TRACE; + + if (fd_data[fd].sz > 0) { + erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf); + ASSERT(erts_smp_atomic_read_nob(&sys_misc_mem_sz) >= fd_data[fd].sz); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*fd_data[fd].sz); + } + fd_data[fd].buf = NULL; + fd_data[fd].sz = 0; + fd_data[fd].remain = 0; + fd_data[fd].cpos = NULL; + fd_data[fd].psz = 0; +} + +static void nbio_stop_fd(ErlDrvPort prt, ErlDrvEvent ev) +{ + int fd; + TRACE; + driver_select(prt,ev,DO_READ|DO_WRITE,0); + erl_drv_ose_event_fetch(ev, NULL, &fd); + clear_fd_data(fd); + SET_BLOCKING(fd); +} + +static void fd_stop(ErlDrvData fd) /* Does not close the fds */ +{ + int ofd; + + TRACE; + + nbio_stop_fd(driver_data[(int)(long)fd].port_num, driver_data[(int)(long)fd].in_sig_descr); + ofd = driver_data[(int)(long)fd].ofd; + if (ofd != (int)(long)fd && ofd != -1) + nbio_stop_fd(driver_data[(int)(long)fd].port_num, driver_data[(int)(long)fd].out_sig_descr); +} + +/* 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 erl_stop(ErlDrvData fd) +{ + ErlDrvPort prt; + int ofd; + + TRACE; + + prt = driver_data[(int)(long)fd].port_num; + nbio_stop_fd(prt, driver_data[(int)(long)fd].in_sig_descr); + + ofd = driver_data[(int)(long)fd].ofd; + if (ofd != (int)(long)fd && (int)(long)ofd != -1) + nbio_stop_fd(prt, driver_data[(int)(long)fd].out_sig_descr); + else + ofd = -1; + + CHLD_STAT_LOCK; + + /* Mark as unused. */ + driver_data[(int)(long)fd].pid = -1; + + CHLD_STAT_UNLOCK; + + /* SMP note: Close has to be last thing done (open file descriptors work + as locks on driver_data[] entries) */ + driver_select(prt, driver_data[(int)(long)fd].in_sig_descr, ERL_DRV_USE, 0); /* close(fd); */ + if (ofd >= 0) { + driver_select(prt, driver_data[(int)(long)fd].out_sig_descr, ERL_DRV_USE, 0); /* close(ofd); */ + } +} + +static void outputv(ErlDrvData e, ErlIOVec* ev) +{ + int fd = (int)(long)e; + ErlDrvPort ix = driver_data[fd].port_num; + int pb = driver_data[fd].packet_bytes; + ErlDrvSizeT sz; + char lb[4]; + char* lbp; + ErlDrvSizeT len = ev->size; + + TRACE; + + /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ + /* if (pb >= 0 && (len & (((ErlDrvSizeT)1 << (pb*8))) - 1) != len) {*/ + if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) { + driver_failure_posix(ix, EINVAL); + return; /* -1; */ + } + /* Handles 0 <= pb <= 4 only */ + put_int32((Uint32) len, lb); + lbp = lb + (4-pb); + + ev->iov[0].iov_base = lbp; + ev->iov[0].iov_len = pb; + ev->size += pb; + driver_pdl_lock(driver_data[fd].pdl); + if ((sz = driver_sizeq(ix)) > 0) { + /* fprintf(stderr,"0x%x: outputv, enq\n", current_process()); */ + driver_enqv(ix, ev, 0); + if (sz + ev->size >= (1 << 13)) + set_busy_port(ix, 1); + driver_pdl_unlock(driver_data[fd].pdl); + } + else { + OseSignal *sig; + /* fprintf(stderr,"0x%x: outputv, enq+sel\n", current_process()); */ + driver_enqv(ix, ev, 0); /* n is the skip value */ + driver_pdl_unlock(driver_data[fd].pdl); + driver_select(ix, driver_data[fd].out_sig_descr, ERL_DRV_WRITE|ERL_DRV_USE, 1); + sig = alloc(sizeof(SysDriverAsyncSignal),SYSDRIVERASYNCSIG); + sig->sys_async.type = fd; + sig->sys_async.res = pb+len; + send(&sig,driver_data[fd].out_proc); + } + /* return 0;*/ +} + + +static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) +{ + int fd = (int)(long)e; + ErlDrvPort ix = driver_data[fd].port_num; + int pb = driver_data[fd].packet_bytes; + int ofd = driver_data[fd].ofd; + ErlDrvSizeT sz; + char lb[4]; + char* lbp; +#if 0 + struct iovec iv[2]; +#endif + + TRACE; + + /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ + if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) { + driver_failure_posix(ix, EINVAL); + return; /* -1; */ + } + put_int32(len, lb); + lbp = lb + (4-pb); + + driver_pdl_lock(driver_data[fd].pdl); + if ((sz = driver_sizeq(ix)) > 0) { + /* fprintf(stderr,"0x%x: output, enq\n", current_process()); */ + driver_enq(ix, lbp, pb); + driver_enq(ix, buf, len); + driver_pdl_unlock(driver_data[fd].pdl); + if (sz + len + pb >= (1 << 13)) + set_busy_port(ix, 1); + } + else { + OseSignal *sig; + /* fprintf(stderr,"0x%x: output, enq+select\n", current_process()); */ +#if 0 + iv[0].iov_base = lbp; + iv[0].iov_len = pb; /* should work for pb=0 */ + iv[1].iov_base = buf; + iv[1].iov_len = len; +#endif + driver_enq(ix, lbp, pb); + driver_enq(ix, buf, len); + driver_pdl_unlock(driver_data[fd].pdl); + driver_select(ix, driver_data[ofd].out_sig_descr, ERL_DRV_WRITE|ERL_DRV_USE, 1); + sig = alloc(sizeof(SysDriverAsyncSignal),SYSDRIVERASYNCSIG); + sig->sys_async.type = fd; + sig->sys_async.res = pb+len; + send(&sig,driver_data[fd].out_proc); + } + return; /* 0; */ +} + +static int port_inp_failure(ErlDrvPort port_num, ErlDrvEvent ready_fd, int res) + /* Result: 0 (eof) or -1 (error) */ +{ + int err = errno; + int fd; + + ASSERT(res <= 0); + (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0); + erl_drv_ose_event_fetch(ready_fd,NULL,&fd); + clear_fd_data(fd); + if (res == 0) { + if (driver_data[fd].report_exit) { + CHLD_STAT_LOCK; + + if (driver_data[fd].alive) { + /* + * We have eof and want to report exit status, but the process + * hasn't exited yet. When it does report_exit_status() will + * driver_select() this fd which will make sure that we get + * back here with driver_data[ready_fd].alive == 0 and + * driver_data[ready_fd].status set. + */ + CHLD_STAT_UNLOCK; + return 0; + } + else { + int status = driver_data[fd].status; + CHLD_STAT_UNLOCK; + +#if 0 /*ose we should find something for these statuses*/ + /* We need not be prepared for stopped/continued processes. */ + if (WIFSIGNALED(status)) + status = 128 + WTERMSIG(status); + else + status = WEXITSTATUS(status); +#endif + driver_report_exit(driver_data[fd].port_num, status); + } + } + driver_failure_eof(port_num); + } else { + driver_failure_posix(port_num, err); + } + return 0; +} + +static int async_read(ErlDrvEvent fd, byte *buff, int size) { + OseSignal *sigptr = erl_drv_ose_get_input_signal(fd); + int res = sigptr->sys_async.res; + if (res > 0) + memcpy(buff,sigptr->sys_async.buff,sigptr->sys_async.res); + errno = sigptr->sys_async.errno_copy; + send(&sigptr,sender(&sigptr)); + ASSERT(erl_drv_ose_get_input_signal(fd) == NULL); + return res; +} + +/* fd is the drv_data that is returned from the */ +/* initial start routine */ +/* ready_fd is the descriptor that is ready to read */ + +static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) +{ + int fd = (int)(long)e; + ErlDrvPort port_num; + int packet_bytes; + int res; + Uint h; + + TRACE; + + port_num = driver_data[fd].port_num; + packet_bytes = driver_data[fd].packet_bytes; + + if (packet_bytes == 0) { + byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, + ERTS_SYS_READ_BUF_SZ); + res = async_read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ); + if (res < 0) { + if ((errno != EINTR) && (errno != ERRNO_BLOCK)) + port_inp_failure(port_num, ready_fd, res); + } + else if (res == 0) + port_inp_failure(port_num, ready_fd, res); + else + driver_output(port_num, (char*) read_buf, res); + erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf); + } + else if (fd_data[fd].remain > 0) { /* We try to read the remainder */ + /* space is allocated in buf */ + res = async_read(ready_fd, (byte*)fd_data[fd].cpos, + fd_data[fd].remain); + if (res < 0) { + if ((errno != EINTR) && (errno != ERRNO_BLOCK)) + port_inp_failure(port_num, ready_fd, res); + } + else if (res == 0) { + port_inp_failure(port_num, ready_fd, res); + } + else if (res == fd_data[fd].remain) { /* we're done */ + driver_output(port_num, fd_data[fd].buf, + fd_data[fd].sz); + clear_fd_data(fd); + } + else { /* if (res < fd_data[ready_fd].remain) */ + fd_data[fd].cpos += res; + fd_data[fd].remain -= res; + } + } + else if (fd_data[fd].remain == 0) { /* clean fd */ + byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, + ERTS_SYS_READ_BUF_SZ); + /* We make one read attempt and see what happens */ + res = async_read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ); + if (res < 0) { + if ((errno != EINTR) && (errno != ERRNO_BLOCK)) + port_inp_failure(port_num, ready_fd, res); + } + else if (res == 0) { /* eof */ + port_inp_failure(port_num, ready_fd, res); + } + else if (res < packet_bytes - fd_data[fd].psz) { + memcpy(fd_data[fd].pbuf+fd_data[fd].psz, + read_buf, res); + fd_data[fd].psz += res; + } + else { /* if (res >= packet_bytes) */ + unsigned char* cpos = read_buf; + int bytes_left = res; + + while (1) { + int psz = fd_data[fd].psz; + char* pbp = fd_data[fd].pbuf + psz; + + while(bytes_left && (psz < packet_bytes)) { + *pbp++ = *cpos++; + bytes_left--; + psz++; + } + + if (psz < packet_bytes) { + fd_data[fd].psz = psz; + break; + } + fd_data[fd].psz = 0; + + switch (packet_bytes) { + case 1: h = get_int8(fd_data[fd].pbuf); break; + case 2: h = get_int16(fd_data[fd].pbuf); break; + case 4: h = get_int32(fd_data[fd].pbuf); break; + default: ASSERT(0); return; /* -1; */ + } + + if (h <= (bytes_left)) { + driver_output(port_num, (char*) cpos, h); + cpos += h; + bytes_left -= h; + continue; + } + else { /* The last message we got was split */ + char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); + if (!buf) { + errno = ENOMEM; + port_inp_failure(port_num, ready_fd, -1); + } + else { + erts_smp_atomic_add_nob(&sys_misc_mem_sz, h); + sys_memcpy(buf, cpos, bytes_left); + fd_data[fd].buf = buf; + fd_data[fd].sz = h; + fd_data[fd].remain = h - bytes_left; + fd_data[fd].cpos = buf + bytes_left; + } + break; + } + } + } + erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf); + } +} + + +/* fd is the drv_data that is returned from the */ +/* initial start routine */ +/* ready_fd is the descriptor that is ready to read */ + +static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) +{ + int fd = (int)(long)e; + ErlDrvPort ix = driver_data[fd].port_num; + OseSignal *sigptr = erl_drv_ose_get_output_signal(ready_fd); + ssize_t n; + struct iovec* iv; + int vsize; + + while (sigptr != NULL) { + + driver_pdl_lock(driver_data[fd].pdl); + if ((iv = (struct iovec*) driver_peekq(ix, &vsize)) == NULL) { + /* fprintf(stderr,"0x%x: ready_output, unselect\n", current_process()); */ + driver_pdl_unlock(driver_data[fd].pdl); + driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); + set_busy_port(ix, 0); + free_buf(&sigptr); + if ((sigptr = erl_drv_ose_get_output_signal(ready_fd)) == NULL) + return; /* 0; */ + continue; + } + driver_pdl_unlock(driver_data[fd].pdl); + n = sigptr->sys_async.res; + if (n < 0) { + if (errno == ERRNO_BLOCK || errno == EINTR) { + /* fprintf(stderr,"0x%x: ready_output, send to %x\n", current_process(),driver_data[fd].out_proc);*/ + send(&sigptr,driver_data[fd].out_proc); + if ((sigptr = erl_drv_ose_get_output_signal(ready_fd)) == NULL) + return; /* 0; */ + continue; + } else { + int res = sigptr->sys_async.errno_copy; + /* fprintf(stderr,"0x%x: ready_output, error\n", current_process()); */ + free_buf(&sigptr); + driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); + driver_failure_posix(ix, res); + if ((sigptr = erl_drv_ose_get_output_signal(ready_fd)) == NULL) + return; /* -1; */ + continue; + } + } else { + int remain; + driver_pdl_lock(driver_data[fd].pdl); + if ((remain = driver_deq(driver_data[fd].port_num, n)) == -1) + abort(); + /* fprintf(stderr, "0x%x: ready_output, %d to %x, remain %d\n", current_process(), + n, driver_data[fd].out_proc, remain); */ + driver_pdl_unlock(driver_data[fd].pdl); + if (remain != 0) + send(&sigptr, driver_data[fd].out_proc); + else + continue; + } + sigptr = erl_drv_ose_get_output_signal(ready_fd); + } + return; /* 0; */ +} + +static void stop_select(ErlDrvEvent fd, void* _) +{ + close((int)fd); +} + + +void erts_do_break_handling(void) +{ + struct termios temp_mode; + int saved = 0; + + /* + * Most functions that do_break() calls are intentionally not thread safe; + * therefore, make sure that all threads but this one are blocked before + * proceeding! + */ + erts_smp_thr_progress_block(); + + /* during break we revert to initial settings */ + /* this is done differently for oldshell */ + if (using_oldshell && !replace_intr) { + SET_BLOCKING(1); + } + else if (isatty(0)) { + tcgetattr(0,&temp_mode); + tcsetattr(0,TCSANOW,&initial_tty_mode); + saved = 1; + } + + /* call the break handling function, reset the flag */ + do_break(); + + fflush(stdout); + + /* after break we go back to saved settings */ + if (using_oldshell && !replace_intr) { + SET_NONBLOCKING(1); + } + else if (saved) { + tcsetattr(0,TCSANOW,&temp_mode); + } + + erts_smp_thr_progress_unblock(); +} + +static pid_t +getpid(void) +{ + return get_bid(current_process()); +} + +int getpagesize(void) +{ + return 1024; +} + + +/* Fills in the systems representation of the jam/beam process identifier. +** The Pid is put in STRING representation in the supplied buffer, +** no interpretatione of this should be done by the rest of the +** emulator. The buffer should be at least 21 bytes long. +*/ +void sys_get_pid(char *buffer, size_t buffer_size){ + pid_t p = getpid(); + /* Assume the pid is scalar and can rest in an unsigned long... */ + erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p); +} + +int +erts_sys_putenv_raw(char *key, char *value) { + return erts_sys_putenv(key, value); +} +int +erts_sys_putenv(char *key, char *value) +{ + int res; + char *env; + Uint need = strlen(key) + strlen(value) + 2; + +#ifdef HAVE_COPYING_PUTENV + env = erts_alloc(ERTS_ALC_T_TMP, need); +#else + 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; +} + +int +erts_sys_getenv__(char *key, char *value, size_t *size) +{ + int res; + char *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_raw(char *key, char *value, size_t *size) { + return erts_sys_getenv(key, value, size); +} + +/* + * erts_sys_getenv + * returns: + * -1, if environment key is not set with a value + * 0, if environment key is set and value fits into buffer res + * 1, if environment key is set but does not fit into buffer res + * res is set with the needed buffer res value + */ + +int +erts_sys_getenv(char *key, char *value, size_t *size) +{ + int res; + erts_smp_rwmtx_rlock(&environ_rwmtx); + res = erts_sys_getenv__(key, value, size); + erts_smp_rwmtx_runlock(&environ_rwmtx); + return res; +} + +void +sys_init_io(void) +{ + fd_data = (struct fd_data *) + erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(struct fd_data)); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, + max_files * sizeof(struct fd_data)); +} + +extern const char pre_loaded_code[]; +extern Preload pre_loaded[]; + +void erts_sys_alloc_init(void) +{ +} + +void *erts_sys_alloc(ErtsAlcType_t t, void *x, Uint sz) +{ + void *res = malloc((size_t) sz); +#if HAVE_ERTS_MSEG + if (!res) { + erts_mseg_clear_cache(); + return malloc((size_t) sz); + } +#endif + return res; +} + +void *erts_sys_realloc(ErtsAlcType_t t, void *x, void *p, Uint sz) +{ + void *res = realloc(p, (size_t) sz); +#if HAVE_ERTS_MSEG + if (!res) { + erts_mseg_clear_cache(); + return realloc(p, (size_t) sz); + } +#endif + return res; +} + +void erts_sys_free(ErtsAlcType_t t, void *x, void *p) +{ + free(p); +} + +/* Return a pointer to a vector of names of preloaded modules */ + +Preload* +sys_preloaded(void) +{ + return pre_loaded; +} + +/* Return a pointer to preloaded code for module "module" */ +unsigned char* +sys_preload_begin(Preload* p) +{ + return p->code; +} + +/* Clean up if allocated */ +void sys_preload_end(Preload* p) +{ + /* Nothing */ +} + +/* Read a key from console (?) */ + +int sys_get_key(fd) +int fd; +{ + int c; + unsigned char rbuf[64]; + + TRACE; + + fflush(stdout); /* Flush query ??? */ + + if ((c = read(fd,rbuf,64)) <= 0) { + return c; + } + + return rbuf[0]; +} + + +#ifdef DEBUG + +extern int erts_initialized; +void +erl_assert_error(char* expr, char* file, int line) +{ + fflush(stdout); + fprintf(stderr, "Assertion failed: %s in %s, line %d\n", + expr, file, line); + fflush(stderr); + ramlog_printf("%d: Assertion failed: %s in %s, line %d\n", + current_process(), expr, file, line); + abort(); +} + +void +erl_debug(char* fmt, ...) +{ + char sbuf[1024]; /* Temporary buffer. */ + va_list va; + + if (debug_log) { + va_start(va, fmt); + vsprintf(sbuf, fmt, va); + va_end(va); + fprintf(stderr, "%s", sbuf); + } +} + +#endif /* DEBUG */ + +static ERTS_INLINE void +report_exit_status(ErtsSysReportExit *rep, int status) +{ + Port *pp; +#ifdef ERTS_SMP + CHLD_STAT_UNLOCK; + pp = erts_thr_id2port_sflgs(rep->port, + ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); + CHLD_STAT_LOCK; +#else + pp = erts_id2port_sflgs(rep->port, + NULL, + 0, + ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); +#endif + if (pp) { + if (rep->ifd >= 0) { + driver_data[rep->ifd].alive = 0; + driver_data[rep->ifd].status = status; + (void) driver_select(ERTS_Port2ErlDrvPort(pp), + rep->in_sig_descr, + (ERL_DRV_READ|ERL_DRV_USE), + 1); + } + if (rep->ofd >= 0) { + driver_data[rep->ofd].alive = 0; + driver_data[rep->ofd].status = status; + (void) driver_select(ERTS_Port2ErlDrvPort(pp), + rep->out_sig_descr, + (ERL_DRV_WRITE|ERL_DRV_USE), + 1); + } +#ifdef ERTS_SMP + erts_thr_port_release(pp); +#else + erts_port_release(pp); +#endif + } + erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep); +} + +#define ERTS_REPORT_EXIT_STATUS report_exit_status + +/* + * Called from schedule() when it runs out of runnable processes, + * or when Erlang code has performed INPUT_REDUCTIONS reduction + * steps. runnable == 0 iff there are no runnable Erlang processes. + */ +void +erl_sys_schedule(int runnable) +{ + ASSERT(get_fsem(current_process()) == 0); +#ifdef ERTS_SMP + ERTS_CHK_IO(!runnable); +#else + ERTS_CHK_IO( 1 ); +#endif + ASSERT(get_fsem(current_process()) == 0); + ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); +} + + +#ifdef ERTS_SMP + +void +erts_sys_main_thread(void) +{ + erts_thread_disable_fpe(); + + /* Become signal receiver thread... */ +#ifdef ERTS_ENABLE_LOCK_CHECK + erts_lc_set_thread_name("signal_receiver"); +#endif + + while (1) { + static const SIGSELECT sigsel[] = {0}; + OseSignal *msg = receive(sigsel); + + fprintf(stderr,"Main thread got message %d from 0x%x!!\r\n", + msg->sig_no, sender(&msg)); + free_buf(&msg); + } +} + +#endif /* ERTS_SMP */ + +void +erl_sys_args(int* argc, char** argv) +{ + int i, j; + + erts_smp_rwmtx_init(&environ_rwmtx, "environ"); + + init_check_io(); + + /* Handled arguments have been marked with NULL. Slide arguments + not handled towards the beginning of argv. */ + for (i = 0, j = 0; i < *argc; i++) { + if (argv[i]) + argv[j++] = argv[i]; + } + *argc = j; + +} diff --git a/erts/emulator/sys/ose/sys_float.c b/erts/emulator/sys/ose/sys_float.c new file mode 100644 index 0000000000..d9d6bb7c04 --- /dev/null +++ b/erts/emulator/sys/ose/sys_float.c @@ -0,0 +1,844 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * + * The 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 "global.h" +#include "erl_process.h" + + +#ifdef NO_FPE_SIGNALS + +void +erts_sys_init_float(void) +{ +# ifdef SIGFPE + sys_sigset(SIGFPE, SIG_IGN); /* Ignore so we can test for NaN and Inf */ +# endif +} + +#else /* !NO_FPE_SIGNALS */ + +#ifdef ERTS_SMP +static erts_tsd_key_t fpe_key; + +/* once-only initialisation early in the main thread (via erts_sys_init_float()) */ +static void erts_init_fp_exception(void) +{ + /* XXX: the wrappers prevent using a pthread destructor to + deallocate the key's value; so when/where do we do that? */ + erts_tsd_key_create(&fpe_key); +} + +void erts_thread_init_fp_exception(void) +{ + unsigned long *fpe = erts_alloc(ERTS_ALC_T_FP_EXCEPTION, sizeof(*fpe)); + *fpe = 0L; + erts_tsd_set(fpe_key, fpe); +} + +static ERTS_INLINE volatile unsigned long *erts_thread_get_fp_exception(void) +{ + return (volatile unsigned long*)erts_tsd_get(fpe_key); +} +#else /* !SMP */ +#define erts_init_fp_exception() /*empty*/ +static volatile unsigned long fp_exception; +#define erts_thread_get_fp_exception() (&fp_exception) +#endif /* SMP */ + +volatile unsigned long *erts_get_current_fp_exception(void) +{ + Process *c_p; + + c_p = erts_get_current_process(); + if (c_p) + return &c_p->fp_exception; + return erts_thread_get_fp_exception(); +} + +static void set_current_fp_exception(unsigned long pc) +{ + volatile unsigned long *fpexnp = erts_get_current_fp_exception(); + ASSERT(fpexnp != NULL); + *fpexnp = pc; +} + +void erts_fp_check_init_error(volatile unsigned long *fpexnp) +{ + char buf[64]; + snprintf(buf, sizeof buf, "ERTS_FP_CHECK_INIT at %p: detected unhandled FPE at %p\r\n", + __builtin_return_address(0), (void*)*fpexnp); + if (write(2, buf, strlen(buf)) <= 0) + erl_exit(ERTS_ABORT_EXIT, "%s", buf); + *fpexnp = 0; +#if defined(__i386__) || defined(__x86_64__) + erts_restore_fpu(); +#endif +} + +/* Is there no standard identifier for Darwin/MacOSX ? */ +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +#define __DARWIN__ 1 +#endif + +#if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__) + +static void unmask_x87(void) +{ + unsigned short cw; + + __asm__ __volatile__("fstcw %0" : "=m"(cw)); + cw &= ~(0x01|0x04|0x08); /* unmask IM, ZM, OM */ + __asm__ __volatile__("fldcw %0" : : "m"(cw)); +} + +/* mask x87 FPE, return true if the previous state was unmasked */ +static int mask_x87(void) +{ + unsigned short cw; + int unmasked; + + __asm__ __volatile__("fstcw %0" : "=m"(cw)); + unmasked = (cw & (0x01|0x04|0x08)) == 0; + /* or just set cw = 0x37f */ + cw |= (0x01|0x04|0x08); /* mask IM, ZM, OM */ + __asm__ __volatile__("fldcw %0" : : "m"(cw)); + return unmasked; +} + +static void unmask_sse2(void) +{ + unsigned int mxcsr; + + __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr)); + mxcsr &= ~(0x003F|0x0680); /* clear exn flags, unmask OM, ZM, IM (not PM, UM, DM) */ + __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr)); +} + +/* mask SSE2 FPE, return true if the previous state was unmasked */ +static int mask_sse2(void) +{ + unsigned int mxcsr; + int unmasked; + + __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr)); + unmasked = (mxcsr & 0x0680) == 0; + /* or just set mxcsr = 0x1f80 */ + mxcsr &= ~0x003F; /* clear exn flags */ + mxcsr |= 0x0680; /* mask OM, ZM, IM (not PM, UM, DM) */ + __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr)); + return unmasked; +} + +#if defined(__x86_64__) + +static inline int cpu_has_sse2(void) { return 1; } + +#else /* !__x86_64__ */ + +/* + * Check if an x86-32 processor has SSE2. + */ +static unsigned int xor_eflags(unsigned int mask) +{ + unsigned int eax, edx; + + eax = mask; /* eax = mask */ + __asm__("pushfl\n\t" + "popl %0\n\t" /* edx = original EFLAGS */ + "xorl %0, %1\n\t" /* eax = mask ^ EFLAGS */ + "pushl %1\n\t" + "popfl\n\t" /* new EFLAGS = mask ^ original EFLAGS */ + "pushfl\n\t" + "popl %1\n\t" /* eax = new EFLAGS */ + "xorl %0, %1\n\t" /* eax = new EFLAGS ^ old EFLAGS */ + "pushl %0\n\t" + "popfl" /* restore original EFLAGS */ + : "=d"(edx), "=a"(eax) + : "1"(eax)); + return eax; +} + +static __inline__ unsigned int cpuid_eax(unsigned int op) +{ + unsigned int eax, save_ebx; + + /* In PIC mode i386 reserves EBX. So we must save + and restore it ourselves to not upset gcc. */ + __asm__( + "movl %%ebx, %1\n\t" + "cpuid\n\t" + "movl %1, %%ebx" + : "=a"(eax), "=m"(save_ebx) + : "0"(op) + : "cx", "dx"); + return eax; +} + +static __inline__ unsigned int cpuid_edx(unsigned int op) +{ + unsigned int eax, edx, save_ebx; + + /* In PIC mode i386 reserves EBX. So we must save + and restore it ourselves to not upset gcc. */ + __asm__( + "movl %%ebx, %2\n\t" + "cpuid\n\t" + "movl %2, %%ebx" + : "=a"(eax), "=d"(edx), "=m"(save_ebx) + : "0"(op) + : "cx"); + return edx; +} + +/* The AC bit, bit #18, is a new bit introduced in the EFLAGS + * register on the Intel486 processor to generate alignment + * faults. This bit cannot be set on the Intel386 processor. + */ +static __inline__ int is_386(void) +{ + return ((xor_eflags(1<<18) >> 18) & 1) == 0; +} + +/* Newer x86 processors have a CPUID instruction, as indicated by + * the ID bit (#21) in EFLAGS being modifiable. + */ +static __inline__ int has_CPUID(void) +{ + return (xor_eflags(1<<21) >> 21) & 1; +} + +static int cpu_has_sse2(void) +{ + unsigned int maxlev, features; + static int has_sse2 = -1; + + if (has_sse2 >= 0) + return has_sse2; + has_sse2 = 0; + + if (is_386()) + return 0; + if (!has_CPUID()) + return 0; + maxlev = cpuid_eax(0); + /* Intel A-step Pentium had a preliminary version of CPUID. + It also didn't have SSE2. */ + if ((maxlev & 0xFFFFFF00) == 0x0500) + return 0; + /* If max level is zero then CPUID cannot report any features. */ + if (maxlev == 0) + return 0; + features = cpuid_edx(1); + has_sse2 = (features & (1 << 26)) != 0; + + return has_sse2; +} +#endif /* !__x86_64__ */ + +static void unmask_fpe(void) +{ + __asm__ __volatile__("fnclex"); + unmask_x87(); + if (cpu_has_sse2()) + unmask_sse2(); +} + +static void unmask_fpe_conditional(int unmasked) +{ + if (unmasked) + unmask_fpe(); +} + +/* mask x86 FPE, return true if the previous state was unmasked */ +static int mask_fpe(void) +{ + int unmasked; + + unmasked = mask_x87(); + if (cpu_has_sse2()) + unmasked |= mask_sse2(); + return unmasked; +} + +void erts_restore_fpu(void) +{ + __asm__ __volatile__("fninit"); + unmask_x87(); + if (cpu_has_sse2()) + unmask_sse2(); +} + +#elif defined(__sparc__) && defined(__linux__) + +#if defined(__arch64__) +#define LDX "ldx" +#define STX "stx" +#else +#define LDX "ld" +#define STX "st" +#endif + +static void unmask_fpe(void) +{ + unsigned long fsr; + + __asm__(STX " %%fsr, %0" : "=m"(fsr)); + fsr &= ~(0x1FUL << 23); /* clear FSR[TEM] field */ + fsr |= (0x1AUL << 23); /* enable NV, OF, DZ exceptions */ + __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr)); +} + +static void unmask_fpe_conditional(int unmasked) +{ + if (unmasked) + unmask_fpe(); +} + +/* mask SPARC FPE, return true if the previous state was unmasked */ +static int mask_fpe(void) +{ + unsigned long fsr; + int unmasked; + + __asm__(STX " %%fsr, %0" : "=m"(fsr)); + unmasked = ((fsr >> 23) & 0x1A) == 0x1A; + fsr &= ~(0x1FUL << 23); /* clear FSR[TEM] field */ + __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr)); + return unmasked; +} + +#elif (defined(__powerpc__) && defined(__linux__)) || (defined(__ppc__) && defined(__DARWIN__)) + +#if defined(__linux__) +#include + +static void set_fpexc_precise(void) +{ + if (prctl(PR_SET_FPEXC, PR_FP_EXC_PRECISE) < 0) { + perror("PR_SET_FPEXC"); + exit(1); + } +} + +#elif defined(__DARWIN__) + +#include +#include + +/* + * FE0 FE1 MSR bits + * 0 0 floating-point exceptions disabled + * 0 1 floating-point imprecise nonrecoverable + * 1 0 floating-point imprecise recoverable + * 1 1 floating-point precise mode + * + * Apparently: + * - Darwin 5.5 (MacOS X <= 10.1) starts with FE0 == FE1 == 0, + * and resets FE0 and FE1 to 0 after each SIGFPE. + * - Darwin 6.0 (MacOS X 10.2) starts with FE0 == FE1 == 1, + * and does not reset FE0 or FE1 after a SIGFPE. + */ +#define FE0_MASK (1<<11) +#define FE1_MASK (1<<8) + +/* a thread cannot get or set its own MSR bits */ +static void *fpu_fpe_enable(void *arg) +{ + thread_t t = *(thread_t*)arg; + struct ppc_thread_state state; + unsigned int state_size = PPC_THREAD_STATE_COUNT; + + if (thread_get_state(t, PPC_THREAD_STATE, (natural_t*)&state, &state_size) != KERN_SUCCESS) { + perror("thread_get_state"); + exit(1); + } + if ((state.srr1 & (FE1_MASK|FE0_MASK)) != (FE1_MASK|FE0_MASK)) { +#if 1 + /* This would also have to be performed in the SIGFPE handler + to work around the MSR reset older Darwin releases do. */ + state.srr1 |= (FE1_MASK|FE0_MASK); + thread_set_state(t, PPC_THREAD_STATE, (natural_t*)&state, state_size); +#else + fprintf(stderr, "srr1 == 0x%08x, your Darwin is too old\n", state.srr1); + exit(1); +#endif + } + return NULL; /* Ok, we appear to be on Darwin 6.0 or later */ +} + +static void set_fpexc_precise(void) +{ + thread_t self = mach_thread_self(); + pthread_t enabler; + + if (pthread_create(&enabler, NULL, fpu_fpe_enable, &self)) { + perror("pthread_create"); + } else if (pthread_join(enabler, NULL)) { + perror("pthread_join"); + } +} + +#endif + +static void set_fpscr(unsigned int fpscr) +{ + union { + double d; + unsigned int fpscr[2]; + } u; + + u.fpscr[0] = 0xFFF80000; + u.fpscr[1] = fpscr; + __asm__ __volatile__("mtfsf 255,%0" : : "f"(u.d)); +} + +static unsigned int get_fpscr(void) +{ + union { + double d; + unsigned int fpscr[2]; + } u; + + __asm__("mffs %0" : "=f"(u.d)); + return u.fpscr[1]; +} + +static void unmask_fpe(void) +{ + set_fpexc_precise(); + set_fpscr(0x80|0x40|0x10); /* VE, OE, ZE; not UE or XE */ +} + +static void unmask_fpe_conditional(int unmasked) +{ + if (unmasked) + unmask_fpe(); +} + +/* mask PowerPC FPE, return true if the previous state was unmasked */ +static int mask_fpe(void) +{ + int unmasked; + + unmasked = (get_fpscr() & (0x80|0x40|0x10)) == (0x80|0x40|0x10); + set_fpscr(0x00); + return unmasked; +} + +#else + +static void unmask_fpe(void) +{ + fpsetmask(FP_X_INV | FP_X_OFL | FP_X_DZ); +} + +static void unmask_fpe_conditional(int unmasked) +{ + if (unmasked) + unmask_fpe(); +} + +/* mask IEEE FPE, return true if previous state was unmasked */ +static int mask_fpe(void) +{ + const fp_except unmasked_mask = FP_X_INV | FP_X_OFL | FP_X_DZ; + fp_except old_mask; + + old_mask = fpsetmask(0); + return (old_mask & unmasked_mask) == unmasked_mask; +} + +#endif + +#if (defined(__linux__) && (defined(__i386__) || defined(__x86_64__) || defined(__sparc__) || defined(__powerpc__))) || (defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__) || defined(__ppc__))) || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__))) || ((defined(__NetBSD__) || defined(__OpenBSD__)) && defined(__x86_64__)) || (defined(__sun__) && defined(__x86_64__)) + +#if defined(__linux__) && defined(__i386__) +#if !defined(X86_FXSR_MAGIC) +#define X86_FXSR_MAGIC 0x0000 +#endif +#elif defined(__FreeBSD__) && defined(__x86_64__) +#include +#include +#elif defined(__FreeBSD__) && defined(__i386__) +#include +#include +#elif defined(__DARWIN__) +#include +#elif defined(__OpenBSD__) && defined(__x86_64__) +#include +#include +#endif +#if !(defined(__OpenBSD__) && defined(__x86_64__)) +#include +#endif +#include + +#if defined(__linux__) && defined(__x86_64__) +#define mc_pc(mc) ((mc)->gregs[REG_RIP]) +#elif defined(__linux__) && defined(__i386__) +#define mc_pc(mc) ((mc)->gregs[REG_EIP]) +#elif defined(__DARWIN__) && defined(__i386__) +#ifdef DARWIN_MODERN_MCONTEXT +#define mc_pc(mc) ((mc)->__ss.__eip) +#else +#define mc_pc(mc) ((mc)->ss.eip) +#endif +#elif defined(__DARWIN__) && defined(__x86_64__) +#ifdef DARWIN_MODERN_MCONTEXT +#define mc_pc(mc) ((mc)->__ss.__rip) +#else +#define mc_pc(mc) ((mc)->ss.rip) +#endif +#elif defined(__FreeBSD__) && defined(__x86_64__) +#define mc_pc(mc) ((mc)->mc_rip) +#elif defined(__FreeBSD__) && defined(__i386__) +#define mc_pc(mc) ((mc)->mc_eip) +#elif defined(__NetBSD__) && defined(__x86_64__) +#define mc_pc(mc) ((mc)->__gregs[_REG_RIP]) +#elif defined(__NetBSD__) && defined(__i386__) +#define mc_pc(mc) ((mc)->__gregs[_REG_EIP]) +#elif defined(__OpenBSD__) && defined(__x86_64__) +#define mc_pc(mc) ((mc)->sc_rip) +#elif defined(__sun__) && defined(__x86_64__) +#define mc_pc(mc) ((mc)->gregs[REG_RIP]) +#endif + +static void fpe_sig_action(int sig, siginfo_t *si, void *puc) +{ + ucontext_t *uc = puc; + unsigned long pc; + +#if defined(__linux__) +#if defined(__x86_64__) + mcontext_t *mc = &uc->uc_mcontext; + fpregset_t fpstate = mc->fpregs; + pc = mc_pc(mc); + /* A failed SSE2 instruction will restart. To avoid + looping we mask SSE2 exceptions now and unmask them + again later in erts_check_fpe()/erts_restore_fpu(). + On RISCs we update PC to skip the failed instruction, + but the ever increasing complexity of the x86 instruction + set encoding makes that a poor solution here. */ + fpstate->mxcsr = 0x1F80; + fpstate->swd &= ~0xFF; +#elif defined(__i386__) + mcontext_t *mc = &uc->uc_mcontext; + fpregset_t fpstate = mc->fpregs; + pc = mc_pc(mc); + if ((fpstate->status >> 16) == X86_FXSR_MAGIC) + ((struct _fpstate*)fpstate)->mxcsr = 0x1F80; + fpstate->sw &= ~0xFF; +#elif defined(__sparc__) && defined(__arch64__) + /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */ + struct sigcontext *sc = (struct sigcontext*)puc; + pc = sc->sigc_regs.tpc; + sc->sigc_regs.tpc = sc->sigc_regs.tnpc; + sc->sigc_regs.tnpc += 4; +#elif defined(__sparc__) + /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */ + struct sigcontext *sc = (struct sigcontext*)puc; + pc = sc->si_regs.pc; + sc->si_regs.pc = sc->si_regs.npc; + sc->si_regs.npc = (unsigned long)sc->si_regs.npc + 4; +#elif defined(__powerpc__) +#if defined(__powerpc64__) + mcontext_t *mc = &uc->uc_mcontext; + unsigned long *regs = &mc->gp_regs[0]; +#else + mcontext_t *mc = uc->uc_mcontext.uc_regs; + unsigned long *regs = &mc->gregs[0]; +#endif + pc = regs[PT_NIP]; + regs[PT_NIP] += 4; + regs[PT_FPSCR] = 0x80|0x40|0x10; /* VE, OE, ZE; not UE or XE */ +#endif +#elif defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__)) +#ifdef DARWIN_MODERN_MCONTEXT + mcontext_t mc = uc->uc_mcontext; + pc = mc_pc(mc); + mc->__fs.__fpu_mxcsr = 0x1F80; + *(unsigned short *)&mc->__fs.__fpu_fsw &= ~0xFF; +#else + mcontext_t mc = uc->uc_mcontext; + pc = mc_pc(mc); + mc->fs.fpu_mxcsr = 0x1F80; + *(unsigned short *)&mc->fs.fpu_fsw &= ~0xFF; +#endif /* DARWIN_MODERN_MCONTEXT */ +#elif defined(__DARWIN__) && defined(__ppc__) + mcontext_t mc = uc->uc_mcontext; + pc = mc->ss.srr0; + mc->ss.srr0 += 4; + mc->fs.fpscr = 0x80|0x40|0x10; +#elif defined(__FreeBSD__) && defined(__x86_64__) + mcontext_t *mc = &uc->uc_mcontext; + struct savefpu *savefpu = (struct savefpu*)&mc->mc_fpstate; + struct envxmm *envxmm = &savefpu->sv_env; + pc = mc_pc(mc); + envxmm->en_mxcsr = 0x1F80; + envxmm->en_sw &= ~0xFF; +#elif defined(__FreeBSD__) && defined(__i386__) + mcontext_t *mc = &uc->uc_mcontext; + union savefpu *savefpu = (union savefpu*)&mc->mc_fpstate; + pc = mc_pc(mc); + if (mc->mc_fpformat == _MC_FPFMT_XMM) { + struct envxmm *envxmm = &savefpu->sv_xmm.sv_env; + envxmm->en_mxcsr = 0x1F80; + envxmm->en_sw &= ~0xFF; + } else { + struct env87 *env87 = &savefpu->sv_87.sv_env; + env87->en_sw &= ~0xFF; + } +#elif defined(__NetBSD__) && defined(__x86_64__) + mcontext_t *mc = &uc->uc_mcontext; + struct fxsave64 *fxsave = (struct fxsave64 *)&mc->__fpregs; + pc = mc_pc(mc); + fxsave->fx_mxcsr = 0x1F80; + fxsave->fx_fsw &= ~0xFF; +#elif defined(__NetBSD__) && defined(__i386__) + mcontext_t *mc = &uc->uc_mcontext; + pc = mc_pc(mc); + if (uc->uc_flags & _UC_FXSAVE) { + struct envxmm *envxmm = (struct envxmm *)&mc->__fpregs; + envxmm->en_mxcsr = 0x1F80; + envxmm->en_sw &= ~0xFF; + } else { + struct env87 *env87 = (struct env87 *)&mc->__fpregs; + env87->en_sw &= ~0xFF; + } +#elif defined(__OpenBSD__) && defined(__x86_64__) + struct fxsave64 *fxsave = uc->sc_fpstate; + pc = mc_pc(uc); + fxsave->fx_mxcsr = 0x1F80; + fxsave->fx_fsw &= ~0xFF; +#elif defined(__sun__) && defined(__x86_64__) + mcontext_t *mc = &uc->uc_mcontext; + struct fpchip_state *fpstate = &mc->fpregs.fp_reg_set.fpchip_state; + pc = mc_pc(mc); + fpstate->mxcsr = 0x1F80; + fpstate->sw &= ~0xFF; +#endif +#if 0 + { + char buf[64]; + snprintf(buf, sizeof buf, "%s: FPE at %p\r\n", __FUNCTION__, (void*)pc); + write(2, buf, strlen(buf)); + } +#endif + set_current_fp_exception(pc); +} + +static void erts_thread_catch_fp_exceptions(void) +{ + struct sigaction act; + memset(&act, 0, sizeof act); + act.sa_sigaction = fpe_sig_action; + act.sa_flags = SA_SIGINFO; + sigaction(SIGFPE, &act, NULL); + unmask_fpe(); +} + +#else /* !((__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */ + +static void fpe_sig_handler(int sig) +{ + set_current_fp_exception(1); /* XXX: convert to sigaction so we can get the trap PC */ +} + +static void erts_thread_catch_fp_exceptions(void) +{ + sys_sigset(SIGFPE, fpe_sig_handler); + unmask_fpe(); +} + +#endif /* (__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */ + +/* once-only initialisation early in the main thread */ +void erts_sys_init_float(void) +{ + erts_init_fp_exception(); + erts_thread_catch_fp_exceptions(); + erts_printf_block_fpe = erts_sys_block_fpe; + erts_printf_unblock_fpe = erts_sys_unblock_fpe; +} + +#endif /* NO_FPE_SIGNALS */ + +void erts_thread_init_float(void) +{ +#ifdef ERTS_SMP + /* This allows Erlang schedulers to leave Erlang-process context + and still have working FP exceptions. XXX: is this needed? */ + erts_thread_init_fp_exception(); +#endif + +#ifndef NO_FPE_SIGNALS + /* NOTE: + * erts_thread_disable_fpe() is called in all threads at + * creation. We at least need to call unmask_fpe() + */ +#if defined(__DARWIN__) || defined(__FreeBSD__) + /* Darwin (7.9.0) does not appear to propagate FP exception settings + to a new thread from its parent. So if we want FP exceptions, we + must manually re-enable them in each new thread. + FreeBSD 6.1 appears to suffer from a similar issue. */ + erts_thread_catch_fp_exceptions(); +#else + unmask_fpe(); +#endif + +#endif +} + +void erts_thread_disable_fpe(void) +{ +#if !defined(NO_FPE_SIGNALS) + (void)mask_fpe(); +#endif +} + +#if !defined(NO_FPE_SIGNALS) +int erts_sys_block_fpe(void) +{ + return mask_fpe(); +} + +void erts_sys_unblock_fpe(int unmasked) +{ + unmask_fpe_conditional(unmasked); +} +#endif + +/* The following check is incorporated from the Vee machine */ + +#define ISDIGIT(d) ((d) >= '0' && (d) <= '9') + +/* + ** Convert a double to ascii format 0.dddde[+|-]ddd + ** return number of characters converted or -1 if error. + ** + ** These two functions should maybe use localeconv() to pick up + ** the current radix character, but since it is uncertain how + ** expensive such a system call is, and since no-one has heard + ** of other radix characters than '.' and ',' an ad-hoc + ** low execution time solution is used instead. + */ + +int +sys_double_to_chars_ext(double fp, char *buffer, size_t buffer_size, size_t decimals) +{ + char *s = buffer; + + if (erts_snprintf(buffer, buffer_size, "%.*e", decimals, fp) >= buffer_size) + return -1; + /* Search upto decimal point */ + if (*s == '+' || *s == '-') s++; + while (ISDIGIT(*s)) s++; + if (*s == ',') *s++ = '.'; /* Replace ',' with '.' */ + /* Scan to end of string */ + while (*s) s++; + return s-buffer; /* i.e strlen(buffer) */ +} + +/* Float conversion */ + +int +sys_chars_to_double(char* buf, double* fp) +{ +#ifndef NO_FPE_SIGNALS + volatile unsigned long *fpexnp = erts_get_current_fp_exception(); +#endif + char *s = buf, *t, *dp; + + /* Robert says that something like this is what he really wanted: + * (The [.,] radix test is NOT what Robert wanted - it was added later) + * + * 7 == sscanf(Tbuf, "%[+-]%[0-9][.,]%[0-9]%[eE]%[+-]%[0-9]%s", ....); + * if (*s2 == 0 || *s3 == 0 || *s4 == 0 || *s6 == 0 || *s7) + * break; + */ + + /* Scan string to check syntax. */ + if (*s == '+' || *s == '-') s++; + if (!ISDIGIT(*s)) /* Leading digits. */ + return -1; + while (ISDIGIT(*s)) s++; + if (*s != '.' && *s != ',') /* Decimal part. */ + return -1; + dp = s++; /* Remember decimal point pos just in case */ + if (!ISDIGIT(*s)) + return -1; + while (ISDIGIT(*s)) s++; + if (*s == 'e' || *s == 'E') { + /* There is an exponent. */ + s++; + if (*s == '+' || *s == '-') s++; + if (!ISDIGIT(*s)) + return -1; + while (ISDIGIT(*s)) s++; + } + if (*s) /* That should be it */ + return -1; + +#ifdef NO_FPE_SIGNALS + errno = 0; +#endif + __ERTS_FP_CHECK_INIT(fpexnp); + *fp = strtod(buf, &t); + __ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1); + if (t != s) { /* Whole string not scanned */ + /* Try again with other radix char */ + *dp = (*dp == '.') ? ',' : '.'; + errno = 0; + __ERTS_FP_CHECK_INIT(fpexnp); + *fp = strtod(buf, &t); + __ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1); + } + +#ifdef NO_FPE_SIGNALS + if (errno == ERANGE) { + if (*fp == HUGE_VAL || *fp == -HUGE_VAL) { + /* overflow, should give error */ + return -1; + } else if (t == s && *fp == 0.0) { + /* This should give 0.0 - OTP-7178 */ + errno = 0; + + } else if (*fp == 0.0) { + return -1; + } + } +#endif + return 0; +} + +int +matherr(struct exception *exc) +{ +#if !defined(NO_FPE_SIGNALS) + volatile unsigned long *fpexnp = erts_get_current_fp_exception(); + if (fpexnp != NULL) + *fpexnp = (unsigned long)__builtin_return_address(0); +#endif + return 1; +} diff --git a/erts/emulator/sys/ose/sys_time.c b/erts/emulator/sys/ose/sys_time.c new file mode 100644 index 0000000000..7e96f68424 --- /dev/null +++ b/erts/emulator/sys/ose/sys_time.c @@ -0,0 +1,56 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2005-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 "global.h" + +/******************* Routines for time measurement *********************/ + +int erts_ticks_per_sec = 0; /* Will be SYS_CLK_TCK in erl_unix_sys.h */ + +int sys_init_time(void) +{ + return SYS_CLOCK_RESOLUTION; +} + +clock_t sys_times(SysTimes *now) { + now->tms_utime = now->tms_stime = now->tms_cutime = now->tms_cstime = 0; + return 0; +} + +static OSTICK last_tick_count = 0; +static SysHrTime wrap = 0; +static OSTICK us_per_tick; + +void sys_init_hrtime() { + us_per_tick = system_tick(); +} + +SysHrTime sys_gethrtime() { + OSTICK ticks = get_ticks(); + if (ticks < (SysHrTime) last_tick_count) { + wrap += 1ULL << 32; + } + last_tick_count = ticks; + return ((((SysHrTime) ticks) + wrap) * 1000*us_per_tick); +} diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in index e94674e6f4..faf0101ad7 100644 --- a/erts/epmd/src/Makefile.in +++ b/erts/epmd/src/Makefile.in @@ -18,6 +18,9 @@ # include $(ERL_TOP)/make/target.mk +ifeq ($(findstring ose,$(TARGET)),ose) +include $(ERL_TOP)/make/$(TARGET)/ose_lm.mk +endif ifeq ($(TYPE),debug) PURIFY = @@ -64,9 +67,13 @@ else ifeq ($(findstring vxworks,$(TARGET)),vxworks) ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ else +ifeq ($(findstring ose,$(TARGET)),ose) +ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ +else ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ -lm endif endif +endif ERTS_LIB = $(ERL_TOP)/erts/lib_src/obj/$(TARGET)/$(TYPE)/MADE @@ -74,7 +81,11 @@ CC = @CC@ WFLAGS = @WFLAGS@ CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) $(WFLAGS) $(ERTS_INCL) LD = @LD@ +ifeq ($(findstring ose,$(TARGET)),ose) +LIBS = $(ERTS_INTERNAL_LIBS) @LIBS@ +else LIBS = @LIBS@ $(ERTS_INTERNAL_LIBS) +endif LDFLAGS = @LDFLAGS@ @@ -123,12 +134,25 @@ clean: rm -f *.o rm -f *~ core +ifeq ($(findstring ose,$(TARGET)),ose) +$(OBJDIR)/ose_confd.o: $(OSEROOT)/src/ose_confd.c + $(V_CC) $(CFLAGS) -o $@ -c $< +$(OBJDIR)/crt0_lm.o: $(OSEROOT)/src/crt0_lm.c + $(V_CC) $(CFLAGS) -o $@ -c $< +OSE_LM_OBJS += $(OBJDIR)/ose_confd.o $(OBJDIR)/crt0_lm.o +endif + # # Objects & executables # +ifeq ($(findstring ose,$(TARGET)),ose) +$(BINDIR)/$(EPMD): $(EPMD_OBJS) $(ERTS_LIB) $(OSE_LM_OBJS) + $(call build-ose-load-module, $@, $(EPMD_OBJS) $(OSE_LM_OBJS), $(LIBS), $(LMCONF)) +else $(BINDIR)/$(EPMD): $(EPMD_OBJS) $(ERTS_LIB) $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(EPMD_OBJS) $(LIBS) +endif $(OBJDIR)/%.o: %.c epmd.h epmd_int.h $(V_CC) $(CFLAGS) $(EPMD_FLAGS) -o $@ -c $< diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 2d55b37ff3..5d5c3a1c3c 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -389,7 +389,7 @@ static void run_daemon(EpmdVars *g) } #endif -#if defined(VXWORKS) +#if defined(VXWORKS) || defined(__OSE__) static void run_daemon(EpmdVars *g) { run(g); diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index 656dbd1f45..d4597be30c 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -36,6 +36,13 @@ #define DONT_USE_MAIN #endif +#ifdef __OSE__ +# define NO_DAEMON +# define NO_SYSLOG +# define NO_SYSCONF +# define NO_FCNTL +#endif + /* ************************************************************************ */ /* Standard includes */ @@ -92,7 +99,11 @@ #endif /* ! WIN32 */ #include -#include + +#if !defined(__OSE__) +# include +#endif + #include @@ -110,6 +121,11 @@ #include +#ifdef __OSE__ +# include "sys/select.h" +#endif + + /* ************************************************************************ */ /* Replace some functions by others by making the function name a macro */ diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 90df7cc25a..247fd34d5a 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -29,6 +29,11 @@ # define INADDR_NONE 0xffffffff #endif +#if defined(__OSE__) +# include "sys/ioctl.h" +# define sleep(x) delay(x*1000) +#endif + /* * * This server is a local name server for Erlang nodes. Erlang nodes can @@ -273,7 +278,7 @@ void run(EpmdVars *g) num_sockets = 1; } -#if !defined(__WIN32__) +#if !defined(__WIN32__) && !defined(__OSE__) /* We ignore the SIGPIPE signal that is raised when we call write twice on a socket closed by the other end. */ signal(SIGPIPE, SIG_IGN); diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index 5c1ce51644..d7fe75ce6f 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -20,6 +20,10 @@ include $(ERL_TOP)/make/output.mk include $(ERL_TOP)/make/target.mk +ifeq ($(findstring ose,$(TARGET)),ose) +include $(ERL_TOP)/make/$(TARGET)/ose_lm.mk +endif + ERTS_LIB_TYPEMARKER=.$(TYPE) USING_MINGW=@MIXED_CYGWIN_MINGW@ @@ -161,7 +165,26 @@ endif PORT_ENTRY_POINT=erl_port_entry ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT) -else # UNIX (!win32) +else +ifeq ($(findstring ose,$(TARGET)),ose) +ENTRY_LDFLAGS= +ENTRY_OBJ= +ERLSRV_OBJECTS= +MC_OUTPUTS= +INET_GETHOST = +INSTALL_EMBEDDED_PROGS = +INSTALL_EMBEDDED_DATA = +INSTALL_TOP = Install +INSTALL_TOP_BIN = +INSTALL_MISC = +INSTALL_SRC = +ERLEXECDIR = . +INSTALL_LIBS = +INSTALL_OBJS = +INSTALL_INCLUDES = +TEXTFILES = Install erl.src +INSTALL_PROGS = +else # UNIX (!win32 && !ose) ENTRY_LDFLAGS= ENTRY_OBJ= ERLSRV_OBJECTS= @@ -186,6 +209,7 @@ INSTALL_PROGS = \ $(BINDIR)/$(ERLEXEC) \ $(INSTALL_EMBEDDED_PROGS) endif +endif .PHONY: etc etc: $(ENTRY_OBJ) $(INSTALL_PROGS) $(INSTALL_LIBS) $(TEXTFILES) $(INSTALL_TOP_BIN) @@ -370,27 +394,27 @@ $(OBJDIR)/heart.o: heart.c $(RC_GENERATED) $(OBJDIR)/inet_gethost.o: inet_gethost.c $(RC_GENERATED) $(V_CC) $(CFLAGS) -o $@ -c inet_gethost.c +# inet_gethost $(BINDIR)/inet_gethost@EXEEXT@: $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(ERTS_LIB) $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(LIBS) $(ERTS_INTERNAL_LIBS) +# run_erl $(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o - $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(LIBS) - + $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(LIBS) $(ERTS_INTERNAL_LIBS) $(OBJDIR)/run_erl.o: ../unix/run_erl.c $(RC_GENERATED) $(V_CC) $(CFLAGS) -o $@ -c ../unix/run_erl.c +# to_erl $(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o - $(OBJDIR)/to_erl.o: ../unix/to_erl.c $(RC_GENERATED) $(V_CC) $(CFLAGS) -o $@ -c ../unix/to_erl.c +# dyn_erl $(BINDIR)/dyn_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o - $(OBJDIR)/dyn_erl.o: ../unix/dyn_erl.c $(RC_GENERATED) $(V_CC) $(CFLAGS) -o $@ -c ../unix/dyn_erl.c - $(OBJDIR)/safe_string.o: ../unix/safe_string.c $(RC_GENERATED) $(V_CC) $(CFLAGS) -o $@ -c ../unix/safe_string.c @@ -401,6 +425,7 @@ $(BINDIR)/$(ERLEXEC): $(OBJDIR)/$(ERLEXEC).o $(ERTS_LIB) $(OBJDIR)/$(ERLEXEC).o: $(ERLEXECDIR)/$(ERLEXEC).c $(RC_GENERATED) $(V_CC) -I$(EMUDIR) $(CFLAGS) -o $@ -c $(ERLEXECDIR)/$(ERLEXEC).c endif + $(BINDIR)/erlc@EXEEXT@: $(OBJDIR)/erlc.o $(ERTS_LIB) $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS) @@ -478,11 +503,6 @@ ifneq ($(INSTALL_MISC),) $(INSTALL_DIR) "$(RELEASE_PATH)/misc" $(INSTALL_SCRIPT) $(INSTALL_MISC) "$(RELEASE_PATH)/misc" endif -ifneq ($(INSTALL_ERL_OSE),) - $(INSTALL_DIR) "$(RELEASE_PATH)/build_erl_ose" - cd $(OSEETC) && $(TAR) erl_ose_$(SYSTEM_VSN).tar $(INSTALL_ERL_OSE) - cd $(OSEETC) && $(INSTALL_SCRIPT) erl_ose_$(SYSTEM_VSN).tar "$(RELEASE_PATH)/build_erl_ose" -endif ifneq ($(INSTALL_SRC),) $(INSTALL_DIR) "$(RELEASE_PATH)/erts-$(VSN)/src" $(INSTALL_DATA) $(INSTALL_SRC) "$(RELEASE_PATH)/erts-$(VSN)/src" diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c index bef97862a3..9ec4192667 100644 --- a/erts/etc/common/inet_gethost.c +++ b/erts/etc/common/inet_gethost.c @@ -1209,7 +1209,7 @@ static void start_que_request(Worker *w) #endif } -#ifndef WIN32 +#ifndef WIN32 /* Signal utilities */ static RETSIGTYPE (*sys_sigset(int sig, RETSIGTYPE (*func)(int)))(int) { diff --git a/erts/etc/ose/run_erl.c b/erts/etc/ose/run_erl.c new file mode 100644 index 0000000000..7e62502be2 --- /dev/null +++ b/erts/etc/ose/run_erl.c @@ -0,0 +1,34 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013. All Rights Reserved. + * + * The 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: run_erl.c + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +int main(int argc, char **argv) +{ + fprintf(stderr, "run_erl is not supported on OSE targets.\n"); + return 1; +} diff --git a/erts/etc/ose/to_erl.c b/erts/etc/ose/to_erl.c new file mode 100644 index 0000000000..af672159e2 --- /dev/null +++ b/erts/etc/ose/to_erl.c @@ -0,0 +1,34 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013. All Rights Reserved. + * + * The 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: to_erl.c + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +int main(int argc, char **argv) +{ + fprintf(stderr, "to_erl is not supported on OSE targets.\n"); + return 1; +} diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 8520d58f47..9e39764195 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -3133,6 +3133,24 @@ document etp-ets-tabledump %--------------------------------------------------------------------------- end + +############################################################################ +# OSE support +# +define etp-ose-attach + target ose $arg0:21768 + attach block start_beam start_beam +end + +document etp-ose-attach +%--------------------------------------------------------------------------- +% etp-ose-attach Host +% +% Connect and attach to erlang vm at Host. +%--------------------------------------------------------------------------- +end + + ############################################################################ # Erlang support module handling # diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h index 86a1e9fbdf..9511add075 100644 --- a/erts/include/internal/ethr_mutex.h +++ b/erts/include/internal/ethr_mutex.h @@ -103,6 +103,9 @@ void LeaveCriticalSection(CRITICAL_SECTION *); #elif defined(ETHR_WIN32_THREADS) # define ETHR_MTX_Q_LOCK_CRITICAL_SECTION__ # define ETHR_MTX_QLOCK_TYPE__ CRITICAL_SECTION +#elif defined(ETHR_OSE_THREADS) +# define ETHR_MTX_Q_LOCK_PTHREAD_MUTEX__ +# define ETHR_MTX_QLOCK_TYPE__ pthread_mutex_t #else # error Need a qlock implementation #endif @@ -255,6 +258,25 @@ struct ethr_cond_ { #endif }; +#elif defined(ETHR_OSE_THREADS) + +typedef struct ethr_mutex_ ethr_mutex; +struct ethr_mutex_ { + pthread_mutex_t pt_mtx; +#if ETHR_XCHK + int initialized; +#endif +}; + +typedef struct ethr_cond_ ethr_cond; +struct ethr_cond_ { + pthread_cond_t pt_cnd; +#if ETHR_XCHK + int initialized; +#endif +}; + + #else # error "no mutex implementation" #endif diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 38b8e9e9b6..581eb22edb 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -37,6 +37,11 @@ # define ETHR_DEBUG #endif +#if defined(__PPC__) || defined (__POWERPC) +/* OSE compiler should be fixed! */ +#define __ppc__ +#endif + #if defined(ETHR_DEBUG) # undef ETHR_XCHK # define ETHR_XCHK 1 @@ -190,6 +195,36 @@ typedef DWORD ethr_tsd_key; #define ETHR_YIELD() (Sleep(0), 0) +#elif defined(ETHR_OSE_THREADS) + +#include "ose.h" +#undef NIL + +#if defined(ETHR_HAVE_PTHREAD_H) +#include +#endif + +typedef struct { + PROCESS id; + unsigned int tsd_key_index; + void *res; +} ethr_tid; + +struct ethr_tsd_key__ { + PROCESS id; + char key[]; +}; +typedef struct ethr_tsd_key__* ethr_tsd_key; + +#undef ETHR_HAVE_ETHR_SIG_FUNCS + +#define ETHR_USE_OWN_RWMTX_IMPL__ +#define ETHR_USE_OWN_MTX_IMPL__ +#define ETHR_HAVE_THREAD_NAMES +#define ETHR_HAVE_THREAD_NICENESS + +#define ETHR_PPC_HAVE_NO_LWSYNC + #else /* No supported thread lib found */ #ifdef ETHR_NO_SUPP_THR_LIB_NOT_FATAL @@ -367,7 +402,7 @@ extern ethr_runtime_t ethr_runtime__; #include "ethr_atomics.h" /* The atomics API */ -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(ETHR_OSE_THREADS) # ifndef ETHR_SPIN_BODY # if defined(__i386__) || defined(__x86_64__) # define ETHR_SPIN_BODY __asm__ __volatile__("rep;nop" : : : "memory") @@ -383,9 +418,20 @@ extern ethr_runtime_t ethr_runtime__; # ifndef ETHR_SPIN_BODY # define ETHR_SPIN_BODY do {YieldProcessor();ETHR_COMPILER_BARRIER;} while(0) # endif +#elif defined(ETHR_OSE_THREADS) +# ifndef ETHR_SPIN_BODY +# define ETHR_SPIN_BODY delay(1) +# else +# error "Have to use delay on OSE" +# endif #endif +#ifndef ETHR_OSE_THREADS #define ETHR_YIELD_AFTER_BUSY_LOOPS 50 +#else +#define ETHR_YIELD_AFTER_BUSY_LOOPS 0 +#endif + #ifndef ETHR_SPIN_BODY # define ETHR_SPIN_BODY ETHR_COMPILER_BARRIER @@ -408,6 +454,8 @@ extern ethr_runtime_t ethr_runtime__; # else # define ETHR_YIELD() (pthread_yield(), 0) # endif +# elif defined(ETHR_OSE_THREADS) +# define ETHR_YIELD() (delay(1), 0) # else # define ETHR_YIELD() (ethr_compiler_barrier(), 0) # endif @@ -459,9 +507,20 @@ typedef struct { typedef struct { int detached; /* boolean (default false) */ int suggested_stack_size; /* kilo words (default sys dependent) */ +#ifdef ETHR_OSE_THREADS + char *name; + OSPRIORITY prio; + U32 coreNo; +#endif } ethr_thr_opts; +#if defined(ETHR_OSE_THREADS) +/* Default ethr name is big as we want to be able to sprint stuff in there */ +#define ETHR_THR_OPTS_DEFAULT_INITER \ + {0, -1, "ethread", get_pri(current_process()), 0} +#else #define ETHR_THR_OPTS_DEFAULT_INITER {0, -1} +#endif #if !defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) @@ -571,8 +630,10 @@ typedef struct ethr_ts_event_ ethr_ts_event; /* Needed by ethr_mutex.h */ #if defined(ETHR_WIN32_THREADS) # include "win/ethr_event.h" -#else +#elif defined(ETHR_PTHREADS) # include "pthread/ethr_event.h" +#elif defined(ETHR_OSE_THREADS) +# include "ose/ethr_event.h" #endif int ethr_set_main_thr_status(int, int); @@ -662,6 +723,38 @@ ETHR_INLINE_FUNC_NAME_(ethr_leave_ts_event)(ethr_ts_event *tsep) #endif +#elif defined (ETHR_OSE_THREADS) + +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHREAD_IMPL__) + +extern char* ethr_ts_event_key__; + +static ETHR_INLINE ethr_ts_event * +ETHR_INLINE_FUNC_NAME_(ethr_get_ts_event)(void) +{ + ethr_ts_event *tsep = (ethr_ts_event *)get_envp(current_process(), + ethr_ts_event_key__); + if (!tsep) { + int res = ethr_get_tmp_ts_event__(&tsep); + if (res != 0) + ETHR_FATAL_ERROR__(res); + ETHR_ASSERT(tsep); + } + return tsep; +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_leave_ts_event)(ethr_ts_event *tsep) +{ + if (tsep->iflgs & ETHR_TS_EV_TMP) { + int res = ethr_free_ts_event__(tsep); + if (res != 0) + ETHR_FATAL_ERROR__(res); + } +} + +#endif + #endif #include "ethr_mutex.h" /* Need atomic declarations and tse */ diff --git a/erts/include/internal/ose/ethr_event.h b/erts/include/internal/ose/ethr_event.h new file mode 100644 index 0000000000..86811a87db --- /dev/null +++ b/erts/include/internal/ose/ethr_event.h @@ -0,0 +1,104 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2009-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: Rickard Green + */ + +//#define USE_PTHREAD_API + +#define ETHR_EVENT_OFF_WAITER__ -1L +#define ETHR_EVENT_OFF__ 1L +#define ETHR_EVENT_ON__ 0L + +#ifdef USE_PTHREAD_API + +typedef struct { + ethr_atomic32_t state; + pthread_mutex_t mtx; + pthread_cond_t cnd; +} ethr_event; + +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__) + +static void ETHR_INLINE +ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) +{ + ethr_sint32_t val; + val = ethr_atomic32_xchg_mb(&e->state, ETHR_EVENT_ON__); + if (val == ETHR_EVENT_OFF_WAITER__) { + int res = pthread_mutex_lock(&e->mtx); + if (res != 0) + ETHR_FATAL_ERROR__(res); + res = pthread_cond_signal(&e->cnd); + if (res != 0) + ETHR_FATAL_ERROR__(res); + res = pthread_mutex_unlock(&e->mtx); + if (res != 0) + ETHR_FATAL_ERROR__(res); + } +} + +static void ETHR_INLINE +ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) +{ + ethr_atomic32_set(&e->state, ETHR_EVENT_OFF__); + ETHR_MEMORY_BARRIER; +} + +#endif + +#else + +typedef struct { + ethr_atomic32_t state; + PROCESS proc; +} ethr_event; + +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__) + +static void ETHR_INLINE +ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) +{ + ethr_sint32_t val = ethr_atomic32_xchg_mb(&e->state, ETHR_EVENT_ON__); + if (val == ETHR_EVENT_OFF_WAITER__) { + ETHR_ASSERT(get_fsem(e->proc) == -1); + signal_fsem(e->proc); + } +} + +static void ETHR_INLINE +ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) +{ + ethr_atomic32_set(&e->state, ETHR_EVENT_OFF__); + ETHR_MEMORY_BARRIER; +} + +#endif + +#endif + +int ethr_event_init(ethr_event *e); +int ethr_event_destroy(ethr_event *e); +int ethr_event_wait(ethr_event *e); +int ethr_event_swait(ethr_event *e, int spincount); +#if !defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__) +void ethr_event_set(ethr_event *e); +void ethr_event_reset(ethr_event *e); +#endif diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c index b32681f40e..5a271c5268 100644 --- a/erts/lib_src/common/erl_misc_utils.c +++ b/erts/lib_src/common/erl_misc_utils.c @@ -158,6 +158,8 @@ erts_milli_sleep(long ms) if (ms > 0) { #ifdef __WIN32__ Sleep((DWORD) ms); +#elif defined(__OSE__) + delay(ms); #else struct timeval tv; tv.tv_sec = ms / 1000; @@ -316,6 +318,10 @@ erts_cpu_info_update(erts_cpu_info_t *cpuinfo) online = 0; #endif } +#elif defined(__OSE__) + online = ose_num_cpus(); + configured = ose_num_cpus(); + available = ose_num_cpus(); #endif if (online > configured) diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c index f4ff08d368..ceecdcef64 100644 --- a/erts/lib_src/common/ethr_aux.c +++ b/erts/lib_src/common/ethr_aux.c @@ -204,7 +204,18 @@ ethr_init_common__(ethr_init_data *id) ethr_min_stack_size__ = ETHR_B2KW(ethr_min_stack_size__); +#ifdef __OSE__ + /* For supervisor processes, OSE adds a number of bytes to the requested stack. With this + * addition, the resulting size must not exceed the largest available stack size. The number + * of bytes that will be added is configured in the monolith and can therefore not be + * specified here. We simply assume that it is less than 0x1000. The available stack sizes + * are configured in the .lmconf file and the largest one is usually 65536 bytes. + * Consequently, the requested stack size is limited to 0xF000. + */ + ethr_max_stack_size__ = 0xF000; +#else ethr_max_stack_size__ = 32*1024*1024; +#endif #if SIZEOF_VOID_P == 8 ethr_max_stack_size__ *= 2; #endif @@ -650,6 +661,10 @@ ETHR_IMPL_NORETURN__ ethr_fatal_error__(const char *file, int ethr_assert_failed(const char *file, int line, const char *func, char *a) { fprintf(stderr, "%s:%d: %s(): Assertion failed: %s\n", file, line, func, a); +#ifdef __OSE__ + ramlog_printf("%d: %s:%d: %s(): Assertion failed: %s\n", + current_process(),file, line, func, a); +#endif ethr_abort__(); return 0; } diff --git a/erts/lib_src/ose/ethr_event.c b/erts/lib_src/ose/ethr_event.c new file mode 100644 index 0000000000..1cb3c223ab --- /dev/null +++ b/erts/lib_src/ose/ethr_event.c @@ -0,0 +1,213 @@ +/* + * %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% + */ + +/* + * Author: Rickard Green + */ + +#define ETHR_INLINE_FUNC_NAME_(X) X ## __ +#define ETHR_EVENT_IMPL__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ethread.h" + +#ifdef USE_PTHREAD_API + +int +ethr_event_init(ethr_event *e) +{ + int res; + ethr_atomic32_init(&e->state, ETHR_EVENT_OFF__); + res = pthread_mutex_init(&e->mtx, NULL); + if (res != 0) + return res; + res = pthread_cond_init(&e->cnd, NULL); + if (res != 0) { + pthread_mutex_destroy(&e->mtx); + return res; + } + return 0; +} + +int +ethr_event_destroy(ethr_event *e) +{ + int res; + res = pthread_mutex_destroy(&e->mtx); + if (res != 0) + return res; + res = pthread_cond_destroy(&e->cnd); + if (res != 0) + return res; + return 0; +} + +static ETHR_INLINE int +wait__(ethr_event *e, int spincount) +{ + int sc = spincount; + ethr_sint32_t val; + int res, ulres; + int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; + + if (spincount < 0) + ETHR_FATAL_ERROR__(EINVAL); + + while (1) { + val = ethr_atomic32_read(&e->state); + if (val == ETHR_EVENT_ON__) + return 0; + if (sc == 0) + break; + sc--; + ETHR_SPIN_BODY; + if (--until_yield == 0) { + until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; + res = ETHR_YIELD(); + if (res != 0) + ETHR_FATAL_ERROR__(res); + } + } + + if (val != ETHR_EVENT_OFF_WAITER__) { + val = ethr_atomic32_cmpxchg(&e->state, + ETHR_EVENT_OFF_WAITER__, + ETHR_EVENT_OFF__); + if (val == ETHR_EVENT_ON__) + return 0; + ETHR_ASSERT(val == ETHR_EVENT_OFF__); + } + + ETHR_ASSERT(val == ETHR_EVENT_OFF_WAITER__ + || val == ETHR_EVENT_OFF__); + + res = pthread_mutex_lock(&e->mtx); + if (res != 0) + ETHR_FATAL_ERROR__(res); + + while (1) { + + val = ethr_atomic32_read(&e->state); + if (val == ETHR_EVENT_ON__) + break; + + res = pthread_cond_wait(&e->cnd, &e->mtx); + if (res == EINTR) + break; + if (res != 0) + ETHR_FATAL_ERROR__(res); + } + + ulres = pthread_mutex_unlock(&e->mtx); + if (ulres != 0) + ETHR_FATAL_ERROR__(ulres); + + return res; /* 0 || EINTR */ +} + +#else +/* --- OSE implementation of events ---------------------------- */ + +int +ethr_event_init(ethr_event *e) +{ + ethr_atomic32_init(&e->state, ETHR_EVENT_OFF__); + e->proc = current_process(); + return 0; +} + +int +ethr_event_destroy(ethr_event *e) +{ + return 0; +} + +static ETHR_INLINE int +wait__(ethr_event *e, int spincount) +{ + int sc = spincount; + int res; + int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; + + if (spincount < 0) + ETHR_FATAL_ERROR__(EINVAL); + + ETHR_ASSERT(e->proc == current_process()); + ETHR_ASSERT(get_fsem(current_process()) == 0); + + while (1) { + ethr_sint32_t val; + while (1) { + val = ethr_atomic32_read(&e->state); + if (val == ETHR_EVENT_ON__) + return 0; + if (sc == 0) + break; + sc--; + ETHR_SPIN_BODY; + if (--until_yield == 0) { + until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; + res = ETHR_YIELD(); + if (res != 0) + ETHR_FATAL_ERROR__(res); + } + } + if (val != ETHR_EVENT_OFF_WAITER__) { + val = ethr_atomic32_cmpxchg(&e->state, + ETHR_EVENT_OFF_WAITER__, + ETHR_EVENT_OFF__); + if (val == ETHR_EVENT_ON__) + return 0; + ETHR_ASSERT(val == ETHR_EVENT_OFF__); + } + + + wait_fsem(1); + ETHR_ASSERT(get_fsem(current_process()) == 0); + } +} + +#endif + +void +ethr_event_reset(ethr_event *e) +{ + ethr_event_reset__(e); +} + +void +ethr_event_set(ethr_event *e) +{ + ethr_event_set__(e); +} + +int +ethr_event_wait(ethr_event *e) +{ + return wait__(e, 0); +} + +int +ethr_event_swait(ethr_event *e, int spincount) +{ + return wait__(e, spincount); +} diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c new file mode 100644 index 0000000000..7d5101d115 --- /dev/null +++ b/erts/lib_src/ose/ethread.c @@ -0,0 +1,654 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-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: OSE implementation of the ethread library + * Author: Lukas Larsson + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#ifdef ETHR_TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef ETHR_HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include +#include + +#include + +#define ETHR_INLINE_FUNC_NAME_(X) X ## __ +#define ETHREAD_IMPL__ + +#include "ethread.h" +#include "ethr_internal.h" + +#include "erl_printf.h" +#include "efs.h" + +#include "ose_spi.h" + +#include "string.h" + +#ifndef ETHR_HAVE_ETHREAD_DEFINES +#error Missing configure defines +#endif + +#define ETHR_INVALID_TID_ID -1 + +static ethr_tid main_thr_tid; +static const char* own_tid_key = "ethread_own_tid"; +char* ethr_ts_event_key__ = "ethread_tse"; + +#define ETHREADWRAPDATASIG 1 + +/* Init data sent to thr_wrapper() */ +typedef struct { + SIGSELECT sig_no; + ethr_ts_event *tse; + ethr_tid *tid; + ethr_sint32_t result; + void *(*thr_func)(void *); + void *arg; + void *prep_func_res; + const char *name; +} ethr_thr_wrap_data__; + +union SIGNAL { + SIGSELECT sig_no; + ethr_thr_wrap_data__ data; +}; + +#define ETHR_GET_OWN_TID__ ((ethr_tid *) get_envp(current_process(),\ + own_tid_key)) + +/* + * -------------------------------------------------------------------------- + * Static functions + * -------------------------------------------------------------------------- + */ + +static PROCESS blockId(void) { + static PROCESS bid = (PROCESS)0; + + if (bid == 0) { + bid = create_block("Erlang-VM", 0, 0, 0, 0); + } + + return bid; +} + +static void thr_exit_cleanup(ethr_tid *tid, void *res) +{ + + ETHR_ASSERT(tid == ETHR_GET_OWN_TID__); + + tid->res = res; + + ethr_run_exit_handlers__(); + ethr_ts_event_destructor__((void *) ethr_get_tse__()); +} + +/* For debug purpose. The process name will be stored in a per-process pointer + * for quick access. + */ +static OSPPDKEY nameKey = 0; + +//static OS_PROCESS(thr_wrapper); +static OS_PROCESS(thr_wrapper) +{ + ethr_tid my_tid; + ethr_sint32_t result; + void *res; + void *(*thr_func)(void *); + void *arg; + ethr_ts_event *tsep = NULL; + + { + PROCESS pid = current_process(); + + const char *execMode = get_pid_info(pid, OSE_PI_SUPERVISOR) + ? "Supervisor" + : "User"; + PROCESS bid = get_bid(pid); + + /* In the call below, 16 is a secret number provided by frbr that makes + * the function return current domain. */ + OSADDRESS domain = get_pid_info(current_process(), 16); + + fprintf(stderr,"[0x%x] New process. Bid:0x%x, domain:%d, exec mode:%s\n", + current_process(), bid, domain, execMode); + } + + { + SIGSELECT sigsel[] = {1,ETHREADWRAPDATASIG}; + union SIGNAL *init_msg = receive(sigsel); + + { + char **name = (char**)ose_get_ppdata(nameKey); + + *name = (char*)alloc(strlen(init_msg->data.name)+1, 0); + strcpy(*name, init_msg->data.name); + } + + thr_func = init_msg->data.thr_func; + arg = init_msg->data.arg; + + result = (ethr_sint32_t) ethr_make_ts_event__(&tsep); + + if (result == 0) { + tsep->iflgs |= ETHR_TS_EV_ETHREAD; + my_tid = *init_msg->data.tid; + set_envp(current_process(), own_tid_key, (OSADDRESS)&my_tid); + if (ethr_thr_child_func__) + ethr_thr_child_func__(init_msg->data.prep_func_res); + } + + init_msg->data.result = result; + + send(&init_msg,sender(&init_msg)); + } + + /* pthread mutex api says we have to do this */ + /*erts_fprintf(stderr, "(%s / %d) curr_pid 0x%x / signal_fsem to 0x%x (fsem val %d)\n", + __FUNCTION__, __LINE__, current_process(), current_process(), get_fsem(current_process()));*/ + signal_fsem(current_process()); + ETHR_ASSERT(get_fsem(current_process()) == 0); + + res = result == 0 ? (*thr_func)(arg) : NULL; + + thr_exit_cleanup(&my_tid, res); +} + +/* internal exports */ + +int ethr_set_tse__(ethr_ts_event *tsep) +{ + return set_envp(current_process(),ethr_ts_event_key__, (OSADDRESS) tsep); +} + +ethr_ts_event *ethr_get_tse__(void) +{ + return (ethr_ts_event *) get_envp(current_process(),ethr_ts_event_key__); +} + +#if defined(ETHR_PPC_RUNTIME_CONF__) + +static int +ppc_init__(void) +{ + int pid; + + + ethr_runtime__.conf.have_lwsync = 0; + + return 0; +} + +#endif + +#if defined(ETHR_X86_RUNTIME_CONF__) + +void +ethr_x86_cpuid__(int *eax, int *ebx, int *ecx, int *edx) +{ +#if ETHR_SIZEOF_PTR == 4 + int have_cpuid; + /* + * If it is possible to toggle eflags bit 21, + * we have the cpuid instruction. + */ + __asm__ ("pushf\n\t" + "popl %%eax\n\t" + "movl %%eax, %%ecx\n\t" + "xorl $0x200000, %%eax\n\t" + "pushl %%eax\n\t" + "popf\n\t" + "pushf\n\t" + "popl %%eax\n\t" + "movl $0x0, %0\n\t" + "xorl %%ecx, %%eax\n\t" + "jz no_cpuid\n\t" + "movl $0x1, %0\n\t" + "no_cpuid:\n\t" + : "=r"(have_cpuid) + : + : "%eax", "%ecx", "cc"); + if (!have_cpuid) { + *eax = *ebx = *ecx = *edx = 0; + return; + } +#endif +#if ETHR_SIZEOF_PTR == 4 && defined(__PIC__) && __PIC__ + /* + * When position independet code is used in 32-bit mode, the B register + * is used for storage of global offset table address, and we may not + * use it as input or output in an asm. We need to save and restore the + * B register explicitly (for some reason gcc doesn't provide this + * service to us). + */ + __asm__ ("pushl %%ebx\n\t" + "cpuid\n\t" + "movl %%ebx, %1\n\t" + "popl %%ebx\n\t" + : "=a"(*eax), "=r"(*ebx), "=c"(*ecx), "=d"(*edx) + : "0"(*eax) + : "cc"); +#else + __asm__ ("cpuid\n\t" + : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) + : "0"(*eax) + : "cc"); +#endif +} + +#endif /* ETHR_X86_RUNTIME_CONF__ */ + +/* + * -------------------------------------------------------------------------- + * Exported functions + * -------------------------------------------------------------------------- + */ + +int +ethr_init(ethr_init_data *id) +{ + int res; + + if (!ethr_not_inited__) + return EINVAL; + + +#if defined(ETHR_PPC_RUNTIME_CONF__) + res = ppc_init__(); + if (res != 0) + goto error; +#endif + + res = ethr_init_common__(id); + if (res != 0) + goto error; + + main_thr_tid.id = current_process(); + main_thr_tid.tsd_key_index = 0; + + set_envp(current_process(),own_tid_key,(OSADDRESS)&main_thr_tid); + /*erts_fprintf(stderr, "(%s / %d) curr_pid 0x%x / signal_fsem to 0x%x (fsem_val %d)\n", + __FUNCTION__, __LINE__, current_process(), current_process(), get_fsem(current_process()));*/ + signal_fsem(current_process()); + + ETHR_ASSERT(&main_thr_tid == ETHR_GET_OWN_TID__); + + ose_create_ppdata("ProcName", &nameKey); + + ethr_not_inited__ = 0; + + return 0; + error: + ethr_not_inited__ = 1; + return res; + +} + +int +ethr_late_init(ethr_late_init_data *id) +{ + int res = ethr_late_init_common__(id); + if (res != 0) + return res; + ethr_not_completely_inited__ = 0; + return res; +} + +int +ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, + ethr_thr_opts *opts) +{ + int res; + int use_stack_size = (opts && opts->suggested_stack_size >= 0 + ? opts->suggested_stack_size + : 0x200 /* Use system default */); + union SIGNAL *init_msg; + SIGSELECT sigsel[] = {1,ETHREADWRAPDATASIG}; + void *prep_func_res; + +#ifdef ETHR_MODIFIED_DEFAULT_STACK_SIZE + if (use_stack_size < 0) + use_stack_size = ETHR_MODIFIED_DEFAULT_STACK_SIZE; +#endif + +#if ETHR_XCHK + if (ethr_not_completely_inited__) { + ETHR_ASSERT(0); + return EACCES; + } + if (!tid || !func) { + ETHR_ASSERT(0); + return EINVAL; + } +#endif + + if (use_stack_size >= 0) { + size_t suggested_stack_size = (size_t) use_stack_size; + size_t stack_size; +#ifdef ETHR_DEBUG + suggested_stack_size /= 2; /* Make sure we got margin */ +#endif +#ifdef ETHR_STACK_GUARD_SIZE + /* The guard is at least on some platforms included in the stack size + passed when creating threads */ + suggested_stack_size += ETHR_B2KW(ETHR_STACK_GUARD_SIZE); +#endif + + if (suggested_stack_size < ethr_min_stack_size__) + stack_size = ETHR_KW2B(ethr_min_stack_size__); + else if (suggested_stack_size > ethr_max_stack_size__) + stack_size = ETHR_KW2B(ethr_max_stack_size__); + else + stack_size = ETHR_PAGE_ALIGN(ETHR_KW2B(suggested_stack_size)); + use_stack_size = stack_size; + } + + init_msg = alloc(sizeof(ethr_thr_wrap_data__), ETHREADWRAPDATASIG); + + /* Call prepare func if it exist */ + if (ethr_thr_prepare_func__) + init_msg->data.prep_func_res = ethr_thr_prepare_func__(); + else + init_msg->data.prep_func_res = NULL; + + /*erts_fprintf(stderr, "creating process %s / stack %d\n", opts->name, use_stack_size);*/ + +#if 0 + ramlog_printf("[0x%x] process '%s', coreNo = %u\n", + current_process(), opts->name, opts->coreNo); +#endif + tid->id = create_process(/*OS_PRI_PROC*/OS_BG_PROC, opts->name, thr_wrapper, + use_stack_size, /*opts->prio+5*/31, 0, + blockId(), NULL, 0, 0); + + if (ose_bind_process(tid->id, opts->coreNo)) { + printf("[0x%x] Binding pid 0x%x (%s) to core no %u.\n", + current_process(), tid->id, opts->name, opts->coreNo); + } + + /*FIXME!!! Normally this shouldn't be used in shared mode. Still there is + * a problem with stdin fd in fd_ processes which should be further + * investigated */ + efs_clone(tid->id); + + tid->tsd_key_index = 0; + tid->res = NULL; + + init_msg->data.tse = ethr_get_ts_event(); + init_msg->data.thr_func = func; + init_msg->data.arg = arg; + init_msg->data.tid = tid; + init_msg->data.name = opts->name; + + send(&init_msg, tid->id); + + start(tid->id); + init_msg = receive(sigsel); + + res = init_msg->data.result; + prep_func_res = init_msg->data.prep_func_res; + + free_buf(&init_msg); + /* Cleanup... */ + + if (ethr_thr_parent_func__) + ethr_thr_parent_func__(prep_func_res); + + return res; +} + +int +ethr_thr_join(ethr_tid tid, void **res) +{ + SIGSELECT sigsel[] = {1,OS_ATTACH_SIG}; +#if ETHR_XCHK + if (ethr_not_inited__) { + ETHR_ASSERT(0); + return EACCES; + } +#endif + + if (tid.id == ETHR_INVALID_TID_ID) + return EINVAL; + + attach(NULL,tid.id); + receive(sigsel); + + if (res) + *res = tid.res; + + return 0; +} + +int +ethr_thr_detach(ethr_tid tid) +{ +#if ETHR_XCHK + if (ethr_not_inited__) { + ETHR_ASSERT(0); + return EACCES; + } +#endif + return 0; +} + +void +ethr_thr_exit(void *res) +{ + ethr_tid *tid; +#if ETHR_XCHK + if (ethr_not_inited__) { + ETHR_ASSERT(0); + return; + } +#endif + tid = ETHR_GET_OWN_TID__; + if (!tid) { + ETHR_ASSERT(0); + kill_proc(current_process()); + } + thr_exit_cleanup(tid, res); + /* Harakiri possible? */ + kill_proc(current_process()); +} + +ethr_tid +ethr_self(void) +{ + ethr_tid *tid; +#if ETHR_XCHK + if (ethr_not_inited__) { + ethr_tid dummy_tid = {ETHR_INVALID_TID_ID, 0, NULL}; + ETHR_ASSERT(0); + return dummy_tid; + } +#endif + tid = ETHR_GET_OWN_TID__; + if (!tid) { + ethr_tid dummy_tid = {ETHR_INVALID_TID_ID, 0, NULL}; + return dummy_tid; + } + return *tid; +} + +int +ethr_equal_tids(ethr_tid tid1, ethr_tid tid2) +{ + return tid1.id == tid2.id && tid1.id != ETHR_INVALID_TID_ID; +} + + +/* + * Thread specific events + */ + +ethr_ts_event * +ethr_get_ts_event(void) +{ + return ethr_get_ts_event__(); +} + +void +ethr_leave_ts_event(ethr_ts_event *tsep) +{ + ethr_leave_ts_event__(tsep); +} + +/* + * Thread specific data + */ + +int +ethr_tsd_key_create(ethr_tsd_key *keyp) +{ + ethr_tid *tid = ETHR_GET_OWN_TID__; + ethr_tsd_key key; + +#if ETHR_XCHK + if (ethr_not_inited__) { + ETHR_ASSERT(0); + return EACCES; + } + if (!keyp) { + ETHR_ASSERT(0); + return EINVAL; + } +#endif + if (tid->tsd_key_index > 999) + return EAGAIN; + + /* ethread_tsd_key_YYYYYYYY_XXX\0 */ + key = malloc(sizeof(ethr_tsd_key)+sizeof(char)*(strlen("ethread_tsd_key_0xYYYYYYYY_XXX")+1)); + /* What do we do it tds_key_index happens to wrap? Slot search? */ + sprintf(key->key,"ethread_tsd_key_0x%x_%d",tid->id,tid->tsd_key_index++); + key->id = current_process(); + + *keyp = key; + + return 0; +} + +int +ethr_tsd_key_delete(ethr_tsd_key key) +{ +#if ETHR_XCHK + if (ethr_not_inited__) { + ETHR_ASSERT(0); + return EACCES; + } +#endif + free(key); + return 0; +} + +int +ethr_tsd_set(ethr_tsd_key key, void *value) +{ +#if ETHR_XCHK + if (ethr_not_inited__) { + ETHR_ASSERT(0); + return EACCES; + } +#endif + return set_envp(current_process(), key->key, (OSADDRESS)value)?0:1; +} + +void * +ethr_tsd_get(ethr_tsd_key key) +{ +#if ETHR_XCHK + if (ethr_not_inited__) { + ETHR_ASSERT(0); + return NULL; + } +#endif + return (void*)get_envp(current_process(),key->key); +} + +/* + * Signal functions + */ + +#if ETHR_HAVE_ETHR_SIG_FUNCS + +int ethr_sigmask(int how, const sigset_t *set, sigset_t *oset) +{ +#if ETHR_XCHK + if (ethr_not_inited__) { + ETHR_ASSERT(0); + return EACCES; + } + if (!set && !oset) { + ETHR_ASSERT(0); + return EINVAL; + } +#endif + return pthread_sigmask(how, set, oset); +} + +int ethr_sigwait(const sigset_t *set, int *sig) +{ +#if ETHR_XCHK + if (ethr_not_inited__) { + ETHR_ASSERT(0); + return EACCES; + } + if (!set || !sig) { + ETHR_ASSERT(0); + return EINVAL; + } +#endif + if (sigwait(set, sig) < 0) + return errno; + return 0; +} + +#endif /* #if ETHR_HAVE_ETHR_SIG_FUNCS */ + +ETHR_IMPL_NORETURN__ +ethr_abort__(void) +{ + abort(); +} + +const char *procName(void); +const char * +procName(void) { + char **procName_p = (char**)ose_get_ppdata(nameKey); + if (procName_p) { + return *procName_p; + } + + return NULL; +} diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 6b132dc38b..879db5437c 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 4b9e901c6d..578913b633 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -262,6 +262,8 @@ check_file_result(_, _, {error,enoent}) -> error; check_file_result(_, _, {error,enotdir}) -> error; +check_file_result(_, _, {error,einval}) -> + error; check_file_result(Func, Target, {error,Reason}) -> case (catch atom_to_list(Reason)) of {'EXIT',_} -> % exit trapped @@ -1392,6 +1394,8 @@ absname_vr([Drive, $\: | NameRest], _) -> %% Assumes normalized name pathtype(Name) when is_list(Name) -> case erlang:system_info(os_type) of + {ose, _} -> + unix_pathtype(Name); {unix, _} -> unix_pathtype(Name); {win32, _} -> -- cgit v1.2.3 From 8084f0f9797daf83bf6da698e5e1e5784c11601b Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Sun, 23 Feb 2014 16:48:17 +0100 Subject: ose: Add unsetenv and update to use get_env Before get_envp was used and this caused the environment variables to not be accessible in the correct way by use debug tools. --- erts/emulator/sys/ose/sys.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index c475aafe38..4fd740b02a 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -1530,24 +1530,24 @@ int erts_sys_putenv(char *key, char *value) { int res; - char *env; - Uint need = strlen(key) + strlen(value) + 2; -#ifdef HAVE_COPYING_PUTENV - env = erts_alloc(ERTS_ALC_T_TMP, need); -#else - 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); + res = set_env(get_bid(current_process()), key, + value); erts_smp_rwmtx_rwunlock(&environ_rwmtx); -#ifdef HAVE_COPYING_PUTENV - erts_free(ERTS_ALC_T_TMP, env); -#endif + return res; +} + + +int +erts_sys_unsetenv(char *key) +{ + int res; + + erts_smp_rwmtx_rwlock(&environ_rwmtx); + res = set_env(get_bid(current_process()),key,NULL); + erts_smp_rwmtx_rwunlock(&environ_rwmtx); + return res; } @@ -1555,7 +1555,7 @@ int erts_sys_getenv__(char *key, char *value, size_t *size) { int res; - char *orig_value = getenv(key); + char *orig_value = get_env(get_bid(current_process()), key); if (!orig_value) res = -1; else { @@ -1569,6 +1569,7 @@ erts_sys_getenv__(char *key, char *value, size_t *size) sys_memcpy((void *) value, (void *) orig_value, len+1); res = 0; } + free_buf((union SIGNAL **)&orig_value); } return res; } -- cgit v1.2.3 From 33f5280f9e130ebbf19c63470e38c1dc71fd0003 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Sun, 23 Feb 2014 16:50:00 +0100 Subject: ose: Add signal debugging macros To enable just uncomment and all signals going in or out will be printed to stdout. --- erts/emulator/sys/ose/erl_ose_sys.h | 122 ++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) (limited to 'erts') diff --git a/erts/emulator/sys/ose/erl_ose_sys.h b/erts/emulator/sys/ose/erl_ose_sys.h index a3308e9ba4..94f1e58883 100644 --- a/erts/emulator/sys/ose/erl_ose_sys.h +++ b/erts/emulator/sys/ose/erl_ose_sys.h @@ -235,4 +235,126 @@ extern int getpagesize(void); # define UINT_MAX 4294967295U #endif +/* +static void erts_ose_sys_send(union SIGNAL **signal,PROCESS dst, + char* file,int line) { + SIGSELECT **ziggy = (SIGSELECT**)signal; + printf("%s:%d 0x%x Send signal 0x%x(0x%x) to 0x%x\r\n", + file,line,current_process(),ziggy[0][0],*ziggy,dst); + send(signal,dst); +} +#define send(signal,dst) erts_ose_sys_send(signal,dst,__FILE__,__LINE__) + +static void erts_ose_sys_send_w_sender(union SIGNAL **signal, + PROCESS sender,PROCESS dst, + char* file,int line) { + SIGSELECT **ziggy = (SIGSELECT**)signal; + printf("%s:%d 0x%x Send signal 0x%x(0x%x) to 0x%x as 0x%x\r\n", + file,line,current_process(),ziggy[0][0],*ziggy,dst,sender); + send_w_sender(signal,sender,dst); +} +#define send_w_sender(signal,sender,dst) \ + erts_ose_sys_send_w_sender(signal,sender,dst,__FILE__,__LINE__) + + +static union SIGNAL *erts_ose_sys_receive(SIGSELECT *sigsel, + char *file, + int line) { + SIGSELECT *sig; + int i; + + printf("%s:%d 0x%x receive({%d,",file,line,current_process(),sigsel[0]); + for (i = 1; i < sigsel[0]; i++) + printf("0x%x, ",sigsel[i]); + if (sigsel[0] != 0) + printf("0x%x",sigsel[i]); + printf("})\n"); + sig = (SIGSELECT*)receive(sigsel); + printf("%s:%d 0x%x got 0x%x from 0x%x\n",file,line,current_process(), + *sig,sender((union SIGNAL**)(&sig))); + return (union SIGNAL*)sig; +} +#define receive(SIGSEL) erts_ose_sys_receive(SIGSEL,__FILE__,__LINE__) + +static union SIGNAL *erts_ose_sys_receive_w_tmo(OSTIME tmo,SIGSELECT *sigsel, + char *file,int line) { + SIGSELECT *sig; + int i; + if (tmo == 0) { + sig = (SIGSELECT*)receive_w_tmo(tmo,sigsel); + if (sig != NULL) { + printf("%s:%d 0x%x receive_w_tmo(0,{%d,",file,line,current_process(), + sigsel[0]); + for (i = 1; i < sigsel[0]; i++) + printf("0x%x, ",sigsel[i]); + if (sigsel[0] != 0) + printf("0x%x",sigsel[i]); + printf("})\n"); + printf("%s:%d 0x%x got 0x%x from 0x%x\n",file,line,current_process(), + *sig,sender((union SIGNAL**)(&sig))); + } + } else { + printf("%s:%d 0x%x receive_w_tmo(%u,{%d,",file,line,current_process(),tmo, + sigsel[0]); + for (i = 1; i < sigsel[0]; i++) + printf("0x%x, ",sigsel[i]); + if (sigsel[0] != 0) + printf("0x%x",sigsel[i]); + printf("})\n"); + sig = (SIGSELECT*)receive_w_tmo(tmo,sigsel); + printf("%s:%d 0x%x got ",file,line,current_process()); + if (sig == NULL) + printf("TIMEOUT\n"); + else + printf("0x%x from 0x%x\n",*sig,sender((union SIGNAL**)(&sig))); + } + + return (union SIGNAL*)sig; +} + +#define receive_w_tmo(tmo,sigsel) erts_ose_sys_receive_w_tmo(tmo,sigsel, \ + __FILE__,__LINE__) + +static union SIGNAL *erts_ose_sys_receive_fsem(OSTIME tmo,SIGSELECT *sigsel, + OSFSEMVAL fsem, + char *file,int line) { + SIGSELECT *sig; + int i; + if (tmo == 0) { + sig = (SIGSELECT*)receive_fsem(tmo,sigsel,fsem); + if (sig != NULL && sig != OS_RCV_FSEM) { + printf("%s:%d 0x%x receive_fsem(0,{%d,",file,line,current_process(), + sigsel[0]); + for (i = 1; i < sigsel[0]; i++) + printf("0x%x, ",sigsel[i]); + if (sigsel[0] != 0) + printf("0x%x",sigsel[i]); + printf("},%d)\n",fsem); + printf("%s:%d 0x%x got 0x%x from 0x%x\n",file,line,current_process(), + *sig,sender((union SIGNAL**)(&sig))); + } + } else { + printf("%s:%d 0x%x receive_fsem(%u,{%d,",file,line,current_process(),tmo, + sigsel[0]); + for (i = 1; i < sigsel[0]; i++) + printf("0x%x, ",sigsel[i]); + if (sigsel[0] != 0) + printf("0x%x",sigsel[i]); + printf("},%d)\n",fsem); + sig = (SIGSELECT*)receive_fsem(tmo,sigsel,fsem); + printf("%s:%d 0x%x got ",file,line,current_process()); + if (sig == NULL) + printf("TIMEOUT\n"); + else if (sig == OS_RCV_FSEM) + printf("FSEM\n"); + else + printf("0x%x from 0x%x\n",*sig,sender((union SIGNAL**)(&sig))); + } + + return (union SIGNAL*)sig; +} + +#define receive_fsem(tmo,sigsel,fsem) \ + erts_ose_sys_receive_fsem(tmo,sigsel,fsem,__FILE__,__LINE__) +*/ #endif /* _ERL_OSE_SYS_H */ -- cgit v1.2.3 From 48201abc7961b44be9e5cf0feb74d18a399d6099 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 1 Aug 2013 15:50:46 +0200 Subject: erts: Make source file info available in lc --- erts/emulator/beam/beam_bp.c | 3 +- erts/emulator/beam/beam_emu.c | 3 +- erts/emulator/beam/erl_alloc_util.c | 4 +- erts/emulator/beam/erl_lock_check.c | 74 ++++++++++++--------- erts/emulator/beam/erl_lock_check.h | 26 ++++++-- erts/emulator/beam/erl_lock_count.h | 6 ++ erts/emulator/beam/erl_port_task.h | 8 ++- erts/emulator/beam/erl_process.h | 10 ++- erts/emulator/beam/erl_process_lock.c | 62 +++++++++-------- erts/emulator/beam/erl_process_lock.h | 25 ++++--- erts/emulator/beam/erl_smp.h | 78 ++++++++++++++-------- erts/emulator/beam/erl_threads.h | 122 +++++++++++++++++++++++++++------- erts/emulator/beam/io.c | 13 ++-- erts/emulator/hipe/hipe_bif2.c | 3 +- erts/emulator/hipe/hipe_mode_switch.c | 3 +- 15 files changed, 293 insertions(+), 147 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 68907a771a..49a34ab4ad 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -46,7 +46,8 @@ #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) + if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN,\ + __FILE__, __LINE__) # define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \ if ((P)) erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN) #else diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index ceff43b24f..c82d2fb2be 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -71,7 +71,8 @@ do { \ ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); \ } while (0) # define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) \ - if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN) + if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN,\ + __FILE__, __LINE__) # define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \ if ((P)) erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN) # else diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index c6cea0185f..45f0cc4312 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -5561,11 +5561,11 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) erts_mtx_init_x_opt(&allctr->mutex, "alcu_allocator", make_small(allctr->alloc_no), - ERTS_LCNT_LT_ALLOC); + ERTS_LCNT_LT_ALLOC,1); #else erts_mtx_init_x(&allctr->mutex, "alcu_allocator", - make_small(allctr->alloc_no)); + make_small(allctr->alloc_no),1); #endif /*ERTS_ENABLE_LOCK_COUNT*/ #ifdef DEBUG diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index a8ff94ac89..706d284a00 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -241,6 +241,8 @@ struct erts_lc_locked_lock_t_ { erts_lc_locked_lock_t *prev; UWord extra; Sint16 id; + char *file; + unsigned int line; Uint16 flags; }; @@ -430,47 +432,51 @@ make_my_locked_locks(void) } static ERTS_INLINE erts_lc_locked_lock_t * -new_locked_lock(erts_lc_lock_t *lck, Uint16 op_flags) +new_locked_lock(erts_lc_lock_t *lck, Uint16 op_flags, + char *file, unsigned int line) { erts_lc_locked_lock_t *l_lck = (erts_lc_locked_lock_t *) lc_alloc(); l_lck->next = NULL; l_lck->prev = NULL; l_lck->id = lck->id; l_lck->extra = lck->extra; + l_lck->file = file; + l_lck->line = line; l_lck->flags = lck->flags | op_flags; return l_lck; } static void -print_lock2(char *prefix, Sint16 id, Wterm extra, Uint16 flags, char *suffix) +raw_print_lock(char *prefix, Sint16 id, Wterm extra, Uint16 flags, + char* file, unsigned int line, char *suffix) { char *lname = (0 <= id && id < ERTS_LOCK_ORDER_SIZE ? erts_lock_order[id].name : "unknown"); + erts_fprintf(stderr,"%s'%s:",prefix,lname); + if (is_not_immed(extra)) - erts_fprintf(stderr, - "%s'%s:%p%s'%s%s", - prefix, - lname, - _unchecked_boxed_val(extra), - lock_type(flags), - rw_op_str(flags), - suffix); + erts_fprintf(stderr,"%p",_unchecked_boxed_val(extra)); else - erts_fprintf(stderr, - "%s'%s:%T%s'%s%s", - prefix, - lname, - extra, - lock_type(flags), - rw_op_str(flags), - suffix); + erts_fprintf(stderr,"%T",extra); + erts_fprintf(stderr,"%s",lock_type(flags)); + + if (file) + erts_fprintf(stderr,"(%s:%d)",file,line); + + erts_fprintf(stderr,"'%s%s",rw_op_str(flags),suffix); +} + +static void +print_lock2(char *prefix, Sint16 id, Wterm extra, Uint16 flags, char *suffix) +{ + raw_print_lock(prefix, id, extra, flags, NULL, 0, suffix); } static void print_lock(char *prefix, erts_lc_lock_t *lck, char *suffix) { - print_lock2(prefix, lck->id, lck->extra, lck->flags, suffix); + raw_print_lock(prefix, lck->id, lck->extra, lck->flags, NULL, 0, suffix); } static void @@ -486,7 +492,8 @@ print_curr_locks(erts_lc_locked_locks_t *l_lcks) "Currently these locks are locked by the %s thread:\n", l_lcks->thread_name); for (l_lck = l_lcks->locked.first; l_lck; l_lck = l_lck->next) - print_lock2(" ", l_lck->id, l_lck->extra, l_lck->flags, "\n"); + raw_print_lock(" ", l_lck->id, l_lck->extra, l_lck->flags, + l_lck->file, l_lck->line, "\n"); } } @@ -1002,7 +1009,8 @@ erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, Uint16 op_flags) #endif } -void erts_lc_trylock_flg(int locked, erts_lc_lock_t *lck, Uint16 op_flags) +void erts_lc_trylock_flg_x(int locked, erts_lc_lock_t *lck, Uint16 op_flags, + char *file, unsigned int line) { erts_lc_locked_locks_t *l_lcks; erts_lc_locked_lock_t *l_lck; @@ -1014,7 +1022,7 @@ void erts_lc_trylock_flg(int locked, erts_lc_lock_t *lck, Uint16 op_flags) return; l_lcks = make_my_locked_locks(); - l_lck = locked ? new_locked_lock(lck, op_flags) : NULL; + l_lck = locked ? new_locked_lock(lck, op_flags, file, line) : NULL; if (!l_lcks->locked.last) { ASSERT(!l_lcks->locked.first); @@ -1055,13 +1063,14 @@ void erts_lc_trylock_flg(int locked, erts_lc_lock_t *lck, Uint16 op_flags) } -void erts_lc_require_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags) +void erts_lc_require_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags, + char *file, unsigned int line) { erts_lc_locked_locks_t *l_lcks = make_my_locked_locks(); erts_lc_locked_lock_t *l_lck = l_lcks->locked.first; if (!find_lock(&l_lck, lck)) required_not_locked(l_lcks, lck); - l_lck = new_locked_lock(lck, op_flags); + l_lck = new_locked_lock(lck, op_flags, file, line); if (!l_lcks->required.last) { ASSERT(!l_lcks->required.first); l_lck->next = l_lck->prev = NULL; @@ -1129,7 +1138,8 @@ void erts_lc_unrequire_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags) lc_free((void *) l_lck); } -void erts_lc_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags) +void erts_lc_lock_flg_x(erts_lc_lock_t *lck, Uint16 op_flags, + char *file, unsigned int line) { erts_lc_locked_locks_t *l_lcks; erts_lc_locked_lock_t *l_lck; @@ -1141,7 +1151,7 @@ void erts_lc_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags) return; l_lcks = make_my_locked_locks(); - l_lck = new_locked_lock(lck, op_flags); + l_lck = new_locked_lock(lck, op_flags, file, line); if (!l_lcks->locked.last) { ASSERT(!l_lcks->locked.first); @@ -1232,15 +1242,15 @@ erts_lc_trylock_force_busy(erts_lc_lock_t *lck) } void -erts_lc_trylock(int locked, erts_lc_lock_t *lck) +erts_lc_trylock_x(int locked, erts_lc_lock_t *lck, char *file, unsigned int line) { - erts_lc_trylock_flg(locked, lck, 0); + erts_lc_trylock_flg_x(locked, lck, 0, file, line); } void -erts_lc_lock(erts_lc_lock_t *lck) +erts_lc_lock_x(erts_lc_lock_t *lck, char *file, unsigned int line) { - erts_lc_lock_flg(lck, 0); + erts_lc_lock_flg_x(lck, 0, file, line); } void @@ -1254,9 +1264,9 @@ void erts_lc_might_unlock(erts_lc_lock_t *lck) erts_lc_might_unlock_flg(lck, 0); } -void erts_lc_require_lock(erts_lc_lock_t *lck) +void erts_lc_require_lock(erts_lc_lock_t *lck, char *file, unsigned int line) { - erts_lc_require_lock_flg(lck, 0); + erts_lc_require_lock_flg(lck, 0, file, line); } void erts_lc_unrequire_lock(erts_lc_lock_t *lck) diff --git a/erts/emulator/beam/erl_lock_check.h b/erts/emulator/beam/erl_lock_check.h index 068340abe7..3f7f417e61 100644 --- a/erts/emulator/beam/erl_lock_check.h +++ b/erts/emulator/beam/erl_lock_check.h @@ -35,6 +35,11 @@ #ifdef ERTS_ENABLE_LOCK_CHECK +#ifndef ERTS_ENABLE_LOCK_POSITION +/* Enable in order for _x variants of mtx functions to be used. */ +#define ERTS_ENABLE_LOCK_POSITION 1 +#endif + typedef struct { int inited; Sint16 id; @@ -79,13 +84,16 @@ void erts_lc_have_locks(int *resv, erts_lc_lock_t *lcks, int len); void erts_lc_have_lock_ids(int *resv, int *ids, int len); void erts_lc_check_no_locked_of_type(Uint16 flags); int erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, Uint16 op_flags); -void erts_lc_trylock_flg(int locked, erts_lc_lock_t *lck, Uint16 op_flags); -void erts_lc_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags); +void erts_lc_trylock_flg_x(int locked, erts_lc_lock_t *lck, Uint16 op_flags, + char *file, unsigned int line); +void erts_lc_lock_flg_x(erts_lc_lock_t *lck, Uint16 op_flags, + char *file, unsigned int line); void erts_lc_unlock_flg(erts_lc_lock_t *lck, Uint16 op_flags); void erts_lc_might_unlock_flg(erts_lc_lock_t *lck, Uint16 op_flags); int erts_lc_trylock_force_busy(erts_lc_lock_t *lck); -void erts_lc_trylock(int locked, erts_lc_lock_t *lck); -void erts_lc_lock(erts_lc_lock_t *lck); +void erts_lc_trylock_x(int locked, erts_lc_lock_t *lck, + char* file, unsigned int line); +void erts_lc_lock_x(erts_lc_lock_t *lck, char* file, unsigned int line); void erts_lc_unlock(erts_lc_lock_t *lck); void erts_lc_might_unlock(erts_lc_lock_t *lck); void erts_lc_init_lock(erts_lc_lock_t *lck, char *name, Uint16 flags); @@ -96,10 +104,11 @@ int erts_lc_assert_failed(char *file, int line, char *assertion); void erts_lc_set_thread_name(char *thread_name); void erts_lc_pll(void); -void erts_lc_require_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags); +void erts_lc_require_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags, + char *file, unsigned int line); void erts_lc_unrequire_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags); -void erts_lc_require_lock(erts_lc_lock_t *lck); +void erts_lc_require_lock(erts_lc_lock_t *lck, char *file, unsigned int line); void erts_lc_unrequire_lock(erts_lc_lock_t *lck); int erts_lc_is_emu_thr(void); @@ -116,4 +125,9 @@ int erts_lc_is_emu_thr(void); #define ERTS_LC_ASSERT(A) ((void) 1) #endif /* #ifdef ERTS_ENABLE_LOCK_CHECK */ +#define erts_lc_lock(lck) erts_lc_lock_x(lck,__FILE__,__LINE__) +#define erts_lc_trylock(res,lck) erts_lc_trylock_x(res,lck,__FILE__,__LINE__) +#define erts_lc_lock_flg(lck) erts_lc_lock_flg_x(lck,__FILE__,__LINE__) +#define erts_lc_trylock_flg(res,lck) erts_lc_trylock_flg_x(res,lck,__FILE__,__LINE__) + #endif /* #ifndef ERTS_LOCK_CHECK_H__ */ diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h index a4fc91b510..75f7cd028b 100644 --- a/erts/emulator/beam/erl_lock_count.h +++ b/erts/emulator/beam/erl_lock_count.h @@ -61,8 +61,14 @@ #define ERTS_LOCK_COUNT_H__ #ifdef ERTS_ENABLE_LOCK_COUNT +#ifndef ERTS_ENABLE_LOCK_POSITION +/* Enable in order for _x variants of mtx functions to be used. */ +#define ERTS_ENABLE_LOCK_POSITION 1 +#endif + #include "ethread.h" + #define ERTS_LCNT_MAX_LOCK_LOCATIONS (10) #define ERTS_LCNT_LT_SPINLOCK (((Uint16) 1) << 0) diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h index 123253a057..1d30465ec9 100644 --- a/erts/emulator/beam/erl_port_task.h +++ b/erts/emulator/beam/erl_port_task.h @@ -185,11 +185,13 @@ erts_port_task_init_sched(ErtsPortTaskSched *ptsp, Eterm instr_id) ptsp->taskq.in.last = NULL; erts_smp_atomic32_init_nob(&ptsp->flags, 0); #ifdef ERTS_SMP + erts_mtx_init_x(&ptsp->mtx, lock_str, instr_id, #ifdef ERTS_ENABLE_LOCK_COUNT - if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK)) - lock_str = NULL; + (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) +#else + 1 #endif - erts_mtx_init_x(&ptsp->mtx, lock_str, instr_id); + ); #endif } diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index dcb9251d0d..cf20ed5acf 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -2009,12 +2009,6 @@ erts_get_runq_current(ErtsSchedulerData *esdp) #endif } -#ifdef ERTS_ENABLE_LOCK_COUNT - -#define erts_smp_runq_lock(rq) erts_smp_mtx_lock_x(&(rq)->mtx, __FILE__, __LINE__) - -#else - ERTS_GLB_INLINE void erts_smp_runq_lock(ErtsRunQueue *rq) { @@ -2023,6 +2017,10 @@ erts_smp_runq_lock(ErtsRunQueue *rq) #endif } +#ifdef ERTS_ENABLE_LOCK_COUNT + +#define erts_smp_runq_lock(rq) erts_smp_mtx_lock_x(&(rq)->mtx, __FILE__, __LINE__) + #endif ERTS_GLB_INLINE int diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index bb17593297..82cc68222d 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -117,7 +117,7 @@ erts_init_proc_lock(int cpus) for (i = 0; i < ERTS_NO_OF_PIX_LOCKS; i++) { #ifdef ERTS_ENABLE_LOCK_COUNT erts_mtx_init_x(&erts_pix_locks[i].u.mtx, - "pix_lock", make_small(i)); + "pix_lock", make_small(i), 1); #else erts_mtx_init(&erts_pix_locks[i].u.mtx, "pix_lock"); #endif @@ -901,7 +901,7 @@ erts_pid2proc_opt(Process *c_p, 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); + erts_proc_lc_trylock(proc, need_locks, !busy, __FILE__,__LINE__); #endif #ifdef ERTS_PROC_LOCK_DEBUG if (!busy) @@ -1013,25 +1013,33 @@ erts_proc_lock_init(Process *p) 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); + erts_proc_lc_trylock(p, ERTS_PROC_LOCKS_ALL, 1,__FILE__,__LINE__); #endif #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL - erts_mtx_init_x(&p->lock.main, "proc_main", p->common.id); + +#ifdef ERTS_ENABLE_LOCK_COUNT + int do_lock_count = 1; +#else + int do_lock_count = 0; +#endif + + erts_mtx_init_x(&p->lock.main, "proc_main", p->common.id, do_lock_count); ethr_mutex_lock(&p->lock.main.mtx); #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_trylock(1, &p->lock.main.lc); #endif - erts_mtx_init_x(&p->lock.link, "proc_link", p->common.id); + erts_mtx_init_x(&p->lock.link, "proc_link", p->common.id, do_lock_count); ethr_mutex_lock(&p->lock.link.mtx); #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_trylock(1, &p->lock.link.lc); #endif - erts_mtx_init_x(&p->lock.msgq, "proc_msgq", p->common.id); + erts_mtx_init_x(&p->lock.msgq, "proc_msgq", p->common.id, do_lock_count); 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->common.id); + erts_mtx_init_x(&p->lock.status, "proc_status", p->common.id, + do_lock_count); ethr_mutex_lock(&p->lock.status.mtx); #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_trylock(1, &p->lock.status.lc); @@ -1210,50 +1218,51 @@ void erts_lcnt_enable_proc_lock_count(int enable) #if ERTS_PROC_LOCK_OWN_IMPL void -erts_proc_lc_lock(Process *p, ErtsProcLocks locks) +erts_proc_lc_lock(Process *p, ErtsProcLocks locks, char *file, unsigned int line) { erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, p->common.id, ERTS_LC_FLG_LT_PROCLOCK); if (locks & ERTS_PROC_LOCK_MAIN) { lck.id = lc_id.proc_lock_main; - erts_lc_lock(&lck); + erts_lc_lock_x(&lck,file,line); } if (locks & ERTS_PROC_LOCK_LINK) { lck.id = lc_id.proc_lock_link; - erts_lc_lock(&lck); + erts_lc_lock_x(&lck,file,line); } if (locks & ERTS_PROC_LOCK_MSGQ) { lck.id = lc_id.proc_lock_msgq; - erts_lc_lock(&lck); + erts_lc_lock_x(&lck,file,line); } if (locks & ERTS_PROC_LOCK_STATUS) { lck.id = lc_id.proc_lock_status; - erts_lc_lock(&lck); + erts_lc_lock_x(&lck,file,line); } } void -erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked) +erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked, + char* file, unsigned int line) { erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, p->common.id, ERTS_LC_FLG_LT_PROCLOCK); if (locks & ERTS_PROC_LOCK_MAIN) { lck.id = lc_id.proc_lock_main; - erts_lc_trylock(locked, &lck); + erts_lc_trylock_x(locked, &lck, file, line); } if (locks & ERTS_PROC_LOCK_LINK) { lck.id = lc_id.proc_lock_link; - erts_lc_trylock(locked, &lck); + erts_lc_trylock_x(locked, &lck, file, line); } if (locks & ERTS_PROC_LOCK_MSGQ) { lck.id = lc_id.proc_lock_msgq; - erts_lc_trylock(locked, &lck); + erts_lc_trylock_x(locked, &lck, file, line); } if (locks & ERTS_PROC_LOCK_STATUS) { lck.id = lc_id.proc_lock_status; - erts_lc_trylock(locked, &lck); + erts_lc_trylock_x(locked, &lck, file, line); } } @@ -1319,7 +1328,8 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks) } void -erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks) +erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file, + unsigned int line) { #if ERTS_PROC_LOCK_OWN_IMPL erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, @@ -1327,29 +1337,29 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks) ERTS_LC_FLG_LT_PROCLOCK); if (locks & ERTS_PROC_LOCK_MAIN) { lck.id = lc_id.proc_lock_main; - erts_lc_require_lock(&lck); + erts_lc_require_lock(&lck, file, line); } if (locks & ERTS_PROC_LOCK_LINK) { lck.id = lc_id.proc_lock_link; - erts_lc_require_lock(&lck); + erts_lc_require_lock(&lck, file, line); } if (locks & ERTS_PROC_LOCK_MSGQ) { lck.id = lc_id.proc_lock_msgq; - erts_lc_require_lock(&lck); + erts_lc_require_lock(&lck, file, line); } if (locks & ERTS_PROC_LOCK_STATUS) { lck.id = lc_id.proc_lock_status; - erts_lc_require_lock(&lck); + erts_lc_require_lock(&lck, file, line); } #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL if (locks & ERTS_PROC_LOCK_MAIN) - erts_lc_require_lock(&p->lock.main.lc); + erts_lc_require_lock(&p->lock.main.lc, file, line); if (locks & ERTS_PROC_LOCK_LINK) - erts_lc_require_lock(&p->lock.link.lc); + erts_lc_require_lock(&p->lock.link.lc, file, line); if (locks & ERTS_PROC_LOCK_MSGQ) - erts_lc_require_lock(&p->lock.msgq.lc); + erts_lc_require_lock(&p->lock.msgq.lc, file, line); if (locks & ERTS_PROC_LOCK_STATUS) - erts_lc_require_lock(&p->lock.status.lc); + erts_lc_require_lock(&p->lock.status.lc, file, line); #endif } diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index 9dd503f3cb..052d992d3f 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -215,7 +215,7 @@ typedef struct erts_proc_lock_t_ { /* Lock counter implemetation */ -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION #define erts_smp_proc_lock__(P,I,L) erts_smp_proc_lock_x__(P,I,L,__FILE__,__LINE__) #define erts_smp_proc_lock(P,L) erts_smp_proc_lock_x(P,L,__FILE__,__LINE__) #endif @@ -243,8 +243,10 @@ void erts_lcnt_enable_proc_lock_count(int enable); erts_proc_lc_chk_no_proc_locks(__FILE__, __LINE__) #define ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(P) \ erts_proc_lc_chk_only_proc_main((P)) -void erts_proc_lc_lock(Process *p, ErtsProcLocks locks); -void erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked); +void erts_proc_lc_lock(Process *p, ErtsProcLocks locks, + char *file, unsigned int line); +void erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked, + char *file, unsigned int line); void erts_proc_lc_unlock(Process *p, ErtsProcLocks locks); void erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks); void erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks); @@ -253,7 +255,8 @@ void erts_proc_lc_chk_only_proc_main(Process *p); void erts_proc_lc_chk_no_proc_locks(char *file, int line); ErtsProcLocks erts_proc_lc_my_proc_locks(Process *p); int erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks); -void erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks); +void erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, + char* file, unsigned int line); void erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks); #else #define ERTS_SMP_CHK_NO_PROC_LOCKS @@ -372,7 +375,7 @@ ERTS_GLB_INLINE int erts_lc_pix_lock_is_locked(erts_pix_lock_t *); ERTS_GLB_INLINE ErtsProcLocks erts_smp_proc_raw_trylock__(Process *p, ErtsProcLocks locks); -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION ERTS_GLB_INLINE void erts_smp_proc_lock_x__(Process *, erts_pix_lock_t *, ErtsProcLocks, @@ -482,7 +485,7 @@ busy_main: } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_smp_proc_lock_x__(Process *p, erts_pix_lock_t *pix_lck, ErtsProcLocks locks, @@ -528,7 +531,7 @@ erts_smp_proc_lock__(Process *p, erts_lcnt_proc_lock_post_x(&(p->lock), locks, file, line); #endif #ifdef ERTS_ENABLE_LOCK_CHECK - erts_proc_lc_lock(p, locks); + erts_proc_lc_lock(p, locks, file, line); #endif #ifdef ERTS_PROC_LOCK_DEBUG @@ -695,7 +698,7 @@ erts_smp_proc_trylock__(Process *p, #endif #ifdef ERTS_ENABLE_LOCK_CHECK - erts_proc_lc_trylock(p, locks, res == 0); + erts_proc_lc_trylock(p, locks, res == 0, __FILE__, __LINE__); #endif #if ERTS_PROC_LOCK_ATOMIC_IMPL @@ -741,7 +744,7 @@ erts_proc_lock_op_debug(Process *p, ErtsProcLocks locks, int locked) #endif /* ERTS_SMP */ -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION ERTS_GLB_INLINE void erts_smp_proc_lock_x(Process *, ErtsProcLocks, char *file, unsigned int line); #else ERTS_GLB_INLINE void erts_smp_proc_lock(Process *, ErtsProcLocks); @@ -756,13 +759,13 @@ ERTS_GLB_INLINE void erts_smp_proc_add_refc(Process *, Sint32); #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_smp_proc_lock_x(Process *p, ErtsProcLocks locks, char *file, unsigned int line) #else erts_smp_proc_lock(Process *p, ErtsProcLocks locks) #endif { -#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT) +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_POSITION) erts_smp_proc_lock_x__(p, #if ERTS_PROC_LOCK_ATOMIC_IMPL NULL, diff --git a/erts/emulator/beam/erl_smp.h b/erts/emulator/beam/erl_smp.h index ecb5525022..971d68be75 100644 --- a/erts/emulator/beam/erl_smp.h +++ b/erts/emulator/beam/erl_smp.h @@ -26,10 +26,13 @@ #define ERL_SMP_H #include "erl_threads.h" -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION #define erts_smp_mtx_lock(L) erts_smp_mtx_lock_x(L, __FILE__, __LINE__) +#define erts_smp_mtx_trylock(L) erts_smp_mtx_trylock_x(L, __FILE__, __LINE__) #define erts_smp_spin_lock(L) erts_smp_spin_lock_x(L, __FILE__, __LINE__) +#define erts_smp_rwmtx_tryrlock(L) erts_smp_rwmtx_tryrlock_x(L, __FILE__, __LINE__) #define erts_smp_rwmtx_rlock(L) erts_smp_rwmtx_rlock_x(L, __FILE__, __LINE__) +#define erts_smp_rwmtx_tryrwlock(L) erts_smp_rwmtx_tryrwlock_x(L, __FILE__, __LINE__) #define erts_smp_rwmtx_rwlock(L) erts_smp_rwmtx_rwlock_x(L, __FILE__, __LINE__) #define erts_smp_read_lock(L) erts_smp_read_lock_x(L, __FILE__, __LINE__) #define erts_smp_write_lock(L) erts_smp_write_lock_x(L, __FILE__, __LINE__) @@ -131,10 +134,11 @@ ERTS_GLB_INLINE void erts_smp_mtx_init_locked_x(erts_smp_mtx_t *mtx, ERTS_GLB_INLINE void erts_smp_mtx_init(erts_smp_mtx_t *mtx, char *name); ERTS_GLB_INLINE void erts_smp_mtx_init_locked(erts_smp_mtx_t *mtx, char *name); ERTS_GLB_INLINE void erts_smp_mtx_destroy(erts_smp_mtx_t *mtx); -ERTS_GLB_INLINE int erts_smp_mtx_trylock(erts_smp_mtx_t *mtx); -#ifdef ERTS_ENABLE_LOCK_COUNT -ERTS_GLB_INLINE void erts_smp_mtx_lock_x(erts_smp_mtx_t *mtx, char *file, int line); +#ifdef ERTS_ENABLE_LOCK_POSITION +ERTS_GLB_INLINE int erts_smp_mtx_trylock_x(erts_smp_mtx_t *mtx, char *file, unsigned int line); +ERTS_GLB_INLINE void erts_smp_mtx_lock_x(erts_smp_mtx_t *mtx, char *file, unsigned int line); #else +ERTS_GLB_INLINE int erts_smp_mtx_trylock(erts_smp_mtx_t *mtx); ERTS_GLB_INLINE void erts_smp_mtx_lock(erts_smp_mtx_t *mtx); #endif ERTS_GLB_INLINE void erts_smp_mtx_unlock(erts_smp_mtx_t *mtx); @@ -159,16 +163,18 @@ ERTS_GLB_INLINE void erts_smp_rwmtx_init_opt(erts_smp_rwmtx_t *rwmtx, ERTS_GLB_INLINE void erts_smp_rwmtx_init(erts_smp_rwmtx_t *rwmtx, char *name); ERTS_GLB_INLINE void erts_smp_rwmtx_destroy(erts_smp_rwmtx_t *rwmtx); -ERTS_GLB_INLINE int erts_smp_rwmtx_tryrlock(erts_smp_rwmtx_t *rwmtx); -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION +ERTS_GLB_INLINE int erts_smp_rwmtx_tryrlock_x(erts_smp_rwmtx_t *rwmtx, char *file, unsigned int line); ERTS_GLB_INLINE void erts_smp_rwmtx_rlock_x(erts_smp_rwmtx_t *rwmtx, char *file, unsigned int line); ERTS_GLB_INLINE void erts_smp_rwmtx_rwlock_x(erts_smp_rwmtx_t *rwmtx, char *file, unsigned int line); +ERTS_GLB_INLINE int erts_smp_rwmtx_tryrwlock_x(erts_smp_rwmtx_t *rwmtx, char *file, unsigned int line); #else +ERTS_GLB_INLINE int erts_smp_rwmtx_tryrlock(erts_smp_rwmtx_t *rwmtx); ERTS_GLB_INLINE void erts_smp_rwmtx_rlock(erts_smp_rwmtx_t *rwmtx); ERTS_GLB_INLINE void erts_smp_rwmtx_rwlock(erts_smp_rwmtx_t *rwmtx); +ERTS_GLB_INLINE int erts_smp_rwmtx_tryrwlock(erts_smp_rwmtx_t *rwmtx); #endif ERTS_GLB_INLINE void erts_smp_rwmtx_runlock(erts_smp_rwmtx_t *rwmtx); -ERTS_GLB_INLINE int erts_smp_rwmtx_tryrwlock(erts_smp_rwmtx_t *rwmtx); ERTS_GLB_INLINE void erts_smp_rwmtx_rwunlock(erts_smp_rwmtx_t *rwmtx); ERTS_GLB_INLINE int erts_smp_lc_rwmtx_is_rlocked(erts_smp_rwmtx_t *mtx); ERTS_GLB_INLINE int erts_smp_lc_rwmtx_is_rwlocked(erts_smp_rwmtx_t *mtx); @@ -179,7 +185,7 @@ ERTS_GLB_INLINE void erts_smp_spinlock_init(erts_smp_spinlock_t *lock, char *name); ERTS_GLB_INLINE void erts_smp_spinlock_destroy(erts_smp_spinlock_t *lock); ERTS_GLB_INLINE void erts_smp_spin_unlock(erts_smp_spinlock_t *lock); -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION ERTS_GLB_INLINE void erts_smp_spin_lock_x(erts_smp_spinlock_t *lock, char *file, unsigned int line); #else ERTS_GLB_INLINE void erts_smp_spin_lock(erts_smp_spinlock_t *lock); @@ -192,7 +198,7 @@ ERTS_GLB_INLINE void erts_smp_rwlock_init(erts_smp_rwlock_t *lock, char *name); ERTS_GLB_INLINE void erts_smp_rwlock_destroy(erts_smp_rwlock_t *lock); ERTS_GLB_INLINE void erts_smp_read_unlock(erts_smp_rwlock_t *lock); -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION ERTS_GLB_INLINE void erts_smp_read_lock_x(erts_smp_rwlock_t *lock, char *file, unsigned int line); ERTS_GLB_INLINE void erts_smp_write_lock_x(erts_smp_rwlock_t *lock, char *file, unsigned int line); #else @@ -835,7 +841,7 @@ ERTS_GLB_INLINE void erts_smp_mtx_init_x(erts_smp_mtx_t *mtx, char *name, Eterm extra) { #ifdef ERTS_SMP - erts_mtx_init_x(mtx, name, extra); + erts_mtx_init_x(mtx, name, extra, 1); #endif } @@ -843,7 +849,7 @@ ERTS_GLB_INLINE void erts_smp_mtx_init_locked_x(erts_smp_mtx_t *mtx, char *name, Eterm extra) { #ifdef ERTS_SMP - erts_mtx_init_locked_x(mtx, name, extra); + erts_mtx_init_locked_x(mtx, name, extra, 1); #endif } @@ -872,9 +878,15 @@ erts_smp_mtx_destroy(erts_smp_mtx_t *mtx) } ERTS_GLB_INLINE int +#ifdef ERTS_ENABLE_LOCK_POSITION +erts_smp_mtx_trylock_x(erts_smp_mtx_t *mtx, char *file, unsigned int line) +#else erts_smp_mtx_trylock(erts_smp_mtx_t *mtx) +#endif { -#ifdef ERTS_SMP +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_POSITION) + return erts_mtx_trylock_x(mtx,file,line); +#elif defined(ERTS_SMP) return erts_mtx_trylock(mtx); #else return 0; @@ -884,13 +896,13 @@ erts_smp_mtx_trylock(erts_smp_mtx_t *mtx) ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT -erts_smp_mtx_lock_x(erts_smp_mtx_t *mtx, char *file, int line) +#ifdef ERTS_ENABLE_LOCK_POSITION +erts_smp_mtx_lock_x(erts_smp_mtx_t *mtx, char *file, unsigned int line) #else erts_smp_mtx_lock(erts_smp_mtx_t *mtx) #endif { -#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT) +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_POSITION) erts_mtx_lock_x(mtx, file, line); #elif defined(ERTS_SMP) erts_mtx_lock(mtx); @@ -1020,9 +1032,15 @@ erts_smp_rwmtx_destroy(erts_smp_rwmtx_t *rwmtx) } ERTS_GLB_INLINE int +#ifdef ERTS_ENABLE_LOCK_POSITION +erts_smp_rwmtx_tryrlock_x(erts_smp_rwmtx_t *rwmtx, char *file, unsigned int line) +#else erts_smp_rwmtx_tryrlock(erts_smp_rwmtx_t *rwmtx) +#endif { -#ifdef ERTS_SMP +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_POSITION) + return erts_rwmtx_tryrlock_x(rwmtx, file, line); +#elif defined(ERTS_SMP) return erts_rwmtx_tryrlock(rwmtx); #else return 0; @@ -1030,13 +1048,13 @@ erts_smp_rwmtx_tryrlock(erts_smp_rwmtx_t *rwmtx) } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_smp_rwmtx_rlock_x(erts_smp_rwmtx_t *rwmtx, char *file, unsigned int line) #else erts_smp_rwmtx_rlock(erts_smp_rwmtx_t *rwmtx) #endif { -#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT) +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_POSITION) erts_rwmtx_rlock_x(rwmtx, file, line); #elif defined(ERTS_SMP) erts_rwmtx_rlock(rwmtx); @@ -1053,9 +1071,15 @@ erts_smp_rwmtx_runlock(erts_smp_rwmtx_t *rwmtx) ERTS_GLB_INLINE int +#ifdef ERTS_ENABLE_LOCK_POSITION +erts_smp_rwmtx_tryrwlock_x(erts_smp_rwmtx_t *rwmtx, char *file, unsigned int line) +#else erts_smp_rwmtx_tryrwlock(erts_smp_rwmtx_t *rwmtx) +#endif { -#ifdef ERTS_SMP +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_POSITION) + return erts_rwmtx_tryrwlock_x(rwmtx, file, line); +#elif defined(ERTS_SMP) return erts_rwmtx_tryrwlock(rwmtx); #else return 0; @@ -1063,13 +1087,13 @@ erts_smp_rwmtx_tryrwlock(erts_smp_rwmtx_t *rwmtx) } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_smp_rwmtx_rwlock_x(erts_smp_rwmtx_t *rwmtx, char *file, unsigned int line) #else erts_smp_rwmtx_rwlock(erts_smp_rwmtx_t *rwmtx) #endif { -#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT) +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_POSITION) erts_rwmtx_rwlock_x(rwmtx, file, line); #elif defined(ERTS_SMP) erts_rwmtx_rwlock(rwmtx); @@ -1171,13 +1195,13 @@ erts_smp_spin_unlock(erts_smp_spinlock_t *lock) } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_smp_spin_lock_x(erts_smp_spinlock_t *lock, char *file, unsigned int line) #else erts_smp_spin_lock(erts_smp_spinlock_t *lock) #endif { -#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT) +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_POSITION) erts_spin_lock_x(lock, file, line); #elif defined(ERTS_SMP) erts_spin_lock(lock); @@ -1237,13 +1261,13 @@ erts_smp_read_unlock(erts_smp_rwlock_t *lock) } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_smp_read_lock_x(erts_smp_rwlock_t *lock, char *file, unsigned int line) #else erts_smp_read_lock(erts_smp_rwlock_t *lock) #endif { -#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP) +#if defined(ERTS_ENABLE_LOCK_POSITION) && defined(ERTS_SMP) erts_read_lock_x(lock, file, line); #elif defined(ERTS_SMP) erts_read_lock(lock); @@ -1263,13 +1287,13 @@ erts_smp_write_unlock(erts_smp_rwlock_t *lock) } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_smp_write_lock_x(erts_smp_rwlock_t *lock, char *file, unsigned int line) #else erts_smp_write_lock(erts_smp_rwlock_t *lock) #endif { -#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT) +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_POSITION) erts_write_lock_x(lock, file, line); #elif defined(ERTS_SMP) erts_write_lock(lock); diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index 759c8f4c33..b2f68462a6 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -281,10 +281,13 @@ #define ERTS_THR_READ_MEMORY_BARRIER ETHR_READ_MEMORY_BARRIER #define ERTS_THR_DATA_DEPENDENCY_READ_MEMORY_BARRIER ETHR_READ_DEPEND_MEMORY_BARRIER -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION #define erts_mtx_lock(L) erts_mtx_lock_x(L, __FILE__, __LINE__) +#define erts_mtx_trylock(L) erts_mtx_trylock_x(L, __FILE__, __LINE__) #define erts_spin_lock(L) erts_spin_lock_x(L, __FILE__, __LINE__) +#define erts_rwmtx_tryrlock(L) erts_rwmtx_tryrlock_x(L, __FILE__, __LINE__) #define erts_rwmtx_rlock(L) erts_rwmtx_rlock_x(L, __FILE__, __LINE__) +#define erts_rwmtx_tryrwlock(L) erts_rwmtx_tryrwlock_x(L, __FILE__, __LINE__) #define erts_rwmtx_rwlock(L) erts_rwmtx_rwlock_x(L, __FILE__, __LINE__) #define erts_read_lock(L) erts_read_lock_x(L, __FILE__, __LINE__) #define erts_write_lock(L) erts_write_lock_x(L, __FILE__, __LINE__) @@ -461,18 +464,24 @@ ERTS_GLB_INLINE void erts_thr_exit(void *res); ERTS_GLB_INLINE void erts_thr_install_exit_handler(void (*exit_handler)(void)); ERTS_GLB_INLINE erts_tid_t erts_thr_self(void); ERTS_GLB_INLINE int erts_equal_tids(erts_tid_t x, erts_tid_t y); -ERTS_GLB_INLINE void erts_mtx_init_x(erts_mtx_t *mtx, char *name, Eterm extra); -ERTS_GLB_INLINE void erts_mtx_init_x_opt(erts_mtx_t *mtx, char *name, Eterm extra, Uint16 opt); +ERTS_GLB_INLINE void erts_mtx_init_x(erts_mtx_t *mtx, char *name, Eterm extra, + int enable_lcnt); +ERTS_GLB_INLINE void erts_mtx_init_x_opt(erts_mtx_t *mtx, char *name, Eterm extra, + Uint16 opt, int enable_lcnt); ERTS_GLB_INLINE void erts_mtx_init_locked_x(erts_mtx_t *mtx, char *name, - Eterm extra); + Eterm extra, + int enable_lcnt); ERTS_GLB_INLINE void erts_mtx_init(erts_mtx_t *mtx, char *name); ERTS_GLB_INLINE void erts_mtx_init_locked(erts_mtx_t *mtx, char *name); ERTS_GLB_INLINE void erts_mtx_destroy(erts_mtx_t *mtx); -ERTS_GLB_INLINE int erts_mtx_trylock(erts_mtx_t *mtx); -#ifdef ERTS_ENABLE_LOCK_COUNT -ERTS_GLB_INLINE void erts_mtx_lock_x(erts_mtx_t *mtx, char *file, unsigned int line); +#ifdef ERTS_ENABLE_LOCK_POSITION +ERTS_GLB_INLINE int erts_mtx_trylock_x(erts_mtx_t *mtx, char *file, + unsigned int line); +ERTS_GLB_INLINE void erts_mtx_lock_x(erts_mtx_t *mtx, char *file, + unsigned int line); #else +ERTS_GLB_INLINE int erts_mtx_trylock(erts_mtx_t *mtx); ERTS_GLB_INLINE void erts_mtx_lock(erts_mtx_t *mtx); #endif ERTS_GLB_INLINE void erts_mtx_unlock(erts_mtx_t *mtx); @@ -496,16 +505,18 @@ ERTS_GLB_INLINE void erts_rwmtx_init_opt(erts_rwmtx_t *rwmtx, ERTS_GLB_INLINE void erts_rwmtx_init(erts_rwmtx_t *rwmtx, char *name); ERTS_GLB_INLINE void erts_rwmtx_destroy(erts_rwmtx_t *rwmtx); -ERTS_GLB_INLINE int erts_rwmtx_tryrlock(erts_rwmtx_t *rwmtx); -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION +ERTS_GLB_INLINE int erts_rwmtx_tryrlock_x(erts_rwmtx_t *rwmtx, char *file, unsigned int line); ERTS_GLB_INLINE void erts_rwmtx_rlock_x(erts_rwmtx_t *rwmtx, char *file, unsigned int line); ERTS_GLB_INLINE void erts_rwmtx_rwlock_x(erts_rwmtx_t *rwmtx, char *file, unsigned int line); +ERTS_GLB_INLINE int erts_rwmtx_tryrwlock_x(erts_rwmtx_t *rwmtx, char *file, unsigned int line); #else +ERTS_GLB_INLINE int erts_rwmtx_tryrlock(erts_rwmtx_t *rwmtx); ERTS_GLB_INLINE void erts_rwmtx_rlock(erts_rwmtx_t *rwmtx); ERTS_GLB_INLINE void erts_rwmtx_rwlock(erts_rwmtx_t *rwmtx); +ERTS_GLB_INLINE int erts_rwmtx_tryrwlock(erts_rwmtx_t *rwmtx); #endif ERTS_GLB_INLINE void erts_rwmtx_runlock(erts_rwmtx_t *rwmtx); -ERTS_GLB_INLINE int erts_rwmtx_tryrwlock(erts_rwmtx_t *rwmtx); ERTS_GLB_INLINE void erts_rwmtx_rwunlock(erts_rwmtx_t *rwmtx); ERTS_GLB_INLINE int erts_lc_rwmtx_is_rlocked(erts_rwmtx_t *mtx); ERTS_GLB_INLINE int erts_lc_rwmtx_is_rwlocked(erts_rwmtx_t *mtx); @@ -571,7 +582,7 @@ ERTS_GLB_INLINE void erts_spinlock_init(erts_spinlock_t *lock, char *name); ERTS_GLB_INLINE void erts_spinlock_destroy(erts_spinlock_t *lock); ERTS_GLB_INLINE void erts_spin_unlock(erts_spinlock_t *lock); -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION ERTS_GLB_INLINE void erts_spin_lock_x(erts_spinlock_t *lock, char *file, unsigned int line); #else ERTS_GLB_INLINE void erts_spin_lock(erts_spinlock_t *lock); @@ -584,7 +595,7 @@ ERTS_GLB_INLINE void erts_rwlock_init(erts_rwlock_t *lock, char *name); ERTS_GLB_INLINE void erts_rwlock_destroy(erts_rwlock_t *lock); ERTS_GLB_INLINE void erts_read_unlock(erts_rwlock_t *lock); -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION ERTS_GLB_INLINE void erts_read_lock_x(erts_rwlock_t *lock, char *file, unsigned int line); ERTS_GLB_INLINE void erts_write_lock_x(erts_rwlock_t *lock, char *file, unsigned int line); #else @@ -1549,7 +1560,7 @@ erts_equal_tids(erts_tid_t x, erts_tid_t y) } ERTS_GLB_INLINE void -erts_mtx_init_x(erts_mtx_t *mtx, char *name, Eterm extra) +erts_mtx_init_x(erts_mtx_t *mtx, char *name, Eterm extra, int enable_lcnt) { #ifdef USE_THREADS int res = ethr_mutex_init(&mtx->mtx); @@ -1559,13 +1570,17 @@ erts_mtx_init_x(erts_mtx_t *mtx, char *name, Eterm extra) erts_lc_init_lock_x(&mtx->lc, name, ERTS_LC_FLG_LT_MUTEX, extra); #endif #ifdef ERTS_ENABLE_LOCK_COUNT - erts_lcnt_init_lock_x(&mtx->lcnt, name, ERTS_LCNT_LT_MUTEX, extra); + if (enable_lcnt) + erts_lcnt_init_lock_x(&mtx->lcnt, name, ERTS_LCNT_LT_MUTEX, extra); + else + erts_lcnt_init_lock_x(&mtx->lcnt, NULL, ERTS_LCNT_LT_MUTEX, extra); #endif #endif } ERTS_GLB_INLINE void -erts_mtx_init_x_opt(erts_mtx_t *mtx, char *name, Eterm extra, Uint16 opt) +erts_mtx_init_x_opt(erts_mtx_t *mtx, char *name, Eterm extra, Uint16 opt, + int enable_lcnt) { #ifdef USE_THREADS int res = ethr_mutex_init(&mtx->mtx); @@ -1575,14 +1590,17 @@ erts_mtx_init_x_opt(erts_mtx_t *mtx, char *name, Eterm extra, Uint16 opt) erts_lc_init_lock_x(&mtx->lc, name, ERTS_LC_FLG_LT_MUTEX, extra); #endif #ifdef ERTS_ENABLE_LOCK_COUNT - erts_lcnt_init_lock_x(&mtx->lcnt, name, ERTS_LCNT_LT_MUTEX | opt, extra); + if (enable_lcnt) + erts_lcnt_init_lock_x(&mtx->lcnt, name, ERTS_LCNT_LT_MUTEX | opt, extra); + else + erts_lcnt_init_lock_x(&mtx->lcnt, NULL, ERTS_LCNT_LT_MUTEX | opt, extra); #endif #endif } ERTS_GLB_INLINE void -erts_mtx_init_locked_x(erts_mtx_t *mtx, char *name, Eterm extra) +erts_mtx_init_locked_x(erts_mtx_t *mtx, char *name, Eterm extra, int enable_lcnt) { #ifdef USE_THREADS int res = ethr_mutex_init(&mtx->mtx); @@ -1592,7 +1610,10 @@ erts_mtx_init_locked_x(erts_mtx_t *mtx, char *name, Eterm extra) erts_lc_init_lock_x(&mtx->lc, name, ERTS_LC_FLG_LT_MUTEX, extra); #endif #ifdef ERTS_ENABLE_LOCK_COUNT - erts_lcnt_init_lock_x(&mtx->lcnt, name, ERTS_LCNT_LT_MUTEX, extra); + if (enable_lcnt) + erts_lcnt_init_lock_x(&mtx->lcnt, name, ERTS_LCNT_LT_MUTEX, extra); + else + erts_lcnt_init_lock_x(&mtx->lcnt, NULL, ERTS_LCNT_LT_MUTEX, extra); #endif ethr_mutex_lock(&mtx->mtx); #ifdef ERTS_ENABLE_LOCK_CHECK @@ -1670,7 +1691,11 @@ erts_mtx_destroy(erts_mtx_t *mtx) } ERTS_GLB_INLINE int +#ifdef ERTS_ENABLE_LOCK_POSITION +erts_mtx_trylock_x(erts_mtx_t *mtx, char *file, unsigned int line) +#else erts_mtx_trylock(erts_mtx_t *mtx) +#endif { #ifdef USE_THREADS int res; @@ -1684,8 +1709,12 @@ erts_mtx_trylock(erts_mtx_t *mtx) res = ethr_mutex_trylock(&mtx->mtx); #ifdef ERTS_ENABLE_LOCK_CHECK +#ifdef ERTS_ENABLE_LOCK_POSITION + erts_lc_trylock_x(res == 0, &mtx->lc,file,line); +#else erts_lc_trylock(res == 0, &mtx->lc); #endif +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_trylock(&mtx->lcnt, res); #endif @@ -1697,7 +1726,7 @@ erts_mtx_trylock(erts_mtx_t *mtx) } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_mtx_lock_x(erts_mtx_t *mtx, char *file, unsigned int line) #else erts_mtx_lock(erts_mtx_t *mtx) @@ -1705,8 +1734,12 @@ erts_mtx_lock(erts_mtx_t *mtx) { #ifdef USE_THREADS #ifdef ERTS_ENABLE_LOCK_CHECK +#ifdef ERTS_ENABLE_LOCK_POSITION + erts_lc_lock_x(&mtx->lc, file, line); +#else erts_lc_lock(&mtx->lc); #endif +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock(&mtx->lcnt); #endif @@ -1857,7 +1890,10 @@ erts_rwmtx_init_opt_x(erts_rwmtx_t *rwmtx, erts_lc_init_lock_x(&rwmtx->lc, name, ERTS_LC_FLG_LT_RWMUTEX, extra); #endif #ifdef ERTS_ENABLE_LOCK_COUNT - erts_lcnt_init_lock_x(&rwmtx->lcnt, name, ERTS_LCNT_LT_RWMUTEX, extra); + if (name && name[0] == '\0') + erts_lcnt_init_lock_x(&rwmtx->lcnt, NULL, ERTS_LCNT_LT_RWMUTEX, extra); + else + erts_lcnt_init_lock_x(&rwmtx->lcnt, name, ERTS_LCNT_LT_RWMUTEX, extra); #endif #endif } @@ -1921,7 +1957,11 @@ erts_rwmtx_destroy(erts_rwmtx_t *rwmtx) } ERTS_GLB_INLINE int +#ifdef ERTS_ENABLE_LOCK_POSITION +erts_rwmtx_tryrlock_x(erts_rwmtx_t *rwmtx, char *file, unsigned int line) +#else erts_rwmtx_tryrlock(erts_rwmtx_t *rwmtx) +#endif { #ifdef USE_THREADS int res; @@ -1935,8 +1975,12 @@ erts_rwmtx_tryrlock(erts_rwmtx_t *rwmtx) res = ethr_rwmutex_tryrlock(&rwmtx->rwmtx); #ifdef ERTS_ENABLE_LOCK_CHECK +#ifdef ERTS_ENABLE_LOCK_POSITION + erts_lc_trylock_flg_x(res == 0, &rwmtx->lc, ERTS_LC_FLG_LO_READ,file,line); +#else erts_lc_trylock_flg(res == 0, &rwmtx->lc, ERTS_LC_FLG_LO_READ); #endif +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_trylock_opt(&rwmtx->lcnt, res, ERTS_LCNT_LO_READ); #endif @@ -1948,7 +1992,7 @@ erts_rwmtx_tryrlock(erts_rwmtx_t *rwmtx) } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_rwmtx_rlock_x(erts_rwmtx_t *rwmtx, char *file, unsigned int line) #else erts_rwmtx_rlock(erts_rwmtx_t *rwmtx) @@ -1956,8 +2000,12 @@ erts_rwmtx_rlock(erts_rwmtx_t *rwmtx) { #ifdef USE_THREADS #ifdef ERTS_ENABLE_LOCK_CHECK +#ifdef ERTS_ENABLE_LOCK_POSITION + erts_lc_lock_flg_x(&rwmtx->lc, ERTS_LC_FLG_LO_READ,file,line); +#else erts_lc_lock_flg(&rwmtx->lc, ERTS_LC_FLG_LO_READ); #endif +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock_opt(&rwmtx->lcnt, ERTS_LCNT_LO_READ); #endif @@ -1984,7 +2032,11 @@ erts_rwmtx_runlock(erts_rwmtx_t *rwmtx) ERTS_GLB_INLINE int +#ifdef ERTS_ENABLE_LOCK_POSITION +erts_rwmtx_tryrwlock_x(erts_rwmtx_t *rwmtx, char *file, unsigned int line) +#else erts_rwmtx_tryrwlock(erts_rwmtx_t *rwmtx) +#endif { #ifdef USE_THREADS int res; @@ -1998,8 +2050,12 @@ erts_rwmtx_tryrwlock(erts_rwmtx_t *rwmtx) res = ethr_rwmutex_tryrwlock(&rwmtx->rwmtx); #ifdef ERTS_ENABLE_LOCK_CHECK +#ifdef ERTS_ENABLE_LOCK_POSITION + erts_lc_trylock_flg_x(res == 0, &rwmtx->lc, ERTS_LC_FLG_LO_READ_WRITE,file,line); +#else erts_lc_trylock_flg(res == 0, &rwmtx->lc, ERTS_LC_FLG_LO_READ_WRITE); #endif +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_trylock_opt(&rwmtx->lcnt, res, ERTS_LCNT_LO_READ_WRITE); #endif @@ -2011,7 +2067,7 @@ erts_rwmtx_tryrwlock(erts_rwmtx_t *rwmtx) } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_rwmtx_rwlock_x(erts_rwmtx_t *rwmtx, char *file, unsigned int line) #else erts_rwmtx_rwlock(erts_rwmtx_t *rwmtx) @@ -2019,8 +2075,12 @@ erts_rwmtx_rwlock(erts_rwmtx_t *rwmtx) { #ifdef USE_THREADS #ifdef ERTS_ENABLE_LOCK_CHECK +#ifdef ERTS_ENABLE_LOCK_POSITION + erts_lc_lock_flg_x(&rwmtx->lc, ERTS_LC_FLG_LO_READ_WRITE,file,line); +#else erts_lc_lock_flg(&rwmtx->lc, ERTS_LC_FLG_LO_READ_WRITE); #endif +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock_opt(&rwmtx->lcnt, ERTS_LCNT_LO_READ_WRITE); #endif @@ -2426,7 +2486,7 @@ erts_spin_unlock(erts_spinlock_t *lock) } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_spin_lock_x(erts_spinlock_t *lock, char *file, unsigned int line) #else erts_spin_lock(erts_spinlock_t *lock) @@ -2434,8 +2494,12 @@ erts_spin_lock(erts_spinlock_t *lock) { #ifdef USE_THREADS #ifdef ERTS_ENABLE_LOCK_CHECK +#ifdef ERTS_ENABLE_LOCK_POSITION + erts_lc_lock_x(&lock->lc,file,line); +#else erts_lc_lock(&lock->lc); #endif +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock(&lock->lcnt); #endif @@ -2545,7 +2609,7 @@ erts_read_unlock(erts_rwlock_t *lock) } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_read_lock_x(erts_rwlock_t *lock, char *file, unsigned int line) #else erts_read_lock(erts_rwlock_t *lock) @@ -2553,8 +2617,12 @@ erts_read_lock(erts_rwlock_t *lock) { #ifdef USE_THREADS #ifdef ERTS_ENABLE_LOCK_CHECK +#ifdef ERTS_ENABLE_LOCK_POSITION + erts_lc_lock_flg_x(&lock->lc, ERTS_LC_FLG_LO_READ,file,line); +#else erts_lc_lock_flg(&lock->lc, ERTS_LC_FLG_LO_READ); #endif +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock_opt(&lock->lcnt, ERTS_LCNT_LO_READ); #endif @@ -2584,7 +2652,7 @@ erts_write_unlock(erts_rwlock_t *lock) } ERTS_GLB_INLINE void -#ifdef ERTS_ENABLE_LOCK_COUNT +#ifdef ERTS_ENABLE_LOCK_POSITION erts_write_lock_x(erts_rwlock_t *lock, char *file, unsigned int line) #else erts_write_lock(erts_rwlock_t *lock) @@ -2592,8 +2660,12 @@ erts_write_lock(erts_rwlock_t *lock) { #ifdef USE_THREADS #ifdef ERTS_ENABLE_LOCK_CHECK +#ifdef ERTS_ENABLE_LOCK_POSITION + erts_lc_lock_flg_x(&lock->lc, ERTS_LC_FLG_LO_READ_WRITE,file,line); +#else erts_lc_lock_flg(&lock->lc, ERTS_LC_FLG_LO_READ_WRITE); #endif +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock_opt(&lock->lcnt, ERTS_LCNT_LO_READ_WRITE); #endif diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index f4d905552d..c7c6e5a5d7 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -247,11 +247,13 @@ static ERTS_INLINE void port_init_instr(Port *prt ASSERT(prt->drv_ptr && prt->lock); if (!prt->drv_ptr->lock) { char *lock_str = "port_lock"; + erts_mtx_init_locked_x(prt->lock, lock_str, id, #ifdef ERTS_ENABLE_LOCK_COUNT - if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK)) - lock_str = NULL; + (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) +#else + 0 #endif - erts_mtx_init_locked_x(prt->lock, lock_str, id); + ); } #endif erts_port_task_init_sched(&prt->sched, id); @@ -7310,10 +7312,11 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle) erts_atom_put((byte *) drv->name, sys_strlen(drv->name), ERTS_ATOM_ENC_LATIN1, - 1) + 1), #else - NIL + NIL, #endif + 1 ); } #endif diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c index c3687681cf..7637049bc3 100644 --- a/erts/emulator/hipe/hipe_bif2.c +++ b/erts/emulator/hipe/hipe_bif2.c @@ -157,7 +157,8 @@ BIF_RETTYPE hipe_bifs_modeswitch_debug_off_0(BIF_ALIST_0) BIF_RETTYPE hipe_debug_bif_wrapper(BIF_ALIST_1); # define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) \ - if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN) + if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN,\ + __FILE__, __LINE__) # define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \ if ((P)) erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN) diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index adc8793469..8ebe5da670 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -37,7 +37,8 @@ #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) + if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN, \ + __FILE__, __LINE__) # define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \ if ((P)) erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN) #else -- cgit v1.2.3 From c743ed359f16f791dd15b58b86af7f77db4799aa Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Sun, 23 Feb 2014 17:00:41 +0100 Subject: ose: Force atleast one async thread for ose This is needed because a file has to be opened and operated on in the same process at all times. Using async threads guarantee this. --- erts/emulator/beam/erl_init.c | 2 +- erts/emulator/sys/ose/erl_ose_sys.h | 5 +++++ erts/emulator/sys/unix/erl_unix_sys.h | 5 +++++ erts/emulator/sys/win32/erl_win_sys.h | 4 ++++ 4 files changed, 15 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index c17256f466..e2227e8de4 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -780,7 +780,7 @@ early_init(int *argc, char **argv) /* case 'A': { /* set number of threads in thread pool */ char *arg = get_arg(argv[i]+2, argv[i+1], &i); - if (((erts_async_max_threads = atoi(arg)) < 0) || + if (((erts_async_max_threads = atoi(arg)) < ERTS_MIN_NO_OF_ASYNC_THREADS) || (erts_async_max_threads > ERTS_MAX_NO_OF_ASYNC_THREADS)) { erts_fprintf(stderr, "bad number of async threads %s\n", diff --git a/erts/emulator/sys/ose/erl_ose_sys.h b/erts/emulator/sys/ose/erl_ose_sys.h index 94f1e58883..5243886bac 100644 --- a/erts/emulator/sys/ose/erl_ose_sys.h +++ b/erts/emulator/sys/ose/erl_ose_sys.h @@ -69,6 +69,11 @@ struct erts_sys_fd_type { }; +/* + * Min number of async threads + */ +#define ERTS_MIN_NO_OF_ASYNC_THREADS 1 + /* * Our own type of "FD's" */ diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 2c47aa06c2..176fc049a7 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -127,6 +127,11 @@ # endif #endif +/* + * Min number of async threads + */ +#define ERTS_MIN_NO_OF_ASYNC_THREADS 0 + /* File descriptors are numbers anc consecutively allocated on Unix */ #define ERTS_SYS_CONTINOUS_FD_NUMBERS diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 0deb097b1a..8015e8f378 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -103,6 +103,10 @@ #define CreateAutoEvent(state) CreateEvent(NULL, FALSE, state, NULL) #define CreateManualEvent(state) CreateEvent(NULL, TRUE, state, NULL) +/* + * Min number of async threads + */ +#define ERTS_MIN_NO_OF_ASYNC_THREADS 0 /* * Our own type of "FD's" -- cgit v1.2.3 From eed38c1bb1a12015377d30e9ceff9525e050850e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 3 Sep 2013 11:48:00 +0200 Subject: ose: Convert EFILE_SEEK to unistd seek for gzio This is needed because OSE does not have the same integers as unix/win32 for SEEK_ST and friends. --- erts/emulator/drivers/common/gzio.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'erts') diff --git a/erts/emulator/drivers/common/gzio.c b/erts/emulator/drivers/common/gzio.c index 653f3954b1..9a9e297fca 100644 --- a/erts/emulator/drivers/common/gzio.c +++ b/erts/emulator/drivers/common/gzio.c @@ -20,6 +20,7 @@ #endif #include #include "erl_driver.h" +#include "erl_efile.h" #include "sys.h" #ifdef __WIN32__ @@ -597,6 +598,15 @@ erts_gzseek(ErtsGzFile file, int offset, int whence) int pos; gz_stream* s = (gz_stream *) file; + switch (whence) { + case EFILE_SEEK_SET: whence = SEEK_SET; break; + case EFILE_SEEK_CUR: whence = SEEK_CUR; break; + case EFILE_SEEK_END: whence = SEEK_END; break; + default: + errno = EINVAL; + return -1; + } + if (s == NULL) { errno = EINVAL; return -1; -- cgit v1.2.3 From e6c7086e6befb3eb9e8ad4fb7de62f1ee810508c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 25 Sep 2013 11:53:59 +0200 Subject: ose: Debug wait__ does receive_fsem instead of wait_fsem This is done in order to catch rogue signals --- erts/include/internal/ose/ethr_event.h | 13 +++++++++++-- erts/lib_src/ose/ethr_event.c | 26 +++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/include/internal/ose/ethr_event.h b/erts/include/internal/ose/ethr_event.h index 86811a87db..000a600813 100644 --- a/erts/include/internal/ose/ethr_event.h +++ b/erts/include/internal/ose/ethr_event.h @@ -78,8 +78,17 @@ ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) { ethr_sint32_t val = ethr_atomic32_xchg_mb(&e->state, ETHR_EVENT_ON__); if (val == ETHR_EVENT_OFF_WAITER__) { - ETHR_ASSERT(get_fsem(e->proc) == -1); - signal_fsem(e->proc); +#ifdef DEBUG + OSFSEMVAL fsem_val = get_fsem(e->proc); + + /* There is a race in this assert. + This is because the state is set before the wait call in wait__. + We hope that a delay of 10 ms is enough */ + if (fsem_val == 0) + delay(10); + ETHR_ASSERT(get_fsem(e->proc) == -1); +#endif + signal_fsem(e->proc); } } diff --git a/erts/lib_src/ose/ethr_event.c b/erts/lib_src/ose/ethr_event.c index 1cb3c223ab..5905fe22a9 100644 --- a/erts/lib_src/ose/ethr_event.c +++ b/erts/lib_src/ose/ethr_event.c @@ -127,6 +127,12 @@ wait__(ethr_event *e, int spincount) #else /* --- OSE implementation of events ---------------------------- */ +#ifdef DEBUG +union SIGNAL { + SIGSELECT signo; +}; +#endif + int ethr_event_init(ethr_event *e) { @@ -180,8 +186,26 @@ wait__(ethr_event *e, int spincount) ETHR_ASSERT(val == ETHR_EVENT_OFF__); } - +#if defined(DEBUG) + while (1) { + /* In debug we also receive any signals to make sure that we do + not get any! redir tables should send all to scheduler_1 */ + SIGSELECT sigsel[] = {0}; + union SIGNAL *sig = receive_fsem(OSE_NO_TIMEOUT,sigsel,1); + //ETHR_ASSERT(sig == OS_RCV_FSEM); + if (sig != OS_RCV_FSEM) { + int i; + printf("0x%x: Got signal in wait: %u ",current_process(),sig->signo); + for (i = 0; i < (sigsize(&sig) / sizeof(SIGSELECT)) && i < 5; i++) { + printf("%x ",sig[i+1]); + } + printf("\n"); + } else + break; + } +#else wait_fsem(1); +#endif ETHR_ASSERT(get_fsem(current_process()) == 0); } } -- cgit v1.2.3 From 3a4bdad8b488a6e7a4a18a95573059d5a21589c8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 30 Sep 2013 15:06:57 +0200 Subject: ose: Extract signal numbers to common file --- erts/emulator/sys/ose/erl_ose_sys.h | 1 + erts/emulator/sys/ose/erl_poll.c | 4 +--- erts/emulator/sys/ose/erts.sig | 12 +++++++++++ erts/emulator/sys/ose/sys.c | 41 +++++++++++++++++-------------------- 4 files changed, 33 insertions(+), 25 deletions(-) create mode 100644 erts/emulator/sys/ose/erts.sig (limited to 'erts') diff --git a/erts/emulator/sys/ose/erl_ose_sys.h b/erts/emulator/sys/ose/erl_ose_sys.h index 5243886bac..8c72afa9a5 100644 --- a/erts/emulator/sys/ose/erl_ose_sys.h +++ b/erts/emulator/sys/ose/erl_ose_sys.h @@ -27,6 +27,7 @@ #include "ose.h" #undef NIL #include "ramlog.h" +#include "erts.sig" #include "fcntl.h" #include "math.h" diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c index 878bd362e4..0bea6865ca 100644 --- a/erts/emulator/sys/ose/erl_poll.c +++ b/erts/emulator/sys/ose/erl_poll.c @@ -68,8 +68,6 @@ # define SEL_REALLOC realloc_wrap # define SEL_FREE erts_free -#define ERTS_POLL_INVALID_SIGNO 12345 - #ifdef ERTS_SMP #define ERTS_POLLSET_LOCK(PS) \ @@ -308,7 +306,7 @@ static int update_sigsel(ErtsPollSet ps) { */ ps->sigs = SEL_ALLOC(ERTS_ALC_T_POLLSET,sizeof(SIGSELECT)*(2)); ps->sigs[0] = 1; - ps->sigs[1] = ERTS_POLL_INVALID_SIGNO; + ps->sigs[1] = ERTS_SIGNAL_INVALID; return 0; } diff --git a/erts/emulator/sys/ose/erts.sig b/erts/emulator/sys/ose/erts.sig new file mode 100644 index 0000000000..760d0896c0 --- /dev/null +++ b/erts/emulator/sys/ose/erts.sig @@ -0,0 +1,12 @@ +#ifndef ERTS_OSE_SIGNALS +#define ERTS_OSE_SIGNALS + +#ifndef ERTS_OSE_SIGNAL_BASE +#define ERTS_OSE_SIGNAL_BASE 1000 +#endif + +#define ERTS_SIGNAL_INVALID ERTS_OSE_SIGNAL_BASE +#define ERTS_SIGNAL_FD_DRV_CONFIG ERTS_OSE_SIGNAL_BASE+1 +#define ERTS_SIGNAL_FD_DRV_ASYNC ERTS_OSE_SIGNAL_BASE+2 + +#endif diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index 4fd740b02a..a8eb0b93b4 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -583,12 +583,6 @@ void fini_getenv_state(GETENV_STATE *state) /* I. Common stuff */ -/* - * Decreasing the size of it below 16384 is not allowed. - */ -#define SYSDRIVERASYNCSIG 1000 -#define SYSDRIVERCONFSIG 1001 - typedef struct SysDriverAsyncSignal_ { SIGSELECT sig_no; int type; @@ -611,6 +605,9 @@ union SIGNAL { /* II. The spawn/fd drivers */ +/* + * Decreasing the size of it below 16384 is not allowed. + */ #define ERTS_SYS_READ_BUF_SZ (64*1024) /* Driver interfaces */ @@ -627,7 +624,7 @@ static void output(ErlDrvData, char*, ErlDrvSizeT); static void outputv(ErlDrvData, ErlIOVec*); static void stop_select(ErlDrvEvent, void*); static int resolve_signal(OseSignal* sig, int *mode) { - return sig->sig_no == SYSDRIVERASYNCSIG ? sig->sys_async.type : -1; + return sig->sig_no == ERTS_SIGNAL_FD_DRV_ASYNC ? sig->sys_async.type : -1; } OS_PROCESS(fd_writer_process); @@ -712,9 +709,9 @@ static int set_driver_data(ErlDrvPort port_num, report_exit->ofd = read_write & DO_WRITE ? ofd : -1; if (read_write & DO_READ) - report_exit->in_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG, ifd); + report_exit->in_sig_descr = erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ifd); if (read_write & DO_WRITE) - report_exit->out_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG, ofd); + report_exit->out_sig_descr = erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ofd); report_exit_list = report_exit; } @@ -730,13 +727,13 @@ static int set_driver_data(ErlDrvPort port_num, driver_data[ifd].pid = pid; driver_data[ifd].alive = 1; driver_data[ifd].status = 0; - driver_data[ifd].in_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG,ifd); + driver_data[ifd].in_sig_descr = erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ifd); driver_data[ifd].in_proc = create_process(OS_PRI_PROC,"beam_fd_reader", fd_reader_process, 0x800, FD_PROC_PRI, 0, 0, NULL, 0, 0); efs_clone(driver_data[ifd].in_proc); - sig = alloc(sizeof(SysDriverConfSignal), SYSDRIVERCONFSIG); + sig = alloc(sizeof(SysDriverConfSignal), ERTS_SIGNAL_FD_DRV_CONFIG); sig->conf_async.fd = ifd; sig->conf_async.parent = current_process(); send(&sig, driver_data[ifd].in_proc); @@ -745,12 +742,12 @@ static int set_driver_data(ErlDrvPort port_num, if (read_write & DO_WRITE) { driver_data[ifd].ofd = ofd; driver_data[ifd].out_sig_descr = - erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG,ofd); + erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ofd); driver_data[ifd].pdl = driver_pdl_create(port_num); driver_data[ifd].out_proc = create_process(OS_PRI_PROC, "beam_fd_writer", fd_writer_process, 0x800, FD_PROC_PRI, 0, 0, NULL, 0, 0); - sig = alloc(sizeof(SysDriverConfSignal), SYSDRIVERCONFSIG); + sig = alloc(sizeof(SysDriverConfSignal), ERTS_SIGNAL_FD_DRV_CONFIG); sig->conf_async.fd = ofd; sig->conf_async.parent = current_process(); send(&sig, driver_data[ifd].out_proc); @@ -771,13 +768,13 @@ static int set_driver_data(ErlDrvPort port_num, driver_data[ofd].pid = pid; driver_data[ofd].alive = 1; driver_data[ofd].status = 0; - driver_data[ofd].in_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG, + driver_data[ofd].in_sig_descr = erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ofd); driver_data[ofd].out_sig_descr = driver_data[ofd].in_sig_descr; driver_data[ofd].out_proc = create_process(OS_PRI_PROC, "beam_fd_writer", fd_writer_process, 0x800, FD_PROC_PRI, 0, 0, NULL, 0, 0); - sig = alloc(sizeof(SysDriverConfSignal), SYSDRIVERCONFSIG); + sig = alloc(sizeof(SysDriverConfSignal), ERTS_SIGNAL_FD_DRV_CONFIG); sig->conf_async.fd = ofd; sig->conf_async.parent = current_process(); send(&sig, driver_data[ofd].out_proc); @@ -830,7 +827,7 @@ OS_PROCESS(fd_reader_process) { int fd; byte *read_buf; - SIGSELECT sigsel[] = {1,SYSDRIVERCONFSIG}; + SIGSELECT sigsel[] = {1,ERTS_SIGNAL_FD_DRV_CONFIG}; #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_init(); @@ -855,7 +852,7 @@ OS_PROCESS(fd_reader_process) { } #endif - sigsel[1] = SYSDRIVERASYNCSIG; + sigsel[1] = ERTS_SIGNAL_FD_DRV_ASYNC; read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, ERTS_SYS_READ_BUF_SZ); @@ -863,7 +860,7 @@ OS_PROCESS(fd_reader_process) { int errno_copy = errno; ssize_t res; res = read(fd, read_buf, ERTS_SYS_READ_BUF_SZ); - sig = alloc(sizeof(SysDriverAsyncSignal), SYSDRIVERASYNCSIG); + sig = alloc(sizeof(SysDriverAsyncSignal), ERTS_SIGNAL_FD_DRV_ASYNC); sig->sys_async.buff = read_buf; sig->sys_async.res = res; if (res <= 0 && errno == EBADF) { @@ -888,7 +885,7 @@ OS_PROCESS(fd_writer_process) { OseSignal *sig; PROCESS parent; int fd; - SIGSELECT sigsel[] = { 1, SYSDRIVERCONFSIG, SYSDRIVERASYNCSIG }; + SIGSELECT sigsel[] = { 1, ERTS_SIGNAL_FD_DRV_CONFIG, ERTS_SIGNAL_FD_DRV_ASYNC }; TRACE; /* Only wait for config event with the fd which we are printing to */ @@ -928,7 +925,7 @@ OS_PROCESS(fd_writer_process) { /* fprintf(stderr,"0x%x: fd_writer, receive\n", current_process()); */ sig = receive(sigsel); /* size = sig->sys_async.res;*/ - if (sig->sig_no == SYSDRIVERCONFSIG) + if (sig->sig_no == ERTS_SIGNAL_FD_DRV_CONFIG) return; driver_pdl_lock(driver_data[fd].pdl); @@ -1147,7 +1144,7 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) driver_enqv(ix, ev, 0); /* n is the skip value */ driver_pdl_unlock(driver_data[fd].pdl); driver_select(ix, driver_data[fd].out_sig_descr, ERL_DRV_WRITE|ERL_DRV_USE, 1); - sig = alloc(sizeof(SysDriverAsyncSignal),SYSDRIVERASYNCSIG); + sig = alloc(sizeof(SysDriverAsyncSignal),ERTS_SIGNAL_FD_DRV_ASYNC); sig->sys_async.type = fd; sig->sys_async.res = pb+len; send(&sig,driver_data[fd].out_proc); @@ -1201,7 +1198,7 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) driver_enq(ix, buf, len); driver_pdl_unlock(driver_data[fd].pdl); driver_select(ix, driver_data[ofd].out_sig_descr, ERL_DRV_WRITE|ERL_DRV_USE, 1); - sig = alloc(sizeof(SysDriverAsyncSignal),SYSDRIVERASYNCSIG); + sig = alloc(sizeof(SysDriverAsyncSignal),ERTS_SIGNAL_FD_DRV_ASYNC); sig->sys_async.type = fd; sig->sys_async.res = pb+len; send(&sig,driver_data[fd].out_proc); -- cgit v1.2.3 From e2d4b774f4141bdf530696c0abe3704655ed3aa3 Mon Sep 17 00:00:00 2001 From: Jonas Karlsson Date: Tue, 15 Oct 2013 16:59:38 +0200 Subject: ose: lseek() to use SEEK_CUR This is needed because OSE does not use 1 for SEEK_CUR --- erts/emulator/drivers/ose/ose_efile.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/ose/ose_efile.c b/erts/emulator/drivers/ose/ose_efile.c index 8cd34a7bb0..fc255e11ef 100644 --- a/erts/emulator/drivers/ose/ose_efile.c +++ b/erts/emulator/drivers/ose/ose_efile.c @@ -33,8 +33,10 @@ #include "erl_efile.h" /*#include */ #ifdef HAVE_UNISTD_H +#ifndef __OSE__ #include #endif +#endif #ifdef HAVE_SYS_UIO_H #include #include @@ -60,8 +62,11 @@ #endif #ifdef __OSE__ +#include "unistd.h" #include "sys/stat.h" #include "dirent.h" +#include "sys/time.h" +#include "time.h" #endif /* Find a definition of MAXIOV, that is used in the code later. */ @@ -826,10 +831,8 @@ efile_truncate_file(Efile_error* errInfo, int *fd, int flags) { #ifndef NO_FTRUNCATE off_t offset; - - return check_error((offset = lseek(*fd, 0, 1)) >= 0 && - ftruncate(*fd, offset) == 0 ? 1 : -1, - errInfo); + return check_error((offset = lseek(*fd, 0, SEEK_CUR)) >= 0 && + ftruncate(*fd, offset) == 0 ? 1 : -1, errInfo); #else return 1; #endif -- cgit v1.2.3 From 8ed59e4a9dddf083d2046e1bd58f397221928c0e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 15 Oct 2013 18:10:47 +0200 Subject: ose: Change start way to use arguments to beam.smp directly The old way registered a shell command that needed to be executed. This way is more flexible as you can also use the lmconf file to set arguments there. --- erts/emulator/sys/ose/default.lmconf | 7 ++++-- erts/emulator/sys/ose/erl_main.c | 44 +----------------------------------- erts/lib_src/ose/ethread.c | 14 ++++++++---- 3 files changed, 15 insertions(+), 50 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/default.lmconf b/erts/emulator/sys/ose/default.lmconf index f897872fa2..150111b1f6 100644 --- a/erts/emulator/sys/ose/default.lmconf +++ b/erts/emulator/sys/ose/default.lmconf @@ -2,9 +2,12 @@ OSE_LM_STACK_SIZES=256,512,1024,2048,4096,8192,16384,65536 OSE_LM_SIGNAL_SIZES=31,63,127,255,1023,4095,16383,65535 OSE_LM_POOL_SIZE=0x200000 OSE_LM_MAIN_NAME=main -OSE_LM_MAIN_STACK_SIZE=0x400 +OSE_LM_MAIN_STACK_SIZE=0xF000 OSE_LM_MAIN_PRIORITY=20 -OSE_LM_PROGRAM_TYPE=SYS_RAM +OSE_LM_PROGRAM_TYPE=APP_RAM OSE_LM_DATA_INIT=YES OSE_LM_BSS_INIT=YES OSE_LM_EXEC_MODEL=SHARED +HEAP_MAX_SIZE=1000000000 +HEAP_SMALL_BUF_INIT_SIZE=64000000 +HEAP_LARGE_BUF_THRESHOLD=16000000 \ No newline at end of file diff --git a/erts/emulator/sys/ose/erl_main.c b/erts/emulator/sys/ose/erl_main.c index 21cfce9463..322058c87b 100644 --- a/erts/emulator/sys/ose/erl_main.c +++ b/erts/emulator/sys/ose/erl_main.c @@ -16,53 +16,11 @@ * * %CopyrightEnd% */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include "sys.h" -#include "erl_vm.h" -#include "global.h" - -#include "shell.h" -#include "ramlog.h" -#include "ose_err/ose_err.h" - -static PROCESS mainPid; - -#ifdef DEBUG -static OSADDRESS err_handler(OSBOOLEAN user_called, OSERRCODE ecode, OSERRCODE extra) { - fprintf(stderr,"err_handler: %p %p\n",ecode,extra); - return 1; -} -#endif - -static int -cmd_ek(int argc, char **argv) { - kill_proc(mainPid); - return 0; -} - -static int -cmd_erl_start(int argc, char **argv) { - ramlog_printf("\n"); - ramlog_printf("================================================================\n"); - ramlog_printf("\n"); -#ifdef DEBUG - create_error_handler(get_bid(current_process()),err_handler,0x100); -#endif - erl_start(argc, argv); - return 0; -} int main(int argc, char **argv) { - mainPid = current_process(); - - shell_add_cmd_attrs("start_beam", "start_beam [params]", "Start the Erlang VM", - cmd_erl_start, OS_PRI_PROC, 20, 0xF000); - shell_add_cmd_attrs("ek", "ek", "Kills the Erlang VM", - cmd_ek, OS_PRI_PROC, 20, 0x100); + erl_start(argc,argv); stop(current_process()); diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c index 7d5101d115..db20eab99f 100644 --- a/erts/lib_src/ose/ethread.c +++ b/erts/lib_src/ose/ethread.c @@ -96,11 +96,12 @@ union SIGNAL { static PROCESS blockId(void) { static PROCESS bid = (PROCESS)0; - if (bid == 0) { + /* For now we only use the same block. */ + /* if (bid == 0) { bid = create_block("Erlang-VM", 0, 0, 0, 0); } - - return bid; + return bid; */ + return 0; } static void thr_exit_cleanup(ethr_tid *tid, void *res) @@ -141,8 +142,10 @@ static OS_PROCESS(thr_wrapper) * the function return current domain. */ OSADDRESS domain = get_pid_info(current_process(), 16); +#ifdef DEBUG fprintf(stderr,"[0x%x] New process. Bid:0x%x, domain:%d, exec mode:%s\n", current_process(), bid, domain, execMode); +#endif } { @@ -392,10 +395,11 @@ ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, use_stack_size, /*opts->prio+5*/31, 0, blockId(), NULL, 0, 0); - if (ose_bind_process(tid->id, opts->coreNo)) { + /* For now we do not attempt to bind schedulers to different cores. + if (ose_bind_process(tid->id, opts->coreNo)) { printf("[0x%x] Binding pid 0x%x (%s) to core no %u.\n", current_process(), tid->id, opts->name, opts->coreNo); - } + }*/ /*FIXME!!! Normally this shouldn't be used in shared mode. Still there is * a problem with stdin fd in fd_ processes which should be further -- cgit v1.2.3 From fa3dd14716b2a7ad0c223ebacd2ffc6ecf6437e6 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 30 Sep 2013 15:07:49 +0200 Subject: ose: Add module that allows interaction with any OSE process The interface of this module is made to be as generic as possible in order for other IPC mechanisms to mimic it and allow porting of code between different os:es. --- erts/emulator/Makefile.in | 1 + erts/emulator/drivers/ose/ose_signal_drv.c | 884 +++++++++++++++++++++++++++++ erts/emulator/sys/ose/erl_poll.c | 2 - erts/emulator/sys/ose/erts.sig | 2 + 4 files changed, 887 insertions(+), 2 deletions(-) create mode 100644 erts/emulator/drivers/ose/ose_signal_drv.c (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 299d524bd3..9935b911f6 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -823,6 +823,7 @@ OS_OBJS += $(OBJDIR)/sys_float.o \ DRV_OBJS = \ $(OBJDIR)/efile_drv.o \ + $(OBJDIR)/ose_signal_drv.o \ $(OBJDIR)/inet_drv.o \ $(OBJDIR)/zlib_drv.o \ $(OBJDIR)/ram_file_drv.o diff --git a/erts/emulator/drivers/ose/ose_signal_drv.c b/erts/emulator/drivers/ose/ose_signal_drv.c new file mode 100644 index 0000000000..acf09f748e --- /dev/null +++ b/erts/emulator/drivers/ose/ose_signal_drv.c @@ -0,0 +1,884 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013-2013. All Rights Reserved. + * + * The 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 "errno.h" +#include "stdio.h" +#include "string.h" +#include "stddef.h" + +#include "sys.h" +#include "erl_driver.h" +#include "ose.h" +#include "ose_spi/ose_spi.h" + +#define DEBUG_ATTACH 0 +#define DEBUG_HUNT 0 +#define DEBUG_SEND 0 +#define DEBUG_LISTEN 0 + +#if 0 +#define DEBUGP(FMT,...) printf(FMT, __VA_ARGS__) +#else +#define DEBUGP(FMT,...) +#endif + +#if DEBUG_ATTACH +#define DEBUGP_ATTACH(...) DEBUGP( __VA_ARGS__) +#else +#define DEBUGP_ATTACH(...) +#endif + +#if DEBUG_HUNT +#define DEBUGP_HUNT(...) DEBUGP( __VA_ARGS__) +#else +#define DEBUGP_HUNT(...) +#endif + +#if DEBUG_LISTEN +#define DEBUGP_LISTEN(...) DEBUGP( __VA_ARGS__) +#else +#define DEBUGP_LISTEN(...) +#endif + +#if DEBUG_SEND +#define DEBUGP_SEND(...) DEBUGP( __VA_ARGS__) +#else +#define DEBUGP_SEND(...) +#endif + + +#define DRIVER_NAME "ose_signal_drv" +#define GET_SPID 1 +#define GET_NAME 2 +#define HUNT 100 +#define DEHUNT 101 +#define ATTACH 102 +#define DETACH 103 +#define SEND 104 +#define SEND_W_S 105 +#define LISTEN 106 +#define OPEN 200 + +#define REF_SEGMENT_SIZE 8 + +struct async { + SIGSELECT signo; + ErlDrvTermData port; + ErlDrvTermData proc; + PROCESS spid; + PROCESS target; + Uint32 ref; +}; + +/** + * OSE signals + **/ +union SIGNAL { + SIGSELECT signo; + struct async async; +}; + +/** + * The driver's context + **/ +typedef struct _driver_context { + ErlDrvPort port; + PROCESS spid; + ErlDrvEvent perm_events[2]; + ErlDrvEvent *events; + Uint32 event_cnt; + Uint32 ref; + Uint32 *outstanding_refs; + Uint32 outstanding_refs_max; + Uint32 outstanding_refs_cnt; +} driver_context_t; + +/** + * Global variables + **/ +static ErlDrvTermData a_ok; +static ErlDrvTermData a_error; +static ErlDrvTermData a_enomem; +static ErlDrvTermData a_enoent; +static ErlDrvTermData a_badarg; +static ErlDrvTermData a_mailbox_up; +static ErlDrvTermData a_mailbox_down; +static ErlDrvTermData a_ose_drv_reply; +static ErlDrvTermData a_message; +static PROCESS proxy_proc; + + +/** + * Serialize/unserialize unsigned 32-bit values + **/ +static char *put_u32(unsigned int value, char *ptr) { + *ptr++ = (value & 0xff000000) >> 24; + *ptr++ = (value & 0x00ff0000) >> 16; + *ptr++ = (value & 0x0000ff00) >> 8; + *ptr++ = (value & 0xff); + + return ptr; +} + +static unsigned int get_u32(char *ptr) { + unsigned int result = 0; + result += (ptr[0] & 0xff) << 24; + result += (ptr[1] & 0xff) << 16; + result += (ptr[2] & 0xff) << 8; + result += (ptr[3] & 0xff); + + return result; +} + + +/* Stolen from efile_drv.c */ + +/* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */ +#define EV_CHAR_P(ev, p, q) \ + (((char *)(ev)->iov[(q)].iov_base) + (p)) + +/* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */ +#define EV_GET_CHAR(ev, p, pp, qp) ev_get_char(ev, p ,pp, qp) +static int +ev_get_char(ErlIOVec *ev, char *p, int *pp, int *qp) { + if (*(pp)+1 <= (ev)->iov[*(qp)].iov_len) { + *(p) = *EV_CHAR_P(ev, *(pp), *(qp)); + if (*(pp)+1 < (ev)->iov[*(qp)].iov_len) + *(pp) = *(pp)+1; + else { + (*(qp))++; + *pp = 0; + } + return !0; + } + return 0; +} + +/* Uint32 EV_UINT32(ErlIOVec *ev, int p, int q)*/ +#define EV_UINT32(ev, p, q) \ + ((Uint32) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p))) + +/* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */ +#define EV_GET_UINT32(ev, p, pp, qp) ev_get_uint32(ev,(Uint32*)(p),pp,qp) +static int +ev_get_uint32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) { + if (*(pp)+4 <= (ev)->iov[*(qp)].iov_len) { + *(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24) + | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16) + | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8) + | (EV_UINT32(ev, *(pp)+3, *(qp))); + if (*(pp)+4 < (ev)->iov[*(qp)].iov_len) + *(pp) = *(pp)+4; + else { + (*(qp))++; + *pp = 0; + } + return !0; + } + return 0; +} + +/** + * Convinience macros + **/ +#define send_response(port,output) erl_drv_send_term(driver_mk_port(port),\ + driver_caller(port), output, sizeof(output) / sizeof(output[0])); + +void iov_memcpy(void *dest,ErlIOVec *ev,int ind,int off); +void iov_memcpy(void *dest,ErlIOVec *ev,int ind,int off) { + int i; + memcpy(dest,ev->iov[ind].iov_base+off,ev->iov[ind].iov_len-off); + for (i = ind+1; i < ev->vsize; i++) + memcpy(dest,ev->iov[i].iov_base,ev->iov[i].iov_len); +} + +/** + * Reference handling + **/ + +static int add_reference(driver_context_t *ctxt, Uint32 ref) { + + /* + * Premature optimizations may be evil, but they sure are fun. + */ + + if (ctxt->outstanding_refs == NULL) { + /* First ref to be ignored */ + ctxt->outstanding_refs = driver_alloc(REF_SEGMENT_SIZE*sizeof(Uint32)); + if (!ctxt->outstanding_refs) + return 1; + + memset(ctxt->outstanding_refs,0,REF_SEGMENT_SIZE*sizeof(Uint32)); + ctxt->outstanding_refs_max += REF_SEGMENT_SIZE; + ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref; + } else if (ctxt->outstanding_refs_cnt == ctxt->outstanding_refs_max) { + /* Expand ref array */ + Uint32 *new_array; + ctxt->outstanding_refs_max += REF_SEGMENT_SIZE; + new_array = driver_realloc(ctxt->outstanding_refs, + ctxt->outstanding_refs_max*sizeof(Uint32)); + + if (!new_array) { + ctxt->outstanding_refs_max -= REF_SEGMENT_SIZE; + return 1; + } + + ctxt->outstanding_refs = new_array; + + memset(ctxt->outstanding_refs+ctxt->outstanding_refs_cnt,0, + REF_SEGMENT_SIZE*sizeof(Uint32)); + ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref; + + } else { + /* Find an empty slot: + * First we try current index, + * then we scan for a slot. + */ + if (!ctxt->outstanding_refs[ctxt->outstanding_refs_cnt]) { + ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref; + } else { + int i; + ASSERT(ctxt->outstanding_refs_cnt < ctxt->outstanding_refs_max); + for (i = 0; i < ctxt->outstanding_refs_max; i++) + if (!ctxt->outstanding_refs[i]) + break; + ASSERT(ctxt->outstanding_refs[i] == 0); + ctxt->outstanding_refs[i] = ref; + ctxt->outstanding_refs_cnt++; + } + } + return 0; +} + +/* Return 0 if removed, 1 if does not exist, */ +static int remove_reference(driver_context_t *ctxt, Uint32 ref) { + int i,j; + + if (ctxt->outstanding_refs_max == 0 && ctxt->outstanding_refs_cnt == 0) { + ASSERT(ctxt->outstanding_refs == NULL); + return 1; + } + + for (i = 0; i < ctxt->outstanding_refs_max; i++) { + if (ctxt->outstanding_refs[i] == ref) { + ctxt->outstanding_refs[i] = 0; + ctxt->outstanding_refs_cnt--; + i = -1; + break; + } + } + + if (i != -1) + return 1; + + if (ctxt->outstanding_refs_cnt == 0) { + driver_free(ctxt->outstanding_refs); + ctxt->outstanding_refs = NULL; + ctxt->outstanding_refs_max = 0; + } else if (ctxt->outstanding_refs_cnt == (ctxt->outstanding_refs_max - REF_SEGMENT_SIZE)) { + Uint32 *new_array; + for (i = 0, j = 0; i < ctxt->outstanding_refs_cnt; i++) { + if (ctxt->outstanding_refs[i] == 0) { + for (j = i+1; j < ctxt->outstanding_refs_max; j++) + if (ctxt->outstanding_refs[j]) { + ctxt->outstanding_refs[i] = ctxt->outstanding_refs[j]; + ctxt->outstanding_refs[j] = 0; + break; + } + } + } + ctxt->outstanding_refs_max -= REF_SEGMENT_SIZE; + new_array = driver_realloc(ctxt->outstanding_refs, + ctxt->outstanding_refs_max*sizeof(Uint32)); + if (!new_array) { + ctxt->outstanding_refs_max += REF_SEGMENT_SIZE; + return 2; + } + + ctxt->outstanding_refs = new_array; + + } + + return 0; +} + +/** + * The OSE proxy process. This only handles ERTS_SIGNAL_OSE_DRV_ATTACH. + * The process is needed because signals triggered by attach ignore + * redir tables. + * + * We have one global proxy process to save memory. An attempt to make each + * port phantom into a proxy was made, but that used way to much memory. + */ +static OS_PROCESS(driver_proxy_process) { + SIGSELECT sigs[] = {1,ERTS_SIGNAL_OSE_DRV_ATTACH}; + PROCESS master = 0; + + while (1) { + union SIGNAL *sig = receive(sigs); + + if (sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) { + + /* The first message is used to determine who to send messages to. */ + if (master == 0) + master = sender(&sig); + + if (sig->async.target == 0) { + PROCESS from = sender(&sig); + restore(sig); + DEBUGP_ATTACH("0x%x: got attach 0x%x, sending to 0x%x\n", + current_process(),from,master); + sig->async.target = from; + send(&sig,master); + } else { + PROCESS target = sig->async.target; + restore(sig); + sig->async.target = 0; + DEBUGP_ATTACH("0x%x: doing attach on 0x%x\n",current_process(),target); + attach(&sig,target); + } + } + } +} + +/** + * Init routine for the driver + **/ +static int drv_init(void) { + + a_ok = driver_mk_atom("ok"); + a_error = driver_mk_atom("error"); + a_enomem = driver_mk_atom("enomem"); + a_enoent = driver_mk_atom("enoent"); + a_badarg = driver_mk_atom("badarg"); + a_mailbox_up = driver_mk_atom("mailbox_up"); + a_mailbox_down = driver_mk_atom("mailbox_down"); + a_ose_drv_reply = driver_mk_atom("ose_drv_reply"); + a_message = driver_mk_atom("message"); + + proxy_proc = create_process(get_ptype(current_process()), + "ose_signal_driver_proxy", + driver_proxy_process, 10000, + get_pri(current_process()), + 0, 0, NULL, 0, 0); + +#ifdef DEBUG + efs_clone(proxy_proc); +#endif + start(proxy_proc); + + return 0; +} + +/** + * Start routine for the driver + **/ +static ErlDrvData drv_start(ErlDrvPort port, char *command) +{ + driver_context_t *ctxt = driver_alloc(sizeof(driver_context_t)); + + ctxt->perm_events[0] = NULL; + ctxt->perm_events[1] = NULL; + + ctxt->spid = 0; + ctxt->port = port; + ctxt->event_cnt = 0; + ctxt->events = NULL; + ctxt->ref = 0; + ctxt->outstanding_refs = NULL; + ctxt->outstanding_refs_max = 0; + ctxt->outstanding_refs_cnt = 0; + + + /* Set the communication protocol to Erlang to be binary */ + set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); + + /* Everything ok */ + return (ErlDrvData)ctxt; +} + +/** + * Stop routine for the driver + **/ +static void drv_stop(ErlDrvData driver_data) +{ + driver_context_t *ctxt = (driver_context_t *)driver_data; + int i; + + /* HUNT + ATTACH */ + if (ctxt->perm_events[0]) + driver_select(ctxt->port, ctxt->perm_events[0], + ERL_DRV_USE|ERL_DRV_READ, 0); + if (ctxt->perm_events[1]) + driver_select(ctxt->port, ctxt->perm_events[1], + ERL_DRV_USE|ERL_DRV_READ, 0); + + for (i = 0; i < ctxt->event_cnt; i++) { + driver_select(ctxt->port, ctxt->events[i], ERL_DRV_USE|ERL_DRV_READ, 0); + } + + if (ctxt->spid != 0) + kill_proc(ctxt->spid); + DEBUGP("0x%x: stopped\n",ctxt->spid); + if (ctxt->events) + driver_free(ctxt->events); + if (ctxt->outstanding_refs) + driver_free(ctxt->outstanding_refs); + + driver_free(ctxt); +} + +/** + * Output from Erlang + **/ +static void outputv(ErlDrvData driver_data, ErlIOVec *ev) +{ + driver_context_t *ctxt = (driver_context_t *)driver_data; + int p = 0, q = 1; + char cmd; + + if (! EV_GET_CHAR(ev,&cmd,&p,&q)) { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_badarg, + ERL_DRV_TUPLE, 3}; + send_response(ctxt->port, output); + return; + } + + /* Command is in the buffer's first byte */ + switch(cmd) { + + case OPEN: { + char *name = driver_alloc(ev->size - 1+1); + struct OS_redir_entry redir[2]; + + redir[0].sig = 1; + redir[0].pid = current_process(); + + iov_memcpy(name,ev,q,p); + name[ev->size-1] = '\0'; + + ctxt->spid = create_process(OS_PHANTOM, name, NULL, 0, + 0, 0, 0, redir, 0, 0); + + DEBUGP("0x%x: open\n",ctxt->spid); + + ctxt->perm_events[1] = + erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH,(int)ctxt->spid); + driver_select(ctxt->port,ctxt->perm_events[1],ERL_DRV_READ|ERL_DRV_USE,1); + + ctxt->perm_events[0] = + erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_HUNT,(int)ctxt->spid); + driver_select(ctxt->port,ctxt->perm_events[0],ERL_DRV_READ|ERL_DRV_USE,1); + + start(ctxt->spid); + + { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_ok, + ERL_DRV_TUPLE, 3}; + + send_response(ctxt->port, output); + } + + break; + + } + + case ATTACH: + case HUNT: + { + union SIGNAL *sig = alloc(sizeof(union SIGNAL), + cmd == HUNT ? ERTS_SIGNAL_OSE_DRV_HUNT:ERTS_SIGNAL_OSE_DRV_ATTACH); + + sig->async.port = driver_mk_port(ctxt->port); + sig->async.proc = driver_caller(ctxt->port); + sig->async.spid = ctxt->spid; + sig->async.ref = ++ctxt->ref; + + if (add_reference(ctxt,ctxt->ref)) { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_enomem, + ERL_DRV_TUPLE, 3}; + send_response(ctxt->port, output); + free_buf(&sig); + } else { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_INT, (ErlDrvUInt)ctxt->ref, + ERL_DRV_TUPLE, 2, + ERL_DRV_TUPLE, 3}; + send_response(ctxt->port, output); + + if (cmd == HUNT) { + char *huntname = driver_alloc(sizeof(char)*((ev->size-1)+1)); + + iov_memcpy(huntname,ev,q,p); + huntname[ev->size-1] = '\0'; + + DEBUGP_HUNT("0x%x: hunt %s -> %u (%u,%u)\n", + ctxt->spid,huntname,ctxt->ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + + hunt(huntname, 0, NULL, &sig); + + driver_free(huntname); + } else { + EV_GET_UINT32(ev,&sig->async.target,&p,&q); + DEBUGP_ATTACH("0x%x: attach %u -> %u (%u,%u)\n", + ctxt->spid,sig->async.target, + ctxt->ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + + send(&sig,proxy_proc); + } + + } + + break; + } + + case DETACH: + case DEHUNT: + { + + Uint32 ref; + + EV_GET_UINT32(ev,&ref,&p,&q); + if (cmd == DETACH) { + DEBUGP_ATTACH("0x%x: detach %u (%u,%u)\n",ctxt->spid,ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + } else { + DEBUGP_HUNT("0x%x: dehunt %u (%u,%u)\n",ctxt->spid,ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + } + + if (remove_reference(ctxt,ref)) { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_error, + ERL_DRV_ATOM, a_enoent, + ERL_DRV_TUPLE, 2, + ERL_DRV_TUPLE, 3}; + + send_response(ctxt->port, output); + } else { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_ok, + ERL_DRV_TUPLE, 3}; + + send_response(ctxt->port, output); + } + + break; + } + + case SEND: + case SEND_W_S: + { + PROCESS spid; + PROCESS sender; + SIGSELECT signo; + OSBUFSIZE size = ev->size-9; + union SIGNAL *sig; + + EV_GET_UINT32(ev,&spid,&p,&q); + + if (cmd == SEND_W_S) { + EV_GET_UINT32(ev,&sender,&p,&q); + size -= 4; + } else { + sender = ctxt->spid; + } + + EV_GET_UINT32(ev,&signo,&p,&q); + + sig = alloc(size + sizeof(SIGSELECT),signo); + + if (cmd == SEND_W_S) { + DEBUGP_SEND("0x%x: send_w_s(%u,%u,%u)\n",ctxt->spid,spid,signo,sender); + } else { + DEBUGP_SEND("0x%x: send(%u,%u)\n",ctxt->spid,spid,signo); + } + + iov_memcpy(((char *)&sig->signo) + sizeof(SIGSELECT),ev,q,p); + + send_w_s(&sig, sender, spid); + + break; + } + + case LISTEN: + { + int i,j,event_cnt = (ev->size - 1)/4; + ErlDrvEvent *events = NULL; + SIGSELECT signo,tmp_signo; + + if (event_cnt == 0) { + for (i = 0; i < ctxt->event_cnt; i++) + driver_select(ctxt->port,ctxt->events[i],ERL_DRV_READ|ERL_DRV_USE,0); + if (ctxt->events) + driver_free(ctxt->events); + } else { + events = driver_alloc(sizeof(ErlDrvEvent)*event_cnt); + EV_GET_UINT32(ev,&signo,&p,&q); + for (i = 0, j = 0; i < event_cnt || j < ctxt->event_cnt; ) { + + if (ctxt->events) + erl_drv_ose_event_fetch(ctxt->events[j],&tmp_signo,NULL); + + if (signo == tmp_signo) { + events[i++] = ctxt->events[j++]; + EV_GET_UINT32(ev,&signo,&p,&q); + } else if (signo < tmp_signo || !ctxt->events) { + /* New signal to select on */ + events[i] = erl_drv_ose_event_alloc(signo,(int)ctxt->spid); + driver_select(ctxt->port,events[i++],ERL_DRV_READ|ERL_DRV_USE,1); + EV_GET_UINT32(ev,&signo,&p,&q); + } else { + /* Remove old signal to select on */ + driver_select(ctxt->port,ctxt->events[j++],ERL_DRV_READ|ERL_DRV_USE,0); + } + } + if (ctxt->events) + driver_free(ctxt->events); + } + ctxt->events = events; + ctxt->event_cnt = event_cnt; + + { + ErlDrvTermData output[] = { + ERL_DRV_ATOM, a_ose_drv_reply, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_ATOM, a_ok, + ERL_DRV_TUPLE, 3}; + send_response(ctxt->port, output); + } + break; + } + + default: + { + DEBUGP("Warning: 'ose_signal_drv' unknown command '%d'\n", cmd); + break; + } + } +} + +/** + * Handler for when OSE signal arrives + **/ +static void ready_input(ErlDrvData driver_data, ErlDrvEvent event) +{ + driver_context_t *ctxt = (driver_context_t *)driver_data; + union SIGNAL *sig = erl_drv_ose_get_input_signal(event); + + while (sig != NULL) { + + switch(sig->signo) + { + /* Remote process is available */ + case ERTS_SIGNAL_OSE_DRV_HUNT: + { + const PROCESS spid = sender(&sig); + + if (remove_reference(ctxt,sig->async.ref)) { + DEBUGP_HUNT("0x%x: Got hunt from 0x%x -> %u (CANCELLED) (%u,%u)\n", + ctxt->spid,spid,sig->async.ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + /* Already removed by dehunt */ + } else { + ErlDrvTermData reply[] = { + ERL_DRV_ATOM, a_mailbox_up, + ERL_DRV_PORT, sig->async.port, + ERL_DRV_PORT, sig->async.port, + ERL_DRV_UINT, (ErlDrvUInt)sig->async.ref, + ERL_DRV_TUPLE, 2, + ERL_DRV_UINT, (ErlDrvUInt)spid, + ERL_DRV_TUPLE, 4}; + DEBUGP_HUNT("0x%x: Got hunt from 0x%x -> %u (%u,%u)\n", + ctxt->spid,spid,sig->async.ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + erl_drv_send_term(sig->async.port, sig->async.proc, reply, + sizeof(reply) / sizeof(reply[0])); + } + break; + } + + /* Remote process is down */ + case ERTS_SIGNAL_OSE_DRV_ATTACH: + { + PROCESS spid = sig->async.target; + + if (remove_reference(ctxt,sig->async.ref)) { + DEBUGP_ATTACH("0x%x: Got attach from 0x%x -> %u (CANCELLED) (%u,%u)\n", + ctxt->spid,spid,sig->async.ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + /* Already removed by detach */ + } else { + ErlDrvTermData reply[] = { + ERL_DRV_ATOM, a_mailbox_down, + ERL_DRV_PORT, sig->async.port, + ERL_DRV_PORT, sig->async.port, + ERL_DRV_UINT, sig->async.ref, + ERL_DRV_TUPLE, 2, + ERL_DRV_UINT, (ErlDrvUInt)spid, + ERL_DRV_TUPLE, 4}; + DEBUGP_ATTACH("0x%x: Got attach from 0x%x -> %u (%u,%u)\n", + ctxt->spid,spid,sig->async.ref, + ctxt->outstanding_refs_cnt, + ctxt->outstanding_refs_max); + erl_drv_send_term(sig->async.port, sig->async.proc, reply, + sizeof(reply) / sizeof(reply[0])); + } + break; + } + + /* Received user defined signal */ + default: + { + const PROCESS spid = sender(&sig); + const OSBUFSIZE size = sigsize(&sig) - sizeof(SIGSELECT); + const char *sig_data = ((char *)&sig->signo) + sizeof(SIGSELECT); + + ErlDrvTermData reply[] = { + ERL_DRV_ATOM, a_message, + ERL_DRV_PORT, driver_mk_port(ctxt->port), + ERL_DRV_UINT, (ErlDrvUInt)spid, + ERL_DRV_UINT, (ErlDrvUInt)ctxt->spid, + ERL_DRV_UINT, (ErlDrvUInt)sig->signo, + ERL_DRV_BUF2BINARY, (ErlDrvTermData)sig_data, (ErlDrvUInt)size, + ERL_DRV_TUPLE, 4, + ERL_DRV_TUPLE, 3}; + + DEBUGP_SEND("0x%x: Got 0x%u\r\n", spid, sig->signo); + + erl_drv_output_term(driver_mk_port(ctxt->port), reply, + sizeof(reply) / sizeof(reply[0])); + break; + } + } + + free_buf(&sig); + sig = erl_drv_ose_get_input_signal(event); + } +} + +/** + * Handler for 'port_control' + **/ +static ErlDrvSSizeT control(ErlDrvData driver_data, unsigned int cmd, + char *buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen) +{ + driver_context_t *ctxt = (driver_context_t *)driver_data; + + switch(cmd) + { + case GET_SPID: + { + const PROCESS spid = ctxt->spid; + put_u32(spid, *rbuf); + return sizeof(PROCESS); + } + + case GET_NAME: + { + const PROCESS spid = get_u32(buf); + char *name = (char*)get_pid_info(spid,OSE_PI_NAME); + int n; + if (!name) { + *rbuf = NULL; + return 0; + } + + if (rlen < (n = strlen(name))) { + ErlDrvBinary *bin = driver_alloc_binary(n); + strncpy(bin->orig_bytes,name,n); + *rbuf = (char*)bin; + } else + strncpy(*rbuf,name,n); + free_buf((union SIGNAL**)&name); + + return n; + } + default: + { + /* Unknown command */ + return (ErlDrvSSizeT)ERL_DRV_ERROR_GENERAL; + break; + } + } +} + +static void stop_select(ErlDrvEvent event, void *reserved) +{ + erl_drv_ose_event_free(event); +} + +static int resolve_signal(OseSignal* osig, int *mode) { + union SIGNAL *sig = osig; + if (sig->signo == ERTS_SIGNAL_OSE_DRV_HUNT || + sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) { + return sig->async.spid; + } + DEBUGP("%p: Got signal %d sent to %p from 0x%p\n", + current_process(),sig->signo,addressee(&sig),sender(&sig)); + return addressee(&sig); +} + +/** + * Setup the driver entry for the Erlang runtime + **/ +ErlDrvEntry ose_signal_driver_entry = { + .init = drv_init, + .start = drv_start, + .stop = drv_stop, + .outputv = outputv, + .ready_input = ready_input, + .driver_name = DRIVER_NAME, + .control = control, + .extended_marker = ERL_DRV_EXTENDED_MARKER, + .major_version = ERL_DRV_EXTENDED_MAJOR_VERSION, + .minor_version = ERL_DRV_EXTENDED_MINOR_VERSION, + .driver_flags = ERL_DRV_FLAG_USE_PORT_LOCKING, + .stop_select = stop_select, + .resolve_signal = resolve_signal +}; diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c index 0bea6865ca..b1e256afc3 100644 --- a/erts/emulator/sys/ose/erl_poll.c +++ b/erts/emulator/sys/ose/erl_poll.c @@ -275,7 +275,6 @@ static void update_redir_tables(ErtsPollSet ps) { redir_table[0].pid = 0; for (i = 1; i < ps->sig_count+1; i++) { - ramlog_printf("Adding 0x%p -> 0x%p to redir table\n",ps->sigs[i],sched_1); redir_table[i].sig = ps->sigs[i]; redir_table[i].pid = sched_1; } @@ -283,7 +282,6 @@ static void update_redir_tables(ErtsPollSet ps) { for (i = 1; i < erts_no_schedulers; i++) { ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(i); set_redirection(esdp->tid.id,redir_table); - ramlog_printf("Setting redir table to 0x%p\n",esdp->tid.id); } SEL_FREE(ERTS_ALC_T_POLLSET,redir_table); diff --git a/erts/emulator/sys/ose/erts.sig b/erts/emulator/sys/ose/erts.sig index 760d0896c0..4bd2056cb0 100644 --- a/erts/emulator/sys/ose/erts.sig +++ b/erts/emulator/sys/ose/erts.sig @@ -8,5 +8,7 @@ #define ERTS_SIGNAL_INVALID ERTS_OSE_SIGNAL_BASE #define ERTS_SIGNAL_FD_DRV_CONFIG ERTS_OSE_SIGNAL_BASE+1 #define ERTS_SIGNAL_FD_DRV_ASYNC ERTS_OSE_SIGNAL_BASE+2 +#define ERTS_SIGNAL_OSE_DRV_ATTACH ERTS_OSE_SIGNAL_BASE+3 +#define ERTS_SIGNAL_OSE_DRV_HUNT ERTS_OSE_SIGNAL_BASE+4 #endif -- cgit v1.2.3 From 7e6cdce4925cd033812bbb9b9f113c1862225439 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 15 Oct 2013 18:40:03 +0200 Subject: ose: Copied driver_int.h from unix --- erts/emulator/sys/ose/driver_int.h | 41 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 erts/emulator/sys/ose/driver_int.h (limited to 'erts') diff --git a/erts/emulator/sys/ose/driver_int.h b/erts/emulator/sys/ose/driver_int.h new file mode 100644 index 0000000000..2c9ac955d8 --- /dev/null +++ b/erts/emulator/sys/ose/driver_int.h @@ -0,0 +1,41 @@ +/* + * %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% + */ +/* + * System dependant driver declarations + */ + +#ifndef __DRIVER_INT_H__ +#define __DRIVER_INT_H__ + +#ifdef HAVE_SYS_UIO_H +#include +#include + +typedef struct iovec SysIOVec; + +#else + +typedef struct { + char* iov_base; + int iov_len; +} SysIOVec; + +#endif + +#endif -- cgit v1.2.3 From 5dc2c6bfaed18e9a6cd23abe70be785599b5d9cc Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 21 Oct 2013 17:43:32 +0200 Subject: ose: Change get_envp to ose_get_ppdata for tsd There is a system limit on the number of ppdata that is available but that should not be reached, and ppdata is faster than using get_envp. --- erts/include/internal/ethread.h | 6 +----- erts/lib_src/ose/ethread.c | 44 ++++++++++------------------------------- 2 files changed, 11 insertions(+), 39 deletions(-) (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 581eb22edb..7f3786ec63 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -210,11 +210,7 @@ typedef struct { void *res; } ethr_tid; -struct ethr_tsd_key__ { - PROCESS id; - char key[]; -}; -typedef struct ethr_tsd_key__* ethr_tsd_key; +typedef OSPPDKEY ethr_tsd_key; #undef ETHR_HAVE_ETHR_SIG_FUNCS diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c index db20eab99f..7046cc8d03 100644 --- a/erts/lib_src/ose/ethread.c +++ b/erts/lib_src/ose/ethread.c @@ -115,11 +115,6 @@ static void thr_exit_cleanup(ethr_tid *tid, void *res) ethr_ts_event_destructor__((void *) ethr_get_tse__()); } -/* For debug purpose. The process name will be stored in a per-process pointer - * for quick access. - */ -static OSPPDKEY nameKey = 0; - //static OS_PROCESS(thr_wrapper); static OS_PROCESS(thr_wrapper) { @@ -152,13 +147,6 @@ static OS_PROCESS(thr_wrapper) SIGSELECT sigsel[] = {1,ETHREADWRAPDATASIG}; union SIGNAL *init_msg = receive(sigsel); - { - char **name = (char**)ose_get_ppdata(nameKey); - - *name = (char*)alloc(strlen(init_msg->data.name)+1, 0); - strcpy(*name, init_msg->data.name); - } - thr_func = init_msg->data.thr_func; arg = init_msg->data.arg; @@ -307,8 +295,6 @@ ethr_init(ethr_init_data *id) ETHR_ASSERT(&main_thr_tid == ETHR_GET_OWN_TID__); - ose_create_ppdata("ProcName", &nameKey); - ethr_not_inited__ = 0; return 0; @@ -537,7 +523,7 @@ int ethr_tsd_key_create(ethr_tsd_key *keyp) { ethr_tid *tid = ETHR_GET_OWN_TID__; - ethr_tsd_key key; + char keyname[31]; #if ETHR_XCHK if (ethr_not_inited__) { @@ -552,13 +538,10 @@ ethr_tsd_key_create(ethr_tsd_key *keyp) if (tid->tsd_key_index > 999) return EAGAIN; - /* ethread_tsd_key_YYYYYYYY_XXX\0 */ - key = malloc(sizeof(ethr_tsd_key)+sizeof(char)*(strlen("ethread_tsd_key_0xYYYYYYYY_XXX")+1)); /* What do we do it tds_key_index happens to wrap? Slot search? */ - sprintf(key->key,"ethread_tsd_key_0x%x_%d",tid->id,tid->tsd_key_index++); - key->id = current_process(); + sprintf(keyname,"ethread_tsd_key_0x%x_%d",tid->id,tid->tsd_key_index++); - *keyp = key; + ose_create_ppdata(keyname,keyp); return 0; } @@ -572,20 +555,24 @@ ethr_tsd_key_delete(ethr_tsd_key key) return EACCES; } #endif - free(key); + /* Not possible to delete ppdata */ + return 0; } int ethr_tsd_set(ethr_tsd_key key, void *value) { + void **ppdp; #if ETHR_XCHK if (ethr_not_inited__) { ETHR_ASSERT(0); return EACCES; } #endif - return set_envp(current_process(), key->key, (OSADDRESS)value)?0:1; + ppdp = (void **)ose_get_ppdata(key); + *ppdp = value; + return 0; } void * @@ -597,7 +584,7 @@ ethr_tsd_get(ethr_tsd_key key) return NULL; } #endif - return (void*)get_envp(current_process(),key->key); + return *(void**)ose_get_ppdata(key); } /* @@ -645,14 +632,3 @@ ethr_abort__(void) { abort(); } - -const char *procName(void); -const char * -procName(void) { - char **procName_p = (char**)ose_get_ppdata(nameKey); - if (procName_p) { - return *procName_p; - } - - return NULL; -} -- cgit v1.2.3 From 51a07bb5770ddbbf10c5be969985c5f6b36e4a4c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 22 Oct 2013 17:49:17 +0200 Subject: ose: Add link conf for gcc 4.4.3 --- erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf | 147 +++++++++++++++++++ erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf | 219 +++++++++++++++++++++++++++++ erts/emulator/sys/ose/gcc_lm_ppc.lcf | 219 ----------------------------- 3 files changed, 366 insertions(+), 219 deletions(-) create mode 100644 erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf create mode 100644 erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf delete mode 100644 erts/emulator/sys/ose/gcc_lm_ppc.lcf (limited to 'erts') diff --git a/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf b/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf new file mode 100644 index 0000000000..65a3f1757d --- /dev/null +++ b/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf @@ -0,0 +1,147 @@ +OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc") +OUTPUT_ARCH("powerpc") +ENTRY("crt0_lm") +MEMORY +{ + rom : ORIGIN = 0x01000000, LENGTH = 0x01000000 + ram : ORIGIN = 0x02000000, LENGTH = 0x01000000 +} +PHDRS +{ + ph_conf PT_LOAD ; + ph_rom PT_LOAD ; + ph_ram PT_LOAD ; +} +SECTIONS +{ + .text : + { + *(.text_first) + *(.text) + *(.text.*) + *(.stub) + *(oscode) + *(.init*) + *(.fini*) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + *(.glue_7t) + *(.glue_7) + } > rom :ph_rom = 0 + .ose_sfk_biosentry : + { + *(.ose_sfk_biosentry) + } > rom :ph_rom + .ctors : + { + __CTOR_LIST__ = .; + *(.ctors) + *(SORT(.ctors.*)) + __CTOR_END__ = .; + } > rom :ph_rom + .dtors : + { + __DTOR_LIST__ = .; + *(.dtors) + *(SORT(.dtors.*)) + __DTOR_END__ = .; + } > rom :ph_rom + OSESYMS : + { + *(.osesyms) + } > rom :ph_rom + .rodata : + { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + } > rom :ph_rom + .eh_frame : + { + __EH_FRAME_BEGIN__ = .; + *(.eh_frame) + LONG(0) + __EH_FRAME_END__ = .; + } > rom :ph_rom + .gcc_except_table : + { + *(.gcc_except_table .gcc_except_table.*) + } > rom :ph_rom + .sdata2 : + { + PROVIDE (_SDA2_BASE_ = .); + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + } > rom :ph_rom + .sbss2 : + { + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + } > rom :ph_rom + LMCONF : + { + obj/?*?/ose_confd.o(.rodata) + *(LMCONF) + } > rom :ph_conf + .data : + { + LONG(0xDEADBABE) + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + . = ALIGN(0x10); + } > ram :ph_ram = 0 + .sdata2 : + { + _SDA2_BASE_ = .; + *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) + }> ram :ph_ram + .sdata : + { + PROVIDE (_SDA_BASE_ = .); + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + } > ram :ph_ram + .sbss : + { + *(.sbss) + *(.sbss.*) + *(.scommon) + *(.gnu.linkonce.sb.*) + } > ram :ph_ram + .bss (NOLOAD) : + { + *(.bss) + *(.bss.*) + *(COMMON) + *(.gnu.linkonce.b.*) + *(.osvars) + } > ram :ph_ram + .ignore (NOLOAD) : + { + *(.rel.dyn) + } > ram :ph_ram + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} +__OSESYMS_START = ADDR(OSESYMS); +__OSESYMS_END = ADDR(OSESYMS) + SIZEOF(OSESYMS); diff --git a/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf b/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf new file mode 100644 index 0000000000..a2399c93da --- /dev/null +++ b/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf @@ -0,0 +1,219 @@ +/******************************************************************************* + * gcc_lm_ppc.lcf: GCC linker control file for PowerPC load modules + * @(#) $FilePath: /vobs/ose5/system/refsys/compilers/gcc_lm_ppc.lcf $ + * @(#) $FileRevision: /main/tb_current_ose5/10 $ + * $Author: joka $$Date: 01/21/13 16:35:41 $ + * $Copyright: (C) 2006 by Enea AB. All rights reserved. $ + ******************************************************************************/ + +OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc") +OUTPUT_ARCH("powerpc") + +ENTRY("crt0_lm") + +/* Note: + * You may have to increase the length of the "rom" memory region and the + * origin and length of the "ram" memory region below depending on the size + * of the code and data in your load module. + */ + +MEMORY +{ + conf : ORIGIN = 0x00100000, LENGTH = 0x00030000 + rom : ORIGIN = 0x01000000, LENGTH = 0x01000000 + ram : ORIGIN = 0x03000000, LENGTH = 0x01000000 +} + +PHDRS +{ + ph_conf PT_LOAD ; + ph_rom PT_LOAD ; + ph_ram PT_LOAD ; +} + +SECTIONS +{ +/*--------------------------------------------------------------------------- + * Load module configuration area + *-------------------------------------------------------------------------*/ + + /* Load module configuration section. */ + LMCONF : + { + obj/?*?/ose_confd.o(.rodata) + *(LMCONF) + } > conf :ph_conf + +/*--------------------------------------------------------------------------- + * Read-only area + *-------------------------------------------------------------------------*/ + + /* Code section. */ + .text : + { + *(.text) + *(.text.*) + *(.stub) + *(oscode) + *(.init*) + *(.fini*) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + } > rom :ph_rom = 0 + + /* OSE symbols section. */ + OSESYMS : + { + *(.osesyms) + } > rom :ph_rom + + /* Read-only data section. */ + .rodata : + { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + } > rom :ph_rom + + /* C++ exception handling section. */ + .eh_frame : + { + __EH_FRAME_BEGIN__ = .; + *(.eh_frame) + LONG(0) + __EH_FRAME_END__ = .; + } > rom :ph_rom + + /* C++ exception handling section. */ + .gcc_except_table : + { + *(.gcc_except_table .gcc_except_table.*) + } > rom :ph_rom + + /* PowerPC EABI initialized read-only data section. */ + .sdata2 : + { + PROVIDE (_SDA2_BASE_ = .); + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + } > rom :ph_rom + + /* PowerPC EABI uninitialized read-only data section. */ + .sbss2 : + { + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + } > rom :ph_rom + +/*--------------------------------------------------------------------------- + * Read-write area + *-------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------- + * Initialized data (copied by PM) + *-----------------------------------------------------------------*/ + + /* Data section. */ + .data : + { + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } > ram :ph_ram + + /* C++ constructor section. */ + .ctors : + { + __CTOR_LIST__ = .; + *(.ctors) + *(SORT(.ctors.*)) + __CTOR_END__ = .; + } > ram :ph_ram + + /* C++ destructor section. */ + .dtors : + { + __DTOR_LIST__ = .; + *(.dtors) + *(SORT(.dtors.*)) + __DTOR_END__ = .; + } > ram :ph_ram + + + /* Small data section. */ + .sdata ALIGN(0x10) : + { + PROVIDE (_SDA_BASE_ = .); + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + } > ram :ph_ram + + /*------------------------------------------------------------------- + * Uninitialized data (cleared by PM) + *-----------------------------------------------------------------*/ + + /* Small bss section. */ + .sbss : + { + *(.sbss) + *(.sbss.*) + *(.scommon) + *(.gnu.linkonce.sb.*) + } > ram :ph_ram + + /* Bss section. */ + .bss : + { + *(.bss) + *(.bss.*) + *(COMMON) + *(.gnu.linkonce.b.*) + } > ram :ph_ram + +/*--------------------------------------------------------------------------- + * Debug information + *-------------------------------------------------------------------------*/ + + /* + * Stabs debug sections. + */ + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + + /* + * DWARF debug sections. + */ + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} diff --git a/erts/emulator/sys/ose/gcc_lm_ppc.lcf b/erts/emulator/sys/ose/gcc_lm_ppc.lcf deleted file mode 100644 index a2399c93da..0000000000 --- a/erts/emulator/sys/ose/gcc_lm_ppc.lcf +++ /dev/null @@ -1,219 +0,0 @@ -/******************************************************************************* - * gcc_lm_ppc.lcf: GCC linker control file for PowerPC load modules - * @(#) $FilePath: /vobs/ose5/system/refsys/compilers/gcc_lm_ppc.lcf $ - * @(#) $FileRevision: /main/tb_current_ose5/10 $ - * $Author: joka $$Date: 01/21/13 16:35:41 $ - * $Copyright: (C) 2006 by Enea AB. All rights reserved. $ - ******************************************************************************/ - -OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc") -OUTPUT_ARCH("powerpc") - -ENTRY("crt0_lm") - -/* Note: - * You may have to increase the length of the "rom" memory region and the - * origin and length of the "ram" memory region below depending on the size - * of the code and data in your load module. - */ - -MEMORY -{ - conf : ORIGIN = 0x00100000, LENGTH = 0x00030000 - rom : ORIGIN = 0x01000000, LENGTH = 0x01000000 - ram : ORIGIN = 0x03000000, LENGTH = 0x01000000 -} - -PHDRS -{ - ph_conf PT_LOAD ; - ph_rom PT_LOAD ; - ph_ram PT_LOAD ; -} - -SECTIONS -{ -/*--------------------------------------------------------------------------- - * Load module configuration area - *-------------------------------------------------------------------------*/ - - /* Load module configuration section. */ - LMCONF : - { - obj/?*?/ose_confd.o(.rodata) - *(LMCONF) - } > conf :ph_conf - -/*--------------------------------------------------------------------------- - * Read-only area - *-------------------------------------------------------------------------*/ - - /* Code section. */ - .text : - { - *(.text) - *(.text.*) - *(.stub) - *(oscode) - *(.init*) - *(.fini*) - *(.gnu.warning) - *(.gnu.linkonce.t.*) - } > rom :ph_rom = 0 - - /* OSE symbols section. */ - OSESYMS : - { - *(.osesyms) - } > rom :ph_rom - - /* Read-only data section. */ - .rodata : - { - *(.rodata) - *(.rodata.*) - *(.gnu.linkonce.r.*) - } > rom :ph_rom - - /* C++ exception handling section. */ - .eh_frame : - { - __EH_FRAME_BEGIN__ = .; - *(.eh_frame) - LONG(0) - __EH_FRAME_END__ = .; - } > rom :ph_rom - - /* C++ exception handling section. */ - .gcc_except_table : - { - *(.gcc_except_table .gcc_except_table.*) - } > rom :ph_rom - - /* PowerPC EABI initialized read-only data section. */ - .sdata2 : - { - PROVIDE (_SDA2_BASE_ = .); - *(.sdata2) - *(.sdata2.*) - *(.gnu.linkonce.s2.*) - } > rom :ph_rom - - /* PowerPC EABI uninitialized read-only data section. */ - .sbss2 : - { - *(.sbss2) - *(.sbss2.*) - *(.gnu.linkonce.sb2.*) - } > rom :ph_rom - -/*--------------------------------------------------------------------------- - * Read-write area - *-------------------------------------------------------------------------*/ - - /*------------------------------------------------------------------- - * Initialized data (copied by PM) - *-----------------------------------------------------------------*/ - - /* Data section. */ - .data : - { - *(.data) - *(.data.*) - *(.gnu.linkonce.d.*) - SORT(CONSTRUCTORS) - } > ram :ph_ram - - /* C++ constructor section. */ - .ctors : - { - __CTOR_LIST__ = .; - *(.ctors) - *(SORT(.ctors.*)) - __CTOR_END__ = .; - } > ram :ph_ram - - /* C++ destructor section. */ - .dtors : - { - __DTOR_LIST__ = .; - *(.dtors) - *(SORT(.dtors.*)) - __DTOR_END__ = .; - } > ram :ph_ram - - - /* Small data section. */ - .sdata ALIGN(0x10) : - { - PROVIDE (_SDA_BASE_ = .); - *(.sdata) - *(.sdata.*) - *(.gnu.linkonce.s.*) - } > ram :ph_ram - - /*------------------------------------------------------------------- - * Uninitialized data (cleared by PM) - *-----------------------------------------------------------------*/ - - /* Small bss section. */ - .sbss : - { - *(.sbss) - *(.sbss.*) - *(.scommon) - *(.gnu.linkonce.sb.*) - } > ram :ph_ram - - /* Bss section. */ - .bss : - { - *(.bss) - *(.bss.*) - *(COMMON) - *(.gnu.linkonce.b.*) - } > ram :ph_ram - -/*--------------------------------------------------------------------------- - * Debug information - *-------------------------------------------------------------------------*/ - - /* - * Stabs debug sections. - */ - - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } - - /* - * DWARF debug sections. - */ - - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } -} -- cgit v1.2.3 From 5009faf90001b6fdba3226c227db758b75627a20 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 23 Oct 2013 10:26:15 +0200 Subject: ose: Add eh_frame_hdr section to linker file --- erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf b/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf index 65a3f1757d..5ebc328616 100644 --- a/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf +++ b/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf @@ -56,6 +56,10 @@ SECTIONS *(.rodata.*) *(.gnu.linkonce.r.*) } > rom :ph_rom + .eh_frame_hdr : + { + *(.eh_frame_hdr) + } > rom :ph_rom .eh_frame : { __EH_FRAME_BEGIN__ = .; -- cgit v1.2.3 From a3eba23b0caedcbd1d4cf181cb06b6f0d86f8bd4 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 28 Oct 2013 12:11:18 +0100 Subject: ose: Change signal base to registered range --- erts/emulator/sys/ose/erts.sig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/erts.sig b/erts/emulator/sys/ose/erts.sig index 4bd2056cb0..95c12652f2 100644 --- a/erts/emulator/sys/ose/erts.sig +++ b/erts/emulator/sys/ose/erts.sig @@ -2,7 +2,7 @@ #define ERTS_OSE_SIGNALS #ifndef ERTS_OSE_SIGNAL_BASE -#define ERTS_OSE_SIGNAL_BASE 1000 +#define ERTS_OSE_SIGNAL_BASE 0x01900280 #endif #define ERTS_SIGNAL_INVALID ERTS_OSE_SIGNAL_BASE -- cgit v1.2.3 From d932131754c2bfb2e0539b6419e3d09533fe84e8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 28 Oct 2013 16:10:07 +0100 Subject: erts: configure number of write_concurrency locks Make it possible to change the number of write_concurrency locks to use. This is usefull to change when you for some reason want to use more/less locks per write_concurrency ets table. eg. OSs with a limit on how many mutexes can exist at once. --- erts/configure.in | 11 +++++++++++ erts/emulator/beam/erl_db_hash.h | 5 +++++ 2 files changed, 16 insertions(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 7ad0a6caf0..c964327ebc 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -426,6 +426,17 @@ AS_HELP_STRING([--enable-static-drivers], [comma seperated list of linked-in dri STATIC_DRIVERS=no) AC_SUBST(STATIC_DRIVERS) +AC_ARG_WITH(ets-write-concurrency-locks, + AS_HELP_STRING([--with-ets-write-concurrency-locks={8,16,32,64,128,256}], + [specify how many locks the write_concurrency option for ets should use.]) + AS_HELP_STRING([--without-ets-write-concurrency-locks], + [use the default number of write_concurrency locks (default)])) + +if test X"$with_ets_write_concurrency_locks" != X""; then + AC_DEFINE_UNQUOTED(ERTS_DB_HASH_LOCK_CNT,$with_ets_write_concurrency_locks, + [Define to override the default number of write_concurrency locks]) +fi + dnl ---------------------------------------------------------------------- dnl Checks for programs. dnl ---------------------------------------------------------------------- diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h index d17bd9f693..908cec11d4 100644 --- a/erts/emulator/beam/erl_db_hash.h +++ b/erts/emulator/beam/erl_db_hash.h @@ -33,7 +33,12 @@ typedef struct hash_db_term { DbTerm dbterm; /* The actual term */ } HashDbTerm; +#ifdef ERTS_DB_HASH_LOCK_CNT +#define DB_HASH_LOCK_CNT ERTS_DB_HASH_LOCK_CNT +#else #define DB_HASH_LOCK_CNT 64 +#endif + typedef struct db_table_hash_fine_locks { union { erts_smp_rwmtx_t lck; -- cgit v1.2.3 From a6788ea337a2319a2d1a42ee4618553a1c7765bf Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 30 Oct 2013 17:56:37 +0100 Subject: ose: Fix various build environment issues --- erts/aclocal.m4 | 9 ++++++++- erts/emulator/Makefile.in | 12 ++++++++++-- erts/emulator/drivers/ose/ose_signal_drv.c | 5 +++++ erts/emulator/sys/ose/erl_main.c | 15 +++++++++++++++ erts/emulator/sys/ose/sys.c | 7 +++++++ erts/epmd/src/Makefile.in | 4 ++-- erts/lib_src/ose/ethread.c | 17 ++++++++++++----- 7 files changed, 59 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 4a3407e0eb..09d0f0194c 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -84,6 +84,8 @@ AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load mo AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)]) AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)]) AC_ARG_VAR(erl_xcomp_ose_LM_CONF, [OSE load module default configuration file (only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE OSE confd source file]) +AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file]) ]) @@ -1106,7 +1108,12 @@ case "$THR_LIB_NAME" in AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads]) ;; ose_threads) - AC_DEFINE(ETHR_OSE_THREADS, 1, [Define if you have OSE style threads]) ETHR_THR_LIB_BASE_DIR=ose + AC_DEFINE(ETHR_OSE_THREADS, 1, + [Define if you have OSE style threads]) + ETHR_THR_LIB_BASE_DIR=ose + AC_CHECK_HEADER(ose_spi/ose_spi.h, + AC_DEFINE(HAVE_OSE_SPI, 1, + [Define if you have the "ose_spi/ose_spi.h" header file.])) ;; esac if test "x$THR_LIB_NAME" == "xpthread"; then diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 9935b911f6..63deae76d5 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -690,6 +690,14 @@ $(OBJDIR)/%.o: $(TTF_DIR)/%.c $(OBJDIR)/%.o: sys/$(ERLANG_OSTYPE)/%.c $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ +ifeq ($(findstring ose,$(TARGET)),ose) +$(OBJDIR)/ose_confd.o: $(OSE_CONFD) + $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +$(OBJDIR)/crt0_lm.o: $(CRT0_LM) + $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ +endif + $(OBJDIR)/%.o: sys/common/%.c $(V_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@ @@ -815,8 +823,8 @@ OS_OBJS = \ $(OBJDIR)/gzio.o \ $(OBJDIR)/elib_memmove.o -OS_OBJS += $(OSEROOT)/src/ose_confd.o \ - $(OSEROOT)/src/crt0_lm.o +OS_OBJS += $(OBJDIR)/ose_confd.o \ + $(OBJDIR)/crt0_lm.o OS_OBJS += $(OBJDIR)/sys_float.o \ $(OBJDIR)/sys_time.o diff --git a/erts/emulator/drivers/ose/ose_signal_drv.c b/erts/emulator/drivers/ose/ose_signal_drv.c index acf09f748e..c1d861cc5a 100644 --- a/erts/emulator/drivers/ose/ose_signal_drv.c +++ b/erts/emulator/drivers/ose/ose_signal_drv.c @@ -28,7 +28,10 @@ #include "sys.h" #include "erl_driver.h" #include "ose.h" + +#ifdef HAVE_OSE_SPI_H #include "ose_spi/ose_spi.h" +#endif #define DEBUG_ATTACH 0 #define DEBUG_HUNT 0 @@ -819,6 +822,7 @@ static ErlDrvSSizeT control(ErlDrvData driver_data, unsigned int cmd, return sizeof(PROCESS); } +#ifdef HAVE_OSE_SPI_H case GET_NAME: { const PROCESS spid = get_u32(buf); @@ -839,6 +843,7 @@ static ErlDrvSSizeT control(ErlDrvData driver_data, unsigned int cmd, return n; } +#endif default: { /* Unknown command */ diff --git a/erts/emulator/sys/ose/erl_main.c b/erts/emulator/sys/ose/erl_main.c index 322058c87b..a17fc7eabc 100644 --- a/erts/emulator/sys/ose/erl_main.c +++ b/erts/emulator/sys/ose/erl_main.c @@ -17,10 +17,25 @@ * %CopyrightEnd% */ +#include + int main(int argc, char **argv) { + /* When starting using pm_create -c ARGV="-- -root ..", argv[0] is the first + part of ARGV and not the name of the executable. So we shuffle some + pointers here to make erl_start happy. */ + if (argv[0][0] == '-') { + int i; + char **tmp_argv = malloc(sizeof(char*)*(argc+1)); + for (i = 0; i < argc; i++) + tmp_argv[i+1] = argv[i]; + tmp_argv = "beam"; + erl_start(argc,tmp_argv); + free(tmp_argv); + } else { erl_start(argc,argv); + } stop(current_process()); diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index a8eb0b93b4..a8a99ceca6 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -852,6 +852,11 @@ OS_PROCESS(fd_reader_process) { } #endif + if (fd == 0) { + FILE *ffd = stdin; + (void)stdin; + } + sigsel[1] = ERTS_SIGNAL_FD_DRV_ASYNC; read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, @@ -909,8 +914,10 @@ OS_PROCESS(fd_writer_process) { /* Why do I need these?!? */ if (fd == 1) { FILE* ffd = stdout; + (void)stdout; } else if (fd == 2) { FILE* ffd = stderr; + (void)stderr; } while (1) { diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in index faf0101ad7..2ea8630491 100644 --- a/erts/epmd/src/Makefile.in +++ b/erts/epmd/src/Makefile.in @@ -135,9 +135,9 @@ clean: rm -f *~ core ifeq ($(findstring ose,$(TARGET)),ose) -$(OBJDIR)/ose_confd.o: $(OSEROOT)/src/ose_confd.c +$(OBJDIR)/ose_confd.o: $(OSE_CONFD) $(V_CC) $(CFLAGS) -o $@ -c $< -$(OBJDIR)/crt0_lm.o: $(OSEROOT)/src/crt0_lm.c +$(OBJDIR)/crt0_lm.o: $(CRT0_LM) $(V_CC) $(CFLAGS) -o $@ -c $< OSE_LM_OBJS += $(OBJDIR)/ose_confd.o $(OBJDIR)/crt0_lm.o endif diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c index 7046cc8d03..01d58e65b2 100644 --- a/erts/lib_src/ose/ethread.c +++ b/erts/lib_src/ose/ethread.c @@ -125,23 +125,30 @@ static OS_PROCESS(thr_wrapper) void *arg; ethr_ts_event *tsep = NULL; +#ifdef DEBUG { PROCESS pid = current_process(); - const char *execMode = get_pid_info(pid, OSE_PI_SUPERVISOR) - ? "Supervisor" - : "User"; + const char *execMode; + PROCESS bid = get_bid(pid); /* In the call below, 16 is a secret number provided by frbr that makes * the function return current domain. */ OSADDRESS domain = get_pid_info(current_process(), 16); -#ifdef DEBUG +#ifdef HAVE_OSE_SPI_H + execMode = get_pid_info(pid, OSE_PI_SUPERVISOR) + ? "Supervisor" + : "User"; +#else + execMode = "unknown"; +#endif + fprintf(stderr,"[0x%x] New process. Bid:0x%x, domain:%d, exec mode:%s\n", current_process(), bid, domain, execMode); -#endif } +#endif { SIGSELECT sigsel[] = {1,ETHREADWRAPDATASIG}; -- cgit v1.2.3 From fd3aa9d63649084b345d0977fa121805f13c9d33 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 31 Oct 2013 11:29:25 +0100 Subject: ose: Style updates only --- erts/emulator/sys/ose/sys.c | 145 +++++++++++++++----------------------------- 1 file changed, 50 insertions(+), 95 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index a8a99ceca6..6e55301d6e 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -26,13 +26,8 @@ #define _XOPEN_SOURCE #endif -#include /* ose*/ +#include #include -/* ose -#include -#include -#include -*/ #include #include @@ -51,32 +46,19 @@ #endif #define ERTS_WANT_BREAK_HANDLING -#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */ +#define WANT_NONBLOCKING #include "sys.h" #include "erl_thr_progress.h" -#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) -#define __DARWIN__ 1 -#endif - #ifdef USE_THREADS #include "erl_threads.h" #endif #include "erl_mseg.h" -/*ose*/ #include "unistd.h" #include "efs.h" #include "erl_printf.h" -#if 0 -#define TRACE do { \ - erts_fprintf(stderr, " %s / %d / pid 0x%x\n", __FUNCTION__, __LINE__, current_process()); \ - } while(0) -#else -#define TRACE do {} while(0) -#endif -/*ose*/ extern char **environ; static erts_smp_rwmtx_t environ_rwmtx; @@ -95,28 +77,9 @@ static erts_smp_rwmtx_t environ_rwmtx; #include "erl_check_io.h" #include "erl_cpu_topology.h" -#ifndef DISABLE_VFORK -#define DISABLE_VFORK 0 -#endif - /* The priority for reader/writer processes */ #define FD_PROC_PRI 20 -/* - * [OTP-3906] - * Solaris signal management gets confused when threads are used and a - * lot of child processes dies. The confusion results in that SIGCHLD - * signals aren't delivered to the emulator which in turn results in - * a lot of defunct processes in the system. - * - * The problem seems to appear when a signal is frequently - * blocked/unblocked at the same time as the signal is frequently - * propagated. The child waiter thread is a workaround for this problem. - * The SIGCHLD signal is always blocked (in all threads), and the child - * waiter thread fetches the signal by a call to sigwait(). See - * child_waiter(). - */ - typedef struct ErtsSysReportExit_ ErtsSysReportExit; struct ErtsSysReportExit_ { ErtsSysReportExit *next; @@ -516,8 +479,8 @@ int sys_max_files(void) char os_type[] = "ose"; void -os_flavor(char* namebuf, /* Where to return the name. */ - unsigned size) /* Size of name buffer. */ +os_flavor(char* namebuf, /* Where to return the name. */ + unsigned size) /* Size of name buffer. */ { #if 0 struct utsname uts; /* Information about the system. */ @@ -694,7 +657,8 @@ static int set_driver_data(ErlDrvPort port_num, ErtsSysReportExit *report_exit; OseSignal *sig; - /*erts_fprintf(stderr, " %s / pid %x / ofd %d / ifd %d\n", __FUNCTION__, current_process(), ofd, ifd);*/ + /*erts_fprintf(stderr, " %s / pid %x / ofd %d / ifd %d\n", + __FUNCTION__, current_process(), ofd, ifd);*/ if (!exit_status) @@ -709,9 +673,11 @@ static int set_driver_data(ErlDrvPort port_num, report_exit->ofd = read_write & DO_WRITE ? ofd : -1; if (read_write & DO_READ) - report_exit->in_sig_descr = erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ifd); + report_exit->in_sig_descr = + erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ifd); if (read_write & DO_WRITE) - report_exit->out_sig_descr = erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ofd); + report_exit->out_sig_descr = + erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ofd); report_exit_list = report_exit; } @@ -727,11 +693,13 @@ static int set_driver_data(ErlDrvPort port_num, driver_data[ifd].pid = pid; driver_data[ifd].alive = 1; driver_data[ifd].status = 0; - driver_data[ifd].in_sig_descr = erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ifd); + driver_data[ifd].in_sig_descr = + erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ifd); driver_data[ifd].in_proc = create_process(OS_PRI_PROC,"beam_fd_reader", fd_reader_process, 0x800, - FD_PROC_PRI, 0, 0, NULL, 0, 0); + FD_PROC_PRI, 0, 0, + NULL, 0, 0); efs_clone(driver_data[ifd].in_proc); sig = alloc(sizeof(SysDriverConfSignal), ERTS_SIGNAL_FD_DRV_CONFIG); sig->conf_async.fd = ifd; @@ -744,9 +712,10 @@ static int set_driver_data(ErlDrvPort port_num, driver_data[ifd].out_sig_descr = erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ofd); driver_data[ifd].pdl = driver_pdl_create(port_num); - driver_data[ifd].out_proc = create_process(OS_PRI_PROC, "beam_fd_writer", - fd_writer_process, 0x800, - FD_PROC_PRI, 0, 0, NULL, 0, 0); + driver_data[ifd].out_proc = + create_process(OS_PRI_PROC,"beam_fd_writer", + fd_writer_process, 0x800, + FD_PROC_PRI, 0, 0, NULL, 0, 0); sig = alloc(sizeof(SysDriverConfSignal), ERTS_SIGNAL_FD_DRV_CONFIG); sig->conf_async.fd = ofd; sig->conf_async.parent = current_process(); @@ -758,7 +727,8 @@ static int set_driver_data(ErlDrvPort port_num, } else { /* DO_READ only */ driver_data[ifd].ofd = -1; } - (void) driver_select(port_num, driver_data[ifd].in_sig_descr, (ERL_DRV_READ | ERL_DRV_USE), 1); + (void) driver_select(port_num, driver_data[ifd].in_sig_descr, + (ERL_DRV_READ | ERL_DRV_USE), 1); return(ifd); } else { /* DO_WRITE only */ driver_data[ofd].packet_bytes = packet_bytes; @@ -768,12 +738,13 @@ static int set_driver_data(ErlDrvPort port_num, driver_data[ofd].pid = pid; driver_data[ofd].alive = 1; driver_data[ofd].status = 0; - driver_data[ofd].in_sig_descr = erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, - ofd); + driver_data[ofd].in_sig_descr = + erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ofd); driver_data[ofd].out_sig_descr = driver_data[ofd].in_sig_descr; - driver_data[ofd].out_proc = create_process(OS_PRI_PROC, "beam_fd_writer", - fd_writer_process, 0x800, - FD_PROC_PRI, 0, 0, NULL, 0, 0); + driver_data[ofd].out_proc = + create_process(OS_PRI_PROC, "beam_fd_writer", + fd_writer_process, 0x800, + FD_PROC_PRI, 0, 0, NULL, 0, 0); sig = alloc(sizeof(SysDriverConfSignal), ERTS_SIGNAL_FD_DRV_CONFIG); sig->conf_async.fd = ofd; sig->conf_async.parent = current_process(); @@ -788,11 +759,11 @@ static int set_driver_data(ErlDrvPort port_num, static int spawn_init() { int i; - TRACE; driver_data = (struct driver_data *) erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data)); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, max_files * sizeof(struct driver_data)); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, + max_files * sizeof(struct driver_data)); for (i = 0; i < max_files; i++) driver_data[i].pid = -1; @@ -802,7 +773,6 @@ static int spawn_init() static void init_fd_data(int fd, ErlDrvPort port_num) { - TRACE; fd_data[fd].buf = NULL; fd_data[fd].cpos = NULL; @@ -811,12 +781,13 @@ static void init_fd_data(int fd, ErlDrvPort port_num) fd_data[fd].psz = 0; } -static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) +static ErlDrvData spawn_start(ErlDrvPort port_num, + char* name, + SysDriverOpts* opts) { long res = 0; - TRACE; /* Have to implement for OSE */ return (ErlDrvData)res; } @@ -833,12 +804,8 @@ OS_PROCESS(fd_reader_process) { erts_lcnt_init(); #endif - TRACE; - sig = receive(sigsel); - TRACE; - fd = sig->conf_async.fd; parent = sig->conf_async.parent; @@ -890,14 +857,12 @@ OS_PROCESS(fd_writer_process) { OseSignal *sig; PROCESS parent; int fd; - SIGSELECT sigsel[] = { 1, ERTS_SIGNAL_FD_DRV_CONFIG, ERTS_SIGNAL_FD_DRV_ASYNC }; + SIGSELECT sigsel[] = { 1, ERTS_SIGNAL_FD_DRV_CONFIG, + ERTS_SIGNAL_FD_DRV_ASYNC }; - TRACE; /* Only wait for config event with the fd which we are printing to */ sig = receive(sigsel); - TRACE; - fd = sig->conf_async.fd; parent = sig->conf_async.parent; free_buf(&sig); @@ -944,12 +909,13 @@ OS_PROCESS(fd_writer_process) { ; iov = driver_alloc(sizeof(SysIOVec) * iovcnt); memcpy(iov, iov0, iovcnt * sizeof(SysIOVec)); - driver_pdl_unlock(driver_data[fd].pdl); /* Let go of lock until we deque from original vector */ + driver_pdl_unlock(driver_data[fd].pdl); if (iovlen > 0) { for (i = 0; i < iovcnt; i++) { - res = write(fd, iov[i].iov_base, iov[i].iov_len > 256 ? 256 : iov[i].iov_len); + res = write(fd, iov[i].iov_base, + iov[i].iov_len > 256 ? 256 : iov[i].iov_len); if (res < 0) break; n += res; @@ -1024,8 +990,6 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name, { ErlDrvData res; - TRACE; - CHLD_STAT_LOCK; if (opts->read_write & DO_READ) { init_fd_data(opts->ifd, port_num); @@ -1042,8 +1006,6 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name, static void clear_fd_data(int fd) { - TRACE; - if (fd_data[fd].sz > 0) { erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf); ASSERT(erts_smp_atomic_read_nob(&sys_misc_mem_sz) >= fd_data[fd].sz); @@ -1059,7 +1021,6 @@ static void clear_fd_data(int fd) static void nbio_stop_fd(ErlDrvPort prt, ErlDrvEvent ev) { int fd; - TRACE; driver_select(prt,ev,DO_READ|DO_WRITE,0); erl_drv_ose_event_fetch(ev, NULL, &fd); clear_fd_data(fd); @@ -1070,12 +1031,12 @@ static void fd_stop(ErlDrvData fd) /* Does not close the fds */ { int ofd; - TRACE; - - nbio_stop_fd(driver_data[(int)(long)fd].port_num, driver_data[(int)(long)fd].in_sig_descr); + nbio_stop_fd(driver_data[(int)(long)fd].port_num, + driver_data[(int)(long)fd].in_sig_descr); ofd = driver_data[(int)(long)fd].ofd; if (ofd != (int)(long)fd && ofd != -1) - nbio_stop_fd(driver_data[(int)(long)fd].port_num, driver_data[(int)(long)fd].out_sig_descr); + nbio_stop_fd(driver_data[(int)(long)fd].port_num, + driver_data[(int)(long)fd].out_sig_descr); } /* Note that driver_data[fd].ifd == fd if the port was opened for reading, */ @@ -1086,8 +1047,6 @@ static void erl_stop(ErlDrvData fd) ErlDrvPort prt; int ofd; - TRACE; - prt = driver_data[(int)(long)fd].port_num; nbio_stop_fd(prt, driver_data[(int)(long)fd].in_sig_descr); @@ -1106,9 +1065,11 @@ static void erl_stop(ErlDrvData fd) /* SMP note: Close has to be last thing done (open file descriptors work as locks on driver_data[] entries) */ - driver_select(prt, driver_data[(int)(long)fd].in_sig_descr, ERL_DRV_USE, 0); /* close(fd); */ + driver_select(prt, driver_data[(int)(long)fd].in_sig_descr, + ERL_DRV_USE, 0); /* close(fd); */ if (ofd >= 0) { - driver_select(prt, driver_data[(int)(long)fd].out_sig_descr, ERL_DRV_USE, 0); /* close(ofd); */ + driver_select(prt, driver_data[(int)(long)fd].out_sig_descr, + ERL_DRV_USE, 0); /* close(ofd); */ } } @@ -1122,8 +1083,6 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) char* lbp; ErlDrvSizeT len = ev->size; - TRACE; - /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ /* if (pb >= 0 && (len & (((ErlDrvSizeT)1 << (pb*8))) - 1) != len) {*/ if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) { @@ -1150,7 +1109,8 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) /* fprintf(stderr,"0x%x: outputv, enq+sel\n", current_process()); */ driver_enqv(ix, ev, 0); /* n is the skip value */ driver_pdl_unlock(driver_data[fd].pdl); - driver_select(ix, driver_data[fd].out_sig_descr, ERL_DRV_WRITE|ERL_DRV_USE, 1); + driver_select(ix, driver_data[fd].out_sig_descr, + ERL_DRV_WRITE|ERL_DRV_USE, 1); sig = alloc(sizeof(SysDriverAsyncSignal),ERTS_SIGNAL_FD_DRV_ASYNC); sig->sys_async.type = fd; sig->sys_async.res = pb+len; @@ -1173,8 +1133,6 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) struct iovec iv[2]; #endif - TRACE; - /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) { driver_failure_posix(ix, EINVAL); @@ -1204,7 +1162,8 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) driver_enq(ix, lbp, pb); driver_enq(ix, buf, len); driver_pdl_unlock(driver_data[fd].pdl); - driver_select(ix, driver_data[ofd].out_sig_descr, ERL_DRV_WRITE|ERL_DRV_USE, 1); + driver_select(ix, driver_data[ofd].out_sig_descr, + ERL_DRV_WRITE|ERL_DRV_USE, 1); sig = alloc(sizeof(SysDriverAsyncSignal),ERTS_SIGNAL_FD_DRV_ASYNC); sig->sys_async.type = fd; sig->sys_async.res = pb+len; @@ -1282,8 +1241,6 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) int res; Uint h; - TRACE; - port_num = driver_data[fd].port_num; packet_bytes = driver_data[fd].packet_bytes; @@ -1331,7 +1288,7 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) if ((errno != EINTR) && (errno != ERRNO_BLOCK)) port_inp_failure(port_num, ready_fd, res); } - else if (res == 0) { /* eof */ + else if (res == 0) { /* eof */ port_inp_failure(port_num, ready_fd, res); } else if (res < packet_bytes - fd_data[fd].psz) { @@ -1373,7 +1330,7 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) continue; } else { /* The last message we got was split */ - char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); + char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); if (!buf) { errno = ENOMEM; port_inp_failure(port_num, ready_fd, -1); @@ -1676,8 +1633,6 @@ int fd; int c; unsigned char rbuf[64]; - TRACE; - fflush(stdout); /* Flush query ??? */ if ((c = read(fd,rbuf,64)) <= 0) { -- cgit v1.2.3 From 8103fed4090e9f1457fb1705e9a4df0821f5db9b Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 31 Oct 2013 14:40:33 +0100 Subject: ose,erts: Specify name for tsd keys This simplified debugging on OSE and also limits the number of ppdata keys that are created when beam is restarted. --- erts/emulator/beam/code_ix.c | 3 ++- erts/emulator/beam/erl_alloc.c | 3 ++- erts/emulator/beam/erl_db_util.c | 3 ++- erts/emulator/beam/erl_drv_thread.c | 2 +- erts/emulator/beam/erl_init.c | 2 +- erts/emulator/beam/erl_lock_check.c | 2 +- erts/emulator/beam/erl_lock_count.c | 2 +- erts/emulator/beam/erl_process.c | 2 +- erts/emulator/beam/erl_smp.h | 7 ++++--- erts/emulator/beam/erl_thr_progress.c | 3 ++- erts/emulator/beam/erl_threads.h | 6 +++--- erts/emulator/beam/io.c | 6 ++++-- erts/emulator/sys/unix/sys_float.c | 2 +- erts/emulator/sys/win32/sys.c | 2 +- erts/include/internal/ethread.h | 2 +- erts/lib_src/ose/ethread.c | 9 +-------- erts/lib_src/pthread/ethread.c | 2 +- erts/lib_src/win/ethread.c | 2 +- erts/test/ethread_SUITE_data/ethread_tests.c | 2 +- 19 files changed, 31 insertions(+), 31 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c index c66d5a2f05..4344558348 100644 --- a/erts/emulator/beam/code_ix.c +++ b/erts/emulator/beam/code_ix.c @@ -58,7 +58,8 @@ void erts_code_ix_init(void) erts_smp_atomic32_init_nob(&the_staging_code_index, 0); erts_smp_mtx_init(&code_write_permission_mtx, "code_write_permission"); #ifdef ERTS_ENABLE_LOCK_CHECK - erts_tsd_key_create(&has_code_write_permission); + erts_tsd_key_create(&has_code_write_permission, + "erts_has_code_write_permission"); #endif CIX_TRACE("init"); } diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index ca7e39041b..c2bca9e54f 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -585,7 +585,8 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) if (ncpu < 1) ncpu = 1; - erts_tsd_key_create(&erts_allctr_prelock_tsd_key); + erts_tsd_key_create(&erts_allctr_prelock_tsd_key, + "erts_allctr_prelock_tsd_key"); erts_sys_alloc_init(); erts_init_utils_mem(); diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 3986ccd4d3..3927615e04 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -483,7 +483,8 @@ void match_pseudo_process_init(void) { #ifdef ERTS_SMP - erts_smp_tsd_key_create(&match_pseudo_process_key); + erts_smp_tsd_key_create(&match_pseudo_process_key, + "erts_match_pseudo_process_key"); erts_smp_install_exit_handler(destroy_match_pseudo_process); #else match_pseudo_process = create_match_pseudo_process(); diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index beaeed03a2..147249f751 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -121,7 +121,7 @@ void erl_drv_thr_init(void) { int i; #ifdef USE_THREADS - int res = ethr_tsd_key_create(&tid_key); + int res = ethr_tsd_key_create(&tid_key,"erts_tid_key"); if (res == 0) res = ethr_install_exit_handler(thread_exit_handler); if (res != 0) diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index e2227e8de4..6b190326aa 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -709,7 +709,7 @@ early_init(int *argc, char **argv) /* #endif #ifdef ERTS_SMP erts_smp_atomic32_init_nob(&erts_writing_erl_crash_dump, 0L); - erts_tsd_key_create(&erts_is_crash_dumping_key); + erts_tsd_key_create(&erts_is_crash_dumping_key,"erts_is_crash_dumping_key"); #else erts_writing_erl_crash_dump = 0; #endif diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 706d284a00..7e3a90779d 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -1332,7 +1332,7 @@ erts_lc_init(void) if (ethr_spinlock_init(&free_blocks_lock) != 0) lc_abort(); - erts_tsd_key_create(&locks_key); + erts_tsd_key_create(&locks_key,"erts_lock_check_key"); } void diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index 5f75b0ac0b..6f44bf097b 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -236,7 +236,7 @@ void erts_lcnt_init() { /* init tsd */ lcnt_n_thr = 0; - ethr_tsd_key_create(&lcnt_thr_data_key); + ethr_tsd_key_create(&lcnt_thr_data_key,"lcnt_data"); lcnt_lock(); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 7e5d88cb24..929dbfd16b 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -512,7 +512,7 @@ void erts_pre_init_process(void) { #ifdef USE_THREADS - erts_tsd_key_create(&sched_data_key); + erts_tsd_key_create(&sched_data_key, "erts_sched_data_key"); #endif #ifdef ERTS_ENABLE_LOCK_CHECK diff --git a/erts/emulator/beam/erl_smp.h b/erts/emulator/beam/erl_smp.h index 971d68be75..c38ef47d87 100644 --- a/erts/emulator/beam/erl_smp.h +++ b/erts/emulator/beam/erl_smp.h @@ -208,7 +208,8 @@ ERTS_GLB_INLINE void erts_smp_write_lock(erts_smp_rwlock_t *lock); ERTS_GLB_INLINE void erts_smp_write_unlock(erts_smp_rwlock_t *lock); ERTS_GLB_INLINE int erts_smp_lc_rwlock_is_rlocked(erts_smp_rwlock_t *lock); ERTS_GLB_INLINE int erts_smp_lc_rwlock_is_rwlocked(erts_smp_rwlock_t *lock); -ERTS_GLB_INLINE void erts_smp_tsd_key_create(erts_smp_tsd_key_t *keyp); +ERTS_GLB_INLINE void erts_smp_tsd_key_create(erts_smp_tsd_key_t *keyp, + char *keyname); ERTS_GLB_INLINE void erts_smp_tsd_key_delete(erts_smp_tsd_key_t key); ERTS_GLB_INLINE void erts_smp_tsd_set(erts_smp_tsd_key_t key, void *value); ERTS_GLB_INLINE void * erts_smp_tsd_get(erts_smp_tsd_key_t key); @@ -1323,10 +1324,10 @@ erts_smp_lc_rwlock_is_rwlocked(erts_smp_rwlock_t *lock) } ERTS_GLB_INLINE void -erts_smp_tsd_key_create(erts_smp_tsd_key_t *keyp) +erts_smp_tsd_key_create(erts_smp_tsd_key_t *keyp, char* keyname) { #ifdef ERTS_SMP - erts_tsd_key_create(keyp); + erts_tsd_key_create(keyp,keyname); #endif } diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index cf5e3dc012..545a0343d0 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -417,7 +417,8 @@ void erts_thr_progress_pre_init(void) { intrnl = NULL; - erts_tsd_key_create(&erts_thr_prgr_data_key__); + erts_tsd_key_create(&erts_thr_prgr_data_key__, + "erts_thr_prgr_data_key"); init_nob(&erts_thr_prgr__.current, ERTS_THR_PRGR_VAL_FIRST); } diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index b2f68462a6..80026104db 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -605,7 +605,7 @@ ERTS_GLB_INLINE void erts_write_lock(erts_rwlock_t *lock); ERTS_GLB_INLINE void erts_write_unlock(erts_rwlock_t *lock); ERTS_GLB_INLINE int erts_lc_rwlock_is_rlocked(erts_rwlock_t *lock); ERTS_GLB_INLINE int erts_lc_rwlock_is_rwlocked(erts_rwlock_t *lock); -ERTS_GLB_INLINE void erts_tsd_key_create(erts_tsd_key_t *keyp); +ERTS_GLB_INLINE void erts_tsd_key_create(erts_tsd_key_t *keyp, char *keyname); ERTS_GLB_INLINE void erts_tsd_key_delete(erts_tsd_key_t key); ERTS_GLB_INLINE void erts_tsd_set(erts_tsd_key_t key, void *value); ERTS_GLB_INLINE void * erts_tsd_get(erts_tsd_key_t key); @@ -2707,10 +2707,10 @@ erts_lc_rwlock_is_rwlocked(erts_rwlock_t *lock) } ERTS_GLB_INLINE void -erts_tsd_key_create(erts_tsd_key_t *keyp) +erts_tsd_key_create(erts_tsd_key_t *keyp, char *keyname) { #ifdef USE_THREADS - int res = ethr_tsd_key_create(keyp); + int res = ethr_tsd_key_create(keyp, keyname); if (res) erts_thr_fatal_error(res, "create thread specific data key"); #endif diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index c7c6e5a5d7..09681f167d 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -2747,8 +2747,10 @@ void erts_init_io(int port_tab_size, &drv_list_rwmtx_opts, "driver_list"); driver_list = NULL; - erts_smp_tsd_key_create(&driver_list_lock_status_key); - erts_smp_tsd_key_create(&driver_list_last_error_key); + erts_smp_tsd_key_create(&driver_list_lock_status_key, + "erts_driver_list_lock_status_key"); + erts_smp_tsd_key_create(&driver_list_last_error_key, + "erts_driver_list_last_error_key"); erts_ptab_init_table(&erts_port, ERTS_ALC_T_PORT_TABLE, diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c index 689be98969..cafeab547e 100644 --- a/erts/emulator/sys/unix/sys_float.c +++ b/erts/emulator/sys/unix/sys_float.c @@ -46,7 +46,7 @@ static void erts_init_fp_exception(void) { /* XXX: the wrappers prevent using a pthread destructor to deallocate the key's value; so when/where do we do that? */ - erts_tsd_key_create(&fpe_key); + erts_tsd_key_create(&fpe_key,"fp_exception"); } void erts_thread_init_fp_exception(void) diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 5ea4703a7a..296da90c6c 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -3197,7 +3197,7 @@ void erl_sys_init(void) noinherit_std_handle(STD_ERROR_HANDLE); #ifdef ERTS_SMP - erts_smp_tsd_key_create(&win32_errstr_key); + erts_smp_tsd_key_create(&win32_errstr_key,"win32_errstr_key"); InitializeCriticalSection(&htbc_lock); #endif erts_smp_atomic_init_nob(&pipe_creation_counter,0); diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 7f3786ec63..7307bde8d9 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -534,7 +534,7 @@ void ethr_thr_exit(void *); ethr_tid ethr_self(void); int ethr_equal_tids(ethr_tid, ethr_tid); -int ethr_tsd_key_create(ethr_tsd_key *); +int ethr_tsd_key_create(ethr_tsd_key *,char *); int ethr_tsd_key_delete(ethr_tsd_key); int ethr_tsd_set(ethr_tsd_key, void *); void *ethr_tsd_get(ethr_tsd_key); diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c index 01d58e65b2..41c3392677 100644 --- a/erts/lib_src/ose/ethread.c +++ b/erts/lib_src/ose/ethread.c @@ -527,10 +527,8 @@ ethr_leave_ts_event(ethr_ts_event *tsep) */ int -ethr_tsd_key_create(ethr_tsd_key *keyp) +ethr_tsd_key_create(ethr_tsd_key *keyp, char *keyname) { - ethr_tid *tid = ETHR_GET_OWN_TID__; - char keyname[31]; #if ETHR_XCHK if (ethr_not_inited__) { @@ -542,11 +540,6 @@ ethr_tsd_key_create(ethr_tsd_key *keyp) return EINVAL; } #endif - if (tid->tsd_key_index > 999) - return EAGAIN; - - /* What do we do it tds_key_index happens to wrap? Slot search? */ - sprintf(keyname,"ethread_tsd_key_0x%x_%d",tid->id,tid->tsd_key_index++); ose_create_ppdata(keyname,keyp); diff --git a/erts/lib_src/pthread/ethread.c b/erts/lib_src/pthread/ethread.c index fb7d135418..7f27b5f29c 100644 --- a/erts/lib_src/pthread/ethread.c +++ b/erts/lib_src/pthread/ethread.c @@ -472,7 +472,7 @@ ethr_leave_ts_event(ethr_ts_event *tsep) */ int -ethr_tsd_key_create(ethr_tsd_key *keyp) +ethr_tsd_key_create(ethr_tsd_key *keyp, char *keyname) { #if ETHR_XCHK if (ethr_not_inited__) { diff --git a/erts/lib_src/win/ethread.c b/erts/lib_src/win/ethread.c index 3abda6de4c..14d0b6deff 100644 --- a/erts/lib_src/win/ethread.c +++ b/erts/lib_src/win/ethread.c @@ -520,7 +520,7 @@ ethr_equal_tids(ethr_tid tid1, ethr_tid tid2) */ int -ethr_tsd_key_create(ethr_tsd_key *keyp) +ethr_tsd_key_create(ethr_tsd_key *keyp, char *keyname) { DWORD key; #if ETHR_XCHK diff --git a/erts/test/ethread_SUITE_data/ethread_tests.c b/erts/test/ethread_SUITE_data/ethread_tests.c index ed96ecdbd2..1d8083ef1f 100644 --- a/erts/test/ethread_SUITE_data/ethread_tests.c +++ b/erts/test/ethread_SUITE_data/ethread_tests.c @@ -976,7 +976,7 @@ tsd_test(void) ethr_tid tid[TT_THREADS]; int values[TT_THREADS]; - res = ethr_tsd_key_create(&tt_key); + res = ethr_tsd_key_create(&tt_key,"tsd_test"); ASSERT(res == 0); for (i = 1; i < TT_THREADS; i++) { -- cgit v1.2.3 From 53863fdecda434076711708ba22ffe54a63c90c1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 1 Nov 2013 17:11:22 +0100 Subject: ose: Fix clearmake compat issue --- erts/emulator/Makefile.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 63deae76d5..3c874f6142 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -874,7 +874,9 @@ endif ifeq ($(COMPILE_STATIC_LIBS),yes) $(STATIC_NIF_LIBS) $(STATIC_DRIVER_LIBS): - $(MAKE) BUILD_STATIC_LIBS=1 TYPE=$(TYPE) -C $(ERL_TOP)/lib/ static_lib + echo "=== Entering lib to make static libs" + (cd $(ERL_TOP)/lib/ && $(MAKE) BUILD_STATIC_LIBS=1 TYPE=$(TYPE) static_lib) + echo "=== Leaving lib after making static libs" endif ifeq ($(ERTS_ENABLE_KERNEL_POLL),yes) -- cgit v1.2.3 From cbfff8e7148a51a76f31effc6b2486f4bda927f6 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 1 Nov 2013 18:00:11 +0100 Subject: ose: Handle lseek() with offset beyond EOF in the file driver. --- erts/emulator/drivers/ose/ose_efile.c | 316 +++++++++++++++++++++++++++++++--- 1 file changed, 288 insertions(+), 28 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/ose/ose_efile.c b/erts/emulator/drivers/ose/ose_efile.c index fc255e11ef..897d654e89 100644 --- a/erts/emulator/drivers/ose/ose_efile.c +++ b/erts/emulator/drivers/ose/ose_efile.c @@ -62,11 +62,13 @@ #endif #ifdef __OSE__ +#include "ose.h" #include "unistd.h" #include "sys/stat.h" #include "dirent.h" #include "sys/time.h" #include "time.h" +#include "heapapi.h" #endif /* Find a definition of MAXIOV, that is used in the code later. */ @@ -99,8 +101,142 @@ #define IS_DOT_OR_DOTDOT(s) \ (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0'))) + +/* + * Local file descriptor handling, this is needed for filesystems + * that does not handle seeking outside EOF. + */ + +#define L_FD_EXISTS(fd) \ + ((fdm->is_init == 1) && fdm->fd_array[(fd)] != NULL) + +#define L_FD_INVALIDATE(fd) \ + fdm->fd_array[(fd)]->valid = 0 + +#define L_FD_IS_VALID(fd) \ + (fdm->fd_array[(fd)]->valid == 1) + +#define L_FD_OFFSET_BEYOND_EOF(fd, offset) \ + (fdm->fd_array[(fd)]->size < offset) + +#define L_FD_CUR(fd) \ + (fdm->fd_array[(fd)]->pos) + + +struct fd_meta +{ + int is_init; + uint32_t fd_count; + struct fd_data *fd_array[256]; +}; + +struct fd_data +{ + int pos; + int whence; + int valid; + size_t size; +}; + +static struct fd_meta *fdm; +static int l_init_local_fd(void); +static int l_pad_file(int fd, off_t offset); +static int l_update_local_fd(int fd, off_t offset, int whence); + static int check_error(int result, Efile_error* errInfo); +static int +l_init_local_fd() +{ + fdm = heap_alloc_private(sizeof(struct fd_meta), __FILE__, __LINE__); + fdm->fd_count = 0; + fdm->is_init = 1; + + memset(fdm->fd_array, NULL, sizeof(fdm->fd_array)); + return 1; +} + +static int +l_update_local_fd(int fd, off_t offset, int whence) +{ + errno = 0; + + if (fdm->fd_array[fd] == NULL) + { + fdm->fd_array[fd] = heap_alloc_private(sizeof(struct fd_data), __FILE__, __LINE__); + fdm->fd_count++; + } + + switch (whence) + { + case SEEK_CUR: + fdm->fd_array[fd]->pos = lseek(fd, 0, SEEK_CUR) + offset; + break; + + case SEEK_END: + fdm->fd_array[fd]->pos = lseek(fd, 0, SEEK_END) + offset; + break; + + case SEEK_SET: + fdm->fd_array[fd]->pos = offset; + + default: + errno = ENOSYS; + break; + } + fdm->fd_array[fd]->size = lseek(fd, 0, SEEK_END); + + fdm->fd_array[fd]->whence = whence; + fdm->fd_array[fd]->valid = 1; + + return ((errno != 0) ? -1 : 1); +} + +static int +l_pad_file(int fd, off_t offset) +{ + int size_dif; + char *pad; + char *cursor; + int file_size; + int written = 0; + + if (!L_FD_IS_VALID(fd)) + { + l_update_local_fd(fd, offset, SEEK_END); + } + + if (!L_FD_OFFSET_BEYOND_EOF(fd, offset)) + { + + return -1; + } + + + file_size = lseek(fd, 0, SEEK_END); + size_dif = offset - file_size; + pad = heap_alloc_private(size_dif, __FILE__, __LINE__); + cursor = pad; + memset(pad, '\0', size_dif); + + while (size_dif > 0) + { + written = write(fd, cursor, size_dif); + + if (written < 0) + { + return -1; + } + cursor += written; + size_dif -= written; + } + + L_FD_INVALIDATE(fd); + heap_free_private(pad); + + return ((errno != 0) ? -1 : 1); +} + static int check_error(int result, Efile_error *errInfo) { @@ -111,6 +247,10 @@ check_error(int result, Efile_error *errInfo) return 1; } +/* + * public API + */ + int efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to create. */ @@ -155,6 +295,15 @@ int efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of file to delete. */ { + struct stat statbuf; + + /* OSE will accept removal of directories with unlink() */ + if (stat(name, &statbuf) >= 0 && ISDIR(statbuf)) + { + errno = EPERM; + return check_error(-1, errInfo); + } + if (unlink(name) == 0) { return 1; } @@ -202,7 +351,22 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */ char* src, /* Original name. */ char* dst) /* New name. */ { - if (rename(src, dst) == 0) { + + /* temporary fix AFM does not recognize ./ + * in destination */ + + char *dot_str; + if (dst != NULL) + { + dot_str = strchr(dst, '.'); + + if (dot_str && dot_str == dst && dot_str[1] == '/') + { + dst = dst+2; + } + } + + if (rename(src, dst) == 0) { return 1; } if (errno == ENOTEMPTY) { @@ -445,7 +609,12 @@ efile_may_openfile(Efile_error* errInfo, char *name) { void efile_closefile(int fd) { - close(fd); + if (L_FD_EXISTS(fd)) + { + free(fdm->fd_array[fd]); + fdm->fd_array[fd] = NULL; + } + close(fd); } int @@ -546,6 +715,10 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, int efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) { +/* FIXME, this will be dificult to get to work right with the tests on ose + * reason being that the info values are sometimes used in different ways, and in + * some times not used at all. */ + #ifndef __OSE__ struct utimbuf tval; #endif @@ -593,16 +766,40 @@ efile_write(Efile_error* errInfo, /* Where to return error codes. */ { ssize_t written; /* Bytes written in last operation. */ - while (count > 0) { - if ((written = write(fd, buf, count)) < 0) { - if (errno != EINTR) - return check_error(-1, errInfo); - else - written = 0; - } - ASSERT(written <= count); - buf += written; - count -= written; + if (L_FD_EXISTS(fd)) + { + off_t off; + off = (L_FD_IS_VALID(fd)) ? L_FD_CUR(fd) : lseek(fd, 0, SEEK_CUR); + + if (L_FD_OFFSET_BEYOND_EOF(fd, off)) + { + l_pad_file(fd, off); + } + + L_FD_INVALIDATE(fd); + } + + while (count > 0) + { + if ((written = write(fd, buf, count)) < 0) + { + if (errno != EINTR) + { + return check_error(-1, errInfo); + } + else + { + written = 0; + } + } + ASSERT(written <= count); + buf += written; + count -= written; + } + + if (L_FD_EXISTS(fd)) + { + L_FD_INVALIDATE(fd); } return 1; } @@ -676,12 +873,38 @@ efile_read(Efile_error* errInfo, /* Where to return error codes. */ bytes read. */ { ssize_t n; + if (L_FD_EXISTS(fd)) + { + off_t off; + off = (L_FD_IS_VALID(fd)) ? L_FD_CUR(fd) : lseek(fd, 0, SEEK_CUR); + if (L_FD_IS_VALID(fd) == 0) + { + l_update_local_fd(fd, off, SEEK_SET); + } - for (;;) { - if ((n = read(fd, buf, count)) >= 0) - break; - else if (errno != EINTR) - return check_error(-1, errInfo); + if (L_FD_OFFSET_BEYOND_EOF(fd, off)) + { + *pBytesRead = 0; + return 1; + } + /* FIXME .. is this needed? */ + lseek(fd, off, SEEK_SET); + } + + for (;;) + { + if ((n = read(fd, buf, count)) >= 0) + { + break; + } + else if (errno != EINTR) + { + return check_error(-1, errInfo); + } + } + if (n != 0 && L_FD_EXISTS(fd)) + { + L_FD_INVALIDATE(fd); } *pBytesRead = (size_t) n; return 1; @@ -789,6 +1012,7 @@ efile_seek(Efile_error* errInfo, /* Where to return error codes. */ Sint64 *new_location) /* Resulting new location in file. */ { off_t off, result; + off = (off_t) offset; switch (origin) { case EFILE_SEEK_SET: origin = SEEK_SET; break; @@ -798,15 +1022,19 @@ efile_seek(Efile_error* errInfo, /* Where to return error codes. */ errno = EINVAL; return check_error(-1, errInfo); } - off = (off_t) offset; if (off != offset) { - errno = EINVAL; - return check_error(-1, errInfo); + errno = EINVAL; + return check_error(-1, errInfo); } errno = 0; result = lseek(fd, off, origin); + if (result >= 0 && L_FD_EXISTS(fd)) + { + L_FD_INVALIDATE(fd); + } + /* * Note that the man page for lseek (on SunOs 5) says: * @@ -815,14 +1043,40 @@ efile_seek(Efile_error* errInfo, /* Where to return error codes. */ * negative." */ - if (result < 0 && errno == 0) - errno = EINVAL; - if (result < 0) - return check_error(-1, errInfo); - if (new_location) { - *new_location = result; - } - return 1; + if (result < 0 && errno == 0) + errno = EINVAL; + if (result < 0) + { + /* Some filesystems on OSE does not handle seeking beyond EOF + * We handle this here, by localy storing offsets. */ + + if ((lseek(fd, 0, SEEK_END) < off) && errno == 78) + { + if (fdm == NULL) /* first time */ + { + if (l_init_local_fd() == -1) + { + errno = EINVAL; + check_error(-1, errInfo); + } + } + l_update_local_fd(fd, off, origin); + result = off; + } + else if (off < 0 && errno == ENOSYS) + { + errno = EINVAL; + return check_error(-1, errInfo); + } + else + { + return check_error(-1, errInfo); + } + } + if (new_location) { + *new_location = result; + } + return 1; } @@ -831,6 +1085,12 @@ efile_truncate_file(Efile_error* errInfo, int *fd, int flags) { #ifndef NO_FTRUNCATE off_t offset; + + if (L_FD_EXISTS(fileno(fd))) + { + L_FD_INVALIDATE(fileno(fd)); + } + return check_error((offset = lseek(*fd, 0, SEEK_CUR)) >= 0 && ftruncate(*fd, offset) == 0 ? 1 : -1, errInfo); #else -- cgit v1.2.3 From 5352e800edf7e59d352a8d2dc7a7bee7ba916e1b Mon Sep 17 00:00:00 2001 From: Jonas Karlsson Date: Mon, 4 Nov 2013 13:36:37 +0100 Subject: ose: Use spinlock as default heap lock mechanism --- erts/emulator/sys/ose/default.lmconf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/default.lmconf b/erts/emulator/sys/ose/default.lmconf index 150111b1f6..6d3499df8f 100644 --- a/erts/emulator/sys/ose/default.lmconf +++ b/erts/emulator/sys/ose/default.lmconf @@ -10,4 +10,5 @@ OSE_LM_BSS_INIT=YES OSE_LM_EXEC_MODEL=SHARED HEAP_MAX_SIZE=1000000000 HEAP_SMALL_BUF_INIT_SIZE=64000000 -HEAP_LARGE_BUF_THRESHOLD=16000000 \ No newline at end of file +HEAP_LARGE_BUF_THRESHOLD=16000000 +HEAP_LOCK_TYPE=2 -- cgit v1.2.3 From a0766a4efeefe0ac035d2be04816274aee5751f3 Mon Sep 17 00:00:00 2001 From: Jonas Karlsson Date: Fri, 8 Nov 2013 14:00:36 +0100 Subject: ose: Bugfixes to filesystem related issues. --- erts/emulator/drivers/ose/ose_efile.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/drivers/ose/ose_efile.c b/erts/emulator/drivers/ose/ose_efile.c index 897d654e89..9980b07817 100644 --- a/erts/emulator/drivers/ose/ose_efile.c +++ b/erts/emulator/drivers/ose/ose_efile.c @@ -258,7 +258,11 @@ efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ #ifdef NO_MKDIR_MODE return check_error(mkdir(name), errInfo); #else - return check_error(mkdir(name, DIR_MODE), errInfo); + int res = mkdir(name, DIR_MODE); + if (res < 0 && errno == EINVAL) { + errno = ENOENT; + } + return check_error(res, errInfo); #endif } -- cgit v1.2.3 From 7d924d6c1279178db9c81fd04dd87c8d902a6549 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 12 Nov 2013 18:27:53 +0100 Subject: ose: Fix printout to stdout to not duplicate long buffers --- erts/emulator/sys/ose/sys.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index 6e55301d6e..ba8851e203 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -892,7 +892,7 @@ OS_PROCESS(fd_writer_process) { SysIOVec *iov; int iovlen; int iovcnt; - int n = 0, i; + int n = 0, i = 0, offset = 0; size_t p; /* fprintf(stderr,"0x%x: fd_writer, receive\n", current_process()); */ sig = receive(sigsel); @@ -913,12 +913,22 @@ OS_PROCESS(fd_writer_process) { driver_pdl_unlock(driver_data[fd].pdl); if (iovlen > 0) { - for (i = 0; i < iovcnt; i++) { - res = write(fd, iov[i].iov_base, - iov[i].iov_len > 256 ? 256 : iov[i].iov_len); + while(i < iovcnt) { + /* We only write 256 chars at the time in order to + not overflow the stdout process */ + if ((iov[i].iov_len-offset) > 256) { + res = write(fd, iov[i].iov_base+offset, 256); if (res < 0) break; - n += res; + offset += res; + } else { + res = write(fd, iov[i].iov_base+offset, iov[i].iov_len-offset); + if (res < 0) + break; + offset = 0; + i++; + } + n += res; } if (res > 0) res = n; -- cgit v1.2.3 From 3d24208d607501207af371098c1466758844e667 Mon Sep 17 00:00:00 2001 From: Jonas Karlsson Date: Fri, 22 Nov 2013 09:45:05 +0100 Subject: ose: efile driver updates. --- erts/emulator/drivers/common/efile_drv.c | 8 +- erts/emulator/drivers/common/erl_efile.h | 2 +- erts/emulator/drivers/ose/ose_efile.c | 1531 +++++++++++++----------------- erts/emulator/drivers/unix/unix_efile.c | 5 + erts/emulator/drivers/win32/win_efile.c | 5 + 5 files changed, 665 insertions(+), 886 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index fbd72c6c1b..7ef985dc41 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -373,9 +373,6 @@ struct erl_drv_entry efile_driver_entry = { #else NULL #endif /* HAVE_SENDFILE */ -#ifdef __OSE__ - ,NULL -#endif }; @@ -768,6 +765,9 @@ file_init(void) : 0); driver_system_info(&sys_info, sizeof(ErlDrvSysInfo)); + /* run initiation of efile_driver if needed */ + efile_init(); + #ifdef USE_VM_PROBES erts_mtx_init(&dt_driver_mutex, "efile_drv dtrace mutex"); pthread_key_create(&dt_driver_key, NULL); @@ -914,6 +914,7 @@ static void reply_Uint_posix_error(file_descriptor *desc, Uint num, driver_output2(desc->port, response, t-response, NULL, 0); } +#ifdef HAVE_SENDFILE static void reply_string_error(file_descriptor *desc, char* str) { char response[256]; /* Response buffer. */ char* s; @@ -924,6 +925,7 @@ static void reply_string_error(file_descriptor *desc, char* str) { *t = tolower(*s); driver_output2(desc->port, response, t-response, NULL, 0); } +#endif static int reply_error(file_descriptor *desc, Efile_error *errInfo) /* The error codes. */ diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h index 95c036db8f..5a8e3bc5db 100644 --- a/erts/emulator/drivers/common/erl_efile.h +++ b/erts/emulator/drivers/common/erl_efile.h @@ -127,7 +127,7 @@ struct t_sendfile_hdtl { /* * Functions. */ - +int efile_init(void); int efile_mkdir(Efile_error* errInfo, char* name); int efile_rmdir(Efile_error* errInfo, char* name); int efile_delete_file(Efile_error* errInfo, char* name); diff --git a/erts/emulator/drivers/ose/ose_efile.c b/erts/emulator/drivers/ose/ose_efile.c index 9980b07817..035ff81a9b 100644 --- a/erts/emulator/drivers/ose/ose_efile.c +++ b/erts/emulator/drivers/ose/ose_efile.c @@ -31,45 +31,16 @@ #include "sys.h" #include "erl_driver.h" #include "erl_efile.h" -/*#include */ -#ifdef HAVE_UNISTD_H -#ifndef __OSE__ -#include -#endif -#endif -#ifdef HAVE_SYS_UIO_H -#include -#include -#endif -#if defined(HAVE_SENDFILE) && (defined(__linux__) || (defined(__sun) && defined(__SVR4))) -#include -#endif - -#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) -#define DARWIN 1 -#endif - #if defined(DARWIN) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE) -#include -#endif - -#ifdef HAVE_LINUX_FALLOC_H -#include +#include "fcntl.h" #endif - -#ifdef SUNOS4 -# define getcwd(buf, size) getwd(buf) -#endif - -#ifdef __OSE__ #include "ose.h" #include "unistd.h" #include "sys/stat.h" #include "dirent.h" #include "sys/time.h" #include "time.h" -#include "heapapi.h" -#endif +#include "assert.h" /* Find a definition of MAXIOV, that is used in the code later. */ #if defined IOV_MAX @@ -80,7 +51,6 @@ #define MAXIOV 16 #endif - /* * Macros for testing file types. */ @@ -88,7 +58,7 @@ #define ISDIR(st) (((st).st_mode & S_IFMT) == S_IFDIR) #define ISREG(st) (((st).st_mode & S_IFMT) == S_IFREG) #define ISDEV(st) \ - (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) + (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) #define ISLNK(st) (((st).st_mode & S_IFLNK) == S_IFLNK) #ifdef NO_UMASK #define FILE_MODE 0644 @@ -101,159 +71,261 @@ #define IS_DOT_OR_DOTDOT(s) \ (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0'))) - /* - * Local file descriptor handling, this is needed for filesystems - * that does not handle seeking outside EOF. + * Macros for handling local file descriptors + * and mutexes. + * + * Handling of files like this is necessary because OSE + * does not allow seeking after the end of a file. So + * what we do it emulate this by keeping track of the size + * of the file and where the file's positions is. If a + * write happens after eof then we pad it. + * + * Given time this should be rewritten to get rid of the + * mutex and use the port lock to protect the data. This + * could be done be done by adapting the efile api for some + * calls to allow some meta-data to be associated with the + * open file. */ -#define L_FD_EXISTS(fd) \ - ((fdm->is_init == 1) && fdm->fd_array[(fd)] != NULL) +#define L_FD_IS_VALID(fd_data) ((fd_data)->beyond_eof > 0) +#define L_FD_INVALIDATE(fd_data) (fd_data)->beyond_eof = 0 +#define L_FD_CUR(fd_data) (fd_data)->pos +#define L_FD_OFFS_BEYOND_EOF(fd_data, offs) \ + (((fd_data)->size > offs) ? 0 : 1) + +#define L_FD_FAIL -1 +#define L_FD_SUCCESS 1 +#define L_FD_PAD_SIZE 255 -#define L_FD_INVALIDATE(fd) \ - fdm->fd_array[(fd)]->valid = 0 +struct fd_meta { + ErlDrvMutex *meta_mtx; + struct fd_data *fd_data_list; +}; -#define L_FD_IS_VALID(fd) \ - (fdm->fd_array[(fd)]->valid == 1) +struct fd_data { + int fd; + struct fd_data *next; + struct fd_data *prev; + int pos; + int beyond_eof; + size_t size; +#ifdef DEBUG + PROCESS owner; +#endif +}; -#define L_FD_OFFSET_BEYOND_EOF(fd, offset) \ - (fdm->fd_array[(fd)]->size < offset) +static int l_invalidate_local_fd(int fd); +static int l_pad_file(struct fd_data *fd_data, off_t offset); +static int check_error(int result, Efile_error* errInfo); +static struct fd_data* l_new_fd(void); +static int l_remove_local_fd(int fd); +static struct fd_data* l_find_local_fd(int fd); +static int l_update_local_fd(int fd, int pos, int size); -#define L_FD_CUR(fd) \ - (fdm->fd_array[(fd)]->pos) +static struct fd_meta* fdm = NULL; -struct fd_meta -{ - int is_init; - uint32_t fd_count; - struct fd_data *fd_array[256]; -}; +/***************************************************************************/ -struct fd_data +static int +l_remove_local_fd(int fd) { - int pos; - int whence; - int valid; - size_t size; -}; + struct fd_data *fd_data; + fd_data = l_find_local_fd(fd); + + if (fd_data == NULL) { + return L_FD_FAIL; + } +#ifdef DEBUG + assert(fd_data->owner == current_process()); +#endif + erl_drv_mutex_lock(fdm->meta_mtx); + /* head ? */ + if (fd_data == fdm->fd_data_list) { + if (fd_data->next != NULL) { + /* remove link to head */ + fd_data->next->prev = NULL; + /* set new head */ + fdm->fd_data_list = fd_data->next; + } + else { + /* head is lonely */ + fdm->fd_data_list = NULL; + } + } + else { /* not head */ + if (fd_data->prev == NULL) { + erl_drv_mutex_unlock(fdm->meta_mtx); + return L_FD_FAIL; + } + else { + if (fd_data->next != NULL) { + fd_data->next->prev = fd_data->prev; + fd_data->prev->next = fd_data->next; + } + else { + fd_data->prev->next = NULL; + } + } + } -static struct fd_meta *fdm; -static int l_init_local_fd(void); -static int l_pad_file(int fd, off_t offset); -static int l_update_local_fd(int fd, off_t offset, int whence); + /* scramble values */ + fd_data->beyond_eof = -1; + fd_data->next = NULL; + fd_data->prev = NULL; + fd_data->fd = -1; -static int check_error(int result, Efile_error* errInfo); + /* unlock and clean */ + driver_free(fd_data); + erl_drv_mutex_unlock(fdm->meta_mtx); + + return L_FD_SUCCESS; +} + +/***************************************************************************/ static int -l_init_local_fd() -{ - fdm = heap_alloc_private(sizeof(struct fd_meta), __FILE__, __LINE__); - fdm->fd_count = 0; - fdm->is_init = 1; +l_invalidate_local_fd(int fd) { + struct fd_data *fd_data; + + if ((fd_data = l_find_local_fd(fd)) == NULL) { + return L_FD_FAIL; + } - memset(fdm->fd_array, NULL, sizeof(fdm->fd_array)); - return 1; + fd_data->beyond_eof = 0; + return L_FD_SUCCESS; } -static int -l_update_local_fd(int fd, off_t offset, int whence) -{ - errno = 0; +/****************************************************************************/ - if (fdm->fd_array[fd] == NULL) - { - fdm->fd_array[fd] = heap_alloc_private(sizeof(struct fd_data), __FILE__, __LINE__); - fdm->fd_count++; - } +static struct fd_data* +l_find_local_fd(int fd) { + struct fd_data *fd_data; - switch (whence) - { - case SEEK_CUR: - fdm->fd_array[fd]->pos = lseek(fd, 0, SEEK_CUR) + offset; - break; + fd_data = NULL; + erl_drv_mutex_lock(fdm->meta_mtx); + for (fd_data = fdm->fd_data_list; fd_data != NULL; ) { + if (fd_data->fd == fd) { +#ifdef DEBUG + assert(fd_data->owner == current_process()); +#endif + break; + } + fd_data = fd_data->next; + } + erl_drv_mutex_unlock(fdm->meta_mtx); + return fd_data; +} + +/***************************************************************************/ - case SEEK_END: - fdm->fd_array[fd]->pos = lseek(fd, 0, SEEK_END) + offset; - break; +static struct fd_data* +l_new_fd(void) { + struct fd_data *fd_data; - case SEEK_SET: - fdm->fd_array[fd]->pos = offset; + fd_data = driver_alloc(sizeof(struct fd_data)); + if (fd_data == NULL) { + return NULL; + } + erl_drv_mutex_lock(fdm->meta_mtx); + if (fdm->fd_data_list == NULL) { + fdm->fd_data_list = fd_data; + fdm->fd_data_list->prev = NULL; + fdm->fd_data_list->next = NULL; + } + else { + fd_data->next = fdm->fd_data_list; + fdm->fd_data_list = fd_data; + fdm->fd_data_list->prev = NULL; + } +#ifdef DEBUG + fd_data->owner = current_process(); +#endif + erl_drv_mutex_unlock(fdm->meta_mtx); + return fd_data; +} - default: - errno = ENOSYS; - break; - } - fdm->fd_array[fd]->size = lseek(fd, 0, SEEK_END); +/***************************************************************************/ - fdm->fd_array[fd]->whence = whence; - fdm->fd_array[fd]->valid = 1; +static int +l_update_local_fd(int fd, int pos, int size) { + struct fd_data *fd_data = NULL; + + fd_data = l_find_local_fd(fd); + /* new fd to handle? */ + if (fd_data == NULL) { + fd_data = l_new_fd(); + if (fd_data == NULL) { + /* out of memory */ + return L_FD_FAIL; + } + } + fd_data->size = size; + fd_data->pos = pos; + fd_data->fd = fd; + fd_data->beyond_eof = 1; - return ((errno != 0) ? -1 : 1); + return L_FD_SUCCESS; } +/***************************************************************************/ + static int -l_pad_file(int fd, off_t offset) -{ - int size_dif; - char *pad; - char *cursor; - int file_size; - int written = 0; - - if (!L_FD_IS_VALID(fd)) - { - l_update_local_fd(fd, offset, SEEK_END); - } - - if (!L_FD_OFFSET_BEYOND_EOF(fd, offset)) - { - - return -1; - } - - - file_size = lseek(fd, 0, SEEK_END); - size_dif = offset - file_size; - pad = heap_alloc_private(size_dif, __FILE__, __LINE__); - cursor = pad; - memset(pad, '\0', size_dif); - - while (size_dif > 0) - { - written = write(fd, cursor, size_dif); - - if (written < 0) - { - return -1; - } - cursor += written; - size_dif -= written; - } - - L_FD_INVALIDATE(fd); - heap_free_private(pad); - - return ((errno != 0) ? -1 : 1); +l_pad_file(struct fd_data *fd_data, off_t offset) { + int size_dif; + int written = 0; + int ret_val = L_FD_SUCCESS; + char padding[L_FD_PAD_SIZE]; + + size_dif = (offset - fd_data->size); + memset(&padding, '\0', L_FD_PAD_SIZE); + + while (size_dif > 0) { + written = write(fd_data->fd, padding, + (size_dif < L_FD_PAD_SIZE) ? + size_dif : L_FD_PAD_SIZE); + if (written < 0 && errno != EINTR && errno != EAGAIN) { + ret_val = -1; + break; + } + size_dif -= written; + } + L_FD_INVALIDATE(fd_data); + return ret_val; } +/***************************************************************************/ + static int -check_error(int result, Efile_error *errInfo) -{ +check_error(int result, Efile_error *errInfo) { if (result < 0) { - errInfo->posix_errno = errInfo->os_errno = errno; - return 0; + errInfo->posix_errno = errInfo->os_errno = errno; + return 0; } return 1; } -/* - * public API - */ +/***************************************************************************/ + +int +efile_init() { + fdm = driver_alloc(sizeof(struct fd_meta)); + if (fdm == NULL) { + return L_FD_FAIL; + } + fdm->meta_mtx = erl_drv_mutex_create("ose_efile local fd mutex\n"); + erl_drv_mutex_lock(fdm->meta_mtx); + fdm->fd_data_list = NULL; + erl_drv_mutex_unlock(fdm->meta_mtx); + return L_FD_SUCCESS; +} + +/***************************************************************************/ int -efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ - char* name) /* Name of directory to create. */ +efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of directory to create. */ { #ifdef NO_MKDIR_MODE return check_error(mkdir(name), errInfo); @@ -266,53 +338,69 @@ efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ #endif } +/***************************************************************************/ + int -efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ - char* name) /* Name of directory to delete. */ +efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of directory to delete. */ { if (rmdir(name) == 0) { - return 1; + return 1; } if (errno == ENOTEMPTY) { - errno = EEXIST; + errno = EEXIST; } - if (errno == EEXIST) { - int saved_errno = errno; - struct stat file_stat; - struct stat cwd_stat; - - /* - * The error code might be wrong if this is the current directory. - */ - - if (stat(name, &file_stat) == 0 && stat(".", &cwd_stat) == 0 && - file_stat.st_ino == cwd_stat.st_ino && - file_stat.st_dev == cwd_stat.st_dev) { - saved_errno = EINVAL; - } - errno = saved_errno; + if (errno == EEXIST || errno == EINVAL) { + int saved_errno = errno; + struct stat file_stat; + struct stat cwd_stat; + + if(stat(name, &file_stat) != 0) { + errno = ENOENT; + return check_error(-1, errInfo); + } + /* + * The error code might be wrong if this is the current directory. + */ + if (stat(name, &file_stat) == 0 && stat(".", &cwd_stat) == 0 && + file_stat.st_ino == cwd_stat.st_ino && + file_stat.st_dev == cwd_stat.st_dev) { + saved_errno = EACCES; + } + errno = saved_errno; } return check_error(-1, errInfo); } +/***************************************************************************/ + int -efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ - char* name) /* Name of file to delete. */ +efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of file to delete. */ { struct stat statbuf; - /* OSE will accept removal of directories with unlink() */ - if (stat(name, &statbuf) >= 0 && ISDIR(statbuf)) - { - errno = EPERM; - return check_error(-1, errInfo); - } + if (stat(name, &statbuf) >= 0) { + /* Do not let unlink() remove directories */ + if (ISDIR(statbuf)) { + errno = EPERM; + return check_error(-1, errInfo); + } - if (unlink(name) == 0) { - return 1; + if (unlink(name) == 0) { + return 1; + } + + if (errno == EISDIR) { + errno = EPERM; + return check_error(-1, errInfo); + } } - if (errno == EISDIR) { /* Linux sets the wrong error code. */ - errno = EPERM; + else { + if (errno == EINVAL) { + errno = ENOENT; + return check_error(-1, errInfo); + } } return check_error(-1, errInfo); } @@ -321,150 +409,107 @@ efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ *--------------------------------------------------------------------------- * * Changes the name of an existing file or directory, from src to dst. - * If src and dst refer to the same file or directory, does nothing - * and returns success. Otherwise if dst already exists, it will be - * deleted and replaced by src subject to the following conditions: - * If src is a directory, dst may be an empty directory. - * If src is a file, dst may be a file. - * In any other situation where dst already exists, the rename will - * fail. + * If src and dst refer to the same file or directory, does nothing + * and returns success. Otherwise if dst already exists, it will be + * deleted and replaced by src subject to the following conditions: + * If src is a directory, dst may be an empty directory. + * If src is a file, dst may be a file. + * In any other situation where dst already exists, the rename will + * fail. * * Results: - * If the directory was successfully created, returns 1. - * Otherwise the return value is 0 and errno is set to - * indicate the error. Some possible values for errno are: + * If the directory was successfully created, returns 1. + * Otherwise the return value is 0 and errno is set to + * indicate the error. Some possible values for errno are: * - * EACCES: src or dst parent directory can't be read and/or written. - * EEXIST: dst is a non-empty directory. - * EINVAL: src is a root directory or dst is a subdirectory of src. - * EISDIR: dst is a directory, but src is not. - * ENOENT: src doesn't exist, or src or dst is "". - * ENOTDIR: src is a directory, but dst is not. - * EXDEV: src and dst are on different filesystems. + * EACCES: src or dst parent directory can't be read and/or written. + * EEXIST: dst is a non-empty directory. + * EINVAL: src is a root directory or dst is a subdirectory of src. + * EISDIR: dst is a directory, but src is not. + * ENOENT: src doesn't exist, or src or dst is "". + * ENOTDIR: src is a directory, but dst is not. + * EXDEV: src and dst are on different filesystems. * * Side effects: - * The implementation of rename may allow cross-filesystem renames, - * but the caller should be prepared to emulate it with copy and - * delete if errno is EXDEV. + * The implementation of rename may allow cross-filesystem renames, + * but the caller should be prepared to emulate it with copy and + * delete if errno is EXDEV. * *--------------------------------------------------------------------------- */ int -efile_rename(Efile_error* errInfo, /* Where to return error codes. */ - char* src, /* Original name. */ - char* dst) /* New name. */ +efile_rename(Efile_error* errInfo, /* Where to return error codes. */ + char* src, /* Original name. */ + char* dst) /* New name. */ { - /* temporary fix AFM does not recognize ./ - * in destination */ - - char *dot_str; - if (dst != NULL) - { - dot_str = strchr(dst, '.'); + /* temporary fix AFM does not recognize ./ + * in destination remove pending on adaption of AFM fix + */ - if (dot_str && dot_str == dst && dot_str[1] == '/') - { - dst = dst+2; - } - } + char *dot_str; + if (dst != NULL) { + dot_str = strchr(dst, '.'); + if (dot_str && dot_str == dst && dot_str[1] == '/') { + dst = dst+2; + } + } - if (rename(src, dst) == 0) { - return 1; + if (rename(src, dst) == 0) { + return 1; } if (errno == ENOTEMPTY) { - errno = EEXIST; + errno = EEXIST; } -#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 - * EINVAL result code for moving a directory into itself). Must be - * conditionally compiled because realpath() is only defined on SunOS. - */ - if (errno == EINVAL) { - char srcPath[MAXPATHLEN], dstPath[MAXPATHLEN]; - DIR *dirPtr; - struct dirent *dirEntPtr; - -#ifdef PURIFY - memset(srcPath, '\0', sizeof(srcPath)); - memset(dstPath, '\0', sizeof(dstPath)); -#endif + struct stat file_stat; - if ((realpath(src, srcPath) != NULL) - && (realpath(dst, dstPath) != NULL) - && (strncmp(srcPath, dstPath, strlen(srcPath)) != 0)) { - dirPtr = opendir(dst); - if (dirPtr != NULL) { - while ((dirEntPtr = readdir(dirPtr)) != NULL) { - if ((strcmp(dirEntPtr->d_name, ".") != 0) && - (strcmp(dirEntPtr->d_name, "..") != 0)) { - errno = EEXIST; - closedir(dirPtr); - return check_error(-1, errInfo); - } - } - closedir(dirPtr); - } - } - errno = EINVAL; + if (stat(dst, &file_stat)== 0) { + if (ISDIR(file_stat)) { + errno = EISDIR; + } + else if (ISREG(file_stat)) { + errno = ENOTDIR; + } + else { + errno = EINVAL; + } + } + else { + errno = EINVAL; + } } -#endif /* sparc */ if (strcmp(src, "/") == 0) { - /* - * Alpha reports renaming / as EBUSY and Linux reports it as EACCES, - * instead of EINVAL. - */ - - errno = EINVAL; + errno = EINVAL; } - - /* - * DEC Alpha OSF1 V3.0 returns EACCES when attempting to move a - * file across filesystems and the parent directory of that file is - * not writable. Most other systems return EXDEV. Does nothing to - * correct this behavior. - */ - return check_error(-1, errInfo); } +/***************************************************************************/ + int efile_chdir(Efile_error* errInfo, /* Where to return error codes. */ - char* name) /* Name of directory to make current. */ + char* name) /* Name of directory to make current. */ { return check_error(chdir(name), errInfo); } +/***************************************************************************/ int -efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */ - int drive, /* 0 - current, 1 - A, 2 - B etc. */ - char* buffer, /* Where to return the current - directory. */ - size_t size) /* Size of buffer. */ +efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */ + int drive, /* 0 - current, 1 - A, 2 - B etc. */ + char* buffer, /* Where to return the current + directory. */ + size_t size) /* Size of buffer. */ { if (drive == 0) { - if (getcwd(buffer, size) == NULL) - return check_error(-1, errInfo); - -#ifdef SIMSPARCSOLARIS - /* We get "host:" prepended to the dirname - remove!. */ - { - int i = 0; - int j = 0; - while ((buffer[i] != ':') && (buffer[i] != '\0')) i++; - if (buffer[i] == ':') { - i++; - while ((buffer[j++] = buffer[i++]) != '\0'); - } - } -#endif - return 1; + if (getcwd(buffer, size) == NULL) + return check_error(-1, errInfo); + + return 1; } /* @@ -475,29 +520,31 @@ efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */ return check_error(-1, errInfo); } +/***************************************************************************/ + int -efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ - char* name, /* Name of directory to open. */ - EFILE_DIR_HANDLE* p_dir_handle, /* Pointer to directory - handle of - open directory.*/ - char* buffer, /* Pointer to buffer for - one filename. */ - size_t *size) /* in-out Size of buffer, length - of name. */ +efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name, /* Name of directory to open. */ + EFILE_DIR_HANDLE* p_dir_handle, /* Pointer to directory + handle of + open directory.*/ + char* buffer, /* Pointer to buffer for + one filename. */ + size_t *size) /* in-out Size of buffer, length + of name. */ { - DIR *dp; /* Pointer to directory structure. */ - struct dirent* dirp; /* Pointer to directory entry. */ + DIR *dp; /* Pointer to directory structure. */ + struct dirent* dirp; /* Pointer to directory entry. */ /* * If this is the first call, we must open the directory. */ if (*p_dir_handle == NULL) { - dp = opendir(name); - if (dp == NULL) - return check_error(-1, errInfo); - *p_dir_handle = (EFILE_DIR_HANDLE) dp; + dp = opendir(name); + if (dp == NULL) + return check_error(-1, errInfo); + *p_dir_handle = (EFILE_DIR_HANDLE) dp; } /* @@ -506,165 +553,138 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ dp = *((DIR **)((void *)p_dir_handle)); for (;;) { - dirp = readdir(dp); - if (dirp == NULL) { - closedir(dp); - return 0; - } - if (IS_DOT_OR_DOTDOT(dirp->d_name)) - continue; - buffer[0] = '\0'; - strncat(buffer, dirp->d_name, (*size)-1); - *size = strlen(dirp->d_name); - return 1; + dirp = readdir(dp); + if (dirp == NULL) { + closedir(dp); + return 0; + } + if (IS_DOT_OR_DOTDOT(dirp->d_name)) + continue; + buffer[0] = '\0'; + strncat(buffer, dirp->d_name, (*size)-1); + *size = strlen(dirp->d_name); + return 1; } } +/***************************************************************************/ + int -efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ - char* name, /* Name of directory to open. */ - int flags, /* Flags to user for opening. */ - int* pfd, /* Where to store the file - descriptor. */ - Sint64 *pSize) /* Where to store the size of the - file. */ +efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ + char* name, /* Name of directory to open. */ + int flags, /* Flags to user for opening. */ + int* pfd, /* Where to store the file + descriptor. */ + Sint64 *pSize) /* Where to store the size of the + file. */ { struct stat statbuf; int fd; - int mode; /* Open mode. */ + int mode; /* Open mode. */ if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) { - /* - * For UNIX only, here is some ugly code to allow - * /dev/null to be opened as a file. - * - * Assumption: The i-node number for /dev/null cannot be zero. - */ - static ino_t dev_null_ino = 0; - - if (dev_null_ino == 0) { - struct stat nullstatbuf; - - if (stat("/dev/null", &nullstatbuf) >= 0) { - dev_null_ino = nullstatbuf.st_ino; - } - } - if (!(dev_null_ino && statbuf.st_ino == dev_null_ino)) { - errno = EISDIR; - return check_error(-1, errInfo); - } + errno = EISDIR; + return check_error(-1, errInfo); } switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) { - case EFILE_MODE_READ: - mode = O_RDONLY; - break; - case EFILE_MODE_WRITE: - if (flags & EFILE_NO_TRUNCATE) - mode = O_WRONLY | O_CREAT; - else - mode = O_WRONLY | O_CREAT | O_TRUNC; - break; - case EFILE_MODE_READ_WRITE: - mode = O_RDWR | O_CREAT; - break; - default: - errno = EINVAL; - return check_error(-1, errInfo); + case EFILE_MODE_READ: + mode = O_RDONLY; + break; + case EFILE_MODE_WRITE: + if (flags & EFILE_NO_TRUNCATE) + mode = O_WRONLY | O_CREAT; + else + mode = O_WRONLY | O_CREAT | O_TRUNC; + break; + case EFILE_MODE_READ_WRITE: + mode = O_RDWR | O_CREAT; + break; + default: + errno = EINVAL; + return check_error(-1, errInfo); } if (flags & EFILE_MODE_APPEND) { - mode &= ~O_TRUNC; - mode |= O_APPEND; + mode &= ~O_TRUNC; + mode |= O_APPEND; } if (flags & EFILE_MODE_EXCL) { - mode |= O_EXCL; + mode |= O_EXCL; } fd = open(name, mode, FILE_MODE); if (!check_error(fd, errInfo)) - return 0; + return 0; *pfd = fd; if (pSize) { - *pSize = statbuf.st_size; + *pSize = statbuf.st_size; } return 1; } +/***************************************************************************/ + int efile_may_openfile(Efile_error* errInfo, char *name) { - struct stat statbuf; /* Information about the file */ + struct stat statbuf; /* Information about the file */ int result; result = stat(name, &statbuf); if (!check_error(result, errInfo)) - return 0; + return 0; if (!ISREG(statbuf)) { - errno = EISDIR; - return check_error(-1, errInfo); + errno = EISDIR; + return check_error(-1, errInfo); } return 1; } +/***************************************************************************/ + void efile_closefile(int fd) { - if (L_FD_EXISTS(fd)) - { - free(fdm->fd_array[fd]); - fdm->fd_array[fd] = NULL; - } - close(fd); + if (l_find_local_fd(fd) != NULL) { + l_remove_local_fd(fd); + } + close(fd); } +/***************************************************************************/ + int efile_fdatasync(Efile_error *errInfo, /* Where to return error codes. */ - int fd) /* File descriptor for file to sync data. */ + int fd) /* File descriptor for file to sync data. */ { -#ifdef HAVE_FDATASYNC - return check_error(fdatasync(fd), errInfo); -#else return efile_fsync(errInfo, fd); -#endif } +/***************************************************************************/ + int efile_fsync(Efile_error *errInfo, /* Where to return error codes. */ - int fd) /* File descriptor for file to sync. */ + int fd) /* File descriptor for file to sync. */ { -#ifdef NO_FSYNC - undefined fsync /* XXX: Really? */ -#else -#if defined(DARWIN) && defined(F_FULLFSYNC) - return check_error(fcntl(fd, F_FULLFSYNC), errInfo); -#else return check_error(fsync(fd), errInfo); -#endif /* DARWIN */ -#endif /* NO_FSYNC */ } +/***************************************************************************/ + int efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, - char* name, int info_for_link) + char* name, int info_for_link) { - struct stat statbuf; /* Information about the file */ + struct stat statbuf; /* Information about the file */ int result; - if (info_for_link) { -#ifndef __OSE__ - result = lstat(name, &statbuf); -#else - result = stat(name, &statbuf); -#endif - } else { - result = stat(name, &statbuf); - } + result = stat(name, &statbuf); if (!check_error(result, errInfo)) { - return 0; + return 0; } #if SIZEOF_OFF_T == 4 @@ -682,22 +702,22 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, #else pInfo->access = FA_NONE; if (access(name, R_OK) == 0) - pInfo->access |= FA_READ; + pInfo->access |= FA_READ; if (access(name, W_OK) == 0) - pInfo->access |= FA_WRITE; + pInfo->access |= FA_WRITE; #endif if (ISDEV(statbuf)) - pInfo->type = FT_DEVICE; + pInfo->type = FT_DEVICE; else if (ISDIR(statbuf)) - pInfo->type = FT_DIRECTORY; + pInfo->type = FT_DIRECTORY; else if (ISREG(statbuf)) - pInfo->type = FT_REGULAR; + pInfo->type = FT_REGULAR; else if (ISLNK(statbuf)) - pInfo->type = FT_SYMLINK; + pInfo->type = FT_SYMLINK; else - pInfo->type = FT_OTHER; + pInfo->type = FT_OTHER; pInfo->accessTime = statbuf.st_atime; pInfo->modifyTime = statbuf.st_mtime; @@ -706,9 +726,6 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, pInfo->mode = statbuf.st_mode; pInfo->links = statbuf.st_nlink; pInfo->major_device = statbuf.st_dev; -#ifndef __OSE__ - pInfo->minor_device = statbuf.st_rdev; -#endif pInfo->inode = statbuf.st_ino; pInfo->uid = statbuf.st_uid; pInfo->gid = statbuf.st_gid; @@ -716,205 +733,164 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, return 1; } +/***************************************************************************/ + int efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) { -/* FIXME, this will be dificult to get to work right with the tests on ose - * reason being that the info values are sometimes used in different ways, and in - * some times not used at all. */ - -#ifndef __OSE__ - struct utimbuf tval; -#endif - /* * On some systems chown will always fail for a non-root user unless * POSIX_CHOWN_RESTRICTED is not set. Others will succeed as long as * you don't try to chown a file to someone besides youself. */ -#ifndef __OSE__ - if (chown(name, pInfo->uid, pInfo->gid) && errno != EPERM) { - return check_error(-1, errInfo); - } -#endif - if (pInfo->mode != -1) { - mode_t newMode = pInfo->mode & (S_ISUID | S_ISGID | - S_IRWXU | S_IRWXG | S_IRWXO); - if (chmod(name, newMode)) { - newMode &= ~(S_ISUID | S_ISGID); - if (chmod(name, newMode)) { - return check_error(-1, errInfo); - } - } + mode_t newMode = pInfo->mode & (S_ISUID | S_ISGID | + S_IRWXU | S_IRWXG | S_IRWXO); + if (chmod(name, newMode)) { + newMode &= ~(S_ISUID | S_ISGID); + if (chmod(name, newMode)) { + return check_error(-1, errInfo); + } + } } -#ifndef __OSE__ - tval.actime = pInfo->accessTime; - tval.modtime = pInfo->modifyTime; - - return check_error(utime(name, &tval), errInfo); -#else - return 1; -#endif + return 1; } +/***************************************************************************/ int -efile_write(Efile_error* errInfo, /* Where to return error codes. */ - int flags, /* Flags given when file was - opened. */ - int fd, /* File descriptor to write to. */ - char* buf, /* Buffer to write. */ - size_t count) /* Number of bytes to write. */ +efile_write(Efile_error* errInfo, /* Where to return error codes. */ + int flags, /* Flags given when file was + opened. */ + int fd, /* File descriptor to write to. */ + char* buf, /* Buffer to write. */ + size_t count) /* Number of bytes to write. */ { - ssize_t written; /* Bytes written in last operation. */ - - if (L_FD_EXISTS(fd)) - { - off_t off; - off = (L_FD_IS_VALID(fd)) ? L_FD_CUR(fd) : lseek(fd, 0, SEEK_CUR); - - if (L_FD_OFFSET_BEYOND_EOF(fd, off)) - { - l_pad_file(fd, off); - } - - L_FD_INVALIDATE(fd); - } - - while (count > 0) - { - if ((written = write(fd, buf, count)) < 0) - { - if (errno != EINTR) - { - return check_error(-1, errInfo); - } - else - { - written = 0; - } - } - ASSERT(written <= count); - buf += written; - count -= written; + ssize_t written; /* Bytes written in last operation. */ + struct fd_data *fd_data; + + if ((fd_data = l_find_local_fd(fd)) != NULL) { + if (L_FD_IS_VALID(fd_data)) { + /* we are beyond eof and need to pad*/ + if (l_pad_file(fd_data, L_FD_CUR(fd_data)) < 0) { + return check_error(-1, errInfo); + } + } } - if (L_FD_EXISTS(fd)) - { - L_FD_INVALIDATE(fd); + while (count > 0) { + if ((written = write(fd, buf, count)) < 0) { + if (errno != EINTR) { + return check_error(-1, errInfo); + } + else { + written = 0; + } + } + ASSERT(written <= count); + buf += written; + count -= written; } return 1; } +/***************************************************************************/ + int efile_writev(Efile_error* errInfo, /* Where to return error codes */ - int flags, /* Flags given when file was - * opened */ - int fd, /* File descriptor to write to */ - SysIOVec* iov, /* Vector of buffer structs. - * The structs may be changed i.e. - * due to incomplete writes */ - int iovcnt) /* Number of structs in vector */ + int flags, /* Flags given when file was + * opened */ + int fd, /* File descriptor to write to */ + SysIOVec* iov, /* Vector of buffer structs. + * The structs may be changed i.e. + * due to incomplete writes */ + int iovcnt) /* Number of structs in vector */ { + struct fd_data *fd_data; int cnt = 0; /* Buffers so far written */ ASSERT(iovcnt >= 0); - + if ((fd_data = l_find_local_fd(fd)) != NULL) { + if (L_FD_IS_VALID(fd_data)) { + /* we are beyond eof and need to pad*/ + if (l_pad_file(fd_data, L_FD_CUR(fd_data)) < 0) { + return check_error(-1, errInfo); + } + } + } while (cnt < iovcnt) { - if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) { - /* Empty buffer - skip */ - cnt++; - } else { /* Non-empty buffer */ - ssize_t w; /* Bytes written in this call */ -#ifdef HAVE_WRITEV - int b = iovcnt - cnt; /* Buffers to write */ - /* Use as many buffers as MAXIOV allows */ - if (b > MAXIOV) - b = MAXIOV; - if (b > 1) { - do { - w = writev(fd, &iov[cnt], b); - } while (w < 0 && errno == EINTR); - } else - /* Degenerated io vector - use regular write */ -#endif - { - do { - w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len); - } while (w < 0 && errno == EINTR); - ASSERT(w <= iov[cnt].iov_len || w == -1); - } - if (w < 0) return check_error(-1, errInfo); - /* Move forward to next buffer to write */ - for (; cnt < iovcnt && w > 0; cnt++) { - if (iov[cnt].iov_base && iov[cnt].iov_len > 0) { - if (w < iov[cnt].iov_len) { - /* Adjust the buffer for next write */ - iov[cnt].iov_len -= w; - iov[cnt].iov_base += w; - w = 0; - break; - } else { - w -= iov[cnt].iov_len; - } - } - } - ASSERT(w == 0); - } /* else Non-empty buffer */ + if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) { + /* Empty buffer - skip */ + cnt++; + } + else { /* Non-empty buffer */ + ssize_t w; /* Bytes written in this call */ + do { + w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len); + } while (w < 0 && errno == EINTR); + + ASSERT(w <= iov[cnt].iov_len || w == -1); + + if (w < 0) { + return check_error(-1, errInfo); + } + /* Move forward to next buffer to write */ + for (; cnt < iovcnt && w > 0; cnt++) { + if (iov[cnt].iov_base && iov[cnt].iov_len > 0) { + if (w < iov[cnt].iov_len) { + /* Adjust the buffer for next write */ + iov[cnt].iov_len -= w; + iov[cnt].iov_base += w; + w = 0; + break; + } + else { + w -= iov[cnt].iov_len; + } + } + } + ASSERT(w == 0); + } /* else Non-empty buffer */ } /* while (cnt< iovcnt) */ return 1; } +/***************************************************************************/ + int efile_read(Efile_error* errInfo, /* Where to return error codes. */ - int flags, /* Flags given when file was opened. */ - int fd, /* File descriptor to read from. */ - char* buf, /* Buffer to read into. */ - size_t count, /* Number of bytes to read. */ - size_t *pBytesRead) /* Where to return number of - bytes read. */ + int flags, /* Flags given when file was opened. */ + int fd, /* File descriptor to read from. */ + char* buf, /* Buffer to read into. */ + size_t count, /* Number of bytes to read. */ + size_t *pBytesRead) /* Where to return number of + bytes read. */ { ssize_t n; - if (L_FD_EXISTS(fd)) - { - off_t off; - off = (L_FD_IS_VALID(fd)) ? L_FD_CUR(fd) : lseek(fd, 0, SEEK_CUR); - if (L_FD_IS_VALID(fd) == 0) - { - l_update_local_fd(fd, off, SEEK_SET); - } - - if (L_FD_OFFSET_BEYOND_EOF(fd, off)) - { - *pBytesRead = 0; - return 1; - } - /* FIXME .. is this needed? */ - lseek(fd, off, SEEK_SET); - } - - for (;;) - { - if ((n = read(fd, buf, count)) >= 0) - { - break; - } - else if (errno != EINTR) - { - return check_error(-1, errInfo); - } + struct fd_data *fd_data; + + if ((fd_data = l_find_local_fd(fd)) != NULL) { + if (L_FD_IS_VALID(fd_data)) { + *pBytesRead = 0; + return 1; + } } - if (n != 0 && L_FD_EXISTS(fd)) - { - L_FD_INVALIDATE(fd); + for (;;) { + if ((n = read(fd, buf, count)) >= 0) { + break; + } + else if (errno != EINTR) { + return check_error(-1, errInfo); + } + } + if (fd_data != NULL && L_FD_IS_VALID(fd_data)) { + L_FD_INVALIDATE(fd_data); } *pBytesRead = (size_t) n; return 1; } - /* pread() and pwrite() */ /* Some unix systems, notably Solaris has these syscalls */ /* It is especially nice for i.e. the dets module to have support */ @@ -928,198 +904,159 @@ efile_read(Efile_error* errInfo, /* Where to return error codes. */ int efile_pread(Efile_error* errInfo, /* Where to return error codes. */ - int fd, /* File descriptor to read from. */ - Sint64 offset, /* Offset in bytes from BOF. */ - char* buf, /* Buffer to read into. */ - size_t count, /* Number of bytes to read. */ - size_t *pBytesRead) /* Where to return - number of bytes read. */ + int fd, /* File descriptor to read from. */ + Sint64 offset, /* Offset in bytes from BOF. */ + char* buf, /* Buffer to read into. */ + size_t count, /* Number of bytes to read. */ + size_t *pBytesRead) /* Where to return + number of bytes read. */ { -#if defined(HAVE_PREAD) && defined(HAVE_PWRITE) - ssize_t n; - off_t off = (off_t) offset; - if (off != offset) { - errno = EINVAL; - return check_error(-1, errInfo); - } - for (;;) { - if ((n = pread(fd, buf, count, offset)) >= 0) - break; - else if (errno != EINTR) - return check_error(-1, errInfo); - } - *pBytesRead = (size_t) n; - return 1; -#else - { - int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); - if (res) { - return efile_read(errInfo, 0, fd, buf, count, pBytesRead); - } else { - return res; - } + int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + if (res) { + return efile_read(errInfo, 0, fd, buf, count, pBytesRead); + } else { + return res; } -#endif } +/***************************************************************************/ int efile_pwrite(Efile_error* errInfo, /* Where to return error codes. */ - int fd, /* File descriptor to write to. */ - char* buf, /* Buffer to write. */ - size_t count, /* Number of bytes to write. */ - Sint64 offset) /* where to write it */ + int fd, /* File descriptor to write to. */ + char* buf, /* Buffer to write. */ + size_t count, /* Number of bytes to write. */ + Sint64 offset) /* where to write it */ { -#if defined(HAVE_PREAD) && defined(HAVE_PWRITE) - ssize_t written; /* Bytes written in last operation. */ - off_t off = (off_t) offset; - if (off != offset) { - errno = EINVAL; - return check_error(-1, errInfo); - } - - while (count > 0) { - if ((written = pwrite(fd, buf, count, offset)) < 0) { - if (errno != EINTR) - return check_error(-1, errInfo); - else - written = 0; - } - ASSERT(written <= count); - buf += written; - count -= written; - offset += written; - } - return 1; -#else /* For unix systems that don't support pread() and pwrite() */ - { - int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); - if (res) { - return efile_write(errInfo, 0, fd, buf, count); - } else { - return res; - } + if (res) { + return efile_write(errInfo, 0, fd, buf, count); + } else { + return res; } -#endif } +/***************************************************************************/ int efile_seek(Efile_error* errInfo, /* Where to return error codes. */ - int fd, /* File descriptor to do the seek on. */ - Sint64 offset, /* Offset in bytes from the given - origin. */ - int origin, /* Origin of seek (SEEK_SET, SEEK_CUR, - SEEK_END). */ - Sint64 *new_location) /* Resulting new location in file. */ + int fd, /* File descriptor to do the seek on. */ + Sint64 offset, /* Offset in bytes from the given + origin. */ + int origin, /* Origin of seek (SEEK_SET, SEEK_CUR, + SEEK_END). */ + Sint64 *new_location) /* Resulting new location in file. */ { off_t off, result; off = (off_t) offset; switch (origin) { - case EFILE_SEEK_SET: origin = SEEK_SET; break; - case EFILE_SEEK_CUR: origin = SEEK_CUR; break; - case EFILE_SEEK_END: origin = SEEK_END; break; - default: - errno = EINVAL; - return check_error(-1, errInfo); + case EFILE_SEEK_SET: + origin = SEEK_SET; + break; + case EFILE_SEEK_CUR: + origin = SEEK_CUR; + break; + case EFILE_SEEK_END: + origin = SEEK_END; + break; + default: + errno = EINVAL; + return check_error(-1, errInfo); } + if (off != offset) { - errno = EINVAL; - return check_error(-1, errInfo); + errno = EINVAL; + return check_error(-1, errInfo); } errno = 0; result = lseek(fd, off, origin); - if (result >= 0 && L_FD_EXISTS(fd)) - { - L_FD_INVALIDATE(fd); + if (result >= 0) { + l_invalidate_local_fd(fd); } - /* - * Note that the man page for lseek (on SunOs 5) says: - * - * "if fildes is a remote file descriptor and offset is - * negative, lseek() returns the file pointer even if it is - * negative." - */ + if (result < 0) + { + if (errno == ENOSYS) { + int size, cur_pos; + + if (off < 0) { + errno = EINVAL; + return check_error(-1, errInfo); + } + + cur_pos = lseek(fd, 0, SEEK_CUR); + size = lseek(fd, 0, SEEK_END); + + if (origin == SEEK_SET) { + result = offset; + } + else if (origin == SEEK_CUR) { + result = offset + cur_pos; + } + else if (origin == SEEK_END) { + result = size + offset; + } - if (result < 0 && errno == 0) - errno = EINVAL; - if (result < 0) - { - /* Some filesystems on OSE does not handle seeking beyond EOF - * We handle this here, by localy storing offsets. */ - - if ((lseek(fd, 0, SEEK_END) < off) && errno == 78) - { - if (fdm == NULL) /* first time */ - { - if (l_init_local_fd() == -1) - { - errno = EINVAL; - check_error(-1, errInfo); - } + /* sanity check our result */ + if (size > result) { + return check_error(-1, errInfo); } - l_update_local_fd(fd, off, origin); - result = off; - } - else if (off < 0 && errno == ENOSYS) - { + + /* store the data localy */ + l_update_local_fd(fd, result, size); + + /* reset the original file position */ + if (origin != SEEK_END) { + lseek(fd, cur_pos, SEEK_SET); + } + } + else if (errno == 0) { errno = EINVAL; - return check_error(-1, errInfo); - } - else - { - return check_error(-1, errInfo); - } - } - if (new_location) { - *new_location = result; - } - return 1; + } + } + + if (new_location) { + *new_location = result; + } + + return 1; } +/***************************************************************************/ int efile_truncate_file(Efile_error* errInfo, int *fd, int flags) { -#ifndef NO_FTRUNCATE off_t offset; + struct fd_data *fd_data; - if (L_FD_EXISTS(fileno(fd))) - { - L_FD_INVALIDATE(fileno(fd)); + if ((fd_data = l_find_local_fd(*fd)) != NULL && L_FD_IS_VALID(fd_data)) { + offset = L_FD_CUR(fd_data); + } + else { + offset = lseek(*fd, 0, SEEK_CUR); } - return check_error((offset = lseek(*fd, 0, SEEK_CUR)) >= 0 && - ftruncate(*fd, offset) == 0 ? 1 : -1, errInfo); -#else - return 1; -#endif + return check_error(((offset >= 0) && + (ftruncate(*fd, offset) == 0)) ? 1 : -1, errInfo); } +/***************************************************************************/ + int efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) { -#ifndef __OSE__ - int len; - ASSERT(size > 0); - len = readlink(name, buffer, size-1); - if (len == -1) { - return check_error(-1, errInfo); - } - buffer[len] = '\0'; - return 1; -#else errno = ENOTSUP; return check_error(-1, errInfo); -#endif } +/***************************************************************************/ + int efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size) { @@ -1127,158 +1064,35 @@ efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size) return check_error(-1, errInfo); } +/***************************************************************************/ + int efile_link(Efile_error* errInfo, char* old, char* new) { -#ifndef __OSE__ - return check_error(link(old, new), errInfo); -#else errno = ENOTSUP; return check_error(-1, errInfo); -#endif } +/***************************************************************************/ + int efile_symlink(Efile_error* errInfo, char* old, char* new) { -#ifndef __OSE__ - return check_error(symlink(old, new), errInfo); -#else errno = ENOTSUP; return check_error(-1, errInfo); -#endif } +/***************************************************************************/ + int efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, - Sint64 length, int advise) + Sint64 length, int advise) { -#ifdef HAVE_POSIX_FADVISE return check_error(posix_fadvise(fd, offset, length, advise), errInfo); -#else - return check_error(0, errInfo); -#endif } -#ifdef HAVE_SENDFILE -/* For some reason the maximum size_t cannot be used as the max size - 3GB seems to work on all platforms */ -#define SENDFILE_CHUNK_SIZE ((1UL << 30) -1) - -/* - * sendfile: The implementation of the sendfile system call varies - * a lot on different *nix platforms so to make the api similar in all - * we have to emulate some things in linux and play with variables on - * bsd/darwin. - * - * All of the calls will split a command which tries to send more than - * SENDFILE_CHUNK_SIZE of data at once. - * - * On platforms where *nbytes of 0 does not mean the entire file, this is - * simulated. - * - * It could be possible to implement header/trailer in sendfile. Though - * you would have to emulate it in linux and on BSD/Darwin some complex - * calculations have to be made when using a non blocking socket to figure - * out how much of the header/file/trailer was sent in each command. - * - * The semantics of the API is this: - * Return value: 1 if all data was sent and the function does not need to - * be called again. 0 if an error occures OR if there is more data which - * has to be sent (EAGAIN or EINTR will be set appropriately) - * - * The amount of data written in a call is returned through nbytes. - * - */ - -int -efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd, - off_t *offset, Uint64 *nbytes, struct t_sendfile_hdtl* hdtl) -{ - Uint64 written = 0; -#if defined(__linux__) - ssize_t retval; - do { - /* check if *nbytes is 0 or greater than chunk size */ - if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE) - retval = sendfile(out_fd, in_fd, offset, SENDFILE_CHUNK_SIZE); - else - retval = sendfile(out_fd, in_fd, offset, *nbytes); - if (retval > 0) { - written += retval; - *nbytes -= retval; - } - } while (retval == SENDFILE_CHUNK_SIZE); - if (written != 0) { - /* -1 is not returned by the linux API so we have to simulate it */ - retval = -1; - errno = EAGAIN; - } -#elif defined(__sun) && defined(__SVR4) && defined(HAVE_SENDFILEV) - ssize_t retval; - size_t len; - sendfilevec_t fdrec; - fdrec.sfv_fd = in_fd; - fdrec.sfv_flag = 0; - do { - fdrec.sfv_off = *offset; - len = 0; - /* check if *nbytes is 0 or greater than chunk size */ - if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE) - fdrec.sfv_len = SENDFILE_CHUNK_SIZE; - else - fdrec.sfv_len = *nbytes; - retval = sendfilev(out_fd, &fdrec, 1, &len); - - /* Sometimes sendfilev can return -1 and still send data. - When that happens we just pretend that no error happend. */ - if (retval != -1 || errno == EAGAIN || errno == EINTR || - len != 0) { - *offset += len; - *nbytes -= len; - written += len; - if (errno != EAGAIN && errno != EINTR && len != 0) - retval = len; - } - } while (len == SENDFILE_CHUNK_SIZE); -#elif defined(DARWIN) - int retval; - off_t len; - do { - /* check if *nbytes is 0 or greater than chunk size */ - if(*nbytes > SENDFILE_CHUNK_SIZE) - len = SENDFILE_CHUNK_SIZE; - else - len = *nbytes; - retval = sendfile(in_fd, out_fd, *offset, &len, NULL, 0); - if (retval != -1 || errno == EAGAIN || errno == EINTR) { - *offset += len; - *nbytes -= len; - written += len; - } - } while (len == SENDFILE_CHUNK_SIZE); -#elif defined(__FreeBSD__) || defined(__DragonFly__) - off_t len; - int retval; - do { - if (*nbytes > SENDFILE_CHUNK_SIZE) - retval = sendfile(in_fd, out_fd, *offset, SENDFILE_CHUNK_SIZE, - NULL, &len, 0); - else - retval = sendfile(in_fd, out_fd, *offset, *nbytes, NULL, &len, 0); - if (retval != -1 || errno == EAGAIN || errno == EINTR) { - *offset += len; - *nbytes -= len; - written += len; - } - } while(len == SENDFILE_CHUNK_SIZE); -#endif - *nbytes = written; - return check_error(retval, errInfo); -} -#endif /* HAVE_SENDFILE */ +/***************************************************************************/ -#ifdef HAVE_POSIX_FALLOCATE static int call_posix_fallocate(int fd, Sint64 offset, Sint64 length) { @@ -1300,58 +1114,11 @@ call_posix_fallocate(int fd, Sint64 offset, Sint64 length) return ret; } -#endif /* HAVE_POSIX_FALLOCATE */ + +/***************************************************************************/ int efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length) { -#if defined HAVE_FALLOCATE - /* Linux specific, more efficient than posix_fallocate. */ - int ret; - - do { - ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, (off_t) offset, (off_t) length); - } while (ret != 0 && errno == EINTR); - -#if defined HAVE_POSIX_FALLOCATE - /* Fallback to posix_fallocate if available. */ - if (ret != 0) { - ret = call_posix_fallocate(fd, offset, length); - } -#endif - - return check_error(ret, errInfo); -#elif defined F_PREALLOCATE - /* Mac OS X specific, equivalent to posix_fallocate. */ - int ret; - fstore_t fs; - - memset(&fs, 0, sizeof(fs)); - fs.fst_flags = F_ALLOCATECONTIG; - fs.fst_posmode = F_VOLPOSMODE; - fs.fst_offset = (off_t) offset; - fs.fst_length = (off_t) length; - - ret = fcntl(fd, F_PREALLOCATE, &fs); - - if (-1 == ret) { - fs.fst_flags = F_ALLOCATEALL; - ret = fcntl(fd, F_PREALLOCATE, &fs); - -#if defined HAVE_POSIX_FALLOCATE - /* Fallback to posix_fallocate if available. */ - if (-1 == ret) { - ret = call_posix_fallocate(fd, offset, length); - } -#endif - } - - return check_error(ret, errInfo); -#elif defined HAVE_POSIX_FALLOCATE - /* Other Unixes, use posix_fallocate if available. */ return check_error(call_posix_fallocate(fd, offset, length), errInfo); -#else - errno = ENOTSUP; - return check_error(-1, errInfo); -#endif } diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index 8ffc05da99..06078858ca 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -101,6 +101,11 @@ check_error(int result, Efile_error *errInfo) return 1; } +int +efile_init() { + return 1; +} + int efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to create. */ diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index d693d7d593..480ba23239 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -196,6 +196,11 @@ set_error(Efile_error* errInfo) return 0; } +int +efile_init() { + return 1; +} + /* * A writev with Unix semantics, but with Windows arguments */ -- cgit v1.2.3 From 2145db903b665622ea885db175c5e606d9f840a4 Mon Sep 17 00:00:00 2001 From: Jonas Karlsson Date: Fri, 22 Nov 2013 09:50:34 +0100 Subject: ose: OSE port related cleanup and fixes Some OSE cross-chains have problems with system includes being used, so for atleast OSE specific parts we use "" instead of <>. --- erts/emulator/drivers/common/efile_drv.c | 12 ++++++++++-- erts/emulator/sys/ose/sys.c | 28 +++++++++------------------- erts/lib_src/ose/ethread.c | 31 ++++++++++++++----------------- 3 files changed, 33 insertions(+), 38 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 7ef985dc41..b62e9a0306 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -99,7 +99,16 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif + +#ifndef __OSE__ +#include +#include #include +#else +#include "ctype.h" +#include "sys/types.h" +#include "stdlib.h" +#endif /* Need (NON)BLOCKING macros for sendfile */ #ifndef WANT_NONBLOCKING @@ -113,8 +122,7 @@ #include "erl_threads.h" #include "gzio.h" #include "dtrace-wrapper.h" -#include -#include + void erl_exit(int n, char *fmt, ...); diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index ba8851e203..a62d7c150b 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -20,29 +20,19 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include "sys/time.h" +#include "time.h" +#include "sys/uio.h" +#include "termios.h" +#include "ctype.h" +#include "termios.h" -#ifdef ISC32 -#define _POSIX_SOURCE -#define _XOPEN_SOURCE -#endif - -#include -#include - -#include -#include -#include - -#ifdef ISC32 -#include -#endif - -#include #ifdef HAVE_FCNTL_H -#include +#include "fcntl.h" #endif + #ifdef HAVE_SYS_IOCTL_H -#include +#include "sys/ioctl.h" #endif #define ERTS_WANT_BREAK_HANDLING diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c index 41c3392677..f3cb4ed0ec 100644 --- a/erts/lib_src/ose/ethread.c +++ b/erts/lib_src/ose/ethread.c @@ -26,21 +26,21 @@ #include "config.h" #endif -#include +#include "stdio.h" #ifdef ETHR_TIME_WITH_SYS_TIME -# include -# include +# include "time.h" +# include "sys/time.h" #else # ifdef ETHR_HAVE_SYS_TIME_H -# include +# include "sys/time.h" # else -# include +# include "time.h" # endif #endif -#include -#include +#include "sys/types.h" +#include "unistd.h" -#include +#include "limits.h" #define ETHR_INLINE_FUNC_NAME_(X) X ## __ #define ETHREAD_IMPL__ @@ -380,19 +380,16 @@ ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, /*erts_fprintf(stderr, "creating process %s / stack %d\n", opts->name, use_stack_size);*/ -#if 0 ramlog_printf("[0x%x] process '%s', coreNo = %u\n", current_process(), opts->name, opts->coreNo); -#endif - tid->id = create_process(/*OS_PRI_PROC*/OS_BG_PROC, opts->name, thr_wrapper, - use_stack_size, /*opts->prio+5*/31, 0, - blockId(), NULL, 0, 0); + tid->id = create_process(OS_PRI_PROC, opts->name, thr_wrapper, + use_stack_size, /*opts->prio+5*/24, 0, + get_bid(current_process()), NULL, 0, 0); - /* For now we do not attempt to bind schedulers to different cores. if (ose_bind_process(tid->id, opts->coreNo)) { - printf("[0x%x] Binding pid 0x%x (%s) to core no %u.\n", - current_process(), tid->id, opts->name, opts->coreNo); - }*/ + printf("[0x%x] Binding pid 0x%x (%s) to core no %u.\n", + current_process(), tid->id, opts->name, opts->coreNo); + } /*FIXME!!! Normally this shouldn't be used in shared mode. Still there is * a problem with stdin fd in fd_ processes which should be further -- cgit v1.2.3 From 8080aa615ad9d82bfb7e2d9b92d45b9c81a95929 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 11 Dec 2013 17:17:56 +0100 Subject: ose: Update erl_assert_error after R16B03 api change --- erts/emulator/sys/ose/sys.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index a62d7c150b..b786d19ecf 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -1647,14 +1647,16 @@ int fd; extern int erts_initialized; void -erl_assert_error(char* expr, char* file, int line) +erl_assert_error(const char* expr, const char* func, + const char* file, int line) { fflush(stdout); - fprintf(stderr, "Assertion failed: %s in %s, line %d\n", - expr, file, line); + fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", + file, func, line, expr); fflush(stderr); - ramlog_printf("%d: Assertion failed: %s in %s, line %d\n", - current_process(), expr, file, line); + ramlog_printf("%s:%d:%s() Assertion failed: %s\n", + file, func, line, expr); + abort(); } -- cgit v1.2.3 From 809ee10f37bac29b3056f4fb09c072b951e29f0c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 14 Nov 2013 18:18:12 +0100 Subject: ose: Fix starting with pm_create For some reason starting with pm_create offsets the input argument list by one, so we have to adjust them before calling erl_start. --- erts/emulator/sys/ose/erl_main.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/erl_main.c b/erts/emulator/sys/ose/erl_main.c index a17fc7eabc..03119c3fec 100644 --- a/erts/emulator/sys/ose/erl_main.c +++ b/erts/emulator/sys/ose/erl_main.c @@ -17,8 +17,16 @@ * %CopyrightEnd% */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include +#include "sys.h" +#include "erl_vm.h" +#include "global.h" +#include "ose.h" + int main(int argc, char **argv) { @@ -30,8 +38,8 @@ main(int argc, char **argv) { char **tmp_argv = malloc(sizeof(char*)*(argc+1)); for (i = 0; i < argc; i++) tmp_argv[i+1] = argv[i]; - tmp_argv = "beam"; - erl_start(argc,tmp_argv); + tmp_argv[0] = "beam"; + erl_start(argc+1,tmp_argv); free(tmp_argv); } else { erl_start(argc,argv); -- cgit v1.2.3 From 6930aacf7236bbf1db67c8ad1bc1953127ac43f9 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 11 Dec 2013 19:08:10 +0100 Subject: ose: Shutdown cleanly --- erts/lib_src/ose/ethread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c index f3cb4ed0ec..bde85f9f87 100644 --- a/erts/lib_src/ose/ethread.c +++ b/erts/lib_src/ose/ethread.c @@ -180,7 +180,7 @@ static OS_PROCESS(thr_wrapper) res = result == 0 ? (*thr_func)(arg) : NULL; - thr_exit_cleanup(&my_tid, res); + ethr_thr_exit(&res); } /* internal exports */ -- cgit v1.2.3 From c64851d619fb916362abc8db9c28534eff39f53c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 10 Jan 2014 17:17:58 +0100 Subject: ose: Rewrite resolve_signal API for ose drivers This new API has less impact on the check_io code and also removes the callback from ErlDrvEntry. The downside is that you have to give the resolve function when creating each event. Also the mode if the resolve was removed as this mimics the win32 code and decreases complexity. --- erts/emulator/beam/erl_driver.h | 21 ++---- erts/emulator/beam/global.h | 3 - erts/emulator/beam/io.c | 3 - erts/emulator/drivers/ose/ose_signal_drv.c | 41 +++++----- erts/emulator/sys/common/erl_check_io.c | 99 ++++-------------------- erts/emulator/sys/common/erl_poll.h | 20 ++++- erts/emulator/sys/ose/erl_ose_sys.h | 14 ---- erts/emulator/sys/ose/erl_poll.c | 116 ++++++++++++++++++----------- erts/emulator/sys/ose/sys.c | 49 ++++++------ 9 files changed, 158 insertions(+), 208 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 9ede2982de..a15b0e17f5 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -271,14 +271,6 @@ typedef struct ErlDrvCond_ ErlDrvCond; typedef struct ErlDrvRWLock_ ErlDrvRWLock; typedef int ErlDrvTSDKey; -/* - * Potential OSE signals - */ -#ifdef __OSE__ -typedef union SIGNAL OseSignal; -#endif - - /* * */ @@ -353,9 +345,6 @@ typedef struct erl_drv_entry { /* Called on behalf of driver_select when it is safe to release 'event'. A typical unix driver would call close(event) */ -#ifdef __OSE__ - int (*resolve_signal)(OseSignal* sig, int* mode); -#endif /* When adding entries here, dont forget to pad in obsolete/driver.h */ } ErlDrvEntry; @@ -691,11 +680,13 @@ EXTERN int erl_drv_putenv(char *key, char *value); EXTERN int erl_drv_getenv(char *key, char *value, size_t *value_size); #ifdef __OSE__ -EXTERN OseSignal *erl_drv_ose_get_output_signal(ErlDrvEvent ev); -EXTERN OseSignal *erl_drv_ose_get_input_signal(ErlDrvEvent ev); -EXTERN ErlDrvEvent erl_drv_ose_event_alloc(SIGSELECT sig,int id); +typedef ErlDrvUInt ErlDrvOseEventId; +EXTERN union SIGNAL *erl_drv_ose_get_signal(ErlDrvEvent ev); +EXTERN ErlDrvEvent erl_drv_ose_event_alloc(SIGSELECT sig,ErlDrvOseEventId id, + ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig)); EXTERN void erl_drv_ose_event_free(ErlDrvEvent ev); -EXTERN void erl_drv_ose_event_fetch(ErlDrvEvent ev, SIGSELECT *sig,int *id); +EXTERN void erl_drv_ose_event_fetch(ErlDrvEvent ev, SIGSELECT *sig, + ErlDrvOseEventId *id); #endif #endif /* !ERL_DRIVER_TYPES_ONLY */ diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 5a97ac5892..8fcb95d0e2 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -160,9 +160,6 @@ struct erts_driver_t_ { void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data); /* Might be NULL */ void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor); void (*stop_select)(ErlDrvEvent event, void*); /* Might be NULL */ -#ifdef __OSE__ - int (*resolve_signal)(OseSignal* sig, int* mode); -#endif }; extern erts_driver_t *driver_list; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 09681f167d..cd5060ebb3 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -7342,9 +7342,6 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle) drv->stop_select = de->stop_select; else drv->stop_select = no_stop_select_callback; -#ifdef __OSE__ - drv->resolve_signal = de->resolve_signal; -#endif if (!de->init) return 0; diff --git a/erts/emulator/drivers/ose/ose_signal_drv.c b/erts/emulator/drivers/ose/ose_signal_drv.c index c1d861cc5a..46890a1503 100644 --- a/erts/emulator/drivers/ose/ose_signal_drv.c +++ b/erts/emulator/drivers/ose/ose_signal_drv.c @@ -392,6 +392,19 @@ static int drv_init(void) { return 0; } +/* Signal resolution callback */ +static ErlDrvOseEventId resolve_signal(union SIGNAL* osig) { + union SIGNAL *sig = osig; + if (sig->signo == ERTS_SIGNAL_OSE_DRV_HUNT || + sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) { + return sig->async.spid; + } + DEBUGP("%p: Got signal %d sent to %p from 0x%p\n", + current_process(),sig->signo,addressee(&sig),sender(&sig)); + return addressee(&sig); +} + + /** * Start routine for the driver **/ @@ -488,11 +501,13 @@ static void outputv(ErlDrvData driver_data, ErlIOVec *ev) DEBUGP("0x%x: open\n",ctxt->spid); ctxt->perm_events[1] = - erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH,(int)ctxt->spid); + erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH,(int)ctxt->spid, + resolve_signal); driver_select(ctxt->port,ctxt->perm_events[1],ERL_DRV_READ|ERL_DRV_USE,1); ctxt->perm_events[0] = - erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_HUNT,(int)ctxt->spid); + erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_HUNT,(int)ctxt->spid, + resolve_signal); driver_select(ctxt->port,ctxt->perm_events[0],ERL_DRV_READ|ERL_DRV_USE,1); start(ctxt->spid); @@ -669,7 +684,8 @@ static void outputv(ErlDrvData driver_data, ErlIOVec *ev) EV_GET_UINT32(ev,&signo,&p,&q); } else if (signo < tmp_signo || !ctxt->events) { /* New signal to select on */ - events[i] = erl_drv_ose_event_alloc(signo,(int)ctxt->spid); + events[i] = erl_drv_ose_event_alloc(signo,(int)ctxt->spid, + resolve_signal); driver_select(ctxt->port,events[i++],ERL_DRV_READ|ERL_DRV_USE,1); EV_GET_UINT32(ev,&signo,&p,&q); } else { @@ -708,7 +724,7 @@ static void outputv(ErlDrvData driver_data, ErlIOVec *ev) static void ready_input(ErlDrvData driver_data, ErlDrvEvent event) { driver_context_t *ctxt = (driver_context_t *)driver_data; - union SIGNAL *sig = erl_drv_ose_get_input_signal(event); + union SIGNAL *sig = erl_drv_ose_get_signal(event); while (sig != NULL) { @@ -800,7 +816,7 @@ static void ready_input(ErlDrvData driver_data, ErlDrvEvent event) } free_buf(&sig); - sig = erl_drv_ose_get_input_signal(event); + sig = erl_drv_ose_get_signal(event); } } @@ -858,17 +874,6 @@ static void stop_select(ErlDrvEvent event, void *reserved) erl_drv_ose_event_free(event); } -static int resolve_signal(OseSignal* osig, int *mode) { - union SIGNAL *sig = osig; - if (sig->signo == ERTS_SIGNAL_OSE_DRV_HUNT || - sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) { - return sig->async.spid; - } - DEBUGP("%p: Got signal %d sent to %p from 0x%p\n", - current_process(),sig->signo,addressee(&sig),sender(&sig)); - return addressee(&sig); -} - /** * Setup the driver entry for the Erlang runtime **/ @@ -884,6 +889,6 @@ ErlDrvEntry ose_signal_driver_entry = { .major_version = ERL_DRV_EXTENDED_MAJOR_VERSION, .minor_version = ERL_DRV_EXTENDED_MINOR_VERSION, .driver_flags = ERL_DRV_FLAG_USE_PORT_LOCKING, - .stop_select = stop_select, - .resolve_signal = resolve_signal + .stop_select = stop_select }; + diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index dab056e2df..245841a768 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -79,11 +79,7 @@ typedef char EventStateFlags; #define ERTS_CIO_POLL_INIT ERTS_POLL_EXPORT(erts_poll_init) #define ERTS_CIO_POLL_INFO ERTS_POLL_EXPORT(erts_poll_info) -#ifdef __OSE__ -#define GET_FD(fd) fd->id -#else #define GET_FD(fd) fd -#endif static struct pollset_info { @@ -442,11 +438,7 @@ deselect(ErtsDrvEventState *state, int mode) } } - state->events = ERTS_CIO_POLL_CTL(pollset.ps, state->fd, rm_events, 0, &do_wake -#ifdef __OSE__ - ,NULL -#endif - ); + state->events = ERTS_CIO_POLL_CTL(pollset.ps, state->fd, rm_events, 0, &do_wake); if (!(state->events)) { switch (state->type) { @@ -595,11 +587,7 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, wake_poller = 1; } - new_events = ERTS_CIO_POLL_CTL(pollset.ps, state->fd, ctl_events, on, &wake_poller -#ifdef __OSE__ - ,prt->drv_ptr->resolve_signal -#endif - ); + new_events = ERTS_CIO_POLL_CTL(pollset.ps, state->fd, ctl_events, on, &wake_poller); if (new_events & (ERTS_POLL_EV_ERR|ERTS_POLL_EV_NVAL)) { if (state->type == ERTS_EV_TYPE_DRV_SEL && !state->events) { @@ -984,9 +972,6 @@ print_select_op(erts_dsprintf_buf_t *dsbufp, mode & ERL_DRV_WRITE ? " ERL_DRV_WRITE" : "", mode & ERL_DRV_USE ? " ERL_DRV_USE" : "", mode & (ERL_DRV_USE_NO_CALLBACK & ~ERL_DRV_USE) ? "_NO_CALLBACK" : "", -#ifdef __OSE__ - fd->signo, -#endif on); print_driver_name(dsbufp, pp != ERTS_INVALID_ERL_DRV_PORT ? pp->common.id : NIL); erts_dsprintf(dsbufp, "driver %T ", pp != ERTS_INVALID_ERL_DRV_PORT ? pp->common.id : NIL); @@ -1417,20 +1402,26 @@ stale_drv_select(Eterm id, ErtsDrvEventState *state, int mode) } #ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS + #ifdef __OSE__ static SafeHashValue drv_ev_state_hash(void *des) { - SafeHashValue val = (SafeHashValue) ((ErtsDrvEventState *) des)->fd; - return val ^ (val >> 8); /* Good enough for aligned pointer values? */ + ErtsSysFdType fd = ((ErtsDrvEventState *) des)->fd; + /* We use hash on signo ^ id in order for steal to happen when the + same signo + fd is selected on by two different ports */ + SafeHashValue val = (SafeHashValue)(fd->signo ^ fd->id); + return val ^ (val >> 8); } static int drv_ev_state_cmp(void *des1, void *des2) { - return ( ((((ErtsDrvEventState *) des1)->fd->id == ((ErtsDrvEventState *) des2)->fd->id) - && (((ErtsDrvEventState *) des1)->fd->signo == ((ErtsDrvEventState *) des2)->fd->signo)) - ? 0 : 1); + ErtsSysFdType fd1 = ((ErtsDrvEventState *) des1)->fd; + ErtsSysFdType fd2 = ((ErtsDrvEventState *) des2)->fd; + if (fd1->signo == fd2->signo && fd1->id == fd2->id) + return 0; + return 1; } -#else +#else /* !__OSE__ && !ERTS_SYS_CONTINOUS_FD_NUMBERS i.e. probably windows */ static SafeHashValue drv_ev_state_hash(void *des) { SafeHashValue val = (SafeHashValue) ((ErtsDrvEventState *) des)->fd; @@ -1473,69 +1464,7 @@ static void drv_ev_state_free(void *des) erts_smp_spin_unlock(&state_prealloc_lock); } #endif -#ifdef __OSE__ -OseSignal *erl_drv_ose_get_input_signal(ErlDrvEvent drv_ev) { - struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; - ethr_mutex_lock(&ev->mtx); - if (ev->imsgs == NULL) { - ethr_mutex_unlock(&ev->mtx); - return NULL; - } else { - ErtsPollOseMsgList *msg = ev->imsgs; - OseSignal *sig = (OseSignal*)msg->data; - ASSERT(msg->data); - ev->imsgs = msg->next; - ethr_mutex_unlock(&ev->mtx); - erts_free(ERTS_ALC_T_FD_SIG_LIST,msg); - restore(sig); - return sig; - } -} - -OseSignal *erl_drv_ose_get_output_signal(ErlDrvEvent drv_ev) { - struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; - ethr_mutex_lock(&ev->mtx); - if (ev->omsgs == NULL) { - ethr_mutex_unlock(&ev->mtx); - return NULL; - } else { - ErtsPollOseMsgList *msg = ev->omsgs; - OseSignal *sig = (OseSignal*)msg->data; - ASSERT(msg->data); - ev->omsgs = msg->next; - ethr_mutex_unlock(&ev->mtx); - erts_free(ERTS_ALC_T_FD_SIG_LIST,msg); - restore(sig); - return sig; - } -} -ErlDrvEvent erl_drv_ose_event_alloc(SIGSELECT signo, int id) { - struct erts_sys_fd_type *ev = erts_alloc(ERTS_ALC_T_DRV_EV, - sizeof(struct erts_sys_fd_type)); - ev->signo = signo; - ev->id = id; - ev->imsgs = NULL; - ev->omsgs = NULL; - ethr_mutex_init(&ev->mtx); - return (ErlDrvEvent)ev; -} - -void erl_drv_ose_event_free(ErlDrvEvent drv_ev) { - struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; - ethr_mutex_destroy(&ev->mtx); - erts_free(ERTS_ALC_T_DRV_EV,ev); -} - -void erl_drv_ose_event_fetch(ErlDrvEvent drv_ev, SIGSELECT *signo, int *id) { - struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; - if (signo) - *signo = ev->signo; - if (id) - *id = ev->id; -} - -#endif void ERTS_CIO_EXPORT(erts_init_check_io)(void) { diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index e1ea8fb207..ce8dcd3f90 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -106,6 +106,23 @@ typedef Uint32 ErtsPollEvents; #define ERTS_POLL_EV_ERR 4 #define ERTS_POLL_EV_NVAL 8 +#ifdef __OSE__ + +typedef struct ErtsPollOseMsgList_ { + struct ErtsPollOseMsgList_ *next; + union SIGNAL *data; +} ErtsPollOseMsgList; + +struct erts_sys_fd_type { + SIGSELECT signo; + ErlDrvOseEventId id; + ErtsPollOseMsgList *msgs; + ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig); + ethr_mutex mtx; +}; + +#endif + #elif ERTS_POLL_USE_EPOLL /* --- epoll ------------------------------- */ #include @@ -229,9 +246,6 @@ ErtsPollEvents ERTS_POLL_EXPORT(erts_poll_control)(ErtsPollSet, ErtsPollEvents, int on, int* wake_poller -#ifdef __OSE__ - ,int (*decode)(OseSignal* sig, int* mode) -#endif ); void ERTS_POLL_EXPORT(erts_poll_controlv)(ErtsPollSet, ErtsPollControlEntry [], diff --git a/erts/emulator/sys/ose/erl_ose_sys.h b/erts/emulator/sys/ose/erl_ose_sys.h index 8c72afa9a5..db8607b650 100644 --- a/erts/emulator/sys/ose/erl_ose_sys.h +++ b/erts/emulator/sys/ose/erl_ose_sys.h @@ -56,20 +56,6 @@ # include "sys/mman.h" #endif -typedef struct ErtsPollOseMsgList_ { - struct ErtsPollOseMsgList_ *next; - void *data; -} ErtsPollOseMsgList; - -struct erts_sys_fd_type { - SIGSELECT signo; - int id; - ErtsPollOseMsgList *imsgs; - ErtsPollOseMsgList *omsgs; - ethr_mutex mtx; -}; - - /* * Min number of async threads */ diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c index b1e256afc3..78dc275c06 100644 --- a/erts/emulator/sys/ose/erl_poll.c +++ b/erts/emulator/sys/ose/erl_poll.c @@ -103,7 +103,7 @@ typedef struct erts_sigsel_info_ ErtsSigSelInfo; struct erts_sigsel_info_ { ErtsSigSelInfo *next; SIGSELECT signo; - int (*decode)(OseSignal* sig, int* mode); + ErlDrvOseEventId (*decode)(union SIGNAL* sig); ErtsSigSelItem *fds; }; @@ -131,8 +131,10 @@ static int max_fds = -1; /* signal list prototypes */ static ErtsSigSelInfo *get_sigsel_info(ErtsPollSet ps, SIGSELECT signo); static ErtsSigSelItem *get_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd); -static ErtsSigSelInfo *add_sigsel_info(ErtsPollSet ps, ErtsSysFdType fd, int (*decode)(OseSignal* sig, int* mode)); -static ErtsSigSelItem *add_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd, int (*decode)(OseSignal* sig, int* mode)); +static ErtsSigSelInfo *add_sigsel_info(ErtsPollSet ps, ErtsSysFdType fd, + ErlDrvOseEventId (*decode)(union SIGNAL* sig)); +static ErtsSigSelItem *add_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd, + ErlDrvOseEventId (*decode)(union SIGNAL* sig)); static int del_sigsel_info(ErtsPollSet ps, ErtsSigSelInfo *info); static int del_sigsel_item(ErtsPollSet ps, ErtsSigSelItem *item); static int update_sigsel(ErtsPollSet ps); @@ -170,7 +172,7 @@ get_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd) { static ErtsSigSelInfo * add_sigsel_info(ErtsPollSet ps, ErtsSysFdType fd, - int (*decode)(OseSignal* sig, int* mode)) { + ErlDrvOseEventId (*decode)(union SIGNAL* sig)) { ErtsSigSelInfo *info = SEL_ALLOC(ERTS_ALC_T_POLLSET, sizeof(ErtsSigSelInfo)); info->next = ps->info; @@ -184,7 +186,7 @@ add_sigsel_info(ErtsPollSet ps, ErtsSysFdType fd, static ErtsSigSelItem * add_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd, - int (*decode)(OseSignal* sig, int* mode)) { + ErlDrvOseEventId (*decode)(union SIGNAL* sig)) { ErtsSigSelInfo *info = get_sigsel_info(ps,fd->signo); ErtsSigSelItem *item = SEL_ALLOC(ERTS_ALC_T_POLLSET, sizeof(ErtsSigSelItem)); @@ -394,8 +396,7 @@ void erts_poll_interrupt_timed(ErtsPollSet ps,int set,erts_short_time_t msec) { } ErtsPollEvents erts_poll_control(ErtsPollSet ps, ErtsSysFdType fd, - ErtsPollEvents pe, int on, int* do_wake, - int(*decode)(OseSignal* sig, int* mode)) { + ErtsPollEvents pe, int on, int* do_wake) { ErtsSigSelItem *curr; ErtsPollEvents new_events; int old_sig_count; @@ -406,11 +407,17 @@ ErtsPollEvents erts_poll_control(ErtsPollSet ps, ErtsSysFdType fd, ERTS_POLLSET_LOCK(ps); + if (on && (pe & ERTS_POLL_EV_IN) && (pe & ERTS_POLL_EV_OUT)) { + /* Check to make sure both in and out are not used at the same time */ + new_events = ERTS_POLL_EV_NVAL; + goto done; + } + curr = get_sigsel_item(ps, fd); old_sig_count = ps->sig_count; if (curr == NULL && on) { - curr = add_sigsel_item(ps, fd, decode); + curr = add_sigsel_item(ps, fd, fd->resolve_signal); } else if (curr == NULL && !on) { new_events = ERTS_POLL_EV_NVAL; goto done; @@ -451,7 +458,7 @@ int erts_poll_wait(ErtsPollSet ps, SysTimeval *utvp) { int res = ETIMEDOUT, no_fds, currid = 0; OSTIME timeout; - OseSignal *sig; + union SIGNAL *sig; // HARDTRACEF("%ux: In erts_poll_wait",ps); if (ps->interrupt == (PROCESS)0) ps->interrupt = current_process(); @@ -515,8 +522,7 @@ int erts_poll_wait(ErtsPollSet ps, } { ErtsSigSelInfo *info = get_sigsel_info(ps, sig->sig_no); - int mode = -1; - struct erts_sys_fd_type fd = { sig->sig_no, info->decode(sig, &mode) }; + struct erts_sys_fd_type fd = { sig->sig_no, info->decode(sig) }; ErtsSigSelItem *item = get_sigsel_item(ps, &fd); ASSERT(sig); @@ -546,38 +552,16 @@ int erts_poll_wait(ErtsPollSet ps, erts_send_error_to_logger_nogl(dsbufp); timeout = 0; ASSERT(0); - } else if (mode == -1 && item->events == (ERTS_POLL_EV_IN|ERTS_POLL_EV_OUT)) { - erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - erts_dsprintf( - dsbufp, - "erts_poll_wait() failed: found ambigous signal id %d (signo %u) " - "(curr_proc 0x%x /sender 0x%x)\n You have to give a specify a mode " - "in the resolve_signal callback for this signal.\n", - fd.id, fd.signo, current_process(), sender(&sig)); - erts_send_error_to_logger_nogl(dsbufp); - timeout = 0; - ASSERT(0); - } else { + } else { int i; struct erts_sys_fd_type *fd = NULL; ErtsPollOseMsgList *tl,*new; - /* Figure out which mode to set and which queue to store - the signal in */ - if (mode == -1) - mode = item->events; - else if (mode == 0) - mode = ERTS_POLL_EV_IN; - else if (mode == 1) - mode = ERTS_POLL_EV_OUT; - else - abort(); - /* Check if this fd has already been triggered by a previous signal */ for (i = 0; i < currid;i++) { if (pr[i].fd == item->fd) { fd = pr[i].fd; - pr[i].events |= mode; + pr[i].events |= item->events; break; } } @@ -585,7 +569,7 @@ int erts_poll_wait(ErtsPollSet ps, /* First time this fd is triggered */ if (fd == NULL) { pr[currid].fd = item->fd; - pr[currid].events = mode; + pr[currid].events = item->events; fd = item->fd; timeout = 0; currid++; @@ -597,16 +581,10 @@ int erts_poll_wait(ErtsPollSet ps, new->data = sig; ethr_mutex_lock(&fd->mtx); - if (mode & ERTS_POLL_EV_IN) - tl = fd->imsgs; - else if (mode & ERTS_POLL_EV_OUT) - tl = fd->omsgs; + tl = fd->msgs; if (tl == NULL) { - if (mode & ERTS_POLL_EV_IN) - fd->imsgs = new; - else if (mode & ERTS_POLL_EV_OUT) - fd->omsgs = new; + fd->msgs = new; } else { while (tl->next != NULL) tl = tl->next; @@ -742,3 +720,53 @@ void erts_poll_init(void) HARDTRACEF("Out %s", __FUNCTION__); } + + +/* OSE driver functions */ + +union SIGNAL *erl_drv_ose_get_signal(ErlDrvEvent drv_ev) { + struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; + ethr_mutex_lock(&ev->mtx); + if (ev->msgs == NULL) { + ethr_mutex_unlock(&ev->mtx); + return NULL; + } else { + ErtsPollOseMsgList *msg = ev->msgs; + union SIGNAL *sig = (union SIGNAL*)msg->data; + ASSERT(msg->data); + ev->msgs = msg->next; + ethr_mutex_unlock(&ev->mtx); + erts_free(ERTS_ALC_T_FD_SIG_LIST,msg); + restore(sig); + return sig; + } +} + +ErlDrvEvent +erl_drv_ose_event_alloc(SIGSELECT signo, ErlDrvOseEventId id, + ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig)) { + struct erts_sys_fd_type *ev = erts_alloc(ERTS_ALC_T_DRV_EV, + sizeof(struct erts_sys_fd_type)); + ev->signo = signo; + ev->id = id; + ev->msgs = NULL; + ev->resolve_signal = resolve_signal; + ethr_mutex_init(&ev->mtx); + return (ErlDrvEvent)ev; +} + +void erl_drv_ose_event_free(ErlDrvEvent drv_ev) { + struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; + ASSERT(ev->msgs == NULL); + ethr_mutex_destroy(&ev->mtx); + erts_free(ERTS_ALC_T_DRV_EV,ev); +} + +void erl_drv_ose_event_fetch(ErlDrvEvent drv_ev, SIGSELECT *signo, + ErlDrvOseEventId *id) { + struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; + if (signo) + *signo = ev->signo; + if (id) + *id = ev->id; +} diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index b786d19ecf..ad82e4587d 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -576,7 +576,7 @@ static void ready_output(ErlDrvData, ErlDrvEvent); static void output(ErlDrvData, char*, ErlDrvSizeT); static void outputv(ErlDrvData, ErlIOVec*); static void stop_select(ErlDrvEvent, void*); -static int resolve_signal(OseSignal* sig, int *mode) { +static ErlDrvOseEventId resolve_signal(union SIGNAL* sig) { return sig->sig_no == ERTS_SIGNAL_FD_DRV_ASYNC ? sig->sys_async.type : -1; } @@ -605,8 +605,7 @@ struct erl_drv_entry spawn_driver_entry = { ERL_DRV_EXTENDED_MINOR_VERSION, ERL_DRV_FLAG_USE_PORT_LOCKING, NULL, NULL, - stop_select, - resolve_signal + stop_select }; struct erl_drv_entry fd_driver_entry = { NULL, @@ -631,8 +630,7 @@ struct erl_drv_entry fd_driver_entry = { 0, /* ERL_DRV_FLAGs */ NULL, /* handle2 */ NULL, /* process_exit */ - stop_select, - resolve_signal + stop_select }; static int set_driver_data(ErlDrvPort port_num, @@ -645,7 +643,7 @@ static int set_driver_data(ErlDrvPort port_num, { Port *prt; ErtsSysReportExit *report_exit; - OseSignal *sig; + union SIGNAL *sig; /*erts_fprintf(stderr, " %s / pid %x / ofd %d / ifd %d\n", __FUNCTION__, current_process(), ofd, ifd);*/ @@ -664,10 +662,12 @@ static int set_driver_data(ErlDrvPort port_num, if (read_write & DO_READ) report_exit->in_sig_descr = - erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ifd); + erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ifd, + resolve_signal); if (read_write & DO_WRITE) report_exit->out_sig_descr = - erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ofd); + erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ofd, + resolve_signal); report_exit_list = report_exit; } @@ -684,7 +684,8 @@ static int set_driver_data(ErlDrvPort port_num, driver_data[ifd].alive = 1; driver_data[ifd].status = 0; driver_data[ifd].in_sig_descr = - erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ifd); + erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ifd, + resolve_signal); driver_data[ifd].in_proc = create_process(OS_PRI_PROC,"beam_fd_reader", fd_reader_process, 0x800, @@ -700,7 +701,8 @@ static int set_driver_data(ErlDrvPort port_num, if (read_write & DO_WRITE) { driver_data[ifd].ofd = ofd; driver_data[ifd].out_sig_descr = - erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ofd); + erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ofd, + resolve_signal); driver_data[ifd].pdl = driver_pdl_create(port_num); driver_data[ifd].out_proc = create_process(OS_PRI_PROC,"beam_fd_writer", @@ -729,7 +731,8 @@ static int set_driver_data(ErlDrvPort port_num, driver_data[ofd].alive = 1; driver_data[ofd].status = 0; driver_data[ofd].in_sig_descr = - erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ofd); + erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ofd, + resolve_signal); driver_data[ofd].out_sig_descr = driver_data[ofd].in_sig_descr; driver_data[ofd].out_proc = create_process(OS_PRI_PROC, "beam_fd_writer", @@ -783,7 +786,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, } OS_PROCESS(fd_reader_process) { - OseSignal *sig; + union SIGNAL *sig; PROCESS parent; int fd; byte *read_buf; @@ -844,7 +847,7 @@ OS_PROCESS(fd_reader_process) { } OS_PROCESS(fd_writer_process) { - OseSignal *sig; + union SIGNAL *sig; PROCESS parent; int fd; SIGSELECT sigsel[] = { 1, ERTS_SIGNAL_FD_DRV_CONFIG, @@ -1105,7 +1108,7 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) driver_pdl_unlock(driver_data[fd].pdl); } else { - OseSignal *sig; + union SIGNAL *sig; /* fprintf(stderr,"0x%x: outputv, enq+sel\n", current_process()); */ driver_enqv(ix, ev, 0); /* n is the skip value */ driver_pdl_unlock(driver_data[fd].pdl); @@ -1151,7 +1154,7 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) set_busy_port(ix, 1); } else { - OseSignal *sig; + union SIGNAL *sig; /* fprintf(stderr,"0x%x: output, enq+select\n", current_process()); */ #if 0 iv[0].iov_base = lbp; @@ -1219,13 +1222,13 @@ static int port_inp_failure(ErlDrvPort port_num, ErlDrvEvent ready_fd, int res) } static int async_read(ErlDrvEvent fd, byte *buff, int size) { - OseSignal *sigptr = erl_drv_ose_get_input_signal(fd); + union SIGNAL *sigptr = erl_drv_ose_get_signal(fd); int res = sigptr->sys_async.res; if (res > 0) memcpy(buff,sigptr->sys_async.buff,sigptr->sys_async.res); errno = sigptr->sys_async.errno_copy; send(&sigptr,sender(&sigptr)); - ASSERT(erl_drv_ose_get_input_signal(fd) == NULL); + ASSERT(erl_drv_ose_get_signal(fd) == NULL); return res; } @@ -1360,7 +1363,7 @@ static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) { int fd = (int)(long)e; ErlDrvPort ix = driver_data[fd].port_num; - OseSignal *sigptr = erl_drv_ose_get_output_signal(ready_fd); + union SIGNAL *sigptr = erl_drv_ose_get_signal(ready_fd); ssize_t n; struct iovec* iv; int vsize; @@ -1374,7 +1377,7 @@ static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); set_busy_port(ix, 0); free_buf(&sigptr); - if ((sigptr = erl_drv_ose_get_output_signal(ready_fd)) == NULL) + if ((sigptr = erl_drv_ose_get_signal(ready_fd)) == NULL) return; /* 0; */ continue; } @@ -1384,7 +1387,7 @@ static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) if (errno == ERRNO_BLOCK || errno == EINTR) { /* fprintf(stderr,"0x%x: ready_output, send to %x\n", current_process(),driver_data[fd].out_proc);*/ send(&sigptr,driver_data[fd].out_proc); - if ((sigptr = erl_drv_ose_get_output_signal(ready_fd)) == NULL) + if ((sigptr = erl_drv_ose_get_signal(ready_fd)) == NULL) return; /* 0; */ continue; } else { @@ -1393,7 +1396,7 @@ static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) free_buf(&sigptr); driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); driver_failure_posix(ix, res); - if ((sigptr = erl_drv_ose_get_output_signal(ready_fd)) == NULL) + if ((sigptr = erl_drv_ose_get_signal(ready_fd)) == NULL) return; /* -1; */ continue; } @@ -1410,7 +1413,7 @@ static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) else continue; } - sigptr = erl_drv_ose_get_output_signal(ready_fd); + sigptr = erl_drv_ose_get_signal(ready_fd); } return; /* 0; */ } @@ -1752,7 +1755,7 @@ erts_sys_main_thread(void) while (1) { static const SIGSELECT sigsel[] = {0}; - OseSignal *msg = receive(sigsel); + union SIGNAL *msg = receive(sigsel); fprintf(stderr,"Main thread got message %d from 0x%x!!\r\n", msg->sig_no, sender(&msg)); -- cgit v1.2.3 From b309ad9b4a3e4ff2d6d3a6e6270d37355a798bb1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 10 Jan 2014 17:16:15 +0100 Subject: ose: Fix check for HAVE_OSE_SPI_H --- erts/aclocal.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 09d0f0194c..c51c26794a 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1112,7 +1112,7 @@ case "$THR_LIB_NAME" in [Define if you have OSE style threads]) ETHR_THR_LIB_BASE_DIR=ose AC_CHECK_HEADER(ose_spi/ose_spi.h, - AC_DEFINE(HAVE_OSE_SPI, 1, + AC_DEFINE(HAVE_OSE_SPI_H, 1, [Define if you have the "ose_spi/ose_spi.h" header file.])) ;; esac -- cgit v1.2.3 From cd69c2a54201b4e0c4ba86d4248937120e1957e7 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 8 Jan 2014 18:54:15 +0100 Subject: ose: Create OSE application Create an specific OSE application that mainly contains documentation around the OSE specific part of Erlang/OTP. --- erts/doc/src/driver_entry.xml | 8 ++++++-- erts/doc/src/erl_driver.xml | 6 ++++-- erts/doc/src/run_erl.xml | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml index 48dfcb09b1..469b59dba6 100644 --- a/erts/doc/src/driver_entry.xml +++ b/erts/doc/src/driver_entry.xml @@ -245,10 +245,14 @@ typedef struct erl_drv_entry { something that the WaitForMultipleObjects API function understands). (Some trickery in the emulator allows more than the built-in limit of 64 Events to be used.)

+

On Enea OSE the event is one or more signals that can + be retrieved using erl_drv_ose_get_input_signal.

To use this with threads and asynchronous routines, create a - pipe on unix and an Event on Windows. When the routine + pipe on unix, an Event on Windows or a unique signal number on + Enea OSE. When the routine completes, write to the pipe (use SetEvent on - Windows), this will make the emulator call + Windows or send a message to the emulator process on Enea OSE), + this will make the emulator call ready_input or ready_output.

Spurious events may happen. That is, calls to ready_input or ready_output even though no real events are signaled. In diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 710c9b19cf..8da1836da7 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -1035,7 +1035,9 @@ typedef struct ErlIOVec { select/poll can use). On windows, the Win32 API function WaitForMultipleObjects is used. This places other restrictions on the event object. - Refer to the Win32 SDK documentation.

+ Refer to the Win32 SDK documentation. + On Enea OSE, the receive function is used. See the for more details.

The on parameter should be 1 for setting events and 0 for clearing them.

The mode argument is a bitwise-or combination of @@ -1047,7 +1049,7 @@ typedef struct ErlIOVec { ready_output.

-

Some OS (Windows) do not differentiate between read and write events. +

Some OS (Windows and Enea OSE) do not differentiate between read and write events. The call-back for a fired event then only depends on the value of mode.

ERL_DRV_USE specifies if we are using the event object or if we want to close it. diff --git a/erts/doc/src/run_erl.xml b/erts/doc/src/run_erl.xml index 684f7b1ddd..28e94c6da8 100644 --- a/erts/doc/src/run_erl.xml +++ b/erts/doc/src/run_erl.xml @@ -58,7 +58,7 @@ first argument to run_erl on the command line. pipe_dir This is where to put the named pipe, usually - . It shall be suffixed by a (slash), + on Unix or on OSE. It shall be suffixed by a (slash), i.e. not , but . log_dir This is where the log files are written. There will be one -- cgit v1.2.3 From d6ead9a129fbe5a2df335a00e029a88a1f1849a2 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 7 Jan 2014 10:48:45 +0100 Subject: to_erl: Fix handshake ^R -> ^L code comments --- erts/etc/unix/to_erl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c index b9e397cbf2..d7e1127057 100644 --- a/erts/etc/unix/to_erl.c +++ b/erts/etc/unix/to_erl.c @@ -345,7 +345,7 @@ int main(int argc, char **argv) show_terminal_settings(&tty_smode); #endif /* - * "Write a ^R to the FIFO which causes the other end to redisplay + * "Write a ^L to the FIFO which causes the other end to redisplay * the input line." * This does not seem to work as was intended in old comment above. * However, this control character is now (R12B-3) used by run_erl @@ -354,7 +354,7 @@ int main(int argc, char **argv) */ if (write(wfd, "\014", 1) < 0) { - fprintf(stderr, "Error in writing ^R to FIFO.\n"); + fprintf(stderr, "Error in writing ^L to FIFO.\n"); } /* @@ -526,7 +526,7 @@ static int window_size_seq(char* buf, size_t bufsz) /* to_erl run_erl * | | - * |---------- '\022' -------->| (session start) + * |---------- '\014' -------->| (session start) * | | * |<---- "[run_erl v1-0]" ----| (version interval) * | | -- cgit v1.2.3 From aa80261396941ee63758d6b5965a7e075b79b4ee Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 16 Dec 2013 16:34:58 +0100 Subject: erts: Refactor common parts out of run_erl and to_erl This is in preperation for writing ose version of run_erl and to_erl --- erts/etc/common/Makefile.in | 28 +- erts/etc/common/run_erl_common.c | 610 +++++++++++++++++++++++++++++++++++++++ erts/etc/common/run_erl_common.h | 95 ++++++ erts/etc/common/run_erl_vsn.h | 29 ++ erts/etc/common/safe_string.c | 122 ++++++++ erts/etc/common/safe_string.h | 64 ++++ erts/etc/common/to_erl_common.c | 607 ++++++++++++++++++++++++++++++++++++++ erts/etc/common/to_erl_common.h | 28 ++ erts/etc/unix/run_erl.c | 601 ++------------------------------------ erts/etc/unix/run_erl.h | 30 -- erts/etc/unix/safe_string.c | 123 -------- erts/etc/unix/safe_string.h | 65 ----- erts/etc/unix/to_erl.c | 589 +------------------------------------ 13 files changed, 1605 insertions(+), 1386 deletions(-) create mode 100644 erts/etc/common/run_erl_common.c create mode 100644 erts/etc/common/run_erl_common.h create mode 100644 erts/etc/common/run_erl_vsn.h create mode 100644 erts/etc/common/safe_string.c create mode 100644 erts/etc/common/safe_string.h create mode 100644 erts/etc/common/to_erl_common.c create mode 100644 erts/etc/common/to_erl_common.h delete mode 100644 erts/etc/unix/run_erl.h delete mode 100644 erts/etc/unix/safe_string.c delete mode 100644 erts/etc/unix/safe_string.h (limited to 'erts') diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index d7fe75ce6f..f5df53ec01 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -254,7 +254,9 @@ endif rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/run_erl.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/to_erl.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/dyn_erl.o - rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/safe_string.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/safe_string.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/run_erl_common.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/to_erl_common.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/typer.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/ct_run.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/vxcall.o @@ -399,24 +401,28 @@ $(BINDIR)/inet_gethost@EXEEXT@: $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(ERTS_LIB $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(LIBS) $(ERTS_INTERNAL_LIBS) # run_erl -$(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o - $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(LIBS) $(ERTS_INTERNAL_LIBS) -$(OBJDIR)/run_erl.o: ../unix/run_erl.c $(RC_GENERATED) - $(V_CC) $(CFLAGS) -o $@ -c ../unix/run_erl.c +$(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(OBJDIR)/run_erl_common.o + $(V_LD) $(LDFLAGS) -o $@ $^ $(LIBS) +$(OBJDIR)/run_erl.o: ../unix/run_erl.c ../common/run_erl_common.h $(RC_GENERATED) + $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c ../unix/run_erl.c +$(OBJDIR)/run_erl_common.o: ../common/run_erl_common.c ../common/run_erl_common.h $(RC_GENERATED) + $(V_CC) $(CFLAGS) -o $@ -c $< # to_erl -$(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o - $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o -$(OBJDIR)/to_erl.o: ../unix/to_erl.c $(RC_GENERATED) - $(V_CC) $(CFLAGS) -o $@ -c ../unix/to_erl.c +$(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o $(OBJDIR)/to_erl_common.o + $(V_LD) $(LDFLAGS) -o $@ $^ +$(OBJDIR)/to_erl.o: ../unix/to_erl.c ../common/safe_string.h $(RC_GENERATED) + $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c ../unix/to_erl.c +$(OBJDIR)/to_erl_common.o: ../common/to_erl_common.c ../common/to_erl_common.h $(RC_GENERATED) + $(V_CC) $(CFLAGS) -o $@ -c $< # dyn_erl $(BINDIR)/dyn_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o $(OBJDIR)/dyn_erl.o: ../unix/dyn_erl.c $(RC_GENERATED) $(V_CC) $(CFLAGS) -o $@ -c ../unix/dyn_erl.c -$(OBJDIR)/safe_string.o: ../unix/safe_string.c $(RC_GENERATED) - $(V_CC) $(CFLAGS) -o $@ -c ../unix/safe_string.c +$(OBJDIR)/safe_string.o: ../common/safe_string.c $(RC_GENERATED) + $(V_CC) $(CFLAGS) -o $@ -c ../common/safe_string.c ifneq ($(TARGET),win32) $(BINDIR)/$(ERLEXEC): $(OBJDIR)/$(ERLEXEC).o $(ERTS_LIB) diff --git a/erts/etc/common/run_erl_common.c b/erts/etc/common/run_erl_common.c new file mode 100644 index 0000000000..dc899c5349 --- /dev/null +++ b/erts/etc/common/run_erl_common.c @@ -0,0 +1,610 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2014. All Rights Reserved. + * + * The 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYSLOG_H +# include +#endif + +#include "run_erl_common.h" +#include "safe_string.h" + +#define DEFAULT_LOG_GENERATIONS 5 +#define LOG_MAX_GENERATIONS 1000 /* No more than 1000 log files */ +#define LOG_MIN_GENERATIONS 2 /* At least two to switch between */ +#define DEFAULT_LOG_MAXSIZE 100000 +#define LOG_MIN_MAXSIZE 1000 /* Smallast value for changing log file */ +#define LOG_STUBNAME "erlang.log." +#define LOG_PERM 0664 +#define DEFAULT_LOG_ACTIVITY_MINUTES 5 +#define DEFAULT_LOG_ALIVE_MINUTES 15 +#define DEFAULT_LOG_ALIVE_FORMAT "%a %b %e %T %Z %Y" +#define ALIVE_BUFFSIZ 1024 + +#define STATUSFILENAME "/run_erl.log" + +#define PIPE_STUBNAME "erlang.pipe" +#define PIPE_STUBLEN strlen(PIPE_STUBNAME) +#define PERM (S_IWUSR | S_IRUSR | S_IWOTH | S_IROTH | S_IWGRP | S_IRGRP) + +#if !defined(O_SYNC) +#define O_SYNC 0 +#define USE_FSYNC 1 +#endif + +/* prototypes */ + +static int next_log(int log_num); +static int prev_log(int log_num); +static int find_next_log_num(void); +static int open_log(int log_num, int flags); + +/* static data */ +static char statusfile[FILENAME_BUFSIZ]; +static char log_dir[FILENAME_BUFSIZ]; +static FILE *stdstatus = NULL; +static int log_generations = DEFAULT_LOG_GENERATIONS; +static int log_maxsize = DEFAULT_LOG_MAXSIZE; +static int log_activity_minutes = DEFAULT_LOG_ACTIVITY_MINUTES; +static int log_alive_in_gmt = 0; +static char log_alive_format[ALIVE_BUFFSIZ+1]; +static int run_daemon = 0; +static unsigned protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ + +int erts_run_erl_log_alive_minutes = DEFAULT_LOG_ALIVE_MINUTES; + +/* + * Current log number and log fd + */ +static int log_num; +static int lfd=0; + + +/* + * getenv_int: + */ +static char *getenv_int(const char *name) { + return getenv(name); +} + +/* + * next_log: + * Returns the index number that follows the given index number. + * (Wrapping after log_generations) + */ +static int next_log(int log_num) { + return log_num>=log_generations?1:log_num+1; +} + +/* + * prev_log: + * Returns the index number that precedes the given index number. + * (Wrapping after log_generations) + */ +static int prev_log(int log_num) { + return log_num<=1?log_generations:log_num-1; +} + +/* + * find_next_log_num() + * Searches through the log directory to check which logs that already + * exist. It finds the "hole" in the sequence, and returns the index + * number for the last log in the log sequence. If there is no hole, index + * 1 is returned. + */ +static int find_next_log_num(void) { + int i, next_gen, log_gen; + DIR *dirp; + struct dirent *direntp; + int log_exists[LOG_MAX_GENERATIONS+1]; + int stub_len = strlen(LOG_STUBNAME); + + /* Initialize exiting log table */ + + for(i=log_generations; i>=0; i--) + log_exists[i] = 0; + dirp = opendir(log_dir); + if(!dirp) { + ERRNO_ERR1(LOG_ERR,"Can't access log directory '%s'", log_dir); + exit(1); + } + + /* Check the directory for existing logs */ + + while((direntp=readdir(dirp)) != NULL) { + if(strncmp(direntp->d_name,LOG_STUBNAME,stub_len)==0) { + int num = atoi(direntp->d_name+stub_len); + if(num < 1 || num > log_generations) + continue; + log_exists[num] = 1; + } + } + closedir(dirp); + + /* Find out the next available log file number */ + + next_gen = 0; + for(i=log_generations; i>=0; i--) { + if(log_exists[i]) + if(next_gen) + break; + else + ; + else + next_gen = i; + } + + /* Find out the current log file number */ + + if(next_gen) + log_gen = prev_log(next_gen); + else + log_gen = 1; + + return log_gen; +} /* find_next_log_num() */ + +static int open_log(int log_num, int flags) +{ + char buf[FILENAME_MAX]; + time_t now; + struct tm *tmptr; + char log_buffer[ALIVE_BUFFSIZ+1]; + + /* Remove the next log (to keep a "hole" in the log sequence) */ + sn_printf(buf, sizeof(buf), "%s/%s%d", + log_dir, LOG_STUBNAME, next_log(log_num)); + unlink(buf); + + /* Create or continue on the current log file */ + sn_printf(buf, sizeof(buf), "%s/%s%d", log_dir, LOG_STUBNAME, log_num); + + do { + lfd = open(buf, flags, LOG_PERM); + } while (lfd < 0 && errno == EINTR); + + if(lfd <0){ + ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf); + exit(1); + } + + /* Write a LOGGING STARTED and time stamp into the log file */ + time(&now); + if (log_alive_in_gmt) { + tmptr = gmtime(&now); + } else { + tmptr = localtime(&now); + } + if (!strftime(log_buffer, ALIVE_BUFFSIZ, log_alive_format, + tmptr)) { + strn_cpy(log_buffer, sizeof(log_buffer), + "(could not format time in 256 positions " + "with current format string.)"); + } + log_buffer[ALIVE_BUFFSIZ] = '\0'; + + sn_printf(buf, sizeof(buf), "\n=====\n===== LOGGING STARTED %s\n=====\n", + log_buffer); + if (erts_run_erl_write_all(lfd, buf, strlen(buf)) < 0) + erts_run_erl_log_status("Error in writing to log.\n"); + +#if USE_FSYNC + fsync(lfd); +#endif + + return lfd; +} + +/* Instead of making sure basename exists, we do our own */ +char *simple_basename(char *path) +{ + char *ptr; + for (ptr = path; *ptr != '\0'; ++ptr) { + if (*ptr == '/') { + path = ptr + 1; + } + } + return path; +} + +ssize_t sf_read(int fd, void *buffer, size_t len) { + ssize_t n = 0; + + do { n = read(fd, buffer, len); } while (n < 0 && errno == EINTR); + + return n; +} + +ssize_t sf_write(int fd, const void *buffer, size_t len) { + ssize_t n = 0; + + do { n = write(fd, buffer, len); } while (n < 0 && errno == EINTR); + + return n; +} + +int sf_open(const char *path, int type, mode_t mode) { + int fd = 0; + + do { fd = open(path, type, mode); } while(fd < 0 && errno == EINTR); + + return fd; +} + +int sf_close(int fd) { + int res = 0; + + do { res = close(fd); } while(res < 0 && errno == EINTR); + + return res; +} + +/* Call write() until entire buffer has been written or error. + * Return len or -1. + */ +int erts_run_erl_write_all(int fd, const char* buf, int len) +{ + int left = len; + int written; + for (;;) { + do { + written = write(fd,buf,left); + } while (written < 0 && errno == EINTR); + if (written == left) { + return len; + } + if (written < 0) { + return -1; + } + left -= written; + buf += written; + } + return written; +} + +/* erts_run_erl_log_status() + * Prints the arguments to a status file + * Works like printf (see vfrpintf) + */ +void erts_run_erl_log_status(const char *format,...) +{ + va_list args; + time_t now; + + if (stdstatus == NULL) + stdstatus = fopen(statusfile, "w"); + if (stdstatus == NULL) + return; + now = time(NULL); + fprintf(stdstatus, "run_erl [%d] %s", + (int)getpid(), + ctime(&now)); + va_start(args, format); + vfprintf(stdstatus, format, args); + va_end(args); + fflush(stdstatus); + return; +} + +/* error_logf() + * Prints the arguments to stderr or syslog + * Works like printf (see vfprintf) + */ +void erts_run_erl_log_error(int priority, int line, const char *format, ...) +{ + va_list args; + va_start(args, format); + +#ifdef HAVE_SYSLOG_H + if (run_daemon) { + vsyslog(priority,format,args); + } + else +#endif + { + time_t now = time(NULL); + fprintf(stderr, "run_erl:%d [%d] %s", line, + (int)getpid(), + ctime(&now)); + vfprintf(stderr, format, args); + } + va_end(args); +} + +/* erts_run_erl_log_write() + * Writes a message to lfd. If the current log file is full, + * a new log file is opened. + */ +int erts_run_erl_log_write(char* buf, size_t len) +{ + int size; + ssize_t res; + /* Decide if new logfile needed, and open if so */ + + size = lseek(lfd,0,SEEK_END); + if(size+len > log_maxsize) { + int res; + do { + res = close(lfd); + } while (res < 0 && errno == EINTR); + log_num = next_log(log_num); + lfd = open_log(log_num, O_RDWR|O_CREAT|O_TRUNC|O_SYNC); + } + + /* Write to log file */ + + if ((res = erts_run_erl_write_all(lfd, buf, len)) < 0) { + erts_run_erl_log_status("Error in writing to log.\n"); + } + +#if USE_FSYNC + fsync(lfd); +#endif + return res; +} + +int erts_run_erl_log_activity(int timeout,time_t now,time_t last_activity) { + char log_alive_buffer[ALIVE_BUFFSIZ+1]; + char buf[BUFSIZ]; + + if (timeout || now - last_activity > log_activity_minutes*60) { + /* Either a time out: 15 minutes without action, */ + /* or something is coming in right now, but it's a long time */ + /* since last time, so let's write a time stamp this message */ + struct tm *tmptr; + if (log_alive_in_gmt) { + tmptr = gmtime(&now); + } else { + tmptr = localtime(&now); + } + if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, log_alive_format, + tmptr)) { + strn_cpy(log_alive_buffer, sizeof(log_alive_buffer), + "(could not format time in 256 positions " + "with current format string.)"); + } + log_alive_buffer[ALIVE_BUFFSIZ] = '\0'; + + sn_printf(buf, sizeof(buf), "\n===== %s%s\n", + timeout?"ALIVE ":"", log_alive_buffer); + return erts_run_erl_log_write(buf, strlen(buf)); + } + return 0; +} + +int erts_run_erl_log_open() { + + log_num = find_next_log_num(); + lfd = open_log(log_num, O_RDWR|O_APPEND|O_CREAT|O_SYNC); + return 0; +} + +int erts_run_erl_log_init(int daemon, char* logdir) { + char *p; + + /* Get values for LOG file handling from the environment */ + if ((p = getenv_int("RUN_ERL_LOG_ALIVE_MINUTES"))) { + erts_run_erl_log_alive_minutes = atoi(p); + if (!erts_run_erl_log_alive_minutes) { + ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ALIVE_MINUTES is 1 " + "(current value is %s)",p); + } + log_activity_minutes = erts_run_erl_log_alive_minutes / 3; + if (!log_activity_minutes) { + ++log_activity_minutes; + } + } + if ((p = getenv_int( + "RUN_ERL_LOG_ACTIVITY_MINUTES"))) { + log_activity_minutes = atoi(p); + if (!log_activity_minutes) { + ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ACTIVITY_MINUTES is 1 " + "(current value is %s)",p); + } + } + if ((p = getenv_int("RUN_ERL_LOG_ALIVE_FORMAT"))) { + if (strlen(p) > ALIVE_BUFFSIZ) { + ERROR1(LOG_ERR, "RUN_ERL_LOG_ALIVE_FORMAT can contain a maximum of " + "%d characters", ALIVE_BUFFSIZ); + } + strn_cpy(log_alive_format, sizeof(log_alive_format), p); + } else { + strn_cpy(log_alive_format, sizeof(log_alive_format), + DEFAULT_LOG_ALIVE_FORMAT); + } + if ((p = getenv_int("RUN_ERL_LOG_ALIVE_IN_UTC")) + && strcmp(p,"0")) { + ++log_alive_in_gmt; + } + if ((p = getenv_int("RUN_ERL_LOG_GENERATIONS"))) { + log_generations = atoi(p); + if (log_generations < LOG_MIN_GENERATIONS) + ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_GENERATIONS is %d", + LOG_MIN_GENERATIONS); + if (log_generations > LOG_MAX_GENERATIONS) + ERROR1(LOG_ERR,"Maximum RUN_ERL_LOG_GENERATIONS is %d", + LOG_MAX_GENERATIONS); + } + + if ((p = getenv_int("RUN_ERL_LOG_MAXSIZE"))) { + log_maxsize = atoi(p); + if (log_maxsize < LOG_MIN_MAXSIZE) + ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_MAXSIZE is %d", LOG_MIN_MAXSIZE); + } + + run_daemon = daemon; + + strn_cpy(log_dir, sizeof(log_dir), logdir); + strn_cpy(statusfile, sizeof(statusfile), log_dir); + strn_cat(statusfile, sizeof(statusfile), STATUSFILENAME); + + return 0; +} + +/* create_fifo() + * Creates a new fifo with the given name and permission. + */ +static int create_fifo(char *name, int perm) +{ + if ((mkfifo(name, perm) < 0) && (errno != EEXIST)) + return -1; + return 0; +} + +/* + * w- and r_pipename have to be pre-allocated of atleast FILENAME_MAX size + */ +int erts_run_erl_open_fifo(char *pipename,char *w_pipename,char *r_pipename) { + int calculated_pipename = 0; + int highest_pipe_num = 0; + int fd; + + /* + * Create FIFOs and open them + */ + + if(*pipename && pipename[strlen(pipename)-1] == '/') { + /* The user wishes us to find a unique pipe name in the specified */ + /* directory */ + DIR *dirp; + struct dirent *direntp; + + calculated_pipename = 1; + dirp = opendir(pipename); + if(!dirp) { + ERRNO_ERR1(LOG_ERR,"Can't access pipe directory '%s'.", pipename); + return 1; + } + + /* Check the directory for existing pipes */ + + while((direntp=readdir(dirp)) != NULL) { + if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) { + int num = atoi(direntp->d_name+PIPE_STUBLEN+1); + if(num > highest_pipe_num) + highest_pipe_num = num; + } + } + closedir(dirp); + strn_catf(pipename, BUFSIZ, "%s.%d", + PIPE_STUBNAME, highest_pipe_num+1); + } /* if */ + + for(;;) { + /* write FIFO - is read FIFO for `to_erl' program */ + strn_cpy(w_pipename, BUFSIZ, pipename); + strn_cat(w_pipename, BUFSIZ, ".r"); + if (create_fifo(w_pipename, PERM) < 0) { + ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for writing.", + w_pipename); + return 1; + } + + /* read FIFO - is write FIFO for `to_erl' program */ + strn_cpy(r_pipename, BUFSIZ, pipename); + strn_cat(r_pipename, BUFSIZ, ".w"); + + /* Check that nobody is running run_erl already */ + if ((fd = sf_open(r_pipename, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { + /* Open as client succeeded -- run_erl is already running! */ + sf_close(fd); + if (calculated_pipename) { + ++highest_pipe_num; + strn_catf(pipename, BUFSIZ, "%s.%d", + PIPE_STUBNAME, highest_pipe_num+1); + continue; + } + fprintf(stderr, "Erlang already running on pipe %s.\n", pipename); + return 1; + } + if (create_fifo(r_pipename, PERM) < 0) { + ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for reading.", + r_pipename); + return 1; + } + break; + } + return 0; +} + +/* Extract any control sequences that are ment only for run_erl + * and should not be forwarded to the pty. + */ +int erts_run_erl_extract_ctrl_seq(char* buf, int len) +{ + static const char prefix[] = "\033_"; + static const char suffix[] = "\033\\"; + char* bufend = buf + len; + char* start = buf; + char* command; + char* end; + + for (;;) { + start = find_str(start, bufend-start, prefix); + if (!start) break; + + command = start + strlen(prefix); + end = find_str(command, bufend-command, suffix); + if (end) { + unsigned col, row; + if (sscanf(command,"version=%u", &PROTOCOL_VER)==1) { + /*fprintf(stderr,"to_erl v%u\n", protocol_ver);*/ + } + else if (sscanf(command,"winsize=%u,%u", &col, &row)==2) { +#ifdef TIOCSWINSZ + struct winsize ws; + ws.ws_col = col; + ws.ws_row = row; + if (ioctl(MFD, TIOCSWINSZ, &ws) < 0) { + ERRNO_ERR0(LOG_ERR,"Failed to set window size"); + } +#endif + } + else { + ERROR2(LOG_ERR, "Ignoring unknown ctrl command '%.*s'\n", + (int)(end-command), command); + } + + /* Remove ctrl sequence from buf */ + end += strlen(suffix); + memmove(start, end, bufend-end); + bufend -= end - start; + } + else { + ERROR2(LOG_ERR, "Missing suffix in ctrl sequence '%.*s'\n", + (int)(bufend-start), start); + break; + } + } + return bufend - buf; +} diff --git a/erts/etc/common/run_erl_common.h b/erts/etc/common/run_erl_common.h new file mode 100644 index 0000000000..dca1af93f2 --- /dev/null +++ b/erts/etc/common/run_erl_common.h @@ -0,0 +1,95 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013. All Rights Reserved. + * + * The 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% + */ +/* + * Functions that are common to both OSE and unix implementations of run_erl + */ +#ifndef ERL_RUN_ERL_LOG_H +#define ERL_RUN_ERL_LOG_H + +#include +#include +#include + +#include "run_erl_vsn.h" + +/* Log handling */ +int erts_run_erl_log_init(int run_daemon, char* logdir); +int erts_run_erl_log_open(void); +int erts_run_erl_log_close(void); +int erts_run_erl_log_write(char *buff, size_t len); +int erts_run_erl_log_activity(int timeout, time_t now, time_t last_activity); + +void erts_run_erl_log_status(const char *format,...); +void erts_run_erl_log_error(int priority, int line, const char *format,...); + +int erts_run_erl_open_fifo(char *pipename,char *w_pipename,char *r_pipename); +int erts_run_erl_log_alive_minutes(void); +int erts_run_erl_extract_ctrl_seq(char* buf, int len); + +/* File operations */ +ssize_t sf_read(int fd, void *buffer, size_t len); +ssize_t sf_write(int fd, const void *buffer, size_t len); +int sf_open(const char *path, int type, mode_t mode); +int sf_close(int fd); +int erts_run_erl_write_all(int fd, const char* buf, int len); +char *simple_basename(char *path); + +#ifndef LOG_ERR +#define LOG_ERR NULL +#endif + +#define ERROR0(Prio,Format) erts_run_erl_log_error(Prio,__LINE__,Format"\n") +#define ERROR1(Prio,Format,A1) erts_run_erl_log_error(Prio,__LINE__,Format"\n",A1) +#define ERROR2(Prio,Format,A1,A2) erts_run_erl_log_error(Prio,__LINE__,Format"\n",A1,A2) + +#ifdef HAVE_STRERROR +# define ADD_ERRNO(Format) "errno=%d '%s'\n"Format"\n",errno,strerror(errno) +#else +# define ADD_ERRNO(Format) "errno=%d\n"Format"\n",errno +#endif +#define ERRNO_ERR0(Prio,Format) erts_run_erl_log_error(Prio,__LINE__,ADD_ERRNO(Format)) +#define ERRNO_ERR1(Prio,Format,A1) erts_run_erl_log_error(Prio,__LINE__,ADD_ERRNO(Format),A1) +#define ERRNO_ERR2(Prio,Format,A1,A2) erts_run_erl_log_error(Prio,__LINE__,ADD_ERRNO(Format),A1,A2) + +/* defined in run_common.c */ +extern int erts_run_erl_log_alive_minutes; + +#define RUN_ERL_USAGE \ + "%s (pipe_name|pipe_dir/) log_dir \"command [parameters ...]\"" \ + "\n\nDESCRIPTION:\n" \ + "You may also set the environment variables RUN_ERL_LOG_GENERATIONS\n" \ + "and RUN_ERL_LOG_MAXSIZE to the number of log files to use and the\n" \ + "size of the log file when to switch to the next log file\n" + +#ifndef FILENAME_MAX +#define FILENAME_MAX 250 +#endif + +#define FILENAME_BUFSIZ FILENAME_MAX + +#ifdef O_NONBLOCK +# define DONT_BLOCK_PLEASE O_NONBLOCK +#else +# define DONT_BLOCK_PLEASE O_NDELAY +# ifndef EAGAIN +# define EAGAIN -3898734 +# endif +#endif + +#endif diff --git a/erts/etc/common/run_erl_vsn.h b/erts/etc/common/run_erl_vsn.h new file mode 100644 index 0000000000..f6ac753bde --- /dev/null +++ b/erts/etc/common/run_erl_vsn.h @@ -0,0 +1,29 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2008-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% + */ + +/* + * The protocol version number used between to_erl and run_erl. + */ +#define RUN_ERL_HI_VER 1 /* My preferred protocol version */ +#define RUN_ERL_LO_VER 0 /* The lowest version I accept to talk with */ + +/* Version history: + * 0: Older, without version handshake + * 1: R12B-3, version handshake + window size ctrl + */ diff --git a/erts/etc/common/safe_string.c b/erts/etc/common/safe_string.c new file mode 100644 index 0000000000..b2f8814408 --- /dev/null +++ b/erts/etc/common/safe_string.c @@ -0,0 +1,122 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2008-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: safe_string.c + * + * This is a bunch of generic string operation + * that are safe regarding buffer overflow. + * + * All string functions terminate the process with an error message + * on buffer overflow. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "safe_string.h" +#include +#include +#include +#include + + +static void string_overflow_handler(const char* format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr,format,args); + va_end(args); + exit(1); +} + +int vsn_printf(char* dst, size_t size, const char* format, va_list args) +{ + int ret = vsnprintf(dst, size, format, args); + if (ret >= size || ret < 0) { + string_overflow_handler("Buffer truncated '%s'\n",dst); + } + return ret; +} + +int sn_printf(char* dst, size_t size, const char* format, ...) +{ + va_list args; + int ret; + va_start(args, format); + ret = vsn_printf(dst,size,format,args); + va_end(args); + return ret; +} + +int strn_cpy(char* dst, size_t size, const char* src) +{ + return sn_printf(dst,size,"%s",src); +} + +int strn_cat(char* dst, size_t size, const char* src) +{ + return strn_catf(dst,size,"%s",src); +} + +int strn_catf(char* dst, size_t size, const char* format, ...) +{ + int ret; + va_list args; +#ifdef _GNU_SOURCE + int len = strnlen(dst,size); +#else + int len = strlen(dst); +#endif + + if (len >= size) { + string_overflow_handler("Buffer already overflowed '%.*s'\n", + size, dst); + } + va_start(args, format); + ret = vsn_printf(dst+len, size-len, format, args); + va_end(args); + return len+ret; +} + +char* find_str(const char* haystack, int hsize, const char* needle) +{ + int i = 0; + int nsize = strlen(needle); + hsize -= nsize - 1; + for (i=0; i dest) { + for (i=0; i=0; i--) ((char*)dest)[i] = ((char*)src)[i]; + } + return dest; +} +#endif /* HAVE_MEMMOVE */ diff --git a/erts/etc/common/safe_string.h b/erts/etc/common/safe_string.h new file mode 100644 index 0000000000..ff063fe641 --- /dev/null +++ b/erts/etc/common/safe_string.h @@ -0,0 +1,64 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2008-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: safe_string.h + * + * This is an interface to a bunch of generic string operation + * that are safe regarding buffer overflow. + * + * All string functions terminate the process with an error message + * on buffer overflow. + */ + +#include +#include + +/* Like vsnprintf() + */ +int vsn_printf(char* dst, size_t size, const char* format, va_list args); + +/* Like snprintf() + */ +int sn_printf(char* dst, size_t size, const char* format, ...); + +/* Like strncpy() + * Returns length of copied string. + */ +int strn_cpy(char* dst, size_t size, const char* src); + +/* Almost like strncat() + * size is sizeof entire dst buffer. + * Returns length of resulting string. + */ +int strn_cat(char* dst, size_t size, const char* src); + +/* Combination of strncat() and snprintf() + * size is sizeof entire dst buffer. + * Returns length of resulting string. + */ +int strn_catf(char* dst, size_t size, const char* format, ...); + +/* Simular to strstr() but search size bytes of haystack + * without regard to '\0' characters. + */ +char* find_str(const char* haystack, int size, const char* needle); + +#ifndef HAVE_MEMMOVE +void* memmove(void *dest, const void *src, size_t n); +#endif diff --git a/erts/etc/common/to_erl_common.c b/erts/etc/common/to_erl_common.c new file mode 100644 index 0000000000..4c38877277 --- /dev/null +++ b/erts/etc/common/to_erl_common.c @@ -0,0 +1,607 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * + * The 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: to_erl.c + * + * This module implements a process that opens two specified FIFOs, one + * for reading and one for writing; reads from its stdin, and writes what + * it has read to the write FIF0; reads from the read FIFO, and writes to + * its stdout. + * + ________ _________ + | |--<-- pipe.r (fifo1) --<--| | + | to_erl | | run_erl | (parent) + |________|-->-- pipe.w (fifo2) -->--|_________| + ^ master pty + | + | slave pty + ____V____ + | | + | "erl" | (child) + |_________| + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_IOCTL_H +# include +#endif + +#include "to_erl_common.h" +#include "run_erl_vsn.h" +#include "safe_string.h" /* strn_cpy, strn_catf, sn_printf, etc. */ + +#if defined(O_NONBLOCK) +# define DONT_BLOCK_PLEASE O_NONBLOCK +#else +# define DONT_BLOCK_PLEASE O_NDELAY +# if !defined(EAGAIN) +# define EAGAIN -3898734 +# endif +#endif + +#ifdef HAVE_STRERROR +# define STRERROR(x) strerror(x) +#else +# define STRERROR(x) "" +#endif + +#define noDEBUG + +#define PIPE_DIR "/tmp/" +#define PIPE_STUBNAME "erlang.pipe" +#define PIPE_STUBLEN strlen(PIPE_STUBNAME) + +#ifdef DEBUG +#define STATUS(s) { fprintf(stderr, (s)); fflush(stderr); } +#else +#define STATUS(s) +#endif + +#ifndef FILENAME_MAX +#define FILENAME_MAX 250 +#endif + +static struct termios tty_smode, tty_rmode; +static int tty_eof = 0; +static int recv_sig = 0; +static int protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ + +static int write_all(int fd, const char* buf, int len); +static int window_size_seq(char* buf, size_t bufsz); +static int version_handshake(char* buf, int len, int wfd); +#ifdef DEBUG +static void show_terminal_settings(struct termios *); +#endif + +static void handle_ctrlc(int sig) +{ + /* Reinstall the handler, and signal break flag */ + signal(SIGINT,handle_ctrlc); + recv_sig = SIGINT; +} + +static void handle_sigwinch(int sig) +{ + recv_sig = SIGWINCH; +} + +static void usage(char *pname) +{ + fprintf(stderr, "Usage: "); + fprintf(stderr,TO_ERL_USAGE,pname); +} + +int to_erl(int argc, char **argv) +{ + char FIFO1[FILENAME_MAX], FIFO2[FILENAME_MAX]; + int i, len, wfd, rfd; + fd_set readfds; + char buf[BUFSIZ]; + char pipename[FILENAME_MAX]; + int pipeIx = 1; + int force_lock = 0; + int got_some = 0; + + if (argc >= 2 && argv[1][0]=='-') { + switch (argv[1][1]) { + case 'h': + usage(argv[0]); + exit(1); + case 'F': + force_lock = 1; + break; + default: + fprintf(stderr,"Invalid option '%s'\n",argv[1]); + exit(1); + } + pipeIx = 2; + } + +#ifdef DEBUG + fprintf(stderr, "%s: pid is : %d\n", argv[0], (int)getpid()); +#endif + + strn_cpy(pipename, sizeof(pipename), + (argv[pipeIx] ? argv[pipeIx] : PIPE_DIR)); + + if(*pipename && pipename[strlen(pipename)-1] == '/') { + /* The user wishes us to find a pipe name in the specified */ + /* directory */ + int highest_pipe_num = 0; + DIR *dirp; + struct dirent *direntp; + + dirp = opendir(pipename); + if(!dirp) { + fprintf(stderr, "Can't access pipe directory %s: %s\n", pipename, strerror(errno)); + exit(1); + } + + /* Check the directory for existing pipes */ + + while((direntp=readdir(dirp)) != NULL) { + if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) { + int num = atoi(direntp->d_name+PIPE_STUBLEN+1); + if(num > highest_pipe_num) + highest_pipe_num = num; + } + } + closedir(dirp); + strn_catf(pipename, sizeof(pipename), (highest_pipe_num?"%s.%d":"%s"), + PIPE_STUBNAME, highest_pipe_num); + } /* if */ + + /* read FIFO */ + sn_printf(FIFO1,sizeof(FIFO1),"%s.r",pipename); + /* write FIFO */ + sn_printf(FIFO2,sizeof(FIFO2),"%s.w",pipename); + + /* Check that nobody is running to_erl on this pipe already */ + if ((wfd = open (FIFO1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { + /* Open as server succeeded -- to_erl is already running! */ + close(wfd); + fprintf(stderr, "Another to_erl process already attached to pipe " + "%s.\n", pipename); + if (force_lock) { + fprintf(stderr, "But we proceed anyway by force (-F).\n"); + } + else { + exit(1); + } + } + + if ((rfd = open (FIFO1, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) { +#ifdef DEBUG + fprintf(stderr, "Could not open FIFO %s for reading.\n", FIFO1); +#endif + fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno)); + exit(1); + } +#ifdef DEBUG + fprintf(stderr, "to_erl: %s opened for reading\n", FIFO1); +#endif + + if ((wfd = open (FIFO2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { +#ifdef DEBUG + fprintf(stderr, "Could not open FIFO %s for writing.\n", FIFO2); +#endif + fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno)); + close(rfd); + exit(1); + } +#ifdef DEBUG + fprintf(stderr, "to_erl: %s opened for writing\n", FIFO2); +#endif + + fprintf(stderr, "Attaching to %s (^D to exit)\n\n", pipename); + + /* Set break handler to our handler */ + signal(SIGINT,handle_ctrlc); + + /* + * Save the current state of the terminal, and set raw mode. + */ + if (tcgetattr(0, &tty_rmode) , 0) { + fprintf(stderr, "Cannot get terminals current mode\n"); + exit(-1); + } + tty_smode = tty_rmode; + tty_eof = '\004'; /* Ctrl+D to exit */ +#ifdef DEBUG + show_terminal_settings(&tty_rmode); +#endif + tty_smode.c_iflag = + 1*BRKINT |/*Signal interrupt on break.*/ + 1*IGNPAR |/*Ignore characters with parity errors.*/ + 1*ISTRIP |/*Strip character.*/ + 0; + +#if 0 +0*IGNBRK |/*Ignore break condition.*/ +0*PARMRK |/*Mark parity errors.*/ +0*INPCK |/*Enable input parity check.*/ +0*INLCR |/*Map NL to CR on input.*/ +0*IGNCR |/*Ignore CR.*/ +0*ICRNL |/*Map CR to NL on input.*/ +0*IUCLC |/*Map upper-case to lower-case on input.*/ +0*IXON |/*Enable start/stop output control.*/ +0*IXANY |/*Enable any character to restart output.*/ +0*IXOFF |/*Enable start/stop input control.*/ +0*IMAXBEL|/*Echo BEL on input line too long.*/ +#endif + + tty_smode.c_oflag = + 1*OPOST |/*Post-process output.*/ + 1*ONLCR |/*Map NL to CR-NL on output.*/ +#ifdef XTABS + 1*XTABS |/*Expand tabs to spaces. (Linux)*/ +#endif +#ifdef OXTABS + 1*OXTABS |/*Expand tabs to spaces. (FreeBSD)*/ +#endif +#ifdef NL0 + 1*NL0 |/*Select newline delays*/ +#endif +#ifdef CR0 + 1*CR0 |/*Select carriage-return delays*/ +#endif +#ifdef TAB0 + 1*TAB0 |/*Select horizontal tab delays*/ +#endif +#ifdef BS0 + 1*BS0 |/*Select backspace delays*/ +#endif +#ifdef VT0 + 1*VT0 |/*Select vertical tab delays*/ +#endif +#ifdef FF0 + 1*FF0 |/*Select form feed delays*/ +#endif + 0; + +#if 0 +0*OLCUC |/*Map lower case to upper on output.*/ +0*OCRNL |/*Map CR to NL on output.*/ +0*ONOCR |/*No CR output at column 0.*/ +0*ONLRET |/*NL performs CR function.*/ +0*OFILL |/*Use fill characters for delay.*/ +0*OFDEL |/*Fill is DEL, else NULL.*/ +0*NL1 | +0*CR1 | +0*CR2 | +0*CR3 | +0*TAB1 | +0*TAB2 | +0*TAB3 |/*Expand tabs to spaces.*/ +0*BS1 | +0*VT1 | +0*FF1 | +#endif + + /* JALI: removed setting the tty_smode.c_cflag flags, since this is not */ + /* advisable if this is a *real* terminal, such as the console. In fact */ + /* this may hang the entire machine, deep, deep down (signalling break */ + /* or toggling the abort switch doesn't help) */ + + tty_smode.c_lflag = + 0; + +#if 0 +0*ISIG |/*Enable signals.*/ +0*ICANON |/*Canonical input (erase and kill processing).*/ +0*XCASE |/*Canonical upper/lower presentation.*/ +0*ECHO |/*Enable echo.*/ +0*ECHOE |/*Echo erase character as BS-SP-BS.*/ +0*ECHOK |/*Echo NL after kill character.*/ +0*ECHONL |/*Echo NL.*/ +0*NOFLSH |/*Disable flush after interrupt or quit.*/ +0*TOSTOP |/*Send SIGTTOU for background output.*/ +0*ECHOCTL|/*Echo control characters as ^char, delete as ^?.*/ +0*ECHOPRT|/*Echo erase character as character erased.*/ +0*ECHOKE |/*BS-SP-BS erase entire line on line kill.*/ +0*FLUSHO |/*Output is being flushed.*/ +0*PENDIN |/*Retype pending input at next read or input character.*/ +0*IEXTEN |/*Enable extended (implementation-defined) functions.*/ +#endif + + tty_smode.c_cc[VMIN] =0;/* Note that VMIN is the same as VEOF! */ + tty_smode.c_cc[VTIME] =0;/* Note that VTIME is the same as VEOL! */ + tty_smode.c_cc[VINTR] =3; + + tcsetattr(0, TCSADRAIN, &tty_smode); + +#ifdef DEBUG + show_terminal_settings(&tty_smode); +#endif + /* + * "Write a ^L to the FIFO which causes the other end to redisplay + * the input line." + * This does not seem to work as was intended in old comment above. + * However, this control character is now (R12B-3) used by run_erl + * to trigger the version handshaking between to_erl and run_erl + * at the start of every new to_erl-session. + */ + + if (write(wfd, "\014", 1) < 0) { + fprintf(stderr, "Error in writing ^L to FIFO.\n"); + } + + /* + * read and write + */ + while (1) { + FD_ZERO(&readfds); + FD_SET(0, &readfds); + FD_SET(rfd, &readfds); + if (select(rfd + 1, &readfds, NULL, NULL, NULL) < 0) { + if (recv_sig) { + FD_ZERO(&readfds); + } + else { + fprintf(stderr, "Error in select.\n"); + break; + } + } + len = 0; + + /* + * Read from terminal and write to FIFO + */ + if (recv_sig) { + switch (recv_sig) { + case SIGINT: + fprintf(stderr, "[Break]\n\r"); + buf[0] = '\003'; + len = 1; + break; + case SIGWINCH: + len = window_size_seq(buf,sizeof(buf)); + break; + default: + fprintf(stderr,"Unexpected signal: %u\n",recv_sig); + } + recv_sig = 0; + } + else if (FD_ISSET(0, &readfds)) { + len = read(0, buf, sizeof(buf)); + if (len <= 0) { + close(rfd); + close(wfd); + if (len < 0) { + fprintf(stderr, "Error in reading from stdin.\n"); + } else { + fprintf(stderr, "[EOF]\n\r"); + } + break; + } + /* check if there is an eof character in input */ + for (i = 0; i < len && buf[i] != tty_eof; i++); + if (buf[i] == tty_eof) { + fprintf(stderr, "[Quit]\n\r"); + break; + } + } + + if (len) { +#ifdef DEBUG + if(write(1, buf, len)); +#endif + if (write_all(wfd, buf, len) != len) { + fprintf(stderr, "Error in writing to FIFO.\n"); + close(rfd); + close(wfd); + break; + } + STATUS("\" OK\r\n"); + } + + /* + * Read from FIFO, write to terminal. + */ + if (FD_ISSET(rfd, &readfds)) { + STATUS("FIFO read: "); + len = read(rfd, buf, BUFSIZ); + if (len < 0 && errno == EAGAIN) { + /* + * No data this time, but the writing end of the FIFO is still open. + * Do nothing. + */ + ; + } else if (len <= 0) { + /* + * Either an error or end of file. In either case, break out + * of the loop. + */ + close(rfd); + close(wfd); + if (len < 0) { + fprintf(stderr, "Error in reading from FIFO.\n"); + } else + fprintf(stderr, "[End]\n\r"); + break; + } else { + if (!got_some) { + if ((len=version_handshake(buf,len,wfd)) < 0) { + close(rfd); + close(wfd); + break; + } + if (protocol_ver >= 1) { + /* Tell run_erl size of terminal window */ + signal(SIGWINCH, handle_sigwinch); + raise(SIGWINCH); + } + got_some = 1; + } + + /* + * We successfully read at least one character. Write what we got. + */ + STATUS("Terminal write: \""); + if (write_all(1, buf, len) != len) { + fprintf(stderr, "Error in writing to terminal.\n"); + close(rfd); + close(wfd); + break; + } + STATUS("\" OK\r\n"); + } + } + } + + /* + * Reset terminal characterstics + * XXX + */ + tcsetattr(0, TCSADRAIN, &tty_rmode); + return 0; +} + +/* Call write() until entire buffer has been written or error. + * Return len or -1. + */ +static int write_all(int fd, const char* buf, int len) +{ + int left = len; + int written; + while (left) { + written = write(fd,buf,left); + if (written < 0) { + return -1; + } + left -= written; + buf += written; + } + return len; +} + +static int window_size_seq(char* buf, size_t bufsz) +{ +#ifdef TIOCGWINSZ + struct winsize ws; + static const char prefix[] = "\033_"; + static const char suffix[] = "\033\\"; + /* This Esc sequence is called "Application Program Command" + and seems suitable to use for our own customized stuff. */ + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) { + int len = sn_printf(buf, bufsz, "%swinsize=%u,%u%s", + prefix, ws.ws_col, ws.ws_row, suffix); + return len; + } +#endif /* TIOCGWINSZ */ + return 0; +} + +/* to_erl run_erl + * | | + * |---------- '\014' -------->| (session start) + * | | + * |<---- "[run_erl v1-0]" ----| (version interval) + * | | + * |--- Esc_"version=1"Esc\ -->| (common version) + * | | + */ +static int version_handshake(char* buf, int len, int wfd) +{ + unsigned re_high=0, re_low; + char *end = find_str(buf,len,"]\n"); + + if (end && sscanf(buf,"[run_erl v%u-%u",&re_high,&re_low)==2) { + char wbuf[30]; + int wlen; + + if (re_low > RUN_ERL_HI_VER || re_high < RUN_ERL_LO_VER) { + fprintf(stderr,"Incompatible versions: to_erl=v%u-%u run_erl=v%u-%u\n", + RUN_ERL_HI_VER, RUN_ERL_LO_VER, re_high, re_low); + return -1; + } + /* Choose highest common version */ + protocol_ver = re_high < RUN_ERL_HI_VER ? re_high : RUN_ERL_HI_VER; + + wlen = sn_printf(wbuf, sizeof(wbuf), "\033_version=%u\033\\", + protocol_ver); + if (write_all(wfd, wbuf, wlen) < 0) { + fprintf(stderr,"Failed to send version handshake\n"); + return -1; + } + end += 2; + len -= (end-buf); + memmove(buf,end,len); + + } + else { /* we assume old run_erl without version handshake */ + protocol_ver = 0; + } + + if (re_high != RUN_ERL_HI_VER) { + fprintf(stderr,"run_erl has different version, " + "using common protocol level %u\n", protocol_ver); + } + + return len; +} + + +#ifdef DEBUG +#define S(x) ((x) > 0 ? 1 : 0) + +static void show_terminal_settings(struct termios *t) +{ + fprintf(stderr,"c_iflag:\n"); + fprintf(stderr,"Signal interrupt on break: BRKINT %d\n", S(t->c_iflag & BRKINT)); + fprintf(stderr,"Map CR to NL on input: ICRNL %d\n", S(t->c_iflag & ICRNL)); + fprintf(stderr,"Ignore break condition: IGNBRK %d\n", S(t->c_iflag & IGNBRK)); + fprintf(stderr,"Ignore CR: IGNCR %d\n", S(t->c_iflag & IGNCR)); + fprintf(stderr,"Ignore char with par. err's: IGNPAR %d\n", S(t->c_iflag & IGNPAR)); + fprintf(stderr,"Map NL to CR on input: INLCR %d\n", S(t->c_iflag & INLCR)); + fprintf(stderr,"Enable input parity check: INPCK %d\n", S(t->c_iflag & INPCK)); + fprintf(stderr,"Strip character ISTRIP %d\n", S(t->c_iflag & ISTRIP)); + fprintf(stderr,"Enable start/stop input ctrl IXOFF %d\n", S(t->c_iflag & IXOFF)); + fprintf(stderr,"ditto output ctrl IXON %d\n", S(t->c_iflag & IXON)); + fprintf(stderr,"Mark parity errors PARMRK %d\n", S(t->c_iflag & PARMRK)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_oflag:\n"); + fprintf(stderr,"Perform output processing OPOST %d\n", S(t->c_oflag & OPOST)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_cflag:\n"); + fprintf(stderr,"Ignore modem status lines CLOCAL %d\n", S(t->c_cflag & CLOCAL)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_local:\n"); + fprintf(stderr,"Enable echo ECHO %d\n", S(t->c_lflag & ECHO)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_cc:\n"); + fprintf(stderr,"c_cc[VEOF] %d\n", t->c_cc[VEOF]); +} +#endif diff --git a/erts/etc/common/to_erl_common.h b/erts/etc/common/to_erl_common.h new file mode 100644 index 0000000000..9967db94b8 --- /dev/null +++ b/erts/etc/common/to_erl_common.h @@ -0,0 +1,28 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013. All Rights Reserved. + * + * The 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_TO_ERL_H +#define ERL_TO_ERL_H + +#define TO_ERL_USAGE "to_erl [-h|-F] %s\n" \ + "\t-h\tThis help text.\n" \ + "\t-f\tForce connection even though pipe is locked by other to_erl process." + +int to_erl(int argc, char **argv); + +#endif diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index 2018bc007c..b3f0591b07 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -79,81 +79,25 @@ # include #endif -#include "run_erl.h" +#include "run_erl_common.h" #include "safe_string.h" /* sn_printf, strn_cpy, strn_cat, etc */ -#ifdef O_NONBLOCK -# define DONT_BLOCK_PLEASE O_NONBLOCK -#else -# define DONT_BLOCK_PLEASE O_NDELAY -# ifndef EAGAIN -# define EAGAIN -3898734 -# endif -#endif - -#define noDEBUG - -#define DEFAULT_LOG_GENERATIONS 5 -#define LOG_MAX_GENERATIONS 1000 /* No more than 1000 log files */ -#define LOG_MIN_GENERATIONS 2 /* At least two to switch between */ -#define DEFAULT_LOG_MAXSIZE 100000 -#define LOG_MIN_MAXSIZE 1000 /* Smallast value for changing log file */ -#define LOG_STUBNAME "erlang.log." -#define LOG_PERM 0664 -#define DEFAULT_LOG_ACTIVITY_MINUTES 5 -#define DEFAULT_LOG_ALIVE_MINUTES 15 -#define DEFAULT_LOG_ALIVE_FORMAT "%a %b %e %T %Z %Y" -#define ALIVE_BUFFSIZ 256 - -#define PERM 0600 -#define STATUSFILENAME "/run_erl.log" -#define PIPE_STUBNAME "erlang.pipe" -#define PIPE_STUBLEN strlen(PIPE_STUBNAME) - -#ifndef FILENAME_MAX -#define FILENAME_MAX 250 -#endif - -#ifndef O_SYNC -#define O_SYNC 0 -#define USE_FSYNC 1 -#endif - #define MAX(x,y) ((x) > (y) ? (x) : (y)) -#define FILENAME_BUFSIZ FILENAME_MAX - /* prototypes */ static void usage(char *); -static int create_fifo(char *name, int perm); static int open_pty_master(char **name, int *sfd); static int open_pty_slave(char *name); static void pass_on(pid_t); static void exec_shell(char **); -static void status(const char *format,...); -static void error_logf(int priority, int line, const char *format,...); static void catch_sigchild(int); -static int next_log(int log_num); -static int prev_log(int log_num); -static int find_next_log_num(void); -static int open_log(int log_num, int flags); -static void write_to_log(int* lfd, int* log_num, char* buf, int len); static void daemon_init(void); -static char *simple_basename(char *path); static void init_outbuf(void); static int outbuf_size(void); static void clear_outbuf(void); static char* outbuf_first(void); static void outbuf_delete(int bytes); static void outbuf_append(const char* bytes, int n); -static int write_all(int fd, const char* buf, int len); -static int extract_ctrl_seq(char* buf, int len); -static void set_window_size(unsigned col, unsigned row); - -static ssize_t sf_write(int fd, const void *buffer, size_t len); -static ssize_t sf_read(int fd, void *buffer, size_t len); -static int sf_open(const char *path, int flags, mode_t mode); -static int sf_close(int fd); #ifdef DEBUG static void show_terminal_settings(struct termios *t); @@ -161,20 +105,11 @@ static void show_terminal_settings(struct termios *t); /* static data */ static char fifo1[FILENAME_BUFSIZ], fifo2[FILENAME_BUFSIZ]; -static char statusfile[FILENAME_BUFSIZ]; -static char log_dir[FILENAME_BUFSIZ]; static char pipename[FILENAME_BUFSIZ]; static FILE *stdstatus = NULL; -static int log_generations = DEFAULT_LOG_GENERATIONS; -static int log_maxsize = DEFAULT_LOG_MAXSIZE; -static int log_alive_minutes = DEFAULT_LOG_ALIVE_MINUTES; -static int log_activity_minutes = DEFAULT_LOG_ACTIVITY_MINUTES; -static int log_alive_in_gmt = 0; -static char log_alive_format[ALIVE_BUFFSIZ+1]; static int run_daemon = 0; static char *program_name; static int mfd; /* master pty fd */ -static unsigned protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ /* * Output buffer. @@ -205,29 +140,13 @@ static char* outbuf_in; LOG_PID|LOG_CONS|LOG_NOWAIT,LOG_USER) #endif -#define ERROR0(Prio,Format) error_logf(Prio,__LINE__,Format"\n") -#define ERROR1(Prio,Format,A1) error_logf(Prio,__LINE__,Format"\n",A1) -#define ERROR2(Prio,Format,A1,A2) error_logf(Prio,__LINE__,Format"\n",A1,A2) - -#ifdef HAVE_STRERROR -# define ADD_ERRNO(Format) "errno=%d '%s'\n"Format"\n",errno,strerror(errno) -#else -# define ADD_ERRNO(Format) "errno=%d\n"Format"\n",errno -#endif -#define ERRNO_ERR0(Prio,Format) error_logf(Prio,__LINE__,ADD_ERRNO(Format)) -#define ERRNO_ERR1(Prio,Format,A1) error_logf(Prio,__LINE__,ADD_ERRNO(Format),A1) - - int main(int argc, char **argv) { int childpid; int sfd = -1; - int fd; - char *p, *ptyslave=NULL; + char *ptyslave=NULL; int i = 1; int off_argv; - int calculated_pipename = 0; - int highest_pipe_num = 0; program_name = argv[0]; @@ -245,122 +164,16 @@ int main(int argc, char **argv) off_argv = i; strn_cpy(pipename, sizeof(pipename), argv[i++]); - strn_cpy(log_dir, sizeof(log_dir), argv[i]); - strn_cpy(statusfile, sizeof(statusfile), log_dir); - strn_cat(statusfile, sizeof(statusfile), STATUSFILENAME); + + erts_run_erl_log_init(run_daemon,argv[i]); #ifdef DEBUG - status("%s: pid is : %d\n", argv[0], getpid()); + erts_run_erl_log_status("%s: pid is : %d\n", argv[0], getpid()); #endif - /* Get values for LOG file handling from the environment */ - if ((p = getenv("RUN_ERL_LOG_ALIVE_MINUTES"))) { - log_alive_minutes = atoi(p); - if (!log_alive_minutes) { - ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ALIVE_MINUTES is 1 " - "(current value is %s)",p); - } - log_activity_minutes = log_alive_minutes / 3; - if (!log_activity_minutes) { - ++log_activity_minutes; - } - } - if ((p = getenv("RUN_ERL_LOG_ACTIVITY_MINUTES"))) { - log_activity_minutes = atoi(p); - if (!log_activity_minutes) { - ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ACTIVITY_MINUTES is 1 " - "(current value is %s)",p); - } - } - if ((p = getenv("RUN_ERL_LOG_ALIVE_FORMAT"))) { - if (strlen(p) > ALIVE_BUFFSIZ) { - ERROR1(LOG_ERR, "RUN_ERL_LOG_ALIVE_FORMAT can contain a maximum of " - "%d characters", ALIVE_BUFFSIZ); - } - strn_cpy(log_alive_format, sizeof(log_alive_format), p); - } else { - strn_cpy(log_alive_format, sizeof(log_alive_format), DEFAULT_LOG_ALIVE_FORMAT); - } - if ((p = getenv("RUN_ERL_LOG_ALIVE_IN_UTC")) && strcmp(p,"0")) { - ++log_alive_in_gmt; - } - if ((p = getenv("RUN_ERL_LOG_GENERATIONS"))) { - log_generations = atoi(p); - if (log_generations < LOG_MIN_GENERATIONS) - ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_GENERATIONS is %d", LOG_MIN_GENERATIONS); - if (log_generations > LOG_MAX_GENERATIONS) - ERROR1(LOG_ERR,"Maximum RUN_ERL_LOG_GENERATIONS is %d", LOG_MAX_GENERATIONS); - } - - if ((p = getenv("RUN_ERL_LOG_MAXSIZE"))) { - log_maxsize = atoi(p); - if (log_maxsize < LOG_MIN_MAXSIZE) - ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_MAXSIZE is %d", LOG_MIN_MAXSIZE); - } - - /* - * Create FIFOs and open them - */ - - if(*pipename && pipename[strlen(pipename)-1] == '/') { - /* The user wishes us to find a unique pipe name in the specified */ - /* directory */ - DIR *dirp; - struct dirent *direntp; - - calculated_pipename = 1; - dirp = opendir(pipename); - if(!dirp) { - ERRNO_ERR1(LOG_ERR,"Can't access pipe directory '%s'.", pipename); - exit(1); - } - - /* Check the directory for existing pipes */ - - while((direntp=readdir(dirp)) != NULL) { - if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) { - int num = atoi(direntp->d_name+PIPE_STUBLEN+1); - if(num > highest_pipe_num) - highest_pipe_num = num; - } - } - closedir(dirp); - strn_catf(pipename, sizeof(pipename), "%s.%d", - PIPE_STUBNAME, highest_pipe_num+1); - } /* if */ - - for(;;) { - /* write FIFO - is read FIFO for `to_erl' program */ - strn_cpy(fifo1, sizeof(fifo1), pipename); - strn_cat(fifo1, sizeof(fifo1), ".r"); - if (create_fifo(fifo1, PERM) < 0) { - ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for writing.", fifo1); - exit(1); - } - - /* read FIFO - is write FIFO for `to_erl' program */ - strn_cpy(fifo2, sizeof(fifo2), pipename); - strn_cat(fifo2, sizeof(fifo2), ".w"); - - /* Check that nobody is running run_erl already */ - if ((fd = sf_open(fifo2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { - /* Open as client succeeded -- run_erl is already running! */ - sf_close(fd); - if (calculated_pipename) { - ++highest_pipe_num; - strn_catf(pipename, sizeof(pipename), "%s.%d", - PIPE_STUBNAME, highest_pipe_num+1); - continue; - } - fprintf(stderr, "Erlang already running on pipe %s.\n", pipename); - exit(1); - } - if (create_fifo(fifo2, PERM) < 0) { - ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for reading.", fifo2); - exit(1); - } - break; - } + /* Open read and write fifo */ + if (erts_run_erl_open_fifo(pipename,fifo1,fifo2)) + exit(1); /* * Open master pseudo-terminal @@ -432,7 +245,7 @@ int main(int argc, char **argv) sf_close(2); if (dup(sfd) != 0 || dup(sfd) != 1 || dup(sfd) != 2) { - status("Cannot dup\n"); + erts_run_erl_log_status("Cannot dup\n"); } sf_close(sfd); exec_shell(argv+off_argv); /* exec_shell expects argv[2] to be */ @@ -475,9 +288,7 @@ static void pass_on(pid_t childpid) struct timeval timeout; time_t last_activity; char buf[BUFSIZ]; - char log_alive_buffer[ALIVE_BUFFSIZ+1]; - int lognum; - int rfd, wfd=0, lfd=0; + int rfd, wfd=0; int maxfd; int ready; int got_some = 0; /* from to_erl */ @@ -492,13 +303,12 @@ static void pass_on(pid_t childpid) } #ifdef DEBUG - status("run_erl: %s opened for reading\n", fifo2); + erts_run_erl_log_status("run_erl: %s opened for reading\n", fifo2); #endif /* Open the log file */ - lognum = find_next_log_num(); - lfd = open_log(lognum, O_RDWR|O_APPEND|O_CREAT|O_SYNC); + erts_run_erl_log_open(); /* Enter the work loop */ @@ -517,7 +327,8 @@ static void pass_on(pid_t childpid) writefds_ptr = &writefds; } time(&last_activity); - timeout.tv_sec = log_alive_minutes*60; /* don't assume old BSD bug */ + /* don't assume old BSD bug */ + timeout.tv_sec = erts_run_erl_log_alive_minutes*60; timeout.tv_usec = 0; ready = select(maxfd + 1, &readfds, writefds_ptr, NULL, &timeout); if (ready < 0) { @@ -547,28 +358,7 @@ static void pass_on(pid_t childpid) /* Check how long time we've been inactive */ time(&now); - if(!ready || now - last_activity > log_activity_minutes*60) { - /* Either a time out: 15 minutes without action, */ - /* or something is coming in right now, but it's a long time */ - /* since last time, so let's write a time stamp this message */ - struct tm *tmptr; - if (log_alive_in_gmt) { - tmptr = gmtime(&now); - } else { - tmptr = localtime(&now); - } - if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, log_alive_format, - tmptr)) { - strn_cpy(log_alive_buffer, sizeof(log_alive_buffer), - "(could not format time in 256 positions " - "with current format string.)"); - } - log_alive_buffer[ALIVE_BUFFSIZ] = '\0'; - - sn_printf(buf, sizeof(buf), "\n===== %s%s\n", - ready?"":"ALIVE ", log_alive_buffer); - write_to_log(&lfd, &lognum, buf, strlen(buf)); - } + erts_run_erl_log_activity(!ready,now,last_activity); } /* @@ -603,7 +393,7 @@ static void pass_on(pid_t childpid) */ if (FD_ISSET(mfd, &readfds)) { #ifdef DEBUG - status("Pty master read; "); + erts_run_erl_log_status("Pty master read; "); #endif if ((len = sf_read(mfd, buf, BUFSIZ)) <= 0) { sf_close(rfd); @@ -621,7 +411,7 @@ static void pass_on(pid_t childpid) exit(0); } - write_to_log(&lfd, &lognum, buf, len); + erts_run_erl_log_write(buf, len); /* * Save in the output queue. @@ -637,7 +427,7 @@ static void pass_on(pid_t childpid) */ if (FD_ISSET(rfd, &readfds)) { #ifdef DEBUG - status("FIFO read; "); + erts_run_erl_log_status("FIFO read; "); #endif if ((len = sf_read(rfd, buf, BUFSIZ)) < 0) { sf_close(rfd); @@ -666,7 +456,7 @@ static void pass_on(pid_t childpid) * should succeed. But in case of error, we just ignore it. */ if ((wfd = sf_open(fifo1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { - status("Client expected on FIFO %s, but can't open (len=%d)\n", + erts_run_erl_log_status("Client expected on FIFO %s, but can't open (len=%d)\n", fifo1, len); sf_close(rfd); rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0); @@ -678,7 +468,7 @@ static void pass_on(pid_t childpid) } else { #ifdef DEBUG - status("run_erl: %s opened for writing\n", fifo1); + erts_run_erl_log_status("run_erl: %s opened for writing\n", fifo1); #endif } } @@ -694,14 +484,15 @@ static void pass_on(pid_t childpid) /* Write the message */ #ifdef DEBUG - status("Pty master write; "); + erts_run_erl_log_status("Pty master write; "); #endif - len = extract_ctrl_seq(buf, len); + len = erts_run_erl_extract_ctrl_seq(buf, len); if(len==1 && buf[0] == '\003') { kill(childpid,SIGINT); - } - else if (len>0 && write_all(mfd, buf, len) != len) { + } + else if (len>0 && erts_run_erl_write_all(mfd, buf, len) != len) + { ERRNO_ERR0(LOG_ERR,"Error in writing to terminal."); sf_close(rfd); if(wfd) sf_close(wfd); @@ -710,7 +501,7 @@ static void pass_on(pid_t childpid) } } #ifdef DEBUG - status("OK\n"); + erts_run_erl_log_status("OK\n"); #endif } } @@ -720,173 +511,6 @@ static void catch_sigchild(int sig) { } -/* - * next_log: - * Returns the index number that follows the given index number. - * (Wrapping after log_generations) - */ -static int next_log(int log_num) { - return log_num>=log_generations?1:log_num+1; -} - -/* - * prev_log: - * Returns the index number that precedes the given index number. - * (Wrapping after log_generations) - */ -static int prev_log(int log_num) { - return log_num<=1?log_generations:log_num-1; -} - -/* - * find_next_log_num() - * Searches through the log directory to check which logs that already - * exist. It finds the "hole" in the sequence, and returns the index - * number for the last log in the log sequence. If there is no hole, index - * 1 is returned. - */ -static int find_next_log_num(void) { - int i, next_gen, log_gen; - DIR *dirp; - struct dirent *direntp; - int log_exists[LOG_MAX_GENERATIONS+1]; - int stub_len = strlen(LOG_STUBNAME); - - /* Initialize exiting log table */ - - for(i=log_generations; i>=0; i--) - log_exists[i] = 0; - dirp = opendir(log_dir); - if(!dirp) { - ERRNO_ERR1(LOG_ERR,"Can't access log directory '%s'", log_dir); - exit(1); - } - - /* Check the directory for existing logs */ - - while((direntp=readdir(dirp)) != NULL) { - if(strncmp(direntp->d_name,LOG_STUBNAME,stub_len)==0) { - int num = atoi(direntp->d_name+stub_len); - if(num < 1 || num > log_generations) - continue; - log_exists[num] = 1; - } - } - closedir(dirp); - - /* Find out the next available log file number */ - - next_gen = 0; - for(i=log_generations; i>=0; i--) { - if(log_exists[i]) - if(next_gen) - break; - else - ; - else - next_gen = i; - } - - /* Find out the current log file number */ - - if(next_gen) - log_gen = prev_log(next_gen); - else - log_gen = 1; - - return log_gen; -} /* find_next_log_num() */ - -/* open_log() - * Opens a log file (with given index) for writing. Writing may be - * at the end or a trucnating write, according to flags. - * A LOGGING STARTED and time stamp message is inserted into the log file - */ -static int open_log(int log_num, int flags) -{ - char buf[FILENAME_MAX]; - time_t now; - struct tm *tmptr; - char log_buffer[ALIVE_BUFFSIZ+1]; - int lfd; - - /* Remove the next log (to keep a "hole" in the log sequence) */ - sn_printf(buf, sizeof(buf), "%s/%s%d", - log_dir, LOG_STUBNAME, next_log(log_num)); - unlink(buf); - - /* Create or continue on the current log file */ - sn_printf(buf, sizeof(buf), "%s/%s%d", log_dir, LOG_STUBNAME, log_num); - if((lfd = sf_open(buf, flags, LOG_PERM))<0){ - ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf); - exit(1); - } - - /* Write a LOGGING STARTED and time stamp into the log file */ - time(&now); - if (log_alive_in_gmt) { - tmptr = gmtime(&now); - } else { - tmptr = localtime(&now); - } - if (!strftime(log_buffer, ALIVE_BUFFSIZ, log_alive_format, - tmptr)) { - strn_cpy(log_buffer, sizeof(log_buffer), - "(could not format time in 256 positions " - "with current format string.)"); - } - log_buffer[ALIVE_BUFFSIZ] = '\0'; - - sn_printf(buf, sizeof(buf), "\n=====\n===== LOGGING STARTED %s\n=====\n", - log_buffer); - if (write_all(lfd, buf, strlen(buf)) < 0) - status("Error in writing to log.\n"); - -#if USE_FSYNC - fsync(lfd); -#endif - - return lfd; -} - -/* write_to_log() - * Writes a message to a log file. If the current log file is full, - * a new log file is opened. - */ -static void write_to_log(int* lfd, int* log_num, char* buf, int len) -{ - int size; - - /* Decide if new logfile needed, and open if so */ - - size = lseek(*lfd,0,SEEK_END); - if(size+len > log_maxsize) { - sf_close(*lfd); - *log_num = next_log(*log_num); - *lfd = open_log(*log_num, O_RDWR|O_CREAT|O_TRUNC|O_SYNC); - } - - /* Write to log file */ - - if (write_all(*lfd, buf, len) < 0) { - status("Error in writing to log.\n"); - } - -#if USE_FSYNC - fsync(*lfd); -#endif -} - -/* create_fifo() - * Creates a new fifo with the given name and permission. - */ -static int create_fifo(char *name, int perm) -{ - if ((mkfifo(name, perm) < 0) && (errno != EEXIST)) - return -1; - return 0; -} - /* open_pty_master() * Find a master device, open and return fd and slave device name. @@ -1083,9 +707,9 @@ static void exec_shell(char **argv) else argv[0] = sh; argv[1] = "-c"; - status("Args before exec of shell:\n"); + erts_run_erl_log_status("Args before exec of shell:\n"); for (vp = argv, i = 0; *vp; vp++, i++) - status("argv[%d] = %s\n", i, *vp); + erts_run_erl_log_status("argv[%d] = %s\n", i, *vp); if (stdstatus) { fclose(stdstatus); } @@ -1096,26 +720,6 @@ static void exec_shell(char **argv) ERRNO_ERR0(LOG_ERR,"Could not execv"); } -/* status() - * Prints the arguments to a status file - * Works like printf (see vfrpintf) - */ -static void status(const char *format,...) -{ - va_list args; - time_t now; - - if (stdstatus == NULL) - stdstatus = fopen(statusfile, "w"); - if (stdstatus == NULL) - return; - now = time(NULL); - fprintf(stdstatus, "run_erl [%d] %s", (int)getpid(), ctime(&now)); - va_start(args, format); - vfprintf(stdstatus, format, args); - va_end(args); - fflush(stdstatus); -} static void daemon_init(void) /* As R Stevens wants it, to a certain extent anyway... */ @@ -1155,47 +759,10 @@ static void daemon_init(void) run_daemon = 1; } -/* error_logf() - * Prints the arguments to stderr or syslog - * Works like printf (see vfprintf) - */ -static void error_logf(int priority, int line, const char *format, ...) -{ - va_list args; - va_start(args, format); - -#ifdef HAVE_SYSLOG_H - if (run_daemon) { - vsyslog(priority,format,args); - } - else -#endif - { - time_t now = time(NULL); - fprintf(stderr, "run_erl:%d [%d] %s", line, (int)getpid(), ctime(&now)); - vfprintf(stderr, format, args); - } - va_end(args); -} - static void usage(char *pname) { - fprintf(stderr, "Usage: %s (pipe_name|pipe_dir/) log_dir \"command [parameters ...]\"\n", pname); - fprintf(stderr, "\nYou may also set the environment variables RUN_ERL_LOG_GENERATIONS\n"); - fprintf(stderr, "and RUN_ERL_LOG_MAXSIZE to the number of log files to use and the\n"); - fprintf(stderr, "size of the log file when to switch to the next log file\n"); -} - -/* Instead of making sure basename exists, we do our own */ -static char *simple_basename(char *path) -{ - char *ptr; - for (ptr = path; *ptr != '\0'; ++ptr) { - if (*ptr == '/') { - path = ptr + 1; - } - } - return path; + fprintf(stderr, "Usage: "); + fprintf(stderr, RUN_ERL_USAGE, pname); } static void init_outbuf(void) @@ -1266,114 +833,6 @@ static void outbuf_append(const char* buf, int n) outbuf_in += n; } -/* Call write() until entire buffer has been written or error. - * Return len or -1. - */ -static int write_all(int fd, const char* buf, int len) -{ - int left = len; - int written; - for (;;) { - written = sf_write(fd,buf,left); - if (written == left) { - return len; - } - if (written < 0) { - return -1; - } - left -= written; - buf += written; - } -} - -static ssize_t sf_read(int fd, void *buffer, size_t len) { - ssize_t n = 0; - - do { n = read(fd, buffer, len); } while (n < 0 && errno == EINTR); - - return n; -} - -static ssize_t sf_write(int fd, const void *buffer, size_t len) { - ssize_t n = 0; - - do { n = write(fd, buffer, len); } while (n < 0 && errno == EINTR); - - return n; -} - -static int sf_open(const char *path, int type, mode_t mode) { - int fd = 0; - - do { fd = open(path, type, mode); } while(fd < 0 && errno == EINTR); - - return fd; -} -static int sf_close(int fd) { - int res = 0; - - do { res = close(fd); } while(fd < 0 && errno == EINTR); - - return res; -} -/* Extract any control sequences that are ment only for run_erl - * and should not be forwarded to the pty. - */ -static int extract_ctrl_seq(char* buf, int len) -{ - static const char prefix[] = "\033_"; - static const char suffix[] = "\033\\"; - char* bufend = buf + len; - char* start = buf; - char* command; - char* end; - - for (;;) { - start = find_str(start, bufend-start, prefix); - if (!start) break; - - command = start + strlen(prefix); - end = find_str(command, bufend-command, suffix); - if (end) { - unsigned col, row; - if (sscanf(command,"version=%u", &protocol_ver)==1) { - /*fprintf(stderr,"to_erl v%u\n", protocol_ver);*/ - } - else if (sscanf(command,"winsize=%u,%u", &col, &row)==2) { - set_window_size(col,row); - } - else { - ERROR2(LOG_ERR, "Ignoring unknown ctrl command '%.*s'\n", - (int)(end-command), command); - } - - /* Remove ctrl sequence from buf */ - end += strlen(suffix); - memmove(start, end, bufend-end); - bufend -= end - start; - } - else { - ERROR2(LOG_ERR, "Missing suffix in ctrl sequence '%.*s'\n", - (int)(bufend-start), start); - break; - } - } - return bufend - buf; -} - -static void set_window_size(unsigned col, unsigned row) -{ -#ifdef TIOCSWINSZ - struct winsize ws; - ws.ws_col = col; - ws.ws_row = row; - if (ioctl(mfd, TIOCSWINSZ, &ws) < 0) { - ERRNO_ERR0(LOG_ERR,"Failed to set window size"); - } -#endif -} - - #ifdef DEBUG #define S(x) ((x) > 0 ? 1 : 0) diff --git a/erts/etc/unix/run_erl.h b/erts/etc/unix/run_erl.h deleted file mode 100644 index 843cda680c..0000000000 --- a/erts/etc/unix/run_erl.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2008-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% - */ - -/* - * The protocol version number used between to_erl and run_erl. - */ -#define RUN_ERL_HI_VER 1 /* My preferred protocol version */ -#define RUN_ERL_LO_VER 0 /* The lowest version I accept to talk with */ - -/* Version history: - * 0: Older, without version handshake - * 1: R12B-3, version handshake + window size ctrl - */ - diff --git a/erts/etc/unix/safe_string.c b/erts/etc/unix/safe_string.c deleted file mode 100644 index a77d9c5456..0000000000 --- a/erts/etc/unix/safe_string.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2008-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: safe_string.c - * - * This is a bunch of generic string operation - * that are safe regarding buffer overflow. - * - * All string functions terminate the process with an error message - * on buffer overflow. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include "safe_string.h" -#include -#include -#include -#include - - -static void string_overflow_handler(const char* format, ...) -{ - va_list args; - va_start(args, format); - vfprintf(stderr,format,args); - va_end(args); - exit(1); -} - -int vsn_printf(char* dst, size_t size, const char* format, va_list args) -{ - int ret = vsnprintf(dst, size, format, args); - if (ret >= size || ret < 0) { - string_overflow_handler("Buffer truncated '%s'\n",dst); - } - return ret; -} - -int sn_printf(char* dst, size_t size, const char* format, ...) -{ - va_list args; - int ret; - va_start(args, format); - ret = vsn_printf(dst,size,format,args); - va_end(args); - return ret; -} - -int strn_cpy(char* dst, size_t size, const char* src) -{ - return sn_printf(dst,size,"%s",src); -} - -int strn_cat(char* dst, size_t size, const char* src) -{ - return strn_catf(dst,size,"%s",src); -} - -int strn_catf(char* dst, size_t size, const char* format, ...) -{ - int ret; - va_list args; -#ifdef _GNU_SOURCE - int len = strnlen(dst,size); -#else - int len = strlen(dst); -#endif - - if (len >= size) { - string_overflow_handler("Buffer already overflowed '%.*s'\n", - size, dst); - } - va_start(args, format); - ret = vsn_printf(dst+len, size-len, format, args); - va_end(args); - return len+ret; -} - -char* find_str(const char* haystack, int hsize, const char* needle) -{ - int i = 0; - int nsize = strlen(needle); - hsize -= nsize - 1; - for (i=0; i dest) { - for (i=0; i=0; i--) ((char*)dest)[i] = ((char*)src)[i]; - } - return dest; -} -#endif /* HAVE_MEMMOVE */ - diff --git a/erts/etc/unix/safe_string.h b/erts/etc/unix/safe_string.h deleted file mode 100644 index c70e528814..0000000000 --- a/erts/etc/unix/safe_string.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2008-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: safe_string.h - * - * This is an interface to a bunch of generic string operation - * that are safe regarding buffer overflow. - * - * All string functions terminate the process with an error message - * on buffer overflow. - */ - -#include -#include - -/* Like vsnprintf() - */ -int vsn_printf(char* dst, size_t size, const char* format, va_list args); - -/* Like snprintf() - */ -int sn_printf(char* dst, size_t size, const char* format, ...); - -/* Like strncpy() - * Returns length of copied string. - */ -int strn_cpy(char* dst, size_t size, const char* src); - -/* Almost like strncat() - * size is sizeof entire dst buffer. - * Returns length of resulting string. - */ -int strn_cat(char* dst, size_t size, const char* src); - -/* Combination of strncat() and snprintf() - * size is sizeof entire dst buffer. - * Returns length of resulting string. - */ -int strn_catf(char* dst, size_t size, const char* format, ...); - -/* Simular to strstr() but search size bytes of haystack - * without regard to '\0' characters. - */ -char* find_str(const char* haystack, int size, const char* needle); - -#ifndef HAVE_MEMMOVE -void* memmove(void *dest, const void *src, size_t n); -#endif - diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c index d7e1127057..38a94ed9c3 100644 --- a/erts/etc/unix/to_erl.c +++ b/erts/etc/unix/to_erl.c @@ -16,592 +16,9 @@ * * %CopyrightEnd% */ -/* - * Module: to_erl.c - * - * This module implements a process that opens two specified FIFOs, one - * for reading and one for writing; reads from its stdin, and writes what - * it has read to the write FIF0; reads from the read FIFO, and writes to - * its stdout. - * - ________ _________ - | |--<-- pipe.r (fifo1) --<--| | - | to_erl | | run_erl | (parent) - |________|-->-- pipe.w (fifo2) -->--|_________| - ^ master pty - | - | slave pty - ____V____ - | | - | "erl" | (child) - |_________| - */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_SYS_IOCTL_H -# include -#endif - -#include "run_erl.h" -#include "safe_string.h" /* strn_cpy, strn_catf, sn_printf, etc. */ - -#if defined(O_NONBLOCK) -# define DONT_BLOCK_PLEASE O_NONBLOCK -#else -# define DONT_BLOCK_PLEASE O_NDELAY -# if !defined(EAGAIN) -# define EAGAIN -3898734 -# endif -#endif - -#ifdef HAVE_STRERROR -# define STRERROR(x) strerror(x) -#else -# define STRERROR(x) "" -#endif - -#define noDEBUG - -#define PIPE_DIR "/tmp/" -#define PIPE_STUBNAME "erlang.pipe" -#define PIPE_STUBLEN strlen(PIPE_STUBNAME) - -#ifdef DEBUG -#define STATUS(s) { fprintf(stderr, (s)); fflush(stderr); } -#else -#define STATUS(s) -#endif - -#ifndef FILENAME_MAX -#define FILENAME_MAX 250 -#endif - -static struct termios tty_smode, tty_rmode; -static int tty_eof = 0; -static int recv_sig = 0; -static int protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ - -static int write_all(int fd, const char* buf, int len); -static int window_size_seq(char* buf, size_t bufsz); -static int version_handshake(char* buf, int len, int wfd); -#ifdef DEBUG -static void show_terminal_settings(struct termios *); -#endif - -static void handle_ctrlc(int sig) -{ - /* Reinstall the handler, and signal break flag */ - signal(SIGINT,handle_ctrlc); - recv_sig = SIGINT; -} - -static void handle_sigwinch(int sig) -{ - recv_sig = SIGWINCH; -} - -static void usage(char *pname) -{ - fprintf(stderr, "Usage: %s [-h|-F] [pipe_name|pipe_dir/]\n", pname); - fprintf(stderr, "\t-h\tThis help text.\n"); - fprintf(stderr, "\t-F\tForce connection even though pipe is locked by other to_erl process.\n"); -} - -int main(int argc, char **argv) -{ - char FIFO1[FILENAME_MAX], FIFO2[FILENAME_MAX]; - int i, len, wfd, rfd; - fd_set readfds; - char buf[BUFSIZ]; - char pipename[FILENAME_MAX]; - int pipeIx = 1; - int force_lock = 0; - int got_some = 0; - - if (argc >= 2 && argv[1][0]=='-') { - switch (argv[1][1]) { - case 'h': - usage(argv[0]); - exit(1); - case 'F': - force_lock = 1; - break; - default: - fprintf(stderr,"Invalid option '%s'\n",argv[1]); - exit(1); - } - pipeIx = 2; - } - -#ifdef DEBUG - fprintf(stderr, "%s: pid is : %d\n", argv[0], (int)getpid()); -#endif - - strn_cpy(pipename, sizeof(pipename), - (argv[pipeIx] ? argv[pipeIx] : PIPE_DIR)); - - if(*pipename && pipename[strlen(pipename)-1] == '/') { - /* The user wishes us to find a pipe name in the specified */ - /* directory */ - int highest_pipe_num = 0; - DIR *dirp; - struct dirent *direntp; - - dirp = opendir(pipename); - if(!dirp) { - fprintf(stderr, "Can't access pipe directory %s: %s\n", pipename, strerror(errno)); - exit(1); - } - - /* Check the directory for existing pipes */ - - while((direntp=readdir(dirp)) != NULL) { - if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) { - int num = atoi(direntp->d_name+PIPE_STUBLEN+1); - if(num > highest_pipe_num) - highest_pipe_num = num; - } - } - closedir(dirp); - strn_catf(pipename, sizeof(pipename), (highest_pipe_num?"%s.%d":"%s"), - PIPE_STUBNAME, highest_pipe_num); - } /* if */ - - /* read FIFO */ - sn_printf(FIFO1,sizeof(FIFO1),"%s.r",pipename); - /* write FIFO */ - sn_printf(FIFO2,sizeof(FIFO2),"%s.w",pipename); - - /* Check that nobody is running to_erl on this pipe already */ - if ((wfd = open (FIFO1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { - /* Open as server succeeded -- to_erl is already running! */ - close(wfd); - fprintf(stderr, "Another to_erl process already attached to pipe " - "%s.\n", pipename); - if (force_lock) { - fprintf(stderr, "But we proceed anyway by force (-F).\n"); - } - else { - exit(1); - } - } - - if ((rfd = open (FIFO1, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) { -#ifdef DEBUG - fprintf(stderr, "Could not open FIFO %s for reading.\n", FIFO1); -#endif - fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno)); - exit(1); - } -#ifdef DEBUG - fprintf(stderr, "to_erl: %s opened for reading\n", FIFO1); -#endif - - if ((wfd = open (FIFO2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { -#ifdef DEBUG - fprintf(stderr, "Could not open FIFO %s for writing.\n", FIFO2); -#endif - fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno)); - close(rfd); - exit(1); - } -#ifdef DEBUG - fprintf(stderr, "to_erl: %s opened for writing\n", FIFO2); -#endif - - fprintf(stderr, "Attaching to %s (^D to exit)\n\n", pipename); - - /* Set break handler to our handler */ - signal(SIGINT,handle_ctrlc); - - /* - * Save the current state of the terminal, and set raw mode. - */ - if (tcgetattr(0, &tty_rmode) , 0) { - fprintf(stderr, "Cannot get terminals current mode\n"); - exit(-1); - } - tty_smode = tty_rmode; - tty_eof = '\004'; /* Ctrl+D to exit */ -#ifdef DEBUG - show_terminal_settings(&tty_rmode); -#endif - tty_smode.c_iflag = - 1*BRKINT |/*Signal interrupt on break.*/ - 1*IGNPAR |/*Ignore characters with parity errors.*/ - 1*ISTRIP |/*Strip character.*/ - 0; - -#if 0 -0*IGNBRK |/*Ignore break condition.*/ -0*PARMRK |/*Mark parity errors.*/ -0*INPCK |/*Enable input parity check.*/ -0*INLCR |/*Map NL to CR on input.*/ -0*IGNCR |/*Ignore CR.*/ -0*ICRNL |/*Map CR to NL on input.*/ -0*IUCLC |/*Map upper-case to lower-case on input.*/ -0*IXON |/*Enable start/stop output control.*/ -0*IXANY |/*Enable any character to restart output.*/ -0*IXOFF |/*Enable start/stop input control.*/ -0*IMAXBEL|/*Echo BEL on input line too long.*/ -#endif - - tty_smode.c_oflag = - 1*OPOST |/*Post-process output.*/ - 1*ONLCR |/*Map NL to CR-NL on output.*/ -#ifdef XTABS - 1*XTABS |/*Expand tabs to spaces. (Linux)*/ -#endif -#ifdef OXTABS - 1*OXTABS |/*Expand tabs to spaces. (FreeBSD)*/ -#endif -#ifdef NL0 - 1*NL0 |/*Select newline delays*/ -#endif -#ifdef CR0 - 1*CR0 |/*Select carriage-return delays*/ -#endif -#ifdef TAB0 - 1*TAB0 |/*Select horizontal tab delays*/ -#endif -#ifdef BS0 - 1*BS0 |/*Select backspace delays*/ -#endif -#ifdef VT0 - 1*VT0 |/*Select vertical tab delays*/ -#endif -#ifdef FF0 - 1*FF0 |/*Select form feed delays*/ -#endif - 0; - -#if 0 -0*OLCUC |/*Map lower case to upper on output.*/ -0*OCRNL |/*Map CR to NL on output.*/ -0*ONOCR |/*No CR output at column 0.*/ -0*ONLRET |/*NL performs CR function.*/ -0*OFILL |/*Use fill characters for delay.*/ -0*OFDEL |/*Fill is DEL, else NULL.*/ -0*NL1 | -0*CR1 | -0*CR2 | -0*CR3 | -0*TAB1 | -0*TAB2 | -0*TAB3 |/*Expand tabs to spaces.*/ -0*BS1 | -0*VT1 | -0*FF1 | -#endif - - /* JALI: removed setting the tty_smode.c_cflag flags, since this is not */ - /* advisable if this is a *real* terminal, such as the console. In fact */ - /* this may hang the entire machine, deep, deep down (signalling break */ - /* or toggling the abort switch doesn't help) */ - - tty_smode.c_lflag = - 0; - -#if 0 -0*ISIG |/*Enable signals.*/ -0*ICANON |/*Canonical input (erase and kill processing).*/ -0*XCASE |/*Canonical upper/lower presentation.*/ -0*ECHO |/*Enable echo.*/ -0*ECHOE |/*Echo erase character as BS-SP-BS.*/ -0*ECHOK |/*Echo NL after kill character.*/ -0*ECHONL |/*Echo NL.*/ -0*NOFLSH |/*Disable flush after interrupt or quit.*/ -0*TOSTOP |/*Send SIGTTOU for background output.*/ -0*ECHOCTL|/*Echo control characters as ^char, delete as ^?.*/ -0*ECHOPRT|/*Echo erase character as character erased.*/ -0*ECHOKE |/*BS-SP-BS erase entire line on line kill.*/ -0*FLUSHO |/*Output is being flushed.*/ -0*PENDIN |/*Retype pending input at next read or input character.*/ -0*IEXTEN |/*Enable extended (implementation-defined) functions.*/ -#endif - - tty_smode.c_cc[VMIN] =0;/* Note that VMIN is the same as VEOF! */ - tty_smode.c_cc[VTIME] =0;/* Note that VTIME is the same as VEOL! */ - tty_smode.c_cc[VINTR] =3; - - tcsetattr(0, TCSADRAIN, &tty_smode); - -#ifdef DEBUG - show_terminal_settings(&tty_smode); -#endif - /* - * "Write a ^L to the FIFO which causes the other end to redisplay - * the input line." - * This does not seem to work as was intended in old comment above. - * However, this control character is now (R12B-3) used by run_erl - * to trigger the version handshaking between to_erl and run_erl - * at the start of every new to_erl-session. - */ - - if (write(wfd, "\014", 1) < 0) { - fprintf(stderr, "Error in writing ^L to FIFO.\n"); - } - - /* - * read and write - */ - while (1) { - FD_ZERO(&readfds); - FD_SET(0, &readfds); - FD_SET(rfd, &readfds); - if (select(rfd + 1, &readfds, NULL, NULL, NULL) < 0) { - if (recv_sig) { - FD_ZERO(&readfds); - } - else { - fprintf(stderr, "Error in select.\n"); - break; - } - } - len = 0; - - /* - * Read from terminal and write to FIFO - */ - if (recv_sig) { - switch (recv_sig) { - case SIGINT: - fprintf(stderr, "[Break]\n\r"); - buf[0] = '\003'; - len = 1; - break; - case SIGWINCH: - len = window_size_seq(buf,sizeof(buf)); - break; - default: - fprintf(stderr,"Unexpected signal: %u\n",recv_sig); - } - recv_sig = 0; - } - else if (FD_ISSET(0, &readfds)) { - len = read(0, buf, sizeof(buf)); - if (len <= 0) { - close(rfd); - close(wfd); - if (len < 0) { - fprintf(stderr, "Error in reading from stdin.\n"); - } else { - fprintf(stderr, "[EOF]\n\r"); - } - break; - } - /* check if there is an eof character in input */ - for (i = 0; i < len && buf[i] != tty_eof; i++); - if (buf[i] == tty_eof) { - fprintf(stderr, "[Quit]\n\r"); - break; - } - } - - if (len) { -#ifdef DEBUG - if(write(1, buf, len)); -#endif - if (write_all(wfd, buf, len) != len) { - fprintf(stderr, "Error in writing to FIFO.\n"); - close(rfd); - close(wfd); - break; - } - STATUS("\" OK\r\n"); - } - - /* - * Read from FIFO, write to terminal. - */ - if (FD_ISSET(rfd, &readfds)) { - STATUS("FIFO read: "); - len = read(rfd, buf, BUFSIZ); - if (len < 0 && errno == EAGAIN) { - /* - * No data this time, but the writing end of the FIFO is still open. - * Do nothing. - */ - ; - } else if (len <= 0) { - /* - * Either an error or end of file. In either case, break out - * of the loop. - */ - close(rfd); - close(wfd); - if (len < 0) { - fprintf(stderr, "Error in reading from FIFO.\n"); - } else - fprintf(stderr, "[End]\n\r"); - break; - } else { - if (!got_some) { - if ((len=version_handshake(buf,len,wfd)) < 0) { - close(rfd); - close(wfd); - break; - } - if (protocol_ver >= 1) { - /* Tell run_erl size of terminal window */ - signal(SIGWINCH, handle_sigwinch); - raise(SIGWINCH); - } - got_some = 1; - } - - /* - * We successfully read at least one character. Write what we got. - */ - STATUS("Terminal write: \""); - if (write_all(1, buf, len) != len) { - fprintf(stderr, "Error in writing to terminal.\n"); - close(rfd); - close(wfd); - break; - } - STATUS("\" OK\r\n"); - } - } - } - - /* - * Reset terminal characterstics - * XXX - */ - tcsetattr(0, TCSADRAIN, &tty_rmode); - return 0; -} - -/* Call write() until entire buffer has been written or error. - * Return len or -1. - */ -static int write_all(int fd, const char* buf, int len) -{ - int left = len; - int written; - while (left) { - written = write(fd,buf,left); - if (written < 0) { - return -1; - } - left -= written; - buf += written; - } - return len; -} - -static int window_size_seq(char* buf, size_t bufsz) -{ -#ifdef TIOCGWINSZ - struct winsize ws; - static const char prefix[] = "\033_"; - static const char suffix[] = "\033\\"; - /* This Esc sequence is called "Application Program Command" - and seems suitable to use for our own customized stuff. */ - - if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) { - int len = sn_printf(buf, bufsz, "%swinsize=%u,%u%s", - prefix, ws.ws_col, ws.ws_row, suffix); - return len; - } -#endif /* TIOCGWINSZ */ - return 0; -} - -/* to_erl run_erl - * | | - * |---------- '\014' -------->| (session start) - * | | - * |<---- "[run_erl v1-0]" ----| (version interval) - * | | - * |--- Esc_"version=1"Esc\ -->| (common version) - * | | - */ -static int version_handshake(char* buf, int len, int wfd) -{ - unsigned re_high=0, re_low; - char *end = find_str(buf,len,"]\n"); - - if (end && sscanf(buf,"[run_erl v%u-%u",&re_high,&re_low)==2) { - char wbuf[30]; - int wlen; - - if (re_low > RUN_ERL_HI_VER || re_high < RUN_ERL_LO_VER) { - fprintf(stderr,"Incompatible versions: to_erl=v%u-%u run_erl=v%u-%u\n", - RUN_ERL_HI_VER, RUN_ERL_LO_VER, re_high, re_low); - return -1; - } - /* Choose highest common version */ - protocol_ver = re_high < RUN_ERL_HI_VER ? re_high : RUN_ERL_HI_VER; - - wlen = sn_printf(wbuf, sizeof(wbuf), "\033_version=%u\033\\", - protocol_ver); - if (write_all(wfd, wbuf, wlen) < 0) { - fprintf(stderr,"Failed to send version handshake\n"); - return -1; - } - end += 2; - len -= (end-buf); - memmove(buf,end,len); - - } - else { /* we assume old run_erl without version handshake */ - protocol_ver = 0; - } - - if (re_high != RUN_ERL_HI_VER) { - fprintf(stderr,"run_erl has different version, " - "using common protocol level %u\n", protocol_ver); - } - - return len; -} - -#ifdef DEBUG -#define S(x) ((x) > 0 ? 1 : 0) +#include "to_erl_common.h" -static void show_terminal_settings(struct termios *t) -{ - fprintf(stderr,"c_iflag:\n"); - fprintf(stderr,"Signal interrupt on break: BRKINT %d\n", S(t->c_iflag & BRKINT)); - fprintf(stderr,"Map CR to NL on input: ICRNL %d\n", S(t->c_iflag & ICRNL)); - fprintf(stderr,"Ignore break condition: IGNBRK %d\n", S(t->c_iflag & IGNBRK)); - fprintf(stderr,"Ignore CR: IGNCR %d\n", S(t->c_iflag & IGNCR)); - fprintf(stderr,"Ignore char with par. err's: IGNPAR %d\n", S(t->c_iflag & IGNPAR)); - fprintf(stderr,"Map NL to CR on input: INLCR %d\n", S(t->c_iflag & INLCR)); - fprintf(stderr,"Enable input parity check: INPCK %d\n", S(t->c_iflag & INPCK)); - fprintf(stderr,"Strip character ISTRIP %d\n", S(t->c_iflag & ISTRIP)); - fprintf(stderr,"Enable start/stop input ctrl IXOFF %d\n", S(t->c_iflag & IXOFF)); - fprintf(stderr,"ditto output ctrl IXON %d\n", S(t->c_iflag & IXON)); - fprintf(stderr,"Mark parity errors PARMRK %d\n", S(t->c_iflag & PARMRK)); - fprintf(stderr,"\n"); - fprintf(stderr,"c_oflag:\n"); - fprintf(stderr,"Perform output processing OPOST %d\n", S(t->c_oflag & OPOST)); - fprintf(stderr,"\n"); - fprintf(stderr,"c_cflag:\n"); - fprintf(stderr,"Ignore modem status lines CLOCAL %d\n", S(t->c_cflag & CLOCAL)); - fprintf(stderr,"\n"); - fprintf(stderr,"c_local:\n"); - fprintf(stderr,"Enable echo ECHO %d\n", S(t->c_lflag & ECHO)); - fprintf(stderr,"\n"); - fprintf(stderr,"c_cc:\n"); - fprintf(stderr,"c_cc[VEOF] %d\n", t->c_cc[VEOF]); +int main(int argc,char **argv) { + return to_erl(argc,argv); } -#endif -- cgit v1.2.3 From 5c299e355e05a464215e49cfd95f62b5194de609 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 8 Jan 2014 11:35:29 +0100 Subject: ose: Port run_erl and to_erl --- erts/emulator/sys/ose/erts.sig | 3 + erts/etc/common/Makefile.in | 64 +++- erts/etc/common/run_erl_common.c | 38 ++- erts/etc/common/run_erl_common.h | 4 + erts/etc/common/to_erl_common.c | 128 +++++++- erts/etc/ose/run_erl.c | 637 ++++++++++++++++++++++++++++++++++++++- erts/etc/ose/run_erl.h | 29 ++ erts/etc/ose/run_erl_main.c | 77 +++++ erts/etc/ose/to_erl.c | 34 --- 9 files changed, 942 insertions(+), 72 deletions(-) create mode 100644 erts/etc/ose/run_erl.h create mode 100644 erts/etc/ose/run_erl_main.c delete mode 100644 erts/etc/ose/to_erl.c (limited to 'erts') diff --git a/erts/emulator/sys/ose/erts.sig b/erts/emulator/sys/ose/erts.sig index 95c12652f2..78b883ee6c 100644 --- a/erts/emulator/sys/ose/erts.sig +++ b/erts/emulator/sys/ose/erts.sig @@ -11,4 +11,7 @@ #define ERTS_SIGNAL_OSE_DRV_ATTACH ERTS_OSE_SIGNAL_BASE+3 #define ERTS_SIGNAL_OSE_DRV_HUNT ERTS_OSE_SIGNAL_BASE+4 +#define ERTS_SIGNAL_RUN_ERL_SETUP ERTS_OSE_SIGNAL_BASE+100 +#define ERTS_SIGNAL_RUN_ERL_DAEMON ERTS_OSE_SIGNAL_BASE+101 + #endif diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index f5df53ec01..5be0942952 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -85,6 +85,16 @@ UXETC = ../unix OSEETC = ../ose WINETC = ../win32 +ifeq ($(TARGET), win32) +ETC = $(WINETC) +else +ifeq ($(findstring ose,$(TARGET)),ose) +ETC = $(OSEETC) +else +ETC = $(UXETC) +endif +endif + ifeq ($(TARGET), win32) ERLEXEC = erlexec.dll else @@ -172,7 +182,7 @@ ENTRY_OBJ= ERLSRV_OBJECTS= MC_OUTPUTS= INET_GETHOST = -INSTALL_EMBEDDED_PROGS = +INSTALL_EMBEDDED_PROGS = $(BINDIR)/run_erl_lm INSTALL_EMBEDDED_DATA = INSTALL_TOP = Install INSTALL_TOP_BIN = @@ -183,7 +193,7 @@ INSTALL_LIBS = INSTALL_OBJS = INSTALL_INCLUDES = TEXTFILES = Install erl.src -INSTALL_PROGS = +INSTALL_PROGS = $(INSTALL_EMBEDDED_PROGS) else # UNIX (!win32 && !ose) ENTRY_LDFLAGS= ENTRY_OBJ= @@ -193,11 +203,11 @@ INET_GETHOST = $(BINDIR)/inet_gethost@EXEEXT@ INSTALL_EMBEDDED_PROGS += $(BINDIR)/typer@EXEEXT@ $(BINDIR)/dialyzer@EXEEXT@ \ $(BINDIR)/erlc@EXEEXT@ $(BINDIR)/escript@EXEEXT@ $(BINDIR)/ct_run@EXEEXT@ \ $(BINDIR)/run_erl $(BINDIR)/to_erl $(BINDIR)/dyn_erl -INSTALL_EMBEDDED_DATA = ../unix/start.src ../unix/start_erl.src +INSTALL_EMBEDDED_DATA = $(UXETC)/start.src $(UXETC)/start_erl.src INSTALL_TOP = Install INSTALL_TOP_BIN = -INSTALL_MISC = ../unix/format_man_pages ../unix/makewhatis -INSTALL_SRC = ../unix/setuid_socket_wrap.c #delivered as an example +INSTALL_MISC = $(UXETC)/format_man_pages $(UXETC)/makewhatis +INSTALL_SRC = $(UXETC)/setuid_socket_wrap.c #delivered as an example ERLEXECDIR = . INSTALL_LIBS = INSTALL_OBJS = @@ -403,24 +413,24 @@ $(BINDIR)/inet_gethost@EXEEXT@: $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(ERTS_LIB # run_erl $(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(OBJDIR)/run_erl_common.o $(V_LD) $(LDFLAGS) -o $@ $^ $(LIBS) -$(OBJDIR)/run_erl.o: ../unix/run_erl.c ../common/run_erl_common.h $(RC_GENERATED) - $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c ../unix/run_erl.c +$(OBJDIR)/run_erl.o: $(ETC)/run_erl.c ../common/run_erl_common.h $(RC_GENERATED) + $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c $(ETC)/run_erl.c $(OBJDIR)/run_erl_common.o: ../common/run_erl_common.c ../common/run_erl_common.h $(RC_GENERATED) $(V_CC) $(CFLAGS) -o $@ -c $< # to_erl $(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o $(OBJDIR)/to_erl_common.o $(V_LD) $(LDFLAGS) -o $@ $^ -$(OBJDIR)/to_erl.o: ../unix/to_erl.c ../common/safe_string.h $(RC_GENERATED) - $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c ../unix/to_erl.c +$(OBJDIR)/to_erl.o: $(ETC)/to_erl.c ../common/safe_string.h $(RC_GENERATED) + $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c $(ETC)/to_erl.c $(OBJDIR)/to_erl_common.o: ../common/to_erl_common.c ../common/to_erl_common.h $(RC_GENERATED) $(V_CC) $(CFLAGS) -o $@ -c $< # dyn_erl $(BINDIR)/dyn_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o -$(OBJDIR)/dyn_erl.o: ../unix/dyn_erl.c $(RC_GENERATED) - $(V_CC) $(CFLAGS) -o $@ -c ../unix/dyn_erl.c +$(OBJDIR)/dyn_erl.o: $(UXETC)/dyn_erl.c $(RC_GENERATED) + $(V_CC) $(CFLAGS) -o $@ -c $(UXETC)/dyn_erl.c $(OBJDIR)/safe_string.o: ../common/safe_string.c $(RC_GENERATED) $(V_CC) $(CFLAGS) -o $@ -c ../common/safe_string.c @@ -464,18 +474,42 @@ $(OBJDIR)/ct_run.o: ct_run.c $(RC_GENERATED) -Install: ../unix/Install.src ../../vsn.mk $(TARGET)/Makefile +Install: $(UXETC)/Install.src ../../vsn.mk $(TARGET)/Makefile $(vsn_verbose)sed -e 's;%I_VSN%;$(VSN);' \ -e 's;%EMULATOR%;$(EMULATOR);' \ -e 's;%EMULATOR_NUMBER%;$(EMULATOR_NUMBER);' \ -e 's;%I_SYSTEM_VSN%;$(SYSTEM_VSN);' \ - ../unix/Install.src > Install + $(UXETC)/Install.src > Install -erl.src: ../unix/erl.src.src ../../vsn.mk $(TARGET)/Makefile +erl.src: $(UXETC)/erl.src.src ../../vsn.mk $(TARGET)/Makefile $(vsn_verbose)sed -e 's;%EMULATOR%;$(EMULATOR);' \ -e 's;%EMULATOR_NUMBER%;$(EMULATOR_NUMBER);' \ -e 's;%VSN%;$(VSN);' \ - ../unix/erl.src.src > erl.src + $(UXETC)/erl.src.src > erl.src + +#--------------------------------------------------------- +# OSE specific targets +#--------------------------------------------------------- +ifeq ($(findstring ose,$(TARGET)),ose) +$(OBJDIR)/ose_confd.o: $(OSE_CONFD) + $(V_CC) $(CFLAGS) -o $@ -c $< +$(OBJDIR)/crt0_lm.o: $(CRT0_LM) + $(V_CC) $(CFLAGS) -o $@ -c $< +OSE_LM_OBJS += $(OBJDIR)/ose_confd.o $(OBJDIR)/crt0_lm.o + +$(BINDIR)/run_erl_lm: $(OBJDIR)/run_erl_main.o $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(OBJDIR)/run_erl_common.o $(OBJDIR)/to_erl_common.o $(OSE_LM_OBJS) + $(call build-ose-load-module, $@, $^, $(LIBS), $(LMCONF)) + + +$(OBJDIR)/run_erl_main.o: $(OSEETC)/run_erl_main.c $(OSEETC)/run_erl.h ../common/to_erl_common.h $(RC_GENERATED) + $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c $(OSEETC)/run_erl_main.c + +endif + +#--------------------------------------------------------- +# End of ose specific targets. +#--------------------------------------------------------- + # ---------------------------------------------------- # Release Target diff --git a/erts/etc/common/run_erl_common.c b/erts/etc/common/run_erl_common.c index dc899c5349..bf55056090 100644 --- a/erts/etc/common/run_erl_common.c +++ b/erts/etc/common/run_erl_common.c @@ -36,6 +36,10 @@ # include #endif +#ifdef __OSE__ +# include "ramlog.h" +#endif + #include "run_erl_common.h" #include "safe_string.h" @@ -57,7 +61,9 @@ #define PIPE_STUBLEN strlen(PIPE_STUBNAME) #define PERM (S_IWUSR | S_IRUSR | S_IWOTH | S_IROTH | S_IWGRP | S_IRGRP) -#if !defined(O_SYNC) +/* OSE has defined O_SYNC but it is not recognized by open */ +#if !defined(O_SYNC) || defined(__OSE__) +#undef O_SYNC #define O_SYNC 0 #define USE_FSYNC 1 #endif @@ -81,8 +87,6 @@ static char log_alive_format[ALIVE_BUFFSIZ+1]; static int run_daemon = 0; static unsigned protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ -int erts_run_erl_log_alive_minutes = DEFAULT_LOG_ALIVE_MINUTES; - /* * Current log number and log fd */ @@ -94,7 +98,11 @@ static int lfd=0; * getenv_int: */ static char *getenv_int(const char *name) { +#ifdef __OSE__ + return get_env(get_bid(current_process()),name); +#else return getenv(name); +#endif } /* @@ -189,9 +197,7 @@ static int open_log(int log_num, int flags) /* Create or continue on the current log file */ sn_printf(buf, sizeof(buf), "%s/%s%d", log_dir, LOG_STUBNAME, log_num); - do { - lfd = open(buf, flags, LOG_PERM); - } while (lfd < 0 && errno == EINTR); + lfd = sf_open(buf, flags, LOG_PERM); if(lfd <0){ ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf); @@ -307,7 +313,11 @@ void erts_run_erl_log_status(const char *format,...) return; now = time(NULL); fprintf(stdstatus, "run_erl [%d] %s", +#ifdef __OSE__ + (int)current_process(), +#else (int)getpid(), +#endif ctime(&now)); va_start(args, format); vfprintf(stdstatus, format, args); @@ -330,11 +340,23 @@ void erts_run_erl_log_error(int priority, int line, const char *format, ...) vsyslog(priority,format,args); } else +#endif +#ifdef __OSE__ + if (RUN_DAEMON) { + char *buff = malloc(sizeof(char)*1024); + vsnprintf(buff,1024,format, args); + ramlog_printf(buff); + } + else #endif { time_t now = time(NULL); fprintf(stderr, "run_erl:%d [%d] %s", line, +#ifdef __OSE__ + (int)current_process(), +#else (int)getpid(), +#endif ctime(&now)); vfprintf(stderr, format, args); } @@ -544,10 +566,12 @@ int erts_run_erl_open_fifo(char *pipename,char *w_pipename,char *r_pipename) { PIPE_STUBNAME, highest_pipe_num+1); continue; } - fprintf(stderr, "Erlang already running on pipe %s.\n", pipename); + ERROR1(LOG_ERR, "Erlang already running on pipe %s.\n", pipename); + unlink(w_pipename); return 1; } if (create_fifo(r_pipename, PERM) < 0) { + unlink(w_pipename); ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for reading.", r_pipename); return 1; diff --git a/erts/etc/common/run_erl_common.h b/erts/etc/common/run_erl_common.h index dca1af93f2..3b763ad927 100644 --- a/erts/etc/common/run_erl_common.h +++ b/erts/etc/common/run_erl_common.h @@ -51,8 +51,12 @@ int erts_run_erl_write_all(int fd, const char* buf, int len); char *simple_basename(char *path); #ifndef LOG_ERR +#ifdef __OSE__ +#define LOG_ERR 0 +#else #define LOG_ERR NULL #endif +#endif #define ERROR0(Prio,Format) erts_run_erl_log_error(Prio,__LINE__,Format"\n") #define ERROR1(Prio,Format,A1) erts_run_erl_log_error(Prio,__LINE__,Format"\n",A1) diff --git a/erts/etc/common/to_erl_common.c b/erts/etc/common/to_erl_common.c index 4c38877277..1b2f27fa04 100644 --- a/erts/etc/common/to_erl_common.c +++ b/erts/etc/common/to_erl_common.c @@ -49,10 +49,19 @@ #include #include #include -#include #include -#include #include + +#ifdef __OSE__ +#include +#include "ose.h" +#include "efs.h" +#include "ose_spi/fm.sig" +#else /* __UNIX__ */ +#include +#include +#endif + #ifdef HAVE_SYS_IOCTL_H # include #endif @@ -78,7 +87,11 @@ #define noDEBUG +#ifdef __OSE__ +#define PIPE_DIR "/pipe/" +#else #define PIPE_DIR "/tmp/" +#endif #define PIPE_STUBNAME "erlang.pipe" #define PIPE_STUBLEN strlen(PIPE_STUBNAME) @@ -92,14 +105,38 @@ #define FILENAME_MAX 250 #endif -static struct termios tty_smode, tty_rmode; static int tty_eof = 0; -static int recv_sig = 0; static int protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ static int write_all(int fd, const char* buf, int len); -static int window_size_seq(char* buf, size_t bufsz); static int version_handshake(char* buf, int len, int wfd); + + +#ifdef __OSE__ + +#define SET_AIO(REQ,FD,SIZE,BUFF) \ + /* Make sure to clean data structure of previous request */ \ + memset(&(REQ),0,sizeof(REQ)); \ + (REQ).aio_fildes = FD; \ + (REQ).aio_offset = FM_POSITION_CURRENT; \ + (REQ).aio_nbytes = SIZE; \ + (REQ).aio_buf = BUFF; \ + (REQ).aio_sigevent.sigev_notify = SIGEV_NONE + +#define READ_AIO(REQ,FD,SIZE,BUFF) \ + SET_AIO(REQ,FD,SIZE,BUFF); \ + if (aio_read(&(REQ)) != 0) \ + fprintf(stderr,"aio_read of child_read_req(%d) failed\n",FD) + +union SIGNAL { + SIGSELECT signo; + struct FmReadPtr fm_read_ptr; +}; + +#else /* __UNIX__ */ +static int recv_sig = 0; +static struct termios tty_smode, tty_rmode; +static int window_size_seq(char* buf, size_t bufsz); #ifdef DEBUG static void show_terminal_settings(struct termios *); #endif @@ -115,6 +152,7 @@ static void handle_sigwinch(int sig) { recv_sig = SIGWINCH; } +#endif static void usage(char *pname) { @@ -126,13 +164,22 @@ int to_erl(int argc, char **argv) { char FIFO1[FILENAME_MAX], FIFO2[FILENAME_MAX]; int i, len, wfd, rfd; - fd_set readfds; - char buf[BUFSIZ]; char pipename[FILENAME_MAX]; int pipeIx = 1; int force_lock = 0; int got_some = 0; +#ifdef __OSE__ + struct aiocb stdin_read_req, pipe_read_req; + FmHandle stdin_fh, pipe_fh; + char *stdin_buf, *pipe_buf; + char *buf; + union SIGNAL *sig; +#else /* __UNIX__ */ + char buf[BUFSIZ]; + fd_set readfds; +#endif + if (argc >= 2 && argv[1][0]=='-') { switch (argv[1][1]) { case 'h': @@ -149,7 +196,13 @@ int to_erl(int argc, char **argv) } #ifdef DEBUG - fprintf(stderr, "%s: pid is : %d\n", argv[0], (int)getpid()); + fprintf(stderr, "%s: pid is : %d\n", argv[0],(int) +#ifdef __OSE__ + current_process() +#else /* __UNIX__ */ + getpid() +#endif + ); #endif strn_cpy(pipename, sizeof(pipename), @@ -187,6 +240,7 @@ int to_erl(int argc, char **argv) /* write FIFO */ sn_printf(FIFO2,sizeof(FIFO2),"%s.w",pipename); +#ifndef __OSE__ /* Check that nobody is running to_erl on this pipe already */ if ((wfd = open (FIFO1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { /* Open as server succeeded -- to_erl is already running! */ @@ -200,6 +254,7 @@ int to_erl(int argc, char **argv) exit(1); } } +#endif if ((rfd = open (FIFO1, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) { #ifdef DEBUG @@ -226,6 +281,7 @@ int to_erl(int argc, char **argv) fprintf(stderr, "Attaching to %s (^D to exit)\n\n", pipename); +#ifndef __OSE__ /* Set break handler to our handler */ signal(SIGINT,handle_ctrlc); @@ -344,6 +400,8 @@ int to_erl(int argc, char **argv) #ifdef DEBUG show_terminal_settings(&tty_smode); #endif + +#endif /* !__OSE__ */ /* * "Write a ^L to the FIFO which causes the other end to redisplay * the input line." @@ -357,10 +415,22 @@ int to_erl(int argc, char **argv) fprintf(stderr, "Error in writing ^L to FIFO.\n"); } +#ifdef __OSE__ + /* we have a tiny stack so we malloc the buffers */ + stdin_buf = malloc(sizeof(char) * BUFSIZ); + pipe_buf = malloc(sizeof(char) * BUFSIZ); + + efs_examine_fd(rfd,FLIB_FD_HANDLE,&pipe_fh); + efs_examine_fd(0,FLIB_FD_HANDLE,&stdin_fh); + READ_AIO(stdin_read_req,0,BUFSIZ,stdin_buf); + READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_buf); +#endif + /* * read and write */ while (1) { +#ifndef __OSE__ FD_ZERO(&readfds); FD_SET(0, &readfds); FD_SET(rfd, &readfds); @@ -393,8 +463,21 @@ int to_erl(int argc, char **argv) } recv_sig = 0; } - else if (FD_ISSET(0, &readfds)) { + else +#else /* __OSE__ */ + SIGSELECT sigsel[] = {0}; + sig = receive(sigsel); + len = 0; +#endif +#ifndef __OSE__ + if (FD_ISSET(0,&readfds)) { len = read(0, buf, sizeof(buf)); +#else /* __OSE__ */ + if (sig->signo == FM_READ_PTR_REPLY && + sig->fm_read_ptr.handle == stdin_fh) { + len = sig->fm_read_ptr.status == EFS_SUCCESS ? sig->fm_read_ptr.actual : -1; + buf = sig->fm_read_ptr.buffer; +#endif if (len <= 0) { close(rfd); close(wfd); @@ -406,7 +489,7 @@ int to_erl(int argc, char **argv) break; } /* check if there is an eof character in input */ - for (i = 0; i < len && buf[i] != tty_eof; i++); + for (i = 0; i < len-1 && buf[i] != tty_eof; i++); if (buf[i] == tty_eof) { fprintf(stderr, "[Quit]\n\r"); break; @@ -424,14 +507,25 @@ int to_erl(int argc, char **argv) break; } STATUS("\" OK\r\n"); +#ifdef __OSE__ + aio_dispatch(sig); + READ_AIO(stdin_read_req, 0, BUFSIZ, stdin_buf); +#endif } /* * Read from FIFO, write to terminal. */ +#ifndef __OSE__ if (FD_ISSET(rfd, &readfds)) { STATUS("FIFO read: "); len = read(rfd, buf, BUFSIZ); +#else /* __OSE__ */ + if (sig->signo == FM_READ_PTR_REPLY && + sig->fm_read_ptr.handle == pipe_fh) { + len = sig->fm_read_ptr.status == EFS_SUCCESS ? sig->fm_read_ptr.actual : -1; + buf = sig->fm_read_ptr.buffer; +#endif if (len < 0 && errno == EAGAIN) { /* * No data this time, but the writing end of the FIFO is still open. @@ -457,11 +551,13 @@ int to_erl(int argc, char **argv) close(wfd); break; } +#ifndef __OSE__ if (protocol_ver >= 1) { /* Tell run_erl size of terminal window */ signal(SIGWINCH, handle_sigwinch); raise(SIGWINCH); } +#endif got_some = 1; } @@ -476,15 +572,21 @@ int to_erl(int argc, char **argv) break; } STATUS("\" OK\r\n"); +#ifdef __OSE__ + aio_dispatch(sig); + READ_AIO(pipe_read_req, rfd, BUFSIZ, pipe_buf); +#endif } } } +#ifndef __OSE__ /* * Reset terminal characterstics * XXX */ tcsetattr(0, TCSADRAIN, &tty_rmode); +#endif return 0; } @@ -506,6 +608,7 @@ static int write_all(int fd, const char* buf, int len) return len; } +#ifndef __OSE__ static int window_size_seq(char* buf, size_t bufsz) { #ifdef TIOCGWINSZ @@ -523,6 +626,7 @@ static int window_size_seq(char* buf, size_t bufsz) #endif /* TIOCGWINSZ */ return 0; } +#endif /* !__OSE__ */ /* to_erl run_erl * | | @@ -574,7 +678,7 @@ static int version_handshake(char* buf, int len, int wfd) } -#ifdef DEBUG +#if defined(DEBUG) && !defined(__OSE__) #define S(x) ((x) > 0 ? 1 : 0) static void show_terminal_settings(struct termios *t) @@ -604,4 +708,4 @@ static void show_terminal_settings(struct termios *t) fprintf(stderr,"c_cc:\n"); fprintf(stderr,"c_cc[VEOF] %d\n", t->c_cc[VEOF]); } -#endif +#endif /* DEBUG && !__OSE__ */ diff --git a/erts/etc/ose/run_erl.c b/erts/etc/ose/run_erl.c index 7e62502be2..c1a258515c 100644 --- a/erts/etc/ose/run_erl.c +++ b/erts/etc/ose/run_erl.c @@ -25,10 +25,639 @@ # include "config.h" #endif +/* System includes */ +#include +#include +#include +#include +#include #include +#include +#include -int main(int argc, char **argv) -{ - fprintf(stderr, "run_erl is not supported on OSE targets.\n"); - return 1; +/* OSE includes */ +#include "ose.h" +#include "ose_spi/ose_spi.h" +#include "efs.h" +#include "pm.h" +#include "ose_spi/fm.sig" + +/* erts includes */ +#include "run_erl.h" +#include "run_erl_common.h" +#include "safe_string.h" /* sn_printf, strn_cpy, strn_cat, etc */ + +typedef struct RunErlSetup_ { + SIGSELECT signo; + int run_daemon; + char *logdir; + char *command; + char *pipename; + char *blockname; +} RunErlSetup; + +typedef struct ProgramState_ { + /* child process */ + int ifd, ofd; + OSDOMAIN domain; + PROCESS progpid, mainbid; + struct PmProgramInfo *info; + /* to_erl */ + char w_pipe[FILENAME_BUFSIZ], + r_pipe[FILENAME_BUFSIZ]; +} ProgramState; + +union SIGNAL { + SIGSELECT signo; + RunErlSetup setup; + struct FmReadPtr fm_read_ptr; + struct FmWritePtr fm_write_ptr; +}; + +static OSBOOLEAN hunt_in_block(char *block_name, + char *process_name, + PROCESS *pid); +static int create_child_process(char *command_string, char *blockname, + ProgramState *state); + + +static OSBOOLEAN hunt_in_block(char *block_name, + char *process_name, + PROCESS *pid) { + struct OS_pid_list *list; + PROCESS block_id = OSE_ILLEGAL_PROCESS; + int i; + char *name; + + *pid = OSE_ILLEGAL_PROCESS; + + list = get_bid_list(0); + + if (!list) + return 0; + + for (i = 0; i < list->count; i++) { + + if (list->list[i] == get_bid(current_process())) + continue; + + name = (char*)get_pid_info(list->list[i], OSE_PI_NAME); + if (name) { + if (strcmp(name,block_name) == 0) { + block_id = list->list[i]; + free_buf((union SIGNAL**)&name); + break; + } + free_buf((union SIGNAL**)&name); + } + } + + free_buf((union SIGNAL**)&list); + + if (block_id == OSE_ILLEGAL_PROCESS) + return 0; + + list = get_pid_list(block_id); + + if (!list) + return 0; + + for (i = 0; i < list->count; i++) { + name = (char*)get_pid_info(list->list[i], OSE_PI_NAME); + if (name) { + if (strcmp(name,process_name) == 0) { + *pid = list->list[i]; + free_buf((union SIGNAL**)&name); + break; + } + free_buf((union SIGNAL**)&name); + } + } + + free_buf((union SIGNAL**)&list); + + if (*pid == OSE_ILLEGAL_PROCESS) + return 0; + + return 1; + +} + + +static int create_child_process(char *command_string, char *blockname, + ProgramState *state) { + char *command = command_string; + char *argv; + int i = 0; + int ret_status; + PmStatus pm_status; + int tmp_io[2]; + int fd_arr[3]; + int ifd[2], ofd[2]; + char *handle; + struct PmLoadModuleInfoReply *mod_info; + + /* Parse out cmd and argv from the command string */ + while (1) { + if (command[i] == ' ' || command[i] == '\0') { + if (command[i] == '\0') + argv = NULL; + else { + command[i] = '\0'; + argv = command_string + i + 1; + } + break; + } + i++; + } + + if (blockname) + handle = blockname; + else + handle = simple_basename(command); + + if (ose_pm_load_module_info(handle,&mod_info) == PM_SUCCESS) { + /* Already installed */ + free_buf((union SIGNAL**)&mod_info); + } else if ((pm_status = ose_pm_install_load_module(0,"ELF",command,handle,0,0,NULL)) + != PM_SUCCESS) { + ERROR1(LOG_ERR,"ose_pm_install_load_module failed - pmstatus: 0x%08x\n", + pm_status); + return 0; + } + + state->domain = PM_NEW_DOMAIN; + + pm_status = ose_pm_create_program(&state->domain, handle, 0, 0 , NULL, + &state->progpid, &state->mainbid); + + if (pm_status != PM_SUCCESS) { + if (pm_status == PM_EINSTALL_HANDLE_IN_USE) + ERROR1(LOG_ERR,"ose_pm_create_program failed - " + "install handle \"%s\" is in use. You can specify another " + "install handle by using the -block option to run_erl.\n",handle); + else + ERROR1(LOG_ERR,"ose_pm_create_program failed - pmstatus: 0x%08x\n", + pm_status); + return 0; + } + + pm_status = ose_pm_program_info(state->progpid, &state->info); + /* FIXME don't forget to free this ((union SIGNAL **)&info) */ + if (pm_status != PM_SUCCESS) { + ERROR1(LOG_ERR,"ose_pm_program_info failed - pmstatus: 0x%08x\n", + pm_status); + return 0; + } + + /* We only clone stdin+stdout, what about stderr? */ + + /* create pipes */ + if (pipe(ifd) < 0) { + if (errno == ENOENT) + ERRNO_ERR0(LOG_ERR,"The /pipe file system is not available\n"); + else + ERRNO_ERR0(LOG_ERR,"pipe ifd failed\n"); + return 0; + } + + if (pipe(ofd) < 0) { + ERRNO_ERR0(LOG_ERR,"pipe ofd failed\n"); + return 0; + } + + /* FIXME Lock? */ + + /* backup our stdin stdout */ + if ((tmp_io[0] = dup(0)) < 0) { + ERRNO_ERR0(LOG_ERR,"dup 0 failed\n"); + return 0; + } + + if ((tmp_io[1] = dup(1)) < 0) { + ERRNO_ERR0(LOG_ERR,"dup 1 failed\n"); + return 0; + } + + /* set new pipe to fd 0,1 */ + if (dup2(ifd[1], 1) < 0) { + ERRNO_ERR0(LOG_ERR,"dup2 1 failed\n"); + return 0; + } + + if (dup2(ofd[0], 0) < 0) { + ERRNO_ERR0(LOG_ERR,"dup2 0 failed\n"); + return 0; + } + + /* clone array to newly created */ + fd_arr[0] = 2; /* Number of fd's */ + fd_arr[1] = 0; + fd_arr[2] = 1; + + if ((ret_status = efs_clone_array(state->info->main_process, fd_arr)) + != EFS_SUCCESS) { + ERROR1(LOG_ERR,"efs_close_array filed, errcode: %d\n", ret_status); + return 0; + } + + if (dup2(tmp_io[1], 1) < 0) { + ERRNO_ERR0(LOG_ERR,"restoring dup2 1 failed\n"); + return 0; + } + + if (dup2(tmp_io[0], 0) < 0) { + ERRNO_ERR0(LOG_ERR,"restoring dup2 1 failed\n"); + return 0; + } + + /* close loose-ends */ + sf_close(tmp_io[0]); + sf_close(tmp_io[1]); + sf_close(ifd[1]); + sf_close(ofd[0]); + state->ifd = ifd[0]; + state->ofd = ofd[1]; + + if (argv && set_env(state->progpid, "ARGV", argv)) { + ERRNO_ERR0(LOG_ERR,"something went wrong with set_env\n"); + } + + /* + * Start the program. + */ + pm_status = ose_pm_start_program(state->progpid); + if (pm_status != PM_SUCCESS) { + ERROR1(LOG_ERR,"ose_pm_install_load_module failed - pmstatus: 0x%08x\n", + pm_status); + return 0; + } + + return 1; +} + +#define SET_AIO(REQ,FD,SIZE,BUFF) \ + /* Make sure to clean data structure of previous request */ \ + memset(&(REQ),0,sizeof(REQ)); \ + (REQ).aio_fildes = FD; \ + (REQ).aio_offset = FM_POSITION_CURRENT; \ + (REQ).aio_nbytes = SIZE; \ + (REQ).aio_buf = BUFF; \ + (REQ).aio_sigevent.sigev_notify = SIGEV_NONE + +#define READ_AIO(REQ,FD,SIZE,BUFF) do { \ + SET_AIO(REQ,FD,SIZE,BUFF); \ + if (aio_read(&(REQ)) != 0) \ + ERRNO_ERR1(LOG_ERR,"aio_read of child_read_req(%d) failed\n",FD); \ + } while (0) + +#define WRITE_AIO(FD,SIZE,BUFF) do { \ + struct aiocb *write_req = malloc(sizeof(struct aiocb)); \ + char *write_buff = malloc(sizeof(char)*SIZE); \ + memcpy(write_buff,BUFF,SIZE); \ + SET_AIO(*write_req,FD,SIZE,write_buff); \ + if (aio_write(write_req) != 0) \ + ERRNO_ERR1(LOG_ERR,"aio_write of write_req(%d) failed\n",FD); \ + } while(0) + +int pass_on(ProgramState *state); +int pass_on(ProgramState *s) { + SIGSELECT sigsel[] = {0,FM_READ_PTR_REPLY}; + union SIGNAL *sig; + char child_read_buff[BUFSIZ], pipe_read_buff[BUFSIZ]; + struct aiocb child_read_req, pipe_read_req; + int rfd, wfd = 0; + FmHandle rfh, child_rfh; + int outstanding_writes = 0, got_some = 0, child_done = 0; + + if ((rfd = sf_open(s->r_pipe, O_RDONLY, 0)) < 0) { + ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.\n", s->r_pipe); + rfd = 0; + return 1; + } + + attach(NULL,s->progpid); + + /* Open the log file */ + erts_run_erl_log_open(); + + efs_examine_fd(rfd,FLIB_FD_HANDLE,&rfh); + efs_examine_fd(s->ifd,FLIB_FD_HANDLE,&child_rfh); + + READ_AIO(child_read_req,s->ifd,BUFSIZ,child_read_buff); + READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff); + + while (1) { + time_t now,last_activity; + + time(&last_activity); + sig = receive_w_tmo(erts_run_erl_log_alive_minutes*60000,sigsel); + + time(&now); + + if (sig) { + erts_run_erl_log_activity(0,now,last_activity); + } else { + /* timeout */ + erts_run_erl_log_activity(1,now,last_activity); + continue; + } + + switch (sig->signo) { + case OS_ATTACH_SIG: { + if (rfd) { sf_close(rfd); rfd = 0; } + free_buf(&sig); + child_done = 1; + /* Make sure to to let all outstanding write request finish */ + if (outstanding_writes) + break; + if (wfd) sf_close(wfd); + return 0; + } + case FM_WRITE_PTR_REPLY: { + if (sig->fm_write_ptr.status == EFS_SUCCESS) { + if (sig->fm_write_ptr.actual < sig->fm_write_ptr.requested) { + WRITE_AIO(wfd, sig->fm_write_ptr.requested-sig->fm_write_ptr.actual, + sig->fm_write_ptr.buffer+sig->fm_write_ptr.actual); + } + } else { + /* Assume to_erl has terminated. */ + sf_close(wfd); + wfd = 0; + } + free((char*)sig->fm_write_ptr.buffer); + aio_dispatch(sig); + if ((--outstanding_writes == 0) && child_done) { + if (wfd) sf_close(wfd); + return 0; + } + break; + } + case FM_READ_PTR_REPLY: { + /* Child fd */ + if (sig->fm_read_ptr.handle == child_rfh) { + + /* Child terminated */ + if (sig->fm_read_ptr.status != EFS_SUCCESS || + sig->fm_read_ptr.actual == 0) { + + if (rfd) { sf_close(rfd); rfd = 0; } + + if (sig->fm_read_ptr.status != EFS_SUCCESS) { + ERROR0(LOG_ERR,"Erlang closed the connection."); + aio_dispatch(sig); + return 1; + } + + /* child closed connection gracefully */ + aio_dispatch(sig); + if (outstanding_writes) { + child_done = 1; + break; + } + + if (wfd) sf_close(wfd); + + return 0; + } else { + erts_run_erl_log_write(sig->fm_read_ptr.buffer, + sig->fm_read_ptr.actual); + if (wfd) { + WRITE_AIO(wfd, sig->fm_read_ptr.actual, sig->fm_read_ptr.buffer); + outstanding_writes++; + } + aio_dispatch(sig); + READ_AIO(child_read_req, s->ifd,BUFSIZ, child_read_buff); + } + /* pipe fd */ + } else if (sig->fm_read_ptr.handle == rfh) { + if (sig->fm_read_ptr.status != EFS_SUCCESS) { + if(rfd) sf_close(rfd); + if(wfd) sf_close(wfd); + aio_dispatch(sig); + ERRNO_ERR0(LOG_ERR,"Error in reading from FIFO."); + return 1; + } + if (sig->fm_read_ptr.actual == 0) { + /* to_erl closed its end of the pipe */ + aio_dispatch(sig); + sf_close(rfd); + rfd = sf_open(s->r_pipe,O_RDONLY|DONT_BLOCK_PLEASE, 0); + if (rfd < 0) { + ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", + s->r_pipe); + rfd = 0; + } else { + READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff); + } + got_some = 0; /* reset for next session */ + } else { + int len = sig->fm_read_ptr.actual; + char *buffer = sig->fm_read_ptr.buffer; + if (!wfd) { + /* Try to open the write pipe to to_erl. Now that we got some data + * from to_erl, to_erl should already be reading this pipe - open + * should succeed. But in case of error, we just ignore it. + */ + if ((wfd = sf_open(s->w_pipe, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { + erts_run_erl_log_status("Client expected on FIFO %s, " + "but can't open (len=%d)\n", + s->w_pipe, sig->fm_read_ptr.actual); + sf_close(rfd); + rfd = sf_open(s->r_pipe, O_RDONLY|DONT_BLOCK_PLEASE, 0); + if (rfd < 0) { + ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", + s->r_pipe); + return 1; + } + wfd = 0; + } else { +#ifdef DEBUG + erts_run_erl_log_status("run_erl: %s opened for writing\n", + s->w_pipe); +#endif + } + } + + if (!got_some && wfd && buffer[0] == '\014') { + char wbuf[30]; + int wlen = sn_printf(wbuf,sizeof(wbuf),"[run_erl v%u-%u]\n", + RUN_ERL_HI_VER, RUN_ERL_LO_VER); + /* For some reason this, the first write aio seems to + not get an FM_WRITE_PTR_REPLY, so we do not do: + outstanding_writes++; + */ + WRITE_AIO(wfd, wlen, wbuf); + } + got_some = 1; + + /* Write the message */ +#ifdef DEBUG + erts_run_erl_log_status("Pty master write; "); +#endif + len = erts_run_erl_extract_ctrl_seq(buffer,len); + + if (len > 0) { + int wlen = erts_run_erl_write_all(s->ofd, buffer, len); + if (wlen != len) { + aio_dispatch(sig); + ERRNO_ERR0(LOG_ERR,"Error in writing to terminal."); + if(rfd) sf_close(rfd); + if(wfd) sf_close(wfd); + return 1; + } + } +#ifdef DEBUG + erts_run_erl_log_status("OK\n"); +#endif + aio_dispatch(sig); + READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff); + } + } + break; + } + default: { + free_buf(&sig); + break; + } + } + } +} + +OS_PROCESS(run_erl_process) { + char *logdir, *command, *blockname; + SIGSELECT sigsel[] = {1,ERTS_SIGNAL_RUN_ERL_SETUP}; + union SIGNAL *sig = receive(sigsel); + ProgramState state; + char pipename[FILENAME_BUFSIZ]; + + state.info = NULL; + + logdir = strdup(sig->setup.logdir); + command = strdup(sig->setup.command); + strn_cpy(pipename,sizeof(pipename),sig->setup.pipename); + + if (sig->setup.blockname) + blockname = strdup(sig->setup.blockname); + else + blockname = NULL; + + erts_run_erl_log_init(sig->setup.run_daemon, logdir); + + free_buf(&sig); + + if (erts_run_erl_open_fifo(pipename,state.w_pipe,state.r_pipe)) + kill_proc(current_process()); + + if (create_child_process(command,blockname,&state)) + pass_on(&state); + + free(logdir); + free(command); + if (blockname) + free(blockname); + + if (state.info) + free_buf(((union SIGNAL**)&state.info)); + + sf_close(state.ifd); + sf_close(state.ofd); + + unlink(state.w_pipe); + unlink(state.r_pipe); + + kill_proc(current_process()); +} + +int run_erl(int argc,char **argv) { + char *pipename, *logdir, *command, *blockname = NULL; + int pipename_len, logdir_len, command_len, blockname_len = 0; + int i = 1, run_daemon = 0; + PROCESS pid; + SIGSELECT sigsel[] = {0}; + union SIGNAL *sig; + + if(argc < 4) { + fprintf(stderr,RUN_ERL_USAGE,"run_erl"); + return 1; + } + + while (1) { + if (argv[i][0] != '-') + break; + if (!strcmp(argv[i],"-daemon")) { + run_daemon = 1; + i++; + continue; + } + if (!strcmp(argv[i],"-block")) { + blockname = argv[i+1]; + blockname_len = strlen(argv[i+1]) + 1; + i+=2; + continue; + } + fprintf(stderr,RUN_ERL_USAGE,"run_erl"); + return 1; + } + + pipename = argv[i++]; + logdir = argv[i++]; + command = argv[i++]; + + /* + 1 to include NULL at end */ + logdir_len = strlen(logdir) + 1; + command_len = strlen(command) + 1; + pipename_len = strlen(pipename) + 1; + + if (run_daemon) { + /* We request that the run_erl_process should be started from the + main process so that it does not die when the shell command + returns */ + PROCESS main_pid; + hunt_in_block("run_erl","main",&main_pid); + sig = alloc(sizeof(sig),ERTS_SIGNAL_RUN_ERL_DAEMON); + send(&sig,main_pid); + sig = receive(sigsel); + pid = sender(&sig); + free_buf(&sig); + } else { + pid = create_process(OS_BG_PROC,"run_erl_process", + run_erl_process, 0x800, + 0, 0, 0, NULL, 0, 0); + } + + sig = alloc(sizeof(RunErlSetup)+ + logdir_len+command_len+pipename_len+blockname_len, + ERTS_SIGNAL_RUN_ERL_SETUP); + sig->setup.run_daemon = run_daemon; + sig->setup.logdir = ((char*)sig)+sizeof(RunErlSetup); + sig->setup.command = ((char*)sig)+sizeof(RunErlSetup)+logdir_len; + sig->setup.pipename = ((char*)sig)+sizeof(RunErlSetup)+logdir_len+command_len; + if (blockname) + sig->setup.blockname = ((char*)sig)+sizeof(RunErlSetup)+ + logdir_len+command_len+pipename_len; + else + sig->setup.blockname = NULL; + + strcpy(sig->setup.logdir,logdir); + strcpy(sig->setup.command,command); + strcpy(sig->setup.pipename,pipename); + if (blockname) strcpy(sig->setup.blockname,blockname); + + send(&sig,pid); + + if (run_daemon) { + /* We are a daemon, error msgs will be sent to ramlog */ + start(pid); + return 1; + } + + /* We are not daemon, error msgs will be sent to stderr and we block here */ + efs_clone(pid); + start(pid); + + attach(NULL,pid); + sig = receive(sigsel); + + return 1; } diff --git a/erts/etc/ose/run_erl.h b/erts/etc/ose/run_erl.h new file mode 100644 index 0000000000..128f551670 --- /dev/null +++ b/erts/etc/ose/run_erl.h @@ -0,0 +1,29 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013. All Rights Reserved. + * + * The 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_RUN_ERL_H +#define ERL_RUN_ERL_H + +#include "ose.h" + +#include "erts.sig" + +int run_erl(int argc, char **argv); +OS_PROCESS(run_erl_process); + +#endif diff --git a/erts/etc/ose/run_erl_main.c b/erts/etc/ose/run_erl_main.c new file mode 100644 index 0000000000..d396ebe93b --- /dev/null +++ b/erts/etc/ose/run_erl_main.c @@ -0,0 +1,77 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013. All Rights Reserved. + * + * The 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: run_erl_main.c + * + * Container for load module that installs both run_erl and to_erl command. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "ose.h" +#include "shell.h" + +#include "run_erl_common.h" +#include "run_erl.h" +#include "to_erl_common.h" + +union SIGNAL { + SIGSELECT signo; +}; + +int main(int argc, char **argv) +{ + + char run_erl_usage[320], + to_erl_usage[120]; + + sprintf(run_erl_usage,RUN_ERL_USAGE,"run_erl [-daemon] [-block blockname]"); + sprintf(to_erl_usage,TO_ERL_USAGE,"pipename"); + + shell_add_cmd_attrs( + "run_erl",run_erl_usage, + "Redirect Erlang input and output streams", + run_erl,DEFAULT_PROC_TYPE,DEFAULT_PRIORITY,DEFAULT_STACK_SIZE); + + shell_add_cmd_attrs( + "to_erl",to_erl_usage, + "Attach to redirected Erlang input and output streams", + to_erl,DEFAULT_PROC_TYPE,DEFAULT_PRIORITY,DEFAULT_STACK_SIZE); + + while (1) { + static const SIGSELECT sigsel[] = {0}; + union SIGNAL *sig = receive(sigsel); + + if (sig->signo == ERTS_SIGNAL_RUN_ERL_DAEMON) { + PROCESS pid = create_process(OS_BG_PROC,"run_erl_daemon", + run_erl_process, 0x800, + 0, 0, 0, NULL, 0, 0); + send_w_s(&sig,pid,sender(&sig)); + } else { + printf("Got unexpected signal!"); + free_buf(&sig); + } + } + + return 1; +} diff --git a/erts/etc/ose/to_erl.c b/erts/etc/ose/to_erl.c deleted file mode 100644 index af672159e2..0000000000 --- a/erts/etc/ose/to_erl.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2013. All Rights Reserved. - * - * The 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: to_erl.c - * - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -int main(int argc, char **argv) -{ - fprintf(stderr, "to_erl is not supported on OSE targets.\n"); - return 1; -} -- cgit v1.2.3 From ae561e41e5578b373311556c9f9e619f277f825b Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 8 Jan 2014 16:42:12 +0100 Subject: ose: Refactor global variables to ppdata This is needed because when starting multiple processes from the same shell command they will see the same global data if using status variables. --- erts/etc/common/run_erl_common.c | 198 ++++++++++++++++++++++++--------------- erts/etc/common/run_erl_common.h | 3 - erts/etc/ose/run_erl.c | 2 +- erts/etc/unix/run_erl.c | 2 +- 4 files changed, 127 insertions(+), 78 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/run_erl_common.c b/erts/etc/common/run_erl_common.c index bf55056090..dc55c2bea4 100644 --- a/erts/etc/common/run_erl_common.c +++ b/erts/etc/common/run_erl_common.c @@ -68,6 +68,54 @@ #define USE_FSYNC 1 #endif +/* Global variable definitions + * We need this complex way of handling global variables because of how + * OSE works here. We want to make it possible to run the shell command + * run_erl multiple times with different global variables without them + * effecting eachother. + */ +typedef struct run_erl_ run_erl; + +#ifdef __OSE__ +static OSPPDKEY run_erl_pp_key; +#define RE_DATA (*(run_erl**)ose_get_ppdata(run_erl_pp_key)) +#else +static run_erl re; +#define RE_DATA (&re) +#endif + +#define STATUSFILE (RE_DATA->statusfile) +#define LOG_DIR (RE_DATA->log_dir) +#define STDSTATUS (RE_DATA->stdstatus) +#define LOG_GENERATIONS (RE_DATA->log_generations) +#define LOG_MAXSIZE (RE_DATA->log_maxsize) +#define LOG_ACTIVITY_MINUTES (RE_DATA->log_activity_minutes) +#define LOG_ALIVE_IN_GMT (RE_DATA->log_alive_in_gmt) +#define LOG_ALIVE_FORMAT (RE_DATA->log_alive_format) +#define RUN_DAEMON (RE_DATA->run_daemon) +#define LOG_ALIVE_MINUTES (RE_DATA->log_alive_minutes) +#define LOG_NUM (RE_DATA->log_num) +#define LFD (RE_DATA->lfd) +#define PROTOCOL_VER (RE_DATA->protocol_ver) + +struct run_erl_ { + /* constant config data */ + char statusfile[FILENAME_BUFSIZ]; + char log_dir[FILENAME_BUFSIZ]; + FILE *stdstatus; + int log_generations; + int log_maxsize; + int log_activity_minutes; + int log_alive_in_gmt; + char log_alive_format[ALIVE_BUFFSIZ+1]; + int run_daemon; + int log_alive_minutes; + /* Current log number and log fd */ + int log_num; + int lfd; + unsigned protocol_ver; +}; + /* prototypes */ static int next_log(int log_num); @@ -75,25 +123,6 @@ static int prev_log(int log_num); static int find_next_log_num(void); static int open_log(int log_num, int flags); -/* static data */ -static char statusfile[FILENAME_BUFSIZ]; -static char log_dir[FILENAME_BUFSIZ]; -static FILE *stdstatus = NULL; -static int log_generations = DEFAULT_LOG_GENERATIONS; -static int log_maxsize = DEFAULT_LOG_MAXSIZE; -static int log_activity_minutes = DEFAULT_LOG_ACTIVITY_MINUTES; -static int log_alive_in_gmt = 0; -static char log_alive_format[ALIVE_BUFFSIZ+1]; -static int run_daemon = 0; -static unsigned protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ - -/* - * Current log number and log fd - */ -static int log_num; -static int lfd=0; - - /* * getenv_int: */ @@ -111,7 +140,7 @@ static char *getenv_int(const char *name) { * (Wrapping after log_generations) */ static int next_log(int log_num) { - return log_num>=log_generations?1:log_num+1; + return log_num>=LOG_GENERATIONS?1:log_num+1; } /* @@ -120,7 +149,7 @@ static int next_log(int log_num) { * (Wrapping after log_generations) */ static int prev_log(int log_num) { - return log_num<=1?log_generations:log_num-1; + return log_num<=1?LOG_GENERATIONS:log_num-1; } /* @@ -139,11 +168,11 @@ static int find_next_log_num(void) { /* Initialize exiting log table */ - for(i=log_generations; i>=0; i--) + for(i=LOG_GENERATIONS; i>=0; i--) log_exists[i] = 0; - dirp = opendir(log_dir); + dirp = opendir(LOG_DIR); if(!dirp) { - ERRNO_ERR1(LOG_ERR,"Can't access log directory '%s'", log_dir); + ERRNO_ERR1(LOG_ERR,"Can't access log directory '%s'", LOG_DIR); exit(1); } @@ -152,7 +181,7 @@ static int find_next_log_num(void) { while((direntp=readdir(dirp)) != NULL) { if(strncmp(direntp->d_name,LOG_STUBNAME,stub_len)==0) { int num = atoi(direntp->d_name+stub_len); - if(num < 1 || num > log_generations) + if(num < 1 || num > LOG_GENERATIONS) continue; log_exists[num] = 1; } @@ -162,7 +191,7 @@ static int find_next_log_num(void) { /* Find out the next available log file number */ next_gen = 0; - for(i=log_generations; i>=0; i--) { + for(i=LOG_GENERATIONS; i>=0; i--) { if(log_exists[i]) if(next_gen) break; @@ -191,27 +220,27 @@ static int open_log(int log_num, int flags) /* Remove the next log (to keep a "hole" in the log sequence) */ sn_printf(buf, sizeof(buf), "%s/%s%d", - log_dir, LOG_STUBNAME, next_log(log_num)); + LOG_DIR, LOG_STUBNAME, next_log(log_num)); unlink(buf); /* Create or continue on the current log file */ - sn_printf(buf, sizeof(buf), "%s/%s%d", log_dir, LOG_STUBNAME, log_num); + sn_printf(buf, sizeof(buf), "%s/%s%d", LOG_DIR, LOG_STUBNAME, log_num); - lfd = sf_open(buf, flags, LOG_PERM); + LFD = sf_open(buf, flags, LOG_PERM); - if(lfd <0){ + if(LFD <0){ ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf); exit(1); } /* Write a LOGGING STARTED and time stamp into the log file */ time(&now); - if (log_alive_in_gmt) { + if (LOG_ALIVE_IN_GMT) { tmptr = gmtime(&now); } else { tmptr = localtime(&now); } - if (!strftime(log_buffer, ALIVE_BUFFSIZ, log_alive_format, + if (!strftime(log_buffer, ALIVE_BUFFSIZ, LOG_ALIVE_FORMAT, tmptr)) { strn_cpy(log_buffer, sizeof(log_buffer), "(could not format time in 256 positions " @@ -221,14 +250,14 @@ static int open_log(int log_num, int flags) sn_printf(buf, sizeof(buf), "\n=====\n===== LOGGING STARTED %s\n=====\n", log_buffer); - if (erts_run_erl_write_all(lfd, buf, strlen(buf)) < 0) + if (erts_run_erl_write_all(LFD, buf, strlen(buf)) < 0) erts_run_erl_log_status("Error in writing to log.\n"); #if USE_FSYNC - fsync(lfd); + fsync(LFD); #endif - return lfd; + return LFD; } /* Instead of making sure basename exists, we do our own */ @@ -307,12 +336,12 @@ void erts_run_erl_log_status(const char *format,...) va_list args; time_t now; - if (stdstatus == NULL) - stdstatus = fopen(statusfile, "w"); - if (stdstatus == NULL) + if (STDSTATUS == NULL) + STDSTATUS = fopen(STATUSFILE, "w"); + if (STDSTATUS == NULL) return; now = time(NULL); - fprintf(stdstatus, "run_erl [%d] %s", + fprintf(STDSTATUS, "run_erl [%d] %s", #ifdef __OSE__ (int)current_process(), #else @@ -320,12 +349,17 @@ void erts_run_erl_log_status(const char *format,...) #endif ctime(&now)); va_start(args, format); - vfprintf(stdstatus, format, args); + vfprintf(STDSTATUS, format, args); va_end(args); - fflush(stdstatus); + fflush(STDSTATUS); return; } +/* Fetch the current log alive minutes */ +int erts_run_erl_log_alive_minutes() { + return LOG_ALIVE_MINUTES; +} + /* error_logf() * Prints the arguments to stderr or syslog * Works like printf (see vfprintf) @@ -336,7 +370,7 @@ void erts_run_erl_log_error(int priority, int line, const char *format, ...) va_start(args, format); #ifdef HAVE_SYSLOG_H - if (run_daemon) { + if (RUN_DAEMON) { vsyslog(priority,format,args); } else @@ -373,24 +407,24 @@ int erts_run_erl_log_write(char* buf, size_t len) ssize_t res; /* Decide if new logfile needed, and open if so */ - size = lseek(lfd,0,SEEK_END); - if(size+len > log_maxsize) { + size = lseek(LFD,0,SEEK_END); + if(size+len > LOG_MAXSIZE) { int res; do { - res = close(lfd); + res = close(LFD); } while (res < 0 && errno == EINTR); - log_num = next_log(log_num); - lfd = open_log(log_num, O_RDWR|O_CREAT|O_TRUNC|O_SYNC); + LOG_NUM = next_log(LOG_NUM); + LFD = open_log(LOG_NUM, O_RDWR|O_CREAT|O_TRUNC|O_SYNC); } /* Write to log file */ - if ((res = erts_run_erl_write_all(lfd, buf, len)) < 0) { + if ((res = erts_run_erl_write_all(LFD, buf, len)) < 0) { erts_run_erl_log_status("Error in writing to log.\n"); } #if USE_FSYNC - fsync(lfd); + fsync(LFD); #endif return res; } @@ -399,17 +433,17 @@ int erts_run_erl_log_activity(int timeout,time_t now,time_t last_activity) { char log_alive_buffer[ALIVE_BUFFSIZ+1]; char buf[BUFSIZ]; - if (timeout || now - last_activity > log_activity_minutes*60) { + if (timeout || now - last_activity > LOG_ACTIVITY_MINUTES*60) { /* Either a time out: 15 minutes without action, */ /* or something is coming in right now, but it's a long time */ /* since last time, so let's write a time stamp this message */ struct tm *tmptr; - if (log_alive_in_gmt) { + if (LOG_ALIVE_IN_GMT) { tmptr = gmtime(&now); } else { tmptr = localtime(&now); } - if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, log_alive_format, + if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, LOG_ALIVE_FORMAT, tmptr)) { strn_cpy(log_alive_buffer, sizeof(log_alive_buffer), "(could not format time in 256 positions " @@ -426,30 +460,48 @@ int erts_run_erl_log_activity(int timeout,time_t now,time_t last_activity) { int erts_run_erl_log_open() { - log_num = find_next_log_num(); - lfd = open_log(log_num, O_RDWR|O_APPEND|O_CREAT|O_SYNC); + LOG_NUM = find_next_log_num(); + LFD = open_log(LOG_NUM, O_RDWR|O_APPEND|O_CREAT|O_SYNC); return 0; } int erts_run_erl_log_init(int daemon, char* logdir) { char *p; +#ifdef __OSE__ + run_erl **re_pp; + if (!run_erl_pp_key) + ose_create_ppdata("run_erl_ppdata",&run_erl_pp_key); + re_pp = (run_erl **)ose_get_ppdata(run_erl_pp_key); + *re_pp = malloc(sizeof(run_erl)); +#endif + + STDSTATUS = NULL; + LOG_GENERATIONS = DEFAULT_LOG_GENERATIONS; + LOG_MAXSIZE = DEFAULT_LOG_MAXSIZE; + LOG_ACTIVITY_MINUTES = DEFAULT_LOG_ACTIVITY_MINUTES; + LOG_ALIVE_IN_GMT = 0; + RUN_DAEMON = 0; + LOG_ALIVE_MINUTES = DEFAULT_LOG_ALIVE_MINUTES; + LFD = 0; + PROTOCOL_VER = RUN_ERL_LO_VER; /* assume lowest to begin with */ + /* Get values for LOG file handling from the environment */ if ((p = getenv_int("RUN_ERL_LOG_ALIVE_MINUTES"))) { - erts_run_erl_log_alive_minutes = atoi(p); - if (!erts_run_erl_log_alive_minutes) { + LOG_ALIVE_MINUTES = atoi(p); + if (!LOG_ALIVE_MINUTES) { ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ALIVE_MINUTES is 1 " "(current value is %s)",p); } - log_activity_minutes = erts_run_erl_log_alive_minutes / 3; - if (!log_activity_minutes) { - ++log_activity_minutes; + LOG_ACTIVITY_MINUTES = LOG_ALIVE_MINUTES / 3; + if (!LOG_ACTIVITY_MINUTES) { + ++LOG_ACTIVITY_MINUTES; } } if ((p = getenv_int( "RUN_ERL_LOG_ACTIVITY_MINUTES"))) { - log_activity_minutes = atoi(p); - if (!log_activity_minutes) { + LOG_ACTIVITY_MINUTES = atoi(p); + if (!LOG_ACTIVITY_MINUTES) { ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ACTIVITY_MINUTES is 1 " "(current value is %s)",p); } @@ -459,36 +511,36 @@ int erts_run_erl_log_init(int daemon, char* logdir) { ERROR1(LOG_ERR, "RUN_ERL_LOG_ALIVE_FORMAT can contain a maximum of " "%d characters", ALIVE_BUFFSIZ); } - strn_cpy(log_alive_format, sizeof(log_alive_format), p); + strn_cpy(LOG_ALIVE_FORMAT, sizeof(LOG_ALIVE_FORMAT), p); } else { - strn_cpy(log_alive_format, sizeof(log_alive_format), + strn_cpy(LOG_ALIVE_FORMAT, sizeof(LOG_ALIVE_FORMAT), DEFAULT_LOG_ALIVE_FORMAT); } if ((p = getenv_int("RUN_ERL_LOG_ALIVE_IN_UTC")) && strcmp(p,"0")) { - ++log_alive_in_gmt; + ++LOG_ALIVE_IN_GMT; } if ((p = getenv_int("RUN_ERL_LOG_GENERATIONS"))) { - log_generations = atoi(p); - if (log_generations < LOG_MIN_GENERATIONS) + LOG_GENERATIONS = atoi(p); + if (LOG_GENERATIONS < LOG_MIN_GENERATIONS) ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_GENERATIONS is %d", LOG_MIN_GENERATIONS); - if (log_generations > LOG_MAX_GENERATIONS) + if (LOG_GENERATIONS > LOG_MAX_GENERATIONS) ERROR1(LOG_ERR,"Maximum RUN_ERL_LOG_GENERATIONS is %d", LOG_MAX_GENERATIONS); } if ((p = getenv_int("RUN_ERL_LOG_MAXSIZE"))) { - log_maxsize = atoi(p); - if (log_maxsize < LOG_MIN_MAXSIZE) + LOG_MAXSIZE = atoi(p); + if (LOG_MAXSIZE < LOG_MIN_MAXSIZE) ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_MAXSIZE is %d", LOG_MIN_MAXSIZE); } - run_daemon = daemon; + RUN_DAEMON = daemon; - strn_cpy(log_dir, sizeof(log_dir), logdir); - strn_cpy(statusfile, sizeof(statusfile), log_dir); - strn_cat(statusfile, sizeof(statusfile), STATUSFILENAME); + strn_cpy(LOG_DIR, sizeof(LOG_DIR), logdir); + strn_cpy(STATUSFILE, sizeof(STATUSFILE), LOG_DIR); + strn_cat(STATUSFILE, sizeof(STATUSFILE), STATUSFILENAME); return 0; } diff --git a/erts/etc/common/run_erl_common.h b/erts/etc/common/run_erl_common.h index 3b763ad927..c47a0db054 100644 --- a/erts/etc/common/run_erl_common.h +++ b/erts/etc/common/run_erl_common.h @@ -71,9 +71,6 @@ char *simple_basename(char *path); #define ERRNO_ERR1(Prio,Format,A1) erts_run_erl_log_error(Prio,__LINE__,ADD_ERRNO(Format),A1) #define ERRNO_ERR2(Prio,Format,A1,A2) erts_run_erl_log_error(Prio,__LINE__,ADD_ERRNO(Format),A1,A2) -/* defined in run_common.c */ -extern int erts_run_erl_log_alive_minutes; - #define RUN_ERL_USAGE \ "%s (pipe_name|pipe_dir/) log_dir \"command [parameters ...]\"" \ "\n\nDESCRIPTION:\n" \ diff --git a/erts/etc/ose/run_erl.c b/erts/etc/ose/run_erl.c index c1a258515c..6bb59b7f7e 100644 --- a/erts/etc/ose/run_erl.c +++ b/erts/etc/ose/run_erl.c @@ -351,7 +351,7 @@ int pass_on(ProgramState *s) { time_t now,last_activity; time(&last_activity); - sig = receive_w_tmo(erts_run_erl_log_alive_minutes*60000,sigsel); + sig = receive_w_tmo(erts_run_erl_log_alive_minutes()*60000,sigsel); time(&now); diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index b3f0591b07..a6fc4c2bf5 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -328,7 +328,7 @@ static void pass_on(pid_t childpid) } time(&last_activity); /* don't assume old BSD bug */ - timeout.tv_sec = erts_run_erl_log_alive_minutes*60; + timeout.tv_sec = erts_run_erl_log_alive_minutes()*60; timeout.tv_usec = 0; ready = select(maxfd + 1, &readfds, writefds_ptr, NULL, &timeout); if (ready < 0) { -- cgit v1.2.3 From ff463a10f37ca74c0b6a8c0e24ce919e56b12bb8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 29 Jan 2014 14:38:56 +0100 Subject: ose: Add fair scheduling yields This is needed on OSs that do not do round robin scheduling of threads. --- erts/emulator/beam/erl_process.c | 10 ++++++++-- erts/emulator/beam/erl_process.h | 7 +++++++ erts/emulator/beam/erl_trace.c | 2 ++ erts/emulator/sys/ose/erl_ose_sys.h | 5 +++-- 4 files changed, 20 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 929dbfd16b..48e9b63462 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2620,6 +2620,8 @@ aux_thread(void *unused) erts_thr_progress_active(NULL, thr_prgr_active = 0); erts_thr_progress_prepare_wait(NULL); + ERTS_SCHED_FAIR_YIELD(); + flgs = sched_spin_wait(ssi, 0); if (flgs & ERTS_SSI_FLG_SLEEPING) { @@ -2733,6 +2735,8 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erts_thr_progress_prepare_wait(esdp); } + ERTS_SCHED_FAIR_YIELD(); + flgs = sched_spin_wait(ssi, spincount); if (flgs & ERTS_SSI_FLG_SLEEPING) { ASSERT(flgs & ERTS_SSI_FLG_WAITING); @@ -2798,7 +2802,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) #ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 sys_poll_try: while(!prepare_for_sys_schedule()) { - delay(1); + ERTS_SCHED_FAIR_YIELD(); } #endif @@ -8069,10 +8073,12 @@ Process *schedule(Process *p, int calls) erts_aint32_t aux_work; int leader_update = erts_thr_progress_update(esdp); aux_work = erts_atomic32_read_acqb(&esdp->ssi->aux_work); - if (aux_work | leader_update) { + if (aux_work | leader_update | ERTS_SCHED_FAIR) { erts_smp_runq_unlock(rq); if (leader_update) erts_thr_progress_leader_update(esdp); + else if (ERTS_SCHED_FAIR) + ERTS_SCHED_FAIR_YIELD(); if (aux_work) handle_aux_work(&esdp->aux_work_data, aux_work, 0); erts_smp_runq_lock(rq); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index cf20ed5acf..a86ac65aa2 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -607,6 +607,13 @@ extern ErtsAlignedSchedulerData *erts_aligned_dirty_io_scheduler_data; extern ErtsSchedulerData *erts_scheduler_data; #endif +#ifdef ERTS_SCHED_FAIR +#define ERTS_SCHED_FAIR_YIELD() ETHR_YIELD() +#else +#define ERTS_SCHED_FAIR 0 +#define ERTS_SCHED_FAIR_YIELD() +#endif + #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) int erts_smp_lc_runq_is_locked(ErtsRunQueue *); #endif diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 62a0b8ccea..43e7435d2a 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -3309,6 +3309,8 @@ sys_msg_dispatcher_func(void *unused) if (erts_thr_progress_update(NULL)) erts_thr_progress_leader_update(NULL); + ERTS_SCHED_FAIR_YIELD(); + #ifdef DEBUG_PRINTOUTS print_msg_type(smqp); #endif diff --git a/erts/emulator/sys/ose/erl_ose_sys.h b/erts/emulator/sys/ose/erl_ose_sys.h index db8607b650..cd66d95c26 100644 --- a/erts/emulator/sys/ose/erl_ose_sys.h +++ b/erts/emulator/sys/ose/erl_ose_sys.h @@ -41,8 +41,9 @@ #include "ethread.h" /* FIXME: configuration options */ -#define ERTS_SCHED_MIN_SPIN -#define ERTS_SCHED_ONLY_POLL_SCHED_1 +#define ERTS_SCHED_MIN_SPIN 1 +#define ERTS_SCHED_ONLY_POLL_SCHED_1 1 +#define ERTS_SCHED_FAIR 1 #define NO_SYSCONF 1 #define OPEN_MAX FOPEN_MAX -- cgit v1.2.3 From e48c536be1b21711e6e4b16bd8a9b74cf2aa019b Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 30 Jan 2014 18:47:52 +0100 Subject: ose: Update ddll interface after rebase to 17.0-rc1 Also deleted all the copy-paste stuff --- erts/emulator/sys/ose/erl_ose_sys_ddll.c | 77 ++------------------------------ 1 file changed, 3 insertions(+), 74 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/erl_ose_sys_ddll.c b/erts/emulator/sys/ose/erl_ose_sys_ddll.c index 4121199096..ebd80deeaf 100644 --- a/erts/emulator/sys/ose/erl_ose_sys_ddll.c +++ b/erts/emulator/sys/ose/erl_ose_sys_ddll.c @@ -19,7 +19,7 @@ /* * Interface functions to the dynamic linker using dl* functions. - * (As far as I know it works on SunOS 4, 5, Linux and FreeBSD. /Seb) + * (No support in OSE, we use static linkage instead) */ #ifdef HAVE_CONFIG_H @@ -29,68 +29,15 @@ #include "sys.h" #include "erl_vm.h" #include "global.h" -#ifdef HAVE_DLFCN_H -#include -#endif - - -/* some systems do not have RTLD_NOW defined, and require the "mode" - * argument to dload() always be 1. - */ -#ifndef RTLD_NOW -# define RTLD_NOW 1 -#endif - -#define MAX_NAME_LEN 255 /* XXX should we get the system path size? */ -#define EXT_LEN 3 -#define FILE_EXT ".so" /* extension appended to the filename */ - -static char **errcodes = NULL; -static int num_errcodes = 0; -static int num_errcodes_allocated = 0; - -#define my_strdup(WHAT) my_strdup_in(ERTS_ALC_T_DDLL_ERRCODES, WHAT); - -static char *my_strdup_in(ErtsAlcType_t type, char *what) -{ - char *res = erts_alloc(type, strlen(what) + 1); - strcpy(res, what); - return res; -} -static int find_errcode(char *string, ErtsSysDdllError* err) -{ - int i; - - if (err != NULL) { - erts_sys_ddll_free_error(err); /* in case we ignored an earlier error */ - err->str = my_strdup_in(ERTS_ALC_T_DDLL_TMP_BUF, string); - return 0; - } - for(i=0;i ERL_DE_DYNAMIC_ERROR_OFFSET) { - return "Unspecified error"; - } - actual_code = -1*(code - ERL_DE_DYNAMIC_ERROR_OFFSET); -#if defined(HAVE_DLOPEN) - { - char *msg; - - if (actual_code >= num_errcodes) { - msg = "Unknown dlload error"; - } else { - msg = errcodes[actual_code]; - } - return msg; - } -#endif - return "no error"; + return "Unspecified error"; } void erts_sys_ddll_free_error(ErtsSysDdllError* err) -- cgit v1.2.3 From d3b26048bff761f90da7e4ed9ef8453cbc5289b0 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 30 Jan 2014 18:20:24 +0100 Subject: ose: Remove receive in debug Sometimes scheduler 1 should go in here and it will have signals, so remove this. If needed later a check to see that fsem is used for scheduler 1 is needed. --- erts/lib_src/ose/ethr_event.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/lib_src/ose/ethr_event.c b/erts/lib_src/ose/ethr_event.c index 5905fe22a9..87294c98ea 100644 --- a/erts/lib_src/ose/ethr_event.c +++ b/erts/lib_src/ose/ethr_event.c @@ -186,26 +186,8 @@ wait__(ethr_event *e, int spincount) ETHR_ASSERT(val == ETHR_EVENT_OFF__); } -#if defined(DEBUG) - while (1) { - /* In debug we also receive any signals to make sure that we do - not get any! redir tables should send all to scheduler_1 */ - SIGSELECT sigsel[] = {0}; - union SIGNAL *sig = receive_fsem(OSE_NO_TIMEOUT,sigsel,1); - //ETHR_ASSERT(sig == OS_RCV_FSEM); - if (sig != OS_RCV_FSEM) { - int i; - printf("0x%x: Got signal in wait: %u ",current_process(),sig->signo); - for (i = 0; i < (sigsize(&sig) / sizeof(SIGSELECT)) && i < 5; i++) { - printf("%x ",sig[i+1]); - } - printf("\n"); - } else - break; - } -#else wait_fsem(1); -#endif + ETHR_ASSERT(get_fsem(current_process()) == 0); } } -- cgit v1.2.3 From b16f213791509f4de13215a83e68208128522520 Mon Sep 17 00:00:00 2001 From: Robert Paal Date: Tue, 18 Feb 2014 11:27:12 +0100 Subject: ose: Eliminating delays when trying to open files When opening filers on not mounted volumes the default timeout on OSE is quite big and since at startup we load something like 20 beam files this slows down startup by as much as 50 seconds. --- erts/emulator/sys/ose/default.lmconf | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/sys/ose/default.lmconf b/erts/emulator/sys/ose/default.lmconf index 6d3499df8f..17b7a1e5fa 100644 --- a/erts/emulator/sys/ose/default.lmconf +++ b/erts/emulator/sys/ose/default.lmconf @@ -12,3 +12,8 @@ HEAP_MAX_SIZE=1000000000 HEAP_SMALL_BUF_INIT_SIZE=64000000 HEAP_LARGE_BUF_THRESHOLD=16000000 HEAP_LOCK_TYPE=2 + +# Setting the environment variable EFS_RESOLVE_TMO on the block to 0. +# This will eliminiate delays when trying to open files on not mounted +# volumes. +EFS_RESOLVE_TMO=0 -- cgit v1.2.3 From 15350e561e8a8feca4a8073ec9db68fa92ed976f Mon Sep 17 00:00:00 2001 From: Robert Paal Date: Tue, 18 Feb 2014 15:55:04 +0100 Subject: ose: Added Enea copyright header to lcf files. Also removed softkernel lcf files. --- erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf | 31 ++++ erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf | 33 +++- erts/emulator/sys/ose/gcc_lm.lcf | 146 --------------- erts/emulator/sys/ose/gcc_lm_x86_4.4.3.lcf | 277 ----------------------------- erts/emulator/sys/ose/gcc_lm_x86_4.6.3.lcf | 252 -------------------------- 5 files changed, 59 insertions(+), 680 deletions(-) delete mode 100644 erts/emulator/sys/ose/gcc_lm.lcf delete mode 100644 erts/emulator/sys/ose/gcc_lm_x86_4.4.3.lcf delete mode 100644 erts/emulator/sys/ose/gcc_lm_x86_4.6.3.lcf (limited to 'erts') diff --git a/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf b/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf index 5ebc328616..a19d23facf 100644 --- a/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf +++ b/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf @@ -1,3 +1,34 @@ +/******************************************************************************* + * Copyright (C) 2013-2014 by Enea Software AB, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc") OUTPUT_ARCH("powerpc") ENTRY("crt0_lm") diff --git a/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf b/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf index a2399c93da..3440c2961b 100644 --- a/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf +++ b/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf @@ -1,9 +1,32 @@ /******************************************************************************* - * gcc_lm_ppc.lcf: GCC linker control file for PowerPC load modules - * @(#) $FilePath: /vobs/ose5/system/refsys/compilers/gcc_lm_ppc.lcf $ - * @(#) $FileRevision: /main/tb_current_ose5/10 $ - * $Author: joka $$Date: 01/21/13 16:35:41 $ - * $Copyright: (C) 2006 by Enea AB. All rights reserved. $ + * Copyright (C) 2013-2014 by Enea Software AB, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc") diff --git a/erts/emulator/sys/ose/gcc_lm.lcf b/erts/emulator/sys/ose/gcc_lm.lcf deleted file mode 100644 index 42b6f89851..0000000000 --- a/erts/emulator/sys/ose/gcc_lm.lcf +++ /dev/null @@ -1,146 +0,0 @@ -OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") -OUTPUT_ARCH("i386") -ENTRY("crt0_lm") -MEMORY -{ - rom : ORIGIN = 0x01000000, LENGTH = 0x01000000 - ram : ORIGIN = 0x02000000, LENGTH = 0x01000000 -} -PHDRS -{ - ph_conf PT_LOAD ; - ph_rom PT_LOAD ; - ph_ram PT_LOAD ; -} -SECTIONS -{ - .text : - { - *(.text_first) - *(.text) - *(.text.*) - *(.stub) - *(oscode) - *(.init*) - *(.fini*) - *(.gnu.warning) - *(.gnu.linkonce.t.*) - *(.glue_7t) - *(.glue_7) - } > rom :ph_rom = 0 - .ose_sfk_biosentry : - { - *(.ose_sfk_biosentry) - } > rom :ph_rom - .ctors : - { - __CTOR_LIST__ = .; - *(.ctors) - *(SORT(.ctors.*)) - __CTOR_END__ = .; - } > rom :ph_rom - .dtors : - { - __DTOR_LIST__ = .; - *(.dtors) - *(SORT(.dtors.*)) - __DTOR_END__ = .; - } > rom :ph_rom - OSESYMS : - { - *(.osesyms) - } > rom :ph_rom - .rodata : - { - *(.rodata) - *(.rodata.*) - *(.gnu.linkonce.r.*) - } > rom :ph_rom - .eh_frame : - { - __EH_FRAME_BEGIN__ = .; - *(.eh_frame) - LONG(0) - __EH_FRAME_END__ = .; - } > rom :ph_rom - .gcc_except_table : - { - *(.gcc_except_table) - } > rom :ph_rom - .sdata2 : - { - *(.sdata2) - *(.sdata2.*) - *(.gnu.linkonce.s2.*) - } > rom :ph_rom - .sbss2 : - { - *(.sbss2) - *(.sbss2.*) - *(.gnu.linkonce.sb2.*) - } > rom :ph_rom - LMCONF : - { - obj/?*?/ose_confd.o(.rodata) - *(LMCONF) - } > rom :ph_conf - .data : - { - LONG(0xDEADBABE) - *(.data) - *(.data.*) - *(.gnu.linkonce.d.*) - SORT(CONSTRUCTORS) - . = ALIGN(0x10); - } > ram :ph_ram = 0 - .sdata2 : - { - _SDA2_BASE_ = .; - *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) - }> ram :ph_ram - .sdata : - { - _SDA_BASE_ = .; - *(.sdata) - *(.sdata.*) - *(.gnu.linkonce.s.*) - } > ram :ph_ram - .sbss : - { - *(.sbss) - *(.sbss.*) - *(.scommon) - *(.gnu.linkonce.sb.*) - } > ram :ph_ram - .bss (NOLOAD) : - { - *(.bss) - *(.bss.*) - *(COMMON) - *(.gnu.linkonce.b.*) - *(.osvars) - } > ram :ph_ram - .ignore (NOLOAD) : - { - *(.rel.dyn) - } > ram :ph_ram - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } -} -__OSESYMS_START = ADDR(OSESYMS); -__OSESYMS_END = ADDR(OSESYMS) + SIZEOF(OSESYMS); diff --git a/erts/emulator/sys/ose/gcc_lm_x86_4.4.3.lcf b/erts/emulator/sys/ose/gcc_lm_x86_4.4.3.lcf deleted file mode 100644 index d64fd91604..0000000000 --- a/erts/emulator/sys/ose/gcc_lm_x86_4.4.3.lcf +++ /dev/null @@ -1,277 +0,0 @@ -/* COPYRIGHT-ENEA-EXAMPLE-R2 * -************************************************************************** -* Copyright (C) 2010 by Enea Software AB. -* All rights reserved. -* -* This Example is furnished under a Software License Agreement and -* may be used only in accordance with the terms of such agreement. -* No title to and ownership of the Example is hereby transferred. -* -* The information in this Example is subject to change -* without notice and should not be construed as a commitment -* by Enea Software AB. -* -* DISCLAIMER -* This Example is delivered "AS IS", consequently -* Enea Software AB makes no representations or warranties, -* expressed or implied, for the Example. -************************************************************************** -* COPYRIGHT-END */ - -OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") -OUTPUT_ARCH("i386") - -ENTRY("crt0_lm") - -MEMORY -{ - rom : ORIGIN = 0x01000000, LENGTH = 0x01000000 - ram : ORIGIN = 0x02000000, LENGTH = 0x01000000 -} - -PHDRS -{ - ph_conf PT_LOAD ; - ph_rom PT_LOAD ; - ph_ram PT_LOAD ; -} - -SECTIONS -{ -/*--------------------------------------------------------------------------- - * Read-only area - *-------------------------------------------------------------------------*/ - - /* Code section. */ - .text : - { - *(.text_first) - *(.text) - *(.text.*) - *(.stub) - *(oscode) - *(.init*) - *(.fini*) - *(.gnu.warning) - *(.gnu.linkonce.t.*) - *(.glue_7t) - *(.glue_7) - } > rom :ph_rom = 0 - - - .ose_sfk_biosentry : - { - *(.ose_sfk_biosentry) - } > rom :ph_rom - - /* C++ constructor section. */ - .ctors : - { - __CTOR_LIST__ = .; - *(.ctors) - *(SORT(.ctors.*)) - __CTOR_END__ = .; - } > rom :ph_rom - - /* C++ destructor section. */ - .dtors : - { - __DTOR_LIST__ = .; - *(.dtors) - *(SORT(.dtors.*)) - __DTOR_END__ = .; - } > rom :ph_rom - - /* OSE symbols section. */ - OSESYMS : - { - *(.osesyms) - } > rom :ph_rom - - .plt : - { - *(.plt) - *(.iplt) - } > rom :ph_rom - - - /* Read-only data section. */ - .rodata : - { - *(.rodata) - *(.rodata.*) - *(.gnu.linkonce.r.*) - } > rom :ph_rom - - /* C++ exception handling section. */ - .eh_frame : - { - __EH_FRAME_BEGIN__ = .; - *(.eh_frame) - LONG(0) - __EH_FRAME_END__ = .; - } > rom :ph_rom - - /* C++ exception handling section. */ - .gcc_except_table : - { - *(.gcc_except_table) - } > rom :ph_rom - - /* PowerPC EABI initialized read-only data section. */ - .sdata2 : - { - *(.sdata2) - *(.sdata2.*) - *(.gnu.linkonce.s2.*) - } > rom :ph_rom - - /* PowerPC EABI uninitialized read-only data section. */ - .sbss2 : - { - *(.sbss2) - *(.sbss2.*) - *(.gnu.linkonce.sb2.*) - } > rom :ph_rom - - /* Dynamic relocations */ - .rel.dyn : - { - *(.rel.init) - *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) - *(.rel.fini) - *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) - *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) - *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) - *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) - *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) - *(.rel.ctors) - *(.rel.dtors) - *(.rel.got) - *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) - *(.rel.ifunc) - } > rom :ph_rom - -/*--------------------------------------------------------------------------- - * Load module configuration area - *-------------------------------------------------------------------------*/ - - /* Load module configuration section. */ - LMCONF : - { - obj/?*?/ose_confd.o(.rodata) - *(LMCONF) - } > rom :ph_conf - - -/*--------------------------------------------------------------------------- - * Read-write area - *-------------------------------------------------------------------------*/ - - /*------------------------------------------------------------------- - * Initialized data (copied by PM ) - *-----------------------------------------------------------------*/ - - /* Data section. */ - .data : - { - LONG(0xDEADBABE) - *(.data) - *(.data.*) - *(.gnu.linkonce.d.*) - SORT(CONSTRUCTORS) - . = ALIGN(0x10); - } > ram :ph_ram = 0 - - .got : - { - *(.got.plt) - *(.got) - } > rom :ph_ram - - /* Global offset table for dynamically linked procedures. */ - .got.plt : - { - *(.got.plt) - *(.igot.plt) - } > rom :ph_ram - - /* Small data section. */ - .sdata2 : - { - _SDA2_BASE_ = .; - *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) - }> ram :ph_ram - .sdata : - { - _SDA_BASE_ = .; - *(.sdata) - *(.sdata.*) - *(.gnu.linkonce.s.*) - } > ram :ph_ram - - /*------------------------------------------------------------------- - * Uninitialized data (cleared by PM ) - *-----------------------------------------------------------------*/ - - /* Small bss section. */ - .sbss : - { - *(.sbss) - *(.sbss.*) - *(.scommon) - *(.gnu.linkonce.sb.*) - } > ram :ph_ram - - /* Bss section. */ - .bss (NOLOAD) : - { - *(.bss) - *(.bss.*) - *(COMMON) - *(.gnu.linkonce.b.*) - *(.osvars) - } > ram :ph_ram - - /* Ignore unwanted sections that are not used in OSE. */ - .ignore (NOLOAD) : - { - *(.rel.dyn) - } > ram :ph_ram - -/*--------------------------------------------------------------------------- - * Debug information - *-------------------------------------------------------------------------*/ - - /* - * DWARF debug sections. - */ - - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } - -} - -/* Symbols used by DDA. */ -__OSESYMS_START = ADDR(OSESYMS); -__OSESYMS_END = ADDR(OSESYMS) + SIZEOF(OSESYMS); diff --git a/erts/emulator/sys/ose/gcc_lm_x86_4.6.3.lcf b/erts/emulator/sys/ose/gcc_lm_x86_4.6.3.lcf deleted file mode 100644 index bb357afcb1..0000000000 --- a/erts/emulator/sys/ose/gcc_lm_x86_4.6.3.lcf +++ /dev/null @@ -1,252 +0,0 @@ -/******************************************************************************* - * gcc_lm_x86.lcf: GCC linker control file for x86 load modules - * @(#) $FilePath: /vobs/ose5/system/refsys/compilers/gcc_lm_x86.lcf $ - * @(#) $FileRevision: /main/tb_current_ose5/9 $ - * $Author: rand $$Date: 10/29/12 17:45:04 $ - * $Copyright: (C) 2006 by Enea AB. All rights reserved. $ - ******************************************************************************/ - -OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") -OUTPUT_ARCH("i386") - -ENTRY("crt0_lm") - -/* Note: - * You may have to increase the length of the "rom" memory region below - * depending on the size of the code and data in your load module. - */ - -MEMORY -{ - conf : ORIGIN = 0x00300000, LENGTH = 0x00001000 - rom : ORIGIN = 0x00000000, LENGTH = 0x00300000 -} - -PHDRS -{ - ph_conf PT_LOAD ; - ph_rom PT_LOAD ; - ph_ram PT_LOAD ; -} - -SECTIONS -{ -/*--------------------------------------------------------------------------- - * Load module configuration area - *-------------------------------------------------------------------------*/ - - /* Load module configuration section. */ - LMCONF : - { - dummy = ALIGN(0x10000); - */?*?/*ose_confd.o(LMCONF*) - *(LMCONF*) - *ose_confd.o(.rodata) - } > conf :ph_conf - -/*--------------------------------------------------------------------------- - * Read-only area - *-------------------------------------------------------------------------*/ - - /* Init section. */ - .init : - { - KEEP(*(.init)) - } > rom :ph_rom = 0x90909090 - - /* Code section. */ - .text : - { - *(.text) - *(.text.*) - *(.stub) - *(oscode) - *(.init*) - *(.fini*) - *(.gnu.warning) - *(.gnu.linkonce.t.*) - } > rom :ph_rom = 0 - - /* Read-only data section. */ - .rodata : - { - *(.rodata) - *(.rodata.*) - *(.gnu.linkonce.r.*) - } > rom :ph_rom - - /* OSE symbols section. */ - OSESYMS : - { - *(.osesyms) - } > rom :ph_rom - - /* Procedure linkage table section. */ - .plt : - { - *(.plt) - *(.iplt) - } > rom :ph_rom - - /* Dynamic relocations */ - .rel.dyn : - { - *(.rel.init) - *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) - *(.rel.fini) - *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) - *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) - *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) - *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) - *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) - *(.rel.ctors) - *(.rel.dtors) - *(.rel.got) - *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) - *(.rel.ifunc) - } > rom :ph_rom - -/*--------------------------------------------------------------------------- - * Read-write area - *-------------------------------------------------------------------------*/ - - /* C++ constructor section. */ - .ctors : - { - __CTOR_LIST__ = .; - *(.ctors) - *(SORT(.ctors.*)) - __CTOR_END__ = .; - } > rom :ph_rom - - /* C++ destructor section. */ - .dtors : - { - __DTOR_LIST__ = .; - *(.dtors) - *(SORT(.dtors.*)) - __DTOR_END__ = .; - } > rom :ph_rom - - /* C++ exception handling section. */ - .eh_frame : - { - __EH_FRAME_BEGIN__ = .; - *(.eh_frame) - LONG(0) - __EH_FRAME_END__ = .; - } > rom :ph_rom - - /*------------------------------------------------------------------- - * Initialized data (copied by PM) - *-----------------------------------------------------------------*/ - - /* Data section. */ - .data : - { - *(.data) - *(.data.*) - *(.gnu.linkonce.d.*) - SORT(CONSTRUCTORS) - } > rom :ph_ram - - /* Global offset table section. */ - .got : - { - *(.got.plt) - *(.got) - } > rom :ph_ram - - /* Global offset table for dynamically linked procedures. */ - .got.plt : - { - *(.got.plt) - *(.igot.plt) - } > rom :ph_ram - - /* SFK BIOS entry section. */ - .ose_sfk_biosentry : - { - *(.ose_sfk_biosentry) - } > rom :ph_ram - - /* C++ exception handling section. */ - .gcc_except_table : - { - *(.gcc_except_table .gcc_except_table.*) - } > rom :ph_ram - - /* Small data section. */ - .sdata ALIGN(0x10) : - { - PROVIDE (_SDA_BASE_ = .); - *(.sdata) - *(.sdata.*) - *(.gnu.linkonce.s.*) - } > rom :ph_ram - - /*------------------------------------------------------------------- - * Uninitialized data (cleared by PM) - *-----------------------------------------------------------------*/ - - /* Bss section. */ - .bss : - { - *(.bss) - *(.bss.*) - *(COMMON) - *(.gnu.linkonce.b.*) - } > rom :ph_ram - - /* Small bss section. */ - .sbss ALIGN(0x10) : - { - *(.sbss) - *(.sbss.*) - *(.scommon) - *(.gnu.linkonce.sb.*) - } > rom :ph_ram - -/*--------------------------------------------------------------------------- - * Debug information - *-------------------------------------------------------------------------*/ - - /* - * Stabs debug sections. - */ - - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } - - /* - * DWARF debug sections. - */ - - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } -} -- cgit v1.2.3 From 3b0eb33f899f361d5006824782e1ef1d16f57e5c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 10 Feb 2014 18:48:44 +0100 Subject: ose: Cleanup POLL_SCHED_1 code Now schedulers 2..N make sure to wake sched 1 if they find that all io has been consumed and sched 1 is sleeping. Before sched 1 was spinning in sys_schedule waiting for sched 2..N to finish consuming io jobs --- erts/emulator/beam/erl_process.c | 60 ++++++++++++++++------------------------ erts/emulator/sys/ose/sys.c | 1 + 2 files changed, 25 insertions(+), 36 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 48e9b63462..a4241c6d43 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -472,7 +472,7 @@ dbg_chk_aux_work_val(erts_aint32_t value) #ifdef ERTS_SMP static void handle_pending_exiters(ErtsProcList *); - +static void wake_scheduler(ErtsRunQueue *rq); #endif #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) @@ -2307,14 +2307,24 @@ try_set_sys_scheduling(void) #endif static ERTS_INLINE int -prepare_for_sys_schedule(void) +prepare_for_sys_schedule(ErtsSchedulerData *esdp) { #ifdef ERTS_SMP while (!erts_port_task_have_outstanding_io_tasks() && try_set_sys_scheduling()) { - if (!erts_port_task_have_outstanding_io_tasks()) - return 1; +#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 + if (esdp->no != 1) { + /* If we are not scheduler 1 and ERTS_SCHED_ONLY_POLL_SCHED_1 is used + then we make sure to wake scheduler 1 */ + ErtsRunQueue *rq = ERTS_RUNQ_IX(0); clear_sys_scheduling(); + wake_scheduler(rq); + return 0; + } +#endif + if (!erts_port_task_have_outstanding_io_tasks()) + return 1; + clear_sys_scheduling(); } return 0; #else @@ -2689,11 +2699,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * be waiting in erl_sys_schedule() */ -#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 - if (esdp->no != 1) { -#else - if (ERTS_SCHEDULER_IS_DIRTY(esdp) || !prepare_for_sys_schedule()) { -#endif + if (ERTS_SCHEDULER_IS_DIRTY(esdp) || !prepare_for_sys_schedule(esdp)) { sched_waiting(esdp->no, rq); @@ -2701,11 +2707,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) spincount = sched_busy_wait.tse; -#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 - ASSERT(esdp->no != 1); -#else tse_wait: -#endif if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && thr_prgr_active != working) sched_wall_time_change(esdp, thr_prgr_active); @@ -2792,6 +2794,10 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) #ifdef ERTS_DIRTY_SCHEDULERS ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); #endif + +#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 + ASSERT(esdp->no == 1); +#endif sched_waiting_sys(esdp->no, rq); @@ -2799,12 +2805,6 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ASSERT(working); sched_wall_time_change(esdp, working = 0); -#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 - sys_poll_try: - while(!prepare_for_sys_schedule()) { - ERTS_SCHED_FAIR_YIELD(); - } -#endif spincount = sched_busy_wait.sys_schedule; if (spincount == 0) @@ -2863,13 +2863,9 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * Got to check that we still got I/O tasks; otherwise * we have to continue checking for I/O... */ - if (!prepare_for_sys_schedule()) { + if (!prepare_for_sys_schedule(esdp)) { spincount *= ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT; -#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 - goto sys_poll_try; -#else goto tse_wait; -#endif } } #endif @@ -2889,7 +2885,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * Got to check that we still got I/O tasks; otherwise * we have to wait in erl_sys_schedule() after all... */ - if (!prepare_for_sys_schedule()) { + if (!prepare_for_sys_schedule(esdp)) { /* * Not allowed to wait in erl_sys_schedule; * do tse wait instead... @@ -2897,11 +2893,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) sched_change_waiting_sys_to_waiting(esdp->no, rq); erts_smp_runq_unlock(rq); spincount = 0; -#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 - goto sys_poll_try; -#else goto tse_wait; -#endif } } #endif @@ -6883,7 +6875,6 @@ erts_start_schedulers(void) res = ethr_thr_create(&esdp->tid, sched_thread_func,(void*)esdp,&opts); if (res != 0) { - //actual--; break; } @@ -8140,12 +8131,9 @@ Process *schedule(Process *p, int calls) goto check_activities_to_run; } - else if ( -#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 - esdp->no == 1 && -#endif - !ERTS_SCHEDULER_IS_DIRTY(esdp) && - (fcalls > input_reductions && prepare_for_sys_schedule())) { + else if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && + (fcalls > input_reductions && + prepare_for_sys_schedule(esdp))) { /* * Schedule system-level activities. */ diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index ad82e4587d..beb7f5944f 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -1732,6 +1732,7 @@ erl_sys_schedule(int runnable) { ASSERT(get_fsem(current_process()) == 0); #ifdef ERTS_SMP + ASSERT(erts_get_scheduler_data()->no == 1); ERTS_CHK_IO(!runnable); #else ERTS_CHK_IO( 1 ); -- cgit v1.2.3 From 6c4ee8337dff6e9b680dbff0796038948d718b5a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 13 Feb 2014 17:16:42 +0100 Subject: ose: Fix support for crypto To enable it you have to modify the OSESSL variable in the ose xcomp file. --- erts/configure.in | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index c964327ebc..7ef29bd959 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3940,7 +3940,7 @@ AC_SUBST(STATIC_KERBEROS_LIBS) AC_SUBST(SSL_LINK_WITH_ZLIB) AC_SUBST(STATIC_ZLIB_LIBS) -std_ssl_locations="/usr/local /usr/sfw /usr /opt/local /usr/pkg /usr/local/openssl /usr/lib/openssl /usr/openssl /usr/local/ssl /usr/lib/ssl /usr/ssl" +std_ssl_locations="/usr/local /usr/sfw /usr /opt/local /usr/pkg /usr/local/openssl /usr/lib/openssl /usr/openssl /usr/local/ssl /usr/lib/ssl /usr/ssl /" AC_ARG_WITH(ssl-zlib, AS_HELP_STRING([--with-ssl-zlib=PATH], @@ -4047,6 +4047,7 @@ for a in ssl crypto ssh; do done SSL_DYNAMIC_ONLY=$enable_dynamic_ssl +SSL_STATIC_ONLY=no case "$erl_xcomp_without_sysroot-$with_ssl" in yes-* | no-no) @@ -4166,6 +4167,10 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in else is_real_ssl=no fi + elif test -f "$dir/lib/powerpc/libsslcrypto.a"; then + SSL_CRYPTO_LIBNAME=sslcrypto + SSL_LIBDIR="$dir/lib/powerpc/" + SSL_RUNTIME_LIBDIR="$rdir/lib/powerpc/" else if test "x$ac_cv_sizeof_void_p" = "x8"; then if test -f "$dir/lib64/libcrypto.a"; then @@ -4189,9 +4194,12 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in SSL_LIBDIR="$dir/lib" fi fi - if test '!' -f $SSL_LIBDIR/libcrypto.a; then + if test '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.a"; then SSL_DYNAMIC_ONLY=yes fi + if test '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.so"; then + SSL_STATIC_ONLY=yes + fi SSL_BINDIR="$rdir/bin" if test "x$is_real_ssl" = "xyes" ; then SSL_INCLUDE="-I$dir/include" @@ -4212,13 +4220,20 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in if test "x$ssl_found" = "xyes"; then if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes"; then ssl_linkable=yes + elif test "x${SSL_CRYPTO_LIBNAME}" = "xsslcrypto"; then + # This should only be triggered seen OSE + ssl_linkable=yes else saveCFLAGS="$CFLAGS" saveLDFLAGS="$LDFLAGS" saveLIBS="$LIBS" CFLAGS="$CFLAGS $SSL_INCLUDE" - LDFLAGS="$LDFLAGS -L$SSL_LIBDIR" - LIBS="-lcrypto" + if test "x$SSL_STATIC_ONLY" = "xyes"; then + LIBS="${SSL_LIBDIR}/lib${SSL_CRYPTO_LIBNAME}.a" + else + LDFLAGS="$LDFLAGS -L$SSL_LIBDIR" + LIBS="$LIBS -l${SSL_CRYPTO_LIBNAME}" + fi AC_TRY_LINK([ #include #include ], @@ -4346,6 +4361,9 @@ dnl so it is - be adoptable # This probably wont work, but that's what the user said, so... SSL_LIBDIR="$with_ssl/lib" fi + elif test -f "$dir/lib/powerpc/libsslcrypto.a"; then + SSL_CRYPTO_LIBNAME=sslcrypto + SSL_LIBDIR="$with_ssl/lib/powerpc/" elif test "x$ac_cv_sizeof_void_p" = "x8"; then if test -f "$with_ssl/lib64/libcrypto.a"; then SSL_LIBDIR="$with_ssl/lib64" @@ -4364,6 +4382,9 @@ dnl so it is - be adoptable if test '!' -f $SSL_LIBDIR/libcrypto.a; then SSL_DYNAMIC_ONLY=yes fi + if test '!' -f $SSL_LIBDIR/libcrypto.so; then + SSL_STATIC_ONLY=yes + fi SSL_INCLUDE="-I$with_ssl/include" SSL_APP=ssl CRYPTO_APP=crypto -- cgit v1.2.3 From c6d34b4d3e6c7817d066dbe20e9c569ae332d2a6 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 14 Feb 2014 14:39:49 +0100 Subject: ose: Do not use spinlocks on OSE This is because it is very easy to deadlock/livelock inbetween processes on OSE. --- erts/include/internal/ethread.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 7307bde8d9..9b1313c538 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -220,6 +220,7 @@ typedef OSPPDKEY ethr_tsd_key; #define ETHR_HAVE_THREAD_NICENESS #define ETHR_PPC_HAVE_NO_LWSYNC +#undef ETHR_HAVE_NATIVE_SPINLOCKS #else /* No supported thread lib found */ @@ -457,7 +458,11 @@ extern ethr_runtime_t ethr_runtime__; # endif #endif -#ifdef VALGRIND /* mutex as fallback for spinlock for VALGRIND */ +#if defined(VALGRIND) || defined(ETHR_OSE_THREADS) +/* mutex as fallback for spinlock for VALGRIND and OSE. + OSE cannot use spinlocks as processes working on the + same execution unit have a tendency to deadlock. + */ # undef ETHR_HAVE_NATIVE_SPINLOCKS # undef ETHR_HAVE_NATIVE_RWSPINLOCKS #else -- cgit v1.2.3 From 0f9fd47d9141846950bd439d8401f8a759068136 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 14 Feb 2014 14:41:49 +0100 Subject: ose: Start using ppdata for tse key --- erts/include/internal/ethread.h | 5 ++--- erts/lib_src/ose/ethread.c | 13 ++++++------- 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 9b1313c538..ebddfc8ba8 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -728,13 +728,12 @@ ETHR_INLINE_FUNC_NAME_(ethr_leave_ts_event)(ethr_ts_event *tsep) #if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHREAD_IMPL__) -extern char* ethr_ts_event_key__; +extern ethr_tsd_key ethr_ts_event_key__; static ETHR_INLINE ethr_ts_event * ETHR_INLINE_FUNC_NAME_(ethr_get_ts_event)(void) { - ethr_ts_event *tsep = (ethr_ts_event *)get_envp(current_process(), - ethr_ts_event_key__); + ethr_ts_event *tsep = *(ethr_ts_event**)ose_get_ppdata(ethr_ts_event_key__); if (!tsep) { int res = ethr_get_tmp_ts_event__(&tsep); if (res != 0) diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c index bde85f9f87..da66c27e77 100644 --- a/erts/lib_src/ose/ethread.c +++ b/erts/lib_src/ose/ethread.c @@ -63,7 +63,7 @@ static ethr_tid main_thr_tid; static const char* own_tid_key = "ethread_own_tid"; -char* ethr_ts_event_key__ = "ethread_tse"; +ethr_tsd_key ethr_ts_event_key__; #define ETHREADWRAPDATASIG 1 @@ -173,8 +173,6 @@ static OS_PROCESS(thr_wrapper) } /* pthread mutex api says we have to do this */ - /*erts_fprintf(stderr, "(%s / %d) curr_pid 0x%x / signal_fsem to 0x%x (fsem val %d)\n", - __FUNCTION__, __LINE__, current_process(), current_process(), get_fsem(current_process()));*/ signal_fsem(current_process()); ETHR_ASSERT(get_fsem(current_process()) == 0); @@ -187,12 +185,12 @@ static OS_PROCESS(thr_wrapper) int ethr_set_tse__(ethr_ts_event *tsep) { - return set_envp(current_process(),ethr_ts_event_key__, (OSADDRESS) tsep); + return ethr_tsd_set(ethr_ts_event_key__,(void *) tsep); } ethr_ts_event *ethr_get_tse__(void) { - return (ethr_ts_event *) get_envp(current_process(),ethr_ts_event_key__); + return (ethr_ts_event *) ethr_tsd_get(ethr_ts_event_key__); } #if defined(ETHR_PPC_RUNTIME_CONF__) @@ -296,14 +294,15 @@ ethr_init(ethr_init_data *id) main_thr_tid.tsd_key_index = 0; set_envp(current_process(),own_tid_key,(OSADDRESS)&main_thr_tid); - /*erts_fprintf(stderr, "(%s / %d) curr_pid 0x%x / signal_fsem to 0x%x (fsem_val %d)\n", - __FUNCTION__, __LINE__, current_process(), current_process(), get_fsem(current_process()));*/ signal_fsem(current_process()); + ETHR_ASSERT(&main_thr_tid == ETHR_GET_OWN_TID__); ethr_not_inited__ = 0; + ethr_tsd_key_create(ðr_ts_event_key__,"ethread_tse"); + return 0; error: ethr_not_inited__ = 1; -- cgit v1.2.3 From 2bf4f22a9488d94ac1fc3c72a4e9d84151744004 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Sun, 23 Feb 2014 18:25:59 +0100 Subject: ose: Yielding the cpu is done "the OSE" way --- erts/include/internal/ethread.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index ebddfc8ba8..58b4ef55b6 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -417,9 +417,9 @@ extern ethr_runtime_t ethr_runtime__; # endif #elif defined(ETHR_OSE_THREADS) # ifndef ETHR_SPIN_BODY -# define ETHR_SPIN_BODY delay(1) +# define ETHR_SPIN_BODY set_pri(get_pri(current_process())) # else -# error "Have to use delay on OSE" +# error "OSE should use set_pri(get_pri(current_process()))" # endif #endif @@ -452,7 +452,7 @@ extern ethr_runtime_t ethr_runtime__; # define ETHR_YIELD() (pthread_yield(), 0) # endif # elif defined(ETHR_OSE_THREADS) -# define ETHR_YIELD() (delay(1), 0) +# define ETHR_YIELD() (set_pri(get_pri(current_process())), 0) # else # define ETHR_YIELD() (ethr_compiler_barrier(), 0) # endif -- cgit v1.2.3 From a35d0f5f82f8152f1b953eda039807a7d4f4e9b9 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Sun, 23 Feb 2014 18:26:38 +0100 Subject: ose: Thread priorities configurable from lmconf The pattern used for getting the priority from the lmconf is based on the name of the process created. The pattern is: ERTS_%%PROCESS_NAME%%_PRIO with the %%PROCESS_NAME%% replaced by the prefix of the process the priority applies to. eg: ERTS_SCHEDULER_PRIO=24 applies to processes with name SCHEDULER_1, SCHEDULER_2 etc. --- erts/emulator/beam/erl_async.c | 14 +- erts/emulator/beam/erl_process.c | 38 +++-- erts/emulator/beam/erl_trace.c | 5 +- erts/emulator/drivers/ose/ose_signal_drv.c | 2 + erts/emulator/sys/ose/default.lmconf | 6 + erts/emulator/sys/ose/sys.c | 10 +- erts/include/internal/ethread.h | 4 +- erts/lib_src/ose/ethread.c | 220 +++++++++++++++++++++++++++-- 8 files changed, 265 insertions(+), 34 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index afb5bc302f..b3dc327704 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -166,6 +166,7 @@ async_ready_q(Uint sched_id) #endif + void erts_init_async(void) { @@ -226,18 +227,23 @@ erts_init_async(void) thr_opts.suggested_stack_size = erts_async_thread_suggested_stack_size; -#ifdef ETHR_HAVE_THREAD_NICENESS - thr_opts.prio += 2; -#endif #ifdef ETHR_HAVE_THREAD_NAMES - thr_opts.name = "async_thread"; + thr_opts.name = malloc(sizeof(char)*(strlen("async_XXXX")+1)); #endif for (i = 0; i < erts_async_max_threads; i++) { ErtsAsyncQ *aq = async_q(i); + +#ifdef ETHR_HAVE_THREAD_NAMES + sprintf(thr_opts.name, "async_%d", i+1); +#endif + erts_thr_create(&aq->thr_id, async_main, (void*) aq, &thr_opts); } +#ifdef ETHR_HAVE_THREAD_NAMES + free(thr_opts.name); +#endif /* Wait for async threads to initialize... */ erts_mtx_lock(&async->init.data.mtx); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a4241c6d43..83856ab983 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -6830,9 +6830,16 @@ erts_start_schedulers(void) opts.detached = 1; +#ifdef ETHR_HAVE_THREAD_NAMES + opts.name = malloc(80); +#endif + #ifdef ERTS_SMP if (erts_runq_supervision_interval) { opts.suggested_stack_size = 16; +#ifdef ETHR_HAVE_THREAD_NAMES + sprintf(opts.name, "runq_supervisor"); +#endif erts_atomic_init_nob(&runq_supervisor_sleeping, 0); if (0 != ethr_event_init(&runq_supervision_event)) erl_exit(1, "Failed to create run-queue supervision event\n"); @@ -6854,17 +6861,13 @@ erts_start_schedulers(void) res = ENOTSUP; } -#ifdef ETHR_HAVE_THREAD_NAMES - opts.name = malloc(sizeof(char)*(strlen("scheduler_XXXX")+1)); -#endif - for (actual = 0; actual < wanted; actual++) { ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(actual); ASSERT(actual == esdp->no - 1); #ifdef ETHR_HAVE_THREAD_NAMES - sprintf(opts.name,"scheduler_%d", actual+1); + sprintf(opts.name, "scheduler_%d", actual + 1); #endif #ifdef __OSE__ @@ -6872,14 +6875,13 @@ erts_start_schedulers(void) opts.coreNo = (actual+1) % ose_num_cpus(); #endif - res = ethr_thr_create(&esdp->tid, sched_thread_func,(void*)esdp,&opts); + res = ethr_thr_create(&esdp->tid, sched_thread_func, (void*)esdp, &opts); if (res != 0) { break; } - } - + erts_no_schedulers = actual; #ifdef ERTS_DIRTY_SCHEDULERS @@ -6888,12 +6890,18 @@ erts_start_schedulers(void) int ix; for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { ErtsSchedulerData *esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); +#ifdef ETHR_HAVE_THREAD_NAMES + sprintf(opts.name,"dirty_cpu_scheduler_%d", ix + 1); +#endif res = ethr_thr_create(&esdp->tid,sched_dirty_cpu_thread_func,(void*)esdp,&opts); if (res != 0) erl_exit(1, "Failed to create dirty cpu scheduler thread %d\n", ix); } for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix); +#ifdef ETHR_HAVE_THREAD_NAMES + sprintf(opts.name,"dirty_io_scheduler_%d", ix + 1); +#endif res = ethr_thr_create(&esdp->tid,sched_dirty_io_thread_func,(void*)esdp,&opts); if (res != 0) erl_exit(1, "Failed to create dirty io scheduler thread %d\n", ix); @@ -6903,16 +6911,14 @@ erts_start_schedulers(void) #endif ERTS_THR_MEMORY_BARRIER; + #ifdef ETHR_HAVE_THREAD_NAMES - free(opts.name); - opts.name = "aux_thread"; + sprintf(opts.name, "aux"); #endif + #ifdef __OSE__ opts.coreNo = 0; -#endif -#ifdef ETHR_HAVE_THREAD_NICENESS - opts.prio++; -#endif +#endif /* __OSE__ */ res = ethr_thr_create(&aux_tid, aux_thread, NULL, &opts); if (res != 0) @@ -6933,6 +6939,10 @@ erts_start_schedulers(void) actual, actual == 1 ? " was" : "s were"); erts_send_error_to_logger_nogl(dsbufp); } + +#ifdef ETHR_HAVE_THREAD_NAMES + free(opts.name); +#endif } #endif /* ERTS_SMP */ diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 43e7435d2a..6978a5f11a 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -3478,12 +3478,11 @@ init_sys_msg_dispatcher(void) sys_message_queue_end = NULL; erts_smp_cnd_init(&smq_cnd); erts_smp_mtx_init(&smq_mtx, "sys_msg_q"); + #ifdef ETHR_HAVE_THREAD_NAMES thr_opts.name = "sys_msg_dispatcher"; #endif -#ifdef ETHR_HAVE_THREAD_NICENESS - thr_opts.prio++; -#endif + erts_smp_thr_create(&sys_msg_dispatcher_tid, sys_msg_dispatcher_func, NULL, diff --git a/erts/emulator/drivers/ose/ose_signal_drv.c b/erts/emulator/drivers/ose/ose_signal_drv.c index 46890a1503..1335bffe18 100644 --- a/erts/emulator/drivers/ose/ose_signal_drv.c +++ b/erts/emulator/drivers/ose/ose_signal_drv.c @@ -29,6 +29,7 @@ #include "erl_driver.h" #include "ose.h" + #ifdef HAVE_OSE_SPI_H #include "ose_spi/ose_spi.h" #endif @@ -363,6 +364,7 @@ static OS_PROCESS(driver_proxy_process) { } } + /** * Init routine for the driver **/ diff --git a/erts/emulator/sys/ose/default.lmconf b/erts/emulator/sys/ose/default.lmconf index 17b7a1e5fa..a66b0ece56 100644 --- a/erts/emulator/sys/ose/default.lmconf +++ b/erts/emulator/sys/ose/default.lmconf @@ -13,6 +13,12 @@ HEAP_SMALL_BUF_INIT_SIZE=64000000 HEAP_LARGE_BUF_THRESHOLD=16000000 HEAP_LOCK_TYPE=2 +ERTS_DEFAULT_PRIO=24 +ERTS_SCHEDULER_PRIO=24 +ERTS_ASYNC_PRIO=22 +ERTS_AUX_PRIO=24 +ERTS_SYS_MSG_DISPATCHER_PRIO=21 + # Setting the environment variable EFS_RESOLVE_TMO on the block to 0. # This will eliminiate delays when trying to open files on not mounted # volumes. diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index beb7f5944f..88dbd7fcf8 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -50,6 +50,14 @@ #include "efs.h" #include "erl_printf.h" +/* Set the define to 1 to get some logging */ +#if 0 +#include "ramlog.h" +#define LOG(output) ramlog_printf output +#else +#define LOG(output) +#endif + extern char **environ; static erts_smp_rwmtx_t environ_rwmtx; @@ -68,7 +76,7 @@ static erts_smp_rwmtx_t environ_rwmtx; #include "erl_cpu_topology.h" /* The priority for reader/writer processes */ -#define FD_PROC_PRI 20 +#define FD_PROC_PRI get_pri(current_process()) typedef struct ErtsSysReportExit_ ErtsSysReportExit; struct ErtsSysReportExit_ { diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 58b4ef55b6..8512ba5884 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -217,7 +217,6 @@ typedef OSPPDKEY ethr_tsd_key; #define ETHR_USE_OWN_RWMTX_IMPL__ #define ETHR_USE_OWN_MTX_IMPL__ #define ETHR_HAVE_THREAD_NAMES -#define ETHR_HAVE_THREAD_NICENESS #define ETHR_PPC_HAVE_NO_LWSYNC #undef ETHR_HAVE_NATIVE_SPINLOCKS @@ -510,7 +509,6 @@ typedef struct { int suggested_stack_size; /* kilo words (default sys dependent) */ #ifdef ETHR_OSE_THREADS char *name; - OSPRIORITY prio; U32 coreNo; #endif } ethr_thr_opts; @@ -518,7 +516,7 @@ typedef struct { #if defined(ETHR_OSE_THREADS) /* Default ethr name is big as we want to be able to sprint stuff in there */ #define ETHR_THR_OPTS_DEFAULT_INITER \ - {0, -1, "ethread", get_pri(current_process()), 0} + {0, -1, "ethread", 0} #else #define ETHR_THR_OPTS_DEFAULT_INITER {0, -1} #endif diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c index da66c27e77..53628382b1 100644 --- a/erts/lib_src/ose/ethread.c +++ b/erts/lib_src/ose/ethread.c @@ -22,6 +22,7 @@ * Author: Lukas Larsson */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -50,10 +51,13 @@ #include "erl_printf.h" #include "efs.h" +#include "ose.h" #include "ose_spi.h" #include "string.h" +#include "ctype.h" +#include "stdlib.h" #ifndef ETHR_HAVE_ETHREAD_DEFINES #error Missing configure defines @@ -61,6 +65,16 @@ #define ETHR_INVALID_TID_ID -1 +#define DEFAULT_PRIO_NAME "ERTS_ETHR_DEFAULT_PRIO" + +/* Set the define to 1 to get some logging */ +#if 0 +#include "ramlog.h" +#define LOG(output) ramlog_printf output +#else +#define LOG(output) +#endif + static ethr_tid main_thr_tid; static const char* own_tid_key = "ethread_own_tid"; ethr_tsd_key ethr_ts_event_key__; @@ -93,6 +107,157 @@ union SIGNAL { * -------------------------------------------------------------------------- */ +/* Will retrive the instrinsic name by removing the 'prefix' and the + * suffix from 'name'. + * The 'prefix' is given as an inparameter. If NULL or an empty string no + * prefix will be removed. + * If 'strip_suffix' is 1 suffixes in the form of '_123' will be removed. + * Will return a pointer to a newly allocated buffer containing the intrinsic + * name in uppercase characters. + * The caller must remember to free this buffer when no lnger needed. + */ +static char * +ethr_intrinsic_name(const char *name, const char *prefix, int strip_suffix) +{ + const char *start = name; + const char *end = name + strlen(name); + char *intrinsic_name = NULL; + int i; + + if (name == NULL) { + LOG(("ERTS - ethr_intrinsic_namNo input name.\n")); + return NULL; + } + + /* take care of the prefix */ + if ((prefix != NULL) && (*prefix != '\0')) { + const char *found = strstr(name, prefix); + + if (found == name) { + /* found the prefix at the beginning */ + start += strlen(prefix); + } + } + + /* take care of the suffix */ + if (strip_suffix) { + const char *suffix_start = strrchr(start, '_'); + + if (suffix_start != NULL) { + const char *ch; + int only_numbers = 1; + + for (ch = suffix_start + 1; *ch != '\0'; ch++) { + if (strchr("0123456789", *ch) == NULL) { + only_numbers = 0; + break; + } + } + + if (only_numbers) { + end = suffix_start; + } + } + } + + intrinsic_name = malloc(end - start + 1); + for (i = 0; (start + i) < end; i++) { + intrinsic_name[i] = toupper(start[i]); + } + intrinsic_name[i] = '\0'; + + return intrinsic_name; +} + +static char * +ethr_get_amended_env(const char *name, const char *prefix, const char *suffix) +{ + unsigned len; + char *env_name = NULL; + char *env_value = NULL; + + if (name == NULL) { + return NULL; + } + + len = strlen(name); + + if (prefix != NULL) { + len += strlen(prefix); + } + + if (suffix != NULL) { + len += strlen(suffix); + } + + env_name = malloc(len + 1); + sprintf(env_name, "%s%s%s", (prefix != NULL) ? prefix : "", + name, + (suffix != NULL) ? suffix : ""); + env_value = get_env(get_bid(current_process()), env_name); + + if (env_value == NULL) { + LOG(("ERTS - ethr_get_amended_env(): %s environment variable not present\n", env_name)); + } else { + LOG(("ERTS - ethr_get_amended_env(): Found %s environment variable: %s.\n", env_name, env_value)); + } + free(env_name); + + return env_value; +} + +/* Reads the environment variable derived from 'name' and interprets it as as an + * OSE priority. If successfull it will update 'out_prio'. + * Returns: 0 if successfull + * -1 orherwise. + */ +static int +ethr_get_prio(const char *name, OSPRIORITY *out_prio) +{ + int rc = -1; + char *intrinsic_name = NULL; + char *prio_env = NULL; + long prio; + char *endptr = NULL; + + LOG(("ERTS - ethr_get_prio(): name: %s.\n", name)); + + intrinsic_name = ethr_intrinsic_name(name, NULL, 1); + LOG(("ERTS - ethr_get_prio(): Intrinsic name: %s.\n", intrinsic_name)); + + prio_env = ethr_get_amended_env(intrinsic_name, "ERTS_", "_PRIO"); + if (prio_env == NULL) { + goto fini; + } + + prio = efs_str_to_long(prio_env, (const char **)&endptr); + if (endptr != NULL) { + LOG(("ERTS - ethr_get_prio(): Environment varible for '%s' includes " + "non-numerical characters: '%s'.\n", intrinsic_name, prio_env)); + goto fini; + } + + if ((prio < 0) || (prio > 32)) { + LOG(("ERTS - ethr_get_prio(): prio for '%s' (%d) is out of bounds (0-32).\n", + intrinsic_name, prio)); + goto fini; + } + + /* Success */ + *out_prio = (OSPRIORITY)prio; + rc = 0; + +fini: + if (intrinsic_name != NULL) { + free(intrinsic_name); + } + if (prio_env != NULL) { + free_buf((union SIGNAL **) &prio_env); + } + + return rc; +} + static PROCESS blockId(void) { static PROCESS bid = (PROCESS)0; @@ -328,10 +493,45 @@ ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, int use_stack_size = (opts && opts->suggested_stack_size >= 0 ? opts->suggested_stack_size : 0x200 /* Use system default */); + OSPRIORITY use_prio; + char *use_name; + char default_thr_name[20]; + static int no_of_thr = 0; + cpuid_t use_core; + union SIGNAL *init_msg; SIGSELECT sigsel[] = {1,ETHREADWRAPDATASIG}; void *prep_func_res; + + if (opts != NULL) { + LOG(("ERTS - ethr_thr_create(): opts supplied: name: %s, coreNo: %u.\n", + opts->name, opts->coreNo)); + use_name = opts->name; + use_core = opts->coreNo; + if (0 != ethr_get_prio(use_name, &use_prio)) { + if (0 != ethr_get_prio("DEFAULT", &use_prio)) { + use_prio = get_pri(current_process()); + LOG(("ERTS - ethr_thr_create(): Using current process' prio: %d.\n", use_prio)); + } else { + LOG(("ERTS - ethr_thr_create(): Using default prio: %d.\n", use_prio)); + } + } else { + LOG(("ERTS - ethr_thr_create(): Using configured prio: %d.\n", use_prio)); + } + } else { + LOG(("ERTS - ethr_thr_create(): opts not supplied. Using defaults.\n")); + no_of_thr++; + sprintf(default_thr_name, "ethread_%d", no_of_thr); + use_name = default_thr_name; + use_core = ose_cpu_id(); + + if (0 != ethr_get_prio("DEFAULT", &use_prio)) { + use_prio = get_pri(current_process()); + LOG(("ERTS - ethr_thr_create(): Using current process' prio: %d.\n", use_prio)); + } + } + #ifdef ETHR_MODIFIED_DEFAULT_STACK_SIZE if (use_stack_size < 0) use_stack_size = ETHR_MODIFIED_DEFAULT_STACK_SIZE; @@ -377,17 +577,18 @@ ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, else init_msg->data.prep_func_res = NULL; - /*erts_fprintf(stderr, "creating process %s / stack %d\n", opts->name, use_stack_size);*/ + LOG(("ERTS - ethr_thr_create(): Process [0x%x] is creating '%s', coreNo = %u, prio:%u\n", + current_process(), use_name, use_core, use_prio)); - ramlog_printf("[0x%x] process '%s', coreNo = %u\n", - current_process(), opts->name, opts->coreNo); - tid->id = create_process(OS_PRI_PROC, opts->name, thr_wrapper, - use_stack_size, /*opts->prio+5*/24, 0, + tid->id = create_process(OS_PRI_PROC, use_name, thr_wrapper, + use_stack_size, use_prio, 0, get_bid(current_process()), NULL, 0, 0); - - if (ose_bind_process(tid->id, opts->coreNo)) { - printf("[0x%x] Binding pid 0x%x (%s) to core no %u.\n", - current_process(), tid->id, opts->name, opts->coreNo); + if (ose_bind_process(tid->id, use_core)) { + LOG(("ERTS - ethr_thr_create(): Bound pid 0x%x (%s) to core no %u.\n", + tid->id, use_name, use_core)); + } else { + LOG(("ERTS - ethr_thr_create(): Failed binding pid 0x%x (%s) to core no %u.\n", + tid->id, use_name, use_core)); } /*FIXME!!! Normally this shouldn't be used in shared mode. Still there is @@ -418,6 +619,7 @@ ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, if (ethr_thr_parent_func__) ethr_thr_parent_func__(prep_func_res); + LOG(("ERTS - ethr_thr_create(): Exiting.\n")); return res; } -- cgit v1.2.3 From ff77d5049cca221e82d1b28e862512069c399eb7 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 14 Feb 2014 15:29:43 +0100 Subject: ose: Fix broken doc links --- erts/doc/src/driver_entry.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml index 469b59dba6..b34ca136f3 100644 --- a/erts/doc/src/driver_entry.xml +++ b/erts/doc/src/driver_entry.xml @@ -246,7 +246,7 @@ typedef struct erl_drv_entry { function understands). (Some trickery in the emulator allows more than the built-in limit of 64 Events to be used.)

On Enea OSE the event is one or more signals that can - be retrieved using erl_drv_ose_get_input_signal.

+ be retrieved using erl_drv_ose_get_signal.

To use this with threads and asynchronous routines, create a pipe on unix, an Event on Windows or a unique signal number on Enea OSE. When the routine -- cgit v1.2.3 From 2c20bdf2e146dbe27554be7f02982b503ecf08d5 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 19 Feb 2014 15:12:29 +0100 Subject: erts: Fix sys_msg_dispatcher assert Schedulers is too restrictive. Managed threads should be able to clean this up. --- erts/emulator/beam/erl_ptab.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c index fa5482b841..eabf016081 100644 --- a/erts/emulator/beam/erl_ptab.c +++ b/erts/emulator/beam/erl_ptab.c @@ -756,7 +756,8 @@ erts_ptab_delete_element(ErtsPTab *ptab, pix = erts_ptab_id2pix(ptab, ptab_el->id); - ASSERT(erts_get_scheduler_id()); /* *Need* to be a scheduler */ + /* *Need* to be an managed thread */ + ERTS_SMP_LC_ASSERT(erts_thr_progress_is_managed_thread()); erts_ptab_rlock(ptab); maybe_save = ptab->list.data.deleted.end != NULL; -- cgit v1.2.3 From 7687e5b3c015b4ac54914137ffed951100ebc9af Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 19 Feb 2014 16:06:23 +0100 Subject: ose: Fix ssl configure test for osx --- erts/configure.in | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 7ef29bd959..142e3a3ad7 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -4196,8 +4196,7 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in fi if test '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.a"; then SSL_DYNAMIC_ONLY=yes - fi - if test '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.so"; then + elif test '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.so" -a '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.dylib"; then SSL_STATIC_ONLY=yes fi SSL_BINDIR="$rdir/bin" @@ -4379,10 +4378,9 @@ dnl so it is - be adoptable else SSL_LIBDIR="$with_ssl/lib" fi - if test '!' -f $SSL_LIBDIR/libcrypto.a; then + if test '!' -f "${SSL_LIBDIR}/lib${SSL_CRYPTO_LIBNAME}.a"; then SSL_DYNAMIC_ONLY=yes - fi - if test '!' -f $SSL_LIBDIR/libcrypto.so; then + elif test '!' -f ${SSL_LIBDIR}/lib${SSL_CRYPTO_LIBNAME}.so -a '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.dylib"; then SSL_STATIC_ONLY=yes fi SSL_INCLUDE="-I$with_ssl/include" -- cgit v1.2.3 From 04e8415fa7174e69660f1349457ad473d2921c56 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 19 Feb 2014 16:45:52 +0100 Subject: ose: Remove uneccesary define --- erts/include/internal/ethread.h | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 8512ba5884..7b06b8b231 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -219,7 +219,6 @@ typedef OSPPDKEY ethr_tsd_key; #define ETHR_HAVE_THREAD_NAMES #define ETHR_PPC_HAVE_NO_LWSYNC -#undef ETHR_HAVE_NATIVE_SPINLOCKS #else /* No supported thread lib found */ -- cgit v1.2.3 From 6d010bd4ea3a5bb182cab4aff4436eb4f058c301 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 20 Feb 2014 18:02:26 +0100 Subject: win32: Compile erl_log.exe erl_log is used together with debug emulator. Use 'erl -debug -console' to get a denug console. --- erts/etc/common/Makefile.in | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index 5be0942952..4e50058b00 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -161,7 +161,8 @@ INSTALL_TOP_BIN = $(BINDIR)/Install.exe INSTALL_PROGS = \ $(INET_GETHOST) \ $(BINDIR)/heart.exe $(BINDIR)/erlsrv.exe \ - $(BINDIR)/erl.exe $(BINDIR)/werl.exe \ + $(BINDIR)/erl.exe $(BINDIR)/erl_log.exe \ + $(BINDIR)/werl.exe \ $(BINDIR)/$(ERLEXEC) \ $(INSTALL_EMBEDDED_PROGS) @@ -271,6 +272,7 @@ endif rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/ct_run.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/vxcall.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erl.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erl_log.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/werl.o rm -f $(TEXTFILES) rm -f *~ core @@ -294,6 +296,9 @@ $(BINDIR)/erl@EXEEXT@: $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_ $(BINDIR)/werl@EXEEXT@: $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) +$(BINDIR)/erl_log@EXEEXT@: $(OBJDIR)/erl_log.o + $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl_log.o + $(BINDIR)/start_erl@EXEEXT@: $(OBJDIR)/start_erl.o $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/start_erl.o @@ -347,6 +352,10 @@ $(OBJDIR)/werl.o: $(WINETC)/erl.c $(WINETC)/init_file.h $(RC_GENERATED) $(V_CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \ -DWIN32_WERL -o $@ -c $(WINETC)/erl.c +$(OBJDIR)/erl_log.o: $(WINETC)/erl_log.c $(RC_GENERATED) + $(V_CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \ + -o $@ -c $(WINETC)/erl_log.c + $(OBJDIR)/erl.o: $(WINETC)/erl.c $(WINETC)/init_file.h $(RC_GENERATED) $(V_CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \ -o $@ -c $(WINETC)/erl.c -- cgit v1.2.3 From ad9d36c55fb6c77ce0a6163e009313deaa2d2bb0 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 21 Feb 2014 09:14:34 +0100 Subject: ose: Cleanup of mutex selection defines --- erts/include/internal/ethr_mutex.h | 28 +++------------------------- erts/include/internal/ethread.h | 5 ++--- erts/lib_src/common/ethr_mutex.c | 4 +++- 3 files changed, 8 insertions(+), 29 deletions(-) (limited to 'erts') diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h index 9511add075..ee861065c5 100644 --- a/erts/include/internal/ethr_mutex.h +++ b/erts/include/internal/ethr_mutex.h @@ -97,15 +97,12 @@ void LeaveCriticalSection(CRITICAL_SECTION *); #if 0 # define ETHR_MTX_Q_LOCK_SPINLOCK__ # define ETHR_MTX_QLOCK_TYPE__ ethr_spinlock_t -#elif defined(ETHR_PTHREADS) +#elif defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS) # define ETHR_MTX_Q_LOCK_PTHREAD_MUTEX__ # define ETHR_MTX_QLOCK_TYPE__ pthread_mutex_t #elif defined(ETHR_WIN32_THREADS) # define ETHR_MTX_Q_LOCK_CRITICAL_SECTION__ # define ETHR_MTX_QLOCK_TYPE__ CRITICAL_SECTION -#elif defined(ETHR_OSE_THREADS) -# define ETHR_MTX_Q_LOCK_PTHREAD_MUTEX__ -# define ETHR_MTX_QLOCK_TYPE__ pthread_mutex_t #else # error Need a qlock implementation #endif @@ -213,7 +210,7 @@ struct ethr_cond_ { #endif }; -#elif defined(ETHR_PTHREADS) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) +#elif (defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS)) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) typedef struct ethr_mutex_ ethr_mutex; struct ethr_mutex_ { @@ -258,25 +255,6 @@ struct ethr_cond_ { #endif }; -#elif defined(ETHR_OSE_THREADS) - -typedef struct ethr_mutex_ ethr_mutex; -struct ethr_mutex_ { - pthread_mutex_t pt_mtx; -#if ETHR_XCHK - int initialized; -#endif -}; - -typedef struct ethr_cond_ ethr_cond; -struct ethr_cond_ { - pthread_cond_t pt_cnd; -#if ETHR_XCHK - int initialized; -#endif -}; - - #else # error "no mutex implementation" #endif @@ -655,7 +633,7 @@ ETHR_INLINE_MTX_FUNC_NAME_(ethr_mutex_unlock)(ethr_mutex *mtx) #endif /* ETHR_TRY_INLINE_FUNCS */ -#elif defined(ETHR_PTHREADS) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) +#elif (defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS)) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) #if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_MUTEX_IMPL__) diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 7b06b8b231..2682718962 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -214,11 +214,10 @@ typedef OSPPDKEY ethr_tsd_key; #undef ETHR_HAVE_ETHR_SIG_FUNCS +/* Out own RW mutexes are probably faster, but use OSEs mutexes */ #define ETHR_USE_OWN_RWMTX_IMPL__ -#define ETHR_USE_OWN_MTX_IMPL__ -#define ETHR_HAVE_THREAD_NAMES -#define ETHR_PPC_HAVE_NO_LWSYNC +#define ETHR_HAVE_THREAD_NAMES #else /* No supported thread lib found */ diff --git a/erts/lib_src/common/ethr_mutex.c b/erts/lib_src/common/ethr_mutex.c index 036914af7b..47f2f884af 100644 --- a/erts/lib_src/common/ethr_mutex.c +++ b/erts/lib_src/common/ethr_mutex.c @@ -1249,7 +1249,7 @@ ethr_cond_wait(ethr_cond *cnd, ethr_mutex *mtx) return 0; } -#elif defined(ETHR_PTHREADS) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) +#elif (defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS)) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) /* -- pthread mutex and condition variables -------------------------------- */ int @@ -1428,6 +1428,8 @@ void LeaveCriticalSection(CRITICAL_SECTION *cs) ETHR_FATAL_ERROR__(res); } +#else +#error "No mutex implementation found" #endif #define ETHR_CND_WAIT__ ((ethr_sint32_t) 0x11dead11) -- cgit v1.2.3 From 4a6850e522b91eb009ddd0ed9d9f542f1baf1bee Mon Sep 17 00:00:00 2001 From: Jonas Karlsson Date: Fri, 21 Feb 2014 14:01:38 +0100 Subject: ose: Updating event and signal API for OSE --- erts/emulator/beam/erl_driver.h | 6 +++--- erts/emulator/drivers/ose/ose_signal_drv.c | 8 ++++---- erts/emulator/sys/common/erl_poll.h | 1 + erts/emulator/sys/ose/erl_poll.c | 12 +++++++----- 4 files changed, 15 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index a15b0e17f5..5517c26ba4 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -682,11 +682,11 @@ EXTERN int erl_drv_getenv(char *key, char *value, size_t *value_size); #ifdef __OSE__ typedef ErlDrvUInt ErlDrvOseEventId; EXTERN union SIGNAL *erl_drv_ose_get_signal(ErlDrvEvent ev); -EXTERN ErlDrvEvent erl_drv_ose_event_alloc(SIGSELECT sig,ErlDrvOseEventId id, - ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig)); +EXTERN ErlDrvEvent erl_drv_ose_event_alloc(SIGSELECT sig, ErlDrvOseEventId handle, + ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig), void *extra); EXTERN void erl_drv_ose_event_free(ErlDrvEvent ev); EXTERN void erl_drv_ose_event_fetch(ErlDrvEvent ev, SIGSELECT *sig, - ErlDrvOseEventId *id); + ErlDrvOseEventId *handle, void **extra); #endif #endif /* !ERL_DRIVER_TYPES_ONLY */ diff --git a/erts/emulator/drivers/ose/ose_signal_drv.c b/erts/emulator/drivers/ose/ose_signal_drv.c index 1335bffe18..4929b53856 100644 --- a/erts/emulator/drivers/ose/ose_signal_drv.c +++ b/erts/emulator/drivers/ose/ose_signal_drv.c @@ -504,12 +504,12 @@ static void outputv(ErlDrvData driver_data, ErlIOVec *ev) ctxt->perm_events[1] = erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH,(int)ctxt->spid, - resolve_signal); + resolve_signal, NULL); driver_select(ctxt->port,ctxt->perm_events[1],ERL_DRV_READ|ERL_DRV_USE,1); ctxt->perm_events[0] = erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_HUNT,(int)ctxt->spid, - resolve_signal); + resolve_signal, NULL); driver_select(ctxt->port,ctxt->perm_events[0],ERL_DRV_READ|ERL_DRV_USE,1); start(ctxt->spid); @@ -679,7 +679,7 @@ static void outputv(ErlDrvData driver_data, ErlIOVec *ev) for (i = 0, j = 0; i < event_cnt || j < ctxt->event_cnt; ) { if (ctxt->events) - erl_drv_ose_event_fetch(ctxt->events[j],&tmp_signo,NULL); + erl_drv_ose_event_fetch(ctxt->events[j],&tmp_signo,NULL,NULL); if (signo == tmp_signo) { events[i++] = ctxt->events[j++]; @@ -687,7 +687,7 @@ static void outputv(ErlDrvData driver_data, ErlIOVec *ev) } else if (signo < tmp_signo || !ctxt->events) { /* New signal to select on */ events[i] = erl_drv_ose_event_alloc(signo,(int)ctxt->spid, - resolve_signal); + resolve_signal, NULL); driver_select(ctxt->port,events[i++],ERL_DRV_READ|ERL_DRV_USE,1); EV_GET_UINT32(ev,&signo,&p,&q); } else { diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index ce8dcd3f90..2f1c05f401 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -119,6 +119,7 @@ struct erts_sys_fd_type { ErtsPollOseMsgList *msgs; ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig); ethr_mutex mtx; + void *extra; }; #endif diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c index 78dc275c06..ca1ed6e53a 100644 --- a/erts/emulator/sys/ose/erl_poll.c +++ b/erts/emulator/sys/ose/erl_poll.c @@ -547,8 +547,8 @@ int erts_poll_wait(ErtsPollSet ps, erts_dsprintf( dsbufp, "erts_poll_wait() failed: found unkown signal id %d (signo %u) " - "(curr_proc 0x%x /sender 0x%x)\n", - fd.id, fd.signo, current_process(), sender(&sig)); + "(curr_proc 0x%x)\n", + fd.id, fd.signo, current_process()); erts_send_error_to_logger_nogl(dsbufp); timeout = 0; ASSERT(0); @@ -737,17 +737,17 @@ union SIGNAL *erl_drv_ose_get_signal(ErlDrvEvent drv_ev) { ev->msgs = msg->next; ethr_mutex_unlock(&ev->mtx); erts_free(ERTS_ALC_T_FD_SIG_LIST,msg); - restore(sig); return sig; } } ErlDrvEvent erl_drv_ose_event_alloc(SIGSELECT signo, ErlDrvOseEventId id, - ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig)) { + ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig), void *extra) { struct erts_sys_fd_type *ev = erts_alloc(ERTS_ALC_T_DRV_EV, sizeof(struct erts_sys_fd_type)); ev->signo = signo; + ev->extra = extra; ev->id = id; ev->msgs = NULL; ev->resolve_signal = resolve_signal; @@ -763,10 +763,12 @@ void erl_drv_ose_event_free(ErlDrvEvent drv_ev) { } void erl_drv_ose_event_fetch(ErlDrvEvent drv_ev, SIGSELECT *signo, - ErlDrvOseEventId *id) { + ErlDrvOseEventId *id, void **extra) { struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; if (signo) *signo = ev->signo; + if (extra) + *extra = ev->extra; if (id) *id = ev->id; } -- cgit v1.2.3 From 06e55b6f2ac30c95717532a259a6148226f63b24 Mon Sep 17 00:00:00 2001 From: Jonas Karlsson Date: Fri, 21 Feb 2014 14:03:00 +0100 Subject: ose: Updating fd_driver and spawn_driver for OSE --- erts/emulator/sys/ose/sys.c | 1339 +++++++++++++++++++++---------------------- 1 file changed, 650 insertions(+), 689 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index 88dbd7fcf8..c892cc69c7 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -49,6 +49,9 @@ #include "unistd.h" #include "efs.h" #include "erl_printf.h" +#include "aio.h" +#include "pm.h" +#include "fcntl.h" /* Set the define to 1 to get some logging */ #if 0 @@ -60,6 +63,7 @@ extern char **environ; static erts_smp_rwmtx_t environ_rwmtx; +static PROCESS sig_proxy_pid = 0; #define MAX_VSIZE 16 /* Max number of entries allowed in an I/O * vector sock_sendv(). @@ -80,31 +84,44 @@ static erts_smp_rwmtx_t environ_rwmtx; typedef struct ErtsSysReportExit_ ErtsSysReportExit; struct ErtsSysReportExit_ { - ErtsSysReportExit *next; - Eterm port; - int pid; - int ifd; - int ofd; - ErlDrvEvent in_sig_descr; - ErlDrvEvent out_sig_descr; + ErtsSysReportExit *next; + Eterm port; + int pid; + int ifd; + int ofd; + ErlDrvEvent attach_event; + ErlDrvEvent input_event; + ErlDrvEvent output_event; }; /* This data is shared by these drivers - initialized by spawn_init() */ static struct driver_data { - ErlDrvPort port_num; - int ofd, packet_bytes; - ErtsSysReportExit *report_exit; - int pid; - int alive; - int status; - ErlDrvEvent in_sig_descr; - ErlDrvEvent out_sig_descr; - PROCESS in_proc; - PROCESS out_proc; - ErlDrvPDL pdl; + ErlDrvPort port_num; + int ofd; + int ifd; + int packet_bytes; + ErtsSysReportExit *report_exit; + int pid; + int alive; + int status; + ErlDrvEvent input_event; + ErlDrvEvent output_event; + struct aiocb aiocb; + FmHandle handle; + char *install_handle; } *driver_data; /* indexed by fd */ +struct async { + SIGSELECT signo; + ErlDrvTermData port; + ErlDrvTermData proc; + PROCESS spid; + PROCESS target; + Uint32 ref; +}; + static ErtsSysReportExit *report_exit_list; +static ERTS_INLINE void report_exit_status(ErtsSysReportExit *rep, int status); extern int driver_interrupt(int, int); extern void do_break(void); @@ -157,6 +174,66 @@ erts_mtx_t chld_stat_mtx; static volatile int children_died; #endif +#define SET_AIO(REQ,FD,SIZE,BUFF) \ + memset(&(REQ),0,sizeof(REQ)); \ + (REQ).aio_fildes = FD; \ + (REQ).aio_offset = FM_POSITION_CURRENT; \ + (REQ).aio_nbytes = SIZE; \ + (REQ).aio_buf = BUFF; \ + (REQ).aio_sigevent.sigev_notify = SIGEV_NONE + +/* the first sizeof(struct aiocb *) bytes of the write buffer + * will contain the pointer to the aiocb struct, this needs + * to be freed between asynchronous writes. + * A write of 0 bytes is ignored. */ +#define WRITE_AIO(FD,SIZE,BUFF) do { \ + if (SIZE > 0) { \ + struct aiocb *write_req = driver_alloc(sizeof(struct aiocb)); \ + char *write_buff = driver_alloc((sizeof(char)*SIZE)+1+ \ + (sizeof(struct aiocb *))); \ + *(struct aiocb **)write_buff = (struct aiocb *)write_req; \ + write_buff += sizeof(struct aiocb *); \ + memcpy(write_buff,BUFF,SIZE+1); \ + SET_AIO(*write_req,FD,SIZE,write_buff); \ + aio_write(write_req); \ + } \ +} while(0) + +/* free the write_buffer and write_req + * created in the WRITE_AIO() request macro */ +#define FREE_AIO(ptr) do { \ + struct aiocb *aiocb_ptr; \ + char *buffer_ptr; \ + aiocb_ptr = *(struct aiocb **)((ptr)-sizeof(struct aiocb *)); \ + buffer_ptr = (((char*)ptr)-sizeof(struct aiocb *)); \ + driver_free(aiocb_ptr); \ + driver_free(buffer_ptr); \ +} while(0) + +/* When we have several schedulers, we need to make sure + * that scheduler issuing aio_dispatch() is the owner on the signal */ +#define DISPATCH_AIO(sig) do { \ + restore(sig); \ + aio_dispatch(sig); \ + } while(0) + + +/* debug print macros */ +#define DEBUG_RES 0 + +#ifdef DEBUG_RES +#define DEBUG_CHECK_RES(actual, expected) \ + do { \ + if (actual != expected ) { \ + ramlog_printf("Result check failed" \ + " got: 0x%08x expected:0x%08x\nat: %s:%d\n", \ + actual, expected, __FILE__, __LINE__); \ + abort(); /* This might perhaps be too harsh? */ \ + } \ + } while(0) +#else +#define DEBUG_CHECK_RES +#endif static struct fd_data { char pbuf[4]; /* hold partial packet bytes */ @@ -191,6 +268,7 @@ static struct termios initial_tty_mode; static int replace_intr = 0; /* assume yes initially, ttsl_init will clear it */ int using_oldshell = 1; +static PROCESS get_signal_proxy_pid(void); static void init_check_io(void) @@ -540,28 +618,13 @@ void fini_getenv_state(GETENV_STATE *state) /************************** Port I/O *******************************/ - - /* I. Common stuff */ -typedef struct SysDriverAsyncSignal_ { - SIGSELECT sig_no; - int type; - byte *buff; - ssize_t res; - int errno_copy; -} SysDriverAsyncSignal; - -typedef struct SysDriverConfSignal_ { - SIGSELECT sig_no; - int fd; - PROCESS parent; -} SysDriverConfSignal; - union SIGNAL { SIGSELECT sig_no; - SysDriverAsyncSignal sys_async; - SysDriverConfSignal conf_async; + struct FmReadPtr fm_read_reply; + struct FmWritePtr fm_write_reply; + struct async async; }; /* II. The spawn/fd drivers */ @@ -582,14 +645,42 @@ static void erl_stop(ErlDrvData); static void ready_input(ErlDrvData, ErlDrvEvent); static void ready_output(ErlDrvData, ErlDrvEvent); static void output(ErlDrvData, char*, ErlDrvSizeT); -static void outputv(ErlDrvData, ErlIOVec*); static void stop_select(ErlDrvEvent, void*); -static ErlDrvOseEventId resolve_signal(union SIGNAL* sig) { - return sig->sig_no == ERTS_SIGNAL_FD_DRV_ASYNC ? sig->sys_async.type : -1; + +static PROCESS +get_signal_proxy_pid(void) { + union SIGNAL *sig; + SIGSELECT any_sig[] = {0}; + + if (!sig_proxy_pid) { + sig = alloc(sizeof(union SIGNAL), ERTS_SIGNAL_OSE_DRV_ATTACH); + hunt("ose_signal_driver_proxy", 0, NULL, &sig); + sig = receive(any_sig); + sig_proxy_pid = sender(&sig); + free_buf(&sig); + } + ASSERT(sig_proxy_pid); + return sig_proxy_pid; } -OS_PROCESS(fd_writer_process); -OS_PROCESS(fd_reader_process); +static ErlDrvOseEventId +resolve_signal(union SIGNAL* sig) { + switch(sig->sig_no) { + + case FM_READ_PTR_REPLY: + return (ErlDrvOseEventId)sig->fm_read_reply.handle; + + case FM_WRITE_PTR_REPLY: + return (ErlDrvOseEventId)sig->fm_write_reply.handle; + + case ERTS_SIGNAL_OSE_DRV_ATTACH: + return (ErlDrvOseEventId)sig->async.target; + + default: + break; + } + return (ErlDrvOseEventId)-1; +} struct erl_drv_entry spawn_driver_entry = { spawn_init, @@ -627,7 +718,7 @@ struct erl_drv_entry fd_driver_entry = { NULL, fd_control, NULL, - outputv, + NULL, NULL, /* ready_async */ NULL, /* flush */ NULL, /* call */ @@ -641,120 +732,167 @@ struct erl_drv_entry fd_driver_entry = { stop_select }; -static int set_driver_data(ErlDrvPort port_num, +static void +set_spawn_fd(int local_fd, int remote_fd, PROCESS remote_pid) { + PROCESS vm_pid; + FmHandle handle; + char env_val[55]; + char env_name[10]; + EfsStatus efs_res; + + /* get pid of pipevm and handle of chosen fd */ + efs_res = efs_examine_fd(local_fd, FLIB_FD_VMPID, &vm_pid, 0); + DEBUG_CHECK_RES(efs_res, EFS_SUCCESS); + + /* setup the file descriptor to buffer per line */ + efs_res = efs_config_fd(local_fd, FLIB_FD_BUFMODE, FM_BUFF_LINE, + FLIB_FD_BUFSIZE, 80, 0); + DEBUG_CHECK_RES(efs_res, EFS_SUCCESS); + + /* duplicate handle and set spawn pid owner */ + efs_res = efs_dup_to(local_fd, remote_pid, &handle); + DEBUG_CHECK_RES(efs_res, EFS_SUCCESS); + + sprintf(env_name, "FD%d", remote_fd); + + /* Syntax of the environment variable: + * "FD#" ",,,," */ + sprintf(env_val, "0x%lx,0x%lx,%lu,%lu,0x%x", + vm_pid, handle, + FM_BUFF_LINE, 80, + O_APPEND); + + set_env(remote_pid, env_name, env_val); +} + +static ErlDrvData +set_driver_data(ErlDrvPort port_num, int ifd, int ofd, int packet_bytes, int read_write, int exit_status, - int pid) + PROCESS pid) { Port *prt; ErtsSysReportExit *report_exit; - union SIGNAL *sig; - - /*erts_fprintf(stderr, " %s / pid %x / ofd %d / ifd %d\n", - __FUNCTION__, current_process(), ofd, ifd);*/ - - - if (!exit_status) - report_exit = NULL; - else { - report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT, - sizeof(ErtsSysReportExit)); - report_exit->next = report_exit_list; - report_exit->port = erts_drvport2id(port_num); - report_exit->pid = pid; - report_exit->ifd = read_write & DO_READ ? ifd : -1; - report_exit->ofd = read_write & DO_WRITE ? ofd : -1; - - if (read_write & DO_READ) - report_exit->in_sig_descr = - erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ifd, - resolve_signal); - if (read_write & DO_WRITE) - report_exit->out_sig_descr = - erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC, ofd, - resolve_signal); - - report_exit_list = report_exit; - } prt = erts_drvport2port(port_num); - if (prt != ERTS_INVALID_ERL_DRV_PORT) - prt->os_pid = pid; + if (prt != ERTS_INVALID_ERL_DRV_PORT) { + prt->os_pid = pid; + } + /* READ */ if (read_write & DO_READ) { - driver_data[ifd].packet_bytes = packet_bytes; - driver_data[ifd].port_num = port_num; - driver_data[ifd].report_exit = report_exit; - driver_data[ifd].pid = pid; - driver_data[ifd].alive = 1; - driver_data[ifd].status = 0; - driver_data[ifd].in_sig_descr = - erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ifd, - resolve_signal); - - driver_data[ifd].in_proc = create_process(OS_PRI_PROC,"beam_fd_reader", - fd_reader_process, 0x800, - FD_PROC_PRI, 0, 0, - NULL, 0, 0); - efs_clone(driver_data[ifd].in_proc); - sig = alloc(sizeof(SysDriverConfSignal), ERTS_SIGNAL_FD_DRV_CONFIG); - sig->conf_async.fd = ifd; - sig->conf_async.parent = current_process(); - send(&sig, driver_data[ifd].in_proc); - start(driver_data[ifd].in_proc); - - if (read_write & DO_WRITE) { - driver_data[ifd].ofd = ofd; - driver_data[ifd].out_sig_descr = - erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ofd, - resolve_signal); - driver_data[ifd].pdl = driver_pdl_create(port_num); - driver_data[ifd].out_proc = - create_process(OS_PRI_PROC,"beam_fd_writer", - fd_writer_process, 0x800, - FD_PROC_PRI, 0, 0, NULL, 0, 0); - sig = alloc(sizeof(SysDriverConfSignal), ERTS_SIGNAL_FD_DRV_CONFIG); - sig->conf_async.fd = ofd; - sig->conf_async.parent = current_process(); - send(&sig, driver_data[ifd].out_proc); - // efs_clone(driver_data[ifd].out_proc); - start(driver_data[ifd].out_proc); - if (ifd != ofd) - driver_data[ofd] = driver_data[ifd]; /* structure copy */ - } else { /* DO_READ only */ - driver_data[ifd].ofd = -1; - } - (void) driver_select(port_num, driver_data[ifd].in_sig_descr, + efs_examine_fd(ifd, FLIB_FD_HANDLE, &driver_data[ifd].handle, 0); + driver_data[ifd].ifd = ifd; + driver_data[ifd].packet_bytes = packet_bytes; + driver_data[ifd].port_num = port_num; + driver_data[ifd].pid = pid; + + /* async read struct */ + memset(&driver_data[ifd].aiocb, 0, sizeof(struct aiocb)); + driver_data[ifd].aiocb.aio_buf = driver_alloc(255); + driver_data[ifd].aiocb.aio_fildes = ifd; + driver_data[ifd].aiocb.aio_nbytes = 255; + + driver_data[ifd].alive = 1; + driver_data[ifd].status = 0; + driver_data[ifd].input_event = + erl_drv_ose_event_alloc(FM_READ_PTR_REPLY, + driver_data[ifd].handle, resolve_signal, + &driver_data[ifd].ifd); + + /* READ & WRITE */ + if (read_write & DO_WRITE) { + driver_data[ifd].ofd = ofd; + efs_examine_fd(ofd, FLIB_FD_HANDLE, &driver_data[ofd].handle, 0); + + driver_data[ifd].output_event = + erl_drv_ose_event_alloc(FM_WRITE_PTR_REPLY, + driver_data[ofd].handle, resolve_signal, + &driver_data[ofd].ofd); + driver_data[ofd].pid = pid; + if (ifd != ofd) { + driver_data[ofd] = driver_data[ifd]; + driver_data[ofd].aiocb.aio_buf = NULL; + } + } + else { /* READ ONLY */ + driver_data[ifd].ofd = -1; + } + + /* enable input event */ + (void) driver_select(port_num, driver_data[ifd].input_event, (ERL_DRV_READ | ERL_DRV_USE), 1); - return(ifd); - } else { /* DO_WRITE only */ - driver_data[ofd].packet_bytes = packet_bytes; - driver_data[ofd].port_num = port_num; - driver_data[ofd].report_exit = report_exit; - driver_data[ofd].ofd = ofd; - driver_data[ofd].pid = pid; - driver_data[ofd].alive = 1; - driver_data[ofd].status = 0; - driver_data[ofd].in_sig_descr = - erl_drv_ose_event_alloc(ERTS_SIGNAL_FD_DRV_ASYNC,ofd, - resolve_signal); - driver_data[ofd].out_sig_descr = driver_data[ofd].in_sig_descr; - driver_data[ofd].out_proc = - create_process(OS_PRI_PROC, "beam_fd_writer", - fd_writer_process, 0x800, - FD_PROC_PRI, 0, 0, NULL, 0, 0); - sig = alloc(sizeof(SysDriverConfSignal), ERTS_SIGNAL_FD_DRV_CONFIG); - sig->conf_async.fd = ofd; - sig->conf_async.parent = current_process(); - send(&sig, driver_data[ofd].out_proc); - start(driver_data[ofd].out_proc); - //efs_clone(driver_data[ifd].out_proc); - driver_data[ofd].pdl = driver_pdl_create(port_num); - return(ofd); + + aio_read(&driver_data[ifd].aiocb); + } + else { /* WRITE ONLY */ + efs_examine_fd(ofd, FLIB_FD_HANDLE, &driver_data[ofd].handle, 0); + driver_data[ofd].packet_bytes = packet_bytes; + driver_data[ofd].port_num = port_num; + driver_data[ofd].ofd = ofd; + driver_data[ofd].pid = pid; + driver_data[ofd].alive = 1; + driver_data[ofd].status = 0; + driver_data[ofd].output_event = + erl_drv_ose_event_alloc(FM_WRITE_PTR_REPLY, driver_data[ofd].handle, + resolve_signal, &driver_data[ofd].ofd); + driver_data[ofd].input_event = driver_data[ofd].output_event; } + + /* this is used for spawned load modules, and is needed + * to properly uninstall them */ + if (exit_status) { + struct PmProgramInfo *info; + int install_handle_size; + union SIGNAL *sig; + PmStatus pm_status; + report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT, + sizeof(ErtsSysReportExit)); + report_exit->next = report_exit_list; + report_exit->port = erts_drvport2id(port_num); + report_exit->pid = pid; + report_exit->ifd = (read_write & DO_READ) ? ifd : -1; + report_exit->ofd = (read_write & DO_WRITE) ? ofd : -1; + report_exit_list = report_exit; + report_exit->attach_event = + erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH, pid, + resolve_signal, &driver_data[ifd].ifd); + + /* setup ifd and ofd report exit */ + driver_data[ifd].report_exit = report_exit; + driver_data[ofd].report_exit = report_exit; + + pm_status = ose_pm_program_info(pid, &info); + DEBUG_CHECK_RES(pm_status, PM_SUCCESS); + + install_handle_size = strlen(info->install_handle)+1; + driver_data[ifd].install_handle = driver_alloc(install_handle_size); + strcpy(driver_data[ifd].install_handle, + info->install_handle); + + free_buf((union SIGNAL **)&info); + + sig = alloc(sizeof(struct async), ERTS_SIGNAL_OSE_DRV_ATTACH); + sig->async.target = pid; + send(&sig, get_signal_proxy_pid()); + + /* this event will trigger when we receive an attach signal + * from the recently dead load module */ + (void)driver_select(port_num,report_exit->attach_event, DO_READ, 1); + } + else { + report_exit = NULL; + } + + /* the return value is the pointer to the driver_data struct we created + * in this function, it will be used in the drivers input + * and output functions */ + return (ErlDrvData)((!(read_write & DO_READ) && read_write & DO_WRITE) + ? &driver_data[ofd] + : &driver_data[ifd]); } static int spawn_init() @@ -772,9 +910,9 @@ static int spawn_init() return 1; } -static void init_fd_data(int fd, ErlDrvPort port_num) +static void +init_fd_data(int fd, ErlDrvPort port_num) { - fd_data[fd].buf = NULL; fd_data[fd].cpos = NULL; fd_data[fd].remain = 0; @@ -782,173 +920,109 @@ static void init_fd_data(int fd, ErlDrvPort port_num) fd_data[fd].psz = 0; } -static ErlDrvData spawn_start(ErlDrvPort port_num, - char* name, - SysDriverOpts* opts) +/* FIXME write a decent text on pipes on ose */ +static ErlDrvData +spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) { - - long res = 0; - - /* Have to implement for OSE */ - return (ErlDrvData)res; -} - -OS_PROCESS(fd_reader_process) { - union SIGNAL *sig; - PROCESS parent; - int fd; - byte *read_buf; - - SIGSELECT sigsel[] = {1,ERTS_SIGNAL_FD_DRV_CONFIG}; - -#ifdef ERTS_ENABLE_LOCK_COUNT - erts_lcnt_init(); -#endif - - sig = receive(sigsel); - - fd = sig->conf_async.fd; - - parent = sig->conf_async.parent; - free_buf(&sig); - -#ifdef ERTS_ENABLE_LOCK_CHECK - { - char buf[31]; - erts_snprintf(&buf[0], 31, "fd_reader %beu", fd); - erts_lc_set_thread_name(&buf[0]); + int ifd[2]; + int ofd[2]; + static uint32_t ticker = 0; + PmStatus pm_status; + OSDOMAIN domain = PM_NEW_DOMAIN; + PROCESS progpid, mainbid, mainpid; + char *handle = NULL; + struct PmProgramInfo *info; + char *args = NULL; + char *tmp_handle; + ErlDrvData res = (ErlDrvData)-1; + int handle_size; + char *ptr; + + /* handle arguments */ + ptr = strchr(name, ' '); + if (ptr != NULL) { + *ptr ='\0'; + ptr++; + args = ptr; } -#endif - - if (fd == 0) { - FILE *ffd = stdin; - (void)stdin; + else { + args = NULL; } - sigsel[1] = ERTS_SIGNAL_FD_DRV_ASYNC; - - read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, - ERTS_SYS_READ_BUF_SZ); - while (1) { - int errno_copy = errno; - ssize_t res; - res = read(fd, read_buf, ERTS_SYS_READ_BUF_SZ); - sig = alloc(sizeof(SysDriverAsyncSignal), ERTS_SIGNAL_FD_DRV_ASYNC); - sig->sys_async.buff = read_buf; - sig->sys_async.res = res; - if (res <= 0 && errno == EBADF) { - fprintf(stderr,"Could not read from input fd (fd %d/ errno %d/ res %d)\n", - fd, errno, res); - break; - } - if (errno != errno_copy) - sig->sys_async.errno_copy = errno; - else - sig->sys_async.errno_copy = -1; - sig->sys_async.type = fd; - send(&sig,parent); - /* Wait for acc from async_read */ - sig = receive(sigsel); - free_buf(&sig); + /* create an install handle */ + ptr = strrchr(name, '/'); + if (ptr != NULL) { + ptr++; + tmp_handle = ptr; + } + else { + tmp_handle = name; + } + handle_size = strlen(tmp_handle)+1; + handle_size += (ticker<10)?3:((ticker<100)?4:5); + + handle = driver_alloc(handle_size); + snprintf(handle, handle_size, "%s_%d", tmp_handle, ticker); + + do { + snprintf(handle, handle_size, "%s_%d", tmp_handle, ticker++); + pm_status = ose_pm_install_load_module(0, "ELF", name, handle, + 0, 0, NULL); + + } while (pm_status == PM_EINSTALL_HANDLE_ALREADY_INSTALLED); + DEBUG_CHECK_RES(pm_status, PM_SUCCESS); + + /* Create Program */ + pm_status = ose_pm_create_program(&domain, handle, 0, 0, + NULL, &progpid, &mainbid); + DEBUG_CHECK_RES(pm_status, PM_SUCCESS); + + /* Get the mainpid from the newly created program */ + pm_status = ose_pm_program_info(progpid, &info); + DEBUG_CHECK_RES(pm_status, PM_SUCCESS); + + mainpid = info->main_process; + free_buf ((union SIGNAL **)&info); + + /* pipevm needs to be started + * pipe will return 0 if success, -1 if not, + * errno will be set */ + if (pipe(ifd) != 0 || pipe(ofd) != 0) { + DEBUG_CHECK_RES(0, -1); + ASSERT(0); } - erts_free(ERTS_ALC_T_SYS_READ_BUF, read_buf); -} - -OS_PROCESS(fd_writer_process) { - union SIGNAL *sig; - PROCESS parent; - int fd; - SIGSELECT sigsel[] = { 1, ERTS_SIGNAL_FD_DRV_CONFIG, - ERTS_SIGNAL_FD_DRV_ASYNC }; - /* Only wait for config event with the fd which we are printing to */ - sig = receive(sigsel); + /* setup driver data */ + res = set_driver_data(port_num, ofd[0], ifd[1], opts->packet_bytes, + opts->read_write, 1 /* opts->exit_status */, progpid); - fd = sig->conf_async.fd; - parent = sig->conf_async.parent; - free_buf(&sig); + /* init the fd_data array for read/write */ + init_fd_data(ofd[0], port_num); + init_fd_data(ifd[1], port_num); -#ifdef ERTS_ENABLE_LOCK_COUNT - { - char buf[31]; - erts_snprintf(&buf[0], 31, "fd_writer %beu", fd); - erts_lc_set_thread_name(&buf[0]); + /* setup additional configurations + * for the spawned applications environment */ + if (args != NULL) { + set_env(progpid, "ARGV", args); } -#endif + set_env(mainbid, "EFS_RESOLVE_TMO", 0); + set_spawn_fd(ifd[0], 0, mainpid); + set_spawn_fd(ofd[1], 1, mainpid); + set_spawn_fd(ofd[1], 2, mainpid); - sigsel[0] = 2; - /* Why do I need these?!? */ - if (fd == 1) { - FILE* ffd = stdout; - (void)stdout; - } else if (fd == 2) { - FILE* ffd = stderr; - (void)stderr; - } + /* start the spawned program */ + pm_status = ose_pm_start_program(mainbid); + DEBUG_CHECK_RES(pm_status, PM_SUCCESS); - while (1) { - int errno_copy = errno; - int res; - SysIOVec *iov0; - SysIOVec *iov; - int iovlen; - int iovcnt; - int n = 0, i = 0, offset = 0; - size_t p; - /* fprintf(stderr,"0x%x: fd_writer, receive\n", current_process()); */ - sig = receive(sigsel); - /* size = sig->sys_async.res;*/ - if (sig->sig_no == ERTS_SIGNAL_FD_DRV_CONFIG) - return; - driver_pdl_lock(driver_data[fd].pdl); - - iov0 = driver_peekq(driver_data[fd].port_num, &iovlen); - - /* Calculate iovcnt */ - for (p = 0, iovcnt = 0; iovcnt < iovlen; - p += iov0[iovcnt++].iov_len) - ; - iov = driver_alloc(sizeof(SysIOVec) * iovcnt); - memcpy(iov, iov0, iovcnt * sizeof(SysIOVec)); - /* Let go of lock until we deque from original vector */ - driver_pdl_unlock(driver_data[fd].pdl); - - if (iovlen > 0) { - while(i < iovcnt) { - /* We only write 256 chars at the time in order to - not overflow the stdout process */ - if ((iov[i].iov_len-offset) > 256) { - res = write(fd, iov[i].iov_base+offset, 256); - if (res < 0) - break; - offset += res; - } else { - res = write(fd, iov[i].iov_base+offset, iov[i].iov_len-offset); - if (res < 0) - break; - offset = 0; - i++; - } - n += res; - } - if (res > 0) - res = n; - } else if (iovlen == 0) { - res = 0; - } else { /* Port has terminated */ - res = -1; - } - driver_free(iov); - - sig->sys_async.buff = NULL; - sig->sys_async.res = res; - if (errno != errno_copy) - sig->sys_async.errno_copy = errno; - else - sig->sys_async.errno_copy = -1; - sig->sys_async.type = fd; - send(&sig, parent); + /* close unused fd's */ + close(ifd[0]); + close(ofd[1]); + + if (handle) { + driver_free(handle); } + + return (ErlDrvData)res; } #define FD_DEF_HEIGHT 24 @@ -974,13 +1048,13 @@ static ErlDrvSSizeT fd_control(ErlDrvData drv_data, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) { - int fd = (int)(long)drv_data; + struct driver_data *data = (struct driver_data *)drv_data; char resbuff[2*sizeof(Uint32)]; switch (command) { case FD_CTRL_OP_GET_WINSIZE: { Uint32 w,h; - if (fd_get_window_size(fd,&w,&h)) + if (fd_get_window_size(data->ifd,&w,&h)) return 0; memcpy(resbuff,&w,sizeof(Uint32)); memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32)); @@ -1008,7 +1082,7 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name, if (opts->read_write & DO_WRITE) { init_fd_data(opts->ofd, port_num); } - res = (ErlDrvData)(long)set_driver_data(port_num, opts->ifd, opts->ofd, + res = set_driver_data(port_num, opts->ifd, opts->ofd, opts->packet_bytes, opts->read_write, 0, -1); CHLD_STAT_UNLOCK; @@ -1031,404 +1105,317 @@ static void clear_fd_data(int fd) static void nbio_stop_fd(ErlDrvPort prt, ErlDrvEvent ev) { - int fd; + int *fd; driver_select(prt,ev,DO_READ|DO_WRITE,0); - erl_drv_ose_event_fetch(ev, NULL, &fd); - clear_fd_data(fd); - SET_BLOCKING(fd); + erl_drv_ose_event_fetch(ev, NULL, NULL, (void **)&fd); + clear_fd_data(*fd); + SET_BLOCKING(*fd); } -static void fd_stop(ErlDrvData fd) /* Does not close the fds */ +static void fd_stop(ErlDrvData drv_data) /* Does not close the fds */ { - int ofd; + struct driver_data *data = (struct driver_data *)drv_data; - nbio_stop_fd(driver_data[(int)(long)fd].port_num, - driver_data[(int)(long)fd].in_sig_descr); - ofd = driver_data[(int)(long)fd].ofd; - if (ofd != (int)(long)fd && ofd != -1) - nbio_stop_fd(driver_data[(int)(long)fd].port_num, - driver_data[(int)(long)fd].out_sig_descr); + if (data->ofd != -1) { + if (data->ifd != data->ofd) { /* read and write */ + nbio_stop_fd(data->port_num, data->input_event); + nbio_stop_fd(data->port_num, data->output_event); + } + else { /* write only */ + nbio_stop_fd(data->port_num, data->output_event); + } + } + else { /* read only */ + nbio_stop_fd(data->port_num, data->input_event); + } } -/* 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 erl_stop(ErlDrvData fd) -{ - ErlDrvPort prt; - int ofd; - - prt = driver_data[(int)(long)fd].port_num; - nbio_stop_fd(prt, driver_data[(int)(long)fd].in_sig_descr); - - ofd = driver_data[(int)(long)fd].ofd; - if (ofd != (int)(long)fd && (int)(long)ofd != -1) - nbio_stop_fd(prt, driver_data[(int)(long)fd].out_sig_descr); - else - ofd = -1; - - CHLD_STAT_LOCK; - - /* Mark as unused. */ - driver_data[(int)(long)fd].pid = -1; - - CHLD_STAT_UNLOCK; - /* SMP note: Close has to be last thing done (open file descriptors work - as locks on driver_data[] entries) */ - driver_select(prt, driver_data[(int)(long)fd].in_sig_descr, - ERL_DRV_USE, 0); /* close(fd); */ - if (ofd >= 0) { - driver_select(prt, driver_data[(int)(long)fd].out_sig_descr, - ERL_DRV_USE, 0); /* close(ofd); */ - } -} - -static void outputv(ErlDrvData e, ErlIOVec* ev) +static void erl_stop(ErlDrvData drv_data) { - int fd = (int)(long)e; - ErlDrvPort ix = driver_data[fd].port_num; - int pb = driver_data[fd].packet_bytes; - ErlDrvSizeT sz; - char lb[4]; - char* lbp; - ErlDrvSizeT len = ev->size; - - /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ - /* if (pb >= 0 && (len & (((ErlDrvSizeT)1 << (pb*8))) - 1) != len) {*/ - if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) { - driver_failure_posix(ix, EINVAL); - return; /* -1; */ - } - /* Handles 0 <= pb <= 4 only */ - put_int32((Uint32) len, lb); - lbp = lb + (4-pb); - - ev->iov[0].iov_base = lbp; - ev->iov[0].iov_len = pb; - ev->size += pb; - driver_pdl_lock(driver_data[fd].pdl); - if ((sz = driver_sizeq(ix)) > 0) { - /* fprintf(stderr,"0x%x: outputv, enq\n", current_process()); */ - driver_enqv(ix, ev, 0); - if (sz + ev->size >= (1 << 13)) - set_busy_port(ix, 1); - driver_pdl_unlock(driver_data[fd].pdl); - } - else { - union SIGNAL *sig; - /* fprintf(stderr,"0x%x: outputv, enq+sel\n", current_process()); */ - driver_enqv(ix, ev, 0); /* n is the skip value */ - driver_pdl_unlock(driver_data[fd].pdl); - driver_select(ix, driver_data[fd].out_sig_descr, - ERL_DRV_WRITE|ERL_DRV_USE, 1); - sig = alloc(sizeof(SysDriverAsyncSignal),ERTS_SIGNAL_FD_DRV_ASYNC); - sig->sys_async.type = fd; - sig->sys_async.res = pb+len; - send(&sig,driver_data[fd].out_proc); - } - /* return 0;*/ -} - - -static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) + struct driver_data *data = (struct driver_data *)drv_data; + + CHLD_STAT_LOCK; + data->pid = -1; + CHLD_STAT_UNLOCK; + + if (data->ofd != -1) { + if (data->ifd != data->ofd) { /* read and write */ + nbio_stop_fd(data->port_num, data->input_event); + nbio_stop_fd(data->port_num, data->output_event); + driver_select(data->port_num, data->input_event, ERL_DRV_USE, 0); + driver_select(data->port_num, data->output_event, ERL_DRV_USE, 0); + } + else { /* write only */ + nbio_stop_fd(data->port_num, data->output_event); + driver_select(data->port_num, data->output_event, ERL_DRV_USE, 0); + } + } + else { /* read only */ + nbio_stop_fd(data->port_num, data->input_event); + driver_select(data->port_num, data->input_event, ERL_DRV_USE, 0); + } + close(data->ifd); + close(data->ofd); +} + +/* The parameter e is a pointer to the driver_data structure + * related to the fd to be used as output */ +static void output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len) { - int fd = (int)(long)e; - ErlDrvPort ix = driver_data[fd].port_num; - int pb = driver_data[fd].packet_bytes; - int ofd = driver_data[fd].ofd; ErlDrvSizeT sz; char lb[4]; char* lbp; -#if 0 - struct iovec iv[2]; -#endif + struct driver_data *data = (struct driver_data *)drv_data; - /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ - if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) { - driver_failure_posix(ix, EINVAL); + if (((data->packet_bytes == 2) && + (len > 0xffff)) || (data->packet_bytes == 1 && len > 0xff)) { + driver_failure_posix(data->port_num, EINVAL); return; /* -1; */ } put_int32(len, lb); - lbp = lb + (4-pb); - - driver_pdl_lock(driver_data[fd].pdl); - if ((sz = driver_sizeq(ix)) > 0) { - /* fprintf(stderr,"0x%x: output, enq\n", current_process()); */ - driver_enq(ix, lbp, pb); - driver_enq(ix, buf, len); - driver_pdl_unlock(driver_data[fd].pdl); - if (sz + len + pb >= (1 << 13)) - set_busy_port(ix, 1); + lbp = lb + (4-(data->packet_bytes)); + + if ((sz = driver_sizeq(data->port_num)) > 0) { + driver_enq(data->port_num, lbp, data->packet_bytes); + driver_enq(data->port_num, buf, len); + if (sz + len + data->packet_bytes >= (1 << 13)) + set_busy_port(data->port_num, 1); } else { - union SIGNAL *sig; - /* fprintf(stderr,"0x%x: output, enq+select\n", current_process()); */ -#if 0 - iv[0].iov_base = lbp; - iv[0].iov_len = pb; /* should work for pb=0 */ - iv[1].iov_base = buf; - iv[1].iov_len = len; -#endif - driver_enq(ix, lbp, pb); - driver_enq(ix, buf, len); - driver_pdl_unlock(driver_data[fd].pdl); - driver_select(ix, driver_data[ofd].out_sig_descr, + driver_enq(data->port_num, buf, len); /* n is the skip value */ + + driver_select(data->port_num, data->output_event, ERL_DRV_WRITE|ERL_DRV_USE, 1); - sig = alloc(sizeof(SysDriverAsyncSignal),ERTS_SIGNAL_FD_DRV_ASYNC); - sig->sys_async.type = fd; - sig->sys_async.res = pb+len; - send(&sig,driver_data[fd].out_proc); + + WRITE_AIO(data->ofd, len, buf); } return; /* 0; */ } +/* This function is being run when we in recieve + * either a read of 0 bytes, or the attach signal from a dying + * spawned load module */ static int port_inp_failure(ErlDrvPort port_num, ErlDrvEvent ready_fd, int res) /* Result: 0 (eof) or -1 (error) */ { - int err = errno; - int fd; - + int *fd; + SIGSELECT sig_no; ASSERT(res <= 0); - (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0); - erl_drv_ose_event_fetch(ready_fd,NULL,&fd); - clear_fd_data(fd); - if (res == 0) { - if (driver_data[fd].report_exit) { - CHLD_STAT_LOCK; - - if (driver_data[fd].alive) { - /* - * We have eof and want to report exit status, but the process - * hasn't exited yet. When it does report_exit_status() will - * driver_select() this fd which will make sure that we get - * back here with driver_data[ready_fd].alive == 0 and - * driver_data[ready_fd].status set. - */ - CHLD_STAT_UNLOCK; - return 0; - } - else { - int status = driver_data[fd].status; - CHLD_STAT_UNLOCK; - -#if 0 /*ose we should find something for these statuses*/ - /* We need not be prepared for stopped/continued processes. */ - if (WIFSIGNALED(status)) - status = 128 + WTERMSIG(status); - else - status = WEXITSTATUS(status); -#endif - driver_report_exit(driver_data[fd].port_num, status); - } - } - driver_failure_eof(port_num); - } else { - driver_failure_posix(port_num, err); + + erl_drv_ose_event_fetch(ready_fd,&sig_no, NULL, (void **)&fd); + + /* As we need to handle two signals, we do this in two steps */ + if (driver_data[*fd].alive) { + report_exit_status(driver_data[*fd].report_exit, 0); /* status? */ + } + else { + clear_fd_data(*fd); + driver_report_exit(driver_data[*fd].port_num, driver_data[*fd].status); + /* As we do not really know if the spawn has crashed or exited nicely + * we do not check the result status of the following call.. FIXME + * can we handle this in a better way? */ + ose_pm_uninstall_load_module(driver_data[*fd].install_handle); + driver_free(driver_data[*fd].install_handle); + driver_free((void *)driver_data[*fd].aiocb.aio_buf); + + close(*fd); } - return 0; -} -static int async_read(ErlDrvEvent fd, byte *buff, int size) { - union SIGNAL *sigptr = erl_drv_ose_get_signal(fd); - int res = sigptr->sys_async.res; - if (res > 0) - memcpy(buff,sigptr->sys_async.buff,sigptr->sys_async.res); - errno = sigptr->sys_async.errno_copy; - send(&sigptr,sender(&sigptr)); - ASSERT(erl_drv_ose_get_signal(fd) == NULL); - return 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 e, ErlDrvEvent ready_fd) +/* The parameter e is a pointer to the driver_data structure + * related to the fd to be used as output. + * ready_fd is the event that triggered this call to ready_input */ +static void ready_input(ErlDrvData drv_data, ErlDrvEvent ready_fd) { - int fd = (int)(long)e; - ErlDrvPort port_num; - int packet_bytes; int res; Uint h; + char *buf; + union SIGNAL *sig; + struct driver_data *data = (struct driver_data *)drv_data; - port_num = driver_data[fd].port_num; - packet_bytes = driver_data[fd].packet_bytes; + sig = erl_drv_ose_get_signal(ready_fd); + ASSERT(sig); - if (packet_bytes == 0) { - byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, - ERTS_SYS_READ_BUF_SZ); - res = async_read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ); - if (res < 0) { - if ((errno != EINTR) && (errno != ERRNO_BLOCK)) - port_inp_failure(port_num, ready_fd, res); - } - else if (res == 0) - port_inp_failure(port_num, ready_fd, res); - else - driver_output(port_num, (char*) read_buf, res); - erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf); - } - else if (fd_data[fd].remain > 0) { /* We try to read the remainder */ - /* space is allocated in buf */ - res = async_read(ready_fd, (byte*)fd_data[fd].cpos, - fd_data[fd].remain); - if (res < 0) { - if ((errno != EINTR) && (errno != ERRNO_BLOCK)) - port_inp_failure(port_num, ready_fd, res); - } - else if (res == 0) { - port_inp_failure(port_num, ready_fd, res); - } - else if (res == fd_data[fd].remain) { /* we're done */ - driver_output(port_num, fd_data[fd].buf, - fd_data[fd].sz); - clear_fd_data(fd); - } - else { /* if (res < fd_data[ready_fd].remain) */ - fd_data[fd].cpos += res; - fd_data[fd].remain -= res; - } - } - else if (fd_data[fd].remain == 0) { /* clean fd */ - byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, - ERTS_SYS_READ_BUF_SZ); - /* We make one read attempt and see what happens */ - res = async_read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ); - if (res < 0) { - if ((errno != EINTR) && (errno != ERRNO_BLOCK)) - port_inp_failure(port_num, ready_fd, res); - } - else if (res == 0) { /* eof */ - port_inp_failure(port_num, ready_fd, res); - } - else if (res < packet_bytes - fd_data[fd].psz) { - memcpy(fd_data[fd].pbuf+fd_data[fd].psz, - read_buf, res); - fd_data[fd].psz += res; - } - else { /* if (res >= packet_bytes) */ - unsigned char* cpos = read_buf; - int bytes_left = res; - - while (1) { - int psz = fd_data[fd].psz; - char* pbp = fd_data[fd].pbuf + psz; - - while(bytes_left && (psz < packet_bytes)) { - *pbp++ = *cpos++; - bytes_left--; - psz++; - } - - if (psz < packet_bytes) { - fd_data[fd].psz = psz; - break; - } - fd_data[fd].psz = 0; - - switch (packet_bytes) { - case 1: h = get_int8(fd_data[fd].pbuf); break; - case 2: h = get_int16(fd_data[fd].pbuf); break; - case 4: h = get_int32(fd_data[fd].pbuf); break; - default: ASSERT(0); return; /* -1; */ - } - - if (h <= (bytes_left)) { - driver_output(port_num, (char*) cpos, h); - cpos += h; - bytes_left -= h; - continue; - } - else { /* The last message we got was split */ - char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); - if (!buf) { - errno = ENOMEM; - port_inp_failure(port_num, ready_fd, -1); - } - else { - erts_smp_atomic_add_nob(&sys_misc_mem_sz, h); - sys_memcpy(buf, cpos, bytes_left); - fd_data[fd].buf = buf; - fd_data[fd].sz = h; - fd_data[fd].remain = h - bytes_left; - fd_data[fd].cpos = buf + bytes_left; - } - break; - } - } - } - erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf); + + while (sig) { + /* If we've recieved an attach signal, we need to handle + * it in port_inp_failure */ + if (sig->sig_no == ERTS_SIGNAL_OSE_DRV_ATTACH) { + port_inp_failure(data->port_num, ready_fd, 0); + } + else { + res = sig->fm_read_reply.actual; + + if (data->packet_bytes == 0) { + if (res < 0) { + if ((errno != EINTR) && (errno != ERRNO_BLOCK)) { + port_inp_failure(data->port_num, ready_fd, res); + } + } + else if (res == 0) { + /* read of 0 bytes, eof, otherside of pipe is assumed dead */ + port_inp_failure(data->port_num, ready_fd, res); + } + else { + buf = driver_alloc(res); + memcpy(buf, (void *)data->aiocb.aio_buf, res); + driver_select(data->port_num, data->output_event, + ERL_DRV_WRITE|ERL_DRV_USE, 1); + driver_output(data->port_num, (char*) buf, res); + driver_free(buf); + } + } + /* We try to read the remainder */ + else if (fd_data[data->ifd].remain > 0) { + if (res < 0) { + if ((errno != EINTR) && (errno != ERRNO_BLOCK)) { + port_inp_failure(data->port_num, ready_fd, res); + } + } + else if (res == 0) { + port_inp_failure(data->port_num, ready_fd, res); + } + else if (res == fd_data[data->ifd].remain) { /* we're done */ + driver_output(data->port_num, + fd_data[data->ifd].buf, + fd_data[data->ifd].sz); + clear_fd_data(data->ifd); + } + else { /* if (res < fd_data[fd].remain) */ + fd_data[data->ifd].cpos += res; + fd_data[data->ifd].remain -= res; + } + } + else if (fd_data[data->ifd].remain == 0) { /* clean fd */ + if (res < 0) { + if ((errno != EINTR) && (errno != ERRNO_BLOCK)) { + port_inp_failure(data->port_num, ready_fd, res); + } + } + else if (res == 0) { /* eof */ + port_inp_failure(data->port_num, ready_fd, res); + } + else if (res < data->packet_bytes - fd_data[data->ifd].psz) { + memcpy(fd_data[data->ifd].pbuf+fd_data[data->ifd].psz, + (void *)data->aiocb.aio_buf, res); + fd_data[data->ifd].psz += res; + } + else { /* if (res >= packet_bytes) */ + unsigned char* cpos = (unsigned char*)data->aiocb.aio_buf; + int bytes_left = res; + + while (1) { + int psz = fd_data[data->ifd].psz; + char* pbp = fd_data[data->ifd].pbuf + psz; + + while (bytes_left && (psz < data->packet_bytes)) { + *pbp++ = *cpos++; + bytes_left--; + psz++; + } + + if (psz < data->packet_bytes) { + fd_data[data->ifd].psz = psz; + break; + } + fd_data[data->ifd].psz = 0; + + switch (data->packet_bytes) { + case 1: h = get_int8(fd_data[data->ifd].pbuf); break; + case 2: h = get_int16(fd_data[data->ifd].pbuf); break; + case 4: h = get_int32(fd_data[data->ifd].pbuf); break; + default: ASSERT(0); return; /* -1; */ + } + + if (h <= (bytes_left)) { + driver_output(data->port_num, (char*) cpos, h); + cpos += h; + bytes_left -= h; + continue; + } + else { /* The last message we got was split */ + char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); + if (!buf) { + errno = ENOMEM; + port_inp_failure(data->port_num, ready_fd, -1); + } + else { + erts_smp_atomic_add_nob(&sys_misc_mem_sz, h); + sys_memcpy(buf, cpos, bytes_left); + fd_data[data->ifd].buf = buf; + fd_data[data->ifd].sz = h; + fd_data[data->ifd].remain = h - bytes_left; + fd_data[data->ifd].cpos = buf + bytes_left; + } + break; + } + } + } + } + + /* reset the read buffer and init next asynch read */ + DISPATCH_AIO(sig); + memset((void *)data->aiocb.aio_buf, 0, 255); + + if (res > 0) { + aio_read(&data->aiocb); + } + } + sig = erl_drv_ose_get_signal(ready_fd); } } -/* fd is the drv_data that is returned from the */ -/* initial start routine */ -/* ready_fd is the descriptor that is ready to read */ - -static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) +/* The parameter e is a pointer to the driver_data structure + * related to the fd to be used as output. + * ready_fd is the event that triggered this call to ready_input */ +static void ready_output(ErlDrvData drv_data, ErlDrvEvent ready_fd) { - int fd = (int)(long)e; - ErlDrvPort ix = driver_data[fd].port_num; - union SIGNAL *sigptr = erl_drv_ose_get_signal(ready_fd); - ssize_t n; - struct iovec* iv; - int vsize; - - while (sigptr != NULL) { - - driver_pdl_lock(driver_data[fd].pdl); - if ((iv = (struct iovec*) driver_peekq(ix, &vsize)) == NULL) { - /* fprintf(stderr,"0x%x: ready_output, unselect\n", current_process()); */ - driver_pdl_unlock(driver_data[fd].pdl); - driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); - set_busy_port(ix, 0); - free_buf(&sigptr); - if ((sigptr = erl_drv_ose_get_signal(ready_fd)) == NULL) - return; /* 0; */ - continue; + SysIOVec *iov; + int vlen; + int res; + union SIGNAL *sig; + struct driver_data *data = (struct driver_data *)drv_data; + + sig = erl_drv_ose_get_signal(ready_fd); + ASSERT(sig); + + while (sig != NULL) { + if (sig->fm_write_reply.actual <= 0) { + int status; + + status = efs_status_to_errno(sig->fm_write_reply.status); + driver_select(data->port_num, ready_fd, ERL_DRV_WRITE, 0); + DISPATCH_AIO(sig); + FREE_AIO(sig->fm_write_reply.buffer); + + driver_failure_posix(data->port_num, status); } - driver_pdl_unlock(driver_data[fd].pdl); - n = sigptr->sys_async.res; - if (n < 0) { - if (errno == ERRNO_BLOCK || errno == EINTR) { - /* fprintf(stderr,"0x%x: ready_output, send to %x\n", current_process(),driver_data[fd].out_proc);*/ - send(&sigptr,driver_data[fd].out_proc); - if ((sigptr = erl_drv_ose_get_signal(ready_fd)) == NULL) - return; /* 0; */ - continue; - } else { - int res = sigptr->sys_async.errno_copy; - /* fprintf(stderr,"0x%x: ready_output, error\n", current_process()); */ - free_buf(&sigptr); - driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); - driver_failure_posix(ix, res); - if ((sigptr = erl_drv_ose_get_signal(ready_fd)) == NULL) - return; /* -1; */ - continue; - } - } else { - int remain; - driver_pdl_lock(driver_data[fd].pdl); - if ((remain = driver_deq(driver_data[fd].port_num, n)) == -1) - abort(); - /* fprintf(stderr, "0x%x: ready_output, %d to %x, remain %d\n", current_process(), - n, driver_data[fd].out_proc, remain); */ - driver_pdl_unlock(driver_data[fd].pdl); - if (remain != 0) - send(&sigptr, driver_data[fd].out_proc); - else - continue; + else { /* written bytes > 0 */ + iov = driver_peekq(data->port_num, &vlen); + if (vlen > 0) { + DISPATCH_AIO(sig); + FREE_AIO(sig->fm_write_reply.buffer); + res = driver_deq(data->port_num, iov[0].iov_len); + if (res > 0) { + iov = driver_peekq(data->port_num, &vlen); + WRITE_AIO(data->ofd, iov[0].iov_len, iov[0].iov_base); + } + } } - sigptr = erl_drv_ose_get_signal(ready_fd); - } - return; /* 0; */ + sig = erl_drv_ose_get_signal(ready_fd); + } } -static void stop_select(ErlDrvEvent fd, void* _) +static void stop_select(ErlDrvEvent ready_fd, void* _) { - close((int)fd); + int *fd; + erl_drv_ose_event_fetch(ready_fd, NULL, NULL, (void **)&fd); + erl_drv_ose_event_free(ready_fd); + close(*fd); } @@ -1690,42 +1677,16 @@ erl_debug(char* fmt, ...) static ERTS_INLINE void report_exit_status(ErtsSysReportExit *rep, int status) { - Port *pp; -#ifdef ERTS_SMP - CHLD_STAT_UNLOCK; - pp = erts_thr_id2port_sflgs(rep->port, - ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); - CHLD_STAT_LOCK; -#else - pp = erts_id2port_sflgs(rep->port, - NULL, - 0, - ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); -#endif - if (pp) { - if (rep->ifd >= 0) { - driver_data[rep->ifd].alive = 0; - driver_data[rep->ifd].status = status; - (void) driver_select(ERTS_Port2ErlDrvPort(pp), - rep->in_sig_descr, - (ERL_DRV_READ|ERL_DRV_USE), - 1); - } - if (rep->ofd >= 0) { - driver_data[rep->ofd].alive = 0; - driver_data[rep->ofd].status = status; - (void) driver_select(ERTS_Port2ErlDrvPort(pp), - rep->out_sig_descr, - (ERL_DRV_WRITE|ERL_DRV_USE), - 1); - } -#ifdef ERTS_SMP - erts_thr_port_release(pp); -#else - erts_port_release(pp); -#endif - } - erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep); + if (rep->ifd >= 0) { + driver_data[rep->ifd].alive = 0; + driver_data[rep->ifd].status = status; + } + if (rep->ofd >= 0) { + driver_data[rep->ofd].alive = 0; + driver_data[rep->ofd].status = status; + } + + erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep); } #define ERTS_REPORT_EXIT_STATUS report_exit_status -- cgit v1.2.3 From f4a93aded400f55f76706030bebcabbc68a7168a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 21 Feb 2014 09:51:58 +0100 Subject: ose: Polish mmap configure checks --- erts/configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 142e3a3ad7..8cf25a70be 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2079,9 +2079,9 @@ esac case $host_os in *ose) AC_MSG_CHECKING([for mmap]) - AC_MSG_RESULT(no) + AC_MSG_RESULT(not using for OSE) AC_MSG_CHECKING([for mremap]) - AC_MSG_RESULT(no) ;; + AC_MSG_RESULT(not using for OSE) ;; *) AC_CHECK_FUNCS([mmap mremap]) ;; esac -- cgit v1.2.3 From 494d9125a92658d852e22c429ca61687e0709c09 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 21 Feb 2014 11:30:41 +0100 Subject: ose: Cleanup cleanup of mutex selection defines --- erts/lib_src/common/ethr_mutex.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/lib_src/common/ethr_mutex.c b/erts/lib_src/common/ethr_mutex.c index 47f2f884af..72b44033ad 100644 --- a/erts/lib_src/common/ethr_mutex.c +++ b/erts/lib_src/common/ethr_mutex.c @@ -1428,8 +1428,6 @@ void LeaveCriticalSection(CRITICAL_SECTION *cs) ETHR_FATAL_ERROR__(res); } -#else -#error "No mutex implementation found" #endif #define ETHR_CND_WAIT__ ((ethr_sint32_t) 0x11dead11) @@ -1754,6 +1752,8 @@ ethr_cond_destroy(ethr_cond *cnd) return 0; } +#else +#error "No mutex implementation found" #endif /* -- Exported symbols of inline functions --------------------------------- */ -- cgit v1.2.3 From 08910c59fb8dda35ee3c272d84cba450954fcd24 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 21 Feb 2014 15:19:31 +0100 Subject: ose: Add dummy ttsl driver --- erts/emulator/Makefile.in | 3 +- erts/emulator/drivers/ose/ttsl_drv.c | 68 ++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 erts/emulator/drivers/ose/ttsl_drv.c (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 3c874f6142..4de4e03c01 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -834,7 +834,8 @@ DRV_OBJS = \ $(OBJDIR)/ose_signal_drv.o \ $(OBJDIR)/inet_drv.o \ $(OBJDIR)/zlib_drv.o \ - $(OBJDIR)/ram_file_drv.o + $(OBJDIR)/ram_file_drv.o \ + $(OBJDIR)/ttsl_drv.o else OS_OBJS = \ diff --git a/erts/emulator/drivers/ose/ttsl_drv.c b/erts/emulator/drivers/ose/ttsl_drv.c new file mode 100644 index 0000000000..8af2ce6af3 --- /dev/null +++ b/erts/emulator/drivers/ose/ttsl_drv.c @@ -0,0 +1,68 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * + * The 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% + */ +/* + * Stub tty driver because group/user depend on this. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "erl_driver.h" + +static int ttysl_init(void); +static ErlDrvData ttysl_start(ErlDrvPort, char*); + +/* Define the driver table entry. */ +struct erl_drv_entry ttsl_driver_entry = { + ttysl_init, + ttysl_start, + NULL, + NULL, + NULL, + NULL, + "tty_sl", + NULL, + NULL, + NULL, + NULL, /* timeout */ + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, /* flush */ + NULL, /* call */ + NULL, /* event */ + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, /* ERL_DRV_FLAGs */ + NULL, + NULL, /* process_exit */ + NULL +}; + + +static int ttysl_init(void) +{ + return 0; +} + +static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) +{ + return ERL_DRV_ERROR_GENERAL; +} -- cgit v1.2.3 From 13ed57745c639f72be8ae1f25fbd206f6e7307f3 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 21 Feb 2014 15:19:53 +0100 Subject: ose: Expand OSE docs --- erts/etc/common/to_erl_common.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/etc/common/to_erl_common.c b/erts/etc/common/to_erl_common.c index 1b2f27fa04..a49be44b6c 100644 --- a/erts/etc/common/to_erl_common.c +++ b/erts/etc/common/to_erl_common.c @@ -279,7 +279,11 @@ int to_erl(int argc, char **argv) fprintf(stderr, "to_erl: %s opened for writing\n", FIFO2); #endif +#ifndef __OSE__ fprintf(stderr, "Attaching to %s (^D to exit)\n\n", pipename); +#else + fprintf(stderr, "Attaching to %s (^C to exit)\n\n", pipename); +#endif #ifndef __OSE__ /* Set break handler to our handler */ -- cgit v1.2.3 From 81abbc48e12317a07a8d2fcc041031c1c1567c8d Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 24 Feb 2014 09:43:11 +0100 Subject: erts: Fix unix efile assert If writev return an error (eg ENOSPC) we do not want to abort here but instead propagate upwards into erlang. --- erts/emulator/drivers/unix/unix_efile.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index 06078858ca..42f41c5f3d 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -634,7 +634,8 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */ do { w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len); } while (w < 0 && errno == EINTR); - ASSERT(w <= iov[cnt].iov_len); + ASSERT(w <= iov[cnt].iov_len || + (w == -1 && errno != EINTR)); } if (w < 0) return check_error(-1, errInfo); /* Move forward to next buffer to write */ -- cgit v1.2.3 From eb53a3f0b7a7d6c4d0a877fe71bc0b0ca11d1597 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 20 Feb 2014 14:34:41 +0100 Subject: erts: Fix heap overwrite by hipe "trap frames" when GC is disabled by trapping BIFs like term_to_binary and binary_to_term. --- erts/emulator/beam/external.c | 65 ++++++++++++++++++++++++++++++++++- erts/emulator/hipe/hipe_bif_list.m4 | 14 ++++++-- erts/emulator/hipe/hipe_mode_switch.c | 31 +++++++++++++++-- erts/emulator/hipe/hipe_mode_switch.h | 3 ++ 4 files changed, 107 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 9fb2dbd8bf..2ca52c8025 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1068,7 +1068,7 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1) BIF_RET(res); } } - + BIF_RETTYPE term_to_binary_1(BIF_ALIST_1) { Eterm res = erts_term_to_binary_int(BIF_P, BIF_ARG_1, 0, TERM_TO_BINARY_DFLAGS, NULL); @@ -4459,3 +4459,66 @@ error: #undef SKIP2 #undef CHKSIZE } + + +#ifdef HIPE +BIF_RETTYPE hipe_wrapper_term_to_binary_1(BIF_ALIST_1); +BIF_RETTYPE hipe_wrapper_term_to_binary_2(BIF_ALIST_2); +BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_1(BIF_ALIST_1); +BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_2(BIF_ALIST_2); + +/* Hipe wrappers used by native code for BIFs that disable GC while trapping. + * + * Problem: + * When native code calls a BIF that traps, hipe_mode_switch will push a + * "trap frame" on the Erlang stack in order to find its way back from beam_emu + * back to native caller when finally done. If GC is disabled and stack/heap + * is full there is no place to push the "trap frame". + * + * Solution: + * We reserve space on stack for the "trap frame" here before the BIF is called. + * If the BIF does not trap, the space is reclaimed here before returning. + * If the BIF traps, hipe_push_beam_trap_frame() will detect that a "trap frame" + * already is reserved and use it. + */ +BIF_RETTYPE hipe_wrapper_term_to_binary_1(BIF_ALIST_1) +{ + Eterm res; + hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 1); + res = term_to_binary_1(BIF_P, BIF__ARGS); + if (is_value(res) || BIF_P->freason != TRAP) { + hipe_unreserve_beam_trap_frame(BIF_P); + } + return res; +} +BIF_RETTYPE hipe_wrapper_term_to_binary_2(BIF_ALIST_2) +{ + Eterm res; + hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 2); + res = term_to_binary_2(BIF_P, BIF__ARGS); + if (is_value(res) || BIF_P->freason != TRAP) { + hipe_unreserve_beam_trap_frame(BIF_P); + } + return res; +} +BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_1(BIF_ALIST_1) +{ + Eterm res; + hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 1); + res = erts_internal_binary_to_term_1(BIF_P, BIF__ARGS); + if (is_value(res) || BIF_P->freason != TRAP) { + hipe_unreserve_beam_trap_frame(BIF_P); + } + return res; +} +BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_2(BIF_ALIST_2) +{ + Eterm res; + hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 2); + res = erts_internal_binary_to_term_2(BIF_P, BIF__ARGS); + if (is_value(res) || BIF_P->freason != TRAP) { + hipe_unreserve_beam_trap_frame(BIF_P); + } + return res; +} +#endif /*HIPE*/ diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index b1fedf4838..0997d81b2f 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2012. All Rights Reserved. + * Copyright Ericsson AB 2004-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -262,7 +262,17 @@ noproc_primop_interface_1(nbif_atomic_inc, hipe_atomic_inc) * Standard BIFs. * BIF_LIST(ModuleAtom,FunctionAtom,Arity,CFun,Index) */ -define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, $4)') + +/* BIFs that disable GC while trapping are called via a wrapper + * to reserve stack space for the "trap frame". + */ +define(CFUN,`ifelse($1,term_to_binary_1,hipe_wrapper_term_to_binary_1, +ifelse($1,term_to_binary_2,hipe_wrapper_term_to_binary_2, +ifelse($1,erts_internal_binary_to_term_1,hipe_wrapper_erts_internal_binary_to_term_1, +ifelse($1,erts_internal_binary_to_term_2,hipe_wrapper_erts_internal_binary_to_term_2, +$1))))') + +define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))') include(TARGET/`erl_bif_list.h') /* diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index adc8793469..15cdb231a2 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -184,21 +184,46 @@ void hipe_set_call_trap(Uint *bfun, void *nfun, int is_closure) bfun[-4] = (Uint)nfun; } -static __inline__ void -hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) +void hipe_reserve_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) { /* ensure that at least 2 words are available on the BEAM stack */ if ((p->stop - 2) < p->htop) { - DPRINTF("calling gc to increase BEAM stack size"); + DPRINTF("calling gc to reserve BEAM stack size"); p->fcalls -= erts_garbage_collect(p, 2, reg, arity); + ASSERT(!((p->stop - 2) < p->htop)); } p->stop -= 2; + p->stop[0] = NIL; + p->stop[1] = NIL; +} + +static __inline__ void +hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) +{ + if (p->flags & F_DISABLE_GC) { + /* Trap frame already reserved */ + ASSERT(p->stop[0] == NIL && p->stop[1] == NIL); + } + else { + if ((p->stop - 2) < p->htop) { + DPRINTF("calling gc to increase BEAM stack size"); + p->fcalls -= erts_garbage_collect(p, 2, reg, arity); + ASSERT(!((p->stop - 2) < p->htop)); + } + p->stop -= 2; + } p->stop[1] = hipe_beam_catch_throw; p->stop[0] = make_cp(p->cp); ++p->catches; p->cp = hipe_beam_pc_return; } +void hipe_unreserve_beam_trap_frame(Process *p) +{ + ASSERT(p->stop[0] == NIL && p->stop[1] == NIL); + p->stop += 2; +} + static __inline__ void hipe_pop_beam_trap_frame(Process *p) { p->cp = cp_val(p->stop[0]); diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h index a3e908a3b3..06721e3c04 100644 --- a/erts/emulator/hipe/hipe_mode_switch.h +++ b/erts/emulator/hipe/hipe_mode_switch.h @@ -59,6 +59,9 @@ void hipe_empty_nstack(Process *p); void hipe_set_closure_stub(ErlFunEntry *fe, unsigned num_free); Eterm hipe_build_stacktrace(Process *p, struct StackTrace *s); +void hipe_reserve_beam_trap_frame(Process*, Eterm reg[], unsigned arity); +void hipe_unreserve_beam_trap_frame(Process*); + extern Uint hipe_beam_pc_return[]; extern Uint hipe_beam_pc_throw[]; extern Uint hipe_beam_pc_resume[]; -- cgit v1.2.3 From f3646fe69c2b36ba51e3fd8d345856df5145362a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 26 Feb 2014 13:24:42 +0100 Subject: Disable accidentally enabled floating point exceptions on MacOS X In c7ddafbe, which disabled floating point exceptions on Linux, floating point exceptions were accidentally enabled on MacOS X. --- erts/configure.in | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 408c00c9e9..074882532f 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2829,8 +2829,6 @@ if test X${enable_fp_exceptions} = Xauto ; then *linux*) enable_fp_exceptions=no AC_MSG_NOTICE([Floating point exceptions disabled by default on Linux]) ;; - *) - ;; darwin*) enable_fp_exceptions=no AC_MSG_NOTICE([Floating point exceptions disabled by default on MacOS X]) ;; -- cgit v1.2.3 From e444e9a2ac6a2d70a34d95c97c4af0d39aac232d Mon Sep 17 00:00:00 2001 From: Jay True Date: Wed, 26 Feb 2014 22:29:00 +0800 Subject: ensure argv large enough for all possible args --- erts/epmd/src/epmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 2d55b37ff3..eb32a08e0b 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -52,7 +52,7 @@ static int epmd_main(int, char **, int); int epmd_dbg(int level,int port) /* Utility to debug epmd... */ { - char* argv[MAX_DEBUG+2]; + char* argv[MAX_DEBUG+4]; char ibuff[100]; int argc = 0; -- cgit v1.2.3 From 7d4bb870a8400599166407202d584ad4ea2b741d Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 26 Feb 2014 15:36:17 +0100 Subject: Update preloaded and primary bootstrap --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 54760 -> 54792 bytes erts/preloaded/ebin/erlang.beam | Bin 98256 -> 98276 bytes erts/preloaded/ebin/erts_internal.beam | Bin 4276 -> 4296 bytes erts/preloaded/ebin/init.beam | Bin 48804 -> 48816 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1464 -> 1480 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1340 -> 1360 bytes erts/preloaded/ebin/prim_file.beam | Bin 44880 -> 44916 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72916 -> 72932 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23436 -> 23448 bytes erts/preloaded/ebin/zlib.beam | Bin 13164 -> 13196 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 8f3bfd356c..26f851fd7a 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index eb696bb32f..344176b71b 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 4a84b3945a..bd402f9a98 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 758fe2cc23..5d49eed469 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 3fede5f0d9..2607739d12 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 42c15f681f..d665d35ab1 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index e40e33b332..201252de34 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 90395a7564..ff8265414e 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index e1aaa0f451..f80d4a96e5 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index ffd80f51ba..dba3ec9546 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 615e41b3810cecc7a30668193ce1fb0207cc42c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 27 Feb 2014 17:50:58 +0100 Subject: erts: Fix Map cmp exact equal of an empty map The expression, #{} =:= M where M was any Map, would always result in 'true'. This commit fixes this issue by first comparing sizes for both terms and then checking for size zero. --- erts/emulator/beam/utils.c | 6 +++++- erts/emulator/test/map_SUITE.erl | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 5b43d25e3c..738f793020 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2125,7 +2125,11 @@ tailrecur_ne: if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa) goto not_equal; bb = map_val_rel(b,b_base); - if ((sz = map_get_size((map_t*)aa)) == 0) goto pop_next; + sz = map_get_size((map_t*)aa); + + if (sz != map_get_size((map_t*)bb)) goto not_equal; + if (sz == 0) goto pop_next; + aa += 2; bb += 2; sz += 1; /* increment for tuple-keys */ diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 8cc5621181..753d6f7727 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -29,6 +29,7 @@ t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, t_map_sort_literals/1, + t_map_equal/1, %t_size/1, t_map_size/1, @@ -74,6 +75,7 @@ all() -> [ t_update_assoc,t_update_exact, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, + t_map_equal, t_map_sort_literals, %% Specific Map BIFs @@ -450,6 +452,23 @@ t_map_sort_literals(Config) when is_list(Config) -> ok. +t_map_equal(Config) when is_list(Config) -> + true = id(#{}) =:= id(#{}), + false = id(#{}) =:= id(#{a=>1}), + false = id(#{a=>1}) =:= id(#{}), + true = id(#{ "a" => "hi", b => 134 }) =:= id(#{ b => 134,"a" => "hi"}), + + false = id(#{ a => 1 }) =:= id(#{ a => 2}), + false = id(#{ a => 2 }) =:= id(#{ a => 1}), + false = id(#{ a => 2, b => 1 }) =:= id(#{ a => 1, b => 3}), + false = id(#{ a => 1, b => 1 }) =:= id(#{ a => 1, b => 3}), + + true = id(#{ a => 1 }) =:= id(#{ a => 1}), + true = id(#{ "a" => 2 }) =:= id(#{ "a" => 2}), + true = id(#{ "a" => 2, b => 3 }) =:= id(#{ "a" => 2, b => 3}), + true = id(#{ a => 1, b => 3, c => <<"wat">> }) =:= id(#{ a => 1, b => 3, c=><<"wat">>}), + ok. + %% BIFs t_bif_map_get(Config) when is_list(Config) -> -- cgit v1.2.3 From 81c7db0e247a6ee1b7a5c2764aa9575d7f6fb88a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 28 Feb 2014 06:08:51 +0100 Subject: hipe: Break apart hipe_bif:make_fe/3 into two BIFs This commit is a preparation for eliminating a race condition loading the native code for modules whose BEAM code has already been loaded. Here we introduce two new BIFs so that looking up a fun entry is separate from setting the native address in the fun entry. --- erts/emulator/hipe/hipe_bif0.c | 39 ++++++++++++++++++++++++++------------- erts/emulator/hipe/hipe_bif0.tab | 3 ++- 2 files changed, 28 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index fa99c817f0..2497d51df1 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1101,9 +1101,9 @@ BIF_RETTYPE hipe_bifs_make_fun_3(BIF_ALIST_3) #endif /* - * args: Nativecodeaddress, Module, {Uniq, Index, BeamAddress} + * args: Module, {Uniq, Index, BeamAddress} */ -BIF_RETTYPE hipe_bifs_make_fe_3(BIF_ALIST_3) +BIF_RETTYPE hipe_bifs_get_fe_2(BIF_ALIST_2) { Eterm mod; Uint index; @@ -1111,20 +1111,15 @@ BIF_RETTYPE hipe_bifs_make_fe_3(BIF_ALIST_3) void *beam_address; ErlFunEntry *fe; Eterm *tp; - void *native_address; - - native_address = term_to_address(BIF_ARG_1); - if (!native_address) - BIF_ERROR(BIF_P, BADARG); - if (is_not_atom(BIF_ARG_2)) + if (is_not_atom(BIF_ARG_1)) BIF_ERROR(BIF_P, BADARG); - mod = BIF_ARG_2; + mod = BIF_ARG_1; - if (is_not_tuple(BIF_ARG_3) || - (arityval(*tuple_val(BIF_ARG_3)) != 3)) + if (is_not_tuple(BIF_ARG_2) || + (arityval(*tuple_val(BIF_ARG_2)) != 3)) BIF_ERROR(BIF_P, BADARG); - tp = tuple_val(BIF_ARG_3); + tp = tuple_val(BIF_ARG_2); if (term_to_Uint(tp[1], &uniq) == 0) BIF_ERROR(BIF_P, BADARG); if (term_to_Uint(tp[2], &index) == 0) @@ -1144,10 +1139,28 @@ BIF_RETTYPE hipe_bifs_make_fe_3(BIF_ALIST_3) printf("no fun entry for %s %ld:%ld\n", atom_buf, uniq, index); BIF_ERROR(BIF_P, BADARG); } + BIF_RET(address_to_term((void *)fe, BIF_P)); +} + +/* + * args: FE, Nativecodeaddress + */ +BIF_RETTYPE hipe_bifs_set_native_address_in_fe_2(BIF_ALIST_2) +{ + ErlFunEntry *fe; + void *native_address; + + fe = (ErlFunEntry *)term_to_address(BIF_ARG_1); + if (!fe) + BIF_ERROR(BIF_P, BADARG); + native_address = term_to_address(BIF_ARG_2); + if (!native_address) + BIF_ERROR(BIF_P, BADARG); + fe->native_address = native_address; if (erts_refc_dectest(&fe->refc, 0) == 0) erts_erase_fun_entry(fe); - BIF_RET(address_to_term((void *)fe, BIF_P)); + BIF_RET(am_true); } #if 0 /* XXX: unused */ diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab index ce641365e9..2514b1c3a5 100644 --- a/erts/emulator/hipe/hipe_bif0.tab +++ b/erts/emulator/hipe/hipe_bif0.tab @@ -69,7 +69,8 @@ bif hipe_bifs:atom_to_word/1 bif hipe_bifs:term_to_word/1 #bif hipe_bifs:make_fun/3 -bif hipe_bifs:make_fe/3 +bif hipe_bifs:get_fe/2 +bif hipe_bifs:set_native_address_in_fe/2 #bif hipe_bifs:make_native_stub/2 bif hipe_bifs:find_na_or_make_stub/2 -- cgit v1.2.3 From adb5dc0090bc419e2c4c1250653badbddeb6263b Mon Sep 17 00:00:00 2001 From: Jani Hakala Date: Thu, 27 Feb 2014 15:23:44 +0200 Subject: Add ETHR_FORCE_INLINE define to hide compiler specific directives Some win32 specific code does not compile with gcc (mingw-w64) since '__forceinline' is not supported by gcc. This can be avoided by defining a new macro ETHR_FORCE_INLINE similar to ETHR_INLINE. --- erts/include/internal/ethread.h | 3 +++ erts/include/internal/win/ethr_membar.h | 8 ++++---- erts/lib_src/common/erl_misc_utils.c | 7 ++++--- erts/lib_src/common/ethr_mutex.c | 6 +++--- 4 files changed, 14 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 64f1fae6d8..6f9bc184ef 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -54,13 +54,16 @@ #undef ETHR_INLINE #if defined(__GNUC__) # define ETHR_INLINE __inline__ +# define ETHR_FORCE_INLINE __inline__ __attribute__((__always_inline__)) #elif defined(__WIN32__) # define ETHR_INLINE __forceinline +# define ETHR_FORCE_INLINE __forceinline #endif #if defined(ETHR_DEBUG) || !defined(ETHR_INLINE) || ETHR_XCHK \ || (defined(__GNUC__) && defined(ERTS_MIXED_CYGWIN_VC)) # undef ETHR_INLINE # define ETHR_INLINE +# define ETHR_FORCE_INLINE # undef ETHR_TRY_INLINE_FUNCS #endif diff --git a/erts/include/internal/win/ethr_membar.h b/erts/include/internal/win/ethr_membar.h index 8237660b2c..a17f2459fc 100644 --- a/erts/include/internal/win/ethr_membar.h +++ b/erts/include/internal/win/ethr_membar.h @@ -63,13 +63,13 @@ do { \ #pragma intrinsic(_mm_sfence) #pragma intrinsic(_mm_lfence) -static __forceinline void +static ETHR_FORCE_INLINE void ethr_cfence__(void) { _ReadWriteBarrier(); } -static __forceinline void +static ETHR_FORCE_INLINE void ethr_mfence__(void) { #if ETHR_SIZEOF_PTR == 4 @@ -80,7 +80,7 @@ ethr_mfence__(void) _mm_mfence(); } -static __forceinline void +static ETHR_FORCE_INLINE void ethr_sfence__(void) { #if ETHR_SIZEOF_PTR == 4 @@ -91,7 +91,7 @@ ethr_sfence__(void) _mm_sfence(); } -static __forceinline void +static ETHR_FORCE_INLINE void ethr_lfence__(void) { #if ETHR_SIZEOF_PTR == 4 diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c index 5a271c5268..8bf7656bb0 100644 --- a/erts/lib_src/common/erl_misc_utils.c +++ b/erts/lib_src/common/erl_misc_utils.c @@ -25,6 +25,7 @@ # include #endif +#include "ethread.h" #include "erl_misc_utils.h" #if defined(__WIN32__) @@ -191,7 +192,7 @@ struct erts_cpu_info_t_ { #if defined(__WIN32__) -static __forceinline int +static ETHR_FORCE_INLINE int get_proc_affinity(erts_cpu_info_t *cpuinfo, cpu_set_t *cpuset) { DWORD_PTR pamask; @@ -206,7 +207,7 @@ get_proc_affinity(erts_cpu_info_t *cpuinfo, cpu_set_t *cpuset) } } -static __forceinline int +static ETHR_FORCE_INLINE int set_thr_affinity(cpu_set_t *set) { if (*set == (cpu_set_t) 0) @@ -1157,7 +1158,7 @@ read_topology(erts_cpu_info_t *cpuinfo) #define ERTS_MU_RELATION_CACHE 2 /* RelationCache */ #define ERTS_MU_RELATION_PROCESSOR_PACKAGE 3 /* RelationProcessorPackage */ -static __forceinline int +static ETHR_FORCE_INLINE int rel_cmp_val(int r) { switch (r) { diff --git a/erts/lib_src/common/ethr_mutex.c b/erts/lib_src/common/ethr_mutex.c index 72b44033ad..4e56efaf8b 100644 --- a/erts/lib_src/common/ethr_mutex.c +++ b/erts/lib_src/common/ethr_mutex.c @@ -1433,7 +1433,7 @@ void LeaveCriticalSection(CRITICAL_SECTION *cs) #define ETHR_CND_WAIT__ ((ethr_sint32_t) 0x11dead11) #define ETHR_CND_WAKEUP__ ((ethr_sint32_t) 0x11beef11) -static __forceinline void +static ETHR_FORCE_INLINE void cond_wakeup(ethr_ts_event *tse) { ETHR_ASSERT(ethr_atomic32_read(&tse->uaflgs) == ETHR_CND_WAIT__); @@ -1574,7 +1574,7 @@ ethr_cond_wait(ethr_cond *cnd, ethr_mutex *mtx) return 0; } -static __forceinline void +static ETHR_FORCE_INLINE void posix_compliant_mtx_enqueue(ethr_mutex *mtx, ethr_ts_event *tse_start, ethr_ts_event *tse_end) @@ -1614,7 +1614,7 @@ posix_compliant_mtx_enqueue(ethr_mutex *mtx, } } -static __forceinline void +static ETHR_FORCE_INLINE void enqueue_cond_wakeups(ethr_ts_event *queue, int posix_compliant) { if (queue) { -- cgit v1.2.3 From 2fc2df4f7f99b1e1b4e39cfd063a65d9d1266756 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 28 Feb 2014 16:51:04 +0100 Subject: erts: Fix leak in nif_SUITE:resource_takeover (again) --- erts/emulator/test/nif_SUITE_data/nif_mod.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c index 55a0d2ac4f..11b5d0cc35 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.c +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c @@ -217,7 +217,8 @@ static int upgrade(ErlNifEnv* env, void** priv, void** old_priv_data, ERL_NIF_TE *priv = *old_priv_data; do_load_info(env, load_info, &retval); - + if (retval) + NifModPrivData_release(data); return retval; } -- cgit v1.2.3 From 2cacb193552f4037ef56b5a39905dd91f0f39c8c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 28 Feb 2014 17:38:04 +0100 Subject: erts: Suppress false leak in hipe_thread_signal_init --- erts/emulator/hipe/hipe_x86_signal.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index 8f997aafab..f5668013e2 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -2,7 +2,7 @@ * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -304,7 +304,9 @@ static void hipe_sigaltstack(void *ss_sp) */ void hipe_thread_signal_init(void) { - hipe_sigaltstack(erts_alloc(ERTS_ALC_T_HIPE, SIGSTKSZ)); + /* Stack don't really need to be cache aligned. + We use it to suppress false leak report from valgrind */ + hipe_sigaltstack(erts_alloc_permanent_cache_aligned(ERTS_ALC_T_HIPE, SIGSTKSZ)); } #endif -- cgit v1.2.3 From 75937381ec52e1714cfbc5936441b15bb1240bdb Mon Sep 17 00:00:00 2001 From: Cobus Carstens Date: Wed, 5 Mar 2014 15:30:48 +0200 Subject: Fix comment that differs from code The comment in the code state that the tree is traversed to the left, when in fact it is traversed to the right. --- erts/emulator/beam/erl_db_tree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 25029ba90f..a62a83a928 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -485,7 +485,7 @@ static int db_first_tree(Process *p, DbTable *tbl, Eterm *ret) *ret = am_EOT; return DB_ERROR_NONE; } - /* Walk down to the tree to the left */ + /* Walk down the tree to the left */ if ((stack = get_static_stack(tb)) != NULL) { stack->pos = stack->slot = 0; } @@ -531,7 +531,7 @@ static int db_last_tree(Process *p, DbTable *tbl, Eterm *ret) *ret = am_EOT; return DB_ERROR_NONE; } - /* Walk down to the tree to the left */ + /* Walk down the tree to the right */ if ((stack = get_static_stack(tb)) != NULL) { stack->pos = stack->slot = 0; } -- cgit v1.2.3 From a307b90ba474d2700289cd010d606b8c133fbacb Mon Sep 17 00:00:00 2001 From: Yiannis Tsiouris Date: Tue, 28 Jan 2014 18:05:31 +0200 Subject: Add a BIF that only returns the atom ok A call in llvm_fix_pinned_regs/0 is inserted in the beginning of LLVM unwind blocks (i.e. code executed when an LLVM 'invoke' call triggers an exception) in order to get the BP and SP registers right. This is needed because LLVM exception handling doesn't provide the return value (that also contains the values for these registers, as defined in the HiPE Calling Convention for LLVM) on the 'landingpad's. --- erts/emulator/hipe/hipe_bif2.c | 7 +++++++ erts/emulator/hipe/hipe_bif2.tab | 1 + 2 files changed, 8 insertions(+) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c index 7637049bc3..054911e822 100644 --- a/erts/emulator/hipe/hipe_bif2.c +++ b/erts/emulator/hipe/hipe_bif2.c @@ -182,3 +182,10 @@ BIF_RETTYPE hipe_bifs_debug_native_called_2(BIF_ALIST_2) BIF_RET(am_ok); } +/* Stub-BIF for LLVM: + * Reloads BP, SP (in llvm unwind label) */ + +BIF_RETTYPE hipe_bifs_llvm_fix_pinned_regs_0(BIF_ALIST_0) +{ + BIF_RET(am_ok); +} diff --git a/erts/emulator/hipe/hipe_bif2.tab b/erts/emulator/hipe/hipe_bif2.tab index 45a395bf57..1b659cfa90 100644 --- a/erts/emulator/hipe/hipe_bif2.tab +++ b/erts/emulator/hipe/hipe_bif2.tab @@ -30,3 +30,4 @@ 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 +bif hipe_bifs:llvm_fix_pinned_regs/0 -- cgit v1.2.3 From e3e8a510ed6c5b242e2ce2b83447843d0c1a47fb Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sun, 9 Mar 2014 02:16:35 +0100 Subject: Remove a superfluous echo in the emulator Makefile It pollutes the output of silent rules. --- erts/emulator/Makefile.in | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 523130d01a..d900b3eec7 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -1035,7 +1035,6 @@ $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(LCF) else $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) - echo $(DEPLIBS) $(ld_verbose)$(PURIFY) $(LD) -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \ $(HIPEBEAMLDFLAGS) $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) \ $(STATIC_NIF_LIBS) $(STATIC_DRIVER_LIBS) $(LIBS) -- cgit v1.2.3 From 38bd20f4f58e8025bd3ffc718cb7e40a4bde6396 Mon Sep 17 00:00:00 2001 From: Rick Reed Date: Fri, 16 Mar 2012 15:46:39 -0700 Subject: Demote rare debug slogan of message discarding to debug build --- erts/emulator/beam/bif.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 06a1230ca0..03c436210f 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1869,6 +1869,7 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { } else if (is_external_pid(to)) { dep = external_pid_dist_entry(to); if(dep == erts_this_dist_entry) { +#if DEBUG erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); erts_dsprintf(dsbufp, "Discarding message %T from %T to %T in an old " @@ -1879,6 +1880,7 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { external_pid_creation(to), erts_this_node->creation); erts_send_error_to_logger(p->group_leader, dsbufp); +#endif return 0; } return remote_send(p, dep, to, to, msg, suspend); @@ -1907,6 +1909,7 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { } else if (is_external_port(to) && (external_port_dist_entry(to) == erts_this_dist_entry)) { +#if DEBUG erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); erts_dsprintf(dsbufp, "Discarding message %T from %T to %T in an old " @@ -1917,6 +1920,7 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { external_port_creation(to), erts_this_node->creation); erts_send_error_to_logger(p->group_leader, dsbufp); +#endif return 0; } else if (is_internal_port(to)) { int ret_val; -- cgit v1.2.3 From e2c11e89563f0c11794c91193b29bce00ca9c740 Mon Sep 17 00:00:00 2001 From: Rick Reed Date: Tue, 22 May 2012 12:28:09 -0700 Subject: Add missing error string to syslog logging in epmd --- erts/epmd/src/epmd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 5d5c3a1c3c..5dd4a2cc2e 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -486,7 +486,11 @@ static void dbg_gen_printf(int onsyslog,int perr,int from_level, #ifdef HAVE_SYSLOG_H if (onsyslog) { - erts_vsnprintf(buf, DEBUG_BUFFER_SIZE, format, args); + int len; + len = erts_vsnprintf(buf, DEBUG_BUFFER_SIZE, format, args); + if (perr != 0 && len < sizeof(buf)) { + erts_snprintf(buf+len, sizeof(buf)-len, ": %s", strerror(perr)); + } syslog(LOG_ERR,"epmd: %s",buf); } #endif -- cgit v1.2.3 From 345af4a0c8d68b9369c3556fa6d911854c123d3f Mon Sep 17 00:00:00 2001 From: Rick Reed Date: Wed, 13 Feb 2013 08:48:18 -0800 Subject: Add run queue index to process dump info --- erts/doc/src/crash_dump.xml | 3 +++ erts/emulator/beam/break.c | 1 + 2 files changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index c59741f250..885b7c3bb4 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -246,6 +246,9 @@ Last scheduled in for | Current call The current function of the process. These fields will not always exist. + Run queue + The identifier of the scheduler run queue in which the process is + running. Spawned by The parent of the process, i.e. the process which executed or . diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 7d4f52ee23..08265b590d 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -256,6 +256,7 @@ print_process_info(int to, void *to_arg, Process *p) p->current[1], p->current[2]); } + erts_print(to, to_arg, "Run queue: %d\n", erts_get_runq_proc(p)->ix); erts_print(to, to_arg, "Spawned by: %T\n", p->parent); approx_started = (time_t) p->approx_started; -- cgit v1.2.3 From 5d5f9c1857029d7e8e1de141e29d20dd3de929be Mon Sep 17 00:00:00 2001 From: Rick Reed Date: Wed, 13 Feb 2013 08:49:20 -0800 Subject: Add thread index to allocator enomem dump slogan --- erts/doc/src/crash_dump.xml | 28 +++++++++++++++------------- erts/emulator/beam/erl_alloc.c | 4 ++-- 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index 885b7c3bb4..d3de29b876 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -85,20 +85,22 @@ operating system.

"<A>: Cannot allocate <N> - bytes of memory (of type "<T>")." - The system - has run out of memory. <A> is the allocator that failed - to allocate memory, <N> is the number of bytes that - <A> tried to allocate, and <T> is the memory block - type that the memory was needed for. The most common case is - that a process stores huge amounts of data. In this case - <T> is most often , , - , or . For more information on - allocators see - erts_alloc(3). + bytes of memory (of type "<T>", thread + <I>em>)." - The system has run out of memory. <A> + is the allocator that failed to allocate memory, <N> is the + number of bytes that <A> tried to allocate, <T> is the + memory block type that the memory was needed for, and <I> is the + thread identifier. The most common case is that a process stores huge + amounts of data. In this case <T> is most often + , , + , or . + For more information on allocators see + erts_alloc(3).
"<A>: Cannot reallocate <N> - bytes of memory (of type "<T>")." - Same as - above with the exception that memory was being reallocated - instead of being allocated when the system ran out of memory. + bytes of memory (of type "<T>", thread + <I>em>)." - Same as above with the exception that memory + was being reallocated instead of being allocated when the system ran + out of memory. "Unexpected op code N" - Error in compiled code, file damaged or error in the compiler. "Module Name undefined" "Function diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 05ac24e04d..90cd227fae 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1873,8 +1873,8 @@ erts_alc_fatal_error(int error, int func, ErtsAlcType_t n, ...) size = va_arg(argp, Uint); va_end(argp); erl_exit(1, - "%s: Cannot %s %lu bytes of memory (of type \"%s\").\n", - allctr_str, op, size, t_str); + "%s: Cannot %s %lu bytes of memory (of type \"%s\", thread %d).\n", + allctr_str, op, size, t_str, ERTS_ALC_GET_THR_IX()); break; } case ERTS_ALC_E_NOALLCTR: -- cgit v1.2.3 From a2820c8db97c05d4fc11e04048c123e1da24da88 Mon Sep 17 00:00:00 2001 From: Rick Reed Date: Thu, 13 Dec 2012 10:13:53 -0800 Subject: Use closefrom(2) when available in child_setup --- erts/configure.in | 2 +- erts/emulator/sys/unix/erl_child_setup.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 074882532f..6d5eca8966 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1151,7 +1151,7 @@ fi AC_SUBST(ERTS_BUILD_SMP_EMU) -AC_CHECK_FUNCS([posix_fadvise]) +AC_CHECK_FUNCS([posix_fadvise closefrom]) AC_CHECK_HEADERS([linux/falloc.h]) dnl * Old glibcs have broken fallocate64(). Make sure not to use it. AC_CACHE_CHECK([whether fallocate() works],i_cv_fallocate_works,[ diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 7c6e4a2f37..99d2b2a739 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -89,8 +89,12 @@ main(int argc, char *argv[]) if (sscanf(argv[CS_ARGV_FD_CR_IX], "%d:%d", &from, &to) != 2) return 1; +#if defined(HAVE_CLOSEFROM) + closefrom(from); +#else for (i = from; i <= to; i++) (void) close(i); +#endif if (!(argv[CS_ARGV_WD_IX][0] == '.' && argv[CS_ARGV_WD_IX][1] == '\0') && chdir(argv[CS_ARGV_WD_IX]) < 0) -- cgit v1.2.3 From 8b4b4a026041bfbe5a8b4e2535c5fd0804b18103 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Sun, 2 Mar 2014 16:48:06 -0500 Subject: enable enif_send to work from a dirty scheduler --- erts/emulator/beam/erl_alloc.h | 2 +- erts/emulator/test/nif_SUITE.erl | 23 +++++++++++++++++-- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 32 +++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index 942eaa47d0..d3109b9432 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -492,7 +492,7 @@ static TYPE * \ NAME##_alloc(void) \ { \ ErtsSchedulerData *esdp = erts_get_scheduler_data(); \ - if (!esdp) \ + if (!esdp || ERTS_SCHEDULER_IS_DIRTY(esdp)) \ return NULL; \ return (TYPE *) erts_sspa_alloc(sspa_data_##NAME##__, \ (int) esdp->no - 1); \ diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index a854d3f05b..b2da6f58af 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -37,7 +37,7 @@ threading/1, send/1, send2/1, send3/1, send_threaded/1, neg/1, is_checks/1, get_length/1, make_atom/1, make_string/1, reverse_list_test/1, - otp_9668/1, consume_timeslice/1, dirty_nif/1 + otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1 ]). -export([many_args_100/100]). @@ -64,7 +64,7 @@ all() -> resource_takeover, threading, send, send2, send3, send_threaded, neg, is_checks, get_length, make_atom, make_string,reverse_list_test, - otp_9668, consume_timeslice, dirty_nif + otp_9668, consume_timeslice, dirty_nif, dirty_nif_send ]. groups() -> @@ -1538,6 +1538,24 @@ dirty_nif(Config) when is_list(Config) -> {skipped,"No dirty scheduler support"} end. +dirty_nif_send(Config) when is_list(Config) -> + try erlang:system_info(dirty_cpu_schedulers) of + N when is_integer(N) -> + ensure_lib_loaded(Config), + Parent = self(), + Pid = spawn_link(fun() -> + Self = self(), + {ok, Self} = receive_any(), + Parent ! {ok, Self} + end), + {ok, Pid} = send_from_dirty_nif(Pid), + {ok, Pid} = receive_any(), + ok + catch + error:badarg -> + {skipped,"No dirty scheduler support"} + end. + next_msg(_Pid) -> receive M -> M @@ -1668,6 +1686,7 @@ type_sizes() -> ?nif_stub. otp_9668_nif(_) -> ?nif_stub. consume_timeslice_nif(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. +send_from_dirty_nif(_) -> ?nif_stub. %% maps is_map_nif(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 160f4843ad..955dc64189 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1533,6 +1533,37 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM return dirty_nif(env, argc, argv); } } + +static ERL_NIF_TERM dirty_sender(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM result; + ErlNifPid pid; + ErlNifEnv* menv; + int res; + + enif_get_local_pid(env, argv[0], &pid); + result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid)); + menv = enif_alloc_env(); + res = enif_send(env, &pid, menv, result); + enif_free_env(menv); + if (!res) + /* Note the next line will crash, since dirty nifs can't return exceptions. + * This is intentional, since enif_send should not fail if the test succeeds. + */ + return enif_schedule_dirty_nif_finalizer(env, enif_make_badarg(env), enif_dirty_nif_finalizer); + else + return enif_schedule_dirty_nif_finalizer(env, result, enif_dirty_nif_finalizer); +} + +static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM result; + ErlNifPid pid; + + if (!enif_get_local_pid(env, argv[0], &pid)) + return enif_make_badarg(env); + return enif_schedule_dirty_nif(env, ERL_NIF_DIRTY_JOB_CPU_BOUND, dirty_sender, argc, argv); +} #endif static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) @@ -1713,6 +1744,7 @@ static ErlNifFunc nif_funcs[] = {"consume_timeslice_nif", 2, consume_timeslice_nif}, #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT {"call_dirty_nif", 3, call_dirty_nif}, + {"send_from_dirty_nif", 1, send_from_dirty_nif}, #endif {"is_map_nif", 1, is_map_nif}, {"get_map_size_nif", 1, get_map_size_nif}, -- cgit v1.2.3 From 8fbe76d64e7d55eb41943484602b928658313a65 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 11 Mar 2014 12:23:06 +0100 Subject: erts: Document external format for maps (MAP_EXT) --- erts/doc/src/erl_ext_dist.xml | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml index f91ed78122..9a53f3f829 100644 --- a/erts/doc/src/erl_ext_dist.xml +++ b/erts/doc/src/erl_ext_dist.xml @@ -5,7 +5,7 @@
2007 - 2013 + 2014 Ericsson AB, All Rights Reserved @@ -572,6 +572,36 @@

+
+ + MAP_EXT + + + + 1 + 4 + N + M + + + 116 + Arity + Keys + Values + +
+

+ MAP_EXT encodes a map. The Arity field is an unsigned + 4 byte integer in big endian format that determines the number of + key-value pairs in the map. All key terms follow in the Keys + section and then all value terms in the Values section. Keys + and values are paired according to order; first key with first value + and so on. Duplicate keys are not allowed within the same + map. +

+

Since: OTP 17.0

+
+
NIL_EXT -- cgit v1.2.3 From 785cb2081ff2c84453506380c97aced147df2b73 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 11 Mar 2014 09:41:19 +0100 Subject: erts: Clarify escript's encoding of the I/O-server A note has been added that clarifies that the encoding of the I/O-server has to be set explicitly, irrespective of any encoding comment. --- erts/doc/src/escript.xml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml index d2b09d4515..1abbdb2180 100644 --- a/erts/doc/src/escript.xml +++ b/erts/doc/src/escript.xml @@ -4,7 +4,7 @@
- 20072013 + 20072014 Ericsson AB. All Rights Reserved. @@ -92,6 +92,18 @@ $ escript factorial 5 marker="stdlib:epp#encoding">encoding it can be located on the second line.

+

+ The encoding specified by the above mentioned comment + applies to the script itself. The encoding of the + I/O-server, however, has to be set explicitly like this: +io:setopts([{encoding, unicode}])

+

The default encoding of the I/O-server for standard_io + is latin1 + since the script runs in a non-interactive terminal + (see + Using Unicode in Erlang). +

+

On the third line (or second line depending on the presence of the Emacs directive), it is possible to give arguments to the emulator, such as

@@ -141,8 +153,9 @@ halt(1). -include_lib("kernel/include/file.hrl").

to include the record definitions for the records used by the file:read_link_info/1 function. You can also select - encoding here, but if there is a valid encoding comment on - the second line it takes precedence.

+ encoding by including a encoding comment here, but if there + is a valid encoding comment on the second line it takes + precedence.

The script will be checked for syntactic and semantic correctness before being run. If there are warnings (such as @@ -163,7 +176,7 @@ halt(1). If much of the execution takes place in interpreted code it may be worthwhile to compile it, even though the compilation itself will take a little while. It is also possible to supply - native instead of compile, this will compile the script + native instead of compile, this will compile the script using the native flag, again depending on the characteristics of the escript this could or could not be worth while.

@@ -239,7 +252,7 @@ factorial 5 = 120 can either be returned as a binary or written to file.

As an example of how the function can be used, we create an - interpreted escript which uses emu_args to set some emulator + interpreted escript which uses emu_args to set some emulator flag. In this case it happens to disable the smp_support. We do also extract the different sections from the newly created script:

-- cgit v1.2.3 From effb1c0a593e71664dec5b06da72518e74254c1b Mon Sep 17 00:00:00 2001 From: Michal Ptaszek Date: Wed, 12 Mar 2014 16:22:08 +0100 Subject: Pass full strings to DTrace probes Whenever string is passed as an argument to a DTrace probe, its length should be properly computed. Until now in order to get length of the input buffer size_of(char *) was used - which evalutes to 4 or 8 (depending on the architecture). To get a proper length, size_of(DTRACE_CHARBUF_NAME(buffer_name)) should be used. --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/copy.c | 3 +- erts/emulator/beam/dist.c | 64 +++++++++++++++++++++++--------------- erts/emulator/beam/erl_async.c | 6 ++-- erts/emulator/beam/erl_bif_port.c | 2 +- erts/emulator/beam/erl_port_task.c | 4 +-- erts/emulator/beam/erl_process.c | 2 +- erts/emulator/beam/io.c | 14 ++++----- 8 files changed, 57 insertions(+), 40 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 78ab6fa30f..b8688608c2 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1270,7 +1270,7 @@ void process_main(void) (Eterm)fptr[1], (Uint)fptr[2], NULL, fun_buf); } else { - erts_snprintf(fun_buf, sizeof(fun_buf), + erts_snprintf(fun_buf, sizeof(DTRACE_CHARBUF_NAME(fun_buf)), "", next); } } diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 23c0fca6aa..ff2ce975ea 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -47,7 +47,8 @@ copy_object(Eterm obj, Process* to) if (DTRACE_ENABLED(copy_object)) { DTRACE_CHARBUF(proc_name, 64); - erts_snprintf(proc_name, sizeof(proc_name), "%T", to->common.id); + erts_snprintf(proc_name, sizeof(DTRACE_CHARBUF_NAME(proc_name)), + "%T", to->common.id); DTRACE2(copy_object, proc_name, size); } #endif diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 6ecf3f0722..ec07ddcd9c 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -851,9 +851,12 @@ erts_dsig_send_msg(ErtsDSigData *dsdp, Eterm remote, Eterm message) #ifdef USE_VM_PROBES *node_name = *sender_name = *receiver_name = '\0'; if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) { - erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname); - erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->common.id); - erts_snprintf(receiver_name, sizeof(receiver_name), "%T", remote); + erts_snprintf(node_name, sizeof(DTRACE_CHARBUF_NAME(node_name)), + "%T", dsdp->dep->sysname); + erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), + "%T", sender->common.id); + erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), + "%T", remote); msize = size_object(message); if (token != NIL && token != am_have_dt_utag) { tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); @@ -908,9 +911,11 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message) #ifdef USE_VM_PROBES *node_name = *sender_name = *receiver_name = '\0'; if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) { - erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname); - erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->common.id); - erts_snprintf(receiver_name, sizeof(receiver_name), + erts_snprintf(node_name, sizeof(DTRACE_CHARBUF_NAME(node_name)), + "%T", dsdp->dep->sysname); + erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), + "%T", sender->common.id); + erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), "{%T,%s}", remote_name, node_name); msize = size_object(message); if (token != NIL && token != am_have_dt_utag) { @@ -971,11 +976,14 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote, #ifdef USE_VM_PROBES *node_name = *sender_name = *remote_name = '\0'; if (DTRACE_ENABLED(process_exit_signal_remote)) { - erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname); - erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->common.id); - erts_snprintf(remote_name, sizeof(remote_name), + erts_snprintf(node_name, sizeof(DTRACE_CHARBUF_NAME(node_name)), + "%T", dsdp->dep->sysname); + erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), + "%T", sender->common.id); + erts_snprintf(remote_name, sizeof(DTRACE_CHARBUF_NAME(remote_name)), "{%T,%s}", remote, node_name); - erts_snprintf(reason_str, sizeof(reason), "%T", reason); + erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)), + "%T", reason); if (token != NIL && token != am_have_dt_utag) { tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); @@ -1797,8 +1805,9 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy) DTRACE_CHARBUF(port_str, 64); DTRACE_CHARBUF(remote_str, 64); - erts_snprintf(port_str, sizeof(port_str), "%T", cid); - erts_snprintf(remote_str, sizeof(remote_str), + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), + "%T", cid); + erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)), "%T", dep->sysname); DTRACE3(dist_port_not_busy, erts_this_node_sysname, port_str, remote_str); @@ -1855,9 +1864,11 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy) DTRACE_CHARBUF(remote_str, 64); DTRACE_CHARBUF(pid_str, 16); - erts_snprintf(port_str, sizeof(port_str), "%T", cid); - erts_snprintf(remote_str, sizeof(remote_str), "%T", dep->sysname); - erts_snprintf(pid_str, sizeof(pid_str), "%T", c_p->common.id); + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", cid); + erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)), + "%T", dep->sysname); + erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), + "%T", c_p->common.id); DTRACE4(dist_port_busy, erts_this_node_sysname, port_str, remote_str, pid_str); } @@ -1890,8 +1901,9 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf) DTRACE_CHARBUF(port_str, 64); DTRACE_CHARBUF(remote_str, 64); - erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id); - erts_snprintf(remote_str, sizeof(remote_str), + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), + "%T", prt->common.id); + erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)), "%T", prt->dist_entry->sysname); DTRACE4(dist_output, erts_this_node_sysname, port_str, remote_str, size); @@ -1944,8 +1956,9 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf) DTRACE_CHARBUF(port_str, 64); DTRACE_CHARBUF(remote_str, 64); - erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id); - erts_snprintf(remote_str, sizeof(remote_str), + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), + "%T", prt->common.id); + erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)), "%T", prt->dist_entry->sysname); DTRACE4(dist_outputv, erts_this_node_sysname, port_str, remote_str, size); @@ -2280,8 +2293,9 @@ erts_dist_port_not_busy(Port *prt) DTRACE_CHARBUF(port_str, 64); DTRACE_CHARBUF(remote_str, 64); - erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id); - erts_snprintf(remote_str, sizeof(remote_str), + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), + "%T", prt->common.id); + erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)), "%T", prt->dist_entry->sysname); DTRACE3(dist_port_not_busy, erts_this_node_sysname, port_str, remote_str); @@ -3246,10 +3260,10 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas DTRACE_CHARBUF(type_str, 12); DTRACE_CHARBUF(reason_str, 64); - erts_snprintf(what_str, sizeof(what_str), "%T", what); - erts_snprintf(node_str, sizeof(node_str), "%T", node); - erts_snprintf(type_str, sizeof(type_str), "%T", type); - erts_snprintf(reason_str, sizeof(reason_str), "%T", reason); + erts_snprintf(what_str, sizeof(DTRACE_CHARBUF_NAME(what_str)), "%T", what); + erts_snprintf(node_str, sizeof(DTRACE_CHARBUF_NAME(node_str)), "%T", node); + erts_snprintf(type_str, sizeof(DTRACE_CHARBUF_NAME(type_str)), "%T", type); + erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)), "%T", reason); DTRACE5(dist_monitor, erts_this_node_sysname, what_str, node_str, type_str, reason_str); } diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index e6d72f569b..aaeab0d56c 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -279,7 +279,8 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q) if (DTRACE_ENABLED(aio_pool_add)) { DTRACE_CHARBUF(port_str, 16); - erts_snprintf(port_str, sizeof(port_str), "%T", a->port); + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), + "%T", a->port); /* DTRACE TODO: Get the queue length from erts_thr_q_enqueue() ? */ len = -1; DTRACE2(aio_pool_add, port_str, len); @@ -314,7 +315,8 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, if (DTRACE_ENABLED(aio_pool_get)) { DTRACE_CHARBUF(port_str, 16); - erts_snprintf(port_str, sizeof(port_str), "%T", a->port); + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), + "%T", a->port); /* DTRACE TODO: Get the length from erts_thr_q_dequeue() ? */ len = -1; DTRACE2(aio_pool_get, port_str, len); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 3cd53ef65d..c014d81bb6 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -917,7 +917,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); dtrace_proc_str(p, process_str); - erts_snprintf(port_str, sizeof(port_str), "%T", port->common.id); + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", port->common.id); DTRACE3(port_open, process_str, name_buf, port_str); } #endif diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 547a42beb2..d6c0c9e78e 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -2029,9 +2029,9 @@ begin_port_cleanup(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p) DTRACE_CHARBUF(pid_str, 16); ErtsProcList* plp2 = plp; - erts_snprintf(port_str, sizeof(port_str), "%T", pp->common.id); + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", pp->common.id); while (plp2 != NULL) { - erts_snprintf(pid_str, sizeof(pid_str), "%T", plp2->pid); + erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->pid); DTRACE2(process_port_unblocked, pid_str, port_str); } } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 1efd070afd..dbe54a25c3 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -8349,7 +8349,7 @@ send_exit_signal(Process *c_p, /* current process if and only dtrace_pid_str(from, sender_str); dtrace_proc_str(rp, receiver_str); - erts_snprintf(reason_buf, sizeof(reason_buf) - 1, "%T", reason); + erts_snprintf(reason_buf, sizeof(DTRACE_CHARBUF_NAME(reason_buf)) - 1, "%T", reason); DTRACE3(process_exit_signal, sender_str, receiver_str, reason_buf); } #endif diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 9076bbe73c..920701c086 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -2468,7 +2468,7 @@ set_port_connected(int bang_op, DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE); dtrace_pid_str(connect, process_str); - erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id); + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", prt->common.id); dtrace_proc_str(rp, newprocess_str); DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str); } @@ -3581,9 +3581,9 @@ erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed) DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); DTRACE_CHARBUF(rreason_str, 64); - erts_snprintf(from_str, sizeof(from_str), "%T", from); + erts_snprintf(from_str, sizeof(DTRACE_CHARBUF_NAME(from_str)), "%T", from); dtrace_port_str(p, port_str); - erts_snprintf(rreason_str, sizeof(rreason_str), "%T", rreason); + erts_snprintf(rreason_str, sizeof(DTRACE_CHARBUF_NAME(rreason_str)), "%T", rreason); DTRACE4(port_exit, from_str, port_str, p->name, rreason_str); } #endif @@ -4650,7 +4650,7 @@ set_busy_port(ErlDrvPort dprt, int on) #ifdef USE_VM_PROBES if (DTRACE_ENABLED(port_busy)) { - erts_snprintf(port_str, sizeof(port_str), + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", prt->common.id); DTRACE1(port_busy, port_str); } @@ -4663,7 +4663,7 @@ set_busy_port(ErlDrvPort dprt, int on) #ifdef USE_VM_PROBES if (DTRACE_ENABLED(port_not_busy)) { - erts_snprintf(port_str, sizeof(port_str), + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", prt->common.id); DTRACE1(port_not_busy, port_str); } @@ -4715,9 +4715,9 @@ erts_port_resume_procs(Port *prt) DTRACE_CHARBUF(pid_str, 16); ErtsProcList* plp2 = plp; - erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id); + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", prt->common.id); while (plp2 != NULL) { - erts_snprintf(pid_str, sizeof(pid_str), "%T", plp2->pid); + erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->pid); DTRACE2(process_port_unblocked, pid_str, port_str); } } -- cgit v1.2.3 From fe00fbc345a58ad4313ba34ec303784353ec6039 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 13 Mar 2014 14:30:08 +0100 Subject: erts: Fix file driver to handle long paths on windows --- erts/emulator/drivers/win32/win_efile.c | 752 +++++++++++++++++++++++++------- 1 file changed, 603 insertions(+), 149 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index d693d7d593..e4a7dac1df 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-2013. All Rights Reserved. + * Copyright Ericsson AB 1997-2014. All Rights Reserved. * * The 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,12 +29,27 @@ #include #include "erl_efile.h" +// 1 = file name ops +// 2 = file descr ops +// 4 = errors +// 8 = path name conversion +#define SVERK_TRACE_MASK 0 + +#if !SVERK_TRACE_MASK +# define SVERK_TRACE(M,S) +# define SVERK_TRACE1(M,FMT,A) +# define SVERK_TRACE2(M,FMT,A,B) +#else +# define SVERK_TRACE(M,S) do { if ((M)&SVERK_TRACE_MASK) fwprintf(stderr, L"SVERK TRACE %d: %s\r\n", __LINE__, (WCHAR*)(S)); }while(0) +# define SVERK_TRACE1(M,FMT,A) do { if ((M)&SVERK_TRACE_MASK) fwprintf(stderr, L"SVERK TRACE %d: " L##FMT L"\r\n", __LINE__, (A)); }while(0) +# define SVERK_TRACE2(M,FMT,A,B) do { if ((M)&SVERK_TRACE_MASK) fwprintf(stderr, L"SVERK TRACE %d: " L##FMT L"\r\n", __LINE__, (A), (B)); }while(0) +#endif + /* * Microsoft-specific function to map a WIN32 error code to a Posix errno. */ #define ISSLASH(a) ((a) == L'\\' || (a) == L'/') - #define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR) #define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG) @@ -69,10 +84,92 @@ static int check_error(int result, Efile_error* errInfo); static int set_error(Efile_error* errInfo); +static int set_os_errno(Efile_error* errInfo, DWORD os_errno); static int is_root_unc_name(const WCHAR *path); static int extract_root(WCHAR *name); static unsigned short dos_to_posix_mode(int attr, const WCHAR *name); + +struct wpath_tmp_buffer { + struct wpath_tmp_buffer* next; + WCHAR buffer[1]; +}; + +typedef struct { + Efile_error* errInfo; + struct wpath_tmp_buffer* buf_list; +}Efile_call_state; + +static void call_state_init(Efile_call_state* state, Efile_error* errInfo) +{ + state->errInfo = errInfo; + state->buf_list = NULL; +} +static WCHAR* wpath_tmp_alloc(Efile_call_state* state, size_t len) +{ + size_t sz = offsetof(struct wpath_tmp_buffer, buffer) + + (len+1)*sizeof(WCHAR); + struct wpath_tmp_buffer* p = driver_alloc(sz); + p->next = state->buf_list; + state->buf_list = p; + return p->buffer; +} +static void call_state_free(Efile_call_state* state) +{ + while(state->buf_list) { + struct wpath_tmp_buffer* next = state->buf_list->next; + driver_free(state->buf_list); + state->buf_list = next; + } +} +static WCHAR* get_cwd_wpath_tmp(Efile_call_state* state) +{ + WCHAR dummy; + DWORD size = GetCurrentDirectoryW(0, &dummy); + WCHAR* ret = NULL; + + if (size) { + ret = wpath_tmp_alloc(state, size); + if (!GetCurrentDirectoryW(size, ret)) { + ret = NULL; + } + } + return ret; +} +static WCHAR* get_full_wpath_tmp(Efile_call_state* state, + const WCHAR* file, + WCHAR** file_part, + DWORD extra) +{ + WCHAR dummy; + DWORD size = GetFullPathNameW(file, 0, &dummy, NULL); + WCHAR* ret = NULL; + + if (size) { + int ok; + ret = wpath_tmp_alloc(state, size + extra); + if (file_part) { + ok = (GetFullPathNameW(file, size, ret, file_part) != 0); + } + else { + ok = (_wfullpath(ret, file, size) != NULL); + } + if (!ok) { + ret = NULL; + } + } + return ret; +} + +static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max); +static int do_rmdir(Efile_call_state*, char* name); +static int do_rename(Efile_call_state*, char* src, char* dst); +static int do_readdir(Efile_call_state*, char* name, EFILE_DIR_HANDLE*, char* buffer, size_t *size); +static int do_fileinfo(Efile_call_state*, Efile_info*, char* orig_name, int info_for_link); +static char* do_readlink(Efile_call_state*, char* name, char* buffer, size_t size); +static int do_altname(Efile_call_state*, char* orig_name, char* buffer, size_t size); + + static int errno_map(DWORD last_error) { switch (last_error) { @@ -176,11 +273,23 @@ check_error(int result, Efile_error* errInfo) if (result < 0) { errInfo->posix_errno = errno; errInfo->os_errno = GetLastError(); + SVERK_TRACE2(4, "ERROR os_error=%d errno=%d @@@@@@@@@@@@@@@@@@@@@@@@@@@@", + errInfo->os_errno, errInfo->posix_errno); return 0; } return 1; } +static void +save_last_error(Efile_error* errInfo) +{ + errInfo->posix_errno = errno; + errInfo->os_errno = GetLastError(); + SVERK_TRACE2(4, "ERROR os_error=%d errno=%d $$$$$$$$$$$$$$$$$$$$$$$$$$$$$", + errInfo->os_errno, errInfo->posix_errno); +} + + /* * Fills the provided error information structure with information * with the error code given by GetLastError() and its corresponding @@ -192,10 +301,21 @@ check_error(int result, Efile_error* errInfo) static int set_error(Efile_error* errInfo) { - errInfo->posix_errno = errno_map(errInfo->os_errno = GetLastError()); + set_os_errno(errInfo, GetLastError()); return 0; } +static int +set_os_errno(Efile_error* errInfo, DWORD os_errno) +{ + errInfo->os_errno = os_errno; + errInfo->posix_errno = errno_map(os_errno); + SVERK_TRACE2(4, "ERROR os_error=%d errno=%d ############################", + errInfo->os_errno, errInfo->posix_errno); + return 0; +} + + /* * A writev with Unix semantics, but with Windows arguments */ @@ -221,21 +341,143 @@ win_writev(Efile_error* errInfo, } +/* Check '*pathp' and convert it if needed to something that windows will accept. + * Typically use UNC path with \\?\ prefix if absolute path is longer than 260. + */ +static void ensure_wpath(Efile_call_state* state, WCHAR** pathp) +{ + ensure_wpath_max(state, pathp, MAX_PATH); +} + +static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max) +{ + WCHAR* path = *pathp; + WCHAR* p; + size_t len = wcslen(path); + int unc_fixup = 0; + + if (path[0] == 0) { + SVERK_TRACE(8,"Let empty path pass through"); + return; + } + + if (path[1] == L':' && ISSLASH(path[2])) { /* absolute path */ + if (len >= max) { + WCHAR *src, *dst; + + *pathp = wpath_tmp_alloc(state, 4+len+1); + dst = *pathp; + wcscpy(dst, L"\\\\?\\"); + for (src=path,dst+=4; *src; src++,dst++) + *dst = (*src == L'/') ? L'\\' : *src; + *dst = 0; + unc_fixup = 1; + } + } + else if (!(ISSLASH(path[0]) && ISSLASH(path[1]))) { /* relative path */ + DWORD cwdLen = GetCurrentDirectoryW(0, NULL); + DWORD absLen = cwdLen + 1 + len; + if (absLen >= max) { + WCHAR *cwd; + + cwd = wpath_tmp_alloc(state, 4+absLen); + wcscpy(cwd, L"\\\\?\\"); + cwdLen = GetCurrentDirectoryW(cwdLen, cwd+4); + if (wcsncmp(cwd+4, L"\\\\?\\", 4) == 0) { + cwd += 4; + cwdLen -= 4; + } + p = cwd + 4 + cwdLen; + if (!ISSLASH(p[-1])) + *p++ = L'\\'; + wcscpy(p, path); + + for (p=cwd; *p; p++) + if (*p == L'/') + *p = L'\\'; + *pathp = cwd; + unc_fixup = 1; + } + } + + if (unc_fixup) { + WCHAR* endp; + + SVERK_TRACE1(8,"IN: %s", path); + + p = *pathp; + len = wcslen(p); + endp = p + len; + if (len > 4) { + p += 4; + while (*p) { + if (p[0] == L'\\' && p[1] == L'.') { + if (p[2] == L'\\' || !p[2]) { /* single dot */ + wmemmove(p, p+2, (&endp[1] - &p[2])); + endp -= 2; + } + else if (p[2] == L'.' && (p[3] == L'\\' || !p[3])) { /* double dot */ + WCHAR* r; + for (r=p-1; *r == L'\\'; --r) + /*skip redundant slashes*/; + for (; *r != L'\\'; --r) + /*find start of prev directory*/; + if (r < *pathp + 6) + break; + wmemmove(r, p+3, (&endp[1] - &p[3])); + p = r; + } + else p += 3; + } + else ++p; + } + } + SVERK_TRACE1(8,"OUT: %s", *pathp); + } +} int efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to create. */ { - return check_error(_wmkdir((WCHAR *) name), errInfo); + Efile_call_state state; + WCHAR* wname = (WCHAR*)name; + int ret; + + SVERK_TRACE(1, name); + call_state_init(&state, errInfo); + ensure_wpath_max(&state, &wname, 248); /* Yes, 248 limit for normal paths */ + + ret = (int) CreateDirectoryW(wname, NULL); + if (!ret) + set_error(errInfo); + + call_state_free(&state); + return ret; } int efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to delete. */ +{ + Efile_call_state state; + int ret; + + SVERK_TRACE(1, name); + call_state_init(&state, errInfo); + ret = do_rmdir(&state, name); + call_state_free(&state); + return ret; +} + +static int do_rmdir(Efile_call_state* state, char* name) { OSVERSIONINFO os; DWORD attr; WCHAR *wname = (WCHAR *) name; + WCHAR *buffer = NULL; + + ensure_wpath(state, &wname); if (RemoveDirectoryW(wname) != FALSE) { return 1; @@ -265,10 +507,9 @@ efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ if (os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { HANDLE handle; WIN32_FIND_DATAW data; - WCHAR buffer[2*MAX_PATH]; - int len; + int len = wcslen(wname); - len = wcslen(wname); + buffer = wpath_tmp_alloc(state, len + 4); wcscpy(buffer, wname); if (buffer[0] && buffer[len-1] != L'\\' && buffer[len-1] != L'/') { wcscat(buffer, L"\\"); @@ -306,16 +547,30 @@ efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ } end: - return check_error(-1, errInfo); + save_last_error(state->errInfo); + return 0; } int efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of file to delete. */ +{ + Efile_call_state state; + int ret; + SVERK_TRACE(1, name); + call_state_init(&state, errInfo); + ret = do_delete_file(&state, name); + call_state_free(&state); + return ret; +} + +static int do_delete_file(Efile_call_state* state, char* name) { DWORD attr; WCHAR *wname = (WCHAR *) name; + ensure_wpath(state, &wname); + if (DeleteFileW(wname) != FALSE) { return 1; } @@ -354,7 +609,7 @@ efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ errno = EACCES; } - return check_error(-1, errInfo); + return check_error(-1, state->errInfo); } /* @@ -388,14 +643,29 @@ efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ */ int -efile_rename(Efile_error* errInfo, /* Where to return error codes. */ - char* src, /* Original name. */ - char* dst) /* New name. */ +efile_rename(Efile_error* errInfo, char* src, char* dst) +{ + Efile_call_state state; + int ret; + SVERK_TRACE(1, src); + call_state_init(&state, errInfo); + ret = do_rename(&state, src, dst); + call_state_free(&state); + return ret; +} + +static int +do_rename(Efile_call_state* state, + char* src, /* Original name. */ + char* dst) /* New name. */ { DWORD srcAttr, dstAttr; WCHAR *wsrc = (WCHAR *) src; WCHAR *wdst = (WCHAR *) dst; - + + ensure_wpath(state, &wsrc); + ensure_wpath(state, &wdst); + if (MoveFileW(wsrc, wdst) != FALSE) { return 1; } @@ -412,23 +682,27 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */ if (errno == EBADF) { errno = EACCES; - return check_error(-1, errInfo); + return check_error(-1, state->errInfo); } if (errno == EACCES) { decode: if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) { - WCHAR srcPath[MAX_PATH], dstPath[MAX_PATH]; + WCHAR *srcPath, *dstPath; WCHAR *srcRest, *dstRest; int size; - size = GetFullPathNameW(wsrc, MAX_PATH, srcPath, &srcRest); - if ((size == 0) || (size > MAX_PATH)) { - return check_error(-1, errInfo); + srcPath = get_full_wpath_tmp(state, wsrc, &srcRest, 0); + if (!srcPath) { + save_last_error(state->errInfo); + return 0; } - size = GetFullPathNameW(wdst, MAX_PATH, dstPath, &dstRest); - if ((size == 0) || (size > MAX_PATH)) { - return check_error(-1, errInfo); + + dstPath = get_full_wpath_tmp(state, wdst, &dstRest, 0); + if (!dstPath) { + save_last_error(state->errInfo); + return 0; } + if (srcRest == NULL) { srcRest = srcPath + wcslen(srcPath); } @@ -533,14 +807,16 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */ * put temp file back to old name. */ - WCHAR tempName[MAX_PATH]; - int result, size; + WCHAR *tempName; + int result; WCHAR *rest; - size = GetFullPathNameW(wdst, MAX_PATH, tempName, &rest); - if ((size == 0) || (size > MAX_PATH) || (rest == NULL)) { - return check_error(-1, errInfo); + tempName = get_full_wpath_tmp(state, wdst, &rest, 14); + if (!tempName || !rest) { + save_last_error(state->errInfo); + return 0; } + *rest = L'\0'; result = -1; if (GetTempFileNameW(tempName, L"erlr", 0, tempName) != 0) { @@ -573,7 +849,6 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */ /* * Decode the EACCES to a more meaningful error. */ - goto decode; } } @@ -581,17 +856,30 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */ } } } - return check_error(-1, errInfo); + return check_error(-1, state->errInfo); } int efile_chdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to make current. */ { - int success = check_error(_wchdir((WCHAR *) name), errInfo); - if (!success && errInfo->posix_errno == EINVAL) - /* POSIXification of errno */ - errInfo->posix_errno = ENOENT; + Efile_call_state state; + WCHAR* wname = (WCHAR*)name; + int success; + SVERK_TRACE(1, name); + + call_state_init(&state, errInfo); + ensure_wpath(&state, &wname); + success = (int) SetCurrentDirectoryW(wname); + if (!success) { + set_error(state.errInfo); + if (state.errInfo->posix_errno == EINVAL) { + /* POSIXification of errno */ + errInfo->posix_errno = ENOENT; + } + } + + call_state_free(&state); return success; } @@ -603,28 +891,45 @@ efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */ { WCHAR *wbuffer = (WCHAR *) buffer; size_t wbuffer_size = size / 2; - if (_wgetdcwd(drive, wbuffer, wbuffer_size) == NULL) + SVERK_TRACE(1, L"#getdcwd#"); + if (_wgetdcwd(drive, wbuffer, wbuffer_size) == NULL) { return check_error(-1, errInfo); + } + SVERK_TRACE1(8, "getdcwd OS=%s", wbuffer); + if (wcsncmp(wbuffer, L"\\\\?\\", 4) == 0) { + wmemmove(wbuffer, wbuffer+4, wcslen(wbuffer+4)+1); + } for ( ; *wbuffer; wbuffer++) if (*wbuffer == L'\\') *wbuffer = L'/'; + SVERK_TRACE1(8, "getdcwd ERLANG=%s", (WCHAR*)buffer); return 1; } int -efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ - char* name, /* Name of directory to list */ - EFILE_DIR_HANDLE* dir_handle, /* Handle of opened directory or NULL */ - char* buffer, /* Buffer to put one filename in */ - size_t *size) /* in-out size of buffer/size of filename excluding zero - termination in bytes*/ +efile_readdir(Efile_error* errInfo, char* name, EFILE_DIR_HANDLE* dir_handle, + char* buffer, size_t *size) +{ + Efile_call_state state; + int ret; + SVERK_TRACE(dir_handle?2:1, name); + call_state_init(&state, errInfo); + ret = do_readdir(&state, name, dir_handle, buffer, size); + call_state_free(&state); + return ret; +} + +static int do_readdir(Efile_call_state* state, + char* name, /* Name of directory to list */ + EFILE_DIR_HANDLE* dir_handle, /* Handle of opened directory or NULL */ + char* buffer, /* Buffer to put one filename in */ + size_t *size) /* in-out size of buffer/size of filename excluding zero + termination in bytes*/ { HANDLE dir; /* Handle to directory. */ - WCHAR wildcard[MAX_PATH]; /* Wildcard to search for. */ WIN32_FIND_DATAW findData; /* Data found by FindFirstFile() or FindNext(). */ /* Alignment is not honored, this works on x86 because of alignment fixup by processor. Not perfect, but faster than alinging by hand (really) */ - WCHAR *wname = (WCHAR *) name; WCHAR *wbuffer = (WCHAR *) buffer; /* @@ -632,13 +937,15 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ */ if (*dir_handle == NULL) { - int length = wcslen(wname); + WCHAR *wname = (WCHAR *) name; + WCHAR* wildcard; + int length; WCHAR* s; - if (length+3 >= MAX_PATH) { - errno = ENAMETOOLONG; - return check_error(-1, errInfo); - } + ensure_wpath(state, &wname); + length = wcslen(wname); + + wildcard = wpath_tmp_alloc(state, length+3); wcscpy(wildcard, wname); s = wildcard+length-1; @@ -648,8 +955,10 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ *++s = L'\0'; DEBUGF(("Reading %ws\n", wildcard)); dir = FindFirstFileW(wildcard, &findData); - if (dir == INVALID_HANDLE_VALUE) - return set_error(errInfo); + if (dir == INVALID_HANDLE_VALUE) { + set_error(state->errInfo); + return 0; + } *dir_handle = (EFILE_DIR_HANDLE) dir; if (!IS_DOT_OR_DOTDOT(findData.cFileName)) { @@ -659,7 +968,6 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ } } - /* * Retrieve the name of the next file using the directory handle. */ @@ -676,24 +984,36 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ } if (GetLastError() == ERROR_NO_MORE_FILES) { - FindClose(dir); - errInfo->posix_errno = errInfo->os_errno = 0; - return 0; + state->errInfo->posix_errno = state->errInfo->os_errno = 0; + } + else { + set_error(state->errInfo); } - - set_error(errInfo); FindClose(dir); return 0; } } int -efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ - char* name, /* Name of directory to open. */ - int flags, /* Flags to use for opening. */ - int* pfd, /* Where to store the file descriptor. */ - Sint64* pSize) /* Where to store the size of the file. */ +efile_openfile(Efile_error* errInfo, char* name, int flags, int* pfd, Sint64* pSize) +{ + Efile_call_state state; + int ret; + SVERK_TRACE(1, name); + call_state_init(&state, errInfo); + ret = do_openfile(&state, name, flags, pfd, pSize); + call_state_free(&state); + return ret; +} + +static +int do_openfile(Efile_call_state* state, /* Where to return error codes. */ + char* name, /* Name of directory to open. */ + int flags, /* Flags to use for opening. */ + int* pfd, /* Where to store the file descriptor. */ + Sint64* pSize) /* Where to store the size of the file. */ { + Efile_error* errInfo = state->errInfo; BY_HANDLE_FILE_INFORMATION fileInfo; /* File information from a handle. */ HANDLE fd; /* Handle to open file. */ DWORD access; /* Access mode: GENERIC_READ, GENERIC_WRITE. */ @@ -730,6 +1050,7 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ if (flags & EFILE_MODE_EXCL) { crFlags = CREATE_NEW; } + ensure_wpath(state, &wname); fd = CreateFileW(wname, access, FILE_SHARE_FLAGS, NULL, crFlags, flagsAndAttrs, NULL); @@ -772,26 +1093,35 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ } int -efile_may_openfile(Efile_error* errInfo, char *name) { +efile_may_openfile(Efile_error* errInfo, char *name) +{ + Efile_call_state state; WCHAR *wname = (WCHAR *) name; DWORD attr; + int ret; + SVERK_TRACE(1, name); + call_state_init(&state, errInfo); + ensure_wpath(&state, &wname); if ((attr = GetFileAttributesW(wname)) == INVALID_FILE_ATTRIBUTES) { errno = ENOENT; - return check_error(-1, errInfo); + ret = check_error(-1, errInfo); } - - if (attr & FILE_ATTRIBUTE_DIRECTORY) { + else if (attr & FILE_ATTRIBUTE_DIRECTORY) { errno = EISDIR; - return check_error(-1, errInfo); + ret = check_error(-1, errInfo); } - return 1; + else ret = 1; + + call_state_free(&state); + return ret; } void efile_closefile(fd) int fd; /* File descriptor for file to close. */ { + SVERK_TRACE(2, L""); CloseHandle((HANDLE) fd); } @@ -800,6 +1130,7 @@ efile_fdatasync(errInfo, fd) Efile_error* errInfo; /* Where to return error codes. */ int fd; /* File descriptor for file to sync. */ { + SVERK_TRACE(2, L""); /* Not available in Windows, just call regular fsync */ return efile_fsync(errInfo, fd); } @@ -809,6 +1140,7 @@ efile_fsync(errInfo, fd) Efile_error* errInfo; /* Where to return error codes. */ int fd; /* File descriptor for file to sync. */ { + SVERK_TRACE(2, L""); if (!FlushFileBuffers((HANDLE) fd)) { return check_error(-1, errInfo); } @@ -819,64 +1151,87 @@ int efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, char* orig_name, int info_for_link) { + Efile_call_state state; + int ret; + SVERK_TRACE(1, L""); + call_state_init(&state, errInfo); + ret = do_fileinfo(&state, pInfo, orig_name, info_for_link); + call_state_free(&state); + return ret; +} + +static int +do_fileinfo(Efile_call_state* state, Efile_info* pInfo, + char* orig_name, int info_for_link) +{ + Efile_error* errInfo = state->errInfo; HANDLE findhandle; /* Handle returned by FindFirstFile(). */ WIN32_FIND_DATAW findbuf; /* Data return by FindFirstFile(). */ - WCHAR name[_MAX_PATH]; + WCHAR* name = NULL; + WCHAR* win_path; int name_len; - WCHAR *path; - WCHAR pathbuf[_MAX_PATH]; int drive; /* Drive for filename (1 = A:, 2 = B: etc). */ - WCHAR *worig_name = (WCHAR *) orig_name; + WCHAR *worig_name = (WCHAR *) orig_name; + ensure_wpath(state, &worig_name); /* Don't allow wildcards to be interpreted by system */ - if (wcspbrk(worig_name, L"?*")) { - enoent: - errInfo->posix_errno = ENOENT; - errInfo->os_errno = ERROR_FILE_NOT_FOUND; - return 0; - } /* * Move the name to a buffer and make sure to remove a trailing * slash, because it causes FindFirstFile() to fail on Win95. */ - if ((name_len = wcslen(worig_name)) >= _MAX_PATH) { - goto enoent; - } else { - wcscpy(name, worig_name); - if (name_len > 2 && ISSLASH(name[name_len-1]) && - name[name_len-2] != L':') { - name[name_len-1] = L'\0'; - } + name_len = wcslen(worig_name); + + name = wpath_tmp_alloc(state, name_len+1); + wcscpy(name, worig_name); + if (name_len > 2 && ISSLASH(name[name_len-1]) && + name[name_len-2] != L':') { + name[name_len-1] = L'\0'; } - + + win_path = name; + if (wcsncmp(name, L"\\\\?\\", 4) == 0) { + win_path += 4; + } + + if (wcspbrk(win_path, L"?*")) { + enoent: + errInfo->posix_errno = ENOENT; + errInfo->os_errno = ERROR_FILE_NOT_FOUND; + return 0; + } + /* Try to get disk from name. If none, get current disk. */ - if (name[1] != L':') { + if (win_path[1] != L':') { + WCHAR* cwd_path = get_cwd_wpath_tmp(state); drive = 0; - if (GetCurrentDirectoryW(_MAX_PATH, pathbuf) && - pathbuf[1] == L':') { - drive = towlower(pathbuf[0]) - L'a' + 1; + if (cwd_path[1] == L':') { + drive = towlower(cwd_path[0]) - L'a' + 1; } - } else if (*name && name[2] == L'\0') { + } else if (*win_path && win_path[2] == L'\0') { /* * X: and nothing more is an error. */ errInfo->posix_errno = ENOENT; errInfo->os_errno = ERROR_FILE_NOT_FOUND; return 0; - } else - drive = towlower(*name) - L'a' + 1; + } else { + drive = towlower(*win_path) - L'a' + 1; + } findhandle = FindFirstFileW(name, &findbuf); if (findhandle == INVALID_HANDLE_VALUE) { + WCHAR* path = NULL; + if (!(wcspbrk(name, L"./\\") && - (path = _wfullpath(pathbuf, name, _MAX_PATH)) && + (path = get_full_wpath_tmp(state, name, NULL, 0)) && /* root dir. ('C:\') or UNC root dir. ('\\server\share\') */ ((wcslen(path) == 3) || is_root_unc_name(path)) && (GetDriveTypeW(path) > 1) ) ) { + errInfo->posix_errno = ENOENT; errInfo->os_errno = ERROR_FILE_NOT_FOUND; return 0; @@ -903,13 +1258,11 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, /* * given that we know this is a symlink, we should be able to find its target */ - WCHAR target_name[_MAX_PATH]; - if (efile_readlink(errInfo, (char *) name, - (char *) target_name, - _MAX_PATH * sizeof(WCHAR)) == 1) { + WCHAR* target_name = (WCHAR*) do_readlink(state, (char *) name, NULL, 0); + if (target_name) { FindClose(findhandle); - return efile_fileinfo(errInfo, pInfo, - (char *) target_name, info_for_link); + return do_fileinfo(state, pInfo, + (char *) target_name, info_for_link); } } @@ -976,6 +1329,20 @@ efile_write_info(Efile_error* errInfo, Efile_info* pInfo, char* name) { + Efile_call_state state; + int ret; + call_state_init(&state, errInfo); + ret = do_write_info(&state, pInfo, name); + call_state_free(&state); + return ret; +} + +static int +do_write_info(Efile_call_state* state, + Efile_info* pInfo, + char* name) +{ + Efile_error* errInfo = state->errInfo; SYSTEMTIME timebuf; FILETIME ModifyFileTime; FILETIME AccessFileTime; @@ -985,6 +1352,10 @@ efile_write_info(Efile_error* errInfo, DWORD tempAttr; WCHAR *wname = (WCHAR *) name; + SVERK_TRACE(1, name); + + ensure_wpath(state, &wname); + /* * Get the attributes for the file. */ @@ -1061,7 +1432,9 @@ char* buf; /* Buffer to write. */ size_t count; /* Number of bytes to write. */ Sint64 offset; /* where to write it */ { - int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + int res; + SVERK_TRACE(2, L""); + res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); if (res) { return efile_write(errInfo, EFILE_MODE_WRITE, fd, buf, count); } else { @@ -1079,7 +1452,9 @@ char* buf; /* Buffer to read into. */ size_t count; /* Number of bytes to read. */ size_t* pBytesRead; /* Where to return number of bytes read. */ { - int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + int res; + SVERK_TRACE(2, L""); + res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); if (res) { return efile_read(errInfo, EFILE_MODE_READ, fd, buf, count, pBytesRead); } else { @@ -1101,6 +1476,7 @@ size_t count; /* Number of bytes to write. */ OVERLAPPED overlapped; OVERLAPPED* pOverlapped = NULL; + SVERK_TRACE(2, L""); if (flags & EFILE_MODE_APPEND) { memset(&overlapped, 0, sizeof(overlapped)); overlapped.Offset = 0xffffffff; @@ -1130,6 +1506,7 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */ OVERLAPPED overlapped; OVERLAPPED* pOverlapped = NULL; + SVERK_TRACE(2, L""); ASSERT(iovcnt >= 0); if (flags & EFILE_MODE_APPEND) { @@ -1166,6 +1543,8 @@ size_t count; /* Number of bytes to read. */ size_t* pBytesRead; /* Where to return number of bytes read. */ { DWORD nbytes = 0; + + SVERK_TRACE(2, L""); if (!ReadFile((HANDLE) fd, buf, count, &nbytes, NULL)) return set_error(errInfo); @@ -1185,6 +1564,7 @@ Sint64* new_location; /* Resulting new location in file. */ { LARGE_INTEGER off, new_loc; + SVERK_TRACE(2, L""); switch (origin) { case EFILE_SEEK_SET: origin = FILE_BEGIN; break; case EFILE_SEEK_CUR: origin = FILE_CURRENT; break; @@ -1216,6 +1596,7 @@ Efile_error* errInfo; /* Where to return error codes. */ int *fd; /* File descriptor for file to truncate. */ int flags; { + SVERK_TRACE(2, L""); if (!SetEndOfFile((HANDLE) (*fd))) return set_error(errInfo); return 1; @@ -1368,8 +1749,23 @@ dos_to_posix_mode(int attr, const WCHAR *name) return uxmode; } + int efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) +{ + Efile_call_state state; + int ret; + SVERK_TRACE(1, name); + call_state_init(&state, errInfo); + ret = !!do_readlink(&state, name, buffer, size); + call_state_free(&state); + return ret; +} + +/* If buffer==0, return buffer allocated by wpath_tmp_allocate +*/ +static char* +do_readlink(Efile_call_state* state, char* name, char* buffer, size_t size) { /* * load dll and see if we have CreateSymbolicLink at runtime: @@ -1378,6 +1774,9 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) HINSTANCE hModule = NULL; WCHAR *wname = (WCHAR *) name; WCHAR *wbuffer = (WCHAR *) buffer; + DWORD wsize = size / sizeof(WCHAR) - 1; + char* ret = NULL; + if ((hModule = LoadLibrary("kernel32.dll")) != NULL) { typedef DWORD (WINAPI * GETFINALPATHNAMEBYHANDLEPTR)( HANDLE hFile, @@ -1388,58 +1787,84 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) GETFINALPATHNAMEBYHANDLEPTR pGetFinalPathNameByHandle = (GETFINALPATHNAMEBYHANDLEPTR)GetProcAddress(hModule, "GetFinalPathNameByHandleW"); - if (pGetFinalPathNameByHandle == NULL) { - FreeLibrary(hModule); - } else { + if (pGetFinalPathNameByHandle != NULL) { + DWORD fileAttributes; + ensure_wpath(state, &wname); /* first check if file is a symlink; {error, einval} otherwise */ - DWORD fileAttributes = GetFileAttributesW(wname); + fileAttributes = GetFileAttributesW(wname); if ((fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { - BOOLEAN success = 0; + DWORD success = 0; HANDLE h = CreateFileW(wname, GENERIC_READ, FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); int len; if(h != INVALID_HANDLE_VALUE) { - success = pGetFinalPathNameByHandle(h, wbuffer, size / sizeof(WCHAR),0); - /* GetFinalPathNameByHandle prepends path with "\\?\": */ - len = wcslen(wbuffer); - wmemmove(wbuffer,wbuffer+4,len-3); - if (len - 4 >= 2 && wbuffer[1] == L':' && wbuffer[0] >= L'A' && - wbuffer[0] <= L'Z') { - wbuffer[0] = wbuffer[0] + L'a' - L'A'; + if (!wbuffer) { /* dynamic allocation */ + WCHAR dummy; + wsize = pGetFinalPathNameByHandle(h, &dummy, 0, 0); + if (wsize) { + wbuffer = wpath_tmp_alloc(state, wsize); + wsize--; + } } + if (wbuffer + && (success = pGetFinalPathNameByHandle(h, wbuffer, wsize, 0)) + && success <= wsize) { + + /* GetFinalPathNameByHandle prepends path with "\\?\": */ + len = wcslen(wbuffer); + wmemmove(wbuffer,wbuffer+4,len-3); + if (len - 4 >= 2 && wbuffer[1] == L':' && wbuffer[0] >= L'A' && + wbuffer[0] <= L'Z') { + wbuffer[0] = wbuffer[0] + L'a' - L'A'; + } - for ( ; *wbuffer; wbuffer++) - if (*wbuffer == L'\\') - *wbuffer = L'/'; + for ( ; *wbuffer; wbuffer++) + if (*wbuffer == L'\\') + *wbuffer = L'/'; + } CloseHandle(h); - } - FreeLibrary(hModule); + } if (success) { - return 1; + ret = (char*) wbuffer; } else { - return set_error(errInfo); + set_error(state->errInfo); } } else { - FreeLibrary(hModule); errno = EINVAL; - return check_error(-1, errInfo); + save_last_error(state->errInfo); } + goto done; } } errno = ENOTSUP; - return check_error(-1, errInfo); + save_last_error(state->errInfo); + +done: + if (hModule) + FreeLibrary(hModule); + return ret; } int efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size) +{ + Efile_call_state state; + int ret; + SVERK_TRACE(1, orig_name); + call_state_init(&state, errInfo); + ret = do_altname(&state, orig_name, buffer, size); + call_state_free(&state); + return ret; +} + +static int +do_altname(Efile_call_state* state, char* orig_name, char* buffer, size_t size) { WIN32_FIND_DATAW wfd; HANDLE fh; - WCHAR name[_MAX_PATH+1]; + WCHAR* name; int name_len; - WCHAR* path; - WCHAR pathbuf[_MAX_PATH+1]; /* Unclear weather GetCurrentDirectory will access one char after - _MAX_PATH */ + WCHAR* full_path = NULL; WCHAR *worig_name = (WCHAR *) orig_name; WCHAR *wbuffer = (WCHAR *) buffer; int drive; /* Drive for filename (1 = A:, 2 = B: etc). */ @@ -1448,8 +1873,8 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size) if (wcspbrk(worig_name, L"?*")) { enoent: - errInfo->posix_errno = ENOENT; - errInfo->os_errno = ERROR_FILE_NOT_FOUND; + state->errInfo->posix_errno = ENOENT; + state->errInfo->os_errno = ERROR_FILE_NOT_FOUND; return 0; } @@ -1457,24 +1882,23 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size) * Move the name to a buffer and make sure to remove a trailing * slash, because it causes FindFirstFile() to fail on Win95. */ - - if ((name_len = wcslen(worig_name)) >= _MAX_PATH) { - goto enoent; - } else { - wcscpy(name, worig_name); - if (name_len > 2 && ISSLASH(name[name_len-1]) && - name[name_len-2] != L':') { - name[name_len-1] = L'\0'; - } + ensure_wpath(state, &worig_name); + name_len = wcslen(worig_name); + + name = wpath_tmp_alloc(state, name_len + 1); + wcscpy(name, worig_name); + if (name_len > 2 && ISSLASH(name[name_len-1]) && + name[name_len-2] != L':') { + name[name_len-1] = L'\0'; } /* Try to get disk from name. If none, get current disk. */ if (name[1] != L':') { + WCHAR* cwd_path = get_cwd_wpath_tmp(state); drive = 0; - if (GetCurrentDirectoryW(_MAX_PATH, pathbuf) && - pathbuf[1] == L':') { - drive = towlower(pathbuf[0]) - L'a' + 1; + if (cwd_path[1] == L':') { + drive = towlower(cwd_path[0]) - L'a' + 1; } } else if (*name && name[2] == L'\0') { /* @@ -1486,13 +1910,15 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size) } fh = FindFirstFileW(name,&wfd); if (fh == INVALID_HANDLE_VALUE) { + DWORD fff_error = GetLastError(); if (!(wcspbrk(name, L"./\\") && - (path = _wfullpath(pathbuf, name, _MAX_PATH)) && + (full_path = get_full_wpath_tmp(state, name, NULL, 0)) && /* root dir. ('C:\') or UNC root dir. ('\\server\share\') */ - ((wcslen(path) == 3) || is_root_unc_name(path)) && - (GetDriveTypeW(path) > 1) ) ) { - errno = errno_map(GetLastError()); - return check_error(-1, errInfo); + ((wcslen(full_path) == 3) || is_root_unc_name(full_path)) && + (GetDriveTypeW(full_path) > 1) ) ) { + + set_os_errno(state->errInfo, fff_error); + return 0; } /* * Root directories (such as C:\ or \\server\share\ are fabricated. @@ -1513,16 +1939,36 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size) int efile_link(Efile_error* errInfo, char* old, char* new) { + Efile_call_state state; WCHAR *wold = (WCHAR *) old; WCHAR *wnew = (WCHAR *) new; + int ret; + SVERK_TRACE(1, old); + call_state_init(&state, errInfo); + ensure_wpath(&state, &wold); + ensure_wpath(&state, &wnew); if(!CreateHardLinkW(wnew, wold, NULL)) { - return set_error(errInfo); + ret = set_error(errInfo); } - return 1; + else ret =1; + call_state_free(&state); + return ret; } int efile_symlink(Efile_error* errInfo, char* old, char* new) +{ + Efile_call_state state; + int ret; + SVERK_TRACE(1, old); + call_state_init(&state, errInfo); + ret = do_symlink(&state, old, new); + call_state_free(&state); + return ret; +} + +static int +do_symlink(Efile_call_state* state, char* old, char* new) { /* * Load dll and see if we have CreateSymbolicLink at runtime: @@ -1531,6 +1977,8 @@ efile_symlink(Efile_error* errInfo, char* old, char* new) HINSTANCE hModule = NULL; WCHAR *wold = (WCHAR *) old; WCHAR *wnew = (WCHAR *) new; + + SVERK_TRACE(1, old); if ((hModule = LoadLibrary("kernel32.dll")) != NULL) { typedef BOOLEAN (WINAPI * CREATESYMBOLICLINKFUNCPTR) ( LPCWSTR lpSymlinkFileName, @@ -1542,6 +1990,9 @@ efile_symlink(Efile_error* errInfo, char* old, char* new) "CreateSymbolicLinkW"); /* A for MBCS, W for UNICODE... char* above implies 'W'! */ if (pCreateSymbolicLink != NULL) { + ensure_wpath(state, &wold); + ensure_wpath(state, &wnew); + { DWORD attr = GetFileAttributesW(wold); int flag = (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; @@ -1552,19 +2003,21 @@ efile_symlink(Efile_error* errInfo, char* old, char* new) if (success) { return 1; } else { - return set_error(errInfo); + return set_error(state->errInfo); } + } } else FreeLibrary(hModule); } errno = ENOTSUP; - return check_error(-1, errInfo); + return check_error(-1, state->errInfo); } int efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length, int advise) { + SVERK_TRACE(2, L""); /* posix_fadvise is not available on Windows, do nothing */ errno = ERROR_SUCCESS; return check_error(0, errInfo); @@ -1573,6 +2026,7 @@ efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, int efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length) { + SVERK_TRACE(2, L""); /* No file preallocation method available in Windows. */ errno = errno_map(ERROR_NOT_SUPPORTED); SetLastError(ERROR_NOT_SUPPORTED); -- cgit v1.2.3 From eec90640fb8d122dd713d2c892fa2c365c05aae6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 13 Mar 2014 14:30:38 +0100 Subject: erts: Use GetFullPathNameW to construct abs paths from relative ones --- erts/emulator/drivers/win32/win_efile.c | 42 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index e4a7dac1df..d9bc390d13 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -361,6 +361,8 @@ static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max) return; } + SVERK_TRACE1(8,"IN: %s", path); + if (path[1] == L':' && ISSLASH(path[2])) { /* absolute path */ if (len >= max) { WCHAR *src, *dst; @@ -378,33 +380,31 @@ static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max) DWORD cwdLen = GetCurrentDirectoryW(0, NULL); DWORD absLen = cwdLen + 1 + len; if (absLen >= max) { - WCHAR *cwd; - - cwd = wpath_tmp_alloc(state, 4+absLen); - wcscpy(cwd, L"\\\\?\\"); - cwdLen = GetCurrentDirectoryW(cwdLen, cwd+4); - if (wcsncmp(cwd+4, L"\\\\?\\", 4) == 0) { - cwd += 4; - cwdLen -= 4; + WCHAR *fullPath = wpath_tmp_alloc(state, 4+4+absLen); + DWORD fullLen; + + fullLen = GetFullPathNameW(path, 4 + absLen, fullPath+4, NULL); + if (fullLen >= 4+absLen) { + *pathp = path; + SVERK_TRACE2(8,"ensure_wpath FAILED absLen=%u %s", (int)absLen, path); + return; + } + /* GetFullPathNameW can return paths longer than MAX_PATH without the \\?\ prefix. + * At least seen on Windows 7. Go figure... + */ + if (fullLen >= max && wcsncmp(fullPath+4, L"\\\\?\\", 4) != 0) { + wcsncpy(fullPath, L"\\\\?\\", 4); + *pathp = fullPath; + } + else { + *pathp = fullPath + 4; } - p = cwd + 4 + cwdLen; - if (!ISSLASH(p[-1])) - *p++ = L'\\'; - wcscpy(p, path); - - for (p=cwd; *p; p++) - if (*p == L'/') - *p = L'\\'; - *pathp = cwd; - unc_fixup = 1; } } if (unc_fixup) { WCHAR* endp; - SVERK_TRACE1(8,"IN: %s", path); - p = *pathp; len = wcslen(p); endp = p + len; @@ -432,8 +432,8 @@ static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max) else ++p; } } - SVERK_TRACE1(8,"OUT: %s", *pathp); } + SVERK_TRACE1(8,"OUT: %s", *pathp); } int -- cgit v1.2.3 From 0e9eb5c6586c927e3407f33951596e22c04a0cfe Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 13 Mar 2014 14:30:48 +0100 Subject: erts: Fix long windows paths for compressed files --- erts/emulator/drivers/common/gzio.c | 3 ++- erts/emulator/drivers/win32/win_efile.c | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/gzio.c b/erts/emulator/drivers/common/gzio.c index 653f3954b1..86734452a3 100644 --- a/erts/emulator/drivers/common/gzio.c +++ b/erts/emulator/drivers/common/gzio.c @@ -229,6 +229,7 @@ local ErtsGzFile gz_open (path, mode) errno = 0; #if defined(FILENAMES_16BIT) { + FILE* efile_wfopen(const WCHAR* name, const WCHAR* mode); WCHAR wfmode[80]; int i = 0; int j; @@ -236,7 +237,7 @@ local ErtsGzFile gz_open (path, mode) wfmode[i++] = (WCHAR) fmode[j]; } wfmode[i++] = L'\0'; - s->file = _wfopen((WCHAR *)path, wfmode); + s->file = efile_wfopen((WCHAR *)path, wfmode); if (s->file == NULL) { return s->destroy(s), (ErtsGzFile)Z_NULL; } diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index d9bc390d13..afb0252724 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -1125,6 +1125,18 @@ int fd; /* File descriptor for file to close. */ CloseHandle((HANDLE) fd); } +FILE* efile_wfopen(const WCHAR* name, const WCHAR* mode) +{ + Efile_call_state state; + Efile_error dummy; + FILE* f; + call_state_init(&state, &dummy); + ensure_wpath(&state, &name); + f = _wfopen(name, mode); + call_state_free(&state); + return f; +} + int efile_fdatasync(errInfo, fd) Efile_error* errInfo; /* Where to return error codes. */ -- cgit v1.2.3 From 189e2f0313eb6cc96e92d2c7efb790cb4d9849a4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 12 Mar 2014 16:39:29 +0100 Subject: erts: Ignore reduntant slashes in windows paths --- erts/emulator/drivers/win32/win_efile.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index afb0252724..c25fb34637 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -370,8 +370,16 @@ static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max) *pathp = wpath_tmp_alloc(state, 4+len+1); dst = *pathp; wcscpy(dst, L"\\\\?\\"); - for (src=path,dst+=4; *src; src++,dst++) - *dst = (*src == L'/') ? L'\\' : *src; + for (src=path,dst+=4; *src; src++) { + if (*src == L'/') { + if (dst[-1] != L'\\') { + *dst++ = L'\\'; + } + /*else ignore redundant slashes */ + } + else + *dst++ = *src; + } *dst = 0; unc_fixup = 1; } -- cgit v1.2.3 From cb748a7989ed79cdac9454fe25a6b93b3b6f8393 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 12 Mar 2014 16:49:50 +0100 Subject: erts: Revert file:set_cwd impl for windows No need to even try as CWD can not bee a long path anyway. --- erts/emulator/drivers/win32/win_efile.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index c25fb34637..3236da8a98 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -870,24 +870,15 @@ do_rename(Efile_call_state* state, int efile_chdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to make current. */ -{ - Efile_call_state state; - WCHAR* wname = (WCHAR*)name; - int success; - SVERK_TRACE(1, name); - - call_state_init(&state, errInfo); - ensure_wpath(&state, &wname); - success = (int) SetCurrentDirectoryW(wname); - if (!success) { - set_error(state.errInfo); - if (state.errInfo->posix_errno == EINVAL) { - /* POSIXification of errno */ - errInfo->posix_errno = ENOENT; - } - } - - call_state_free(&state); +{ + /* We don't even try to handle long paths here + * as current working directory is always limited to MAX_PATH + * even if we use UNC paths and SetCurrentDirectoryW() + */ + int success = check_error(_wchdir((WCHAR *) name), errInfo); + if (!success && errInfo->posix_errno == EINVAL) + /* POSIXification of errno */ + errInfo->posix_errno = ENOENT; return success; } -- cgit v1.2.3 From 03d322c57e070998a9b321ef0221436c15180455 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 13 Mar 2014 20:03:08 +0100 Subject: erts: Deprecate halfword emulator --- erts/configure.in | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 074882532f..7b13921be2 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. -*-m4-*- dnl %CopyrightBegin% dnl -dnl Copyright Ericsson AB 1997-2013. All Rights Reserved. +dnl Copyright Ericsson AB 1997-2014. 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 @@ -144,7 +144,7 @@ AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]), AC_ARG_ENABLE(halfword-emulator, AS_HELP_STRING([--enable-halfword-emulator], - [enable halfword emulator (only for 64bit builds)]), + [enable halfword emulator (only for 64bit builds). Note: Halfword emulator is marked as deprecated and scheduled for removal in future major release.]), [ case "$enableval" in no) enable_halfword_emualtor=no ;; *) enable_halfword_emulator=yes ;; @@ -816,6 +816,16 @@ if test "$enable_halfword_emulator" = "yes"; then [Define if building a halfword-heap 64bit emulator]) ENABLE_ALLOC_TYPE_VARS="$ENABLE_ALLOC_TYPE_VARS halfword" AC_MSG_RESULT([yes]) + + test -f "$ERL_TOP/erts/CONF_INFO" || + echo "" > "$ERL_TOP/erts/CONF_INFO" + cat >> $ERL_TOP/erts/CONF_INFO < Date: Wed, 12 Mar 2014 20:11:10 +0100 Subject: erts: Change external format for maps to be: 116,Arity, K1,V1,K2,V2,...,Kn,Vn instead of: 116,Arity, K1,K2,...,Kn, V1,V2,....,Vn We think this will be better for future internal map structures like HAMT. Would be bad if we need to iterate twice over HAMT in term_to_binary, one for keys and one for values. --- erts/doc/src/erl_ext_dist.xml | 13 ++++------ erts/emulator/beam/external.c | 55 +++++++++++++--------------------------- erts/emulator/test/map_SUITE.erl | 19 +++++++++----- 3 files changed, 36 insertions(+), 51 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml index 9a53f3f829..fa083db4c7 100644 --- a/erts/doc/src/erl_ext_dist.xml +++ b/erts/doc/src/erl_ext_dist.xml @@ -581,23 +581,20 @@ 1 4 N - M 116 Arity - Keys - Values + Pairs

MAP_EXT encodes a map. The Arity field is an unsigned 4 byte integer in big endian format that determines the number of - key-value pairs in the map. All key terms follow in the Keys - section and then all value terms in the Values section. Keys - and values are paired according to order; first key with first value - and so on. Duplicate keys are not allowed within the same - map. + key-value pairs in the map. Key and value pairs (Ki => Vi) + are encoded in the Pairs section in the following order: + K1, V1, K2, V2,..., Kn, Vn. + Duplicate keys are not allowed within the same map.

Since: OTP 17.0

diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 9671cde228..656de7c49a 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2562,29 +2562,25 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, { map_t *mp = (map_t*)map_val(obj); Uint size = map_get_size(mp); - Eterm *mptr; *ep++ = MAP_EXT; put_int32(size, ep); ep += 4; - /* Push values first */ if (size > 0) { - mptr = map_get_values(mp); + Eterm *kptr = map_get_keys(mp); + Eterm *vptr = map_get_values(mp); + for (i = size-1; i >= 1; i--) { WSTACK_PUSH(s, ENC_TERM); - WSTACK_PUSH(s, (UWord) mptr[i]); + WSTACK_PUSH(s, (UWord) vptr[i]); + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) kptr[i]); } WSTACK_PUSH(s, ENC_TERM); - WSTACK_PUSH(s, (UWord) mptr[0]); - - mptr = map_get_keys(mp); - for (i = size-1; i >= 1; i--) { - WSTACK_PUSH(s, ENC_TERM); - WSTACK_PUSH(s, (UWord) mptr[i]); - } + WSTACK_PUSH(s, (UWord) vptr[0]); - obj = mptr[0]; + obj = kptr[0]; goto L_jump_start; } } @@ -3518,16 +3514,16 @@ dec_term_atom_common: keys = make_tuple(hp); *hp++ = make_arityval(size); - kptr = hp; hp += size; + kptr = hp - 1; mp = (map_t*)hp; hp += MAP_HEADER_SIZE; - vptr = hp; hp += size; + vptr = hp - 1; - /* kptr, first word for keys - * vptr, first word for values + /* kptr, last word for keys + * vptr, last word for values */ /* @@ -3542,27 +3538,12 @@ dec_term_atom_common: mp->keys = keys; *objp = make_map(mp); - /* We assume the map is wellformed, meaning: - * - ascending key order - * - unique keys - */ - - objp = vptr + size - 1; - n = size; - - while (n-- > 0) { - *objp = (Eterm) COMPRESS_POINTER(next); - next = objp; - objp--; - } - - objp = kptr + size - 1; - n = size; - - while (n-- > 0) { - *objp = (Eterm) COMPRESS_POINTER(next); - next = objp; - objp--; + for (n = size; n; n--) { + *vptr = (Eterm) COMPRESS_POINTER(next); + *kptr = (Eterm) COMPRESS_POINTER(vptr); + next = kptr; + vptr--; + kptr--; } } break; diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 753d6f7727..888ed8e272 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -813,16 +813,16 @@ t_map_encode_decode(Config) when is_list(Config) -> %% literally #{ b=>2, a=>1 } in the internal order #{ a:=1, b:=2 } = - erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,2,97,1>>), + erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,97,2,100,0,1,97,97,1>>), %% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order #{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3, 107,0,2,104,105, % "hi" :: list() - 100,0,1,97, % a :: atom() - 100,0,1,98, % b :: atom() 107,0,5,118,97,108,117,101, % "value" :: list() + 100,0,1,97, % a :: atom() 97,33, % 33 :: integer() + 100,0,1,98, % b :: atom() 97,55 % 55 :: integer() >>), @@ -834,11 +834,17 @@ t_map_encode_decode(Config) when is_list(Config) -> %% uniqueness violation %% literally #{ a=>1, "hi"=>"value", a=>2 } {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch - erlang:binary_to_term(<<131,116,0,0,0,3,100,0,1,97,107,0,2,104,105,100,0,1,97,97,1,107,0,5,118,97,108,117,101,97,2>>)), + erlang:binary_to_term(<<131,116,0,0,0,3, + 100,0,1,97, + 97,1, + 107,0,2,104,105, + 107,0,5,118,97,108,117,101, + 100,0,1,97, + 97,2>>)), %% bad size (too large) {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch - erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,100,0,1,98,97,1,97,1>>)), + erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,97,1,100,0,1,98,97,1>>)), %% bad size (too small) .. should fail just truncate it .. weird. %% possibly change external format so truncated will be #{a:=1} @@ -852,7 +858,8 @@ map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> B0 = erlang:term_to_binary(M1), Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), %% sort Ks and Vs according to term spec, then match it - ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]), + KVbins = lists:foldr(fun({_,Kbin,Vbin}, Acc) -> [Kbin,Vbin | Acc] end, [], Ls), + ok = match_encoded_map(B0, length(Ls), KVbins), %% decode and match it M1 = erlang:binary_to_term(B0), map_encode_decode_and_match(Pairs,Ls,M1); -- cgit v1.2.3 From a996e168bfebe599cfe393cd132a87984d905d84 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 13 Mar 2014 15:23:44 +0100 Subject: erts: Add distribution capability flag for maps DFLAG_MAP_TAG This is just a preparation to allow detection of older nodes that do not understand maps (R16 and older). --- erts/emulator/beam/dist.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index 0519a9225e..f32b999198 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -40,6 +40,7 @@ #define DFLAG_SMALL_ATOM_TAGS 0x4000 #define DFLAG_INTERNAL_TAGS 0x8000 #define DFLAG_UTF8_ATOMS 0x10000 +#define DFLAG_MAP_TAG 0x20000 /* All flags that should be enabled when term_to_binary/1 is used. */ #define TERM_TO_BINARY_DFLAGS (DFLAG_EXTENDED_REFERENCES \ @@ -47,7 +48,8 @@ | DFLAG_NEW_FLOATS \ | DFLAG_EXTENDED_PIDS_PORTS \ | DFLAG_EXPORT_PTR_TAG \ - | DFLAG_BIT_BINARIES) + | DFLAG_BIT_BINARIES \ + | DFLAG_MAP_TAG) /* opcodes used in distribution messages */ #define DOP_LINK 1 -- cgit v1.2.3 From e6ff6cce0dba69ed495f098921d8ed6115007816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sun, 16 Mar 2014 23:19:19 +0100 Subject: erts: Fix is_map/1 spec --- erts/preloaded/src/erlang.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index fbc37bd955..cabbbd191f 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1745,9 +1745,9 @@ is_pid(_Term) -> erlang:nif_error(undefined). %% Shadowed by erl_bif_types: erlang:is_map/1 --spec is_map(Map) -> boolean() when - Map :: map(). -is_map(_Map) -> +-spec is_map(Term) -> boolean() when + Term :: term(). +is_map(_Term) -> erlang:nif_error(undefined). %% Shadowed by erl_bif_types: erlang:is_port/1 -- cgit v1.2.3 From 50cbd99857a674f2b082f5c436b7e721d33f4cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sun, 16 Mar 2014 23:32:44 +0100 Subject: erts: Document map guard functions * erlang:is_map/1 * erlang:map_size/1 --- erts/doc/src/erlang.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index aeded7c719..e34646eaf0 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1783,6 +1783,15 @@ os_prompt%

Allowed in guard tests.

+ + + Check whether a term is a map + +

Returns true if Term is a map; + otherwise returns false.

+

Allowed in guard tests.

+
+
Check whether a term is a number @@ -2219,6 +2228,17 @@ os_prompt% {{[],aa,[],[],zz} + + + Return the size of a map + +

Returns an integer which is the number of key-value pairs in Map.

+
+> map_size(#{a=>1, b=>2, c=>3}).
+3
+

Allowed in guard tests.

+
+
Return the largest of two term -- cgit v1.2.3 From 66280266a4b9147ea683836e2409a2ad1f2a78af Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 26 Feb 2014 11:24:06 +0100 Subject: Change encoding for XML files to utf-8 These are some files that were erronously missed earlier: erts/doc/src/time_correction.xml lib/crypto/doc/src/crypto_app.xml lib/snmp/doc/src/snmpa_mib_data.xml lib/snmp/doc/src/snmpa_mib_storage.xml --- erts/doc/src/time_correction.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index d52cc7f3e2..7f7c28fc30 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -1,10 +1,10 @@ - +
- 19992013 + 19992014 Ericsson AB. All Rights Reserved. -- cgit v1.2.3 From 54a53c31e4bff25aef79d1624361b52407f572ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 6 Mar 2014 19:06:25 +0100 Subject: erts: Handle literals in is_map/1 --- erts/emulator/beam/ops.tab | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 73630fda8e..68fcc177ae 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1484,7 +1484,8 @@ new_map j d I I update_map_assoc j s d I I update_map_exact j s d I I -is_map Fail cq => jump Fail +is_map Fail Literal=q => move Literal x | is_map Fail x +is_map Fail c => jump Fail %macro: is_map IsMap -fail_action is_map f r -- cgit v1.2.3 From 9327904dcbf2708823c05baa7532783fb0214792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 7 Mar 2014 10:41:32 +0100 Subject: Teach the call_time trace to notice when the trace dies (non-SMP system) The call_time trace is a special kind of tracing that requires a tracer process just like ordinary call trace, but it never actually sends anything to the tracer. It merely use the existence of a trace process (and call trace flags) as an indication that call_time tracing is active for the process. If the tracer dies in a non-SMP run-time system, processes with call_time tracing would not notice that the tracer had died. Furthermore, if the set_on_spawn flag was active, the dead tracer could be propagaged to newly spawned processes. Before accumulating trace information in a non-SMP system, always validate the tracer process. (In an SMP system, a reference to a dead tracer will be cleared away each time a process is scheduled.) While we could put all of the new code beam_bp.c, we have chosen to make a function call from beam_bp.c to a function in erl_trace.c for clarity's sake and to ease further maintenance. In the future, we might want to handle tracing in more similar ways in the SMP and non-SMP system. --- erts/emulator/beam/beam_bp.c | 5 +- erts/emulator/beam/erl_trace.c | 19 ++++++ erts/emulator/beam/erl_trace.h | 1 + erts/emulator/test/trace_call_time_SUITE.erl | 96 ++++++++++++++++++++++++++-- 4 files changed, 115 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 49a34ab4ad..4e711c89e0 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -642,7 +642,7 @@ erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg) erts_smp_atomic_inc_nob(&bp->count->acount); } - if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE) { + if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE && erts_is_tracer_proc_valid(c_p)) { Eterm w; erts_trace_time_call(c_p, I, bp->time); w = (BeamInstr) *c_p->cp; @@ -730,7 +730,8 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) } } if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE && - IS_TRACED_FL(p, F_TRACE_CALLS)) { + IS_TRACED_FL(p, F_TRACE_CALLS) && + erts_is_tracer_proc_valid(p)) { BeamInstr *pc = (BeamInstr *)ep->code+3; erts_trace_time_call(p, pc, bp->time); } diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 6978a5f11a..305058ceff 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -151,6 +151,11 @@ do { \ message dispatcher thread takes care of that). */ #define ERTS_GET_TRACER_REF(RES, TPID, TRACEE_FLGS) \ do { (RES) = (TPID); } while(0) +int +erts_is_tracer_proc_valid(Process* p) +{ + return 1; +} #else #define ERTS_NULL_TRACER_REF NULL #define ERTS_TRACER_REF_TYPE Process * @@ -163,6 +168,20 @@ do { \ return; \ } \ } while (0) +int +erts_is_tracer_proc_valid(Process* p) +{ + Process* tracer; + + tracer = erts_proc_lookup(ERTS_TRACER_PROC(p)); + if (tracer && ERTS_TRACE_FLAGS(tracer) & F_TRACER) { + return 1; + } else { + ERTS_TRACER_PROC(p) = NIL; + ERTS_TRACE_FLAGS(p) = ~TRACEE_FLAGS; + return 0; + } +} #endif static Uint active_sched; diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index 853c6cb0d8..4f2c70d6e7 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -39,6 +39,7 @@ void erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp); void erts_get_default_tracing(Uint *flagsp, Eterm *tracerp); void erts_set_system_monitor(Eterm monitor); Eterm erts_get_system_monitor(void); +int erts_is_tracer_proc_valid(Process* p); #ifdef ERTS_SMP void erts_check_my_tracer_proc(Process *); diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 5dfa87bbee..3036d2957b 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -33,7 +33,7 @@ %% Exported end user tests -export([seq/3, seq_r/3]). --export([loaded/1, a_function/1, a_called_function/1, dec/1, nif_dec/1]). +-export([loaded/1, a_function/1, a_called_function/1, dec/1, nif_dec/1, dead_tracer/1]). -define(US_ERROR, 10000). -define(R_ERROR, 0.8). @@ -89,7 +89,7 @@ all() -> true -> [not_run]; false -> [basic, on_and_off, info, pause_and_restart, scheduling, - combo, bif, nif, called_function] + combo, bif, nif, called_function, dead_tracer] end. groups() -> @@ -470,6 +470,92 @@ called_function(Config) when is_list(Config) -> ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ok. +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +dead_tracer(Config) when is_list(Config) -> + Self = self(), + FirstTracer = tracer(), + StartTracing = fun() -> turn_on_tracing(Self) end, + tell_tracer(FirstTracer, StartTracing), + [1,2,3,4,5,6,7,8] = seq(1, 8, fun(I) -> I + 1 end), + Ref = erlang:monitor(process, FirstTracer), + FirstTracer ! quit, + receive + {'DOWN',Ref,process,FirstTracer,normal} -> + ok + end, + erlang:yield(), + + %% Collect and check that we only get call_time info for the current process. + Info1 = collect_all_info(), + [] = other_than_self(Info1), + io:format("~p\n", [Info1]), + + %% Note that we have not turned off tracing for the current process, + %% but that the tracer has terminated. No more call_time information should be recorded. + [1,2,3] = seq(1, 3, fun(I) -> I + 1 end), + [] = collect_all_info(), + + %% When we start a second tracer process, that tracer process must + %% not inherit the tracing flags and the dead tracer (even though + %% we used set_on_spawn). + SecondTracer = tracer(), + tell_tracer(SecondTracer, StartTracing), + Seq20 = lists:seq(1, 20), + Seq20 = seq(1, 20, fun(I) -> I + 1 end), + Info2 = collect_all_info(), + io:format("~p\n", [Info2]), + [] = other_than_self(Info2), + SecondTracer ! quit, + + ok. + +other_than_self(Info) -> + [{Pid,MFA} || {MFA,[{Pid,_,_,_}]} <- Info, + Pid =/= self()]. + +tell_tracer(Tracer, Fun) -> + Tracer ! {execute,self(),Fun}, + receive + {Tracer,executed} -> + ok + end. + +tracer() -> + spawn_link(fun Loop() -> + receive + quit -> + ok; + {execute,From,Fun} -> + Fun(), + From ! {self(),executed}, + Loop() + end + end). + +turn_on_tracing(Pid) -> + _ = erlang:trace(Pid, true, [call,set_on_spawn]), + _ = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_time]), + _ = now(), + ok. + +collect_all_info() -> + collect_all_info([{?MODULE,F,A} || {F,A} <- module_info(functions)] ++ + erlang:system_info(snifs)). + +collect_all_info([MFA|T]) -> + CallTime = erlang:trace_info(MFA, call_time), + erlang:trace_pattern(MFA, restart, [call_time]), + case CallTime of + {call_time,false} -> + collect_all_info(T); + {call_time,[]} -> + collect_all_info(T); + {call_time,[_|_]=List} -> + [{MFA,List}|collect_all_info(T)] + end; +collect_all_info([]) -> []. + %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% The Tests %%% @@ -478,7 +564,6 @@ called_function(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Local helpers - load_nif(Config) -> ?line Path = ?config(data_dir, Config), ?line ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0). @@ -602,8 +687,11 @@ collect(A, Ref) -> end. setup() -> + setup([]). + +setup(Opts) -> Pid = spawn_link(fun() -> loop() end), - ?line 1 = erlang:trace(Pid, true, [call]), + 1 = erlang:trace(Pid, true, [call|Opts]), Pid. execute(Pids, Mfa) when is_list(Pids) -> -- cgit v1.2.3 From 22ff87a0c8db877e3ce53b6ff915dcc6a75c5c0c Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 10 Mar 2014 17:15:38 +0100 Subject: Introduce runtime_dependencies in .app files Most dependencies introduced are exactly the dependencies to other applications found by xref. That is, there might be real dependencies missing. There might also be pure debug dependencies listed that probably should be removed. Each application has to be manually inspected in order to ensure that all real dependencies are listed. All dependencies introduced are to application versions used in OTP 17.0. This since the previously used version scheme wasn't designed for this, and in order to minimize the work of introducing the dependencies. --- erts/preloaded/src/erts.app.src | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index fd3e8cb692..a15da3a421 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -35,7 +35,8 @@ {registered, []}, {applications, []}, {env, []}, - {mod, {erts, []}} + {mod, {erts, []}}, + {runtime_dependencies, ["stdlib-2.0", "kernel-3.0", "sasl-2.4"]} ]}. %% vim: ft=erlang -- cgit v1.2.3 From 0453d4ebd924f504e8d37ac5830ecc28ccc15046 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 11 Mar 2014 12:04:25 +0100 Subject: Add test-case verifying runtime dependencies found by xref --- erts/test/otp_SUITE.erl | 62 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 8e4a1a4b1c..1fb452501f 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -24,7 +24,7 @@ -export([undefined_functions/1,deprecated_not_in_obsolete/1, obsolete_but_not_deprecated/1,call_to_deprecated/1, call_to_size_1/1,strong_components/1, - erl_file_encoding/1,xml_file_encoding/1]). + erl_file_encoding/1,xml_file_encoding/1,runtime_dependencies/1]). -include_lib("test_server/include/test_server.hrl"). @@ -36,7 +36,8 @@ all() -> [undefined_functions, deprecated_not_in_obsolete, obsolete_but_not_deprecated, call_to_deprecated, call_to_size_1, strong_components, - erl_file_encoding, xml_file_encoding]. + erl_file_encoding, xml_file_encoding, + runtime_dependencies]. groups() -> []. @@ -380,6 +381,63 @@ is_bad_encoding(File) -> true end. +runtime_dependencies(Config) -> + %% Verify that (at least) OTP application runtime dependencies found + %% by xref are listed in the runtime_dependencies field of the .app file + %% of each application. + Server = ?config(xref_server, Config), + {ok, AE} = xref:q(Server, "AE"), + SAE = lists:keysort(1, AE), + {AppDep, AppDeps} = lists:foldl(fun ({App, App}, Acc) -> + Acc; + ({App, Dep}, {undefined, []}) -> + {{App, [Dep]}, []}; + ({App, Dep}, {{App, Deps}, AppDeps}) -> + {{App, [Dep|Deps]}, AppDeps}; + ({App, Dep}, {AppDep, AppDeps}) -> + {{App, [Dep]}, [AppDep | AppDeps]} + end, + {undefined, []}, + SAE), + [] = check_apps_deps([AppDep|AppDeps]), + ok. + +have_rdep(_App, [], _Dep) -> + false; +have_rdep(App, [RDep | RDeps], Dep) -> + [AppStr, _VsnStr] = string:tokens(RDep, "-"), + case Dep == list_to_atom(AppStr) of + true -> + io:format("~p -> ~s~n", [App, RDep]), + true; + false -> + have_rdep(App, RDeps, Dep) + end. + +check_app_deps(_App, _AppFile, _AFDeps, []) -> + []; +check_app_deps(App, AppFile, AFDeps, [XRDep | XRDeps]) -> + ResOtherDeps = check_app_deps(App, AppFile, AFDeps, XRDeps), + case have_rdep(App, AFDeps, XRDep) of + true -> + ResOtherDeps; + false -> + [{missing_runtime_dependency, AppFile, XRDep} | ResOtherDeps] + end. + +check_apps_deps([]) -> + []; +check_apps_deps([{App, Deps}|AppDeps]) -> + ResOtherApps = check_apps_deps(AppDeps), + AppFile = code:where_is_file(atom_to_list(App) ++ ".app"), + {ok,[{application, App, Info}]} = file:consult(AppFile), + case lists:keyfind(runtime_dependencies, 1, Info) of + {runtime_dependencies, RDeps} -> + check_app_deps(App, AppFile, RDeps, Deps) ++ ResOtherApps; + false -> + [{missing_runtime_dependencies_key, AppFile} | ResOtherApps] + end. + %%% %%% Common help functions. %%% -- cgit v1.2.3 From e310677df7b3ce6506b35044abafcb507caa7e07 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 10 Mar 2014 17:44:25 +0100 Subject: Verify runtime_dependencies when running 'otp_build patch_app' --- erts/Makefile.in | 5 +++++ erts/doc/src/erlang.xml | 4 ++-- erts/etc/win32/nsis/erlang.nsi | 1 - erts/etc/win32/nsis/erlang20.nsi | 1 - 4 files changed, 7 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/Makefile.in b/erts/Makefile.in index e3db37d3fd..47298cccba 100644 --- a/erts/Makefile.in +++ b/erts/Makefile.in @@ -48,6 +48,7 @@ debug opt clean: ( cd $$d && $(MAKE) $@ FLAVOR=$(FLAVOR) ) || exit $$? ; \ fi ; \ done + (cd preloaded/src && $(MAKE) ../ebin/erts.app) # ---------------------------------------------------------------------- # These are "convenience targets", provided as shortcuts for developers @@ -135,6 +136,10 @@ release: ( cd $$d && $(MAKE) $@ ) || exit $$? ; \ fi ; \ done + ( $(MAKE) -f "$(ERL_TOP)/make/otp_released_app.mk" \ + APP_PWD="$(ERL_TOP)/erts" APP_VSN=VSN APP=erts \ + TESTROOT="$(TESTROOT)" update) \ + || exit $$? .PHONY: release_docs release_docs: diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index aeded7c719..b06d5aeb12 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6121,8 +6121,8 @@ ok erlang:system_info() argument giving the exact OTP version. This since the exact OTP version in the general case is hard to determine. For more information see - the - documentation of the OTP version in the installation + the + documentation of versions in the system principles guide.

port_parallelism diff --git a/erts/etc/win32/nsis/erlang.nsi b/erts/etc/win32/nsis/erlang.nsi index 162e634148..f4fd2b4cdb 100644 --- a/erts/etc/win32/nsis/erlang.nsi +++ b/erts/etc/win32/nsis/erlang.nsi @@ -93,7 +93,6 @@ SectionIn 1 RO skip_silent_mode: SetOutPath "$INSTDIR" - File "${TESTROOT}\OTP_VERSION" File "${TESTROOT}\Install.ini" File "${TESTROOT}\Install.exe" File /r "${TESTROOT}\releases" diff --git a/erts/etc/win32/nsis/erlang20.nsi b/erts/etc/win32/nsis/erlang20.nsi index 3ee33e8121..3333c4a9aa 100644 --- a/erts/etc/win32/nsis/erlang20.nsi +++ b/erts/etc/win32/nsis/erlang20.nsi @@ -144,7 +144,6 @@ Section "Development" SecErlangDev SectionIn 1 RO SetOutPath "$INSTDIR" - File "${TESTROOT}\OTP_VERSION" File "${TESTROOT}\Install.ini" File "${TESTROOT}\Install.exe" SetOutPath "$INSTDIR\releases" -- cgit v1.2.3 From f43a1dc55d42de3097f75ca65baead9a2ff05c78 Mon Sep 17 00:00:00 2001 From: Dmitry Kolesnikov Date: Tue, 11 Feb 2014 00:27:11 +0200 Subject: Raspberry PI / Android a minimal cross-compile configuration Enable a cross compile Erlang/OTP platform to Android or Raspberry PI using Android NDK. Port emulator and core application to support target HW platform. Exclude any add-on services required for OTP platform deployment into target hardware due to device fragmentation and jail-break requirements. * fix erts/emulator/beam/sys.h Disable redefinition of __noreturn macro * port erts/emulator/sys/unix/erl_child_setup.c Use techniques proposed by https://code.google.com/p/erlang4android to access system properties * fix erts/emulator/sys/unix/erl_unix_sys_ddll.c The static linking of emulator cannot find dlerror(), dlopen() symbols * port erts/emulator/sys/unix/sys.c make path to shell configurable at build time * port erts/etc/common/Makefile.in disable librt for *-linux-androideabi * port erts/lib_src/pthread/ethread.c Use techniques proposed by https://code.google.com/p/erlang4android to disable emulator crash if kernel threads are on. Replace unreliable pthread_sigmask() by sigprocmask() * port lib/erl_interface/src/connect/ei_connect.c Disable call to undefined gethostid() * port lib/erl_interface/src/connect/ei_resolve.c Use gethostbyname_r() on Android platform --- erts/emulator/Makefile.in | 2 ++ erts/emulator/beam/sys.h | 6 ++++- erts/emulator/sys/unix/erl_child_setup.c | 39 +++++++++++++++++++++++++++++- erts/emulator/sys/unix/erl_unix_sys_ddll.c | 4 +++ erts/emulator/sys/unix/sys.c | 11 +++++++-- erts/etc/common/Makefile.in | 2 ++ erts/lib_src/pthread/ethread.c | 6 ++++- 7 files changed, 65 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 2a9a0a5c2f..58e77ed1fa 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -378,7 +378,9 @@ LIBS += -l$(ERTS_INTERNAL_LIB)$(TYPEMARKER) endif # erts_internal_r +ifneq ($(TARGET),arm-unknown-linux-androideabi) LIBS += @LIBRT@ +endif LIBS += @LIBCARBON@ diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index e273056a2b..05f07e57b2 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -154,10 +154,14 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType; /* In VC++, noreturn is a declspec that has to be before the types, * but in GNUC it is an att ribute to be placed between return type * and function name, hence __decl_noreturn __noreturn + * + * at some platforms (e.g. Android) __noreturn is defined at sys/cdef.h */ #if __GNUC__ # define __decl_noreturn -# define __noreturn __attribute__((noreturn)) +# ifndef __noreturn +# define __noreturn __attribute__((noreturn)) +# endif #else # if defined(__WIN32__) && defined(_MSC_VER) # define __noreturn diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 7c6e4a2f37..34d7f9e0e5 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -54,6 +54,17 @@ void sys_sigrelease(int sig) #endif /* !SIG_SIGNAL */ #endif /* !SIG_SIGSET */ +#if defined(__ANDROID__) +int __system_properties_fd(void); +#endif /* __ANDROID__ */ + +#if defined(__ANDROID__) +#define SHELL "/system/bin/sh" +#else +#define SHELL "/bin/sh" +#endif /* __ANDROID__ */ + + int main(int argc, char *argv[]) { @@ -89,8 +100,16 @@ main(int argc, char *argv[]) if (sscanf(argv[CS_ARGV_FD_CR_IX], "%d:%d", &from, &to) != 2) return 1; + +#if defined(__ANDROID__) + for (i = from; i <= to; i++) { + if (i!=__system_properties_fd) + (void) close(i); + } +#else for (i = from; i <= to; i++) (void) close(i); +#endif /* __ANDROID__ */ if (!(argv[CS_ARGV_WD_IX][0] == '.' && argv[CS_ARGV_WD_IX][1] == '\0') && chdir(argv[CS_ARGV_WD_IX]) < 0) @@ -116,7 +135,25 @@ main(int argc, char *argv[]) execv(argv[CS_ARGV_NO_OF_ARGS],&(argv[CS_ARGV_NO_OF_ARGS + 1])); } } else { - execl("/bin/sh", "sh", "-c", argv[CS_ARGV_CMD_IX], (char *) NULL); + execl(SHELL, "sh", "-c", argv[CS_ARGV_CMD_IX], (char *) NULL); } return 1; } + + + +#if defined(__ANDROID__) +int __system_properties_fd(void) +{ + int s, fd; + char *env; + + env = getenv("ANDROID_PROPERTY_WORKSPACE"); + if (!env) { + return -1; + } + fd = atoi(env); + return fd; +} +#endif /* __ANDROID__ */ + diff --git a/erts/emulator/sys/unix/erl_unix_sys_ddll.c b/erts/emulator/sys/unix/erl_unix_sys_ddll.c index 8760b58839..2659d623c7 100644 --- a/erts/emulator/sys/unix/erl_unix_sys_ddll.c +++ b/erts/emulator/sys/unix/erl_unix_sys_ddll.c @@ -123,6 +123,7 @@ int erts_sys_ddll_open(const char *full_name, void **handle, ErtsSysDdllError* e int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err) { +#if defined(HAVE_DLOPEN) int ret = ERL_DE_NO_ERROR; char *str; dlerror(); @@ -148,6 +149,9 @@ int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err) ret = ERL_DE_DYNAMIC_ERROR_OFFSET - find_errcode(str, err); } return ret; +#else + return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY; +#endif } /* diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 865cb50a56..c3d7440409 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -149,6 +149,13 @@ extern void erl_crash_dump(char* file, int line, char* fmt, ...); #define DIR_SEPARATOR_CHAR '/' +#if defined(__ANDROID__) +#define SHELL "/system/bin/sh" +#else +#define SHELL "/bin/sh" +#endif /* __ANDROID__ */ + + #if defined(DEBUG) #define ERL_BUILD_TYPE_MARKER ".debug" #elif defined(PURIFY) @@ -1596,7 +1603,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op } } } else { - execle("/bin/sh", "sh", "-c", cmd_line, (char *) NULL, new_environ); + execle(SHELL, "sh", "-c", cmd_line, (char *) NULL, new_environ); } child_error: _exit(1); @@ -1717,7 +1724,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op fcntl(i, F_SETFD, 1); qnx_spawn_options.flags = _SPAWN_SETSID; - if ((pid = spawnl(P_NOWAIT, "/bin/sh", "/bin/sh", "-c", cmd_line, + if ((pid = spawnl(P_NOWAIT, SHELL, SHELL, "-c", cmd_line, (char *) 0)) < 0) { erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); reset_qnx_spawn(); diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index 5c2cd8aded..cfd36af962 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -66,7 +66,9 @@ LIBS = @LIBS@ LDFLAGS = @LDFLAGS@ # For clock_gettime in heart +ifneq ($(TARGET),arm-unknown-linux-androideabi) RTLIBS = @LIBRT@ +endif ifeq ($(TARGET),win32) ifeq ($(TYPE),debug) diff --git a/erts/lib_src/pthread/ethread.c b/erts/lib_src/pthread/ethread.c index 7f27b5f29c..79784c5b84 100644 --- a/erts/lib_src/pthread/ethread.c +++ b/erts/lib_src/pthread/ethread.c @@ -541,7 +541,11 @@ int ethr_sigmask(int how, const sigset_t *set, sigset_t *oset) return EINVAL; } #endif - return pthread_sigmask(how, set, oset); +#if defined(__ANDROID__) + return sigprocmask(how, set, oset); +#else + return pthread_sigmask(how, set, oset); +#endif } int ethr_sigwait(const sigset_t *set, int *sig) -- cgit v1.2.3 From bf3222f10edbd1f6a5186c8fb35c29900ad0665f Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 20 Mar 2014 19:01:11 +0100 Subject: Introduce minimum allowed major driver and nif versions on load --- erts/doc/src/erl_driver.xml | 7 ++++-- erts/doc/src/erl_nif.xml | 26 ++++++++++++++++++++++ erts/emulator/beam/erl_bif_ddll.c | 6 +++-- erts/emulator/beam/erl_driver.h | 16 +++++++++++++ erts/emulator/beam/erl_nif.c | 6 +++-- erts/emulator/beam/erl_nif.h | 12 ++++++++++ .../test/driver_SUITE_data/smaller_major_vsn_drv.c | 4 ++-- 7 files changed, 69 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 8da1836da7..ad37813ac0 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -315,10 +315,13 @@ ERL_DRV_EXTENDED_MINOR_VERSION will be incremented when new features are added. The runtime system uses the minor version of the driver to determine what features to use. - The runtime system will refuse to load a driver if the major + The runtime system will normally refuse to load a driver if the major versions differ, or if the major versions are equal and the minor version used by the driver is greater than the one used - by the runtime system.

+ by the runtime system. Old drivers with lower major versions + will however be allowed after a bump of the major version during + a transition period of two major releases. Such old drivers might + however fail if deprecated features are used.

The emulator will refuse to load a driver that does not use the extended driver interface, to allow for 64-bit capable drivers, diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 8b19725c02..6b1f4cccf8 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -316,6 +316,32 @@ ok

The library initialization callbacks load, reload and upgrade are all thread-safe even for shared state data.

+ + Version Management +

+ When a NIF library is built, information about NIF API version + is compiled into the library. When a NIF library is loaded the + runtime system verifies that the library is of a compatible version. + erl_nif.h defines ERL_NIF_MAJOR_VERSION, and + ERL_NIF_MINOR_VERSION. ERL_NIF_MAJOR_VERSION will be + incremented when NIF library incompatible changes are made to the + Erlang runtime system. Normally it will suffice to recompile the NIF + library when the ERL_NIF_MAJOR_VERSION has changed, but it + could, under rare circumstances, mean that NIF libraries have to + be slightly modified. If so, this will of course be documented. + ERL_NIF_MINOR_VERSION will be incremented when + new features are added. The runtime system uses the minor version + to determine what features to use. +

+ The runtime system will normally refuse to load a NIF library if + the major versions differ, or if the major versions are equal and + the minor version used by the NIF library is greater than the one + used by the runtime system. Old NIF libraries with lower major + versions will however be allowed after a bump of the major version + during a transition period of two major releases. Such old NIF + libraries might however fail if deprecated features are used. +

+ Dirty NIFs

Note that the dirty NIF functionality is experimental and that you have to enable support for dirty diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index 1728b200f7..56cd2ba04f 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -1548,8 +1548,10 @@ static int do_load_driver_entry(DE_Handle *dh, char *path, char *name) switch (dp->extended_marker) { case ERL_DRV_EXTENDED_MARKER: - if (ERL_DRV_EXTENDED_MAJOR_VERSION != dp->major_version - || ERL_DRV_EXTENDED_MINOR_VERSION < dp->minor_version) { + if (dp->major_version < ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD + || (ERL_DRV_EXTENDED_MAJOR_VERSION < dp->major_version + || (ERL_DRV_EXTENDED_MAJOR_VERSION == dp->major_version + && ERL_DRV_EXTENDED_MINOR_VERSION < dp->minor_version))) { /* Incompatible driver version */ res = ERL_DE_LOAD_ERROR_INCORRECT_VERSION; goto error; diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 5517c26ba4..3ecb379326 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -135,6 +135,22 @@ typedef struct { #define ERL_DRV_EXTENDED_MAJOR_VERSION 3 #define ERL_DRV_EXTENDED_MINOR_VERSION 0 +/* + * The emulator will refuse to load a driver with a major version + * lower than ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD. The load + * may however fail if user have not removed use of deprecated + * symbols. + * + * The ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD have to allow + * loading of drivers built at least two major OTP releases + * ago. + * + * Bump of major version to 3 happened in OTP 17. That is, in + * OTP 19 we can increase ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD + * to 3. + */ +#define ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD 2 + /* * The emulator will refuse to load a driver with different major * version than the one used by the emulator. diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 40860e141c..063dba056e 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -2049,8 +2049,10 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) (entry = erts_sys_ddll_call_nif_init(init_func)) == NULL)) { ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful"); } - else if (entry->major != ERL_NIF_MAJOR_VERSION - || entry->minor > ERL_NIF_MINOR_VERSION + else if (entry->major < ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD + || (ERL_NIF_MAJOR_VERSION < entry->major + || (ERL_NIF_MAJOR_VERSION == entry->major + && ERL_NIF_MINOR_VERSION < entry->minor)) || (entry->major==2 && entry->minor == 5)) { /* experimental maps */ ret = load_nif_error(BIF_P, bad_lib, "Library version (%d.%d) not compatible (with %d.%d).", diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index c12ba4d554..5b93c2398e 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -46,6 +46,18 @@ #define ERL_NIF_MAJOR_VERSION 2 #define ERL_NIF_MINOR_VERSION 6 +/* + * The emulator will refuse to load a nif-lib with a major version + * lower than ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD. The load + * may however fail if user have not removed use of deprecated + * symbols. + * + * The ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD have to allow + * loading of nif-libs built at least two major OTP releases + * ago. + */ +#define ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD 2 + #include #ifdef SIZEOF_CHAR diff --git a/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c b/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c index a1299fe807..6b9d4745ba 100644 --- a/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c +++ b/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c @@ -20,12 +20,12 @@ * Author: Rickard Green * * Description: Implementation of a driver with a smaller major - * driver version than the current system. + * driver version than allowed on load. */ #define VSN_MISMATCH_DRV_NAME_STR "smaller_major_vsn_drv" #define VSN_MISMATCH_DRV_NAME smaller_major_vsn_drv -#define VSN_MISMATCH_DRV_MAJOR_VSN_DIFF (-1) +#define VSN_MISMATCH_DRV_MAJOR_VSN_DIFF (ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD - ERL_DRV_EXTENDED_MAJOR_VERSION - 1) #define VSN_MISMATCH_DRV_MINOR_VSN_DIFF 0 #include "vsn_mismatch_drv_impl.c" -- cgit v1.2.3 From 6e318949a19c7603d23da64f312b96ecf950ee30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 20 Mar 2014 16:50:53 +0100 Subject: zlib: Fix adler32_combine/4 and crc32_combine/4 Since our config.h is included in each source file in the zlib directory, we must make sure that config.h is also included every time zlib functions are called to ensure that definitions of types are consistent. zlib_drv.c did not include config.h, which caused problems on a MacOS X Leopard system. What happened was that HAVE_UNISTD_H was not defined when zlib.h was included. When unistd.h is not known to exist, the type z_off_t will be set to a long (32 bits on this platform). But when the zlib source files were compiled, HAVE_UNISTD_H was defined, unistd.h would be included, and z_off_t would be set to off_t (64 bits; defined in unistd.h). As a result of the mismatch, calls to the adler32_combine() and crc32_combine() would pass only 32 bits in the len2 argument when 64 bits were expected. --- erts/emulator/drivers/common/zlib_drv.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'erts') diff --git a/erts/emulator/drivers/common/zlib_drv.c b/erts/emulator/drivers/common/zlib_drv.c index 3fe5d282dc..3143e4511d 100644 --- a/erts/emulator/drivers/common/zlib_drv.c +++ b/erts/emulator/drivers/common/zlib_drv.c @@ -21,6 +21,9 @@ * ZLib interface for erlang * */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include #include #include -- cgit v1.2.3 From d4b97ae200f123ed8f478fa53eb980ef016c14b8 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 21 Mar 2014 16:37:14 +0100 Subject: Change release name from "OTP APN 181 01" to "Erlang/OTP" --- erts/start_scripts/no_dot_erlang.rel.src | 4 ++-- erts/start_scripts/start_all_example.rel.src | 4 ++-- erts/start_scripts/start_clean.rel.src | 4 ++-- erts/start_scripts/start_sasl.rel.src | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/start_scripts/no_dot_erlang.rel.src b/erts/start_scripts/no_dot_erlang.rel.src index 03b64ebf1a..6208572c00 100644 --- a/erts/start_scripts/no_dot_erlang.rel.src +++ b/erts/start_scripts/no_dot_erlang.rel.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,6 +16,6 @@ %% %% %CopyrightEnd% %% -{release, {"OTP APN 181 01","%SYS_VSN%"}, {erts, "%ERTS_VSN%"}, +{release, {"Erlang/OTP","%SYS_VSN%"}, {erts, "%ERTS_VSN%"}, [{kernel,"%KERNEL_VSN%"}, {stdlib,"%STDLIB_VSN%"}]}. diff --git a/erts/start_scripts/start_all_example.rel.src b/erts/start_scripts/start_all_example.rel.src index 581eb2eb0b..2a1cabe7bb 100644 --- a/erts/start_scripts/start_all_example.rel.src +++ b/erts/start_scripts/start_all_example.rel.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,7 +16,7 @@ %% %% %CopyrightEnd% %% -{release, {"OTP APN 181 01","%SYS_VSN%"}, {erts, "%ERTS_VSN%"}, +{release, {"Erlang/OTP","%SYS_VSN%"}, {erts, "%ERTS_VSN%"}, [{kernel,"%KERNEL_VSN%"}, {stdlib,"%STDLIB_VSN%"}, {sasl, "%SASL_VSN%"}, diff --git a/erts/start_scripts/start_clean.rel.src b/erts/start_scripts/start_clean.rel.src index d2df422c51..e229721e36 100644 --- a/erts/start_scripts/start_clean.rel.src +++ b/erts/start_scripts/start_clean.rel.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,6 +16,6 @@ %% %% %CopyrightEnd% %% -{release, {"OTP APN 181 01","%SYS_VSN%"}, {erts, "%ERTS_VSN%"}, +{release, {"Erlang/OTP","%SYS_VSN%"}, {erts, "%ERTS_VSN%"}, [{kernel,"%KERNEL_VSN%"}, {stdlib,"%STDLIB_VSN%"}]}. diff --git a/erts/start_scripts/start_sasl.rel.src b/erts/start_scripts/start_sasl.rel.src index e521e8df91..e68a34af76 100644 --- a/erts/start_scripts/start_sasl.rel.src +++ b/erts/start_scripts/start_sasl.rel.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,7 +16,7 @@ %% %% %CopyrightEnd% %% -{release, {"OTP APN 181 01","%SYS_VSN%"}, {erts, "%ERTS_VSN%"}, +{release, {"Erlang/OTP","%SYS_VSN%"}, {erts, "%ERTS_VSN%"}, [{kernel,"%KERNEL_VSN%"}, {stdlib,"%STDLIB_VSN%"}, {sasl, "%SASL_VSN%"}]}. -- cgit v1.2.3 From 78b118bc5f503435b1d9216b3a3279e0c9fd9ecd Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 21 Mar 2014 16:38:13 +0100 Subject: erts: Fix heap overflow in maps:remove/2 when key is not found One key-value pair too many was copied. --- erts/emulator/beam/erl_map.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 2fff7f9390..fdd2d0c0f6 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -647,22 +647,24 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { *mhp++ = tup; if (is_immed(key)) { - while(n--) { + while (1) { if (*ks == key) { goto found_key; - } else { + } else if (--n) { *mhp++ = *vs++; *thp++ = *ks++; - } + } else + break; } } else { - while(n--) { + while(1) { if (EQ(*ks, key)) { goto found_key; - } else { + } else if (--n) { *mhp++ = *vs++; *thp++ = *ks++; - } + } else + break; } } @@ -676,7 +678,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { found_key: /* Copy rest of keys and values */ - if (n) { + if (--n) { sys_memcpy(mhp, vs+1, n*sizeof(Eterm)); sys_memcpy(thp, ks+1, n*sizeof(Eterm)); } -- cgit v1.2.3 From 483161dd3e05d5d4285bae90ac625e761c7b8214 Mon Sep 17 00:00:00 2001 From: Adrian Mangeac Date: Mon, 3 Mar 2014 11:18:52 +0100 Subject: ose: Restore the owner of the signal As any scheduler process can consume any message we have to make sure that the message is owned by the scheduler before returning it in erl_drv_ose_get_signal. --- erts/emulator/sys/ose/erl_poll.c | 1 + erts/emulator/sys/ose/sys.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c index ca1ed6e53a..f0a9097252 100644 --- a/erts/emulator/sys/ose/erl_poll.c +++ b/erts/emulator/sys/ose/erl_poll.c @@ -737,6 +737,7 @@ union SIGNAL *erl_drv_ose_get_signal(ErlDrvEvent drv_ev) { ev->msgs = msg->next; ethr_mutex_unlock(&ev->mtx); erts_free(ERTS_ALC_T_FD_SIG_LIST,msg); + restore(sig); return sig; } } diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index c892cc69c7..e3041ff1ca 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -213,7 +213,6 @@ static volatile int children_died; /* When we have several schedulers, we need to make sure * that scheduler issuing aio_dispatch() is the owner on the signal */ #define DISPATCH_AIO(sig) do { \ - restore(sig); \ aio_dispatch(sig); \ } while(0) -- cgit v1.2.3 From ebad8e4c3a26e5ed3813ede2ca539123a1d31812 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 12 Mar 2014 15:58:03 +0100 Subject: ose: Reset busy port when pdq empty --- erts/emulator/sys/ose/sys.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index e3041ff1ca..6e6303820c 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -1394,16 +1394,15 @@ static void ready_output(ErlDrvData drv_data, ErlDrvEvent ready_fd) driver_failure_posix(data->port_num, status); } else { /* written bytes > 0 */ - iov = driver_peekq(data->port_num, &vlen); - if (vlen > 0) { - DISPATCH_AIO(sig); - FREE_AIO(sig->fm_write_reply.buffer); - res = driver_deq(data->port_num, iov[0].iov_len); - if (res > 0) { - iov = driver_peekq(data->port_num, &vlen); - WRITE_AIO(data->ofd, iov[0].iov_len, iov[0].iov_base); - } - } + DISPATCH_AIO(sig); + res = driver_deq(data->port_num, sig->fm_write_reply.actual); + FREE_AIO(sig->fm_write_reply.buffer); + if (res == 0) + set_busy_port(data->port_num, 0); + else { + iov = driver_peekq(data->port_num, &vlen); + WRITE_AIO(data->ofd, iov[0].iov_len, iov[0].iov_base); + } } sig = erl_drv_ose_get_signal(ready_fd); } -- cgit v1.2.3 From a9fd7c1ffbb2b3509821b6655f460e27f70c2892 Mon Sep 17 00:00:00 2001 From: Vlad Dumitrescu Date: Fri, 21 Mar 2014 21:24:28 +0100 Subject: Specify that +fn* flags affect even command-line parameters and environment variables --- erts/doc/src/erl.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 4aa3033f40..9724a1345a 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -536,7 +536,7 @@

The VM works with file names as if they are encoded using the ISO-latin-1 encoding, disallowing Unicode characters with codepoints beyond 255.

-

See STDLIB User's Guide for more infomation about unicode file names.

+

See STDLIB User's Guide for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see STDLIB User's Guide).

@@ -555,7 +555,7 @@ encountered. w is the default. Note that file:read_link/1 will always return an error if the link points to an invalid file name.

-

See STDLIB User's Guide for more infomation about unicode file names.

+

See STDLIB User's Guide for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see STDLIB User's Guide).

@@ -572,7 +572,7 @@ settings cause the behavior of +fnl to be selected, then w, i, or e will not have any effect.

-

See STDLIB User's Guide for more infomation about unicode file names.

+

See STDLIB User's Guide for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see STDLIB User's Guide).

-- cgit v1.2.3 From 2d2ce325546bc93178d1343e2a53911b890cc259 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 12 Mar 2014 17:07:16 +0100 Subject: erts: Make file:make_symlink/2 return {error,eperm} on Windows if the user has not the privilege SE_CREATE_SYMBOLIC_LINK_NAME --- erts/emulator/drivers/win32/win_efile.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index 3236da8a98..3d3a5bcb84 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -251,6 +251,8 @@ static int errno_map(DWORD last_error) { return EAGAIN; case ERROR_CANT_RESOLVE_FILENAME: return EMLINK; + case ERROR_PRIVILEGE_NOT_HELD: + return EPERM; case ERROR_ARENA_TRASHED: case ERROR_INVALID_BLOCK: case ERROR_BAD_ENVIRONMENT: -- cgit v1.2.3 From 2be50d2eb4690b0f849f86170ad176928443de30 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 12 Mar 2014 17:14:04 +0100 Subject: erts: Fix compiler warning in win_efile.c and some improved debug tracing --- erts/emulator/drivers/win32/win_efile.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index 3d3a5bcb84..4176f137c1 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -1000,7 +1000,7 @@ efile_openfile(Efile_error* errInfo, char* name, int flags, int* pfd, Sint64* pS { Efile_call_state state; int ret; - SVERK_TRACE(1, name); + SVERK_TRACE1(1, "openfile(%s)", name); call_state_init(&state, errInfo); ret = do_openfile(&state, name, flags, pfd, pSize); call_state_free(&state); @@ -1132,7 +1132,7 @@ FILE* efile_wfopen(const WCHAR* name, const WCHAR* mode) Efile_error dummy; FILE* f; call_state_init(&state, &dummy); - ensure_wpath(&state, &name); + ensure_wpath(&state, (WCHAR**)&name); f = _wfopen(name, mode); call_state_free(&state); return f; @@ -1973,7 +1973,7 @@ efile_symlink(Efile_error* errInfo, char* old, char* new) { Efile_call_state state; int ret; - SVERK_TRACE(1, old); + SVERK_TRACE2(1, "symlink(%s <- %s)", old, new); call_state_init(&state, errInfo); ret = do_symlink(&state, old, new); call_state_free(&state); -- cgit v1.2.3 From 3700d457197ae91ba75248d19716005c4013c177 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 18 Mar 2014 16:00:44 +0100 Subject: erts: Fix bug in efile_readlink for long win paths --- erts/emulator/drivers/win32/win_efile.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index 4176f137c1..9621fcca45 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -1787,7 +1787,7 @@ do_readlink(Efile_call_state* state, char* name, char* buffer, size_t size) HINSTANCE hModule = NULL; WCHAR *wname = (WCHAR *) name; WCHAR *wbuffer = (WCHAR *) buffer; - DWORD wsize = size / sizeof(WCHAR) - 1; + DWORD wsize = size / sizeof(WCHAR); char* ret = NULL; if ((hModule = LoadLibrary("kernel32.dll")) != NULL) { @@ -1815,12 +1815,12 @@ do_readlink(Efile_call_state* state, char* name, char* buffer, size_t size) wsize = pGetFinalPathNameByHandle(h, &dummy, 0, 0); if (wsize) { wbuffer = wpath_tmp_alloc(state, wsize); - wsize--; } } if (wbuffer && (success = pGetFinalPathNameByHandle(h, wbuffer, wsize, 0)) - && success <= wsize) { + && success < wsize) { + WCHAR* wp; /* GetFinalPathNameByHandle prepends path with "\\?\": */ len = wcslen(wbuffer); @@ -1830,9 +1830,9 @@ do_readlink(Efile_call_state* state, char* name, char* buffer, size_t size) wbuffer[0] = wbuffer[0] + L'a' - L'A'; } - for ( ; *wbuffer; wbuffer++) - if (*wbuffer == L'\\') - *wbuffer = L'/'; + for (wp=wbuffer ; *wp; wp++) + if (*wp == L'\\') + *wp = L'/'; } CloseHandle(h); } -- cgit v1.2.3 From 09635c1b75a3659f05704dcebe5ed813f75b5ceb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 18 Mar 2014 16:02:56 +0100 Subject: erts: Increase MAXPATHLEN to 4096 for windows --- erts/emulator/sys/win32/erl_win_sys.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 0deb097b1a..14db52476a 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-2012. All Rights Reserved. + * Copyright Ericsson AB 1997-2014. All Rights Reserved. * * The 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,16 +60,18 @@ #include #undef WIN32_LEAN_AND_MEAN -/* - * Define MAXPATHLEN in terms of MAXPATH if available. - */ - -#ifndef MAXPATH -#define MAXPATH MAX_PATH -#endif /* MAXPATH */ #ifndef MAXPATHLEN -#define MAXPATHLEN MAXPATH +#define MAXPATHLEN 4096 +/* + erts-6.0 (OTP 17.0): + We now accept windows paths longer than 260 (MAX_PATH) by conversion to + UNC path format. In order to also return long paths from the driver we + increased MAXPATHLEN from 260 to larger (but arbitrary) value 4096. + It would of course be nicer to instead dynamically allocate large enough + tmp buffers when efile_drv needs to return really long paths, and do that + for unix as well. + */ #endif /* MAXPATHLEN */ /* -- cgit v1.2.3 From 921c6c64cd8168956f0c5f09c20d1fa0a4753edf Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 20 Mar 2014 15:00:09 +0100 Subject: erts: Fix file:list_dir for windows paths 258 or 259 chars long Appending wildcard "\*" made the path too long (>= 260 chars). --- erts/emulator/drivers/win32/win_efile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index 9621fcca45..fb746f1f54 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -943,7 +943,7 @@ static int do_readdir(Efile_call_state* state, int length; WCHAR* s; - ensure_wpath(state, &wname); + ensure_wpath_max(state, &wname, MAX_PATH-2); length = wcslen(wname); wildcard = wpath_tmp_alloc(state, length+3); -- cgit v1.2.3 From 293543db1ca5fc17a24e7dc2562d376ea186b009 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 20 Mar 2014 15:16:06 +0100 Subject: erts: Cleanup debug tracing in win_efile.c --- erts/emulator/drivers/win32/win_efile.c | 98 ++++++++++++++++----------------- 1 file changed, 49 insertions(+), 49 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index fb746f1f54..7ab0e09072 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -29,20 +29,20 @@ #include #include "erl_efile.h" -// 1 = file name ops -// 2 = file descr ops -// 4 = errors -// 8 = path name conversion -#define SVERK_TRACE_MASK 0 - -#if !SVERK_TRACE_MASK -# define SVERK_TRACE(M,S) -# define SVERK_TRACE1(M,FMT,A) -# define SVERK_TRACE2(M,FMT,A,B) +#define DBG_TRACE_MASK 0 +/* 1 = file name ops + * 2 = file descr ops + * 4 = errors + * 8 = path name conversion + */ +#if !DBG_TRACE_MASK +# define DBG_TRACE(M,S) +# define DBG_TRACE1(M,FMT,A) +# define DBG_TRACE2(M,FMT,A,B) #else -# define SVERK_TRACE(M,S) do { if ((M)&SVERK_TRACE_MASK) fwprintf(stderr, L"SVERK TRACE %d: %s\r\n", __LINE__, (WCHAR*)(S)); }while(0) -# define SVERK_TRACE1(M,FMT,A) do { if ((M)&SVERK_TRACE_MASK) fwprintf(stderr, L"SVERK TRACE %d: " L##FMT L"\r\n", __LINE__, (A)); }while(0) -# define SVERK_TRACE2(M,FMT,A,B) do { if ((M)&SVERK_TRACE_MASK) fwprintf(stderr, L"SVERK TRACE %d: " L##FMT L"\r\n", __LINE__, (A), (B)); }while(0) +# define DBG_TRACE(M,S) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: %s\r\n", __LINE__, (WCHAR*)(S)); }while(0) +# define DBG_TRACE1(M,FMT,A) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: " L##FMT L"\r\n", __LINE__, (A)); }while(0) +# define DBG_TRACE2(M,FMT,A,B) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: " L##FMT L"\r\n", __LINE__, (A), (B)); }while(0) #endif /* @@ -275,7 +275,7 @@ check_error(int result, Efile_error* errInfo) if (result < 0) { errInfo->posix_errno = errno; errInfo->os_errno = GetLastError(); - SVERK_TRACE2(4, "ERROR os_error=%d errno=%d @@@@@@@@@@@@@@@@@@@@@@@@@@@@", + DBG_TRACE2(4, "ERROR os_error=%d errno=%d @@@@@@@@@@@@@@@@@@@@@@@@@@@@", errInfo->os_errno, errInfo->posix_errno); return 0; } @@ -287,7 +287,7 @@ save_last_error(Efile_error* errInfo) { errInfo->posix_errno = errno; errInfo->os_errno = GetLastError(); - SVERK_TRACE2(4, "ERROR os_error=%d errno=%d $$$$$$$$$$$$$$$$$$$$$$$$$$$$$", + DBG_TRACE2(4, "ERROR os_error=%d errno=%d $$$$$$$$$$$$$$$$$$$$$$$$$$$$$", errInfo->os_errno, errInfo->posix_errno); } @@ -312,7 +312,7 @@ set_os_errno(Efile_error* errInfo, DWORD os_errno) { errInfo->os_errno = os_errno; errInfo->posix_errno = errno_map(os_errno); - SVERK_TRACE2(4, "ERROR os_error=%d errno=%d ############################", + DBG_TRACE2(4, "ERROR os_error=%d errno=%d ############################", errInfo->os_errno, errInfo->posix_errno); return 0; } @@ -359,11 +359,11 @@ static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max) int unc_fixup = 0; if (path[0] == 0) { - SVERK_TRACE(8,"Let empty path pass through"); + DBG_TRACE(8, L"Let empty path pass through"); return; } - SVERK_TRACE1(8,"IN: %s", path); + DBG_TRACE1(8,"IN: %s", path); if (path[1] == L':' && ISSLASH(path[2])) { /* absolute path */ if (len >= max) { @@ -396,7 +396,7 @@ static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max) fullLen = GetFullPathNameW(path, 4 + absLen, fullPath+4, NULL); if (fullLen >= 4+absLen) { *pathp = path; - SVERK_TRACE2(8,"ensure_wpath FAILED absLen=%u %s", (int)absLen, path); + DBG_TRACE2(8,"ensure_wpath FAILED absLen=%u %s", (int)absLen, path); return; } /* GetFullPathNameW can return paths longer than MAX_PATH without the \\?\ prefix. @@ -443,7 +443,7 @@ static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max) } } } - SVERK_TRACE1(8,"OUT: %s", *pathp); + DBG_TRACE1(8,"OUT: %s", *pathp); } int @@ -454,7 +454,7 @@ efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ WCHAR* wname = (WCHAR*)name; int ret; - SVERK_TRACE(1, name); + DBG_TRACE(1, name); call_state_init(&state, errInfo); ensure_wpath_max(&state, &wname, 248); /* Yes, 248 limit for normal paths */ @@ -473,7 +473,7 @@ efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ Efile_call_state state; int ret; - SVERK_TRACE(1, name); + DBG_TRACE(1, name); call_state_init(&state, errInfo); ret = do_rmdir(&state, name); call_state_free(&state); @@ -567,7 +567,7 @@ efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ { Efile_call_state state; int ret; - SVERK_TRACE(1, name); + DBG_TRACE(1, name); call_state_init(&state, errInfo); ret = do_delete_file(&state, name); call_state_free(&state); @@ -657,7 +657,7 @@ efile_rename(Efile_error* errInfo, char* src, char* dst) { Efile_call_state state; int ret; - SVERK_TRACE(1, src); + DBG_TRACE(1, src); call_state_init(&state, errInfo); ret = do_rename(&state, src, dst); call_state_free(&state); @@ -892,18 +892,18 @@ efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */ { WCHAR *wbuffer = (WCHAR *) buffer; size_t wbuffer_size = size / 2; - SVERK_TRACE(1, L"#getdcwd#"); + DBG_TRACE(1, L"#getdcwd#"); if (_wgetdcwd(drive, wbuffer, wbuffer_size) == NULL) { return check_error(-1, errInfo); } - SVERK_TRACE1(8, "getdcwd OS=%s", wbuffer); + DBG_TRACE1(8, "getdcwd OS=%s", wbuffer); if (wcsncmp(wbuffer, L"\\\\?\\", 4) == 0) { wmemmove(wbuffer, wbuffer+4, wcslen(wbuffer+4)+1); } for ( ; *wbuffer; wbuffer++) if (*wbuffer == L'\\') *wbuffer = L'/'; - SVERK_TRACE1(8, "getdcwd ERLANG=%s", (WCHAR*)buffer); + DBG_TRACE1(8, "getdcwd ERLANG=%s", (WCHAR*)buffer); return 1; } @@ -913,7 +913,7 @@ efile_readdir(Efile_error* errInfo, char* name, EFILE_DIR_HANDLE* dir_handle, { Efile_call_state state; int ret; - SVERK_TRACE(dir_handle?2:1, name); + DBG_TRACE(dir_handle?2:1, name); call_state_init(&state, errInfo); ret = do_readdir(&state, name, dir_handle, buffer, size); call_state_free(&state); @@ -1000,7 +1000,7 @@ efile_openfile(Efile_error* errInfo, char* name, int flags, int* pfd, Sint64* pS { Efile_call_state state; int ret; - SVERK_TRACE1(1, "openfile(%s)", name); + DBG_TRACE1(1, "openfile(%s)", name); call_state_init(&state, errInfo); ret = do_openfile(&state, name, flags, pfd, pSize); call_state_free(&state); @@ -1101,7 +1101,7 @@ efile_may_openfile(Efile_error* errInfo, char *name) DWORD attr; int ret; - SVERK_TRACE(1, name); + DBG_TRACE(1, name); call_state_init(&state, errInfo); ensure_wpath(&state, &wname); if ((attr = GetFileAttributesW(wname)) == INVALID_FILE_ATTRIBUTES) { @@ -1122,7 +1122,7 @@ void efile_closefile(fd) int fd; /* File descriptor for file to close. */ { - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); CloseHandle((HANDLE) fd); } @@ -1143,7 +1143,7 @@ efile_fdatasync(errInfo, fd) Efile_error* errInfo; /* Where to return error codes. */ int fd; /* File descriptor for file to sync. */ { - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); /* Not available in Windows, just call regular fsync */ return efile_fsync(errInfo, fd); } @@ -1153,7 +1153,7 @@ efile_fsync(errInfo, fd) Efile_error* errInfo; /* Where to return error codes. */ int fd; /* File descriptor for file to sync. */ { - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); if (!FlushFileBuffers((HANDLE) fd)) { return check_error(-1, errInfo); } @@ -1166,7 +1166,7 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, { Efile_call_state state; int ret; - SVERK_TRACE(1, L""); + DBG_TRACE(1, L""); call_state_init(&state, errInfo); ret = do_fileinfo(&state, pInfo, orig_name, info_for_link); call_state_free(&state); @@ -1365,7 +1365,7 @@ do_write_info(Efile_call_state* state, DWORD tempAttr; WCHAR *wname = (WCHAR *) name; - SVERK_TRACE(1, name); + DBG_TRACE(1, name); ensure_wpath(state, &wname); @@ -1446,7 +1446,7 @@ size_t count; /* Number of bytes to write. */ Sint64 offset; /* where to write it */ { int res; - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); if (res) { return efile_write(errInfo, EFILE_MODE_WRITE, fd, buf, count); @@ -1466,7 +1466,7 @@ size_t count; /* Number of bytes to read. */ size_t* pBytesRead; /* Where to return number of bytes read. */ { int res; - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); if (res) { return efile_read(errInfo, EFILE_MODE_READ, fd, buf, count, pBytesRead); @@ -1489,7 +1489,7 @@ size_t count; /* Number of bytes to write. */ OVERLAPPED overlapped; OVERLAPPED* pOverlapped = NULL; - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); if (flags & EFILE_MODE_APPEND) { memset(&overlapped, 0, sizeof(overlapped)); overlapped.Offset = 0xffffffff; @@ -1519,7 +1519,7 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */ OVERLAPPED overlapped; OVERLAPPED* pOverlapped = NULL; - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); ASSERT(iovcnt >= 0); if (flags & EFILE_MODE_APPEND) { @@ -1557,7 +1557,7 @@ size_t* pBytesRead; /* Where to return number of bytes read. */ { DWORD nbytes = 0; - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); if (!ReadFile((HANDLE) fd, buf, count, &nbytes, NULL)) return set_error(errInfo); @@ -1577,7 +1577,7 @@ Sint64* new_location; /* Resulting new location in file. */ { LARGE_INTEGER off, new_loc; - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); switch (origin) { case EFILE_SEEK_SET: origin = FILE_BEGIN; break; case EFILE_SEEK_CUR: origin = FILE_CURRENT; break; @@ -1609,7 +1609,7 @@ Efile_error* errInfo; /* Where to return error codes. */ int *fd; /* File descriptor for file to truncate. */ int flags; { - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); if (!SetEndOfFile((HANDLE) (*fd))) return set_error(errInfo); return 1; @@ -1768,7 +1768,7 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) { Efile_call_state state; int ret; - SVERK_TRACE(1, name); + DBG_TRACE(1, name); call_state_init(&state, errInfo); ret = !!do_readlink(&state, name, buffer, size); call_state_free(&state); @@ -1863,7 +1863,7 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size) { Efile_call_state state; int ret; - SVERK_TRACE(1, orig_name); + DBG_TRACE(1, orig_name); call_state_init(&state, errInfo); ret = do_altname(&state, orig_name, buffer, size); call_state_free(&state); @@ -1956,7 +1956,7 @@ efile_link(Efile_error* errInfo, char* old, char* new) WCHAR *wold = (WCHAR *) old; WCHAR *wnew = (WCHAR *) new; int ret; - SVERK_TRACE(1, old); + DBG_TRACE(1, old); call_state_init(&state, errInfo); ensure_wpath(&state, &wold); ensure_wpath(&state, &wnew); @@ -1973,7 +1973,7 @@ efile_symlink(Efile_error* errInfo, char* old, char* new) { Efile_call_state state; int ret; - SVERK_TRACE2(1, "symlink(%s <- %s)", old, new); + DBG_TRACE2(1, "symlink(%s <- %s)", old, new); call_state_init(&state, errInfo); ret = do_symlink(&state, old, new); call_state_free(&state); @@ -1991,7 +1991,7 @@ do_symlink(Efile_call_state* state, char* old, char* new) WCHAR *wold = (WCHAR *) old; WCHAR *wnew = (WCHAR *) new; - SVERK_TRACE(1, old); + DBG_TRACE(1, old); if ((hModule = LoadLibrary("kernel32.dll")) != NULL) { typedef BOOLEAN (WINAPI * CREATESYMBOLICLINKFUNCPTR) ( LPCWSTR lpSymlinkFileName, @@ -2030,7 +2030,7 @@ int efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length, int advise) { - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); /* posix_fadvise is not available on Windows, do nothing */ errno = ERROR_SUCCESS; return check_error(0, errInfo); @@ -2039,7 +2039,7 @@ efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, int efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length) { - SVERK_TRACE(2, L""); + DBG_TRACE(2, L""); /* No file preallocation method available in Windows. */ errno = errno_map(ERROR_NOT_SUPPORTED); SetLastError(ERROR_NOT_SUPPORTED); -- cgit v1.2.3 From c8b55a755da15e766a745a948f3d8020f581fd37 Mon Sep 17 00:00:00 2001 From: Scott Lystig Fritchie Date: Tue, 25 Mar 2014 18:34:11 +0900 Subject: Fix DTrace/SystemTap-related formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to Michał Ptaszek for bringing this embarrassing formatting error to my attention. Many DTrace/SystemTap trace strings are incorrectly truncated at 4/8 bytes, depending on the CPU word size. This patch expands the work from commit d032e097 by zheng siyao. Michał's report to the erlang-bugs list can be found at: http://erlang.org/pipermail/erlang-bugs/2014-March/004250.html Conflicts: erts/emulator/beam/beam_emu.c erts/emulator/beam/copy.c erts/emulator/beam/dist.c erts/emulator/beam/erl_async.c erts/emulator/beam/erl_bif_port.c erts/emulator/beam/erl_port_task.c erts/emulator/beam/erl_process.c erts/emulator/beam/io.c --- erts/emulator/beam/erl_message.c | 6 ++++-- erts/emulator/beam/erl_port_task.c | 2 +- erts/emulator/beam/io.c | 2 +- erts/emulator/sys/common/erl_check_io.c | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 6a9030fd99..0eb8117980 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -896,8 +896,10 @@ erts_send_message(Process* sender, #ifdef USE_VM_PROBES *sender_name = *receiver_name = '\0'; if (DTRACE_ENABLED(message_send)) { - erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), "%T", sender->common.id); - erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), "%T", receiver->common.id); + erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), + "%T", sender->common.id); + erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), + "%T", receiver->common.id); } #endif if (SEQ_TRACE_TOKEN(sender) != NIL && !(flags & ERTS_SND_FLG_NO_SEQ_TRACE)) { diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index d6c0c9e78e..e9665c0142 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -2031,7 +2031,7 @@ begin_port_cleanup(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p) erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", pp->common.id); while (plp2 != NULL) { - erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->pid); + erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->pid); DTRACE2(process_port_unblocked, pid_str, port_str); } } diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 920701c086..6bb3ffd8ce 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4717,7 +4717,7 @@ erts_port_resume_procs(Port *prt) erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", prt->common.id); while (plp2 != NULL) { - erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->pid); + erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->pid); DTRACE2(process_port_unblocked, pid_str, port_str); } } diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 7035dc77df..8e2f4cefa3 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -524,7 +524,8 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, /* fast track to stop_select callback */ stop_select_fn = prt->drv_ptr->stop_select; #ifdef USE_VM_PROBES - strncpy(name, prt->drv_ptr->name, sizeof(name)-1); + strncpy(name, prt->drv_ptr->name, + sizeof(DTRACE_CHARBUF_NAME(name))-1); name[sizeof(name)-1] = '\0'; #endif ret = 0; -- cgit v1.2.3 From 64ffc509c5069e29c5d172dfcad6655efbeafd9b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 25 Mar 2014 14:23:45 +0100 Subject: erts: Fix compile warning in io.c for windows --- erts/emulator/beam/io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index cd5060ebb3..0480cdf823 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -914,8 +914,8 @@ int erts_port_handle_xports(Port *prt) (iov)->iov_base = (ptr); \ (iov)->iov_len = (len); \ if (sizeof((iov)->iov_len) < sizeof(len) \ - /* Check if (len) overflowed (iov)->iov_len */ \ - && ((len) >> (sizeof((iov)->iov_len)*CHAR_BIT)) != 0) { \ + /* Check if (len) overflowed (iov)->iov_len */ \ + && (iov)->iov_len != (len)) { \ goto L_overflow; \ } \ *(bv)++ = (bin); \ -- cgit v1.2.3 From f6570cf6201bf6476d903def9b04fe9c33f13a3d Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 17 Mar 2014 15:49:50 +0100 Subject: ose: Break lmconf into one per load module --- erts/aclocal.m4 | 6 ++++-- erts/emulator/Makefile.in | 2 +- erts/emulator/sys/ose/beam.lmconf | 26 ++++++++++++++++++++++++++ erts/emulator/sys/ose/default.lmconf | 25 ------------------------- erts/epmd/src/Makefile.in | 2 +- erts/etc/common/Makefile.in | 2 +- erts/etc/ose/etc.lmconf | 20 ++++++++++++++++++++ 7 files changed, 53 insertions(+), 30 deletions(-) create mode 100644 erts/emulator/sys/ose/beam.lmconf delete mode 100644 erts/emulator/sys/ose/default.lmconf create mode 100644 erts/etc/ose/etc.lmconf (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index c51c26794a..2b47f7c4bc 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -83,8 +83,10 @@ AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)]) AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)]) AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_LM_CONF, [OSE load module default configuration file (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE OSE confd source file]) +AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)]) +AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file]) AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file]) ]) diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 523130d01a..c6c9b4cf8c 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -1031,7 +1031,7 @@ else ifeq ($(findstring ose,$(TARGET)),ose) $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(LCF) $(call build-ose-load-module, $@, $(INIT_OBJS) $(OBJS), $(STATIC_NIF_LIBS) \ - $(STATIC_DRIVER_LIBS) $(LIBS), $(LMCONF)) + $(STATIC_DRIVER_LIBS) $(LIBS), $(BEAM_LMCONF)) else $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) diff --git a/erts/emulator/sys/ose/beam.lmconf b/erts/emulator/sys/ose/beam.lmconf new file mode 100644 index 0000000000..2cee836f9a --- /dev/null +++ b/erts/emulator/sys/ose/beam.lmconf @@ -0,0 +1,26 @@ +OSE_LM_STACK_SIZES=256,512,1024,2048,4096,8192,16384,65536 +OSE_LM_SIGNAL_SIZES=31,63,127,255,1023,4095,16383,65535 +OSE_LM_POOL_SIZE=0x200000 +OSE_LM_MAIN_NAME=main +OSE_LM_MAIN_STACK_SIZE=0xF000 +OSE_LM_MAIN_PRIORITY=20 +## Has to be of a type that allows MAM +OSE_LM_PROGRAM_TYPE=APP_RAM +OSE_LM_DATA_INIT=YES +OSE_LM_BSS_INIT=YES +OSE_LM_EXEC_MODEL=SHARED +HEAP_MAX_SIZE=1000000000 +HEAP_SMALL_BUF_INIT_SIZE=64000000 +HEAP_LARGE_BUF_THRESHOLD=16000000 +HEAP_LOCK_TYPE=2 + +ERTS_DEFAULT_PRIO=24 +ERTS_SCHEDULER_PRIO=24 +ERTS_ASYNC_PRIO=22 +ERTS_AUX_PRIO=24 +ERTS_SYS_MSG_DISPATCHER_PRIO=21 + +# Setting the environment variable EFS_RESOLVE_TMO on the block to 0. +# This will eliminiate delays when trying to open files on not mounted +# volumes. +EFS_RESOLVE_TMO=0 diff --git a/erts/emulator/sys/ose/default.lmconf b/erts/emulator/sys/ose/default.lmconf deleted file mode 100644 index a66b0ece56..0000000000 --- a/erts/emulator/sys/ose/default.lmconf +++ /dev/null @@ -1,25 +0,0 @@ -OSE_LM_STACK_SIZES=256,512,1024,2048,4096,8192,16384,65536 -OSE_LM_SIGNAL_SIZES=31,63,127,255,1023,4095,16383,65535 -OSE_LM_POOL_SIZE=0x200000 -OSE_LM_MAIN_NAME=main -OSE_LM_MAIN_STACK_SIZE=0xF000 -OSE_LM_MAIN_PRIORITY=20 -OSE_LM_PROGRAM_TYPE=APP_RAM -OSE_LM_DATA_INIT=YES -OSE_LM_BSS_INIT=YES -OSE_LM_EXEC_MODEL=SHARED -HEAP_MAX_SIZE=1000000000 -HEAP_SMALL_BUF_INIT_SIZE=64000000 -HEAP_LARGE_BUF_THRESHOLD=16000000 -HEAP_LOCK_TYPE=2 - -ERTS_DEFAULT_PRIO=24 -ERTS_SCHEDULER_PRIO=24 -ERTS_ASYNC_PRIO=22 -ERTS_AUX_PRIO=24 -ERTS_SYS_MSG_DISPATCHER_PRIO=21 - -# Setting the environment variable EFS_RESOLVE_TMO on the block to 0. -# This will eliminiate delays when trying to open files on not mounted -# volumes. -EFS_RESOLVE_TMO=0 diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in index 2ea8630491..8dc8dae5f6 100644 --- a/erts/epmd/src/Makefile.in +++ b/erts/epmd/src/Makefile.in @@ -148,7 +148,7 @@ endif ifeq ($(findstring ose,$(TARGET)),ose) $(BINDIR)/$(EPMD): $(EPMD_OBJS) $(ERTS_LIB) $(OSE_LM_OBJS) - $(call build-ose-load-module, $@, $(EPMD_OBJS) $(OSE_LM_OBJS), $(LIBS), $(LMCONF)) + $(call build-ose-load-module, $@, $(EPMD_OBJS) $(OSE_LM_OBJS), $(LIBS), $(EPMD_LMCONF)) else $(BINDIR)/$(EPMD): $(EPMD_OBJS) $(ERTS_LIB) $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(EPMD_OBJS) $(LIBS) diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index 5c2cd8aded..f74c670731 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -507,7 +507,7 @@ $(OBJDIR)/crt0_lm.o: $(CRT0_LM) OSE_LM_OBJS += $(OBJDIR)/ose_confd.o $(OBJDIR)/crt0_lm.o $(BINDIR)/run_erl_lm: $(OBJDIR)/run_erl_main.o $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(OBJDIR)/run_erl_common.o $(OBJDIR)/to_erl_common.o $(OSE_LM_OBJS) - $(call build-ose-load-module, $@, $^, $(LIBS), $(LMCONF)) + $(call build-ose-load-module, $@, $^, $(LIBS), $(RUN_ERL_LMCONF)) $(OBJDIR)/run_erl_main.o: $(OSEETC)/run_erl_main.c $(OSEETC)/run_erl.h ../common/to_erl_common.h $(RC_GENERATED) diff --git a/erts/etc/ose/etc.lmconf b/erts/etc/ose/etc.lmconf new file mode 100644 index 0000000000..b402b325b1 --- /dev/null +++ b/erts/etc/ose/etc.lmconf @@ -0,0 +1,20 @@ +OSE_LM_STACK_SIZES=256,512,1024,2048,4096,8192,16384,65536 +OSE_LM_SIGNAL_SIZES=31,63,127,255,1023,4095,16383,65535 +OSE_LM_POOL_SIZE=0x200000 +OSE_LM_MAIN_NAME=main +OSE_LM_MAIN_STACK_SIZE=0xF000 +OSE_LM_MAIN_PRIORITY=20 +## Has to be of a type that allows MAM +OSE_LM_PROGRAM_TYPE=APP_RAM +OSE_LM_DATA_INIT=YES +OSE_LM_BSS_INIT=YES +OSE_LM_EXEC_MODEL=SHARED +HEAP_MAX_SIZE=1000000000 +HEAP_SMALL_BUF_INIT_SIZE=64000000 +HEAP_LARGE_BUF_THRESHOLD=16000000 +HEAP_LOCK_TYPE=2 + +# Setting the environment variable EFS_RESOLVE_TMO on the block to 0. +# This will eliminiate delays when trying to open files on not mounted +# volumes. +EFS_RESOLVE_TMO=0 -- cgit v1.2.3 From 675e046c886f358fdd80d9b818282cf714b577d2 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 17 Mar 2014 16:43:20 +0100 Subject: ose: Initiate stdin/stdout/stderr Needed to get std io to work in some OSE systems where stdio is not opened when the program starts. --- erts/emulator/sys/ose/erl_main.c | 2 ++ erts/etc/ose/run_erl_main.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/sys/ose/erl_main.c b/erts/emulator/sys/ose/erl_main.c index 03119c3fec..23a9bc93a4 100644 --- a/erts/emulator/sys/ose/erl_main.c +++ b/erts/emulator/sys/ose/erl_main.c @@ -30,6 +30,8 @@ int main(int argc, char **argv) { + (void)stdin;(void)stdout;(void)stderr; + /* When starting using pm_create -c ARGV="-- -root ..", argv[0] is the first part of ARGV and not the name of the executable. So we shuffle some pointers here to make erl_start happy. */ diff --git a/erts/etc/ose/run_erl_main.c b/erts/etc/ose/run_erl_main.c index d396ebe93b..2d92924ff2 100644 --- a/erts/etc/ose/run_erl_main.c +++ b/erts/etc/ose/run_erl_main.c @@ -45,6 +45,8 @@ int main(int argc, char **argv) char run_erl_usage[320], to_erl_usage[120]; + (void)stdin;(void)stdout;(void)stderr; + sprintf(run_erl_usage,RUN_ERL_USAGE,"run_erl [-daemon] [-block blockname]"); sprintf(to_erl_usage,TO_ERL_USAGE,"pipename"); -- cgit v1.2.3 From ff3ac97b2fedf6621de5f3442a3b81f29f08daae Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 14 Mar 2014 16:18:33 +0100 Subject: ose: erlang display goes to ramlog printf --- erts/doc/src/erlang.xml | 2 +- erts/emulator/sys/ose/sys.c | 60 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index aeded7c719..e06c56daac 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -887,7 +887,7 @@ Print a term on standard output

Prints a text representation of Term on the standard - output.

+ output. On OSE the term is printed to the ramlog.

This BIF is intended for debugging only.

diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index 6e6303820c..f1bac6897a 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -370,6 +370,63 @@ thr_create_prepare_child(void *vtcdp) #endif /* #ifdef USE_THREADS */ +/* The two functions below are stolen from win_con.c + They have to use malloc/free/realloc directly becasue + we want to do able to do erts_printf very early on. + */ +#define VPRINTF_BUF_INC_SIZE 128 +static erts_dsprintf_buf_t * +grow_vprintf_buf(erts_dsprintf_buf_t *dsbufp, size_t need) +{ + char *buf; + size_t size; + + ASSERT(dsbufp); + + if (!dsbufp->str) { + size = (((need + VPRINTF_BUF_INC_SIZE - 1) + / VPRINTF_BUF_INC_SIZE) + * VPRINTF_BUF_INC_SIZE); + buf = (char *) malloc(size * sizeof(char)); + } + else { + size_t free_size = dsbufp->size - dsbufp->str_len; + + if (need <= free_size) + return dsbufp; + + size = need - free_size + VPRINTF_BUF_INC_SIZE; + size = (((size + VPRINTF_BUF_INC_SIZE - 1) + / VPRINTF_BUF_INC_SIZE) + * VPRINTF_BUF_INC_SIZE); + size += dsbufp->size; + buf = (char *) realloc((void *) dsbufp->str, + size * sizeof(char)); + } + if (!buf) + return NULL; + if (buf != dsbufp->str) + dsbufp->str = buf; + dsbufp->size = size; + return dsbufp; +} + +static int erts_sys_ramlog_printf(char *format, va_list arg_list) +{ + int res,i; + erts_dsprintf_buf_t dsbuf = ERTS_DSPRINTF_BUF_INITER(grow_vprintf_buf); + res = erts_vdsprintf(&dsbuf, format, arg_list); + if (res >= 0) { + for (i = 0; i < dsbuf.str_len; i+= 50) + /* We print 50 characters at a time because otherwise + the ramlog looks broken */ + ramlog_printf("%.*s",dsbuf.str_len-50 < 0?dsbuf.str_len:50,dsbuf.str+i); + } + if (dsbuf.str) + free((void *) dsbuf.str); + return res; +} + void erts_sys_pre_init(void) { @@ -408,6 +465,9 @@ erts_sys_pre_init(void) children_died = 0; #endif #endif /* USE_THREADS */ + + erts_printf_stdout_func = erts_sys_ramlog_printf; + erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0); } -- cgit v1.2.3 From c4e7924f48e629319a8a0f74e73116319ba98423 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 13 Mar 2014 18:37:02 +0100 Subject: ose: Prinout errno when to_erl read fails --- erts/etc/common/to_erl_common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/common/to_erl_common.c b/erts/etc/common/to_erl_common.c index a49be44b6c..ab706fffe0 100644 --- a/erts/etc/common/to_erl_common.c +++ b/erts/etc/common/to_erl_common.c @@ -126,7 +126,8 @@ static int version_handshake(char* buf, int len, int wfd); #define READ_AIO(REQ,FD,SIZE,BUFF) \ SET_AIO(REQ,FD,SIZE,BUFF); \ if (aio_read(&(REQ)) != 0) \ - fprintf(stderr,"aio_read of child_read_req(%d) failed\n",FD) + fprintf(stderr,"aio_read of child_read_req(%d) failed" \ + "with error %d\n",FD,errno) union SIGNAL { SIGSELECT signo; -- cgit v1.2.3 From f4522575a15dd41caf13d2c36eb454407fb3836e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 17 Mar 2014 18:05:09 +0100 Subject: ose: Print faults in aio sys driver calls --- erts/emulator/sys/ose/sys.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index f1bac6897a..aaf515876f 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -195,7 +195,9 @@ static volatile int children_died; write_buff += sizeof(struct aiocb *); \ memcpy(write_buff,BUFF,SIZE+1); \ SET_AIO(*write_req,FD,SIZE,write_buff); \ - aio_write(write_req); \ + if (aio_write(write_req)) \ + ramlog_printf("%s:%d: write failed with %d\n", \ + __FILE__,__LINE__,errno); \ } \ } while(0) @@ -213,7 +215,9 @@ static volatile int children_died; /* When we have several schedulers, we need to make sure * that scheduler issuing aio_dispatch() is the owner on the signal */ #define DISPATCH_AIO(sig) do { \ - aio_dispatch(sig); \ + if (aio_dispatch(sig)) \ + ramlog_printf("%s:%d: dispatch failed with %d\n", \ + __FILE__,__LINE__,errno); \ } while(0) @@ -843,7 +847,11 @@ set_driver_data(ErlDrvPort port_num, /* READ */ if (read_write & DO_READ) { - efs_examine_fd(ifd, FLIB_FD_HANDLE, &driver_data[ifd].handle, 0); + EfsStatus res = efs_examine_fd(ifd, FLIB_FD_HANDLE, + &driver_data[ifd].handle, 0); + if (res != EFS_SUCCESS) + ramlog_printf("%s:%d: efs_examine_fd(%d) failed with %d\n", + __FILE__,__LINE__,ifd,errno); driver_data[ifd].ifd = ifd; driver_data[ifd].packet_bytes = packet_bytes; driver_data[ifd].port_num = port_num; @@ -885,7 +893,9 @@ set_driver_data(ErlDrvPort port_num, (void) driver_select(port_num, driver_data[ifd].input_event, (ERL_DRV_READ | ERL_DRV_USE), 1); - aio_read(&driver_data[ifd].aiocb); + if (aio_read(&driver_data[ifd].aiocb)) + ramlog_printf("%s:%d: aio_read(%d) failed with %d\n", + __FILE__,__LINE__,ifd,errno); } else { /* WRITE ONLY */ efs_examine_fd(ofd, FLIB_FD_HANDLE, &driver_data[ofd].handle, 0); @@ -1420,7 +1430,9 @@ static void ready_input(ErlDrvData drv_data, ErlDrvEvent ready_fd) memset((void *)data->aiocb.aio_buf, 0, 255); if (res > 0) { - aio_read(&data->aiocb); + if (aio_read(&data->aiocb)) + ramlog_printf("%s:%d: aio_read(%d) failed with %d\n", + __FILE__,__LINE__,data->ifd,errno); } } sig = erl_drv_ose_get_signal(ready_fd); -- cgit v1.2.3 From d5df89a921dbea5711f07b15d1bc595ffc9aa56c Mon Sep 17 00:00:00 2001 From: Robert Paal Date: Fri, 21 Mar 2014 13:44:23 +0100 Subject: ose: Yielding has to be done differently for background processes. --- erts/include/internal/ethread.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 64f1fae6d8..54acd1295a 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -396,6 +396,18 @@ extern ethr_runtime_t ethr_runtime__; #include "ethr_atomics.h" /* The atomics API */ +#if defined (ETHR_OSE_THREADS) +static ETHR_INLINE void +ose_yield(void) +{ + if (get_ptype(current_process()) == OS_PRI_PROC) { + set_pri(get_pri(current_process())); + } else { + delay(1); + } +} +#endif + #if defined(__GNUC__) && !defined(ETHR_OSE_THREADS) # ifndef ETHR_SPIN_BODY # if defined(__i386__) || defined(__x86_64__) @@ -414,9 +426,9 @@ extern ethr_runtime_t ethr_runtime__; # endif #elif defined(ETHR_OSE_THREADS) # ifndef ETHR_SPIN_BODY -# define ETHR_SPIN_BODY set_pri(get_pri(current_process())) +# define ETHR_SPIN_BODY ose_yield() # else -# error "OSE should use set_pri(get_pri(current_process()))" +# error "OSE should use ose_yield()" # endif #endif @@ -449,7 +461,7 @@ extern ethr_runtime_t ethr_runtime__; # define ETHR_YIELD() (pthread_yield(), 0) # endif # elif defined(ETHR_OSE_THREADS) -# define ETHR_YIELD() (set_pri(get_pri(current_process())), 0) +# define ETHR_YIELD() (ose_yield(), 0) # else # define ETHR_YIELD() (ethr_compiler_barrier(), 0) # endif -- cgit v1.2.3 From 68c1740b8b2a23ea8caf46b024fc339e10f375f0 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Sun, 23 Mar 2014 22:20:36 +0100 Subject: ose: Add ifdefs for HAVE_UDP --- erts/emulator/drivers/common/inet_drv.c | 45 ++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 357a4b7bcb..a846df9bf3 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -200,6 +200,7 @@ typedef unsigned long long llu_t; #define HAVE_MULTICAST_SUPPORT +#define HAVE_UDP #define ERRNO_BLOCK WSAEWOULDBLOCK @@ -438,6 +439,8 @@ typedef unsigned long u_long; #include #endif +#define HAVE_UDP + /* SCTP support -- currently for UNIX platforms only: */ #undef HAVE_SCTP #if defined(HAVE_SCTP_H) @@ -1181,6 +1184,7 @@ static struct erl_drv_entry tcp_inet_driver_entry = +#ifdef HAVE_UDP static int packet_inet_init(void); static void packet_inet_stop(ErlDrvData); static void packet_inet_command(ErlDrvData, char*, ErlDrvSizeT); @@ -1230,6 +1234,7 @@ static struct erl_drv_entry udp_inet_driver_entry = NULL, inet_stop_select }; +#endif #ifdef HAVE_SCTP static struct erl_drv_entry sctp_inet_driver_entry = @@ -1293,6 +1298,7 @@ static int tcp_deliver(tcp_descriptor* desc, int len); static int tcp_inet_output(tcp_descriptor* desc, HANDLE event); static int tcp_inet_input(tcp_descriptor* desc, HANDLE event); +#ifdef HAVE_UDP typedef struct { inet_descriptor inet; /* common data structure (DON'T MOVE) */ int read_packets; /* Number of packets to read per invocation */ @@ -1304,6 +1310,7 @@ typedef struct { static int packet_inet_input(udp_descriptor* udesc, HANDLE event); static int packet_inet_output(udp_descriptor* udesc, HANDLE event); +#endif /* convert descriptor poiner to inet_descriptor pointer */ #define INETP(d) (&(d)->inet) @@ -1324,7 +1331,6 @@ static int async_ref = 0; /* async reference id generator */ static ErlDrvTermData am_ok; static ErlDrvTermData am_tcp; -static ErlDrvTermData am_udp; static ErlDrvTermData am_error; static ErlDrvTermData am_einval; static ErlDrvTermData am_inet_async; @@ -1334,10 +1340,13 @@ static ErlDrvTermData am_closed; static ErlDrvTermData am_tcp_passive; static ErlDrvTermData am_tcp_closed; static ErlDrvTermData am_tcp_error; -static ErlDrvTermData am_udp_passive; -static ErlDrvTermData am_udp_error; static ErlDrvTermData am_empty_out_q; static ErlDrvTermData am_ssl_tls; +#ifdef HAVE_UDP +static ErlDrvTermData am_udp; +static ErlDrvTermData am_udp_passive; +static ErlDrvTermData am_udp_error; +#endif #ifdef HAVE_SCTP static ErlDrvTermData am_sctp; static ErlDrvTermData am_sctp_passive; @@ -1545,6 +1554,7 @@ static void *realloc_wrapper(void *current, ErlDrvSizeT size){ # define SCTP_ANC_BUFF_SIZE INET_DEF_BUFFER/2 /* XXX: not very good... */ #endif +#ifdef HAVE_UDP static int load_ip_port(ErlDrvTermData* spec, int i, char* buf) { spec[i++] = ERL_DRV_INT; @@ -1579,6 +1589,7 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) } return i; } +#endif #ifdef HAVE_SCTP @@ -1745,10 +1756,12 @@ static void release_buffer(ErlDrvBinary* buf) } } +#ifdef HAVE_UDP static ErlDrvBinary* realloc_buffer(ErlDrvBinary* buf, ErlDrvSizeT newsz) { return driver_realloc_binary(buf, newsz); } +#endif /* use a TRICK, access the refc field to see if any one else has * a ref to this buffer then call driver_free_binary else @@ -3409,6 +3422,7 @@ static int tcp_error_message(tcp_descriptor* desc, int err) return erl_drv_output_term(desc->inet.dport, spec, i); } +#ifdef HAVE_UDP /* ** active mode message: ** {udp, S, IP, Port, [H1,...Hsz | Data]} or @@ -3499,6 +3513,7 @@ static int packet_binary_message ASSERT(i <= PACKET_ERL_DRV_TERM_DATA_LEN); return erl_drv_output_term(desc->dport, spec, i); } +#endif /* ** active mode message: send active-to-passive transition message @@ -3528,6 +3543,7 @@ static int packet_binary_message return erl_drv_output_term(desc->dport, spec, i); } +#ifdef HAVE_UDP /* ** send active message {udp_error|sctp_error, S, Error} */ @@ -3554,7 +3570,7 @@ static int packet_error_message(udp_descriptor* udesc, int err) ASSERT(i == sizeof(spec)/sizeof(*spec)); return erl_drv_output_term(desc->dport, spec, i); } - +#endif /* ** active=TRUE: @@ -3619,7 +3635,7 @@ tcp_reply_binary_data(tcp_descriptor* desc, ErlDrvBinary* bin, int offs, int len return code; } - +#ifdef HAVE_UDP static int packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz, ErlDrvBinary * bin, int offs, int len, @@ -3642,6 +3658,7 @@ packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz, return code; } } +#endif /* ---------------------------------------------------------------------------- @@ -3817,7 +3834,9 @@ static int inet_init() INIT_ATOM(ok); INIT_ATOM(tcp); +#ifdef HAVE_UDP INIT_ATOM(udp); +#endif INIT_ATOM(error); INIT_ATOM(einval); INIT_ATOM(inet_async); @@ -3827,8 +3846,10 @@ static int inet_init() INIT_ATOM(tcp_passive); INIT_ATOM(tcp_closed); INIT_ATOM(tcp_error); +#ifdef HAVE_UDP INIT_ATOM(udp_passive); INIT_ATOM(udp_error); +#endif INIT_ATOM(empty_out_q); INIT_ATOM(ssl_tls); @@ -3847,7 +3868,10 @@ static int inet_init() /* add TCP, UDP and SCTP drivers */ add_driver_entry(&tcp_inet_driver_entry); +#ifdef HAVE_UDP add_driver_entry(&udp_inet_driver_entry); +#endif + #ifdef HAVE_SCTP /* Check the size of SCTP AssocID -- currently both this driver and the Erlang part require 32 bit: */ @@ -5929,6 +5953,7 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) } continue; +#ifdef HAVE_UDP case INET_LOPT_UDP_READ_PACKETS: if (desc->stype == SOCK_DGRAM) { udp_descriptor* udesc = (udp_descriptor*) desc; @@ -5936,6 +5961,7 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) udesc->read_packets = ival; } continue; +#endif #ifdef HAVE_SETNS case INET_LOPT_NETNS: @@ -6902,6 +6928,7 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, } continue; +#ifdef HAVE_UDP case INET_LOPT_UDP_READ_PACKETS: if (desc->stype == SOCK_DGRAM) { *ptr++ = opt; @@ -6911,6 +6938,7 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, TRUNCATE_TO(0,ptr); } continue; +#endif #ifdef HAVE_SETNS case INET_LOPT_NETNS: @@ -10499,6 +10527,7 @@ static udp_descriptor* sctp_inet_copy(udp_descriptor* desc, SOCKET s, int* err) +#ifdef HAVE_UDP static int packet_inet_init() { return 0; @@ -10529,6 +10558,7 @@ static ErlDrvData udp_inet_start(ErlDrvPort port, char *args) set_default_msgq_limits(port); return data; } +#endif #ifdef HAVE_SCTP static ErlDrvData sctp_inet_start(ErlDrvPort port, char *args) @@ -10539,6 +10569,7 @@ static ErlDrvData sctp_inet_start(ErlDrvPort port, char *args) } #endif +#ifdef HAVE_UDP static void packet_inet_stop(ErlDrvData e) { /* There should *never* be any "empty out q" subscribers on @@ -11049,7 +11080,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) else inet_reply_ok(desc); } - +#endif #ifdef __WIN32__ static void packet_inet_event(ErlDrvData e, ErlDrvEvent event) @@ -11071,6 +11102,7 @@ static void packet_inet_event(ErlDrvData e, ErlDrvEvent event) #endif +#ifdef HAVE_UDP static void packet_inet_drv_input(ErlDrvData e, ErlDrvEvent event) { (void) packet_inet_input((udp_descriptor*)e, (HANDLE)event); @@ -11327,6 +11359,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) DEBUGF(("packet_inet_output(%ld) }\r\n", (long)desc->port)); return ret; } +#endif /*---------------------------------------------------------------------------*/ -- cgit v1.2.3 From 38db84be84daefe80806e6a4c06d3ed8c6c8db31 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Sun, 23 Mar 2014 22:21:38 +0100 Subject: ose: Implement tcp inet driver for OSE The inet driver for OSE has to handle signals instead of selects and thus the wrappers for ready_input/output are a little bit different. However the majority of the inet code remains the same. --- erts/emulator/drivers/common/inet_drv.c | 548 +++++++++++++++++++++++++++++--- erts/emulator/sys/ose/beam.lmconf | 2 +- erts/emulator/sys/ose/erl_poll.c | 7 +- 3 files changed, 510 insertions(+), 47 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index a846df9bf3..e33594b026 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -93,6 +93,10 @@ typedef unsigned long long llu_t; #define INT16_MAX (32767) #endif +#ifdef __OSE__ +#include "inet.h" +#endif + #ifdef __WIN32__ #define STRNCASECMP strncasecmp @@ -121,10 +125,6 @@ typedef unsigned long long llu_t; #undef WANT_NONBLOCKING #include "sys.h" -#ifdef __OSE__ -#include "inet.h" -#endif - #undef EWOULDBLOCK #undef ETIMEDOUT @@ -295,29 +295,55 @@ static unsigned long zero_value = 0; static unsigned long one_value = 1; #elif defined (__OSE__) + +/* + * Some notes about how inet (currently only tcp) works on OSE. + * The driver uses OSE signals to communicate with the one_inet + * process. Because of the difference in how signals and file descriptors + * work the whole select/deselect mechanic is very different. + * In ose when a sock_select is done a function is called. That function + * notes the changes that the driver want to do, but does not act on it. + * later when the function returns the new desired state is compared + * to the previous state and the apprioriate actions are taken. The action + * is usually to either request more data from the stack or stop requesting + * data. + * + * One thing to note is that the driver never does select/deselect. It always + * listens for the signals. Flow of data is regulated by sending or not sending + * signals to the ose inet process. + * + * The interesting functions to look at are: + * * inet_driver_select : called when sock_select is called + * * tcp_inet_ose_dispatch_signal : checks state changes and sends new signals + * * tcp_inet_drv_output_ose : ready output callback, reads signals and calls + * dispatch_signal + * * tcp_inet_drv_input_ose : ready input callback. + */ + +#include "efs.h" #include "sys/socket.h" #include "sys/uio.h" #include "sfk/sys/sfk_uio.h" #include "netinet/in.h" #include "netinet/tcp.h" #include "netdb.h" +#include "ose_spi/socket.sig" -ssize_t writev(int fd, const struct iovec *iov, int iovcnt) -{ - return 0; -} + +static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int max_sz); #define INVALID_SOCKET -1 #define INVALID_EVENT -1 #define SOCKET_ERROR -1 #define SOCKET int -#define HANDLE long int -#define FD_READ ERL_DRV_READ -#define FD_WRITE ERL_DRV_WRITE -#define FD_CLOSE 0 -#define FD_CONNECT ERL_DRV_WRITE -#define FD_ACCEPT ERL_DRV_READ +#define HANDLE int +#define FD_READ ERL_DRV_READ +#define FD_WRITE ERL_DRV_WRITE +#define FD_CLOSE 0 +#define FD_CONNECT (1<<4) +#define FD_ACCEPT (1<<5) +#define SOCK_FD_ERROR (1<<6) #define sock_connect(s, addr, len) connect((s), (addr), (len)) #define sock_listen(s, b) listen((s), (b)) @@ -336,11 +362,12 @@ ssize_t writev(int fd, const struct iovec *iov, int iovcnt) #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) #define sock_sendv(s, vec, size, np, flag) \ - (*(np) = writev((s), (struct iovec*)(vec), (size))) + (*(np) = writev_fallback((s), (struct iovec*)(vec), (size), (*(np)))) #define sock_sendmsg(s,msghdr,flag) sendmsg((s),(msghdr),(flag)) #define sock_open(af, type, proto) socket((af), (type), (proto)) #define sock_close(s) close((s)) +#define sock_dup(s) dup((s)) #define sock_shutdown(s, how) shutdown((s), (how)) #define sock_hostname(buf, len) gethostname((buf), (len)) @@ -356,19 +383,6 @@ ssize_t writev(int fd, const struct iovec *iov, int iovcnt) #define sock_create_event(d) ((d)->s) /* return file descriptor */ #define sock_close_event(e) /* do nothing */ -#define inet_driver_select(port, e, mode, on) \ - driver_select(port, e, mode | (on?ERL_DRV_USE:0), on) - -#define sock_select(d, flags, onoff) do { \ - ASSERT(!(d)->is_ignored); \ - (d)->event_mask = (onoff) ? \ - ((d)->event_mask | (flags)) : \ - ((d)->event_mask & ~(flags)); \ - DEBUGF(("sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX\r\n", \ - (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask)); \ - inet_driver_select((d)->port, (ErlDrvEvent)(long)(d)->event, (flags), (onoff)); \ - } while(0) - #ifndef WANT_NONBLOCKING #define WANT_NONBLOCKING #endif @@ -398,6 +412,16 @@ typedef unsigned long u_long; #define IN_EXPERIMENTAL(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xe0000000) #define IN_BADCLASS(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xf0000000) +#define sock_select(d, flags, onoff) do { \ + ASSERT(!(d)->is_ignored); \ + (d)->event_mask = (onoff) ? \ + ((d)->event_mask | (flags)) : \ + ((d)->event_mask & ~(flags)); \ + DEBUGF(("(%s / %d) sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX, s=%d\r\n", \ + __FILE__, __LINE__, (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask, (d)->s)); \ + inet_driver_select((d), (flags), (onoff)); \ + } while(0) + #else /* !__OSE__ && !__WIN32__ */ #include @@ -663,13 +687,13 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) (d)->event_mask = (onoff) ? \ ((d)->event_mask | (flags)) : \ ((d)->event_mask & ~(flags)); \ - DEBUGF(("sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX\r\n", \ - (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask)); \ + DEBUGF(("(%s / %d) sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX\r\n", \ + __FILE__, __LINE__, (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask)); \ inet_driver_select((d)->port, (ErlDrvEvent)(long)(d)->event, (flags), (onoff)); \ } while(0) -#endif /* #ifdef __WIN32__ #else */ +#endif /* !__WIN32__ && !__OSE__ */ #ifdef HAVE_SOCKLEN_T # define SOCKLEN_T socklen_t @@ -1122,6 +1146,13 @@ typedef struct { char *netns; /* Socket network namespace name as full file path */ #endif +#ifdef __OSE__ + int select_state; /* state to keep track of whether we + should trigger another read/write + request at end of ready_input/output */ + ErlDrvEvent events[6]; +#endif + } inet_descriptor; @@ -1137,8 +1168,10 @@ static void tcp_inet_stop(ErlDrvData); static void tcp_inet_command(ErlDrvData, char*, ErlDrvSizeT); static void tcp_inet_commandv(ErlDrvData, ErlIOVec*); static void tcp_inet_flush(ErlDrvData drv_data); +#ifndef __OSE__ static void tcp_inet_drv_input(ErlDrvData, ErlDrvEvent); static void tcp_inet_drv_output(ErlDrvData data, ErlDrvEvent event); +#endif static ErlDrvData tcp_inet_start(ErlDrvPort, char* command); static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData, unsigned int, char*, ErlDrvSizeT, char**, ErlDrvSizeT); @@ -1150,7 +1183,72 @@ static void tcp_inet_event(ErlDrvData, ErlDrvEvent); static void find_dynamic_functions(void); #endif -static struct erl_drv_entry tcp_inet_driver_entry = +#ifdef __OSE__ +/* The structure of the signal used for requesting asynchronous + * notification from the stack. Under normal circumstances the network stack + * shouldn't overwrite the value set in the fd field by the sender + * of the request */ +struct OseAsyncSig { + struct FmEvent event; + int fd; +}; + +union SIGNAL { + SIGSELECT signo; + struct OseAsyncSig async; +}; + +static ErlDrvSSizeT tcp_inet_ctl_ose(ErlDrvData e, unsigned int cmd, + char* buf, ErlDrvSizeT len, + char** rbuf, ErlDrvSizeT rsize); +static void tcp_inet_commandv_ose(ErlDrvData e, ErlIOVec* ev); +static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event); +static void tcp_inet_drv_input_ose(ErlDrvData data, ErlDrvEvent event); +static ErlDrvOseEventId inet_resolve_signal(union SIGNAL *sig); + +#ifdef INET_DRV_DEBUG + +static char *read_req = "SO_EVENT_READ_REQUEST"; +static char *read_rep = "SO_EVENT_READ_REPLY"; +static char *write_req = "SO_EVENT_WRITE_REQUEST"; +static char *write_rep = "SO_EVENT_WRITE_REPLY"; +static char *eof_req = "SO_EVENT_EOF_REQUEST"; +static char *eof_rep = "SO_EVENT_EOF_REPLY"; +static char *accept_req = "SO_EVENT_ACCEPT_REQUEST"; +static char *accept_rep = "SO_EVENT_ACCEPT_REPLY"; +static char *connect_req = "SO_EVENT_CONNECT_REQUEST"; +static char *connect_rep = "SO_EVENT_CONNECT_REPLY"; +static char *error_req = "SO_EVENT_ERROR_REQUEST"; +static char *error_rep = "SO_EVENT_ERROR_REPLY"; +static char signo_tmp[32]; + +static char *signo_to_string(SIGSELECT signo) { + switch (signo) { + case SO_EVENT_READ_REQUEST: { return read_req; } + case SO_EVENT_READ_REPLY: { return read_rep; } + case SO_EVENT_WRITE_REQUEST: { return write_req; } + case SO_EVENT_WRITE_REPLY: { return write_rep; } + case SO_EVENT_EOF_REQUEST: { return eof_req; } + case SO_EVENT_EOF_REPLY: { return eof_rep; } + case SO_EVENT_ACCEPT_REQUEST: { return accept_req; } + case SO_EVENT_ACCEPT_REPLY: { return accept_rep; } + case SO_EVENT_CONNECT_REQUEST: { return connect_req; } + case SO_EVENT_CONNECT_REPLY: { return connect_rep; } + case SO_EVENT_ERROR_REQUEST: { return error_req; } + case SO_EVENT_ERROR_REPLY: { return error_rep; } + } + + snprintf(signo_tmp,32,"0x%x",signo); + + return signo_tmp; +} + +#endif + +#endif /* __OSE__ */ + + +static struct erl_drv_entry tcp_inet_driver_entry = { tcp_inet_init, /* inet_init will add this driver !! */ tcp_inet_start, @@ -1159,6 +1257,9 @@ static struct erl_drv_entry tcp_inet_driver_entry = #ifdef __WIN32__ tcp_inet_event, NULL, +#elif defined(__OSE__) + tcp_inet_drv_input_ose, /*ready_input*/ + tcp_inet_drv_output_ose, /*ready_output*/ #else tcp_inet_drv_input, tcp_inet_drv_output, @@ -1166,9 +1267,17 @@ static struct erl_drv_entry tcp_inet_driver_entry = "tcp_inet", NULL, NULL, +#ifdef __OSE__ + tcp_inet_ctl_ose, +#else tcp_inet_ctl, +#endif tcp_inet_timeout, +#ifdef __OSE__ + tcp_inet_commandv_ose, +#else tcp_inet_commandv, +#endif NULL, tcp_inet_flush, NULL, @@ -1315,6 +1424,14 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event); /* convert descriptor poiner to inet_descriptor pointer */ #define INETP(d) (&(d)->inet) +#ifdef __OSE__ +static void inet_driver_select(inet_descriptor* desc, + int flags, int onoff); +static void tcp_inet_ose_dispatch_signals(tcp_descriptor *desc, + int prev_select_state, + union SIGNAL *sig); +#endif + static int async_ref = 0; /* async reference id generator */ #define NEW_ASYNC_ID() ((async_ref++) & 0xffff) @@ -3528,6 +3645,9 @@ static int packet_binary_message DEBUGF(("packet_passive_message(%ld):\r\n", (long)desc->port)); +#if !defined(HAVE_UDP) && !defined(HAVE_SCTP) + i = LOAD_ATOM(spec, i, am_tcp_passive); +#else if (desc->sprotocol == IPPROTO_TCP) i = LOAD_ATOM(spec, i, am_tcp_passive); else { @@ -3537,6 +3657,7 @@ static int packet_binary_message i = LOAD_ATOM(spec, i, am_udp_passive); #endif } +#endif i = LOAD_PORT(spec, i, desc->dport); i = LOAD_TUPLE(spec, i, 2); ASSERT(i <= 6); @@ -4200,6 +4321,16 @@ static void desc_close(inet_descriptor* desc) desc->forced_events = 0; desc->send_would_block = 0; #endif +#ifdef __OSE__ + if (desc->events[0]) { + driver_select(desc->port,desc->events[0],FD_READ|FD_WRITE|ERL_DRV_USE,0); + driver_select(desc->port,desc->events[1],FD_READ|FD_WRITE|ERL_DRV_USE,0); + driver_select(desc->port,desc->events[2],FD_READ|FD_WRITE|ERL_DRV_USE,0); + driver_select(desc->port,desc->events[3],FD_READ|FD_WRITE|ERL_DRV_USE,0); + driver_select(desc->port,desc->events[4],FD_READ|FD_WRITE|ERL_DRV_USE,0); + driver_select(desc->port,desc->events[5],FD_READ|FD_WRITE|ERL_DRV_USE,0); + } +#else /* * We should close the fd here, but the other driver might still * be selecting on it. @@ -4209,6 +4340,7 @@ static void desc_close(inet_descriptor* desc) ERL_DRV_USE, 0); else inet_stop_select((ErlDrvEvent)(long)desc->event,NULL); +#endif desc->event = INVALID_EVENT; /* closed by stop_select callback */ desc->s = INVALID_SOCKET; desc->event_mask = 0; @@ -4250,6 +4382,64 @@ static int erl_inet_close(inet_descriptor* desc) return 0; } +#ifdef __OSE__ +static void inet_select_init(inet_descriptor* desc) +{ + desc->events[0] = + erl_drv_ose_event_alloc(SO_EVENT_READ_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[0], + ERL_DRV_READ|ERL_DRV_USE, 1); + + desc->events[1] = + erl_drv_ose_event_alloc(SO_EVENT_EOF_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[1], + ERL_DRV_READ|ERL_DRV_USE, 1); + + desc->events[2] = + erl_drv_ose_event_alloc(SO_EVENT_ACCEPT_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[2], + ERL_DRV_READ|ERL_DRV_USE, 1); + + /* trigger tcp_inet_input */ + desc->events[3] = + erl_drv_ose_event_alloc(SO_EVENT_WRITE_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[3], + ERL_DRV_WRITE|ERL_DRV_USE, 1); + + desc->events[4] = + erl_drv_ose_event_alloc(SO_EVENT_CONNECT_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[4], + ERL_DRV_WRITE|ERL_DRV_USE, 1); + + desc->events[5] = + erl_drv_ose_event_alloc(SO_EVENT_ERROR_REPLY, + desc->s, + inet_resolve_signal, + NULL); + driver_select(desc->port, desc->events[5], + ERL_DRV_WRITE|ERL_DRV_USE, 1); + + /* Issue a select on error event before any other select to be sure we are + prepared to receive error notifications from the stack, even in the + situations when select isn't issued */ + sock_select(desc, SOCK_FD_ERROR, 1); +} +#endif static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, char** rbuf, ErlDrvSizeT rsize) @@ -4333,6 +4523,10 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, #ifdef __WIN32__ driver_select(desc->port, desc->event, ERL_DRV_READ, 1); #endif +#ifdef __OSE__ + inet_select_init(desc); +#endif + desc->state = INET_STATE_OPEN; desc->stype = type; desc->sfamily = domain; @@ -4352,7 +4546,14 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, return ctl_error(sock_errno(), rbuf, rsize); if (name.sa.sa_family != domain) return ctl_error(EINVAL, rbuf, rsize); +#ifdef __OSE__ + /* for fdopen duplicating the sd will allow to uniquely identify + the signal from OSE with erlang port */ + desc->s = sock_dup(s); +#else desc->s = s; +#endif + if ((desc->event = sock_create_event(desc)) == INVALID_EVENT) return ctl_error(sock_errno(), rbuf, rsize); SET_NONBLOCKING(desc->s); @@ -4362,8 +4563,15 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, desc->state = INET_STATE_BOUND; /* assume bound */ if (type == SOCK_STREAM) { /* check if connected */ sz = sizeof(name); - if (!IS_SOCKET_ERROR(sock_peer(s, (struct sockaddr*) &name, &sz))) + if (!IS_SOCKET_ERROR(sock_peer(s, (struct sockaddr*) &name, &sz))) { desc->state = INET_STATE_CONNECTED; +#ifdef __OSE__ + /* since we are dealing with different descriptors (i.e. inet and + socket) the select part should be initialized with the right + values */ + inet_select_init(desc); +#endif + } } desc->prebound = 1; /* used to prevent a real close since @@ -4389,8 +4597,7 @@ struct addr_if { #ifndef SIOCGIFNETMASK -static struct in_addr net_mask(in) -struct in_addr in; +static struct in_addr net_mask(struct in_addr in) { register u_long i = sock_ntohl(in.s_addr); @@ -8069,6 +8276,15 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) #ifdef HAVE_SETNS desc->netns = NULL; #endif +#ifdef __OSE__ + desc->select_state = 0; + desc->events[0] = NULL; + desc->events[1] = NULL; + desc->events[2] = NULL; + desc->events[3] = NULL; + desc->events[4] = NULL; + desc->events[5] = NULL; +#endif return (ErlDrvData)desc; } @@ -8789,6 +9005,11 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s, copy_desc->inet.port = port; copy_desc->inet.dport = driver_mk_port(port); + +#ifdef __OSE__ + inet_select_init(©_desc->inet); +#endif + *err = 0; return copy_desc; } @@ -8850,8 +9071,22 @@ static void tcp_inet_stop(ErlDrvData e) inet_stop(INETP(desc)); } +#ifdef __OSE__ - +static ErlDrvSSizeT tcp_inet_ctl_ose(ErlDrvData e, unsigned int cmd, + char* buf, ErlDrvSizeT len, + char** rbuf, ErlDrvSizeT rsize) { + + tcp_descriptor* desc = (tcp_descriptor*)e; + int prev_select_state = INETP(desc)->select_state; + + ErlDrvSSizeT res = tcp_inet_ctl(e,cmd,buf,len,rbuf,rsize); + + tcp_inet_ose_dispatch_signals((tcp_descriptor*)e,prev_select_state,NULL); + + return res; +} +#endif /* TCP requests from Erlang */ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, @@ -9067,7 +9302,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, ErlDrvTermData caller = driver_caller(desc->inet.port); tcp_descriptor* accept_desc; int err; - + if ((accept_desc = tcp_inet_copy(desc,s,caller,&err)) == NULL) { sock_close(s); return ctl_error(err, rbuf, rsize); @@ -9101,7 +9336,8 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char tbuf[2]; int n; - DEBUGF(("tcp_inet_ctl(%ld): RECV\r\n", (long)desc->inet.port)); + DEBUGF(("tcp_inet_ctl(%ld): RECV (s=%d)\r\n", + (long)desc->inet.port, desc->inet.s)); /* INPUT: Timeout(4), Length(4) */ if (!IS_CONNECTED(INETP(desc))) { if (desc->tcp_add_flags & TCP_ADDF_DELAYED_CLOSE_RECV) { @@ -9273,6 +9509,16 @@ static void tcp_inet_command(ErlDrvData e, char *buf, ErlDrvSizeT len) DEBUGF(("tcp_inet_command(%ld) }\r\n", (long)desc->inet.port)); } +#ifdef __OSE__ + +static void tcp_inet_commandv_ose(ErlDrvData e, ErlIOVec* ev) { + int prev_select_state = INETP((tcp_descriptor*)e)->select_state; + tcp_inet_commandv(e, ev); + tcp_inet_ose_dispatch_signals((tcp_descriptor*)e,prev_select_state,NULL); +} + +#endif + static void tcp_inet_commandv(ErlDrvData e, ErlIOVec* ev) { @@ -9336,6 +9582,22 @@ static void inet_stop_select(ErlDrvEvent event, void* _) { #ifdef __WIN32__ WSACloseEvent((HANDLE)event); +#elif defined(__OSE__) + ErlDrvOseEventId id; + union SIGNAL *sig; + erl_drv_ose_event_fetch(event, NULL, &id,NULL); + DEBUGF(("inet_stop_select(?#?) {s=%d\n",id)); + sock_close((int)id); + /* On socket close all the signals waiting to be processed as part of the + select should be deallocated */ + while((sig = erl_drv_ose_get_signal(event))) { + DEBUGF(("inet_stop_select(?#?): Freeing signal %s\n", + signo_to_string(sig->signo))); + free_buf(&sig); + } + erl_drv_ose_event_free(event); + DEBUGF(("inet_stop_select(?#?) }\n")); + #else sock_close((SOCKET)(long)event); #endif @@ -9455,12 +9717,13 @@ static int tcp_remain(tcp_descriptor* desc, int* len) int n = desc->i_ptr - ptr; /* number of bytes read */ int tlen; - DEBUGF(("tcp_remain(%ld): s=%d, n=%d, nfill=%d nsz=%d\r\n", - (long)desc->inet.port, desc->inet.s, n, nfill, nsz)); + DEBUGF(("tcp_remain(%ld): s=%d, n=%d, nfill=%d nsz=%d, tlen %d\r\n", + (long)desc->inet.port, desc->inet.s, n, nfill, nsz, tlen)); tlen = packet_get_length(desc->inet.htype, ptr, n, desc->inet.psize, desc->i_bufsz, &desc->http_state); + if (tlen > 0) { if (tlen <= n) { /* got a packet */ *len = tlen; @@ -9868,7 +10131,146 @@ static void tcp_inet_event(ErlDrvData e, ErlDrvEvent event) return; } -#endif /* WIN32 */ +#elif defined(__OSE__) /* !__WIN32__ */ +/* The specific resolve signal function. It will return the socket descriptor + for which the select was issued */ +static ErlDrvOseEventId inet_resolve_signal(union SIGNAL *sig) { + DEBUGF(("%s(?#?): s=%d got signal %s, status = %d, extra = %d, sender = 0x%x\n", + __FUNCTION__,sig->async.fd,signo_to_string(sig->signo), + sig->async.event.status, + sig->async.event.extra,sender(&sig))); + if (sig->signo == SO_EVENT_READ_REPLY || + sig->signo == SO_EVENT_ACCEPT_REPLY || + sig->signo == SO_EVENT_EOF_REPLY || + sig->signo == SO_EVENT_WRITE_REPLY || + sig->signo == SO_EVENT_ERROR_REPLY || + sig->signo == SO_EVENT_CONNECT_REPLY ) { + return sig->async.fd; + } + + return -1; +} + +static void inet_driver_select(inet_descriptor* desc, + int flags, int onoff) { + ASSERT(!desc->is_ignored); + + if(onoff) { + desc->select_state |= flags; + } else { + desc->select_state &= ~flags; + } +} + +static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int max_sz) +{ + size_t data_len = 0; + size_t sent = 0; + ssize_t n; + int i; + + for(i = 0; i < iovcnt; i++) + { + data_len = iov[i].iov_len; +tryagain: + n = sock_send(fd, iov[i].iov_base, data_len, 0); + if (IS_SOCKET_ERROR(n)) { + /* If buffer length is greater than the amount stack is able to + * send out then try to send at least max_sz (this comes with + * SO_EVENT_WRITE_REPLY signal*/ + if ((errno == EMSGSIZE) && (max_sz > 0) && (data_len > max_sz)) { + data_len = max_sz; + goto tryagain; + } + break; + } + sent += n; + } + return sent; +} + +#define OSE_EVENT_REQ(TCP_DESC,EVENT) do { \ + union SIGNAL *sig = alloc(sizeof(struct OseAsyncSig), EVENT); \ + sig->async.fd = INETP(TCP_DESC)->s; \ + ose_request_event(INETP(TCP_DESC)->s, &sig, 1); \ + DEBUGF(("%s(%ld): s=%d sent %s\r\n",__FUNCTION__, \ + INETP(TCP_DESC)->port,INETP(TCP_DESC)->s,signo_to_string(EVENT))); \ + } while(0) + +static void tcp_inet_ose_dispatch_signals(tcp_descriptor *desc, + int prev_select_state, + union SIGNAL *sig) { + if (sig) { + DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) {s=%d resend\r\n", + (long)INETP(desc)->port,INETP(desc)->s)); + /* We are reacting to a signal, which means that if + the select_state for that signal is still activated + we should send a new signal */ + switch (sig->signo) { + case SO_EVENT_READ_REPLY: { + if (INETP(desc)->select_state & FD_READ) + OSE_EVENT_REQ(desc,SO_EVENT_READ_REQUEST); + break; + } + case SO_EVENT_WRITE_REPLY: { + if (INETP(desc)->select_state & FD_WRITE) + OSE_EVENT_REQ(desc,SO_EVENT_WRITE_REQUEST); + break; + } + case SO_EVENT_CONNECT_REPLY: { + if (INETP(desc)->select_state & FD_CONNECT) + OSE_EVENT_REQ(desc,SO_EVENT_CONNECT_REQUEST); + break; + } + case SO_EVENT_ACCEPT_REPLY: { + if (INETP(desc)->select_state & FD_ACCEPT) + OSE_EVENT_REQ(desc,SO_EVENT_ACCEPT_REQUEST); + break; + } + case SO_EVENT_ERROR_REPLY: { + if (INETP(desc)->select_state & SOCK_FD_ERROR) + OSE_EVENT_REQ(desc,SO_EVENT_ERROR_REQUEST); + break; + } + + } + DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) }\r\n", + (long)INETP(desc)->port)); + } + + if (INETP(desc)->select_state != prev_select_state) { + /* If the select state has changed we have to issue signals for + the state parts that have changed. */ + int xor_select_state = INETP(desc)->select_state ^ prev_select_state; + DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) {s=%d select change\r\n", + (long)INETP(desc)->port,INETP(desc)->s)); + if ((xor_select_state & FD_READ) && + (INETP(desc)->select_state & FD_READ)) { + OSE_EVENT_REQ(desc,SO_EVENT_READ_REQUEST); + } + if ((xor_select_state & FD_WRITE) && + (INETP(desc)->select_state & FD_WRITE)) { + OSE_EVENT_REQ(desc,SO_EVENT_WRITE_REQUEST); + } + if ((xor_select_state & FD_CONNECT) && + (INETP(desc)->select_state & FD_CONNECT)) { + OSE_EVENT_REQ(desc,SO_EVENT_CONNECT_REQUEST); + } + if ((xor_select_state & FD_ACCEPT) && + (INETP(desc)->select_state & FD_ACCEPT)) { + OSE_EVENT_REQ(desc,SO_EVENT_ACCEPT_REQUEST); + } + if ((xor_select_state & SOCK_FD_ERROR) && + (INETP(desc)->select_state & SOCK_FD_ERROR)) { + OSE_EVENT_REQ(desc,SO_EVENT_ERROR_REQUEST); + } + + DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) }\r\n", + (long)INETP(desc)->port)); + } +} + +#endif /* __OSE__ */ /* socket has input: @@ -9889,7 +10291,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) unsigned int len; inet_address remote; inet_async_op *this_op = desc->inet.opt; - + len = sizeof(desc->inet.remote); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len); if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) { @@ -9958,7 +10360,6 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) while (desc->inet.state == INET_STATE_MULTI_ACCEPTING) { len = sizeof(desc->inet.remote); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len); - if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) { /* Just try again, no real error, keep the last return code */ goto done; @@ -10284,6 +10685,49 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len) return 0; } +#ifdef __OSE__ + +static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event) +{ + union SIGNAL *event_sig = erl_drv_ose_get_signal(event); + + while (event_sig) { + int prev_select_state = INETP((tcp_descriptor*)data)->select_state; + int res = tcp_inet_output((tcp_descriptor*)data, (HANDLE)event_sig); + if (res != -1) { + tcp_inet_ose_dispatch_signals((tcp_descriptor*)data, + prev_select_state,event_sig); + free_buf(&event_sig); + event_sig = erl_drv_ose_get_signal(event); + } else { + /* NOTE: here the event object could have been deallocated!!!! + inet_stop_select is called when doing driver_select(ERL_DRV_USE,0) + */ + free_buf(&event_sig); + return; + } + } +} + +static void tcp_inet_drv_input_ose(ErlDrvData data, ErlDrvEvent event) +{ + union SIGNAL *event_sig = erl_drv_ose_get_signal(event); + + while (event_sig) { + int prev_select_state = INETP((tcp_descriptor*)data)->select_state; + int res = tcp_inet_input((tcp_descriptor*)data, (HANDLE)event); + if (res != -1) { + tcp_inet_ose_dispatch_signals((tcp_descriptor*)data, prev_select_state, + event_sig); + free_buf(&event_sig); + event_sig = erl_drv_ose_get_signal(event); + } else { + free_buf(&event_sig); + return; + } + } +} +#else static void tcp_inet_drv_output(ErlDrvData data, ErlDrvEvent event) { (void)tcp_inet_output((tcp_descriptor*)data, (HANDLE)event); @@ -10293,6 +10737,7 @@ static void tcp_inet_drv_input(ErlDrvData data, ErlDrvEvent event) { (void)tcp_inet_input((tcp_descriptor*)data, (HANDLE)event); } +#endif /* socket ready for ouput: ** 1. INET_STATE_CONNECTING => non block connect ? @@ -10358,6 +10803,13 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) ssize_t n; SysIOVec* iov; +#ifdef __OSE__ + /* For large size buffers case the amount of data that the stack is + able to send out (received in the .extra field) should be passed + down to writev_fallback */ + n = event ? ((union SIGNAL*)event)->async.event.extra : 0; +#endif + if ((iov = driver_peekq(ix, &vsize)) == NULL) { sock_select(INETP(desc), FD_WRITE, 0); send_empty_out_q_msgs(INETP(desc)); @@ -10369,8 +10821,8 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) if (IS_SOCKET_ERROR(sock_sendv(desc->inet.s, iov, vsize, &n, 0))) { write_error: if ((sock_errno() != ERRNO_BLOCK) && (sock_errno() != EINTR)) { - DEBUGF(("tcp_inet_output(%ld): sock_sendv(%d) errno = %d\r\n", - (long)desc->inet.port, vsize, sock_errno())); + DEBUGF(("tcp_inet_output(%ld): sock_sendv(%d) errno = %d (errno %d)\r\n", + (long)desc->inet.port, vsize, sock_errno(), errno)); ret = tcp_send_error(desc, sock_errno()); goto done; } @@ -10383,6 +10835,12 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) sizes > (max 32 bit signed int) */ size_t howmuch = 0x7FFFFFFF; /* max signed 32 bit */ int x; +#ifdef __OSE__ + /* For EWOULDBLOCK sock_sendv returns 0 so we have to be sure it + wasn't the case */ + if(sock_errno() == ERRNO_BLOCK) + goto done; +#endif for(x = 0; x < vsize && iov[x].iov_len == 0; ++x) ; if (x < vsize) { diff --git a/erts/emulator/sys/ose/beam.lmconf b/erts/emulator/sys/ose/beam.lmconf index 2cee836f9a..4ad46b01d9 100644 --- a/erts/emulator/sys/ose/beam.lmconf +++ b/erts/emulator/sys/ose/beam.lmconf @@ -10,7 +10,7 @@ OSE_LM_DATA_INIT=YES OSE_LM_BSS_INIT=YES OSE_LM_EXEC_MODEL=SHARED HEAP_MAX_SIZE=1000000000 -HEAP_SMALL_BUF_INIT_SIZE=64000000 +HEAP_SMALL_BUF_INIT_SIZE=20971520 HEAP_LARGE_BUF_THRESHOLD=16000000 HEAP_LOCK_TYPE=2 diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c index f0a9097252..7d2a3d1e0b 100644 --- a/erts/emulator/sys/ose/erl_poll.c +++ b/erts/emulator/sys/ose/erl_poll.c @@ -551,7 +551,12 @@ int erts_poll_wait(ErtsPollSet ps, fd.id, fd.signo, current_process()); erts_send_error_to_logger_nogl(dsbufp); timeout = 0; - ASSERT(0); + /* Under normal circumstances the signal is deallocated by the + * driver that issued the select operation. But in this case + * there's no driver waiting for such signal so we have to + * deallocate it here */ + if (sig) + free_buf(&sig); } else { int i; struct erts_sys_fd_type *fd = NULL; -- cgit v1.2.3 From 09c1cbf863864a5740f7c13c136b1164aac70469 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Sun, 23 Mar 2014 22:50:10 +0100 Subject: ose: Fix bug when hunting for signal proxy Sometimes the wrong signal, i.e. a read reply, would be received from this non-selective receive. --- erts/emulator/sys/ose/sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index aaf515876f..d58e90b352 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -713,7 +713,7 @@ static void stop_select(ErlDrvEvent, void*); static PROCESS get_signal_proxy_pid(void) { union SIGNAL *sig; - SIGSELECT any_sig[] = {0}; + SIGSELECT any_sig[] = {1,ERTS_SIGNAL_OSE_DRV_ATTACH}; if (!sig_proxy_pid) { sig = alloc(sizeof(union SIGNAL), ERTS_SIGNAL_OSE_DRV_ATTACH); -- cgit v1.2.3 From d28049c638f19de0f0f4c805d3efd004de417210 Mon Sep 17 00:00:00 2001 From: Jonas Karlsson Date: Fri, 21 Mar 2014 17:09:16 +0100 Subject: ose: fix for packet_bytes in fd/spawn driver. --- erts/emulator/sys/ose/sys.c | 289 ++++++++++++++++++++++++-------------------- 1 file changed, 155 insertions(+), 134 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index d58e90b352..414dfb2264 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -212,14 +212,13 @@ static volatile int children_died; driver_free(buffer_ptr); \ } while(0) -/* When we have several schedulers, we need to make sure - * that scheduler issuing aio_dispatch() is the owner on the signal */ #define DISPATCH_AIO(sig) do { \ if (aio_dispatch(sig)) \ ramlog_printf("%s:%d: dispatch failed with %d\n", \ __FILE__,__LINE__,errno); \ } while(0) +#define AIO_PIPE_SIZE 1024 /* debug print macros */ #define DEBUG_RES 0 @@ -748,7 +747,7 @@ resolve_signal(union SIGNAL* sig) { struct erl_drv_entry spawn_driver_entry = { spawn_init, spawn_start, - erl_stop, + NULL, /* erl_stop, */ output, ready_input, ready_output, @@ -859,10 +858,9 @@ set_driver_data(ErlDrvPort port_num, /* async read struct */ memset(&driver_data[ifd].aiocb, 0, sizeof(struct aiocb)); - driver_data[ifd].aiocb.aio_buf = driver_alloc(255); + driver_data[ifd].aiocb.aio_buf = driver_alloc(AIO_PIPE_SIZE); driver_data[ifd].aiocb.aio_fildes = ifd; - driver_data[ifd].aiocb.aio_nbytes = 255; - + driver_data[ifd].aiocb.aio_nbytes = (packet_bytes?packet_bytes:AIO_PIPE_SIZE); driver_data[ifd].alive = 1; driver_data[ifd].status = 0; driver_data[ifd].input_event = @@ -995,7 +993,7 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) { int ifd[2]; int ofd[2]; - static uint32_t ticker = 0; + static uint32_t ticker = 1; PmStatus pm_status; OSDOMAIN domain = PM_NEW_DOMAIN; PROCESS progpid, mainbid, mainpid; @@ -1007,39 +1005,53 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) int handle_size; char *ptr; - /* handle arguments */ - ptr = strchr(name, ' '); - if (ptr != NULL) { - *ptr ='\0'; - ptr++; - args = ptr; + + args = driver_alloc(strlen(name)+1); + strcpy(args, name); + /* We need to handle name in three parts + * - install handle (must be unique) + * - install binary (needed for ose_pm_install_load_module()) + * - full path (as argument to the spawned applications env.var + */ + + /* full path including arguments */ + args = driver_alloc(strlen(name)+1); + strcpy(args, name); + + /* handle path */ + tmp_handle = strrchr(name, '/'); + if (tmp_handle == NULL) { + tmp_handle = name; } else { - args = NULL; + tmp_handle++; } - /* create an install handle */ - ptr = strrchr(name, '/'); + /* handle args */ + ptr = strchr(tmp_handle, ' '); if (ptr != NULL) { - ptr++; - tmp_handle = ptr; + *ptr = '\0'; + handle_size = ptr - tmp_handle; } else { - tmp_handle = name; + handle_size = strlen(name)+1; } - handle_size = strlen(tmp_handle)+1; - handle_size += (ticker<10)?3:((ticker<100)?4:5); + /* make room for ticker */ + handle_size += (ticker<10)?3:((ticker<100)?4:5); handle = driver_alloc(handle_size); - snprintf(handle, handle_size, "%s_%d", tmp_handle, ticker); - + do { - snprintf(handle, handle_size, "%s_%d", tmp_handle, ticker++); + snprintf(handle, handle_size, "%s_%d", tmp_handle, ticker); pm_status = ose_pm_install_load_module(0, "ELF", name, handle, 0, 0, NULL); - + ticker++; } while (pm_status == PM_EINSTALL_HANDLE_ALREADY_INSTALLED); - DEBUG_CHECK_RES(pm_status, PM_SUCCESS); + + if (pm_status != PM_SUCCESS) { + errno = ENOSYS; /* FIXME add comment */ + return ERL_DRV_ERROR_ERRNO; + } /* Create Program */ pm_status = ose_pm_create_program(&domain, handle, 0, 0, @@ -1212,17 +1224,13 @@ static void erl_stop(ErlDrvData drv_data) if (data->ifd != data->ofd) { /* read and write */ nbio_stop_fd(data->port_num, data->input_event); nbio_stop_fd(data->port_num, data->output_event); - driver_select(data->port_num, data->input_event, ERL_DRV_USE, 0); - driver_select(data->port_num, data->output_event, ERL_DRV_USE, 0); } else { /* write only */ nbio_stop_fd(data->port_num, data->output_event); - driver_select(data->port_num, data->output_event, ERL_DRV_USE, 0); } } else { /* read only */ nbio_stop_fd(data->port_num, data->input_event); - driver_select(data->port_num, data->input_event, ERL_DRV_USE, 0); } close(data->ifd); close(data->ofd); @@ -1246,18 +1254,31 @@ static void output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len) lbp = lb + (4-(data->packet_bytes)); if ((sz = driver_sizeq(data->port_num)) > 0) { - driver_enq(data->port_num, lbp, data->packet_bytes); - driver_enq(data->port_num, buf, len); - if (sz + len + data->packet_bytes >= (1 << 13)) + if (data->packet_bytes != 0) { + driver_enq(data->port_num, lbp, data->packet_bytes); + } + driver_enq(data->port_num, buf, len); + + if (sz + len + data->packet_bytes >= (1 << 13)) set_busy_port(data->port_num, 1); } else { - driver_enq(data->port_num, buf, len); /* n is the skip value */ - + char *pbbuf; + if (data->packet_bytes != 0) { + pbbuf = malloc(len + data->packet_bytes); + int i; + for (i = 0; i < data->packet_bytes; i++) { + *pbbuf++ = *lbp++; + } + strncpy(pbbuf, buf, len); + pbbuf -= data->packet_bytes; + } driver_select(data->port_num, data->output_event, ERL_DRV_WRITE|ERL_DRV_USE, 1); - - WRITE_AIO(data->ofd, len, buf); + WRITE_AIO(data->ofd, + (data->packet_bytes ? len+data->packet_bytes : len), + (data->packet_bytes ? pbbuf : buf)); + if (data->packet_bytes != 0) free(pbbuf); } return; /* 0; */ } @@ -1273,12 +1294,12 @@ static int port_inp_failure(ErlDrvPort port_num, ErlDrvEvent ready_fd, int res) ASSERT(res <= 0); erl_drv_ose_event_fetch(ready_fd,&sig_no, NULL, (void **)&fd); - /* As we need to handle two signals, we do this in two steps */ if (driver_data[*fd].alive) { report_exit_status(driver_data[*fd].report_exit, 0); /* status? */ } else { + driver_select(port_num,ready_fd,DO_READ|DO_WRITE,0); clear_fd_data(*fd); driver_report_exit(driver_data[*fd].port_num, driver_data[*fd].status); /* As we do not really know if the spawn has crashed or exited nicely @@ -1317,6 +1338,10 @@ static void ready_input(ErlDrvData drv_data, ErlDrvEvent ready_fd) } else { res = sig->fm_read_reply.actual; + if (res == 0) { + port_inp_failure(data->port_num, ready_fd, res); + break; + } if (data->packet_bytes == 0) { if (res < 0) { @@ -1327,6 +1352,7 @@ static void ready_input(ErlDrvData drv_data, ErlDrvEvent ready_fd) else if (res == 0) { /* read of 0 bytes, eof, otherside of pipe is assumed dead */ port_inp_failure(data->port_num, ready_fd, res); + break; } else { buf = driver_alloc(res); @@ -1336,103 +1362,92 @@ static void ready_input(ErlDrvData drv_data, ErlDrvEvent ready_fd) driver_output(data->port_num, (char*) buf, res); driver_free(buf); } + /* clear the previous read */ + memset(data->aiocb.aio_buf, 0, res); + + /* issue a new read */ + DISPATCH_AIO(sig); + aio_read(&data->aiocb); } - /* We try to read the remainder */ - else if (fd_data[data->ifd].remain > 0) { - if (res < 0) { - if ((errno != EINTR) && (errno != ERRNO_BLOCK)) { - port_inp_failure(data->port_num, ready_fd, res); + else if (data->packet_bytes && fd_data[data->ifd].remain > 0) { + /* we've read a partial package, or a header */ + + if (res == fd_data[data->ifd].remain) { /* we are done! */ + char *buf = data->aiocb.aio_buf; + int i; + + /* do we have anything buffered? */ + if (fd_data[data->ifd].buf != NULL) { + memcpy(fd_data[data->ifd].buf + fd_data[data->ifd].sz, + buf, res); + buf = fd_data[data->ifd].buf; } - } - else if (res == 0) { - port_inp_failure(data->port_num, ready_fd, res); - } - else if (res == fd_data[data->ifd].remain) { /* we're done */ - driver_output(data->port_num, - fd_data[data->ifd].buf, - fd_data[data->ifd].sz); + + fd_data[data->ifd].sz += res; + driver_output(data->port_num, buf, (fd_data[data->ifd].sz>0?fd_data[data->ifd].sz:res)); clear_fd_data(data->ifd); - } - else { /* if (res < fd_data[fd].remain) */ - fd_data[data->ifd].cpos += res; - fd_data[data->ifd].remain -= res; - } - } - else if (fd_data[data->ifd].remain == 0) { /* clean fd */ - if (res < 0) { - if ((errno != EINTR) && (errno != ERRNO_BLOCK)) { - port_inp_failure(data->port_num, ready_fd, res); + + /* clear the previous read */ + memset(data->aiocb.aio_buf, 0, res); + + /* issue a new read */ + DISPATCH_AIO(sig); + data->aiocb.aio_nbytes = data->packet_bytes; + + if (data->aiocb.aio_buf == NULL) { + port_inp_failure(data->port_num, ready_fd, -1); } + aio_read(&data->aiocb); } - else if (res == 0) { /* eof */ - port_inp_failure(data->port_num, ready_fd, res); - } - else if (res < data->packet_bytes - fd_data[data->ifd].psz) { - memcpy(fd_data[data->ifd].pbuf+fd_data[data->ifd].psz, - (void *)data->aiocb.aio_buf, res); - fd_data[data->ifd].psz += res; - } - else { /* if (res >= packet_bytes) */ - unsigned char* cpos = (unsigned char*)data->aiocb.aio_buf; - int bytes_left = res; - - while (1) { - int psz = fd_data[data->ifd].psz; - char* pbp = fd_data[data->ifd].pbuf + psz; - - while (bytes_left && (psz < data->packet_bytes)) { - *pbp++ = *cpos++; - bytes_left--; - psz++; - } - - if (psz < data->packet_bytes) { - fd_data[data->ifd].psz = psz; - break; - } - fd_data[data->ifd].psz = 0; - - switch (data->packet_bytes) { - case 1: h = get_int8(fd_data[data->ifd].pbuf); break; - case 2: h = get_int16(fd_data[data->ifd].pbuf); break; - case 4: h = get_int32(fd_data[data->ifd].pbuf); break; - default: ASSERT(0); return; /* -1; */ - } - - if (h <= (bytes_left)) { - driver_output(data->port_num, (char*) cpos, h); - cpos += h; - bytes_left -= h; - continue; - } - else { /* The last message we got was split */ - char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); - if (!buf) { - errno = ENOMEM; - port_inp_failure(data->port_num, ready_fd, -1); - } - else { - erts_smp_atomic_add_nob(&sys_misc_mem_sz, h); - sys_memcpy(buf, cpos, bytes_left); - fd_data[data->ifd].buf = buf; - fd_data[data->ifd].sz = h; - fd_data[data->ifd].remain = h - bytes_left; - fd_data[data->ifd].cpos = buf + bytes_left; - } - break; - } + else if(res < fd_data[data->ifd].remain) { /* received part of a package */ + if (fd_data[data->ifd].sz == 0) { + + fd_data[data->ifd].sz += res; + memcpy(fd_data[data->ifd].buf, data->aiocb.aio_buf, res); + fd_data[data->ifd].remain -= res; + } + else { + memcpy(fd_data[data->ifd].buf + fd_data[data->ifd].sz, + data->aiocb.aio_buf, res); + fd_data[data->ifd].sz += res; + fd_data[data->ifd].remain -= res; } + /* clear the previous read */ + memset(data->aiocb.aio_buf, 0, res); + + /* issue a new read */ + DISPATCH_AIO(sig); + data->aiocb.aio_nbytes = fd_data[data->ifd].remain; + + if (data->aiocb.aio_buf == NULL) { + port_inp_failure(data->port_num, ready_fd, -1); + } + aio_read(&data->aiocb); } } + else if (data->packet_bytes && fd_data[data->ifd].remain == 0) { /* we've recieved a header */ + + /* analyze the header FIXME */ + switch (data->packet_bytes) { + case 1: h = get_int8(data->aiocb.aio_buf); break; + case 2: h = get_int16(data->aiocb.aio_buf); break; + case 4: h = get_int32(data->aiocb.aio_buf); break; + } - /* reset the read buffer and init next asynch read */ - DISPATCH_AIO(sig); - memset((void *)data->aiocb.aio_buf, 0, 255); + fd_data[data->ifd].buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h + data->packet_bytes); + fd_data[data->ifd].remain = ((h + data->packet_bytes) - res); - if (res > 0) { - if (aio_read(&data->aiocb)) - ramlog_printf("%s:%d: aio_read(%d) failed with %d\n", - __FILE__,__LINE__,data->ifd,errno); + /* clear the previous read */ + memset(data->aiocb.aio_buf, 0, data->packet_bytes); + + /* issue a new read */ + DISPATCH_AIO(sig); + data->aiocb.aio_nbytes = h; + + if (data->aiocb.aio_buf == NULL) { + port_inp_failure(data->port_num, ready_fd, -1); + } + aio_read(&data->aiocb); } } sig = erl_drv_ose_get_signal(ready_fd); @@ -1466,15 +1481,21 @@ static void ready_output(ErlDrvData drv_data, ErlDrvEvent ready_fd) driver_failure_posix(data->port_num, status); } else { /* written bytes > 0 */ - DISPATCH_AIO(sig); - res = driver_deq(data->port_num, sig->fm_write_reply.actual); - FREE_AIO(sig->fm_write_reply.buffer); - if (res == 0) - set_busy_port(data->port_num, 0); - else { - iov = driver_peekq(data->port_num, &vlen); - WRITE_AIO(data->ofd, iov[0].iov_len, iov[0].iov_base); - } + iov = driver_peekq(data->port_num, &vlen); + if (vlen > 0) { + DISPATCH_AIO(sig); + FREE_AIO(sig->fm_write_reply.buffer); + res = driver_deq(data->port_num, iov[0].iov_len); + if (res > 0) { + iov = driver_peekq(data->port_num, &vlen); + WRITE_AIO(data->ofd, iov[0].iov_len, iov[0].iov_base); + } + } + else if (vlen == 0) { + DISPATCH_AIO(sig); + FREE_AIO(sig->fm_write_reply.buffer); + } + } sig = erl_drv_ose_get_signal(ready_fd); } -- cgit v1.2.3 From d2a5dc042c02dce5bc518eb576ea496af50e6373 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 25 Mar 2014 17:43:09 +0100 Subject: ose: Fix erts assert failed printouts --- erts/emulator/sys/ose/sys.c | 4 ++-- erts/include/internal/ethr_mutex.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index 414dfb2264..5b950a7dae 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -1741,10 +1741,10 @@ erl_assert_error(const char* expr, const char* func, { fflush(stdout); fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", - file, func, line, expr); + file, line, func, expr); fflush(stderr); ramlog_printf("%s:%d:%s() Assertion failed: %s\n", - file, func, line, expr); + file, line, func, expr); abort(); } diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h index ee861065c5..6c931e0cd4 100644 --- a/erts/include/internal/ethr_mutex.h +++ b/erts/include/internal/ethr_mutex.h @@ -354,7 +354,7 @@ void ethr_rwmutex_rwunlock(ethr_rwmutex *); #ifdef ETHR_MTX_HARD_DEBUG #define ETHR_MTX_HARD_ASSERT(A) \ - ((void) ((A) ? 1 : ethr_assert_failed(__FILE__, __LINE__, #A))) + ((void) ((A) ? 1 : ethr_assert_failed(__FILE__, __LINE__, __func__,#A))) #else #define ETHR_MTX_HARD_ASSERT(A) ((void) 1) #endif -- cgit v1.2.3 From d59a203a4e79c8ef3d4470e0039e1d4dc99cbce5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 26 Mar 2014 21:33:31 +0100 Subject: erts: Reduce runtime of test port_SUITE:close_deaf_port as it fails with timeout sometimes. --- erts/emulator/test/port_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 202a8b7537..bacb73671e 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -2318,7 +2318,7 @@ close_deaf_port(Config) when is_list(Config) -> test_server:timetrap_cancel(Dog), Res. -close_deaf_port_1(1000, _) -> +close_deaf_port_1(200, _) -> ok; close_deaf_port_1(N, Cmd) -> Timeout = integer_to_list(random:uniform(5*1000)), -- cgit v1.2.3 From 32eae452dbd6da109914af3a9003d9bbcb696d42 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Wed, 26 Mar 2014 23:51:05 +0100 Subject: Change the subtag used for maps from 0xB to 0xF The subtag chosen for maps breaks crucial assumptions in other parts of the system, in particular the native code generation scheme used in the HiPE compiler. Therefore it is better to use 'the other' unused subtag. The main change here is the use of 0xF subtag for maps instead of 0xB. The rest of the differences are changes to and additions of comments. One more comment from my part. I noticed that the file contains the following two lines: The comment of the second line is most certainly a copy and paste error and should be appropriately fixed by the OTP developers. More importantly, it would be great if that subtag (0x7) turned out to be unused and could be used for maps instead of the 0xF one. This might turn out very handy in the future. I can elaborate on this, if needed. --- erts/emulator/beam/erl_term.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index f10a3a9d38..982e63ee31 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -112,11 +112,11 @@ struct erl_node_; /* Declared in erl_node_tables.h */ * 1000 REFC_BINARY | | * 1001 HEAP_BINARY | BINARIES | * 1010 SUB_BINARY | | - * 1011 Not used + * 1011 Not used; see comment below * 1100 EXTERNAL_PID | | * 1101 EXTERNAL_PORT | EXTERNAL THINGS | * 1110 EXTERNAL_REF | | - * 1111 Not used + * 1111 MAP * * COMMENTS: * @@ -140,10 +140,11 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #define REFC_BINARY_SUBTAG (0x8 << _TAG_PRIMARY_SIZE) /* BINARY */ #define HEAP_BINARY_SUBTAG (0x9 << _TAG_PRIMARY_SIZE) /* BINARY */ #define SUB_BINARY_SUBTAG (0xA << _TAG_PRIMARY_SIZE) /* BINARY */ -#define MAP_SUBTAG (0xB << _TAG_PRIMARY_SIZE) /* MAP */ +/* _BINARY_XXX_MASK depends on 0xB being unused */ #define EXTERNAL_PID_SUBTAG (0xC << _TAG_PRIMARY_SIZE) /* EXTERNAL_PID */ #define EXTERNAL_PORT_SUBTAG (0xD << _TAG_PRIMARY_SIZE) /* EXTERNAL_PORT */ #define EXTERNAL_REF_SUBTAG (0xE << _TAG_PRIMARY_SIZE) /* EXTERNAL_REF */ +#define MAP_SUBTAG (0xF << _TAG_PRIMARY_SIZE) /* MAP */ #define _TAG_HEADER_ARITYVAL (TAG_PRIMARY_HEADER|ARITYVAL_SUBTAG) @@ -156,11 +157,11 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #define _TAG_HEADER_REFC_BIN (TAG_PRIMARY_HEADER|REFC_BINARY_SUBTAG) #define _TAG_HEADER_HEAP_BIN (TAG_PRIMARY_HEADER|HEAP_BINARY_SUBTAG) #define _TAG_HEADER_SUB_BIN (TAG_PRIMARY_HEADER|SUB_BINARY_SUBTAG) -#define _TAG_HEADER_MAP (TAG_PRIMARY_HEADER|MAP_SUBTAG) #define _TAG_HEADER_EXTERNAL_PID (TAG_PRIMARY_HEADER|EXTERNAL_PID_SUBTAG) #define _TAG_HEADER_EXTERNAL_PORT (TAG_PRIMARY_HEADER|EXTERNAL_PORT_SUBTAG) #define _TAG_HEADER_EXTERNAL_REF (TAG_PRIMARY_HEADER|EXTERNAL_REF_SUBTAG) #define _TAG_HEADER_BIN_MATCHSTATE (TAG_PRIMARY_HEADER|BIN_MATCHSTATE_SUBTAG) +#define _TAG_HEADER_MAP (TAG_PRIMARY_HEADER|MAP_SUBTAG) #define _TAG_HEADER_MASK 0x3F -- cgit v1.2.3 From be34551be2119a6c3c1a7edf113b7db12789423b Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Thu, 19 Dec 2013 14:36:06 +0000 Subject: Document an escript:create/2 hidden feature escript:create/2 accepts a 3-elements tuple containing files and zip:create/3 options to build a zip file. Also had to update zip typespecs to allow referral from escript docs. --- erts/doc/src/escript.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml index 9e2a87dde6..bac8dd1e01 100644 --- a/erts/doc/src/escript.xml +++ b/erts/doc/src/escript.xml @@ -221,8 +221,13 @@ factorial 5 = 120 EmuArgs = string() | 'undefined' Body = {source, SourceCode} | {beam, BeamCode} - | {archive, ZipArchive} - SourceCode = BeamCode = ZipArchive = binary() + | {archive, ZipArchive} + | {archive, ZipFiles, ZipOptions} + SourceCode = BeamCode = file:filename() | binary() + ZipArchive = zip:filename() | binary() + ZipFiles = [ZipFile] + ZipFile = file:filename() | {file:filename(), binary()} | {file:filename(), binary(), file:file_info()} + ZipOptions = [zip:create_option()]

The create/2 -- cgit v1.2.3 From d1fde60e2814ba1d8013c48983bc206c1112cb98 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 27 Mar 2014 17:13:43 +0100 Subject: erts: Adjust is_external_header() for new map tag to not mistake a map for an external term (pid, port or ref). --- erts/emulator/beam/erl_term.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 982e63ee31..37014ccf94 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -893,7 +893,8 @@ typedef struct external_thing_ { (((x) & _TAG_HEADER_MASK) == _TAG_HEADER_EXTERNAL_REF) #define is_external_header(x) \ - (((x) & (_TAG_HEADER_MASK-_BINARY_XXX_MASK)) == _TAG_HEADER_EXTERNAL_PID) + (((x) & (_TAG_HEADER_MASK-_BINARY_XXX_MASK)) == _TAG_HEADER_EXTERNAL_PID \ + && ((x) & _TAG_HEADER_MASK) != _TAG_HEADER_MAP) #define is_external(x) (is_boxed((x)) && is_external_header(*boxed_val((x)))) -- cgit v1.2.3 From 56018ef95e70c5630c1c6fe16d22b54f8ecd0791 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 27 Mar 2014 20:59:01 +0100 Subject: erts: Fix bug of scheduling a suspended process This is a race that can cause RUNNING (instead of RUNNING_SYS) set on a SUSPENDED process. The effect of this race happening is probably quite benign. The bug was discovered by process_SUITE:processes_gc_trap on debug VM crashing on last assert in schedule(): /* Never run a suspended process */ ASSERT(!(ERTS_PSFLG_SUSPENDED & erts_smp_atomic32_read_nob(&p->state))); --- erts/emulator/beam/erl_process.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 37e1d07107..bc69d5ad91 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -9207,7 +9207,6 @@ Process *schedule(Process *p, int calls) */ pick_next_process: { erts_aint32_t psflg_band_mask; - erts_aint32_t running_flag; int prio_q; int qmask; @@ -9269,12 +9268,6 @@ Process *schedule(Process *p, int calls) state = erts_smp_atomic32_read_nob(&p->state); } - - if (state & ERTS_PSFLG_ACTIVE_SYS) - running_flag = ERTS_PSFLG_RUNNING_SYS; - else - running_flag = ERTS_PSFLG_RUNNING; - while (1) { erts_aint32_t exp, new, tmp; tmp = new = exp = state; @@ -9284,8 +9277,12 @@ Process *schedule(Process *p, int calls) tmp = state & (ERTS_PSFLG_SUSPENDED | ERTS_PSFLG_PENDING_EXIT | ERTS_PSFLG_ACTIVE_SYS); - if (tmp != ERTS_PSFLG_SUSPENDED) - new |= running_flag; + if (tmp != ERTS_PSFLG_SUSPENDED) { + if (state & ERTS_PSFLG_ACTIVE_SYS) + new |= ERTS_PSFLG_RUNNING_SYS; + else + new |= ERTS_PSFLG_RUNNING; + } } state = erts_smp_atomic32_cmpxchg_relb(&p->state, new, exp); if (state == exp) { -- cgit v1.2.3 From 93d5acafec2c65a2a2b511e27cb4e05186811e82 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 27 Mar 2014 21:20:31 +0100 Subject: erts: Fix bug in test case port_SUITE:otp_5119 --- erts/emulator/test/port_SUITE.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index bacb73671e..e01b2f253b 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1698,12 +1698,13 @@ otp_5119(Config) when is_list(Config) -> 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"}, [])), + Port2 = erlang:open_port({spawn, "exit_drv"}, []), + PI2 = port_ix(Port2), {PortIx1, PortIx2} = case PI2 > PI1 of true -> {PI1, PI2}; false -> - {port_ix(otp_5119_fill_empty_port_tab([PI2])), + {port_ix(otp_5119_fill_empty_port_tab([Port2])), port_ix(erlang:open_port({spawn, "exit_drv"}, []))} end, MaxPorts = max_ports(), -- cgit v1.2.3 From 547aa83f94c6614757e9033849c0c13563930300 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 28 Mar 2014 09:56:29 +0100 Subject: erts: Move debug printout to eliminate gcc warning --- erts/emulator/drivers/common/inet_drv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index e33594b026..09bada457d 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -9717,13 +9717,13 @@ static int tcp_remain(tcp_descriptor* desc, int* len) int n = desc->i_ptr - ptr; /* number of bytes read */ int tlen; - DEBUGF(("tcp_remain(%ld): s=%d, n=%d, nfill=%d nsz=%d, tlen %d\r\n", - (long)desc->inet.port, desc->inet.s, n, nfill, nsz, tlen)); - tlen = packet_get_length(desc->inet.htype, ptr, n, desc->inet.psize, desc->i_bufsz, &desc->http_state); + DEBUGF(("tcp_remain(%ld): s=%d, n=%d, nfill=%d nsz=%d, tlen %d\r\n", + (long)desc->inet.port, desc->inet.s, n, nfill, nsz, tlen)); + if (tlen > 0) { if (tlen <= n) { /* got a packet */ *len = tlen; -- cgit v1.2.3 From e10e918f3c08245949297e5ff1483752dd196e88 Mon Sep 17 00:00:00 2001 From: Lars Thorsen Date: Thu, 20 Mar 2014 14:59:48 +0100 Subject: Remove orber/cos*/ic files from encoding test These applications contains generated code with the latin1 directivce. --- erts/test/otp_SUITE.erl | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 1fb452501f..cd5cfcbab4 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -328,7 +328,9 @@ erl_file_encoding(_Config) -> Wc = filename:join([Root,"**","*.erl"]), ErlFiles = ordsets:subtract(ordsets:from_list(filelib:wildcard(Wc)), release_files(Root, "*.erl")), + {ok, MP} = re:compile(".*lib/(ic)|(orber)|(cos).*", [unicode]), Fs = [F || F <- ErlFiles, + filter_use_latin1_coding(F, MP), case epp:read_encoding(F) of none -> false; _ -> true @@ -342,6 +344,14 @@ erl_file_encoding(_Config) -> ?t:fail() end. +filter_use_latin1_coding(F, MP) -> + case re:run(F, MP) of + nomatch -> + true; + {match, _} -> + false + end. + xml_file_encoding(_Config) -> XmlFiles = xml_files(), Fs = [F || F <- XmlFiles, is_bad_encoding(F)], -- cgit v1.2.3 From 49d8e0f92e0b496c9be4c13edecc498b31380cc9 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 25 Mar 2014 17:41:31 +0100 Subject: erts: Add etp-lc-dump and etp-ppc-stacktrace macro --- erts/emulator/beam/erl_lock_check.c | 4 +- erts/emulator/beam/erl_process.c | 7 +++ erts/etc/unix/etp-commands.in | 87 +++++++++++++++++++++++++++++++++++-- 3 files changed, 93 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 7e3a90779d..c13eb87012 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -270,9 +270,9 @@ union erts_lc_free_block_t_ { static ethr_tsd_key locks_key; -static erts_lc_locked_locks_t *erts_locked_locks; +static erts_lc_locked_locks_t *erts_locked_locks = NULL; -static erts_lc_free_block_t *free_blocks; +static erts_lc_free_block_t *free_blocks = NULL; #ifdef ERTS_LC_STATIC_ALLOC #define ERTS_LC_FB_CHUNK_SIZE 10000 diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 8bf0cc9491..baca73f7f6 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2672,6 +2672,13 @@ aux_thread(void *unused) ErtsThrPrgrCallbacks callbacks; int thr_prgr_active = 1; +#ifdef ERTS_ENABLE_LOCK_CHECK + { + char buf[] = "aux_thread"; + erts_lc_set_thread_name(buf); + } +#endif + ssi->event = erts_tse_fetch(); callbacks.arg = (void *) ssi; diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 9e39764195..77777196a6 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -54,13 +54,23 @@ document etp-help % etp-mfa, etp-cp, % etp-msgq, etpf-msgq, % etp-stacktrace, etp-stackdump, etpf-stackdump, etp-dictdump -% etp-offheapdump, etpf-offheapdump, -% etp-print-procs, etp-search-heaps, etp-search-alloc, +% etp-process-info, etp-process-memory-info +% etp-port-info, etp-port-state, etp-port-sched-flags +% etp-heapdump, etp-offheapdump, etpf-offheapdump, +% etp-search-heaps, etp-search-alloc, % etp-ets-tables, etp-ets-tabledump % % Complex commands that use the Erlang support module. % etp-overlapped-heaps, etp-chart, etp-chart-start, etp-chart-end -% +% +% System inspection +% etp-system-info, etp-schedulers, etp-process, etp-ports, etp-lc-dump, +% etp-migration-info, etp-processes-memory, +% etp-compile-info, etp-config-h-info +% +% Platform specific (when gdb fails you) +% etp-ppc-stacktrace +% % Erlang support module handling commands: % etp-run % @@ -3133,6 +3143,77 @@ document etp-ets-tabledump %--------------------------------------------------------------------------- end +define etp-lc-dump +# Non-reentrant + set $etp_lc_dump_thread = erts_locked_locks + while $etp_lc_dump_thread + printf "Thread %s\n", $etp_lc_dump_thread->thread_name + set $etp_lc_dump_thread_locked = $etp_lc_dump_thread->locked.first + while $etp_lc_dump_thread_locked + if 0 <= $etp_lc_dump_thread_locked->id && $etp_lc_dump_thread_locked->id < sizeof(erts_lock_order)/sizeof(erts_lc_lock_order_t) + printf " %s:", erts_lock_order[$etp_lc_dump_thread_locked->id].name + else + printf " unkown:" + end + if ($etp_lc_dump_thread_locked->extra & 0x3) == 0x3 + etp-1 $etp_lc_dump_thread_locked->extra + else + printf "%p", $etp_lc_dump_thread_locked->extra + end + if ($etp_lc_dump_thread_locked->flags & (0x1f)) == (1 << 0) + printf "[spinlock]" + end + if ($etp_lc_dump_thread_locked->flags & (0x1f)) == (1 << 1) + printf "[rw(spin)lock]" + end + if ($etp_lc_dump_thread_locked->flags & (0x1f)) == (1 << 2) + printf "[mutex]" + end + if ($etp_lc_dump_thread_locked->flags & (0x1f)) == (1 << 3) + printf "[rwmutex]" + end + if ($etp_lc_dump_thread_locked->flags & (0x1f)) == (1 << 4) + printf "[proclock]" + end + printf "(%s:%d)", $etp_lc_dump_thread_locked->file, $etp_lc_dump_thread_locked->line + if ($etp_lc_dump_thread_locked->flags & (0x60)) == (1 << 5) + printf "(r)" + end + if ($etp_lc_dump_thread_locked->flags & (0x60)) == ((1 << 5) | (1 << 6)) + printf "(rw)" + end + printf "\n" + set $etp_lc_dump_thread_locked = $etp_lc_dump_thread_locked->next + end + set $etp_lc_dump_thread = $etp_lc_dump_thread->next + end +end + +document etp-lc-dump +%--------------------------------------------------------------------------- +% etp-lc-dump +% +% Dump all info about locks in the lock checker +%--------------------------------------------------------------------------- +end + +define etp-ppc-stacktrace +# Args: R1 +# Non-reentrant + set $etp_ppc_st_fp = ($arg0) + while $etp_ppc_st_fp + info symbol ((void**)$etp_ppc_st_fp)[1] + set $etp_ppc_st_fp = ((void**)$etp_ppc_st_fp)[0] + end +end + +document etp-ppc-stacktrace +%--------------------------------------------------------------------------- +% etp-ppc-stacktrace R1 +% +% Dump stacktrace from given $r1 frame pointer +%--------------------------------------------------------------------------- +end ############################################################################ # OSE support -- cgit v1.2.3 From 98ca47d657fafa4d91b128053e9286114115c0a8 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Fri, 28 Mar 2014 08:58:05 -0400 Subject: fix dirty NIF invalid memory read Dirty NIF support used an Export structure to facilitate calls to dirty NIFs and finalizers, but Export isn't large enough to hold all necessary data. This was causing an invalid memory read in beam_emu.c past the end of the Export object. Add a local extended Export struct to erl_nif.c that can hold all the necessary data. --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/erl_nif.c | 42 ++++++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 9634faff1d..1026e5f649 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3525,7 +3525,7 @@ get_map_elements_fail: erts_post_nif(&env); #ifdef ERTS_DIRTY_SCHEDULERS if (is_non_value(nif_bif_result) && c_p->freason == TRAP) { - Export* ep = (Export*) c_p->psd->data[ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT]; + Export* ep = ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(c_p); ep->code[0] = I[-3]; ep->code[1] = I[-2]; } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 063dba056e..f503b222d0 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1515,19 +1515,28 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent) #ifdef ERTS_DIRTY_SCHEDULERS +/* NIFs exports need one more item than the Export struct provides, the + * erl_module_nif*, so the DirtyNifExport below adds that. The Export + * member must be first in the struct. + */ +typedef struct { + Export exp; + struct erl_module_nif* m; +} DirtyNifExport; + static void -alloc_proc_psd(Process* proc, Export **ep) +alloc_proc_psd(Process* proc, DirtyNifExport **ep) { int i; if (!*ep) { - *ep = erts_alloc(ERTS_ALC_T_PSD, sizeof(Export)); - sys_memset((void*) *ep, 0, sizeof(Export)); + *ep = erts_alloc(ERTS_ALC_T_PSD, sizeof(DirtyNifExport)); + sys_memset((void*) *ep, 0, sizeof(DirtyNifExport)); for (i=0; iaddressv[i] = &(*ep)->code[3]; + (*ep)->exp.addressv[i] = &(*ep)->exp.code[3]; } - (*ep)->code[3] = (BeamInstr) em_call_nif; + (*ep)->exp.code[3] = (BeamInstr) em_call_nif; } - (void) ERTS_PROC_SET_DIRTY_SCHED_TRAP_EXPORT(proc, ERTS_PROC_LOCK_MAIN, *ep); + (void) ERTS_PROC_SET_DIRTY_SCHED_TRAP_EXPORT(proc, ERTS_PROC_LOCK_MAIN, &(*ep)->exp); } static ERL_NIF_TERM @@ -1560,7 +1569,7 @@ enif_schedule_dirty_nif(ErlNifEnv* env, int flags, erts_aint32_t state, n, a; Process* proc = env->proc; Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; - Export* ep = NULL; + DirtyNifExport* ep = NULL; int i; int chkflgs = (flags & (ERL_NIF_DIRTY_JOB_IO_BOUND|ERL_NIF_DIRTY_JOB_CPU_BOUND)); @@ -1585,15 +1594,16 @@ enif_schedule_dirty_nif(ErlNifEnv* env, int flags, if (a == state) break; } - if (!(ep = ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc))) + if (!(ep = (DirtyNifExport*) ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc))) alloc_proc_psd(proc, &ep); ERTS_VBUMP_ALL_REDS(proc); - ep->code[2] = argc; + ep->exp.code[2] = argc; for (i = 0; i < argc; i++) { reg[i] = (Eterm) argv[i]; } - proc->i = (BeamInstr*) ep->addressv[0]; - ep->code[4] = (BeamInstr) fp; + proc->i = (BeamInstr*) ep->exp.addressv[0]; + ep->exp.code[4] = (BeamInstr) fp; + ep->m = env->mod_nif; proc->freason = TRAP; return THE_NON_VALUE; @@ -1609,17 +1619,17 @@ enif_schedule_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result, #ifdef USE_THREADS Process* proc = env->proc; Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; - Export* ep; + DirtyNifExport* ep; erts_smp_atomic32_read_band_mb(&proc->state, ~(ERTS_PSFLG_DIRTY_CPU_PROC |ERTS_PSFLG_DIRTY_IO_PROC |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q |ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)); - if (!(ep = ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc))) + if (!(ep = (DirtyNifExport*) ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc))) alloc_proc_psd(proc, &ep); ERTS_VBUMP_ALL_REDS(proc); - ep->code[2] = 2; + ep->exp.code[2] = 2; reg[0] = (Eterm) result; #if HAVE_INT64 && SIZEOF_LONG != 8 ASSERT(sizeof(fp) <= sizeof(ErlNifUInt64)); @@ -1628,8 +1638,8 @@ enif_schedule_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result, ASSERT(sizeof(fp) <= sizeof(unsigned long)); reg[1] = (Eterm) enif_make_ulong(env, (unsigned long) fp); #endif - proc->i = (BeamInstr*) ep->addressv[0]; - ep->code[4] = (BeamInstr) execute_dirty_nif_finalizer; + proc->i = (BeamInstr*) ep->exp.addressv[0]; + ep->exp.code[4] = (BeamInstr) execute_dirty_nif_finalizer; proc->freason = TRAP; return THE_NON_VALUE; -- cgit v1.2.3 From 4ec8d3be1936bda8cb69a97619e7b7796c54948a Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Fri, 28 Mar 2014 09:10:48 -0400 Subject: prevent NIF purge during dirty NIF execution Reference-count the NIF before and after invoking a NIF on dirty schedulers to prevent having the NIF purged during the call. --- erts/emulator/beam/erl_nif.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index f503b222d0..ff551ea3af 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1543,7 +1543,7 @@ static ERL_NIF_TERM execute_dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { Eterm* reg = ERTS_PROC_GET_SCHDATA(env->proc)->x_reg_array; - ERL_NIF_TERM result = (ERL_NIF_TERM) reg[0]; + ERL_NIF_TERM result, dirty_result = (ERL_NIF_TERM) reg[0]; typedef ERL_NIF_TERM (*FinalizerFP)(ErlNifEnv*, ERL_NIF_TERM); FinalizerFP fp; #if HAVE_INT64 && SIZEOF_LONG != 8 @@ -1553,7 +1553,11 @@ execute_dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ASSERT(sizeof(fp) <= sizeof(unsigned long)); enif_get_ulong(env, reg[1], (unsigned long *) &fp); #endif - return (*fp)(env, result); + result = (*fp)(env, dirty_result); + if (erts_refc_dectest(&env->mod_nif->rt_dtor_cnt, 0) == 0 + && env->mod_nif->mod == NULL) + close_lib(env->mod_nif); + return result; } #endif /* ERTS_DIRTY_SCHEDULERS */ @@ -1606,6 +1610,8 @@ enif_schedule_dirty_nif(ErlNifEnv* env, int flags, ep->m = env->mod_nif; proc->freason = TRAP; + erts_refc_inc(&env->mod_nif->rt_dtor_cnt, 1); + return THE_NON_VALUE; #else return (*fp)(env, argc, argv); -- cgit v1.2.3 From 678649b1b2bea58c20788baa3b8dbcc06c458801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Apr 2014 11:56:25 +0200 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 54792 -> 54768 bytes erts/preloaded/ebin/erlang.beam | Bin 98276 -> 98240 bytes erts/preloaded/ebin/erts_internal.beam | Bin 4296 -> 4276 bytes erts/preloaded/ebin/init.beam | Bin 48816 -> 48796 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1480 -> 1460 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1360 -> 1340 bytes erts/preloaded/ebin/prim_file.beam | Bin 44916 -> 44892 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72932 -> 72912 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23448 -> 23424 bytes erts/preloaded/ebin/zlib.beam | Bin 13196 -> 13176 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 26f851fd7a..f1e588320b 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 344176b71b..b4e22f6d74 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index bd402f9a98..d41c833e05 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 5d49eed469..7f2d2740e1 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 2607739d12..4d22d8bace 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index d665d35ab1..efc8347b6e 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 201252de34..6c49b5185e 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index ff8265414e..fe5431c5ff 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index f80d4a96e5..73be297bbb 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index dba3ec9546..193cebdc31 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From ecbf87d335d2d3b396b50dfa69722003f3f7f646 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 1 Apr 2014 10:47:01 +0200 Subject: Support for ignoring apps in runtime_dependencies test --- erts/test/otp_SUITE.erl | 80 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index cd5cfcbab4..229d10ccee 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -392,12 +392,18 @@ is_bad_encoding(File) -> end. runtime_dependencies(Config) -> + %% Ignore applications intentionally not declaring dependencies + %% found by xref. + IgnoreApps = [diameter], + + %% Verify that (at least) OTP application runtime dependencies found %% by xref are listed in the runtime_dependencies field of the .app file %% of each application. Server = ?config(xref_server, Config), {ok, AE} = xref:q(Server, "AE"), SAE = lists:keysort(1, AE), + put(ignored_failures, []), {AppDep, AppDeps} = lists:foldl(fun ({App, App}, Acc) -> Acc; ({App, Dep}, {undefined, []}) -> @@ -409,8 +415,45 @@ runtime_dependencies(Config) -> end, {undefined, []}, SAE), - [] = check_apps_deps([AppDep|AppDeps]), - ok. + [] = lists:filter(fun ({missing_runtime_dependency, + AppFile, + common_test}) -> + %% The test_server app is contaminated by + %% common_test when run in a source tree. It + %% should however *not* be contaminated + %% when run in an installation. + case {filename:basename(AppFile), + is_run_in_src_tree()} of + {"test_server.app", true} -> + false; + _ -> + true + end; + (_) -> + true + end, + check_apps_deps([AppDep|AppDeps], IgnoreApps)), + case IgnoreApps of + [] -> + ok; + _ -> + Comment = lists:flatten(io_lib:format("Ignored applications: ~p " + "Ignored failures: ~p", + [IgnoreApps, + get(ignored_failures)])), + {comment, Comment} + end. + +is_run_in_src_tree() -> + %% At least currently run_erl is not present in /bin + %% in the source tree, but present in /bin of an + %% ordinary installation. + case file:read_file_info(filename:join([code:root_dir(), + "bin", + "run_erl"])) of + {ok, _} -> false; + {error, _} -> true + end. have_rdep(_App, [], _Dep) -> false; @@ -424,28 +467,43 @@ have_rdep(App, [RDep | RDeps], Dep) -> have_rdep(App, RDeps, Dep) end. -check_app_deps(_App, _AppFile, _AFDeps, []) -> +check_app_deps(_App, _AppFile, _AFDeps, [], _IgnoreApps) -> []; -check_app_deps(App, AppFile, AFDeps, [XRDep | XRDeps]) -> - ResOtherDeps = check_app_deps(App, AppFile, AFDeps, XRDeps), +check_app_deps(App, AppFile, AFDeps, [XRDep | XRDeps], IgnoreApps) -> + ResOtherDeps = check_app_deps(App, AppFile, AFDeps, XRDeps, IgnoreApps), case have_rdep(App, AFDeps, XRDep) of true -> ResOtherDeps; false -> - [{missing_runtime_dependency, AppFile, XRDep} | ResOtherDeps] + Failure = {missing_runtime_dependency, AppFile, XRDep}, + case lists:member(App, IgnoreApps) of + true -> + put(ignored_failures, [Failure | get(ignored_failures)]), + ResOtherDeps; + false -> + [Failure | ResOtherDeps] + end end. -check_apps_deps([]) -> +check_apps_deps([], _IgnoreApps) -> []; -check_apps_deps([{App, Deps}|AppDeps]) -> - ResOtherApps = check_apps_deps(AppDeps), +check_apps_deps([{App, Deps}|AppDeps], IgnoreApps) -> + ResOtherApps = check_apps_deps(AppDeps, IgnoreApps), AppFile = code:where_is_file(atom_to_list(App) ++ ".app"), {ok,[{application, App, Info}]} = file:consult(AppFile), case lists:keyfind(runtime_dependencies, 1, Info) of {runtime_dependencies, RDeps} -> - check_app_deps(App, AppFile, RDeps, Deps) ++ ResOtherApps; + check_app_deps(App, AppFile, RDeps, Deps, IgnoreApps) + ++ ResOtherApps; false -> - [{missing_runtime_dependencies_key, AppFile} | ResOtherApps] + Failure = {missing_runtime_dependencies_key, AppFile}, + case lists:member(App, IgnoreApps) of + true -> + put(ignored_failures, [Failure | get(ignored_failures)]), + ResOtherApps; + false -> + [Failure | ResOtherApps] + end end. %%% -- cgit v1.2.3 From e64d80a565de01e7a2493e2072967af6ddea29a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Apr 2014 15:22:26 +0200 Subject: emulator: Increase timetrap timeout for match_spec_SUITE --- erts/emulator/test/match_spec_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 330bef7104..8038888796 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -42,7 +42,7 @@ -export([init_per_testcase/2, end_per_testcase/2]). init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:seconds(10)), + Dog=?t:timetrap(?t:seconds(30)), [{watchdog, Dog}|Config]. end_per_testcase(_Func, Config) -> -- cgit v1.2.3 From 59330b43ad3f4856885f43ecaeb4df2c26d92630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Apr 2014 16:00:06 +0200 Subject: emulator: Increase timetrap timeout for binary_SUITE --- erts/emulator/test/binary_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 938aac6a0e..5da69c2cab 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -64,7 +64,7 @@ -export([sleeper/0,trapping_loop/4]). suite() -> [{ct_hooks,[ts_install_cth]}, - {timetrap,{minutes,2}}]. + {timetrap,{minutes,4}}]. all() -> [copy_terms, conversions, deep_lists, deep_bitstr_lists, -- cgit v1.2.3 From 8220849ec4019160e900bece8a7d1d1a1b1d57ba Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 1 Apr 2014 16:59:20 +0200 Subject: erts: Make binary_SUITE:deep less stressful as it times out on some machines. Not much point in redoing with lower input reductions when the terms are so big we will trap several times anyway. --- erts/emulator/test/binary_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 938aac6a0e..2ca51bfac4 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1266,7 +1266,7 @@ deep(Config) when is_list(Config) -> deep_roundtrip(T) -> B = term_to_binary(T), - T = binary_to_term_stress(B). + T = binary_to_term(B). obsolete_funs(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), -- cgit v1.2.3 From f719d0fe308f00b85f92c29d7cdf9b0dc20d98a2 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 7 Apr 2014 19:52:48 +0200 Subject: Update release notes --- erts/doc/src/notes.xml | 709 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 709 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index b4ebef72f4..eba4cdf06f 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,715 @@

This document describes the changes made to the ERTS application.

+
Erts 6.0 + +
Fixed Bugs and Malfunctions + + +

+ The option dupnames did not work as intended in re. When + looking for names with {capture, [Name, ...]}, re:run + returned a random instance of the match for that name, + instead of the leftmost matching instance, which was what + the documentation stated. This is now corrected to adhere + to the documentation. The option {capture,all_names} + along with a re:inspect/2 function is also added to + further help in using named subpatterns.

+

+ Own Id: OTP-11205

+
+ +

+ Allow loading of NIF library with unicode path name

+

+ Own Id: OTP-11408

+
+ +

+ Allow loading of driver with unicode path name

+

+ Own Id: OTP-11549

+
+ +

+ Fixed a bug where starting Erlang without having an open + stdin on fd 0 would sometimes deadlock the emulator when + terminating.

+

+ Own Id: OTP-11558

+
+ +

+ The option '-names' in epmd now works on Windows (Thanks + to Johannes Weißl)

+

+ Own Id: OTP-11565

+
+ +

+ Correction of the examples in escript documentation. + (Thanks to Pierre Fenoll).

+

+ Own Id: OTP-11577

+
+ +

+ Fix bs_get_integer instruction

+

+ The instruction bs_get_integer could unnecessarily + trigger a garbage collection in failure cases which is + unwanted or outright dangerous.

+

+ Ex:

+

+ <<X:Sz,_/bits>> = <<"some + binary">>

+

+ Previously, if Sz induced X to a bignum it would reserved + memory size this on the heap via a garbage collection + before checking if the size could actually match.

+

+ It will now check the binary size before triggering a + collection.

+

+ Own Id: OTP-11581

+
+ +

+ Remove heap space overestimation in binary_to_term + (and remote message reception) for integers in the + intervals [-2147483648,-1] and [256,2147483647] on 64-bit + emulators.

+

+ Own Id: OTP-11585

+
+ +

+ Add support for detecting the separate tinfo library from + ncurses (Thanks to Dirkjan Ochtman)

+

+ Own Id: OTP-11590

+
+ +

+ Deprecation warning for system_flag(cpu_topology) has + been extended for removal in OTP 18 (Thanks to Steve + Vinoski for the update)

+

+ Own Id: OTP-11602

+
+ +

+ Documentation improvement regarding some awkward wording + around the +spp flag. (Thanks to Brian L. Troutwine )

+

+ Own Id: OTP-11607

+
+ +

+ Fixed bug where sendfile would return the wrong error + code for a remotely closed socket if the socket was in + passive mode. (Thanks to Vincent Siliakus for reporting + the bug.)

+

+ Own Id: OTP-11614

+
+ +

+ Increase garbage collection tenure rate

+

The garbage collector tries to maintain the previous + heap block size during a minor gc, i.e. 'need' is not + utilized in determining the size of the new heap, instead + it relies on tenure and garbage to be sufficiently + large.

+

In instances during intense growing with exclusively + live data on the heap coupled with delayed tenure, + fullsweeps would be triggered directly after a minor gc + to make room for 'need' since the new heap would be + full.

+

To remedy this, the tenure of terms on the minor heap + will always happen (if it is below the high watermark) + instead of every other minor gc.

+

Characteristics Impact: Reduced CPU-time spent in + garbage collection but may infer delays in collecting + garbage from the heap. Tweak 'fullsweep_after' options to + increase gc pressure if needed.

+

+ Own Id: OTP-11617

+
+ +

+ Fix bug when comparing integers with floats larger than + 2^992. The bug could potentially cause memory corruption + on 32-bit emulators.

+

+ Own Id: OTP-11618

+
+ +

+ Cross-compilation fixes for TileraMDE-3.0.1.125620

+

+ Own Id: OTP-11635

+
+ +

+ sendfile no longer uses async threads by default

+

+ This has been done because a slow client attack is + possible if the async thread pool is used. The scenario + is:

+

+ Client does a request for a file and then slowly receives + the file one byte at a time. This will eventually fill + the async thread pool with blocking sendfile operations + and thus starving the vm of all file operations.

+

+ If you still want to use the async threads pool for + sendfile an option to enable it has been introduced.

+

+ Thanks to Christopher Faulet for identifying this + vulnerability.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11639

+
+ +

+ Do proper rollback of calls to + enif_open_resource_type when load/upgrade + callbacks of NIF library return failure.

+

+ Own Id: OTP-11722

+
+ +

+ Changed the default configuration when configuring with + $ERL_TOP/configure to be the same as when + configuring with $ERL_TOP/otp_build configure.

+

+ Previously floating point exceptions got enabled by + default on Linux when HiPE was enabled when configuring + with $ERL_TOP/configure, but not when configuring + with $ERL_TOP/otp_build configure. The default is + now in both cases not to use floating point exceptions + since there still exist unresolved issues with floating + point exceptions on Linux.

+

+ For more information see $ERL_TOP/HOWTO/INSTALL.md.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11723

+
+ +

+ A comment in erl_db_tree.c no longer differ from the + code. (Thanks to Cobus Carstens)

+

+ Own Id: OTP-11793

+
+ +

+ Fix epmd debug functionality for VxWorks (Thanks to Jay + True)

+

+ Own Id: OTP-11808

+
+ +

+ Use closefrom/2 when available in child_setup (Thanks to + Rick Reed and Anthony Ramine)

+

+ Own Id: OTP-11809

+
+ +

+ Fix dtrace/systemtap bug where the probe arguments would + be concatenated due to faulty length calculation.

+

+ Thanks to Michal Ptaszek and Scott Lystig Fritchie

+

+ Own Id: OTP-11816

+
+ +

+ It is now better documented that the +fn* flags to + erl also affect how command line parameters and + environment variables are read. (Thanks to Vlad + Dumitrescu)

+

+ Own Id: OTP-11818

+
+
+
+ + +
Improvements and New Features + + +

+ Options to set match_limit and match_limit_recursion are + added to re:run. The option report_errors is also added + to get more information when re:run fails due to limits + or compilation errors.

+

+ Own Id: OTP-10285

+
+ +

Dialyzer's unmatched_return warnings have been + corrected.

+

+ Own Id: OTP-10908

+
+ +

+ A common case is to wrap an argument to + list_to_binary/1 in a list to ensure conversion + can happen even though the argument may already be a + binary. Take special care of this case and do not copy + binary.

+

+ Impact: May cause incompatibility since a single binary + is no longer copied. Use binary:copy/1,2 instead.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11082

+
+ +

+ Make erlang:open_port/2 spawn and spawn_executable handle + unicode.

+

+ Own Id: OTP-11105

+
+ +

+ Handle unicode (widestring) in erl, erlc, heart, etc on + windows.

+

+ Own Id: OTP-11135

+
+ +

+ The version of the PCRE library Used by Erlang's re + module is raised to 8.33 from 7.6. This means, among + other things, better Unicode and Unicode Character + Properties support. New options connected to PCRE 8.33 + are also added to the re module (ucd, notempty_atstart, + no_start_optimize). PCRE has extended the regular + expression syntax between 7.6 and 8.33, why this imposes + a potential incompatibility. Only very complicated + regular expressions may be affected, but if you know you + are using obscure features, please test run your regular + expressions and verify that their behavior has not + changed.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11204

+
+ +

Filenames containing UTF-8 encoded characters can now + be handled by erlc.

+

If you have set the ERLC_EMULATOR environment + variable, note that erlc in OTP 17 will only work + with erl in OTP 17 since the protocol between the + erlc program and the erl_compile module has + changed.

+

+ Own Id: OTP-11248

+
+ +

+ By giving --enable-static-{nifs,drivers} to configure it + is now possible to statically linking of nifs and drivers + to the main Erlang VM binary. At the moment only the asn1 + and crypto nifs of the Erlang/OTP nifs and drivers have + been prepared to be statically linked. For more details + see the Installation Guide in the System documentation.

+

+ Own Id: OTP-11258

+
+ +

+ Erlang/OTP has been ported to the realtime operating + system OSE. The port supports both smp and non-smp + emulator. For details around the port and how to started + see the User's Guide in the ose application.

+

+ Note that not all parts of Erlang/OTP has been ported.

+

+ Notable things that work are: non-smp and smp emulators, + OSE signal interaction, crypto, asn1, run_erl/to_erl, + tcp, epmd, distribution and most if not all non-os + specific functionality of Erlang.

+

+ Notable things that does not work are: udp/sctp, os_mon, + erl_interface, binding of schedulers.

+

+ Own Id: OTP-11334

+
+ +

+ Add the {active,N} socket option for TCP, UDP, and SCTP, + where N is an integer in the range -32768..32767, to + allow a caller to specify the number of data messages to + be delivered to the controlling process. Once the + socket's delivered message count either reaches 0 or is + explicitly set to 0 with inet:setopts/2 or by including + {active,0} as an option when the socket is created, the + socket transitions to passive ({active, false}) mode and + the socket's controlling process receives a message to + inform it of the transition. TCP sockets receive + {tcp_passive,Socket}, UDP sockets receive + {udp_passive,Socket} and SCTP sockets receive + {sctp_passive,Socket}.

+

+ The socket's delivered message counter defaults to 0, but + it can be set using {active,N} via any gen_tcp, gen_udp, + or gen_sctp function that takes socket options as + arguments, or via inet:setopts/2. New N values are added + to the socket's current counter value, and negative + numbers can be used to reduce the counter value. + Specifying a number that would cause the socket's counter + value to go above 32767 causes an einval error. If a + negative number is specified such that the counter value + would become negative, the socket's counter value is set + to 0 and the socket transitions to passive mode. If the + counter value is already 0 and inet:setopts(Socket, + [{active,0}]) is specified, the counter value remains at + 0 but the appropriate passive mode transition message is + generated for the socket.

+

+ Thanks to Steve Vinoski

+

+ Own Id: OTP-11368

+
+ +

+ A new optional scheduler utilization balancing mechanism + has been introduced. For more information see the + +sub command + line argument.

+

+ Characteristics impact: None, when not enabled. When + enabled, changed timing in the system, normally a small + overhead due to measuring of utilization and calculating + balancing information. On some systems, such as old + Windows systems, the overhead can be quite substantial. + This time measurement overhead highly depend on the + underlying primitives provided by the OS.

+

+ Own Id: OTP-11385

+
+ +

+ A call to either the garbage_collect/1 BIF or the + check_process_code/2 BIF may trigger garbage + collection of another processes than the process calling + the BIF. The previous implementations performed these + kinds of garbage collections without considering the + internal state of the process being garbage collected. In + order to be able to more easily and more efficiently + implement yielding native code, these types of garbage + collections have been rewritten. A garbage collection + like this is now triggered by an asynchronous request + signal, the actual garbage collection is performed by the + process being garbage collected itself, and finalized by + a reply signal to the process issuing the request. Using + this approach processes can disable garbage collection + and yield without having to set up the heap in a state + that can be garbage collected.

+

+ The garbage_collect/2, + and check_process_code/3 + BIFs have been introduced. Both taking an option list as + last argument. Using these, one can issue asynchronous + requests.

+

+ code:purge/1 and code:soft_purge/1 have + been rewritten to utilize asynchronous + check_process_code requests in order to + parallelize work.

+

+ Characteristics impact: A call to the + garbage_collect/1 BIF or the + check_process_code/2 BIF will normally take longer + time to complete while the system as a whole wont be as + much negatively effected by the operation as before. A + call to code:purge/1 and code:soft_purge/1 + may complete faster or slower depending on the state of + the system while the system as a whole wont be as much + negatively effected by the operation as before.

+

+ Own Id: OTP-11388 Aux Id: OTP-11535, OTP-11648

+
+ +

+ Cleanup 'Buckets' and 'Time left' fields in crashdump to + ease parsing.

+

+ Own Id: OTP-11419

+
+ +

+ Add sync option to file:open/2.

+

+ The sync option adds the POSIX O_SYNC flag to the open + system call on platforms that support the flag or its + equivalent, e.g., FILE_FLAG_WRITE_THROUGH on Windows. For + platforms that don't support it, file:open/2 returns + {error, enotsup} if the sync option is passed in. Thank + to Steve Vinoski and Joseph Blomstedt

+

+ Own Id: OTP-11498

+
+ +

+ erlang:binary_to_term will now cost an appropriate amount + of reductions and will interrupt (yield) for reschedule + if the term is big. This avoids too long schedules when + binary_to_term is used. (Thanks to Svante Karlsson for + the original patch)

+

+ Impact: Programs running binary_to_term on large binaries + will run more smoothly, but rescheduling will impact the + single process performance of the BIF. Single threaded + benchmarks might show degraded performance of the BIF, + while general system behaviour will be improved.

+

+ Own Id: OTP-11535 Aux Id: OTP-11388

+
+ +

+ Added high resolution icon for windows. (Thanks to Daniel + Goertz for the inspiration.)

+

+ Own Id: OTP-11560

+
+ +

+ Migration of memory carriers has been enabled by default + on all ERTS internal memory allocators based on the + alloc_util + framework except for temp_alloc. That is, +M<S>acul + de is default for these allocators. Note + that this also implies changed allocation strategies for + all of these allocators. They will all now use the + "address order first fit carrier best fit" strategy.

+

+ By passing +Muacul 0 on the command line, all + configuration changes made by this change will be + reverted.

+

+ Characteristics impact: Improved memory characteristics + with a smaller memory footprint at the expense of a quite + small performance cost.

+

+ Own Id: OTP-11604 Aux Id: OTP-10279

+
+ +

A clarification has been added to the documentation of + -on_load() in the Reference Manual that it is only + recommended for loading NIF libraries.

+

+ Own Id: OTP-11611

+
+ +

+fnaw is now default when starting the + emulator; it used to be +fnl.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11612

+
+ +

+ EEP43: New data type - Maps

+

+ With Maps you may for instance: M0 = + #{ a => 1, b => 2}, % create + associations M1 = M0#{ a := 10 }, % + update values M2 = M1#{ "hi" => + "hello"}, % add new associations #{ + "hi" := V1, a := V2, b := V3} = M2. % match keys with + values

+

+ For information on how to use Maps please see the + Reference + Manual.

+

+ The current implementation is without the following + features: No variable keys + No single value access No map + comprehensions

+

+ Note that Maps is experimental during OTP 17.0.

+

+ Own Id: OTP-11616

+
+ +

+ The previously deprecated driver API function + driver_async_cancel() has been removed. Due to + this, the driver API version has been bumped to 3.0.

+

+ Thanks to Steve Vinoski.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11628

+
+ +

+ Experimental "dirty scheduler" functionality has been + introduced. In order to try the functionality out, you + need to pass the command line argument + --enable-dirty-schedulers to configure when + building the system.

+

+ Dirty schedulers can currently only be used by NIFs on a + system with SMP support. More information can be found in + the erl_nif(3) + documentation, the erl(1) documentation, and + in the git commit comment of commit + 'c1c03ae4ee50e58b7669ea88ec4d29c6b2b67c7b'.

+

+ Note that the functionality is experimental, and + not supported. This functionality will + be subject to backward incompatible changes. You should + not enable the dirty scheduler functionality on + production systems. It is only provided for testing.

+

+ Thanks to Steve Vinoski.

+

+ Own Id: OTP-11629

+
+ +

+ Improve reduction cost and yielding of + term_to_binary. The reduction cost is increased + and garbage collection is disabled during yield.

+

+ Impact: Improves system responsiveness when + term_to_binary is called with large terms without + significant degradation of single threaded performance.

+

+ Own Id: OTP-11648 Aux Id: OTP-11388

+
+ +

+ By default, the system's version of zlib will be used, + provided its version is 1.2.4 or higher; otherwise the + built-in zlib will be used. The built-in version of zlib + has been bumped to 1.2.8. (Use the + --enable-builtin-zlib option to configure + to force the use of the built-in zlib.)

+

+ Own Id: OTP-11669

+
+ +

+ The default float encoding in binary_to_term and + external_size has been changed to use minor_mode 1 + instead of 0.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11738

+
+ +

+ Introduced the configure option + --with-assumed-cache-line-size=SIZE. For more + information see $ERL_TOP/HOWTO/INSTALL.md.

+

+ Own Id: OTP-11742

+
+ +

+ Halfword emulator is marked as deprecated. It still works + as before but is planned to be removed in a future major + release.

+

+ Own Id: OTP-11777

+
+ +

+ The external format for Maps has changed in a way that is + not compatible with the format used in OTP 17.0-rc1 and + OTP 17.0-rc2.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11782

+
+ +

+ Fixed faulty make dependency that would make some make + versions fail while building gen_git_version.mk.

+

+ Own Id: OTP-11784

+
+ +

+ Introduced functionality for allowing old drivers and NIF + libraries to be loaded during a transition period. For + more information see the version + management section in the erl_driver(3) + documentation and the version + management section in the erl_nif(3) + documentation.

+

+ Own Id: OTP-11799

+
+ +

+ Support file paths longer than 259 characters on Windows. + Long absolute paths are automatically converted to UNC + format with a \\?\ prefix which is the only way to + represent long paths. The 259 character limit still + applies for individual file names, relative paths and the + current working directory.

+

+ Own Id: OTP-11813

+
+ +

+ Document that escript:create/2 also accepts a 3-elements + tuple containing files and zip:create/3 options to build + a zip file.

+

+ Thanks to Pierre Fenoll

+

+ Own Id: OTP-11827

+
+ +

+ Add systemd socket activation for epmd.

+

+ Thanks to Matwey V. Kornilov

+

+ Own Id: OTP-11829

+
+
+
+ +
+
Erts 5.10.4
Fixed Bugs and Malfunctions -- cgit v1.2.3 From fa7f3e7ec457f1143ed4a3db1aa9d7aed8005a57 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 8 Apr 2014 15:03:17 +0200 Subject: erts: Fix system_monitor(large_heap) for non-smp VM No message for large_heap monitoring was ever sent on non-smp VM. Bug introduced in R16B. --- erts/emulator/beam/erl_trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 305058ceff..ea5c850a30 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2013. All Rights Reserved. + * Copyright Ericsson AB 1999-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -2524,7 +2524,7 @@ monitor_large_heap(Process *p) { #ifndef ERTS_SMP ASSERT(is_internal_pid(system_monitor)); monitor_p = erts_proc_lookup(system_monitor); - if (monitor_p || p == monitor_p) { + if (!monitor_p || p == monitor_p) { return; } #endif -- cgit v1.2.3 From 44648ef43dc7cf2929a9df34025e93369776747f Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 10 Apr 2014 03:00:19 +0200 Subject: Fixed type spec of erlang:system_info/1 --- erts/preloaded/ebin/erlang.beam | Bin 98240 -> 98248 bytes erts/preloaded/src/erlang.erl | 1 - 2 files changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index b4e22f6d74..e19bb370bc 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index cabbbd191f..1508eed9ee 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2264,7 +2264,6 @@ tuple_to_list(_Tuple) -> (modified_timing_level) -> integer() | undefined; (multi_scheduling) -> disabled | blocked | enabled; (multi_scheduling_blockers) -> [PID :: pid()]; - (otp_correction_package) -> string(); (otp_release) -> string(); (port_count) -> non_neg_integer(); (port_limit) -> pos_integer(); -- cgit v1.2.3 From c6237ad7b6b485990f915daa2b1cf0a9095f129d Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 10 Apr 2014 04:46:19 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 23 +++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index eba4cdf06f..68feaa027a 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,29 @@

This document describes the changes made to the ERTS application.

+
Erts 6.0.1 + +
Fixed Bugs and Malfunctions + + +

+ Fix broken system monitoring of large_heap for + non-smp VM. No message for large_heap was ever + sent on non-smp VM. Bug exist since R16B.

+

+ Own Id: OTP-11852

+
+ +

+ Fixed type spec of erlang:system_info/1.

+

+ Own Id: OTP-11859 Aux Id: OTP-11615

+
+
+
+ +
+
Erts 6.0
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 081fb66398..a108b1d100 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.0 +VSN = 6.0.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 9633e0ebc304cb003b3ef06433e6b7ccdc9e983a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 8 Apr 2014 17:46:52 +0200 Subject: win32: Fix quoting of paths --- erts/emulator/sys/win32/sys.c | 114 ++++++++++++++++++-------------------- erts/emulator/test/port_SUITE.erl | 6 ++ 2 files changed, 60 insertions(+), 60 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 0ded6b274e..ae44c8424f 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -1392,39 +1392,46 @@ int parse_command(wchar_t* cmd){ return i; } -static BOOL need_quotes(wchar_t *str) -{ - int in_quote = 0; - int backslashed = 0; - int naked_space = 0; - while (*str != L'\0') { - switch (*str) { - case L'\\' : - backslashed = !backslashed; - break; - case L'"': - if (backslashed) { - backslashed=0; - } else { - in_quote = !in_quote; - } - break; - case L' ': - backslashed = 0; - if (!(backslashed || in_quote)) { - naked_space++; - } - break; - default: - backslashed = 0; +/* + * Translating of command line arguments to correct format. In the examples + * below the '' are not part of the actual string. + * 'io:format("hello").' -> 'io:format(\"hello\").' + * 'io:format("is anybody in there?").' -> '"io:format(\"is anybody in there?\")."' + * 'Just nod if you can hear me.' -> '"Just nod if you can hear me."' + * 'Is there ""anyone at home?' -> '"Is there \"\"anyone at home?"' + * 'Relax."' -> 'Relax.\"' + * + * If new == NULL we just calculate the length. + * + * The reason for having to quote all of the is becasue CreateProcessW removes + * one level of escaping since it takes a single long command line rather + * than the argument chunks that unix uses. + */ +static int escape_and_quote(wchar_t *str, wchar_t *new, BOOL *quoted) { + int i, j = 0; + if (new == NULL) + *quoted = FALSE; + else if (*quoted) + new[j++] = L'"'; + for ( i = 0; str[i] != L'\0'; i++,j++) { + if (str[i] == L' ' && new == NULL && *quoted == FALSE) { + *quoted = TRUE; + j++; + } + /* check if we have to escape quotes */ + if (str[i] == L'"') { + if (new) new[j] = L'\\'; + j++; } - ++str; + if (new) new[j] = str[i]; } - return (naked_space > 0); + if (*quoted) { + if (new) new[j] = L'"'; + j++; + } + return j; } - - /* *---------------------------------------------------------------------- @@ -1585,31 +1592,24 @@ create_child_process wcscpy(appname, execPath); } if (argv == NULL) { - BOOL orig_need_q = need_quotes(execPath); + BOOL orig_need_q; wchar_t *ptr; - int ocl = wcslen(execPath); + int ocl = escape_and_quote(execPath, NULL, &orig_need_q); if (run_cmd) { newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, - (ocl + ((orig_need_q) ? 3 : 1) - + 11)*sizeof(wchar_t)); + (ocl + 1 + 11)*sizeof(wchar_t)); memcpy(newcmdline,L"cmd.exe /c ",11*sizeof(wchar_t)); ptr = newcmdline + 11; } else { newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, - (ocl + ((orig_need_q) ? 3 : 1))*sizeof(wchar_t)); + (ocl + 1)*sizeof(wchar_t)); ptr = (wchar_t *) newcmdline; } - if (orig_need_q) { - *ptr++ = L'"'; - } - memcpy(ptr,execPath,ocl*sizeof(wchar_t)); - ptr += ocl; - if (orig_need_q) { - *ptr++ = L'"'; - } - *ptr = L'\0'; + ptr += escape_and_quote(execPath, ptr, &orig_need_q); + ptr[0] = L'\0'; } else { - int sum = 1; /* '\0' */ + int sum = 0; + BOOL *qte = NULL; wchar_t **ar = argv; wchar_t *n; wchar_t *save_arg0 = NULL; @@ -1620,11 +1620,13 @@ create_child_process if (run_cmd) { sum += 11; /* cmd.exe /c */ } + + while (*ar != NULL) ar++; + qte = erts_alloc(ERTS_ALC_T_TMP, (ar - argv)*sizeof(BOOL)); + + ar = argv; while (*ar != NULL) { - sum += wcslen(*ar); - if (need_quotes(*ar)) { - sum += 2; /* quotes */ - } + sum += escape_and_quote(*ar,NULL,qte+(ar - argv)); sum++; /* space */ ++ar; } @@ -1636,26 +1638,18 @@ create_child_process n += 11; } while (*ar != NULL) { - int q = need_quotes(*ar); - sum = wcslen(*ar); - if (q) { - *n++ = L'"'; - } - memcpy(n,*ar,sum*sizeof(wchar_t)); - n += sum; - if (q) { - *n++ = L'"'; - } + n += escape_and_quote(*ar,n,qte+(ar - argv)); *n++ = L' '; ++ar; } - *(n-1) = L'\0'; + *(n-1) = L'\0'; /* overwrite last space with '\0' */ if (save_arg0 != NULL) { argv[0] = save_arg0; } + erts_free(ERTS_ALC_T_TMP, qte); } - DEBUGF(("Creating child process: %s, createFlags = %d\n", newcmdline, createFlags)); + DEBUGF((stderr,"Creating child process: %S, createFlags = %d\n", newcmdline, createFlags)); ok = CreateProcessW((wchar_t *) appname, (wchar_t *) newcmdline, NULL, diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index e01b2f253b..738d60b8a4 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1405,6 +1405,12 @@ spawn_executable(Config) when is_list(Config) -> run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]), [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[binary, ExactFile2,"hello world","dlrow olleh"]), + + [ExactFile2,"hello \"world\"","\"dlrow\" olleh"] = + run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]), + [ExactFile2,"hello \"world\"","\"dlrow\" olleh"] = + run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]), + [ExactFile2] = run_echo_args(SpaceDir,[default]), [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world", -- cgit v1.2.3 From fc2b873cc6236841f73afdcdc5ac81105a9a3cd3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 10 Apr 2014 20:38:41 +0200 Subject: Development versions --- erts/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index 081fb66398..2e773079f3 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.0 +VSN = 6.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From c118faa71ee507d32cfb3e7bcac58353045c79e8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 10 Apr 2014 20:42:10 +0200 Subject: Development versions --- erts/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index 081fb66398..ab98bd4a17 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.0 +VSN = 7.0 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 338698874e1f57a38c469e6e5b876d12ee14a0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 11 Apr 2014 15:22:23 +0200 Subject: The encoding for '-eval Program' should be UTF-8 If file:name_native_encoding() returns 'utf8', any arguments following the -run option are assumed to be encoded in UTF-8. -eval should treat is argument the same way. --- erts/preloaded/ebin/init.beam | Bin 48796 -> 48780 bytes erts/preloaded/src/init.erl | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 7f2d2740e1..26f779500c 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index ab8464956c..e95e11b3e6 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -1041,7 +1041,7 @@ start_em([]) -> ok. start_it([]) -> ok; start_it({eval,Bin}) -> - Str = binary_to_list(Bin), + Str = b2s(Bin), {ok,Ts,_} = erl_scan:string(Str), Ts1 = case reverse(Ts) of [{dot,_}|_] -> Ts; -- cgit v1.2.3 From 824dea7e53116af0ff722aaf2f9b21f564b3bfd2 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 16 Apr 2014 14:08:12 +0200 Subject: erts: Add etp commands for alloc_util block and carrier inspection etp-block etp-block2mbc etp-carrier-blocks --- erts/etc/unix/etp-commands.in | 150 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 9e39764195..53c1e24a57 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2012. All Rights Reserved. +# Copyright Ericsson AB 2005-2014. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -3176,6 +3176,154 @@ define etp-thr source @ERL_TOP@/erts/etc/unix/etp-thr.py end +############################################################################ +# erl_alloc_util (blocks and carriers) +# + +define etp-block-size-1 +# +# In: (Block_t*) in $arg0 +# Out: Byte size in $etp_blk_sz +# + if ($arg0)->bhdr & 1 + # Free block + set $etp_blk_sz = ($arg0)->bhdr & ~7 + else + # Allocated block + if !$etp_MBC_ABLK_SZ_MASK + if etp_arch_bits == 64 + set $etp_MBC_ABLK_OFFSET_SHIFT = (64 - 24) + else + set $etp_MBC_ABLK_OFFSET_SHIFT = (32 - 9) + end + set $etp_MBC_ABLK_SZ_MASK = ~(~0 << $etp_MBC_ABLK_OFFSET_SHIFT) & ~7 + end + set $etp_blk_sz = ($arg0)->bhdr & $etp_MBC_ABLK_SZ_MASK + end +end + +define etp-block2mbc-1 +# +# In: (Block_t*) in $arg0 +# Out: (Carrier_t*) in $etp-mbc +# + if (($arg0)->bhdr) & 1 + # Free block + set $etp_mbc = ($arg0)->u.carrier + else + # Allocated block + if !$etp_MBC_ABLK_OFFSET_SHIFT + if etp_arch_bits == 64 + set $etp_MBC_ABLK_OFFSET_SHIFT = (64 - 24) + else + set $etp_MBC_ABLK_OFFSET_SHIFT = (32 - 9) + end + end + set $etp_mbc = (Carrier_t*) ((((UWord)($arg0)) & (~0 << 18)) - ((($arg0)->bhdr >> $etp_MBC_ABLK_OFFSET_SHIFT) << 18)) + end +end + +define etp-block2mbc + etp-block2mbc-1 ((Block_t*)$arg0) + print $etp_mbc +end + +document etp-block2mbc +%--------------------------------------------------------------------------- +% Print pointer to multiblock carrier containing the argument (Block_t*) +%--------------------------------------------------------------------------- +end + +define etp-block + etp-block-size-1 ((Block_t*)$arg0) + if ((Block_t*)$arg0)->bhdr & 1 + printf "%#lx: FREE sz=%#x\n", ($arg0), $etp_blk_sz + else + printf "%#lx: ALLOCATED sz=%#x\n", ($arg0), $etp_blk_sz + end +end + +document etp-block +%--------------------------------------------------------------------------- +% Print memory block (Block_t*) +%--------------------------------------------------------------------------- +end + +define etp-carrier-blocks + set $etp_crr = (Carrier_t*) $arg0 + set $etp_alc = (Allctr_t*)($etp_crr->allctr.counter & ~7) + set $etp_blk = (Block_t*) ((char*)$etp_crr + $etp_alc->mbc_header_size) + set $etp_prev_blk = 0 + set $etp_error_cnt = 0 + set $etp_ablk_cnt = 0 + set $etp_fblk_cnt = 0 + + if $argc == 2 + set $etp_be_silent = $arg1 + else + set $etp_be_silent = 0 + end + + while 1 + if !$etp_be_silent + etp-block $etp_blk + else + etp-block-size-1 $etp_blk + end + etp-block2mbc-1 $etp_blk + if $etp_mbc != $etp_crr + printf "ERROR: Invalid carrier pointer %#lx in block at %#lx\n", $etp_mbc, $etp_blk + set $etp_error_cnt = $etp_error_cnt + 1 + end + if $etp_prev_blk + if ($etp_prev_blk->bhdr & 1) + # Prev is FREE + if ($etp_blk->bhdr & 1) + printf "ERROR: Adjacent FREE blocks at %#lx and %#lx\n", $etp_prev_blk, $etp_blk + set $etp_error_cnt = $etp_error_cnt + 1 + end + if !($etp_blk->bhdr & 2) + printf "ERROR: Missing PREV_FREE_BLK_HDR_FLG (2) in block at %#lx\n", $etp_blk + set $etp_error_cnt = $etp_error_cnt + 1 + end + end + end + if $etp_blk->bhdr & 1 + set $etp_fblk_cnt = $etp_fblk_cnt + 1 + else + set $etp_ablk_cnt = $etp_ablk_cnt + 1 + end + if $etp_blk->bhdr & 4 + # Last block + loop_break + end + # All free blocks except the last have a footer + if ($etp_blk->bhdr & 1) && ((UWord*)((char*)$etp_blk + $etp_blk_sz))[-1] != $etp_blk_sz + printf "ERROR: Invalid footer of free block at %#lx\n", $etp_blk + end + set $etp_prev_blk = $etp_blk + set $etp_blk = (Block_t*) ((char*)$etp_blk + $etp_blk_sz) + end + + if ((char*)$etp_blk + $etp_blk_sz) != ((char*)$etp_crr + ($etp_crr->chdr & ~7)) + printf "ERROR: Last block not at end of carrier\n" + set $etp_error_cnt = $etp_error_cnt + 1 + end + printf "Allocated blocks: %u\n", $etp_ablk_cnt + printf "Free blocks: %u\n", $etp_fblk_cnt + if $etp_error_cnt + printf "%u ERRORs reported above\n", $etp-error-cnt + end +end + +document etp-carrier-blocks +%--------------------------------------------------------------------------- +% Check and (maybe) print all memory blocks in carrier +% Args: (Carrier_t*) [1=be_silent] +%--------------------------------------------------------------------------- +end + + ############################################################################ # Toolbox parameter handling # -- cgit v1.2.3 From 21207dc3deb90e02a7746df9c8794f5471d4a5d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 7 Apr 2014 12:16:45 +0200 Subject: Document that spawn_opt/5 does not support the 'monitor' option --- erts/doc/src/erlang.xml | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index e7e9b218f2..0f4dfc0f98 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4842,6 +4842,8 @@ true Node does not exist, a useless pid is returned. Otherwise works like spawn_opt/4.

+

The monitor option is currently not supported by + spawn_opt/5.

-- cgit v1.2.3 From 0e38f3d443842728be24e4cbd1608dcaa3a41bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Fri, 28 Mar 2014 15:08:30 +0100 Subject: Remove the pg module and related documentation This module has been marked experimental for more than 15 years, and has largely been superseded by the pg2 module from the kernel application. The original pg also has no tests and has not been updated in the last 15 years other than small maintenance edits (like adding specs or replacing pid/1 by is_pid/1). It is pretty unlikely that anyone uses it today and its presence is simply confusing as people should be using pg2 anyway. --- erts/test/erlc_SUITE_data/src/start_ok.script | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/test/erlc_SUITE_data/src/start_ok.script b/erts/test/erlc_SUITE_data/src/start_ok.script index 4cd89f0439..7ef97dc3f3 100644 --- a/erts/test/erlc_SUITE_data/src/start_ok.script +++ b/erts/test/erlc_SUITE_data/src/start_ok.script @@ -52,7 +52,6 @@ shell_default, timer, gen_fsm, - pg, unix, dict, pool, @@ -156,7 +155,6 @@ {timer,1}, {gen_fsm,1}, {io_lib_pretty,1}, - {pg,1}, {slave,1}, {unix,1}, {dict,1}, -- cgit v1.2.3 From e492b43c3e4366e865e5f1c34d0834df2a91d490 Mon Sep 17 00:00:00 2001 From: Eiichi Tsukata Date: Wed, 23 Apr 2014 21:46:21 +0900 Subject: Add erlang:system_info(tolerant_timeofday) Add erlang:system_info(tolerant_timeofday), an API to check whether compensation for sudden changes of system time is enabled or not. --- erts/doc/src/erl.xml | 5 ++++- erts/doc/src/erlang.xml | 7 +++++++ erts/emulator/beam/erl_bif_info.c | 5 +++++ erts/emulator/test/system_info_SUITE.erl | 1 + erts/preloaded/src/erlang.erl | 1 + 5 files changed, 18 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 9724a1345a..f8f4d14436 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -495,7 +495,7 @@ , not (). Note also that is used instead of on Windows.

- +

Disable compensation for sudden changes of system time.

Normally, will not immediately reflect @@ -510,6 +510,9 @@ reflect the current system time. Note that timers are based on . If the system time jumps, timers then time out at the wrong time.

+

NOTE: You can check whether the adjustment is enabled or + disabled by calling + erlang:system_info(tolerant_timeofday).

diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index e7e9b218f2..49cd0a8a52 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6293,6 +6293,13 @@ ok (driver_async()) as an integer.

+ tolerant_timeofday + +

Returns whether compensation for sudden changes of system + time is enabled or disabled.

+

See also +c + command line flag.

+
trace_control_word

Returns the value of the node's trace control word. diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 2adba9b240..4d5e55aaf5 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2691,6 +2691,11 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) else if (ERTS_IS_ATOM_STR("ets_limit",BIF_ARG_1)) { BIF_RET(make_small(erts_db_get_max_tabs())); } + else if (ERTS_IS_ATOM_STR("tolerant_timeofday",BIF_ARG_1)) { + BIF_RET(erts_disable_tolerant_timeofday + ? am_disabled + : am_enabled); + } BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index ceb4afb5cf..f959714be7 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -155,6 +155,7 @@ misc_smoke_tests(Config) when is_list(Config) -> ?line true = is_binary(erlang:system_info(loaded)), ?line true = is_binary(erlang:system_info(dist)), ?line ok = try erlang:system_info({cpu_topology,erts_get_cpu_topology_error_case}), fail catch error:badarg -> ok end, + true = lists:member(erlang:system_info(tolerant_timeofday), [enabled, disabled]), ?line ok. diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index cabbbd191f..23dbd37a8f 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2287,6 +2287,7 @@ tuple_to_list(_Tuple) -> (system_architecture) -> string(); (threads) -> boolean(); (thread_pool_size) -> non_neg_integer(); + (tolerant_timeofday) -> enabled | disabled; (trace_control_word) -> non_neg_integer(); (update_cpu_info) -> changed | unchanged; (version) -> string(); -- cgit v1.2.3 From daa54059610c3f36057a19285357da19ad45e8f0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 7 Apr 2014 21:12:19 +0200 Subject: Fix timeout for match_spec_SUITE:otp_9422 Must receive 'abort' even after loop_runner has finished. --- erts/emulator/test/match_spec_SUITE.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 8038888796..fdce157abc 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -1009,12 +1009,14 @@ loop_runner(Collector, Fun, Laps) -> end, loop_runner_cont(Collector, Fun, 0, Laps). -loop_runner_cont(_Collector, _Fun, Laps, Laps) -> +loop_runner_cont(Collector, _Fun, Laps, Laps) -> receive - {done, Collector} -> - io:format("loop_runner ~p exit after ~p laps\n", [self(), Laps]), - Collector ! {gone, self()} - end; + {done, Collector} -> ok; + {abort, Collector} -> ok + end, + io:format("loop_runner ~p exit after ~p laps\n", [self(), Laps]), + Collector ! {gone, self()}; + loop_runner_cont(Collector, Fun, N, Laps) -> Fun(), receive -- cgit v1.2.3 From a652d1d882d6f4ddb46a23bf550cb8d12e403e8c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 29 Apr 2014 15:24:41 +0200 Subject: Fix race between ETS table deletion and unfixation Symptom: VM crash running mnesia_SUITE Scenario: Process A terminates while still having fixed table T and process B "at the same time" deletes table T with ets:delete/1 or by terminating. Problem: A table scheduled for deallocation do only have a valid 'common' part. The unfix-table-at-process-exit code tried to read the hash-specific part of such a table. Solution: Must back off if DB_DELETE flag is set. Since: R16B --- erts/emulator/beam/erl_db.c | 62 ++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 29 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index a5d67571e2..8f246ffa07 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -259,10 +259,11 @@ static void schedule_free_dbtable(DbTable* tb) /* * NON-SMP case: Caller is *not* allowed to access the *tb * structure after this function has returned! - * SMP case: Caller is allowed to access the *tb structure - * until the bif has returned (we typically - * need to unlock the table lock after this - * function has returned). + * SMP case: Caller is allowed to access the *common* part of the *tb + * structure until the bif has returned (we typically need to + * unlock the table lock after this function has returned). + * Caller is *not* allowed to access the specialized part + * (hash or tree) of *tb after this function has returned. */ ASSERT(erts_refc_read(&tb->common.ref, 0) == 0); erts_schedule_thr_prgr_later_cleanup_op(free_dbtable, @@ -3279,34 +3280,37 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks) } erts_smp_rwmtx_runlock(mmtl); if (tb) { - int reds; - DbFixation** pp; + int reds = 0; db_lock(tb, LCK_WRITE_REC); - #ifdef ERTS_SMP - erts_smp_mtx_lock(&tb->common.fixlock); - #endif - reds = 10; - - for (pp = &tb->common.fixations; *pp != NULL; - pp = &(*pp)->next) { - if ((*pp)->pid == pid) { - DbFixation* fix = *pp; - erts_aint_t diff = -((erts_aint_t) fix->counter); - erts_refc_add(&tb->common.ref,diff,0); - *pp = fix->next; - erts_db_free(ERTS_ALC_T_DB_FIXATION, - tb, fix, sizeof(DbFixation)); - ERTS_ETS_MISC_MEM_ADD(-sizeof(DbFixation)); - break; + if (!(tb->common.status & DB_DELETE)) { + DbFixation** pp; + + #ifdef ERTS_SMP + erts_smp_mtx_lock(&tb->common.fixlock); + #endif + reds = 10; + + for (pp = &tb->common.fixations; *pp != NULL; + pp = &(*pp)->next) { + if ((*pp)->pid == pid) { + DbFixation* fix = *pp; + erts_aint_t diff = -((erts_aint_t) fix->counter); + erts_refc_add(&tb->common.ref,diff,0); + *pp = fix->next; + erts_db_free(ERTS_ALC_T_DB_FIXATION, + tb, fix, sizeof(DbFixation)); + ERTS_ETS_MISC_MEM_ADD(-sizeof(DbFixation)); + break; + } + } + #ifdef ERTS_SMP + erts_smp_mtx_unlock(&tb->common.fixlock); + #endif + if (!IS_FIXED(tb) && IS_HASH_TABLE(tb->common.status)) { + db_unfix_table_hash(&(tb->hash)); + reds += 40; } - } - #ifdef ERTS_SMP - erts_smp_mtx_unlock(&tb->common.fixlock); - #endif - if (!IS_FIXED(tb) && IS_HASH_TABLE(tb->common.status)) { - db_unfix_table_hash(&(tb->hash)); - reds += 40; } db_unlock(tb, LCK_WRITE_REC); BUMP_REDS(c_p, reds); -- cgit v1.2.3 From 23246d73bbd3188e2c0a45408b9bd29fd034ccaf Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 25 Apr 2014 22:31:41 +0200 Subject: erts: Save some space in process struct for hipe by combining hipe.ncallee and hipe.closure in a union as the comment indicate should be possible. --- erts/emulator/beam/beam_emu.c | 4 ++-- erts/emulator/hipe/hipe_debug.c | 2 +- erts/emulator/hipe/hipe_mkliterals.c | 4 ++-- erts/emulator/hipe/hipe_mode_switch.c | 20 ++++++++++---------- erts/emulator/hipe/hipe_process.h | 7 ++++--- erts/emulator/hipe/hipe_risc_stack.c | 4 ++-- erts/emulator/hipe/hipe_x86_stack.c | 4 ++-- 7 files changed, 23 insertions(+), 22 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 1026e5f649..f6524f36d2 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4993,14 +4993,14 @@ get_map_elements_fail: * ... remainder of original BEAM code */ ASSERT(I[-5] == (Uint) OpCode(i_func_info_IaaI)); - c_p->hipe.ncallee = (void(*)(void)) I[-4]; + c_p->hipe.u.ncallee = (void(*)(void)) I[-4]; cmd = HIPE_MODE_SWITCH_CMD_CALL | (I[-1] << 8); ++hipe_trap_count; goto L_hipe_mode_switch; } OpCase(hipe_trap_call_closure): { ASSERT(I[-5] == (Uint) OpCode(i_func_info_IaaI)); - c_p->hipe.ncallee = (void(*)(void)) I[-4]; + c_p->hipe.u.ncallee = (void(*)(void)) I[-4]; cmd = HIPE_MODE_SWITCH_CMD_CALL_CLOSURE | (I[-1] << 8); ++hipe_trap_count; goto L_hipe_mode_switch; diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c index 32694a8f97..7f82252308 100644 --- a/erts/emulator/hipe/hipe_debug.c +++ b/erts/emulator/hipe/hipe_debug.c @@ -231,7 +231,7 @@ void hipe_print_pcb(Process *p) U("nsp ", hipe.nsp); U("nstack ", hipe.nstack); U("nstend ", hipe.nstend); - U("ncallee ", hipe.ncallee); + U("ncallee ", hipe.u.ncallee); hipe_arch_print_pcb(&p->hipe); #endif /* HIPE */ #undef U diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index 0e287908b1..23e9214cec 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -498,8 +498,8 @@ static const struct rts_param rts_params[] = { { 38, "P_ARG4", 1, offsetof(struct process, def_arg_reg[4]) }, { 39, "P_ARG5", 1, offsetof(struct process, def_arg_reg[5]) }, { 40, "P_NSP", 1, offsetof(struct process, hipe.nsp) }, - { 41, "P_NCALLEE", 1, offsetof(struct process, hipe.ncallee) }, - { 42, "P_CLOSURE", 1, offsetof(struct process, hipe.closure) }, + { 41, "P_NCALLEE", 1, offsetof(struct process, hipe.u.ncallee) }, + { 42, "P_CLOSURE", 1, offsetof(struct process, hipe.u.closure) }, { 43, "P_NSP_LIMIT", 1, offsetof(struct process, hipe.nstack) }, { 44, "P_CSP", #if defined(__i386__) || defined(__x86_64__) diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 4ddc2790b1..7b9f2b5350 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -257,14 +257,14 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) /* BEAM calls a native code function */ unsigned arity = cmd >> 8; - /* p->hipe.ncallee set in beam_emu */ + /* p->hipe.u.ncallee set in beam_emu */ if (p->cp == hipe_beam_pc_return) { /* Native called BEAM, which now tailcalls native. */ hipe_pop_beam_trap_frame(p); result = hipe_tailcall_to_native(p, arity, reg); break; } - DPRINTF("calling %#lx/%u", (long)p->hipe.ncallee, arity); + DPRINTF("calling %#lx/%u", (long)p->hipe.u.ncallee, arity); result = hipe_call_to_native(p, arity, reg); break; } @@ -282,18 +282,18 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) arity -= funp->num_free; /* arity == #formals */ reg[arity] = fun; ++arity; /* correct for having added the closure */ - /* HIPE_ASSERT(p->hipe.ncallee == (void(*)(void))funp->native_address); */ + /* HIPE_ASSERT(p->hipe.u.ncallee == (void(*)(void))funp->native_address); */ /* just like a normal call from now on */ - /* p->hipe.ncallee set in beam_emu */ + /* p->hipe.u.ncallee set in beam_emu */ if (p->cp == hipe_beam_pc_return) { /* Native called BEAM, which now tailcalls native. */ hipe_pop_beam_trap_frame(p); result = hipe_tailcall_to_native(p, arity, reg); break; } - DPRINTF("calling %#lx/%u", (long)p->hipe.ncallee, arity); + DPRINTF("calling %#lx/%u", (long)p->hipe.u.ncallee, arity); result = hipe_call_to_native(p, arity, reg); break; } @@ -422,15 +422,15 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) * F(A1, ..., AN, FV1, ..., FVM, Closure) * (Where Ai is argument i and FVj is free variable j) * - * p->hipe.closure contains the closure + * p->hipe.u.closure contains the closure * p->def_arg_reg[] contains the register parameters * p->hipe.nsp[] contains the stacked parameters */ ErlFunThing *closure; unsigned num_free, arity, i, is_recursive; - HIPE_ASSERT(is_fun(p->hipe.closure)); - closure = (ErlFunThing*)fun_val(p->hipe.closure); + HIPE_ASSERT(is_fun(p->hipe.u.closure)); + closure = (ErlFunThing*)fun_val(p->hipe.u.closure); num_free = closure->num_free; arity = closure->fe->arity; @@ -463,7 +463,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) result = HIPE_MODE_SWITCH_RES_CALL; } /* Append the closure as the last parameter. Don't increment arity. */ - reg[arity] = p->hipe.closure; + reg[arity] = p->hipe.u.closure; if (is_recursive) { /* BEAM called native, which now calls BEAM. @@ -569,7 +569,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) address = hipe_get_remote_na(mfa[0], mfa[1], arity); if (!address) goto do_apply_fail; - p->hipe.ncallee = (void(*)(void)) address; + p->hipe.u.ncallee = (void(*)(void)) address; result = hipe_tailcall_to_native(p, arity, reg); goto do_return_from_native; do_apply_fail: diff --git a/erts/emulator/hipe/hipe_process.h b/erts/emulator/hipe/hipe_process.h index 4ee99d78a2..cd88a37f80 100644 --- a/erts/emulator/hipe/hipe_process.h +++ b/erts/emulator/hipe/hipe_process.h @@ -28,9 +28,10 @@ struct hipe_process_state { Eterm *nsp; /* Native stack pointer. */ Eterm *nstack; /* Native stack block start. */ Eterm *nstend; /* Native stack block end (start+size). */ - /* XXX: ncallee and closure could share space in a union */ - void (*ncallee)(void); /* Native code callee (label) to invoke. */ - Eterm closure; /* Used to pass a closure from native code. */ + union { + void (*ncallee)(void); /* Native code callee (label) to invoke. */ + Eterm closure; /* Used to pass a closure from native code. */ + }u; Eterm *nstgraylim; /* Gray/white stack boundary. */ Eterm *nstblacklim; /* Black/gray stack boundary. Must exist if graylim exists. Ignored if no graylim. */ diff --git a/erts/emulator/hipe/hipe_risc_stack.c b/erts/emulator/hipe/hipe_risc_stack.c index 1183856c7e..bea3a0fecd 100644 --- a/erts/emulator/hipe/hipe_risc_stack.c +++ b/erts/emulator/hipe/hipe_risc_stack.c @@ -226,7 +226,7 @@ void (*hipe_handle_stack_trap(Process *p))(void) * The native stack MUST contain a stack frame as it appears on * entry to a function (actuals, caller's frame, caller's return address). * p->hipe.narity MUST contain the arity (number of actuals). - * On exit, p->hipe.ncallee is set to the handler's PC and p->hipe.nsp + * On exit, p->hipe.u.ncallee is set to the handler's PC and p->hipe.nsp * is set to its SP (low address of its stack frame). */ void hipe_find_handler(Process *p) @@ -254,7 +254,7 @@ void hipe_find_handler(Process *p) if ((exnra = sdesc_exnra(sdesc)) != 0 && (p->catches >= 0 || exnra == (unsigned long)&nbif_fail)) { - p->hipe.ncallee = (void(*)(void)) exnra; + p->hipe.u.ncallee = (void(*)(void)) exnra; p->hipe.nsp = nsp; p->hipe.narity = 0; /* update the gray/white boundary if we threw past it */ diff --git a/erts/emulator/hipe/hipe_x86_stack.c b/erts/emulator/hipe/hipe_x86_stack.c index 9ad3fa9d31..7f1c2f7d41 100644 --- a/erts/emulator/hipe/hipe_x86_stack.c +++ b/erts/emulator/hipe/hipe_x86_stack.c @@ -209,7 +209,7 @@ void (*hipe_handle_stack_trap(Process *p))(void) * The native stack MUST contain a stack frame as it appears on * entry to a function (return address, actuals, caller's frame). * p->hipe.narity MUST contain the arity (number of actuals). - * On exit, p->hipe.ncallee is set to the handler's PC and p->hipe.nsp + * On exit, p->hipe.u.ncallee is set to the handler's PC and p->hipe.nsp * is set to its SP (low address of its stack frame). */ void hipe_find_handler(Process *p) @@ -240,7 +240,7 @@ void hipe_find_handler(Process *p) if ((exnra = sdesc_exnra(sdesc)) != 0 && (p->catches >= 0 || exnra == (unsigned long)nbif_fail)) { - p->hipe.ncallee = (void(*)(void)) exnra; + p->hipe.u.ncallee = (void(*)(void)) exnra; p->hipe.nsp = nsp; p->hipe.narity = 0; /* update the gray/white boundary if we threw past it */ -- cgit v1.2.3 From c4cdb1847e09d1153a6044d7a6aac05db4fad515 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 25 Apr 2014 23:02:26 +0200 Subject: erts: Fix global tracing of beam function when called from hipe code Make hipe to beam calls use export entry. Makes it a lot easier to handle global tracing correctly (breakpoints in export entry). A beam function should now be traced correctly regardless how it is called. This will also fix a SEGV crash when a hipe stub is made pointing into a traced export entry and tracing is then stopped which clears the export entry causing the hipe stub to execute beam instruction NULL. This commit assumes that hipe code never calls local beam functions, which should be the case nowadays as we only hipe compile entire modules. --- erts/emulator/beam/beam_emu.c | 5 ++- erts/emulator/hipe/hipe_amd64.c | 59 ++++++++++++++++++----------------- erts/emulator/hipe/hipe_amd64_glue.S | 4 +-- erts/emulator/hipe/hipe_arm.c | 16 +++++----- erts/emulator/hipe/hipe_arm_glue.S | 6 ++-- erts/emulator/hipe/hipe_bif0.c | 15 +++------ erts/emulator/hipe/hipe_mkliterals.c | 1 + erts/emulator/hipe/hipe_mode_switch.c | 10 +++--- erts/emulator/hipe/hipe_mode_switch.h | 4 ++- erts/emulator/hipe/hipe_ppc.c | 34 ++++++++++---------- erts/emulator/hipe/hipe_ppc_glue.S | 6 ++-- erts/emulator/hipe/hipe_process.h | 2 ++ erts/emulator/hipe/hipe_sparc.c | 8 ++--- erts/emulator/hipe/hipe_sparc_glue.S | 8 ++--- erts/emulator/hipe/hipe_x86.c | 32 +++++++++---------- erts/emulator/hipe/hipe_x86_glue.S | 4 +-- 16 files changed, 106 insertions(+), 108 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index f6524f36d2..9b251a6ad1 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5034,7 +5034,10 @@ get_map_elements_fail: case HIPE_MODE_SWITCH_RES_RETURN: ASSERT(is_value(reg[0])); MoveReturn(reg[0], r(0)); - case HIPE_MODE_SWITCH_RES_CALL: + case HIPE_MODE_SWITCH_RES_CALL_EXPORTED: + c_p->i = c_p->hipe.u.callee_exp->addressv[erts_active_code_ix()]; + /*fall through*/ + case HIPE_MODE_SWITCH_RES_CALL_BEAM: SET_I(c_p->i); r(0) = reg[0]; Dispatch(); diff --git a/erts/emulator/hipe/hipe_amd64.c b/erts/emulator/hipe/hipe_amd64.c index b5dff06987..16c597e7b4 100644 --- a/erts/emulator/hipe/hipe_amd64.c +++ b/erts/emulator/hipe/hipe_amd64.c @@ -224,18 +224,19 @@ void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process * return alloc_code(nrbytes); } -/* called from hipe_bif0.c:hipe_bifs_make_native_stub_2() - and hipe_bif0.c:hipe_make_stub() */ -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) + +/* Make stub for native code calling exported beam function. +*/ +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) { /* * This creates a native code stub with the following contents: * - * movq $Address, P_BEAM_IP(%ebp) %% Actually two movl + * movq $Address, P_CALLEE_EXP(%ebp) %% Actually two movl * movb $Arity, P_ARITY(%ebp) * jmp callemu * - * The stub has variable size, depending on whether the P_BEAM_IP + * The stub has variable size, depending on whether the P_CALLEE_EXP * and P_ARITY offsets fit in 8-bit signed displacements or not. * The rel32 offset in the final jmp depends on its actual location, * which also depends on the size of the previous instructions. @@ -248,49 +249,49 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) codeSize = /* 23, 26, 29, or 32 bytes */ 23 + /* 23 when all offsets are 8-bit */ - (P_BEAM_IP >= 128 ? 3 : 0) + - ((P_BEAM_IP + 4) >= 128 ? 3 : 0) + + (P_CALLEE_EXP >= 128 ? 3 : 0) + + ((P_CALLEE_EXP + 4) >= 128 ? 3 : 0) + (P_ARITY >= 128 ? 3 : 0); codep = code = alloc_code(codeSize); - /* movl $beamAddress, P_BEAM_IP(%ebp); 3 or 6 bytes, plus 4 */ + /* movl $callee_exp, P_CALLEE_EXP(%ebp); 3 or 6 bytes, plus 4 */ codep[0] = 0xc7; -#if P_BEAM_IP >= 128 +#if P_CALLEE_EXP >= 128 codep[1] = 0x85; /* disp32[EBP] */ - codep[2] = P_BEAM_IP & 0xFF; - codep[3] = (P_BEAM_IP >> 8) & 0xFF; - codep[4] = (P_BEAM_IP >> 16) & 0xFF; - codep[5] = (P_BEAM_IP >> 24) & 0xFF; + codep[2] = P_CALLEE_EXP & 0xFF; + codep[3] = (P_CALLEE_EXP >> 8) & 0xFF; + codep[4] = (P_CALLEE_EXP >> 16) & 0xFF; + codep[5] = (P_CALLEE_EXP >> 24) & 0xFF; codep += 6; #else codep[1] = 0x45; /* disp8[EBP] */ - codep[2] = P_BEAM_IP; + codep[2] = P_CALLEE_EXP; codep += 3; #endif - codep[0] = ((unsigned long)beamAddress ) & 0xFF; - codep[1] = ((unsigned long)beamAddress >> 8) & 0xFF; - codep[2] = ((unsigned long)beamAddress >> 16) & 0xFF; - codep[3] = ((unsigned long)beamAddress >> 24) & 0xFF; + codep[0] = ((unsigned long)callee_exp ) & 0xFF; + codep[1] = ((unsigned long)callee_exp >> 8) & 0xFF; + codep[2] = ((unsigned long)callee_exp >> 16) & 0xFF; + codep[3] = ((unsigned long)callee_exp >> 24) & 0xFF; codep += 4; - /* movl (shl 32 $beamAddress), P_BEAM_IP+4(%ebp); 3 or 6 bytes, plus 4 */ + /* movl (shl 32 $callee_exp), P_CALLEE_EXP+4(%ebp); 3 or 6 bytes, plus 4 */ codep[0] = 0xc7; -#if P_BEAM_IP+4 >= 128 +#if P_CALLEE_EXP+4 >= 128 codep[1] = 0x85; /* disp32[EBP] */ - codep[2] = (P_BEAM_IP+4) & 0xFF; - codep[3] = ((P_BEAM_IP+4) >> 8) & 0xFF; - codep[4] = ((P_BEAM_IP+4) >> 16) & 0xFF; - codep[5] = ((P_BEAM_IP+4) >> 24) & 0xFF; + codep[2] = (P_CALLEE_EXP+4) & 0xFF; + codep[3] = ((P_CALLEE_EXP+4) >> 8) & 0xFF; + codep[4] = ((P_CALLEE_EXP+4) >> 16) & 0xFF; + codep[5] = ((P_CALLEE_EXP+4) >> 24) & 0xFF; codep += 6; #else codep[1] = 0x45; /* disp8[EBP] */ - codep[2] = (P_BEAM_IP+4); + codep[2] = (P_CALLEE_EXP+4); codep += 3; #endif - codep[0] = ((unsigned long)beamAddress >> 32) & 0xFF; - codep[1] = ((unsigned long)beamAddress >> 40) & 0xFF; - codep[2] = ((unsigned long)beamAddress >> 48) & 0xFF; - codep[3] = ((unsigned long)beamAddress >> 56) & 0xFF; + codep[0] = ((unsigned long)callee_exp >> 32) & 0xFF; + codep[1] = ((unsigned long)callee_exp >> 40) & 0xFF; + codep[2] = ((unsigned long)callee_exp >> 48) & 0xFF; + codep[3] = ((unsigned long)callee_exp >> 56) & 0xFF; codep += 4; /* movb $beamArity, P_ARITY(%ebp); 3 or 6 bytes */ diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S index 8816906870..bebe0a8fd1 100644 --- a/erts/emulator/hipe/hipe_amd64_glue.S +++ b/erts/emulator/hipe/hipe_amd64_glue.S @@ -109,7 +109,7 @@ ASYM(nbif_return): * stub (hipe_x86_loader.erl) which should look as follows: * * stub for f/N: - * movq $, P_BEAM_IP(P) + * movq $, P_CALLEE_EXP(P) * movb $, P_ARITY(P) * jmp nbif_callemu * @@ -119,7 +119,7 @@ ASYM(nbif_return): GLOBAL(ASYM(nbif_callemu)) ASYM(nbif_callemu): STORE_ARG_REGS - movl $HIPE_MODE_SWITCH_RES_CALL, %eax + movl $HIPE_MODE_SWITCH_RES_CALL_EXPORTED, %eax jmp .suspend_exit /* diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c index 3db3ffe9b1..165eb543c8 100644 --- a/erts/emulator/hipe/hipe_arm.c +++ b/erts/emulator/hipe/hipe_arm.c @@ -260,9 +260,9 @@ int hipe_patch_insn(void *address, Uint32 value, Eterm type) return 0; } -/* called from hipe_bif0.c:hipe_bifs_make_native_stub_2() - and hipe_bif0.c:hipe_make_stub() */ -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) +/* Make stub for native code calling exported beam function +*/ +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) { unsigned int *code; unsigned int *tramp_callemu; @@ -272,9 +272,9 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) * Native code calls BEAM via a stub looking as follows: * * mov r0, #beamArity - * ldr r8, [pc,#0] // beamAddress + * ldr r8, [pc,#0] // callee_exp * b nbif_callemu - * .long beamAddress + * .long callee_exp * * I'm using r0 and r8 since they aren't used for * parameter passing in native code. The branch to @@ -292,12 +292,12 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) /* mov r0, #beamArity */ code[0] = 0xE3A00000 | (beamArity & 0xFF); - /* ldr r8, [pc,#0] // beamAddress */ + /* ldr r8, [pc,#0] // callee_exp */ code[1] = 0xE59F8000; /* b nbif_callemu */ code[2] = 0xEA000000 | (callemu_offset & 0x00FFFFFF); - /* .long beamAddress */ - code[3] = (unsigned int)beamAddress; + /* .long callee_exp */ + code[3] = (unsigned int)callee_exp; hipe_flush_icache_range(code, 4*sizeof(int)); diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S index 2e2b8604a6..e58e112ca7 100644 --- a/erts/emulator/hipe/hipe_arm_glue.S +++ b/erts/emulator/hipe/hipe_arm_glue.S @@ -135,7 +135,7 @@ hipe_arm_throw_to_native: * which should look as follows: * * stub for f/N: - * + * * * b nbif_callemu * @@ -143,10 +143,10 @@ hipe_arm_throw_to_native: */ .global nbif_callemu nbif_callemu: - str r8, [P, #P_BEAM_IP] + str r8, [P, #P_CALLEE_EXP] str r0, [P, #P_ARITY] STORE_ARG_REGS - mov r0, #HIPE_MODE_SWITCH_RES_CALL + mov r0, #HIPE_MODE_SWITCH_RES_CALL_EXPORTED b .suspend_exit /* diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 2497d51df1..b686717814 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1490,18 +1490,13 @@ void hipe_mfa_save_orig_beam_op(Eterm mod, Eterm fun, unsigned int ari, Eterm *p static void *hipe_make_stub(Eterm m, Eterm f, unsigned int arity, int is_remote) { - void *BEAMAddress; + Export *export_entry; void *StubAddress; -#if 0 - if (is_not_atom(m) || is_not_atom(f) || arity > 255) - return NULL; -#endif - BEAMAddress = hipe_get_emu_address(m, f, arity, is_remote); - StubAddress = hipe_make_native_stub(BEAMAddress, arity); -#if 0 - hipe_mfa_set_na(m, f, arity, StubAddress); -#endif + ASSERT(is_remote); + + export_entry = erts_export_get_or_make_stub(m, f, arity); + StubAddress = hipe_make_native_stub(export_entry, arity); return StubAddress; } diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index 23e9214cec..ed355ce264 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -524,6 +524,7 @@ static const struct rts_param rts_params[] = { }, { 49, "P_MSG_FIRST", 1, offsetof(struct process, msg.first) }, { 50, "P_MSG_SAVE", 1, offsetof(struct process, msg.save) }, + { 51, "P_CALLEE_EXP", 1, offsetof(struct process, hipe.u.callee_exp) }, }; #define NR_PARAMS ARRAY_SIZE(rts_params) diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 7b9f2b5350..4dbba9da61 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -396,13 +396,13 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) if (is_recursive) hipe_push_beam_trap_frame(p, reg, p->arity); - result = HIPE_MODE_SWITCH_RES_CALL; + result = HIPE_MODE_SWITCH_RES_CALL_BEAM; break; } - case HIPE_MODE_SWITCH_RES_CALL: { + case HIPE_MODE_SWITCH_RES_CALL_EXPORTED: { /* Native code calls or tailcalls BEAM. * - * p->i is the callee's BEAM code + * p->hipe.u.callee_exp is the callee's export entry * p->arity is the callee's arity * p->def_arg_reg[] contains the register parameters * p->hipe.nsp[] contains the stacked parameters @@ -460,7 +460,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) p->i = closure->fe->address; /* Change result code to the faster plain CALL type. */ - result = HIPE_MODE_SWITCH_RES_CALL; + result = HIPE_MODE_SWITCH_RES_CALL_BEAM; } /* Append the closure as the last parameter. Don't increment arity. */ reg[arity] = p->hipe.u.closure; @@ -541,7 +541,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) } } HIPE_CHECK_PCB(p); - result = HIPE_MODE_SWITCH_RES_CALL; + result = HIPE_MODE_SWITCH_RES_CALL_BEAM; p->def_arg_reg[3] = result; return p; } diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h index 06721e3c04..6ec5da1ae9 100644 --- a/erts/emulator/hipe/hipe_mode_switch.h +++ b/erts/emulator/hipe/hipe_mode_switch.h @@ -31,7 +31,7 @@ /* result codes for beam_emu <- hipe_mode_switch() return */ #define HIPE_MODE_SWITCH_RES_RETURN 4 -#define HIPE_MODE_SWITCH_RES_CALL 5 +#define HIPE_MODE_SWITCH_RES_CALL_EXPORTED 5 #define HIPE_MODE_SWITCH_RES_THROW 6 /* additional result codes for hipe_mode_switch() <- native return */ @@ -45,6 +45,8 @@ #define HIPE_MODE_SWITCH_RES_APPLY 13 /* mode_switch <- native */ +#define HIPE_MODE_SWITCH_RES_CALL_BEAM 14 + #ifndef ASM #include "error.h" diff --git a/erts/emulator/hipe/hipe_ppc.c b/erts/emulator/hipe/hipe_ppc.c index 2d8fd61e1e..4dc26cdbc8 100644 --- a/erts/emulator/hipe/hipe_ppc.c +++ b/erts/emulator/hipe/hipe_ppc.c @@ -285,7 +285,7 @@ int hipe_patch_insn(void *address, Uint64 value, Eterm type) } } -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) { unsigned int *code; @@ -294,16 +294,16 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) code = alloc_stub(7); - /* addis r12,0,beamAddress@highest */ - code[0] = 0x3d800000 | (((unsigned long)beamAddress >> 48) & 0xffff); - /* ori r12,r12,beamAddress@higher */ - code[1] = 0x618c0000 | (((unsigned long)beamAddress >> 32) & 0xffff); + /* addis r12,0,callee_exp@highest */ + code[0] = 0x3d800000 | (((unsigned long)callee_exp >> 48) & 0xffff); + /* ori r12,r12,callee_exp@higher */ + code[1] = 0x618c0000 | (((unsigned long)callee_exp >> 32) & 0xffff); /* sldi r12,r12,32 (rldicr r12,r12,32,31) */ code[2] = 0x798c07c6; - /* oris r12,r12,beamAddress@h */ - code[3] = 0x658c0000 | (((unsigned long)beamAddress >> 16) & 0xffff); - /* ori r12,r12,beamAddress@l */ - code[4] = 0x618c0000 | ((unsigned long)beamAddress & 0xffff); + /* oris r12,r12,callee_exp@h */ + code[3] = 0x658c0000 | (((unsigned long)callee_exp >> 16) & 0xffff); + /* ori r12,r12,callee_exp@l */ + code[4] = 0x618c0000 | ((unsigned long)callee_exp & 0xffff); /* addi r0,0,beamArity */ code[5] = 0x38000000 | (beamArity & 0x7FFF); /* ba nbif_callemu */ @@ -355,18 +355,16 @@ int hipe_patch_insn(void *address, Uint32 value, Eterm type) return 0; } -/* called from hipe_bif0.c:hipe_bifs_make_native_stub_2() - and hipe_bif0.c:hipe_make_stub() */ -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) { unsigned int *code; /* * Native code calls BEAM via a stub looking as follows: * - * addi r12,0,beamAddress@l + * addi r12,0,callee_exp@l * addi r0,0,beamArity - * addis r12,r12,beamAddress@ha + * addis r12,r12,callee_exp@ha * ba nbif_callemu * * I'm using r0 and r12 since the standard SVR4 ABI allows @@ -384,12 +382,12 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) code = alloc_stub(4); - /* addi r12,0,beamAddress@l */ - code[0] = 0x39800000 | ((unsigned long)beamAddress & 0xFFFF); + /* addi r12,0,callee_exp@l */ + code[0] = 0x39800000 | ((unsigned long)callee_exp & 0xFFFF); /* addi r0,0,beamArity */ code[1] = 0x38000000 | (beamArity & 0x7FFF); - /* addis r12,r12,beamAddress@ha */ - code[2] = 0x3D8C0000 | at_ha((unsigned long)beamAddress); + /* addis r12,r12,callee_exp@ha */ + code[2] = 0x3D8C0000 | at_ha((unsigned long)callee_exp); /* ba nbif_callemu */ code[3] = 0x48000002 | (unsigned long)&nbif_callemu; diff --git a/erts/emulator/hipe/hipe_ppc_glue.S b/erts/emulator/hipe/hipe_ppc_glue.S index 6f0217c738..0c337a14df 100644 --- a/erts/emulator/hipe/hipe_ppc_glue.S +++ b/erts/emulator/hipe/hipe_ppc_glue.S @@ -296,7 +296,7 @@ CSYM(hipe_ppc_throw_to_native): * which should look as follows: * * stub for f/N: - * + * * * b nbif_callemu * @@ -312,10 +312,10 @@ CSYM(hipe_ppc_throw_to_native): */ GLOBAL(ASYM(nbif_callemu)) ASYM(nbif_callemu): - STORE r12, P_BEAM_IP(P) + STORE r12, P_CALLEE_EXP(P) STORE r0, P_ARITY(P) STORE_ARG_REGS - li r3, HIPE_MODE_SWITCH_RES_CALL + li r3, HIPE_MODE_SWITCH_RES_CALL_EXPORTED b .suspend_exit /* diff --git a/erts/emulator/hipe/hipe_process.h b/erts/emulator/hipe/hipe_process.h index cd88a37f80..86655ad42c 100644 --- a/erts/emulator/hipe/hipe_process.h +++ b/erts/emulator/hipe/hipe_process.h @@ -23,6 +23,7 @@ #define HIPE_PROCESS_H #include "erl_alloc.h" +#include "export.h" struct hipe_process_state { Eterm *nsp; /* Native stack pointer. */ @@ -31,6 +32,7 @@ struct hipe_process_state { union { void (*ncallee)(void); /* Native code callee (label) to invoke. */ Eterm closure; /* Used to pass a closure from native code. */ + Export* callee_exp; /* Used to pass export entry from native code */ }u; Eterm *nstgraylim; /* Gray/white stack boundary. */ Eterm *nstblacklim; /* Black/gray stack boundary. Must exist if diff --git a/erts/emulator/hipe/hipe_sparc.c b/erts/emulator/hipe/hipe_sparc.c index 49d4da7bab..2052aa8498 100644 --- a/erts/emulator/hipe/hipe_sparc.c +++ b/erts/emulator/hipe/hipe_sparc.c @@ -204,9 +204,7 @@ void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process * return alloc_code(nrbytes); } -/* called from hipe_bif0.c:hipe_bifs_make_native_stub_2() - and hipe_bif0.c:hipe_make_stub() */ -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) { unsigned int *code; unsigned int callEmuOffset; @@ -215,11 +213,11 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) code = alloc_code(5*sizeof(int)); /* sethi %hi(Address), %i4 */ - code[0] = 0x39000000 | (((unsigned int)beamAddress >> 10) & 0x3FFFFF); + code[0] = 0x39000000 | (((unsigned int)callee_exp >> 10) & 0x3FFFFF); /* or %g0, %o7, %i3 ! mov %o7, %i3 */ code[1] = 0xB610000F; /* or %i4, %lo(Address), %i4 */ - code[2] = 0xB8172000 | ((unsigned int)beamAddress & 0x3FF); + code[2] = 0xB8172000 | ((unsigned int)callee_exp & 0x3FF); /* call callemu */ callEmuOffset = (char*)nbif_callemu - (char*)&code[3]; code[3] = (1 << 30) | ((callEmuOffset >> 2) & 0x3FFFFFFF); diff --git a/erts/emulator/hipe/hipe_sparc_glue.S b/erts/emulator/hipe/hipe_sparc_glue.S index 44bdf1bc7e..ab40a48ee7 100644 --- a/erts/emulator/hipe/hipe_sparc_glue.S +++ b/erts/emulator/hipe/hipe_sparc_glue.S @@ -155,9 +155,9 @@ hipe_sparc_throw_to_native: * which should look as follows: * * stub for f/N: - * sethi %hi(f's BEAM code address), TEMP_ARG0 + * sethi %hi(f's export entry address), TEMP_ARG0 * mov RA, TEMP_RA ! because the call below clobbers RA (%o7) - * or TEMP_ARG0, %lo(f's BEAM code address), TEMP_ARG0 + * or TEMP_ARG0, %lo(f's export entry address), TEMP_ARG0 * call nbif_callemu ! clobbers RA! * mov N, TEMP_ARG1 ! delay slot: TEMP_ARG1 := ARITY * @@ -165,12 +165,12 @@ hipe_sparc_throw_to_native: */ .global nbif_callemu nbif_callemu: - st TEMP_ARG0, [P+P_BEAM_IP] + st TEMP_ARG0, [P+P_CALLEE_EXP] st TEMP_ARG1, [P+P_ARITY] st TEMP_RA, [P+P_NRA] STORE_ARG_REGS ba .flush_exit - mov HIPE_MODE_SWITCH_RES_CALL, %o0 + mov HIPE_MODE_SWITCH_RES_CALL_EXPORTED, %o0 /* * nbif_apply diff --git a/erts/emulator/hipe/hipe_x86.c b/erts/emulator/hipe/hipe_x86.c index 327c74e9aa..314f6b597c 100644 --- a/erts/emulator/hipe/hipe_x86.c +++ b/erts/emulator/hipe/hipe_x86.c @@ -182,18 +182,16 @@ void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process * return alloc_code(nrbytes); } -/* called from hipe_bif0.c:hipe_bifs_make_native_stub_2() - and hipe_bif0.c:hipe_make_stub() */ -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) { /* * This creates a native code stub with the following contents: * - * movl $Address, P_BEAM_IP(%ebp) + * movl $Address, P_CALLEE_EXP(%ebp) * movb $Arity, P_ARITY(%ebp) * jmp callemu * - * The stub has variable size, depending on whether the P_BEAM_IP + * The stub has variable size, depending on whether the P_CALLEE_EXP * and P_ARITY offsets fit in 8-bit signed displacements or not. * The rel32 offset in the final jmp depends on its actual location, * which also depends on the size of the previous instructions. @@ -206,28 +204,28 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) codeSize = /* 16, 19, or 22 bytes */ 16 + /* 16 when both offsets are 8-bit */ - (P_BEAM_IP >= 128 ? 3 : 0) + + (P_CALLEE_EXP >= 128 ? 3 : 0) + (P_ARITY >= 128 ? 3 : 0); codep = code = alloc_code(codeSize); - /* movl $beamAddress, P_BEAM_IP(%ebp); 3 or 6 bytes, plus 4 */ + /* movl $beamAddress, P_CALLEE_EXP(%ebp); 3 or 6 bytes, plus 4 */ codep[0] = 0xc7; -#if P_BEAM_IP >= 128 +#if P_CALLEE_EXP >= 128 codep[1] = 0x85; /* disp32[EBP] */ - codep[2] = P_BEAM_IP & 0xFF; - codep[3] = (P_BEAM_IP >> 8) & 0xFF; - codep[4] = (P_BEAM_IP >> 16) & 0xFF; - codep[5] = (P_BEAM_IP >> 24) & 0xFF; + codep[2] = P_CALLEE_EXP & 0xFF; + codep[3] = (P_CALLEE_EXP >> 8) & 0xFF; + codep[4] = (P_CALLEE_EXP >> 16) & 0xFF; + codep[5] = (P_CALLEE_EXP >> 24) & 0xFF; codep += 6; #else codep[1] = 0x45; /* disp8[EBP] */ - codep[2] = P_BEAM_IP; + codep[2] = P_CALLEE_EXP; codep += 3; #endif - codep[0] = ((unsigned int)beamAddress) & 0xFF; - codep[1] = ((unsigned int)beamAddress >> 8) & 0xFF; - codep[2] = ((unsigned int)beamAddress >> 16) & 0xFF; - codep[3] = ((unsigned int)beamAddress >> 24) & 0xFF; + codep[0] = ((unsigned int)callee_exp) & 0xFF; + codep[1] = ((unsigned int)callee_exp >> 8) & 0xFF; + codep[2] = ((unsigned int)callee_exp >> 16) & 0xFF; + codep[3] = ((unsigned int)callee_exp >> 24) & 0xFF; codep += 4; /* movb $beamArity, P_ARITY(%ebp); 3 or 6 bytes */ diff --git a/erts/emulator/hipe/hipe_x86_glue.S b/erts/emulator/hipe/hipe_x86_glue.S index 88b86f4de7..638780156a 100644 --- a/erts/emulator/hipe/hipe_x86_glue.S +++ b/erts/emulator/hipe/hipe_x86_glue.S @@ -104,7 +104,7 @@ ASYM(nbif_return): * stub (hipe_x86_loader.erl) which should look as follows: * * stub for f/N: - * movl $, P_BEAM_IP(P) + * movl $, P_CALLEE_EXP(P) * movb $, P_ARITY(P) * jmp nbif_callemu * @@ -114,7 +114,7 @@ ASYM(nbif_return): GLOBAL(ASYM(nbif_callemu)) ASYM(nbif_callemu): STORE_ARG_REGS - movl $HIPE_MODE_SWITCH_RES_CALL, %eax + movl $HIPE_MODE_SWITCH_RES_CALL_EXPORTED, %eax jmp .suspend_exit /* -- cgit v1.2.3 From 0799ae534f61aef1a47d751d48a09c8b49995e7a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 25 Apr 2014 23:09:44 +0200 Subject: erts: Remove some dead hipe specific code --- erts/emulator/hipe/hipe_bif0.c | 217 ----------------------------------------- 1 file changed, 217 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index b686717814..327546bfd0 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -89,25 +89,6 @@ static Eterm address_to_term(const void *address, Process *p) /* * BIFs for reading and writing memory. Used internally by HiPE. */ -#if 0 /* XXX: unused */ -BIF_RETTYPE hipe_bifs_read_u8_1(BIF_ALIST_1) -{ - unsigned char *address = term_to_address(BIF_ARG_1); - if (!address) - BIF_ERROR(BIF_P, BADARG); - BIF_RET(make_small(*address)); -} -#endif - -#if 0 /* XXX: unused */ -BIF_RETTYPE hipe_bifs_read_u32_1(BIF_ALIST_1) -{ - Uint32 *address = term_to_address(BIF_ARG_1); - if (!address || !hipe_word32_address_ok(address)) - BIF_ERROR(BIF_P, BADARG); - BIF_RET(Uint_to_term(*address, BIF_P)); -} -#endif BIF_RETTYPE hipe_bifs_write_u8_2(BIF_ALIST_2) { @@ -120,22 +101,6 @@ BIF_RETTYPE hipe_bifs_write_u8_2(BIF_ALIST_2) BIF_RET(NIL); } -#if 0 /* XXX: unused */ -BIF_RETTYPE hipe_bifs_write_s32_2(BIF_ALIST_2) -{ - Sint32 *address; - Sint value; - - address = term_to_address(BIF_ARG_1); - if (!address || !hipe_word32_address_ok(address)) - BIF_ERROR(BIF_P, BADARG); - if (!term_to_Sint32(BIF_ARG_2, &value)) - BIF_ERROR(BIF_P, BADARG); - *address = value; - BIF_RET(NIL); -} -#endif - BIF_RETTYPE hipe_bifs_write_u32_2(BIF_ALIST_2) { Uint32 *address; @@ -639,33 +604,6 @@ BIF_RETTYPE hipe_bifs_fun_to_address_1(BIF_ALIST_1) BIF_RET(address_to_term(pc, BIF_P)); } -static void *hipe_get_emu_address(Eterm m, Eterm f, unsigned int arity, int is_remote) -{ - void *address = NULL; - if (!is_remote) - address = hipe_find_emu_address(m, f, arity); - if (!address) { - /* 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->addressv[erts_active_code_ix()]; - } - return address; -} - -#if 0 /* XXX: unused */ -BIF_RETTYPE hipe_bifs_get_emu_address_1(BIF_ALIST_1) -{ - struct mfa mfa; - void *address; - - if (!term_to_mfa(BIF_ARG_1, &mfa)) - BIF_ERROR(BIF_P, BADARG); - address = hipe_get_emu_address(mfa.mod, mfa.fun, mfa.ari); - BIF_RET(address_to_term(address, BIF_P)); -} -#endif - BIF_RETTYPE hipe_bifs_set_native_address_3(BIF_ALIST_3) { Eterm *pc; @@ -713,33 +651,6 @@ BIF_RETTYPE hipe_bifs_set_native_address_3(BIF_ALIST_3) BIF_RET(am_false); } -#if 0 /* XXX: unused */ -/* - * hipe_bifs_address_to_fun(Address) - * - Address is the address of the start of a emu function's code - * - returns {Module, Function, Arity} - */ -BIF_RETTYPE hipe_bifs_address_to_fun_1(BIF_ALIST_1) -{ - Eterm *pc; - Eterm *funcinfo; - Eterm *hp; - - pc = term_to_address(BIF_ARG_1); - if (!pc) - BIF_ERROR(BIF_P, BADARG); - funcinfo = find_function_from_pc(pc); - if (!funcinfo) - BIF_RET(am_false); - hp = HAlloc(BIF_P, 4); - hp[0] = make_arityval(3); - hp[1] = funcinfo[0]; - hp[2] = funcinfo[1]; - hp[3] = make_small(funcinfo[2]); - BIF_RET(make_tuple(hp)); -} -#endif - BIF_RETTYPE hipe_bifs_enter_sdesc_1(BIF_ALIST_1) { struct sdesc *sdesc; @@ -948,37 +859,6 @@ BIF_RETTYPE hipe_bifs_primop_address_1(BIF_ALIST_1) BIF_RET(address_to_term(primop->address, BIF_P)); } -#if 0 /* XXX: unused */ -/* - * hipe_bifs_gbif_address(F,A) -> address or false - */ -#define GBIF_LIST(ATOM,ARY,CFUN) extern Eterm gbif_##CFUN(void); -#include "hipe_gbif_list.h" -#undef GBIF_LIST - -BIF_RETTYPE hipe_bifs_gbif_address_2(BIF_ALIST_2) -{ - Uint arity; - void *address; - - if (is_not_atom(BIF_ARG_1) || is_not_small(BIF_ARG_2)) - BIF_RET(am_false); /* error or false, does it matter? */ - arity = signed_val(BIF_ARG_2); - /* XXX: replace with a hash table later */ - do { /* trick to let us use 'break' instead of 'goto' */ -#define GBIF_LIST(ATOM,ARY,CFUN) if (BIF_ARG_1 == ATOM && arity == ARY) { address = CFUN; break; } -#include "hipe_gbif_list.h" -#undef GBIF_LIST - printf("\r\n%s: guard BIF ", __FUNCTION__); - fflush(stdout); - erts_printf("%T", BIF_ARG_1); - printf("/%lu isn't listed in hipe_gbif_list.h\r\n", arity); - BIF_RET(am_false); - } while (0); - BIF_RET(address_to_term(address, BIF_P)); -} -#endif - BIF_RETTYPE hipe_bifs_atom_to_word_1(BIF_ALIST_1) { if (is_not_atom(BIF_ARG_1)) @@ -1028,77 +908,6 @@ void hipe_emulate_fpe(Process* p) } #endif -#if 0 /* XXX: unused */ -/* - * At least parts of this should be inlined in native code. - * The rest could be made a primop used by both the emulator and - * native code... - */ -BIF_RETTYPE hipe_bifs_make_fun_3(BIF_ALIST_3) -{ - Eterm free_vars; - Eterm mod; - Eterm *tp; - Uint index; - Uint uniq; - Uint num_free; - Eterm tmp_var; - Uint *tmp_ptr; - unsigned needed; - ErlFunThing *funp; - Eterm *hp; - int i; - - if (is_not_list(BIF_ARG_1) && is_not_nil(BIF_ARG_1)) - BIF_ERROR(BIF_P, BADARG); - free_vars = BIF_ARG_1; - - if (is_not_atom(BIF_ARG_2)) - BIF_ERROR(BIF_P, BADARG); - mod = BIF_ARG_2; - - if (is_not_tuple(BIF_ARG_3) || - (arityval(*tuple_val(BIF_ARG_3)) != 3)) - BIF_ERROR(BIF_P, BADARG); - tp = tuple_val(BIF_ARG_3); - - if (term_to_Uint(tp[1], &index) == 0) - BIF_ERROR(BIF_P, BADARG); - if (term_to_Uint(tp[2], &uniq) == 0) - BIF_ERROR(BIF_P, BADARG); - if (term_to_Uint(tp[3], &num_free) == 0) - BIF_ERROR(BIF_P, BADARG); - - needed = ERL_FUN_SIZE + num_free; - funp = (ErlFunThing *) HAlloc(BIF_P, needed); - hp = funp->env; - - funp->thing_word = HEADER_FUN; - - /* Need a ErlFunEntry *fe - * fe->refc++; - * funp->fe = fe; - */ - - funp->num_free = num_free; - funp->creator = BIF_P->id; - for (i = 0; i < num_free; i++) { - if (is_nil(free_vars)) - BIF_ERROR(BIF_P, BADARG); - tmp_ptr = list_val(free_vars); - tmp_var = CAR(tmp_ptr); - free_vars = CDR(tmp_ptr); - *hp++ = tmp_var; - } - if (is_not_nil(free_vars)) - BIF_ERROR(BIF_P, BADARG); - - funp->next = MSO(BIF_P).funs; - MSO(BIF_P).funs = funp; - - BIF_RET(make_fun(funp)); -} -#endif /* * args: Module, {Uniq, Index, BeamAddress} @@ -1163,22 +972,6 @@ BIF_RETTYPE hipe_bifs_set_native_address_in_fe_2(BIF_ALIST_2) BIF_RET(am_true); } -#if 0 /* XXX: unused */ -BIF_RETTYPE hipe_bifs_make_native_stub_2(BIF_ALIST_2) -{ - void *beamAddress; - Uint beamArity; - void *stubAddress; - - if ((beamAddress = term_to_address(BIF_ARG_1)) == 0 || - is_not_small(BIF_ARG_2) || - (beamArity = unsigned_val(BIF_ARG_2)) >= 256) - BIF_ERROR(BIF_P, BADARG); - stubAddress = hipe_make_native_stub(beamAddress, beamArity); - BIF_RET(address_to_term(stubAddress, BIF_P)); -} -#endif - /* * MFA info hash table: * - maps MFA to native code entry point @@ -1323,16 +1116,6 @@ static inline struct hipe_mfa_info *hipe_mfa_info_table_get_locked(Eterm m, Eter return NULL; } -#if 0 /* XXX: unused */ -void *hipe_mfa_find_na(Eterm m, Eterm f, unsigned int arity) -{ - const struct hipe_mfa_info *p; - - p = hipe_mfa_info_table_get(m, f, arity); - return p ? p->address : NULL; -} -#endif - static struct hipe_mfa_info *hipe_mfa_info_table_put_locked(Eterm m, Eterm f, unsigned int arity) { unsigned long h; -- cgit v1.2.3 From 88fb069f83c39a47f65809ee5d6e7cadc170e56b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 30 Apr 2014 14:47:24 +0200 Subject: erts: Add erts_internal:map_to_tuple_keys/1 * Used for introspection. * Will return the internal key tuple if applicable * Not documented - not for public use --- erts/emulator/beam/bif.tab | 1 + erts/emulator/beam/erl_map.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 2d888862bf..fbdddf09db 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -157,6 +157,7 @@ bif erts_internal:binary_to_term/2 bif erts_internal:request_system_task/3 bif erts_internal:check_process_code/2 +bif erts_internal:map_to_tuple_keys/1 # inet_db support bif erlang:port_set_data/2 diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index fdd2d0c0f6..5e740aacdd 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -58,6 +58,8 @@ * - maps:size/1 * - maps:without/2 * + * DEBUG: for sharing calculation + * - erts_internal:map_to_tuple_keys/1 */ /* erlang:map_size/1 @@ -819,3 +821,17 @@ int erts_validate_and_sort_map(map_t* mp) } return 1; } + +/* + * erts_internal:map_to_tuple_keys/1 + * + * Used in erts_debug:size/1 + */ + +BIF_RETTYPE erts_internal_map_to_tuple_keys_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + map_t *mp = (map_t*)map_val(BIF_ARG_1); + BIF_RET(mp->keys); + } + BIF_ERROR(BIF_P, BADARG); +} -- cgit v1.2.3 From 7059845da597df041dc74f5d2f5efdc534a4af5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 30 Apr 2014 16:02:02 +0200 Subject: erts: Add testcase for erts_debug:size/1 Map terms --- erts/emulator/test/erts_debug_SUITE.erl | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl index 87778dd0c2..e5c904cfb9 100644 --- a/erts/emulator/test/erts_debug_SUITE.erl +++ b/erts/emulator/test/erts_debug_SUITE.erl @@ -67,6 +67,9 @@ test_size(Config) when is_list(Config) -> 2 = do_test_size({[]}), 3 = do_test_size({a,b}), 7 = do_test_size({a,[b,c]}), + 8 = do_test_size(#{b => 2,c => 3}), + 4 = do_test_size(#{}), + 32 = do_test_size(#{b => 2,c => 3,txt => "hello world"}), %% Test internal consistency of sizes, but without testing %% exact sizes. @@ -97,6 +100,9 @@ test_size(Config) when is_list(Config) -> do_test_size({SimplestFun,SimplestFun}, 2*FunSz0+do_test_size({a,b}), FunSz0+do_test_size({a,b})), + + M = id(#{ "atom" => first, i => 0}), + do_test_size([M,M#{ "atom" := other },M#{i := 42}],54,32), ok. do_test_size(Term) -> -- cgit v1.2.3 From 8f673dbd8d4cb5c832b6e79bd2304949f4123981 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 28 Apr 2014 15:52:58 +0200 Subject: erts: Fix faulty process suspend assert --- erts/emulator/beam/erl_process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b4b97d7df1..55036a569e 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -6144,7 +6144,7 @@ suspend_process(Process *c_p, Process *p) if (c_p == p) { state = erts_smp_atomic32_read_bor_relb(&p->state, ERTS_PSFLG_SUSPENDED); - ASSERT(state & ERTS_PSFLG_RUNNING); + ASSERT(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)); suspended = (state & ERTS_PSFLG_SUSPENDED) ? -1: 1; } else { -- cgit v1.2.3 From 64cb86b407cf7d0c0f7ac834dd93be6708ed89b1 Mon Sep 17 00:00:00 2001 From: Piotr Nosek Date: Thu, 8 May 2014 12:04:14 +0200 Subject: Add Visual Studio macros to erl_driver.h and ei.h --- erts/emulator/beam/erl_driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 3ecb379326..5ced8c5ca0 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -198,7 +198,7 @@ typedef long long ErlDrvSInt64; #error No 64-bit integer type #endif -#if defined(__WIN32__) +#if defined(__WIN32__) || defined(_WIN32) typedef ErlDrvUInt ErlDrvSizeT; typedef ErlDrvSInt ErlDrvSSizeT; #else -- cgit v1.2.3 From 12cd5e5b394623fab9907622ad99163c5b9350e1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 24 Apr 2014 15:20:08 +0200 Subject: erts: Fix various autoconf issues * Check of atomics on bsd * Add --enable-systemd for epmd * Remove unused --enable-tsp option --- erts/aclocal.m4 | 4 ++-- erts/configure.in | 52 +++++++++++++++++++++++++++++------------------ erts/epmd/src/Makefile.in | 2 +- erts/epmd/src/epmd_srv.c | 4 ++-- 4 files changed, 37 insertions(+), 25 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 2b47f7c4bc..ed492d55ff 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1118,7 +1118,7 @@ case "$THR_LIB_NAME" in [Define if you have the "ose_spi/ose_spi.h" header file.])) ;; esac - if test "x$THR_LIB_NAME" == "xpthread"; then + if test "x$THR_LIB_NAME" = "xpthread"; then case $host_os in openbsd*) # The default stack size is insufficient for our needs @@ -1222,7 +1222,7 @@ case "$THR_LIB_NAME" in dnl dnl Check for functions dnl - if test "x$THR_LIB_NAME" == "xpthread"; then + if test "x$THR_LIB_NAME" = "xpthread"; then AC_CHECK_FUNC(pthread_spin_lock, \ [ethr_have_native_spinlock=yes \ AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \ diff --git a/erts/configure.in b/erts/configure.in index 9ebb56e3bc..04303da4f8 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -214,9 +214,6 @@ AC_ARG_ENABLE(native-libs, AS_HELP_STRING([--enable-native-libs], [compile Erlang libraries to native code])) -AC_ARG_ENABLE(tsp, -AS_HELP_STRING([--enable-tsp], [compile tsp app])) - AC_ARG_ENABLE(fp-exceptions, AS_HELP_STRING([--enable-fp-exceptions], [use hardware floating point exceptions (default if hipe enabled)]), @@ -366,6 +363,11 @@ AC_DEFINE_UNQUOTED(ASSUMED_CACHE_LINE_SIZE, $with_assumed_cache_line_size, [Assumed cache-line size (in bytes)]) +AC_ARG_ENABLE(systemd, +AS_HELP_STRING([--enable-systemd], [enable systemd support in epmd]), +[], +[enable_systemd=no]) + dnl Magic test for clearcase. OTP_RELEASE= if test "${ERLANG_COMMERCIAL_BUILD}" != ""; then @@ -456,10 +458,10 @@ AS_HELP_STRING([--enable-static-drivers], [comma seperated list of linked-in dri AC_SUBST(STATIC_DRIVERS) AC_ARG_WITH(ets-write-concurrency-locks, - AS_HELP_STRING([--with-ets-write-concurrency-locks={8,16,32,64,128,256}], - [specify how many locks the write_concurrency option for ets should use.]) - AS_HELP_STRING([--without-ets-write-concurrency-locks], - [use the default number of write_concurrency locks (default)])) +AS_HELP_STRING([--with-ets-write-concurrency-locks={8|16|32|64|128|256}], + [specify how many locks the write_concurrency option for ets should use.]) +AS_HELP_STRING([--without-ets-write-concurrency-locks], + [use the default number of write_concurrency locks (default)])) if test X"$with_ets_write_concurrency_locks" != X""; then AC_DEFINE_UNQUOTED(ERTS_DB_HASH_LOCK_CNT,$with_ets_write_concurrency_locks, @@ -1042,8 +1044,6 @@ AC_CHECK_LIB(dl, dlopen) AC_CHECK_LIB(inet, main) AC_CHECK_LIB(util, openpty) -AC_CHECK_LIB(systemd-daemon, sd_listen_fds) - dnl Try to find a thread library. dnl dnl ETHR_LIB_NAME, ETHR_LIBS, ETHR_X_LIBS, ETHR_THR_LIB_BASE and ETHR_DEFS @@ -1679,7 +1679,29 @@ AC_CHECK_MEMBERS([struct ifreq.ifr_enaddr], [], [], #endif ]) -AC_CHECK_HEADERS(systemd/sd-daemon.h) +dnl ---------------------------------------------------------------------- +dnl Check the availability of systemd +dnl ---------------------------------------------------------------------- +if test x"$enable_systemd" != x"no"; then + +systemd_daemon_save_LIBS=$LIBS +LIBS= +AC_SEARCH_LIBS(sd_listen_fds,[systemd systemd-daemon], + [have_sd_listen_fds=yes],[have_sd_listen_fds=no],$systemd_daemon_save_LIBS) +AC_CHECK_HEADERS(systemd/sd-daemon.h, + [have_systemd_sd_daemon_h=yes],[have_systemd_sd_daemon_h=no]) + +if test x"$have_sd_listen_fds" = x"yes" && \ + test x"$have_systemd_sd_daemon_h" = x"yes"; then + AC_DEFINE([HAVE_SYSTEMD_DAEMON],[1],[Define if you have systemd daemon]) + SYSTEMD_DAEMON_LIBS=$LIBS +elif test x"$enable_systemd" = x"yes"; then + AC_MSG_FAILURE([--enable-systemd was given, but test for systemd failed]) +fi +LIBS=$systemd_daemon_save_LIBS +fi +AC_SUBST(SYSTEMD_DAEMON_LIBS) + dnl ---------------------------------------------------------------------- dnl Check the availability for libdlpi @@ -3431,16 +3453,6 @@ dnl Tests related to configurable options given on command line dnl (using the --disable, --enable and --with switches). dnl ---------------------------------------------------------------------- -# -# Check if we should compile TSP app -# - -TSP_APP= -if test X${enable_tsp} = Xyes; then - TSP_APP=tsp -fi -AC_SUBST(TSP_APP) - # # Check if we should enable HiPE. # diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in index 8dc8dae5f6..0c7787a3b1 100644 --- a/erts/epmd/src/Makefile.in +++ b/erts/epmd/src/Makefile.in @@ -84,7 +84,7 @@ LD = @LD@ ifeq ($(findstring ose,$(TARGET)),ose) LIBS = $(ERTS_INTERNAL_LIBS) @LIBS@ else -LIBS = @LIBS@ $(ERTS_INTERNAL_LIBS) +LIBS = @LIBS@ @SYSTEMD_DAEMON_LIBS@ $(ERTS_INTERNAL_LIBS) endif LDFLAGS = @LDFLAGS@ diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 93982c2f60..48fd7a5f9c 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -213,7 +213,7 @@ void run(EpmdVars *g) node_init(g); g->conn = conn_init(g); -#ifdef HAVE_SYSTEMD_SD_DAEMON_H +#ifdef HAVE_SYSTEMD_DAEMON if (g->is_systemd) { int n; @@ -310,7 +310,7 @@ void run(EpmdVars *g) SET_ADDR(iserv_addr[0],EPMD_ADDR_ANY,sport); num_sockets = 1; } -#ifdef HAVE_SYSTEMD_SD_DAEMON_H +#ifdef HAVE_SYSTEMD_DAEMON } #endif -- cgit v1.2.3 From 5b62ab3db6292a731fccfc31eafec7e9fb0906df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 9 May 2014 18:32:05 +0200 Subject: erts: Add spec for erts_internal:map_to_tuple_keys/1 --- erts/preloaded/src/erts_internal.erl | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'erts') diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index edcd50c77e..764d7730aa 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -31,6 +31,7 @@ -export([await_port_send_result/3]). -export([binary_to_term/1, binary_to_term/2]). -export([cmp_term/2]). +-export([map_to_tuple_keys/1]). -export([port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). @@ -181,3 +182,11 @@ binary_to_term(_Binary, _Opts) -> cmp_term(_A,_B) -> erlang:nif_error(undefined). + +%% return the internal key tuple for map keys +-spec map_to_tuple_keys(M) -> Keys when + M :: map(), + Keys :: tuple(). + +map_to_tuple_keys(_M) -> + erlang:nif_error(undefined). -- cgit v1.2.3 From e1d5e4841a657ad26254597dc8f2bb09a38c6646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 9 May 2014 18:33:44 +0200 Subject: erts: Update preloaded erts_internal.beam --- erts/preloaded/ebin/erts_internal.beam | Bin 4276 -> 4456 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index d41c833e05..3d650aff74 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ -- cgit v1.2.3 From 7ff17ddfd337d1c49c1174d8d24b2ce0671b2c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 12 May 2014 14:26:30 +0200 Subject: BIFs should be considered exported All BIFs now have stub functions and are exported. For example in the erlang module: -export([..., is_list/1, ...]). . . . is_list(_Term) -> erlang:nif_error(undefined). But erlang:function_exported(erlang, is_list, 1) returns false, which is weird. Change erlang:function_exported/3 to return true for BIFs. --- erts/doc/src/erlang.xml | 9 +++++---- erts/emulator/beam/bif.c | 11 +++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 0f4dfc0f98..03d184f4d2 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1252,10 +1252,11 @@ true Check if a function is exported and loaded

Returns true if the module Module is loaded - and contains an exported function Function/Arity; - otherwise false.

-

Returns false for any BIF (functions implemented in C - rather than in Erlang).

+ and contains an exported function Function/Arity, + or if there is a BIF (a built-in function implemented in C) + with the given name; otherwise returns false.

+

This function used to return false for built-in + functions before the 18.0 release.

diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 06a1230ca0..787a3da1f4 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3984,16 +3984,19 @@ BIF_RETTYPE halt_2(BIF_ALIST_2) BIF_RETTYPE function_exported_3(BIF_ALIST_3) { + int arity; if (is_not_atom(BIF_ARG_1) || is_not_atom(BIF_ARG_2) || 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), - erts_active_code_ix()) == NULL) { - BIF_RET(am_false); + arity = signed_val(BIF_ARG_3); + if (erts_find_function(BIF_ARG_1, BIF_ARG_2, arity, + erts_active_code_ix()) != NULL || + erts_is_builtin(BIF_ARG_1, BIF_ARG_2, arity)) { + BIF_RET(am_true); } - BIF_RET(am_true); + BIF_RET(am_false); } /**********************************************************************/ -- cgit v1.2.3 From 8eccb186d9f19d1a14f44102acbd7a3a7bf3a029 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 12 May 2014 15:13:03 +0200 Subject: erts: Fix bug in etp-block commands --- erts/etc/unix/etp-commands.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index ed90e26024..ae1b1734af 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -3277,7 +3277,7 @@ define etp-block-size-1 else set $etp_MBC_ABLK_OFFSET_SHIFT = (32 - 9) end - set $etp_MBC_ABLK_SZ_MASK = ~(~0 << $etp_MBC_ABLK_OFFSET_SHIFT) & ~7 + set $etp_MBC_ABLK_SZ_MASK = ((UWord)1 << $etp_MBC_ABLK_OFFSET_SHIFT) - 1 - 7 end set $etp_blk_sz = ($arg0)->bhdr & $etp_MBC_ABLK_SZ_MASK end @@ -3300,7 +3300,7 @@ define etp-block2mbc-1 set $etp_MBC_ABLK_OFFSET_SHIFT = (32 - 9) end end - set $etp_mbc = (Carrier_t*) ((((UWord)($arg0)) & (~0 << 18)) - ((($arg0)->bhdr >> $etp_MBC_ABLK_OFFSET_SHIFT) << 18)) + set $etp_mbc = (Carrier_t*) ((((UWord)($arg0) >> 18) - (($arg0)->bhdr >> $etp_MBC_ABLK_OFFSET_SHIFT)) << 18) end end -- cgit v1.2.3 From 81e77a082ba1de759db17e9cbdb4b8cbe79c0d1b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 12 May 2014 19:03:12 +0200 Subject: erts: Make erlang:display show content of binaries --- erts/emulator/beam/erl_printf_term.c | 86 +++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index d18760dc43..1a0c7a9fc9 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2013. All Rights Reserved. + * Copyright Ericsson AB 2005-2014. All Rights Reserved. * * The 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,7 @@ #include "sys.h" #include "big.h" #include "erl_map.h" +#include "erl_binary.h" #define PRINT_CHAR(CNT, FN, ARG, C) \ do { \ @@ -138,6 +139,25 @@ is_printable_string(Eterm list, Eterm* base) return 0; } +static int is_printable_ascii(byte* bytep, Uint bytesize, Uint bitoffs) +{ + if (!bitoffs) { + while (bytesize--) { + if (*bytep < ' ' || *bytep >= 127) + return 0; + bytep++; + } + } else { + while (bytesize--) { + byte octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs)); + if (octet < ' ' || octet >= 127) + return 0; + bytep++; + } + } + return 1; +} + /* print a atom doing what quoting is necessary */ static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount) { @@ -446,13 +466,65 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, PRINT_STRING(res, fn, arg, "#MatchState"); } else { - ProcBin* pb = (ProcBin *) binary_val(wobj); - if (pb->size == 1) - PRINT_STRING(res, fn, arg, "<<1 byte>>"); - else { + byte* bytep; + Uint bytesize = binary_size_rel(obj,obj_base); + Uint bitoffs; + Uint bitsize; + byte octet; + ERTS_GET_BINARY_BYTES_REL(obj, bytep, bitoffs, bitsize, obj_base); + + if (bitsize || !bytesize + || !is_printable_ascii(bytep, bytesize, bitoffs)) { + int is_first = 1; PRINT_STRING(res, fn, arg, "<<"); - PRINT_UWORD(res, fn, arg, 'u', 0, 1, (ErlPfUWord) pb->size); - PRINT_STRING(res, fn, arg, " bytes>>"); + while (bytesize) { + if (is_first) + is_first = 0; + else + PRINT_CHAR(res, fn, arg, ','); + if (bitoffs) + octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs)); + else + octet = bytep[0]; + PRINT_UWORD(res, fn, arg, 'u', 0, 1, octet); + ++bytep; + --bytesize; + } + if (bitsize) { + Uint bits = bitoffs + bitsize; + octet = bytep[0]; + if (bits < 8) + octet >>= 8 - bits; + else if (bits > 8) { + bits -= 8; /* bits in last byte */ + octet <<= bits; + octet |= bytep[1] >> (8 - bits); + } + octet &= (1 << bitsize) - 1; + if (is_first) + is_first = 0; + else + PRINT_CHAR(res, fn, arg, ','); + PRINT_UWORD(res, fn, arg, 'u', 0, 1, octet); + PRINT_CHAR(res, fn, arg, ':'); + PRINT_UWORD(res, fn, arg, 'u', 0, 1, bitsize); + } + PRINT_STRING(res, fn, arg, ">>"); + } + else { + PRINT_STRING(res, fn, arg, "<<\""); + while (bytesize) { + if (bitoffs) + octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs)); + else + octet = bytep[0]; + if (octet == '"') + PRINT_CHAR(res, fn, arg, '\\'); + PRINT_CHAR(res, fn, arg, octet); + ++bytep; + --bytesize; + } + PRINT_STRING(res, fn, arg, "\">>"); } } break; -- cgit v1.2.3 From 94d877a95de7f22fc66f0737da54cc6500a7231c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 2 May 2014 14:57:35 +0200 Subject: HiPE wrappers for BIFs disabling GC --- erts/emulator/beam/bif.h | 45 ++++++++++++++++++++++++++ erts/emulator/beam/external.c | 74 +++++-------------------------------------- 2 files changed, 53 insertions(+), 66 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index 51b77a95ed..669cbfd102 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -392,6 +392,51 @@ erts_bif_prep_await_proc_exit_apply_trap(Process *c_p, Eterm args[], int nargs); +#ifndef HIPE + +#define HIPE_WRAPPER_BIF_DISABLE_GC(BIF_NAME, ARITY) + +#else + +#include "erl_fun.h" +#include "hipe_mode_switch.h" + +/* + * Hipe wrappers used by native code for BIFs that disable GC while trapping. + * Also add usage of the wrapper in ../hipe/hipe_bif_list.m4 + * + * Problem: + * When native code calls a BIF that traps, hipe_mode_switch will push a + * "trap frame" on the Erlang stack in order to find its way back from beam_emu + * back to native caller when finally done. If GC is disabled and stack/heap + * is full there is no place to push the "trap frame". + * + * Solution: + * We reserve space on stack for the "trap frame" here before the BIF is called. + * If the BIF does not trap, the space is reclaimed here before returning. + * If the BIF traps, hipe_push_beam_trap_frame() will detect that a "trap frame" + * already is reserved and use it. + */ + + +#define HIPE_WRAPPER_BIF_DISABLE_GC(BIF_NAME, ARITY) \ +BIF_RETTYPE hipe_wrapper_ ## BIF_NAME ## _ ## ARITY (Process* c_p, \ + Eterm* args); \ +BIF_RETTYPE hipe_wrapper_ ## BIF_NAME ## _ ## ARITY (Process* c_p, \ + Eterm* args) \ +{ \ + BIF_RETTYPE res; \ + hipe_reserve_beam_trap_frame(c_p, args, ARITY); \ + res = BIF_NAME ## _ ## ARITY (c_p, args); \ + if (is_value(res) || c_p->freason != TRAP) { \ + hipe_unreserve_beam_trap_frame(c_p); \ + } \ + return res; \ +} + +#endif + + #include "erl_bif_table.h" #endif diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 656de7c49a..e376253400 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -44,9 +44,6 @@ #include "erl_zlib.h" #include "erl_map.h" -#ifdef HIPE -#include "hipe_mode_switch.h" -#endif #define in_area(ptr,start,nbytes) ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes)) #define MAX_STRING_LEN 0xffff @@ -1069,6 +1066,8 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1) } } +HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 1) + BIF_RETTYPE term_to_binary_1(BIF_ALIST_1) { Eterm res = erts_term_to_binary_int(BIF_P, BIF_ARG_1, 0, TERM_TO_BINARY_DFLAGS, NULL); @@ -1081,6 +1080,8 @@ BIF_RETTYPE term_to_binary_1(BIF_ALIST_1) } } +HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 2) + BIF_RETTYPE term_to_binary_2(BIF_ALIST_2) { Process* p = BIF_P; @@ -1544,11 +1545,15 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con BIF_TRAP1(&binary_to_term_trap_export, p, ctx->trap_bin); } +HIPE_WRAPPER_BIF_DISABLE_GC(erts_internal_binary_to_term, 1) + BIF_RETTYPE erts_internal_binary_to_term_1(BIF_ALIST_1) { return binary_to_term_int(BIF_P, 0, BIF_ARG_1, NULL); } +HIPE_WRAPPER_BIF_DISABLE_GC(erts_internal_binary_to_term, 2) + BIF_RETTYPE erts_internal_binary_to_term_2(BIF_ALIST_2) { Eterm opts; @@ -4440,66 +4445,3 @@ error: #undef SKIP2 #undef CHKSIZE } - - -#ifdef HIPE -BIF_RETTYPE hipe_wrapper_term_to_binary_1(BIF_ALIST_1); -BIF_RETTYPE hipe_wrapper_term_to_binary_2(BIF_ALIST_2); -BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_1(BIF_ALIST_1); -BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_2(BIF_ALIST_2); - -/* Hipe wrappers used by native code for BIFs that disable GC while trapping. - * - * Problem: - * When native code calls a BIF that traps, hipe_mode_switch will push a - * "trap frame" on the Erlang stack in order to find its way back from beam_emu - * back to native caller when finally done. If GC is disabled and stack/heap - * is full there is no place to push the "trap frame". - * - * Solution: - * We reserve space on stack for the "trap frame" here before the BIF is called. - * If the BIF does not trap, the space is reclaimed here before returning. - * If the BIF traps, hipe_push_beam_trap_frame() will detect that a "trap frame" - * already is reserved and use it. - */ -BIF_RETTYPE hipe_wrapper_term_to_binary_1(BIF_ALIST_1) -{ - Eterm res; - hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 1); - res = term_to_binary_1(BIF_P, BIF__ARGS); - if (is_value(res) || BIF_P->freason != TRAP) { - hipe_unreserve_beam_trap_frame(BIF_P); - } - return res; -} -BIF_RETTYPE hipe_wrapper_term_to_binary_2(BIF_ALIST_2) -{ - Eterm res; - hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 2); - res = term_to_binary_2(BIF_P, BIF__ARGS); - if (is_value(res) || BIF_P->freason != TRAP) { - hipe_unreserve_beam_trap_frame(BIF_P); - } - return res; -} -BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_1(BIF_ALIST_1) -{ - Eterm res; - hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 1); - res = erts_internal_binary_to_term_1(BIF_P, BIF__ARGS); - if (is_value(res) || BIF_P->freason != TRAP) { - hipe_unreserve_beam_trap_frame(BIF_P); - } - return res; -} -BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_2(BIF_ALIST_2) -{ - Eterm res; - hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 2); - res = erts_internal_binary_to_term_2(BIF_P, BIF__ARGS); - if (is_value(res) || BIF_P->freason != TRAP) { - hipe_unreserve_beam_trap_frame(BIF_P); - } - return res; -} -#endif /*HIPE*/ -- cgit v1.2.3 From a6c681cf14810a007a2bd4ad0066b4403460bffc Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 7 May 2014 21:27:54 +0200 Subject: Add support for failing in BIF that has trapped --- erts/emulator/beam/bif.h | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index 669cbfd102..72c55ccb55 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -124,12 +124,85 @@ do { \ return THE_NON_VALUE; \ } while(0) +#define ERTS_BIF_ERROR_TRAPPED0(Proc, Reason, Bif) \ +do { \ + (Proc)->freason = (Reason); \ + (Proc)->current = (Bif)->code; \ + return THE_NON_VALUE; \ +} while (0) + +#define ERTS_BIF_ERROR_TRAPPED1(Proc, Reason, Bif, A0) \ +do { \ + Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + (Proc)->freason = (Reason); \ + (Proc)->current = (Bif)->code; \ + reg[0] = (Eterm) (A0); \ + return THE_NON_VALUE; \ +} while (0) + +#define ERTS_BIF_ERROR_TRAPPED2(Proc, Reason, Bif, A0, A1) \ +do { \ + Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + (Proc)->freason = (Reason); \ + (Proc)->current = (Bif)->code; \ + reg[0] = (Eterm) (A0); \ + reg[1] = (Eterm) (A1); \ + return THE_NON_VALUE; \ +} while (0) + +#define ERTS_BIF_ERROR_TRAPPED3(Proc, Reason, Bif, A0, A1, A2) \ +do { \ + Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + (Proc)->freason = (Reason); \ + (Proc)->current = (Bif)->code; \ + reg[0] = (Eterm) (A0); \ + reg[1] = (Eterm) (A1); \ + reg[2] = (Eterm) (A2); \ + return THE_NON_VALUE; \ +} while (0) + #define ERTS_BIF_PREP_ERROR(Ret, Proc, Reason) \ do { \ (Proc)->freason = (Reason); \ (Ret) = THE_NON_VALUE; \ } while (0) +#define ERTS_BIF_PREP_ERROR_TRAPPED0(Ret, Proc, Reason, Bif) \ +do { \ + (Proc)->freason = (Reason); \ + (Proc)->current = (Bif)->code; \ + (Ret) = THE_NON_VALUE; \ +} while (0) + +#define ERTS_BIF_PREP_ERROR_TRAPPED1(Ret, Proc, Reason, Bif, A0) \ +do { \ + Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + (Proc)->freason = (Reason); \ + (Proc)->current = (Bif)->code; \ + reg[0] = (Eterm) (A0); \ + (Ret) = THE_NON_VALUE; \ +} while (0) + +#define ERTS_BIF_PREP_ERROR_TRAPPED2(Ret, Proc, Reason, Bif, A0, A1) \ +do { \ + Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + (Proc)->freason = (Reason); \ + (Proc)->current = (Bif)->code; \ + reg[0] = (Eterm) (A0); \ + reg[1] = (Eterm) (A1); \ + (Ret) = THE_NON_VALUE; \ +} while (0) + +#define ERTS_BIF_PREP_ERROR_TRAPPED3(Ret, Proc, Reason, Bif, A0, A1, A2) \ +do { \ + Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + (Proc)->freason = (Reason); \ + (Proc)->current = (Bif)->code; \ + reg[0] = (Eterm) (A0); \ + reg[1] = (Eterm) (A1); \ + reg[2] = (Eterm) (A2); \ + (Ret) = THE_NON_VALUE; \ +} while (0) #define ERTS_BIF_PREP_TRAP0(Ret, Trap, Proc) \ do { \ -- cgit v1.2.3 From fdb350a4c98f070c264b8bd4ea9eca2513352f21 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Wed, 14 May 2014 14:46:46 +0200 Subject: Add 'md5' entry for module_info/0/1 functions. --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/beam_load.c | 56 ++++++++++++++++++++++++++++---- erts/emulator/beam/beam_load.h | 8 +++-- erts/emulator/test/module_info_SUITE.erl | 21 ++++++++++-- erts/preloaded/src/erlang.erl | 2 +- 5 files changed, 75 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index d28e519ae1..7d11f7f956 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -333,6 +333,7 @@ atom max atom maximum atom max_tables max_processes atom mbuf_size +atom md5 atom memory atom memory_internal atom memory_types diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index e96177cfd9..5c98d5c0b5 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -245,7 +245,7 @@ typedef struct { /* * This structure contains all information about the module being loaded. */ - +#define MD5_SIZE 16 typedef struct LoaderState { /* * The current logical file within the binary. @@ -292,7 +292,7 @@ typedef struct LoaderState { StringPatch* string_patches; /* Linked list of position into string table to patch. */ BeamInstr catches; /* Linked list of catch_yf instructions. */ unsigned loaded_size; /* Final size of code when loaded. */ - byte mod_md5[16]; /* MD5 for module code. */ + byte mod_md5[MD5_SIZE]; /* MD5 for module code. */ int may_load_nif; /* true if NIFs may later be loaded for this module */ int on_load; /* Index in the code for the on_load function * (or 0 if there is no on_load function) @@ -528,6 +528,7 @@ static Eterm exported_from_module(Process* p, Eterm mod); static Eterm functions_in_module(Process* p, Eterm mod); static Eterm attributes_for_module(Process* p, Eterm mod); static Eterm compilation_info_for_module(Process* p, Eterm mod); +static Eterm md5_of_module(Process* p, Eterm mod); static Eterm native_addresses(Process* p, Eterm mod); int patch_funentries(Eterm Patchlist); int patch(Eterm Addresses, Uint fe); @@ -648,6 +649,7 @@ erts_prepare_loading(Binary* magic, 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_MD5_PTR] = 0; /* * Read the atom table. @@ -4038,7 +4040,7 @@ freeze_code(LoaderState* stp) } size = (stp->ci * sizeof(BeamInstr)) + (stp->total_literal_size * sizeof(Eterm)) + - strtab_size + attr_size + compile_size + line_size; + strtab_size + attr_size + compile_size + MD5_SIZE + line_size; /* * Move the code to its final location. @@ -4247,11 +4249,20 @@ freeze_code(LoaderState* stp) code[MI_COMPILE_SIZE_ON_HEAP] = decoded_size; } CHKBLK(ERTS_ALC_T_CODE,code); + { + byte* md5_sum = str_table + strtab_size + attr_size + compile_size; + CHKBLK(ERTS_ALC_T_CODE,code); + sys_memcpy(md5_sum, stp->mod_md5, MD5_SIZE); + CHKBLK(ERTS_ALC_T_CODE,code); + code[MI_MD5_PTR] = (BeamInstr) md5_sum; + CHKBLK(ERTS_ALC_T_CODE,code); + } + CHKBLK(ERTS_ALC_T_CODE,code); /* * Make sure that we have not overflowed the allocated code space. */ - ASSERT(str_table + strtab_size + attr_size + compile_size == + ASSERT(str_table + strtab_size + attr_size + compile_size + MD5_SIZE == ((byte *) code) + size); /* @@ -5103,6 +5114,7 @@ erts_module_info_0(Process* p, Eterm module) hp += 3; \ list = CONS(hp, tup, list) + BUILD_INFO(am_md5); BUILD_INFO(am_compile); BUILD_INFO(am_attributes); BUILD_INFO(am_imports); @@ -5118,6 +5130,8 @@ erts_module_info_1(Process* p, Eterm module, Eterm what) return module; } else if (what == am_imports) { return NIL; + } else if (what == am_md5) { + return md5_of_module(p, module); } else if (what == am_exports) { return exported_from_module(p, module); } else if (what == am_functions) { @@ -5306,7 +5320,7 @@ attributes_for_module(Process* p, /* Process whose heap to use. */ Eterm result = NIL; Eterm* end; - if (is_not_atom(mod) || (is_not_list(result) && is_not_nil(result))) { + if (is_not_atom(mod)) { return THE_NON_VALUE; } @@ -5345,7 +5359,7 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */ Eterm result = NIL; Eterm* end; - if (is_not_atom(mod) || (is_not_list(result) && is_not_nil(result))) { + if (is_not_atom(mod)) { return THE_NON_VALUE; } @@ -5367,6 +5381,33 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */ return result; } +/* + * Returns the MD5 checksum for a module + * + * Returns a tagged term, or 0 on error. + */ + +Eterm +md5_of_module(Process* p, /* Process whose heap to use. */ + Eterm mod) /* Tagged atom for module. */ +{ + Module* modp; + BeamInstr* code; + Eterm res = NIL; + + if (is_not_atom(mod)) { + return THE_NON_VALUE; + } + + modp = erts_get_module(mod, erts_active_code_ix()); + if (modp == NULL) { + return THE_NON_VALUE; + } + code = modp->curr.code; + res = new_binary(p, (byte *) code[MI_MD5_PTR], MD5_SIZE); + return res; +} + /* * Build a single {M,F,A,Loction} item to be part of * a stack trace. @@ -5543,7 +5584,7 @@ code_module_md5_1(BIF_ALIST_1) res = am_undefined; goto done; } - res = new_binary(p, stp->mod_md5, sizeof(stp->mod_md5)); + res = new_binary(p, stp->mod_md5, MD5_SIZE); done: erts_free_aligned_binary_bytes(temp_alloc); @@ -5939,6 +5980,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) code[MI_LITERALS_END] = 0; code[MI_LITERALS_OFF_HEAP] = 0; code[MI_ON_LOAD_FUNCTION_PTR] = 0; + code[MI_MD5_PTR] = 0; ci = MI_FUNCTIONS + n + 1; /* diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index bd22b0c4de..0e3ca0bdb0 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -91,7 +91,6 @@ extern Uint erts_total_code_size; #define MI_LITERALS_END 8 #define MI_LITERALS_OFF_HEAP 9 - /* * Pointer to the on_load function (or NULL if none). */ @@ -102,6 +101,11 @@ extern Uint erts_total_code_size; */ #define MI_LINE_TABLE 11 +/* + * Pointer to the module MD5 sum (16 bytes) + */ +#define MI_MD5_PTR 12 + /* * Start of function pointer table. This table contains pointers to * all functions in the module plus an additional pointer just beyond @@ -111,7 +115,7 @@ extern Uint erts_total_code_size; * this table. */ -#define MI_FUNCTIONS 12 +#define MI_FUNCTIONS 13 /* * Layout of the line table. diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index 8a63d9fe3e..06cd2a9b9a 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -24,7 +24,7 @@ -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, - exports/1,functions/1,native/1]). + exports/1,functions/1,native/1,info/1]). %%-compile(native). @@ -52,8 +52,8 @@ end_per_group(_GroupName, Config) -> Config. -modules() -> - [exports, functions, native]. +modules() -> + [exports, functions, native, info]. init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> Dog = ?t:timetrap(?t:minutes(3)), @@ -122,6 +122,21 @@ native_proj({Name,Arity,Addr}) -> native_filter(Set) -> sofs:no_elements(Set) =/= 1. +%% Test that the module info of this module is correct. Use +%% erlang:get_module_info(?MODULE) to avoid compiler optimization tricks. +info(Config) when is_list(Config) -> + Info = erlang:get_module_info(?MODULE), + All = all_exported(), + {ok,{?MODULE,MD5}} = beam_lib:md5(code:which(?MODULE)), + {md5, MD5} = lists:keyfind(md5, 1, Info), + {exports, Exports} = lists:keyfind(exports, 1, Info), + All = lists:sort(Exports), + {attributes, Attrs} = lists:keyfind(attributes, 1, Info), + {vsn,_} = lists:keyfind(vsn, 1, Attrs), + {compile, Compile} = lists:keyfind(compile, 1, Info), + {options,_} = lists:keyfind(options, 1, Compile), + ok. + %% Helper functions (local). add_arity(L) -> diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index cabbbd191f..bcc1250abd 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1664,7 +1664,7 @@ element(_N, _Tuple) -> %% Not documented -spec erlang:get_module_info(Module, Item) -> ModuleInfo when Module :: atom(), - Item :: module | imports | exports | functions | attributes | compile | native_addresses, + Item :: module | imports | exports | functions | attributes | compile | native_addresses | md5, ModuleInfo :: atom() | [] | [{atom(), arity()}] | [{atom(), term()}] | [{atom(), arity(), integer()}]. get_module_info(_Module, _Item) -> erlang:nif_error(undefined). -- cgit v1.2.3 From 4ad572a8b66df8174d92157411326aab95ee8124 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Wed, 14 May 2014 14:49:10 +0200 Subject: Add 'module' entry for module_info/0 function for completeness as it exist in module_info/1. --- erts/emulator/beam/beam_load.c | 1 + erts/emulator/test/module_info_SUITE.erl | 1 + 2 files changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 5c98d5c0b5..8b3fc90826 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -5119,6 +5119,7 @@ erts_module_info_0(Process* p, Eterm module) BUILD_INFO(am_attributes); BUILD_INFO(am_imports); BUILD_INFO(am_exports); + BUILD_INFO(am_module); #undef BUILD_INFO return list; } diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index 06cd2a9b9a..f3986f0c4f 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -128,6 +128,7 @@ info(Config) when is_list(Config) -> Info = erlang:get_module_info(?MODULE), All = all_exported(), {ok,{?MODULE,MD5}} = beam_lib:md5(code:which(?MODULE)), + {module, ?MODULE} = lists:keyfind(module, 1, Info), {md5, MD5} = lists:keyfind(md5, 1, Info), {exports, Exports} = lists:keyfind(exports, 1, Info), All = lists:sort(Exports), -- cgit v1.2.3 From 0dbcbea0cf52672ef1cf051c3cd7640eb7ff728e Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Wed, 14 May 2014 14:54:16 +0200 Subject: Remove obsolete 'imports' entry from module_info/1/2 functions It previously always returned an empty list and was documented to be removed. --- erts/emulator/beam/atom.names | 1 - erts/emulator/beam/beam_load.c | 3 --- 2 files changed, 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 7d11f7f956..dc930f2d38 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -278,7 +278,6 @@ atom http httph https http_response http_request http_header http_eoh http_error atom id atom if_clause atom ignore -atom imports atom in atom in_exiting atom inactive diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 8b3fc90826..a4e72a130a 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -5117,7 +5117,6 @@ erts_module_info_0(Process* p, Eterm module) BUILD_INFO(am_md5); BUILD_INFO(am_compile); BUILD_INFO(am_attributes); - BUILD_INFO(am_imports); BUILD_INFO(am_exports); BUILD_INFO(am_module); #undef BUILD_INFO @@ -5129,8 +5128,6 @@ erts_module_info_1(Process* p, Eterm module, Eterm what) { if (what == am_module) { return module; - } else if (what == am_imports) { - return NIL; } else if (what == am_md5) { return md5_of_module(p, module); } else if (what == am_exports) { -- cgit v1.2.3 From 78540afe8d4a15fe3d6c42208b82bde546e4c0c8 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Wed, 14 May 2014 16:23:02 +0200 Subject: Fix efile_openfile() to handle stat() failure If the initial stat() fails then efile_openfile() will still proceed to open() the file. If that succeeds and the caller passed a non-NULL pSize, then it will copy bogus data from the statbuf into *pSize. This has been observed to cause file:read_file/1 to return truncated file data with no error indication. The use case involved a large file system mounted via NFS, with some directories containing large number of files, and NFS mount options that allow the NFS client to return EIO if the NFS server does not respond quickly enough. Depending on the caching state of the client and server machines, a few stat() calls (fewer than 1 per 10 million) would take long enough to trigger EIO errors, but subsequent open() calls would succeed, and read_file/1 would return truncated data. This sequence of events has been observed via "strace" on beam.smp. Signed-off-by: Mikael Pettersson --- erts/emulator/drivers/unix/unix_efile.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index 42f41c5f3d..878beb055b 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -360,7 +360,12 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ int fd; int mode; /* Open mode. */ - if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) { + if (stat(name, &statbuf) < 0) { + /* statbuf is undefined: if the caller depends on it, + i.e. invoke_read_file(), fail the call immediately */ + if (pSize && flags == EFILE_MODE_READ) + return check_error(-1, errInfo); + } else if (!ISREG(statbuf)) { /* * For UNIX only, here is some ugly code to allow * /dev/null to be opened as a file. -- cgit v1.2.3 From f19fe9b2ada7824973660b41da0873825c5781ca Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 5 May 2014 17:46:07 +0200 Subject: Fix conversion of empty string in erts_convert_native_to_filename() --- erts/emulator/beam/erl_unicode.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index 3a968594f3..f8e1431a53 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -2126,6 +2126,8 @@ Eterm erts_convert_native_to_filename(Process *p, byte *bytes) mac = 1; case ERL_FILENAME_UTF8: size = strlen((char *) bytes); + if (size == 0) + return NIL; if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK) { goto noconvert; } -- cgit v1.2.3 From e291c1776cff0446435b392285b7ec6abe5373bd Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 25 Apr 2014 21:37:17 +0200 Subject: Make binary BIFs converting to lists yield on large input - erlang:binary_to_list/1 - erlang:binary_to_list/3 - erlang:bitstring_to_list/1 --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/binary.c | 149 ++++++++++++++++++++++++++++++++++-- erts/emulator/beam/erl_init.c | 2 +- erts/emulator/hipe/hipe_bif_list.m4 | 5 +- erts/emulator/test/binary_SUITE.erl | 23 +++++- 5 files changed, 170 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index d28e519ae1..d6e312fafd 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -116,6 +116,7 @@ atom binary_longest_prefix_trap atom binary_longest_suffix_trap atom binary_match_trap atom binary_matches_trap +atom binary_to_list_continue atom binary_to_term_trap atom block atom blocked diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index c7926f18af..4fd47210a2 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -38,6 +38,11 @@ static int list_to_bitstr_buf(Eterm obj, char* buf); #endif static int bitstr_list_len(Eterm obj, Uint* num_bytes); +static Export binary_to_list_continue_export; + +static BIF_RETTYPE binary_to_list_continue(BIF_ALIST_1); + + void erts_init_binary(void) { @@ -49,6 +54,11 @@ erts_init_binary(void) "Internal error: Address of orig_bytes[0] of a Binary" " is *not* 8-byte aligned\n"); } + + erts_init_trap_export(&binary_to_list_continue_export, + am_erts_internal, am_binary_to_list_continue, 1, + &binary_to_list_continue); + } /* @@ -333,6 +343,132 @@ BIF_RETTYPE integer_to_binary_1(BIF_ALIST_1) BIF_RET(res); } +#define ERTS_B2L_BYTES_PER_REDUCTION 256 + +typedef struct { + Eterm res; + Eterm *hp; +#ifdef DEBUG + Eterm *hp_end; +#endif + byte *bytes; + Uint size; + Uint bitoffs; +} ErtsB2LState; + +static void b2l_state_destructor(Binary *mbp) +{ + ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(mbp) == b2l_state_destructor); +} + +static BIF_RETTYPE +binary_to_list_chunk(Process *c_p, + Eterm mb_eterm, + ErtsB2LState* sp, + int reds_left, + int gc_disabled) +{ + BIF_RETTYPE ret; + int bump_reds; + Uint size; + byte *bytes; + + size = (reds_left + 1)*ERTS_B2L_BYTES_PER_REDUCTION; + if (size > sp->size) + size = sp->size; + bytes = sp->bytes + (sp->size - size); + + bump_reds = (size - 1)/ERTS_B2L_BYTES_PER_REDUCTION + 1; + BUMP_REDS(c_p, bump_reds); + + ASSERT(is_list(sp->res) || is_nil(sp->res)); + + sp->res = erts_bin_bytes_to_list(sp->res, + sp->hp, + bytes, + size, + sp->bitoffs); + sp->size -= size; + sp->hp += 2*size; + + if (sp->size > 0) { + + if (!gc_disabled) + erts_set_gc_state(c_p, 0); + + ASSERT(c_p->flags & F_DISABLE_GC); + ASSERT(is_value(mb_eterm)); + ERTS_BIF_PREP_TRAP1(ret, + &binary_to_list_continue_export, + c_p, + mb_eterm); + } + else { + + ASSERT(sp->hp == sp->hp_end); + ASSERT(sp->size == 0); + + if (!gc_disabled || !erts_set_gc_state(c_p, 1)) + ERTS_BIF_PREP_RET(ret, sp->res); + else + ERTS_BIF_PREP_YIELD_RETURN(ret, c_p, sp->res); + ASSERT(!(c_p->flags & F_DISABLE_GC)); + } + + return ret; +} + +static ERTS_INLINE BIF_RETTYPE +binary_to_list(Process *c_p, Eterm *hp, Eterm tail, byte *bytes, Uint size, Uint bitoffs) +{ + int reds_left = ERTS_BIF_REDS_LEFT(c_p); + if (size < reds_left*ERTS_B2L_BYTES_PER_REDUCTION) { + Eterm res; + BIF_RETTYPE ret; + int bump_reds = (size - 1)/ERTS_B2L_BYTES_PER_REDUCTION + 1; + BUMP_REDS(c_p, bump_reds); + res = erts_bin_bytes_to_list(tail, hp, bytes, size, bitoffs); + ERTS_BIF_PREP_RET(ret, res); + return ret; + } + else { + Binary *mbp = erts_create_magic_binary(sizeof(ErtsB2LState), + b2l_state_destructor); + ErtsB2LState *sp = ERTS_MAGIC_BIN_DATA(mbp); + Eterm mb; + + sp->res = tail; + sp->hp = hp; +#ifdef DEBUG + sp->hp_end = sp->hp + 2*size; +#endif + sp->bytes = bytes; + sp->size = size; + sp->bitoffs = bitoffs; + + hp = HAlloc(c_p, PROC_BIN_SIZE); + mb = erts_mk_magic_binary_term(&hp, &MSO(c_p), mbp); + return binary_to_list_chunk(c_p, mb, sp, reds_left, 0); + } +} + +static BIF_RETTYPE binary_to_list_continue(BIF_ALIST_1) +{ + Binary *mbp = ((ProcBin *) binary_val(BIF_ARG_1))->val; + + ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(mbp) == b2l_state_destructor); + + ASSERT(BIF_P->flags & F_DISABLE_GC); + + return binary_to_list_chunk(BIF_P, + BIF_ARG_1, + (ErtsB2LState*) ERTS_MAGIC_BIN_DATA(mbp), + ERTS_BIF_REDS_LEFT(BIF_P), + 1); +} + +HIPE_WRAPPER_BIF_DISABLE_GC(binary_to_list, 1) + BIF_RETTYPE binary_to_list_1(BIF_ALIST_1) { Eterm real_bin; @@ -354,14 +490,15 @@ BIF_RETTYPE binary_to_list_1(BIF_ALIST_1) } else { Eterm* hp = HAlloc(BIF_P, 2 * size); byte* bytes = binary_bytes(real_bin)+offset; - - BIF_RET(erts_bin_bytes_to_list(NIL, hp, bytes, size, bitoffs)); + return binary_to_list(BIF_P, hp, NIL, bytes, size, bitoffs); } error: BIF_ERROR(BIF_P, BADARG); } +HIPE_WRAPPER_BIF_DISABLE_GC(binary_to_list, 3) + BIF_RETTYPE binary_to_list_3(BIF_ALIST_3) { byte* bytes; @@ -387,12 +524,13 @@ BIF_RETTYPE binary_to_list_3(BIF_ALIST_3) } i = stop-start+1; hp = HAlloc(BIF_P, 2*i); - BIF_RET(erts_bin_bytes_to_list(NIL, hp, bytes+start-1, i, bitoffs)); - + return binary_to_list(BIF_P, hp, NIL, bytes+start-1, i, bitoffs); error: BIF_ERROR(BIF_P, BADARG); } +HIPE_WRAPPER_BIF_DISABLE_GC(bitstring_to_list, 1) + BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1) { Eterm real_bin; @@ -431,7 +569,8 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1) previous = CONS(hp, make_binary(last), previous); hp += 2; } - BIF_RET(erts_bin_bytes_to_list(previous, hp, bytes, size, bitoffs)); + + return binary_to_list(BIF_P, hp, previous, bytes, size, bitoffs); } diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index d54658f1ea..5e6d812242 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -324,7 +324,6 @@ erl_init(int ncpu, BIN_VH_MIN_SIZE = erts_next_heap_size(BIN_VH_MIN_SIZE, 0); erts_init_trace(); - erts_init_binary(); erts_init_bits(); erts_code_ix_init(); erts_init_fun_table(); @@ -337,6 +336,7 @@ erl_init(int ncpu, erts_ddll_init(); init_emulator(); erts_ptab_init(); /* Must be after init_emulator() */ + erts_init_binary(); /* Must be after init_emulator() */ erts_bp_init(); init_db(); /* Must be after init_emulator */ erts_bif_timer_init(); diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 0997d81b2f..e3563af61e 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -270,7 +270,10 @@ define(CFUN,`ifelse($1,term_to_binary_1,hipe_wrapper_term_to_binary_1, ifelse($1,term_to_binary_2,hipe_wrapper_term_to_binary_2, ifelse($1,erts_internal_binary_to_term_1,hipe_wrapper_erts_internal_binary_to_term_1, ifelse($1,erts_internal_binary_to_term_2,hipe_wrapper_erts_internal_binary_to_term_2, -$1))))') +ifelse($1,binary_to_list_1,hipe_wrapper_binary_to_list_1, +ifelse($1,binary_to_list_3,hipe_wrapper_binary_to_list_3, +ifelse($1,bitstring_to_list_1,hipe_wrapper_bitstring_to_list_1, +$1)))))))') define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))') include(TARGET/`erl_bif_list.h') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 7aba367e33..fe34e60c27 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -58,7 +58,7 @@ ordering/1,unaligned_order/1,gc_test/1, bit_sized_binary_sizes/1, otp_6817/1,deep/1,obsolete_funs/1,robustness/1,otp_8117/1, - otp_8180/1, trapping/1]). + otp_8180/1, trapping/1, large/1]). %% Internal exports. -export([sleeper/0,trapping_loop/4]). @@ -76,7 +76,7 @@ all() -> bad_term_to_binary, more_bad_terms, otp_5484, otp_5933, ordering, unaligned_order, gc_test, bit_sized_binary_sizes, otp_6817, otp_8117, deep, - obsolete_funs, robustness, otp_8180, trapping]. + obsolete_funs, robustness, otp_8180, trapping, large]. groups() -> []. @@ -1351,7 +1351,12 @@ trapping(Config) when is_list(Config)-> do_trapping(5, term_to_binary, fun() -> [lists:duplicate(2000000,2000000)] end), do_trapping(5, binary_to_term, - fun() -> [term_to_binary(lists:duplicate(2000000,2000000))] end). + fun() -> [term_to_binary(lists:duplicate(2000000,2000000))] end), + do_trapping(5, binary_to_list, + fun() -> [list_to_binary(lists:duplicate(2000000,$x))] end), + do_trapping(5, bitstring_to_list, + fun() -> [list_to_bitstring([lists:duplicate(2000000,$x),<<7:4>>])] end) + . do_trapping(0, _, _) -> ok; @@ -1384,6 +1389,18 @@ trapping_loop2(Bif,Args,N) -> apply(erlang,Bif,Args), trapping_loop2(Bif, Args, N-1). +large(Config) when is_list(Config) -> + List = lists:flatten(lists:map(fun (_) -> + [0,1,2,3,4,5,6,7,8] + end, + lists:seq(1, 131072))), + Bin = list_to_binary(List), + List = binary_to_list(Bin), + PartList = lists:reverse(tl(tl(lists:reverse(tl(tl(List)))))), + PartList = binary_to_list(Bin, 3, length(List)-2), + ListBS = List ++ [<<7:4>>], + ListBS = bitstring_to_list(list_to_bitstring(ListBS)), + ok. %% Utilities. -- cgit v1.2.3 From 3c1e5f014d8c957cfc085d200ca744bd4ad2a7bb Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 13 May 2014 16:10:30 +0200 Subject: Do not GC other processes in non-smp runtime --- erts/emulator/beam/erl_message.c | 37 ------------------------------------- 1 file changed, 37 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 0eb8117980..59a677a12c 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1032,7 +1032,6 @@ erts_send_message(Process* sender, } BM_SWAP_TIMER(send,system); } else { -#ifdef ERTS_SMP ErlOffHeap *ohp; Eterm *hp; erts_aint32_t state; @@ -1064,42 +1063,6 @@ erts_send_message(Process* sender, #endif ); BM_SWAP_TIMER(send,system); -#else - ErlMessage* mp = message_alloc(); - Eterm *hp; - BM_SWAP_TIMER(send,size); - msize = size_object(message); - BM_SWAP_TIMER(size,send); - - if (receiver->stop - receiver->htop <= msize) { - BM_SWAP_TIMER(send,system); - erts_garbage_collect(receiver, msize, receiver->arg_reg, receiver->arity); - BM_SWAP_TIMER(system,send); - } - hp = receiver->htop; - receiver->htop = hp + msize; - BM_SWAP_TIMER(send,copy); - message = copy_struct(message, msize, &hp, &receiver->off_heap); - BM_MESSAGE_COPIED(msize); - BM_SWAP_TIMER(copy,send); - DTRACE6(message_send, sender_name, receiver_name, - (uint32_t)msize, tok_label, tok_lastcnt, tok_serial); - ERL_MESSAGE_TERM(mp) = message; - ERL_MESSAGE_TOKEN(mp) = NIL; -#ifdef USE_VM_PROBES - ERL_MESSAGE_DT_UTAG(mp) = NIL; -#endif - mp->next = NULL; - mp->data.attached = NULL; - LINK_MESSAGE(receiver, mp); - res = receiver->msg.len; - erts_proc_notify_new_message(receiver); - - if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) { - trace_receive(receiver, message); - } - BM_SWAP_TIMER(send,system); -#endif /* #ifndef ERTS_SMP */ } return res; } -- cgit v1.2.3 From 4eabd78146594a9cd75eb9dc62ba449f7b6b8eb5 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 22 May 2014 18:18:47 +0200 Subject: Do not use __always_inline__ attribute unless gcc vsn >= 3.1.1 --- erts/include/internal/ethread.h | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 6f9bc184ef..230388cce5 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -51,10 +51,27 @@ # endif #endif +#if !defined(__GNUC__) +# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) 0 +#elif !defined(__GNUC_MINOR__) +# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ + ((__GNUC__ << 24) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) +#elif !defined(__GNUC_PATCHLEVEL__) +# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ + (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12)) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) +#else +# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ + (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12) | __GNUC_PATCHLEVEL__) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) +#endif + #undef ETHR_INLINE #if defined(__GNUC__) # define ETHR_INLINE __inline__ -# define ETHR_FORCE_INLINE __inline__ __attribute__((__always_inline__)) +# if ETHR_AT_LEAST_GCC_VSN__(3, 1, 1) +# define ETHR_FORCE_INLINE __inline__ __attribute__((__always_inline__)) +# else +# define ETHR_FORCE_INLINE __inline__ +# endif #elif defined(__WIN32__) # define ETHR_INLINE __forceinline # define ETHR_FORCE_INLINE __forceinline @@ -288,19 +305,6 @@ ETHR_PROTO_NORETURN__ ethr_fatal_error__(const char *file, const char *func, int err); -#if !defined(__GNUC__) -# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) 0 -#elif !defined(__GNUC_MINOR__) -# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ - ((__GNUC__ << 24) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) -#elif !defined(__GNUC_PATCHLEVEL__) -# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ - (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12)) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) -#else -# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ - (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12) | __GNUC_PATCHLEVEL__) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) -#endif - #if !ETHR_AT_LEAST_GCC_VSN__(2, 96, 0) #define __builtin_expect(X, Y) (X) #endif -- cgit v1.2.3 From 8231acf6362e8902ca60ecabbafc18f42ec4534a Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 22 May 2014 22:07:28 +0200 Subject: Change ERTS vsn --- erts/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index 2e773079f3..fff334c89f 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.1 +VSN = 6.0.2 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From c15cc0737b70ff5d4d1b79e8171106fcafcb1dda Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 28 Apr 2014 23:55:12 +0200 Subject: Make binary BIFs converting from lists yield on large input - erlang:list_to_binary/1 - erlang:iolist_to_binary/1 - erlang:list_to_bitstring/1 - binary:list_to_bin/1 --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/binary.c | 1012 +++++++++++++++++++++++++++-------- erts/emulator/beam/erl_bif_binary.c | 11 +- erts/emulator/beam/erl_binary.h | 2 +- erts/emulator/beam/global.h | 53 +- erts/emulator/beam/utils.c | 490 +++++++++++++---- erts/emulator/hipe/hipe_bif_list.m4 | 6 +- erts/emulator/test/binary_SUITE.erl | 175 +++++- 8 files changed, 1408 insertions(+), 342 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index d6e312fafd..5d06a32941 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -316,6 +316,7 @@ atom line_length atom linked_in_driver atom links atom list +atom list_to_binary_continue atom little atom loaded atom load_cancelled diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index 4fd47210a2..f50d484576 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -31,17 +31,11 @@ #include "erl_binary.h" #include "erl_bits.h" -#ifdef DEBUG -static int list_to_bitstr_buf(Eterm obj, char* buf, Uint len); -#else -static int list_to_bitstr_buf(Eterm obj, char* buf); -#endif -static int bitstr_list_len(Eterm obj, Uint* num_bytes); - static Export binary_to_list_continue_export; +static Export list_to_binary_continue_export; static BIF_RETTYPE binary_to_list_continue(BIF_ALIST_1); - +static BIF_RETTYPE list_to_binary_continue(BIF_ALIST_1); void erts_init_binary(void) @@ -59,6 +53,10 @@ erts_init_binary(void) am_erts_internal, am_binary_to_list_continue, 1, &binary_to_list_continue); + erts_init_trap_export(&list_to_binary_continue_export, + am_erts_internal, am_list_to_binary_continue, 1, + &list_to_binary_continue); + } /* @@ -577,117 +575,433 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1) /* Turn a possibly deep list of ints (and binaries) into */ /* One large binary object */ -/* - * This bif also exists in the binary module, under the name - * binary:list_to_bin/1, why it's divided into interface and - * implementation. Also the backend for iolist_to_binary_1. - */ +typedef enum { + ERTS_L2B_OK, + ERTS_L2B_YIELD, + ERTS_L2B_TYPE_ERROR, + ERTS_L2B_OVERFLOW_ERROR +} ErtsL2BResult; -BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg) -{ +#define ERTS_L2B_STATE_INITER(C_P, ARG, BIF, SZFunc, TBufFunc) \ + {ERTS_IOLIST2BUF_STATE_INITER((C_P), (ARG)), \ + (ARG), THE_NON_VALUE, (BIF), (SZFunc), (TBufFunc)} + +#define ERTS_L2B_STATE_MOVE(TO, FROM) \ + sys_memcpy((void *) (TO), (void *) (FROM), sizeof(ErtsL2BState)) + +typedef struct ErtsL2BState_ ErtsL2BState; + +struct ErtsL2BState_ { + ErtsIOList2BufState buf; + Eterm arg; Eterm bin; - Eterm h,t; - ErlDrvSizeT size; - byte* bytes; -#ifdef DEBUG - ErlDrvSizeT offset; -#endif + Export *bif; + int (*iolist_to_buf_size)(ErtsIOListState *); + ErlDrvSizeT (*iolist_to_buf)(ErtsIOList2BufState *); +}; + +static ERTS_INLINE ErtsL2BResult +list_to_binary_engine(ErtsL2BState *sp) +{ + ErlDrvSizeT res; + Process *c_p = sp->buf.iolist.c_p; + + /* + * have_size == 0 while sp->iolist_to_buf_size() + * has not finished the calculation. + */ + + if (!sp->buf.iolist.have_size) { + switch (sp->iolist_to_buf_size(&sp->buf.iolist)) { + case ERTS_IOLIST_YIELD: + return ERTS_L2B_YIELD; + case ERTS_IOLIST_OVERFLOW: + return ERTS_L2B_OVERFLOW_ERROR; + case ERTS_IOLIST_TYPE: + return ERTS_L2B_TYPE_ERROR; + case ERTS_IOLIST_OK: + break; + default: + ASSERT(0); + break; + } + + ASSERT(sp->buf.iolist.have_size); + + /* + * Size calculated... Setup state for + * sp->iolist_to_buf_*() + */ + + sp->bin = new_binary(c_p, + (byte *) NULL, + sp->buf.iolist.size); + + if (sp->buf.iolist.size == 0) + return ERTS_L2B_OK; + + sp->buf.buf = (char *) binary_bytes(sp->bin); + sp->buf.len = sp->buf.iolist.size; + sp->buf.iolist.obj = sp->arg; - if (is_nil(arg)) { - BIF_RET(new_binary(p,(byte*)"",0)); + if (sp->buf.iolist.reds_left <= 0) { + BUMP_ALL_REDS(c_p); + return ERTS_L2B_YIELD; + } } - if (is_not_list(arg)) { - goto error; + + ASSERT(sp->buf.iolist.size != 0); + ASSERT(is_value(sp->bin)); + ASSERT(sp->buf.buf); + + res = sp->iolist_to_buf(&sp->buf); + + if (!ERTS_IOLIST_TO_BUF_FAILED(res)) { + ASSERT(res == 0); + return ERTS_L2B_OK; } - /* check for [binary()] case */ - h = CAR(list_val(arg)); - t = CDR(list_val(arg)); - if (is_binary(h) && is_nil(t) && !( - HEADER_SUB_BIN == *(binary_val(h)) && ( - ((ErlSubBin *)binary_val(h))->bitoffs != 0 || - ((ErlSubBin *)binary_val(h))->bitsize != 0 - ))) { - return h; - } - switch (erts_iolist_size(arg, &size)) { - case ERTS_IOLIST_OVERFLOW: BIF_ERROR(p, SYSTEM_LIMIT); - case ERTS_IOLIST_TYPE: goto error; - default: ; - } - bin = new_binary(p, (byte *)NULL, size); - bytes = binary_bytes(bin); -#ifdef DEBUG - offset = -#endif - erts_iolist_to_buf(arg, (char*) bytes, size); - ASSERT(offset == 0); - BIF_RET(bin); + switch (res) { + case ERTS_IOLIST_TO_BUF_YIELD: + return ERTS_L2B_YIELD; + case ERTS_IOLIST_TO_BUF_OVERFLOW: + return ERTS_L2B_OVERFLOW_ERROR; + case ERTS_IOLIST_TO_BUF_TYPE_ERROR: + return ERTS_L2B_TYPE_ERROR; + default: + ERTS_INTERNAL_ERROR("Invalid return value from iolist_to_buf_yielding()"); + return ERTS_L2B_TYPE_ERROR; + } +} + +static void +l2b_state_destructor(Binary *mbp) +{ + ErtsL2BState *sp = ERTS_MAGIC_BIN_DATA(mbp); + ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(mbp) == l2b_state_destructor); + DESTROY_SAVED_ESTACK(&sp->buf.iolist.estack); +} + +static ERTS_INLINE Eterm +l2b_final_touch(Process *c_p, ErtsL2BState *sp) +{ + Eterm *hp; + ErlSubBin* sbin; + if (sp->buf.offset == 0) + return sp->bin; + + hp = HAlloc(c_p, ERL_SUB_BIN_SIZE); + ASSERT(sp->buf.offset > 0); + sbin = (ErlSubBin *) hp; + sbin->thing_word = HEADER_SUB_BIN; + sbin->size = sp->buf.iolist.size-1; + sbin->offs = 0; + sbin->orig = sp->bin; + sbin->bitoffs = 0; + sbin->bitsize = sp->buf.offset; + sbin->is_writable = 0; + return make_binary(sbin); +} + +static BIF_RETTYPE +list_to_binary_chunk(Eterm mb_eterm, + ErtsL2BState* sp, + int reds_left, + int gc_disabled) +{ + Eterm err = BADARG; + BIF_RETTYPE ret; + Process *c_p = sp->buf.iolist.c_p; + + sp->buf.iolist.reds_left = reds_left; - error: - BIF_ERROR(p, BADARG); + switch (list_to_binary_engine(sp)) { + + case ERTS_L2B_OK: { + Eterm result = l2b_final_touch(c_p, sp); + if (!gc_disabled || !erts_set_gc_state(c_p, 1)) + ERTS_BIF_PREP_RET(ret, result); + else + ERTS_BIF_PREP_YIELD_RETURN(ret, c_p, result); + ASSERT(!(c_p->flags & F_DISABLE_GC)); + break; + } + case ERTS_L2B_YIELD: + if (!gc_disabled) { + /* first yield... */ + Eterm *hp; + Binary *mbp = erts_create_magic_binary(sizeof(ErtsL2BState), + l2b_state_destructor); + ErtsL2BState *new_sp = ERTS_MAGIC_BIN_DATA(mbp); + + ERTS_L2B_STATE_MOVE(new_sp, sp); + sp = new_sp; + + hp = HAlloc(c_p, PROC_BIN_SIZE); + mb_eterm = erts_mk_magic_binary_term(&hp, &MSO(c_p), mbp); + + ASSERT(is_value(mb_eterm)); + + erts_set_gc_state(c_p, 0); + } + + ASSERT(c_p->flags & F_DISABLE_GC); + + ERTS_BIF_PREP_TRAP1(ret, + &list_to_binary_continue_export, + c_p, + mb_eterm); + break; + + case ERTS_L2B_OVERFLOW_ERROR: + err = SYSTEM_LIMIT; + /* fall through */ + + case ERTS_L2B_TYPE_ERROR: + if (!gc_disabled) + ERTS_BIF_PREP_ERROR(ret, c_p, err); + else { + if (erts_set_gc_state(c_p, 1)) + ERTS_VBUMP_ALL_REDS(c_p); + + ERTS_BIF_PREP_ERROR_TRAPPED1(ret, + c_p, + err, + sp->bif, + sp->arg); + } + + ASSERT(!(c_p->flags & F_DISABLE_GC)); + break; + + default: + ERTS_INTERNAL_ERROR("Invalid return value from list_to_binary_engine()"); + ERTS_BIF_PREP_ERROR(ret,c_p, EXC_INTERNAL_ERROR); + break; + } + return ret; +} + +static BIF_RETTYPE list_to_binary_continue(BIF_ALIST_1) +{ + Binary *mbp = ((ProcBin *) binary_val(BIF_ARG_1))->val; + ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(mbp) == l2b_state_destructor); + + ASSERT(BIF_P->flags & F_DISABLE_GC); + + return list_to_binary_chunk(BIF_ARG_1, + ERTS_MAGIC_BIN_DATA(mbp), + ERTS_BIF_REDS_LEFT(BIF_P), + 1); } +BIF_RETTYPE erts_list_to_binary_bif(Process *c_p, Eterm arg, Export *bif) +{ + BIF_RETTYPE ret; + + if (is_nil(arg)) + ERTS_BIF_PREP_RET(ret, new_binary(c_p, (byte *) "", 0)); + else if (is_not_list(arg)) + ERTS_BIF_PREP_ERROR(ret, c_p, BADARG); + else { + /* check for [binary()] case */ + Eterm h = CAR(list_val(arg)); + Eterm t = CDR(list_val(arg)); + if (is_binary(h) + && is_nil(t) + && !(HEADER_SUB_BIN == *(binary_val(h)) + && (((ErlSubBin *)binary_val(h))->bitoffs != 0 + || ((ErlSubBin *)binary_val(h))->bitsize != 0))) { + ERTS_BIF_PREP_RET(ret, h); + } + else { + ErtsL2BState state = ERTS_L2B_STATE_INITER(c_p, + arg, + bif, + erts_iolist_size_yielding, + erts_iolist_to_buf_yielding); + int orig_reds_left = ERTS_BIF_REDS_LEFT(c_p); + + /* + * First try to do it all at once without having to use + * yielding iolist_to_buf(). + */ + state.buf.iolist.reds_left = orig_reds_left; + switch (erts_iolist_size_yielding(&state.buf.iolist)) { + case ERTS_IOLIST_OK: { + ErlDrvSizeT size = state.buf.iolist.size; + Eterm bin; + char *buf; + + if (size == 0) { + ERTS_BIF_PREP_RET(ret, new_binary(c_p, (byte *) NULL, 0)); + break; /* done */ + } + + bin = new_binary(c_p, (byte *) NULL, size); + buf = (char *) binary_bytes(bin); + + if (size < ERTS_IOLIST_TO_BUF_BYTES_PER_RED*CONTEXT_REDS) { + /* An (over) estimation of reductions needed */ + int reds_left = state.buf.iolist.reds_left; + int to_buf_reds = orig_reds_left - reds_left; + to_buf_reds += size/ERTS_IOLIST_TO_BUF_BYTES_PER_RED; + if (to_buf_reds <= reds_left) { + ErlDrvSizeT res; + + res = erts_iolist_to_buf(arg, buf, size); + if (res == 0) { + BUMP_REDS(c_p, to_buf_reds); + ERTS_BIF_PREP_RET(ret, bin); + break; /* done */ + } + if (!ERTS_IOLIST_TO_BUF_FAILED(res)) + ERTS_INTERNAL_ERROR("iolist_size/iolist_to_buf missmatch"); + if (res == ERTS_IOLIST_TO_BUF_OVERFLOW) + goto overflow; + goto type_error; + } + } + /* + * Since size has been computed list_to_binary_chunk() expects + * state prepared for iolist_to_buf. + */ + state.bin = bin; + state.buf.buf = buf; + state.buf.len = size; + state.buf.iolist.obj = arg; + /* Fall through... */ + } + case ERTS_IOLIST_YIELD: + ret = list_to_binary_chunk(THE_NON_VALUE, + &state, + state.buf.iolist.reds_left, + 0); + break; + case ERTS_IOLIST_OVERFLOW: + overflow: + ERTS_BIF_PREP_ERROR(ret, c_p, SYSTEM_LIMIT); + break; + case ERTS_IOLIST_TYPE: + type_error: + default: + ERTS_BIF_PREP_ERROR(ret, c_p, BADARG); + break; + } + } + } + return ret; +} + +HIPE_WRAPPER_BIF_DISABLE_GC(list_to_binary, 1) + BIF_RETTYPE list_to_binary_1(BIF_ALIST_1) { - return erts_list_to_binary_bif(BIF_P, BIF_ARG_1); + return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_list_to_binary_1]); } -/* Turn a possibly deep list of ints (and binaries) into */ -/* One large binary object */ +HIPE_WRAPPER_BIF_DISABLE_GC(iolist_to_binary, 1) BIF_RETTYPE iolist_to_binary_1(BIF_ALIST_1) { if (is_binary(BIF_ARG_1)) { BIF_RET(BIF_ARG_1); } - return erts_list_to_binary_bif(BIF_P, BIF_ARG_1); + return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_iolist_to_binary_1]); } +static int bitstr_list_len(ErtsIOListState *); +static ErlDrvSizeT list_to_bitstr_buf_yielding(ErtsIOList2BufState *); +static ErlDrvSizeT list_to_bitstr_buf_not_yielding(ErtsIOList2BufState *); + +HIPE_WRAPPER_BIF_DISABLE_GC(list_to_bitstring, 1) + BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1) { - Eterm bin; - Uint sz; - int offset; - byte* bytes; - ErlSubBin* sb1; - Eterm* hp; - - if (is_nil(BIF_ARG_1)) { - BIF_RET(new_binary(BIF_P,(byte*)"",0)); - } - if (is_not_list(BIF_ARG_1)) { - error: - BIF_ERROR(BIF_P, BADARG); - } - switch (bitstr_list_len(BIF_ARG_1, &sz)) { - case ERTS_IOLIST_TYPE: - goto error; - case ERTS_IOLIST_OVERFLOW: - BIF_ERROR(BIF_P, SYSTEM_LIMIT); - } - bin = new_binary(BIF_P, (byte *)NULL, sz); - bytes = binary_bytes(bin); -#ifdef DEBUG - offset = list_to_bitstr_buf(BIF_ARG_1, (char*) bytes, sz); -#else - offset = list_to_bitstr_buf(BIF_ARG_1, (char*) bytes); -#endif - ASSERT(offset >= 0); - if (offset > 0) { - hp = HAlloc(BIF_P, ERL_SUB_BIN_SIZE); - sb1 = (ErlSubBin *) hp; - sb1->thing_word = HEADER_SUB_BIN; - sb1->size = sz-1; - sb1->offs = 0; - sb1->orig = bin; - sb1->bitoffs = 0; - sb1->bitsize = offset; - sb1->is_writable = 0; - bin = make_binary(sb1); + BIF_RETTYPE ret; + + if (is_nil(BIF_ARG_1)) + ERTS_BIF_PREP_RET(ret, new_binary(BIF_P, (byte *) "", 0)); + else if (is_not_list(BIF_ARG_1)) + ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG); + else { + /* check for [bitstring()] case */ + Eterm h = CAR(list_val(BIF_ARG_1)); + Eterm t = CDR(list_val(BIF_ARG_1)); + if (is_binary(h) && is_nil(t)) { + ERTS_BIF_PREP_RET(ret, h); + } + else { + ErtsL2BState state = ERTS_L2B_STATE_INITER(BIF_P, + BIF_ARG_1, + bif_export[BIF_list_to_bitstring_1], + bitstr_list_len, + list_to_bitstr_buf_yielding); + int orig_reds_left = ERTS_BIF_REDS_LEFT(BIF_P); + + /* + * First try to do it all at once without having to use + * yielding list_to_bitstr_buf(). + */ + state.buf.iolist.reds_left = orig_reds_left; + switch (bitstr_list_len(&state.buf.iolist)) { + case ERTS_IOLIST_OK: { + ErlDrvSizeT size = state.buf.iolist.size; + + state.bin = new_binary(BIF_P, (byte *) NULL, size); + state.buf.buf = (char *) binary_bytes(state.bin); + state.buf.len = size; + state.buf.iolist.obj = BIF_ARG_1; + + if (size < ERTS_IOLIST_TO_BUF_BYTES_PER_RED*CONTEXT_REDS) { + /* An (over) estimation of reductions needed */ + int reds_left = state.buf.iolist.reds_left; + int to_buf_reds = orig_reds_left - reds_left; + to_buf_reds += size/ERTS_IOLIST_TO_BUF_BYTES_PER_RED; + if (to_buf_reds <= reds_left) { + ErlDrvSizeT res; + + res = list_to_bitstr_buf_not_yielding(&state.buf); + if (res == 0) { + Eterm res_bin = l2b_final_touch(BIF_P, &state); + BUMP_REDS(BIF_P, to_buf_reds); + ERTS_BIF_PREP_RET(ret, res_bin); + break; /* done */ + } + if (!ERTS_IOLIST_TO_BUF_FAILED(res)) + ERTS_INTERNAL_ERROR("iolist_size/iolist_to_buf missmatch"); + if (res == ERTS_IOLIST_TO_BUF_OVERFLOW) + goto overflow; + goto type_error; + } + } + /* + * Since size has been computed list_to_binary_chunk() expects + * the state prepared for list_to_bitstr_buf. + */ + + /* Fall through... */ + } + case ERTS_IOLIST_YIELD: + ret = list_to_binary_chunk(THE_NON_VALUE, + &state, + state.buf.iolist.reds_left, + 0); + break; + case ERTS_IOLIST_OVERFLOW: + overflow: + ERTS_BIF_PREP_ERROR(ret, BIF_P, SYSTEM_LIMIT); + break; + case ERTS_IOLIST_TYPE: + type_error: + default: + ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG); + break; + } + } } - - BIF_RET(bin); + + return ret; } BIF_RETTYPE split_binary_2(BIF_ALIST_2) @@ -744,123 +1058,353 @@ BIF_RETTYPE split_binary_2(BIF_ALIST_2) * Local functions. */ +static int +list_to_bitstr_buf_bcopy(ErtsIOList2BufState *state, Eterm obj, int *yield_countp); + /* * The input list is assumed to be type-correct and the buffer is * assumed to be of sufficient size. Those assumptions are verified in * the DEBUG-built emulator. */ -static int +static ErlDrvSizeT +list_to_bitstr_buf(int yield_support, ErtsIOList2BufState *state) +{ + +#undef LIST_TO_BITSTR_BUF_BCOPY_DBG +#undef LIST_TO_BITSTR_BUF_BCOPY #ifdef DEBUG -list_to_bitstr_buf(Eterm obj, char* buf, Uint len) +#define LIST_TO_BITSTR_BUF_BCOPY_DBG \ + len -= size + (offset>7); #else -list_to_bitstr_buf(Eterm obj, char* buf) +#define LIST_TO_BITSTR_BUF_BCOPY_DBG #endif -{ - Eterm* objp; - int offset = 0; +#define LIST_TO_BITSTR_BUF_BCOPY(CONSP) \ + do { \ + byte* bptr; \ + Uint bitsize; \ + Uint bitoffs; \ + Uint num_bits; \ + size_t size = binary_size(obj); \ + if (yield_support) { \ + size_t max_size = ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; \ + if (yield_count > 0) \ + max_size *= yield_count+1; \ + if (size > max_size) { \ + state->objp = CONSP; \ + goto L_bcopy_yield; \ + } \ + if (size >= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT) { \ + int cost = (int) size; \ + cost /= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; \ + yield_count -= cost; \ + } \ + } \ + ASSERT(size <= len); \ + ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); \ + num_bits = 8*size+bitsize; \ + copy_binary_to_buffer(buf, offset, bptr, bitoffs, num_bits); \ + offset += bitsize; \ + buf += size + (offset>7); \ + LIST_TO_BITSTR_BUF_BCOPY_DBG; \ + offset = offset & 7; \ + } while(0) + +#ifdef DEBUG + ErlDrvSizeT len; +#endif + Eterm obj; + char *buf; + Eterm *objp = NULL; + int offset; + int init_yield_count = 0, yield_count; DECLARE_ESTACK(s); - goto L_again; - - while (!ESTACK_ISEMPTY(s)) { - obj = ESTACK_POP(s); - L_again: - if (is_list(obj)) { - L_iter_list: - objp = list_val(obj); - obj = CAR(objp); - if (is_byte(obj)) { - ASSERT(len > 0); - if (offset == 0) { - *buf++ = unsigned_val(obj); - } else { - *buf = (char)((unsigned_val(obj) >> offset) | - ((*buf >> (8-offset)) << (8-offset))); - buf++; - *buf = (unsigned_val(obj) << (8-offset)); - } + + obj = state->iolist.obj; + buf = state->buf; + offset = state->offset; #ifdef DEBUG - len--; + len = state->len; #endif - } else if (is_binary(obj)) { - byte* bptr; - size_t size = binary_size(obj); - Uint bitsize; - Uint bitoffs; - Uint num_bits; - - ASSERT(size <= len); - ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); - num_bits = 8*size+bitsize; - copy_binary_to_buffer(buf, offset, bptr, bitoffs, num_bits); - offset += bitsize; - buf += size + (offset>7); + + if (!yield_support) { + yield_count = init_yield_count = 0; /* Shut up faulty warning... >:-( */ + goto L_again; + } + else { + + if (state->iolist.reds_left <= 0) + return ERTS_IOLIST_TO_BUF_YIELD; + + ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); + init_yield_count = (ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED + * state->iolist.reds_left); + yield_count = init_yield_count; + + if (!state->iolist.estack.start) + goto L_again; + else { + int chk_stack; + /* Restart; restore state... */ + ESTACK_RESTORE(s, &state->iolist.estack); + + if (!state->bcopy.bptr) + chk_stack = 0; + else { + chk_stack = 1; + if (list_to_bitstr_buf_bcopy(state, THE_NON_VALUE, &yield_count)) { + /* Yield again... */ + BUMP_ALL_REDS(state->iolist.c_p); + state->iolist.reds_left = 0; + ESTACK_SAVE(s, &state->iolist.estack); + return ERTS_IOLIST_TO_BUF_YIELD; + } + buf = state->buf; + offset = state->offset; #ifdef DEBUG - len -= size + (offset>7); + len = state->len; #endif - offset = offset & 7; - } else if (is_list(obj)) { - ESTACK_PUSH(s, CDR(objp)); - goto L_iter_list; /* on head */ - } else { - ASSERT(is_nil(obj)); } - obj = CDR(objp); - if (is_list(obj)) { - goto L_iter_list; /* on tail */ - } else if (is_binary(obj)) { - byte* bptr; - size_t size = binary_size(obj); - Uint bitsize; - Uint bitoffs; - Uint num_bits; - - ASSERT(size <= len); - ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); - num_bits = 8*size+bitsize; - copy_binary_to_buffer(buf, offset, bptr, bitoffs, num_bits); - offset += bitsize; - buf += size+(offset>7); + objp = state->objp; + state->objp = NULL; + + if (objp) + goto L_tail; + if (!chk_stack) + goto L_again; + /* check stack */ + } + } + + while (!ESTACK_ISEMPTY(s)) { + obj = ESTACK_POP(s); + L_again: + if (is_list(obj)) { + while (1) { /* Tail loop */ + while (1) { /* Head loop */ + if (yield_support && --yield_count <= 0) + goto L_yield; + objp = list_val(obj); + obj = CAR(objp); + if (is_byte(obj)) { + ASSERT(len > 0); + if (offset == 0) { + *buf++ = unsigned_val(obj); + } else { + *buf = (char)((unsigned_val(obj) >> offset) | + ((*buf >> (8-offset)) << (8-offset))); + buf++; + *buf = (unsigned_val(obj) << (8-offset)); + } #ifdef DEBUG - len -= size+(offset>7); + len--; #endif - offset = offset & 7; - } else { - ASSERT(is_nil(obj)); + } else if (is_binary(obj)) { + LIST_TO_BITSTR_BUF_BCOPY(objp); + } else if (is_list(obj)) { + ESTACK_PUSH(s, CDR(objp)); + continue; /* Head loop */ + } else { + ASSERT(is_nil(obj)); + } + break; + } + + L_tail: + + obj = CDR(objp); + if (is_list(obj)) { + continue; /* Tail loop */ + } else if (is_binary(obj)) { + LIST_TO_BITSTR_BUF_BCOPY(NULL); + } else { + ASSERT(is_nil(obj)); + } + break; } } else if (is_binary(obj)) { - byte* bptr; - size_t size = binary_size(obj); - Uint bitsize; - Uint bitoffs; - Uint num_bits; - - ASSERT(size <= len); - ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); - num_bits = 8*size+bitsize; - copy_binary_to_buffer(buf, offset, bptr, bitoffs, num_bits); - offset += bitsize; - buf += size + (offset>7); -#ifdef DEBUG - len -= size + (offset>7); -#endif - offset = offset & 7; + LIST_TO_BITSTR_BUF_BCOPY(NULL); } else { + if (yield_support && --yield_count <= 0) + goto L_yield; ASSERT(is_nil(obj)); } } DESTROY_ESTACK(s); - return offset; + + if (yield_support) { + int reds; + CLEAR_SAVED_ESTACK(&state->iolist.estack); + reds = ((init_yield_count - yield_count - 1) + / ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED) + 1; + BUMP_REDS(state->iolist.c_p, reds); + state->iolist.reds_left -= reds; + if (state->iolist.reds_left < 0) + state->iolist.reds_left = 0; + } + state->buf = buf; + state->offset = offset; + return 0; + +L_bcopy_yield: + + state->buf = buf; + state->offset = offset; +#ifdef DEBUG + state->len = len; +#endif + + if (list_to_bitstr_buf_bcopy(state, obj, &yield_count) == 0) + ERTS_INTERNAL_ERROR("Missing yield"); + + BUMP_ALL_REDS(state->iolist.c_p); + state->iolist.reds_left = 0; + ESTACK_SAVE(s, &state->iolist.estack); + return ERTS_IOLIST_TO_BUF_YIELD; + +L_yield: + + BUMP_ALL_REDS(state->iolist.c_p); + state->iolist.reds_left = 0; + state->iolist.obj = obj; + state->buf = buf; + state->offset = offset; + ESTACK_SAVE(s, &state->iolist.estack); +#ifdef DEBUG + state->len = len; +#endif + return ERTS_IOLIST_TO_BUF_YIELD; + + +#undef LIST_TO_BITSTR_BUF_BCOPY_DBG +#undef LIST_TO_BITSTR_BUF_BCOPY + +} + +static ErlDrvSizeT +list_to_bitstr_buf_yielding(ErtsIOList2BufState *state) +{ + return list_to_bitstr_buf(1, state); +} + +static ErlDrvSizeT +list_to_bitstr_buf_not_yielding(ErtsIOList2BufState *state) +{ + return list_to_bitstr_buf(0, state); } static int -bitstr_list_len(Eterm obj, Uint* num_bytes) +list_to_bitstr_buf_bcopy(ErtsIOList2BufState *state, Eterm obj, int *yield_countp) +{ + int res; + char *buf = state->buf; + char *next_buf; + int offset = state->offset; + int next_offset; +#ifdef DEBUG + ErlDrvSizeT len = state->len; + ErlDrvSizeT next_len; +#endif + byte* bptr; + size_t size; + size_t max_size; + Uint bitoffs; + Uint num_bits; + Uint bitsize; + int yield_count = *yield_countp; + + if (state->bcopy.bptr) { + bptr = state->bcopy.bptr; + size = state->bcopy.size; + bitoffs = state->bcopy.bitoffs; + bitsize = state->bcopy.bitsize; + state->bcopy.bptr = NULL; + } + else { + + ASSERT(is_binary(obj)); + + size = binary_size(obj); + + ASSERT(size <= len); + + ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); + } + + max_size = (size_t) ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; + if (yield_count > 0) + max_size *= (size_t) (yield_count+1); + + if (size <= max_size) { + if (size >= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT) { + int cost = (int) size; + cost /= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; + yield_count -= cost; + } + next_offset = offset + bitsize; + next_buf = buf + size+(next_offset>7); +#ifdef DEBUG + next_len = len - size+(next_offset>7); +#endif + next_offset &= 7; + num_bits = 8*size+bitsize; + res = 0; + } + else { + ASSERT(0 < max_size && max_size < size); + yield_count = 0; + state->bcopy.bptr = bptr + max_size; + state->bcopy.bitoffs = bitoffs; + state->bcopy.bitsize = bitsize; + state->bcopy.size = size - max_size; + next_buf = buf + max_size; +#ifdef DEBUG + next_len = len - max_size; +#endif + next_offset = offset; + num_bits = 8*max_size; + size = max_size; + res = 1; + } + + copy_binary_to_buffer(buf, offset, bptr, bitoffs, num_bits); + + state->offset = next_offset; + state->buf = next_buf; +#ifdef DEBUG + state->len = next_len; +#endif + *yield_countp = yield_count; + + return res; +} + +static int +bitstr_list_len(ErtsIOListState *state) { Eterm* objp; - Uint len = 0; - Uint offs = 0; + Eterm obj; + Uint len, offs; + int res, init_yield_count, yield_count; DECLARE_ESTACK(s); + + if (state->reds_left <= 0) + return ERTS_IOLIST_YIELD; + + len = (Uint) state->size; + offs = state->offs; + obj = state->obj; + + ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); + init_yield_count = ERTS_IOLIST_SIZE_YIELDS_COUNT_PER_RED; + init_yield_count *= state->reds_left; + yield_count = init_yield_count; + if (state->estack.start) { + /* Restart; restore estack... */ + ESTACK_RESTORE(s, &state->estack); + } + goto L_again; #define SAFE_ADD(Var, Val) \ @@ -887,46 +1431,55 @@ bitstr_list_len(Eterm obj, Uint* num_bytes) obj = ESTACK_POP(s); L_again: if (is_list(obj)) { - L_iter_list: - objp = list_val(obj); - /* Head */ - obj = CAR(objp); - if (is_byte(obj)) { - len++; - if (len == 0) { - goto L_overflow_error; + while (1) { /* Tail loop */ + while (1) { /* Head loop */ + if (--yield_count <= 0) + goto L_yield; + objp = list_val(obj); + /* Head */ + obj = CAR(objp); + if (is_byte(obj)) { + len++; + if (len == 0) { + goto L_overflow_error; + } + } else if (is_binary(obj)) { + SAFE_ADD(len, binary_size(obj)); + SAFE_ADD_BITSIZE(offs, obj); + } else if (is_list(obj)) { + ESTACK_PUSH(s, CDR(objp)); + continue; /* Head loop */ + } else if (is_not_nil(obj)) { + goto L_type_error; + } + break; } - } else if (is_binary(obj)) { - SAFE_ADD(len, binary_size(obj)); - SAFE_ADD_BITSIZE(offs, obj); - } else if (is_list(obj)) { - ESTACK_PUSH(s, CDR(objp)); - goto L_iter_list; /* on head */ - } else if (is_not_nil(obj)) { - goto L_type_error; + /* Tail */ + obj = CDR(objp); + if (is_list(obj)) + continue; /* Tail loop */ + else if (is_binary(obj)) { + SAFE_ADD(len, binary_size(obj)); + SAFE_ADD_BITSIZE(offs, obj); + } else if (is_not_nil(obj)) { + goto L_type_error; + } + break; } - /* Tail */ - obj = CDR(objp); - if (is_list(obj)) - goto L_iter_list; /* on tail */ - else if (is_binary(obj)) { + } else { + if (--yield_count <= 0) + goto L_yield; + if (is_binary(obj)) { SAFE_ADD(len, binary_size(obj)); SAFE_ADD_BITSIZE(offs, obj); } else if (is_not_nil(obj)) { goto L_type_error; } - } else if (is_binary(obj)) { - SAFE_ADD(len, binary_size(obj)); - SAFE_ADD_BITSIZE(offs, obj); - } else if (is_not_nil(obj)) { - goto L_type_error; } } #undef SAFE_ADD #undef SAFE_ADD_BITSIZE - DESTROY_ESTACK(s); - /* * Make sure that the number of bits in the bitstring will fit * in an Uint to ensure that the binary can be matched using @@ -939,15 +1492,42 @@ bitstr_list_len(Eterm obj, Uint* num_bytes) if (len << 3 < len) { goto L_overflow_error; } - *num_bytes = len; - return ERTS_IOLIST_OK; + state->size = len; - L_type_error: - DESTROY_ESTACK(s); - return ERTS_IOLIST_TYPE; + res = ERTS_IOLIST_OK; + + L_return: { + int yc = init_yield_count - yield_count; + int reds; + + DESTROY_ESTACK(s); + CLEAR_SAVED_ESTACK(&state->estack); + + reds = (yc - 1)/ERTS_IOLIST_SIZE_YIELDS_COUNT_PER_RED + 1; + BUMP_REDS(state->c_p, reds); + state->reds_left -= reds; + state->size = (ErlDrvSizeT) len; + state->have_size = 1; + return res; + } L_overflow_error: - DESTROY_ESTACK(s); - return ERTS_IOLIST_OVERFLOW; + res = ERTS_IOLIST_OVERFLOW; + len = 0; + goto L_return; + + L_type_error: + res = ERTS_IOLIST_TYPE; + len = 0; + goto L_return; + + L_yield: + BUMP_ALL_REDS(state->c_p); + state->reds_left = 0; + state->size = len; + state->offs = offs; + state->obj = obj; + ESTACK_SAVE(s, &state->estack); + return ERTS_IOLIST_YIELD; } diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index ff775691b3..7e0e825a0d 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -2294,18 +2294,11 @@ BIF_RETTYPE binary_bin_to_list_1(BIF_ALIST_1) BIF_ERROR(BIF_P,BADARG); } -/* - * Ok, erlang:list_to_binary does not interrupt, and we really don't want - * an alternative implementation for the exact same thing, why we - * have descided to use the old non-restarting implementation for now. - * In reality, there are seldom many iterations involved in doing this, so the - * problem of long-running bifs is not really that big in this case. - * So, for now we use the old implementation also in the module binary. - */ +HIPE_WRAPPER_BIF_DISABLE_GC(binary_list_to_bin, 1) BIF_RETTYPE binary_list_to_bin_1(BIF_ALIST_1) { - return erts_list_to_binary_bif(BIF_P, BIF_ARG_1); + return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_binary_list_to_bin_1]); } typedef struct { diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index 819b19e566..6c9f53ce87 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -166,7 +166,7 @@ Eterm erts_bin_bytes_to_list(Eterm previous, Eterm* hp, byte* bytes, Uint size, * Common implementation for erlang:list_to_binary/1 and binary:list_to_bin/1 */ -BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg); +BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg, Export *bif); BIF_RETTYPE erts_gc_binary_part(Process *p, Eterm *reg, Eterm live, int range_is_tuple); BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 8fcb95d0e2..891046a8b5 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -435,6 +435,8 @@ do {\ }\ } while(0) +#define CLEAR_SAVED_ESTACK(estack) ((void) ((estack)->start = NULL)) + /* * Use on empty stack, only the allocator can be changed before this. * The src stack is reset to NULL. @@ -551,6 +553,8 @@ do {\ }\ } while(0) +#define CLEAR_SAVED_WSTACK(wstack) ((void) ((wstack)->wstart = NULL)) + /* * Use on empty stack, only the allocator can be changed before this. * The src stack is reset to NULL. @@ -951,20 +955,67 @@ struct Sint_buf { }; char* Sint_to_buf(Sint, struct Sint_buf*); +#define ERTS_IOLIST_STATE_INITER(C_P, OBJ) \ + {(C_P), 0, 0, (OBJ), {NULL, NULL, NULL, ERTS_ALC_T_INVALID}, 0, 0} + +#define ERTS_IOLIST_STATE_MOVE(TO, FROM) \ + sys_memcpy((void *) (TO), (void *) (FROM), sizeof(ErtsIOListState)) + +#define ERTS_IOLIST_SIZE_YIELDS_COUNT_PER_RED 8 + +typedef struct { + Process *c_p; + ErlDrvSizeT size; + Uint offs; + Eterm obj; + ErtsEStack estack; + int reds_left; + int have_size; +} ErtsIOListState; + +#define ERTS_IOLIST2BUF_STATE_INITER(C_P, OBJ) \ + {ERTS_IOLIST_STATE_INITER((C_P), (OBJ)), {NULL, 0, 0, 0}, NULL, 0, NULL, 0} + +#define ERTS_IOLIST2BUF_STATE_MOVE(TO, FROM) \ + sys_memcpy((void *) (TO), (void *) (FROM), sizeof(ErtsIOList2BufState)) + +#define ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT 32 +#define ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED 8 +#define ERTS_IOLIST_TO_BUF_BYTES_PER_RED \ + (ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED*ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT) + +typedef struct { + ErtsIOListState iolist; + struct { + byte *bptr; + size_t size; + Uint bitoffs; + Uint bitsize; + } bcopy; + char *buf; + ErlDrvSizeT len; + Eterm *objp; + int offset; +} ErtsIOList2BufState; + #define ERTS_IOLIST_OK 0 #define ERTS_IOLIST_OVERFLOW 1 #define ERTS_IOLIST_TYPE 2 +#define ERTS_IOLIST_YIELD 3 Eterm buf_to_intlist(Eterm**, const char*, size_t, Eterm); /* most callers pass plain char*'s */ #define ERTS_IOLIST_TO_BUF_OVERFLOW (~((ErlDrvSizeT) 0)) #define ERTS_IOLIST_TO_BUF_TYPE_ERROR (~((ErlDrvSizeT) 1)) +#define ERTS_IOLIST_TO_BUF_YIELD (~((ErlDrvSizeT) 2)) #define ERTS_IOLIST_TO_BUF_FAILED(R) \ - (((R) & (~((ErlDrvSizeT) 1))) == (~((ErlDrvSizeT) 1))) + (((R) & (~((ErlDrvSizeT) 3))) == (~((ErlDrvSizeT) 3))) #define ERTS_IOLIST_TO_BUF_SUCCEEDED(R) \ (!ERTS_IOLIST_TO_BUF_FAILED((R))) ErlDrvSizeT erts_iolist_to_buf(Eterm, char*, ErlDrvSizeT); +ErlDrvSizeT erts_iolist_to_buf_yielding(ErtsIOList2BufState *); +int erts_iolist_size_yielding(ErtsIOListState *state); int erts_iolist_size(Eterm, ErlDrvSizeT *); int is_string(Eterm); void erl_at_exit(void (*) (void*), void*); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 738f793020..72092ec7b0 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3197,106 +3197,303 @@ buf_to_intlist(Eterm** hpp, const char *buf, size_t len, Eterm tail) ** */ -ErlDrvSizeT erts_iolist_to_buf(Eterm obj, char* buf, ErlDrvSizeT alloced_len) +typedef enum { + ERTS_IL2B_BCOPY_OK, + ERTS_IL2B_BCOPY_YIELD, + ERTS_IL2B_BCOPY_OVERFLOW, + ERTS_IL2B_BCOPY_TYPE_ERROR +} ErtsIL2BBCopyRes; + +static ErtsIL2BBCopyRes +iolist_to_buf_bcopy(ErtsIOList2BufState *state, Eterm obj, int *yield_countp); + +static ERTS_INLINE ErlDrvSizeT +iolist_to_buf(const int yield_support, + ErtsIOList2BufState *state, + Eterm obj, + char* buf, + ErlDrvSizeT alloced_len) { - ErlDrvSizeT len = (ErlDrvSizeT) alloced_len; - Eterm* objp; +#undef IOLIST_TO_BUF_BCOPY +#define IOLIST_TO_BUF_BCOPY(CONSP) \ +do { \ + size_t size = binary_size(obj); \ + if (size > 0) { \ + Uint bitsize; \ + byte* bptr; \ + Uint bitoffs; \ + Uint num_bits; \ + if (yield_support) { \ + size_t max_size = ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; \ + if (yield_count > 0) \ + max_size *= yield_count+1; \ + if (size > max_size) { \ + state->objp = CONSP; \ + goto L_bcopy_yield; \ + } \ + if (size >= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT) { \ + int cost = (int) size; \ + cost /= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; \ + yield_count -= cost; \ + } \ + } \ + if (len < size) \ + goto L_overflow; \ + ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); \ + if (bitsize != 0) \ + goto L_type_error; \ + num_bits = 8*size; \ + copy_binary_to_buffer(buf, 0, bptr, bitoffs, num_bits); \ + buf += size; \ + len -= size; \ + } \ +} while (0) + + ErlDrvSizeT res, len; + Eterm* objp = NULL; + int init_yield_count; + int yield_count; DECLARE_ESTACK(s); - goto L_again; - - while (!ESTACK_ISEMPTY(s)) { - obj = ESTACK_POP(s); - L_again: - if (is_list(obj)) { - L_iter_list: - objp = list_val(obj); - obj = CAR(objp); - if (is_byte(obj)) { - if (len == 0) { - goto L_overflow; - } - *buf++ = unsigned_val(obj); - len--; - } else if (is_binary(obj)) { - byte* bptr; - size_t size = binary_size(obj); - Uint bitsize; - Uint bitoffs; - Uint num_bits; - - if (len < size) { + + len = (ErlDrvSizeT) alloced_len; + + if (!yield_support) { + yield_count = init_yield_count = 0; /* Shut up faulty warning... >:-( */ + goto L_again; + } + else { + + if (state->iolist.reds_left <= 0) + return ERTS_IOLIST_TO_BUF_YIELD; + + ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); + init_yield_count = (ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED + * state->iolist.reds_left); + yield_count = init_yield_count; + + if (!state->iolist.estack.start) + goto L_again; + else { + int chk_stack; + /* Restart; restore state... */ + ESTACK_RESTORE(s, &state->iolist.estack); + + if (!state->bcopy.bptr) + chk_stack = 0; + else { + chk_stack = 1; + switch (iolist_to_buf_bcopy(state, THE_NON_VALUE, &yield_count)) { + case ERTS_IL2B_BCOPY_OK: + break; + case ERTS_IL2B_BCOPY_YIELD: + BUMP_ALL_REDS(state->iolist.c_p); + state->iolist.reds_left = 0; + ESTACK_SAVE(s, &state->iolist.estack); + return ERTS_IOLIST_TO_BUF_YIELD; + case ERTS_IL2B_BCOPY_OVERFLOW: goto L_overflow; - } - ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); - if (bitsize != 0) { + case ERTS_IL2B_BCOPY_TYPE_ERROR: goto L_type_error; } - num_bits = 8*size; - copy_binary_to_buffer(buf, 0, bptr, bitoffs, num_bits); - buf += size; - len -= size; - } else if (is_list(obj)) { - ESTACK_PUSH(s, CDR(objp)); - goto L_iter_list; /* on head */ - } else if (is_not_nil(obj)) { - goto L_type_error; } - obj = CDR(objp); - if (is_list(obj)) { - goto L_iter_list; /* on tail */ - } else if (is_binary(obj)) { - byte* bptr; - size_t size = binary_size(obj); - Uint bitsize; - Uint bitoffs; - Uint num_bits; - if (len < size) { - goto L_overflow; + obj = state->iolist.obj; + buf = state->buf; + len = state->len; + objp = state->objp; + state->objp = NULL; + if (objp) + goto L_tail; + if (!chk_stack) + goto L_again; + /* check stack */ + } + } + + while (!ESTACK_ISEMPTY(s)) { + obj = ESTACK_POP(s); + L_again: + if (is_list(obj)) { + while (1) { /* Tail loop */ + while (1) { /* Head loop */ + if (yield_support && --yield_count <= 0) + goto L_yield; + objp = list_val(obj); + obj = CAR(objp); + if (is_byte(obj)) { + if (len == 0) { + goto L_overflow; + } + *buf++ = unsigned_val(obj); + len--; + } else if (is_binary(obj)) { + IOLIST_TO_BUF_BCOPY(objp); + } else if (is_list(obj)) { + ESTACK_PUSH(s, CDR(objp)); + continue; /* Head loop */ + } else if (is_not_nil(obj)) { + goto L_type_error; + } + break; } - ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); - if (bitsize != 0) { + + L_tail: + + obj = CDR(objp); + + if (is_list(obj)) { + continue; /* Tail loop */ + } else if (is_binary(obj)) { + IOLIST_TO_BUF_BCOPY(NULL); + } else if (is_not_nil(obj)) { goto L_type_error; } - num_bits = 8*size; - copy_binary_to_buffer(buf, 0, bptr, bitoffs, num_bits); - buf += size; - len -= size; - } else if (is_not_nil(obj)) { - goto L_type_error; + break; } } else if (is_binary(obj)) { - byte* bptr; - size_t size = binary_size(obj); - Uint bitsize; - Uint bitoffs; - Uint num_bits; - if (len < size) { - goto L_overflow; - } - ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); - if (bitsize != 0) { - goto L_type_error; - } - num_bits = 8*size; - copy_binary_to_buffer(buf, 0, bptr, bitoffs, num_bits); - buf += size; - len -= size; + IOLIST_TO_BUF_BCOPY(NULL); } else if (is_not_nil(obj)) { goto L_type_error; - } + } else if (yield_support && --yield_count <= 0) + goto L_yield; } + res = len; + + L_return: + DESTROY_ESTACK(s); - return len; + + if (yield_support) { + int reds; + CLEAR_SAVED_ESTACK(&state->iolist.estack); + reds = ((init_yield_count - yield_count - 1) + / ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED) + 1; + BUMP_REDS(state->iolist.c_p, reds); + state->iolist.reds_left -= reds; + if (state->iolist.reds_left < 0) + state->iolist.reds_left = 0; + } + + + return res; L_type_error: - DESTROY_ESTACK(s); - return ERTS_IOLIST_TO_BUF_TYPE_ERROR; + res = ERTS_IOLIST_TO_BUF_TYPE_ERROR; + goto L_return; L_overflow: - DESTROY_ESTACK(s); - return ERTS_IOLIST_TO_BUF_OVERFLOW; + res = ERTS_IOLIST_TO_BUF_OVERFLOW; + goto L_return; + + L_bcopy_yield: + + state->buf = buf; + state->len = len; + + switch (iolist_to_buf_bcopy(state, obj, &yield_count)) { + case ERTS_IL2B_BCOPY_OK: + ERTS_INTERNAL_ERROR("Missing yield"); + case ERTS_IL2B_BCOPY_YIELD: + BUMP_ALL_REDS(state->iolist.c_p); + state->iolist.reds_left = 0; + ESTACK_SAVE(s, &state->iolist.estack); + return ERTS_IOLIST_TO_BUF_YIELD; + case ERTS_IL2B_BCOPY_OVERFLOW: + goto L_overflow; + case ERTS_IL2B_BCOPY_TYPE_ERROR: + goto L_type_error; + } + + L_yield: + + BUMP_ALL_REDS(state->iolist.c_p); + state->iolist.reds_left = 0; + state->iolist.obj = obj; + state->buf = buf; + state->len = len; + ESTACK_SAVE(s, &state->iolist.estack); + return ERTS_IOLIST_TO_BUF_YIELD; + +#undef IOLIST_TO_BUF_BCOPY +} + +static ErtsIL2BBCopyRes +iolist_to_buf_bcopy(ErtsIOList2BufState *state, Eterm obj, int *yield_countp) +{ + ErtsIL2BBCopyRes res; + char *buf = state->buf; + ErlDrvSizeT len = state->len; + byte* bptr; + size_t size; + size_t max_size; + Uint bitoffs; + Uint num_bits; + int yield_count = *yield_countp; + + if (state->bcopy.bptr) { + bptr = state->bcopy.bptr; + size = state->bcopy.size; + bitoffs = state->bcopy.bitoffs; + state->bcopy.bptr = NULL; + } + else { + Uint bitsize; + + ASSERT(is_binary(obj)); + + size = binary_size(obj); + if (size <= 0) + return ERTS_IL2B_BCOPY_OK; + + if (len < size) + return ERTS_IL2B_BCOPY_OVERFLOW; + + ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); + if (bitsize != 0) + return ERTS_IL2B_BCOPY_TYPE_ERROR; + } + + ASSERT(size > 0); + max_size = (size_t) ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; + if (yield_count > 0) + max_size *= (size_t) (yield_count+1); + + if (size <= max_size) { + if (size >= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT) { + int cost = (int) size; + cost /= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; + yield_count -= cost; + } + res = ERTS_IL2B_BCOPY_OK; + } + else { + ASSERT(0 < max_size && max_size < size); + yield_count = 0; + state->bcopy.bptr = bptr + max_size; + state->bcopy.bitoffs = bitoffs; + state->bcopy.size = size - max_size; + size = max_size; + res = ERTS_IL2B_BCOPY_YIELD; + } + + num_bits = 8*size; + copy_binary_to_buffer(buf, 0, bptr, bitoffs, num_bits); + state->buf += size; + state->len -= size; + *yield_countp = yield_count; + + return res; +} + +ErlDrvSizeT erts_iolist_to_buf_yielding(ErtsIOList2BufState *state) +{ + return iolist_to_buf(1, state, state->iolist.obj, state->buf, state->len); +} + +ErlDrvSizeT erts_iolist_to_buf(Eterm obj, char* buf, ErlDrvSizeT alloced_len) +{ + return iolist_to_buf(0, NULL, obj, buf, alloced_len); } /* @@ -3307,11 +3504,32 @@ ErlDrvSizeT erts_iolist_to_buf(Eterm obj, char* buf, ErlDrvSizeT alloced_len) * Any input term error detected in erts_iolist_to_buf should also * be detected in this function! */ -int erts_iolist_size(Eterm obj, ErlDrvSizeT* sizep) + +static ERTS_INLINE int +iolist_size(const int yield_support, ErtsIOListState *state, Eterm obj, ErlDrvSizeT* sizep) { + int res, init_yield_count, yield_count; Eterm* objp; - Uint size = 0; /* Intentionally Uint due to halfword heap */ + Uint size = (Uint) *sizep; /* Intentionally Uint due to halfword heap */ DECLARE_ESTACK(s); + + if (!yield_support) + yield_count = init_yield_count = 0; /* Shut up faulty warning... >:-( */ + else { + if (state->reds_left <= 0) + return ERTS_IOLIST_YIELD; + ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); + init_yield_count = ERTS_IOLIST_SIZE_YIELDS_COUNT_PER_RED; + init_yield_count *= state->reds_left; + yield_count = init_yield_count; + if (state->estack.start) { + /* Restart; restore state... */ + ESTACK_RESTORE(s, &state->estack); + size = (Uint) state->size; + obj = state->obj; + } + } + goto L_again; #define SAFE_ADD(Var, Val) \ @@ -3327,51 +3545,101 @@ int erts_iolist_size(Eterm obj, ErlDrvSizeT* sizep) obj = ESTACK_POP(s); L_again: if (is_list(obj)) { - L_iter_list: - objp = list_val(obj); - /* Head */ - obj = CAR(objp); - if (is_byte(obj)) { - size++; - if (size == 0) { - goto L_overflow_error; + while (1) { /* Tail loop */ + while (1) { /* Head loop */ + if (yield_support && --yield_count <= 0) + goto L_yield; + objp = list_val(obj); + /* Head */ + obj = CAR(objp); + if (is_byte(obj)) { + size++; + if (size == 0) { + goto L_overflow_error; + } + } else if (is_binary(obj) && binary_bitsize(obj) == 0) { + SAFE_ADD(size, binary_size(obj)); + } else if (is_list(obj)) { + ESTACK_PUSH(s, CDR(objp)); + continue; /* Head loop */ + } else if (is_not_nil(obj)) { + goto L_type_error; + } + break; } - } else if (is_binary(obj) && binary_bitsize(obj) == 0) { - SAFE_ADD(size, binary_size(obj)); - } else if (is_list(obj)) { - ESTACK_PUSH(s, CDR(objp)); - goto L_iter_list; /* on head */ - } else if (is_not_nil(obj)) { - goto L_type_error; + /* Tail */ + obj = CDR(objp); + if (is_list(obj)) + continue; /* Tail loop */ + else if (is_binary(obj) && binary_bitsize(obj) == 0) { + SAFE_ADD(size, binary_size(obj)); + } else if (is_not_nil(obj)) { + goto L_type_error; + } + break; } - /* Tail */ - obj = CDR(objp); - if (is_list(obj)) - goto L_iter_list; /* on tail */ - else if (is_binary(obj) && binary_bitsize(obj) == 0) { + } else { + if (yield_support && --yield_count <= 0) + goto L_yield; + if (is_binary(obj) && binary_bitsize(obj) == 0) { /* Tail was binary */ SAFE_ADD(size, binary_size(obj)); } else if (is_not_nil(obj)) { goto L_type_error; } - } else if (is_binary(obj) && binary_bitsize(obj) == 0) { /* Tail was binary */ - SAFE_ADD(size, binary_size(obj)); - } else if (is_not_nil(obj)) { - goto L_type_error; } } #undef SAFE_ADD - DESTROY_ESTACK(s); *sizep = (ErlDrvSizeT) size; - return ERTS_IOLIST_OK; - L_overflow_error: + res = ERTS_IOLIST_OK; + + L_return: + DESTROY_ESTACK(s); - return ERTS_IOLIST_OVERFLOW; + + if (yield_support) { + int yc, reds; + CLEAR_SAVED_ESTACK(&state->estack); + yc = init_yield_count - yield_count; + reds = ((yc - 1) / ERTS_IOLIST_SIZE_YIELDS_COUNT_PER_RED) + 1; + BUMP_REDS(state->c_p, reds); + state->reds_left -= reds; + state->size = (ErlDrvSizeT) size; + state->have_size = 1; + } + + return res; + + L_overflow_error: + res = ERTS_IOLIST_OVERFLOW; + size = 0; + goto L_return; L_type_error: - DESTROY_ESTACK(s); - return ERTS_IOLIST_TYPE; + res = ERTS_IOLIST_TYPE; + size = 0; + goto L_return; + + L_yield: + BUMP_ALL_REDS(state->c_p); + state->reds_left = 0; + state->size = size; + state->obj = obj; + ESTACK_SAVE(s, &state->estack); + return ERTS_IOLIST_YIELD; +} + +int erts_iolist_size_yielding(ErtsIOListState *state) +{ + ErlDrvSizeT size = state->size; + return iolist_size(1, state, state->obj, &size); +} + +int erts_iolist_size(Eterm obj, ErlDrvSizeT* sizep) +{ + *sizep = 0; + return iolist_size(0, NULL, obj, sizep); } /* return 0 if item is not a non-empty flat list of bytes */ diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index e3563af61e..16765fdb99 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -273,7 +273,11 @@ ifelse($1,erts_internal_binary_to_term_2,hipe_wrapper_erts_internal_binary_to_te ifelse($1,binary_to_list_1,hipe_wrapper_binary_to_list_1, ifelse($1,binary_to_list_3,hipe_wrapper_binary_to_list_3, ifelse($1,bitstring_to_list_1,hipe_wrapper_bitstring_to_list_1, -$1)))))))') +ifelse($1,list_to_binary_1,hipe_wrapper_list_to_binary_1, +ifelse($1,iolist_to_binary_1,hipe_wrapper_iolist_to_binary_1, +ifelse($1,binary_list_to_bin_1,hipe_wrapper_binary_list_to_bin_1, +ifelse($1,list_to_bitstring_1,hipe_wrapper_list_to_bitstring_1, +$1)))))))))))') define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))') include(TARGET/`erl_bif_list.h') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index fe34e60c27..04f3213ede 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -58,7 +58,8 @@ ordering/1,unaligned_order/1,gc_test/1, bit_sized_binary_sizes/1, otp_6817/1,deep/1,obsolete_funs/1,robustness/1,otp_8117/1, - otp_8180/1, trapping/1, large/1]). + otp_8180/1, trapping/1, large/1, + error_after_yield/1]). %% Internal exports. -export([sleeper/0,trapping_loop/4]). @@ -76,7 +77,8 @@ all() -> bad_term_to_binary, more_bad_terms, otp_5484, otp_5933, ordering, unaligned_order, gc_test, bit_sized_binary_sizes, otp_6817, otp_8117, deep, - obsolete_funs, robustness, otp_8180, trapping, large]. + obsolete_funs, robustness, otp_8180, trapping, large, + error_after_yield]. groups() -> []. @@ -1354,8 +1356,12 @@ trapping(Config) when is_list(Config)-> fun() -> [term_to_binary(lists:duplicate(2000000,2000000))] end), do_trapping(5, binary_to_list, fun() -> [list_to_binary(lists:duplicate(2000000,$x))] end), + do_trapping(5, list_to_binary, + fun() -> [lists:duplicate(2000000,$x)] end), do_trapping(5, bitstring_to_list, - fun() -> [list_to_bitstring([lists:duplicate(2000000,$x),<<7:4>>])] end) + fun() -> [list_to_bitstring([lists:duplicate(2000000,$x),<<7:4>>])] end), + do_trapping(5, list_to_bitstring, + fun() -> [[lists:duplicate(2000000,$x),<<7:4>>]] end) . do_trapping(0, _, _) -> @@ -1400,10 +1406,98 @@ large(Config) when is_list(Config) -> PartList = binary_to_list(Bin, 3, length(List)-2), ListBS = List ++ [<<7:4>>], ListBS = bitstring_to_list(list_to_bitstring(ListBS)), + BitStr1 = list_to_bitstring(lists:duplicate(1024*1024, [<<1,5:3>>])), + BitStr1 = list_to_bitstring(bitstring_to_list(BitStr1)), + BitStr2 = list_to_bitstring([lists:duplicate(512*1024, [<<1,5:3>>]), + Bin]), + BitStr2 = list_to_bitstring(bitstring_to_list(BitStr2)), ok. +error_after_yield(Config) when is_list(Config) -> + L2BTrap = {erts_internal, list_to_binary_continue, 1}, + error_after_yield(badarg, erlang, list_to_binary, 1, fun () -> [[mk_list(1000000), oops]] end, L2BTrap), + error_after_yield(badarg, erlang, iolist_to_binary, 1, fun () -> [[list2iolist(mk_list(1000000)), oops]] end, L2BTrap), + error_after_yield(badarg, erlang, list_to_bitstring, 1, fun () -> [[list2bitstrlist(mk_list(1000000)), oops]] end, L2BTrap), + error_after_yield(badarg, binary, list_to_bin, 1, fun () -> [[mk_list(1000000), oops]] end, L2BTrap), + + case erlang:system_info(wordsize) of + 4 -> + SysLimitSz = 1 bsl 32, + error_after_yield(system_limit, erlang, list_to_binary, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap), + error_after_yield(system_limit, erlang, iolist_to_binary, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap), + error_after_yield(system_limit, erlang, list_to_bitstring, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap), + error_after_yield(system_limit, binary, list_to_bin, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap); + 8 -> + % Takes waaaay to long time to test system_limit on 64-bit archs... + ok + end, + ok. + +error_after_yield(Type, M, F, AN, AFun, TrapFunc) -> + io:format("Testing ~p for ~p:~p/~p~n", [Type, M, F, AN]), + Tracer = self(), + {Pid, Mon} = spawn_monitor(fun () -> + A = AFun(), + try + erlang:yield(), + erlang:trace(self(),true,[running,{tracer,Tracer}]), + apply(M, F, A), + exit({unexpected_success, {M, F, A}}) + catch + error:Type -> + erlang:trace(self(),false,[running,{tracer,Tracer}]), + %% We threw the exception from the native + %% function we trapped to, but we want + %% the BIF that originally was called + %% to appear in the stack trace. + [{M, F, A, _} | _] = erlang:get_stacktrace() + end + end), + receive + {'DOWN', Mon, process, Pid, Reason} -> + normal = Reason + end, + TD = erlang:trace_delivered(Pid), + receive + {trace_delivered, Pid, TD} -> + NoYields = error_after_yield_sched(Pid, TrapFunc, 0), + io:format("No of yields: ~p~n", [NoYields]), + true = NoYields > 10 + end, + ok. + +error_after_yield_sched(P, TrapFunc, N) -> + receive + {trace, P, out, TrapFunc} -> + receive + {trace, P, in, TrapFunc} -> + error_after_yield_sched(P, TrapFunc, N+1) + after 0 -> + exit(trap_sched_mismatch) + end; + {trace, P, out, Func} -> + receive + {trace, P, in, Func} -> + error_after_yield_sched(P, TrapFunc, N) + after 0 -> + exit(other_sched_mismatch) + end + after 0 -> + N + end. + + %% Utilities. +huge_iolist(Lim) -> + Sz = 1024, + huge_iolist(list_to_binary(mk_list(Sz)), Sz, Lim). + +huge_iolist(X, Sz, Lim) when Sz >= Lim -> + X; +huge_iolist(X, Sz, Lim) -> + huge_iolist([X, X], Sz*2, Lim). + make_sub_binary(Bin) when is_binary(Bin) -> {_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3), B; @@ -1484,3 +1578,78 @@ get_reds() -> erts_debug:set_internal_state(available_internal_state, true), get_reds() end. + +-define(LARGE_BIN, (512*1024+10)). +-define(LARGE_BIN_LIM, (1024*1024)). + +mk_list(0, Acc) -> + Acc; +mk_list(Sz, Acc) -> + mk_list(Sz-1, [$A+(Sz band 63) | Acc]). + +mk_list(Sz) when Sz >= ?LARGE_BIN_LIM -> + SzLeft = Sz - ?LARGE_BIN, + SzHd = SzLeft div 2, + SzTl = SzLeft - SzHd, + [mk_list(SzHd, []), erlang:list_to_binary(mk_list(?LARGE_BIN, [])), mk_list(SzTl, [])]; +mk_list(Sz) -> + mk_list(Sz, []). + +mk_list_lb(Sz) when Sz >= ?LARGE_BIN_LIM -> + SzLeft = Sz - ?LARGE_BIN, + SzHd = SzLeft div 2, + SzTl = SzLeft - SzHd, + [mk_list(SzHd, []), erlang:list_to_binary(mk_list(?LARGE_BIN, [])), mk_list(SzTl, [])]; +mk_list_lb(Sz) -> + mk_list(Sz, []). + + +list2iolist(List) -> + list2iolist(List, []). + +list2iolist([], Acc) -> + Acc; +list2iolist([X0, X1, X2, X3, X4, X5 | Xs], Acc) when is_integer(X0), 0 =< X0, X0 < 256, + is_integer(X1), 0 =< X1, X1 < 256, + is_integer(X2), 0 =< X2, X2 < 256, + is_integer(X3), 0 =< X3, X3 < 256, + is_integer(X4), 0 =< X4, X4 < 256, + is_integer(X5), 0 =< X5, X5 < 256 -> + NewAcc = case (X0+X1+X2+X3+X4+X5) band 3 of + 0 -> + [Acc, [[[[[[[[[[[[X0,[],<<"">>,X1]]]]]]]]],[X2,X3]],[],[],[],[],X4],X5]]; + 1 -> + [Acc, [], erlang:list_to_binary([X0, X1, X2, X3, X4, X5])]; + 2 -> + [Acc, [[[[X0|erlang:list_to_binary([X1])],[X2|erlang:list_to_binary([X3])],[X4|erlang:list_to_binary([X5])]]]|<<"">>]]; + 3 -> + [Acc, X0, X1, X2, <<"">>, [], X3, X4 | erlang:list_to_binary([X5])] + end, + list2iolist(Xs, NewAcc); +list2iolist([X | Xs], Acc) -> + list2iolist(Xs, [Acc,X]). + +list2bitstrlist(List) -> + [list2bitstrlist(List, []), <<4:7>>]. + +list2bitstrlist([], Acc) -> + Acc; +list2bitstrlist([X0, X1, X2, X3, X4, X5 | Xs], Acc) when is_integer(X0), 0 =< X0, X0 < 256, + is_integer(X1), 0 =< X1, X1 < 256, + is_integer(X2), 0 =< X2, X2 < 256, + is_integer(X3), 0 =< X3, X3 < 256, + is_integer(X4), 0 =< X4, X4 < 256, + is_integer(X5), 0 =< X5, X5 < 256 -> + NewAcc = case (X0+X1+X2+X3+X4+X5) band 3 of + 0 -> + [Acc, [[[[[[[[[[[[X0,[],<<"">>,X1]]]]]]]]],[X2,X3]],[],[],[],[],X4],X5]]; + 1 -> + [Acc, [], <>, <>, <>]; + 2 -> + [Acc, [[[[X0|<>],X3]],[X4|erlang:list_to_binary([X5])]|<<"">>]]; + 3 -> + [Acc, X0, X1, X2, <<"">>, [], X3, X4 | erlang:list_to_binary([X5])] + end, + list2bitstrlist(Xs, NewAcc); +list2bitstrlist([X | Xs], Acc) -> + list2bitstrlist(Xs, [Acc,X]). -- cgit v1.2.3 From efa69184de378b99b1d222c5a0b1c07d9b5a39aa Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 12 May 2014 16:21:15 +0200 Subject: Add test-case comparing old and new implementations --- erts/emulator/test/binary_SUITE.erl | 64 +++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 04f3213ede..22c4ed29ad 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -59,7 +59,7 @@ bit_sized_binary_sizes/1, otp_6817/1,deep/1,obsolete_funs/1,robustness/1,otp_8117/1, otp_8180/1, trapping/1, large/1, - error_after_yield/1]). + error_after_yield/1, cmp_old_impl/1]). %% Internal exports. -export([sleeper/0,trapping_loop/4]). @@ -78,7 +78,7 @@ all() -> ordering, unaligned_order, gc_test, bit_sized_binary_sizes, otp_6817, otp_8117, deep, obsolete_funs, robustness, otp_8180, trapping, large, - error_after_yield]. + error_after_yield, cmp_old_impl]. groups() -> []. @@ -1486,6 +1486,61 @@ error_after_yield_sched(P, TrapFunc, N) -> N end. +cmp_old_impl(Config) when is_list(Config) -> + %% Compare results from new yielding implementations with + %% old non yielding implementations + Cookie = atom_to_list(erlang:get_cookie()), + Rel = "r16b_latest", + case test_server:is_release_available(Rel) of + false -> + {skipped, "No "++Rel++" available"}; + true -> + {ok, Node} = ?t:start_node(list_to_atom(atom_to_list(?MODULE)++"_"++Rel), + peer, + [{args, " -setcookie "++Cookie}, + {erl, [{release, Rel}]}]), + + cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(1))]}), + cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(10))]}), + cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(100))]}), + cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(1000))]}), + cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(10000))]}), + cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(100000))]}), + cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(1000000))]}), + cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(10000000))]}), + cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list_lb(10000000))]}), + + cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(1))]}), + cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(10))]}), + cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(100))]}), + cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(1000))]}), + cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(10000))]}), + cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(100000))]}), + cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(1000000))]}), + cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(10000000))]}), + + cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(1))]}), + cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(10))]}), + cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(100))]}), + cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(1000))]}), + cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(10000))]}), + cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(100000))]}), + cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(1000000))]}), + cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(10000000))]}), + + cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(1)))]}), + cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(10)))]}), + cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(100)))]}), + cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(1000)))]}), + cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(10000)))]}), + cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(100000)))]}), + cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(1000000)))]}), + cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(10000000)))]}), + + ?t:stop_node(Node), + + ok + end. %% Utilities. @@ -1498,6 +1553,11 @@ huge_iolist(X, Sz, Lim) when Sz >= Lim -> huge_iolist(X, Sz, Lim) -> huge_iolist([X, X], Sz*2, Lim). +cmp_node(Node, {M, F, A}) -> + Res = rpc:call(Node, M, F, A), + Res = apply(M, F, A), + ok. + make_sub_binary(Bin) when is_binary(Bin) -> {_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3), B; -- cgit v1.2.3 From a933cda32fd2dbae4e1367f98206bebfea39b86b Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sun, 25 May 2014 14:26:08 +0200 Subject: Fix permissions of some files in the repository These files aren't supposed to be executable. For reference, the command used to find them was: git ls-files -z | xargs -0 -J % find % -type f -perm ++x --- erts/emulator/beam/erl_bif_info.c | 0 erts/emulator/beam/global.h | 0 erts/emulator/sys/win32/sys.c | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 erts/emulator/beam/erl_bif_info.c mode change 100755 => 100644 erts/emulator/beam/global.h mode change 100755 => 100644 erts/emulator/sys/win32/sys.c (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c old mode 100755 new mode 100644 diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h old mode 100755 new mode 100644 diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c old mode 100755 new mode 100644 -- cgit v1.2.3 From 918d786cc3a6381a623bc1d2c09249a170110835 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 28 May 2014 15:18:06 +0200 Subject: erts: Fix bug in gdb function etp-cp-1 --- erts/etc/unix/etp-commands.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index ae1b1734af..19c92681d0 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -1065,7 +1065,9 @@ define etp-cp-1 set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2 end if $etp_cp_p - set $etp_cp_low = (Eterm**)($etp_cp_p->start + 8) + # 12 = MI_FUNCTIONS + set $etp_cp_low = (Eterm**)($etp_cp_p->start + 12) + # 0 = MI_NUM_FUNCTIONS set $etp_cp_high = $etp_cp_low +$etp_cp_p->start[0] set $etp_cp_p = 0 while $etp_cp_low < $etp_cp_high -- cgit v1.2.3 From 7adc2c36b0e839384baac50be35c0999d1e546df Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sun, 25 May 2014 16:06:17 +0200 Subject: Implement ets:take/2 This new ETS BIF returns and deletes objects from tables. --- erts/emulator/beam/bif.tab | 2 + erts/emulator/beam/erl_db.c | 25 ++++++++++++ erts/emulator/beam/erl_db_hash.c | 83 ++++++++++++++++++++++++++++++++-------- erts/emulator/beam/erl_db_tree.c | 24 ++++++++++++ erts/emulator/beam/erl_db_util.h | 1 + 5 files changed, 120 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index fbdddf09db..fe25b2d27c 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -596,6 +596,8 @@ bif maps:values/1 bif erts_internal:cmp_term/2 +bif ets:take/2 + # # Obsolete # diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 8f246ffa07..fba1e32230 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -753,6 +753,31 @@ BIF_RETTYPE ets_prev_2(BIF_ALIST_2) BIF_RET(ret); } +/* +** take(Tab, Key) +*/ +BIF_RETTYPE ets_take_2(BIF_ALIST_2) +{ + DbTable* tb; +#ifdef DEBUG + int cret; +#endif + Eterm ret; + CHECK_TABLES(); + + tb = db_get_table(BIF_P, BIF_ARG_1, DB_WRITE, LCK_WRITE_REC); + if (!tb) { + BIF_ERROR(BIF_P, BADARG); + } +#ifdef DEBUG + cret = +#endif + tb->common.meth->db_take(BIF_P, tb, BIF_ARG_2, &ret); + ASSERT(cret == DB_ERROR_NONE); + db_unlock(tb, LCK_WRITE_REC); + BIF_RET(ret); +} + /* ** update_element(Tab, Key, {Pos, Value}) ** update_element(Tab, Key, [{Pos, Value}]) diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 06dac8f161..633a2272ad 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -426,6 +426,7 @@ static int db_select_count_continue_hash(Process *p, DbTable *tbl, static int db_select_delete_continue_hash(Process *p, DbTable *tbl, Eterm continuation, Eterm *ret); +static int db_take_hash(Process *, DbTable *, Eterm, Eterm *); static void db_print_hash(int to, void *to_arg, int show, @@ -536,6 +537,7 @@ DbTableMethod db_hash = db_select_delete_continue_hash, db_select_count_hash, db_select_count_continue_hash, + db_take_hash, db_delete_all_objects_hash, db_free_table_hash, db_free_table_continue_hash, @@ -879,34 +881,45 @@ Ldone: return ret; } +static Eterm +get_term_list(Process *p, DbTableHash *tb, Eterm key, HashValue hval, + HashDbTerm *b1, HashDbTerm **bend) +{ + HashDbTerm* b2 = b1->next; + Eterm copy; + + if (tb->common.status & (DB_BAG | DB_DUPLICATE_BAG)) { + while (b2 && has_key(tb, b2, key, hval)) { + b2 = b2->next; + } + } + copy = build_term_list(p, b1, b2, tb); + CHECK_TABLES(); + if (bend) { + *bend = b2; + } + return copy; +} + int db_get_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret) { DbTableHash *tb = &tbl->hash; HashValue hval; int ix; - HashDbTerm* b1; + HashDbTerm* b; erts_smp_rwmtx_t* lck; hval = MAKE_HASH(key); lck = RLOCK_HASH(tb,hval); ix = hash_to_ix(tb, hval); - b1 = BUCKET(tb, ix); + b = BUCKET(tb, ix); - while(b1 != 0) { - if (has_live_key(tb,b1,key,hval)) { - HashDbTerm* b2 = b1->next; - Eterm copy; - - if (tb->common.status & (DB_BAG | DB_DUPLICATE_BAG)) { - while(b2 != NULL && has_key(tb,b2,key,hval)) - b2 = b2->next; - } - copy = build_term_list(p, b1, b2, tb); - CHECK_TABLES(); - *ret = copy; + while(b != 0) { + if (has_live_key(tb, b, key, hval)) { + *ret = get_term_list(p, tb, key, hval, b, NULL); goto done; } - b1 = b1->next; + b = b->next; } *ret = NIL; done: @@ -2069,6 +2082,46 @@ trap: } +static int db_take_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret) +{ + DbTableHash *tb = &tbl->hash; + HashDbTerm **bp, *b; + HashValue hval = MAKE_HASH(key); + erts_smp_rwmtx_t *lck = WLOCK_HASH(tb, hval); + int ix = hash_to_ix(tb, hval); + int nitems_diff = 0; + + *ret = NIL; + for (bp = &BUCKET(tb, ix), b = *bp; b; bp = &b->next, b = b->next) { + if (has_live_key(tb, b, key, hval)) { + HashDbTerm *bend; + + *ret = get_term_list(p, tb, key, hval, b, &bend); + while (b != bend) { + --nitems_diff; + if (nitems_diff == -1 && IS_FIXED(tb)) { + /* Pseudo remove (no need to keep several of same key) */ + add_fixed_deletion(tb, ix); + bp = &b->next; + b->hvalue = INVALID_HASH; + b = b->next; + } else { + *bp = b->next; + free_term(tb, b); + b = *bp; + } + } + break; + } + } + WUNLOCK_HASH(lck); + if (nitems_diff) { + erts_smp_atomic_add_nob(&tb->common.nitems, nitems_diff); + try_shrink(tb); + } + return DB_ERROR_NONE; +} + /* ** Other interface routines (not directly coupled to one bif) */ diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index a62a83a928..720c0659c3 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -383,6 +383,7 @@ static int db_select_delete_tree(Process *p, DbTable *tbl, Eterm pattern, Eterm *ret); static int db_select_delete_continue_tree(Process *p, DbTable *tbl, Eterm continuation, Eterm *ret); +static int db_take_tree(Process *, DbTable *, Eterm, Eterm *); static void db_print_tree(int to, void *to_arg, int show, DbTable *tbl); static int db_free_table_tree(DbTable *tbl); @@ -431,6 +432,7 @@ DbTableMethod db_tree = db_select_delete_continue_tree, db_select_count_tree, db_select_count_continue_tree, + db_take_tree, db_delete_all_objects_tree, db_free_table_tree, db_free_table_continue_tree, @@ -1722,6 +1724,28 @@ static int db_select_delete_tree(Process *p, DbTable *tbl, } +static int db_take_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret) +{ + DbTableTree *tb = &tbl->tree; + TreeDbTerm *this; + + *ret = NIL; + this = linkout_tree(tb, key, NULL); + if (this) { + Eterm copy, *hp, *hend; + + hp = HAlloc(p, this->dbterm.size + 2); + hend = hp + this->dbterm.size + 2; + copy = db_copy_object_from_ets(&tb->common, + &this->dbterm, &hp, &MSO(p)); + *ret = CONS(hp, copy, NIL); + hp += 2; + HRelease(p, hend, hp); + free_term(tb, this); + } + return DB_ERROR_NONE; +} + /* ** Other interface routines (not directly coupled to one bif) */ diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 328b19dfc9..5ace93c8ed 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -165,6 +165,7 @@ typedef struct db_table_method DbTable* tb, /* [in out] */ Eterm continuation, Eterm* ret); + int (*db_take)(Process *, DbTable *, Eterm, Eterm *); int (*db_delete_all_objects)(Process* p, DbTable* db /* [in out] */ ); -- cgit v1.2.3 From a8aee451650d0734350f5af419a9a8ca0dfc8ab7 Mon Sep 17 00:00:00 2001 From: "Matwey V. Kornilov" Date: Sat, 31 May 2014 16:05:27 +0400 Subject: Add correct detection of armv6hl and armv7hl architectures. --- erts/configure.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 04303da4f8..342fab4f7e 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -680,8 +680,10 @@ case $chk_arch_ in armv5teb) ARCH=arm;; armv5tel) ARCH=arm;; armv5tejl) ARCH=arm;; - armv6l) ARCH=arm;; + armv6l) ARCH=arm;; + armv6hl) ARCH=arm;; armv7l) ARCH=arm;; + armv7hl) ARCH=arm;; tile) ARCH=tile;; *) ARCH=noarch;; esac -- cgit v1.2.3 From 8c4b42329f3e25e387a6511f9287c1f2bf8f2c4d Mon Sep 17 00:00:00 2001 From: "Matwey V. Kornilov" Date: Sat, 31 May 2014 16:15:41 +0400 Subject: Add correct detection of powerpc architecture. --- erts/configure.in | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 342fab4f7e..f66110b98b 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -673,6 +673,7 @@ case $chk_arch_ in x86_64) ARCH=amd64;; amd64) ARCH=amd64;; macppc) ARCH=ppc;; + powerpc) ARCH=ppc;; ppc) ARCH=ppc;; ppc64) ARCH=ppc64;; "Power Macintosh") ARCH=ppc;; -- cgit v1.2.3 From 74d314b0085d5b83b1c7e9aefee8e770b66f93cb Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 9 Apr 2014 09:35:19 +0200 Subject: erts: Fix git version script --- erts/emulator/utils/gen_git_version | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/utils/gen_git_version b/erts/emulator/utils/gen_git_version index ef06a4b8e2..9faf015b62 100755 --- a/erts/emulator/utils/gen_git_version +++ b/erts/emulator/utils/gen_git_version @@ -5,9 +5,9 @@ OUTPUT_FILE=$1 if command -v git 2>&1 >/dev/null && test -d $ERL_TOP/.git -o -f $ERL_TOP/.git then - VSN=`git describe --match "OTP_R[0-9][0-9][A-B]*" HEAD` + VSN=`git describe --match "OTP-[0-9]*" HEAD` case "$VSN" in - OTP_R*-g*) + OTP-*-g*) VSN=`echo $VSN | sed -e 's/.*-g\\(.*\\)/\\1/g'` ;; *) VSN="na" ;; esac @@ -36,4 +36,4 @@ then fi exit 0 fi -exit 1 \ No newline at end of file +exit 1 -- cgit v1.2.3 From 0e6516b55dfdb750a7d27cbb5d45e76f2c2e755b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 3 Jun 2014 14:16:06 +0200 Subject: erts: Fixup for gdb command etp-cp-1 fdb350a4 increased MI_FUNCTIONS to 13 --- erts/etc/unix/etp-commands.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 19c92681d0..6042a5c6f2 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -1065,8 +1065,8 @@ define etp-cp-1 set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2 end if $etp_cp_p - # 12 = MI_FUNCTIONS - set $etp_cp_low = (Eterm**)($etp_cp_p->start + 12) + # 13 = MI_FUNCTIONS + set $etp_cp_low = (Eterm**)($etp_cp_p->start + 13) # 0 = MI_NUM_FUNCTIONS set $etp_cp_high = $etp_cp_low +$etp_cp_p->start[0] set $etp_cp_p = 0 -- cgit v1.2.3 From aa3a7c229ca39400de7d281ae11c4ec78592e095 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 4 Jun 2014 10:55:49 +0200 Subject: erts: Fix cache line alignment bug for ts_event_pool --- erts/lib_src/common/ethr_aux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c index ceecdcef64..b77f2178f2 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-2012. All Rights Reserved. + * Copyright Ericsson AB 2010-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -360,10 +360,10 @@ static ethr_ts_event *ts_event_pool(int size, ethr_ts_event **endpp) int i; ethr_aligned_ts_event *atsev; atsev = ethr_mem__.std.alloc(sizeof(ethr_aligned_ts_event) * size - + ETHR_CACHE_LINE_SIZE); + + ETHR_CACHE_LINE_SIZE - 1); if (!atsev) return NULL; - if ((((ethr_uint_t) atsev) & ETHR_CACHE_LINE_MASK) == 0) + if ((((ethr_uint_t) atsev) & ETHR_CACHE_LINE_MASK) != 0) atsev = ((ethr_aligned_ts_event *) ((((ethr_uint_t) atsev) & ~ETHR_CACHE_LINE_MASK) + ETHR_CACHE_LINE_SIZE)); -- cgit v1.2.3 From 1e377a3c944518ecdfe82c113fe73de48a44519c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 3 Jun 2014 11:18:20 +0200 Subject: erts: tracing on send now works for registered processes This bug was introduced in R16B. Testcases have been adapted to verify the correct behaviour. --- erts/emulator/beam/bif.c | 7 ++++++- erts/emulator/test/trace_SUITE.erl | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 06a1230ca0..fcbeb6cf5c 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1886,8 +1886,13 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { Eterm id = erts_whereis_name_to_id(p, to); rp = erts_proc_lookup(id); - if (rp) + if (rp) { + if (IS_TRACED(p)) + trace_send(p, to, msg); + if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) + save_calls(p, &exp_send); goto send_message; + } pt = erts_port_lookup(id, (erts_port_synchronous_ops diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 2251575e5a..4d7598cf1f 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -181,6 +181,13 @@ send_trace(Config) when is_list(Config) -> ?line {trace, Sender, send, to_receiver, Receiver} = receive_first(), ?line receive_nothing(), + %% Check that a message sent to another registered process is traced. + register(?MODULE,Receiver), + Sender ! {send_please, ?MODULE, to_receiver}, + {trace, Sender, send, to_receiver, ?MODULE} = receive_first(), + receive_nothing(), + unregister(?MODULE), + %% Check that a message sent to this process is traced. ?line Sender ! {send_please, self(), to_myself}, ?line receive to_myself -> ok end, @@ -188,6 +195,21 @@ send_trace(Config) when is_list(Config) -> ?line {trace, Sender, send, to_myself, Self} = receive_first(), ?line receive_nothing(), + %% Check that a message sent to dead process is traced. + {Pid,Ref} = spawn_monitor(fun() -> ok end), + receive {'DOWN',Ref,_,_,_} -> ok end, + Sender ! {send_please, Pid, to_dead}, + {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first(), + receive_nothing(), + + %% Check that a message sent to unknown registrated process is traced. + BadargSender = fun_spawn(fun sender/0), + 1 = erlang:trace(BadargSender, true, [send]), + unlink(BadargSender), + BadargSender ! {send_please, not_registered, to_unknown}, + {trace, BadargSender, send, to_unknown, not_registered} = receive_first(), + receive_nothing(), + %% Another process should not be able to trace Sender. ?line Intruder = fun_spawn(fun() -> erlang:trace(Sender, true, [send]) end), ?line {'EXIT', Intruder, {badarg, _}} = receive_first(), -- cgit v1.2.3 From 0b54f4c6d8901a516e84b670598f375f0cac77f9 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 5 Jun 2014 11:07:26 +0200 Subject: [upgrade_SUITE] Set previous major to r16b When running OTP-17, the previous major release was earlier specified to r16. This sometimes caused an r16a release to be started, which would for sure fail the upgrade attempt. The previous major is now specified to be r16b instead. --- erts/test/upgrade_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index ee84a5dfd7..d5a920e03d 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -264,7 +264,7 @@ do_upgrade(FromVsn,FromApps,ToRel,ToApps,InstallDir) -> %%%----------------------------------------------------------------- %%% Library functions previous_major("17") -> - "r16"; + "r16b"; previous_major(Rel) -> integer_to_list(list_to_integer(Rel)-1). -- cgit v1.2.3 From 82c048745efcb5d811ec0489858b821ef39ea387 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 19 May 2014 18:09:13 +0200 Subject: Replace erlang:binary_to_term() Erlang wrappers Replace the 'erlang:binary_to_term/1' and 'erlang:binary_to_term/2' Erlang wrappers taking care of failure after yield with management of this in the hidden yield BIF. --- erts/emulator/beam/bif.tab | 8 +++- erts/emulator/beam/external.c | 74 ++++++++++++++++++++------------- erts/emulator/hipe/hipe_bif_list.m4 | 4 +- erts/emulator/test/binary_SUITE.erl | 22 +++++++++- erts/preloaded/ebin/erlang.beam | Bin 98248 -> 97940 bytes erts/preloaded/ebin/erts_internal.beam | Bin 4276 -> 3992 bytes erts/preloaded/src/erlang.erl | 18 ++------ erts/preloaded/src/erts_internal.erl | 12 ------ 8 files changed, 79 insertions(+), 59 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 2d888862bf..9816883c9a 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -45,6 +45,7 @@ bif erlang:apply/3 bif erlang:atom_to_list/1 bif erlang:binary_to_list/1 bif erlang:binary_to_list/3 +bif erlang:binary_to_term/1 bif erlang:crc32/1 bif erlang:crc32/2 bif erlang:crc32_combine/3 @@ -151,8 +152,6 @@ bif erts_internal:port_command/3 bif erts_internal:port_control/3 bif erts_internal:port_close/1 bif erts_internal:port_connect/2 -bif erts_internal:binary_to_term/1 -bif erts_internal:binary_to_term/2 bif erts_internal:request_system_task/3 bif erts_internal:check_process_code/2 @@ -479,6 +478,11 @@ bif erlang:load_nif/2 bif erlang:call_on_load_function/1 bif erlang:finish_after_on_load/2 +# +# New Bifs in R13B04 +# +bif erlang:binary_to_term/2 + # # The binary match bifs (New in R14A - EEP9) # diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index e376253400..8d240355b0 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -108,26 +108,17 @@ static int encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acm static Export binary_to_term_trap_export; static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1); -static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b); +static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b, + Export *bif, Eterm arg0, Eterm arg1); void erts_init_external(void) { -#if 1 /* In R16 */ erts_init_trap_export(&term_to_binary_trap_export, - am_erlang, am_term_to_binary_trap, 1, + am_erts_internal, am_term_to_binary_trap, 1, &term_to_binary_trap_1); erts_init_trap_export(&binary_to_term_trap_export, - am_erlang, am_binary_to_term_trap, 1, + am_erts_internal, am_binary_to_term_trap, 1, &binary_to_term_trap_1); -#else - sys_memset((void *) &term_to_binary_trap_export, 0, sizeof(Export)); - term_to_binary_trap_export.address = &term_to_binary_trap_export.code[3]; - term_to_binary_trap_export.code[0] = am_erlang; - term_to_binary_trap_export.code[1] = am_term_to_binary_trap; - term_to_binary_trap_export.code[2] = 1; - term_to_binary_trap_export.code[3] = (BeamInstr) em_apply_bif; - term_to_binary_trap_export.code[4] = (BeamInstr) &term_to_binary_trap_1; -#endif return; } @@ -1186,6 +1177,8 @@ typedef struct B2TContext_t { Uint32 flags; SWord reds; Eterm trap_bin; + Export *bif; + Eterm arg[2]; enum B2TState state; union { B2TSizeContext sc; @@ -1357,7 +1350,8 @@ static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1) Binary *context_bin = ((ProcBin *) binary_val(BIF_ARG_1))->val; ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(context_bin) == b2t_context_destructor); - return binary_to_term_int(BIF_P, 0, THE_NON_VALUE, context_bin); + return binary_to_term_int(BIF_P, 0, THE_NON_VALUE, context_bin, NULL, + THE_NON_VALUE, THE_NON_VALUE); } @@ -1392,8 +1386,10 @@ static B2TContext* b2t_export_context(Process* p, B2TContext* src) return ctx; } -static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b) +static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b, + Export *bif_init, Eterm arg0, Eterm arg1) { + BIF_RETTYPE ret_val; #ifdef EXTREME_B2T_TRAPPING SWord initial_reds = 1 + b2t_rand() % 4; #else @@ -1410,6 +1406,9 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con ctx->state = B2TPrepare; ctx->aligned_alloc = NULL; ctx->flags = flags; + ctx->bif = bif_init; + ctx->arg[0] = arg0; + ctx->arg[1] = arg1; IF_DEBUG(ctx->trap_bin = THE_NON_VALUE;) } else { is_first_call = 0; @@ -1505,12 +1504,24 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con HRelease(p, ctx->u.dc.hp_end, ctx->u.dc.hp_start); /*fall through*/ case B2TBadArg: - b2t_destroy_context(ctx); - if (!is_first_call) { - erts_set_gc_state(p, 1); - } BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION); - BIF_ERROR(p, BADARG & ~EXF_SAVETRACE); + + ASSERT(ctx->bif == bif_export[BIF_binary_to_term_1] + || ctx->bif == bif_export[BIF_binary_to_term_2]); + + if (is_first_call) + ERTS_BIF_PREP_ERROR(ret_val, p, BADARG); + else { + erts_set_gc_state(p, 1); + if (is_non_value(ctx->arg[1])) + ERTS_BIF_PREP_ERROR_TRAPPED1(ret_val, p, BADARG, ctx->bif, + ctx->arg[0]); + else + ERTS_BIF_PREP_ERROR_TRAPPED2(ret_val, p, BADARG, ctx->bif, + ctx->arg[0], ctx->arg[1]); + } + b2t_destroy_context(ctx); + return ret_val; case B2TDone: b2t_destroy_context(ctx); @@ -1525,7 +1536,8 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con erts_set_gc_state(p, 1); } BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION); - return ctx->u.dc.res; + ERTS_BIF_PREP_RET(ret_val, ctx->u.dc.res); + return ret_val; default: ASSERT(!"Unknown state in binary_to_term"); @@ -1542,19 +1554,24 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con erts_set_gc_state(p, 0); } BUMP_ALL_REDS(p); - BIF_TRAP1(&binary_to_term_trap_export, p, ctx->trap_bin); + + ERTS_BIF_PREP_TRAP1(ret_val, &binary_to_term_trap_export, + p, ctx->trap_bin); + + return ret_val; } -HIPE_WRAPPER_BIF_DISABLE_GC(erts_internal_binary_to_term, 1) +HIPE_WRAPPER_BIF_DISABLE_GC(binary_to_term, 1) -BIF_RETTYPE erts_internal_binary_to_term_1(BIF_ALIST_1) +BIF_RETTYPE binary_to_term_1(BIF_ALIST_1) { - return binary_to_term_int(BIF_P, 0, BIF_ARG_1, NULL); + return binary_to_term_int(BIF_P, 0, BIF_ARG_1, NULL, bif_export[BIF_binary_to_term_1], + BIF_ARG_1, THE_NON_VALUE); } -HIPE_WRAPPER_BIF_DISABLE_GC(erts_internal_binary_to_term, 2) +HIPE_WRAPPER_BIF_DISABLE_GC(binary_to_term, 2) -BIF_RETTYPE erts_internal_binary_to_term_2(BIF_ALIST_2) +BIF_RETTYPE binary_to_term_2(BIF_ALIST_2) { Eterm opts; Eterm opt; @@ -1575,7 +1592,8 @@ BIF_RETTYPE erts_internal_binary_to_term_2(BIF_ALIST_2) if (is_not_nil(opts)) goto error; - return binary_to_term_int(BIF_P, flags, BIF_ARG_1, NULL); + return binary_to_term_int(BIF_P, flags, BIF_ARG_1, NULL, bif_export[BIF_binary_to_term_2], + BIF_ARG_1, BIF_ARG_2); error: BIF_ERROR(BIF_P, BADARG); diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 16765fdb99..5f92b6bac4 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -268,8 +268,8 @@ noproc_primop_interface_1(nbif_atomic_inc, hipe_atomic_inc) */ define(CFUN,`ifelse($1,term_to_binary_1,hipe_wrapper_term_to_binary_1, ifelse($1,term_to_binary_2,hipe_wrapper_term_to_binary_2, -ifelse($1,erts_internal_binary_to_term_1,hipe_wrapper_erts_internal_binary_to_term_1, -ifelse($1,erts_internal_binary_to_term_2,hipe_wrapper_erts_internal_binary_to_term_2, +ifelse($1,binary_to_term_1,hipe_wrapper_binary_to_term_1, +ifelse($1,binary_to_term_2,hipe_wrapper_binary_to_term_2, ifelse($1,binary_to_list_1,hipe_wrapper_binary_to_list_1, ifelse($1,binary_to_list_3,hipe_wrapper_binary_to_list_3, ifelse($1,bitstring_to_list_1,hipe_wrapper_bitstring_to_list_1, diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 22c4ed29ad..44e9e4f243 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1420,6 +1420,11 @@ error_after_yield(Config) when is_list(Config) -> error_after_yield(badarg, erlang, list_to_bitstring, 1, fun () -> [[list2bitstrlist(mk_list(1000000)), oops]] end, L2BTrap), error_after_yield(badarg, binary, list_to_bin, 1, fun () -> [[mk_list(1000000), oops]] end, L2BTrap), + B2TTrap = {erts_internal, binary_to_term_trap, 1}, + + error_after_yield(badarg, erlang, binary_to_term, 1, fun () -> [error_after_yield_bad_ext_term()] end, B2TTrap), + error_after_yield(badarg, erlang, binary_to_term, 2, fun () -> [error_after_yield_bad_ext_term(), [safe]] end, B2TTrap), + case erlang:system_info(wordsize) of 4 -> SysLimitSz = 1 bsl 32, @@ -1462,7 +1467,7 @@ error_after_yield(Type, M, F, AN, AFun, TrapFunc) -> {trace_delivered, Pid, TD} -> NoYields = error_after_yield_sched(Pid, TrapFunc, 0), io:format("No of yields: ~p~n", [NoYields]), - true = NoYields > 10 + true = NoYields > 2 end, ok. @@ -1485,6 +1490,21 @@ error_after_yield_sched(P, TrapFunc, N) -> after 0 -> N end. + +error_after_yield_bad_ext_term() -> + TupleSz = 2000000, + <<131, % Version magic + AtomExt/binary>> = term_to_binary(an_atom_we_use_for_this), + BadAtomExt = [100, %% ATOM_EXT + 255, 255, % Invalid size of 65535 bytes + "oops"], + + %% Produce a large tuple where the last element is invalid + list_to_binary([131, %% Version magic + 105, %% LARGE_TUPLE_EXT + <>, %% Tuple size + lists:duplicate(TupleSz-1, AtomExt), %% Valid atoms + BadAtomExt]). %% Invalid atom at the end cmp_old_impl(Config) when is_list(Config) -> %% Compare results from new yielding implementations with diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index e19bb370bc..531c561d52 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index d41c833e05..6ea8ea9773 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 1508eed9ee..2fe85287b6 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -361,25 +361,15 @@ binary_to_list(_Binary, _Start, _Stop) -> %% binary_to_term/1 -spec binary_to_term(Binary) -> term() when Binary :: ext_binary(). -binary_to_term(Binary) -> - %% This BIF may throw badarg while trapping - try - erts_internal:binary_to_term(Binary) - catch - error:Reason -> erlang:error(Reason,[Binary]) - end. +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) -> - %% This BIF may throw badarg while trapping - try - erts_internal:binary_to_term(Binary,Opts) - catch - error:Reason -> erlang:error(Reason,[Binary,Opts]) - end. +binary_to_term(_Binary, _Opts) -> + erlang:nif_error(undefined). %% bit_size/1 %% Shadowed by erl_bif_types: erlang:bit_size/1 diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index edcd50c77e..e9044503b3 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -29,7 +29,6 @@ -module(erts_internal). -export([await_port_send_result/3]). --export([binary_to_term/1, binary_to_term/2]). -export([cmp_term/2]). -export([port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). @@ -161,17 +160,6 @@ request_system_task(_Pid, _Prio, _Request) -> check_process_code(_Module, _OptionList) -> erlang:nif_error(undefined). --spec binary_to_term(Binary) -> term() when - Binary :: binary(). -binary_to_term(_Binary) -> - erlang:nif_error(undefined). - --spec binary_to_term(Binary, Opts) -> term() when - Binary :: binary(), - Opts :: [safe]. -binary_to_term(_Binary, _Opts) -> - erlang:nif_error(undefined). - %% term compare where integer() < float() = true -spec cmp_term(A,B) -> Result when -- cgit v1.2.3 From d23496206b4892dae0efd053104d16904d1d8eab Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 20 May 2014 21:12:45 +0200 Subject: Remove invalid 'bitstr' exports in erlang.erl The dummy BIF exports 'erlang:bitstr_to_list/1' and 'erlang:list_to_bitstr/1' seem to have appeared as a result of a copy-paste error and are now removed. --- erts/preloaded/ebin/erlang.beam | Bin 97940 -> 97732 bytes erts/preloaded/src/erlang.erl | 16 ++-------------- 2 files changed, 2 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 531c561d52..fdc7401475 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 2fe85287b6..67099093c5 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -79,7 +79,7 @@ -export([binary_to_integer/1,binary_to_integer/2]). -export([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([bit_size/1, bitsize/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, check_process_code/3, crc32/1]). @@ -100,7 +100,7 @@ -export([integer_to_binary/1, integer_to_list/1]). -export([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_atom/1, list_to_binary/1]). -export([list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1]). -export([list_to_integer/1, list_to_integer/2]). -export([list_to_pid/1, list_to_tuple/1, loaded/0]). @@ -384,12 +384,6 @@ bit_size(_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(). @@ -1072,12 +1066,6 @@ list_to_atom(_String) -> 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(). -- cgit v1.2.3 From 6db23381adb9c580e1d03cccf2a8b67c6cc5189b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 11 Jun 2014 18:22:05 +0200 Subject: erts: Fix cache alignment for ETS write_concurrency locks Effects if sizeof(erts_smp_rwmtx_t) > 64: + Better performance due to less false sharing - Increased memory footprint per table --- erts/emulator/beam/erl_db_hash.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h index 908cec11d4..e68081a5b1 100644 --- a/erts/emulator/beam/erl_db_hash.h +++ b/erts/emulator/beam/erl_db_hash.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2014. All Rights Reserved. * * The 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,7 +42,7 @@ typedef struct hash_db_term { typedef struct db_table_hash_fine_locks { union { erts_smp_rwmtx_t lck; - byte _cache_line_alignment[64]; + byte _cache_line_alignment[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_smp_rwmtx_t))]; }lck_vec[DB_HASH_LOCK_CNT]; } DbTableHashFineLocks; -- cgit v1.2.3 From 6fc9f2916f98bf539ebe2117c3f4111917649f37 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 28 May 2014 19:32:10 +0200 Subject: erts: Add etp-alloc-stats and etp-alloc-instances --- erts/etc/unix/etp-commands.in | 148 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 9e39764195..f3507c2510 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2012. All Rights Reserved. +# Copyright Ericsson AB 2005-2014. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -2830,6 +2830,152 @@ document etp-search-alloc end +define etp-alloc-stats + printf "\nIx Name Inst. Blocks Bytes Carriers Crr.bytes Util\n" + set $etp_tot_block_no = 0 + set $etp_tot_block_sz = 0 + set $etp_tot_crr_no = 0 + set $etp_tot_crr_sz = 0 + set $etp_ERTS_ALC_A_MIN = 1 + set $etp_ERTS_ALC_A_MAX = (sizeof(erts_allctrs) / sizeof(*erts_allctrs)) - 1 + + set $etp_ix = $etp_ERTS_ALC_A_MIN + while $etp_ix <= $etp_ERTS_ALC_A_MAX + set $etp_allctr = 0 + set $etp_alloc = erts_allctrs[$etp_ix].alloc + if $etp_alloc != erts_sys_alloc + if $etp_alloc == erts_alcu_alloc_thr_spec || \ + $etp_alloc == erts_alcu_alloc_thr_pref + set $etp_instance = 0 + set $etp_block_no = 0 + set $etp_block_sz = 0 + set $etp_crr_no = 0 + set $etp_crr_sz = 0 + set $etp_tspec = (ErtsAllocatorThrSpec_t *) erts_allctrs[$etp_ix].extra + if $etp_tspec->enabled + while $etp_instance < $etp_tspec->size + set $etp_allctr = $etp_tspec->allctr[$etp_instance] + set $etp_block_no = $etp_block_no + $etp_allctr->mbcs.blocks.curr.no \ + + $etp_allctr->sbcs.blocks.curr.no + set $etp_block_sz = $etp_block_sz + $etp_allctr->mbcs.blocks.curr.size \ + + $etp_allctr->sbcs.blocks.curr.size + set $etp_crr_no = $etp_crr_no + $etp_allctr->mbcs.curr.norm.mseg.no \ + + $etp_allctr->sbcs.curr.norm.mseg.no \ + + $etp_allctr->mbcs.curr.norm.sys_alloc.no \ + + $etp_allctr->sbcs.curr.norm.sys_alloc.no + set $etp_crr_sz = $etp_crr_sz + $etp_allctr->mbcs.curr.norm.mseg.size \ + + $etp_allctr->sbcs.curr.norm.mseg.size \ + + $etp_allctr->mbcs.curr.norm.sys_alloc.size \ + + $etp_allctr->sbcs.curr.norm.sys_alloc.size + set $etp_instance = $etp_instance + 1 + end + else + printf "erts_allctr[%d]: Disabled (thread specific)\n", $etp_ix + end + else + if $etp_alloc == erts_alcu_alloc_ts || $etp_alloc == erts_alcu_alloc + set $etp_allctr = (Allctr_t*) erts_allctrs[$etp_ix].extra + set $etp_block_no = $etp_allctr->mbcs.blocks.curr.no \ + + $etp_allctr->sbcs.blocks.curr.no + set $etp_block_sz = $etp_allctr->mbcs.blocks.curr.size \ + + $etp_allctr->sbcs.blocks.curr.size + set $etp_crr_no = $etp_allctr->mbcs.curr.norm.mseg.no \ + + $etp_allctr->sbcs.curr.norm.mseg.no \ + + $etp_allctr->mbcs.curr.norm.sys_alloc.no \ + + $etp_allctr->sbcs.curr.norm.sys_alloc.no + set $etp_crr_sz = $etp_allctr->mbcs.curr.norm.mseg.size \ + + $etp_allctr->sbcs.curr.norm.mseg.size \ + + $etp_allctr->mbcs.curr.norm.sys_alloc.size \ + + $etp_allctr->sbcs.curr.norm.sys_alloc.size + set $etp_instance = 1 + else + printf "erts_allctr[%d]: Unknown allocation function: ", $etp_ix + p $etp_alloc + end + end + end + if $etp_allctr != 0 + printf "%2d %-8s%2d%12lu%13lu%12lu%13lu", $etp_ix, $etp_allctr->name_prefix, \ + $etp_instance, \ + $etp_block_no, $etp_block_sz, $etp_crr_no, $etp_crr_sz + if $etp_crr_sz != 0 + printf "%5lu%%", ($etp_block_sz * 100) / $etp_crr_sz + end + printf "\n" + set $etp_tot_block_no = $etp_tot_block_no + $etp_block_no + set $etp_tot_block_sz = $etp_tot_block_sz + $etp_block_sz + set $etp_tot_crr_no = $etp_tot_crr_no + $etp_crr_no + set $etp_tot_crr_sz = $etp_tot_crr_sz + $etp_crr_sz + end + set $etp_ix = $etp_ix + 1 + end + printf "\nTotal: %12lu%13lu%12lu%13lu", $etp_tot_block_no, $etp_tot_block_sz, \ + $etp_tot_crr_no, $etp_tot_crr_sz + if $etp_tot_crr_sz != 0 + printf "%5lu%%", ($etp_tot_block_sz * 100) / $etp_tot_crr_sz + end + printf "\n" +end + +document etp-alloc-stats +%--------------------------------------------------------------------------- +% etp-alloc-stats +% +% Combine and print allocator statistics +%--------------------------------------------------------------------------- +end + + +define etp-alloc-instances + set $etp_ERTS_ALC_A_MIN = 1 + set $etp_ERTS_ALC_A_MAX = (sizeof(erts_allctrs) / sizeof(*erts_allctrs)) - 1 + + set $etp_ix = $arg0 + if $etp_ix >= $etp_ERTS_ALC_A_MIN && $etp_ix <= $etp_ERTS_ALC_A_MAX + set $etp_allctr = 0 + set $etp_alloc = erts_allctrs[$etp_ix].alloc + if $etp_alloc == erts_sys_alloc + printf "Allocator %d is sys_alloc\n", $etp_ix + else + if $etp_alloc == erts_alcu_alloc_thr_spec || \ + $etp_alloc == erts_alcu_alloc_thr_pref + set $etp_instance = 0 + set $etp_tspec = (ErtsAllocatorThrSpec_t *) erts_allctrs[$etp_ix].extra + if $etp_tspec->enabled + printf "All instances for allocator '%s'\n", $etp_tspec->allctr[0]->name_prefix + while $etp_instance < $etp_tspec->size + p $etp_tspec->allctr[$etp_instance] + set $etp_instance = $etp_instance + 1 + end + else + printf "erts_allctr[%d]: Disabled (thread specific)\n", $etp_ix + end + else + if $etp_alloc == erts_alcu_alloc_ts || $etp_alloc == erts_alcu_alloc + set $etp_allctr = (Allctr_t*) erts_allctrs[$etp_ix].extra + printf "Single instances for allocator '%s'\n", $etp_allctr->name_prefix + p $etp_allctr + else + printf "erts_allctr[%d]: Unknown allocation function: ", $etp_ix + p $etp_alloc + end + end + end + else + printf "Allocator type not between %d and %d\n", $etp_ERTS_ALC_A_MIN, $etp_ERTS_ALC_A_MAX + end +end + +document etp-alloc-instances +%--------------------------------------------------------------------------- +% etp-alloc-instances +% +% Print pointers to all allocator instances for a specific type (Ix) +%--------------------------------------------------------------------------- +end + + + define etp-overlapped-heaps # Args: -- cgit v1.2.3 From 02c00c02bedde6bad018eef490527aa513fbd552 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 12 Jun 2014 17:13:29 +0200 Subject: erts: Fix documentation for no of default allocator instances ll_alloc does not default to single instance since 17.0 --- erts/doc/src/erts_alloc.xml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index c9eca39a99..1ade41f1aa 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -4,7 +4,7 @@
- 20022013 + 20022014 Ericsson AB. All Rights Reserved. @@ -531,15 +531,9 @@

Multiple, thread specific instances of the allocator. This option will only have any effect on the runtime system with SMP support. Default behaviour on the runtime system with - SMP support:

- - ll_alloc - 1 instance. - Other allocators - NoSchedulers+1 instances. Each scheduler will use - a lock-free instance of its own and other threads will use - a common instance. - + SMP support is NoSchedulers+1 instances. Each scheduler will use + a lock-free instance of its own and other threads will use + a common instance.

It was previously (before ERTS version 5.9) possible to configure a smaller amount of thread specific instances than schedulers. This is, however, not possible any more.

-- cgit v1.2.3 From ab8c6675511cb1968c57fab3dd8e47cc48037c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 13 Jun 2014 12:57:35 +0200 Subject: add_abstract_code: Remove 'from_asm' option The purpose of add_abstract_code is to give Dialyzer some abstract code so that it will not fail fatally when analysing prim_eval which was compiled from BEAM assembly. But if Dialyzer were to pass along the compiler options that the module was compiled with when translating the abstract code to Core Erlang, the 'from_asm' option would crash the compilation. Thus, since we are already cheating, we should cheat a little bit more and also remove the 'from_asm' option. --- erts/preloaded/src/add_abstract_code | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/add_abstract_code b/erts/preloaded/src/add_abstract_code index e670156d21..211a60c930 100644 --- a/erts/preloaded/src/add_abstract_code +++ b/erts/preloaded/src/add_abstract_code @@ -27,8 +27,18 @@ main([BeamFile,AbstrFile]) -> {ok,_,Chunks0} = beam_lib:all_chunks(BeamFile), {ok,Abstr} = file:consult(AbstrFile), - Chunks = lists:keyreplace("Abst", 1, Chunks0, - {"Abst",term_to_binary({raw_abstract_v1,Abstr})}), + Chunks1 = lists:keyreplace("Abst", 1, Chunks0, + {"Abst",term_to_binary({raw_abstract_v1,Abstr})}), + {"CInf",CInf0} = lists:keyfind("CInf", 1, Chunks1), + CInf = fix_options(CInf0), + Chunks = lists:keyreplace("CInf", 1, Chunks1, {"CInf",CInf}), {ok,Module} = beam_lib:build_module(Chunks), ok = file:write_file(BeamFile, Module), init:stop(). + +fix_options(CInf0) -> + CInf1 = binary_to_term(CInf0), + {options,Opts0} = lists:keyfind(options, 1, CInf1), + Opts = Opts0 -- [from_asm], + CInf = lists:keyreplace(options, 1, CInf1, {options,Opts}), + term_to_binary(CInf). -- cgit v1.2.3 From 5fb0d815d3b418a605a479c6691e75cee197f3c9 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 16 Jun 2014 15:09:56 +0200 Subject: Fix redefinition of ETHR_FORCE_INLINE --- erts/include/internal/ethread.h | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 230388cce5..a3558167ed 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -80,6 +80,7 @@ || (defined(__GNUC__) && defined(ERTS_MIXED_CYGWIN_VC)) # undef ETHR_INLINE # define ETHR_INLINE +# undef ETHR_FORCE_INLINE # define ETHR_FORCE_INLINE # undef ETHR_TRY_INLINE_FUNCS #endif -- cgit v1.2.3 From f47320f43c3fb39f78054834e60ae9424ac79f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 Jun 2014 16:26:50 +0200 Subject: doc: Fix broken links --- erts/doc/src/notes.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 68feaa027a..fbbaf1c674 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -595,9 +595,9 @@ "hi" := V1, a := V2, b := V3} = M2. % match keys with values

- For information on how to use Maps please see the - Reference - Manual.

+ For information on how to use Maps please see Map Expressions in the + + Reference Manual.

The current implementation is without the following features: No variable keys -- cgit v1.2.3 From 139825694c587b0f543224bf083170fad0e1ad75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 Jun 2014 16:56:09 +0200 Subject: doc: Fix broken links in Installation Guide --- erts/doc/src/notes.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index fbbaf1c674..6b3e41c1f2 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -4854,7 +4854,7 @@

The configure command line argument --enable-ethread-pre-pentium4-compatibility + marker="doc/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP">--enable-ethread-pre-pentium4-compatibility had no effect. This option is now also automatically enabled if required on the build machine.

@@ -5433,7 +5433,7 @@ platforms than before. If configure warns about no atomic implementation available, try using the libatomic_ops library. Use the --with-libatomic_ops=PATH + marker="doc/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP">--with-libatomic_ops=PATH configure command line argument when specifying where the libatomic_ops installation is located. The libatomic_ops library can be downloaded from: @@ -5451,7 +5451,7 @@ the pentium 4 processor. If you want the runtime system to be compatible with older processors (back to 486) you need to pass the --enable-ethread-pre-pentium4-compatibility + marker="doc/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP">--enable-ethread-pre-pentium4-compatibility configure command line argument when configuring the system.

-- cgit v1.2.3 From 9ae89c4b3497bf4f0f83e93f7db07d2b44993c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Lid=C3=A9n?= Date: Wed, 18 Jun 2014 15:36:58 +0200 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 54768 -> 54776 bytes erts/preloaded/ebin/erlang.beam | Bin 97732 -> 97740 bytes erts/preloaded/ebin/erts_internal.beam | Bin 4176 -> 4160 bytes erts/preloaded/ebin/init.beam | Bin 48780 -> 48800 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1460 -> 1464 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1340 -> 1332 bytes erts/preloaded/ebin/prim_file.beam | Bin 44892 -> 44900 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72912 -> 72916 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23424 -> 23432 bytes erts/preloaded/ebin/zlib.beam | Bin 13176 -> 13180 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index f1e588320b..eec49f3983 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index fdc7401475..260badbcb3 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index ba45e4e011..7dc7407a81 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 26f779500c..5c139c4550 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 4d22d8bace..cf32b79e8d 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index efc8347b6e..37ed8d0365 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 6c49b5185e..d49578abfa 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index fe5431c5ff..93e70cd623 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 73be297bbb..8dc8cb961b 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 193cebdc31..7507efb076 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 77da984614ae462912a1896ba1bb73c798ffd4f8 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 19 Jun 2014 13:47:21 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 148 +++++++++++++++++++++++++++++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 149 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 6b3e41c1f2..68875fe56f 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,154 @@

This document describes the changes made to the ERTS application.

+
Erts 6.1 + +
Fixed Bugs and Malfunctions + + +

The documentation for spawn_opt/5 now has a + note mentioning that the monitor option is not + supported.

+

+ Own Id: OTP-11849

+
+ +

+ Fix broken system monitoring of large_heap for + non-smp VM. No message for large_heap was ever + sent on non-smp VM. Bug exist since R16B.

+

+ Own Id: OTP-11852

+
+ +

+ The emulator without SMP support crashed when passing a + message to a process without enough heap space for the + message. This bug was introduced in erts-6.0.

+

+ Own Id: OTP-11887 Aux Id: OTP-11388

+
+ +

+ Fix race between ETS table deletion and unfixation that + could cause VM crash. The race could happen between a + terminating process that does not own the table but has a + fixation on it and another process that deletes the table + (maybe the owner terminating) at the same time. Bug + existed since R15B02.

+

+ Own Id: OTP-11892

+
+ +

The string following the -eval option when + invoking erl would not be properly translated from + UTF-8 to a list of Unicode characters (as would the + arguments for -run).

+

That bug would cause the build of Erlang/OTP to fail + when building in a directory whose pathname contained + non-US ASCII characters encoded in UTF-8. (Thanks to Eric + Pailleau for reporting this bug.)

+

+ Own Id: OTP-11916

+
+ +

+ Fix erts_debug:size/1 to handle Map sizes

+

+ Own Id: OTP-11923

+
+ +

+ Removed erlang:bitstr_to_list/1 and + erlang:list_to_bitstr/1. They were added by + mistake, and have always raised an undefined + exception when called.

+

+ Own Id: OTP-11942

+
+ +

+ Fixed compilation using mingw-w64 on Windows.

+

+ Thanks to Jani Hakala.

+

+ Own Id: OTP-11945

+
+ +

+ The git sha is no longer printed in the shell start + header when erlang is built from a tagged git release.

+

+ Own Id: OTP-11961

+
+ +

+ Fixed a bug where send trace events were + erroneously dropped when the send was done to a + registered process. This bug was introduced in R16B.

+

+ Own Id: OTP-11968

+
+
+
+ + +
Improvements and New Features + + +

The following native functions now bump an appropriate + amount of reductions and yield when out of + reductions:

+ erlang:binary_to_list/1 + erlang:binary_to_list/3 + erlang:bitstring_to_list/1 + erlang:list_to_binary/1 + erlang:iolist_to_binary/1 + erlang:list_to_bitstring/1 + binary:list_to_bin/1 +

Characteristics impact:

+ Performance The functions converting + from lists got a performance loss for very small lists, + and a performance gain for very large lists. + Priority Previously a process executing + one of these functions effectively got an unfair priority + boost. This priority boost depended on the input size. + The larger the input was, the larger the priority boost + got. This unfair priority boost is now lost. + +

+ Own Id: OTP-11888

+
+ +

+ The systemd features of epmd have been removed from epmd + by default. To enable them you have to build erlang with + the configure option --enable-systemd.

+

+ Own Id: OTP-11921

+
+ +

+ Removed Erlang wrapper code used when calling + binary_to_term/1, and binary_to_term/2. + This improves the performance of these BIFs especially + when they are called with small binaries as input.

+

+ Own Id: OTP-11931

+
+ +

+ Add erlang:system_info(tolerant_timeofday), an API to + check whether compensation for sudden changes of system + time is enabled or not.

+

+ Own Id: OTP-11970

+
+
+
+ +
+
Erts 6.0.1
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index fff334c89f..2e773079f3 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.0.2 +VSN = 6.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 864ebdc6264d134b1face2cd6b400b992b31e6e5 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Sat, 21 Jun 2014 17:22:34 -0400 Subject: add missing description for erl +SDio option Add explanatory text for the erl +SDio option, which is used to set the number of dirty I/O schedulers. --- erts/doc/src/erl.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 9724a1345a..1a4a955d8b 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -848,6 +848,19 @@

+ +

Sets the number of dirty I/O scheduler threads to create when threading + support has been enabled. The valid range is 0-1024. By default, the number + of dirty I/O scheduler threads created is 10, same as the default number of + threads in the async thread pool + . +

+

This option is ignored if the emulator doesn't have threading support + enabled. Currently, this option is experimental and is supported only + if the emulator was configured and built with support for dirty schedulers + enabled (it's disabled by default). +

+

Scheduling specific flags.

-- cgit v1.2.3 From 2528bca8db70a917ffdb1801df1181eff0d0e089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 24 Jun 2014 00:37:58 +0200 Subject: erts: Don't redefine '_XOPEN_SOURCE' --- erts/etc/unix/run_erl.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index a6fc4c2bf5..4b123b8911 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -40,9 +40,13 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif + #ifdef HAVE_WORKING_POSIX_OPENPT +#ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif +#endif + #include #include #include -- cgit v1.2.3 From 353cbeecbb396aa641424b036bc0b7f2519db3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 24 Jun 2014 00:44:36 +0200 Subject: erts: Fix tentative-definition-incomplete-type --- erts/etc/common/run_erl_common.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/run_erl_common.c b/erts/etc/common/run_erl_common.c index dc55c2bea4..580b6cc3c5 100644 --- a/erts/etc/common/run_erl_common.c +++ b/erts/etc/common/run_erl_common.c @@ -74,15 +74,6 @@ * run_erl multiple times with different global variables without them * effecting eachother. */ -typedef struct run_erl_ run_erl; - -#ifdef __OSE__ -static OSPPDKEY run_erl_pp_key; -#define RE_DATA (*(run_erl**)ose_get_ppdata(run_erl_pp_key)) -#else -static run_erl re; -#define RE_DATA (&re) -#endif #define STATUSFILE (RE_DATA->statusfile) #define LOG_DIR (RE_DATA->log_dir) @@ -116,6 +107,16 @@ struct run_erl_ { unsigned protocol_ver; }; +typedef struct run_erl_ run_erl; + +#ifdef __OSE__ +static OSPPDKEY run_erl_pp_key; +#define RE_DATA (*(run_erl**)ose_get_ppdata(run_erl_pp_key)) +#else +static run_erl re; +#define RE_DATA (&re) +#endif + /* prototypes */ static int next_log(int log_num); -- cgit v1.2.3 From ab50e4e5121c2c29b8a49916f8ac89e42e37a978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Rasc=C3=A3o?= Date: Fri, 27 Jun 2014 00:08:07 +0100 Subject: fix escript archive symlinked across drives real_path method used while prim loading archive files was not taking into account the fact that windows directory symlinks can be across different drives (eg. c:\tmp\test is a symlink to j:\tmp\test). when performing a path split the drive precedes the symlink, but that has be rewritten also since it's different. This issue never arises in Unix since obviously there are no drives --- erts/preloaded/src/erl_prim_loader.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 578913b633..dfd6151b69 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -1487,7 +1487,12 @@ real_path(Name,[Path|Paths],Acc,Links) -> [""|_] = LinkPaths -> real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]); LinkPaths -> - real_path(Name,LinkPaths++Paths,Acc,[ThisFile|Links]) + case erlang:system_info(os_type) of + {win32, _} -> + real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]); + _ -> + real_path(Name,LinkPaths++Paths,Acc,[ThisFile|Links]) + end end; _ -> real_path(Name,Paths,This,Links) -- cgit v1.2.3 From 1d583e08cd5235881ede6f92800c25ddee13056f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 27 Jun 2014 20:13:46 +0200 Subject: erts: Fix size overflow bugs in memory allocation --- erts/emulator/beam/erl_alloc_util.c | 9 +++++++++ erts/emulator/beam/erl_binary.h | 31 ++++++++++++++++++------------- erts/emulator/beam/sys.h | 2 ++ 3 files changed, 29 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 45f0cc4312..a4e164bf51 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -3274,6 +3274,15 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) ASSERT(!(flags & CFLG_FORCE_MSEG && flags & CFLG_FORCE_SYS_ALLOC)); + if (umem_sz > (ERTS_UINT_MAX - ERTS_UINT_MAX/100)) { + /* Do an overly conservative _overflow_ check here so we don't + * have to deal with it from here on. I guess we could be more accurate + * but I don't think the need to allocate over 99% of the address space + * will ever arise on any machine, neither 32 nor 64 bit. + */ + return NULL; + } + blk_sz = UMEMSZ2BLKSZ(allctr, umem_sz); #ifdef ERTS_SMP diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index 6c9f53ce87..06dfeb1260 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -236,6 +236,8 @@ erts_bin_drv_alloc_fnf(Uint size) { Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD; void *res; + if (bsize < size) /* overflow */ + return NULL; res = erts_alloc_fnf(ERTS_ALC_T_DRV_BINARY, bsize); ERTS_CHK_BIN_ALIGNMENT(res); return (Binary *) res; @@ -246,6 +248,8 @@ erts_bin_drv_alloc(Uint size) { Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD; void *res; + if (bsize < size) /* overflow */ + erts_alloc_enomem(ERTS_ALC_T_DRV_BINARY, size); res = erts_alloc(ERTS_ALC_T_DRV_BINARY, bsize); ERTS_CHK_BIN_ALIGNMENT(res); return (Binary *) res; @@ -257,6 +261,8 @@ erts_bin_nrml_alloc(Uint size) { Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD; void *res; + if (bsize < size) /* overflow */ + erts_alloc_enomem(ERTS_ALC_T_BINARY, size); res = erts_alloc(ERTS_ALC_T_BINARY, bsize); ERTS_CHK_BIN_ALIGNMENT(res); return (Binary *) res; @@ -267,11 +273,12 @@ erts_bin_realloc_fnf(Binary *bp, Uint size) { Binary *nbp; Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD; + ErtsAlcType_t type = (bp->flags & BIN_FLAG_DRV) ? ERTS_ALC_T_DRV_BINARY + : ERTS_ALC_T_BINARY; ASSERT((bp->flags & BIN_FLAG_MAGIC) == 0); - if (bp->flags & BIN_FLAG_DRV) - nbp = erts_realloc_fnf(ERTS_ALC_T_DRV_BINARY, (void *) bp, bsize); - else - nbp = erts_realloc_fnf(ERTS_ALC_T_BINARY, (void *) bp, bsize); + if (bsize < size) /* overflow */ + return NULL; + nbp = erts_realloc_fnf(type, (void *) bp, bsize); ERTS_CHK_BIN_ALIGNMENT(nbp); return nbp; } @@ -281,17 +288,14 @@ erts_bin_realloc(Binary *bp, Uint size) { Binary *nbp; Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD; + ErtsAlcType_t type = (bp->flags & BIN_FLAG_DRV) ? ERTS_ALC_T_DRV_BINARY + : ERTS_ALC_T_BINARY; ASSERT((bp->flags & BIN_FLAG_MAGIC) == 0); - if (bp->flags & BIN_FLAG_DRV) - nbp = erts_realloc_fnf(ERTS_ALC_T_DRV_BINARY, (void *) bp, bsize); - else - nbp = erts_realloc_fnf(ERTS_ALC_T_BINARY, (void *) bp, bsize); + if (bsize < size) /* overflow */ + erts_realloc_enomem(type, bp, size); + nbp = erts_realloc_fnf(type, (void *) bp, bsize); if (!nbp) - erts_realloc_n_enomem(ERTS_ALC_T2N(bp->flags & BIN_FLAG_DRV - ? ERTS_ALC_T_DRV_BINARY - : ERTS_ALC_T_BINARY), - bp, - bsize); + erts_realloc_enomem(type, bp, bsize); ERTS_CHK_BIN_ALIGNMENT(nbp); return nbp; } @@ -312,6 +316,7 @@ erts_create_magic_binary(Uint size, void (*destructor)(Binary *)) { Uint bsize = ERTS_MAGIC_BIN_SIZE(size); Binary* bptr = erts_alloc_fnf(ERTS_ALC_T_BINARY, bsize); + ASSERT(bsize > size); if (!bptr) erts_alloc_n_enomem(ERTS_ALC_T2N(ERTS_ALC_T_BINARY), bsize); ERTS_CHK_BIN_ALIGNMENT(bptr); diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 05f07e57b2..3d8dd9c6d0 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -274,6 +274,7 @@ __decl_noreturn void __noreturn erl_assert_error(const char* expr, const char *f typedef unsigned int Eterm; typedef unsigned int Uint; typedef int Sint; +#define ERTS_UINT_MAX UINT_MAX #define ERTS_SIZEOF_ETERM SIZEOF_INT #define ErtsStrToSint strtol #else @@ -347,6 +348,7 @@ typedef long long Sint; typedef Uint UWord; typedef Sint SWord; +#define ERTS_UINT_MAX ERTS_UWORD_MAX #endif /* HALFWORD_HEAP */ -- cgit v1.2.3 From 50169c34511242987e6f2a047c79a0f7e1b7cfcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 25 Jun 2014 16:56:55 +0200 Subject: erts: Separate ethread inlining from ethread.h The commit adb5dc0090bc419e2c4c1250653badbddeb6263b (ETHR_FORCE_INLINE) broke some platforms without adequate thread support. --- erts/include/internal/ethread.h | 26 +----------------- erts/include/internal/ethread_inline.h | 49 ++++++++++++++++++++++++++++++++++ erts/lib_src/Makefile.in | 1 + erts/lib_src/common/erl_misc_utils.c | 2 +- 4 files changed, 52 insertions(+), 26 deletions(-) create mode 100644 erts/include/internal/ethread_inline.h (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 31d19902f5..72c054b588 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -31,6 +31,7 @@ #endif #include +#include "ethread_inline.h" #include "erl_errno.h" #if defined(DEBUG) @@ -51,31 +52,6 @@ # endif #endif -#if !defined(__GNUC__) -# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) 0 -#elif !defined(__GNUC_MINOR__) -# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ - ((__GNUC__ << 24) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) -#elif !defined(__GNUC_PATCHLEVEL__) -# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ - (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12)) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) -#else -# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ - (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12) | __GNUC_PATCHLEVEL__) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) -#endif - -#undef ETHR_INLINE -#if defined(__GNUC__) -# define ETHR_INLINE __inline__ -# if ETHR_AT_LEAST_GCC_VSN__(3, 1, 1) -# define ETHR_FORCE_INLINE __inline__ __attribute__((__always_inline__)) -# else -# define ETHR_FORCE_INLINE __inline__ -# endif -#elif defined(__WIN32__) -# define ETHR_INLINE __forceinline -# define ETHR_FORCE_INLINE __forceinline -#endif #if defined(ETHR_DEBUG) || !defined(ETHR_INLINE) || ETHR_XCHK \ || (defined(__GNUC__) && defined(ERTS_MIXED_CYGWIN_VC)) # undef ETHR_INLINE diff --git a/erts/include/internal/ethread_inline.h b/erts/include/internal/ethread_inline.h new file mode 100644 index 0000000000..ffb756c84f --- /dev/null +++ b/erts/include/internal/ethread_inline.h @@ -0,0 +1,49 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2004-2014. All Rights Reserved. + * + * The 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 ETHREAD_INLINE_H__ +#define ETHREAD_INLINE_H__ + +#if !defined(__GNUC__) +# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) 0 +#elif !defined(__GNUC_MINOR__) +# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ + ((__GNUC__ << 24) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) +#elif !defined(__GNUC_PATCHLEVEL__) +# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ + (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12)) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) +#else +# define ETHR_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ + (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12) | __GNUC_PATCHLEVEL__) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) +#endif + +#undef ETHR_INLINE +#if defined(__GNUC__) +# define ETHR_INLINE __inline__ +# if ETHR_AT_LEAST_GCC_VSN__(3, 1, 1) +# define ETHR_FORCE_INLINE __inline__ __attribute__((__always_inline__)) +# else +# define ETHR_FORCE_INLINE __inline__ +# endif +#elif defined(__WIN32__) +# define ETHR_INLINE __forceinline +# define ETHR_FORCE_INLINE __forceinline +#endif + +#endif /* #ifndef ETHREAD_INLINE_H__ */ diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in index cf1aef518a..b680c03b1d 100644 --- a/erts/lib_src/Makefile.in +++ b/erts/lib_src/Makefile.in @@ -465,6 +465,7 @@ RELEASE_LIBS=$(ERTS_LIBS) INTERNAL_RELEASE_INCLUDES= \ $(ERTS_INCL_INT)/README \ $(ERTS_INCL_INT)/ethread.h \ + $(ERTS_INCL_INT)/ethread_inline.h \ $(ERTS_INCL_INT)/ethr_mutex.h \ $(ERTS_INCL_INT)/ethr_optimized_fallbacks.h \ $(ERTS_INCL_INT)/ethr_atomics.h \ diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c index 8bf7656bb0..d58a28b5cb 100644 --- a/erts/lib_src/common/erl_misc_utils.c +++ b/erts/lib_src/common/erl_misc_utils.c @@ -25,7 +25,7 @@ # include #endif -#include "ethread.h" +#include "ethread_inline.h" #include "erl_misc_utils.h" #if defined(__WIN32__) -- cgit v1.2.3 From fbb4d5a85baaeb0a946a00e5994fc1f4a12b2855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 30 Jun 2014 15:39:05 +0200 Subject: vxworks: Fix SYSTEMD_DAEMON_LIBS in configure --- erts/autoconf/vxworks/sed.general | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general index dbb9420b67..efa4e99054 100644 --- a/erts/autoconf/vxworks/sed.general +++ b/erts/autoconf/vxworks/sed.general @@ -57,6 +57,7 @@ s|@ETHR_LIB_NAME@|| s|@ETHR_DEFS@|| s|@ETHR_THR_LIB_BASE@|| s|@ETHR_THR_LIB_BASE_DIR@|| +s|@SYSTEMD_DAEMON_LIBS@|| s|@EMU_THR_DEFS@|| s|@EMU_THR_LIBS@|| s|@EMU_THR_LIB_NAME@|ethread| -- cgit v1.2.3 From dba1d881f3232a939c6620f5fd6b4a97ff454bee Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 1 Jul 2014 13:58:47 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 17 +++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 68875fe56f..5f39822712 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,23 @@

This document describes the changes made to the ERTS application.

+
Erts 6.1.1 + +
Fixed Bugs and Malfunctions + + +

+ Fixed ETHR_FORCE_INLINE which caused the build to break + on some platforms without adequate thread support + (VxWorks).

+

+ Own Id: OTP-12010

+
+
+
+ +
+
Erts 6.1
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 2e773079f3..96edae99d9 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.1 +VSN = 6.1.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From acf19fc9190985f643af06293141a1083f032563 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Wed, 23 Oct 2013 19:17:11 +0200 Subject: Use offsetof() in io.c This silences the following UBSan errors: beam/io.c:7131:27: runtime error: member access within null pointer of type 'ErlDrvSysInfo' beam/io.c:7140:20: runtime error: member access within null pointer of type 'ErlDrvSysInfo' beam/io.c:7166:20: runtime error: member access within null pointer of type 'ErlDrvSysInfo' beam/io.c:7174:20: runtime error: member access within null pointer of type 'ErlDrvSysInfo' --- erts/emulator/beam/io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index edf4a28784..c34a5b17d0 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -7166,7 +7166,7 @@ char *driver_dl_error(void) #define ERL_DRV_SYS_INFO_SIZE(LAST_FIELD) \ - (((size_t) &((ErlDrvSysInfo *) 0)->LAST_FIELD) \ + (offsetof(ErlDrvSysInfo, LAST_FIELD) \ + sizeof(((ErlDrvSysInfo *) 0)->LAST_FIELD)) void -- cgit v1.2.3 From a8cbf025f6e20a68b6575747200be149c6c09932 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Wed, 23 Oct 2013 19:34:57 +0200 Subject: Properly handle SINT_MIN in small_to_big() As there is no overflow for signed integers, -SINT_MIN is undefined behaviour and the cast to unsigned needs to happen before negation. SINT_MIN denotes the minimum value that can be stored in the Sint type. beam/big.c:1512:6: runtime error: negation of -9223372036854775808 cannot be represented in type 'Sint' (aka 'long'); cast to an unsigned type to negate this value to itself --- erts/emulator/beam/big.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 41a041eba6..4d087bf967 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1506,13 +1506,15 @@ Eterm uword_to_big(UWord x, Eterm *y) */ Eterm small_to_big(Sint x, Eterm *y) { + Uint xu; if (x >= 0) { + xu = x; *y = make_pos_bignum_header(1); } else { - x = -x; + xu = -(Uint)x; *y = make_neg_bignum_header(1); } - BIG_DIGIT(y, 0) = x; + BIG_DIGIT(y, 0) = xu; return make_big(y); } -- cgit v1.2.3 From 61fefeb9d96b4899efbc945138b23b73cb9b6ac9 Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Fri, 20 Jun 2014 10:55:29 +0100 Subject: erlang:statistics(runtime) returns milliseconds Specify in the documentation that erlang:statistics(runtime) returns milliseconds. --- erts/doc/src/erlang.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 9ad42374bf..84168397f6 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4968,7 +4968,7 @@ true

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.

+ than the wall-clock time. The time is returned in milliseconds.

 > statistics(runtime).
 {1690,1620}
-- 
cgit v1.2.3


From de2fb97f15ac98aa1d1c5533aacca378334f4778 Mon Sep 17 00:00:00 2001
From: Anthony Ramine 
Date: Tue, 1 Jul 2014 00:58:26 +0200
Subject: Fix handling of broken symlinks in filelib

This fixes a bug introduced in f11aabdc9fec593c31e6c4f3fa25c1707e9c35df where
filelib:eval_read_file_info/2 was made to use file:read_link_info/1 to never
follow symlinks. This fixed wildcard/1 but broke every other function using
eval_read_file_info/2.

Reported-by: Louis-Philippe Gauthier
Reported-by: Danil Zagoskin
---
 erts/doc/src/erl_prim_loader.xml       | 16 ++++++++++++++
 erts/preloaded/src/erl_prim_loader.erl | 38 ++++++++++++++++++++++++++--------
 2 files changed, 45 insertions(+), 9 deletions(-)

(limited to 'erts')

diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml
index 6751deda4d..171f84decc 100644
--- a/erts/doc/src/erl_prim_loader.xml
+++ b/erts/doc/src/erl_prim_loader.xml
@@ -147,6 +147,22 @@
           See code(3) about archive files.

+ + + Get information about a link or file + +

This function works like + read_file_info/1 + except that if Filename is a symbolic link, + information about the link will be returned in the file_info + record and the type field of the record will be set to + symlink.

+

If Filename is not a symbolic link, this function + returns exactly the same result as read_file_info/1. + On platforms that do not support symbolic links, this function + is always equivalent to read_file_info/1.

+
+
Set the path of the loader diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 578913b633..466e0b0020 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -42,11 +42,11 @@ %% Public -export([start/3, set_path/1, get_path/0, get_file/1, get_files/2, - list_dir/1, read_file_info/1, get_cwd/0, get_cwd/1]). + list_dir/1, read_file_info/1, read_link_info/1, get_cwd/0, get_cwd/1]). %% Used by erl_boot_server -export([prim_init/0, prim_get_file/2, prim_list_dir/2, - prim_read_file_info/2, prim_get_cwd/2]). + prim_read_file_info/3, prim_get_cwd/2]). %% Used by escript and code -export([set_primary_archive/4, release_archives/0]). @@ -223,6 +223,12 @@ list_dir(Dir) -> read_file_info(File) -> check_file_result(read_file_info, File, request({read_file_info,File})). +-spec read_link_info(Filename) -> {'ok', FileInfo} | 'error' when + Filename :: string(), + FileInfo :: file:file_info(). +read_link_info(File) -> + check_file_result(read_link_info, File, request({read_link_info,File})). + -spec get_cwd() -> {'ok', string()} | 'error'. get_cwd() -> check_file_result(get_cwd, [], request({get_cwd,[]})). @@ -325,6 +331,9 @@ loop(State, Parent, Paths) -> {read_file_info,File} -> {Res,State1} = handle_read_file_info(State, File), {Res,State1,Paths}; + {read_link_info,File} -> + {Res,State1} = handle_read_link_info(State, File), + {Res,State1,Paths}; {get_cwd,[]} -> {Res,State1} = handle_get_cwd(State, []), {Res,State1,Paths}; @@ -387,10 +396,15 @@ handle_list_dir(State = #state{loader = inet}, Dir) -> ?SAFE2(inet_list_dir(State, Dir), State). handle_read_file_info(State = #state{loader = efile}, File) -> - ?SAFE2(efile_read_file_info(State, File), State); + ?SAFE2(efile_read_file_info(State, File, true), State); handle_read_file_info(State = #state{loader = inet}, File) -> ?SAFE2(inet_read_file_info(State, File), State). +handle_read_link_info(State = #state{loader = efile}, File) -> + ?SAFE2(efile_read_file_info(State, File, false), State); +handle_read_link_info(State = #state{loader = inet}, File) -> + ?SAFE2(inet_read_link_info(State, File), State). + handle_get_cwd(State = #state{loader = efile}, Drive) -> ?SAFE2(efile_get_cwd(State, Drive), State); handle_get_cwd(State = #state{loader = inet}, Drive) -> @@ -514,8 +528,8 @@ efile_list_dir(#state{prim_state = PS} = State, Dir) -> {Res, PS2} = prim_list_dir(PS, Dir), {Res, State#state{prim_state = PS2}}. -efile_read_file_info(#state{prim_state = PS} = State, File) -> - {Res, PS2} = prim_read_file_info(PS, File), +efile_read_file_info(#state{prim_state = PS} = State, File, FollowLinks) -> + {Res, PS2} = prim_read_file_info(PS, File, FollowLinks), {Res, State#state{prim_state = PS2}}. efile_get_cwd(#state{prim_state = PS} = State, Drive) -> @@ -718,6 +732,10 @@ inet_list_dir(State, Dir) -> inet_read_file_info(State, File) -> inet_send_and_rcv({read_file_info,File}, read_file_info, State). +%% -> {{ok,Info},State} | {{error,Reason},State} +inet_read_link_info(State, File) -> + inet_send_and_rcv({read_link_info,File}, read_link_info, State). + %% -> {{ok,Cwd},State} | {{error,Reason},State} inet_get_cwd(State, []) -> inet_send_and_rcv(get_cwd, get_cwd, State); @@ -951,16 +969,18 @@ prim_list_dir(PS, Dir) -> debug(PS, {return, Res2}), {Res2, PS3}. --spec prim_read_file_info(prim_state(), file:filename()) -> +-spec prim_read_file_info(prim_state(), file:filename(), boolean()) -> {{'ok', #file_info{}}, prim_state()} | {{'error', term()}, prim_state()}. -prim_read_file_info(PS, File) -> +prim_read_file_info(PS, File, FollowLinks) -> debug(PS, {read_file_info, File}), {Res2, PS2} = case name_split(PS#prim_state.primary_archive, File) of {file, PrimFile} -> - Res = prim_file:read_file_info(PrimFile), - {Res, PS}; + case FollowLinks of + true -> {prim_file:read_file_info(PrimFile), PS}; + false -> {prim_file:read_link_info(PrimFile), PS} + end; {archive, ArchiveFile, []} -> %% Fake top directory debug(PS, {archive_read_file_info, ArchiveFile}), -- cgit v1.2.3 From d9cc32f6dccbea95817598594bbb58c7508959b7 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 9 Jul 2014 15:05:47 +0200 Subject: Update preloaded --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 54768 -> 56120 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index f1e588320b..0a87e2b5ab 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ -- cgit v1.2.3 From bbfc75ea8795d26a2fe9254f3f646e761f2ad61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 4 Jul 2014 19:34:24 +0200 Subject: erts: Add histogram to lcnt --- erts/emulator/beam/erl_bif_info.c | 48 +++++++----- erts/emulator/beam/erl_lock_count.c | 149 ++++++++++++++++++++---------------- erts/emulator/beam/erl_lock_count.h | 20 ++++- erts/emulator/beam/io.c | 2 +- 4 files changed, 129 insertions(+), 90 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 4d5e55aaf5..6915765dab 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3856,16 +3856,19 @@ static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_s Uint tries = 0, colls = 0; unsigned long timer_s = 0, timer_ns = 0, timer_n = 0; unsigned int line = 0; + unsigned int i; Eterm af, uil; Eterm uit, uic; Eterm uits, uitns, uitn; Eterm tt, tstat, tloc, t; + Eterm thist, vhist[ERTS_LCNT_HISTOGRAM_SLOT_SIZE]; /* term: - * [{{file, line}, {tries, colls, {seconds, nanoseconds, n_blocks}}}] + * [{{file, line}, {tries, colls, {seconds, nanoseconds, n_blocks}}, + * { .. histogram .. }] */ - + tries = (Uint) ethr_atomic_read(&stats->tries); colls = (Uint) ethr_atomic_read(&stats->colls); @@ -3874,23 +3877,27 @@ static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_s timer_ns = stats->timer.ns; timer_n = stats->timer_n; - af = erts_atom_put(stats->file, strlen(stats->file), ERTS_ATOM_ENC_LATIN1, 1); + af = erts_atom_put((byte *)stats->file, strlen(stats->file), ERTS_ATOM_ENC_LATIN1, 1); uil = erts_bld_uint( hpp, szp, line); tloc = erts_bld_tuple(hpp, szp, 2, af, uil); - uit = erts_bld_uint( hpp, szp, tries); - uic = erts_bld_uint( hpp, szp, colls); - + uit = erts_bld_uint( hpp, szp, tries); + uic = erts_bld_uint( hpp, szp, colls); + uits = erts_bld_uint( hpp, szp, timer_s); uitns = erts_bld_uint( hpp, szp, timer_ns); uitn = erts_bld_uint( hpp, szp, timer_n); tt = erts_bld_tuple(hpp, szp, 3, uits, uitns, uitn); tstat = erts_bld_tuple(hpp, szp, 3, uit, uic, tt); - - t = erts_bld_tuple(hpp, szp, 2, tloc, tstat); - - res = erts_bld_cons( hpp, szp, t, res); + + for(i = 0; i < ERTS_LCNT_HISTOGRAM_SLOT_SIZE; i++) { + vhist[i] = erts_bld_uint(hpp, szp, stats->hist.ns[i]); + } + thist = erts_bld_tuplev(hpp, szp, ERTS_LCNT_HISTOGRAM_SLOT_SIZE, vhist); + + t = erts_bld_tuple(hpp, szp, 3, tloc, tstat, thist); + res = erts_bld_cons( hpp, szp, t, res); return res; } @@ -3911,13 +3918,13 @@ static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_t *lock ASSERT(ltype); - type = erts_atom_put(ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1); - name = erts_atom_put(lock->name, strlen(lock->name), ERTS_ATOM_ENC_LATIN1, 1); + type = erts_atom_put((byte *)ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1); + name = erts_atom_put((byte *)lock->name, strlen(lock->name), ERTS_ATOM_ENC_LATIN1, 1); if (lock->flag & ERTS_LCNT_LT_ALLOC) { /* use allocator types names as id's for allocator locks */ ltype = (char *) ERTS_ALC_A2AD(signed_val(lock->id)); - id = erts_atom_put(ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1); + id = erts_atom_put((byte *)ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1); } else if (lock->flag & ERTS_LCNT_LT_PROCLOCK) { /* use registered names as id's for process locks if available */ proc = erts_proc_lookup(lock->id); @@ -3928,16 +3935,15 @@ static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_t *lock id = lock->id; } } else { - id = lock->id; + id = lock->id; } - + for (i = 0; i < lock->n_stats; i++) { stats = lcnt_build_lock_stats_term(hpp, szp, &(lock->stats[i]), stats); } - - t = erts_bld_tuple(hpp, szp, 4, name, id, type, stats); - - res = erts_bld_cons( hpp, szp, t, res); + + t = erts_bld_tuple(hpp, szp, 4, name, id, type, stats); + res = erts_bld_cons( hpp, szp, t, res); return res; } @@ -3957,12 +3963,12 @@ static Eterm lcnt_build_result_term(Eterm **hpp, Uint *szp, erts_lcnt_data_t *da dtns = erts_bld_uint( hpp, szp, data->duration.ns); tdt = erts_bld_tuple(hpp, szp, 2, dts, dtns); - adur = erts_atom_put(str_duration, strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1); + adur = erts_atom_put((byte *)str_duration, strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1); tdur = erts_bld_tuple(hpp, szp, 2, adur, tdt); /* lock tuple */ - aloc = erts_atom_put(str_locks, strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1); + aloc = erts_atom_put((byte *)str_locks, strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1); for (lock = data->current_locks->head; lock != NULL ; lock = lock->next ) { lloc = lcnt_build_lock_term(hpp, szp, lock, lloc); diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index 6f44bf097b..17ddfdc8ae 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -61,6 +61,25 @@ static ERTS_INLINE void lcnt_unlock(void) { ethr_mutex_unlock(&lcnt_data_lock); } +const int log2_tab64[64] = { + 63, 0, 58, 1, 59, 47, 53, 2, + 60, 39, 48, 27, 54, 33, 42, 3, + 61, 51, 37, 40, 49, 18, 28, 20, + 55, 30, 34, 11, 43, 14, 22, 4, + 62, 57, 46, 52, 38, 26, 32, 41, + 50, 36, 17, 19, 29, 10, 13, 21, + 56, 45, 25, 31, 35, 16, 9, 12, + 44, 24, 15, 8, 23, 7, 6, 5}; + +static ERTS_INLINE int lcnt_log2(Uint64 v) { + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + return log2_tab64[((Uint64)((v - (v >> 1))*0x07EDD5E59A4E28C2)) >> 58]; +} static char* lcnt_lock_type(Uint16 flag) { switch(flag & ERTS_LCNT_LT_ALL) { @@ -81,19 +100,20 @@ static void lcnt_clear_stats(erts_lcnt_lock_stats_t *stats) { stats->timer_n = 0; stats->file = (char *)str_undefined; stats->line = 0; + sys_memzero(stats->hist.ns, sizeof(stats->hist.ns)); } static void lcnt_time(erts_lcnt_time_t *time) { -#ifdef HAVE_GETHRTIME +#if 0 || defined(HAVE_GETHRTIME) SysHrTime hr_time; hr_time = sys_gethrtime(); time->s = (unsigned long)(hr_time / 1000000000LL); time->ns = (unsigned long)(hr_time - 1000000000LL*time->s); -#else - SysTimeval tv; - sys_gettimeofday(&tv); - time->s = tv.tv_sec; - time->ns = tv.tv_usec*1000LL; +#else + SysTimeval tv; + sys_gettimeofday(&tv); + time->s = tv.tv_sec; + time->ns = tv.tv_usec*1000LL; #endif } @@ -111,22 +131,20 @@ static void lcnt_time_diff(erts_lcnt_time_t *d, erts_lcnt_time_t *t1, erts_lcnt_ dns += 1000000000LL; } + ASSERT(ds >= 0); + d->s = ds; d->ns = dns; } -/* difference d must be positive */ +/* difference d must be non-negative */ static void lcnt_time_add(erts_lcnt_time_t *t, erts_lcnt_time_t *d) { - unsigned long ngns = 0; - t->s += d->s; t->ns += d->ns; - ngns = t->ns / 1000000000LL; + t->s += t->ns / 1000000000LL; t->ns = t->ns % 1000000000LL; - - t->s += ngns; } static erts_lcnt_thread_data_t *lcnt_thread_data_alloc(void) { @@ -158,59 +176,64 @@ static char* lock_opt(Uint16 flag) { return "--"; } -static void print_lock_x(erts_lcnt_lock_t *lock, Uint16 flag, char *action, char *extra) { - erts_aint_t colls, tries, w_state, r_state; - erts_lcnt_lock_stats_t *stats = NULL; - +static void print_lock_x(erts_lcnt_lock_t *lock, Uint16 flag, char *action) { + erts_aint_t w_state, r_state; char *type; - int i; - + + if (strcmp(lock->name, "run_queue") != 0) return; type = lcnt_lock_type(lock->flag); r_state = ethr_atomic_read(&lock->r_state); w_state = ethr_atomic_read(&lock->w_state); - if (lock->flag & flag) { - erts_printf("%20s [%30s] [r/w state %4ld/%4ld] id %T %s\r\n", - action, - lock->name, - r_state, - w_state, - lock->id, - extra); + erts_fprintf(stderr,"%10s [%24s] [r/w state %4ld/%4ld] %2s id %T\r\n", + action, + lock->name, + r_state, + w_state, + type, + lock->id); } } - -static void print_lock(erts_lcnt_lock_t *lock, char *action) { - if (strcmp(lock->name, "proc_main") == 0) { - print_lock_x(lock, ERTS_LCNT_LT_ALL, action, ""); - } -} - #endif static erts_lcnt_lock_stats_t *lcnt_get_lock_stats(erts_lcnt_lock_t *lock, char *file, unsigned int line) { unsigned int i; erts_lcnt_lock_stats_t *stats = NULL; - - for (i = 0; i < lock->n_stats; i++) { - if ((lock->stats[i].file == file) && (lock->stats[i].line == line)) { - return &(lock->stats[i]); - } - } - if (lock->n_stats < ERTS_LCNT_MAX_LOCK_LOCATIONS) { - stats = &lock->stats[lock->n_stats]; - lock->n_stats++; - stats->file = file; - stats->line = line; - return stats; + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_LOCATION) { + for (i = 0; i < lock->n_stats; i++) { + if ((lock->stats[i].file == file) && (lock->stats[i].line == line)) { + return &(lock->stats[i]); + } + } + if (lock->n_stats < ERTS_LCNT_MAX_LOCK_LOCATIONS) { + stats = &lock->stats[lock->n_stats]; + lock->n_stats++; + stats->file = file; + stats->line = line; + return stats; + } } return &lock->stats[0]; +} + +static void lcnt_update_stats_hist(erts_lcnt_hist_t *hist, erts_lcnt_time_t *time_wait) { + int idx; + unsigned long r; + if (time_wait->s > 0 || time_wait->ns > ERTS_LCNT_HISTOGRAM_MAX_NS) { + idx = ERTS_LCNT_HISTOGRAM_SLOT_SIZE - 1; + } else { + r = time_wait->ns >> ERTS_LCNT_HISTOGRAM_RSHIFT; + if (r) idx = lcnt_log2(r); + else idx = 0; + } + hist->ns[idx]++; } -static void lcnt_update_stats(erts_lcnt_lock_stats_t *stats, int lock_in_conflict, erts_lcnt_time_t *time_wait) { +static void lcnt_update_stats(erts_lcnt_lock_stats_t *stats, int lock_in_conflict, + erts_lcnt_time_t *time_wait) { ethr_atomic_inc(&stats->tries); @@ -220,6 +243,7 @@ static void lcnt_update_stats(erts_lcnt_lock_stats_t *stats, int lock_in_conflic if (time_wait) { lcnt_time_add(&(stats->timer), time_wait); stats->timer_n++; + lcnt_update_stats_hist(&stats->hist,time_wait); } } @@ -330,8 +354,9 @@ void erts_lcnt_list_delete(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock) /* interface to erl_threads.h */ /* only lock on init and destroy, all others should use atomics */ void erts_lcnt_init_lock(erts_lcnt_lock_t *lock, char *name, Uint16 flag ) { - erts_lcnt_init_lock_x(lock, name, flag, am_undefined); + erts_lcnt_init_lock_x(lock, name, flag, NIL); } + void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id) { int i; if (!name) { @@ -360,7 +385,6 @@ void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eter } erts_lcnt_list_insert(erts_lcnt_data->current_locks, lock); - lcnt_unlock(); } @@ -417,8 +441,9 @@ void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option) { if ((w_state > 0) || (r_state > 0)) { eltd->lock_in_conflict = 1; - if (eltd->timer_set == 0) + if (eltd->timer_set == 0) { lcnt_time(&eltd->timer); + } eltd->timer_set++; } else { eltd->lock_in_conflict = 0; @@ -433,7 +458,7 @@ void erts_lcnt_lock(erts_lcnt_lock_t *lock) { if (!ERTS_LCNT_LOCK_TYPE(lock)) return; w_state = ethr_atomic_read(&lock->w_state); - ethr_atomic_inc( &lock->w_state); + ethr_atomic_inc(&lock->w_state); eltd = lcnt_get_thread_data(); @@ -446,10 +471,10 @@ void erts_lcnt_lock(erts_lcnt_lock_t *lock) { * 'atomicly'. All other locks will block the thread if w_state > 0 * i.e. locked. */ - if (eltd->timer_set == 0) + if (eltd->timer_set == 0) { lcnt_time(&eltd->timer); + } eltd->timer_set++; - } else { eltd->lock_in_conflict = 0; } @@ -459,11 +484,10 @@ void erts_lcnt_lock(erts_lcnt_lock_t *lock) { void erts_lcnt_lock_unaquire(erts_lcnt_lock_t *lock) { /* should check if this thread was "waiting" */ - if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; if (!ERTS_LCNT_LOCK_TYPE(lock)) return; - ethr_atomic_dec( &lock->w_state); + ethr_atomic_dec(&lock->w_state); } /* erts_lcnt_lock_post @@ -491,7 +515,7 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line if (!(lock->flag & (ERTS_LCNT_LT_RWMUTEX | ERTS_LCNT_LT_RWSPINLOCK))) { flowstate = ethr_atomic_read(&lock->flowstate); ASSERT(flowstate == 0); - ethr_atomic_inc( &lock->flowstate); + ethr_atomic_inc(&lock->flowstate); } #endif @@ -500,19 +524,12 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line ASSERT(eltd); /* if lock was in conflict, time it */ - - if (erts_lcnt_rt_options & ERTS_LCNT_OPT_LOCATION) { - stats = lcnt_get_lock_stats(lock, file, line); - } else { - stats = &lock->stats[0]; - } - + stats = lcnt_get_lock_stats(lock, file, line); if (eltd->timer_set) { lcnt_time(&timer); lcnt_time_diff(&time_wait, &timer, &(eltd->timer)); lcnt_update_stats(stats, eltd->lock_in_conflict, &time_wait); - eltd->timer_set--; ASSERT(eltd->timer_set >= 0); } else { @@ -541,11 +558,11 @@ void erts_lcnt_unlock(erts_lcnt_lock_t *lock) { /* flowstate */ flowstate = ethr_atomic_read(&lock->flowstate); ASSERT(flowstate == 1); - ethr_atomic_dec( &lock->flowstate); + ethr_atomic_dec(&lock->flowstate); /* write state */ w_state = ethr_atomic_read(&lock->w_state); - ASSERT(w_state > 0) + ASSERT(w_state > 0); #endif ethr_atomic_dec(&lock->w_state); } @@ -582,9 +599,7 @@ void erts_lcnt_trylock(erts_lcnt_lock_t *lock, int res) { ethr_atomic_inc( &lock->flowstate); #endif ethr_atomic_inc(&lock->w_state); - lcnt_update_stats(&(lock->stats[0]), 0, NULL); - } else { ethr_atomic_inc(&lock->stats[0].tries); ethr_atomic_inc(&lock->stats[0].colls); diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h index 75f7cd028b..ffbb93da1b 100644 --- a/erts/emulator/beam/erl_lock_count.h +++ b/erts/emulator/beam/erl_lock_count.h @@ -35,6 +35,10 @@ * | | | - collisions (including trylock busy) * | | | - timer (time spent in waiting for lock) * | | | - n_timer (collisions excluding trylock busy) + * | | | - histogram + * | | | | - # 0 = log2(lock wait_time ns) + * | | | | - ... + * | | | | - # n = log2(lock wait_time ns) * * Each instance of a lock is the unique lock, i.e. set and id in that set. * For each lock there is a set of statistics with where and what impact @@ -68,8 +72,17 @@ #include "ethread.h" +#define ERTS_LCNT_MAX_LOCK_LOCATIONS (10) -#define ERTS_LCNT_MAX_LOCK_LOCATIONS (10) +/* histogram */ +#define ERTS_LCNT_HISTOGRAM_MAX_NS (((unsigned long)1LL << 28) - 1) +#if 0 || defined(HAVE_GETHRTIME) +#define ERTS_LCNT_HISTOGRAM_SLOT_SIZE (30) +#define ERTS_LCNT_HISTOGRAM_RSHIFT (0) +#else +#define ERTS_LCNT_HISTOGRAM_SLOT_SIZE (20) +#define ERTS_LCNT_HISTOGRAM_RSHIFT (10) +#endif #define ERTS_LCNT_LT_SPINLOCK (((Uint16) 1) << 0) #define ERTS_LCNT_LT_RWSPINLOCK (((Uint16) 1) << 1) @@ -104,6 +117,10 @@ typedef struct { extern erts_lcnt_time_t timer_start; +typedef struct { + Uint32 ns[ERTS_LCNT_HISTOGRAM_SLOT_SIZE]; /* log2 array of nano seconds occurences */ +} erts_lcnt_hist_t; + typedef struct erts_lcnt_lock_stats_s { /* "tries" and "colls" needs to be atomic since * trylock busy does not aquire a lock and there @@ -118,6 +135,7 @@ typedef struct erts_lcnt_lock_stats_s { unsigned long timer_n; /* #times waited for lock */ erts_lcnt_time_t timer; /* total wait time for lock */ + erts_lcnt_hist_t hist; } erts_lcnt_lock_stats_t; /* rw locks uses both states, other locks only uses w_state */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index edf4a28784..d028737664 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -6129,7 +6129,7 @@ driver_pdl_create(ErlDrvPort dp) return NULL; pdl = erts_alloc(ERTS_ALC_T_PORT_DATA_LOCK, sizeof(struct erl_drv_port_data_lock)); - erts_mtx_init(&pdl->mtx, "port_data_lock"); + erts_mtx_init_x(&pdl->mtx, "port_data_lock", pp->common.id, 1); pdl_init_refc(pdl); erts_port_inc_refc(pp); pdl->prt = pp; -- cgit v1.2.3 From 41d2dc56d5d2a3be1ba59c6999e1003360b1e308 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 10 Jul 2014 10:45:27 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 19 +++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 5f39822712..086d29c668 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,25 @@

This document describes the changes made to the ERTS application.

+
Erts 6.1.2 + +
Fixed Bugs and Malfunctions + + +

+ OTP-11850 fixed filelib:wildcard/1 to work with broken + symlinks. This correction, however, introduced problems + since symlinks were no longer followed for functions like + filelib:ensure_dir/1, filelib:is_dir/1, + filelib:file_size/1, etc. This is now corrected.

+

+ Own Id: OTP-12054 Aux Id: seq12660

+
+
+
+ +
+
Erts 6.1.1
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 96edae99d9..0db4370ea8 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.1.1 +VSN = 6.1.2 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From e1f903d5210fb5b91ff47229fb57bf3b0edeb79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 11 Jun 2014 23:27:06 +0200 Subject: erts: Introduce erlang:fun_info_mfa/1 Introduced for proc_lib:init_p/3 --- erts/emulator/beam/bif.tab | 4 ++++ erts/emulator/beam/erl_bif_info.c | 19 +++++++++++++++++++ erts/preloaded/src/erlang.erl | 11 ++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 011e49f1fe..e68b8e6274 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -600,6 +600,10 @@ bif maps:values/1 bif erts_internal:cmp_term/2 +# +# New in 17.1. +# +bif erlang:fun_info_mfa/1 # # Obsolete # diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 4d5e55aaf5..faa2655c11 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3055,6 +3055,25 @@ fun_info_2(BIF_ALIST_2) return TUPLE2(hp, what, val); } +BIF_RETTYPE +fun_info_mfa_1(BIF_ALIST_1) +{ + Process* p = BIF_P; + Eterm fun = BIF_ARG_1; + Eterm* hp; + + if (is_fun(fun)) { + ErlFunThing* funp = (ErlFunThing *) fun_val(fun); + hp = HAlloc(p, 4); + BIF_RET(TUPLE3(hp,funp->fe->module,funp->fe->address[-2],make_small(funp->arity))); + } else if (is_export(fun)) { + Export* exp = (Export *) ((UWord) (export_val(fun))[1]); + hp = HAlloc(p, 4); + BIF_RET(TUPLE3(hp,exp->code[0],exp->code[1],make_small(exp->code[2]))); + } + BIF_ERROR(p, BADARG); +} + BIF_RETTYPE is_process_alive_1(BIF_ALIST_1) { if(is_internal_pid(BIF_ARG_1)) { diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 4ff0513321..98d7a942a6 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -91,7 +91,7 @@ -export([external_size/2, finish_after_on_load/2, finish_loading/1, float/1]). -export([float_to_binary/1, float_to_binary/2, float_to_list/1, float_to_list/2]). --export([fun_info/2, fun_to_list/1, function_exported/3]). +-export([fun_info/2, fun_info_mfa/1, fun_to_list/1, function_exported/3]). -export([garbage_collect/0, garbage_collect/1, garbage_collect/2]). -export([garbage_collect_message_area/0, get/0, get/1, get_keys/1]). -export([get_module_info/1, get_stacktrace/0, group_leader/0]). @@ -827,6 +827,15 @@ float_to_list(_Float, _Options) -> fun_info(_Fun, _Item) -> erlang:nif_error(undefined). +%% fun_info_mfa/1 +-spec erlang:fun_info_mfa(Fun) -> {Mod, Name, Arity} when + Fun :: function(), + Mod :: atom(), + Name :: atom(), + Arity :: non_neg_integer(). +fun_info_mfa(_Fun) -> + erlang:nif_error(undefined). + %% fun_to_list/1 -spec erlang:fun_to_list(Fun) -> string() when Fun :: function(). -- cgit v1.2.3 From e47da13f6a0b73a2cae5d5f602ac25c4ff3c440d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 11 Jul 2014 17:43:32 +0200 Subject: erts: Test erlang:fun_info_mfa/1 --- erts/emulator/test/fun_SUITE.erl | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 8ad5f290ed..2968f5bebb 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -30,7 +30,7 @@ fun_to_port/1,t_hash/1,t_phash/1,t_phash2/1,md5/1, refc/1,refc_ets/1,refc_dist/1, const_propagation/1,t_arity/1,t_is_function2/1, - t_fun_info/1]). + t_fun_info/1,t_fun_info_mfa/1]). -export([nothing/0]). @@ -42,7 +42,8 @@ all() -> [bad_apply, bad_fun_call, badarity, ext_badarity, equality, ordering, fun_to_port, t_hash, t_phash, t_phash2, md5, refc, refc_ets, refc_dist, - const_propagation, t_arity, t_is_function2, t_fun_info]. + const_propagation, t_arity, t_is_function2, t_fun_info, + t_fun_info_mfa]. groups() -> []. @@ -824,6 +825,24 @@ t_fun_info(Config) when is_list(Config) -> ?line bad_info(<<1,2>>), ok. +t_fun_info_mfa(Config) when is_list(Config) -> + Fun1 = fun spawn_call/2, + {module,M1} = erlang:fun_info(Fun1, module), + {name,F1} = erlang:fun_info(Fun1, name), + {arity,A1} = erlang:fun_info(Fun1, arity), + {M1,F1,A1=2} = erlang:fun_info_mfa(Fun1), + %% Module fun. + Fun2 = fun ?MODULE:t_fun_info/1, + {module,M2} = erlang:fun_info(Fun2, module), + {name,F2} = erlang:fun_info(Fun2, name), + {arity,A2} = erlang:fun_info(Fun2, arity), + {M2,F2,A2=1} = erlang:fun_info_mfa(Fun2), + + %% Not fun. + {'EXIT',_} = (catch erlang:fun_info_mfa(id(d))), + ok. + + bad_info(Term) -> try erlang:fun_info(Term, module) of Any -> -- cgit v1.2.3 From cc894a72098322e12700e543297dc333b11951de Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Fri, 11 Jul 2014 21:24:50 +0200 Subject: Implement --enable-sanitizers[=sanitizers] Similar to debugging with Valgrind, it's very useful to enable -fsanitize= switches to catch bugs at runtime. $ ./configure Result: no sanitizer enabled $ ./configure --enable-sanitizers Result: -fsanitize=address,undefined $ ./configure --enable-sanitizers=address,thread,undefined Result: -fsanitize=address,thread,undefined $ ./configure --enable-sanitizers=undefined Result: -fsanitize=undefined --- erts/configure.in | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index f66110b98b..40b335849c 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -4820,6 +4820,26 @@ if test "x$GCC" = xyes; then CFLAGS="$WERRORFLAGS $CFLAGS" fi +dnl ---------------------------------------------------------------------- +dnl Enable -fsanitize= flags. +dnl ---------------------------------------------------------------------- + +m4_define(DEFAULT_SANITIZERS, [address,undefined]) +AC_ARG_ENABLE( + sanitizers, + AS_HELP_STRING( + [--enable-sanitizers@<:@=comma-separated list of sanitizers@:>@], + [Default=DEFAULT_SANITIZERS]), +[ +case "$enableval" in + no) sanitizers= ;; + yes) sanitizers="-fsanitize=DEFAULT_SANITIZERS" ;; + *) sanitizers="-fsanitize=$enableval" ;; +esac +CFLAGS="$CFLAGS $sanitizers" +LDFLAGS="$LDFLAGS $sanitizers" +]) + dnl ---------------------------------------------------------------------- dnl Output the result. dnl ---------------------------------------------------------------------- -- cgit v1.2.3 From f7cabadae0d786fbfc2be607efbd5b3f737cb0c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 14 Jul 2014 16:57:36 +0200 Subject: erts: Update preloaded erlang.beam Specs for erlang:fun_info_mfa/1 --- erts/preloaded/ebin/erlang.beam | Bin 97740 -> 97900 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 260badbcb3..cf3effc1e5 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ -- cgit v1.2.3 From e5ecd0d0924af44aaed9f59f61ef8430c8e1f355 Mon Sep 17 00:00:00 2001 From: Derek Brown Date: Mon, 21 Jul 2014 15:15:40 -0400 Subject: Fix minor grammatical errors in epmd docs Small grammar changes. --- erts/doc/src/epmd.xml | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml index 963d35c3c8..25f819ab50 100644 --- a/erts/doc/src/epmd.xml +++ b/erts/doc/src/epmd.xml @@ -58,12 +58,12 @@ of the IP address and a port number. The name of the node is an atom on the form of . The job of the daemon is to keep track of which - node name listens on which address. Hence, map + node name listens on which address. Hence, maps symbolic node names to machine addresses.

The TCP/IP epmd daemon actually only keeps track of - the Name (first) part of an Erlang node name, the Host - part (whatever is after the is implicit in the + the Name (first) part of an Erlang node name. The Host + part (whatever is after the ) is implicit in the node name where the epmd daemon was actually contacted, as is the IP address where the Erlang node can be reached. Consistent and correct TCP naming services are @@ -77,12 +77,12 @@

The daemon is started automatically by the erl command if the node is to be distributed and there is no running instance present. If automatically launched, - environment variables has to be used to alter the behavior of + environment variables have to be used to alter the behavior of the daemon. See the Environment variables section below.

-

If the -daemon argument is not given, the +

If the -daemon argument is not given, runs as a normal program with the controlling terminal of the shell in which it is started. Normally, it should run as a daemon.

@@ -122,7 +122,7 @@ comma-separated list of IP addresses and on the loopback address (which is implicitly added to the list if it has not been specified). This can also be set using the - environment variable, see the + environment variable. See the section Environment variables below.

@@ -130,7 +130,7 @@

Let this instance of epmd listen to another TCP port than default 4369. This can also be set using the - environment variable, see the + environment variable. See the section Environment variables below

@@ -153,7 +153,7 @@

With relaxed command checking, the epmd daemon can be killed from the localhost with i.e. epmd -kill even if there are active nodes registered. Normally only daemons with an empty node database can be killed with the epmd -kill command.

-

The epmd -stop command (and the corresponding messages to epmd, as can be given using erl_interface/ei) is normally always ignored, as it opens up for strange situation when two nodes of the same name can be alive at the same time. A node unregisters itself by just closing the connection to epmd, why the stop command was only intended for use in debugging situations.

+

The epmd -stop command (and the corresponding messages to epmd, as can be given using erl_interface/ei) is normally always ignored, as it opens up the possibility of a strange situation where two nodes of the same name can be alive at the same time. A node unregisters itself by just closing the connection to epmd, which is why the stop command was only intended for use in debugging situations.

With relaxed command checking enabled, you can forcibly unregister live nodes.

@@ -166,7 +166,7 @@
DbgExtra options -

These options are purely for debugging and testing epmd clients, they should not be used in normal operation.

+

These options are purely for debugging and testing epmd clients. They should not be used in normal operation.

@@ -177,9 +177,9 @@ -

To simulate a busy server you can insert a delay between epmd - gets notified about that a new connection is requested and - when the connections gets accepted.

+

To simulate a busy server you can insert a delay between when epmd + gets notified that a new connection is requested and + when the connection gets accepted.

@@ -191,15 +191,15 @@
Interactive options -

These options make epmd run as an interactive command displaying the results of sending queries ta an already running instance of epmd. The epmd contacted is always on the local node, but the -port option can be used to select between instances if several are running using different port on the host.

+

These options make epmd run as an interactive command, displaying the results of sending queries to an already running instance of epmd. The epmd contacted is always on the local node, but the -port option can be used to select between instances if several are running using different ports on the host.

Contacts the epmd listening on the given TCP port number (default 4369). This can also be set using the - environment variable, see the + environment variable. See the section Environment - variables below

+ variables below.

@@ -210,7 +210,7 @@

Kill the currently running epmd.

Killing the running epmd is only allowed if epmd - -names show an empty database or + -names shows an empty database or -relaxed_command_check was given when the running instance of epmd was started. Note that -relaxed_command_check is given when starting the @@ -228,7 +228,7 @@

This command can only be used when contacting epmd instances started with the -relaxed_command_check flag. Note that relaxed command checking has to be enabled for - the epmd daemon contacted, When running epmd + the epmd daemon contacted. When running epmd interactively, -relaxed_command_check has no effect.

@@ -259,7 +259,7 @@

If set prior to start, the epmd daemon will behave as if the -relaxed_command_check option was given at - start-up. If consequently setting this option before starting + start-up. Consequently, if this option is set before starting the Erlang virtual machine, the automatically started epmd will accept the -kill and -stop commands without restrictions.

@@ -287,8 +287,8 @@ remote hosts. However, only the query commands are answered (and acted upon) if the query comes from a remote host. It is always an error to try to register a nodename if the client is not a process - located on the same host as the epmd instance is running on, - why such requests are considered hostile and the connection is + located on the same host as the epmd instance is running on- + such requests are considered hostile and the connection is immediately closed.

The queries accepted from remote nodes are:

@@ -307,3 +307,4 @@ + -- cgit v1.2.3 From 8a6390613225ec3f3e2d5bbf537d2e0737342e87 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 11 Jul 2014 10:46:37 +0200 Subject: kernel: When doing an fdopen we now also bind the fd to the specified addr/port --- erts/emulator/drivers/common/inet_drv.c | 33 ++++++++++++++++++++++++-------- erts/preloaded/ebin/prim_inet.beam | Bin 72280 -> 72484 bytes erts/preloaded/src/prim_inet.erl | 9 +++++++-- erts/vsn.mk | 2 +- 4 files changed, 33 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 45fac69303..4f0bf1167e 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -4155,7 +4155,8 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, /* as inet_open but pass in an open socket (MUST BE OF RIGHT TYPE) */ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, - SOCKET s, char** rbuf, ErlDrvSizeT rsize) + SOCKET s, Uint32 bound, + char** rbuf, ErlDrvSizeT rsize) { inet_address name; unsigned int sz = sizeof(name); @@ -4172,7 +4173,12 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, #ifdef __WIN32__ driver_select(desc->port, desc->event, ERL_DRV_READ, 1); #endif - desc->state = INET_STATE_BOUND; /* assume bound */ + + if (bound) + desc->state = INET_STATE_BOUND; + else + desc->state = INET_STATE_OPEN; + if (type == SOCK_STREAM) { /* check if connected */ sz = sizeof(name); if (!IS_SOCKET_ERROR(sock_peer(s, (struct sockaddr*) &name, &sz))) @@ -8642,10 +8648,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, break; } - case INET_REQ_FDOPEN: { /* pass in an open socket */ + case INET_REQ_FDOPEN: { /* pass in an open (and optionally bound) socket */ int domain; + int bound; DEBUGF(("tcp_inet_ctl(%ld): FDOPEN\r\n", (long)desc->inet.port)); - if (len != 6) return ctl_error(EINVAL, rbuf, rsize); + if (len != 6 && len != 10) return ctl_error(EINVAL, rbuf, rsize); switch(buf[0]) { case INET_AF_INET: domain = AF_INET; @@ -8663,8 +8670,13 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_error(EINVAL, rbuf, rsize); } if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize); + + if (len == 6) bound = 1; + else bound = get_int32(buf+2+4); + return inet_ctl_fdopen(INETP(desc), domain, SOCK_STREAM, - (SOCKET) get_int32(buf+2), rbuf, rsize); + (SOCKET) get_int32(buf+2), + bound, rbuf, rsize); break; } @@ -10409,10 +10421,11 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return replen; - case INET_REQ_FDOPEN: { /* pass in an open (and bound) socket */ + case INET_REQ_FDOPEN: { /* pass in an open (and optionally bound) socket */ SOCKET s; + int bound; DEBUGF(("packet inet_ctl(%ld): FDOPEN\r\n", (long)desc->port)); - if (len != 6) { + if (len != 6 && len != 10) { return ctl_error(EINVAL, rbuf, rsize); } switch (buf[0]) { @@ -10437,7 +10450,11 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_error(EINVAL, rbuf, rsize); } s = (SOCKET)get_int32(buf+2); - replen = inet_ctl_fdopen(desc, af, type, s, rbuf, rsize); + + if (len == 6) bound = 1; + else bound = get_int32(buf+2+4); + + replen = inet_ctl_fdopen(desc, af, type, s, bound, rbuf, rsize); if ((*rbuf)[0] != INET_REP_ERROR) { if (desc->active) diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 656fbc6627..412ad8290b 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index a9df75327c..e3ba8a328a 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -25,7 +25,7 @@ %% Primitive inet_drv interface --export([open/3, open/4, fdopen/4, close/1]). +-export([open/3, open/4, fdopen/4, fdopen/5, close/1]). -export([bind/3, listen/1, listen/2, peeloff/2]). -export([connect/3, connect/4, async_connect/4]). -export([accept/1, accept/2, async_accept/2]). @@ -70,7 +70,12 @@ open(Protocol, Family, Type, Opts) -> open(Protocol, Family, Type, Opts, ?INET_REQ_OPEN, []). fdopen(Protocol, Family, Type, Fd) when is_integer(Fd) -> - open(Protocol, Family, Type, [], ?INET_REQ_FDOPEN, ?int32(Fd)). + fdopen(Protocol, Family, Type, Fd, true). + +fdopen(Protocol, Family, Type, Fd, Bound) + when is_integer(Fd), Bound == true orelse Bound == false -> + open(Protocol, Family, Type, [], ?INET_REQ_FDOPEN, + [?int32(Fd), enc_value_2(bool, Bound)]). open(Protocol, Family, Type, Opts, Req, Data) -> Drv = protocol2drv(Protocol), diff --git a/erts/vsn.mk b/erts/vsn.mk index 0355901877..af74fb96c9 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 5.10.4 +VSN = 5.10.4.1 SYSTEM_VSN = R16B03-1 # Port number 4365 in 4.2 -- cgit v1.2.3 From 03a1fcf5346233f76c8dc50844e132f4760154d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 26 May 2014 19:33:24 +0200 Subject: erts: Fix inet close on prebound fds Inet close must remove fd from select/poll without closing the fd. --- erts/emulator/drivers/common/inet_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 4f0bf1167e..4cc31c1344 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -4053,7 +4053,7 @@ static int erl_inet_close(inet_descriptor* desc) desc_close(desc); desc->state = INET_STATE_CLOSED; } else if (desc->prebound && (desc->s != INVALID_SOCKET)) { - sock_select(desc, FD_READ | FD_WRITE | FD_CLOSE, 0); + sock_select(desc, FD_READ | FD_WRITE | FD_CLOSE | ERL_DRV_USE_NO_CALLBACK, 0); desc->event_mask = 0; #ifdef __WIN32__ desc->forced_events = 0; -- cgit v1.2.3 From 65e335e255cb76d979f605ed34700e4e02041139 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 22 Jul 2014 18:01:29 +0200 Subject: Update release notes --- erts/doc/src/notes.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 8c008c493e..2fe8879b93 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,27 @@

This document describes the changes made to the ERTS application.

+
Erts 5.10.4.1 + +
Known Bugs and Problems + + +

+ When using gen_tcp:connect and the fd option with + port and/or ip, the port and + ip options were ignored. This has been fixed so + that if port and/or ip is specified + together with fd a bind is requested for that + fd. If port and/or ip is not + specified bind will not be called.

+

+ Own Id: OTP-12061

+
+
+
+ +
+
Erts 5.10.4
Fixed Bugs and Malfunctions -- cgit v1.2.3 From 62081266545df8f5eda8e2043f33055cfe575126 Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Fri, 25 Jul 2014 09:11:35 +0200 Subject: fix xml file merge messup --- erts/doc/src/notes.xml | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 50de3c5d4d..5c4bb3ed25 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -940,6 +940,12 @@ Thanks to Matwey V. Kornilov

Own Id: OTP-11829

+ + +
+ +
+
Erts 5.10.4.1
Known Bugs and Problems -- cgit v1.2.3 From 4007e9342c9d80808f856ce11391b664d1920d80 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 3 Jul 2014 14:47:33 +0200 Subject: Fix some spelling misstakes --- erts/configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index f66110b98b..06e2490e0c 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -446,13 +446,13 @@ else fi AC_ARG_ENABLE(static-nifs, -AS_HELP_STRING([--enable-static-nifs], [link nifs statically. If yes then all nifs in all Erlang/OTP applications will be statically linked into the main binary. It is also possible to give a list of nifs that should be linked statically. The list should be a comma seperated and contain the absolute path to a .a archive for each nif that is to be statically linked. The name of the .a archive has to be the same as the name of the nif. Note that you have to link any external dependencies that the nifs have to the main binary, so for the crypto nif you want to pass LIBS=-lcrypto to configure.]), +AS_HELP_STRING([--enable-static-nifs], [link nifs statically. If yes then all nifs in all Erlang/OTP applications will be statically linked into the main binary. It is also possible to give a list of nifs that should be linked statically. The list should be a comma separated and contain the absolute path to a .a archive for each nif that is to be statically linked. The name of the .a archive has to be the same as the name of the nif. Note that you have to link any external dependencies that the nifs have to the main binary, so for the crypto nif you want to pass LIBS=-lcrypto to configure.]), STATIC_NIFS="$enableval", STATIC_NIFS=no) AC_SUBST(STATIC_NIFS) AC_ARG_ENABLE(static-drivers, -AS_HELP_STRING([--enable-static-drivers], [comma seperated list of linked-in drivers to link statically with the main binary. The list should contain the absolute path to a .a archive for each driver that is to be statically linked. The name of the .a archive has to be the same as the name of the driver.]), +AS_HELP_STRING([--enable-static-drivers], [comma separated list of linked-in drivers to link statically with the main binary. The list should contain the absolute path to a .a archive for each driver that is to be statically linked. The name of the .a archive has to be the same as the name of the driver.]), STATIC_DRIVERS="$enableval", STATIC_DRIVERS=no) AC_SUBST(STATIC_DRIVERS) -- cgit v1.2.3 From 961cfeb7b30d721ac8264261d89bb7a4bd3182e5 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 5 Aug 2014 17:33:19 +0200 Subject: Fix abort of nosuspend-tasks in erts_port_task_schedule() The counter for the amount of outstanding data in the port queue became inconsistent when aborting nosuspend-signals in erts_port_task_schedule(). This since the counter was subtracted by the data size of the signal although the data size had never been added to it. This inconsistency caused the port queue to remain in a busy state forever. --- erts/emulator/beam/erl_port_task.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 31d9a1e26e..2fc95ed5d8 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -798,12 +798,13 @@ schedule_port_task_handle_list_free(ErtsPortTaskHandleList *pthlp) static ERTS_INLINE void abort_nosuspend_task(Port *pp, ErtsPortTaskType type, - ErtsPortTaskTypeData *tdp) + ErtsPortTaskTypeData *tdp, + int bpq_data) { ASSERT(type == ERTS_PORT_TASK_PROC_SIG); - if (!pp->sched.taskq.bpq) + if (!bpq_data) tdp->psig.callback(NULL, ERTS_PORT_SFLG_INVALID, ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND, @@ -1345,7 +1346,7 @@ erts_port_task_abort_nosuspend_tasks(Port *pp) #endif schedule_port_task_handle_list_free(pthlp); - abort_nosuspend_task(pp, type, &td); + abort_nosuspend_task(pp, type, &td, pp->sched.taskq.bpq != NULL); } } @@ -1525,7 +1526,7 @@ abort_nosuspend: erts_port_dec_refc(pp); #endif - abort_nosuspend_task(pp, ptp->type, &ptp->u.alive.td); + abort_nosuspend_task(pp, ptp->type, &ptp->u.alive.td, 0); ASSERT(ns_pthlp); erts_free(ERTS_ALC_T_PT_HNDL_LIST, ns_pthlp); -- cgit v1.2.3 From b29fa5a40e04f4243c15391b70c37ebce0198fc1 Mon Sep 17 00:00:00 2001 From: Sergey Kudryashov Date: Mon, 4 Aug 2014 18:11:55 +0400 Subject: Add async_ports test --- erts/emulator/test/Makefile | 1 + erts/emulator/test/async_ports_SUITE.erl | 117 +++++++++++++++++++++ .../test/async_ports_SUITE_data/Makefile.src | 5 + erts/emulator/test/async_ports_SUITE_data/cport.c | 65 ++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 erts/emulator/test/async_ports_SUITE.erl create mode 100644 erts/emulator/test/async_ports_SUITE_data/Makefile.src create mode 100644 erts/emulator/test/async_ports_SUITE_data/cport.c (limited to 'erts') diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 0b0568c31a..dfbe47786a 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -31,6 +31,7 @@ MODULES= \ a_SUITE \ after_SUITE \ alloc_SUITE \ + async_ports_SUITE \ beam_SUITE \ beam_literals_SUITE \ bif_SUITE \ diff --git a/erts/emulator/test/async_ports_SUITE.erl b/erts/emulator/test/async_ports_SUITE.erl new file mode 100644 index 0000000000..9e0cc8ffab --- /dev/null +++ b/erts/emulator/test/async_ports_SUITE.erl @@ -0,0 +1,117 @@ +-module(async_ports_SUITE). + +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +-define(PACKET_SIZE, (10 * 1024 * 8)). +-define(CPORT_DELAY, 100). +-define(TEST_LOOPS_COUNT, 100000). +-define(SLEEP_BEFORE_CHECK, 1000). +-define(TEST_PROCS_COUNT, 2). +-define(TC_TIMETRAP_SECONDS, 10). + + +all() -> + [ + permanent_busy_test + ]. + +permanent_busy_test(Config) -> + ct:timetrap({seconds, ?TC_TIMETRAP_SECONDS}), + ExePath = filename:join(?config(data_dir, Config), "cport"), + + Self = self(), + spawn_link( + fun() -> + Block = <<0:?PACKET_SIZE>>, + + Port = open_port(ExePath), + + Testers = + lists:map( + fun(_) -> + erlang:spawn_link(?MODULE, run_loop, + [Self, + Port, + Block, + ?TEST_LOOPS_COUNT, + 0]) + end, + lists:seq(1, ?TEST_PROCS_COUNT)), + Self ! {test_info, Port, Testers}, + endless_flush(Port) + end), + + receive + {test_info, Port, Testers} -> + MaxWaitTime = round(0.7 * ?TC_TIMETRAP_SECONDS * 1000), + ct:log("wait testers, maximum ~w mcsec~n", [MaxWaitTime]), + ok = wait_testers(MaxWaitTime, Testers), + timer:sleep(?SLEEP_BEFORE_CHECK), + case erlang:port_command(Port, <<"test">>, [nosuspend]) of + false -> + exit(port_dead); + true -> + ok + end + end. + +wait_testers(Timeout, Testers) -> + lists:foldl( + fun(Pid, AccIn) -> + StartWait = os:timestamp(), + receive + {Pid, port_dead} -> + recalc_timeout(AccIn, StartWait) + after AccIn -> + Pid ! stop, + recalc_timeout(AccIn, StartWait) + end + end, Timeout, Testers), + ok. + +recalc_timeout(TimeoutIn, WaitStart) -> + erlang:max(0, TimeoutIn - round(timer:now_diff(os:timestamp(), WaitStart)) div 1000). + +open_port(ExePath) -> + erlang:open_port({spawn, ExePath ++ " 100"}, [{packet, 4}, eof, exit_status, use_stdio, binary]). + +run_loop(RootProc, Port, Block, CheckLimit, BusyCnt) -> + receive + stop -> + ok + after 0 -> + case erlang:port_command(Port, Block, [nosuspend]) of + true -> + run_loop(RootProc, Port, Block, CheckLimit, 0); + false -> + if + BusyCnt + 1 > CheckLimit -> + check_dead(RootProc, Port, Block, CheckLimit); + true -> + run_loop(RootProc, Port, Block, CheckLimit, BusyCnt + 1) + end + end + end. + +check_dead(RootProc, Port, Block, CheckLimit) -> + ct:log("~p: check port dead~n", [self()]), + timer:sleep(?SLEEP_BEFORE_CHECK), + case erlang:port_command(Port, Block, [nosuspend]) of + true -> + ct:log("not dead~n"), + run_loop(RootProc, Port, Block, CheckLimit, 0); + false -> + ct:log("port dead: ~p~n", [Port]), + RootProc ! {self(), port_dead}, + ok + end. + +endless_flush(Port) -> + receive + {Port, {data, _}} -> + endless_flush(Port); + {Port, SomethingWrong} -> + erlang:error({someting_wrong, SomethingWrong}) + end. diff --git a/erts/emulator/test/async_ports_SUITE_data/Makefile.src b/erts/emulator/test/async_ports_SUITE_data/Makefile.src new file mode 100644 index 0000000000..17e6a6ee6a --- /dev/null +++ b/erts/emulator/test/async_ports_SUITE_data/Makefile.src @@ -0,0 +1,5 @@ +all: busy_drv@dll@ hard_busy_drv@dll@ soft_busy_drv@dll@ scheduling_drv@dll@ + +@SHLIB_RULES@ + +cport@obj@: cport.c diff --git a/erts/emulator/test/async_ports_SUITE_data/cport.c b/erts/emulator/test/async_ports_SUITE_data/cport.c new file mode 100644 index 0000000000..179cdc4b8b --- /dev/null +++ b/erts/emulator/test/async_ports_SUITE_data/cport.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include + +typedef unsigned char byte; + +int read_cmd(byte *buf) +{ + int len; + if (read_exact(buf, 4) != 4) + return(-1); + + len = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + return read_exact(buf, len); +} + +int write_cmd(byte *buf, int len) +{ + byte li[4]; + li[0] = (len >> 24) & 0xff; + li[1] = (len >> 16) & 0xff; + li[2] = (len >> 8) & 0xff; + li[3] = len & 0xff; + write_exact(&li, 4); + + return write_exact(buf, len); +} + +int read_exact(byte *buf, int len) +{ + int i, got=0; + do { + if ((i = read(0, buf+got, len-got)) <= 0) + { + return(i); + } + got += i; + } while (got 0) { + usleep(sleep_time); + write_cmd(buf, len); + } +} -- cgit v1.2.3 From ace34ce01c9378525d024246c7d76ab01add4a8d Mon Sep 17 00:00:00 2001 From: kudryashov-sv Date: Mon, 4 Aug 2014 20:55:17 +0400 Subject: Update Makefile.src --- erts/emulator/test/async_ports_SUITE_data/Makefile.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/async_ports_SUITE_data/Makefile.src b/erts/emulator/test/async_ports_SUITE_data/Makefile.src index 17e6a6ee6a..d2cd90f5b3 100644 --- a/erts/emulator/test/async_ports_SUITE_data/Makefile.src +++ b/erts/emulator/test/async_ports_SUITE_data/Makefile.src @@ -1,4 +1,4 @@ -all: busy_drv@dll@ hard_busy_drv@dll@ soft_busy_drv@dll@ scheduling_drv@dll@ +all: cport@obj@ @SHLIB_RULES@ -- cgit v1.2.3 From ad57501215fc02a39abf3fbce978dbc43d010859 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 4 Aug 2014 11:32:52 +0200 Subject: erts: Print error reason when malloc fails --- erts/emulator/beam/erl_lock_check.c | 17 +++++++---------- erts/emulator/beam/erl_lock_count.c | 12 ++++++++++++ erts/emulator/beam/erl_process.c | 6 ++++++ erts/emulator/beam/utils.c | 3 +++ 4 files changed, 28 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index c13eb87012..c665aa51a2 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -227,8 +227,7 @@ rw_op_str(Uint16 flags) case ERTS_LC_FLG_LO_READ: return " (r)"; case ERTS_LC_FLG_LO_WRITE: - erts_fprintf(stderr, "\nInternal error\n"); - lc_abort(); + ERTS_INTERNAL_ERROR("Only write flag present"); default: break; } @@ -311,8 +310,7 @@ static ERTS_INLINE void lc_free(void *p) static void *lc_core_alloc(void) { lc_unlock(); - erts_fprintf(stderr, "Lock checker out of memory!\n"); - lc_abort(); + ERTS_INTERNAL_ERROR("Lock checker out of memory!\n"); } #else @@ -325,8 +323,7 @@ static void *lc_core_alloc(void) fbs = (erts_lc_free_block_t *) malloc(sizeof(erts_lc_free_block_t) * ERTS_LC_FB_CHUNK_SIZE); if (!fbs) { - erts_fprintf(stderr, "Lock checker failed to allocate memory!\n"); - lc_abort(); + ERTS_INTERNAL_ERROR("Lock checker failed to allocate memory!"); } for (i = 1; i < ERTS_LC_FB_CHUNK_SIZE - 1; i++) { #ifdef DEBUG @@ -366,11 +363,11 @@ create_locked_locks(char *thread_name) { erts_lc_locked_locks_t *l_lcks = malloc(sizeof(erts_lc_locked_locks_t)); if (!l_lcks) - lc_abort(); + ERTS_INTERNAL_ERROR("Lock checker failed to allocate memory!"); l_lcks->thread_name = strdup(thread_name ? thread_name : "unknown"); if (!l_lcks->thread_name) - lc_abort(); + ERTS_INTERNAL_ERROR("Lock checker failed to allocate memory!"); l_lcks->emu_thread = 0; l_lcks->tid = erts_thr_self(); @@ -691,7 +688,7 @@ erts_lc_set_thread_name(char *thread_name) free((void *) l_lcks->thread_name); l_lcks->thread_name = strdup(thread_name ? thread_name : "unknown"); if (!l_lcks->thread_name) - lc_abort(); + ERTS_INTERNAL_ERROR("strdup failed"); } l_lcks->emu_thread = 1; } @@ -1330,7 +1327,7 @@ erts_lc_init(void) #endif /* #ifdef ERTS_LC_STATIC_ALLOC */ if (ethr_spinlock_init(&free_blocks_lock) != 0) - lc_abort(); + ERTS_INTERNAL_ERROR("spinlock_init failed"); erts_tsd_key_create(&locks_key,"erts_lock_check_key"); } diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index 17ddfdc8ae..cf6996ea06 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -151,6 +151,9 @@ static erts_lcnt_thread_data_t *lcnt_thread_data_alloc(void) { erts_lcnt_thread_data_t *eltd; eltd = (erts_lcnt_thread_data_t*)malloc(sizeof(erts_lcnt_thread_data_t)); + if (!eltd) { + ERTS_INTERNAL_ERROR("Lock counter failed to allocate memory!"); + } eltd->timer_set = 0; eltd->lock_in_conflict = 0; @@ -272,6 +275,9 @@ void erts_lcnt_init() { /* init lcnt structure */ erts_lcnt_data = (erts_lcnt_data_t*)malloc(sizeof(erts_lcnt_data_t)); + if (!erts_lcnt_data) { + ERTS_INTERNAL_ERROR("Lock counter failed to allocate memory!"); + } erts_lcnt_data->current_locks = erts_lcnt_list_init(); erts_lcnt_data->deleted_locks = erts_lcnt_list_init(); @@ -293,6 +299,9 @@ erts_lcnt_lock_list_t *erts_lcnt_list_init(void) { erts_lcnt_lock_list_t *list; list = (erts_lcnt_lock_list_t*)malloc(sizeof(erts_lcnt_lock_list_t)); + if (!list) { + ERTS_INTERNAL_ERROR("Lock counter failed to allocate memory!"); + } list->head = NULL; list->tail = NULL; list->n = 0; @@ -399,6 +408,9 @@ void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) { /* copy structure and insert the copy */ deleted_lock = (erts_lcnt_lock_t*)malloc(sizeof(erts_lcnt_lock_t)); + if (!deleted_lock) { + ERTS_INTERNAL_ERROR("Lock counter failed to allocate memory!"); + } memcpy(deleted_lock, lock, sizeof(erts_lcnt_lock_t)); deleted_lock->next = NULL; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b73f9b7f92..1606ad119d 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2211,6 +2211,9 @@ aux_work_timeout_early_init(int no_schedulers) p = (UWord) malloc((sizeof(ErtsAuxWorkTmo) + sizeof(erts_atomic32_t)*(no_schedulers+1)) + ERTS_CACHE_LINE_SIZE-1); + if (!p) { + ERTS_INTERNAL_ERROR("malloc failed to allocate memory!"); + } if (p & ERTS_CACHE_LINE_MASK) p = (p & ~ERTS_CACHE_LINE_MASK) + ERTS_CACHE_LINE_SIZE; ASSERT((p & ERTS_CACHE_LINE_MASK) == 0); @@ -7818,6 +7821,9 @@ erts_start_schedulers(void) #ifdef ETHR_HAVE_THREAD_NAMES opts.name = malloc(80); + if (!opts.name) { + ERTS_INTERNAL_ERROR("malloc failed to allocate memory!"); + } #endif #ifdef ERTS_SMP diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 72092ec7b0..55f9e68e78 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3948,6 +3948,9 @@ erts_save_emu_args(int argc, char **argv) size += sz+1; } ptr = (char *) malloc(size); + if (!ptr) { + ERTS_INTERNAL_ERROR("malloc failed to allocate memory!"); + } #ifdef DEBUG end_ptr = ptr + size; #endif -- cgit v1.2.3 From 6e3f09186e0a5e89eb729840f71e2a73684e9b50 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 4 Aug 2014 17:36:12 +0200 Subject: erts: Fix tc and docs after {fd,FD} bind change The sha of the original change is 52810718b --- erts/emulator/test/driver_SUITE.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index c62bc0c454..344bde7c91 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -1062,10 +1062,9 @@ otp_6602(Config) when is_list(Config) -> %% Inet driver use port locking... {ok, S} = gen_udp:open(0), {ok, Fd} = inet:getfd(S), - {ok, Port} = inet:port(S), %% Steal fd (lock checker used to %% trigger here). - {ok, _S2} = gen_udp:open(Port,[{fd,Fd}]), + {ok, _S2} = gen_udp:open(0,[{fd,Fd}]), Parent ! Done end), ?line receive Done -> ok end, -- cgit v1.2.3 From cde6c2e042834d66649308093d5522c1a2001949 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 5 Aug 2014 18:04:46 +0200 Subject: Fix build of test port program --- erts/emulator/test/async_ports_SUITE.erl | 1 + .../test/async_ports_SUITE_data/Makefile.src | 14 ++++++++++++-- erts/emulator/test/async_ports_SUITE_data/cport.c | 22 +++++++++++++++++++--- 3 files changed, 32 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/async_ports_SUITE.erl b/erts/emulator/test/async_ports_SUITE.erl index 9e0cc8ffab..c89b3655ff 100644 --- a/erts/emulator/test/async_ports_SUITE.erl +++ b/erts/emulator/test/async_ports_SUITE.erl @@ -11,6 +11,7 @@ -define(TEST_PROCS_COUNT, 2). -define(TC_TIMETRAP_SECONDS, 10). +suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ diff --git a/erts/emulator/test/async_ports_SUITE_data/Makefile.src b/erts/emulator/test/async_ports_SUITE_data/Makefile.src index d2cd90f5b3..56da3fbe12 100644 --- a/erts/emulator/test/async_ports_SUITE_data/Makefile.src +++ b/erts/emulator/test/async_ports_SUITE_data/Makefile.src @@ -1,5 +1,15 @@ -all: cport@obj@ +CC = @CC@ +LD = @LD@ +CFLAGS = @CFLAGS@ @DEFS@ +CROSSLDFLAGS = @CROSSLDFLAGS@ -@SHLIB_RULES@ +PROGS = cport@exe@ + + +all: $(PROGS) + +cport@exe@: cport@obj@ + $(LD) $(CROSSLDFLAGS) -o cport cport@obj@ @LIBS@ cport@obj@: cport.c + $(CC) -c -o cport@obj@ $(CFLAGS) cport.c diff --git a/erts/emulator/test/async_ports_SUITE_data/cport.c b/erts/emulator/test/async_ports_SUITE_data/cport.c index 179cdc4b8b..033aff382a 100644 --- a/erts/emulator/test/async_ports_SUITE_data/cport.c +++ b/erts/emulator/test/async_ports_SUITE_data/cport.c @@ -1,9 +1,13 @@ #include #include -#include #include #include -#include +#ifdef __WIN32__ +# include "windows.h" +# include "winbase.h" +#else +# include +#endif typedef unsigned char byte; @@ -53,13 +57,25 @@ int write_exact(byte *buf, int len) return len; } +byte static_buf[31457280]; // 30 mb + int main(int argc, char **argv) { int sleep_time = atoi(argv[1]); int fn, arg, res; - byte *buf = malloc(31457280); // 30 mb + byte *buf = &static_buf[0]; int len = 0; + if (sleep_time <= 0) + sleep_time = 0; +#ifdef __WIN32__ + else + sleep_time = ((sleep_time - 1) / 1000) + 1; /* Milli seconds */ +#endif while ((len = read_cmd(buf)) > 0) { +#ifdef __WIN32__ + Sleep((DWORD) sleep_time); +#else usleep(sleep_time); +#endif write_cmd(buf, len); } } -- cgit v1.2.3 From 18c03b13d1a53282e69160fa71a7195a11e84392 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 7 Aug 2014 22:02:25 +0200 Subject: Fix emigrate bug in erts_port_task_schedule() While current run-queue lock is unlocked in the call to erts_check_emigration_need() from erts_port_task_schedule() the port can be migrated to another run-queue by another thread. The code in erts_port_task_schedule() needs to check if this has occurred when returning from erts_check_emigration_need(), and if so respect the migration decision. When this was not done, the thread calling erts_port_task_schedule() held the wrong run-queue lock which caused invalid updates of the port task queue. This bug was automatically fixed by the rewrites in the branch rickard/r16b/port-optimizations-fixes/OTP-10336 (commit 56cef897ca3ad2377e34a6ea5800a54a28cbeb6e) introduced in erts-5.10 and do not effect erts versions after that. --- erts/emulator/beam/erl_port_task.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 3dc7c14faf..11027dda7a 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -576,8 +576,20 @@ erts_port_task_schedule(Eterm id, if (!pp->sched.taskq && !pp->sched.exe_taskq) { #ifdef ERTS_SMP ErtsRunQueue *xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL); - if (xrunq) { - /* Port emigrated ... */ + ERTS_SMP_LC_ASSERT(runq != xrunq); + if (runq != (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue)) { + /* + * Someone else migrated this port while we had the run-queue + * lock on runq unlocked in erts_check_emigration_need(). Respect + * that migration decision... + */ + erts_smp_runq_unlock(runq); + if (xrunq) + erts_smp_runq_unlock(xrunq); + runq = erts_port_runq(pp); + } + else if (xrunq) { + /* Emigrate port... */ erts_smp_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq); erts_smp_runq_unlock(runq); runq = xrunq; @@ -929,6 +941,8 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) #ifdef ERTS_SMP xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL); + ERTS_SMP_LC_ASSERT(runq != xrunq); + ERTS_SMP_LC_ASSERT(runq == (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue)); if (!xrunq) { #endif enqueue_port(runq, pp); @@ -938,7 +952,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) #ifdef ERTS_SMP } else { - /* Port emigrated ... */ + /* Emigrate port... */ erts_smp_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq); enqueue_port(xrunq, pp); ASSERT(pp->sched.exe_taskq); -- cgit v1.2.3 From 9d3c22934491afe85fbcf8543ae43fb2eb1ab387 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 4 Aug 2014 12:04:45 +0200 Subject: erts: Fix neg int overflow when sint is min size When INT64_MIN is the value of a Sint64 we have to first cast it to an Uint64 before negating it. Otherwise we get an integer overflow which is undefined behaviour and in gcc 4.9 this results in -0 instead of -9223372036854775808 in gcc 4.8. --- erts/emulator/beam/big.c | 18 ++++++++++++------ erts/emulator/beam/big.h | 2 +- erts/emulator/beam/erl_bif_binary.c | 16 ++++++++-------- erts/emulator/beam/erl_init.c | 6 ++++-- 4 files changed, 25 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 41a041eba6..520034d68a 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -274,6 +274,9 @@ _b = _b << _s; \ _vn1 = _b >> H_EXP; \ _vn0 = _b & LO_MASK; \ + /* Sometimes _s is 0 which triggers undefined behaviour for the \ + (_a0>>(D_EXP-_s)) shift, but this is ok because the \ + & -s will make it all to 0 later anyways. */ \ _un32 = (_a1 << _s) | ((_a0>>(D_EXP-_s)) & (-_s >> (D_EXP-1))); \ _un10 = _a0 << _s; \ _un1 = _un10 >> H_EXP; \ @@ -1540,21 +1543,24 @@ Eterm erts_uint64_to_big(Uint64 x, Eterm **hpp) Eterm erts_sint64_to_big(Sint64 x, Eterm **hpp) { Eterm *hp = *hpp; + Uint64 ux; int neg; - if (x >= 0) + if (x >= 0) { neg = 0; + ux = x; + } else { neg = 1; - x = -x; + ux = -(Uint64)x; } #if defined(ARCH_32) || HALFWORD_HEAP - if (x >= (((Uint64) 1) << 32)) { + if (ux >= (((Uint64) 1) << 32)) { if (neg) *hp = make_neg_bignum_header(2); else *hp = make_pos_bignum_header(2); - BIG_DIGIT(hp, 0) = (Uint) (x & ((Uint) 0xffffffff)); - BIG_DIGIT(hp, 1) = (Uint) ((x >> 32) & ((Uint) 0xffffffff)); + BIG_DIGIT(hp, 0) = (Uint) (ux & ((Uint) 0xffffffff)); + BIG_DIGIT(hp, 1) = (Uint) ((ux >> 32) & ((Uint) 0xffffffff)); *hpp += 3; } else @@ -1564,7 +1570,7 @@ Eterm erts_sint64_to_big(Sint64 x, Eterm **hpp) *hp = make_neg_bignum_header(1); else *hp = make_pos_bignum_header(1); - BIG_DIGIT(hp, 0) = (Uint) x; + BIG_DIGIT(hp, 0) = (Uint) ux; *hpp += 2; } return make_big(hp); diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index d80111822e..da31876d75 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -101,7 +101,7 @@ typedef Uint dsize_t; /* Vector size type */ #define ERTS_SINT64_HEAP_SIZE(X) \ (IS_SSMALL((X)) \ ? 0 \ - : ERTS_UINT64_BIG_HEAP_SIZE__((X) >= 0 ? (X) : -(X))) + : ERTS_UINT64_BIG_HEAP_SIZE__((X) >= 0 ? (X) : -(Uint64)(X))) #define ERTS_UINT64_HEAP_SIZE(X) \ (IS_USMALL(0, (X)) ? 0 : ERTS_UINT64_BIG_HEAP_SIZE__((X))) diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 7e0e825a0d..3bf78adce7 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -1324,9 +1324,9 @@ static int parse_match_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp) goto badarg; } if (len < 0) { - Sint lentmp = -len; + Uint lentmp = -(Uint)len; /* overflow */ - if (lentmp == len || lentmp < 0 || -lentmp != len) { + if ((Sint)lentmp < 0) { goto badarg; } len = lentmp; @@ -1555,9 +1555,9 @@ BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen) goto badarg; } if (len < 0) { - Sint lentmp = -len; + Uint lentmp = -(Uint)len; /* overflow */ - if (lentmp == len || lentmp < 0 || -lentmp != len) { + if ((Sint)lentmp < 0) { goto badarg; } len = lentmp; @@ -1644,9 +1644,9 @@ BIF_RETTYPE erts_gc_binary_part(Process *p, Eterm *reg, Eterm live, int range_is goto badarg; } if (len < 0) { - Sint lentmp = -len; + Uint lentmp = -(Uint)len; /* overflow */ - if (lentmp == len || lentmp < 0 || -lentmp != len) { + if ((Sint)lentmp < 0) { goto badarg; } len = lentmp; @@ -2213,9 +2213,9 @@ static BIF_RETTYPE binary_bin_to_list_common(Process *p, goto badarg; } if (len < 0) { - Sint lentmp = -len; + Uint lentmp = -(Uint)len; /* overflow */ - if (lentmp == len || lentmp < 0 || -lentmp != len) { + if ((Sint)lentmp < 0) { goto badarg; } len = lentmp; diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 5e6d812242..88c4006934 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -2066,8 +2066,10 @@ erl_exit_vv(int n, int flush_async, char *fmt, va_list args1, va_list args2) system_cleanup(flush_async); save_statistics(); - - an = abs(n); + if (n < 0) + an = -(unsigned int)n; + else + an = n; if (erts_mtrace_enabled) erts_mtrace_exit((Uint32) an); -- cgit v1.2.3 From 81c2cd3755b4adb60d498b2763ee32279f3cfc0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Rasc=C3=A3o?= Date: Tue, 12 Aug 2014 22:40:28 +0100 Subject: fix indentation, add comment describing windows symlink creation assumption --- erts/preloaded/src/erl_prim_loader.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index dfd6151b69..6953db533c 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -1487,11 +1487,13 @@ real_path(Name,[Path|Paths],Acc,Links) -> [""|_] = LinkPaths -> real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]); LinkPaths -> + % windows currently does not allow creation of relative symlinks + % across different drives case erlang:system_info(os_type) of - {win32, _} -> - real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]); - _ -> - real_path(Name,LinkPaths++Paths,Acc,[ThisFile|Links]) + {win32, _} -> + real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]); + _ -> + real_path(Name,LinkPaths++Paths,Acc,[ThisFile|Links]) end end; _ -> -- cgit v1.2.3 From cc4031b2f139bb980a948455ec60dfaca7f49d24 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 13 Aug 2014 16:35:41 +0200 Subject: crypto: Add valgrind suppression for RC4 Invalid read of size 8 (Address 0x61f7730 is 144 bytes inside a block of size 151 alloc'd) Suspected: rc4_encrypt_with_state:2321 (-> 0x11B168F5) [crypto.c] Stackdump: RC4:??? (-> 0x11DAA060) [/lib/libcrypto.so.0.9.8] rc4_encrypt_with_state:2321 (-> 0x11B168F5) [crypto.c] process_main:3524 (-> 0x58D975) [beam_emu.c] sched_thread_func:7672 (-> 0x4C131E) [erl_process.c] thr_wrapper:106 (-> 0x656F68) [ethread.c] start_thread:300 (-> 0x57019C9) [pthread_create.c] --- erts/emulator/valgrind/suppress.patched.3.6.0 | 5 +++++ erts/emulator/valgrind/suppress.standard | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'erts') diff --git a/erts/emulator/valgrind/suppress.patched.3.6.0 b/erts/emulator/valgrind/suppress.patched.3.6.0 index b3507bdba7..f79e3ff634 100644 --- a/erts/emulator/valgrind/suppress.patched.3.6.0 +++ b/erts/emulator/valgrind/suppress.patched.3.6.0 @@ -273,6 +273,11 @@ obj:*/ssleay.* fun:AES_cbc_encrypt ... } +{ + crypto RC4 can do harmless word aligned read past end of input + Memcheck:Addr8 + fun:RC4 +} { erts_bits_init_state; Why is this needed? diff --git a/erts/emulator/valgrind/suppress.standard b/erts/emulator/valgrind/suppress.standard index a4da31a61d..b3c77119fb 100644 --- a/erts/emulator/valgrind/suppress.standard +++ b/erts/emulator/valgrind/suppress.standard @@ -260,6 +260,11 @@ obj:*/ssleay.* fun:AES_cbc_encrypt ... } +{ + crypto RC4 can do harmless word aligned read past end of input + Memcheck:Addr8 + fun:RC4 +} { Prebuilt constant terms in os_info_init (PossiblyLost) -- cgit v1.2.3 From b6a5919cf78028c2778985e8ec6eccc467bb6039 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 14 Aug 2014 16:04:56 +0200 Subject: Verify run-queue asserts --- erts/emulator/beam/erl_port_task.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 11027dda7a..738f7d318b 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -76,6 +76,13 @@ do { \ #define DTRACE_DRIVER(PROBE_NAME, PP) do {} while(0) #endif +#define ERTS_SMP_LC_VERIFY_RQ(RQ, PP) \ + do { \ + ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq)); \ + ERTS_SMP_LC_ASSERT((RQ) == ((ErtsRunQueue *) \ + erts_smp_atomic_read_nob(&(PP)->run_queue))); \ + } while (0) + erts_smp_atomic_t erts_port_task_outstanding_io_tasks; struct ErtsPortTaskQueue_ { @@ -786,7 +793,9 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) erts_smp_port_lock(pp); erts_smp_runq_lock(runq); } - + + ERTS_SMP_LC_VERIFY_RQ(runq, pp); + if (erts_sched_stat.enabled) { ErtsSchedulerData *esdp = erts_get_scheduler_data(); Uint old = ERTS_PORT_SCHED_ID(pp, esdp->no); @@ -828,6 +837,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) case ERTS_PORT_TASK_FREE: /* May be pushed in q at any time */ reds += ERTS_PORT_REDS_FREE; erts_smp_runq_lock(runq); + ERTS_SMP_LC_VERIFY_RQ(runq, pp); erts_unblock_fpe(fpe_was_unmasked); ASSERT(pp->status & ERTS_PORT_SFLG_FREE_SCHEDULED); @@ -906,6 +916,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) port_task_free(ptp); erts_smp_runq_lock(runq); + ERTS_SMP_LC_VERIFY_RQ(runq, pp); ptp = pop_task(ptqp); } @@ -942,7 +953,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) #ifdef ERTS_SMP xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL); ERTS_SMP_LC_ASSERT(runq != xrunq); - ERTS_SMP_LC_ASSERT(runq == (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue)); + ERTS_SMP_LC_VERIFY_RQ(runq, pp); if (!xrunq) { #endif enqueue_port(runq, pp); @@ -1052,6 +1063,7 @@ handle_remaining_tasks(ErtsRunQueue *runq, Port *pp) port_task_free(ptp); erts_smp_runq_lock(runq); + ERTS_SMP_LC_VERIFY_RQ(runq, pp); ptp = pop_task(ptqps[i]); } } -- cgit v1.2.3 From 5e72adfd8b56f50416d66a018028e2e6895032e0 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 14 Aug 2014 21:32:53 +0200 Subject: Fix +swct doc --- erts/doc/src/erl.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 5bde285311..f856b9ab86 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1186,7 +1186,7 @@ utilization.

- +sws very_eager|eager|medium|lazy|very_lazy + +swct very_eager|eager|medium|lazy|very_lazy

Set scheduler wake cleanup threshold. Default is medium. -- cgit v1.2.3 From 44f2f04cb1b5f024ba6934e6cf350b1ffbcaa3ee Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 16 Aug 2014 18:23:25 +0200 Subject: Fix HiPE debug lock checking on OS X 64bit Position-independent code is mandatory on OS X. We use r11 as an intermediate register to fill BIF_P->hipe.bif_callee. This fixes the following error when doing `make debug FLAVOR=smp`: clang -cc1as: fatal error: error in backend: 32-bit absolute addressing is not supported in 64-bit mode --- erts/emulator/hipe/hipe_amd64_bifs.m4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4 index 0de69a617f..a3219c7586 100644 --- a/erts/emulator/hipe/hipe_amd64_bifs.m4 +++ b/erts/emulator/hipe/hipe_amd64_bifs.m4 @@ -39,7 +39,10 @@ define(HANDLE_GOT_MBUF,` jmp 2b') `#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) -# define CALL_BIF(F) movq $CSYM(F), P_BIF_CALLEE(P); call CSYM(hipe_debug_bif_wrapper) +# define CALL_BIF(F) \ + movq CSYM(F)@GOTPCREL(%rip), %r11; \ + movq %r11, P_BIF_CALLEE(P); \ + call CSYM(hipe_debug_bif_wrapper) #else # define CALL_BIF(F) call CSYM(F) #endif' -- cgit v1.2.3 From 631474e50fd84fd526dd1a5db4753b3d27ebb8e7 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 27 Jun 2014 14:41:12 +0200 Subject: Return pointer to value in erts_maps_get() While at it, implement erts_maps_get_rel() for halfword support. --- erts/emulator/beam/erl_map.c | 91 +++++++++++++++++++------------------------- erts/emulator/beam/erl_map.h | 12 +++++- erts/emulator/beam/erl_nif.c | 8 +++- 3 files changed, 57 insertions(+), 54 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 5e740aacdd..b2a16eb5ed 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -113,36 +113,55 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { * return value if key *matches* a key in the map */ -int erts_maps_find(Eterm key, Eterm map, Eterm *value) { - - Eterm *ks,*vs; +const Eterm * +#if HALFWORD_HEAP +erts_maps_get_rel(Eterm key, Eterm map, Eterm *map_base) +#else +erts_maps_get(Eterm key, Eterm map) +#endif +{ + Eterm *ks, *vs; map_t *mp; - Uint n,i; + Uint n, i; - mp = (map_t*)map_val(map); + mp = (map_t *)map_val_rel(map, map_base); n = map_get_size(mp); - ks = map_get_keys(mp); + + if (n == 0) { + return NULL; + } + + ks = (Eterm *)tuple_val_rel(mp->keys, map_base) + 1; vs = map_get_values(mp); - for( i = 0; i < n; i++) { - if (EQ(ks[i], key)) { - *value = vs[i]; - return 1; - } + if (is_immed(key)) { + for (i = 0; i < n; i++) { + if (ks[i] == key) { + return &vs[i]; + } + } + } + + for (i = 0; i < n; i++) { + if (eq_rel(ks[i], NULL, key, map_base)) { + return &vs[i]; + } } - return 0; + return NULL; } BIF_RETTYPE maps_find_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { - Eterm *hp, value,res; + Eterm *hp, res; + const Eterm *value; - if (erts_maps_find(BIF_ARG_1, BIF_ARG_2, &value)) { + value = erts_maps_get(BIF_ARG_1, BIF_ARG_2); + if (value) { hp = HAlloc(BIF_P, 3); res = make_tuple(hp); *hp++ = make_arityval(2); *hp++ = am_ok; - *hp++ = value; + *hp++ = *value; BIF_RET(res); } @@ -150,52 +169,22 @@ BIF_RETTYPE maps_find_2(BIF_ALIST_2) { } BIF_ERROR(BIF_P, BADARG); } + /* maps:get/2 * return value if key *matches* a key in the map * exception bad_key if none matches */ - -int erts_maps_get(Eterm key, Eterm map, Eterm *value) { - Eterm *ks,*vs; - map_t *mp; - Uint n,i; - - mp = (map_t*)map_val(map); - n = map_get_size(mp); - - if (n == 0) - return 0; - - ks = map_get_keys(mp); - vs = map_get_values(mp); - - if (is_immed(key)) { - for( i = 0; i < n; i++) { - if (ks[i] == key) { - *value = vs[i]; - return 1; - } - } - } - - for( i = 0; i < n; i++) { - if (EQ(ks[i], key)) { - *value = vs[i]; - return 1; - } - } - return 0; -} - BIF_RETTYPE maps_get_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Eterm *hp; - Eterm value, error; + Eterm error; + const Eterm *value; char *s_error; - if (erts_maps_get(BIF_ARG_1, BIF_ARG_2, &value)) { - BIF_RET(value); + value = erts_maps_get(BIF_ARG_1, BIF_ARG_2); + if (value) { + BIF_RET(*value); } s_error = "bad_key"; diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index cfacb2ec28..2e02ca4677 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -64,9 +64,17 @@ typedef struct map_s { Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map); int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res); -int erts_maps_find(Eterm key, Eterm map, Eterm *value); -int erts_maps_get(Eterm key, Eterm map, Eterm *value); int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res); int erts_validate_and_sort_map(map_t* map); + +#if HALFWORD_HEAP +const Eterm * +erts_maps_get_rel(Eterm key, Eterm map, Eterm *map_base); +# define erts_maps_get(A, B) erts_maps_get_rel(A, B, NULL) +#else +const Eterm * +erts_maps_get(Eterm key, Eterm map); +# define erts_maps_get_rel(A, B, B_BASE) erts_maps_get(A, B) #endif +#endif diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index ff551ea3af..dd88f59908 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1733,10 +1733,16 @@ int enif_get_map_value(ErlNifEnv* env, Eterm key, Eterm *value) { + Eterm *ret; if (is_not_map(map)) { return 0; } - return erts_maps_get(key, map, value); + ret = erts_maps_get(key, map); + if (ret) { + *value = *ret; + return 1; + } + return 0; } int enif_make_map_update(ErlNifEnv* env, -- cgit v1.2.3 From a46f30f38f0e757e9aa335e1452879db608d8489 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 27 Jun 2014 14:47:26 +0200 Subject: Properly support maps in match_specs --- erts/doc/src/match_spec.xml | 57 +++--- erts/emulator/beam/erl_db_util.c | 316 +++++++++++++++++++++++++------- erts/emulator/test/match_spec_SUITE.erl | 29 ++- 3 files changed, 314 insertions(+), 88 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml index 334b47d34c..b4cc8e9f78 100644 --- a/erts/doc/src/match_spec.xml +++ b/erts/doc/src/match_spec.xml @@ -76,22 +76,26 @@ { GuardFunction, ConditionExpression, ... } BoolFunction ::= | - | | | - | | | - | | | - | | | - | | | | - | + | | + | | + | | + | | + | | + | | + | | + | | + | | + ConditionExpression ::= ExprMatchVariable | { GuardFunction } | { GuardFunction, ConditionExpression, ... } | TermConstruct ExprMatchVariable ::= MatchVariable (bound in the MatchHead) | | - TermConstruct = {{}} | {{ ConditionExpression, ... }} | - | [ConditionExpression, ...] | NonCompositeTerm | Constant - - NonCompositeTerm ::= term() (not list or tuple) - + TermConstruct = {{}} | {{ ConditionExpression, ... }} | + | [ConditionExpression, ...] | + | #{term() => ConditionExpression, ...} | + NonCompositeTerm | Constant + NonCompositeTerm ::= term() (not list or tuple or map) Constant ::= {, term()} GuardFunction ::= BoolFunction | | @@ -134,22 +138,26 @@ { GuardFunction, ConditionExpression, ... } BoolFunction ::= | - | | | - | | | - | | | - | | | - | | | | - | + | | + | | + | | + | | + | | + | | + | | + | | + | | + ConditionExpression ::= ExprMatchVariable | { GuardFunction } | { GuardFunction, ConditionExpression, ... } | TermConstruct ExprMatchVariable ::= MatchVariable (bound in the MatchHead) | | TermConstruct = {{}} | {{ ConditionExpression, ... }} | - | [ConditionExpression, ...] | NonCompositeTerm | Constant - - NonCompositeTerm ::= term() (not list or tuple) - + | [ConditionExpression, ...] | #{} | + #{term() => ConditionExpression, ...} | NonCompositeTerm | + Constant + NonCompositeTerm ::= term() (not list or tuple or map) Constant ::= {, term()} GuardFunction ::= BoolFunction | | @@ -172,9 +180,10 @@ Functions allowed in all types of match specifications

The different functions allowed in work like this:

-

is_atom, is_float, is_integer, is_list, is_number, is_pid, is_port, is_reference, is_tuple, is_binary, is_function: Like the corresponding guard tests in - Erlang, return or . -

+

is_atom, is_float, is_integer, is_list, is_number, is_pid, is_port, + is_reference, is_tuple, is_map, is_binary, is_function: Like the + corresponding guard tests in Erlang, return or + .

is_record: Takes an additional parameter, which SHALL be the result of )]]>, like in . diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 3927615e04..b9fd3b208e 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -198,11 +198,6 @@ set_match_trace(Process *tracee_p, Eterm fail_term, Eterm tracer, return ret; } - -/* Type checking... */ - -#define BOXED_IS_TUPLE(Boxed) is_arity_value(*boxed_val((Boxed))) - /* ** ** Types and enum's (compiled matches) @@ -218,6 +213,8 @@ typedef enum { matchTuple, matchPushT, matchPushL, + matchPushM, + matchPushK, matchPop, matchBind, matchCmp, @@ -227,11 +224,13 @@ typedef enum { matchEqRef, matchEq, matchList, + matchMap, matchSkip, matchPushC, matchConsA, /* Car is below Cdr */ matchConsB, /* Cdr is below Car (unusual) */ matchMkTuple, + matchMkMap, matchCall0, matchCall1, matchCall2, @@ -856,6 +855,13 @@ static int match_compact(ErlHeapFragment *expr, DMCErrInfo *err_info); static Uint my_size_object(Eterm t); static Eterm my_copy_struct(Eterm t, Eterm **hp, ErlOffHeap* off_heap); +/* Guard subroutines */ +static void +dmc_rearrange_constants(DMCContext *context, DMC_STACK_TYPE(UWord) *text, + int textpos, Eterm *p, Uint nelems); +static DMCRet +dmc_array(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, + Eterm *p, Uint nelems, int *constant); /* Guard compilation */ static void do_emit_constant(DMCContext *context, DMC_STACK_TYPE(UWord) *text, Eterm t); @@ -869,6 +875,9 @@ static DMCRet dmc_tuple(DMCContext *context, DMC_STACK_TYPE(UWord) *text, Eterm t, int *constant); +static DMCRet +dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, + Eterm t, int *constant); static DMCRet dmc_variable(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, @@ -888,12 +897,14 @@ static DMCRet compile_guard_expr(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, Eterm t); -/* match expression subroutine */ +/* match expression subroutines */ static DMCRet dmc_one_term(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(Eterm) *stack, DMC_STACK_TYPE(UWord) *text, Eterm c); +static Eterm +dmc_private_copy(DMCContext *context, Eterm c); #ifdef DMC_DEBUG @@ -1364,7 +1375,51 @@ restart: for (;;) { switch (t & _TAG_PRIMARY_MASK) { case TAG_PRIMARY_BOXED: - if (!BOXED_IS_TUPLE(t)) { + if (is_map(t)) { + num_iters = map_get_size(map_val(t)); + if (!structure_checked) { + DMC_PUSH(text, matchMap); + DMC_PUSH(text, num_iters); + } + structure_checked = 0; + for (i = 0; i < num_iters; ++i) { + Eterm key = map_get_keys(map_val(t))[i]; + if (db_is_variable(key) >= 0) { + if (context.err_info) { + add_dmc_err(context.err_info, + "Variable found in map key.", + -1, 0UL, dmcError); + } + goto error; + } else if (key == am_Underscore) { + if (context.err_info) { + add_dmc_err(context.err_info, + "Underscore found in map key.", + -1, 0UL, dmcError); + } + goto error; + } + DMC_PUSH(text, matchPushK); + ++(context.stack_used); + DMC_PUSH(text, dmc_private_copy(&context, key)); + } + if (context.stack_used > context.stack_need) { + context.stack_need = context.stack_used; + } + for (i = num_iters; i--; ) { + Eterm value = map_get_values(map_val(t))[i]; + DMC_PUSH(text, matchPop); + --(context.stack_used); + res = dmc_one_term(&context, &heap, &stack, &text, + value); + ASSERT(res != retFail); + if (res == retRestart) { + goto restart; + } + } + break; + } + if (!is_tuple(t)) { goto simple_term; } num_iters = arityval(*tuple_val(t)); @@ -1715,10 +1770,8 @@ Eterm db_prog_match(Process *c_p, Binary *bprog, Uint32 *return_flags) { MatchProg *prog = Binary2MatchProg(bprog); - Eterm *ep; - Eterm *tp; + const Eterm *ep, *tp, **sp; Eterm t; - Eterm **sp; Eterm *esp; MatchVariable* variables; BeamInstr *cp; @@ -1808,7 +1861,7 @@ Eterm db_prog_match(Process *c_p, Binary *bprog, restart: ep = &term; esp = (Eterm*)((char*)mpsp->u.heap + prog->stack_offset); - sp = (Eterm **) esp; + sp = (const Eterm **)esp; ret = am_true; do_catch = 0; fail_label = -1; @@ -1887,6 +1940,34 @@ restart: *sp++ = list_val_rel(*ep,base); ++ep; break; + case matchMap: + if (!is_map_rel(*ep, base)) { + FAIL(); + } + n = *pc++; + if (map_get_size(map_val_rel(*ep, base)) < n) { + FAIL(); + } + ep = map_val_rel(*ep, base); + break; + case matchPushM: + if (!is_map_rel(*ep, base)) { + FAIL(); + } + n = *pc++; + if (map_get_size(map_val_rel(*ep, base)) < n) { + FAIL(); + } + *sp++ = map_val_rel(*ep++, base); + break; + case matchPushK: + t = (Eterm) *pc++; + tp = erts_maps_get_rel(t, make_map_rel(ep, base), base); + if (!tp) { + FAIL(); + } + *sp++ = tp; + break; case matchPop: ep = *(--sp); break; @@ -1987,6 +2068,23 @@ restart: } *esp++ = t; break; + case matchMkMap: + n = *pc++; + ehp = HAllocX(build_proc, 1 + MAP_HEADER_SIZE + n, HEAP_XTRA); + t = *ehp++ = *--esp; + { + map_t *m = (map_t *)ehp; + m->thing_word = MAP_HEADER; + m->size = n; + m->keys = t; + } + t = make_map(ehp); + ehp += MAP_HEADER_SIZE; + while (n--) { + *ehp++ = *--esp; + } + *esp++ = t; + break; case matchCall0: bif = (Eterm (*)(Process*, ...)) *pc++; t = (*bif)(build_proc, bif_args); @@ -3168,7 +3266,7 @@ int db_has_variable(Eterm obj) return(db_has_variable(obj)); /* Non wellformed list or [] */ } case TAG_PRIMARY_BOXED: - if (!BOXED_IS_TUPLE(obj)) { + if (!is_tuple(obj)) { return 0; } else { Eterm *tuple = tuple_val(obj); @@ -3243,7 +3341,6 @@ static DMCRet dmc_one_term(DMCContext *context, { Sint n; Eterm *hp; - ErlHeapFragment *tmp_mb; Uint sz, sz2, sz3; Uint i, j; @@ -3334,6 +3431,13 @@ static DMCRet dmc_one_term(DMCContext *context, DMC_PUSH(*text, n); DMC_PUSH(*stack, c); break; + case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): + n = map_get_size(map_val(c)); + DMC_PUSH(*text, matchPushM); + ++(context->stack_used); + DMC_PUSH(*text, n); + DMC_PUSH(*stack, c); + break; case (_TAG_HEADER_REF >> _TAG_PRIMARY_SIZE): { Eterm* ref_val = internal_ref_val(c); @@ -3415,16 +3519,8 @@ static DMCRet dmc_one_term(DMCContext *context, #endif break; default: /* BINARY, FUN, VECTOR, or EXTERNAL */ - /* - ** Make a private copy... - */ - n = size_object(c); - tmp_mb = new_message_buffer(n); - hp = tmp_mb->mem; DMC_PUSH(*text, matchEqBin); - DMC_PUSH(*text, copy_struct(c, n, &hp, &(tmp_mb->off_heap))); - tmp_mb->next = context->save; - context->save = tmp_mb; + DMC_PUSH(*text, dmc_private_copy(context, c)); break; } break; @@ -3436,6 +3532,22 @@ static DMCRet dmc_one_term(DMCContext *context, return retOk; } +/* +** Make a private copy of a term in a context. +*/ + +static Eterm +dmc_private_copy(DMCContext *context, Eterm c) +{ + Uint n = size_object(c); + ErlHeapFragment *tmp_mb = new_message_buffer(n); + Eterm *hp = tmp_mb->mem; + Eterm copy = copy_struct(c, n, &hp, &(tmp_mb->off_heap)); + tmp_mb->next = context->save; + context->save = tmp_mb; + return copy; +} + /* ** Match guard compilation */ @@ -3527,57 +3639,78 @@ static DMCRet dmc_list(DMCContext *context, return retOk; } -static DMCRet dmc_tuple(DMCContext *context, - DMCHeap *heap, - DMC_STACK_TYPE(UWord) *text, - Eterm t, - int *constant) +static void +dmc_rearrange_constants(DMCContext *context, DMC_STACK_TYPE(UWord) *text, + int textpos, Eterm *p, Uint nelems) { DMC_STACK_TYPE(UWord) instr_save; + Uint i; + + DMC_INIT_STACK(instr_save); + while (DMC_STACK_NUM(*text) > textpos) { + DMC_PUSH(instr_save, DMC_POP(*text)); + } + for (i = nelems; i--;) { + do_emit_constant(context, text, p[i]); + } + while(!DMC_EMPTY(instr_save)) { + DMC_PUSH(*text, DMC_POP(instr_save)); + } + DMC_FREE(instr_save); +} + +static DMCRet +dmc_array(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, + Eterm *p, Uint nelems, int *constant) +{ int all_constant = 1; int textpos = DMC_STACK_NUM(*text); - Eterm *p = tuple_val(t); - Uint nelems = arityval(*p); Uint i; - int c; - DMCRet ret; /* - ** We remember where we started to layout code, + ** We remember where we started to layout code, ** assume all is constant and back up and restart if not so. - ** The tuple should be laid out with the last element first, - ** so we can memcpy the tuple to the eheap. + ** The array should be laid out with the last element first, + ** so we can memcpy it to the eheap. */ - for (i = nelems; i > 0; --i) { - if ((ret = dmc_expr(context, heap, text, p[i], &c)) != retOk) - return ret; - if (!c && all_constant) { - all_constant = 0; - if (i < nelems) { - Uint j; + for (i = nelems; i--;) { + DMCRet ret; + int c; - /* - * Oops, we need to relayout the constants. - * Save the already laid out instructions. - */ - DMC_INIT_STACK(instr_save); - while (DMC_STACK_NUM(*text) > textpos) - DMC_PUSH(instr_save, DMC_POP(*text)); - for (j = nelems; j > i; --j) - do_emit_constant(context, text, p[j]); - while(!DMC_EMPTY(instr_save)) - DMC_PUSH(*text, DMC_POP(instr_save)); - DMC_FREE(instr_save); - } - } else if (c && !all_constant) { - /* push a constant */ - do_emit_constant(context, text, p[i]); - } + ret = dmc_expr(context, heap, text, p[i], &c); + if (ret != retOk) { + return ret; + } + if (!c && all_constant) { + all_constant = 0; + if (i < nelems - 1) { + dmc_rearrange_constants(context, text, textpos, + p + i + 1, nelems - i - 1); + } + } else if (c && !all_constant) { + do_emit_constant(context, text, p[i]); + } + } + *constant = all_constant; + return retOk; +} + +static DMCRet +dmc_tuple(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, + Eterm t, int *constant) +{ + int all_constant; + Eterm *p = tuple_val(t); + Uint nelems = arityval(*p); + DMCRet ret; + + ret = dmc_array(context, heap, text, p + 1, nelems, &all_constant); + if (ret != retOk) { + return ret; } - if (all_constant) { - *constant = 1; - return retOk; + *constant = 1; + return retOk; } DMC_PUSH(*text, matchMkTuple); DMC_PUSH(*text, nelems); @@ -3586,6 +3719,36 @@ static DMCRet dmc_tuple(DMCContext *context, return retOk; } +static DMCRet +dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, + Eterm t, int *constant) +{ + map_t *m = (map_t *)map_val(t); + Eterm *values = map_get_values(m); + int nelems = map_get_size(m); + int constant_values; + DMCRet ret; + + ret = dmc_array(context, heap, text, values, nelems, &constant_values); + if (ret != retOk) { + return ret; + } + if (constant_values) { + *constant = 1; + return retOk; + } + DMC_PUSH(*text, matchPushC); + DMC_PUSH(*text, dmc_private_copy(context, m->keys)); + if (++context->stack_used > context->stack_need) { + context->stack_need = context->stack_used; + } + DMC_PUSH(*text, matchMkMap); + DMC_PUSH(*text, nelems); + context->stack_used -= nelems; + *constant = 0; + return retOk; +} + static DMCRet dmc_whole_expression(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, @@ -4580,7 +4743,10 @@ static DMCRet dmc_expr(DMCContext *context, return ret; break; case TAG_PRIMARY_BOXED: - if (!BOXED_IS_TUPLE(t)) { + if (is_map(t)) { + return dmc_map(context, heap, text, t, constant); + } + if (!is_tuple(t)) { goto simple_term; } p = tuple_val(t); @@ -4855,7 +5021,7 @@ static Eterm my_copy_struct(Eterm t, Eterm **hp, ErlOffHeap* off_heap) *hp += 2; break; case TAG_PRIMARY_BOXED: - if (BOXED_IS_TUPLE(t)) { + if (is_tuple(t)) { if (arityval(*tuple_val(t)) == 1 && is_tuple(a = tuple_val(t)[1])) { Uint i,n; @@ -5126,6 +5292,12 @@ void db_match_dis(Binary *bp) ++t; erts_printf("Tuple\t%beu\n", n); break; + case matchMap: + ++t; + n = *t; + ++t; + erts_printf("Map\t%beu\n", n); + break; case matchPushT: ++t; n = *t; @@ -5136,6 +5308,18 @@ void db_match_dis(Binary *bp) ++t; erts_printf("PushL\n"); break; + case matchPushM: + ++t; + n = *t; + ++t; + erts_printf("PushM\t%beu\n", n); + break; + case matchPushK: + ++t; + p = (Eterm) *t; + ++t; + erts_printf("PushK\t%p (%T)\n", t, p); + break; case matchPop: ++t; erts_printf("Pop\n"); @@ -5252,6 +5436,12 @@ void db_match_dis(Binary *bp) ++t; erts_printf("MkTuple\t%beu\n", n); break; + case matchMkMap: + ++t; + n = *t; + ++t; + erts_printf("MkMapA\t%beu\n", n); + break; case matchOr: ++t; n = *t; diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index fdce157abc..fc4a5028e1 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -30,6 +30,7 @@ -export([fpe/1]). -export([otp_9422/1]). -export([faulty_seq_trace/1, do_faulty_seq_trace/0]). +-export([maps/1]). -export([runner/2, loop_runner/3]). -export([f1/1, f2/2, f3/2, fn/1, fn/2, fn/3]). -export([do_boxed_and_small/0]). @@ -62,7 +63,8 @@ all() -> moving_labels, faulty_seq_trace, empty_list, - otp_9422]; + otp_9422, + maps]; true -> [not_run] end. @@ -899,6 +901,31 @@ fpe(Config) when is_list(Config) -> _ -> ok end. +maps(Config) when is_list(Config) -> + {ok,#{},[],[]} = erlang:match_spec_test(#{}, [{'_',[],['$_']}], table), + {ok,#{},[],[]} = erlang:match_spec_test(#{}, [{#{},[],['$_']}], table), + {ok,false,[],[]} = + erlang:match_spec_test(#{}, [{not_a_map,[],['$_']}], table), + {ok,bar,[],[]} = + erlang:match_spec_test(#{foo => bar}, + [{#{foo => '$1'},[],['$1']}], + table), + {ok,false,[],[]} = + erlang:match_spec_test(#{foo => bar}, + [{#{foo => qux},[],[qux]}], + table), + {ok,false,[],[]} = + erlang:match_spec_test(#{}, [{#{foo => '_'},[],[foo]}], table), + {error,_} = + erlang:match_spec_test(#{}, [{#{'$1' => '_'},[],[foo]}], table), + {ok,bar,[],[]} = + erlang:match_spec_test({#{foo => bar}}, + [{{#{foo => '$1'}},[],['$1']}], + table), + {ok,#{foo := 3},[],[]} = + erlang:match_spec_test({}, [{{},[],[#{foo => {'+',1,2}}]}], table), + ok. + empty_list(Config) when is_list(Config) -> Val=[{'$1',[], [{message,'$1'},{message,{caller}},{return_trace}]}], %% Did crash debug VM in faulty assert: -- cgit v1.2.3 From 05c183c014c658810fc5a3391429eba9db14ac8f Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 18 Aug 2014 13:03:35 +0200 Subject: Ensure "runnable proc" trace messages are not sent out of order --- erts/emulator/beam/erl_message.c | 16 ++++++- erts/emulator/beam/erl_process.c | 92 ++++++++++++++++++++++++++++++---------- erts/emulator/beam/erl_process.h | 8 ++-- erts/emulator/beam/io.c | 15 ++++++- 4 files changed, 101 insertions(+), 30 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 59a677a12c..8870fac7d9 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -415,7 +415,13 @@ erts_queue_dist_message(Process *rcvr, if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ); - erts_proc_notify_new_message(rcvr); + erts_proc_notify_new_message(rcvr, +#ifdef ERTS_SMP + *rcvr_locks +#else + 0 +#endif + ); } } @@ -542,7 +548,13 @@ queue_message(Process *c_p, if (locked_msgq) erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ); - erts_proc_notify_new_message(receiver); + erts_proc_notify_new_message(receiver, +#ifdef ERTS_SMP + *receiver_locks +#else + 0 +#endif + ); #ifndef ERTS_SMP ERTS_HOLE_CHECK(receiver); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 1606ad119d..1571a78247 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -5877,6 +5877,9 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces case ERTS_ENQUEUE_NOT: if (erts_system_profile_flags.runnable_procs) { + /* Status lock prevents out of order "runnable proc" trace msgs */ + ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); + if (!(a & ERTS_PSFLG_ACTIVE_SYS) && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) { @@ -5990,7 +5993,8 @@ change_proc_schedule_state(Process *p, erts_aint32_t clear_state_flags, erts_aint32_t set_state_flags, erts_aint32_t *statep, - erts_aint32_t *enq_prio_p) + erts_aint32_t *enq_prio_p, + ErtsProcLocks locks) { /* * NOTE: ERTS_PSFLG_RUNNING, ERTS_PSFLG_RUNNING_SYS and @@ -5999,6 +6003,11 @@ change_proc_schedule_state(Process *p, */ erts_aint32_t a = *statep, n; int enqueue; /* < 0 -> use proxy */ + unsigned int prof_runnable_procs = erts_system_profile_flags.runnable_procs; + unsigned int lock_status = (prof_runnable_procs + && !(locks & ERTS_PROC_LOCK_STATUS)); + + ERTS_SMP_LC_ASSERT(locks == erts_proc_lc_my_proc_locks(p)); ASSERT(!(a & ERTS_PSFLG_PROXY)); ASSERT((clear_state_flags & (ERTS_PSFLG_RUNNING @@ -6008,6 +6017,9 @@ change_proc_schedule_state(Process *p, | ERTS_PSFLG_RUNNING_SYS | ERTS_PSFLG_ACTIVE_SYS)) == 0); + if (lock_status) + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + while (1) { erts_aint32_t e; n = e = a; @@ -6043,7 +6055,9 @@ change_proc_schedule_state(Process *p, break; } - if (erts_system_profile_flags.runnable_procs) { + if (prof_runnable_procs) { + + /* Status lock prevents out of order "runnable proc" trace msgs */ if (((n & (ERTS_PSFLG_SUSPENDED | ERTS_PSFLG_ACTIVE)) == ERTS_PSFLG_ACTIVE) @@ -6056,15 +6070,18 @@ change_proc_schedule_state(Process *p, profile_runnable_proc(p, am_active); } + if (lock_status) + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); } + *statep = a; return enqueue; } static ERTS_INLINE void -schedule_process(Process *p, erts_aint32_t in_state) +schedule_process(Process *p, erts_aint32_t in_state, ErtsProcLocks locks) { erts_aint32_t enq_prio = -1; erts_aint32_t state = in_state; @@ -6072,7 +6089,8 @@ schedule_process(Process *p, erts_aint32_t in_state) 0, ERTS_PSFLG_ACTIVE, &state, - &enq_prio); + &enq_prio, + locks); if (enqueue != ERTS_ENQUEUE_NOT) add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), state, @@ -6080,16 +6098,27 @@ schedule_process(Process *p, erts_aint32_t in_state) } void -erts_schedule_process(Process *p, erts_aint32_t state) +erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks) { - schedule_process(p, state); + schedule_process(p, state, locks); } static void schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) { + /* + * Expects status lock to be locked when called, and + * returns with status lock unlocked... + */ erts_aint32_t a = state, n, enq_prio = -1; int enqueue; /* < 0 -> use proxy */ + unsigned int prof_runnable_procs = erts_system_profile_flags.runnable_procs; + + /* Status lock prevents out of order "runnable proc" trace msgs */ + ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); + + if (!prof_runnable_procs) + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); ASSERT(!(state & ERTS_PSFLG_PROXY)); @@ -6098,7 +6127,7 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) n = e = a; if (a & ERTS_PSFLG_FREE) - return; /* We don't want to schedule free processes... */ + goto cleanup; /* We don't want to schedule free processes... */ enqueue = ERTS_ENQUEUE_NOT; n |= ERTS_PSFLG_ACTIVE_SYS; @@ -6111,7 +6140,7 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) goto cleanup; } - if (erts_system_profile_flags.runnable_procs) { + if (prof_runnable_procs) { if (!(a & (ERTS_PSFLG_ACTIVE_SYS | ERTS_PSFLG_RUNNING @@ -6121,6 +6150,8 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) profile_runnable_proc(p, am_active); } + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + prof_runnable_procs = 0; } if (enqueue != ERTS_ENQUEUE_NOT) { @@ -6135,8 +6166,14 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) } cleanup: + + if (prof_runnable_procs) + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + if (proxy) free_proxy_proc(proxy); + + ERTS_SMP_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); } static ERTS_INLINE int @@ -6203,7 +6240,7 @@ suspend_process(Process *c_p, Process *p) } static ERTS_INLINE void -resume_process(Process *p) +resume_process(Process *p, ErtsProcLocks locks) { erts_aint32_t state, enq_prio = -1; int enqueue; @@ -6220,7 +6257,8 @@ resume_process(Process *p) ERTS_PSFLG_SUSPENDED, 0, &state, - &enq_prio); + &enq_prio, + locks); if (enqueue) add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), state, @@ -8036,7 +8074,8 @@ handle_pend_sync_suspend(Process *suspendee, } /* suspender is suspended waiting for suspendee to suspend; resume suspender */ - resume_process(suspender); + ASSERT(suspendee != suspender); + resume_process(suspender, ERTS_PROC_LOCK_STATUS); erts_smp_proc_unlock(suspender, ERTS_PROC_LOCK_STATUS); } } @@ -8071,7 +8110,7 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, ASSERT(c_p->flags & F_P2PNR_RESCHED); c_p->flags &= ~F_P2PNR_RESCHED; if (!suspend && rp) - resume_process(rp); + resume_process(rp, rp_locks); } else { @@ -8229,7 +8268,8 @@ handle_pend_bif_sync_suspend(Process *suspendee, } /* suspender is suspended waiting for suspendee to suspend; resume suspender */ - resume_process(suspender); + ASSERT(suspender != suspendee); + resume_process(suspender, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); erts_smp_proc_unlock(suspender, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); } @@ -8589,7 +8629,8 @@ resume_process_1(BIF_ALIST_1) ASSERT(ERTS_PSFLG_SUSPENDED & erts_smp_atomic32_read_nob(&suspendee->state)); - resume_process(suspendee); + ASSERT(BIF_P != suspendee); + resume_process(suspendee, ERTS_PROC_LOCK_STATUS); erts_smp_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS); } @@ -8719,7 +8760,7 @@ erts_resume(Process* process, ErtsProcLocks process_locks) 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); - resume_process(process); + resume_process(process, process_locks|ERTS_PROC_LOCK_STATUS); if (!(process_locks & ERTS_PROC_LOCK_STATUS)) erts_smp_proc_unlock(process, ERTS_PROC_LOCK_STATUS); } @@ -8738,7 +8779,7 @@ erts_resume_processes(ErtsProcList *list) proc = erts_pid2proc(NULL, 0, plp->pid, ERTS_PROC_LOCK_STATUS); if (proc) { if (erts_proclist_same(plp, proc)) { - resume_process(proc); + resume_process(proc, ERTS_PROC_LOCK_STATUS); nresumed++; } erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_STATUS); @@ -9974,8 +10015,10 @@ erts_internal_request_system_task_3(BIF_ALIST_3) rp_state = n; } - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); - + /* + * schedule_process_sys_task() unlocks status + * lock on process. + */ schedule_process_sys_task(rp, rp_state, NULL); if (free_stqs) @@ -10720,7 +10763,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). * Schedule process for execution. */ - schedule_process(p, state); + schedule_process(p, state, 0); VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->common.id)); @@ -11041,7 +11084,8 @@ set_proc_exiting(Process *p, ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT, ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE, &state, - &enq_prio); + &enq_prio, + ERTS_PROC_LOCKS_ALL); p->fvalue = reason; if (bp) @@ -11082,7 +11126,8 @@ set_proc_self_exiting(Process *c_p) ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT, ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE, &state, - &enq_prio); + &enq_prio, + ERTS_PROC_LOCKS_ALL); ASSERT(!enqueue); return state; @@ -11727,8 +11772,9 @@ resume_suspend_monitor(ErtsSuspendMonitor *smon, void *vc_p) Process *suspendee = erts_pid2proc((Process *) vc_p, ERTS_PROC_LOCK_MAIN, smon->pid, ERTS_PROC_LOCK_STATUS); if (suspendee) { + ASSERT(suspendee != vc_p); if (smon->active) - resume_process(suspendee); + resume_process(suspendee, ERTS_PROC_LOCK_STATUS); erts_smp_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS); } erts_destroy_suspend_monitor(smon); @@ -12061,7 +12107,7 @@ timeout_proc(Process* p) state = erts_smp_atomic32_read_acqb(&p->state); if (!(state & ERTS_PSFLG_ACTIVE)) - schedule_process(p, state); + schedule_process(p, state, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS); } diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index ed6dadbffa..084399726c 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1704,17 +1704,17 @@ ErtsSchedulerData *erts_get_scheduler_data(void) #endif #endif -void erts_schedule_process(Process *, erts_aint32_t); +void erts_schedule_process(Process *, erts_aint32_t, ErtsProcLocks); -ERTS_GLB_INLINE void erts_proc_notify_new_message(Process *p); +ERTS_GLB_INLINE void erts_proc_notify_new_message(Process *p, ErtsProcLocks locks); #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE void -erts_proc_notify_new_message(Process *p) +erts_proc_notify_new_message(Process *p, ErtsProcLocks locks) { /* 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); + erts_schedule_process(p, state, locks); } #endif diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 0f86d8e41d..50e08de8a4 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1253,12 +1253,18 @@ try_imm_drv_call(ErtsTryImmDrvCallState *sp) goto locked_fail; } - if (!c_p) reds_left_in = CONTEXT_REDS/10; else { if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS)) trace_virtual_sched(c_p, am_out); + /* + * No status lock held while sending runnable + * proc trace messages. It is however not needed + * in this case, since only this thread can send + * such messages for this process until the process + * has been scheduled out. + */ if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) profile_runnable_proc(c_p, am_inactive); @@ -1319,6 +1325,13 @@ finalize_imm_drv_call(ErtsTryImmDrvCallState *sp) if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS)) trace_virtual_sched(c_p, am_in); + /* + * No status lock held while sending runnable + * proc trace messages. It is however not needed + * in this case, since only this thread can send + * such messages for this process until the process + * has been scheduled out. + */ if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) profile_runnable_proc(c_p, am_active); -- cgit v1.2.3 From e8c43908d0634763c8347883ff3e69635263e039 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 18 Aug 2014 15:11:39 +0200 Subject: Ensure "runnable port" trace messages are not sent out of order --- erts/emulator/beam/erl_lock_check.c | 2 +- erts/emulator/beam/erl_port_task.c | 48 +++++++++++++++++++-------- erts/emulator/beam/erl_port_task.h | 2 ++ erts/emulator/beam/io.c | 65 ++++++++++++++++++++++++++++--------- 4 files changed, 86 insertions(+), 31 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index c665aa51a2..b105ece6f1 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -139,7 +139,6 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "drv_tsd", NULL }, { "async_enq_mtx", NULL }, #ifdef ERTS_SMP - { "sys_msg_q", NULL }, { "atom_tab", NULL }, { "make_ref", NULL }, { "misc_op_list_pre_alloc_lock", "address" }, @@ -148,6 +147,7 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "btm_pre_alloc_lock", NULL, }, { "dist_entry_out_queue", "address" }, { "port_sched_lock", "port_id" }, + { "sys_msg_q", NULL }, { "port_table", NULL }, #endif { "mtrace_op", NULL }, diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 4103d1192a..682f6f8f4b 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -999,6 +999,7 @@ static ERTS_INLINE int finalize_exec(Port *pp, ErtsPortTask **execq, int processing_busy_q) { erts_aint32_t act; + unsigned int prof_runnable_ports; if (!processing_busy_q) pp->sched.taskq.local.first = *execq; @@ -1015,6 +1016,10 @@ finalize_exec(Port *pp, ErtsPortTask **execq, int processing_busy_q) if (act & ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q) act = check_unset_busy_port_q(pp, act, pp->sched.taskq.bpq); + prof_runnable_ports = erts_system_profile_flags.runnable_ports; + if (prof_runnable_ports) + erts_port_task_sched_lock(&pp->sched); + while (1) { erts_aint32_t new, exp; @@ -1026,12 +1031,24 @@ finalize_exec(Port *pp, ErtsPortTask **execq, int processing_busy_q) act = erts_smp_atomic32_cmpxchg_relb(&pp->sched.flags, new, exp); - ASSERT(!(act & ERTS_PTS_FLG_IN_RUNQ)); + ERTS_LC_ASSERT(!(act & ERTS_PTS_FLG_IN_RUNQ)); + ERTS_LC_ASSERT(!(act & ERTS_PTS_FLG_EXEC_IMM)); if (exp == act) break; } + if (prof_runnable_ports | IS_TRACED_FL(pp, F_TRACE_SCHED_PORTS)) { + /* trace port scheduling, out */ + if (IS_TRACED_FL(pp, F_TRACE_SCHED_PORTS)) + trace_sched_ports(pp, am_out); + if (prof_runnable_ports) { + if (!(act & (ERTS_PTS_FLG_EXEC_IMM|ERTS_PTS_FLG_HAVE_TASKS))) + profile_runnable_port(pp, am_inactive); + erts_port_task_sched_unlock(&pp->sched); + } + } + return (act & ERTS_PTS_FLG_HAVE_TASKS) != 0; } @@ -1377,6 +1394,7 @@ erts_port_task_schedule(Eterm id, Port *pp; ErtsPortTask *ptp = NULL; erts_aint32_t act, add_flags; + unsigned int prof_runnable_ports; if (pthp && erts_port_task_is_scheduled(pthp)) { ASSERT(0); @@ -1465,6 +1483,10 @@ erts_port_task_schedule(Eterm id, if (ns_pthlp) add_flags |= ERTS_PTS_FLG_HAVE_NS_TASKS; + prof_runnable_ports = erts_system_profile_flags.runnable_ports; + if (prof_runnable_ports) + erts_port_task_sched_lock(&pp->sched); + while (1) { erts_aint32_t new, exp; @@ -1489,6 +1511,13 @@ erts_port_task_schedule(Eterm id, goto done; /* Died after our task insert... */ } + if (prof_runnable_ports) { + if (!(act & ERTS_PTS_FLG_EXEC_IMM)) + profile_runnable_port(pp, am_active); + erts_port_task_sched_unlock(&pp->sched); + prof_runnable_ports = 0; + } + /* Enqueue port on run-queue */ runq = erts_port_runq(pp); @@ -1510,10 +1539,6 @@ erts_port_task_schedule(Eterm id, #endif enqueue_port(runq, pp); - - if (erts_system_profile_flags.runnable_ports) { - profile_runnable_port(pp, am_active); - } erts_smp_runq_unlock(runq); @@ -1521,6 +1546,9 @@ erts_port_task_schedule(Eterm id, done: + if (prof_runnable_ports) + erts_port_task_sched_unlock(&pp->sched); + #ifdef ERTS_SMP if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED) erts_port_dec_refc(pp); @@ -1777,10 +1805,6 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) erts_unblock_fpe(fpe_was_unmasked); - /* trace port scheduling, out */ - if (IS_TRACED_FL(pp, F_TRACE_SCHED_PORTS)) { - trace_sched_ports(pp, am_out); - } if (io_tasks_executed) { ASSERT(erts_smp_atomic_read_nob(&erts_port_task_outstanding_io_tasks) @@ -1803,11 +1827,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) erts_smp_runq_lock(runq); - if (!active) { - if (erts_system_profile_flags.runnable_ports) - profile_runnable_port(pp, am_inactive); - } - else { + if (active) { #ifdef ERTS_SMP ErtsRunQueue *xrunq; #endif diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h index 1d30465ec9..9ef0cfcedc 100644 --- a/erts/emulator/beam/erl_port_task.h +++ b/erts/emulator/beam/erl_port_task.h @@ -78,6 +78,7 @@ extern erts_smp_atomic_t erts_port_task_outstanding_io_tasks; #define ERTS_PTS_FLG_PARALLELISM (((erts_aint32_t) 1) << 9) #define ERTS_PTS_FLG_FORCE_SCHED (((erts_aint32_t) 1) << 10) #define ERTS_PTS_FLG_EXITING (((erts_aint32_t) 1) << 11) +#define ERTS_PTS_FLG_EXEC_IMM (((erts_aint32_t) 1) << 12) #define ERTS_PTS_FLGS_BUSY \ (ERTS_PTS_FLG_BUSY_PORT | ERTS_PTS_FLG_BUSY_PORT_Q) @@ -87,6 +88,7 @@ extern erts_smp_atomic_t erts_port_task_outstanding_io_tasks; | ERTS_PTS_FLG_HAVE_BUSY_TASKS \ | ERTS_PTS_FLG_HAVE_TASKS \ | ERTS_PTS_FLG_EXEC \ + | ERTS_PTS_FLG_EXEC_IMM \ | ERTS_PTS_FLG_FORCE_SCHED \ | ERTS_PTS_FLG_EXITING) diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 50e08de8a4..ae053fc191 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1218,9 +1218,10 @@ typedef struct { static ERTS_INLINE ErtsTryImmDrvCallResult try_imm_drv_call(ErtsTryImmDrvCallState *sp) { + unsigned int prof_runnable_ports; ErtsTryImmDrvCallResult res; int reds_left_in; - erts_aint32_t invalid_state, invalid_sched_flags; + erts_aint32_t act, exp, invalid_state, invalid_sched_flags; Port *prt = sp->port; Process *c_p = sp->c_p; @@ -1247,11 +1248,26 @@ try_imm_drv_call(ErtsTryImmDrvCallState *sp) goto locked_fail; } - sp->sched_flags = erts_smp_atomic32_read_nob(&prt->sched.flags); - if (sp->sched_flags & invalid_sched_flags) { - res = ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS; - goto locked_fail; - } + prof_runnable_ports = erts_system_profile_flags.runnable_ports; + if (prof_runnable_ports) + erts_port_task_sched_lock(&prt->sched); + + act = erts_smp_atomic32_read_nob(&prt->sched.flags); + + do { + erts_aint32_t new; + + if (act & invalid_sched_flags) { + res = ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS; + sp->sched_flags = act; + goto locked_fail; + } + exp = act; + new = act | ERTS_PTS_FLG_EXEC_IMM; + act = erts_smp_atomic32_cmpxchg_mb(&prt->sched.flags, new, exp); + } while (act != exp); + + sp->sched_flags = act; if (!c_p) reds_left_in = CONTEXT_REDS/10; @@ -1279,11 +1295,14 @@ try_imm_drv_call(ErtsTryImmDrvCallState *sp) ERTS_SMP_CHK_NO_PROC_LOCKS; - if (IS_TRACED_FL(prt, F_TRACE_SCHED_PORTS)) - trace_sched_ports_where(prt, am_in, sp->port_op); - if (erts_system_profile_flags.runnable_ports - && !erts_port_is_scheduled(prt)) - profile_runnable_port(prt, am_active); + if (prof_runnable_ports | IS_TRACED_FL(prt, F_TRACE_SCHED_PORTS)) { + if (prof_runnable_ports && !(act & (ERTS_PTS_FLG_IN_RUNQ|ERTS_PTS_FLG_EXEC))) + profile_runnable_port(prt, am_active); + if (IS_TRACED_FL(prt, F_TRACE_SCHED_PORTS)) + trace_sched_ports_where(prt, am_in, sp->port_op); + if (prof_runnable_ports) + erts_port_task_sched_unlock(&prt->sched); + } sp->fpe_was_unmasked = erts_block_fpe(); @@ -1300,17 +1319,31 @@ finalize_imm_drv_call(ErtsTryImmDrvCallState *sp) int reds; Port *prt = sp->port; Process *c_p = sp->c_p; + erts_aint32_t act; + unsigned int prof_runnable_ports; reds = prt->reds; reds += erts_port_driver_callback_epilogue(prt, NULL); erts_unblock_fpe(sp->fpe_was_unmasked); - if (IS_TRACED_FL(prt, F_TRACE_SCHED_PORTS)) - trace_sched_ports_where(prt, am_out, sp->port_op); - if (erts_system_profile_flags.runnable_ports - && !erts_port_is_scheduled(prt)) - profile_runnable_port(prt, am_inactive); + prof_runnable_ports = erts_system_profile_flags.runnable_ports; + if (prof_runnable_ports) + erts_port_task_sched_lock(&prt->sched); + + act = erts_smp_atomic32_read_band_mb(&prt->sched.flags, + ~ERTS_PTS_FLG_EXEC_IMM); + ERTS_SMP_LC_ASSERT(act & ERTS_PTS_FLG_EXEC_IMM); + + if (prof_runnable_ports | IS_TRACED_FL(prt, F_TRACE_SCHED_PORTS)) { + if (IS_TRACED_FL(prt, F_TRACE_SCHED_PORTS)) + trace_sched_ports_where(prt, am_out, sp->port_op); + if (prof_runnable_ports) { + if (!(act & (ERTS_PTS_FLG_IN_RUNQ|ERTS_PTS_FLG_EXEC))) + profile_runnable_port(prt, am_inactive); + erts_port_task_sched_unlock(&prt->sched); + } + } erts_port_release(prt); -- cgit v1.2.3 From 14e7c8f79847668d815a55f4328f3a86bd527451 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 19 Aug 2014 12:23:11 +0200 Subject: Fix busy_port_SUITE:io_to_busy test-case --- erts/emulator/test/busy_port_SUITE.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index 4b4af0babe..2ed5aaa0d0 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -98,8 +98,10 @@ generator(0, Writer, _Data) -> %% Calling process_info(Pid, current_function) on a suspended process %% used to crash Beam. - {current_function, {erlang, send, 2}} = - process_info(Writer, current_function), + case process_info(Writer, [status,current_function]) of + [{status,suspended},{current_function,{erlang,send,2}}] -> ok; + [{status,suspended},{current_function,{erlang,bif_return_trap,_}}] -> ok + end, unlock_slave(); generator(N, Writer, Data) -> Writer ! {exec, Data}, -- cgit v1.2.3 From 44edeae19bec364b9689f84b06da140e47401eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Jun 2014 14:47:18 +0200 Subject: erts: Add BIF erlang:get_keys/0 Returns a list of all keys in the process dictionary. --- erts/emulator/beam/bif.tab | 11 ++++++-- erts/emulator/beam/erl_process_dict.c | 52 +++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index e68b8e6274..55ac778475 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -578,7 +578,7 @@ bif io:printable_range/0 bif os:unsetenv/1 # -# New in R17A +# New in 17.0 # bif re:inspect/2 @@ -601,9 +601,16 @@ bif maps:values/1 bif erts_internal:cmp_term/2 # -# New in 17.1. +# New in 17.1 # + bif erlang:fun_info_mfa/1 + +# New in 18.0 +# + +bif erlang:get_keys/0 + # # Obsolete # diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 23e5bf737f..3ce707efda 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -82,6 +82,7 @@ static void pd_hash_erase(Process *p, Eterm id, Eterm *ret); static void pd_hash_erase_all(Process *p); static Eterm pd_hash_get_keys(Process *p, Eterm value); +static Eterm pd_hash_get_all_keys(Process *p, ProcDict *pd); static Eterm pd_hash_get_all(Process *p, ProcDict *pd); static Eterm pd_hash_put(Process *p, Eterm id, Eterm value); @@ -275,6 +276,16 @@ BIF_RETTYPE get_1(BIF_ALIST_1) BIF_RET(ret); } +BIF_RETTYPE get_keys_0(BIF_ALIST_0) +{ + Eterm ret; + + PD_CHECK(BIF_P->dictionary); + ret = pd_hash_get_all_keys(BIF_P,BIF_P->dictionary); + PD_CHECK(BIF_P->dictionary); + BIF_RET(ret); +} + BIF_RETTYPE get_keys_1(BIF_ALIST_1) { Eterm ret; @@ -412,6 +423,47 @@ Eterm erts_pd_hash_get(Process *p, Eterm id) return am_undefined; } +#define PD_GET_TKEY(Dst,Src) \ +do { \ + ASSERT(is_tuple((Src))); \ + ASSERT(arityval(*((Eterm*)tuple_val((Src)))) == 2); \ + (Dst) = ((Eterm*)tuple_val((Src)))[1]; \ +} while(0) + +static Eterm pd_hash_get_all_keys(Process *p, ProcDict *pd) { + Eterm* hp; + Eterm res = NIL; + Eterm tmp, tmp2; + unsigned int i; + unsigned int num; + + if (pd == NULL) { + return res; + } + + num = HASH_RANGE(pd); + hp = HAlloc(p, pd->numElements * 2); + + for (i = 0; i < num; ++i) { + tmp = ARRAY_GET(pd, i); + if (is_boxed(tmp)) { + PD_GET_TKEY(tmp,tmp); + res = CONS(hp, tmp, res); + hp += 2; + } else if (is_list(tmp)) { + while (tmp != NIL) { + tmp2 = TCAR(tmp); + PD_GET_TKEY(tmp2,tmp2); + res = CONS(hp, tmp2, res); + hp += 2; + tmp = TCDR(tmp); + } + } + } + return res; +} +#undef PD_GET_TKEY + static Eterm pd_hash_get_keys(Process *p, Eterm value) { Eterm *hp; -- cgit v1.2.3 From 7cd4ac96cb8e8bef78c6eebfadb2e9590f88055d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Jun 2014 15:53:11 +0200 Subject: erts: Add spec for erlang:get_keys/0 --- erts/preloaded/src/erlang.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 7903901b81..b96a601792 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -93,7 +93,7 @@ float_to_list/1, float_to_list/2]). -export([fun_info/2, fun_info_mfa/1, fun_to_list/1, function_exported/3]). -export([garbage_collect/0, garbage_collect/1, garbage_collect/2]). --export([garbage_collect_message_area/0, get/0, get/1, get_keys/1]). +-export([garbage_collect_message_area/0, get/0, get/1, get_keys/0, get_keys/1]). -export([get_module_info/1, get_stacktrace/0, group_leader/0]). -export([group_leader/2, halt/0, halt/1, halt/2, hash/2, hibernate/3]). -export([insert_element/3]). @@ -931,6 +931,12 @@ get() -> get(_Key) -> erlang:nif_error(undefined). +%% get_keys/0 +-spec get_keys() -> [Key] when + Key :: term(). +get_keys() -> + erlang:nif_error(undefined). + %% get_keys/1 -spec get_keys(Val) -> [Key] when Val :: term(), -- cgit v1.2.3 From 4ec6f6875469f32bc499876605df2542a4c13532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Jun 2014 16:37:26 +0200 Subject: erts: Document erlang:get_keys/0 --- erts/doc/src/erlang.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 3d8ef9a97d..97fe6d2915 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1376,6 +1376,19 @@ true alive; otherwise the atom nocookie.

+ + + Return a list of all keys from the process dictionary + +

Returns a list of keys all keys present in the process dictionary.

+
+> put(dog, {animal,1}),
+put(cow, {animal,2}),
+put(lamb, {animal,3}),
+get_keys().
+[dog,cow,lamb]
+
+
Return a list of keys from the process dictionary -- cgit v1.2.3 From 6ec6493dcca2724493700e7d269d2984efe30b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 22 Aug 2014 19:06:16 +0200 Subject: Update preloaded erlang.beam --- erts/preloaded/ebin/erlang.beam | Bin 97908 -> 98008 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 3bf5f42a43..32ff6a3874 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ -- cgit v1.2.3 From ceec6ba5c24ceef3c074afc2b49d8e77c492c0c4 Mon Sep 17 00:00:00 2001 From: Michael Truog Date: Fri, 22 Aug 2014 11:16:02 -0700 Subject: Fix ERTS_POLL_DEBUG_PRINT usage --- erts/emulator/sys/common/erl_poll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 0a58a625b2..aa412a20c8 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -2157,7 +2157,7 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps, #ifdef ERTS_POLL_DEBUG_PRINT erts_printf("Entering erts_poll_wait(), timeout=%d\n", - (int) tv->tv_sec*1000 + tv->tv_usec/1000); + (int) tvp->tv_sec*1000 + tvp->tv_usec/1000); #endif if (ERTS_POLLSET_SET_POLLED_CHK(ps)) { -- cgit v1.2.3 From 63d7fbe5cdf2b714500467b771a237e9ec11aaa8 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Sun, 24 Aug 2014 19:05:02 +0200 Subject: Fix misspellings of 'another' --- erts/emulator/drivers/common/inet_drv.c | 2 +- erts/emulator/drivers/unix/multi_drv.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 09d90f4984..891589d1c5 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -5778,7 +5778,7 @@ done: ia_p->Ipv6IfIndex && ia_p->Ipv6IfIndex != index) { - /* Oops, there was an other interface for IPv6. Possible? XXX */ + /* Oops, there was another interface for IPv6. Possible? XXX */ index = ia_p->Ipv6IfIndex; goto index; } diff --git a/erts/emulator/drivers/unix/multi_drv.c b/erts/emulator/drivers/unix/multi_drv.c index 822c96730c..724d325ed5 100644 --- a/erts/emulator/drivers/unix/multi_drv.c +++ b/erts/emulator/drivers/unix/multi_drv.c @@ -20,7 +20,7 @@ /* Purpose: Multidriver interface This is an example of a driver which allows multiple instances of itself. I.e have one erlang process execute open_port(multi......) and - at the same time have an other erlang process open an other port + at the same time have another erlang process open another port running multi there as well. */ -- cgit v1.2.3 From e167bca85a86cc7a149d53da5cdd08b0905e71a6 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Thu, 29 May 2014 22:10:06 -0400 Subject: add enif_schedule_nif() to NIF API In the #erlang IRC channel Anthony Ramine once mentioned the idea of allowing a NIF to use an emulator trap, similar to a BIF trap, to schedule another NIF for execution. This is exactly how dirty NIFs were implemented for Erlang/OTP 17.0, so this commit refactors and generalizes that dirty NIF code to support a new enif_schedule_nif() API function. The enif_schedule_nif() function allows a long-running NIF to be broken into separate NIF invocations. The NIF first executes part of the long-running task, then calls enif_schedule_nif() to schedule a NIF for later execution to continue the task. Any number of NIFs can be scheduled in this manner, one after another. Since the emulator regains control between invocations, this helps avoid problems caused by native code tying up scheduler threads for too long. The enif_schedule_nif() function also replaces the original experimental dirty NIF API. The function takes a flags parameter that a caller can use to indicate the NIF should be scheduled onto either a dirty CPU scheduler thread, a dirty I/O scheduler thread, or scheduled as a regular NIF on a regular scheduler thread. With this change, the original experimental enif_schedule_dirty_nif(), enif_schedule_dirty_nif_finalizer() and enif_dirty_nif_finalizer() API functions are no longer needed and have been removed. Explicit scheduling of a dirty NIF finalization function is no longer necessary; if an application wants similar functionality, it can have a dirty NIF just invoke enif_schedule_nif() to schedule a non-dirty NIF to complete its task. Lift the restriction that dirty NIFs can't call enif_make_badarg() to raise an exception. This was a problem with the original dirty NIF API because it forced developers to get and check all incoming arguments in a regular NIF, and then schedule the dirty NIF which then had to get all the arguments again. Now, the argument checking can be done in the dirty NIF and it can call enif_make_badarg() itself to flag incorrect arguments. Extend the ErlNifFunc struct with a new flags field that allows NIFs to be declared as dirty. The default value for this field is 0, indicating a regular NIF, so it's backwards compatible with all existing statically initialized ErlNifFunc struct instances, and so such instances require no code changes. Defining the flags field with a value of ERL_NIF_DIRTY_JOB_CPU_BOUND indicates that the NIF should execute on a dirty CPU scheduler thread, or defining it with a value of ERL_NIF_DIRTY_JOB_IO_BOUND indicates that the NIF should execute on a dirty I/O scheduler thread. Any other flags field value causes a NIF library loading error. Extend the ErlNifEntry struct with a new options field that indicates whether a NIF library was built with support for optional features such as dirty NIFs. When a NIF library is loaded, the runtime checks the options field to ensure compatibility. If a NIF library built with dirty NIF support is loaded into a runtime that does not support dirty NIFs, and the library defines one or more ErlNifFunc entries with non-zero flags fields indicating dirty NIFs, a NIF library loading error results. There is no error if a NIF library built with dirty NIF support is loaded into a runtime that does not support dirty NIFs but the library does not have any dirty NIFs. It is also not an error if a library without dirty NIF support is loaded into a runtime built with dirty NIF support. Add documentation and tests for enif_schedule_nif(). --- erts/doc/src/erl_nif.xml | 141 ++++--- erts/emulator/beam/beam_emu.c | 8 +- erts/emulator/beam/beam_load.c | 6 +- erts/emulator/beam/erl_gc.c | 14 + erts/emulator/beam/erl_nif.c | 513 ++++++++++++++++++++------ erts/emulator/beam/erl_nif.h | 26 +- erts/emulator/beam/erl_nif_api_funcs.h | 8 +- erts/emulator/beam/erl_process.c | 34 +- erts/emulator/beam/erl_process.h | 24 +- erts/emulator/test/nif_SUITE.erl | 40 +- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 73 +++- 11 files changed, 626 insertions(+), 261 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 6b1f4cccf8..1d33b334bb 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -168,16 +168,18 @@ ok

As mentioned in the warning text at the beginning of this document it is of vital importance that a native function - does return relatively fast. It is hard to give an exact maximum amount + return relatively quickly. It is hard to give an exact maximum amount of time that a native function is allowed to work, but as a rule of thumb - a well behaving native function should return to its caller before a + a well-behaving native function should return to its caller before a millisecond has passed. This can be achieved using different approaches. - If you have full control over the code that are to execute in the native + If you have full control over the code to execute in the native function, the best approach is to divide the work into multiple chunks of - work and call the native function multiple times. Function + work and call the native function multiple times, either directly from Erlang code + or by having a native function schedule a future NIF call via the + enif_schedule_nif function. Function enif_consume_timeslice can be - used this facilitate such work division. In some cases, however, this might not - be possible, e.g. when calling third party libraries. Then you typically want + used to help with such work division. In some cases, however, this might not + be possible, e.g. when calling third-party libraries. Then you typically want to dispatch the work to another thread, return from the native function, and wait for the result. The thread can send the result back to the calling thread using message passing. Information @@ -342,29 +344,31 @@ ok libraries might however fail if deprecated features are used.

- Dirty NIFs -

Note that the dirty NIF functionality - is experimental and that you have to enable support for dirty - schedulers when building OTP in order to try the functionality out. Native functions + Long-running NIFs +

Native functions must normally run quickly, as explained earlier in this document. They generally should execute for no more than a millisecond. But not all native functions can execute so quickly; for example, functions that encrypt large blocks of data or perform lengthy file system operations can often run for tens of seconds or more.

-

A NIF that cannot execute in a millisecond or less is called a "dirty NIF" since - it performs work that the Erlang runtime cannot handle cleanly. Applications - that make use of such functions must indicate to the runtime that the functions are +

If the functionality of a long-running NIF can be split so that its work can be + achieved through a series of shorter NIF calls, the application can either make that series + of NIF calls from the Erlang level, or it can call a NIF that first performs a chunk of the + work, then invokes the enif_schedule_nif + function to schedule another NIF call to perform the next chunk. The final call scheduled + in this manner can then return the overall result. Breaking up a long-running function in + this manner enables the VM to regain control between calls to the NIFs, thereby avoiding + degraded responsiveness, scheduler load balancing problems, and other strange behaviours.

+

A NIF that cannot be split and cannot execute in a millisecond or less is called a "dirty NIF" + because it performs work that the Erlang runtime cannot handle cleanly. + Note that the dirty NIF functionality described here is experimental and that you have to + enable support for dirty schedulers when building OTP in order to try the functionality out. + Applications that make use of such functions must indicate to the runtime that the functions are dirty so they can be handled specially. To schedule a dirty NIF for execution, the - application calls enif_schedule_dirty_nif, - passing to it a pointer to the dirty NIF to be executed and indicating with a flag + appropriate flags value can be set for the NIF in its ErlNifFunc + entry, or the application can call enif_schedule_nif, + passing to it a pointer to the dirty NIF to be executed and indicating with the flags argument whether it expects the operation to be CPU-bound or I/O-bound.

-

All dirty NIFs must ultimately invoke the - enif_schedule_dirty_nif_finalizer as their final action, passing to it the - result they wish to return to the original caller. A finalizer function can either - receive the result and return it directly, or it can return a different value instead. - For convenience, the NIF API provides the - enif_dirty_nif_finalizer function that applications can use as a finalizer; - it simply returns its result argument.

Dirty NIF support is available only when the emulator is configured with dirty schedulers enabled. This feature is currently disabled by default. To determine whether the dirty NIF API is available, native code can check to see if the C preprocessor macro @@ -498,6 +502,7 @@ typedef struct { const char* name; unsigned arity; ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + unsigned flags; } ErlNifFunc;

Describes a NIF by its name, arity and implementation. @@ -508,7 +513,17 @@ typedef struct { will thus denote the Nth argument to the NIF. Note that the argc argument allows for the same C function to implement several Erlang functions with different arity (but - same name probably).

+ same name probably). For a regular NIF, flags is 0 (and + so its value can be omitted for statically initialized ErlNifFunc + instances), or it can be used to indicate that the NIF is a dirty NIF that should be executed + on a dirty scheduler thread (note that the dirty NIF functionality + described here is experimental and that you have to enable + support for dirty schedulers when building OTP in order to try the + functionality out). If the dirty NIF is expected to be + CPU-bound, its flags field should be set to + ERL_NIF_DIRTY_JOB_CPU_BOUND, or for I/O-bound jobs, + ERL_NIF_DIRTY_JOB_IO_BOUND.

ErlNifBinary @@ -672,18 +687,6 @@ typedef enum { See also the warning text at the beginning of this document.

- ERL_NIF_TERMenif_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result) - Simple dirty NIF result finalizer - -

A convenience function that a dirty NIF can use as a finalizer that simply - return its result argument as its return value. This function is provided - for dirty NIFs with results that should be returned directly to the original caller.

-

This function is available only when the emulator is configured with dirty - schedulers enabled. This feature is currently disabled by default. To determine whether - the dirty NIF API is available, native code can check to see if the C preprocessor macro - ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined.

-
-
intenif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)

Same as erl_drv_equal_tids. @@ -811,9 +814,9 @@ typedef enum { built with threading support, dirty scheduler threads are available and enif_have_dirty_schedulers() returns true. If the emulator was built without threading support, enif_have_dirty_schedulers() returns false.

-

If dirty scheduler threads are not available in the emulator, calls to - enif_schedule_dirty_nif and enif_schedule_dirty_nif_finalizer result in - the NIF and finalizer functions being called directly within the calling thread.

+

If dirty scheduler threads are not available in the emulator, a call to + enif_schedule_nif with its flags argument set to indicate that the specified + NIF is to be executed on a dirty scheduler thread results in a badarg exception.

This function is available only when the emulator is configured with dirty schedulers enabled. This feature is currently disabled by default. To determine whether the dirty NIF API is available, native code can check to see if the C preprocessor macro @@ -873,8 +876,8 @@ typedef enum {

Check to see if the current NIF is executing on a dirty scheduler thread. If the emulator is built with threading support, calling enif_is_on_dirty_scheduler from within a dirty NIF returns true. It returns false when the calling NIF is a regular - NIF or a NIF finalizer, both of which run on normal scheduler threads, or when the emulator - is built without threading support.

+ NIF running on a normal scheduler thread, or when the emulator is built without threading + support.

This function is available only when the emulator is configured with dirty schedulers enabled. This feature is currently disabled by default. To determine whether the dirty NIF API is available, native code can check to see if the C preprocessor macro @@ -1245,46 +1248,27 @@ typedef enum {

Same as erl_drv_rwlock_tryrwlock.

- ERL_NIF_TERMenif_schedule_dirty_nif(ErlNifEnv* env, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM argv[]) - Schedule a dirty NIF for execution + ERL_NIF_TERMenif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM argv[]) + Schedule a NIF for execution -

Schedule dirty NIF fp to execute a long-running operation. The flags - argument must be set to either ERL_NIF_DIRTY_JOB_CPU_BOUND if the job is expected to - be primarily CPU-bound, or ERL_NIF_DIRTY_JOB_IO_BOUND for jobs that will be - I/O-bound. The argc and argv arguments can either be the originals passed - into the calling NIF, or they can be values created by the calling NIF. The calling - NIF must use the return value of enif_schedule_dirty_nif as its own return value.

-

Be aware that enif_schedule_dirty_nif, as its name implies, only schedules the - dirty NIF for future execution. The calling NIF does not block waiting for the dirty NIF to - execute and return, which means that the calling NIF can't expect to receive the dirty NIF +

Schedule NIF fp to execute. This function allows an application to break up long-running + work into multiple regular NIF calls or to schedule a dirty NIF + to execute on a dirty scheduler thread (note that the dirty NIF functionality described here is + experimental and that you have to enable support for dirty schedulers when building OTP in + order to try the functionality out).

+

The fun_name argument provides a name for the NIF being scheduled for execution. If it cannot + be converted to an atom, enif_schedule_nif returns a badarg exception.

+

The flags argument must be set to 0 for a regular NIF, or if the emulator was built the + experimental dirty scheduler support enabled, flags can be set to either ERL_NIF_DIRTY_JOB_CPU_BOUND + if the job is expected to be primarily CPU-bound, or ERL_NIF_DIRTY_JOB_IO_BOUND for jobs that will + be I/O-bound.

+

The argc and argv arguments can either be the originals passed into the calling NIF, or + they can be values created by the calling NIF.

+

The calling NIF must use the return value of enif_schedule_nif as its own return value.

+

Be aware that enif_schedule_nif, as its name implies, only schedules the + NIF for future execution. The calling NIF does not block waiting for the scheduled NIF to + execute and return, which means that the calling NIF can't expect to receive the scheduled NIF return value and use it for further operations.

-

A dirty NIF may not invoke the enif_make_badarg - to raise an exception. If it wishes to return an exception, the dirty NIF should pass a - regular result indicating the exception details to its finalizer, and allow the finalizer - to raise the exception on its behalf.

-

This function is available only when the emulator is configured with dirty schedulers - enabled. This feature is currently disabled by default. To determine whether the dirty NIF API - is available, native code can check to see if the C preprocessor macro - ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined.

-
-
- ERL_NIF_TERMenif_schedule_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result, ERL_NIF_TERM (*fp)(ErlNifEnv* env, ERL_NIF_TERM result)) - Schedule a dirty NIF finalizer - -

When a dirty NIF finishes executing, it must schedule a finalizer function to return - its result to the original NIF caller. The dirty NIF passes result as the value it - wants the finalizer to use as the return value. The fp argument is a pointer to the - finalizer function. The NIF API provides the - enif_dirty_nif_finalizer function that can be used as a finalizer that simply - returns its result argument. You are also free to write your own custom finalizer - that uses result to derive a different return value, or ignores result - entirely and returns a completely different value.

-

Without exception, all dirty NIFs must invoke enif_schedule_dirty_nif_finalizer - to complete their execution.

-

This function is available only when the emulator is configured with dirty - schedulers enabled. This feature is currently disabled by default. To determine whether - the dirty NIF API is available, native code can check to see if the C preprocessor macro - ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined.

ErlNifPid *enif_self(ErlNifEnv* caller_env, ErlNifPid* pid) @@ -1384,4 +1368,3 @@ typedef enum {

erlang:load_nif/2

- diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 1026e5f649..8bfb7d2ad2 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3503,6 +3503,7 @@ get_map_elements_fail: * I[0]: &&call_nif * I[1]: Function pointer to NIF function * I[2]: Pointer to erl_module_nif + * I[3]: Function pointer to dirty NIF */ BifFunction vbf; @@ -3523,13 +3524,6 @@ get_map_elements_fail: reg[0] = r(0); nif_bif_result = (*fp)(&env, bif_nif_arity, reg); erts_post_nif(&env); -#ifdef ERTS_DIRTY_SCHEDULERS - if (is_non_value(nif_bif_result) && c_p->freason == TRAP) { - Export* ep = ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(c_p); - ep->code[0] = I[-3]; - ep->code[1] = I[-2]; - } -#endif } ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result)); PROCESS_MAIN_CHK_LOCKS(c_p); diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index e96177cfd9..cfc6146b0a 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -2363,7 +2363,11 @@ load_code(LoaderState* stp) if (stp->may_load_nif) { const int finfo_ix = ci - FUNC_INFO_SZ; - enum { MIN_FUNC_SZ = 3 }; +#ifdef ERTS_DIRTY_SCHEDULERS + enum { MIN_FUNC_SZ = 4 }; +#else + enum { MIN_FUNC_SZ = 3 }; +#endif if (finfo_ix - last_func_start < MIN_FUNC_SZ && last_func_start) { /* Must make room for call_nif op */ int pad = MIN_FUNC_SZ - (finfo_ix - last_func_start); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index aa15d2cc57..0db42d4325 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2018,6 +2018,20 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) roots[n].sz = 1; n++; } + + /* + * If a NIF has saved arguments, they need to be added + */ + if (ERTS_PROC_GET_NIF_TRAP_EXPORT(p)) { + Eterm* argv; + int argc; + if (erts_setup_nif_gc(p, &argv, &argc)) { + roots[n].v = argv; + roots[n].sz = argc; + n++; + } + } + ASSERT(n <= rootset->size); mp = p->msg.first; diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index ff551ea3af..1414744763 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1513,72 +1513,251 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent) return ERTS_BIF_REDS_LEFT(env->proc) == 0; } -#ifdef ERTS_DIRTY_SCHEDULERS - -/* NIFs exports need one more item than the Export struct provides, the - * erl_module_nif*, so the DirtyNifExport below adds that. The Export - * member must be first in the struct. +/* + * NIF exports need a few more items than the Export struct provides, + * including the erl_module_nif* and a NIF function pointer, so the + * NifExport below adds those. The Export member must be first in the + * struct. The saved_mfa, saved_argc, nif_level, alloced_argv_sz and argv + * members are used to track the MFA and arguments of the top NIF in case a + * chain of one or more enif_schedule_nif() calls results in an exception, + * since in that case the original MFA and registers have to be restored + * before returning to Erlang to ensure stacktrace information associated + * with the exception is correct. */ +typedef ERL_NIF_TERM (*NativeFunPtr)(ErlNifEnv*, int, const ERL_NIF_TERM[]); + typedef struct { Export exp; struct erl_module_nif* m; -} DirtyNifExport; + NativeFunPtr fp; + Eterm saved_mfa[3]; + int saved_argc; + int alloced_argv_sz; + Eterm argv[1]; +} NifExport; -static void -alloc_proc_psd(Process* proc, DirtyNifExport **ep) +/* + * If a process has saved arguments, they need to be part of the GC + * rootset. The function below is called from setup_rootset() in + * erl_gc.c. This function is declared in erl_process.h. + */ +int +erts_setup_nif_gc(Process* proc, Eterm** objv, int* nobj) +{ + NifExport* ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + int gc = (ep && ep->saved_argc > 0); + + if (gc) { + *objv = ep->argv; + *nobj = ep->saved_argc; + } + return gc; +} + +/* + * Allocate a NifExport and set it in proc specific data + */ +static NifExport* +allocate_nif_sched_data(Process* proc, int argc) { + NifExport* ep; + size_t argv_extra, total; int i; - if (!*ep) { - *ep = erts_alloc(ERTS_ALC_T_PSD, sizeof(DirtyNifExport)); - sys_memset((void*) *ep, 0, sizeof(DirtyNifExport)); - for (i=0; iexp.addressv[i] = &(*ep)->exp.code[3]; - } - (*ep)->exp.code[3] = (BeamInstr) em_call_nif; + + argv_extra = argc > 1 ? sizeof(Eterm)*(argc-1) : 0; + total = sizeof(NifExport) + argv_extra; + ep = erts_alloc(ERTS_ALC_T_PSD, total); + sys_memset((void*) ep, 0, total); + ep->alloced_argv_sz = argc; + for (i=0; iexp.addressv[i] = &ep->exp.code[3]; } - (void) ERTS_PROC_SET_DIRTY_SCHED_TRAP_EXPORT(proc, ERTS_PROC_LOCK_MAIN, &(*ep)->exp); + ep->exp.code[3] = (BeamInstr) em_call_nif; + (void) ERTS_PROC_SET_NIF_TRAP_EXPORT(proc, ERTS_PROC_LOCK_MAIN, &ep->exp); + return ep; } +/* + * Initialize a NifExport struct. Create it if needed and store it in the + * proc. The direct_fp function is what will be invoked by op_call_nif, and + * the indirect_fp function, if not NULL, is what the direct_fp function + * will call. If the allocated NifExport isn't enough to hold all of argv, + * allocate a larger one. Save MFA and registers only if the need_save + * parameter is true. + */ static ERL_NIF_TERM -execute_dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp, + int need_save, int argc, const ERL_NIF_TERM argv[]) { - Eterm* reg = ERTS_PROC_GET_SCHDATA(env->proc)->x_reg_array; - ERL_NIF_TERM result, dirty_result = (ERL_NIF_TERM) reg[0]; - typedef ERL_NIF_TERM (*FinalizerFP)(ErlNifEnv*, ERL_NIF_TERM); - FinalizerFP fp; -#if HAVE_INT64 && SIZEOF_LONG != 8 - ASSERT(sizeof(fp) <= sizeof(ErlNifUInt64)); - enif_get_uint64(env, reg[1], (ErlNifUInt64 *) &fp); -#else - ASSERT(sizeof(fp) <= sizeof(unsigned long)); - enif_get_ulong(env, reg[1], (unsigned long *) &fp); -#endif - result = (*fp)(env, dirty_result); - if (erts_refc_dectest(&env->mod_nif->rt_dtor_cnt, 0) == 0 - && env->mod_nif->mod == NULL) - close_lib(env->mod_nif); - return result; + Process* proc = env->proc; + Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; + NifExport* ep; + int i; + + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + if (!ep) + ep = allocate_nif_sched_data(proc, argc); + else if (need_save && ep->alloced_argv_sz < argc) { + NifExport* new_ep = allocate_nif_sched_data(proc, argc); + erts_free(ERTS_ALC_T_PSD, (void*) ep); + ep = new_ep; + } + ERTS_VBUMP_ALL_REDS(proc); + for (i = 0; i < argc; i++) { + if (need_save) + ep->argv[i] = reg[i]; + reg[i] = (Eterm) argv[i]; + } + if (need_save) { + ep->saved_mfa[0] = proc->current[0]; + ep->saved_mfa[1] = proc->current[1]; + ep->saved_mfa[2] = proc->current[2]; + ep->saved_argc = argc; + } + proc->i = (BeamInstr*) ep->exp.addressv[0]; + ep->exp.code[0] = (BeamInstr) proc->current[0]; + ep->exp.code[1] = (BeamInstr) proc->current[1]; + ep->exp.code[2] = argc; + ep->exp.code[4] = (BeamInstr) direct_fp; + ep->m = env->mod_nif; + ep->fp = indirect_fp; + proc->freason = TRAP; + return THE_NON_VALUE; } -#endif /* ERTS_DIRTY_SCHEDULERS */ +/* + * Restore saved MFA and registers. Registers are restored only when the + * exception flag is true. + */ +static void +restore_nif_mfa(Process* proc, NifExport* ep, int exception) +{ + int i; + Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT + proc->current[0] = ep->saved_mfa[0]; + proc->current[1] = ep->saved_mfa[1]; + proc->current[2] = ep->saved_mfa[2]; + if (exception) + for (i = 0; i < ep->saved_argc; i++) + reg[i] = ep->argv[i]; + ep->saved_argc = 0; + ep->saved_mfa[0] = THE_NON_VALUE; +} -ERL_NIF_TERM -enif_schedule_dirty_nif(ErlNifEnv* env, int flags, - ERL_NIF_TERM (*fp)(ErlNifEnv*, int, const ERL_NIF_TERM[]), - int argc, const ERL_NIF_TERM argv[]) +#ifdef ERTS_DIRTY_SCHEDULERS + +/* + * Finalize a dirty NIF call. This function is scheduled to cause the VM to + * switch the process off a dirty scheduler thread and back onto a regular + * scheduler thread, and then return the result from the dirty NIF. It also + * restores the original NIF MFA when necessary based on the value of + * ep->fp set by execute_dirty_nif via init_nif_sched_data -- non-NULL + * means restore, NULL means do not restore. + */ +static ERL_NIF_TERM +dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + Process* proc = env->proc; + NifExport* ep; + + ASSERT(argc == 1); + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data)); + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + ASSERT(ep); + if (ep->fp) + restore_nif_mfa(proc, ep, 0); + return argv[0]; +} + +/* Finalize a dirty NIF call that raised an exception. Otherwise same as + * the dirty_nif_finalizer() function. + */ +static ERL_NIF_TERM +dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + Process* proc = env->proc; + NifExport* ep; + + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data)); + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + ASSERT(ep); + if (ep->fp) + restore_nif_mfa(proc, ep, 1); + return enif_make_badarg(env); +} + +/* + * Dirty NIF execution wrapper function. Invoke an application's dirty NIF, + * then check the result and schedule the appropriate finalizer function + * where needed. Also restore the original NIF MFA when appropriate. + */ +static ERL_NIF_TERM +execute_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + Process* proc = env->proc; + NativeFunPtr fp = (NativeFunPtr) proc->current[6]; + NifExport* ep; + ERL_NIF_TERM result; + + ASSERT(ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data)); + + /* + * Set ep->fp to NULL before the native call so we know later whether it scheduled another NIF for execution + */ + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + ASSERT(ep); + ep->fp = NULL; + result = (*fp)(env, argc, argv); + erts_smp_atomic32_read_band_mb(&proc->state, + ~(ERTS_PSFLG_DIRTY_CPU_PROC + |ERTS_PSFLG_DIRTY_IO_PROC + |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q + |ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)); + if (erts_refc_dectest(&env->mod_nif->rt_dtor_cnt, 0) == 0 && env->mod_nif->mod == NULL) + close_lib(env->mod_nif); + /* + * If no more NIFs were scheduled by the native call via + * enif_schedule_nif(), then ep->fp will still be NULL as set above, in + * which case we need to restore the original NIF calling + * context. Reuse fp essentially as a boolean for this, passing it to + * init_nif_sched_data below. Both dirty_nif_exception and + * dirty_nif_finalizer then check ep->fp to decide whether or not to + * restore the original calling context. + */ + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + ASSERT(ep); + if (ep->fp) + fp = NULL; + if (is_non_value(result)) { + if (proc->freason != TRAP) { + ASSERT(proc->freason == BADARG); + return init_nif_sched_data(env, dirty_nif_exception, fp, 0, argc, argv); + } else { + if (ep->fp == NULL) + restore_nif_mfa(proc, ep, 1); + return result; + } + } + else + return init_nif_sched_data(env, dirty_nif_finalizer, fp, 0, 1, &result); +} + +/* + * Dirty NIF scheduling wrapper function. Schedule a dirty NIF to execute + * via the execute_dirty_nif() wrapper function. The dirty scheduler thread + * type (CPU or I/O) is indicated in flags parameter. + */ +static ERTS_INLINE ERL_NIF_TERM +schedule_dirty_nif(ErlNifEnv* env, int flags, int argc, const ERL_NIF_TERM argv[]) { -#ifdef USE_THREADS erts_aint32_t state, n, a; Process* proc = env->proc; - Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; - DirtyNifExport* ep = NULL; - int i; + NativeFunPtr fp = (NativeFunPtr) proc->current[6]; + NifExport* ep; + int need_save; - int chkflgs = (flags & (ERL_NIF_DIRTY_JOB_IO_BOUND|ERL_NIF_DIRTY_JOB_CPU_BOUND)); - if (chkflgs != ERL_NIF_DIRTY_JOB_IO_BOUND && chkflgs != ERL_NIF_DIRTY_JOB_CPU_BOUND) - return enif_make_badarg(env); + ASSERT(flags==ERL_NIF_DIRTY_JOB_IO_BOUND || flags==ERL_NIF_DIRTY_JOB_CPU_BOUND); a = erts_smp_atomic32_read_acqb(&proc->state); while (1) { @@ -1590,7 +1769,7 @@ enif_schedule_dirty_nif(ErlNifEnv* env, int flags, */ n &= ~(ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q); - if (chkflgs == ERL_NIF_DIRTY_JOB_CPU_BOUND) + if (flags == ERL_NIF_DIRTY_JOB_CPU_BOUND) n |= ERTS_PSFLG_DIRTY_CPU_PROC; else n |= ERTS_PSFLG_DIRTY_IO_PROC; @@ -1598,69 +1777,100 @@ enif_schedule_dirty_nif(ErlNifEnv* env, int flags, if (a == state) break; } - if (!(ep = (DirtyNifExport*) ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc))) - alloc_proc_psd(proc, &ep); - ERTS_VBUMP_ALL_REDS(proc); - ep->exp.code[2] = argc; - for (i = 0; i < argc; i++) { - reg[i] = (Eterm) argv[i]; - } - proc->i = (BeamInstr*) ep->exp.addressv[0]; - ep->exp.code[4] = (BeamInstr) fp; - ep->m = env->mod_nif; - proc->freason = TRAP; - erts_refc_inc(&env->mod_nif->rt_dtor_cnt, 1); + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + need_save = (ep == NULL || is_non_value(ep->saved_mfa[0])); + return init_nif_sched_data(env, execute_dirty_nif, fp, need_save, argc, argv); +} - return THE_NON_VALUE; -#else - return (*fp)(env, argc, argv); -#endif +static ERL_NIF_TERM +schedule_dirty_io_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return schedule_dirty_nif(env, ERL_NIF_DIRTY_JOB_IO_BOUND, argc, argv); +} + +static ERL_NIF_TERM +schedule_dirty_cpu_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return schedule_dirty_nif(env, ERL_NIF_DIRTY_JOB_CPU_BOUND, argc, argv); +} + +#endif /* ERTS_DIRTY_SCHEDULERS */ + +/* + * NIF execution wrapper used by enif_schedule_nif() for regular NIFs. It + * calls the actual NIF, restores original NIF MFA if necessary, and + * then returns the NIF result. + */ +static ERL_NIF_TERM +execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + Process* proc = env->proc; + NativeFunPtr fp = (NativeFunPtr) proc->current[6]; + NifExport* ep; + ERL_NIF_TERM result; + + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + ASSERT(ep); + ep->fp = NULL; + result = (*fp)(env, argc, argv); + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + ASSERT(ep); + /* + * If no NIFs were scheduled by the native call via + * enif_schedule_nif(), then ep->fp will still be NULL as set above, in + * which case we need to restore the original NIF MFA. + */ + if (ep->fp == NULL) + restore_nif_mfa(proc, ep, is_non_value(result) && proc->freason != TRAP); + return result; } ERL_NIF_TERM -enif_schedule_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result, - ERL_NIF_TERM (*fp)(ErlNifEnv*, ERL_NIF_TERM)) +enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, + ERL_NIF_TERM (*fp)(ErlNifEnv*, int, const ERL_NIF_TERM[]), + int argc, const ERL_NIF_TERM argv[]) { -#ifdef USE_THREADS Process* proc = env->proc; - Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; - DirtyNifExport* ep; + NifExport* ep; + ERL_NIF_TERM fun_name_atom, result; + int need_save; - erts_smp_atomic32_read_band_mb(&proc->state, - ~(ERTS_PSFLG_DIRTY_CPU_PROC - |ERTS_PSFLG_DIRTY_IO_PROC - |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q - |ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)); - if (!(ep = (DirtyNifExport*) ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc))) - alloc_proc_psd(proc, &ep); - ERTS_VBUMP_ALL_REDS(proc); - ep->exp.code[2] = 2; - reg[0] = (Eterm) result; -#if HAVE_INT64 && SIZEOF_LONG != 8 - ASSERT(sizeof(fp) <= sizeof(ErlNifUInt64)); - reg[1] = (Eterm) enif_make_uint64(env, (ErlNifUInt64) fp); -#else - ASSERT(sizeof(fp) <= sizeof(unsigned long)); - reg[1] = (Eterm) enif_make_ulong(env, (unsigned long) fp); -#endif - proc->i = (BeamInstr*) ep->exp.addressv[0]; - ep->exp.code[4] = (BeamInstr) execute_dirty_nif_finalizer; - proc->freason = TRAP; + if (argc > MAX_ARG) + return enif_make_badarg(env); + fun_name_atom = enif_make_atom(env, fun_name); + if (enif_is_exception(env, fun_name_atom)) + return fun_name_atom; - return THE_NON_VALUE; + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + need_save = (ep == NULL || is_non_value(ep->saved_mfa[0])); + + if (flags) { +#ifdef ERTS_DIRTY_SCHEDULERS + NativeFunPtr sched_fun; + int chkflgs = (flags & (ERL_NIF_DIRTY_JOB_IO_BOUND|ERL_NIF_DIRTY_JOB_CPU_BOUND)); + if (chkflgs == ERL_NIF_DIRTY_JOB_IO_BOUND) + sched_fun = schedule_dirty_io_nif; + else if (chkflgs == ERL_NIF_DIRTY_JOB_CPU_BOUND) + sched_fun = schedule_dirty_cpu_nif; + else + return enif_make_badarg(env); + result = init_nif_sched_data(env, sched_fun, fp, need_save, argc, argv); #else - return (*fp)(env, result); + return enif_make_badarg(env); #endif -} + } + else + result = init_nif_sched_data(env, execute_nif, fp, need_save, argc, argv); -/* A simple finalizer that just returns its result argument */ -ERL_NIF_TERM -enif_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result) -{ + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + ASSERT(ep); + ep->exp.code[1] = (BeamInstr) fun_name_atom; return result; } +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT + int enif_is_on_dirty_scheduler(ErlNifEnv* env) { @@ -1977,6 +2187,35 @@ static Eterm load_nif_error(Process* p, const char* atom, const char* format, .. return ret; } +/* + * The function below is for looping through ErlNifFunc arrays, helping + * provide backwards compatibility across the version 2.7 change that added + * the "flags" field to ErlNifFunc. + */ +static ErlNifFunc* next_func(ErlNifEntry* entry, int* incrp, ErlNifFunc* func) +{ + ASSERT(incrp); + if (!*incrp) { + if (entry->major > 2 || (entry->major == 2 && entry->minor >= 7)) + *incrp = sizeof(ErlNifFunc); + else { + /* + * ErlNifFuncV1 below is what ErlNifFunc was before the + * addition of the flags field for 2.7, and is needed to handle + * backward compatibility. + */ + typedef struct { + const char* name; + unsigned arity; + ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + }ErlNifFuncV1; + *incrp = sizeof(ErlNifFuncV1); + } + } + return (ErlNifFunc*) ((char*)func + *incrp); +} + + BIF_RETTYPE load_nif_2(BIF_ALIST_2) { static const char bad_lib[] = "bad_lib"; @@ -2086,22 +2325,48 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) } else { /*erts_fprintf(stderr, "Found module %T\r\n", mod_atom);*/ - + + int maybe_dirty_nifs = ((entry->major > 2 || (entry->major == 2 && entry->minor >= 7)) + && (entry->options & ERL_NIF_DIRTY_NIF_OPTION)); + int incr = 0; + ErlNifFunc* f = entry->funcs; for (i=0; i < entry->num_of_funcs && ret==am_ok; i++) { BeamInstr** code_pp; - ErlNifFunc* f = &entry->funcs[i]; if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1) || (code_pp = get_func_pp(mod->curr.code, f_atom, f->arity))==NULL) { ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u", mod_atom, f->name, f->arity); - } - else if (code_pp[1] - code_pp[0] < (5+3)) { + } + else if (maybe_dirty_nifs && f->flags) { + /* + * If the flags field is non-zero and this emulator was + * built with dirty scheduler support, check that the flags + * value is legal. But if this emulator was built without + * dirty scheduler support, treat a non-zero flags field as + * a load error. + */ +#ifdef ERTS_DIRTY_SCHEDULERS + if (f->flags != ERL_NIF_DIRTY_JOB_IO_BOUND && f->flags != ERL_NIF_DIRTY_JOB_CPU_BOUND) + ret = load_nif_error(BIF_P, bad_lib, "Illegal flags field value %d for NIF %T:%s/%u", + f->flags, mod_atom, f->name, f->arity); +#else + ret = load_nif_error(BIF_P, bad_lib, "NIF %T:%s/%u requires a runtime with dirty scheduler support.", + mod_atom, f->name, f->arity); +#endif + } +#ifdef ERTS_DIRTY_SCHEDULERS + else if (code_pp[1] - code_pp[0] < (5+4)) +#else + else if (code_pp[1] - code_pp[0] < (5+3)) +#endif + { ret = load_nif_error(BIF_P,bad_lib,"No explicit call to load_nif" - " in module (%T:%s/%u to small)", - mod_atom, entry->funcs[i].name, entry->funcs[i].arity); + " in module (%T:%s/%u too small)", + mod_atom, f->name, f->arity); } /*erts_fprintf(stderr, "Found NIF %T:%s/%u\r\n", - mod_atom, entry->funcs[i].name, entry->funcs[i].arity);*/ + mod_atom, f->name, f->arity);*/ + f = next_func(entry, &incr, f); } } @@ -2127,7 +2392,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) * is deprecated and was only ment as a development feature not to * be used in production systems. (See warning below) */ - int k; + int k, old_incr = 0; + ErlNifFunc* old_func; lib->priv_data = mod->curr.nif->priv_data; ASSERT(mod->curr.nif->entry != NULL); @@ -2136,13 +2402,16 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) goto error; } /* Check that no NIF is removed */ + old_func = mod->curr.nif->entry->funcs; for (k=0; k < mod->curr.nif->entry->num_of_funcs; k++) { - ErlNifFunc* old_func = &mod->curr.nif->entry->funcs[k]; + int incr = 0; + ErlNifFunc* f = entry->funcs; 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) { + if (old_func->arity == f->arity + && sys_strcmp(old_func->name, f->name) == 0) { break; } + f = next_func(entry, &incr, f); } if (i == entry->num_of_funcs) { ret = load_nif_error(BIF_P,reload,"Reloaded library missing " @@ -2150,7 +2419,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) old_func->name, old_func->arity); goto error; } - } + old_func = next_func(mod->curr.nif->entry, &old_incr, old_func); + } erts_pre_nif(&env, BIF_P, lib); veto = entry->reload(&env, &lib->priv_data, BIF_ARG_2); erts_post_nif(&env); @@ -2197,13 +2467,17 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) /* ** Everything ok, patch the beam code with op_call_nif */ - mod->curr.nif = lib; + + int incr = 0; + ErlNifFunc* f = entry->funcs; + + 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, ERTS_ATOM_ENC_LATIN1); - code_ptr = *get_func_pp(mod->curr.code, f_atom, entry->funcs[i].arity); - + erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1); + code_ptr = *get_func_pp(mod->curr.code, f_atom, f->arity); + if (code_ptr[1] == 0) { code_ptr[5+0] = (BeamInstr) BeamOp(op_call_nif); } @@ -2211,10 +2485,21 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) 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; + g->orig_instr = (BeamInstr) BeamOp(op_call_nif); + } + if ((entry->major > 2 || (entry->major == 2 && entry->minor >= 7)) + && (entry->options & ERL_NIF_DIRTY_NIF_OPTION) && f->flags) { +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT + code_ptr[5+3] = (BeamInstr) f->fptr; + code_ptr[5+1] = (f->flags == ERL_NIF_DIRTY_JOB_IO_BOUND) ? + (BeamInstr) schedule_dirty_io_nif : + (BeamInstr) schedule_dirty_cpu_nif; +#endif + } + else + code_ptr[5+1] = (BeamInstr) f->fptr; code_ptr[5+2] = (BeamInstr) lib; + f = next_func(entry, &incr, f); } } else { diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 5b93c2398e..226fc199a1 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -42,9 +42,13 @@ ** 2.5: R17 Maps API additions ** 2.6: R17 with maps ** R17 dirty schedulers +** 2.7: 17.3 add enif_schedule_nif +** remove enif_schedule_dirty_nif, enif_schedule_dirty_nif_finalizer, enif_dirty_nif_finalizer +** add ErlNifEntry options +** add ErlNifFunc flags */ #define ERL_NIF_MAJOR_VERSION 2 -#define ERL_NIF_MINOR_VERSION 6 +#define ERL_NIF_MINOR_VERSION 7 /* * The emulator will refuse to load a nif-lib with a major version @@ -125,8 +129,10 @@ typedef struct const char* name; unsigned arity; ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + unsigned flags; }ErlNifFunc; + typedef struct enif_entry_t { int major; @@ -139,8 +145,11 @@ typedef struct enif_entry_t int (*upgrade)(ErlNifEnv*, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info); void (*unload) (ErlNifEnv*, void* priv_data); const char* vm_variant; + unsigned options; }ErlNifEntry; +/* Field bits for ErlNifEntry options */ +#define ERL_NIF_DIRTY_NIF_OPTION 1 typedef struct @@ -232,10 +241,21 @@ extern TWinDynNifCallbacks WinDynNifCallbacks; # else # define ERL_NIF_INIT_DECL(MODNAME) __declspec(dllexport) ErlNifEntry* nif_init(TWinDynNifCallbacks* callbacks) # endif -# define ERL_NIF_INIT_BODY memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks)) +# ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT +# define ERL_NIF_INIT_BODY do { \ + memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks)); \ + entry.options = ERL_NIF_DIRTY_NIF_OPTION; \ + } while(0) +# else +# define ERL_NIF_INIT_BODY memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks)) +# endif #else # define ERL_NIF_INIT_GLOB -# define ERL_NIF_INIT_BODY +# ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT +# define ERL_NIF_INIT_BODY entry.options = ERL_NIF_DIRTY_NIF_OPTION +# else +# define ERL_NIF_INIT_BODY +# endif # ifdef STATIC_ERLANG_NIF # define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _nif_init(void) # else diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index d7c554e60b..be39816a64 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -141,10 +141,8 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_number,(ErlNifEnv*, ERL_NIF_TERM term)); ERL_NIF_API_FUNC_DECL(void*,enif_dlopen,(const char* lib, void (*err_handler)(void*,const char*), void* err_arg)); ERL_NIF_API_FUNC_DECL(void*,enif_dlsym,(void* handle, const char* symbol, void (*err_handler)(void*,const char*), void* err_arg)); ERL_NIF_API_FUNC_DECL(int,enif_consume_timeslice,(ErlNifEnv*, int percent)); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[])); #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_dirty_nif,(ErlNifEnv*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[])); -ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_dirty_nif_finalizer,(ErlNifEnv*,ERL_NIF_TERM,ERL_NIF_TERM (*)(ErlNifEnv*,ERL_NIF_TERM))); -ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_dirty_nif_finalizer,(ErlNifEnv*,ERL_NIF_TERM)); ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); #endif @@ -289,10 +287,8 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_dlopen ERL_NIF_API_FUNC_MACRO(enif_dlopen) # define enif_dlsym ERL_NIF_API_FUNC_MACRO(enif_dlsym) # define enif_consume_timeslice ERL_NIF_API_FUNC_MACRO(enif_consume_timeslice) +# define enif_schedule_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_nif) #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -# define enif_schedule_dirty_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_dirty_nif) -# define enif_schedule_dirty_nif_finalizer ERL_NIF_API_FUNC_MACRO(enif_schedule_dirty_nif_finalizer) -# define enif_dirty_nif_finalizer ERL_NIF_API_FUNC_MACRO(enif_dirty_nif_finalizer) # define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler) # define enif_have_dirty_schedulers ERL_NIF_API_FUNC_MACRO(enif_have_dirty_schedulers) #endif diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 1606ad119d..e96d5c4ccd 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -590,12 +590,10 @@ erts_pre_init_process(void) erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks = ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS; -#ifdef ERTS_DIRTY_SCHEDULERS - erts_psd_required_locks[ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT].get_locks - = ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT_GET_LOCKS; - erts_psd_required_locks[ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT].set_locks - = ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT_SET_LOCKS; -#endif + erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].get_locks + = ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS; + erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].set_locks + = ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS; /* Check that we have locks for all entries */ for (ix = 0; ix < ERTS_PSD_SIZE; ix++) { @@ -3758,17 +3756,25 @@ evacuate_run_queue(ErtsRunQueue *rq, } #ifdef ERTS_DIRTY_SCHEDULERS else if (state & ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q) { - erts_aint32_t old; - old = erts_smp_atomic32_read_band_nob(&proc->state, - ~(ERTS_PSFLG_DIRTY_CPU_PROC - | ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q)); +#ifdef DEBUG + erts_aint32_t old = +#else + (void) +#endif + erts_smp_atomic32_read_band_nob(&proc->state, + ~(ERTS_PSFLG_DIRTY_CPU_PROC + | ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q)); /* assert that no other dirty flags are set */ ASSERT(!(old & (ERTS_PSFLG_DIRTY_IO_PROC|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q))); } else if (state & ERTS_PSFLG_DIRTY_IO_PROC_IN_Q) { - erts_aint32_t old; - old = erts_smp_atomic32_read_band_nob(&proc->state, - ~(ERTS_PSFLG_DIRTY_IO_PROC - | ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)); +#ifdef DEBUG + erts_aint32_t old = +#else + (void) +#endif + erts_smp_atomic32_read_band_nob(&proc->state, + ~(ERTS_PSFLG_DIRTY_IO_PROC + | ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)); /* assert that no other dirty flags are set */ ASSERT(!(old & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q))); } diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index ed6dadbffa..31f4a09c94 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -734,13 +734,9 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi) #define ERTS_PSD_DIST_ENTRY 3 #define ERTS_PSD_CALL_TIME_BP 4 #define ERTS_PSD_DELAYED_GC_TASK_QS 5 -#ifdef ERTS_DIRTY_SCHEDULERS -#define ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT 6 +#define ERTS_PSD_NIF_TRAP_EXPORT 6 #define ERTS_PSD_SIZE 7 -#else -#define ERTS_PSD_SIZE 6 -#endif typedef struct { void *data[ERTS_PSD_SIZE]; @@ -767,10 +763,8 @@ typedef struct { #define ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS ERTS_PROC_LOCK_MAIN #define ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS ERTS_PROC_LOCK_MAIN -#ifdef ERTS_DIRTY_SCHEDULERS -#define ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT_GET_LOCKS ERTS_PROC_LOCK_MAIN -#define ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT_SET_LOCKS ERTS_PROC_LOCK_MAIN -#endif +#define ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS ERTS_PROC_LOCK_MAIN +#define ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS ERTS_PROC_LOCK_MAIN typedef struct { ErtsProcLocks get_locks; @@ -1367,6 +1361,8 @@ Uint64 erts_get_proc_interval(void); Uint64 erts_ensure_later_proc_interval(Uint64); Uint64 erts_step_proc_interval(void); +int erts_setup_nif_gc(Process* proc, Eterm** objv, int* nobj); /* see erl_nif.c */ + ErtsProcList *erts_proclist_create(Process *); void erts_proclist_destroy(ErtsProcList *); @@ -1817,12 +1813,10 @@ erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *data) #define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, L, PBT) \ ((ErtsProcSysTaskQs *) erts_psd_set((P), (L), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT))) -#ifdef ERTS_DIRTY_SCHEDULERS -#define ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(P) \ - ((Export *) erts_psd_get((P), ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT)) -#define ERTS_PROC_SET_DIRTY_SCHED_TRAP_EXPORT(P, L, DSTE) \ - ((Export *) erts_psd_set((P), (L), ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT, (void *) (DSTE))) -#endif +#define ERTS_PROC_GET_NIF_TRAP_EXPORT(P) \ + ((Export *) erts_psd_get((P), ERTS_PSD_NIF_TRAP_EXPORT)) +#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, L, DSTE) \ + ((Export *) erts_psd_set((P), (L), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (DSTE))) ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p); diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index b2da6f58af..01ee71c4fa 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -37,7 +37,8 @@ threading/1, send/1, send2/1, send3/1, send_threaded/1, neg/1, is_checks/1, get_length/1, make_atom/1, make_string/1, reverse_list_test/1, - otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1 + otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1, + dirty_nif_exception/1, nif_schedule/1 ]). -export([many_args_100/100]). @@ -64,7 +65,8 @@ all() -> resource_takeover, threading, send, send2, send3, send_threaded, neg, is_checks, get_length, make_atom, make_string,reverse_list_test, - otp_9668, consume_timeslice, dirty_nif, dirty_nif_send + otp_9668, consume_timeslice, + nif_schedule, dirty_nif, dirty_nif_send, dirty_nif_exception ]. groups() -> @@ -1524,6 +1526,20 @@ consume_timeslice(Config) when is_list(Config) -> ok. +nif_schedule(Config) when is_list(Config) -> + ensure_lib_loaded(Config), + A = "this is a string", + B = {this,is,a,tuple}, + {B,A} = call_nif_schedule(A, B), + ok = try call_nif_schedule(1, 2) + catch + error:badarg -> + [{?MODULE,call_nif_schedule,[1,2],_}|_] = + erlang:get_stacktrace(), + ok + end, + ok. + dirty_nif(Config) when is_list(Config) -> try erlang:system_info(dirty_cpu_schedulers) of N when is_integer(N) -> @@ -1556,6 +1572,24 @@ dirty_nif_send(Config) when is_list(Config) -> {skipped,"No dirty scheduler support"} end. +dirty_nif_exception(Config) when is_list(Config) -> + try erlang:system_info(dirty_cpu_schedulers) of + N when is_integer(N) -> + ensure_lib_loaded(Config), + try + call_dirty_nif_exception(), + ?t:fail(expected_badarg) + catch + error:badarg -> + [{?MODULE,call_dirty_nif_exception,[],_}|_] = + erlang:get_stacktrace(), + ok + end + catch + error:badarg -> + {skipped,"No dirty scheduler support"} + end. + next_msg(_Pid) -> receive M -> M @@ -1685,8 +1719,10 @@ echo_int(_) -> ?nif_stub. type_sizes() -> ?nif_stub. otp_9668_nif(_) -> ?nif_stub. consume_timeslice_nif(_,_) -> ?nif_stub. +call_nif_schedule(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. send_from_dirty_nif(_) -> ?nif_stub. +call_dirty_nif_exception() -> ?nif_stub. %% maps is_map_nif(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 955dc64189..ad9d5d9254 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1493,6 +1493,31 @@ static ERL_NIF_TERM consume_timeslice_nif(ErlNifEnv* env, int argc, const ERL_NI } } +static ERL_NIF_TERM nif_sched2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + char s[64]; + if (!enif_get_string(env, argv[2], s, sizeof s, ERL_NIF_LATIN1)) + return enif_make_badarg(env); + return enif_make_tuple2(env, argv[3], argv[2]); +} + +static ERL_NIF_TERM nif_sched1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM new_argv[4]; + new_argv[0] = enif_make_atom(env, "garbage0"); + new_argv[1] = enif_make_atom(env, "garbage1"); + new_argv[2] = argv[0]; + new_argv[3] = argv[1]; + return enif_schedule_nif(env, "nif_sched2", 0, nif_sched2, 4, new_argv); +} + +static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + if (argc != 2) + return enif_make_atom(env, "false"); + return enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv); +} + #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { @@ -1507,11 +1532,10 @@ static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ enif_get_int(env, argv[0], &n); enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1); enif_inspect_binary(env, argv[2], &b); - result = enif_make_tuple3(env, - enif_make_int(env, n), - enif_make_string(env, s, ERL_NIF_LATIN1), - enif_make_binary(env, &b)); - return enif_schedule_dirty_nif_finalizer(env, result, enif_dirty_nif_finalizer); + return enif_make_tuple3(env, + enif_make_int(env, n), + enif_make_string(env, s, ERL_NIF_LATIN1), + enif_make_binary(env, &b)); } static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) @@ -1526,7 +1550,7 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM if (enif_get_int(env, argv[0], &n) && enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1) && enif_inspect_binary(env, argv[2], &b)) - return enif_schedule_dirty_nif(env, ERL_NIF_DIRTY_JOB_CPU_BOUND, dirty_nif, argc, argv); + return enif_schedule_nif(env, "call_dirty_nif", ERL_NIF_DIRTY_JOB_CPU_BOUND, dirty_nif, argc, argv); else return enif_make_badarg(env); } else { @@ -1534,35 +1558,42 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM } } -static ERL_NIF_TERM dirty_sender(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM result; ErlNifPid pid; ErlNifEnv* menv; int res; - enif_get_local_pid(env, argv[0], &pid); + if (!enif_get_local_pid(env, argv[0], &pid)) + return enif_make_badarg(env); result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid)); menv = enif_alloc_env(); res = enif_send(env, &pid, menv, result); enif_free_env(menv); if (!res) - /* Note the next line will crash, since dirty nifs can't return exceptions. - * This is intentional, since enif_send should not fail if the test succeeds. - */ - return enif_schedule_dirty_nif_finalizer(env, enif_make_badarg(env), enif_dirty_nif_finalizer); + return enif_make_badarg(env); else - return enif_schedule_dirty_nif_finalizer(env, result, enif_dirty_nif_finalizer); + return result; } -static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM result; - ErlNifPid pid; - - if (!enif_get_local_pid(env, argv[0], &pid)) + switch (argc) { + case 0: { + ERL_NIF_TERM args[255]; + int i; + for (i = 0; i < 255; i++) + args[i] = enif_make_int(env, i); + return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, + call_dirty_nif_exception, 255, argv); + } + case 1: return enif_make_badarg(env); - return enif_schedule_dirty_nif(env, ERL_NIF_DIRTY_JOB_CPU_BOUND, dirty_sender, argc, argv); + default: + return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, + call_dirty_nif_exception, argc-1, argv); + } } #endif @@ -1742,9 +1773,11 @@ static ErlNifFunc nif_funcs[] = {"type_sizes", 0, type_sizes}, {"otp_9668_nif", 1, otp_9668_nif}, {"consume_timeslice_nif", 2, consume_timeslice_nif}, + {"call_nif_schedule", 2, call_nif_schedule}, #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT {"call_dirty_nif", 3, call_dirty_nif}, - {"send_from_dirty_nif", 1, send_from_dirty_nif}, + {"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"call_dirty_nif_exception", 0, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, #endif {"is_map_nif", 1, is_map_nif}, {"get_map_size_nif", 1, get_map_size_nif}, -- cgit v1.2.3 From 88b094b6439737b61c117cd6873beea4518757a8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 1 Jul 2014 22:04:55 +0200 Subject: erts: Implement yielding for distributed send of large messages Use same mechanism as term_to_binary to yield while encoding large messages for distributed send. --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/bif.c | 204 ++++++++++++---- erts/emulator/beam/dist.c | 475 +++++++++++++++++++++--------------- erts/emulator/beam/dist.h | 97 +++++++- erts/emulator/beam/external.c | 97 +++----- erts/emulator/beam/external.h | 11 +- erts/emulator/hipe/hipe_bif_list.m4 | 5 +- 7 files changed, 580 insertions(+), 310 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 5d06a32941..ae2c6a3c67 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -198,6 +198,7 @@ atom dotall atom driver atom driver_options atom dsend +atom dsend_continue_trap atom dunlink atom duplicate_bag atom dupnames diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index fcbeb6cf5c..99566443aa 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The 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 @@ static Export* set_cpu_topology_trap = NULL; static Export* await_proc_exit_trap = NULL; static Export* await_port_send_result_trap = NULL; Export* erts_format_cpu_topology_trap = NULL; - +static Export dsend_continue_trap_export; static Export *await_sched_wall_time_mod_trap; static erts_smp_atomic32_t sched_wall_time; @@ -1777,6 +1777,8 @@ BIF_RETTYPE whereis_1(BIF_ALIST_1) * erlang:'!'/2 */ +HIPE_WRAPPER_BIF_DISABLE_GC(ebif_bang, 2) + BIF_RETTYPE ebif_bang_2(BIF_ALIST_2) { @@ -1795,34 +1797,36 @@ ebif_bang_2(BIF_ALIST_2) #define SEND_USER_ERROR (-5) #define SEND_INTERNAL_ERROR (-6) #define SEND_AWAIT_RESULT (-7) +#define SEND_YIELD_CONTINUE (-8) + -Sint do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp); +Sint do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext*); static Sint remote_send(Process *p, DistEntry *dep, - Eterm to, Eterm full_to, Eterm msg, int suspend) + Eterm to, Eterm full_to, Eterm msg, + ErtsSendContext* ctx) { Sint res; int code; - ErtsDSigData dsd; ASSERT(is_atom(to) || is_external_pid(to)); - code = erts_dsig_prepare(&dsd, dep, p, ERTS_DSP_NO_LOCK, !suspend); + code = erts_dsig_prepare(&ctx->dsd, dep, p, ERTS_DSP_NO_LOCK, !ctx->suspend); switch (code) { case ERTS_DSIG_PREP_NOT_ALIVE: case ERTS_DSIG_PREP_NOT_CONNECTED: res = SEND_TRAP; break; case ERTS_DSIG_PREP_WOULD_SUSPEND: - ASSERT(!suspend); + ASSERT(!ctx->suspend); res = SEND_YIELD; break; case ERTS_DSIG_PREP_CONNECTED: { if (is_atom(to)) - code = erts_dsig_send_reg_msg(&dsd, to, msg); + code = erts_dsig_send_reg_msg(to, msg, ctx); else - code = erts_dsig_send_msg(&dsd, to, msg); + code = erts_dsig_send_msg(to, msg, ctx); /* * Note that reductions have been bumped on calling * process by erts_dsig_send_reg_msg() or @@ -1830,6 +1834,8 @@ static Sint remote_send(Process *p, DistEntry *dep, */ if (code == ERTS_DSIG_SEND_YIELD) res = SEND_YIELD_RETURN; + else if (code == ERTS_DSIG_SEND_CONTINUE) + res = SEND_YIELD_CONTINUE; else res = 0; break; @@ -1850,7 +1856,8 @@ static Sint remote_send(Process *p, DistEntry *dep, } Sint -do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { +do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) +{ Eterm portid; Port *pt; Process* rp; @@ -1881,7 +1888,7 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { erts_send_error_to_logger(p->group_leader, dsbufp); return 0; } - return remote_send(p, dep, to, to, msg, suspend); + return remote_send(p, dep, to, to, msg, ctx); } else if (is_atom(to)) { Eterm id = erts_whereis_name_to_id(p, to); @@ -1936,7 +1943,7 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { ret_val = 0; if (pt) { - int ps_flags = suspend ? 0 : ERTS_PORT_SIG_FLG_NOSUSPEND; + int ps_flags = ctx->suspend ? 0 : ERTS_PORT_SIG_FLG_NOSUSPEND; *refp = NIL; switch (erts_port_command(p, ps_flags, pt, msg, refp)) { @@ -1945,12 +1952,12 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { return SEND_USER_ERROR; case ERTS_PORT_OP_BUSY: /* Nothing has been sent */ - if (suspend) + if (ctx->suspend) erts_suspend(p, ERTS_PROC_LOCK_MAIN, pt); return SEND_YIELD; case ERTS_PORT_OP_BUSY_SCHEDULED: /* Message was sent */ - if (suspend) { + if (ctx->suspend) { erts_suspend(p, ERTS_PROC_LOCK_MAIN, pt); ret_val = SEND_YIELD_RETURN; break; @@ -2030,9 +2037,14 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { return 0; } - ret = remote_send(p, dep, tp[1], to, msg, suspend); - if (dep) - erts_deref_dist_entry(dep); + ret = remote_send(p, dep, tp[1], to, msg, ctx); + if (ret != SEND_YIELD_CONTINUE) { + if (dep) { + erts_deref_dist_entry(dep); + } + } else { + ctx->dep_to_deref = dep; + } return ret; } else { if (IS_TRACED(p)) /* XXX Is this really neccessary ??? */ @@ -2063,9 +2075,11 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) { } } +HIPE_WRAPPER_BIF_DISABLE_GC(send, 3) BIF_RETTYPE send_3(BIF_ALIST_3) { + BIF_RETTYPE retval; Eterm ref; Process *p = BIF_P; Eterm to = BIF_ARG_1; @@ -2073,34 +2087,44 @@ BIF_RETTYPE send_3(BIF_ALIST_3) Eterm opts = BIF_ARG_3; int connect = !0; - int suspend = !0; Eterm l = opts; Sint result; - + DeclareTypedTmpHeap(ErtsSendContext, ctx, BIF_P); + UseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), BIF_P); + + ctx->suspend = !0; + ctx->dep_to_deref = NULL; + ctx->return_term = am_ok; + ctx->dss.reds = (Sint) (ERTS_BIF_REDS_LEFT(p) * TERM_TO_BINARY_LOOP_FACTOR); + ctx->dss.phase = ERTS_DSIG_SEND_PHASE_INIT; + while (is_list(l)) { if (CAR(list_val(l)) == am_noconnect) { connect = 0; } else if (CAR(list_val(l)) == am_nosuspend) { - suspend = 0; + ctx->suspend = 0; } else { - BIF_ERROR(p, BADARG); + ERTS_BIF_PREP_ERROR(retval, p, BADARG); + goto done; } l = CDR(list_val(l)); } if(!is_nil(l)) { - BIF_ERROR(p, BADARG); + ERTS_BIF_PREP_ERROR(retval, p, BADARG); + goto done; } #ifdef DEBUG ref = NIL; #endif - result = do_send(p, to, msg, suspend, &ref); + result = do_send(p, to, msg, &ref, ctx); if (result > 0) { ERTS_VBUMP_REDS(p, result); if (ERTS_IS_PROC_OUT_OF_REDS(p)) goto yield_return; - BIF_RET(am_ok); + ERTS_BIF_PREP_RET(retval, am_ok); + goto done; } switch (result) { @@ -2108,68 +2132,127 @@ BIF_RETTYPE send_3(BIF_ALIST_3) /* May need to yield even though we do not bump reds here... */ if (ERTS_IS_PROC_OUT_OF_REDS(p)) goto yield_return; - BIF_RET(am_ok); + ERTS_BIF_PREP_RET(retval, am_ok); break; case SEND_TRAP: if (connect) { - BIF_TRAP3(dsend3_trap, p, to, msg, opts); + ERTS_BIF_PREP_TRAP3(retval, dsend3_trap, p, to, msg, opts); } else { - BIF_RET(am_noconnect); + ERTS_BIF_PREP_RET(retval, am_noconnect); } break; case SEND_YIELD: - if (suspend) { - ERTS_BIF_YIELD3(bif_export[BIF_send_3], p, to, msg, opts); + if (ctx->suspend) { + ERTS_BIF_PREP_YIELD3(retval, + bif_export[BIF_send_3], p, to, msg, opts); } else { - BIF_RET(am_nosuspend); + ERTS_BIF_PREP_RET(retval, am_nosuspend); } break; case SEND_YIELD_RETURN: - if (!suspend) - BIF_RET(am_nosuspend); + if (!ctx->suspend) { + ERTS_BIF_PREP_RET(retval, am_nosuspend); + break; + } yield_return: - ERTS_BIF_YIELD_RETURN(p, am_ok); + ERTS_BIF_PREP_YIELD_RETURN(retval, p, am_ok); + break; case SEND_AWAIT_RESULT: ASSERT(is_internal_ref(ref)); - BIF_TRAP3(await_port_send_result_trap, p, ref, am_nosuspend, am_ok); + ERTS_BIF_PREP_TRAP3(retval, await_port_send_result_trap, p, ref, am_nosuspend, am_ok); + break; case SEND_BADARG: - BIF_ERROR(p, BADARG); + ERTS_BIF_PREP_ERROR(retval, p, BADARG); break; case SEND_USER_ERROR: - BIF_ERROR(p, EXC_ERROR); + ERTS_BIF_PREP_ERROR(retval, p, EXC_ERROR); break; case SEND_INTERNAL_ERROR: - BIF_ERROR(p, EXC_INTERNAL_ERROR); + ERTS_BIF_PREP_ERROR(retval, p, EXC_INTERNAL_ERROR); + break; + case SEND_YIELD_CONTINUE: + BUMP_ALL_REDS(p); + erts_set_gc_state(p, 0); + ERTS_BIF_PREP_TRAP1(retval, &dsend_continue_trap_export, p, + erts_dsend_export_trap_context(p, ctx)); break; default: - ASSERT(! "Illegal send result"); + erl_exit(ERTS_ABORT_EXIT, "send_3 invalid result %d\n", (int)result); break; } - ASSERT(! "Can not arrive here"); - BIF_ERROR(p, BADARG); + +done: + UnUseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), BIF_P); + return retval; } +HIPE_WRAPPER_BIF_DISABLE_GC(send, 2) + BIF_RETTYPE send_2(BIF_ALIST_2) { return erl_send(BIF_P, BIF_ARG_1, BIF_ARG_2); } +static BIF_RETTYPE dsend_continue_trap_1(BIF_ALIST_1) +{ + Binary* bin = ((ProcBin*) binary_val(BIF_ARG_1))->val; + ErtsSendContext* ctx = (ErtsSendContext*) ERTS_MAGIC_BIN_DATA(bin); + Sint initial_reds = (Sint) (ERTS_BIF_REDS_LEFT(BIF_P) * TERM_TO_BINARY_LOOP_FACTOR); + int result; + + ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == erts_dsend_context_dtor); + + ctx->dss.reds = initial_reds; + result = erts_dsig_send(&ctx->dsd, &ctx->dss); + + switch (result) { + case ERTS_DSIG_SEND_OK: + erts_set_gc_state(BIF_P, 1); + BIF_RET(ctx->return_term); + break; + case ERTS_DSIG_SEND_YIELD: /*SEND_YIELD_RETURN*/ + erts_set_gc_state(BIF_P, 1); + if (!ctx->suspend) + BIF_RET(am_nosuspend); + ERTS_BIF_YIELD_RETURN(BIF_P, ctx->return_term); + + case ERTS_DSIG_SEND_CONTINUE: { /*SEND_YIELD_CONTINUE*/ + BUMP_ALL_REDS(BIF_P); + BIF_TRAP1(&dsend_continue_trap_export, BIF_P, BIF_ARG_1); + } + default: + erl_exit(ERTS_ABORT_EXIT, "dsend_continue_trap invalid result %d\n", (int)result); + break; + } + ASSERT(! "Can not arrive here"); + BIF_ERROR(BIF_P, BADARG); +} + Eterm erl_send(Process *p, Eterm to, Eterm msg) { + Eterm retval; Eterm ref; Sint result; + DeclareTypedTmpHeap(ErtsSendContext, ctx, p); + UseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), p); #ifdef DEBUG ref = NIL; #endif + ctx->suspend = !0; + ctx->dep_to_deref = NULL; + ctx->return_term = msg; + ctx->dss.reds = (Sint) (ERTS_BIF_REDS_LEFT(p) * TERM_TO_BINARY_LOOP_FACTOR); + ctx->dss.phase = ERTS_DSIG_SEND_PHASE_INIT; - result = do_send(p, to, msg, !0, &ref); + result = do_send(p, to, msg, &ref, ctx); if (result > 0) { ERTS_VBUMP_REDS(p, result); if (ERTS_IS_PROC_OUT_OF_REDS(p)) goto yield_return; - BIF_RET(msg); + ERTS_BIF_PREP_RET(retval, msg); + goto done; } switch (result) { @@ -2177,35 +2260,46 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg) /* May need to yield even though we do not bump reds here... */ if (ERTS_IS_PROC_OUT_OF_REDS(p)) goto yield_return; - BIF_RET(msg); + ERTS_BIF_PREP_RET(retval, msg); break; case SEND_TRAP: - BIF_TRAP2(dsend2_trap, p, to, msg); + ERTS_BIF_PREP_TRAP2(retval, dsend2_trap, p, to, msg); break; case SEND_YIELD: - ERTS_BIF_YIELD2(bif_export[BIF_send_2], p, to, msg); + ERTS_BIF_PREP_YIELD2(retval, bif_export[BIF_send_2], p, to, msg); break; case SEND_YIELD_RETURN: yield_return: - ERTS_BIF_YIELD_RETURN(p, msg); + ERTS_BIF_PREP_YIELD_RETURN(retval, p, msg); + break; case SEND_AWAIT_RESULT: ASSERT(is_internal_ref(ref)); - BIF_TRAP3(await_port_send_result_trap, p, ref, msg, msg); + ERTS_BIF_PREP_TRAP3(retval, + await_port_send_result_trap, p, ref, msg, msg); + break; case SEND_BADARG: - BIF_ERROR(p, BADARG); + ERTS_BIF_PREP_ERROR(retval, p, BADARG); break; case SEND_USER_ERROR: - BIF_ERROR(p, EXC_ERROR); + ERTS_BIF_PREP_ERROR(retval, p, EXC_ERROR); break; case SEND_INTERNAL_ERROR: - BIF_ERROR(p, EXC_INTERNAL_ERROR); + ERTS_BIF_PREP_ERROR(retval, p, EXC_INTERNAL_ERROR); + break; + case SEND_YIELD_CONTINUE: + BUMP_ALL_REDS(p); + erts_set_gc_state(p, 0); + ERTS_BIF_PREP_TRAP1(retval, &dsend_continue_trap_export, p, + erts_dsend_export_trap_context(p, ctx)); break; default: - ASSERT(! "Illegal send result"); + erl_exit(ERTS_ABORT_EXIT, "invalid send result %d\n", (int)result); break; } - ASSERT(! "Can not arrive here"); - BIF_ERROR(p, BADARG); + +done: + UnUseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), p); + return retval; } /**********************************************************************/ @@ -4809,6 +4903,10 @@ void erts_init_bif(void) #endif , &bif_return_trap); + erts_init_trap_export(&dsend_continue_trap_export, + am_erts_internal, am_dsend_continue_trap, 1, + dsend_continue_trap_1); + flush_monitor_message_trap = erts_export_put(am_erlang, am_flush_monitor_message, 2); diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index ec07ddcd9c..f73374ba71 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The 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 @@ Export* dmonitor_p_trap = NULL; /* forward declarations */ static void clear_dist_entry(DistEntry*); -static int dsig_send(ErtsDSigData *, Eterm, Eterm, int); +static int dsig_send_ctl(ErtsDSigData* dsdp, Eterm ctl, int force_busy); static void send_nodes_mon_msgs(Process *, Eterm, Eterm, Eterm, Eterm); static void init_nodes_monitors(void); @@ -709,6 +709,55 @@ static void clear_dist_entry(DistEntry *dep) } } +void erts_dsend_context_dtor(Binary* ctx_bin) +{ + ErtsSendContext* ctx = ERTS_MAGIC_BIN_DATA(ctx_bin); + switch (ctx->dss.phase) { + case ERTS_DSIG_SEND_PHASE_MSG_SIZE: + DESTROY_SAVED_ESTACK(&ctx->dss.u.sc.estack); + break; + case ERTS_DSIG_SEND_PHASE_MSG_ENCODE: + DESTROY_SAVED_WSTACK(&ctx->dss.u.ec.wstack); + break; + default:; + } + if (ctx->dss.phase >= ERTS_DSIG_SEND_PHASE_ALLOC && ctx->dss.obuf) { + free_dist_obuf(ctx->dss.obuf); + } + if (ctx->dep_to_deref) + erts_deref_dist_entry(ctx->dep_to_deref); +} + +Eterm erts_dsend_export_trap_context(Process* p, ErtsSendContext* ctx) +{ + struct exported_ctx { + ErtsSendContext ctx; + ErtsAtomCacheMap acm; + }; + Binary* ctx_bin = erts_create_magic_binary(sizeof(struct exported_ctx), + erts_dsend_context_dtor); + struct exported_ctx* dst = ERTS_MAGIC_BIN_DATA(ctx_bin); + Uint ctl_size = !HALFWORD_HEAP ? 0 : (arityval(ctx->ctl_heap[0]) + 1); + Eterm* hp = HAlloc(p, ctl_size + PROC_BIN_SIZE); + + sys_memcpy(&dst->ctx, ctx, sizeof(ErtsSendContext)); + ASSERT(ctx->dss.ctl == make_tuple(ctx->ctl_heap)); +#if !HALFWORD_HEAP + dst->ctx.dss.ctl = make_tuple(dst->ctx.ctl_heap); +#else + /* Must put control tuple in low mem */ + sys_memcpy(hp, ctx->ctl_heap, ctl_size*sizeof(Eterm)); + dst->ctx.dss.ctl = make_tuple(hp); + hp += ctl_size; +#endif + if (ctx->dss.acmp) { + sys_memcpy(&dst->acm, ctx->dss.acmp, sizeof(ErtsAtomCacheMap)); + dst->ctx.dss.acmp = &dst->acm; + } + return erts_mk_magic_binary_term(&hp, &MSO(p), ctx_bin); +} + + /* * The erts_dsig_send_*() functions implemented below, sends asynchronous * distributed signals to other Erlang nodes. Before sending a distributed @@ -731,7 +780,7 @@ erts_dsig_send_link(ErtsDSigData *dsdp, Eterm local, Eterm remote) int res; UseTmpHeapNoproc(4); - res = dsig_send(dsdp, ctl, THE_NON_VALUE, 0); + res = dsig_send_ctl(dsdp, ctl, 0); UnUseTmpHeapNoproc(4); return res; } @@ -744,7 +793,7 @@ erts_dsig_send_unlink(ErtsDSigData *dsdp, Eterm local, Eterm remote) int res; UseTmpHeapNoproc(4); - res = dsig_send(dsdp, ctl, THE_NON_VALUE, 0); + res = dsig_send_ctl(dsdp, ctl, 0); UnUseTmpHeapNoproc(4); return res; } @@ -772,7 +821,7 @@ erts_dsig_send_m_exit(ErtsDSigData *dsdp, Eterm watcher, Eterm watched, erts_smp_de_links_unlock(dsdp->dep); #endif - res = dsig_send(dsdp, ctl, THE_NON_VALUE, 1); + res = dsig_send_ctl(dsdp, ctl, 1); UnUseTmpHeapNoproc(6); return res; } @@ -793,7 +842,7 @@ erts_dsig_send_monitor(ErtsDSigData *dsdp, Eterm watcher, Eterm watched, make_small(DOP_MONITOR_P), watcher, watched, ref); - res = dsig_send(dsdp, ctl, THE_NON_VALUE, 0); + res = dsig_send_ctl(dsdp, ctl, 0); UnUseTmpHeapNoproc(5); return res; } @@ -815,18 +864,17 @@ erts_dsig_send_demonitor(ErtsDSigData *dsdp, Eterm watcher, make_small(DOP_DEMONITOR_P), watcher, watched, ref); - res = dsig_send(dsdp, ctl, THE_NON_VALUE, force); + res = dsig_send_ctl(dsdp, ctl, force); UnUseTmpHeapNoproc(5); return res; } int -erts_dsig_send_msg(ErtsDSigData *dsdp, Eterm remote, Eterm message) +erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx) { Eterm ctl; - DeclareTmpHeapNoproc(ctl_heap,5); Eterm token = NIL; - Process *sender = dsdp->proc; + Process *sender = ctx->dsd.proc; int res; #ifdef USE_VM_PROBES Sint tok_label = 0; @@ -838,8 +886,7 @@ erts_dsig_send_msg(ErtsDSigData *dsdp, Eterm remote, Eterm message) DTRACE_CHARBUF(receiver_name, 64); #endif - UseTmpHeapNoproc(5); - if (SEQ_TRACE_TOKEN(sender) != NIL + if (SEQ_TRACE_TOKEN(sender) != NIL #ifdef USE_VM_PROBES && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag #endif @@ -852,7 +899,7 @@ erts_dsig_send_msg(ErtsDSigData *dsdp, Eterm remote, Eterm message) *node_name = *sender_name = *receiver_name = '\0'; if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) { erts_snprintf(node_name, sizeof(DTRACE_CHARBUF_NAME(node_name)), - "%T", dsdp->dep->sysname); + "%T", ctx->dsd.dep->sysname); erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), "%T", sender->common.id); erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), @@ -867,26 +914,28 @@ erts_dsig_send_msg(ErtsDSigData *dsdp, Eterm remote, Eterm message) #endif if (token != NIL) - ctl = TUPLE4(&ctl_heap[0], + ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_SEND_TT), am_Cookie, remote, token); else - ctl = TUPLE3(&ctl_heap[0], make_small(DOP_SEND), am_Cookie, remote); + ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_SEND), am_Cookie, remote); DTRACE6(message_send, sender_name, receiver_name, msize, tok_label, tok_lastcnt, tok_serial); DTRACE7(message_send_remote, sender_name, node_name, receiver_name, msize, tok_label, tok_lastcnt, tok_serial); - res = dsig_send(dsdp, ctl, message, 0); - UnUseTmpHeapNoproc(5); + ctx->dss.ctl = ctl; + ctx->dss.msg = message; + ctx->dss.force_busy = 0; + res = erts_dsig_send(&ctx->dsd, &ctx->dss); return res; } int -erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message) +erts_dsig_send_reg_msg(Eterm remote_name, Eterm message, + ErtsSendContext* ctx) { Eterm ctl; - DeclareTmpHeapNoproc(ctl_heap,6); Eterm token = NIL; - Process *sender = dsdp->proc; + Process *sender = ctx->dsd.proc; int res; #ifdef USE_VM_PROBES Sint tok_label = 0; @@ -898,7 +947,6 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message) DTRACE_CHARBUF(receiver_name, 128); #endif - UseTmpHeapNoproc(6); if (SEQ_TRACE_TOKEN(sender) != NIL #ifdef USE_VM_PROBES && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag @@ -912,7 +960,7 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message) *node_name = *sender_name = *receiver_name = '\0'; if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) { erts_snprintf(node_name, sizeof(DTRACE_CHARBUF_NAME(node_name)), - "%T", dsdp->dep->sysname); + "%T", ctx->dsd.dep->sysname); erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), "%T", sender->common.id); erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), @@ -927,17 +975,19 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message) #endif if (token != NIL) - ctl = TUPLE5(&ctl_heap[0], make_small(DOP_REG_SEND_TT), + ctl = TUPLE5(&ctx->ctl_heap[0], make_small(DOP_REG_SEND_TT), sender->common.id, am_Cookie, remote_name, token); else - ctl = TUPLE4(&ctl_heap[0], make_small(DOP_REG_SEND), + ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_REG_SEND), sender->common.id, am_Cookie, remote_name); DTRACE6(message_send, sender_name, receiver_name, msize, tok_label, tok_lastcnt, tok_serial); DTRACE7(message_send_remote, sender_name, node_name, receiver_name, msize, tok_label, tok_lastcnt, tok_serial); - res = dsig_send(dsdp, ctl, message, 0); - UnUseTmpHeapNoproc(6); + ctx->dss.ctl = ctl; + ctx->dss.msg = message; + ctx->dss.force_busy = 0; + res = erts_dsig_send(&ctx->dsd, &ctx->dss); return res; } @@ -994,7 +1044,7 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote, DTRACE7(process_exit_signal_remote, sender_name, node_name, remote_name, reason_str, tok_label, tok_lastcnt, tok_serial); /* forced, i.e ignore busy */ - res = dsig_send(dsdp, ctl, THE_NON_VALUE, 1); + res = dsig_send_ctl(dsdp, ctl, 1); UnUseTmpHeapNoproc(6); return res; } @@ -1010,7 +1060,7 @@ erts_dsig_send_exit(ErtsDSigData *dsdp, Eterm local, Eterm remote, Eterm reason) ctl = TUPLE4(&ctl_heap[0], make_small(DOP_EXIT), local, remote, reason); /* forced, i.e ignore busy */ - res = dsig_send(dsdp, ctl, THE_NON_VALUE, 1); + res = dsig_send_ctl(dsdp, ctl, 1); UnUseTmpHeapNoproc(5); return res; } @@ -1026,7 +1076,7 @@ erts_dsig_send_exit2(ErtsDSigData *dsdp, Eterm local, Eterm remote, Eterm reason ctl = TUPLE4(&ctl_heap[0], make_small(DOP_EXIT2), local, remote, reason); - res = dsig_send(dsdp, ctl, THE_NON_VALUE, 0); + res = dsig_send_ctl(dsdp, ctl, 0); UnUseTmpHeapNoproc(5); return res; } @@ -1043,7 +1093,7 @@ erts_dsig_send_group_leader(ErtsDSigData *dsdp, Eterm leader, Eterm remote) ctl = TUPLE3(&ctl_heap[0], make_small(DOP_GROUP_LEADER), leader, remote); - res = dsig_send(dsdp, ctl, THE_NON_VALUE, 0); + res = dsig_send_ctl(dsdp, ctl, 0); UnUseTmpHeapNoproc(4); return res; } @@ -1693,194 +1743,235 @@ int erts_net_message(Port *prt, return -1; } -static int -dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy) +static int dsig_send_ctl(ErtsDSigData* dsdp, Eterm ctl, int force_busy) { + struct erts_dsig_send_context ctx; + int ret; + ctx.ctl = ctl; + ctx.msg = THE_NON_VALUE; + ctx.force_busy = force_busy; + ctx.phase = ERTS_DSIG_SEND_PHASE_INIT; +#ifdef DEBUG + ctx.reds = 1; /* provoke assert below (no reduction count without msg) */ +#endif + ret = erts_dsig_send(dsdp, &ctx); + ASSERT(ret != ERTS_DSIG_SEND_CONTINUE); + return ret; +} + +int +erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx) +{ + int retval; + Sint initial_reds = ctx->reds; Eterm cid; - int suspended = 0; - int resume = 0; - Uint32 pass_through_size; - Uint data_size, dhdr_ext_size; - ErtsAtomCacheMap *acmp; - ErtsDistOutputBuf *obuf; - DistEntry *dep = dsdp->dep; - Uint32 flags = dep->flags; - Process *c_p = dsdp->proc; - if (!c_p || dsdp->no_suspend) - force_busy = 1; + while (1) { + switch (ctx->phase) { + case ERTS_DSIG_SEND_PHASE_INIT: + ctx->flags = dsdp->dep->flags; + ctx->c_p = dsdp->proc; - ERTS_SMP_LC_ASSERT(!c_p - || (ERTS_PROC_LOCK_MAIN - == erts_proc_lc_my_proc_locks(c_p))); + if (!ctx->c_p || dsdp->no_suspend) + ctx->force_busy = 1; - if (!erts_is_alive) - return ERTS_DSIG_SEND_OK; + ERTS_SMP_LC_ASSERT(!ctx->c_p + || (ERTS_PROC_LOCK_MAIN + == erts_proc_lc_my_proc_locks(ctx->c_p))); - if (flags & DFLAG_DIST_HDR_ATOM_CACHE) { - acmp = erts_get_atom_cache_map(c_p); - pass_through_size = 0; - } - else { - acmp = NULL; - pass_through_size = 1; - } + if (!erts_is_alive) + return ERTS_DSIG_SEND_OK; -#ifdef ERTS_DIST_MSG_DBG - erts_fprintf(stderr, ">>%s CTL: %T\n", pass_through_size ? "P" : " ", ctl); - if (is_value(msg)) - erts_fprintf(stderr, " MSG: %T\n", msg); -#endif + if (ctx->flags & DFLAG_DIST_HDR_ATOM_CACHE) { + ctx->acmp = erts_get_atom_cache_map(ctx->c_p); + ctx->pass_through_size = 0; + } + else { + ctx->acmp = NULL; + ctx->pass_through_size = 1; + } - data_size = pass_through_size; - erts_reset_atom_cache_map(acmp); - data_size += erts_encode_dist_ext_size(ctl, flags, acmp); - if (is_value(msg)) - data_size += erts_encode_dist_ext_size(msg, flags, acmp); - erts_finalize_atom_cache_map(acmp, flags); + #ifdef ERTS_DIST_MSG_DBG + erts_fprintf(stderr, ">>%s CTL: %T\n", ctx->pass_through_size ? "P" : " ", ctx->ctl); + if (is_value(msg)) + erts_fprintf(stderr, " MSG: %T\n", msg); + #endif + + ctx->data_size = ctx->pass_through_size; + erts_reset_atom_cache_map(ctx->acmp); + erts_encode_dist_ext_size(ctx->ctl, ctx->flags, ctx->acmp, &ctx->data_size); + + if (is_value(ctx->msg)) { + ctx->u.sc.estack.start = NULL; + ctx->u.sc.flags = ctx->flags; + ctx->u.sc.level = 0; + ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_SIZE; + } else { + ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC; + } + break; - dhdr_ext_size = erts_encode_ext_dist_header_size(acmp); - data_size += dhdr_ext_size; + case ERTS_DSIG_SEND_PHASE_MSG_SIZE: + if (erts_encode_dist_ext_size_int(ctx->msg, ctx, &ctx->data_size)) { + retval = ERTS_DSIG_SEND_CONTINUE; + goto done; + } - obuf = alloc_dist_obuf(data_size); - obuf->ext_endp = &obuf->data[0] + pass_through_size + dhdr_ext_size; + ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC; + case ERTS_DSIG_SEND_PHASE_ALLOC: + erts_finalize_atom_cache_map(ctx->acmp, ctx->flags); + + ctx->dhdr_ext_size = erts_encode_ext_dist_header_size(ctx->acmp); + ctx->data_size += ctx->dhdr_ext_size; + + ctx->obuf = alloc_dist_obuf(ctx->data_size); + ctx->obuf->ext_endp = &ctx->obuf->data[0] + ctx->pass_through_size + ctx->dhdr_ext_size; + + /* Encode internal version of dist header */ + ctx->obuf->extp = erts_encode_ext_dist_header_setup(ctx->obuf->ext_endp, ctx->acmp); + /* Encode control message */ + erts_encode_dist_ext(ctx->ctl, &ctx->obuf->ext_endp, ctx->flags, ctx->acmp, NULL, NULL); + if (is_value(ctx->msg)) { + ctx->u.ec.flags = ctx->flags; + ctx->u.ec.level = 0; + ctx->u.ec.wstack.wstart = NULL; + ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_ENCODE; + } else { + ctx->phase = ERTS_DSIG_SEND_PHASE_FIN; + } + break; - /* Encode internal version of dist header */ - obuf->extp = erts_encode_ext_dist_header_setup(obuf->ext_endp, acmp); - /* Encode control message */ - erts_encode_dist_ext(ctl, &obuf->ext_endp, flags, acmp); - if (is_value(msg)) { - /* Encode message */ - erts_encode_dist_ext(msg, &obuf->ext_endp, flags, acmp); - } + case ERTS_DSIG_SEND_PHASE_MSG_ENCODE: + if (erts_encode_dist_ext(ctx->msg, &ctx->obuf->ext_endp, ctx->flags, ctx->acmp, &ctx->u.ec, &ctx->reds)) { + retval = ERTS_DSIG_SEND_CONTINUE; + goto done; + } - ASSERT(obuf->extp < obuf->ext_endp); - ASSERT(&obuf->data[0] <= obuf->extp - pass_through_size); - ASSERT(obuf->ext_endp <= &obuf->data[0] + data_size); + ctx->phase = ERTS_DSIG_SEND_PHASE_FIN; + case ERTS_DSIG_SEND_PHASE_FIN: { + DistEntry *dep = dsdp->dep; + int suspended = 0; + int resume = 0; - data_size = obuf->ext_endp - obuf->extp; + ASSERT(ctx->obuf->extp < ctx->obuf->ext_endp); + ASSERT(&ctx->obuf->data[0] <= ctx->obuf->extp - ctx->pass_through_size); + ASSERT(ctx->obuf->ext_endp <= &ctx->obuf->data[0] + ctx->data_size); - /* - * Signal encoded; now verify that the connection still exists, - * and if so enqueue the signal and schedule it for send. - */ - obuf->next = NULL; - erts_smp_de_rlock(dep); - cid = dep->cid; - if (cid != dsdp->cid - || dep->connection_id != dsdp->connection_id - || dep->status & ERTS_DE_SFLG_EXITING) { - /* Not the same connection as when we started; drop message... */ - erts_smp_de_runlock(dep); - free_dist_obuf(obuf); - } - else { - ErtsProcList *plp = NULL; - erts_smp_mtx_lock(&dep->qlock); - dep->qsize += size_obuf(obuf); - if (dep->qsize >= erts_dist_buf_busy_limit) - dep->qflgs |= ERTS_DE_QFLG_BUSY; - if (!force_busy && (dep->qflgs & ERTS_DE_QFLG_BUSY)) { - erts_smp_mtx_unlock(&dep->qlock); + ctx->data_size = ctx->obuf->ext_endp - ctx->obuf->extp; - plp = erts_proclist_create(c_p); - erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL); - suspended = 1; - erts_smp_mtx_lock(&dep->qlock); - } + /* + * Signal encoded; now verify that the connection still exists, + * and if so enqueue the signal and schedule it for send. + */ + ctx->obuf->next = NULL; + erts_smp_de_rlock(dep); + cid = dep->cid; + if (cid != dsdp->cid + || dep->connection_id != dsdp->connection_id + || dep->status & ERTS_DE_SFLG_EXITING) { + /* Not the same connection as when we started; drop message... */ + erts_smp_de_runlock(dep); + free_dist_obuf(ctx->obuf); + } + else { + ErtsProcList *plp = NULL; + erts_smp_mtx_lock(&dep->qlock); + dep->qsize += size_obuf(ctx->obuf); + if (dep->qsize >= erts_dist_buf_busy_limit) + dep->qflgs |= ERTS_DE_QFLG_BUSY; + if (!ctx->force_busy && (dep->qflgs & ERTS_DE_QFLG_BUSY)) { + erts_smp_mtx_unlock(&dep->qlock); + + plp = erts_proclist_create(ctx->c_p); + erts_suspend(ctx->c_p, ERTS_PROC_LOCK_MAIN, NULL); + suspended = 1; + erts_smp_mtx_lock(&dep->qlock); + } - /* Enqueue obuf on dist entry */ - if (dep->out_queue.last) - dep->out_queue.last->next = obuf; - else - dep->out_queue.first = obuf; - dep->out_queue.last = obuf; + /* Enqueue obuf on dist entry */ + if (dep->out_queue.last) + dep->out_queue.last->next = ctx->obuf; + else + dep->out_queue.first = ctx->obuf; + dep->out_queue.last = ctx->obuf; + + if (!ctx->force_busy) { + if (!(dep->qflgs & ERTS_DE_QFLG_BUSY)) { + if (suspended) + resume = 1; /* was busy when we started, but isn't now */ + #ifdef USE_VM_PROBES + if (resume && DTRACE_ENABLED(dist_port_not_busy)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), + "%T", cid); + erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)), + "%T", dep->sysname); + DTRACE3(dist_port_not_busy, erts_this_node_sysname, + port_str, remote_str); + } + #endif + } + else { + /* Enqueue suspended process on dist entry */ + ASSERT(plp); + erts_proclist_store_last(&dep->suspended, plp); + } + } - if (!force_busy) { - if (!(dep->qflgs & ERTS_DE_QFLG_BUSY)) { - if (suspended) - resume = 1; /* was busy when we started, but isn't now */ -#ifdef USE_VM_PROBES - if (resume && DTRACE_ENABLED(dist_port_not_busy)) { - DTRACE_CHARBUF(port_str, 64); - DTRACE_CHARBUF(remote_str, 64); - - erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), - "%T", cid); - erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)), - "%T", dep->sysname); - DTRACE3(dist_port_not_busy, erts_this_node_sysname, - port_str, remote_str); - } -#endif + erts_smp_mtx_unlock(&dep->qlock); + erts_schedule_dist_command(NULL, dep); + erts_smp_de_runlock(dep); + + if (resume) { + erts_resume(ctx->c_p, ERTS_PROC_LOCK_MAIN); + erts_proclist_destroy(plp); + /* + * Note that the calling process still have to yield as if it + * suspended. If not, the calling process could later be + * erroneously scheduled when it shouldn't be. + */ + } } - else { - /* Enqueue suspended process on dist entry */ - ASSERT(plp); - erts_proclist_store_last(&dep->suspended, plp); + ctx->obuf = NULL; + + if (suspended) { + #ifdef USE_VM_PROBES + if (!resume && DTRACE_ENABLED(dist_port_busy)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + DTRACE_CHARBUF(pid_str, 16); + + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", cid); + erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)), + "%T", dep->sysname); + erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), + "%T", ctx->c_p->common.id); + DTRACE4(dist_port_busy, erts_this_node_sysname, + port_str, remote_str, pid_str); + } + #endif + if (!resume && erts_system_monitor_flags.busy_dist_port) + monitor_generic(ctx->c_p, am_busy_dist_port, cid); + retval = ERTS_DSIG_SEND_YIELD; + } else { + retval = ERTS_DSIG_SEND_OK; } + goto done; } - - erts_smp_mtx_unlock(&dep->qlock); - erts_schedule_dist_command(NULL, dep); - erts_smp_de_runlock(dep); - - if (resume) { - erts_resume(c_p, ERTS_PROC_LOCK_MAIN); - erts_proclist_destroy(plp); - /* - * Note that the calling process still have to yield as if it - * suspended. If not, the calling process could later be - * erroneously scheduled when it shouldn't be. - */ + default: + erl_exit(ERTS_ABORT_EXIT, "dsig_send invalid phase (%d)\n", (int)ctx->phase); } } - if (c_p) { - int reds; - /* - * Bump reductions on calling process. - * - * This is the reduction cost: Always a base cost of 8 reductions - * plus 16 reductions per kilobyte generated external data. - */ - - data_size >>= (10-4); -#if defined(ARCH_64) && !HALFWORD_HEAP - data_size &= 0x003fffffffffffff; -#elif defined(ARCH_32) || HALFWORD_HEAP - data_size &= 0x003fffff; -#else -# error "Ohh come on ... !?!" -#endif - reds = 8 + ((int) data_size > 1000000 ? 1000000 : (int) data_size); - BUMP_REDS(c_p, reds); - } - - if (suspended) { -#ifdef USE_VM_PROBES - if (!resume && DTRACE_ENABLED(dist_port_busy)) { - DTRACE_CHARBUF(port_str, 64); - DTRACE_CHARBUF(remote_str, 64); - DTRACE_CHARBUF(pid_str, 16); - - erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", cid); - erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)), - "%T", dep->sysname); - erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), - "%T", c_p->common.id); - DTRACE4(dist_port_busy, erts_this_node_sysname, - port_str, remote_str, pid_str); - } -#endif - if (!resume && erts_system_monitor_flags.busy_dist_port) - monitor_generic(c_p, am_busy_dist_port, cid); - return ERTS_DSIG_SEND_YIELD; +done: + if (ctx->msg && ctx->c_p) { + BUMP_REDS(ctx->c_p, (initial_reds - ctx->reds) / TERM_TO_BINARY_LOOP_FACTOR); } - return ERTS_DSIG_SEND_OK; + return retval; } - static Uint dist_port_command(Port *prt, ErtsDistOutputBuf *obuf) { diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index f32b999198..2a2ba0c83f 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -22,6 +22,7 @@ #include "erl_process.h" #include "erl_node_tables.h" +#include "zlib.h" #define DFLAG_PUBLISHED 0x01 #define DFLAG_ATOM_CACHE 0x02 @@ -264,17 +265,105 @@ erts_destroy_dist_link(ErtsDistLinkData *dldp) #endif + + +/* Define for testing */ +/* #define EXTREME_TTB_TRAPPING 1 */ + +#ifndef EXTREME_TTB_TRAPPING +#define TERM_TO_BINARY_LOOP_FACTOR 32 +#else +#define TERM_TO_BINARY_LOOP_FACTOR 1 +#endif + +typedef enum { TTBSize, TTBEncode, TTBCompress } TTBState; +typedef struct TTBSizeContext_ { + Uint flags; + int level; + Uint result; + Eterm obj; + ErtsEStack estack; +} TTBSizeContext; + +typedef struct TTBEncodeContext_ { + Uint flags; + int level; + byte* ep; + Eterm obj; + ErtsWStack wstack; + Binary *result_bin; +} TTBEncodeContext; + +typedef struct { + Uint real_size; + Uint dest_len; + byte *dbytes; + Binary *result_bin; + Binary *destination_bin; + z_stream stream; +} TTBCompressContext; + +typedef struct { + int alive; + TTBState state; + union { + TTBSizeContext sc; + TTBEncodeContext ec; + TTBCompressContext cc; + } s; +} TTBContext; + +enum erts_dsig_send_phase { + ERTS_DSIG_SEND_PHASE_INIT, + ERTS_DSIG_SEND_PHASE_MSG_SIZE, + ERTS_DSIG_SEND_PHASE_ALLOC, + ERTS_DSIG_SEND_PHASE_MSG_ENCODE, + ERTS_DSIG_SEND_PHASE_FIN +}; + +struct erts_dsig_send_context { + enum erts_dsig_send_phase phase; + Sint reds; + + Eterm ctl; + Eterm msg; + int force_busy; + Uint32 pass_through_size; + Uint data_size, dhdr_ext_size; + ErtsAtomCacheMap *acmp; + ErtsDistOutputBuf *obuf; + Uint32 flags; + Process *c_p; + union { + TTBSizeContext sc; + TTBEncodeContext ec; + }u; +}; + +typedef struct { + int suspend; + + Eterm ctl_heap[6]; + ErtsDSigData dsd; + DistEntry* dep_to_deref; + struct erts_dsig_send_context dss; + + Eterm return_term; +}ErtsSendContext; + + /* * erts_dsig_send_* return values. */ #define ERTS_DSIG_SEND_OK 0 #define ERTS_DSIG_SEND_YIELD 1 +#define ERTS_DSIG_SEND_CONTINUE 2 extern int erts_dsig_send_link(ErtsDSigData *, Eterm, Eterm); -extern int erts_dsig_send_msg(ErtsDSigData *, Eterm, Eterm); +extern int erts_dsig_send_msg(Eterm, Eterm, ErtsSendContext*); extern int erts_dsig_send_exit_tt(ErtsDSigData *, Eterm, Eterm, Eterm, Eterm); extern int erts_dsig_send_unlink(ErtsDSigData *, Eterm, Eterm); -extern int erts_dsig_send_reg_msg(ErtsDSigData *, Eterm, Eterm); +extern int erts_dsig_send_reg_msg(Eterm, Eterm, ErtsSendContext*); extern int erts_dsig_send_group_leader(ErtsDSigData *, Eterm, Eterm); extern int erts_dsig_send_exit(ErtsDSigData *, Eterm, Eterm, Eterm); extern int erts_dsig_send_exit2(ErtsDSigData *, Eterm, Eterm, Eterm); @@ -282,6 +371,10 @@ extern int erts_dsig_send_demonitor(ErtsDSigData *, Eterm, Eterm, Eterm, int); extern int erts_dsig_send_monitor(ErtsDSigData *, Eterm, Eterm, Eterm); extern int erts_dsig_send_m_exit(ErtsDSigData *, Eterm, Eterm, Eterm, Eterm); +extern int erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx); +extern void erts_dsend_context_dtor(Binary*); +extern Eterm erts_dsend_export_trap_context(Process* p, ErtsSendContext* ctx); + extern int erts_dist_command(Port *prt, int reds); extern void erts_dist_port_not_busy(Port *prt); extern void erts_kill_dist_connection(DistEntry *dep, Uint32); diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 8d240355b0..564657a13b 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -498,15 +498,37 @@ byte *erts_encode_ext_dist_header_finalize(byte *ext, ErtsAtomCache *cache, Uint return ep; } -Uint erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp) +int erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp, + Uint* szp) { - Uint sz = 0; + Uint sz; + if (encode_size_struct_int(NULL, acmp, term, flags, NULL, &sz)) { + return -1; + } else { #ifndef ERTS_DEBUG_USE_DIST_SEP - if (!(flags & DFLAG_DIST_HDR_ATOM_CACHE)) + if (!(flags & DFLAG_DIST_HDR_ATOM_CACHE)) #endif - sz++ /* VERSION_MAGIC */; - sz += encode_size_struct2(acmp, term, flags); - return sz; + sz++ /* VERSION_MAGIC */; + + *szp += sz; + return 0; + } +} + +int erts_encode_dist_ext_size_int(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp) +{ + Uint sz; + if (encode_size_struct_int(&ctx->u.sc, ctx->acmp, term, ctx->flags, &ctx->reds, &sz)) { + return -1; + } else { +#ifndef ERTS_DEBUG_USE_DIST_SEP + if (!(ctx->flags & DFLAG_DIST_HDR_ATOM_CACHE)) +#endif + sz++ /* VERSION_MAGIC */; + + *szp += sz; + return 0; + } } Uint erts_encode_ext_size(Eterm term) @@ -527,19 +549,16 @@ Uint erts_encode_ext_size_ets(Eterm term) } -void erts_encode_dist_ext(Eterm term, byte **ext, Uint32 flags, ErtsAtomCacheMap *acmp) +int erts_encode_dist_ext(Eterm term, byte **ext, Uint32 flags, ErtsAtomCacheMap *acmp, + TTBEncodeContext* ctx, Sint* reds) { - byte *ep = *ext; -#ifndef ERTS_DEBUG_USE_DIST_SEP - if (!(flags & DFLAG_DIST_HDR_ATOM_CACHE)) -#endif - *ep++ = VERSION_MAGIC; - ep = enc_term(acmp, term, ep, flags, NULL); - if (!ep) - erl_exit(ERTS_ABORT_EXIT, - "%s:%d:erts_encode_dist_ext(): Internal data structure error\n", - __FILE__, __LINE__); - *ext = ep; + if (!ctx || !ctx->wstack.wstart) { + #ifndef ERTS_DEBUG_USE_DIST_SEP + if (!(flags & DFLAG_DIST_HDR_ATOM_CACHE)) + #endif + *(*ext)++ = VERSION_MAGIC; + } + return enc_term_int(ctx, acmp, term, *ext, flags, NULL, reds, ext); } void erts_encode_ext(Eterm term, byte **ext) @@ -1740,55 +1759,15 @@ erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags) { return erts_term_to_binary_simple(p, Term, size, level, flags); } -/* Define for testing */ -/* #define EXTREME_TTB_TRAPPING 1 */ +/* Define EXTREME_TTB_TRAPPING for testing in dist.h */ #ifndef EXTREME_TTB_TRAPPING -#define TERM_TO_BINARY_LOOP_FACTOR 32 #define TERM_TO_BINARY_COMPRESS_CHUNK (1 << 18) #else -#define TERM_TO_BINARY_LOOP_FACTOR 1 #define TERM_TO_BINARY_COMPRESS_CHUNK 10 #endif -typedef enum { TTBSize, TTBEncode, TTBCompress } TTBState; -typedef struct TTBSizeContext_ { - Uint flags; - int level; - Uint result; - Eterm obj; - ErtsEStack estack; -} TTBSizeContext; - -typedef struct TTBEncodeContext_ { - Uint flags; - int level; - byte* ep; - Eterm obj; - ErtsWStack wstack; - Binary *result_bin; -} TTBEncodeContext; - -typedef struct { - Uint real_size; - Uint dest_len; - byte *dbytes; - Binary *result_bin; - Binary *destination_bin; - z_stream stream; -} TTBCompressContext; - -typedef struct { - int alive; - TTBState state; - union { - TTBSizeContext sc; - TTBEncodeContext ec; - TTBCompressContext cc; - } s; -} TTBContext; - static void ttb_context_destructor(Binary *context_bin) { TTBContext *context = ERTS_MAGIC_BIN_DATA(context_bin); diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index bf00958eb1..f120e96e3b 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The 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,6 +150,7 @@ typedef struct { Uint extsize; } ErtsBinary2TermState; + /* -------------------------------------------------------------------------- */ void erts_init_atom_cache_map(ErtsAtomCacheMap *); @@ -161,8 +162,12 @@ Uint erts_encode_ext_dist_header_size(ErtsAtomCacheMap *); Uint erts_encode_ext_dist_header_size(ErtsAtomCacheMap *); byte *erts_encode_ext_dist_header_setup(byte *, ErtsAtomCacheMap *); byte *erts_encode_ext_dist_header_finalize(byte *, ErtsAtomCache *, Uint32); -Uint erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap *); -void erts_encode_dist_ext(Eterm, byte **, Uint32, ErtsAtomCacheMap *); +struct erts_dsig_send_context; +int erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap*, Uint* szp); +int erts_encode_dist_ext_size_int(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp); +struct TTBEncodeContext_; +int erts_encode_dist_ext(Eterm, byte **, Uint32, ErtsAtomCacheMap *, + struct TTBEncodeContext_ *, Sint* reds); Uint erts_encode_ext_size(Eterm); Uint erts_encode_ext_size_2(Eterm, unsigned); diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 5f92b6bac4..96a849621f 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -277,7 +277,10 @@ ifelse($1,list_to_binary_1,hipe_wrapper_list_to_binary_1, ifelse($1,iolist_to_binary_1,hipe_wrapper_iolist_to_binary_1, ifelse($1,binary_list_to_bin_1,hipe_wrapper_binary_list_to_bin_1, ifelse($1,list_to_bitstring_1,hipe_wrapper_list_to_bitstring_1, -$1)))))))))))') +ifelse($1,send_2,hipe_wrapper_send_2, +ifelse($1,send_3,hipe_wrapper_send_3, +ifelse($1,ebif_bang_2,hipe_wrapper_ebif_bang_2, +$1))))))))))))))') define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))') include(TARGET/`erl_bif_list.h') -- cgit v1.2.3 From ac2eba5ea214152d806a73a8c992911cdf3aae1d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 18 Aug 2014 17:48:42 +0200 Subject: erts: Yield in term_to_binary while copying large binaries Applies also to distributed send. --- erts/emulator/beam/external.c | 45 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 564657a13b..c6e6a8a954 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2304,8 +2304,8 @@ dec_pid(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, Ete #define ENC_TERM ((Eterm) 0) #define ENC_ONE_CONS ((Eterm) 1) #define ENC_PATCH_FUN_SIZE ((Eterm) 2) -#define ENC_LAST_ARRAY_ELEMENT ((Eterm) 3) - +#define ENC_BIN_COPY ((Eterm) 3) +#define ENC_LAST_ARRAY_ELEMENT ((Eterm) 4) static byte* enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags, @@ -2341,6 +2341,9 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, WSTACK_RESTORE(s, &ctx->wstack); ep = ctx->ep; obj = ctx->obj; + if (is_non_value(obj)) { + goto outer_loop; + } } } @@ -2378,6 +2381,26 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, put_int32(ep - size_p, size_p); } goto outer_loop; + case ENC_BIN_COPY: { + Uint bits = (Uint)obj; + Uint bitoffs = WSTACK_POP(s); + byte* bytes = (byte*) WSTACK_POP(s); + byte* dst = (byte*) WSTACK_POP(s); + if (bits > r * (B2T_MEMCPY_FACTOR * 8)) { + Uint n = r * B2T_MEMCPY_FACTOR; + WSTACK_PUSH3(s, (UWord)(dst + n), (UWord)(bytes + n), bitoffs); + WSTACK_PUSH2(s, ENC_BIN_COPY, bits - 8*n); + bits = 8*n; + copy_binary_to_buffer(dst, 0, bytes, bitoffs, bits); + obj = THE_NON_VALUE; + r = 0; /* yield */ + break; + } else { + copy_binary_to_buffer(dst, 0, bytes, bitoffs, bits); + r -= bits / (B2T_MEMCPY_FACTOR * 8); + goto outer_loop; + } + } case ENC_LAST_ARRAY_ELEMENT: /* obj is the tuple */ { @@ -2405,8 +2428,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, L_jump_start: - if (ctx && --r == 0) { - *reds = r; + if (ctx && --r <= 0) { + *reds = 0; ctx->obj = obj; ctx->ep = ep; WSTACK_SAVE(s, &ctx->wstack); @@ -2621,6 +2644,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint bitoffs; Uint bitsize; byte* bytes; + byte* data_dst; ERTS_GET_BINARY_BYTES(obj, bytes, bitoffs, bitsize); if (dflags & DFLAG_INTERNAL_TAGS) { @@ -2666,7 +2690,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, j = binary_size(obj); put_int32(j, ep); ep += 4; - copy_binary_to_buffer(ep, 0, bytes, bitoffs, 8*j); + data_dst = ep; ep += j; } else if (dflags & DFLAG_BIT_BINARIES) { /* Bit-level binary. */ @@ -2676,7 +2700,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, ep += 4; *ep++ = bitsize; ep[j] = 0; /* Zero unused bits at end of binary */ - copy_binary_to_buffer(ep, 0, bytes, bitoffs, 8*j+bitsize); + data_dst = ep; ep += j + 1; } else { /* @@ -2690,11 +2714,18 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, put_int32((j+1), ep); ep += 4; ep[j] = 0; /* Zero unused bits at end of binary */ - copy_binary_to_buffer(ep, 0, bytes, bitoffs, 8*j+bitsize); + data_dst = ep; ep += j+1; *ep++ = SMALL_INTEGER_EXT; *ep++ = bitsize; } + if (ctx && j > r * B2T_MEMCPY_FACTOR) { + WSTACK_PUSH3(s, (UWord)data_dst, (UWord)bytes, bitoffs); + WSTACK_PUSH2(s, ENC_BIN_COPY, 8*j + bitsize); + } else { + copy_binary_to_buffer(data_dst, 0, bytes, bitoffs, + 8 * j + bitsize); + } } break; case EXPORT_DEF: -- cgit v1.2.3 From fb25e93d290138251d0305bd2698ad29575c661c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 18 Aug 2014 20:17:07 +0200 Subject: erts: Remove unnecessary goto for fun encoding --- erts/emulator/beam/external.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index c6e6a8a954..914214c5b5 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2754,10 +2754,9 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, case FUN_DEF: { ErlFunThing* funp = (ErlFunThing *) fun_val(obj); + int ei; if ((dflags & DFLAG_NEW_FUN_TAGS) != 0) { - int ei; - *ep++ = NEW_FUN_EXT; WSTACK_PUSH(s, ENC_PATCH_FUN_SIZE); WSTACK_PUSH(s, (UWord) ep); /* Position for patching in size */ @@ -2774,16 +2773,6 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, ep = enc_term(acmp, make_small(funp->fe->old_index), ep, dflags, off_heap); ep = enc_term(acmp, make_small(funp->fe->old_uniq), ep, dflags, off_heap); ep = enc_pid(acmp, funp->creator, ep, dflags); - - fun_env: - for (ei = funp->num_free-1; ei > 0; ei--) { - WSTACK_PUSH(s, ENC_TERM); - WSTACK_PUSH(s, (UWord) funp->env[ei]); - } - if (funp->num_free != 0) { - obj = funp->env[0]; - goto L_jump_start; - } } else { /* * Communicating with an obsolete erl_interface or @@ -2815,7 +2804,14 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, *ep++ = SMALL_TUPLE_EXT; put_int8(funp->num_free, ep); ep += 1; - goto fun_env; + } + for (ei = funp->num_free-1; ei > 0; ei--) { + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) funp->env[ei]); + } + if (funp->num_free != 0) { + obj = funp->env[0]; + goto L_jump_start; } } break; -- cgit v1.2.3 From c1676f09ea1176116b503de88604816a2da36ac5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 19 Aug 2014 16:40:52 +0200 Subject: erts: Yield in term_to_binary when encoding big maps --- erts/emulator/beam/external.c | 38 +++++++++++++++++++++----------------- erts/emulator/beam/global.h | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 914214c5b5..fb2fe146f3 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2305,7 +2305,8 @@ dec_pid(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, Ete #define ENC_ONE_CONS ((Eterm) 1) #define ENC_PATCH_FUN_SIZE ((Eterm) 2) #define ENC_BIN_COPY ((Eterm) 3) -#define ENC_LAST_ARRAY_ELEMENT ((Eterm) 4) +#define ENC_MAP_PAIR ((Eterm) 4) +#define ENC_LAST_ARRAY_ELEMENT ((Eterm) 5) static byte* enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags, @@ -2388,8 +2389,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, byte* dst = (byte*) WSTACK_POP(s); if (bits > r * (B2T_MEMCPY_FACTOR * 8)) { Uint n = r * B2T_MEMCPY_FACTOR; - WSTACK_PUSH3(s, (UWord)(dst + n), (UWord)(bytes + n), bitoffs); - WSTACK_PUSH2(s, ENC_BIN_COPY, bits - 8*n); + WSTACK_PUSH5(s, (UWord)(dst + n), (UWord)(bytes + n), bitoffs, + ENC_BIN_COPY, bits - 8*n); bits = 8*n; copy_binary_to_buffer(dst, 0, bytes, bitoffs, bits); obj = THE_NON_VALUE; @@ -2401,6 +2402,19 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, goto outer_loop; } } + case ENC_MAP_PAIR: { + Uint pairs_left = obj; + Eterm *vptr = (Eterm*) WSTACK_POP(s); + Eterm *kptr = (Eterm*) WSTACK_POP(s); + + obj = *kptr; + if (--pairs_left > 0) { + WSTACK_PUSH4(s, (UWord)(kptr+1), (UWord)(vptr+1), + ENC_MAP_PAIR, pairs_left); + } + WSTACK_PUSH2(s, ENC_TERM, *vptr); + break; + } case ENC_LAST_ARRAY_ELEMENT: /* obj is the tuple */ { @@ -2595,18 +2609,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Eterm *kptr = map_get_keys(mp); Eterm *vptr = map_get_values(mp); - for (i = size-1; i >= 1; i--) { - WSTACK_PUSH(s, ENC_TERM); - WSTACK_PUSH(s, (UWord) vptr[i]); - WSTACK_PUSH(s, ENC_TERM); - WSTACK_PUSH(s, (UWord) kptr[i]); - } - - WSTACK_PUSH(s, ENC_TERM); - WSTACK_PUSH(s, (UWord) vptr[0]); - - obj = kptr[0]; - goto L_jump_start; + WSTACK_PUSH4(s, (UWord)kptr, (UWord)vptr, + ENC_MAP_PAIR, size); } } break; @@ -2720,8 +2724,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, *ep++ = bitsize; } if (ctx && j > r * B2T_MEMCPY_FACTOR) { - WSTACK_PUSH3(s, (UWord)data_dst, (UWord)bytes, bitoffs); - WSTACK_PUSH2(s, ENC_BIN_COPY, 8*j + bitsize); + WSTACK_PUSH5(s, (UWord)data_dst, (UWord)bytes, bitoffs, + ENC_BIN_COPY, 8*j + bitsize); } else { copy_binary_to_buffer(data_dst, 0, bytes, bitoffs, 8 * j + bitsize); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 891046a8b5..02b8f399b5 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -597,6 +597,28 @@ do { \ *s.wsp++ = (z); \ } while(0) +#define WSTACK_PUSH4(s, A1, A2, A3, A4) \ +do { \ + if (s.wsp > s.wend - 4) { \ + erl_grow_wstack(&s, WSTK_DEF_STACK(s)); \ + } \ + *s.wsp++ = (A1); \ + *s.wsp++ = (A2); \ + *s.wsp++ = (A3); \ + *s.wsp++ = (A4); \ +} while(0) + +#define WSTACK_PUSH5(s, A1, A2, A3, A4, A5) \ +do { \ + if (s.wsp > s.wend - 5) { \ + erl_grow_wstack(&s, WSTK_DEF_STACK(s)); \ + } \ + *s.wsp++ = (A1); \ + *s.wsp++ = (A2); \ + *s.wsp++ = (A3); \ + *s.wsp++ = (A4); \ + *s.wsp++ = (A5); \ +} while(0) #define WSTACK_COUNT(s) (s.wsp - s.wstart) #define WSTACK_ISEMPTY(s) (s.wsp == s.wstart) #define WSTACK_POP(s) (*(--s.wsp)) -- cgit v1.2.3 From 5d66bc37e4583074abd00cb05f9a377f39e2081f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 19 Aug 2014 16:45:45 +0200 Subject: erts: Optimize some repeated calls to {E,W}STACK_PUSH --- erts/emulator/beam/erl_printf_term.c | 32 ++++++++------------------------ erts/emulator/beam/external.c | 17 +++++++---------- erts/emulator/beam/global.h | 25 +++++++++++++++++++++++++ erts/emulator/beam/utils.c | 11 +++-------- 4 files changed, 43 insertions(+), 42 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index d18760dc43..31acfdc6d6 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2013. All Rights Reserved. + * Copyright Ericsson AB 2005-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -283,13 +283,9 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, tl = CDR(cons); if (is_not_nil(tl)) { if (is_list(tl)) { - WSTACK_PUSH(s, tl); - WSTACK_PUSH(s, PRT_ONE_CONS); - WSTACK_PUSH(s, PRT_COMMA); + WSTACK_PUSH3(s, tl, PRT_ONE_CONS, PRT_COMMA); } else { - WSTACK_PUSH(s, tl); - WSTACK_PUSH(s, PRT_TERM); - WSTACK_PUSH(s, PRT_BAR); + WSTACK_PUSH3(s, tl, PRT_TERM, PRT_BAR); } } } @@ -299,9 +295,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, break; default: /* PRT_LAST_ARRAY_ELEMENT+1 and upwards */ obj = *popped.ptr; - WSTACK_PUSH(s, (UWord) (popped.ptr + 1)); - WSTACK_PUSH(s, val-1); - WSTACK_PUSH(s, PRT_COMMA); + WSTACK_PUSH3(s, (UWord) (popped.ptr + 1), val-1, PRT_COMMA); break; } break; @@ -431,8 +425,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, WSTACK_PUSH(s,PRT_CLOSE_TUPLE); ++nobj; if (i > 0) { - WSTACK_PUSH(s, (UWord) nobj); - WSTACK_PUSH(s, PRT_LAST_ARRAY_ELEMENT+i-1); + WSTACK_PUSH2(s, (UWord) nobj, PRT_LAST_ARRAY_ELEMENT+i-1); } break; case FLOAT_DEF: { @@ -502,19 +495,10 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, WSTACK_PUSH(s, PRT_CLOSE_TUPLE); if (n > 0) { n--; - WSTACK_PUSH(s, vs[n]); - WSTACK_PUSH(s, PRT_TERM); - WSTACK_PUSH(s, PRT_ASSOC); - WSTACK_PUSH(s, ks[n]); - WSTACK_PUSH(s, PRT_TERM); - + WSTACK_PUSH5(s, vs[n], PRT_TERM, PRT_ASSOC, ks[n], PRT_TERM); while (n--) { - WSTACK_PUSH(s, PRT_COMMA); - WSTACK_PUSH(s, vs[n]); - WSTACK_PUSH(s, PRT_TERM); - WSTACK_PUSH(s, PRT_ASSOC); - WSTACK_PUSH(s, ks[n]); - WSTACK_PUSH(s, PRT_TERM); + WSTACK_PUSH6(s, PRT_COMMA, vs[n], PRT_TERM, PRT_ASSOC, + ks[n], PRT_TERM); } } } diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index fb2fe146f3..5e1f5c56e1 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2368,8 +2368,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, obj = CAR(cons); tl = CDR(cons); - WSTACK_PUSH(s, is_list(tl) ? ENC_ONE_CONS : ENC_TERM); - WSTACK_PUSH(s, tl); + WSTACK_PUSH2(s, (is_list(tl) ? ENC_ONE_CONS : ENC_TERM), + tl); } break; case ENC_PATCH_FUN_SIZE: @@ -2433,9 +2433,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, #else Eterm* ptr = (Eterm *) obj; #endif - WSTACK_PUSH(s, val-1); obj = *ptr++; - WSTACK_PUSH(s, (UWord)ptr); + WSTACK_PUSH2(s, val-1, (UWord)ptr); } break; } @@ -2592,8 +2591,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, ep += 4; } if (i > 0) { - WSTACK_PUSH(s, ENC_LAST_ARRAY_ELEMENT+i-1); - WSTACK_PUSH(s, (UWord)ptr); + WSTACK_PUSH2(s, ENC_LAST_ARRAY_ELEMENT+i-1, (UWord)ptr); } break; @@ -2762,8 +2760,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, if ((dflags & DFLAG_NEW_FUN_TAGS) != 0) { *ep++ = NEW_FUN_EXT; - WSTACK_PUSH(s, ENC_PATCH_FUN_SIZE); - WSTACK_PUSH(s, (UWord) ep); /* Position for patching in size */ + WSTACK_PUSH2(s, ENC_PATCH_FUN_SIZE, + (UWord) ep); /* Position for patching in size */ ep += 4; *ep = funp->arity; ep += 1; @@ -2810,8 +2808,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, ep += 1; } for (ei = funp->num_free-1; ei > 0; ei--) { - WSTACK_PUSH(s, ENC_TERM); - WSTACK_PUSH(s, (UWord) funp->env[ei]); + WSTACK_PUSH2(s, ENC_TERM, (UWord) funp->env[ei]); } if (funp->num_free != 0) { obj = funp->env[0]; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 02b8f399b5..7f1bd36b28 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -479,6 +479,17 @@ do { \ *s.sp++ = (z); \ } while(0) +#define ESTACK_PUSH4(s, E1, E2, E3, E4) \ +do { \ + if (s.sp > s.end - 4) { \ + erl_grow_estack(&s, ESTK_DEF_STACK(s)); \ + } \ + *s.sp++ = (E1); \ + *s.sp++ = (E2); \ + *s.sp++ = (E3); \ + *s.sp++ = (E4); \ +} while(0) + #define ESTACK_COUNT(s) (s.sp - s.start) #define ESTACK_ISEMPTY(s) (s.sp == s.start) #define ESTACK_POP(s) (*(--s.sp)) @@ -619,6 +630,20 @@ do { \ *s.wsp++ = (A4); \ *s.wsp++ = (A5); \ } while(0) + +#define WSTACK_PUSH6(s, A1, A2, A3, A4, A5, A6) \ +do { \ + if (s.wsp > s.wend - 6) { \ + erl_grow_wstack(&s, WSTK_DEF_STACK(s)); \ + } \ + *s.wsp++ = (A1); \ + *s.wsp++ = (A2); \ + *s.wsp++ = (A3); \ + *s.wsp++ = (A4); \ + *s.wsp++ = (A5); \ + *s.wsp++ = (A6); \ +} while(0) + #define WSTACK_COUNT(s) (s.wsp - s.wstart) #define WSTACK_ISEMPTY(s) (s.wsp == s.wstart) #define WSTACK_POP(s) (*(--s.wsp)) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 72092ec7b0..cd62394987 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1228,25 +1228,20 @@ make_hash2(Eterm term) if (size == 0) { goto hash2_common; } - ESTACK_PUSH(s, hash_xor_values); - ESTACK_PUSH(s, hash_xor_keys); - ESTACK_PUSH(s, hash); - ESTACK_PUSH(s, HASH_MAP_TAIL); + ESTACK_PUSH4(s, hash_xor_values, hash_xor_keys, hash, HASH_MAP_TAIL); hash = 0; hash_xor_keys = 0; hash_xor_values = 0; for (i = size - 1; i >= 0; i--) { tmp = vs[i]; - ESTACK_PUSH(s, HASH_MAP_VAL); - ESTACK_PUSH(s, tmp); + ESTACK_PUSH2(s, HASH_MAP_VAL, tmp); } /* We do not want to expose the tuple representation. * Do not push the keys as a tuple. */ for (i = size - 1; i >= 0; i--) { tmp = ks[i]; - ESTACK_PUSH(s, HASH_MAP_KEY); - ESTACK_PUSH(s, tmp); + ESTACK_PUSH2(s, HASH_MAP_KEY, tmp); } goto hash2_common; } -- cgit v1.2.3 From 05f748bfdaa4297ae45264ae9297cadb5f33966e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 2 Sep 2014 21:16:58 +0200 Subject: erts: Fix bug in term_to_binary that reallocates binary with wrong size --- erts/emulator/beam/external.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 8d240355b0..9b9b4b2a62 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1925,6 +1925,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla } real_size = endp - bytes; result_bin = erts_bin_realloc(context->s.ec.result_bin,real_size); + result_bin->orig_size = real_size; level = context->s.ec.level; BUMP_REDS(p, (initial_reds - reds) / TERM_TO_BINARY_LOOP_FACTOR); if (level == 0 || real_size < 6) { /* We are done */ @@ -2004,6 +2005,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla erl_zlib_deflate_finish(&(context->s.cc.stream)); result_bin = erts_bin_realloc(context->s.cc.destination_bin, context->s.cc.dest_len+6); + result_bin->orig_size = context->s.cc.dest_len+6; context->s.cc.destination_bin = NULL; pb = (ProcBin *) HAlloc(p, PROC_BIN_SIZE); pb->thing_word = HEADER_PROC_BIN; -- cgit v1.2.3 From b182aa2cfab793e566c92183c361e78f4d6f84cb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 3 Sep 2014 17:13:22 +0200 Subject: erts: Fix bug with enif_make_copy reallocating writable binary that could invalidate a pointer received from an earlier call to enif_inspect_binary. Solution: Emasculate writable binary at enif_inspect_binary. There are room for optimizations here as we now do an unconditional emasculation even though enif_make_copy is not called later in the NIF. --- erts/emulator/beam/erl_nif.c | 12 ++++++++++++ erts/emulator/test/nif_SUITE.erl | 17 +++++++++++++++++ erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 21 +++++++++++++++++++++ 3 files changed, 50 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index ff551ea3af..8c4bf73dbe 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -472,6 +472,18 @@ int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin) struct enif_tmp_obj_t* tmp; byte* raw_ptr; }u; + + if (is_boxed(bin_term) && *binary_val(bin_term) == HEADER_SUB_BIN) { + ErlSubBin* sb = (ErlSubBin*) binary_val(bin_term); + if (sb->is_writable) { + ProcBin* pb = (ProcBin*) binary_val(sb->orig); + ASSERT(pb->thing_word == HEADER_PROC_BIN); + if (pb->flags) { + erts_emasculate_writable_binary(pb); + sb->is_writable = 0; + } + } + } u.tmp = NULL; bin->data = erts_get_aligned_binary_bytes_extra(bin_term, &u.raw_ptr, allocator, sizeof(struct enif_tmp_obj_t)); diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index b2da6f58af..2e9b4aeab1 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -37,6 +37,7 @@ threading/1, send/1, send2/1, send3/1, send_threaded/1, neg/1, is_checks/1, get_length/1, make_atom/1, make_string/1, reverse_list_test/1, + otp_9828/1, otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1 ]). @@ -64,6 +65,7 @@ all() -> resource_takeover, threading, send, send2, send3, send_threaded, neg, is_checks, get_length, make_atom, make_string,reverse_list_test, + otp_9828, otp_9668, consume_timeslice, dirty_nif, dirty_nif_send ]. @@ -1440,6 +1442,20 @@ otp_9668(Config) -> ?line verify_tmpmem(TmpMem), ok. +otp_9828(doc) -> ["Copy of writable binary"]; +otp_9828(Config) -> + ensure_lib_loaded(Config, 1), + + otp_9828_loop(<<"I'm alive!">>, 1000). + +otp_9828_loop(Bin, 0) -> + ok; +otp_9828_loop(Bin, Val) -> + WrtBin = <>, + ok = otp_9828_nif(WrtBin), + otp_9828_loop(WrtBin, Val-1). + + consume_timeslice(Config) when is_list(Config) -> CONTEXT_REDS = 2000, Me = self(), @@ -1684,6 +1700,7 @@ reverse_list(_) -> ?nif_stub. echo_int(_) -> ?nif_stub. type_sizes() -> ?nif_stub. otp_9668_nif(_) -> ?nif_stub. +otp_9828_nif(_) -> ?nif_stub. consume_timeslice_nif(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. send_from_dirty_nif(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 955dc64189..25e20f3e7d 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1473,6 +1473,26 @@ static ERL_NIF_TERM otp_9668_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar return atom_ok; } +static ERL_NIF_TERM otp_9828_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + /* copy a writable binary could reallocate it due to "emasculation" + and thereby render a previous inspection invalid. + */ + ErlNifBinary bin1; + ErlNifEnv* myenv; + + if (!enif_inspect_binary(env, argv[0], &bin1)) { + return enif_make_badarg(env); + } + + myenv = enif_alloc_env(); + enif_make_copy(myenv, argv[0]); + enif_free_env(myenv); + + return memcmp(bin1.data, "I'm alive!", 10)==0 ? atom_ok : atom_false; +} + + static ERL_NIF_TERM consume_timeslice_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { int percent; @@ -1741,6 +1761,7 @@ static ErlNifFunc nif_funcs[] = {"echo_int", 1, echo_int}, {"type_sizes", 0, type_sizes}, {"otp_9668_nif", 1, otp_9668_nif}, + {"otp_9828_nif", 1, otp_9828_nif}, {"consume_timeslice_nif", 2, consume_timeslice_nif}, #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT {"call_dirty_nif", 3, call_dirty_nif}, -- cgit v1.2.3 From 21e4cf5b378ed378296a544d0ad8da08cf95700e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 3 Sep 2014 20:22:42 +0200 Subject: erts: Correct conversion of MIN_SMALL numeral to fixnum list_to_integer and binary_to_integer returned un-normalized bignum for -134217728 on 32-bit and -576460752303423488 on 64-bit. Thanks to Jesper Louis Andersen, Mikael Pettersson and Anthony Ramine for report, initial patch and optimization suggestion. --- erts/emulator/beam/bif.c | 13 +++++++------ erts/emulator/beam/big.c | 11 ++++++----- erts/emulator/test/num_bif_SUITE.erl | 32 +++++++++++++++----------------- 3 files changed, 28 insertions(+), 28 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index fcbeb6cf5c..b835946729 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -2887,9 +2887,6 @@ static int do_list_to_integer(Process *p, Eterm orig_list, res = big_plus_small(res, m, hp); } - if (is_big(res)) /* check if small */ - res = big_plus_small(res, 0, hp); /* includes conversion to small */ - if (neg) { if (is_small(res)) res = make_small(-signed_val(res)); @@ -2899,8 +2896,12 @@ static int do_list_to_integer(Process *p, Eterm orig_list, } } - if (is_big(res)) { - hp += (big_arity(res)+1); + if (is_not_small(res)) { + res = big_plus_small(res, 0, hp); /* includes conversion to small */ + + if (is_not_small(res)) { + hp += (big_arity(res)+1); + } } HRelease(p,hp_end,hp); } diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index e62caa6b22..a8710dd910 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -2675,9 +2675,6 @@ Eterm erts_chars_to_integer(Process *BIF_P, char *bytes, res = big_plus_small(res, m, hp); } - if (is_big(res)) /* check if small */ - res = big_plus_small(res, 0, hp); /* includes conversion to small */ - if (neg) { if (is_small(res)) res = make_small(-signed_val(res)); @@ -2687,8 +2684,12 @@ Eterm erts_chars_to_integer(Process *BIF_P, char *bytes, } } - if (is_big(res)) { - hp += (big_arity(res) + 1); + if (is_not_small(res)) { + res = big_plus_small(res, 0, hp); /* includes conversion to small */ + + if (is_not_small(res)) { + hp += (big_arity(res) + 1); + } } HRelease(BIF_P, hp_end, hp); goto bytebuf_to_integer_1_done; diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index ff8d18eef8..8cf8377c30 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -394,18 +394,15 @@ t_string_to_integer(Config) when is_list(Config) -> test_sti(268435455), test_sti(-268435455), - %% 1 bsl 28 - 1, just before 32 bit bignum - test_sti(1 bsl 28 - 1), - %% 1 bsl 28, just beyond 32 bit small - test_sti(1 bsl 28), - %% 1 bsl 33, just beyond 32 bit - test_sti(1 bsl 33), - %% 1 bsl 60 - 1, just before 64 bit bignum - test_sti(1 bsl 60 - 1), - %% 1 bsl 60, just beyond 64 bit small - test_sti(1 bsl 60), - %% 1 bsl 65, just beyond 64 bit - test_sti(1 bsl 65), + % Interesting values around 2-pows, such as MIN_SMALL and MAX_SMALL. + lists:foreach(fun(Bits) -> + N = 1 bsl Bits, + test_sti(N - 1), + test_sti(N), + test_sti(N + 1) + end, + lists:seq(16, 130)), + %% Bignums. test_sti(123456932798748738738,16), test_sti(list_to_integer(lists:duplicate(2000, $1))), @@ -454,10 +451,11 @@ test_sti(Num) -> end|| Base <- lists:seq(2,36)]. test_sti(Num,Base) -> - Num = list_to_integer(int2list(Num,Base),Base), - Num = -1*list_to_integer(int2list(Num*-1,Base),Base), - Num = binary_to_integer(int2bin(Num,Base),Base), - Num = -1*binary_to_integer(int2bin(Num*-1,Base),Base). + Neg = -Num, + Num = list_to_integer(int2list(Num,Base),Base), + Neg = list_to_integer(int2list(Num*-1,Base),Base), + Num = binary_to_integer(int2bin(Num,Base),Base), + Neg = binary_to_integer(int2bin(Num*-1,Base),Base). % Calling this function (which is not supposed to be inlined) prevents % the compiler from calculating the answer, so we don't test the compiler -- cgit v1.2.3 From 2b53f2b1514b0d405ab92c767277483c761ad928 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 4 Sep 2014 20:10:54 +0200 Subject: erts: Refactor binary allocation interface to also initialize Binary except the reference counter 'refc', as different callers have different strategies regarding the lifetime of the binary. --- erts/emulator/beam/beam_emu.c | 4 ---- erts/emulator/beam/binary.c | 5 ----- erts/emulator/beam/dist.c | 2 -- erts/emulator/beam/erl_bif_binary.c | 2 -- erts/emulator/beam/erl_binary.h | 34 +++++++++++++++++++++++++++------- erts/emulator/beam/erl_bits.c | 10 ---------- erts/emulator/beam/erl_gc.c | 1 - erts/emulator/beam/erl_nif.c | 3 --- erts/emulator/beam/external.c | 10 ---------- erts/emulator/beam/io.c | 7 ------- erts/emulator/hipe/hipe_native_bif.c | 3 --- 11 files changed, 27 insertions(+), 54 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 8bfb7d2ad2..32961da271 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3782,8 +3782,6 @@ get_map_elements_fail: * Allocate the binary struct itself. */ bptr = erts_bin_nrml_alloc(num_bytes); - bptr->flags = 0; - bptr->orig_size = num_bytes; erts_refc_init(&bptr->refc, 1); erts_current_bin = (byte *) bptr->orig_bytes; @@ -3883,8 +3881,6 @@ get_map_elements_fail: * Allocate the binary struct itself. */ bptr = erts_bin_nrml_alloc(tmp_arg1); - bptr->flags = 0; - bptr->orig_size = tmp_arg1; erts_refc_init(&bptr->refc, 1); erts_current_bin = (byte *) bptr->orig_bytes; diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index f50d484576..b014bca108 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -83,8 +83,6 @@ new_binary(Process *p, byte *buf, Uint len) * Allocate the binary struct itself. */ bptr = erts_bin_nrml_alloc(len); - bptr->flags = 0; - bptr->orig_size = len; erts_refc_init(&bptr->refc, 1); if (buf != NULL) { sys_memcpy(bptr->orig_bytes, buf, len); @@ -122,8 +120,6 @@ Eterm erts_new_mso_binary(Process *p, byte *buf, int len) * Allocate the binary struct itself. */ bptr = erts_bin_nrml_alloc(len); - bptr->flags = 0; - bptr->orig_size = len; erts_refc_init(&bptr->refc, 1); if (buf != NULL) { sys_memcpy(bptr->orig_bytes, buf, len); @@ -177,7 +173,6 @@ erts_realloc_binary(Eterm bin, size_t size) } else { /* REFC */ ProcBin* pb = (ProcBin *) bval; Binary* newbin = erts_bin_realloc(pb->val, size); - newbin->orig_size = size; pb->val = newbin; pb->size = size; pb->bytes = (byte*) newbin->orig_bytes; diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index ec07ddcd9c..dcbbb857da 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -622,9 +622,7 @@ alloc_dist_obuf(Uint size) ErtsDistOutputBuf *obuf; Uint obuf_size = sizeof(ErtsDistOutputBuf)+sizeof(byte)*(size-1); Binary *bin = erts_bin_drv_alloc(obuf_size); - bin->flags = BIN_FLAG_DRV; erts_refc_init(&bin->refc, 1); - bin->orig_size = (SWord) obuf_size; obuf = (ErtsDistOutputBuf *) &bin->orig_bytes[0]; #ifdef DEBUG obuf->dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN; diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 3bf78adce7..85bc2daf5d 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -2424,8 +2424,6 @@ static BIF_RETTYPE do_binary_copy(Process *p, Eterm bin, Eterm en) } cbs->result = erts_bin_nrml_alloc(target_size); /* Always offheap if trapping */ - cbs->result->flags = 0; - cbs->result->orig_size = target_size; erts_refc_init(&(cbs->result->refc), 1); t = (byte *) cbs->result->orig_bytes; /* No offset or anything */ pos = 0; diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index 06dfeb1260..8d264d166e 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -231,41 +231,58 @@ erts_free_aligned_binary_bytes(byte* buf) # define CHICKEN_PAD (sizeof(void*) - 1) #endif +/* Caller must initialize 'refc' +*/ ERTS_GLB_INLINE Binary * erts_bin_drv_alloc_fnf(Uint size) { Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD; - void *res; + Binary *res; + if (bsize < size) /* overflow */ return NULL; res = erts_alloc_fnf(ERTS_ALC_T_DRV_BINARY, bsize); ERTS_CHK_BIN_ALIGNMENT(res); - return (Binary *) res; + if (res) { + res->orig_size = size; + res->flags = BIN_FLAG_DRV; + } + return res; } +/* Caller must initialize 'refc' +*/ ERTS_GLB_INLINE Binary * erts_bin_drv_alloc(Uint size) { Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD; - void *res; + Binary *res; + if (bsize < size) /* overflow */ erts_alloc_enomem(ERTS_ALC_T_DRV_BINARY, size); res = erts_alloc(ERTS_ALC_T_DRV_BINARY, bsize); ERTS_CHK_BIN_ALIGNMENT(res); - return (Binary *) res; + res->orig_size = size; + res->flags = BIN_FLAG_DRV; + return res; } +/* Caller must initialize 'refc' +*/ ERTS_GLB_INLINE Binary * erts_bin_nrml_alloc(Uint size) { Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD; - void *res; + Binary *res; + if (bsize < size) /* overflow */ erts_alloc_enomem(ERTS_ALC_T_BINARY, size); res = erts_alloc(ERTS_ALC_T_BINARY, bsize); ERTS_CHK_BIN_ALIGNMENT(res); - return (Binary *) res; + res->orig_size = size; + res->flags = 0; + return res; } ERTS_GLB_INLINE Binary * @@ -280,6 +297,8 @@ erts_bin_realloc_fnf(Binary *bp, Uint size) return NULL; nbp = erts_realloc_fnf(type, (void *) bp, bsize); ERTS_CHK_BIN_ALIGNMENT(nbp); + if (nbp) + nbp->orig_size = size; return nbp; } @@ -297,6 +316,7 @@ erts_bin_realloc(Binary *bp, Uint size) if (!nbp) erts_realloc_enomem(type, bp, bsize); ERTS_CHK_BIN_ALIGNMENT(nbp); + nbp->orig_size = size; return nbp; } @@ -329,4 +349,4 @@ erts_create_magic_binary(Uint size, void (*destructor)(Binary *)) #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ -#endif +#endif /* !__ERL_BINARY_H */ diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 73765772c8..71d31c01aa 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -1299,7 +1299,6 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term, if (binp->orig_size < pb->size) { Uint new_size = 2*pb->size; binp = erts_bin_realloc(binp, new_size); - binp->orig_size = new_size; pb->val = binp; pb->bytes = (byte *) binp->orig_bytes; } @@ -1371,8 +1370,6 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term, * Allocate the binary data struct itself. */ bptr = erts_bin_nrml_alloc(bin_size); - bptr->flags = 0; - bptr->orig_size = bin_size; erts_refc_init(&bptr->refc, 1); erts_current_bin = (byte *) bptr->orig_bytes; @@ -1475,7 +1472,6 @@ erts_bs_private_append(Process* p, Eterm bin, Eterm build_size_term, Uint unit) * is safe to reallocate it. */ binp = erts_bin_realloc(binp, new_size); - binp->orig_size = new_size; pb->val = binp; pb->bytes = (byte *) binp->orig_bytes; } else { @@ -1488,8 +1484,6 @@ erts_bs_private_append(Process* p, Eterm bin, Eterm build_size_term, Uint unit) * binary and copy the contents of the old binary into it. */ Binary* bptr = erts_bin_nrml_alloc(new_size); - bptr->flags = 0; - bptr->orig_size = new_size; erts_refc_init(&bptr->refc, 1); sys_memcpy(bptr->orig_bytes, binp->orig_bytes, binp->orig_size); pb->flags |= PB_IS_WRITABLE | PB_ACTIVE_WRITER; @@ -1537,8 +1531,6 @@ erts_bs_init_writable(Process* p, Eterm sz) * Allocate the binary data struct itself. */ bptr = erts_bin_nrml_alloc(bin_size); - bptr->flags = 0; - bptr->orig_size = bin_size; erts_refc_init(&bptr->refc, 1); /* @@ -1585,9 +1577,7 @@ erts_emasculate_writable_binary(ProcBin* pb) /* Our allocators are 8 byte aligned, i.e., shrinking with less than 8 bytes will have no real effect */ if (unused >= 8) { - Uint new_size = pb->size; binp = erts_bin_realloc(binp, pb->size); - binp->orig_size = new_size; pb->val = binp; pb->bytes = (byte *) binp->orig_bytes; } diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 0db42d4325..1dc9e8a786 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2400,7 +2400,6 @@ sweep_off_heap(Process *p, int fullsweep) } pb->val = erts_bin_realloc(pb->val, new_size); - pb->val->orig_size = new_size; pb->bytes = (byte *) pb->val->orig_bytes; } } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 1caea6dcf8..769d13f860 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -551,9 +551,7 @@ int enif_alloc_binary(size_t size, ErlNifBinary* bin) if (refbin == NULL) { return 0; /* The NIF must take action */ } - refbin->flags = BIN_FLAG_DRV; /* BUGBUG: Flag? */ erts_refc_init(&refbin->refc, 1); - refbin->orig_size = (SWord) size; bin->size = size; bin->data = (unsigned char*) refbin->orig_bytes; @@ -573,7 +571,6 @@ int enif_realloc_binary(ErlNifBinary* bin, size_t size) if (!newbin) { return 0; } - newbin->orig_size = size; bin->ref_bin = newbin; bin->data = (unsigned char*) newbin->orig_bytes; bin->size = size; diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 9b9b4b2a62..196913a741 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1899,8 +1899,6 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla } result_bin = erts_bin_nrml_alloc(size); - result_bin->flags = 0; - result_bin->orig_size = size; erts_refc_init(&result_bin->refc, 0); result_bin->orig_bytes[0] = VERSION_MAGIC; /* Next state immediately, no need to export context */ @@ -1925,7 +1923,6 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla } real_size = endp - bytes; result_bin = erts_bin_realloc(context->s.ec.result_bin,real_size); - result_bin->orig_size = real_size; level = context->s.ec.level; BUMP_REDS(p, (initial_reds - reds) / TERM_TO_BINARY_LOOP_FACTOR); if (level == 0 || real_size < 6) { /* We are done */ @@ -1962,8 +1959,6 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla context->s.cc.result_bin = result_bin; result_bin = erts_bin_nrml_alloc(real_size); - result_bin->flags = 0; - result_bin->orig_size = real_size; erts_refc_init(&result_bin->refc, 0); result_bin->orig_bytes[0] = VERSION_MAGIC; @@ -2005,7 +2000,6 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla erl_zlib_deflate_finish(&(context->s.cc.stream)); result_bin = erts_bin_realloc(context->s.cc.destination_bin, context->s.cc.dest_len+6); - result_bin->orig_size = context->s.cc.dest_len+6; context->s.cc.destination_bin = NULL; pb = (ProcBin *) HAlloc(p, PROC_BIN_SIZE); pb->thing_word = HEADER_PROC_BIN; @@ -3387,8 +3381,6 @@ dec_term_atom_common: } else { Binary* dbin = erts_bin_nrml_alloc(n); ProcBin* pb; - dbin->flags = 0; - dbin->orig_size = n; erts_refc_init(&dbin->refc, 1); pb = (ProcBin *) hp; hp += PROC_BIN_SIZE; @@ -3441,8 +3433,6 @@ dec_term_atom_common: Binary* dbin = erts_bin_nrml_alloc(n); ProcBin* pb; - dbin->flags = 0; - dbin->orig_size = n; erts_refc_init(&dbin->refc, 1); pb = (ProcBin *) hp; pb->thing_word = HEADER_PROC_BIN; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index ae053fc191..afafa96cdf 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -3153,8 +3153,6 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to, Binary* bptr; bptr = erts_bin_nrml_alloc(len); - bptr->flags = 0; - bptr->orig_size = len; erts_refc_init(&bptr->refc, 1); sys_memcpy(bptr->orig_bytes, buf, len); @@ -5506,8 +5504,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) ProcBin* pbp; Binary* bp = erts_bin_nrml_alloc(size); ASSERT(bufp); - bp->flags = 0; - bp->orig_size = (SWord) size; erts_refc_init(&bp->refc, 1); sys_memcpy((void *) bp->orig_bytes, (void *) bufp, size); pbp = (ProcBin *) hp; @@ -5999,9 +5995,7 @@ driver_alloc_binary(ErlDrvSizeT size) bin = erts_bin_drv_alloc_fnf((Uint) size); if (!bin) return NULL; /* The driver write must take action */ - bin->flags = BIN_FLAG_DRV; erts_refc_init(&bin->refc, 1); - bin->orig_size = (SWord) size; return Binary2ErlDrvBinary(bin); } @@ -6031,7 +6025,6 @@ ErlDrvBinary* driver_realloc_binary(ErlDrvBinary* bin, ErlDrvSizeT size) if (!newbin) return NULL; - newbin->orig_size = size; return Binary2ErlDrvBinary(newbin); } diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index 7d343dd91e..7e8632b50d 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -330,8 +330,6 @@ char *hipe_bs_allocate(int len) Binary *bptr; bptr = erts_bin_nrml_alloc(len); - bptr->flags = 0; - bptr->orig_size = len; erts_smp_atomic_init_nob(&bptr->refc, 1); return bptr->orig_bytes; } @@ -341,7 +339,6 @@ Binary *hipe_bs_reallocate(Binary* oldbptr, int newsize) Binary *bptr; bptr = erts_bin_realloc(oldbptr, newsize); - bptr->orig_size = newsize; return bptr; } -- cgit v1.2.3 From 933a5f071e0dc7f2051e1ccc58ca4e361db52ef4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 5 Sep 2014 11:51:59 +0200 Subject: erts: Fix gdb command etp-carrier-blocks for 32-bit --- erts/etc/unix/etp-commands.in | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index bf6eb00314..bec110045a 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -3481,11 +3481,13 @@ end define etp-carrier-blocks set $etp_crr = (Carrier_t*) $arg0 set $etp_alc = (Allctr_t*)($etp_crr->allctr.counter & ~7) + set $etp_crr_end = ((char*)$etp_crr + ($etp_crr->chdr & ~7) - (sizeof(void*) & ~8)) set $etp_blk = (Block_t*) ((char*)$etp_crr + $etp_alc->mbc_header_size) set $etp_prev_blk = 0 set $etp_error_cnt = 0 set $etp_ablk_cnt = 0 set $etp_fblk_cnt = 0 + set $etp_aborted = 0 if $argc == 2 set $etp_be_silent = $arg1 @@ -3532,14 +3534,21 @@ define etp-carrier-blocks end set $etp_prev_blk = $etp_blk set $etp_blk = (Block_t*) ((char*)$etp_blk + $etp_blk_sz) + if $etp_blk < (Block_t*) ((char*)$etp_prev_blk + $etp_alc->min_block_size) || $etp_blk >= $etp_crr_end + printf "ERROR: Invalid size of block at %#lx. ABORTING\n", $etp_prev_blk + set $etp_aborted = 1 + loop_break + end end - if ((char*)$etp_blk + $etp_blk_sz) != ((char*)$etp_crr + ($etp_crr->chdr & ~7)) - printf "ERROR: Last block not at end of carrier\n" - set $etp_error_cnt = $etp_error_cnt + 1 + if !$etp_aborted + if ((char*)$etp_blk + $etp_blk_sz) != $etp_crr_end + printf "ERROR: Last block not at end of carrier\n" + set $etp_error_cnt = $etp_error_cnt + 1 + end + printf "Allocated blocks: %u\n", $etp_ablk_cnt + printf "Free blocks: %u\n", $etp_fblk_cnt end - printf "Allocated blocks: %u\n", $etp_ablk_cnt - printf "Free blocks: %u\n", $etp_fblk_cnt if $etp_error_cnt printf "%u ERRORs reported above\n", $etp-error-cnt end -- cgit v1.2.3 From eb3d3e8f726c64f12c00517be87c53dc5de934e2 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 5 Sep 2014 14:54:25 +0200 Subject: Use separate allocation type for NIF export --- erts/emulator/beam/erl_alloc.types | 2 ++ erts/emulator/beam/erl_nif.c | 18 +++++++++++++++--- erts/emulator/beam/erl_process.h | 7 ++++--- 3 files changed, 21 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 17ac6316b7..37354b7f8d 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -357,6 +357,7 @@ type DB_MS_PSDO_PROC LONG_LIVED_LOW ETS db_match_pseudo_proc type SCHDLR_DATA LONG_LIVED_LOW SYSTEM scheduler_data type LL_TEMP_TERM LONG_LIVED_LOW SYSTEM ll_temp_term +type NIF_TRAP_EXPORT STANDARD_LOW CODE nif_trap_export_entry type EXPORT LONG_LIVED_LOW CODE export_entry type MONITOR_SH STANDARD_LOW PROCESSES monitor_sh type NLINK_SH STANDARD_LOW PROCESSES nlink_sh @@ -375,6 +376,7 @@ type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data type LL_TEMP_TERM LONG_LIVED SYSTEM ll_temp_term +type NIF_TRAP_EXPORT STANDARD CODE nif_trap_export_entry type EXPORT LONG_LIVED CODE export_entry type MONITOR_SH FIXED_SIZE PROCESSES monitor_sh type NLINK_SH FIXED_SIZE PROCESSES nlink_sh diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 1414744763..df135c09c5 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1566,17 +1566,29 @@ allocate_nif_sched_data(Process* proc, int argc) argv_extra = argc > 1 ? sizeof(Eterm)*(argc-1) : 0; total = sizeof(NifExport) + argv_extra; - ep = erts_alloc(ERTS_ALC_T_PSD, total); + ep = erts_alloc(ERTS_ALC_T_NIF_TRAP_EXPORT, total); sys_memset((void*) ep, 0, total); ep->alloced_argv_sz = argc; for (i=0; iexp.addressv[i] = &ep->exp.code[3]; } ep->exp.code[3] = (BeamInstr) em_call_nif; - (void) ERTS_PROC_SET_NIF_TRAP_EXPORT(proc, ERTS_PROC_LOCK_MAIN, &ep->exp); + (void) ERTS_PROC_SET_NIF_TRAP_EXPORT(proc, ERTS_PROC_LOCK_MAIN, ep); return ep; } +static ERTS_INLINE void +destroy_nif_export(NifExport *nif_export) +{ + erts_free(ERTS_ALC_T_NIF_TRAP_EXPORT, (void *) nif_export); +} + +void +erts_destroy_nif_export(void *nif_export) +{ + destroy_nif_export((NifExport *) nif_export); +} + /* * Initialize a NifExport struct. Create it if needed and store it in the * proc. The direct_fp function is what will be invoked by op_call_nif, and @@ -1599,7 +1611,7 @@ init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirec ep = allocate_nif_sched_data(proc, argc); else if (need_save && ep->alloced_argv_sz < argc) { NifExport* new_ep = allocate_nif_sched_data(proc, argc); - erts_free(ERTS_ALC_T_PSD, (void*) ep); + destroy_nif_export(ep); ep = new_ep; } ERTS_VBUMP_ALL_REDS(proc); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 31f4a09c94..d12eb92bff 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1362,6 +1362,7 @@ Uint64 erts_ensure_later_proc_interval(Uint64); Uint64 erts_step_proc_interval(void); int erts_setup_nif_gc(Process* proc, Eterm** objv, int* nobj); /* see erl_nif.c */ +void erts_destroy_nif_export(void *); /* see erl_nif.c */ ErtsProcList *erts_proclist_create(Process *); void erts_proclist_destroy(ErtsProcList *); @@ -1814,9 +1815,9 @@ erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *data) ((ErtsProcSysTaskQs *) erts_psd_set((P), (L), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT))) #define ERTS_PROC_GET_NIF_TRAP_EXPORT(P) \ - ((Export *) erts_psd_get((P), ERTS_PSD_NIF_TRAP_EXPORT)) -#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, L, DSTE) \ - ((Export *) erts_psd_set((P), (L), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (DSTE))) + erts_psd_get((P), ERTS_PSD_NIF_TRAP_EXPORT) +#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, L, NTE) \ + erts_psd_set((P), (L), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE)) ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p); -- cgit v1.2.3 From 18a38b9e5f5fbf4aa8fb7d349bc493c78626d3f6 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 5 Sep 2014 14:55:25 +0200 Subject: Fix leak of NIF exports --- erts/emulator/beam/erl_process.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index e96d5c4ccd..716875c326 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11826,6 +11826,7 @@ erts_continue_exit_process(Process *p) struct saved_calls *scb; process_breakpoint_time_t *pbt; erts_aint32_t state; + void *nif_export; #ifdef DEBUG int yield_allowed = 1; @@ -11976,6 +11977,7 @@ erts_continue_exit_process(Process *p) : NULL); scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, ERTS_PROC_LOCKS_ALL, NULL); pbt = ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCKS_ALL, NULL); + nif_export = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, ERTS_PROC_LOCKS_ALL, NULL); erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL); #ifdef BM_COUNTERS @@ -12023,6 +12025,9 @@ erts_continue_exit_process(Process *p) if (pbt) erts_free(ERTS_ALC_T_BPD, (void *) pbt); + if (nif_export) + erts_destroy_nif_export(nif_export); + delete_process(p); #ifdef ERTS_SMP -- cgit v1.2.3 From 8a7fea6b836569583459f2540b17f0946f85b62f Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 1 Sep 2014 11:53:54 +0200 Subject: Only run upper boundry tuple test with lots of memory --- erts/emulator/test/tuple_SUITE.erl | 95 ++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/tuple_SUITE.erl b/erts/emulator/test/tuple_SUITE.erl index 46ece41096..f627eea07f 100644 --- a/erts/emulator/test/tuple_SUITE.erl +++ b/erts/emulator/test/tuple_SUITE.erl @@ -21,8 +21,9 @@ init_per_group/2,end_per_group/2, t_size/1, t_tuple_size/1, t_element/1, t_setelement/1, t_insert_element/1, t_delete_element/1, - t_list_to_tuple/1, t_tuple_to_list/1, - t_make_tuple_2/1, t_make_tuple_3/1, t_append_element/1, + t_list_to_tuple/1, t_list_to_upper_boundry_tuple/1, t_tuple_to_list/1, + t_make_tuple_2/1, t_make_upper_boundry_tuple_2/1, t_make_tuple_3/1, + t_append_element/1, t_append_element_upper_boundry/1, build_and_match/1, tuple_with_case/1, tuple_in_guard/1]). -include_lib("test_server/include/test_server.hrl"). @@ -40,8 +41,10 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [build_and_match, t_size, t_tuple_size, t_list_to_tuple, + t_list_to_upper_boundry_tuple, t_tuple_to_list, t_element, t_setelement, - t_make_tuple_2, t_make_tuple_3, t_append_element, + t_make_tuple_2, t_make_upper_boundry_tuple_2, t_make_tuple_3, + t_append_element, t_append_element_upper_boundry, t_insert_element, t_delete_element, tuple_with_case, tuple_in_guard]. @@ -49,11 +52,21 @@ groups() -> []. init_per_suite(Config) -> + A0 = case application:start(sasl) of + ok -> [sasl]; + _ -> [] + end, + A = case application:start(os_mon) of + ok -> [os_mon|A0]; + _ -> A0 + end, + [{started_apps, A}|Config]. + +end_per_suite(Config) -> + As = ?config(started_apps, Config), + lists:foreach(fun (A) -> application:stop(A) end, As), Config. -end_per_suite(_Config) -> - ok. - init_per_group(_GroupName, Config) -> Config. @@ -176,14 +189,19 @@ t_list_to_tuple(Config) when is_list(Config) -> {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))), {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))), - % test upper boundry, 16777215 elements - MaxSize = 1 bsl 24 - 1, - MaxTuple = list_to_tuple(lists:seq(1, MaxSize)), - MaxSize = size(MaxTuple), - {'EXIT', {badarg,_}} = (catch list_to_tuple(lists:seq(1, 1 bsl 24))), ok. +t_list_to_upper_boundry_tuple(Config) when is_list(Config) -> + sys_mem_cond_run(2048, + fun () -> + %% test upper boundry, 16777215 elements + MaxSize = 1 bsl 24 - 1, + MaxTuple = list_to_tuple(lists:seq(1, MaxSize)), + MaxSize = size(MaxTuple), + ok + end). + %% Tests tuple_to_list/1. t_tuple_to_list(Config) when is_list(Config) -> @@ -214,8 +232,6 @@ t_make_tuple_2(Config) when is_list(Config) -> t_make_tuple1({a}), t_make_tuple1(erlang:make_tuple(400, [])), - % test upper boundry, 16777215 elements - t_make_tuple(1 bsl 24 - 1, a), {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 24, a)), {'EXIT', {badarg,_}} = (catch erlang:make_tuple(-1, a)), @@ -225,6 +241,13 @@ t_make_tuple_2(Config) when is_list(Config) -> {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 65 + 3, a)), ok. +t_make_upper_boundry_tuple_2(Config) when is_list(Config) -> + sys_mem_cond_run(2048, + fun () -> + %% test upper boundry, 16777215 elements + t_make_tuple(1 bsl 24 - 1, a) + end). + t_make_tuple1(Element) -> lists:foreach(fun(Size) -> t_make_tuple(Size, Element) end, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 255, 256, 511, 512, 999, @@ -309,13 +332,17 @@ t_delete_element(Config) when is_list(Config) -> %% Tests the append_element/2 BIF. t_append_element(Config) when is_list(Config) -> - ok = t_append_element({}, 2048, 2048), - - % test upper boundry, 16777215 elements - MaxSize = 1 bsl 24 - 1, - MaxTuple = list_to_tuple(lists:seq(1, MaxSize)), - {'EXIT',{badarg,_}} = (catch erlang:append_element(MaxTuple, a)), - ok. + ok = t_append_element({}, 2048, 2048). + +t_append_element_upper_boundry(Config) when is_list(Config) -> + sys_mem_cond_run(2048, + fun () -> + %% test upper boundry, 16777215 elements + MaxSize = 1 bsl 24 - 1, + MaxTuple = list_to_tuple(lists:seq(1, MaxSize)), + {'EXIT',{badarg,_}} = (catch erlang:append_element(MaxTuple, a)), + ok + end). t_append_element(_Tuple, 0, _High) -> ok; t_append_element(Tuple, N, High) -> @@ -371,3 +398,31 @@ tuple_in_guard(Config) when is_list(Config) -> %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. + +sys_mem_cond_run(ReqSizeMB, TestFun) when is_integer(ReqSizeMB) -> + case total_memory() of + TotMem when is_integer(TotMem), TotMem >= ReqSizeMB -> + TestFun(); + TotMem when is_integer(TotMem) -> + {skipped, "Not enough memory ("++integer_to_list(TotMem)++" MB)"}; + undefined -> + {skipped, "Could not retrieve memory information"} + end. + + +total_memory() -> + %% Totat memory in MB. + try + MemoryData = memsup:get_system_memory_data(), + case lists:keysearch(total_memory, 1, MemoryData) of + {value, {total_memory, TM}} -> + TM div (1024*1024); + false -> + {value, {system_total_memory, STM}} = + lists:keysearch(system_total_memory, 1, MemoryData), + STM div (1024*1024) + end + catch + _ : _ -> + undefined + end. -- cgit v1.2.3 From a1c09befb56cf8e73b722fe69ff87db65815bb9d Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 8 Sep 2014 09:19:21 +0200 Subject: erts: Print that we are crashdumping earlier Change so that we print that the we generating a crash dump before we actually generate it. So that if a huge crashdump is being dumped, the user knows that something is going on. --- erts/emulator/beam/break.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 08265b590d..5aee85174f 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -754,6 +754,8 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) dumpname = "erl_crash.dump"; else dumpname = &dumpnamebuf[0]; + + erts_fprintf(stderr,"\nCrash dump is being written to: %s...", dumpname); fd = open(dumpname,O_WRONLY | O_CREAT | O_TRUNC,0640); if (fd < 0) @@ -804,7 +806,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) erts_fdprintf(fd, "=end\n"); close(fd); - erts_fprintf(stderr,"\nCrash dump was written to: %s\n", dumpname); + erts_fprintf(stderr,"done\n"); } void -- cgit v1.2.3 From 84144e03cd902813d1c9f9d75a8b6ecc5e58270d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 10 Sep 2014 12:02:22 +0200 Subject: erts: Correct dirty scheduler NIF API for Windows enif_schedule_nif() put LAST of the unconditional functions to keep the order which is vital for ABI compatibility on Windows. The conditional dirty scheduler stuff moved down at the end of the list to keep them out of the way. We don't want them mess things up then they become unconditional some day. --- erts/emulator/beam/erl_nif_api_funcs.h | 43 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index be39816a64..e99cd36153 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -22,7 +22,7 @@ #endif /* -** WARNING: add new ERL_NIF_API_FUNC_DECL entries at the bottom of the list +** WARNING: Add new ERL_NIF_API_FUNC_DECL entries at the bottom of the list ** to keep compatibility on Windows!!! ** ** And don't forget to increase ERL_NIF_MINOR_VERSION in erl_nif.h @@ -141,12 +141,6 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_number,(ErlNifEnv*, ERL_NIF_TERM term)); ERL_NIF_API_FUNC_DECL(void*,enif_dlopen,(const char* lib, void (*err_handler)(void*,const char*), void* err_arg)); ERL_NIF_API_FUNC_DECL(void*,enif_dlsym,(void* handle, const char* symbol, void (*err_handler)(void*,const char*), void* err_arg)); ERL_NIF_API_FUNC_DECL(int,enif_consume_timeslice,(ErlNifEnv*, int percent)); -ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[])); -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); -ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); -#endif - ERL_NIF_API_FUNC_DECL(int, enif_is_map, (ErlNifEnv* env, ERL_NIF_TERM term)); ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); @@ -161,12 +155,23 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_is_tail, (ErlNifEnv *env, ErlNifMap ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIterator *iter)); ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter)); ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)); - +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[])); /* -** Add new entries here to keep compatibility on Windows!!! +** ADD NEW ENTRIES HERE (before this comment) !!! */ + + +/* + * Conditional EXPERIMENTAL stuff always last. + * Must be moved up and made unconditional to support binary backward + * compatibility on Windows. + */ +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT +ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); +ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); #endif +#endif /* ERL_NIF_API_FUNC_DECL */ /* ** Please keep the ERL_NIF_API_FUNC_MACRO list below in the same order @@ -280,19 +285,12 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_make_int64 ERL_NIF_API_FUNC_MACRO(enif_make_int64) # define enif_make_uint64 ERL_NIF_API_FUNC_MACRO(enif_make_uint64) #endif - # define enif_is_exception ERL_NIF_API_FUNC_MACRO(enif_is_exception) # define enif_make_reverse_list ERL_NIF_API_FUNC_MACRO(enif_make_reverse_list) # define enif_is_number ERL_NIF_API_FUNC_MACRO(enif_is_number) # define enif_dlopen ERL_NIF_API_FUNC_MACRO(enif_dlopen) # define enif_dlsym ERL_NIF_API_FUNC_MACRO(enif_dlsym) # define enif_consume_timeslice ERL_NIF_API_FUNC_MACRO(enif_consume_timeslice) -# define enif_schedule_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_nif) -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler) -# define enif_have_dirty_schedulers ERL_NIF_API_FUNC_MACRO(enif_have_dirty_schedulers) -#endif - # define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map) # define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size) # define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map) @@ -307,11 +305,22 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_map_iterator_next ERL_NIF_API_FUNC_MACRO(enif_map_iterator_next) # define enif_map_iterator_prev ERL_NIF_API_FUNC_MACRO(enif_map_iterator_prev) # define enif_map_iterator_get_pair ERL_NIF_API_FUNC_MACRO(enif_map_iterator_get_pair) +# define enif_schedule_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_nif) /* -** Add new entries here +** ADD NEW ENTRIES HERE (before this comment) */ + +/* + * Conditional EXPERIMENTAL stuff always last + * Must be moved up and made unconditional to support binary backward + * compatibility on Windows. + */ +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT +# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler) +# define enif_have_dirty_schedulers ERL_NIF_API_FUNC_MACRO(enif_have_dirty_schedulers) #endif +#endif /* ERL_NIF_API_FUNC_MACRO */ #if defined(__GNUC__) && !(defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) -- cgit v1.2.3 From df5ecc7200c275eb4ac43b79fa2e587d571da696 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 10 Sep 2014 20:48:24 +0200 Subject: Add configure option --with-ssl-incl=PATH to be used together with --with-ssl=PATH if the include directory is in another location. --- erts/configure.in | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 40b335849c..308ec0295e 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3988,7 +3988,7 @@ dnl If set to --with-ssl=PATH we use that path as the prefix, i.e. we dnl use "PATH/include" and "PATH/lib". AC_SUBST(SSL_INCLUDE) -AC_SUBST(SSL_ROOT) +AC_SUBST(SSL_INCDIR) AC_SUBST(SSL_LIBDIR) AC_SUBST(SSL_CRYPTO_LIBNAME) AC_SUBST(SSL_SSL_LIBNAME) @@ -4082,6 +4082,15 @@ AS_HELP_STRING([--with-ssl=PATH], [specify location of OpenSSL include and lib]) AS_HELP_STRING([--with-ssl], [use SSL (default)]) AS_HELP_STRING([--without-ssl], [don't use SSL])) +AC_ARG_WITH(ssl-incl, +AS_HELP_STRING([--with-ssl-incl=PATH], [location of OpenSSL include dir, if different than specified by --with-ssl=PATH]), +[ +case X$with_ssl in + X | Xyes | Xno) AC_MSG_ERROR([--with-ssl-incl=PATH set without --with-ssl=PATH]);; +esac +], +[with_ssl_incl=$with_ssl]) #default + AC_ARG_ENABLE(dynamic-ssl-lib, AS_HELP_STRING([--disable-dynamic-ssl-lib], [disable using dynamic openssl libraries]), @@ -4196,7 +4205,7 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in dir="$erl_xcomp_sysroot$rdir" if test -f "$erl_xcomp_isysroot$rdir/include/openssl/opensslv.h"; then is_real_ssl=yes - SSL_ROOT="$dir" + SSL_INCDIR="$dir" if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes"; then if test -f "$dir/lib/VC/libeay32.lib"; then SSL_RUNTIME_LIBDIR="$rdir/lib/VC" @@ -4326,8 +4335,8 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in # Trust OpenBSD to have everything the in the correct locations. ssl_found=yes ssl_linkable=yes - SSL_ROOT="$erl_xcomp_sysroot/usr" - AC_MSG_RESULT([$SSL_ROOT]) + SSL_INCDIR="$erl_xcomp_sysroot/usr" + AC_MSG_RESULT([$SSL_INCDIR]) SSL_RUNTIME_LIB="/usr/lib" SSL_LIB="$erl_xcomp_sysroot/usr/lib" SSL_BINDIR="/usr/sbin" @@ -4394,7 +4403,10 @@ dnl so it is - be adoptable if test ! -d "$with_ssl" ; then AC_MSG_ERROR(Invalid path to option --with-ssl=PATH) fi - SSL_ROOT="$with_ssl" + if test ! -d "$with_ssl_incl" ; then + AC_MSG_ERROR(Invalid path to option --with-ssl-incl=PATH) + fi + SSL_INCDIR="$with_ssl_incl" SSL_CRYPTO_LIBNAME=crypto SSL_SSL_LIBNAME=ssl if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes" && test -d "$with_ssl/lib/VC"; then @@ -4444,7 +4456,7 @@ dnl so it is - be adoptable elif test '!' -f ${SSL_LIBDIR}/lib${SSL_CRYPTO_LIBNAME}.so -a '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.dylib"; then SSL_STATIC_ONLY=yes fi - SSL_INCLUDE="-I$with_ssl/include" + SSL_INCLUDE="-I$with_ssl_incl/include" SSL_APP=ssl CRYPTO_APP=crypto SSH_APP=ssh @@ -4507,8 +4519,8 @@ if test "x$SSL_APP" != "x" ; then SSL_KRB5_INCLUDE= if test "x$ssl_krb5_enabled" = "xyes" ; then AC_MSG_CHECKING(for krb5.h in standard locations) - for dir in $extra_dir "$SSL_ROOT/include" "$SSL_ROOT/include/openssl" \ - "$SSL_ROOT/include/kerberos" \ + for dir in $extra_dir "$SSL_INCDIR/include" "$SSL_INCDIR/include/openssl" \ + "$SSL_INCDIR/include/kerberos" \ "$erl_xcomp_isysroot/cygdrive/c/kerberos/include" \ "$erl_xcomp_isysroot/usr/local/kerberos/include" \ "$erl_xcomp_isysroot/usr/kerberos/include" \ -- cgit v1.2.3 From ededb6c8c75061ddf3d02a129166fc4f2b12e54a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 10 Sep 2014 20:53:28 +0200 Subject: Correct SSL_RUNTIME_LIBDIR when erl_xcomp_sysroot ends with a / --- erts/configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 308ec0295e..ab2119f0d3 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -4461,7 +4461,7 @@ dnl so it is - be adoptable CRYPTO_APP=crypto SSH_APP=ssh if test "$cross_compiling" = "yes"; then - SSL_RUNTIME_LIBDIR=`echo "$SSL_LIBDIR" | sed -n "s|^$erl_xcomp_sysroot\(.*\)\$|\1|p"` + SSL_RUNTIME_LIBDIR=`echo "$SSL_LIBDIR" | sed -n "s|^$erl_xcomp_sysroot\(/*\)\(.*\)\$|/\2|p"` else SSL_RUNTIME_LIBDIR="$SSL_LIBDIR" fi -- cgit v1.2.3 From 3c248c14a0f5eeab25a38e550e672e074affb79e Mon Sep 17 00:00:00 2001 From: Marcus Arendt Date: Thu, 11 Sep 2014 10:22:30 +0200 Subject: Update preloaded --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 56176 -> 56292 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 5f2b619322..a85f5598b6 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ -- cgit v1.2.3 From a67792b856e68deba9d452b5fd51892c80624e91 Mon Sep 17 00:00:00 2001 From: Marcus Arendt Date: Thu, 11 Sep 2014 10:38:57 +0200 Subject: Update preloaded --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 56328 -> 56328 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index a019714c43..c8ec111e57 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ -- cgit v1.2.3 From a5bfb0a58783c472887ce0ad2060e4a395aa941d Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 12 Sep 2014 12:06:34 +0200 Subject: erts: getsockname is not allowed on non-bound sockets This only produces an error on win32, but should not really be called on *nix either. --- erts/emulator/drivers/common/inet_drv.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 891589d1c5..db8a251fdd 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -4542,11 +4542,13 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, inet_address name; unsigned int sz = sizeof(name); - /* check that it is a socket and that the socket is bound */ - if (IS_SOCKET_ERROR(sock_name(s, (struct sockaddr*) &name, &sz))) - return ctl_error(sock_errno(), rbuf, rsize); - if (name.sa.sa_family != domain) - return ctl_error(EINVAL, rbuf, rsize); + if (bound) { + /* check that it is a socket and that the socket is bound */ + if (IS_SOCKET_ERROR(sock_name(s, (struct sockaddr*) &name, &sz))) + return ctl_error(sock_errno(), rbuf, rsize); + if (name.sa.sa_family != domain) + return ctl_error(EINVAL, rbuf, rsize); + } #ifdef __OSE__ /* for fdopen duplicating the sd will allow to uniquely identify the signal from OSE with erlang port */ -- cgit v1.2.3 From 12d3d25535225934cb2190c82c1b5dae005f1cda Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 11 Sep 2014 17:47:32 +0200 Subject: erts: Remove enif_have_dirty_schedulers() and add 'dirty_scheduler_support' to ErlNifSysInfo --- erts/doc/src/erl_driver.xml | 5 +++ erts/doc/src/erl_nif.xml | 25 +++----------- erts/emulator/beam/erl_driver.h | 2 +- erts/emulator/beam/erl_drv_nif.h | 1 + erts/emulator/beam/erl_nif.c | 10 ------ erts/emulator/beam/erl_nif_api_funcs.h | 2 -- erts/emulator/beam/io.c | 12 +++++++ erts/emulator/test/driver_SUITE.erl | 38 ++++++++++++++++------ .../test/driver_SUITE_data/sys_info_base_drv.c | 9 +++-- .../test/driver_SUITE_data/sys_info_curr_drv.c | 11 +++++-- .../test/driver_SUITE_data/sys_info_prev_drv.c | 9 +++-- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 12 +++++-- 12 files changed, 85 insertions(+), 51 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index ad37813ac0..4a1aab75c7 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -546,6 +546,7 @@ typedef struct ErlDrvSysInfo { int scheduler_threads; int nif_major_version; int nif_minor_version; + int dirty_scheduler_support; } ErlDrvSysInfo; @@ -610,6 +611,10 @@ typedef struct ErlDrvSysInfo { nif_minor_version The value of ERL_NIF_MINOR_VERSION when the runtime system was compiled. + dirty_scheduler_support + A value != 0 if the runtime system has support for dirty scheduler threads; + otherwise 0. + ErlDrvBinary diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 1d33b334bb..3de94be9ff 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -374,9 +374,8 @@ ok the dirty NIF API is available, native code can check to see if the C preprocessor macro ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined. Also, if the Erlang runtime was built without threading support, dirty schedulers are disabled. To check at runtime for the presence - of dirty scheduler threads, code can call the - enif_have_dirty_schedulers() API function, which returns true if dirty - scheduler threads are present, false otherwise.

+ of dirty scheduler threads, code can use the + enif_system_info() API function.

@@ -807,22 +806,6 @@ typedef enum { and return true, or return false if term is not an unsigned integer or is outside the bounds of type unsigned long.

- intenif_have_dirty_schedulers() - Runtime check for the presence of dirty scheduler threads - -

Check at runtime for the presence of dirty scheduler threads. If the emulator is - built with threading support, dirty scheduler threads are available and - enif_have_dirty_schedulers() returns true. If the emulator was built without - threading support, enif_have_dirty_schedulers() returns false.

-

If dirty scheduler threads are not available in the emulator, a call to - enif_schedule_nif with its flags argument set to indicate that the specified - NIF is to be executed on a dirty scheduler thread results in a badarg exception.

-

This function is available only when the emulator is configured with dirty - schedulers enabled. This feature is currently disabled by default. To determine whether - the dirty NIF API is available, native code can check to see if the C preprocessor macro - ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined.

-
-
intenif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin) Inspect the content of a binary

Initialize the structure pointed to by bin with @@ -1261,7 +1244,9 @@ typedef enum {

The flags argument must be set to 0 for a regular NIF, or if the emulator was built the experimental dirty scheduler support enabled, flags can be set to either ERL_NIF_DIRTY_JOB_CPU_BOUND if the job is expected to be primarily CPU-bound, or ERL_NIF_DIRTY_JOB_IO_BOUND for jobs that will - be I/O-bound.

+ be I/O-bound. If dirty scheduler threads are not available in the emulator, a try to schedule such a job + will result in a badarg exception.

+

The argc and argv arguments can either be the originals passed into the calling NIF, or they can be values created by the calling NIF.

The calling NIF must use the return value of enif_schedule_nif as its own return value.

diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 5ced8c5ca0..f9938fc66c 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -133,7 +133,7 @@ typedef struct { #define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed) #define ERL_DRV_EXTENDED_MAJOR_VERSION 3 -#define ERL_DRV_EXTENDED_MINOR_VERSION 0 +#define ERL_DRV_EXTENDED_MINOR_VERSION 1 /* * The emulator will refuse to load a driver with a major version diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h index 3f829ea7ea..4e8c6dc68b 100644 --- a/erts/emulator/beam/erl_drv_nif.h +++ b/erts/emulator/beam/erl_drv_nif.h @@ -35,6 +35,7 @@ typedef struct { int scheduler_threads; int nif_major_version; int nif_minor_version; + int dirty_scheduler_support; } ErlDrvSysInfo; typedef struct { diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 44914d3681..ede5f335dc 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1901,16 +1901,6 @@ enif_is_on_dirty_scheduler(ErlNifEnv* env) return ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data); } -int -enif_have_dirty_schedulers() -{ -#ifdef USE_THREADS - return 1; -#else - return 0; -#endif -} - #endif /* ERL_NIF_DIRTY_SCHEDULER_SUPPORT */ /* Maps */ diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index e99cd36153..630cefae93 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -169,7 +169,6 @@ ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int */ #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); -ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); #endif #endif /* ERL_NIF_API_FUNC_DECL */ @@ -318,7 +317,6 @@ ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); */ #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT # define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler) -# define enif_have_dirty_schedulers ERL_NIF_API_FUNC_MACRO(enif_have_dirty_schedulers) #endif #endif /* ERL_NIF_API_FUNC_MACRO */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index ae053fc191..9ae973e108 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -7274,6 +7274,18 @@ driver_system_info(ErlDrvSysInfo *sip, size_t si_size) sip->nif_major_version = ERL_NIF_MAJOR_VERSION; sip->nif_minor_version = ERL_NIF_MINOR_VERSION; } + /* + * 'dirty_scheduler_support' is the last field in the 4th version + * (driver version 3.1, NIF version 2.7) + */ + if (si_size >= ERL_DRV_SYS_INFO_SIZE(dirty_scheduler_support)) { +#if defined(ERL_NIF_DIRTY_SCHEDULER_SUPPORT) && defined(USE_THREADS) + sip->dirty_scheduler_support = 1; +#else + sip->dirty_scheduler_support = 0; +#endif + } + } diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 344bde7c91..336b6188f6 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -1084,7 +1084,15 @@ otp_6602(Config) when is_list(Config) -> ["async_thrs", "sched_thrs"])). --define(EXPECTED_SYSTEM_INFO_NAMES, ?EXPECTED_SYSTEM_INFO_NAMES2). +-define(EXPECTED_SYSTEM_INFO_NAMES3, + (?EXPECTED_SYSTEM_INFO_NAMES2 ++ + ["emu_nif_vsn"])). + +-define(EXPECTED_SYSTEM_INFO_NAMES4, + (?EXPECTED_SYSTEM_INFO_NAMES3 ++ + ["dirty_sched"])). + +-define(EXPECTED_SYSTEM_INFO_NAMES, ?EXPECTED_SYSTEM_INFO_NAMES4). 'driver_system_info_base_ver'(doc) -> []; @@ -1132,16 +1140,18 @@ check_driver_system_info_result(Result) -> drv_vsn_str2tup(erlang:system_info(driver_version))} of {DDVSN, DDVSN} -> ?line [] = Ns; - {{1, 0}, _} -> + %% {{1, 0}, _} -> + %% ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES + %% -- ?EXPECTED_SYSTEM_INFO_NAMES1), + %% ?line ExpNs = lists:sort(Ns); + %% {{1, 1}, _} -> + %% ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES + %% -- ?EXPECTED_SYSTEM_INFO_NAMES2), + %% ?line ExpNs = lists:sort(Ns); + {{3, 0}, _} -> ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES - -- ?EXPECTED_SYSTEM_INFO_NAMES1), - ?line ExpNs = lists:sort(Ns); - {{1, 1}, _} -> - ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES - -- ?EXPECTED_SYSTEM_INFO_NAMES2), - ?line ExpNs = lists:sort(Ns); - {{2, 0}, _} -> - ?line [] = Ns + -- ?EXPECTED_SYSTEM_INFO_NAMES3), + ?line ExpNs = lists:sort(Ns) end. chk_sis(SIs, Ns) -> @@ -1188,6 +1198,14 @@ check_si_res(["async_thrs", Value]) -> check_si_res(["sched_thrs", Value]) -> ?line Value = integer_to_list(erlang:system_info(schedulers)); +%% Data added in 3rd version of driver_system_info() (driver version 1.5) +check_si_res(["emu_nif_vsn", _Value]) -> + true; + +%% Data added in 4th version of driver_system_info() (driver version 3.1) +check_si_res(["dirty_sched", _Value]) -> + true; + check_si_res(Unexpected) -> ?line ?t:fail({unexpected_result, Unexpected}). diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c index e44c7dbd5e..964034f5a6 100644 --- a/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c +++ b/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c @@ -41,7 +41,9 @@ "thread=%s " \ "smp=%s " \ "async_thrs=%d " \ - "sched_thrs=%d" + "sched_thrs=%d " \ + "emu_nif_vsn=%d.%d" + static size_t sys_info_drv_max_res_len(ErlDrvSysInfo *sip) @@ -55,6 +57,7 @@ sys_info_drv_max_res_len(ErlDrvSysInfo *sip) slen += 5; /* smp */ slen += 20; /* async_thrs */ slen += 20; /* sched_thrs */ + slen += 2*20; /* emu_nif_vsn */ return slen; } @@ -72,7 +75,9 @@ sys_info_drv_sprintf_sys_info(ErlDrvSysInfo *sip, char *str) sip->thread_support ? "true" : "false", sip->smp_support ? "true" : "false", sip->async_threads, - sip->scheduler_threads); + sip->scheduler_threads, + sip->nif_major_version, + sip->nif_minor_version); } #include "sys_info_drv_impl.c" diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c index 5bbc966932..6d2c47fdaf 100644 --- a/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c +++ b/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c @@ -40,7 +40,9 @@ "thread=%s " \ "smp=%s " \ "async_thrs=%d " \ - "sched_thrs=%d" + "sched_thrs=%d " \ + "emu_nif_vsn=%d.%d " \ + "dirty_sched=%s" static size_t sys_info_drv_max_res_len(ErlDrvSysInfo *sip) @@ -54,6 +56,8 @@ sys_info_drv_max_res_len(ErlDrvSysInfo *sip) slen += 5; /* smp */ slen += 20; /* async_thrs */ slen += 20; /* sched_thrs */ + slen += 2*20; /* emu_nif_vsn */ + slen += 5; /* dirty_sched */ return slen; } @@ -71,7 +75,10 @@ sys_info_drv_sprintf_sys_info(ErlDrvSysInfo *sip, char *str) sip->thread_support ? "true" : "false", sip->smp_support ? "true" : "false", sip->async_threads, - sip->scheduler_threads); + sip->scheduler_threads, + sip->nif_major_version, + sip->nif_minor_version, + sip->dirty_scheduler_support ? "true" : "false"); } #include "sys_info_drv_impl.c" diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c index 63c69f751c..2271d7027b 100644 --- a/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c +++ b/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c @@ -41,7 +41,9 @@ "thread=%s " \ "smp=%s " \ "async_thrs=%d " \ - "sched_thrs=%d" + "sched_thrs=%d " \ + "emu_nif_vsn=%d.%d" + static size_t sys_info_drv_max_res_len(ErlDrvSysInfo *sip) @@ -55,6 +57,7 @@ sys_info_drv_max_res_len(ErlDrvSysInfo *sip) slen += 5; /* smp */ slen += 20; /* async_thrs */ slen += 20; /* sched_thrs */ + slen += 2*20; /* emu_nif_vsn */ return slen; } @@ -72,7 +75,9 @@ sys_info_drv_sprintf_sys_info(ErlDrvSysInfo *sip, char *str) sip->thread_support ? "true" : "false", sip->smp_support ? "true" : "false", sip->async_threads, - sip->scheduler_threads); + sip->scheduler_threads, + sip->nif_major_version, + sip->nif_minor_version); } #include "sys_info_drv_impl.c" diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index ff5fb8c5af..291c903947 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1539,13 +1539,21 @@ static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TE } #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT + +static int have_dirty_schedulers(void) +{ + ErlNifSysInfo si; + enif_system_info(&si, sizeof(si)); + return si.dirty_scheduler_support; +} + static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { int n; char s[10]; ErlNifBinary b; ERL_NIF_TERM result; - if (enif_have_dirty_schedulers()) { + if (have_dirty_schedulers()) { assert(enif_is_on_dirty_scheduler(env)); } assert(argc == 3); @@ -1566,7 +1574,7 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM assert(!enif_is_on_dirty_scheduler(env)); if (argc != 3) return enif_make_badarg(env); - if (enif_have_dirty_schedulers()) { + if (have_dirty_schedulers()) { if (enif_get_int(env, argv[0], &n) && enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1) && enif_inspect_binary(env, argv[2], &b)) -- cgit v1.2.3 From fcd39487c552cd18c49e23137e5c3808d38e3a0a Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Fri, 12 Sep 2014 15:41:43 +0200 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 56292 -> 56292 bytes erts/preloaded/ebin/erlang.beam | Bin 97900 -> 97908 bytes erts/preloaded/ebin/erts_internal.beam | Bin 4160 -> 4164 bytes erts/preloaded/ebin/init.beam | Bin 48800 -> 48808 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1464 -> 1468 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1332 -> 1340 bytes erts/preloaded/ebin/prim_file.beam | Bin 44900 -> 44904 bytes erts/preloaded/ebin/prim_inet.beam | Bin 73128 -> 73128 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23432 -> 23440 bytes erts/preloaded/ebin/zlib.beam | Bin 13180 -> 13188 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index a85f5598b6..001c9c76ed 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index cf3effc1e5..c5cf4b459f 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 7dc7407a81..b467633a4d 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 5c139c4550..915a2d1aef 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index cf32b79e8d..df1bf932a3 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 37ed8d0365..95b4bbca2d 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index d49578abfa..5e4fc5ba84 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 8420052533..9d9a4886d9 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 8dc8cb961b..d98b0275f4 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 7507efb076..0744bdb21d 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 2191e216a95d3cb41edd7ad2069e3b2d88b907e7 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 15 Sep 2014 12:00:20 +0200 Subject: Update version numbers --- erts/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index 0db4370ea8..b6a38f9361 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.1.2 +VSN = 6.2 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 950d808c97a4c5b579f4f1cc16b95f2d419d3505 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 15 Sep 2014 12:02:14 +0200 Subject: Update release notes --- erts/doc/src/notes.xml | 170 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 5c4bb3ed25..743369951f 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,176 @@

This document describes the changes made to the ERTS application.

+
Erts 6.2 + +
Fixed Bugs and Malfunctions + + +

+ General documentation updates.

+

+ Own Id: OTP-12052

+
+ +

A bug in the VM code implementing sending of signals + to ports could cause the receiving port queue to remain + in a busy state forever. When this state had been + reached, processes sending command signals to the port + either got suspended forever, or, if the nosuspend + feature was used, always failed to send to the port. This + bug was introduced in ERTS version 5.10.

+

In order for this bug to be triggered on a port, one + had to at least once utilize the nosuspend + functionality when passing a signal to the port. This by + either calling

port_command(Port, + Data, [nosuspend | Options]), + erlang:send(Port, {PortOwner, + {command, Data}}, [nosuspend | Options]), + erlang:send_nosuspend(Port, + {PortOwner, {command, Data}}), or + erlang:send_nosuspend(Port, + {PortOwner, {command, Data}}, Options). + +

Thanks Vasily Demidenok for reporting the issue, and + Sergey Kudryashov for providing a testcase.

+

+ Own Id: OTP-12082 Aux Id: OTP-10336

+
+ +

+ Fix size overflow bug at memory allocation. A memory + allocation call, with an insane size close to the entire + address space, could return successfully as if it had + allocated just a few bytes. (Thanks to Don A. Bailey for + reporting)

+

+ Own Id: OTP-12091

+
+ +

+ Fix various issues where negating a signed integer would + trigger undefined behaviour. This fixes issues in the + enif_make_int64 interface and some edge cases inside the + erlang runtime system.

+

+ Own Id: OTP-12097

+
+ +

+ The documentation erroneously listed the +swct command line + argument under +sws.

+

+ Own Id: OTP-12102 Aux Id: OTP-10994

+
+ +

+ Profiling messages could be delivered out of order when + profiling on runnable_procs and/or + runnable_ports using erlang:system_profile/2. + This bug was introduced in ERTS version 5.10.

+

+ Own Id: OTP-12105 Aux Id: OTP-10336

+
+ +

+ Various logging fixes, including: Add run queue index to + the process dump in crash dumps.
Add thread index to + enomem slogan when crashing.
Remove error logger + message for sending messages to old instances of the same + node.

+

+ Own Id: OTP-12115

+
+ +

+ Fix compiler warnings reported by LLVM

+

+ Own Id: OTP-12138

+
+ +

+ Correct conversion of MIN_SMALL by + list_to_integer/1 and binary_to_integer/1. + The bug produced an unnormalized bignum which can cause + strange behavior such as comparing different to a correct + MIN_SMALL integer. The value MIN_SMALL is + -(1 bsl 27) = -134217728 on a 32-bit VM and -(1 + bsl 59) = -576460752303423488 on a 64-bit VM. (Thanks + to Jesper Louis Andersen, Mikael Pettersson and Anthony + Ramine for report, patch and optimization suggestion)

+

+ Own Id: OTP-12140

+
+ +

+ Fix bug in term_to_binary that reallocates binary + with inconsistent size information. Bug has never been + confirmed to be the cause of any faulty behavior.

+

+ Own Id: OTP-12141

+
+ +

+ Real_path method used while prim loading archive files + was not taking into account the fact that windows + directory symlinks can be across different drives.

+

+ Own Id: OTP-12155

+
+
+
+ + +
Improvements and New Features + + +

+ Add log2 histogram to lcnt for lock wait time

+

+ Own Id: OTP-12059

+
+ +

+ Introduced enif_schedule_nif() + to the NIF API.

+

+ The enif_schedule_nif() function allows a + long-running NIF to be broken into separate NIF + invocations without the help of a wrapper function + written in Erlang. The NIF first executes part of the + long-running task, then calls enif_schedule_nif() + to schedule a NIF for later execution to continue the + task. Any number of NIFs can be scheduled in this manner, + one after another. Since the emulator regains control + between invocations, this helps avoid problems caused by + native code tying up scheduler threads for too long.

+

+ The enif_schedule_nif() function also replaces the + enif_schedule_dirty_nif() in the experimental + dirty NIF API. Note that the only incompatible changes + made are in the experimental dirty NIF API.

+

+ See the NIF + documentation for more information.

+

+ Thanks to Steve Vinoski.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12128

+
+
+
+ +
+
Erts 6.1.2
Fixed Bugs and Malfunctions -- cgit v1.2.3 From 6ace9a5b381fcd6e6efb4d45edeb70f566e24f93 Mon Sep 17 00:00:00 2001 From: Chris Dituri Date: Fri, 15 Aug 2014 22:38:26 -0500 Subject: epmd: pedantic spelling fix in comments Correct spelling errors in the run_daemon() comments which describe the redirection of stdin, stdout, and stderr to /dev/null for safety reasons. --- erts/epmd/src/epmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 9630e0cdf0..0823dcaa4e 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -345,7 +345,7 @@ static void run_daemon(EpmdVars *g) inform it of that the log is closed. */ closelog(); - /* These chouldn't be needed but for safety... */ + /* These shouldn't be needed but for safety... */ open("/dev/null", O_RDONLY); /* Order is important! */ open("/dev/null", O_WRONLY); @@ -386,7 +386,7 @@ static void run_daemon(EpmdVars *g) close(1); close(2); - /* These chouldn't be needed but for safety... */ + /* These shouldn't be needed but for safety... */ open("nul", O_RDONLY); open("nul", O_WRONLY); -- cgit v1.2.3 From bee90407de17bfe6e32439321b4e99effb25384d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Sep 2014 15:24:17 +0200 Subject: erts: Fix erts app-file --- erts/preloaded/src/erts.app.src | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index a15da3a421..345a6ae3be 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -35,7 +35,6 @@ {registered, []}, {applications, []}, {env, []}, - {mod, {erts, []}}, {runtime_dependencies, ["stdlib-2.0", "kernel-3.0", "sasl-2.4"]} ]}. -- cgit v1.2.3 From 9c9878d6d4e99aba195177baef66067da0a2b797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Sep 2014 15:22:15 +0200 Subject: erts: Add icount build type for opcode counter Enables ERTS_OPCODE_COUNTER_SUPPORT. --- erts/emulator/Makefile.in | 11 +++++++++-- erts/emulator/beam/erl_bif_info.c | 5 ++++- erts/etc/unix/cerl.src | 6 ++++++ erts/lib_src/Makefile.in | 6 ++++++ 4 files changed, 25 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 7145824f91..53fc7bd713 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -112,18 +112,24 @@ NO_INLINE_FUNCTIONS=true else ifeq ($(TYPE),lcnt) -PURIFY = +PURIFY = TYPEMARKER = .lcnt TYPE_FLAGS = @CFLAGS@ -DERTS_ENABLE_LOCK_COUNT else ifeq ($(TYPE),frmptr) -PURIFY = +PURIFY = OMIT_OMIT_FP=yes TYPEMARKER = .frmptr TYPE_FLAGS = @CFLAGS@ -DERTS_FRMPTR else +ifeq ($(TYPE),icount) +PURIFY = +TYPEMARKER = .icount +TYPE_FLAGS = @CFLAGS@ -DERTS_OPCODE_COUNTER_SUPPORT +else + # If type isn't one of the above, it *is* opt type... override TYPE=opt PURIFY = @@ -138,6 +144,7 @@ endif endif endif endif +endif comma:=, space:= diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 6efe9d9550..e3fa9e9cb7 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -115,6 +115,9 @@ static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE #ifdef ERTS_ENABLE_LOCK_COUNT " [lock-counting]" #endif +#ifdef ERTS_OPCODE_COUNTER_SUPPORT + " [instruction-counting]" +#endif #ifdef PURIFY " [purify-compiled]" #endif @@ -2300,7 +2303,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) for (i = num_instructions-1; i >= 0; i--) { res = erts_bld_cons(hpp, hszp, erts_bld_tuple(hpp, hszp, 2, - erts_atom_put(opc[i].name, + erts_atom_put((byte *)opc[i].name, strlen(opc[i].name), ERTS_ATOM_ENC_LATIN1, 1), diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 78fefbea55..aa51eabfc5 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -43,6 +43,7 @@ # -gcov Run emulator compiled for gcov # -valgrind Run emulator compiled for valgrind # -lcnt Run emulator compiled for lock counting +# -icount Run emulator compiled for instruction counting # -nox Unset the DISPLAY variable to disable us of X Windows # # FIXME For GDB you can also set the break point using "-break FUNCTION". @@ -180,6 +181,11 @@ while [ $# -gt 0 ]; do cargs="$cargs -frmptr" TYPE=.frmptr ;; + "-icount") + shift + cargs="$cargs -icount" + TYPE=.icount + ;; "-dump") shift GDB=dump diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in index b680c03b1d..d0ebab49d8 100644 --- a/erts/lib_src/Makefile.in +++ b/erts/lib_src/Makefile.in @@ -92,6 +92,11 @@ CFLAGS += -DERTS_FRMPTR OMIT_OMIT_FP=yes PRE_LD= else +ifeq ($(TYPE),icount) +TYPE_SUFFIX = .icount +CFLAGS += -DERTS_OPCODE_COUNTER_SUPPORT +PRE_LD= +else override TYPE=opt OMIT_FP=true TYPE_SUFFIX= @@ -105,6 +110,7 @@ endif endif endif endif +endif OPSYS=@OPSYS@ sol2CFLAGS= -- cgit v1.2.3 From f175f46b74efc2419fab09cfe4c09d741bcebf67 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 17 Sep 2014 17:25:37 +0200 Subject: erts: Add gdb command etp-address-to-beam-opcode --- erts/etc/unix/etp-commands.in | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index bf6eb00314..e22771c582 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -3552,6 +3552,39 @@ document etp-carrier-blocks %--------------------------------------------------------------------------- end +define etp-address-to-beam-opcode + set $etp_i = 0 + set $etp_min_diff = ((UWord)1 << (sizeof(UWord)*8 - 1)) + set $etp_min_opcode = -1 + set $etp_addr = (UWord) ($arg0) + + while $etp_i < num_instructions && $etp_min_diff > 0 + if ($etp_addr - (UWord)beam_ops[$etp_i]) < $etp_min_diff + set $etp_min_diff = $etp_addr - (UWord)beam_ops[$etp_i] + set $etp_min_opcode = $etp_i + end + set $etp_i = $etp_i + 1 + end + if $etp_min_diff == 0 + printf "Address %p is start of '%s'\n", $etp_addr, opc[$etp_min_opcode].name + else + if $etp_min_opcode >= 0 + printf "Address is %ld bytes into opcode '%s' at %p\n", $etp_min_diff, opc[$etp_min_opcode].name, beam_ops[$etp_min_opcode] + else + printf "Invalid opcode address\n" + end + end +end + +document etp-address-to-beam-opcode +%--------------------------------------------------------------------------- +% Get beam opcode from a native instruction address (within process_main()) +% Arg: Instructon pointer value +% +% Does not work with NO_JUMP_TABLE +%--------------------------------------------------------------------------- +end + ############################################################################ # Toolbox parameter handling -- cgit v1.2.3 From b4f191993631630ee14cd48e0e792bb3dee1ed5c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 22 Sep 2014 11:11:40 +0200 Subject: erts: Initialize links when reading file info --- erts/emulator/drivers/win32/win_efile.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index a321bb9641..7e4043fc1b 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -1288,6 +1288,10 @@ do_fileinfo(Efile_call_state* state, Efile_info* pInfo, { HANDLE handle; /* Handle returned by CreateFile() */ BY_HANDLE_FILE_INFORMATION fileInfo; /* from CreateFile() */ + + /* We initialise nNumberOfLinks as GetFileInformationByHandle + does not always initialise this field */ + fileInfo.nNumberOfLinks = 1; if (handle = CreateFileW(name, GENERIC_READ, FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, 0, NULL)) { GetFileInformationByHandle(handle, &fileInfo); -- cgit v1.2.3 From 16d8a6ce56fd066efa9ecb1d3fa3b3dab6d9613c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 22 Sep 2014 12:00:05 +0200 Subject: erts: Fix ub in list_to_integer and bignum div --- erts/emulator/beam/bif.c | 8 +++++--- erts/emulator/beam/big.c | 7 +++---- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index a5be8e1529..42dd160e38 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2772,6 +2772,7 @@ static int do_list_to_integer(Process *p, Eterm orig_list, Eterm *integer, Eterm *rest) { Sint i = 0; + Uint ui = 0; int skip = 0; int neg = 0; int n = 0; @@ -2825,8 +2826,8 @@ static int do_list_to_integer(Process *p, Eterm orig_list, unsigned_val(CAR(list_val(lst))) > '9') { break; } - i = i * 10; - i = i + unsigned_val(CAR(list_val(lst))) - '0'; + ui = ui * 10; + ui = ui + unsigned_val(CAR(list_val(lst))) - '0'; n++; lst = CDR(list_val(lst)); if (is_nil(lst)) { @@ -2850,7 +2851,8 @@ static int do_list_to_integer(Process *p, Eterm orig_list, */ if (n <= SMALL_DIGITS) { /* It must be small */ - if (neg) i = -i; + if (neg) i = -(Sint)ui; + else i = (Sint)ui; res = make_small(i); } else { lg2 = (n+1)*230/69+1; diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index a8710dd910..de7d370938 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -274,10 +274,9 @@ _b = _b << _s; \ _vn1 = _b >> H_EXP; \ _vn0 = _b & LO_MASK; \ - /* Sometimes _s is 0 which triggers undefined behaviour for the \ - (_a0>>(D_EXP-_s)) shift, but this is ok because the \ - & -s will make it all to 0 later anyways. */ \ - _un32 = (_a1 << _s) | ((_a0>>(D_EXP-_s)) & (-_s >> (D_EXP-1))); \ + /* If needed to avoid undefined behaviour */ \ + if (_s) _un32 = (_a1 << _s) | ((_a0>>(D_EXP-_s)) & (-_s >> (D_EXP-1))); \ + else _un32 = _a1; \ _un10 = _a0 << _s; \ _un1 = _un10 >> H_EXP; \ _un0 = _un10 & LO_MASK; \ -- cgit v1.2.3 From 6084a42a24fca52a5de2bc487c0cd2be46dcc21f Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 26 Aug 2014 17:26:31 +0200 Subject: Introduce support for eager check I/O scheduling --- erts/doc/src/erl.xml | 17 + erts/doc/src/erlang.xml | 11 + erts/emulator/beam/erl_alloc.types | 1 + erts/emulator/beam/erl_bif_info.c | 38 +- erts/emulator/beam/erl_init.c | 15 + erts/emulator/beam/erl_port_task.c | 24 +- erts/emulator/beam/erl_port_task.h | 2 +- erts/emulator/beam/erl_process.c | 38 +- erts/emulator/beam/erl_process.h | 1 + erts/emulator/beam/sys.h | 12 + erts/emulator/beam/utils.c | 1 + erts/emulator/sys/common/erl_check_io.c | 694 +++++++++++++++++++++---- erts/emulator/sys/common/erl_check_io.h | 45 +- erts/emulator/sys/common/erl_sys_common_misc.c | 8 + erts/emulator/sys/unix/erl_unix_sys.h | 3 - erts/emulator/sys/unix/sys.c | 6 +- erts/emulator/sys/win32/erl_poll.c | 2 +- erts/emulator/sys/win32/erl_win_sys.h | 4 +- erts/emulator/test/a_SUITE.erl | 14 +- erts/emulator/test/driver_SUITE.erl | 62 ++- erts/emulator/test/z_SUITE.erl | 28 +- erts/etc/common/erlexec.c | 1 + erts/preloaded/ebin/erlang.beam | Bin 94264 -> 94280 bytes erts/preloaded/src/erlang.erl | 1 + 24 files changed, 850 insertions(+), 178 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 528a2d95aa..967226056e 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1065,6 +1065,23 @@

For more information, see erlang:system_info(cpu_topology).

+ +secio true|false + +

Enable or disable eager check I/O scheduling. The default + is currently false, but will most likely be changed + to true in OTP 18. The behaviour before this flag + was introduced corresponds to +secio false.

+

The flag effects when schedulers will check for I/O + operations possible to execute, and when such I/O operations + will execute. As the name of the parameter implies, + schedulers will be more eager to check for I/O when + true is passed. This however also implies that + execution of outstanding I/O operation will not be + prioritized to the same extent as when false is + passed.

+

erlang:system_info(eager_check_io) + returns the value of this parameter used when starting the VM.

+
+sfwi Interval

Set scheduler forced wakeup interval. All run queues will diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index e3ef48a6c1..e02e4cbbc9 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5611,6 +5611,7 @@ ok + Information about the system

Returns various information about the current system @@ -5740,6 +5741,16 @@ ok The return value will always be false since the elib_malloc allocator has been removed.

+ eager_check_io + +

+ Returns the value of the erl + +secio command line + flag which is either true or false. See the + documentation of the command line flag for information about + the different values. +

+
ets_limit

Returns the maximum number of ETS tables allowed. This limit diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index bb5eba80be..0bbd3e2dd5 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -393,6 +393,7 @@ type DRV_EV_STATE LONG_LIVED SYSTEM driver_event_state type DRV_EV_D_STATE FIXED_SIZE SYSTEM driver_event_data_state type DRV_SEL_D_STATE FIXED_SIZE SYSTEM driver_select_data_state type FD_LIST SHORT_LIVED SYSTEM fd_list +type ACTIVE_FD_ARR SHORT_LIVED SYSTEM active_fd_array type POLLSET LONG_LIVED SYSTEM pollset type POLLSET_UPDREQ SHORT_LIVED SYSTEM pollset_update_req type POLL_FDS LONG_LIVED SYSTEM poll_fds diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index d7f1e2d971..8f4095f236 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2643,6 +2643,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) else if (ERTS_IS_ATOM_STR("ets_limit",BIF_ARG_1)) { BIF_RET(make_small(erts_db_get_max_tabs())); } + else if (ERTS_IS_ATOM_STR("eager_check_io",BIF_ARG_1)) { + BIF_RET(erts_eager_check_io ? am_true : am_false); + } BIF_ERROR(BIF_P, BADARG); } @@ -3232,17 +3235,38 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) BIF_RET(make_small((Uint) words)); } else if (ERTS_IS_ATOM_STR("check_io_debug", BIF_ARG_1)) { - /* Used by (emulator) */ - int res; + /* Used by driver_SUITE (emulator) */ + Uint sz, *szp; + Eterm res, *hp, **hpp; + int no_errors; + ErtsCheckIoDebugInfo ciodi = {0}; #ifdef HAVE_ERTS_CHECK_IO_DEBUG erts_smp_proc_unlock(BIF_P,ERTS_PROC_LOCK_MAIN); - res = erts_check_io_debug(); + no_errors = erts_check_io_debug(&ciodi); erts_smp_proc_lock(BIF_P,ERTS_PROC_LOCK_MAIN); #else - res = 0; -#endif - ASSERT(res >= 0); - BIF_RET(erts_make_integer((Uint) res, BIF_P)); + no_errors = 0; +#endif + sz = 0; + szp = &sz; + hpp = NULL; + while (1) { + res = erts_bld_tuple(hpp, szp, 4, + erts_bld_uint(hpp, szp, + (Uint) no_errors), + erts_bld_uint(hpp, szp, + (Uint) ciodi.no_used_fds), + erts_bld_uint(hpp, szp, + (Uint) ciodi.no_driver_select_structs), + erts_bld_uint(hpp, szp, + (Uint) ciodi.no_driver_event_structs)); + if (hpp) + break; + hp = HAlloc(BIF_P, sz); + szp = NULL; + hpp = &hp; + } + BIF_RET(res); } else if (ERTS_IS_ATOM_STR("process_info_args", BIF_ARG_1)) { /* Used by process_SUITE (emulator) */ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 8c4fffa75b..c3be23c84a 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -537,6 +537,8 @@ void erts_usage(void) erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); erts_fprintf(stderr, "-sct cput set cpu topology,\n"); erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); + erts_fprintf(stderr, "-secio bool enable/disable eager check I/O scheduling,\n"); + erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); erts_fprintf(stderr, "-sws val set scheduler wakeup strategy, valid values are:\n"); erts_fprintf(stderr, " default|legacy.\n"); erts_fprintf(stderr, "-swct val set scheduler wake cleanup threshold, valid values are:\n"); @@ -1487,6 +1489,19 @@ erl_start(int argc, char **argv) erts_usage(); } } + else if (has_prefix("ecio", sub_param)) { + arg = get_arg(sub_param+4, argv[i+1], &i); + if (sys_strcmp("true", arg) == 0) + erts_eager_check_io = 1; + else if (sys_strcmp("false", arg) == 0) + erts_eager_check_io = 0; + else { + erts_fprintf(stderr, + "bad schedule eager check I/O value '%s'\n", + arg); + erts_usage(); + } + } else if (has_prefix("pp", sub_param)) { arg = get_arg(sub_param+2, argv[i+1], &i); if (sys_strcmp(arg, "true") == 0) diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 547a42beb2..ff4fdc70aa 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -32,6 +32,7 @@ #include "global.h" #include "erl_port_task.h" #include "dist.h" +#include "erl_check_io.h" #include "dtrace-wrapper.h" #include @@ -542,6 +543,16 @@ reset_handle(ErtsPortTask *ptp) } } +static ERTS_INLINE void +reset_executed_io_task_handle(ErtsPortTask *ptp) +{ + if (ptp->u.alive.handle) { + ASSERT(ptp == handle2task(ptp->u.alive.handle)); + erts_io_notify_port_task_executed(ptp->u.alive.handle); + reset_port_task_handle(ptp->u.alive.handle); + } +} + static ERTS_INLINE void set_handle(ErtsPortTask *ptp, ErtsPortTaskHandle *pthp) { @@ -1365,10 +1376,7 @@ erts_port_task_schedule(Eterm id, ErtsPortTask *ptp = NULL; erts_aint32_t act, add_flags; - if (pthp && erts_port_task_is_scheduled(pthp)) { - ASSERT(0); - erts_port_task_abort(pthp); - } + ERTS_LC_ASSERT(!pthp || !erts_port_task_is_scheduled(pthp)); ASSERT(is_internal_port(id)); @@ -1654,8 +1662,6 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) goto aborted_port_task; } - reset_handle(ptp); - if (erts_system_monitor_long_schedule != 0) { start_time = erts_timestamp_millis(); } @@ -1666,6 +1672,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) switch (ptp->type) { case ERTS_PORT_TASK_TIMEOUT: + reset_handle(ptp); reds = ERTS_PORT_REDS_TIMEOUT; if (!(state & ERTS_PORT_SFLGS_DEAD)) { DTRACE_DRIVER(driver_timeout, pp); @@ -1679,6 +1686,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) /* NOTE some windows drivers use ->ready_input for input and output */ (*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data, ptp->u.alive.td.io.event); + reset_executed_io_task_handle(ptp); io_tasks_executed++; break; case ERTS_PORT_TASK_OUTPUT: @@ -1687,6 +1695,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) DTRACE_DRIVER(driver_ready_output, pp); (*pp->drv_ptr->ready_output)((ErlDrvData) pp->drv_data, ptp->u.alive.td.io.event); + reset_executed_io_task_handle(ptp); io_tasks_executed++; break; case ERTS_PORT_TASK_EVENT: @@ -1696,10 +1705,12 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) (*pp->drv_ptr->event)((ErlDrvData) pp->drv_data, ptp->u.alive.td.io.event, ptp->u.alive.td.io.event_data); + reset_executed_io_task_handle(ptp); io_tasks_executed++; break; case ERTS_PORT_TASK_PROC_SIG: { ErtsProc2PortSigData *sigdp = &ptp->u.alive.td.psig.data; + reset_handle(ptp); ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0); if (!pp->sched.taskq.bpq) reds = ptp->u.alive.td.psig.callback(pp, @@ -1717,6 +1728,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) break; } case ERTS_PORT_TASK_DIST_CMD: + reset_handle(ptp); reds = erts_dist_command(pp, CONTEXT_REDS - pp->reds); break; default: diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h index 123253a057..e1e06b565e 100644 --- a/erts/emulator/beam/erl_port_task.h +++ b/erts/emulator/beam/erl_port_task.h @@ -154,7 +154,7 @@ erts_port_task_handle_init(ErtsPortTaskHandle *pthp) ERTS_GLB_INLINE int erts_port_task_is_scheduled(ErtsPortTaskHandle *pthp) { - return ((void *) erts_smp_atomic_read_nob(pthp)) != NULL; + return ((void *) erts_smp_atomic_read_acqb(pthp)) != NULL; } ERTS_GLB_INLINE void erts_port_task_pre_init_sched(ErtsPortTaskSched *ptsp, diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 1efd070afd..4f3ea92cf5 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -143,6 +143,7 @@ extern BeamInstr beam_apply[]; extern BeamInstr beam_exit[]; extern BeamInstr beam_continue_exit[]; +int erts_eager_check_io = 0; int erts_sched_compact_load; Uint erts_no_schedulers; @@ -1977,19 +1978,28 @@ try_set_sys_scheduling(void) #endif static ERTS_INLINE int -prepare_for_sys_schedule(void) +prepare_for_sys_schedule(int non_blocking) { + if (non_blocking && erts_eager_check_io) { #ifdef ERTS_SMP - while (!erts_port_task_have_outstanding_io_tasks() - && try_set_sys_scheduling()) { - if (!erts_port_task_have_outstanding_io_tasks()) - return 1; - clear_sys_scheduling(); + return try_set_sys_scheduling(); +#else + return 1; +#endif } - return 0; + else { +#ifdef ERTS_SMP + while (!erts_port_task_have_outstanding_io_tasks() + && try_set_sys_scheduling()) { + if (!erts_port_task_have_outstanding_io_tasks()) + return 1; + clear_sys_scheduling(); + } + return 0; #else - return !erts_port_task_have_outstanding_io_tasks(); + return !erts_port_task_have_outstanding_io_tasks(); #endif + } } #ifdef ERTS_SMP @@ -2307,7 +2317,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * be waiting in erl_sys_schedule() */ - if (!prepare_for_sys_schedule()) { + if (!prepare_for_sys_schedule(0)) { sched_waiting(esdp->no, rq); @@ -2459,7 +2469,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * Got to check that we still got I/O tasks; otherwise * we have to continue checking for I/O... */ - if (!prepare_for_sys_schedule()) { + if (!prepare_for_sys_schedule(0)) { spincount *= ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT; goto tse_wait; } @@ -2481,7 +2491,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * Got to check that we still got I/O tasks; otherwise * we have to wait in erl_sys_schedule() after all... */ - if (!prepare_for_sys_schedule()) { + if (!prepare_for_sys_schedule(0)) { /* * Not allowed to wait in erl_sys_schedule; * do tse wait instead... @@ -7022,7 +7032,7 @@ Process *schedule(Process *p, int calls) goto check_activities_to_run; } - else if (fcalls > input_reductions && prepare_for_sys_schedule()) { + else if (fcalls > input_reductions && prepare_for_sys_schedule(!0)) { /* * Schedule system-level activities. */ @@ -7030,8 +7040,6 @@ Process *schedule(Process *p, int calls) erts_smp_atomic32_set_relb(&function_calls, 0); fcalls = 0; - ASSERT(!erts_port_task_have_outstanding_io_tasks()); - #if 0 /* Not needed since we wont wait in sys schedule */ erts_sys_schedule_interrupt(0); #endif @@ -7063,7 +7071,7 @@ Process *schedule(Process *p, int calls) 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) + if ((!erts_eager_check_io && have_outstanding_io && fcalls > 2*input_reductions) || rq->halt_in_progress) { /* * If we have performed more than 2*INPUT_REDUCTIONS since diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 8d136f6e8b..41936c1bb0 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -97,6 +97,7 @@ struct saved_calls { }; extern Export exp_send, exp_receive, exp_timeout; +extern int erts_eager_check_io; extern int erts_sched_compact_load; extern Uint erts_no_schedulers; extern Uint erts_no_run_queues; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 31252ed78f..e691d5c55c 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -64,8 +64,12 @@ */ #ifndef ERTS_SYS_FD_TYPE +#define ERTS_SYS_FD_INVALID ((ErtsSysFdType) -1) typedef int ErtsSysFdType; #else +#ifndef ERTS_SYS_FD_INVALID +# error missing ERTS_SYS_FD_INVALID +#endif typedef ERTS_SYS_FD_TYPE ErtsSysFdType; #endif @@ -731,6 +735,14 @@ void init_getenv_state(GETENV_STATE *); char * getenv_string(GETENV_STATE *); void fini_getenv_state(GETENV_STATE *); +#define HAVE_ERTS_CHECK_IO_DEBUG +typedef struct { + int no_used_fds; + int no_driver_select_structs; + int no_driver_event_structs; +} ErtsCheckIoDebugInfo; +int erts_check_io_debug(ErtsCheckIoDebugInfo *ip); + /* xxxP */ #define SYS_DEFAULT_FLOAT_DECIMALS 20 void init_sys_float(void); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 0d75bbcc77..426e43d5d8 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -47,6 +47,7 @@ #include "erl_sched_spec_pre_alloc.h" #include "beam_bp.h" #include "erl_ptab.h" +#include "erl_check_io.h" #undef M_TRIM_THRESHOLD #undef M_TOP_PAD diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 7035dc77df..e62f34b679 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -51,8 +51,17 @@ typedef char EventStateType; #define ERTS_EV_TYPE_STOP_USE ((EventStateType) 3) /* pending stop_select */ typedef char EventStateFlags; -#define ERTS_EV_FLAG_USED ((EventStateFlags) 1) /* ERL_DRV_USE has been turned on */ +#define ERTS_EV_FLAG_USED ((EventStateFlags) 1) /* ERL_DRV_USE has been turned on */ +#define ERTS_EV_FLAG_DEFER_IN_EV ((EventStateFlags) 2) +#define ERTS_EV_FLAG_DEFER_OUT_EV ((EventStateFlags) 4) +#ifdef DEBUG +# define ERTS_ACTIVE_FD_INC 2 +#else +# define ERTS_ACTIVE_FD_INC 128 +#endif + +#define ERTS_CHECK_IO_POLL_RES_LEN 512 #if defined(ERTS_KERNEL_POLL_VERSION) # define ERTS_CIO_EXPORT(FUNC) FUNC ## _kp @@ -66,6 +75,7 @@ typedef char EventStateFlags; (ERTS_POLL_USE_POLL && !ERTS_POLL_USE_KERNEL_POLL) #define ERTS_CIO_POLL_CTL ERTS_POLL_EXPORT(erts_poll_control) +#define ERTS_CIO_POLL_CTLV ERTS_POLL_EXPORT(erts_poll_controlv) #define ERTS_CIO_POLL_WAIT ERTS_POLL_EXPORT(erts_poll_wait) #ifdef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT #define ERTS_CIO_POLL_AS_INTR ERTS_POLL_EXPORT(erts_poll_async_sig_interrupt) @@ -82,6 +92,13 @@ static struct pollset_info { ErtsPollSet ps; erts_smp_atomic_t in_poll_wait; /* set while doing poll */ + struct { + int six; /* start index */ + int eix; /* end index */ + erts_smp_atomic32_t no; + int size; + ErtsSysFdType *array; + } active_fd; #ifdef ERTS_SMP struct removed_fd* removed_list; /* list of deselected fd's*/ erts_smp_spinlock_t removed_list_lock; @@ -94,9 +111,11 @@ typedef struct { SafeHashBucket hb; #endif ErtsSysFdType fd; - union { - ErtsDrvEventDataState *event; /* ERTS_EV_TYPE_DRV_EV */ + struct { ErtsDrvSelectDataState *select; /* ERTS_EV_TYPE_DRV_SEL */ +#if ERTS_CIO_HAVE_DRV_EVENT + ErtsDrvEventDataState *event; /* ERTS_EV_TYPE_DRV_EV */ +#endif erts_driver_t* drv_ptr; /* ERTS_EV_TYPE_STOP_USE */ } driver; ErtsPollEvents events; @@ -166,6 +185,10 @@ static ERTS_INLINE ErtsDrvEventState* hash_new_drv_ev_state(ErtsSysFdType fd) ErtsDrvEventState tmpl; tmpl.fd = fd; tmpl.driver.select = NULL; +#if ERTS_CIO_HAVE_DRV_EVENT + tmpl.driver.event = NULL; +#endif + tmpl.driver.drv_ptr = NULL; tmpl.events = 0; tmpl.remove_cnt = 0; tmpl.type = ERTS_EV_TYPE_NONE; @@ -205,6 +228,65 @@ static void steal_pending_stop_select(erts_dsprintf_buf_t*, ErlDrvPort, ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(removed_fd, struct removed_fd, 64, ERTS_ALC_T_FD_LIST) #endif +static ERTS_INLINE void +init_iotask(ErtsIoTask *io_task) +{ + erts_port_task_handle_init(&io_task->task); + erts_smp_atomic_init_nob(&io_task->executed_time, ~((erts_aint_t) 0)); +} + +static ERTS_INLINE int +is_iotask_active(ErtsIoTask *io_task, erts_aint_t current_cio_time) +{ + if (erts_port_task_is_scheduled(&io_task->task)) + return 1; + if (erts_smp_atomic_read_nob(&io_task->executed_time) == current_cio_time) + return 1; + return 0; +} + +static ERTS_INLINE ErtsDrvSelectDataState * +alloc_drv_select_data(void) +{ + ErtsDrvSelectDataState *dsp = erts_alloc(ERTS_ALC_T_DRV_SEL_D_STATE, + sizeof(ErtsDrvSelectDataState)); + dsp->inport = NIL; + dsp->outport = NIL; + init_iotask(&dsp->iniotask); + init_iotask(&dsp->outiotask); + return dsp; +} + +static ERTS_INLINE void +free_drv_select_data(ErtsDrvSelectDataState *dsp) +{ + ASSERT(!erts_port_task_is_scheduled(&dsp->iniotask.task)); + ASSERT(!erts_port_task_is_scheduled(&dsp->outiotask.task)); + erts_free(ERTS_ALC_T_DRV_SEL_D_STATE, dsp); +} + +static ERTS_INLINE ErtsDrvEventDataState * +alloc_drv_event_data(void) +{ + ErtsDrvEventDataState *dep = erts_alloc(ERTS_ALC_T_DRV_EV_D_STATE, + sizeof(ErtsDrvEventDataState)); + dep->port = NIL; + dep->data = NULL; + dep->removed_events = 0; +#if ERTS_CIO_DEFER_ACTIVE_EVENTS + dep->deferred_events = 0; +#endif + init_iotask(&dep->iotask); + return dep; +} + +static ERTS_INLINE void +free_drv_event_data(ErtsDrvEventDataState *dep) +{ + ASSERT(!erts_port_task_is_scheduled(&dep->iotask.task)); + erts_free(ERTS_ALC_T_DRV_EV_D_STATE, dep); +} + static ERTS_INLINE void remember_removed(ErtsDrvEventState *state, struct pollset_info* psi) { @@ -285,7 +367,7 @@ forget_removed(struct pollset_info* psi) drv_ptr = state->driver.drv_ptr; ASSERT(drv_ptr); state->type = ERTS_EV_TYPE_NONE; - state->flags = 0; + state->flags &= ~ERTS_EV_FLAG_USED; state->driver.drv_ptr = NULL; /* Fall through */ case ERTS_EV_TYPE_NONE: @@ -342,6 +424,10 @@ grow_drv_ev_state(int min_ix) for (i = erts_smp_atomic_read_nob(&drv_ev_state_len); i < new_len; i++) { drv_ev_state[i].fd = (ErtsSysFdType) i; drv_ev_state[i].driver.select = NULL; +#if ERTS_CIO_HAVE_DRV_EVENT + drv_ev_state[i].driver.event = NULL; +#endif + drv_ev_state[i].driver.drv_ptr = NULL; drv_ev_state[i].events = 0; drv_ev_state[i].remove_cnt = 0; drv_ev_state[i].type = ERTS_EV_TYPE_NONE; @@ -362,11 +448,7 @@ grow_drv_ev_state(int min_ix) static ERTS_INLINE void abort_task(Eterm id, ErtsPortTaskHandle *pthp, EventStateType type) { - if (is_nil(id)) { - ASSERT(type == ERTS_EV_TYPE_NONE - || !erts_port_task_is_scheduled(pthp)); - } - else if (erts_port_task_is_scheduled(pthp)) { + if (is_not_nil(id) && erts_port_task_is_scheduled(pthp)) { erts_port_task_abort(pthp); ASSERT(erts_is_port_alive(id)); } @@ -381,7 +463,7 @@ abort_tasks(ErtsDrvEventState *state, int mode) #if ERTS_CIO_HAVE_DRV_EVENT case ERTS_EV_TYPE_DRV_EV: abort_task(state->driver.event->port, - &state->driver.event->task, + &state->driver.event->iotask.task, ERTS_EV_TYPE_DRV_EV); return; #endif @@ -395,14 +477,14 @@ abort_tasks(ErtsDrvEventState *state, int mode) case ERL_DRV_WRITE: ASSERT(state->type == ERTS_EV_TYPE_DRV_SEL); abort_task(state->driver.select->outport, - &state->driver.select->outtask, + &state->driver.select->outiotask.task, state->type); if (mode == ERL_DRV_WRITE) break; case ERL_DRV_READ: ASSERT(state->type == ERTS_EV_TYPE_DRV_SEL); abort_task(state->driver.select->inport, - &state->driver.select->intask, + &state->driver.select->iniotask.task, state->type); break; default: @@ -440,16 +522,14 @@ deselect(ErtsDrvEventState *state, int mode) if (!(state->events)) { switch (state->type) { case ERTS_EV_TYPE_DRV_SEL: - ASSERT(!erts_port_task_is_scheduled(&state->driver.select->intask)); - ASSERT(!erts_port_task_is_scheduled(&state->driver.select->outtask)); - erts_free(ERTS_ALC_T_DRV_SEL_D_STATE, - state->driver.select); + state->driver.select->inport = NIL; + state->driver.select->outport = NIL; break; #if ERTS_CIO_HAVE_DRV_EVENT case ERTS_EV_TYPE_DRV_EV: - ASSERT(!erts_port_task_is_scheduled(&state->driver.event->task)); - erts_free(ERTS_ALC_T_DRV_EV_D_STATE, - state->driver.event); + state->driver.event->port = NIL; + state->driver.event->data = NULL; + state->driver.event->removed_events = (ErtsPollEvents) 0; break; #endif case ERTS_EV_TYPE_NONE: @@ -459,20 +539,297 @@ deselect(ErtsDrvEventState *state, int mode) break; } - state->driver.select = NULL; state->type = ERTS_EV_TYPE_NONE; - state->flags = 0; + state->flags &= ~ERTS_EV_FLAG_USED; remember_removed(state, &pollset); } } - #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS # define IS_FD_UNKNOWN(state) ((state)->type == ERTS_EV_TYPE_NONE && (state)->remove_cnt == 0) #else # define IS_FD_UNKNOWN(state) ((state) == NULL) #endif +static ERTS_INLINE void +check_fd_cleanup(ErtsDrvEventState *state, +#if ERTS_CIO_HAVE_DRV_EVENT + ErtsDrvEventDataState **free_event, +#endif + ErtsDrvSelectDataState **free_select) +{ + erts_aint_t current_cio_time; + + ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(fd_mtx(state->fd))); + + current_cio_time = erts_smp_atomic_read_acqb(&erts_check_io_time); + *free_select = NULL; + if (state->driver.select + && (state->type != ERTS_EV_TYPE_DRV_SEL) + && !is_iotask_active(&state->driver.select->iniotask, current_cio_time) + && !is_iotask_active(&state->driver.select->outiotask, current_cio_time)) { + + *free_select = state->driver.select; + state->driver.select = NULL; + } + +#if ERTS_CIO_HAVE_DRV_EVENT + *free_event = NULL; + if (state->driver.event + && (state->type != ERTS_EV_TYPE_DRV_EV) + && !is_iotask_active(&state->driver.event->iotask, current_cio_time)) { + + *free_event = state->driver.event; + state->driver.event = NULL; + } +#endif + +#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS + if (((state->type != ERTS_EV_TYPE_NONE) + | state->remove_cnt +#if ERTS_CIO_HAVE_DRV_EVENT + | (state->driver.event != NULL) +#endif + | (state->driver.select != NULL)) == 0) { + + hash_erase_drv_ev_state(state); + + } +#endif +} + +static ERTS_INLINE int +check_cleanup_active_fd(ErtsSysFdType fd, +#if ERTS_CIO_DEFER_ACTIVE_EVENTS + ErtsPollControlEntry *pce, + int *pce_ix, +#endif + erts_aint_t current_cio_time) +{ + ErtsDrvEventState *state; + int active = 0; + erts_smp_mtx_t *mtx = fd_mtx(fd); + void *free_select = NULL; +#if ERTS_CIO_HAVE_DRV_EVENT + void *free_event = NULL; +#endif +#if ERTS_CIO_DEFER_ACTIVE_EVENTS + ErtsPollEvents evon = 0, evoff = 0; +#endif + + erts_smp_mtx_lock(mtx); + +#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS + state = &drv_ev_state[(int) fd]; +#else + state = hash_get_drv_ev_state(fd); /* may be NULL! */ + if (state) +#endif + { + if (state->driver.select) { +#if ERTS_CIO_DEFER_ACTIVE_EVENTS + if (is_iotask_active(&state->driver.select->iniotask, current_cio_time)) { + active = 1; + if ((state->events & ERTS_POLL_EV_IN) + && !(state->flags & ERTS_EV_FLAG_DEFER_IN_EV)) { + evoff |= ERTS_POLL_EV_IN; + state->flags |= ERTS_EV_FLAG_DEFER_IN_EV; + } + } + else if (state->flags & ERTS_EV_FLAG_DEFER_IN_EV) { + if (state->events & ERTS_POLL_EV_IN) + evon |= ERTS_POLL_EV_IN; + state->flags &= ~ERTS_EV_FLAG_DEFER_IN_EV; + } + if (is_iotask_active(&state->driver.select->outiotask, current_cio_time)) { + active = 1; + if ((state->events & ERTS_POLL_EV_OUT) + && !(state->flags & ERTS_EV_FLAG_DEFER_OUT_EV)) { + evoff |= ERTS_POLL_EV_OUT; + state->flags |= ERTS_EV_FLAG_DEFER_OUT_EV; + } + } + else if (state->flags & ERTS_EV_FLAG_DEFER_OUT_EV) { + if (state->events & ERTS_POLL_EV_OUT) + evon |= ERTS_POLL_EV_OUT; + state->flags &= ~ERTS_EV_FLAG_DEFER_OUT_EV; + } + if (active) + (void) 0; + else +#else + if (is_iotask_active(&state->driver.select->iniotask, current_cio_time) + || is_iotask_active(&state->driver.select->outiotask, current_cio_time)) + active = 1; + else +#endif + if (state->type != ERTS_EV_TYPE_DRV_SEL) { + free_select = state->driver.select; + state->driver.select = NULL; + } + } + +#if ERTS_CIO_HAVE_DRV_EVENT + if (state->driver.event) { + if (is_iotask_active(&state->driver.event->iotask, current_cio_time)) { +#if ERTS_CIO_DEFER_ACTIVE_EVENTS + ErtsPollEvents evs = state->events & ~state->driver.event->deferred_events; + if (evs) { + evoff |= evs; + state->driver.event->deferred_events |= evs; + } +#endif + active = 1; + } + else if (state->type != ERTS_EV_TYPE_DRV_EV) { + free_event = state->driver.event; + state->driver.event = NULL; + } +#if ERTS_CIO_DEFER_ACTIVE_EVENTS + else { + ErtsPollEvents evs = state->events & state->driver.event->deferred_events; + if (evs) { + evon |= evs; + state->driver.event->deferred_events = 0; + } + } +#endif + + } +#endif + +#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS + if (((state->type != ERTS_EV_TYPE_NONE) | state->remove_cnt | active) == 0) + hash_erase_drv_ev_state(state); +#endif + + } + + erts_smp_mtx_unlock(mtx); + + if (free_select) + free_drv_select_data(free_select); +#if ERTS_CIO_HAVE_DRV_EVENT + if (free_event) + free_drv_event_data(free_event); +#endif + +#if ERTS_CIO_DEFER_ACTIVE_EVENTS + if (evoff) { + ErtsPollControlEntry *pcep = &pce[(*pce_ix)++]; + pcep->fd = fd; + pcep->events = evoff; + pcep->on = 0; + } + if (evon) { + ErtsPollControlEntry *pcep = &pce[(*pce_ix)++]; + pcep->fd = fd; + pcep->events = evon; + pcep->on = 1; + } +#endif + + return active; +} + +static void +check_cleanup_active_fds(erts_aint_t current_cio_time) +{ + int six = pollset.active_fd.six; + int eix = pollset.active_fd.eix; + erts_aint32_t no = erts_smp_atomic32_read_dirty(&pollset.active_fd.no); + int size = pollset.active_fd.size; + int ix = six; +#if ERTS_CIO_DEFER_ACTIVE_EVENTS + /* every fd might add two entries */ + Uint pce_sz = 2*sizeof(ErtsPollControlEntry)*no; + ErtsPollControlEntry *pctrl_entries = (pce_sz + ? erts_alloc(ERTS_ALC_T_TMP, pce_sz) + : NULL); + int pctrl_ix = 0; +#endif + + while (ix != eix) { + ErtsSysFdType fd = pollset.active_fd.array[ix]; + int nix = ix + 1; + if (nix >= size) + nix = 0; + ASSERT(fd != ERTS_SYS_FD_INVALID); + if (!check_cleanup_active_fd(fd, +#if ERTS_CIO_DEFER_ACTIVE_EVENTS + pctrl_entries, + &pctrl_ix, +#endif + current_cio_time)) { + no--; + if (ix == six) { +#ifdef DEBUG + pollset.active_fd.array[ix] = ERTS_SYS_FD_INVALID; +#endif + six = nix; + } + else { + pollset.active_fd.array[ix] = pollset.active_fd.array[six]; +#ifdef DEBUG + pollset.active_fd.array[six] = ERTS_SYS_FD_INVALID; +#endif + six++; + if (six >= size) + six = 0; + } + } + ix = nix; + } + +#if ERTS_CIO_DEFER_ACTIVE_EVENTS + ASSERT(pctrl_ix <= pce_sz/sizeof(ErtsPollControlEntry)); + if (pctrl_ix) + ERTS_CIO_POLL_CTLV(pollset.ps, pctrl_entries, pctrl_ix); + if (pctrl_entries) + erts_free(ERTS_ALC_T_TMP, pctrl_entries); +#endif + + pollset.active_fd.six = six; + pollset.active_fd.eix = eix; + erts_smp_atomic32_set_relb(&pollset.active_fd.no, no); +} + +static ERTS_INLINE void +add_active_fd(ErtsSysFdType fd) +{ + int eix = pollset.active_fd.eix; + int size = pollset.active_fd.size; + + + pollset.active_fd.array[eix] = fd; + + erts_smp_atomic32_set_relb(&pollset.active_fd.no, + (erts_smp_atomic32_read_dirty(&pollset.active_fd.no) + + 1)); + + eix++; + if (eix >= size) + eix = 0; + if (pollset.active_fd.six == eix) { + pollset.active_fd.six = 0; + eix = size; + size += ERTS_ACTIVE_FD_INC; + pollset.active_fd.array = erts_realloc(ERTS_ALC_T_ACTIVE_FD_ARR, + pollset.active_fd.array, + sizeof(ErtsSysFdType)*size); + pollset.active_fd.size = size; +#ifdef DEBUG + { + int i; + for (i = eix + 1; i < size; i++) + pollset.active_fd.array[i] = ERTS_SYS_FD_INVALID; + } +#endif + + } + + pollset.active_fd.eix = eix; +} int ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, @@ -489,6 +846,10 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, ErtsDrvEventState *state; int wake_poller; int ret; +#if ERTS_CIO_HAVE_DRV_EVENT + ErtsDrvEventDataState *free_event = NULL; +#endif + ErtsDrvSelectDataState *free_select = NULL; #ifdef USE_VM_PROBES DTRACE_CHARBUF(name, 64); #endif @@ -589,9 +950,9 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, if (new_events & (ERTS_POLL_EV_ERR|ERTS_POLL_EV_NVAL)) { if (state->type == ERTS_EV_TYPE_DRV_SEL && !state->events) { state->type = ERTS_EV_TYPE_NONE; - state->flags = 0; - erts_free(ERTS_ALC_T_DRV_SEL_D_STATE, state->driver.select); - state->driver.select = NULL; + state->flags &= ~ERTS_EV_FLAG_USED; + state->driver.select->inport = NIL; + state->driver.select->outport = NIL; } ret = -1; goto done; @@ -609,18 +970,10 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, state->events = new_events; if (ctl_events) { if (on) { - if (state->type == ERTS_EV_TYPE_NONE) { - ErtsDrvSelectDataState *dsdsp - = erts_alloc(ERTS_ALC_T_DRV_SEL_D_STATE, - sizeof(ErtsDrvSelectDataState)); - dsdsp->inport = NIL; - dsdsp->outport = NIL; - erts_port_task_handle_init(&dsdsp->intask); - erts_port_task_handle_init(&dsdsp->outtask); - ASSERT(state->driver.select == NULL); - state->driver.select = dsdsp; + if (!state->driver.select) + state->driver.select = alloc_drv_select_data(); + if (state->type == ERTS_EV_TYPE_NONE) state->type = ERTS_EV_TYPE_DRV_SEL; - } ASSERT(state->type == ERTS_EV_TYPE_DRV_SEL); if (ctl_events & ERTS_POLL_EV_IN) state->driver.select->inport = id; @@ -641,17 +994,12 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, state->driver.select->outport = NIL; } if (new_events == 0) { - ASSERT(!erts_port_task_is_scheduled(&state->driver.select->intask)); - ASSERT(!erts_port_task_is_scheduled(&state->driver.select->outtask)); if (old_events != 0) { remember_removed(state, &pollset); } if ((mode & ERL_DRV_USE) || !(state->flags & ERTS_EV_FLAG_USED)) { state->type = ERTS_EV_TYPE_NONE; - state->flags = 0; - erts_free(ERTS_ALC_T_DRV_SEL_D_STATE, - state->driver.select); - state->driver.select = NULL; + state->flags &= ~ERTS_EV_FLAG_USED; } /*else keep it, as fd will probably be selected upon again */ } @@ -682,13 +1030,15 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, ret = 0; -done:; -#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS - if (state->type == ERTS_EV_TYPE_NONE && state->remove_cnt == 0) { - hash_erase_drv_ev_state(state); - } +done: + + check_fd_cleanup(state, +#if ERTS_CIO_HAVE_DRV_EVENT + &free_event, #endif -done_unknown: + &free_select); + +done_unknown: erts_smp_mtx_unlock(fd_mtx(fd)); if (stop_select_fn) { int was_unmasked = erts_block_fpe(); @@ -696,6 +1046,12 @@ done_unknown: (*stop_select_fn)(e, NULL); erts_unblock_fpe(was_unmasked); } + if (free_select) + free_drv_select_data(free_select); +#if ERTS_CIO_HAVE_DRV_EVENT + if (free_event) + free_drv_event_data(free_event); +#endif return ret; } @@ -715,6 +1071,10 @@ ERTS_CIO_EXPORT(driver_event)(ErlDrvPort ix, ErtsDrvEventState *state; int do_wake = 0; int ret; +#if ERTS_CIO_HAVE_DRV_EVENT + ErtsDrvEventDataState *free_event; +#endif + ErtsDrvSelectDataState *free_select; Port *prt = erts_drvport2port(ix); if (prt == ERTS_INVALID_ERL_DRV_PORT) @@ -795,10 +1155,8 @@ ERTS_CIO_EXPORT(driver_event)(ErlDrvPort ix, state->driver.event->removed_events |= remove_events; } else { - state->driver.event - = erts_alloc(ERTS_ALC_T_DRV_EV_D_STATE, - sizeof(ErtsDrvEventDataState)); - erts_port_task_handle_init(&state->driver.event->task); + if (!state->driver.event) + state->driver.event = alloc_drv_event_data(); state->driver.event->port = id; state->driver.event->removed_events = (ErtsPollEvents) 0; state->type = ERTS_EV_TYPE_DRV_EV; @@ -808,10 +1166,10 @@ ERTS_CIO_EXPORT(driver_event)(ErlDrvPort ix, else { if (state->type == ERTS_EV_TYPE_DRV_EV) { abort_tasks(state, 0); - erts_free(ERTS_ALC_T_DRV_EV_D_STATE, - state->driver.event); + state->driver.event->port = NIL; + state->driver.event->data = NULL; + state->driver.event->removed_events = (ErtsPollEvents) 0; } - state->driver.select = NULL; state->type = ERTS_EV_TYPE_NONE; remember_removed(state, &pollset); } @@ -821,12 +1179,22 @@ ERTS_CIO_EXPORT(driver_event)(ErlDrvPort ix, ret = 0; done: -#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS - if (state->type == ERTS_EV_TYPE_NONE && state->remove_cnt == 0) { - hash_erase_drv_ev_state(state); - } + + check_fd_cleanup(state, +#if ERTS_CIO_HAVE_DRV_EVENT + &free_event, #endif + &free_select); + erts_smp_mtx_unlock(fd_mtx(fd)); + + if (free_select) + free_drv_select_data(free_select); +#if ERTS_CIO_HAVE_DRV_EVENT + if (free_event) + free_drv_event_data(free_event); +#endif + return ret; #endif } @@ -1019,7 +1387,7 @@ steal_pending_stop_select(erts_dsprintf_buf_t *dsbufp, ErlDrvPort ix, * In either case stop_select should not be called. */ state->type = ERTS_EV_TYPE_NONE; - state->flags = 0; + state->flags &= ~ERTS_EV_FLAG_USED; if (state->driver.drv_ptr->handle) { erts_ddll_dereference_driver(state->driver.drv_ptr->handle); } @@ -1091,38 +1459,103 @@ event_large_fd_error(ErlDrvPort ix, ErtsSysFdType fd, ErlDrvEventData event_data #endif #endif +static ERTS_INLINE int +io_task_schedule_allowed(ErtsDrvEventState *state, + ErtsPortTaskType type, + erts_aint_t current_cio_time) +{ + ErtsIoTask *io_task; + + switch (type) { + case ERTS_PORT_TASK_INPUT: + if (!state->driver.select) + return 0; +#if ERTS_CIO_HAVE_DRV_EVENT + if (state->driver.event) + return 0; +#endif + io_task = &state->driver.select->iniotask; + break; + case ERTS_PORT_TASK_OUTPUT: + if (!state->driver.select) + return 0; +#if ERTS_CIO_HAVE_DRV_EVENT + if (state->driver.event) + return 0; +#endif + io_task = &state->driver.select->outiotask; + break; +#if ERTS_CIO_HAVE_DRV_EVENT + case ERTS_PORT_TASK_EVENT: + if (!state->driver.event) + return 0; + if (state->driver.select) + return 0; + io_task = &state->driver.event->iotask; + break; +#endif + default: + ERTS_INTERNAL_ERROR("Invalid I/O-task type"); + return 0; + } + + return !is_iotask_active(io_task, current_cio_time); +} + static ERTS_INLINE void -iready(Eterm id, ErtsDrvEventState *state) +iready(Eterm id, ErtsDrvEventState *state, erts_aint_t current_cio_time) { - if (erts_port_task_schedule(id, - &state->driver.select->intask, - ERTS_PORT_TASK_INPUT, - (ErlDrvEvent) state->fd) != 0) { - stale_drv_select(id, state, ERL_DRV_READ); + if (io_task_schedule_allowed(state, + ERTS_PORT_TASK_INPUT, + current_cio_time)) { + ErtsIoTask *iotask = &state->driver.select->iniotask; + erts_smp_atomic_set_nob(&iotask->executed_time, current_cio_time); + if (erts_port_task_schedule(id, + &iotask->task, + ERTS_PORT_TASK_INPUT, + (ErlDrvEvent) state->fd) != 0) { + stale_drv_select(id, state, ERL_DRV_READ); + } + add_active_fd(state->fd); } } static ERTS_INLINE void -oready(Eterm id, ErtsDrvEventState *state) +oready(Eterm id, ErtsDrvEventState *state, erts_aint_t current_cio_time) { - if (erts_port_task_schedule(id, - &state->driver.select->outtask, - ERTS_PORT_TASK_OUTPUT, - (ErlDrvEvent) state->fd) != 0) { - stale_drv_select(id, state, ERL_DRV_WRITE); + if (io_task_schedule_allowed(state, + ERTS_PORT_TASK_OUTPUT, + current_cio_time)) { + ErtsIoTask *iotask = &state->driver.select->outiotask; + erts_smp_atomic_set_nob(&iotask->executed_time, current_cio_time); + if (erts_port_task_schedule(id, + &iotask->task, + ERTS_PORT_TASK_OUTPUT, + (ErlDrvEvent) state->fd) != 0) { + stale_drv_select(id, state, ERL_DRV_WRITE); + } + add_active_fd(state->fd); } } #if ERTS_CIO_HAVE_DRV_EVENT static ERTS_INLINE void -eready(Eterm id, ErtsDrvEventState *state, ErlDrvEventData event_data) +eready(Eterm id, ErtsDrvEventState *state, ErlDrvEventData event_data, + erts_aint_t current_cio_time) { - if (erts_port_task_schedule(id, - &state->driver.event->task, - ERTS_PORT_TASK_EVENT, - (ErlDrvEvent) state->fd, - event_data) != 0) { - stale_drv_select(id, state, 0); + if (io_task_schedule_allowed(state, + ERTS_PORT_TASK_EVENT, + current_cio_time)) { + ErtsIoTask *iotask = &state->driver.event->iotask; + erts_smp_atomic_set_nob(&iotask->executed_time, current_cio_time); + if (erts_port_task_schedule(id, + &iotask->task, + ERTS_PORT_TASK_EVENT, + (ErlDrvEvent) state->fd, + event_data) != 0) { + stale_drv_select(id, state, 0); + } + add_active_fd(state->fd); } } #endif @@ -1153,10 +1586,11 @@ ERTS_CIO_EXPORT(erts_check_io_interrupt_timed)(int set, void ERTS_CIO_EXPORT(erts_check_io)(int do_wait) { - ErtsPollResFd pollres[256]; + ErtsPollResFd *pollres; int pollres_len; SysTimeval wait_time; int poll_ret, i; + erts_aint_t current_cio_time; restart: @@ -1173,10 +1607,24 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) wait_time.tv_usec = 0; } + /* + * No need for an atomic inc op when incrementing + * erts_check_io_time, since only one thread can + * check io at a time. + */ + current_cio_time = erts_smp_atomic_read_dirty(&erts_check_io_time); + current_cio_time++; + erts_smp_atomic_set_relb(&erts_check_io_time, current_cio_time); + + check_cleanup_active_fds(current_cio_time); + #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_check_exact(NULL, 0); /* No locks should be locked */ #endif - pollres_len = sizeof(pollres)/sizeof(ErtsPollResFd); + + pollres_len = erts_smp_atomic32_read_dirty(&pollset.active_fd.no) + ERTS_CHECK_IO_POLL_RES_LEN; + + pollres = erts_alloc(ERTS_ALC_T_TMP, sizeof(ErtsPollResFd)*pollres_len); erts_smp_atomic_set_nob(&pollset.in_poll_wait, 1); @@ -1196,6 +1644,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) if (poll_ret != 0) { erts_smp_atomic_set_nob(&pollset.in_poll_wait, 0); forget_removed(&pollset); + erts_free(ERTS_ALC_T_TMP, pollres); if (poll_ret == EAGAIN) { goto restart; } @@ -1255,15 +1704,15 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) if ((revents & ERTS_POLL_EV_IN) || (!(revents & ERTS_POLL_EV_OUT) && state->events & ERTS_POLL_EV_IN)) { - iready(state->driver.select->inport, state); + iready(state->driver.select->inport, state, current_cio_time); } else if (state->events & ERTS_POLL_EV_OUT) { - oready(state->driver.select->outport, state); + oready(state->driver.select->outport, state, current_cio_time); } } else if (revents & (ERTS_POLL_EV_IN|ERTS_POLL_EV_OUT)) { if (revents & ERTS_POLL_EV_OUT) { - oready(state->driver.select->outport, state); + oready(state->driver.select->outport, state, current_cio_time); } /* Someone might have deselected input since revents was read (true also on the non-smp emulator since @@ -1271,7 +1720,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) revents... */ revents &= ~(~state->events & ERTS_POLL_EV_IN); if (revents & ERTS_POLL_EV_IN) { - iready(state->driver.select->inport, state); + iready(state->driver.select->inport, state, current_cio_time); } } else if (revents & ERTS_POLL_EV_NVAL) { @@ -1279,6 +1728,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) state->driver.select->inport, state->driver.select->outport, state->events); + add_active_fd(state->fd); } break; } @@ -1296,8 +1746,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) if (revents) { event_data->events = state->events; event_data->revents = revents; - - eready(state->driver.event->port, state, event_data); + eready(state->driver.event->port, state, event_data, current_cio_time); } break; } @@ -1315,6 +1764,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) (int) state->type); ASSERT(0); deselect(state, 0); + add_active_fd(state->fd); break; } } @@ -1326,6 +1776,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) } erts_smp_atomic_set_nob(&pollset.in_poll_wait, 0); + erts_free(ERTS_ALC_T_TMP, pollres); forget_removed(&pollset); } @@ -1440,10 +1891,27 @@ static void drv_ev_state_free(void *des) void ERTS_CIO_EXPORT(erts_init_check_io)(void) { + erts_smp_atomic_init_nob(&erts_check_io_time, 0); erts_smp_atomic_init_nob(&pollset.in_poll_wait, 0); + ERTS_CIO_POLL_INIT(); pollset.ps = ERTS_CIO_NEW_POLLSET(); + pollset.active_fd.six = 0; + pollset.active_fd.eix = 0; + erts_smp_atomic32_init_nob(&pollset.active_fd.no, 0); + pollset.active_fd.size = ERTS_ACTIVE_FD_INC; + pollset.active_fd.array = erts_alloc(ERTS_ALC_T_ACTIVE_FD_ARR, + sizeof(ErtsSysFdType)*ERTS_ACTIVE_FD_INC); +#ifdef DEBUG + { + int i; + for (i = 0; i < ERTS_ACTIVE_FD_INC; i++) + pollset.active_fd.array[i] = ERTS_SYS_FD_INVALID; + } +#endif + + #ifdef ERTS_SMP init_removed_fd_alloc(); pollset.removed_list = NULL; @@ -1519,12 +1987,27 @@ Eterm ERTS_CIO_EXPORT(erts_check_io_info)(void *proc) { Process *p = (Process *) proc; - Eterm tags[15], values[15], res; + Eterm tags[16], values[16], res; Uint sz, *szp, *hp, **hpp, memory_size; Sint i; ErtsPollInfo pi; - - ERTS_CIO_POLL_INFO(pollset.ps, &pi); + erts_aint_t cio_time = erts_smp_atomic_read_acqb(&erts_check_io_time); + int active_fds = (int) erts_smp_atomic32_read_acqb(&pollset.active_fd.no); + + while (1) { + erts_aint_t post_cio_time; + int post_active_fds; + + ERTS_CIO_POLL_INFO(pollset.ps, &pi); + + post_cio_time = erts_smp_atomic_read_mb(&erts_check_io_time); + post_active_fds = (int) erts_smp_atomic32_read_acqb(&pollset.active_fd.no); + if (cio_time == post_cio_time && active_fds == post_active_fds) + break; + cio_time = post_cio_time; + active_fds = post_active_fds; + } + memory_size = pi.memory_size; #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS memory_size += sizeof(ErtsDrvEventState) * erts_smp_atomic_read_nob(&drv_ev_state_len); @@ -1588,6 +2071,9 @@ ERTS_CIO_EXPORT(erts_check_io_info)(void *proc) tags[i] = erts_bld_atom(hpp, szp, "max_fds"); values[i++] = erts_bld_uint(hpp, szp, (Uint) pi.max_fds); + tags[i] = erts_bld_atom(hpp, szp, "active_fds"); + values[i++] = erts_bld_uint(hpp, szp, (Uint) active_fds); + #ifdef ERTS_POLL_COUNT_AVOIDED_WAKEUPS tags[i] = erts_bld_atom(hpp, szp, "no_avoided_wakeups"); values[i++] = erts_bld_uint(hpp, szp, (Uint) pi.no_avoided_wakeups); @@ -1642,6 +2128,8 @@ print_events(ErtsPollEvents ev) typedef struct { int used_fds; int num_errors; + int no_driver_select_structs; + int no_driver_event_structs; #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS int internal_fds; ErtsPollEvents *epep; @@ -1664,6 +2152,13 @@ static void doit_erts_check_io_debug(void *vstate, void *vcounters) struct stat stat_buf; #endif + if (state->driver.select) + counters->no_driver_select_structs++; +#if ERTS_CIO_HAVE_DRV_EVENT + if (state->driver.event) + counters->no_driver_event_structs++; +#endif + #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS if (state->events || ep_events) { if (ep_events & ERTS_POLL_EV_NVAL) { @@ -1802,6 +2297,7 @@ static void doit_erts_check_io_debug(void *vstate, void *vcounters) } } } +#if ERTS_CIO_HAVE_DRV_EVENT else if (state->type == ERTS_EV_TYPE_DRV_EV) { Eterm id; erts_printf("driver_event "); @@ -1837,6 +2333,7 @@ static void doit_erts_check_io_debug(void *vstate, void *vcounters) erts_free_port_names(pnp); } } +#endif #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS else if (internal) { erts_printf("internal "); @@ -1876,7 +2373,7 @@ static void doit_erts_check_io_debug(void *vstate, void *vcounters) } int -ERTS_CIO_EXPORT(erts_check_io_debug)(void) +ERTS_CIO_EXPORT(erts_check_io_debug)(ErtsCheckIoDebugInfo *ciodip) { #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS int fd, len; @@ -1885,6 +2382,10 @@ ERTS_CIO_EXPORT(erts_check_io_debug)(void) ErtsDrvEventState null_des; null_des.driver.select = NULL; +#if ERTS_CIO_HAVE_DRV_EVENT + null_des.driver.event = NULL; +#endif + null_des.driver.drv_ptr = NULL; null_des.events = 0; null_des.remove_cnt = 0; null_des.type = ERTS_EV_TYPE_NONE; @@ -1904,6 +2405,8 @@ ERTS_CIO_EXPORT(erts_check_io_debug)(void) #endif counters.used_fds = 0; counters.num_errors = 0; + counters.no_driver_select_structs = 0; + counters.no_driver_event_structs = 0; #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS len = erts_smp_atomic_read_nob(&drv_ev_state_len); @@ -1920,8 +2423,16 @@ ERTS_CIO_EXPORT(erts_check_io_debug)(void) erts_smp_thr_progress_unblock(); + ciodip->no_used_fds = counters.used_fds; + ciodip->no_driver_select_structs = counters.no_driver_select_structs; + ciodip->no_driver_event_structs = counters.no_driver_event_structs; + erts_printf("\n"); erts_printf("used fds=%d\n", counters.used_fds); + erts_printf("Number of driver_select() structures=%d\n", counters.no_driver_select_structs); +#if ERTS_CIO_HAVE_DRV_EVENT + erts_printf("Number of driver_event() structures=%d\n", counters.no_driver_event_structs); +#endif #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS erts_printf("internal fds=%d\n", counters.internal_fds); #endif @@ -1930,6 +2441,7 @@ ERTS_CIO_EXPORT(erts_check_io_debug)(void) #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS erts_free(ERTS_ALC_T_TMP, (void *) counters.epep); #endif + return counters.num_errors; } diff --git a/erts/emulator/sys/common/erl_check_io.h b/erts/emulator/sys/common/erl_check_io.h index edab7947ba..d01297d55c 100644 --- a/erts/emulator/sys/common/erl_check_io.h +++ b/erts/emulator/sys/common/erl_check_io.h @@ -26,6 +26,7 @@ #ifndef ERL_CHECK_IO_H__ #define ERL_CHECK_IO_H__ +#include "sys.h" #include "erl_sys_driver.h" #ifdef ERTS_ENABLE_KERNEL_POLL @@ -52,8 +53,8 @@ void erts_check_io_kp(int); void erts_check_io_nkp(int); void erts_init_check_io_kp(void); void erts_init_check_io_nkp(void); -int erts_check_io_debug_kp(void); -int erts_check_io_debug_nkp(void); +int erts_check_io_debug_kp(ErtsCheckIoDebugInfo *); +int erts_check_io_debug_nkp(ErtsCheckIoDebugInfo *); #else /* !ERTS_ENABLE_KERNEL_POLL */ @@ -70,6 +71,27 @@ void erts_init_check_io(void); #endif +extern erts_smp_atomic_t erts_check_io_time; + +typedef struct { + ErtsPortTaskHandle task; + erts_smp_atomic_t executed_time; +} ErtsIoTask; + +ERTS_GLB_INLINE void erts_io_notify_port_task_executed(ErtsPortTaskHandle *pthp); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE void +erts_io_notify_port_task_executed(ErtsPortTaskHandle *pthp) +{ + ErtsIoTask *itp = (ErtsIoTask *) (((char *) pthp) - offsetof(ErtsIoTask, task)); + erts_aint_t ci_time = erts_smp_atomic_read_acqb(&erts_check_io_time); + erts_smp_atomic_set_relb(&itp->executed_time, ci_time); +} + +#endif + #endif /* ERL_CHECK_IO_H__ */ #if !defined(ERL_CHECK_IO_C__) && !defined(ERTS_ALLOC_C__) @@ -81,6 +103,16 @@ void erts_init_check_io(void); #include "erl_poll.h" #include "erl_port_task.h" +#ifdef __WIN32__ +/* + * Current erts_poll implementation for Windows cannot handle + * active events in the set of events polled. + */ +# define ERTS_CIO_DEFER_ACTIVE_EVENTS 1 +#else +# define ERTS_CIO_DEFER_ACTIVE_EVENTS 0 +#endif + /* * ErtsDrvEventDataState is used by driver_event() which is almost never * used. We allocate ErtsDrvEventDataState separate since we dont wan't @@ -91,13 +123,16 @@ typedef struct { Eterm port; ErlDrvEventData data; ErtsPollEvents removed_events; - ErtsPortTaskHandle task; +#if ERTS_CIO_DEFER_ACTIVE_EVENTS + ErtsPollEvents deferred_events; +#endif + ErtsIoTask iotask; } ErtsDrvEventDataState; typedef struct { Eterm inport; Eterm outport; - ErtsPortTaskHandle intask; - ErtsPortTaskHandle outtask; + ErtsIoTask iniotask; + ErtsIoTask outiotask; } ErtsDrvSelectDataState; #endif /* #ifndef ERL_CHECK_IO_INTERNAL__ */ diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c index 31ad3b82d5..c5e804a62d 100644 --- a/erts/emulator/sys/common/erl_sys_common_misc.c +++ b/erts/emulator/sys/common/erl_sys_common_misc.c @@ -44,6 +44,14 @@ #endif #endif +/* + * erts_check_io_time is used by the erl_check_io implementation. The + * global erts_check_io_time variable is declared here since there + * (often) exist two versions of erl_check_io (kernel-poll and + * non-kernel-poll), and we dont want two versions of this variable. + */ +erts_smp_atomic_t erts_check_io_time; + /* Written once and only once */ static int filename_encoding = ERL_FILENAME_UNKNOWN; diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 2c47aa06c2..2c86bf5fd1 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -130,9 +130,6 @@ /* File descriptors are numbers anc consecutively allocated on Unix */ #define ERTS_SYS_CONTINOUS_FD_NUMBERS -#define HAVE_ERTS_CHECK_IO_DEBUG -int erts_check_io_debug(void); - #ifndef ERTS_SMP # undef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT # define ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 61f9f6a59a..ada62f71f9 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -277,7 +277,7 @@ struct { void (*check_io)(int); Uint (*size)(void); Eterm (*info)(void *); - int (*check_io_debug)(void); + int (*check_io_debug)(ErtsCheckIoDebugInfo *); } io_func = {0}; @@ -299,9 +299,9 @@ Eterm erts_check_io_info(void *p) } int -erts_check_io_debug(void) +erts_check_io_debug(ErtsCheckIoDebugInfo *ip) { - return (*io_func.check_io_debug)(); + return (*io_func.check_io_debug)(ip); } diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 7a1d129cd5..972170d465 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -1085,7 +1085,7 @@ void erts_poll_controlv(ErtsPollSet ps, pcev[i].events, pcev[i].on); } - ERTS_POLLSET_LOCK(ps); + ERTS_POLLSET_UNLOCK(ps); HARDTRACEF(("Out erts_poll_controlv")); } diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 0deb097b1a..74b15d3534 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -107,12 +107,10 @@ /* * Our own type of "FD's" */ +#define ERTS_SYS_FD_INVALID INVALID_HANDLE_VALUE #define ERTS_SYS_FD_TYPE HANDLE #define NO_FSTAT_ON_SYS_FD_TYPE 1 /* They are events, not files */ -#define HAVE_ERTS_CHECK_IO_DEBUG -int erts_check_io_debug(void); - /* * For erl_time_sup */ diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl index 195c9c0a5f..17579be416 100644 --- a/erts/emulator/test/a_SUITE.erl +++ b/erts/emulator/test/a_SUITE.erl @@ -97,23 +97,13 @@ display_check_io(ChkIo) -> catch erlang:display('--- CHECK IO INFO ---'), catch erlang:display(ChkIo), catch erts_debug:set_internal_state(available_internal_state, true), - NoOfErrorFds = (catch erts_debug:get_internal_state(check_io_debug)), + NoOfErrorFds = (catch element(1, erts_debug:get_internal_state(check_io_debug))), catch erlang:display({'NoOfErrorFds', NoOfErrorFds}), catch erts_debug:set_internal_state(available_internal_state, false), catch erlang:display('--- CHECK IO INFO ---'), ok. get_check_io_info() -> - ChkIo = erlang:system_info(check_io), - case lists:keysearch(pending_updates, 1, ChkIo) of - {value, {pending_updates, 0}} -> - display_check_io(ChkIo), - ChkIo; - false -> - ChkIo; - _ -> - receive after 10 -> ok end, - get_check_io_info() - end. + z_SUITE:get_check_io_info(). diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 7087542899..269e50f3ce 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -31,8 +31,9 @@ end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2, + + a_test/1, outputv_echo/1, - timer_measure/1, timer_cancel/1, timer_change/1, @@ -79,7 +80,8 @@ thr_free_drv/1, async_blast/1, thr_msg_blast/1, - consume_timeslice/1]). + consume_timeslice/1, + z_test/1]). -export([bin_prefix/2]). @@ -122,19 +124,19 @@ init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> _ -> erts_debug:set_internal_state(available_internal_state, true) end, erlang:display({init_per_testcase, Case}), - ?line 0 = erts_debug:get_internal_state(check_io_debug), + ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)), [{watchdog, Dog},{testcase, Case}|Config]. end_per_testcase(Case, Config) -> Dog = ?config(watchdog, Config), erlang:display({end_per_testcase, Case}), - ?line 0 = erts_debug:get_internal_state(check_io_debug), + ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)), ?t:timetrap_cancel(Dog). suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> - [outputv_errors, outputv_echo, queue_echo, {group, timer}, +all() -> %% Keep a_test first and z_test last... + [a_test, outputv_errors, outputv_echo, queue_echo, {group, timer}, driver_unloaded, io_ready_exit, use_fallback_pollset, bad_fd_in_pollset, driver_event, fd_change, steal_control, otp_6602, driver_system_info_base_ver, @@ -151,7 +153,8 @@ all() -> thr_free_drv, async_blast, thr_msg_blast, - consume_timeslice]. + consume_timeslice, + z_test]. groups() -> [{timer, [], @@ -917,8 +920,7 @@ steal_control_test(Hndl = {erts_poll_info, Before}) -> end. chkio_test_init(Config) when is_list(Config) -> - ?line wait_until_no_pending_updates(), - ?line ChkIo = erlang:system_info(check_io), + ?line ChkIo = get_stable_check_io_info(), ?line case catch lists:keysearch(name, 1, ChkIo) of {value, {name, erts_poll}} -> ?line ?t:format("Before test: ~p~n", [ChkIo]), @@ -937,8 +939,7 @@ chkio_test_fini({skipped, _} = Res) -> chkio_test_fini({chkio_test_result, Res, Before}) -> ?line ok = erl_ddll:unload_driver('chkio_drv'), ?line ok = erl_ddll:stop(), - ?line wait_until_no_pending_updates(), - ?line After = erlang:system_info(check_io), + ?line After = get_stable_check_io_info(), ?line ?t:format("After test: ~p~n", [After]), ?line verify_chkio_state(Before, After), ?line Res. @@ -985,7 +986,7 @@ chkio_test({erts_poll_info, Before}, ?line Fun(), ?line During = erlang:system_info(check_io), ?line erlang:display(During), - ?line 0 = erts_debug:get_internal_state(check_io_debug), + ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)), ?line ?t:format("During test: ~p~n", [During]), ?line chk_chkio_port(Port), ?line case erlang:port_control(Port, ?CHKIO_STOP, "") of @@ -1034,18 +1035,22 @@ verify_chkio_state(Before, After) -> After) end, ?line ok. - - -wait_until_no_pending_updates() -> - case lists:keysearch(pending_updates, 1, erlang:system_info(check_io)) of - {value, {pending_updates, 0}} -> - ok; - false -> - ok; +get_stable_check_io_info() -> + ChkIo = erlang:system_info(check_io), + PendUpdNo = case lists:keysearch(pending_updates, 1, ChkIo) of + {value, {pending_updates, PendNo}} -> + PendNo; + false -> + 0 + end, + {value, {active_fds, ActFds}} = lists:keysearch(active_fds, 1, ChkIo), + case {PendUpdNo, ActFds} of + {0, 0} -> + ChkIo; _ -> receive after 10 -> ok end, - wait_until_no_pending_updates() + get_stable_check_io_info() end. otp_6602(doc) -> ["Missed port lock when stealing control of fd from a " @@ -2374,10 +2379,25 @@ count_proc_sched(Ps, PNs) -> PNs end. +a_test(Config) when is_list(Config) -> + check_io_debug(). + +z_test(Config) when is_list(Config) -> + check_io_debug(). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Utilities %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +check_io_debug() -> + get_stable_check_io_info(), + {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs} + = erts_debug:get_internal_state(check_io_debug), + 0 = NoErrorFds, + NoUsedFds = NoDrvSelStructs, + 0 = NoDrvEvStructs, + ok. + %flush_msgs() -> % receive % M -> diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index 4b3075a164..b0c6224dfe 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -38,7 +38,7 @@ -export([schedulers_alive/1, node_container_refc_check/1, long_timers/1, pollset_size/1, - check_io_debug/1]). + check_io_debug/1, get_check_io_info/0]). -define(DEFAULT_TIMEOUT, ?t:minutes(5)). @@ -288,11 +288,14 @@ check_io_debug(Config) when is_list(Config) -> end. check_io_debug_test() -> + ?line erlang:display(get_check_io_info()), ?line erts_debug:set_internal_state(available_internal_state, true), - ?line erlang:display(erlang:system_info(check_io)), - ?line NoOfErrorFds = erts_debug:get_internal_state(check_io_debug), + ?line {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs} + = erts_debug:get_internal_state(check_io_debug), ?line erts_debug:set_internal_state(available_internal_state, false), - ?line 0 = NoOfErrorFds, + ?line 0 = NoErrorFds, + ?line NoUsedFds = NoDrvSelStructs, + ?line 0 = NoDrvEvStructs, ?line ok. @@ -305,7 +308,7 @@ display_check_io(ChkIo) -> catch erlang:display('--- CHECK IO INFO ---'), catch erlang:display(ChkIo), catch erts_debug:set_internal_state(available_internal_state, true), - NoOfErrorFds = (catch erts_debug:get_internal_state(check_io_debug)), + NoOfErrorFds = (catch element(1, erts_debug:get_internal_state(check_io_debug))), catch erlang:display({'NoOfErrorFds', NoOfErrorFds}), catch erts_debug:set_internal_state(available_internal_state, false), catch erlang:display('--- CHECK IO INFO ---'), @@ -313,14 +316,19 @@ display_check_io(ChkIo) -> get_check_io_info() -> ChkIo = erlang:system_info(check_io), - case lists:keysearch(pending_updates, 1, ChkIo) of - {value, {pending_updates, 0}} -> + PendUpdNo = case lists:keysearch(pending_updates, 1, ChkIo) of + {value, {pending_updates, PendNo}} -> + PendNo; + false -> + 0 + end, + {value, {active_fds, ActFds}} = lists:keysearch(active_fds, 1, ChkIo), + case {PendUpdNo, ActFds} of + {0, 0} -> display_check_io(ChkIo), ChkIo; - false -> - ChkIo; _ -> - receive after 10 -> ok end, + receive after 100 -> ok end, get_check_io_info() end. diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index f098e56a2e..9248b3844f 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -128,6 +128,7 @@ static char *pluss_val_switches[] = { "bwt", "cl", "ct", + "ecio", "fwi", "tbt", "wct", diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 9dd826fa26..82e9de382a 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 291926578e..58c08fcb34 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2106,6 +2106,7 @@ tuple_to_list(_Tuple) -> (dynamic_trace) -> none | dtrace | systemtap; (dynamic_trace_probes) -> boolean(); (elib_malloc) -> false; + (eager_check_io) -> boolean(); (ets_limit) -> pos_integer(); (fullsweep_after) -> {fullsweep_after, non_neg_integer()}; (garbage_collection) -> [{atom(), integer()}]; -- cgit v1.2.3 From 404c44d58058da8cfead5dd310fcb24bf3e8d344 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 24 Sep 2014 17:48:28 +0200 Subject: No eager check I/O on OSE --- erts/emulator/beam/erl_init.c | 5 ++++- erts/emulator/beam/erl_process.c | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index ef77e1d7fc..61f8385efc 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -1678,9 +1678,12 @@ erl_start(int argc, char **argv) } else if (has_prefix("ecio", sub_param)) { arg = get_arg(sub_param+4, argv[i+1], &i); +#ifndef __OSE__ if (sys_strcmp("true", arg) == 0) erts_eager_check_io = 1; - else if (sys_strcmp("false", arg) == 0) + else +#endif + if (sys_strcmp("false", arg) == 0) erts_eager_check_io = 0; else { erts_fprintf(stderr, diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 65abd87c08..e5bb1203c8 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -148,7 +148,12 @@ extern BeamInstr beam_apply[]; extern BeamInstr beam_exit[]; extern BeamInstr beam_continue_exit[]; +#ifdef __OSE__ +/* Eager check I/O not supported on OSE yet. */ +int erts_eager_check_io = 0; +#else int erts_eager_check_io = 0; +#endif int erts_sched_compact_load; int erts_sched_balance_util = 0; Uint erts_no_schedulers; -- cgit v1.2.3 From cb604704efd28fafd0f8edce03db00f7fef53909 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 23 Sep 2014 18:06:44 +0200 Subject: Change default to "eager check I/O" Conflicts: erts/emulator/beam/erl_process.c --- erts/doc/src/erl.xml | 6 +++--- erts/emulator/beam/erl_process.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 141754e4f9..f3ada61f3e 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1144,9 +1144,9 @@ +secio true|false

Enable or disable eager check I/O scheduling. The default - is currently false, but will most likely be changed - to true in OTP 18. The behaviour before this flag - was introduced corresponds to +secio false.

+ is currently true. The default was changed from false + to true as of erts version 7.0. The behaviour before this + flag was introduced corresponds to +secio false.

The flag effects when schedulers will check for I/O operations possible to execute, and when such I/O operations will execute. As the name of the parameter implies, diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index e5bb1203c8..7b272885a7 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -152,7 +152,7 @@ extern BeamInstr beam_continue_exit[]; /* Eager check I/O not supported on OSE yet. */ int erts_eager_check_io = 0; #else -int erts_eager_check_io = 0; +int erts_eager_check_io = 1; #endif int erts_sched_compact_load; int erts_sched_balance_util = 0; -- cgit v1.2.3 From c9b40bf1c30d60f76b6954c0d35bee82912531a0 Mon Sep 17 00:00:00 2001 From: Jani Hakala Date: Sun, 28 Sep 2014 15:54:56 +0300 Subject: Fix if-clauses that make code to be unreachable Fix two cases where use of assigment operator instead of comparison operator causes if-clauses to be always false and code to be unreachable. --- erts/etc/win32/Install.c | 2 +- erts/etc/win32/erl.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c index 500fd166f8..9d85d642ab 100644 --- a/erts/etc/win32/Install.c +++ b/erts/etc/win32/Install.c @@ -80,7 +80,7 @@ int wmain(int argc, wchar_t **argv) } } if (root == NULL) { - if (module = NULL) { + if (module == NULL) { fprintf(stderr, "Cannot GetModuleHandle()\n"); exit(1); } diff --git a/erts/etc/win32/erl.c b/erts/etc/win32/erl.c index 1d116bf36e..772b668586 100644 --- a/erts/etc/win32/erl.c +++ b/erts/etc/win32/erl.c @@ -264,7 +264,7 @@ static void get_parameters(void) int len; - if (module = NULL) { + if (module == NULL) { error("Cannot GetModuleHandle()"); } -- cgit v1.2.3 From bfc6a9b3325969a7bc1d0c228766537049a4f637 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 30 Sep 2014 16:55:06 +0200 Subject: erts: Fix erlang:port_set_data/2 for non immediate data hsize field was not set leading to VM crash --- erts/emulator/beam/erl_bif_port.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index afb33c1cdb..8a622e5e6e 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -554,6 +554,7 @@ BIF_RETTYPE port_set_data_2(BIF_ALIST_2) hp = &pdhp->heap[0]; pdhp->off_heap.first = NULL; pdhp->off_heap.overhead = 0; + pdhp->hsize = hsize; pdhp->data = copy_struct(BIF_ARG_2, hsize, &hp, &pdhp->off_heap); data = (erts_aint_t) pdhp; ASSERT((data & 0x3) == 0); -- cgit v1.2.3 From b7801392325784027e93e3488e7e28d596e6b658 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 30 Sep 2014 17:00:37 +0200 Subject: erts: Fix race between port_set_data, port_get_data and port termination Always update prt->data with atomic xchg-op. Check for NULL data to detect racing port terminator. Use NULL, as THE_NON_VALUE can be a valid pointer on debug VM. --- erts/emulator/beam/erl_bif_port.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 8a622e5e6e..64bd598ba6 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -493,8 +493,8 @@ void erts_cleanup_port_data(Port *prt) { ASSERT(erts_atomic32_read_nob(&prt->state) & ERTS_PORT_SFLGS_INVALID_LOOKUP); - cleanup_old_port_data(erts_smp_atomic_read_nob(&prt->data)); - erts_smp_atomic_set_nob(&prt->data, (erts_aint_t) THE_NON_VALUE); + cleanup_old_port_data(erts_smp_atomic_xchg_nob(&prt->data, + (erts_aint_t) NULL)); } Uint @@ -562,8 +562,14 @@ BIF_RETTYPE port_set_data_2(BIF_ALIST_2) data = erts_smp_atomic_xchg_wb(&prt->data, data); + if (data == (erts_aint_t)NULL) { + /* Port terminated by racing thread */ + data = erts_smp_atomic_xchg_wb(&prt->data, data); + ASSERT(data != (erts_aint_t)NULL); + cleanup_old_port_data(data); + BIF_ERROR(BIF_P, BADARG); + } cleanup_old_port_data(data); - BIF_RET(am_true); } @@ -582,6 +588,8 @@ BIF_RETTYPE port_get_data_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); data = erts_smp_atomic_read_ddrb(&prt->data); + if (data == (erts_aint_t)NULL) + BIF_ERROR(BIF_P, BADARG); /* Port terminated by racing thread */ if ((data & 0x3) != 0) { res = (Eterm) (UWord) data; -- cgit v1.2.3 From 170b55244a049be50cac9c66862f962683ee969a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 30 Sep 2014 20:52:00 +0200 Subject: erts: Add test case for port_set_data and port_get_data --- erts/emulator/test/port_SUITE.erl | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index e01b2f253b..8792f25d76 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -90,6 +90,7 @@ mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1, exit_status_multi_scheduling_block/1, ports/1, spawn_driver/1, spawn_executable/1, close_deaf_port/1, + port_setget_data/1, unregister_name/1, parallelism_option/1]). -export([do_iter_max_ports/2]). @@ -115,6 +116,7 @@ all() -> mix_up_ports, otp_5112, otp_5119, exit_status_multi_scheduling_block, ports, spawn_driver, spawn_executable, close_deaf_port, unregister_name, + port_setget_data, parallelism_option]. groups() -> @@ -2333,6 +2335,55 @@ close_deaf_port_1(N, Cmd) -> {comment, "Could not spawn more than " ++ integer_to_list(N) ++ " OS processes."} end. +%% Test undocumented port_set_data/2 and port_get_data/1 +%% Hammer from multiple processes a while +%% and then abrubtly close the port (OTP-12208). +port_setget_data(Config) when is_list(Config) -> + ok = load_driver(?config(data_dir, Config), "echo_drv"), + Port = erlang:open_port({spawn_driver, "echo_drv"}, []), + + NSched = erlang:system_info(schedulers_online), + PRs = lists:map(fun(I) -> + spawn_opt(fun() -> port_setget_data_hammer(Port,1) end, + [monitor, {scheduler, I rem NSched}]) + end, + lists:seq(1,10)), + receive after 100 -> ok end, + Papa = self(), + lists:foreach(fun({Pid,_}) -> Pid ! {Papa,prepare_for_close} end, PRs), + lists:foreach(fun({Pid,_}) -> + receive {Pid,prepare_for_close} -> ok end + end, + PRs), + port_close(Port), + lists:foreach(fun({Pid,Ref}) -> + receive {'DOWN', Ref, process, Pid, normal} -> ok end + end, + PRs), + ok. + +port_setget_data_hammer(Port, N) -> + Rand = random:uniform(3), + try case Rand of + 1 -> true = erlang:port_set_data(Port, atom); + 2 -> true = erlang:port_set_data(Port, {1,2,3}); + 3 -> erlang:port_get_data(Port) + end + catch + error:badarg -> + true = get(prepare_for_close), + io:format("~p did ~p rounds before port closed\n", [self(), N]), + exit(normal) + end, + receive {Papa, prepare_for_close} -> + put(prepare_for_close, true), + Papa ! {self(),prepare_for_close} + after 0 -> + ok + end, + port_setget_data_hammer(Port, N+1). + + wait_until(Fun) -> case catch Fun() of true -> -- cgit v1.2.3 From a22560bae02fcb531538d692cbfcc0e293d57747 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Wed, 1 Oct 2014 18:44:36 -0400 Subject: Fix missing field initializer in ERL_NIF_INIT macro In the ERL_NIF_INIT macro, add a missing static initializer for the new options field which was added to the ErlNifEntry struct for 17.3. This fix prevents compile-time errors in NIFs when the compiler is instructed to treat missing field initializers as errors. --- erts/emulator/beam/erl_nif.h | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 226fc199a1..849024453c 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -241,21 +241,10 @@ extern TWinDynNifCallbacks WinDynNifCallbacks; # else # define ERL_NIF_INIT_DECL(MODNAME) __declspec(dllexport) ErlNifEntry* nif_init(TWinDynNifCallbacks* callbacks) # endif -# ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -# define ERL_NIF_INIT_BODY do { \ - memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks)); \ - entry.options = ERL_NIF_DIRTY_NIF_OPTION; \ - } while(0) -# else -# define ERL_NIF_INIT_BODY memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks)) -# endif +# define ERL_NIF_INIT_BODY memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks)) #else # define ERL_NIF_INIT_GLOB -# ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -# define ERL_NIF_INIT_BODY entry.options = ERL_NIF_DIRTY_NIF_OPTION -# else -# define ERL_NIF_INIT_BODY -# endif +# define ERL_NIF_INIT_BODY # ifdef STATIC_ERLANG_NIF # define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _nif_init(void) # else @@ -263,6 +252,11 @@ extern TWinDynNifCallbacks WinDynNifCallbacks; # endif #endif +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT +# define ERL_NIF_ENTRY_OPTIONS ERL_NIF_DIRTY_NIF_OPTION +#else +# define ERL_NIF_ENTRY_OPTIONS 0 +#endif #ifdef __cplusplus } @@ -288,7 +282,8 @@ ERL_NIF_INIT_DECL(NAME) \ sizeof(FUNCS) / sizeof(*FUNCS), \ FUNCS, \ LOAD, RELOAD, UPGRADE, UNLOAD, \ - ERL_NIF_VM_VARIANT \ + ERL_NIF_VM_VARIANT, \ + ERL_NIF_ENTRY_OPTIONS \ }; \ ERL_NIF_INIT_BODY; \ return &entry; \ -- cgit v1.2.3 From 21a16f6c19cd8aba39481c89bf2b32c7910f3d32 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 2 Oct 2014 21:40:28 +0200 Subject: erts: Mend port_set_data with non-immed data for halfword VM --- erts/emulator/beam/erl_alloc.types | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 37354b7f8d..df33bbc2f7 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -267,7 +267,6 @@ type CODE_IX_LOCK_Q SHORT_LIVED SYSTEM code_ix_lock_q type PROC_INTERVAL LONG_LIVED SYSTEM process_interval type BUSY_CALLER_TAB SHORT_LIVED SYSTEM busy_caller_table type BUSY_CALLER SHORT_LIVED SYSTEM busy_caller -type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap type PROC_SYS_TSK SHORT_LIVED PROCESSES proc_sys_task type PROC_SYS_TSK_QS SHORT_LIVED PROCESSES proc_sys_task_queues @@ -364,6 +363,7 @@ type NLINK_SH STANDARD_LOW PROCESSES nlink_sh type AINFO_REQ STANDARD_LOW SYSTEM alloc_info_request type SCHED_WTIME_REQ STANDARD_LOW SYSTEM sched_wall_time_request type GC_INFO_REQ STANDARD_LOW SYSTEM gc_info_request +type PORT_DATA_HEAP STANDARD_LOW SYSTEM port_data_heap +else # "fullword" @@ -383,6 +383,7 @@ type NLINK_SH FIXED_SIZE PROCESSES nlink_sh type AINFO_REQ SHORT_LIVED SYSTEM alloc_info_request type SCHED_WTIME_REQ SHORT_LIVED SYSTEM sched_wall_time_request type GC_INFO_REQ SHORT_LIVED SYSTEM gc_info_request +type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap +endif -- cgit v1.2.3 From 891cc466c957e91c7770f0a91ba83b65a268c2c1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 3 Oct 2014 11:37:52 +0200 Subject: erts: Fix bug when delayed deallocated carrier is reused by cpool_fetch The delayed dealloc queue destroyes one word but cpool_fetch() is expected to return healthy carriers. So we restore that overwritten word with a little bit of hackish code. --- erts/emulator/beam/erl_alloc_util.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index a4e164bf51..55052430e1 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -1775,6 +1775,18 @@ handle_delayed_dealloc(Allctr_t *allctr, * data has been overwritten by the queue. */ Carrier_t *crr = FIRST_BLK_TO_MBC(allctr, blk); + + /* Restore word overwritten by the dd-queue as it will be read + * if this carrier is pulled from dc_list by cpool_fetch() + */ + ERTS_ALC_CPOOL_ASSERT(FBLK_TO_MBC(blk) != crr); + ERTS_ALC_CPOOL_ASSERT(sizeof(ErtsAllctrDDBlock_t) == sizeof(void*)); +#ifdef MBC_ABLK_OFFSET_BITS + blk->u.carrier = crr; +#else + blk->carrier = crr; +#endif + ERTS_ALC_CPOOL_ASSERT(ERTS_ALC_IS_CPOOL_ENABLED(allctr)); ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr); ERTS_ALC_CPOOL_ASSERT(((erts_aint_t) allctr) -- cgit v1.2.3 From e16f287c28ac93b54772169958c965733aa67e66 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 25 Sep 2014 22:50:24 +0200 Subject: erts: Add pooled_list and traitor_list --- erts/emulator/beam/erl_alloc_util.c | 300 +++++++++++++++++++++++++++++++----- erts/emulator/beam/erl_alloc_util.h | 13 +- 2 files changed, 272 insertions(+), 41 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 55052430e1..ba16e5c3a3 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -205,7 +205,7 @@ MBC after deallocating first block: ASSERT(((UWord)(F) & (~FLG_MASK|THIS_FREE_BLK_HDR_FLG|PREV_FREE_BLK_HDR_FLG)) == THIS_FREE_BLK_HDR_FLG), \ (B)->bhdr = ((Sz) | (F)), \ (B)->u.carrier = (C)) - + # define IS_MBC_FIRST_ABLK(AP,B) \ ((((UWord)(B) & ~ERTS_SACRR_UNIT_MASK) == MBC_HEADER_SIZE(AP)) \ && ((B)->bhdr & MBC_ABLK_OFFSET_MASK) == 0) @@ -378,9 +378,8 @@ do { \ #ifdef ERTS_SMP #define SBC_HEADER_SIZE \ - (UNIT_CEILING(sizeof(Carrier_t) \ - - sizeof(ErtsAlcCPoolData_t) \ - + ABLK_HDR_SZ) \ + (UNIT_CEILING(offsetof(Carrier_t, cpool) \ + + ABLK_HDR_SZ) \ - ABLK_HDR_SZ) #else #define SBC_HEADER_SIZE \ @@ -929,6 +928,88 @@ unlink_carrier(CarrierList_t *cl, Carrier_t *crr) #ifdef ERTS_SMP +#ifdef DEBUG +static int is_in_list(ErtsDoubleLink_t* sentinel, ErtsDoubleLink_t* node) +{ + ErtsDoubleLink_t* p; + + ASSERT(node != sentinel); + for (p = sentinel->next; p != sentinel; p = p->next) { + if (p == node) + return 1; + } + return 0; +} +#endif /* DEBUG */ + +static ERTS_INLINE void +link_edl_after(ErtsDoubleLink_t* after_me, ErtsDoubleLink_t* node) +{ + ErtsDoubleLink_t* before_me = after_me->next; + ASSERT(node != after_me && node != before_me); + node->next = before_me; + node->prev = after_me; + before_me->prev = node; + after_me->next = node; +} + +static ERTS_INLINE void +link_edl_before(ErtsDoubleLink_t* before_me, ErtsDoubleLink_t* node) +{ + ErtsDoubleLink_t* after_me = before_me->prev; + ASSERT(node != before_me && node != after_me); + node->next = before_me; + node->prev = after_me; + before_me->prev = node; + after_me->next = node; +} + +static ERTS_INLINE void +unlink_edl(ErtsDoubleLink_t* node) +{ + node->next->prev = node->prev; + node->prev->next = node->next; +} + +static ERTS_INLINE void +relink_edl_before(ErtsDoubleLink_t* before_me, ErtsDoubleLink_t* node) +{ + if (node != before_me && node != before_me->prev) { + unlink_edl(node); + link_edl_before(before_me, node); + } +} + +static ERTS_INLINE int is_abandoned(Carrier_t *crr) +{ + return crr->cpool.abandoned.next != NULL; +} + +static ERTS_INLINE void +link_abandoned_carrier(ErtsDoubleLink_t* list, Carrier_t *crr) +{ + ASSERT(!is_abandoned(crr)); + + link_edl_after(list, &crr->cpool.abandoned); + + ASSERT(crr->cpool.abandoned.next != &crr->cpool.abandoned); + ASSERT(crr->cpool.abandoned.prev != &crr->cpool.abandoned); +} + +static ERTS_INLINE void +unlink_abandoned_carrier(Carrier_t *crr) +{ + ASSERT(is_in_list(&crr->cpool.orig_allctr->cpool.pooled_list, + &crr->cpool.abandoned) || + is_in_list(&crr->cpool.orig_allctr->cpool.traitor_list, + &crr->cpool.abandoned)); + + unlink_edl(&crr->cpool.abandoned); + + crr->cpool.abandoned.next = NULL; + crr->cpool.abandoned.prev = NULL; +} + static ERTS_INLINE void clear_busy_pool_carrier(Allctr_t *allctr, Carrier_t *crr) { @@ -955,7 +1036,7 @@ clear_busy_pool_carrier(Allctr_t *allctr, Carrier_t *crr) } } -#endif +#endif /* ERTS_SMP */ #if 0 #define ERTS_DBG_CHK_FIX_LIST(A, FIX, IX, B) \ @@ -2575,10 +2656,9 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs, #ifdef ERTS_SMP #define ERTS_ALC_MAX_DEALLOC_CARRIER 10 -#define ERTS_ALC_CPOOL_MAX_FETCH_INSPECT 10 +#define ERTS_ALC_CPOOL_MAX_FETCH_INSPECT 20 +#define ERTS_ALC_CPOOL_MAX_TRAITOR_INSPECT 10 #define ERTS_ALC_CPOOL_CHECK_LIMIT_COUNT 100 -#define ERTS_ALC_CPOOL_MAX_NO_CARRIERS 5 -#define ERTS_ALC_CPOOL_INSERT_ALLOWED_OFFSET 100 #define ERTS_ALC_CPOOL_MAX_FAILED_STAT_READS 3 #define ERTS_ALC_CPOOL_PTR_MOD_MRK (((erts_aint_t) 1) << 0) @@ -2916,59 +2996,163 @@ cpool_delete(Allctr_t *allctr, Allctr_t *prev_allctr, Carrier_t *crr) static Carrier_t * cpool_fetch(Allctr_t *allctr, UWord size) { - int i; + int i, i_stop, has_passed_sentinel; Carrier_t *crr; ErtsAlcCPoolData_t *cpdp; - ErtsAlcCPoolData_t *sentinel = &carrier_pool[allctr->alloc_no].sentinel; + ErtsAlcCPoolData_t *cpool_entrance; + ErtsAlcCPoolData_t *sentinel; + ErtsDoubleLink_t* dl; + ErtsDoubleLink_t* first_old_traitor; ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_INVALID /* testcase */ || erts_thr_progress_is_managed_thread()); - i = 0; + i = ERTS_ALC_CPOOL_MAX_FETCH_INSPECT; + first_old_traitor = allctr->cpool.traitor_list.next; + cpool_entrance = NULL; - /* First; check our own pending dealloc carrier list... */ - crr = allctr->cpool.dc_list.last; - while (crr && i < ERTS_ALC_CPOOL_MAX_FETCH_INSPECT) { - if (erts_atomic_read_nob(&crr->cpool.max_size) >= size) { - unlink_carrier(&allctr->cpool.dc_list, crr); -#ifdef ERTS_ALC_CPOOL_DEBUG - ERTS_ALC_CPOOL_ASSERT(erts_smp_atomic_xchg_nob(&crr->allctr, - ((erts_aint_t) allctr)) - == (((erts_aint_t) allctr) & ~ERTS_CRR_ALCTR_FLG_MASK)); -#else - erts_smp_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr)); -#endif - return crr; + /* + * Search my own pooled_list, + * i.e my abandoned carriers that were in the pool last time I checked. + */ + + dl = allctr->cpool.pooled_list.next; + while(dl != &allctr->cpool.pooled_list) { + erts_aint_t exp, act; + crr = (Carrier_t *) (((char *) dl) - offsetof(Carrier_t, cpool.abandoned)); + + ASSERT(!is_in_list(&allctr->cpool.traitor_list, dl)); + ASSERT(crr->cpool.orig_allctr == allctr); + dl = dl->next; + exp = erts_smp_atomic_read_rb(&crr->allctr); + if ((exp & ERTS_CRR_ALCTR_FLG_MASK) == ERTS_CRR_ALCTR_FLG_IN_POOL + && erts_atomic_read_nob(&crr->cpool.max_size) >= size) { + /* Try to fetch it... */ + act = erts_smp_atomic_cmpxchg_mb(&crr->allctr, + (erts_aint_t) allctr, + exp); + if (act == exp) { + cpool_delete(allctr, ((Allctr_t *) (act & ~ERTS_CRR_ALCTR_FLG_MASK)), crr); + unlink_abandoned_carrier(crr); + + /* Move sentinel to continue next search from here */ + relink_edl_before(dl, &allctr->cpool.pooled_list); + return crr; + } + exp = act; + } + if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) { + if (!cpool_entrance) + cpool_entrance = &crr->cpool; + } + else { /* Not in pool, move to traitor_list */ + unlink_abandoned_carrier(crr); + link_abandoned_carrier(&allctr->cpool.traitor_list, crr); + } + if (--i <= 0) { + /* Move sentinel to continue next search from here */ + relink_edl_before(dl, &allctr->cpool.pooled_list); + return NULL; } - crr = crr->prev; - i++; } - /* ... then the pool ... */ + /* Now search traitor_list. + * i.e carriers employed by other allocators last time I checked. + * They might have been abandoned since then. + */ + + i_stop = (i < ERTS_ALC_CPOOL_MAX_TRAITOR_INSPECT ? + 0 : i - ERTS_ALC_CPOOL_MAX_TRAITOR_INSPECT); + dl = first_old_traitor; + while(dl != &allctr->cpool.traitor_list) { + erts_aint_t exp, act; + crr = (Carrier_t *) (((char *) dl) - offsetof(Carrier_t, cpool.abandoned)); + ASSERT(dl != &allctr->cpool.pooled_list); + ASSERT(crr->cpool.orig_allctr == allctr); + dl = dl->next; + exp = erts_smp_atomic_read_rb(&crr->allctr); + if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) { + if (!(exp & ERTS_CRR_ALCTR_FLG_BUSY) + && erts_atomic_read_nob(&crr->cpool.max_size) >= size) { + /* Try to fetch it... */ + act = erts_smp_atomic_cmpxchg_mb(&crr->allctr, + (erts_aint_t) allctr, + exp); + if (act == exp) { + cpool_delete(allctr, ((Allctr_t *) (act & ~ERTS_CRR_ALCTR_FLG_MASK)), crr); + unlink_abandoned_carrier(crr); + + /* Move sentinel to continue next search from here */ + relink_edl_before(dl, &allctr->cpool.traitor_list); + return crr; + } + exp = act; + } + if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) { + if (!cpool_entrance) + cpool_entrance = &crr->cpool; + + /* Move to pooled_list */ + unlink_abandoned_carrier(crr); + link_abandoned_carrier(&allctr->cpool.pooled_list, crr); + } + } + if (--i <= i_stop) { + /* Move sentinel to continue next search from here */ + relink_edl_before(dl, &allctr->cpool.traitor_list); + if (i > 0) + break; + else + return NULL; + } + } /* - * We search in 'prev' direction and begin by passing - * one element before trying to fetch. This in order to - * avoid contention with threads inserting elements. + * Finally search the shared pool and try employ foreign carriers */ - cpdp = cpool_aint2cpd(cpool_read(&sentinel->prev)); - if (cpdp == sentinel) - return NULL; + sentinel = &carrier_pool[allctr->alloc_no].sentinel; + if (cpool_entrance) { + /* We saw a pooled carried above, use it as entrance into the pool + */ + cpdp = cpool_entrance; + } + else { + /* No pooled carried seen above. Start search at cpool sentinel, + * but begin by passing one element before trying to fetch. + * This in order to avoid contention with threads inserting elements. + */ + cpool_entrance = sentinel; + cpdp = cpool_aint2cpd(cpool_read(&cpool_entrance->prev)); + if (cpdp == sentinel) + return NULL; + } - while (i < ERTS_ALC_CPOOL_MAX_FETCH_INSPECT) { + has_passed_sentinel = 0; + while (1) { erts_aint_t exp; cpdp = cpool_aint2cpd(cpool_read(&cpdp->prev)); - if (cpdp == sentinel) { + if (cpdp == cpool_entrance) { + if (cpool_entrance == sentinel) { + cpdp = cpool_aint2cpd(cpool_read(&cpdp->prev)); + if (cpdp == sentinel) + return NULL; + } + i = 0; /* Last one to inspect */ + } + else if (cpdp == sentinel) { + if (has_passed_sentinel) { + /* We been here before. cpool_entrance must have been removed */ + return NULL; + } cpdp = cpool_aint2cpd(cpool_read(&cpdp->prev)); if (cpdp == sentinel) return NULL; - i = ERTS_ALC_CPOOL_MAX_FETCH_INSPECT; /* Last one to inspect */ + has_passed_sentinel = 1; } - crr = (Carrier_t *) (((char *) cpdp) - offsetof(Carrier_t, cpool)); + crr = (Carrier_t *)(((char *)cpdp) - offsetof(Carrier_t, cpool)); exp = erts_smp_atomic_read_rb(&crr->allctr); - if (((exp & (ERTS_CRR_ALCTR_FLG_IN_POOL|ERTS_CRR_ALCTR_FLG_BUSY)) - == ERTS_CRR_ALCTR_FLG_IN_POOL) + if (((exp & (ERTS_CRR_ALCTR_FLG_MASK)) == ERTS_CRR_ALCTR_FLG_IN_POOL) && (erts_atomic_read_nob(&cpdp->max_size) >= size)) { erts_aint_t act; /* Try to fetch it... */ @@ -2977,11 +3161,35 @@ cpool_fetch(Allctr_t *allctr, UWord size) exp); if (act == exp) { cpool_delete(allctr, ((Allctr_t *) (act & ~ERTS_CRR_ALCTR_FLG_MASK)), crr); + if (crr->cpool.orig_allctr == allctr) { + unlink_abandoned_carrier(crr); + } return crr; } } - i++; + if (--i <= 0) + return NULL; } + + /* Last; check our own pending dealloc carrier list... */ + crr = allctr->cpool.dc_list.last; + while (crr) { + if (erts_atomic_read_nob(&crr->cpool.max_size) >= size) { + unlink_carrier(&allctr->cpool.dc_list, crr); +#ifdef ERTS_ALC_CPOOL_DEBUG + ERTS_ALC_CPOOL_ASSERT(erts_smp_atomic_xchg_nob(&crr->allctr, + ((erts_aint_t) allctr)) + == (((erts_aint_t) allctr) & ~ERTS_CRR_ALCTR_FLG_MASK)); +#else + erts_smp_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr)); +#endif + return crr; + } + crr = crr->prev; + if (--i <= 0) + return NULL; + } + return NULL; } @@ -3078,6 +3286,9 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr) return; } + if (is_abandoned(crr)) + unlink_abandoned_carrier(crr); + if (crr->cpool.thr_prgr == ERTS_THR_PRGR_INVALID || erts_thr_progress_has_reached(crr->cpool.thr_prgr)) { dealloc_carrier(allctr, crr, 1); @@ -3124,6 +3335,8 @@ cpool_init_carrier_data(Allctr_t *allctr, Carrier_t *crr) limit = (csz/100)*allctr->cpool.util_limit; crr->cpool.abandon_limit = limit; } + crr->cpool.abandoned.next = NULL; + crr->cpool.abandoned.prev = NULL; } static void @@ -3154,6 +3367,9 @@ abandon_carrier(Allctr_t *allctr, Carrier_t *crr) STAT_MBC_CPOOL_INSERT(allctr, crr); unlink_carrier(&allctr->mbc_list, crr); + if (crr->cpool.orig_allctr == allctr) { + link_abandoned_carrier(&allctr->cpool.pooled_list, crr); + } allctr->remove_mbc(allctr, crr); @@ -5540,6 +5756,10 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) allctr->min_block_size = sz; } + allctr->cpool.pooled_list.next = &allctr->cpool.pooled_list; + allctr->cpool.pooled_list.prev = &allctr->cpool.pooled_list; + allctr->cpool.traitor_list.next = &allctr->cpool.traitor_list; + allctr->cpool.traitor_list.prev = &allctr->cpool.traitor_list; allctr->cpool.dc_list.first = NULL; allctr->cpool.dc_list.last = NULL; allctr->cpool.abandon_limit = 0; diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index 7be6b1ed9d..eee920e66c 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -268,6 +268,11 @@ typedef union {char c[ERTS_ALLOC_ALIGN_BYTES]; long l; double d;} Unit_t; #ifdef ERTS_SMP +typedef struct ErtsDoubleLink_t_ { + struct ErtsDoubleLink_t_ *next; + struct ErtsDoubleLink_t_ *prev; +}ErtsDoubleLink_t; + typedef struct { erts_atomic_t next; erts_atomic_t prev; @@ -277,6 +282,7 @@ typedef struct { UWord abandon_limit; UWord blocks; UWord blocks_size; + ErtsDoubleLink_t abandoned; /* node in pooled_list or traitor_list */ } ErtsAlcCPoolData_t; #endif @@ -500,7 +506,12 @@ struct Allctr_t_ { CarrierList_t sbc_list; #ifdef ERTS_SMP struct { - CarrierList_t dc_list; + /* pooled_list, traitor list and dc_list contain only + carriers _created_ by this allocator */ + ErtsDoubleLink_t pooled_list; + ErtsDoubleLink_t traitor_list; + CarrierList_t dc_list; + UWord abandon_limit; int disable_abandon; int check_limit_count; -- cgit v1.2.3 From aeb2316c8ee83a1a39a4b179f8b844cb02bf918a Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 6 Oct 2014 14:13:56 +0200 Subject: Specify tar-file as basename in upgrade_SUITE Earlier, the tar-file containing the to-release was specified with absolut path. The correct way to do this is instead to copy the file to the $ROOT/releases directory and only specify the basename of the file in the call to release_handler:unpack_release/1. --- erts/test/upgrade_SUITE.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index d5a920e03d..7b3bc1b063 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -237,7 +237,10 @@ do_upgrade(FromVsn,FromApps,ToRel,ToApps,InstallDir) -> [{"OTP upgrade test",FromVsn,_,permanent}] = rpc:call(Node,release_handler,which_releases,[]), - {ok,ToVsn} = rpc:call(Node,release_handler,unpack_release,[ToRel]), + ToRelName = filename:basename(ToRel), + copy_file(ToRel++".tar.gz", + filename:join([InstallDir,releases,ToRelName++".tar.gz"])), + {ok,ToVsn} = rpc:call(Node,release_handler,unpack_release,[ToRelName]), [{"OTP upgrade test",ToVsn,_,unpacked}, {"OTP upgrade test",FromVsn,_,permanent}] = rpc:call(Node,release_handler,which_releases,[]), -- cgit v1.2.3 From dd853b15e9fb770a3f04fe4504fbfbb8de89e28d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 6 Oct 2014 22:29:44 +0200 Subject: erts: Fix bug causing mbc removed from cpool to be used at pool entrance Clear both IN_POOL and BUSY flags when empty carrier is removed is removed from pool to be destroyed. Earlier it was enough to leave BUSY flag set but now with pooled_list we must clear IN_POOL to avoid using it as cpool_entrance in cpool_fetch(). --- erts/emulator/beam/erl_alloc_util.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index ba16e5c3a3..0ea9b62103 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -3877,6 +3877,11 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk, Carrier_t **busy_pcrr_pp) if (busy_pcrr_pp && *busy_pcrr_pp) { ERTS_ALC_CPOOL_ASSERT(*busy_pcrr_pp == crr); *busy_pcrr_pp = NULL; + ERTS_ALC_CPOOL_ASSERT(erts_smp_atomic_read_nob(&crr->allctr) + == (((erts_aint_t) allctr) + | ERTS_CRR_ALCTR_FLG_IN_POOL + | ERTS_CRR_ALCTR_FLG_BUSY)); + erts_smp_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr)); cpool_delete(allctr, allctr, crr); } else -- cgit v1.2.3 From a5ce823283d36eda4788c55b0b9b645577473983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 22 Sep 2014 17:21:15 +0200 Subject: erts: Fix SMP for ERTS_OPCODE_COUNTER_SUPPORT Cleanup macro code. --- erts/emulator/beam/beam_emu.c | 29 ++++++++++------------------- erts/emulator/beam/erl_init.c | 8 -------- erts/emulator/beam/erl_vm.h | 2 -- 3 files changed, 10 insertions(+), 29 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 8bfb7d2ad2..f02ace9094 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -241,10 +241,6 @@ BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */ void** beam_ops; #endif -#ifndef ERTS_SMP /* Not supported with smp emulator */ -extern int count_instructions; -#endif - #define SWAPIN \ HTOP = HEAP_TOP(c_p); \ E = c_p->stop @@ -1163,13 +1159,14 @@ void process_main(void) Eterm (*arith_func)(Process* p, Eterm* reg, Uint live); -#ifndef NO_JUMP_TABLE - static void* opcodes[] = { DEFINE_OPCODES }; #ifdef ERTS_OPCODE_COUNTER_SUPPORT static void* counting_opcodes[] = { DEFINE_COUNTING_OPCODES }; -#endif +#else +#ifndef NO_JUMP_TABLE + static void* opcodes[] = { DEFINE_OPCODES }; #else int Go; +#endif #endif Uint temp_bits; /* Temporary used by BsSkipBits2 & BsGetInteger2 */ @@ -5145,22 +5142,16 @@ get_map_elements_fail: #ifndef NO_JUMP_TABLE #ifdef ERTS_OPCODE_COUNTER_SUPPORT - /* Are tables correctly generated by beam_makeops? */ ASSERT(sizeof(counting_opcodes) == sizeof(opcodes)); - - if (count_instructions) { #ifdef DEBUG - counting_opcodes[op_catch_end_y] = LabelAddr(lb_catch_end_y); + counting_opcodes[op_catch_end_y] = LabelAddr(lb_catch_end_y); #endif - counting_opcodes[op_i_func_info_IaaI] = LabelAddr(lb_i_func_info_IaaI); - beam_ops = counting_opcodes; - } - else -#endif /* #ifndef ERTS_OPCODE_COUNTER_SUPPORT */ - { - beam_ops = opcodes; - } + counting_opcodes[op_i_func_info_IaaI] = LabelAddr(lb_i_func_info_IaaI); + beam_ops = counting_opcodes; +#else /* #ifndef ERTS_OPCODE_COUNTER_SUPPORT */ + beam_ops = opcodes; +#endif /* ERTS_OPCODE_COUNTER_SUPPORT */ #endif /* NO_JUMP_TABLE */ em_call_error_handler = OpCode(call_error_handler); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 88c4006934..890ef89cc1 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -161,9 +161,6 @@ int H_MIN_SIZE; /* The minimum heap grain */ int BIN_VH_MIN_SIZE; /* The minimum binary virtual*/ Uint32 erts_debug_flags; /* Debug flags. */ -#ifdef ERTS_OPCODE_COUNTER_SUPPORT -int count_instructions; -#endif int erts_backtrace_depth; /* How many functions to show in a backtrace * in error codes. */ @@ -1882,11 +1879,6 @@ erl_start(int argc, char **argv) if (argv[i][2] == 0) { /* -c: documented option */ erts_disable_tolerant_timeofday = 1; } -#ifdef ERTS_OPCODE_COUNTER_SUPPORT - else if (argv[i][2] == 'i') { /* -ci: undcoumented option*/ - count_instructions = 1; - } -#endif break; case 'W': arg = get_arg(argv[i]+2, argv[i+1], &i); diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index b7de8208ad..78d98229d8 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -20,8 +20,6 @@ #ifndef __ERL_VM_H__ #define __ERL_VM_H__ -/* #define ERTS_OPCODE_COUNTER_SUPPORT */ - /* FORCE_HEAP_FRAGS: * Debug provocation to make HAlloc always create heap fragments (if allowed) * even if there is room on heap. -- cgit v1.2.3 From 2e84a2ab5e0f0c82c2d79f69cd2f4bff762762a4 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 26 Jun 2014 18:50:51 +0200 Subject: erts: Make tty driver non-blocking Instead of using blocking call to fwrite, the tty driver now uses non-blocking calls to writev and queues any output data that cannot be written into the driver queue. Without this change an stdout write could block an entire scheduler if for some reason the pseudo tty on the other side does not consume the output of the Erlang shell. OTP-12239 --- erts/emulator/drivers/unix/ttsl_drv.c | 130 ++++++++++++++++++++++++++++++---- erts/emulator/sys/unix/sys.c | 23 ++++-- 2 files changed, 135 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index 491e0a090e..73ce27c6ec 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -32,6 +32,10 @@ static ErlDrvData ttysl_start(ErlDrvPort, char*); #ifdef HAVE_TERMCAP /* else make an empty driver that can not be opened */ +#ifndef WANT_NONBLOCKING +#define WANT_NONBLOCKING +#endif + #include "sys.h" #include #include @@ -39,6 +43,7 @@ static ErlDrvData ttysl_start(ErlDrvPort, char*); #include #include #include +#include #include #include #include @@ -57,6 +62,14 @@ static ErlDrvData ttysl_start(ErlDrvPort, char*); #include #endif +#if defined IOV_MAX +#define MAXIOV IOV_MAX +#elif defined UIO_MAXIOV +#define MAXIOV UIO_MAXIOV +#else +#define MAXIOV 16 +#endif + #define TRUE 1 #define FALSE 0 @@ -85,7 +98,9 @@ static volatile int cols_needs_update = FALSE; #define CTRL_OP_GET_UNICODE_STATE 101 #define CTRL_OP_SET_UNICODE_STATE 102 - +/* We use 1024 as the buf size as that was the default buf size of FILE streams + on all platforms that I checked. */ +#define TTY_BUFFSIZE 1024 static int lbuf_size = BUFSIZ; static Uint32 *lbuf; /* The current line buffer */ @@ -113,13 +128,18 @@ static int lpos; /* The current "cursor position" in the line buf /* Main interface functions. */ static void ttysl_stop(ErlDrvData); static void ttysl_from_erlang(ErlDrvData, char*, ErlDrvSizeT); +static void ttysl_to_tty(ErlDrvData, ErlDrvEvent); +static void ttysl_flush_tty(ErlDrvData); static void ttysl_from_tty(ErlDrvData, ErlDrvEvent); static void ttysl_stop_select(ErlDrvEvent, void*); static Sint16 get_sint16(char*); static ErlDrvPort ttysl_port; static int ttysl_fd; -static FILE *ttysl_out; +static int ttysl_terminate = 0; +static ErlDrvBinary *putcbuf; +static int putcpos; +static int putclen; /* Functions that work on the line buffer. */ static int start_lbuf(void); @@ -201,22 +221,22 @@ struct erl_drv_entry ttsl_driver_entry = { IF_IMPL(ttysl_stop), IF_IMPL(ttysl_from_erlang), IF_IMPL(ttysl_from_tty), - NULL, - "tty_sl", - NULL, - NULL, + IF_IMPL(ttysl_to_tty), + "tty_sl", /* driver_name */ + NULL, /* finish */ + NULL, /* handle */ IF_IMPL(ttysl_control), NULL, /* timeout */ NULL, /* outputv */ NULL, /* ready_async */ - NULL, /* flush */ + IF_IMPL(ttysl_flush_tty), NULL, /* call */ NULL, /* event */ ERL_DRV_EXTENDED_MARKER, ERL_DRV_EXTENDED_MAJOR_VERSION, ERL_DRV_EXTENDED_MINOR_VERSION, 0, /* ERL_DRV_FLAGs */ - NULL, + NULL, /* handle2 */ NULL, /* process_exit */ IF_IMPL(ttysl_stop_select) }; @@ -296,8 +316,7 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) return ERL_DRV_ERROR_GENERAL; } - /* Open the terminal and set the terminal */ - ttysl_out = fdopen(ttysl_fd, "w"); + SET_NONBLOCKING(ttysl_fd); #ifdef PRIMITIVE_UTF8_CHECK setlocale(LC_CTYPE, ""); /* Set international environment, @@ -400,12 +419,14 @@ static void ttysl_stop(ErlDrvData ttysl_data) stop_lbuf(); stop_termcap(); tty_reset(ttysl_fd); - driver_select(ttysl_port, (ErlDrvEvent)(UWord)ttysl_fd, ERL_DRV_READ|ERL_DRV_USE, 0); + driver_select(ttysl_port, (ErlDrvEvent)(UWord)ttysl_fd, + ERL_DRV_WRITE|ERL_DRV_READ|ERL_DRV_USE, 0); sys_sigset(SIGCONT, SIG_DFL); sys_sigset(SIGWINCH, SIG_DFL); } ttysl_port = (ErlDrvPort)-1; ttysl_fd = -1; + ttysl_terminate = 0; /* return TRUE; */ } @@ -650,6 +671,14 @@ static int check_buf_size(byte *s, int n) static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT count) { + ErlDrvSizeT sz; + + sz = driver_sizeq(ttysl_port); + + putclen = count > TTY_BUFFSIZE ? TTY_BUFFSIZE : count; + putcbuf = driver_alloc_binary(putclen); + putcpos = 0; + if (lpos > MAXSIZE) put_chars((byte*)"\n", 1); @@ -678,10 +707,78 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun /* Unknown op, just ignore. */ break; } - fflush(ttysl_out); + + driver_enq_bin(ttysl_port,putcbuf,0,putcpos); + + if (sz == 0) { + for (;;) { + int written, qlen; + SysIOVec *iov; + + iov = driver_peekq(ttysl_port,&qlen); + if (iov) + written = writev(ttysl_fd, iov, qlen > MAXIOV ? MAXIOV : qlen); + else + written = 0; + if (written < 0) { + if (errno == EAGAIN) { + driver_select(ttysl_port,(ErlDrvEvent)(long)ttysl_fd, + ERL_DRV_USE|ERL_DRV_WRITE,1); + break; + } else { + /* we ignore all other errors */ + break; + } + } else { + if (driver_deq(ttysl_port, written) == 0) + break; + } + } + } + return; /* TRUE; */ } +static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) { + for (;;) { + int written, qlen; + SysIOVec *iov; + ErlDrvSizeT sz; + + iov = driver_peekq(ttysl_port,&qlen); + if (iov) + written = writev(ttysl_fd, iov, qlen > MAXIOV ? MAXIOV : qlen); + else + written = 0; + if (written < 0) { + if (errno == EAGAIN) { + break; + } else { + /* we ignore all other errors */ + } + } else { + sz = driver_deq(ttysl_port, written); + if (sz == 0) { + driver_select(ttysl_port,(ErlDrvEvent)(long)ttysl_fd, + ERL_DRV_WRITE,0); + if (ttysl_terminate) + /* flush has been called, which means we should terminate + when queue is empty. This will not send any exit + message */ + driver_failure_atom(ttysl_port, "normal"); + break; + } + } + } + + return; +} + +static void ttysl_flush_tty(ErlDrvData ttysl_data) { + ttysl_terminate = 1; + return; +} + static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) { byte b[1024]; @@ -1070,7 +1167,14 @@ static int write_buf(Uint32 *s, int n) /* The basic procedure for outputting one character. */ static int outc(int c) { - return (int)putc(c, ttysl_out); + putcbuf->orig_bytes[putcpos++] = c; + if (putcpos == putclen) { + driver_enq_bin(ttysl_port,putcbuf,0,putclen); + putcpos = 0; + putclen = TTY_BUFFSIZE; + putcbuf = driver_alloc_binary(BUFSIZ); + } + return 1; } static int move_cursor(int from, int to) diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index c3d7440409..ad8b60fe7c 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef ISC32 #include @@ -2658,18 +2659,30 @@ void sys_preload_end(Preload* p) /* Nothing */ } -/* Read a key from console (?) */ - +/* Read a key from console, used by break.c + Here we assume that all schedulers are stopped so that erl_poll + does not interfere with the select below. +*/ int sys_get_key(fd) int fd; { - int c; + int c, ret; unsigned char rbuf[64]; + fd_set fds; fflush(stdout); /* Flush query ??? */ - if ((c = read(fd,rbuf,64)) <= 0) { - return c; + FD_ZERO(&fds); + FD_SET(fd,&fds); + + ret = select(fd+1, &fds, NULL, NULL, NULL); + + if (ret == 1) { + do { + c = read(fd,rbuf,64); + } while (c < 0 && errno == EAGAIN); + if (c <= 0) + return c; } return rbuf[0]; -- cgit v1.2.3 From 5ab275d6554f4db877624a152dda90c0e3c4cddc Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 30 Jun 2014 17:14:11 +0200 Subject: erts: Make writing to non-tty fds non-blocking If the fd_driver is given non-tty fds it will now use an async thread instead of doing a blocking write. An example of a non-tty fd is the fd used by stderr, but could be anything handed down to the driver. --- erts/emulator/beam/erl_alloc.types | 2 + erts/emulator/sys/unix/sys.c | 230 ++++++++++++++++++++++++++++++++++--- 2 files changed, 217 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 17ac6316b7..d23d26a0fc 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -411,6 +411,8 @@ type CS_PROG_PATH LONG_LIVED SYSTEM cs_prog_path type ENVIRONMENT TEMPORARY SYSTEM environment type PUTENV_STR SYSTEM SYSTEM putenv_string type PRT_REP_EXIT STANDARD SYSTEM port_report_exit +type SYS_BLOCKING STANDARD SYSTEM sys_blocking +type SYS_WRITE_BUF TEMPORARY SYSTEM sys_write_buf +endif diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index ad8b60fe7c..eed93cb2a0 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -92,8 +92,10 @@ static erts_smp_rwmtx_t environ_rwmtx; # else # define CHLDWTHR 0 # endif +# define FDBLOCK 1 #else # define CHLDWTHR 0 +# define FDBLOCK 0 #endif /* * [OTP-3906] @@ -122,6 +124,15 @@ struct ErtsSysReportExit_ { #endif }; +/* Used by the fd driver iff the fd could not be set to non-blocking */ +typedef struct ErtsSysBlocking_ { + ErlDrvPDL pdl; + int res; + int err; + unsigned int pkey; +} ErtsSysBlocking; + + /* This data is shared by these drivers - initialized by spawn_init() */ static struct driver_data { ErlDrvPort port_num; @@ -130,6 +141,8 @@ static struct driver_data { int pid; int alive; int status; + int terminating; + ErtsSysBlocking *blocking; } *driver_data; /* indexed by fd */ static ErtsSysReportExit *report_exit_list; @@ -1109,11 +1122,16 @@ void fini_getenv_state(GETENV_STATE *state) /* Driver interfaces */ static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*); static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*); +#if FDBLOCK +static void fd_async(void *); +static void fd_ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data); +#endif static ErlDrvSSizeT fd_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT, char **, ErlDrvSizeT); static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*); static int spawn_init(void); static void fd_stop(ErlDrvData); +static void fd_flush(ErlDrvData); static void stop(ErlDrvData); static void ready_input(ErlDrvData, ErlDrvEvent); static void ready_output(ErlDrvData, ErlDrvEvent); @@ -1158,8 +1176,12 @@ struct erl_drv_entry fd_driver_entry = { fd_control, NULL, outputv, - NULL, /* ready_async */ - NULL, /* flush */ +#if FDBLOCK + fd_ready_async, /* ready_async */ +#else + NULL, +#endif + fd_flush, /* flush */ NULL, /* call */ NULL, /* event */ ERL_DRV_EXTENDED_MARKER, @@ -1213,13 +1235,28 @@ static RETSIGTYPE onchld(int signum) #endif } +static int set_blocking_data(struct driver_data *dd) { + + dd->blocking = erts_alloc(ERTS_ALC_T_SYS_BLOCKING, sizeof(ErtsSysBlocking)); + + erts_smp_atomic_add_nob(&sys_misc_mem_sz, sizeof(ErtsSysBlocking)); + + dd->blocking->pdl = driver_pdl_create(dd->port_num); + dd->blocking->res = 0; + dd->blocking->err = 0; + dd->blocking->pkey = driver_async_port_key(dd->port_num); + + return 1; +} + static int set_driver_data(ErlDrvPort port_num, int ifd, int ofd, int packet_bytes, int read_write, int exit_status, - int pid) + int pid, + int is_blocking) { Port *prt; ErtsSysReportExit *report_exit; @@ -1251,8 +1288,13 @@ static int set_driver_data(ErlDrvPort port_num, driver_data[ifd].pid = pid; driver_data[ifd].alive = 1; driver_data[ifd].status = 0; + driver_data[ifd].terminating = 0; + driver_data[ifd].blocking = NULL; if (read_write & DO_WRITE) { driver_data[ifd].ofd = ofd; + if (is_blocking && FDBLOCK) + if (!set_blocking_data(driver_data+ifd)) + return -1; if (ifd != ofd) driver_data[ofd] = driver_data[ifd]; /* structure copy */ } else { /* DO_READ only */ @@ -1268,6 +1310,11 @@ static int set_driver_data(ErlDrvPort port_num, driver_data[ofd].pid = pid; driver_data[ofd].alive = 1; driver_data[ofd].status = 0; + driver_data[ofd].terminating = 0; + driver_data[ofd].blocking = NULL; + if (is_blocking && FDBLOCK) + if (!set_blocking_data(driver_data+ofd)) + return -1; return(ofd); } } @@ -1277,6 +1324,7 @@ static int spawn_init() int i; #if CHLDWTHR erts_thr_opts_t thr_opts = ERTS_THR_OPTS_DEFAULT_INITER; + thr_opts.detached = 0; thr_opts.suggested_stack_size = 0; /* Smallest possible */ #endif @@ -1756,7 +1804,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op } res = set_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes, - opts->read_write, opts->exit_status, pid); + opts->read_write, opts->exit_status, pid, 0); /* Don't unblock SIGCHLD until now, since the call above must first complete putting away the info about our new subprocess. */ unblock_signals(); @@ -1841,6 +1889,7 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) { ErlDrvData res; + int non_blocking = 0; if (((opts->read_write & DO_READ) && opts->ifd >= max_files) || ((opts->read_write & DO_WRITE) && opts->ofd >= max_files)) @@ -1913,6 +1962,20 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name, * case - it can be called with any old pre-existing file descriptors, * the relations between which (if they're even two) we can only guess * at - still, we try our best... + * + * Added note OTP 18: Some systems seem to use stdout/stderr to log data + * using unix pipes, so we cannot allow the system to block on a write. + * Therefore we use an async thread to write the data to fd's that could + * not be set to non-blocking. When no async threads are available we + * fall back on the old behaviour. + * + * Also the guarantee about what is delivered to the OS has changed. + * Pre 18 the fd driver did no flushing of data before terminating. + * Now it does. This is because we want to be able to guarantee that things + * such as escripts and friends really have outputted all data before + * terminating. This could potentially block the termination of the system + * for a very long time, but if the user wants to terminate fast she should + * use erlang:halt with flush=false. */ if (opts->read_write & DO_READ) { @@ -1935,6 +1998,7 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name, imagine a scenario where setting non-blocking mode here would cause problems - go ahead and do it. */ + non_blocking = 1; SET_NONBLOCKING(opts->ofd); } else { /* output fd is a tty, input fd isn't */ @@ -1977,6 +2041,7 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name, (nfd = open(tty, O_WRONLY)) != -1) { dup2(nfd, opts->ofd); close(nfd); + non_blocking = 1; SET_NONBLOCKING(opts->ofd); } } @@ -1985,8 +2050,9 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name, } CHLD_STAT_LOCK; res = (ErlDrvData)(long)set_driver_data(port_num, opts->ifd, opts->ofd, - opts->packet_bytes, - opts->read_write, 0, -1); + opts->packet_bytes, + opts->read_write, 0, -1, + !non_blocking); CHLD_STAT_UNLOCK; return res; } @@ -2012,14 +2078,30 @@ static void nbio_stop_fd(ErlDrvPort prt, int fd) SET_BLOCKING(fd); } -static void fd_stop(ErlDrvData fd) /* Does not close the fds */ +static void fd_stop(ErlDrvData ev) /* Does not close the fds */ { int ofd; + int fd = (int)(long)ev; + ErlDrvPort prt = driver_data[fd].port_num; - nbio_stop_fd(driver_data[(int)(long)fd].port_num, (int)(long)fd); - ofd = driver_data[(int)(long)fd].ofd; - if (ofd != (int)(long)fd && ofd != -1) - nbio_stop_fd(driver_data[(int)(long)fd].port_num, (int)(long)ofd); +#if FDBLOCK + if (driver_data[fd].blocking) { + erts_free(ERTS_ALC_T_SYS_BLOCKING,driver_data[fd].blocking); + driver_data[fd].blocking = NULL; + erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*sizeof(ErtsSysBlocking)); + } +#endif + + nbio_stop_fd(prt, fd); + ofd = driver_data[fd].ofd; + if (ofd != fd && ofd != -1) + nbio_stop_fd(prt, ofd); +} + +static void fd_flush(ErlDrvData fd) +{ + if (!driver_data[(int)(long)fd].terminating) + driver_data[(int)(long)fd].terminating = 1; } static ErlDrvData vanilla_start(ErlDrvPort port_num, char* name, @@ -2042,8 +2124,8 @@ static ErlDrvData vanilla_start(ErlDrvPort port_num, char* name, CHLD_STAT_LOCK; res = (ErlDrvData)(long)set_driver_data(port_num, fd, fd, - opts->packet_bytes, - opts->read_write, 0, -1); + opts->packet_bytes, + opts->read_write, 0, -1, 0); CHLD_STAT_UNLOCK; return res; } @@ -2080,6 +2162,7 @@ static void stop(ErlDrvData fd) } } +/* used by fd_driver */ static void outputv(ErlDrvData e, ErlIOVec* ev) { int fd = (int)(long)e; @@ -2105,12 +2188,21 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) ev->iov[0].iov_base = lbp; ev->iov[0].iov_len = pb; ev->size += pb; + + if (driver_data[fd].blocking && FDBLOCK) + driver_pdl_lock(driver_data[fd].blocking->pdl); + if ((sz = driver_sizeq(ix)) > 0) { driver_enqv(ix, ev, 0); + + if (driver_data[fd].blocking && FDBLOCK) + driver_pdl_unlock(driver_data[fd].blocking->pdl); + if (sz + ev->size >= (1 << 13)) set_busy_port(ix, 1); } - else { + else if (!driver_data[fd].blocking || !FDBLOCK) { + /* We try to write directly if the fd in non-blocking */ int vsize = ev->vsize > MAX_VSIZE ? MAX_VSIZE : ev->vsize; n = writev(ofd, (const void *) (ev->iov), vsize); @@ -2126,10 +2218,22 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) driver_enqv(ix, ev, n); /* n is the skip value */ driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1); } +#if FDBLOCK + else { + if (ev->size != 0) { + driver_enqv(ix, ev, 0); + driver_pdl_unlock(driver_data[fd].blocking->pdl); + driver_async(ix, &driver_data[fd].blocking->pkey, + fd_async, driver_data+fd, NULL); + } else { + driver_pdl_unlock(driver_data[fd].blocking->pdl); + } + } +#endif /* return 0;*/ } - +/* Used by spawn_driver and vanilla driver */ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) { int fd = (int)(long)e; @@ -2192,6 +2296,23 @@ static int port_inp_failure(ErlDrvPort port_num, int ready_fd, int res) ASSERT(res <= 0); (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0); clear_fd_data(ready_fd); + + if (driver_data[ready_fd].blocking && FDBLOCK) { + driver_pdl_lock(driver_data[ready_fd].blocking->pdl); + if (driver_sizeq(driver_data[ready_fd].port_num) > 0) { + driver_pdl_unlock(driver_data[ready_fd].blocking->pdl); + /* We have stuff in the output queue, so we just + set the state to terminating and wait for fd_async_ready + to terminate the port */ + if (res == 0) + driver_data[ready_fd].terminating = 2; + else + driver_data[ready_fd].terminating = -err; + return 0; + } + driver_pdl_unlock(driver_data[ready_fd].blocking->pdl); + } + if (res == 0) { if (driver_data[ready_fd].report_exit) { CHLD_STAT_LOCK; @@ -2242,6 +2363,7 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) port_num = driver_data[fd].port_num; packet_bytes = driver_data[fd].packet_bytes; + if (packet_bytes == 0) { byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, ERTS_SYS_READ_BUF_SZ); @@ -2365,6 +2487,8 @@ static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) if ((iv = (struct iovec*) driver_peekq(ix, &vsize)) == NULL) { driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); + if (driver_data[fd].terminating) + driver_failure_atom(driver_data[fd].port_num,"normal"); return; /* 0; */ } vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize; @@ -2390,6 +2514,82 @@ static void stop_select(ErlDrvEvent fd, void* _) close((int)fd); } +#if FDBLOCK + +static void +fd_async(void *async_data) +{ + int res; + struct driver_data *dd = (struct driver_data*)async_data; + SysIOVec *iov0; + SysIOVec *iov; + int iovlen; + int iovcnt; + int p; + /* much of this code is stolen from efile_drv:invoke_writev */ + driver_pdl_lock(dd->blocking->pdl); + iov0 = driver_peekq(dd->port_num, &iovlen); + /* Calculate iovcnt */ + for (p = 0, iovcnt = 0; iovcnt < iovlen; + p += iov0[iovcnt++].iov_len) + ; + iov = erts_alloc_fnf(ERTS_ALC_T_SYS_WRITE_BUF, + sizeof(SysIOVec)*iovcnt); + if (!iov) { + res = -1; + errno = ENOMEM; + erts_free(ERTS_ALC_T_SYS_WRITE_BUF, iov); + driver_pdl_unlock(dd->blocking->pdl); + } else { + memcpy(iov,iov0,iovcnt*sizeof(SysIOVec)); + driver_pdl_unlock(dd->blocking->pdl); + + res = writev(dd->ofd, iov, iovlen); + + erts_free(ERTS_ALC_T_SYS_WRITE_BUF, iov); + } + dd->blocking->res = res; + dd->blocking->err = errno; +} + +void fd_ready_async(ErlDrvData drv_data, + ErlDrvThreadData thread_data) { + struct driver_data *dd = (struct driver_data *)thread_data; + ErlDrvPort port_num = dd->port_num; + + ASSERT(dd->blocking); + ASSERT(dd == (driver_data + (int)(long)drv_data)); + + if (dd->blocking->res > 0) { + driver_pdl_lock(dd->blocking->pdl); + if (driver_deq(port_num, dd->blocking->res) == 0) { + driver_pdl_unlock(dd->blocking->pdl); + set_busy_port(port_num, 0); + if (dd->terminating) { + /* The port is has been ordered to terminate + from either fd_flush or port_inp_failure */ + if (dd->terminating == 1) + driver_failure_atom(port_num, "normal"); + else if (dd->terminating == 2) + driver_failure_eof(port_num); + else if (dd->terminating < 0) + driver_failure_posix(port_num, -dd->terminating); + return; /* -1; */ + } + } else { + driver_pdl_unlock(dd->blocking->pdl); + /* still data left to write in queue */ + driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL); + return /* 0; */; + } + } else if (dd->blocking->res < 0) { + driver_failure_posix(port_num, dd->blocking->err); + return; /* -1; */ + } + return; /* 0; */ +} + +#endif void erts_do_break_handling(void) { -- cgit v1.2.3 From 48083e54b502afb2768066394074d29423162dc8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 26 Jun 2014 18:55:47 +0200 Subject: kernel,ssh: Add synchronous user_drv protocol Added a put_chars_sync to the protocol that can be used to talk to user_drv and made group use it. This is needed in order to guarantee that bytes has been pushed to the tty port when doing something like this: io:format("halting\n"),erlang:halt(0). Before this change the halting message could be lost in the message queue of the user_drv process, this is no longer possible. This commit also fixes ssh_cli as that plugs itself in as a user_drv process. OTP-12240 --- erts/emulator/drivers/unix/ttsl_drv.c | 36 ++++++++++++++++++++++++++++++++++ erts/emulator/drivers/win32/ttsl_drv.c | 16 +++++++++++++++ 2 files changed, 52 insertions(+) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index 73ce27c6ec..be2fee1f25 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -93,6 +93,7 @@ static volatile int cols_needs_update = FALSE; #define OP_INSC 2 #define OP_DELC 3 #define OP_BEEP 4 +#define OP_PUTC_SYNC 5 /* Control op */ #define CTRL_OP_GET_WINSIZE 100 #define CTRL_OP_GET_UNICODE_STATE 101 @@ -137,6 +138,7 @@ static Sint16 get_sint16(char*); static ErlDrvPort ttysl_port; static int ttysl_fd; static int ttysl_terminate = 0; +static int ttysl_send_ok = 0; static ErlDrvBinary *putcbuf; static int putcpos; static int putclen; @@ -683,6 +685,14 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun put_chars((byte*)"\n", 1); switch (buf[0]) { + case OP_PUTC_SYNC: + /* Using sync means that we have to send an ok to the + controlling process for each command call. We delay + sending ok if the driver queue exceeds a certain size. + We do not set ourselves as a busy port, as this + could be very bad for user_drv, if it gets blocked on + the port_command. */ + /* fall through */ case OP_PUTC: DEBUGLOG(("OP: Putc(%lu)",(unsigned long) count-1)); if (check_buf_size((byte*)buf+1, count-1) == 0) @@ -736,6 +746,22 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun } } + if (buf[0] == OP_PUTC_SYNC) { + if (driver_sizeq(ttysl_port) > TTY_BUFFSIZE && !ttysl_terminate) { + /* We delay sending the ack until the buffer has been consumed */ + ttysl_send_ok = 1; + } else { + ErlDrvTermData spec[] = { + ERL_DRV_PORT, driver_mk_port(ttysl_port), + ERL_DRV_ATOM, driver_mk_atom("ok"), + ERL_DRV_TUPLE, 2 + }; + ASSERT(ttysl_send_ok == 0); + erl_drv_output_term(driver_mk_port(ttysl_port), spec, + sizeof(spec) / sizeof(spec[0])); + } + } + return; /* TRUE; */ } @@ -758,6 +784,16 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) { } } else { sz = driver_deq(ttysl_port, written); + if (sz < TTY_BUFFSIZE && ttysl_send_ok) { + ErlDrvTermData spec[] = { + ERL_DRV_PORT, driver_mk_port(ttysl_port), + ERL_DRV_ATOM, driver_mk_atom("ok"), + ERL_DRV_TUPLE, 2 + }; + ttysl_send_ok = 0; + erl_drv_output_term(driver_mk_port(ttysl_port), spec, + sizeof(spec) / sizeof(spec[0])); + } if (sz == 0) { driver_select(ttysl_port,(ErlDrvEvent)(long)ttysl_fd, ERL_DRV_WRITE,0); diff --git a/erts/emulator/drivers/win32/ttsl_drv.c b/erts/emulator/drivers/win32/ttsl_drv.c index 502cb58dfa..851c336a11 100644 --- a/erts/emulator/drivers/win32/ttsl_drv.c +++ b/erts/emulator/drivers/win32/ttsl_drv.c @@ -46,6 +46,7 @@ static int rows; /* Number of rows available. */ #define OP_INSC 2 #define OP_DELC 3 #define OP_BEEP 4 +#define OP_PUTC_SYNC 5 /* Control op */ #define CTRL_OP_GET_WINSIZE 100 @@ -458,6 +459,7 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun switch (buf[0]) { case OP_PUTC: + case OP_PUTC_SYNC: DEBUGLOG(("OP: Putc(%I64u)",(unsigned long long)count-1)); if (check_buf_size((byte*)buf+1, count-1) == 0) return; @@ -481,6 +483,20 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun /* Unknown op, just ignore. */ break; } + + if (buf[0] == OP_PUTC_SYNC) { + /* On windows we do a blocking write to the tty so we just + send the ack immidiately. If at some point in the future + someone has a problem with tty output being blocking + this has to be changed. */ + ErlDrvTermData spec[] = { + ERL_DRV_PORT, driver_mk_port(ttysl_port), + ERL_DRV_ATOM, driver_mk_atom("ok"), + ERL_DRV_TUPLE, 2 + }; + erl_drv_output_term(driver_mk_port(ttysl_port), spec, + sizeof(spec) / sizeof(spec[0])); + } return; } -- cgit v1.2.3 From 91f9cfb68ea08adf1fa230027ec4b9404b9e6f43 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 17 Oct 2014 14:30:30 +0200 Subject: Optimize atomic ops with release barrier for 32-bit PowerPC --- erts/include/internal/ppc32/atomic.h | 146 ++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/include/internal/ppc32/atomic.h b/erts/include/internal/ppc32/atomic.h index 6001620677..b558626b09 100644 --- a/erts/include/internal/ppc32/atomic.h +++ b/erts/include/internal/ppc32/atomic.h @@ -91,6 +91,20 @@ ethr_native_atomic32_add_return_acqb(ethr_native_atomic32_t *var, ethr_sint32_t return res; } + +#ifndef ETHR_PPC_HAVE_NO_LWSYNC + +#define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_ADD_RETURN_RELB 1 + +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_add_return_relb(ethr_native_atomic32_t *var, ethr_sint32_t incr) +{ + ethr_lwsync__(); + return ethr_native_atomic32_add_return(var, incr); +} + +#endif + #define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_INC_RETURN 1 static ETHR_INLINE ethr_sint32_t @@ -120,7 +134,19 @@ ethr_native_atomic32_inc_return_acqb(ethr_native_atomic32_t *var) __asm__ __volatile("isync\n\t" : : : "memory"); return res; } - + +#ifndef ETHR_PPC_HAVE_NO_LWSYNC + +#define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_INC_RETURN_RELB 1 + +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_inc_return_relb(ethr_native_atomic32_t *var) +{ + ethr_lwsync__(); + return ethr_native_atomic32_inc_return(var); +} + +#endif #define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_DEC_RETURN 1 @@ -152,6 +178,19 @@ ethr_native_atomic32_dec_return_acqb(ethr_native_atomic32_t *var) return res; } +#ifndef ETHR_PPC_HAVE_NO_LWSYNC + +#define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_DEC_RETURN_RELB 1 + +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_dec_return_relb(ethr_native_atomic32_t *var) +{ + ethr_lwsync__(); + return ethr_native_atomic32_dec_return(var); +} + +#endif + #define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_AND_RETOLD 1 static ETHR_INLINE ethr_sint32_t @@ -182,6 +221,19 @@ ethr_native_atomic32_and_retold_acqb(ethr_native_atomic32_t *var, ethr_sint32_t return res; } +#ifndef ETHR_PPC_HAVE_NO_LWSYNC + +#define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_AND_RETOLD_RELB 1 + +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_and_retold_relb(ethr_native_atomic32_t *var, ethr_sint32_t mask) +{ + ethr_lwsync__(); + return ethr_native_atomic32_and_retold(var, mask); +} + +#endif + #define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_OR_RETOLD 1 static ETHR_INLINE ethr_sint32_t @@ -212,6 +264,18 @@ ethr_native_atomic32_or_retold_acqb(ethr_native_atomic32_t *var, ethr_sint32_t m return res; } +#ifndef ETHR_PPC_HAVE_NO_LWSYNC + +#define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_OR_RETOLD_RELB 1 + +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_or_retold_relb(ethr_native_atomic32_t *var, ethr_sint32_t mask) +{ + ethr_lwsync__(); + return ethr_native_atomic32_or_retold(var, mask); +} + +#endif #define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_XCHG 1 @@ -242,6 +306,19 @@ ethr_native_atomic32_xchg_acqb(ethr_native_atomic32_t *var, ethr_sint32_t val) return res; } +#ifndef ETHR_PPC_HAVE_NO_LWSYNC + +#define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_XCHG_RELB 1 + +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_xchg_relb(ethr_native_atomic32_t *var, ethr_sint32_t val) +{ + ethr_lwsync__(); + return ethr_native_atomic32_xchg(var, val); +} + +#endif + #define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_CMPXCHG 1 static ETHR_INLINE ethr_sint32_t @@ -291,6 +368,73 @@ ethr_native_atomic32_cmpxchg_acqb(ethr_native_atomic32_t *var, return old; } +#if !defined(ETHR_DISABLE_LWSYNC_FOR_CMPXCHG_RELB) && !defined(ETHR_PPC_HAVE_NO_LWSYNC) + +#define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_CMPXCHG_RELB 1 + +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_cmpxchg_relb(ethr_native_atomic32_t *var, + ethr_sint32_t new, + ethr_sint32_t expected) +{ + ethr_sint32_t actual; + + /* + * We want to implement the release barrier using the + * 'lwsync' instruction instead of using the more + * expensive 'sync' instruction. + * + * cmpxchg looks something like this: + * + * lwarx # Load + * ... + * if (fail) + * goto done; + * stwcx # Store + * if (fail) + * goto done; + * ... + * + * In the case we succeeded, 'lwsync' will have + * ordered all previously issued loads and stores + * against the successful store to this variable. + * That is everything is fine! + * + * In the case we did not succeed, we need to order + * all previously issued loads and stores against + * the load of this variable. 'lwsync' does not + * guarantee this. In order to solve this we issue + * a 'sync' and redo the load. If the value has + * changed to what the user passed as expected value + * we need to try the cmpxchg operation again, since + * this value indicates success. + */ + + ethr_lwsync__(); + + actual = ethr_native_atomic32_cmpxchg(var, new, expected); + +#ifndef ETHR_PPC_HAVE_LWSYNC + /* We checked for lwsync support in runtime... */ + if (ETHR_PPC_RUNTIME_CONF_HAVE_NO_LWSYNC__) + return actual; /* No need to; ethr_lwsync__() issued a sync... */ +#endif + + /* ethr_lwsync__() issued an lwsync... */ + if (actual == expected) + return actual; /* Successful operation */ + + /* Failure... need to issue a sync... */ + ethr_sync__(); + actual = ethr_native_atomic32_read(var); + if (actual != expected) + return actual; /* Fail... */ + /* Try again... */ + return ethr_native_atomic32_cmpxchg(var, new, expected); +} + +#endif + #endif /* ETHR_TRY_INLINE_FUNCS */ #endif /* ETHREAD_PPC_ATOMIC_H */ -- cgit v1.2.3 From 024ad413bd20219e92c7642e4c29689886bf96ce Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Fri, 17 Oct 2014 15:50:41 -0400 Subject: Clarify docs for driver_async() return value The documentation for driver_async() still referred to the return value of the function as a "handle", which is no longer meaningful since driver_async_cancel() was deprecated and removed. Modify the documentation to simply indicate that the driver_async() return value will be -1 if it fails. --- erts/doc/src/erl_driver.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 4a1aab75c7..77fc906aca 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -2033,7 +2033,8 @@ ERL_DRV_MAP int sz entry function is called. If ready_async is null in the driver entry, the async_free function is called instead.

-

The return value is a handle to the asynchronous task.

+

The return value is -1 if the driver_async call + fails.

As of erts version 5.5.4.3 the default stack size for threads in the async-thread pool is 16 kilowords, -- cgit v1.2.3 From 0aa4dc4f66815aa4b40e8b3b2d8b9a24c180debe Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Fri, 17 Oct 2014 17:21:37 -0400 Subject: Clarify the use of SIGUSR1 for forcing crash dumps The crash_dump document mentions indirectly that delivering a SIGUSR1 to a running emulator process can force it to crash dump. Clarify that text to make it clear SIGUSR1 can be used for that purpose, and also add a similar note about using SIGUSR1 to the erl documentation. --- erts/doc/src/crash_dump.xml | 5 +++-- erts/doc/src/erl.xml | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index d3de29b876..2b5fc877c3 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -115,8 +115,9 @@ sockets/pipes can be used simultaneously by Erlang (due to limitations in the Unix call). The number of open regular files is not affected by this. - "Received SIGUSR1" - The SIGUSR1 signal was sent to the - Erlang machine (Unix only). + "Received SIGUSR1" - Sending the SIGUSR1 signal to a + Erlang machine (Unix only) forces a crash dump. This slogan reflects + that the Erlang machine crash-dumped due to receiving that signal. "Kernel pid terminated (Who) (Exit-reason)" - The kernel supervisor has detected a failure, usually that the diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index f856b9ab86..e85519185f 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -525,7 +525,8 @@ core dump and no crash dump if an internal error is detected.

Calling erlang:halt/1 with a string argument will still - produce a crash dump.

+ produce a crash dump. On Unix systems, sending an emulator process + a SIGUSR1 signal will also force a crash dump.

-- cgit v1.2.3 From c8ae3f2797d9c613e07b916683c51efea2da81aa Mon Sep 17 00:00:00 2001 From: Paul Guyot Date: Sun, 19 Oct 2014 17:21:09 +0200 Subject: Fix bug in FreeBSD topology detection code Code incorrectly relied on undefined C behavior. Clang and gcc 4.9 do not behave as code expected. This affected cores and processors detection on FreeBSD. --- erts/lib_src/common/erl_misc_utils.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c index d58a28b5cb..7833dd8219 100644 --- a/erts/lib_src/common/erl_misc_utils.c +++ b/erts/lib_src/common/erl_misc_utils.c @@ -1515,7 +1515,7 @@ const char* parse_topology_spec_group(erts_cpu_info_t *cpuinfo, const char* xml, if (is_thread_group) { thread++; } else { - *core_p = (*core_p)++; + *core_p = (*core_p) + 1; } index_procs++; } @@ -1535,9 +1535,9 @@ const char* parse_topology_spec_group(erts_cpu_info_t *cpuinfo, const char* xml, if (parentCacheLevel == 0) { *core_p = 0; - *processor_p = (*processor_p)++; + *processor_p = (*processor_p) + 1; } else { - *core_p = (*core_p)++; + *core_p = (*core_p) + 1; } if (error) -- cgit v1.2.3 From 3ca75c7e42ab2c0612ceb5e56f60e7932c40f335 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 20 Oct 2014 16:04:44 +0200 Subject: erts: Fix bug causing mbc to be deleted from cpool before it was inserted Set IN_POOL flag _after_ mbc has been actually inserted. Earlier it did not matter if IN_POOL was set early as it was not possible to find a carrier before is was fully inserted. Now when searching pooled_list and traitor_list we must make sure a found carrier has been fully inserted into cpool before removing it. --- erts/emulator/beam/erl_alloc_util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 0ea9b62103..e3172dc4fb 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -2835,9 +2835,6 @@ cpool_insert(Allctr_t *allctr, Carrier_t *crr) (erts_aint_t) CARRIER_SZ(crr)); erts_atomic_inc_nob(&allctr->cpool.stat.no_carriers); - erts_smp_atomic_set_nob(&crr->allctr, - ((erts_aint_t) allctr)|ERTS_CRR_ALCTR_FLG_IN_POOL); - /* * We search in 'next' direction and begin by passing * one element before trying to insert. This in order to @@ -2896,6 +2893,9 @@ cpool_insert(Allctr_t *allctr, Carrier_t *crr) cpool_set_mod_marked(&cpd2p->prev, (erts_aint_t) &crr->cpool, (erts_aint_t) cpd1p); + + erts_smp_atomic_set_wb(&crr->allctr, + ((erts_aint_t) allctr)|ERTS_CRR_ALCTR_FLG_IN_POOL); } static void -- cgit v1.2.3 From 5a87432bf4273e6e7619f24de1781c51bf6dbd95 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 28 Aug 2014 21:01:31 +0200 Subject: erts: Fix hipe bug when gc-disabling bif traps with gc enabled The trap_frame got pushed twice, first by the wrapper then by hipe_push_beam_trap_frame as it was looking at F_DISABLE_GC. --- erts/emulator/hipe/hipe_mode_switch.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 4ddc2790b1..38d58ef744 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -2,7 +2,7 @@ * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -195,25 +195,26 @@ void hipe_reserve_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) } p->stop -= 2; p->stop[0] = NIL; - p->stop[1] = NIL; + p->stop[1] = hipe_beam_catch_throw; } static __inline__ void hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) { - if (p->flags & F_DISABLE_GC) { + if (&p->stop[1] < p->hend && p->stop[1] == hipe_beam_catch_throw) { /* Trap frame already reserved */ - ASSERT(p->stop[0] == NIL && p->stop[1] == NIL); + ASSERT(p->stop[0] == NIL); } else { + ASSERT(!(p->flags & F_DISABLE_GC)); if ((p->stop - 2) < p->htop) { DPRINTF("calling gc to increase BEAM stack size"); p->fcalls -= erts_garbage_collect(p, 2, reg, arity); ASSERT(!((p->stop - 2) < p->htop)); } p->stop -= 2; + p->stop[1] = hipe_beam_catch_throw; } - p->stop[1] = hipe_beam_catch_throw; p->stop[0] = make_cp(p->cp); ++p->catches; p->cp = hipe_beam_pc_return; @@ -221,12 +222,13 @@ hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) void hipe_unreserve_beam_trap_frame(Process *p) { - ASSERT(p->stop[0] == NIL && p->stop[1] == NIL); + ASSERT(p->stop[0] == NIL && p->stop[1] == hipe_beam_catch_throw); p->stop += 2; } static __inline__ void hipe_pop_beam_trap_frame(Process *p) { + ASSERT(p->stop[1] == hipe_beam_catch_throw); p->cp = cp_val(p->stop[0]); --p->catches; p->stop += 2; -- cgit v1.2.3 From bc59e9a04bf446b3a73f20ca0e91c122de7aa571 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 10 Oct 2014 17:42:13 +0200 Subject: erts: Fix bug when hipe tailcalls trapping BIF that disables GC Symptom: base64_SUITE:roundtrip crashes with hipe compiled stdlib. Problem: HIPE_WRAPPER_BIF_DISABLE_GC pushed a "trap frame", but the frame was only popped if the call was recursive. Solution: Only reserve "trap frame" if BIF call is recursive. --- erts/emulator/hipe/hipe_mode_switch.c | 6 ++++++ erts/emulator/hipe/hipe_risc_glue.h | 8 ++++++++ erts/emulator/hipe/hipe_x86_glue.h | 8 ++++++++ 3 files changed, 22 insertions(+) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 38d58ef744..1ae1d17b7f 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -187,6 +187,9 @@ void hipe_set_call_trap(Uint *bfun, void *nfun, int is_closure) void hipe_reserve_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) { + if (!hipe_bifcall_from_native_is_recursive(p)) + return; + /* ensure that at least 2 words are available on the BEAM stack */ if ((p->stop - 2) < p->htop) { DPRINTF("calling gc to reserve BEAM stack size"); @@ -222,6 +225,9 @@ hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) void hipe_unreserve_beam_trap_frame(Process *p) { + if (!hipe_bifcall_from_native_is_recursive(p)) + return; + ASSERT(p->stop[0] == NIL && p->stop[1] == hipe_beam_catch_throw); p->stop += 2; } diff --git a/erts/emulator/hipe/hipe_risc_glue.h b/erts/emulator/hipe/hipe_risc_glue.h index cc2671c016..dbb7086dae 100644 --- a/erts/emulator/hipe/hipe_risc_glue.h +++ b/erts/emulator/hipe/hipe_risc_glue.h @@ -214,6 +214,14 @@ hipe_trap_from_native_is_recursive(Process *p) return 0; } +/* Native called BIF. Is it a recursive call? + i.e should we return back to native when BIF is done? */ +static __inline__ int +hipe_bifcall_from_native_is_recursive(Process *p) +{ + return (p->hipe.nra != (void(*)(void))&nbif_return); +} + /* Native makes a call which needs to unload the parameters. This differs from hipe_call_from_native_is_recursive() in diff --git a/erts/emulator/hipe/hipe_x86_glue.h b/erts/emulator/hipe/hipe_x86_glue.h index 63ad250d60..4b6e495b9a 100644 --- a/erts/emulator/hipe/hipe_x86_glue.h +++ b/erts/emulator/hipe/hipe_x86_glue.h @@ -207,6 +207,14 @@ hipe_trap_from_native_is_recursive(Process *p) return 0; } +/* Native called BIF. Is it a recursive call? + i.e should we return back to native when BIF is done? */ +static __inline__ int +hipe_bifcall_from_native_is_recursive(Process *p) +{ + return (*p->hipe.nsp != (Eterm)nbif_return); +} + /* Native makes a call which needs to unload the parameters. This differs from hipe_call_from_native_is_recursive() in -- cgit v1.2.3 From e45a4a8a3797a637a23ad5d660138c021f3ba1b1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 29 Aug 2014 16:06:20 +0200 Subject: erts: Make hipe_{un}reserve_beam_trap_frame INLINE --- erts/emulator/beam/bif.c | 2 ++ erts/emulator/beam/bif.h | 3 +++ erts/emulator/beam/binary.c | 2 ++ erts/emulator/beam/copy.c | 2 ++ erts/emulator/beam/erl_bif_binary.c | 2 ++ erts/emulator/beam/erl_gc.c | 2 ++ erts/emulator/beam/erl_gc.h | 35 ++++++++++++++++++++++--- erts/emulator/beam/erl_process.h | 6 +---- erts/emulator/beam/external.c | 2 ++ erts/emulator/beam/global.h | 18 +------------ erts/emulator/beam/utils.c | 3 +++ erts/emulator/hipe/hipe_debug.c | 2 ++ erts/emulator/hipe/hipe_gc.c | 3 +++ erts/emulator/hipe/hipe_mode_switch.c | 36 +------------------------ erts/emulator/hipe/hipe_mode_switch.h | 49 +++++++++++++++++++++++++++++++++-- erts/emulator/hipe/hipe_stack.h | 11 ++++++++ 16 files changed, 116 insertions(+), 62 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index a5be8e1529..5b069eb30d 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -28,7 +28,9 @@ #include "global.h" #include "erl_process.h" #include "error.h" +#define ERL_WANT_HIPE_BIF_WRAPPER__ #include "bif.h" +#undef ERL_WANT_HIPE_BIF_WRAPPER__ #include "big.h" #include "dist.h" #include "erl_version.h" diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index 72c55ccb55..7b69b39511 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -465,6 +465,8 @@ erts_bif_prep_await_proc_exit_apply_trap(Process *c_p, Eterm args[], int nargs); +#ifdef ERL_WANT_HIPE_BIF_WRAPPER__ + #ifndef HIPE #define HIPE_WRAPPER_BIF_DISABLE_GC(BIF_NAME, ARITY) @@ -509,6 +511,7 @@ BIF_RETTYPE hipe_wrapper_ ## BIF_NAME ## _ ## ARITY (Process* c_p, \ #endif +#endif /* ERL_WANT_HIPE_BIF_WRAPPER__ */ #include "erl_bif_table.h" diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index f50d484576..a094a13f43 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -26,7 +26,9 @@ #include "global.h" #include "erl_process.h" #include "error.h" +#define ERL_WANT_HIPE_BIF_WRAPPER__ #include "bif.h" +#undef ERL_WANT_HIPE_BIF_WRAPPER__ #include "big.h" #include "erl_binary.h" #include "erl_bits.h" diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 50548850eb..0010f6a440 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -21,6 +21,8 @@ # include "config.h" #endif +#define ERL_WANT_GC_INTERNALS__ + #include "sys.h" #include "erl_vm.h" #include "global.h" diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 3bf78adce7..26afffa09b 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -32,7 +32,9 @@ #include "global.h" #include "erl_process.h" #include "error.h" +#define ERL_WANT_HIPE_BIF_WRAPPER__ #include "bif.h" +#undef ERL_WANT_HIPE_BIF_WRAPPER__ #include "big.h" #include "erl_binary.h" #include "erl_bits.h" diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 0db42d4325..a48ad0eac2 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -20,6 +20,8 @@ # include "config.h" #endif +#define ERL_WANT_GC_INTERNALS__ + #include "sys.h" #include "erl_vm.h" #include "global.h" diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index 5203dda263..bf0496c112 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -20,10 +20,12 @@ #ifndef __ERL_GC_H__ #define __ERL_GC_H__ -#include "erl_map.h" +#if defined(ERL_WANT_GC_INTERNALS__) || defined(ERTS_DO_INCL_GLB_INLINE_FUNC_DEF) /* GC declarations shared by beam/erl_gc.c and hipe/hipe_gc.c */ +#include "erl_map.h" + #if defined(DEBUG) && !ERTS_GLB_INLINE_INCL_FUNC_DEF # define HARDDEBUG 1 #endif @@ -67,8 +69,6 @@ do { \ #define in_area(ptr,start,nbytes) \ ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes)) -extern Uint erts_test_long_gc_sleep; - #if defined(DEBUG) || defined(ERTS_OFFHEAP_DEBUG) int within(Eterm *ptr, Process *p); #endif @@ -97,4 +97,33 @@ ERTS_GLB_INLINE Eterm follow_moved(Eterm term) } #endif +#endif /* ERL_GC_C__ || HIPE_GC_C__ */ + +/* + * Global exported + */ + +extern Uint erts_test_long_gc_sleep; + +typedef struct { + Uint64 reclaimed; + Uint64 garbage_cols; +} ErtsGCInfo; + +void erts_gc_info(ErtsGCInfo *gcip); +void erts_init_gc(void); +int erts_garbage_collect(struct process*, int, Eterm*, int); +void erts_garbage_collect_hibernate(struct process* p); +Eterm erts_gc_after_bif_call(struct process* p, Eterm result, Eterm* regs, Uint arity); +void erts_garbage_collect_literals(struct process* p, Eterm* literals, + Uint lit_size, + struct erl_off_heap_header* oh); +Uint erts_next_heap_size(Uint, Uint); +Eterm erts_heap_sizes(struct process* p); + +void erts_offset_off_heap(struct erl_off_heap*, Sint, Eterm*, Eterm*); +void erts_offset_heap_ptr(Eterm*, Uint, Sint, Eterm*, Eterm*); +void erts_offset_heap(Eterm*, Uint, Sint, Eterm*, Eterm*); +void erts_free_heap_frags(struct process* p); + #endif /* __ERL_GC_H__ */ diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 27a3a3553b..3d08be25ff 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -58,6 +58,7 @@ typedef struct process Process; #include "external.h" #include "erl_mseg.h" #include "erl_async.h" +#include "erl_gc.h" #ifdef HIPE #include "hipe_process.h" @@ -488,11 +489,6 @@ typedef struct { } working; } ErtsSchedWallTime; -typedef struct { - Uint64 reclaimed; - Uint64 garbage_cols; -} ErtsGCInfo; - typedef struct { int sched; erts_aint32_t aux_work; diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 9b9b4b2a62..c95479bc0b 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -36,7 +36,9 @@ #include "erl_process.h" #include "error.h" #include "external.h" +#define ERL_WANT_HIPE_BIF_WRAPPER__ #include "bif.h" +#undef ERL_WANT_HIPE_BIF_WRAPPER__ #include "big.h" #include "dist.h" #include "erl_binary.h" diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 891046a8b5..da9f029a9f 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -41,6 +41,7 @@ #include "error.h" #include "erl_utils.h" #include "erl_port.h" +#include "erl_gc.h" struct enif_environment_t /* ErlNifEnv */ { @@ -809,23 +810,6 @@ void MD5Init(MD5_CTX *); void MD5Update(MD5_CTX *, unsigned char *, unsigned int); void MD5Final(unsigned char [16], MD5_CTX *); -/* ggc.c */ - -void erts_gc_info(ErtsGCInfo *gcip); -void erts_init_gc(void); -int erts_garbage_collect(Process*, int, Eterm*, int); -void erts_garbage_collect_hibernate(Process* p); -Eterm erts_gc_after_bif_call(Process* p, Eterm result, Eterm* regs, Uint arity); -void erts_garbage_collect_literals(Process* p, Eterm* literals, - Uint lit_size, - struct erl_off_heap_header* oh); -Uint erts_next_heap_size(Uint, Uint); -Eterm erts_heap_sizes(Process* p); - -void erts_offset_off_heap(ErlOffHeap *, Sint, Eterm*, Eterm*); -void erts_offset_heap_ptr(Eterm*, Uint, Sint, Eterm*, Eterm*); -void erts_offset_heap(Eterm*, Uint, Sint, Eterm*, Eterm*); -void erts_free_heap_frags(Process* p); /* io.c */ diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index f20e6e5665..9b3ee5cc65 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -49,6 +49,9 @@ #include "beam_bp.h" #include "erl_ptab.h" #include "erl_check_io.h" +#ifdef HIPE +# include "hipe_mode_switch.h" +#endif #undef M_TRIM_THRESHOLD #undef M_TOP_PAD diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c index 32694a8f97..6d7748f206 100644 --- a/erts/emulator/hipe/hipe_debug.c +++ b/erts/emulator/hipe/hipe_debug.c @@ -172,8 +172,10 @@ void hipe_print_pcb(Process *p) printf("P: 0x%0*lx\r\n", 2*(int)sizeof(long), (unsigned long)p); printf("-----------------------------------------------\r\n"); printf("Offset| Name | Value | *Value |\r\n"); +#undef U #define U(n,x) \ printf(" % 4d | %s | 0x%0*lx | |\r\n", (int)offsetof(Process,x), n, 2*(int)sizeof(long), (unsigned long)p->x) +#undef P #define P(n,x) \ printf(" % 4d | %s | 0x%0*lx | 0x%0*lx |\r\n", (int)offsetof(Process,x), n, 2*(int)sizeof(long), (unsigned long)p->x, 2*(int)sizeof(long), p->x ? (unsigned long)*(p->x) : -1UL) diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c index 86c4068072..b10263f6e2 100644 --- a/erts/emulator/hipe/hipe_gc.c +++ b/erts/emulator/hipe/hipe_gc.c @@ -22,6 +22,9 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif + +#define ERL_WANT_GC_INTERNALS__ + #include "global.h" #include "erl_gc.h" diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 1ae1d17b7f..35fbd09cd2 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -140,7 +140,6 @@ void hipe_check_pcb(Process *p, const char *file, unsigned line) #endif /* HIPE_DEBUG > 0 */ /* ensure that at least nwords words are available on the native stack */ -static void hipe_check_nstack(Process *p, unsigned nwords); #if defined(__sparc__) #include "hipe_sparc_glue.h" @@ -159,7 +158,7 @@ static void hipe_check_nstack(Process *p, unsigned nwords); Uint hipe_beam_pc_return[1]; /* needed in hipe_debug.c */ Uint hipe_beam_pc_throw[1]; /* needed in hipe_debug.c */ Uint hipe_beam_pc_resume[1]; /* needed by hipe_set_timeout() */ -static Eterm hipe_beam_catch_throw; +Eterm hipe_beam_catch_throw; void hipe_mode_switch_init(void) { @@ -185,22 +184,6 @@ void hipe_set_call_trap(Uint *bfun, void *nfun, int is_closure) bfun[-4] = (Uint)nfun; } -void hipe_reserve_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) -{ - if (!hipe_bifcall_from_native_is_recursive(p)) - return; - - /* ensure that at least 2 words are available on the BEAM stack */ - if ((p->stop - 2) < p->htop) { - DPRINTF("calling gc to reserve BEAM stack size"); - p->fcalls -= erts_garbage_collect(p, 2, reg, arity); - ASSERT(!((p->stop - 2) < p->htop)); - } - p->stop -= 2; - p->stop[0] = NIL; - p->stop[1] = hipe_beam_catch_throw; -} - static __inline__ void hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) { @@ -223,15 +206,6 @@ hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) p->cp = hipe_beam_pc_return; } -void hipe_unreserve_beam_trap_frame(Process *p) -{ - if (!hipe_bifcall_from_native_is_recursive(p)) - return; - - ASSERT(p->stop[0] == NIL && p->stop[1] == hipe_beam_catch_throw); - p->stop += 2; -} - static __inline__ void hipe_pop_beam_trap_frame(Process *p) { ASSERT(p->stop[1] == hipe_beam_catch_throw); @@ -607,7 +581,6 @@ static unsigned hipe_next_nstack_size(unsigned size) } #if 0 && defined(HIPE_NSTACK_GROWS_UP) -#define hipe_nstack_avail(p) ((p)->hipe.nstend - (p)->hipe.nsp) void hipe_inc_nstack(Process *p) { Eterm *old_nstack = p->hipe.nstack; @@ -631,7 +604,6 @@ void hipe_inc_nstack(Process *p) #endif #if defined(HIPE_NSTACK_GROWS_DOWN) -#define hipe_nstack_avail(p) ((unsigned)((p)->hipe.nsp - (p)->hipe.nstack)) void hipe_inc_nstack(Process *p) { unsigned old_size = p->hipe.nstend - p->hipe.nstack; @@ -663,12 +635,6 @@ void hipe_empty_nstack(Process *p) p->hipe.nstend = NULL; } -static void hipe_check_nstack(Process *p, unsigned nwords) -{ - while (hipe_nstack_avail(p) < nwords) - hipe_inc_nstack(p); -} - void hipe_set_closure_stub(ErlFunEntry *fe, unsigned num_free) { unsigned arity; diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h index 06721e3c04..e60c5ad9ab 100644 --- a/erts/emulator/hipe/hipe_mode_switch.h +++ b/erts/emulator/hipe/hipe_mode_switch.h @@ -59,13 +59,58 @@ void hipe_empty_nstack(Process *p); void hipe_set_closure_stub(ErlFunEntry *fe, unsigned num_free); Eterm hipe_build_stacktrace(Process *p, struct StackTrace *s); -void hipe_reserve_beam_trap_frame(Process*, Eterm reg[], unsigned arity); -void hipe_unreserve_beam_trap_frame(Process*); +ERTS_GLB_INLINE void hipe_reserve_beam_trap_frame(Process*, Eterm reg[], unsigned arity); +ERTS_GLB_INLINE void hipe_unreserve_beam_trap_frame(Process*); extern Uint hipe_beam_pc_return[]; extern Uint hipe_beam_pc_throw[]; extern Uint hipe_beam_pc_resume[]; +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +#include "erl_gc.h" +#include "hipe_stack.h" + +#if defined(__sparc__) +#include "hipe_sparc_glue.h" +#elif defined(__i386__) +#include "hipe_x86_glue.h" +#elif defined(__x86_64__) +#include "hipe_amd64_glue.h" +#elif defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__) +#include "hipe_ppc_glue.h" +#elif defined(__arm__) +#include "hipe_arm_glue.h" +#endif + +extern Eterm hipe_beam_catch_throw; + +ERTS_GLB_INLINE void hipe_reserve_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) +{ + if (!hipe_bifcall_from_native_is_recursive(p)) + return; + + /* ensure that at least 2 words are available on the BEAM stack */ + if ((p->stop - 2) < p->htop) { + p->fcalls -= erts_garbage_collect(p, 2, reg, arity); + ASSERT(!((p->stop - 2) < p->htop)); + } + p->stop -= 2; + p->stop[0] = NIL; + p->stop[1] = hipe_beam_catch_throw; +} + +ERTS_GLB_INLINE void hipe_unreserve_beam_trap_frame(Process *p) +{ + if (!hipe_bifcall_from_native_is_recursive(p)) + return; + + ASSERT(p->stop[0] == NIL && p->stop[1] == hipe_beam_catch_throw); + p->stop += 2; +} + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + #endif /* ASM */ #endif /* HIPE_MODE_SWITCH_H */ diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h index 66f9f04c73..4cfdb54dd8 100644 --- a/erts/emulator/hipe/hipe_stack.h +++ b/erts/emulator/hipe/hipe_stack.h @@ -108,12 +108,23 @@ extern int hipe_fill_stacktrace(Process*, int, Eterm**); #if 0 && defined(HIPE_NSTACK_GROWS_UP) #define hipe_nstack_start(p) ((p)->hipe.nstack) #define hipe_nstack_used(p) ((p)->hipe.nsp - (p)->hipe.nstack) +#define hipe_nstack_avail(p) ((p)->hipe.nstend - (p)->hipe.nsp) #endif #if defined(HIPE_NSTACK_GROWS_DOWN) #define hipe_nstack_start(p) ((p)->hipe.nsp) #define hipe_nstack_used(p) ((p)->hipe.nstend - (p)->hipe.nsp) +#define hipe_nstack_avail(p) ((unsigned)((p)->hipe.nsp - (p)->hipe.nstack)) #endif +/* ensure that at least nwords words are available on the native stack */ +static __inline__ void hipe_check_nstack(Process *p, unsigned nwords) +{ + extern void hipe_inc_nstack(Process *p); + + while (hipe_nstack_avail(p) < nwords) + hipe_inc_nstack(p); +} + /* * GC support procedures */ -- cgit v1.2.3 From da3073e8d3d26ec6c63979a22c913d7758eeed71 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 13 Oct 2014 20:09:19 +0200 Subject: erts: Extend usage of ASM macro to avoid including asm macros in C code and reduce the probability of macro name collisions Catalyst: ppc macro "r0" conflicting with local variable in external.c --- erts/emulator/hipe/hipe_amd64_asm.m4 | 50 ++++++++++++++++++++++++----------- erts/emulator/hipe/hipe_amd64_bifs.m4 | 2 +- erts/emulator/hipe/hipe_amd64_glue.S | 3 +-- erts/emulator/hipe/hipe_arm_asm.m4 | 12 +++++++-- erts/emulator/hipe/hipe_arm_bifs.m4 | 1 + erts/emulator/hipe/hipe_arm_glue.S | 3 +-- erts/emulator/hipe/hipe_ppc_asm.m4 | 27 ++++++++++++------- erts/emulator/hipe/hipe_ppc_bifs.m4 | 1 + erts/emulator/hipe/hipe_ppc_glue.S | 3 +-- erts/emulator/hipe/hipe_sparc_asm.m4 | 13 ++++++--- erts/emulator/hipe/hipe_sparc_bifs.m4 | 1 + erts/emulator/hipe/hipe_sparc_glue.S | 3 +-- erts/emulator/hipe/hipe_x86_asm.m4 | 20 ++++++++++---- erts/emulator/hipe/hipe_x86_bifs.m4 | 1 + erts/emulator/hipe/hipe_x86_glue.S | 3 +-- 15 files changed, 97 insertions(+), 46 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_amd64_asm.m4 b/erts/emulator/hipe/hipe_amd64_asm.m4 index 7c81040b8b..b4b3c073ab 100644 --- a/erts/emulator/hipe/hipe_amd64_asm.m4 +++ b/erts/emulator/hipe/hipe_amd64_asm.m4 @@ -33,7 +33,35 @@ define(HEAP_LIMIT_IN_REGISTER,0)dnl global for HL define(SIMULATE_NSP,0)dnl change to 1 to simulate call/ret insns `#define AMD64_LEAF_WORDS 'LEAF_WORDS -`#define LEAF_WORDS 'LEAF_WORDS +`#define LEAF_WORDS 'LEAF_WORDS +`#define AMD64_NR_ARG_REGS 'NR_ARG_REGS +`#define NR_ARG_REGS 'NR_ARG_REGS + +`#define AMD64_HP_IN_REGISTER 'HP_IN_REGISTER +`#if AMD64_HP_IN_REGISTER' +`#define AMD64_HEAP_POINTER 15' +define(HP,%r15)dnl Only change this together with above +`#endif' + +`#define AMD64_FCALLS_IN_REGISTER 'FCALLS_IN_REGISTER +`#if AMD64_FCALLS_IN_REGISTER' +`#define AMD64_FCALLS_REGISTER 11' +define(FCALLS,%r11)dnl This goes together with line above +`#endif' + +`#define AMD64_HEAP_LIMIT_IN_REGISTER 'HEAP_LIMIT_IN_REGISTER +`#if AMD64_HEAP_LIMIT_IN_REGISTER' +`#define AMD64_HEAP_LIMIT_REGISTER 12' +define(HEAP_LIMIT,%r12)dnl Change this together with line above +`#endif' + +`#define AMD64_SIMULATE_NSP 'SIMULATE_NSP + + +`#ifdef ASM' +/* + * Only assembler stuff from here on (when included from *.S) + */ /* * Workarounds for Darwin. @@ -63,33 +91,24 @@ ifelse(OPSYS,darwin,`` */ `#define P %rbp' -`#define AMD64_HP_IN_REGISTER 'HP_IN_REGISTER `#if AMD64_HP_IN_REGISTER -#define AMD64_HEAP_POINTER 15' -define(HP,%r15)dnl Only change this together with above -`#define SAVE_HP movq 'HP`, P_HP(P) +#define SAVE_HP movq 'HP`, P_HP(P) #define RESTORE_HP movq P_HP(P), 'HP` #else #define SAVE_HP /*empty*/ #define RESTORE_HP /*empty*/ #endif' -`#define AMD64_FCALLS_IN_REGISTER 'FCALLS_IN_REGISTER `#if AMD64_FCALLS_IN_REGISTER -#define AMD64_FCALLS_REGISTER 11' -define(FCALLS,%r11)dnl This goes together with line above -`#define SAVE_FCALLS movq 'FCALLS`, P_FCALLS(P) +#define SAVE_FCALLS movq 'FCALLS`, P_FCALLS(P) #define RESTORE_FCALLS movq P_FCALLS(P), 'FCALLS` #else #define SAVE_FCALLS /*empty*/ #define RESTORE_FCALLS /*empty*/ #endif' -`#define AMD64_HEAP_LIMIT_IN_REGISTER 'HEAP_LIMIT_IN_REGISTER `#if AMD64_HEAP_LIMIT_IN_REGISTER -#define AMD64_HEAP_LIMIT_REGISTER 12' -define(HEAP_LIMIT,%r12)dnl Change this together with line above -`#define RESTORE_HEAP_LIMIT movq P_HP_LIMIT(P), 'HEAP_LIMIT` +#define RESTORE_HEAP_LIMIT movq P_HP_LIMIT(P), 'HEAP_LIMIT` #else #define RESTORE_HEAP_LIMIT /*empty*/ #endif' @@ -99,7 +118,6 @@ define(NSP,%rsp)dnl `#define SAVE_CSP movq %rsp, P_CSP(P) #define RESTORE_CSP movq P_CSP(P), %rsp' -`#define AMD64_SIMULATE_NSP 'SIMULATE_NSP /* * Context switching macros. @@ -132,8 +150,6 @@ define(NSP,%rsp)dnl /* * Argument (parameter) registers. */ -`#define AMD64_NR_ARG_REGS 'NR_ARG_REGS -`#define NR_ARG_REGS 'NR_ARG_REGS define(defarg,`define(ARG$1,`$2')dnl #`define ARG'$1 $2' @@ -263,4 +279,6 @@ define(NBIF_RET,`NBIF_RET_N(eval(RET_POP($1)))')dnl `/* #define NBIF_RET_3 'NBIF_RET(3)` */' `/* #define NBIF_RET_5 'NBIF_RET(5)` */' +`#endif /* ASM */' + `#endif /* HIPE_AMD64_ASM_H */' diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4 index a3219c7586..7a4bb30447 100644 --- a/erts/emulator/hipe/hipe_amd64_bifs.m4 +++ b/erts/emulator/hipe/hipe_amd64_bifs.m4 @@ -18,7 +18,7 @@ changecom(`/*', `*/')dnl * %CopyrightEnd% */ - +#`define ASM' include(`hipe/hipe_amd64_asm.m4') #`include' "config.h" #`include' "hipe_literals.h" diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S index 8816906870..fa883f2db2 100644 --- a/erts/emulator/hipe/hipe_amd64_glue.S +++ b/erts/emulator/hipe/hipe_amd64_glue.S @@ -17,10 +17,9 @@ * %CopyrightEnd% */ - +#define ASM #include "hipe_amd64_asm.h" #include "hipe_literals.h" -#define ASM #include "hipe_mode_switch.h" /* diff --git a/erts/emulator/hipe/hipe_arm_asm.m4 b/erts/emulator/hipe/hipe_arm_asm.m4 index 85dc84973d..b2e3f83d1e 100644 --- a/erts/emulator/hipe/hipe_arm_asm.m4 +++ b/erts/emulator/hipe/hipe_arm_asm.m4 @@ -29,6 +29,14 @@ define(LEAF_WORDS,16)dnl number of stack words for leaf functions define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive `#define ARM_LEAF_WORDS 'LEAF_WORDS +`#define ARM_NR_ARG_REGS 'NR_ARG_REGS +`#define NR_ARG_REGS 'NR_ARG_REGS + + +`#ifdef ASM' +/* + * Only assembler stuff from here on (when included from *.S) + */ /* * Reserved registers. @@ -77,8 +85,6 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive /* * Argument (parameter) registers. */ -`#define ARM_NR_ARG_REGS 'NR_ARG_REGS -`#define NR_ARG_REGS 'NR_ARG_REGS define(defarg,`define(ARG$1,`$2')dnl #`define ARG'$1 $2' @@ -195,4 +201,6 @@ define(QUICK_CALL_RET,`NBIF_POP_N(eval(RET_POP($2)))b $1')dnl `/* #define QUICK_CALL_RET_F_3 'QUICK_CALL_RET(F,3)` */' `/* #define QUICK_CALL_RET_F_5 'QUICK_CALL_RET(F,5)` */' +`#endif /* ASM */' + `#endif /* HIPE_ARM_ASM_H */' diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4 index bd8bc5ab6b..57e51bb8b1 100644 --- a/erts/emulator/hipe/hipe_arm_bifs.m4 +++ b/erts/emulator/hipe/hipe_arm_bifs.m4 @@ -19,6 +19,7 @@ changecom(`/*', `*/')dnl */ +#`define ASM' include(`hipe/hipe_arm_asm.m4') #`include' "config.h" #`include' "hipe_literals.h" diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S index 2e2b8604a6..884b473897 100644 --- a/erts/emulator/hipe/hipe_arm_glue.S +++ b/erts/emulator/hipe/hipe_arm_glue.S @@ -17,10 +17,9 @@ * %CopyrightEnd% */ - +#define ASM #include "hipe_arm_asm.h" #include "hipe_literals.h" -#define ASM #include "hipe_mode_switch.h" .text diff --git a/erts/emulator/hipe/hipe_ppc_asm.m4 b/erts/emulator/hipe/hipe_ppc_asm.m4 index 343402f9f0..4a1caa1543 100644 --- a/erts/emulator/hipe/hipe_ppc_asm.m4 +++ b/erts/emulator/hipe/hipe_ppc_asm.m4 @@ -22,6 +22,22 @@ changecom(`/*', `*/')dnl `#ifndef HIPE_PPC_ASM_H #define HIPE_PPC_ASM_H' +/* + * Tunables. + */ +define(LEAF_WORDS,16)dnl number of stack words for leaf functions +define(NR_ARG_REGS,4)dnl admissible values are 0 to 6, inclusive + +`#define PPC_LEAF_WORDS 'LEAF_WORDS +`#define PPC_NR_ARG_REGS 'NR_ARG_REGS +`#define NR_ARG_REGS 'NR_ARG_REGS + + +`#ifdef ASM' +/* + * Only assembler stuff from here on (when included from *.S) + */ + /* * Handle 32 vs 64-bit. */ @@ -53,13 +69,6 @@ define(WSIZE,4)dnl `#define STORE 'STORE `#define CMPI 'CMPI -/* - * Tunables. - */ -define(LEAF_WORDS,16)dnl number of stack words for leaf functions -define(NR_ARG_REGS,4)dnl admissible values are 0 to 6, inclusive - -`#define PPC_LEAF_WORDS 'LEAF_WORDS /* * Workarounds for Darwin. @@ -193,8 +202,6 @@ NAME: \ /* * Argument (parameter) registers. */ -`#define PPC_NR_ARG_REGS 'NR_ARG_REGS -`#define NR_ARG_REGS 'NR_ARG_REGS define(defarg,`define(ARG$1,`$2')dnl #`define ARG'$1 $2' @@ -309,4 +316,6 @@ define(QUICK_CALL_RET,`NBIF_POP_N(eval(RET_POP($2)))b $1')dnl `/* #define QUICK_CALL_RET_F_3 'QUICK_CALL_RET(F,3)` */' `/* #define QUICK_CALL_RET_F_5 'QUICK_CALL_RET(F,5)` */' +`#endif /* ASM */' + `#endif /* HIPE_PPC_ASM_H */' diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4 index 7cc2b5c7b6..f53b79b52e 100644 --- a/erts/emulator/hipe/hipe_ppc_bifs.m4 +++ b/erts/emulator/hipe/hipe_ppc_bifs.m4 @@ -19,6 +19,7 @@ changecom(`/*', `*/')dnl */ +#`define ASM' include(`hipe/hipe_ppc_asm.m4') #`include' "config.h" #`include' "hipe_literals.h" diff --git a/erts/emulator/hipe/hipe_ppc_glue.S b/erts/emulator/hipe/hipe_ppc_glue.S index 6f0217c738..5c98f75d0b 100644 --- a/erts/emulator/hipe/hipe_ppc_glue.S +++ b/erts/emulator/hipe/hipe_ppc_glue.S @@ -17,10 +17,9 @@ * %CopyrightEnd% */ - +#define ASM #include "hipe_ppc_asm.h" #include "hipe_literals.h" -#define ASM #include "hipe_mode_switch.h" .text diff --git a/erts/emulator/hipe/hipe_sparc_asm.m4 b/erts/emulator/hipe/hipe_sparc_asm.m4 index 227d10ed80..c3c3bcb74a 100644 --- a/erts/emulator/hipe/hipe_sparc_asm.m4 +++ b/erts/emulator/hipe/hipe_sparc_asm.m4 @@ -29,6 +29,14 @@ define(LEAF_WORDS,16)dnl number of stack words for leaf functions define(NR_ARG_REGS,4)dnl admissible values are 0 to 6, inclusive `#define SPARC_LEAF_WORDS 'LEAF_WORDS +`#define SPARC_NR_ARG_REGS 'NR_ARG_REGS +`#define NR_ARG_REGS 'NR_ARG_REGS + + +`#ifdef ASM' +/* + * Only assembler stuff from here on (when included from *.S) + */ /* * Reserved registers. @@ -80,9 +88,6 @@ define(NR_ARG_REGS,4)dnl admissible values are 0 to 6, inclusive /* * Argument (parameter) registers. */ -`#define SPARC_NR_ARG_REGS 'NR_ARG_REGS -`#define NR_ARG_REGS 'NR_ARG_REGS - define(defarg,`define(ARG$1,`$2')dnl #`define ARG'$1 $2' )dnl @@ -210,4 +215,6 @@ define(QUICK_CALL_RET,`ba $1; NBIF_POP_N(eval(RET_POP($2)))')dnl `/* #define QUICK_CALL_RET_F_3 'QUICK_CALL_RET(F,3)` */' `/* #define QUICK_CALL_RET_F_5 'QUICK_CALL_RET(F,5)` */' +`#endif /* ASM */' + `#endif /* HIPE_SPARC_ASM_H */' diff --git a/erts/emulator/hipe/hipe_sparc_bifs.m4 b/erts/emulator/hipe/hipe_sparc_bifs.m4 index ca5af45d58..2bfe3a4646 100644 --- a/erts/emulator/hipe/hipe_sparc_bifs.m4 +++ b/erts/emulator/hipe/hipe_sparc_bifs.m4 @@ -19,6 +19,7 @@ changecom(`/*', `*/')dnl */ +#`define ASM' include(`hipe/hipe_sparc_asm.m4') #`include' "config.h" #`include' "hipe_literals.h" diff --git a/erts/emulator/hipe/hipe_sparc_glue.S b/erts/emulator/hipe/hipe_sparc_glue.S index 44bdf1bc7e..e5362b2a50 100644 --- a/erts/emulator/hipe/hipe_sparc_glue.S +++ b/erts/emulator/hipe/hipe_sparc_glue.S @@ -18,10 +18,9 @@ * %CopyrightEnd% */ - +#define ASM #include "hipe_sparc_asm.h" #include "hipe_literals.h" -#define ASM #include "hipe_mode_switch.h" .section ".text" diff --git a/erts/emulator/hipe/hipe_x86_asm.m4 b/erts/emulator/hipe/hipe_x86_asm.m4 index 020ccf8d4b..39c5cb1044 100644 --- a/erts/emulator/hipe/hipe_x86_asm.m4 +++ b/erts/emulator/hipe/hipe_x86_asm.m4 @@ -33,6 +33,18 @@ define(SIMULATE_NSP,0)dnl change to 1 to simulate call/ret insns `#define X86_LEAF_WORDS 'LEAF_WORDS `#define LEAF_WORDS 'LEAF_WORDS +`#define X86_NR_ARG_REGS 'NR_ARG_REGS +`#define NR_ARG_REGS 'NR_ARG_REGS + +`#define X86_HP_IN_ESI 'HP_IN_ESI +`#define X86_SIMULATE_NSP 'SIMULATE_NSP + + +`#ifdef ASM' +/* + * Only assembler stuff from here on (when included from *.S) + */ + /* * Workarounds for Darwin. */ @@ -60,7 +72,6 @@ ifelse(OPSYS,darwin,`` */ `#define P %ebp' -`#define X86_HP_IN_ESI 'HP_IN_ESI `#if X86_HP_IN_ESI #define SAVE_HP movl %esi, P_HP(P) #define RESTORE_HP movl P_HP(P), %esi @@ -73,7 +84,6 @@ ifelse(OPSYS,darwin,`` #define SAVE_CSP movl %esp, P_CSP(P) #define RESTORE_CSP movl P_CSP(P), %esp' -`#define X86_SIMULATE_NSP 'SIMULATE_NSP /* * Context switching macros. @@ -100,12 +110,10 @@ ifelse(OPSYS,darwin,`` SAVE_CACHED_STATE; \ SWITCH_ERLANG_TO_C_QUICK' + /* * Argument (parameter) registers. */ -`#define X86_NR_ARG_REGS 'NR_ARG_REGS -`#define NR_ARG_REGS 'NR_ARG_REGS - ifelse(eval(NR_ARG_REGS >= 1),0,, ``#define ARG0 %eax '')dnl @@ -282,4 +290,6 @@ define(LOAD_CALLER_SAVE,`LAR_N(eval(NR_CALLER_SAVE-1))')dnl `#define STORE_CALLER_SAVE 'STORE_CALLER_SAVE `#define LOAD_CALLER_SAVE 'LOAD_CALLER_SAVE +`#endif /* ASM */' + `#endif /* HIPE_X86_ASM_H */' diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4 index dd6980f555..a0f16efa33 100644 --- a/erts/emulator/hipe/hipe_x86_bifs.m4 +++ b/erts/emulator/hipe/hipe_x86_bifs.m4 @@ -19,6 +19,7 @@ changecom(`/*', `*/')dnl */ +#`define ASM' include(`hipe/hipe_x86_asm.m4') #`include' "config.h" #`include' "hipe_literals.h" diff --git a/erts/emulator/hipe/hipe_x86_glue.S b/erts/emulator/hipe/hipe_x86_glue.S index 88b86f4de7..152c515fa7 100644 --- a/erts/emulator/hipe/hipe_x86_glue.S +++ b/erts/emulator/hipe/hipe_x86_glue.S @@ -18,10 +18,9 @@ * %CopyrightEnd% */ - +#define ASM #include "hipe_x86_asm.h" #include "hipe_literals.h" -#define ASM #include "hipe_mode_switch.h" /* -- cgit v1.2.3 From 1e5eb4314301e33a105a0ff1f860d9d290ea2618 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Sun, 26 Oct 2014 21:23:00 -0400 Subject: Fix "-smp disable" for emulator with dirty schedulers Running "erl -smp disable" on an emulator built with dirty scheduler support caused problems such as segmentation violations and emulator status line outputs containing garbage. For example: $ erl -smp disable Segmentation fault (core dumped) and: $ erl -smp disable Erlang/OTP 17 [DEVELOPMENT] [erts-6.2] [source] [64-bit] [ds:10:4297895689:4299948152] [async-threads:280] This problem also caused the emulator smoke_test_SUITE to hit these same problems if run in an emulator started with the "-smp disable" option. Fix this segmentation violation by ensuring that dirty scheduler information is printed in the status line only when the emulator is compiled with ERTS_SMP enabled. With this fix in place, the smoke_test_SUITE now passes when the "-smp disable" option is used, and the emulator status line prints correctly for both "-smp enable" and "-smp disable": $ erl -smp enable Erlang/OTP 17 [DEVELOPMENT] [erts-6.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [kernel-poll:false] and: $ erl -smp disable Erlang/OTP 17 [DEVELOPMENT] [erts-6.2] [source] [64-bit] [async-threads:10] [kernel-poll:false] --- erts/emulator/beam/erl_bif_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 6efe9d9550..e977c2ec5f 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -90,7 +90,7 @@ static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE " [smp:%beu:%beu]" #endif #ifdef USE_THREADS -#ifdef ERTS_DIRTY_SCHEDULERS +#if defined(ERTS_DIRTY_SCHEDULERS) && defined(ERTS_SMP) " [ds:%beu:%beu:%beu]" #endif " [async-threads:%d]" -- cgit v1.2.3 From a2ffd6fd0e7f693847450aa633f7a4a18f9baead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Oct 2014 18:10:37 +0100 Subject: erts: Fix return value from erts_maps_get to be const From "warning: assignment discards qualifiers from pointer target type" --- erts/emulator/beam/erl_nif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index dd88f59908..10c7af5a9d 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1733,7 +1733,7 @@ int enif_get_map_value(ErlNifEnv* env, Eterm key, Eterm *value) { - Eterm *ret; + const Eterm *ret; if (is_not_map(map)) { return 0; } -- cgit v1.2.3 From 4d73c647b55977d23c4295073945bd8aeb91ff83 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 14 Nov 2013 17:03:40 +0100 Subject: Use isfinite() instead of finite() when available OS X Mavericks builds result in a number of warnings about finite() being deprecated, like these: beam/erl_arith.c:451:7: warning: 'finite' is deprecated: first deprecated in OS X 10.9 [-Wdeprecated-declarations] ERTS_FP_ERROR(p, f1.fd, goto badarith); ^ sys/unix/erl_unix_sys.h:319:33: note: expanded from macro 'ERTS_FP_ERROR' ^ sys/unix/erl_unix_sys.h:244:51: note: expanded from macro '__ERTS_FP_ERROR' ^ /usr/include/math.h:718:12: note: 'finite' has been explicitly marked deprecated here extern int finite(double) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0, __MAC_10_9, __IPHONE_NA, __IPHONE_NA); Add checks to use isfinite() instead of finite() where available. Verified on OS X Mavericks 10.9.5 and Ubuntu 12.04. --- erts/configure.in | 13 ++++++++++++- erts/emulator/beam/sys.h | 2 +- erts/emulator/hipe/hipe_bif0.c | 2 +- erts/emulator/sys/unix/erl_unix_sys.h | 13 +++++++++---- erts/emulator/test/float_SUITE_data/fp_drv.c | 17 +++++++++++------ 5 files changed, 34 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 766e35fb2b..9864d03cde 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2109,6 +2109,17 @@ AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlop flockfile fstat strlcpy strlcat setsid posix2time time2posix \ setlocale nl_langinfo poll mlockall]) +AC_MSG_CHECKING([for isfinite]) +AC_TRY_LINK([#include ], + [isfinite(0);], have_isfinite=yes, have_isfinite=no), + +if test $have_isfinite = yes; then + AC_DEFINE(HAVE_ISFINITE,[1], + [Define to 1 if you have the `isfinite' function.]) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi case X$erl_xcomp_posix_memalign in Xno) ;; @@ -4817,7 +4828,7 @@ AH_BOTTOM([ #define HAVE_GETHRVTIME #endif -#ifndef HAVE_FINITE +#if !defined(HAVE_ISFINITE) && !defined(HAVE_FINITE) # if defined(HAVE_ISINF) && defined(HAVE_ISNAN) # define USE_ISINF_ISNAN # endif diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 3d8dd9c6d0..42272a14a3 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -501,7 +501,7 @@ extern volatile int erts_writing_erl_crash_dump; # define NO_ERF # define NO_ERFC /* This definition doesn't take NaN into account, but matherr() gets those */ -# define finite(x) (fabs(x) != HUGE_VAL) +# define isfinite(x) (fabs(x) != HUGE_VAL) # define USE_MATHERR # define HAVE_FINITE #endif diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 2497d51df1..c9eee2acf2 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1022,7 +1022,7 @@ BIF_RETTYPE hipe_conv_big_to_float(BIF_ALIST_1) */ void hipe_emulate_fpe(Process* p) { - if (!finite(p->hipe.float_result)) { + if (!isfinite(p->hipe.float_result)) { p->fp_exception = 1; } } diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 176fc049a7..12552843f5 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -230,8 +230,13 @@ extern void sys_stop_cat(void); */ #ifdef USE_ISINF_ISNAN /* simulate finite() */ -# define finite(f) (!isinf(f) && !isnan(f)) -# define HAVE_FINITE +# define isfinite(f) (!isinf(f) && !isnan(f)) +# define HAVE_ISFINITE +#elif defined(isfinite) && !defined(HAVE_ISFINITE) +# define HAVE_ISFINITE +#elif !defined(HAVE_ISFINITE) && defined(HAVE_FINITE) +# define isfinite finite +# define HAVE_ISFINITE #endif #ifdef NO_FPE_SIGNALS @@ -241,7 +246,7 @@ extern void sys_stop_cat(void); #define erts_thread_init_fp_exception() do{}while(0) #endif # define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0) -# define __ERTS_FP_ERROR(fpexnp, f, Action) if (!finite(f)) { Action; } else {} +# define __ERTS_FP_ERROR(fpexnp, f, Action) if (!isfinite(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) @@ -305,7 +310,7 @@ static __inline__ void __ERTS_FP_CHECK_INIT(volatile unsigned long *fp_exception code to always throw floating-point exceptions on errors. */ static __inline__ int erts_check_fpe_thorough(volatile unsigned long *fp_exception, double f) { - return erts_check_fpe(fp_exception, f) || !finite(f); + return erts_check_fpe(fp_exception, f) || !isfinite(f); } # define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) \ do { if (erts_check_fpe_thorough((fpexnp),(f))) { Action; } } while (0) diff --git a/erts/emulator/test/float_SUITE_data/fp_drv.c b/erts/emulator/test/float_SUITE_data/fp_drv.c index b80385c3f9..82d18d6440 100644 --- a/erts/emulator/test/float_SUITE_data/fp_drv.c +++ b/erts/emulator/test/float_SUITE_data/fp_drv.c @@ -29,9 +29,14 @@ #if defined (__GNUC__) int _finite(double x); #endif -#ifndef finite -#define finite _finite +#ifndef isfinite +#define isfinite _finite #endif +#elif !defined(HAVE_ISFINITE) && defined(HAVE_FINITE) +/* If not windows and we do not have isfinite */ +#define isfinite finite +#elif !defined(HAVE_ISFINITE) +# error "No finite function found!" #endif #include "erl_driver.h" @@ -79,21 +84,21 @@ do_test(void *unused) x = 3.23e133; y = 3.57e257; z = x*y; - if (finite(z)) + if (isfinite(z)) return "is finite (1)"; x = 5.0; y = 0.0; z = x/y; - if (finite(z)) + if (isfinite(z)) return "is finite (2)"; z = log(-1.0); - if (finite(z)) + if (isfinite(z)) return "is finite (3)"; z = log(0.0); - if (finite(z)) + if (isfinite(z)) return "is finite (4)"; return "ok"; -- cgit v1.2.3 From 5b4cb838afbe9e700139f810a1c4c9b0e91c511c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 29 Oct 2014 17:11:31 +0100 Subject: erts: Fix bug in beam_ranges Symptom: VM on OSX (darwin11.4.2) with +Meamin running sasl tests, crashing when init:reboot() does erlang:purge_module(installer). Problem: Off-by-one bug in beam_ranges:find_range, returning the wrong range if the 'end' of one module is the 'start' of the next. This is only possible if using sys_alloc (+Meamin) as our own allocators always put block headers between allocated payload data. --- erts/emulator/beam/beam_ranges.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c index 0f2d5d0c2a..cb6470638f 100644 --- a/erts/emulator/beam/beam_ranges.c +++ b/erts/emulator/beam/beam_ranges.c @@ -282,7 +282,7 @@ find_range(BeamInstr* pc) while (low < high) { if (pc < mid->start) { high = mid; - } else if (pc > RANGE_END(mid)) { + } else if (pc >= RANGE_END(mid)) { low = mid + 1; } else { erts_smp_atomic_set_nob(&r[active].mid, (erts_aint_t) mid); -- cgit v1.2.3 From 3193ae58b922c46b7e81df9e183eb5882e3fd823 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 29 Oct 2014 17:23:46 +0100 Subject: erts: Add gdb command etp-check-beam-ranges --- erts/etc/unix/etp-commands.in | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index bf6eb00314..3972bf01ef 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -1130,6 +1130,39 @@ document etp-cp %--------------------------------------------------------------------------- end +define etp-check-beam-ranges + set $etp_ci = 0 + while $etp_ci < 3 + printf "Checking code index %i...\n", $etp_ci + set $etp_j = 0 + while $etp_j < r[$etp_ci].n + set $etp_p = &r[$etp_ci].modules[$etp_j] + if $etp_j > 0 && $etp_p->start < (Range*)$etp_p[-1].end.counter + printf "r[%i].modules[%i]: ERROR start < previous\n", $etp_ci, $etp_j + end + if $etp_p->start > (Range*)$etp_p->end.counter + printf "r[%i].modules[%i]: ERROR start > end\n", $etp_ci, $etp_j + else + if $etp_p->start == (Range*)$etp_p->end.counter + printf "r[%i].modules[%i]: Purged\n", $etp_ci, $etp_j + end + end + set $etp_j = $etp_j + 1 + end + set $etp_ci = $etp_ci + 1 + end +end + +document etp-check-beam-ranges +%--------------------------------------------------------------------------- +% etp-check-beam-ranges +% +% Do consistency check of beam_ranges data structure +% and print errors and empty slots from purged modules. +%--------------------------------------------------------------------------- +end + + ############################################################################ # Commands for special term bunches. # -- cgit v1.2.3 From c3baa2ad6a7877ef1c2081d50903f3e513292339 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 30 Oct 2014 11:27:15 +0100 Subject: erts: Fix hanging in fd_stop on windows When reaping ports the flushReplyEvent is not signalled and therefore an errornous loop check caused the emulator to hang forever during shutdown. --- erts/emulator/sys/win32/sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 0ded6b274e..62de462b93 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -2190,7 +2190,7 @@ static void fd_stop(ErlDrvData data) ASSERT(dp->out.flushEvent); SetEvent(dp->out.flushEvent); } while (WaitForSingleObject(dp->out.flushReplyEvent, 10) == WAIT_TIMEOUT - || !(dp->out.flags & DF_THREAD_FLUSHED)); + && !(dp->out.flags & DF_THREAD_FLUSHED)); } } -- cgit v1.2.3 From 5fb392632142f3903e2be91d5ada95fc4474d2ab Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 30 Oct 2014 11:53:03 +0100 Subject: erts: Mend run_erl to set windows size of terminal sent from to_erl Need to include sys/ioctl.h for TIOCSWINSZ to be defined. Seems this was broken when refactoring run_erl for OSE in OTP 17.0. --- erts/etc/common/run_erl_common.c | 8 ++++++-- erts/etc/common/run_erl_common.h | 2 +- erts/etc/ose/run_erl.c | 2 +- erts/etc/unix/run_erl.c | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/run_erl_common.c b/erts/etc/common/run_erl_common.c index 580b6cc3c5..20b78eb05e 100644 --- a/erts/etc/common/run_erl_common.c +++ b/erts/etc/common/run_erl_common.c @@ -36,6 +36,10 @@ # include #endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif + #ifdef __OSE__ # include "ramlog.h" #endif @@ -637,7 +641,7 @@ int erts_run_erl_open_fifo(char *pipename,char *w_pipename,char *r_pipename) { /* Extract any control sequences that are ment only for run_erl * and should not be forwarded to the pty. */ -int erts_run_erl_extract_ctrl_seq(char* buf, int len) +int erts_run_erl_extract_ctrl_seq(char* buf, int len, int mfd) { static const char prefix[] = "\033_"; static const char suffix[] = "\033\\"; @@ -662,7 +666,7 @@ int erts_run_erl_extract_ctrl_seq(char* buf, int len) struct winsize ws; ws.ws_col = col; ws.ws_row = row; - if (ioctl(MFD, TIOCSWINSZ, &ws) < 0) { + if (ioctl(mfd, TIOCSWINSZ, &ws) < 0) { ERRNO_ERR0(LOG_ERR,"Failed to set window size"); } #endif diff --git a/erts/etc/common/run_erl_common.h b/erts/etc/common/run_erl_common.h index c47a0db054..14207ee4de 100644 --- a/erts/etc/common/run_erl_common.h +++ b/erts/etc/common/run_erl_common.h @@ -40,7 +40,7 @@ void erts_run_erl_log_error(int priority, int line, const char *format,...); int erts_run_erl_open_fifo(char *pipename,char *w_pipename,char *r_pipename); int erts_run_erl_log_alive_minutes(void); -int erts_run_erl_extract_ctrl_seq(char* buf, int len); +int erts_run_erl_extract_ctrl_seq(char* buf, int len, int mfd); /* File operations */ ssize_t sf_read(int fd, void *buffer, size_t len); diff --git a/erts/etc/ose/run_erl.c b/erts/etc/ose/run_erl.c index 6bb59b7f7e..8bc49a485e 100644 --- a/erts/etc/ose/run_erl.c +++ b/erts/etc/ose/run_erl.c @@ -495,7 +495,7 @@ int pass_on(ProgramState *s) { #ifdef DEBUG erts_run_erl_log_status("Pty master write; "); #endif - len = erts_run_erl_extract_ctrl_seq(buffer,len); + len = erts_run_erl_extract_ctrl_seq(buffer,len, s->ofd); if (len > 0) { int wlen = erts_run_erl_write_all(s->ofd, buffer, len); diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index 4b123b8911..049e83f9e4 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -490,7 +490,7 @@ static void pass_on(pid_t childpid) #ifdef DEBUG erts_run_erl_log_status("Pty master write; "); #endif - len = erts_run_erl_extract_ctrl_seq(buf, len); + len = erts_run_erl_extract_ctrl_seq(buf, len, mfd); if(len==1 && buf[0] == '\003') { kill(childpid,SIGINT); -- cgit v1.2.3 From 1bc59d68f5d22650fa18aa064ed8e50fc9a6a216 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Sun, 2 Nov 2014 19:49:55 +0300 Subject: Expose NIF version This patch allows checking for NIF API version in a way similar to driver version. E.g. by calling erlang:system_info(nif_version). Signed-off-by: Peter Lemenkov --- erts/doc/src/erlang.xml | 5 +++++ erts/emulator/beam/erl_bif_info.c | 8 ++++++++ erts/emulator/test/driver_SUITE.erl | 4 ++-- erts/preloaded/src/erlang.erl | 1 + 4 files changed, 16 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 111756407f..483d81cfb6 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6144,6 +6144,11 @@ ok erlang:system_info(multi_scheduling), and erlang:system_info(schedulers).

+ nif_version + +

Returns a string containing the erlang NIF version + used by the runtime system. It will be on the form "<major ver>.<minor ver>".

+
otp_release

Returns a string containing the OTP release number of the diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 61e4469600..2c00a5860e 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -27,6 +27,7 @@ #include "erl_process.h" #include "error.h" #include "erl_driver.h" +#include "erl_nif.h" #include "bif.h" #include "big.h" #include "erl_version.h" @@ -2459,6 +2460,13 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) ERL_DRV_EXTENDED_MINOR_VERSION); hp = HAlloc(BIF_P, 2*n); BIF_RET(buf_to_intlist(&hp, buf, n, NIL)); + } else if (ERTS_IS_ATOM_STR("nif_version", BIF_ARG_1)) { + char buf[42]; + int n = erts_snprintf(buf, 42, "%d.%d", + ERL_NIF_MAJOR_VERSION, + ERL_NIF_MINOR_VERSION); + hp = HAlloc(BIF_P, 2*n); + BIF_RET(buf_to_intlist(&hp, buf, n, NIL)); } else if (ERTS_IS_ATOM_STR("smp_support", BIF_ARG_1)) { #ifdef ERTS_SMP BIF_RET(am_true); diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 8d2c620be0..623d62f876 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -1204,8 +1204,8 @@ check_si_res(["sched_thrs", Value]) -> ?line Value = integer_to_list(erlang:system_info(schedulers)); %% Data added in 3rd version of driver_system_info() (driver version 1.5) -check_si_res(["emu_nif_vsn", _Value]) -> - true; +check_si_res(["emu_nif_vsn", Value]) -> + ?line Value = erlang:system_info(nif_version); %% Data added in 4th version of driver_system_info() (driver version 3.1) check_si_res(["dirty_sched", _Value]) -> diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 4ba623d921..611a568014 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2252,6 +2252,7 @@ tuple_to_list(_Tuple) -> (modified_timing_level) -> integer() | undefined; (multi_scheduling) -> disabled | blocked | enabled; (multi_scheduling_blockers) -> [PID :: pid()]; + (nif_version) -> string(); (otp_release) -> string(); (port_count) -> non_neg_integer(); (port_limit) -> pos_integer(); -- cgit v1.2.3 From d3f0abb4daf5c823f05c3908fdc4f6ee01035937 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 3 Nov 2014 18:04:26 +0100 Subject: erts: Add constant TERM_TO_BINARY_MEMCPY_FACTOR and do not piggyback on B2T_MEMCPY_FACTOR --- erts/emulator/beam/external.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 5e1f5c56e1..831af2be4a 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1766,7 +1766,7 @@ erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags) { #else #define TERM_TO_BINARY_COMPRESS_CHUNK 10 #endif - +#define TERM_TO_BINARY_MEMCPY_FACTOR 8 static void ttb_context_destructor(Binary *context_bin) { @@ -2387,8 +2387,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint bitoffs = WSTACK_POP(s); byte* bytes = (byte*) WSTACK_POP(s); byte* dst = (byte*) WSTACK_POP(s); - if (bits > r * (B2T_MEMCPY_FACTOR * 8)) { - Uint n = r * B2T_MEMCPY_FACTOR; + if (bits > r * (TERM_TO_BINARY_MEMCPY_FACTOR * 8)) { + Uint n = r * TERM_TO_BINARY_MEMCPY_FACTOR; WSTACK_PUSH5(s, (UWord)(dst + n), (UWord)(bytes + n), bitoffs, ENC_BIN_COPY, bits - 8*n); bits = 8*n; @@ -2398,7 +2398,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, break; } else { copy_binary_to_buffer(dst, 0, bytes, bitoffs, bits); - r -= bits / (B2T_MEMCPY_FACTOR * 8); + r -= bits / (TERM_TO_BINARY_MEMCPY_FACTOR * 8); goto outer_loop; } } @@ -2721,7 +2721,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, *ep++ = SMALL_INTEGER_EXT; *ep++ = bitsize; } - if (ctx && j > r * B2T_MEMCPY_FACTOR) { + if (ctx && j > r * TERM_TO_BINARY_MEMCPY_FACTOR) { WSTACK_PUSH5(s, (UWord)data_dst, (UWord)bytes, bitoffs, ENC_BIN_COPY, 8*j + bitsize); } else { -- cgit v1.2.3 From f482c5cc6dc4c23d39319586c3e1049e859a6f01 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 4 Nov 2014 12:21:27 +0100 Subject: Prepare release --- erts/doc/src/notes.xml | 25 +++++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 743369951f..7bc39fd351 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,31 @@

This document describes the changes made to the ERTS application.

+
Erts 6.2.1 + +
Fixed Bugs and Malfunctions + + +

+ Fix bug when an migrated empty memory carrier is reused + just before it should be destroyed by the thread that + created it.

+

+ Own Id: OTP-12249

+
+ +

+ Repair run_erl terminal window size adjustment sent from + to_erl. This was broken in OTP 17.0 which could lead to + strange cursor behaviour in the to_erl shell.

+

+ Own Id: OTP-12275 Aux Id: seq12739

+
+
+
+ +
+
Erts 6.2
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index b6a38f9361..c8c533a221 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.2 +VSN = 6.2.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 66a184c576d9262045194e95c752a50c74877802 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Tue, 4 Nov 2014 11:46:19 -0500 Subject: Fix gc-related problem with dirty NIFs Ensure that the return value from a dirty NIF call is made part of the GC rootset. Add a new regression test to nif_SUITE. Thanks to Daniel Goertzen for reporting the error and providing a test case, and to Sverker Eriksson for making test case reproducible and finding the fix. --- erts/emulator/beam/erl_nif.c | 1 + erts/emulator/test/nif_SUITE.erl | 3 +++ erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 13 +++++++++++++ 3 files changed, 17 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index ede5f335dc..adc3520ebb 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1646,6 +1646,7 @@ init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirec ep->m = env->mod_nif; ep->fp = indirect_fp; proc->freason = TRAP; + proc->arity = argc; return THE_NON_VALUE; } diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 14e6585220..4560077a51 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1564,6 +1564,8 @@ dirty_nif(Config) when is_list(Config) -> Val2 = "Erlang", Val3 = list_to_binary([Val2, 0]), {Val1, Val2, Val3} = call_dirty_nif(Val1, Val2, Val3), + LargeArray = lists:duplicate(1000, ok), + LargeArray = call_dirty_nif_zero_args(), ok catch error:badarg -> @@ -1740,6 +1742,7 @@ call_nif_schedule(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. send_from_dirty_nif(_) -> ?nif_stub. call_dirty_nif_exception() -> ?nif_stub. +call_dirty_nif_zero_args() -> ?nif_stub. %% maps is_map_nif(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 291c903947..85544db2ab 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1623,6 +1623,18 @@ static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL call_dirty_nif_exception, argc-1, argv); } } + +static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int i; + ERL_NIF_TERM result[1000]; + ERL_NIF_TERM ok = enif_make_atom(env, "ok"); + assert(argc == 0); + for (i = 0; i < sizeof(result)/sizeof(*result); i++) { + result[i] = ok; + } + return enif_make_list_from_array(env, result, i); +} #endif static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) @@ -1807,6 +1819,7 @@ static ErlNifFunc nif_funcs[] = {"call_dirty_nif", 3, call_dirty_nif}, {"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND}, {"call_dirty_nif_exception", 0, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, + {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND}, #endif {"is_map_nif", 1, is_map_nif}, {"get_map_size_nif", 1, get_map_size_nif}, -- cgit v1.2.3 From 0f4b940cd6173a1e991c2bce7a6004d95c52c088 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 4 Nov 2014 17:46:28 +0100 Subject: erts: Make erts_printf work with non-blocking Now that stdout can be set as non-blocking by the ttsl driver we can no longer use putc and friends for the stdout/stderr streams. Therefore we fallback on write. OTP-12239 --- erts/lib_src/common/erl_printf.c | 45 ++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c index 2c177ee5ac..0a0346ac2d 100644 --- a/erts/lib_src/common/erl_printf.c +++ b/erts/lib_src/common/erl_printf.c @@ -87,6 +87,39 @@ void (*erts_printf_unblock_fpe)(int) = NULL; # define FWRITE fwrite #endif +/* We use write for stdout and stderr as they could be + set to non-blocking by shell drivers, and non-blocking + FILE * functions work unpredictably as best */ +static int +printf_putc(int c, FILE *stream) { + if ((FILE*)stream == stdout || (FILE*)stream == stderr) { + int fd = stream == stdout ? fileno(stdout) : fileno(stdin); + int res; + do { + res = write(fd,&c,1); + } while (res == -1 && (errno == EAGAIN || errno == EINTR)); + if (res == -1) return EOF; + return res; + } + + return PUTC(c, stream); +} + +static size_t +printf_fwrite(const void *ptr, size_t size, size_t nitems, + FILE *stream) { + if ((FILE*)stream == stdout || (FILE*)stream == stderr) { + int fd = stream == stdout ? fileno(stdout) : fileno(stdin); + int res; + do { + res = write(fd, ptr, size*nitems); + } while (res == -1 && (errno == EAGAIN || errno == EINTR)); + if (res == -1) return 0; + return res; + } + return FWRITE(ptr, size, nitems, stream); +} + static int get_error_result(void) { @@ -103,10 +136,10 @@ write_f_add_cr(void *vfp, char* buf, size_t len) size_t i; ASSERT(vfp); for (i = 0; i < len; i++) { - if (buf[i] == '\n' && PUTC('\r', (FILE *) vfp) == EOF) - return get_error_result(); - if (PUTC(buf[i], (FILE *) vfp) == EOF) - return get_error_result(); + if (buf[i] == '\n' && printf_putc('\r', (FILE *) vfp) == EOF) + return get_error_result(); + if (printf_putc(buf[i], (FILE *) vfp) == EOF) + return get_error_result(); } return len; } @@ -119,12 +152,12 @@ write_f(void *vfp, char* buf, size_t len) if (len <= 64) { /* Try to optimize writes of small bufs. */ int i; for (i = 0; i < len; i++) - if (PUTC(buf[i], (FILE *) vfp) == EOF) + if (printf_putc(buf[i], (FILE *) vfp) == EOF) return get_error_result(); } else #endif - if (FWRITE((void *) buf, sizeof(char), len, (FILE *) vfp) != len) + if (printf_fwrite((void *) buf, sizeof(char), len, (FILE *) vfp) != len) return get_error_result(); return len; } -- cgit v1.2.3 From c2fd0dadaeccd42585770fa672acde7bdfae3121 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 4 Nov 2014 18:00:15 +0100 Subject: Do not let non-empty run-queue prevent activation of scheduler Conflicts: erts/emulator/beam/erl_process.c --- erts/emulator/beam/erl_process.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 1efd070afd..ef59fa8ae0 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2692,11 +2692,11 @@ chk_wake_sched(ErtsRunQueue *crq, int ix, int activate) return 0; wrq = ERTS_RUNQ_IX(ix); flags = ERTS_RUNQ_FLGS_GET(wrq); + if (activate && !(flags & ERTS_RUNQ_FLG_SUSPENDED)) { + if (try_inc_no_active_runqs(ix+1)) + (void) ERTS_RUNQ_FLGS_UNSET(wrq, ERTS_RUNQ_FLG_INACTIVE); + } if (!(flags & (ERTS_RUNQ_FLG_SUSPENDED|ERTS_RUNQ_FLG_NONEMPTY))) { - if (activate) { - if (try_inc_no_active_runqs(ix+1)) - (void) ERTS_RUNQ_FLGS_UNSET(wrq, ERTS_RUNQ_FLG_INACTIVE); - } wake_scheduler(wrq, 0); return 1; } -- cgit v1.2.3 From a0184cdca374f112ecfaa4c85a73b88d036f69f3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 6 Nov 2014 20:21:11 +0100 Subject: erts: Add internal docs for super carrier and new cpool search. --- erts/emulator/internal_doc/CarrierMigration.md | 104 ++++++++++++-- erts/emulator/internal_doc/SuperCarrier.md | 191 +++++++++++++++++++++++++ 2 files changed, 285 insertions(+), 10 deletions(-) create mode 100644 erts/emulator/internal_doc/SuperCarrier.md (limited to 'erts') diff --git a/erts/emulator/internal_doc/CarrierMigration.md b/erts/emulator/internal_doc/CarrierMigration.md index b93c11c6ec..7afdb70aef 100644 --- a/erts/emulator/internal_doc/CarrierMigration.md +++ b/erts/emulator/internal_doc/CarrierMigration.md @@ -146,28 +146,53 @@ Since the carrier has been unlinked from the data structure of available free blocks, no more allocations will be made in the carrier. The allocator instance putting the carrier into the pool, however, still has the responsibility of performing deallocations in -it while it remains in the pool. +it while it remains in the pool. The allocator instance with this +deallocation responsibility is here called the **employer**. -Each carrier has a flag field containing information about allocator -instance owning the carrier, a flag indicating if the carrier is in +Each carrier has a flag field containing information about the +employing allocator instance, a flag indicating if the carrier is in the pool or not, and a flag indicating if it is busy or not. When the -carrier is in the pool, the owning allocator instance needs to mark it +carrier is in the pool, the employing allocator instance needs to mark it as busy while operating on it. If another thread inspects it in order -to try to fetch it from the pool, it will abort the fetch if it is -busy. When fetching the carrier from the pool, ownership will changed -and further deallocations in the carrier will be redirected to the new -owner using the delayed dealloc functionality. +to try to fetch it from the pool, it will skip it if it is busy. When +fetching the carrier from the pool, employment will change and further +deallocations in the carrier will be redirected to the new +employer using the delayed dealloc functionality. If a carrier in the pool becomes empty, it will be withdrawn from the pool. All carriers that become empty are also always passed to its -originating allocator instance for deallocation using the delayed +**owning** allocator instance for deallocation using the delayed dealloc functionality. Since carriers this way always will be -deallocated by the allocator instance that allocated the carrier the +deallocated by the owner, that allocated the carrier, the underlying functionality of allocating and deallocating carriers can remain simple and doesn't have to bother about multiple threads. In a NUMA system we will also not mix carriers originating from multiple NUMA nodes. +In short: + +* The allocator instance that created a carrier **owns** it. +* An empty carrier is always deallocated by its **owner**. +* **Ownership** never changes. +* The allocator instance that uses a carrier **employs** it. +* An **employer** can abandon a carrier into the pool. +* Pooled carriers are not allocated from. +* Deallocation in a pooled carrier is still performed by its **employer**. +* **Employment** can only change when a carrier is fetched from the pool. + +### Searching the pool ### + +To harbor real time characteristics, searching the pool is +limited. We only inspect a limited number of carriers. If none of +those carriers had a free block large enough to satisfy the allocation +request, the search will fail. A carrier in the pool can also be busy, +if another thread is currently doing block deallocation work on the +carrier. A busy carrier will also be skipped by the search as it can +not satisfy the request. The pool is lock free and we do not want to +block, waiting for the other thread to finish. + +#### Before OTP 17.4 #### + When an allocator instance needs more carrier space, it always begins by inspecting its own carriers that are waiting for thread progress before they can be deallocated. If no such carrier could be found, it @@ -176,6 +201,65 @@ it will allocate a new carrier. Regardless of where the allocator instance gets the carrier from it the just links in the carrier into its data structure of free blocks. +#### After OTP 17.4 #### + +The old search algorithm had a problem as the search always started at +the same position in the pool, the sentinel. This could lead to +contention from concurrent searching processes. But even worse, it +could lead to a "bad" state when searches fail with a high rate +leading to new carriers instead being allocated. These new carriers +may later be inserted into the pool due to bad utilization. If the +frequency of insertions into the pool is higher than successful +fetching from the pool, memory will eventually get exhausted. + +This "bad" state, consist of a cluster of small and/or highly +fragmented carriers located at the sentinel in the pool. The largest free +block in such a "bad" carrier is rather small, making it not able to satisfy +most allocation requests. As the search always started at the +sentinel, any such "bad" carriers that had been left in the pool would +eventually cluster together at the sentinel. All searches first +have to skip past this cluster of "bad" carriers to reach a "good" +carrier. When the cluster gets to the same size as the search limit, +all searches will essentially fail. + +To counter the "bad cluster" problem and also ease the contention, the +search will now always start by first looking at the allocators **own** +carriers. That is, carriers that were initially created by the +allocator itself and later had been abandoned to the pool. If none of +our own abandoned carrier would do, then the search continues into the +pool, as before, to look for carriers created by other +allocators. However, if we have at least one abandoned carrier of our +own, that could not satisfy the request, we can use that as entry point +into the pool. + +The result is that we prefer carriers created by the thread itself, +which is good for NUMA performance. And we get more entry points when +searching the pool, which will ease contention and clustering. + +To do the first search among own carriers, every allocator instance +has two new lists; `pooled_list` and `traitor_list`. These lists are only +accessed by the allocator itself and they only contain the allocators +own carriers. When an owned carrier is abandoned and put in the +pool, it is also linked into `pooled_list`. When we search our +`pooled_list` and find a carrier that is no longer in the pool, we +move that carrier from `pooled_list` to `traitor_list` as it is now +employed by another allocator. If searching `pooled_list` fails, we +also do a limited search of `traitor_list`. When finding an abandoned +carrier in `traitor_list` it is either employed, or moved back to +`pooled_list` if it could not satisfy the allocation request. + +When searching `pooled_list` and `traitor_list` we always start at the +point where the last search ended. This to avoid clustering +problems and increase the probability to find a "good" carrier. As +`pooled_list` and `traitor_list` are only accessed by the owning +allocator instance, they need no thread synchronization at all. + +Furthermore, the search for own carriers that are scheduled +for deallocation is now done as the last search option. The idea is +that it is better to reuse a poorly utilized carrier, than to +resurrect an empty carrier that was just about to be released back to +the OS. + ### Result ### The use of this strategy of abandoning carriers with poor utilization diff --git a/erts/emulator/internal_doc/SuperCarrier.md b/erts/emulator/internal_doc/SuperCarrier.md new file mode 100644 index 0000000000..0ad6af41de --- /dev/null +++ b/erts/emulator/internal_doc/SuperCarrier.md @@ -0,0 +1,191 @@ +Super Carrier +============= + +A super carrier is large memory area, allocated at VM start, which can +be used during runtime to allocate normal carriers from. + +The super carrier feature was introduced in OTP R16B03. It is +enabled with command line option +MMscs +and can be configured with other options. + +Problem +------- + +The initial motivation for this feature was customers asking for a way +to pre-allocate physcial memory at VM start for it to use. + +Other problems were different experienced limitations of the OS +implementation of mmap: + +* Increasingly bad performance of mmap/munmap as the number of mmap'ed areas grow. +* Fragmentation problem between mmap'ed areas. + +A third problem was management of low memory in the halfword +emulator. The implementation used a naive linear search structure to +hold free segments which would lead to poor performance when +fragmentation increased. + + +Solution +-------- + +Allocate one large continious area of address space at VM start and +then use that area to satisfy our dynamic memory need during +runtime. In other words: implement our own mmap. + +### Use cases ### + +If command line option +MMscrpm (Reserve Physical Memory) is set to +false, only virtual space is allocated for the super carrier from +start. The super carrier then acts as an "alternative mmap" implementation +without changing the consumption of physical memory pages. Physical +pages will be reserved on demand when an allocation is done from the super +carrier and be unreserved when the memory is released back to the +super carrier. + +If +MMscrpm is set to true, which is default, the initial allocation +will reserve physical memory for the entire super carrier. This can be +used by users that want to ensure a certain *minimum* amount of +physical memory for the VM. + +However, what reservation of physical memory actually means highly +depends on the operating system, and how it is configured. For +example, different memory overcommit settings on Linux drastically +change the behaviour. + +A third feature is to have the super carrier limit the *maximum* +amount of memory used by the VM. If +MMsco (Super Carrier Only) is set +to true, which is default, allocations will only be done from the +super carrier. When the super carrier gets full, the VM will fail due +to out of memory. +If +MMsco is false, allocations will use mmap directly if the super +carrier is full. + + + +### Implementation ### + +The entire super carrier implementation is kept in erl_mmap.c. The +name suggest that it can be viewed as our own mmap implementation. + +A super carrier needs to satisfy two slightly different kinds of +allocation requests; multi block carriers (MBC) and single block +carriers (SBC). They are both rather large blocks of continious +memory, but MBCs and SBCs have different demands on alignment and +size. + +SBCs can have arbitrary size and do only need minimum 8-byte +alignment. + +MBCs are more restricted. They can only have a number of fixed +sizes that are powers of 2. The start address need to have a very +large aligment (currently 256 kb, called "super alignment"). This is a +design choice that allows very low overhead per allocated block in the +MBC. + +To reduce fragmentation within the super carrier, it is good to keep SBCs +and MBCs apart. MBCs with their uniform alignment and sizes can be +packed very efficiently together. SBCs without demand for aligment can +also be allocated quite efficiently together. But mixing them can lead +to a lot of memory wasted when we need to create large holes of +padding to the next alignment limit. + +The super carrier thus contains two areas. One area for MBCs growing from +the bottom and up. And one area for SBCs growing from the top and +down. Like a process with a heap and a stack growing towards each +other. + + +### Data structures ### + +The MBC area is called **sa** as in super aligned and the SBC area is +called **sua** as in super un-aligned. + +Note that the "super" in super alignment and the "super" in super +carrier has nothing to do with each other. We could have choosen +another naming to avoid confusion, such as "meta" carrier or "giant" +aligment. + + +-------+ <---- sua.top + | sua | + | | + |-------| <---- sua.bot + | | + | | + | | + |-------| <---- sa.top + | | + | sa | + | | + +-------+ <---- sa.bot + + +When a carrier is deallocated a free memory segment will be created +inside the corresponding area, unless the carrier was at the very top +(in `sa`) or bottom (in `sua`) in which case the area will just shrink +down or up. + +We need to keep track of all the free segments in order to reuse them +for new carrier allocations. One initial idea was to use the same +mechanism that is used to keep track of free blocks within MBCs +(alloc_util and the different strategies). However, that would not be +as straight forward as one can think and can also waste quite a lot of +memory as it uses prepended block headers. The granularity of the +super carrier is one memory page (usually 4kb). We want to allocate +and free entire pages and we don't want to waste an entire page just +to hold the block header of the following pages. + +Instead we store the meta information about all the free segments in a +dedicated area apart from the `sa` and `sua` areas. Every free segment is +represented by a descriptor struct (`ErtsFreeSegDesc`). + + typedef struct { + RBTNode snode; /* node in 'stree' */ + RBTNode anode; /* node in 'atree' */ + char* start; + char* end; + }ErtsFreeSegDesc; + +To find the smallest free segment that will satisfy a carrier allocation +(best fit), the free segments are organized in a tree sorted by +size (`stree`). We search in this tree at allocation. If no free segment of +sufficient size was found, the area (`sa` or `sua`) is instead expanded. +If two or more free segments with equal size exist, the one at lowest +address is choosen for `sa` and highest address for `sua`. + +At carrier deallocation, we want to coalesce with any adjacent free +segments, to form one large free segment. To do that, all free +segments are also organized in a tree sorted in address order (`atree`). + +So, in total we keep four trees of free descriptors for the super +carrier; two for `sa` and two for `sua`. They all use the same +red-black-tree implementation that support the different sorting +orders used. + +When allocating a new MBC we first search after a free segment in `sa`, +then try to raise `sa.top`, and then as a fallback try to search after a +free segment in `sua`. When an MBC is allocated in `sua`, a larger segment +is allocated which is then trimmed to obtain the right +alignment. Allocation search for an SBC is done in reverse order. When +an SBC is allocated in `sa`, the size is aligned up to super aligned +size. + +### The free descriptor area ### + +As mentioned above, the descriptors for the free segments are +allocated in a separate area. This area has a constant configurable +size (+MMscrfsd) that defaults to 65536 descriptors. This should be +more than enough in most cases. If the descriptors area should fill up, +new descriptor areas will be allocated first directly from the OS, and +then from `sua` and `sa` in the super carrier, and lastly from the memory +segment itself which is being deallocated. Allocating free descriptor +areas from the super carrier is only a last resort, and should be +avoided, as it creates fragmentation. + +### Halfword emulator ### + +The halfword emulator uses the super carrier implementation to manage +its low memory mappings thar are needed for all term storage. The +super carrier can here not be configured by command line options. One +could imagine a second configurable instance of the super carrier used +by high memory allocation, but that has not been implemented. -- cgit v1.2.3 From 3b7508f6e75fd304736145528b53652f839a9b46 Mon Sep 17 00:00:00 2001 From: Sina Samavati Date: Mon, 10 Nov 2014 16:01:53 +0330 Subject: Fix a typo in the zlib documentation --- erts/doc/src/zlib.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index 11a7437f5a..da8ccdecdf 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -302,7 +302,7 @@ list_to_binary([B1,B2]) Decompress data

inflate/2 decompresses as much data as possible. - It may some introduce some output latency (reading + It may introduce some output latency (reading input without producing any output).

If a preset dictionary is needed at this point (see inflateSetDictionary below), inflate/2 throws a -- cgit v1.2.3 From d5a4eb5a9e24a07cb3653967ff91557c9a21ecb4 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 7 Nov 2014 14:41:02 +0100 Subject: Improve usage of libatomic_ops for word size atomics Use AO_fetch_compare_and_swap*() when present --- erts/aclocal.m4 | 22 ++ erts/include/internal/ethread.h | 2 +- erts/include/internal/libatomic_ops/ethr_atomic.h | 343 +++++++++++++++++++++- erts/include/internal/libatomic_ops/ethread.h | 2 + 4 files changed, 359 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index ed492d55ff..d78025b0be 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1421,9 +1421,31 @@ case "$THR_LIB_NAME" in int z; AO_nop_full(); +#if defined(AO_HAVE_store) AO_store(&x, (AO_t) 0); +#elif defined(AO_HAVE_store_release) + AO_store_release(&x, (AO_t) 0); +#else +#error No store +#endif +#if defined(AO_HAVE_load) z = AO_load(&x); +#elif defined(AO_HAVE_load_acquire) + z = AO_load_acquire(&x); +#else +#error No load +#endif +#if defined(AO_HAVE_compare_and_swap_full) z = AO_compare_and_swap_full(&x, (AO_t) 0, (AO_t) 1); +#elif defined(AO_HAVE_compare_and_swap_release) + z = AO_compare_and_swap_release(&x, (AO_t) 0, (AO_t) 1); +#elif defined(AO_HAVE_compare_and_swap_acquire) + z = AO_compare_and_swap_acquire(&x, (AO_t) 0, (AO_t) 1); +#elif defined(AO_HAVE_compare_and_swap) + z = AO_compare_and_swap(&x, (AO_t) 0, (AO_t) 1); +#else +#error No compare_and_swap +#endif ], [ethr_have_native_atomics=yes ethr_have_libatomic_ops=yes]) diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 72c054b588..ad5d05704c 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -364,8 +364,8 @@ extern ethr_runtime_t ethr_runtime__; # include "sparc64/ethread.h" # endif # endif -# include "gcc/ethread.h" # include "libatomic_ops/ethread.h" +# include "gcc/ethread.h" # endif # elif defined(ETHR_HAVE_LIBATOMIC_OPS) # include "libatomic_ops/ethread.h" diff --git a/erts/include/internal/libatomic_ops/ethr_atomic.h b/erts/include/internal/libatomic_ops/ethr_atomic.h index fb1288c330..734cdf0890 100644 --- a/erts/include/internal/libatomic_ops/ethr_atomic.h +++ b/erts/include/internal/libatomic_ops/ethr_atomic.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2014. All Rights Reserved. * * The 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,22 +32,23 @@ * These operations need to be defined by libatomic_ops; * otherwise, we won't compile: * - AO_nop_full() - * - AO_load() - * - AO_store() - * - AO_compare_and_swap() + * - AO_load() || AO_load_aquire() + * - AO_store() || AO_store_release() + * - AO_compare_and_swap() || AO_compare_and_swap_acquire() + * || AO_compare_and_swap_release() || AO_compare_and_swap_full() * */ #if ETHR_SIZEOF_AO_T == 4 #define ETHR_HAVE_NATIVE_ATOMIC32 1 -#define ETHR_NATIVE_ATOMIC32_IMPL "libatomic_ops" +#define ETHR_NATIVE_ATOMIC32_IMPL ETHR_NATIVE_IMPL__ #define ETHR_NATMC_FUNC__(X) ethr_native_atomic32_ ## X #define ETHR_ATMC_T__ ethr_native_atomic32_t #define ETHR_AINT_T__ ethr_sint32_t #define ETHR_AINT_SUFFIX__ "l" #elif ETHR_SIZEOF_AO_T == 8 #define ETHR_HAVE_NATIVE_ATOMIC64 1 -#define ETHR_NATIVE_ATOMIC64_IMPL "libatomic_ops" +#define ETHR_NATIVE_ATOMIC64_IMPL ETHR_NATIVE_IMPL__ #define ETHR_NATMC_FUNC__(X) ethr_native_atomic64_ ## X #define ETHR_ATMC_T__ ethr_native_atomic64_t #define ETHR_AINT_T__ ethr_sint64_t @@ -74,6 +75,8 @@ ETHR_NATMC_FUNC__(addr)(ETHR_ATMC_T__ *var) return (ETHR_AINT_T__ *) &var->counter; } +#ifdef AO_HAVE_store + #if ETHR_SIZEOF_AO_T == 4 # define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_SET 1 #else @@ -86,6 +89,24 @@ ETHR_NATMC_FUNC__(set)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) AO_store(&var->counter, (AO_t) value); } +#endif + +#ifdef AO_HAVE_store_write + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_SET_WB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_SET_WB 1 +#endif + +static ETHR_INLINE void +ETHR_NATMC_FUNC__(set_wb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) +{ + AO_store_write(&var->counter, (AO_t) value); +} + +#endif + #ifdef AO_HAVE_store_release #if ETHR_SIZEOF_AO_T == 4 @@ -102,6 +123,24 @@ ETHR_NATMC_FUNC__(set_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) #endif +#ifdef AO_HAVE_store_full + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_SET_MB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_SET_MB 1 +#endif + +static ETHR_INLINE void +ETHR_NATMC_FUNC__(set_mb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) +{ + AO_store_full(&var->counter, (AO_t) value); +} + +#endif + +#ifdef AO_HAVE_load + #if ETHR_SIZEOF_AO_T == 4 # define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_READ 1 #else @@ -114,6 +153,24 @@ ETHR_NATMC_FUNC__(read)(ETHR_ATMC_T__ *var) return (ETHR_AINT_T__) AO_load(&var->counter); } +#endif + +#ifdef AO_HAVE_load_read + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_READ_RB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_READ_RB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read_rb)(ETHR_ATMC_T__ *var) +{ + return (ETHR_AINT_T__) AO_load_read(&var->counter); +} + +#endif + #ifdef AO_HAVE_load_acquire #if ETHR_SIZEOF_AO_T == 4 @@ -130,6 +187,22 @@ ETHR_NATMC_FUNC__(read_acqb)(ETHR_ATMC_T__ *var) #endif +#ifdef AO_HAVE_load_full + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_READ_MB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_READ_MB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read_mb)(ETHR_ATMC_T__ *var) +{ + return (ETHR_AINT_T__) AO_load_full(&var->counter); +} + +#endif + #ifdef AO_HAVE_fetch_and_add #if ETHR_SIZEOF_AO_T == 4 @@ -146,6 +219,54 @@ ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) #endif +#ifdef AO_HAVE_fetch_and_add_acquire + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_ADD_RETURN_ACQB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_ADD_RETURN_ACQB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(add_return_acqb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) +{ + return ((ETHR_AINT_T__) AO_fetch_and_add_acquire(&var->counter, (AO_t) incr)) + incr; +} + +#endif + +#ifdef AO_HAVE_fetch_and_add_release + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_ADD_RETURN_RELB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_ADD_RETURN_RELB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(add_return_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) +{ + return ((ETHR_AINT_T__) AO_fetch_and_add_release(&var->counter, (AO_t) incr)) + incr; +} + +#endif + +#ifdef AO_HAVE_fetch_and_add_full + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_ADD_RETURN_MB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_ADD_RETURN_MB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(add_return_mb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) +{ + return ((ETHR_AINT_T__) AO_fetch_and_add_full(&var->counter, (AO_t) incr)) + incr; +} + +#endif + #ifdef AO_HAVE_fetch_and_add1 #if ETHR_SIZEOF_AO_T == 4 @@ -178,6 +299,38 @@ ETHR_NATMC_FUNC__(inc_return_acqb)(ETHR_ATMC_T__ *var) #endif +#ifdef AO_HAVE_fetch_and_add1_release + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_INC_RETURN_RELB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_INC_RETURN_RELB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return_relb)(ETHR_ATMC_T__ *var) +{ + return ((ETHR_AINT_T__) AO_fetch_and_add1_release(&var->counter)) + 1; +} + +#endif + +#ifdef AO_HAVE_fetch_and_add1_full + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_INC_RETURN_MB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_INC_RETURN_MB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return_mb)(ETHR_ATMC_T__ *var) +{ + return ((ETHR_AINT_T__) AO_fetch_and_add1_full(&var->counter)) + 1; +} + +#endif + #ifdef AO_HAVE_fetch_and_sub1 #if ETHR_SIZEOF_AO_T == 4 @@ -194,6 +347,22 @@ ETHR_NATMC_FUNC__(dec_return)(ETHR_ATMC_T__ *var) #endif +#ifdef AO_HAVE_fetch_and_sub1_acquire + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_DEC_RETURN_ACQB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_DEC_RETURN_ACQB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return_acqb)(ETHR_ATMC_T__ *var) +{ + return ((ETHR_AINT_T__) AO_fetch_and_sub1_acquire(&var->counter)) - 1; +} + +#endif + #ifdef AO_HAVE_fetch_and_sub1_release #if ETHR_SIZEOF_AO_T == 4 @@ -210,7 +379,60 @@ ETHR_NATMC_FUNC__(dec_return_relb)(ETHR_ATMC_T__ *var) #endif -#ifdef AO_HAVE_compare_and_swap +#ifdef AO_HAVE_fetch_and_sub1_full + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_DEC_RETURN_MB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_DEC_RETURN_MB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return_mb)(ETHR_ATMC_T__ *var) +{ + return ((ETHR_AINT_T__) AO_fetch_and_sub1_full(&var->counter)) - 1; +} + +#endif + +#if defined(AO_HAVE_compare_and_swap_full) || defined(AO_HAVE_fetch_compare_and_swap_full) + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_CMPXCHG_MB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_CMPXCHG_MB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_mb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ exp) +{ +#if defined(AO_HAVE_fetch_compare_and_swap_full) + return (ETHR_AINT_T__) AO_fetch_compare_and_swap_full(&var->counter, + (AO_t) exp, + (AO_t) new); +#else + ETHR_AINT_T__ act; + do { + if (AO_compare_and_swap_full(&var->counter, (AO_t) exp, (AO_t) new)) + return exp; +#ifdef AO_HAVE_load_acquire + act = (ETHR_AINT_T__) AO_load_acquire(&var->counter); +#else + act = (ETHR_AINT_T__) AO_load(&var->counter); +#endif + } while (act == exp); +#ifndef AO_HAVE_load_acquire + AO_nop_full(); +#endif + return act; +#endif +} + +#endif + +#if defined(AO_HAVE_compare_and_swap) || defined(AO_HAVE_fetch_compare_and_swap) #if ETHR_SIZEOF_AO_T == 4 # define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_CMPXCHG 1 @@ -223,18 +445,28 @@ ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ exp) { +#if defined(AO_HAVE_fetch_compare_and_swap) + return (ETHR_AINT_T__) AO_fetch_compare_and_swap(&var->counter, + (AO_t) exp, + (AO_t) new); +#else ETHR_AINT_T__ act; do { if (AO_compare_and_swap(&var->counter, (AO_t) exp, (AO_t) new)) return exp; +#ifdef AO_HAVE_load act = (ETHR_AINT_T__) AO_load(&var->counter); +#else + act = (ETHR_AINT_T__) AO_load_aquire(&var->counter); +#endif } while (act == exp); return act; +#endif } #endif -#ifdef AO_HAVE_compare_and_swap_acquire +#if defined(AO_HAVE_compare_and_swap_acquire) || defined(AO_HAVE_fetch_compare_and_swap_acquire) #if ETHR_SIZEOF_AO_T == 4 # define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_CMPXCHG_ACQB 1 @@ -247,6 +479,11 @@ ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ exp) { +#if defined(AO_HAVE_fetch_compare_and_swap_acquire) + return (ETHR_AINT_T__) AO_fetch_compare_and_swap_acquire(&var->counter, + (AO_t) exp, + (AO_t) new); +#else ETHR_AINT_T__ act; do { if (AO_compare_and_swap_acquire(&var->counter, (AO_t) exp, (AO_t) new)) @@ -261,11 +498,55 @@ ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, AO_nop_full(); #endif return act; +#endif +} + +#endif + +#if defined(AO_HAVE_compare_and_swap_read) || defined(AO_HAVE_fetch_compare_and_swap_read) + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_CMPXCHG_RB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_CMPXCHG_RB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_rb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ exp) +{ +#if defined(AO_HAVE_fetch_compare_and_swap_read) + return (ETHR_AINT_T__) AO_fetch_compare_and_swap_read(&var->counter, + (AO_t) exp, + (AO_t) new); +#else + ETHR_AINT_T__ act; + do { + if (AO_compare_and_swap_read(&var->counter, (AO_t) exp, (AO_t) new)) + return exp; +#if defined(AO_HAVE_load_read) + act = (ETHR_AINT_T__) AO_load_read(&var->counter); +#elif defined(AO_HAVE_load) + act = (ETHR_AINT_T__) AO_load(&var->counter); +#else + act = (ETHR_AINT_T__) AO_load_acquire(&var->counter); +#endif + } while (act == exp); +#ifndef AO_HAVE_load_read +#ifdef AO_HAVE_nop_read + AO_nop_read(); +#else + AO_nop_full(); +#endif +#endif + return act; +#endif } #endif -#ifdef AO_HAVE_compare_and_swap_release +#if defined(AO_HAVE_compare_and_swap_release) || defined(AO_HAVE_fetch_compare_and_swap_release) #if ETHR_SIZEOF_AO_T == 4 # define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_CMPXCHG_RELB 1 @@ -278,13 +559,57 @@ ETHR_NATMC_FUNC__(cmpxchg_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ exp) { +#if defined(AO_HAVE_fetch_compare_and_swap_release) + return (ETHR_AINT_T__) AO_fetch_compare_and_swap_release(&var->counter, + (AO_t) exp, + (AO_t) new); +#else ETHR_AINT_T__ act; do { if (AO_compare_and_swap_release(&var->counter, (AO_t) exp, (AO_t) new)) return exp; +#ifdef AO_HAVE_load act = (ETHR_AINT_T__) AO_load(&var->counter); +#else + act = (ETHR_AINT_T__) AO_load_acquire(&var->counter); +#endif + } while (act == exp); + return act; +#endif +} + +#endif + +#if defined(AO_HAVE_compare_and_swap_write) || defined(AO_HAVE_fetch_compare_and_swap_write) + +#if ETHR_SIZEOF_AO_T == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_CMPXCHG_WB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_CMPXCHG_WB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_wb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ exp) +{ +#if defined(AO_HAVE_fetch_compare_and_swap_write) + return (ETHR_AINT_T__) AO_fetch_compare_and_swap_write(&var->counter, + (AO_t) exp, + (AO_t) new); +#else + ETHR_AINT_T__ act; + do { + if (AO_compare_and_swap_write(&var->counter, (AO_t) exp, (AO_t) new)) + return exp; +#ifdef AO_HAVE_load + act = (ETHR_AINT_T__) AO_load(&var->counter); +#else + act = (ETHR_AINT_T__) AO_load_acquire(&var->counter); +#endif } while (act == exp); return act; +#endif } #endif diff --git a/erts/include/internal/libatomic_ops/ethread.h b/erts/include/internal/libatomic_ops/ethread.h index e1fdd588bb..7375b73793 100644 --- a/erts/include/internal/libatomic_ops/ethread.h +++ b/erts/include/internal/libatomic_ops/ethread.h @@ -33,6 +33,8 @@ #define AO_USE_PENTIUM4_INSTRS #endif +#define ETHR_NATIVE_IMPL__ "libatomic_ops" + #include "atomic_ops.h" #include "ethr_membar.h" #include "ethr_atomic.h" -- cgit v1.2.3 From d642dc89e098b572d1b7419fc98c36f324d8407e Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 7 Nov 2014 16:52:56 +0100 Subject: Implement support for double word atomics using libatomic_ops --- .../internal/libatomic_ops/ethr_dw_atomic.h | 567 +++++++++++++++++++++ erts/include/internal/libatomic_ops/ethread.h | 1 + 2 files changed, 568 insertions(+) create mode 100644 erts/include/internal/libatomic_ops/ethr_dw_atomic.h (limited to 'erts') diff --git a/erts/include/internal/libatomic_ops/ethr_dw_atomic.h b/erts/include/internal/libatomic_ops/ethr_dw_atomic.h new file mode 100644 index 0000000000..4dd9f41e96 --- /dev/null +++ b/erts/include/internal/libatomic_ops/ethr_dw_atomic.h @@ -0,0 +1,567 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2014. All Rights Reserved. + * + * The 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: Native double word atomics using libatomic_ops + * Author: Rickard Green + */ + +#ifndef ETHR_LIBATOMIC_OPS_DW_ATOMIC_H__ +#define ETHR_LIBATOMIC_OPS_DW_ATOMIC_H__ + +#if defined(AO_HAVE_double_t) \ + && (defined(AO_HAVE_double_load_acquire) \ + || defined(AO_HAVE_double_load)) \ + && (defined(AO_HAVE_compare_double_and_swap_double) \ + || defined(AO_HAVE_compare_double_and_swap_double_full) \ + || defined(AO_HAVE_compare_double_and_swap_double_acquire) \ + || defined(AO_HAVE_compare_double_and_swap_double_release) \ + || defined(AO_HAVE_double_compare_and_swap) \ + || defined(AO_HAVE_double_compare_and_swap_full) \ + || defined(AO_HAVE_double_compare_and_swap_acquire) \ + || defined(AO_HAVE_double_compare_and_swap_release)) + +#if ETHR_SIZEOF_PTR == 4 +# define ETHR_NATIVE_SU_DW_SINT_T ethr_sint64_t +#elif ETHR_SIZEOF_PTR == 8 && defined(ETHR_HAVE_INT128_T) +# define ETHR_NATIVE_SU_DW_SINT_T ethr_sint128_t +#endif + +typedef union { + volatile AO_double_t dw_mem; +#if defined(ETHR_NATIVE_SU_DW_SINT_T) + ETHR_NATIVE_SU_DW_SINT_T su_dw_sint; +#endif +} ethr_native_dw_atomic_t; + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_NATIVE_SU_DW_ATOMIC +#else +# define ETHR_HAVE_NATIVE_DW_ATOMIC +#endif + +#define ETHR_NATIVE_DW_ATOMIC_IMPL ETHR_NATIVE_IMPL__ + +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_ATOMIC_IMPL__) + + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_NDWA_FUNC__(Func) ethr_native_su_dw_atomic_ ## Func +# define ETHR_NDWA_RET_3_TYPE__ ETHR_NATIVE_SU_DW_SINT_T +# define ETHR_NDWA_RET_2_TYPE__ ETHR_NATIVE_SU_DW_SINT_T +# define ETHR_NDWA_VAL_ARG_TYPE__ ETHR_NATIVE_SU_DW_SINT_T +# define ETHR_NDWA_DECL_ARG__(Arg) +# if defined(AO_HAVE_DOUBLE_PTR_STORAGE) +# define ETHR_NDWA_VAL2AOVAL__(AOV, V) \ + ((AOV).AO_whole = (double_ptr_storage) (V)) +# define ETHR_NDWA_AOVAL2VAL__(AOV, V) \ + ((V) = (ETHR_NATIVE_SU_DW_SINT_T) (AOV).AO_whole) +# define ETHR_NDWA_RETURN_VAL_3__(SUCCESS, AOVAL, VAL) \ + do { \ + return (ETHR_NATIVE_SU_DW_SINT_T) (AOVAL).AO_whole; \ + } while (0) +# define ETHR_NDWA_RETURN_VAL_2__(AOVAL, VAL) \ + do { \ + return (ETHR_NATIVE_SU_DW_SINT_T) (AOVAL).AO_whole; \ + } while (0) +# define ETHR_NDWA_AOVAL_EQ__(AOV1, AOV2) \ + ((AOV1).AO_whole == (AOV2).AO_whole) +# else +typedef union { + ethr_sint_t sint[2]; + ETHR_NATIVE_SU_DW_SINT_T dw_sint; +} ethr_dw_splitter_t; +# define ETHR_NDWA_VAL2AOVAL__(AOV, V) \ + do { \ + ethr_dw_splitter_t tmp__; \ + tmp__.dw_sint = (V); \ + (AOV).AO_val1 = (AO_t) tmp__.sint[0]; \ + (AOV).AO_val2 = (AO_t) tmp__.sint[1]; \ + } while (0) +# define ETHR_NDWA_AOVAL2VAL__(AOV, V) \ + do { \ + ethr_dw_splitter_t tmp__; \ + tmp__.sint[0] = (ethr_sint_t) (AOV).AO_val1; \ + tmp__.sint[1] = (ethr_sint_t) (AOV).AO_val2; \ + (V) = tmp__.dw_sint; \ + } while (0) +# define ETHR_NDWA_RETURN_VAL_3__(SUCCESS, AOVAL, VAL) \ + do { \ + ethr_dw_splitter_t tmp__; \ + tmp__.sint[0] = (ethr_sint_t) (AOVAL).AO_val1; \ + tmp__.sint[1] = (ethr_sint_t) (AOVAL).AO_val2; \ + return tmp__.dw_sint; \ + } while (0) +# define ETHR_NDWA_AOVAL_EQ__(AOV1, AOV2) \ + ((AOV1).AO_val1 == (AOV2).AO_val1 \ + && (AOV1).AO_val2 == (AOV2).AO_val2) +# endif +#else +# define ETHR_NDWA_FUNC__(Func) ethr_native_dw_atomic_ ## Func +# define ETHR_NDWA_RET_3_TYPE__ int +# define ETHR_NDWA_RET_2_TYPE__ void +# define ETHR_NDWA_VAL_ARG_TYPE__ ethr_sint_t * +# define ETHR_NDWA_DECL_ARG__(Arg) , ETHR_NDWA_VAL_ARG_TYPE__ Arg +# define ETHR_NDWA_VAL2AOVAL__(AOV, V) \ + do { \ + (AOV).AO_val1 = (AO_t) (V)[0]; \ + (AOV).AO_val2 = (AO_t) (V)[1]; \ + } while (0) +# define ETHR_NDWA_AOVAL2VAL__(AOV, V) \ + do { \ + ethr_dw_splitter_t tmp__; \ + (V)[0] = (ethr_sint_t) (AOV).AO_val1; \ + (V)[1] = (ethr_sint_t) (AOV).AO_val2; \ + } while (0) +# define ETHR_NDWA_RETURN_VAL_3__(SUCCESS, AOVAL, VAL) \ + do { \ + (VAL)[0] = (ethr_sint_t) (AOVAL).AO_val1; \ + (VAL)[1] = (ethr_sint_t) (AOVAL).AO_val2; \ + return (SUCCESS); \ + } while (0) +# define ETHR_NDWA_RETURN_VAL_2__(AOVAL, VAL) \ + do { \ + (VAL)[0] = (ethr_sint_t) (AOVAL).AO_val1; \ + (VAL)[1] = (ethr_sint_t) (AOVAL).AO_val2; \ + return; \ + } while (0) +# if defined(AO_HAVE_DOUBLE_PTR_STORAGE) +# define ETHR_NDWA_AOVAL_EQ__(AOV1, AOV2) \ + ((AOV1).AO_whole == (AOV2).AO_whole) +# else +# define ETHR_NDWA_AOVAL_EQ__(AOV1, AOV2) \ + ((AOV1).AO_val1 == (AOV2).AO_val1 \ + && (AOV1).AO_val2 == (AOV2).AO_val2) +# endif +#endif + +#define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_ADDR +static ETHR_INLINE ethr_sint_t * +ethr_native_dw_atomic_addr(ethr_native_dw_atomic_t *var) +{ + return (ethr_sint_t *) &var->dw_mem; +} + +#ifdef AO_HAVE_double_load + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_READ +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_READ +#endif + +static ETHR_INLINE ETHR_NDWA_RET_2_TYPE__ +ETHR_NDWA_FUNC__(read)(ethr_native_dw_atomic_t *var + ETHR_NDWA_DECL_ARG__(val)) +{ + AO_double_t act = AO_double_load(&var->dw_mem); + ETHR_NDWA_RETURN_VAL_2__(act, val); +} + +#endif + +#ifdef AO_HAVE_double_load_read + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_READ_RB +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_READ_RB +#endif + +static ETHR_INLINE ETHR_NDWA_RET_2_TYPE__ +ETHR_NDWA_FUNC__(read_rb)(ethr_native_dw_atomic_t *var + ETHR_NDWA_DECL_ARG__(val)) +{ + AO_double_t act = AO_double_load_read(&var->dw_mem); + ETHR_NDWA_RETURN_VAL_2__(act, val); +} + +#endif + +#ifdef AO_HAVE_double_load_acquire + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_READ_ACQB +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_READ_ACQB +#endif + +static ETHR_INLINE ETHR_NDWA_RET_2_TYPE__ +ETHR_NDWA_FUNC__(read_acqb)(ethr_native_dw_atomic_t *var + ETHR_NDWA_DECL_ARG__(val)) +{ + AO_double_t act = AO_double_load_acquire(&var->dw_mem); + ETHR_NDWA_RETURN_VAL_2__(act, val); +} + +#endif + +#ifdef AO_HAVE_double_store + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_SET +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_SET +#endif + +static ETHR_INLINE void +ETHR_NDWA_FUNC__(set)(ethr_native_dw_atomic_t *var, + ETHR_NDWA_VAL_ARG_TYPE__ val) +{ + AO_double_t new; + ETHR_NDWA_VAL2AOVAL__(new, val); + AO_double_store(&var->dw_mem, new); +} + +#endif + +#ifdef AO_HAVE_double_store_write + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_SET_WB +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_SET_WB +#endif + +static ETHR_INLINE void +ETHR_NDWA_FUNC__(set_wb)(ethr_native_dw_atomic_t *var, + ETHR_NDWA_VAL_ARG_TYPE__ val) +{ + AO_double_t new; + ETHR_NDWA_VAL2AOVAL__(new, val); + AO_double_store_write(&var->dw_mem, new); +} + +#endif + +#ifdef AO_HAVE_double_store_release + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_SET_RELB +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_SET_RELB +#endif + +static ETHR_INLINE void +ETHR_NDWA_FUNC__(set_relb)(ethr_native_dw_atomic_t *var, + ETHR_NDWA_VAL_ARG_TYPE__ val) +{ + AO_double_t new; + ETHR_NDWA_VAL2AOVAL__(new, val); + AO_double_store_release(&var->dw_mem, new); +} + +#endif + +#if defined(AO_HAVE_double_compare_and_swap_full) || defined(AO_HAVE_compare_double_and_swap_double_full) + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_CMPXCHG_MB +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_CMPXCHG_MB +#endif + +static ETHR_INLINE ETHR_NDWA_RET_3_TYPE__ +ETHR_NDWA_FUNC__(cmpxchg_mb)(ethr_native_dw_atomic_t *var, + ETHR_NDWA_VAL_ARG_TYPE__ new, + ETHR_NDWA_VAL_ARG_TYPE__ exp) +{ + AO_double_t ao_act, ao_new, ao_exp; + + ETHR_NDWA_VAL2AOVAL__(ao_exp, exp); + ETHR_NDWA_VAL2AOVAL__(ao_new, new); + + do { + int xchgd; +#if defined(AO_HAVE_double_compare_and_swap_full) + xchgd = AO_double_compare_and_swap_full(&var->dw_mem, ao_exp, ao_new); +#elif defined(AO_HAVE_compare_double_and_swap_double_full) + xchgd = AO_compare_double_and_swap_double_full(&var->dw_mem, + ao_exp.AO_val1, + ao_exp.AO_val2, + ao_new.AO_val1, + ao_new.AO_val2); +#endif + + if (xchgd) + ETHR_NDWA_RETURN_VAL_3__(1, ao_exp, exp); + +#ifdef AO_HAVE_double_load_acquire + ao_act = AO_double_load_acquire(&var->dw_mem); +#else + ao_act = AO_double_load(&var->dw_mem); +#endif + + } while (ETHR_NDWA_AOVAL_EQ__(ao_exp, ao_act)); + +#ifndef AO_HAVE_double_load_acquire + AO_nop_full(); +#endif + + ETHR_NDWA_RETURN_VAL_3__(1, ao_act, exp); +} + +#endif + +#if defined(AO_HAVE_double_compare_and_swap) || defined(AO_HAVE_compare_double_and_swap_double) + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_CMPXCHG +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_CMPXCHG +#endif + +static ETHR_INLINE ETHR_NDWA_RET_3_TYPE__ +ETHR_NDWA_FUNC__(cmpxchg)(ethr_native_dw_atomic_t *var, + ETHR_NDWA_VAL_ARG_TYPE__ new, + ETHR_NDWA_VAL_ARG_TYPE__ exp) +{ + AO_double_t ao_act, ao_new, ao_exp; + + ETHR_NDWA_VAL2AOVAL__(ao_exp, exp); + ETHR_NDWA_VAL2AOVAL__(ao_new, new); + + do { + int xchgd; +#if defined(AO_HAVE_double_compare_and_swap) + xchgd = AO_double_compare_and_swap(&var->dw_mem, ao_exp, ao_new); +#elif defined(AO_HAVE_compare_double_and_swap_double) + xchgd = AO_compare_double_and_swap_double(&var->dw_mem, + ao_exp.AO_val1, + ao_exp.AO_val2, + ao_new.AO_val1, + ao_new.AO_val2); +#endif + + if (xchgd) + ETHR_NDWA_RETURN_VAL_3__(1, ao_exp, exp); + +#ifdef AO_HAVE_double_load + ao_act = AO_double_load(&var->dw_mem); +#else + ao_act = AO_double_load_acquire(&var->dw_mem); +#endif + + } while (ETHR_NDWA_AOVAL_EQ__(ao_exp, ao_act)); + + ETHR_NDWA_RETURN_VAL_3__(1, ao_act, exp); +} + +#endif + +#if defined(AO_HAVE_double_compare_and_swap_read) || defined(AO_HAVE_compare_double_and_swap_double_read) + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_CMPXCHG_RB +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_CMPXCHG_RB +#endif + +static ETHR_INLINE ETHR_NDWA_RET_3_TYPE__ +ETHR_NDWA_FUNC__(cmpxchg_rb)(ethr_native_dw_atomic_t *var, + ETHR_NDWA_VAL_ARG_TYPE__ new, + ETHR_NDWA_VAL_ARG_TYPE__ exp) +{ + AO_double_t ao_act, ao_new, ao_exp; + + ETHR_NDWA_VAL2AOVAL__(ao_exp, exp); + ETHR_NDWA_VAL2AOVAL__(ao_new, new); + + do { + int xchgd; +#if defined(AO_HAVE_double_compare_and_swap_read) + xchgd = AO_double_compare_and_swap_read(&var->dw_mem, ao_exp, ao_new); +#elif defined(AO_HAVE_compare_double_and_swap_double_read) + xchgd = AO_compare_double_and_swap_double_read(&var->dw_mem, + ao_exp.AO_val1, + ao_exp.AO_val2, + ao_new.AO_val1, + ao_new.AO_val2); +#endif + + if (xchgd) + ETHR_NDWA_RETURN_VAL_3__(1, ao_exp, exp); + +#if defined(AO_HAVE_double_load_read) + ao_act = AO_double_load_read(&var->dw_mem); +#elif defined(AO_HAVE_double_load) + ao_act = AO_double_load(&var->dw_mem); +#else + ao_act = AO_double_load_acquire(&var->dw_mem); +#endif + + } while (ETHR_NDWA_AOVAL_EQ__(ao_exp, ao_act)); + +#ifndef AO_HAVE_double_load_read +#ifdef AO_HAVE_nop_read + AO_nop_read(); +#else + AO_nop_full(); +#endif +#endif + + ETHR_NDWA_RETURN_VAL_3__(1, ao_act, exp); +} + +#endif + +#if defined(AO_HAVE_double_compare_and_swap_acquire) || defined(AO_HAVE_compare_double_and_swap_double_acquire) + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_CMPXCHG_ACQB +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_CMPXCHG_ACQB +#endif + +static ETHR_INLINE ETHR_NDWA_RET_3_TYPE__ +ETHR_NDWA_FUNC__(cmpxchg_acqb)(ethr_native_dw_atomic_t *var, + ETHR_NDWA_VAL_ARG_TYPE__ new, + ETHR_NDWA_VAL_ARG_TYPE__ exp) +{ + AO_double_t ao_act, ao_new, ao_exp; + + ETHR_NDWA_VAL2AOVAL__(ao_exp, exp); + ETHR_NDWA_VAL2AOVAL__(ao_new, new); + + do { + int xchgd; +#if defined(AO_HAVE_double_compare_and_swap_acquire) + xchgd = AO_double_compare_and_swap_acquire(&var->dw_mem, ao_exp, ao_new); +#elif defined(AO_HAVE_compare_double_and_swap_double_acquire) + xchgd = AO_compare_double_and_swap_double_acquire(&var->dw_mem, + ao_exp.AO_val1, + ao_exp.AO_val2, + ao_new.AO_val1, + ao_new.AO_val2); +#endif + + if (xchgd) + ETHR_NDWA_RETURN_VAL_3__(1, ao_exp, exp); + +#ifdef AO_HAVE_double_load_acquire + ao_act = AO_double_load_acquire(&var->dw_mem); +#else + ao_act = AO_double_load(&var->dw_mem); +#endif + + } while (ETHR_NDWA_AOVAL_EQ__(ao_exp, ao_act)); + +#ifndef AO_HAVE_double_load_acquire + AO_nop_full(); +#endif + + ETHR_NDWA_RETURN_VAL_3__(1, ao_act, exp); +} + +#endif + +#if defined(AO_HAVE_double_compare_and_swap_write) || defined(AO_HAVE_compare_double_and_swap_double_write) + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_CMPXCHG_WB +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_CMPXCHG_WB +#endif + +static ETHR_INLINE ETHR_NDWA_RET_3_TYPE__ +ETHR_NDWA_FUNC__(cmpxchg_wb)(ethr_native_dw_atomic_t *var, + ETHR_NDWA_VAL_ARG_TYPE__ new, + ETHR_NDWA_VAL_ARG_TYPE__ exp) +{ + AO_double_t ao_act, ao_new, ao_exp; + + ETHR_NDWA_VAL2AOVAL__(ao_exp, exp); + ETHR_NDWA_VAL2AOVAL__(ao_new, new); + + do { + int xchgd; +#if defined(AO_HAVE_double_compare_and_swap_write) + xchgd = AO_double_compare_and_swap_write(&var->dw_mem, ao_exp, ao_new); +#elif defined(AO_HAVE_compare_double_and_swap_double_write) + xchgd = AO_compare_double_and_swap_double_write(&var->dw_mem, + ao_exp.AO_val1, + ao_exp.AO_val2, + ao_new.AO_val1, + ao_new.AO_val2); +#endif + + if (xchgd) + ETHR_NDWA_RETURN_VAL_3__(1, ao_exp, exp); + +#ifdef AO_HAVE_double_load + ao_act = AO_double_load(&var->dw_mem); +#else + ao_act = AO_double_load_acquire(&var->dw_mem); +#endif + + } while (ETHR_NDWA_AOVAL_EQ__(ao_exp, ao_act)); + + ETHR_NDWA_RETURN_VAL_3__(1, ao_act, exp); +} + +#endif + +#if defined(AO_HAVE_double_compare_and_swap_release) || defined(AO_HAVE_compare_double_and_swap_double_release) + +#if defined(ETHR_NATIVE_SU_DW_SINT_T) +# define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_CMPXCHG_RELB +#else +# define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_CMPXCHG_RELB +#endif + +static ETHR_INLINE ETHR_NDWA_RET_3_TYPE__ +ETHR_NDWA_FUNC__(cmpxchg_relb)(ethr_native_dw_atomic_t *var, + ETHR_NDWA_VAL_ARG_TYPE__ new, + ETHR_NDWA_VAL_ARG_TYPE__ exp) +{ + AO_double_t ao_act, ao_new, ao_exp; + + ETHR_NDWA_VAL2AOVAL__(ao_exp, exp); + ETHR_NDWA_VAL2AOVAL__(ao_new, new); + + do { + int xchgd; +#if defined(AO_HAVE_double_compare_and_swap_release) + xchgd = AO_double_compare_and_swap_release(&var->dw_mem, ao_exp, ao_new); +#elif defined(AO_HAVE_compare_double_and_swap_double_release) + xchgd = AO_compare_double_and_swap_double_release(&var->dw_mem, + ao_exp.AO_val1, + ao_exp.AO_val2, + ao_new.AO_val1, + ao_new.AO_val2); +#endif + + if (xchgd) + ETHR_NDWA_RETURN_VAL_3__(1, ao_exp, exp); + + ao_act = AO_double_load(&var->dw_mem); + + } while (ETHR_NDWA_AOVAL_EQ__(ao_exp, ao_act)); + + ETHR_NDWA_RETURN_VAL_3__(1, ao_act, exp); +} + +#endif + +#endif /* defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_ATOMIC_IMPL__) */ + +#endif /* Have AO double functionality ... */ + +#endif /* ETHR_LIBATOMIC_OPS_DW_ATOMIC_H__ */ + diff --git a/erts/include/internal/libatomic_ops/ethread.h b/erts/include/internal/libatomic_ops/ethread.h index 7375b73793..d65ee19b04 100644 --- a/erts/include/internal/libatomic_ops/ethread.h +++ b/erts/include/internal/libatomic_ops/ethread.h @@ -38,6 +38,7 @@ #include "atomic_ops.h" #include "ethr_membar.h" #include "ethr_atomic.h" +#include "ethr_dw_atomic.h" #endif -- cgit v1.2.3 From 08504087f66ab23e39c082782524e2d1e531e3e7 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 11 Nov 2014 15:16:03 +0100 Subject: Remove comments about unicode atoms in OTP 18 There was once a plan to implement support for unicode atoms in OTP 18. This plan has been stopped until further notice, and the information about this is now removed from the documentation. --- erts/doc/src/erl_ext_dist.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml index fa083db4c7..a6e7dddbed 100644 --- a/erts/doc/src/erl_ext_dist.xml +++ b/erts/doc/src/erl_ext_dist.xml @@ -126,9 +126,8 @@ However, only characters that can be encoded using Latin1 (ISO-8859-1) are currently supported in atoms. The support for UTF-8 encoded atoms in the external format has been implemented in order to be able to support - all Unicode characters in atoms in some future release. Full - support for Unicode atoms will not happen before OTP-R18, and might - be introduced even later than that. Until full Unicode support for + all Unicode characters in atoms in some future release. + Until full Unicode support for atoms has been introduced, it is an error to pass atoms containing characters that cannot be encoded in Latin1, and the behavior is undefined.

-- cgit v1.2.3 From afd79e01d3375d8e8217b258ffb4d9b90541c922 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 13 Nov 2014 19:59:27 +0100 Subject: erts: Optimize ets:lookup and ets:take for bags by reducing number of iterations through objects with matching key --- erts/emulator/beam/erl_db_hash.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 633a2272ad..6e8d892561 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -382,7 +382,7 @@ static HashDbTerm* search_list(DbTableHash* tb, Eterm key, static void shrink(DbTableHash* tb, int nactive); static void grow(DbTableHash* tb, int nactive); static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2, - DbTableHash*); + Uint sz, DbTableHash*); static int analyze_pattern(DbTableHash *tb, Eterm pattern, struct mp_info *mpi); @@ -887,13 +887,17 @@ get_term_list(Process *p, DbTableHash *tb, Eterm key, HashValue hval, { HashDbTerm* b2 = b1->next; Eterm copy; + Uint sz = b1->dbterm.size + 2; if (tb->common.status & (DB_BAG | DB_DUPLICATE_BAG)) { while (b2 && has_key(tb, b2, key, hval)) { + if (b2->hvalue != INVALID_HASH) + sz += b2->dbterm.size + 2; + b2 = b2->next; } } - copy = build_term_list(p, b1, b2, tb); + copy = build_term_list(p, b1, b2, sz, tb); CHECK_TABLES(); if (bend) { *bend = b2; @@ -1253,7 +1257,7 @@ static int db_slot_hash(Process *p, DbTable *tbl, Eterm slot_term, Eterm *ret) lck = RLOCK_HASH(tb, slot); nactive = NACTIVE(tb); if (slot < nactive) { - *ret = build_term_list(p, BUCKET(tb, slot), 0, tb); + *ret = build_term_list(p, BUCKET(tb, slot), NULL, 0, tb); retval = DB_ERROR_NONE; } else if (slot == nactive) { @@ -2536,23 +2540,23 @@ static int free_seg(DbTableHash *tb, int free_records) ** Copy terms from ptr1 until ptr2 ** works for ptr1 == ptr2 == 0 => [] ** or ptr2 == 0 +** sz is either precalculated heap size or 0 if not known */ static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2, - DbTableHash* tb) + Uint sz, DbTableHash* tb) { - int sz = 0; HashDbTerm* ptr; Eterm list = NIL; Eterm copy; Eterm *hp, *hend; - ptr = ptr1; - while(ptr != ptr2) { - - if (ptr->hvalue != INVALID_HASH) - sz += ptr->dbterm.size + 2; - - ptr = ptr->next; + if (!sz) { + ptr = ptr1; + while(ptr != ptr2) { + if (ptr->hvalue != INVALID_HASH) + sz += ptr->dbterm.size + 2; + ptr = ptr->next; + } } hp = HAlloc(p, sz); -- cgit v1.2.3 From 23aa4adad9b2daad07f92284ffafd7e9df3e688d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 20 Nov 2014 18:09:36 +0100 Subject: erts,crypto: Add configure option --with-ssl-rpath --- erts/configure.in | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 9864d03cde..400fb5e71f 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -4102,6 +4102,20 @@ esac ], [with_ssl_incl=$with_ssl]) #default +AC_ARG_WITH(ssl-rpath, +AS_HELP_STRING([--with-ssl-rpath=yes|no|PATHS], + [runtime library path for OpenSSL. Default is "yes", which equates to a + number of standard locations. If "no", then no runtime + library paths wil be used. Anything else should be a + comma separated list of paths.]), +[ +case X$with_ssl in + Xno) AC_MSG_ERROR([--with-ssl-rpath set without --with-ssl]);; +esac +], +[with_ssl_rpath=yes]) #default + + AC_ARG_ENABLE(dynamic-ssl-lib, AS_HELP_STRING([--disable-dynamic-ssl-lib], [disable using dynamic openssl libraries]), @@ -4567,7 +4581,12 @@ cc_rflg="$CFLAG_RUNTIME_LIBRARY_PATH" ld_rflg="$LDFLAG_RUNTIME_LIBRARY_PATH" ded_ld_rflg="$DED_LD_FLAG_RUNTIME_LIBRARY_PATH" -if test "$SSL_APP" != "" && test "$SSL_DYNAMIC_ONLY" = "yes" && \ + +case "$with_ssl_rpath" in + +yes) # Use standard lib locations for ssl runtime library path + + if test "$SSL_APP" != "" && test "$SSL_DYNAMIC_ONLY" = "yes" && \ { test "$cc_rflg" != "" || test "$ld_rflg" != "" || test "$ded_ld_rflg" != ""; } ; then AC_MSG_CHECKING(for ssl runtime library path to use) @@ -4651,7 +4670,25 @@ if test "$SSL_APP" != "" && test "$SSL_DYNAMIC_ONLY" = "yes" && \ AC_MSG_RESULT([$rpath]) test "$rpath" != "" || AC_MSG_WARN([Cannot set run path during linking]) -fi + fi + ;; + +no) # Use no ssl runtime library path + SSL_DED_LD_RUNTIME_LIBRARY_PATH= + ;; + +*) # Use ssl runtime library paths set by --with-ssl-rpath (without any check) + ded_ld_rpath= + delimit= + for dir in `echo $with_ssl_rpath | sed "s/,/ /g"`; do + ded_ld_rpath="$ded_ld_rpath$delimit$ded_ld_rflg$dir" + delimit=" " + done + SSL_DED_LD_RUNTIME_LIBRARY_PATH="$ded_ld_rpath" + ;; + +esac + #-------------------------------------------------------------------- # Os mon stuff. -- cgit v1.2.3 From e3f4cd83939f28e17addac889208985196d22f22 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 5 Nov 2014 11:18:05 +0100 Subject: erts: Use finite instead of isfinite with gcc Turns out that isfinite emits a function call and not an instruction in gcc, this makes estone float arith about 50-75% slower. finite emits the instruction so we use that instead. --- erts/emulator/sys/unix/erl_unix_sys.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index c3dba69acb..f7a6298d5b 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -229,6 +229,17 @@ extern void sys_stop_cat(void); #ifdef USE_ISINF_ISNAN /* simulate finite() */ # define isfinite(f) (!isinf(f) && !isnan(f)) # define HAVE_ISFINITE +#elif defined(__GNUC__) && defined(HAVE_FINITE) +/* We use finite in gcc as it emits assembler instead of + the function call that isfinite emits. The assembler is + significantly faster. */ +# ifdef isfinite +# undef isfinite +# endif +# define isfinite finite +# ifndef HAVE_ISFINITE +# define HAVE_ISFINITE +# endif #elif defined(isfinite) && !defined(HAVE_ISFINITE) # define HAVE_ISFINITE #elif !defined(HAVE_ISFINITE) && defined(HAVE_FINITE) -- cgit v1.2.3 From 59740d8fee66f3326b30ac25882b52ffada4bac2 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Fri, 6 Jun 2014 15:29:49 +0400 Subject: epmd: Added systemd notify support to EPMD Signed-off-by: Peter Lemenkov --- erts/configure.in | 3 +++ erts/epmd/src/epmd.c | 8 +++++--- erts/epmd/src/epmd_srv.c | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 9864d03cde..a594d59355 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1691,10 +1691,13 @@ systemd_daemon_save_LIBS=$LIBS LIBS= AC_SEARCH_LIBS(sd_listen_fds,[systemd systemd-daemon], [have_sd_listen_fds=yes],[have_sd_listen_fds=no],$systemd_daemon_save_LIBS) +AC_SEARCH_LIBS(sd_notify,[systemd systemd-daemon], + [have_sd_notify=yes],[have_sd_notify=no],$systemd_daemon_save_LIBS) AC_CHECK_HEADERS(systemd/sd-daemon.h, [have_systemd_sd_daemon_h=yes],[have_systemd_sd_daemon_h=no]) if test x"$have_sd_listen_fds" = x"yes" && \ + test x"$have_sd_notify" = x"yes" && \ test x"$have_systemd_sd_daemon_h" = x"yes"; then AC_DEFINE([HAVE_SYSTEMD_DAEMON],[1],[Define if you have systemd daemon]) SYSTEMD_DAEMON_LIBS=$LIBS diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 9630e0cdf0..dff9bc64de 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -593,9 +593,11 @@ void epmd_cleanup_exit(EpmdVars *g, int exitval) for(i=0; g->argv[i] != NULL; ++i) free(g->argv[i]); free(g->argv); - } - - + } +#ifdef HAVE_SYSTEMD_SD_DAEMON_H + sd_notifyf(0, "STATUS=Exited.\n" + "ERRNO=%i", exitval); +#endif // HAVE_SYSTEMD_SD_DAEMON_H exit(exitval); } diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 48fd7a5f9c..18d898a915 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -399,8 +399,11 @@ void run(EpmdVars *g) } select_fd_set(g, listensock[i]); } -#ifdef HAVE_SYSTEMD_SD_DAEMON_H +#ifdef HAVE_SYSTEMD_SD_DAEMON_H } + sd_notifyf(0, "READY=1\n" + "STATUS=Processing port mapping requests...\n" + "MAINPID=%lu", (unsigned long) getpid()); #endif dbg_tty_printf(g,2,"entering the main select() loop"); -- cgit v1.2.3 From 5db2345baa3bb9c835f91aafac3dde6e595debc7 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Sun, 2 Nov 2014 18:06:21 +0300 Subject: epmd: Unify systemd autoconf macros usage Don't use both HAVE_SYSTEMD_DAEMON and HAVE_SYSTEMD_SD_DAEMON - use only the former one and remove the latter one entirely. Signed-off-by: Peter Lemenkov --- erts/epmd/src/epmd.c | 16 ++++++++-------- erts/epmd/src/epmd_int.h | 8 ++++---- erts/epmd/src/epmd_srv.c | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index dff9bc64de..9699491526 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -175,9 +175,9 @@ int main(int argc, char** argv) g->nodes.reg = g->nodes.unreg = g->nodes.unreg_tail = NULL; g->nodes.unreg_count = 0; g->active_conn = 0; -#ifdef HAVE_SYSTEMD_SD_DAEMON_H +#ifdef HAVE_SYSTEMD_DAEMON g->is_systemd = 0; -#endif +#endif /* HAVE_SYSTEMD_DAEMON */ for (i = 0; i < MAX_LISTEN_SOCKETS; i++) g->listenfd[i] = -1; @@ -251,11 +251,11 @@ int main(int argc, char** argv) else usage(g); epmd_cleanup_exit(g,0); -#ifdef HAVE_SYSTEMD_SD_DAEMON_H +#ifdef HAVE_SYSTEMD_DAEMON } else if (strcmp(argv[0], "-systemd") == 0) { g->is_systemd = 1; argv++; argc--; -#endif +#endif /* HAVE_SYSTEMD_DAEMON */ } else usage(g); } @@ -461,11 +461,11 @@ static void usage(EpmdVars *g) fprintf(stderr, " Forcibly unregisters a name with epmd\n"); fprintf(stderr, " (only allowed if -relaxed_command_check was given when \n"); fprintf(stderr, " epmd was started).\n"); -#ifdef HAVE_SYSTEMD_SD_DAEMON_H +#ifdef HAVE_SYSTEMD_DAEMON fprintf(stderr, " -systemd\n"); fprintf(stderr, " Wait for socket from systemd. The option makes sense\n"); fprintf(stderr, " when started from .socket unit.\n"); -#endif +#endif /* HAVE_SYSTEMD_DAEMON */ epmd_cleanup_exit(g,1); } @@ -594,10 +594,10 @@ void epmd_cleanup_exit(EpmdVars *g, int exitval) free(g->argv[i]); free(g->argv); } -#ifdef HAVE_SYSTEMD_SD_DAEMON_H +#ifdef HAVE_SYSTEMD_DAEMON sd_notifyf(0, "STATUS=Exited.\n" "ERRNO=%i", exitval); -#endif // HAVE_SYSTEMD_SD_DAEMON_H +#endif /* HAVE_SYSTEMD_DAEMON */ exit(exitval); } diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index c8f2192f7f..52badd7086 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -125,9 +125,9 @@ # include "sys/select.h" #endif -#ifdef HAVE_SYSTEMD_SD_DAEMON_H +#ifdef HAVE_SYSTEMD_DAEMON # include -#endif +#endif /* HAVE_SYSTEMD_DAEMON */ /* ************************************************************************ */ /* Replace some functions by others by making the function name a macro */ @@ -340,9 +340,9 @@ typedef struct { int listenfd[MAX_LISTEN_SOCKETS]; char *addresses; char **argv; -#ifdef HAVE_SYSTEMD_SD_DAEMON_H +#ifdef HAVE_SYSTEMD_DAEMON int is_systemd; -#endif +#endif /* HAVE_SYSTEMD_DAEMON */ } EpmdVars; void dbg_printf(EpmdVars*,int,const char*,...); diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 18d898a915..26e42adb19 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -244,7 +244,7 @@ void run(EpmdVars *g) } else { -#endif +#endif /* HAVE_SYSTEMD_DAEMON */ dbg_printf(g,2,"try to initiate listening port %d", g->port); @@ -312,7 +312,7 @@ void run(EpmdVars *g) } #ifdef HAVE_SYSTEMD_DAEMON } -#endif +#endif /* HAVE_SYSTEMD_DAEMON */ #if !defined(__WIN32__) && !defined(__OSE__) /* We ignore the SIGPIPE signal that is raised when we call write @@ -330,13 +330,13 @@ void run(EpmdVars *g) FD_ZERO(&g->orig_read_mask); g->select_fd_top = 0; -#ifdef HAVE_SYSTEMD_SD_DAEMON_H +#ifdef HAVE_SYSTEMD_DAEMON if (g->is_systemd) for (i = 0; i < num_sockets; i++) select_fd_set(g, listensock[i]); else { -#endif +#endif /* HAVE_SYSTEMD_DAEMON */ for (i = 0; i < num_sockets; i++) { if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0) @@ -399,12 +399,12 @@ void run(EpmdVars *g) } select_fd_set(g, listensock[i]); } -#ifdef HAVE_SYSTEMD_SD_DAEMON_H +#ifdef HAVE_SYSTEMD_DAEMON } sd_notifyf(0, "READY=1\n" "STATUS=Processing port mapping requests...\n" "MAINPID=%lu", (unsigned long) getpid()); -#endif +#endif /* HAVE_SYSTEMD_DAEMON */ dbg_tty_printf(g,2,"entering the main select() loop"); -- cgit v1.2.3 From 6b435239e4eeb2bc57ef3e8ca8a2de48817b5571 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 25 Nov 2014 14:33:04 +0100 Subject: erts: Fix port data memory allocation bug for non-immediate port data >= sizeof(Eterm)*2 words. --- erts/emulator/beam/erl_bif_port.c | 6 +++--- erts/emulator/test/port_SUITE.erl | 22 ++++++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 64bd598ba6..7ce950e090 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -472,7 +472,7 @@ cleanup_old_port_data(erts_aint_t data) ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data; size_t size; ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER; - size = sizeof(ErtsPortDataHeap) + pdhp->hsize*(sizeof(Eterm) - 1); + size = sizeof(ErtsPortDataHeap) + (pdhp->hsize-1)*sizeof(Eterm); erts_schedule_thr_prgr_later_cleanup_op(free_port_data_heap, (void *) pdhp, &pdhp->later_op, @@ -508,7 +508,7 @@ erts_port_data_size(Port *prt) } else { ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data; - return (Uint) sizeof(ErtsPortDataHeap) + pdhp->hsize*(sizeof(Eterm)-1); + return (Uint) sizeof(ErtsPortDataHeap) + (pdhp->hsize-1)*sizeof(Eterm); } } @@ -550,7 +550,7 @@ BIF_RETTYPE port_set_data_2(BIF_ALIST_2) hsize = size_object(BIF_ARG_2); pdhp = erts_alloc(ERTS_ALC_T_PORT_DATA_HEAP, - sizeof(ErtsPortDataHeap) + hsize*(sizeof(Eterm)-1)); + sizeof(ErtsPortDataHeap) + (hsize-1)*sizeof(Eterm)); hp = &pdhp->heap[0]; pdhp->off_heap.first = NULL; pdhp->off_heap.overhead = 0; diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 8792f25d76..9083545060 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -2343,8 +2343,10 @@ port_setget_data(Config) when is_list(Config) -> Port = erlang:open_port({spawn_driver, "echo_drv"}, []), NSched = erlang:system_info(schedulers_online), + HeapData = {1,2,3,<<"A heap binary">>,fun()->"This is fun"end, + list_to_binary(lists:seq(1,100))}, PRs = lists:map(fun(I) -> - spawn_opt(fun() -> port_setget_data_hammer(Port,1) end, + spawn_opt(fun() -> port_setget_data_hammer(Port,HeapData,false,1) end, [monitor, {scheduler, I rem NSched}]) end, lists:seq(1,10)), @@ -2362,13 +2364,17 @@ port_setget_data(Config) when is_list(Config) -> PRs), ok. -port_setget_data_hammer(Port, N) -> +port_setget_data_hammer(Port, HeapData, IsSet0, N) -> Rand = random:uniform(3), - try case Rand of - 1 -> true = erlang:port_set_data(Port, atom); - 2 -> true = erlang:port_set_data(Port, {1,2,3}); - 3 -> erlang:port_get_data(Port) - end + IsSet1 = try case Rand of + 1 -> true = erlang:port_set_data(Port, atom), true; + 2 -> true = erlang:port_set_data(Port, HeapData), true; + 3 -> case erlang:port_get_data(Port) of + atom -> true; + HeapData -> true; + undefined -> false=IsSet0 + end + end catch error:badarg -> true = get(prepare_for_close), @@ -2381,7 +2387,7 @@ port_setget_data_hammer(Port, N) -> after 0 -> ok end, - port_setget_data_hammer(Port, N+1). + port_setget_data_hammer(Port, HeapData, IsSet1, N+1). wait_until(Fun) -> -- cgit v1.2.3 From 9adde956a986fc41354b7581e5cf4c8eec059606 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 26 Nov 2014 11:55:26 +0100 Subject: Sort keys before generating This has to be done in order to consistently generate the same file so that we do not get rebuilds all the time. --- erts/emulator/utils/make_compiler_flags | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/utils/make_compiler_flags b/erts/emulator/utils/make_compiler_flags index cebe8cd0c5..ca1bc47113 100755 --- a/erts/emulator/utils/make_compiler_flags +++ b/erts/emulator/utils/make_compiler_flags @@ -70,7 +70,7 @@ my($prog) = $prog[$#prog]; print "/* Warning: Do not edit this file.\n"; print " Auto-generated by '$prog'.*/\n"; -foreach(keys %constants) { +foreach (sort(keys %constants)) { print "const char* erts_build_flags_$_ = \"$constants{$_}\";\n" } -- cgit v1.2.3 From b0617782416be99aa29281fed4d808d0653b63cb Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Wed, 26 Nov 2014 06:52:37 -0500 Subject: Clean up temporary dtrace file during config When configuring erts to support dynamic trace via dtrace, be sure to clean up the temporary file "erts/foo-dtrace.h" used to help check for dtrace support. Otherwise, it shows up as an untracked file in git. --- erts/configure.in | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 877e0d4c1c..1676d3d216 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3970,6 +3970,7 @@ if test "$enable_dtrace_test" = "yes" ; then DTRACE_ENABLED_2STEP=yes fi], []) + $RM -f foo-dtrace.h AS_IF([test "x$DTRACE_ENABLED_2STEP" = "xyes"], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])]) -- cgit v1.2.3 From 9f24ed8e0ddfa426c79f13c7091c39834c66780f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 26 Nov 2014 18:41:47 +0100 Subject: erts: Use internal stack for ets db_has_variable --- erts/emulator/beam/erl_db_util.c | 52 +++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index b9fd3b208e..7eb80e3bb1 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -3254,34 +3254,38 @@ int db_is_variable(Eterm obj) /* return 1 if obj contains a variable or underscore */ /* return 0 if obj is fully ground */ -int db_has_variable(Eterm obj) -{ - switch(obj & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_LIST: { - while (is_list(obj)) { - if (db_has_variable(CAR(list_val(obj)))) +int db_has_variable(Eterm node) { + DECLARE_ESTACK(s); + + ESTACK_PUSH(s,node); + while (!ESTACK_ISEMPTY(s)) { + node = ESTACK_POP(s); + switch(node & _TAG_PRIMARY_MASK) { + case TAG_PRIMARY_LIST: + while (is_list(node)) { + ESTACK_PUSH(s,CAR(list_val(node))); + node = CDR(list_val(node)); + } + ESTACK_PUSH(s,node); /* Non wellformed list or [] */ + break; + case TAG_PRIMARY_BOXED: + if (is_tuple(node)) { + Eterm *tuple = tuple_val(node); + int arity = arityval(*tuple); + while(arity--) { + ESTACK_PUSH(s,*(++tuple)); + } + } + break; + case TAG_PRIMARY_IMMED1: + if (node == am_Underscore || db_is_variable(node) >= 0) { + DESTROY_ESTACK(s); return 1; - obj = CDR(list_val(obj)); - } - return(db_has_variable(obj)); /* Non wellformed list or [] */ - } - case TAG_PRIMARY_BOXED: - if (!is_tuple(obj)) { - return 0; - } else { - Eterm *tuple = tuple_val(obj); - int arity = arityval(*tuple++); - while(arity--) { - if (db_has_variable(*tuple)) - return 1; - tuple++; } - return(0); + break; } - case TAG_PRIMARY_IMMED1: - if (obj == am_Underscore || db_is_variable(obj) >= 0) - return 1; } + DESTROY_ESTACK(s); return 0; } -- cgit v1.2.3 From 5266be37c198b7b5ba3df9c1fa530bb0b11aa7d9 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 1 Dec 2014 15:43:35 +0100 Subject: erts: Fix finite warning for clang clang aka llvm claims to be __GNUC__ and thus we have to explicitly check that it is not used. --- erts/emulator/sys/unix/erl_unix_sys.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index f7a6298d5b..26ed2fb558 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -229,7 +229,7 @@ extern void sys_stop_cat(void); #ifdef USE_ISINF_ISNAN /* simulate finite() */ # define isfinite(f) (!isinf(f) && !isnan(f)) # define HAVE_ISFINITE -#elif defined(__GNUC__) && defined(HAVE_FINITE) +#elif (defined(__GNUC__) && !defined(__llvm__)) && defined(HAVE_FINITE) /* We use finite in gcc as it emits assembler instead of the function call that isfinite emits. The assembler is significantly faster. */ -- cgit v1.2.3 From b29e4293de1b029c0b5327dc2a42c3414d13d059 Mon Sep 17 00:00:00 2001 From: David Haguenauer Date: Mon, 1 Dec 2014 14:41:48 -0500 Subject: Replaced "Endianess" with "Endianness" everywhere --- erts/etc/unix/etp-commands.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 0190ea613e..141d51824f 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -2434,7 +2434,7 @@ define etp-system-info printf "ERTS version: %s\n", etp_erts_version printf "Compile date: %s\n", etp_compile_date printf "Arch: %s\n", etp_arch - printf "Endianess: " + printf "Endianness: " if (etp_big_endian) printf "Big\n" else -- cgit v1.2.3 From b3e52c026ce4920e1a4e36ef98e5de94666e91ef Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 2 Dec 2014 17:37:13 +0100 Subject: erts: Add compile time assert ERTS_CT_ASSERT and usage --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/erl_alloc.c | 2 +- erts/emulator/beam/erl_alloc_util.c | 6 +++--- erts/emulator/beam/erl_gc.c | 18 +++++++++--------- erts/emulator/beam/erl_instrument.c | 2 +- erts/emulator/beam/erl_mtrace.c | 2 +- erts/emulator/beam/erl_printf_term.c | 2 +- erts/emulator/beam/erl_process.c | 3 +-- erts/emulator/beam/erl_trace.c | 2 +- erts/emulator/beam/io.c | 4 ++-- erts/emulator/beam/sys.h | 16 ++++++++++++++++ erts/emulator/drivers/common/inet_drv.c | 6 +++--- 12 files changed, 40 insertions(+), 25 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index e9f5fd798b..3af7c43abf 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5142,7 +5142,7 @@ get_map_elements_fail: #ifndef NO_JUMP_TABLE #ifdef ERTS_OPCODE_COUNTER_SUPPORT /* Are tables correctly generated by beam_makeops? */ - ASSERT(sizeof(counting_opcodes) == sizeof(opcodes)); + ERTS_CT_ASSERT(sizeof(counting_opcodes) == sizeof(opcodes)); #ifdef DEBUG counting_opcodes[op_catch_end_y] = LabelAddr(lb_catch_end_y); #endif diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 90cd227fae..f2bceff4eb 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3939,7 +3939,7 @@ static Uint install_debug_functions(void) { int i; - ASSERT(sizeof(erts_allctrs) == sizeof(real_allctrs)); + ERTS_CT_ASSERT(sizeof(erts_allctrs) == sizeof(real_allctrs)); sys_memcpy((void *)real_allctrs,(void *)erts_allctrs,sizeof(erts_allctrs)); diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index e3172dc4fb..2f277690e4 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -1442,7 +1442,7 @@ get_pref_allctr(void *extra) pref_ix = ERTS_ALC_GET_THR_IX(); - ASSERT(sizeof(UWord) == sizeof(Allctr_t *)); + ERTS_CT_ASSERT(sizeof(UWord) == sizeof(Allctr_t *)); ASSERT(0 <= pref_ix && pref_ix < tspec->size); return tspec->allctr[pref_ix]; @@ -1861,7 +1861,7 @@ handle_delayed_dealloc(Allctr_t *allctr, * if this carrier is pulled from dc_list by cpool_fetch() */ ERTS_ALC_CPOOL_ASSERT(FBLK_TO_MBC(blk) != crr); - ERTS_ALC_CPOOL_ASSERT(sizeof(ErtsAllctrDDBlock_t) == sizeof(void*)); + ERTS_CT_ASSERT(sizeof(ErtsAllctrDDBlock_t) == sizeof(void*)); #ifdef MBC_ABLK_OFFSET_BITS blk->u.carrier = crr; #else @@ -5942,7 +5942,7 @@ erts_alcu_init(AlcUInit_t *init) erts_atomic_init_nob(&sentinel->prev, (erts_aint_t) sentinel); } #endif - ASSERT(SBC_BLK_SZ_MASK == MBC_FBLK_SZ_MASK); /* see BLK_SZ */ + ERTS_CT_ASSERT(SBC_BLK_SZ_MASK == MBC_FBLK_SZ_MASK); /* see BLK_SZ */ #if HAVE_ERTS_MSEG ASSERT(erts_mseg_unit_size() == ERTS_SACRR_UNIT_SZ); max_mseg_carriers = init->mmc; diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 5f78a7b532..d1a7ee113b 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -175,15 +175,15 @@ erts_init_gc(void) int i = 0, ix; Sint max_heap_size = 0; - ASSERT(offsetof(ProcBin,thing_word) == offsetof(struct erl_off_heap_header,thing_word)); - ASSERT(offsetof(ProcBin,thing_word) == offsetof(ErlFunThing,thing_word)); - ASSERT(offsetof(ProcBin,thing_word) == offsetof(ExternalThing,header)); - ASSERT(offsetof(ProcBin,size) == offsetof(struct erl_off_heap_header,size)); - ASSERT(offsetof(ProcBin,size) == offsetof(ErlSubBin,size)); - ASSERT(offsetof(ProcBin,size) == offsetof(ErlHeapBin,size)); - ASSERT(offsetof(ProcBin,next) == offsetof(struct erl_off_heap_header,next)); - ASSERT(offsetof(ProcBin,next) == offsetof(ErlFunThing,next)); - ASSERT(offsetof(ProcBin,next) == offsetof(ExternalThing,next)); + ERTS_CT_ASSERT(offsetof(ProcBin,thing_word) == offsetof(struct erl_off_heap_header,thing_word)); + ERTS_CT_ASSERT(offsetof(ProcBin,thing_word) == offsetof(ErlFunThing,thing_word)); + ERTS_CT_ASSERT(offsetof(ProcBin,thing_word) == offsetof(ExternalThing,header)); + ERTS_CT_ASSERT(offsetof(ProcBin,size) == offsetof(struct erl_off_heap_header,size)); + ERTS_CT_ASSERT(offsetof(ProcBin,size) == offsetof(ErlSubBin,size)); + ERTS_CT_ASSERT(offsetof(ProcBin,size) == offsetof(ErlHeapBin,size)); + ERTS_CT_ASSERT(offsetof(ProcBin,next) == offsetof(struct erl_off_heap_header,next)); + ERTS_CT_ASSERT(offsetof(ProcBin,next) == offsetof(ErlFunThing,next)); + ERTS_CT_ASSERT(offsetof(ProcBin,next) == offsetof(ExternalThing,next)); erts_test_long_gc_sleep = 0; diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c index df7c443387..da85b86c87 100644 --- a/erts/emulator/beam/erl_instrument.c +++ b/erts/emulator/beam/erl_instrument.c @@ -1226,7 +1226,7 @@ erts_instr_init(int stat, int map_stat) mem_anchor = NULL; /* Install instrumentation functions */ - ASSERT(sizeof(erts_allctrs) == sizeof(real_allctrs)); + ERTS_CT_ASSERT(sizeof(erts_allctrs) == sizeof(real_allctrs)); sys_memcpy((void *)real_allctrs,(void *)erts_allctrs,sizeof(erts_allctrs)); diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c index c8bb126687..fa1bde1c87 100644 --- a/erts/emulator/beam/erl_mtrace.c +++ b/erts/emulator/beam/erl_mtrace.c @@ -627,7 +627,7 @@ erts_mtrace_install_wrapper_functions(void) if (erts_mtrace_enabled) { int i; /* Install trace functions */ - ASSERT(sizeof(erts_allctrs) == sizeof(real_allctrs)); + ERTS_CT_ASSERT(sizeof(erts_allctrs) == sizeof(real_allctrs)); sys_memcpy((void *) real_allctrs, (void *) erts_allctrs, diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 74e38c13df..c982dc2080 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -594,7 +594,7 @@ erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision, ErlPfEterm* term_base) { int res; - ASSERT(sizeof(ErlPfEterm) == sizeof(Eterm)); + ERTS_CT_ASSERT(sizeof(ErlPfEterm) == sizeof(Eterm)); res = print_term(fn, arg, (Eterm)term, &precision, (Eterm*)term_base); if (res < 0) diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b0e0cf13f8..08c03e7135 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2366,7 +2366,6 @@ erts_active_schedulers(void) ERTS_ATOMIC_FOREACH_RUNQ(rq, as -= abs(rq->waiting)); - ASSERT(as >= 0); return as; } @@ -10484,7 +10483,7 @@ alloc_process(ErtsRunQueue *rq, erts_aint32_t state) init_arg.run_queue = rq; init_arg.state = state; - ASSERT(((char *) p) == ((char *) &p->common)); + ERTS_CT_ASSERT(offsetof(Process,common) == 0); if (!erts_ptab_new_element(&erts_proc, &p->common, diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index ea5c850a30..eba07667ad 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -2225,7 +2225,7 @@ trace_gc(Process *p, Eterm what) Eterm* limit; #endif - ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(Eterm)); + ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(Eterm)); UseTmpHeap(LOCAL_HEAP_SIZE,p); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 4d262ff022..d46ac9b336 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -391,7 +391,7 @@ static Port *create_port(char *name, /* Set default tracing */ erts_get_default_tracing(&ERTS_TRACE_FLAGS(prt), &ERTS_TRACER_PROC(prt)); - ASSERT(((char *) prt) == ((char *) &prt->common)); + ERTS_CT_ASSERT(offsetof(Port,common) == 0); #if !ERTS_PORT_INIT_INSTR_NEED_ID /* @@ -6698,7 +6698,7 @@ static void ref_to_driver_monitor(Eterm ref, ErlDrvMonitor *mon) { RefThing *refp; ASSERT(is_internal_ref(ref)); - ASSERT(sizeof(RefThing) <= sizeof(ErlDrvMonitor)); + ERTS_CT_ASSERT(sizeof(RefThing) <= sizeof(ErlDrvMonitor)); refp = ref_thing_ptr(ref); memset(mon,0,sizeof(ErlDrvMonitor)); memcpy(mon,refp,sizeof(RefThing)); diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index c29d4b3777..7661bf2dc7 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -188,6 +188,22 @@ __decl_noreturn void __noreturn erl_assert_error(const char* expr, const char *f # define ASSERT(e) ((void) 1) #endif +/* + * Compile time assert + * (the actual compiler error msg can be a bit confusing) + */ +#if ERTS_AT_LEAST_GCC_VSN__(3,1,1) +# define ERTS_CT_ASSERT(e) \ + do { \ + enum { compile_time_assert__ = __builtin_choose_expr((e),0,(void)0) }; \ + } while(0) +#else +# define ERTS_CT_ASSERT(e) \ + do { \ + enum { compile_time_assert__ = 1/(e) }; \ + } while (0) +#endif + /* * Microsoft C/C++: We certainly want to use stdarg.h and prototypes. * But MSC doesn't define __STDC__, unless we compile with the -Za diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index db8a251fdd..c8b69b0a94 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -3948,9 +3948,9 @@ static int inet_init() if (0 != erl_drv_tsd_key_create("inet_buffer_stack_key", &buffer_stack_key)) goto error; - ASSERT(sizeof(struct in_addr) == 4); + ERTS_CT_ASSERT(sizeof(struct in_addr) == 4); # if defined(HAVE_IN6) && defined(AF_INET6) - ASSERT(sizeof(struct in6_addr) == 16); + ERTS_CT_ASSERT(sizeof(struct in6_addr) == 16); # endif INIT_ATOM(ok); @@ -3996,7 +3996,7 @@ static int inet_init() #ifdef HAVE_SCTP /* Check the size of SCTP AssocID -- currently both this driver and the Erlang part require 32 bit: */ - ASSERT(sizeof(sctp_assoc_t)==ASSOC_ID_LEN); + ERTS_CT_ASSERT(sizeof(sctp_assoc_t)==ASSOC_ID_LEN); # if defined(HAVE_SCTP_BINDX) p_sctp_bindx = sctp_bindx; # if defined(HAVE_SCTP_PEELOFF) -- cgit v1.2.3 From a8377a44bb81ab86a694c57f2f10527cf51612d4 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Tue, 2 Dec 2014 19:13:16 +0100 Subject: fix eacces spelling --- erts/doc/src/erlang.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 483d81cfb6..cba2c07959 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2840,7 +2840,7 @@ os_prompt% raised. The error reason may differ between operating systems. Typically the error enoent is raised when one tries to run a program that is not found and - eaccess is raised when the given file is not + eacces is raised when the given file is not executable.

{fd, In, Out} -- cgit v1.2.3 From fbc4e9c2bb2db3a3e3efb50c9a8348d3945b88f2 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 3 Dec 2014 16:43:30 +0100 Subject: erts: Fix some spelling in internal docs --- erts/emulator/internal_doc/CarrierMigration.md | 50 +++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'erts') diff --git a/erts/emulator/internal_doc/CarrierMigration.md b/erts/emulator/internal_doc/CarrierMigration.md index 7afdb70aef..2a9594db25 100644 --- a/erts/emulator/internal_doc/CarrierMigration.md +++ b/erts/emulator/internal_doc/CarrierMigration.md @@ -16,12 +16,12 @@ When a carrier is empty, i.e. contains only one large free block, it is deallocated. Since multiblock carriers can contain both allocated blocks and free blocks at the same time, an allocator instance might be stuck with a large amount of poorly utilized carriers if the memory -load decrease. After a peak in memory usage it is expected that not -all memory can be returned since the blocks still allocated is likely +load decreases. After a peak in memory usage it is expected that not +all memory can be returned since the blocks still allocated are likely to be dispersed over multiple carriers. Such poorly utilized carriers -can usually be reused if the memory load increase again. However, +can usually be reused if the memory load increases again. However, since each scheduler thread manages its own set of allocator -instances, and memory load is not necessarily connected to CPU load we +instances, and memory load is not necessarily correlated to CPU load, we might get into a situation where there are lots of poorly utilized multiblock carriers on some allocator instances while we need to allocate new multiblock carriers on other allocator instances. In @@ -50,13 +50,13 @@ the allocator instance manages. Free blocks in one specific carrier can be referred to from potentially every other carrier that is managed, and the amount of such references can be huge. That is, the work of removing the free blocks of such a carrier from the search -tree will be huge. One way of solving this could be to not migrate +tree will be huge. One way of solving this could be not to migrate carriers that contain lots of free blocks, but this would prevent us -from migrating carriers that potentially needs to be migrated in order +from migrating carriers that potentially need to be migrated in order to solve the problem we set out to solve. By using one data structure of free blocks in each carrier and an -allocator instance wide data structure of carriers managed by the +allocator instance-wide data structure of carriers managed by the allocator instance, the work needed in order to remove and add carriers can be kept to a minimum. When migration of carriers is enabled on a specific allocator type, we require that an allocation @@ -76,9 +76,9 @@ through a pool of carriers. In order for a carrier migration to complete, one scheduler needs to move the carrier into the pool, and another scheduler needs to take the carrier out of the pool. -The pool is implemented as a lock free, circular, double linked, +The pool is implemented as a lock-free, circular, double linked, list. The list contains a sentinel which is used as the starting point -when inserting to, or fetching from the pool. Carriers in the pool are +when inserting to, or fetching from, the pool. Carriers in the pool are elements in this list. The list can be modified by all scheduler threads @@ -108,19 +108,19 @@ all search operations need to read the content of the sentinel. If we were to modify the sentinel, the cache line containing the sentinel would unnecessarily be bounced between processors. -The `prev`, and `next` fields in the elements of the list contains the +The `prev` and `next` fields in the elements of the list contain the value of the pointer, a modification marker, and a deleted marker. Memory operations on these fields are done using atomic memory operations. When a thread has set the modification marker in a field, no-one except the thread that set the marker is allowed to modify the -field. If multiple modification markers needs to be set, we always +field. If multiple modification markers need to be set, we always begin with `next` fields followed by `prev` fields in the order following the actual pointers. This guarantees that no deadlocks will occur. When a carrier is being removed from a pool, we mark it with a thread progress value that needs to be reached before we are allowed to -modify the `next`, and `prev` fields. That is, until we reach this +modify the `next` and `prev` fields. That is, until we reach this thread progress we are not allowed to insert the carrier into the pool again, and we are not allowed to deallocate the carrier. This ensures that threads inspecting the pool always will be able to traverse the @@ -130,12 +130,12 @@ threads may have references to it via the pool. ### Migration ### -There exist one pool for each allocator type enabling migration of +There exists one pool for each allocator type enabling migration of carriers between scheduler specific allocator instances of the same allocator type. Each allocator instance keeps track of the current utilization of its -multiblock carriers. When the utilization falls below the "abandon +multiblock carriers. When the total utilization falls below the "abandon carrier utilization limit" it starts to inspect the utilization of the current carrier when deallocations are made. If also the utilization of the carrier falls below the "abandon carrier utilization limit" it @@ -163,7 +163,7 @@ If a carrier in the pool becomes empty, it will be withdrawn from the pool. All carriers that become empty are also always passed to its **owning** allocator instance for deallocation using the delayed dealloc functionality. Since carriers this way always will be -deallocated by the owner, that allocated the carrier, the +deallocated by the owner that allocated the carrier, the underlying functionality of allocating and deallocating carriers can remain simple and doesn't have to bother about multiple threads. In a NUMA system we will also not mix carriers originating from multiple @@ -185,10 +185,10 @@ In short: To harbor real time characteristics, searching the pool is limited. We only inspect a limited number of carriers. If none of those carriers had a free block large enough to satisfy the allocation -request, the search will fail. A carrier in the pool can also be busy, +request, the search will fail. A carrier in the pool can also be busy if another thread is currently doing block deallocation work on the carrier. A busy carrier will also be skipped by the search as it can -not satisfy the request. The pool is lock free and we do not want to +not satisfy the request. The pool is lock-free and we do not want to block, waiting for the other thread to finish. #### Before OTP 17.4 #### @@ -212,9 +212,9 @@ may later be inserted into the pool due to bad utilization. If the frequency of insertions into the pool is higher than successful fetching from the pool, memory will eventually get exhausted. -This "bad" state, consist of a cluster of small and/or highly +This "bad" state consists of a cluster of small and/or highly fragmented carriers located at the sentinel in the pool. The largest free -block in such a "bad" carrier is rather small, making it not able to satisfy +block in such a "bad" carrier is rather small, making it unable to satisfy most allocation requests. As the search always started at the sentinel, any such "bad" carriers that had been left in the pool would eventually cluster together at the sentinel. All searches first @@ -229,7 +229,7 @@ allocator itself and later had been abandoned to the pool. If none of our own abandoned carrier would do, then the search continues into the pool, as before, to look for carriers created by other allocators. However, if we have at least one abandoned carrier of our -own, that could not satisfy the request, we can use that as entry point +own that could not satisfy the request, we can use that as entry point into the pool. The result is that we prefer carriers created by the thread itself, @@ -237,15 +237,15 @@ which is good for NUMA performance. And we get more entry points when searching the pool, which will ease contention and clustering. To do the first search among own carriers, every allocator instance -has two new lists; `pooled_list` and `traitor_list`. These lists are only -accessed by the allocator itself and they only contain the allocators +has two new lists: `pooled_list` and `traitor_list`. These lists are only +accessed by the allocator itself and they only contain the allocator's own carriers. When an owned carrier is abandoned and put in the pool, it is also linked into `pooled_list`. When we search our `pooled_list` and find a carrier that is no longer in the pool, we move that carrier from `pooled_list` to `traitor_list` as it is now employed by another allocator. If searching `pooled_list` fails, we also do a limited search of `traitor_list`. When finding an abandoned -carrier in `traitor_list` it is either employed, or moved back to +carrier in `traitor_list` it is either employed or moved back to `pooled_list` if it could not satisfy the allocation request. When searching `pooled_list` and `traitor_list` we always start at the @@ -256,14 +256,14 @@ allocator instance, they need no thread synchronization at all. Furthermore, the search for own carriers that are scheduled for deallocation is now done as the last search option. The idea is -that it is better to reuse a poorly utilized carrier, than to +that it is better to reuse a poorly utilized carrier than to resurrect an empty carrier that was just about to be released back to the OS. ### Result ### The use of this strategy of abandoning carriers with poor utilization -and reusing these in allocator instances with an increased carrier +and reusing them in allocator instances with an increased carrier demand is extremely effective and completely eliminates the problems that otherwise sometimes occurred when CPU load dropped while memory load did not. -- cgit v1.2.3 From e7b0010ef2a190f1be2c775e6a236cbd7f0f577d Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 3 Dec 2014 23:51:53 +0100 Subject: Fix function "defined but not used" warning --- erts/emulator/sys/common/erl_check_io.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 81cb5dc4bb..0051b45b31 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -268,6 +268,8 @@ free_drv_select_data(ErtsDrvSelectDataState *dsp) erts_free(ERTS_ALC_T_DRV_SEL_D_STATE, dsp); } +#if ERTS_CIO_HAVE_DRV_EVENT + static ERTS_INLINE ErtsDrvEventDataState * alloc_drv_event_data(void) { @@ -290,6 +292,8 @@ free_drv_event_data(ErtsDrvEventDataState *dep) erts_free(ERTS_ALC_T_DRV_EV_D_STATE, dep); } +#endif /* ERTS_CIO_HAVE_DRV_EVENT */ + static ERTS_INLINE void remember_removed(ErtsDrvEventState *state, struct pollset_info* psi) { -- cgit v1.2.3 From aa6c60768825915825b3730bb836afa9253ea0a0 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 4 Dec 2014 10:57:06 +0100 Subject: erts: Start compilation of beam_emu earlier --- erts/emulator/Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 53fc7bd713..e4cd566285 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -934,7 +934,7 @@ ifdef HIPE_ENABLED EXTRA_BASE_OBJS += $(HIPE_OBJS) endif -BASE_OBJS = $(RUN_OBJS) $(EMU_OBJS) $(OS_OBJS) $(EXTRA_BASE_OBJS) +BASE_OBJS = $(EMU_OBJS) $(RUN_OBJS) $(OS_OBJS) $(EXTRA_BASE_OBJS) before_DTrace_OBJS = $(BASE_OBJS) $(DRV_OBJS) -- cgit v1.2.3 From b9a8cebcc30ee1bce8e6b57041bf07cf84630386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 2 Dec 2014 12:31:44 +0100 Subject: erts: Use linear search for small select_val arrays For searching a key in an array we use linear search in arrays up to 10 elements. Selecting on tuple arity will always use linear search. Instead of using two different instructions we assume selecting on different tuple arities are always few in numbers. --- erts/emulator/beam/beam_debug.c | 42 +++++++-- erts/emulator/beam/beam_emu.c | 55 +++++++++--- erts/emulator/beam/beam_load.c | 184 ++++++++++++++++++++++++++++++---------- erts/emulator/beam/ops.tab | 22 +++-- 4 files changed, 226 insertions(+), 77 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index a3cd08834f..6bb987985d 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -579,9 +579,29 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) unpacked = ap; ap = addr + size; switch (op) { - case op_i_select_val_rfI: - case op_i_select_val_xfI: - case op_i_select_val_yfI: + case op_i_select_val_lins_rfI: + case op_i_select_val_lins_xfI: + case op_i_select_val_lins_yfI: + { + int n = ap[-1]; + int ix = n; + + while (ix--) { + erts_print(to, to_arg, "%T ", (Eterm) ap[0]); + ap++; + size++; + } + ix = n; + while (ix--) { + erts_print(to, to_arg, "f(" HEXF ") ", (Eterm) ap[0]); + ap++; + size++; + } + } + break; + case op_i_select_val_bins_rfI: + case op_i_select_val_bins_xfI: + case op_i_select_val_bins_yfI: { int n = ap[-1]; @@ -598,13 +618,19 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) case op_i_select_tuple_arity_yfI: { int n = ap[-1]; + int ix = n; - while (n > 0) { + while (ix--) { Uint arity = arityval(ap[0]); - erts_print(to, to_arg, " {%d} f(" HEXF ")", arity, ap[1]); - ap += 2; - size += 2; - n--; + erts_print(to, to_arg, "{%d} ", arity, ap[1]); + ap++; + size++; + } + ix = n; + while (ix--) { + erts_print(to, to_arg, "f(" HEXF ") ", ap[0]); + ap++; + size++; } } break; diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index e9f5fd798b..292971a387 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2138,19 +2138,18 @@ void process_main(void) NextPF(0, next); } - { Eterm select_val2; - OpCase(i_select_tuple_arity2_yfAfAf): + OpCase(i_select_tuple_arity2_yfAAff): select_val2 = yb(Arg(0)); goto do_select_tuple_arity2; - OpCase(i_select_tuple_arity2_xfAfAf): + OpCase(i_select_tuple_arity2_xfAAff): select_val2 = xb(Arg(0)); goto do_select_tuple_arity2; - OpCase(i_select_tuple_arity2_rfAfAf): + OpCase(i_select_tuple_arity2_rfAAff): select_val2 = r(0); I--; @@ -2161,22 +2160,22 @@ void process_main(void) select_val2 = *tuple_val(select_val2); goto do_select_val2; - OpCase(i_select_val2_yfcfcf): + OpCase(i_select_val2_yfccff): select_val2 = yb(Arg(0)); goto do_select_val2; - OpCase(i_select_val2_xfcfcf): + OpCase(i_select_val2_xfccff): select_val2 = xb(Arg(0)); goto do_select_val2; - OpCase(i_select_val2_rfcfcf): + OpCase(i_select_val2_rfccff): select_val2 = r(0); I--; do_select_val2: if (select_val2 == Arg(2)) { - I += 2; - } else if (select_val2 == Arg(4)) { + I += 3; + } else if (select_val2 == Arg(3)) { I += 4; } @@ -2203,20 +2202,50 @@ void process_main(void) do_select_tuple_arity: if (is_tuple(select_val)) { select_val = *tuple_val(select_val); - goto do_binary_search; + goto do_linear_search; + } + SET_I((BeamInstr *) Arg(1)); + Goto(*I); + + OpCase(i_select_val_lins_xfI): + select_val = xb(Arg(0)); + goto do_linear_search; + + OpCase(i_select_val_lins_yfI): + select_val = yb(Arg(0)); + goto do_linear_search; + + OpCase(i_select_val_lins_rfI): + select_val = r(0); + I--; + + do_linear_search: { + BeamInstr *vs = &Arg(3); + int ix = 0; + + for(;;) { + if (vs[ix+0] >= select_val) { ix += 0; break; } + if (vs[ix+1] >= select_val) { ix += 1; break; } + ix += 2; + } + + if (vs[ix] == select_val) { + I += ix + Arg(2) + 2; } + SET_I((BeamInstr *) Arg(1)); Goto(*I); + } - OpCase(i_select_val_xfI): + OpCase(i_select_val_bins_xfI): select_val = xb(Arg(0)); goto do_binary_search; - OpCase(i_select_val_yfI): + OpCase(i_select_val_bins_yfI): select_val = yb(Arg(0)); goto do_binary_search; - OpCase(i_select_val_rfI): + OpCase(i_select_val_bins_rfI): select_val = r(0); I--; diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 07654f6d5c..b9a6536ac6 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -3321,9 +3321,10 @@ gen_select_tuple_arity(LoaderState* stp, GenOpArg S, GenOpArg Fail, { GenOp* op; + GenOpArg *tmp; int arity = Size.val + 3; int size = Size.val / 2; - int i; + int i, j, align = 0; /* * Verify the validity of the list. @@ -3337,10 +3338,38 @@ gen_select_tuple_arity(LoaderState* stp, GenOpArg S, GenOpArg Fail, } } + /* + * Use a special-cased instruction if there are only two values. + */ + if (size == 2) { + NEW_GENOP(stp, op); + op->next = NULL; + op->op = genop_i_select_tuple_arity2_6; + GENOP_ARITY(op, arity - 1); + op->a[0] = S; + op->a[1] = Fail; + op->a[2].type = TAG_u; + op->a[2].val = Rest[0].val; + op->a[3].type = TAG_u; + op->a[3].val = Rest[2].val; + op->a[4] = Rest[1]; + op->a[5] = Rest[3]; + + return op; + } + /* * Generate the generic instruction. + * Assumption: + * Few different tuple arities to select on (fewer than 20). + * Use linear scan approach. */ + align = 1; + + arity += 2*align; + size += align; + NEW_GENOP(stp, op); op->next = NULL; op->op = genop_i_select_tuple_arity_3; @@ -3348,39 +3377,36 @@ gen_select_tuple_arity(LoaderState* stp, GenOpArg S, GenOpArg Fail, op->a[0] = S; op->a[1] = Fail; op->a[2].type = TAG_u; - op->a[2].val = Size.val / 2; - for (i = 0; i < Size.val; i += 2) { - op->a[i+3].type = TAG_v; - op->a[i+3].val = make_arityval(Rest[i].val); - op->a[i+4] = Rest[i+1]; - } + op->a[2].val = size; - /* - * Sort the values to make them useful for a binary search. - */ + tmp = (GenOpArg *) erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(GenOpArg)*(arity-2*align)); - qsort(op->a+3, size, 2*sizeof(GenOpArg), - (int (*)(const void *, const void *)) genopargcompare); -#ifdef DEBUG - for (i = 3; i < arity-2; i += 2) { - ASSERT(op->a[i].val < op->a[i+2].val); + for (i = 3; i < arity - 2*align; i+=2) { + tmp[i-3].type = TAG_v; + tmp[i-3].val = make_arityval(Rest[i-3].val); + tmp[i-2] = Rest[i-2]; } -#endif /* - * Use a special-cased instruction if there are only two values. + * Sort the values to make them useful for a sentinel search */ - if (size == 2) { - op->op = genop_i_select_tuple_arity2_6; - op->arity--; - op->a[2].type = TAG_u; - op->a[2].val = arityval(op->a[3].val); - op->a[3] = op->a[4]; - op->a[4].type = TAG_u; - op->a[4].val = arityval(op->a[5].val); - op->a[5] = op->a[6]; + + qsort(tmp, size - align, 2*sizeof(GenOpArg), + (int (*)(const void *, const void *)) genopargcompare); + + j = 3; + for (i = 3; i < arity - 2*align; i += 2) { + op->a[j] = tmp[i-3]; + op->a[j + size] = tmp[i-2]; + j++; } + erts_free(ERTS_ALC_T_LOADER_TMP, (void *) tmp); + + op->a[j].type = TAG_u; + op->a[j].val = ~((BeamInstr)0); + op->a[j+size] = Fail; + return op; } @@ -3602,45 +3628,109 @@ gen_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail, GenOpArg Size, GenOpArg* Rest) { GenOp* op; + GenOpArg *tmp; int arity = Size.val + 3; int size = Size.val / 2; - int i; + int i, j, align = 0; + + if (size == 2) { + + /* + * Use a special-cased instruction if there are only two values. + */ + + NEW_GENOP(stp, op); + op->next = NULL; + op->op = genop_i_select_val2_6; + GENOP_ARITY(op, arity - 1); + op->a[0] = S; + op->a[1] = Fail; + op->a[2] = Rest[0]; + op->a[3] = Rest[2]; + op->a[4] = Rest[1]; + op->a[5] = Rest[3]; + + return op; + + } else if (size > 10) { + + /* binary search instruction */ + + NEW_GENOP(stp, op); + op->next = NULL; + op->op = genop_i_select_val_bins_3; + GENOP_ARITY(op, arity); + op->a[0] = S; + op->a[1] = Fail; + op->a[2].type = TAG_u; + op->a[2].val = size; + for (i = 3; i < arity; i++) { + op->a[i] = Rest[i-3]; + } + + /* + * Sort the values to make them useful for a binary search. + */ + + qsort(op->a+3, size, 2*sizeof(GenOpArg), + (int (*)(const void *, const void *)) genopargcompare); +#ifdef DEBUG + for (i = 3; i < arity-2; i += 2) { + ASSERT(op->a[i].val < op->a[i+2].val); + } +#endif + return op; + } + + /* linear search instruction */ + + align = 1; + + arity += 2*align; + size += align; NEW_GENOP(stp, op); op->next = NULL; - op->op = genop_i_select_val_3; + op->op = genop_i_select_val_lins_3; GENOP_ARITY(op, arity); op->a[0] = S; op->a[1] = Fail; op->a[2].type = TAG_u; op->a[2].val = size; - for (i = 3; i < arity; i++) { - op->a[i] = Rest[i-3]; + + tmp = (GenOpArg *) erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(GenOpArg)*(arity-2*align)); + + for (i = 3; i < arity - 2*align; i++) { + tmp[i-3] = Rest[i-3]; } /* - * Sort the values to make them useful for a binary search. + * Sort the values to make them useful for a sentinel search */ - qsort(op->a+3, size, 2*sizeof(GenOpArg), - (int (*)(const void *, const void *)) genopargcompare); -#ifdef DEBUG - for (i = 3; i < arity-2; i += 2) { - ASSERT(op->a[i].val < op->a[i+2].val); + qsort(tmp, size - align, 2*sizeof(GenOpArg), + (int (*)(const void *, const void *)) genopargcompare); + + j = 3; + for (i = 3; i < arity - 2*align; i += 2) { + op->a[j] = tmp[i-3]; + op->a[j+size] = tmp[i-2]; + j++; } -#endif - /* - * Use a special-cased instruction if there are only two values. - */ - if (size == 2) { - op->op = genop_i_select_val2_6; - op->arity--; - op->a[2] = op->a[3]; - op->a[3] = op->a[4]; - op->a[4] = op->a[5]; - op->a[5] = op->a[6]; + erts_free(ERTS_ALC_T_LOADER_TMP, (void *) tmp); + + /* add sentinel */ + + op->a[j].type = TAG_u; + op->a[j].val = ~((BeamInstr)0); + op->a[j+size] = Fail; + +#ifdef DEBUG + for (i = 0; i < size; i++) { + ASSERT(op->a[i+3].val <= op->a[i+4].val); } +#endif return op; } diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 68fcc177ae..d3649080dc 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -166,22 +166,26 @@ is_tuple Fail=f S | select_tuple_arity S=d Fail=f Size=u Rest=* => \ select_tuple_arity S=d Fail=f Size=u Rest=* => \ gen_select_tuple_arity(S, Fail, Size, Rest) -i_select_val r f I -i_select_val x f I -i_select_val y f I +i_select_val_bins r f I +i_select_val_bins x f I +i_select_val_bins y f I -i_select_val2 r f c f c f -i_select_val2 x f c f c f -i_select_val2 y f c f c f +i_select_val_lins r f I +i_select_val_lins x f I +i_select_val_lins y f I -i_select_tuple_arity2 r f A f A f -i_select_tuple_arity2 x f A f A f -i_select_tuple_arity2 y f A f A f +i_select_val2 r f c c f f +i_select_val2 x f c c f f +i_select_val2 y f c c f f i_select_tuple_arity r f I i_select_tuple_arity x f I i_select_tuple_arity y f I +i_select_tuple_arity2 r f A A f f +i_select_tuple_arity2 x f A A f f +i_select_tuple_arity2 y f A A f f + i_jump_on_val_zero r f I i_jump_on_val_zero x f I i_jump_on_val_zero y f I -- cgit v1.2.3 From ed4f65e9e52bc0792b4a4e6a45acb99cf0e695b2 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 8 Dec 2014 11:49:25 +0100 Subject: Add .appup file --- erts/test/upgrade_SUITE.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index d5a920e03d..217056971e 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -37,8 +37,9 @@ %% - hipe does not support any upgrade at all %% - dialyzer requires hipe (in the .app file) %% - typer requires hipe (in the .app file) +%% - erl_interface, jinterface support no upgrade -define(appup_exclude, - [dialyzer,hipe,typer]). + [dialyzer,hipe,typer,erl_interface,jinterface]). init_per_suite(Config) -> %% Check that a real release is running, not e.g. cerl -- cgit v1.2.3 From 6d49fd3d4d66237924912cc6e6ab3d2512fce728 Mon Sep 17 00:00:00 2001 From: Simon Cornish Date: Tue, 2 Dec 2014 13:01:54 -0800 Subject: Fix inet:getopts involving #sctp_paddrinfo{} Handle peer addresses that are unconfirmed (i.e. in state SCTP_UNCONFIRMED). Handle unknown states instead of using ASSERT --- erts/configure.in | 2 +- erts/emulator/drivers/common/inet_drv.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 1676d3d216..b3fe48d62c 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1784,7 +1784,7 @@ if test x"$ac_cv_header_netinet_sctp_h" = x"yes"; then AC_CHECK_DECLS([SCTP_UNORDERED, SCTP_ADDR_OVER, SCTP_ABORT, SCTP_EOF, SCTP_SENDALL, SCTP_ADDR_CONFIRMED, SCTP_DELAYED_ACK_TIME, - SCTP_EMPTY, + SCTP_EMPTY, SCTP_UNCONFIRMED, SCTP_CLOSED, SCTPS_IDLE, SCTP_BOUND, SCTPS_BOUND, SCTP_LISTEN, SCTPS_LISTEN, diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index db8a251fdd..3fe5dac813 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -2890,6 +2890,9 @@ static ErlDrvTermData am_sctp_rtoinfo, /* Option names */ /* For #sctp_paddrinfo{}: */ am_active, am_inactive, +# if HAVE_DECL_SCTP_UNCONFIRMED + am_unconfirmed, +# endif /* For #sctp_status{}: */ # if HAVE_DECL_SCTP_EMPTY @@ -3919,7 +3922,10 @@ static void inet_init_sctp(void) { /* For #sctp_paddrinfo{}: */ INIT_ATOM(active); INIT_ATOM(inactive); - +# if HAVE_DECL_SCTP_UNCONFIRMED + INIT_ATOM(unconfirmed); +# endif + /* For #sctp_status{}: */ # if HAVE_DECL_SCTP_EMPTY INIT_ATOM(empty); @@ -7338,8 +7344,13 @@ static int load_paddrinfo (ErlDrvTermData * spec, int i, case SCTP_INACTIVE: i = LOAD_ATOM (spec, i, am_inactive); break; +# if HAVE_DECL_SCTP_UNCONFIRMED + case SCTP_UNCONFIRMED: + i = LOAD_ATOM (spec, i, am_unconfirmed); + break; +# endif default: - ASSERT(0); /* NB: SCTP_UNCONFIRMED modifier not yet supported */ + i = LOAD_ATOM (spec, i, am_undefined); } i = LOAD_INT (spec, i, pai->spinfo_cwnd); i = LOAD_INT (spec, i, pai->spinfo_srtt); -- cgit v1.2.3 From 6e3057ddddb77835664f5264a7da62452dc3d9c1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 11 Sep 2014 18:26:00 +0200 Subject: erts: Allow cpu_timestamp tracing for Linux --- erts/aclocal.m4 | 38 ++++++++++++++--------------------- erts/doc/src/erlang.xml | 4 +++- erts/emulator/beam/erl_bif_trace.c | 4 ++-- erts/emulator/beam/erl_time.h | 2 +- erts/emulator/sys/unix/erl_unix_sys.h | 4 ++-- 5 files changed, 23 insertions(+), 29 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index d78025b0be..63f5a9c840 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1881,38 +1881,30 @@ case $erl_gethrvtime in exit(0); return 0; } ], - erl_clock_gettime=yes, - erl_clock_gettime=no, + erl_clock_gettime_cpu_time=yes, + erl_clock_gettime_cpu_time=no, [ case X$erl_xcomp_clock_gettime_cpu_time in - X) erl_clock_gettime=cross;; - Xyes|Xno) erl_clock_gettime=$erl_xcomp_clock_gettime_cpu_time;; + X) erl_clock_gettime_cpu_time=cross;; + Xyes|Xno) erl_clock_gettime_cpu_time=$erl_xcomp_clock_gettime_cpu_time;; *) AC_MSG_ERROR([Bad erl_xcomp_clock_gettime_cpu_time value: $erl_xcomp_clock_gettime_cpu_time]);; esac ]) LIBS=$save_libs - case $host_os in - linux*) - AC_MSG_RESULT([no; not stable]) + AC_MSG_RESULT($erl_clock_gettime_cpu_time) + case $erl_clock_gettime_cpu_time in + yes) + AC_DEFINE(HAVE_CLOCK_GETTIME_CPU_TIME,[], + [define if clock_gettime() works for getting process time]) + LIBRT=-lrt + ;; + cross) + erl_clock_gettime_cpu_time=no + AC_MSG_WARN([result no guessed because of cross compilation]) LIBRT=$xrtlib ;; *) - AC_MSG_RESULT($erl_clock_gettime) - case $erl_clock_gettime in - yes) - AC_DEFINE(HAVE_CLOCK_GETTIME,[], - [define if clock_gettime() works for getting process time]) - LIBRT=-lrt - ;; - cross) - erl_clock_gettime=no - AC_MSG_WARN([result no guessed because of cross compilation]) - LIBRT=$xrtlib - ;; - *) - LIBRT=$xrtlib - ;; - esac + LIBRT=$xrtlib ;; esac AC_SUBST(LIBRT) diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index f9e8717847..37431cbf1b 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6829,7 +6829,9 @@ ok only allowed with PidSpec==all. If the host machine operating system does not support high resolution CPU time measurements, trace/3 exits with - badarg.

+ badarg. Note that most operating systems do + not synchronize this value across cores, so be prepared + that time might seem to go backwards when using this option.

arity diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 06fbbea123..f5e582b1c5 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -651,7 +651,7 @@ Eterm trace_3(BIF_ALIST_3) if (pid_spec == am_all) { if (on) { if (!erts_cpu_timestamp) { -#ifdef HAVE_CLOCK_GETTIME +#ifdef HAVE_CLOCK_GETTIME_CPU_TIME /* Perhaps clock_gettime was found during config on a different machine than this. We check @@ -678,7 +678,7 @@ Eterm trace_3(BIF_ALIST_3) if (erts_start_now_cpu() < 0) { goto error; } -#endif /* HAVE_CLOCK_GETTIME */ +#endif /* HAVE_CLOCK_GETTIME_CPU_TIME */ erts_cpu_timestamp = !0; } } diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 4bbdcaa3e3..7ed1a395ad 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -107,7 +107,7 @@ ERTS_GLB_INLINE void erts_do_time_add(erts_short_time_t elapsed) /* time_sup */ -#if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME)) +#if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME_CPU_TIME)) # ifndef HAVE_ERTS_NOW_CPU # define HAVE_ERTS_NOW_CPU # ifdef HAVE_GETHRVTIME diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 26ed2fb558..b6bca5c0f4 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -188,7 +188,7 @@ typedef hrtime_t SysHrTime; #endif /* GETHRTIME_WITH_CLOCK_GETTIME */ #endif /* HAVE_GETHRTIME */ -#if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME)) +#if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME_CPU_TIME)) typedef long long SysCpuTime; typedef struct timespec SysTimespec; @@ -200,7 +200,7 @@ typedef struct timespec SysTimespec; int sys_start_hrvtime(void); int sys_stop_hrvtime(void); -#elif defined(HAVE_CLOCK_GETTIME) +#elif defined(HAVE_CLOCK_GETTIME_CPU_TIME) #define sys_clock_gettime(cid,tp) clock_gettime((cid),&(tp)) #define sys_get_proc_cputime(t,tp) sys_clock_gettime(CLOCK_PROCESS_CPUTIME_ID,(tp)) -- cgit v1.2.3 From 6d0033735b0b75321fb70d5e2a7645533cc24250 Mon Sep 17 00:00:00 2001 From: Marcus Arendt Date: Tue, 9 Dec 2014 15:09:51 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 56292 -> 56292 bytes erts/preloaded/ebin/erlang.beam | Bin 97972 -> 97956 bytes erts/preloaded/ebin/erts_internal.beam | Bin 4164 -> 4164 bytes erts/preloaded/ebin/init.beam | Bin 48808 -> 48808 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1468 -> 1468 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1340 -> 1340 bytes erts/preloaded/ebin/prim_file.beam | Bin 44904 -> 44904 bytes erts/preloaded/ebin/prim_inet.beam | Bin 73128 -> 73128 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23440 -> 23440 bytes erts/preloaded/ebin/zlib.beam | Bin 13188 -> 13188 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 001c9c76ed..10138cab22 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 1893dd84ac..7a3d94dd16 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index b467633a4d..9b364d8b21 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 915a2d1aef..a190129b2c 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index df1bf932a3..621f036f33 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 95b4bbca2d..6695f4cd15 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 5e4fc5ba84..5e9230826d 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 9d9a4886d9..83230d42ae 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index d98b0275f4..b3abba3223 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 0744bdb21d..7006764d96 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 7f3486a5ddc02a366f2945dfd009c4a2697a2b98 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 9 Dec 2014 15:21:47 +0100 Subject: Prepare release --- erts/doc/src/notes.xml | 252 +++++++++++++++++++++++++++++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 253 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 7bc39fd351..c896ee0cae 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,258 @@

This document describes the changes made to the ERTS application.

+
Erts 6.3 + +
Fixed Bugs and Malfunctions + + +

+ Fix HiPE debug lock checking on OS X 64bit

+

+ Position-independent code is mandatory on OS X. We use + r11 as an intermediate register to fill + BIF_P->hipe.bif_callee. This fixes the following error + when doing `make debug FLAVOR=smp`:

+

+ clang -cc1as: fatal error: error in backend: 32-bit + absolute addressing is not supported in 64-bit mode

+

+ Own Id: OTP-12188

+
+ +

+ Fix race bug that could cause VM crash in + erlang:port_get_data/1 if the port was closed by a + concurrent process. Also fix fatal bug if + port_set_data/2 is called with a non-immediate + data term. Both bugs exist since R16B01.

+

+ Own Id: OTP-12208

+
+ +

+ Correct make variable SSL_DED_LD_RUNTIME_LIBRARY_PATH + when erl_xcomp_sysroot ends with a slash.

+

+ Own Id: OTP-12216 Aux Id: seq12700

+
+ +

+ Fix two cases of unreachable code caused by false use of + assigment operators.

+

+ Own Id: OTP-12222

+
+ +

+ Fix bug when hipe compiled code makes tail call to a BIF + that disables GC while trapping (sush as binary_to_list, + list_to_binary, binary_to_term, term_to_binary).

+

+ Own Id: OTP-12231

+
+ +

+ Fix bug when a migrated empty memory carrier is reused + just before it should be destroyed by the thread that + created it.

+

+ Own Id: OTP-12249

+
+ +

+ Prevents compile-time errors in NIFs, when the compiler + is instructed to treat missing field initializers as + errors, by adding an initializer for the new options + field which was added to ErlNifEntry for 17.3.

+

+ Own Id: OTP-12266

+
+ +

+ Fixed CPU topology detection on FreeBSD systems where + Erlang/OTP is compiled by new C compilers (including, but + possibly not limited to, gcc 4.9 and clang).

+

+ Own Id: OTP-12267

+
+ +

+ Use C99 function isfinite() instead of finite() when + available on non GCC compilers.

+

+ Own Id: OTP-12268

+
+ +

+ Fix bug on windows where an incorrect number of links + could be returned when doing file:read_file_info on a + directory.

+

+ Own Id: OTP-12269

+
+ +

+ Fix rare bug when purging module on VM started with + +Meamin.

+

+ Own Id: OTP-12273

+
+ +

+ Repair run_erl terminal window size adjustment sent from + to_erl. This was broken in OTP 17.0 which could lead to + strange cursor behaviour in the to_erl shell.

+

+ Own Id: OTP-12275 Aux Id: seq12739

+
+ +

+ Fixed bug on windows causing gen_tcp/udp to return an + error when given an fd to work with.

+

+ Own Id: OTP-12289

+
+ +

+ Fix various internal erts issues where negating a signed + integer in C would trigger undefined behavior. This fixes + issues when dividing with bignums and list_to_integer.

+

+ Own Id: OTP-12290

+
+ +

+ When flushing output to stdout on windows, the emulator + could sometimes hang indefinitely waiting for the flush + to complete. This has been fixed.

+

+ Own Id: OTP-12291

+
+ +

+ Fix so that non-smp emulators with dirty scheduler + support shows the correct number of dirty schedulers when + calling erlang:system_info(system_version).

+

+ Own Id: OTP-12295

+
+ +

+ Add nif_version to erlang:system_info/1 in + order to get the NIF API version of the runtime system in + a way similar to driver_version.

+

+ Own Id: OTP-12298

+
+ +

+ Fix bug that could cause the return value from dirty NIF + with zero arity to be treated as garbage, leading to VM + crash.

+

+ Own Id: OTP-12300

+
+ +

+ Improve allocation carrier migration search logic. This + will reduce the risk of failed migrations that could lead + to excess memory consumption. It will also improve smp + performance due to reduced memory contention on the + migration pool.

+

+ Own Id: OTP-12323

+
+
+
+ + +
Improvements and New Features + + +

Introduced support for eager check I/O.

+

By default eager check I/O will be disabled, but this + will most likely be changed in OTP 18. When eager check + I/O is enabled, schedulers will more frequently check for + I/O work. Outstanding I/O operations will however not be + prioritized to the same extent as when eager check I/O is + disabled.

+

Eager check I/O can be enabled using the erl + command line argument: +secio true

+

Characteristics impact when enabled:

+ Lower latency and smoother management of externally + triggered I/O operations. A slightly reduced + priority of externally triggered I/O operations. + +

+ Own Id: OTP-12117

+
+ +

+ Fix erts .app-file

+

+ Own Id: OTP-12189

+
+ +

+ Add configure option --with-ssl-incl=PATH to support + OpenSSL installations with headers and libraries at + different places.

+

+ Own Id: OTP-12215 Aux Id: seq12700

+
+ +

+ Optimization of atomic memory operations with release + barrier semantics on 32-bit PowerPC when using the + implementation included in OTP.

+

+ Own Id: OTP-12250

+
+ +

+ Minor adjustment of scheduler activation code making sure + that an activation of a scheduler is not prevented by its + run-queue being non-empty. (Thanks to Songlu Cai)

+

+ Own Id: OTP-12287

+
+ +

+ Improved support for atomic memory operations provided by + the libatomic_ops + library. Most importantly support for use of native + double word atomics when implemented by + libatomic_ops (for example, implemented for ARM).

+

+ The $ERL_TOP/HOWTO/INSTALL.md + document now also more clearly describes when you want to + build together with a libatomic_ops installation.

+

+ Own Id: OTP-12302

+
+ +

+ Add configure option --with-ssl-rpath to control which + runtime library path to use for dynamic linkage toward + OpenSSL.

+

+ Own Id: OTP-12316 Aux Id: seq12753

+
+ +

+ Added systemd notify support to epmd

+

+ Own Id: OTP-12321

+
+
+
+ +
+
Erts 6.2.1
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index c8c533a221..d0dc8f7243 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.2.1 +VSN = 6.3 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 31b54a211eac951e3a0f2d1f5fe221d8296988ba Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 3 Dec 2014 22:45:35 +0100 Subject: Introduce a 64-bit atomic ops API The 64-bit atomic ops API is implemented by * native word size atomic ops on 64-bit architectures, and * native double word size atomic ops on 32-bit architectures when available. When native double word size atomic is not available, the fallback using modification counters is used. --- erts/emulator/beam/erl_smp.h | 222 +++++++++++++ erts/emulator/beam/erl_threads.h | 679 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 901 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_smp.h b/erts/emulator/beam/erl_smp.h index c38ef47d87..6c40edeb3e 100644 --- a/erts/emulator/beam/erl_smp.h +++ b/erts/emulator/beam/erl_smp.h @@ -60,6 +60,7 @@ typedef erts_tsd_key_t erts_smp_tsd_key_t; #define erts_smp_dw_atomic_t erts_dw_atomic_t #define erts_smp_atomic_t erts_atomic_t #define erts_smp_atomic32_t erts_atomic32_t +#define erts_smp_atomic64_t erts_atomic64_t typedef erts_spinlock_t erts_smp_spinlock_t; typedef erts_rwlock_t erts_smp_rwlock_t; void erts_thr_fatal_error(int, char *); /* implemented in erl_init.c */ @@ -95,6 +96,7 @@ typedef int erts_smp_tsd_key_t; #define erts_smp_dw_atomic_t erts_no_dw_atomic_t #define erts_smp_atomic_t erts_no_atomic_t #define erts_smp_atomic32_t erts_no_atomic32_t +#define erts_smp_atomic64_t erts_no_atomic64_t #if __GNUC__ > 2 typedef struct { } erts_smp_spinlock_t; typedef struct { } erts_smp_rwlock_t; @@ -489,6 +491,116 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig); #define erts_smp_atomic32_set_dirty erts_atomic32_set_dirty #define erts_smp_atomic32_read_dirty erts_atomic32_read_dirty +/* 64-bit atomics */ + +#define erts_smp_atomic64_init_nob erts_atomic64_init_nob +#define erts_smp_atomic64_set_nob erts_atomic64_set_nob +#define erts_smp_atomic64_read_nob erts_atomic64_read_nob +#define erts_smp_atomic64_inc_read_nob erts_atomic64_inc_read_nob +#define erts_smp_atomic64_dec_read_nob erts_atomic64_dec_read_nob +#define erts_smp_atomic64_inc_nob erts_atomic64_inc_nob +#define erts_smp_atomic64_dec_nob erts_atomic64_dec_nob +#define erts_smp_atomic64_add_read_nob erts_atomic64_add_read_nob +#define erts_smp_atomic64_add_nob erts_atomic64_add_nob +#define erts_smp_atomic64_read_bor_nob erts_atomic64_read_bor_nob +#define erts_smp_atomic64_read_band_nob erts_atomic64_read_band_nob +#define erts_smp_atomic64_xchg_nob erts_atomic64_xchg_nob +#define erts_smp_atomic64_cmpxchg_nob erts_atomic64_cmpxchg_nob +#define erts_smp_atomic64_read_bset_nob erts_atomic64_read_bset_nob + +#define erts_smp_atomic64_init_mb erts_atomic64_init_mb +#define erts_smp_atomic64_set_mb erts_atomic64_set_mb +#define erts_smp_atomic64_read_mb erts_atomic64_read_mb +#define erts_smp_atomic64_inc_read_mb erts_atomic64_inc_read_mb +#define erts_smp_atomic64_dec_read_mb erts_atomic64_dec_read_mb +#define erts_smp_atomic64_inc_mb erts_atomic64_inc_mb +#define erts_smp_atomic64_dec_mb erts_atomic64_dec_mb +#define erts_smp_atomic64_add_read_mb erts_atomic64_add_read_mb +#define erts_smp_atomic64_add_mb erts_atomic64_add_mb +#define erts_smp_atomic64_read_bor_mb erts_atomic64_read_bor_mb +#define erts_smp_atomic64_read_band_mb erts_atomic64_read_band_mb +#define erts_smp_atomic64_xchg_mb erts_atomic64_xchg_mb +#define erts_smp_atomic64_cmpxchg_mb erts_atomic64_cmpxchg_mb +#define erts_smp_atomic64_read_bset_mb erts_atomic64_read_bset_mb + +#define erts_smp_atomic64_init_acqb erts_atomic64_init_acqb +#define erts_smp_atomic64_set_acqb erts_atomic64_set_acqb +#define erts_smp_atomic64_read_acqb erts_atomic64_read_acqb +#define erts_smp_atomic64_inc_read_acqb erts_atomic64_inc_read_acqb +#define erts_smp_atomic64_dec_read_acqb erts_atomic64_dec_read_acqb +#define erts_smp_atomic64_inc_acqb erts_atomic64_inc_acqb +#define erts_smp_atomic64_dec_acqb erts_atomic64_dec_acqb +#define erts_smp_atomic64_add_read_acqb erts_atomic64_add_read_acqb +#define erts_smp_atomic64_add_acqb erts_atomic64_add_acqb +#define erts_smp_atomic64_read_bor_acqb erts_atomic64_read_bor_acqb +#define erts_smp_atomic64_read_band_acqb erts_atomic64_read_band_acqb +#define erts_smp_atomic64_xchg_acqb erts_atomic64_xchg_acqb +#define erts_smp_atomic64_cmpxchg_acqb erts_atomic64_cmpxchg_acqb +#define erts_smp_atomic64_read_bset_acqb erts_atomic64_read_bset_acqb + +#define erts_smp_atomic64_init_relb erts_atomic64_init_relb +#define erts_smp_atomic64_set_relb erts_atomic64_set_relb +#define erts_smp_atomic64_read_relb erts_atomic64_read_relb +#define erts_smp_atomic64_inc_read_relb erts_atomic64_inc_read_relb +#define erts_smp_atomic64_dec_read_relb erts_atomic64_dec_read_relb +#define erts_smp_atomic64_inc_relb erts_atomic64_inc_relb +#define erts_smp_atomic64_dec_relb erts_atomic64_dec_relb +#define erts_smp_atomic64_add_read_relb erts_atomic64_add_read_relb +#define erts_smp_atomic64_add_relb erts_atomic64_add_relb +#define erts_smp_atomic64_read_bor_relb erts_atomic64_read_bor_relb +#define erts_smp_atomic64_read_band_relb erts_atomic64_read_band_relb +#define erts_smp_atomic64_xchg_relb erts_atomic64_xchg_relb +#define erts_smp_atomic64_cmpxchg_relb erts_atomic64_cmpxchg_relb +#define erts_smp_atomic64_read_bset_relb erts_atomic64_read_bset_relb + +#define erts_smp_atomic64_init_ddrb erts_atomic64_init_ddrb +#define erts_smp_atomic64_set_ddrb erts_atomic64_set_ddrb +#define erts_smp_atomic64_read_ddrb erts_atomic64_read_ddrb +#define erts_smp_atomic64_inc_read_ddrb erts_atomic64_inc_read_ddrb +#define erts_smp_atomic64_dec_read_ddrb erts_atomic64_dec_read_ddrb +#define erts_smp_atomic64_inc_ddrb erts_atomic64_inc_ddrb +#define erts_smp_atomic64_dec_ddrb erts_atomic64_dec_ddrb +#define erts_smp_atomic64_add_read_ddrb erts_atomic64_add_read_ddrb +#define erts_smp_atomic64_add_ddrb erts_atomic64_add_ddrb +#define erts_smp_atomic64_read_bor_ddrb erts_atomic64_read_bor_ddrb +#define erts_smp_atomic64_read_band_ddrb erts_atomic64_read_band_ddrb +#define erts_smp_atomic64_xchg_ddrb erts_atomic64_xchg_ddrb +#define erts_smp_atomic64_cmpxchg_ddrb erts_atomic64_cmpxchg_ddrb +#define erts_smp_atomic64_read_bset_ddrb erts_atomic64_read_bset_ddrb + +#define erts_smp_atomic64_init_rb erts_atomic64_init_rb +#define erts_smp_atomic64_set_rb erts_atomic64_set_rb +#define erts_smp_atomic64_read_rb erts_atomic64_read_rb +#define erts_smp_atomic64_inc_read_rb erts_atomic64_inc_read_rb +#define erts_smp_atomic64_dec_read_rb erts_atomic64_dec_read_rb +#define erts_smp_atomic64_inc_rb erts_atomic64_inc_rb +#define erts_smp_atomic64_dec_rb erts_atomic64_dec_rb +#define erts_smp_atomic64_add_read_rb erts_atomic64_add_read_rb +#define erts_smp_atomic64_add_rb erts_atomic64_add_rb +#define erts_smp_atomic64_read_bor_rb erts_atomic64_read_bor_rb +#define erts_smp_atomic64_read_band_rb erts_atomic64_read_band_rb +#define erts_smp_atomic64_xchg_rb erts_atomic64_xchg_rb +#define erts_smp_atomic64_cmpxchg_rb erts_atomic64_cmpxchg_rb +#define erts_smp_atomic64_read_bset_rb erts_atomic64_read_bset_rb + +#define erts_smp_atomic64_init_wb erts_atomic64_init_wb +#define erts_smp_atomic64_set_wb erts_atomic64_set_wb +#define erts_smp_atomic64_read_wb erts_atomic64_read_wb +#define erts_smp_atomic64_inc_read_wb erts_atomic64_inc_read_wb +#define erts_smp_atomic64_dec_read_wb erts_atomic64_dec_read_wb +#define erts_smp_atomic64_inc_wb erts_atomic64_inc_wb +#define erts_smp_atomic64_dec_wb erts_atomic64_dec_wb +#define erts_smp_atomic64_add_read_wb erts_atomic64_add_read_wb +#define erts_smp_atomic64_add_wb erts_atomic64_add_wb +#define erts_smp_atomic64_read_bor_wb erts_atomic64_read_bor_wb +#define erts_smp_atomic64_read_band_wb erts_atomic64_read_band_wb +#define erts_smp_atomic64_xchg_wb erts_atomic64_xchg_wb +#define erts_smp_atomic64_cmpxchg_wb erts_atomic64_cmpxchg_wb +#define erts_smp_atomic64_read_bset_wb erts_atomic64_read_bset_wb + +#define erts_smp_atomic64_set_dirty erts_atomic64_set_dirty +#define erts_smp_atomic64_read_dirty erts_atomic64_read_dirty + #else /* !ERTS_SMP */ /* Double word size atomics */ @@ -751,6 +863,116 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig); #define erts_smp_atomic32_set_dirty erts_no_atomic32_set #define erts_smp_atomic32_read_dirty erts_no_atomic32_read +/* 64-bit atomics */ + +#define erts_smp_atomic64_init_nob erts_no_atomic64_set +#define erts_smp_atomic64_set_nob erts_no_atomic64_set +#define erts_smp_atomic64_read_nob erts_no_atomic64_read +#define erts_smp_atomic64_inc_read_nob erts_no_atomic64_inc_read +#define erts_smp_atomic64_dec_read_nob erts_no_atomic64_dec_read +#define erts_smp_atomic64_inc_nob erts_no_atomic64_inc +#define erts_smp_atomic64_dec_nob erts_no_atomic64_dec +#define erts_smp_atomic64_add_read_nob erts_no_atomic64_add_read +#define erts_smp_atomic64_add_nob erts_no_atomic64_add +#define erts_smp_atomic64_read_bor_nob erts_no_atomic64_read_bor +#define erts_smp_atomic64_read_band_nob erts_no_atomic64_read_band +#define erts_smp_atomic64_xchg_nob erts_no_atomic64_xchg +#define erts_smp_atomic64_cmpxchg_nob erts_no_atomic64_cmpxchg +#define erts_smp_atomic64_read_bset_nob erts_no_atomic64_read_bset + +#define erts_smp_atomic64_init_mb erts_no_atomic64_set +#define erts_smp_atomic64_set_mb erts_no_atomic64_set +#define erts_smp_atomic64_read_mb erts_no_atomic64_read +#define erts_smp_atomic64_inc_read_mb erts_no_atomic64_inc_read +#define erts_smp_atomic64_dec_read_mb erts_no_atomic64_dec_read +#define erts_smp_atomic64_inc_mb erts_no_atomic64_inc +#define erts_smp_atomic64_dec_mb erts_no_atomic64_dec +#define erts_smp_atomic64_add_read_mb erts_no_atomic64_add_read +#define erts_smp_atomic64_add_mb erts_no_atomic64_add +#define erts_smp_atomic64_read_bor_mb erts_no_atomic64_read_bor +#define erts_smp_atomic64_read_band_mb erts_no_atomic64_read_band +#define erts_smp_atomic64_xchg_mb erts_no_atomic64_xchg +#define erts_smp_atomic64_cmpxchg_mb erts_no_atomic64_cmpxchg +#define erts_smp_atomic64_read_bset_mb erts_no_atomic64_read_bset + +#define erts_smp_atomic64_init_acqb erts_no_atomic64_set +#define erts_smp_atomic64_set_acqb erts_no_atomic64_set +#define erts_smp_atomic64_read_acqb erts_no_atomic64_read +#define erts_smp_atomic64_inc_read_acqb erts_no_atomic64_inc_read +#define erts_smp_atomic64_dec_read_acqb erts_no_atomic64_dec_read +#define erts_smp_atomic64_inc_acqb erts_no_atomic64_inc +#define erts_smp_atomic64_dec_acqb erts_no_atomic64_dec +#define erts_smp_atomic64_add_read_acqb erts_no_atomic64_add_read +#define erts_smp_atomic64_add_acqb erts_no_atomic64_add +#define erts_smp_atomic64_read_bor_acqb erts_no_atomic64_read_bor +#define erts_smp_atomic64_read_band_acqb erts_no_atomic64_read_band +#define erts_smp_atomic64_xchg_acqb erts_no_atomic64_xchg +#define erts_smp_atomic64_cmpxchg_acqb erts_no_atomic64_cmpxchg +#define erts_smp_atomic64_read_bset_acqb erts_no_atomic64_read_bset + +#define erts_smp_atomic64_init_relb erts_no_atomic64_set +#define erts_smp_atomic64_set_relb erts_no_atomic64_set +#define erts_smp_atomic64_read_relb erts_no_atomic64_read +#define erts_smp_atomic64_inc_read_relb erts_no_atomic64_inc_read +#define erts_smp_atomic64_dec_read_relb erts_no_atomic64_dec_read +#define erts_smp_atomic64_inc_relb erts_no_atomic64_inc +#define erts_smp_atomic64_dec_relb erts_no_atomic64_dec +#define erts_smp_atomic64_add_read_relb erts_no_atomic64_add_read +#define erts_smp_atomic64_add_relb erts_no_atomic64_add +#define erts_smp_atomic64_read_bor_relb erts_no_atomic64_read_bor +#define erts_smp_atomic64_read_band_relb erts_no_atomic64_read_band +#define erts_smp_atomic64_xchg_relb erts_no_atomic64_xchg +#define erts_smp_atomic64_cmpxchg_relb erts_no_atomic64_cmpxchg +#define erts_smp_atomic64_read_bset_relb erts_no_atomic64_read_bset + +#define erts_smp_atomic64_init_ddrb erts_no_atomic64_set +#define erts_smp_atomic64_set_ddrb erts_no_atomic64_set +#define erts_smp_atomic64_read_ddrb erts_no_atomic64_read +#define erts_smp_atomic64_inc_read_ddrb erts_no_atomic64_inc_read +#define erts_smp_atomic64_dec_read_ddrb erts_no_atomic64_dec_read +#define erts_smp_atomic64_inc_ddrb erts_no_atomic64_inc +#define erts_smp_atomic64_dec_ddrb erts_no_atomic64_dec +#define erts_smp_atomic64_add_read_ddrb erts_no_atomic64_add_read +#define erts_smp_atomic64_add_ddrb erts_no_atomic64_add +#define erts_smp_atomic64_read_bor_ddrb erts_no_atomic64_read_bor +#define erts_smp_atomic64_read_band_ddrb erts_no_atomic64_read_band +#define erts_smp_atomic64_xchg_ddrb erts_no_atomic64_xchg +#define erts_smp_atomic64_cmpxchg_ddrb erts_no_atomic64_cmpxchg +#define erts_smp_atomic64_read_bset_ddrb erts_no_atomic64_read_bset + +#define erts_smp_atomic64_init_rb erts_no_atomic64_set +#define erts_smp_atomic64_set_rb erts_no_atomic64_set +#define erts_smp_atomic64_read_rb erts_no_atomic64_read +#define erts_smp_atomic64_inc_read_rb erts_no_atomic64_inc_read +#define erts_smp_atomic64_dec_read_rb erts_no_atomic64_dec_read +#define erts_smp_atomic64_inc_rb erts_no_atomic64_inc +#define erts_smp_atomic64_dec_rb erts_no_atomic64_dec +#define erts_smp_atomic64_add_read_rb erts_no_atomic64_add_read +#define erts_smp_atomic64_add_rb erts_no_atomic64_add +#define erts_smp_atomic64_read_bor_rb erts_no_atomic64_read_bor +#define erts_smp_atomic64_read_band_rb erts_no_atomic64_read_band +#define erts_smp_atomic64_xchg_rb erts_no_atomic64_xchg +#define erts_smp_atomic64_cmpxchg_rb erts_no_atomic64_cmpxchg +#define erts_smp_atomic64_read_bset_rb erts_no_atomic64_read_bset + +#define erts_smp_atomic64_init_wb erts_no_atomic64_set +#define erts_smp_atomic64_set_wb erts_no_atomic64_set +#define erts_smp_atomic64_read_wb erts_no_atomic64_read +#define erts_smp_atomic64_inc_read_wb erts_no_atomic64_inc_read +#define erts_smp_atomic64_dec_read_wb erts_no_atomic64_dec_read +#define erts_smp_atomic64_inc_wb erts_no_atomic64_inc +#define erts_smp_atomic64_dec_wb erts_no_atomic64_dec +#define erts_smp_atomic64_add_read_wb erts_no_atomic64_add_read +#define erts_smp_atomic64_add_wb erts_no_atomic64_add +#define erts_smp_atomic64_read_bor_wb erts_no_atomic64_read_bor +#define erts_smp_atomic64_read_band_wb erts_no_atomic64_read_band +#define erts_smp_atomic64_xchg_wb erts_no_atomic64_xchg +#define erts_smp_atomic64_cmpxchg_wb erts_no_atomic64_cmpxchg +#define erts_smp_atomic64_read_bset_wb erts_no_atomic64_read_bset + +#define erts_smp_atomic64_set_dirty erts_no_atomic64_set +#define erts_smp_atomic64_read_dirty erts_no_atomic64_read + #endif /* !ERTS_SMP */ #if ERTS_GLB_INLINE_INCL_FUNC_DEF diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index 80026104db..7214f3ea33 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -344,6 +344,16 @@ typedef ethr_ts_event erts_tse_t; #define erts_aint32_t ethr_sint32_t #define erts_atomic32_t ethr_atomic32_t +#if defined(ARCH_32) +# define erts_atomic64_t ethr_dw_atomic_t +# define erts_aint64_t ethr_sint64_t +#elif defined(ARCH_64) +# define erts_atomic64_t ethr_atomic_t +# define erts_aint64_t ethr_sint_t +#else +# error "Not supported architecture" +#endif + #define ERTS_DW_AINT_HIGH_WORD ETHR_DW_SINT_HIGH_WORD #define ERTS_DW_AINT_LOW_WORD ETHR_DW_SINT_LOW_WORD @@ -414,10 +424,12 @@ typedef int erts_tse_t; typedef struct { SWord sint[2]; } erts_dw_aint_t; typedef SWord erts_aint_t; typedef Sint32 erts_aint32_t; +typedef Sint64 erts_aint64_t; #define erts_dw_atomic_t erts_dw_aint_t #define erts_atomic_t erts_aint_t #define erts_atomic32_t erts_aint32_t +#define erts_atomic64_t erts_aint64_t #if __GNUC__ > 2 typedef struct { } erts_spinlock_t; @@ -446,6 +458,7 @@ typedef struct { int gcc_is_buggy; } erts_rwlock_t; #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_no_atomic64_t erts_aint64_t #define ERTS_AINT_NULL ((erts_aint_t) NULL) @@ -570,6 +583,29 @@ ERTS_GLB_INLINE erts_aint32_t erts_no_atomic32_cmpxchg(erts_no_atomic32_t *xchgp ERTS_GLB_INLINE erts_aint32_t erts_no_atomic32_read_bset(erts_no_atomic32_t *var, erts_aint32_t mask, erts_aint32_t set); +ERTS_GLB_INLINE void erts_no_atomic64_set(erts_no_atomic64_t *var, + erts_aint64_t i); +ERTS_GLB_INLINE erts_aint64_t erts_no_atomic64_read(erts_no_atomic64_t *var); +ERTS_GLB_INLINE erts_aint64_t erts_no_atomic64_inc_read(erts_no_atomic64_t *incp); +ERTS_GLB_INLINE erts_aint64_t erts_no_atomic64_dec_read(erts_no_atomic64_t *decp); +ERTS_GLB_INLINE void erts_no_atomic64_inc(erts_no_atomic64_t *incp); +ERTS_GLB_INLINE void erts_no_atomic64_dec(erts_no_atomic64_t *decp); +ERTS_GLB_INLINE erts_aint64_t erts_no_atomic64_add_read(erts_no_atomic64_t *addp, + erts_aint64_t i); +ERTS_GLB_INLINE void erts_no_atomic64_add(erts_no_atomic64_t *addp, + erts_aint64_t i); +ERTS_GLB_INLINE erts_aint64_t erts_no_atomic64_read_bor(erts_no_atomic64_t *var, + erts_aint64_t mask); +ERTS_GLB_INLINE erts_aint64_t erts_no_atomic64_read_band(erts_no_atomic64_t *var, + erts_aint64_t mask); +ERTS_GLB_INLINE erts_aint64_t erts_no_atomic64_xchg(erts_no_atomic64_t *xchgp, + erts_aint64_t new); +ERTS_GLB_INLINE erts_aint64_t erts_no_atomic64_cmpxchg(erts_no_atomic64_t *xchgp, + erts_aint64_t new, + erts_aint64_t expected); +ERTS_GLB_INLINE erts_aint64_t erts_no_atomic64_read_bset(erts_no_atomic64_t *var, + erts_aint64_t mask, + erts_aint64_t set); ERTS_GLB_INLINE void erts_spinlock_init_x_opt(erts_spinlock_t *lock, char *name, @@ -1200,6 +1236,441 @@ erts_atomic32_read_dirty(erts_atomic32_t *var) #endif +/* 64-bit atomics */ + +#if defined(ARCH_64) + +#define erts_atomic64_init_nob ethr_atomic_init +#define erts_atomic64_set_nob ethr_atomic_set +#define erts_atomic64_read_nob ethr_atomic_read +#define erts_atomic64_inc_read_nob ethr_atomic_inc_read +#define erts_atomic64_dec_read_nob ethr_atomic_dec_read +#define erts_atomic64_inc_nob ethr_atomic_inc +#define erts_atomic64_dec_nob ethr_atomic_dec +#define erts_atomic64_add_read_nob ethr_atomic_add_read +#define erts_atomic64_add_nob ethr_atomic_add +#define erts_atomic64_read_bor_nob ethr_atomic_read_bor +#define erts_atomic64_read_band_nob ethr_atomic_read_band +#define erts_atomic64_xchg_nob ethr_atomic_xchg +#define erts_atomic64_cmpxchg_nob ethr_atomic_cmpxchg +#define erts_atomic64_read_bset_nob erts_atomic_read_bset_nob + +#define erts_atomic64_init_mb ethr_atomic_init_mb +#define erts_atomic64_set_mb ethr_atomic_set_mb +#define erts_atomic64_read_mb ethr_atomic_read_mb +#define erts_atomic64_inc_read_mb ethr_atomic_inc_read_mb +#define erts_atomic64_dec_read_mb ethr_atomic_dec_read_mb +#define erts_atomic64_inc_mb ethr_atomic_inc_mb +#define erts_atomic64_dec_mb ethr_atomic_dec_mb +#define erts_atomic64_add_read_mb ethr_atomic_add_read_mb +#define erts_atomic64_add_mb ethr_atomic_add_mb +#define erts_atomic64_read_bor_mb ethr_atomic_read_bor_mb +#define erts_atomic64_read_band_mb ethr_atomic_read_band_mb +#define erts_atomic64_xchg_mb ethr_atomic_xchg_mb +#define erts_atomic64_cmpxchg_mb ethr_atomic_cmpxchg_mb +#define erts_atomic64_read_bset_mb erts_atomic_read_bset_mb + +#define erts_atomic64_init_acqb ethr_atomic_init_acqb +#define erts_atomic64_set_acqb ethr_atomic_set_acqb +#define erts_atomic64_read_acqb ethr_atomic_read_acqb +#define erts_atomic64_inc_read_acqb ethr_atomic_inc_read_acqb +#define erts_atomic64_dec_read_acqb ethr_atomic_dec_read_acqb +#define erts_atomic64_inc_acqb ethr_atomic_inc_acqb +#define erts_atomic64_dec_acqb ethr_atomic_dec_acqb +#define erts_atomic64_add_read_acqb ethr_atomic_add_read_acqb +#define erts_atomic64_add_acqb ethr_atomic_add_acqb +#define erts_atomic64_read_bor_acqb ethr_atomic_read_bor_acqb +#define erts_atomic64_read_band_acqb ethr_atomic_read_band_acqb +#define erts_atomic64_xchg_acqb ethr_atomic_xchg_acqb +#define erts_atomic64_cmpxchg_acqb ethr_atomic_cmpxchg_acqb +#define erts_atomic64_read_bset_acqb erts_atomic_read_bset_acqb + +#define erts_atomic64_init_relb ethr_atomic_init_relb +#define erts_atomic64_set_relb ethr_atomic_set_relb +#define erts_atomic64_read_relb ethr_atomic_read_relb +#define erts_atomic64_inc_read_relb ethr_atomic_inc_read_relb +#define erts_atomic64_dec_read_relb ethr_atomic_dec_read_relb +#define erts_atomic64_inc_relb ethr_atomic_inc_relb +#define erts_atomic64_dec_relb ethr_atomic_dec_relb +#define erts_atomic64_add_read_relb ethr_atomic_add_read_relb +#define erts_atomic64_add_relb ethr_atomic_add_relb +#define erts_atomic64_read_bor_relb ethr_atomic_read_bor_relb +#define erts_atomic64_read_band_relb ethr_atomic_read_band_relb +#define erts_atomic64_xchg_relb ethr_atomic_xchg_relb +#define erts_atomic64_cmpxchg_relb ethr_atomic_cmpxchg_relb +#define erts_atomic64_read_bset_relb erts_atomic_read_bset_relb + +#define erts_atomic64_init_ddrb ethr_atomic_init_ddrb +#define erts_atomic64_set_ddrb ethr_atomic_set_ddrb +#define erts_atomic64_read_ddrb ethr_atomic_read_ddrb +#define erts_atomic64_inc_read_ddrb ethr_atomic_inc_read_ddrb +#define erts_atomic64_dec_read_ddrb ethr_atomic_dec_read_ddrb +#define erts_atomic64_inc_ddrb ethr_atomic_inc_ddrb +#define erts_atomic64_dec_ddrb ethr_atomic_dec_ddrb +#define erts_atomic64_add_read_ddrb ethr_atomic_add_read_ddrb +#define erts_atomic64_add_ddrb ethr_atomic_add_ddrb +#define erts_atomic64_read_bor_ddrb ethr_atomic_read_bor_ddrb +#define erts_atomic64_read_band_ddrb ethr_atomic_read_band_ddrb +#define erts_atomic64_xchg_ddrb ethr_atomic_xchg_ddrb +#define erts_atomic64_cmpxchg_ddrb ethr_atomic_cmpxchg_ddrb +#define erts_atomic64_read_bset_ddrb erts_atomic_read_bset_ddrb + +#define erts_atomic64_init_rb ethr_atomic_init_rb +#define erts_atomic64_set_rb ethr_atomic_set_rb +#define erts_atomic64_read_rb ethr_atomic_read_rb +#define erts_atomic64_inc_read_rb ethr_atomic_inc_read_rb +#define erts_atomic64_dec_read_rb ethr_atomic_dec_read_rb +#define erts_atomic64_inc_rb ethr_atomic_inc_rb +#define erts_atomic64_dec_rb ethr_atomic_dec_rb +#define erts_atomic64_add_read_rb ethr_atomic_add_read_rb +#define erts_atomic64_add_rb ethr_atomic_add_rb +#define erts_atomic64_read_bor_rb ethr_atomic_read_bor_rb +#define erts_atomic64_read_band_rb ethr_atomic_read_band_rb +#define erts_atomic64_xchg_rb ethr_atomic_xchg_rb +#define erts_atomic64_cmpxchg_rb ethr_atomic_cmpxchg_rb +#define erts_atomic64_read_bset_rb erts_atomic_read_bset_rb + +#define erts_atomic64_init_wb ethr_atomic_init_wb +#define erts_atomic64_set_wb ethr_atomic_set_wb +#define erts_atomic64_read_wb ethr_atomic_read_wb +#define erts_atomic64_inc_read_wb ethr_atomic_inc_read_wb +#define erts_atomic64_dec_read_wb ethr_atomic_dec_read_wb +#define erts_atomic64_inc_wb ethr_atomic_inc_wb +#define erts_atomic64_dec_wb ethr_atomic_dec_wb +#define erts_atomic64_add_read_wb ethr_atomic_add_read_wb +#define erts_atomic64_add_wb ethr_atomic_add_wb +#define erts_atomic64_read_bor_wb ethr_atomic_read_bor_wb +#define erts_atomic64_read_band_wb ethr_atomic_read_band_wb +#define erts_atomic64_xchg_wb ethr_atomic_xchg_wb +#define erts_atomic64_cmpxchg_wb ethr_atomic_cmpxchg_wb +#define erts_atomic64_read_bset_wb erts_atomic_read_bset_wb + +#define erts_atomic64_set_dirty erts_atomic_set_dirty +#define erts_atomic64_read_dirty erts_atomic_read_dirty + +#elif defined(ARCH_32) + +#undef ERTS_ATOMIC64_OPS_DECL__ + +#define ERTS_ATOMIC64_OPS_DECL__(BARRIER) \ +ERTS_GLB_INLINE void \ +erts_atomic64_init_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val); \ +ERTS_GLB_INLINE void \ +erts_atomic64_set_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val); \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_read_ ## BARRIER(erts_atomic64_t *var); \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_inc_read_ ## BARRIER(erts_atomic64_t *var); \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_dec_read_ ## BARRIER(erts_atomic64_t *var); \ +ERTS_GLB_INLINE void \ +erts_atomic64_inc_ ## BARRIER(erts_atomic64_t *var); \ +ERTS_GLB_INLINE void \ +erts_atomic64_dec_ ## BARRIER(erts_atomic64_t *var); \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_add_read_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val); \ +ERTS_GLB_INLINE void \ +erts_atomic64_add_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val); \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_read_bor_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val); \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_read_band_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val); \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_xchg_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val); \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_cmpxchg_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t new, \ + erts_aint64_t exp); \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_read_bset_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t mask, \ + erts_aint64_t set) + +ERTS_ATOMIC64_OPS_DECL__(nob); +ERTS_ATOMIC64_OPS_DECL__(mb); +ERTS_ATOMIC64_OPS_DECL__(acqb); +ERTS_ATOMIC64_OPS_DECL__(relb); +ERTS_ATOMIC64_OPS_DECL__(ddrb); +ERTS_ATOMIC64_OPS_DECL__(rb); +ERTS_ATOMIC64_OPS_DECL__(wb); + +#undef ERTS_ATOMIC64_OPS_DECL__ + +ERTS_GLB_INLINE void +erts_atomic64_set_dirty(erts_atomic64_t *var, erts_aint64_t val); +ERTS_GLB_INLINE erts_aint64_t +erts_atomic64_read_dirty(erts_atomic64_t *var); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +/* + * The ethr_dw_atomic_*_nob() functions below + * are here to make it possible for the + * ERTS_ATOMIC64_OPS_IMPL__() to map erts + * barriers to ethread barriers... + */ +static ERTS_INLINE void +ethr_dw_atomic_init_nob(ethr_dw_atomic_t *var, + ethr_dw_sint_t *val) +{ + ethr_dw_atomic_init(var, val); +} + +static ERTS_INLINE void +ethr_dw_atomic_set_nob(ethr_dw_atomic_t *var, + ethr_dw_sint_t *val) +{ + ethr_dw_atomic_set(var, val); +} + +static ERTS_INLINE void +ethr_dw_atomic_read_nob(ethr_dw_atomic_t *var, + ethr_dw_sint_t *val) +{ + ethr_dw_atomic_read(var, val); +} + +static ERTS_INLINE int +ethr_dw_atomic_cmpxchg_nob(ethr_dw_atomic_t *var, + ethr_dw_sint_t *new, + ethr_dw_sint_t *xchg) +{ + return ethr_dw_atomic_cmpxchg(var, new, xchg); +} + +#undef ERTS_ATOMIC64_OPS_IMPL__ +#undef ERTS_ATOMIC64_DW_CMPXCHG_IMPL__ +#undef ERTS_DW_SINT_TO_AINT64__ +#undef ERTS_AINT64_TO_DW_SINT__ + +#ifdef ETHR_SU_DW_NAINT_T__ +#define ERTS_DW_SINT_TO_AINT64__(DW) \ + ((erts_aint64_t) DW.dw_sint) +#define ERTS_AINT64_TO_DW_SINT__(DW, AINT64) \ + (DW.dw_sint = (ETHR_SU_DW_NAINT_T__) AINT64) +#else /* !ETHR_SU_DW_NAINT_T__ */ +#define ERTS_DW_SINT_TO_AINT64__(DW) \ + ((((erts_aint64_t) DW.sint[ETHR_DW_SINT_HIGH_WORD]) << 32) \ + | (((erts_aint64_t) DW.sint[ETHR_DW_SINT_LOW_WORD]) \ + & ((erts_aint64_t) 0xffffffff))) +#define ERTS_AINT64_TO_DW_SINT__(DW, AINT64) \ + do { \ + DW.sint[ETHR_DW_SINT_LOW_WORD] = \ + (ethr_sint_t) (AINT64 & 0xffffffff); \ + DW.sint[ETHR_DW_SINT_HIGH_WORD] = \ + (ethr_sint_t) ((AINT64 >> 32) & 0xffffffff); \ + } while (0) +#endif /* !ETHR_SU_DW_NAINT_T__ */ + +#define ERTS_ATOMIC64_DW_CMPXCHG_IMPL__(CmpXchgOp, \ + AVarP, XchgVar, NewVar, \ + ModificationCode) \ +do { \ + ethr_dw_sint_t dw_xchg__, dw_new__; \ + ethr_dw_atomic_read(AVarP, &dw_xchg__); \ + do { \ + XchgVar = ERTS_DW_SINT_TO_AINT64__(dw_xchg__); \ + { \ + ModificationCode; \ + } \ + ERTS_AINT64_TO_DW_SINT__(dw_new__, NewVar); \ + } while (!CmpXchgOp((AVarP), &dw_new__, &dw_xchg__)); \ +} while (0) + +#define ERTS_ATOMIC64_OPS_IMPL__(BARRIER) \ + \ +ERTS_GLB_INLINE void \ +erts_atomic64_init_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val) \ +{ \ + ethr_dw_sint_t dw; \ + ERTS_AINT64_TO_DW_SINT__(dw, val); \ + ethr_dw_atomic_init_ ## BARRIER(var, &dw); \ +} \ + \ +ERTS_GLB_INLINE void \ +erts_atomic64_set_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val) \ +{ \ + ethr_dw_sint_t dw; \ + ERTS_AINT64_TO_DW_SINT__(dw, val); \ + ethr_dw_atomic_set_ ## BARRIER(var, &dw); \ +} \ + \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_read_ ## BARRIER(erts_atomic64_t *var) \ +{ \ + ethr_dw_sint_t dw; \ + ethr_dw_atomic_read_ ## BARRIER(var, &dw); \ + return ERTS_DW_SINT_TO_AINT64__(dw); \ +} \ + \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_inc_read_ ## BARRIER(erts_atomic64_t *var) \ +{ \ + erts_aint64_t xchg, new; \ + ERTS_ATOMIC64_DW_CMPXCHG_IMPL__(ethr_dw_atomic_cmpxchg_ ## BARRIER, \ + var, xchg, new, \ + new = xchg + 1); \ + return new; \ +} \ + \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_dec_read_ ## BARRIER(erts_atomic64_t *var) \ +{ \ + erts_aint64_t xchg, new; \ + ERTS_ATOMIC64_DW_CMPXCHG_IMPL__(ethr_dw_atomic_cmpxchg_ ## BARRIER, \ + var, xchg, new, \ + new = xchg - 1); \ + return new; \ +} \ + \ +ERTS_GLB_INLINE void \ +erts_atomic64_inc_ ## BARRIER(erts_atomic64_t *var) \ +{ \ + erts_aint64_t xchg, new; \ + ERTS_ATOMIC64_DW_CMPXCHG_IMPL__(ethr_dw_atomic_cmpxchg_ ## BARRIER, \ + var, xchg, new, \ + new = xchg + 1); \ +} \ + \ +ERTS_GLB_INLINE void \ +erts_atomic64_dec_ ## BARRIER(erts_atomic64_t *var) \ +{ \ + erts_aint64_t xchg, new; \ + ERTS_ATOMIC64_DW_CMPXCHG_IMPL__(ethr_dw_atomic_cmpxchg_ ## BARRIER, \ + var, xchg, new, \ + new = xchg - 1); \ +} \ + \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_add_read_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val) \ +{ \ + erts_aint64_t xchg, new; \ + ERTS_ATOMIC64_DW_CMPXCHG_IMPL__(ethr_dw_atomic_cmpxchg_ ## BARRIER, \ + var, xchg, new, \ + new = xchg + val); \ + return new; \ +} \ + \ +ERTS_GLB_INLINE void \ +erts_atomic64_add_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val) \ +{ \ + erts_aint64_t xchg, new; \ + ERTS_ATOMIC64_DW_CMPXCHG_IMPL__(ethr_dw_atomic_cmpxchg_ ## BARRIER, \ + var, xchg, new, \ + new = xchg + val); \ +} \ + \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_read_bor_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val) \ +{ \ + erts_aint64_t xchg, new; \ + ERTS_ATOMIC64_DW_CMPXCHG_IMPL__(ethr_dw_atomic_cmpxchg_ ## BARRIER, \ + var, xchg, new, \ + new = xchg | val); \ + return xchg; \ +} \ + \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_read_band_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val) \ +{ \ + erts_aint64_t xchg, new; \ + ERTS_ATOMIC64_DW_CMPXCHG_IMPL__(ethr_dw_atomic_cmpxchg_ ## BARRIER, \ + var, xchg, new, \ + new = xchg & val); \ + return xchg; \ +} \ + \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_xchg_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t val) \ +{ \ + erts_aint64_t xchg, new; \ + ERTS_ATOMIC64_DW_CMPXCHG_IMPL__(ethr_dw_atomic_cmpxchg_ ## BARRIER, \ + var, xchg, new, \ + new = val); \ + return xchg; \ +} \ + \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_cmpxchg_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t new, \ + erts_aint64_t exp) \ +{ \ + ethr_dw_sint_t dw_xchg, dw_new; \ + ERTS_AINT64_TO_DW_SINT__(dw_xchg, exp); \ + ERTS_AINT64_TO_DW_SINT__(dw_new, new); \ + if (ethr_dw_atomic_cmpxchg_ ## BARRIER(var, &dw_new, &dw_xchg)) \ + return exp; \ + return ERTS_DW_SINT_TO_AINT64__(dw_xchg); \ +} \ + \ +ERTS_GLB_INLINE erts_aint64_t \ +erts_atomic64_read_bset_ ## BARRIER(erts_atomic64_t *var, \ + erts_aint64_t mask, \ + erts_aint64_t set) \ +{ \ + erts_aint64_t xchg, new; \ + ERTS_ATOMIC64_DW_CMPXCHG_IMPL__(ethr_dw_atomic_cmpxchg_ ## BARRIER, \ + var, xchg, new, \ + { \ + new = xchg & ~mask; \ + new |= mask & set; \ + }); \ + return xchg; \ +} + +ERTS_ATOMIC64_OPS_IMPL__(nob) +ERTS_ATOMIC64_OPS_IMPL__(mb) +ERTS_ATOMIC64_OPS_IMPL__(acqb) +ERTS_ATOMIC64_OPS_IMPL__(relb) +ERTS_ATOMIC64_OPS_IMPL__(ddrb) +ERTS_ATOMIC64_OPS_IMPL__(rb) +ERTS_ATOMIC64_OPS_IMPL__(wb) + +#undef ERTS_ATOMIC64_OPS_IMPL__ +#undef ERTS_ATOMIC64_DW_CMPXCHG_IMPL__ + +ERTS_GLB_INLINE void +erts_atomic64_set_dirty(erts_atomic64_t *var, erts_aint64_t val) +{ + ethr_sint_t *sint = ethr_dw_atomic_addr(var); + ethr_dw_sint_t dw; + ERTS_AINT64_TO_DW_SINT__(dw, val); + sint[0] = dw.sint[0]; + sint[1] = dw.sint[1]; +} + +ERTS_GLB_INLINE erts_aint64_t +erts_atomic64_read_dirty(erts_atomic64_t *var) +{ + ethr_sint_t *sint; + ethr_dw_sint_t dw; + sint = ethr_dw_atomic_addr(var); + dw.sint[0] = sint[0]; + dw.sint[1] = sint[1]; + return ERTS_DW_SINT_TO_AINT64__(dw); +} + +#undef ERTS_DW_SINT_TO_AINT64__ +#undef ERTS_AINT64_TO_DW_SINT__ + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +#endif /* ARCH_32 */ + #else /* !USE_THREADS */ /* Double word size atomics */ @@ -1462,6 +1933,116 @@ erts_atomic32_read_dirty(erts_atomic32_t *var) #define erts_atomic32_set_dirty erts_no_atomic32_set #define erts_atomic32_read_dirty erts_no_atomic32_read +/* 64-bit atomics */ + +#define erts_atomic64_init_nob erts_no_atomic64_set +#define erts_atomic64_set_nob erts_no_atomic64_set +#define erts_atomic64_read_nob erts_no_atomic64_read +#define erts_atomic64_inc_read_nob erts_no_atomic64_inc_read +#define erts_atomic64_dec_read_nob erts_no_atomic64_dec_read +#define erts_atomic64_inc_nob erts_no_atomic64_inc +#define erts_atomic64_dec_nob erts_no_atomic64_dec +#define erts_atomic64_add_read_nob erts_no_atomic64_add_read +#define erts_atomic64_add_nob erts_no_atomic64_add +#define erts_atomic64_read_bor_nob erts_no_atomic64_read_bor +#define erts_atomic64_read_band_nob erts_no_atomic64_read_band +#define erts_atomic64_xchg_nob erts_no_atomic64_xchg +#define erts_atomic64_cmpxchg_nob erts_no_atomic64_cmpxchg +#define erts_atomic64_read_bset_nob erts_no_atomic64_read_bset + +#define erts_atomic64_init_mb erts_no_atomic64_set +#define erts_atomic64_set_mb erts_no_atomic64_set +#define erts_atomic64_read_mb erts_no_atomic64_read +#define erts_atomic64_inc_read_mb erts_no_atomic64_inc_read +#define erts_atomic64_dec_read_mb erts_no_atomic64_dec_read +#define erts_atomic64_inc_mb erts_no_atomic64_inc +#define erts_atomic64_dec_mb erts_no_atomic64_dec +#define erts_atomic64_add_read_mb erts_no_atomic64_add_read +#define erts_atomic64_add_mb erts_no_atomic64_add +#define erts_atomic64_read_bor_mb erts_no_atomic64_read_bor +#define erts_atomic64_read_band_mb erts_no_atomic64_read_band +#define erts_atomic64_xchg_mb erts_no_atomic64_xchg +#define erts_atomic64_cmpxchg_mb erts_no_atomic64_cmpxchg +#define erts_atomic64_read_bset_mb erts_no_atomic64_read_bset + +#define erts_atomic64_init_acqb erts_no_atomic64_set +#define erts_atomic64_set_acqb erts_no_atomic64_set +#define erts_atomic64_read_acqb erts_no_atomic64_read +#define erts_atomic64_inc_read_acqb erts_no_atomic64_inc_read +#define erts_atomic64_dec_read_acqb erts_no_atomic64_dec_read +#define erts_atomic64_inc_acqb erts_no_atomic64_inc +#define erts_atomic64_dec_acqb erts_no_atomic64_dec +#define erts_atomic64_add_read_acqb erts_no_atomic64_add_read +#define erts_atomic64_add_acqb erts_no_atomic64_add +#define erts_atomic64_read_bor_acqb erts_no_atomic64_read_bor +#define erts_atomic64_read_band_acqb erts_no_atomic64_read_band +#define erts_atomic64_xchg_acqb erts_no_atomic64_xchg +#define erts_atomic64_cmpxchg_acqb erts_no_atomic64_cmpxchg +#define erts_atomic64_read_bset_acqb erts_no_atomic64_read_bset + +#define erts_atomic64_init_relb erts_no_atomic64_set +#define erts_atomic64_set_relb erts_no_atomic64_set +#define erts_atomic64_read_relb erts_no_atomic64_read +#define erts_atomic64_inc_read_relb erts_no_atomic64_inc_read +#define erts_atomic64_dec_read_relb erts_no_atomic64_dec_read +#define erts_atomic64_inc_relb erts_no_atomic64_inc +#define erts_atomic64_dec_relb erts_no_atomic64_dec +#define erts_atomic64_add_read_relb erts_no_atomic64_add_read +#define erts_atomic64_add_relb erts_no_atomic64_add +#define erts_atomic64_read_bor_relb erts_no_atomic64_read_bor +#define erts_atomic64_read_band_relb erts_no_atomic64_read_band +#define erts_atomic64_xchg_relb erts_no_atomic64_xchg +#define erts_atomic64_cmpxchg_relb erts_no_atomic64_cmpxchg +#define erts_atomic64_read_bset_relb erts_no_atomic64_read_bset + +#define erts_atomic64_init_ddrb erts_no_atomic64_set +#define erts_atomic64_set_ddrb erts_no_atomic64_set +#define erts_atomic64_read_ddrb erts_no_atomic64_read +#define erts_atomic64_inc_read_ddrb erts_no_atomic64_inc_read +#define erts_atomic64_dec_read_ddrb erts_no_atomic64_dec_read +#define erts_atomic64_inc_ddrb erts_no_atomic64_inc +#define erts_atomic64_dec_ddrb erts_no_atomic64_dec +#define erts_atomic64_add_read_ddrb erts_no_atomic64_add_read +#define erts_atomic64_add_ddrb erts_no_atomic64_add +#define erts_atomic64_read_bor_ddrb erts_no_atomic64_read_bor +#define erts_atomic64_read_band_ddrb erts_no_atomic64_read_band +#define erts_atomic64_xchg_ddrb erts_no_atomic64_xchg +#define erts_atomic64_cmpxchg_ddrb erts_no_atomic64_cmpxchg +#define erts_atomic64_read_bset_ddrb erts_no_atomic64_read_bset + +#define erts_atomic64_init_rb erts_no_atomic64_set +#define erts_atomic64_set_rb erts_no_atomic64_set +#define erts_atomic64_read_rb erts_no_atomic64_read +#define erts_atomic64_inc_read_rb erts_no_atomic64_inc_read +#define erts_atomic64_dec_read_rb erts_no_atomic64_dec_read +#define erts_atomic64_inc_rb erts_no_atomic64_inc +#define erts_atomic64_dec_rb erts_no_atomic64_dec +#define erts_atomic64_add_read_rb erts_no_atomic64_add_read +#define erts_atomic64_add_rb erts_no_atomic64_add +#define erts_atomic64_read_bor_rb erts_no_atomic64_read_bor +#define erts_atomic64_read_band_rb erts_no_atomic64_read_band +#define erts_atomic64_xchg_rb erts_no_atomic64_xchg +#define erts_atomic64_cmpxchg_rb erts_no_atomic64_cmpxchg +#define erts_atomic64_read_bset_rb erts_no_atomic64_read_bset + +#define erts_atomic64_init_wb erts_no_atomic64_set +#define erts_atomic64_set_wb erts_no_atomic64_set +#define erts_atomic64_read_wb erts_no_atomic64_read +#define erts_atomic64_inc_read_wb erts_no_atomic64_inc_read +#define erts_atomic64_dec_read_wb erts_no_atomic64_dec_read +#define erts_atomic64_inc_wb erts_no_atomic64_inc +#define erts_atomic64_dec_wb erts_no_atomic64_dec +#define erts_atomic64_add_read_wb erts_no_atomic64_add_read +#define erts_atomic64_add_wb erts_no_atomic64_add +#define erts_atomic64_read_bor_wb erts_no_atomic64_read_bor +#define erts_atomic64_read_band_wb erts_no_atomic64_read_band +#define erts_atomic64_xchg_wb erts_no_atomic64_xchg +#define erts_atomic64_cmpxchg_wb erts_no_atomic64_cmpxchg +#define erts_atomic64_read_bset_wb erts_no_atomic64_read_bset + +#define erts_atomic64_set_dirty erts_no_atomic64_set +#define erts_atomic64_read_dirty erts_no_atomic64_read + #endif /* !USE_THREADS */ #if ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -2383,6 +2964,104 @@ erts_no_atomic32_read_bset(erts_no_atomic32_t *var, return old; } +/* atomic64 */ + +ERTS_GLB_INLINE void +erts_no_atomic64_set(erts_no_atomic64_t *var, erts_aint64_t i) +{ + *var = i; +} + +ERTS_GLB_INLINE erts_aint64_t +erts_no_atomic64_read(erts_no_atomic64_t *var) +{ + return *var; +} + +ERTS_GLB_INLINE erts_aint64_t +erts_no_atomic64_inc_read(erts_no_atomic64_t *incp) +{ + return ++(*incp); +} + +ERTS_GLB_INLINE erts_aint64_t +erts_no_atomic64_dec_read(erts_no_atomic64_t *decp) +{ + return --(*decp); +} + +ERTS_GLB_INLINE void +erts_no_atomic64_inc(erts_no_atomic64_t *incp) +{ + ++(*incp); +} + +ERTS_GLB_INLINE void +erts_no_atomic64_dec(erts_no_atomic64_t *decp) +{ + --(*decp); +} + +ERTS_GLB_INLINE erts_aint64_t +erts_no_atomic64_add_read(erts_no_atomic64_t *addp, erts_aint64_t i) +{ + return *addp += i; +} + +ERTS_GLB_INLINE void +erts_no_atomic64_add(erts_no_atomic64_t *addp, erts_aint64_t i) +{ + *addp += i; +} + +ERTS_GLB_INLINE erts_aint64_t +erts_no_atomic64_read_bor(erts_no_atomic64_t *var, erts_aint64_t mask) +{ + erts_aint64_t old; + old = *var; + *var |= mask; + return old; +} + +ERTS_GLB_INLINE erts_aint64_t +erts_no_atomic64_read_band(erts_no_atomic64_t *var, erts_aint64_t mask) +{ + erts_aint64_t old; + old = *var; + *var &= mask; + return old; +} + +ERTS_GLB_INLINE erts_aint64_t +erts_no_atomic64_xchg(erts_no_atomic64_t *xchgp, erts_aint64_t new) +{ + erts_aint64_t old = *xchgp; + *xchgp = new; + return old; +} + +ERTS_GLB_INLINE erts_aint64_t +erts_no_atomic64_cmpxchg(erts_no_atomic64_t *xchgp, + erts_aint64_t new, + erts_aint64_t expected) +{ + erts_aint64_t old = *xchgp; + if (old == expected) + *xchgp = new; + return old; +} + +ERTS_GLB_INLINE erts_aint64_t +erts_no_atomic64_read_bset(erts_no_atomic64_t *var, + erts_aint64_t mask, + erts_aint64_t set) +{ + erts_aint64_t old = *var; + *var &= ~mask; + *var |= (mask & set); + return old; +} + /* spinlock */ ERTS_GLB_INLINE void -- cgit v1.2.3 From 843cf9120d2845be2c3304fc8dcb4c16b49bbfca Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 3 Dec 2014 22:50:28 +0100 Subject: Use the new 64-bit atomic ops API --- erts/emulator/beam/erl_process.c | 54 +-------------- erts/emulator/beam/erl_process.h | 8 +-- erts/emulator/beam/erl_ptab.c | 100 ++------------------------- erts/emulator/beam/erl_ptab.h | 6 +- erts/emulator/beam/erl_thr_progress.c | 52 +------------- erts/emulator/beam/erl_thr_progress.h | 60 ++-------------- erts/emulator/beam/erl_utils.h | 43 +----------- erts/emulator/beam/utils.c | 124 ++-------------------------------- 8 files changed, 29 insertions(+), 418 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index f84677dea4..ea63d20dfa 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -716,72 +716,24 @@ sched_wall_time_ts(void) #if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT -#ifdef ARCH_64 - -static ERTS_INLINE Uint64 -aschedtime_read(ErtsAtomicSchedTime *var) -{ - return (Uint64) erts_atomic_read_nob((erts_atomic_t *) var); -} - -static ERTS_INLINE void -aschedtime_set(ErtsAtomicSchedTime *var, Uint64 val) -{ - erts_atomic_set_nob((erts_atomic_t *) var, (erts_aint_t) val); -} - -static ERTS_INLINE void -aschedtime_init(ErtsAtomicSchedTime *var) -{ - erts_atomic_init_nob((erts_atomic_t *) var, (erts_aint_t) 0); -} - -#elif defined(ARCH_32) - static ERTS_INLINE Uint64 aschedtime_read(ErtsAtomicSchedTime *var) { - erts_dw_aint_t dw; - erts_dw_atomic_read_nob((erts_dw_atomic_t *) var, &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 + return (Uint64) erts_atomic64_read_nob((erts_atomic64_t *) var); } static ERTS_INLINE void aschedtime_set(ErtsAtomicSchedTime *var, Uint64 val) { - erts_dw_aint_t dw; -#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 - erts_dw_atomic_set_nob((erts_dw_atomic_t *) var, &dw); + erts_atomic64_set_nob((erts_atomic64_t *) var, (erts_aint64_t) val); } static ERTS_INLINE void aschedtime_init(ErtsAtomicSchedTime *var) { - erts_dw_aint_t dw; - dw.sint[ERTS_DW_AINT_LOW_WORD] = (erts_aint_t) 0; - dw.sint[ERTS_DW_AINT_HIGH_WORD] = (erts_aint_t) 0; - erts_dw_atomic_init_nob((erts_dw_atomic_t *) var, &dw); + erts_atomic64_init_nob((erts_atomic64_t *) var, (erts_aint64_t) 0); } -#else -# error :-/ -#endif - #define ERTS_GET_AVG_MAX_UNLOCKED_TRY 50 #define ERTS_SCHED_AVG_UTIL_WRITE_MARKER (~((Uint64) 0)) diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 27a3a3553b..f50b217d4a 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -351,13 +351,7 @@ typedef struct { #undef ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT #define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT -#ifdef ARCH_64 -typedef erts_atomic_t ErtsAtomicSchedTime; -#elif defined(ARCH_32) -typedef erts_dw_atomic_t ErtsAtomicSchedTime; -#else -# error :-/ -#endif +typedef erts_atomic64_t ErtsAtomicSchedTime; #if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT typedef struct { diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c index eabf016081..02943ee683 100644 --- a/erts/emulator/beam/erl_ptab.c +++ b/erts/emulator/beam/erl_ptab.c @@ -280,124 +280,38 @@ struct ErtsPTabListBifData_ { }; -#ifdef ARCH_32 - -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_data_init_nob(ErtsPTab *ptab, Uint64 val) { - erts_dw_aint_t dw; - unint64_to_dw_aint(&dw, val); - erts_smp_dw_atomic_init_nob(&ptab->vola.tile.last_data, &dw); + erts_smp_atomic64_init_nob(&ptab->vola.tile.last_data, (erts_aint64_t) val); } static ERTS_INLINE void last_data_set_relb(ErtsPTab *ptab, Uint64 val) { - erts_dw_aint_t dw; - unint64_to_dw_aint(&dw, val); - erts_smp_dw_atomic_set_relb(&ptab->vola.tile.last_data, &dw); + erts_smp_atomic64_set_relb(&ptab->vola.tile.last_data, (erts_aint64_t) val); } static ERTS_INLINE Uint64 last_data_read_nob(ErtsPTab *ptab) { - erts_dw_aint_t dw; - erts_smp_dw_atomic_read_nob(&ptab->vola.tile.last_data, &dw); - return dw_aint_to_uint64(&dw); + return (Uint64) erts_smp_atomic64_read_nob(&ptab->vola.tile.last_data); } static ERTS_INLINE Uint64 last_data_read_acqb(ErtsPTab *ptab) { - erts_dw_aint_t dw; - erts_smp_dw_atomic_read_acqb(&ptab->vola.tile.last_data, &dw); - return dw_aint_to_uint64(&dw); + return (Uint64) erts_smp_atomic64_read_acqb(&ptab->vola.tile.last_data); } static ERTS_INLINE Uint64 last_data_cmpxchg_relb(ErtsPTab *ptab, 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(&ptab->vola.tile.last_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_data_init_nob(ErtsPTab *ptab, Uint64 val) -{ - erts_smp_atomic_init_nob(&ptab->vola.tile.last_data, (erts_aint_t) val); + return (Uint64) erts_smp_atomic64_cmpxchg_relb(&ptab->vola.tile.last_data, + (erts_aint64_t) new, + (erts_aint64_t) exp); } -static ERTS_INLINE void -last_data_set_relb(ErtsPTab *ptab, Uint64 val) -{ - erts_smp_atomic_set_relb(&ptab->vola.tile.last_data, (erts_aint_t) val); -} - -static ERTS_INLINE Uint64 -last_data_read_nob(ErtsPTab *ptab) -{ - return (Uint64) erts_smp_atomic_read_nob(&ptab->vola.tile.last_data); -} - -static ERTS_INLINE Uint64 -last_data_read_acqb(ErtsPTab *ptab) -{ - return (Uint64) erts_smp_atomic_read_acqb(&ptab->vola.tile.last_data); -} - -static ERTS_INLINE Uint64 -last_data_cmpxchg_relb(ErtsPTab *ptab, Uint64 new, Uint64 exp) -{ - return (Uint64) erts_smp_atomic_cmpxchg_relb(&ptab->vola.tile.last_data, - (erts_aint_t) new, - (erts_aint_t) exp); -} - -#else -# error "Not 64-bit, nor 32-bit architecture..." -#endif - static ERTS_INLINE int last_data_cmp(Uint64 ld1, Uint64 ld2) { diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h index e3e05f14af..876241159b 100644 --- a/erts/emulator/beam/erl_ptab.h +++ b/erts/emulator/beam/erl_ptab.h @@ -88,11 +88,7 @@ typedef struct { } ErtsPTabListData; typedef struct { -#ifdef ARCH_32 - erts_smp_dw_atomic_t last_data; -#else - erts_smp_atomic_t last_data; -#endif + erts_smp_atomic64_t last_data; erts_smp_atomic32_t count; erts_smp_atomic32_t aid_ix; erts_smp_atomic32_t fid_ix; diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 545a0343d0..664c479eb6 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -115,70 +115,24 @@ #undef read_nob #define read_nob erts_thr_prgr_read_nob__ -#ifdef ARCH_64 - static ERTS_INLINE void set_mb(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) { - erts_atomic_set_mb(atmc, val); + erts_atomic64_set_mb(atmc, (erts_aint64_t) val); } static ERTS_INLINE void set_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) { - erts_atomic_set_nob(atmc, val); + erts_atomic64_set_nob(atmc, (erts_aint64_t) val); } static ERTS_INLINE void init_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) { - erts_atomic_init_nob(atmc, val); -} - -#else - -#undef dw_aint_to_val -#define dw_aint_to_val erts_thr_prgr_dw_aint_to_val__ - -static void -val_to_dw_aint(erts_dw_aint_t *dw_aint, ErtsThrPrgrVal val) -{ -#ifdef ETHR_SU_DW_NAINT_T__ - dw_aint->dw_sint = (ETHR_SU_DW_NAINT_T__) val; -#else - dw_aint->sint[ERTS_DW_AINT_LOW_WORD] - = (erts_aint_t) (val & 0xffffffff); - dw_aint->sint[ERTS_DW_AINT_HIGH_WORD] - = (erts_aint_t) ((val >> 32) & 0xffffffff); -#endif + erts_atomic64_init_nob(atmc, (erts_aint64_t) val); } -static ERTS_INLINE void -set_mb(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) -{ - erts_dw_aint_t dw_aint; - val_to_dw_aint(&dw_aint, val); - erts_dw_atomic_set_mb(atmc, &dw_aint); -} - -static ERTS_INLINE void -set_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) -{ - erts_dw_aint_t dw_aint; - val_to_dw_aint(&dw_aint, val); - erts_dw_atomic_set_nob(atmc, &dw_aint); -} - -static ERTS_INLINE void -init_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) -{ - erts_dw_aint_t dw_aint; - val_to_dw_aint(&dw_aint, val); - erts_dw_atomic_init_nob(atmc, &dw_aint); -} - -#endif - /* #define ERTS_THR_PROGRESS_STATE_DEBUG */ #ifdef ERTS_THR_PROGRESS_STATE_DEBUG diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h index 5f392944c2..03ddbd467c 100644 --- a/erts/emulator/beam/erl_thr_progress.h +++ b/erts/emulator/beam/erl_thr_progress.h @@ -115,11 +115,7 @@ struct ErtsThrPrgrLaterOp_ { extern erts_tsd_key_t erts_thr_prgr_data_key__; -#ifdef ARCH_64 -# define ERTS_THR_PRGR_ATOMIC erts_atomic_t -#else /* ARCH_32 */ -# define ERTS_THR_PRGR_ATOMIC erts_dw_atomic_t -#endif +#define ERTS_THR_PRGR_ATOMIC erts_atomic64_t typedef struct { void *arg; @@ -158,10 +154,6 @@ void erts_thr_progress_unmanaged_continue__(int umrefc_ix); void erts_thr_progress_dbg_print_state(void); -#ifdef ARCH_32 -#define ERTS_THR_PRGR_ATOMIC erts_dw_atomic_t -ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_dw_aint_to_val__(erts_dw_aint_t *dw_aint); -#endif ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_nob__(ERTS_THR_PRGR_ATOMIC *atmc); ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_acqb__(ERTS_THR_PRGR_ATOMIC *atmc); ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_mb__(ERTS_THR_PRGR_ATOMIC *atmc); @@ -184,68 +176,24 @@ ERTS_GLB_INLINE int erts_thr_progress_has_reached(ErtsThrPrgrVal val); #if ERTS_GLB_INLINE_INCL_FUNC_DEF -#ifdef ARCH_64 - -ERTS_GLB_INLINE ErtsThrPrgrVal -erts_thr_prgr_read_nob__(ERTS_THR_PRGR_ATOMIC *atmc) -{ - return (ErtsThrPrgrVal) erts_atomic_read_nob(atmc); -} - -ERTS_GLB_INLINE ErtsThrPrgrVal -erts_thr_prgr_read_acqb__(ERTS_THR_PRGR_ATOMIC *atmc) -{ - return (ErtsThrPrgrVal) erts_atomic_read_acqb(atmc); -} - -ERTS_GLB_INLINE ErtsThrPrgrVal -erts_thr_prgr_read_mb__(ERTS_THR_PRGR_ATOMIC *atmc) -{ - return (ErtsThrPrgrVal) erts_atomic_read_mb(atmc); -} - -#else /* ARCH_32 */ - -ERTS_GLB_INLINE ErtsThrPrgrVal -erts_thr_prgr_dw_aint_to_val__(erts_dw_aint_t *dw_aint) -{ -#ifdef ETHR_SU_DW_NAINT_T__ - return (ErtsThrPrgrVal) dw_aint->dw_sint; -#else - ErtsThrPrgrVal res; - res = (ErtsThrPrgrVal) ((Uint32) dw_aint->sint[ERTS_DW_AINT_HIGH_WORD]); - res <<= 32; - res |= (ErtsThrPrgrVal) ((Uint32) dw_aint->sint[ERTS_DW_AINT_LOW_WORD]); - return res; -#endif -} - ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_nob__(ERTS_THR_PRGR_ATOMIC *atmc) { - erts_dw_aint_t dw_aint; - erts_dw_atomic_read_nob(atmc, &dw_aint); - return erts_thr_prgr_dw_aint_to_val__(&dw_aint); + return (ErtsThrPrgrVal) erts_atomic64_read_nob(atmc); } ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_acqb__(ERTS_THR_PRGR_ATOMIC *atmc) { - erts_dw_aint_t dw_aint; - erts_dw_atomic_read_acqb(atmc, &dw_aint); - return erts_thr_prgr_dw_aint_to_val__(&dw_aint); + return (ErtsThrPrgrVal) erts_atomic64_read_acqb(atmc); } ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_mb__(ERTS_THR_PRGR_ATOMIC *atmc) { - erts_dw_aint_t dw_aint; - erts_dw_atomic_read_mb(atmc, &dw_aint); - return erts_thr_prgr_dw_aint_to_val__(&dw_aint); + return (ErtsThrPrgrVal) erts_atomic64_read_mb(atmc); } -#endif - ERTS_GLB_INLINE int erts_thr_progress_is_managed_thread(void) { diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 0807649ea1..c32f8fd61c 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -32,11 +32,7 @@ typedef struct { #endif union { Uint64 not_atomic; -#ifdef ARCH_64 - erts_atomic_t atomic; -#else - erts_dw_atomic_t atomic; -#endif + erts_atomic64_t atomic; } counter; } erts_interval_t; @@ -50,9 +46,6 @@ 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 *); @@ -62,46 +55,16 @@ 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 + return (Uint64) erts_atomic64_read_nob(&icp->counter.atomic); } 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 + return (Uint64) erts_atomic64_read_acqb(&icp->counter.atomic); } ERTS_GLB_INLINE Uint64 diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index f20e6e5665..c505c44905 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -4216,19 +4216,7 @@ void erts_silence_warn_unused_result(long unused) 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 + erts_atomic64_init_nob(&icp->counter.atomic, 0); #ifdef DEBUG icp->smp_api = 0; #endif @@ -4250,55 +4238,13 @@ erts_smp_interval_init(erts_interval_t *icp) 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 + return (Uint64) erts_atomic64_inc_read_nob(&icp->counter.atomic); } 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 + return (Uint64) erts_atomic64_inc_read_relb(&icp->counter.atomic); } @@ -4306,38 +4252,10 @@ 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); + curr_ic = (Uint64) erts_atomic64_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 + return (Uint64) erts_atomic64_inc_read_nob(&icp->counter.atomic); } @@ -4345,38 +4263,10 @@ 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); + curr_ic = (Uint64) erts_atomic64_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 + return (Uint64) erts_atomic64_inc_read_acqb(&icp->counter.atomic); } Uint64 -- cgit v1.2.3 From fb064173870a75d80402c3ad1e48dbaf59f086fb Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 10 Dec 2014 10:36:51 +0100 Subject: erts: Make sure IOV_MAX is enforced for writev --- erts/emulator/sys/unix/sys.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index eed93cb2a0..c51954be38 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -86,6 +86,14 @@ static erts_smp_rwmtx_t environ_rwmtx; #define DISABLE_VFORK 0 #endif +#if defined IOV_MAX +#define MAXIOV IOV_MAX +#elif defined UIO_MAXIOV +#define MAXIOV UIO_MAXIOV +#else +#define MAXIOV 16 +#endif + #ifdef USE_THREADS # ifdef ENABLE_CHILD_WAITER_THREAD # define CHLDWTHR ENABLE_CHILD_WAITER_THREAD @@ -2524,32 +2532,28 @@ fd_async(void *async_data) SysIOVec *iov0; SysIOVec *iov; int iovlen; - int iovcnt; - int p; + int err; /* much of this code is stolen from efile_drv:invoke_writev */ driver_pdl_lock(dd->blocking->pdl); iov0 = driver_peekq(dd->port_num, &iovlen); - /* Calculate iovcnt */ - for (p = 0, iovcnt = 0; iovcnt < iovlen; - p += iov0[iovcnt++].iov_len) - ; + iovlen = iovlen < MAXIOV ? iovlen : MAXIOV; iov = erts_alloc_fnf(ERTS_ALC_T_SYS_WRITE_BUF, - sizeof(SysIOVec)*iovcnt); + sizeof(SysIOVec)*iovlen); if (!iov) { res = -1; - errno = ENOMEM; - erts_free(ERTS_ALC_T_SYS_WRITE_BUF, iov); + err = ENOMEM; driver_pdl_unlock(dd->blocking->pdl); } else { - memcpy(iov,iov0,iovcnt*sizeof(SysIOVec)); + memcpy(iov,iov0,iovlen*sizeof(SysIOVec)); driver_pdl_unlock(dd->blocking->pdl); res = writev(dd->ofd, iov, iovlen); + err = errno; erts_free(ERTS_ALC_T_SYS_WRITE_BUF, iov); } dd->blocking->res = res; - dd->blocking->err = errno; + dd->blocking->err = err; } void fd_ready_async(ErlDrvData drv_data, -- cgit v1.2.3 From b94fe0f28d68747a2e9b78101f91b86b37c8f83b Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 15 Dec 2014 17:26:01 +0100 Subject: erts: Fix big-endian issue with printf_putc On little-endian machines, doing &int will give the smallest byte which is what we want to give to write. But on big-endian it will give the highest byte, which will always be \000 here which results in write never doing any writes. So we have to cast c to an unsigned char before passing it to write. --- erts/lib_src/common/erl_printf.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c index 0a0346ac2d..a38017b62f 100644 --- a/erts/lib_src/common/erl_printf.c +++ b/erts/lib_src/common/erl_printf.c @@ -93,10 +93,12 @@ void (*erts_printf_unblock_fpe)(int) = NULL; static int printf_putc(int c, FILE *stream) { if ((FILE*)stream == stdout || (FILE*)stream == stderr) { - int fd = stream == stdout ? fileno(stdout) : fileno(stdin); + int fd = stream == stdout ? fileno(stdout) : fileno(stderr); + /* cast to a char here, because write expects bytes. */ + unsigned char buf[1] = { c }; int res; do { - res = write(fd,&c,1); + res = write(fd, buf, 1); } while (res == -1 && (errno == EAGAIN || errno == EINTR)); if (res == -1) return EOF; return res; @@ -109,7 +111,7 @@ static size_t printf_fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream) { if ((FILE*)stream == stdout || (FILE*)stream == stderr) { - int fd = stream == stdout ? fileno(stdout) : fileno(stdin); + int fd = stream == stdout ? fileno(stdout) : fileno(stderr); int res; do { res = write(fd, ptr, size*nitems); -- cgit v1.2.3 From 3aa7023f2e4f5454faddd663f00ee4c935f9b8f6 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 4 Dec 2014 11:09:28 +0100 Subject: ets: Increase data available in crash dumps and ets:info OTP-12376 --- erts/emulator/beam/erl_db.c | 15 +++++++++-- erts/emulator/beam/erl_db_hash.c | 55 +++++++++++++++++++++++++--------------- erts/emulator/beam/erl_db_hash.h | 1 + 3 files changed, 48 insertions(+), 23 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 8f246ffa07..0d75dbbc04 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -2643,7 +2643,9 @@ BIF_RETTYPE ets_match_object_3(BIF_ALIST_3) BIF_RETTYPE ets_info_1(BIF_ALIST_1) { static Eterm fields[] = {am_protection, am_keypos, am_type, am_named_table, - am_node, am_size, am_name, am_heir, am_owner, am_memory, am_compressed}; + am_node, am_size, am_name, am_heir, am_owner, am_memory, am_compressed, + am_write_concurrency, + am_read_concurrency}; Eterm results[sizeof(fields)/sizeof(Eterm)]; DbTable* tb; Eterm res; @@ -3670,6 +3672,10 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What) ret = am_protected; else if (tb->common.status & DB_PUBLIC) ret = am_public; + } else if (What == am_write_concurrency) { + ret = tb->common.status & DB_FINE_LOCKED ? am_true : am_false; + } else if (What == am_read_concurrency) { + ret = tb->common.status & DB_FREQ_READ ? am_true : am_false; } else if (What == am_name) { ret = tb->common.the_name; } else if (What == am_keypos) { @@ -3752,7 +3758,7 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What) avg, std_dev_real, std_dev_exp, make_small(stats.min_chain_len), make_small(stats.max_chain_len), - make_small(db_kept_items_hash(&tb->hash))); + make_small(stats.kept_items)); } else { ret = am_false; @@ -3774,6 +3780,11 @@ static void print_table(int to, void *to_arg, int show, DbTable* tb) + sizeof(Uint) - 1) / sizeof(Uint))); + erts_print(to, to_arg, "Type: %T\n", table_info(NULL, tb, am_type)); + erts_print(to, to_arg, "Protection: %T\n", table_info(NULL, tb, am_protection)); + erts_print(to, to_arg, "Compressed: %T\n", table_info(NULL, tb, am_compressed)); + erts_print(to, to_arg, "Write Concurrency: %T\n", table_info(NULL, tb, am_write_concurrency)); + erts_print(to, to_arg, "Read Concurrency: %T\n", table_info(NULL, tb, am_read_concurrency)); } void db_info(int to, void *to_arg, int show) /* Called by break handler */ diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 06dac8f161..82d1f894ae 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -646,25 +646,6 @@ restart: /* ToDo: Maybe try grow/shrink the table as well */ } -/* Only used by tests -*/ -Uint db_kept_items_hash(DbTableHash *tb) -{ - Uint kept_items = 0; - Uint ix = 0; - erts_smp_rwmtx_t* lck = RLOCK_HASH(tb,ix); - HashDbTerm* b; - do { - for (b = BUCKET(tb, ix); b != NULL; b = b->next) { - if (b->hvalue == INVALID_HASH) { - ++kept_items; - } - } - ix = next_slot(tb, ix, &lck); - }while (ix); - return kept_items; -} - int db_create_hash(Process *p, DbTable *tbl) { DbTableHash *tb = &tbl->hash; @@ -2104,10 +2085,38 @@ int db_mark_all_deleted_hash(DbTable *tbl) static void db_print_hash(int to, void *to_arg, int show, DbTable *tbl) { DbTableHash *tb = &tbl->hash; + DbHashStats stats; int i; erts_print(to, to_arg, "Buckets: %d\n", NACTIVE(tb)); - + +#ifdef ERTS_SMP + i = tbl->common.is_thread_safe; + /* If crash dumping we set table to thread safe in order to + avoid taking any locks */ + if (ERTS_IS_CRASH_DUMPING) + tbl->common.is_thread_safe = 1; + + db_calc_stats_hash(&tbl->hash, &stats); + + tbl->common.is_thread_safe = i; +#else + db_calc_stats_hash(&tbl->hash, &stats); +#endif + + erts_print(to, to_arg, "Chain Length Avg: %f\n", stats.avg_chain_len); + erts_print(to, to_arg, "Chain Length Max: %d\n", stats.max_chain_len); + erts_print(to, to_arg, "Chain Length Min: %d\n", stats.min_chain_len); + erts_print(to, to_arg, "Chain Length Std Dev: %f\n", + stats.std_dev_chain_len); + erts_print(to, to_arg, "Chain Length Expected Std Dev: %f\n", + stats.std_dev_expected); + + if (IS_FIXED(tb)) + erts_print(to, to_arg, "Fixed: %d\n", stats.kept_items); + else + erts_print(to, to_arg, "Fixed: false\n"); + if (show) { for (i = 0; i < NACTIVE(tb); i++) { HashDbTerm* list = BUCKET(tb,i); @@ -2833,6 +2842,7 @@ void db_calc_stats_hash(DbTableHash* tb, DbHashStats* stats) erts_smp_rwmtx_t* lck; int sum = 0; int sq_sum = 0; + int kept_items = 0; int ix; int len; @@ -2844,6 +2854,8 @@ void db_calc_stats_hash(DbTableHash* tb, DbHashStats* stats) len = 0; for (b = BUCKET(tb,ix); b!=NULL; b=b->next) { len++; + if (b->hvalue == INVALID_HASH) + ++kept_items; } sum += len; sq_sum += len*len; @@ -2855,7 +2867,8 @@ void db_calc_stats_hash(DbTableHash* tb, DbHashStats* stats) stats->std_dev_chain_len = sqrt((sq_sum - stats->avg_chain_len*sum) / NACTIVE(tb)); /* Expected standard deviation from a good uniform hash function, ie binomial distribution (not taking the linear hashing into acount) */ - stats->std_dev_expected = sqrt(stats->avg_chain_len * (1 - 1.0/NACTIVE(tb))); + stats->std_dev_expected = sqrt(stats->avg_chain_len * (1 - 1.0/NACTIVE(tb))); + stats->kept_items = kept_items; } #ifdef HARDDEBUG diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h index e68081a5b1..f12cd363b0 100644 --- a/erts/emulator/beam/erl_db_hash.h +++ b/erts/emulator/beam/erl_db_hash.h @@ -104,6 +104,7 @@ typedef struct { float std_dev_expected; int max_chain_len; int min_chain_len; + int kept_items; }DbHashStats; void db_calc_stats_hash(DbTableHash* tb, DbHashStats*); -- cgit v1.2.3 From f03bce6a77ff5c7885a3b200fe879210299194bb Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 4 Dec 2014 11:00:22 +0100 Subject: erts: Add support for thread names --- erts/aclocal.m4 | 51 ++++++++++++++++++++++++++++++++++++- erts/emulator/beam/erl_async.c | 13 +++------- erts/emulator/beam/erl_drv_thread.c | 7 ++--- erts/emulator/beam/erl_process.c | 31 +++++----------------- erts/emulator/beam/erl_threads.h | 11 ++++++++ erts/emulator/beam/erl_trace.c | 5 +--- erts/emulator/sys/unix/sys.c | 2 ++ erts/include/internal/ethread.h | 13 ++++------ erts/lib_src/pthread/ethread.c | 35 +++++++++++++++++++++++++ erts/lib_src/win/ethread.c | 13 ++++++++++ 10 files changed, 129 insertions(+), 52 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index d78025b0be..fc8c587d6f 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1349,7 +1349,56 @@ case "$THR_LIB_NAME" in AC_MSG_RESULT([$linux_futex]) test $linux_futex = yes && AC_DEFINE(ETHR_HAVE_LINUX_FUTEX, 1, [Define if you have a linux futex implementation.]) - fi + pthread_setname=no + AC_MSG_CHECKING([for pthread_setname_np]) + old_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -Werror" + AC_TRY_LINK([#define __USE_GNU + #include ], + [pthread_setname_np(pthread_self(), "name");], + pthread_setname=linux) + AC_TRY_LINK([#define __USE_GNU + #include ], + [pthread_set_name_np(pthread_self(), "name");], + pthread_setname=bsd) + AC_TRY_LINK([#define _DARWIN_C_SOURCE + #include ], + [pthread_setname_np("name");], + pthread_setname=darwin) + AC_MSG_RESULT([$pthread_setname]) + case $pthread_setname in + linux) AC_DEFINE(ETHR_HAVE_PTHREAD_SETNAME_NP_2, 1, + [Define if you have linux style pthread_setname_np]);; + bsd) AC_DEFINE(ETHR_HAVE_PTHREAD_SET_NAME_NP_2, 1, + [Define if you have bsd style pthread_set_name_np]);; + darwin) AC_DEFINE(ETHR_HAVE_PTHREAD_SETNAME_NP_1, 1, + [Define if you have darwin style pthread_setname_np]);; + *) ;; + esac + + pthread_getname=no + AC_MSG_CHECKING([for pthread_getname_np]) + AC_TRY_LINK([#define __USE_GNU + #define _DARWIN_C_SOURCE + #include ], + [char buff[256]; pthread_getname_np(pthread_self(), buff, 256);], + pthread_getname=normal) + AC_TRY_LINK([#define __USE_GNU + #define _DARWIN_C_SOURCE + #include ], + [char buff[256]; pthread_getname_np(pthread_self(), buff);], + pthread_getname=ibm) + AC_MSG_RESULT([$pthread_getname]) + case $pthread_getname in + linux) AC_DEFINE(ETHR_HAVE_PTHREAD_GETNAME_NP_3, 1, + [Define if you have linux style pthread_getname_np]);; + ibm) AC_DEFINE(ETHR_HAVE_PTHREAD_GETNAME_NP_2, 1, + [Define if you have ibm style pthread_getname_np]);; + *) ;; + esac + CFLAGS=$old_CFLAGS + + fi ## test "x$THR_LIB_NAME" = "xpthread" AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index decae6b2ca..bc06d41720 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -176,7 +176,7 @@ erts_init_async(void) ErtsThrQInit_t qinit = ERTS_THR_Q_INIT_DEFAULT; #endif erts_thr_opts_t thr_opts = ERTS_THR_OPTS_DEFAULT_INITER; - char *ptr; + char *ptr, thr_name[16]; size_t tot_size = 0; int i; @@ -227,23 +227,16 @@ erts_init_async(void) thr_opts.suggested_stack_size = erts_async_thread_suggested_stack_size; -#ifdef ETHR_HAVE_THREAD_NAMES - thr_opts.name = malloc(sizeof(char)*(strlen("async_XXXX")+1)); -#endif + thr_opts.name = thr_name; for (i = 0; i < erts_async_max_threads; i++) { ErtsAsyncQ *aq = async_q(i); -#ifdef ETHR_HAVE_THREAD_NAMES - sprintf(thr_opts.name, "async_%d", i+1); -#endif + erts_snprintf(thr_opts.name, 16, "async_%d", i+1); erts_thr_create(&aq->thr_id, async_main, (void*) aq, &thr_opts); } -#ifdef ETHR_HAVE_THREAD_NAMES - free(thr_opts.name); -#endif /* Wait for async threads to initialize... */ erts_mtx_lock(&async->init.data.mtx); diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index 147249f751..31b05d22af 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -601,17 +601,14 @@ erl_drv_thread_create(char *name, #ifdef USE_THREADS int res; struct ErlDrvTid_ *dtid; - ethr_thr_opts ethr_opts; + ethr_thr_opts ethr_opts = ETHR_THR_OPTS_DEFAULT_INITER; ethr_thr_opts *use_opts; - ethr_thr_opts def_ethr_opts = ETHR_THR_OPTS_DEFAULT_INITER; if (!opts) use_opts = NULL; else { - sys_memcpy((void *) ðr_opts, - (void *) &def_ethr_opts, - sizeof(ethr_thr_opts)); ethr_opts.suggested_stack_size = opts->suggested_stack_size; + ethr_opts.name = name; use_opts = ðr_opts; } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b0e0cf13f8..31dda21b47 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -7883,23 +7883,17 @@ erts_start_schedulers(void) Uint actual; Uint wanted = erts_no_schedulers; Uint wanted_no_schedulers = erts_no_schedulers; + char name[16]; ethr_thr_opts opts = ETHR_THR_OPTS_DEFAULT_INITER; opts.detached = 1; -#ifdef ETHR_HAVE_THREAD_NAMES - opts.name = malloc(80); - if (!opts.name) { - ERTS_INTERNAL_ERROR("malloc failed to allocate memory!"); - } -#endif + opts.name = name; #ifdef ERTS_SMP if (erts_runq_supervision_interval) { opts.suggested_stack_size = 16; -#ifdef ETHR_HAVE_THREAD_NAMES - sprintf(opts.name, "runq_supervisor"); -#endif + erts_snprintf(opts.name, 16, "runq_supervisor"); erts_atomic_init_nob(&runq_supervisor_sleeping, 0); if (0 != ethr_event_init(&runq_supervision_event)) erl_exit(1, "Failed to create run-queue supervision event\n"); @@ -7926,9 +7920,7 @@ erts_start_schedulers(void) ASSERT(actual == esdp->no - 1); -#ifdef ETHR_HAVE_THREAD_NAMES - sprintf(opts.name, "scheduler_%d", actual + 1); -#endif + erts_snprintf(opts.name, 16, "%lu_scheduler", actual + 1); #ifdef __OSE__ /* This should be done in the bind strategy */ @@ -7950,18 +7942,14 @@ erts_start_schedulers(void) int ix; for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { ErtsSchedulerData *esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); -#ifdef ETHR_HAVE_THREAD_NAMES - sprintf(opts.name,"dirty_cpu_scheduler_%d", ix + 1); -#endif + erts_snprintf(opts.name, 16, "%d_dirty_cpu_scheduler", ix + 1); res = ethr_thr_create(&esdp->tid,sched_dirty_cpu_thread_func,(void*)esdp,&opts); if (res != 0) erl_exit(1, "Failed to create dirty cpu scheduler thread %d\n", ix); } for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix); -#ifdef ETHR_HAVE_THREAD_NAMES - sprintf(opts.name,"dirty_io_scheduler_%d", ix + 1); -#endif + erts_snprintf(opts.name, 16, "%d_dirty_io_scheduler", ix + 1); res = ethr_thr_create(&esdp->tid,sched_dirty_io_thread_func,(void*)esdp,&opts); if (res != 0) erl_exit(1, "Failed to create dirty io scheduler thread %d\n", ix); @@ -7972,9 +7960,7 @@ erts_start_schedulers(void) ERTS_THR_MEMORY_BARRIER; -#ifdef ETHR_HAVE_THREAD_NAMES - sprintf(opts.name, "aux"); -#endif + erts_snprintf(opts.name, 16, "aux"); #ifdef __OSE__ opts.coreNo = 0; @@ -8000,9 +7986,6 @@ erts_start_schedulers(void) erts_send_error_to_logger_nogl(dsbufp); } -#ifdef ETHR_HAVE_THREAD_NAMES - free(opts.name); -#endif } #endif /* ERTS_SMP */ diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index 80026104db..1ab5418741 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -463,6 +463,7 @@ ERTS_GLB_INLINE void erts_thr_detach(erts_tid_t tid); ERTS_GLB_INLINE void erts_thr_exit(void *res); ERTS_GLB_INLINE void erts_thr_install_exit_handler(void (*exit_handler)(void)); ERTS_GLB_INLINE erts_tid_t erts_thr_self(void); +ERTS_GLB_INLINE int erts_thr_getname(erts_tid_t tid, char *buf, size_t len); ERTS_GLB_INLINE int erts_equal_tids(erts_tid_t x, erts_tid_t y); ERTS_GLB_INLINE void erts_mtx_init_x(erts_mtx_t *mtx, char *name, Eterm extra, int enable_lcnt); @@ -1548,6 +1549,16 @@ erts_thr_self(void) #endif } +ERTS_GLB_INLINE int +erts_thr_getname(erts_tid_t tid, char *buf, size_t len) +{ +#ifdef USE_THREADS + return ethr_getname(tid, buf, len); +#else + return -1; +#endif +} + ERTS_GLB_INLINE int erts_equal_tids(erts_tid_t x, erts_tid_t y) diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index ea5c850a30..15648d6056 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -3492,16 +3492,13 @@ init_sys_msg_dispatcher(void) thr_opts.coreNo = 0; #endif thr_opts.detached = 1; + thr_opts.name = "sys_msg_dispatcher"; init_smq_element_alloc(); sys_message_queue = NULL; sys_message_queue_end = NULL; erts_smp_cnd_init(&smq_cnd); erts_smp_mtx_init(&smq_mtx, "sys_msg_q"); -#ifdef ETHR_HAVE_THREAD_NAMES - thr_opts.name = "sys_msg_dispatcher"; -#endif - erts_smp_thr_create(&sys_msg_dispatcher_tid, sys_msg_dispatcher_func, NULL, diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 5de0c281c4..3320d3e314 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -1327,6 +1327,7 @@ static int spawn_init() thr_opts.detached = 0; thr_opts.suggested_stack_size = 0; /* Smallest possible */ + thr_opts.name = "child_waiter"; #endif sys_sigset(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */ @@ -3230,6 +3231,7 @@ init_smp_sig_notify(void) { erts_smp_thr_opts_t thr_opts = ERTS_SMP_THR_OPTS_DEFAULT_INITER; thr_opts.detached = 1; + thr_opts.name = "sys_sig_dispatcher"; if (pipe(sig_notify_fds) < 0) { erl_exit(ERTS_ABORT_EXIT, diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index ad5d05704c..0f466dbb00 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -214,8 +214,6 @@ typedef OSPPDKEY ethr_tsd_key; /* Out own RW mutexes are probably faster, but use OSEs mutexes */ #define ETHR_USE_OWN_RWMTX_IMPL__ -#define ETHR_HAVE_THREAD_NAMES - #else /* No supported thread lib found */ #ifdef ETHR_NO_SUPP_THR_LIB_NOT_FATAL @@ -501,21 +499,18 @@ typedef struct { typedef struct { int detached; /* boolean (default false) */ int suggested_stack_size; /* kilo words (default sys dependent) */ + char *name; /* max 14 char long (default no-name) */ #ifdef ETHR_OSE_THREADS - char *name; U32 coreNo; #endif } ethr_thr_opts; #if defined(ETHR_OSE_THREADS) -/* Default ethr name is big as we want to be able to sprint stuff in there */ -#define ETHR_THR_OPTS_DEFAULT_INITER \ - {0, -1, "ethread", 0} +#define ETHR_THR_OPTS_DEFAULT_INITER {0, -1, NULL, 0} #else -#define ETHR_THR_OPTS_DEFAULT_INITER {0, -1} +#define ETHR_THR_OPTS_DEFAULT_INITER {0, -1, NULL} #endif - #if !defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) # define ETHR_NEED_SPINLOCK_PROTOTYPES__ # define ETHR_NEED_RWSPINLOCK_PROTOTYPES__ @@ -529,6 +524,8 @@ int ethr_thr_join(ethr_tid, void **); int ethr_thr_detach(ethr_tid); void ethr_thr_exit(void *); ethr_tid ethr_self(void); +int ethr_getname(ethr_tid, char *, size_t); +void ethr_setname(char *); int ethr_equal_tids(ethr_tid, ethr_tid); int ethr_tsd_key_create(ethr_tsd_key *,char *); diff --git a/erts/lib_src/pthread/ethread.c b/erts/lib_src/pthread/ethread.c index 79784c5b84..340d085428 100644 --- a/erts/lib_src/pthread/ethread.c +++ b/erts/lib_src/pthread/ethread.c @@ -42,6 +42,7 @@ #include #include #include +#include #include @@ -77,6 +78,8 @@ typedef struct { void *(*thr_func)(void *); void *arg; void *prep_func_res; + char *name; + char name_buff[16]; } ethr_thr_wrap_data__; static void *thr_wrapper(void *vtwd) @@ -98,6 +101,8 @@ static void *thr_wrapper(void *vtwd) tsep = twd->tse; /* We aren't allowed to follow twd after result has been set! */ + if (twd->name) + ethr_setname(twd->name); ethr_atomic32_set(&twd->result, result); @@ -315,6 +320,12 @@ ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, twd.thr_func = func; twd.arg = arg; + if (opts && opts->name) { + snprintf(twd.name_buff, 16, "%s", opts->name); + twd.name = twd.name_buff; + } else + twd.name = NULL; + res = pthread_attr_init(&attr); if (res != 0) return res; @@ -444,6 +455,30 @@ ethr_self(void) return (ethr_tid) pthread_self(); } +int +ethr_getname(ethr_tid tid, char *buf, size_t len) +{ +#if defined(ETHR_HAVE_PTHREAD_GETNAME_NP_3) + return pthread_getname_np((pthread_t) tid, buf, len); +#elif defined(ETHR_HAVE_PTHREAD_GETNAME_NP_2) + return pthread_getname_np((pthread_t) tid, buf); +#else + return ENOSYS; +#endif +} + +void +ethr_setname(char *name) +{ +#if defined(ETHR_HAVE_PTHREAD_SETNAME_NP_2) + pthread_setname_np(ethr_self(), name); +#elif defined(ETHR_HAVE_PTHREAD_SET_NAME_NP_2) + pthread_set_name_np(ethr_self(), name); +#elif defined(ETHR_HAVE_PTHREAD_SETNAME_NP_1) + pthread_setname_np(name); +#endif +} + int ethr_equal_tids(ethr_tid tid1, ethr_tid tid2) { diff --git a/erts/lib_src/win/ethread.c b/erts/lib_src/win/ethread.c index 14d0b6deff..fe5d4a327f 100644 --- a/erts/lib_src/win/ethread.c +++ b/erts/lib_src/win/ethread.c @@ -508,6 +508,19 @@ ethr_self(void) return *tid; } +/* getname and setname are not available on windows */ +int +ethr_getname(ethr_tid tid, char *buf, size_t len) +{ + return ENOSYS; +} + +void +ethr_setname(char *name) +{ + return; +} + int ethr_equal_tids(ethr_tid tid1, ethr_tid tid2) { -- cgit v1.2.3 From d6d1ef4787951070dd152daf44eff2003b7f2ed2 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 9 Dec 2014 08:47:49 +0100 Subject: erts: Remove usage of QUANTIFY signal --- erts/emulator/sys/unix/sys.c | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 3320d3e314..81ed5626c2 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -894,21 +894,6 @@ static RETSIGTYPE user_signal1(int signum) #endif } -#ifdef QUANTIFY -#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL)) -static RETSIGTYPE user_signal2(void) -#else -static RETSIGTYPE user_signal2(int signum) -#endif -{ -#ifdef ERTS_SMP - smp_sig_notify('2'); -#else - quantify_save_data(); -#endif -} -#endif - #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ static void @@ -960,9 +945,6 @@ void init_break_handler(void) sys_sigset(SIGINT, request_break); #ifndef ETHR_UNUSABLE_SIGUSRX sys_sigset(SIGUSR1, user_signal1); -#ifdef QUANTIFY - sys_sigset(SIGUSR2, user_signal2); -#endif #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ sys_sigset(SIGQUIT, do_quit); } @@ -3207,13 +3189,6 @@ signal_dispatcher_thread_func(void *unused) case '1': /* SIGUSR1 */ sigusr1_exit(); break; -#ifdef QUANTIFY - case '2': /* SIGUSR2 */ - quantify_save_data(); /* Might take a substantial amount of - time, but this is a test/debug - build */ - break; -#endif default: erl_exit(ERTS_ABORT_EXIT, "signal-dispatcher thread received unknown " -- cgit v1.2.3 From b8a2313263d0f120dacbe82ced5c99545bbd15a3 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 4 Dec 2014 11:04:23 +0100 Subject: erts: Introduce thread suspend functions These functions allow any thread to suspend any other thread immediately and then resume all threads. This is useful when doing a crash dump in order to get a more accurate picture of what state the system is in. --- erts/emulator/beam/erl_threads.h | 15 +++++++++ erts/emulator/beam/sys.h | 5 +++ erts/emulator/sys/unix/sys.c | 69 +++++++++++++++++++++++++++++++++++++--- erts/include/internal/ethread.h | 1 + erts/lib_src/pthread/ethread.c | 11 +++++++ 5 files changed, 97 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index 1ab5418741..463271252d 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -621,11 +621,17 @@ ERTS_GLB_INLINE void erts_thr_set_main_status(int, int); ERTS_GLB_INLINE int erts_thr_get_main_status(void); ERTS_GLB_INLINE void erts_thr_yield(void); + #ifdef ETHR_HAVE_ETHR_SIG_FUNCS #define ERTS_THR_HAVE_SIG_FUNCS 1 ERTS_GLB_INLINE void erts_thr_sigmask(int how, const sigset_t *set, sigset_t *oset); ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig); + +#ifdef USE_THREADS +ERTS_GLB_INLINE void erts_thr_kill(erts_tid_t tid, int sig); +#endif + #endif /* #ifdef HAVE_ETHR_SIG_FUNCS */ #ifdef USE_THREADS @@ -2848,6 +2854,15 @@ ERTS_GLB_INLINE void erts_thr_yield(void) #ifdef ETHR_HAVE_ETHR_SIG_FUNCS +ERTS_GLB_INLINE void +erts_thr_kill(erts_tid_t tid, int sig) { +#ifdef USE_THREADS + int res = ethr_kill((ethr_tid)tid, sig); + if (res) + erts_thr_fatal_error(res, "killing thread"); +#endif +} + ERTS_GLB_INLINE void erts_thr_sigmask(int how, const sigset_t *set, sigset_t *oset) { diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index c29d4b3777..bdc5237815 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -784,6 +784,11 @@ int erts_sys_unsetenv(char *key); char *erts_read_env(char *key); void erts_free_read_env(void *value); +#if defined(ERTS_THR_HAVE_SIG_FUNCS) && !defined(ETHR_UNUSABLE_SIGUSRX) +extern void sys_thr_resume(erts_tid_t tid); +extern void sys_thr_suspend(erts_tid_t tid); +#endif + /* utils.c */ /* Options to sys_alloc_opt */ diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 81ed5626c2..841f0c7575 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -216,6 +216,9 @@ static erts_smp_atomic_t sys_misc_mem_sz; #if defined(ERTS_SMP) static void smp_sig_notify(char c); static int sig_notify_fds[2] = {-1, -1}; + +static int sig_suspend_fds[2] = {-1, -1}; +#define ERTS_SYS_SUSPEND_SIGNAL SIGUSR2 #elif defined(USE_THREADS) static int async_fd[2]; #endif @@ -789,6 +792,9 @@ prepare_crash_dump(int secs) /* We don't want to close the signal notification pipe... */ if (i == sig_notify_fds[0] || i == sig_notify_fds[1]) continue; + /* We don't want to close the signal syspend pipe... */ + if (i == sig_suspend_fds[0] || i == sig_suspend_fds[1]) + continue; #elif defined(USE_THREADS) /* We don't want to close the async notification pipe... */ if (i == async_fd[0] || i == async_fd[1]) @@ -877,9 +883,23 @@ sigusr1_exit(void) #ifdef ETHR_UNUSABLE_SIGUSRX #warning "Unusable SIGUSR1 & SIGUSR2. Disabling use of these signals" -#endif -#ifndef ETHR_UNUSABLE_SIGUSRX +#else + +#ifdef ERTS_SMP +void +sys_thr_suspend(erts_tid_t tid) { + erts_thr_kill(tid, ERTS_SYS_SUSPEND_SIGNAL); +} + +void +sys_thr_resume(erts_tid_t tid) { + int i = 0, res; + do { + res = write(sig_suspend_fds[1],&i,sizeof(i)); + } while (res < 0 && errno == EAGAIN); +} +#endif #if (defined(SIG_SIGSET) || defined(SIG_SIGNAL)) static RETSIGTYPE user_signal1(void) @@ -894,6 +914,21 @@ static RETSIGTYPE user_signal1(int signum) #endif } +#ifdef ERTS_SMP +#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL)) +static RETSIGTYPE suspend_signal(void) +#else +static RETSIGTYPE suspend_signal(int signum) +#endif +{ + int res; + int buf[1]; + do { + res = read(sig_suspend_fds[0], buf, sizeof(int)); + } while (res < 0 && errno == EINTR); +} +#endif /* #ifdef ERTS_SMP */ + #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ static void @@ -944,7 +979,10 @@ void init_break_handler(void) { sys_sigset(SIGINT, request_break); #ifndef ETHR_UNUSABLE_SIGUSRX - sys_sigset(SIGUSR1, user_signal1); + sys_signal(SIGUSR1, user_signal1); +#ifdef ERTS_SMP + sys_signal(ERTS_SYS_SUSPEND_SIGNAL, suspend_signal); +#endif /* #ifdef ERTS_SMP */ #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ sys_sigset(SIGQUIT, do_quit); } @@ -963,8 +1001,13 @@ static void block_signals(void) sys_sigblock(SIGINT); #ifndef ETHR_UNUSABLE_SIGUSRX sys_sigblock(SIGUSR1); +#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ +#endif /* #ifndef ERTS_SMP */ + +#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX) + sys_sigblock(ERTS_SYS_SUSPEND_SIGNAL); #endif -#endif + } static void unblock_signals(void) @@ -978,8 +1021,14 @@ static void unblock_signals(void) #ifndef ETHR_UNUSABLE_SIGUSRX sys_sigrelease(SIGUSR1); #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ +#endif /* #ifndef ERTS_SMP */ + +#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX) + sys_sigrelease(ERTS_SYS_SUSPEND_SIGNAL); #endif + } + /************************** Time stuff **************************/ #ifdef HAVE_GETHRTIME #ifdef GETHRTIME_WITH_CLOCK_GETTIME @@ -3221,6 +3270,17 @@ init_smp_sig_notify(void) NULL, &thr_opts); } + +static void +init_smp_sig_suspend(void) { + if (pipe(sig_suspend_fds) < 0) { + erl_exit(ERTS_ABORT_EXIT, + "Failed to create sig_suspend pipe: %s (%d)\n", + erl_errno_id(errno), + errno); + } +} + #ifdef __DARWIN__ int erts_darwin_main_thread_pipe[2]; @@ -3371,6 +3431,7 @@ erl_sys_args(int* argc, char** argv) #ifdef ERTS_SMP init_smp_sig_notify(); + init_smp_sig_suspend(); #endif /* Handled arguments have been marked with NULL. Slide arguments diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 0f466dbb00..0d9a4a4305 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -537,6 +537,7 @@ void *ethr_tsd_get(ethr_tsd_key); #include int ethr_sigmask(int how, const sigset_t *set, sigset_t *oset); int ethr_sigwait(const sigset_t *set, int *sig); +int ethr_kill(const ethr_tid tid, const int sig); #endif void ethr_compiler_barrier(void); diff --git a/erts/lib_src/pthread/ethread.c b/erts/lib_src/pthread/ethread.c index 340d085428..7cf38580c5 100644 --- a/erts/lib_src/pthread/ethread.c +++ b/erts/lib_src/pthread/ethread.c @@ -600,6 +600,17 @@ int ethr_sigwait(const sigset_t *set, int *sig) return 0; } +int ethr_kill(const ethr_tid tid, const int sig) +{ +#if ETHR_XCHK + if (ethr_not_inited__) { + ETHR_ASSERT(0); + return EACCES; + } +#endif + return pthread_kill((const pthread_t)tid, sig); +} + #endif /* #if ETHR_HAVE_ETHR_SIG_FUNCS */ ETHR_IMPL_NORETURN__ -- cgit v1.2.3 From e2a600341324d2ce3689119f8fd61b248702d79f Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 5 Dec 2014 10:14:51 +0100 Subject: erts: Rename sys_sigset to sys_signal Also removed old legacy fallback that is no longer used --- erts/emulator/drivers/unix/ttsl_drv.c | 12 +++---- erts/emulator/sys/unix/erl_unix_sys.h | 9 ++--- erts/emulator/sys/unix/sys.c | 65 +++++------------------------------ erts/emulator/sys/unix/sys_float.c | 4 +-- 4 files changed, 18 insertions(+), 72 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index be2fee1f25..a5960716f2 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -338,8 +338,8 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) } #endif DEBUGLOG(("utf8_mode is %s\n",(utf8_mode) ? "on" : "off")); - sys_sigset(SIGCONT, cont); - sys_sigset(SIGWINCH, winch); + sys_signal(SIGCONT, cont); + sys_signal(SIGWINCH, winch); driver_select(port, (ErlDrvEvent)(UWord)ttysl_fd, ERL_DRV_READ|ERL_DRV_USE, 1); ttysl_port = port; @@ -423,8 +423,8 @@ static void ttysl_stop(ErlDrvData ttysl_data) tty_reset(ttysl_fd); driver_select(ttysl_port, (ErlDrvEvent)(UWord)ttysl_fd, ERL_DRV_WRITE|ERL_DRV_READ|ERL_DRV_USE, 0); - sys_sigset(SIGCONT, SIG_DFL); - sys_sigset(SIGWINCH, SIG_DFL); + sys_signal(SIGCONT, SIG_DFL); + sys_signal(SIGWINCH, SIG_DFL); } ttysl_port = (ErlDrvPort)-1; ttysl_fd = -1; @@ -1458,11 +1458,11 @@ static RETSIGTYPE suspend(int sig) exit(1); } - sys_sigset(sig, SIG_DFL); /* Set signal handler to default */ + sys_signal(sig, SIG_DFL); /* Set signal handler to default */ sys_sigrelease(sig); /* Allow 'sig' to come through */ kill(getpid(), sig); /* Send ourselves the signal */ sys_sigblock(sig); /* Reset to old mask */ - sys_sigset(sig, suspend); /* Reset signal handler */ + sys_signal(sig, suspend); /* Reset signal handler */ if (tty_set(ttysl_fd) < 0) { fprintf(stderr,"Can't set tty raw \n"); diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 26ed2fb558..bc55fab8fb 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -211,13 +211,8 @@ int sys_stop_hrvtime(void); #define SYS_CLOCK_RESOLUTION 1 /* These are defined in sys.c */ -#if defined(SIG_SIGSET) /* Old SysV */ -RETSIGTYPE (*sys_sigset())(); -#elif defined(SIG_SIGNAL) /* Old BSD */ -RETSIGTYPE (*sys_sigset())(); -#else -RETSIGTYPE (*sys_sigset(int, RETSIGTYPE (*func)(int)))(int); -#endif +typedef void (*SIGFUNC)(int); +extern SIGFUNC sys_signal(int, SIGFUNC); extern void sys_sigrelease(int); extern void sys_sigblock(int); extern void sys_stop_cat(void); diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 841f0c7575..74d67cbd57 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -649,39 +649,7 @@ erl_sys_init(void) /* signal handling */ -#ifdef SIG_SIGSET /* Old SysV */ -RETSIGTYPE (*sys_sigset(sig, func))() -int sig; -RETSIGTYPE (*func)(); -{ - return(sigset(sig, func)); -} -void sys_sigblock(int sig) -{ - sighold(sig); -} -void sys_sigrelease(int sig) -{ - sigrelse(sig); -} -#else /* !SIG_SIGSET */ -#ifdef SIG_SIGNAL /* Old BSD */ -RETSIGTYPE (*sys_sigset(sig, func))(int, int) -int sig; -RETSIGTYPE (*func)(); -{ - return(signal(sig, func)); -} -sys_sigblock(int sig) -{ - sigblock(sig); -} -sys_sigrelease(int sig) -{ - sigsetmask(sigblock(0) & ~sigmask(sig)); -} -#else /* !SIG_SIGNAL */ /* The True Way - POSIX!:-) */ -RETSIGTYPE (*sys_sigset(int sig, RETSIGTYPE (*func)(int)))(int) +SIGFUNC sys_signal(int sig, SIGFUNC func) { struct sigaction act, oact; @@ -714,23 +682,6 @@ void sys_sigrelease(int sig) sigaddset(&mask, sig); sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL); } -#endif /* !SIG_SIGNAL */ -#endif /* !SIG_SIGSET */ - -#if (0) /* not used? -- gordon */ -static void (*break_func)(); -static RETSIGTYPE break_handler(int sig) -{ -#ifdef QNX - /* Turn off SIGCHLD during break processing */ - sys_sigblock(SIGCHLD); -#endif - (*break_func)(); -#ifdef QNX - sys_sigrelease(SIGCHLD); -#endif -} -#endif /* 0 */ static ERTS_INLINE int prepare_crash_dump(int secs) @@ -952,9 +903,9 @@ static RETSIGTYPE do_quit(int signum) /* Disable break */ void erts_set_ignore_break(void) { - sys_sigset(SIGINT, SIG_IGN); - sys_sigset(SIGQUIT, SIG_IGN); - sys_sigset(SIGTSTP, SIG_IGN); + sys_signal(SIGINT, SIG_IGN); + sys_signal(SIGQUIT, SIG_IGN); + sys_signal(SIGTSTP, SIG_IGN); } /* Don't use ctrl-c for break handler but let it be @@ -977,14 +928,14 @@ void erts_replace_intr(void) { void init_break_handler(void) { - sys_sigset(SIGINT, request_break); + sys_signal(SIGINT, request_break); #ifndef ETHR_UNUSABLE_SIGUSRX sys_signal(SIGUSR1, user_signal1); #ifdef ERTS_SMP sys_signal(ERTS_SYS_SUSPEND_SIGNAL, suspend_signal); #endif /* #ifdef ERTS_SMP */ #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ - sys_sigset(SIGQUIT, do_quit); + sys_signal(SIGQUIT, do_quit); } int sys_max_files(void) @@ -1361,7 +1312,7 @@ static int spawn_init() thr_opts.name = "child_waiter"; #endif - sys_sigset(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */ + sys_signal(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */ driver_data = (struct driver_data *) erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data)); erts_smp_atomic_add_nob(&sys_misc_mem_sz, @@ -1374,7 +1325,7 @@ static int spawn_init() sys_sigblock(SIGCHLD); #endif - sys_sigset(SIGCHLD, onchld); /* Reap children */ + sys_signal(SIGCHLD, onchld); /* Reap children */ #if CHLDWTHR erts_thr_create(&child_waiter_tid, child_waiter, NULL, &thr_opts); diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c index cafeab547e..2ffa649767 100644 --- a/erts/emulator/sys/unix/sys_float.c +++ b/erts/emulator/sys/unix/sys_float.c @@ -32,7 +32,7 @@ void erts_sys_init_float(void) { # ifdef SIGFPE - sys_sigset(SIGFPE, SIG_IGN); /* Ignore so we can test for NaN and Inf */ + sys_signal(SIGFPE, SIG_IGN); /* Ignore so we can test for NaN and Inf */ # endif } @@ -667,7 +667,7 @@ static void fpe_sig_handler(int sig) static void erts_thread_catch_fp_exceptions(void) { - sys_sigset(SIGFPE, fpe_sig_handler); + sys_signal(SIGFPE, fpe_sig_handler); unmask_fpe(); } -- cgit v1.2.3 From ffd0153e6dffcc29cf79d0191860047dba0438bb Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 4 Dec 2014 11:09:06 +0100 Subject: erts: Improve crash dumps This commit improves crash dumps in several ways: * Suspends schedulers to get a current snapshot * Dumps information about scheduler * Dumps stack trace of current running process (including Garbing processes) --- erts/doc/src/crash_dump.xml | 88 +++++++++++++- erts/emulator/beam/break.c | 128 +++++++++++++++----- erts/emulator/beam/erl_bif_info.c | 7 ++ erts/emulator/beam/erl_process.c | 215 +++++++++++++++++++++++++++++++++- erts/emulator/beam/erl_process.h | 13 ++ erts/emulator/beam/erl_process_dump.c | 174 ++++++++++++++++++++++++++- erts/emulator/beam/erl_thr_progress.c | 42 +++---- erts/emulator/beam/erl_thr_progress.h | 4 +- erts/emulator/beam/sys.h | 2 + erts/emulator/sys/unix/erl_unix_sys.h | 26 +++- erts/emulator/sys/unix/sys.c | 31 +++++ erts/emulator/sys/win32/erl_win_sys.h | 12 ++ erts/emulator/sys/win32/sys.c | 21 ++++ erts/emulator/test/bif_SUITE.erl | 33 +++++- 14 files changed, 731 insertions(+), 65 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index 2b5fc877c3..8291bf38b7 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -55,10 +55,12 @@ emulator or the operating system can be reconfigured to avoid the crash, which is why interpreting the crash dump correctly is important.

+

On systems that support OS signals, it is also possible to stop + the runtime system and generate a crash dump by sending the SIGUSR1.

The erlang crash dump is a readable text file, but it might not be very easy to read. Using the Crashdump Viewer tool in the application will simplify the task. This is an - HTML based tool for browsing Erlang crash dumps.

+ wx-widget based tool for browsing Erlang crash dumps.

@@ -66,8 +68,9 @@

The first part of the dump shows the creation time for the dump, a slogan indicating the reason for the dump, the system version, of the node from which the dump originates, the compile time of - the emulator running the originating node and the number of - atoms in the atom table. + the emulator running the originating node, the number of + atoms in the atom table and the runtime system thread that caused + the crash dump to happen.

@@ -169,6 +172,60 @@
+
+ + Scheduler information +

Under the tag =scheduler information about the current state + and statistics of the schedulers in the runtime system is displayed. + On OSs that do allow instant suspension of other threads, the data within + this section will reflect what the runtime system looks like at the moment + when the crash happens.

+

The following fields can exist for a process:

+ + =scheduler:id + Header, states the scheduler identifier. + Scheduler Sleep Info Flags + If empty the scheduler was doing some work. + If not empty the scheduler is either in some state of sleep, + or suspended. This entry is only present in a SMP enabled emulator + Scheduler Sleep Info Aux Work + If not empty, a scheduler internal auxiliary work is scheduled + to be done. + Current Port + The port identifier of the port that is currently being + executed by the scheduler. + Current Process + The process identifier of the process that is currently being + executed by the scheduler. If there is such a process this entry is + followed by the State,Internal State, + Program Counter, CP of that same process. See + Process Information for a + description what the different entries mean. Keep in mind that + this is a snapshot of what the entries are exactly when the crash + dump is starting to be generated. Therefore they will most likely + be different (and more telling) then the entries for the same + processes found in the =proc section. If there is no currently + running process, only the Current Process entry will be printed. + + Current Process Limited Stack Trace + This entry only shows up if there is a current process. It is very + similar to =proc_stack, + except that only the function frames are printed (i.e. the stack variables + are omited). It is also limited to only print the top and bottom part + of the stack. If the stack is small (less that 512 slots) then the + entire stack will be printed. If not, an entry stating + skipping ## slots will be printed where ## is + replaced by the number of slots that has been skipped. + Run Queue + Displays statistics about how many processes and ports + of different priorities are scheduled on this scheduler. + ** crashed ** + This entry is normally not printed. It signifies that getting + the rest of the information about this scheduler failed for some reason. + + +
+
Memory information @@ -314,6 +371,9 @@ The number of live argument registers. The argument registers, if any are live, will follow. These may contain the arguments of the function if they are not yet moved to the stack. + Internal State + A more detailed internal represantation of the state of + this process.

See also the section about process data.

@@ -339,18 +399,38 @@ Name The name of the table, regardless of whether it is a or not. - Buckets + Hash table, Buckets This occurs if the table is a hash table, i.e. if it is not an . + Hash table, Chain Length + Only applicable for hash tables. Contains statistics about the + hash table, such as the max, min and avg chain length. Having a max much + larger than the avg, and a std dev much larger that + the expected std dev is a sign that the hashing of the terms is + behaving badly for some reason. Ordered set (AVL tree), Elements This occurs only if the table is an . (The number of elements is the same as the number of objects in the table.) + Fixed + If the table is fixed using ets:safe_fixtable or some internal + mechanism. Objects The number of objects in the table Words The number of words (usually 4 bytes/word) allocated to data in the table. + Type + The type of the table, i.e. set, bag, + dublicate_bag or ordered_set. + Compressed + If this table was compressed. + Protection + The protection of this table. + Write Concurrency + If write_concurrency was enabled for this table. + Read Concurrency + If read_concurrency was enabled for this table.
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 5aee85174f..41765dad12 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -210,25 +210,12 @@ print_process_info(int to, void *to_arg, Process *p) erts_print(to, to_arg, "State: "); state = erts_smp_atomic32_read_acqb(&p->state); - if (state & ERTS_PSFLG_FREE) - erts_print(to, to_arg, "Non Existing\n"); /* Should never happen */ - else if (state & ERTS_PSFLG_EXITING) - erts_print(to, to_arg, "Exiting\n"); - else if (state & ERTS_PSFLG_GC) { - garbing = 1; - running = 1; - 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"); + erts_dump_process_state(to, to_arg, state); + if (state & ERTS_PSFLG_GC) { + garbing = 1; + running = 1; + } else if (state & ERTS_PSFLG_RUNNING) + running = 1; /* * If the process is registered as a global process, display the @@ -352,6 +339,10 @@ print_process_info(int to, void *to_arg, Process *p) #endif erts_stack_dump(to, to_arg, p); } + + /* Display all states */ + erts_print(to, to_arg, "Internal State: "); + erts_dump_extended_process_state(to, to_arg, state); } static void @@ -671,6 +662,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) { #ifdef ERTS_SMP ErtsThrPrgrData tpd_buf; /* in case we aren't a managed thread... */ + int bc; #endif int fd; size_t envsz; @@ -681,27 +673,39 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) char* dumpname; int secs; int env_erl_crash_dump_seconds_set = 1; + int i; if (ERTS_SOMEONE_IS_CRASH_DUMPING) return; #ifdef ERTS_SMP + /* Order all managed threads to block, this has to be done + first to guarantee that this is the only thread to generate + crash dump. */ + bc = erts_thr_progress_fatal_error_block(&tpd_buf); + +#ifdef ERTS_THR_HAVE_SIG_FUNCS /* - * Wait for all managed threads to block. If all threads haven't blocked - * after a minute, we go anyway and hope for the best... - * - * We do not release system again. We expect an exit() or abort() after - * dump has been written. + * We suspend all scheduler threads so that we can dump some + * data about the currently running processes and scheduler data. + * We have to be very very careful when doing this as the schedulers + * could be anywhere. */ - erts_thr_progress_fatal_error_block(60000, &tpd_buf); - /* Either worked or not... */ + for (i = 0; i < erts_no_schedulers; i++) { + erts_tid_t tid = ERTS_SCHEDULER_IX(i)->tid; + if (!erts_equal_tids(tid,erts_thr_self())) + sys_thr_suspend(tid); + } + +#endif /* Allow us to pass certain places without locking... */ erts_smp_atomic32_set_mb(&erts_writing_erl_crash_dump, 1); erts_smp_tsd_set(erts_is_crash_dumping_key, (void *) 1); -#else + +#else /* !ERTS_SMP */ erts_writing_erl_crash_dump = 1; -#endif +#endif /* ERTS_SMP */ envsz = sizeof(env); /* ERL_CRASH_DUMP_SECONDS not set @@ -758,9 +762,8 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) erts_fprintf(stderr,"\nCrash dump is being written to: %s...", dumpname); fd = open(dumpname,O_WRONLY | O_CREAT | O_TRUNC,0640); - if (fd < 0) + if (fd < 0) return; /* Can't create the crash dump, skip it */ - time(&now); erts_fdprintf(fd, "=erl_crash_dump:0.3\n%s", ctime(&now)); @@ -774,9 +777,74 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) erts_fdprintf(fd, "System version: "); erts_print_system_version(fd, NULL, NULL); erts_fdprintf(fd, "%s\n", "Compiled: " ERLANG_COMPILE_DATE); + erts_fdprintf(fd, "Taints: "); erts_print_nif_taints(fd, NULL); erts_fdprintf(fd, "Atoms: %d\n", atom_table_size()); + +#ifdef USE_THREADS + /* We want to note which thread it was that called erl_exit */ + if (erts_get_scheduler_data()) { + erts_fdprintf(fd, "Calling Thread: scheduler:%d\n", + erts_get_scheduler_data()->no); + } else { + if (!erts_thr_getname(erts_thr_self(), dumpnamebuf, MAXPATHLEN)) + erts_fdprintf(fd, "Calling Thread: %s\n", dumpnamebuf); + else + erts_fdprintf(fd, "Calling Thread: %p\n", erts_thr_self()); + } +#else + erts_fdprintf(fd, "Calling Thread: scheduler:1\n"); +#endif + +#if defined(ERTS_HAVE_TRY_CATCH) + + /* + * erts_print_scheduler_info is not guaranteed to be safe to call + * here for all schedulers as we may have suspended a scheduler + * in the middle of updating the STACK_TOP and STACK_START + * variables and thus when scanning the stack we could get + * segmentation faults. We protect against this very unlikely + * scenario by using the ERTS_SYS_TRY_CATCH. + */ + for (i = 0; i < erts_no_schedulers; i++) { + ERTS_SYS_TRY_CATCH( + erts_print_scheduler_info(fd, NULL, ERTS_SCHEDULER_IX(i)), + erts_fdprintf(fd, "** crashed **\n")); + } +#endif + +#ifdef ERTS_SMP + +#if defined(ERTS_THR_HAVE_SIG_FUNCS) + + /* We resume all schedulers so that we are in a known safe state + when we write the rest of the crash dump */ + for (i = 0; i < erts_no_schedulers; i++) { + erts_tid_t tid = ERTS_SCHEDULER_IX(i)->tid; + if (!erts_equal_tids(tid,erts_thr_self())) + sys_thr_resume(tid); + } +#endif + + /* + * Wait for all managed threads to block. If all threads haven't blocked + * after a minute, we go anyway and hope for the best... + * + * We do not release system again. We expect an exit() or abort() after + * dump has been written. + */ + erts_thr_progress_fatal_error_wait(60000); + /* Either worked or not... */ +#endif + +#ifndef ERTS_HAVE_TRY_CATCH + /* This is safe to call here, as all schedulers are blocked */ + for (i = 0; i < erts_no_schedulers; i++) { + erts_print_scheduler_info(fd, NULL, ERTS_SCHEDULER_IX(i)); + } +#endif + info(fd, NULL); /* General system info */ if (erts_ptab_initialized(&erts_proc)) process_info(fd, NULL); /* Info about each process and port */ diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index d2ee5e4224..d750e34be3 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3900,6 +3900,13 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) } } } + else if (ERTS_IS_ATOM_STR("broken_halt", BIF_ARG_1)) { + /* Ugly ugly code used by bif_SUITE:erlang_halt/1 */ +#if defined(ERTS_HAVE_TRY_CATCH) + erts_get_scheduler_data()->run_queue = NULL; +#endif + erl_exit(ERTS_DUMP_EXIT, "%T", BIF_ARG_2); + } } BIF_ERROR(BIF_P, BADARG); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 31dda21b47..1ec931e312 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -457,8 +457,7 @@ do { \ static void exec_misc_ops(ErtsRunQueue *); static void print_function_from_pc(int to, void *to_arg, BeamInstr* x); -static int stack_element_dump(int to, void *to_arg, Process* p, Eterm* sp, - int yreg); +static int stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg); static void aux_work_timeout(void *unused); static void aux_work_timeout_early_init(int no_schedulers); @@ -12186,7 +12185,7 @@ erts_stack_dump(int to, void *to_arg, Process *p) } erts_program_counter_info(to, to_arg, p); for (sp = p->stop; sp < STACK_START(p); sp++) { - yreg = stack_element_dump(to, to_arg, p, sp, yreg); + yreg = stack_element_dump(to, to_arg, sp, yreg); } } @@ -12243,7 +12242,7 @@ print_function_from_pc(int to, void *to_arg, BeamInstr* x) } static int -stack_element_dump(int to, void *to_arg, Process* p, Eterm* sp, int yreg) +stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg) { Eterm x = *sp; @@ -12271,6 +12270,214 @@ stack_element_dump(int to, void *to_arg, Process* p, Eterm* sp, int yreg) return yreg; } +/* + * Print scheduler information + */ +void +erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp) { + int i; + erts_aint32_t flg; + Process *p; + + erts_print(to, to_arg, "=scheduler:%u\n", esdp->no); + +#ifdef ERTS_SMP + flg = erts_smp_atomic32_read_dirty(&esdp->ssi->flags); + erts_print(to, to_arg, "Scheduler Sleep Info Flags: "); + for (i = 0; i < ERTS_SSI_FLGS_MAX && flg; i++) { + erts_aint32_t chk = (1 << i); + if (flg & chk) { + switch (chk) { + case ERTS_SSI_FLG_SLEEPING: + erts_print(to, to_arg, "SLEEPING"); break; + case ERTS_SSI_FLG_POLL_SLEEPING: + erts_print(to, to_arg, "POLL_SLEEPING"); break; + case ERTS_SSI_FLG_TSE_SLEEPING: + erts_print(to, to_arg, "TSE_SLEEPING"); break; + case ERTS_SSI_FLG_WAITING: + erts_print(to, to_arg, "WAITING"); break; + case ERTS_SSI_FLG_SUSPENDED: + erts_print(to, to_arg, "SUSPENDED"); break; + default: + erts_print(to, to_arg, "UNKNOWN(%d)", flg); break; + } + if (flg > chk) + erts_print(to, to_arg, " | "); + flg -= chk; + } + } + erts_print(to, to_arg, "\n"); +#endif + + flg = erts_atomic32_read_dirty(&esdp->ssi->aux_work); + erts_print(to, to_arg, "Scheduler Sleep Info Aux Work: "); + for (i = 0; i < ERTS_SSI_AUX_WORK_MAX && flg; i++) { + erts_aint32_t chk = (1 << i); + if (flg & chk) { + switch (chk) { + case ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP: + erts_print(to, to_arg, "DELAYED_AW_WAKEUP"); break; + case ERTS_SSI_AUX_WORK_DD: + erts_print(to, to_arg, "DELAYED_DEALLOC"); break; + case ERTS_SSI_AUX_WORK_DD_THR_PRGR: + erts_print(to, to_arg, "DELAYED_DEALLOC_THR_PRGR"); break; + case ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC: + erts_print(to, to_arg, "FIX_ALLOC_DEALLOC"); break; + case ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM: + erts_print(to, to_arg, "FIX_ALLOC_LOWER_LIM"); break; + case ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP: + erts_print(to, to_arg, "THR_PRGR_LATER_OP"); break; + case ERTS_SSI_AUX_WORK_ASYNC_READY: + erts_print(to, to_arg, "ASYNC_READY"); break; + case ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN: + erts_print(to, to_arg, "ASYNC_READY_CLEAN"); break; + case ERTS_SSI_AUX_WORK_MISC_THR_PRGR: + erts_print(to, to_arg, "MISC_THR_PRGR"); break; + case ERTS_SSI_AUX_WORK_MISC: + erts_print(to, to_arg, "MISC"); break; + case ERTS_SSI_AUX_WORK_CHECK_CHILDREN: + erts_print(to, to_arg, "CHECK_CHILDREN"); break; + case ERTS_SSI_AUX_WORK_SET_TMO: + erts_print(to, to_arg, "SET_TMO"); break; + case ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK: + erts_print(to, to_arg, "MSEG_CACHE_CHECK"); break; + case ERTS_SSI_AUX_WORK_REAP_PORTS: + erts_print(to, to_arg, "REAP_PORTS"); break; + default: + erts_print(to, to_arg, "UNKNOWN(%d)", flg); break; + } + if (flg > chk) + erts_print(to, to_arg, " | "); + flg -= chk; + } + } + erts_print(to, to_arg, "\n"); + + erts_print(to, to_arg, "Current Port: "); + if (esdp->current_port) + erts_print(to, to_arg, "%T", esdp->current_port->common.id); + erts_print(to, to_arg, "\n"); + + p = esdp->current_process; + erts_print(to, to_arg, "Current Process: "); + if (esdp->current_process && !(ERTS_TRACE_FLAGS(p) & F_SENSITIVE)) { + flg = erts_smp_atomic32_read_dirty(&p->state); + erts_print(to, to_arg, "%T\n", p->common.id); + + erts_print(to, to_arg, "Current Process State: "); + erts_dump_process_state(to, to_arg, flg); + + erts_print(to, to_arg, "Current Process Internal State: "); + erts_dump_extended_process_state(to, to_arg, flg); + + erts_print(to, to_arg, "Current Process Program counter: %p (", p->i); + print_function_from_pc(to, to_arg, p->i); + erts_print(to, to_arg, ")\n"); + erts_print(to, to_arg, "Current Process CP: %p (", p->cp); + print_function_from_pc(to, to_arg, p->cp); + erts_print(to, to_arg, ")\n"); + + /* Getting this stacktrace can segfault if we are very very + unlucky if called while a process is being garbage collected. + Therefore we only call this on other schedulers if we either + have protection against segfaults, or we know that the process + is not garbage collecting. It *should* always be safe to call + on a process owned by us, even if it is currently being garbage + collected. + */ + erts_print(to, to_arg, "Current Process Limited Stack Trace:\n"); + erts_limited_stack_trace(to, to_arg, p); + } else + erts_print(to, to_arg, "\n"); + + for (i = 0; i < ERTS_NO_PROC_PRIO_LEVELS; i++) { + erts_print(to, to_arg, "Run Queue "); + switch (i) { + case PRIORITY_MAX: + erts_print(to, to_arg, "Max "); + break; + case PRIORITY_HIGH: + erts_print(to, to_arg, "High "); + break; + case PRIORITY_NORMAL: + erts_print(to, to_arg, "Normal "); + break; + case PRIORITY_LOW: + erts_print(to, to_arg, "Low "); + break; + default: + erts_print(to, to_arg, "Unknown "); + break; + } + erts_print(to, to_arg, "Length: %d\n", + erts_smp_atomic32_read_dirty(&esdp->run_queue->procs.prio_info[i].len)); + } + erts_print(to, to_arg, "Run Queue Port Length: %d\n", + erts_smp_atomic32_read_dirty(&esdp->run_queue->ports.info.len)); + + flg = erts_smp_atomic32_read_dirty(&esdp->run_queue->flags); + erts_print(to, to_arg, "Run Queue Flags: "); + for (i = 0; i < ERTS_RUNQ_FLG_MAX && flg; i++) { + erts_aint32_t chk = (1 << i); + if (flg & chk) { + switch (chk) { + case (1 << PRIORITY_MAX): + erts_print(to, to_arg, "NONEMPTY_MAX"); break; + case (1 << PRIORITY_HIGH): + erts_print(to, to_arg, "NONEMPTY_HIGH"); break; + case (1 << PRIORITY_NORMAL): + erts_print(to, to_arg, "NONEMPTY_NORMAL"); break; + case (1 << PRIORITY_LOW): + erts_print(to, to_arg, "NONEMPTY_LOW"); break; + case (1 << (PRIORITY_MAX + ERTS_RUNQ_FLGS_EMIGRATE_SHFT)): + erts_print(to, to_arg, "EMIGRATE_MAX"); break; + case (1 << (PRIORITY_HIGH + ERTS_RUNQ_FLGS_EMIGRATE_SHFT)): + erts_print(to, to_arg, "EMIGRATE_HIGH"); break; + case (1 << (PRIORITY_NORMAL + ERTS_RUNQ_FLGS_EMIGRATE_SHFT)): + erts_print(to, to_arg, "EMIGRATE_NORMAL"); break; + case (1 << (PRIORITY_LOW + ERTS_RUNQ_FLGS_EMIGRATE_SHFT)): + erts_print(to, to_arg, "EMIGRATE_LOW"); break; + case (1 << (PRIORITY_MAX + ERTS_RUNQ_FLGS_IMMIGRATE_SHFT)): + erts_print(to, to_arg, "IMMIGRATE_MAX"); break; + case (1 << (PRIORITY_HIGH + ERTS_RUNQ_FLGS_IMMIGRATE_SHFT)): + erts_print(to, to_arg, "IMMIGRATE_HIGH"); break; + case (1 << (PRIORITY_NORMAL + ERTS_RUNQ_FLGS_IMMIGRATE_SHFT)): + erts_print(to, to_arg, "IMMIGRATE_NORMAL"); break; + case (1 << (PRIORITY_LOW + ERTS_RUNQ_FLGS_IMMIGRATE_SHFT)): + erts_print(to, to_arg, "IMMIGRATE_LOW"); break; + case (1 << (PRIORITY_MAX + ERTS_RUNQ_FLGS_EVACUATE_SHFT)): + erts_print(to, to_arg, "EVACUATE_MAX"); break; + case (1 << (PRIORITY_HIGH + ERTS_RUNQ_FLGS_EVACUATE_SHFT)): + erts_print(to, to_arg, "EVACUATE_HIGH"); break; + case (1 << (PRIORITY_NORMAL + ERTS_RUNQ_FLGS_EVACUATE_SHFT)): + erts_print(to, to_arg, "EVACUATE_NORMAL"); break; + case (1 << (PRIORITY_LOW + ERTS_RUNQ_FLGS_EVACUATE_SHFT)): + erts_print(to, to_arg, "EVACUATE_LOW"); break; + case ERTS_RUNQ_FLG_OUT_OF_WORK: + erts_print(to, to_arg, "OUT_OF_WORK"); break; + case ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK: + erts_print(to, to_arg, "HALFTIME_OUT_OF_WORK"); break; + case ERTS_RUNQ_FLG_SUSPENDED: + erts_print(to, to_arg, "SUSPENDED"); break; + case ERTS_RUNQ_FLG_CHK_CPU_BIND: + erts_print(to, to_arg, "CHK_CPU_BIND"); break; + case ERTS_RUNQ_FLG_INACTIVE: + erts_print(to, to_arg, "INACTIVE"); break; + case ERTS_RUNQ_FLG_NONEMPTY: + erts_print(to, to_arg, "NONEMPTY"); break; + case ERTS_RUNQ_FLG_PROTECTED: + erts_print(to, to_arg, "PROTECTED"); break; + default: + erts_print(to, to_arg, "UNKNOWN(%d)", flg); break; + } + if (flg > chk) + erts_print(to, to_arg, " | "); + flg -= chk; + } + } + erts_print(to, to_arg, "\n"); +} + /* * A nice system halt closing all open port goes as follows: * 1) This function schedules the aux work ERTS_SSI_AUX_WORK_REAP_PORTS diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 3d08be25ff..a12b577656 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -170,6 +170,8 @@ extern int erts_sched_thread_suggested_stack_size; #define ERTS_RUNQ_FLG_PROTECTED \ (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 6)) +#define ERTS_RUNQ_FLG_MAX (ERTS_RUNQ_FLG_BASE2 + 7) + #define ERTS_RUNQ_FLGS_MIGRATION_QMASKS \ (ERTS_RUNQ_FLGS_EMIGRATE_QMASK \ | ERTS_RUNQ_FLGS_IMMIGRATE_QMASK \ @@ -252,6 +254,8 @@ typedef enum { #define ERTS_SSI_FLG_WAITING (((erts_aint32_t) 1) << 3) #define ERTS_SSI_FLG_SUSPENDED (((erts_aint32_t) 1) << 4) +#define ERTS_SSI_FLGS_MAX 5 + #define ERTS_SSI_FLGS_SLEEP_TYPE \ (ERTS_SSI_FLG_TSE_SLEEPING|ERTS_SSI_FLG_POLL_SLEEPING) @@ -283,6 +287,8 @@ typedef enum { #define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 12) #define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 13) +#define ERTS_SSI_AUX_WORK_MAX 14 + typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo; #ifdef ERTS_DIRTY_SCHEDULERS @@ -1081,6 +1087,9 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(19) #define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q ERTS_PSFLG_BIT(20) #define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q ERTS_PSFLG_BIT(21) +#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 22) +#else +#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 18) #endif #define ERTS_PSFLGS_IN_PRQ_MASK (ERTS_PSFLG_IN_PRQ_MAX \ @@ -1616,7 +1625,11 @@ void erts_cleanup_empty_process(Process* p); void erts_debug_verify_clean_empty_process(Process* p); #endif void erts_stack_dump(int to, void *to_arg, Process *); +void erts_limited_stack_trace(int to, void *to_arg, Process *); void erts_program_counter_info(int to, void *to_arg, Process *); +void erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp); +void erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg); +void erts_dump_process_state(int to, void *to_arg, erts_aint32_t psflg); Eterm erts_get_process_priority(Process *p); Eterm erts_set_process_priority(Process *p, Eterm prio); diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index 2f3cf23b00..36bb6b2f0e 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -43,8 +43,9 @@ static void dump_process_info(int to, void *to_arg, Process *p); static void dump_element(int to, void *to_arg, Eterm x); static void dump_dist_ext(int to, void *to_arg, ErtsDistExternal *edep); static void dump_element_nl(int to, void *to_arg, Eterm x); -static int stack_element_dump(int to, void *to_arg, Process* p, Eterm* sp, +static int stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg); +static void stack_trace_dump(int to, void *to_arg, Eterm* sp); static void print_function_from_pc(int to, void *to_arg, BeamInstr* x); static void heap_dump(int to, void *to_arg, Eterm x); static void dump_binaries(int to, void *to_arg, Binary* root); @@ -148,7 +149,7 @@ dump_process_info(int to, void *to_arg, Process *p) if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) { erts_print(to, to_arg, "=proc_stack:%T\n", p->common.id); for (sp = p->stop; sp < STACK_START(p); sp++) { - yreg = stack_element_dump(to, to_arg, p, sp, yreg); + yreg = stack_element_dump(to, to_arg, sp, yreg); } erts_print(to, to_arg, "=proc_heap:%T\n", p->common.id); @@ -243,9 +244,65 @@ dump_element_nl(int to, void *to_arg, Eterm x) erts_putc(to, to_arg, '\n'); } +static void +stack_trace_dump(int to, void *to_arg, Eterm *sp) { + Eterm x = *sp; + if (is_CP(x)) { + erts_print(to, to_arg, "%p:", sp); + erts_print(to, to_arg, "SReturn addr 0x%X (", cp_val(x)); + print_function_from_pc(to, to_arg, cp_val(x)); + erts_print(to, to_arg, ")\n"); + } +} + +void +erts_limited_stack_trace(int to, void *to_arg, Process *p) +{ + Eterm* sp; + + + if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE) { + return; + } + + if (STACK_START(p) < STACK_TOP(p)) { + return; + } + + if ((STACK_START(p) - STACK_TOP(p)) < 512) { + if (erts_sys_is_area_readable((char*)STACK_TOP(p), + (char*)STACK_START(p))) + for (sp = STACK_TOP(p); sp < STACK_START(p); sp++) + stack_trace_dump(to, to_arg, sp); + else + erts_print(to, to_arg, "Could not read from stack memory: %p - %p\n", + STACK_TOP(p), STACK_START(p)); + } else { + sp = STACK_TOP(p); + if (erts_sys_is_area_readable((char*)STACK_TOP(p), + (char*)(STACK_TOP(p) + 25))) + for (; sp < (STACK_TOP(p) + 256); sp++) + stack_trace_dump(to, to_arg, sp); + else + erts_print(to, to_arg, "Could not read from stack memory: %p - %p\n", + STACK_TOP(p), STACK_TOP(p) + 256); + + erts_print(to, to_arg, "%p: skipping %d frames\n", + sp, STACK_START(p) - STACK_TOP(p) - 512); + + if (erts_sys_is_area_readable((char*)(STACK_START(p) - 256), + (char*)STACK_START(p))) + for (sp = STACK_START(p) - 256; sp < STACK_START(p); sp++) + stack_trace_dump(to, to_arg, sp); + else + erts_print(to, to_arg, "Could not read from stack memory: %p - %p\n", + STACK_START(p) - 256, STACK_START(p)); + } + +} static int -stack_element_dump(int to, void *to_arg, Process* p, Eterm* sp, int yreg) +stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg) { Eterm x = *sp; @@ -508,3 +565,114 @@ dump_externally(int to, void *to_arg, Eterm term) erts_print(to, to_arg, "%02X", *s++); } } + +void erts_dump_process_state(int to, void *to_arg, erts_aint32_t psflg) { + if (psflg & ERTS_PSFLG_FREE) + erts_print(to, to_arg, "Non Existing\n"); /* Should never happen */ + else if (psflg & ERTS_PSFLG_EXITING) + erts_print(to, to_arg, "Exiting\n"); + else if (psflg & ERTS_PSFLG_GC) { + erts_print(to, to_arg, "Garbing\n"); + } + else if (psflg & ERTS_PSFLG_SUSPENDED) + erts_print(to, to_arg, "Suspended\n"); + else if (psflg & ERTS_PSFLG_RUNNING) { + erts_print(to, to_arg, "Running\n"); + } + else if (psflg & ERTS_PSFLG_ACTIVE) + erts_print(to, to_arg, "Scheduled\n"); + else + erts_print(to, to_arg, "Waiting\n"); +} + +void +erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg) { + + int i; + + switch (ERTS_PSFLGS_GET_ACT_PRIO(psflg)) { + case PRIORITY_MAX: erts_print(to, to_arg, "ACT_PRIO_MAX | "); break; + case PRIORITY_HIGH: erts_print(to, to_arg, "ACT_PRIO_HIGH | "); break; + case PRIORITY_NORMAL: erts_print(to, to_arg, "ACT_PRIO_NORMAL | "); break; + case PRIORITY_LOW: erts_print(to, to_arg, "ACT_PRIO_LOW | "); break; + } + switch (ERTS_PSFLGS_GET_USR_PRIO(psflg)) { + case PRIORITY_MAX: erts_print(to, to_arg, "USR_PRIO_MAX | "); break; + case PRIORITY_HIGH: erts_print(to, to_arg, "USR_PRIO_HIGH | "); break; + case PRIORITY_NORMAL: erts_print(to, to_arg, "USR_PRIO_NORMAL | "); break; + case PRIORITY_LOW: erts_print(to, to_arg, "USR_PRIO_LOW | "); break; + } + switch (ERTS_PSFLGS_GET_PRQ_PRIO(psflg)) { + case PRIORITY_MAX: erts_print(to, to_arg, "PRQ_PRIO_MAX"); break; + case PRIORITY_HIGH: erts_print(to, to_arg, "PRQ_PRIO_HIGH"); break; + case PRIORITY_NORMAL: erts_print(to, to_arg, "PRQ_PRIO_NORMAL"); break; + case PRIORITY_LOW: erts_print(to, to_arg, "PRQ_PRIO_LOW"); break; + } + + psflg &= ~(ERTS_PSFLGS_ACT_PRIO_MASK | + ERTS_PSFLGS_USR_PRIO_MASK | + ERTS_PSFLGS_PRQ_PRIO_MASK); + + if (psflg) + erts_print(to, to_arg, " | "); + + for (i = 0; i < ERTS_PSFLG_MAX && psflg; i++) { + erts_aint32_t chk = (1 << i); + if (psflg & chk) { + switch (chk) { + case ERTS_PSFLG_IN_PRQ_MAX: + erts_print(to, to_arg, "IN_PRQ_MAX"); break; + case ERTS_PSFLG_IN_PRQ_HIGH: + erts_print(to, to_arg, "IN_PRQ_HIGH"); break; + case ERTS_PSFLG_IN_PRQ_NORMAL: + erts_print(to, to_arg, "IN_PRQ_NORMAL"); break; + case ERTS_PSFLG_IN_PRQ_LOW: + erts_print(to, to_arg, "IN_PRQ_LOW"); break; + case ERTS_PSFLG_FREE: + erts_print(to, to_arg, "FREE"); break; + case ERTS_PSFLG_EXITING: + erts_print(to, to_arg, "EXITING"); break; + case ERTS_PSFLG_PENDING_EXIT: + erts_print(to, to_arg, "PENDING_EXIT"); break; + case ERTS_PSFLG_ACTIVE: + erts_print(to, to_arg, "ACTIVE"); break; + case ERTS_PSFLG_IN_RUNQ: + erts_print(to, to_arg, "IN_RUNQ"); break; + case ERTS_PSFLG_RUNNING: + erts_print(to, to_arg, "RUNNING"); break; + case ERTS_PSFLG_SUSPENDED: + erts_print(to, to_arg, "SUSPENDED"); break; + case ERTS_PSFLG_GC: + erts_print(to, to_arg, "GC"); break; + case ERTS_PSFLG_BOUND: + erts_print(to, to_arg, "BOUND"); break; + case ERTS_PSFLG_TRAP_EXIT: + erts_print(to, to_arg, "TRAP_EXIT"); break; + case ERTS_PSFLG_ACTIVE_SYS: + erts_print(to, to_arg, "ACTIVE_SYS"); break; + case ERTS_PSFLG_RUNNING_SYS: + erts_print(to, to_arg, "RUNNING_SYS"); break; + case ERTS_PSFLG_PROXY: + erts_print(to, to_arg, "PROXY"); break; + case ERTS_PSFLG_DELAYED_SYS: + erts_print(to, to_arg, "DELAYED_SYS"); break; +#ifdef ERTS_DIRTY_SCHEDULERS + case ERTS_PSFLG_DIRTY_CPU_PROC: + erts_print(to, to_arg, "DIRTY_CPU_PROC"); break; + case ERTS_PSFLG_DIRTY_IO_PROC: + erts_print(to, to_arg, "DIRTY_IO_PROC"); break; + case ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q: + erts_print(to, to_arg, "DIRTY_CPU_PROC_IN_Q"); break; + case ERTS_PSFLG_DIRTY_IO_PROC_IN_Q: + erts_print(to, to_arg, "DIRTY_IO_PROC_IN_Q"); break; +#endif + default: + erts_print(to, to_arg, "UNKNOWN(%d)", chk); break; + } + if (psflg > chk) + erts_print(to, to_arg, " | "); + psflg -= chk; + } + } + erts_print(to, to_arg, "\n"); +} diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 545a0343d0..3dca2aba10 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -1381,25 +1381,10 @@ erts_thr_progress_block(void) thr_progress_block(tmp_thr_prgr_data(NULL), 1); } -void -erts_thr_progress_fatal_error_block(SWord timeout, - ErtsThrPrgrData *tmp_tpd_bufp) +int +erts_thr_progress_fatal_error_block(ErtsThrPrgrData *tmp_tpd_bufp) { ErtsThrPrgrData *tpd = perhaps_thr_prgr_data(NULL); - erts_aint32_t bc; - SWord time_left = timeout; - SysTimeval to; - - /* - * Counting poll intervals may give us a too long timeout - * if cpu is busy. If we got tolerant time of day we use it - * to prevent this. - */ - if (!erts_disable_tolerant_timeofday) { - erts_get_timeval(&to); - to.tv_sec += timeout / 1000; - to.tv_sec += timeout % 1000; - } if (!tpd) { /* @@ -1412,9 +1397,26 @@ erts_thr_progress_fatal_error_block(SWord timeout, init_tmp_thr_prgr_data(tpd); } - bc = thr_progress_block(tpd, 0); - if (bc == 0) - return; /* Succefully blocked all managed threads */ + /* Returns number of threads that have not yes been blocked */ + return thr_progress_block(tpd, 0); +} + +void +erts_thr_progress_fatal_error_wait(SWord timeout) { + erts_aint32_t bc; + SWord time_left = timeout; + SysTimeval to; + + /* + * Counting poll intervals may give us a too long timeout + * if cpu is busy. If we got tolerant time of day we use it + * to prevent this. + */ + if (!erts_disable_tolerant_timeofday) { + erts_get_timeval(&to); + to.tv_sec += timeout / 1000; + to.tv_sec += timeout % 1000; + } while (1) { if (erts_milli_sleep(ERTS_THR_PRGR_FTL_ERR_BLCK_POLL_INTERVAL) == 0) diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h index 5f392944c2..85b4eeb0eb 100644 --- a/erts/emulator/beam/erl_thr_progress.h +++ b/erts/emulator/beam/erl_thr_progress.h @@ -83,8 +83,8 @@ typedef struct { ErtsThrPrgrLeaderState leader_state; } ErtsThrPrgrData; -void erts_thr_progress_fatal_error_block(SWord timeout, - ErtsThrPrgrData *tmp_tpd_bufp); +int erts_thr_progress_fatal_error_block(ErtsThrPrgrData *tmp_tpd_bufp); +void erts_thr_progress_fatal_error_wait(SWord timeout); #endif /* ERTS_SMP */ diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index bdc5237815..9227ce74a2 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -756,6 +756,8 @@ typedef struct { } ErtsCheckIoDebugInfo; int erts_check_io_debug(ErtsCheckIoDebugInfo *ip); +int erts_sys_is_area_readable(char *start, char *stop); + /* xxxP */ #define SYS_DEFAULT_FLOAT_DECIMALS 20 void init_sys_float(void); diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index bc55fab8fb..1932e97742 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -45,7 +45,7 @@ #include #include "erl_errno.h" #include - +#include #if HAVE_SYS_SOCKETIO_H # include @@ -349,4 +349,28 @@ extern int exit_async(void); #define ERTS_EXIT_AFTER_DUMP _exit +#if !defined(__APPLE__) && !defined(__MACH__) +/* Some OS X versions do not allow (ab)using signal handlers like this */ +#define ERTS_HAVE_TRY_CATCH 1 + +/* We try to simulate a try catch in C with the help of signal handlers. + * Only use this as a very last resort, as it is not very portable and + * quite unstable. It is also not thread safe, so make sure that only + * one thread can call this at a time! + */ +extern void erts_sys_sigsegv_handler(int); +extern jmp_buf erts_sys_sigsegv_jmp; +#define ERTS_SYS_TRY_CATCH(EXPR,CATCH) \ + do { \ + SIGFUNC prev_handler = sys_signal(SIGSEGV, \ + erts_sys_sigsegv_handler); \ + if (!setjmp(erts_sys_sigsegv_jmp)) { \ + EXPR; \ + } else { \ + CATCH; \ + } \ + sys_signal(SIGSEGV,prev_handler); \ + } while(0) +#endif + #endif /* #ifndef _ERL_UNIX_SYS_H */ diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 74d67cbd57..57f9fbef53 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -223,6 +223,8 @@ static int sig_suspend_fds[2] = {-1, -1}; static int async_fd[2]; #endif +jmp_buf erts_sys_sigsegv_jmp; + #if CHLDWTHR || defined(ERTS_SMP) erts_mtx_t chld_stat_mtx; #endif @@ -683,6 +685,35 @@ void sys_sigrelease(int sig) sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL); } +void erts_sys_sigsegv_handler(int signo) { + if (signo == SIGSEGV) { + longjmp(erts_sys_sigsegv_jmp, 1); + } +} + +/* + * Function returns 1 if we can read from all values in between + * start and stop. + */ +int +erts_sys_is_area_readable(char *start, char *stop) { + int fds[2]; + if (!pipe(fds)) { + /* We let write try to figure out if the pointers are readable */ + int res = write(fds[1], start, (char*)stop - (char*)start); + if (res == -1) { + close(fds[0]); + close(fds[1]); + return 0; + } + close(fds[0]); + close(fds[1]); + return 1; + } + return 0; + +} + static ERTS_INLINE int prepare_crash_dump(int secs) { diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 838f0c61eb..fde32c8684 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -236,4 +236,16 @@ typedef long ssize_t; int init_async(int); int exit_async(void); #endif + +#define ERTS_HAVE_TRY_CATCH 1 + +#define ERTS_SYS_TRY_CATCH(EXPR,CATCH) \ + __try { \ + EXPR; \ + } \ + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) \ + { \ + CATCH; \ + } + #endif diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 164ef95629..5d51659b4e 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -247,6 +247,27 @@ void erl_sys_args(int* argc, char** argv) #endif } +/* + * Function returns 1 if we can read from all values in between + * start and stop. + */ +int +erts_sys_is_area_readable(char *start, char *stop) { + volatile char tmp; + __try + { + while(start < stop) { + tmp = *start; + start++; + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + return 0; + } + return 1; +} + int erts_sys_prepare_crash_dump(int secs) { Port *heart_port; diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index fbc229bc53..fc9bdae0a0 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -20,6 +20,7 @@ -module(bif_SUITE). -include_lib("test_server/include/test_server.hrl"). +-include_lib("kernel/include/file.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, @@ -681,8 +682,38 @@ erlang_halt(Config) when is_list(Config) -> {badrpc,nodedown} = rpc:call(N2, erlang, halt, [0]), {ok,N3} = slave:start(H, halt_node3), {badrpc,nodedown} = rpc:call(N3, erlang, halt, [0,[]]), - ok. + % This test triggers a segfault when dumping a crash dump + % to make sure that we can handle it properly. + {ok,N4} = slave:start(H, halt_node4), + CrashDump = filename:join(proplists:get_value(priv_dir,Config), + "segfault_erl_crash.dump"), + true = rpc:call(N4, os, putenv, ["ERL_CRASH_DUMP",CrashDump]), + false = rpc:call(N4, erts_debug, set_internal_state, + [available_internal_state, true]), + {badrpc,nodedown} = rpc:call(N4, erts_debug, set_internal_state, + [broken_halt, "Validate correct crash dump"]), + ok = wait_until_stable_size(CrashDump,-1), + {ok, Bin} = file:read_file(CrashDump), + case {string:str(binary_to_list(Bin),"\n=end\n"), + string:str(binary_to_list(Bin),"\r\n=end\r\n")} of + {0,0} -> ct:fail("Could not find end marker in crash dump"); + _ -> ok + end. + +wait_until_stable_size(_File,-10) -> + {error,enoent}; +wait_until_stable_size(File,PrevSz) -> + timer:sleep(250), + case file:read_file_info(File) of + {error,enoent} -> + wait_until_stable_size(File,PrevSz-1); + {ok,#file_info{size = PrevSz }} when PrevSz /= -1 -> + io:format("Crashdump file size was: ~p (~s)~n",[PrevSz,File]), + ok; + {ok,#file_info{size = NewSz }} -> + wait_until_stable_size(File,NewSz) + end. %% Helpers -- cgit v1.2.3 From bd4b900c09308a6c3ab41e3a2070cb90410ee01d Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 12 Dec 2014 15:13:31 +0100 Subject: erts: Make main thread safe from pipe closed event --- erts/emulator/sys/unix/sys.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 57f9fbef53..e1a932fdda 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -3290,9 +3290,11 @@ erts_sys_main_thread(void) #endif smp_sig_notify(0); /* Notify initialized */ - while (1) { - /* Wait for a signal to arrive... */ + + /* Wait for a signal to arrive... */ + #ifdef __DARWIN__ + while (1) { /* * The wx driver needs to be able to steal the main thread for Cocoa to * work properly. @@ -3307,12 +3309,24 @@ erts_sys_main_thread(void) void* (*func)(void*); void* arg; void *resp; - read(erts_darwin_main_thread_pipe[0],&func,sizeof(void* (*)(void*))); - read(erts_darwin_main_thread_pipe[0],&arg, sizeof(void*)); + res = read(erts_darwin_main_thread_pipe[0],&func,sizeof(void* (*)(void*))); + if (res != sizeof(void* (*)(void*))) + break; + res = read(erts_darwin_main_thread_pipe[0],&arg,sizeof(void*)); + if (res != sizeof(void*)) + break; resp = (*func)(arg); write(erts_darwin_main_thread_result_pipe[1],&resp,sizeof(void *)); } -#else + + if (res == -1 && errno != EINTR) + break; + } + /* Something broke with the main thread pipe, so we ignore it for now. + Most probably erts has closed this pipe and is about to exit. */ +#endif /* #ifdef __DARWIN__ */ + + while (1) { #ifdef DEBUG int res = #else @@ -3321,7 +3335,6 @@ erts_sys_main_thread(void) select(0, NULL, NULL, NULL, NULL); ASSERT(res < 0); ASSERT(errno == EINTR); -#endif } } -- cgit v1.2.3 From 9a065ead487d1d655b8a73e84ee68e2c51ee21b0 Mon Sep 17 00:00:00 2001 From: Alex Wilson Date: Fri, 31 Oct 2014 17:25:24 +1000 Subject: os_mon: cpu_sup should use native sysctl/libkvm calls on BSD This avoids forking off with os:cmd every time we just want to collect the load averages. riak does this every second, which results in a lot of unnecessary load. --- erts/configure.in | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 1676d3d216..c41a5bec5a 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -4705,9 +4705,18 @@ AC_CHECK_LIB(kstat, kstat_open, [ CPU_SUP_LIBS="$CPU_SUP_LIBS -lkstat" ]) +AC_CHECK_LIB(kvm, kvm_open, [ + os_mon_programs="$os_mon_programs cpu_sup" + CPU_SUP_LIBS="$CPU_SUP_LIBS -lkvm" + ]) + case $host_os in solaris2*) os_mon_programs="$os_mon_programs ferrule mod_syslog" ;; + darwin*) + os_mon_programs="$os_mon_programs cpu_sup" ;; + openbsd*) + os_mon_programs="$os_mon_programs cpu_sup" ;; linux*) os_mon_programs="$os_mon_programs cpu_sup" ;; esac -- cgit v1.2.3 From ce73c38b10d1dee5209b505ef054b108e747b522 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 5 Jan 2015 11:50:15 +0100 Subject: Fix missing quotation in LM_FIND_EMU_CC --- erts/aclocal.m4 | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index d78025b0be..5735cdea5c 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -246,31 +246,31 @@ lbl1: return 1; lbl2: return 2; -],ac_cv_prog_emu_cc=$CC,ac_cv_prog_emu_cc=no) +],ac_cv_prog_emu_cc="$CC",ac_cv_prog_emu_cc=no) -if test $ac_cv_prog_emu_cc = no; then +if test "$ac_cv_prog_emu_cc" = no; then for ac_progname in emu_cc.sh gcc-4.2 gcc; do IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$ac_progname; then - ac_cv_prog_emu_cc=$ac_dir/$ac_progname + if test -f "$ac_dir/$ac_progname"; then + ac_cv_prog_emu_cc="$ac_dir/$ac_progname" break fi done IFS="$ac_save_ifs" - if test $ac_cv_prog_emu_cc != no; then + if test "$ac_cv_prog_emu_cc" != no; then break fi done fi -if test $ac_cv_prog_emu_cc != no; then - save_CC=$CC +if test "$ac_cv_prog_emu_cc" != no; then + save_CC="$CC" save_CFLAGS=$CFLAGS save_CPPFLAGS=$CPPFLAGS - CC=$ac_cv_prog_emu_cc + CC="$ac_cv_prog_emu_cc" CFLAGS="" CPPFLAGS="" AC_TRY_COMPILE([],[ @@ -291,17 +291,17 @@ if test $ac_cv_prog_emu_cc != no; then return 1; lbl2: return 2; - ],ac_cv_prog_emu_cc=$CC,ac_cv_prog_emu_cc=no) + ],ac_cv_prog_emu_cc="$CC",ac_cv_prog_emu_cc=no) CC=$save_CC CFLAGS=$save_CFLAGS CPPFLAGS=$save_CPPFLAGS fi ]) -if test $ac_cv_prog_emu_cc = no; then +if test "$ac_cv_prog_emu_cc" = no; then AC_DEFINE(NO_JUMP_TABLE,[],[Defined if no found C compiler can handle jump tables]) - EMU_CC=$CC + EMU_CC="$CC" else - EMU_CC=$ac_cv_prog_emu_cc + EMU_CC="$ac_cv_prog_emu_cc" fi AC_SUBST(EMU_CC) ]) -- cgit v1.2.3 From 132f27299130a5183d14122c71f9c23587e4945e Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 11 Jan 2015 13:35:25 +0100 Subject: hipe: fix ARM/Thumb interworking HiPE on ARM is currently severely broken if the rest of the VM is compiled to run in Thumb mode -- calling native code quickly ends up executing code in the wrong mode and crashing the VM. This is a problem on e.g. Ubuntu which configures its system GCC to generate Thumb by default. It can also be triggered by overriding CC or CFLAGS when compiling the VM. There were three issues that caused the breakage: 1. Assembly-coded functions in hipe_arm_glue.S weren't explicitly tagged as functions, preventing the linker from generating the correct mode-switching call instructions for calls from C to these functions. Fixed by tagging those symbols as functions. 2. A few BIF wrappers were so simple that they performed tailcalls to the C BIFs. This fails to switch mode when C is in Thumb. Fixed by performing ordinary recursive calls when C is in Thumb. 3. The assembly-coded source files weren't explicitly tagged as ARM. Tested with the HiPE testsuite on ARMv7, with the VM built as ARM and as Thumb. Also manually inspected the object code for the beam executable and checked that call sites from C to HiPE's ARM runtime code and vice versa used the correct mode-switching instructions. --- erts/emulator/hipe/hipe_arm_bifs.m4 | 73 ++++++++++++++++++++++++++++++++++++- erts/emulator/hipe/hipe_arm_glue.S | 41 +++++++++++++++++++-- 2 files changed, 110 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4 index bd8bc5ab6b..3ca9a1bcdb 100644 --- a/erts/emulator/hipe/hipe_arm_bifs.m4 +++ b/erts/emulator/hipe/hipe_arm_bifs.m4 @@ -25,6 +25,7 @@ include(`hipe/hipe_arm_asm.m4') .text .p2align 2 + .arm `#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) # define CALL_BIF(F) ldr r14, =F; str r14, [r0, #P_BIF_CALLEE]; bl hipe_debug_bif_wrapper @@ -391,7 +392,14 @@ $1: mov r0, P /* Perform a quick save;call;restore;ret sequence. */ +#ifdef __thumb__ + SAVE_CONTEXT_QUICK + bl $2 + RESTORE_CONTEXT_QUICK + NBIF_RET(0) +#else QUICK_CALL_RET($2,0) +#endif .size $1, .-$1 .type $1, %function #endif') @@ -407,7 +415,14 @@ $1: NBIF_ARG(r1,1,0) /* Perform a quick save;call;restore;ret sequence. */ +#ifdef __thumb__ + SAVE_CONTEXT_QUICK + bl $2 + RESTORE_CONTEXT_QUICK + NBIF_RET(1) +#else QUICK_CALL_RET($2,1) +#endif .size $1, .-$1 .type $1, %function #endif') @@ -424,7 +439,14 @@ $1: NBIF_ARG(r2,2,1) /* Perform a quick save;call;restore;ret sequence. */ +#ifdef __thumb__ + SAVE_CONTEXT_QUICK + bl $2 + RESTORE_CONTEXT_QUICK + NBIF_RET(2) +#else QUICK_CALL_RET($2,2) +#endif .size $1, .-$1 .type $1, %function #endif') @@ -442,7 +464,14 @@ $1: NBIF_ARG(r3,3,2) /* Perform a quick save;call;restore;ret sequence. */ +#ifdef __thumb__ + SAVE_CONTEXT_QUICK + bl $2 + RESTORE_CONTEXT_QUICK + NBIF_RET(3) +#else QUICK_CALL_RET($2,3) +#endif .size $1, .-$1 .type $1, %function #endif') @@ -466,7 +495,14 @@ $1: NBIF_ARG(r3,5,2) /* Perform a quick save;call;restore;ret sequence. */ +#ifdef __thumb__ + SAVE_CONTEXT_QUICK + bl $2 + RESTORE_CONTEXT_QUICK + NBIF_RET(5) +#else QUICK_CALL_RET($2,5) +#endif .size $1, .-$1 .type $1, %function #endif') @@ -488,9 +524,16 @@ define(noproc_primop_interface_0, #`define' HAVE_$1 .global $1 $1: - /* XXX: this case is always trivial; how to suppress the branch? */ /* Perform a quick save;call;restore;ret sequence. */ +#ifdef __thumb__ + SAVE_CONTEXT_QUICK + bl $2 + RESTORE_CONTEXT_QUICK + NBIF_RET(0) +#else + /* XXX: this case is always trivial; how to suppress the branch? */ QUICK_CALL_RET($2,0) +#endif .size $1, .-$1 .type $1, %function #endif') @@ -505,7 +548,14 @@ $1: NBIF_ARG(r0,1,0) /* Perform a quick save;call;restore;ret sequence. */ +#ifdef __thumb__ + SAVE_CONTEXT_QUICK + bl $2 + RESTORE_CONTEXT_QUICK + NBIF_RET(1) +#else QUICK_CALL_RET($2,1) +#endif .size $1, .-$1 .type $1, %function #endif') @@ -521,7 +571,14 @@ $1: NBIF_ARG(r1,2,1) /* Perform a quick save;call;restore;ret sequence. */ +#ifdef __thumb__ + SAVE_CONTEXT_QUICK + bl $2 + RESTORE_CONTEXT_QUICK + NBIF_RET(2) +#else QUICK_CALL_RET($2,2) +#endif .size $1, .-$1 .type $1, %function #endif') @@ -538,7 +595,14 @@ $1: NBIF_ARG(r2,3,2) /* Perform a quick save;call;restore;ret sequence. */ +#ifdef __thumb__ + SAVE_CONTEXT_QUICK + bl $2 + RESTORE_CONTEXT_QUICK + NBIF_RET(3) +#else QUICK_CALL_RET($2,3) +#endif .size $1, .-$1 .type $1, %function #endif') @@ -558,7 +622,14 @@ $1: str r4, [sp, #0] /* Perform a quick save;call;restore;ret sequence. */ +#ifdef __thumb__ + SAVE_CONTEXT_QUICK + bl $2 + RESTORE_CONTEXT_QUICK + NBIF_RET(5) +#else QUICK_CALL_RET($2,5) +#endif .size $1, .-$1 .type $1, %function #endif') diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S index 2e2b8604a6..5723afac12 100644 --- a/erts/emulator/hipe/hipe_arm_glue.S +++ b/erts/emulator/hipe/hipe_arm_glue.S @@ -25,6 +25,7 @@ .text .p2align 2 + .arm /* * Enter Erlang from C. @@ -70,6 +71,7 @@ * Emulated code recursively calls native code. */ .global hipe_arm_call_to_native + .type hipe_arm_call_to_native, %function hipe_arm_call_to_native: ENTER_FROM_C /* get argument registers */ @@ -85,6 +87,7 @@ hipe_arm_call_to_native: * This is where native code returns to emulated code. */ .global nbif_return + .type nbif_return, %function nbif_return: str r0, [P, #P_ARG0] /* save retval */ mov r0, #HIPE_MODE_SWITCH_RES_RETURN @@ -95,6 +98,7 @@ nbif_return: * Emulated code returns to its native code caller. */ .global hipe_arm_return_to_native + .type hipe_arm_return_to_native, %function hipe_arm_return_to_native: ENTER_FROM_C /* get return value */ @@ -111,6 +115,7 @@ hipe_arm_return_to_native: * Emulated code tailcalls native code. */ .global hipe_arm_tailcall_to_native + .type hipe_arm_tailcall_to_native, %function hipe_arm_tailcall_to_native: ENTER_FROM_C /* get argument registers */ @@ -125,6 +130,7 @@ hipe_arm_tailcall_to_native: * Emulated code throws an exception to its native code caller. */ .global hipe_arm_throw_to_native + .type hipe_arm_throw_to_native, %function hipe_arm_throw_to_native: ENTER_FROM_C /* invoke the handler */ @@ -142,6 +148,7 @@ hipe_arm_throw_to_native: * XXX: Different stubs for different number of register parameters? */ .global nbif_callemu + .type nbif_callemu, %function nbif_callemu: str r8, [P, #P_BEAM_IP] str r0, [P, #P_ARITY] @@ -153,6 +160,7 @@ nbif_callemu: * nbif_apply */ .global nbif_apply + .type nbif_apply, %function nbif_apply: STORE_ARG_REGS mov r0, #HIPE_MODE_SWITCH_RES_APPLY @@ -169,6 +177,7 @@ nbif_apply: */ #if NR_ARG_REGS >= 6 .global nbif_ccallemu6 + .type nbif_ccallemu6, %function nbif_ccallemu6: str ARG5, [P, #P_ARG5] #if NR_ARG_REGS > 6 @@ -181,6 +190,7 @@ nbif_ccallemu6: #if NR_ARG_REGS >= 5 .global nbif_ccallemu5 + .type nbif_ccallemu5, %function nbif_ccallemu5: str ARG4, [P, #P_ARG4] #if NR_ARG_REGS > 5 @@ -193,6 +203,7 @@ nbif_ccallemu5: #if NR_ARG_REGS >= 4 .global nbif_ccallemu4 + .type nbif_ccallemu4, %function nbif_ccallemu4: str ARG3, [P, #P_ARG3] #if NR_ARG_REGS > 4 @@ -205,6 +216,7 @@ nbif_ccallemu4: #if NR_ARG_REGS >= 3 .global nbif_ccallemu3 + .type nbif_ccallemu3, %function nbif_ccallemu3: str ARG2, [P, #P_ARG2] #if NR_ARG_REGS > 3 @@ -217,6 +229,7 @@ nbif_ccallemu3: #if NR_ARG_REGS >= 2 .global nbif_ccallemu2 + .type nbif_ccallemu2, %function nbif_ccallemu2: str ARG1, [P, #P_ARG1] #if NR_ARG_REGS > 2 @@ -229,6 +242,7 @@ nbif_ccallemu2: #if NR_ARG_REGS >= 1 .global nbif_ccallemu1 + .type nbif_ccallemu1, %function nbif_ccallemu1: str ARG0, [P, #P_ARG0] #if NR_ARG_REGS > 1 @@ -240,6 +254,7 @@ nbif_ccallemu1: #endif .global nbif_ccallemu0 + .type nbif_ccallemu0, %function nbif_ccallemu0: /* We use r1 not ARG0 here because ARG0 is not defined when NR_ARG_REGS == 0. */ @@ -254,6 +269,7 @@ nbif_ccallemu0: * This is where native code suspends. */ .global nbif_suspend_0 + .type nbif_suspend_0, %function nbif_suspend_0: mov r0, #HIPE_MODE_SWITCH_RES_SUSPEND b .suspend_exit @@ -262,6 +278,7 @@ nbif_suspend_0: * Suspend from a receive (waiting for a message) */ .global nbif_suspend_msg + .type nbif_suspend_msg, %function nbif_suspend_msg: mov r0, #HIPE_MODE_SWITCH_RES_WAIT b .suspend_exit @@ -272,6 +289,7 @@ nbif_suspend_msg: * else { return 0; } */ .global nbif_suspend_msg_timeout + .type nbif_suspend_msg_timeout, %function nbif_suspend_msg_timeout: ldr r1, [P, #P_FLAGS] mov r0, #HIPE_MODE_SWITCH_RES_WAIT_TIMEOUT @@ -286,23 +304,31 @@ nbif_suspend_msg_timeout: * This is the default exception handler for native code. */ .global nbif_fail + .type nbif_fail, %function nbif_fail: mov r0, #HIPE_MODE_SWITCH_RES_THROW b .flush_exit /* no need to save RA */ .global nbif_0_gc_after_bif - .global nbif_1_gc_after_bif - .global nbif_2_gc_after_bif - .global nbif_3_gc_after_bif + .type nbif_0_gc_after_bif, %function nbif_0_gc_after_bif: mov r1, #0 b .gc_after_bif + + .global nbif_1_gc_after_bif + .type nbif_1_gc_after_bif, %function nbif_1_gc_after_bif: mov r1, #1 b .gc_after_bif + + .global nbif_2_gc_after_bif + .type nbif_2_gc_after_bif, %function nbif_2_gc_after_bif: mov r1, #2 b .gc_after_bif + + .global nbif_3_gc_after_bif + .type nbif_3_gc_after_bif, %function nbif_3_gc_after_bif: mov r1, #3 /*FALLTHROUGH*/ @@ -330,18 +356,25 @@ nbif_3_gc_after_bif: * TEMP_LR contains a copy of LR */ .global nbif_0_simple_exception + .type nbif_0_simple_exception, %function nbif_0_simple_exception: mov r1, #0 b .nbif_simple_exception + .global nbif_1_simple_exception + .type nbif_1_simple_exception, %function nbif_1_simple_exception: mov r1, #1 b .nbif_simple_exception + .global nbif_2_simple_exception + .type nbif_2_simple_exception, %function nbif_2_simple_exception: mov r1, #2 b .nbif_simple_exception + .global nbif_3_simple_exception + .type nbif_3_simple_exception, %function nbif_3_simple_exception: mov r1, #3 /*FALLTHROUGH*/ @@ -385,6 +418,7 @@ nbif_3_simple_exception: * the gray/white stack boundary */ .global nbif_stack_trap_ra + .type nbif_stack_trap_ra, %function nbif_stack_trap_ra: /* a return address, not a function */ # This only handles a single return value. # If we have more, we need to save them in the PCB. @@ -401,6 +435,7 @@ nbif_stack_trap_ra: /* a return address, not a function */ * Caller saved its LR in TEMP_LR (== TEMP1) before calling us. */ .global hipe_arm_inc_stack + .type hipe_arm_inc_stack, %function hipe_arm_inc_stack: STORE_ARG_REGS mov TEMP_ARG0, lr -- cgit v1.2.3 From d373dc49ae4c8a2d27dde23564bcaf2642cc46e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 12 Jan 2015 17:30:45 +0100 Subject: erts: Extend driver interface with emergency_close The intention of this callback is to close all sockets associated to a port. It is closed only on crashdumps. This will currently only be used for the epmd port. --- erts/emulator/beam/erl_driver.h | 5 ++++- erts/emulator/beam/global.h | 1 + erts/emulator/beam/io.c | 1 + erts/emulator/drivers/common/inet_drv.c | 36 ++++++++++++++++++++++++--------- 4 files changed, 32 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index f9938fc66c..e498ac70ec 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -133,7 +133,7 @@ typedef struct { #define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed) #define ERL_DRV_EXTENDED_MAJOR_VERSION 3 -#define ERL_DRV_EXTENDED_MINOR_VERSION 1 +#define ERL_DRV_EXTENDED_MINOR_VERSION 2 /* * The emulator will refuse to load a driver with a major version @@ -361,6 +361,9 @@ typedef struct erl_drv_entry { /* Called on behalf of driver_select when it is safe to release 'event'. A typical unix driver would call close(event) */ + void (*emergency_close)(ErlDrvData drv_data); + /* called when the port is closed abruptly. + specifically when erl_crash_dump is called. */ /* When adding entries here, dont forget to pad in obsolete/driver.h */ } ErlDrvEntry; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 891046a8b5..d25daaf7b8 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -160,6 +160,7 @@ struct erts_driver_t_ { void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data); /* Might be NULL */ void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor); void (*stop_select)(ErlDrvEvent event, void*); /* Might be NULL */ + void (*emergency_close)(ErlDrvData drv_data); /* Might be NULL */ }; extern erts_driver_t *driver_list; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 9ae973e108..4ae8fafb2c 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -7396,6 +7396,7 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle) drv->timeout = de->timeout ? de->timeout : no_timeout_callback; drv->ready_async = de->ready_async; drv->process_exit = de->process_exit; + drv->emergency_close = de->emergency_close; if (de->stop_select) drv->stop_select = de->stop_select; else diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index db8a251fdd..be02a4b243 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -268,14 +268,13 @@ static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD); #define sock_htonl(x) htonl((x)) #define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) #define sock_sendv(s, vec, size, np, flag) \ - WSASend((s),(WSABUF*)(vec),\ - (size),(np),(flag),NULL,NULL) + WSASend((s),(WSABUF*)(vec),(size),(np),(flag),NULL,NULL) #define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag)) #define sock_recvfrom(s,buf,blen,flag,addr,alen) \ - recvfrom((s),(buf),(blen),(flag),(addr),(alen)) + recvfrom((s),(buf),(blen),(flag),(addr),(alen)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ - sendto((s),(buf),(blen),(flag),(addr),(alen)) + sendto((s),(buf),(blen),(flag),(addr),(alen)) #define sock_hostname(buf, len) gethostname((buf), (len)) #define sock_getservbyname(name,proto) getservbyname((name),(proto)) @@ -360,9 +359,9 @@ static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int #define sock_accept(s, addr, len) accept((s), (addr), (len)) #define sock_send(s,buf,len,flag) inet_send((s),(buf),(len),(flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ - sendto((s),(buf),(blen),(flag),(addr),(alen)) + sendto((s),(buf),(blen),(flag),(addr),(alen)) #define sock_sendv(s, vec, size, np, flag) \ - (*(np) = writev_fallback((s), (struct iovec*)(vec), (size), (*(np)))) + (*(np) = writev_fallback((s), (struct iovec*)(vec), (size), (*(np)))) #define sock_sendmsg(s,msghdr,flag) sendmsg((s),(msghdr),(flag)) #define sock_open(af, type, proto) socket((af), (type), (proto)) @@ -1178,6 +1177,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData, unsigned int, static void tcp_inet_timeout(ErlDrvData); static void tcp_inet_process_exit(ErlDrvData, ErlDrvMonitor *); static void inet_stop_select(ErlDrvEvent, void*); +static void inet_emergency_close(ErlDrvData); #ifdef __WIN32__ static void tcp_inet_event(ErlDrvData, ErlDrvEvent); static void find_dynamic_functions(void); @@ -1288,7 +1288,8 @@ static struct erl_drv_entry tcp_inet_driver_entry = ERL_DRV_FLAG_USE_PORT_LOCKING|ERL_DRV_FLAG_SOFT_BUSY, NULL, tcp_inet_process_exit, - inet_stop_select + inet_stop_select, + inet_emergency_close }; @@ -1341,7 +1342,8 @@ static struct erl_drv_entry udp_inet_driver_entry = ERL_DRV_FLAG_USE_PORT_LOCKING, NULL, NULL, - inet_stop_select + inet_stop_select, + inet_emergency_close }; #endif @@ -1375,7 +1377,8 @@ static struct erl_drv_entry sctp_inet_driver_entry = ERL_DRV_FLAG_USE_PORT_LOCKING, NULL, NULL, /* process_exit */ - inet_stop_select + inet_stop_select, + inet_emergency_close }; #endif @@ -1421,7 +1424,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event); static int packet_inet_output(udp_descriptor* udesc, HANDLE event); #endif -/* convert descriptor poiner to inet_descriptor pointer */ +/* convert descriptor pointer to inet_descriptor pointer */ #define INETP(d) (&(d)->inet) #ifdef __OSE__ @@ -8204,6 +8207,19 @@ static void inet_stop(inet_descriptor* desc) FREE(desc); } +static void inet_emergency_close(ErlDrvData data) +{ + /* valid for any (UDP, TCP or SCTP) descriptor */ + tcp_descriptor* tcp_desc = (tcp_descriptor*)data; + inet_descriptor* desc = INETP(tcp_desc); + DEBUGF(("inet_emergency_close(%ld) {s=%d\r\n", + (long)desc->inet.port, desc->inet.s)); + if (desc->s != INVALID_SOCKET) { + sock_close(desc->s); + } +} + + static void set_default_msgq_limits(ErlDrvPort port) { ErlDrvSizeT q_high = INET_HIGH_MSGQ_WATERMARK; -- cgit v1.2.3 From fdf09e81de5e7f1ecfe71f98b56c411073badae8 Mon Sep 17 00:00:00 2001 From: Olivier Girondel Date: Sun, 4 Jan 2015 04:40:55 +0100 Subject: Add math:log2/1 --- erts/configure.in | 5 +++++ erts/emulator/beam/bif.tab | 1 + erts/emulator/beam/erl_math.c | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 1676d3d216..6bea197a80 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -4904,6 +4904,11 @@ CFLAGS="$CFLAGS $sanitizers" LDFLAGS="$LDFLAGS $sanitizers" ]) +dnl ---------------------------------------------------------------------- +dnl Check for log2 +dnl ---------------------------------------------------------------------- +AC_CHECK_FUNCS([log2]) + dnl ---------------------------------------------------------------------- dnl Output the result. dnl ---------------------------------------------------------------------- diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index aea3224342..1d0d214e77 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -194,6 +194,7 @@ bif math:erf/1 bif math:erfc/1 bif math:exp/1 bif math:log/1 +bif math:log2/1 bif math:log10/1 bif math:sqrt/1 bif math:atan2/2 diff --git a/erts/emulator/beam/erl_math.c b/erts/emulator/beam/erl_math.c index 16d4fdc09c..9b864628db 100644 --- a/erts/emulator/beam/erl_math.c +++ b/erts/emulator/beam/erl_math.c @@ -207,6 +207,24 @@ BIF_RETTYPE math_log_1(BIF_ALIST_1) return math_call_1(BIF_P, log, BIF_ARG_1); } +#ifdef HAVE_LOG2 +static double +log2_wrapper(double x) +{ + return log2(x); +} +#else +static double +log2_wrapper(double x) +{ + return log(x) / 0.6931471805599453; /* log(2.0); */ +} +#endif + +BIF_RETTYPE math_log2_1(BIF_ALIST_1) +{ + return math_call_1(BIF_P, log2_wrapper, BIF_ARG_1); +} BIF_RETTYPE math_log10_1(BIF_ALIST_1) { -- cgit v1.2.3 From 24fa075b5c0d54f2035a2ff510a82aa19187eda4 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 5 Jan 2015 11:04:34 +0100 Subject: Improve ethread atomics based on GCC builtins * Use of __atomic builtins when available. * Improved configure test that checks for missing memory barrier in __sync_synchronize(). The old approach was to verify known working gcc versions and check gcc version at compile time. Besides not being very safe, the old approach often unnecessarily caused usage of the very expensive workaround. * Introduced (no overhead) workaround for missing clobber in __sync_synchronize() when using buggy LLVM implementation of __sync_synchronize(). * Implement native memory barriers for ARM processors supporting the DMB instruction. * Use of volatile store on Alpha as atomic set operation if no __atomic_store_n() is available (already used on x86/x86_64 Sparc V9, PowerPC, and MIPS). Fallback used when not using volatile store is typically very expensive. * Use volatile load on Alpha and ARM as atomic read operation if no __atomic_load_n() is available (already used on x86/x86_64 Sparc V9, PowerPC, and MIPS). Fallback when not using volatile load is typically very expensive. --- erts/aclocal.m4 | 304 ++++++++++++--- erts/configure.in | 25 +- erts/include/internal/ethread.h | 5 +- erts/include/internal/ethread_header_config.h.in | 68 +++- erts/include/internal/ethread_inline.h | 23 ++ erts/include/internal/gcc/ethr_atomic.h | 477 ++++++++++++++++++++--- erts/include/internal/gcc/ethr_dw_atomic.h | 178 +++++++-- erts/include/internal/gcc/ethr_membar.h | 208 ++++++++-- erts/include/internal/gcc/ethread.h | 329 +++++++++++++++- 9 files changed, 1404 insertions(+), 213 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 5735cdea5c..e87bcde7b8 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1,7 +1,7 @@ dnl dnl %CopyrightBegin% dnl -dnl Copyright Ericsson AB 1998-2013. All Rights Reserved. +dnl Copyright Ericsson AB 1998-2015. 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 @@ -908,24 +908,226 @@ AC_SUBST(ERTS_INTERNAL_X_LIBS) ]) -AC_DEFUN(ETHR_CHK_SYNC_OP, +AC_DEFUN(ETHR_CHK_GCC_ATOMIC_OP__, [ - AC_MSG_CHECKING([for $3-bit $1()]) - case "$2" in - "1") sync_call="$1(&var);";; - "2") sync_call="$1(&var, ($4) 0);";; - "3") sync_call="$1(&var, ($4) 0, ($4) 0);";; + # $1 - atomic_op + + for atomic_bit_size in 32 64 128; do + case $atomic_bit_size in + 32) gcc_atomic_type="$gcc_atomic_type32";; + 64) gcc_atomic_type="$gcc_atomic_type64";; + 128) gcc_atomic_type="$gcc_atomic_type128";; + esac + gcc_atomic_lockfree="int x[[(2*__atomic_always_lock_free(sizeof($gcc_atomic_type), 0))-1]]" + case $1 in + __sync_add_and_fetch | __sync_fetch_and_and | __sync_fetch_and_or) + atomic_call="volatile $gcc_atomic_type var; $gcc_atomic_type res = $1(&var, ($gcc_atomic_type) 0);" + ;; + __sync_val_compare_and_swap) + atomic_call="volatile $gcc_atomic_type var; $gcc_atomic_type res = $1(&var, ($gcc_atomic_type) 0, ($gcc_atomic_type) 0);" + ;; + __atomic_store_n) + atomic_call="$gcc_atomic_lockfree; volatile $gcc_atomic_type var; $1(&var, ($gcc_atomic_type) 0, __ATOMIC_RELAXED); $1(&var, ($gcc_atomic_type) 0, __ATOMIC_RELEASE);" + ;; + __atomic_load_n) + atomic_call="$gcc_atomic_lockfree; volatile $gcc_atomic_type var; $gcc_atomic_type res = $1(&var, __ATOMIC_RELAXED); res = $1(&var, __ATOMIC_ACQUIRE);" + ;; + __atomic_add_fetch| __atomic_fetch_and | __atomic_fetch_or) + atomic_call="$gcc_atomic_lockfree; volatile $gcc_atomic_type var; $gcc_atomic_type res = $1(&var, ($gcc_atomic_type) 0, __ATOMIC_RELAXED); res = $1(&var, ($gcc_atomic_type) 0, __ATOMIC_ACQUIRE); res = $1(&var, ($gcc_atomic_type) 0, __ATOMIC_RELEASE);" + ;; + __atomic_compare_exchange_n) + atomic_call="$gcc_atomic_lockfree; volatile $gcc_atomic_type var; $gcc_atomic_type val; int res = $1(&var, &val, ($gcc_atomic_type) 0, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); res = $1(&var, &val, ($gcc_atomic_type) 0, 0, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);" + ;; + *) + AC_MSG_ERROR([Internal error: missing implementation for $1]) + ;; + esac + eval atomic${atomic_bit_size}_call=\"$atomic_call\" + done + + AC_CACHE_CHECK([for 32-bit $1()], ethr_cv_32bit_$1, + [ + ethr_cv_32bit_$1=no + AC_TRY_LINK([], [$atomic32_call], [ethr_cv_32bit_$1=yes]) + ]) + AC_CACHE_CHECK([for 64-bit $1()], ethr_cv_64bit_$1, + [ + ethr_cv_64bit_$1=no + AC_TRY_LINK([], [$atomic64_call], [ethr_cv_64bit_$1=yes]) + ]) + AC_CACHE_CHECK([for 128-bit $1()], ethr_cv_128bit_$1, + [ + ethr_cv_128bit_$1=no + AC_TRY_LINK([], [$atomic128_call], [ethr_cv_128bit_$1=yes]) + ]) + + case $ethr_cv_128bit_$1-$ethr_cv_64bit_$1-$ethr_cv_32bit_$1 in + no-no-no) + have_atomic_ops=0;; + no-no-yes) + have_atomic_ops=4;; + no-yes-no) + have_atomic_ops=8;; + no-yes-yes) + have_atomic_ops=12;; + yes-no-no) + have_atomic_ops=16;; + yes-no-yes) + have_atomic_ops=20;; + yes-yes-no) + have_atomic_ops=24;; + yes-yes-yes) + have_atomic_ops=28;; + esac + AC_DEFINE_UNQUOTED([ETHR_HAVE_$1], [$have_atomic_ops], [Define as a bitmask corresponding to the word sizes that $1() can handle on your system]) +]) + +AC_DEFUN(ETHR_CHK_IF_NOOP, +[ + ethr_test_filename="chk_if_$1$3_noop_config1test.$$" + cat > "${ethr_test_filename}.c" < "${ethr_test_filename}.c" </dev/null 2>&1; then + ethr_$1$3_noop=yes + else + ethr_$1$3_noop=no + fi + rm -f "${ethr_test_filename}.c" "${ethr_test_filename}1.o" "${ethr_test_filename}2.o" +]) + +AC_DEFUN(ETHR_CHK_GCC_ATOMIC_OPS, +[ + AC_CHECK_SIZEOF(short) + AC_CHECK_SIZEOF(int) + AC_CHECK_SIZEOF(long) + AC_CHECK_SIZEOF(long long) + AC_CHECK_SIZEOF(__int128_t) + + if test "$ac_cv_sizeof_short" = "4"; then + gcc_atomic_type32="short" + elif test "$ac_cv_sizeof_int" = "4"; then + gcc_atomic_type32="int" + elif test "$ac_cv_sizeof_long" = "4"; then + gcc_atomic_type32="long" + else + AC_MSG_ERROR([No 32-bit type found]) + fi + + if test "$ac_cv_sizeof_int" = "8"; then + gcc_atomic_type64="int" + elif test "$ac_cv_sizeof_long" = "8"; then + gcc_atomic_type64="long" + elif test "$ac_cv_sizeof_long_long" = "8"; then + gcc_atomic_type64="long long" + else + AC_MSG_ERROR([No 64-bit type found]) + fi + + if test "$ac_cv_sizeof___int128_t" = "16"; then + gcc_atomic_type128="__int128_t" + else + gcc_atomic_type128="#error " + fi + AC_CACHE_CHECK([for a working __sync_synchronize()], ethr_cv___sync_synchronize, + [ + ethr_cv___sync_synchronize=no + AC_TRY_LINK([], + [ __sync_synchronize(); ], + [ethr_cv___sync_synchronize=yes]) + if test $ethr_cv___sync_synchronize = yes; then + # + # Old gcc versions on at least x86 have a buggy + # __sync_synchronize() which does not emit a + # memory barrier. We try to detect this by + # compiling to assembly with and without + # __sync_synchronize() and compare the results. + # + ETHR_CHK_IF_NOOP(__sync_synchronize, [()], []) + if test $ethr___sync_synchronize_noop = yes; then + # Got a buggy implementation of + # __sync_synchronize... + ethr_cv___sync_synchronize="no; buggy implementation" + fi + fi + ]) + + if test "$ethr_cv___sync_synchronize" = "yes"; then + have_sync_synchronize_value="~0" + else + have_sync_synchronize_value="0" + fi + AC_DEFINE_UNQUOTED([ETHR_HAVE___sync_synchronize], [$have_sync_synchronize_value], [Define as a bitmask corresponding to the word sizes that __sync_synchronize() can handle on your system]) + + ETHR_CHK_GCC_ATOMIC_OP__(__sync_add_and_fetch) + ETHR_CHK_GCC_ATOMIC_OP__(__sync_fetch_and_and) + ETHR_CHK_GCC_ATOMIC_OP__(__sync_fetch_and_or) + ETHR_CHK_GCC_ATOMIC_OP__(__sync_val_compare_and_swap) + + ETHR_CHK_GCC_ATOMIC_OP__(__atomic_store_n) + ETHR_CHK_GCC_ATOMIC_OP__(__atomic_load_n) + ETHR_CHK_GCC_ATOMIC_OP__(__atomic_add_fetch) + ETHR_CHK_GCC_ATOMIC_OP__(__atomic_fetch_and) + ETHR_CHK_GCC_ATOMIC_OP__(__atomic_fetch_or) + ETHR_CHK_GCC_ATOMIC_OP__(__atomic_compare_exchange_n) + + ethr_have_gcc_native_atomics=no + ethr_arm_dbm_instr_val=0 + case "$GCC-$host_cpu" in + yes-arm*) + AC_CACHE_CHECK([for ARM DMB instruction], ethr_cv_arm_dbm_instr, + [ + ethr_cv_arm_dbm_instr=no + AC_TRY_LINK([], + [ + __asm__ __volatile__("dmb sy" : : : "memory"); + __asm__ __volatile__("dmb st" : : : "memory"); + ], + [ethr_cv_arm_dbm_instr=yes]) + ]) + if test $ethr_cv_arm_dbm_instr = yes; then + ethr_arm_dbm_instr_val=1 + test $ethr_cv_64bit___atomic_compare_exchange_n = yes && + ethr_have_gcc_native_atomics=yes + fi;; + *) + ;; esac - have_sync_op=no - AC_TRY_LINK([], - [ - $4 res; - volatile $4 var; - res = $sync_call - ], - [have_sync_op=yes]) - test $have_sync_op = yes && $5 - AC_MSG_RESULT([$have_sync_op]) + AC_DEFINE_UNQUOTED([ETHR_HAVE_GCC_ASM_ARM_DMB_INSTRUCTION], [$ethr_arm_dbm_instr_val], [Define as a boolean indicating whether you have a gcc compatible compiler capable of generating the ARM DMB instruction, and are compiling for an ARM processor with ARM DMB instruction support, or not]) + test $ethr_cv_32bit___sync_val_compare_and_swap = yes && + ethr_have_gcc_native_atomics=yes + test $ethr_cv_64bit___sync_val_compare_and_swap = yes && + ethr_have_gcc_native_atomics=yes + if test "$ethr_cv___sync_synchronize" = "yes"; then + test $ethr_cv_64bit___atomic_compare_exchange_n = yes && + ethr_have_gcc_native_atomics=yes + test $ethr_cv_32bit___atomic_compare_exchange_n = yes && + ethr_have_gcc_native_atomics=yes + fi + ethr_have_gcc_atomic_builtins=0 + if test $ethr_have_gcc_native_atomics = yes; then + ethr_native_atomic_implementation=gcc_sync + test $ethr_cv_32bit___atomic_compare_exchange_n = yes && ethr_have_gcc_atomic_builtins=1 + test $ethr_cv_64bit___atomic_compare_exchange_n = yes && ethr_have_gcc_atomic_builtins=1 + test $ethr_have_gcc_atomic_builtins = 1 && ethr_native_atomic_implementation=gcc_atomic_sync + fi + AC_DEFINE_UNQUOTED([ETHR_HAVE_GCC___ATOMIC_BUILTINS], [$ethr_have_gcc_atomic_builtins], [Define as a boolean indicating whether you have a gcc __atomic builtins or not]) + test $ethr_have_gcc_native_atomics = yes && ethr_have_native_atomics=yes ]) AC_DEFUN(ETHR_CHK_INTERLOCKED, @@ -1005,6 +1207,16 @@ AC_ARG_ENABLE(prefer-gcc-native-ethr-impls, test $enable_prefer_gcc_native_ethr_impls = yes && AC_DEFINE(ETHR_PREFER_GCC_NATIVE_IMPLS, 1, [Define if you prefer gcc native ethread implementations]) +AC_ARG_ENABLE(trust-gcc-atomic-builtins-memory-barriers, + AS_HELP_STRING([--enable-trust-gcc-atomic-builtins-memory-barriers], + [trust gcc atomic builtins memory barriers]), +[ case "$enableval" in + yes) trust_gcc_atomic_builtins_mbs=1 ;; + *) trust_gcc_atomic_builtins_mbs=0 ;; + esac ], trust_gcc_atomic_builtins_mbs=0) + +AC_DEFINE_UNQUOTED(ETHR_TRUST_GCC_ATOMIC_BUILTINS_MEMORY_BARRIERS, [$trust_gcc_atomic_builtins_mbs], [Define as a boolean indicating whether you trust gcc's __atomic_* builtins memory barrier implementations, or not]) + AC_ARG_WITH(libatomic_ops, AS_HELP_STRING([--with-libatomic_ops=PATH], [specify and prefer usage of libatomic_ops in the ethread library])) @@ -1016,6 +1228,7 @@ AC_ARG_WITH(with_sparc_memory_order, LM_CHECK_THR_LIB ERL_INTERNAL_LIBS +ethr_native_atomic_implementation=none ethr_have_native_atomics=no ethr_have_native_spinlock=no ETHR_THR_LIB_BASE="$THR_LIB_NAME" @@ -1100,7 +1313,10 @@ case "$THR_LIB_NAME" in ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange128], [4], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE128, 1, [Define if you have _InterlockedCompareExchange128()])) fi - test "$ethr_have_native_atomics" = "yes" && ethr_have_native_spinlock=yes + if test "$ethr_have_native_atomics" = "yes"; then + ethr_native_atomic_implementation=windows + ethr_have_native_spinlock=yes + fi ;; pthread|ose_threads) @@ -1351,54 +1567,11 @@ case "$THR_LIB_NAME" in fi - AC_CHECK_SIZEOF(int) - AC_CHECK_SIZEOF(long) - AC_CHECK_SIZEOF(long long) - AC_CHECK_SIZEOF(__int128_t) - - if test "$ac_cv_sizeof_int" = "4"; then - int32="int" - elif test "$ac_cv_sizeof_long" = "4"; then - int32="long" - elif test "$ac_cv_sizeof_long_long" = "4"; then - int32="long long" - else - AC_MSG_ERROR([No 32-bit type found]) - fi - - if test "$ac_cv_sizeof_int" = "8"; then - int64="int" - elif test "$ac_cv_sizeof_long" = "8"; then - int64="long" - elif test "$ac_cv_sizeof_long_long" = "8"; then - int64="long long" - else - AC_MSG_ERROR([No 64-bit type found]) - fi - - int128=no - if test "$ac_cv_sizeof___int128_t" = "16"; then - int128="__int128_t" - fi - if test "X$disable_native_ethr_impls" = "Xyes"; then ethr_have_native_atomics=no else - ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP32, 1, [Define if you have __sync_val_compare_and_swap() for 32-bit integers])) - test "$have_sync_op" = "yes" && ethr_have_native_atomics=yes - ETHR_CHK_SYNC_OP([__sync_add_and_fetch], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_ADD_AND_FETCH32, 1, [Define if you have __sync_add_and_fetch() for 32-bit integers])) - ETHR_CHK_SYNC_OP([__sync_fetch_and_and], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_AND32, 1, [Define if you have __sync_fetch_and_and() for 32-bit integers])) - ETHR_CHK_SYNC_OP([__sync_fetch_and_or], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_OR32, 1, [Define if you have __sync_fetch_and_or() for 32-bit integers])) - - ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP64, 1, [Define if you have __sync_val_compare_and_swap() for 64-bit integers])) - test "$have_sync_op" = "yes" && ethr_have_native_atomics=yes - ETHR_CHK_SYNC_OP([__sync_add_and_fetch], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_ADD_AND_FETCH64, 1, [Define if you have __sync_add_and_fetch() for 64-bit integers])) - ETHR_CHK_SYNC_OP([__sync_fetch_and_and], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_AND64, 1, [Define if you have __sync_fetch_and_and() for 64-bit integers])) - ETHR_CHK_SYNC_OP([__sync_fetch_and_or], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_OR64, 1, [Define if you have __sync_fetch_and_or() for 64-bit integers])) - - if test $int128 != no; then - ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [128], [$int128], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP128, 1, [Define if you have __sync_val_compare_and_swap() for 128-bit integers])) - fi + + ETHR_CHK_GCC_ATOMIC_OPS([]) AC_MSG_CHECKING([for a usable libatomic_ops implementation]) case "x$with_libatomic_ops" in @@ -1448,6 +1621,7 @@ case "$THR_LIB_NAME" in #endif ], [ethr_have_native_atomics=yes + ethr_native_atomic_implementation=libatomic_ops ethr_have_libatomic_ops=yes]) AC_MSG_RESULT([$ethr_have_libatomic_ops]) if test $ethr_have_libatomic_ops = yes; then @@ -1479,15 +1653,19 @@ case "$THR_LIB_NAME" in *) AC_MSG_ERROR([Unsupported Sparc memory order: $with_sparc_memory_order]);; esac + ethr_native_atomic_implementation=ethread ethr_have_native_atomics=yes;; i86pc | i*86 | x86_64 | amd64) if test "$enable_x86_out_of_order" = "yes"; then AC_DEFINE(ETHR_X86_OUT_OF_ORDER, 1, [Define if x86/x86_64 out of order instructions should be synchronized]) fi + ethr_native_atomic_implementation=ethread ethr_have_native_atomics=yes;; macppc | ppc | powerpc | "Power Macintosh") + ethr_native_atomic_implementation=ethread ethr_have_native_atomics=yes;; tile) + ethr_native_atomic_implementation=ethread ethr_have_native_atomics=yes;; *) ;; diff --git a/erts/configure.in b/erts/configure.in index 1676d3d216..09a04ca1a9 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. -*-m4-*- dnl %CopyrightBegin% dnl -dnl Copyright Ericsson AB 1997-2014. All Rights Reserved. +dnl Copyright Ericsson AB 1997-2015. 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 @@ -1123,10 +1123,31 @@ if test $ERTS_BUILD_SMP_EMU = yes; then case "$ethr_have_native_atomics-$smp_require_native_atomics-$ethr_have_native_spinlock" in yes-*) + if test "$ethr_native_atomic_implementation" = "gcc_sync"; then + test -f "$ERL_TOP/erts/CONF_INFO" || + echo "" > "$ERL_TOP/erts/CONF_INFO" + cat >> $ERL_TOP/erts/CONF_INFO <counter; + return (ETHR_AINT_T__ *) &var->value; } -#if ETHR_READ_AND_SET_WITHOUT_SYNC_OP__ +/* + * set() + */ +#if (ETHR_HAVE___atomic_store_n & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if (ETHR_GCC_RELAXED_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) #if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 # define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_SET 1 @@ -115,12 +124,109 @@ ETHR_NATMC_FUNC__(addr)(ETHR_ATMC_T__ *var) static ETHR_INLINE void ETHR_NATMC_FUNC__(set)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) { - var->counter = value; + __atomic_store_n(&var->value, value, __ATOMIC_RELAXED); } -#endif /* ETHR_READ_AND_SET_WITHOUT_SYNC_OP__ */ +#endif /* ETHR_GCC_RELAXED_VERSIONS__ */ -#if ETHR_READ_AND_SET_WITHOUT_SYNC_OP__ +#if (ETHR_GCC_RELB_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_SET_RELB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_SET_RELB 1 +#endif + +static ETHR_INLINE void +ETHR_NATMC_FUNC__(set_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) +{ + __atomic_store_n(&var->value, value, __ATOMIC_RELEASE); +} + +#endif /* ETHR_GCC_RELB_VERSIONS__ */ + +#elif (ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if (ETHR_GCC_RELAXED_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_SET 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_SET 1 +#endif + +static ETHR_INLINE void +ETHR_NATMC_FUNC__(set)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) +{ + var->value = value; +} + +#endif /* ETHR_GCC_RELAXED_VERSIONS__ */ + +#if (ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE_RELB__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if (ETHR_GCC_RELB_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_SET_RELB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_SET_RELB 1 +#endif + +static ETHR_INLINE void +ETHR_NATMC_FUNC__(set_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) +{ + var->value = value; +} + +#endif /* ETHR_GCC_RELB_VERSIONS__ */ + +#endif /* ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE_RELB__ */ + +#endif /* ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE__ */ + +/* + * read() + */ + +#if (ETHR_HAVE___atomic_load_n & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if (ETHR_GCC_RELAXED_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_READ 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_READ 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read)(ETHR_ATMC_T__ *var) +{ + return __atomic_load_n(&var->value, __ATOMIC_RELAXED); +} + +#endif /* ETHR_GCC_RELAXED_VERSIONS__ */ + +#if ((ETHR_GCC_ACQB_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) \ + & ~ETHR___atomic_load_ACQUIRE_barrier_bug) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_READ_ACQB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_READ_ACQB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read_acqb)(ETHR_ATMC_T__ *var) +{ + return __atomic_load_n(&var->value, __ATOMIC_ACQUIRE); +} + +#endif /* ETHR_GCC_ACQB_VERSIONS__ */ + +#elif (ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if (ETHR_GCC_RELAXED_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) #if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 # define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_READ 1 @@ -131,12 +237,90 @@ ETHR_NATMC_FUNC__(set)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(read)(ETHR_ATMC_T__ *var) { - return var->counter; + return var->value; +} + +#endif /* ETHR_GCC_RELAXED_VERSIONS__ */ + +#if (ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD_ACQB__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if (ETHR_GCC_ACQB_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_READ_ACQB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_READ_ACQB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read_acqb)(ETHR_ATMC_T__ *var) +{ + return var->value; +} + +#endif /* ETHR_GCC_ACQB_VERSIONS__ */ + +#endif /* ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD_ACQB__ */ + +#endif /* ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD__ */ + +/* + * add_return() + */ +#if (ETHR_HAVE___atomic_add_fetch & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if (ETHR_GCC_RELAXED_MOD_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_ADD_RETURN 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_ADD_RETURN 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) +{ + return __atomic_add_fetch(&var->value, incr, __ATOMIC_RELAXED); } -#endif /* ETHR_READ_AND_SET_WITHOUT_SYNC_OP__ */ +#endif /* ETHR_GCC_RELAXED_MOD_VERSIONS__ */ -#if defined(ETHR_HAVE___SYNC_ADD_AND_FETCH) +#if (ETHR_GCC_ACQB_MOD_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_ADD_RETURN_ACQB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_ADD_RETURN_ACQB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(add_return_acqb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) +{ + return __atomic_add_fetch(&var->value, incr, __ATOMIC_ACQUIRE); +} + +#endif /* ETHR_GCC_ACQB_MOD_VERSIONS__ */ + +#if (ETHR_GCC_RELB_MOD_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_ADD_RETURN_RELB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_ADD_RETURN_RELB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(add_return_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) +{ + return __atomic_add_fetch(&var->value, incr, __ATOMIC_RELEASE); +} + +#endif /* ETHR_GCC_RELB_MOD_VERSIONS__ */ + +#endif /* ETHR_HAVE___atomic_add_fetch */ + +#if ((ETHR_HAVE___sync_add_and_fetch & ETHR_INCLUDE_ATOMIC_IMPL__) \ + & ETHR_GCC_MB_MOD_VERSIONS__) #if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 # define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_ADD_RETURN_MB 1 @@ -147,12 +331,68 @@ ETHR_NATMC_FUNC__(read)(ETHR_ATMC_T__ *var) static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(add_return_mb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { - return __sync_add_and_fetch(&var->counter, incr); + return __sync_add_and_fetch(&var->value, incr); +} + +#endif /* ETHR_HAVE___sync_add_and_fetch */ + +/* + * and_retold() + */ +#if (ETHR_HAVE___atomic_fetch_and & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if (ETHR_GCC_RELAXED_MOD_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_AND_RETOLD 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_AND_RETOLD 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) +{ + return __atomic_fetch_and(&var->value, mask, __ATOMIC_RELAXED); +} + +#endif /* ETHR_GCC_RELAXED_MOD_VERSIONS__ */ + +#if (ETHR_GCC_ACQB_MOD_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_AND_RETOLD_ACQB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_AND_RETOLD_ACQB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(and_retold_acqb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) +{ + return __atomic_fetch_and(&var->value, mask, __ATOMIC_ACQUIRE); } +#endif /* ETHR_GCC_ACQB_MOD_VERSIONS__ */ + +#if (ETHR_GCC_RELB_MOD_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_AND_RETOLD_RELB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_AND_RETOLD_RELB 1 #endif -#if defined(ETHR_HAVE___SYNC_FETCH_AND_AND) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(and_retold_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) +{ + return __atomic_fetch_and(&var->value, mask, __ATOMIC_RELEASE); +} + +#endif /* ETHR_GCC_RELB_MOD_VERSIONS__ */ + +#endif /* ETHR_HAVE___atomic_fetch_and */ + +#if ((ETHR_HAVE___sync_fetch_and_and & ETHR_INCLUDE_ATOMIC_IMPL__) \ + & ETHR_GCC_MB_MOD_VERSIONS__) #if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 # define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_AND_RETOLD_MB 1 @@ -163,12 +403,68 @@ ETHR_NATMC_FUNC__(add_return_mb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(and_retold_mb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { - return __sync_fetch_and_and(&var->counter, mask); + return __sync_fetch_and_and(&var->value, mask); +} + +#endif /* ETHR_HAVE___sync_fetch_and_and */ + +/* + * or_retold() + */ +#if (ETHR_HAVE___atomic_fetch_or & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if (ETHR_GCC_RELAXED_MOD_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_OR_RETOLD 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_OR_RETOLD 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) +{ + return __atomic_fetch_or(&var->value, mask, __ATOMIC_RELAXED); +} + +#endif /* ETHR_GCC_RELAXED_MOD_VERSIONS__ */ + +#if (ETHR_GCC_ACQB_MOD_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_OR_RETOLD_ACQB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_OR_RETOLD_ACQB 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(or_retold_acqb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) +{ + return __atomic_fetch_or(&var->value, mask, __ATOMIC_ACQUIRE); } +#endif /* ETHR_GCC_ACQB_MOD_VERSIONS__ */ + +#if (ETHR_GCC_RELB_MOD_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_OR_RETOLD_RELB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_OR_RETOLD_RELB 1 #endif -#if defined(ETHR_HAVE___SYNC_FETCH_AND_OR) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(or_retold_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) +{ + return __atomic_fetch_or(&var->value, mask, __ATOMIC_RELEASE); +} + +#endif /* ETHR_GCC_RELB_MOD_VERSIONS__ */ + +#endif /* ETHR_HAVE___atomic_fetch_or */ + +#if ((ETHR_HAVE___sync_fetch_and_or & ETHR_INCLUDE_ATOMIC_IMPL__) \ + & ETHR_GCC_MB_MOD_VERSIONS__) #if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 # define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_OR_RETOLD_MB 1 @@ -179,11 +475,73 @@ ETHR_NATMC_FUNC__(and_retold_mb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(or_retold_mb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { - return (ETHR_AINT_T__) __sync_fetch_and_or(&var->counter, mask); + return (ETHR_AINT_T__) __sync_fetch_and_or(&var->value, mask); +} + +#endif /* ETHR_HAVE___sync_fetch_and_or */ + +/* + * cmpxchg() + */ +#if (ETHR_HAVE___atomic_compare_exchange_n & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if (ETHR_GCC_RELAXED_MOD_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_CMPXCHG 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_CMPXCHG 1 +#endif + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ exp) +{ + ETHR_AINT_T__ xchg = exp; + if (__atomic_compare_exchange_n(&var->value, + &xchg, + new, + 0, /* No spurious failures, please */ + __ATOMIC_RELAXED, + __ATOMIC_RELAXED)) + return exp; + return xchg; } +#endif /* ETHR_GCC_RELAXED_MOD_VERSIONS__ */ + +#if (ETHR_GCC_ACQB_MOD_VERSIONS__ & ETHR_INCLUDE_ATOMIC_IMPL__) + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_CMPXCHG_ACQB 1 +#else +# define ETHR_HAVE_ETHR_NATIVE_ATOMIC64_CMPXCHG_ACQB 1 #endif +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ exp) +{ + ETHR_AINT_T__ xchg = exp; + if (__atomic_compare_exchange_n(&var->value, + &xchg, + new, + 0, /* No spurious failures, please */ + __ATOMIC_ACQUIRE, + __ATOMIC_ACQUIRE)) + return exp; + return xchg; +} + +#endif /* ETHR_GCC_ACQB_MOD_VERSIONS__ */ + +#endif /* ETHR_HAVE___atomic_compare_exchange_n */ + +#if ((ETHR_HAVE___sync_val_compare_and_swap & ETHR_INCLUDE_ATOMIC_IMPL__) \ + & ETHR_GCC_MB_MOD_VERSIONS__) + #if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 # define ETHR_HAVE_ETHR_NATIVE_ATOMIC32_CMPXCHG_MB 1 #else @@ -195,17 +553,16 @@ ETHR_NATMC_FUNC__(cmpxchg_mb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ old) { - return __sync_val_compare_and_swap(&var->counter, old, new); + return __sync_val_compare_and_swap(&var->value, old, new); } +#endif /* ETHR_HAVE___sync_val_compare_and_swap */ + #endif /* ETHR_TRY_INLINE_FUNCS */ #undef ETHR_NATMC_FUNC__ #undef ETHR_ATMC_T__ #undef ETHR_AINT_T__ #undef ETHR_AINT_SUFFIX__ -#undef ETHR_HAVE___SYNC_ADD_AND_FETCH -#undef ETHR_HAVE___SYNC_FETCH_AND_AND -#undef ETHR_HAVE___SYNC_FETCH_AND_OR #endif diff --git a/erts/include/internal/gcc/ethr_dw_atomic.h b/erts/include/internal/gcc/ethr_dw_atomic.h index 6736f9c547..c2c8f85b7b 100644 --- a/erts/include/internal/gcc/ethr_dw_atomic.h +++ b/erts/include/internal/gcc/ethr_dw_atomic.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2015. All Rights Reserved. * * The 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,35 +18,39 @@ */ /* - * Description: Native double word atomics using gcc's builtins + * Description: Native double word atomics using gcc's __atomic + * and __sync builtins * Author: Rickard Green + * + * Note: The C11 memory model implemented by gcc's __atomic + * builtins does not match the ethread API very well. + * + * Due to this we cannot use the __ATOMIC_SEQ_CST + * memory model. For more information see the comment + * in the begining of ethr_membar.h in this directory. */ #undef ETHR_INCLUDE_DW_ATOMIC_IMPL__ -#ifndef ETHR_GCC_DW_ATOMIC_H__ -# define ETHR_GCC_DW_ATOMIC_H__ -# if ((ETHR_SIZEOF_PTR == 4 \ - && defined(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP64)) \ - || (ETHR_SIZEOF_PTR == 8 \ - && defined(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP128) \ - && defined(ETHR_HAVE_INT128_T))) -# define ETHR_INCLUDE_DW_ATOMIC_IMPL__ -# endif +#if !defined(ETHR_GCC_ATOMIC_DW_ATOMIC_H__) \ + && ((ETHR_HAVE___sync_val_compare_and_swap & (2*ETHR_SIZEOF_PTR)) \ + || (ETHR_HAVE___atomic_compare_exchange_n & (2*ETHR_SIZEOF_PTR))) +# define ETHR_GCC_ATOMIC_DW_ATOMIC_H__ +# define ETHR_INCLUDE_DW_ATOMIC_IMPL__ #endif #ifdef ETHR_INCLUDE_DW_ATOMIC_IMPL__ # define ETHR_HAVE_NATIVE_SU_DW_ATOMIC -# define ETHR_NATIVE_DW_ATOMIC_IMPL "gcc" -# if defined(__i386__) || defined(__x86_64__) -/* - * If ETHR_RTCHK_USE_NATIVE_DW_ATOMIC_IMPL__ is defined, it will be used - * at runtime in order to determine if native or fallback implementation - * should be used. - */ -# define ETHR_RTCHK_USE_NATIVE_DW_ATOMIC_IMPL__ \ - ETHR_X86_RUNTIME_CONF_HAVE_DW_CMPXCHG__ -# endif +#if ((ETHR_HAVE___sync_val_compare_and_swap & (2*ETHR_SIZEOF_PTR)) \ + && (ETHR_HAVE___atomic_compare_exchange_n & (2*ETHR_SIZEOF_PTR))) +# define ETHR_NATIVE_DW_ATOMIC_IMPL "gcc_atomic_and_sync_builtins" +#elif (ETHR_HAVE___atomic_compare_exchange_n & (2*ETHR_SIZEOF_PTR)) +# define ETHR_NATIVE_DW_ATOMIC_IMPL "gcc_atomic_builtins" +#elif (ETHR_HAVE___sync_val_compare_and_swap & (2*ETHR_SIZEOF_PTR)) +# define ETHR_NATIVE_DW_ATOMIC_IMPL "gcc_sync_builtins" +#else +# error "!?" +#endif # if ETHR_SIZEOF_PTR == 4 # define ETHR_DW_NATMC_ALIGN_MASK__ 0x7 @@ -89,15 +93,138 @@ typedef union { # define ETHR_DW_DBG_ALIGNED__(PTR) # endif -#define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_ADDR + +#define ETHR_HAVE_ETHR_NATIVE_DW_ATOMIC_ADDR 1 + static ETHR_INLINE ethr_sint_t * ethr_native_dw_atomic_addr(ethr_native_dw_atomic_t *var) { return (ethr_sint_t *) ETHR_DW_NATMC_MEM__(var); } +#if (ETHR_HAVE___atomic_store_n & (2*ETHR_SIZEOF_PTR)) + +#if (ETHR_GCC_RELAXED_VERSIONS__ & (2*ETHR_SIZEOF_PTR)) + +#define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_SET 1 + +static ETHR_INLINE void +ethr_native_su_dw_atomic_set(ethr_native_dw_atomic_t *var, + ETHR_NATIVE_SU_DW_SINT_T value) +{ + ethr_native_dw_ptr_t p = (ethr_native_dw_ptr_t) ETHR_DW_NATMC_MEM__(var); + ETHR_DW_DBG_ALIGNED__(p); + __atomic_store_n(p, value, __ATOMIC_RELAXED); +} + +#endif /* ETHR_GCC_RELAXED_VERSIONS__ */ + +#if (ETHR_GCC_RELB_VERSIONS__ & (2*ETHR_SIZEOF_PTR)) -#define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_CMPXCHG_MB +#define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_SET_RELB 1 + +static ETHR_INLINE void +ethr_native_su_dw_atomic_set_relb(ethr_native_dw_atomic_t *var, + ETHR_NATIVE_SU_DW_SINT_T value) +{ + ethr_native_dw_ptr_t p = (ethr_native_dw_ptr_t) ETHR_DW_NATMC_MEM__(var); + ETHR_DW_DBG_ALIGNED__(p); + __atomic_store_n(p, value, __ATOMIC_RELEASE); +} + +#endif /* ETHR_GCC_RELB_VERSIONS__ */ + +#endif /* ETHR_HAVE___atomic_store_n */ + +#if (ETHR_HAVE___atomic_load_n & (2*ETHR_SIZEOF_PTR)) + +#if (ETHR_GCC_RELAXED_VERSIONS__ & (2*ETHR_SIZEOF_PTR)) + +#define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_READ 1 + +static ETHR_INLINE ETHR_NATIVE_SU_DW_SINT_T +ethr_native_su_dw_atomic_read(ethr_native_dw_atomic_t *var) +{ + ethr_native_dw_ptr_t p = (ethr_native_dw_ptr_t) ETHR_DW_NATMC_MEM__(var); + ETHR_DW_DBG_ALIGNED__(p); + return __atomic_load_n(p, __ATOMIC_RELAXED); +} + +#endif /* ETHR_GCC_RELAXED_VERSIONS__ */ + +#if ((ETHR_GCC_ACQB_VERSIONS__ & (2*ETHR_SIZEOF_PTR)) \ + & ~ETHR___atomic_load_ACQUIRE_barrier_bug) + +#define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_READ_ACQB 1 + +static ETHR_INLINE ETHR_NATIVE_SU_DW_SINT_T +ethr_native_su_dw_atomic_read_acqb(ethr_native_dw_atomic_t *var) +{ + ethr_native_dw_ptr_t p = (ethr_native_dw_ptr_t) ETHR_DW_NATMC_MEM__(var); + ETHR_DW_DBG_ALIGNED__(p); + return __atomic_load_n(p, __ATOMIC_ACQUIRE); +} + +#endif /* ETHR_GCC_ACQB_VERSIONS__ */ + +#endif /* ETHR_HAVE___atomic_load_n */ + +#if (ETHR_HAVE___atomic_compare_exchange_n & (2*ETHR_SIZEOF_PTR)) + +#if (ETHR_GCC_RELAXED_MOD_VERSIONS__ & (2*ETHR_SIZEOF_PTR)) + +#define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_CMPXCHG 1 + +static ETHR_INLINE ETHR_NATIVE_SU_DW_SINT_T +ethr_native_su_dw_atomic_cmpxchg(ethr_native_dw_atomic_t *var, + ETHR_NATIVE_SU_DW_SINT_T new, + ETHR_NATIVE_SU_DW_SINT_T exp) +{ + ethr_native_dw_ptr_t p = (ethr_native_dw_ptr_t) ETHR_DW_NATMC_MEM__(var); + ETHR_NATIVE_SU_DW_SINT_T xchg = exp; + ETHR_DW_DBG_ALIGNED__(p); + if (__atomic_compare_exchange_n(p, + &xchg, + new, + 0, + __ATOMIC_RELAXED, + __ATOMIC_RELAXED)) + return exp; + return xchg; +} + +#endif /* ETHR_GCC_RELAXED_MOD_VERSIONS__ */ + +#if (ETHR_GCC_ACQB_MOD_VERSIONS__ & (2*ETHR_SIZEOF_PTR)) + +#define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_CMPXCHG_ACQB 1 + +static ETHR_INLINE ETHR_NATIVE_SU_DW_SINT_T +ethr_native_su_dw_atomic_cmpxchg_acqb(ethr_native_dw_atomic_t *var, + ETHR_NATIVE_SU_DW_SINT_T new, + ETHR_NATIVE_SU_DW_SINT_T exp) +{ + ethr_native_dw_ptr_t p = (ethr_native_dw_ptr_t) ETHR_DW_NATMC_MEM__(var); + ETHR_NATIVE_SU_DW_SINT_T xchg = exp; + ETHR_DW_DBG_ALIGNED__(p); + if (__atomic_compare_exchange_n(p, + &xchg, + new, + 0, + __ATOMIC_ACQUIRE, + __ATOMIC_ACQUIRE)) + return exp; + return xchg; +} + +#endif /* ETHR_GCC_ACQB_MOD_VERSIONS__ */ + +#endif /* ETHR_HAVE___atomic_compare_exchange_n */ + +#if ((ETHR_HAVE___sync_val_compare_and_swap & (2*ETHR_SIZEOF_PTR)) \ + & ETHR_GCC_MB_MOD_VERSIONS__) + +#define ETHR_HAVE_ETHR_NATIVE_SU_DW_ATOMIC_CMPXCHG_MB 1 static ETHR_INLINE ETHR_NATIVE_SU_DW_SINT_T ethr_native_su_dw_atomic_cmpxchg_mb(ethr_native_dw_atomic_t *var, @@ -109,7 +236,8 @@ ethr_native_su_dw_atomic_cmpxchg_mb(ethr_native_dw_atomic_t *var, return __sync_val_compare_and_swap(p, old, new); } -#endif /* ETHR_TRY_INLINE_FUNCS */ +#endif /* ETHR_HAVE___sync_val_compare_and_swap */ -#endif /* ETHR_GCC_DW_ATOMIC_H__ */ +#endif /* ETHR_TRY_INLINE_FUNCS */ +#endif /* ETHR_INCLUDE_DW_ATOMIC_IMPL__ */ diff --git a/erts/include/internal/gcc/ethr_membar.h b/erts/include/internal/gcc/ethr_membar.h index 7d428fc68e..d2d36907f3 100644 --- a/erts/include/internal/gcc/ethr_membar.h +++ b/erts/include/internal/gcc/ethr_membar.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2015. All Rights Reserved. * * The 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,56 +18,196 @@ */ /* - * Description: Memory barriers when using gcc's builtins + * Description: Memory barriers when using gcc's __atomic and + * __sync builtins * Author: Rickard Green + * + * Note: The C11 memory model implemented by gcc's __atomic + * builtins does not match the ethread API very well. + * + * A function with a barrier postfix in the ethread atomic + * API needs to ensure that all stores and loads are + * ordered around it according to the semantics of the + * barrier specified. + * + * The C11 aproch is different. The __atomic builtins + * API takes a memory model parameter. Assuming that all + * memory syncronizations using the involved atomic + * variables are made using this API, the synchronizations + * will adhere to the memory models used. That is, you do + * *not* know how loads and stores will be ordered around + * a specific __atomic operation in the general case. You + * only know the total effect of the combination of + * operations issued will adhere to the model. + * + * This limits how we can use the __atomic builtins. What + * we cannot use: + * + * 1. We cannot rely on __atomic_thread_fence() to issue + * any specific memory barriers at all. This regardless + * of memory model parameter passed. That is, we cannot + * use the __atomic_thread_fence() builtin at all. + * + * Why is this? If all __atomic builtins accessing + * memory issue memory barriers, __atomic_thread_fence() + * does not have to issue memory barriers. The + * implementation for the Itanium architecture is an + * example of this. Even using the __ATOMIC_RELAXED + * memory model all __atomic builtins accessing memory + * will issue memory barriers. Due to this no memory + * barriers at all will be issued by + * __atomic_thread_fence() using either one of the + * __ATOMIC_CONSUME, __ATOMIC_ACQUIRE, or + * __ATOMIC_RELEASE memory models. + * + * 2. We cannot rely on any __atomic builtin with the + * __ATOMIC_SEQ_CST memory model parameters to + * issue any specific memory barriers. That is, we + * cannot use these memory models at all. + * + * Why is this? Since all synchronizations is expected + * to be made using the __atomic builtins, memory + * barriers only have to be issued by some of them, + * and you do not know which ones wont issue memory + * barriers. + * + * One can easily be fooled into believing that when + * using the __ATOMIC_SEQ_CST memory model on all + * operations, all operations will issue full memory + * barriers. This is however not the case. The + * implementation for the x86_64 architecture is an + * example of this. Since all operations except loads + * issue full memory barriers, no memory barriers at + * all is issued by loads. This could also be + * implemented by issuing a full memory barrier on + * loads, but no barrier at all on stores. + * + * What can be used then? + * 1. All (legacy) __sync builtins implying full memory + * barriers issued. + * 2. All __atomic builtins using the __ATOMIC_RELAXED + * memory model can, of course, be used. This since + * no ordering guarantees at all are made. + * 3. All __atomic builtins accessing memory using the + * __ATOMIC_ACQUIRE and __ATOMIC_RELEASE memory + * models. This since an __atomic builtin memory + * access using the __ATOMIC_ACQUIRE must at least + * issue an aquire memory barrier and an __atomic + * builtin memory acess with the __ATOMIC_RELEASE + * memory model must at least issue a release memory + * barrier. Otherwise the two can not be paired. + * 4. All __atomic builtins accessing memory using the + * __ATOMIC_CONSUME builtin can be used for the same + * reason __ATOMIC_ACQUIRE can be used. The ethread + * atomic framework implementing the ethread API + * using native implementations does not expect the + * native implementations to produce versions with + * data dependent read barriers, so until the + * framework is changed we haven't got any use for + * for it. + * + * For some architectures we have our own memory barrier + * implementations. We prefer to use these since they + * should be as fine grained as possible. For other + * architectures we use the __sync_synchronize() builtin + * which issue a full memory barrier. For these + * architectures we have to assume that all loads and + * stores can be reordered without limitation. That is, + * unnecessary memory barriers will be issued if such + * reordering actually cannot occur. */ -#ifndef ETHR_GCC_MEMBAR_H__ -#define ETHR_GCC_MEMBAR_H__ +/* + * We prefer to use our own memory barrier implementation if + * such exist instead of using __sync_synchronize()... + */ +#if defined(__i386__) || defined(__x86_64__) +# include "../i386/ethr_membar.h" +#elif defined(__sparc__) +# include "../sparc32/ethr_membar.h" +#elif defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__) +# include "../ppc32/ethr_membar.h" +#elif !defined(ETHR_GCC_ATOMIC_MEMBAR_H__) \ + && (ETHR_HAVE_GCC_ASM_ARM_DMB_INSTRUCTION \ + || ETHR_HAVE___sync_synchronize \ + || (ETHR_HAVE___sync_val_compare_and_swap & 12)) +#define ETHR_GCC_ATOMIC_MEMBAR_H__ #define ETHR_LoadLoad (1 << 0) #define ETHR_LoadStore (1 << 1) #define ETHR_StoreLoad (1 << 2) #define ETHR_StoreStore (1 << 3) +#define ETHR_COMPILER_BARRIER __asm__ __volatile__("" : : : "memory") + +#if ETHR_HAVE_GCC_ASM_ARM_DMB_INSTRUCTION + +static __inline__ __attribute__((__always_inline__)) void +ethr_full_fence__(void) +{ + __asm__ __volatile__("dmb sy" : : : "memory"); +} + +static __inline__ __attribute__((__always_inline__)) void +ethr_store_fence__(void) +{ + __asm__ __volatile__("dmb st" : : : "memory"); +} + +#define ETHR_MEMBAR(B) \ + ETHR_CHOOSE_EXPR((B) == ETHR_StoreStore, ethr_store_fence__(), ethr_full_fence__()) + +#elif ETHR_HAVE___sync_synchronize + +static __inline__ __attribute__((__always_inline__)) void +ethr_full_fence__(void) +{ + /* + * The compiler barriers are here to fix missing clobbers + * in __sync_synchronize() when using buggy LLVM + * implementation of __sync_synchronize(). They + * do not introduce any unnecessary overhead when used + * here, so we use them for all systems. + */ + ETHR_COMPILER_BARRIER; + __sync_synchronize(); + ETHR_COMPILER_BARRIER; +} + +#else /* !ETHR_HAVE___sync_synchronize */ + /* - * According to the documentation __sync_synchronize() will - * issue a full memory barrier. However, __sync_synchronize() - * is known to erroneously be a noop on at least some - * platforms with some gcc versions. This has suposedly been - * fixed in some gcc version, but we don't know from which - * version. Therefore, we only use it when it has been - * verified to work. Otherwise we use the workaround - * below. + * Buggy __sync_synchronize(); call __sync_val_compare_and_swap() + * instead which imply a full memory barrier (and hope that one + * isn't buggy too). */ -#if defined(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP32) +#if (ETHR_HAVE___sync_val_compare_and_swap & 4) # define ETHR_MB_T__ ethr_sint32_t -#elif defined(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP64) +#elif (ETHR_HAVE___sync_val_compare_and_swap & 8) # define ETHR_MB_T__ ethr_sint64_t -#else -# error "No __sync_val_compare_and_swap" #endif -#define ETHR_SYNC_SYNCHRONIZE_WORKAROUND__ \ -do { \ - volatile ETHR_MB_T__ x___ = 0; \ - (void) __sync_val_compare_and_swap(&x___, (ETHR_MB_T__) 0, (ETHR_MB_T__) 1); \ -} while (0) -#define ETHR_COMPILER_BARRIER __asm__ __volatile__("" : : : "memory") +static __inline__ __attribute__((__always_inline__)) void +ethr_full_fence__(void) +{ + volatile ETHR_MB_T__ x = 0; + (void) __sync_val_compare_and_swap(&x, (ETHR_MB_T__) 0, (ETHR_MB_T__) 1); +} -#if defined(__mips__) && ETHR_AT_LEAST_GCC_VSN__(4, 2, 0) -# define ETHR_MEMBAR(B) __sync_synchronize() -# define ETHR_READ_DEPEND_MEMORY_BARRIER __sync_synchronize() -#elif ((defined(__powerpc__) || defined(__ppc__)) \ - && ETHR_AT_LEAST_GCC_VSN__(4, 1, 2)) -# define ETHR_MEMBAR(B) __sync_synchronize() -#else /* Use workaround */ -# define ETHR_MEMBAR(B) \ - ETHR_SYNC_SYNCHRONIZE_WORKAROUND__ -# define ETHR_READ_DEPEND_MEMORY_BARRIER \ - ETHR_SYNC_SYNCHRONIZE_WORKAROUND__ +#endif /* !ETHR_HAVE___sync_synchronize */ + +#ifndef ETHR_MEMBAR +# define ETHR_MEMBAR(B) ethr_full_fence__() #endif +/* + * Define ETHR_READ_DEPEND_MEMORY_BARRIER for all architechtures + * not known to order data dependent loads + */ + +#if !defined(__ia64__) && !defined(__arm__) +# define ETHR_READ_DEPEND_MEMORY_BARRIER ETHR_MEMBAR(ETHR_LoadLoad) +#endif -#endif /* ETHR_GCC_MEMBAR_H__ */ +#endif /* ETHR_GCC_ATOMIC_MEMBAR_H__ */ diff --git a/erts/include/internal/gcc/ethread.h b/erts/include/internal/gcc/ethread.h index 365a3535cf..be3e1da90e 100644 --- a/erts/include/internal/gcc/ethread.h +++ b/erts/include/internal/gcc/ethread.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2015. All Rights Reserved. * * The 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,20 +18,292 @@ */ /* - * Description: Native atomic ethread support when using gcc + * Description: Native atomic ethread support when using gcc's __atomic + * and __sync builtins * Author: Rickard Green */ -#ifndef ETHREAD_GCC_H__ -#define ETHREAD_GCC_H__ - -#if defined(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP32) \ - || defined(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP64) +#if !defined(ETHREAD_GCC_NATIVE_H__) && ETHR_GCC_COMPILER +#define ETHREAD_GCC_NATIVE_H__ #ifndef ETHR_MEMBAR # include "ethr_membar.h" #endif +#define ETHR_GCC_VERSIONS_MASK__ 28 + +#undef ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE__ +#undef ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE_RELB__ +#undef ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD__ +#undef ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD_ACQB__ +#undef ETHR_GCC_RELAXED_VERSIONS__ +#undef ETHR_GCC_RELAXED_MOD_VERSIONS__ +#undef ETHR_GCC_ACQB_VERSIONS__ +#undef ETHR_GCC_ACQB_MOD_VERSIONS__ +#undef ETHR_GCC_RELB_VERSIONS__ +#undef ETHR_GCC_RELB_MOD_VERSIONS__ +#undef ETHR_GCC_MB_MOD_VERSIONS__ + +/* + * True GNU GCCs before version 4.8 do not emit a memory barrier + * after the load in the __atomic_load_n(_, __ATOMIC_ACQUIRE) + * case (which is needed on most architectures). + */ +#undef ETHR___atomic_load_ACQUIRE_barrier_bug +#if ETHR_GCC_COMPILER != ETHR_GCC_COMPILER_TRUE +/* + * A gcc compatible compiler. We have no information + * about the existence of this bug, but we assume + * that it is not impossible that it could have + * been "inherited". Therefore, until we are certain + * that the bug does not exist, we assume that it + * does. + */ +# define ETHR___atomic_load_ACQUIRE_barrier_bug ETHR_GCC_VERSIONS_MASK__ +#elif !ETHR_AT_LEAST_GCC_VSN__(4, 8, 0) +/* True gcc of version < 4.8, i.e., bug exist... */ +# define ETHR___atomic_load_ACQUIRE_barrier_bug ETHR_GCC_VERSIONS_MASK__ +#else /* True gcc of version >= 4.8 */ +/* + * Sizes less than or equal to word size have been fixed, + * but double word size has not been fixed. + */ +# if ETHR_SIZEOF_PTR == 8 +# define ETHR___atomic_load_ACQUIRE_barrier_bug \ + (~(8|4) & ETHR_GCC_VERSIONS_MASK__) +# elif ETHR_SIZEOF_PTR == 4 +# define ETHR___atomic_load_ACQUIRE_barrier_bug \ + (~4 & ETHR_GCC_VERSIONS_MASK__) +# else +# error word size not supported +# endif +#endif + +#define ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE__ 0 +#define ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE_RELB__ 0 +#define ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD__ 0 +#define ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD_ACQB__ 0 +#define ETHR_GCC_RELAXED_VERSIONS__ ETHR_GCC_VERSIONS_MASK__ +#define ETHR_GCC_RELAXED_MOD_VERSIONS__ ETHR_GCC_VERSIONS_MASK__ + +#if ETHR_TRUST_GCC_ATOMIC_BUILTINS_MEMORY_BARRIERS +# define ETHR_GCC_ACQB_VERSIONS__ ETHR_GCC_VERSIONS_MASK__ +# define ETHR_GCC_ACQB_MOD_VERSIONS__ ETHR_GCC_VERSIONS_MASK__ +# define ETHR_GCC_RELB_VERSIONS__ ETHR_GCC_VERSIONS_MASK__ +# define ETHR_GCC_RELB_MOD_VERSIONS__ ETHR_GCC_VERSIONS_MASK__ +#else +/* + * This is currently the default (on most platforms) since + * we've seen too many memory barrier bugs produced by gcc... + */ +# define ETHR_GCC_ACQB_VERSIONS__ 0 +# define ETHR_GCC_ACQB_MOD_VERSIONS__ 0 +# define ETHR_GCC_RELB_VERSIONS__ 0 +# define ETHR_GCC_RELB_MOD_VERSIONS__ 0 +#endif +/* + * In the general case we do not want any full barrier versions + * if we can implement more relaxed ones (using __atomic_* builtins). + * This since the implementations normally need extra memory barrier + * instructions to implement these. The x86/x86_64 implementations + * are an exception see below. + */ +#define ETHR_GCC_MB_MOD_VERSIONS__ \ + (ETHR_GCC_VERSIONS_MASK__ & ~ETHR_HAVE___atomic_compare_exchange_n) + +#if ETHR_SIZEOF_PTR == 8 +# define ETHR_GCC_VOLATILE_BIT_MASK__ 12 +#elif ETHR_SIZEOF_PTR == 4 +# define ETHR_GCC_VOLATILE_BIT_MASK__ 4 +#endif + +#if defined(__i386__) || defined(__x86_64__) || defined(__sparc__) \ + || defined(__powerpc__) || defined(__ppc__) || defined(__mips__) \ + || defined(__alpha__) || defined(__ia64__) + +/* + * Aligned volatile stores and loads of data smaller + * than or equal to word size are atomic... + */ +# undef ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE__ +# define ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE__ ETHR_GCC_VOLATILE_BIT_MASK__ +# undef ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD__ +# define ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD__ ETHR_GCC_VOLATILE_BIT_MASK__ + +#elif defined(__arm__) + +/* volatile stores are problematic on some machines... */ +# undef ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD__ +# define ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD__ ETHR_GCC_VOLATILE_BIT_MASK__ + +#endif + +#if defined(__ia64__) + +/* Volatile stores produce stores with release barriers. */ +# undef ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE_RELB__ +# define ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE_RELB__ ETHR_GCC_VOLATILE_BIT_MASK__ + +/* Volatile loads produce loads with acquire barrier. */ +# undef ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD_ACQB__ +# define ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD_ACQB__ ETHR_GCC_VOLATILE_BIT_MASK__ + +/* + * We trust gcc to produce acquire/release barriers on itanium. + * Since all atomic ops also have at least acquire or release + * barriers (also when passed the relaxed memory model) it + * would be very inefficient not to use these as native + * barriers on Itanium. + */ +# undef ETHR_GCC_ACQB_VERSIONS__ +# define ETHR_GCC_ACQB_VERSIONS__ ETHR_GCC_VERSIONS_MASK__ +# undef ETHR_GCC_ACQB_MOD_VERSIONS__ +# define ETHR_GCC_ACQB_MOD_VERSIONS__ ETHR_GCC_VERSIONS_MASK__ +# undef ETHR_GCC_RELB_VERSIONS__ +# define ETHR_GCC_RELB_VERSIONS__ ETHR_GCC_VERSIONS_MASK__ +# undef ETHR_GCC_RELB_MOD_VERSIONS__ +# define ETHR_GCC_RELB_MOD_VERSIONS__ ETHR_GCC_VERSIONS_MASK__ + +/* + * Itanium is not effected by the load acquire + * bug since the barrier is part of the instruction + * on Itanium (ld.acq), and not a separate instruction + * as on most platforms. + */ +# undef ETHR___atomic_load_ACQUIRE_barrier_bug +# define ETHR___atomic_load_ACQUIRE_barrier_bug 0 + +/* + * No point exposing relaxed versions since they are + * implemended using either acquire or release + * barriers. + */ +# undef ETHR_GCC_RELAXED_VERSIONS__ +# define ETHR_GCC_RELAXED_VERSIONS__ 0 + +/* #endif defined(__ia64__) */ +#elif defined(__i386__) || defined(__x86_64__) + +/* + * Want full barrier versions of all modification + * operations since all of these are implemented + * using locked instructions implying full memory + * barriers. + */ +# undef ETHR_GCC_MB_MOD_VERSIONS__ +# define ETHR_GCC_MB_MOD_VERSIONS__ ETHR_HAVE___sync_val_compare_and_swap + +/* + * No point exposing acquire/release versions + * when we got full memory barrier versions + * of modification operations since all of these + * are implemented using locked instructions + * implying full memory barriers. + */ +# if ETHR_GCC_ACQB_MOD_VERSIONS__ +# undef ETHR_GCC_ACQB_MOD_VERSIONS__ +# define ETHR_GCC_ACQB_MOD_VERSIONS__ \ + (ETHR_GCC_VERSIONS_MASK__ & ~ETHR_HAVE___sync_val_compare_and_swap) +# endif +# if ETHR_GCC_RELB_MOD_VERSIONS__ +# undef ETHR_GCC_RELB_MOD_VERSIONS__ +# define ETHR_GCC_RELB_MOD_VERSIONS__ \ + (ETHR_GCC_VERSIONS_MASK__ & ~ETHR_HAVE___sync_val_compare_and_swap) +# endif + +# ifdef ETHR_X86_OUT_OF_ORDER + +/* See above... */ +# undef ETHR_GCC_RELAXED_MOD_VERSIONS__ +# define ETHR_GCC_RELAXED_MOD_VERSIONS__ 0 + +# else /* !ETHR_X86_OUT_OF_ORDER, i.e., we don't use any x86-OOO instructions... */ + +/* + * Not effected by the load acquire barrier bug, + * since no barrier at all is needed for a load + * acquire... + */ +# undef ETHR___atomic_load_ACQUIRE_barrier_bug +# define ETHR___atomic_load_ACQUIRE_barrier_bug 0 + +/* Stores imply release barriers semantics. */ +# undef ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE_RELB__ +# define ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE_RELB__ ETHR_GCC_VOLATILE_BIT_MASK__ + +/* Loads imply acquire barrier semantics. */ +# undef ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD_ACQB__ +# define ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD_ACQB__ ETHR_GCC_VOLATILE_BIT_MASK__ + +/* + * Trust load acquire and store release for sizes + * where volatile operation implies these barrier + * semantics since no barriers are needed. + */ +# if !ETHR_GCC_ACQB_VERSIONS__ +# undef ETHR_GCC_ACQB_VERSIONS__ +# define ETHR_GCC_ACQB_VERSIONS__ ETHR_GCC_VOLATILE_BIT_MASK__ +# endif +# if !ETHR_GCC_RELB_VERSIONS__ +# undef ETHR_GCC_RELB_VERSIONS__ +# define ETHR_GCC_RELB_VERSIONS__ ETHR_GCC_VOLATILE_BIT_MASK__ +# endif + +/* + * No point exposing relaxed versions at all since + * all mod operations are implemented with locked + * instructions implying full memory barriers and + * volatile store and load imply release and + * acquire barrier semantics. + */ +# undef ETHR_GCC_RELAXED_VERSIONS__ +# define ETHR_GCC_RELAXED_VERSIONS__ 0 + +# endif /* !ETHR_X86_OUT_OF_ORDER */ + +/* #endif defined(__i386__) || defined(__x86_64__) */ +#elif defined(__powerpc__) || defined(__ppc__) + +# if !defined(ETHR_PPC_HAVE_LWSYNC) +/* + * Release barriers are typically implemented using + * the lwsync instruction. We want our runtime + * configure test to determine if the lwsync + * instruction is available on the system or not + * before we use it. Therefore, do not implement any + * native ops using the __ATOMIC_RELEASE model. + */ +# undef ETHR_GCC_RELB_VERSIONS__ +# define ETHR_GCC_RELB_VERSIONS__ 0 +# if defined(ETHR_GCC_IMPLEMENT_ACQB_USING_LWSYNC) +/* + * Acquire barriers are usually implemented by other means + * than lwsync, but can be implemented using lwsync. Define + * ETHR_GCC_IMPLEMENT_ACQB_USING_LWSYNC if acquire barriers + * are implemented using lwsync. + */ +# undef ETHR_GCC_ACQB_VERSIONS__ +# define ETHR_GCC_ACQB_VERSIONS__ 0 +# endif +# endif + +#endif /* defined(__powerpc__) || defined(__ppc__) */ + +#if !ETHR_GCC_RELAXED_VERSIONS__ +# undef ETHR_GCC_RELAXED_MOD_VERSIONS__ +# define ETHR_GCC_RELAXED_MOD_VERSIONS__ 0 +#endif + +#if !ETHR_GCC_ACQB_VERSIONS__ +# undef ETHR_GCC_ACQB_MOD_VERSIONS__ +# define ETHR_GCC_ACQB_MOD_VERSIONS__ 0 +#endif + +#if !ETHR_GCC_RELB_VERSIONS__ +# undef ETHR_GCC_RELB_MOD_VERSIONS__ +# define ETHR_GCC_RELB_MOD_VERSIONS__ 0 +#endif + #if !defined(ETHR_HAVE_NATIVE_ATOMIC32) # define ETHR_ATOMIC_WANT_32BIT_IMPL__ # include "ethr_atomic.h" @@ -42,12 +314,51 @@ # include "ethr_atomic.h" #endif +#if defined(__x86_64__) +/* + * No instructions available for native implementation + * of these for dw-atomics... + */ +# undef ETHR_GCC_RELAXED_VERSIONS__ +# define ETHR_GCC_RELAXED_VERSIONS__ 0 +# undef ETHR_GCC_ACQB_VERSIONS__ +# define ETHR_GCC_ACQB_VERSIONS__ 0 +# undef ETHR_GCC_RELB_VERSIONS__ +# define ETHR_GCC_RELB_VERSIONS__ 0 +#endif + +#if !ETHR_GCC_RELAXED_VERSIONS__ +# undef ETHR_GCC_RELAXED_MOD_VERSIONS__ +# define ETHR_GCC_RELAXED_MOD_VERSIONS__ 0 +#endif + +#if !ETHR_GCC_ACQB_VERSIONS__ +# undef ETHR_GCC_ACQB_MOD_VERSIONS__ +# define ETHR_GCC_ACQB_MOD_VERSIONS__ 0 +#endif + +#if !ETHR_GCC_RELB_VERSIONS__ +# undef ETHR_GCC_RELB_MOD_VERSIONS__ +# define ETHR_GCC_RELB_MOD_VERSIONS__ 0 +#endif + #if (!defined(ETHR_HAVE_NATIVE_DW_ATOMIC) \ && !(ETHR_SIZEOF_PTR == 4 && defined(ETHR_HAVE_NATIVE_ATOMIC64)) \ && !(ETHR_SIZEOF_PTR == 8 && defined(ETHR_HAVE_NATIVE_ATOMIC128))) # include "ethr_dw_atomic.h" #endif -#endif +#undef ETHR___atomic_load_ACQUIRE_barrier_bug +#undef ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE__ +#undef ETHR_GCC_VOLATILE_STORE_IS_ATOMIC_STORE_RELB__ +#undef ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD__ +#undef ETHR_GCC_VOLATILE_LOAD_IS_ATOMIC_LOAD_ACQB__ +#undef ETHR_GCC_RELAXED_VERSIONS__ +#undef ETHR_GCC_RELB_VERSIONS__ +#undef ETHR_GCC_RELB_VERSIONS__ +#undef ETHR_GCC_RELAXED_MOD_VERSIONS__ +#undef ETHR_GCC_ACQB_MOD_VERSIONS__ +#undef ETHR_GCC_RELB_MOD_VERSIONS__ +#undef ETHR_GCC_MB_MOD_VERSIONS__ -#endif +#endif /* ETHREAD_GCC_NATIVE_H__ */ -- cgit v1.2.3 From 86d605807356f79f3484c36cba632ff88d4e2417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 13 Jan 2015 12:13:43 +0100 Subject: erts: Use emergency close to close epmd Closes all open socket before writing crashdump to file. --- erts/emulator/beam/global.h | 1 + erts/emulator/beam/io.c | 20 +++++++++++++++ erts/emulator/drivers/common/inet_drv.c | 2 +- erts/emulator/sys/unix/sys.c | 43 +++++++-------------------------- 4 files changed, 31 insertions(+), 35 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index d25daaf7b8..32a2dc43e8 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -853,6 +853,7 @@ Uint erts_port_ioq_size(Port *pp); void erts_stale_drv_select(Eterm, ErlDrvPort, ErlDrvEvent, int, int); Port *erts_get_heart_port(void); +void erts_emergency_close_ports(void); #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT) void erts_lcnt_enable_io_lock_count(int enable); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 4ae8fafb2c..3316654790 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -7569,3 +7569,23 @@ Port *erts_get_heart_port(void) return NULL; } + +void erts_emergency_close_ports(void) +{ + int ix, max = erts_ptab_max(&erts_port); + + for (ix = 0; ix < max; ix++) { + Port *port = erts_pix2port(ix); + + if (!port) + continue; + /* only examine undead or alive ports */ + if (erts_atomic32_read_nob(&port->state) & ERTS_PORT_SFLGS_DEAD) + continue; + + /* emergency close socket */ + if (port->drv_ptr->emergency_close) { + port->drv_ptr->emergency_close((ErlDrvData) port->drv_data); + } + } +} diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index be02a4b243..33d37f8a6a 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -8213,7 +8213,7 @@ static void inet_emergency_close(ErlDrvData data) tcp_descriptor* tcp_desc = (tcp_descriptor*)data; inet_descriptor* desc = INETP(tcp_desc); DEBUGF(("inet_emergency_close(%ld) {s=%d\r\n", - (long)desc->inet.port, desc->inet.s)); + (long)desc->port, desc->s)); if (desc->s != INVALID_SOCKET) { sock_close(desc->s); } diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 0d677d5f34..24f229ae68 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -202,8 +202,6 @@ static erts_smp_atomic_t sys_misc_mem_sz; #if defined(ERTS_SMP) static void smp_sig_notify(char c); static int sig_notify_fds[2] = {-1, -1}; -#elif defined(USE_THREADS) -static int async_fd[2]; #endif #if CHLDWTHR || defined(ERTS_SMP) @@ -719,14 +717,13 @@ static ERTS_INLINE int prepare_crash_dump(int secs) { #define NUFBUF (3) - int i, max; + int i; char env[21]; /* enough to hold any 64-bit integer */ size_t envsz; DeclareTmpHeapNoproc(heap,NUFBUF); Port *heart_port; Eterm *hp = heap; Eterm list = NIL; - int heart_fd[2] = {-1,-1}; int has_heart = 0; UseTmpHeapNoproc(NUFBUF); @@ -749,43 +746,21 @@ prepare_crash_dump(int secs) alarm((unsigned int)secs); } - if (heart_port) { - /* hearts input fd - * We "know" drv_data is the in_fd since the port is started with read|write - */ - heart_fd[0] = (int)heart_port->drv_data; - heart_fd[1] = (int)driver_data[heart_fd[0]].ofd; - has_heart = 1; + /* close all viable sockets via emergency close callbacks. + * Specifically we want to close epmd sockets. + */ - list = CONS(hp, make_small(8), list); hp += 2; + erts_emergency_close_ports(); + if (heart_port) { + has_heart = 1; + list = CONS(hp, make_small(8), list); hp += 2; /* send to heart port, CMD = 8, i.e. prepare crash dump =o */ erts_port_output(NULL, ERTS_PORT_SIG_FLG_FORCE_IMM_CALL, heart_port, heart_port->common.id, list, NULL); } - /* Make sure we unregister at epmd (unknown fd) and get at least - one free filedescriptor (for erl_crash.dump) */ - - max = max_files; - if (max < 1024) - max = 1024; - for (i = 3; i < max; i++) { -#if defined(ERTS_SMP) - /* We don't want to close the signal notification pipe... */ - if (i == sig_notify_fds[0] || i == sig_notify_fds[1]) - continue; -#elif defined(USE_THREADS) - /* We don't want to close the async notification pipe... */ - if (i == async_fd[0] || i == async_fd[1]) - continue; -#endif - /* We don't want to close our heart yet ... */ - if (i == heart_fd[0] || i == heart_fd[1]) - continue; - - close(i); - } + /* FIXME: Reserve one file descriptor */ envsz = sizeof(env); i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz); -- cgit v1.2.3 From 5f036bc287f325b3367f8437997534c44812078f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 13 Jan 2015 15:21:20 +0100 Subject: erts: Reserve a file descriptor for the crashdump file --- erts/emulator/sys/unix/sys.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 24f229ae68..eff64b2c11 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -244,6 +244,8 @@ static void note_child_death(int, int); static void* child_waiter(void *); #endif +static int crashdump_companion_cube_fd = -1; + /********************* General functions ****************************/ /* This is used by both the drivers and general I/O, must be set early */ @@ -573,6 +575,14 @@ erts_sys_pre_init(void) close(fd); } + /* We need a file descriptor to close in the crashdump creation. + * We close this one to be sure we can get a fd for our real file ... + * so, we create one here ... a stone to carry all the way home. + */ + + crashdump_companion_cube_fd = open("/dev/null", O_RDONLY); + + /* don't lose it, there will be cake */ } void @@ -760,7 +770,8 @@ prepare_crash_dump(int secs) heart_port->common.id, list, NULL); } - /* FIXME: Reserve one file descriptor */ + /* Make sure we have a fd for our crashdump file. */ + close(crashdump_companion_cube_fd); envsz = sizeof(env); i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz); -- cgit v1.2.3 From 1520bca342e8433089841d2c79d3d862d06479e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 13 Jan 2015 17:04:00 +0100 Subject: erts: Don't lookup invalid port for crashdump handling --- erts/emulator/beam/io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 3316654790..2ac27f024e 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -7558,7 +7558,7 @@ Port *erts_get_heart_port(void) if (!port) continue; /* only examine undead or alive ports */ - if (erts_atomic32_read_nob(&port->state) & ERTS_PORT_SFLGS_DEAD) + if (erts_atomic32_read_nob(&port->state) & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) continue; /* immediate atom compare */ reg = port->common.u.alive.reg; @@ -7580,7 +7580,7 @@ void erts_emergency_close_ports(void) if (!port) continue; /* only examine undead or alive ports */ - if (erts_atomic32_read_nob(&port->state) & ERTS_PORT_SFLGS_DEAD) + if (erts_atomic32_read_nob(&port->state) & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) continue; /* emergency close socket */ -- cgit v1.2.3 From 86e4d8ead48501f00466d685c157eaa0921d96a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 15 Jan 2015 14:40:50 +0100 Subject: erts: Check driver version before assigning callback --- erts/emulator/beam/io.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 2ac27f024e..012a7d1a4b 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -7349,6 +7349,8 @@ no_stop_select_callback(ErlDrvEvent event, void* private) erts_send_error_to_logger_nogl(dsbufp); } +#define IS_DRIVER_VERSION_GE(DE,MAJOR,MINOR) \ + ((DE)->major_version >= (MAJOR) && (DE)->minor_version >= (MINOR)) static int init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle) @@ -7396,7 +7398,7 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle) drv->timeout = de->timeout ? de->timeout : no_timeout_callback; drv->ready_async = de->ready_async; drv->process_exit = de->process_exit; - drv->emergency_close = de->emergency_close; + drv->emergency_close = IS_DRIVER_VERSION_GE(de,3,2) ? de->emergency_close : NULL; if (de->stop_select) drv->stop_select = de->stop_select; else @@ -7415,6 +7417,8 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle) } } +#undef IS_DRIVER_VERSION_GE + void erts_destroy_driver(erts_driver_t *drv) { -- cgit v1.2.3 From f9886a6d15c396ae1e0e69a58fb265c4c338ac93 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 11 Jan 2015 16:29:47 +0100 Subject: hipe: remove HIPE_ALLOC_CODE macro The HIPE_ALLOC_CODE macro in the HiPE runtime was introduced ages ago to allow x86 and amd64 to switch from erts_alloc() to an mmap() implementation with proper flag setting. Nowadays the macro is identical on all platforms, and serves no purpose. Delete the macro, move the hipe_alloc_code() prototype to hipe_arch.h, and simplify hipe_bifs_enter_code_2(). --- erts/emulator/hipe/hipe_arch.h | 1 + erts/emulator/hipe/hipe_arm.h | 4 ---- erts/emulator/hipe/hipe_bif0.c | 8 +------- erts/emulator/hipe/hipe_ppc.h | 4 ---- erts/emulator/hipe/hipe_sparc.h | 4 ---- erts/emulator/hipe/hipe_x86.h | 4 ---- 6 files changed, 2 insertions(+), 23 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_arch.h b/erts/emulator/hipe/hipe_arch.h index 04ed980126..b45209b3f7 100644 --- a/erts/emulator/hipe/hipe_arch.h +++ b/erts/emulator/hipe/hipe_arch.h @@ -29,6 +29,7 @@ extern void hipe_patch_load_fe(Uint *address, Uint value); extern int hipe_patch_insn(void *address, Uint value, Eterm type); extern int hipe_patch_call(void *callAddress, void *destAddress, void *trampoline); +extern void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *p); extern void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity); #if defined(__sparc__) diff --git a/erts/emulator/hipe/hipe_arm.h b/erts/emulator/hipe/hipe_arm.h index 19f2a986cf..b9cd1a750c 100644 --- a/erts/emulator/hipe/hipe_arm.h +++ b/erts/emulator/hipe/hipe_arm.h @@ -40,8 +40,4 @@ static __inline__ int hipe_word32_address_ok(void *address) extern void hipe_arm_inc_stack(void); -/* for hipe_bifs_enter_code_2 */ -extern void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *p); -#define HIPE_ALLOC_CODE(n,c,t,p) hipe_alloc_code((n),(c),(t),(p)) - #endif /* HIPE_ARM_H */ diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 9eb0b88ced..dc45e60411 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -397,15 +397,9 @@ BIF_RETTYPE hipe_bifs_enter_code_2(BIF_ALIST_2) ASSERT(bitoffs == 0); ASSERT(bitsize == 0); trampolines = NIL; -#ifdef HIPE_ALLOC_CODE - address = HIPE_ALLOC_CODE(nrbytes, BIF_ARG_2, &trampolines, BIF_P); + address = hipe_alloc_code(nrbytes, BIF_ARG_2, &trampolines, BIF_P); if (!address) BIF_ERROR(BIF_P, BADARG); -#else - if (is_not_nil(BIF_ARG_2)) - BIF_ERROR(BIF_P, BADARG); - address = erts_alloc(ERTS_ALC_T_HIPE, nrbytes); -#endif memcpy(address, bytes, nrbytes); hipe_flush_icache_range(address, nrbytes); hp = HAlloc(BIF_P, 3); diff --git a/erts/emulator/hipe/hipe_ppc.h b/erts/emulator/hipe/hipe_ppc.h index 66000c1846..e9d3e6564b 100644 --- a/erts/emulator/hipe/hipe_ppc.h +++ b/erts/emulator/hipe/hipe_ppc.h @@ -64,10 +64,6 @@ AEXTERN(void,hipe_ppc_inc_stack,(void)); extern void hipe_ppc_inc_stack(void); /* we don't have the AEXTERN() fallback :-( */ #endif -/* for hipe_bifs_enter_code_2 */ -extern void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *p); -#define HIPE_ALLOC_CODE(n,c,t,p) hipe_alloc_code((n),(c),(t),(p)) - #if !defined(__powerpc64__) extern const unsigned int fconv_constant[]; #endif diff --git a/erts/emulator/hipe/hipe_sparc.h b/erts/emulator/hipe/hipe_sparc.h index 1134b86004..2d92ca3ca8 100644 --- a/erts/emulator/hipe/hipe_sparc.h +++ b/erts/emulator/hipe/hipe_sparc.h @@ -47,8 +47,4 @@ static __inline__ int hipe_word32_address_ok(void *address) extern void hipe_sparc_inc_stack(void); -/* for hipe_bifs_enter_code_2 */ -extern void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *p); -#define HIPE_ALLOC_CODE(n,c,t,p) hipe_alloc_code((n),(c),(t),(p)) - #endif /* HIPE_SPARC_H */ diff --git a/erts/emulator/hipe/hipe_x86.h b/erts/emulator/hipe/hipe_x86.h index 97f09e38cd..f29117d0c4 100644 --- a/erts/emulator/hipe/hipe_x86.h +++ b/erts/emulator/hipe/hipe_x86.h @@ -53,8 +53,4 @@ extern void nbif_inc_stack_0(void); extern void nbif_handle_fp_exception(void); #endif -/* for hipe_bifs_enter_code_2 */ -extern void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *p); -#define HIPE_ALLOC_CODE(n,c,t,p) hipe_alloc_code((n),(c),(t),(p)) - #endif /* HIPE_X86_H */ -- cgit v1.2.3 From 14d585fdcafad61a377feeba1035d6ddcfaf0248 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 11 Jan 2015 16:40:06 +0100 Subject: hipe: remove two obsolete BIFs The hipe_bifs:make_native_stub/2 and hipe_bifs:get_emu_address/1 BIFs were originally used by hipe_unified_loader.erl, but the code been obsolete and disabled for ages. Remove the BIFs and all references to them. In hipe_unified_loader.erl, remove the no-op emu_make_stubs/1 function. --- erts/emulator/hipe/hipe_amd64.c | 1 - erts/emulator/hipe/hipe_bif0.tab | 2 -- 2 files changed, 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_amd64.c b/erts/emulator/hipe/hipe_amd64.c index 16c597e7b4..627ba7603d 100644 --- a/erts/emulator/hipe/hipe_amd64.c +++ b/erts/emulator/hipe/hipe_amd64.c @@ -224,7 +224,6 @@ void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process * return alloc_code(nrbytes); } - /* Make stub for native code calling exported beam function. */ void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab index 2514b1c3a5..d715a0914b 100644 --- a/erts/emulator/hipe/hipe_bif0.tab +++ b/erts/emulator/hipe/hipe_bif0.tab @@ -49,7 +49,6 @@ bif hipe_bifs:constants_size/0 bif hipe_bifs:merge_term/1 bif hipe_bifs:fun_to_address/1 -#bif hipe_bifs:get_emu_address/1 bif hipe_bifs:set_native_address/3 #bif hipe_bifs:address_to_fun/1 @@ -72,7 +71,6 @@ bif hipe_bifs:term_to_word/1 bif hipe_bifs:get_fe/2 bif hipe_bifs:set_native_address_in_fe/2 -#bif hipe_bifs:make_native_stub/2 bif hipe_bifs:find_na_or_make_stub/2 bif hipe_bifs:check_crc/1 -- cgit v1.2.3 From 3d41006a0e17d57fef4324c2a49c778f7a4a3390 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 11 Jan 2015 16:48:20 +0100 Subject: hipe: improve error handling at code allocation failure --- erts/emulator/hipe/hipe_amd64.c | 17 ++++++++++------- erts/emulator/hipe/hipe_arm.c | 2 ++ erts/emulator/hipe/hipe_bif0.c | 14 ++++++++++++-- erts/emulator/hipe/hipe_ppc.c | 4 ++++ erts/emulator/hipe/hipe_sparc.c | 17 ++++++++++------- erts/emulator/hipe/hipe_x86.c | 17 ++++++++++------- 6 files changed, 48 insertions(+), 23 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_amd64.c b/erts/emulator/hipe/hipe_amd64.c index 627ba7603d..63646825b2 100644 --- a/erts/emulator/hipe/hipe_amd64.c +++ b/erts/emulator/hipe/hipe_amd64.c @@ -125,7 +125,7 @@ static void atexit_alloc_code_stats(void) #define MAP_ANONYMOUS MAP_ANON #endif -static void morecore(unsigned int alloc_bytes) +static int morecore(unsigned int alloc_bytes) { unsigned int map_bytes; char *map_hint, *map_start; @@ -174,10 +174,9 @@ static void morecore(unsigned int alloc_bytes) abort(); } #endif - if (map_start == MAP_FAILED) { - perror("mmap"); - abort(); - } + if (map_start == MAP_FAILED) + return -1; + ALLOC_CODE_STATS(total_mapped += map_bytes); /* Merge adjacent mappings, so the trailing portion of the previous @@ -197,6 +196,8 @@ static void morecore(unsigned int alloc_bytes) } ALLOC_CODE_STATS(atexit_alloc_code_stats()); + + return 0; } static void *alloc_code(unsigned int alloc_bytes) @@ -206,8 +207,8 @@ static void *alloc_code(unsigned int alloc_bytes) /* Align function entries. */ alloc_bytes = (alloc_bytes + 3) & ~3; - if (code_bytes < alloc_bytes) - morecore(alloc_bytes); + if (code_bytes < alloc_bytes && morecore(alloc_bytes) != 0) + return NULL; ALLOC_CODE_STATS(++nr_allocs); ALLOC_CODE_STATS(total_alloc += alloc_bytes); res = code_next; @@ -252,6 +253,8 @@ void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) ((P_CALLEE_EXP + 4) >= 128 ? 3 : 0) + (P_ARITY >= 128 ? 3 : 0); codep = code = alloc_code(codeSize); + if (!code) + return NULL; /* movl $callee_exp, P_CALLEE_EXP(%ebp); 3 or 6 bytes, plus 4 */ codep[0] = 0xc7; diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c index 165eb543c8..c0c6305c68 100644 --- a/erts/emulator/hipe/hipe_arm.c +++ b/erts/emulator/hipe/hipe_arm.c @@ -283,6 +283,8 @@ void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) */ code = alloc_stub(4, &tramp_callemu); + if (!code) + return NULL; callemu_offset = ((int)&nbif_callemu - ((int)&code[2] + 8)) >> 2; if (!(callemu_offset >= -0x00800000 && callemu_offset <= 0x007FFFFF)) { callemu_offset = ((int)tramp_callemu - ((int)&code[2] + 8)) >> 2; diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index dc45e60411..9e5830f345 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -398,8 +398,16 @@ BIF_RETTYPE hipe_bifs_enter_code_2(BIF_ALIST_2) ASSERT(bitsize == 0); trampolines = NIL; address = hipe_alloc_code(nrbytes, BIF_ARG_2, &trampolines, BIF_P); - if (!address) - BIF_ERROR(BIF_P, BADARG); + if (!address) { + Uint nrcallees; + + if (is_tuple(BIF_ARG_2)) + nrcallees = arityval(tuple_val(BIF_ARG_2)[0]); + else + nrcallees = 0; + erl_exit(1, "%s: failed to allocate %lu bytes and %lu trampolines\r\n", + __func__, (unsigned long)nrbytes, (unsigned long)nrcallees); + } memcpy(address, bytes, nrbytes); hipe_flush_icache_range(address, nrbytes); hp = HAlloc(BIF_P, 3); @@ -1274,6 +1282,8 @@ static void *hipe_make_stub(Eterm m, Eterm f, unsigned int arity, int is_remote) export_entry = erts_export_get_or_make_stub(m, f, arity); StubAddress = hipe_make_native_stub(export_entry, arity); + if (!StubAddress) + erl_exit(1, "hipe_make_stub: code allocation failed\r\n"); return StubAddress; } diff --git a/erts/emulator/hipe/hipe_ppc.c b/erts/emulator/hipe/hipe_ppc.c index 4dc26cdbc8..1eaa9f6855 100644 --- a/erts/emulator/hipe/hipe_ppc.c +++ b/erts/emulator/hipe/hipe_ppc.c @@ -293,6 +293,8 @@ void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) abort(); code = alloc_stub(7); + if (!code) + return NULL; /* addis r12,0,callee_exp@highest */ code[0] = 0x3d800000 | (((unsigned long)callee_exp >> 48) & 0xffff); @@ -381,6 +383,8 @@ void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) abort(); code = alloc_stub(4); + if (!code) + return NULL; /* addi r12,0,callee_exp@l */ code[0] = 0x39800000 | ((unsigned long)callee_exp & 0xFFFF); diff --git a/erts/emulator/hipe/hipe_sparc.c b/erts/emulator/hipe/hipe_sparc.c index 2052aa8498..fea3b623a9 100644 --- a/erts/emulator/hipe/hipe_sparc.c +++ b/erts/emulator/hipe/hipe_sparc.c @@ -130,7 +130,7 @@ static void atexit_alloc_code_stats(void) #define ALLOC_CODE_STATS(X) do{}while(0) #endif -static void morecore(unsigned int alloc_bytes) +static int morecore(unsigned int alloc_bytes) { unsigned int map_bytes; char *map_hint, *map_start; @@ -158,10 +158,9 @@ static void morecore(unsigned int alloc_bytes) #endif , -1, 0); - if (map_start == MAP_FAILED) { - perror("mmap"); - abort(); - } + if (map_start == MAP_FAILED) + return -1; + ALLOC_CODE_STATS(total_mapped += map_bytes); /* Merge adjacent mappings, so the trailing portion of the previous @@ -177,6 +176,8 @@ static void morecore(unsigned int alloc_bytes) } ALLOC_CODE_STATS(atexit_alloc_code_stats()); + + return 0; } static void *alloc_code(unsigned int alloc_bytes) @@ -186,8 +187,8 @@ static void *alloc_code(unsigned int alloc_bytes) /* Align function entries. */ alloc_bytes = (alloc_bytes + 3) & ~3; - if (code_bytes < alloc_bytes) - morecore(alloc_bytes); + if (code_bytes < alloc_bytes && morecore(alloc_bytes) != 0) + return NULL; ALLOC_CODE_STATS(++nr_allocs); ALLOC_CODE_STATS(total_alloc += alloc_bytes); res = code_next; @@ -211,6 +212,8 @@ void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) int i; code = alloc_code(5*sizeof(int)); + if (!code) + return NULL; /* sethi %hi(Address), %i4 */ code[0] = 0x39000000 | (((unsigned int)callee_exp >> 10) & 0x3FFFFF); diff --git a/erts/emulator/hipe/hipe_x86.c b/erts/emulator/hipe/hipe_x86.c index 314f6b597c..998905ea63 100644 --- a/erts/emulator/hipe/hipe_x86.c +++ b/erts/emulator/hipe/hipe_x86.c @@ -108,7 +108,7 @@ static void atexit_alloc_code_stats(void) #define MAP_ANONYMOUS MAP_ANON #endif -static void morecore(unsigned int alloc_bytes) +static int morecore(unsigned int alloc_bytes) { unsigned int map_bytes; char *map_hint, *map_start; @@ -136,10 +136,9 @@ static void morecore(unsigned int alloc_bytes) #endif , -1, 0); - if (map_start == MAP_FAILED) { - perror("mmap"); - abort(); - } + if (map_start == MAP_FAILED) + return -1; + ALLOC_CODE_STATS(total_mapped += map_bytes); /* Merge adjacent mappings, so the trailing portion of the previous @@ -155,6 +154,8 @@ static void morecore(unsigned int alloc_bytes) } ALLOC_CODE_STATS(atexit_alloc_code_stats()); + + return 0; } static void *alloc_code(unsigned int alloc_bytes) @@ -164,8 +165,8 @@ static void *alloc_code(unsigned int alloc_bytes) /* Align function entries. */ alloc_bytes = (alloc_bytes + 3) & ~3; - if (code_bytes < alloc_bytes) - morecore(alloc_bytes); + if (code_bytes < alloc_bytes && morecore(alloc_bytes) != 0) + return NULL; ALLOC_CODE_STATS(++nr_allocs); ALLOC_CODE_STATS(total_alloc += alloc_bytes); res = code_next; @@ -207,6 +208,8 @@ void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity) (P_CALLEE_EXP >= 128 ? 3 : 0) + (P_ARITY >= 128 ? 3 : 0); codep = code = alloc_code(codeSize); + if (!code) + return NULL; /* movl $beamAddress, P_CALLEE_EXP(%ebp); 3 or 6 bytes, plus 4 */ codep[0] = 0xc7; -- cgit v1.2.3 From 043ad57d27f7f320043c48c0a8dadfc86c797ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 27 Jan 2015 15:32:17 +0100 Subject: erts: Don't close all fds twice in child_setup The commit c2b4eab25c907f453a394d382c04cd04e6c06b49 introduced an error in which child_setup erroneously tried to close all file descriptors twice. --- erts/emulator/sys/unix/erl_child_setup.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 94eb6b1547..5ad92dad02 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -101,7 +101,9 @@ main(int argc, char *argv[]) if (sscanf(argv[CS_ARGV_FD_CR_IX], "%d:%d", &from, &to) != 2) return 1; -#if defined(__ANDROID__) +#if defined(HAVE_CLOSEFROM) + closefrom(from); +#elif defined(__ANDROID__) for (i = from; i <= to; i++) { if (i!=__system_properties_fd) (void) close(i); @@ -109,13 +111,6 @@ main(int argc, char *argv[]) #else for (i = from; i <= to; i++) (void) close(i); -#endif /* __ANDROID__ */ - -#if defined(HAVE_CLOSEFROM) - closefrom(from); -#else - for (i = from; i <= to; i++) - (void) close(i); #endif if (!(argv[CS_ARGV_WD_IX][0] == '.' && argv[CS_ARGV_WD_IX][1] == '\0') @@ -147,8 +142,6 @@ main(int argc, char *argv[]) return 1; } - - #if defined(__ANDROID__) int __system_properties_fd(void) { -- cgit v1.2.3 From 7e16e552e24859025e9029c9f24277596b59b3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 27 Jan 2015 16:06:01 +0100 Subject: erts: Use closefrom() if available when closing fds closefrom() was only used in the vfork() case before, now also used in the fork() case. --- erts/emulator/sys/unix/sys.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 0d677d5f34..ef38d3ecaa 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -1574,9 +1574,13 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op goto child_error; } +#if defined(HAVE_CLOSEFROM) + closefrom(opts->use_stdio ? 3 : 5); +#else for (i = opts->use_stdio ? 3 : 5; i < max_files; i++) (void) close(i); - +#endif + if (opts->wd && chdir(opts->wd) < 0) goto child_error; -- cgit v1.2.3 From 4d960f16d89c880507c951e1c74ac17e349f5dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 29 Jan 2015 16:04:46 +0100 Subject: erts: Fix getifaddrs realloc ptr mismatch When a buffer was exhausted and subsequently a realloc, we could get an invalid pointer. For this to occur we would need to have a realloc to lower adresses. The symptom would be garbage returned from erlang:port_control(Port, 25, []) (prim_inet:getifaddrs(Port) resulting in a badarg) or a segmentation fault. --- erts/emulator/drivers/common/inet_drv.c | 53 ++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index db8a251fdd..ff155cc07c 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -4721,6 +4721,36 @@ static char* sockaddr_to_buf(struct sockaddr* addr, char* ptr, char* end) return NULL; } +/* sockaddr_bufsz_need + * Returns the number of bytes needed to store the information + * through sockaddr_to_buf + */ + +static size_t sockaddr_bufsz_need(struct sockaddr* addr) +{ + if (addr->sa_family == AF_INET || addr->sa_family == 0) { + return 1 + sizeof(struct in_addr); + } +#if defined(HAVE_IN6) && defined(AF_INET6) + else if (addr->sa_family == AF_INET6) { + return 1 + sizeof(struct in6_addr); + } +#endif +#if defined(AF_LINK) + if (addr->sa_family == AF_LINK) { + struct sockaddr_dl *sdl_p = (struct sockaddr_dl*) addr; + return 2 + sdl_p->sdl_alen; + } +#endif +#if defined(AF_PACKET) && defined(HAVE_NETPACKET_PACKET_H) + else if(addr->sa_family == AF_PACKET) { + struct sockaddr_ll *sll_p = (struct sockaddr_ll*) addr; + return 2 + sll_p->sll_halen; + } +#endif + return 0; +} + static char* buf_to_sockaddr(char* ptr, char* end, struct sockaddr* addr) { buf_check(ptr,end,1); @@ -5799,6 +5829,11 @@ done: } #elif defined(HAVE_GETIFADDRS) +#ifdef DEBUG +#define GETIFADDRS_BUFSZ (1) +#else +#define GETIFADDRS_BUFSZ (512) +#endif static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, char **rbuf_pp, ErlDrvSizeT rsize) @@ -5809,15 +5844,15 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, char *buf_p; char *buf_alloc_p; - buf_size = 512; - buf_alloc_p = ALLOC(buf_size); + buf_size = GETIFADDRS_BUFSZ; + buf_alloc_p = ALLOC(GETIFADDRS_BUFSZ); buf_p = buf_alloc_p; # define BUF_ENSURE(Size) \ do { \ int NEED_, GOT_ = buf_p - buf_alloc_p; \ NEED_ = GOT_ + (Size); \ if (NEED_ > buf_size) { \ - buf_size = NEED_ + 512; \ + buf_size = NEED_ + GETIFADDRS_BUFSZ; \ buf_alloc_p = REALLOC(buf_alloc_p, buf_size); \ buf_p = buf_alloc_p + GOT_; \ } \ @@ -5830,7 +5865,7 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, while (! (P_ = sockaddr_to_buf((sa), buf_p, \ buf_alloc_p+buf_size))) { \ int GOT_ = buf_p - buf_alloc_p; \ - buf_size += 512; \ + buf_size += GETIFADDRS_BUFSZ; \ buf_alloc_p = REALLOC(buf_alloc_p, buf_size); \ buf_p = buf_alloc_p + GOT_; \ } \ @@ -5887,10 +5922,11 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, || ifa_p->ifa_addr->sa_family == AF_PACKET #endif ) { - char *bp = buf_p; - BUF_ENSURE(1); - SOCKADDR_TO_BUF(INET_IFOPT_HWADDR, ifa_p->ifa_addr); - if (buf_p - bp < 4) buf_p = bp; /* Empty hwaddr */ + size_t need = sockaddr_bufsz_need(ifa_p->ifa_addr); + if (need > 3) { + BUF_ENSURE(1 + need); + SOCKADDR_TO_BUF(INET_IFOPT_HWADDR, ifa_p->ifa_addr); + } } #endif } @@ -5905,6 +5941,7 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, return buf_size; # undef BUF_ENSURE } +#undef GETIFADDRS_BUFSZ #else -- cgit v1.2.3 From cd450ec99350bff295c42252b191687fee415c7a Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 30 Jan 2015 16:57:04 +0100 Subject: Prepare release --- erts/doc/src/notes.xml | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 60 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index c896ee0cae..af0d4d7377 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,65 @@

This document describes the changes made to the ERTS application.

+
Erts 6.3.1 + +
Fixed Bugs and Malfunctions + + +

+ Fix getifaddrs realloc pointer error

+

+ When a buffer was exhausted and subsequently reallocated, + we could get an unsafe pointer pointing to faulty memory.

+

+ For this to occur we would need to have a large number of + interfaces and a reallocation of memory to a lower + addresses.

+

+ The symptom would be garbage returned from + erlang:port_control(Port, 25, []) + (prim_inet:getifaddrs(Port) resulting in a badarg) or a + segmentation fault.

+

+ Own Id: OTP-12445

+
+ +

+ Don't close all file descriptors twice in child_setup

+

+ The commit c2b4eab25c907f453a394d382c04cd04e6c06b49 + introduced an error in which child_setup erroneously + tried to close all file descriptors twice.

+

+ Use closefrom() if available when closing all file + descriptors.

+

+ The function closefrom() was only used in the vfork() + case before but is now also used in the fork() case if + available.

+

+ Own Id: OTP-12446

+
+ +

+ During a crashdump all file descriptors are closed to + ensure the closing of the epmd port and to reserve a file + descriptor for the crashdump file.

+

+ If a driver (third party library) cannot handle closing + of sockets this could result in a segmentation fault in + which case a crashdump would not be produced. This is now + fixed by only closing inets sockets via an emergency + close callback to the driver and thus closing the epmd + socket.

+

+ Own Id: OTP-12447

+
+
+
+ +
+
Erts 6.3
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index d0dc8f7243..e4b071b090 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.3 +VSN = 6.3.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 8c0bebb66ba01b174e6482cd81949eaf08748bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Sat, 31 Jan 2015 08:17:25 +0100 Subject: trace_bif_SUITE: Ensure that a call to time/0 is not removed Assigning the result of a BIF call to a variable used to be sufficient to prevent the call from being optimized away. With the more aggressive optimization that will be introduced in a future commit it will not be sufficient. Matching the return value will prevent the compiler from doing the optimization. --- erts/emulator/test/trace_bif_SUITE.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index 2c78aa394f..063e348836 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -260,7 +260,9 @@ bif_process() -> apply(erlang, Name, Args), bif_process(); {do_time_bif} -> - _ = time(), %Assignment tells compiler to keep call. + %% Match the return value to ensure that the time() call + %% is not optimized away. + {_,_,_} = time(), bif_process(); {do_statistics_bif} -> statistics(runtime), -- cgit v1.2.3 From aaa0536429ac827f3651bccd938f7d891f448b42 Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Wed, 4 Feb 2015 12:13:26 +0100 Subject: fix faulty merge --- erts/emulator/sys/unix/sys.c | 28 ---------------------------- 1 file changed, 28 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 69be052381..0d9c743c0c 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -778,36 +778,8 @@ prepare_crash_dump(int secs) heart_port->common.id, list, NULL); } -<<<<<<< HEAD - /* Make sure we unregister at epmd (unknown fd) and get at least - one free filedescriptor (for erl_crash.dump) */ - - max = max_files; - if (max < 1024) - max = 1024; - for (i = 3; i < max; i++) { -#if defined(ERTS_SMP) - /* We don't want to close the signal notification pipe... */ - if (i == sig_notify_fds[0] || i == sig_notify_fds[1]) - continue; - /* We don't want to close the signal syspend pipe... */ - if (i == sig_suspend_fds[0] || i == sig_suspend_fds[1]) - continue; -#elif defined(USE_THREADS) - /* We don't want to close the async notification pipe... */ - if (i == async_fd[0] || i == async_fd[1]) - continue; -#endif - /* We don't want to close our heart yet ... */ - if (i == heart_fd[0] || i == heart_fd[1]) - continue; - - close(i); - } -======= /* Make sure we have a fd for our crashdump file. */ close(crashdump_companion_cube_fd); ->>>>>>> maint envsz = sizeof(env); i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz); -- cgit v1.2.3 From 7e147a05683c709128b6777d0c360fcde067f567 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Wed, 4 Feb 2015 20:27:37 +0100 Subject: don't create oversize bignums in binary matching Bignums are artifically restricted in size. Arithmetic and logical operations check the sizes of resulting bignums, and turn oversize results into system_limit exceptions. However, this check is not performed when bignums are constructed by binary matching. The consequence is that such matchings can construct oversize bignums that satisfy is_integer/1 yet don't work. Performing arithmetic such as Term - 0 fails with a system_limit exception. Worse, performing a logical operation such as Term band Term results in []. The latter occurs because the size checking (e.g. in erts_band()) is a simple ASSERT(is_not_nil(...)) on the result of the bignum operation, which internally is [] (NIL) in the case of oversize results. However, ASSERT is a no-op in release builds, so the error goes unnoticed and [] is returned as the result of the band/2. This patch addresses this by preventing oversize bignums from entering the VM via binary matching: - the internal bytes_to_big() procedure is augmented to return NIL for oversize results, just like big_norm() - callers of bytes_to_big() are augmented to check for NIL returns and signal errors in those cases - erts_bs_get_integer_2() can only fail with badmatch, so that is the Erlang-level result of oversize bignums from binary matches - big_SUITE.erl is extended with a test case that fails without this fix (no error signalled) and passes with it (badmatch occurs) Credit goes to Nico Kruber for the initial bug report. --- erts/emulator/beam/beam_load.c | 3 ++- erts/emulator/beam/big.c | 2 ++ erts/emulator/beam/erl_bits.c | 4 +++- erts/emulator/beam/external.c | 2 ++ erts/emulator/test/big_SUITE.erl | 14 ++++++++++++-- 5 files changed, 21 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index cfc6146b0a..41c1b5d2c2 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4971,7 +4971,8 @@ get_tag_and_value(LoaderState* stp, Uint len_code, arity = count/sizeof(Eterm); *result = new_literal(stp, &hp, arity+1); - (void) bytes_to_big(bigbuf, count, neg, hp); + if (is_nil(bytes_to_big(bigbuf, count, neg, hp))) + goto load_error; if (bigbuf != default_buf) { erts_free(ERTS_ALC_T_LOADER_TMP, (void *) bigbuf); diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index de7d370938..d1e46e3063 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1900,6 +1900,8 @@ Eterm bytes_to_big(byte *xp, dsize_t xsz, int xsgn, Eterm *r) *rwp = d; rwp++; } + if (rsz > BIG_ARITY_MAX) + return NIL; if (xsgn) { *r = make_neg_bignum_header(rsz); } diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 73765772c8..642f56a15e 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -403,7 +403,9 @@ erts_bs_get_integer_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuff words_needed = 1+WSIZE(bytes); hp = HeapOnlyAlloc(p, words_needed); res = bytes_to_big(LSB, bytes, sgn, hp); - if (is_small(res)) { + if (is_nil(res)) { + res = THE_NON_VALUE; + } else if (is_small(res)) { p->htop = hp; } else if ((actual = bignum_header_arity(*hp)+1) < words_needed) { p->htop = hp + actual; diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 9b9b4b2a62..45d1f7514e 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3056,6 +3056,8 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, big = make_small(0); } else { big = bytes_to_big(first, n, neg, hp); + if (is_nil(big)) + goto error; if (is_big(big)) { hp += big_arity(big) + 1; } diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl index 413bd3bcae..3193d56e2a 100644 --- a/erts/emulator/test/big_SUITE.erl +++ b/erts/emulator/test/big_SUITE.erl @@ -23,7 +23,7 @@ init_per_group/2,end_per_group/2]). -export([t_div/1, eq_28/1, eq_32/1, eq_big/1, eq_math/1, big_literals/1, borders/1, negative/1, big_float_1/1, big_float_2/1, - shift_limit_1/1, powmod/1, system_limit/1, otp_6692/1]). + shift_limit_1/1, powmod/1, system_limit/1, toobig/1, otp_6692/1]). %% Internal exports. -export([eval/1]). @@ -40,7 +40,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [t_div, eq_28, eq_32, eq_big, eq_math, big_literals, borders, negative, {group, big_float}, shift_limit_1, - powmod, system_limit, otp_6692]. + powmod, system_limit, toobig, otp_6692]. groups() -> [{big_float, [], [big_float_1, big_float_2]}]. @@ -370,6 +370,16 @@ maxbig() -> id(I) -> I. +toobig(Config) when is_list(Config) -> + ?line {'EXIT',{{badmatch,_},_}} = (catch toobig()), + ok. + +toobig() -> + A = erlang:term_to_binary(lists:seq(1000000, 2200000)), + ASize = erlang:bit_size(A), + <> = A, % should fail + ANr band ANr. + otp_6692(suite) -> []; otp_6692(doc) -> -- cgit v1.2.3 From b24651c3bb6fef59c0e92c24e69151d1a92c4b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=9F=D1=80=D0=BE?= =?UTF-8?q?=D1=85=D0=BE=D1=80=D0=BE=D0=B2?= Date: Mon, 19 Jan 2015 05:06:53 +0300 Subject: Add zlib limited output buffer size functionality This functionality may be useful for compressed streams with high compression ratio (in case of gzip it may be up to x1000), when small amount of compressed data will produce large amount of uncompressed output. This may lead to DoS attacks, because server easily goes out of memory. Example of such high compression ratio stream: ``` dd if=/dev/zero of=sparse.bin bs=1MB count=100 # 100mb of zeroes gzip sparse.bin # 95kb sparse.bin.gz $ erl > {ok, Compressed} = file:read_file("sparse.bin.gz"), > 97082 = size(Compressed), > Uncompressed = zlib:gunzip(Compressed), > 100000000 = iolist_size(Uncompressed). ``` --- erts/doc/src/zlib.xml | 47 ++++++++++++++++++++ erts/emulator/drivers/common/zlib_drv.c | 73 ++++++++++++++++++++++++++++++++ erts/preloaded/ebin/zlib.beam | Bin 13188 -> 14156 bytes erts/preloaded/src/zlib.erl | 39 ++++++++++++++++- 4 files changed, 158 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index da8ccdecdf..673b743e2e 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -311,6 +311,53 @@ list_to_binary([B1,B2]) compressor.

+ + + Decompress data with limited output size + +

Like inflate/2, but decompress no more data than + will fit in the buffer configured via setBufSize/2. + Is is useful when decompressing a stream with a high compression + ratio such that a small amount of compressed input may expand up to + 1000 times. + It returns {more, Decompressed}, when there is more output + available, and inflateChunk/1 should be used to read it. + It may introduce some output latency (reading + input without producing any output).

+

If a preset dictionary is needed at this point (see + inflateSetDictionary below), inflateChunk/2 throws a + {need_dictionary,Adler} exception where Adler is + the adler32 checksum of the dictionary chosen by the + compressor.

+ +
+walk(Compressed, Handler) ->
+    Z = zlib:open(),
+    zlib:inflateInit(Z),
+    % Limit single uncompressed chunk size to 512kb
+    zlib:setBufSize(Z, 512 * 1024),
+    loop(Z, Handler, zlib:inflateChunk(Z, Compressed)),
+    zlib:inflateEnd(Z),
+    zlib:close(Z).
+
+loop(Z, Handler, {more, Uncompressed}) ->
+    Handler(Uncompressed),
+    loop(Z, Handler, zlib:inflateChunk(Z));
+loop(Z, Handler, Uncompressed) ->
+    Handler(Uncompressed).
+        
+
+
+ + + Read next uncompressed chunk + +

Read next chunk of uncompressed data, initialized by + inflateChunk/2.

+

This function should be repeatedly called, while it returns + {more, Decompressed}.

+
+
Initialize the decompression dictionary diff --git a/erts/emulator/drivers/common/zlib_drv.c b/erts/emulator/drivers/common/zlib_drv.c index 3143e4511d..f7b2d91d23 100644 --- a/erts/emulator/drivers/common/zlib_drv.c +++ b/erts/emulator/drivers/common/zlib_drv.c @@ -62,8 +62,17 @@ #define CRC32_COMBINE 23 #define ADLER32_COMBINE 24 +#define INFLATE_CHUNK 25 + + #define DEFAULT_BUFSZ 4000 +/* This flag is used in the same places, where zlib return codes + * (Z_OK, Z_STREAM_END, Z_NEED_DICT) are. So, we need to set it to + * relatively large value to avoid possible value clashes in future. + * */ +#define INFLATE_HAS_MORE 100 + static int zlib_init(void); static ErlDrvData zlib_start(ErlDrvPort port, char* buf); static void zlib_stop(ErlDrvData e); @@ -295,6 +304,58 @@ static int zlib_inflate(ZLibData* d, int flush) return res; } +static int zlib_inflate_chunk(ZLibData* d) +{ + int res = Z_OK; + + if ((d->bin == NULL) && (zlib_output_init(d) < 0)) { + errno = ENOMEM; + return Z_ERRNO; + } + + while ((driver_sizeq(d->port) > 0) && (d->s.avail_out > 0) && + (res != Z_STREAM_END)) { + int vlen; + SysIOVec* iov = driver_peekq(d->port, &vlen); + int len; + + d->s.next_in = iov[0].iov_base; + d->s.avail_in = iov[0].iov_len; + while((d->s.avail_in > 0) && (d->s.avail_out > 0) && (res != Z_STREAM_END)) { + res = inflate(&d->s, Z_NO_FLUSH); + if (res == Z_NEED_DICT) { + /* Essential to eat the header bytes that zlib has looked at */ + len = iov[0].iov_len - d->s.avail_in; + driver_deq(d->port, len); + return res; + } + if (res == Z_BUF_ERROR) { + /* Was possible more output, but actually not */ + res = Z_OK; + } + else if (res < 0) { + return res; + } + } + len = iov[0].iov_len - d->s.avail_in; + driver_deq(d->port, len); + } + + /* We are here because all input was consumed or EOS reached or output + * buffer is full */ + if (d->want_crc) { + d->crc = crc32(d->crc, (unsigned char*) d->bin->orig_bytes, + d->binsz - d->s.avail_out); + } + zlib_output(d); + if ((res == Z_OK) && (d->s.avail_in > 0)) + res = INFLATE_HAS_MORE; + else if (res == Z_STREAM_END) { + d->inflate_eos_seen = 1; + } + return res; +} + static int zlib_deflate(ZLibData* d, int flush) { int res = Z_OK; @@ -568,6 +629,18 @@ static ErlDrvSSizeT zlib_ctl(ErlDrvData drv_data, unsigned int command, char *bu return zlib_return(res, rbuf, rlen); } + case INFLATE_CHUNK: + if (d->state != ST_INFLATE) goto badarg; + if (len != 0) goto badarg; + res = zlib_inflate_chunk(d); + if (res == INFLATE_HAS_MORE) { + return zlib_value2(4, 0, rbuf, rlen); + } else if (res == Z_NEED_DICT) { + return zlib_value2(3, d->s.adler, rbuf, rlen); + } else { + return zlib_return(res, rbuf, rlen); + } + case GET_QSIZE: return zlib_value(driver_sizeq(d->port), rbuf, rlen); diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 7006764d96..ed6ab5b254 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index df7b2e6198..5ebc67dcaa 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -24,6 +24,7 @@ deflate/2,deflate/3,deflateEnd/1, inflateInit/1,inflateInit/2,inflateSetDictionary/2, inflateSync/1,inflateReset/1,inflate/2,inflateEnd/1, + inflateChunk/1, inflateChunk/2, setBufSize/2,getBufSize/1, crc32/1,crc32/2,crc32/3,adler32/2,adler32/3,getQSize/1, crc32_combine/4,adler32_combine/4, @@ -100,6 +101,7 @@ -define(INFLATE_RESET, 12). -define(INFLATE_END, 13). -define(INFLATE, 14). +-define(INFLATE_CHUNK, 25). -define(CRC32_0, 15). -define(CRC32_1, 16). @@ -263,6 +265,39 @@ inflate(Z, Data) -> erlang:error(badarg) end. +-spec inflateChunk(Z, Data) -> Decompressed | {more, Decompressed} when + Z :: zstream(), + Data :: iodata(), + Decompressed :: iolist(). +inflateChunk(Z, Data) -> + try port_command(Z, Data) of + true -> + inflateChunk(Z) + catch + error:_Err -> + flush(Z), + erlang:error(badarg) + end. + +-spec inflateChunk(Z) -> Decompressed | {more, Decompressed} when + Z :: zstream(), + Decompressed :: iolist(). +inflateChunk(Z) -> + Status = call(Z, ?INFLATE_CHUNK, []), + Data = receive + {Z, {data, Bin}} -> + Bin + after 0 -> + [] + end, + + case Status of + Good when (Good == ok) orelse (Good == stream_end) -> + Data; + inflate_has_more -> + {more, Data} + end. + -spec inflateEnd(Z) -> 'ok' when Z :: zstream(). inflateEnd(Z) -> @@ -514,7 +549,9 @@ call(Z, Cmd, Arg) -> [2,A,B,C,D] -> (A bsl 24)+(B bsl 16)+(C bsl 8)+D; [3,A,B,C,D] -> - erlang:error({need_dictionary,(A bsl 24)+(B bsl 16)+(C bsl 8)+D}) + erlang:error({need_dictionary,(A bsl 24)+(B bsl 16)+(C bsl 8)+D}); + [4, _, _, _, _] -> + inflate_has_more catch error:badarg -> %% Rethrow loses port_control from stacktrace. erlang:error(badarg) -- cgit v1.2.3 From 7f82fdee75c2c3c3c5eaf259e2671737163be32b Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sat, 14 Feb 2015 16:18:10 +0100 Subject: don't leave a heap hole in erts_bs_get_integer_2 Reset p->htop in the oversize bignum error case to avoid leaving a hole in the heap, which would crash the debug emulator. --- erts/emulator/beam/erl_bits.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 642f56a15e..53c21c40e1 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -404,6 +404,7 @@ erts_bs_get_integer_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuff hp = HeapOnlyAlloc(p, words_needed); res = bytes_to_big(LSB, bytes, sgn, hp); if (is_nil(res)) { + p->htop = hp; res = THE_NON_VALUE; } else if (is_small(res)) { p->htop = hp; -- cgit v1.2.3 From b0bc9466e5a20dc23fff86dffce620f1fc1350bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erland=20Sch=C3=B6nbeck?= Date: Wed, 18 Feb 2015 12:45:12 +0100 Subject: otp_SUITE: Ignore diameter undefined function errors --- erts/test/otp_SUITE.erl | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 229d10ccee..171f722357 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2015. All Rights Reserved. %% %% The 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,8 @@ undefined_functions(Config) when is_list(Config) -> Undef4 = eunit_filter(Undef3), Undef5 = dialyzer_filter(Undef4), Undef6 = wx_filter(Undef5), - Undef = gs_filter(Undef6), + Undef7 = gs_filter(Undef6), + Undef = diameter_filter(Undef7), case Undef of [] -> ok; @@ -217,6 +218,21 @@ gs_filter(Undef) -> _ -> Undef end. +diameter_filter(Undef) -> + %% Filter away function calls that are catched. + filter(fun({{diameter_lib,_,_},{erlang,convert_time_resolution,3}}) -> + false; + ({{diameter_lib,_,_},{erlang,monotonic_time,0}}) -> + false; + ({{diameter_lib,_,_},{erlang,time_resolution,0}}) -> + false; + ({{diameter_lib,_,_},{erlang,unique_integer,0}}) -> + false; + ({{diameter_lib,_,_},{erlang,time_offset,0}}) -> + false; + (_) -> true + end, Undef). + deprecated_not_in_obsolete(Config) when is_list(Config) -> ?line Server = ?config(xref_server, Config), ?line {ok,DeprecatedFunctions} = xref:q(Server, "DF"), -- cgit v1.2.3 From cfa9229ba1eb8851caecfc92ca1bc9813fb1d567 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 8 Feb 2015 21:19:35 +0100 Subject: remove perfctr support Perfctr is a Linux kernel extension that allows programmatic access to the performance monitoring counters found in most current CPUs. However, development of perfctr ceased after 2010, and it cannot be used with Linux kernels newer than 2.6.32. Therefore the perfctr support code in the Erlang VM is effectively dead code, so this patch removes it. --- erts/autoconf/vxworks/sed.general | 2 - erts/configure.in | 31 ----- erts/emulator/Makefile.in | 20 ---- erts/emulator/beam/benchmark.c | 65 +--------- erts/emulator/beam/benchmark.h | 39 +----- erts/emulator/hipe/hipe_bif1.c | 53 --------- erts/emulator/hipe/hipe_perfctr.c | 229 ------------------------------------ erts/emulator/hipe/hipe_perfctr.h | 23 ---- erts/emulator/hipe/hipe_perfctr.tab | 25 ---- 9 files changed, 3 insertions(+), 484 deletions(-) delete mode 100644 erts/emulator/hipe/hipe_perfctr.c delete mode 100644 erts/emulator/hipe/hipe_perfctr.h delete mode 100644 erts/emulator/hipe/hipe_perfctr.tab (limited to 'erts') diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general index efa4e99054..c82b7348cf 100644 --- a/erts/autoconf/vxworks/sed.general +++ b/erts/autoconf/vxworks/sed.general @@ -78,8 +78,6 @@ s|@WITH_SCTP@|| # HiPE s|@HIPE_ENABLED@|| -s|@PERFCTR_PATH@|| -s|@USE_PERFCTR@|| # m4 s|@OPSYS@|noopsys| diff --git a/erts/configure.in b/erts/configure.in index 7cb6b50708..f248cbe34d 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -264,24 +264,6 @@ AS_HELP_STRING([--enable-m32-build], esac ],enable_m32_build=no) -AC_SUBST(PERFCTR_PATH) -AC_ARG_WITH(perfctr, -AS_HELP_STRING([--with-perfctr=PATH], - [specify location of perfctr include and lib]) -AS_HELP_STRING([--without-perfctr], [don't use perfctr (default)])) - -if test "x$with_perfctr" = "xno" -o "x$with_perfctr" = "x" ; then - PERFCTR_PATH= -else - if test ! -f "$with_perfctr/usr.lib/libperfctr.a" ; then - AC_MSG_ERROR(Invalid path to option --with-perfctr=PATH) - fi - PERFCTR_PATH="$with_perfctr" - AC_DEFINE(USE_PERFCTR,[1], - [Define to enable hrvtime() on Linux systems with perfctr extension]) -fi - - AC_ARG_WITH(dynamic-trace, AS_HELP_STRING([--with-dynamic-trace={dtrace|systemtap}], [specify use of dynamic trace framework, dtrace or systemtap]) @@ -3502,19 +3484,6 @@ if test X${enable_native_libs} = Xyes -a X${HIPE_ENABLED} = Xyes; then fi AC_SUBST(NATIVE_LIBS_ENABLED) -# -# Check if HiPE should use a standard installation of perfctr. -# -AC_SUBST(USE_PERFCTR) -if test "x$HIPE_ENABLED" = "xyes" ; then - if test "x$with_perfctr" = "x" ; then - AC_CHECK_LIB(perfctr, vperfctr_info, [USE_PERFCTR=1 - AC_DEFINE(USE_PERFCTR,[1],[Define to enable hrvtime() on Linux systems with perfctr extension])]) - elif test "x$with_perfctr" != "xno" ; then - USE_PERFCTR=1 - fi -fi - # # Check for working poll(). # diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index e4cd566285..a632faf57d 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -349,16 +349,6 @@ endif EPCRE_LIB = $(ERL_TOP)/erts/emulator/pcre/obj/$(TARGET)/$(TYPE)/$(LIB_PREFIX)epcre$(LIB_SUFFIX) DEPLIBS += $(EPCRE_LIB) -PERFCTR_PATH=@PERFCTR_PATH@ -USE_PERFCTR=@USE_PERFCTR@ -ifdef PERFCTR_PATH -LIBS += $(PERFCTR_PATH)/usr.lib/libperfctr.a -else -ifdef USE_PERFCTR -LIBS += -lperfctr -endif -endif - LIBSCTP = @LIBSCTP@ ORG_THR_LIBS=@EMU_THR_LIBS@ @@ -566,9 +556,6 @@ HIPE_ppc64_TAB=hipe/hipe_ppc64.tab $(HIPE_ARCH64_TAB) HIPE_arm_TAB=hipe/hipe_arm.tab HIPE_ARCH_TAB=$(HIPE_$(ARCH)_TAB) BIFS += hipe/hipe_bif0.tab hipe/hipe_bif1.tab hipe/hipe_bif2.tab $(HIPE_ARCH_TAB) -ifdef USE_PERFCTR -BIFS += hipe/hipe_perfctr.tab -endif endif $(TARGET)/erl_bif_table.c \ @@ -664,10 +651,6 @@ COMMON_INCLUDES += -I../include/internal -I../include/internal/$(TARGET) INCLUDES = -I$(TTF_DIR) $(COMMON_INCLUDES) -ifdef PERFCTR_PATH -INCLUDES += -I$(PERFCTR_PATH)/usr.lib -I$(PERFCTR_PATH)/linux/include -endif - ifeq ($(TARGET),win32) $(OBJDIR)/dll_sys.o: sys/$(ERLANG_OSTYPE)/sys.c $(V_CC) $(CFLAGS) -DERL_RUN_SHARED_LIB=1 $(INCLUDES) -c $< -o $@ @@ -927,9 +910,6 @@ HIPE_OBJS= \ $(OBJDIR)/hipe_mode_switch.o \ $(OBJDIR)/hipe_native_bif.o \ $(OBJDIR)/hipe_stack.o $(HIPE_ARCH_OBJS) -ifdef USE_PERFCTR -HIPE_OBJS += $(OBJDIR)/hipe_perfctr.o -endif ifdef HIPE_ENABLED EXTRA_BASE_OBJS += $(HIPE_OBJS) endif diff --git a/erts/emulator/beam/benchmark.c b/erts/emulator/beam/benchmark.c index 8613131176..b16fe6b271 100644 --- a/erts/emulator/beam/benchmark.c +++ b/erts/emulator/beam/benchmark.c @@ -37,37 +37,9 @@ unsigned long long major_gc; #ifdef BM_TIMERS -#if (defined(__i386__) || defined(__x86_64__)) && USE_PERFCTR - -#include "libperfctr.h" -struct vperfctr *system_clock; -double cpu_khz; -BM_NEW_TIMER(start); - -static double get_hrvtime(void) -{ - unsigned long long ticks; - double milli_seconds; - - ticks = vperfctr_read_tsc(system_clock); - milli_seconds = (double)ticks / cpu_khz; - return milli_seconds; -} - -static void stop_hrvtime(void) -{ - if(system_clock) - { - vperfctr_stop(system_clock); - vperfctr_close(system_clock); - system_clock = NULL; - } -} - -#else /* not perfctr, asuming Solaris */ +/* assuming Solaris */ #include BM_TIMER_T system_clock; -#endif unsigned long local_pause_times[MAX_PAUSE_TIME]; unsigned long pause_times[MAX_PAUSE_TIME]; @@ -117,40 +89,6 @@ unsigned long long message_sizes[1000]; void init_benchmarking() { #ifdef BM_TIMERS -#if (defined(__i386__) || defined(__x86_64__)) && USE_PERFCTR - /* pass `--with-perfctr=/path/to/perfctr' when configuring */ - struct perfctr_info info; - struct vperfctr_control control; - int i; - - system_clock = vperfctr_open(); - if (system_clock != NULL) - { - if (vperfctr_info(system_clock,&info) >= 0) - { - cpu_khz = (double)info.cpu_khz; - if (info.cpu_features & PERFCTR_FEATURE_RDTSC) - { - memset(&control,0,sizeof control); - control.cpu_control.tsc_on = 1; - } - } - if (vperfctr_control(system_clock,&control) < 0) - { - vperfctr_close(system_clock); - system_clock = NULL; - } - } - - for (i = 0; i < 1000; i++) - { - BM_START_TIMER(system); - BM_STOP_TIMER(system); - } - - timer_time = system_time / 1000; - start_time = 0; -#else int i; for (i = 0; i < 1000; i++) { @@ -158,7 +96,6 @@ void init_benchmarking() BM_STOP_TIMER(system); } timer_time = system_time / 1000; -#endif for (i = 0; i < MAX_PAUSE_TIME; i++) { local_pause_times[i] = 0; diff --git a/erts/emulator/beam/benchmark.h b/erts/emulator/beam/benchmark.h index 766edaac42..904564a96b 100644 --- a/erts/emulator/beam/benchmark.h +++ b/erts/emulator/beam/benchmark.h @@ -37,10 +37,7 @@ /* BM_TIMERS keeps track of the time spent in diferent parts of the * system. It only measures accual active time, not time spent in idle - * mode. These timers requires hardware support. For Linux, use the - * package perfctr from user.it.uu.se/~mikpe/linux/perfctr. If this - * package is not specified when configuring the system - * (--with-perfctr=PATH), the Solaris hrtime_t will be used. + * mode. Currently, the Solaris hrtime_t will be used. * To add new timers look below. */ #define BM_TIMERS @@ -142,38 +139,7 @@ extern unsigned long long major_gc; * meassure (send time in shared heap for instance). */ -#if (defined(__i386__) || defined(__x86_64__)) && USE_PERFCTR -#include "libperfctr.h" - -#define BM_TIMER_T double - -extern struct vperfctr *system_clock; -extern double cpu_khz; -extern BM_TIMER_T start_time; - -#define BM_START_TIMER(t) start_time = \ - (BM_TIMER_T)vperfctr_read_tsc(system_clock) / \ - cpu_khz; - -#define BM_STOP_TIMER(t) do { \ - BM_TIMER_T tmp = ((BM_TIMER_T)vperfctr_read_tsc(system_clock) / cpu_khz); \ - tmp -= (start_time + timer_time); \ - t##_time += (tmp > 0 ? tmp : 0); \ -} while(0) - -#define BM_TIME_PRINTER(str,time) do { \ - int min,sec,milli,micro; \ - BM_TIMER_T tmp = (time) * 1000; \ - micro = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \ - tmp /= 1000; \ - milli = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \ - tmp /= 1000; \ - sec = (uint)(tmp - ((int)(tmp / 60)) * 60); \ - min = (uint)tmp / 60; \ - erts_fprintf(file,str": %d:%02d.%03d %03d\n",min,sec,milli,micro); \ -} while(0) - -#else /* !USE_PERFCTR (Assuming Solaris) */ +/* (Assuming Solaris) */ #define BM_TIMER_T hrtime_t #define BM_START_TIMER(t) system_clock = sys_gethrtime() @@ -196,7 +162,6 @@ extern BM_TIMER_T start_time; } while(0) extern BM_TIMER_T system_clock; -#endif /* USE_PERFCTR */ extern BM_TIMER_T timer_time; extern BM_TIMER_T system_time; diff --git a/erts/emulator/hipe/hipe_bif1.c b/erts/emulator/hipe/hipe_bif1.c index 56767ef04b..ecb34df412 100644 --- a/erts/emulator/hipe/hipe_bif1.c +++ b/erts/emulator/hipe/hipe_bif1.c @@ -574,22 +574,6 @@ BIF_RETTYPE hipe_bifs_pause_times_0(BIF_ALIST_0) /* XXX: these macros have free variables */ #ifdef BM_TIMERS -#if USE_PERFCTR -#define MAKE_TIME(_timer_) { \ - BM_TIMER_T tmp = _timer_##_time; \ - milli = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \ - tmp /= 1000; \ - sec = (uint)(tmp - ((int)(tmp / 60)) * 60); \ - min = (uint)tmp / 60; } - -#define MAKE_MICRO_TIME(_timer_) { \ - BM_TIMER_T tmp = _timer_##_time * 1000; \ - micro = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \ - tmp /= 1000; \ - milli = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \ - sec = (uint)tmp / 1000; } - -#else #define MAKE_TIME(_timer_) { \ BM_TIMER_T tmp = _timer_##_time / 1000000; \ milli = tmp % 1000; \ @@ -604,7 +588,6 @@ BIF_RETTYPE hipe_bifs_pause_times_0(BIF_ALIST_0) milli = tmp % 1000; \ sec = tmp / 1000; } -#endif #else #define MAKE_TIME(_timer_) #define MAKE_MICRO_TIME(_timer_) @@ -852,9 +835,6 @@ BIF_RETTYPE hipe_bifs_misc_timer_clear_0(BIF_ALIST_0) /* * HiPE hrvtime(). * These implementations are currently available: - * + On Linux with the perfctr extension we can use the process' - * virtualised time-stamp counter. To enable this mode you must - * pass `--with-perfctr=/path/to/perfctr' when configuring. * + The fallback, which is the same as {X,_} = runtime(statistics). */ @@ -866,37 +846,6 @@ static double fallback_get_hrvtime(void) return (double)ms_user; } -#if USE_PERFCTR - -#include "hipe_perfctr.h" -static int hrvtime_started; /* 0: closed, +1: perfctr, -1: fallback */ -#define hrvtime_is_started() (hrvtime_started != 0) - -static void start_hrvtime(void) -{ - if (hipe_perfctr_hrvtime_open() >= 0) - hrvtime_started = 1; - else - hrvtime_started = -1; -} - -static void stop_hrvtime(void) -{ - if (hrvtime_started > 0) - hipe_perfctr_hrvtime_close(); - hrvtime_started = 0; -} - -static double get_hrvtime(void) -{ - if (hrvtime_started > 0) - return hipe_perfctr_hrvtime_get(); - else - return fallback_get_hrvtime(); -} - -#else /* !USE_PERFCTR */ - /* * Fallback, if nothing better exists. * This is the same as {X,_} = statistics(runtime), which uses @@ -908,8 +857,6 @@ static double get_hrvtime(void) #define stop_hrvtime() do{}while(0) #define get_hrvtime() fallback_get_hrvtime() -#endif /* !USE_PERFCTR */ - BIF_RETTYPE hipe_bifs_get_hrvtime_0(BIF_ALIST_0) { Eterm *hp; diff --git a/erts/emulator/hipe/hipe_perfctr.c b/erts/emulator/hipe/hipe_perfctr.c deleted file mode 100644 index 371b3fb097..0000000000 --- a/erts/emulator/hipe/hipe_perfctr.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2004-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 "error.h" -#include "global.h" -#include "bif.h" -#include "big.h" -#include "erl_binary.h" -#include "hipe_perfctr.h" -#include "libperfctr.h" - -static struct vperfctr *vperfctr; -static unsigned int have_rdtsc; -static double tsc_to_ms; -static unsigned int tsc_on; /* control calls must set tsc_on if have_rdtsc is true */ -static unsigned int nractrs; -static unsigned int users; -#define USER_BIFS (1<<0) -#define USER_HRVTIME (1<<1) - -static int hipe_perfctr_open(unsigned int user) -{ - struct perfctr_info info; - - if (!vperfctr) { - vperfctr = vperfctr_open(); - if (!vperfctr) - return -1; - if (vperfctr_info(vperfctr, &info) >= 0) { - tsc_to_ms = (double)(info.tsc_to_cpu_mult ? : 1) / (double)info.cpu_khz; - have_rdtsc = (info.cpu_features & PERFCTR_FEATURE_RDTSC) ? 1 : 0; - } - tsc_on = 0; - nractrs = 0; - } - users |= user; - return 0; -} - -static void hipe_perfctr_reset(void) -{ - struct vperfctr_control control; - - memset(&control, 0, sizeof control); - if (have_rdtsc) - control.cpu_control.tsc_on = 1; - nractrs = 0; - if (vperfctr_control(vperfctr, &control) >= 0) - tsc_on = 1; -} - -static void hipe_perfctr_close(unsigned int user) -{ - if (!vperfctr) - return; - users &= ~user; - switch (users) { - case 0: - vperfctr_unlink(vperfctr); - vperfctr_close(vperfctr); - vperfctr = NULL; - tsc_on = 0; - nractrs = 0; - break; - case USER_HRVTIME: - hipe_perfctr_reset(); - } -} - -/* - * Interface for HiPE's hrvtime code. - */ - -int hipe_perfctr_hrvtime_open(void) -{ - if (hipe_perfctr_open(USER_HRVTIME) < 0) - return -1; - if (have_rdtsc) { - if (!tsc_on) - hipe_perfctr_reset(); /* note: updates tsc_on */ - if (tsc_on) - return 0; - } - hipe_perfctr_hrvtime_close(); - return -1; -} - -void hipe_perfctr_hrvtime_close(void) -{ - hipe_perfctr_close(USER_HRVTIME); -} - -double hipe_perfctr_hrvtime_get(void) -{ - return (double)vperfctr_read_tsc(vperfctr) * tsc_to_ms; -} - -/* - * BIF interface for user-programmable performance counters. - */ - -BIF_RETTYPE hipe_bifs_vperfctr_open_0(BIF_ALIST_0) -{ - if (hipe_perfctr_open(USER_BIFS) < 0) - BIF_RET(am_false); /* arity 0 BIFs can't fail :-( */ - BIF_RET(am_true); -} - -BIF_RETTYPE hipe_bifs_vperfctr_close_0(BIF_ALIST_0) -{ - hipe_perfctr_close(USER_BIFS); - BIF_RET(NIL); -} - -static Eterm ull_to_integer(unsigned long long x, Process *p) -{ - unsigned long long tmpx; - unsigned int ds, i; - size_t sz; - Eterm *hp; - ErtsDigit *xp; - - if (x <= (unsigned long long)MAX_SMALL) - return make_small(x); - - /* Calculate number of digits. */ - ds = 0; - tmpx = x; - do { - ++ds; - tmpx = (tmpx >> (D_EXP / 2)) >> (D_EXP / 2); - } while (tmpx != 0); - - sz = BIG_NEED_SIZE(ds); /* number of words including arity */ - hp = HAlloc(p, sz); - *hp = make_pos_bignum_header(sz-1); - - xp = (ErtsDigit*)(hp+1); - i = 0; - do { - xp[i++] = (ErtsDigit)x; - x = (x >> (D_EXP / 2)) >> (D_EXP / 2); - } while (i < ds); - while (i & (BIG_DIGITS_PER_WORD-1)) - xp[i++] = 0; - - return make_big(hp); -} - -BIF_RETTYPE hipe_bifs_vperfctr_info_0(BIF_ALIST_0) -{ - struct perfctr_info info; - - if (!vperfctr || vperfctr_info(vperfctr, &info) < 0) - BIF_RET(am_false); /* arity 0 BIFs can't fail :-( */ - BIF_RET(new_binary(BIF_P, (void*)&info, sizeof info)); -} - -BIF_RETTYPE hipe_bifs_vperfctr_read_tsc_0(BIF_ALIST_0) -{ - unsigned long long val; - - if (!vperfctr || !tsc_on) - BIF_RET(am_false); /* arity 0 BIFs can't fail :-( */ - val = vperfctr_read_tsc(vperfctr); - BIF_RET(ull_to_integer(val, BIF_P)); -} - -BIF_RETTYPE hipe_bifs_vperfctr_read_pmc_1(BIF_ALIST_1) -{ - Uint pmc; - unsigned long long val; - - if (!vperfctr || - is_not_small(BIF_ARG_1) || - (pmc = unsigned_val(BIF_ARG_1), pmc >= nractrs)) - BIF_RET(am_false); /* for consistency with the arity 0 BIFs */ - val = vperfctr_read_pmc(vperfctr, pmc); - BIF_RET(ull_to_integer(val, BIF_P)); -} - -BIF_RETTYPE hipe_bifs_vperfctr_control_1(BIF_ALIST_1) -{ - void *bytes; - struct vperfctr_control control; - Uint bitoffs; - Uint bitsize; - - if (!vperfctr) - BIF_ERROR(BIF_P, BADARG); - if (is_not_binary(BIF_ARG_1)) - BIF_ERROR(BIF_P, BADARG); - if (binary_size(BIF_ARG_1) != sizeof control) - BIF_ERROR(BIF_P, BADARG); - ERTS_GET_BINARY_BYTES(BIF_ARG_1, bytes, bitoffs, bitsize); - ASSERT(bitoffs == 0); - ASSERT(bitsize == 0); - memcpy(&control, bytes, sizeof control); - if (have_rdtsc) - control.cpu_control.tsc_on = 1; - if (vperfctr_control(vperfctr, &control) < 0) { - hipe_perfctr_reset(); - BIF_ERROR(BIF_P, BADARG); - } - tsc_on = control.cpu_control.tsc_on; - nractrs = control.cpu_control.nractrs; - BIF_RET(NIL); -} diff --git a/erts/emulator/hipe/hipe_perfctr.h b/erts/emulator/hipe/hipe_perfctr.h deleted file mode 100644 index 8fbf9ecf35..0000000000 --- a/erts/emulator/hipe/hipe_perfctr.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2004-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% - */ - - -extern int hipe_perfctr_hrvtime_open(void); -extern void hipe_perfctr_hrvtime_close(void); -extern double hipe_perfctr_hrvtime_get(void); diff --git a/erts/emulator/hipe/hipe_perfctr.tab b/erts/emulator/hipe/hipe_perfctr.tab deleted file mode 100644 index eaecea4651..0000000000 --- a/erts/emulator/hipe/hipe_perfctr.tab +++ /dev/null @@ -1,25 +0,0 @@ -# -# %CopyrightBegin% -# -# Copyright Ericsson AB 2004-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% -# - -bif hipe_bifs:vperfctr_open/0 -bif hipe_bifs:vperfctr_close/0 -bif hipe_bifs:vperfctr_info/0 -bif hipe_bifs:vperfctr_read_tsc/0 -bif hipe_bifs:vperfctr_read_pmc/1 -bif hipe_bifs:vperfctr_control/1 -- cgit v1.2.3 From 3a9b8b9e09a56cf23c86794094da321ae0a26145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 20 Feb 2015 11:50:29 +0100 Subject: erts: Fix erroneous printout in crashdump An extra '}' were printed in remote links. --- erts/emulator/beam/break.c | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 08265b590d..2cddfe2800 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -181,7 +181,6 @@ static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext) ASSERT(is_node_name_atom(mon->pid)); erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mon->name, mon->pid, mon->ref); - erts_print(to, to_arg,"}"); } else if (is_atom(mon->name)){ /* local by name */ erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mon->name, erts_this_dist_entry->sysname, mon->ref); -- cgit v1.2.3 From b493fbf2f6f788e9ca7f65df30c3af2bbcb0c169 Mon Sep 17 00:00:00 2001 From: Wasif Malik Date: Sat, 21 Feb 2015 21:58:16 +0100 Subject: Fix make_hash description --- erts/emulator/beam/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index c505c44905..f810fca9a4 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -707,7 +707,7 @@ erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length, ** If N < 0, Y = FUNNY_NUMBER4 else Y = FUNNY_NUMBER3. ** The hash value is Y*h(J) mod 2^32 where h(J) is calculated like ** h(0) = -** h(i) = h(i-i)*X + B(i-1) +** h(i) = h(i-1)*X + B(i-1) ** The above should hold regardless of internal representation. ** Pids are hashed like small numbers but with differrent constants, as are ** ports. -- cgit v1.2.3 From 0ec91ff571518e199aac7a4da961a80c153483b9 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 9 May 2014 16:00:54 +0200 Subject: Allow 4-ary BIFs --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/bif.h | 2 ++ erts/emulator/hipe/hipe_amd64_abi.txt | 2 +- erts/emulator/hipe/hipe_amd64_asm.m4 | 5 +++++ erts/emulator/hipe/hipe_amd64_bifs.m4 | 40 ++++++++++++++++++++++++++++++++++- erts/emulator/hipe/hipe_amd64_glue.S | 10 +++++++++ erts/emulator/hipe/hipe_ppc_asm.m4 | 5 +++++ erts/emulator/hipe/hipe_ppc_bifs.m4 | 38 ++++++++++++++++++++++++++++++++- erts/emulator/hipe/hipe_ppc_glue.S | 10 +++++++++ erts/emulator/hipe/hipe_sparc_asm.m4 | 5 +++++ erts/emulator/hipe/hipe_sparc_bifs.m4 | 36 ++++++++++++++++++++++++++++++- erts/emulator/hipe/hipe_sparc_glue.S | 11 ++++++++-- erts/emulator/hipe/hipe_x86_asm.m4 | 6 ++++++ erts/emulator/hipe/hipe_x86_bifs.m4 | 38 +++++++++++++++++++++++++++++++++ erts/emulator/hipe/hipe_x86_glue.S | 10 +++++++++ 15 files changed, 213 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 3af7c43abf..2d6fad8ae6 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3553,7 +3553,7 @@ get_map_elements_fail: vbf = (BifFunction) Arg(0); PROCESS_MAIN_CHK_LOCKS(c_p); bif_nif_arity = I[-1]; - ASSERT(bif_nif_arity <= 3); + ASSERT(bif_nif_arity <= 4); ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); reg[0] = r(0); { diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index 7b69b39511..837cb017ac 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -30,10 +30,12 @@ extern Export* erts_format_cpu_topology_trap; #define BIF_ALIST_1 Process* A__p, Eterm* BIF__ARGS #define BIF_ALIST_2 Process* A__p, Eterm* BIF__ARGS #define BIF_ALIST_3 Process* A__p, Eterm* BIF__ARGS +#define BIF_ALIST_4 Process* A__p, Eterm* BIF__ARGS #define BIF_ARG_1 (BIF__ARGS[0]) #define BIF_ARG_2 (BIF__ARGS[1]) #define BIF_ARG_3 (BIF__ARGS[2]) +#define BIF_ARG_4 (BIF__ARGS[3]) #define ERTS_IS_PROC_OUT_OF_REDS(p) \ ((p)->fcalls > 0 \ diff --git a/erts/emulator/hipe/hipe_amd64_abi.txt b/erts/emulator/hipe/hipe_amd64_abi.txt index 8a34bfa67f..72aed13995 100644 --- a/erts/emulator/hipe/hipe_amd64_abi.txt +++ b/erts/emulator/hipe/hipe_amd64_abi.txt @@ -45,7 +45,7 @@ The first return value from a function is placed in %rax, the second (if any) is placed in %rdx. Notes: -- Currently, NR_ARG_REGS==0. +- Currently, NR_ARG_REGS == 4. - C BIFs expect P in C parameter register 1: %rdi. By making Erlang parameter registers 1-5 coincide with C parameter registers 2-6, our BIF wrappers can simply move P to %rdi without having to shift diff --git a/erts/emulator/hipe/hipe_amd64_asm.m4 b/erts/emulator/hipe/hipe_amd64_asm.m4 index b4b3c073ab..ca55d5bf3b 100644 --- a/erts/emulator/hipe/hipe_amd64_asm.m4 +++ b/erts/emulator/hipe/hipe_amd64_asm.m4 @@ -253,6 +253,10 @@ define(NBIF_ARG,`ifelse(eval($3 >= NR_ARG_REGS),0,`NBIF_REG_ARG($1,$3)',`NBIF_ST `/* #define NBIF_ARG_3_0 'NBIF_ARG(%rsi,3,0)` */' `/* #define NBIF_ARG_3_1 'NBIF_ARG(%rdx,3,1)` */' `/* #define NBIF_ARG_3_2 'NBIF_ARG(%rcx,3,2)` */' +`/* #define NBIF_ARG_4_0 'NBIF_ARG(%rsi,4,0)` */' +`/* #define NBIF_ARG_4_1 'NBIF_ARG(%rdx,4,1)` */' +`/* #define NBIF_ARG_4_2 'NBIF_ARG(%rcx,4,2)` */' +`/* #define NBIF_ARG_4_3 'NBIF_ARG(%r8,4,3)` */' `/* #define NBIF_ARG_5_0 'NBIF_ARG(%rsi,5,0)` */' `/* #define NBIF_ARG_5_1 'NBIF_ARG(%rdx,5,1)` */' `/* #define NBIF_ARG_5_2 'NBIF_ARG(%rcx,5,2)` */' @@ -277,6 +281,7 @@ define(NBIF_RET,`NBIF_RET_N(eval(RET_POP($1)))')dnl `/* #define NBIF_RET_1 'NBIF_RET(1)` */' `/* #define NBIF_RET_2 'NBIF_RET(2)` */' `/* #define NBIF_RET_3 'NBIF_RET(3)` */' +`/* #define NBIF_RET_4 'NBIF_RET(4)` */' `/* #define NBIF_RET_5 'NBIF_RET(5)` */' `#endif /* ASM */' diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4 index 7a4bb30447..7d94aa05b3 100644 --- a/erts/emulator/hipe/hipe_amd64_bifs.m4 +++ b/erts/emulator/hipe/hipe_amd64_bifs.m4 @@ -51,9 +51,10 @@ define(HANDLE_GOT_MBUF,` * standard_bif_interface_1(nbif_name, cbif_name) * standard_bif_interface_2(nbif_name, cbif_name) * standard_bif_interface_3(nbif_name, cbif_name) + * standard_bif_interface_4(nbif_name, cbif_name) * standard_bif_interface_0(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-3 parameters and + * Generate native interface for a BIF with 0-4 parameters and * standard failure mode. */ define(standard_bif_interface_1, @@ -154,6 +155,43 @@ ASYM($1): TYPE_FUNCTION(ASYM($1)) #endif') +define(standard_bif_interface_4, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + TEXT + .align 4 + GLOBAL(ASYM($1)) +ASYM($1): + /* set up the parameters */ + movq P, %rdi + NBIF_ARG(%rsi,4,0) + NBIF_ARG(%rdx,4,1) + NBIF_ARG(%rcx,4,2) + NBIF_ARG(%r8,4,3) + + /* make the call on the C stack */ + SWITCH_ERLANG_TO_C + pushq %r8 + pushq %rcx + pushq %rdx + pushq %rsi + movq %rsp, %rsi /* Eterm* BIF__ARGS */ + sub $(8), %rsp /* stack frame 16-byte alignment */ + CALL_BIF($2) + add $(4*8 + 8), %rsp + TEST_GOT_MBUF + SWITCH_C_TO_ERLANG + + /* throw exception if failure, otherwise return */ + TEST_GOT_EXN + jz nbif_4_simple_exception + NBIF_RET(4) + HANDLE_GOT_MBUF(4) + SET_SIZE(ASYM($1)) + TYPE_FUNCTION(ASYM($1)) +#endif') + define(standard_bif_interface_0, ` #ifndef HAVE_$1 diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S index 955f7362b4..3cb0a2875b 100644 --- a/erts/emulator/hipe/hipe_amd64_glue.S +++ b/erts/emulator/hipe/hipe_amd64_glue.S @@ -321,6 +321,7 @@ ASYM(nbif_fail): GLOBAL(nbif_1_gc_after_bif) GLOBAL(nbif_2_gc_after_bif) GLOBAL(nbif_3_gc_after_bif) + GLOBAL(nbif_4_gc_after_bif) .align 4 nbif_0_gc_after_bif: xorl %edx, %edx @@ -336,6 +337,10 @@ nbif_2_gc_after_bif: .align 4 nbif_3_gc_after_bif: movl $3, %edx + jmp .gc_after_bif + .align 4 +nbif_4_gc_after_bif: + movl $4, %edx /*FALLTHROUGH*/ .align 4 .gc_after_bif: @@ -359,6 +364,7 @@ nbif_3_gc_after_bif: GLOBAL(nbif_1_simple_exception) GLOBAL(nbif_2_simple_exception) GLOBAL(nbif_3_simple_exception) + GLOBAL(nbif_4_simple_exception) .align 4 nbif_0_simple_exception: xorl %eax, %eax @@ -374,6 +380,10 @@ nbif_2_simple_exception: .align 4 nbif_3_simple_exception: movl $3, %eax + jmp .nbif_simple_exception + .align 4 +nbif_4_simple_exception: + movl $4, %eax /*FALLTHROUGH*/ .align 4 .nbif_simple_exception: diff --git a/erts/emulator/hipe/hipe_ppc_asm.m4 b/erts/emulator/hipe/hipe_ppc_asm.m4 index 4a1caa1543..e5a56de687 100644 --- a/erts/emulator/hipe/hipe_ppc_asm.m4 +++ b/erts/emulator/hipe/hipe_ppc_asm.m4 @@ -280,6 +280,10 @@ define(NBIF_ARG,`ifelse(eval($3 >= NR_ARG_REGS),0,`NBIF_REG_ARG($1,$3)',`NBIF_ST `/* #define NBIF_ARG_3_0 'NBIF_ARG(r3,3,0)` */' `/* #define NBIF_ARG_3_1 'NBIF_ARG(r3,3,1)` */' `/* #define NBIF_ARG_3_2 'NBIF_ARG(r3,3,2)` */' +`/* #define NBIF_ARG_4_0 'NBIF_ARG(r3,4,0)` */' +`/* #define NBIF_ARG_4_1 'NBIF_ARG(r3,4,1)` */' +`/* #define NBIF_ARG_4_2 'NBIF_ARG(r3,4,2)` */' +`/* #define NBIF_ARG_4_3 'NBIF_ARG(r3,4,3)` */' `/* #define NBIF_ARG_5_0 'NBIF_ARG(r3,5,0)` */' `/* #define NBIF_ARG_5_1 'NBIF_ARG(r3,5,1)` */' `/* #define NBIF_ARG_5_2 'NBIF_ARG(r3,5,2)` */' @@ -301,6 +305,7 @@ define(NBIF_RET,`NBIF_RET_N(eval(RET_POP($1)))')dnl `/* #define NBIF_RET_1 'NBIF_RET(1)` */' `/* #define NBIF_RET_2 'NBIF_RET(2)` */' `/* #define NBIF_RET_3 'NBIF_RET(3)` */' +`/* #define NBIF_RET_4 'NBIF_RET(4)` */' `/* #define NBIF_RET_5 'NBIF_RET(5)` */' dnl diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4 index f53b79b52e..b173b896b8 100644 --- a/erts/emulator/hipe/hipe_ppc_bifs.m4 +++ b/erts/emulator/hipe/hipe_ppc_bifs.m4 @@ -46,9 +46,10 @@ define(HANDLE_GOT_MBUF,` * standard_bif_interface_1(nbif_name, cbif_name) * standard_bif_interface_2(nbif_name, cbif_name) * standard_bif_interface_3(nbif_name, cbif_name) + * standard_bif_interface_4(nbif_name, cbif_name) * standard_bif_interface_0(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-3 parameters and + * Generate native interface for a BIF with 0-4 parameters and * standard failure mode. */ define(standard_bif_interface_1, @@ -144,6 +145,41 @@ ASYM($1): TYPE_FUNCTION(ASYM($1)) #endif') +define(standard_bif_interface_4, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + GLOBAL(ASYM($1)) +ASYM($1): + /* Set up C argument registers. */ + mr r3, P + NBIF_ARG(r4,4,0) + NBIF_ARG(r5,4,1) + NBIF_ARG(r6,4,2) + NBIF_ARG(r7,4,3) + + /* 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 r5, P_ARG1(r3) + STORE r6, P_ARG2(r3) + STORE r7, P_ARG3(r3) + addi r4, r3, P_ARG0 + CALL_BIF($2) + TEST_GOT_MBUF + + /* Restore registers. Check for exception. */ + CMPI r3, THE_NON_VALUE + RESTORE_CONTEXT_BIF + beq- 1f + NBIF_RET(4) +1: /* workaround for bc:s small offset operand */ + b CSYM(nbif_4_simple_exception) + HANDLE_GOT_MBUF(4) + SET_SIZE(ASYM($1)) + TYPE_FUNCTION(ASYM($1)) +#endif') + define(standard_bif_interface_0, ` #ifndef HAVE_$1 diff --git a/erts/emulator/hipe/hipe_ppc_glue.S b/erts/emulator/hipe/hipe_ppc_glue.S index c48fb150af..b07f4bc9c8 100644 --- a/erts/emulator/hipe/hipe_ppc_glue.S +++ b/erts/emulator/hipe/hipe_ppc_glue.S @@ -462,10 +462,12 @@ ASYM(nbif_fail): OPD(nbif_1_gc_after_bif) OPD(nbif_2_gc_after_bif) OPD(nbif_3_gc_after_bif) + OPD(nbif_4_gc_after_bif) GLOBAL(CSYM(nbif_0_gc_after_bif)) GLOBAL(CSYM(nbif_1_gc_after_bif)) GLOBAL(CSYM(nbif_2_gc_after_bif)) GLOBAL(CSYM(nbif_3_gc_after_bif)) + GLOBAL(CSYM(nbif_4_gc_after_bif)) CSYM(nbif_0_gc_after_bif): li r4, 0 b .gc_after_bif @@ -477,6 +479,9 @@ CSYM(nbif_2_gc_after_bif): b .gc_after_bif CSYM(nbif_3_gc_after_bif): li r4, 3 + b .gc_after_bif +CSYM(nbif_4_gc_after_bif): + li r4, 4 /*FALLTHROUGH*/ .gc_after_bif: stw r4, P_NARITY(P) /* Note: narity is a 32-bit field */ @@ -519,6 +524,11 @@ CSYM(nbif_2_simple_exception): GLOBAL(CSYM(nbif_3_simple_exception)) CSYM(nbif_3_simple_exception): li r4, 3 + b .nbif_simple_exception + OPD(nbif_3_simple_exception) + GLOBAL(CSYM(nbif_4_simple_exception)) +CSYM(nbif_4_simple_exception): + li r4, 4 /*FALLTHROUGH*/ .nbif_simple_exception: LOAD r3, P_FREASON(P) diff --git a/erts/emulator/hipe/hipe_sparc_asm.m4 b/erts/emulator/hipe/hipe_sparc_asm.m4 index c3c3bcb74a..8020104e40 100644 --- a/erts/emulator/hipe/hipe_sparc_asm.m4 +++ b/erts/emulator/hipe/hipe_sparc_asm.m4 @@ -176,6 +176,10 @@ define(NBIF_ARG,`ifelse(eval($3 >= NR_ARG_REGS),0,`NBIF_REG_ARG($1,$3)',`NBIF_ST `/* #define NBIF_ARG_3_0 'NBIF_ARG(r1,3,0)` */' `/* #define NBIF_ARG_3_1 'NBIF_ARG(r2,3,1)` */' `/* #define NBIF_ARG_3_2 'NBIF_ARG(r3,3,2)` */' +`/* #define NBIF_ARG_4_0 'NBIF_ARG(r1,4,0)` */' +`/* #define NBIF_ARG_4_1 'NBIF_ARG(r2,4,1)` */' +`/* #define NBIF_ARG_4_2 'NBIF_ARG(r3,4,2)` */' +`/* #define NBIF_ARG_4_3 'NBIF_ARG(r3,4,3)` */' `/* #define NBIF_ARG_5_0 'NBIF_ARG(r1,5,0)` */' `/* #define NBIF_ARG_5_1 'NBIF_ARG(r2,5,1)` */' `/* #define NBIF_ARG_5_2 'NBIF_ARG(r3,5,2)` */' @@ -200,6 +204,7 @@ define(NBIF_RET,`NBIF_RET_N(eval(RET_POP($1)))')dnl `/* #define NBIF_RET_1 'NBIF_RET(1)` */' `/* #define NBIF_RET_2 'NBIF_RET(2)` */' `/* #define NBIF_RET_3 'NBIF_RET(3)` */' +`/* #define NBIF_RET_4 'NBIF_RET(4)` */' `/* #define NBIF_RET_5 'NBIF_RET(5)` */' dnl diff --git a/erts/emulator/hipe/hipe_sparc_bifs.m4 b/erts/emulator/hipe/hipe_sparc_bifs.m4 index 2bfe3a4646..8dfb28c8e0 100644 --- a/erts/emulator/hipe/hipe_sparc_bifs.m4 +++ b/erts/emulator/hipe/hipe_sparc_bifs.m4 @@ -54,9 +54,10 @@ define(HANDLE_GOT_MBUF,` * standard_bif_interface_1(nbif_name, cbif_name) * standard_bif_interface_2(nbif_name, cbif_name) * standard_bif_interface_3(nbif_name, cbif_name) + * standard_bif_interface_3(nbif_name, cbif_name) * standard_bif_interface_0(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-3 parameters and + * Generate native interface for a BIF with 0-4 parameters and * standard failure mode. */ define(standard_bif_interface_1, @@ -146,6 +147,39 @@ $1: .type $1, #function #endif') +define(standard_bif_interface_4, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + .global $1 +$1: + /* Set up C argument registers. */ + mov P, %o0 + NBIF_ARG(%o1,4,0) + NBIF_ARG(%o2,4,1) + NBIF_ARG(%o3,4,2) + NBIF_ARG(%o4,4,3) + + /* Save caller-save registers and call the C function. */ + SAVE_CONTEXT_BIF + st %o1, [%o0+P_ARG0] ! Store BIF__ARGS in def_arg_reg + st %o2, [%o0+P_ARG1] + st %o3, [%o0+P_ARG2] + st %o4, [%o0+P_ARG3] + add %o0, P_ARG0, %o1 + CALL_BIF($2) + nop + TEST_GOT_MBUF + + /* Restore registers. Check for exception. */ + TEST_GOT_EXN(4) + RESTORE_CONTEXT_BIF + NBIF_RET(4) + HANDLE_GOT_MBUF(4) + .size $1, .-$1 + .type $1, #function +#endif') + define(standard_bif_interface_0, ` #ifndef HAVE_$1 diff --git a/erts/emulator/hipe/hipe_sparc_glue.S b/erts/emulator/hipe/hipe_sparc_glue.S index 6c8c841194..90adad42ab 100644 --- a/erts/emulator/hipe/hipe_sparc_glue.S +++ b/erts/emulator/hipe/hipe_sparc_glue.S @@ -326,7 +326,10 @@ nbif_2_gc_after_bif: ba .gc_after_bif mov 2, %o1 /* delay slot */ nbif_3_gc_after_bif: - mov 3, %o1 + ba .gc_after_bif + mov 3, %o1 /* delay slot */ +nbif_4_gc_after_bif: + mov 4, %o1 /*FALLTHROUGH*/ .gc_after_bif: st %o1, [P+P_NARITY] @@ -364,7 +367,11 @@ nbif_2_simple_exception: mov 2, %o1 /* delay slot */ .global nbif_3_simple_exception nbif_3_simple_exception: - mov 3, %o1 + ba .nbif_simple_exception + mov 3, %o1 /* delay slot */ + .global nbif_4_simple_exception +nbif_4_simple_exception: + mov 4, %o1 /*FALLTHROUGH*/ .nbif_simple_exception: ld [P+P_FREASON], %o0 diff --git a/erts/emulator/hipe/hipe_x86_asm.m4 b/erts/emulator/hipe/hipe_x86_asm.m4 index 39c5cb1044..436feca506 100644 --- a/erts/emulator/hipe/hipe_x86_asm.m4 +++ b/erts/emulator/hipe/hipe_x86_asm.m4 @@ -212,6 +212,7 @@ define(NBIF_COPY_NSP,`ifelse(eval($1 > NR_ARG_REGS),0,,`movl %esp, TEMP_NSP')')d `/* #define NBIF_COPY_NSP_1 'NBIF_COPY_NSP(1)` */' `/* #define NBIF_COPY_NSP_2 'NBIF_COPY_NSP(2)` */' `/* #define NBIF_COPY_NSP_3 'NBIF_COPY_NSP(3)` */' +`/* #define NBIF_COPY_NSP_4 'NBIF_COPY_NSP(4)` */' `/* #define NBIF_COPY_NSP_5 'NBIF_COPY_NSP(5)` */' dnl @@ -235,6 +236,10 @@ define(NBIF_ARG_OPND,`ifelse(eval($2 >= NR_ARG_REGS),0,`ARG'$2,BASE_OFFSET(eval( `/* #define NBIF_ARG_OPND_3_0 'NBIF_ARG_OPND(3,0)` */' `/* #define NBIF_ARG_OPND_3_1 'NBIF_ARG_OPND(3,1)` */' `/* #define NBIF_ARG_OPND_3_2 'NBIF_ARG_OPND(3,2)` */' +`/* #define NBIF_ARG_OPND_4_0 'NBIF_ARG_OPND(4,0)` */' +`/* #define NBIF_ARG_OPND_4_1 'NBIF_ARG_OPND(4,1)` */' +`/* #define NBIF_ARG_OPND_4_2 'NBIF_ARG_OPND(4,2)` */' +`/* #define NBIF_ARG_OPND_4_3 'NBIF_ARG_OPND(4,3)` */' `/* #define NBIF_ARG_OPND_5_0 'NBIF_ARG_OPND(5,0)` */' `/* #define NBIF_ARG_OPND_5_1 'NBIF_ARG_OPND(5,1)` */' `/* #define NBIF_ARG_OPND_5_2 'NBIF_ARG_OPND(5,2)` */' @@ -274,6 +279,7 @@ define(NBIF_RET,`NBIF_RET_N(eval(RET_POP($1)))')dnl `/* #define NBIF_RET_1 'NBIF_RET(1)` */' `/* #define NBIF_RET_2 'NBIF_RET(2)` */' `/* #define NBIF_RET_3 'NBIF_RET(3)` */' +`/* #define NBIF_RET_4 'NBIF_RET(4)` */' `/* #define NBIF_RET_5 'NBIF_RET(5)` */' dnl diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4 index a0f16efa33..b0064ee628 100644 --- a/erts/emulator/hipe/hipe_x86_bifs.m4 +++ b/erts/emulator/hipe/hipe_x86_bifs.m4 @@ -48,6 +48,7 @@ define(HANDLE_GOT_MBUF,` * standard_bif_interface_1(nbif_name, cbif_name) * standard_bif_interface_2(nbif_name, cbif_name) * standard_bif_interface_3(nbif_name, cbif_name) + * standard_bif_interface_4(nbif_name, cbif_name) * standard_bif_interface_0(nbif_name, cbif_name) * * Generate native interface for a BIF with 0-3 parameters and @@ -158,6 +159,43 @@ ASYM($1): TYPE_FUNCTION(ASYM($1)) #endif') +define(standard_bif_interface_4, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + TEXT + .align 4 + GLOBAL(ASYM($1)) +ASYM($1): + /* copy native stack pointer */ + NBIF_COPY_NSP(4) + + /* switch to C stack */ + SWITCH_ERLANG_TO_C + + /* make the call on the C stack */ + NBIF_ARG_REG(0,P) + NBIF_ARG(2,4,0) + NBIF_ARG(3,4,1) + NBIF_ARG(4,4,2) + NBIF_ARG(5,4,3) + lea 8(%esp), %eax + NBIF_ARG_REG(1,%eax) /* BIF__ARGS */ + CALL_BIF($2) + TEST_GOT_MBUF + + /* switch to native stack */ + SWITCH_C_TO_ERLANG + + /* throw exception if failure, otherwise return */ + TEST_GOT_EXN + jz nbif_4_simple_exception + NBIF_RET(4) + HANDLE_GOT_MBUF(4) + SET_SIZE(ASYM($1)) + TYPE_FUNCTION(ASYM($1)) +#endif') + define(standard_bif_interface_0, ` #ifndef HAVE_$1 diff --git a/erts/emulator/hipe/hipe_x86_glue.S b/erts/emulator/hipe/hipe_x86_glue.S index 9d38eaaafd..f124e36a26 100644 --- a/erts/emulator/hipe/hipe_x86_glue.S +++ b/erts/emulator/hipe/hipe_x86_glue.S @@ -299,6 +299,7 @@ ASYM(nbif_fail): GLOBAL(nbif_1_gc_after_bif) GLOBAL(nbif_2_gc_after_bif) GLOBAL(nbif_3_gc_after_bif) + GLOBAL(nbif_4_gc_after_bif) .align 4 nbif_0_gc_after_bif: xorl %edx, %edx @@ -314,6 +315,10 @@ nbif_2_gc_after_bif: .align 4 nbif_3_gc_after_bif: movl $3, %edx + jmp .gc_after_bif + .align 4 +nbif_4_gc_after_bif: + movl $4, %edx /*FALLTHROUGH*/ .align 4 .gc_after_bif: @@ -337,6 +342,7 @@ nbif_3_gc_after_bif: GLOBAL(nbif_1_simple_exception) GLOBAL(nbif_2_simple_exception) GLOBAL(nbif_3_simple_exception) + GLOBAL(nbif_4_simple_exception) .align 4 nbif_0_simple_exception: xorl %eax, %eax @@ -352,6 +358,10 @@ nbif_2_simple_exception: .align 4 nbif_3_simple_exception: movl $3, %eax + jmp .nbif_simple_exception + .align 4 +nbif_4_simple_exception: + movl $4, %eax /*FALLTHROUGH*/ .align 4 .nbif_simple_exception: -- cgit v1.2.3 From af972aaf14a5f53510e692f48f672f7e6805ee6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erland=20Sch=C3=B6nbeck?= Date: Wed, 25 Feb 2015 10:52:56 +0100 Subject: Use new time API and be back-compatible in ssh otp_SUITE: Ignore undefined functions in ssh --- erts/test/otp_SUITE.erl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 171f722357..385353f046 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -95,7 +95,8 @@ undefined_functions(Config) when is_list(Config) -> Undef5 = dialyzer_filter(Undef4), Undef6 = wx_filter(Undef5), Undef7 = gs_filter(Undef6), - Undef = diameter_filter(Undef7), + Undef8 = diameter_filter(Undef7), + Undef = ssh_filter(Undef8), case Undef of [] -> ok; @@ -219,7 +220,7 @@ gs_filter(Undef) -> end. diameter_filter(Undef) -> - %% Filter away function calls that are catched. + %% Filter away function calls that are catched for OTP 18 time API filter(fun({{diameter_lib,_,_},{erlang,convert_time_resolution,3}}) -> false; ({{diameter_lib,_,_},{erlang,monotonic_time,0}}) -> @@ -233,6 +234,13 @@ diameter_filter(Undef) -> (_) -> true end, Undef). +ssh_filter(Undef) -> + %% Filter away function calls that are catched for OTP 18 time API + filter(fun({{ssh_info,_,_},{erlang,timestamp,0}}) -> + false; + (_) -> true + end, Undef). + deprecated_not_in_obsolete(Config) when is_list(Config) -> ?line Server = ?config(xref_server, Config), ?line {ok,DeprecatedFunctions} = xref:q(Server, "DF"), -- cgit v1.2.3 From ec74d0123ae385f201e978c78fd985206a449830 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Mon, 23 Feb 2015 14:14:58 +0100 Subject: hipe: change mfa_info_table lock to rwmutex The hipe_mfa_info_table lock is currently a mutex, causing unnecessary contention and serialization of lookups for apply-like constructs. - change the lock from a mutex to an rwmutex - split hipe_get_na_nofail_locked into a "try" path which only needs a read lock, and a "slow" path which requires a write lock - reimplement hipe_get_na_nofail (used by apply ops etc) to first call the "try" path with a read lock, and if that fails the "slow" path with a write lock - reimplement hipe_get_na_nofail_locked (used by maintenance code) to call the "try" path and then optionally the "slow" path, but without taking locks since its caller already has the write lock - adjust remaining lock ops to take/release full write lock - use _rwlocked as suffix on functions requiring a write lock - change hipe_mfa_get_trampoline to call get_locked not put_locked, allowing it to take a read lock instead of a write lock - change hipe_find_mfa_from_ra() to only take a read lock --- erts/emulator/hipe/hipe_bif0.c | 131 +++++++++++++++++++++++++++-------------- 1 file changed, 86 insertions(+), 45 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index c9eee2acf2..8af174170d 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1217,22 +1217,32 @@ static struct { * they create a new stub for the mfa, which forces locking. * XXX: Redesign apply et al to avoid those updates. */ - erts_smp_mtx_t lock; + erts_smp_rwmtx_t lock; } hipe_mfa_info_table; static inline void hipe_mfa_info_table_init_lock(void) { - erts_smp_mtx_init(&hipe_mfa_info_table.lock, "hipe_mfait_lock"); + erts_smp_rwmtx_init(&hipe_mfa_info_table.lock, "hipe_mfait_lock"); } -static inline void hipe_mfa_info_table_lock(void) +static inline void hipe_mfa_info_table_rlock(void) { - erts_smp_mtx_lock(&hipe_mfa_info_table.lock); + erts_smp_rwmtx_rlock(&hipe_mfa_info_table.lock); } -static inline void hipe_mfa_info_table_unlock(void) +static inline void hipe_mfa_info_table_runlock(void) { - erts_smp_mtx_unlock(&hipe_mfa_info_table.lock); + erts_smp_rwmtx_runlock(&hipe_mfa_info_table.lock); +} + +static inline void hipe_mfa_info_table_rwlock(void) +{ + erts_smp_rwmtx_rwlock(&hipe_mfa_info_table.lock); +} + +static inline void hipe_mfa_info_table_rwunlock(void) +{ + erts_smp_rwmtx_rwunlock(&hipe_mfa_info_table.lock); } #define HIPE_MFA_HASH(M,F,A) ((M) * (F) + (A)) @@ -1333,7 +1343,7 @@ void *hipe_mfa_find_na(Eterm m, Eterm f, unsigned int arity) } #endif -static struct hipe_mfa_info *hipe_mfa_info_table_put_locked(Eterm m, Eterm f, unsigned int arity) +static struct hipe_mfa_info *hipe_mfa_info_table_put_rwlocked(Eterm m, Eterm f, unsigned int arity) { unsigned long h; unsigned int i; @@ -1362,8 +1372,8 @@ static void hipe_mfa_set_na(Eterm m, Eterm f, unsigned int arity, void *address, { struct hipe_mfa_info *p; - hipe_mfa_info_table_lock(); - p = hipe_mfa_info_table_put_locked(m, f, arity); + hipe_mfa_info_table_rwlock(); + p = hipe_mfa_info_table_put_rwlocked(m, f, arity); #ifdef DEBUG_LINKER printf("%s: ", __FUNCTION__); print_mfa(m, f, arity); @@ -1372,7 +1382,7 @@ static void hipe_mfa_set_na(Eterm m, Eterm f, unsigned int arity, void *address, p->local_address = address; if (is_exported) p->remote_address = address; - hipe_mfa_info_table_unlock(); + hipe_mfa_info_table_rwunlock(); } #if defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__) || defined(__arm__) @@ -1381,10 +1391,10 @@ void *hipe_mfa_get_trampoline(Eterm m, Eterm f, unsigned int arity) struct hipe_mfa_info *p; void *trampoline; - hipe_mfa_info_table_lock(); - p = hipe_mfa_info_table_put_locked(m, f, arity); - trampoline = p->trampoline; - hipe_mfa_info_table_unlock(); + hipe_mfa_info_table_rlock(); + p = hipe_mfa_info_table_get_locked(m, f, arity); + trampoline = p ? p->trampoline : NULL; + hipe_mfa_info_table_runlock(); return trampoline; } @@ -1392,10 +1402,10 @@ void hipe_mfa_set_trampoline(Eterm m, Eterm f, unsigned int arity, void *trampol { struct hipe_mfa_info *p; - hipe_mfa_info_table_lock(); - p = hipe_mfa_info_table_put_locked(m, f, arity); + hipe_mfa_info_table_rwlock(); + p = hipe_mfa_info_table_put_rwlocked(m, f, arity); p->trampoline = trampoline; - hipe_mfa_info_table_unlock(); + hipe_mfa_info_table_rwunlock(); } #endif @@ -1426,7 +1436,7 @@ BIF_RETTYPE hipe_bifs_invalidate_funinfo_native_addresses_1(BIF_ALIST_1) struct mfa mfa; struct hipe_mfa_info *p; - hipe_mfa_info_table_lock(); + hipe_mfa_info_table_rwlock(); lst = BIF_ARG_1; while (is_list(lst)) { if (!term_to_mfa(CAR(list_val(lst)), &mfa)) @@ -1455,7 +1465,7 @@ BIF_RETTYPE hipe_bifs_invalidate_funinfo_native_addresses_1(BIF_ALIST_1) } } } - hipe_mfa_info_table_unlock(); + hipe_mfa_info_table_rwunlock(); if (is_not_nil(lst)) BIF_ERROR(BIF_P, BADARG); BIF_RET(NIL); @@ -1469,8 +1479,8 @@ void hipe_mfa_save_orig_beam_op(Eterm mod, Eterm fun, unsigned int ari, Eterm *p orig_beam_op = pc[0]; if (orig_beam_op != BeamOpCode(op_hipe_trap_call_closure) && orig_beam_op != BeamOpCode(op_hipe_trap_call)) { - hipe_mfa_info_table_lock(); - p = hipe_mfa_info_table_put_locked(mod, fun, ari); + hipe_mfa_info_table_rwlock(); + p = hipe_mfa_info_table_put_rwlocked(mod, fun, ari); #ifdef DEBUG_LINKER printf("%s: ", __FUNCTION__); print_mfa(mod, fun, ari); @@ -1478,7 +1488,7 @@ void hipe_mfa_save_orig_beam_op(Eterm mod, Eterm fun, unsigned int ari, Eterm *p #endif p->beam_code = pc; p->orig_beam_op = orig_beam_op; - hipe_mfa_info_table_unlock(); + hipe_mfa_info_table_rwunlock(); } else { #ifdef DEBUG_LINKER printf("%s: ", __FUNCTION__); @@ -1505,7 +1515,7 @@ static void *hipe_make_stub(Eterm m, Eterm f, unsigned int arity, int is_remote) return StubAddress; } -static void *hipe_get_na_nofail_locked(Eterm m, Eterm f, unsigned int a, int is_remote) +static void *hipe_get_na_try_locked(Eterm m, Eterm f, unsigned int a, int is_remote, struct hipe_mfa_info **pp) { struct hipe_mfa_info *p; void *address; @@ -1523,22 +1533,53 @@ static void *hipe_get_na_nofail_locked(Eterm m, Eterm f, unsigned int a, int is_ address = p->remote_address; if (address) return address; - } else - p = hipe_mfa_info_table_put_locked(m, f, a); + } + /* Caller must take the slow path with the write lock held, but allow + it to avoid some work if it already holds the write lock. */ + if (pp) + *pp = p; + return NULL; +} + +static void *hipe_get_na_slow_rwlocked(Eterm m, Eterm f, unsigned int a, int is_remote, struct hipe_mfa_info *p) +{ + void *address; + + if (!p) + p = hipe_mfa_info_table_put_rwlocked(m, f, a); address = hipe_make_stub(m, f, a, is_remote); /* XXX: how to tell if a BEAM MFA is exported or not? */ p->remote_address = address; return address; } +static void *hipe_get_na_nofail_rwlocked(Eterm m, Eterm f, unsigned int a, int is_remote) +{ + struct hipe_mfa_info *p; + void *address; + + address = hipe_get_na_try_locked(m, f, a, is_remote, &p); + if (address) + return address; + + address = hipe_get_na_slow_rwlocked(m, f, a, is_remote, p); + return address; +} + static void *hipe_get_na_nofail(Eterm m, Eterm f, unsigned int a, int is_remote) { - void *p; + void *address; - hipe_mfa_info_table_lock(); - p = hipe_get_na_nofail_locked(m, f, a, is_remote); - hipe_mfa_info_table_unlock(); - return p; + hipe_mfa_info_table_rlock(); + address = hipe_get_na_try_locked(m, f, a, is_remote, NULL); + hipe_mfa_info_table_runlock(); + if (address) + return address; + + hipe_mfa_info_table_rwlock(); + address = hipe_get_na_slow_rwlocked(m, f, a, is_remote, NULL); + hipe_mfa_info_table_rwunlock(); + return address; } /* used for apply/3 in hipe_mode_switch */ @@ -1617,7 +1658,7 @@ int hipe_find_mfa_from_ra(const void *ra, Eterm *m, Eterm *f, unsigned int *a) /* Note about locking: the table is only updated from the loader, which runs with the rest of the system suspended. */ /* XXX: alas not true; see comment at hipe_mfa_info_table.lock */ - hipe_mfa_info_table_lock(); + hipe_mfa_info_table_rlock(); bucket = hipe_mfa_info_table.bucket; nrbuckets = 1 << hipe_mfa_info_table.log2size; mfa = NULL; @@ -1638,7 +1679,7 @@ int hipe_find_mfa_from_ra(const void *ra, Eterm *m, Eterm *f, unsigned int *a) *f = mfa->f; *a = mfa->a; } - hipe_mfa_info_table_unlock(); + hipe_mfa_info_table_runlock(); return mfa ? 1 : 0; } @@ -1715,9 +1756,9 @@ BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2) default: goto badarg; } - hipe_mfa_info_table_lock(); - callee_mfa = hipe_mfa_info_table_put_locked(callee.mod, callee.fun, callee.ari); - caller_mfa = hipe_mfa_info_table_put_locked(caller.mod, caller.fun, caller.ari); + hipe_mfa_info_table_rwlock(); + callee_mfa = hipe_mfa_info_table_put_rwlocked(callee.mod, callee.fun, callee.ari); + caller_mfa = hipe_mfa_info_table_put_rwlocked(caller.mod, caller.fun, caller.ari); refers_to = erts_alloc(ERTS_ALC_T_HIPE, sizeof(*refers_to)); refers_to->mfa = callee_mfa; @@ -1731,7 +1772,7 @@ BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2) ref->flags = flags; ref->next = callee_mfa->referred_from; callee_mfa->referred_from = ref; - hipe_mfa_info_table_unlock(); + hipe_mfa_info_table_rwunlock(); BIF_RET(NIL); @@ -1751,12 +1792,12 @@ BIF_RETTYPE hipe_bifs_mark_referred_from_1(BIF_ALIST_1) /* get_refs_from */ if (!term_to_mfa(BIF_ARG_1, &mfa)) BIF_ERROR(BIF_P, BADARG); - hipe_mfa_info_table_lock(); + hipe_mfa_info_table_rwlock(); p = hipe_mfa_info_table_get_locked(mfa.mod, mfa.fun, mfa.ari); if (p) for (ref = p->referred_from; ref != NULL; ref = ref->next) ref->flags |= REF_FLAG_PENDING_REDIRECT; - hipe_mfa_info_table_unlock(); + hipe_mfa_info_table_rwunlock(); BIF_RET(NIL); } @@ -1770,7 +1811,7 @@ static void hipe_purge_all_refs(void) struct hipe_mfa_info **bucket; unsigned int i, nrbuckets; - hipe_mfa_info_table_lock(); + hipe_mfa_info_table_rwlock(); bucket = hipe_mfa_info_table.bucket; nrbuckets = 1 << hipe_mfa_info_table.log2size; @@ -1792,7 +1833,7 @@ static void hipe_purge_all_refs(void) erts_free(ERTS_ALC_T_HIPE, mfa); } } - hipe_mfa_info_table_unlock(); + hipe_mfa_info_table_rwunlock(); } BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1) @@ -1809,7 +1850,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1) if (!term_to_mfa(BIF_ARG_1, &mfa)) BIF_ERROR(BIF_P, BADARG); - hipe_mfa_info_table_lock(); + hipe_mfa_info_table_rwlock(); caller_mfa = hipe_mfa_info_table_get_locked(mfa.mod, mfa.fun, mfa.ari); if (caller_mfa) { refers_to = caller_mfa->refers_to; @@ -1840,7 +1881,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1) } caller_mfa->refers_to = NULL; } - hipe_mfa_info_table_unlock(); + hipe_mfa_info_table_rwunlock(); BIF_RET(am_ok); } @@ -1859,7 +1900,7 @@ BIF_RETTYPE hipe_bifs_redirect_referred_from_1(BIF_ALIST_1) if (!term_to_mfa(BIF_ARG_1, &mfa)) BIF_ERROR(BIF_P, BADARG); - hipe_mfa_info_table_lock(); + hipe_mfa_info_table_rwlock(); p = hipe_mfa_info_table_get_locked(mfa.mod, mfa.fun, mfa.ari); if (p) { prev = &p->referred_from; @@ -1867,7 +1908,7 @@ BIF_RETTYPE hipe_bifs_redirect_referred_from_1(BIF_ALIST_1) while (ref) { if (ref->flags & REF_FLAG_PENDING_REDIRECT) { is_remote = ref->flags & REF_FLAG_IS_REMOTE; - new_address = hipe_get_na_nofail_locked(p->m, p->f, p->a, is_remote); + new_address = hipe_get_na_nofail_rwlocked(p->m, p->f, p->a, is_remote); if (ref->flags & REF_FLAG_IS_LOAD_MFA) res = hipe_patch_insn(ref->address, (Uint)new_address, am_load_mfa); else @@ -1890,7 +1931,7 @@ BIF_RETTYPE hipe_bifs_redirect_referred_from_1(BIF_ALIST_1) } } } - hipe_mfa_info_table_unlock(); + hipe_mfa_info_table_rwunlock(); BIF_RET(NIL); } -- cgit v1.2.3 From c472fc520fc30d6d19adc89bdc42817870274db2 Mon Sep 17 00:00:00 2001 From: Leo Liu Date: Fri, 12 Dec 2014 20:56:40 +0800 Subject: Two minor fixes * Fix documentation on $char for unicode * Remove duplicate declaration for erts_encode_ext_dist_header_size --- erts/emulator/beam/external.h | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index bf00958eb1..10565f67e5 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -156,7 +156,6 @@ void erts_init_atom_cache_map(ErtsAtomCacheMap *); void erts_reset_atom_cache_map(ErtsAtomCacheMap *); void erts_destroy_atom_cache_map(ErtsAtomCacheMap *); void erts_finalize_atom_cache_map(ErtsAtomCacheMap *, Uint32); -Uint erts_encode_ext_dist_header_size(ErtsAtomCacheMap *); Uint erts_encode_ext_dist_header_size(ErtsAtomCacheMap *); byte *erts_encode_ext_dist_header_setup(byte *, ErtsAtomCacheMap *); -- cgit v1.2.3 From af4957d6224bfa529fb3a2087f41035eb13b2c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Tue, 3 Mar 2015 20:00:49 +0100 Subject: Export useful types from zlib module --- erts/preloaded/src/zlib.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index df7b2e6198..f028495a30 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -30,7 +30,7 @@ compress/1,uncompress/1,zip/1,unzip/1, gzip/1,gunzip/1]). --export_type([zstream/0]). +-export_type([zstream/0, zlevel/0, zwindowbits/0, zmemlevel/0, zstrategy/0]). %% flush argument encoding -define(Z_NO_FLUSH, 0). -- cgit v1.2.3 From 7d82a632f4837764ac79dfb4986d102060bd3080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Tue, 3 Mar 2015 18:55:22 +0100 Subject: Update zlib:zwindowbits/0 type to accept 8 and -8 Commit 7e8f5a776cbfa376e03369d058a90c8dd9f217fc (importing R11B-3) updated zlib, which had changed what values it accepts for window bits from 9-15 to 8-15. From deflate.c: - windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || - strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { return Z_STREAM_ERROR; } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ In inflate.c 8 was already an accepted value. This commit updates OTP to also accept the values 8 and -8. --- erts/doc/src/zlib.xml | 6 +++--- erts/preloaded/src/zlib.erl | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index da8ccdecdf..b46baad7b5 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -99,7 +99,7 @@ list_to_binary([Compressed|Last]) -

Normally in the range -15..-9 | 9..15.

+

Normally in the range -15..-8 | 8..15.

@@ -149,7 +149,7 @@ list_to_binary([Compressed|Last]) currently the only supported method is deflated.

The WindowBits parameter is the base two logarithm of the window size (the size of the history buffer). It - should be in the range 9 through 15. Larger values + should be in the range 8 through 15. Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if deflateInit/2. A negative WindowBits @@ -288,7 +288,7 @@ list_to_binary([B1,B2])

Initialize decompression session on zlib stream.

The WindowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). - It should be in the range 9 through 15. + It should be in the range 8 through 15. The default value is 15 if inflateInit/1 is used. If a compressed stream with a larger window size is given as input, inflate() will throw the data_error diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index df7b2e6198..988d837e3a 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -124,7 +124,7 @@ -type zlevel() :: 'none' | 'default' | 'best_compression' | 'best_speed' | 0..9. -type zmethod() :: 'deflated'. --type zwindowbits() :: -15..-9 | 9..47. +-type zwindowbits() :: -15..-8 | 8..47. -type zmemlevel() :: 1..9. -type zstrategy() :: 'default' | 'filtered' | 'huffman_only' | 'rle'. @@ -496,8 +496,8 @@ arg_method(_) -> erlang:error(badarg). -spec arg_bitsz(zwindowbits()) -> zwindowbits(). arg_bitsz(Bits) when is_integer(Bits) andalso - ((8 < Bits andalso Bits < 48) orelse - (-15 =< Bits andalso Bits < -8)) -> + ((8 =< Bits andalso Bits < 48) orelse + (-15 =< Bits andalso Bits =< -8)) -> Bits; arg_bitsz(_) -> erlang:error(badarg). -- cgit v1.2.3 From e330a493032c35c0ca69cd78142e2ec03f5e370b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 4 Mar 2015 17:08:31 +0100 Subject: erts: Enable command line argument for initial pd size Use '+hpds size' to set initial process dictionary size for spawned processes. --- erts/emulator/beam/erl_init.c | 13 +++++++++++++ erts/emulator/beam/erl_process_dict.c | 2 +- erts/emulator/beam/erl_vm.h | 1 + erts/etc/common/erlexec.c | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 61f8385efc..743fd235bc 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -194,6 +194,8 @@ int erts_disable_tolerant_timeofday; /* Time correction can be disabled it is int erts_atom_table_size = ATOM_LIMIT; /* Maximum number of atoms */ +int erts_pd_initial_size = 10; + int erts_modified_timing_level; int erts_no_crash_dump = 0; /* Use -d to suppress crash dump. */ @@ -519,6 +521,8 @@ void erts_usage(void) H_DEFAULT_SIZE); erts_fprintf(stderr, "-hmbs size set minimum binary virtual heap size in words (default %d)\n", VH_DEFAULT_SIZE); + erts_fprintf(stderr, "-hpds size initial process dictionary size (default %d)\n", + erts_pd_initial_size); /* erts_fprintf(stderr, "-i module set the boot module (default init)\n"); */ @@ -1408,6 +1412,7 @@ erl_start(int argc, char **argv) * * h|ms - min_heap_size * h|mbs - min_bin_vheap_size + * h|pds - erts_pd_initial_size * */ if (has_prefix("mbs", sub_param)) { @@ -1425,6 +1430,14 @@ erl_start(int argc, char **argv) erts_usage(); } VERBOSE(DEBUG_SYSTEM, ("using minimum heap size %d\n", H_MIN_SIZE)); + } else if (has_prefix("pds", sub_param)) { + arg = get_arg(sub_param+3, argv[i+1], &i); + if ((erts_pd_initial_size = atoi(arg)) <= 0) { + erts_fprintf(stderr, "bad initial process dictionary size %s\n", arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, ("using initial process dictionary size %d\n", + erts_pd_initial_size)); } else { /* backward compatibility */ arg = get_arg(argv[i]+2, argv[i+1], &i); diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 23e5bf737f..af20b26b15 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -47,7 +47,7 @@ /* Hash constant macros */ #define MAX_HASH 1342177280UL -#define INITIAL_SIZE 10 +#define INITIAL_SIZE (erts_pd_initial_size) /* Hash utility macros */ #define HASH_RANGE(PDict) ((PDict)->homeSize + (PDict)->splitPosition) diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index b7de8208ad..6687b044ee 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -174,6 +174,7 @@ extern int H_MIN_SIZE; /* minimum (heap + stack) */ extern int BIN_VH_MIN_SIZE; /* minimum virtual (bin) heap */ extern int erts_atom_table_size;/* Atom table size */ +extern int erts_pd_initial_size;/* Initial Process dictionary table size */ #define ORIG_CREATION 0 #define INTERNAL_CREATION 255 diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 5ebde8ca3c..b68e109b43 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -143,6 +143,7 @@ static char *pluss_val_switches[] = { static char *plush_val_switches[] = { "ms", "mbs", + "pds", "", NULL }; -- cgit v1.2.3 From 93f0ff928b89dce4e62e4017065d87fcc5cf4cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 4 Mar 2015 17:28:37 +0100 Subject: erts: Document option 'hpds' --- erts/doc/src/erl.xml | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index d11f6b0c6d..f08467bfc6 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -588,6 +588,11 @@

Sets the default binary virtual heap size of processes to the size .

+ + +

Sets the initial process dictionary size of processes to the size + .

+

Enables or disables the kernel poll functionality if -- cgit v1.2.3 From 2b69d295dc73f845305a0e2d7763a53657af0725 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 6 Mar 2015 09:23:42 +0100 Subject: Make access to control_flags safe in non-smp emulator --- erts/emulator/beam/io.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 9ae973e108..86fb0a39bf 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4087,6 +4087,9 @@ erts_port_control(Process* c_p, size, &resp_bufp, &resp_size); + + control_flags = prt->control_flags; + finalize_imm_drv_call(&try_call_state); if (tmp_alloced) erts_free(ERTS_ALC_T_TMP, bufp); @@ -4094,8 +4097,6 @@ erts_port_control(Process* c_p, return ERTS_PORT_OP_BADARG; } - control_flags = prt->control_flags; - hsz = port_control_result_size(control_flags, resp_bufp, &resp_size, -- cgit v1.2.3 From cdf87da805533282e5efb214a4dd9fc6e3049266 Mon Sep 17 00:00:00 2001 From: Nick Mills Date: Sat, 7 Mar 2015 16:32:32 -0500 Subject: Correct typo in erlang(3) documentation --- erts/doc/src/erlang.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index cba2c07959..0e5909a52d 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1356,7 +1356,7 @@ true Return a value from the process dictionary -

Returns the value Valassociated with Key in +

Returns the value Val associated with Key in the process dictionary, or undefined if Key does not exist.

-- 
cgit v1.2.3


From de657dee68a37e083d000bfa2767b9740d996ffd Mon Sep 17 00:00:00 2001
From: Maxim Mai 
Date: Tue, 10 Mar 2015 13:10:49 -0400
Subject: Update config.guess and config.sub to latest versions

---
 erts/autoconf/config.guess | 342 ++++++++++++++++-----------------------------
 erts/autoconf/config.sub   |  69 +++++----
 2 files changed, 168 insertions(+), 243 deletions(-)

(limited to 'erts')

diff --git a/erts/autoconf/config.guess b/erts/autoconf/config.guess
index f475ceb413..f7eb141e75 100755
--- a/erts/autoconf/config.guess
+++ b/erts/autoconf/config.guess
@@ -1,8 +1,8 @@
 #! /bin/sh
 # Attempt to guess a canonical system name.
-#   Copyright 1992-2013 Free Software Foundation, Inc.
+#   Copyright 1992-2015 Free Software Foundation, Inc.
 
-timestamp='2013-02-12'
+timestamp='2015-03-04'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -24,12 +24,12 @@ timestamp='2013-02-12'
 # program.  This Exception is an additional permission under section 7
 # of the GNU General Public License, version 3 ("GPLv3").
 #
-# Originally written by Per Bothner.
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
 #
 # You can get the latest version of this script from:
 # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
 #
-# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+# Please send patches to .
 
 
 me=`echo "$0" | sed -e 's,.*/,,'`
@@ -50,7 +50,7 @@ version="\
 GNU config.guess ($timestamp)
 
 Originally written by Per Bothner.
-Copyright 1992-2013 Free Software Foundation, Inc.
+Copyright 1992-2015 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -132,6 +132,27 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
 UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
 UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
 
+case "${UNAME_SYSTEM}" in
+Linux|GNU|GNU/*)
+	# If the system lacks a compiler, then just pick glibc.
+	# We could probably try harder.
+	LIBC=gnu
+
+	eval $set_cc_for_build
+	cat <<-EOF > $dummy.c
+	#include 
+	#if defined(__UCLIBC__)
+	LIBC=uclibc
+	#elif defined(__dietlibc__)
+	LIBC=dietlibc
+	#else
+	LIBC=gnu
+	#endif
+	EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
+	;;
+esac
+
 # Note: order is significant - the case branches are not exclusive.
 
 case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
@@ -147,20 +168,27 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 	# Note: NetBSD doesn't particularly care about the vendor
 	# portion of the name.  We always set it to "unknown".
 	sysctl="sysctl -n hw.machine_arch"
-	UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
-	    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+	UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+	    /sbin/$sysctl 2>/dev/null || \
+	    /usr/sbin/$sysctl 2>/dev/null || \
+	    echo unknown)`
 	case "${UNAME_MACHINE_ARCH}" in
 	    armeb) machine=armeb-unknown ;;
 	    arm*) machine=arm-unknown ;;
 	    sh3el) machine=shl-unknown ;;
 	    sh3eb) machine=sh-unknown ;;
 	    sh5el) machine=sh5le-unknown ;;
+	    earmv*)
+		arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+		endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'`
+		machine=${arch}${endian}-unknown
+		;;
 	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
 	esac
 	# The Operating System including object format, if it has switched
 	# to ELF recently, or will in the future.
 	case "${UNAME_MACHINE_ARCH}" in
-	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+	    arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax)
 		eval $set_cc_for_build
 		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
 			| grep -q __ELF__
@@ -176,6 +204,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 		os=netbsd
 		;;
 	esac
+	# Determine ABI tags.
+	case "${UNAME_MACHINE_ARCH}" in
+	    earm*)
+		expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+		abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"`
+		;;
+	esac
 	# The OS release
 	# Debian GNU/NetBSD machines have a different userland, and
 	# thus, need a distinct triplet. However, they do not need
@@ -192,7 +227,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
 	# contains redundant information, the shorter form:
 	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
-	echo "${machine}-${os}${release}"
+	echo "${machine}-${os}${release}${abi}"
 	exit ;;
     *:Bitrig:*:*)
 	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
@@ -558,8 +593,9 @@ EOF
 	else
 		IBM_ARCH=powerpc
 	fi
-	if [ -x /usr/bin/oslevel ] ; then
-		IBM_REV=`/usr/bin/oslevel`
+	if [ -x /usr/bin/lslpp ] ; then
+		IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+			   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
 	else
 		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
 	fi
@@ -805,7 +841,7 @@ EOF
     *:MINGW*:*)
 	echo ${UNAME_MACHINE}-pc-mingw32
 	exit ;;
-    i*:MSYS*:*)
+    *:MSYS*:*)
 	echo ${UNAME_MACHINE}-pc-msys
 	exit ;;
     i*:windows32*:*)
@@ -853,21 +889,21 @@ EOF
 	exit ;;
     *:GNU:*:*)
 	# the GNU system
-	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
 	exit ;;
     *:GNU/*:*:*)
 	# other systems with GNU libc and userland
-	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
 	exit ;;
     i*86:Minix:*:*)
 	echo ${UNAME_MACHINE}-pc-minix
 	exit ;;
     aarch64:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     aarch64_be:Linux:*:*)
 	UNAME_MACHINE=aarch64_be
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     alpha:Linux:*:*)
 	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
@@ -880,59 +916,57 @@ EOF
 	  EV68*) UNAME_MACHINE=alphaev68 ;;
 	esac
 	objdump --private-headers /bin/sh | grep -q ld.so.1
-	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
-	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+	if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    arc:Linux:*:* | arceb:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     arm*:Linux:*:*)
 	eval $set_cc_for_build
 	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
 	    | grep -q __ARM_EABI__
 	then
-	    echo ${UNAME_MACHINE}-unknown-linux-gnu
+	    echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	else
 	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
 		| grep -q __ARM_PCS_VFP
 	    then
-		echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
 	    else
-		echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
 	    fi
 	fi
 	exit ;;
     avr32*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     cris:Linux:*:*)
-	echo ${UNAME_MACHINE}-axis-linux-gnu
+	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
 	exit ;;
     crisv32:Linux:*:*)
-	echo ${UNAME_MACHINE}-axis-linux-gnu
+	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
+	exit ;;
+    e2k:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     frv:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     hexagon:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     i*86:Linux:*:*)
-	LIBC=gnu
-	eval $set_cc_for_build
-	sed 's/^	//' << EOF >$dummy.c
-	#ifdef __dietlibc__
-	LIBC=dietlibc
-	#endif
-EOF
-	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
-	echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+	echo ${UNAME_MACHINE}-pc-linux-${LIBC}
 	exit ;;
     ia64:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     m32r*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     m68*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     mips:Linux:*:* | mips64:Linux:*:*)
 	eval $set_cc_for_build
@@ -951,57 +985,63 @@ EOF
 	#endif
 EOF
 	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
-	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
 	;;
-    or1k:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+    openrisc*:Linux:*:*)
+	echo or1k-unknown-linux-${LIBC}
 	exit ;;
-    or32:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+    or32:Linux:*:* | or1k*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     padre:Linux:*:*)
-	echo sparc-unknown-linux-gnu
+	echo sparc-unknown-linux-${LIBC}
 	exit ;;
     parisc64:Linux:*:* | hppa64:Linux:*:*)
-	echo hppa64-unknown-linux-gnu
+	echo hppa64-unknown-linux-${LIBC}
 	exit ;;
     parisc:Linux:*:* | hppa:Linux:*:*)
 	# Look for CPU level
 	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
-	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
-	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
-	  *)    echo hppa-unknown-linux-gnu ;;
+	  PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
+	  PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
+	  *)    echo hppa-unknown-linux-${LIBC} ;;
 	esac
 	exit ;;
     ppc64:Linux:*:*)
-	echo powerpc64-unknown-linux-gnu
+	echo powerpc64-unknown-linux-${LIBC}
 	exit ;;
     ppc:Linux:*:*)
-	echo powerpc-unknown-linux-gnu
+	echo powerpc-unknown-linux-${LIBC}
+	exit ;;
+    ppc64le:Linux:*:*)
+	echo powerpc64le-unknown-linux-${LIBC}
+	exit ;;
+    ppcle:Linux:*:*)
+	echo powerpcle-unknown-linux-${LIBC}
 	exit ;;
     s390:Linux:*:* | s390x:Linux:*:*)
-	echo ${UNAME_MACHINE}-ibm-linux
+	echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
 	exit ;;
     sh64*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     sh*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     sparc:Linux:*:* | sparc64:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     tile*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     vax:Linux:*:*)
-	echo ${UNAME_MACHINE}-dec-linux-gnu
+	echo ${UNAME_MACHINE}-dec-linux-${LIBC}
 	exit ;;
     x86_64:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     xtensa*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     i*86:DYNIX/ptx:4*:*)
 	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
@@ -1234,19 +1274,31 @@ EOF
 	exit ;;
     *:Darwin:*:*)
 	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
-	case $UNAME_PROCESSOR in
-	    i386)
-		eval $set_cc_for_build
-		if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
-		  if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
-		      (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
-		      grep IS_64BIT_ARCH >/dev/null
-		  then
-		      UNAME_PROCESSOR="x86_64"
-		  fi
-		fi ;;
-	    unknown) UNAME_PROCESSOR=powerpc ;;
-	esac
+	eval $set_cc_for_build
+	if test "$UNAME_PROCESSOR" = unknown ; then
+	    UNAME_PROCESSOR=powerpc
+	fi
+	if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
+	    if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+		if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+		    (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+		    grep IS_64BIT_ARCH >/dev/null
+		then
+		    case $UNAME_PROCESSOR in
+			i386) UNAME_PROCESSOR=x86_64 ;;
+			powerpc) UNAME_PROCESSOR=powerpc64 ;;
+		    esac
+		fi
+	    fi
+	elif test "$UNAME_PROCESSOR" = i386 ; then
+	    # Avoid executing cc on OS X 10.9, as it ships with a stub
+	    # that puts up a graphical alert prompting to install
+	    # developer tools.  Any system running Mac OS X 10.7 or
+	    # later (Darwin 11 and later) is required to have a 64-bit
+	    # processor. This is not true of the ARM version of Darwin
+	    # that Apple uses in portable devices.
+	    UNAME_PROCESSOR=x86_64
+	fi
 	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
 	exit ;;
     *:procnto*:*:* | *:QNX:[0123456789]*:*)
@@ -1337,154 +1389,6 @@ EOF
 	exit ;;
 esac
 
-eval $set_cc_for_build
-cat >$dummy.c <
-# include 
-#endif
-main ()
-{
-#if defined (sony)
-#if defined (MIPSEB)
-  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
-     I don't know....  */
-  printf ("mips-sony-bsd\n"); exit (0);
-#else
-#include 
-  printf ("m68k-sony-newsos%s\n",
-#ifdef NEWSOS4
-	"4"
-#else
-	""
-#endif
-	); exit (0);
-#endif
-#endif
-
-#if defined (__arm) && defined (__acorn) && defined (__unix)
-  printf ("arm-acorn-riscix\n"); exit (0);
-#endif
-
-#if defined (hp300) && !defined (hpux)
-  printf ("m68k-hp-bsd\n"); exit (0);
-#endif
-
-#if defined (NeXT)
-#if !defined (__ARCHITECTURE__)
-#define __ARCHITECTURE__ "m68k"
-#endif
-  int version;
-  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
-  if (version < 4)
-    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
-  else
-    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
-  exit (0);
-#endif
-
-#if defined (MULTIMAX) || defined (n16)
-#if defined (UMAXV)
-  printf ("ns32k-encore-sysv\n"); exit (0);
-#else
-#if defined (CMU)
-  printf ("ns32k-encore-mach\n"); exit (0);
-#else
-  printf ("ns32k-encore-bsd\n"); exit (0);
-#endif
-#endif
-#endif
-
-#if defined (__386BSD__)
-  printf ("i386-pc-bsd\n"); exit (0);
-#endif
-
-#if defined (sequent)
-#if defined (i386)
-  printf ("i386-sequent-dynix\n"); exit (0);
-#endif
-#if defined (ns32000)
-  printf ("ns32k-sequent-dynix\n"); exit (0);
-#endif
-#endif
-
-#if defined (_SEQUENT_)
-    struct utsname un;
-
-    uname(&un);
-
-    if (strncmp(un.version, "V2", 2) == 0) {
-	printf ("i386-sequent-ptx2\n"); exit (0);
-    }
-    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
-	printf ("i386-sequent-ptx1\n"); exit (0);
-    }
-    printf ("i386-sequent-ptx\n"); exit (0);
-
-#endif
-
-#if defined (vax)
-# if !defined (ultrix)
-#  include 
-#  if defined (BSD)
-#   if BSD == 43
-      printf ("vax-dec-bsd4.3\n"); exit (0);
-#   else
-#    if BSD == 199006
-      printf ("vax-dec-bsd4.3reno\n"); exit (0);
-#    else
-      printf ("vax-dec-bsd\n"); exit (0);
-#    endif
-#   endif
-#  else
-    printf ("vax-dec-bsd\n"); exit (0);
-#  endif
-# else
-    printf ("vax-dec-ultrix\n"); exit (0);
-# endif
-#endif
-
-#if defined (alliant) && defined (i860)
-  printf ("i860-alliant-bsd\n"); exit (0);
-#endif
-
-  exit (1);
-}
-EOF
-
-$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
-	{ echo "$SYSTEM_NAME"; exit; }
-
-# Apollos put the system type in the environment.
-
-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
-
-# Convex versions that predate uname can use getsysinfo(1)
-
-if [ -x /usr/convex/getsysinfo ]
-then
-    case `getsysinfo -f cpu_type` in
-    c1*)
-	echo c1-convex-bsd
-	exit ;;
-    c2*)
-	if getsysinfo -f scalar_acc
-	then echo c32-convex-bsd
-	else echo c2-convex-bsd
-	fi
-	exit ;;
-    c34*)
-	echo c34-convex-bsd
-	exit ;;
-    c38*)
-	echo c38-convex-bsd
-	exit ;;
-    c4*)
-	echo c4-convex-bsd
-	exit ;;
-    esac
-fi
-
 cat >&2 <.
 #
 # Configuration subroutine to validate and canonicalize a configuration type.
 # Supply the specified configuration type as an argument.
@@ -68,7 +68,7 @@ Report bugs and patches to ."
 version="\
 GNU config.sub ($timestamp)
 
-Copyright 1992-2013 Free Software Foundation, Inc.
+Copyright 1992-2015 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -117,7 +117,7 @@ maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
 case $maybe_os in
   nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
   linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
-  knetbsd*-gnu* | netbsd*-gnu* | \
+  knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
   kopensolaris*-gnu* | \
   storm-chaos* | os2-emx* | rtmk-nova*)
     os=-$maybe_os
@@ -252,19 +252,20 @@ case $basic_machine in
 	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
 	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
 	| am33_2.0 \
-	| arc \
+	| arc | arceb \
 	| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
 	| avr | avr32 \
 	| be32 | be64 \
 	| bfin \
-	| c4x | clipper \
+	| c4x | c8051 | clipper \
 	| d10v | d30v | dlx | dsp16xx \
-	| epiphany \
-	| fido | fr30 | frv \
+	| e2k | epiphany \
+	| fido | fr30 | frv | ft32 \
 	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
 	| hexagon \
 	| i370 | i860 | i960 | ia64 \
 	| ip2k | iq2000 \
+	| k1om \
 	| le32 | le64 \
 	| lm32 \
 	| m32c | m32r | m32rle | m68000 | m68k | m88k \
@@ -282,8 +283,10 @@ case $basic_machine in
 	| mips64vr5900 | mips64vr5900el \
 	| mipsisa32 | mipsisa32el \
 	| mipsisa32r2 | mipsisa32r2el \
+	| mipsisa32r6 | mipsisa32r6el \
 	| mipsisa64 | mipsisa64el \
 	| mipsisa64r2 | mipsisa64r2el \
+	| mipsisa64r6 | mipsisa64r6el \
 	| mipsisa64sb1 | mipsisa64sb1el \
 	| mipsisa64sr71k | mipsisa64sr71kel \
 	| mipsr5900 | mipsr5900el \
@@ -295,11 +298,11 @@ case $basic_machine in
 	| nds32 | nds32le | nds32be \
 	| nios | nios2 | nios2eb | nios2el \
 	| ns16k | ns32k \
-	| open8 \
-	| or1k | or32 \
+	| open8 | or1k | or1knd | or32 \
 	| pdp10 | pdp11 | pj | pjl \
 	| powerpc | powerpc64 | powerpc64le | powerpcle \
 	| pyramid \
+	| riscv32 | riscv64 \
 	| rl78 | rx \
 	| score \
 	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
@@ -310,6 +313,7 @@ case $basic_machine in
 	| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
 	| ubicom32 \
 	| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+	| visium \
 	| we32k \
 	| x86 | xc16x | xstormy16 | xtensa \
 	| z8k | z80)
@@ -324,7 +328,10 @@ case $basic_machine in
 	c6x)
 		basic_machine=tic6x-unknown
 		;;
-	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
+	leon|leon[3-9])
+		basic_machine=sparc-$basic_machine
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
 		basic_machine=$basic_machine-unknown
 		os=-none
 		;;
@@ -366,21 +373,22 @@ case $basic_machine in
 	| aarch64-* | aarch64_be-* \
 	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
 	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
-	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
 	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
 	| avr-* | avr32-* \
 	| be32-* | be64-* \
 	| bfin-* | bs2000-* \
 	| c[123]* | c30-* | [cjt]90-* | c4x-* \
-	| clipper-* | craynv-* | cydra-* \
+	| c8051-* | clipper-* | craynv-* | cydra-* \
 	| d10v-* | d30v-* | dlx-* \
-	| elxsi-* \
+	| e2k-* | elxsi-* \
 	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
 	| h8300-* | h8500-* \
 	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
 	| hexagon-* \
 	| i*86-* | i860-* | i960-* | ia64-* \
 	| ip2k-* | iq2000-* \
+	| k1om-* \
 	| le32-* | le64-* \
 	| lm32-* \
 	| m32c-* | m32r-* | m32rle-* \
@@ -400,8 +408,10 @@ case $basic_machine in
 	| mips64vr5900-* | mips64vr5900el-* \
 	| mipsisa32-* | mipsisa32el-* \
 	| mipsisa32r2-* | mipsisa32r2el-* \
+	| mipsisa32r6-* | mipsisa32r6el-* \
 	| mipsisa64-* | mipsisa64el-* \
 	| mipsisa64r2-* | mipsisa64r2el-* \
+	| mipsisa64r6-* | mipsisa64r6el-* \
 	| mipsisa64sb1-* | mipsisa64sb1el-* \
 	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
 	| mipsr5900-* | mipsr5900el-* \
@@ -413,6 +423,7 @@ case $basic_machine in
 	| nios-* | nios2-* | nios2eb-* | nios2el-* \
 	| none-* | np1-* | ns16k-* | ns32k-* \
 	| open8-* \
+	| or1k*-* \
 	| orion-* \
 	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
 	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
@@ -430,6 +441,7 @@ case $basic_machine in
 	| ubicom32-* \
 	| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
 	| vax-* \
+	| visium-* \
 	| we32k-* \
 	| x86-* | x86_64-* | xc16x-* | xps100-* \
 	| xstormy16-* | xtensa*-* \
@@ -506,6 +518,9 @@ case $basic_machine in
 		basic_machine=i386-pc
 		os=-aros
 		;;
+        asmjs)
+		basic_machine=asmjs-unknown
+		;;
 	aux)
 		basic_machine=m68k-apple
 		os=-aux
@@ -767,6 +782,9 @@ case $basic_machine in
 		basic_machine=m68k-isi
 		os=-sysv
 		;;
+	leon-*|leon[3-9]-*)
+		basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
+		;;
 	m68knommu)
 		basic_machine=m68k-unknown
 		os=-linux
@@ -794,7 +812,7 @@ case $basic_machine in
 		os=-mingw64
 		;;
 	mingw32)
-		basic_machine=i386-pc
+		basic_machine=i686-pc
 		os=-mingw32
 		;;
 	mingw32ce)
@@ -822,6 +840,10 @@ case $basic_machine in
 		basic_machine=powerpc-unknown
 		os=-morphos
 		;;
+	moxiebox)
+		basic_machine=moxie-unknown
+		os=-moxiebox
+		;;
 	msdos)
 		basic_machine=i386-pc
 		os=-msdos
@@ -830,7 +852,7 @@ case $basic_machine in
 		basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
 		;;
 	msys)
-		basic_machine=i386-pc
+		basic_machine=i686-pc
 		os=-msys
 		;;
 	mvs)
@@ -1354,7 +1376,7 @@ case $os in
 	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
 	      | -sym* | -kopensolaris* | -plan9* \
 	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
-	      | -aos* | -aros* \
+	      | -aos* | -aros* | -cloudabi* \
 	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
 	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
 	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
@@ -1367,14 +1389,14 @@ case $os in
 	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
 	      | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
 	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \
-	      | -uxpv* | -beos* | -mpeix* | -udk* \
+	      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
 	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
 	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
 	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
 	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
 	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
 	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
-	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
 	# Remember, each alternative MUST END IN *, to match a version number.
 		;;
 	-qnx*)
@@ -1546,6 +1568,9 @@ case $basic_machine in
 	c4x-* | tic4x-*)
 		os=-coff
 		;;
+	c8051-*)
+		os=-elf
+		;;
 	hexagon-*)
 		os=-elf
 		;;
@@ -1589,9 +1614,6 @@ case $basic_machine in
 	mips*-*)
 		os=-elf
 		;;
-	or1k-*)
-		os=-elf
-		;;
 	or32-*)
 		os=-coff
 		;;
@@ -1786,4 +1808,3 @@ exit
 # time-stamp-format: "%:y-%02m-%02d"
 # time-stamp-end: "'"
 # End:
-
-- 
cgit v1.2.3


From d6073d78109f026ef96b29af4ce748242df2389d Mon Sep 17 00:00:00 2001
From: Anthony Ramine 
Date: Wed, 11 Mar 2015 14:03:58 +0100
Subject: Fix compilation of match specs with maps
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The previous compilation was just plain wrong with push/pop mismatches.

Reported-by: Björn-Egil Dahlberg
---
 erts/emulator/beam/erl_db_util.c        | 63 ++++++++++++++++++++-------------
 erts/emulator/test/match_spec_SUITE.erl |  3 ++
 2 files changed, 42 insertions(+), 24 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 7eb80e3bb1..1d986f2447 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -214,8 +214,8 @@ typedef enum {
     matchPushT,
     matchPushL,
     matchPushM,
-    matchPushK,
     matchPop,
+    matchSwap,
     matchBind,
     matchCmp,
     matchEqBin,
@@ -225,6 +225,7 @@ typedef enum {
     matchEq,
     matchList,
     matchMap,
+    matchKey,
     matchSkip,
     matchPushC,
     matchConsA, /* Car is below Cdr */
@@ -1399,22 +1400,26 @@ restart:
                             }
                             goto error;
                         }
-                        DMC_PUSH(text, matchPushK);
-                        ++(context.stack_used);
+                        DMC_PUSH(text, matchKey);
                         DMC_PUSH(text, dmc_private_copy(&context, key));
-                    }
-                    if (context.stack_used > context.stack_need) {
-                        context.stack_need = context.stack_used;
-                    }
-                    for (i = num_iters; i--; ) {
-                        Eterm value = map_get_values(map_val(t))[i];
-                        DMC_PUSH(text, matchPop);
-                        --(context.stack_used);
-                        res = dmc_one_term(&context, &heap, &stack, &text,
-                                           value);
-                        ASSERT(res != retFail);
-                        if (res == retRestart) {
-                            goto restart;
+                        {
+                            int old_stack = ++(context.stack_used);
+                            Eterm value = map_get_values(map_val(t))[i];
+                            res = dmc_one_term(&context, &heap, &stack, &text,
+                                               value);
+                            ASSERT(res != retFail);
+                            if (res == retRestart) {
+                                goto restart;
+                            }
+                            if (old_stack != context.stack_used) {
+                                ASSERT(old_stack + 1 == context.stack_used);
+                                DMC_PUSH(text, matchSwap);
+                            }
+                            if (context.stack_used > context.stack_need) {
+                                context.stack_need = context.stack_used;
+                            }
+                            DMC_PUSH(text, matchPop);
+                            --(context.stack_used);
                         }
                     }
                     break;
@@ -1960,17 +1965,23 @@ restart:
             }
             *sp++ = map_val_rel(*ep++, base);
             break;
-        case matchPushK:
+        case matchKey:
             t = (Eterm) *pc++;
             tp = erts_maps_get_rel(t, make_map_rel(ep, base), base);
             if (!tp) {
                 FAIL();
             }
-            *sp++ = tp;
+            *sp++ = ep;
+            ep = tp;
             break;
 	case matchPop:
 	    ep = *(--sp);
 	    break;
+        case matchSwap:
+            tp = sp[-1];
+            sp[-1] = sp[-2];
+            sp[-2] = tp;
+            break;
 	case matchBind:
 	    n = *pc++;
 	    variables[n].term = *ep++;
@@ -5302,6 +5313,12 @@ void db_match_dis(Binary *bp)
             ++t;
             erts_printf("Map\t%beu\n", n);
             break;
+        case matchKey:
+            ++t;
+            p = (Eterm) *t;
+            ++t;
+            erts_printf("Key\t%p (%T)\n", t, p);
+            break;
 	case matchPushT:
 	    ++t;
 	    n = *t;
@@ -5318,16 +5335,14 @@ void db_match_dis(Binary *bp)
             ++t;
             erts_printf("PushM\t%beu\n", n);
             break;
-        case matchPushK:
-            ++t;
-            p = (Eterm) *t;
-            ++t;
-            erts_printf("PushK\t%p (%T)\n", t, p);
-            break;
 	case matchPop:
 	    ++t;
 	    erts_printf("Pop\n");
 	    break;
+        case matchSwap:
+            ++t;
+            erts_printf("Swap\n");
+            break;
 	case matchBind:
 	    ++t;
 	    n = *t;
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index fc4a5028e1..02ef4f8e3f 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -924,6 +924,9 @@ maps(Config) when is_list(Config) ->
                                table),
     {ok,#{foo := 3},[],[]} =
         erlang:match_spec_test({}, [{{},[],[#{foo => {'+',1,2}}]}], table),
+    {ok,"camembert",[],[]} =
+        erlang:match_spec_test(#{b => "camembert",c => "cabécou"},
+                               [{#{b => '$1',c => "cabécou"},[],['$1']}], table),
     ok.
 
 empty_list(Config) when is_list(Config) ->
-- 
cgit v1.2.3


From 36576d7de20e4e8cb7b3943cfe1b7e272b5e3971 Mon Sep 17 00:00:00 2001
From: Anthony Ramine 
Date: Wed, 11 Mar 2015 14:21:37 +0100
Subject: Properly collect variables in match specs with maps

---
 erts/emulator/beam/erl_db_util.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 1d986f2447..748be93fe3 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -3286,7 +3286,14 @@ int db_has_variable(Eterm node) {
 		while(arity--) {
 		    ESTACK_PUSH(s,*(++tuple));
 		}
-	    }
+            } else if (is_map(node)) {
+                Eterm *values = map_get_values(map_val(node));
+                int size = map_get_size(map_val(node));
+                ESTACK_PUSH(s, ((map_t *) map_val(node))->keys);
+                while (size--) {
+                    ESTACK_PUSH(s, *(values++));
+                }
+            }
 	    break;
 	case TAG_PRIMARY_IMMED1:
 	    if (node == am_Underscore || db_is_variable(node) >= 0) {
-- 
cgit v1.2.3


From 12408cf3657a88540d7afde88e68639c72212299 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 12 Mar 2015 09:55:04 +0100
Subject: erts: Strengthen maps match spec compilation tests

---
 erts/emulator/test/match_spec_SUITE.erl | 5 +++++
 1 file changed, 5 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index 02ef4f8e3f..b231c2bbd9 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -927,6 +927,11 @@ maps(Config) when is_list(Config) ->
     {ok,"camembert",[],[]} =
         erlang:match_spec_test(#{b => "camembert",c => "cabécou"},
                                [{#{b => '$1',c => "cabécou"},[],['$1']}], table),
+
+    {ok,#{a :="camembert",b := "hi"},[],[]} =
+        erlang:match_spec_test(#{<<"b">> =>"camembert","c"=>"cabécou", "wat"=>"hi", b=><<"other">>},
+                               [{#{<<"b">> => '$1',"wat" => '$2'},[],[#{a=>'$1',b=>'$2'}]}],
+                               table),
     ok.
 
 empty_list(Config) when is_list(Config) ->
-- 
cgit v1.2.3


From 0e464f7be1ae9b54d0fba748ab2dc7bd435ac118 Mon Sep 17 00:00:00 2001
From: Anthony Ramine 
Date: Fri, 9 May 2014 16:01:49 +0200
Subject: Create new BIF ets:update_counter/4

Conflicts:
	erts/emulator/beam/bif.tab
	lib/stdlib/src/ets.erl
---
 erts/emulator/beam/bif.tab           |   1 +
 erts/emulator/beam/erl_db.c          |  79 +++++++++++++-------
 erts/emulator/beam/erl_db_hash.c     | 141 ++++++++++++++++++++++++++---------
 erts/emulator/beam/erl_db_tree.c     |  58 +++++++++++---
 erts/emulator/beam/erl_db_util.c     |  10 +--
 erts/emulator/beam/erl_db_util.h     |  20 ++---
 erts/emulator/hipe/hipe_sparc_glue.S |   1 +
 7 files changed, 223 insertions(+), 87 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 1d0d214e77..788c866e63 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -613,6 +613,7 @@ bif erlang:fun_info_mfa/1
 #
 
 bif erlang:get_keys/0
+bif ets:update_counter/4
 
 #
 # Obsolete
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 4806befd99..fff892ae54 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -805,7 +805,7 @@ BIF_RETTYPE ets_update_element_3(BIF_ALIST_3)
 	list = BIF_ARG_3;
     }
 
-    if (!tb->common.meth->db_lookup_dbterm(tb, BIF_ARG_2, &handle)) {
+    if (!tb->common.meth->db_lookup_dbterm(BIF_P, tb, BIF_ARG_2, THE_NON_VALUE, &handle)) {
 	cret = DB_ERROR_BADKEY;
 	goto bail_out;
     }
@@ -844,7 +844,7 @@ BIF_RETTYPE ets_update_element_3(BIF_ALIST_3)
     }
 
 finalize:
-    tb->common.meth->db_finalize_dbterm(&handle);
+    tb->common.meth->db_finalize_dbterm(cret, &handle);
 
 bail_out:
     UnUseTmpHeap(2,BIF_P);
@@ -863,14 +863,8 @@ bail_out:
     }
 }
 
-/* 
-** update_counter(Tab, Key, Incr) 
-** update_counter(Tab, Key, {Upop}) 
-** update_counter(Tab, Key, [{Upop}]) 
-** Upop = {Pos,Incr} | {Pos,Incr,Threshold,WarpTo}
-** Returns new value(s) (integer or [integer])
-*/
-BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3)
+static BIF_RETTYPE
+do_update_counter(Process *p, Eterm arg1, Eterm arg2, Eterm arg3, Eterm arg4)
 {
     DbTable* tb;
     int cret = DB_ERROR_BADITEM;
@@ -880,7 +874,7 @@ BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3)
     Eterm* ret_list_currp = NULL;
     Eterm* ret_list_prevp = NULL;
     Eterm iter;
-    DeclareTmpHeap(cell,5,BIF_P);
+    DeclareTmpHeap(cell, 5, p);
     Eterm *tuple = cell+2;
     DbUpdateHandle handle;
     Uint halloc_size = 0; /* overestimated heap usage */
@@ -888,28 +882,29 @@ BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3)
     Eterm* hstart;
     Eterm* hend;
 
-    if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_WRITE, LCK_WRITE_REC)) == NULL) {
-	BIF_ERROR(BIF_P, BADARG);
+    if ((tb = db_get_table(p, arg1, DB_WRITE, LCK_WRITE_REC)) == NULL) {
+        BIF_ERROR(p, BADARG);
     }
 
-    UseTmpHeap(5,BIF_P);
+    UseTmpHeap(5, p);
 
     if (!(tb->common.status & (DB_SET | DB_ORDERED_SET))) {
 	goto bail_out;
     }
-    if (is_integer(BIF_ARG_3)) {  /* Incr */
-	upop_list = CONS(cell, TUPLE2(tuple, make_small(tb->common.keypos+1),
-				      BIF_ARG_3), NIL);
+    if (is_integer(arg3)) { /* Incr */
+        upop_list = CONS(cell,
+                         TUPLE2(tuple, make_small(tb->common.keypos+1), arg3),
+                         NIL);
     }
-    else if (is_tuple(BIF_ARG_3)) { /* {Upop} */
-	upop_list = CONS(cell, BIF_ARG_3, NIL);
+    else if (is_tuple(arg3)) { /* {Upop} */
+        upop_list = CONS(cell, arg3, NIL);
     }
     else { /* [{Upop}] (probably) */
-	upop_list = BIF_ARG_3;
+        upop_list = arg3;
 	ret_list_prevp = &ret;
     }
 
-    if (!tb->common.meth->db_lookup_dbterm(tb, BIF_ARG_2, &handle)) {
+    if (!tb->common.meth->db_lookup_dbterm(p, tb, arg2, arg4, &handle)) {
 	goto bail_out; /* key not found */
     }
 
@@ -982,13 +977,13 @@ BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3)
     if (ret_list_prevp) { /* Prepare to return a list */
 	ret = NIL;
 	halloc_size += list_size;
-	hstart = HAlloc(BIF_P, halloc_size);
+	hstart = HAlloc(p, halloc_size);
 	ret_list_currp = hstart;
 	htop = hstart + list_size;
 	hend = hstart + halloc_size;
     }
     else {
-	hstart = htop = HAlloc(BIF_P, halloc_size);
+	hstart = htop = HAlloc(p, halloc_size);
     }
     hend = hstart + halloc_size;
 
@@ -1035,26 +1030,54 @@ BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3)
 	   (is_list(ret) && (list_val(ret)+list_size)==ret_list_currp));
     ASSERT(htop <= hend);
 
-    HRelease(BIF_P,hend,htop);
+    HRelease(p, hend, htop);
 
 finalize:
-    tb->common.meth->db_finalize_dbterm(&handle);
+    tb->common.meth->db_finalize_dbterm(cret, &handle);
 
 bail_out:
-    UnUseTmpHeap(5,BIF_P);
+    UnUseTmpHeap(5, p);
     db_unlock(tb, LCK_WRITE_REC);
 
     switch (cret) {
     case DB_ERROR_NONE:
 	BIF_RET(ret);
     case DB_ERROR_SYSRES:
-	BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+        BIF_ERROR(p, SYSTEM_LIMIT);
     default:
-	BIF_ERROR(BIF_P, BADARG);
+        BIF_ERROR(p, BADARG);
 	break;
     }
 }
 
+/*
+** update_counter(Tab, Key, Incr)
+** update_counter(Tab, Key, Upop)
+** update_counter(Tab, Key, [{Upop}])
+** Upop = {Pos,Incr} | {Pos,Incr,Threshold,WarpTo}
+** Returns new value(s) (integer or [integer])
+*/
+BIF_RETTYPE ets_update_counter_3(BIF_ALIST_3)
+{
+    return do_update_counter(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, THE_NON_VALUE);
+}
+
+/*
+** update_counter(Tab, Key, Incr, Default)
+** update_counter(Tab, Key, Upop, Default)
+** update_counter(Tab, Key, [{Upop}], Default)
+** Upop = {Pos,Incr} | {Pos,Incr,Threshold,WarpTo}
+** Returns new value(s) (integer or [integer])
+*/
+BIF_RETTYPE ets_update_counter_4(BIF_ALIST_4)
+{
+    if (is_not_tuple(BIF_ARG_4)) {
+        BIF_ERROR(BIF_P, BADARG);
+    }
+    return do_update_counter(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, BIF_ARG_4);
+}
+
+
 /* 
 ** The put BIF 
 */
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index c2157457a0..8668a87ba1 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -444,8 +444,11 @@ static int db_delete_all_objects_hash(Process* p, DbTable* tbl);
 #ifdef HARDDEBUG
 static void db_check_table_hash(DbTableHash *tb);
 #endif
-static int db_lookup_dbterm_hash(DbTable *tbl, Eterm key, DbUpdateHandle* handle);
-static void db_finalize_dbterm_hash(DbUpdateHandle* handle);
+static int
+db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
+                      DbUpdateHandle* handle);
+static void
+db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle);
 
 static ERTS_INLINE void try_shrink(DbTableHash* tb)
 {
@@ -2796,59 +2799,129 @@ static HashDbTerm* next(DbTableHash *tb, Uint *iptr, erts_smp_rwmtx_t** lck_ptr,
     return NULL;
 }
 
-static int db_lookup_dbterm_hash(DbTable *tbl, Eterm key, DbUpdateHandle* handle)
+static int
+db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
+                      DbUpdateHandle* handle)
 {
     DbTableHash *tb = &tbl->hash;
-    HashDbTerm* b;
-    HashDbTerm** prevp;
-    int ix;
     HashValue hval;
+    HashDbTerm **bp, *b;
     erts_smp_rwmtx_t* lck;
+    int flags = 0;
+
+    ASSERT(tb->common.status & DB_SET);
 
     hval = MAKE_HASH(key);
-    lck = WLOCK_HASH(tb,hval);
-    ix = hash_to_ix(tb, hval);
-    prevp = &BUCKET(tb, ix);
-    b = *prevp;
+    lck = WLOCK_HASH(tb, hval);
+    bp = &BUCKET(tb, hash_to_ix(tb, hval));
+    b = *bp;
 
-    while (b != 0) {
-	if (has_live_key(tb,b,key,hval)) {
-	    handle->tb = tbl;
-	    handle->bp = (void**) prevp;
-	    handle->dbterm = &b->dbterm;
-	    handle->mustResize = 0;
-	    handle->new_size = b->dbterm.size;
-	#if HALFWORD_HEAP
-	    handle->abs_vec = NULL;
-	#endif
-	    handle->lck = lck;
-	    /* KEEP hval WLOCKED, db_finalize_dbterm_hash will WUNLOCK */
-	    return 1;
-	}
-	prevp = &b->next;
-	b = *prevp;
+    for (;;) {
+        if (b == NULL) {
+            break;
+        }
+        if (has_key(tb, b, key, hval)) {
+            if (b->hvalue != INVALID_HASH) {
+                goto Ldone;
+            }
+            break;
+        }
+        bp = &b->next;
+        b = *bp;
     }
-    WUNLOCK_HASH(lck);
-    return 0;
+
+    if (obj == THE_NON_VALUE) {
+        WUNLOCK_HASH(lck);
+        return 0;
+    }
+
+    {
+        Eterm *objp = tuple_val(obj);
+        int arity = arityval(*objp);
+        Eterm *htop, *hend;
+
+        ASSERT(arity >= tb->common.keypos);
+        htop = HAlloc(p, arity + 1);
+        hend = htop + arity + 1;
+        sys_memcpy(htop, objp, sizeof(Eterm) * (arity + 1));
+        htop[tb->common.keypos] = key;
+        obj = make_tuple(htop);
+
+        if (b == NULL) {
+            HashDbTerm *q = new_dbterm(tb, obj);
+
+            q->hvalue = hval;
+            q->next = NULL;
+            *bp = b = q;
+
+            {
+                int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems);
+                int nactive = NACTIVE(tb);
+
+                if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) {
+                    grow(tb, nactive);
+                }
+            }
+        } else {
+            HashDbTerm *q, *next = b->next;
+
+            ASSERT(b->hvalue == INVALID_HASH);
+            q = replace_dbterm(tb, b, obj);
+            q->next = next;
+            q->hvalue = hval;
+            *bp = b = q;
+            erts_smp_atomic_inc_nob(&tb->common.nitems);
+        }
+
+        HRelease(p, hend, htop);
+        flags |= DB_NEW_OBJECT;
+    }
+
+Ldone:
+    handle->tb = tbl;
+    handle->bp = (void **)bp;
+    handle->dbterm = &b->dbterm;
+    handle->flags = flags;
+    handle->new_size = b->dbterm.size;
+#if HALFWORD_HEAP
+    handle->abs_vec = NULL;
+#endif
+    handle->lck = lck;
+    return 1;
 }
 
 /* Must be called after call to db_lookup_dbterm
 */
-static void db_finalize_dbterm_hash(DbUpdateHandle* handle)
+static void
+db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
 {
     DbTable* tbl = handle->tb;
-    HashDbTerm* oldp = (HashDbTerm*) *(handle->bp);
+    DbTableHash *tb = &tbl->hash;
+    HashDbTerm **bp = (HashDbTerm **) handle->bp;
+    HashDbTerm *b = *bp;
     erts_smp_rwmtx_t* lck = (erts_smp_rwmtx_t*) handle->lck;
 
-    ERTS_SMP_LC_ASSERT(IS_HASH_WLOCKED(&tbl->hash,lck));  /* locked by db_lookup_dbterm_hash */
+    ERTS_SMP_LC_ASSERT(IS_HASH_WLOCKED(tb, lck));  /* locked by db_lookup_dbterm_hash */
+
+    ASSERT((&b->dbterm == handle->dbterm) == !(tb->common.compress && handle->flags & DB_MUST_RESIZE));
 
-    ASSERT((&oldp->dbterm == handle->dbterm) == !(tbl->common.compress && handle->mustResize));
+    if (handle->flags & DB_NEW_OBJECT && cret != DB_ERROR_NONE) {
+        if (IS_FIXED(tb)) {
+            add_fixed_deletion(tb, hash_to_ix(tb, b->hvalue));
+            b->hvalue = INVALID_HASH;
+        } else {
+            *bp = b->next;
+            free_term(tb, b);
+        }
 
-    if (handle->mustResize) {
+        WUNLOCK_HASH(lck);
+        erts_smp_atomic_dec_nob(&tb->common.nitems);
+        try_shrink(tb);
+    } else if (handle->flags & DB_MUST_RESIZE) {
 	db_finalize_resize(handle, offsetof(HashDbTerm,dbterm));
 	WUNLOCK_HASH(lck);
 
-	free_term(&tbl->hash, oldp);
+	free_term(tb, b);
     }
     else {
 	WUNLOCK_HASH(lck);
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index 720c0659c3..577da35b75 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -399,8 +399,11 @@ static int db_delete_all_objects_tree(Process* p, DbTable* tbl);
 #ifdef HARDDEBUG
 static void db_check_table_tree(DbTable *tbl);
 #endif
-static int db_lookup_dbterm_tree(DbTable *, Eterm key, DbUpdateHandle*);
-static void db_finalize_dbterm_tree(DbUpdateHandle*);
+static int
+db_lookup_dbterm_tree(Process *, DbTable *, Eterm key, Eterm obj,
+                      DbUpdateHandle*);
+static void
+db_finalize_dbterm_tree(int cret, DbUpdateHandle *);
 
 /*
 ** Static variables
@@ -2546,16 +2549,43 @@ static TreeDbTerm **find_node2(DbTableTree *tb, Eterm key)
     return this;
 }
 
-static int db_lookup_dbterm_tree(DbTable *tbl, Eterm key, DbUpdateHandle* handle)
+static int
+db_lookup_dbterm_tree(Process *p, DbTable *tbl, Eterm key, Eterm obj,
+                      DbUpdateHandle* handle)
 {
     DbTableTree *tb = &tbl->tree;
     TreeDbTerm **pp = find_node2(tb, key);
-
-    if (pp == NULL) return 0;
+    int flags = 0;
+
+    if (pp == NULL) {
+        if (obj == THE_NON_VALUE) {
+            return 0;
+        } else {
+            Eterm *objp = tuple_val(obj);
+            int arity = arityval(*objp);
+            Eterm *htop, *hend;
+
+            ASSERT(arity >= tb->common.keypos);
+            htop = HAlloc(p, arity + 1);
+            hend = htop + arity + 1;
+            sys_memcpy(htop, objp, sizeof(Eterm) * (arity + 1));
+            htop[tb->common.keypos] = key;
+            obj = make_tuple(htop);
+
+            if (db_put_tree(tbl, obj, 1) != DB_ERROR_NONE) {
+                return 0;
+            }
+
+            pp = find_node2(tb, key);
+            ASSERT(pp != NULL);
+            HRelease(p, hend, htop);
+            flags |= DB_NEW_OBJECT;
+        }
+    }
 
     handle->tb = tbl;
     handle->dbterm = &(*pp)->dbterm;
-    handle->mustResize = 0;
+    handle->flags = flags;
     handle->bp = (void**) pp;
     handle->new_size = (*pp)->dbterm.size;
 #if HALFWORD_HEAP
@@ -2564,15 +2594,21 @@ static int db_lookup_dbterm_tree(DbTable *tbl, Eterm key, DbUpdateHandle* handle
     return 1;
 }
 
-static void db_finalize_dbterm_tree(DbUpdateHandle* handle)
+static void
+db_finalize_dbterm_tree(int cret, DbUpdateHandle *handle)
 {
-    if (handle->mustResize) {
-	TreeDbTerm* oldp = (TreeDbTerm*) *handle->bp;
+    DbTable *tbl = handle->tb;
+    DbTableTree *tb = &tbl->tree;
+    TreeDbTerm *bp = (TreeDbTerm *) *handle->bp;
 
+    if (handle->flags & DB_NEW_OBJECT && cret != DB_ERROR_NONE) {
+        Eterm ret;
+        db_erase_tree(tbl, GETKEY(tb, bp->dbterm.tpl), &ret);
+    } else if (handle->flags & DB_MUST_RESIZE) {
 	db_finalize_resize(handle, offsetof(TreeDbTerm,dbterm));
-	reset_static_stack(&handle->tb->tree);
+        reset_static_stack(tb);
 
-	free_term(&handle->tb->tree, oldp);
+        free_term(tb, bp);
     }
 #ifdef DEBUG
     handle->dbterm = 0;
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 7eb80e3bb1..221f091396 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -2699,10 +2699,10 @@ Wterm db_do_read_element(DbUpdateHandle* handle, Sint position)
     }
 
     ASSERT(((DbTableCommon*)handle->tb)->compress);
-    ASSERT(!handle->mustResize);
+    ASSERT(!(handle->flags & DB_MUST_RESIZE));
     handle->dbterm = db_alloc_tmp_uncompressed(&handle->tb->common,
 					       handle->dbterm);
-    handle->mustResize = 1;
+    handle->flags |= DB_MUST_RESIZE;
     return handle->dbterm->tpl[position];
 }
 
@@ -2735,11 +2735,11 @@ void db_do_update_element(DbUpdateHandle* handle,
     #endif
 	return;
     }
-    if (!handle->mustResize) {
+    if (!(handle->flags & DB_MUST_RESIZE)) {
 	if (handle->tb->common.compress) {
 	    handle->dbterm = db_alloc_tmp_uncompressed(&handle->tb->common,
 						       handle->dbterm);
-	    handle->mustResize = 1;
+	    handle->flags |= DB_MUST_RESIZE;
 	    oldval = handle->dbterm->tpl[position];
         #if HALFWORD_HEAP
 	    old_base = NULL;
@@ -2799,7 +2799,7 @@ both_size_set:
 
     /* write new value in old dbterm, finalize will make a flat copy */
     handle->dbterm->tpl[position] = newval;
-    handle->mustResize = 1;
+    handle->flags |= DB_MUST_RESIZE;
 
 #if HALFWORD_HEAP
     if (old_base && newval_sz > 0) {
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 5ace93c8ed..ca206c7f58 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -76,6 +76,9 @@ typedef struct db_term {
 union db_table;
 typedef union db_table DbTable;
 
+#define DB_MUST_RESIZE 1
+#define DB_NEW_OBJECT 2
+
 /* Info about a database entry while it's being updated
  * (by update_counter or update_element)
  */
@@ -84,7 +87,7 @@ typedef struct {
     DbTerm* dbterm;
     void** bp;         /* {Hash|Tree}DbTerm** */
     Uint new_size;
-    int mustResize;
+    int flags;
     void* lck;
 #if HALFWORD_HEAP
     unsigned char* abs_vec;  /* [i] true if dbterm->tpl[i] is absolute Eterm */
@@ -183,15 +186,14 @@ typedef struct db_table_method
 			       void *arg);
     void (*db_check_table)(DbTable* tb);
 
-    /* Lookup a dbterm for updating. Return false if not found.
-    */
-    int (*db_lookup_dbterm)(DbTable*, Eterm key, 
-			    DbUpdateHandle* handle); /* [out] */
+    /* Lookup a dbterm for updating. Return false if not found. */
+    int (*db_lookup_dbterm)(Process *, DbTable *, Eterm key, Eterm obj,
+                            DbUpdateHandle* handle);
 
-    /* Must be called for each db_lookup_dbterm that returned true,
-    ** even if dbterm was not updated.
-    */
-    void (*db_finalize_dbterm)(DbUpdateHandle* handle);
+    /* Must be called for each db_lookup_dbterm that returned true, even if
+    ** dbterm was not updated. If the handle was of a new object and cret is
+    ** not DB_ERROR_NONE, the object is removed from the table. */
+    void (*db_finalize_dbterm)(int cret, DbUpdateHandle* handle);
 
 } DbTableMethod;
 
diff --git a/erts/emulator/hipe/hipe_sparc_glue.S b/erts/emulator/hipe/hipe_sparc_glue.S
index 90adad42ab..094a87fd58 100644
--- a/erts/emulator/hipe/hipe_sparc_glue.S
+++ b/erts/emulator/hipe/hipe_sparc_glue.S
@@ -316,6 +316,7 @@ nbif_fail:
 	.global	nbif_1_gc_after_bif
 	.global	nbif_2_gc_after_bif
 	.global	nbif_3_gc_after_bif
+	.global nbif_4_gc_after_bif
 nbif_0_gc_after_bif:
 	ba	.gc_after_bif
 	mov	0, %o1		/* delay slot */
-- 
cgit v1.2.3


From 10a1f3a2d5d9343bd4d2b8ec77c32ec18da21666 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Thu, 12 Mar 2015 13:42:15 +0100
Subject: erts: Add missing binary offheap overhead in binary_to_term

Binary offheap overhead is used to trigger GC when a process is
referring "too much" binary offheap data.

Offheap binaries created from external format (binary_to_term,
distributed messages or compacted ets tables) were not accounted for.

Example: A process receiving a lot of binary data in distributed messages,
while not building much terms on its heap, could cause an extensive
memory consumption for garbage binaries.
---
 erts/emulator/beam/external.c | 4 ++++
 1 file changed, 4 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 9b9b4b2a62..ae011a34bd 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -3396,6 +3396,7 @@ dec_term_atom_common:
 		    pb->size = n;
 		    pb->next = off_heap->first;
 		    off_heap->first = (struct erl_off_heap_header*)pb;
+                    OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm));
 		    pb->val = dbin;
 		    pb->bytes = (byte*) dbin->orig_bytes;
 		    pb->flags = 0;
@@ -3449,6 +3450,7 @@ dec_term_atom_common:
 		    pb->size = n;
 		    pb->next = off_heap->first;
 		    off_heap->first = (struct erl_off_heap_header*)pb;
+                    OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm));
 		    pb->val = dbin;
 		    pb->bytes = (byte*) dbin->orig_bytes;
 		    pb->flags = 0;
@@ -3747,6 +3749,7 @@ dec_term_atom_common:
 		hp += PROC_BIN_SIZE;
 		pb->next = off_heap->first;
 		off_heap->first = (struct erl_off_heap_header*)pb;
+                OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm));
 		pb->flags = 0;
 		*objp = make_binary(pb);
 		break;
@@ -3764,6 +3767,7 @@ dec_term_atom_common:
 		hp += PROC_BIN_SIZE;
 		pb->next = off_heap->first;
 		off_heap->first = (struct erl_off_heap_header*)pb;
+                OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm));
 		pb->flags = 0;
 
 		sub = (ErlSubBin*)hp;
-- 
cgit v1.2.3


From 8d3dba44bc2ac5ff9e724e90aa832854280b7d1e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Wed, 27 Jun 2012 17:03:54 +0200
Subject: Initial Persistent HAMT - Map framework

Conflicts:
	erts/emulator/Makefile.in
	erts/emulator/beam/bif.tab
	erts/emulator/beam/erl_gc.c
	erts/emulator/beam/erl_gc.h
	erts/emulator/beam/erl_printf_term.c
	erts/emulator/beam/erl_term.c
	erts/emulator/beam/erl_term.h
---
 erts/emulator/Makefile.in            |   3 +-
 erts/emulator/beam/beam_emu.c        |   1 +
 erts/emulator/beam/bif.tab           |  10 +
 erts/emulator/beam/copy.c            |  47 +++-
 erts/emulator/beam/erl_gc.c          |   1 +
 erts/emulator/beam/erl_gc.h          |   3 +-
 erts/emulator/beam/erl_hashmap.c     | 506 +++++++++++++++++++++++++++++++++++
 erts/emulator/beam/erl_hashmap.h     | 149 +++++++++++
 erts/emulator/beam/erl_printf_term.c |  76 +++++-
 erts/emulator/beam/erl_term.c        |   4 +-
 erts/emulator/beam/erl_term.h        |   9 +-
 erts/emulator/beam/erl_vm.h          |   4 +-
 erts/emulator/beam/global.h          |  10 +-
 erts/emulator/hipe/hipe_gc.c         |   1 +
 14 files changed, 807 insertions(+), 17 deletions(-)
 create mode 100644 erts/emulator/beam/erl_hashmap.c
 create mode 100644 erts/emulator/beam/erl_hashmap.h

(limited to 'erts')

diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index a632faf57d..db7eac4690 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -785,7 +785,8 @@ RUN_OBJS = \
 	$(OBJDIR)/erl_zlib.o		$(OBJDIR)/erl_nif.o \
 	$(OBJDIR)/erl_bif_binary.o      $(OBJDIR)/erl_ao_firstfit_alloc.o \
 	$(OBJDIR)/erl_thr_queue.o	$(OBJDIR)/erl_sched_spec_pre_alloc.o \
-	$(OBJDIR)/erl_ptab.o		$(OBJDIR)/erl_map.o
+	$(OBJDIR)/erl_ptab.o		$(OBJDIR)/erl_map.o \
+	$(OBJDIR)/erl_hashmap.o
 
 ifeq ($(TARGET),win32)
 DRV_OBJS = \
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index b89c8b3900..b734d34872 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -2801,6 +2801,7 @@ get_map_elements_fail:
 	}
 	PreFetch(1, next);
 	ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+	ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
 	reg[0] = r(0);
 	result = (*bf)(c_p, reg, I);
 	ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 1d0d214e77..a3fb21ee52 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -614,6 +614,16 @@ bif erlang:fun_info_mfa/1
 
 bif erlang:get_keys/0
 
+# Hash Array Mappped Trie
+bif hashmap:put/3
+bif hashmap:get/2
+#bif hashmap:info/1
+bif hashmap:to_list/1
+bif hashmap:new/0
+# bif hashmap:keys/1
+bif hashmap:size/1
+bif erlang:is_hashmap/1
+
 #
 # Obsolete
 #
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index 0010f6a440..6294ba9412 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -33,6 +33,7 @@
 #include "erl_binary.h"
 #include "erl_bits.h"
 #include "dtrace-wrapper.h"
+#include "erl_hashmap.h"
 
 static void move_one_frag(Eterm** hpp, Eterm* src, Uint src_sz, ErlOffHeap*);
 
@@ -127,6 +128,35 @@ Uint size_object(Eterm obj)
 			obj = *bptr;
 			break;
 		    }
+		case HASHMAP_SUBTAG:
+		    switch (MAP_HEADER_TYPE(hdr)) {
+			case MAP_HEADER_TAG_HAMT_HEAD_BITMAP :
+			case MAP_HEADER_TAG_HAMT_HEAD_ARRAY :
+			case MAP_HEADER_TAG_HAMT_NODE_BITMAP :
+			    {
+				Eterm *head;
+				Uint sz;
+				head  = hashmap_val_rel(obj, base);
+				sz    = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+				sum  += 1 + sz + header_arity(hdr);
+				head += 1 + header_arity(hdr);
+
+				if (sz == 0) {
+				    goto pop_next;
+				}
+				while(sz-- > 1) {
+				    obj = head[sz];
+				    if (!IS_CONST(obj)) {
+					ESTACK_PUSH(s, obj);
+				    }
+				}
+				obj = head[0];
+			    }
+			    break;
+			default:
+			    erl_exit(ERTS_ABORT_EXIT, "size_object: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr));
+		    }
+		    break;
 		case SUB_BINARY_SUBTAG:
 		    {
 			Eterm real_bin;
@@ -459,7 +489,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
 		{
 		  ExternalThing *etp = (ExternalThing *) htop;
 
-		  i =  thing_arityval(hdr) + 1;
+		  i  = thing_arityval(hdr) + 1;
 		  tp = htop;
 
 		  while (i--)  {
@@ -473,6 +503,21 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
 		  *argp = make_external_rel(tp, dst_base);
 		}
 		break;
+	    case HASHMAP_SUBTAG:
+		tp = htop;
+		switch (MAP_HEADER_TYPE(hdr)) {
+		    case MAP_HEADER_TAG_HAMT_HEAD_BITMAP :
+		    case MAP_HEADER_TAG_HAMT_HEAD_ARRAY :
+			*htop++ = *objp++;
+		    case MAP_HEADER_TAG_HAMT_NODE_BITMAP :
+			i = 1 + hashmap_bitcount(MAP_HEADER_VAL(hdr));
+			while (i--)  { *htop++ = *objp++; }
+			*argp = make_hashmap_rel(tp, dstbase);
+			break;
+		    default:
+			erl_exit(ERTS_ABORT_EXIT, "copy_struct: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr));
+		}
+		break;
 	    case BIN_MATCHSTATE_SUBTAG:
 		erl_exit(ERTS_ABORT_EXIT,
 			 "copy_struct: matchstate term not allowed");
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index d1a7ee113b..bdf7aa362e 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -31,6 +31,7 @@
 #include "erl_binary.h"
 #include "erl_bits.h"
 #include "erl_map.h"
+#include "erl_hashmap.h"
 #include "error.h"
 #include "big.h"
 #include "erl_gc.h"
diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h
index bf0496c112..3fec553684 100644
--- a/erts/emulator/beam/erl_gc.h
+++ b/erts/emulator/beam/erl_gc.h
@@ -56,6 +56,8 @@ do {                                                                    \
     switch ((HDR) & _HEADER_SUBTAG_MASK) {                              \
     case SUB_BINARY_SUBTAG: nelts++; break;                             \
     case MAP_SUBTAG: nelts+=map_get_size(PTR) + 1; break;               \
+    case HASHMAP_SUBTAG: nelts=hashmap_bitcount(MAP_HEADER_VAL(HDR));   \
+	 nelts += is_hashmap_header_head(HDR) ? 1 : 0; break;           \
     case FUN_SUBTAG: nelts+=((ErlFunThing*)(PTR))->num_free+1; break;   \
     }                                                                   \
     gval    = make_boxed(HTOP);                                         \
@@ -63,7 +65,6 @@ do {                                                                    \
     *HTOP++ = HDR;                                                      \
     *PTR++  = gval;                                                     \
     while (nelts--) *HTOP++ = *PTR++;                                   \
-                                                                        \
 } while(0)
 
 #define in_area(ptr,start,nbytes) \
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
new file mode 100644
index 0000000000..b7d5d2e2eb
--- /dev/null
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -0,0 +1,506 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 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%
+ *
+ * hashmaps are an adaption of Rich Hickeys Persistent HashMaps
+ *   which were an adaption of Phil Bagwells - Hash Array Mapped Tries
+ *
+ * Author: Björn-Egil Dahlberg
+ */
+/*
+ * Ls = lists:seq(1,188888).
+ * A = lists:foldl(fun(I,O) -> hashmap:put(I,I,O) end, hashmap:new(), Ls).
+ * lists:foreach(fun(I) -> io:format("looking up ~p got ~p~n", [I, hashmap:get(I, A)]), I = hashmap:get(I,A) end, Ls).
+ *
+ * lists:foldl(fun(I,O) -> hashmap:put(I,I,O) end, hashmap:new(), lists:seq(1,7)).
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#include "erl_process.h"
+#include "error.h"
+#include "bif.h"
+
+#include "erl_hashmap.h"
+
+#ifndef DECL_AM
+#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
+#endif
+
+static char *format_binary(Uint64 x, char *b) {
+    int z;
+    b[64] = '\0';
+    for (z = 0; z < 64; z++) { 
+	b[63-z] = ((x>>z) & 0x1) ? '1' : '0'; 
+    }
+    return b;
+}
+
+static Uint32 hashmap_shift_hash(Uint32 hx, Uint *lvl, Eterm key);
+static Uint32 hashmap_restore_hash(Uint lvl, Eterm key);
+static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node);
+static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
+static Eterm hashmap_to_list(Process *p, Eterm map);
+
+/* hashmap:new/0 */
+
+BIF_RETTYPE hashmap_new_0(BIF_ALIST_0) {
+    Eterm* hp;
+    hashmap_head_t *head;
+
+    hp   = HAlloc(BIF_P, HAMT_HEAD_EMPTY_SZ);
+    head = (hashmap_head_t *) hp;
+
+    head->thing_word = MAP_HEADER_HAMT_HEAD_BITMAP(0);
+    head->size       = 0;
+
+    BIF_RET(make_hashmap(head));
+}
+
+/* hashmap:put/3 */
+
+BIF_RETTYPE hashmap_put_3(BIF_ALIST_3) {
+    if (is_hashmap(BIF_ARG_3)) {
+	Uint32 hx = make_hash2(BIF_ARG_1);
+	Eterm map;
+
+	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+	ASSERT(is_hashmap(map));
+	BIF_RET(map);
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
+/* hashmap:to_list/1 */
+
+BIF_RETTYPE hashmap_to_list_1(BIF_ALIST_1) {
+    if (is_hashmap(BIF_ARG_1)) {
+	return hashmap_to_list(BIF_P, BIF_ARG_1);
+    }
+
+    BIF_ERROR(BIF_P, BADARG);
+}
+
+/* hashmap:get/2 */
+
+BIF_RETTYPE hashmap_get_2(BIF_ALIST_2) {
+    if (is_hashmap(BIF_ARG_2)) {
+	const Eterm *value;
+	Uint32 hx = make_hash2(BIF_ARG_1);
+	
+	if ((value = hashmap_get(hx, BIF_ARG_1, BIF_ARG_2)) != NULL) {
+	    BIF_RET(*value);
+	}
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
+/* hashmap:size/1 */
+
+BIF_RETTYPE hashmap_size_1(BIF_ALIST_1) {
+    if (is_hashmap(BIF_ARG_1)) {
+	Eterm *head, *hp, res;
+	Uint size, hsz=0;
+
+	head = hashmap_val(BIF_ARG_1);
+	size = head[1];
+	(void) erts_bld_uint(NULL, &hsz, size);
+	hp = HAlloc(BIF_P, hsz);
+	res = erts_bld_uint(&hp, NULL, size);
+	BIF_RET(res);
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
+/* erlang:is_hashmap/1 */
+
+BIF_RETTYPE is_hashmap_1(BIF_ALIST_1) {
+    if (is_hashmap(BIF_ARG_1)) {
+	BIF_RET(am_true);
+    }
+    BIF_RET(am_false);
+}
+
+/* impl. */
+
+static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
+    Eterm *ptr, hdr;
+    Uint ix,slot, lvl = 0;
+    Uint32 hval,bp;
+
+    for (;;) {
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */
+		ptr = list_val(node);
+		if (EQ(CAR(ptr), key)) {
+		    return &(CDR(ptr));
+		}
+		return NULL;
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			ix   = hashmap_index(hx);
+			hx   = hashmap_shift_hash(hx,&lvl,key);
+			node = ptr[ix+1];
+			break;
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			ix   = hashmap_index(hx);
+			hx   = hashmap_shift_hash(hx,&lvl,key);
+			node = ptr[ix+2];
+			break;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    node  = ptr[slot+1];
+			    break;
+			}
+			/* not occupied */
+			return NULL;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    node  = ptr[slot+2];
+			    break;
+			}
+			/* not occupied */
+			return NULL;
+		    default:
+			erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
+			break;
+		}
+		break;
+	    default:
+		erl_exit(1, "bad primary tag %p\r\n", node);
+		break;
+	}
+    }
+    return NULL;
+}
+
+static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node) {
+    Eterm *hp = NULL, *nhp = NULL;
+    Eterm *ptr;
+    Eterm hdr,res,ckey,fake;
+    Uint32 ix, cix, bp, hval, chx;
+    Uint slot, lvl = 0, clvl;
+    Uint size = 0, n = 0, update_size = 1;
+    DECLARE_ESTACK(stack);
+
+    for (;;) {
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */
+		ptr  = list_val(node);
+		ckey = CAR(ptr);
+		if (EQ(ckey, key)) {
+		    update_size = 0;
+		    goto unroll;
+		}
+		goto insert_subnodes;
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			ix    = hashmap_index(hx);
+			hx    = hashmap_shift_hash(hx,&lvl,key);
+			size += HAMT_NODE_ARRAY_SZ;
+			ESTACK_PUSH2(stack, ix, node);
+			node  = ptr[ix+1];
+			break;
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			ix    = hashmap_index(hx);
+			hx    = hashmap_shift_hash(hx,&lvl,key);
+			size += HAMT_HEAD_ARRAY_SZ;
+			ESTACK_PUSH2(stack, ix, node);
+			node  = ptr[ix+2];
+			break;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+			n    = hashmap_bitcount(hval);
+
+			ESTACK_PUSH(stack, n);
+			ESTACK_PUSH3(stack, bp, slot, node);
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    node  = ptr[slot+1];
+			    ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17); 
+			    size += HAMT_NODE_BITMAP_SZ(n);
+			    break;
+			}
+			/* not occupied */
+			size += HAMT_NODE_BITMAP_SZ(n+1);
+			goto unroll;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+			n    = hashmap_bitcount(hval);
+
+			ESTACK_PUSH(stack, n);
+			ESTACK_PUSH3(stack, bp, slot, node);
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    node  = ptr[slot+2];
+			    ASSERT(HAMT_HEAD_BITMAP_SZ(n) <= 18); 
+			    size += HAMT_HEAD_BITMAP_SZ(n);
+			    break;
+			}
+			/* not occupied */
+			size += HAMT_HEAD_BITMAP_SZ(n+1);
+			goto unroll;
+		    default:
+			erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
+			break;
+		}
+		break;
+	    default:
+		erl_exit(1, "bad primary tag %p\r\n", node);
+		break;
+	}
+    }
+insert_subnodes:
+    clvl  = lvl;
+    chx   = hashmap_restore_hash(clvl,ckey);
+    size += HAMT_NODE_BITMAP_SZ(2);
+    ix    = hashmap_index(hx);
+    cix   = hashmap_index(chx);
+
+    while (cix == ix) {
+	ESTACK_PUSH(stack, 0);
+	ESTACK_PUSH3(stack, 1 << ix, 0, MAP_HEADER_HAMT_NODE_BITMAP(0));
+	size += HAMT_NODE_BITMAP_SZ(1);
+	hx    = hashmap_shift_hash(hx,&lvl,key);
+	chx   = hashmap_shift_hash(chx,&clvl,ckey);
+	ix    = hashmap_index(hx);
+	cix   = hashmap_index(chx);
+    }
+    ESTACK_PUSH3(stack, cix, ix, node);
+
+unroll:
+    size += 2;
+    hp  = HAlloc(p, size);
+    res = CONS(hp, key, value); hp += 2;
+
+    do {
+	node = ESTACK_POP(stack);
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST:
+		ix   = (Uint32) ESTACK_POP(stack);
+		cix  = (Uint32) ESTACK_POP(stack);
+
+		nhp   = hp;
+		*hp++ = MAP_HEADER_HAMT_NODE_BITMAP((1 << ix) | (1 << cix));
+		if (ix < cix) {
+		    *hp++ = res;
+		    *hp++ = node;
+		} else {
+		    *hp++ = node;
+		    *hp++ = res;
+		}
+		res = make_hashmap(nhp);
+		break;
+	    case TAG_PRIMARY_HEADER:
+		/* subnodes, fake it */
+		fake = node;
+		node = make_boxed(&fake);
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			slot  = (Uint)   ESTACK_POP(stack);
+			nhp   = hp;
+			n     = HAMT_NODE_ARRAY_SZ;
+			while(n--) { *hp++ = *ptr++; }
+			nhp[slot+1] = res;
+			res = make_hashmap(nhp);
+			break;
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			slot  = (Uint)   ESTACK_POP(stack);
+			nhp   = hp;
+			n     = HAMT_HEAD_ARRAY_SZ - 2;
+			*hp++ = MAP_HEADER_HAMT_HEAD_ARRAY; ptr++;
+			*hp++ = (*ptr++) + update_size;
+			while(n--) { *hp++ = *ptr++; }
+			nhp[slot+2] = res;
+			res = make_hashmap(nhp);
+			break;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			slot  = (Uint)   ESTACK_POP(stack);
+			bp    = (Uint32) ESTACK_POP(stack);
+			n     = (Uint32) ESTACK_POP(stack);
+			hval  = MAP_HEADER_VAL(hdr);
+			nhp   = hp;
+			*hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval | bp); ptr++;
+
+			n -= slot;
+			while(slot--) { *hp++ = *ptr++; }
+			*hp++ = res;
+			if (hval & bp) { ptr++; n--; }
+			while(n--) { *hp++ = *ptr++; }
+
+			if ((hval | bp) == 0xffff) {
+			    *nhp = make_arityval(16);
+			}
+			res = make_hashmap(nhp);
+			break;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			slot  = (Uint)   ESTACK_POP(stack);
+			bp    = (Uint32) ESTACK_POP(stack);
+			n     = (Uint32) ESTACK_POP(stack);
+			hval  = MAP_HEADER_VAL(hdr);
+			nhp   = hp;
+			*hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(hval | bp); ptr++;
+			*hp++ = (*ptr++) + update_size;
+
+			n -= slot;
+			while(slot--) { *hp++ = *ptr++; }
+			*hp++ = res;
+			if (hval & bp) { ptr++; n--; }
+			while(n--) { *hp++ = *ptr++; }
+
+			if ((hval | bp) == 0xffff) {
+			    *nhp = MAP_HEADER_HAMT_HEAD_ARRAY;
+			}
+			res = make_hashmap(nhp);
+			break;
+		    default:
+			erl_exit(1, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
+			break;
+		}
+		break;
+	    default:
+		erl_exit(1, "bad primary tag %x\r\n", primary_tag(node));
+		break;
+	}
+
+    } while(!ESTACK_ISEMPTY(stack));
+
+    DESTROY_ESTACK(stack);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+    ERTS_HOLE_CHECK(p);
+    return res;
+}
+
+static Eterm hashmap_to_list(Process *p, Eterm node) {
+    Eterm *hp;
+    Eterm res = NIL;
+    Eterm *ptr, tup, hdr;
+    Uint sz, n;
+    DECLARE_ESTACK(stack);
+
+    ptr = boxed_val(node);
+    n   = (Uint)ptr[1];
+    hp  = HAlloc(p, n * (2 + 3));
+    ESTACK_PUSH(stack, node);
+    do {
+	node = ESTACK_POP(stack);
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST:
+		ptr = list_val(node);
+		tup = TUPLE2(hp, CAR(ptr), CDR(ptr)); hp += 3;
+		res = CONS(hp, tup, res); hp += 2;
+		break;
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			ptr++;
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			ptr++;
+			sz = 16;
+			while(sz--) { ESTACK_PUSH(stack, ptr[sz]); }
+			break;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			ptr++;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+			ASSERT(sz < 17);
+			ptr++;
+			while(sz--) { ESTACK_PUSH(stack, ptr[sz]); }
+			break;
+		    default:
+			erl_exit(1, "bad header\r\n");
+			break;
+		}
+	}
+    } while(!ESTACK_ISEMPTY(stack));
+
+    DESTROY_ESTACK(stack);
+    ERTS_HOLE_CHECK(p);
+    return res;
+}
+
+static Uint32 hashmap_restore_hash(Uint lvl, Eterm key) {
+    Eterm heap[2], ret;
+
+    if (lvl < 8) {
+	return make_hash2(key) >> (4*lvl);
+    }
+    ret = CONS(heap, make_small(lvl), key);
+
+    return make_hash2(ret) >> (4*(lvl % 8));
+}
+
+static Uint32 hashmap_shift_hash(Uint32 hx, Uint *lvl, Eterm key) {
+    Eterm heap[2], ret;
+
+    /* rehash with [ lvl | key ] */
+    *lvl = *lvl + 1;
+    if (*lvl % 8) {
+	return hx >> 4;
+    }
+
+    ret = CONS(heap, make_small((*lvl)), key);
+    return make_hash2(ret);
+}
diff --git a/erts/emulator/beam/erl_hashmap.h b/erts/emulator/beam/erl_hashmap.h
new file mode 100644
index 0000000000..03739fa1f9
--- /dev/null
+++ b/erts/emulator/beam/erl_hashmap.h
@@ -0,0 +1,149 @@
+/*
+ * %CopyrightBegin%
+ * 
+ * Copyright Ericsson AB 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_HASH_H__
+#define __ERL_HASH_H__
+
+#include "sys.h"
+
+Eterm erts_hashmap_get(Eterm key, Eterm map);
+
+/* erl_term.h stuff */
+#define make_hashmap(x)		make_boxed((Eterm*)(x))
+#define make_hashmap_rel 	make_boxed_rel
+#define is_hashmap(x)		(is_boxed((x)) && is_hashmap_header(*boxed_val((x))))
+#define is_hashmap_header(x)	(((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_HASHMAP)
+#define hashmap_val(x)		_unchecked_boxed_val((x))
+#define hashmap_val_rel(RTERM, BASE) hashmap_val(rterm2wterm(RTERM, BASE))
+
+/* HASH */
+
+
+#if defined(__GNUC__)
+#define hashmap_bitcount(x) 	(Uint32) __builtin_popcount((unsigned int) (x))
+#else
+const Uint32 SK5 = 0x55555555, SK3 = 0x33333333;
+const Uint32 SKF0 = 0xF0F0F0F, SKFF = 0xFF00FF;
+
+/* CTPOP emulation */
+Uint32 hashmap_bitcount(Uint32 map) {
+    map -= (( map >> 1  ) & SK5 );
+    map  = ( map & SK3  ) + (( map >> 2 ) & SK3 );
+    map  = ( map & SKF0 ) + (( map >> 4 ) & SKF0);
+    map +=   map >> 8;
+    return ( map + ( map >> 16)) & 0x3F;
+}
+#endif
+
+/* hamt nodes v2.0
+ * 
+ * node :: leaf | array | bitmap
+ * head
+ */
+
+/* the head-node is a bitmap or array with an untagged size
+ */
+typedef struct hashmap_head_s {
+    Eterm thing_word;
+    Uint size;
+    Eterm items[1];
+} hashmap_head_t;
+ 
+/* the bitmap-node
+ * typedef struct hashmap_bitmap_node_s {
+ *     Eterm thing_word;
+ *     Eterm items[1];
+ * } hashmap_bitmap_node_t;
+ *
+ * the array-node is a tuple
+ * typedef struct hashmap_bitmap_node_s {
+ *     Eterm thing_word; 
+ *     Eterm items[1];
+ * } hashmap_bitmap_node_t;
+ *
+ * the leaf-node
+ * cons-cell
+ */
+
+/* thing_word tagscheme
+ * Need two bits for map subtags
+ *
+ * Original HEADER representation:
+ *
+ *     aaaaaaaaaaaaaaaa aaaaaaaaaatttt00       arity:26, tag:4
+ *
+ * For maps we have:
+ *
+ *     vvvvvvvvvvvvvvvv aaaaaaaamm111100       val:16, arity:8, mtype:2
+ *
+ * unsure about trailing zeros
+ *
+ * map-tag:
+ *     00 - flat map tag (non-hamt) -> val:16 = #items
+ *     01 - map-node bitmap tag     -> val:16 = bitmap
+ *     10 - map-head (array-node)   -> val:16 = 0xffff
+ *     11 - map-head (bitmap-node)  -> val:16 = bitmap
+ */
+
+/* erl_map.h stuff */
+
+#define MAP_HEADER_TAG_SZ                 (2)
+#define MAP_HEADER_ARITY_SZ               (8)
+#define MAP_HEADER_VAL_SZ                 (16)
+
+#define MAP_HEADER_TAG_FLAT               (0x0)
+#define MAP_HEADER_TAG_HAMT_NODE_BITMAP   (0x1)
+#define MAP_HEADER_TAG_HAMT_HEAD_ARRAY    (0x2)
+#define MAP_HEADER_TAG_HAMT_HEAD_BITMAP   (0x3)
+
+#define MAP_HEADER_TYPE(Hdr)  (((Hdr) >> (_HEADER_ARITY_OFFS)) & (0x3))
+#define MAP_HEADER_ARITY(Hdr) (((Hdr) >> (_HEADER_ARITY_OFFS + MAP_HEADER_TAG_SZ)) & (0xff))
+#define MAP_HEADER_VAL(Hdr)   (((Hdr) >> (_HEADER_ARITY_OFFS + MAP_HEADER_TAG_SZ + MAP_HEADER_ARITY_SZ)) & (0xffff))
+
+#define is_hashmap_header_head(x) ((MAP_HEADER_TYPE(x) & (0x2)))
+
+#define MAKE_MAP_HEADER(Type,Arity,Val) \
+    (_make_header(((((Uint16)Val) << MAP_HEADER_ARITY_SZ) | (Arity)) << MAP_HEADER_TAG_SZ | (Type) , _TAG_HEADER_HASHMAP))
+
+#define MAP_HEADER_HAMT_HEAD_ARRAY \
+    MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_HEAD_ARRAY,0x1,0xffff)
+
+#define MAP_HEADER_HAMT_HEAD_BITMAP(Bmp) \
+    MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_HEAD_BITMAP,0x1,Bmp)
+
+#define MAP_HEADER_HAMT_NODE_BITMAP(Bmp) \
+    MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_NODE_BITMAP,0x0,Bmp)
+
+#define HAMT_HEAD_EMPTY_SZ     (2)
+#define HAMT_NODE_ARRAY_SZ     (17)
+#define HAMT_HEAD_ARRAY_SZ     (18)
+#define HAMT_NODE_BITMAP_SZ(n) (1 + n)
+#define HAMT_HEAD_BITMAP_SZ(n) (2 + n)
+
+#define _HEADER_MAP_SUBTAG_MASK    (0xfc) /* 2 bits maps tag + 4 bits subtag + 2 ignore bits */
+/* SUBTAG_NODE_ARRAY is in fact a tuple with 16 elements */
+#define HAMT_SUBTAG_NODE_ARRAY   (((16 << _HEADER_ARITY_OFFS) | ARITYVAL_SUBTAG) & _HEADER_MAP_SUBTAG_MASK)
+#define HAMT_SUBTAG_NODE_BITMAP  ((MAP_HEADER_TAG_HAMT_NODE_BITMAP << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG)
+#define HAMT_SUBTAG_HEAD_ARRAY   ((MAP_HEADER_TAG_HAMT_HEAD_ARRAY << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG)
+#define HAMT_SUBTAG_HEAD_BITMAP  ((MAP_HEADER_TAG_HAMT_HEAD_BITMAP << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG)
+
+#define hashmap_index(hash)      (((Uint32)hash) & 0xf)
+
+#endif
diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c
index c982dc2080..f07fe30cf6 100644
--- a/erts/emulator/beam/erl_printf_term.c
+++ b/erts/emulator/beam/erl_printf_term.c
@@ -26,12 +26,13 @@
 #include "big.h"
 #include "erl_map.h"
 #include "erl_binary.h"
+#include "erl_hashmap.h"
 
 #define PRINT_CHAR(CNT, FN, ARG, C)					\
 do {									\
     int res__ = erts_printf_char((FN), (ARG), (C));			\
     if (res__ < 0)							\
-	return res__;							\
+	abort();							\
     (CNT) += res__;							\
 } while (0)
 
@@ -39,7 +40,7 @@ do {									\
 do {									\
     int res__ = erts_printf_string((FN), (ARG), (STR));			\
     if (res__ < 0)							\
-	return res__;							\
+	abort();							\
     (CNT) += res__;							\
 } while (0)
 
@@ -47,7 +48,7 @@ do {									\
 do {									\
     int res__ = erts_printf_buf((FN), (ARG), (char*)(BUF), (LEN));	\
     if (res__ < 0)							\
-	return res__;							\
+	abort();							\
     (CNT) += res__;							\
 } while (0)
 
@@ -55,7 +56,7 @@ do {									\
 do {									\
     int res__ = erts_printf_pointer((FN), (ARG), (void *) (PTR));	\
     if (res__ < 0)							\
-	return res__;							\
+	abort();							\
     (CNT) += res__;							\
 } while (0)
 
@@ -63,7 +64,7 @@ do {									\
 do {									\
     int res__ = erts_printf_uword((FN), (ARG), (C), (P), (W), (I));	\
     if (res__ < 0)							\
-	return res__;							\
+	abort();							\
     (CNT) += res__;							\
 } while (0)
 
@@ -71,7 +72,7 @@ do {									\
 do {									\
     int res__ = erts_printf_sword((FN), (ARG), (C), (P), (W), (I));	\
     if (res__ < 0)							\
-	return res__;							\
+	abort();							\
     (CNT) += res__;							\
 } while (0)
 
@@ -79,7 +80,7 @@ do {									\
 do {									\
     int res__ = erts_printf_double((FN), (ARG), (C), (P), (W), (I));	\
     if (res__ < 0)							\
-	return res__;							\
+	abort();							\
     (CNT) += res__;							\
 } while (0)
 
@@ -247,6 +248,17 @@ static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount)
 #define PRT_PATCH_FUN_SIZE     ((Eterm) 7)
 #define PRT_LAST_ARRAY_ELEMENT ((Eterm) 8) /* Note! Must be last... */
 
+#if 0
+static char *format_binary(Uint16 x, char *b) {
+    int z;
+    b[16] = '\0';
+    for (z = 0; z < 16; z++) { 
+	b[15-z] = ((x>>z) & 0x1) ? '1' : '0'; 
+    }
+    return b;
+}
+#endif
+
 static int
 print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
 	   Eterm* obj_base) /* ignored if !HALFWORD_HEAP */
@@ -575,6 +587,54 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
 		}
 	    }
 	    break;
+	case HASHMAP_DEF:
+	    {
+		Uint n,mapval;
+		Eterm *head;
+		head = hashmap_val(wobj);
+		mapval = MAP_HEADER_VAL(*head);
+		switch (MAP_HEADER_TYPE(*head)) {
+		    case MAP_HEADER_TAG_HAMT_HEAD_BITMAP:
+			PRINT_STRING(res, fn, arg, "#<");
+			PRINT_UWORD(res, fn, arg, 'x', 0, 1, mapval);
+			PRINT_STRING(res, fn, arg, ">{");
+			WSTACK_PUSH(s,PRT_CLOSE_TUPLE);
+			n = hashmap_bitcount(mapval);
+			ASSERT(n < 17);
+			head += 2;
+			if (n > 0) {
+			    n--;
+			    WSTACK_PUSH(s, head[n]);
+			    WSTACK_PUSH(s, PRT_TERM);
+			    while (n--) {
+				WSTACK_PUSH(s, PRT_COMMA);
+				WSTACK_PUSH(s, head[n]);
+				WSTACK_PUSH(s, PRT_TERM);
+			    }
+			}
+			break;
+		    case MAP_HEADER_TAG_HAMT_NODE_BITMAP:
+			n = hashmap_bitcount(mapval);
+			head++;
+			PRINT_CHAR(res, fn, arg, '<');
+			PRINT_UWORD(res, fn, arg, 'x', 0, 1, mapval);
+			PRINT_STRING(res, fn, arg, ">{");
+			WSTACK_PUSH(s,PRT_CLOSE_TUPLE);
+			ASSERT(n < 17);
+			if (n > 0) {
+			    n--;
+			    WSTACK_PUSH(s, head[n]);
+			    WSTACK_PUSH(s, PRT_TERM);
+			    while (n--) {
+				WSTACK_PUSH(s, PRT_COMMA);
+				WSTACK_PUSH(s, head[n]);
+				WSTACK_PUSH(s, PRT_TERM);
+			    }
+			}
+			break;
+		}
+	    }
+	    break;
 	default:
 	    PRINT_STRING(res, fn, arg, "> _TAG_PRIMARY_SIZE):	return EXTERNAL_PID_DEF;
 	    case (_TAG_HEADER_EXTERNAL_PORT >> _TAG_PRIMARY_SIZE):	return EXTERNAL_PORT_DEF;
 	    case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE):	return EXTERNAL_REF_DEF;
+	    case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE):	return MAP_DEF;
 	    case (_TAG_HEADER_REFC_BIN >> _TAG_PRIMARY_SIZE):	return BINARY_DEF;
 	    case (_TAG_HEADER_HEAP_BIN >> _TAG_PRIMARY_SIZE):	return BINARY_DEF;
 	    case (_TAG_HEADER_SUB_BIN >> _TAG_PRIMARY_SIZE):	return BINARY_DEF;
-	    case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE):	return MAP_DEF;
+	    case (_TAG_HEADER_HASHMAP >> _TAG_PRIMARY_SIZE):	return HASHMAP_DEF;
 	  }
+ 
 	  break;
       }
       case TAG_PRIMARY_IMMED1: {
diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index 37014ccf94..fde90997e3 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -21,6 +21,7 @@
 #define __ERL_TERM_H
 
 #include "sys.h" /* defines HALFWORD_HEAP */
+#include "erl_hashmap.h"
 
 typedef UWord Wterm;  /* Full word terms */
 
@@ -141,6 +142,7 @@ struct erl_node_; /* Declared in erl_node_tables.h */
 #define HEAP_BINARY_SUBTAG	(0x9 << _TAG_PRIMARY_SIZE) /* BINARY */
 #define SUB_BINARY_SUBTAG	(0xA << _TAG_PRIMARY_SIZE) /* BINARY */
 /*   _BINARY_XXX_MASK depends on 0xB being unused */
+#define HASHMAP_SUBTAG		(0xB << _TAG_PRIMARY_SIZE) /* HASHMAP */
 #define EXTERNAL_PID_SUBTAG	(0xC << _TAG_PRIMARY_SIZE) /* EXTERNAL_PID */
 #define EXTERNAL_PORT_SUBTAG	(0xD << _TAG_PRIMARY_SIZE) /* EXTERNAL_PORT */
 #define EXTERNAL_REF_SUBTAG	(0xE << _TAG_PRIMARY_SIZE) /* EXTERNAL_REF */
@@ -162,6 +164,7 @@ struct erl_node_; /* Declared in erl_node_tables.h */
 #define _TAG_HEADER_EXTERNAL_REF  (TAG_PRIMARY_HEADER|EXTERNAL_REF_SUBTAG)
 #define _TAG_HEADER_BIN_MATCHSTATE (TAG_PRIMARY_HEADER|BIN_MATCHSTATE_SUBTAG)
 #define _TAG_HEADER_MAP	 	(TAG_PRIMARY_HEADER|MAP_SUBTAG)
+#define _TAG_HEADER_HASHMAP	(TAG_PRIMARY_HEADER|HASHMAP_SUBTAG)
 
 
 #define _TAG_HEADER_MASK	0x3F
@@ -298,7 +301,9 @@ _ET_DECLARE_CHECKED(Uint,atom_val,Eterm)
 /* header (arityval or thing) access methods */
 #define _make_header(sz,tag)  ((Uint)(((sz) << _HEADER_ARITY_OFFS) + (tag)))
 #define is_header(x)	(((x) & _TAG_PRIMARY_MASK) == TAG_PRIMARY_HEADER)
-#define _unchecked_header_arity(x)	((x) >> _HEADER_ARITY_OFFS)
+//#define _unchecked_header_arity(x)	((x) >> _HEADER_ARITY_OFFS)
+#define _unchecked_header_arity(x) \
+    (is_hashmap_header(x) ? MAP_HEADER_ARITY(x) : ((x) >> _HEADER_ARITY_OFFS))
 _ET_DECLARE_CHECKED(Uint,header_arity,Eterm)
 #define header_arity(x)	_ET_APPLY(header_arity,(x))
 
@@ -361,6 +366,7 @@ _ET_DECLARE_CHECKED(Uint,thing_subtag,Eterm)
 	((((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_REFC_BIN) || \
 	 (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_HEAP_BIN) || \
 	 (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_SUB_BIN))
+
 #define make_binary(x)	make_boxed((Eterm*)(x))
 #define is_binary(x)	(is_boxed((x)) && is_binary_header(*boxed_val((x))))
 #define is_not_binary(x) (!is_binary((x)))
@@ -1095,6 +1101,7 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint)
 #define FLOAT_DEF		0xe
 #define BIG_DEF			0xf
 #define SMALL_DEF		0x10
+#define HASHMAP_DEF		0x11
 
 #if ET_DEBUG
 extern unsigned tag_val_def_debug(Wterm, const char*, unsigned);
diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h
index 6e9216bef3..3a9fb1e07b 100644
--- a/erts/emulator/beam/erl_vm.h
+++ b/erts/emulator/beam/erl_vm.h
@@ -117,9 +117,9 @@
 
 #if defined(DEBUG) || defined(CHECK_FOR_HOLES)
 #if HALFWORD_HEAP
-# define ERTS_HOLE_MARKER (0xaf5e78ccU)
+# define ERTS_HOLE_MARKER (0xdeadbeef)
 #else
-# define ERTS_HOLE_MARKER (((0xaf5e78ccUL << 24) << 8) | 0xaf5e78ccUL)
+# define ERTS_HOLE_MARKER (((0xdeadbeef << 24) << 8) | 0xdeadbeef)
 #endif
 #endif
 
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 5330f389e0..1fb069232a 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -539,6 +539,12 @@ do {							\
     }							\
 } while(0)
 
+#define WSTACK_DEBUG(s) \
+    do { \
+	fprintf(stderr, "wstack size   = %ld\r\n", s.wsp - s.wstart); \
+	fprintf(stderr, "wstack wstart = %p\r\n", s.wstart); \
+	fprintf(stderr, "wstack wsp    = %p\r\n", s.wsp); \
+    } while(0)
 
 /*
  * Do not free the stack after this, it may have pointers into what
@@ -581,7 +587,7 @@ do {						\
     ASSERT(s.wsp <= s.wend);			\
 } while (0)
 
-#define WSTACK_IS_STATIC(s) (s.wstart == WSTK_DEF_STACK(s)))
+#define WSTACK_IS_STATIC(s) (s.wstart == WSTK_DEF_STACK(s))
 
 #define WSTACK_PUSH(s, x)				\
 do {							\
@@ -648,7 +654,7 @@ do {						\
 
 #define WSTACK_COUNT(s) (s.wsp - s.wstart)
 #define WSTACK_ISEMPTY(s) (s.wsp == s.wstart)
-#define WSTACK_POP(s) (*(--s.wsp))
+#define WSTACK_POP(s) ((ASSERT(s.wsp > s.wstart)),*(--s.wsp))
 
 
 /* binary.c */
diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c
index b10263f6e2..398cbcdf14 100644
--- a/erts/emulator/hipe/hipe_gc.c
+++ b/erts/emulator/hipe/hipe_gc.c
@@ -28,6 +28,7 @@
 #include "global.h"
 
 #include "erl_gc.h"
+#include "erl_hashmap.h"
 
 #include "hipe_stack.h"
 #include "hipe_gc.h"
-- 
cgit v1.2.3


From 666ba589b76857b3592adf96d2cd096de159cb64 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Sun, 23 Nov 2014 00:32:05 +0100
Subject: Add hashmap:info/1

---
 erts/emulator/beam/bif.tab       |   2 +-
 erts/emulator/beam/erl_hashmap.c | 131 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 132 insertions(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index a3fb21ee52..17e61f4664 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -617,7 +617,7 @@ bif erlang:get_keys/0
 # Hash Array Mappped Trie
 bif hashmap:put/3
 bif hashmap:get/2
-#bif hashmap:info/1
+bif hashmap:info/1
 bif hashmap:to_list/1
 bif hashmap:new/0
 # bif hashmap:keys/1
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index b7d5d2e2eb..5f913edfa6 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -27,6 +27,7 @@
  * lists:foreach(fun(I) -> io:format("looking up ~p got ~p~n", [I, hashmap:get(I, A)]), I = hashmap:get(I,A) end, Ls).
  *
  * lists:foldl(fun(I,O) -> hashmap:put(I,I,O) end, hashmap:new(), lists:seq(1,7)).
+ * lists:foldl(fun(I,O) -> hashmap:info(O), hashmap:put(I,I,O) end, hashmap:new(), lists:seq(1,5)).
  *
  */
 
@@ -61,6 +62,7 @@ static Uint32 hashmap_restore_hash(Uint lvl, Eterm key);
 static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node);
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_to_list(Process *p, Eterm map);
+static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 
 /* hashmap:new/0 */
 
@@ -504,3 +506,132 @@ static Uint32 hashmap_shift_hash(Uint32 hx, Uint *lvl, Eterm key) {
     ret = CONS(heap, make_small((*lvl)), key);
     return make_hash2(ret);
 }
+
+/* hashmap:info/0 */
+
+static Eterm hashmap_info(Process *p, Eterm node) {
+    Eterm *hp, **hpp;
+    Eterm res = NIL, info = NIL;
+    Eterm *ptr, tup, hdr;
+    Uint sz;
+    DECL_AM(depth);
+    DECL_AM(leafs);
+    DECL_AM(bitmaps);
+    DECL_AM(arrays);
+    Uint nleaf=0, nbitmap=0, narray=0;
+    Uint bitmap_usage[16], leaf_usage[16];
+    Uint lvl = 0, clvl;
+    DECLARE_ESTACK(stack);
+
+    for (sz = 0; sz < 16; sz++) {
+	bitmap_usage[sz] = 0;
+	leaf_usage[sz] = 0;
+    }
+
+    ptr = boxed_val(node);
+    ESTACK_PUSH(stack, 0);
+    ESTACK_PUSH(stack, node);
+    do {
+	node = ESTACK_POP(stack);
+	clvl = ESTACK_POP(stack);
+	lvl  = MAX(lvl,clvl);
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST:
+		nleaf++;
+		leaf_usage[clvl] += 1;
+		break;
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			narray++;
+			sz = 16;
+			while(sz--) {
+			    ESTACK_PUSH(stack, clvl + 1);
+			    ESTACK_PUSH(stack, ptr[sz+1]);
+			}
+			break;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			nbitmap++;
+			sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+			ASSERT(sz < 17);
+			bitmap_usage[sz-1] += 1;
+			while(sz--) {
+			    ESTACK_PUSH(stack, clvl + 1);
+			    ESTACK_PUSH(stack, ptr[sz+1]);
+			}
+			break;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			nbitmap++;
+			sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+			bitmap_usage[sz-1] += 1;
+			while(sz--) {
+			    ESTACK_PUSH(stack, clvl + 1);
+			    ESTACK_PUSH(stack, ptr[sz+2]);
+			}
+			break;
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			narray++;
+			sz = 16;
+			while(sz--) {
+			    ESTACK_PUSH(stack, clvl + 1);
+			    ESTACK_PUSH(stack, ptr[sz+2]);
+			}
+			break;
+		    default:
+			erl_exit(1, "bad header\r\n");
+			break;
+		}
+	}
+    } while(!ESTACK_ISEMPTY(stack));
+
+
+    /* size */
+    sz = 0;
+    hashmap_bld_tuple_uint(NULL,&sz,16,leaf_usage);
+    hashmap_bld_tuple_uint(NULL,&sz,16,bitmap_usage);
+
+    /* alloc */
+    hp   = HAlloc(p, 2+3 + 3*(2+4) + sz);
+
+    info = hashmap_bld_tuple_uint(&hp,NULL,16,leaf_usage);
+    tup  = TUPLE3(hp, AM_leafs, make_small(nleaf),info); hp += 4;
+    res  = CONS(hp, tup, res); hp += 2;
+
+    info = hashmap_bld_tuple_uint(&hp,NULL,16,bitmap_usage);
+    tup  = TUPLE3(hp, AM_bitmaps, make_small(nbitmap), info); hp += 4;
+    res  = CONS(hp, tup, res); hp += 2;
+
+    tup  = TUPLE3(hp, AM_arrays, make_small(narray),NIL); hp += 4;
+    res  = CONS(hp, tup, res); hp += 2;
+
+    tup  = TUPLE2(hp, AM_depth, make_small(lvl)); hp += 3;
+    res  = CONS(hp, tup, res); hp += 2;
+
+    DESTROY_ESTACK(stack);
+    ERTS_HOLE_CHECK(p);
+    return res;
+}
+
+static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]) {
+    Eterm res = THE_NON_VALUE;
+    Eterm *ts = (Eterm *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(Eterm));
+    Uint i;
+
+
+    for (i = 0; i < n; i++) {
+	ts[i] = erts_bld_uint(hpp, szp, nums[i]);
+    }
+    res = erts_bld_tuplev(hpp, szp, n, ts);
+    erts_free(ERTS_ALC_T_TMP, (void *) ts);
+    return res;
+}
+
+BIF_RETTYPE hashmap_info_1(BIF_ALIST_1) {
+    if (is_hashmap(BIF_ARG_1)) {
+	BIF_RET(hashmap_info(BIF_P,BIF_ARG_1));
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
-- 
cgit v1.2.3


From c3abb4e6825e7db2e8c4ad647edf55a067c91495 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Mon, 17 Nov 2014 20:49:03 +0100
Subject: Add hashmap:remove/2

---
 erts/emulator/beam/bif.tab       |   1 +
 erts/emulator/beam/erl_hashmap.c | 266 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 264 insertions(+), 3 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 17e61f4664..cf606a9deb 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -617,6 +617,7 @@ bif erlang:get_keys/0
 # Hash Array Mappped Trie
 bif hashmap:put/3
 bif hashmap:get/2
+bif hashmap:remove/2
 bif hashmap:info/1
 bif hashmap:to_list/1
 bif hashmap:new/0
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 5f913edfa6..31d54569e0 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -48,6 +48,7 @@
 #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
 #endif
 
+#if 0
 static char *format_binary(Uint64 x, char *b) {
     int z;
     b[64] = '\0';
@@ -56,11 +57,13 @@ static char *format_binary(Uint64 x, char *b) {
     }
     return b;
 }
+#endif
 
 static Uint32 hashmap_shift_hash(Uint32 hx, Uint *lvl, Eterm key);
 static Uint32 hashmap_restore_hash(Uint lvl, Eterm key);
 static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node);
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
+static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_to_list(Process *p, Eterm map);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 
@@ -109,7 +112,7 @@ BIF_RETTYPE hashmap_get_2(BIF_ALIST_2) {
     if (is_hashmap(BIF_ARG_2)) {
 	const Eterm *value;
 	Uint32 hx = make_hash2(BIF_ARG_1);
-	
+
 	if ((value = hashmap_get(hx, BIF_ARG_1, BIF_ARG_2)) != NULL) {
 	    BIF_RET(*value);
 	}
@@ -117,6 +120,16 @@ BIF_RETTYPE hashmap_get_2(BIF_ALIST_2) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
+/* hashmap:remove/2 */
+
+BIF_RETTYPE hashmap_remove_2(BIF_ALIST_2) {
+    if (is_hashmap(BIF_ARG_2)) {
+	Uint32 hx = make_hash2(BIF_ARG_1);
+
+	BIF_RET(hashmap_delete(BIF_P, hx, BIF_ARG_1, BIF_ARG_2));
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
 /* hashmap:size/1 */
 
 BIF_RETTYPE hashmap_size_1(BIF_ALIST_1) {
@@ -432,6 +445,254 @@ unroll:
     return res;
 }
 
+static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node) {
+    Eterm *hp = NULL, *nhp = NULL, *hp_end = NULL;
+    Eterm *ptr;
+    Eterm hdr,res = node;
+    Uint32 ix, bp, hval;
+    Uint slot, lvl = 0;
+    Uint size = 0, n = 0;
+    DECLARE_ESTACK(stack);
+
+    for (;;) {
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST:
+		if (EQ(CAR(list_val(node)), key)) {
+		    goto unroll;
+		}
+		goto not_found;
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			ix    = hashmap_index(hx);
+			hx    = hashmap_shift_hash(hx,&lvl,key);
+			size += HAMT_NODE_ARRAY_SZ;
+			ESTACK_PUSH2(stack, ix, node);
+			node  = ptr[ix+1];
+			break;
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			ix    = hashmap_index(hx);
+			hx    = hashmap_shift_hash(hx,&lvl,key);
+			size += HAMT_HEAD_ARRAY_SZ;
+			ESTACK_PUSH2(stack, ix, node);
+			node  = ptr[ix+2];
+			break;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+			n    = hashmap_bitcount(hval);
+
+			ESTACK_PUSH(stack, n);
+			ESTACK_PUSH3(stack, bp, slot, node);
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    node  = ptr[slot+1];
+			    ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17);
+			    size += HAMT_NODE_BITMAP_SZ(n);
+			    break;
+			}
+			/* not occupied */
+			goto not_found;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+			n    = hashmap_bitcount(hval);
+
+			ESTACK_PUSH(stack, n);
+			ESTACK_PUSH3(stack, bp, slot, node);
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    node  = ptr[slot+2];
+			    ASSERT(HAMT_HEAD_BITMAP_SZ(n) <= 18);
+			    size += HAMT_HEAD_BITMAP_SZ(n);
+			    break;
+			}
+			/* not occupied */
+			goto not_found;
+		    default:
+			erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
+			break;
+		}
+		break;
+	    default:
+		erl_exit(1, "bad primary tag %p\r\n", node);
+		break;
+	}
+    }
+
+unroll:
+    /* the size is bounded and atleast one less than the previous size */
+    size  -= 1;
+    hp     = HAlloc(p, size);
+    hp_end = hp + size;
+    res    = THE_NON_VALUE;
+
+    do {
+	node = ESTACK_POP(stack);
+
+	/* all nodes are things */
+	ptr = boxed_val(node);
+	hdr = *ptr;
+	ASSERT(is_header(hdr));
+
+	switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+	    case HAMT_SUBTAG_NODE_ARRAY:
+		ix  = (Uint) ESTACK_POP(stack);
+		nhp = hp;
+		if (res == THE_NON_VALUE) {
+		    *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(0xffff ^ (1 << ix)); ptr++;
+		    n     = 16;
+		    n    -= ix;
+		    while(ix--) { *hp++ = *ptr++; }
+		    ptr++; n--;
+		    while(n--) { *hp++ = *ptr++; }
+		    res = make_hashmap(nhp);
+		} else {
+		    n = HAMT_NODE_ARRAY_SZ;
+		    while(n--) { *hp++ = *ptr++; }
+		    nhp[ix+1] = res;
+		    res = make_hashmap(nhp);
+		}
+		break;
+	    case HAMT_SUBTAG_HEAD_ARRAY:
+		ix  = (Uint) ESTACK_POP(stack);
+		nhp = hp;
+		if (res == THE_NON_VALUE) {
+		    n     = 16;
+		    n    -= ix;
+		    *hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(0xffff ^ (1 << ix)); ptr++;
+		    *hp++ = (*ptr++) - 1;
+		    while(ix--) { *hp++ = *ptr++; }
+		    ptr++; n--;
+		    while(n--) { *hp++ = *ptr++; }
+		    res = make_hashmap(nhp);
+		} else {
+		    n     = 16;
+		    *hp++ = MAP_HEADER_HAMT_HEAD_ARRAY; ptr++;
+		    *hp++ = (*ptr++) - 1;
+		    while(n--) { *hp++ = *ptr++; }
+		    nhp[ix+2] = res;
+		    res = make_hashmap(nhp);
+		}
+		break;
+	    case HAMT_SUBTAG_NODE_BITMAP:
+		slot = (Uint)   ESTACK_POP(stack);
+		bp   = (Uint32) ESTACK_POP(stack);
+		n    = (Uint32) ESTACK_POP(stack);
+		nhp  = hp;
+
+		/* bitmap change matrix
+		 * res | none    leaf    bitmap
+		 * ----------------------------
+		 * n=1 | remove  remove  keep
+		 * n=2 | other   keep    keep
+		 * n>2 | shrink  keep    keep
+		 *
+		 * other: (remember, n is 2)
+		 *   shrink if the other bitmap value is a bitmap node
+		 *   remove if the other bitmap value is a leaf
+		 *
+		 * remove:
+		 *   this bitmap node is removed, res is moved up in tree (could be none)
+		 *   this is a special case of shrink
+		 *
+		 * keep:
+		 *   the current path index is still used down in the tree, need to keep it
+		 *   copy as usual with the updated res
+		 *
+		 * shrink:
+		 *   the current path index is no longer used down in the tree, remove it (shrink)
+		 */
+		if (res == THE_NON_VALUE) {
+		    if (n == 1) {
+			break;
+		    } else if (n == 2) {
+			if (slot == 0) {
+			    ix = 2; /* off by one 'cause hdr */
+			} else {
+			    ix = 1; /* off by one 'cause hdr */
+			}
+			if (primary_tag(ptr[ix]) == TAG_PRIMARY_LIST) {
+			    res = ptr[ix];
+			} else {
+			    hval  = MAP_HEADER_VAL(hdr);
+			    *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval ^ bp);
+			    *hp++ = ptr[ix];
+			    res = make_hashmap(nhp);
+			}
+		    } else {
+			/* n > 2 */
+			hval  = MAP_HEADER_VAL(hdr);
+			*hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval ^ bp); ptr++;
+			n    -= slot;
+			while(slot--) { *hp++ = *ptr++; }
+			ptr++; n--;
+			while(n--) { *hp++ = *ptr++; }
+			res = make_hashmap(nhp);
+		    }
+		} else if (primary_tag(res) == TAG_PRIMARY_LIST && n == 1) {
+		    break;
+		} else {
+		    /* res is bitmap or leaf && n > 1, keep */
+		    n    -= slot;
+		    *hp++ = *ptr++;
+		    while(slot--) { *hp++ = *ptr++; }
+		    *hp++ = res;
+		    ptr++; n--;
+		    while(n--) { *hp++ = *ptr++; }
+		    res = make_hashmap(nhp);
+		}
+		break;
+	    case HAMT_SUBTAG_HEAD_BITMAP:
+		slot = (Uint)   ESTACK_POP(stack);
+		bp   = (Uint32) ESTACK_POP(stack);
+		n    = (Uint32) ESTACK_POP(stack);
+		nhp  = hp;
+
+		if (res != THE_NON_VALUE) {
+		    *hp++ = *ptr++;
+		    *hp++ = (*ptr++) - 1;
+		    n    -= slot;
+		    while(slot--) { *hp++ = *ptr++; }
+		    *hp++ = res;
+		    ptr++; n--;
+		    while(n--) { *hp++ = *ptr++; }
+		} else {
+		    hval  = MAP_HEADER_VAL(hdr);
+		    *hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(hval ^ bp); ptr++;
+		    *hp++ = (*ptr++) - 1;
+		    n    -= slot;
+		    while(slot--) { *hp++ = *ptr++; }
+		    ptr++; n--;
+		    while(n--) { *hp++ = *ptr++; }
+		}
+		res = make_hashmap(nhp);
+		break;
+	    default:
+		erl_exit(1, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
+		break;
+	}
+    } while(!ESTACK_ISEMPTY(stack));
+    HRelease(p, hp_end, hp);
+not_found:
+    DESTROY_ESTACK(stack);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+    ERTS_HOLE_CHECK(p);
+    return res;
+}
+
 static Eterm hashmap_to_list(Process *p, Eterm node) {
     Eterm *hp;
     Eterm res = NIL;
@@ -510,7 +771,7 @@ static Uint32 hashmap_shift_hash(Uint32 hx, Uint *lvl, Eterm key) {
 /* hashmap:info/0 */
 
 static Eterm hashmap_info(Process *p, Eterm node) {
-    Eterm *hp, **hpp;
+    Eterm *hp;
     Eterm res = NIL, info = NIL;
     Eterm *ptr, tup, hdr;
     Uint sz;
@@ -620,7 +881,6 @@ static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[])
     Eterm *ts = (Eterm *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(Eterm));
     Uint i;
 
-
     for (i = 0; i < n; i++) {
 	ts[i] = erts_bld_uint(hpp, szp, nums[i]);
     }
-- 
cgit v1.2.3


From f56956cfac208939bbfa2164c38cfe0c8907aa1b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Sun, 23 Nov 2014 07:20:38 +0100
Subject: Refactor hashmap_shift

---
 erts/emulator/beam/erl_hashmap.c | 64 +++++++++++++++-------------------------
 1 file changed, 23 insertions(+), 41 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 31d54569e0..b36f0c6150 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -48,6 +48,11 @@
 #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
 #endif
 
+#define hashmap_restore_hash(Heap,Lvl,Key) \
+    ((Lvl) < 8) ? make_hash2(Key) >> (4*(Lvl)) : make_hash2(CONS(Heap, make_small(Lvl), (Key))) >> (4*((Lvl) % 8))
+#define hashmap_shift_hash(Heap,Hx,Lvl,Key) \
+    ((++(Lvl)) % 8) ? (Hx) >> 4 : make_hash2(CONS(Heap, make_small(Lvl), Key))
+
 #if 0
 static char *format_binary(Uint64 x, char *b) {
     int z;
@@ -59,8 +64,6 @@ static char *format_binary(Uint64 x, char *b) {
 }
 #endif
 
-static Uint32 hashmap_shift_hash(Uint32 hx, Uint *lvl, Eterm key);
-static Uint32 hashmap_restore_hash(Uint lvl, Eterm key);
 static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node);
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
@@ -160,6 +163,7 @@ BIF_RETTYPE is_hashmap_1(BIF_ALIST_1) {
 
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
     Eterm *ptr, hdr;
+    Eterm th[2];
     Uint ix,slot, lvl = 0;
     Uint32 hval,bp;
 
@@ -179,12 +183,12 @@ static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
 		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
 		    case HAMT_SUBTAG_NODE_ARRAY:
 			ix   = hashmap_index(hx);
-			hx   = hashmap_shift_hash(hx,&lvl,key);
+			hx   = hashmap_shift_hash(th,hx,lvl,key);
 			node = ptr[ix+1];
 			break;
 		    case HAMT_SUBTAG_HEAD_ARRAY:
 			ix   = hashmap_index(hx);
-			hx   = hashmap_shift_hash(hx,&lvl,key);
+			hx   = hashmap_shift_hash(th,hx,lvl,key);
 			node = ptr[ix+2];
 			break;
 		    case HAMT_SUBTAG_NODE_BITMAP:
@@ -195,7 +199,7 @@ static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
 
 			/* occupied */
 			if (bp & hval) {
-			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
 			    node  = ptr[slot+1];
 			    break;
 			}
@@ -209,7 +213,7 @@ static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
 
 			/* occupied */
 			if (bp & hval) {
-			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
 			    node  = ptr[slot+2];
 			    break;
 			}
@@ -232,6 +236,7 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm
     Eterm *hp = NULL, *nhp = NULL;
     Eterm *ptr;
     Eterm hdr,res,ckey,fake;
+    Eterm th[2];
     Uint32 ix, cix, bp, hval, chx;
     Uint slot, lvl = 0, clvl;
     Uint size = 0, n = 0, update_size = 1;
@@ -255,14 +260,14 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm
 		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
 		    case HAMT_SUBTAG_NODE_ARRAY:
 			ix    = hashmap_index(hx);
-			hx    = hashmap_shift_hash(hx,&lvl,key);
+			hx    = hashmap_shift_hash(th,hx,lvl,key);
 			size += HAMT_NODE_ARRAY_SZ;
 			ESTACK_PUSH2(stack, ix, node);
 			node  = ptr[ix+1];
 			break;
 		    case HAMT_SUBTAG_HEAD_ARRAY:
 			ix    = hashmap_index(hx);
-			hx    = hashmap_shift_hash(hx,&lvl,key);
+			hx    = hashmap_shift_hash(th,hx,lvl,key);
 			size += HAMT_HEAD_ARRAY_SZ;
 			ESTACK_PUSH2(stack, ix, node);
 			node  = ptr[ix+2];
@@ -279,7 +284,7 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm
 
 			/* occupied */
 			if (bp & hval) {
-			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
 			    node  = ptr[slot+1];
 			    ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17); 
 			    size += HAMT_NODE_BITMAP_SZ(n);
@@ -300,7 +305,7 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm
 
 			/* occupied */
 			if (bp & hval) {
-			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
 			    node  = ptr[slot+2];
 			    ASSERT(HAMT_HEAD_BITMAP_SZ(n) <= 18); 
 			    size += HAMT_HEAD_BITMAP_SZ(n);
@@ -321,7 +326,7 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm
     }
 insert_subnodes:
     clvl  = lvl;
-    chx   = hashmap_restore_hash(clvl,ckey);
+    chx   = hashmap_restore_hash(th,clvl,ckey);
     size += HAMT_NODE_BITMAP_SZ(2);
     ix    = hashmap_index(hx);
     cix   = hashmap_index(chx);
@@ -330,8 +335,8 @@ insert_subnodes:
 	ESTACK_PUSH(stack, 0);
 	ESTACK_PUSH3(stack, 1 << ix, 0, MAP_HEADER_HAMT_NODE_BITMAP(0));
 	size += HAMT_NODE_BITMAP_SZ(1);
-	hx    = hashmap_shift_hash(hx,&lvl,key);
-	chx   = hashmap_shift_hash(chx,&clvl,ckey);
+	hx    = hashmap_shift_hash(th,hx,lvl,key);
+	chx   = hashmap_shift_hash(th,chx,clvl,ckey);
 	ix    = hashmap_index(hx);
 	cix   = hashmap_index(chx);
     }
@@ -447,6 +452,7 @@ unroll:
 
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node) {
     Eterm *hp = NULL, *nhp = NULL, *hp_end = NULL;
+    Eterm th[2];
     Eterm *ptr;
     Eterm hdr,res = node;
     Uint32 ix, bp, hval;
@@ -469,14 +475,14 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node) {
 		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
 		    case HAMT_SUBTAG_NODE_ARRAY:
 			ix    = hashmap_index(hx);
-			hx    = hashmap_shift_hash(hx,&lvl,key);
+			hx    = hashmap_shift_hash(th,hx,lvl,key);
 			size += HAMT_NODE_ARRAY_SZ;
 			ESTACK_PUSH2(stack, ix, node);
 			node  = ptr[ix+1];
 			break;
 		    case HAMT_SUBTAG_HEAD_ARRAY:
 			ix    = hashmap_index(hx);
-			hx    = hashmap_shift_hash(hx,&lvl,key);
+			hx    = hashmap_shift_hash(th,hx,lvl,key);
 			size += HAMT_HEAD_ARRAY_SZ;
 			ESTACK_PUSH2(stack, ix, node);
 			node  = ptr[ix+2];
@@ -493,7 +499,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node) {
 
 			/* occupied */
 			if (bp & hval) {
-			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
 			    node  = ptr[slot+1];
 			    ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17);
 			    size += HAMT_NODE_BITMAP_SZ(n);
@@ -513,7 +519,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node) {
 
 			/* occupied */
 			if (bp & hval) {
-			    hx    = hashmap_shift_hash(hx,&lvl,key);
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
 			    node  = ptr[slot+2];
 			    ASSERT(HAMT_HEAD_BITMAP_SZ(n) <= 18);
 			    size += HAMT_HEAD_BITMAP_SZ(n);
@@ -744,30 +750,6 @@ static Eterm hashmap_to_list(Process *p, Eterm node) {
     return res;
 }
 
-static Uint32 hashmap_restore_hash(Uint lvl, Eterm key) {
-    Eterm heap[2], ret;
-
-    if (lvl < 8) {
-	return make_hash2(key) >> (4*lvl);
-    }
-    ret = CONS(heap, make_small(lvl), key);
-
-    return make_hash2(ret) >> (4*(lvl % 8));
-}
-
-static Uint32 hashmap_shift_hash(Uint32 hx, Uint *lvl, Eterm key) {
-    Eterm heap[2], ret;
-
-    /* rehash with [ lvl | key ] */
-    *lvl = *lvl + 1;
-    if (*lvl % 8) {
-	return hx >> 4;
-    }
-
-    ret = CONS(heap, make_small((*lvl)), key);
-    return make_hash2(ret);
-}
-
 /* hashmap:info/0 */
 
 static Eterm hashmap_info(Process *p, Eterm node) {
-- 
cgit v1.2.3


From 6c94fede355561f0b4241005e5ffecdba210825d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 25 Nov 2014 16:58:37 +0100
Subject: Don't use modulus for power of 2

---
 erts/emulator/beam/erl_hashmap.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index b36f0c6150..11d2309fe3 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -49,9 +49,9 @@
 #endif
 
 #define hashmap_restore_hash(Heap,Lvl,Key) \
-    ((Lvl) < 8) ? make_hash2(Key) >> (4*(Lvl)) : make_hash2(CONS(Heap, make_small(Lvl), (Key))) >> (4*((Lvl) % 8))
+    ((Lvl) < 8) ? make_hash2(Key) >> (4*(Lvl)) : make_hash2(CONS(Heap, make_small(Lvl), (Key))) >> (4*((Lvl) & 7))
 #define hashmap_shift_hash(Heap,Hx,Lvl,Key) \
-    ((++(Lvl)) % 8) ? (Hx) >> 4 : make_hash2(CONS(Heap, make_small(Lvl), Key))
+    ((++(Lvl)) & 7) ? (Hx) >> 4 : make_hash2(CONS(Heap, make_small(Lvl), Key))
 
 #if 0
 static char *format_binary(Uint64 x, char *b) {
-- 
cgit v1.2.3


From f602e2327c330969165430bbba1a0b997200606d Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Wed, 14 Jan 2015 20:12:52 +0100
Subject: hashmap: is_key/2, keys/1 and values/1

---
 erts/emulator/beam/bif.tab       |   4 +-
 erts/emulator/beam/erl_hashmap.c | 123 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index cf606a9deb..ed85021f8a 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -621,9 +621,11 @@ bif hashmap:remove/2
 bif hashmap:info/1
 bif hashmap:to_list/1
 bif hashmap:new/0
-# bif hashmap:keys/1
+bif hashmap:is_key/2
+bif hashmap:keys/1
 bif hashmap:size/1
 bif erlang:is_hashmap/1
+bif hashmap:values/1
 
 #
 # Obsolete
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 11d2309fe3..5cb768a3fe 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -68,6 +68,8 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_to_list(Process *p, Eterm map);
+static Eterm hashmap_keys(Process *p, Eterm map);
+static Eterm hashmap_values(Process *p, Eterm map);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 
 /* hashmap:new/0 */
@@ -159,6 +161,38 @@ BIF_RETTYPE is_hashmap_1(BIF_ALIST_1) {
     BIF_RET(am_false);
 }
 
+/* hashmap:is_key/2
+ */
+
+BIF_RETTYPE hashmap_is_key_2(BIF_ALIST_2) {
+    if (is_hashmap(BIF_ARG_1)) {
+	Uint32 hx = make_hash2(BIF_ARG_1);
+
+	BIF_RET(hashmap_get(hx, BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
+/* hashmap:keys/1
+ */
+
+BIF_RETTYPE hashmap_keys_1(BIF_ALIST_1) {
+    if (is_hashmap(BIF_ARG_1)) {
+	BIF_RET(hashmap_keys(BIF_P, BIF_ARG_1));
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
+/* hashmap:keys/1
+ */
+
+BIF_RETTYPE hashmap_values_1(BIF_ALIST_1) {
+    if (is_hashmap(BIF_ARG_1)) {
+	BIF_RET(hashmap_values(BIF_P, BIF_ARG_1));
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
 /* impl. */
 
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
@@ -750,6 +784,95 @@ static Eterm hashmap_to_list(Process *p, Eterm node) {
     return res;
 }
 
+typedef void hashmap_doer(Eterm*, void*);
+
+static void hashmap_do_foreach(Eterm node, hashmap_doer* fptr, void* farg) {
+    Eterm *ptr, hdr;
+    Uint sz;
+    DECLARE_ESTACK(stack);
+
+    ESTACK_PUSH(stack, node);
+    do {
+	node = ESTACK_POP(stack);
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST:
+		ptr = list_val(node);
+		(*fptr)(ptr, farg); /* Do! */
+		break;
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			ptr++;
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			ptr++;
+			sz = 16;
+			while(sz--) { ESTACK_PUSH(stack, ptr[sz]); }
+			break;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			ptr++;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+			ASSERT(sz < 17);
+			ptr++;
+			while(sz--) { ESTACK_PUSH(stack, ptr[sz]); }
+			break;
+		    default:
+			erl_exit(1, "bad header\r\n");
+			break;
+		}
+	}
+    } while(!ESTACK_ISEMPTY(stack));
+
+    DESTROY_ESTACK(stack);
+}
+
+typedef struct {
+    Eterm* hp;
+    Eterm res;
+}hashmap_keys_state;
+static void hashmap_keys_doer(Eterm* kv, hashmap_keys_state*);
+
+static Eterm hashmap_keys(Process* p, Eterm node) {
+    hashmap_head_t* root;
+    hashmap_keys_state state;
+
+    root = (hashmap_head_t*) boxed_val(node);
+    state.hp  = HAlloc(p, root->size * 2);
+    state.res = NIL;
+    hashmap_do_foreach(node, (hashmap_doer*)hashmap_keys_doer, &state);
+    return state.res;
+}
+
+static void hashmap_keys_doer(Eterm* kv, hashmap_keys_state* statep) {
+    statep->res = CONS(statep->hp, CAR(kv), statep->res);
+    statep->hp += 2;
+}
+
+typedef struct {
+    Eterm* hp;
+    Eterm res;
+}hashmap_values_state;
+static void hashmap_values_doer(Eterm* kv, hashmap_values_state*);
+
+static Eterm hashmap_values(Process* p, Eterm node) {
+    hashmap_head_t* root;
+    hashmap_values_state state;
+
+    root = (hashmap_head_t*) boxed_val(node);
+    state.hp  = HAlloc(p, root->size * 2);
+    state.res = NIL;
+    hashmap_do_foreach(node, (hashmap_doer*)hashmap_values_doer, &state);
+    return state.res;
+}
+
+static void hashmap_values_doer(Eterm* kv, hashmap_values_state* statep) {
+    statep->res = CONS(statep->hp, CDR(kv), statep->res);
+    statep->hp += 2;
+}
+
 /* hashmap:info/0 */
 
 static Eterm hashmap_info(Process *p, Eterm node) {
-- 
cgit v1.2.3


From 96f6e470284858f1b8644ea949283dad24161dfe Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Fri, 16 Jan 2015 21:23:44 +0100
Subject: erts: Fix bug in _make_header macro

Called with a signed int 'sz' argument on 64 bit
would cause sign extension 'sz' was larger than 33554431.
---
 erts/emulator/beam/erl_term.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index fde90997e3..72946d9ddf 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -299,7 +299,7 @@ _ET_DECLARE_CHECKED(Uint,atom_val,Eterm)
 #define atom_val(x)	_ET_APPLY(atom_val,(x))
 
 /* header (arityval or thing) access methods */
-#define _make_header(sz,tag)  ((Uint)(((sz) << _HEADER_ARITY_OFFS) + (tag)))
+#define _make_header(sz,tag) ((Uint)(((Uint)(sz) << _HEADER_ARITY_OFFS) + (tag)))
 #define is_header(x)	(((x) & _TAG_PRIMARY_MASK) == TAG_PRIMARY_HEADER)
 //#define _unchecked_header_arity(x)	((x) >> _HEADER_ARITY_OFFS)
 #define _unchecked_header_arity(x) \
-- 
cgit v1.2.3


From 7e045b2c7e545d5640e74280b2e7f75761961c39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 12 Mar 2015 18:43:19 +0100
Subject: erts: Fix beam_load assert

---
 erts/emulator/beam/beam_load.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index fce710f723..02689e5b19 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -3727,7 +3727,7 @@ gen_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail,
     op->a[j+size] = Fail;
 
 #ifdef DEBUG
-    for (i = 0; i < size; i++) {
+    for (i = 0; i < size - 1; i++) {
 	ASSERT(op->a[i+3].val <= op->a[i+4].val);
     }
 #endif
-- 
cgit v1.2.3


From 2dfa8ceaf30d69c9e5753b706dafe741b3cda4ca Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Fri, 16 Jan 2015 21:25:50 +0100
Subject: erts: Add matching of hashmaps

---
 erts/emulator/beam/utils.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index b341c4d949..cea20a6002 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -2325,6 +2325,32 @@ tailrecur_ne:
 		    }
 		    break; /* not equal */
 		}
+	    case HASHMAP_SUBTAG:
+		{
+		    if (!is_boxed(b) || *boxed_val_rel(b,b_base) != hdr)
+			goto not_equal;
+
+		    aa = hashmap_val_rel(a, a_base) + 1;
+		    bb = hashmap_val_rel(b, b_base) + 1;
+		    switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			aa++; bb++;
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			sz = 16;
+			break;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			aa++; bb++;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+			ASSERT(sz > 0 && sz < 16);
+			break;
+		    default:
+			erl_exit(1, "Unknown hashmap subsubtag\n");
+		    }
+		    goto term_array;
+		}
+	    default:
+		ASSERT(!"Unknown boxed subtab in EQ");
 	    }
 	    break;
 	}
-- 
cgit v1.2.3


From eda3fa566070f658e396fe1a2297d956f4f364c1 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Mon, 19 Jan 2015 20:40:45 +0100
Subject: erts: First recursive version of hashmap:merge

---
 erts/emulator/beam/bif.tab       |   1 +
 erts/emulator/beam/erl_hashmap.c | 225 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 226 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index ed85021f8a..6408883e96 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -626,6 +626,7 @@ bif hashmap:keys/1
 bif hashmap:size/1
 bif erlang:is_hashmap/1
 bif hashmap:values/1
+bif hashmap:merge/2
 
 #
 # Obsolete
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 5cb768a3fe..74f27fb3ac 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -70,6 +70,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_to_list(Process *p, Eterm map);
 static Eterm hashmap_keys(Process *p, Eterm map);
 static Eterm hashmap_values(Process *p, Eterm map);
+static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB, Uint lvl, Uint *sizep, int keepA);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 
 /* hashmap:new/0 */
@@ -193,6 +194,16 @@ BIF_RETTYPE hashmap_values_1(BIF_ALIST_1) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
+BIF_RETTYPE hashmap_merge_2(BIF_ALIST_2) {
+    if (is_hashmap(BIF_ARG_1) && is_hashmap(BIF_ARG_2)) {
+	hashmap_head_t* a = (hashmap_head_t*) hashmap_val(BIF_ARG_1);
+	hashmap_head_t* b = (hashmap_head_t*) hashmap_val(BIF_ARG_2);
+	Uint size = a->size + b->size;
+	BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2, 0, &size, 0));
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
 /* impl. */
 
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
@@ -784,6 +795,220 @@ static Eterm hashmap_to_list(Process *p, Eterm node) {
     return res;
 }
 
+#define HALLOC_EXTRA 200
+
+static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB,
+			   Uint lvl, Uint *sizep, int keepA) {
+    Eterm *hp, *nhp;
+    Eterm *ptrA, *ptrB;
+    Eterm array[16];
+    Eterm hdrA, hdrB;
+    Eterm th[2];
+    Uint32 ahx, bhx, abm, bbm, rbm, bit;
+    Uint ix;
+
+    if (primary_tag(nodeA) == TAG_PRIMARY_BOXED &&
+	primary_tag(nodeB) == TAG_PRIMARY_LIST) {
+	/* Avoid implementing this combination by switching places */
+	Eterm tmp = nodeA;
+	nodeA = nodeB;
+	nodeB = tmp;
+	keepA = !keepA;
+    }
+
+    switch (primary_tag(nodeA)) {
+    case TAG_PRIMARY_LIST: { /* LEAF NODE [K|V] */
+	ptrA = list_val(nodeA);
+	switch (primary_tag(nodeB)) {
+	case TAG_PRIMARY_LIST: { /* LEAF NODE [K|V] */
+	    ptrB = list_val(nodeB);
+
+	    if (EQ(CAR(ptrA), CAR(ptrB))) {
+		--(*sizep);
+		return keepA ? nodeA : nodeB;
+	    }
+	    ahx = hashmap_restore_hash(th, lvl, CAR(ptrA));
+	    bhx = hashmap_restore_hash(th, lvl, CAR(ptrB));
+	    abm = 1 << hashmap_index(ahx);
+	    bbm = 1 << hashmap_index(bhx);
+
+	    if (abm != bbm) {
+		hp = HAllocX(p, HAMT_NODE_BITMAP_SZ(2), HALLOC_EXTRA);
+		hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(abm | bbm);
+		if (abm < bbm) {
+		    hp[1] = CAR(ptrA);
+		    hp[2] = CAR(ptrB);
+		} else {
+		    hp[1] = CAR(ptrB);
+		    hp[2] = CAR(ptrA);
+		}
+	    }
+	    else {
+		hp = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
+		hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(abm);
+		hp[1] = hashmap_merge(p, nodeA, nodeB, lvl+1, sizep, keepA);
+	    }
+	    return make_hashmap(hp);
+	}
+	case TAG_PRIMARY_BOXED: {
+	    ptrB = boxed_val(nodeB);
+	    ASSERT(is_header(*ptrB));
+	    hdrB = *ptrB++;
+
+	    ahx = hashmap_restore_hash(th, lvl, CAR(ptrA));
+	    abm = 1 << hashmap_index(ahx);
+
+	    switch(hdrB & _HEADER_MAP_SUBTAG_MASK) {
+	    case HAMT_SUBTAG_HEAD_ARRAY:
+		ptrB++;
+	    case HAMT_SUBTAG_NODE_ARRAY:
+		bbm = 0xffff;
+		ptrA = &nodeA;
+		break;
+
+	    case HAMT_SUBTAG_HEAD_BITMAP:
+		ptrB++;
+	    case HAMT_SUBTAG_NODE_BITMAP:
+		bbm = MAP_HEADER_VAL(hdrB);
+		ptrA = &nodeA;
+		break;
+
+	    default:
+		erl_exit(1, "bad header tag %ld\r\n", *ptrB & _HEADER_MAP_SUBTAG_MASK);
+		break;
+	    }
+	    break;
+	}
+	default:
+	    erl_exit(1, "bad primary tag %ld\r\n", nodeB);
+	}
+	break;
+    }
+    case TAG_PRIMARY_BOXED: {
+	ptrA = boxed_val(nodeA);
+	hdrA = *ptrA;
+	ASSERT(is_header(hdrA));
+	ptrA++;
+	switch (hdrA & _HEADER_MAP_SUBTAG_MASK) {
+	case HAMT_SUBTAG_HEAD_ARRAY:
+	    ptrA++;
+	case HAMT_SUBTAG_NODE_ARRAY: {
+	    ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED);
+	    ptrB = boxed_val(nodeB);
+	    hdrB = *ptrB;
+	    ASSERT(is_header(hdrB));
+	    switch (hdrB & _HEADER_MAP_SUBTAG_MASK) {
+	    case HAMT_SUBTAG_HEAD_ARRAY:
+		ASSERT(lvl == 0);
+		abm = 0xffff;
+		bbm = 0xffff;
+		ptrB += 2;
+		break;
+	    case HAMT_SUBTAG_NODE_ARRAY:
+		ASSERT(lvl > 0);
+		abm = 0xffff;
+		bbm = 0xffff;
+		ptrB++;
+		break;
+	    case HAMT_SUBTAG_HEAD_BITMAP:
+		ASSERT(lvl == 0);
+		abm = 0xffff;
+		bbm = MAP_HEADER_VAL(hdrB);
+		ptrB += 2;
+		break;
+	    case HAMT_SUBTAG_NODE_BITMAP:
+		ASSERT(lvl > 0);
+		abm = 0xffff;
+		bbm = MAP_HEADER_VAL(hdrB);
+		ptrB += 1;
+		break;
+	    default:
+		erl_exit(1, "bad header tag %ld\r\n", *ptrB & _HEADER_MAP_SUBTAG_MASK);
+	    }
+	    break;
+	}
+	case HAMT_SUBTAG_HEAD_BITMAP:
+	    ptrA++;
+	case HAMT_SUBTAG_NODE_BITMAP: {
+	    ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED);
+	    abm = MAP_HEADER_VAL(hdrA);
+	    ptrB = boxed_val(nodeB);
+	    hdrB = *ptrB;
+	    ASSERT(is_header(hdrB));
+	    switch (hdrB & _HEADER_MAP_SUBTAG_MASK) {
+	    case HAMT_SUBTAG_HEAD_ARRAY:
+		ASSERT(lvl == 0);
+		bbm = 0xffff;
+		ptrB += 2;
+		break;
+
+	    case HAMT_SUBTAG_NODE_ARRAY:
+		ASSERT(lvl > 0);
+		bbm = 0xffff;
+		ptrB++;
+		break;
+
+	    case HAMT_SUBTAG_HEAD_BITMAP:
+		ASSERT(lvl == 0);
+		bbm = MAP_HEADER_VAL(hdrB);
+		ptrB += 2;
+		break;
+
+	    case HAMT_SUBTAG_NODE_BITMAP:
+		ASSERT(lvl > 0);
+		bbm = MAP_HEADER_VAL(hdrB);
+		ptrB += 1;
+		break;
+
+	    default:
+		erl_exit(1, "bad header tag %ld\r\n", *ptrB & _HEADER_MAP_SUBTAG_MASK);
+	    }
+	    break;
+	}
+	default:
+	    erl_exit(1, "bad primary tag %ld\r\n", nodeA);
+	}
+	break;
+    }
+    default:
+	erl_exit(1, "bad primary tag %ld\r\n", nodeA);
+    }
+
+    ix = 0;
+    rbm = abm | bbm;
+    ASSERT(rbm != 0);
+    do {
+	Uint32 next = rbm & (rbm-1);
+	bit = rbm ^ next;
+	rbm = next;
+	if (abm & bit) {
+	    if (bbm & bit) {
+		array[ix++] = hashmap_merge(p, *ptrA++, *ptrB++, lvl+1, sizep, keepA);
+	    } else {
+		array[ix++] = *ptrA++;
+	    }
+	} else {
+	    ASSERT(bbm & bit);
+	    array[ix++] = *ptrB++;
+	}
+    }while (rbm);
+
+    if (lvl == 0) {
+	nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(ix), HALLOC_EXTRA);
+	hp = nhp;
+	*hp++ = (ix == 16) ? MAP_HEADER_HAMT_HEAD_ARRAY
+	                   : MAP_HEADER_HAMT_HEAD_BITMAP(abm | bbm);
+	*hp++ = *sizep;
+    } else {
+	nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(ix), HALLOC_EXTRA);
+	hp = nhp;
+	*hp++ = (ix == 16) ? make_arityval(16)
+	                   : MAP_HEADER_HAMT_NODE_BITMAP(abm | bbm);
+    }
+    memcpy(hp, array, ix * sizeof(Eterm));
+    return make_boxed(nhp);
+}
+
 typedef void hashmap_doer(Eterm*, void*);
 
 static void hashmap_do_foreach(Eterm node, hashmap_doer* fptr, void* farg) {
-- 
cgit v1.2.3


From f3c7a82d45db4c8d9a9f3579726719360384da50 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Wed, 21 Jan 2015 14:17:41 +0100
Subject: First non-recursive version of hashmap:merge/2

---
 erts/emulator/beam/erl_hashmap.c | 180 +++++++++++++++++++++++++--------------
 1 file changed, 116 insertions(+), 64 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 74f27fb3ac..bdcb479c30 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -70,7 +70,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_to_list(Process *p, Eterm map);
 static Eterm hashmap_keys(Process *p, Eterm map);
 static Eterm hashmap_values(Process *p, Eterm map);
-static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB, Uint lvl, Uint *sizep, int keepA);
+static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 
 /* hashmap:new/0 */
@@ -196,10 +196,7 @@ BIF_RETTYPE hashmap_values_1(BIF_ALIST_1) {
 
 BIF_RETTYPE hashmap_merge_2(BIF_ALIST_2) {
     if (is_hashmap(BIF_ARG_1) && is_hashmap(BIF_ARG_2)) {
-	hashmap_head_t* a = (hashmap_head_t*) hashmap_val(BIF_ARG_1);
-	hashmap_head_t* b = (hashmap_head_t*) hashmap_val(BIF_ARG_2);
-	Uint size = a->size + b->size;
-	BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2, 0, &size, 0));
+	BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2));
     }
     BIF_ERROR(BIF_P, BADARG);
 }
@@ -797,15 +794,46 @@ static Eterm hashmap_to_list(Process *p, Eterm node) {
 
 #define HALLOC_EXTRA 200
 
-static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB,
-			   Uint lvl, Uint *sizep, int keepA) {
+#if defined(VALGRIND) || defined(GO_SUPER_FAST)
+# define UNDEF(V,I)
+#else
+# define UNDEF(V,I) V=I
+#endif
+
+static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
+    struct {
+	Eterm *ptrA, *ptrB;
+	Uint32 abm, bbm, rbm;
+	Uint ix;
+	int keepA;
+	Eterm array[16];
+    }stack[16];
     Eterm *hp, *nhp;
     Eterm *ptrA, *ptrB;
-    Eterm array[16];
     Eterm hdrA, hdrB;
     Eterm th[2];
     Uint32 ahx, bhx, abm, bbm, rbm, bit;
     Uint ix;
+    Uint size;
+    int keepA = 0;
+    unsigned lvl = 0;
+    Eterm res = THE_NON_VALUE;
+
+    UNDEF(abm,0);
+    UNDEF(bbm,0);
+
+    /*
+     * Strategy: Do depth-first traversal of both trees (at the same time)
+     * and merge each pair of nodes.
+     */
+
+    {
+	hashmap_head_t* a = (hashmap_head_t*) hashmap_val(nodeA);
+	hashmap_head_t* b = (hashmap_head_t*) hashmap_val(nodeB);
+	size = a->size + b->size;
+    }
+
+recurse:
 
     if (primary_tag(nodeA) == TAG_PRIMARY_BOXED &&
 	primary_tag(nodeB) == TAG_PRIMARY_LIST) {
@@ -817,40 +845,27 @@ static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB,
     }
 
     switch (primary_tag(nodeA)) {
-    case TAG_PRIMARY_LIST: { /* LEAF NODE [K|V] */
+    case TAG_PRIMARY_LIST: {
 	ptrA = list_val(nodeA);
 	switch (primary_tag(nodeB)) {
-	case TAG_PRIMARY_LIST: { /* LEAF NODE [K|V] */
+	case TAG_PRIMARY_LIST: { /* LEAF + LEAF */
 	    ptrB = list_val(nodeB);
 
 	    if (EQ(CAR(ptrA), CAR(ptrB))) {
-		--(*sizep);
-		return keepA ? nodeA : nodeB;
-	    }
-	    ahx = hashmap_restore_hash(th, lvl, CAR(ptrA));
-	    bhx = hashmap_restore_hash(th, lvl, CAR(ptrB));
-	    abm = 1 << hashmap_index(ahx);
-	    bbm = 1 << hashmap_index(bhx);
-
-	    if (abm != bbm) {
-		hp = HAllocX(p, HAMT_NODE_BITMAP_SZ(2), HALLOC_EXTRA);
-		hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(abm | bbm);
-		if (abm < bbm) {
-		    hp[1] = CAR(ptrA);
-		    hp[2] = CAR(ptrB);
-		} else {
-		    hp[1] = CAR(ptrB);
-		    hp[2] = CAR(ptrA);
-		}
-	    }
-	    else {
-		hp = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
-		hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(abm);
-		hp[1] = hashmap_merge(p, nodeA, nodeB, lvl+1, sizep, keepA);
+		--size;
+		res = keepA ? nodeA : nodeB;
+	    } else {
+		ahx = hashmap_restore_hash(th, lvl, CAR(ptrA));
+		bhx = hashmap_restore_hash(th, lvl, CAR(ptrB));
+		abm = 1 << hashmap_index(ahx);
+		bbm = 1 << hashmap_index(bhx);
+
+		ptrA = &nodeA;
+		ptrB = &nodeB;
 	    }
-	    return make_hashmap(hp);
+	    break;
 	}
-	case TAG_PRIMARY_BOXED: {
+	case TAG_PRIMARY_BOXED: { /* LEAF + NODE */
 	    ptrB = boxed_val(nodeB);
 	    ASSERT(is_header(*ptrB));
 	    hdrB = *ptrB++;
@@ -884,7 +899,7 @@ static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB,
 	}
 	break;
     }
-    case TAG_PRIMARY_BOXED: {
+    case TAG_PRIMARY_BOXED: { /* NODE + NODE */
 	ptrA = boxed_val(nodeA);
 	hdrA = *ptrA;
 	ASSERT(is_header(hdrA));
@@ -974,39 +989,76 @@ static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB,
 	erl_exit(1, "bad primary tag %ld\r\n", nodeA);
     }
 
-    ix = 0;
-    rbm = abm | bbm;
-    ASSERT(rbm != 0);
-    do {
-	Uint32 next = rbm & (rbm-1);
-	bit = rbm ^ next;
-	rbm = next;
-	if (abm & bit) {
-	    if (bbm & bit) {
-		array[ix++] = hashmap_merge(p, *ptrA++, *ptrB++, lvl+1, sizep, keepA);
+    for (;;) {
+	if (is_value(res)) { /* We have a complete (sub-)tree or leaf */
+	    if (lvl == 0)
+		return res;
+
+	    /* Pop from stack and continue build parent node */
+	    lvl--;
+	    abm = stack[lvl].abm;
+	    bbm = stack[lvl].bbm;
+	    rbm = stack[lvl].rbm;
+	    ix  = stack[lvl].ix;
+	    stack[lvl].array[ix++] = res;
+	    res = THE_NON_VALUE;
+	    if (rbm) {
+		ptrA = stack[lvl].ptrA;
+		ptrB = stack[lvl].ptrB;
+		keepA = stack[lvl].keepA;
+	    }
+	} else { /* Start build a node */
+	    ix = 0;
+	    rbm = abm | bbm;
+	    ASSERT(!(rbm == 0 && lvl > 0));
+	}
+
+	while (rbm) {
+	    Uint32 next = rbm & (rbm-1);
+	    bit = rbm ^ next;
+	    rbm = next;
+	    if (abm & bit) {
+		if (bbm & bit) {
+		    /* Bit clash. Push and resolve by recursive merge */
+		    if (rbm) {
+			stack[lvl].ptrA = ptrA + 1;
+			stack[lvl].ptrB = ptrB + 1;
+			stack[lvl].keepA = keepA;
+		    }
+		    stack[lvl].abm = abm;
+		    stack[lvl].bbm = bbm;
+		    stack[lvl].rbm = rbm;
+		    stack[lvl].ix  = ix;
+		    nodeA = *ptrA;
+		    nodeB = *ptrB;
+		    lvl++;
+		    goto recurse;
+		} else {
+		    stack[lvl].array[ix++] = *ptrA++;
+		}
 	    } else {
-		array[ix++] = *ptrA++;
+		ASSERT(bbm & bit);
+		stack[lvl].array[ix++] = *ptrB++;
 	    }
+	}
+
+	if (lvl == 0) {
+	    nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(ix), HALLOC_EXTRA);
+	    hp = nhp;
+	    *hp++ = (ix == 16) ? MAP_HEADER_HAMT_HEAD_ARRAY
+		: MAP_HEADER_HAMT_HEAD_BITMAP(abm | bbm);
+	    *hp++ = size;
 	} else {
-	    ASSERT(bbm & bit);
-	    array[ix++] = *ptrB++;
+	    nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(ix), HALLOC_EXTRA);
+	    hp = nhp;
+	    *hp++ = (ix == 16) ? make_arityval(16)
+				   : MAP_HEADER_HAMT_NODE_BITMAP(abm | bbm);
 	}
-    }while (rbm);
-
-    if (lvl == 0) {
-	nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(ix), HALLOC_EXTRA);
-	hp = nhp;
-	*hp++ = (ix == 16) ? MAP_HEADER_HAMT_HEAD_ARRAY
-	                   : MAP_HEADER_HAMT_HEAD_BITMAP(abm | bbm);
-	*hp++ = *sizep;
-    } else {
-	nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(ix), HALLOC_EXTRA);
-	hp = nhp;
-	*hp++ = (ix == 16) ? make_arityval(16)
-	                   : MAP_HEADER_HAMT_NODE_BITMAP(abm | bbm);
+	memcpy(hp, stack[lvl].array, ix * sizeof(Eterm));
+	res = make_boxed(nhp);
     }
-    memcpy(hp, array, ix * sizeof(Eterm));
-    return make_boxed(nhp);
+
+    return res;
 }
 
 typedef void hashmap_doer(Eterm*, void*);
-- 
cgit v1.2.3


From 10a4f2777669ef2d7212e234cd272c64bb707e07 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Wed, 21 Jan 2015 14:19:24 +0100
Subject: erts: Add missing parenthesis in MAKE_MAP_HEADER

---
 erts/emulator/beam/erl_hashmap.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.h b/erts/emulator/beam/erl_hashmap.h
index 03739fa1f9..4ef795b11e 100644
--- a/erts/emulator/beam/erl_hashmap.h
+++ b/erts/emulator/beam/erl_hashmap.h
@@ -120,7 +120,7 @@ typedef struct hashmap_head_s {
 #define is_hashmap_header_head(x) ((MAP_HEADER_TYPE(x) & (0x2)))
 
 #define MAKE_MAP_HEADER(Type,Arity,Val) \
-    (_make_header(((((Uint16)Val) << MAP_HEADER_ARITY_SZ) | (Arity)) << MAP_HEADER_TAG_SZ | (Type) , _TAG_HEADER_HASHMAP))
+    (_make_header(((((Uint16)(Val)) << MAP_HEADER_ARITY_SZ) | (Arity)) << MAP_HEADER_TAG_SZ | (Type) , _TAG_HEADER_HASHMAP))
 
 #define MAP_HEADER_HAMT_HEAD_ARRAY \
     MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_HEAD_ARRAY,0x1,0xffff)
-- 
cgit v1.2.3


From 4007301c3cbd9e86a42cb122692736f493d2d3f9 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Wed, 21 Jan 2015 18:09:05 +0100
Subject: erts: Refactor hashmap_do_foreach

to use a common struct hashmap_doer_state.
---
 erts/emulator/beam/erl_hashmap.c | 36 +++++++++++++++++-------------------
 1 file changed, 17 insertions(+), 19 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index bdcb479c30..5a18ec3e9b 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -73,6 +73,13 @@ static Eterm hashmap_values(Process *p, Eterm map);
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 
+typedef struct {
+    Eterm* hp;
+    Eterm res;
+}hashmap_doer_state;
+typedef void hashmap_doer(Eterm*, hashmap_doer_state*);
+static void hashmap_do_foreach(Eterm node, hashmap_doer* fptr, hashmap_doer_state*);
+
 /* hashmap:new/0 */
 
 BIF_RETTYPE hashmap_new_0(BIF_ALIST_0) {
@@ -1061,9 +1068,8 @@ recurse:
     return res;
 }
 
-typedef void hashmap_doer(Eterm*, void*);
-
-static void hashmap_do_foreach(Eterm node, hashmap_doer* fptr, void* farg) {
+static void hashmap_do_foreach(Eterm node, hashmap_doer* fptr,
+			       hashmap_doer_state* farg) {
     Eterm *ptr, hdr;
     Uint sz;
     DECLARE_ESTACK(stack);
@@ -1106,46 +1112,38 @@ static void hashmap_do_foreach(Eterm node, hashmap_doer* fptr, void* farg) {
     DESTROY_ESTACK(stack);
 }
 
-typedef struct {
-    Eterm* hp;
-    Eterm res;
-}hashmap_keys_state;
-static void hashmap_keys_doer(Eterm* kv, hashmap_keys_state*);
+static void hashmap_keys_doer(Eterm* kv, hashmap_doer_state*);
 
 static Eterm hashmap_keys(Process* p, Eterm node) {
     hashmap_head_t* root;
-    hashmap_keys_state state;
+    hashmap_doer_state state;
 
     root = (hashmap_head_t*) boxed_val(node);
     state.hp  = HAlloc(p, root->size * 2);
     state.res = NIL;
-    hashmap_do_foreach(node, (hashmap_doer*)hashmap_keys_doer, &state);
+    hashmap_do_foreach(node, hashmap_keys_doer, &state);
     return state.res;
 }
 
-static void hashmap_keys_doer(Eterm* kv, hashmap_keys_state* statep) {
+static void hashmap_keys_doer(Eterm* kv, hashmap_doer_state* statep) {
     statep->res = CONS(statep->hp, CAR(kv), statep->res);
     statep->hp += 2;
 }
 
-typedef struct {
-    Eterm* hp;
-    Eterm res;
-}hashmap_values_state;
-static void hashmap_values_doer(Eterm* kv, hashmap_values_state*);
+static void hashmap_values_doer(Eterm* kv, hashmap_doer_state*);
 
 static Eterm hashmap_values(Process* p, Eterm node) {
     hashmap_head_t* root;
-    hashmap_values_state state;
+    hashmap_doer_state state;
 
     root = (hashmap_head_t*) boxed_val(node);
     state.hp  = HAlloc(p, root->size * 2);
     state.res = NIL;
-    hashmap_do_foreach(node, (hashmap_doer*)hashmap_values_doer, &state);
+    hashmap_do_foreach(node, hashmap_values_doer, &state);
     return state.res;
 }
 
-static void hashmap_values_doer(Eterm* kv, hashmap_values_state* statep) {
+static void hashmap_values_doer(Eterm* kv, hashmap_doer_state* statep) {
     statep->res = CONS(statep->hp, CDR(kv), statep->res);
     statep->hp += 2;
 }
-- 
cgit v1.2.3


From b0b6eff0bebd25ba371a027b824426b87bd2a1d6 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Wed, 21 Jan 2015 18:09:52 +0100
Subject: erts: Refactor hashmap_to_list to use hashmap_do_foreach

---
 erts/emulator/beam/erl_hashmap.c | 61 ++++++++++------------------------------
 1 file changed, 15 insertions(+), 46 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 5a18ec3e9b..8e651dd776 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -748,55 +748,24 @@ not_found:
     return res;
 }
 
+static void hashmap_to_list_doer(Eterm* kv, hashmap_doer_state* sp);
+
 static Eterm hashmap_to_list(Process *p, Eterm node) {
-    Eterm *hp;
-    Eterm res = NIL;
-    Eterm *ptr, tup, hdr;
-    Uint sz, n;
-    DECLARE_ESTACK(stack);
+    hashmap_head_t* root;
+    hashmap_doer_state state;
 
-    ptr = boxed_val(node);
-    n   = (Uint)ptr[1];
-    hp  = HAlloc(p, n * (2 + 3));
-    ESTACK_PUSH(stack, node);
-    do {
-	node = ESTACK_POP(stack);
-	switch(primary_tag(node)) {
-	    case TAG_PRIMARY_LIST:
-		ptr = list_val(node);
-		tup = TUPLE2(hp, CAR(ptr), CDR(ptr)); hp += 3;
-		res = CONS(hp, tup, res); hp += 2;
-		break;
-	    case TAG_PRIMARY_BOXED:
-		ptr = boxed_val(node);
-		hdr = *ptr;
-		ASSERT(is_header(hdr));
-		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-		    case HAMT_SUBTAG_HEAD_ARRAY:
-			ptr++;
-		    case HAMT_SUBTAG_NODE_ARRAY:
-			ptr++;
-			sz = 16;
-			while(sz--) { ESTACK_PUSH(stack, ptr[sz]); }
-			break;
-		    case HAMT_SUBTAG_HEAD_BITMAP:
-			ptr++;
-		    case HAMT_SUBTAG_NODE_BITMAP:
-			sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
-			ASSERT(sz < 17);
-			ptr++;
-			while(sz--) { ESTACK_PUSH(stack, ptr[sz]); }
-			break;
-		    default:
-			erl_exit(1, "bad header\r\n");
-			break;
-		}
-	}
-    } while(!ESTACK_ISEMPTY(stack));
+    root = (hashmap_head_t*) boxed_val(node);
+    state.hp  = HAlloc(p, root->size * (2 + 3));
+    state.res = NIL;
+    hashmap_do_foreach(node, hashmap_to_list_doer, &state);
+    return state.res;
+}
 
-    DESTROY_ESTACK(stack);
-    ERTS_HOLE_CHECK(p);
-    return res;
+static void hashmap_to_list_doer(Eterm* kv, hashmap_doer_state* sp) {
+    Eterm tup = TUPLE2(sp->hp, CAR(kv), CDR(kv));
+    sp->hp += 3;
+    sp->res = CONS(sp->hp, tup, sp->res);
+    sp->hp += 2;
 }
 
 #define HALLOC_EXTRA 200
-- 
cgit v1.2.3


From a7a0bb29fd162cdfc6cdf6eb18cb5e2cf22a430e Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Wed, 21 Jan 2015 18:22:58 +0100
Subject: erts: Add hashmap:find/2

---
 erts/emulator/beam/bif.tab       |  1 +
 erts/emulator/beam/erl_hashmap.c | 22 ++++++++++++++++++++++
 2 files changed, 23 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 6408883e96..c0fb1f8827 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -617,6 +617,7 @@ bif erlang:get_keys/0
 # Hash Array Mappped Trie
 bif hashmap:put/3
 bif hashmap:get/2
+bif hashmap:find/2
 bif hashmap:remove/2
 bif hashmap:info/1
 bif hashmap:to_list/1
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 8e651dd776..a5d6daadd7 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -133,6 +133,28 @@ BIF_RETTYPE hashmap_get_2(BIF_ALIST_2) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
+/* hashmap:find/2 */
+
+BIF_RETTYPE hashmap_find_2(BIF_ALIST_2) {
+    if (is_hashmap(BIF_ARG_2)) {
+	Eterm *hp, res;
+	const Eterm *value;
+	Uint32 hx = make_hash2(BIF_ARG_1);
+
+	if ((value = hashmap_get(hx, BIF_ARG_1, BIF_ARG_2)) != NULL) {
+	    hp    = HAlloc(BIF_P, 3);
+	    res   = make_tuple(hp);
+	    *hp++ = make_arityval(2);
+	    *hp++ = am_ok;
+            *hp++ = *value;
+	    BIF_RET(res);
+	}
+	BIF_RET(am_error);
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
+
 /* hashmap:remove/2 */
 
 BIF_RETTYPE hashmap_remove_2(BIF_ALIST_2) {
-- 
cgit v1.2.3


From a97632afab52befd2e48f963f1c5e178e3871995 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Wed, 21 Jan 2015 18:48:57 +0100
Subject: erts: Add hashmap:update/3

---
 erts/emulator/beam/bif.tab       |  1 +
 erts/emulator/beam/erl_hashmap.c | 34 +++++++++++++++++++++++++++++++---
 2 files changed, 32 insertions(+), 3 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index c0fb1f8827..27ae8adcec 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -618,6 +618,7 @@ bif erlang:get_keys/0
 bif hashmap:put/3
 bif hashmap:get/2
 bif hashmap:find/2
+bif hashmap:update/3
 bif hashmap:remove/2
 bif hashmap:info/1
 bif hashmap:to_list/1
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index a5d6daadd7..9445a7db9a 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -64,7 +64,7 @@ static char *format_binary(Uint64 x, char *b) {
 }
 #endif
 
-static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node);
+static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node, int is_update);
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_to_list(Process *p, Eterm map);
@@ -102,13 +102,27 @@ BIF_RETTYPE hashmap_put_3(BIF_ALIST_3) {
 	Uint32 hx = make_hash2(BIF_ARG_1);
 	Eterm map;
 
-	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 0);
 	ASSERT(is_hashmap(map));
 	BIF_RET(map);
     }
     BIF_ERROR(BIF_P, BADARG);
 }
 
+/* hashmap:update/3 */
+
+BIF_RETTYPE hashmap_update_3(BIF_ALIST_3) {
+    if (is_hashmap(BIF_ARG_3)) {
+	Uint32 hx = make_hash2(BIF_ARG_1);
+	Eterm map;
+
+	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 1);
+	if (is_value(map))
+	    BIF_RET(map);
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
 /* hashmap:to_list/1 */
 
 BIF_RETTYPE hashmap_to_list_1(BIF_ALIST_1) {
@@ -303,7 +317,8 @@ static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
     return NULL;
 }
 
-static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node) {
+static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
+			    Eterm node, int is_update) {
     Eterm *hp = NULL, *nhp = NULL;
     Eterm *ptr;
     Eterm hdr,res,ckey,fake;
@@ -322,6 +337,10 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm
 		    update_size = 0;
 		    goto unroll;
 		}
+		if (is_update) {
+		    res = THE_NON_VALUE;
+		    goto bail_out;
+		}
 		goto insert_subnodes;
 	    case TAG_PRIMARY_BOXED:
 		ptr = boxed_val(node);
@@ -362,6 +381,10 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm
 			    break;
 			}
 			/* not occupied */
+			if (is_update) {
+			    res = THE_NON_VALUE;
+			    goto bail_out;
+			}
 			size += HAMT_NODE_BITMAP_SZ(n+1);
 			goto unroll;
 		    case HAMT_SUBTAG_HEAD_BITMAP:
@@ -383,6 +406,10 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm
 			    break;
 			}
 			/* not occupied */
+			if (is_update) {
+			    res = THE_NON_VALUE;
+			    goto bail_out;
+			}
 			size += HAMT_HEAD_BITMAP_SZ(n+1);
 			goto unroll;
 		    default:
@@ -515,6 +542,7 @@ unroll:
 
     } while(!ESTACK_ISEMPTY(stack));
 
+bail_out:
     DESTROY_ESTACK(stack);
     ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
     ERTS_HOLE_CHECK(p);
-- 
cgit v1.2.3


From aefea82d97f984c615990729a0f7d4303741f233 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Fri, 23 Jan 2015 12:36:01 +0100
Subject: erts: Add RESERVE and FAST_PUSH for ESTACK & WSTACK

---
 erts/emulator/beam/global.h | 48 +++++++++++++++++++++++++++++++++++----------
 erts/emulator/beam/utils.c  | 20 +++++++++++++++----
 2 files changed, 54 insertions(+), 14 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 1fb069232a..dcba8bbba8 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -372,7 +372,7 @@ extern int stackdump_on_exit;
  * DESTROY_ESTACK(Stack)
  */
 
-typedef struct {
+typedef struct ErtsEStack_ {
     Eterm* start;
     Eterm* sp;
     Eterm* end;
@@ -381,7 +381,7 @@ typedef struct {
 
 #define DEF_ESTACK_SIZE (16)
 
-void erl_grow_estack(ErtsEStack*, Eterm* def_stack);
+void erl_grow_estack(ErtsEStack*, Eterm* def_stack, Uint need);
 #define ESTK_CONCAT(a,b) a##b
 #define ESTK_DEF_STACK(s) ESTK_CONCAT(s,_default_estack)
 
@@ -457,7 +457,7 @@ do {						\
 #define ESTACK_PUSH(s, x)				\
 do {							\
     if (s.sp == s.end) {				\
-	erl_grow_estack(&s, ESTK_DEF_STACK(s)); 	\
+	erl_grow_estack(&s, ESTK_DEF_STACK(s), 1); 	\
     }							\
     *s.sp++ = (x);					\
 } while(0)
@@ -465,7 +465,7 @@ do {							\
 #define ESTACK_PUSH2(s, x, y)			\
 do {						\
     if (s.sp > s.end - 2) {			\
-	erl_grow_estack(&s, ESTK_DEF_STACK(s)); \
+	erl_grow_estack(&s, ESTK_DEF_STACK(s), 2); \
     }						\
     *s.sp++ = (x);				\
     *s.sp++ = (y);				\
@@ -474,7 +474,7 @@ do {						\
 #define ESTACK_PUSH3(s, x, y, z)		\
 do {						\
     if (s.sp > s.end - 3) {			\
-	erl_grow_estack(&s, ESTK_DEF_STACK(s)); \
+	erl_grow_estack(&s, ESTK_DEF_STACK(s), 3); \
     }						\
     *s.sp++ = (x);				\
     *s.sp++ = (y);				\
@@ -492,6 +492,20 @@ do {						\
     *s.sp++ = (E4);				\
 } while(0)
 
+#define ESTACK_RESERVE(s, push_cnt)                         \
+do {						            \
+    if (s.sp > s.end - (push_cnt)) {	                    \
+	erl_grow_estack(&s, ESTK_DEF_STACK(s), (push_cnt)); \
+    }						            \
+} while(0)
+
+/* Must be preceded by ESTACK_RESERVE */
+#define ESTACK_FAST_PUSH(s, x)				\
+do {							\
+    ASSERT(s.sp < s.end);                               \
+    *s.sp++ = (x);					\
+} while(0)
+
 #define ESTACK_COUNT(s) (s.sp - s.start)
 #define ESTACK_ISEMPTY(s) (s.sp == s.start)
 #define ESTACK_POP(s) (*(--s.sp))
@@ -501,7 +515,7 @@ do {						\
  * WSTACK: same as ESTACK but with UWord instead of Eterm
  */
 
-typedef struct {
+typedef struct ErtsWStack_ {
     UWord* wstart;
     UWord* wsp;
     UWord* wend;
@@ -510,7 +524,7 @@ typedef struct {
 
 #define DEF_WSTACK_SIZE (16)
 
-void erl_grow_wstack(ErtsWStack*, UWord* def_stack);
+void erl_grow_wstack(ErtsWStack*, UWord* def_stack, Uint need);
 #define WSTK_CONCAT(a,b) a##b
 #define WSTK_DEF_STACK(s) WSTK_CONCAT(s,_default_wstack)
 
@@ -592,7 +606,7 @@ do {						\
 #define WSTACK_PUSH(s, x)				\
 do {							\
     if (s.wsp == s.wend) {				\
-	erl_grow_wstack(&s, WSTK_DEF_STACK(s)); 	\
+	erl_grow_wstack(&s, WSTK_DEF_STACK(s), 1); 	\
     }							\
     *s.wsp++ = (x);					\
 } while(0)
@@ -600,7 +614,7 @@ do {							\
 #define WSTACK_PUSH2(s, x, y)			\
 do {						\
     if (s.wsp > s.wend - 2) {			\
-	erl_grow_wstack(&s, WSTK_DEF_STACK(s)); \
+	erl_grow_wstack(&s, WSTK_DEF_STACK(s), 2); \
     }						\
     *s.wsp++ = (x);				\
     *s.wsp++ = (y);				\
@@ -609,7 +623,7 @@ do {						\
 #define WSTACK_PUSH3(s, x, y, z)		\
 do {						\
     if (s.wsp > s.wend - 3) {	\
-	erl_grow_wstack(&s, WSTK_DEF_STACK(s)); \
+	erl_grow_wstack(&s, WSTK_DEF_STACK(s), 3); \
     }						\
     *s.wsp++ = (x);				\
     *s.wsp++ = (y);				\
@@ -652,6 +666,20 @@ do {						\
     *s.wsp++ = (A6);				\
 } while(0)
 
+#define WSTACK_RESERVE(s, push_cnt)             \
+do {						\
+    if (s.wsp > s.wend - (push_cnt)) { 	        \
+	erl_grow_wstack(&s, WSTK_DEF_STACK(s), (push_cnt)); \
+    }                                           \
+} while(0)
+
+/* Must be preceded by WSTACK_RESERVE */
+#define WSTACK_FAST_PUSH(s, x)                  \
+do {						\
+    ASSERT(s.wsp < s.wend);                     \
+    *s.wsp++ = (x);                             \
+} while(0)
+
 #define WSTACK_COUNT(s) (s.wsp - s.wstart)
 #define WSTACK_ISEMPTY(s) (s.wsp == s.wstart)
 #define WSTACK_POP(s) ((ASSERT(s.wsp > s.wstart)),*(--s.wsp))
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index cea20a6002..bd90ec2fba 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -190,11 +190,17 @@ erts_set_hole_marker(Eterm* ptr, Uint sz)
  * Helper function for the ESTACK macros defined in global.h.
  */
 void
-erl_grow_estack(ErtsEStack* s, Eterm* default_estack)
+erl_grow_estack(ErtsEStack* s, Eterm* default_estack, Uint need)
 {
     Uint old_size = (s->end - s->start);
-    Uint new_size = old_size * 2;
+    Uint new_size;
     Uint sp_offs = s->sp - s->start;
+
+    if (need < old_size)
+	new_size = 2*old_size;
+    else
+	new_size = ((need / old_size) + 2) * old_size;
+
     if (s->start != default_estack) {
 	s->start = erts_realloc(s->alloc_type, s->start,
 				new_size*sizeof(Eterm));
@@ -210,11 +216,17 @@ erl_grow_estack(ErtsEStack* s, Eterm* default_estack)
  * Helper function for the WSTACK macros defined in global.h.
  */
 void
-erl_grow_wstack(ErtsWStack* s, UWord* default_wstack)
+erl_grow_wstack(ErtsWStack* s, UWord* default_wstack, Uint need)
 {
     Uint old_size = (s->wend - s->wstart);
-    Uint new_size = old_size * 2;
+    Uint new_size;
     Uint sp_offs = s->wsp - s->wstart;
+
+    if (need < old_size)
+	new_size = 2 * old_size;
+    else
+	new_size = ((need / old_size) + 2) * old_size;
+
     if (s->wstart != default_wstack) {
 	s->wstart = erts_realloc(s->alloc_type, s->wstart,
 				 new_size*sizeof(UWord));
-- 
cgit v1.2.3


From e5d514d5adb230bbc832db5c6cf344a69a8df3bf Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Fri, 23 Jan 2015 17:50:57 +0100
Subject: erts: Add term_to_binary for hashmap

---
 erts/emulator/beam/external.c | 80 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 79 insertions(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index e5fb2d3ec1..af8db4c265 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -2304,7 +2304,8 @@ dec_pid(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, Ete
 #define ENC_PATCH_FUN_SIZE ((Eterm) 2)
 #define ENC_BIN_COPY ((Eterm) 3)
 #define ENC_MAP_PAIR ((Eterm) 4)
-#define ENC_LAST_ARRAY_ELEMENT ((Eterm) 5)
+#define ENC_HASHMAP_NODE ((Eterm) 5)
+#define ENC_LAST_ARRAY_ELEMENT ((Eterm) 6)
 
 static byte*
 enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
@@ -2413,6 +2414,13 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
 	    WSTACK_PUSH2(s, ENC_TERM, *vptr);
 	    break;
 	}
+	case ENC_HASHMAP_NODE:
+	    if (is_list(obj)) { /* leaf node [K|V] */
+		ptr = list_val(obj);
+		WSTACK_PUSH2(s, ENC_TERM, CDR(ptr));
+		obj = CAR(ptr);
+	    }
+	    break;
 	case ENC_LAST_ARRAY_ELEMENT:
 	    /* obj is the tuple */
 	    {
@@ -2611,6 +2619,44 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
 	    }
 	    break;
 
+	case HASHMAP_DEF:
+	    {
+		Eterm hdr;
+		Uint node_sz;
+		ptr = boxed_val(obj);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		case HAMT_SUBTAG_HEAD_ARRAY:
+		    *ep++ = MAP_EXT;
+		    ptr++;
+		    put_int32(*ptr, ep); ep += 4;
+		    /*fall through*/
+		case HAMT_SUBTAG_NODE_ARRAY:
+		    node_sz = 16;
+		    break;
+		case HAMT_SUBTAG_HEAD_BITMAP:
+		    *ep++ = MAP_EXT;
+		    ptr++;
+		    put_int32(*ptr, ep); ep += 4;
+		    /*fall through*/
+		case HAMT_SUBTAG_NODE_BITMAP:
+		    node_sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+		    ASSERT(node_sz < 17);
+		    break;
+		default:
+		    erl_exit(1, "bad header\r\n");
+		}
+
+		ptr++;
+		WSTACK_RESERVE(s, node_sz*2);
+		while(node_sz--) {
+		    WSTACK_FAST_PUSH(s, ENC_HASHMAP_NODE);
+		    WSTACK_FAST_PUSH(s, *ptr++);
+		}
+	    }
+	    break;
+
 	case FLOAT_DEF:
 	    GET_DOUBLE(obj, f);
 	    if (dflags & DFLAG_NEW_FLOATS) {
@@ -4047,6 +4093,38 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
 		goto outer_loop;
 	    }
 	    break;
+
+	case HASHMAP_DEF:
+	    {
+		Eterm *ptr;
+		Eterm hdr;
+		Uint node_sz;
+		ptr = boxed_val(obj);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		case HAMT_SUBTAG_HEAD_ARRAY: ptr++;
+		case HAMT_SUBTAG_NODE_ARRAY:
+		    node_sz = 16;
+		    break;
+		case HAMT_SUBTAG_HEAD_BITMAP: ptr++;
+		case HAMT_SUBTAG_NODE_BITMAP:
+		    node_sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+		    ASSERT(node_sz < 17);
+		    break;
+		default:
+		    erl_exit(1, "bad header\r\n");
+		}
+
+		ptr++;
+		ESTACK_RESERVE(s, node_sz);
+		while(node_sz--) {
+		    ESTACK_FAST_PUSH(s, *ptr++);
+		}
+		result += 1 + 4; /* tag + 4 bytes size */
+	    }
+
+	    break;
 	case FLOAT_DEF:
 	    if (dflags & DFLAG_NEW_FLOATS) {
 		result += 9;
-- 
cgit v1.2.3


From b38518ff23477322da221212f29d5ad0d1fb96fa Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Fri, 23 Jan 2015 20:02:27 +0100
Subject: erts: Cleanup hashmap_merge_2

---
 erts/emulator/beam/erl_hashmap.c | 212 ++++++++++++++-------------------------
 1 file changed, 78 insertions(+), 134 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 9445a7db9a..1697449234 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -820,34 +820,23 @@ static void hashmap_to_list_doer(Eterm* kv, hashmap_doer_state* sp) {
 
 #define HALLOC_EXTRA 200
 
-#if defined(VALGRIND) || defined(GO_SUPER_FAST)
-# define UNDEF(V,I)
-#else
-# define UNDEF(V,I) V=I
-#endif
-
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
     struct {
-	Eterm *ptrA, *ptrB;
-	Uint32 abm, bbm, rbm;
-	Uint ix;
+	Eterm *srcA, *srcB, *dst;
+	Uint32 abm, bbm, rbm;        /* node bitmaps */
 	int keepA;
 	Eterm array[16];
-    }stack[16];
+    }stack[16], *sp = stack;
     Eterm *hp, *nhp;
-    Eterm *ptrA, *ptrB;
     Eterm hdrA, hdrB;
     Eterm th[2];
-    Uint32 ahx, bhx, abm, bbm, rbm, bit;
-    Uint ix;
-    Uint size;
+    Uint32 ahx, bhx;
+    Uint size;  /* total key-value counter */
     int keepA = 0;
     unsigned lvl = 0;
+    unsigned node_sz;
     Eterm res = THE_NON_VALUE;
 
-    UNDEF(abm,0);
-    UNDEF(bbm,0);
-
     /*
      * Strategy: Do depth-first traversal of both trees (at the same time)
      * and merge each pair of nodes.
@@ -872,50 +861,46 @@ recurse:
 
     switch (primary_tag(nodeA)) {
     case TAG_PRIMARY_LIST: {
-	ptrA = list_val(nodeA);
+	sp->srcA = list_val(nodeA);
 	switch (primary_tag(nodeB)) {
 	case TAG_PRIMARY_LIST: { /* LEAF + LEAF */
-	    ptrB = list_val(nodeB);
+	    sp->srcB = list_val(nodeB);
 
-	    if (EQ(CAR(ptrA), CAR(ptrB))) {
+	    if (EQ(CAR(sp->srcA), CAR(sp->srcB))) {
 		--size;
 		res = keepA ? nodeA : nodeB;
 	    } else {
-		ahx = hashmap_restore_hash(th, lvl, CAR(ptrA));
-		bhx = hashmap_restore_hash(th, lvl, CAR(ptrB));
-		abm = 1 << hashmap_index(ahx);
-		bbm = 1 << hashmap_index(bhx);
+		ahx = hashmap_restore_hash(th, lvl, CAR(sp->srcA));
+		bhx = hashmap_restore_hash(th, lvl, CAR(sp->srcB));
+		sp->abm = 1 << hashmap_index(ahx);
+		sp->bbm = 1 << hashmap_index(bhx);
 
-		ptrA = &nodeA;
-		ptrB = &nodeB;
+		sp->srcA = &nodeA;
+		sp->srcB = &nodeB;
 	    }
 	    break;
 	}
 	case TAG_PRIMARY_BOXED: { /* LEAF + NODE */
-	    ptrB = boxed_val(nodeB);
-	    ASSERT(is_header(*ptrB));
-	    hdrB = *ptrB++;
-
-	    ahx = hashmap_restore_hash(th, lvl, CAR(ptrA));
-	    abm = 1 << hashmap_index(ahx);
+	    sp->srcB = boxed_val(nodeB);
+	    ASSERT(is_header(*sp->srcB));
+	    hdrB = *sp->srcB++;
 
+	    ahx = hashmap_restore_hash(th, lvl, CAR(sp->srcA));
+	    sp->abm = 1 << hashmap_index(ahx);
+	    sp->srcA = &nodeA;
 	    switch(hdrB & _HEADER_MAP_SUBTAG_MASK) {
-	    case HAMT_SUBTAG_HEAD_ARRAY:
-		ptrB++;
+	    case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++;
 	    case HAMT_SUBTAG_NODE_ARRAY:
-		bbm = 0xffff;
-		ptrA = &nodeA;
+		sp->bbm = 0xffff;
 		break;
 
-	    case HAMT_SUBTAG_HEAD_BITMAP:
-		ptrB++;
+	    case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++;
 	    case HAMT_SUBTAG_NODE_BITMAP:
-		bbm = MAP_HEADER_VAL(hdrB);
-		ptrA = &nodeA;
+		sp->bbm = MAP_HEADER_VAL(hdrB);
 		break;
 
 	    default:
-		erl_exit(1, "bad header tag %ld\r\n", *ptrB & _HEADER_MAP_SUBTAG_MASK);
+		erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK);
 		break;
 	    }
 	    break;
@@ -926,83 +911,50 @@ recurse:
 	break;
     }
     case TAG_PRIMARY_BOXED: { /* NODE + NODE */
-	ptrA = boxed_val(nodeA);
-	hdrA = *ptrA;
+	sp->srcA = boxed_val(nodeA);
+	hdrA = *sp->srcA++;
 	ASSERT(is_header(hdrA));
-	ptrA++;
 	switch (hdrA & _HEADER_MAP_SUBTAG_MASK) {
-	case HAMT_SUBTAG_HEAD_ARRAY:
-	    ptrA++;
+	case HAMT_SUBTAG_HEAD_ARRAY: sp->srcA++;
 	case HAMT_SUBTAG_NODE_ARRAY: {
 	    ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED);
-	    ptrB = boxed_val(nodeB);
-	    hdrB = *ptrB;
+	    sp->abm = 0xffff;
+	    sp->srcB = boxed_val(nodeB);
+	    hdrB = *sp->srcB++;
 	    ASSERT(is_header(hdrB));
 	    switch (hdrB & _HEADER_MAP_SUBTAG_MASK) {
-	    case HAMT_SUBTAG_HEAD_ARRAY:
-		ASSERT(lvl == 0);
-		abm = 0xffff;
-		bbm = 0xffff;
-		ptrB += 2;
-		break;
+	    case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++;
 	    case HAMT_SUBTAG_NODE_ARRAY:
-		ASSERT(lvl > 0);
-		abm = 0xffff;
-		bbm = 0xffff;
-		ptrB++;
-		break;
-	    case HAMT_SUBTAG_HEAD_BITMAP:
-		ASSERT(lvl == 0);
-		abm = 0xffff;
-		bbm = MAP_HEADER_VAL(hdrB);
-		ptrB += 2;
+		sp->bbm = 0xffff;
 		break;
+	    case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++;
 	    case HAMT_SUBTAG_NODE_BITMAP:
-		ASSERT(lvl > 0);
-		abm = 0xffff;
-		bbm = MAP_HEADER_VAL(hdrB);
-		ptrB += 1;
+		sp->bbm = MAP_HEADER_VAL(hdrB);
 		break;
 	    default:
-		erl_exit(1, "bad header tag %ld\r\n", *ptrB & _HEADER_MAP_SUBTAG_MASK);
+		erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK);
 	    }
 	    break;
 	}
-	case HAMT_SUBTAG_HEAD_BITMAP:
-	    ptrA++;
+	case HAMT_SUBTAG_HEAD_BITMAP: sp->srcA++;
 	case HAMT_SUBTAG_NODE_BITMAP: {
 	    ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED);
-	    abm = MAP_HEADER_VAL(hdrA);
-	    ptrB = boxed_val(nodeB);
-	    hdrB = *ptrB;
+	    sp->abm = MAP_HEADER_VAL(hdrA);
+	    sp->srcB = boxed_val(nodeB);
+	    hdrB = *sp->srcB++;
 	    ASSERT(is_header(hdrB));
 	    switch (hdrB & _HEADER_MAP_SUBTAG_MASK) {
-	    case HAMT_SUBTAG_HEAD_ARRAY:
-		ASSERT(lvl == 0);
-		bbm = 0xffff;
-		ptrB += 2;
-		break;
-
+	    case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++;
 	    case HAMT_SUBTAG_NODE_ARRAY:
-		ASSERT(lvl > 0);
-		bbm = 0xffff;
-		ptrB++;
+		sp->bbm = 0xffff;
 		break;
-
-	    case HAMT_SUBTAG_HEAD_BITMAP:
-		ASSERT(lvl == 0);
-		bbm = MAP_HEADER_VAL(hdrB);
-		ptrB += 2;
-		break;
-
+	    case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++;
 	    case HAMT_SUBTAG_NODE_BITMAP:
-		ASSERT(lvl > 0);
-		bbm = MAP_HEADER_VAL(hdrB);
-		ptrB += 1;
+		sp->bbm = MAP_HEADER_VAL(hdrB);
 		break;
 
 	    default:
-		erl_exit(1, "bad header tag %ld\r\n", *ptrB & _HEADER_MAP_SUBTAG_MASK);
+		erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK);
 	    }
 	    break;
 	}
@@ -1022,69 +974,61 @@ recurse:
 
 	    /* Pop from stack and continue build parent node */
 	    lvl--;
-	    abm = stack[lvl].abm;
-	    bbm = stack[lvl].bbm;
-	    rbm = stack[lvl].rbm;
-	    ix  = stack[lvl].ix;
-	    stack[lvl].array[ix++] = res;
+	    sp--;
+	    *sp->dst++ = res;
 	    res = THE_NON_VALUE;
-	    if (rbm) {
-		ptrA = stack[lvl].ptrA;
-		ptrB = stack[lvl].ptrB;
-		keepA = stack[lvl].keepA;
+	    if (sp->rbm) {
+		sp->srcA++;
+		sp->srcB++;
+		keepA = sp->keepA;
 	    }
 	} else { /* Start build a node */
-	    ix = 0;
-	    rbm = abm | bbm;
-	    ASSERT(!(rbm == 0 && lvl > 0));
+	    sp->dst = sp->array;
+	    sp->rbm = sp->abm | sp->bbm;
+	    ASSERT(!(sp->rbm == 0 && lvl > 0));
 	}
 
-	while (rbm) {
-	    Uint32 next = rbm & (rbm-1);
-	    bit = rbm ^ next;
-	    rbm = next;
-	    if (abm & bit) {
-		if (bbm & bit) {
+	while (sp->rbm) {
+	    Uint32 next = sp->rbm & (sp->rbm-1);
+	    Uint32 bit = sp->rbm ^ next;
+	    sp->rbm = next;
+	    if (sp->abm & bit) {
+		if (sp->bbm & bit) {
 		    /* Bit clash. Push and resolve by recursive merge */
-		    if (rbm) {
-			stack[lvl].ptrA = ptrA + 1;
-			stack[lvl].ptrB = ptrB + 1;
-			stack[lvl].keepA = keepA;
+		    if (sp->rbm) {
+			sp->keepA = keepA;
 		    }
-		    stack[lvl].abm = abm;
-		    stack[lvl].bbm = bbm;
-		    stack[lvl].rbm = rbm;
-		    stack[lvl].ix  = ix;
-		    nodeA = *ptrA;
-		    nodeB = *ptrB;
+		    nodeA = *sp->srcA;
+		    nodeB = *sp->srcB;
 		    lvl++;
+		    sp++;
 		    goto recurse;
 		} else {
-		    stack[lvl].array[ix++] = *ptrA++;
+		    *sp->dst++ = *sp->srcA++;
 		}
 	    } else {
-		ASSERT(bbm & bit);
-		stack[lvl].array[ix++] = *ptrB++;
+		ASSERT(sp->bbm & bit);
+		*sp->dst++ = *sp->srcB++;
 	    }
 	}
 
+	node_sz = sp->dst - sp->array;
+	ASSERT(node_sz == hashmap_bitcount(sp->abm | sp->bbm));
 	if (lvl == 0) {
-	    nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(ix), HALLOC_EXTRA);
+	    nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(node_sz), HALLOC_EXTRA);
 	    hp = nhp;
-	    *hp++ = (ix == 16) ? MAP_HEADER_HAMT_HEAD_ARRAY
-		: MAP_HEADER_HAMT_HEAD_BITMAP(abm | bbm);
+	    *hp++ = (node_sz == 16 ? MAP_HEADER_HAMT_HEAD_ARRAY
+		     : MAP_HEADER_HAMT_HEAD_BITMAP(sp->abm | sp->bbm));
 	    *hp++ = size;
 	} else {
-	    nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(ix), HALLOC_EXTRA);
+	    nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(node_sz), HALLOC_EXTRA);
 	    hp = nhp;
-	    *hp++ = (ix == 16) ? make_arityval(16)
-				   : MAP_HEADER_HAMT_NODE_BITMAP(abm | bbm);
+	    *hp++ = (node_sz == 16 ? make_arityval(16)
+		     : MAP_HEADER_HAMT_NODE_BITMAP(sp->abm | sp->bbm));
 	}
-	memcpy(hp, stack[lvl].array, ix * sizeof(Eterm));
+	memcpy(hp, sp->array, node_sz * sizeof(Eterm));
 	res = make_boxed(nhp);
     }
-
-    return res;
 }
 
 static void hashmap_do_foreach(Eterm node, hashmap_doer* fptr,
-- 
cgit v1.2.3


From b530d8b95055c27c737140c5ec2047919d02aeed Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Mon, 26 Jan 2015 17:58:30 +0100
Subject: erts: Add PSTACK

A lightweight stack that can be used to store any type.
PUSH and POP return pointers into stack slots.
---
 erts/emulator/beam/global.h | 61 +++++++++++++++++++++++++++++++++++++++++++++
 erts/emulator/beam/utils.c  | 26 +++++++++++++++++++
 2 files changed, 87 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index dcba8bbba8..09f43348f4 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -685,6 +685,67 @@ do {						\
 #define WSTACK_POP(s) ((ASSERT(s.wsp > s.wstart)),*(--s.wsp))
 
 
+
+/* PSTACK - Stack of any type.
+ * Usage:
+ * {
+ * #define PSTACK_TYPE MyType
+ *    PSTACK_DECLARE(s,16);
+ *    MyType *sp = PSTACK_PUSH(s);
+ *
+ *    sp->x = ....
+ *    sp->y = ....
+ *    sp = PSTACK_PUSH(s);
+ *    ...
+ *    sp = PSTACK_POP(s);
+ *    if (PSTACK_IS_EMPTY(s)) {
+ *        // sp is invalid when stack is empty after pop
+ *    }
+ *
+ *    PSTACK_DESTROY(s);
+ * }
+ */
+
+
+typedef struct ErtsPStack_ {
+    byte* pstart;
+    byte* psp;
+    byte* pend;
+    ErtsAlcType_t alloc_type;
+}ErtsPStack;
+
+void erl_grow_pstack(ErtsPStack* s, void* default_pstack, unsigned need_bytes);
+#define PSTK_CONCAT(a,b) a##b
+#define PSTK_DEF_STACK(s) PSTK_CONCAT(s,_default_pstack)
+
+#define PSTACK_DECLARE(s, DEF_PSTACK_SIZE) \
+PSTACK_TYPE PSTK_DEF_STACK(s)[DEF_PSTACK_SIZE];                            \
+ErtsPStack s = { (byte*)PSTK_DEF_STACK(s), /* pstart */                    \
+                 (byte*)(PSTK_DEF_STACK(s) - 1), /* psp */                 \
+                 (byte*)(PSTK_DEF_STACK(s) + (DEF_PSTACK_SIZE)), /* pend */\
+                 ERTS_ALC_T_ESTACK   /* alloc_type */                      \
+}
+
+#define PSTACK_DESTROY(s)				\
+do {							\
+    if (s.pstart != (byte*)PSTK_DEF_STACK(s)) {		\
+	erts_free(s.alloc_type, s.pstart); 		\
+    }							\
+} while(0)
+
+#define PSTACK_IS_EMPTY(s) (s.psp < s.pstart)
+
+#define PSTACK_TOP(s) (ASSERT(!PSTACK_IS_EMPTY(s)), (PSTACK_TYPE*)(s.psp))
+
+#define PSTACK_PUSH(s) 		                                           \
+    (s.psp += sizeof(PSTACK_TYPE),                                         \
+     ((s.psp == s.pend) ? erl_grow_pstack(&s, PSTK_DEF_STACK(s),           \
+                                          sizeof(PSTACK_TYPE)) : (void)0), \
+     ((PSTACK_TYPE*) s.psp))
+
+#define PSTACK_POP(s) ((PSTACK_TYPE*) (s.psp -= sizeof(PSTACK_TYPE)))
+
+
 /* binary.c */
 
 void erts_emasculate_writable_binary(ProcBin* pb);
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index bd90ec2fba..a54a93b086 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -239,6 +239,32 @@ erl_grow_wstack(ErtsWStack* s, UWord* default_wstack, Uint need)
     s->wsp = s->wstart + sp_offs;
 }
 
+/*
+ * Helper function for the PSTACK macros defined in global.h.
+ */
+void
+erl_grow_pstack(ErtsPStack* s, void* default_pstack, unsigned need_bytes)
+{
+    Uint old_size = s->pend - s->pstart;
+    Uint new_size;
+    Uint sp_offs = s->psp - s->pstart;
+
+    if (need_bytes < old_size)
+	new_size = 2 * old_size;
+    else
+	new_size = ((need_bytes / old_size) + 2) * old_size;
+
+    if (s->pstart != default_pstack) {
+	s->pstart = erts_realloc(s->alloc_type, s->pstart, new_size);
+    } else {
+	byte* new_ptr = erts_alloc(s->alloc_type, new_size);
+	sys_memcpy(new_ptr, s->pstart, old_size);
+	s->pstart = new_ptr;
+    }
+    s->pend = s->pstart + new_size;
+    s->psp = s->pstart + sp_offs;
+}
+
 /* CTYPE macros */
 
 #define LATIN1
-- 
cgit v1.2.3


From fc21440eec0283da271b36181ed24f25dedda0fe Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 10 Feb 2015 23:41:59 +0100
Subject: erts: Make hashmap_merge use dynamic PSTACK

---
 erts/emulator/beam/erl_hashmap.c | 40 ++++++++++++++++++++++------------------
 1 file changed, 22 insertions(+), 18 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 1697449234..4ca0877385 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -821,12 +821,16 @@ static void hashmap_to_list_doer(Eterm* kv, hashmap_doer_state* sp) {
 #define HALLOC_EXTRA 200
 
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
-    struct {
-	Eterm *srcA, *srcB, *dst;
+#define PSTACK_TYPE struct HashmapMergePStackType
+    struct HashmapMergePStackType {
+	Eterm *srcA, *srcB;
 	Uint32 abm, bbm, rbm;        /* node bitmaps */
 	int keepA;
+	int ix;
 	Eterm array[16];
-    }stack[16], *sp = stack;
+    };
+    PSTACK_DECLARE(s, 4);
+    struct HashmapMergePStackType* sp = PSTACK_PUSH(s);
     Eterm *hp, *nhp;
     Eterm hdrA, hdrB;
     Eterm th[2];
@@ -834,7 +838,6 @@ static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
     Uint size;  /* total key-value counter */
     int keepA = 0;
     unsigned lvl = 0;
-    unsigned node_sz;
     Eterm res = THE_NON_VALUE;
 
     /*
@@ -970,12 +973,12 @@ recurse:
     for (;;) {
 	if (is_value(res)) { /* We have a complete (sub-)tree or leaf */
 	    if (lvl == 0)
-		return res;
+		break;
 
 	    /* Pop from stack and continue build parent node */
 	    lvl--;
-	    sp--;
-	    *sp->dst++ = res;
+	    sp = PSTACK_POP(s);
+	    sp->array[sp->ix++] = res;
 	    res = THE_NON_VALUE;
 	    if (sp->rbm) {
 		sp->srcA++;
@@ -983,7 +986,7 @@ recurse:
 		keepA = sp->keepA;
 	    }
 	} else { /* Start build a node */
-	    sp->dst = sp->array;
+	    sp->ix = 0;
 	    sp->rbm = sp->abm | sp->bbm;
 	    ASSERT(!(sp->rbm == 0 && lvl > 0));
 	}
@@ -1001,34 +1004,35 @@ recurse:
 		    nodeA = *sp->srcA;
 		    nodeB = *sp->srcB;
 		    lvl++;
-		    sp++;
+		    sp = PSTACK_PUSH(s);
 		    goto recurse;
 		} else {
-		    *sp->dst++ = *sp->srcA++;
+		    sp->array[sp->ix++] = *sp->srcA++;
 		}
 	    } else {
 		ASSERT(sp->bbm & bit);
-		*sp->dst++ = *sp->srcB++;
+		sp->array[sp->ix++] = *sp->srcB++;
 	    }
 	}
 
-	node_sz = sp->dst - sp->array;
-	ASSERT(node_sz == hashmap_bitcount(sp->abm | sp->bbm));
+	ASSERT(sp->ix == hashmap_bitcount(sp->abm | sp->bbm));
 	if (lvl == 0) {
-	    nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(node_sz), HALLOC_EXTRA);
+	    nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(sp->ix), HALLOC_EXTRA);
 	    hp = nhp;
-	    *hp++ = (node_sz == 16 ? MAP_HEADER_HAMT_HEAD_ARRAY
+	    *hp++ = (sp->ix == 16 ? MAP_HEADER_HAMT_HEAD_ARRAY
 		     : MAP_HEADER_HAMT_HEAD_BITMAP(sp->abm | sp->bbm));
 	    *hp++ = size;
 	} else {
-	    nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(node_sz), HALLOC_EXTRA);
+	    nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(sp->ix), HALLOC_EXTRA);
 	    hp = nhp;
-	    *hp++ = (node_sz == 16 ? make_arityval(16)
+	    *hp++ = (sp->ix == 16 ? make_arityval(16)
 		     : MAP_HEADER_HAMT_NODE_BITMAP(sp->abm | sp->bbm));
 	}
-	memcpy(hp, sp->array, node_sz * sizeof(Eterm));
+	memcpy(hp, sp->array, sp->ix * sizeof(Eterm));
 	res = make_boxed(nhp);
     }
+    PSTACK_DESTROY(s);
+    return res;
 }
 
 static void hashmap_do_foreach(Eterm node, hashmap_doer* fptr,
-- 
cgit v1.2.3


From 85c8e9c956ac7f2fa15abe56a513a2d97839af23 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 10 Feb 2015 21:00:59 +0100
Subject: erts: Make WSTACK usable through pointer

---
 erts/emulator/beam/global.h | 22 ++++++++++++----------
 erts/emulator/beam/utils.c  |  4 ++--
 2 files changed, 14 insertions(+), 12 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 09f43348f4..7585705949 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -519,12 +519,13 @@ typedef struct ErtsWStack_ {
     UWord* wstart;
     UWord* wsp;
     UWord* wend;
+    UWord* wdefault;
     ErtsAlcType_t alloc_type;
 }ErtsWStack;
 
 #define DEF_WSTACK_SIZE (16)
 
-void erl_grow_wstack(ErtsWStack*, UWord* def_stack, Uint need);
+void erl_grow_wstack(ErtsWStack*, Uint need);
 #define WSTK_CONCAT(a,b) a##b
 #define WSTK_DEF_STACK(s) WSTK_CONCAT(s,_default_wstack)
 
@@ -534,6 +535,7 @@ void erl_grow_wstack(ErtsWStack*, UWord* def_stack, Uint need);
         WSTK_DEF_STACK(s),  /* wstart */ 		\
         WSTK_DEF_STACK(s),  /* wsp */			\
         WSTK_DEF_STACK(s) + DEF_WSTACK_SIZE, /* wend */	\
+        WSTK_DEF_STACK(s),  /* wdflt */ 		\
         ERTS_ALC_T_ESTACK /* alloc_type */		\
     }
 
@@ -606,7 +608,7 @@ do {						\
 #define WSTACK_PUSH(s, x)				\
 do {							\
     if (s.wsp == s.wend) {				\
-	erl_grow_wstack(&s, WSTK_DEF_STACK(s), 1); 	\
+	erl_grow_wstack(&s, 1); 	                \
     }							\
     *s.wsp++ = (x);					\
 } while(0)
@@ -614,7 +616,7 @@ do {							\
 #define WSTACK_PUSH2(s, x, y)			\
 do {						\
     if (s.wsp > s.wend - 2) {			\
-	erl_grow_wstack(&s, WSTK_DEF_STACK(s), 2); \
+	erl_grow_wstack(&s, 2);                 \
     }						\
     *s.wsp++ = (x);				\
     *s.wsp++ = (y);				\
@@ -622,8 +624,8 @@ do {						\
 
 #define WSTACK_PUSH3(s, x, y, z)		\
 do {						\
-    if (s.wsp > s.wend - 3) {	\
-	erl_grow_wstack(&s, WSTK_DEF_STACK(s), 3); \
+    if (s.wsp > s.wend - 3) {	                \
+	erl_grow_wstack(&s, 3);                 \
     }						\
     *s.wsp++ = (x);				\
     *s.wsp++ = (y);				\
@@ -632,8 +634,8 @@ do {						\
 
 #define WSTACK_PUSH4(s, A1, A2, A3, A4)		\
 do {						\
-    if (s.wsp > s.wend - 4) {	\
-	erl_grow_wstack(&s, WSTK_DEF_STACK(s)); \
+    if (s.wsp > s.wend - 4) {	                \
+	erl_grow_wstack(&s, 4);                 \
     }						\
     *s.wsp++ = (A1);				\
     *s.wsp++ = (A2);				\
@@ -644,7 +646,7 @@ do {						\
 #define WSTACK_PUSH5(s, A1, A2, A3, A4, A5)     \
 do {						\
     if (s.wsp > s.wend - 5) {	                \
-	erl_grow_wstack(&s, WSTK_DEF_STACK(s)); \
+	erl_grow_wstack(&s, 5);                 \
     }						\
     *s.wsp++ = (A1);				\
     *s.wsp++ = (A2);				\
@@ -656,7 +658,7 @@ do {						\
 #define WSTACK_PUSH6(s, A1, A2, A3, A4, A5, A6) \
 do {						\
     if (s.wsp > s.wend - 6) {	                \
-	erl_grow_wstack(&s, WSTK_DEF_STACK(s)); \
+	erl_grow_wstack(&s, 6);                 \
     }						\
     *s.wsp++ = (A1);				\
     *s.wsp++ = (A2);				\
@@ -669,7 +671,7 @@ do {						\
 #define WSTACK_RESERVE(s, push_cnt)             \
 do {						\
     if (s.wsp > s.wend - (push_cnt)) { 	        \
-	erl_grow_wstack(&s, WSTK_DEF_STACK(s), (push_cnt)); \
+	erl_grow_wstack(&s, (push_cnt));        \
     }                                           \
 } while(0)
 
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index a54a93b086..471bce8940 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -216,7 +216,7 @@ erl_grow_estack(ErtsEStack* s, Eterm* default_estack, Uint need)
  * Helper function for the WSTACK macros defined in global.h.
  */
 void
-erl_grow_wstack(ErtsWStack* s, UWord* default_wstack, Uint need)
+erl_grow_wstack(ErtsWStack* s, Uint need)
 {
     Uint old_size = (s->wend - s->wstart);
     Uint new_size;
@@ -227,7 +227,7 @@ erl_grow_wstack(ErtsWStack* s, UWord* default_wstack, Uint need)
     else
 	new_size = ((need / old_size) + 2) * old_size;
 
-    if (s->wstart != default_wstack) {
+    if (s->wstart != s->wdefault) {
 	s->wstart = erts_realloc(s->alloc_type, s->wstart,
 				 new_size*sizeof(UWord));
     } else {
-- 
cgit v1.2.3


From 8db065e5f6c07904db0c63175c906334e2eb5c59 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 27 Jan 2015 16:32:37 +0100
Subject: erts: Rewrite hashmap:to_list, keys and values

to use an iterator instead of foreach with callback.

Will be easier to make yielding.
---
 erts/emulator/beam/erl_hashmap.c | 175 +++++++++++++++++++--------------------
 1 file changed, 86 insertions(+), 89 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 4ca0877385..2e3a4b8982 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -73,12 +73,6 @@ static Eterm hashmap_values(Process *p, Eterm map);
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 
-typedef struct {
-    Eterm* hp;
-    Eterm res;
-}hashmap_doer_state;
-typedef void hashmap_doer(Eterm*, hashmap_doer_state*);
-static void hashmap_do_foreach(Eterm node, hashmap_doer* fptr, hashmap_doer_state*);
 
 /* hashmap:new/0 */
 
@@ -798,26 +792,6 @@ not_found:
     return res;
 }
 
-static void hashmap_to_list_doer(Eterm* kv, hashmap_doer_state* sp);
-
-static Eterm hashmap_to_list(Process *p, Eterm node) {
-    hashmap_head_t* root;
-    hashmap_doer_state state;
-
-    root = (hashmap_head_t*) boxed_val(node);
-    state.hp  = HAlloc(p, root->size * (2 + 3));
-    state.res = NIL;
-    hashmap_do_foreach(node, hashmap_to_list_doer, &state);
-    return state.res;
-}
-
-static void hashmap_to_list_doer(Eterm* kv, hashmap_doer_state* sp) {
-    Eterm tup = TUPLE2(sp->hp, CAR(kv), CDR(kv));
-    sp->hp += 3;
-    sp->res = CONS(sp->hp, tup, sp->res);
-    sp->hp += 2;
-}
-
 #define HALLOC_EXTRA 200
 
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
@@ -1035,84 +1009,107 @@ recurse:
     return res;
 }
 
-static void hashmap_do_foreach(Eterm node, hashmap_doer* fptr,
-			       hashmap_doer_state* farg) {
-    Eterm *ptr, hdr;
-    Uint sz;
-    DECLARE_ESTACK(stack);
+static void hashmap_iterator_init(ErtsWStack* s, Eterm node) {
+    WSTACK_PUSH((*s), (UWord)THE_NON_VALUE);  /* end marker */
+    WSTACK_PUSH((*s), (UWord)node);
+}
 
-    ESTACK_PUSH(stack, node);
-    do {
-	node = ESTACK_POP(stack);
-	switch(primary_tag(node)) {
-	    case TAG_PRIMARY_LIST:
-		ptr = list_val(node);
-		(*fptr)(ptr, farg); /* Do! */
-		break;
-	    case TAG_PRIMARY_BOXED:
-		ptr = boxed_val(node);
-		hdr = *ptr;
-		ASSERT(is_header(hdr));
-		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-		    case HAMT_SUBTAG_HEAD_ARRAY:
-			ptr++;
-		    case HAMT_SUBTAG_NODE_ARRAY:
-			ptr++;
-			sz = 16;
-			while(sz--) { ESTACK_PUSH(stack, ptr[sz]); }
-			break;
-		    case HAMT_SUBTAG_HEAD_BITMAP:
-			ptr++;
-		    case HAMT_SUBTAG_NODE_BITMAP:
-			sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
-			ASSERT(sz < 17);
-			ptr++;
-			while(sz--) { ESTACK_PUSH(stack, ptr[sz]); }
-			break;
-		    default:
-			erl_exit(1, "bad header\r\n");
-			break;
-		}
-	}
-    } while(!ESTACK_ISEMPTY(stack));
+static Eterm* hashmap_iterator_next(ErtsWStack* s) {
+    Eterm node, *ptr, hdr;
+    Uint32 sz;
 
-    DESTROY_ESTACK(stack);
+    for (;;) {
+        ASSERT(!WSTACK_ISEMPTY((*s)));
+	node = (Eterm) WSTACK_POP((*s));
+        if (is_non_value(node)) {
+            return NULL;
+        }
+        switch (primary_tag(node)) {
+        case TAG_PRIMARY_LIST:
+            return list_val(node);
+
+        case TAG_PRIMARY_BOXED:
+            ptr = boxed_val(node);
+            hdr = *ptr;
+            ASSERT(is_header(hdr));
+            switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+            case HAMT_SUBTAG_HEAD_ARRAY:
+                ptr++;
+            case HAMT_SUBTAG_NODE_ARRAY:
+                ptr++;
+                sz = 16;
+                while(sz--) { WSTACK_PUSH((*s), (UWord)ptr[sz]); }
+                break;
+            case HAMT_SUBTAG_HEAD_BITMAP:
+                ptr++;
+            case HAMT_SUBTAG_NODE_BITMAP:
+                sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+                ASSERT(sz < 17);
+                ptr++;
+                while(sz--) { WSTACK_PUSH((*s), (UWord)ptr[sz]); }
+                break;
+            default:
+                erl_exit(1, "bad header");
+            }
+            break;
+
+        default:
+            erl_exit(1, "bad hamt node");
+	}
+    }
 }
 
-static void hashmap_keys_doer(Eterm* kv, hashmap_doer_state*);
-
-static Eterm hashmap_keys(Process* p, Eterm node) {
+static Eterm hashmap_to_list(Process *p, Eterm node) {
+    DECLARE_WSTACK(stack);
     hashmap_head_t* root;
-    hashmap_doer_state state;
+    Eterm *hp, *kv;
+    Eterm res = NIL;
 
     root = (hashmap_head_t*) boxed_val(node);
-    state.hp  = HAlloc(p, root->size * 2);
-    state.res = NIL;
-    hashmap_do_foreach(node, hashmap_keys_doer, &state);
-    return state.res;
+    hp  = HAlloc(p, root->size * (2 + 3));
+    hashmap_iterator_init(&stack, node);
+    while ((kv=hashmap_iterator_next(&stack)) != NULL) {
+	Eterm tup = TUPLE2(hp, CAR(kv), CDR(kv));
+	hp += 3;
+	res = CONS(hp, tup, res);
+	hp += 2;
+    }
+    DESTROY_WSTACK(stack);
+    return res;
 }
 
-static void hashmap_keys_doer(Eterm* kv, hashmap_doer_state* statep) {
-    statep->res = CONS(statep->hp, CAR(kv), statep->res);
-    statep->hp += 2;
-}
+static Eterm hashmap_keys(Process* p, Eterm node) {
+    DECLARE_WSTACK(stack);
+    hashmap_head_t* root;
+    Eterm *hp, *kv;
+    Eterm res = NIL;
 
-static void hashmap_values_doer(Eterm* kv, hashmap_doer_state*);
+    root = (hashmap_head_t*) boxed_val(node);
+    hp  = HAlloc(p, root->size * 2);
+    hashmap_iterator_init(&stack, node);
+    while ((kv=hashmap_iterator_next(&stack)) != NULL) {
+	res = CONS(hp, CAR(kv), res);
+	hp += 2;
+    }
+    DESTROY_WSTACK(stack);
+    return res;
+}
 
 static Eterm hashmap_values(Process* p, Eterm node) {
+    DECLARE_WSTACK(stack);
     hashmap_head_t* root;
-    hashmap_doer_state state;
+    Eterm *hp, *kv;
+    Eterm res = NIL;
 
     root = (hashmap_head_t*) boxed_val(node);
-    state.hp  = HAlloc(p, root->size * 2);
-    state.res = NIL;
-    hashmap_do_foreach(node, hashmap_values_doer, &state);
-    return state.res;
-}
-
-static void hashmap_values_doer(Eterm* kv, hashmap_doer_state* statep) {
-    statep->res = CONS(statep->hp, CDR(kv), statep->res);
-    statep->hp += 2;
+    hp  = HAlloc(p, root->size * 2);
+    hashmap_iterator_init(&stack, node);
+    while ((kv=hashmap_iterator_next(&stack)) != NULL) {
+	res = CONS(hp, CDR(kv), res);
+	hp += 2;
+    }
+    DESTROY_WSTACK(stack);
+    return res;
 }
 
 /* hashmap:info/0 */
-- 
cgit v1.2.3


From aad7d6fe4e1e9fe086d275ab3ea34c5285cc8edb Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 10 Feb 2015 21:58:45 +0100
Subject: erts: Change to total ordering of keys in small maps

---
 erts/emulator/beam/utils.c       | 26 +++++++++++++++++++++++---
 erts/emulator/test/map_SUITE.erl |  4 +++-
 2 files changed, 26 insertions(+), 4 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 471bce8940..908116c64c 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -2686,8 +2686,22 @@ tailrecur_ne:
 		}
 		aa += 2;
 		bb += 2;
-		i  += 1; /* increment for tuple-keys */
-		goto term_array;
+		if (exact) {
+		    i  += 1; /* increment for tuple-keys */
+		    goto term_array;
+		}
+		else {
+		    /* Value array */
+		    WSTACK_PUSH3(stack, i, (UWord)(bb+1), (UWord)(aa+1) | TAG_PRIMARY_HEADER);
+		    /* Marker to switch back from 'exact' compare */
+		    WSTACK_PUSH(stack, (UWord)NULL | TAG_PRIMARY_HEADER);
+		    /* Now do 'exact' compare of key tuples */
+		    a = *aa;
+		    b = *bb;
+		    exact = 1;
+		    goto tailrecur;
+		}
+
 	    case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
 		if (!is_float_rel(b,b_base)) {
 		    a_tag = FLOAT_DEF;
@@ -3061,7 +3075,13 @@ pop_next:
     if (!WSTACK_ISEMPTY(stack)) {
 	UWord something = WSTACK_POP(stack);
 	if (primary_tag((Eterm) something) == TAG_PRIMARY_HEADER) { /* a term_array */
-	    aa = (Eterm*) something;
+	    if (something == ((UWord)NULL | TAG_PRIMARY_HEADER)) {
+		/* Done with exact compare of map keys, switch back */
+		ASSERT(exact);
+		exact = 0;
+		goto pop_next;
+	    }
+	    aa = (Eterm *)something;
 	    bb = (Eterm*) WSTACK_POP(stack);
 	    i = WSTACK_POP(stack);
 	    goto term_array;
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 888ed8e272..1e989fbc4d 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -432,7 +432,7 @@ t_map_sort_literals(Config) when is_list(Config) ->
     true  = #{ c => 1, b => 1, a => 1 } < id(#{ b => 1, c => 1, d => 1}),
     true  = #{ "a" => 1 } < id(#{ <<"a">> => 1}),
     false = #{ <<"a">> => 1 } < id(#{ "a" => 1}),
-    false = #{ 1 => 1 } < id(#{ 1.0 => 1}),
+    true = #{ 1 => 1 } < id(#{ 1.0 => 1}),
     false = #{ 1.0 => 1 } < id(#{ 1 => 1}),
 
     %% value order
@@ -440,6 +440,8 @@ t_map_sort_literals(Config) when is_list(Config) ->
     false = #{ a => 2 } < id(#{ a => 1}),
     false = #{ a => 2, b => 1 } < id(#{ a => 1, b => 3}),
     true  = #{ a => 1, b => 1 } < id(#{ a => 1, b => 3}),
+    false = #{ a => 1 } < id(#{ a => 1.0}),
+    false = #{ a => 1.0 } < id(#{ a => 1}),
 
     true  = #{ "a" => "hi", b => 134 } == id(#{ b => 134,"a" => "hi"}),
 
-- 
cgit v1.2.3


From 442c9b4d11a62c55b46ffb25f27b5ec5fb3adda7 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Wed, 11 Feb 2015 21:50:25 +0100
Subject: erts: First recursive version of hashmap compare

---
 erts/emulator/beam/erl_hashmap.c | 129 +++++++++++++++++++++++++++++++++++++++
 erts/emulator/beam/erl_hashmap.h |   2 +
 erts/emulator/beam/utils.c       |  13 ++++
 3 files changed, 144 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 2e3a4b8982..2fd743a2d8 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -1112,6 +1112,135 @@ static Eterm hashmap_values(Process* p, Eterm node) {
     return res;
 }
 
+static int hash_cmp(Uint32 ha, Uint32 hb)
+{
+    int i;
+    for (i=0; i<8; i++) {
+	int cmp = (int)(ha & 0xF) - (int)(hb & 0xF);
+	if (cmp)
+	    return cmp;
+	ha >>= 4;
+	hb >>= 4;
+    }
+    return 0;
+}
+
+static int key_hash_cmp(Eterm* ap, Eterm* bp)
+{
+    Eterm th[2];
+    unsigned lvl = 0;
+
+    if (ap && bp) {
+	ASSERT(CMP_TERM(CAR(ap), CAR(bp)) != 0);
+	for (;;) {
+	    Uint32 ha = hashmap_restore_hash(th, lvl, CAR(ap));
+	    Uint32 hb = hashmap_restore_hash(th, lvl, CAR(bp));
+	    int cmp = hash_cmp(ha, hb);
+	    if (cmp)
+		return cmp;
+	    lvl += 8;
+	}
+    }
+    return ap ? -1 : 1;
+}
+
+int hashmap_cmp(Eterm a, Eterm b)
+{
+    DECLARE_WSTACK(astack);
+    DECLARE_WSTACK(bstack);
+    Eterm *ap;
+    Eterm *bp;
+    Eterm min_key;
+    int cmp_res = 0;
+
+    /* Strategy:
+       Phase 1.
+          While keys are identical
+	  Do synchronous stepping through leafs of both trees in hash order.
+          Maintain min value of min key.
+
+       Phase 2:  (If key diff was found in phase 1)
+          Ignore values from now on.
+          Continue iterate trees by always advancing the one lagging behind hash-wise.
+          Identical keys are skipped
+          A minimal key can only be candidate as tie-breaker if we have passed
+          that hash value in the other tree (which means the key did not exist
+          in the other tree).
+     */
+
+    hashmap_iterator_init(&astack, a);
+    hashmap_iterator_init(&bstack, b);
+
+    for (;;) {   /* Phase 1 */
+	int cmp;
+	ap = hashmap_iterator_next(&astack);
+	bp = hashmap_iterator_next(&bstack);
+	if (!ap) {
+	    ASSERT(!bp);
+	    return cmp_res;
+	}
+	cmp = CMP_TERM(CAR(ap), CAR(bp));
+	if (cmp)
+	    break;
+
+	/* No key diff found so far, compare values */
+	if (!cmp_res || (CMP_TERM(CAR(ap), min_key) < 0)) {
+	    cmp = CMP(CDR(ap), CDR(bp));
+	    if (cmp) {
+		cmp_res = cmp;
+		min_key = CAR(ap);
+	    }
+	}
+    }
+
+    /* Phase 2 */
+
+    if (key_hash_cmp(ap,bp) < 0) {
+	min_key = CAR(ap);
+	cmp_res = -1;
+	ap = hashmap_iterator_next(&astack);
+    }
+    else {
+	min_key = CAR(bp);
+	cmp_res = 1;
+	bp = hashmap_iterator_next(&bstack);
+    }
+
+    for (;;) {
+	int hash_cmp;
+
+	while (ap && bp && CMP_TERM(CAR(ap), CAR(bp)) == 0) {
+	    ap = hashmap_iterator_next(&astack);
+	    bp = hashmap_iterator_next(&bstack);
+	}
+	if (!ap && !bp)
+	    break;
+
+	hash_cmp = key_hash_cmp(ap,bp);
+	if (hash_cmp < 0) {
+	    ASSERT(ap);
+	    if (CMP_TERM(CAR(ap), min_key) < 0) {
+		min_key = CAR(ap);
+                cmp_res = -1;
+	    }
+	    ap = hashmap_iterator_next(&astack);
+	}
+	else if (hash_cmp > 0) {
+	    ASSERT(bp);
+	    if (CMP_TERM(CAR(bp), min_key) < 0) {
+		min_key = CAR(bp);
+		cmp_res = 1;
+	    }
+	    bp = hashmap_iterator_next(&bstack);
+	}
+    }
+
+    DESTROY_WSTACK(astack);
+    DESTROY_WSTACK(bstack);
+
+    return cmp_res;
+}
+
 /* hashmap:info/0 */
 
 static Eterm hashmap_info(Process *p, Eterm node) {
diff --git a/erts/emulator/beam/erl_hashmap.h b/erts/emulator/beam/erl_hashmap.h
index 4ef795b11e..4a4163bce9 100644
--- a/erts/emulator/beam/erl_hashmap.h
+++ b/erts/emulator/beam/erl_hashmap.h
@@ -24,11 +24,13 @@
 #include "sys.h"
 
 Eterm erts_hashmap_get(Eterm key, Eterm map);
+int hashmap_cmp(Eterm a, Eterm b);
 
 /* erl_term.h stuff */
 #define make_hashmap(x)		make_boxed((Eterm*)(x))
 #define make_hashmap_rel 	make_boxed_rel
 #define is_hashmap(x)		(is_boxed((x)) && is_hashmap_header(*boxed_val((x))))
+#define is_hashmap_rel(RTERM,BASE)  is_hashmap(rterm2wterm(RTERM,BASE))
 #define is_hashmap_header(x)	(((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_HASHMAP)
 #define hashmap_val(x)		_unchecked_boxed_val((x))
 #define hashmap_val_rel(RTERM, BASE) hashmap_val(rterm2wterm(RTERM, BASE))
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 908116c64c..9fd53e941d 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -2702,6 +2702,19 @@ tailrecur_ne:
 		    goto tailrecur;
 		}
 
+	    case (_TAG_HEADER_HASHMAP >> _TAG_PRIMARY_SIZE) :
+		{
+		    if (!is_hashmap_rel(b,b_base)) {
+			a_tag = HASHMAP_DEF;
+			goto mixed_types;
+		    }
+		    i = (((hashmap_head_t*) hashmap_val_rel(a,a_base))->size -
+			 ((hashmap_head_t*) hashmap_val_rel(b,b_base))->size);
+		    if (i) {
+			RETURN_NEQ(i);
+		    }
+		    ON_CMP_GOTO(hashmap_cmp(a, b));
+		}
 	    case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
 		if (!is_float_rel(b,b_base)) {
 		    a_tag = FLOAT_DEF;
-- 
cgit v1.2.3


From 6cc099bf98f384de1de3c0d8542c83db43fb5cec Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 10 Feb 2015 23:32:12 +0100
Subject: erts: Make hashmap compare non-recursive

---
 erts/emulator/beam/erl_hashmap.c | 103 +---------------
 erts/emulator/beam/erl_hashmap.h |   8 +-
 erts/emulator/beam/global.h      |   9 +-
 erts/emulator/beam/utils.c       | 253 ++++++++++++++++++++++++++++++++++++---
 4 files changed, 250 insertions(+), 123 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 2fd743a2d8..b423111715 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -1009,12 +1009,12 @@ recurse:
     return res;
 }
 
-static void hashmap_iterator_init(ErtsWStack* s, Eterm node) {
+void hashmap_iterator_init(ErtsWStack* s, Eterm node) {
     WSTACK_PUSH((*s), (UWord)THE_NON_VALUE);  /* end marker */
     WSTACK_PUSH((*s), (UWord)node);
 }
 
-static Eterm* hashmap_iterator_next(ErtsWStack* s) {
+Eterm* hashmap_iterator_next(ErtsWStack* s) {
     Eterm node, *ptr, hdr;
     Uint32 sz;
 
@@ -1125,7 +1125,7 @@ static int hash_cmp(Uint32 ha, Uint32 hb)
     return 0;
 }
 
-static int key_hash_cmp(Eterm* ap, Eterm* bp)
+int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp)
 {
     Eterm th[2];
     unsigned lvl = 0;
@@ -1144,103 +1144,6 @@ static int key_hash_cmp(Eterm* ap, Eterm* bp)
     return ap ? -1 : 1;
 }
 
-int hashmap_cmp(Eterm a, Eterm b)
-{
-    DECLARE_WSTACK(astack);
-    DECLARE_WSTACK(bstack);
-    Eterm *ap;
-    Eterm *bp;
-    Eterm min_key;
-    int cmp_res = 0;
-
-    /* Strategy:
-       Phase 1.
-          While keys are identical
-	  Do synchronous stepping through leafs of both trees in hash order.
-          Maintain min value of min key.
-
-       Phase 2:  (If key diff was found in phase 1)
-          Ignore values from now on.
-          Continue iterate trees by always advancing the one lagging behind hash-wise.
-          Identical keys are skipped
-          A minimal key can only be candidate as tie-breaker if we have passed
-          that hash value in the other tree (which means the key did not exist
-          in the other tree).
-     */
-
-    hashmap_iterator_init(&astack, a);
-    hashmap_iterator_init(&bstack, b);
-
-    for (;;) {   /* Phase 1 */
-	int cmp;
-	ap = hashmap_iterator_next(&astack);
-	bp = hashmap_iterator_next(&bstack);
-	if (!ap) {
-	    ASSERT(!bp);
-	    return cmp_res;
-	}
-	cmp = CMP_TERM(CAR(ap), CAR(bp));
-	if (cmp)
-	    break;
-
-	/* No key diff found so far, compare values */
-	if (!cmp_res || (CMP_TERM(CAR(ap), min_key) < 0)) {
-	    cmp = CMP(CDR(ap), CDR(bp));
-	    if (cmp) {
-		cmp_res = cmp;
-		min_key = CAR(ap);
-	    }
-	}
-    }
-
-    /* Phase 2 */
-
-    if (key_hash_cmp(ap,bp) < 0) {
-	min_key = CAR(ap);
-	cmp_res = -1;
-	ap = hashmap_iterator_next(&astack);
-    }
-    else {
-	min_key = CAR(bp);
-	cmp_res = 1;
-	bp = hashmap_iterator_next(&bstack);
-    }
-
-    for (;;) {
-	int hash_cmp;
-
-	while (ap && bp && CMP_TERM(CAR(ap), CAR(bp)) == 0) {
-	    ap = hashmap_iterator_next(&astack);
-	    bp = hashmap_iterator_next(&bstack);
-	}
-	if (!ap && !bp)
-	    break;
-
-	hash_cmp = key_hash_cmp(ap,bp);
-	if (hash_cmp < 0) {
-	    ASSERT(ap);
-	    if (CMP_TERM(CAR(ap), min_key) < 0) {
-		min_key = CAR(ap);
-                cmp_res = -1;
-	    }
-	    ap = hashmap_iterator_next(&astack);
-	}
-	else if (hash_cmp > 0) {
-	    ASSERT(bp);
-	    if (CMP_TERM(CAR(bp), min_key) < 0) {
-		min_key = CAR(bp);
-		cmp_res = 1;
-	    }
-	    bp = hashmap_iterator_next(&bstack);
-	}
-    }
-
-    DESTROY_WSTACK(astack);
-    DESTROY_WSTACK(bstack);
-
-    return cmp_res;
-}
-
 /* hashmap:info/0 */
 
 static Eterm hashmap_info(Process *p, Eterm node) {
diff --git a/erts/emulator/beam/erl_hashmap.h b/erts/emulator/beam/erl_hashmap.h
index 4a4163bce9..8ba249b053 100644
--- a/erts/emulator/beam/erl_hashmap.h
+++ b/erts/emulator/beam/erl_hashmap.h
@@ -24,7 +24,10 @@
 #include "sys.h"
 
 Eterm erts_hashmap_get(Eterm key, Eterm map);
-int hashmap_cmp(Eterm a, Eterm b);
+struct ErtsWStack_;
+void hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
+Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
+int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
 
 /* erl_term.h stuff */
 #define make_hashmap(x)		make_boxed((Eterm*)(x))
@@ -67,6 +70,9 @@ typedef struct hashmap_head_s {
     Uint size;
     Eterm items[1];
 } hashmap_head_t;
+
+#define hashmap_size(x) (((hashmap_head_t*) hashmap_val(x))->size)
+#define hashmap_size_rel(RTERM, BASE) hashmap_size(rterm2wterm(RTERM, BASE))
  
 /* the bitmap-node
  * typedef struct hashmap_bitmap_node_s {
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 7585705949..ad7e6fb89d 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -529,7 +529,7 @@ void erl_grow_wstack(ErtsWStack*, Uint need);
 #define WSTK_CONCAT(a,b) a##b
 #define WSTK_DEF_STACK(s) WSTK_CONCAT(s,_default_wstack)
 
-#define DECLARE_WSTACK(s)				\
+#define WSTACK_DECLARE(s)				\
     UWord WSTK_DEF_STACK(s)[DEF_WSTACK_SIZE];		\
     ErtsWStack s = {					\
         WSTK_DEF_STACK(s),  /* wstart */ 		\
@@ -538,6 +538,7 @@ void erl_grow_wstack(ErtsWStack*, Uint need);
         WSTK_DEF_STACK(s),  /* wdflt */ 		\
         ERTS_ALC_T_ESTACK /* alloc_type */		\
     }
+#define DECLARE_WSTACK WSTACK_DECLARE
 
 #define WSTACK_CHANGE_ALLOCATOR(s,t)					\
 do {									\
@@ -548,12 +549,13 @@ do {									\
     s.alloc_type = (t);							\
  } while (0)
 
-#define DESTROY_WSTACK(s)				\
+#define WSTACK_DESTROY(s)				\
 do {							\
     if (s.wstart != WSTK_DEF_STACK(s)) {		\
 	erts_free(s.alloc_type, s.wstart); 		\
     }							\
 } while(0)
+#define DESTROY_WSTACK WSTACK_DESTROY
 
 #define WSTACK_DEBUG(s) \
     do { \
@@ -686,7 +688,8 @@ do {						\
 #define WSTACK_ISEMPTY(s) (s.wsp == s.wstart)
 #define WSTACK_POP(s) ((ASSERT(s.wsp > s.wstart)),*(--s.wsp))
 
-
+#define WSTACK_ROLLBACK(s, count) (ASSERT(WSTACK_COUNT(s) >= (count)), \
+                                   s.wsp = s.wstart + (count))
 
 /* PSTACK - Stack of any type.
  * Usage:
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 9fd53e941d..f79cb8db7d 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -2512,7 +2512,18 @@ Sint erts_cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, int exact)
 Sint erts_cmp(Eterm a, Eterm b, int exact)
 #endif
 {
-    DECLARE_WSTACK(stack);
+#define PSTACK_TYPE struct erts_cmp_hashmap_state
+    struct erts_cmp_hashmap_state {
+        Sint wstack_rollback;
+        int was_exact;
+        Eterm *ap;
+        Eterm *bp;
+        Eterm min_key;
+        Sint cmp_res;   /* result so far -1,0,+1 */
+    };
+    PSTACK_DECLARE(hmap_stack, 1);
+    WSTACK_DECLARE(stack);
+    WSTACK_DECLARE(b_stack); /* only used by hashmaps */
     Eterm* aa;
     Eterm* bb;
     int i;
@@ -2528,6 +2539,26 @@ Sint erts_cmp(Eterm a, Eterm b, int exact)
     Uint32 *anum;
     Uint32 *bnum;
 
+/* The WSTACK contains naked Eterms and Operations marked with header-tags */
+#define OP_BITS 4
+#define OP_MASK 0xF
+#define TERM_ARRAY_OP                 0
+#define SWITCH_EXACT_OFF_OP           1
+#define HASHMAP_PHASE1_ARE_KEYS_EQUAL 2
+#define HASHMAP_PHASE1_IS_MIN_KEY     3
+#define HASHMAP_PHASE1_CMP_VALUES     4
+#define HASHMAP_PHASE2_ARE_KEYS_EQUAL 5
+#define HASHMAP_PHASE2_IS_MIN_KEY_A   6
+#define HASHMAP_PHASE2_IS_MIN_KEY_B   7
+
+
+#define OP_WORD(OP)  (((OP)  << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER)
+#define TERM_ARRAY_OP_WORD(SZ) OP_WORD(((SZ) << OP_BITS) | TERM_ARRAY_OP)
+
+#define GET_OP(WORD) (ASSERT(is_header(WORD)), ((WORD) >> _TAG_PRIMARY_SIZE) & OP_MASK)
+#define GET_OP_ARG(WORD) (ASSERT(is_header(WORD)), ((WORD) >> (OP_BITS + _TAG_PRIMARY_SIZE)))
+
+
 #define RETURN_NEQ(cmp) { j=(cmp); ASSERT(j != 0); goto not_equal; }
 #define ON_CMP_GOTO(cmp) if ((j=(cmp)) == 0) goto pop_next; else goto not_equal
 
@@ -2543,6 +2574,8 @@ Sint erts_cmp(Eterm a, Eterm b, int exact)
     } while (0)
 
 
+bodyrecur:
+    j = 0;
 tailrecur:
     if (is_same(a,a_base,b,b_base)) {	/* Equal values or pointers. */
 	goto pop_next;
@@ -2692,28 +2725,60 @@ tailrecur_ne:
 		}
 		else {
 		    /* Value array */
-		    WSTACK_PUSH3(stack, i, (UWord)(bb+1), (UWord)(aa+1) | TAG_PRIMARY_HEADER);
-		    /* Marker to switch back from 'exact' compare */
-		    WSTACK_PUSH(stack, (UWord)NULL | TAG_PRIMARY_HEADER);
+		    WSTACK_PUSH3(stack, (UWord)(bb+1), (UWord)(aa+1), TERM_ARRAY_OP_WORD(i));
+		    /* Switch back from 'exact' key compare */
+		    WSTACK_PUSH(stack, OP_WORD(SWITCH_EXACT_OFF_OP));
 		    /* Now do 'exact' compare of key tuples */
 		    a = *aa;
 		    b = *bb;
 		    exact = 1;
-		    goto tailrecur;
+		    goto bodyrecur;
 		}
 
 	    case (_TAG_HEADER_HASHMAP >> _TAG_PRIMARY_SIZE) :
 		{
+                    struct erts_cmp_hashmap_state* sp;
 		    if (!is_hashmap_rel(b,b_base)) {
 			a_tag = HASHMAP_DEF;
 			goto mixed_types;
 		    }
-		    i = (((hashmap_head_t*) hashmap_val_rel(a,a_base))->size -
-			 ((hashmap_head_t*) hashmap_val_rel(b,b_base))->size);
+		    i = hashmap_size_rel(a,a_base) - hashmap_size_rel(b,b_base);
 		    if (i) {
 			RETURN_NEQ(i);
 		    }
-		    ON_CMP_GOTO(hashmap_cmp(a, b));
+                    if (hashmap_size_rel(a,a_base) == 0) {
+                        goto pop_next;
+                    }
+
+                /* Hashmap compare strategy:
+                   Phase 1. While keys are identical
+                     Do synchronous stepping through leafs of both trees in hash
+                     order. Maintain value compare result of minimal key.
+
+                   Phase 2. If key diff was found in phase 1
+                     Ignore values from now on.
+                     Continue iterate trees by always advancing the one
+                     lagging behind hash-wise. Identical keys are skipped.
+                     A minimal key can only be candidate as tie-breaker if we
+                     have passed that hash value in the other tree (which means
+                     the key did not exist in the other tree).
+                */
+
+                    sp = PSTACK_PUSH(hmap_stack);
+                    hashmap_iterator_init(&stack, a);
+                    hashmap_iterator_init(&b_stack, b);
+                    sp->ap = hashmap_iterator_next(&stack);
+                    sp->bp = hashmap_iterator_next(&b_stack);
+                    sp->cmp_res = 0;
+                    ASSERT(sp->ap && sp->bp);
+
+                    a = CAR(sp->ap);
+                    b = CAR(sp->bp);
+                    sp->was_exact = exact;
+                    exact = 1;
+                    WSTACK_PUSH(stack, OP_WORD(HASHMAP_PHASE1_ARE_KEYS_EQUAL));
+                    sp->wstack_rollback = WSTACK_COUNT(stack);
+                    goto bodyrecur;
 		}
 	    case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
 		if (!is_float_rel(b,b_base)) {
@@ -3074,8 +3139,7 @@ term_array: /* arrays in 'aa' and 'bb', length in 'i' */
 		    goto not_equal;
 		}
 	    } else {
-		/* (ab)Use TAG_PRIMARY_HEADER to recognize a term_array */
-		WSTACK_PUSH3(stack, i, (UWord)bb, (UWord)aa | TAG_PRIMARY_HEADER);
+		WSTACK_PUSH3(stack, (UWord)bb, (UWord)aa, TERM_ARRAY_OP_WORD(i));
 		goto tailrecur_ne;
 	    }
 	}
@@ -3087,28 +3151,179 @@ term_array: /* arrays in 'aa' and 'bb', length in 'i' */
 pop_next:
     if (!WSTACK_ISEMPTY(stack)) {
 	UWord something = WSTACK_POP(stack);
-	if (primary_tag((Eterm) something) == TAG_PRIMARY_HEADER) { /* a term_array */
-	    if (something == ((UWord)NULL | TAG_PRIMARY_HEADER)) {
+        struct erts_cmp_hashmap_state* sp;
+	if (primary_tag((Eterm) something) == TAG_PRIMARY_HEADER) { /* an operation */
+	    switch (GET_OP(something)) {
+	    case TERM_ARRAY_OP:
+		i = GET_OP_ARG(something);
+		aa = (Eterm*)WSTACK_POP(stack);
+		bb = (Eterm*) WSTACK_POP(stack);
+		goto term_array;
+
+	    case SWITCH_EXACT_OFF_OP:
 		/* Done with exact compare of map keys, switch back */
 		ASSERT(exact);
 		exact = 0;
 		goto pop_next;
-	    }
-	    aa = (Eterm *)something;
-	    bb = (Eterm*) WSTACK_POP(stack);
-	    i = WSTACK_POP(stack);
-	    goto term_array;
+
+            case HASHMAP_PHASE1_ARE_KEYS_EQUAL: {
+                sp = PSTACK_TOP(hmap_stack);
+                if (j) {
+                    /* Key diff found, enter phase 2 */
+                    if (hashmap_key_hash_cmp(sp->ap, sp->bp) < 0) {
+                        sp->min_key = CAR(sp->ap);
+                        sp->cmp_res = -1;
+                        sp->ap = hashmap_iterator_next(&stack);
+                    }
+                    else {
+                        sp->min_key = CAR(sp->bp);
+                        sp->cmp_res = 1;
+                        sp->bp = hashmap_iterator_next(&b_stack);
+                    }
+                    exact = 1; /* only exact key compares in phase 2 */
+                    goto case_HASHMAP_PHASE2_LOOP;
+                }
+
+                /* No key diff found so far, compare values if min key */
+
+                if (sp->cmp_res) {
+                    a = CAR(sp->ap);
+                    b = sp->min_key;
+                    exact = 1;
+                    WSTACK_PUSH(stack, OP_WORD(HASHMAP_PHASE1_IS_MIN_KEY));
+                    sp->wstack_rollback = WSTACK_COUNT(stack);
+                    goto bodyrecur;
+                }
+                /* no min key-value found yet */
+                a = CDR(sp->ap);
+                b = CDR(sp->bp);
+                exact = sp->was_exact;
+                WSTACK_PUSH(stack, OP_WORD(HASHMAP_PHASE1_CMP_VALUES));
+                sp->wstack_rollback = WSTACK_COUNT(stack);
+                goto bodyrecur;
+            }
+            case HASHMAP_PHASE1_IS_MIN_KEY:
+                sp = PSTACK_TOP(hmap_stack);
+                if (j < 0) {
+                    a = CDR(sp->ap);
+                    b = CDR(sp->bp);
+                    exact = sp->was_exact;
+                    WSTACK_PUSH(stack, OP_WORD(HASHMAP_PHASE1_CMP_VALUES));
+                    sp->wstack_rollback = WSTACK_COUNT(stack);
+                    goto bodyrecur;
+                }
+                goto case_HASHMAP_PHASE1_LOOP;
+
+            case HASHMAP_PHASE1_CMP_VALUES:
+                sp = PSTACK_TOP(hmap_stack);
+                if (j) {
+                    sp->cmp_res = j;
+                    sp->min_key = CAR(sp->ap);
+                }
+            case_HASHMAP_PHASE1_LOOP:
+                sp->ap = hashmap_iterator_next(&stack);
+                sp->bp = hashmap_iterator_next(&b_stack);
+                if (!sp->ap) {
+                    /* end of maps with identical keys */
+                    ASSERT(!sp->bp);
+                    j = sp->cmp_res;
+                    exact = sp->was_exact;
+                    (void) PSTACK_POP(hmap_stack);
+                    ON_CMP_GOTO(j);
+                }
+                a = CAR(sp->ap);
+                b = CAR(sp->bp);
+                exact = 1;
+                WSTACK_PUSH(stack, OP_WORD(HASHMAP_PHASE1_ARE_KEYS_EQUAL));
+                sp->wstack_rollback = WSTACK_COUNT(stack);
+                goto bodyrecur;
+
+            case_HASHMAP_PHASE2_LOOP:
+                if (sp->ap && sp->bp) {
+                    a = CAR(sp->ap);
+                    b = CAR(sp->bp);
+                    ASSERT(exact);
+                    WSTACK_PUSH(stack, OP_WORD(HASHMAP_PHASE2_ARE_KEYS_EQUAL));
+                    sp->wstack_rollback = WSTACK_COUNT(stack);
+                    goto bodyrecur;
+                }
+                goto case_HASHMAP_PHASE2_NEXT_STEP;
+
+            case HASHMAP_PHASE2_ARE_KEYS_EQUAL:
+                sp = PSTACK_TOP(hmap_stack);
+                if (j == 0) {
+                    /* keys are equal, skip them */
+                    sp->ap = hashmap_iterator_next(&stack);
+                    sp->bp = hashmap_iterator_next(&b_stack);
+                    goto case_HASHMAP_PHASE2_LOOP;
+                }
+                /* fall through */
+            case_HASHMAP_PHASE2_NEXT_STEP:
+                if (sp->ap || sp->bp) {
+                    if (hashmap_key_hash_cmp(sp->ap, sp->bp) < 0) {
+                        ASSERT(sp->ap);
+                        a = CAR(sp->ap);
+                        b = sp->min_key;
+                        ASSERT(exact);
+                        WSTACK_PUSH(stack, OP_WORD(HASHMAP_PHASE2_IS_MIN_KEY_A));
+                    }
+                    else { /* hash_cmp > 0 */
+                        ASSERT(sp->bp);
+                        a = CAR(sp->bp);
+                        b = sp->min_key;
+                        ASSERT(exact);
+                        WSTACK_PUSH(stack, OP_WORD(HASHMAP_PHASE2_IS_MIN_KEY_B));
+                    }
+                    sp->wstack_rollback = WSTACK_COUNT(stack);
+                    goto bodyrecur;
+                }
+                /* End of both maps */
+                j = sp->cmp_res;
+                exact = sp->was_exact;
+                (void) PSTACK_POP(hmap_stack);
+                ON_CMP_GOTO(j);
+
+            case HASHMAP_PHASE2_IS_MIN_KEY_A:
+                sp = PSTACK_TOP(hmap_stack);
+                if (j < 0) {
+                    sp->min_key = CAR(sp->ap);
+                    sp->cmp_res = -1;
+                }
+                sp->ap = hashmap_iterator_next(&stack);
+                goto case_HASHMAP_PHASE2_LOOP;
+
+            case HASHMAP_PHASE2_IS_MIN_KEY_B:
+                sp = PSTACK_TOP(hmap_stack);
+                if (j < 0) {
+                    sp->min_key = CAR(sp->bp);
+                    sp->cmp_res = 1;
+                }
+                sp->bp = hashmap_iterator_next(&b_stack);
+                goto case_HASHMAP_PHASE2_LOOP;
+
+            default:
+                ASSERT(!"Invalid cmp op");
+            } /* switch */
 	}
 	a = (Eterm) something;
 	b = (Eterm) WSTACK_POP(stack);
 	goto tailrecur;
     }
 
-    DESTROY_WSTACK(stack);
+    ASSERT(PSTACK_IS_EMPTY(hmap_stack));
+    PSTACK_DESTROY(hmap_stack);
+    WSTACK_DESTROY(stack);
+    WSTACK_DESTROY(b_stack);
     return 0;
 
 not_equal:
-    DESTROY_WSTACK(stack);
+    if (!PSTACK_IS_EMPTY(hmap_stack)) {
+        WSTACK_ROLLBACK(stack, PSTACK_TOP(hmap_stack)->wstack_rollback);
+        goto pop_next;
+    }
+    PSTACK_DESTROY(hmap_stack);
+    WSTACK_DESTROY(stack);
+    WSTACK_DESTROY(b_stack);
     return j;
 
 #undef CMP_NODES
-- 
cgit v1.2.3


From 2583604581eb07b9a6962bfc76c5015afca840b5 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 10 Feb 2015 23:25:14 +0100
Subject: erts: Add map_SUITE:t_map_compare

---
 erts/emulator/test/map_SUITE.erl | 246 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 245 insertions(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 1e989fbc4d..5944450f33 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -30,6 +30,7 @@
 	t_list_comprehension/1,
 	t_map_sort_literals/1,
 	t_map_equal/1,
+	t_map_compare/1,
 	%t_size/1,
 	t_map_size/1,
 
@@ -66,6 +67,13 @@
 
 -include_lib("stdlib/include/ms_transform.hrl").
 
+-define(CHECK(Cond,Term),
+	case (catch (Cond)) of
+	    true -> true;
+	    _ -> io:format("###### CHECK FAILED ######~nINPUT:  ~p~n", [Term]),
+		 exit(Term)
+	end).
+
 suite() -> [].
 
 all() -> [
@@ -75,7 +83,7 @@ all() -> [
 	t_update_assoc,t_update_exact,
 	t_guard_bifs, t_guard_sequence, t_guard_update,
 	t_guard_receive,t_guard_fun, t_list_comprehension,
-	t_map_equal,
+	t_map_equal, t_map_compare,
 	t_map_sort_literals,
 
 	%% Specific Map BIFs
@@ -471,6 +479,242 @@ t_map_equal(Config) when is_list(Config) ->
     true = id(#{ a => 1, b => 3, c => <<"wat">> }) =:= id(#{ a => 1, b => 3, c=><<"wat">>}),
     ok.
 
+
+t_map_compare(Config) when is_list(Config) ->
+    Seed = erlang:now(),
+    io:format("seed = ~p\n", [Seed]),
+    random:seed(Seed),
+    repeat(100, fun(_) -> float_int_compare(maps) end, []),
+    repeat(100, fun(_) -> float_int_compare(hashmap) end, []),
+    repeat(1000, fun(_) -> recursive_compare() end, []),
+    ok.
+
+float_int_compare(MapMod) ->
+    Terms = numeric_keys(3),
+    io:format("Keys to use: ~p\n", [Terms]),
+    Pairs = lists:map(fun(K) -> list_to_tuple([{K,V} || V <- Terms]) end, Terms),
+    lists:foreach(fun(Size) ->
+			  MapGen = fun() -> map_gen(MapMod, list_to_tuple(Pairs), Size) end,
+			  repeat(100, fun do_compare/1, [MapMod, MapGen, MapGen])
+		  end,
+		  lists:seq(1,length(Terms))),
+    ok.
+
+numeric_keys(N) ->
+    lists:foldl(fun(_,Acc) ->
+			Int = random:uniform(N*4) - N*2,
+			Float = float(Int),
+			[Int, Float, Float * 0.99, Float * 1.01 | Acc]
+		end,
+		[],
+		lists:seq(1,N)).
+
+
+repeat(0, _, _) ->
+    ok;
+repeat(N, Fun, Arg) ->
+    Fun(Arg),
+    repeat(N-1, Fun, Arg).
+
+copy_term(T) ->
+    Papa = self(),
+    P = spawn_link(fun() -> receive Msg -> Papa ! Msg end end),
+    P ! T,
+    receive R -> R end.
+
+do_compare([MapMod, Gen1, Gen2]) ->
+    M1 = Gen1(),
+    M2 = Gen2(),
+    %%io:format("Maps to compare: ~p AND ~p\n", [M1, M2]),
+    C = (M1 < M2),
+    Erlang = maps_lessthan(MapMod, M1, M2),
+    C = Erlang,
+    ?CHECK(M1==M1, M1),
+
+    %% Change one key from int to float (or vice versa) and check compare
+    ML1 = MapMod:to_list(M1),
+    {K1,V1} = lists:nth(random:uniform(length(ML1)), ML1),
+    case K1 of
+	I when is_integer(I) ->
+	    case MapMod:find(float(I),M1) of
+		error ->
+		    M1f = MapMod:remove(I, MapMod:put(float(I), V1, M1)),
+		    ?CHECK(M1f > M1, [M1f, M1]);
+		_ -> ok
+	    end;
+
+	F when is_float(F), round(F) == F ->
+	    case MapMod:find(round(F),M1) of
+		error ->
+		    M1i = MapMod:remove(F, MapMod:put(round(F), V1, M1)),
+		    ?CHECK(M1i < M1, [M1i, M1]);
+		_ -> ok
+	    end;
+
+	_ -> ok   % skip floats with decimals
+    end,
+
+    ?CHECK(M2 == M2, [M2]).
+
+
+maps_lessthan(MapMod, M1, M2) ->
+  case {MapMod:size(M1),MapMod:size(M2)} of
+      {_S,_S} ->
+	  {K1,V1} = lists:unzip(term_sort(MapMod:to_list(M1))),
+	  {K2,V2} = lists:unzip(term_sort(MapMod:to_list(M2))),
+
+	  case erts_internal:cmp_term(K1,K2) of
+	      -1 -> true;
+	      0 -> (V1 < V2);
+	      1 -> false
+	  end;
+
+      {S1, S2} ->
+	  S1 < S2
+  end.
+
+term_sort(L) ->
+    lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) =< 0 end,
+	       L).
+
+
+cmp(T1, T2, Exact) when is_tuple(T1) and is_tuple(T2) ->
+    case {size(T1),size(T2)} of
+	{_S,_S} -> cmp(tuple_to_list(T1), tuple_to_list(T2), Exact);
+	{S1,S2} when S1 < S2 -> -1;
+	{S1,S2} when S1 > S2 -> 1
+    end;
+
+cmp([H1|T1], [H2|T2], Exact) ->
+    case cmp(H1,H2, Exact) of
+	0 -> cmp(T1,T2, Exact);
+	C -> C
+    end;
+
+cmp(M1, M2, Exact) -> %when is_hashmap(M1) and is_hashmap(M2)
+    case {erlang:is_hashmap(M1), erlang:is_hashmap(M2)} of
+	{true, true} -> cmp_hashmaps(M1, M2, Exact);
+	_ -> cmp_others(M1, M2, Exact)
+    end.
+
+cmp_hashmaps(M1, M2, Exact) ->
+    case {hashmap:size(M1),hashmap:size(M2)} of
+	{_S,_S} ->
+	    {K1,V1} = lists:unzip(term_sort(hashmap:to_list(M1))),
+	    {K2,V2} = lists:unzip(term_sort(hashmap:to_list(M2))),
+
+	    case cmp(K1, K2, true) of
+		0 -> cmp(V1, V2, Exact);
+		C -> C
+	    end;
+
+	{S1,S2} when S1 < S2 -> -1;
+	{S1,S2} when S1 > S2 -> 1
+    end.
+
+cmp_others(I, F, true) when is_integer(I), is_float(F) ->
+    -1;
+cmp_others(F, I, true) when is_float(F), is_integer(I) ->
+    1;
+cmp_others(T1, T2, _) ->
+    case {T1 -1;
+	{false,true} -> 0;
+	{false,false} -> 1
+    end.
+
+map_gen(MapMod, Pairs, Size) ->
+    {_,L} = lists:foldl(fun(_, {Keys, Acc}) ->
+				KI = random:uniform(size(Keys)),
+				K = element(KI,Keys),
+				KV = element(random:uniform(size(K)), K),
+				{erlang:delete_element(KI,Keys), [KV | Acc]}
+			end,
+			{Pairs, []},
+			lists:seq(1,Size)),
+
+    map_from_list(MapMod, L).
+
+-define(NO_MAPS, 1). % Todo: Remove NO_MAPS when hashing of hashmaps is implemented
+-define(NO_LEAF, 2).
+
+recursive_compare() ->
+    Leafs = {atom, 17, 16.9, 17.1, [], self(), spawn(fun() -> ok end), make_ref(), make_ref()},
+    {A, B} = term_gen_recursive(Leafs, ?NO_LEAF),
+    %erlang:display({"Recursive term A", A}),
+    %erlang:display({"Recursive term B", B}),
+
+    {true,false} =  case do_cmp(A, B, false) of
+			-1 -> {A=B};
+			0 -> {A==B, A/=B};
+			1 -> {A>B, A=
+    C = cmp(A, B, Exact),
+    io:format("cmp = ~p\n", [C]),
+    C.
+
+
+%% Generate two terms {A,B} that may only differ
+%% at float vs integer types.
+term_gen_recursive(Leafs, Flags0) ->
+    Rnd = case Flags0 of
+	      0 -> random:uniform(size(Leafs)+3);
+	      ?NO_MAPS -> random:uniform(size(Leafs)+2) + 1;
+	      ?NO_LEAF -> random:uniform(3)
+	  end,
+    Flags1 = Flags0 band (bnot ?NO_LEAF),
+    case Rnd of
+	1 -> % Make hashmap
+	    Size = random:uniform(size(Leafs)),
+	    %%io:format("Generate hashmap with size ~p:\n", [Size]),
+	    lists:foldl(fun(_, {Acc1,Acc2}) ->
+				{K1,K2} = term_gen_recursive(Leafs, Flags1 bor ?NO_MAPS),
+				{V1,V2} = term_gen_recursive(Leafs, Flags1),
+				%%io:format("hashmap:put(~p, ~p)\n", [K,V]),
+				{hashmap:put(K1,V1, Acc1), hashmap:put(K2,V2, Acc2)}
+			end,
+			{hashmap:new(), hashmap:new()},
+			lists:seq(1,Size));
+	2 -> % Make cons
+	    {Car1,Car2} = term_gen_recursive(Leafs, Flags1),
+	    {Cdr1,Cdr2} = term_gen_recursive(Leafs, Flags1),
+	    {[Car1 | Cdr1], [Car2 | Cdr2]};
+	3 -> % Make tuple
+	    Size = random:uniform(size(Leafs)),
+	    L = lists:map(fun(_) -> term_gen_recursive(Leafs, Flags1) end,
+			  lists:seq(1,Size)),
+	    {L1, L2} = lists:unzip(L),
+	    {list_to_tuple(L1), list_to_tuple(L2)};
+
+	N -> % Make leaf
+	    case element(N-3, Leafs) of
+		I when is_integer(I) ->
+		    case random:uniform(4) of
+			1 -> {I, float(I)};
+			2 -> {float(I), I};
+			_ -> {I,I}
+		    end;
+		T -> {T,T}
+	    end
+    end.
+
+map_from_list(maps, L) ->
+    maps:from_list(L);
+map_from_list(hashmap, L) ->  %% while waiting for Egil...
+    lists:foldl(fun({K,V},Acc) -> hashmap:put(K,V,Acc) end,
+		hashmap:new(),
+		L).
+
+
 %% BIFs
 t_bif_map_get(Config) when is_list(Config) ->
 
-- 
cgit v1.2.3


From 04237c0948fdaba9af1a50c7fd9c5512afd10bb2 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Fri, 13 Feb 2015 19:45:45 +0100
Subject: erts: Make hashmap use the new hash function

---
 erts/emulator/beam/erl_hashmap.c | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index b423111715..583ff52c26 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -48,10 +48,12 @@
 #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
 #endif
 
+#define hashmap_make_hash(Key) make_hash_vsn(Key, 3)
+
 #define hashmap_restore_hash(Heap,Lvl,Key) \
-    ((Lvl) < 8) ? make_hash2(Key) >> (4*(Lvl)) : make_hash2(CONS(Heap, make_small(Lvl), (Key))) >> (4*((Lvl) & 7))
+    ((Lvl) < 8) ? hashmap_make_hash(Key) >> (4*(Lvl)) : hashmap_make_hash(CONS(Heap, make_small(Lvl), (Key))) >> (4*((Lvl) & 7))
 #define hashmap_shift_hash(Heap,Hx,Lvl,Key) \
-    ((++(Lvl)) & 7) ? (Hx) >> 4 : make_hash2(CONS(Heap, make_small(Lvl), Key))
+    ((++(Lvl)) & 7) ? (Hx) >> 4 : hashmap_make_hash(CONS(Heap, make_small(Lvl), Key))
 
 #if 0
 static char *format_binary(Uint64 x, char *b) {
@@ -93,7 +95,7 @@ BIF_RETTYPE hashmap_new_0(BIF_ALIST_0) {
 
 BIF_RETTYPE hashmap_put_3(BIF_ALIST_3) {
     if (is_hashmap(BIF_ARG_3)) {
-	Uint32 hx = make_hash2(BIF_ARG_1);
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
 	Eterm map;
 
 	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 0);
@@ -107,7 +109,7 @@ BIF_RETTYPE hashmap_put_3(BIF_ALIST_3) {
 
 BIF_RETTYPE hashmap_update_3(BIF_ALIST_3) {
     if (is_hashmap(BIF_ARG_3)) {
-	Uint32 hx = make_hash2(BIF_ARG_1);
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
 	Eterm map;
 
 	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 1);
@@ -132,7 +134,7 @@ BIF_RETTYPE hashmap_to_list_1(BIF_ALIST_1) {
 BIF_RETTYPE hashmap_get_2(BIF_ALIST_2) {
     if (is_hashmap(BIF_ARG_2)) {
 	const Eterm *value;
-	Uint32 hx = make_hash2(BIF_ARG_1);
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
 
 	if ((value = hashmap_get(hx, BIF_ARG_1, BIF_ARG_2)) != NULL) {
 	    BIF_RET(*value);
@@ -147,7 +149,7 @@ BIF_RETTYPE hashmap_find_2(BIF_ALIST_2) {
     if (is_hashmap(BIF_ARG_2)) {
 	Eterm *hp, res;
 	const Eterm *value;
-	Uint32 hx = make_hash2(BIF_ARG_1);
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
 
 	if ((value = hashmap_get(hx, BIF_ARG_1, BIF_ARG_2)) != NULL) {
 	    hp    = HAlloc(BIF_P, 3);
@@ -167,7 +169,7 @@ BIF_RETTYPE hashmap_find_2(BIF_ALIST_2) {
 
 BIF_RETTYPE hashmap_remove_2(BIF_ALIST_2) {
     if (is_hashmap(BIF_ARG_2)) {
-	Uint32 hx = make_hash2(BIF_ARG_1);
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
 
 	BIF_RET(hashmap_delete(BIF_P, hx, BIF_ARG_1, BIF_ARG_2));
     }
@@ -204,7 +206,7 @@ BIF_RETTYPE is_hashmap_1(BIF_ALIST_1) {
 
 BIF_RETTYPE hashmap_is_key_2(BIF_ALIST_2) {
     if (is_hashmap(BIF_ARG_1)) {
-	Uint32 hx = make_hash2(BIF_ARG_1);
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
 
 	BIF_RET(hashmap_get(hx, BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
     }
-- 
cgit v1.2.3


From 354ec7fe8ee31cde73c811bd5dea4f3e6787d10a Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 17 Feb 2015 20:10:46 +0100
Subject: erts: Add hashing of hashmaps

---
 erts/emulator/beam/utils.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index f79cb8db7d..0f2d89bbfe 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1288,6 +1288,55 @@ make_hash2(Eterm term)
 		goto hash2_common;
 	    }
 	    break;
+            case HASHMAP_SUBTAG:
+            {
+                Eterm* ptr = boxed_val(term) + 1;
+                Uint size;
+                int i;
+                switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
+                case HAMT_SUBTAG_HEAD_ARRAY:
+                case HAMT_SUBTAG_HEAD_BITMAP:
+                    size = *ptr++;
+                    UINT32_HASH(size, HCONST_16);
+                    if (size == 0)
+                        goto hash2_common;
+                    ESTACK_PUSH(s, hash_xor_values);
+                    ESTACK_PUSH(s, hash_xor_keys);
+                    ESTACK_PUSH(s, hash);
+                    ESTACK_PUSH(s, HASH_MAP_TAIL);
+                    hash = 0;
+                    hash_xor_keys = 0;
+                    hash_xor_values = 0;
+                }
+                switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
+                case HAMT_SUBTAG_HEAD_ARRAY:
+                case HAMT_SUBTAG_NODE_ARRAY:
+                    i = 16;
+                    break;
+                case HAMT_SUBTAG_HEAD_BITMAP:
+                case HAMT_SUBTAG_NODE_BITMAP:
+                    i = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+                    break;
+                default:
+                    erl_exit(1, "bad header");
+                }
+                while (i) {
+                    if (is_list(*ptr)) {
+                        Eterm* cons = list_val(*ptr);
+                        ESTACK_PUSH(s, HASH_MAP_KEY);
+                        ESTACK_PUSH(s, CAR(cons));
+                        ESTACK_PUSH(s, HASH_MAP_VAL);
+                        ESTACK_PUSH(s, CDR(cons));
+                    }
+                    else {
+                        ASSERT(is_boxed(*ptr));
+                        ESTACK_PUSH(s, *ptr);
+                    }
+                    i--; ptr++;
+                }
+                goto hash2_common;
+            }
+            break;
 	    case EXPORT_SUBTAG:
 	    {
 		Export* ep = *((Export **) (export_val(term) + 1));
-- 
cgit v1.2.3


From 7e3c3c7106095cc5afdbd5cd7e96b907784ada01 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 17 Feb 2015 19:11:24 +0100
Subject: erts: Fix bug in hashmap_restore/shift_hash

Deep hashing should ignore the three lowest bits of the level.
---
 erts/emulator/beam/erl_hashmap.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 583ff52c26..6bca2f1665 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -51,9 +51,9 @@
 #define hashmap_make_hash(Key) make_hash_vsn(Key, 3)
 
 #define hashmap_restore_hash(Heap,Lvl,Key) \
-    ((Lvl) < 8) ? hashmap_make_hash(Key) >> (4*(Lvl)) : hashmap_make_hash(CONS(Heap, make_small(Lvl), (Key))) >> (4*((Lvl) & 7))
+    (((Lvl) < 8) ? hashmap_make_hash(Key) >> (4*(Lvl)) : hashmap_make_hash(CONS(Heap, make_small((Lvl)>>3), (Key))) >> (4*((Lvl) & 7)))
 #define hashmap_shift_hash(Heap,Hx,Lvl,Key) \
-    ((++(Lvl)) & 7) ? (Hx) >> 4 : hashmap_make_hash(CONS(Heap, make_small(Lvl), Key))
+    (((++(Lvl)) & 7) ? (Hx) >> 4 : hashmap_make_hash(CONS(Heap, make_small((Lvl)>>3), Key)))
 
 #if 0
 static char *format_binary(Uint64 x, char *b) {
-- 
cgit v1.2.3


From af34cc859ae9979e9f4f8abf29646efd6b1c9c04 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 17 Feb 2015 19:21:06 +0100
Subject: erts: Add ERTS_UNDEF macro

---
 erts/emulator/beam/sys.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 828f5b427a..d2a8b9e7f4 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -188,6 +188,16 @@ __decl_noreturn void __noreturn erl_assert_error(const char* expr, const char *f
 #  define ASSERT(e) ((void) 1)
 #endif
 
+/* ERTS_UNDEF can be used to silence false warnings about
+ * "variable may be used uninitialized" while keeping the variable
+ * marked as undefined by valgrind.
+ */
+#ifdef VALGRIND
+#  define ERTS_UNDEF(V,I)
+#else
+#  define ERTS_UNDEF(V,I) V = I
+#endif
+
 /*
  * Compile time assert
  * (the actual compiler error msg can be a bit confusing)
-- 
cgit v1.2.3


From 7e3b9d5ab2e0c53b606a653896f3a2857ea5cbce Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 17 Feb 2015 19:27:45 +0100
Subject: erts: Add micro optimization to phash2 of tuples

---
 erts/emulator/beam/utils.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 0f2d89bbfe..3f9cb5dbea 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1252,11 +1252,12 @@ make_hash2(Eterm term)
 		UINT32_HASH(arity, HCONST_9);
 		if (arity == 0) /* Empty tuple */ 
 		    goto hash2_common;
-		for (i = arity; i >= 1; i--) {
-		    tmp = elem[i];
-		    ESTACK_PUSH(s, tmp);
+		for (i = arity; ; i--) {
+		    term = elem[i];
+                    if (i == 1)
+                        break;
+                    ESTACK_PUSH(s, term);
 		}
-		goto hash2_common;
 	    }
 	    break;
 	    case MAP_SUBTAG:
-- 
cgit v1.2.3


From 70bb13c626ffbffc9c7d6fbe1d69e91dd0a853be Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 17 Feb 2015 19:35:59 +0100
Subject: erts: Change phash2 of maps to be sensitive to key-value combos.

The old hashing did not care which value belonged to which key,
for example:

would hash the same.
---
 erts/emulator/beam/utils.c       | 67 ++++++++++++++++++----------------------
 erts/emulator/test/map_SUITE.erl | 18 +++++------
 2 files changed, 39 insertions(+), 46 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 3f9cb5dbea..d234e8fc68 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1133,10 +1133,11 @@ Uint32
 make_hash2(Eterm term)
 {
     Uint32 hash;
-    Uint32 hash_xor_keys   = 0;
-    Uint32 hash_xor_values = 0;
+    Uint32 hash_xor_pairs;
     DeclareTmpHeapNoproc(tmp_big,2);
 
+    ERTS_UNDEF(hash_xor_pairs, 0);
+
 /* (HCONST * {2, ..., 16}) mod 2^32 */
 #define HCONST_2 0x3c6ef372UL
 #define HCONST_3 0xdaa66d2bUL
@@ -1155,8 +1156,8 @@ make_hash2(Eterm term)
 #define HCONST_16 0xe3779b90UL
 
 #define HASH_MAP_TAIL (_make_header(1,_TAG_HEADER_REF))
-#define HASH_MAP_KEY  (_make_header(2,_TAG_HEADER_REF))
-#define HASH_MAP_VAL  (_make_header(3,_TAG_HEADER_REF))
+#define HASH_MAP_PAIR (_make_header(2,_TAG_HEADER_REF))
+#define HASH_CDR      (_make_header(3,_TAG_HEADER_REF))
 
 #define UINT32_HASH_2(Expr1, Expr2, AConst)       \
          do {                                     \
@@ -1233,9 +1234,9 @@ make_hash2(Eterm term)
 	    if (c > 0)
 		UINT32_HASH(sh, HCONST_4);
 	    if (is_list(term)) {
-		term = *ptr;
-		tmp = *++ptr;
-		ESTACK_PUSH(s, tmp);	    
+		tmp = CDR(ptr);
+                ESTACK_PUSH(s, tmp);
+		term = CAR(ptr);
 	    }
 	}
 	break;
@@ -1271,20 +1272,20 @@ make_hash2(Eterm term)
 		if (size == 0) {
 		    goto hash2_common;
 		}
-		ESTACK_PUSH4(s, hash_xor_values, hash_xor_keys, hash, HASH_MAP_TAIL);
-		hash = 0;
-		hash_xor_keys = 0;
-		hash_xor_values = 0;
-		for (i = size - 1; i >= 0; i--) {
-		    tmp = vs[i];
-		    ESTACK_PUSH2(s, HASH_MAP_VAL, tmp);
-		}
-		/* We do not want to expose the tuple representation.
-		 * Do not push the keys as a tuple.
+		/* We want a portable hash function that is *independent* of
+		 * the order in which keys and values are encountered.
+		 * We therefore calculate context independent hashes for all    				      .
+		 * key-value pairs and then xor them together.
 		 */
+		ESTACK_PUSH(s, hash_xor_pairs);
+		ESTACK_PUSH(s, hash);
+		ESTACK_PUSH(s, HASH_MAP_TAIL);
+		hash = 0;
+		hash_xor_pairs = 0;
 		for (i = size - 1; i >= 0; i--) {
-		    tmp = ks[i];
-		    ESTACK_PUSH2(s, HASH_MAP_KEY, tmp);
+		    ESTACK_PUSH(s, HASH_MAP_PAIR);
+		    ESTACK_PUSH(s, vs[i]);
+                    ESTACK_PUSH(s, ks[i]);
 		}
 		goto hash2_common;
 	    }
@@ -1301,13 +1302,11 @@ make_hash2(Eterm term)
                     UINT32_HASH(size, HCONST_16);
                     if (size == 0)
                         goto hash2_common;
-                    ESTACK_PUSH(s, hash_xor_values);
-                    ESTACK_PUSH(s, hash_xor_keys);
+                    ESTACK_PUSH(s, hash_xor_pairs);
                     ESTACK_PUSH(s, hash);
                     ESTACK_PUSH(s, HASH_MAP_TAIL);
                     hash = 0;
-                    hash_xor_keys = 0;
-                    hash_xor_values = 0;
+                    hash_xor_pairs = 0;
                 }
                 switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
                 case HAMT_SUBTAG_HEAD_ARRAY:
@@ -1324,10 +1323,9 @@ make_hash2(Eterm term)
                 while (i) {
                     if (is_list(*ptr)) {
                         Eterm* cons = list_val(*ptr);
-                        ESTACK_PUSH(s, HASH_MAP_KEY);
-                        ESTACK_PUSH(s, CAR(cons));
-                        ESTACK_PUSH(s, HASH_MAP_VAL);
+                        ESTACK_PUSH(s, HASH_MAP_PAIR);
                         ESTACK_PUSH(s, CDR(cons));
+                        ESTACK_PUSH(s, CAR(cons));
                     }
                     else {
                         ASSERT(is_boxed(*ptr));
@@ -1437,7 +1435,8 @@ make_hash2(Eterm term)
 		do {
 		    Uint t;
 		    Uint32 x, y;
-		    t = i < n ? BIG_DIGIT(ptr, i++) : 0;
+                    ASSERT(i < n);
+		    t = BIG_DIGIT(ptr, i++);
 		    x = t & 0xffffffff;
 		    y = t >> 32;
 		    UINT32_HASH_2(x, y, con);
@@ -1545,18 +1544,12 @@ make_hash2(Eterm term)
 	    switch (term) {
 		case HASH_MAP_TAIL: {
 		    hash = (Uint32) ESTACK_POP(s);
-		    UINT32_HASH(hash_xor_keys, HCONST_16);
-		    UINT32_HASH(hash_xor_values, HCONST_16);
-		    hash_xor_keys = (Uint32) ESTACK_POP(s);
-		    hash_xor_values = (Uint32) ESTACK_POP(s);
+                    UINT32_HASH(hash_xor_pairs, HCONST_19);
+		    hash_xor_pairs = (Uint32) ESTACK_POP(s);
 		    goto hash2_common;
 		}
-		case HASH_MAP_KEY:
-		    hash_xor_keys ^= hash;
-		    hash = 0;
-		    goto hash2_common;
-		case HASH_MAP_VAL:
-		    hash_xor_values ^= hash;
+		case HASH_MAP_PAIR:
+		    hash_xor_pairs ^= hash;
 		    hash = 0;
 		    goto hash2_common;
 		default:
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 5944450f33..0205c52c98 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -976,21 +976,21 @@ t_erlang_hash(Config) when is_list(Config) ->
 t_bif_erlang_phash2() ->
 
     39679005 = erlang:phash2(#{}),
-    78942764 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }),
-    37338230 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }),
-    14363616 = erlang:phash2(#{ 1 => a }),
-    51612236 = erlang:phash2(#{ a => 1 }),
+    33667975 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), % 78942764
+    95332690 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), % 37338230
+    108954384 = erlang:phash2(#{ 1 => a }), % 14363616
+    59617982 = erlang:phash2(#{ a => 1 }), % 51612236
 
-    37468437 = erlang:phash2(#{{} => <<>>}),
-    44049159 = erlang:phash2(#{<<>> => {}}),
+    42770201 = erlang:phash2(#{{} => <<>>}), % 37468437
+    71687700 = erlang:phash2(#{<<>> => {}}), % 44049159
 
     M0 = #{ a => 1, "key" => <<"value">> },
     M1 = maps:remove("key",M0),
     M2 = M1#{ "key" => <<"value">> },
 
-    118679416 = erlang:phash2(M0),
-    51612236  = erlang:phash2(M1),
-    118679416 = erlang:phash2(M2),
+    70249457 = erlang:phash2(M0), % 118679416
+    59617982 = erlang:phash2(M1), % 51612236
+    70249457 = erlang:phash2(M2), % 118679416
     ok.
 
 t_bif_erlang_phash() ->
-- 
cgit v1.2.3


From 3cdd2d25859275fd0bc2111c52a8bbd2505ff048 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 17 Feb 2015 20:11:04 +0100
Subject: erts: Improve map_SUITE:t_map_compare

---
 erts/emulator/test/map_SUITE.erl | 84 ++++++++++++++++++++++++++--------------
 1 file changed, 54 insertions(+), 30 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 0205c52c98..aa835f251b 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -486,12 +486,12 @@ t_map_compare(Config) when is_list(Config) ->
     random:seed(Seed),
     repeat(100, fun(_) -> float_int_compare(maps) end, []),
     repeat(100, fun(_) -> float_int_compare(hashmap) end, []),
-    repeat(1000, fun(_) -> recursive_compare() end, []),
+    repeat(100, fun(_) -> recursive_compare() end, []),
     ok.
 
 float_int_compare(MapMod) ->
     Terms = numeric_keys(3),
-    io:format("Keys to use: ~p\n", [Terms]),
+    %%io:format("Keys to use: ~p\n", [Terms]),
     Pairs = lists:map(fun(K) -> list_to_tuple([{K,V} || V <- Terms]) end, Terms),
     lists:foreach(fun(Size) ->
 			  MapGen = fun() -> map_gen(MapMod, list_to_tuple(Pairs), Size) end,
@@ -635,62 +635,65 @@ map_gen(MapMod, Pairs, Size) ->
 
     map_from_list(MapMod, L).
 
--define(NO_MAPS, 1). % Todo: Remove NO_MAPS when hashing of hashmaps is implemented
--define(NO_LEAF, 2).
 
 recursive_compare() ->
     Leafs = {atom, 17, 16.9, 17.1, [], self(), spawn(fun() -> ok end), make_ref(), make_ref()},
-    {A, B} = term_gen_recursive(Leafs, ?NO_LEAF),
-    %erlang:display({"Recursive term A", A}),
-    %erlang:display({"Recursive term B", B}),
-
-    {true,false} =  case do_cmp(A, B, false) of
-			-1 -> {A=B};
-			0 -> {A==B, A/=B};
-			1 -> {A>B, A= {A=B};
+				 0 -> {A==B, A/=B};
+				 1 -> {A>B, A=
     C = cmp(A, B, Exact),
-    io:format("cmp = ~p\n", [C]),
     C.
 
-
 %% Generate two terms {A,B} that may only differ
 %% at float vs integer types.
-term_gen_recursive(Leafs, Flags0) ->
-    Rnd = case Flags0 of
-	      0 -> random:uniform(size(Leafs)+3);
-	      ?NO_MAPS -> random:uniform(size(Leafs)+2) + 1;
-	      ?NO_LEAF -> random:uniform(3)
+term_gen_recursive(Leafs, Flags, Depth) ->
+    MaxDepth = 10,
+    Rnd = case {Flags, Depth} of
+	      {_, MaxDepth} -> % Only leafs
+		  random:uniform(size(Leafs)) + 3;
+	      {0, 0} ->        % Only containers
+		  random:uniform(3);
+	      {0,_} ->         % Anything
+		  random:uniform(size(Leafs)+3)
 	  end,
-    Flags1 = Flags0 band (bnot ?NO_LEAF),
     case Rnd of
 	1 -> % Make hashmap
 	    Size = random:uniform(size(Leafs)),
 	    %%io:format("Generate hashmap with size ~p:\n", [Size]),
 	    lists:foldl(fun(_, {Acc1,Acc2}) ->
-				{K1,K2} = term_gen_recursive(Leafs, Flags1 bor ?NO_MAPS),
-				{V1,V2} = term_gen_recursive(Leafs, Flags1),
+				{K1,K2} = term_gen_recursive(Leafs, Flags,
+							     Depth+1),
+				{V1,V2} = term_gen_recursive(Leafs, Flags, Depth+1),
 				%%io:format("hashmap:put(~p, ~p)\n", [K,V]),
+				%%ok = check_keys(K1,K2, 0),
 				{hashmap:put(K1,V1, Acc1), hashmap:put(K2,V2, Acc2)}
 			end,
 			{hashmap:new(), hashmap:new()},
 			lists:seq(1,Size));
 	2 -> % Make cons
-	    {Car1,Car2} = term_gen_recursive(Leafs, Flags1),
-	    {Cdr1,Cdr2} = term_gen_recursive(Leafs, Flags1),
+	    {Car1,Car2} = term_gen_recursive(Leafs, Flags, Depth+1),
+	    {Cdr1,Cdr2} = term_gen_recursive(Leafs, Flags, Depth+1),
 	    {[Car1 | Cdr1], [Car2 | Cdr2]};
 	3 -> % Make tuple
 	    Size = random:uniform(size(Leafs)),
-	    L = lists:map(fun(_) -> term_gen_recursive(Leafs, Flags1) end,
+	    L = lists:map(fun(_) -> term_gen_recursive(Leafs, Flags, Depth+1) end,
 			  lists:seq(1,Size)),
 	    {L1, L2} = lists:unzip(L),
 	    {list_to_tuple(L1), list_to_tuple(L2)};
@@ -715,6 +718,27 @@ map_from_list(hashmap, L) ->  %% while waiting for Egil...
 		L).
 
 
+check_keys(K1, K2, _) when K1 =:= K2 ->
+    case erlang:phash3(K1) =:= erlang:phash3(K2) of
+	true -> ok;
+	false ->
+	    io:format("Same keys with different hash values !!!\nK1 = ~p\nK2 = ~p\n", [K1,K2]),
+	    error
+    end;
+check_keys(K1, K2, 0) ->
+    case {erlang:phash3(K1), erlang:phash3(K2)} of
+	{H,H} -> check_keys(K1, K2, 1);
+	{_,_} -> ok
+    end;
+check_keys(K1, K2, L) when L < 10 ->
+    case {erlang:phash3([L|K1]), erlang:phash3([L|K2])} of
+	{H,H} -> check_keys(K1, K2, L+1);
+	{_,_} -> ok
+    end;
+check_keys(K1, K2, L) ->
+    io:format("Same hash value at level ~p !!!\nK1 = ~p\nK2 = ~p\n", [L,K1,K2]),
+    error.
+
 %% BIFs
 t_bif_map_get(Config) when is_list(Config) ->
 
-- 
cgit v1.2.3


From 0d10df770cfd1407bb79c31da34108dda1b868a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Wed, 21 Jan 2015 16:08:17 +0100
Subject: erts: Add fallback for builtin clz

* __builtin_clz may not exist
* fix bitcount fallback
---
 erts/emulator/beam/erl_hashmap.c | 27 +++++++++++++++++++++++++++
 erts/emulator/beam/erl_hashmap.h | 17 ++++-------------
 2 files changed, 31 insertions(+), 13 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 6bca2f1665..3da2a53333 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -1273,3 +1273,30 @@ BIF_RETTYPE hashmap_info_1(BIF_ALIST_1) {
     }
     BIF_ERROR(BIF_P, BADARG);
 }
+
+/* implementation of builtin emulations */
+
+#if !defined(__GNUC__)
+/* Count leading zeros emulation */
+Uint32 hashmap_clz(Uint32 x) {
+    Uint32 y;
+    int n = 32;
+    y = x >>16;  if (y != 0) {n = n -16;  x = y;}
+    y = x >> 8;  if (y != 0) {n = n - 8;  x = y;}
+    y = x >> 4;  if (y != 0) {n = n - 4;  x = y;}
+    y = x >> 2;  if (y != 0) {n = n - 2;  x = y;}
+    y = x >> 1;  if (y != 0) return n - 2;
+    return n - x;
+}
+const Uint32 SK5 = 0x55555555, SK3 = 0x33333333;
+const Uint32 SKF0 = 0xF0F0F0F, SKFF = 0xFF00FF;
+
+/* CTPOP emulation */
+Uint32 hashmap_bitcount(Uint32 x) {
+    x -= ((x >> 1  ) & SK5);
+    x  =  (x & SK3 ) + ((x >> 2 ) & SK3 );
+    x  =  (x & SKF0) + ((x >> 4 ) & SKF0);
+    x +=   x >> 8;
+    return (x + (x >> 16)) & 0x3F;
+}
+#endif
diff --git a/erts/emulator/beam/erl_hashmap.h b/erts/emulator/beam/erl_hashmap.h
index 8ba249b053..1964787218 100644
--- a/erts/emulator/beam/erl_hashmap.h
+++ b/erts/emulator/beam/erl_hashmap.h
@@ -40,21 +40,12 @@ int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
 
 /* HASH */
 
-
 #if defined(__GNUC__)
-#define hashmap_bitcount(x) 	(Uint32) __builtin_popcount((unsigned int) (x))
+#define hashmap_clz(x)       ((Uint32) __builtin_clz((unsigned int)(x)))
+#define hashmap_bitcount(x)  ((Uint32) __builtin_popcount((unsigned int) (x)))
 #else
-const Uint32 SK5 = 0x55555555, SK3 = 0x33333333;
-const Uint32 SKF0 = 0xF0F0F0F, SKFF = 0xFF00FF;
-
-/* CTPOP emulation */
-Uint32 hashmap_bitcount(Uint32 map) {
-    map -= (( map >> 1  ) & SK5 );
-    map  = ( map & SK3  ) + (( map >> 2 ) & SK3 );
-    map  = ( map & SKF0 ) + (( map >> 4 ) & SKF0);
-    map +=   map >> 8;
-    return ( map + ( map >> 16)) & 0x3F;
-}
+Uint32 hashmap_clz(Uint32 x);
+Uint32 hashmap_bitcount(Uint32 x);
 #endif
 
 /* hamt nodes v2.0
-- 
cgit v1.2.3


From e179bd5be24b6b07cc4e8d0e58211fd62599ee9d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 3 Feb 2015 15:00:44 +0100
Subject: erts: Fix hashmap head array printf term

---
 erts/emulator/beam/erl_printf_term.c | 1 +
 1 file changed, 1 insertion(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c
index f07fe30cf6..b07b7785dd 100644
--- a/erts/emulator/beam/erl_printf_term.c
+++ b/erts/emulator/beam/erl_printf_term.c
@@ -594,6 +594,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
 		head = hashmap_val(wobj);
 		mapval = MAP_HEADER_VAL(*head);
 		switch (MAP_HEADER_TYPE(*head)) {
+		    case MAP_HEADER_TAG_HAMT_HEAD_ARRAY:
 		    case MAP_HEADER_TAG_HAMT_HEAD_BITMAP:
 			PRINT_STRING(res, fn, arg, "#<");
 			PRINT_UWORD(res, fn, arg, 'x', 0, 1, mapval);
-- 
cgit v1.2.3


From 831ac12e04004c2e93aafc9f52264a57757fa2eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Wed, 10 Dec 2014 17:52:28 +0100
Subject: erts: Add hashmap:from_list/1

---
 erts/emulator/beam/bif.tab       |   1 +
 erts/emulator/beam/erl_hashmap.c | 415 +++++++++++++++++++++++++++++++++++++++
 erts/emulator/beam/erl_hashmap.h |   3 +
 3 files changed, 419 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 27ae8adcec..5ffd5b37b5 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -621,6 +621,7 @@ bif hashmap:find/2
 bif hashmap:update/3
 bif hashmap:remove/2
 bif hashmap:info/1
+bif hashmap:from_list/1
 bif hashmap:to_list/1
 bif hashmap:new/0
 bif hashmap:is_key/2
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 3da2a53333..ddb55c53ce 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -67,14 +67,28 @@ static char *format_binary(Uint64 x, char *b) {
 #endif
 
 static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node, int is_update);
+
+/* for hashmap_from_list/1 */
+typedef struct {
+    Uint32 hx;
+    Uint32 skip;
+    Uint i;
+    Eterm  val;
+} hxnode_t;
+
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
+static Eterm hashmap_from_list(Process *p, Eterm node);
 static Eterm hashmap_to_list(Process *p, Eterm map);
 static Eterm hashmap_keys(Process *p, Eterm map);
 static Eterm hashmap_values(Process *p, Eterm map);
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 
+static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
+static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
+static int hxnodecmp(hxnode_t* a, hxnode_t* b);
+static int hxnodecmpkey(hxnode_t* a, hxnode_t* b);
 
 /* hashmap:new/0 */
 
@@ -129,6 +143,15 @@ BIF_RETTYPE hashmap_to_list_1(BIF_ALIST_1) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
+/* hashmap:from_list/1 */
+
+BIF_RETTYPE hashmap_from_list_1(BIF_ALIST_1) {
+    if (is_list(BIF_ARG_1) || is_nil(BIF_ARG_1)) {
+	return hashmap_from_list(BIF_P, BIF_ARG_1);
+    }
+
+    BIF_ERROR(BIF_P, BADARG);
+}
 /* hashmap:get/2 */
 
 BIF_RETTYPE hashmap_get_2(BIF_ALIST_2) {
@@ -794,6 +817,398 @@ not_found:
     return res;
 }
 
+#define swizzle32(D,S) \
+    do { \
+	(D) = ((S) & 0x0000000f) << 28 | ((S) & 0x000000f0) << 20  \
+	    | ((S) & 0x00000f00) << 12 | ((S) & 0x0000f000) << 4   \
+	    | ((S) & 0x000f0000) >> 4  | ((S) & 0x00f00000) >> 12  \
+	    | ((S) & 0x0f000000) >> 20 | ((S) & 0xf0000000) >> 28; \
+    } while(0)
+
+
+static Eterm hashmap_from_list(Process *p, Eterm list) {
+    Eterm *kv, res, item = list;
+    Eterm *hp;
+    Eterm tmp[2];
+    Uint32 sw, hx;
+    Uint jx = 0, ix = 0, lx, cx, n = 0;
+    hxnode_t *hxns;
+
+    /* Calculate size and check validity */
+
+    if (is_nil(list)) {
+	hp    = HAlloc(p, HAMT_HEAD_EMPTY_SZ);
+	hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(0);
+	hp[1] = 0;
+	return make_hashmap(hp);
+    }
+
+    while(is_list(item)) {
+	res = CAR(list_val(item));
+	if (is_not_tuple(res))
+	    goto error;
+
+	kv = tuple_val(res);
+	if (*kv != make_arityval(2))
+	    goto error;
+
+	n++;
+	item = CDR(list_val(item));
+    }
+
+    if (is_not_nil(item))
+	goto error;
+
+    hp = HAlloc(p, (2 * n));
+
+    /* create tmp hx values and leaf ptrs */
+    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t));
+    item = list;
+
+    while(is_list(item)) {
+	res = CAR(list_val(item));
+	kv  = tuple_val(res);
+	hx  = hashmap_restore_hash(tmp,0,kv[1]);
+	swizzle32(sw,hx);
+	hxns[ix].hx   = sw;
+	hxns[ix].val  = CONS(hp, kv[1], kv[2]); hp += 2;
+	hxns[ix].skip = 1; /* will be reassigned in from_array */
+	hxns[ix].i    = ix;
+	ix++;
+	item = CDR(list_val(item));
+    }
+
+    ASSERT(n > 0);
+
+    /* sort and compact array (remove non-unique entries) */
+    qsort(hxns, n, sizeof(hxnode_t), (int (*)(const void *, const void *)) hxnodecmp);
+
+    ix = 0, cx = 0;
+    while(ix < n - 1) {
+	if (hxns[ix].hx == hxns[ix+1].hx) {
+
+	    /* find region of equal hash values */
+	    jx = ix + 1;
+	    while(jx < n && hxns[ix].hx == hxns[jx].hx) { jx++; }
+	    /* find all correct keys from region
+	     * (last in list but now hash sorted so we check highest id instead) */
+
+	    /* resort with keys instead of hash value within region */
+	    
+	    qsort(&hxns[ix], jx - ix, sizeof(hxnode_t),
+		    (int (*)(const void *, const void *)) hxnodecmpkey);
+
+	    while(ix < jx) {
+		lx = ix;
+		while(ix < jx && EQ(CAR(list_val(hxns[ix].val)), CAR(list_val(hxns[lx].val)))) {
+		    if (hxns[ix].i > hxns[lx].i) {
+			lx = ix;
+		    }
+		    ix++;
+		}
+		hxns[cx].hx  = hxns[lx].hx;
+		hxns[cx].val = hxns[lx].val;
+		cx++;
+	    }
+	    ix = jx;
+	    continue;
+	}
+	if (ix > cx) {
+	    hxns[cx].hx  = hxns[ix].hx;
+	    hxns[cx].val = hxns[ix].val;
+	}
+	cx++;
+	ix++;
+    }
+
+    if (ix < n) {
+	hxns[cx].hx  = hxns[ix].hx;
+	hxns[cx].val = hxns[ix].val;
+	cx++;
+    }
+
+    if (cx > 1) {
+	/* recursive decompose array */
+	res = hashmap_from_sorted_unique_array(p, hxns, cx, 0);
+    } else {
+	 /* hash value has been swizzled, need to drag it down to get the
+	 * correct slot. */
+	hp    = HAlloc(p, HAMT_HEAD_BITMAP_SZ(1));
+	hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(1 << ((hxns[0].hx >> 0x1c) & 0xf));
+	hp[1] = 1;
+	hp[2] = hxns[0].val;
+	res   = make_hashmap(hp);
+    }
+
+    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+
+    BIF_RET(res);
+error:
+    BIF_ERROR(p, BADARG);
+}
+
+
+static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n, int lvl) {
+    Eterm res = NIL;
+    Uint i,ix,jx,elems;
+    Uint32 sw, hx;
+    Eterm val;
+    Eterm th[2];
+    hxnode_t *tmp;
+
+    ASSERT(lvl < 32);
+    ix = 0;
+    elems = 1;
+    while (ix < n - 1) {
+	if (hxns[ix].hx == hxns[ix+1].hx) {
+	    jx = ix + 1;
+	    while (jx < n && hxns[ix].hx == hxns[jx].hx) { jx++; }
+	    tmp = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, ((jx - ix)) * sizeof(hxnode_t));
+
+	    for(i = 0; i < jx - ix; i++) {
+		val = hxns[i + ix].val;
+		hx  = hashmap_restore_hash(th, lvl + 8, CAR(list_val(val)));
+		swizzle32(sw,hx);
+		tmp[i].hx   = sw;
+		tmp[i].val  = val;
+		tmp[i].i    = i;
+		tmp[i].skip = 1;
+	    }
+
+	    qsort(tmp, jx - ix, sizeof(hxnode_t), (int (*)(const void *, const void *)) hxnodecmp);
+
+	    hxns[ix].skip = jx - ix;
+	    hxns[ix].val  = hashmap_from_sorted_unique_array(p, tmp, jx - ix, lvl + 8);
+	    erts_free(ERTS_ALC_T_TMP, (void *) tmp);
+	    ix = jx;
+	    if (ix < n) { elems++; }
+	    continue;
+	}
+	hxns[ix].skip = 1;
+	elems++;
+	ix++;
+    }
+
+    res = hashmap_from_chunked_array(p, hxns, elems, !lvl);
+
+    ERTS_HOLE_CHECK(p);
+
+    return res;
+}
+
+#define maskval(V,L)      (((V) >> ((7 - (L))*4)) & 0xf)
+#define cdepth(V1,V2)     (hashmap_clz((V1) ^ (V2)) >> 2)
+
+/* n must be > 1
+ * hash values in hxns has to be unique
+ */
+
+#define HALLOC_EXTRA 200
+static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int is_root) {
+    Uint ix, d, dn, dc, slot, elems;
+    Uint32 v, vp, vn, hdr;
+    Uint bp, sz;
+    DECLARE_ESTACK(stack);
+    Eterm res = NIL, *hp = NULL, *nhp;
+
+    ASSERT(n > 1);
+
+    /* push initial nodes on the stack,
+     * this is the starting depth */
+
+    ix = 0;
+    d  = 0;
+    vp = hxns[ix].hx;
+    v  = hxns[ix + hxns[ix].skip].hx;
+
+    ASSERT(vp > v);
+    slot = maskval(vp,d);
+
+    while(slot == maskval(v,d)) {
+	ESTACK_PUSH(stack, 1 << slot);
+	d++;
+	slot = maskval(vp,d);
+    }
+
+    res = hxns[ix].val;
+
+    if (hxns[ix].skip > 1) {
+	dc = 7;
+	/* build collision nodes */
+	while (dc > d) {
+	    hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
+	    hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(vp,dc));
+	    hp[1] = res;
+	    res   = make_hashmap(hp);
+	    dc--;
+	}
+    }
+
+    ESTACK_PUSH(stack, res);
+    ESTACK_PUSH(stack, 1 << slot);
+
+    /* all of the other nodes .. */
+    elems = n - 2; /* remove first and last elements */
+    while(elems--) {
+	hdr = ESTACK_POP(stack);
+	ix  = ix + hxns[ix].skip;
+
+	/* determine if node or subtree should be built by looking
+	 * at the next value. */
+
+	vn = hxns[ix + hxns[ix].skip].hx;
+	dn = cdepth(v,vn);
+	ASSERT(v > vn);
+
+	res = hxns[ix].val;
+
+	if (hxns[ix].skip > 1) {
+	    int wat = (d > dn) ? d : dn;
+	    dc = 7;
+	    /* build collision nodes */
+	    while (dc > wat) {
+		hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
+		hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(v,dc));
+		hp[1] = res;
+		res   = make_hashmap(hp);
+		dc--;
+	    }
+	}
+
+	/* next depth is higher (implies collision) */
+	if (d < dn) {
+	    /* hdr is the popped one initially */
+	    while(d < dn) {
+		slot = maskval(v, d);
+		bp   = 1 << slot;
+		ESTACK_PUSH(stack, hdr | bp);
+		d++;
+		hdr = 0; /* clear hdr for all other collisions */
+	    }
+
+	    slot = maskval(v, d);
+	    bp   = 1 << slot;
+	    /* no more collisions */
+	    ESTACK_PUSH(stack,res);
+	    ESTACK_PUSH(stack,bp);
+	} else if (d == dn) {
+	    /* no collisions at all */
+	    slot = maskval(v, d);
+	    bp   = 1 << slot;
+	    ESTACK_PUSH(stack,res);
+	    ESTACK_PUSH(stack,hdr | bp);
+	} else {
+	    /* dn < n, we have a drop and we are done
+	     * build nodes and subtree */
+	    while (dn != d) {
+		slot  = maskval(v, d);
+		bp    = 1 << slot;
+		/* OR bitposition before sz calculation to handle
+		 * redundant collisions */
+		hdr  |= bp;
+		sz    = hashmap_bitcount(hdr);
+		hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA);
+		nhp   = hp;
+		*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr);
+		*hp++ = res; sz--;
+		while (sz--) { *hp++ = ESTACK_POP(stack); }
+		ASSERT((hp - nhp) < 18);
+		res = make_hashmap(nhp);
+
+		/* we need to pop the next hdr and push if we don't need it */
+
+		hdr = ESTACK_POP(stack);
+		d--;
+	    }
+	    ESTACK_PUSH(stack, res);
+	    ESTACK_PUSH(stack, hdr);
+	}
+
+	vp = v;
+	v  = vn;
+	d  = dn;
+	ERTS_HOLE_CHECK(p);
+    }
+
+    /* v and vp are reused from above */
+    dn  = cdepth(vp,v);
+    ix  = ix + hxns[ix].skip;
+    res = hxns[ix].val;
+
+    if (hxns[ix].skip > 1) {
+	dc = 7;
+	/* build collision nodes */
+	while (dc > dn) {
+	    hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
+	    hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(v,dc));
+	    hp[1] = res;
+	    res   = make_hashmap(hp);
+	    dc--;
+	}
+    }
+
+    hdr = ESTACK_POP(stack);
+    /* pop remaining subtree if any */
+    while (dn) {
+	slot  = maskval(v, dn);
+	bp    = 1 << slot;
+	/* OR bitposition before sz calculation to handle
+	 * redundant collisions */
+	hdr  |= bp;
+	sz    = hashmap_bitcount(hdr);
+	hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA);
+	nhp   = hp;
+	*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr);
+	*hp++ = res; sz--;
+
+	while (sz--) { *hp++ = ESTACK_POP(stack); }
+	res = make_hashmap(nhp);
+	hdr = ESTACK_POP(stack);
+	dn--;
+    }
+
+    /* and finally the root .. */
+
+    slot  = maskval(v, dn);
+    bp    = 1 << slot;
+    hdr  |= bp;
+    sz    = hashmap_bitcount(hdr);
+    hp    = HAlloc(p, sz + /* hdr + item */ (is_root ? 2 : 1));
+    nhp   = hp;
+
+    if (is_root) {
+	*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_HEAD_ARRAY : MAP_HEADER_HAMT_HEAD_BITMAP(hdr);
+	*hp++ = n;
+    } else {
+	*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr);
+    }
+
+    *hp++ = res; sz--;
+    while (sz--) { *hp++ = ESTACK_POP(stack); }
+
+    res = make_hashmap(nhp);
+
+    ASSERT(ESTACK_COUNT(stack) == 0);
+    DESTROY_ESTACK(stack);
+    ERTS_HOLE_CHECK(p);
+    return res;
+}
+#undef HALLOC_EXTRA
+
+static int hxnodecmpkey(hxnode_t *a, hxnode_t *b) {
+    return CMP_TERM(CAR(list_val(a->val)), CAR(list_val(b->val)));
+}
+
+static int hxnodecmp(hxnode_t *a, hxnode_t *b) {
+    if (a->hx < b->hx)
+	return 1;
+    else if (a->hx == b->hx)
+	return 0;
+    else
+	return -1;
+}
+
 #define HALLOC_EXTRA 200
 
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
diff --git a/erts/emulator/beam/erl_hashmap.h b/erts/emulator/beam/erl_hashmap.h
index 1964787218..f05505bae0 100644
--- a/erts/emulator/beam/erl_hashmap.h
+++ b/erts/emulator/beam/erl_hashmap.h
@@ -127,6 +127,9 @@ typedef struct hashmap_head_s {
 #define MAP_HEADER_HAMT_HEAD_BITMAP(Bmp) \
     MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_HEAD_BITMAP,0x1,Bmp)
 
+#define MAP_HEADER_HAMT_NODE_ARRAY \
+    make_arityval(16)
+
 #define MAP_HEADER_HAMT_NODE_BITMAP(Bmp) \
     MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_NODE_BITMAP,0x0,Bmp)
 
-- 
cgit v1.2.3


From 7a12c43da25e3dcad54212f538ebae3dc13f5c2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 17 Feb 2015 14:35:14 +0100
Subject: erts: Refactor erl_hashmap header includes

---
 erts/emulator/beam/erl_hashmap.c | 54 +++++++++++++++++++++++++++++++++-------
 erts/emulator/beam/erl_hashmap.h | 43 +++-----------------------------
 erts/emulator/beam/erl_term.h    | 24 +++++++++++++++++-
 erts/emulator/beam/external.c    |  1 +
 erts/emulator/beam/utils.c       |  1 +
 5 files changed, 74 insertions(+), 49 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index ddb55c53ce..5646820ae1 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -84,7 +84,7 @@ static Eterm hashmap_keys(Process *p, Eterm map);
 static Eterm hashmap_values(Process *p, Eterm map);
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
-
+static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n);
 static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
 static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
 static int hxnodecmp(hxnode_t* a, hxnode_t* b);
@@ -831,7 +831,7 @@ static Eterm hashmap_from_list(Process *p, Eterm list) {
     Eterm *hp;
     Eterm tmp[2];
     Uint32 sw, hx;
-    Uint jx = 0, ix = 0, lx, cx, n = 0;
+    Uint ix = 0, n = 0;
     hxnode_t *hxns;
 
     /* Calculate size and check validity */
@@ -880,6 +880,47 @@ static Eterm hashmap_from_list(Process *p, Eterm list) {
 
     ASSERT(n > 0);
 
+    res = hashmap_from_unsorted_array(p, hxns, n);
+
+    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+
+    BIF_RET(res);
+error:
+    BIF_ERROR(p, BADARG);
+}
+
+Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n) {
+    Eterm tmp[2];
+    Uint32 sw, hx;
+    Uint ix;
+    hxnode_t *hxns;
+    Eterm res;
+
+    /* create tmp hx values and leaf ptrs */
+    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t));
+
+    for (ix = 0; ix < n; ix++) {
+	hx  = hashmap_restore_hash(tmp,0,CAR(list_val(leafs[ix])));
+	swizzle32(sw,hx);
+	hxns[ix].hx   = sw;
+	hxns[ix].val  = leafs[ix];
+	hxns[ix].skip = 1;
+	hxns[ix].i    = ix;
+    }
+
+    res = hashmap_from_unsorted_array(p, hxns, n);
+
+    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+
+    return res;
+}
+
+static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n) {
+    Uint jx = 0, ix = 0, lx, cx;
+    Eterm res;
+
     /* sort and compact array (remove non-unique entries) */
     qsort(hxns, n, sizeof(hxnode_t), (int (*)(const void *, const void *)) hxnodecmp);
 
@@ -931,6 +972,7 @@ static Eterm hashmap_from_list(Process *p, Eterm list) {
 	/* recursive decompose array */
 	res = hashmap_from_sorted_unique_array(p, hxns, cx, 0);
     } else {
+	Eterm *hp;
 	 /* hash value has been swizzled, need to drag it down to get the
 	 * correct slot. */
 	hp    = HAlloc(p, HAMT_HEAD_BITMAP_SZ(1));
@@ -940,15 +982,9 @@ static Eterm hashmap_from_list(Process *p, Eterm list) {
 	res   = make_hashmap(hp);
     }
 
-    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
-    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
-
-    BIF_RET(res);
-error:
-    BIF_ERROR(p, BADARG);
+    return res;
 }
 
-
 static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n, int lvl) {
     Eterm res = NIL;
     Uint i,ix,jx,elems;
diff --git a/erts/emulator/beam/erl_hashmap.h b/erts/emulator/beam/erl_hashmap.h
index f05505bae0..b5fbc636e6 100644
--- a/erts/emulator/beam/erl_hashmap.h
+++ b/erts/emulator/beam/erl_hashmap.h
@@ -22,21 +22,14 @@
 #define __ERL_HASH_H__
 
 #include "sys.h"
+#include "erl_term.h"
 
 Eterm erts_hashmap_get(Eterm key, Eterm map);
 struct ErtsWStack_;
 void hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
 int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
-
-/* erl_term.h stuff */
-#define make_hashmap(x)		make_boxed((Eterm*)(x))
-#define make_hashmap_rel 	make_boxed_rel
-#define is_hashmap(x)		(is_boxed((x)) && is_hashmap_header(*boxed_val((x))))
-#define is_hashmap_rel(RTERM,BASE)  is_hashmap(rterm2wterm(RTERM,BASE))
-#define is_hashmap_header(x)	(((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_HASHMAP)
-#define hashmap_val(x)		_unchecked_boxed_val((x))
-#define hashmap_val_rel(RTERM, BASE) hashmap_val(rterm2wterm(RTERM, BASE))
+Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n);
 
 /* HASH */
 
@@ -54,8 +47,8 @@ Uint32 hashmap_bitcount(Uint32 x);
  * head
  */
 
-/* the head-node is a bitmap or array with an untagged size
- */
+/* the head-node is a bitmap or array with an untagged size */
+
 typedef struct hashmap_head_s {
     Eterm thing_word;
     Uint size;
@@ -65,21 +58,6 @@ typedef struct hashmap_head_s {
 #define hashmap_size(x) (((hashmap_head_t*) hashmap_val(x))->size)
 #define hashmap_size_rel(RTERM, BASE) hashmap_size(rterm2wterm(RTERM, BASE))
  
-/* the bitmap-node
- * typedef struct hashmap_bitmap_node_s {
- *     Eterm thing_word;
- *     Eterm items[1];
- * } hashmap_bitmap_node_t;
- *
- * the array-node is a tuple
- * typedef struct hashmap_bitmap_node_s {
- *     Eterm thing_word; 
- *     Eterm items[1];
- * } hashmap_bitmap_node_t;
- *
- * the leaf-node
- * cons-cell
- */
 
 /* thing_word tagscheme
  * Need two bits for map subtags
@@ -103,19 +81,6 @@ typedef struct hashmap_head_s {
 
 /* erl_map.h stuff */
 
-#define MAP_HEADER_TAG_SZ                 (2)
-#define MAP_HEADER_ARITY_SZ               (8)
-#define MAP_HEADER_VAL_SZ                 (16)
-
-#define MAP_HEADER_TAG_FLAT               (0x0)
-#define MAP_HEADER_TAG_HAMT_NODE_BITMAP   (0x1)
-#define MAP_HEADER_TAG_HAMT_HEAD_ARRAY    (0x2)
-#define MAP_HEADER_TAG_HAMT_HEAD_BITMAP   (0x3)
-
-#define MAP_HEADER_TYPE(Hdr)  (((Hdr) >> (_HEADER_ARITY_OFFS)) & (0x3))
-#define MAP_HEADER_ARITY(Hdr) (((Hdr) >> (_HEADER_ARITY_OFFS + MAP_HEADER_TAG_SZ)) & (0xff))
-#define MAP_HEADER_VAL(Hdr)   (((Hdr) >> (_HEADER_ARITY_OFFS + MAP_HEADER_TAG_SZ + MAP_HEADER_ARITY_SZ)) & (0xffff))
-
 #define is_hashmap_header_head(x) ((MAP_HEADER_TYPE(x) & (0x2)))
 
 #define MAKE_MAP_HEADER(Type,Arity,Val) \
diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index 72946d9ddf..7605a41cd8 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -21,7 +21,6 @@
 #define __ERL_TERM_H
 
 #include "sys.h" /* defines HALFWORD_HEAP */
-#include "erl_hashmap.h"
 
 typedef UWord Wterm;  /* Full word terms */
 
@@ -996,6 +995,29 @@ _ET_DECLARE_CHECKED(Uint32*,external_ref_data,Wterm)
 _ET_DECLARE_CHECKED(struct erl_node_*,external_ref_node,Eterm)
 #define external_ref_node(x) _ET_APPLY(external_ref_node,(x))
 
+/* maps */
+
+#define MAP_HEADER_TAG_SZ                 (2)
+#define MAP_HEADER_ARITY_SZ               (8)
+#define MAP_HEADER_VAL_SZ                 (16)
+
+#define MAP_HEADER_TAG_FLAT               (0x0)
+#define MAP_HEADER_TAG_HAMT_NODE_BITMAP   (0x1)
+#define MAP_HEADER_TAG_HAMT_HEAD_ARRAY    (0x2)
+#define MAP_HEADER_TAG_HAMT_HEAD_BITMAP   (0x3)
+
+#define MAP_HEADER_TYPE(Hdr)  (((Hdr) >> (_HEADER_ARITY_OFFS)) & (0x3))
+#define MAP_HEADER_ARITY(Hdr) (((Hdr) >> (_HEADER_ARITY_OFFS + MAP_HEADER_TAG_SZ)) & (0xff))
+#define MAP_HEADER_VAL(Hdr)   (((Hdr) >> (_HEADER_ARITY_OFFS + MAP_HEADER_TAG_SZ + MAP_HEADER_ARITY_SZ)) & (0xffff))
+
+#define make_hashmap(x)		make_boxed((Eterm*)(x))
+#define make_hashmap_rel 	make_boxed_rel
+#define is_hashmap(x)		(is_boxed((x)) && is_hashmap_header(*boxed_val((x))))
+#define is_hashmap_rel(RTERM,BASE)  is_hashmap(rterm2wterm(RTERM,BASE))
+#define is_hashmap_header(x)	(((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_HASHMAP)
+#define hashmap_val(x)		_unchecked_boxed_val((x))
+#define hashmap_val_rel(RTERM, BASE) hashmap_val(rterm2wterm(RTERM, BASE))
+
 /* number tests */
 
 #define is_integer(x)		(is_small(x) || is_big(x))
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index af8db4c265..9030b528a4 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -45,6 +45,7 @@
 #include "erl_bits.h"
 #include "erl_zlib.h"
 #include "erl_map.h"
+#include "erl_hashmap.h"
 
 #define in_area(ptr,start,nbytes) ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
 
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index d234e8fc68..f595cfbacd 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -32,6 +32,7 @@
 #include "erl_binary.h"
 #include "erl_bits.h"
 #include "erl_map.h"
+#include "erl_hashmap.h"
 #include "packet_parser.h"
 #include "erl_gc.h"
 #define ERTS_WANT_DB_INTERNAL__
-- 
cgit v1.2.3


From 9cfaa729d7319ede30f62ffaaf82eb10fbaf8a60 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 19 Feb 2015 16:25:03 +0100
Subject: erts: Move hashmap:size/1 to maps

---
 erts/emulator/beam/bif.tab         |  1 -
 erts/emulator/beam/erl_bif_guard.c | 27 +++++++++++++++------------
 erts/emulator/beam/erl_hashmap.c   | 14 --------------
 erts/emulator/beam/erl_map.c       | 10 ++++++++++
 4 files changed, 25 insertions(+), 27 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 5ffd5b37b5..8606a41c7b 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -626,7 +626,6 @@ bif hashmap:to_list/1
 bif hashmap:new/0
 bif hashmap:is_key/2
 bif hashmap:keys/1
-bif hashmap:size/1
 bif erlang:is_hashmap/1
 bif hashmap:values/1
 bif hashmap:merge/2
diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c
index bbd8aa31d9..a5d1d3a5cb 100644
--- a/erts/emulator/beam/erl_bif_guard.c
+++ b/erts/emulator/beam/erl_bif_guard.c
@@ -34,6 +34,7 @@
 #include "big.h"
 #include "erl_binary.h"
 #include "erl_map.h"
+#include "erl_hashmap.h"
 
 static Eterm gc_double_to_integer(Process* p, double x, Eterm* reg, Uint live);
 
@@ -459,23 +460,25 @@ Eterm erts_gc_byte_size_1(Process* p, Eterm* reg, Uint live)
 Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live)
 {
     Eterm arg = reg[live];
+    Eterm* hp;
+    Uint size;
     if (is_map(arg)) {
 	map_t *mp = (map_t*)map_val(arg);
-	Uint size = map_get_size(mp);
-	if (IS_USMALL(0, size)) {
-	    return make_small(size);
-	} else {
-	    Eterm* hp;
-	    if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) {
-		erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live);
-	    }
-	    hp = p->htop;
-	    p->htop += BIG_UINT_HEAP_SIZE;
-	    return uint_to_big(size, hp);
-	}
+	size = map_get_size(mp);
+    } else if (is_hashmap(arg)) {
+	size = hashmap_size(arg);
     } else {
 	BIF_ERROR(p, BADARG);
     }
+    if (IS_USMALL(0, size)) {
+	return make_small(size);
+    }
+    if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) {
+	erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live);
+    }
+    hp = p->htop;
+    p->htop += BIG_UINT_HEAP_SIZE;
+    return uint_to_big(size, hp);
 }
 
 Eterm erts_gc_abs_1(Process* p, Eterm* reg, Uint live)
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 5646820ae1..3f6e12cdd8 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -200,20 +200,6 @@ BIF_RETTYPE hashmap_remove_2(BIF_ALIST_2) {
 }
 /* hashmap:size/1 */
 
-BIF_RETTYPE hashmap_size_1(BIF_ALIST_1) {
-    if (is_hashmap(BIF_ARG_1)) {
-	Eterm *head, *hp, res;
-	Uint size, hsz=0;
-
-	head = hashmap_val(BIF_ARG_1);
-	size = head[1];
-	(void) erts_bld_uint(NULL, &hsz, size);
-	hp = HAlloc(BIF_P, hsz);
-	res = erts_bld_uint(&hp, NULL, size);
-	BIF_RET(res);
-    }
-    BIF_ERROR(BIF_P, BADARG);
-}
 
 /* erlang:is_hashmap/1 */
 
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index b2a16eb5ed..ecbb91bc33 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -77,6 +77,16 @@ BIF_RETTYPE map_size_1(BIF_ALIST_1) {
 	erts_bld_uint(NULL, &hsz, n);
 	hp = HAlloc(BIF_P, hsz);
 	BIF_RET(erts_bld_uint(&hp, NULL, n));
+    } else if (is_hashmap(BIF_ARG_1)) {
+	Eterm *head, *hp, res;
+	Uint size, hsz=0;
+
+	head = hashmap_val(BIF_ARG_1);
+	size = head[1];
+	(void) erts_bld_uint(NULL, &hsz, size);
+	hp = HAlloc(BIF_P, hsz);
+	res = erts_bld_uint(&hp, NULL, size);
+	BIF_RET(res);
     }
 
     BIF_ERROR(BIF_P, BADARG);
-- 
cgit v1.2.3


From 903740ac57b00d404f430876b82cb21e0bb684a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 19 Feb 2015 16:53:32 +0100
Subject: erts: Move hashmap:to_list/1, keys/1 and values/1 to maps

---
 erts/emulator/beam/bif.tab       |   3 -
 erts/emulator/beam/erl_hashmap.c | 133 +--------------------------------------
 erts/emulator/beam/erl_hashmap.h |  10 +--
 erts/emulator/beam/erl_map.c     | 115 ++++++++++++++++++++++++++++++++-
 erts/emulator/beam/erl_map.h     |   7 +++
 5 files changed, 123 insertions(+), 145 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 8606a41c7b..6bd9291d34 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -622,12 +622,9 @@ bif hashmap:update/3
 bif hashmap:remove/2
 bif hashmap:info/1
 bif hashmap:from_list/1
-bif hashmap:to_list/1
 bif hashmap:new/0
 bif hashmap:is_key/2
-bif hashmap:keys/1
 bif erlang:is_hashmap/1
-bif hashmap:values/1
 bif hashmap:merge/2
 
 #
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 3f6e12cdd8..064ae73acd 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -79,9 +79,6 @@ typedef struct {
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_from_list(Process *p, Eterm node);
-static Eterm hashmap_to_list(Process *p, Eterm map);
-static Eterm hashmap_keys(Process *p, Eterm map);
-static Eterm hashmap_values(Process *p, Eterm map);
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n);
@@ -135,14 +132,6 @@ BIF_RETTYPE hashmap_update_3(BIF_ALIST_3) {
 
 /* hashmap:to_list/1 */
 
-BIF_RETTYPE hashmap_to_list_1(BIF_ALIST_1) {
-    if (is_hashmap(BIF_ARG_1)) {
-	return hashmap_to_list(BIF_P, BIF_ARG_1);
-    }
-
-    BIF_ERROR(BIF_P, BADARG);
-}
-
 /* hashmap:from_list/1 */
 
 BIF_RETTYPE hashmap_from_list_1(BIF_ALIST_1) {
@@ -222,25 +211,10 @@ BIF_RETTYPE hashmap_is_key_2(BIF_ALIST_2) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
-/* hashmap:keys/1
- */
-
-BIF_RETTYPE hashmap_keys_1(BIF_ALIST_1) {
-    if (is_hashmap(BIF_ARG_1)) {
-	BIF_RET(hashmap_keys(BIF_P, BIF_ARG_1));
-    }
-    BIF_ERROR(BIF_P, BADARG);
-}
+/* hashmap:keys/1 */
 
-/* hashmap:keys/1
- */
 
-BIF_RETTYPE hashmap_values_1(BIF_ALIST_1) {
-    if (is_hashmap(BIF_ARG_1)) {
-	BIF_RET(hashmap_values(BIF_P, BIF_ARG_1));
-    }
-    BIF_ERROR(BIF_P, BADARG);
-}
+/* hashmap:values/1 */
 
 BIF_RETTYPE hashmap_merge_2(BIF_ALIST_2) {
     if (is_hashmap(BIF_ARG_1) && is_hashmap(BIF_ARG_2)) {
@@ -1448,109 +1422,6 @@ recurse:
     return res;
 }
 
-void hashmap_iterator_init(ErtsWStack* s, Eterm node) {
-    WSTACK_PUSH((*s), (UWord)THE_NON_VALUE);  /* end marker */
-    WSTACK_PUSH((*s), (UWord)node);
-}
-
-Eterm* hashmap_iterator_next(ErtsWStack* s) {
-    Eterm node, *ptr, hdr;
-    Uint32 sz;
-
-    for (;;) {
-        ASSERT(!WSTACK_ISEMPTY((*s)));
-	node = (Eterm) WSTACK_POP((*s));
-        if (is_non_value(node)) {
-            return NULL;
-        }
-        switch (primary_tag(node)) {
-        case TAG_PRIMARY_LIST:
-            return list_val(node);
-
-        case TAG_PRIMARY_BOXED:
-            ptr = boxed_val(node);
-            hdr = *ptr;
-            ASSERT(is_header(hdr));
-            switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-            case HAMT_SUBTAG_HEAD_ARRAY:
-                ptr++;
-            case HAMT_SUBTAG_NODE_ARRAY:
-                ptr++;
-                sz = 16;
-                while(sz--) { WSTACK_PUSH((*s), (UWord)ptr[sz]); }
-                break;
-            case HAMT_SUBTAG_HEAD_BITMAP:
-                ptr++;
-            case HAMT_SUBTAG_NODE_BITMAP:
-                sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
-                ASSERT(sz < 17);
-                ptr++;
-                while(sz--) { WSTACK_PUSH((*s), (UWord)ptr[sz]); }
-                break;
-            default:
-                erl_exit(1, "bad header");
-            }
-            break;
-
-        default:
-            erl_exit(1, "bad hamt node");
-	}
-    }
-}
-
-static Eterm hashmap_to_list(Process *p, Eterm node) {
-    DECLARE_WSTACK(stack);
-    hashmap_head_t* root;
-    Eterm *hp, *kv;
-    Eterm res = NIL;
-
-    root = (hashmap_head_t*) boxed_val(node);
-    hp  = HAlloc(p, root->size * (2 + 3));
-    hashmap_iterator_init(&stack, node);
-    while ((kv=hashmap_iterator_next(&stack)) != NULL) {
-	Eterm tup = TUPLE2(hp, CAR(kv), CDR(kv));
-	hp += 3;
-	res = CONS(hp, tup, res);
-	hp += 2;
-    }
-    DESTROY_WSTACK(stack);
-    return res;
-}
-
-static Eterm hashmap_keys(Process* p, Eterm node) {
-    DECLARE_WSTACK(stack);
-    hashmap_head_t* root;
-    Eterm *hp, *kv;
-    Eterm res = NIL;
-
-    root = (hashmap_head_t*) boxed_val(node);
-    hp  = HAlloc(p, root->size * 2);
-    hashmap_iterator_init(&stack, node);
-    while ((kv=hashmap_iterator_next(&stack)) != NULL) {
-	res = CONS(hp, CAR(kv), res);
-	hp += 2;
-    }
-    DESTROY_WSTACK(stack);
-    return res;
-}
-
-static Eterm hashmap_values(Process* p, Eterm node) {
-    DECLARE_WSTACK(stack);
-    hashmap_head_t* root;
-    Eterm *hp, *kv;
-    Eterm res = NIL;
-
-    root = (hashmap_head_t*) boxed_val(node);
-    hp  = HAlloc(p, root->size * 2);
-    hashmap_iterator_init(&stack, node);
-    while ((kv=hashmap_iterator_next(&stack)) != NULL) {
-	res = CONS(hp, CDR(kv), res);
-	hp += 2;
-    }
-    DESTROY_WSTACK(stack);
-    return res;
-}
-
 static int hash_cmp(Uint32 ha, Uint32 hb)
 {
     int i;
diff --git a/erts/emulator/beam/erl_hashmap.h b/erts/emulator/beam/erl_hashmap.h
index b5fbc636e6..5a9aa05f61 100644
--- a/erts/emulator/beam/erl_hashmap.h
+++ b/erts/emulator/beam/erl_hashmap.h
@@ -25,9 +25,6 @@
 #include "erl_term.h"
 
 Eterm erts_hashmap_get(Eterm key, Eterm map);
-struct ErtsWStack_;
-void hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
-Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
 int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
 Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n);
 
@@ -46,18 +43,13 @@ Uint32 hashmap_bitcount(Uint32 x);
  * node :: leaf | array | bitmap
  * head
  */
-
-/* the head-node is a bitmap or array with an untagged size */
-
 typedef struct hashmap_head_s {
     Eterm thing_word;
     Uint size;
     Eterm items[1];
 } hashmap_head_t;
 
-#define hashmap_size(x) (((hashmap_head_t*) hashmap_val(x))->size)
-#define hashmap_size_rel(RTERM, BASE) hashmap_size(rterm2wterm(RTERM, BASE))
- 
+
 
 /* thing_word tagscheme
  * Need two bits for map subtags
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index ecbb91bc33..289cd4fd13 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -31,6 +31,7 @@
 #include "bif.h"
 
 #include "erl_map.h"
+#include "erl_hashmap.h"
 
 /* BIFs
  *
@@ -62,6 +63,10 @@
  * - erts_internal:map_to_tuple_keys/1
  */
 
+static Eterm hashmap_to_list(Process *p, Eterm map);
+static Eterm hashmap_keys(Process *p, Eterm map);
+static Eterm hashmap_values(Process *p, Eterm map);
+
 /* erlang:map_size/1
  * the corresponding instruction is implemented in:
  *     beam/erl_bif_guard.c
@@ -92,8 +97,7 @@ BIF_RETTYPE map_size_1(BIF_ALIST_1) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
-/* maps:to_list/1
- */
+/* maps:to_list/1 */
 
 BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) {
     if (is_map(BIF_ARG_1)) {
@@ -114,6 +118,8 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) {
 	}
 
 	BIF_RET(res);
+    } else if (is_hashmap(BIF_ARG_1)) {
+	return hashmap_to_list(BIF_P, BIF_ARG_1);
     }
 
     BIF_ERROR(BIF_P, BADARG);
@@ -386,6 +392,8 @@ BIF_RETTYPE maps_keys_1(BIF_ALIST_1) {
 	}
 
 	BIF_RET(res);
+    } else if (is_hashmap(BIF_ARG_1)) {
+	BIF_RET(hashmap_keys(BIF_P, BIF_ARG_1));
     }
     BIF_ERROR(BIF_P, BADARG);
 }
@@ -786,10 +794,113 @@ BIF_RETTYPE maps_values_1(BIF_ALIST_1) {
 	}
 
 	BIF_RET(res);
+    } else if (is_hashmap(BIF_ARG_1)) {
+	BIF_RET(hashmap_values(BIF_P, BIF_ARG_1));
     }
     BIF_ERROR(BIF_P, BADARG);
 }
 
+static Eterm hashmap_to_list(Process *p, Eterm node) {
+    DECLARE_WSTACK(stack);
+    Eterm *hp, *kv;
+    Eterm res = NIL;
+
+    hp  = HAlloc(p, hashmap_size(node) * (2 + 3));
+    hashmap_iterator_init(&stack, node);
+    while ((kv=hashmap_iterator_next(&stack)) != NULL) {
+	Eterm tup = TUPLE2(hp, CAR(kv), CDR(kv));
+	hp += 3;
+	res = CONS(hp, tup, res);
+	hp += 2;
+    }
+    DESTROY_WSTACK(stack);
+    return res;
+}
+
+void hashmap_iterator_init(ErtsWStack* s, Eterm node) {
+    WSTACK_PUSH((*s), (UWord)THE_NON_VALUE);  /* end marker */
+    WSTACK_PUSH((*s), (UWord)node);
+}
+
+Eterm* hashmap_iterator_next(ErtsWStack* s) {
+    Eterm node, *ptr, hdr;
+    Uint32 sz;
+
+    for (;;) {
+        ASSERT(!WSTACK_ISEMPTY((*s)));
+	node = (Eterm) WSTACK_POP((*s));
+        if (is_non_value(node)) {
+            return NULL;
+        }
+        switch (primary_tag(node)) {
+        case TAG_PRIMARY_LIST:
+            return list_val(node);
+
+        case TAG_PRIMARY_BOXED:
+            ptr = boxed_val(node);
+            hdr = *ptr;
+            ASSERT(is_header(hdr));
+            switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+            case HAMT_SUBTAG_HEAD_ARRAY:
+                ptr++;
+            case HAMT_SUBTAG_NODE_ARRAY:
+                ptr++;
+                sz = 16;
+                while(sz--) { WSTACK_PUSH((*s), (UWord)ptr[sz]); }
+                break;
+            case HAMT_SUBTAG_HEAD_BITMAP:
+                ptr++;
+            case HAMT_SUBTAG_NODE_BITMAP:
+                sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+                ASSERT(sz < 17);
+                ptr++;
+                while(sz--) { WSTACK_PUSH((*s), (UWord)ptr[sz]); }
+                break;
+            default:
+                erl_exit(1, "bad header");
+            }
+            break;
+
+        default:
+            erl_exit(1, "bad hamt node");
+	}
+    }
+}
+
+static Eterm hashmap_keys(Process* p, Eterm node) {
+    DECLARE_WSTACK(stack);
+    hashmap_head_t* root;
+    Eterm *hp, *kv;
+    Eterm res = NIL;
+
+    root = (hashmap_head_t*) boxed_val(node);
+    hp  = HAlloc(p, root->size * 2);
+    hashmap_iterator_init(&stack, node);
+    while ((kv=hashmap_iterator_next(&stack)) != NULL) {
+	res = CONS(hp, CAR(kv), res);
+	hp += 2;
+    }
+    DESTROY_WSTACK(stack);
+    return res;
+}
+
+static Eterm hashmap_values(Process* p, Eterm node) {
+    DECLARE_WSTACK(stack);
+    hashmap_head_t* root;
+    Eterm *hp, *kv;
+    Eterm res = NIL;
+
+    root = (hashmap_head_t*) boxed_val(node);
+    hp  = HAlloc(p, root->size * 2);
+    hashmap_iterator_init(&stack, node);
+    while ((kv=hashmap_iterator_next(&stack)) != NULL) {
+	res = CONS(hp, CDR(kv), res);
+	hp += 2;
+    }
+    DESTROY_WSTACK(stack);
+    return res;
+}
+
 int erts_validate_and_sort_map(map_t* mp)
 {
     Eterm *ks  = map_get_keys(mp);
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 2e02ca4677..c104e08e27 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -42,8 +42,12 @@ typedef struct map_s {
  * -----------
  */
 
+/* the head-node is a bitmap or array with an untagged size */
 
 
+#define hashmap_size(x) (((hashmap_head_t*) hashmap_val(x))->size)
+#define hashmap_size_rel(RTERM, BASE) hashmap_size(rterm2wterm(RTERM, BASE))
+
 /* erl_term.h stuff */
 #define make_map(x)		make_boxed((Eterm*)(x))
 #define make_map_rel(x, BASE)   make_boxed_rel((Eterm*)(x),(BASE))
@@ -62,10 +66,13 @@ typedef struct map_s {
 #define MAP_HEADER             _make_header(1,_TAG_HEADER_MAP)
 #define MAP_HEADER_SIZE        (sizeof(map_t) / sizeof(Eterm))
 
+struct ErtsWStack_;
 Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map);
 int   erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res);
 int   erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res);
 int   erts_validate_and_sort_map(map_t* map);
+void  hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
+Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
 
 #if HALFWORD_HEAP
 const Eterm *
-- 
cgit v1.2.3


From 7da662fb9eb519625b3833fec34419c32620f041 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 19 Feb 2015 17:19:31 +0100
Subject: erts: Move erlang:is_hashmap/1 to maps

---
 erts/emulator/beam/beam_emu.c    |  2 +-
 erts/emulator/beam/bif.tab       |  1 -
 erts/emulator/beam/erl_bif_op.c  |  2 +-
 erts/emulator/beam/erl_hashmap.c | 11 +----------
 erts/emulator/beam/erl_term.h    |  1 +
 5 files changed, 4 insertions(+), 13 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index b734d34872..034436e975 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -699,7 +699,7 @@ void** beam_ops;
         Fail; 								  \
     }
 
-#define IsMap(Src, Fail) if (is_not_map(Src)) { Fail; }
+#define IsMap(Src, Fail) if (is_not_map(Src) && is_not_hashmap(Src)) { Fail; }
 
 #define HasMapField(Src, Key, Fail) if (has_not_map_field(Src, Key)) { Fail; }
 
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 6bd9291d34..f7f6eb9213 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -624,7 +624,6 @@ bif hashmap:info/1
 bif hashmap:from_list/1
 bif hashmap:new/0
 bif hashmap:is_key/2
-bif erlang:is_hashmap/1
 bif hashmap:merge/2
 
 #
diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c
index 37dd6457db..11c6c9e556 100644
--- a/erts/emulator/beam/erl_bif_op.c
+++ b/erts/emulator/beam/erl_bif_op.c
@@ -324,7 +324,7 @@ BIF_RETTYPE is_record_3(BIF_ALIST_3)
 	
 BIF_RETTYPE is_map_1(BIF_ALIST_1)
 {
-    if (is_map(BIF_ARG_1)) {
+    if (is_map(BIF_ARG_1) || is_hashmap(BIF_ARG_1)) {
 	BIF_RET(am_true);
     }
     BIF_RET(am_false);
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 064ae73acd..e51767146e 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -189,18 +189,9 @@ BIF_RETTYPE hashmap_remove_2(BIF_ALIST_2) {
 }
 /* hashmap:size/1 */
 
-
 /* erlang:is_hashmap/1 */
 
-BIF_RETTYPE is_hashmap_1(BIF_ALIST_1) {
-    if (is_hashmap(BIF_ARG_1)) {
-	BIF_RET(am_true);
-    }
-    BIF_RET(am_false);
-}
-
-/* hashmap:is_key/2
- */
+/* hashmap:is_key/2 */
 
 BIF_RETTYPE hashmap_is_key_2(BIF_ALIST_2) {
     if (is_hashmap(BIF_ARG_1)) {
diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index 7605a41cd8..264bf8bd74 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -1013,6 +1013,7 @@ _ET_DECLARE_CHECKED(struct erl_node_*,external_ref_node,Eterm)
 #define make_hashmap(x)		make_boxed((Eterm*)(x))
 #define make_hashmap_rel 	make_boxed_rel
 #define is_hashmap(x)		(is_boxed((x)) && is_hashmap_header(*boxed_val((x))))
+#define is_not_hashmap(x)       (!is_hashmap(x))
 #define is_hashmap_rel(RTERM,BASE)  is_hashmap(rterm2wterm(RTERM,BASE))
 #define is_hashmap_header(x)	(((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_HASHMAP)
 #define hashmap_val(x)		_unchecked_boxed_val((x))
-- 
cgit v1.2.3


From f659c631f33ad86e7532c7198bbce6d7e07fe1dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Mon, 23 Feb 2015 11:48:46 +0100
Subject: erts: Move hashmap:get/2, find/2 and is_key/2 to maps

---
 erts/emulator/beam/bif.tab       |   3 -
 erts/emulator/beam/erl_hashmap.c | 121 +--------------------------------------
 erts/emulator/beam/erl_map.c     |  97 +++++++++++++++++++++++++++++++
 erts/emulator/beam/erl_map.h     |   7 +++
 4 files changed, 105 insertions(+), 123 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index f7f6eb9213..5b0f90d418 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -616,14 +616,11 @@ bif erlang:get_keys/0
 
 # Hash Array Mappped Trie
 bif hashmap:put/3
-bif hashmap:get/2
-bif hashmap:find/2
 bif hashmap:update/3
 bif hashmap:remove/2
 bif hashmap:info/1
 bif hashmap:from_list/1
 bif hashmap:new/0
-bif hashmap:is_key/2
 bif hashmap:merge/2
 
 #
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index e51767146e..7e0e7fde04 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -42,19 +42,13 @@
 #include "error.h"
 #include "bif.h"
 
+#include "erl_map.h"
 #include "erl_hashmap.h"
 
 #ifndef DECL_AM
 #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
 #endif
 
-#define hashmap_make_hash(Key) make_hash_vsn(Key, 3)
-
-#define hashmap_restore_hash(Heap,Lvl,Key) \
-    (((Lvl) < 8) ? hashmap_make_hash(Key) >> (4*(Lvl)) : hashmap_make_hash(CONS(Heap, make_small((Lvl)>>3), (Key))) >> (4*((Lvl) & 7)))
-#define hashmap_shift_hash(Heap,Hx,Lvl,Key) \
-    (((++(Lvl)) & 7) ? (Hx) >> 4 : hashmap_make_hash(CONS(Heap, make_small((Lvl)>>3), Key)))
-
 #if 0
 static char *format_binary(Uint64 x, char *b) {
     int z;
@@ -76,7 +70,6 @@ typedef struct {
     Eterm  val;
 } hxnode_t;
 
-static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_from_list(Process *p, Eterm node);
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
@@ -143,40 +136,8 @@ BIF_RETTYPE hashmap_from_list_1(BIF_ALIST_1) {
 }
 /* hashmap:get/2 */
 
-BIF_RETTYPE hashmap_get_2(BIF_ALIST_2) {
-    if (is_hashmap(BIF_ARG_2)) {
-	const Eterm *value;
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-
-	if ((value = hashmap_get(hx, BIF_ARG_1, BIF_ARG_2)) != NULL) {
-	    BIF_RET(*value);
-	}
-    }
-    BIF_ERROR(BIF_P, BADARG);
-}
-
 /* hashmap:find/2 */
 
-BIF_RETTYPE hashmap_find_2(BIF_ALIST_2) {
-    if (is_hashmap(BIF_ARG_2)) {
-	Eterm *hp, res;
-	const Eterm *value;
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-
-	if ((value = hashmap_get(hx, BIF_ARG_1, BIF_ARG_2)) != NULL) {
-	    hp    = HAlloc(BIF_P, 3);
-	    res   = make_tuple(hp);
-	    *hp++ = make_arityval(2);
-	    *hp++ = am_ok;
-            *hp++ = *value;
-	    BIF_RET(res);
-	}
-	BIF_RET(am_error);
-    }
-    BIF_ERROR(BIF_P, BADARG);
-}
-
-
 /* hashmap:remove/2 */
 
 BIF_RETTYPE hashmap_remove_2(BIF_ALIST_2) {
@@ -193,18 +154,8 @@ BIF_RETTYPE hashmap_remove_2(BIF_ALIST_2) {
 
 /* hashmap:is_key/2 */
 
-BIF_RETTYPE hashmap_is_key_2(BIF_ALIST_2) {
-    if (is_hashmap(BIF_ARG_1)) {
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-
-	BIF_RET(hashmap_get(hx, BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
-    }
-    BIF_ERROR(BIF_P, BADARG);
-}
-
 /* hashmap:keys/1 */
 
-
 /* hashmap:values/1 */
 
 BIF_RETTYPE hashmap_merge_2(BIF_ALIST_2) {
@@ -216,76 +167,6 @@ BIF_RETTYPE hashmap_merge_2(BIF_ALIST_2) {
 
 /* impl. */
 
-static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
-    Eterm *ptr, hdr;
-    Eterm th[2];
-    Uint ix,slot, lvl = 0;
-    Uint32 hval,bp;
-
-    for (;;) {
-	switch(primary_tag(node)) {
-	    case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */
-		ptr = list_val(node);
-		if (EQ(CAR(ptr), key)) {
-		    return &(CDR(ptr));
-		}
-		return NULL;
-	    case TAG_PRIMARY_BOXED:
-		ptr = boxed_val(node);
-		hdr = *ptr;
-		ASSERT(is_header(hdr));
-
-		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-		    case HAMT_SUBTAG_NODE_ARRAY:
-			ix   = hashmap_index(hx);
-			hx   = hashmap_shift_hash(th,hx,lvl,key);
-			node = ptr[ix+1];
-			break;
-		    case HAMT_SUBTAG_HEAD_ARRAY:
-			ix   = hashmap_index(hx);
-			hx   = hashmap_shift_hash(th,hx,lvl,key);
-			node = ptr[ix+2];
-			break;
-		    case HAMT_SUBTAG_NODE_BITMAP:
-			hval = MAP_HEADER_VAL(hdr);
-			ix   = hashmap_index(hx);
-			bp   = 1 << ix;
-			slot = hashmap_bitcount(hval & (bp - 1));
-
-			/* occupied */
-			if (bp & hval) {
-			    hx    = hashmap_shift_hash(th,hx,lvl,key);
-			    node  = ptr[slot+1];
-			    break;
-			}
-			/* not occupied */
-			return NULL;
-		    case HAMT_SUBTAG_HEAD_BITMAP:
-			hval = MAP_HEADER_VAL(hdr);
-			ix   = hashmap_index(hx);
-			bp   = 1 << ix;
-			slot = hashmap_bitcount(hval & (bp - 1));
-
-			/* occupied */
-			if (bp & hval) {
-			    hx    = hashmap_shift_hash(th,hx,lvl,key);
-			    node  = ptr[slot+2];
-			    break;
-			}
-			/* not occupied */
-			return NULL;
-		    default:
-			erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
-			break;
-		}
-		break;
-	    default:
-		erl_exit(1, "bad primary tag %p\r\n", node);
-		break;
-	}
-    }
-    return NULL;
-}
 
 static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
 			    Eterm node, int is_update) {
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 289cd4fd13..0ac5885e6b 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -63,6 +63,7 @@
  * - erts_internal:map_to_tuple_keys/1
  */
 
+static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_to_list(Process *p, Eterm map);
 static Eterm hashmap_keys(Process *p, Eterm map);
 static Eterm hashmap_values(Process *p, Eterm map);
@@ -181,6 +182,20 @@ BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
 	    BIF_RET(res);
 	}
 
+	BIF_RET(am_error);
+    } else if (is_hashmap(BIF_ARG_2)) {
+	Eterm *hp, res;
+	const Eterm *value;
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
+
+	if ((value = hashmap_get(hx, BIF_ARG_1, BIF_ARG_2)) != NULL) {
+	    hp    = HAlloc(BIF_P, 3);
+	    res   = make_tuple(hp);
+	    *hp++ = make_arityval(2);
+	    *hp++ = am_ok;
+            *hp++ = *value;
+	    BIF_RET(res);
+	}
 	BIF_RET(am_error);
     }
     BIF_ERROR(BIF_P, BADARG);
@@ -209,7 +224,15 @@ BIF_RETTYPE maps_get_2(BIF_ALIST_2) {
 	hp = HAlloc(BIF_P, 3);
 	BIF_P->fvalue = TUPLE2(hp, error, BIF_ARG_1);
 	BIF_ERROR(BIF_P, EXC_ERROR_2);
+    } else if (is_hashmap(BIF_ARG_2)) {
+	const Eterm *value;
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
+
+	if ((value = hashmap_get(hx, BIF_ARG_1, BIF_ARG_2)) != NULL) {
+	    BIF_RET(*value);
+	}
     }
+
     BIF_ERROR(BIF_P, BADARG);
 }
 
@@ -365,6 +388,9 @@ BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) {
 	    }
 	}
 	BIF_RET(am_false);
+    } else if (is_hashmap(BIF_ARG_2)) {
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
+	BIF_RET(hashmap_get(hx, BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
     }
     BIF_ERROR(BIF_P, BADARG);
 }
@@ -867,6 +893,77 @@ Eterm* hashmap_iterator_next(ErtsWStack* s) {
     }
 }
 
+static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
+    Eterm *ptr, hdr;
+    Eterm th[2];
+    Uint ix,slot, lvl = 0;
+    Uint32 hval,bp;
+
+    for (;;) {
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */
+		ptr = list_val(node);
+		if (EQ(CAR(ptr), key)) {
+		    return &(CDR(ptr));
+		}
+		return NULL;
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			ix   = hashmap_index(hx);
+			hx   = hashmap_shift_hash(th,hx,lvl,key);
+			node = ptr[ix+1];
+			break;
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			ix   = hashmap_index(hx);
+			hx   = hashmap_shift_hash(th,hx,lvl,key);
+			node = ptr[ix+2];
+			break;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
+			    node  = ptr[slot+1];
+			    break;
+			}
+			/* not occupied */
+			return NULL;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
+			    node  = ptr[slot+2];
+			    break;
+			}
+			/* not occupied */
+			return NULL;
+		    default:
+			erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
+			break;
+		}
+		break;
+	    default:
+		erl_exit(1, "bad primary tag %p\r\n", node);
+		break;
+	}
+    }
+    return NULL;
+}
+
 static Eterm hashmap_keys(Process* p, Eterm node) {
     DECLARE_WSTACK(stack);
     hashmap_head_t* root;
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index c104e08e27..48bc316e3b 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -47,6 +47,13 @@ typedef struct map_s {
 
 #define hashmap_size(x) (((hashmap_head_t*) hashmap_val(x))->size)
 #define hashmap_size_rel(RTERM, BASE) hashmap_size(rterm2wterm(RTERM, BASE))
+#define hashmap_make_hash(Key) make_hash2(Key)
+
+#define hashmap_restore_hash(Heap,Lvl,Key) \
+    (((Lvl) < 8) ? hashmap_make_hash(Key) >> (4*(Lvl)) : hashmap_make_hash(CONS(Heap, make_small((Lvl)>>3), (Key))) >> (4*((Lvl) & 7)))
+#define hashmap_shift_hash(Heap,Hx,Lvl,Key) \
+    (((++(Lvl)) & 7) ? (Hx) >> 4 : hashmap_make_hash(CONS(Heap, make_small((Lvl)>>3), Key)))
+
 
 /* erl_term.h stuff */
 #define make_map(x)		make_boxed((Eterm*)(x))
-- 
cgit v1.2.3


From d8c6b91a9dfb06dbe1143e6b06ef26dab153ca4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 24 Feb 2015 10:50:51 +0100
Subject: erts: Refactor maps:get/2, find/2 and is_key/2

---
 erts/emulator/beam/erl_map.c | 110 +++++++++++++------------------------------
 1 file changed, 33 insertions(+), 77 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 0ac5885e6b..304b5470f5 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -137,38 +137,45 @@ erts_maps_get_rel(Eterm key, Eterm map, Eterm *map_base)
 erts_maps_get(Eterm key, Eterm map)
 #endif
 {
-    Eterm *ks, *vs;
-    map_t *mp;
-    Uint n, i;
+    Uint32 hx;
+    if (is_map(map)) {
+	Eterm *ks, *vs;
+	map_t *mp;
+	Uint n, i;
 
-    mp  = (map_t *)map_val_rel(map, map_base);
-    n   = map_get_size(mp);
+	mp  = (map_t *)map_val_rel(map, map_base);
+	n   = map_get_size(mp);
 
-    if (n == 0) {
-        return NULL;
-    }
+	if (n == 0) {
+	    return NULL;
+	}
 
-    ks  = (Eterm *)tuple_val_rel(mp->keys, map_base) + 1;
-    vs  = map_get_values(mp);
+	ks  = (Eterm *)tuple_val_rel(mp->keys, map_base) + 1;
+	vs  = map_get_values(mp);
 
-    if (is_immed(key)) {
-        for (i = 0; i < n; i++) {
-            if (ks[i] == key) {
-                return &vs[i];
-            }
-        }
-    }
+	if (is_immed(key)) {
+	    for (i = 0; i < n; i++) {
+		if (ks[i] == key) {
+		    return &vs[i];
+		}
+	    }
+	}
 
-    for (i = 0; i < n; i++) {
-        if (eq_rel(ks[i], NULL, key, map_base)) {
-            return &vs[i];
-        }
+	for (i = 0; i < n; i++) {
+	    if (eq_rel(ks[i], NULL, key, map_base)) {
+		return &vs[i];
+	    }
+	}
+	return NULL;
     }
-    return NULL;
+    ASSERT(is_hashmap(map));
+    hx = hashmap_make_hash(key);
+
+    return hashmap_get(hx, key, map);
 }
 
 BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
-    if (is_map(BIF_ARG_2)) {
+    if (is_map(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
         Eterm *hp, res;
         const Eterm *value;
 
@@ -181,21 +188,6 @@ BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
             *hp++ = *value;
 	    BIF_RET(res);
 	}
-
-	BIF_RET(am_error);
-    } else if (is_hashmap(BIF_ARG_2)) {
-	Eterm *hp, res;
-	const Eterm *value;
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-
-	if ((value = hashmap_get(hx, BIF_ARG_1, BIF_ARG_2)) != NULL) {
-	    hp    = HAlloc(BIF_P, 3);
-	    res   = make_tuple(hp);
-	    *hp++ = make_arityval(2);
-	    *hp++ = am_ok;
-            *hp++ = *value;
-	    BIF_RET(res);
-	}
 	BIF_RET(am_error);
     }
     BIF_ERROR(BIF_P, BADARG);
@@ -207,7 +199,7 @@ BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
  */
 
 BIF_RETTYPE maps_get_2(BIF_ALIST_2) {
-    if (is_map(BIF_ARG_2)) {
+    if (is_map(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
 	Eterm *hp;
         Eterm error;
         const Eterm *value;
@@ -224,15 +216,7 @@ BIF_RETTYPE maps_get_2(BIF_ALIST_2) {
 	hp = HAlloc(BIF_P, 3);
 	BIF_P->fvalue = TUPLE2(hp, error, BIF_ARG_1);
 	BIF_ERROR(BIF_P, EXC_ERROR_2);
-    } else if (is_hashmap(BIF_ARG_2)) {
-	const Eterm *value;
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-
-	if ((value = hashmap_get(hx, BIF_ARG_1, BIF_ARG_2)) != NULL) {
-	    BIF_RET(*value);
-	}
     }
-
     BIF_ERROR(BIF_P, BADARG);
 }
 
@@ -361,36 +345,8 @@ error:
  */
 
 BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) {
-    if (is_map(BIF_ARG_2)) {
-	Eterm *ks, key;
-	map_t *mp;
-	Uint n,i;
-
-	mp  = (map_t*)map_val(BIF_ARG_2);
-	key = BIF_ARG_1;
-	n   = map_get_size(mp);
-	ks  = map_get_keys(mp);
-
-	if (n == 0)
-	    BIF_RET(am_false);
-
-	if (is_immed(key)) {
-	    for( i = 0; i < n; i++) {
-		if (ks[i] == key) {
-		    BIF_RET(am_true);
-		}
-	    }
-	}
-
-	for( i = 0; i < n; i++) {
-	    if (EQ(ks[i], key)) {
-		BIF_RET(am_true);
-	    }
-	}
-	BIF_RET(am_false);
-    } else if (is_hashmap(BIF_ARG_2)) {
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-	BIF_RET(hashmap_get(hx, BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
+    if (is_map(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
+	BIF_RET(erts_maps_get(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
     }
     BIF_ERROR(BIF_P, BADARG);
 }
-- 
cgit v1.2.3


From ad19eacbdb3b32bb74897ad2425808b9d669ccb8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Mon, 23 Feb 2015 15:37:55 +0100
Subject: erts: Move hashmap:put/3, update/3 to maps

---
 erts/emulator/beam/bif.tab       |   2 -
 erts/emulator/beam/erl_hashmap.c | 257 ---------------------------------------
 erts/emulator/beam/erl_map.c     | 247 +++++++++++++++++++++++++++++++++++++
 3 files changed, 247 insertions(+), 259 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 5b0f90d418..55a7b62e7d 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -615,8 +615,6 @@ bif erlang:fun_info_mfa/1
 bif erlang:get_keys/0
 
 # Hash Array Mappped Trie
-bif hashmap:put/3
-bif hashmap:update/3
 bif hashmap:remove/2
 bif hashmap:info/1
 bif hashmap:from_list/1
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 7e0e7fde04..594a404e9f 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -60,7 +60,6 @@ static char *format_binary(Uint64 x, char *b) {
 }
 #endif
 
-static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node, int is_update);
 
 /* for hashmap_from_list/1 */
 typedef struct {
@@ -97,32 +96,8 @@ BIF_RETTYPE hashmap_new_0(BIF_ALIST_0) {
 
 /* hashmap:put/3 */
 
-BIF_RETTYPE hashmap_put_3(BIF_ALIST_3) {
-    if (is_hashmap(BIF_ARG_3)) {
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-	Eterm map;
-
-	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 0);
-	ASSERT(is_hashmap(map));
-	BIF_RET(map);
-    }
-    BIF_ERROR(BIF_P, BADARG);
-}
-
 /* hashmap:update/3 */
 
-BIF_RETTYPE hashmap_update_3(BIF_ALIST_3) {
-    if (is_hashmap(BIF_ARG_3)) {
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-	Eterm map;
-
-	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 1);
-	if (is_value(map))
-	    BIF_RET(map);
-    }
-    BIF_ERROR(BIF_P, BADARG);
-}
-
 /* hashmap:to_list/1 */
 
 /* hashmap:from_list/1 */
@@ -168,238 +143,6 @@ BIF_RETTYPE hashmap_merge_2(BIF_ALIST_2) {
 /* impl. */
 
 
-static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
-			    Eterm node, int is_update) {
-    Eterm *hp = NULL, *nhp = NULL;
-    Eterm *ptr;
-    Eterm hdr,res,ckey,fake;
-    Eterm th[2];
-    Uint32 ix, cix, bp, hval, chx;
-    Uint slot, lvl = 0, clvl;
-    Uint size = 0, n = 0, update_size = 1;
-    DECLARE_ESTACK(stack);
-
-    for (;;) {
-	switch(primary_tag(node)) {
-	    case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */
-		ptr  = list_val(node);
-		ckey = CAR(ptr);
-		if (EQ(ckey, key)) {
-		    update_size = 0;
-		    goto unroll;
-		}
-		if (is_update) {
-		    res = THE_NON_VALUE;
-		    goto bail_out;
-		}
-		goto insert_subnodes;
-	    case TAG_PRIMARY_BOXED:
-		ptr = boxed_val(node);
-		hdr = *ptr;
-		ASSERT(is_header(hdr));
-
-		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-		    case HAMT_SUBTAG_NODE_ARRAY:
-			ix    = hashmap_index(hx);
-			hx    = hashmap_shift_hash(th,hx,lvl,key);
-			size += HAMT_NODE_ARRAY_SZ;
-			ESTACK_PUSH2(stack, ix, node);
-			node  = ptr[ix+1];
-			break;
-		    case HAMT_SUBTAG_HEAD_ARRAY:
-			ix    = hashmap_index(hx);
-			hx    = hashmap_shift_hash(th,hx,lvl,key);
-			size += HAMT_HEAD_ARRAY_SZ;
-			ESTACK_PUSH2(stack, ix, node);
-			node  = ptr[ix+2];
-			break;
-		    case HAMT_SUBTAG_NODE_BITMAP:
-			hval = MAP_HEADER_VAL(hdr);
-			ix   = hashmap_index(hx);
-			bp   = 1 << ix;
-			slot = hashmap_bitcount(hval & (bp - 1));
-			n    = hashmap_bitcount(hval);
-
-			ESTACK_PUSH(stack, n);
-			ESTACK_PUSH3(stack, bp, slot, node);
-
-			/* occupied */
-			if (bp & hval) {
-			    hx    = hashmap_shift_hash(th,hx,lvl,key);
-			    node  = ptr[slot+1];
-			    ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17); 
-			    size += HAMT_NODE_BITMAP_SZ(n);
-			    break;
-			}
-			/* not occupied */
-			if (is_update) {
-			    res = THE_NON_VALUE;
-			    goto bail_out;
-			}
-			size += HAMT_NODE_BITMAP_SZ(n+1);
-			goto unroll;
-		    case HAMT_SUBTAG_HEAD_BITMAP:
-			hval = MAP_HEADER_VAL(hdr);
-			ix   = hashmap_index(hx);
-			bp   = 1 << ix;
-			slot = hashmap_bitcount(hval & (bp - 1));
-			n    = hashmap_bitcount(hval);
-
-			ESTACK_PUSH(stack, n);
-			ESTACK_PUSH3(stack, bp, slot, node);
-
-			/* occupied */
-			if (bp & hval) {
-			    hx    = hashmap_shift_hash(th,hx,lvl,key);
-			    node  = ptr[slot+2];
-			    ASSERT(HAMT_HEAD_BITMAP_SZ(n) <= 18); 
-			    size += HAMT_HEAD_BITMAP_SZ(n);
-			    break;
-			}
-			/* not occupied */
-			if (is_update) {
-			    res = THE_NON_VALUE;
-			    goto bail_out;
-			}
-			size += HAMT_HEAD_BITMAP_SZ(n+1);
-			goto unroll;
-		    default:
-			erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
-			break;
-		}
-		break;
-	    default:
-		erl_exit(1, "bad primary tag %p\r\n", node);
-		break;
-	}
-    }
-insert_subnodes:
-    clvl  = lvl;
-    chx   = hashmap_restore_hash(th,clvl,ckey);
-    size += HAMT_NODE_BITMAP_SZ(2);
-    ix    = hashmap_index(hx);
-    cix   = hashmap_index(chx);
-
-    while (cix == ix) {
-	ESTACK_PUSH(stack, 0);
-	ESTACK_PUSH3(stack, 1 << ix, 0, MAP_HEADER_HAMT_NODE_BITMAP(0));
-	size += HAMT_NODE_BITMAP_SZ(1);
-	hx    = hashmap_shift_hash(th,hx,lvl,key);
-	chx   = hashmap_shift_hash(th,chx,clvl,ckey);
-	ix    = hashmap_index(hx);
-	cix   = hashmap_index(chx);
-    }
-    ESTACK_PUSH3(stack, cix, ix, node);
-
-unroll:
-    size += 2;
-    hp  = HAlloc(p, size);
-    res = CONS(hp, key, value); hp += 2;
-
-    do {
-	node = ESTACK_POP(stack);
-	switch(primary_tag(node)) {
-	    case TAG_PRIMARY_LIST:
-		ix   = (Uint32) ESTACK_POP(stack);
-		cix  = (Uint32) ESTACK_POP(stack);
-
-		nhp   = hp;
-		*hp++ = MAP_HEADER_HAMT_NODE_BITMAP((1 << ix) | (1 << cix));
-		if (ix < cix) {
-		    *hp++ = res;
-		    *hp++ = node;
-		} else {
-		    *hp++ = node;
-		    *hp++ = res;
-		}
-		res = make_hashmap(nhp);
-		break;
-	    case TAG_PRIMARY_HEADER:
-		/* subnodes, fake it */
-		fake = node;
-		node = make_boxed(&fake);
-	    case TAG_PRIMARY_BOXED:
-		ptr = boxed_val(node);
-		hdr = *ptr;
-		ASSERT(is_header(hdr));
-
-		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-		    case HAMT_SUBTAG_NODE_ARRAY:
-			slot  = (Uint)   ESTACK_POP(stack);
-			nhp   = hp;
-			n     = HAMT_NODE_ARRAY_SZ;
-			while(n--) { *hp++ = *ptr++; }
-			nhp[slot+1] = res;
-			res = make_hashmap(nhp);
-			break;
-		    case HAMT_SUBTAG_HEAD_ARRAY:
-			slot  = (Uint)   ESTACK_POP(stack);
-			nhp   = hp;
-			n     = HAMT_HEAD_ARRAY_SZ - 2;
-			*hp++ = MAP_HEADER_HAMT_HEAD_ARRAY; ptr++;
-			*hp++ = (*ptr++) + update_size;
-			while(n--) { *hp++ = *ptr++; }
-			nhp[slot+2] = res;
-			res = make_hashmap(nhp);
-			break;
-		    case HAMT_SUBTAG_NODE_BITMAP:
-			slot  = (Uint)   ESTACK_POP(stack);
-			bp    = (Uint32) ESTACK_POP(stack);
-			n     = (Uint32) ESTACK_POP(stack);
-			hval  = MAP_HEADER_VAL(hdr);
-			nhp   = hp;
-			*hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval | bp); ptr++;
-
-			n -= slot;
-			while(slot--) { *hp++ = *ptr++; }
-			*hp++ = res;
-			if (hval & bp) { ptr++; n--; }
-			while(n--) { *hp++ = *ptr++; }
-
-			if ((hval | bp) == 0xffff) {
-			    *nhp = make_arityval(16);
-			}
-			res = make_hashmap(nhp);
-			break;
-		    case HAMT_SUBTAG_HEAD_BITMAP:
-			slot  = (Uint)   ESTACK_POP(stack);
-			bp    = (Uint32) ESTACK_POP(stack);
-			n     = (Uint32) ESTACK_POP(stack);
-			hval  = MAP_HEADER_VAL(hdr);
-			nhp   = hp;
-			*hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(hval | bp); ptr++;
-			*hp++ = (*ptr++) + update_size;
-
-			n -= slot;
-			while(slot--) { *hp++ = *ptr++; }
-			*hp++ = res;
-			if (hval & bp) { ptr++; n--; }
-			while(n--) { *hp++ = *ptr++; }
-
-			if ((hval | bp) == 0xffff) {
-			    *nhp = MAP_HEADER_HAMT_HEAD_ARRAY;
-			}
-			res = make_hashmap(nhp);
-			break;
-		    default:
-			erl_exit(1, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
-			break;
-		}
-		break;
-	    default:
-		erl_exit(1, "bad primary tag %x\r\n", primary_tag(node));
-		break;
-	}
-
-    } while(!ESTACK_ISEMPTY(stack));
-
-bail_out:
-    DESTROY_ESTACK(stack);
-    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
-    ERTS_HOLE_CHECK(p);
-    return res;
-}
-
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node) {
     Eterm *hp = NULL, *nhp = NULL, *hp_end = NULL;
     Eterm th[2];
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 304b5470f5..f99a1b431b 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -64,6 +64,7 @@
  */
 
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
+static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node, int is_update);
 static Eterm hashmap_to_list(Process *p, Eterm map);
 static Eterm hashmap_keys(Process *p, Eterm map);
 static Eterm hashmap_values(Process *p, Eterm map);
@@ -594,6 +595,13 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
 BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
     if (is_map(BIF_ARG_3)) {
 	BIF_RET(erts_maps_put(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3));
+    } else if (is_hashmap(BIF_ARG_3)) {
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
+	Eterm map;
+
+	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 0);
+	ASSERT(is_hashmap(map));
+	BIF_RET(map);
     }
     BIF_ERROR(BIF_P, BADARG);
 }
@@ -748,6 +756,13 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
 	if (erts_maps_update(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &res)) {
 	    BIF_RET(res);
 	}
+    } else if (is_hashmap(BIF_ARG_3)) {
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
+	Eterm map;
+
+	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 1);
+	if (is_value(map))
+	    BIF_RET(map);
     }
     BIF_ERROR(BIF_P, BADARG);
 }
@@ -920,6 +935,238 @@ static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
     return NULL;
 }
 
+static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
+			    Eterm node, int is_update) {
+    Eterm *hp = NULL, *nhp = NULL;
+    Eterm *ptr;
+    Eterm hdr,res,ckey,fake;
+    Eterm th[2];
+    Uint32 ix, cix, bp, hval, chx;
+    Uint slot, lvl = 0, clvl;
+    Uint size = 0, n = 0, update_size = 1;
+    DECLARE_ESTACK(stack);
+
+    for (;;) {
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */
+		ptr  = list_val(node);
+		ckey = CAR(ptr);
+		if (EQ(ckey, key)) {
+		    update_size = 0;
+		    goto unroll;
+		}
+		if (is_update) {
+		    res = THE_NON_VALUE;
+		    goto bail_out;
+		}
+		goto insert_subnodes;
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			ix    = hashmap_index(hx);
+			hx    = hashmap_shift_hash(th,hx,lvl,key);
+			size += HAMT_NODE_ARRAY_SZ;
+			ESTACK_PUSH2(stack, ix, node);
+			node  = ptr[ix+1];
+			break;
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			ix    = hashmap_index(hx);
+			hx    = hashmap_shift_hash(th,hx,lvl,key);
+			size += HAMT_HEAD_ARRAY_SZ;
+			ESTACK_PUSH2(stack, ix, node);
+			node  = ptr[ix+2];
+			break;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+			n    = hashmap_bitcount(hval);
+
+			ESTACK_PUSH(stack, n);
+			ESTACK_PUSH3(stack, bp, slot, node);
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
+			    node  = ptr[slot+1];
+			    ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17);
+			    size += HAMT_NODE_BITMAP_SZ(n);
+			    break;
+			}
+			/* not occupied */
+			if (is_update) {
+			    res = THE_NON_VALUE;
+			    goto bail_out;
+			}
+			size += HAMT_NODE_BITMAP_SZ(n+1);
+			goto unroll;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+			n    = hashmap_bitcount(hval);
+
+			ESTACK_PUSH(stack, n);
+			ESTACK_PUSH3(stack, bp, slot, node);
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
+			    node  = ptr[slot+2];
+			    ASSERT(HAMT_HEAD_BITMAP_SZ(n) <= 18);
+			    size += HAMT_HEAD_BITMAP_SZ(n);
+			    break;
+			}
+			/* not occupied */
+			if (is_update) {
+			    res = THE_NON_VALUE;
+			    goto bail_out;
+			}
+			size += HAMT_HEAD_BITMAP_SZ(n+1);
+			goto unroll;
+		    default:
+			erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
+			break;
+		}
+		break;
+	    default:
+		erl_exit(1, "bad primary tag %p\r\n", node);
+		break;
+	}
+    }
+insert_subnodes:
+    clvl  = lvl;
+    chx   = hashmap_restore_hash(th,clvl,ckey);
+    size += HAMT_NODE_BITMAP_SZ(2);
+    ix    = hashmap_index(hx);
+    cix   = hashmap_index(chx);
+
+    while (cix == ix) {
+	ESTACK_PUSH(stack, 0);
+	ESTACK_PUSH3(stack, 1 << ix, 0, MAP_HEADER_HAMT_NODE_BITMAP(0));
+	size += HAMT_NODE_BITMAP_SZ(1);
+	hx    = hashmap_shift_hash(th,hx,lvl,key);
+	chx   = hashmap_shift_hash(th,chx,clvl,ckey);
+	ix    = hashmap_index(hx);
+	cix   = hashmap_index(chx);
+    }
+    ESTACK_PUSH3(stack, cix, ix, node);
+
+unroll:
+    size += 2;
+    hp  = HAlloc(p, size);
+    res = CONS(hp, key, value); hp += 2;
+
+    do {
+	node = ESTACK_POP(stack);
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST:
+		ix   = (Uint32) ESTACK_POP(stack);
+		cix  = (Uint32) ESTACK_POP(stack);
+
+		nhp   = hp;
+		*hp++ = MAP_HEADER_HAMT_NODE_BITMAP((1 << ix) | (1 << cix));
+		if (ix < cix) {
+		    *hp++ = res;
+		    *hp++ = node;
+		} else {
+		    *hp++ = node;
+		    *hp++ = res;
+		}
+		res = make_hashmap(nhp);
+		break;
+	    case TAG_PRIMARY_HEADER:
+		/* subnodes, fake it */
+		fake = node;
+		node = make_boxed(&fake);
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			slot  = (Uint)   ESTACK_POP(stack);
+			nhp   = hp;
+			n     = HAMT_NODE_ARRAY_SZ;
+			while(n--) { *hp++ = *ptr++; }
+			nhp[slot+1] = res;
+			res = make_hashmap(nhp);
+			break;
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			slot  = (Uint)   ESTACK_POP(stack);
+			nhp   = hp;
+			n     = HAMT_HEAD_ARRAY_SZ - 2;
+			*hp++ = MAP_HEADER_HAMT_HEAD_ARRAY; ptr++;
+			*hp++ = (*ptr++) + update_size;
+			while(n--) { *hp++ = *ptr++; }
+			nhp[slot+2] = res;
+			res = make_hashmap(nhp);
+			break;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			slot  = (Uint)   ESTACK_POP(stack);
+			bp    = (Uint32) ESTACK_POP(stack);
+			n     = (Uint32) ESTACK_POP(stack);
+			hval  = MAP_HEADER_VAL(hdr);
+			nhp   = hp;
+			*hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval | bp); ptr++;
+
+			n -= slot;
+			while(slot--) { *hp++ = *ptr++; }
+			*hp++ = res;
+			if (hval & bp) { ptr++; n--; }
+			while(n--) { *hp++ = *ptr++; }
+
+			if ((hval | bp) == 0xffff) {
+			    *nhp = make_arityval(16);
+			}
+			res = make_hashmap(nhp);
+			break;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			slot  = (Uint)   ESTACK_POP(stack);
+			bp    = (Uint32) ESTACK_POP(stack);
+			n     = (Uint32) ESTACK_POP(stack);
+			hval  = MAP_HEADER_VAL(hdr);
+			nhp   = hp;
+			*hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(hval | bp); ptr++;
+			*hp++ = (*ptr++) + update_size;
+
+			n -= slot;
+			while(slot--) { *hp++ = *ptr++; }
+			*hp++ = res;
+			if (hval & bp) { ptr++; n--; }
+			while(n--) { *hp++ = *ptr++; }
+
+			if ((hval | bp) == 0xffff) {
+			    *nhp = MAP_HEADER_HAMT_HEAD_ARRAY;
+			}
+			res = make_hashmap(nhp);
+			break;
+		    default:
+			erl_exit(1, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
+			break;
+		}
+		break;
+	    default:
+		erl_exit(1, "bad primary tag %x\r\n", primary_tag(node));
+		break;
+	}
+
+    } while(!ESTACK_ISEMPTY(stack));
+
+bail_out:
+    DESTROY_ESTACK(stack);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+    ERTS_HOLE_CHECK(p);
+    return res;
+}
+
 static Eterm hashmap_keys(Process* p, Eterm node) {
     DECLARE_WSTACK(stack);
     hashmap_head_t* root;
-- 
cgit v1.2.3


From 8c9341288af6b66f0fd5caf229c87af29c59e711 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Mon, 23 Feb 2015 17:58:52 +0100
Subject: erts: Reindent erts_maps_update

---
 erts/emulator/beam/erl_map.c | 78 ++++++++++++++++++++++----------------------
 1 file changed, 39 insertions(+), 39 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index f99a1b431b..3e1092bfbe 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -698,56 +698,56 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
  */
 
 int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) {
-	Sint n,i;
-	Eterm* hp,*shp;
-	Eterm *ks,*vs;
-	map_t *mp = (map_t*)map_val(map);
+    Sint n,i;
+    Eterm* hp,*shp;
+    Eterm *ks,*vs;
+    map_t *mp = (map_t*)map_val(map);
 
-	if ((n = map_get_size(mp)) == 0) {
-	    return 0;
-	}
+    if ((n = map_get_size(mp)) == 0) {
+	return 0;
+    }
 
-	ks  = map_get_keys(mp);
-	vs  = map_get_values(mp);
+    ks  = map_get_keys(mp);
+    vs  = map_get_values(mp);
 
-	/* only allocate for values,
-	 * assume key-tuple will be intact
-	 */
+    /* only allocate for values,
+     * assume key-tuple will be intact
+     */
 
-	hp  = HAlloc(p, MAP_HEADER_SIZE + n);
-	shp = hp;
-	*hp++ = MAP_HEADER;
-	*hp++ = n;
-	*hp++ = mp->keys;
+    hp  = HAlloc(p, MAP_HEADER_SIZE + n);
+    shp = hp;
+    *hp++ = MAP_HEADER;
+    *hp++ = n;
+    *hp++ = mp->keys;
 
-	if (is_immed(key)) {
-	    for( i = 0; i < n; i ++) {
-		if (ks[i] == key) {
-		    goto found_key;
-		} else {
-		    *hp++ = *vs++;
-		}
+    if (is_immed(key)) {
+	for( i = 0; i < n; i ++) {
+	    if (ks[i] == key) {
+		goto found_key;
+	    } else {
+		*hp++ = *vs++;
 	    }
-	} else {
-	    for( i = 0; i < n; i ++) {
-		if (EQ(ks[i], key)) {
-		    goto found_key;
-		} else {
-		    *hp++ = *vs++;
-		}
+	}
+    } else {
+	for( i = 0; i < n; i ++) {
+	    if (EQ(ks[i], key)) {
+		goto found_key;
+	    } else {
+		*hp++ = *vs++;
 	    }
 	}
+    }
 
-	HRelease(p, shp + MAP_HEADER_SIZE + n, shp);
-	return 0;
+    HRelease(p, shp + MAP_HEADER_SIZE + n, shp);
+    return 0;
 
 found_key:
-	*hp++ = value;
-	vs++;
-	if (++i < n)
-	    sys_memcpy(hp, vs, (n - i)*sizeof(Eterm));
-	*res = make_map(shp);
-	return 1;
+    *hp++ = value;
+    vs++;
+    if (++i < n)
+	sys_memcpy(hp, vs, (n - i)*sizeof(Eterm));
+    *res = make_map(shp);
+    return 1;
 }
 
 BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
-- 
cgit v1.2.3


From 1d2c8a4280e0eb5d7182986862f20045bc3ed6f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 24 Feb 2015 14:17:51 +0100
Subject: erts: Refactor maps:update/3 and maps:put/3

---
 erts/emulator/beam/erl_map.c | 368 ++++++++++++++++++++++++-------------------
 1 file changed, 206 insertions(+), 162 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 3e1092bfbe..8c44f45125 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -486,122 +486,11 @@ BIF_RETTYPE maps_new_0(BIF_ALIST_0) {
     BIF_RET(make_map(mp));
 }
 
-/* maps:put/3
- */
-
-Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
-    Sint n,i;
-    Sint c = 0;
-    Eterm* hp, *shp;
-    Eterm *ks,*vs, res, tup;
-    map_t *mp = (map_t*)map_val(map);
-
-    n = map_get_size(mp);
-
-    if (n == 0) {
-	hp    = HAlloc(p, MAP_HEADER_SIZE + 1 + 2);
-	tup   = make_tuple(hp);
-	*hp++ = make_arityval(1);
-	*hp++ = key;
-	res   = make_map(hp);
-	*hp++ = MAP_HEADER;
-	*hp++ = 1;
-	*hp++ = tup;
-	*hp++ = value;
-
-	return res;
-    }
-
-    ks  = map_get_keys(mp);
-    vs  = map_get_values(mp);
-
-    /* only allocate for values,
-     * assume key-tuple will be intact
-     */
-
-    hp  = HAlloc(p, MAP_HEADER_SIZE + n);
-    shp = hp; /* save hp, used if optimistic update fails */
-    res = make_map(hp);
-    *hp++ = MAP_HEADER;
-    *hp++ = n;
-    *hp++ = mp->keys;
-
-    if (is_immed(key)) {
-	for( i = 0; i < n; i ++) {
-	    if (ks[i] == key) {
-		*hp++ = value;
-		vs++;
-		c = 1;
-	    } else {
-		*hp++ = *vs++;
-	    }
-	}
-    } else {
-	for( i = 0; i < n; i ++) {
-	    if (EQ(ks[i], key)) {
-		*hp++ = value;
-		vs++;
-		c = 1;
-	    } else {
-		*hp++ = *vs++;
-	    }
-	}
-    }
-
-    if (c)
-	return res;
-
-    /* need to make a new tuple,
-     * use old hp since it needs to be recreated anyway.
-     */
-    tup    = make_tuple(shp);
-    *shp++ = make_arityval(n+1);
-
-    hp    = HAlloc(p, 3 + n + 1);
-    res   = make_map(hp);
-    *hp++ = MAP_HEADER;
-    *hp++ = n + 1;
-    *hp++ = tup;
-
-    ks  = map_get_keys(mp);
-    vs  = map_get_values(mp);
-
-    ASSERT(n >= 0);
-
-    /* copy map in order */
-    while (n && ((c = CMP_TERM(*ks, key)) < 0)) {
-	*shp++ = *ks++;
-	*hp++  = *vs++;
-	n--;
-    }
-
-    *shp++ = key;
-    *hp++  = value;
-
-    ASSERT(n >= 0);
-
-    while(n--) {
-	*shp++ = *ks++;
-	*hp++  = *vs++;
-    }
-    /* we have one word remaining
-     * this will work out fine once we get the size word
-     * in the header.
-     */
-    *shp = make_pos_bignum_header(0);
-    return res;
-}
+/* maps:put/3 */
 
 BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
-    if (is_map(BIF_ARG_3)) {
+    if (is_map(BIF_ARG_3) || is_hashmap(BIF_ARG_3)) {
 	BIF_RET(erts_maps_put(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3));
-    } else if (is_hashmap(BIF_ARG_3)) {
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-	Eterm map;
-
-	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 0);
-	ASSERT(is_hashmap(map));
-	BIF_RET(map);
     }
     BIF_ERROR(BIF_P, BADARG);
 }
@@ -694,82 +583,237 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
-/* maps:update/3
- */
-
 int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) {
-    Sint n,i;
-    Eterm* hp,*shp;
-    Eterm *ks,*vs;
-    map_t *mp = (map_t*)map_val(map);
+    Uint32 hx;
+    if (is_map(map)) {
+	Sint n,i;
+	Eterm* hp,*shp;
+	Eterm *ks,*vs;
+	map_t *mp = (map_t*)map_val(map);
+
+	if ((n = map_get_size(mp)) == 0) {
+	    return 0;
+	}
+
+	ks  = map_get_keys(mp);
+	vs  = map_get_values(mp);
+
+	/* only allocate for values,
+	 * assume key-tuple will be intact
+	 */
+
+	hp  = HAlloc(p, MAP_HEADER_SIZE + n);
+	shp = hp;
+	*hp++ = MAP_HEADER;
+	*hp++ = n;
+	*hp++ = mp->keys;
+
+	if (is_immed(key)) {
+	    for( i = 0; i < n; i ++) {
+		if (ks[i] == key) {
+		    goto found_key;
+		} else {
+		    *hp++ = *vs++;
+		}
+	    }
+	} else {
+	    for( i = 0; i < n; i ++) {
+		if (EQ(ks[i], key)) {
+		    goto found_key;
+		} else {
+		    *hp++ = *vs++;
+		}
+	    }
+	}
 
-    if ((n = map_get_size(mp)) == 0) {
+	HRelease(p, shp + MAP_HEADER_SIZE + n, shp);
 	return 0;
+
+found_key:
+	*hp++ = value;
+	vs++;
+	if (++i < n)
+	    sys_memcpy(hp, vs, (n - i)*sizeof(Eterm));
+	*res = make_map(shp);
+	return 1;
     }
 
-    ks  = map_get_keys(mp);
-    vs  = map_get_values(mp);
+    ASSERT(is_hashmap(map));
+    hx = hashmap_make_hash(key);
+    *res = hashmap_insert(p, hx, key, value, map, 1);
+    if (is_value(*res))
+	return 1;
 
-    /* only allocate for values,
-     * assume key-tuple will be intact
-     */
+    return 0;
+}
 
-    hp  = HAlloc(p, MAP_HEADER_SIZE + n);
-    shp = hp;
-    *hp++ = MAP_HEADER;
-    *hp++ = n;
-    *hp++ = mp->keys;
+Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
+    Uint32 hx;
+    Eterm res;
+    if (is_map(map)) {
+	Sint n,i;
+	Sint c = 0;
+	Eterm* hp, *shp;
+	Eterm *ks, *vs, tup;
+	map_t *mp = (map_t*)map_val(map);
 
-    if (is_immed(key)) {
-	for( i = 0; i < n; i ++) {
-	    if (ks[i] == key) {
-		goto found_key;
-	    } else {
-		*hp++ = *vs++;
+	n = map_get_size(mp);
+
+	if (n == 0) {
+	    hp    = HAlloc(p, MAP_HEADER_SIZE + 1 + 2);
+	    tup   = make_tuple(hp);
+	    *hp++ = make_arityval(1);
+	    *hp++ = key;
+	    res   = make_map(hp);
+	    *hp++ = MAP_HEADER;
+	    *hp++ = 1;
+	    *hp++ = tup;
+	    *hp++ = value;
+
+	    return res;
+	}
+
+	ks = map_get_keys(mp);
+	vs = map_get_values(mp);
+
+	/* only allocate for values,
+	 * assume key-tuple will be intact
+	 */
+
+	hp  = HAlloc(p, MAP_HEADER_SIZE + n);
+	shp = hp; /* save hp, used if optimistic update fails */
+	res = make_map(hp);
+	*hp++ = MAP_HEADER;
+	*hp++ = n;
+	*hp++ = mp->keys;
+
+	if (is_immed(key)) {
+	    for( i = 0; i < n; i ++) {
+		if (ks[i] == key) {
+		    *hp++ = value;
+		    vs++;
+		    c = 1;
+		} else {
+		    *hp++ = *vs++;
+		}
+	    }
+	} else {
+	    for( i = 0; i < n; i ++) {
+		if (EQ(ks[i], key)) {
+		    *hp++ = value;
+		    vs++;
+		    c = 1;
+		} else {
+		    *hp++ = *vs++;
+		}
 	    }
 	}
-    } else {
-	for( i = 0; i < n; i ++) {
-	    if (EQ(ks[i], key)) {
-		goto found_key;
-	    } else {
-		*hp++ = *vs++;
+
+	if (c)
+	    return res;
+
+	/* the map will grow */
+
+	if (n >= MAP_SMALL_MAP_LIMIT) {
+	    hxnode_t *hxns;
+	    Uint32 sw, hx;
+	    Eterm tmp[2];
+
+	    HRelease(p, shp + MAP_HEADER_SIZE + n, shp);
+	    hp = HAlloc(p, (2 * (n + 1)));
+	    ks = map_get_keys(mp);
+	    vs = map_get_values(mp);
+
+	    /* create tmp hx values and leaf ptrs */
+	    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, (n + 1) * sizeof(hxnode_t));
+
+	    for (i = 0; i < n; i++) {
+		hx = hashmap_restore_hash(tmp,0,ks[i]);
+		swizzle32(sw,hx);
+		hxns[i].hx   = sw;
+		hxns[i].val  = CONS(hp, ks[i], vs[i]); hp += 2;
+		hxns[i].skip = 1;
+		hxns[i].i    = i;
 	    }
+
+	    hx = hashmap_restore_hash(tmp,0,key);
+	    swizzle32(sw,hx);
+	    hxns[i].hx   = sw;
+	    hxns[i].val  = CONS(hp, key, value); hp += 2;
+	    hxns[i].skip = 1;
+	    hxns[i].i    = n;
+
+	    res = hashmap_from_unsorted_array(p, hxns, n + 1);
+
+	    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
+	    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+
+	    return res;
+	}
+
+	/* still a small map. need to make a new tuple,
+	 * use old hp since it needs to be recreated anyway. */
+
+	tup    = make_tuple(shp);
+	*shp++ = make_arityval(n+1);
+
+	hp    = HAlloc(p, 3 + n + 1);
+	res   = make_map(hp);
+	*hp++ = MAP_HEADER;
+	*hp++ = n + 1;
+	*hp++ = tup;
+
+	ks = map_get_keys(mp);
+	vs = map_get_values(mp);
+
+	ASSERT(n >= 0);
+
+	/* copy map in order */
+	while (n && ((c = CMP_TERM(*ks, key)) < 0)) {
+	    *shp++ = *ks++;
+	    *hp++  = *vs++;
+	    n--;
 	}
+
+	*shp++ = key;
+	*hp++  = value;
+
+	ASSERT(n >= 0);
+
+	while(n--) {
+	    *shp++ = *ks++;
+	    *hp++  = *vs++;
+	}
+	/* we have one word remaining
+	 * this will work out fine once we get the size word
+	 * in the header.
+	 */
+	*shp = make_pos_bignum_header(0);
+	return res;
     }
+    ASSERT(is_hashmap(map));
 
-    HRelease(p, shp + MAP_HEADER_SIZE + n, shp);
-    return 0;
+    hx  = hashmap_make_hash(key);
+    res = hashmap_insert(p, hx, key, value, map, 0);
+    ASSERT(is_hashmap(res));
 
-found_key:
-    *hp++ = value;
-    vs++;
-    if (++i < n)
-	sys_memcpy(hp, vs, (n - i)*sizeof(Eterm));
-    *res = make_map(shp);
-    return 1;
+    return res;
 }
 
+/* maps:update/3 */
+
 BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
-    if (is_map(BIF_ARG_3)) {
+    if (is_map(BIF_ARG_3) || is_hashmap(BIF_ARG_3)) {
 	Eterm res;
 	if (erts_maps_update(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &res)) {
 	    BIF_RET(res);
 	}
-    } else if (is_hashmap(BIF_ARG_3)) {
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-	Eterm map;
-
-	map = hashmap_insert(BIF_P, hx, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 1);
-	if (is_value(map))
-	    BIF_RET(map);
     }
     BIF_ERROR(BIF_P, BADARG);
 }
 
 
-/* maps:values/1
- */
+/* maps:values/1 */
 
 BIF_RETTYPE maps_values_1(BIF_ALIST_1) {
     if (is_map(BIF_ARG_1)) {
-- 
cgit v1.2.3


From 1b963c425591b75410fe46d9af3b44e47692059e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Mon, 23 Feb 2015 15:49:43 +0100
Subject: erts: Move hashmap:remove/2 to maps

---
 erts/emulator/beam/bif.tab       |   1 -
 erts/emulator/beam/erl_hashmap.c | 258 ---------------------------------------
 erts/emulator/beam/erl_map.c     | 257 +++++++++++++++++++++++++++++++++++++-
 3 files changed, 255 insertions(+), 261 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 55a7b62e7d..320cd39da8 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -615,7 +615,6 @@ bif erlang:fun_info_mfa/1
 bif erlang:get_keys/0
 
 # Hash Array Mappped Trie
-bif hashmap:remove/2
 bif hashmap:info/1
 bif hashmap:from_list/1
 bif hashmap:new/0
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 594a404e9f..9b16a5a88f 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -69,7 +69,6 @@ typedef struct {
     Eterm  val;
 } hxnode_t;
 
-static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_from_list(Process *p, Eterm node);
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
@@ -115,14 +114,6 @@ BIF_RETTYPE hashmap_from_list_1(BIF_ALIST_1) {
 
 /* hashmap:remove/2 */
 
-BIF_RETTYPE hashmap_remove_2(BIF_ALIST_2) {
-    if (is_hashmap(BIF_ARG_2)) {
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-
-	BIF_RET(hashmap_delete(BIF_P, hx, BIF_ARG_1, BIF_ARG_2));
-    }
-    BIF_ERROR(BIF_P, BADARG);
-}
 /* hashmap:size/1 */
 
 /* erlang:is_hashmap/1 */
@@ -143,255 +134,6 @@ BIF_RETTYPE hashmap_merge_2(BIF_ALIST_2) {
 /* impl. */
 
 
-static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node) {
-    Eterm *hp = NULL, *nhp = NULL, *hp_end = NULL;
-    Eterm th[2];
-    Eterm *ptr;
-    Eterm hdr,res = node;
-    Uint32 ix, bp, hval;
-    Uint slot, lvl = 0;
-    Uint size = 0, n = 0;
-    DECLARE_ESTACK(stack);
-
-    for (;;) {
-	switch(primary_tag(node)) {
-	    case TAG_PRIMARY_LIST:
-		if (EQ(CAR(list_val(node)), key)) {
-		    goto unroll;
-		}
-		goto not_found;
-	    case TAG_PRIMARY_BOXED:
-		ptr = boxed_val(node);
-		hdr = *ptr;
-		ASSERT(is_header(hdr));
-
-		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-		    case HAMT_SUBTAG_NODE_ARRAY:
-			ix    = hashmap_index(hx);
-			hx    = hashmap_shift_hash(th,hx,lvl,key);
-			size += HAMT_NODE_ARRAY_SZ;
-			ESTACK_PUSH2(stack, ix, node);
-			node  = ptr[ix+1];
-			break;
-		    case HAMT_SUBTAG_HEAD_ARRAY:
-			ix    = hashmap_index(hx);
-			hx    = hashmap_shift_hash(th,hx,lvl,key);
-			size += HAMT_HEAD_ARRAY_SZ;
-			ESTACK_PUSH2(stack, ix, node);
-			node  = ptr[ix+2];
-			break;
-		    case HAMT_SUBTAG_NODE_BITMAP:
-			hval = MAP_HEADER_VAL(hdr);
-			ix   = hashmap_index(hx);
-			bp   = 1 << ix;
-			slot = hashmap_bitcount(hval & (bp - 1));
-			n    = hashmap_bitcount(hval);
-
-			ESTACK_PUSH(stack, n);
-			ESTACK_PUSH3(stack, bp, slot, node);
-
-			/* occupied */
-			if (bp & hval) {
-			    hx    = hashmap_shift_hash(th,hx,lvl,key);
-			    node  = ptr[slot+1];
-			    ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17);
-			    size += HAMT_NODE_BITMAP_SZ(n);
-			    break;
-			}
-			/* not occupied */
-			goto not_found;
-		    case HAMT_SUBTAG_HEAD_BITMAP:
-			hval = MAP_HEADER_VAL(hdr);
-			ix   = hashmap_index(hx);
-			bp   = 1 << ix;
-			slot = hashmap_bitcount(hval & (bp - 1));
-			n    = hashmap_bitcount(hval);
-
-			ESTACK_PUSH(stack, n);
-			ESTACK_PUSH3(stack, bp, slot, node);
-
-			/* occupied */
-			if (bp & hval) {
-			    hx    = hashmap_shift_hash(th,hx,lvl,key);
-			    node  = ptr[slot+2];
-			    ASSERT(HAMT_HEAD_BITMAP_SZ(n) <= 18);
-			    size += HAMT_HEAD_BITMAP_SZ(n);
-			    break;
-			}
-			/* not occupied */
-			goto not_found;
-		    default:
-			erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
-			break;
-		}
-		break;
-	    default:
-		erl_exit(1, "bad primary tag %p\r\n", node);
-		break;
-	}
-    }
-
-unroll:
-    /* the size is bounded and atleast one less than the previous size */
-    size  -= 1;
-    hp     = HAlloc(p, size);
-    hp_end = hp + size;
-    res    = THE_NON_VALUE;
-
-    do {
-	node = ESTACK_POP(stack);
-
-	/* all nodes are things */
-	ptr = boxed_val(node);
-	hdr = *ptr;
-	ASSERT(is_header(hdr));
-
-	switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-	    case HAMT_SUBTAG_NODE_ARRAY:
-		ix  = (Uint) ESTACK_POP(stack);
-		nhp = hp;
-		if (res == THE_NON_VALUE) {
-		    *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(0xffff ^ (1 << ix)); ptr++;
-		    n     = 16;
-		    n    -= ix;
-		    while(ix--) { *hp++ = *ptr++; }
-		    ptr++; n--;
-		    while(n--) { *hp++ = *ptr++; }
-		    res = make_hashmap(nhp);
-		} else {
-		    n = HAMT_NODE_ARRAY_SZ;
-		    while(n--) { *hp++ = *ptr++; }
-		    nhp[ix+1] = res;
-		    res = make_hashmap(nhp);
-		}
-		break;
-	    case HAMT_SUBTAG_HEAD_ARRAY:
-		ix  = (Uint) ESTACK_POP(stack);
-		nhp = hp;
-		if (res == THE_NON_VALUE) {
-		    n     = 16;
-		    n    -= ix;
-		    *hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(0xffff ^ (1 << ix)); ptr++;
-		    *hp++ = (*ptr++) - 1;
-		    while(ix--) { *hp++ = *ptr++; }
-		    ptr++; n--;
-		    while(n--) { *hp++ = *ptr++; }
-		    res = make_hashmap(nhp);
-		} else {
-		    n     = 16;
-		    *hp++ = MAP_HEADER_HAMT_HEAD_ARRAY; ptr++;
-		    *hp++ = (*ptr++) - 1;
-		    while(n--) { *hp++ = *ptr++; }
-		    nhp[ix+2] = res;
-		    res = make_hashmap(nhp);
-		}
-		break;
-	    case HAMT_SUBTAG_NODE_BITMAP:
-		slot = (Uint)   ESTACK_POP(stack);
-		bp   = (Uint32) ESTACK_POP(stack);
-		n    = (Uint32) ESTACK_POP(stack);
-		nhp  = hp;
-
-		/* bitmap change matrix
-		 * res | none    leaf    bitmap
-		 * ----------------------------
-		 * n=1 | remove  remove  keep
-		 * n=2 | other   keep    keep
-		 * n>2 | shrink  keep    keep
-		 *
-		 * other: (remember, n is 2)
-		 *   shrink if the other bitmap value is a bitmap node
-		 *   remove if the other bitmap value is a leaf
-		 *
-		 * remove:
-		 *   this bitmap node is removed, res is moved up in tree (could be none)
-		 *   this is a special case of shrink
-		 *
-		 * keep:
-		 *   the current path index is still used down in the tree, need to keep it
-		 *   copy as usual with the updated res
-		 *
-		 * shrink:
-		 *   the current path index is no longer used down in the tree, remove it (shrink)
-		 */
-		if (res == THE_NON_VALUE) {
-		    if (n == 1) {
-			break;
-		    } else if (n == 2) {
-			if (slot == 0) {
-			    ix = 2; /* off by one 'cause hdr */
-			} else {
-			    ix = 1; /* off by one 'cause hdr */
-			}
-			if (primary_tag(ptr[ix]) == TAG_PRIMARY_LIST) {
-			    res = ptr[ix];
-			} else {
-			    hval  = MAP_HEADER_VAL(hdr);
-			    *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval ^ bp);
-			    *hp++ = ptr[ix];
-			    res = make_hashmap(nhp);
-			}
-		    } else {
-			/* n > 2 */
-			hval  = MAP_HEADER_VAL(hdr);
-			*hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval ^ bp); ptr++;
-			n    -= slot;
-			while(slot--) { *hp++ = *ptr++; }
-			ptr++; n--;
-			while(n--) { *hp++ = *ptr++; }
-			res = make_hashmap(nhp);
-		    }
-		} else if (primary_tag(res) == TAG_PRIMARY_LIST && n == 1) {
-		    break;
-		} else {
-		    /* res is bitmap or leaf && n > 1, keep */
-		    n    -= slot;
-		    *hp++ = *ptr++;
-		    while(slot--) { *hp++ = *ptr++; }
-		    *hp++ = res;
-		    ptr++; n--;
-		    while(n--) { *hp++ = *ptr++; }
-		    res = make_hashmap(nhp);
-		}
-		break;
-	    case HAMT_SUBTAG_HEAD_BITMAP:
-		slot = (Uint)   ESTACK_POP(stack);
-		bp   = (Uint32) ESTACK_POP(stack);
-		n    = (Uint32) ESTACK_POP(stack);
-		nhp  = hp;
-
-		if (res != THE_NON_VALUE) {
-		    *hp++ = *ptr++;
-		    *hp++ = (*ptr++) - 1;
-		    n    -= slot;
-		    while(slot--) { *hp++ = *ptr++; }
-		    *hp++ = res;
-		    ptr++; n--;
-		    while(n--) { *hp++ = *ptr++; }
-		} else {
-		    hval  = MAP_HEADER_VAL(hdr);
-		    *hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(hval ^ bp); ptr++;
-		    *hp++ = (*ptr++) - 1;
-		    n    -= slot;
-		    while(slot--) { *hp++ = *ptr++; }
-		    ptr++; n--;
-		    while(n--) { *hp++ = *ptr++; }
-		}
-		res = make_hashmap(nhp);
-		break;
-	    default:
-		erl_exit(1, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
-		break;
-	}
-    } while(!ESTACK_ISEMPTY(stack));
-    HRelease(p, hp_end, hp);
-not_found:
-    DESTROY_ESTACK(stack);
-    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
-    ERTS_HOLE_CHECK(p);
-    return res;
-}
-
 #define swizzle32(D,S) \
     do { \
 	(D) = ((S) & 0x0000000f) << 28 | ((S) & 0x000000f0) << 20  \
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 8c44f45125..672ac9f513 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -68,6 +68,7 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm
 static Eterm hashmap_to_list(Process *p, Eterm map);
 static Eterm hashmap_keys(Process *p, Eterm map);
 static Eterm hashmap_values(Process *p, Eterm map);
+static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 
 /* erlang:map_size/1
  * the corresponding instruction is implemented in:
@@ -495,8 +496,7 @@ BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
-/* maps:remove/3
- */
+/* maps:remove/3 */
 
 int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
     Sint n;
@@ -579,6 +579,9 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
 	if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) {
 	    BIF_RET(res);
 	}
+    } else if (is_hashmap(BIF_ARG_2)) {
+	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
+	BIF_RET(hashmap_delete(BIF_P, hx, BIF_ARG_1, BIF_ARG_2));
     }
     BIF_ERROR(BIF_P, BADARG);
 }
@@ -1245,6 +1248,256 @@ static Eterm hashmap_values(Process* p, Eterm node) {
     return res;
 }
 
+static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node) {
+    Eterm *hp = NULL, *nhp = NULL, *hp_end = NULL;
+    Eterm th[2];
+    Eterm *ptr;
+    Eterm hdr,res = node;
+    Uint32 ix, bp, hval;
+    Uint slot, lvl = 0;
+    Uint size = 0, n = 0;
+    DECLARE_ESTACK(stack);
+
+    for (;;) {
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST:
+		if (EQ(CAR(list_val(node)), key)) {
+		    goto unroll;
+		}
+		goto not_found;
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			ix    = hashmap_index(hx);
+			hx    = hashmap_shift_hash(th,hx,lvl,key);
+			size += HAMT_NODE_ARRAY_SZ;
+			ESTACK_PUSH2(stack, ix, node);
+			node  = ptr[ix+1];
+			break;
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			ix    = hashmap_index(hx);
+			hx    = hashmap_shift_hash(th,hx,lvl,key);
+			size += HAMT_HEAD_ARRAY_SZ;
+			ESTACK_PUSH2(stack, ix, node);
+			node  = ptr[ix+2];
+			break;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+			n    = hashmap_bitcount(hval);
+
+			ESTACK_PUSH(stack, n);
+			ESTACK_PUSH3(stack, bp, slot, node);
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
+			    node  = ptr[slot+1];
+			    ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17);
+			    size += HAMT_NODE_BITMAP_SZ(n);
+			    break;
+			}
+			/* not occupied */
+			goto not_found;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			hval = MAP_HEADER_VAL(hdr);
+			ix   = hashmap_index(hx);
+			bp   = 1 << ix;
+			slot = hashmap_bitcount(hval & (bp - 1));
+			n    = hashmap_bitcount(hval);
+
+			ESTACK_PUSH(stack, n);
+			ESTACK_PUSH3(stack, bp, slot, node);
+
+			/* occupied */
+			if (bp & hval) {
+			    hx    = hashmap_shift_hash(th,hx,lvl,key);
+			    node  = ptr[slot+2];
+			    ASSERT(HAMT_HEAD_BITMAP_SZ(n) <= 18);
+			    size += HAMT_HEAD_BITMAP_SZ(n);
+			    break;
+			}
+			/* not occupied */
+			goto not_found;
+		    default:
+			erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
+			break;
+		}
+		break;
+	    default:
+		erl_exit(1, "bad primary tag %p\r\n", node);
+		break;
+	}
+    }
+
+unroll:
+    /* the size is bounded and atleast one less than the previous size */
+    size  -= 1;
+    hp     = HAlloc(p, size);
+    hp_end = hp + size;
+    res    = THE_NON_VALUE;
+
+    do {
+	node = ESTACK_POP(stack);
+
+	/* all nodes are things */
+	ptr = boxed_val(node);
+	hdr = *ptr;
+	ASSERT(is_header(hdr));
+
+	switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+	    case HAMT_SUBTAG_NODE_ARRAY:
+		ix  = (Uint) ESTACK_POP(stack);
+		nhp = hp;
+		if (res == THE_NON_VALUE) {
+		    *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(0xffff ^ (1 << ix)); ptr++;
+		    n     = 16;
+		    n    -= ix;
+		    while(ix--) { *hp++ = *ptr++; }
+		    ptr++; n--;
+		    while(n--) { *hp++ = *ptr++; }
+		    res = make_hashmap(nhp);
+		} else {
+		    n = HAMT_NODE_ARRAY_SZ;
+		    while(n--) { *hp++ = *ptr++; }
+		    nhp[ix+1] = res;
+		    res = make_hashmap(nhp);
+		}
+		break;
+	    case HAMT_SUBTAG_HEAD_ARRAY:
+		ix  = (Uint) ESTACK_POP(stack);
+		nhp = hp;
+		if (res == THE_NON_VALUE) {
+		    n     = 16;
+		    n    -= ix;
+		    *hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(0xffff ^ (1 << ix)); ptr++;
+		    *hp++ = (*ptr++) - 1;
+		    while(ix--) { *hp++ = *ptr++; }
+		    ptr++; n--;
+		    while(n--) { *hp++ = *ptr++; }
+		    res = make_hashmap(nhp);
+		} else {
+		    n     = 16;
+		    *hp++ = MAP_HEADER_HAMT_HEAD_ARRAY; ptr++;
+		    *hp++ = (*ptr++) - 1;
+		    while(n--) { *hp++ = *ptr++; }
+		    nhp[ix+2] = res;
+		    res = make_hashmap(nhp);
+		}
+		break;
+	    case HAMT_SUBTAG_NODE_BITMAP:
+		slot = (Uint)   ESTACK_POP(stack);
+		bp   = (Uint32) ESTACK_POP(stack);
+		n    = (Uint32) ESTACK_POP(stack);
+		nhp  = hp;
+
+		/* bitmap change matrix
+		 * res | none    leaf    bitmap
+		 * ----------------------------
+		 * n=1 | remove  remove  keep
+		 * n=2 | other   keep    keep
+		 * n>2 | shrink  keep    keep
+		 *
+		 * other: (remember, n is 2)
+		 *   shrink if the other bitmap value is a bitmap node
+		 *   remove if the other bitmap value is a leaf
+		 *
+		 * remove:
+		 *   this bitmap node is removed, res is moved up in tree (could be none)
+		 *   this is a special case of shrink
+		 *
+		 * keep:
+		 *   the current path index is still used down in the tree, need to keep it
+		 *   copy as usual with the updated res
+		 *
+		 * shrink:
+		 *   the current path index is no longer used down in the tree, remove it (shrink)
+		 */
+		if (res == THE_NON_VALUE) {
+		    if (n == 1) {
+			break;
+		    } else if (n == 2) {
+			if (slot == 0) {
+			    ix = 2; /* off by one 'cause hdr */
+			} else {
+			    ix = 1; /* off by one 'cause hdr */
+			}
+			if (primary_tag(ptr[ix]) == TAG_PRIMARY_LIST) {
+			    res = ptr[ix];
+			} else {
+			    hval  = MAP_HEADER_VAL(hdr);
+			    *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval ^ bp);
+			    *hp++ = ptr[ix];
+			    res = make_hashmap(nhp);
+			}
+		    } else {
+			/* n > 2 */
+			hval  = MAP_HEADER_VAL(hdr);
+			*hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval ^ bp); ptr++;
+			n    -= slot;
+			while(slot--) { *hp++ = *ptr++; }
+			ptr++; n--;
+			while(n--) { *hp++ = *ptr++; }
+			res = make_hashmap(nhp);
+		    }
+		} else if (primary_tag(res) == TAG_PRIMARY_LIST && n == 1) {
+		    break;
+		} else {
+		    /* res is bitmap or leaf && n > 1, keep */
+		    n    -= slot;
+		    *hp++ = *ptr++;
+		    while(slot--) { *hp++ = *ptr++; }
+		    *hp++ = res;
+		    ptr++; n--;
+		    while(n--) { *hp++ = *ptr++; }
+		    res = make_hashmap(nhp);
+		}
+		break;
+	    case HAMT_SUBTAG_HEAD_BITMAP:
+		slot = (Uint)   ESTACK_POP(stack);
+		bp   = (Uint32) ESTACK_POP(stack);
+		n    = (Uint32) ESTACK_POP(stack);
+		nhp  = hp;
+
+		if (res != THE_NON_VALUE) {
+		    *hp++ = *ptr++;
+		    *hp++ = (*ptr++) - 1;
+		    n    -= slot;
+		    while(slot--) { *hp++ = *ptr++; }
+		    *hp++ = res;
+		    ptr++; n--;
+		    while(n--) { *hp++ = *ptr++; }
+		} else {
+		    hval  = MAP_HEADER_VAL(hdr);
+		    *hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(hval ^ bp); ptr++;
+		    *hp++ = (*ptr++) - 1;
+		    n    -= slot;
+		    while(slot--) { *hp++ = *ptr++; }
+		    ptr++; n--;
+		    while(n--) { *hp++ = *ptr++; }
+		}
+		res = make_hashmap(nhp);
+		break;
+	    default:
+		erl_exit(1, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK);
+		break;
+	}
+    } while(!ESTACK_ISEMPTY(stack));
+    HRelease(p, hp_end, hp);
+not_found:
+    DESTROY_ESTACK(stack);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+    ERTS_HOLE_CHECK(p);
+    return res;
+}
+
+
 int erts_validate_and_sort_map(map_t* mp)
 {
     Eterm *ks  = map_get_keys(mp);
-- 
cgit v1.2.3


From bb05557ff27e16e1e079e8f99b68713c7ca012c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 24 Feb 2015 16:44:51 +0100
Subject: erts: Refactor maps:remove/2

---
 erts/emulator/beam/erl_map.c | 182 +++++++++++++++++++++++++++----------------
 1 file changed, 113 insertions(+), 69 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 672ac9f513..69dd91d079 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -499,89 +499,93 @@ BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
 /* maps:remove/3 */
 
 int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
-    Sint n;
-    Uint need;
-    Eterm *hp_start;
-    Eterm *thp, *mhp;
-    Eterm *ks, *vs, tup;
-    map_t *mp = (map_t*)map_val(map);
-
-    n = map_get_size(mp);
+    Uint32 hx;
+    if (is_map(map)) {
+	Sint n;
+	Uint need;
+	Eterm *hp_start;
+	Eterm *thp, *mhp;
+	Eterm *ks, *vs, tup;
+	map_t *mp = (map_t*)map_val(map);
 
-    if (n == 0) {
-	*res = map;
-	return 1;
-    }
+	n = map_get_size(mp);
 
-    ks = map_get_keys(mp);
-    vs = map_get_values(mp);
-
-    /* Assume key exists.
-     * Release allocated if it didn't.
-     * Allocate key tuple first.
-     */
-
-    need   = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */
-    hp_start = HAlloc(p, need);
-    thp    = hp_start;
-    mhp    = thp + n;               /* offset with tuple heap size */
-
-    tup    = make_tuple(thp);
-    *thp++ = make_arityval(n - 1);
-
-    *res   = make_map(mhp);
-    *mhp++ = MAP_HEADER;
-    *mhp++ = n - 1;
-    *mhp++ = tup;
-
-    if (is_immed(key)) {
-	while (1) {
-	    if (*ks == key) {
-		goto found_key;
-	    } else if (--n) {
-		*mhp++ = *vs++;
-		*thp++ = *ks++;
-	    } else
-		break;
+	if (n == 0) {
+	    *res = map;
+	    return 1;
 	}
-    } else {
-	while(1) {
-	    if (EQ(*ks, key)) {
-		goto found_key;
-	    } else if (--n) {
-		*mhp++ = *vs++;
-		*thp++ = *ks++;
-	    } else
-		break;
+
+	ks = map_get_keys(mp);
+	vs = map_get_values(mp);
+
+	/* Assume key exists.
+	 * Release allocated if it didn't.
+	 * Allocate key tuple first.
+	 */
+
+	need   = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */
+	hp_start = HAlloc(p, need);
+	thp    = hp_start;
+	mhp    = thp + n;               /* offset with tuple heap size */
+
+	tup    = make_tuple(thp);
+	*thp++ = make_arityval(n - 1);
+
+	*res   = make_map(mhp);
+	*mhp++ = MAP_HEADER;
+	*mhp++ = n - 1;
+	*mhp++ = tup;
+
+	if (is_immed(key)) {
+	    while (1) {
+		if (*ks == key) {
+		    goto found_key;
+		} else if (--n) {
+		    *mhp++ = *vs++;
+		    *thp++ = *ks++;
+		} else
+		    break;
+	    }
+	} else {
+	    while(1) {
+		if (EQ(*ks, key)) {
+		    goto found_key;
+		} else if (--n) {
+		    *mhp++ = *vs++;
+		    *thp++ = *ks++;
+		} else
+		    break;
+	    }
 	}
-    }
 
-    /* Not found, remove allocated memory
-     * and return previous map.
-     */
-    HRelease(p, hp_start + need, hp_start);
+	/* Not found, remove allocated memory
+	 * and return previous map.
+	 */
+	HRelease(p, hp_start + need, hp_start);
 
-    *res = map;
-    return 1;
+	*res = map;
+	return 1;
 
 found_key:
-    /* Copy rest of keys and values */
-    if (--n) {
-	sys_memcpy(mhp, vs+1, n*sizeof(Eterm));
-	sys_memcpy(thp, ks+1, n*sizeof(Eterm));
+	/* Copy rest of keys and values */
+	if (--n) {
+	    sys_memcpy(mhp, vs+1, n*sizeof(Eterm));
+	    sys_memcpy(thp, ks+1, n*sizeof(Eterm));
+	}
+	return 1;
     }
+    ASSERT(is_hashmap(map));
+    hx = hashmap_make_hash(key);
+    *res = hashmap_delete(p, hx, key, map);
     return 1;
 }
 
 BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
-    if (is_map(BIF_ARG_2)) {
+    if (is_map(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
 	Eterm res;
 	if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) {
 	    BIF_RET(res);
 	}
-    } else if (is_hashmap(BIF_ARG_2)) {
-	Uint32 hx = hashmap_make_hash(BIF_ARG_1);
-	BIF_RET(hashmap_delete(BIF_P, hx, BIF_ARG_1, BIF_ARG_2));
     }
     BIF_ERROR(BIF_P, BADARG);
 }
@@ -1248,11 +1252,11 @@ static Eterm hashmap_values(Process* p, Eterm node) {
     return res;
 }
 
-static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node) {
+static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) {
     Eterm *hp = NULL, *nhp = NULL, *hp_end = NULL;
     Eterm th[2];
     Eterm *ptr;
-    Eterm hdr,res = node;
+    Eterm hdr, res = map, node = map;
     Uint32 ix, bp, hval;
     Uint slot, lvl = 0;
     Uint size = 0, n = 0;
@@ -1338,7 +1342,47 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node) {
 
 unroll:
     /* the size is bounded and atleast one less than the previous size */
-    size  -= 1;
+    size -= 1;
+    n     = hashmap_size(map) - 1;
+
+    if (n <= MAP_SMALL_MAP_LIMIT) {
+	DECLARE_WSTACK(wstack);
+	Eterm *kv, *ks, *vs;
+	map_t *mp;
+	Eterm keys;
+
+	DESTROY_ESTACK(stack);
+
+	/* build flat structure */
+	hp    = HAlloc(p, 3 + 1 + (2 * n));
+	keys  = make_tuple(hp);
+	*hp++ = make_arityval(n);
+	ks    = hp;
+	hp   += n;
+	mp    = (map_t*)hp;
+	hp   += MAP_HEADER_SIZE;
+	vs    = hp;
+
+	mp->thing_word = MAP_HEADER;
+	mp->size = n;
+	mp->keys = keys;
+
+	hashmap_iterator_init(&wstack, map);
+
+	while ((kv=hashmap_iterator_next(&wstack)) != NULL) {
+	    if (EQ(CAR(kv),key))
+		continue;
+	    *ks++ = CAR(kv);
+	    *vs++ = CDR(kv);
+	}
+
+	/* it cannot have multiple keys */
+	erts_validate_and_sort_map(mp);
+
+	DESTROY_WSTACK(wstack);
+	return make_map(mp);
+    }
+
     hp     = HAlloc(p, size);
     hp_end = hp + size;
     res    = THE_NON_VALUE;
-- 
cgit v1.2.3


From 73885d5d098a58aa6a4e226d075ee90b24045c66 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Mon, 23 Feb 2015 16:03:03 +0100
Subject: erts: Refactor maps:from_list/1

---
 erts/emulator/beam/erl_map.c | 163 ++++++++++++++++++++++---------------------
 1 file changed, 85 insertions(+), 78 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 69dd91d079..5e5e567642 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -69,6 +69,7 @@ static Eterm hashmap_to_list(Process *p, Eterm map);
 static Eterm hashmap_keys(Process *p, Eterm map);
 static Eterm hashmap_values(Process *p, Eterm map);
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
+static Eterm map_from_validated_list(Process *p, Eterm list, Uint size);
 
 /* erlang:map_size/1
  * the corresponding instruction is implemented in:
@@ -227,13 +228,8 @@ BIF_RETTYPE maps_get_2(BIF_ALIST_2) {
  */
 
 BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) {
-    Eterm *kv, item = BIF_ARG_1;
-    Eterm *hp, *thp,*vs, *ks, keys, res;
-    map_t *mp;
-    Uint  size = 0, unused_size = 0;
-    Sint  c = 0;
-    Sint  idx = 0;
-
+    Eterm item = BIF_ARG_1, res, *kv;
+    Uint  size = 0;
     if (is_list(item) || is_nil(item)) {
 
 	/* Calculate size and check validity */
@@ -254,95 +250,106 @@ BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) {
 	if (is_not_nil(item))
 	    goto error;
 
-	hp    = HAlloc(BIF_P, 3 + 1 + (2 * size));
-	thp   = hp;
-	keys  = make_tuple(hp);
-	*hp++ = make_arityval(size);
-	ks    = hp;
-	hp   += size;
-	mp    = (map_t*)hp;
-	res   = make_map(mp);
-	hp   += MAP_HEADER_SIZE;
-	vs    = hp;
+	BIF_RET(map_from_validated_list(BIF_P, BIF_ARG_1, size));
+    }
 
-	mp->thing_word = MAP_HEADER;
-	mp->size = size; /* set later, might shrink*/
-	mp->keys = keys;
+error:
 
-	if (size == 0)
-	    BIF_RET(res);
+    BIF_ERROR(BIF_P, BADARG);
+}
+
+static Eterm map_from_validated_list(Process *p, Eterm list, Uint size) {
+    Eterm *kv, item = list;
+    Eterm *hp, *thp,*vs, *ks, keys, res;
+    map_t *mp;
+    Uint  unused_size = 0;
+    Sint  c = 0;
+    Sint  idx = 0;
 
-	item  = BIF_ARG_1;
 
-	/* first entry */
-	kv    = tuple_val(CAR(list_val(item)));
-	ks[0] = kv[1];
-	vs[0] = kv[2];
-	size  = 1;
-	item  = CDR(list_val(item));
+    hp    = HAlloc(p, 3 + 1 + (2 * size));
+    thp   = hp;
+    keys  = make_tuple(hp);
+    *hp++ = make_arityval(size);
+    ks    = hp;
+    hp   += size;
+    mp    = (map_t*)hp;
+    res   = make_map(mp);
+    hp   += MAP_HEADER_SIZE;
+    vs    = hp;
 
-	/* insert sort key/value pairs */
-	while(is_list(item)) {
+    mp->thing_word = MAP_HEADER;
+    mp->size = size; /* set later, might shrink*/
+    mp->keys = keys;
 
-	    kv = tuple_val(CAR(list_val(item)));
+    if (size == 0)
+	return res;
 
-	    /* compare ks backwards
-	     * idx represent word index to be written (hole position).
-	     * We cannot copy the elements when searching since we might
-	     * have an equal key. So we search for just the index first =(
-	     *
-	     * It is perhaps faster to move the values in the first pass.
-	     * Check for uniqueness during insert phase and then have a
-	     * second phace compacting the map if duplicates are found
-	     * during insert. .. or do someother sort .. shell-sort perhaps.
-	     */
+    /* first entry */
+    kv    = tuple_val(CAR(list_val(item)));
+    ks[0] = kv[1];
+    vs[0] = kv[2];
+    size  = 1;
+    item  = CDR(list_val(item));
+
+    /* insert sort key/value pairs */
+    while(is_list(item)) {
+
+	kv = tuple_val(CAR(list_val(item)));
+
+	/* compare ks backwards
+	 * idx represent word index to be written (hole position).
+	 * We cannot copy the elements when searching since we might
+	 * have an equal key. So we search for just the index first =(
+	 *
+	 * It is perhaps faster to move the values in the first pass.
+	 * Check for uniqueness during insert phase and then have a
+	 * second phace compacting the map if duplicates are found
+	 * during insert. .. or do someother sort .. shell-sort perhaps.
+	 */
 
-	    idx = size;
+	idx = size;
 
-	    while(idx > 0 && (c = CMP_TERM(kv[1],ks[idx-1])) < 0) { idx--; }
+	while(idx > 0 && (c = CMP_TERM(kv[1],ks[idx-1])) < 0) { idx--; }
 
-	    if (c == 0) {
-		/* last compare was equal,
-		 * i.e. we have to release memory
-		 * and overwrite that key/value
-		 */
-		ks[idx-1] = kv[1];
-		vs[idx-1] = kv[2];
-		unused_size++;
-	    } else {
-		Uint i = size;
-		while(i > idx) {
-		    ks[i] = ks[i-1];
-		    vs[i] = vs[i-1];
-		    i--;
-		}
-		ks[idx] = kv[1];
-		vs[idx] = kv[2];
-		size++;
+	if (c == 0) {
+	    /* last compare was equal,
+	     * i.e. we have to release memory
+	     * and overwrite that key/value
+	     */
+	    ks[idx-1] = kv[1];
+	    vs[idx-1] = kv[2];
+	    unused_size++;
+	} else {
+	    Uint i = size;
+	    while(i > idx) {
+		ks[i] = ks[i-1];
+		vs[i] = vs[i-1];
+		i--;
 	    }
-	    item = CDR(list_val(item));
+	    ks[idx] = kv[1];
+	    vs[idx] = kv[2];
+	    size++;
 	}
+	item = CDR(list_val(item));
+    }
 
-	if (unused_size) {
-	    /* the key tuple is embedded in the heap
-	     * write a bignum to clear it.
-	     */
-	    /* release values as normal since they are on the top of the heap */
-
-	    ks[size] = make_pos_bignum_header(unused_size - 1);
-	    HRelease(BIF_P, vs + size + unused_size, vs + size);
-	}
+    if (unused_size) {
+	/* the key tuple is embedded in the heap
+	 * write a bignum to clear it.
+	 */
+	/* release values as normal since they are on the top of the heap */
 
-	*thp = make_arityval(size);
-	mp->size = size;
-	BIF_RET(res);
+	ks[size] = make_pos_bignum_header(unused_size - 1);
+	HRelease(p, vs + size + unused_size, vs + size);
     }
 
-error:
-
-    BIF_ERROR(BIF_P, BADARG);
+    *thp = make_arityval(size);
+    mp->size = size;
+    return res;
 }
 
+
 /* maps:is_key/2
  */
 
-- 
cgit v1.2.3


From 9f0fccbd5e45cd11aecfd782c0dbc121c3895af6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Mon, 23 Feb 2015 16:27:53 +0100
Subject: erts: Move hashmap:from_list/1 to maps

---
 erts/emulator/beam/bif.tab       |   1 -
 erts/emulator/beam/erl_hashmap.c | 472 ---------------------------------------
 erts/emulator/beam/erl_hashmap.h |  12 -
 erts/emulator/beam/erl_map.c     | 442 +++++++++++++++++++++++++++++++++++-
 erts/emulator/beam/erl_map.h     |  13 ++
 5 files changed, 454 insertions(+), 486 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 320cd39da8..7e92e58cab 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -616,7 +616,6 @@ bif erlang:get_keys/0
 
 # Hash Array Mappped Trie
 bif hashmap:info/1
-bif hashmap:from_list/1
 bif hashmap:new/0
 bif hashmap:merge/2
 
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 9b16a5a88f..06012950e1 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -61,22 +61,8 @@ static char *format_binary(Uint64 x, char *b) {
 #endif
 
 
-/* for hashmap_from_list/1 */
-typedef struct {
-    Uint32 hx;
-    Uint32 skip;
-    Uint i;
-    Eterm  val;
-} hxnode_t;
-
-static Eterm hashmap_from_list(Process *p, Eterm node);
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
-static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n);
-static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
-static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
-static int hxnodecmp(hxnode_t* a, hxnode_t* b);
-static int hxnodecmpkey(hxnode_t* a, hxnode_t* b);
 
 /* hashmap:new/0 */
 
@@ -101,13 +87,6 @@ BIF_RETTYPE hashmap_new_0(BIF_ALIST_0) {
 
 /* hashmap:from_list/1 */
 
-BIF_RETTYPE hashmap_from_list_1(BIF_ALIST_1) {
-    if (is_list(BIF_ARG_1) || is_nil(BIF_ARG_1)) {
-	return hashmap_from_list(BIF_P, BIF_ARG_1);
-    }
-
-    BIF_ERROR(BIF_P, BADARG);
-}
 /* hashmap:get/2 */
 
 /* hashmap:find/2 */
@@ -134,434 +113,10 @@ BIF_RETTYPE hashmap_merge_2(BIF_ALIST_2) {
 /* impl. */
 
 
-#define swizzle32(D,S) \
-    do { \
-	(D) = ((S) & 0x0000000f) << 28 | ((S) & 0x000000f0) << 20  \
-	    | ((S) & 0x00000f00) << 12 | ((S) & 0x0000f000) << 4   \
-	    | ((S) & 0x000f0000) >> 4  | ((S) & 0x00f00000) >> 12  \
-	    | ((S) & 0x0f000000) >> 20 | ((S) & 0xf0000000) >> 28; \
-    } while(0)
-
-
-static Eterm hashmap_from_list(Process *p, Eterm list) {
-    Eterm *kv, res, item = list;
-    Eterm *hp;
-    Eterm tmp[2];
-    Uint32 sw, hx;
-    Uint ix = 0, n = 0;
-    hxnode_t *hxns;
-
-    /* Calculate size and check validity */
-
-    if (is_nil(list)) {
-	hp    = HAlloc(p, HAMT_HEAD_EMPTY_SZ);
-	hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(0);
-	hp[1] = 0;
-	return make_hashmap(hp);
-    }
-
-    while(is_list(item)) {
-	res = CAR(list_val(item));
-	if (is_not_tuple(res))
-	    goto error;
-
-	kv = tuple_val(res);
-	if (*kv != make_arityval(2))
-	    goto error;
-
-	n++;
-	item = CDR(list_val(item));
-    }
-
-    if (is_not_nil(item))
-	goto error;
-
-    hp = HAlloc(p, (2 * n));
-
-    /* create tmp hx values and leaf ptrs */
-    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t));
-    item = list;
-
-    while(is_list(item)) {
-	res = CAR(list_val(item));
-	kv  = tuple_val(res);
-	hx  = hashmap_restore_hash(tmp,0,kv[1]);
-	swizzle32(sw,hx);
-	hxns[ix].hx   = sw;
-	hxns[ix].val  = CONS(hp, kv[1], kv[2]); hp += 2;
-	hxns[ix].skip = 1; /* will be reassigned in from_array */
-	hxns[ix].i    = ix;
-	ix++;
-	item = CDR(list_val(item));
-    }
-
-    ASSERT(n > 0);
-
-    res = hashmap_from_unsorted_array(p, hxns, n);
-
-    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
-    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
-
-    BIF_RET(res);
-error:
-    BIF_ERROR(p, BADARG);
-}
-
-Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n) {
-    Eterm tmp[2];
-    Uint32 sw, hx;
-    Uint ix;
-    hxnode_t *hxns;
-    Eterm res;
-
-    /* create tmp hx values and leaf ptrs */
-    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t));
-
-    for (ix = 0; ix < n; ix++) {
-	hx  = hashmap_restore_hash(tmp,0,CAR(list_val(leafs[ix])));
-	swizzle32(sw,hx);
-	hxns[ix].hx   = sw;
-	hxns[ix].val  = leafs[ix];
-	hxns[ix].skip = 1;
-	hxns[ix].i    = ix;
-    }
-
-    res = hashmap_from_unsorted_array(p, hxns, n);
-
-    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
-    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
-
-    return res;
-}
-
-static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n) {
-    Uint jx = 0, ix = 0, lx, cx;
-    Eterm res;
-
-    /* sort and compact array (remove non-unique entries) */
-    qsort(hxns, n, sizeof(hxnode_t), (int (*)(const void *, const void *)) hxnodecmp);
-
-    ix = 0, cx = 0;
-    while(ix < n - 1) {
-	if (hxns[ix].hx == hxns[ix+1].hx) {
-
-	    /* find region of equal hash values */
-	    jx = ix + 1;
-	    while(jx < n && hxns[ix].hx == hxns[jx].hx) { jx++; }
-	    /* find all correct keys from region
-	     * (last in list but now hash sorted so we check highest id instead) */
-
-	    /* resort with keys instead of hash value within region */
-	    
-	    qsort(&hxns[ix], jx - ix, sizeof(hxnode_t),
-		    (int (*)(const void *, const void *)) hxnodecmpkey);
-
-	    while(ix < jx) {
-		lx = ix;
-		while(ix < jx && EQ(CAR(list_val(hxns[ix].val)), CAR(list_val(hxns[lx].val)))) {
-		    if (hxns[ix].i > hxns[lx].i) {
-			lx = ix;
-		    }
-		    ix++;
-		}
-		hxns[cx].hx  = hxns[lx].hx;
-		hxns[cx].val = hxns[lx].val;
-		cx++;
-	    }
-	    ix = jx;
-	    continue;
-	}
-	if (ix > cx) {
-	    hxns[cx].hx  = hxns[ix].hx;
-	    hxns[cx].val = hxns[ix].val;
-	}
-	cx++;
-	ix++;
-    }
-
-    if (ix < n) {
-	hxns[cx].hx  = hxns[ix].hx;
-	hxns[cx].val = hxns[ix].val;
-	cx++;
-    }
-
-    if (cx > 1) {
-	/* recursive decompose array */
-	res = hashmap_from_sorted_unique_array(p, hxns, cx, 0);
-    } else {
-	Eterm *hp;
-	 /* hash value has been swizzled, need to drag it down to get the
-	 * correct slot. */
-	hp    = HAlloc(p, HAMT_HEAD_BITMAP_SZ(1));
-	hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(1 << ((hxns[0].hx >> 0x1c) & 0xf));
-	hp[1] = 1;
-	hp[2] = hxns[0].val;
-	res   = make_hashmap(hp);
-    }
-
-    return res;
-}
-
-static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n, int lvl) {
-    Eterm res = NIL;
-    Uint i,ix,jx,elems;
-    Uint32 sw, hx;
-    Eterm val;
-    Eterm th[2];
-    hxnode_t *tmp;
-
-    ASSERT(lvl < 32);
-    ix = 0;
-    elems = 1;
-    while (ix < n - 1) {
-	if (hxns[ix].hx == hxns[ix+1].hx) {
-	    jx = ix + 1;
-	    while (jx < n && hxns[ix].hx == hxns[jx].hx) { jx++; }
-	    tmp = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, ((jx - ix)) * sizeof(hxnode_t));
-
-	    for(i = 0; i < jx - ix; i++) {
-		val = hxns[i + ix].val;
-		hx  = hashmap_restore_hash(th, lvl + 8, CAR(list_val(val)));
-		swizzle32(sw,hx);
-		tmp[i].hx   = sw;
-		tmp[i].val  = val;
-		tmp[i].i    = i;
-		tmp[i].skip = 1;
-	    }
-
-	    qsort(tmp, jx - ix, sizeof(hxnode_t), (int (*)(const void *, const void *)) hxnodecmp);
-
-	    hxns[ix].skip = jx - ix;
-	    hxns[ix].val  = hashmap_from_sorted_unique_array(p, tmp, jx - ix, lvl + 8);
-	    erts_free(ERTS_ALC_T_TMP, (void *) tmp);
-	    ix = jx;
-	    if (ix < n) { elems++; }
-	    continue;
-	}
-	hxns[ix].skip = 1;
-	elems++;
-	ix++;
-    }
-
-    res = hashmap_from_chunked_array(p, hxns, elems, !lvl);
-
-    ERTS_HOLE_CHECK(p);
-
-    return res;
-}
-
-#define maskval(V,L)      (((V) >> ((7 - (L))*4)) & 0xf)
-#define cdepth(V1,V2)     (hashmap_clz((V1) ^ (V2)) >> 2)
-
 /* n must be > 1
  * hash values in hxns has to be unique
  */
 
-#define HALLOC_EXTRA 200
-static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int is_root) {
-    Uint ix, d, dn, dc, slot, elems;
-    Uint32 v, vp, vn, hdr;
-    Uint bp, sz;
-    DECLARE_ESTACK(stack);
-    Eterm res = NIL, *hp = NULL, *nhp;
-
-    ASSERT(n > 1);
-
-    /* push initial nodes on the stack,
-     * this is the starting depth */
-
-    ix = 0;
-    d  = 0;
-    vp = hxns[ix].hx;
-    v  = hxns[ix + hxns[ix].skip].hx;
-
-    ASSERT(vp > v);
-    slot = maskval(vp,d);
-
-    while(slot == maskval(v,d)) {
-	ESTACK_PUSH(stack, 1 << slot);
-	d++;
-	slot = maskval(vp,d);
-    }
-
-    res = hxns[ix].val;
-
-    if (hxns[ix].skip > 1) {
-	dc = 7;
-	/* build collision nodes */
-	while (dc > d) {
-	    hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
-	    hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(vp,dc));
-	    hp[1] = res;
-	    res   = make_hashmap(hp);
-	    dc--;
-	}
-    }
-
-    ESTACK_PUSH(stack, res);
-    ESTACK_PUSH(stack, 1 << slot);
-
-    /* all of the other nodes .. */
-    elems = n - 2; /* remove first and last elements */
-    while(elems--) {
-	hdr = ESTACK_POP(stack);
-	ix  = ix + hxns[ix].skip;
-
-	/* determine if node or subtree should be built by looking
-	 * at the next value. */
-
-	vn = hxns[ix + hxns[ix].skip].hx;
-	dn = cdepth(v,vn);
-	ASSERT(v > vn);
-
-	res = hxns[ix].val;
-
-	if (hxns[ix].skip > 1) {
-	    int wat = (d > dn) ? d : dn;
-	    dc = 7;
-	    /* build collision nodes */
-	    while (dc > wat) {
-		hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
-		hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(v,dc));
-		hp[1] = res;
-		res   = make_hashmap(hp);
-		dc--;
-	    }
-	}
-
-	/* next depth is higher (implies collision) */
-	if (d < dn) {
-	    /* hdr is the popped one initially */
-	    while(d < dn) {
-		slot = maskval(v, d);
-		bp   = 1 << slot;
-		ESTACK_PUSH(stack, hdr | bp);
-		d++;
-		hdr = 0; /* clear hdr for all other collisions */
-	    }
-
-	    slot = maskval(v, d);
-	    bp   = 1 << slot;
-	    /* no more collisions */
-	    ESTACK_PUSH(stack,res);
-	    ESTACK_PUSH(stack,bp);
-	} else if (d == dn) {
-	    /* no collisions at all */
-	    slot = maskval(v, d);
-	    bp   = 1 << slot;
-	    ESTACK_PUSH(stack,res);
-	    ESTACK_PUSH(stack,hdr | bp);
-	} else {
-	    /* dn < n, we have a drop and we are done
-	     * build nodes and subtree */
-	    while (dn != d) {
-		slot  = maskval(v, d);
-		bp    = 1 << slot;
-		/* OR bitposition before sz calculation to handle
-		 * redundant collisions */
-		hdr  |= bp;
-		sz    = hashmap_bitcount(hdr);
-		hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA);
-		nhp   = hp;
-		*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr);
-		*hp++ = res; sz--;
-		while (sz--) { *hp++ = ESTACK_POP(stack); }
-		ASSERT((hp - nhp) < 18);
-		res = make_hashmap(nhp);
-
-		/* we need to pop the next hdr and push if we don't need it */
-
-		hdr = ESTACK_POP(stack);
-		d--;
-	    }
-	    ESTACK_PUSH(stack, res);
-	    ESTACK_PUSH(stack, hdr);
-	}
-
-	vp = v;
-	v  = vn;
-	d  = dn;
-	ERTS_HOLE_CHECK(p);
-    }
-
-    /* v and vp are reused from above */
-    dn  = cdepth(vp,v);
-    ix  = ix + hxns[ix].skip;
-    res = hxns[ix].val;
-
-    if (hxns[ix].skip > 1) {
-	dc = 7;
-	/* build collision nodes */
-	while (dc > dn) {
-	    hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
-	    hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(v,dc));
-	    hp[1] = res;
-	    res   = make_hashmap(hp);
-	    dc--;
-	}
-    }
-
-    hdr = ESTACK_POP(stack);
-    /* pop remaining subtree if any */
-    while (dn) {
-	slot  = maskval(v, dn);
-	bp    = 1 << slot;
-	/* OR bitposition before sz calculation to handle
-	 * redundant collisions */
-	hdr  |= bp;
-	sz    = hashmap_bitcount(hdr);
-	hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA);
-	nhp   = hp;
-	*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr);
-	*hp++ = res; sz--;
-
-	while (sz--) { *hp++ = ESTACK_POP(stack); }
-	res = make_hashmap(nhp);
-	hdr = ESTACK_POP(stack);
-	dn--;
-    }
-
-    /* and finally the root .. */
-
-    slot  = maskval(v, dn);
-    bp    = 1 << slot;
-    hdr  |= bp;
-    sz    = hashmap_bitcount(hdr);
-    hp    = HAlloc(p, sz + /* hdr + item */ (is_root ? 2 : 1));
-    nhp   = hp;
-
-    if (is_root) {
-	*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_HEAD_ARRAY : MAP_HEADER_HAMT_HEAD_BITMAP(hdr);
-	*hp++ = n;
-    } else {
-	*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr);
-    }
-
-    *hp++ = res; sz--;
-    while (sz--) { *hp++ = ESTACK_POP(stack); }
-
-    res = make_hashmap(nhp);
-
-    ASSERT(ESTACK_COUNT(stack) == 0);
-    DESTROY_ESTACK(stack);
-    ERTS_HOLE_CHECK(p);
-    return res;
-}
-#undef HALLOC_EXTRA
-
-static int hxnodecmpkey(hxnode_t *a, hxnode_t *b) {
-    return CMP_TERM(CAR(list_val(a->val)), CAR(list_val(b->val)));
-}
-
-static int hxnodecmp(hxnode_t *a, hxnode_t *b) {
-    if (a->hx < b->hx)
-	return 1;
-    else if (a->hx == b->hx)
-	return 0;
-    else
-	return -1;
-}
-
 #define HALLOC_EXTRA 200
 
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
@@ -938,30 +493,3 @@ BIF_RETTYPE hashmap_info_1(BIF_ALIST_1) {
     }
     BIF_ERROR(BIF_P, BADARG);
 }
-
-/* implementation of builtin emulations */
-
-#if !defined(__GNUC__)
-/* Count leading zeros emulation */
-Uint32 hashmap_clz(Uint32 x) {
-    Uint32 y;
-    int n = 32;
-    y = x >>16;  if (y != 0) {n = n -16;  x = y;}
-    y = x >> 8;  if (y != 0) {n = n - 8;  x = y;}
-    y = x >> 4;  if (y != 0) {n = n - 4;  x = y;}
-    y = x >> 2;  if (y != 0) {n = n - 2;  x = y;}
-    y = x >> 1;  if (y != 0) return n - 2;
-    return n - x;
-}
-const Uint32 SK5 = 0x55555555, SK3 = 0x33333333;
-const Uint32 SKF0 = 0xF0F0F0F, SKFF = 0xFF00FF;
-
-/* CTPOP emulation */
-Uint32 hashmap_bitcount(Uint32 x) {
-    x -= ((x >> 1  ) & SK5);
-    x  =  (x & SK3 ) + ((x >> 2 ) & SK3 );
-    x  =  (x & SKF0) + ((x >> 4 ) & SKF0);
-    x +=   x >> 8;
-    return (x + (x >> 16)) & 0x3F;
-}
-#endif
diff --git a/erts/emulator/beam/erl_hashmap.h b/erts/emulator/beam/erl_hashmap.h
index 5a9aa05f61..7ac33b34b0 100644
--- a/erts/emulator/beam/erl_hashmap.h
+++ b/erts/emulator/beam/erl_hashmap.h
@@ -24,20 +24,10 @@
 #include "sys.h"
 #include "erl_term.h"
 
-Eterm erts_hashmap_get(Eterm key, Eterm map);
 int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
-Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n);
 
 /* HASH */
 
-#if defined(__GNUC__)
-#define hashmap_clz(x)       ((Uint32) __builtin_clz((unsigned int)(x)))
-#define hashmap_bitcount(x)  ((Uint32) __builtin_popcount((unsigned int) (x)))
-#else
-Uint32 hashmap_clz(Uint32 x);
-Uint32 hashmap_bitcount(Uint32 x);
-#endif
-
 /* hamt nodes v2.0
  * 
  * node :: leaf | array | bitmap
@@ -49,8 +39,6 @@ typedef struct hashmap_head_s {
     Eterm items[1];
 } hashmap_head_t;
 
-
-
 /* thing_word tagscheme
  * Need two bits for map subtags
  *
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 5e5e567642..7281353af5 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -63,6 +63,14 @@
  * - erts_internal:map_to_tuple_keys/1
  */
 
+/* for hashmap_from_list/1 */
+typedef struct {
+    Uint32 hx;
+    Uint32 skip;
+    Uint i;
+    Eterm  val;
+} hxnode_t;
+
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node, int is_update);
 static Eterm hashmap_to_list(Process *p, Eterm map);
@@ -70,6 +78,12 @@ static Eterm hashmap_keys(Process *p, Eterm map);
 static Eterm hashmap_values(Process *p, Eterm map);
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 static Eterm map_from_validated_list(Process *p, Eterm list, Uint size);
+static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size);
+static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n);
+static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
+static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
+static int hxnodecmp(hxnode_t* a, hxnode_t* b);
+static int hxnodecmpkey(hxnode_t* a, hxnode_t* b);
 
 /* erlang:map_size/1
  * the corresponding instruction is implemented in:
@@ -250,7 +264,11 @@ BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) {
 	if (is_not_nil(item))
 	    goto error;
 
-	BIF_RET(map_from_validated_list(BIF_P, BIF_ARG_1, size));
+	if (size > MAP_SMALL_MAP_LIMIT) {
+	    BIF_RET(hashmap_from_validated_list(BIF_P, BIF_ARG_1, size));
+	} else {
+	    BIF_RET(map_from_validated_list(BIF_P, BIF_ARG_1, size));
+	}
     }
 
 error:
@@ -349,6 +367,401 @@ static Eterm map_from_validated_list(Process *p, Eterm list, Uint size) {
     return res;
 }
 
+#define swizzle32(D,S) \
+    do { \
+	(D) = ((S) & 0x0000000f) << 28 | ((S) & 0x000000f0) << 20  \
+	    | ((S) & 0x00000f00) << 12 | ((S) & 0x0000f000) << 4   \
+	    | ((S) & 0x000f0000) >> 4  | ((S) & 0x00f00000) >> 12  \
+	    | ((S) & 0x0f000000) >> 20 | ((S) & 0xf0000000) >> 28; \
+    } while(0)
+
+#define maskval(V,L)      (((V) >> ((7 - (L))*4)) & 0xf)
+#define cdepth(V1,V2)     (hashmap_clz((V1) ^ (V2)) >> 2)
+
+static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
+    Eterm item = list;
+    Eterm *hp;
+    Eterm *kv, res;
+    Eterm tmp[2];
+    Uint32 sw, hx;
+    Uint ix = 0;
+    hxnode_t *hxns;
+
+    ASSERT(size > 0);
+
+    hp = HAlloc(p, (2 * size));
+
+    /* create tmp hx values and leaf ptrs */
+    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, size * sizeof(hxnode_t));
+
+    while(is_list(item)) {
+	res = CAR(list_val(item));
+	kv  = tuple_val(res);
+	hx  = hashmap_restore_hash(tmp,0,kv[1]);
+	swizzle32(sw,hx);
+	hxns[ix].hx   = sw;
+	hxns[ix].val  = CONS(hp, kv[1], kv[2]); hp += 2;
+	hxns[ix].skip = 1; /* will be reassigned in from_array */
+	hxns[ix].i    = ix;
+	ix++;
+	item = CDR(list_val(item));
+    }
+
+    res = hashmap_from_unsorted_array(p, hxns, size);
+
+    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+
+    return res;
+}
+
+Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n) {
+    Eterm tmp[2];
+    Uint32 sw, hx;
+    Uint ix;
+    hxnode_t *hxns;
+    Eterm res;
+
+    /* create tmp hx values and leaf ptrs */
+    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t));
+
+    for (ix = 0; ix < n; ix++) {
+	hx  = hashmap_restore_hash(tmp,0,CAR(list_val(leafs[ix])));
+	swizzle32(sw,hx);
+	hxns[ix].hx   = sw;
+	hxns[ix].val  = leafs[ix];
+	hxns[ix].skip = 1;
+	hxns[ix].i    = ix;
+    }
+
+    res = hashmap_from_unsorted_array(p, hxns, n);
+
+    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+
+    return res;
+}
+
+static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n) {
+    Uint jx = 0, ix = 0, lx, cx;
+    Eterm res;
+
+    /* sort and compact array (remove non-unique entries) */
+    qsort(hxns, n, sizeof(hxnode_t), (int (*)(const void *, const void *)) hxnodecmp);
+
+    ix = 0, cx = 0;
+    while(ix < n - 1) {
+	if (hxns[ix].hx == hxns[ix+1].hx) {
+
+	    /* find region of equal hash values */
+	    jx = ix + 1;
+	    while(jx < n && hxns[ix].hx == hxns[jx].hx) { jx++; }
+	    /* find all correct keys from region
+	     * (last in list but now hash sorted so we check highest id instead) */
+
+	    /* resort with keys instead of hash value within region */
+
+	    qsort(&hxns[ix], jx - ix, sizeof(hxnode_t),
+		    (int (*)(const void *, const void *)) hxnodecmpkey);
+
+	    while(ix < jx) {
+		lx = ix;
+		while(ix < jx && EQ(CAR(list_val(hxns[ix].val)), CAR(list_val(hxns[lx].val)))) {
+		    if (hxns[ix].i > hxns[lx].i) {
+			lx = ix;
+		    }
+		    ix++;
+		}
+		hxns[cx].hx  = hxns[lx].hx;
+		hxns[cx].val = hxns[lx].val;
+		cx++;
+	    }
+	    ix = jx;
+	    continue;
+	}
+	if (ix > cx) {
+	    hxns[cx].hx  = hxns[ix].hx;
+	    hxns[cx].val = hxns[ix].val;
+	}
+	cx++;
+	ix++;
+    }
+
+    if (ix < n) {
+	hxns[cx].hx  = hxns[ix].hx;
+	hxns[cx].val = hxns[ix].val;
+	cx++;
+    }
+
+    if (cx > 1) {
+	/* recursive decompose array */
+	res = hashmap_from_sorted_unique_array(p, hxns, cx, 0);
+    } else {
+	Eterm *hp;
+	 /* hash value has been swizzled, need to drag it down to get the
+	 * correct slot. */
+	hp    = HAlloc(p, HAMT_HEAD_BITMAP_SZ(1));
+	hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(1 << ((hxns[0].hx >> 0x1c) & 0xf));
+	hp[1] = 1;
+	hp[2] = hxns[0].val;
+	res   = make_hashmap(hp);
+    }
+
+    return res;
+}
+
+static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n, int lvl) {
+    Eterm res = NIL;
+    Uint i,ix,jx,elems;
+    Uint32 sw, hx;
+    Eterm val;
+    Eterm th[2];
+    hxnode_t *tmp;
+
+    ASSERT(lvl < 32);
+    ix = 0;
+    elems = 1;
+    while (ix < n - 1) {
+	if (hxns[ix].hx == hxns[ix+1].hx) {
+	    jx = ix + 1;
+	    while (jx < n && hxns[ix].hx == hxns[jx].hx) { jx++; }
+	    tmp = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, ((jx - ix)) * sizeof(hxnode_t));
+
+	    for(i = 0; i < jx - ix; i++) {
+		val = hxns[i + ix].val;
+		hx  = hashmap_restore_hash(th, lvl + 8, CAR(list_val(val)));
+		swizzle32(sw,hx);
+		tmp[i].hx   = sw;
+		tmp[i].val  = val;
+		tmp[i].i    = i;
+		tmp[i].skip = 1;
+	    }
+
+	    qsort(tmp, jx - ix, sizeof(hxnode_t), (int (*)(const void *, const void *)) hxnodecmp);
+
+	    hxns[ix].skip = jx - ix;
+	    hxns[ix].val  = hashmap_from_sorted_unique_array(p, tmp, jx - ix, lvl + 8);
+	    erts_free(ERTS_ALC_T_TMP, (void *) tmp);
+	    ix = jx;
+	    if (ix < n) { elems++; }
+	    continue;
+	}
+	hxns[ix].skip = 1;
+	elems++;
+	ix++;
+    }
+
+    res = hashmap_from_chunked_array(p, hxns, elems, !lvl);
+
+    ERTS_HOLE_CHECK(p);
+
+    return res;
+}
+
+#define HALLOC_EXTRA 200
+static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int is_root) {
+    Uint ix, d, dn, dc, slot, elems;
+    Uint32 v, vp, vn, hdr;
+    Uint bp, sz;
+    DECLARE_ESTACK(stack);
+    Eterm res = NIL, *hp = NULL, *nhp;
+
+    ASSERT(n > 1);
+
+    /* push initial nodes on the stack,
+     * this is the starting depth */
+
+    ix = 0;
+    d  = 0;
+    vp = hxns[ix].hx;
+    v  = hxns[ix + hxns[ix].skip].hx;
+
+    ASSERT(vp > v);
+    slot = maskval(vp,d);
+
+    while(slot == maskval(v,d)) {
+	ESTACK_PUSH(stack, 1 << slot);
+	d++;
+	slot = maskval(vp,d);
+    }
+
+    res = hxns[ix].val;
+
+    if (hxns[ix].skip > 1) {
+	dc = 7;
+	/* build collision nodes */
+	while (dc > d) {
+	    hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
+	    hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(vp,dc));
+	    hp[1] = res;
+	    res   = make_hashmap(hp);
+	    dc--;
+	}
+    }
+
+    ESTACK_PUSH(stack, res);
+    ESTACK_PUSH(stack, 1 << slot);
+
+    /* all of the other nodes .. */
+    elems = n - 2; /* remove first and last elements */
+    while(elems--) {
+	hdr = ESTACK_POP(stack);
+	ix  = ix + hxns[ix].skip;
+
+	/* determine if node or subtree should be built by looking
+	 * at the next value. */
+
+	vn = hxns[ix + hxns[ix].skip].hx;
+	dn = cdepth(v,vn);
+	ASSERT(v > vn);
+
+	res = hxns[ix].val;
+
+	if (hxns[ix].skip > 1) {
+	    int wat = (d > dn) ? d : dn;
+	    dc = 7;
+	    /* build collision nodes */
+	    while (dc > wat) {
+		hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
+		hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(v,dc));
+		hp[1] = res;
+		res   = make_hashmap(hp);
+		dc--;
+	    }
+	}
+
+	/* next depth is higher (implies collision) */
+	if (d < dn) {
+	    /* hdr is the popped one initially */
+	    while(d < dn) {
+		slot = maskval(v, d);
+		bp   = 1 << slot;
+		ESTACK_PUSH(stack, hdr | bp);
+		d++;
+		hdr = 0; /* clear hdr for all other collisions */
+	    }
+
+	    slot = maskval(v, d);
+	    bp   = 1 << slot;
+	    /* no more collisions */
+	    ESTACK_PUSH(stack,res);
+	    ESTACK_PUSH(stack,bp);
+	} else if (d == dn) {
+	    /* no collisions at all */
+	    slot = maskval(v, d);
+	    bp   = 1 << slot;
+	    ESTACK_PUSH(stack,res);
+	    ESTACK_PUSH(stack,hdr | bp);
+	} else {
+	    /* dn < n, we have a drop and we are done
+	     * build nodes and subtree */
+	    while (dn != d) {
+		slot  = maskval(v, d);
+		bp    = 1 << slot;
+		/* OR bitposition before sz calculation to handle
+		 * redundant collisions */
+		hdr  |= bp;
+		sz    = hashmap_bitcount(hdr);
+		hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA);
+		nhp   = hp;
+		*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr);
+		*hp++ = res; sz--;
+		while (sz--) { *hp++ = ESTACK_POP(stack); }
+		ASSERT((hp - nhp) < 18);
+		res = make_hashmap(nhp);
+
+		/* we need to pop the next hdr and push if we don't need it */
+
+		hdr = ESTACK_POP(stack);
+		d--;
+	    }
+	    ESTACK_PUSH(stack, res);
+	    ESTACK_PUSH(stack, hdr);
+	}
+
+	vp = v;
+	v  = vn;
+	d  = dn;
+	ERTS_HOLE_CHECK(p);
+    }
+
+    /* v and vp are reused from above */
+    dn  = cdepth(vp,v);
+    ix  = ix + hxns[ix].skip;
+    res = hxns[ix].val;
+
+    if (hxns[ix].skip > 1) {
+	dc = 7;
+	/* build collision nodes */
+	while (dc > dn) {
+	    hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
+	    hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(v,dc));
+	    hp[1] = res;
+	    res   = make_hashmap(hp);
+	    dc--;
+	}
+    }
+
+    hdr = ESTACK_POP(stack);
+    /* pop remaining subtree if any */
+    while (dn) {
+	slot  = maskval(v, dn);
+	bp    = 1 << slot;
+	/* OR bitposition before sz calculation to handle
+	 * redundant collisions */
+	hdr  |= bp;
+	sz    = hashmap_bitcount(hdr);
+	hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA);
+	nhp   = hp;
+	*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr);
+	*hp++ = res; sz--;
+
+	while (sz--) { *hp++ = ESTACK_POP(stack); }
+	res = make_hashmap(nhp);
+	hdr = ESTACK_POP(stack);
+	dn--;
+    }
+
+    /* and finally the root .. */
+
+    slot  = maskval(v, dn);
+    bp    = 1 << slot;
+    hdr  |= bp;
+    sz    = hashmap_bitcount(hdr);
+    hp    = HAlloc(p, sz + /* hdr + item */ (is_root ? 2 : 1));
+    nhp   = hp;
+
+    if (is_root) {
+	*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_HEAD_ARRAY : MAP_HEADER_HAMT_HEAD_BITMAP(hdr);
+	*hp++ = n;
+    } else {
+	*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr);
+    }
+
+    *hp++ = res; sz--;
+    while (sz--) { *hp++ = ESTACK_POP(stack); }
+
+    res = make_hashmap(nhp);
+
+    ASSERT(ESTACK_COUNT(stack) == 0);
+    DESTROY_ESTACK(stack);
+    ERTS_HOLE_CHECK(p);
+    return res;
+}
+#undef HALLOC_EXTRA
+
+static int hxnodecmpkey(hxnode_t *a, hxnode_t *b) {
+    return CMP_TERM(CAR(list_val(a->val)), CAR(list_val(b->val)));
+}
+
+static int hxnodecmp(hxnode_t *a, hxnode_t *b) {
+    if (a->hx < b->hx)
+	return 1;
+    else if (a->hx == b->hx)
+	return 0;
+    else
+	return -1;
+}
 
 /* maps:is_key/2
  */
@@ -1593,3 +2006,30 @@ BIF_RETTYPE erts_internal_map_to_tuple_keys_1(BIF_ALIST_1) {
     }
     BIF_ERROR(BIF_P, BADARG);
 }
+
+/* implementation of builtin emulations */
+
+#if !defined(__GNUC__)
+/* Count leading zeros emulation */
+Uint32 hashmap_clz(Uint32 x) {
+    Uint32 y;
+    int n = 32;
+    y = x >>16;  if (y != 0) {n = n -16;  x = y;}
+    y = x >> 8;  if (y != 0) {n = n - 8;  x = y;}
+    y = x >> 4;  if (y != 0) {n = n - 4;  x = y;}
+    y = x >> 2;  if (y != 0) {n = n - 2;  x = y;}
+    y = x >> 1;  if (y != 0) return n - 2;
+    return n - x;
+}
+const Uint32 SK5 = 0x55555555, SK3 = 0x33333333;
+const Uint32 SKF0 = 0xF0F0F0F, SKFF = 0xFF00FF;
+
+/* CTPOP emulation */
+Uint32 hashmap_bitcount(Uint32 x) {
+    x -= ((x >> 1  ) & SK5);
+    x  =  (x & SK3 ) + ((x >> 2 ) & SK3 );
+    x  =  (x & SKF0) + ((x >> 4 ) & SKF0);
+    x +=   x >> 8;
+    return (x + (x >> 16)) & 0x3F;
+}
+#endif
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 48bc316e3b..428cfe9b63 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -22,6 +22,16 @@
 #define __ERL_MAP_H__
 
 #include "sys.h"
+
+/* instrinsic wrappers */
+#if defined(__GNUC__)
+#define hashmap_clz(x)       ((Uint32) __builtin_clz((unsigned int)(x)))
+#define hashmap_bitcount(x)  ((Uint32) __builtin_popcount((unsigned int) (x)))
+#else
+Uint32 hashmap_clz(Uint32 x);
+Uint32 hashmap_bitcount(Uint32 x);
+#endif
+
 /* MAP */
 
 typedef struct map_s {
@@ -70,6 +80,7 @@ typedef struct map_s {
 #define map_get_keys(x)        (((Eterm *)tuple_val(((map_t *)(x))->keys)) + 1)
 #define map_get_size(x)        (((map_t*)(x))->size)
 
+#define MAP_SMALL_MAP_LIMIT    (32)
 #define MAP_HEADER             _make_header(1,_TAG_HEADER_MAP)
 #define MAP_HEADER_SIZE        (sizeof(map_t) / sizeof(Eterm))
 
@@ -80,6 +91,8 @@ int   erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res);
 int   erts_validate_and_sort_map(map_t* map);
 void  hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
+Eterm erts_hashmap_get(Eterm key, Eterm map);
+Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n);
 
 #if HALFWORD_HEAP
 const Eterm *
-- 
cgit v1.2.3


From a80156026638b6605b636c16fa59e8206ff7635e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Mon, 23 Feb 2015 16:32:59 +0100
Subject: erts: Remove hashmap:new/0

---
 erts/emulator/beam/bif.tab       |  1 -
 erts/emulator/beam/erl_hashmap.c | 13 -------------
 2 files changed, 14 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 7e92e58cab..e9c5e83203 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -616,7 +616,6 @@ bif erlang:get_keys/0
 
 # Hash Array Mappped Trie
 bif hashmap:info/1
-bif hashmap:new/0
 bif hashmap:merge/2
 
 #
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 06012950e1..29a14a9f20 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -66,19 +66,6 @@ static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 
 /* hashmap:new/0 */
 
-BIF_RETTYPE hashmap_new_0(BIF_ALIST_0) {
-    Eterm* hp;
-    hashmap_head_t *head;
-
-    hp   = HAlloc(BIF_P, HAMT_HEAD_EMPTY_SZ);
-    head = (hashmap_head_t *) hp;
-
-    head->thing_word = MAP_HEADER_HAMT_HEAD_BITMAP(0);
-    head->size       = 0;
-
-    BIF_RET(make_hashmap(head));
-}
-
 /* hashmap:put/3 */
 
 /* hashmap:update/3 */
-- 
cgit v1.2.3


From 746d2d846ebf20fc4dc303c53bcd1e908aee924b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Mon, 23 Feb 2015 16:45:23 +0100
Subject: erts: Move hashmap:info/1 to erts_debug:map_info/1

---
 erts/emulator/beam/bif.tab       |   2 +-
 erts/emulator/beam/erl_hashmap.c | 135 ---------------------------------------
 erts/emulator/beam/erl_map.c     | 133 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 134 insertions(+), 136 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index e9c5e83203..f9127ef955 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -613,9 +613,9 @@ bif erlang:fun_info_mfa/1
 #
 
 bif erlang:get_keys/0
+bif erts_debug:map_info/1
 
 # Hash Array Mappped Trie
-bif hashmap:info/1
 bif hashmap:merge/2
 
 #
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index 29a14a9f20..fecdc2ef31 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -45,10 +45,6 @@
 #include "erl_map.h"
 #include "erl_hashmap.h"
 
-#ifndef DECL_AM
-#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
-#endif
-
 #if 0
 static char *format_binary(Uint64 x, char *b) {
     int z;
@@ -62,7 +58,6 @@ static char *format_binary(Uint64 x, char *b) {
 
 
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
-static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 
 /* hashmap:new/0 */
 
@@ -100,10 +95,6 @@ BIF_RETTYPE hashmap_merge_2(BIF_ALIST_2) {
 /* impl. */
 
 
-/* n must be > 1
- * hash values in hxns has to be unique
- */
-
 #define HALLOC_EXTRA 200
 
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
@@ -354,129 +345,3 @@ int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp)
 }
 
 /* hashmap:info/0 */
-
-static Eterm hashmap_info(Process *p, Eterm node) {
-    Eterm *hp;
-    Eterm res = NIL, info = NIL;
-    Eterm *ptr, tup, hdr;
-    Uint sz;
-    DECL_AM(depth);
-    DECL_AM(leafs);
-    DECL_AM(bitmaps);
-    DECL_AM(arrays);
-    Uint nleaf=0, nbitmap=0, narray=0;
-    Uint bitmap_usage[16], leaf_usage[16];
-    Uint lvl = 0, clvl;
-    DECLARE_ESTACK(stack);
-
-    for (sz = 0; sz < 16; sz++) {
-	bitmap_usage[sz] = 0;
-	leaf_usage[sz] = 0;
-    }
-
-    ptr = boxed_val(node);
-    ESTACK_PUSH(stack, 0);
-    ESTACK_PUSH(stack, node);
-    do {
-	node = ESTACK_POP(stack);
-	clvl = ESTACK_POP(stack);
-	lvl  = MAX(lvl,clvl);
-	switch(primary_tag(node)) {
-	    case TAG_PRIMARY_LIST:
-		nleaf++;
-		leaf_usage[clvl] += 1;
-		break;
-	    case TAG_PRIMARY_BOXED:
-		ptr = boxed_val(node);
-		hdr = *ptr;
-		ASSERT(is_header(hdr));
-		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-		    case HAMT_SUBTAG_NODE_ARRAY:
-			narray++;
-			sz = 16;
-			while(sz--) {
-			    ESTACK_PUSH(stack, clvl + 1);
-			    ESTACK_PUSH(stack, ptr[sz+1]);
-			}
-			break;
-		    case HAMT_SUBTAG_NODE_BITMAP:
-			nbitmap++;
-			sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
-			ASSERT(sz < 17);
-			bitmap_usage[sz-1] += 1;
-			while(sz--) {
-			    ESTACK_PUSH(stack, clvl + 1);
-			    ESTACK_PUSH(stack, ptr[sz+1]);
-			}
-			break;
-		    case HAMT_SUBTAG_HEAD_BITMAP:
-			nbitmap++;
-			sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
-			bitmap_usage[sz-1] += 1;
-			while(sz--) {
-			    ESTACK_PUSH(stack, clvl + 1);
-			    ESTACK_PUSH(stack, ptr[sz+2]);
-			}
-			break;
-		    case HAMT_SUBTAG_HEAD_ARRAY:
-			narray++;
-			sz = 16;
-			while(sz--) {
-			    ESTACK_PUSH(stack, clvl + 1);
-			    ESTACK_PUSH(stack, ptr[sz+2]);
-			}
-			break;
-		    default:
-			erl_exit(1, "bad header\r\n");
-			break;
-		}
-	}
-    } while(!ESTACK_ISEMPTY(stack));
-
-
-    /* size */
-    sz = 0;
-    hashmap_bld_tuple_uint(NULL,&sz,16,leaf_usage);
-    hashmap_bld_tuple_uint(NULL,&sz,16,bitmap_usage);
-
-    /* alloc */
-    hp   = HAlloc(p, 2+3 + 3*(2+4) + sz);
-
-    info = hashmap_bld_tuple_uint(&hp,NULL,16,leaf_usage);
-    tup  = TUPLE3(hp, AM_leafs, make_small(nleaf),info); hp += 4;
-    res  = CONS(hp, tup, res); hp += 2;
-
-    info = hashmap_bld_tuple_uint(&hp,NULL,16,bitmap_usage);
-    tup  = TUPLE3(hp, AM_bitmaps, make_small(nbitmap), info); hp += 4;
-    res  = CONS(hp, tup, res); hp += 2;
-
-    tup  = TUPLE3(hp, AM_arrays, make_small(narray),NIL); hp += 4;
-    res  = CONS(hp, tup, res); hp += 2;
-
-    tup  = TUPLE2(hp, AM_depth, make_small(lvl)); hp += 3;
-    res  = CONS(hp, tup, res); hp += 2;
-
-    DESTROY_ESTACK(stack);
-    ERTS_HOLE_CHECK(p);
-    return res;
-}
-
-static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]) {
-    Eterm res = THE_NON_VALUE;
-    Eterm *ts = (Eterm *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(Eterm));
-    Uint i;
-
-    for (i = 0; i < n; i++) {
-	ts[i] = erts_bld_uint(hpp, szp, nums[i]);
-    }
-    res = erts_bld_tuplev(hpp, szp, n, ts);
-    erts_free(ERTS_ALC_T_TMP, (void *) ts);
-    return res;
-}
-
-BIF_RETTYPE hashmap_info_1(BIF_ALIST_1) {
-    if (is_hashmap(BIF_ARG_1)) {
-	BIF_RET(hashmap_info(BIF_P,BIF_ARG_1));
-    }
-    BIF_ERROR(BIF_P, BADARG);
-}
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 7281353af5..14f16e9050 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -63,6 +63,10 @@
  * - erts_internal:map_to_tuple_keys/1
  */
 
+#ifndef DECL_AM
+#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
+#endif
+
 /* for hashmap_from_list/1 */
 typedef struct {
     Uint32 hx;
@@ -82,6 +86,8 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size);
 static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n);
 static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
 static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
+static Eterm hashmap_info(Process *p, Eterm node);
+static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 static int hxnodecmp(hxnode_t* a, hxnode_t* b);
 static int hxnodecmpkey(hxnode_t* a, hxnode_t* b);
 
@@ -1993,6 +1999,13 @@ int erts_validate_and_sort_map(map_t* mp)
     return 1;
 }
 
+BIF_RETTYPE erts_debug_map_info_1(BIF_ALIST_1) {
+    if (is_hashmap(BIF_ARG_1)) {
+	BIF_RET(hashmap_info(BIF_P,BIF_ARG_1));
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
 /*
  * erts_internal:map_to_tuple_keys/1
  *
@@ -2007,6 +2020,126 @@ BIF_RETTYPE erts_internal_map_to_tuple_keys_1(BIF_ALIST_1) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
+static Eterm hashmap_info(Process *p, Eterm node) {
+    Eterm *hp;
+    Eterm res = NIL, info = NIL;
+    Eterm *ptr, tup, hdr;
+    Uint sz;
+    DECL_AM(depth);
+    DECL_AM(leafs);
+    DECL_AM(bitmaps);
+    DECL_AM(arrays);
+    Uint nleaf=0, nbitmap=0, narray=0;
+    Uint bitmap_usage[16], leaf_usage[16];
+    Uint lvl = 0, clvl;
+    DECLARE_ESTACK(stack);
+
+    for (sz = 0; sz < 16; sz++) {
+	bitmap_usage[sz] = 0;
+	leaf_usage[sz] = 0;
+    }
+
+    ptr = boxed_val(node);
+    ESTACK_PUSH(stack, 0);
+    ESTACK_PUSH(stack, node);
+    do {
+	node = ESTACK_POP(stack);
+	clvl = ESTACK_POP(stack);
+	lvl  = MAX(lvl,clvl);
+	switch(primary_tag(node)) {
+	    case TAG_PRIMARY_LIST:
+		nleaf++;
+		leaf_usage[clvl] += 1;
+		break;
+	    case TAG_PRIMARY_BOXED:
+		ptr = boxed_val(node);
+		hdr = *ptr;
+		ASSERT(is_header(hdr));
+		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+		    case HAMT_SUBTAG_NODE_ARRAY:
+			narray++;
+			sz = 16;
+			while(sz--) {
+			    ESTACK_PUSH(stack, clvl + 1);
+			    ESTACK_PUSH(stack, ptr[sz+1]);
+			}
+			break;
+		    case HAMT_SUBTAG_NODE_BITMAP:
+			nbitmap++;
+			sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+			ASSERT(sz < 17);
+			bitmap_usage[sz-1] += 1;
+			while(sz--) {
+			    ESTACK_PUSH(stack, clvl + 1);
+			    ESTACK_PUSH(stack, ptr[sz+1]);
+			}
+			break;
+		    case HAMT_SUBTAG_HEAD_BITMAP:
+			nbitmap++;
+			sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+			bitmap_usage[sz-1] += 1;
+			while(sz--) {
+			    ESTACK_PUSH(stack, clvl + 1);
+			    ESTACK_PUSH(stack, ptr[sz+2]);
+			}
+			break;
+		    case HAMT_SUBTAG_HEAD_ARRAY:
+			narray++;
+			sz = 16;
+			while(sz--) {
+			    ESTACK_PUSH(stack, clvl + 1);
+			    ESTACK_PUSH(stack, ptr[sz+2]);
+			}
+			break;
+		    default:
+			erl_exit(1, "bad header\r\n");
+			break;
+		}
+	}
+    } while(!ESTACK_ISEMPTY(stack));
+
+
+    /* size */
+    sz = 0;
+    hashmap_bld_tuple_uint(NULL,&sz,16,leaf_usage);
+    hashmap_bld_tuple_uint(NULL,&sz,16,bitmap_usage);
+
+    /* alloc */
+    hp   = HAlloc(p, 2+3 + 3*(2+4) + sz);
+
+    info = hashmap_bld_tuple_uint(&hp,NULL,16,leaf_usage);
+    tup  = TUPLE3(hp, AM_leafs, make_small(nleaf),info); hp += 4;
+    res  = CONS(hp, tup, res); hp += 2;
+
+    info = hashmap_bld_tuple_uint(&hp,NULL,16,bitmap_usage);
+    tup  = TUPLE3(hp, AM_bitmaps, make_small(nbitmap), info); hp += 4;
+    res  = CONS(hp, tup, res); hp += 2;
+
+    tup  = TUPLE3(hp, AM_arrays, make_small(narray),NIL); hp += 4;
+    res  = CONS(hp, tup, res); hp += 2;
+
+    tup  = TUPLE2(hp, AM_depth, make_small(lvl)); hp += 3;
+    res  = CONS(hp, tup, res); hp += 2;
+
+    DESTROY_ESTACK(stack);
+    ERTS_HOLE_CHECK(p);
+    return res;
+}
+
+static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]) {
+    Eterm res = THE_NON_VALUE;
+    Eterm *ts = (Eterm *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(Eterm));
+    Uint i;
+
+    for (i = 0; i < n; i++) {
+	ts[i] = erts_bld_uint(hpp, szp, nums[i]);
+    }
+    res = erts_bld_tuplev(hpp, szp, n, ts);
+    erts_free(ERTS_ALC_T_TMP, (void *) ts);
+    return res;
+}
+
+
 /* implementation of builtin emulations */
 
 #if !defined(__GNUC__)
-- 
cgit v1.2.3


From 7ac42c5d31f7722907b44d2df3421f8cd88d48c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Mon, 23 Feb 2015 17:27:41 +0100
Subject: erts: Move hashmap:merge/2 to maps

---
 erts/emulator/beam/bif.tab       |   3 -
 erts/emulator/beam/erl_hashmap.c | 262 ----------------------
 erts/emulator/beam/erl_map.c     | 467 ++++++++++++++++++++++++++++++++-------
 3 files changed, 392 insertions(+), 340 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index f9127ef955..b4e821a986 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -615,9 +615,6 @@ bif erlang:fun_info_mfa/1
 bif erlang:get_keys/0
 bif erts_debug:map_info/1
 
-# Hash Array Mappped Trie
-bif hashmap:merge/2
-
 #
 # Obsolete
 #
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
index fecdc2ef31..0c146137f5 100644
--- a/erts/emulator/beam/erl_hashmap.c
+++ b/erts/emulator/beam/erl_hashmap.c
@@ -56,9 +56,6 @@ static char *format_binary(Uint64 x, char *b) {
 }
 #endif
 
-
-static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
-
 /* hashmap:new/0 */
 
 /* hashmap:put/3 */
@@ -85,263 +82,4 @@ static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 
 /* hashmap:values/1 */
 
-BIF_RETTYPE hashmap_merge_2(BIF_ALIST_2) {
-    if (is_hashmap(BIF_ARG_1) && is_hashmap(BIF_ARG_2)) {
-	BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2));
-    }
-    BIF_ERROR(BIF_P, BADARG);
-}
-
-/* impl. */
-
-
-#define HALLOC_EXTRA 200
-
-static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
-#define PSTACK_TYPE struct HashmapMergePStackType
-    struct HashmapMergePStackType {
-	Eterm *srcA, *srcB;
-	Uint32 abm, bbm, rbm;        /* node bitmaps */
-	int keepA;
-	int ix;
-	Eterm array[16];
-    };
-    PSTACK_DECLARE(s, 4);
-    struct HashmapMergePStackType* sp = PSTACK_PUSH(s);
-    Eterm *hp, *nhp;
-    Eterm hdrA, hdrB;
-    Eterm th[2];
-    Uint32 ahx, bhx;
-    Uint size;  /* total key-value counter */
-    int keepA = 0;
-    unsigned lvl = 0;
-    Eterm res = THE_NON_VALUE;
-
-    /*
-     * Strategy: Do depth-first traversal of both trees (at the same time)
-     * and merge each pair of nodes.
-     */
-
-    {
-	hashmap_head_t* a = (hashmap_head_t*) hashmap_val(nodeA);
-	hashmap_head_t* b = (hashmap_head_t*) hashmap_val(nodeB);
-	size = a->size + b->size;
-    }
-
-recurse:
-
-    if (primary_tag(nodeA) == TAG_PRIMARY_BOXED &&
-	primary_tag(nodeB) == TAG_PRIMARY_LIST) {
-	/* Avoid implementing this combination by switching places */
-	Eterm tmp = nodeA;
-	nodeA = nodeB;
-	nodeB = tmp;
-	keepA = !keepA;
-    }
-
-    switch (primary_tag(nodeA)) {
-    case TAG_PRIMARY_LIST: {
-	sp->srcA = list_val(nodeA);
-	switch (primary_tag(nodeB)) {
-	case TAG_PRIMARY_LIST: { /* LEAF + LEAF */
-	    sp->srcB = list_val(nodeB);
-
-	    if (EQ(CAR(sp->srcA), CAR(sp->srcB))) {
-		--size;
-		res = keepA ? nodeA : nodeB;
-	    } else {
-		ahx = hashmap_restore_hash(th, lvl, CAR(sp->srcA));
-		bhx = hashmap_restore_hash(th, lvl, CAR(sp->srcB));
-		sp->abm = 1 << hashmap_index(ahx);
-		sp->bbm = 1 << hashmap_index(bhx);
-
-		sp->srcA = &nodeA;
-		sp->srcB = &nodeB;
-	    }
-	    break;
-	}
-	case TAG_PRIMARY_BOXED: { /* LEAF + NODE */
-	    sp->srcB = boxed_val(nodeB);
-	    ASSERT(is_header(*sp->srcB));
-	    hdrB = *sp->srcB++;
-
-	    ahx = hashmap_restore_hash(th, lvl, CAR(sp->srcA));
-	    sp->abm = 1 << hashmap_index(ahx);
-	    sp->srcA = &nodeA;
-	    switch(hdrB & _HEADER_MAP_SUBTAG_MASK) {
-	    case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++;
-	    case HAMT_SUBTAG_NODE_ARRAY:
-		sp->bbm = 0xffff;
-		break;
-
-	    case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++;
-	    case HAMT_SUBTAG_NODE_BITMAP:
-		sp->bbm = MAP_HEADER_VAL(hdrB);
-		break;
-
-	    default:
-		erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK);
-		break;
-	    }
-	    break;
-	}
-	default:
-	    erl_exit(1, "bad primary tag %ld\r\n", nodeB);
-	}
-	break;
-    }
-    case TAG_PRIMARY_BOXED: { /* NODE + NODE */
-	sp->srcA = boxed_val(nodeA);
-	hdrA = *sp->srcA++;
-	ASSERT(is_header(hdrA));
-	switch (hdrA & _HEADER_MAP_SUBTAG_MASK) {
-	case HAMT_SUBTAG_HEAD_ARRAY: sp->srcA++;
-	case HAMT_SUBTAG_NODE_ARRAY: {
-	    ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED);
-	    sp->abm = 0xffff;
-	    sp->srcB = boxed_val(nodeB);
-	    hdrB = *sp->srcB++;
-	    ASSERT(is_header(hdrB));
-	    switch (hdrB & _HEADER_MAP_SUBTAG_MASK) {
-	    case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++;
-	    case HAMT_SUBTAG_NODE_ARRAY:
-		sp->bbm = 0xffff;
-		break;
-	    case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++;
-	    case HAMT_SUBTAG_NODE_BITMAP:
-		sp->bbm = MAP_HEADER_VAL(hdrB);
-		break;
-	    default:
-		erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK);
-	    }
-	    break;
-	}
-	case HAMT_SUBTAG_HEAD_BITMAP: sp->srcA++;
-	case HAMT_SUBTAG_NODE_BITMAP: {
-	    ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED);
-	    sp->abm = MAP_HEADER_VAL(hdrA);
-	    sp->srcB = boxed_val(nodeB);
-	    hdrB = *sp->srcB++;
-	    ASSERT(is_header(hdrB));
-	    switch (hdrB & _HEADER_MAP_SUBTAG_MASK) {
-	    case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++;
-	    case HAMT_SUBTAG_NODE_ARRAY:
-		sp->bbm = 0xffff;
-		break;
-	    case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++;
-	    case HAMT_SUBTAG_NODE_BITMAP:
-		sp->bbm = MAP_HEADER_VAL(hdrB);
-		break;
-
-	    default:
-		erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK);
-	    }
-	    break;
-	}
-	default:
-	    erl_exit(1, "bad primary tag %ld\r\n", nodeA);
-	}
-	break;
-    }
-    default:
-	erl_exit(1, "bad primary tag %ld\r\n", nodeA);
-    }
-
-    for (;;) {
-	if (is_value(res)) { /* We have a complete (sub-)tree or leaf */
-	    if (lvl == 0)
-		break;
-
-	    /* Pop from stack and continue build parent node */
-	    lvl--;
-	    sp = PSTACK_POP(s);
-	    sp->array[sp->ix++] = res;
-	    res = THE_NON_VALUE;
-	    if (sp->rbm) {
-		sp->srcA++;
-		sp->srcB++;
-		keepA = sp->keepA;
-	    }
-	} else { /* Start build a node */
-	    sp->ix = 0;
-	    sp->rbm = sp->abm | sp->bbm;
-	    ASSERT(!(sp->rbm == 0 && lvl > 0));
-	}
-
-	while (sp->rbm) {
-	    Uint32 next = sp->rbm & (sp->rbm-1);
-	    Uint32 bit = sp->rbm ^ next;
-	    sp->rbm = next;
-	    if (sp->abm & bit) {
-		if (sp->bbm & bit) {
-		    /* Bit clash. Push and resolve by recursive merge */
-		    if (sp->rbm) {
-			sp->keepA = keepA;
-		    }
-		    nodeA = *sp->srcA;
-		    nodeB = *sp->srcB;
-		    lvl++;
-		    sp = PSTACK_PUSH(s);
-		    goto recurse;
-		} else {
-		    sp->array[sp->ix++] = *sp->srcA++;
-		}
-	    } else {
-		ASSERT(sp->bbm & bit);
-		sp->array[sp->ix++] = *sp->srcB++;
-	    }
-	}
-
-	ASSERT(sp->ix == hashmap_bitcount(sp->abm | sp->bbm));
-	if (lvl == 0) {
-	    nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(sp->ix), HALLOC_EXTRA);
-	    hp = nhp;
-	    *hp++ = (sp->ix == 16 ? MAP_HEADER_HAMT_HEAD_ARRAY
-		     : MAP_HEADER_HAMT_HEAD_BITMAP(sp->abm | sp->bbm));
-	    *hp++ = size;
-	} else {
-	    nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(sp->ix), HALLOC_EXTRA);
-	    hp = nhp;
-	    *hp++ = (sp->ix == 16 ? make_arityval(16)
-		     : MAP_HEADER_HAMT_NODE_BITMAP(sp->abm | sp->bbm));
-	}
-	memcpy(hp, sp->array, sp->ix * sizeof(Eterm));
-	res = make_boxed(nhp);
-    }
-    PSTACK_DESTROY(s);
-    return res;
-}
-
-static int hash_cmp(Uint32 ha, Uint32 hb)
-{
-    int i;
-    for (i=0; i<8; i++) {
-	int cmp = (int)(ha & 0xF) - (int)(hb & 0xF);
-	if (cmp)
-	    return cmp;
-	ha >>= 4;
-	hb >>= 4;
-    }
-    return 0;
-}
-
-int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp)
-{
-    Eterm th[2];
-    unsigned lvl = 0;
-
-    if (ap && bp) {
-	ASSERT(CMP_TERM(CAR(ap), CAR(bp)) != 0);
-	for (;;) {
-	    Uint32 ha = hashmap_restore_hash(th, lvl, CAR(ap));
-	    Uint32 hb = hashmap_restore_hash(th, lvl, CAR(bp));
-	    int cmp = hash_cmp(ha, hb);
-	    if (cmp)
-		return cmp;
-	    lvl += 8;
-	}
-    }
-    return ap ? -1 : 1;
-}
-
 /* hashmap:info/0 */
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 14f16e9050..8ae02e0183 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -77,6 +77,9 @@ typedef struct {
 
 static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node, int is_update);
+static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB);
+static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args);
+static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm hashmap_to_list(Process *p, Eterm map);
 static Eterm hashmap_keys(Process *p, Eterm map);
 static Eterm hashmap_values(Process *p, Eterm map);
@@ -452,6 +455,15 @@ static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n) {
     Uint jx = 0, ix = 0, lx, cx;
     Eterm res;
 
+    if (n == 0) {
+	Eterm *hp;
+	hp = HAlloc(p, 2);
+	hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(0);
+	hp[1] = 0;
+
+	return make_hashmap(hp);
+    }
+
     /* sort and compact array (remove non-unique entries) */
     qsort(hxns, n, sizeof(hxnode_t), (int (*)(const void *, const void *)) hxnodecmp);
 
@@ -504,8 +516,13 @@ static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n) {
 	res = hashmap_from_sorted_unique_array(p, hxns, cx, 0);
     } else {
 	Eterm *hp;
-	 /* hash value has been swizzled, need to drag it down to get the
+
+	/* we only have one item, either because n was 1 or
+	 * because we hade multiples of the same key.
+	 *
+	 * hash value has been swizzled, need to drag it down to get the
 	 * correct slot. */
+
 	hp    = HAlloc(p, HAMT_HEAD_BITMAP_SZ(1));
 	hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(1 << ((hxns[0].hx >> 0x1c) & 0xf));
 	hp[1] = 1;
@@ -769,8 +786,7 @@ static int hxnodecmp(hxnode_t *a, hxnode_t *b) {
 	return -1;
 }
 
-/* maps:is_key/2
- */
+/* maps:is_key/2 */
 
 BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) {
     if (is_map(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
@@ -779,8 +795,7 @@ BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
-/* maps:keys/1
- */
+/* maps:keys/1 */
 
 BIF_RETTYPE maps_keys_1(BIF_ALIST_1) {
     if (is_map(BIF_ARG_1)) {
@@ -807,94 +822,396 @@ BIF_RETTYPE maps_keys_1(BIF_ALIST_1) {
     }
     BIF_ERROR(BIF_P, BADARG);
 }
-/* maps:merge/2
- */
+/* maps:merge/2 */
 
 BIF_RETTYPE maps_merge_2(BIF_ALIST_2) {
-    if (is_map(BIF_ARG_1) && is_map(BIF_ARG_2)) {
-	Eterm *hp,*thp;
-	Eterm tup;
-	Eterm *ks,*vs,*ks1,*vs1,*ks2,*vs2;
-	map_t *mp1,*mp2,*mp_new;
-	Uint n1,n2,i1,i2,need,unused_size=0;
-	int c = 0;
-
-	mp1  = (map_t*)map_val(BIF_ARG_1);
-	mp2  = (map_t*)map_val(BIF_ARG_2);
-	n1   = map_get_size(mp1);
-	n2   = map_get_size(mp2);
-
-	need = MAP_HEADER_SIZE + 1 + 2*(n1 + n2);
-
-	hp     = HAlloc(BIF_P, need);
-	thp    = hp;
-	tup    = make_tuple(thp);
-	ks     = hp + 1; hp += 1 + n1 + n2;
-	mp_new = (map_t*)hp; hp += MAP_HEADER_SIZE;
-	vs     = hp; hp += n1 + n2;
-
-	mp_new->thing_word = MAP_HEADER;
-	mp_new->size = 0;
-	mp_new->keys = tup;
-
-	i1  = 0; i2 = 0;
-	ks1 = map_get_keys(mp1);
-	vs1 = map_get_values(mp1);
-	ks2 = map_get_keys(mp2);
-	vs2 = map_get_values(mp2);
-
-	while(i1 < n1 && i2 < n2) {
-	    c = CMP_TERM(ks1[i1],ks2[i2]);
-	    if ( c == 0) {
-		/* use righthand side arguments map value,
-		 * but advance both maps */
-		*ks++ = ks2[i2];
-		*vs++ = vs2[i2];
-		i1++, i2++, unused_size++;
-	    } else if ( c < 0) {
-		*ks++ = ks1[i1];
-		*vs++ = vs1[i1];
-		i1++;
-	    } else {
-		*ks++ = ks2[i2];
-		*vs++ = vs2[i2];
-		i2++;
-	    }
+    if (is_map(BIF_ARG_1)) {
+	if (is_map(BIF_ARG_2)) {
+	    BIF_RET(map_merge(BIF_P, BIF_ARG_1, BIF_ARG_2));
+	} else if (is_hashmap(BIF_ARG_2)) {
+	    BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_1, BIF_ARG_2, 0));
+	}
+    } else if (is_hashmap(BIF_ARG_1)) {
+	if (is_hashmap(BIF_ARG_2)) {
+	    BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2));
+	} else if (is_map(BIF_ARG_2)) {
+	    BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_2, BIF_ARG_1, 1));
 	}
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
 
-	/* copy remaining */
-	while (i1 < n1) {
+static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
+    Eterm *hp,*thp;
+    Eterm tup;
+    Eterm *ks,*vs,*ks1,*vs1,*ks2,*vs2;
+    map_t *mp1,*mp2,*mp_new;
+    Uint n1,n2,i1,i2,need,unused_size=0;
+    int c = 0;
+
+    mp1  = (map_t*)map_val(nodeA);
+    mp2  = (map_t*)map_val(nodeB);
+    n1   = map_get_size(mp1);
+    n2   = map_get_size(mp2);
+
+    need = MAP_HEADER_SIZE + 1 + 2*(n1 + n2);
+
+    hp     = HAlloc(p, need);
+    thp    = hp;
+    tup    = make_tuple(thp);
+    ks     = hp + 1; hp += 1 + n1 + n2;
+    mp_new = (map_t*)hp; hp += MAP_HEADER_SIZE;
+    vs     = hp; hp += n1 + n2;
+
+    mp_new->thing_word = MAP_HEADER;
+    mp_new->size = 0;
+    mp_new->keys = tup;
+
+    i1  = 0; i2 = 0;
+    ks1 = map_get_keys(mp1);
+    vs1 = map_get_values(mp1);
+    ks2 = map_get_keys(mp2);
+    vs2 = map_get_values(mp2);
+
+    while(i1 < n1 && i2 < n2) {
+	c = CMP_TERM(ks1[i1],ks2[i2]);
+	if ( c == 0) {
+	    /* use righthand side arguments map value,
+	     * but advance both maps */
+	    *ks++ = ks2[i2];
+	    *vs++ = vs2[i2];
+	    i1++, i2++, unused_size++;
+	} else if ( c < 0) {
 	    *ks++ = ks1[i1];
 	    *vs++ = vs1[i1];
 	    i1++;
-	}
-
-	while (i2 < n2) {
+	} else {
 	    *ks++ = ks2[i2];
 	    *vs++ = vs2[i2];
 	    i2++;
 	}
+    }
 
-	if (unused_size) {
-	    /* the key tuple is embedded in the heap, write a bignum to clear it.
-	     *
-	     * release values as normal since they are on the top of the heap
-	     * size = n1 + n1 - unused_size
-	     */
+    /* copy remaining */
+    while (i1 < n1) {
+	*ks++ = ks1[i1];
+	*vs++ = vs1[i1];
+	i1++;
+    }
+
+    while (i2 < n2) {
+	*ks++ = ks2[i2];
+	*vs++ = vs2[i2];
+	i2++;
+    }
+
+    if (unused_size) {
+	/* the key tuple is embedded in the heap, write a bignum to clear it.
+	 *
+	 * release values as normal since they are on the top of the heap
+	 * size = n1 + n1 - unused_size
+	 */
+
+	*ks = make_pos_bignum_header(unused_size - 1);
+	HRelease(p, vs + unused_size, vs);
+    }
+
+    mp_new->size = n1 + n2 - unused_size;
+    *thp = make_arityval(n1 + n2 - unused_size);
+
+    return make_map(mp_new);
+}
+
+static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args) {
+    Eterm *ks, *vs, *hp, res;
+    map_t *mp;
+    Uint n, i;
+    hxnode_t *hxns;
+    Uint32 sw, hx;
+    Eterm tmp[2];
+
+    /* convert flat to tree */
+
+    ASSERT(is_map(flat));
+    ASSERT(is_hashmap(tree));
+
+    mp = (map_t*)map_val(flat);
+    n  = map_get_size(mp);
+
+    ks = map_get_keys(mp);
+    vs = map_get_values(mp);
+
+    hp = HAlloc(p, 2 * n);
+
+    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t));
+
+    for (i = 0; i < n; i++) {
+	hx = hashmap_restore_hash(tmp,0,ks[i]);
+	swizzle32(sw,hx);
+	hxns[i].hx   = sw;
+	hxns[i].val  = CONS(hp, ks[i], vs[i]); hp += 2;
+	hxns[i].skip = 1;
+	hxns[i].i    = i;
+    }
+
+    res = hashmap_from_unsorted_array(p, hxns, n);
+
+    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+
+    return swap_args ? hashmap_merge(p, tree, res) : hashmap_merge(p, res, tree);
+}
+
+#define HALLOC_EXTRA 200
+
+static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
+#define PSTACK_TYPE struct HashmapMergePStackType
+    struct HashmapMergePStackType {
+	Eterm *srcA, *srcB;
+	Uint32 abm, bbm, rbm;        /* node bitmaps */
+	int keepA;
+	int ix;
+	Eterm array[16];
+    };
+    PSTACK_DECLARE(s, 4);
+    struct HashmapMergePStackType* sp = PSTACK_PUSH(s);
+    Eterm *hp, *nhp;
+    Eterm hdrA, hdrB;
+    Eterm th[2];
+    Uint32 ahx, bhx;
+    Uint size;  /* total key-value counter */
+    int keepA = 0;
+    unsigned lvl = 0;
+    Eterm res = THE_NON_VALUE;
+
+    /*
+     * Strategy: Do depth-first traversal of both trees (at the same time)
+     * and merge each pair of nodes.
+     */
 
-	    *ks = make_pos_bignum_header(unused_size - 1);
-	    HRelease(BIF_P, vs + unused_size, vs);
+    {
+	hashmap_head_t* a = (hashmap_head_t*) hashmap_val(nodeA);
+	hashmap_head_t* b = (hashmap_head_t*) hashmap_val(nodeB);
+	size = a->size + b->size;
+    }
+
+recurse:
+
+    if (primary_tag(nodeA) == TAG_PRIMARY_BOXED &&
+	primary_tag(nodeB) == TAG_PRIMARY_LIST) {
+	/* Avoid implementing this combination by switching places */
+	Eterm tmp = nodeA;
+	nodeA = nodeB;
+	nodeB = tmp;
+	keepA = !keepA;
+    }
+
+    switch (primary_tag(nodeA)) {
+    case TAG_PRIMARY_LIST: {
+	sp->srcA = list_val(nodeA);
+	switch (primary_tag(nodeB)) {
+	case TAG_PRIMARY_LIST: { /* LEAF + LEAF */
+	    sp->srcB = list_val(nodeB);
+
+	    if (EQ(CAR(sp->srcA), CAR(sp->srcB))) {
+		--size;
+		res = keepA ? nodeA : nodeB;
+	    } else {
+		ahx = hashmap_restore_hash(th, lvl, CAR(sp->srcA));
+		bhx = hashmap_restore_hash(th, lvl, CAR(sp->srcB));
+		sp->abm = 1 << hashmap_index(ahx);
+		sp->bbm = 1 << hashmap_index(bhx);
+
+		sp->srcA = &nodeA;
+		sp->srcB = &nodeB;
+	    }
+	    break;
 	}
+	case TAG_PRIMARY_BOXED: { /* LEAF + NODE */
+	    sp->srcB = boxed_val(nodeB);
+	    ASSERT(is_header(*sp->srcB));
+	    hdrB = *sp->srcB++;
+
+	    ahx = hashmap_restore_hash(th, lvl, CAR(sp->srcA));
+	    sp->abm = 1 << hashmap_index(ahx);
+	    sp->srcA = &nodeA;
+	    switch(hdrB & _HEADER_MAP_SUBTAG_MASK) {
+	    case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++;
+	    case HAMT_SUBTAG_NODE_ARRAY:
+		sp->bbm = 0xffff;
+		break;
 
-	mp_new->size = n1 + n2 - unused_size;
-	*thp = make_arityval(n1 + n2 - unused_size);
+	    case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++;
+	    case HAMT_SUBTAG_NODE_BITMAP:
+		sp->bbm = MAP_HEADER_VAL(hdrB);
+		break;
 
-	BIF_RET(make_map(mp_new));
+	    default:
+		erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK);
+		break;
+	    }
+	    break;
+	}
+	default:
+	    erl_exit(1, "bad primary tag %ld\r\n", nodeB);
+	}
+	break;
     }
-    BIF_ERROR(BIF_P, BADARG);
+    case TAG_PRIMARY_BOXED: { /* NODE + NODE */
+	sp->srcA = boxed_val(nodeA);
+	hdrA = *sp->srcA++;
+	ASSERT(is_header(hdrA));
+	switch (hdrA & _HEADER_MAP_SUBTAG_MASK) {
+	case HAMT_SUBTAG_HEAD_ARRAY: sp->srcA++;
+	case HAMT_SUBTAG_NODE_ARRAY: {
+	    ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED);
+	    sp->abm = 0xffff;
+	    sp->srcB = boxed_val(nodeB);
+	    hdrB = *sp->srcB++;
+	    ASSERT(is_header(hdrB));
+	    switch (hdrB & _HEADER_MAP_SUBTAG_MASK) {
+	    case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++;
+	    case HAMT_SUBTAG_NODE_ARRAY:
+		sp->bbm = 0xffff;
+		break;
+	    case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++;
+	    case HAMT_SUBTAG_NODE_BITMAP:
+		sp->bbm = MAP_HEADER_VAL(hdrB);
+		break;
+	    default:
+		erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK);
+	    }
+	    break;
+	}
+	case HAMT_SUBTAG_HEAD_BITMAP: sp->srcA++;
+	case HAMT_SUBTAG_NODE_BITMAP: {
+	    ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED);
+	    sp->abm = MAP_HEADER_VAL(hdrA);
+	    sp->srcB = boxed_val(nodeB);
+	    hdrB = *sp->srcB++;
+	    ASSERT(is_header(hdrB));
+	    switch (hdrB & _HEADER_MAP_SUBTAG_MASK) {
+	    case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++;
+	    case HAMT_SUBTAG_NODE_ARRAY:
+		sp->bbm = 0xffff;
+		break;
+	    case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++;
+	    case HAMT_SUBTAG_NODE_BITMAP:
+		sp->bbm = MAP_HEADER_VAL(hdrB);
+		break;
+
+	    default:
+		erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK);
+	    }
+	    break;
+	}
+	default:
+	    erl_exit(1, "bad primary tag %ld\r\n", nodeA);
+	}
+	break;
+    }
+    default:
+	erl_exit(1, "bad primary tag %ld\r\n", nodeA);
+    }
+
+    for (;;) {
+	if (is_value(res)) { /* We have a complete (sub-)tree or leaf */
+	    if (lvl == 0)
+		break;
+
+	    /* Pop from stack and continue build parent node */
+	    lvl--;
+	    sp = PSTACK_POP(s);
+	    sp->array[sp->ix++] = res;
+	    res = THE_NON_VALUE;
+	    if (sp->rbm) {
+		sp->srcA++;
+		sp->srcB++;
+		keepA = sp->keepA;
+	    }
+	} else { /* Start build a node */
+	    sp->ix = 0;
+	    sp->rbm = sp->abm | sp->bbm;
+	    ASSERT(!(sp->rbm == 0 && lvl > 0));
+	}
+
+	while (sp->rbm) {
+	    Uint32 next = sp->rbm & (sp->rbm-1);
+	    Uint32 bit = sp->rbm ^ next;
+	    sp->rbm = next;
+	    if (sp->abm & bit) {
+		if (sp->bbm & bit) {
+		    /* Bit clash. Push and resolve by recursive merge */
+		    if (sp->rbm) {
+			sp->keepA = keepA;
+		    }
+		    nodeA = *sp->srcA;
+		    nodeB = *sp->srcB;
+		    lvl++;
+		    sp = PSTACK_PUSH(s);
+		    goto recurse;
+		} else {
+		    sp->array[sp->ix++] = *sp->srcA++;
+		}
+	    } else {
+		ASSERT(sp->bbm & bit);
+		sp->array[sp->ix++] = *sp->srcB++;
+	    }
+	}
+
+	ASSERT(sp->ix == hashmap_bitcount(sp->abm | sp->bbm));
+	if (lvl == 0) {
+	    nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(sp->ix), HALLOC_EXTRA);
+	    hp = nhp;
+	    *hp++ = (sp->ix == 16 ? MAP_HEADER_HAMT_HEAD_ARRAY
+		     : MAP_HEADER_HAMT_HEAD_BITMAP(sp->abm | sp->bbm));
+	    *hp++ = size;
+	} else {
+	    nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(sp->ix), HALLOC_EXTRA);
+	    hp = nhp;
+	    *hp++ = (sp->ix == 16 ? make_arityval(16)
+		     : MAP_HEADER_HAMT_NODE_BITMAP(sp->abm | sp->bbm));
+	}
+	memcpy(hp, sp->array, sp->ix * sizeof(Eterm));
+	res = make_boxed(nhp);
+    }
+    PSTACK_DESTROY(s);
+    return res;
 }
-/* maps:new/2
- */
+
+static int hash_cmp(Uint32 ha, Uint32 hb)
+{
+    int i;
+    for (i=0; i<8; i++) {
+	int cmp = (int)(ha & 0xF) - (int)(hb & 0xF);
+	if (cmp)
+	    return cmp;
+	ha >>= 4;
+	hb >>= 4;
+    }
+    return 0;
+}
+
+int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp)
+{
+    Eterm th[2];
+    unsigned lvl = 0;
+
+    if (ap && bp) {
+	ASSERT(CMP_TERM(CAR(ap), CAR(bp)) != 0);
+	for (;;) {
+	    Uint32 ha = hashmap_restore_hash(th, lvl, CAR(ap));
+	    Uint32 hb = hashmap_restore_hash(th, lvl, CAR(bp));
+	    int cmp = hash_cmp(ha, hb);
+	    if (cmp)
+		return cmp;
+	    lvl += 8;
+	}
+    }
+    return ap ? -1 : 1;
+}
+
+/* maps:new/0 */
 
 BIF_RETTYPE maps_new_0(BIF_ALIST_0) {
     Eterm* hp;
-- 
cgit v1.2.3


From efb521c69baccb8ab905595c222abf353c5c3283 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Mon, 23 Feb 2015 17:47:26 +0100
Subject: erts: Remove erl_hashmap.[ch] files

---
 erts/emulator/Makefile.in            |  3 +-
 erts/emulator/beam/copy.c            |  1 -
 erts/emulator/beam/erl_bif_guard.c   |  1 -
 erts/emulator/beam/erl_gc.c          |  1 -
 erts/emulator/beam/erl_hashmap.c     | 85 -------------------------------
 erts/emulator/beam/erl_hashmap.h     | 96 ------------------------------------
 erts/emulator/beam/erl_map.c         |  4 +-
 erts/emulator/beam/erl_map.h         | 67 +++++++++++++++++++++++++
 erts/emulator/beam/erl_printf_term.c |  1 -
 erts/emulator/beam/external.c        |  1 -
 erts/emulator/beam/utils.c           |  1 -
 erts/emulator/hipe/hipe_gc.c         |  1 -
 12 files changed, 71 insertions(+), 191 deletions(-)
 delete mode 100644 erts/emulator/beam/erl_hashmap.c
 delete mode 100644 erts/emulator/beam/erl_hashmap.h

(limited to 'erts')

diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index db7eac4690..a632faf57d 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -785,8 +785,7 @@ RUN_OBJS = \
 	$(OBJDIR)/erl_zlib.o		$(OBJDIR)/erl_nif.o \
 	$(OBJDIR)/erl_bif_binary.o      $(OBJDIR)/erl_ao_firstfit_alloc.o \
 	$(OBJDIR)/erl_thr_queue.o	$(OBJDIR)/erl_sched_spec_pre_alloc.o \
-	$(OBJDIR)/erl_ptab.o		$(OBJDIR)/erl_map.o \
-	$(OBJDIR)/erl_hashmap.o
+	$(OBJDIR)/erl_ptab.o		$(OBJDIR)/erl_map.o
 
 ifeq ($(TARGET),win32)
 DRV_OBJS = \
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index 6294ba9412..5901c00d0a 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -33,7 +33,6 @@
 #include "erl_binary.h"
 #include "erl_bits.h"
 #include "dtrace-wrapper.h"
-#include "erl_hashmap.h"
 
 static void move_one_frag(Eterm** hpp, Eterm* src, Uint src_sz, ErlOffHeap*);
 
diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c
index a5d1d3a5cb..bc0891422b 100644
--- a/erts/emulator/beam/erl_bif_guard.c
+++ b/erts/emulator/beam/erl_bif_guard.c
@@ -34,7 +34,6 @@
 #include "big.h"
 #include "erl_binary.h"
 #include "erl_map.h"
-#include "erl_hashmap.h"
 
 static Eterm gc_double_to_integer(Process* p, double x, Eterm* reg, Uint live);
 
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index bdf7aa362e..d1a7ee113b 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -31,7 +31,6 @@
 #include "erl_binary.h"
 #include "erl_bits.h"
 #include "erl_map.h"
-#include "erl_hashmap.h"
 #include "error.h"
 #include "big.h"
 #include "erl_gc.h"
diff --git a/erts/emulator/beam/erl_hashmap.c b/erts/emulator/beam/erl_hashmap.c
deleted file mode 100644
index 0c146137f5..0000000000
--- a/erts/emulator/beam/erl_hashmap.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 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%
- *
- * hashmaps are an adaption of Rich Hickeys Persistent HashMaps
- *   which were an adaption of Phil Bagwells - Hash Array Mapped Tries
- *
- * Author: Björn-Egil Dahlberg
- */
-/*
- * Ls = lists:seq(1,188888).
- * A = lists:foldl(fun(I,O) -> hashmap:put(I,I,O) end, hashmap:new(), Ls).
- * lists:foreach(fun(I) -> io:format("looking up ~p got ~p~n", [I, hashmap:get(I, A)]), I = hashmap:get(I,A) end, Ls).
- *
- * lists:foldl(fun(I,O) -> hashmap:put(I,I,O) end, hashmap:new(), lists:seq(1,7)).
- * lists:foldl(fun(I,O) -> hashmap:info(O), hashmap:put(I,I,O) end, hashmap:new(), lists:seq(1,5)).
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#  include "config.h"
-#endif
-
-#include "sys.h"
-#include "erl_vm.h"
-#include "global.h"
-#include "erl_process.h"
-#include "error.h"
-#include "bif.h"
-
-#include "erl_map.h"
-#include "erl_hashmap.h"
-
-#if 0
-static char *format_binary(Uint64 x, char *b) {
-    int z;
-    b[64] = '\0';
-    for (z = 0; z < 64; z++) { 
-	b[63-z] = ((x>>z) & 0x1) ? '1' : '0'; 
-    }
-    return b;
-}
-#endif
-
-/* hashmap:new/0 */
-
-/* hashmap:put/3 */
-
-/* hashmap:update/3 */
-
-/* hashmap:to_list/1 */
-
-/* hashmap:from_list/1 */
-
-/* hashmap:get/2 */
-
-/* hashmap:find/2 */
-
-/* hashmap:remove/2 */
-
-/* hashmap:size/1 */
-
-/* erlang:is_hashmap/1 */
-
-/* hashmap:is_key/2 */
-
-/* hashmap:keys/1 */
-
-/* hashmap:values/1 */
-
-/* hashmap:info/0 */
diff --git a/erts/emulator/beam/erl_hashmap.h b/erts/emulator/beam/erl_hashmap.h
deleted file mode 100644
index 7ac33b34b0..0000000000
--- a/erts/emulator/beam/erl_hashmap.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * %CopyrightBegin%
- * 
- * Copyright Ericsson AB 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_HASH_H__
-#define __ERL_HASH_H__
-
-#include "sys.h"
-#include "erl_term.h"
-
-int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
-
-/* HASH */
-
-/* hamt nodes v2.0
- * 
- * node :: leaf | array | bitmap
- * head
- */
-typedef struct hashmap_head_s {
-    Eterm thing_word;
-    Uint size;
-    Eterm items[1];
-} hashmap_head_t;
-
-/* thing_word tagscheme
- * Need two bits for map subtags
- *
- * Original HEADER representation:
- *
- *     aaaaaaaaaaaaaaaa aaaaaaaaaatttt00       arity:26, tag:4
- *
- * For maps we have:
- *
- *     vvvvvvvvvvvvvvvv aaaaaaaamm111100       val:16, arity:8, mtype:2
- *
- * unsure about trailing zeros
- *
- * map-tag:
- *     00 - flat map tag (non-hamt) -> val:16 = #items
- *     01 - map-node bitmap tag     -> val:16 = bitmap
- *     10 - map-head (array-node)   -> val:16 = 0xffff
- *     11 - map-head (bitmap-node)  -> val:16 = bitmap
- */
-
-/* erl_map.h stuff */
-
-#define is_hashmap_header_head(x) ((MAP_HEADER_TYPE(x) & (0x2)))
-
-#define MAKE_MAP_HEADER(Type,Arity,Val) \
-    (_make_header(((((Uint16)(Val)) << MAP_HEADER_ARITY_SZ) | (Arity)) << MAP_HEADER_TAG_SZ | (Type) , _TAG_HEADER_HASHMAP))
-
-#define MAP_HEADER_HAMT_HEAD_ARRAY \
-    MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_HEAD_ARRAY,0x1,0xffff)
-
-#define MAP_HEADER_HAMT_HEAD_BITMAP(Bmp) \
-    MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_HEAD_BITMAP,0x1,Bmp)
-
-#define MAP_HEADER_HAMT_NODE_ARRAY \
-    make_arityval(16)
-
-#define MAP_HEADER_HAMT_NODE_BITMAP(Bmp) \
-    MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_NODE_BITMAP,0x0,Bmp)
-
-#define HAMT_HEAD_EMPTY_SZ     (2)
-#define HAMT_NODE_ARRAY_SZ     (17)
-#define HAMT_HEAD_ARRAY_SZ     (18)
-#define HAMT_NODE_BITMAP_SZ(n) (1 + n)
-#define HAMT_HEAD_BITMAP_SZ(n) (2 + n)
-
-#define _HEADER_MAP_SUBTAG_MASK    (0xfc) /* 2 bits maps tag + 4 bits subtag + 2 ignore bits */
-/* SUBTAG_NODE_ARRAY is in fact a tuple with 16 elements */
-#define HAMT_SUBTAG_NODE_ARRAY   (((16 << _HEADER_ARITY_OFFS) | ARITYVAL_SUBTAG) & _HEADER_MAP_SUBTAG_MASK)
-#define HAMT_SUBTAG_NODE_BITMAP  ((MAP_HEADER_TAG_HAMT_NODE_BITMAP << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG)
-#define HAMT_SUBTAG_HEAD_ARRAY   ((MAP_HEADER_TAG_HAMT_HEAD_ARRAY << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG)
-#define HAMT_SUBTAG_HEAD_BITMAP  ((MAP_HEADER_TAG_HAMT_HEAD_BITMAP << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG)
-
-#define hashmap_index(hash)      (((Uint32)hash) & 0xf)
-
-#endif
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 8ae02e0183..4242807933 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -16,6 +16,9 @@
  *
  * %CopyrightEnd%
  *
+ * hashmaps are an adaption of Rich Hickeys Persistent HashMaps
+ *   which were an adaption of Phil Bagwells - Hash Array Mapped Tries
+ *
  * Author: Björn-Egil Dahlberg
  */
 
@@ -31,7 +34,6 @@
 #include "bif.h"
 
 #include "erl_map.h"
-#include "erl_hashmap.h"
 
 /* BIFs
  *
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 428cfe9b63..7fddc9c240 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -91,6 +91,7 @@ int   erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res);
 int   erts_validate_and_sort_map(map_t* map);
 void  hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
+int   hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
 Eterm erts_hashmap_get(Eterm key, Eterm map);
 Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n);
 
@@ -104,4 +105,70 @@ erts_maps_get(Eterm key, Eterm map);
 #  define erts_maps_get_rel(A, B, B_BASE) erts_maps_get(A, B)
 #endif
 
+/* hamt nodes v2.0
+ *
+ * node :: leaf | array | bitmap
+ * head
+ */
+typedef struct hashmap_head_s {
+    Eterm thing_word;
+    Uint size;
+    Eterm items[1];
+} hashmap_head_t;
+
+/* thing_word tagscheme
+ * Need two bits for map subtags
+ *
+ * Original HEADER representation:
+ *
+ *     aaaaaaaaaaaaaaaa aaaaaaaaaatttt00       arity:26, tag:4
+ *
+ * For maps we have:
+ *
+ *     vvvvvvvvvvvvvvvv aaaaaaaamm111100       val:16, arity:8, mtype:2
+ *
+ * unsure about trailing zeros
+ *
+ * map-tag:
+ *     00 - flat map tag (non-hamt) -> val:16 = #items
+ *     01 - map-node bitmap tag     -> val:16 = bitmap
+ *     10 - map-head (array-node)   -> val:16 = 0xffff
+ *     11 - map-head (bitmap-node)  -> val:16 = bitmap
+ */
+
+/* erl_map.h stuff */
+
+#define is_hashmap_header_head(x) ((MAP_HEADER_TYPE(x) & (0x2)))
+
+#define MAKE_MAP_HEADER(Type,Arity,Val) \
+    (_make_header(((((Uint16)(Val)) << MAP_HEADER_ARITY_SZ) | (Arity)) << MAP_HEADER_TAG_SZ | (Type) , _TAG_HEADER_HASHMAP))
+
+#define MAP_HEADER_HAMT_HEAD_ARRAY \
+    MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_HEAD_ARRAY,0x1,0xffff)
+
+#define MAP_HEADER_HAMT_HEAD_BITMAP(Bmp) \
+    MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_HEAD_BITMAP,0x1,Bmp)
+
+#define MAP_HEADER_HAMT_NODE_ARRAY \
+    make_arityval(16)
+
+#define MAP_HEADER_HAMT_NODE_BITMAP(Bmp) \
+    MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_NODE_BITMAP,0x0,Bmp)
+
+#define HAMT_HEAD_EMPTY_SZ     (2)
+#define HAMT_NODE_ARRAY_SZ     (17)
+#define HAMT_HEAD_ARRAY_SZ     (18)
+#define HAMT_NODE_BITMAP_SZ(n) (1 + n)
+#define HAMT_HEAD_BITMAP_SZ(n) (2 + n)
+
+#define _HEADER_MAP_SUBTAG_MASK    (0xfc) /* 2 bits maps tag + 4 bits subtag + 2 ignore bits */
+/* SUBTAG_NODE_ARRAY is in fact a tuple with 16 elements */
+#define HAMT_SUBTAG_NODE_ARRAY   (((16 << _HEADER_ARITY_OFFS) | ARITYVAL_SUBTAG) & _HEADER_MAP_SUBTAG_MASK)
+#define HAMT_SUBTAG_NODE_BITMAP  ((MAP_HEADER_TAG_HAMT_NODE_BITMAP << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG)
+#define HAMT_SUBTAG_HEAD_ARRAY   ((MAP_HEADER_TAG_HAMT_HEAD_ARRAY << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG)
+#define HAMT_SUBTAG_HEAD_BITMAP  ((MAP_HEADER_TAG_HAMT_HEAD_BITMAP << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG)
+
+#define hashmap_index(hash)      (((Uint32)hash) & 0xf)
+
+
 #endif
diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c
index b07b7785dd..81fd19693a 100644
--- a/erts/emulator/beam/erl_printf_term.c
+++ b/erts/emulator/beam/erl_printf_term.c
@@ -26,7 +26,6 @@
 #include "big.h"
 #include "erl_map.h"
 #include "erl_binary.h"
-#include "erl_hashmap.h"
 
 #define PRINT_CHAR(CNT, FN, ARG, C)					\
 do {									\
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 9030b528a4..af8db4c265 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -45,7 +45,6 @@
 #include "erl_bits.h"
 #include "erl_zlib.h"
 #include "erl_map.h"
-#include "erl_hashmap.h"
 
 #define in_area(ptr,start,nbytes) ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
 
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index f595cfbacd..d234e8fc68 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -32,7 +32,6 @@
 #include "erl_binary.h"
 #include "erl_bits.h"
 #include "erl_map.h"
-#include "erl_hashmap.h"
 #include "packet_parser.h"
 #include "erl_gc.h"
 #define ERTS_WANT_DB_INTERNAL__
diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c
index 398cbcdf14..b10263f6e2 100644
--- a/erts/emulator/hipe/hipe_gc.c
+++ b/erts/emulator/hipe/hipe_gc.c
@@ -28,7 +28,6 @@
 #include "global.h"
 
 #include "erl_gc.h"
-#include "erl_hashmap.h"
 
 #include "hipe_stack.h"
 #include "hipe_gc.h"
-- 
cgit v1.2.3


From 68d3adb3b063935942b2d692f68e2dc197f87077 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 24 Feb 2015 14:39:53 +0100
Subject: erts: Remove hashmap from tests

---
 erts/emulator/test/map_SUITE.erl | 68 +++++++++++++++++-----------------------
 1 file changed, 28 insertions(+), 40 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index aa835f251b..ac9356d173 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -484,18 +484,17 @@ t_map_compare(Config) when is_list(Config) ->
     Seed = erlang:now(),
     io:format("seed = ~p\n", [Seed]),
     random:seed(Seed),
-    repeat(100, fun(_) -> float_int_compare(maps) end, []),
-    repeat(100, fun(_) -> float_int_compare(hashmap) end, []),
+    repeat(100, fun(_) -> float_int_compare() end, []),
     repeat(100, fun(_) -> recursive_compare() end, []),
     ok.
 
-float_int_compare(MapMod) ->
+float_int_compare() ->
     Terms = numeric_keys(3),
     %%io:format("Keys to use: ~p\n", [Terms]),
     Pairs = lists:map(fun(K) -> list_to_tuple([{K,V} || V <- Terms]) end, Terms),
     lists:foreach(fun(Size) ->
-			  MapGen = fun() -> map_gen(MapMod, list_to_tuple(Pairs), Size) end,
-			  repeat(100, fun do_compare/1, [MapMod, MapGen, MapGen])
+			  MapGen = fun() -> map_gen(list_to_tuple(Pairs), Size) end,
+			  repeat(100, fun do_compare/1, [MapGen, MapGen])
 		  end,
 		  lists:seq(1,length(Terms))),
     ok.
@@ -522,31 +521,31 @@ copy_term(T) ->
     P ! T,
     receive R -> R end.
 
-do_compare([MapMod, Gen1, Gen2]) ->
+do_compare([Gen1, Gen2]) ->
     M1 = Gen1(),
     M2 = Gen2(),
     %%io:format("Maps to compare: ~p AND ~p\n", [M1, M2]),
     C = (M1 < M2),
-    Erlang = maps_lessthan(MapMod, M1, M2),
+    Erlang = maps_lessthan(M1, M2),
     C = Erlang,
     ?CHECK(M1==M1, M1),
 
     %% Change one key from int to float (or vice versa) and check compare
-    ML1 = MapMod:to_list(M1),
+    ML1 = maps:to_list(M1),
     {K1,V1} = lists:nth(random:uniform(length(ML1)), ML1),
     case K1 of
 	I when is_integer(I) ->
-	    case MapMod:find(float(I),M1) of
+	    case maps:find(float(I),M1) of
 		error ->
-		    M1f = MapMod:remove(I, MapMod:put(float(I), V1, M1)),
+		    M1f = maps:remove(I, maps:put(float(I), V1, M1)),
 		    ?CHECK(M1f > M1, [M1f, M1]);
 		_ -> ok
 	    end;
 
 	F when is_float(F), round(F) == F ->
-	    case MapMod:find(round(F),M1) of
+	    case maps:find(round(F),M1) of
 		error ->
-		    M1i = MapMod:remove(F, MapMod:put(round(F), V1, M1)),
+		    M1i = maps:remove(F, maps:put(round(F), V1, M1)),
 		    ?CHECK(M1i < M1, [M1i, M1]);
 		_ -> ok
 	    end;
@@ -557,11 +556,11 @@ do_compare([MapMod, Gen1, Gen2]) ->
     ?CHECK(M2 == M2, [M2]).
 
 
-maps_lessthan(MapMod, M1, M2) ->
-  case {MapMod:size(M1),MapMod:size(M2)} of
+maps_lessthan(M1, M2) ->
+  case {maps:size(M1),maps:size(M2)} of
       {_S,_S} ->
-	  {K1,V1} = lists:unzip(term_sort(MapMod:to_list(M1))),
-	  {K2,V2} = lists:unzip(term_sort(MapMod:to_list(M2))),
+	  {K1,V1} = lists:unzip(term_sort(maps:to_list(M1))),
+	  {K2,V2} = lists:unzip(term_sort(maps:to_list(M2))),
 
 	  case erts_internal:cmp_term(K1,K2) of
 	      -1 -> true;
@@ -591,17 +590,16 @@ cmp([H1|T1], [H2|T2], Exact) ->
 	C -> C
     end;
 
-cmp(M1, M2, Exact) -> %when is_hashmap(M1) and is_hashmap(M2)
-    case {erlang:is_hashmap(M1), erlang:is_hashmap(M2)} of
-	{true, true} -> cmp_hashmaps(M1, M2, Exact);
-	_ -> cmp_others(M1, M2, Exact)
-    end.
+cmp(M1, M2, Exact) when is_map(M1) andalso is_map(M2) ->
+    cmp_maps(M1,M2,Exact);
+cmp(M1, M2, Exact) ->
+    cmp_others(M1, M2, Exact).
 
-cmp_hashmaps(M1, M2, Exact) ->
-    case {hashmap:size(M1),hashmap:size(M2)} of
+cmp_maps(M1, M2, Exact) ->
+    case {maps:size(M1),maps:size(M2)} of
 	{_S,_S} ->
-	    {K1,V1} = lists:unzip(term_sort(hashmap:to_list(M1))),
-	    {K2,V2} = lists:unzip(term_sort(hashmap:to_list(M2))),
+	    {K1,V1} = lists:unzip(term_sort(maps:to_list(M1))),
+	    {K2,V2} = lists:unzip(term_sort(maps:to_list(M2))),
 
 	    case cmp(K1, K2, true) of
 		0 -> cmp(V1, V2, Exact);
@@ -623,7 +621,7 @@ cmp_others(T1, T2, _) ->
 	{false,false} -> 1
     end.
 
-map_gen(MapMod, Pairs, Size) ->
+map_gen(Pairs, Size) ->
     {_,L} = lists:foldl(fun(_, {Keys, Acc}) ->
 				KI = random:uniform(size(Keys)),
 				K = element(KI,Keys),
@@ -633,7 +631,7 @@ map_gen(MapMod, Pairs, Size) ->
 			{Pairs, []},
 			lists:seq(1,Size)),
 
-    map_from_list(MapMod, L).
+    maps:from_list(L).
 
 
 recursive_compare() ->
@@ -674,18 +672,16 @@ term_gen_recursive(Leafs, Flags, Depth) ->
 		  random:uniform(size(Leafs)+3)
 	  end,
     case Rnd of
-	1 -> % Make hashmap
+	1 -> % Make map
 	    Size = random:uniform(size(Leafs)),
-	    %%io:format("Generate hashmap with size ~p:\n", [Size]),
 	    lists:foldl(fun(_, {Acc1,Acc2}) ->
 				{K1,K2} = term_gen_recursive(Leafs, Flags,
 							     Depth+1),
 				{V1,V2} = term_gen_recursive(Leafs, Flags, Depth+1),
-				%%io:format("hashmap:put(~p, ~p)\n", [K,V]),
 				%%ok = check_keys(K1,K2, 0),
-				{hashmap:put(K1,V1, Acc1), hashmap:put(K2,V2, Acc2)}
+				{maps:put(K1,V1, Acc1), maps:put(K2,V2, Acc2)}
 			end,
-			{hashmap:new(), hashmap:new()},
+			{maps:new(), maps:new()},
 			lists:seq(1,Size));
 	2 -> % Make cons
 	    {Car1,Car2} = term_gen_recursive(Leafs, Flags, Depth+1),
@@ -710,14 +706,6 @@ term_gen_recursive(Leafs, Flags, Depth) ->
 	    end
     end.
 
-map_from_list(maps, L) ->
-    maps:from_list(L);
-map_from_list(hashmap, L) ->  %% while waiting for Egil...
-    lists:foldl(fun({K,V},Acc) -> hashmap:put(K,V,Acc) end,
-		hashmap:new(),
-		L).
-
-
 check_keys(K1, K2, _) when K1 =:= K2 ->
     case erlang:phash3(K1) =:= erlang:phash3(K2) of
 	true -> ok;
-- 
cgit v1.2.3


From bd827415e7a505b499c2c367faec104f15716d0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 24 Feb 2015 14:56:37 +0100
Subject: erts: Add tests for large maps:get/2 and maps:find/2

---
 erts/emulator/test/map_SUITE.erl | 35 +++++++++++++++++++++++++++--------
 1 file changed, 27 insertions(+), 8 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index ac9356d173..c15cf76a26 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -729,25 +729,35 @@ check_keys(K1, K2, L) ->
 
 %% BIFs
 t_bif_map_get(Config) when is_list(Config) ->
-
+    %% small map
     1    = maps:get(a, #{ a=> 1}),
     2    = maps:get(b, #{ a=> 1, b => 2}),
     "hi" = maps:get("hello", #{ a=>1, "hello" => "hi"}),
     "tuple hi" = maps:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
 
-    M    = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
-    "v4" = maps:get(<<"k2">>, M#{ <<"k2">> => "v4" }),
+    M0    = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
+    "v4" = maps:get(<<"k2">>, M0#{<<"k2">> => "v4"}),
+
+    %% large map
+    M1   = maps:from_list([{I,I}||I<-lists:seq(1,100)] ++
+			  [{a,1},{b,2},{"hello","hi"},{{1,1.0},"tuple hi"},
+			   {k1,"v1"},{<<"k2">>,"v3"}]),
+    1    = maps:get(a, M1),
+    2    = maps:get(b, M1),
+    "hi" = maps:get("hello", M1),
+    "tuple hi" = maps:get({1,1.0}, M1),
+    "v3" = maps:get(<<"k2">>, M1),
 
     %% error case
     {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,[])),
     {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,<<>>)),
     {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})),
     {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{})),
-    {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{ b=>1, c=>2})),
+    {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{b=>1, c=>2})),
     ok.
 
 t_bif_map_find(Config) when is_list(Config) ->
-
+    %% small map
     {ok, 1}     = maps:find(a, #{ a=> 1}),
     {ok, 2}     = maps:find(b, #{ a=> 1, b => 2}),
     {ok, "int"} = maps:find(1, #{ 1   => "int"}),
@@ -756,8 +766,18 @@ t_bif_map_find(Config) when is_list(Config) ->
     {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}),
     {ok, "tuple hi"} = maps:find({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
 
-    M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
-    {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }),
+    M0 = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
+    {ok, "v4"} = maps:find(<<"k2">>, M0#{ <<"k2">> => "v4" }),
+
+    %% large map
+    M1   = maps:from_list([{I,I}||I<-lists:seq(1,100)] ++
+			  [{a,1},{b,2},{"hello","hi"},{{1,1.0},"tuple hi"},
+			   {k1,"v1"},{<<"k2">>,"v3"}]),
+    {ok, 1}    = maps:find(a, M1),
+    {ok, 2}    = maps:find(b, M1),
+    {ok, "hi"} = maps:find("hello", M1),
+    {ok, "tuple hi"} = maps:find({1,1.0}, M1),
+    {ok, "v3"} = maps:find(<<"k2">>, M1),
 
     %% error case
     error = maps:find(a,#{}),
@@ -766,7 +786,6 @@ t_bif_map_find(Config) when is_list(Config) ->
     error = maps:find(1, #{ 1.0  => "float"}),
     error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key
 
-
     {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id([]))),
     {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id(<<>>))),
     ok.
-- 
cgit v1.2.3


From bfd92c4a5905428cbdef0329877433bd64a5e96b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 24 Feb 2015 16:44:33 +0100
Subject: erts: Test building and removing maps

---
 erts/emulator/test/map_SUITE.erl | 60 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index c15cf76a26..176be6cd35 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -52,6 +52,9 @@
 	t_erlang_hash/1,
 	t_map_encode_decode/1,
 
+	%% non specific BIF related
+	t_bif_build_and_check/1,
+
 	%% maps module not bifs
 	t_maps_fold/1,
 	t_maps_map/1,
@@ -98,6 +101,9 @@ all() -> [
 	t_erlang_hash, t_map_encode_decode,
 	t_map_size,
 
+	%% non specific BIF related
+	t_bif_build_and_check,
+
 	%% maps module
 	t_maps_fold, t_maps_map,
 	t_maps_size, t_maps_without,
@@ -1200,6 +1206,60 @@ t_bif_map_from_list(Config) when is_list(Config) ->
     {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))),
     ok.
 
+t_bif_build_and_check(Config) when is_list(Config) ->
+    ok = check_build_and_remove(750,[
+				      fun(K) -> [K,K] end,
+				      fun(K) -> [float(K),K] end,
+				      fun(K) -> K end,
+				      fun(K) -> {1,K} end,
+				      fun(K) -> {K} end,
+				      fun(K) -> [K|K] end,
+				      fun(K) -> [K,1,2,3,4] end,
+				      fun(K) -> {K,atom} end,
+				      fun(K) -> float(K) end,
+				      fun(K) -> integer_to_list(K) end,
+				      fun(K) -> list_to_atom(integer_to_list(K)) end,
+				      fun(K) -> [K,{K,[K,{K,[K]}]}] end,
+				      fun(K) -> <> end
+			      ]),
+
+    ok.
+
+check_build_and_remove(_,[]) -> ok;
+check_build_and_remove(N,[F|Fs]) ->
+    {M,Ks} = build_and_check(N, maps:new(), F, []),
+    ok     = remove_and_check(Ks,M),
+    check_build_and_remove(N,Fs).
+
+build_and_check(0, M0, _, Ks) -> {M0, Ks};
+build_and_check(N, M0, F, Ks) ->
+    K  = build_key(F,N),
+    M1 = maps:put(K,K,M0),
+    ok = check_keys_exist([K|Ks], M1),
+    build_and_check(N-1,M1,F,[K|Ks]).
+
+remove_and_check([],_) -> ok;
+remove_and_check([K|Ks], M0) ->
+    K     = maps:get(K,M0),
+    true  = maps:is_key(K,M0),
+    M1    = maps:remove(K,M0),
+    false = maps:is_key(K,M1),
+    true  = maps:is_key(K,M0),
+    ok    = check_keys_exist(Ks,M1),
+    error = maps:find(K,M1),
+    remove_and_check(Ks, M1).
+
+build_key(F,N) when N rem 3 =:= 0 -> F(N);
+build_key(F,N) when N rem 3 =:= 1 -> K = F(N), {K,K};
+build_key(F,N) when N rem 3 =:= 2 -> K = F(N), [K,K].
+
+check_keys_exist([], _) -> ok;
+check_keys_exist([K|Ks],M) ->
+    K = maps:get(K,M),
+    check_keys_exist(Ks,M).
+
+
+
 %% Maps module, not BIFs
 t_maps_fold(_Config) ->
     Vs = lists:seq(1,100),
-- 
cgit v1.2.3


From 02c573ba7fee64cc65ec7c7ee286aa9ce5415546 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Wed, 25 Feb 2015 17:06:00 +0100
Subject: erts: Extend tests of maps:merge/2

---
 erts/emulator/test/map_SUITE.erl | 75 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 176be6cd35..17a05d9bfc 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -54,6 +54,7 @@
 
 	%% non specific BIF related
 	t_bif_build_and_check/1,
+	t_bif_merge_and_check/1,
 
 	%% maps module not bifs
 	t_maps_fold/1,
@@ -103,6 +104,7 @@ all() -> [
 
 	%% non specific BIF related
 	t_bif_build_and_check,
+	t_bif_merge_and_check,
 
 	%% maps module
 	t_maps_fold, t_maps_map,
@@ -1258,6 +1260,79 @@ check_keys_exist([K|Ks],M) ->
     K = maps:get(K,M),
     check_keys_exist(Ks,M).
 
+t_bif_merge_and_check(Config) when is_list(Config) ->
+    %% simple disjunct ones
+    %% make sure all keys are unique
+    Kss = [[a,b,c,d],
+	   [1,2,3,4],
+	   [],
+	   ["hi"],
+	   [e],
+	   [build_key(fun(K) -> {small,K} end, I) || I <- lists:seq(1,32)],
+	   lists:seq(5, 50),
+	   [build_key(fun(K) -> integer_to_list(K) end, I) || I <- lists:seq(2000,10000)],
+	   [build_key(fun(K) -> <> end, I) || I <- lists:seq(1,80)],
+	   [build_key(fun(K) -> {<>} end, I) || I <- lists:seq(100,1000)]],
+
+
+    KsMs = build_keys_map_pairs(Kss),
+    Cs   = [{CKs1,CM1,CKs2,CM2} || {CKs1,CM1} <- KsMs, {CKs2,CM2} <- KsMs],
+    ok   = merge_and_check_combo(Cs),
+
+    %% overlapping ones
+
+    KVs1 = [{a,1},{b,2},{c,3}],
+    KVs2 = [{b,3},{c,4},{d,5}],
+    KVs  = [{I,I} || I <- lists:seq(1,32)],
+    KVs3 = KVs1 ++ KVs,
+    KVs4 = KVs2 ++ KVs,
+
+    M1  = maps:from_list(KVs1),
+    M2  = maps:from_list(KVs2),
+    M3  = maps:from_list(KVs3),
+    M4  = maps:from_list(KVs4),
+
+    M12 = maps:merge(M1,M2),
+    ok  = check_key_values(KVs2 ++ [{a,1}], M12),
+    M21 = maps:merge(M2,M1),
+    ok  = check_key_values(KVs1 ++ [{d,5}], M21),
+
+    M34 = maps:merge(M3,M4),
+    ok  = check_key_values(KVs4 ++ [{a,1}], M34),
+    M43 = maps:merge(M4,M3),
+    ok  = check_key_values(KVs3 ++ [{d,5}], M43),
+
+    M14 = maps:merge(M1,M4),
+    ok  = check_key_values(KVs4 ++ [{a,1}], M14),
+    M41 = maps:merge(M4,M1),
+    ok  = check_key_values(KVs1 ++ [{d,5}] ++ KVs, M41),
+
+    ok.
+
+check_key_values([],_) -> ok;
+check_key_values([{K,V}|KVs],M) ->
+    V = maps:get(K,M),
+    check_key_values(KVs,M).
+
+merge_and_check_combo([]) -> ok;
+merge_and_check_combo([{Ks1,M1,Ks2,M2}|Cs]) ->
+    M12 = maps:merge(M1,M2),
+    ok  = check_keys_exist(Ks1 ++ Ks2, M12),
+    M21 = maps:merge(M2,M1),
+    ok  = check_keys_exist(Ks1 ++ Ks2, M21),
+
+    true = M12 =:= M21,
+    M12  = M21,
+
+    merge_and_check_combo(Cs).
+
+build_keys_map_pairs([]) -> [];
+build_keys_map_pairs([Ks|Kss]) ->
+    M  = maps:from_list(keys_to_pairs(Ks)),
+    ok = check_keys_exist(Ks, M),
+    [{Ks,M}|build_keys_map_pairs(Kss)].
+
+keys_to_pairs(Ks) -> [{K,K} || K <- Ks].
 
 
 %% Maps module, not BIFs
-- 
cgit v1.2.3


From f7ee6181417f05670e2e733085dd7f038d6f30f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 26 Feb 2015 14:45:38 +0100
Subject: erts: Handle hashmap in get_map_element(s) instructions

---
 erts/emulator/beam/beam_emu.c | 160 +++++++++++++++++++++++++-----------------
 erts/emulator/beam/erl_map.c  |   5 +-
 erts/emulator/beam/erl_map.h  |   3 +-
 3 files changed, 99 insertions(+), 69 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 034436e975..9a05e26859 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -2460,12 +2460,8 @@ do {								\
 
  OpCase(i_get_map_elements_fsI): {
     Eterm map;
-    map_t *mp;
-    Eterm field;
-    Eterm *ks;
-    Eterm *vs;
     BeamInstr *fs;
-    Uint sz,n;
+    Uint sz, n;
 
     GetArg1(1, map);
 
@@ -2473,36 +2469,56 @@ do {								\
      * i.e. that it follows a test is_map if needed.
      */
 
-    mp = (map_t *)map_val(map);
-    sz = map_get_size(mp);
-
-    if (sz == 0) {
-	SET_I((BeamInstr *) Arg(0));
-	goto get_map_elements_fail;
-    }
-
     n  = (Uint)Arg(2) / 2;
     fs = &Arg(3); /* pattern fields and target registers */
-    ks = map_get_keys(mp);
-    vs = map_get_values(mp);
 
-    while(sz) {
-	field = (Eterm)*fs;
-	if (EQ(field,*ks)) {
-	    PUT_TERM_REG(*vs, fs[1]);
-	    n--;
+    if (is_map(map)) {
+	map_t *mp;
+	Eterm *ks;
+	Eterm *vs;
+
+	mp = (map_t *)map_val(map);
+	sz = map_get_size(mp);
+
+	if (sz == 0) {
+	    SET_I((BeamInstr *) Arg(0));
+	    goto get_map_elements_fail;
+	}
+
+	ks = map_get_keys(mp);
+	vs = map_get_values(mp);
+
+	while(sz) {
+	    if (EQ((Eterm)*fs,*ks)) {
+		PUT_TERM_REG(*vs, fs[1]);
+		n--;
+		fs += 2;
+		/* no more values to fetch, we are done */
+		if (n == 0) break;
+	    }
+	    ks++; sz--;
+	    vs++;
+	}
+
+	if (n) {
+	    SET_I((BeamInstr *) Arg(0));
+	    goto get_map_elements_fail;
+	}
+    } else {
+	const Eterm *v;
+	Uint32 hx;
+	ASSERT(is_hashmap(map));
+	while(n--) {
+	    hx = hashmap_make_hash((Eterm)*fs);
+	    if ((v = erts_hashmap_get(hx,(Eterm)*fs, map)) == NULL) {
+		SET_I((BeamInstr *) Arg(0));
+		goto get_map_elements_fail;
+	    }
+	    PUT_TERM_REG(*v, fs[1]);
 	    fs += 2;
-	    /* no more values to fetch, we are done */
-	    if (n == 0) break;
 	}
-	ks++; sz--;
-	vs++;
     }
 
-    if (n) {
-	SET_I((BeamInstr *) Arg(0));
-	goto get_map_elements_fail;
-    }
 
     I += 4 + Arg(2);
 get_map_elements_fail:
@@ -6444,55 +6460,69 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free)
 
 static int has_not_map_field(Eterm map, Eterm key)
 {
-    map_t* mp;
-    Eterm* keys;
-    Uint i;
-    Uint n;
-
-    mp   = (map_t *)map_val(map);
-    keys = map_get_keys(mp);
-    n    = map_get_size(mp);
-    if (is_immed(key)) {
-	for (i = 0; i < n; i++) {
-	    if (keys[i] == key) {
-		return 0;
+    Uint32 hx;
+    if (is_map(map)) {
+	map_t* mp;
+	Eterm* keys;
+	Uint i;
+	Uint n;
+
+	mp   = (map_t *)map_val(map);
+	keys = map_get_keys(mp);
+	n    = map_get_size(mp);
+	if (is_immed(key)) {
+	    for (i = 0; i < n; i++) {
+		if (keys[i] == key) {
+		    return 0;
+		}
 	    }
-	}
-    } else {
-	for (i = 0; i <  n; i++) {
-	    if (EQ(keys[i], key)) {
-		return 0;
+	} else {
+	    for (i = 0; i <  n; i++) {
+		if (EQ(keys[i], key)) {
+		    return 0;
+		}
 	    }
 	}
+	return 1;
     }
-    return 1;
+    ASSERT(is_hashmap(map));
+    hx = hashmap_make_hash(key);
+    return erts_hashmap_get(hx,key,map) ? 0 : 1;
 }
 
 static Eterm get_map_element(Eterm map, Eterm key)
 {
-    map_t *mp;
-    Eterm* ks, *vs;
-    Uint i;
-    Uint n;
-
-    mp = (map_t *)map_val(map);
-    ks = map_get_keys(mp);
-    vs = map_get_values(mp);
-    n  = map_get_size(mp);
-    if (is_immed(key)) {
-	for (i = 0; i < n; i++) {
-	    if (ks[i] == key) {
-		return vs[i];
+    Uint32 hx;
+    const Eterm *vs;
+    if (is_map(map)) {
+	map_t *mp;
+	Eterm *ks;
+	Uint i;
+	Uint n;
+
+	mp = (map_t *)map_val(map);
+	ks = map_get_keys(mp);
+	vs = map_get_values(mp);
+	n  = map_get_size(mp);
+	if (is_immed(key)) {
+	    for (i = 0; i < n; i++) {
+		if (ks[i] == key) {
+		    return vs[i];
+		}
 	    }
-	}
-    } else {
-	for (i = 0; i < n; i++) {
-	    if (EQ(ks[i], key)) {
-		return vs[i];
+	} else {
+	    for (i = 0; i < n; i++) {
+		if (EQ(ks[i], key)) {
+		    return vs[i];
+		}
 	    }
 	}
+	return THE_NON_VALUE;
     }
-    return THE_NON_VALUE;
+    ASSERT(is_hashmap(map));
+    hx = hashmap_make_hash(key);
+    vs = erts_hashmap_get(hx,key,map);
+    return vs ? *vs : THE_NON_VALUE;
 }
 
 #define GET_TERM(term, dest)					\
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 4242807933..2be4c9a730 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -77,7 +77,6 @@ typedef struct {
     Eterm  val;
 } hxnode_t;
 
-static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node);
 static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node, int is_update);
 static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args);
@@ -199,7 +198,7 @@ erts_maps_get(Eterm key, Eterm map)
     ASSERT(is_hashmap(map));
     hx = hashmap_make_hash(key);
 
-    return hashmap_get(hx, key, map);
+    return erts_hashmap_get(hx, key, map);
 }
 
 BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
@@ -1660,7 +1659,7 @@ Eterm* hashmap_iterator_next(ErtsWStack* s) {
     }
 }
 
-static const Eterm *hashmap_get(Uint32 hx, Eterm key, Eterm node) {
+const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) {
     Eterm *ptr, hdr;
     Eterm th[2];
     Uint ix,slot, lvl = 0;
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 7fddc9c240..7175e653d9 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -92,8 +92,9 @@ int   erts_validate_and_sort_map(map_t* map);
 void  hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
 int   hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
-Eterm erts_hashmap_get(Eterm key, Eterm map);
 Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n);
+Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm val, Eterm node, int is_update);
+const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map);
 
 #if HALFWORD_HEAP
 const Eterm *
-- 
cgit v1.2.3


From 6507ec20f00acac025252bf7b7609eee1f593a84 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 26 Feb 2015 16:53:23 +0100
Subject: erts: Make ESTACK usable through pointer

---
 erts/emulator/beam/global.h | 92 +++++++++++++++++++++++----------------------
 erts/emulator/beam/utils.c  |  4 +-
 2 files changed, 49 insertions(+), 47 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index ad7e6fb89d..09bcf71a28 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -376,12 +376,13 @@ typedef struct ErtsEStack_ {
     Eterm* start;
     Eterm* sp;
     Eterm* end;
+    Eterm* edefault;
     ErtsAlcType_t alloc_type;
 }ErtsEStack;
 
 #define DEF_ESTACK_SIZE (16)
 
-void erl_grow_estack(ErtsEStack*, Eterm* def_stack, Uint need);
+void erl_grow_estack(ErtsEStack*, Uint need);
 #define ESTK_CONCAT(a,b) a##b
 #define ESTK_DEF_STACK(s) ESTK_CONCAT(s,_default_estack)
 
@@ -391,22 +392,23 @@ void erl_grow_estack(ErtsEStack*, Eterm* def_stack, Uint need);
         ESTK_DEF_STACK(s),  /* start */ 		\
         ESTK_DEF_STACK(s),  /* sp */			\
         ESTK_DEF_STACK(s) + DEF_ESTACK_SIZE, /* end */	\
+        ESTK_DEF_STACK(s),  /* default */		\
         ERTS_ALC_T_ESTACK /* alloc_type */		\
     }
 
 #define ESTACK_CHANGE_ALLOCATOR(s,t)					\
 do {									\
-    if (s.start != ESTK_DEF_STACK(s)) {					\
+    if ((s).start != ESTK_DEF_STACK(s)) {				\
 	erl_exit(1, "Internal error - trying to change allocator "	\
 		 "type of active estack\n");				\
     }									\
-    s.alloc_type = (t);							\
+    (s).alloc_type = (t);						\
  } while (0)
 
 #define DESTROY_ESTACK(s)				\
 do {							\
-    if (s.start != ESTK_DEF_STACK(s)) {			\
-	erts_free(s.alloc_type, s.start); 		\
+    if ((s).start != ESTK_DEF_STACK(s)) {		\
+	erts_free((s).alloc_type, (s).start); 		\
     }							\
 } while(0)
 
@@ -417,16 +419,16 @@ do {							\
  */
 #define ESTACK_SAVE(s,dst)\
 do {\
-    if (s.start == ESTK_DEF_STACK(s)) {\
+    if ((s).start == ESTK_DEF_STACK(s)) {\
 	UWord _wsz = ESTACK_COUNT(s);\
-	(dst)->start = erts_alloc(s.alloc_type,\
+	(dst)->start = erts_alloc((s).alloc_type,\
 				  DEF_ESTACK_SIZE * sizeof(Eterm));\
-	memcpy((dst)->start, s.start,_wsz*sizeof(Eterm));\
+	memcpy((dst)->start, (s).start,_wsz*sizeof(Eterm));\
 	(dst)->sp = (dst)->start + _wsz;\
 	(dst)->end = (dst)->start + DEF_ESTACK_SIZE;\
-	(dst)->alloc_type = s.alloc_type;\
+	(dst)->alloc_type = (s).alloc_type;\
     } else\
-        *(dst) = s;\
+        *(dst) = (s);\
  } while (0)
 
 #define DESTROY_SAVED_ESTACK(estack)\
@@ -445,70 +447,70 @@ do {\
  */
 #define ESTACK_RESTORE(s, src)			\
 do {						\
-    ASSERT(s.start == ESTK_DEF_STACK(s));	\
-    s = *(src);  /* struct copy */		\
+    ASSERT((s).start == ESTK_DEF_STACK(s));	\
+    (s) = *(src);  /* struct copy */		\
     (src)->start = NULL;			\
-    ASSERT(s.sp >= s.start);			\
-    ASSERT(s.sp <= s.end);			\
+    ASSERT((s).sp >= (s).start);		\
+    ASSERT((s).sp <= (s).end);			\
 } while (0)
 
-#define ESTACK_IS_STATIC(s) (s.start == ESTK_DEF_STACK(s)))
+#define ESTACK_IS_STATIC(s) ((s).start == ESTK_DEF_STACK(s))
 
-#define ESTACK_PUSH(s, x)				\
-do {							\
-    if (s.sp == s.end) {				\
-	erl_grow_estack(&s, ESTK_DEF_STACK(s), 1); 	\
-    }							\
-    *s.sp++ = (x);					\
+#define ESTACK_PUSH(s, x)			\
+do {						\
+    if ((s).sp == (s).end) {			\
+	erl_grow_estack(&(s), 1); 		\
+    }						\
+    *(s).sp++ = (x);				\
 } while(0)
 
 #define ESTACK_PUSH2(s, x, y)			\
 do {						\
-    if (s.sp > s.end - 2) {			\
-	erl_grow_estack(&s, ESTK_DEF_STACK(s), 2); \
+    if ((s).sp > (s).end - 2) {			\
+	erl_grow_estack(&(s), 2);		\
     }						\
-    *s.sp++ = (x);				\
-    *s.sp++ = (y);				\
+    *(s).sp++ = (x);				\
+    *(s).sp++ = (y);				\
 } while(0)
 
 #define ESTACK_PUSH3(s, x, y, z)		\
 do {						\
-    if (s.sp > s.end - 3) {			\
-	erl_grow_estack(&s, ESTK_DEF_STACK(s), 3); \
+    if ((s).sp > (s).end - 3) {			\
+	erl_grow_estack(&s, 3); 		\
     }						\
-    *s.sp++ = (x);				\
-    *s.sp++ = (y);				\
-    *s.sp++ = (z);				\
+    *(s).sp++ = (x);				\
+    *(s).sp++ = (y);				\
+    *(s).sp++ = (z);				\
 } while(0)
 
 #define ESTACK_PUSH4(s, E1, E2, E3, E4)		\
 do {						\
-    if (s.sp > s.end - 4) {			\
-	erl_grow_estack(&s, ESTK_DEF_STACK(s)); \
+    if ((s).sp > (s).end - 4) {			\
+	erl_grow_estack(&s, 4);                 \
     }						\
-    *s.sp++ = (E1);				\
-    *s.sp++ = (E2);				\
-    *s.sp++ = (E3);				\
-    *s.sp++ = (E4);				\
+    *(s).sp++ = (E1);				\
+    *(s).sp++ = (E2);				\
+    *(s).sp++ = (E3);				\
+    *(s).sp++ = (E4);				\
 } while(0)
 
-#define ESTACK_RESERVE(s, push_cnt)                         \
-do {						            \
-    if (s.sp > s.end - (push_cnt)) {	                    \
-	erl_grow_estack(&s, ESTK_DEF_STACK(s), (push_cnt)); \
-    }						            \
+#define ESTACK_RESERVE(s, push_cnt)             \
+do {					        \
+    if ((s).sp > (s).end - (push_cnt)) {	\
+	erl_grow_estack(&(s), (push_cnt));	\
+    }					        \
 } while(0)
 
 /* Must be preceded by ESTACK_RESERVE */
 #define ESTACK_FAST_PUSH(s, x)				\
 do {							\
-    ASSERT(s.sp < s.end);                               \
+    ASSERT((s).sp < (s).end);                           \
     *s.sp++ = (x);					\
 } while(0)
 
-#define ESTACK_COUNT(s) (s.sp - s.start)
-#define ESTACK_ISEMPTY(s) (s.sp == s.start)
-#define ESTACK_POP(s) (*(--s.sp))
+#define ESTACK_COUNT(s) ((s).sp - (s).start)
+#define ESTACK_ISEMPTY(s) ((s).sp == (s).start)
+#define ESTACK_POP(s) (*(--(s).sp))
 
 
 /*
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index d234e8fc68..c8a21adf17 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -190,7 +190,7 @@ erts_set_hole_marker(Eterm* ptr, Uint sz)
  * Helper function for the ESTACK macros defined in global.h.
  */
 void
-erl_grow_estack(ErtsEStack* s, Eterm* default_estack, Uint need)
+erl_grow_estack(ErtsEStack* s, Uint need)
 {
     Uint old_size = (s->end - s->start);
     Uint new_size;
@@ -201,7 +201,7 @@ erl_grow_estack(ErtsEStack* s, Eterm* default_estack, Uint need)
     else
 	new_size = ((need / old_size) + 2) * old_size;
 
-    if (s->start != default_estack) {
+    if (s->start != s->edefault) {
 	s->start = erts_realloc(s->alloc_type, s->start,
 				new_size*sizeof(Eterm));
     } else {
-- 
cgit v1.2.3


From 9f3f98bb7e75a00cc9eb4fc32b7839fa6fbedd3d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 26 Feb 2015 17:06:52 +0100
Subject: erts: Update build_and_check maps testcase

---
 erts/emulator/test/map_SUITE.erl | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 17a05d9bfc..1962ce6a57 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -1238,6 +1238,8 @@ build_and_check(N, M0, F, Ks) ->
     K  = build_key(F,N),
     M1 = maps:put(K,K,M0),
     ok = check_keys_exist([K|Ks], M1),
+    M2 = maps:update(K,v,M1),
+    v  = maps:get(K,M2),
     build_and_check(N-1,M1,F,[K|Ks]).
 
 remove_and_check([],_) -> ok;
-- 
cgit v1.2.3


From cdfa304b1da0eb9fb270fe78ee6acfc0dad864fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 26 Feb 2015 17:10:40 +0100
Subject: erts: Split hashmap_insert to down and up

---
 erts/emulator/beam/erl_map.c | 113 +++++++++++++++++++++++++------------------
 erts/emulator/beam/erl_map.h |  27 +++++++----
 2 files changed, 85 insertions(+), 55 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 2be4c9a730..4ad33f98a7 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -77,7 +77,7 @@ typedef struct {
     Eterm  val;
 } hxnode_t;
 
-static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node, int is_update);
+
 static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args);
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
@@ -1391,7 +1391,7 @@ found_key:
 
     ASSERT(is_hashmap(map));
     hx = hashmap_make_hash(key);
-    *res = hashmap_insert(p, hx, key, value, map, 1);
+    *res = erts_hashmap_insert(p, hx, key, value, map, 1);
     if (is_value(*res))
 	return 1;
 
@@ -1545,7 +1545,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
     ASSERT(is_hashmap(map));
 
     hx  = hashmap_make_hash(key);
-    res = hashmap_insert(p, hx, key, value, map, 0);
+    res = erts_hashmap_insert(p, hx, key, value, map, 0);
     ASSERT(is_hashmap(res));
 
     return res;
@@ -1730,16 +1730,34 @@ const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) {
     return NULL;
 }
 
-static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
-			    Eterm node, int is_update) {
-    Eterm *hp = NULL, *nhp = NULL;
+Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
+			  Eterm map, int is_update) {
+    Uint size, upsz;
+    Eterm *hp, res = THE_NON_VALUE;
+    DECLARE_ESTACK(stack);
+    if (erts_hashmap_insert_down(hx, key, map, &size, &upsz, &stack, is_update)) {
+	hp  = HAlloc(p, size);
+	res = erts_hashmap_insert_up(hp, key, value, &upsz, &stack);
+    }
+
+    DESTROY_ESTACK(stack);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+    ERTS_HOLE_CHECK(p);
+
+    return res;
+}
+
+
+int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz,
+			     Uint *update_size, ErtsEStack *sp, int is_update) {
     Eterm *ptr;
-    Eterm hdr,res,ckey,fake;
+    Eterm hdr, ckey;
     Eterm th[2];
     Uint32 ix, cix, bp, hval, chx;
     Uint slot, lvl = 0, clvl;
-    Uint size = 0, n = 0, update_size = 1;
-    DECLARE_ESTACK(stack);
+    Uint size = 0, n = 0;
+
+    *update_size = 1;
 
     for (;;) {
 	switch(primary_tag(node)) {
@@ -1747,12 +1765,11 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
 		ptr  = list_val(node);
 		ckey = CAR(ptr);
 		if (EQ(ckey, key)) {
-		    update_size = 0;
+		    *update_size = 0;
 		    goto unroll;
 		}
 		if (is_update) {
-		    res = THE_NON_VALUE;
-		    goto bail_out;
+		    return 0;
 		}
 		goto insert_subnodes;
 	    case TAG_PRIMARY_BOXED:
@@ -1765,14 +1782,14 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
 			ix    = hashmap_index(hx);
 			hx    = hashmap_shift_hash(th,hx,lvl,key);
 			size += HAMT_NODE_ARRAY_SZ;
-			ESTACK_PUSH2(stack, ix, node);
+			ESTACK_PUSH2(*sp, ix, node);
 			node  = ptr[ix+1];
 			break;
 		    case HAMT_SUBTAG_HEAD_ARRAY:
 			ix    = hashmap_index(hx);
 			hx    = hashmap_shift_hash(th,hx,lvl,key);
 			size += HAMT_HEAD_ARRAY_SZ;
-			ESTACK_PUSH2(stack, ix, node);
+			ESTACK_PUSH2(*sp, ix, node);
 			node  = ptr[ix+2];
 			break;
 		    case HAMT_SUBTAG_NODE_BITMAP:
@@ -1782,8 +1799,8 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
 			slot = hashmap_bitcount(hval & (bp - 1));
 			n    = hashmap_bitcount(hval);
 
-			ESTACK_PUSH(stack, n);
-			ESTACK_PUSH3(stack, bp, slot, node);
+			ESTACK_PUSH(*sp, n);
+			ESTACK_PUSH3(*sp, bp, slot, node);
 
 			/* occupied */
 			if (bp & hval) {
@@ -1795,8 +1812,7 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
 			}
 			/* not occupied */
 			if (is_update) {
-			    res = THE_NON_VALUE;
-			    goto bail_out;
+			    return 0;
 			}
 			size += HAMT_NODE_BITMAP_SZ(n+1);
 			goto unroll;
@@ -1807,8 +1823,8 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
 			slot = hashmap_bitcount(hval & (bp - 1));
 			n    = hashmap_bitcount(hval);
 
-			ESTACK_PUSH(stack, n);
-			ESTACK_PUSH3(stack, bp, slot, node);
+			ESTACK_PUSH(*sp, n);
+			ESTACK_PUSH3(*sp, bp, slot, node);
 
 			/* occupied */
 			if (bp & hval) {
@@ -1820,8 +1836,7 @@ static Eterm hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
 			}
 			/* not occupied */
 			if (is_update) {
-			    res = THE_NON_VALUE;
-			    goto bail_out;
+			    return 0;
 			}
 			size += HAMT_HEAD_BITMAP_SZ(n+1);
 			goto unroll;
@@ -1843,27 +1858,37 @@ insert_subnodes:
     cix   = hashmap_index(chx);
 
     while (cix == ix) {
-	ESTACK_PUSH(stack, 0);
-	ESTACK_PUSH3(stack, 1 << ix, 0, MAP_HEADER_HAMT_NODE_BITMAP(0));
+	ESTACK_PUSH(*sp, 0);
+	ESTACK_PUSH3(*sp, 1 << ix, 0, MAP_HEADER_HAMT_NODE_BITMAP(0));
 	size += HAMT_NODE_BITMAP_SZ(1);
 	hx    = hashmap_shift_hash(th,hx,lvl,key);
 	chx   = hashmap_shift_hash(th,chx,clvl,ckey);
 	ix    = hashmap_index(hx);
 	cix   = hashmap_index(chx);
     }
-    ESTACK_PUSH3(stack, cix, ix, node);
+    ESTACK_PUSH3(*sp, cix, ix, node);
 
 unroll:
-    size += 2;
-    hp  = HAlloc(p, size);
+    *sz = size + /* res cons */ 2;
+    return 1;
+}
+
+Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value,
+			     Uint *update_size, ErtsEStack *sp) {
+    Eterm node, fake, *ptr, hdr;
+    Eterm res;
+    Eterm *nhp = NULL;
+    Uint32 ix, cix, bp, hval;
+    Uint slot, n;
+
     res = CONS(hp, key, value); hp += 2;
 
     do {
-	node = ESTACK_POP(stack);
+	node = ESTACK_POP(*sp);
 	switch(primary_tag(node)) {
 	    case TAG_PRIMARY_LIST:
-		ix   = (Uint32) ESTACK_POP(stack);
-		cix  = (Uint32) ESTACK_POP(stack);
+		ix  = (Uint32) ESTACK_POP(*sp);
+		cix = (Uint32) ESTACK_POP(*sp);
 
 		nhp   = hp;
 		*hp++ = MAP_HEADER_HAMT_NODE_BITMAP((1 << ix) | (1 << cix));
@@ -1887,7 +1912,7 @@ unroll:
 
 		switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
 		    case HAMT_SUBTAG_NODE_ARRAY:
-			slot  = (Uint)   ESTACK_POP(stack);
+			slot  = (Uint) ESTACK_POP(*sp);
 			nhp   = hp;
 			n     = HAMT_NODE_ARRAY_SZ;
 			while(n--) { *hp++ = *ptr++; }
@@ -1895,19 +1920,19 @@ unroll:
 			res = make_hashmap(nhp);
 			break;
 		    case HAMT_SUBTAG_HEAD_ARRAY:
-			slot  = (Uint)   ESTACK_POP(stack);
+			slot  = (Uint) ESTACK_POP(*sp);
 			nhp   = hp;
 			n     = HAMT_HEAD_ARRAY_SZ - 2;
 			*hp++ = MAP_HEADER_HAMT_HEAD_ARRAY; ptr++;
-			*hp++ = (*ptr++) + update_size;
+			*hp++ = (*ptr++) + *update_size;
 			while(n--) { *hp++ = *ptr++; }
 			nhp[slot+2] = res;
 			res = make_hashmap(nhp);
 			break;
 		    case HAMT_SUBTAG_NODE_BITMAP:
-			slot  = (Uint)   ESTACK_POP(stack);
-			bp    = (Uint32) ESTACK_POP(stack);
-			n     = (Uint32) ESTACK_POP(stack);
+			slot  = (Uint)   ESTACK_POP(*sp);
+			bp    = (Uint32) ESTACK_POP(*sp);
+			n     = (Uint32) ESTACK_POP(*sp);
 			hval  = MAP_HEADER_VAL(hdr);
 			nhp   = hp;
 			*hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval | bp); ptr++;
@@ -1924,13 +1949,13 @@ unroll:
 			res = make_hashmap(nhp);
 			break;
 		    case HAMT_SUBTAG_HEAD_BITMAP:
-			slot  = (Uint)   ESTACK_POP(stack);
-			bp    = (Uint32) ESTACK_POP(stack);
-			n     = (Uint32) ESTACK_POP(stack);
+			slot  = (Uint)   ESTACK_POP(*sp);
+			bp    = (Uint32) ESTACK_POP(*sp);
+			n     = (Uint32) ESTACK_POP(*sp);
 			hval  = MAP_HEADER_VAL(hdr);
 			nhp   = hp;
 			*hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(hval | bp); ptr++;
-			*hp++ = (*ptr++) + update_size;
+			*hp++ = (*ptr++) + *update_size;
 
 			n -= slot;
 			while(slot--) { *hp++ = *ptr++; }
@@ -1953,13 +1978,9 @@ unroll:
 		break;
 	}
 
-    } while(!ESTACK_ISEMPTY(stack));
+    } while(!ESTACK_ISEMPTY(*sp));
 
-bail_out:
-    DESTROY_ESTACK(stack);
-    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
-    ERTS_HOLE_CHECK(p);
-    return res;
+   return res;
 }
 
 static Eterm hashmap_keys(Process* p, Eterm node) {
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 7175e653d9..2d5c5c958c 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -85,16 +85,25 @@ typedef struct map_s {
 #define MAP_HEADER_SIZE        (sizeof(map_t) / sizeof(Eterm))
 
 struct ErtsWStack_;
-Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map);
-int   erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res);
-int   erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res);
-int   erts_validate_and_sort_map(map_t* map);
-void  hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
+struct ErtsEStack_;
+
+Eterm  erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map);
+int    erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res);
+int    erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res);
+
+Eterm  erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
+			   Eterm node, int is_update);
+int    erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz,
+			        Uint *upsz, struct ErtsEStack_ *sp, int is_update);
+Eterm  erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value,
+			      Uint *upsz, struct ErtsEStack_ *sp);
+
+int    erts_validate_and_sort_map(map_t* map);
+void   hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
-int   hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
-Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n);
-Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm val, Eterm node, int is_update);
-const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map);
+int    hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
+Eterm  erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n);
+const  Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map);
 
 #if HALFWORD_HEAP
 const Eterm *
-- 
cgit v1.2.3


From dea2faefbbea2b2e80a43600f47833d47a208b32 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Fri, 27 Feb 2015 14:51:57 +0100
Subject: erts: Handle hashmap in update/assoc instructions

---
 erts/emulator/beam/beam_emu.c | 79 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 71 insertions(+), 8 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 9a05e26859..1166b32a3b 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -6598,11 +6598,43 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
     Eterm new_key;
     Eterm* kp;
 
+    new_p = &Arg(5);
+    num_updates = Arg(4) / 2;
+
     if (is_not_map(map)) {
-	return THE_NON_VALUE;
+	Uint32 hx;
+	Eterm val;
+
+	/* apparently the compiler does not emit is_map instructions,
+	 * bad compiler */
+
+	if (is_not_hashmap(map))
+	    return THE_NON_VALUE;
+
+	res = map;
+	E = p->stop;
+	while(num_updates--) {
+	    /* assoc can't fail */
+	    GET_TERM(new_p[0], new_key);
+	    GET_TERM(new_p[1], val);
+	    hx = hashmap_make_hash(new_key);
+
+	    res = erts_hashmap_insert(p, hx, new_key, val, res,  0);
+	    if (p->mbuf) {
+		Uint live = Arg(3);
+		reg[live] = res;
+		erts_garbage_collect(p, 0, reg, live+1);
+		res       = reg[live];
+	    }
+
+	    E = p->stop;
+
+	    new_p += 2;
+	}
+	return res;
     }
 
-    old_mp = (map_t *) map_val(map);
+    old_mp  = (map_t *) map_val(map);
     num_old = map_get_size(old_mp);
 
     /*
@@ -6618,7 +6650,6 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
      * update list are new).
      */
 
-    num_updates = Arg(4) / 2;
     need = 2*(num_old+num_updates) + 1 + MAP_HEADER_SIZE;
     if (HeapWordsLeft(p) < need) {
 	Uint live = Arg(3);
@@ -6665,7 +6696,6 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
     old_vals = map_get_values(old_mp);
     old_keys = map_get_keys(old_mp);
 
-    new_p = &Arg(5);
     GET_TERM(*new_p, new_key);
     n = num_updates;
 
@@ -6776,8 +6806,44 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
     BeamInstr* new_p;
     Eterm new_key;
 
+    new_p = &Arg(5);
+    n = Arg(4) / 2;		/* Number of values to be updated */
+    ASSERT(n > 0);
+
     if (is_not_map(map)) {
-	return THE_NON_VALUE;
+	Uint32 hx;
+	Eterm val;
+
+	/* apparently the compiler does not emit is_map instructions,
+	 * bad compiler */
+
+	if (is_not_hashmap(map))
+	    return THE_NON_VALUE;
+
+	res = map;
+	E = p->stop;
+	while(n--) {
+	    /* assoc can't fail */
+	    GET_TERM(new_p[0], new_key);
+	    GET_TERM(new_p[1], val);
+	    hx = hashmap_make_hash(new_key);
+
+	    res = erts_hashmap_insert(p, hx, new_key, val, res,  1);
+	    if (is_non_value(res))
+		return res;
+
+	    if (p->mbuf) {
+		Uint live = Arg(3);
+		reg[live] = res;
+		erts_garbage_collect(p, 0, reg, live+1);
+		res       = reg[live];
+	    }
+
+	    E = p->stop;
+
+	    new_p += 2;
+	}
+	return res;
     }
 
     old_mp = (map_t *) map_val(map);
@@ -6822,12 +6888,9 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
     mp->keys = old_mp->keys;
 
     /* Get array of key/value pairs to be updated */
-    new_p = &Arg(5);
     GET_TERM(*new_p, new_key);
 
     /* Update all values */
-    n = Arg(4) / 2;		/* Number of values to be updated */
-    ASSERT(n > 0);
     for (i = 0; i < num_old; i++) {
 	if (!EQ(*old_keys, new_key)) {
 	    /* Not same keys */
-- 
cgit v1.2.3


From 861a865cea33e0f8d84148dfbd64ab00beb1a54a Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Mon, 2 Mar 2015 15:46:37 +0100
Subject: erts: Add make_internal_hash

---
 erts/emulator/beam/erl_db_hash.c |   2 +-
 erts/emulator/beam/erl_map.h     |   2 +-
 erts/emulator/beam/erl_utils.h   |   1 +
 erts/emulator/beam/utils.c       | 394 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 393 insertions(+), 6 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index c2157457a0..6cb2ecac15 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -174,7 +174,7 @@ static ERTS_INLINE void add_fixed_deletion(DbTableHash* tb, int ix)
 /* optimised version of make_hash (normal case? atomic key) */
 #define MAKE_HASH(term) \
     ((is_atom(term) ? (atom_tab(atom_val(term))->slot.bucket.hvalue) : \
-      make_hash2(term)) % MAX_HASH)
+      make_internal_hash(term)) % MAX_HASH)
 
 #ifdef ERTS_SMP
 #  define DB_HASH_LOCK_MASK (DB_HASH_LOCK_CNT-1)
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 2d5c5c958c..3544189936 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -57,7 +57,7 @@ typedef struct map_s {
 
 #define hashmap_size(x) (((hashmap_head_t*) hashmap_val(x))->size)
 #define hashmap_size_rel(RTERM, BASE) hashmap_size(rterm2wterm(RTERM, BASE))
-#define hashmap_make_hash(Key) make_hash2(Key)
+#define hashmap_make_hash(Key) make_internal_hash(Key)
 
 #define hashmap_restore_hash(Heap,Lvl,Key) \
     (((Lvl) < 8) ? hashmap_make_hash(Key) >> (4*(Lvl)) : hashmap_make_hash(CONS(Heap, make_small((Lvl)>>3), (Key))) >> (4*((Lvl) & 7)))
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index c32f8fd61c..c772a750f1 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -119,6 +119,7 @@ Uint32 make_broken_hash(Eterm);
 Uint32 block_hash(byte *, unsigned, Uint32);
 Uint32 make_hash2(Eterm);
 Uint32 make_hash(Eterm);
+Uint32 make_internal_hash(Eterm);
 
 void erts_save_emu_args(int argc, char **argv);
 Eterm erts_get_emu_args(struct process *c_p);
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index c8a21adf17..67331a37f6 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1154,6 +1154,11 @@ make_hash2(Eterm term)
 #define HCONST_14 0xa708a81eUL
 #define HCONST_15 0x454021d7UL
 #define HCONST_16 0xe3779b90UL
+#define HCONST_17 0x81af1549UL
+#define HCONST_18 0x1fe68f02UL
+#define HCONST_19 0xbe1e08bbUL
+#define HCONST_20 0x5c558274UL
+#define HCONST_21 0xfa8cfc2dUL
 
 #define HASH_MAP_TAIL (_make_header(1,_TAG_HEADER_REF))
 #define HASH_MAP_PAIR (_make_header(2,_TAG_HEADER_REF))
@@ -1180,6 +1185,13 @@ make_hash2(Eterm term)
 	} while(0)
 
 #define IS_SSMALL28(x) (((Uint) (((x) >> (28-1)) + 1)) < 2)
+
+#ifdef ARCH_64
+#  define POINTER_HASH(Ptr, AConst) UINT32_HASH_2((Uint32)(UWord)(Ptr), (((UWord)(Ptr)) >> 32), AConst)
+#else
+#  define POINTER_HASH(Ptr, AConst) UINT32_HASH(Ptr, Const)
+#endif
+
     /* Optimization. Simple cases before declaration of estack. */
     if (primary_tag(term) == TAG_PRIMARY_IMMED1) {
 	switch (term & _TAG_IMMED1_MASK) {
@@ -1339,7 +1351,6 @@ make_hash2(Eterm term)
 	    case EXPORT_SUBTAG:
 	    {
 		Export* ep = *((Export **) (export_val(term) + 1));
-
 		UINT32_HASH_2
 		    (ep->code[2], 
 		     atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue,
@@ -1354,7 +1365,6 @@ make_hash2(Eterm term)
 	    {
 		ErlFunThing* funp = (ErlFunThing *) fun_val(term);
 		Uint num_free = funp->num_free;
-
 		UINT32_HASH_2
 		    (num_free, 
 		     atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue,
@@ -1558,10 +1568,386 @@ make_hash2(Eterm term)
 	}
     }
     }
+}
+
+/* Term hash function for internal use.
+ * Is not "portable" in any way betweem different VM instances.
+ *
+ * One IMPORTANT property must hold (for hamt).
+ * EVERY BIT of the term that is significant for equality (see EQ)
+ * must be used as input for the hash. Two different terms must always have a
+ * chance of hashing different when salted: h([Salt|A]) vs h([Salt|B]).
+ *
+ * This is why we can not use cached hash values for atoms for example.
+ */
+
+#define CONST_HASH(AConst)                              \
+do {  /* Lightweight mixing of constant (type info) */  \
+    hash ^= AConst;                                     \
+    hash = (hash << 17) ^ (hash >> (32-17));            \
+} while (0)
+
+Uint32
+make_internal_hash(Eterm term)
+{
+    Uint32 hash;
+    Uint32 hash_xor_pairs;
+
+    ERTS_UNDEF(hash_xor_pairs, 0);
+
+    /* Optimization. Simple cases before declaration of estack. */
+    if (primary_tag(term) == TAG_PRIMARY_IMMED1) {
+        hash = 0;
+    #if ERTS_SIZEOF_ETERM == 8
+        UINT32_HASH_2((Uint32)term, (Uint32)(term >> 32), HCONST);
+    #elif ERTS_SIZEOF_ETERM == 4
+        UINT32_HASH(term, HCONST);
+    #else
+    #  error "No you don't"
+    #endif
+        return hash;
+    }
+    {
+    Eterm tmp;
+    DECLARE_ESTACK(s);
+
+    UseTmpHeapNoproc(2);
+    hash = 0;
+    for (;;) {
+	switch (primary_tag(term)) {
+	case TAG_PRIMARY_LIST:
+	{
+	    int c = 0;
+	    Uint32 sh = 0;
+	    Eterm* ptr = list_val(term);
+	    while (is_byte(*ptr)) {
+		/* Optimization for strings. */
+		sh = (sh << 8) + unsigned_val(*ptr);
+		if (c == 3) {
+		    UINT32_HASH(sh, HCONST_4);
+		    c = sh = 0;
+		} else {
+		    c++;
+		}
+		term = CDR(ptr);
+		if (is_not_list(term))
+		    break;
+		ptr = list_val(term);
+	    }
+	    if (c > 0)
+		UINT32_HASH(sh, HCONST_4);
+	    if (is_list(term)) {
+		tmp = CDR(ptr);
+                CONST_HASH(HCONST_17);  /* Hash CAR in cons cell */
+                ESTACK_PUSH(s, tmp);
+                if (is_not_list(tmp)) {
+                    ESTACK_PUSH(s, HASH_CDR);
+                }
+		term = CAR(ptr);
+	    }
+	}
+	break;
+	case TAG_PRIMARY_BOXED:
+	{
+	    Eterm hdr = *boxed_val(term);
+	    ASSERT(is_header(hdr));
+	    switch (hdr & _TAG_HEADER_MASK) {
+	    case ARITYVAL_SUBTAG:
+	    {
+		int i;
+		int arity = header_arity(hdr);
+		Eterm* elem = tuple_val(term);
+		UINT32_HASH(arity, HCONST_9);
+		if (arity == 0) /* Empty tuple */
+		    goto pop_next;
+		for (i = arity; ; i--) {
+		    term = elem[i];
+                    if (i == 1)
+                        break;
+                    ESTACK_PUSH(s, term);
+		}
+	    }
+	    break;
+	    case MAP_SUBTAG:
+	    {
+		map_t *mp = (map_t *)map_val(term);
+		int i;
+		int size  = map_get_size(mp);
+		Eterm *ks = map_get_keys(mp);
+		Eterm *vs = map_get_values(mp);
+		UINT32_HASH(size, HCONST_16);
+		if (size == 0) {
+		    goto pop_next;
+		}
+		/* We want a hash function that is *independent* of
+		 * the order in which keys and values are encountered.
+		 * We therefore calculate context independent hashes for all    				      .
+		 * key-value pairs and then xor them together.
+		 */
+		ESTACK_PUSH(s, hash_xor_pairs);
+		ESTACK_PUSH(s, hash);
+		ESTACK_PUSH(s, HASH_MAP_TAIL);
+		hash = 0;
+		hash_xor_pairs = 0;
+		for (i = size - 1; i >= 0; i--) {
+		    ESTACK_PUSH(s, HASH_MAP_PAIR);
+		    ESTACK_PUSH(s, vs[i]);
+                    ESTACK_PUSH(s, ks[i]);
+		}
+		goto pop_next;
+	    }
+	    break;
+            case HASHMAP_SUBTAG:
+            {
+                Eterm* ptr = boxed_val(term) + 1;
+                Uint size;
+                int i;
+                switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
+                case HAMT_SUBTAG_HEAD_ARRAY:
+                case HAMT_SUBTAG_HEAD_BITMAP:
+                    size = *ptr++;
+                    UINT32_HASH(size, HCONST_16);
+                    if (size == 0)
+                        goto pop_next;
+                    ESTACK_PUSH(s, hash_xor_pairs);
+                    ESTACK_PUSH(s, hash);
+                    ESTACK_PUSH(s, HASH_MAP_TAIL);
+                    hash = 0;
+                    hash_xor_pairs = 0;
+                }
+                switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
+                case HAMT_SUBTAG_HEAD_ARRAY:
+                case HAMT_SUBTAG_NODE_ARRAY:
+                    i = 16;
+                    break;
+                case HAMT_SUBTAG_HEAD_BITMAP:
+                case HAMT_SUBTAG_NODE_BITMAP:
+                    i = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+                    break;
+                default:
+                    erl_exit(1, "bad header");
+                }
+                while (i) {
+                    if (is_list(*ptr)) {
+                        Eterm* cons = list_val(*ptr);
+                        ESTACK_PUSH(s, HASH_MAP_PAIR);
+                        ESTACK_PUSH(s, CDR(cons));
+                        ESTACK_PUSH(s, CAR(cons));
+                    }
+                    else {
+                        ASSERT(is_boxed(*ptr));
+                        ESTACK_PUSH(s, *ptr);
+                    }
+                    i--; ptr++;
+                }
+                goto pop_next;
+            }
+            break;
+	    case EXPORT_SUBTAG:
+	    {
+		Export* ep = *((Export **) (export_val(term) + 1));
+                /* Assumes Export entries never moves */
+                POINTER_HASH(ep, HCONST_14);
+		goto pop_next;
+	    }
+
+	    case FUN_SUBTAG:
+	    {
+		ErlFunThing* funp = (ErlFunThing *) fun_val(term);
+		Uint num_free = funp->num_free;
+                UINT32_HASH_2(num_free, funp->fe->module, HCONST_20);
+                UINT32_HASH_2(funp->fe->old_index, funp->fe->old_uniq, HCONST_21);
+		if (num_free == 0) {
+		    goto pop_next;
+		} else {
+		    Eterm* bptr = funp->env + num_free - 1;
+		    while (num_free-- > 1) {
+			term = *bptr--;
+			ESTACK_PUSH(s, term);
+		    }
+		    term = *bptr;
+		}
+	    }
+	    break;
+	    case REFC_BINARY_SUBTAG:
+	    case HEAP_BINARY_SUBTAG:
+	    case SUB_BINARY_SUBTAG:
+	    {
+		byte* bptr;
+		unsigned sz = binary_size(term);
+		Uint32 con = HCONST_13 + hash;
+		Uint bitoffs;
+		Uint bitsize;
+
+		ERTS_GET_BINARY_BYTES(term, bptr, bitoffs, bitsize);
+		if (sz == 0 && bitsize == 0) {
+		    hash = con;
+		} else {
+		    if (bitoffs == 0) {
+			hash = block_hash(bptr, sz, con);
+			if (bitsize > 0) {
+			    UINT32_HASH_2(bitsize, (bptr[sz] >> (8 - bitsize)),
+					  HCONST_15);
+			}
+		    } else {
+			byte* buf = (byte *) erts_alloc(ERTS_ALC_T_TMP,
+							sz + (bitsize != 0));
+			erts_copy_bits(bptr, bitoffs, 1, buf, 0, 1, sz*8+bitsize);
+			hash = block_hash(buf, sz, con);
+			if (bitsize > 0) {
+			    UINT32_HASH_2(bitsize, (buf[sz] >> (8 - bitsize)),
+					  HCONST_15);
+			}
+			erts_free(ERTS_ALC_T_TMP, (void *) buf);
+		    }
+		}
+		goto pop_next;
+	    }
+	    break;
+	    case POS_BIG_SUBTAG:
+	    case NEG_BIG_SUBTAG:
+	    {
+		Eterm* ptr = big_val(term);
+		Uint i = 0;
+		Uint n = BIG_SIZE(ptr);
+		Uint32 con = BIG_SIGN(ptr) ? HCONST_10 : HCONST_11;
+#if D_EXP == 16
+		do {
+		    Uint32 x, y;
+		    x = i < n ? BIG_DIGIT(ptr, i++) : 0;
+		    x += (Uint32)(i < n ? BIG_DIGIT(ptr, i++) : 0) << 16;
+		    y = i < n ? BIG_DIGIT(ptr, i++) : 0;
+		    y += (Uint32)(i < n ? BIG_DIGIT(ptr, i++) : 0) << 16;
+		    UINT32_HASH_2(x, y, con);
+		} while (i < n);
+#elif D_EXP == 32
+		do {
+		    Uint32 x, y;
+		    x = i < n ? BIG_DIGIT(ptr, i++) : 0;
+		    y = i < n ? BIG_DIGIT(ptr, i++) : 0;
+		    UINT32_HASH_2(x, y, con);
+		} while (i < n);
+#elif D_EXP == 64
+		do {
+		    Uint t;
+		    Uint32 x, y;
+                    ASSERT(i < n);
+		    t = BIG_DIGIT(ptr, i++);
+		    x = t & 0xffffffff;
+		    y = t >> 32;
+		    UINT32_HASH_2(x, y, con);
+		} while (i < n);
+#else
+#error "unsupported D_EXP size"
+#endif
+		goto pop_next;
+	    }
+	    break;
+	    case REF_SUBTAG:
+		UINT32_HASH(internal_ref_numbers(term)[0], HCONST_7);
+                ASSERT(internal_ref_no_of_numbers(term) == 3);
+                UINT32_HASH_2(internal_ref_numbers(term)[1],
+                              internal_ref_numbers(term)[2], HCONST_8);
+                goto pop_next;
+
+            case EXTERNAL_REF_SUBTAG:
+            {
+                ExternalThing* thing = external_thing_ptr(term);
+
+                ASSERT(external_thing_ref_no_of_numbers(thing) == 3);
+                /*SVERK Is it really ok to hash node POINTER? */
+            #ifdef ARCH_64
+                POINTER_HASH(thing->node, HCONST_7);
+                UINT32_HASH(external_thing_ref_numbers(thing)[0], HCONST_7);
+            #else
+                UINT32_HASH_2(thing->node,
+                              external_thing_ref_numbers(thing)[0], HCONST_7);
+            #endif
+                UINT32_HASH_2(external_thing_ref_numbers(thing)[1],
+                              external_thing_ref_numbers(thing)[2], HCONST_8);
+                goto pop_next;
+            }
+            case EXTERNAL_PID_SUBTAG: {
+                ExternalThing* thing = external_thing_ptr(term);
+                /*SVERK Is it really ok to hash node POINTER? */
+            #ifdef ARCH_64
+                POINTER_HASH(thing->node, HCONST_5);
+                UINT32_HASH(thing->data.ui[0], HCONST_5);
+            #else
+                UINT32_HASH_2(thing->node, thing->data.ui[0], HCONST_5);
+            #endif
+		goto pop_next;
+            }
+	    case EXTERNAL_PORT_SUBTAG: {
+                ExternalThing* thing = external_thing_ptr(term);
+                /*SVERK Is it really ok to hash node POINTER? */
+            #ifdef ARCH_64
+                POINTER_HASH(thing->node, HCONST_6);
+                UINT32_HASH(thing->data.ui[0], HCONST_6);
+            #else
+                UINT32_HASH_2(thing->node, thing->data.ui[0], HCONST_6);
+            #endif
+		goto pop_next;
+            }
+	    case FLOAT_SUBTAG:
+	    {
+		FloatDef ff;
+		GET_DOUBLE(term, ff);
+		UINT32_HASH_2(ff.fw[0], ff.fw[1], HCONST_12);
+		goto pop_next;
+	    }
+
+	    default:
+		erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term);
+	    }
+	}
+	break;
+        case TAG_PRIMARY_IMMED1:
+        #if ERTS_SIZEOF_ETERM == 8
+            UINT32_HASH_2((Uint32)term, (Uint32)(term >> 32), HCONST);
+        #else
+            UINT32_HASH(term, HCONST);
+        #endif
+            goto pop_next;
+
+	default:
+	    erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term);
+
+	pop_next:
+	    if (ESTACK_ISEMPTY(s)) {
+		DESTROY_ESTACK(s);
+		UnUseTmpHeapNoproc(2);
+		return hash;
+	    }
+
+	    term = ESTACK_POP(s);
+
+	    switch (term) {
+		case HASH_MAP_TAIL: {
+		    hash = (Uint32) ESTACK_POP(s);
+                    UINT32_HASH(hash_xor_pairs, HCONST_19);
+		    hash_xor_pairs = (Uint32) ESTACK_POP(s);
+		    goto pop_next;
+		}
+		case HASH_MAP_PAIR:
+		    hash_xor_pairs ^= hash;
+		    hash = 0;
+		    goto pop_next;
+
+	        case HASH_CDR:
+		    CONST_HASH(HCONST_18);   /* Hash CDR i cons cell */
+		    goto pop_next;
+		default:
+		    break;
+	    }
+	}
+    }
+    }
 
+#undef CONST_HASH
 #undef HASH_MAP_TAIL
-#undef HASH_MAP_KEY
-#undef HASH_MAP_VAL
+#undef HASH_MAP_PAIR
+#undef HASH_CDR
 
 #undef UINT32_HASH_2
 #undef UINT32_HASH
-- 
cgit v1.2.3


From 18eabd06e64d21f83f466e99de35302b238ffec7 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Mon, 2 Mar 2015 20:54:42 +0100
Subject: erts: Fix compare order of hamt vs other types

MAP_DEF and HASHMAP_DEF must have adjacent values
---
 erts/emulator/beam/erl_term.h | 30 ++++++++++++++++--------------
 erts/emulator/beam/utils.c    |  8 ++++----
 2 files changed, 20 insertions(+), 18 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index 264bf8bd74..953525a45c 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -1111,20 +1111,22 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint)
 #define LIST_DEF		0x1
 #define NIL_DEF			0x2
 #define MAP_DEF			0x3
-#define TUPLE_DEF		0x4
-#define PID_DEF			0x5
-#define EXTERNAL_PID_DEF	0x6
-#define PORT_DEF		0x7
-#define EXTERNAL_PORT_DEF	0x8
-#define EXPORT_DEF		0x9
-#define FUN_DEF			0xa
-#define REF_DEF			0xb
-#define EXTERNAL_REF_DEF	0xc
-#define ATOM_DEF		0xd
-#define FLOAT_DEF		0xe
-#define BIG_DEF			0xf
-#define SMALL_DEF		0x10
-#define HASHMAP_DEF		0x11
+#define HASHMAP_DEF		0x4
+#define TUPLE_DEF		0x5
+#define PID_DEF			0x6
+#define EXTERNAL_PID_DEF	0x7
+#define PORT_DEF		0x8
+#define EXTERNAL_PORT_DEF	0x9
+#define EXPORT_DEF		0xa
+#define FUN_DEF			0xb
+#define REF_DEF			0xc
+#define EXTERNAL_REF_DEF	0xd
+#define ATOM_DEF		0xe
+#define FLOAT_DEF		0xf
+#define BIG_DEF			0x10
+#define SMALL_DEF		0x11
+
+#define FIRST_VACANT_TAG_DEF    0x12
 
 #if ET_DEBUG
 extern unsigned tag_val_def_debug(Wterm, const char*, unsigned);
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 67331a37f6..3b35750fd7 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -830,10 +830,10 @@ Uint32 make_hash(Eterm term_arg)
     unsigned op;
 
     /* Must not collide with the real tag_val_def's: */
-#define MAKE_HASH_TUPLE_OP 0x11
-#define MAKE_HASH_TERM_ARRAY_OP 0x12
-#define MAKE_HASH_CDR_PRE_OP 0x13
-#define	MAKE_HASH_CDR_POST_OP 0x14
+#define MAKE_HASH_TUPLE_OP      (FIRST_VACANT_TAG_DEF)
+#define MAKE_HASH_TERM_ARRAY_OP (FIRST_VACANT_TAG_DEF+1)
+#define MAKE_HASH_CDR_PRE_OP    (FIRST_VACANT_TAG_DEF+2)
+#define MAKE_HASH_CDR_POST_OP   (FIRST_VACANT_TAG_DEF+3)
 
     /* 
     ** Convenience macro for calculating a bytewise hash on an unsigned 32 bit 
-- 
cgit v1.2.3


From f316f5f718a9e60ed8dc394a6a0a1cb15147dd2a Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 3 Mar 2015 10:46:10 +0100
Subject: erts: Fix erlang:hash and erlang:phash for hamt

by calling make_hash2.
---
 erts/emulator/beam/utils.c       | 32 ++++----------------------------
 erts/emulator/test/map_SUITE.erl | 40 ++++++++++++++++++++--------------------
 2 files changed, 24 insertions(+), 48 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 3b35750fd7..674dddc86f 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1013,21 +1013,9 @@ tail_recur:
 	    break;
 	}	
     case MAP_DEF:
+    case HASHMAP_DEF:
 	{
-	    map_t *mp = (map_t *)map_val(term);
-	    int size  = map_get_size(mp);
-	    Eterm *ks = map_get_keys(mp);
-	    Eterm *vs = map_get_values(mp);
-
-	    /* Use a prime with size to remedy some of
-	     * the {} and <<>> hash problems */
-	    hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + size;
-	    if (size == 0)
-		break;
-
-	    /* push values first */
-	    WSTACK_PUSH3(stack, (UWord)vs, (UWord) size, MAKE_HASH_TERM_ARRAY_OP);
-	    WSTACK_PUSH3(stack, (UWord)ks, (UWord) size, MAKE_HASH_TERM_ARRAY_OP);
+	    hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term);
 	    break;
 	}
     case TUPLE_DEF: 
@@ -2164,21 +2152,9 @@ tail_recur:
 	break;
 
     case MAP_DEF:
+    case HASHMAP_DEF:
 	{
-	    map_t *mp = (map_t *)map_val(term);
-	    int size  = map_get_size(mp);
-	    Eterm *ks = map_get_keys(mp);
-	    Eterm *vs = map_get_values(mp);
-
-	    /* Use a prime with size to remedy some of
-	     * the {} and <<>> hash problems */
-	    hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + size;
-	    if (size == 0)
-		break;
-
-	    /* push values first */
-	    WSTACK_PUSH3(stack, (UWord)vs, (UWord) size, MAKE_HASH_TERM_ARRAY_OP);
-	    WSTACK_PUSH3(stack, (UWord)ks, (UWord) size, MAKE_HASH_TERM_ARRAY_OP);
+	    hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term);
 	    break;
 	}
     case TUPLE_DEF: 
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 1962ce6a57..3e989181f4 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -1034,42 +1034,42 @@ t_bif_erlang_phash2() ->
 
 t_bif_erlang_phash() ->
     Sz = 1 bsl 32,
-    268440612  = erlang:phash(#{},Sz),
-    1196461908 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz),
-    3944426064 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz),
-    1394238263 = erlang:phash(#{ 1 => a },Sz),
-    4066388227 = erlang:phash(#{ a => 1 },Sz),
+    1113425985 = erlang:phash(#{},Sz), % 268440612
+    1510068139 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), % 1196461908
+    3182345590 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), % 3944426064
+    2927531828 = erlang:phash(#{ 1 => a },Sz), % 1394238263
+    1670235874 = erlang:phash(#{ a => 1 },Sz), % 4066388227
 
-    1578050717 = erlang:phash(#{{} => <<>>},Sz),
-    1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken
+    3935089469 = erlang:phash(#{{} => <<>>},Sz), % 1578050717
+    71692856   = erlang:phash(#{<<>> => {}},Sz), % 1578050717
 
     M0 = #{ a => 1, "key" => <<"value">> },
     M1 = maps:remove("key",M0),
     M2 = M1#{ "key" => <<"value">> },
 
-    3590546636 = erlang:phash(M0,Sz),
-    4066388227 = erlang:phash(M1,Sz),
-    3590546636 = erlang:phash(M2,Sz),
+    2620391445 = erlang:phash(M0,Sz), % 3590546636
+    1670235874 = erlang:phash(M1,Sz), % 4066388227
+    2620391445 = erlang:phash(M2,Sz), % 3590546636
     ok.
 
 t_bif_erlang_hash() ->
     Sz = 1 bsl 27 - 1,
-    5158      = erlang:hash(#{},Sz),
-    71555838  = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz),
-    5497225   = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz),
-    126071654 = erlang:hash(#{ 1 => a },Sz),
-    126426236 = erlang:hash(#{ a => 1 },Sz),
+    39684169 = erlang:hash(#{},Sz),  % 5158
+    33673142 = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), % 71555838
+    95337869 = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), % 5497225
+    108959561 = erlang:hash(#{ 1 => a },Sz), % 126071654
+    59623150 = erlang:hash(#{ a => 1 },Sz), % 126426236
 
-    101655720 = erlang:hash(#{{} => <<>>},Sz),
-    101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken
+    42775386 = erlang:hash(#{{} => <<>>},Sz), % 101655720
+    71692856 = erlang:hash(#{<<>> => {}},Sz), % 101655720
 
     M0 = #{ a => 1, "key" => <<"value">> },
     M1 = maps:remove("key",M0),
     M2 = M1#{ "key" => <<"value">> },
 
-    38260486  = erlang:hash(M0,Sz),
-    126426236 = erlang:hash(M1,Sz),
-    38260486  = erlang:hash(M2,Sz),
+    70254632 = erlang:hash(M0,Sz), % 38260486
+    59623150 = erlang:hash(M1,Sz), % 126426236
+    70254632 = erlang:hash(M2,Sz), % 38260486
     ok.
 
 
-- 
cgit v1.2.3


From eb6f1d4923899fcb29a5d3c312f14489e7846a37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 3 Mar 2015 09:35:10 +0100
Subject: erts: Ensure maps becomes hashmaps in maps:merge/2

Maps should become hashmaps when merged size exceeds small limit size.
---
 erts/emulator/beam/erl_map.c | 49 +++++++++++++++++++++++++++++++++++++-------
 1 file changed, 42 insertions(+), 7 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 4ad33f98a7..1703f86b0e 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -830,12 +830,14 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) {
 	if (is_map(BIF_ARG_2)) {
 	    BIF_RET(map_merge(BIF_P, BIF_ARG_1, BIF_ARG_2));
 	} else if (is_hashmap(BIF_ARG_2)) {
+	    /* Will always become a tree */
 	    BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_1, BIF_ARG_2, 0));
 	}
     } else if (is_hashmap(BIF_ARG_1)) {
 	if (is_hashmap(BIF_ARG_2)) {
 	    BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2));
 	} else if (is_map(BIF_ARG_2)) {
+	    /* Will always become a tree */
 	    BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_2, BIF_ARG_1, 1));
 	}
     }
@@ -847,7 +849,7 @@ static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
     Eterm tup;
     Eterm *ks,*vs,*ks1,*vs1,*ks2,*vs2;
     map_t *mp1,*mp2,*mp_new;
-    Uint n1,n2,i1,i2,need,unused_size=0;
+    Uint n,n1,n2,i1,i2,need,unused_size=0;
     int c = 0;
 
     mp1  = (map_t*)map_val(nodeA);
@@ -876,13 +878,13 @@ static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
 
     while(i1 < n1 && i2 < n2) {
 	c = CMP_TERM(ks1[i1],ks2[i2]);
-	if ( c == 0) {
+	if (c == 0) {
 	    /* use righthand side arguments map value,
 	     * but advance both maps */
 	    *ks++ = ks2[i2];
 	    *vs++ = vs2[i2];
 	    i1++, i2++, unused_size++;
-	} else if ( c < 0) {
+	} else if (c < 0) {
 	    *ks++ = ks1[i1];
 	    *vs++ = vs1[i1];
 	    i1++;
@@ -917,8 +919,42 @@ static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
 	HRelease(p, vs + unused_size, vs);
     }
 
-    mp_new->size = n1 + n2 - unused_size;
-    *thp = make_arityval(n1 + n2 - unused_size);
+    n = n1 + n2 - unused_size;
+    *thp = make_arityval(n);
+
+    /* Reshape map to a hashmap if the map exceeds the limit */
+
+    if (n > MAP_SMALL_MAP_LIMIT) {
+	Uint32 hx,sw;
+	Uint i;
+	Eterm res;
+	hxnode_t *hxns;
+
+	ks = map_get_keys(mp_new);
+	vs = map_get_values(mp_new);
+
+	hp = HAlloc(p, 2 * n);
+
+	hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP,n * sizeof(hxnode_t));
+
+	for (i = 0; i < n; i++) {
+	    hx = hashmap_make_hash(ks[i]);
+	    swizzle32(sw,hx);
+	    hxns[i].hx   = sw;
+	    hxns[i].val  = CONS(hp, ks[i], vs[i]); hp += 2;
+	    hxns[i].skip = 1;
+	    hxns[i].i    = i;
+	}
+
+	res = hashmap_from_unsorted_array(p, hxns, n);
+
+	erts_free(ERTS_ALC_T_TMP, (void *) hxns);
+	ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+
+	return res;
+    }
+
+    mp_new->size = n;
 
     return make_map(mp_new);
 }
@@ -929,7 +965,6 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args)
     Uint n, i;
     hxnode_t *hxns;
     Uint32 sw, hx;
-    Eterm tmp[2];
 
     /* convert flat to tree */
 
@@ -947,7 +982,7 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args)
     hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t));
 
     for (i = 0; i < n; i++) {
-	hx = hashmap_restore_hash(tmp,0,ks[i]);
+	hx = hashmap_make_hash(ks[i]);
 	swizzle32(sw,hx);
 	hxns[i].hx   = sw;
 	hxns[i].val  = CONS(hp, ks[i], vs[i]); hp += 2;
-- 
cgit v1.2.3


From ba117527441b58886a06c0feff40677b9fa48983 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 3 Mar 2015 10:10:04 +0100
Subject: erts: Update tests for maps:merge/2 and erlang:map_size/1

---
 erts/emulator/test/map_SUITE.erl | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 3e989181f4..9246760075 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -31,7 +31,6 @@
 	t_map_sort_literals/1,
 	t_map_equal/1,
 	t_map_compare/1,
-	%t_size/1,
 	t_map_size/1,
 
 	%% Specific Map BIFs
@@ -162,17 +161,6 @@ t_build_and_match_literals(Config) when is_list(Config) ->
     ok.
 
 
-%% Tests size(Map).
-%% not implemented, perhaps it shouldn't be either
-
-%t_size(Config) when is_list(Config) ->
-%    0 = size(#{}),
-%    1 = size(#{a=>1}),
-%    1 = size(#{a=>#{a=>1}}),
-%    2 = size(#{a=>1, b=>2}),
-%    3 = size(#{a=>1, b=>2, b=>"3"}),
-%    ok.
-
 t_map_size(Config) when is_list(Config) ->
     0 = map_size(id(#{})),
     1 = map_size(id(#{a=>1})),
@@ -188,12 +176,23 @@ t_map_size(Config) when is_list(Config) ->
     true  = map_is_size(M#{ "a" => 2}, 2),
     false = map_is_size(M#{ "c" => 2}, 2),
 
+    Ks = [build_key(fun(K) -> <<1,K:32,1>> end,I)||I<-lists:seq(1,100)],
+    ok = build_and_check_size(Ks,0,#{}),
+
     %% Error cases.
     {'EXIT',{badarg,_}} = (catch map_size([])),
     {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)),
     {'EXIT',{badarg,_}} = (catch map_size(1)),
     ok.
 
+build_and_check_size([K|Ks],N,M0) ->
+    N = map_size(M0),
+    M1 = M0#{ K => K },
+    build_and_check_size(Ks,N + 1,M1);
+build_and_check_size([],N,M) ->
+    N = map_size(M),
+    ok.
+
 map_is_size(M,N) when map_size(M) =:= N -> true;
 map_is_size(_,_) -> false.
 
@@ -1271,7 +1270,8 @@ t_bif_merge_and_check(Config) when is_list(Config) ->
 	   ["hi"],
 	   [e],
 	   [build_key(fun(K) -> {small,K} end, I) || I <- lists:seq(1,32)],
-	   lists:seq(5, 50),
+	   lists:seq(5, 28),
+	   lists:seq(29, 59),
 	   [build_key(fun(K) -> integer_to_list(K) end, I) || I <- lists:seq(2000,10000)],
 	   [build_key(fun(K) -> <> end, I) || I <- lists:seq(1,80)],
 	   [build_key(fun(K) -> {<>} end, I) || I <- lists:seq(100,1000)]],
-- 
cgit v1.2.3


From 9794b73998690178538a1dfc193565dcd477b4fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 3 Mar 2015 12:46:05 +0100
Subject: erts: Fix update_map_assoc instruction

Did not build a hashmap once the small limit was exceeded.
---
 erts/emulator/beam/beam_emu.c | 13 ++++++++++++-
 erts/emulator/beam/erl_map.c  | 31 +++++++++++++++++++++++++++++++
 erts/emulator/beam/erl_map.h  |  1 +
 3 files changed, 44 insertions(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 1166b32a3b..c753e57ddc 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -6781,8 +6781,19 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
 
     n = kp - p->htop - 1;	/* Actual number of keys/values */
     *p->htop = make_arityval(n);
+    p->htop  = hp;
     mp->size = n;
-    p->htop = hp;
+
+    /* The expensive case, need to build a hashmap */
+    if (n > MAP_SMALL_MAP_LIMIT) {
+	res = erts_hashmap_from_ks_and_vs(p,map_get_keys(mp),map_get_values(mp),n);
+	if (p->mbuf) {
+	    Uint live = Arg(3);
+	    reg[live] = res;
+	    erts_garbage_collect(p, 0, reg, live+1);
+	    res       = reg[live];
+	}
+    }
     return res;
 }
 
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 1703f86b0e..e26b97d75c 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -452,6 +452,37 @@ Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n) {
     return res;
 }
 
+
+Eterm erts_hashmap_from_ks_and_vs(Process *p, Eterm *ks, Eterm *vs, Uint n) {
+    Uint32 sw, hx;
+    Uint i;
+    hxnode_t *hxns;
+    Eterm *hp, res;
+
+    ASSERT(n > 0);
+
+    hp = HAlloc(p, (2 * n));
+
+    /* create tmp hx values and leaf ptrs */
+    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t));
+
+    for(i = 0; i < n; i++) {
+	hx = hashmap_make_hash(ks[i]);
+	swizzle32(sw,hx);
+	hxns[i].hx   = sw;
+	hxns[i].val  = CONS(hp, ks[i], vs[i]); hp += 2;
+	hxns[i].skip = 1; /* will be reassigned in from_array */
+	hxns[i].i    = i;
+    }
+
+    res = hashmap_from_unsorted_array(p, hxns, n);
+
+    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
+    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+
+    return res;
+}
+
 static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n) {
     Uint jx = 0, ix = 0, lx, cx;
     Eterm res;
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 3544189936..6a7b29fe86 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -103,6 +103,7 @@ void   hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
 int    hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
 Eterm  erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n);
+Eterm  erts_hashmap_from_ks_and_vs(Process *p, Eterm *ks, Eterm *vs, Uint n);
 const  Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map);
 
 #if HALFWORD_HEAP
-- 
cgit v1.2.3


From de95f407aba2c8b7bf2a5827842722581aacfd6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 3 Mar 2015 13:34:14 +0100
Subject: erts: Refactor hashmap_from_ks_and_vs

Use extra key and value if needed.
---
 erts/emulator/beam/erl_map.c | 54 +++++++++++++++-----------------------------
 erts/emulator/beam/erl_map.h |  7 +++++-
 2 files changed, 24 insertions(+), 37 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index e26b97d75c..97f94d90ac 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -453,18 +453,18 @@ Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n) {
 }
 
 
-Eterm erts_hashmap_from_ks_and_vs(Process *p, Eterm *ks, Eterm *vs, Uint n) {
+Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n,
+					Eterm key, Eterm value) {
     Uint32 sw, hx;
-    Uint i;
+    Uint i,sz;
     hxnode_t *hxns;
     Eterm *hp, res;
 
-    ASSERT(n > 0);
-
-    hp = HAlloc(p, (2 * n));
+    sz = (key == THE_NON_VALUE) ? n : (n + 1);
+    hp = HAlloc(p, 2 * sz);
 
     /* create tmp hx values and leaf ptrs */
-    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t));
+    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, sz * sizeof(hxnode_t));
 
     for(i = 0; i < n; i++) {
 	hx = hashmap_make_hash(ks[i]);
@@ -475,7 +475,16 @@ Eterm erts_hashmap_from_ks_and_vs(Process *p, Eterm *ks, Eterm *vs, Uint n) {
 	hxns[i].i    = i;
     }
 
-    res = hashmap_from_unsorted_array(p, hxns, n);
+    if (key != THE_NON_VALUE) {
+	hx = hashmap_make_hash(key);
+	swizzle32(sw,hx);
+	hxns[i].hx   = sw;
+	hxns[i].val  = CONS(hp, key, value); hp += 2;
+	hxns[i].skip = 1;
+	hxns[i].i    = i;
+    }
+
+    res = hashmap_from_unsorted_array(p, hxns, sz);
 
     erts_free(ERTS_ALC_T_TMP, (void *) hxns);
     ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
@@ -1531,39 +1540,12 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
 
 	/* the map will grow */
 
-	if (n >= MAP_SMALL_MAP_LIMIT) {
-	    hxnode_t *hxns;
-	    Uint32 sw, hx;
-	    Eterm tmp[2];
-
+	if (n > MAP_SMALL_MAP_LIMIT) {
 	    HRelease(p, shp + MAP_HEADER_SIZE + n, shp);
-	    hp = HAlloc(p, (2 * (n + 1)));
 	    ks = map_get_keys(mp);
 	    vs = map_get_values(mp);
 
-	    /* create tmp hx values and leaf ptrs */
-	    hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, (n + 1) * sizeof(hxnode_t));
-
-	    for (i = 0; i < n; i++) {
-		hx = hashmap_restore_hash(tmp,0,ks[i]);
-		swizzle32(sw,hx);
-		hxns[i].hx   = sw;
-		hxns[i].val  = CONS(hp, ks[i], vs[i]); hp += 2;
-		hxns[i].skip = 1;
-		hxns[i].i    = i;
-	    }
-
-	    hx = hashmap_restore_hash(tmp,0,key);
-	    swizzle32(sw,hx);
-	    hxns[i].hx   = sw;
-	    hxns[i].val  = CONS(hp, key, value); hp += 2;
-	    hxns[i].skip = 1;
-	    hxns[i].i    = n;
-
-	    res = hashmap_from_unsorted_array(p, hxns, n + 1);
-
-	    erts_free(ERTS_ALC_T_TMP, (void *) hxns);
-	    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
+	    res = erts_hashmap_from_ks_and_vs_extra(p,ks,vs,n,key,value);
 
 	    return res;
 	}
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 6a7b29fe86..d1e227183f 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -103,9 +103,14 @@ void   hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
 int    hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
 Eterm  erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n);
-Eterm  erts_hashmap_from_ks_and_vs(Process *p, Eterm *ks, Eterm *vs, Uint n);
 const  Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map);
 
+#define erts_hashmap_from_ks_and_vs(P, KS, VS, N) \
+    erts_hashmap_from_ks_and_vs_extra((P), (KS), (VS), (N), THE_NON_VALUE, THE_NON_VALUE);
+
+Eterm  erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n,
+					 Eterm k, Eterm v);
+
 #if HALFWORD_HEAP
 const Eterm *
 erts_maps_get_rel(Eterm key, Eterm map, Eterm *map_base);
-- 
cgit v1.2.3


From 3697ee925251edfe7a7ac2ed483039f414830012 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 3 Mar 2015 17:16:51 +0100
Subject: erts: Fix instruction new_map

---
 erts/emulator/beam/beam_emu.c | 22 +++++++++++++++++++++-
 erts/emulator/beam/erl_map.c  |  6 +++---
 2 files changed, 24 insertions(+), 4 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index c753e57ddc..8288eb9798 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -6557,6 +6557,27 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
     BeamInstr *ptr;
     map_t *mp;
 
+    ptr = &Arg(4);
+
+    if (n > MAP_SMALL_MAP_LIMIT) {
+	if (HeapWordsLeft(p) < n) {
+	    erts_garbage_collect(p, n, reg, Arg(2));
+	}
+
+	mhp = p->htop;
+	thp = p->htop;
+	E   = p->stop;
+
+	for (i = 0; i < n/2; i++) {
+	    GET_TERM(*ptr++, *mhp++);
+	    GET_TERM(*ptr++, *mhp++);
+	}
+
+	p->htop = mhp;
+
+	return erts_hashmap_from_array(p, thp, n/2);
+    }
+
     if (HeapWordsLeft(p) < need) {
 	erts_garbage_collect(p, need, reg, Arg(2));
     }
@@ -6564,7 +6585,6 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
     thp    = p->htop;
     mhp    = thp + 1 + n/2;
     E      = p->stop;
-    ptr    = &Arg(4);
     keys   = make_tuple(thp);
     *thp++ = make_arityval(n/2);
 
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 97f94d90ac..ea041e2303 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -426,7 +426,6 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
 }
 
 Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n) {
-    Eterm tmp[2];
     Uint32 sw, hx;
     Uint ix;
     hxnode_t *hxns;
@@ -436,12 +435,13 @@ Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n) {
     hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t));
 
     for (ix = 0; ix < n; ix++) {
-	hx  = hashmap_restore_hash(tmp,0,CAR(list_val(leafs[ix])));
+	hx  = hashmap_make_hash(*leafs);
 	swizzle32(sw,hx);
 	hxns[ix].hx   = sw;
-	hxns[ix].val  = leafs[ix];
+	hxns[ix].val  = make_list(leafs);
 	hxns[ix].skip = 1;
 	hxns[ix].i    = ix;
+	leafs += 2;
     }
 
     res = hashmap_from_unsorted_array(p, hxns, n);
-- 
cgit v1.2.3


From 412c30b4dcdb8388b7472c8abb328f2a2fce92c0 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 3 Mar 2015 19:26:03 +0100
Subject: erts: Fix various map vs hamt size bugs

---
 erts/emulator/beam/beam_emu.c |  2 +-
 erts/emulator/beam/erl_map.c  | 36 ++++++++++++++++++++++++++++++++++++
 erts/emulator/beam/erl_term.h |  4 ++--
 3 files changed, 39 insertions(+), 3 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 8288eb9798..15591c3cad 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -6559,7 +6559,7 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
 
     ptr = &Arg(4);
 
-    if (n > MAP_SMALL_MAP_LIMIT) {
+    if (n > 2*MAP_SMALL_MAP_LIMIT) {
 	if (HeapWordsLeft(p) < n) {
 	    erts_garbage_collect(p, n, reg, Arg(2));
 	}
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index ea041e2303..0cf1c0e59d 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -422,6 +422,41 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
     erts_free(ERTS_ALC_T_TMP, (void *) hxns);
     ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
 
+    if (hashmap_size(res) <= MAP_SMALL_MAP_LIMIT) {
+        DECLARE_WSTACK(wstack);
+	Eterm *kv, *ks, *vs;
+	map_t *mp;
+	Eterm keys;
+        Uint n = hashmap_size(res);
+
+	/* build flat structure */
+	hp    = HAlloc(p, 3 + 1 + (2 * n));
+	keys  = make_tuple(hp);
+	*hp++ = make_arityval(n);
+	ks    = hp;
+	hp   += n;
+	mp    = (map_t*)hp;
+	hp   += MAP_HEADER_SIZE;
+	vs    = hp;
+
+	mp->thing_word = MAP_HEADER;
+	mp->size = n;
+	mp->keys = keys;
+
+	hashmap_iterator_init(&wstack, res);
+
+	while ((kv=hashmap_iterator_next(&wstack)) != NULL) {
+	    *ks++ = CAR(kv);
+	    *vs++ = CDR(kv);
+	}
+
+	/* it cannot have multiple keys */
+	erts_validate_and_sort_map(mp);
+
+	DESTROY_WSTACK(wstack);
+	return make_map(mp);
+    }
+
     return res;
 }
 
@@ -461,6 +496,7 @@ Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n
     Eterm *hp, res;
 
     sz = (key == THE_NON_VALUE) ? n : (n + 1);
+    ASSERT(sz > MAP_SMALL_MAP_LIMIT);
     hp = HAlloc(p, 2 * sz);
 
     /* create tmp hx values and leaf ptrs */
diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index 953525a45c..6d97344868 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -1110,8 +1110,8 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint)
 #define BINARY_DEF		0x0
 #define LIST_DEF		0x1
 #define NIL_DEF			0x2
-#define MAP_DEF			0x3
-#define HASHMAP_DEF		0x4
+#define HASHMAP_DEF		0x3
+#define MAP_DEF			0x4
 #define TUPLE_DEF		0x5
 #define PID_DEF			0x6
 #define EXTERNAL_PID_DEF	0x7
-- 
cgit v1.2.3


From f8dbf0c0ff3bd68d720faca230356d281e4e3e42 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 3 Mar 2015 19:32:00 +0100
Subject: First stab at binary_to_term for hamt

with over estimation of heap size.
---
 erts/emulator/beam/beam_emu.c    |   4 +-
 erts/emulator/beam/erl_map.c     |  66 +++++++++-------
 erts/emulator/beam/erl_map.h     |   4 +-
 erts/emulator/beam/erl_message.c |  13 ++++
 erts/emulator/beam/erl_message.h |  17 ++++
 erts/emulator/beam/external.c    | 163 ++++++++++++++++++++++++++++-----------
 6 files changed, 189 insertions(+), 78 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 15591c3cad..4f57037507 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -6556,6 +6556,7 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
     Eterm *E;
     BeamInstr *ptr;
     map_t *mp;
+    ErtsHeapFactory factory;
 
     ptr = &Arg(4);
 
@@ -6575,7 +6576,8 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
 
 	p->htop = mhp;
 
-	return erts_hashmap_from_array(p, thp, n/2);
+        factory.p = p;
+	return erts_hashmap_from_array(&factory, thp, n/2);
     }
 
     if (HeapWordsLeft(p) < need) {
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 0cf1c0e59d..eacdd0b6c3 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -87,9 +87,9 @@ static Eterm hashmap_values(Process *p, Eterm map);
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 static Eterm map_from_validated_list(Process *p, Eterm list, Uint size);
 static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size);
-static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n);
-static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
-static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int is_root);
+static Eterm hashmap_from_unsorted_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n);
+static Eterm hashmap_from_sorted_unique_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int is_root);
+static Eterm hashmap_from_chunked_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int is_root);
 static Eterm hashmap_info(Process *p, Eterm node);
 static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]);
 static int hxnodecmp(hxnode_t* a, hxnode_t* b);
@@ -396,6 +396,7 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
     Uint32 sw, hx;
     Uint ix = 0;
     hxnode_t *hxns;
+    ErtsHeapFactory factory;
 
     ASSERT(size > 0);
 
@@ -417,7 +418,8 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
 	item = CDR(list_val(item));
     }
 
-    res = hashmap_from_unsorted_array(p, hxns, size);
+    factory.p = p;
+    res = hashmap_from_unsorted_array(&factory, hxns, size);
 
     erts_free(ERTS_ALC_T_TMP, (void *) hxns);
     ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
@@ -460,7 +462,7 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
     return res;
 }
 
-Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n) {
+Eterm erts_hashmap_from_array(ErtsHeapFactory* factory, Eterm *leafs, Uint n) {
     Uint32 sw, hx;
     Uint ix;
     hxnode_t *hxns;
@@ -479,10 +481,9 @@ Eterm erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n) {
 	leafs += 2;
     }
 
-    res = hashmap_from_unsorted_array(p, hxns, n);
+    res = hashmap_from_unsorted_array(factory, hxns, n);
 
     erts_free(ERTS_ALC_T_TMP, (void *) hxns);
-    ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
 
     return res;
 }
@@ -493,6 +494,7 @@ Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n
     Uint32 sw, hx;
     Uint i,sz;
     hxnode_t *hxns;
+    ErtsHeapFactory factory;
     Eterm *hp, res;
 
     sz = (key == THE_NON_VALUE) ? n : (n + 1);
@@ -520,7 +522,8 @@ Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n
 	hxns[i].i    = i;
     }
 
-    res = hashmap_from_unsorted_array(p, hxns, sz);
+    factory.p = p;
+    res = hashmap_from_unsorted_array(&factory, hxns, sz);
 
     erts_free(ERTS_ALC_T_TMP, (void *) hxns);
     ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
@@ -528,13 +531,14 @@ Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n
     return res;
 }
 
-static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n) {
+static Eterm hashmap_from_unsorted_array(ErtsHeapFactory* factory,
+                                         hxnode_t *hxns, Uint n) {
     Uint jx = 0, ix = 0, lx, cx;
     Eterm res;
 
     if (n == 0) {
 	Eterm *hp;
-	hp = HAlloc(p, 2);
+	hp = erts_produce_heap(factory, 2, 0);
 	hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(0);
 	hp[1] = 0;
 
@@ -590,7 +594,7 @@ static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n) {
 
     if (cx > 1) {
 	/* recursive decompose array */
-	res = hashmap_from_sorted_unique_array(p, hxns, cx, 0);
+	res = hashmap_from_sorted_unique_array(factory, hxns, cx, 0);
     } else {
 	Eterm *hp;
 
@@ -600,7 +604,7 @@ static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n) {
 	 * hash value has been swizzled, need to drag it down to get the
 	 * correct slot. */
 
-	hp    = HAlloc(p, HAMT_HEAD_BITMAP_SZ(1));
+	hp    = erts_produce_heap(factory, HAMT_HEAD_BITMAP_SZ(1), 0);
 	hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(1 << ((hxns[0].hx >> 0x1c) & 0xf));
 	hp[1] = 1;
 	hp[2] = hxns[0].val;
@@ -610,7 +614,8 @@ static Eterm hashmap_from_unsorted_array(Process *p, hxnode_t *hxns, Uint n) {
     return res;
 }
 
-static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n, int lvl) {
+static Eterm hashmap_from_sorted_unique_array(ErtsHeapFactory* factory,
+                                              hxnode_t *hxns, Uint n, int lvl) {
     Eterm res = NIL;
     Uint i,ix,jx,elems;
     Uint32 sw, hx;
@@ -640,7 +645,7 @@ static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n
 	    qsort(tmp, jx - ix, sizeof(hxnode_t), (int (*)(const void *, const void *)) hxnodecmp);
 
 	    hxns[ix].skip = jx - ix;
-	    hxns[ix].val  = hashmap_from_sorted_unique_array(p, tmp, jx - ix, lvl + 8);
+	    hxns[ix].val  = hashmap_from_sorted_unique_array(factory, tmp, jx - ix, lvl + 8);
 	    erts_free(ERTS_ALC_T_TMP, (void *) tmp);
 	    ix = jx;
 	    if (ix < n) { elems++; }
@@ -651,15 +656,16 @@ static Eterm hashmap_from_sorted_unique_array(Process *p, hxnode_t *hxns, Uint n
 	ix++;
     }
 
-    res = hashmap_from_chunked_array(p, hxns, elems, !lvl);
+    res = hashmap_from_chunked_array(factory, hxns, elems, !lvl);
 
-    ERTS_HOLE_CHECK(p);
+    ERTS_FACTORY_HOLE_CHECK(factory);
 
     return res;
 }
 
 #define HALLOC_EXTRA 200
-static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int is_root) {
+static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory,
+                                        hxnode_t *hxns, Uint n, int is_root) {
     Uint ix, d, dn, dc, slot, elems;
     Uint32 v, vp, vn, hdr;
     Uint bp, sz;
@@ -691,7 +697,7 @@ static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int
 	dc = 7;
 	/* build collision nodes */
 	while (dc > d) {
-	    hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
+	    hp    = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
 	    hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(vp,dc));
 	    hp[1] = res;
 	    res   = make_hashmap(hp);
@@ -722,7 +728,7 @@ static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int
 	    dc = 7;
 	    /* build collision nodes */
 	    while (dc > wat) {
-		hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
+		hp    = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
 		hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(v,dc));
 		hp[1] = res;
 		res   = make_hashmap(hp);
@@ -762,7 +768,7 @@ static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int
 		 * redundant collisions */
 		hdr  |= bp;
 		sz    = hashmap_bitcount(hdr);
-		hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA);
+		hp    = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA);
 		nhp   = hp;
 		*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr);
 		*hp++ = res; sz--;
@@ -782,7 +788,7 @@ static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int
 	vp = v;
 	v  = vn;
 	d  = dn;
-	ERTS_HOLE_CHECK(p);
+	ERTS_FACTORY_HOLE_CHECK(factory);
     }
 
     /* v and vp are reused from above */
@@ -794,7 +800,7 @@ static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int
 	dc = 7;
 	/* build collision nodes */
 	while (dc > dn) {
-	    hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
+	    hp    = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA);
 	    hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(v,dc));
 	    hp[1] = res;
 	    res   = make_hashmap(hp);
@@ -811,7 +817,7 @@ static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int
 	 * redundant collisions */
 	hdr  |= bp;
 	sz    = hashmap_bitcount(hdr);
-	hp    = HAllocX(p, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA);
+	hp    = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA);
 	nhp   = hp;
 	*hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr);
 	*hp++ = res; sz--;
@@ -828,7 +834,7 @@ static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int
     bp    = 1 << slot;
     hdr  |= bp;
     sz    = hashmap_bitcount(hdr);
-    hp    = HAlloc(p, sz + /* hdr + item */ (is_root ? 2 : 1));
+    hp    = erts_produce_heap(factory, sz + /* hdr + item */ (is_root ? 2 : 1), 0);
     nhp   = hp;
 
     if (is_root) {
@@ -845,7 +851,7 @@ static Eterm hashmap_from_chunked_array(Process *p, hxnode_t *hxns, Uint n, int
 
     ASSERT(ESTACK_COUNT(stack) == 0);
     DESTROY_ESTACK(stack);
-    ERTS_HOLE_CHECK(p);
+    ERTS_FACTORY_HOLE_CHECK(factory);
     return res;
 }
 #undef HALLOC_EXTRA
@@ -1005,6 +1011,7 @@ static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
 	Uint i;
 	Eterm res;
 	hxnode_t *hxns;
+        ErtsHeapFactory factory;
 
 	ks = map_get_keys(mp_new);
 	vs = map_get_values(mp_new);
@@ -1022,7 +1029,8 @@ static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
 	    hxns[i].i    = i;
 	}
 
-	res = hashmap_from_unsorted_array(p, hxns, n);
+        factory.p = p;
+	res = hashmap_from_unsorted_array(&factory, hxns, n);
 
 	erts_free(ERTS_ALC_T_TMP, (void *) hxns);
 	ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
@@ -1041,6 +1049,7 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args)
     Uint n, i;
     hxnode_t *hxns;
     Uint32 sw, hx;
+    ErtsHeapFactory factory;
 
     /* convert flat to tree */
 
@@ -1066,7 +1075,8 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args)
 	hxns[i].i    = i;
     }
 
-    res = hashmap_from_unsorted_array(p, hxns, n);
+    factory.p = p;
+    res = hashmap_from_unsorted_array(&factory, hxns, n);
 
     erts_free(ERTS_ALC_T_TMP, (void *) hxns);
     ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
@@ -1576,7 +1586,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
 
 	/* the map will grow */
 
-	if (n > MAP_SMALL_MAP_LIMIT) {
+	if (n >= MAP_SMALL_MAP_LIMIT) {
 	    HRelease(p, shp + MAP_HEADER_SIZE + n, shp);
 	    ks = map_get_keys(mp);
 	    vs = map_get_values(mp);
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index d1e227183f..35dbed86a9 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -80,7 +80,7 @@ typedef struct map_s {
 #define map_get_keys(x)        (((Eterm *)tuple_val(((map_t *)(x))->keys)) + 1)
 #define map_get_size(x)        (((map_t*)(x))->size)
 
-#define MAP_SMALL_MAP_LIMIT    (32)
+#define MAP_SMALL_MAP_LIMIT    (2) /*SVERK (32) */
 #define MAP_HEADER             _make_header(1,_TAG_HEADER_MAP)
 #define MAP_HEADER_SIZE        (sizeof(map_t) / sizeof(Eterm))
 
@@ -102,7 +102,7 @@ int    erts_validate_and_sort_map(map_t* map);
 void   hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
 int    hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
-Eterm  erts_hashmap_from_array(Process *p, Eterm *leafs, Uint n);
+Eterm  erts_hashmap_from_array(ErtsHeapFactory*, Eterm *leafs, Uint n);
 const  Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map);
 
 #define erts_hashmap_from_ks_and_vs(P, KS, VS, N) \
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 8870fac7d9..e4cbd8477d 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -1146,3 +1146,16 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp,
     }
 }
 
+Eterm* erts_produce_heap(ErtsHeapFactory* factory, Uint need, Uint xtra)
+{
+    Eterm* res;
+    if (factory->p) {
+        res = HAllocX(factory->p, need, xtra);
+    } else {
+        res = factory->hp;
+        factory->hp += need;
+        ASSERT(factory->hp <= factory->hp_end);
+    }
+    return res;
+}
+
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 0f3bb8d281..ece75a5ee4 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -68,6 +68,23 @@ struct erl_heap_fragment {
     Eterm mem[1];		/* Data */
 };
 
+typedef struct {
+    Process* p;
+    Eterm* hp;
+    Eterm* hp_end;
+    /* more to come... */
+} ErtsHeapFactory;
+
+Eterm* erts_produce_heap(ErtsHeapFactory*, Uint need, Uint xtra);
+#ifdef CHECK_FOR_HOLES
+# define ERTS_FACTORY_HOLE_CHECK(f) do {    \
+        if ((f)->p) erts_check_for_holes((f)->p); \
+    } while (0)
+#else
+# define ERTS_FACTORY_HOLE_CHECK(p)
+#endif
+
+
 typedef struct erl_mesg {
     struct erl_mesg* next;	/* Next message */
     union {
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index af8db4c265..0e012e9eec 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -1182,7 +1182,8 @@ typedef struct {
     Eterm* hp_end;
     int remaining_n;
     char* remaining_bytes;
-    Eterm* maps_head;
+    Eterm* maps_list;
+    struct dec_term_hamt_placeholder* hamt_list;
 } B2TDecodeContext;
 
 typedef struct {
@@ -1508,7 +1509,8 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar
             ctx->u.dc.hp_start = HAlloc(p, ctx->heap_size);
             ctx->u.dc.hp       = ctx->u.dc.hp_start;
             ctx->u.dc.hp_end   = ctx->u.dc.hp_start + ctx->heap_size;
-	    ctx->u.dc.maps_head = NULL;
+	    ctx->u.dc.maps_list = NULL;
+	    ctx->u.dc.hamt_list = NULL;
             ctx->state = B2TDecode;
             /*fall through*/
 	case B2TDecode:
@@ -2938,9 +2940,21 @@ undo_offheap_in_area(ErlOffHeap* off_heap, Eterm* start, Eterm* end)
 #endif /* DEBUG */
 }
 
+struct dec_term_hamt_placeholder
+{
+    struct dec_term_hamt_placeholder* next;
+    Eterm* objp; /* write result here */
+    Uint size;   /* nr of leafs */
+    Eterm* leaf_array;
+
+    Eterm _heap_capacity_[1];
+};
+
+#define DEC_TERM_HAMT_PLACEHOLDER_SIZE(SZ) \
+    (offsetof(struct dec_term_hamt_placeholder, _heap_capacity_) + (SZ))
 
 /* Decode term from external format into *objp.
-** On failure return NULL and (R13B04) *hpp will be unchanged.
+** On failure return NULL and *hpp will be unchanged.
 */
 static byte*
 dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap,
@@ -2950,7 +2964,8 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap,
     int n;
     ErtsAtomEncoding char_enc;
     register Eterm* hp;        /* Please don't take the address of hp */
-    Eterm *maps_head;   /* for validation of maps */
+    Eterm *maps_list;   /* for preprocessing of small maps */
+    struct dec_term_hamt_placeholder* hamt_list;   /* for preprocessing of big maps */
     Eterm* next;
     SWord reds;
 
@@ -2960,7 +2975,8 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap,
         next     = ctx->u.dc.next;
         ep       = ctx->u.dc.ep;
         hpp      = &ctx->u.dc.hp;
-	maps_head = ctx->u.dc.maps_head;
+	maps_list = ctx->u.dc.maps_list;
+        hamt_list = ctx->u.dc.hamt_list;
 
         if (ctx->state != B2TDecode) {
             int n_limit = reds;
@@ -3041,7 +3057,8 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap,
         reds = ERTS_SWORD_MAX;
         next = objp;
         *next = (Eterm) (UWord) NULL;
-	maps_head = NULL;
+	maps_list = NULL;
+        hamt_list = NULL;
     }
     hp = *hpp;
 
@@ -3577,46 +3594,68 @@ dec_term_atom_common:
 	    break;
 	case MAP_EXT:
 	    {
-		map_t *mp;
 		Uint32 size,n;
 		Eterm *kptr,*vptr;
 		Eterm keys;
 
 		size = get_int32(ep); ep += 4;
 
-		keys  = make_tuple(hp);
-		*hp++ = make_arityval(size);
-		hp   += size;
-		kptr = hp - 1;
-
-		mp    = (map_t*)hp;
-		hp   += MAP_HEADER_SIZE;
-		hp   += size;
-		vptr = hp - 1;
-
-		/* kptr, last word for keys
-		 * vptr, last word for values
-		 */
-
-		/*
-		 * Use thing_word to link through decoded maps.
-		 * The list of maps is for later validation.
-		 */
-
-		mp->thing_word = (Eterm) COMPRESS_POINTER(maps_head);
-		maps_head      = (Eterm *) mp;
-
-		mp->size       = size;
-		mp->keys       = keys;
-		*objp          = make_map(mp);
-
-		for (n = size; n; n--) {
-		    *vptr = (Eterm) COMPRESS_POINTER(next);
-		    *kptr = (Eterm) COMPRESS_POINTER(vptr);
-		    next  = kptr;
-		    vptr--;
-		    kptr--;
-		}
+                if (size <= MAP_SMALL_MAP_LIMIT) {
+                    map_t *mp;
+
+                    keys  = make_tuple(hp);
+                    *hp++ = make_arityval(size);
+                    hp   += size;
+                    kptr = hp - 1;
+
+                    mp    = (map_t*)hp;
+                    hp   += MAP_HEADER_SIZE;
+                    hp   += size;
+                    vptr = hp - 1;
+
+                    /* kptr, last word for keys
+                     * vptr, last word for values
+                     */
+
+                    /*
+                     * Use thing_word to link through decoded maps.
+                     * The list of maps is for later validation.
+                     */
+
+                    mp->thing_word = (Eterm) COMPRESS_POINTER(maps_list);
+                    maps_list      = (Eterm *) mp;
+
+                    mp->size       = size;
+                    mp->keys       = keys;
+                    *objp          = make_map(mp);
+
+                    for (n = size; n; n--) {
+                        *vptr = (Eterm) COMPRESS_POINTER(next);
+                        *kptr = (Eterm) COMPRESS_POINTER(vptr);
+                        next  = kptr;
+                        vptr--;
+                        kptr--;
+                    }
+                }
+                else {  /* Make hamt */
+                    struct dec_term_hamt_placeholder* holder =
+                        (struct dec_term_hamt_placeholder*) hp;
+
+                    holder->next = hamt_list;
+                    hamt_list    = holder;
+                    holder->objp = objp;
+                    holder->size = size;
+
+                    hp += DEC_TERM_HAMT_PLACEHOLDER_SIZE(size);
+                    holder->leaf_array = hp;
+
+                    for (n = size; n; n--) {
+                        CDR(hp) = (Eterm) COMPRESS_POINTER(next);
+                        CAR(hp) = (Eterm) COMPRESS_POINTER(&CDR(hp));
+                        next = &CAR(hp);
+                        hp += 2;
+                    }
+                }
 	    }
 	    break;
 	case NEW_FUN_EXT:
@@ -3837,7 +3876,7 @@ dec_term_atom_common:
                     ctx->u.dc.ep = ep;
                     ctx->u.dc.next = next;
                     ctx->u.dc.hp = hp;
-		    ctx->u.dc.maps_head = maps_head;
+		    ctx->u.dc.maps_list = maps_list;
                     ctx->reds = 0;
                     return NULL;
                 }
@@ -3852,12 +3891,38 @@ dec_term_atom_common:
      * - done here for when we know it is complete.
      */
 
-    while (maps_head) {
-	next  = (Eterm *)(EXPAND_POINTER(*maps_head));
-	*maps_head = MAP_HEADER;
-	if (!erts_validate_and_sort_map((map_t*)maps_head))
+    while (maps_list) {
+	next  = (Eterm *)(EXPAND_POINTER(*maps_list));
+	*maps_list = MAP_HEADER;
+	if (!erts_validate_and_sort_map((map_t*)maps_list))
 	    goto error;
-	maps_head  = next;
+	maps_list  = next;
+    }
+
+    while (hamt_list) {
+        ErtsHeapFactory factory;
+        int residue;
+
+        factory.p = NULL;
+        factory.hp = (Eterm*) hamt_list;
+        factory.hp_end = hamt_list->leaf_array;
+
+        next  = (Eterm *) hamt_list->next;
+        objp = hamt_list->objp;
+
+	/*SVERK Make it reject duplicate keys */
+        *objp = erts_hashmap_from_array(&factory,
+                                        hamt_list->leaf_array,
+                                        hamt_list->size);
+        if (is_non_value(*objp))
+            goto error;
+
+        residue = factory.hp_end - factory.hp;
+        if (residue) {
+            ASSERT(residue > 0);
+            *factory.hp = make_pos_bignum_header(residue-1);
+        }
+        hamt_list = (struct dec_term_hamt_placeholder*) next;
     }
 
     if (ctx) {
@@ -4420,7 +4485,11 @@ init_done:
 	    n = get_int32(ep);
 	    ep += 4;
 	    ADDTERMS(2*n);
-	    heap_size += 3 + n + 1 + n;
+            if (n <= MAP_SMALL_MAP_LIMIT) {
+                heap_size += 3 + n + 1 + n;
+            } else {
+                heap_size += DEC_TERM_HAMT_PLACEHOLDER_SIZE(n) + 2*n;
+            }
 	    break;
 	case STRING_EXT:
 	    CHKSIZE(2);
-- 
cgit v1.2.3


From d0e0608c0585240d18e9193af947bd666e9df892 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 5 Mar 2015 15:16:59 +0100
Subject: erts: Update map tests for unordered maps

---
 erts/emulator/test/map_SUITE.erl | 94 +++++++++++++++++++++-------------------
 1 file changed, 49 insertions(+), 45 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 9246760075..edfa197114 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -826,12 +826,12 @@ t_bif_map_is_key(Config) when is_list(Config) ->
 t_bif_map_keys(Config) when is_list(Config) ->
     [] = maps:keys(#{}),
 
-    [1,2,3,4,5] = maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
-    [1,2,3,4,5] = maps:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+    [1,2,3,4,5] = lists:sort(maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e})),
+    [1,2,3,4,5] = lists:sort(maps:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c})),
 
     % values in key order: [4,int,"hi",<<"key">>]
     M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
-    [4,int,"hi",<<"key">>] = maps:keys(M1),
+    [4,int,"hi",<<"key">>] = lists:sort(maps:keys(M1)),
 
     %% error case
     {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(1 bsl 65 + 3)),
@@ -880,33 +880,33 @@ t_bif_map_put(Config) when is_list(Config) ->
 
     M1 = #{ "hi" := "hello"} = maps:put("hi", "hello", #{}),
 
-    ["hi"]    = maps:keys(M1),
-    ["hello"] = maps:values(M1),
+    true = is_members(["hi"],maps:keys(M1)),
+    true = is_members(["hello"],maps:values(M1)),
 
     M2 = #{ int := 3 } = maps:put(int, 3, M1),
 
-    [int,"hi"]  = maps:keys(M2),
-    [3,"hello"] = maps:values(M2),
+    true = is_members([int,"hi"],maps:keys(M2)),
+    true = is_members([3,"hello"],maps:values(M2)),
 
     M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2),
 
-    [int,"hi",<<"key">>]    = maps:keys(M3),
-    [3,"hello",<<"value">>] = maps:values(M3),
+    true = is_members([int,"hi",<<"key">>],maps:keys(M3)),
+    true = is_members([3,"hello",<<"value">>],maps:values(M3)),
 
     M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3),
 
-    [18446744073709551629,int,"hi",<<"key">>] = maps:keys(M4),
-    [wat,3,"hello",<<"value">>]               = maps:values(M4),
+    true = is_members([18446744073709551629,int,"hi",<<"key">>],maps:keys(M4)),
+    true = is_members([wat,3,"hello",<<"value">>],maps:values(M4)),
 
     M0 = #{ 4 := number } = M5 = maps:put(4, number, M4),
 
-    [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5),
-    [number,wat,3,"hello",<<"value">>]          = maps:values(M5),
+    true = is_members([4,18446744073709551629,int,"hi",<<"key">>],maps:keys(M5)),
+    true = is_members([number,wat,3,"hello",<<"value">>],maps:values(M5)),
 
     M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5),
 
-    [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M6),
-    [number,wat,3,"hello",<<"other value">>]    = maps:values(M6),
+    true = is_members([4,18446744073709551629,int,"hi",<<"key">>],maps:keys(M6)),
+    true = is_members([number,wat,3,"hello",<<"other value">>],maps:values(M6)),
 
     %% error case
     {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)),
@@ -914,7 +914,11 @@ t_bif_map_put(Config) when is_list(Config) ->
     {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,atom)),
     {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,[])),
     {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)),
-     ok.
+    ok.
+
+is_members([],_) -> true;
+is_members([K|Ks],Ls) ->
+    lists:member(K,Ls) andalso is_members(Ks,Ls).
 
 t_bif_map_remove(Config) when is_list(Config) ->
     0  = erlang:map_size(maps:remove(some_key, #{})),
@@ -923,20 +927,20 @@ t_bif_map_remove(Config) when is_list(Config) ->
 	4 => number, 18446744073709551629 => wat},
 
     M1 = maps:remove("hi", M0),
-    [4,18446744073709551629,int,<<"key">>] = maps:keys(M1),
-    [number,wat,3,<<"value">>]             = maps:values(M1),
+    true = is_members([4,18446744073709551629,int,<<"key">>],maps:keys(M1)),
+    true = is_members([number,wat,3,<<"value">>],maps:values(M1)),
 
     M2 = maps:remove(int, M1),
-    [4,18446744073709551629,<<"key">>] = maps:keys(M2),
-    [number,wat,<<"value">>]           = maps:values(M2),
+    true = is_members([4,18446744073709551629,<<"key">>],maps:keys(M2)),
+    true = is_members([number,wat,<<"value">>],maps:values(M2)),
 
     M3 = maps:remove(<<"key">>, M2),
-    [4,18446744073709551629] = maps:keys(M3),
-    [number,wat]             = maps:values(M3),
+    true = is_members([4,18446744073709551629],maps:keys(M3)),
+    true = is_members([number,wat],maps:values(M3)),
 
     M4 = maps:remove(18446744073709551629, M3),
-    [4]      = maps:keys(M4),
-    [number] = maps:values(M4),
+    true = is_members([4],maps:keys(M4)),
+    true = is_members([number],maps:values(M4)),
 
     M5 = maps:remove(4, M4),
     [] = maps:keys(M5),
@@ -986,15 +990,15 @@ t_bif_map_update(Config) when is_list(Config) ->
 t_bif_map_values(Config) when is_list(Config) ->
 
     [] = maps:values(#{}),
+    [1] = maps:values(#{a=>1}),
 
-    [a,b,c,d,e] = maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
-    [a,b,c,d,e] = maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+    true = is_members([a,b,c,d,e],maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e})),
+    true = is_members([a,b,c,d,e],maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c})),
 
-    % values in key order: [4,int,"hi",<<"key">>]
     M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
     M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> },
-    [number,3,"hello2",<<"value2">>] = maps:values(M2),
-    [number,3,"hello",<<"value">>]   = maps:values(M1),
+    true = is_members([number,3,"hello2",<<"value2">>],maps:values(M2)),
+    true = is_members([number,3,"hello",<<"value">>],maps:values(M1)),
 
     %% error case
     {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)),
@@ -1102,12 +1106,12 @@ t_map_encode_decode(Config) when is_list(Config) ->
 
     %% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order
     #{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3,
-	107,0,2,104,105, % "hi" :: list()
+	107,0,2,104,105,            % "hi" :: list()
 	107,0,5,118,97,108,117,101, % "value" :: list()
-	100,0,1,97, % a :: atom()
-	97,33, % 33 :: integer()
-	100,0,1,98, % b :: atom()
-	97,55  % 55 :: integer()
+	100,0,1,97,                 % a :: atom()
+	97,33,                      % 33 :: integer()
+	100,0,1,98,                 % b :: atom()
+	97,55                       % 55 :: integer()
 	>>),
 
 
@@ -1163,16 +1167,16 @@ match_encoded_map(Bin,[<<131,Item/binary>>|Items]) ->
 
 t_bif_map_to_list(Config) when is_list(Config) ->
     [] = maps:to_list(#{}),
-    [{a,1},{b,2}] = maps:to_list(#{a=>1,b=>2}),
-    [{a,1},{b,2},{c,3}] = maps:to_list(#{c=>3,a=>1,b=>2}),
-    [{a,1},{b,2},{g,3}] = maps:to_list(#{g=>3,a=>1,b=>2}),
-    [{a,1},{b,2},{g,3},{"c",4}] = maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4}),
-    [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = maps:to_list(#{
-	    <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}),
+    [{a,1},{b,2}] = lists:sort(maps:to_list(#{a=>1,b=>2})),
+    [{a,1},{b,2},{c,3}] = lists:sort(maps:to_list(#{c=>3,a=>1,b=>2})),
+    [{a,1},{b,2},{g,3}] = lists:sort(maps:to_list(#{g=>3,a=>1,b=>2})),
+    [{a,1},{b,2},{g,3},{"c",4}] = lists:sort(maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4})),
+    [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] =
+	lists:sort(maps:to_list(#{<<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5})),
 
-    [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = maps:to_list(#{
-	    <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5,
-	    <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10}),
+    [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] =
+	lists:sort(maps:to_list(#{<<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5,
+				  <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10})),
 
     %% error cases
     {'EXIT', {badarg,_}} = (catch maps:to_list(id(a))),
@@ -1185,7 +1189,7 @@ t_bif_map_from_list(Config) when is_list(Config) ->
     A   = maps:from_list([]),
     0   = erlang:map_size(A),
 
-    #{a:=1,b:=2}  = maps:from_list([{a,1},{b,2}]),
+    #{a:=1,b:=2}      = maps:from_list([{a,1},{b,2}]),
     #{c:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{c,3}]),
     #{g:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{g,3}]),
 
@@ -1258,7 +1262,7 @@ build_key(F,N) when N rem 3 =:= 2 -> K = F(N), [K,K].
 
 check_keys_exist([], _) -> ok;
 check_keys_exist([K|Ks],M) ->
-    K = maps:get(K,M),
+    true = maps:is_key(K,M),
     check_keys_exist(Ks,M).
 
 t_bif_merge_and_check(Config) when is_list(Config) ->
-- 
cgit v1.2.3


From 10fa0c3d1b1fbe473de385ee7ae2aa77807e0281 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 5 Mar 2015 16:16:30 +0100
Subject: erts: Fix instruction i_has_map_fields_fsI for hashmaps

---
 erts/emulator/beam/beam_emu.c | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 4f57037507..9b4cd38d69 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -2400,10 +2400,24 @@ void process_main(void)
     Uint sz,n;
 
     GetArg1(1, map);
+    n  = (Uint)Arg(2);
+    fs = &Arg(3); /* pattern fields */
 
-    /* this instruction assumes Arg1 is a map,
-     * i.e. that it follows a test is_map if needed.
-     */
+    /* get term from field? */
+    if (is_hashmap(map)) {
+	Uint32 hx;
+	while(n--) {
+	    field = *fs++;
+	    hx = hashmap_make_hash(field);
+	    if (!erts_hashmap_get(hx,field,map)) {
+		SET_I((BeamInstr *) Arg(0));
+		goto has_map_fields_fail;
+	    }
+	}
+	goto has_map_fields_ok;
+    }
+
+    ASSERT(is_map(map));
 
     mp = (map_t *)map_val(map);
     sz = map_get_size(mp);
@@ -2414,8 +2428,6 @@ void process_main(void)
     }
 
     ks = map_get_keys(mp);
-    n  = (Uint)Arg(2);
-    fs = &Arg(3); /* pattern fields */
 
     ASSERT(n>0);
 
@@ -2433,7 +2445,7 @@ void process_main(void)
 	SET_I((BeamInstr *) Arg(0));
 	goto has_map_fields_fail;
     }
-
+has_map_fields_ok:
     I += 4 + Arg(2);
 has_map_fields_fail:
     ASSERT(VALID_INSTR(*I));
-- 
cgit v1.2.3


From 2a023ecbf5adecdf78fd13728e41a44420595101 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 5 Mar 2015 17:54:53 +0100
Subject: erts: Fix maps testcase unordered enc/dec

---
 erts/emulator/test/map_SUITE.erl | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index edfa197114..1ec2484c0b 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -1144,25 +1144,28 @@ t_map_encode_decode(Config) when is_list(Config) ->
 map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) ->
     M1 = maps:put(K,V,M0),
     B0 = erlang:term_to_binary(M1),
-    Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]),
-    %% sort Ks and Vs according to term spec, then match it
-    KVbins = lists:foldr(fun({_,Kbin,Vbin}, Acc) -> [Kbin,Vbin | Acc] end, [], Ls),
-    ok = match_encoded_map(B0, length(Ls), KVbins),
+    Ls = [{erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs],
+    ok = match_encoded_map(B0, length(Ls), Ls),
     %% decode and match it
     M1 = erlang:binary_to_term(B0),
     map_encode_decode_and_match(Pairs,Ls,M1);
 map_encode_decode_and_match([],_,_) -> ok.
 
 match_encoded_map(<<131,116,Size:32,Encoded/binary>>,Size,Items) ->
-    match_encoded_map(Encoded,Items);
+    match_encoded_map_stripped_size(Encoded,Items,Items);
 match_encoded_map(_,_,_) -> no_match_size.
 
-match_encoded_map(<<>>,[]) -> ok;
-match_encoded_map(Bin,[<<131,Item/binary>>|Items]) ->
-    Size = erlang:byte_size(Item),
-    <> = Bin,
-    EncodedTerm = Item, %% Asssert
-    match_encoded_map(Bin1,Items).
+match_encoded_map_stripped_size(<<>>,_,_) -> ok;
+match_encoded_map_stripped_size(B0,[{<<131,K/binary>>,<<131,V/binary>>}|Items],Ls) ->
+    Ksz = byte_size(K),
+    Vsz = byte_size(V),
+    case B0 of
+	<> ->
+	    match_encoded_map_stripped_size(B1,Ls,Ls);
+	_ ->
+	    match_encoded_map_stripped_size(B0,Items,Ls)
+    end;
+match_encoded_map_stripped_size(_,[],_) -> fail.
 
 
 t_bif_map_to_list(Config) when is_list(Config) ->
-- 
cgit v1.2.3


From 494223c5e5de14c3f7a9aa8395d2d99a7faa0eca Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Thu, 5 Mar 2015 18:32:31 +0100
Subject: erts: Make is_members in map_SUITE's more stringent

---
 erts/emulator/test/map_SUITE.erl | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 1ec2484c0b..943c6f262e 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -916,9 +916,13 @@ t_bif_map_put(Config) when is_list(Config) ->
     {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)),
     ok.
 
-is_members([],_) -> true;
-is_members([K|Ks],Ls) ->
-    lists:member(K,Ls) andalso is_members(Ks,Ls).
+is_members(Ks,Ls) when length(Ks) =/= length(Ls) -> false;
+is_members(Ks,Ls) -> is_members_do(Ks,Ls).
+
+is_members_do([],[]) -> true;
+is_members_do([],_) -> false;
+is_members_do([K|Ks],Ls) ->
+    is_members_do(Ks, lists:delete(K,Ls)).
 
 t_bif_map_remove(Config) when is_list(Config) ->
     0  = erlang:map_size(maps:remove(some_key, #{})),
-- 
cgit v1.2.3


From 01e843722aa39b3411d349c6fbea80ad87a6e9ef Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Thu, 5 Mar 2015 18:48:14 +0100
Subject: erts: Reject duplicate keys for hamt in binary_to_term

---
 erts/emulator/beam/beam_emu.c |  2 +-
 erts/emulator/beam/erl_map.c  | 23 ++++++++++++++---------
 erts/emulator/beam/erl_map.h  |  2 +-
 erts/emulator/beam/external.c |  4 ++--
 4 files changed, 18 insertions(+), 13 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 9b4cd38d69..d45aa93f73 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -6589,7 +6589,7 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
 	p->htop = mhp;
 
         factory.p = p;
-	return erts_hashmap_from_array(&factory, thp, n/2);
+	return erts_hashmap_from_array(&factory, thp, n/2, 0);
     }
 
     if (HeapWordsLeft(p) < need) {
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index eacdd0b6c3..3ccfc61b08 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -87,7 +87,7 @@ static Eterm hashmap_values(Process *p, Eterm map);
 static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node);
 static Eterm map_from_validated_list(Process *p, Eterm list, Uint size);
 static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size);
-static Eterm hashmap_from_unsorted_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n);
+static Eterm hashmap_from_unsorted_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int reject_dupkeys);
 static Eterm hashmap_from_sorted_unique_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int is_root);
 static Eterm hashmap_from_chunked_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int is_root);
 static Eterm hashmap_info(Process *p, Eterm node);
@@ -419,7 +419,7 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
     }
 
     factory.p = p;
-    res = hashmap_from_unsorted_array(&factory, hxns, size);
+    res = hashmap_from_unsorted_array(&factory, hxns, size, 0);
 
     erts_free(ERTS_ALC_T_TMP, (void *) hxns);
     ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
@@ -462,7 +462,8 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
     return res;
 }
 
-Eterm erts_hashmap_from_array(ErtsHeapFactory* factory, Eterm *leafs, Uint n) {
+Eterm erts_hashmap_from_array(ErtsHeapFactory* factory, Eterm *leafs, Uint n,
+                              int reject_dupkeys) {
     Uint32 sw, hx;
     Uint ix;
     hxnode_t *hxns;
@@ -481,7 +482,7 @@ Eterm erts_hashmap_from_array(ErtsHeapFactory* factory, Eterm *leafs, Uint n) {
 	leafs += 2;
     }
 
-    res = hashmap_from_unsorted_array(factory, hxns, n);
+    res = hashmap_from_unsorted_array(factory, hxns, n, reject_dupkeys);
 
     erts_free(ERTS_ALC_T_TMP, (void *) hxns);
 
@@ -523,7 +524,7 @@ Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n
     }
 
     factory.p = p;
-    res = hashmap_from_unsorted_array(&factory, hxns, sz);
+    res = hashmap_from_unsorted_array(&factory, hxns, sz, 0);
 
     erts_free(ERTS_ALC_T_TMP, (void *) hxns);
     ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
@@ -532,7 +533,8 @@ Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n
 }
 
 static Eterm hashmap_from_unsorted_array(ErtsHeapFactory* factory,
-                                         hxnode_t *hxns, Uint n) {
+                                         hxnode_t *hxns, Uint n,
+                                         int reject_dupkeys) {
     Uint jx = 0, ix = 0, lx, cx;
     Eterm res;
 
@@ -566,7 +568,10 @@ static Eterm hashmap_from_unsorted_array(ErtsHeapFactory* factory,
 	    while(ix < jx) {
 		lx = ix;
 		while(ix < jx && EQ(CAR(list_val(hxns[ix].val)), CAR(list_val(hxns[lx].val)))) {
-		    if (hxns[ix].i > hxns[lx].i) {
+                    if (reject_dupkeys)
+                        return THE_NON_VALUE;
+
+                    if (hxns[ix].i > hxns[lx].i) {
 			lx = ix;
 		    }
 		    ix++;
@@ -1030,7 +1035,7 @@ static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
 	}
 
         factory.p = p;
-	res = hashmap_from_unsorted_array(&factory, hxns, n);
+	res = hashmap_from_unsorted_array(&factory, hxns, n, 0);
 
 	erts_free(ERTS_ALC_T_TMP, (void *) hxns);
 	ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
@@ -1076,7 +1081,7 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args)
     }
 
     factory.p = p;
-    res = hashmap_from_unsorted_array(&factory, hxns, n);
+    res = hashmap_from_unsorted_array(&factory, hxns, n, 0);
 
     erts_free(ERTS_ALC_T_TMP, (void *) hxns);
     ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 35dbed86a9..39d98b9e03 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -102,7 +102,7 @@ int    erts_validate_and_sort_map(map_t* map);
 void   hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
 int    hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
-Eterm  erts_hashmap_from_array(ErtsHeapFactory*, Eterm *leafs, Uint n);
+Eterm  erts_hashmap_from_array(ErtsHeapFactory*, Eterm *leafs, Uint n, int reject_dupkeys);
 const  Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map);
 
 #define erts_hashmap_from_ks_and_vs(P, KS, VS, N) \
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 0e012e9eec..9bcd9a7a50 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -3910,10 +3910,10 @@ dec_term_atom_common:
         next  = (Eterm *) hamt_list->next;
         objp = hamt_list->objp;
 
-	/*SVERK Make it reject duplicate keys */
         *objp = erts_hashmap_from_array(&factory,
                                         hamt_list->leaf_array,
-                                        hamt_list->size);
+                                        hamt_list->size,
+                                        1);
         if (is_non_value(*objp))
             goto error;
 
-- 
cgit v1.2.3


From 7478569d0a7d619d600816f3a75e56922d8ed428 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 10 Mar 2015 18:07:11 +0100
Subject: erts: Tweak over estimation of hashmap size for binary_to_term

Strategy: Calculate an over estimation of heap size that will give
such a low probability for overflow, that "it will not happen".

Scary assumption 1: Uniformly distributed hash values.
Scary assumption 2: Tree size is normally distributed (right?)
---
 erts/emulator/beam/erl_map.c   | 37 ++++++++++++++++++++++++++
 erts/emulator/beam/erl_map.h   |  1 +
 erts/emulator/beam/erl_utils.h |  1 +
 erts/emulator/beam/external.c  | 60 ++++++++++++++++++++++--------------------
 erts/emulator/beam/utils.c     | 11 ++++++++
 5 files changed, 81 insertions(+), 29 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 3ccfc61b08..5eafa69b06 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -2437,6 +2437,43 @@ int erts_validate_and_sort_map(map_t* mp)
     return 1;
 }
 
+/* Really rough estimate of sqrt(x)
+ * Guaranteed not to be less than sqrt(x)
+ */
+static int int_sqrt_ceiling(Uint x)
+{
+    int n;
+
+    if (x <= 2)
+	return x;
+
+    n = erts_fit_in_bits_uint(x-1);
+    if (n & 1) {
+	/* Calc: sqrt(2^n) = 2^(n/2) * sqrt(2) ~= 2^(n/2) * 3 / 2 */
+	return (1 << (n/2 - 1)) * 3;
+    }
+    else {
+	/* Calc: sqrt(2^n) = 2^(n/2) */
+	return 1 << (n / 2);
+    }
+}
+
+Uint hashmap_over_estimated_heap_size(Uint n)
+{
+    /* n is nr of key-value pairs.
+       Average nr of nodes is about n/3.
+       Standard deviation is about sqrt(n)/3.
+       Assuming normal probability distribution,
+       we overestimate nr of nodes by 14 std.devs, which gives a probability
+       for overrun of 1.0e-49 (same magnitude as a git SHA1 collision).
+     */
+    Uint nodes = (n + int_sqrt_ceiling(n)*14) / 3;
+    return (n*2 +     /* leaf cons cells */
+	    n +       /* leaf list terms */
+	    nodes*2); /* headers + parent refs */
+}
+
+
 BIF_RETTYPE erts_debug_map_info_1(BIF_ALIST_1) {
     if (is_hashmap(BIF_ARG_1)) {
 	BIF_RET(hashmap_info(BIF_P,BIF_ARG_1));
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 39d98b9e03..659295184d 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -99,6 +99,7 @@ Eterm  erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value,
 			      Uint *upsz, struct ErtsEStack_ *sp);
 
 int    erts_validate_and_sort_map(map_t* map);
+Uint   hashmap_over_estimated_heap_size(Uint n);
 void   hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
 int    hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index c772a750f1..7cb8972e29 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -113,6 +113,7 @@ void erts_silence_warn_unused_result(long unused);
 
 int erts_fit_in_bits_int64(Sint64);
 int erts_fit_in_bits_int32(Sint32);
+int erts_fit_in_bits_uint(Uint);
 int erts_list_length(Eterm);
 int erts_is_builtin(Eterm, Eterm, int);
 Uint32 make_broken_hash(Eterm);
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 9bcd9a7a50..bd9ad65086 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -2945,13 +2945,11 @@ struct dec_term_hamt_placeholder
     struct dec_term_hamt_placeholder* next;
     Eterm* objp; /* write result here */
     Uint size;   /* nr of leafs */
-    Eterm* leaf_array;
-
-    Eterm _heap_capacity_[1];
+    Eterm leafs[1];
 };
 
-#define DEC_TERM_HAMT_PLACEHOLDER_SIZE(SZ) \
-    (offsetof(struct dec_term_hamt_placeholder, _heap_capacity_) + (SZ))
+#define DEC_TERM_HAMT_PLACEHOLDER_SIZE \
+    (offsetof(struct dec_term_hamt_placeholder, leafs) / sizeof(Eterm))
 
 /* Decode term from external format into *objp.
 ** On failure return NULL and *hpp will be unchanged.
@@ -3646,8 +3644,7 @@ dec_term_atom_common:
                     holder->objp = objp;
                     holder->size = size;
 
-                    hp += DEC_TERM_HAMT_PLACEHOLDER_SIZE(size);
-                    holder->leaf_array = hp;
+                    hp += DEC_TERM_HAMT_PLACEHOLDER_SIZE;
 
                     for (n = size; n; n--) {
                         CDR(hp) = (Eterm) COMPRESS_POINTER(next);
@@ -3899,30 +3896,33 @@ dec_term_atom_common:
 	maps_list  = next;
     }
 
-    while (hamt_list) {
-        ErtsHeapFactory factory;
-        int residue;
-
-        factory.p = NULL;
-        factory.hp = (Eterm*) hamt_list;
-        factory.hp_end = hamt_list->leaf_array;
+    /* Iterate through all the hamts and build tree nodes.
+     */
+    if (hamt_list) {
+        struct dec_term_hamt_placeholder* hamt = hamt_list;
+	ErtsHeapFactory factory;
+
+	factory.p = NULL;
+        factory.hp = hp;
+	/* We assume heap will suffice (see hashmap_over_estimated_heap_size) */
+        factory.hp_end = hp + (ERTS_SWORD_MAX / sizeof(Eterm));
+
+        do {
+	    *hamt->objp = erts_hashmap_from_array(&factory,
+						  hamt->leafs,
+						  hamt->size,
+						  1);
+	    if (is_non_value(*hamt->objp))
+		goto error;
 
-        next  = (Eterm *) hamt_list->next;
-        objp = hamt_list->objp;
+	    hamt_list = hamt->next;
 
-        *objp = erts_hashmap_from_array(&factory,
-                                        hamt_list->leaf_array,
-                                        hamt_list->size,
-                                        1);
-        if (is_non_value(*objp))
-            goto error;
+	    /* Yes, we waste a couple of heap words per hamt
+	       for the temporary placeholder */
+	    *(Eterm*)hamt = make_pos_bignum_header(DEC_TERM_HAMT_PLACEHOLDER_SIZE-1);
+        } while (hamt_list);
 
-        residue = factory.hp_end - factory.hp;
-        if (residue) {
-            ASSERT(residue > 0);
-            *factory.hp = make_pos_bignum_header(residue-1);
-        }
-        hamt_list = (struct dec_term_hamt_placeholder*) next;
+	hp = factory.hp;
     }
 
     if (ctx) {
@@ -4292,6 +4292,8 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
     return 0;
 }
 
+
+
 static Sint
 decoded_size(byte *ep, byte* endp, int internal_tags, B2TContext* ctx)
 {
@@ -4488,7 +4490,7 @@ init_done:
             if (n <= MAP_SMALL_MAP_LIMIT) {
                 heap_size += 3 + n + 1 + n;
             } else {
-                heap_size += DEC_TERM_HAMT_PLACEHOLDER_SIZE(n) + 2*n;
+                heap_size += hashmap_over_estimated_heap_size(n);
             }
 	    break;
 	case STRING_EXT:
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 674dddc86f..66f13461a1 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -352,6 +352,17 @@ int erts_fit_in_bits_int32(Sint32 value)
     return fit_in_bits((Sint64) (Uint32) value, 4);
 }
 
+int erts_fit_in_bits_uint(Uint value)
+{
+#if ERTS_SIZEOF_ETERM == 4
+    return fit_in_bits((Sint64) (Uint32) value, 4);
+#elif ERTS_SIZEOF_ETERM == 8
+    return fit_in_bits(value, 5);
+#else
+# error "No way, Jose"
+#endif
+}
+
 int
 erts_print(int to, void *arg, char *format, ...)
 {
-- 
cgit v1.2.3


From 3fdd4046c4306256233984e289898f24324273f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 10 Mar 2015 09:04:56 +0100
Subject: erts: Add hashmap_iterator_prev to maps

---
 erts/emulator/beam/erl_map.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
 erts/emulator/beam/erl_map.h |  1 +
 2 files changed, 46 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 5eafa69b06..65a31d3680 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -1758,6 +1758,51 @@ Eterm* hashmap_iterator_next(ErtsWStack* s) {
     }
 }
 
+Eterm* hashmap_iterator_prev(ErtsWStack* s) {
+    Eterm node, *ptr, hdr;
+    Uint32 sz,i;
+
+    for (;;) {
+        ASSERT(!WSTACK_ISEMPTY((*s)));
+	node = (Eterm) WSTACK_POP((*s));
+        if (is_non_value(node)) {
+            return NULL;
+        }
+        switch (primary_tag(node)) {
+        case TAG_PRIMARY_LIST:
+            return list_val(node);
+
+        case TAG_PRIMARY_BOXED:
+            ptr = boxed_val(node);
+            hdr = *ptr;
+            ASSERT(is_header(hdr));
+            switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+            case HAMT_SUBTAG_HEAD_ARRAY:
+                ptr++;
+            case HAMT_SUBTAG_NODE_ARRAY:
+                ptr++;
+                i = 0;
+                while(i < 16) { WSTACK_PUSH((*s), (UWord)ptr[i++]); }
+                break;
+            case HAMT_SUBTAG_HEAD_BITMAP:
+                ptr++;
+            case HAMT_SUBTAG_NODE_BITMAP:
+                sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+                ASSERT(sz < 17);
+		i = 0;
+                ptr++;
+                while(i < sz) { WSTACK_PUSH((*s), (UWord)ptr[i++]); }
+                break;
+            default:
+                erl_exit(1, "bad header");
+            }
+            break;
+
+        default:
+            erl_exit(1, "bad hamt node");
+	}
+    }
+}
 const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) {
     Eterm *ptr, hdr;
     Eterm th[2];
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 659295184d..db22d461ce 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -102,6 +102,7 @@ int    erts_validate_and_sort_map(map_t* map);
 Uint   hashmap_over_estimated_heap_size(Uint n);
 void   hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
+Eterm* hashmap_iterator_prev(struct ErtsWStack_* s);
 int    hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
 Eterm  erts_hashmap_from_array(ErtsHeapFactory*, Eterm *leafs, Uint n, int reject_dupkeys);
 const  Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map);
-- 
cgit v1.2.3


From 05169ff8fa0713f3ea969bc27b9d8f58b439863c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 10 Mar 2015 12:57:59 +0100
Subject: erts: Teach hashmaps to match spec compiler

---
 erts/emulator/beam/erl_db_util.c | 222 +++++++++++++++++++++++++++++++++------
 1 file changed, 189 insertions(+), 33 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 748be93fe3..8b5ab16642 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -231,7 +231,8 @@ typedef enum {
     matchConsA, /* Car is below Cdr */
     matchConsB, /* Cdr is below Car (unusual) */
     matchMkTuple,
-    matchMkMap,
+    matchMkFlatMap,
+    matchMkHashMap,
     matchCall0,
     matchCall1,
     matchCall2,
@@ -1424,6 +1425,63 @@ restart:
                     }
                     break;
                 }
+                if (is_hashmap(t)) {
+                    DECLARE_WSTACK(wstack);
+                    Eterm *kv;
+                    num_iters = hashmap_size(t);
+                    if (!structure_checked) {
+                        DMC_PUSH(text, matchMap);
+                        DMC_PUSH(text, num_iters);
+                    }
+                    structure_checked = 0;
+
+                    hashmap_iterator_init(&wstack, t);
+
+                    while ((kv=hashmap_iterator_next(&wstack)) != NULL) {
+                        Eterm key = CAR(kv);
+                        Eterm value = CDR(kv);
+                        if (db_is_variable(key) >= 0) {
+                            if (context.err_info) {
+                                add_dmc_err(context.err_info,
+                                        "Variable found in map key.",
+                                        -1, 0UL, dmcError);
+                            }
+                            DESTROY_WSTACK(wstack);
+                            goto error;
+                        } else if (key == am_Underscore) {
+                            if (context.err_info) {
+                                add_dmc_err(context.err_info,
+                                        "Underscore found in map key.",
+                                        -1, 0UL, dmcError);
+                            }
+                            DESTROY_WSTACK(wstack);
+                            goto error;
+                        }
+                        DMC_PUSH(text, matchKey);
+                        DMC_PUSH(text, dmc_private_copy(&context, key));
+                        {
+                            int old_stack = ++(context.stack_used);
+                            res = dmc_one_term(&context, &heap, &stack, &text,
+                                               value);
+                            ASSERT(res != retFail);
+                            if (res == retRestart) {
+                                DESTROY_WSTACK(wstack);
+                                goto restart;
+                            }
+                            if (old_stack != context.stack_used) {
+                                ASSERT(old_stack + 1 == context.stack_used);
+                                DMC_PUSH(text, matchSwap);
+                            }
+                            if (context.stack_used > context.stack_need) {
+                                context.stack_need = context.stack_used;
+                            }
+                            DMC_PUSH(text, matchPop);
+                            --(context.stack_used);
+                        }
+                    }
+                    DESTROY_WSTACK(wstack);
+                    break;
+                }
 		if (!is_tuple(t)) {
 		    goto simple_term;
 		}
@@ -1946,23 +2004,37 @@ restart:
 	    ++ep;
 	    break;
         case matchMap:
-            if (!is_map_rel(*ep, base)) {
+            if (!is_map_rel(*ep, base) && !is_hashmap_rel(*ep,base)) {
                 FAIL();
             }
             n = *pc++;
-            if (map_get_size(map_val_rel(*ep, base)) < n) {
-                FAIL();
-            }
+            if (is_map_rel(*ep,base)) {
+		if (map_get_size(map_val_rel(*ep, base)) < n) {
+		    FAIL();
+		}
+            } else {
+		ASSERT(is_hashmap_rel(*ep,base));
+		if (hashmap_size_rel(*ep, base) < n) {
+		    FAIL();
+		}
+	    }
             ep = map_val_rel(*ep, base);
             break;
         case matchPushM:
-            if (!is_map_rel(*ep, base)) {
+            if (!is_map_rel(*ep, base) && !is_hashmap_rel(*ep, base)) {
                 FAIL();
             }
             n = *pc++;
-            if (map_get_size(map_val_rel(*ep, base)) < n) {
-                FAIL();
-            }
+            if (is_map_rel(*ep,base)) {
+		if (map_get_size(map_val_rel(*ep, base)) < n) {
+		    FAIL();
+		}
+	    } else {
+		ASSERT(is_hashmap_rel(*ep,base));
+		if (hashmap_size_rel(*ep, base) < n) {
+		    FAIL();
+		}
+	    }
             *sp++ = map_val_rel(*ep++, base);
             break;
         case matchKey:
@@ -2079,7 +2151,7 @@ restart:
 	    }
 	    *esp++ = t;
 	    break;
-        case matchMkMap:
+        case matchMkFlatMap:
             n = *pc++;
             ehp = HAllocX(build_proc, 1 + MAP_HEADER_SIZE + n, HEAP_XTRA);
             t = *ehp++ = *--esp;
@@ -2096,6 +2168,21 @@ restart:
             }
             *esp++ = t;
             break;
+        case matchMkHashMap:
+            n = *pc++;
+            esp -= 2*n;
+            ehp = HAllocX(build_proc, 2*n, HEAP_XTRA);
+            {
+                ErtsHeapFactory factory;
+                Uint ix;
+                factory.p = build_proc;
+                for (ix = 0; ix < 2*n; ix++){
+                    ehp[ix] = esp[ix];
+                }
+                t = erts_hashmap_from_array(&factory, ehp, n, 0);
+            }
+            *esp++ = t;
+            break;
 	case matchCall0:
 	    bif = (Eterm (*)(Process*, ...)) *pc++;
 	    t = (*bif)(build_proc, bif_args);
@@ -3366,7 +3453,6 @@ static DMCRet dmc_one_term(DMCContext *context,
     Uint sz, sz2, sz3;
     Uint i, j;
 
-
     switch (c & _TAG_PRIMARY_MASK) {
     case TAG_PRIMARY_IMMED1:
 	if ((n = db_is_variable(c)) >= 0) { /* variable */
@@ -3460,6 +3546,13 @@ static DMCRet dmc_one_term(DMCContext *context,
             DMC_PUSH(*text, n);
             DMC_PUSH(*stack, c);
             break;
+        case (_TAG_HEADER_HASHMAP >> _TAG_PRIMARY_SIZE):
+            n = hashmap_size(c);
+            DMC_PUSH(*text, matchPushM);
+            ++(context->stack_used);
+            DMC_PUSH(*text, n);
+            DMC_PUSH(*stack, c);
+            break;
 	case (_TAG_HEADER_REF >> _TAG_PRIMARY_SIZE):
 	{
 	    Eterm* ref_val = internal_ref_val(c);
@@ -3745,30 +3838,87 @@ static DMCRet
 dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text,
         Eterm t, int *constant)
 {
-    map_t *m = (map_t *)map_val(t);
-    Eterm *values = map_get_values(m);
-    int nelems = map_get_size(m);
+    int nelems;
     int constant_values;
     DMCRet ret;
+    if (is_map(t)) {
+        map_t *m = (map_t *)map_val(t);
+        Eterm *values = map_get_values(m);
 
-    ret = dmc_array(context, heap, text, values, nelems, &constant_values);
-    if (ret != retOk) {
-        return ret;
-    }
-    if (constant_values) {
-        *constant = 1;
+        nelems = map_get_size(m);
+        ret = dmc_array(context, heap, text, values, nelems, &constant_values);
+
+        if (ret != retOk) {
+            return ret;
+        }
+        if (constant_values) {
+            *constant = 1;
+            return retOk;
+        }
+        DMC_PUSH(*text, matchPushC);
+        DMC_PUSH(*text, dmc_private_copy(context, m->keys));
+        if (++context->stack_used > context->stack_need) {
+            context->stack_need = context->stack_used;
+        }
+        DMC_PUSH(*text, matchMkFlatMap);
+        DMC_PUSH(*text, nelems);
+        context->stack_used -= nelems;
+        *constant = 0;
+        return retOk;
+    } else {
+        DECLARE_WSTACK(wstack);
+        Eterm *kv;
+        int c;
+
+        ASSERT(is_hashmap(t));
+
+        hashmap_iterator_init(&wstack, t);
+        constant_values = 1;
+        nelems = hashmap_size(t);
+
+        while ((kv=hashmap_iterator_prev(&wstack)) != NULL) {
+            if ((ret = dmc_expr(context, heap, text, CDR(kv), &c)) != retOk) {
+                DESTROY_WSTACK(wstack);
+                return ret;
+            }
+            if (!c)
+                constant_values = 0;
+        }
+
+        if (constant_values) {
+            *constant = 1;
+            DESTROY_WSTACK(wstack);
+            return retOk;
+        }
+
+        *constant = 0;
+
+        hashmap_iterator_init(&wstack, t);
+
+        while ((kv=hashmap_iterator_prev(&wstack)) != NULL) {
+            /* push key */
+            if ((ret = dmc_expr(context, heap, text, CAR(kv), &c)) != retOk) {
+                DESTROY_WSTACK(wstack);
+                return ret;
+            }
+            if (c) {
+                do_emit_constant(context, text, CAR(kv));
+            }
+            /* push value */
+            if ((ret = dmc_expr(context, heap, text, CDR(kv), &c)) != retOk) {
+                DESTROY_WSTACK(wstack);
+                return ret;
+            }
+            if (c) {
+                do_emit_constant(context, text, CDR(kv));
+            }
+        }
+        DMC_PUSH(*text, matchMkHashMap);
+        DMC_PUSH(*text, nelems);
+        context->stack_used -= nelems;
+        DESTROY_WSTACK(wstack);
         return retOk;
     }
-    DMC_PUSH(*text, matchPushC);
-    DMC_PUSH(*text, dmc_private_copy(context, m->keys));
-    if (++context->stack_used > context->stack_need) {
-        context->stack_need = context->stack_used;
-    }
-    DMC_PUSH(*text, matchMkMap);
-    DMC_PUSH(*text, nelems);
-    context->stack_used -= nelems;
-    *constant = 0;
-    return retOk;
 }
 
 static DMCRet dmc_whole_expression(DMCContext *context,
@@ -4765,7 +4915,7 @@ static DMCRet dmc_expr(DMCContext *context,
 	    return ret;
 	break;
     case TAG_PRIMARY_BOXED:
-        if (is_map(t)) {
+        if (is_map(t) || is_hashmap(t)) {
             return dmc_map(context, heap, text, t, constant);
         }
 	if (!is_tuple(t)) {
@@ -5462,11 +5612,17 @@ void db_match_dis(Binary *bp)
 	    ++t;
 	    erts_printf("MkTuple\t%beu\n", n);
 	    break;
-        case matchMkMap:
+        case matchMkFlatMap:
+            ++t;
+            n = *t;
+            ++t;
+            erts_printf("MkFlatMap\t%beu\n", n);
+            break;
+        case matchMkHashMap:
             ++t;
             n = *t;
             ++t;
-            erts_printf("MkMapA\t%beu\n", n);
+            erts_printf("MkHashMap\t%beu\n", n);
             break;
 	case matchOr:
 	    ++t;
-- 
cgit v1.2.3


From 693b99697950661cea7dbf2b504a4cf9a12a0880 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 10 Mar 2015 16:00:39 +0100
Subject: erts: Enhance match spec tests for maps

---
 erts/emulator/test/match_spec_SUITE.erl | 35 +++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index b231c2bbd9..d3c884689f 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -924,6 +924,7 @@ maps(Config) when is_list(Config) ->
                                table),
     {ok,#{foo := 3},[],[]} =
         erlang:match_spec_test({}, [{{},[],[#{foo => {'+',1,2}}]}], table),
+
     {ok,"camembert",[],[]} =
         erlang:match_spec_test(#{b => "camembert",c => "cabécou"},
                                [{#{b => '$1',c => "cabécou"},[],['$1']}], table),
@@ -932,8 +933,42 @@ maps(Config) when is_list(Config) ->
         erlang:match_spec_test(#{<<"b">> =>"camembert","c"=>"cabécou", "wat"=>"hi", b=><<"other">>},
                                [{#{<<"b">> => '$1',"wat" => '$2'},[],[#{a=>'$1',b=>'$2'}]}],
                                table),
+    %% large maps
+
+    Ls0 = [{I,<>}||I <- lists:seq(1,415)],
+    M0  = maps:from_list(Ls0),
+    M1  = #{a=>1,b=>2,c=>3,d=>4},
+
+    R1  = M0#{263 := #{ a=> 3 }},
+    Ms1 = [{M1#{c:='$1'},[],[M0#{263 := #{a => '$1'}}]}],
+
+    {ok,R1,[],[]} = erlang:match_spec_test(M1,Ms1,table),
+
+    Ms2 = [{M0#{63:='$1', 19:='$2'},[],[M0#{19:='$1', 63:='$2'}]}],
+    R2  = M0#{63 := maps:get(19,M0), 19 := maps:get(63,M0) },
+    {ok,R2,[],[]} = erlang:match_spec_test(M0,Ms2,table),
+
+    ok = maps_check_loop(M1),
+    ok = maps_check_loop(M0),
+    M2 = maps:from_list([{integer_to_list(K),V} || {K,V} <- Ls0]),
+    ok = maps_check_loop(M2),
     ok.
 
+maps_check_loop(M) ->
+    Ks = maps:keys(M),
+    maps_check_loop(M,M,M,M,Ks,lists:reverse(Ks),1).
+
+maps_check_loop(Orig,M0,MsM0,Rm0,[K|Ks],[Rk|Rks],Ix) ->
+    MsK  = list_to_atom([$$]++integer_to_list(Ix)),
+    MsM1 = MsM0#{K := MsK},
+    Rm1  = Rm0#{Rk := MsK},
+    M1   = M0#{Rk  := maps:get(K,MsM0)},
+    Ms   = [{MsM1,[],[Rm1]}],
+    {ok,M1,[],[]} = erlang:match_spec_test(Orig,Ms,table),
+    maps_check_loop(Orig,M1,MsM1,Rm1,Ks,Rks,Ix+1);
+maps_check_loop(_,_,_,_,[],[],_) -> ok.
+
+
 empty_list(Config) when is_list(Config) ->
     Val=[{'$1',[], [{message,'$1'},{message,{caller}},{return_trace}]}],
      %% Did crash debug VM in faulty assert:
-- 
cgit v1.2.3


From 27e57aa05354b743b735a41716c0e3af18f2843e Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 10 Mar 2015 19:03:19 +0100
Subject: erts: Refactor maps naming convention

flatmap: Small map
hashmap: Large map
map:     flatmap or hashmap
---
 erts/emulator/beam/beam_emu.c        |  90 ++++++++--------
 erts/emulator/beam/copy.c            |  10 +-
 erts/emulator/beam/erl_bif_guard.c   |   6 +-
 erts/emulator/beam/erl_bif_op.c      |   2 +-
 erts/emulator/beam/erl_db_util.c     |  50 ++++-----
 erts/emulator/beam/erl_gc.h          |   2 +-
 erts/emulator/beam/erl_map.c         | 196 +++++++++++++++++------------------
 erts/emulator/beam/erl_map.h         |  34 +++---
 erts/emulator/beam/erl_nif.c         |  58 +++++------
 erts/emulator/beam/erl_printf_term.c |   8 +-
 erts/emulator/beam/external.c        |  24 ++---
 erts/emulator/beam/io.c              |   8 +-
 erts/emulator/beam/utils.c           |  36 +++----
 13 files changed, 262 insertions(+), 262 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index d45aa93f73..10ae3ed77c 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -699,7 +699,7 @@ void** beam_ops;
         Fail; 								  \
     }
 
-#define IsMap(Src, Fail) if (is_not_map(Src) && is_not_hashmap(Src)) { Fail; }
+#define IsMap(Src, Fail) if (is_not_flatmap(Src) && is_not_hashmap(Src)) { Fail; }
 
 #define HasMapField(Src, Key, Fail) if (has_not_map_field(Src, Key)) { Fail; }
 
@@ -2392,7 +2392,7 @@ void process_main(void)
  }
 
  OpCase(i_has_map_fields_fsI): {
-    map_t* mp;
+    flatmap_t* mp;
     Eterm map;
     Eterm field;
     Eterm *ks;
@@ -2417,17 +2417,17 @@ void process_main(void)
 	goto has_map_fields_ok;
     }
 
-    ASSERT(is_map(map));
+    ASSERT(is_flatmap(map));
 
-    mp = (map_t *)map_val(map);
-    sz = map_get_size(mp);
+    mp = (flatmap_t *)flatmap_val(map);
+    sz = flatmap_get_size(mp);
 
     if (sz == 0) {
 	SET_I((BeamInstr *) Arg(0));
 	goto has_map_fields_fail;
     }
 
-    ks = map_get_keys(mp);
+    ks = flatmap_get_keys(mp);
 
     ASSERT(n>0);
 
@@ -2484,21 +2484,21 @@ do {								\
     n  = (Uint)Arg(2) / 2;
     fs = &Arg(3); /* pattern fields and target registers */
 
-    if (is_map(map)) {
-	map_t *mp;
+    if (is_flatmap(map)) {
+	flatmap_t *mp;
 	Eterm *ks;
 	Eterm *vs;
 
-	mp = (map_t *)map_val(map);
-	sz = map_get_size(mp);
+	mp = (flatmap_t *)flatmap_val(map);
+	sz = flatmap_get_size(mp);
 
 	if (sz == 0) {
 	    SET_I((BeamInstr *) Arg(0));
 	    goto get_map_elements_fail;
 	}
 
-	ks = map_get_keys(mp);
-	vs = map_get_values(mp);
+	ks = flatmap_get_keys(mp);
+	vs = flatmap_get_values(mp);
 
 	while(sz) {
 	    if (EQ((Eterm)*fs,*ks)) {
@@ -6473,15 +6473,15 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free)
 static int has_not_map_field(Eterm map, Eterm key)
 {
     Uint32 hx;
-    if (is_map(map)) {
-	map_t* mp;
+    if (is_flatmap(map)) {
+	flatmap_t* mp;
 	Eterm* keys;
 	Uint i;
 	Uint n;
 
-	mp   = (map_t *)map_val(map);
-	keys = map_get_keys(mp);
-	n    = map_get_size(mp);
+	mp   = (flatmap_t *)flatmap_val(map);
+	keys = flatmap_get_keys(mp);
+	n    = flatmap_get_size(mp);
 	if (is_immed(key)) {
 	    for (i = 0; i < n; i++) {
 		if (keys[i] == key) {
@@ -6506,16 +6506,16 @@ static Eterm get_map_element(Eterm map, Eterm key)
 {
     Uint32 hx;
     const Eterm *vs;
-    if (is_map(map)) {
-	map_t *mp;
+    if (is_flatmap(map)) {
+	flatmap_t *mp;
 	Eterm *ks;
 	Uint i;
 	Uint n;
 
-	mp = (map_t *)map_val(map);
-	ks = map_get_keys(mp);
-	vs = map_get_values(mp);
-	n  = map_get_size(mp);
+	mp = (flatmap_t *)flatmap_val(map);
+	ks = flatmap_get_keys(mp);
+	vs = flatmap_get_values(mp);
+	n  = flatmap_get_size(mp);
 	if (is_immed(key)) {
 	    for (i = 0; i < n; i++) {
 		if (ks[i] == key) {
@@ -6567,7 +6567,7 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
     Eterm *mhp,*thp;
     Eterm *E;
     BeamInstr *ptr;
-    map_t *mp;
+    flatmap_t *mp;
     ErtsHeapFactory factory;
 
     ptr = &Arg(4);
@@ -6602,7 +6602,7 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
     keys   = make_tuple(thp);
     *thp++ = make_arityval(n/2);
 
-    mp = (map_t *)mhp; mhp += MAP_HEADER_SIZE;
+    mp = (flatmap_t *)mhp; mhp += MAP_HEADER_SIZE;
     mp->thing_word = MAP_HEADER;
     mp->size = n/2;
     mp->keys = keys;
@@ -6612,7 +6612,7 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
 	GET_TERM(*ptr++, *mhp++);
     }
     p->htop = mhp;
-    return make_map(mp);
+    return make_flatmap(mp);
 }
 
 static Eterm
@@ -6622,7 +6622,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
     Uint num_old;
     Uint num_updates;
     Uint need;
-    map_t *old_mp, *mp;
+    flatmap_t *old_mp, *mp;
     Eterm res;
     Eterm* hp;
     Eterm* E;
@@ -6635,7 +6635,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
     new_p = &Arg(5);
     num_updates = Arg(4) / 2;
 
-    if (is_not_map(map)) {
+    if (is_not_flatmap(map)) {
 	Uint32 hx;
 	Eterm val;
 
@@ -6668,8 +6668,8 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
 	return res;
     }
 
-    old_mp  = (map_t *) map_val(map);
-    num_old = map_get_size(old_mp);
+    old_mp  = (flatmap_t *) flatmap_val(map);
+    num_old = flatmap_get_size(old_mp);
 
     /*
      * If the old map is empty, create a new map.
@@ -6690,7 +6690,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
 	reg[live] = map;
 	erts_garbage_collect(p, need, reg, live+1);
 	map      = reg[live];
-	old_mp   = (map_t *)map_val(map);
+	old_mp   = (flatmap_t *)flatmap_val(map);
     }
 
     /*
@@ -6721,14 +6721,14 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
     kp = p->htop + 1;		/* Point to first key */
     hp = kp + num_old + num_updates;
 
-    res = make_map(hp);
-    mp = (map_t *)hp;
+    res = make_flatmap(hp);
+    mp = (flatmap_t *)hp;
     hp += MAP_HEADER_SIZE;
     mp->thing_word = MAP_HEADER;
     mp->keys = make_tuple(kp-1);
 
-    old_vals = map_get_values(old_mp);
-    old_keys = map_get_keys(old_mp);
+    old_vals = flatmap_get_values(old_mp);
+    old_keys = flatmap_get_keys(old_mp);
 
     GET_TERM(*new_p, new_key);
     n = num_updates;
@@ -6820,7 +6820,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
 
     /* The expensive case, need to build a hashmap */
     if (n > MAP_SMALL_MAP_LIMIT) {
-	res = erts_hashmap_from_ks_and_vs(p,map_get_keys(mp),map_get_values(mp),n);
+	res = erts_hashmap_from_ks_and_vs(p,flatmap_get_keys(mp),flatmap_get_values(mp),n);
 	if (p->mbuf) {
 	    Uint live = Arg(3);
 	    reg[live] = res;
@@ -6842,7 +6842,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
     Uint i;
     Uint num_old;
     Uint need;
-    map_t *old_mp, *mp;
+    flatmap_t *old_mp, *mp;
     Eterm res;
     Eterm* hp;
     Eterm* E;
@@ -6855,7 +6855,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
     n = Arg(4) / 2;		/* Number of values to be updated */
     ASSERT(n > 0);
 
-    if (is_not_map(map)) {
+    if (is_not_flatmap(map)) {
 	Uint32 hx;
 	Eterm val;
 
@@ -6891,8 +6891,8 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
 	return res;
     }
 
-    old_mp = (map_t *) map_val(map);
-    num_old = map_get_size(old_mp);
+    old_mp = (flatmap_t *) flatmap_val(map);
+    num_old = flatmap_get_size(old_mp);
 
     /*
      * If the old map is empty, create a new map.
@@ -6912,7 +6912,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
 	reg[live] = map;
 	erts_garbage_collect(p, need, reg, live+1);
 	map      = reg[live];
-	old_mp   = (map_t *)map_val(map);
+	old_mp   = (flatmap_t *)flatmap_val(map);
     }
 
     /*
@@ -6922,11 +6922,11 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
     hp = p->htop;
     E = p->stop;
 
-    old_vals = map_get_values(old_mp);
-    old_keys = map_get_keys(old_mp);
+    old_vals = flatmap_get_values(old_mp);
+    old_keys = flatmap_get_keys(old_mp);
 
-    res = make_map(hp);
-    mp = (map_t *)hp;
+    res = make_flatmap(hp);
+    mp = (flatmap_t *)hp;
     hp += MAP_HEADER_SIZE;
     mp->thing_word = MAP_HEADER;
     mp->size = num_old;
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index 5901c00d0a..83214aca19 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -186,10 +186,10 @@ Uint size_object(Eterm obj)
 		case MAP_SUBTAG:
 		    {
 			Uint n;
-			map_t *mp;
-			mp  = (map_t*)map_val_rel(obj,base);
+			flatmap_t *mp;
+			mp  = (flatmap_t*)flatmap_val_rel(obj,base);
 			ptr = (Eterm *)mp;
-			n   = map_get_size(mp) + 1;
+			n   = flatmap_get_size(mp) + 1;
 			sum += n + 2;
 			ptr += 2; /* hdr + size words */
 			while (n--) {
@@ -371,8 +371,8 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
 		break;
 	    case MAP_SUBTAG:
 		{
-		    i = map_get_size(objp) + 3;
-		    *argp = make_map_rel(htop, dst_base);
+		    i = flatmap_get_size(objp) + 3;
+		    *argp = make_flatmap_rel(htop, dst_base);
 		    while (i--) {
 			*htop++ = *objp++;
 		    }
diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c
index bc0891422b..e7d84ebda1 100644
--- a/erts/emulator/beam/erl_bif_guard.c
+++ b/erts/emulator/beam/erl_bif_guard.c
@@ -461,9 +461,9 @@ Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live)
     Eterm arg = reg[live];
     Eterm* hp;
     Uint size;
-    if (is_map(arg)) {
-	map_t *mp = (map_t*)map_val(arg);
-	size = map_get_size(mp);
+    if (is_flatmap(arg)) {
+	flatmap_t *mp = (flatmap_t*)flatmap_val(arg);
+	size = flatmap_get_size(mp);
     } else if (is_hashmap(arg)) {
 	size = hashmap_size(arg);
     } else {
diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c
index 11c6c9e556..af9b7bd908 100644
--- a/erts/emulator/beam/erl_bif_op.c
+++ b/erts/emulator/beam/erl_bif_op.c
@@ -324,7 +324,7 @@ BIF_RETTYPE is_record_3(BIF_ALIST_3)
 	
 BIF_RETTYPE is_map_1(BIF_ALIST_1)
 {
-    if (is_map(BIF_ARG_1) || is_hashmap(BIF_ARG_1)) {
+    if (is_flatmap(BIF_ARG_1) || is_hashmap(BIF_ARG_1)) {
 	BIF_RET(am_true);
     }
     BIF_RET(am_false);
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 8b5ab16642..cca3f381a1 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -1377,15 +1377,15 @@ restart:
 	for (;;) {
 	    switch (t & _TAG_PRIMARY_MASK) {
 	    case TAG_PRIMARY_BOXED:
-                if (is_map(t)) {
-                    num_iters = map_get_size(map_val(t));
+                if (is_flatmap(t)) {
+                    num_iters = flatmap_get_size(flatmap_val(t));
                     if (!structure_checked) {
                         DMC_PUSH(text, matchMap);
                         DMC_PUSH(text, num_iters);
                     }
                     structure_checked = 0;
                     for (i = 0; i < num_iters; ++i) {
-                        Eterm key = map_get_keys(map_val(t))[i];
+                        Eterm key = flatmap_get_keys(flatmap_val(t))[i];
                         if (db_is_variable(key) >= 0) {
                             if (context.err_info) {
                                 add_dmc_err(context.err_info,
@@ -1405,7 +1405,7 @@ restart:
                         DMC_PUSH(text, dmc_private_copy(&context, key));
                         {
                             int old_stack = ++(context.stack_used);
-                            Eterm value = map_get_values(map_val(t))[i];
+                            Eterm value = flatmap_get_values(flatmap_val(t))[i];
                             res = dmc_one_term(&context, &heap, &stack, &text,
                                                value);
                             ASSERT(res != retFail);
@@ -2004,12 +2004,12 @@ restart:
 	    ++ep;
 	    break;
         case matchMap:
-            if (!is_map_rel(*ep, base) && !is_hashmap_rel(*ep,base)) {
+            if (!is_flatmap_rel(*ep, base) && !is_hashmap_rel(*ep,base)) {
                 FAIL();
             }
             n = *pc++;
-            if (is_map_rel(*ep,base)) {
-		if (map_get_size(map_val_rel(*ep, base)) < n) {
+            if (is_flatmap_rel(*ep,base)) {
+		if (flatmap_get_size(flatmap_val_rel(*ep, base)) < n) {
 		    FAIL();
 		}
             } else {
@@ -2018,15 +2018,15 @@ restart:
 		    FAIL();
 		}
 	    }
-            ep = map_val_rel(*ep, base);
+            ep = flatmap_val_rel(*ep, base);
             break;
         case matchPushM:
-            if (!is_map_rel(*ep, base) && !is_hashmap_rel(*ep, base)) {
+            if (!is_flatmap_rel(*ep, base) && !is_hashmap_rel(*ep, base)) {
                 FAIL();
             }
             n = *pc++;
-            if (is_map_rel(*ep,base)) {
-		if (map_get_size(map_val_rel(*ep, base)) < n) {
+            if (is_flatmap_rel(*ep,base)) {
+		if (flatmap_get_size(flatmap_val_rel(*ep, base)) < n) {
 		    FAIL();
 		}
 	    } else {
@@ -2035,11 +2035,11 @@ restart:
 		    FAIL();
 		}
 	    }
-            *sp++ = map_val_rel(*ep++, base);
+            *sp++ = flatmap_val_rel(*ep++, base);
             break;
         case matchKey:
             t = (Eterm) *pc++;
-            tp = erts_maps_get_rel(t, make_map_rel(ep, base), base);
+            tp = erts_maps_get_rel(t, make_flatmap_rel(ep, base), base);
             if (!tp) {
                 FAIL();
             }
@@ -2156,12 +2156,12 @@ restart:
             ehp = HAllocX(build_proc, 1 + MAP_HEADER_SIZE + n, HEAP_XTRA);
             t = *ehp++ = *--esp;
             {
-                map_t *m = (map_t *)ehp;
+                flatmap_t *m = (flatmap_t *)ehp;
                 m->thing_word = MAP_HEADER;
                 m->size = n;
                 m->keys = t;
             }
-            t = make_map(ehp);
+            t = make_flatmap(ehp);
             ehp += MAP_HEADER_SIZE;
             while (n--) {
                 *ehp++ = *--esp;
@@ -3373,10 +3373,10 @@ int db_has_variable(Eterm node) {
 		while(arity--) {
 		    ESTACK_PUSH(s,*(++tuple));
 		}
-            } else if (is_map(node)) {
-                Eterm *values = map_get_values(map_val(node));
-                int size = map_get_size(map_val(node));
-                ESTACK_PUSH(s, ((map_t *) map_val(node))->keys);
+            } else if (is_flatmap(node)) {
+                Eterm *values = flatmap_get_values(flatmap_val(node));
+                Uint size = flatmap_get_size(flatmap_val(node));
+                ESTACK_PUSH(s, ((flatmap_t *) flatmap_val(node))->keys);
                 while (size--) {
                     ESTACK_PUSH(s, *(values++));
                 }
@@ -3540,7 +3540,7 @@ static DMCRet dmc_one_term(DMCContext *context,
 	    DMC_PUSH(*stack, c);
 	    break;
         case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE):
-            n = map_get_size(map_val(c));
+            n = flatmap_get_size(flatmap_val(c));
             DMC_PUSH(*text, matchPushM);
             ++(context->stack_used);
             DMC_PUSH(*text, n);
@@ -3841,11 +3841,11 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text,
     int nelems;
     int constant_values;
     DMCRet ret;
-    if (is_map(t)) {
-        map_t *m = (map_t *)map_val(t);
-        Eterm *values = map_get_values(m);
+    if (is_flatmap(t)) {
+        flatmap_t *m = (flatmap_t *)flatmap_val(t);
+        Eterm *values = flatmap_get_values(m);
 
-        nelems = map_get_size(m);
+        nelems = flatmap_get_size(m);
         ret = dmc_array(context, heap, text, values, nelems, &constant_values);
 
         if (ret != retOk) {
@@ -4915,7 +4915,7 @@ static DMCRet dmc_expr(DMCContext *context,
 	    return ret;
 	break;
     case TAG_PRIMARY_BOXED:
-        if (is_map(t) || is_hashmap(t)) {
+        if (is_flatmap(t) || is_hashmap(t)) {
             return dmc_map(context, heap, text, t, constant);
         }
 	if (!is_tuple(t)) {
diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h
index 3fec553684..8afcb060a1 100644
--- a/erts/emulator/beam/erl_gc.h
+++ b/erts/emulator/beam/erl_gc.h
@@ -55,7 +55,7 @@ do {                                                                    \
     nelts = header_arity(HDR);                                          \
     switch ((HDR) & _HEADER_SUBTAG_MASK) {                              \
     case SUB_BINARY_SUBTAG: nelts++; break;                             \
-    case MAP_SUBTAG: nelts+=map_get_size(PTR) + 1; break;               \
+    case MAP_SUBTAG: nelts+=flatmap_get_size(PTR) + 1; break;               \
     case HASHMAP_SUBTAG: nelts=hashmap_bitcount(MAP_HEADER_VAL(HDR));   \
 	 nelts += is_hashmap_header_head(HDR) ? 1 : 0; break;           \
     case FUN_SUBTAG: nelts+=((ErlFunThing*)(PTR))->num_free+1; break;   \
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 65a31d3680..ca43baf1a6 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -78,7 +78,7 @@ typedef struct {
 } hxnode_t;
 
 
-static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB);
+static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args);
 static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB);
 static Eterm hashmap_to_list(Process *p, Eterm map);
@@ -101,11 +101,11 @@ static int hxnodecmpkey(hxnode_t* a, hxnode_t* b);
  */
 
 BIF_RETTYPE map_size_1(BIF_ALIST_1) {
-    if (is_map(BIF_ARG_1)) {
+    if (is_flatmap(BIF_ARG_1)) {
 	Eterm *hp;
 	Uint hsz  = 0;
-	map_t *mp = (map_t*)map_val(BIF_ARG_1);
-	Uint n    = map_get_size(mp);
+	flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1);
+	Uint n    = flatmap_get_size(mp);
 
 	erts_bld_uint(NULL, &hsz, n);
 	hp = HAlloc(BIF_P, hsz);
@@ -128,15 +128,15 @@ BIF_RETTYPE map_size_1(BIF_ALIST_1) {
 /* maps:to_list/1 */
 
 BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) {
-    if (is_map(BIF_ARG_1)) {
+    if (is_flatmap(BIF_ARG_1)) {
 	Uint n;
 	Eterm* hp;
 	Eterm *ks,*vs, res, tup;
-	map_t *mp = (map_t*)map_val(BIF_ARG_1);
+	flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1);
 
-	ks  = map_get_keys(mp);
-	vs  = map_get_values(mp);
-	n   = map_get_size(mp);
+	ks  = flatmap_get_keys(mp);
+	vs  = flatmap_get_values(mp);
+	n   = flatmap_get_size(mp);
 	hp  = HAlloc(BIF_P, (2 + 3) * n);
 	res = NIL;
 
@@ -165,20 +165,20 @@ erts_maps_get(Eterm key, Eterm map)
 #endif
 {
     Uint32 hx;
-    if (is_map(map)) {
+    if (is_flatmap(map)) {
 	Eterm *ks, *vs;
-	map_t *mp;
+	flatmap_t *mp;
 	Uint n, i;
 
-	mp  = (map_t *)map_val_rel(map, map_base);
-	n   = map_get_size(mp);
+	mp  = (flatmap_t *)flatmap_val_rel(map, map_base);
+	n   = flatmap_get_size(mp);
 
 	if (n == 0) {
 	    return NULL;
 	}
 
 	ks  = (Eterm *)tuple_val_rel(mp->keys, map_base) + 1;
-	vs  = map_get_values(mp);
+	vs  = flatmap_get_values(mp);
 
 	if (is_immed(key)) {
 	    for (i = 0; i < n; i++) {
@@ -202,7 +202,7 @@ erts_maps_get(Eterm key, Eterm map)
 }
 
 BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
-    if (is_map(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
+    if (is_flatmap(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
         Eterm *hp, res;
         const Eterm *value;
 
@@ -226,7 +226,7 @@ BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
  */
 
 BIF_RETTYPE maps_get_2(BIF_ALIST_2) {
-    if (is_map(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
+    if (is_flatmap(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
 	Eterm *hp;
         Eterm error;
         const Eterm *value;
@@ -289,7 +289,7 @@ error:
 static Eterm map_from_validated_list(Process *p, Eterm list, Uint size) {
     Eterm *kv, item = list;
     Eterm *hp, *thp,*vs, *ks, keys, res;
-    map_t *mp;
+    flatmap_t *mp;
     Uint  unused_size = 0;
     Sint  c = 0;
     Sint  idx = 0;
@@ -301,8 +301,8 @@ static Eterm map_from_validated_list(Process *p, Eterm list, Uint size) {
     *hp++ = make_arityval(size);
     ks    = hp;
     hp   += size;
-    mp    = (map_t*)hp;
-    res   = make_map(mp);
+    mp    = (flatmap_t*)hp;
+    res   = make_flatmap(mp);
     hp   += MAP_HEADER_SIZE;
     vs    = hp;
 
@@ -427,7 +427,7 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
     if (hashmap_size(res) <= MAP_SMALL_MAP_LIMIT) {
         DECLARE_WSTACK(wstack);
 	Eterm *kv, *ks, *vs;
-	map_t *mp;
+	flatmap_t *mp;
 	Eterm keys;
         Uint n = hashmap_size(res);
 
@@ -437,7 +437,7 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
 	*hp++ = make_arityval(n);
 	ks    = hp;
 	hp   += n;
-	mp    = (map_t*)hp;
+	mp    = (flatmap_t*)hp;
 	hp   += MAP_HEADER_SIZE;
 	vs    = hp;
 
@@ -453,10 +453,10 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
 	}
 
 	/* it cannot have multiple keys */
-	erts_validate_and_sort_map(mp);
+	erts_validate_and_sort_flatmap(mp);
 
 	DESTROY_WSTACK(wstack);
-	return make_map(mp);
+	return make_flatmap(mp);
     }
 
     return res;
@@ -877,7 +877,7 @@ static int hxnodecmp(hxnode_t *a, hxnode_t *b) {
 /* maps:is_key/2 */
 
 BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) {
-    if (is_map(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
+    if (is_flatmap(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
 	BIF_RET(erts_maps_get(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
     }
     BIF_ERROR(BIF_P, BADARG);
@@ -886,19 +886,19 @@ BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) {
 /* maps:keys/1 */
 
 BIF_RETTYPE maps_keys_1(BIF_ALIST_1) {
-    if (is_map(BIF_ARG_1)) {
+    if (is_flatmap(BIF_ARG_1)) {
 	Eterm *hp, *ks, res = NIL;
-	map_t *mp;
+	flatmap_t *mp;
 	Uint n;
 
-	mp  = (map_t*)map_val(BIF_ARG_1);
-	n   = map_get_size(mp);
+	mp  = (flatmap_t*)flatmap_val(BIF_ARG_1);
+	n   = flatmap_get_size(mp);
 
 	if (n == 0)
 	    BIF_RET(res);
 
 	hp  = HAlloc(BIF_P, (2 * n));
-	ks  = map_get_keys(mp);
+	ks  = flatmap_get_keys(mp);
 
 	while(n--) {
 	    res = CONS(hp, ks[n], res); hp += 2;
@@ -913,9 +913,9 @@ BIF_RETTYPE maps_keys_1(BIF_ALIST_1) {
 /* maps:merge/2 */
 
 BIF_RETTYPE maps_merge_2(BIF_ALIST_2) {
-    if (is_map(BIF_ARG_1)) {
-	if (is_map(BIF_ARG_2)) {
-	    BIF_RET(map_merge(BIF_P, BIF_ARG_1, BIF_ARG_2));
+    if (is_flatmap(BIF_ARG_1)) {
+	if (is_flatmap(BIF_ARG_2)) {
+	    BIF_RET(flatmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2));
 	} else if (is_hashmap(BIF_ARG_2)) {
 	    /* Will always become a tree */
 	    BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_1, BIF_ARG_2, 0));
@@ -923,7 +923,7 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) {
     } else if (is_hashmap(BIF_ARG_1)) {
 	if (is_hashmap(BIF_ARG_2)) {
 	    BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2));
-	} else if (is_map(BIF_ARG_2)) {
+	} else if (is_flatmap(BIF_ARG_2)) {
 	    /* Will always become a tree */
 	    BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_2, BIF_ARG_1, 1));
 	}
@@ -931,18 +931,18 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
-static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
+static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
     Eterm *hp,*thp;
     Eterm tup;
     Eterm *ks,*vs,*ks1,*vs1,*ks2,*vs2;
-    map_t *mp1,*mp2,*mp_new;
+    flatmap_t *mp1,*mp2,*mp_new;
     Uint n,n1,n2,i1,i2,need,unused_size=0;
     int c = 0;
 
-    mp1  = (map_t*)map_val(nodeA);
-    mp2  = (map_t*)map_val(nodeB);
-    n1   = map_get_size(mp1);
-    n2   = map_get_size(mp2);
+    mp1  = (flatmap_t*)flatmap_val(nodeA);
+    mp2  = (flatmap_t*)flatmap_val(nodeB);
+    n1   = flatmap_get_size(mp1);
+    n2   = flatmap_get_size(mp2);
 
     need = MAP_HEADER_SIZE + 1 + 2*(n1 + n2);
 
@@ -950,7 +950,7 @@ static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
     thp    = hp;
     tup    = make_tuple(thp);
     ks     = hp + 1; hp += 1 + n1 + n2;
-    mp_new = (map_t*)hp; hp += MAP_HEADER_SIZE;
+    mp_new = (flatmap_t*)hp; hp += MAP_HEADER_SIZE;
     vs     = hp; hp += n1 + n2;
 
     mp_new->thing_word = MAP_HEADER;
@@ -958,10 +958,10 @@ static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
     mp_new->keys = tup;
 
     i1  = 0; i2 = 0;
-    ks1 = map_get_keys(mp1);
-    vs1 = map_get_values(mp1);
-    ks2 = map_get_keys(mp2);
-    vs2 = map_get_values(mp2);
+    ks1 = flatmap_get_keys(mp1);
+    vs1 = flatmap_get_values(mp1);
+    ks2 = flatmap_get_keys(mp2);
+    vs2 = flatmap_get_values(mp2);
 
     while(i1 < n1 && i2 < n2) {
 	c = CMP_TERM(ks1[i1],ks2[i2]);
@@ -1018,8 +1018,8 @@ static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
 	hxnode_t *hxns;
         ErtsHeapFactory factory;
 
-	ks = map_get_keys(mp_new);
-	vs = map_get_values(mp_new);
+	ks = flatmap_get_keys(mp_new);
+	vs = flatmap_get_values(mp_new);
 
 	hp = HAlloc(p, 2 * n);
 
@@ -1045,12 +1045,12 @@ static Eterm map_merge(Process *p, Eterm nodeA, Eterm nodeB) {
 
     mp_new->size = n;
 
-    return make_map(mp_new);
+    return make_flatmap(mp_new);
 }
 
 static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args) {
     Eterm *ks, *vs, *hp, res;
-    map_t *mp;
+    flatmap_t *mp;
     Uint n, i;
     hxnode_t *hxns;
     Uint32 sw, hx;
@@ -1058,14 +1058,14 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args)
 
     /* convert flat to tree */
 
-    ASSERT(is_map(flat));
+    ASSERT(is_flatmap(flat));
     ASSERT(is_hashmap(tree));
 
-    mp = (map_t*)map_val(flat);
-    n  = map_get_size(mp);
+    mp = (flatmap_t*)flatmap_val(flat);
+    n  = flatmap_get_size(mp);
 
-    ks = map_get_keys(mp);
-    vs = map_get_values(mp);
+    ks = flatmap_get_keys(mp);
+    vs = flatmap_get_values(mp);
 
     hp = HAlloc(p, 2 * n);
 
@@ -1343,24 +1343,24 @@ int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp)
 BIF_RETTYPE maps_new_0(BIF_ALIST_0) {
     Eterm* hp;
     Eterm tup;
-    map_t *mp;
+    flatmap_t *mp;
 
     hp    = HAlloc(BIF_P, (MAP_HEADER_SIZE + 1));
     tup   = make_tuple(hp);
     *hp++ = make_arityval(0);
 
-    mp    = (map_t*)hp;
+    mp    = (flatmap_t*)hp;
     mp->thing_word = MAP_HEADER;
     mp->size = 0;
     mp->keys = tup;
 
-    BIF_RET(make_map(mp));
+    BIF_RET(make_flatmap(mp));
 }
 
 /* maps:put/3 */
 
 BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
-    if (is_map(BIF_ARG_3) || is_hashmap(BIF_ARG_3)) {
+    if (is_flatmap(BIF_ARG_3) || is_hashmap(BIF_ARG_3)) {
 	BIF_RET(erts_maps_put(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3));
     }
     BIF_ERROR(BIF_P, BADARG);
@@ -1370,23 +1370,23 @@ BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
 
 int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
     Uint32 hx;
-    if (is_map(map)) {
+    if (is_flatmap(map)) {
 	Sint n;
 	Uint need;
 	Eterm *hp_start;
 	Eterm *thp, *mhp;
 	Eterm *ks, *vs, tup;
-	map_t *mp = (map_t*)map_val(map);
+	flatmap_t *mp = (flatmap_t*)flatmap_val(map);
 
-	n = map_get_size(mp);
+	n = flatmap_get_size(mp);
 
 	if (n == 0) {
 	    *res = map;
 	    return 1;
 	}
 
-	ks = map_get_keys(mp);
-	vs = map_get_values(mp);
+	ks = flatmap_get_keys(mp);
+	vs = flatmap_get_values(mp);
 
 	/* Assume key exists.
 	 * Release allocated if it didn't.
@@ -1401,7 +1401,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
 	tup    = make_tuple(thp);
 	*thp++ = make_arityval(n - 1);
 
-	*res   = make_map(mhp);
+	*res   = make_flatmap(mhp);
 	*mhp++ = MAP_HEADER;
 	*mhp++ = n - 1;
 	*mhp++ = tup;
@@ -1451,7 +1451,7 @@ found_key:
 }
 
 BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
-    if (is_map(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
+    if (is_flatmap(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
 	Eterm res;
 	if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) {
 	    BIF_RET(res);
@@ -1462,18 +1462,18 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
 
 int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) {
     Uint32 hx;
-    if (is_map(map)) {
+    if (is_flatmap(map)) {
 	Sint n,i;
 	Eterm* hp,*shp;
 	Eterm *ks,*vs;
-	map_t *mp = (map_t*)map_val(map);
+	flatmap_t *mp = (flatmap_t*)flatmap_val(map);
 
-	if ((n = map_get_size(mp)) == 0) {
+	if ((n = flatmap_get_size(mp)) == 0) {
 	    return 0;
 	}
 
-	ks  = map_get_keys(mp);
-	vs  = map_get_values(mp);
+	ks  = flatmap_get_keys(mp);
+	vs  = flatmap_get_values(mp);
 
 	/* only allocate for values,
 	 * assume key-tuple will be intact
@@ -1511,7 +1511,7 @@ found_key:
 	vs++;
 	if (++i < n)
 	    sys_memcpy(hp, vs, (n - i)*sizeof(Eterm));
-	*res = make_map(shp);
+	*res = make_flatmap(shp);
 	return 1;
     }
 
@@ -1527,21 +1527,21 @@ found_key:
 Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
     Uint32 hx;
     Eterm res;
-    if (is_map(map)) {
+    if (is_flatmap(map)) {
 	Sint n,i;
 	Sint c = 0;
 	Eterm* hp, *shp;
 	Eterm *ks, *vs, tup;
-	map_t *mp = (map_t*)map_val(map);
+	flatmap_t *mp = (flatmap_t*)flatmap_val(map);
 
-	n = map_get_size(mp);
+	n = flatmap_get_size(mp);
 
 	if (n == 0) {
 	    hp    = HAlloc(p, MAP_HEADER_SIZE + 1 + 2);
 	    tup   = make_tuple(hp);
 	    *hp++ = make_arityval(1);
 	    *hp++ = key;
-	    res   = make_map(hp);
+	    res   = make_flatmap(hp);
 	    *hp++ = MAP_HEADER;
 	    *hp++ = 1;
 	    *hp++ = tup;
@@ -1550,8 +1550,8 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
 	    return res;
 	}
 
-	ks = map_get_keys(mp);
-	vs = map_get_values(mp);
+	ks = flatmap_get_keys(mp);
+	vs = flatmap_get_values(mp);
 
 	/* only allocate for values,
 	 * assume key-tuple will be intact
@@ -1559,7 +1559,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
 
 	hp  = HAlloc(p, MAP_HEADER_SIZE + n);
 	shp = hp; /* save hp, used if optimistic update fails */
-	res = make_map(hp);
+	res = make_flatmap(hp);
 	*hp++ = MAP_HEADER;
 	*hp++ = n;
 	*hp++ = mp->keys;
@@ -1593,8 +1593,8 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
 
 	if (n >= MAP_SMALL_MAP_LIMIT) {
 	    HRelease(p, shp + MAP_HEADER_SIZE + n, shp);
-	    ks = map_get_keys(mp);
-	    vs = map_get_values(mp);
+	    ks = flatmap_get_keys(mp);
+	    vs = flatmap_get_values(mp);
 
 	    res = erts_hashmap_from_ks_and_vs_extra(p,ks,vs,n,key,value);
 
@@ -1608,13 +1608,13 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
 	*shp++ = make_arityval(n+1);
 
 	hp    = HAlloc(p, 3 + n + 1);
-	res   = make_map(hp);
+	res   = make_flatmap(hp);
 	*hp++ = MAP_HEADER;
 	*hp++ = n + 1;
 	*hp++ = tup;
 
-	ks = map_get_keys(mp);
-	vs = map_get_values(mp);
+	ks = flatmap_get_keys(mp);
+	vs = flatmap_get_values(mp);
 
 	ASSERT(n >= 0);
 
@@ -1653,7 +1653,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
 /* maps:update/3 */
 
 BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
-    if (is_map(BIF_ARG_3) || is_hashmap(BIF_ARG_3)) {
+    if (is_flatmap(BIF_ARG_3) || is_hashmap(BIF_ARG_3)) {
 	Eterm res;
 	if (erts_maps_update(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &res)) {
 	    BIF_RET(res);
@@ -1666,19 +1666,19 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
 /* maps:values/1 */
 
 BIF_RETTYPE maps_values_1(BIF_ALIST_1) {
-    if (is_map(BIF_ARG_1)) {
+    if (is_flatmap(BIF_ARG_1)) {
 	Eterm *hp, *vs, res = NIL;
-	map_t *mp;
+	flatmap_t *mp;
 	Uint n;
 
-	mp  = (map_t*)map_val(BIF_ARG_1);
-	n   = map_get_size(mp);
+	mp  = (flatmap_t*)flatmap_val(BIF_ARG_1);
+	n   = flatmap_get_size(mp);
 
 	if (n == 0)
 	    BIF_RET(res);
 
 	hp  = HAlloc(BIF_P, (2 * n));
-	vs  = map_get_values(mp);
+	vs  = flatmap_get_values(mp);
 
 	while(n--) {
 	    res = CONS(hp, vs[n], res); hp += 2;
@@ -2257,7 +2257,7 @@ unroll:
     if (n <= MAP_SMALL_MAP_LIMIT) {
 	DECLARE_WSTACK(wstack);
 	Eterm *kv, *ks, *vs;
-	map_t *mp;
+	flatmap_t *mp;
 	Eterm keys;
 
 	DESTROY_ESTACK(stack);
@@ -2268,7 +2268,7 @@ unroll:
 	*hp++ = make_arityval(n);
 	ks    = hp;
 	hp   += n;
-	mp    = (map_t*)hp;
+	mp    = (flatmap_t*)hp;
 	hp   += MAP_HEADER_SIZE;
 	vs    = hp;
 
@@ -2286,10 +2286,10 @@ unroll:
 	}
 
 	/* it cannot have multiple keys */
-	erts_validate_and_sort_map(mp);
+	erts_validate_and_sort_flatmap(mp);
 
 	DESTROY_WSTACK(wstack);
-	return make_map(mp);
+	return make_flatmap(mp);
     }
 
     hp     = HAlloc(p, size);
@@ -2451,11 +2451,11 @@ not_found:
 }
 
 
-int erts_validate_and_sort_map(map_t* mp)
+int erts_validate_and_sort_flatmap(flatmap_t* mp)
 {
-    Eterm *ks  = map_get_keys(mp);
-    Eterm *vs  = map_get_values(mp);
-    Uint   sz  = map_get_size(mp);
+    Eterm *ks  = flatmap_get_keys(mp);
+    Eterm *vs  = flatmap_get_values(mp);
+    Uint   sz  = flatmap_get_size(mp);
     Uint   ix,jx;
     Eterm  tmp;
     int c;
@@ -2533,8 +2533,8 @@ BIF_RETTYPE erts_debug_map_info_1(BIF_ALIST_1) {
  */
 
 BIF_RETTYPE erts_internal_map_to_tuple_keys_1(BIF_ALIST_1) {
-    if (is_map(BIF_ARG_1)) {
-	map_t *mp = (map_t*)map_val(BIF_ARG_1);
+    if (is_flatmap(BIF_ARG_1)) {
+	flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1);
 	BIF_RET(mp->keys);
     }
     BIF_ERROR(BIF_P, BADARG);
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index db22d461ce..884bf69a34 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -34,11 +34,11 @@ Uint32 hashmap_bitcount(Uint32 x);
 
 /* MAP */
 
-typedef struct map_s {
+typedef struct flatmap_s {
     Eterm thing_word;
     Uint  size;
     Eterm keys;      /* tuple */
-} map_t;
+} flatmap_t;
 /* map node
  *
  * -----------
@@ -66,23 +66,23 @@ typedef struct map_s {
 
 
 /* erl_term.h stuff */
-#define make_map(x)		make_boxed((Eterm*)(x))
-#define make_map_rel(x, BASE)   make_boxed_rel((Eterm*)(x),(BASE))
-#define is_map(x)		(is_boxed((x)) && is_map_header(*boxed_val((x))))
-#define is_map_rel(RTERM,BASE)  is_map(rterm2wterm(RTERM,BASE))
-#define is_not_map(x)           (!is_map((x)))
-#define is_map_header(x)	(((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_MAP)
-#define header_is_map(x)        ((((x) & (_HEADER_SUBTAG_MASK)) == MAP_SUBTAG))
-#define map_val(x)		(_unchecked_boxed_val((x)))
-#define map_val_rel(RTERM, BASE) map_val(rterm2wterm(RTERM, BASE))
-
-#define map_get_values(x)      (((Eterm *)(x)) + 3)
-#define map_get_keys(x)        (((Eterm *)tuple_val(((map_t *)(x))->keys)) + 1)
-#define map_get_size(x)        (((map_t*)(x))->size)
+#define make_flatmap(x)		make_boxed((Eterm*)(x))
+#define make_flatmap_rel(x, BASE)   make_boxed_rel((Eterm*)(x),(BASE))
+#define is_flatmap(x)		(is_boxed((x)) && is_flatmap_header(*boxed_val((x))))
+#define is_flatmap_rel(RTERM,BASE)  is_flatmap(rterm2wterm(RTERM,BASE))
+#define is_not_flatmap(x)           (!is_flatmap((x)))
+#define is_flatmap_header(x)	(((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_MAP)
+#define header_is_flatmap(x)        ((((x) & (_HEADER_SUBTAG_MASK)) == MAP_SUBTAG))
+#define flatmap_val(x)		(_unchecked_boxed_val((x)))
+#define flatmap_val_rel(RTERM, BASE) flatmap_val(rterm2wterm(RTERM, BASE))
+
+#define flatmap_get_values(x)      (((Eterm *)(x)) + 3)
+#define flatmap_get_keys(x)        (((Eterm *)tuple_val(((flatmap_t *)(x))->keys)) + 1)
+#define flatmap_get_size(x)        (((flatmap_t*)(x))->size)
 
 #define MAP_SMALL_MAP_LIMIT    (2) /*SVERK (32) */
 #define MAP_HEADER             _make_header(1,_TAG_HEADER_MAP)
-#define MAP_HEADER_SIZE        (sizeof(map_t) / sizeof(Eterm))
+#define MAP_HEADER_SIZE        (sizeof(flatmap_t) / sizeof(Eterm))
 
 struct ErtsWStack_;
 struct ErtsEStack_;
@@ -98,7 +98,7 @@ int    erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz,
 Eterm  erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value,
 			      Uint *upsz, struct ErtsEStack_ *sp);
 
-int    erts_validate_and_sort_map(map_t* map);
+int    erts_validate_and_sort_flatmap(flatmap_t* map);
 Uint   hashmap_over_estimated_heap_size(Uint n);
 void   hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 198acfd128..fd793fd7e4 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1905,15 +1905,15 @@ enif_is_on_dirty_scheduler(ErlNifEnv* env)
 
 int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term)
 {
-    return is_map(term);
+    return is_flatmap(term);
 }
 
 int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)
 {
-    if (is_map(term)) {
-	map_t *mp;
-	mp    = (map_t*)map_val(term);
-	*size = map_get_size(mp);
+    if (is_flatmap(term)) {
+	flatmap_t *mp;
+	mp    = (flatmap_t*)flatmap_val(term);
+	*size = flatmap_get_size(mp);
 	return 1;
     }
     return 0;
@@ -1923,16 +1923,16 @@ ERL_NIF_TERM enif_make_new_map(ErlNifEnv* env)
 {
     Eterm* hp = alloc_heap(env,MAP_HEADER_SIZE+1);
     Eterm tup;
-    map_t *mp;
+    flatmap_t *mp;
 
     tup   = make_tuple(hp);
     *hp++ = make_arityval(0);
-    mp    = (map_t*)hp;
+    mp    = (flatmap_t*)hp;
     mp->thing_word = MAP_HEADER;
     mp->size = 0;
     mp->keys = tup;
 
-    return make_map(mp);
+    return make_flatmap(mp);
 }
 
 int enif_make_map_put(ErlNifEnv* env,
@@ -1941,7 +1941,7 @@ int enif_make_map_put(ErlNifEnv* env,
 		      Eterm value,
 		      Eterm *map_out)
 {
-    if (is_not_map(map_in)) {
+    if (is_not_flatmap(map_in)) {
 	return 0;
     }
     flush_env(env);
@@ -1956,7 +1956,7 @@ int enif_get_map_value(ErlNifEnv* env,
 		       Eterm *value)
 {
     const Eterm *ret;
-    if (is_not_map(map)) {
+    if (is_not_flatmap(map)) {
 	return 0;
     }
     ret = erts_maps_get(key, map);
@@ -1974,7 +1974,7 @@ int enif_make_map_update(ErlNifEnv* env,
 			 Eterm *map_out)
 {
     int res;
-    if (is_not_map(map_in)) {
+    if (is_not_flatmap(map_in)) {
 	return 0;
     }
 
@@ -1990,7 +1990,7 @@ int enif_make_map_remove(ErlNifEnv* env,
 			 Eterm *map_out)
 {
     int res;
-    if (is_not_map(map_in)) {
+    if (is_not_flatmap(map_in)) {
 	return 0;
     }
     flush_env(env);
@@ -2004,13 +2004,13 @@ int enif_map_iterator_create(ErlNifEnv *env,
 			     ErlNifMapIterator *iter,
 			     ErlNifMapIteratorEntry entry)
 {
-    if (is_map(map)) {
-	map_t *mp = (map_t*)map_val(map);
+    if (is_flatmap(map)) {
+	flatmap_t *mp = (flatmap_t*)flatmap_val(map);
 	size_t offset;
 
 	switch (entry) {
 	    case ERL_NIF_MAP_ITERATOR_HEAD: offset = 0; break;
-	    case ERL_NIF_MAP_ITERATOR_TAIL: offset = map_get_size(mp) - 1; break;
+	    case ERL_NIF_MAP_ITERATOR_TAIL: offset = flatmap_get_size(mp) - 1; break;
 	    default: goto error;
 	}
 
@@ -2019,9 +2019,9 @@ int enif_map_iterator_create(ErlNifEnv *env,
 	 */
 
 	iter->map     = map;
-	iter->ks      = ((Eterm *)map_get_keys(mp)) + offset;
-	iter->vs      = ((Eterm *)map_get_values(mp)) + offset;
-	iter->t_limit = map_get_size(mp) + 1;
+	iter->ks      = ((Eterm *)flatmap_get_keys(mp)) + offset;
+	iter->vs      = ((Eterm *)flatmap_get_values(mp)) + offset;
+	iter->t_limit = flatmap_get_size(mp) + 1;
 	iter->idx     = offset + 1;
 
 	return 1;
@@ -2045,22 +2045,22 @@ void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)
 
 int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)
 {
-    ASSERT(iter && is_map(iter->map));
-    ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1));
+    ASSERT(iter && is_flatmap(iter->map));
+    ASSERT(iter->idx >= 0 && (iter->idx <= flatmap_get_size(flatmap_val(iter->map)) + 1));
     return (iter->t_limit == 1 || iter->idx == iter->t_limit);
 }
 
 int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)
 {
-    ASSERT(iter && is_map(iter->map));
-    ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1));
+    ASSERT(iter && is_flatmap(iter->map));
+    ASSERT(iter->idx >= 0 && (iter->idx <= flatmap_get_size(flatmap_val(iter->map)) + 1));
     return (iter->t_limit == 1 || iter->idx == 0);
 }
 
 
 int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)
 {
-    ASSERT(iter && is_map(iter->map));
+    ASSERT(iter && is_flatmap(iter->map));
     if (iter->idx < iter->t_limit) {
 	iter->idx++;
 	iter->ks++;
@@ -2071,7 +2071,7 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)
 
 int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)
 {
-    ASSERT(iter && is_map(iter->map));
+    ASSERT(iter && is_flatmap(iter->map));
     if (iter->idx > 0) {
 	iter->idx--;
 	iter->ks--;
@@ -2085,12 +2085,12 @@ int enif_map_iterator_get_pair(ErlNifEnv *env,
 			       Eterm *key,
 			       Eterm *value)
 {
-    ASSERT(iter && is_map(iter->map));
+    ASSERT(iter && is_flatmap(iter->map));
     if (iter->idx > 0 && iter->idx < iter->t_limit) {
-	ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) &&
-	       iter->ks  < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map))));
-	ASSERT(iter->vs >= map_get_values(map_val(iter->map)) &&
-	       iter->vs  < (map_get_values(map_val(iter->map)) + map_get_size(map_val(iter->map))));
+	ASSERT(iter->ks >= flatmap_get_keys(flatmap_val(iter->map)) &&
+	       iter->ks  < (flatmap_get_keys(flatmap_val(iter->map)) + flatmap_get_size(flatmap_val(iter->map))));
+	ASSERT(iter->vs >= flatmap_get_values(flatmap_val(iter->map)) &&
+	       iter->vs  < (flatmap_get_values(flatmap_val(iter->map)) + flatmap_get_size(flatmap_val(iter->map))));
 	*key   = *(iter->ks);
 	*value = *(iter->vs);
 	return 1;
diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c
index 81fd19693a..8046f54a0c 100644
--- a/erts/emulator/beam/erl_printf_term.c
+++ b/erts/emulator/beam/erl_printf_term.c
@@ -568,10 +568,10 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
 	    {
 		Uint n;
 		Eterm *ks, *vs;
-		map_t *mp = (map_t *)map_val(wobj);
-		n  = map_get_size(mp);
-		ks = map_get_keys(mp);
-		vs = map_get_values(mp);
+		flatmap_t *mp = (flatmap_t *)flatmap_val(wobj);
+		n  = flatmap_get_size(mp);
+		ks = flatmap_get_keys(mp);
+		vs = flatmap_get_values(mp);
 
 		PRINT_CHAR(res, fn, arg, '#');
 		PRINT_CHAR(res, fn, arg, '{');
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index bd9ad65086..65b4ae5412 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -2605,15 +2605,15 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
 
 	case MAP_DEF:
 	    {
-		map_t *mp = (map_t*)map_val(obj);
-		Uint size = map_get_size(mp);
+		flatmap_t *mp = (flatmap_t*)flatmap_val(obj);
+		Uint size = flatmap_get_size(mp);
 
 		*ep++ = MAP_EXT;
 		put_int32(size, ep); ep += 4;
 
 		if (size > 0) {
-		    Eterm *kptr = map_get_keys(mp);
-		    Eterm *vptr = map_get_values(mp);
+		    Eterm *kptr = flatmap_get_keys(mp);
+		    Eterm *vptr = flatmap_get_values(mp);
 
 		    WSTACK_PUSH4(s, (UWord)kptr, (UWord)vptr,
 				 ENC_MAP_PAIR, size);
@@ -3599,14 +3599,14 @@ dec_term_atom_common:
 		size = get_int32(ep); ep += 4;
 
                 if (size <= MAP_SMALL_MAP_LIMIT) {
-                    map_t *mp;
+                    flatmap_t *mp;
 
                     keys  = make_tuple(hp);
                     *hp++ = make_arityval(size);
                     hp   += size;
                     kptr = hp - 1;
 
-                    mp    = (map_t*)hp;
+                    mp    = (flatmap_t*)hp;
                     hp   += MAP_HEADER_SIZE;
                     hp   += size;
                     vptr = hp - 1;
@@ -3625,7 +3625,7 @@ dec_term_atom_common:
 
                     mp->size       = size;
                     mp->keys       = keys;
-                    *objp          = make_map(mp);
+                    *objp          = make_flatmap(mp);
 
                     for (n = size; n; n--) {
                         *vptr = (Eterm) COMPRESS_POINTER(next);
@@ -3891,7 +3891,7 @@ dec_term_atom_common:
     while (maps_list) {
 	next  = (Eterm *)(EXPAND_POINTER(*maps_list));
 	*maps_list = MAP_HEADER;
-	if (!erts_validate_and_sort_map((map_t*)maps_list))
+	if (!erts_validate_and_sort_flatmap((flatmap_t*)maps_list))
 	    goto error;
 	maps_list  = next;
     }
@@ -4120,15 +4120,15 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
 	    break;
 	case MAP_DEF:
 	    {
-		map_t *mp = (map_t*)map_val(obj);
-		Uint size = map_get_size(mp);
+		flatmap_t *mp = (flatmap_t*)flatmap_val(obj);
+		Uint size = flatmap_get_size(mp);
 		Uint i;
 		Eterm *ptr;
 
 		result += 1 + 4; /* tag + 4 bytes size */
 
 		/* push values first */
-		ptr = map_get_values(mp);
+		ptr = flatmap_get_values(mp);
 		i   = size;
 		while(i--) {
 		    if (is_list(*ptr)) {
@@ -4142,7 +4142,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
 		    ++ptr;
 		}
 
-		ptr = map_get_keys(mp);
+		ptr = flatmap_get_keys(mp);
 		i   = size;
 		while(i--) {
 		    if (is_list(*ptr)) {
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index dc4c6fc350..804d3ddf50 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -5595,16 +5595,16 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
 	    int size = (int)ptr[0];
 	    Eterm* tp = hp;
 	    Eterm* vp;
-	    map_t *mp;
+	    flatmap_t *mp;
 
 	    *tp = make_arityval(size);
 
 	    hp += 1 + size;
-	    mp = (map_t*)hp;
+	    mp = (flatmap_t*)hp;
 	    mp->thing_word = MAP_HEADER;
 	    mp->size = size;
 	    mp->keys = make_tuple(tp);
-	    mess = make_map(mp);
+	    mess = make_flatmap(mp);
 
 	    hp += MAP_HEADER_SIZE + size;   /* advance "heap" pointer */
 
@@ -5615,7 +5615,7 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
 		*vp-- = ESTACK_POP(stack);
 		*tp-- = ESTACK_POP(stack);
 	    }
-	    if (!erts_validate_and_sort_map(mp))
+	    if (!erts_validate_and_sort_flatmap(mp))
 		ERTS_DDT_FAIL;
 	    ptr++;
 	    break;
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 66f13461a1..cc97e2f3d1 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1274,11 +1274,11 @@ make_hash2(Eterm term)
 	    break;
 	    case MAP_SUBTAG:
 	    {
-		map_t *mp = (map_t *)map_val(term);
+		flatmap_t *mp = (flatmap_t *)flatmap_val(term);
 		int i;
-		int size  = map_get_size(mp);
-		Eterm *ks = map_get_keys(mp);
-		Eterm *vs = map_get_values(mp);
+		int size  = flatmap_get_size(mp);
+		Eterm *ks = flatmap_get_keys(mp);
+		Eterm *vs = flatmap_get_values(mp);
 		UINT32_HASH(size, HCONST_16);
 		if (size == 0) {
 		    goto hash2_common;
@@ -1669,11 +1669,11 @@ make_internal_hash(Eterm term)
 	    break;
 	    case MAP_SUBTAG:
 	    {
-		map_t *mp = (map_t *)map_val(term);
+		flatmap_t *mp = (flatmap_t *)flatmap_val(term);
 		int i;
-		int size  = map_get_size(mp);
-		Eterm *ks = map_get_keys(mp);
-		Eterm *vs = map_get_values(mp);
+		int size  = flatmap_get_size(mp);
+		Eterm *ks = flatmap_get_keys(mp);
+		Eterm *vs = flatmap_get_values(mp);
 		UINT32_HASH(size, HCONST_16);
 		if (size == 0) {
 		    goto pop_next;
@@ -2574,13 +2574,13 @@ tailrecur_ne:
 		}
 	    case MAP_SUBTAG:
 		{
-		    aa = map_val_rel(a, a_base);
+		    aa = flatmap_val_rel(a, a_base);
 		    if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa)
 			goto not_equal;
-		    bb = map_val_rel(b,b_base);
-		    sz = map_get_size((map_t*)aa);
+		    bb = flatmap_val_rel(b,b_base);
+		    sz = flatmap_get_size((flatmap_t*)aa);
 
-		    if (sz != map_get_size((map_t*)bb)) goto not_equal;
+		    if (sz != flatmap_get_size((flatmap_t*)bb)) goto not_equal;
 		    if (sz == 0) goto pop_next;
 
 		    aa += 2;
@@ -3119,16 +3119,16 @@ tailrecur_ne:
 		++bb;
 		goto term_array;
 	    case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE) :
-		if (!is_map_rel(b,b_base)) {
+		if (!is_flatmap_rel(b,b_base)) {
 		    a_tag = MAP_DEF;
 		    goto mixed_types;
 		}
-		aa = (Eterm *)map_val_rel(a,a_base);
-		bb = (Eterm *)map_val_rel(b,b_base);
+		aa = (Eterm *)flatmap_val_rel(a,a_base);
+		bb = (Eterm *)flatmap_val_rel(b,b_base);
 
-		i = map_get_size((map_t*)aa);
-		if (i != map_get_size((map_t*)bb)) {
-		    RETURN_NEQ((int)(i - map_get_size((map_t*)bb)));
+		i = flatmap_get_size((flatmap_t*)aa);
+		if (i != flatmap_get_size((flatmap_t*)bb)) {
+		    RETURN_NEQ((int)(i - flatmap_get_size((flatmap_t*)bb)));
 		}
 		if (i == 0) {
 		    goto pop_next;
-- 
cgit v1.2.3


From 2fadbefc264d33da526ec61cffc9847f724d8d92 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Tue, 10 Mar 2015 19:57:07 +0100
Subject: erts: Reintroduce is_map macro

as shorthand for is_flatmap || is_hashmap
---
 erts/emulator/beam/beam_emu.c    |  2 +-
 erts/emulator/beam/erl_bif_op.c  |  2 +-
 erts/emulator/beam/erl_db_util.c |  6 +++---
 erts/emulator/beam/erl_map.c     | 12 ++++++------
 erts/emulator/beam/erl_term.h    |  3 +++
 5 files changed, 14 insertions(+), 11 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 10ae3ed77c..289b60e1f9 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -699,7 +699,7 @@ void** beam_ops;
         Fail; 								  \
     }
 
-#define IsMap(Src, Fail) if (is_not_flatmap(Src) && is_not_hashmap(Src)) { Fail; }
+#define IsMap(Src, Fail) if (!is_map(Src)) { Fail; }
 
 #define HasMapField(Src, Key, Fail) if (has_not_map_field(Src, Key)) { Fail; }
 
diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c
index af9b7bd908..37dd6457db 100644
--- a/erts/emulator/beam/erl_bif_op.c
+++ b/erts/emulator/beam/erl_bif_op.c
@@ -324,7 +324,7 @@ BIF_RETTYPE is_record_3(BIF_ALIST_3)
 	
 BIF_RETTYPE is_map_1(BIF_ALIST_1)
 {
-    if (is_flatmap(BIF_ARG_1) || is_hashmap(BIF_ARG_1)) {
+    if (is_map(BIF_ARG_1)) {
 	BIF_RET(am_true);
     }
     BIF_RET(am_false);
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index cca3f381a1..efbefb7492 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -2004,7 +2004,7 @@ restart:
 	    ++ep;
 	    break;
         case matchMap:
-            if (!is_flatmap_rel(*ep, base) && !is_hashmap_rel(*ep,base)) {
+            if (!is_map_rel(*ep, base)) {
                 FAIL();
             }
             n = *pc++;
@@ -2021,7 +2021,7 @@ restart:
             ep = flatmap_val_rel(*ep, base);
             break;
         case matchPushM:
-            if (!is_flatmap_rel(*ep, base) && !is_hashmap_rel(*ep, base)) {
+            if (!is_map_rel(*ep, base)) {
                 FAIL();
             }
             n = *pc++;
@@ -4915,7 +4915,7 @@ static DMCRet dmc_expr(DMCContext *context,
 	    return ret;
 	break;
     case TAG_PRIMARY_BOXED:
-        if (is_flatmap(t) || is_hashmap(t)) {
+        if (is_map(t)) {
             return dmc_map(context, heap, text, t, constant);
         }
 	if (!is_tuple(t)) {
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index ca43baf1a6..16293668ad 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -202,7 +202,7 @@ erts_maps_get(Eterm key, Eterm map)
 }
 
 BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
-    if (is_flatmap(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
+    if (is_map(BIF_ARG_2)) {
         Eterm *hp, res;
         const Eterm *value;
 
@@ -226,7 +226,7 @@ BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
  */
 
 BIF_RETTYPE maps_get_2(BIF_ALIST_2) {
-    if (is_flatmap(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
+    if (is_map(BIF_ARG_2)) {
 	Eterm *hp;
         Eterm error;
         const Eterm *value;
@@ -877,7 +877,7 @@ static int hxnodecmp(hxnode_t *a, hxnode_t *b) {
 /* maps:is_key/2 */
 
 BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) {
-    if (is_flatmap(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
+    if (is_map(BIF_ARG_2)) {
 	BIF_RET(erts_maps_get(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false);
     }
     BIF_ERROR(BIF_P, BADARG);
@@ -1360,7 +1360,7 @@ BIF_RETTYPE maps_new_0(BIF_ALIST_0) {
 /* maps:put/3 */
 
 BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
-    if (is_flatmap(BIF_ARG_3) || is_hashmap(BIF_ARG_3)) {
+    if (is_map(BIF_ARG_3)) {
 	BIF_RET(erts_maps_put(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3));
     }
     BIF_ERROR(BIF_P, BADARG);
@@ -1451,7 +1451,7 @@ found_key:
 }
 
 BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
-    if (is_flatmap(BIF_ARG_2) || is_hashmap(BIF_ARG_2)) {
+    if (is_map(BIF_ARG_2)) {
 	Eterm res;
 	if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) {
 	    BIF_RET(res);
@@ -1653,7 +1653,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
 /* maps:update/3 */
 
 BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
-    if (is_flatmap(BIF_ARG_3) || is_hashmap(BIF_ARG_3)) {
+    if (is_map(BIF_ARG_3)) {
 	Eterm res;
 	if (erts_maps_update(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &res)) {
 	    BIF_RET(res);
diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index 6d97344868..1625a4ec15 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -1019,6 +1019,9 @@ _ET_DECLARE_CHECKED(struct erl_node_*,external_ref_node,Eterm)
 #define hashmap_val(x)		_unchecked_boxed_val((x))
 #define hashmap_val_rel(RTERM, BASE) hashmap_val(rterm2wterm(RTERM, BASE))
 
+#define is_map(x)            (is_flatmap(x) || is_hashmap(x))
+#define is_map_rel(x,BASE)   (is_flatmap_rel(x,BASE) || is_hashmap_rel(x,BASE))
+
 /* number tests */
 
 #define is_integer(x)		(is_small(x) || is_big(x))
-- 
cgit v1.2.3


From f9e568cbad942043592453d0fb7640d8bc02b1ae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Wed, 11 Mar 2015 10:25:37 +0100
Subject: erts: Add hashmap construction to driver API

---
 erts/emulator/beam/io.c | 78 ++++++++++++++++++++++++++++++++-----------------
 1 file changed, 52 insertions(+), 26 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 804d3ddf50..b64854aac9 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -5349,7 +5349,11 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
 	case ERL_DRV_MAP: { /* int */
 	    ERTS_DDT_CHK_ENOUGH_ARGS(1);
 	    if ((int) ptr[0] < 0) ERTS_DDT_FAIL;
-	    need += MAP_HEADER_SIZE + 1 + 2*ptr[0];
+            if (ptr[0] > MAP_SMALL_MAP_LIMIT) {
+                need += hashmap_over_estimated_heap_size(ptr[0]);
+            } else {
+                need += MAP_HEADER_SIZE + 1 + 2*ptr[0];
+            }
 	    depth -= 2*ptr[0];
 	    if (depth < 0) ERTS_DDT_FAIL;
 	    ptr++;
@@ -5593,31 +5597,53 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
 
 	case ERL_DRV_MAP: { /* int */
 	    int size = (int)ptr[0];
-	    Eterm* tp = hp;
-	    Eterm* vp;
-	    flatmap_t *mp;
-
-	    *tp = make_arityval(size);
-
-	    hp += 1 + size;
-	    mp = (flatmap_t*)hp;
-	    mp->thing_word = MAP_HEADER;
-	    mp->size = size;
-	    mp->keys = make_tuple(tp);
-	    mess = make_flatmap(mp);
-
-	    hp += MAP_HEADER_SIZE + size;   /* advance "heap" pointer */
-
-	    tp += size;    /* point at last key */
-	    vp = hp - 1;   /* point at last value */
-
-	    while(size--) {
-		*vp-- = ESTACK_POP(stack);
-		*tp-- = ESTACK_POP(stack);
-	    }
-	    if (!erts_validate_and_sort_flatmap(mp))
-		ERTS_DDT_FAIL;
-	    ptr++;
+            if (size > MAP_SMALL_MAP_LIMIT) {
+                int ix = 2*size;
+                ErtsHeapFactory factory;
+                Eterm* leafs = hp;
+
+                hp += 2*size;
+                while(ix--) { *--hp = ESTACK_POP(stack); }
+
+                hp += 2*size;
+                factory.p = NULL;
+                factory.hp = hp;
+                /* We assume heap will suffice (see hashmap_over_estimated_heap_size) */
+                factory.hp_end = hp + (ERTS_SWORD_MAX / sizeof(Eterm));
+
+                mess = erts_hashmap_from_array(&factory, leafs, size, 1);
+
+                if (is_non_value(mess))
+                    ERTS_DDT_FAIL;
+
+                hp = factory.hp;
+            } else {
+                Eterm* tp = hp;
+                Eterm* vp;
+                flatmap_t *mp;
+
+                *tp = make_arityval(size);
+
+                hp += 1 + size;
+                mp = (flatmap_t*)hp;
+                mp->thing_word = MAP_HEADER;
+                mp->size = size;
+                mp->keys = make_tuple(tp);
+                mess = make_flatmap(mp);
+
+                hp += MAP_HEADER_SIZE + size;   /* advance "heap" pointer */
+
+                tp += size;    /* point at last key */
+                vp = hp - 1;   /* point at last value */
+
+                while(size--) {
+                    *vp-- = ESTACK_POP(stack);
+                    *tp-- = ESTACK_POP(stack);
+                }
+                if (!erts_validate_and_sort_flatmap(mp))
+                    ERTS_DDT_FAIL;
+            }
+            ptr++;
 	    break;
 	}
 
-- 
cgit v1.2.3


From c8f731bfec32a34d49304ea78017b63af053eecd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Wed, 11 Mar 2015 18:45:12 +0100
Subject: erts, kernel: Fix erts_debug:size/1 for hashmaps

This commit introduces two BIFs:
* erts_internal:map_type/1
* erts_internal:map_hashmap_children/1

erts_internal:map_hashmap_children/1 is only intended for use within
erts_debug:size/1 since the internal hashmap node is not allowed to
leak anywhere.
---
 erts/emulator/beam/bif.tab   |  2 ++
 erts/emulator/beam/erl_map.c | 75 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)

(limited to 'erts')

diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index b4e821a986..c56a108b34 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -157,6 +157,8 @@ bif erts_internal:request_system_task/3
 bif erts_internal:check_process_code/2
 
 bif erts_internal:map_to_tuple_keys/1
+bif erts_internal:map_type/1
+bif erts_internal:map_hashmap_children/1
 
 # inet_db support
 bif erlang:port_set_data/2
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 16293668ad..0e24f2e1b1 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -2540,6 +2540,81 @@ BIF_RETTYPE erts_internal_map_to_tuple_keys_1(BIF_ALIST_1) {
     BIF_ERROR(BIF_P, BADARG);
 }
 
+/*
+ * erts_internal:map_type/1
+ *
+ * Used in erts_debug:size/1
+ */
+
+BIF_RETTYPE erts_internal_map_type_1(BIF_ALIST_1) {
+    DECL_AM(hashmap);
+    DECL_AM(hashmap_node);
+    DECL_AM(flatmap);
+    if (is_flatmap(BIF_ARG_1)) {
+	BIF_RET(AM_flatmap);
+    } else if (is_hashmap(BIF_ARG_1)) {
+        Eterm hdr = *(boxed_val(BIF_ARG_1));
+        ASSERT(is_header(hdr));
+        switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
+            case HAMT_SUBTAG_HEAD_ARRAY:
+            case HAMT_SUBTAG_HEAD_BITMAP:
+                BIF_RET(AM_hashmap);
+            case HAMT_SUBTAG_NODE_ARRAY:
+            case HAMT_SUBTAG_NODE_BITMAP:
+                BIF_RET(AM_hashmap_node);
+            default:
+                erl_exit(1, "bad header");
+        }
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
+/*
+ * erts_internal:map_hashmap_children/1
+ *
+ * Used in erts_debug:size/1
+ */
+
+BIF_RETTYPE erts_internal_map_hashmap_children_1(BIF_ALIST_1) {
+    if (is_hashmap(BIF_ARG_1)) {
+        Eterm node = BIF_ARG_1;
+        Eterm *ptr, hdr, *hp, res = NIL;
+        Uint  sz = 0;
+        ptr = boxed_val(node);
+        hdr = *ptr;
+
+        ASSERT(is_header(hdr));
+
+        switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+            case HAMT_SUBTAG_NODE_ARRAY:
+                sz   = 16;
+                ptr += 1;
+                break;
+            case HAMT_SUBTAG_NODE_BITMAP:
+                sz   = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+                ptr += 1;
+                break;
+            case HAMT_SUBTAG_HEAD_BITMAP:
+                sz   = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+                ptr += 2;
+                break;
+            case HAMT_SUBTAG_HEAD_ARRAY:
+                sz   = 16;
+                ptr += 2;
+                break;
+            default:
+                erl_exit(1, "bad header\r\n");
+                break;
+        }
+        ASSERT(sz < 17);
+        hp = HAlloc(BIF_P, 2*sz);
+        while(sz--) { res = CONS(hp, *ptr++, res); hp += 2; }
+        BIF_RET(res);
+    }
+    BIF_ERROR(BIF_P, BADARG);
+}
+
+
 static Eterm hashmap_info(Process *p, Eterm node) {
     Eterm *hp;
     Eterm res = NIL, info = NIL;
-- 
cgit v1.2.3


From 81551dd13167b9c4458c4fee7ce08e152f9f5273 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Wed, 11 Mar 2015 20:39:08 +0100
Subject: erts: Make hashmap iterator more flexible

to allow mixing of 'next' and 'prev' operations.
---
 erts/emulator/beam/erl_db_util.c |   6 +-
 erts/emulator/beam/erl_map.c     | 174 +++++++++++++++++++++++----------------
 erts/emulator/beam/erl_map.h     |   2 +-
 erts/emulator/beam/utils.c       |   4 +-
 4 files changed, 108 insertions(+), 78 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index efbefb7492..e522876d03 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -1435,7 +1435,7 @@ restart:
                     }
                     structure_checked = 0;
 
-                    hashmap_iterator_init(&wstack, t);
+                    hashmap_iterator_init(&wstack, t, 0);
 
                     while ((kv=hashmap_iterator_next(&wstack)) != NULL) {
                         Eterm key = CAR(kv);
@@ -3872,7 +3872,7 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text,
 
         ASSERT(is_hashmap(t));
 
-        hashmap_iterator_init(&wstack, t);
+        hashmap_iterator_init(&wstack, t, 1);
         constant_values = 1;
         nelems = hashmap_size(t);
 
@@ -3893,7 +3893,7 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text,
 
         *constant = 0;
 
-        hashmap_iterator_init(&wstack, t);
+        hashmap_iterator_init(&wstack, t, 1);
 
         while ((kv=hashmap_iterator_prev(&wstack)) != NULL) {
             /* push key */
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 0e24f2e1b1..00ed968295 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -445,7 +445,7 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) {
 	mp->size = n;
 	mp->keys = keys;
 
-	hashmap_iterator_init(&wstack, res);
+	hashmap_iterator_init(&wstack, res, 0);
 
 	while ((kv=hashmap_iterator_next(&wstack)) != NULL) {
 	    *ks++ = CAR(kv);
@@ -1697,7 +1697,7 @@ static Eterm hashmap_to_list(Process *p, Eterm node) {
     Eterm res = NIL;
 
     hp  = HAlloc(p, hashmap_size(node) * (2 + 3));
-    hashmap_iterator_init(&stack, node);
+    hashmap_iterator_init(&stack, node, 0);
     while ((kv=hashmap_iterator_next(&stack)) != NULL) {
 	Eterm tup = TUPLE2(hp, CAR(kv), CDR(kv));
 	hp += 3;
@@ -1708,14 +1708,30 @@ static Eterm hashmap_to_list(Process *p, Eterm node) {
     return res;
 }
 
-void hashmap_iterator_init(ErtsWStack* s, Eterm node) {
-    WSTACK_PUSH((*s), (UWord)THE_NON_VALUE);  /* end marker */
-    WSTACK_PUSH((*s), (UWord)node);
+void hashmap_iterator_init(ErtsWStack* s, Eterm node, int reverse) {
+    Eterm hdr = *hashmap_val(node);
+    Uint sz;
+
+    switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+    case HAMT_SUBTAG_HEAD_ARRAY:
+	sz = 16;
+	break;
+    case HAMT_SUBTAG_HEAD_BITMAP:
+	sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+	break;
+    default:
+	erl_exit(1, "bad header");
+    }
+
+    WSTACK_PUSH3((*s), (UWord)THE_NON_VALUE,  /* end marker */
+		 (UWord)(!reverse ? 0 : sz+1),
+		 (UWord)node);
 }
 
 Eterm* hashmap_iterator_next(ErtsWStack* s) {
     Eterm node, *ptr, hdr;
     Uint32 sz;
+    Uint idx;
 
     for (;;) {
         ASSERT(!WSTACK_ISEMPTY((*s)));
@@ -1723,44 +1739,50 @@ Eterm* hashmap_iterator_next(ErtsWStack* s) {
         if (is_non_value(node)) {
             return NULL;
         }
-        switch (primary_tag(node)) {
-        case TAG_PRIMARY_LIST:
-            return list_val(node);
-
-        case TAG_PRIMARY_BOXED:
-            ptr = boxed_val(node);
-            hdr = *ptr;
-            ASSERT(is_header(hdr));
-            switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-            case HAMT_SUBTAG_HEAD_ARRAY:
-                ptr++;
-            case HAMT_SUBTAG_NODE_ARRAY:
-                ptr++;
-                sz = 16;
-                while(sz--) { WSTACK_PUSH((*s), (UWord)ptr[sz]); }
-                break;
-            case HAMT_SUBTAG_HEAD_BITMAP:
-                ptr++;
-            case HAMT_SUBTAG_NODE_BITMAP:
-                sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
-                ASSERT(sz < 17);
-                ptr++;
-                while(sz--) { WSTACK_PUSH((*s), (UWord)ptr[sz]); }
-                break;
-            default:
-                erl_exit(1, "bad header");
-            }
-            break;
+	idx = (Uint) WSTACK_POP((*s));
+        for (;;) {
+	    ASSERT(is_boxed(node));
+	    ptr = boxed_val(node);
+	    hdr = *ptr;
+	    ASSERT(is_header(hdr));
+	    switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+	    case HAMT_SUBTAG_HEAD_ARRAY:
+		ptr++;
+	    case HAMT_SUBTAG_NODE_ARRAY:
+		sz = 16;
+		break;
+	    case HAMT_SUBTAG_HEAD_BITMAP:
+		ptr++;
+	    case HAMT_SUBTAG_NODE_BITMAP:
+		sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+		ASSERT(sz < 17);
+		break;
+	    default:
+		erl_exit(1, "bad header");
+	    }
 
-        default:
-            erl_exit(1, "bad hamt node");
-	}
+	    idx++;
+
+	    if (idx <= sz) {
+		WSTACK_PUSH2((*s), (UWord)idx, (UWord)node);
+
+		if (is_list(ptr[idx])) {
+		    return list_val(ptr[idx]);
+		}
+		ASSERT(is_boxed(ptr[idx]));
+		node = ptr[idx];
+		idx = 0;
+	    }
+	    else
+		break; /* and pop parent node */
+        }
     }
 }
 
 Eterm* hashmap_iterator_prev(ErtsWStack* s) {
     Eterm node, *ptr, hdr;
-    Uint32 sz,i;
+    Uint32 sz;
+    Uint idx;
 
     for (;;) {
         ASSERT(!WSTACK_ISEMPTY((*s)));
@@ -1768,41 +1790,49 @@ Eterm* hashmap_iterator_prev(ErtsWStack* s) {
         if (is_non_value(node)) {
             return NULL;
         }
-        switch (primary_tag(node)) {
-        case TAG_PRIMARY_LIST:
-            return list_val(node);
-
-        case TAG_PRIMARY_BOXED:
-            ptr = boxed_val(node);
-            hdr = *ptr;
-            ASSERT(is_header(hdr));
-            switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-            case HAMT_SUBTAG_HEAD_ARRAY:
-                ptr++;
-            case HAMT_SUBTAG_NODE_ARRAY:
-                ptr++;
-                i = 0;
-                while(i < 16) { WSTACK_PUSH((*s), (UWord)ptr[i++]); }
-                break;
-            case HAMT_SUBTAG_HEAD_BITMAP:
-                ptr++;
-            case HAMT_SUBTAG_NODE_BITMAP:
-                sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
-                ASSERT(sz < 17);
-		i = 0;
-                ptr++;
-                while(i < sz) { WSTACK_PUSH((*s), (UWord)ptr[i++]); }
-                break;
-            default:
-                erl_exit(1, "bad header");
-            }
-            break;
+	idx = (Uint) WSTACK_POP((*s));
+        for (;;) {
+	    ASSERT(is_boxed(node));
+	    ptr = boxed_val(node);
+	    hdr = *ptr;
+	    ASSERT(is_header(hdr));
+	    switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
+	    case HAMT_SUBTAG_HEAD_ARRAY:
+		ptr++;
+	    case HAMT_SUBTAG_NODE_ARRAY:
+		sz = 16;
+		break;
+	    case HAMT_SUBTAG_HEAD_BITMAP:
+		ptr++;
+	    case HAMT_SUBTAG_NODE_BITMAP:
+		sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+		ASSERT(sz < 17);
+		break;
+	    default:
+		erl_exit(1, "bad header");
+	    }
 
-        default:
-            erl_exit(1, "bad hamt node");
-	}
+            if (idx > sz)
+		idx = sz;
+	    else
+		idx--;
+
+	    if (idx >= 1) {
+		WSTACK_PUSH2((*s), (UWord)idx, (UWord)node);
+
+		if (is_list(ptr[idx])) {
+		    return list_val(ptr[idx]);
+		}
+		ASSERT(is_boxed(ptr[idx]));
+		node = ptr[idx];
+		idx = 17;
+	    }
+	    else
+		break; /* and pop parent node */
+        }
     }
 }
+
 const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) {
     Eterm *ptr, hdr;
     Eterm th[2];
@@ -2135,7 +2165,7 @@ static Eterm hashmap_keys(Process* p, Eterm node) {
 
     root = (hashmap_head_t*) boxed_val(node);
     hp  = HAlloc(p, root->size * 2);
-    hashmap_iterator_init(&stack, node);
+    hashmap_iterator_init(&stack, node, 0);
     while ((kv=hashmap_iterator_next(&stack)) != NULL) {
 	res = CONS(hp, CAR(kv), res);
 	hp += 2;
@@ -2152,7 +2182,7 @@ static Eterm hashmap_values(Process* p, Eterm node) {
 
     root = (hashmap_head_t*) boxed_val(node);
     hp  = HAlloc(p, root->size * 2);
-    hashmap_iterator_init(&stack, node);
+    hashmap_iterator_init(&stack, node, 0);
     while ((kv=hashmap_iterator_next(&stack)) != NULL) {
 	res = CONS(hp, CDR(kv), res);
 	hp += 2;
@@ -2276,7 +2306,7 @@ unroll:
 	mp->size = n;
 	mp->keys = keys;
 
-	hashmap_iterator_init(&wstack, map);
+	hashmap_iterator_init(&wstack, map, 0);
 
 	while ((kv=hashmap_iterator_next(&wstack)) != NULL) {
 	    if (EQ(CAR(kv),key))
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 884bf69a34..c6d8b1966c 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -100,7 +100,7 @@ Eterm  erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value,
 
 int    erts_validate_and_sort_flatmap(flatmap_t* map);
 Uint   hashmap_over_estimated_heap_size(Uint n);
-void   hashmap_iterator_init(struct ErtsWStack_* s, Eterm node);
+void   hashmap_iterator_init(struct ErtsWStack_* s, Eterm node, int reverse);
 Eterm* hashmap_iterator_next(struct ErtsWStack_* s);
 Eterm* hashmap_iterator_prev(struct ErtsWStack_* s);
 int    hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index cc97e2f3d1..d098ee4c72 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -3181,8 +3181,8 @@ tailrecur_ne:
                 */
 
                     sp = PSTACK_PUSH(hmap_stack);
-                    hashmap_iterator_init(&stack, a);
-                    hashmap_iterator_init(&b_stack, b);
+                    hashmap_iterator_init(&stack, a, 0);
+                    hashmap_iterator_init(&b_stack, b, 0);
                     sp->ap = hashmap_iterator_next(&stack);
                     sp->bp = hashmap_iterator_next(&b_stack);
                     sp->cmp_res = 0;
-- 
cgit v1.2.3


From 2aeb8cfd42be8ab1b7eee4a7f144122223dd7261 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Wed, 11 Mar 2015 21:09:33 +0100
Subject: erts: Fix nif API for hashmaps

---
 erts/emulator/beam/erl_nif.c                  | 162 ++++++++++++++++++++------
 erts/emulator/beam/erl_nif.h                  |  14 ++-
 erts/emulator/beam/global.h                   |  16 ++-
 erts/emulator/test/nif_SUITE.erl              |   2 +-
 erts/emulator/test/nif_SUITE_data/nif_SUITE.c |   1 +
 5 files changed, 152 insertions(+), 43 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index fd793fd7e4..e28365cb1b 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1905,7 +1905,7 @@ enif_is_on_dirty_scheduler(ErlNifEnv* env)
 
 int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term)
 {
-    return is_flatmap(term);
+    return is_map(term);
 }
 
 int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)
@@ -1916,6 +1916,10 @@ int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)
 	*size = flatmap_get_size(mp);
 	return 1;
     }
+    else if (is_hashmap(term)) {
+        *size = hashmap_size(term);
+        return 1;
+    }
     return 0;
 }
 
@@ -1941,7 +1945,7 @@ int enif_make_map_put(ErlNifEnv* env,
 		      Eterm value,
 		      Eterm *map_out)
 {
-    if (is_not_flatmap(map_in)) {
+    if (!is_map(map_in)) {
 	return 0;
     }
     flush_env(env);
@@ -1956,7 +1960,7 @@ int enif_get_map_value(ErlNifEnv* env,
 		       Eterm *value)
 {
     const Eterm *ret;
-    if (is_not_flatmap(map)) {
+    if (!is_map(map)) {
 	return 0;
     }
     ret = erts_maps_get(key, map);
@@ -1974,7 +1978,7 @@ int enif_make_map_update(ErlNifEnv* env,
 			 Eterm *map_out)
 {
     int res;
-    if (is_not_flatmap(map_in)) {
+    if (!is_map(map_in)) {
 	return 0;
     }
 
@@ -1990,7 +1994,7 @@ int enif_make_map_remove(ErlNifEnv* env,
 			 Eterm *map_out)
 {
     int res;
-    if (is_not_flatmap(map_in)) {
+    if (!is_map(map_in)) {
 	return 0;
     }
     flush_env(env);
@@ -2019,14 +2023,37 @@ int enif_map_iterator_create(ErlNifEnv *env,
 	 */
 
 	iter->map     = map;
-	iter->ks      = ((Eterm *)flatmap_get_keys(mp)) + offset;
-	iter->vs      = ((Eterm *)flatmap_get_values(mp)) + offset;
-	iter->t_limit = flatmap_get_size(mp) + 1;
+	iter->u.flat.ks = ((Eterm *)flatmap_get_keys(mp)) + offset;
+	iter->u.flat.vs = ((Eterm *)flatmap_get_values(mp)) + offset;
+	iter->size    = flatmap_get_size(mp);
 	iter->idx     = offset + 1;
 
 	return 1;
     }
-
+    else if (is_hashmap(map)) {
+        iter->map = map;
+        iter->size = hashmap_size(map);
+        iter->u.hash.wstack = erts_alloc(ERTS_ALC_T_NIF, sizeof(ErtsDynamicWStack));
+        WSTACK_INIT(iter->u.hash.wstack, ERTS_ALC_T_NIF);
+
+        switch (entry) {
+	    case ERL_NIF_MAP_ITERATOR_HEAD:
+                iter->idx = 1;
+                hashmap_iterator_init(&iter->u.hash.wstack->ws, map, 0);
+                iter->u.hash.kv = hashmap_iterator_next(&iter->u.hash.wstack->ws);
+                break;
+	    case ERL_NIF_MAP_ITERATOR_TAIL:
+                iter->idx = hashmap_size(map);
+                hashmap_iterator_init(&iter->u.hash.wstack->ws, map, 1);
+                iter->u.hash.kv = hashmap_iterator_prev(&iter->u.hash.wstack->ws);
+                break;
+	    default:
+                goto error;
+	}
+        ASSERT(!!iter->u.hash.kv == (iter->idx >= 1 &&
+                                     iter->idx <= iter->size));
+        return 1;
+    }
 error:
 #ifdef DEBUG
     iter->map = THE_NON_VALUE;
@@ -2036,48 +2063,97 @@ error:
 
 void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)
 {
-    /* not used */
+    if (is_hashmap(iter->map)) {
+        WSTACK_DESTROY(iter->u.hash.wstack->ws);
+        erts_free(ERTS_ALC_T_NIF, iter->u.hash.wstack);
+    }
+    else
+        ASSERT(is_flatmap(iter->map));
+
 #ifdef DEBUG
     iter->map = THE_NON_VALUE;
 #endif
-
 }
 
 int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)
 {
-    ASSERT(iter && is_flatmap(iter->map));
-    ASSERT(iter->idx >= 0 && (iter->idx <= flatmap_get_size(flatmap_val(iter->map)) + 1));
-    return (iter->t_limit == 1 || iter->idx == iter->t_limit);
+    ASSERT(iter);
+    if (is_flatmap(iter->map)) {
+        ASSERT(iter->idx >= 0);
+        ASSERT(iter->idx <= flatmap_get_size(flatmap_val(iter->map)) + 1);
+        return (iter->size == 0 || iter->idx > iter->size);
+    }
+    else {
+        ASSERT(is_hashmap(iter->map));
+        return iter->idx > iter->size;
+    }
 }
 
 int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)
 {
-    ASSERT(iter && is_flatmap(iter->map));
-    ASSERT(iter->idx >= 0 && (iter->idx <= flatmap_get_size(flatmap_val(iter->map)) + 1));
-    return (iter->t_limit == 1 || iter->idx == 0);
+    ASSERT(iter);
+    if (is_flatmap(iter->map)) {
+        ASSERT(iter->idx >= 0);
+        ASSERT(iter->idx <= flatmap_get_size(flatmap_val(iter->map)) + 1);
+        return (iter->size == 0 || iter->idx == 0);
+    }
+    else {
+        ASSERT(is_hashmap(iter->map));
+        return iter->idx == 0;
+    }
 }
 
 
 int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)
 {
-    ASSERT(iter && is_flatmap(iter->map));
-    if (iter->idx < iter->t_limit) {
-	iter->idx++;
-	iter->ks++;
-	iter->vs++;
+    ASSERT(iter);
+    if (is_flatmap(iter->map)) {
+        if (iter->idx <= iter->size) {
+            iter->idx++;
+            iter->u.flat.ks++;
+            iter->u.flat.vs++;
+        }
+        return (iter->idx <= iter->size);
+    }
+    else {
+        ASSERT(is_hashmap(iter->map));
+
+        if (iter->idx <= hashmap_size(iter->map)) {
+            if (iter->idx < 1) {
+                hashmap_iterator_init(&iter->u.hash.wstack->ws, iter->map, 0);
+            }
+            iter->u.hash.kv = hashmap_iterator_next(&iter->u.hash.wstack->ws);
+            iter->idx++;
+            ASSERT(!!iter->u.hash.kv == (iter->idx <= iter->size));
+        }
+        return iter->idx <= iter->size;
     }
-    return (iter->idx != iter->t_limit);
 }
 
 int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)
 {
-    ASSERT(iter && is_flatmap(iter->map));
-    if (iter->idx > 0) {
-	iter->idx--;
-	iter->ks--;
-	iter->vs--;
+    ASSERT(iter);
+    if (is_flatmap(iter->map)) {
+        if (iter->idx > 0) {
+            iter->idx--;
+            iter->u.flat.ks--;
+            iter->u.flat.vs--;
+        }
+        return iter->idx > 0;
+    }
+    else {
+        ASSERT(is_hashmap(iter->map));
+
+        if (iter->idx > 0) {
+            if (iter->idx > iter->size) {
+                hashmap_iterator_init(&iter->u.hash.wstack->ws, iter->map, 1);
+            }
+            iter->u.hash.kv = hashmap_iterator_prev(&iter->u.hash.wstack->ws);
+            iter->idx--;
+            ASSERT(!!iter->u.hash.kv == (iter->idx > 0));
+        }
+        return iter->idx > 0;
     }
-    return (iter->idx > 0);
 }
 
 int enif_map_iterator_get_pair(ErlNifEnv *env,
@@ -2085,15 +2161,25 @@ int enif_map_iterator_get_pair(ErlNifEnv *env,
 			       Eterm *key,
 			       Eterm *value)
 {
-    ASSERT(iter && is_flatmap(iter->map));
-    if (iter->idx > 0 && iter->idx < iter->t_limit) {
-	ASSERT(iter->ks >= flatmap_get_keys(flatmap_val(iter->map)) &&
-	       iter->ks  < (flatmap_get_keys(flatmap_val(iter->map)) + flatmap_get_size(flatmap_val(iter->map))));
-	ASSERT(iter->vs >= flatmap_get_values(flatmap_val(iter->map)) &&
-	       iter->vs  < (flatmap_get_values(flatmap_val(iter->map)) + flatmap_get_size(flatmap_val(iter->map))));
-	*key   = *(iter->ks);
-	*value = *(iter->vs);
-	return 1;
+    ASSERT(iter);
+    if (is_flatmap(iter->map)) {
+        if (iter->idx > 0 && iter->idx <= iter->size) {
+            ASSERT(iter->u.flat.ks >= flatmap_get_keys(flatmap_val(iter->map)) &&
+                   iter->u.flat.ks  < (flatmap_get_keys(flatmap_val(iter->map)) + flatmap_get_size(flatmap_val(iter->map))));
+            ASSERT(iter->u.flat.vs >= flatmap_get_values(flatmap_val(iter->map)) &&
+                   iter->u.flat.vs  < (flatmap_get_values(flatmap_val(iter->map)) + flatmap_get_size(flatmap_val(iter->map))));
+            *key   = *(iter->u.flat.ks);
+            *value = *(iter->u.flat.vs);
+            return 1;
+        }
+    }
+    else {
+        ASSERT(is_hashmap(iter->map));
+        if (iter->idx > 0 && iter->idx <= iter->size) {
+            *key   = CAR(iter->u.hash.kv);
+            *value = CDR(iter->u.hash.kv);
+            return 1;
+        }
     }
     return 0;
 }
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index 849024453c..9b2b90c82d 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -201,10 +201,18 @@ typedef enum
 typedef struct /* All fields all internal and may change */
 {
     ERL_NIF_TERM map;
-    ERL_NIF_UINT t_limit;
+    ERL_NIF_UINT size;
     ERL_NIF_UINT idx;
-    ERL_NIF_TERM *ks;
-    ERL_NIF_TERM *vs;
+    union {
+        struct {
+            ERL_NIF_TERM *ks;
+            ERL_NIF_TERM *vs;
+        }flat;
+        struct {
+            struct ErtsDynamicWStack_* wstack;
+            ERL_NIF_TERM* kv;
+        }hash;
+    }u;
     void* __spare__[2]; /* for future additions to be ABI compatible (same struct size) */
 } ErlNifMapIterator;
 
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 09bcf71a28..ef7a183d08 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -542,6 +542,20 @@ void erl_grow_wstack(ErtsWStack*, Uint need);
     }
 #define DECLARE_WSTACK WSTACK_DECLARE
 
+typedef struct ErtsDynamicWStack_ {
+    UWord default_stack[DEF_WSTACK_SIZE];
+    ErtsWStack ws;
+}ErtsDynamicWStack;
+
+#define WSTACK_INIT(dwsp, ALC_TYPE)                               \
+do {	 	                                                  \
+    (dwsp)->ws.wstart   = (dwsp)->default_stack;                  \
+    (dwsp)->ws.wsp      = (dwsp)->default_stack;                  \
+    (dwsp)->ws.wend     = (dwsp)->default_stack + DEF_WSTACK_SIZE;\
+    (dwsp)->ws.wdefault = (dwsp)->default_stack;                  \
+    (dwsp)->ws.alloc_type = ALC_TYPE;                             \
+} while (0)
+
 #define WSTACK_CHANGE_ALLOCATOR(s,t)					\
 do {									\
     if (s.wstart != WSTK_DEF_STACK(s)) {				\
@@ -553,7 +567,7 @@ do {									\
 
 #define WSTACK_DESTROY(s)				\
 do {							\
-    if (s.wstart != WSTK_DEF_STACK(s)) {		\
+    if (s.wstart != s.wdefault) {		        \
 	erts_free(s.alloc_type, s.wstart); 		\
     }							\
 } while(0)
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 4560077a51..b0624fb8c1 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -451,7 +451,7 @@ maps(Config) when is_list(Config) ->
     M  = maps_from_list_nif(Pairs),
     R = {RIs,Is} = sorted_list_from_maps_nif(M),
     io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]),
-    Is = lists:sort(Pairs),
+    true = (lists:sort(Is) =:= lists:sort(Pairs)),
     Is = lists:reverse(RIs),
 
     #{} = maps_from_list_nif([]),
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 85544db2ab..7b49f23d0a 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1744,6 +1744,7 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER
 
 	list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b);
 	prev_ret = enif_map_iterator_prev(env,&iter_b);
+	cnt++;
     }
 
     if (cnt) {
-- 
cgit v1.2.3


From 1b13e90161bdb1409b04a273a6b71f02fbb9d5f1 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Thu, 12 Mar 2015 15:19:35 +0100
Subject: erts: Cleanup comments for make_internal_hash

---
 erts/emulator/beam/utils.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index d098ee4c72..23ae885b6d 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1570,14 +1570,22 @@ make_hash2(Eterm term)
 }
 
 /* Term hash function for internal use.
- * Is not "portable" in any way betweem different VM instances.
+ *
+ * Limitation #1: Is not "portable" in any way between different VM instances.
+ *
+ * Limitation #2: The hash value is only valid as long as the term exists
+ * somewhere in the VM. Why? Because external pids, ports and refs are hashed
+ * by mixing the node *pointer* value. If a node disappears and later reappears
+ * with a new ErlNode struct, externals from that node will hash different than
+ * before.
  *
  * One IMPORTANT property must hold (for hamt).
  * EVERY BIT of the term that is significant for equality (see EQ)
- * must be used as input for the hash. Two different terms must always have a
- * chance of hashing different when salted: h([Salt|A]) vs h([Salt|B]).
+ * MUST BE USED AS INPUT FOR THE HASH. Two different terms must always have a
+ * chance of hashing different when salted: hash([Salt|A]) vs hash([Salt|B]).
  *
  * This is why we can not use cached hash values for atoms for example.
+ *
  */
 
 #define CONST_HASH(AConst)                              \
@@ -1854,7 +1862,7 @@ make_internal_hash(Eterm term)
                 ExternalThing* thing = external_thing_ptr(term);
 
                 ASSERT(external_thing_ref_no_of_numbers(thing) == 3);
-                /*SVERK Is it really ok to hash node POINTER? */
+                /* See limitation #2 */
             #ifdef ARCH_64
                 POINTER_HASH(thing->node, HCONST_7);
                 UINT32_HASH(external_thing_ref_numbers(thing)[0], HCONST_7);
@@ -1868,7 +1876,7 @@ make_internal_hash(Eterm term)
             }
             case EXTERNAL_PID_SUBTAG: {
                 ExternalThing* thing = external_thing_ptr(term);
-                /*SVERK Is it really ok to hash node POINTER? */
+                /* See limitation #2 */
             #ifdef ARCH_64
                 POINTER_HASH(thing->node, HCONST_5);
                 UINT32_HASH(thing->data.ui[0], HCONST_5);
@@ -1879,7 +1887,7 @@ make_internal_hash(Eterm term)
             }
 	    case EXTERNAL_PORT_SUBTAG: {
                 ExternalThing* thing = external_thing_ptr(term);
-                /*SVERK Is it really ok to hash node POINTER? */
+                /* See limitation #2 */
             #ifdef ARCH_64
                 POINTER_HASH(thing->node, HCONST_6);
                 UINT32_HASH(thing->data.ui[0], HCONST_6);
-- 
cgit v1.2.3


From a09f11fb0db7f40015f85146691286e1ac735733 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 12 Mar 2015 19:20:36 +0100
Subject: erts: Set Maps small limit to 32 items

Use small limit 3 in debug case
---
 erts/emulator/beam/erl_map.h | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index c6d8b1966c..ff5e6edd1e 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -80,7 +80,11 @@ typedef struct flatmap_s {
 #define flatmap_get_keys(x)        (((Eterm *)tuple_val(((flatmap_t *)(x))->keys)) + 1)
 #define flatmap_get_size(x)        (((flatmap_t*)(x))->size)
 
-#define MAP_SMALL_MAP_LIMIT    (2) /*SVERK (32) */
+#ifdef DEBUG
+#define MAP_SMALL_MAP_LIMIT    (3)
+#else
+#define MAP_SMALL_MAP_LIMIT    (32)
+#endif
 #define MAP_HEADER             _make_header(1,_TAG_HEADER_MAP)
 #define MAP_HEADER_SIZE        (sizeof(flatmap_t) / sizeof(Eterm))
 
-- 
cgit v1.2.3


From 2dae04b0c73f5fc855d4cfb2e6231bf65e51ebce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Thu, 12 Mar 2015 18:39:54 +0100
Subject: Fix beam_load assert

---
 erts/emulator/beam/beam_load.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index fce710f723..02689e5b19 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -3727,7 +3727,7 @@ gen_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail,
     op->a[j+size] = Fail;
 
 #ifdef DEBUG
-    for (i = 0; i < size; i++) {
+    for (i = 0; i < size - 1; i++) {
 	ASSERT(op->a[i+3].val <= op->a[i+4].val);
     }
 #endif
-- 
cgit v1.2.3


From 2ae925af364d1d1a0e1f4a2693a7030e58a26018 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Thu, 12 Mar 2015 20:34:18 +0100
Subject: erts: Fix typo in make_hash2 for 32-bit arch

---
 erts/emulator/beam/utils.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 23ae885b6d..933a8ffa0b 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1188,7 +1188,7 @@ make_hash2(Eterm term)
 #ifdef ARCH_64
 #  define POINTER_HASH(Ptr, AConst) UINT32_HASH_2((Uint32)(UWord)(Ptr), (((UWord)(Ptr)) >> 32), AConst)
 #else
-#  define POINTER_HASH(Ptr, AConst) UINT32_HASH(Ptr, Const)
+#  define POINTER_HASH(Ptr, AConst) UINT32_HASH(Ptr, AConst)
 #endif
 
     /* Optimization. Simple cases before declaration of estack. */
-- 
cgit v1.2.3


From b398819d59863db76fc2ce2f0c1584254e38f3bb Mon Sep 17 00:00:00 2001
From: Sverker Eriksson 
Date: Thu, 12 Mar 2015 21:03:10 +0100
Subject: erts: Fix windows bug in hashmap_info

undefined symbol 'MAX'
---
 erts/emulator/beam/erl_map.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 00ed968295..b7f07fa6c4 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -2670,7 +2670,8 @@ static Eterm hashmap_info(Process *p, Eterm node) {
     do {
 	node = ESTACK_POP(stack);
 	clvl = ESTACK_POP(stack);
-	lvl  = MAX(lvl,clvl);
+	if (lvl < clvl)
+            lvl = clvl;
 	switch(primary_tag(node)) {
 	    case TAG_PRIMARY_LIST:
 		nleaf++;
-- 
cgit v1.2.3


From 59f1846249fbbb76e786d1e73e9be5c72a41fefb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Fri, 13 Mar 2015 10:09:33 +0100
Subject: erts: Restrict GCC intrinsics by compiler version

Intrinsics __builtin_clz and __builtin_popcount are only valid on
GCC version 3.4 and above.
---
 erts/emulator/beam/erl_map.c | 3 ++-
 erts/emulator/beam/erl_map.h | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index b7f07fa6c4..964c09e906 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -2768,7 +2768,7 @@ static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[])
 
 /* implementation of builtin emulations */
 
-#if !defined(__GNUC__)
+#if !ERTS_AT_LEAST_GCC_VSN__(3, 4, 0)
 /* Count leading zeros emulation */
 Uint32 hashmap_clz(Uint32 x) {
     Uint32 y;
@@ -2780,6 +2780,7 @@ Uint32 hashmap_clz(Uint32 x) {
     y = x >> 1;  if (y != 0) return n - 2;
     return n - x;
 }
+
 const Uint32 SK5 = 0x55555555, SK3 = 0x33333333;
 const Uint32 SKF0 = 0xF0F0F0F, SKFF = 0xFF00FF;
 
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index ff5e6edd1e..d075d87c83 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -24,7 +24,7 @@
 #include "sys.h"
 
 /* instrinsic wrappers */
-#if defined(__GNUC__)
+#if ERTS_AT_LEAST_GCC_VSN__(3, 4, 0)
 #define hashmap_clz(x)       ((Uint32) __builtin_clz((unsigned int)(x)))
 #define hashmap_bitcount(x)  ((Uint32) __builtin_popcount((unsigned int) (x)))
 #else
-- 
cgit v1.2.3


From dee91137e9b8c1f32b788ce5b5b34fcb2076d9ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Fri, 13 Mar 2015 11:17:37 +0100
Subject: erts: Fix typo in copy_struct for halfword emulator

---
 erts/emulator/beam/copy.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index 83214aca19..027b85b079 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -511,7 +511,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
 		    case MAP_HEADER_TAG_HAMT_NODE_BITMAP :
 			i = 1 + hashmap_bitcount(MAP_HEADER_VAL(hdr));
 			while (i--)  { *htop++ = *objp++; }
-			*argp = make_hashmap_rel(tp, dstbase);
+			*argp = make_hashmap_rel(tp, dst_base);
 			break;
 		    default:
 			erl_exit(ERTS_ABORT_EXIT, "copy_struct: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr));
-- 
cgit v1.2.3


From 17f4ccfcd91b2ba074f5c6fe224adacfe5952df4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Fri, 13 Mar 2015 11:19:31 +0100
Subject: erts: Remove unused variable in crashdump creation

---
 erts/emulator/beam/break.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 4ede2c9d7d..e2fa572546 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -661,7 +661,6 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
 {
 #ifdef ERTS_SMP
     ErtsThrPrgrData tpd_buf; /* in case we aren't a managed thread... */
-    int bc;
 #endif
     int fd;
     size_t envsz;
@@ -681,7 +680,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
     /* Order all managed threads to block, this has to be done
        first to guarantee that this is the only thread to generate
        crash dump. */
-    bc = erts_thr_progress_fatal_error_block(&tpd_buf);
+    erts_thr_progress_fatal_error_block(&tpd_buf);
 
 #ifdef ERTS_THR_HAVE_SIG_FUNCS
     /*
-- 
cgit v1.2.3


From 68723bb6cd5bb57b288a4af82dc32acf13de0b4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Fri, 13 Mar 2015 16:19:31 +0100
Subject: erts: Enhance maps ordering tests

---
 erts/emulator/test/map_SUITE.erl | 33 +++++++++++++++++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 943c6f262e..c421886431 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -447,7 +447,7 @@ t_map_sort_literals(Config) when is_list(Config) ->
     true  = #{ c => 1, b => 1, a => 1 } < id(#{ b => 1, c => 1, d => 1}),
     true  = #{ "a" => 1 } < id(#{ <<"a">> => 1}),
     false = #{ <<"a">> => 1 } < id(#{ "a" => 1}),
-    true = #{ 1 => 1 } < id(#{ 1.0 => 1}),
+    true  = #{ 1 => 1 } < id(#{ 1.0 => 1}),
     false = #{ 1.0 => 1 } < id(#{ 1 => 1}),
 
     %% value order
@@ -460,13 +460,42 @@ t_map_sort_literals(Config) when is_list(Config) ->
 
     true  = #{ "a" => "hi", b => 134 } == id(#{ b => 134,"a" => "hi"}),
 
+    %% large maps
+
+    M = maps:from_list([{I,I}||I <- lists:seq(1,500)]),
+
+    %% size order
+    true  = M#{ a => 1, b => 2} < id(M#{ a => 1, b => 1, c => 1}),
+    true  = M#{ b => 1, a => 1} < id(M#{ c => 1, a => 1, b => 1}),
+    false = M#{ c => 1, b => 1, a => 1} < id(M#{ c => 1, a => 1}),
+
+    %% key order
+    true  = M#{ a => 1 } < id(M#{ b => 1}),
+    false = M#{ b => 1 } < id(M#{ a => 1}),
+    true  = M#{ a => 1, b => 1, c => 1 } < id(M#{ b => 1, c => 1, d => 1}),
+    true  = M#{ b => 1, c => 1, d => 1 } > id(M#{ a => 1, b => 1, c => 1}),
+    true  = M#{ c => 1, b => 1, a => 1 } < id(M#{ b => 1, c => 1, d => 1}),
+    true  = M#{ "a" => 1 } < id(M#{ <<"a">> => 1}),
+    false = M#{ <<"a">> => 1 } < id(#{ "a" => 1}),
+    true  = M#{ 1 => 1 } < id(maps:remove(1,M#{ 1.0 => 1})),
+    false = M#{ 1.0 => 1 } < id(M#{ 1 => 1}),
+
+    %% value order
+    true  = M#{ a => 1 } < id(M#{ a => 2}),
+    false = M#{ a => 2 } < id(M#{ a => 1}),
+    false = M#{ a => 2, b => 1 } < id(M#{ a => 1, b => 3}),
+    true  = M#{ a => 1, b => 1 } < id(M#{ a => 1, b => 3}),
+    false = M#{ a => 1 } < id(M#{ a => 1.0}),
+    false = M#{ a => 1.0 } < id(M#{ a => 1}),
+
+    true  = M#{ "a" => "hi", b => 134 } == id(M#{ b => 134,"a" => "hi"}),
+
     %% lists:sort
 
     SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}],
     [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]),
     [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs),
     [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)),
-
     ok.
 
 t_map_equal(Config) when is_list(Config) ->
-- 
cgit v1.2.3


From 0989fd90d50ff95d291971f62ed4dde7f5fba911 Mon Sep 17 00:00:00 2001
From: Nick Mills 
Date: Sat, 14 Mar 2015 00:30:17 -0400
Subject: Remove unused time macros from efile_drv

All uses of these macros were removed in
commit c3a615aa2da09bc3a0575e973959f800460a63de.
---
 erts/emulator/drivers/common/efile_drv.c | 17 -----------------
 1 file changed, 17 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index b62e9a0306..3b8e7acb6e 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -264,23 +264,6 @@ dt_private *get_dt_private(int);
 
 
 
-#define GET_TIME(i, b) \
-    (i).year  = get_int32((b) + 0 * 4); \
-    (i).month = get_int32((b) + 1 * 4); \
-    (i).day   = get_int32((b) + 2 * 4); \
-    (i).hour  = get_int32((b) + 3 * 4); \
-    (i).minute = get_int32((b) + 4 * 4); \
-    (i).second = get_int32((b) + 5 * 4)
-
-#define PUT_TIME(i, b) \
-  put_int32((i).year,  (b) + 0 * 4); \
-  put_int32((i).month, (b) + 1 * 4); \
-  put_int32((i).day,   (b) + 2 * 4); \
-  put_int32((i).hour,  (b) + 3 * 4); \
-  put_int32((i).minute,(b) + 4 * 4); \
-  put_int32((i).second,(b) + 5 * 4)
-
-
 #if ALWAYS_READ_LINE_AHEAD
 #define DEFAULT_LINEBUF_SIZE 2048
 #else
-- 
cgit v1.2.3


From f0cfce64b6a061ffeafeda254734f0b1f2f452fd Mon Sep 17 00:00:00 2001
From: Steve Vinoski 
Date: Sun, 1 Mar 2015 12:33:20 -0500
Subject: Ensure NIF term creation disallows illegal values

Add a check to enif_make_double to see if its double argument is
infinity or NaN, returning a badarg exception if it is. Change the
erl_nif documentation to specify that enif_make_double returns a
badarg exception if its double argument is either infinity or NaN. Add
tests to nif_SUITE for this change.

Add checks to the enif_make* functions for atoms to prevent the
creation of atoms whose name lengths are greater than the allowed
maximum atom length. The enif_make_atom and enif_make_atom_len
functions now return a badarg exception if the input string is too
long. The enif_make_existing_atom and enif_make_existing_atom_len
functions return false if the input string is too long. Change the
erl_nif documentation to reflect the changes to these functions. Add
tests to nif_SUITE for these changes.

Add a field to ErlNifEnv to track that a NIF has raised an exception
via enif_make_badarg. If a NIF calls enif_make_badarg but then ignores
its return value and instead tries to return a non-exception term as
its return value, the runtime still raises a badarg. This is needed to
prevent enif_make_badarg values resulting from calls to
enif_make_double, enif_make_atom, or enif_make_atom_len from being
erroneously stored within other terms and returned from a NIF. Calling
enif_make_badarg but not returning its return value has been
documented as being illegal ever since enif_make_badarg was added, but
the runtime has not enforced it until now. Add tests for regular and
dirty NIFs to ensure that calls to enif_make_badarg result in badarg
exceptions even if a NIF fails to return the result of
enif_make_badarg as its return value. Add documentation to
enif_make_badarg to specify that calling it raises a badarg even if a
NIF ignores its return value.
---
 erts/doc/src/erl_nif.xml                      | 23 +++++--
 erts/emulator/beam/beam_emu.c                 |  2 +
 erts/emulator/beam/erl_nif.c                  | 16 +++--
 erts/emulator/beam/global.h                   |  1 +
 erts/emulator/test/nif_SUITE.erl              | 82 +++++++++++++++++++++--
 erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 95 +++++++++++++++++++++++++--
 6 files changed, 197 insertions(+), 22 deletions(-)

(limited to 'erts')

diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 3de94be9ff..feba6daaa0 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -898,12 +898,14 @@ typedef enum {
     ERL_NIF_TERMenif_make_atom(ErlNifEnv* env, const char* name)
       Create an atom term
       

Create an atom term from the null-terminated C-string name - with iso-latin-1 encoding.

+ with iso-latin-1 encoding. If the length of name exceeds the maximum length + allowed for an atom, enif_make_atom returns a badarg exception.

ERL_NIF_TERMenif_make_atom_len(ErlNifEnv* env, const char* name, size_t len) Create an atom term

Create an atom term from the string name with length len. - Null-characters are treated as any other characters.

+ Null-characters are treated as any other characters. If len is greater than the maximum length + allowed for an atom, enif_make_atom returns a badarg exception.

ERL_NIF_TERMenif_make_badarg(ErlNifEnv* env) Make a badarg exception. @@ -911,8 +913,10 @@ typedef enum { an associated exception reason in env. If enif_make_badarg is called, the term it returns must be returned from the function that called it. No other return value - is allowed. Also, the term returned from enif_make_badarg may - be passed only to + is allowed. Once a NIF or any function it calls invokes enif_make_badarg, + the runtime ensures that a badarg exception is raised when the NIF + returns, even if the NIF attempts to return a non-exception term instead. + Also, the term returned from enif_make_badarg may be passed only to enif_is_exception and not to any other NIF API function.

@@ -931,7 +935,9 @@ typedef enum { ERL_NIF_TERMenif_make_double(ErlNifEnv* env, double d) Create a floating-point term -

Create a floating-point term from a double.

+

Create a floating-point term from a double. If the double argument is + not finite or is NaN, enif_make_double returns a badarg exception.

+
intenif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding encode) Create an existing atom term @@ -939,7 +945,8 @@ typedef enum { the null-terminated C-string name with encoding encode. If the atom already exists store the term in *atom and return true, otherwise - return false.

+ return false. If the length of name exceeds the maximum length + allowed for an atom, enif_make_existing_atom returns false.

intenif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding encoding) Create an existing atom term @@ -947,7 +954,9 @@ typedef enum { string name with length len and encoding encode. Null-characters are treated as any other characters. If the atom already exists store the term - in *atom and return true, otherwise return false.

+ in *atom and return true, otherwise return false. If len is greater + than the maximum length allowed for an atom, enif_make_existing_atom_len + returns false.

ERL_NIF_TERMenif_make_int(ErlNifEnv* env, int i) Create an integer term diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 8bfb7d2ad2..d352528059 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3523,6 +3523,8 @@ get_map_elements_fail: erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2]); reg[0] = r(0); nif_bif_result = (*fp)(&env, bif_nif_arity, reg); + if (env.exception_thrown) + nif_bif_result = THE_NON_VALUE; erts_post_nif(&env); } ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result)); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index adc3520ebb..ec82ef251e 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -127,6 +127,7 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif) env->heap_frag = NULL; env->fpe_was_unmasked = erts_block_fpe(); env->tmp_obj_list = NULL; + env->exception_thrown = 0; } static void pre_nif_noproc(ErlNifEnv* env, struct erl_module_nif* mod_nif) @@ -742,6 +743,7 @@ Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, Eterm enif_make_badarg(ErlNifEnv* env) { + env->exception_thrown = 1; BIF_ERROR(env->proc, BADARG); } @@ -964,7 +966,10 @@ ERL_NIF_TERM enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i) ERL_NIF_TERM enif_make_double(ErlNifEnv* env, double d) { - Eterm* hp = alloc_heap(env,FLOAT_SIZE_OBJECT); + Eterm* hp; + if (!isfinite(d)) + return enif_make_badarg(env); + hp = alloc_heap(env,FLOAT_SIZE_OBJECT); FloatDef f; f.fd = d; PUT_DOUBLE(f, hp); @@ -978,6 +983,8 @@ ERL_NIF_TERM enif_make_atom(ErlNifEnv* env, const char* name) ERL_NIF_TERM enif_make_atom_len(ErlNifEnv* env, const char* name, size_t len) { + if (len > MAX_ATOM_CHARACTERS) + return enif_make_badarg(env); return erts_atom_put((byte*)name, len, ERTS_ATOM_ENC_LATIN1, 1); } @@ -991,6 +998,8 @@ int enif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding encoding) { ASSERT(encoding == ERL_NIF_LATIN1); + if (len > MAX_ATOM_CHARACTERS) + return 0; return erts_atom_get(name, len, atom, ERTS_ATOM_ENC_LATIN1); } @@ -1754,14 +1763,13 @@ execute_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ASSERT(ep); if (ep->fp) fp = NULL; - if (is_non_value(result)) { + if (is_non_value(result) || env->exception_thrown) { if (proc->freason != TRAP) { - ASSERT(proc->freason == BADARG); return init_nif_sched_data(env, dirty_nif_exception, fp, 0, argc, argv); } else { if (ep->fp == NULL) restore_nif_mfa(proc, ep, 1); - return result; + return THE_NON_VALUE; } } else diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 32a2dc43e8..d58aecab9b 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -51,6 +51,7 @@ struct enif_environment_t /* ErlNifEnv */ ErlHeapFragment* heap_frag; int fpe_was_unmasked; struct enif_tmp_obj_t* tmp_obj_list; + int exception_thrown; /* boolean */ }; extern void erts_pre_nif(struct enif_environment_t*, Process*, struct erl_module_nif*); diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 4560077a51..c11a6ad7c5 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -39,7 +39,8 @@ get_length/1, make_atom/1, make_string/1, reverse_list_test/1, otp_9828/1, otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1, - dirty_nif_exception/1, nif_schedule/1 + dirty_nif_exception/1, nif_schedule/1, + nif_exception/1, nif_nan_and_inf/1, nif_atom_too_long/1 ]). -export([many_args_100/100]). @@ -68,7 +69,8 @@ all() -> make_string,reverse_list_test, otp_9828, otp_9668, consume_timeslice, - nif_schedule, dirty_nif, dirty_nif_send, dirty_nif_exception + nif_schedule, dirty_nif, dirty_nif_send, dirty_nif_exception, + nif_exception, nif_nan_and_inf, nif_atom_too_long ]. groups() -> @@ -1595,11 +1597,27 @@ dirty_nif_exception(Config) when is_list(Config) -> N when is_integer(N) -> ensure_lib_loaded(Config), try - call_dirty_nif_exception(), + %% this checks that the expected exception + %% occurs when the NIF returns the result + %% of enif_make_badarg directly + call_dirty_nif_exception(1), ?t:fail(expected_badarg) catch error:badarg -> - [{?MODULE,call_dirty_nif_exception,[],_}|_] = + [{?MODULE,call_dirty_nif_exception,[1],_}|_] = + erlang:get_stacktrace(), + ok + end, + try + %% this checks that the expected exception + %% occurs when the NIF calls enif_make_badarg + %% at some point but then returns a value that + %% isn't an exception + call_dirty_nif_exception(0), + ?t:fail(expected_badarg) + catch + error:badarg -> + [{?MODULE,call_dirty_nif_exception,[0],_}|_] = erlang:get_stacktrace(), ok end @@ -1608,6 +1626,57 @@ dirty_nif_exception(Config) when is_list(Config) -> {skipped,"No dirty scheduler support"} end. +nif_exception(Config) when is_list(Config) -> + ensure_lib_loaded(Config), + try + call_nif_exception(), + ?t:fail(expected_badarg) + catch + error:badarg -> + ok + end. + +nif_nan_and_inf(Config) when is_list(Config) -> + ensure_lib_loaded(Config), + try + call_nif_nan_or_inf(nan), + ?t:fail(expected_badarg) + catch + error:badarg -> + ok + end, + try + call_nif_nan_or_inf(inf), + ?t:fail(expected_badarg) + catch + error:badarg -> + ok + end, + try + call_nif_nan_or_inf(tuple), + ?t:fail(expected_badarg) + catch + error:badarg -> + ok + end. + +nif_atom_too_long(Config) when is_list(Config) -> + ensure_lib_loaded(Config), + try + call_nif_atom_too_long(all), + ?t:fail(expected_badarg) + catch + error:badarg -> + ok + end, + try + call_nif_atom_too_long(len), + ?t:fail(expected_badarg) + catch + error:badarg -> + ok + end. + next_msg(_Pid) -> receive M -> M @@ -1741,8 +1810,11 @@ consume_timeslice_nif(_,_) -> ?nif_stub. call_nif_schedule(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. send_from_dirty_nif(_) -> ?nif_stub. -call_dirty_nif_exception() -> ?nif_stub. +call_dirty_nif_exception(_) -> ?nif_stub. call_dirty_nif_zero_args() -> ?nif_stub. +call_nif_exception() -> ?nif_stub. +call_nif_nan_or_inf(_) -> ?nif_stub. +call_nif_atom_too_long(_) -> ?nif_stub. %% maps is_map_nif(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 85544db2ab..d5109f1e58 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -380,7 +380,8 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ ErlNifSInt64 sint64; ErlNifUInt64 uint64; double d; - ERL_NIF_TERM atom, ref1, ref2; + ERL_NIF_TERM atom, ref1, ref2, term; + size_t len; sint = INT_MIN; do { @@ -502,6 +503,7 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ goto error; } } + ref1 = enif_make_ref(env); ref2 = enif_make_ref(env); if (!enif_is_ref(env,ref1) || !enif_is_ref(env,ref2) @@ -1608,16 +1610,26 @@ static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_ static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { switch (argc) { - case 0: { + case 1: { ERL_NIF_TERM args[255]; int i; - for (i = 0; i < 255; i++) + args[0] = argv[0]; + for (i = 1; i < 255; i++) args[i] = enif_make_int(env, i); return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, call_dirty_nif_exception, 255, argv); } - case 1: - return enif_make_badarg(env); + case 2: { + int return_badarg_directly; + enif_get_int(env, argv[0], &return_badarg_directly); + assert(return_badarg_directly == 1 || return_badarg_directly == 0); + if (return_badarg_directly) + return enif_make_badarg(env); + else { + /* ignore return value */ enif_make_badarg(env); + return enif_make_atom(env, "ok"); + } + } default: return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, call_dirty_nif_exception, argc-1, argv); @@ -1637,6 +1649,74 @@ static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL } #endif +/* + * Call enif_make_badarg, but don't return its return value. Instead, + * return ok. Result should still be a badarg exception for the erlang + * caller. + */ +static ERL_NIF_TERM call_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + /* ignore return value */ enif_make_badarg(env); + return enif_make_atom(env, "ok"); +} + +static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + double val; + char arg[6]; + ERL_NIF_TERM res; + + assert(argc == 1); + enif_get_atom(env, argv[0], arg, sizeof arg, ERL_NIF_LATIN1); + if (strcmp(arg, "nan") == 0) { + /* Verify that enif_make_double raises a badarg for NaN */ +#ifdef NAN + val = NAN; +#else + val = 0.0/0.0; +#endif + } else { + /* Verify that enif_make_double raises a badarg for NaN and infinity */ +#ifdef INFINITY + val = INFINITY; +#else + val = 1.0/0.0; +#endif + } + res = enif_make_double(env, val); + assert(enif_is_exception(env, res)); + if (strcmp(arg, "tuple") == 0) { + return enif_make_tuple2(env, argv[0], res); + } else { + return res; + } +} + +static ERL_NIF_TERM call_nif_atom_too_long(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + char str[257]; + char arg[4]; + size_t len; + int i; + ERL_NIF_TERM res; + + assert(argc == 1); + enif_get_atom(env, argv[0], arg, sizeof arg, ERL_NIF_LATIN1); + /* Verify that creating an atom from a string that's too long results in a badarg */ + for (i = 0; i < sizeof str; ++i) { + str[i] = 'a'; + } + str[256] = '\0'; + if (strcmp(arg, "len") == 0) { + len = strlen(str); + res = enif_make_atom_len(env, str, len); + } else { + res = enif_make_atom(env, str); + } + assert(enif_is_exception(env, res)); + return res; +} + static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { return enif_make_int(env, enif_is_map(env,argv[0])); @@ -1818,9 +1898,12 @@ static ErlNifFunc nif_funcs[] = #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT {"call_dirty_nif", 3, call_dirty_nif}, {"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"call_dirty_nif_exception", 0, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, + {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND}, #endif + {"call_nif_exception", 0, call_nif_exception}, + {"call_nif_nan_or_inf", 1, call_nif_nan_or_inf}, + {"call_nif_atom_too_long", 1, call_nif_atom_too_long}, {"is_map_nif", 1, is_map_nif}, {"get_map_size_nif", 1, get_map_size_nif}, {"make_new_map_nif", 0, make_new_map_nif}, -- cgit v1.2.3 From 68f600f7466abca20ab5b2f81ab3c433a2b87064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erland=20Sch=C3=B6nbeck?= Date: Mon, 16 Mar 2015 15:17:26 +0100 Subject: Revert "Use new time API and be back-compatible in ssh" This reverts commit af972aaf14a5f53510e692f48f672f7e6805ee6d. Conflicts: lib/ssh/test/ssh_basic_SUITE.erl --- erts/test/otp_SUITE.erl | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 385353f046..171f722357 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -95,8 +95,7 @@ undefined_functions(Config) when is_list(Config) -> Undef5 = dialyzer_filter(Undef4), Undef6 = wx_filter(Undef5), Undef7 = gs_filter(Undef6), - Undef8 = diameter_filter(Undef7), - Undef = ssh_filter(Undef8), + Undef = diameter_filter(Undef7), case Undef of [] -> ok; @@ -220,7 +219,7 @@ gs_filter(Undef) -> end. diameter_filter(Undef) -> - %% Filter away function calls that are catched for OTP 18 time API + %% Filter away function calls that are catched. filter(fun({{diameter_lib,_,_},{erlang,convert_time_resolution,3}}) -> false; ({{diameter_lib,_,_},{erlang,monotonic_time,0}}) -> @@ -234,13 +233,6 @@ diameter_filter(Undef) -> (_) -> true end, Undef). -ssh_filter(Undef) -> - %% Filter away function calls that are catched for OTP 18 time API - filter(fun({{ssh_info,_,_},{erlang,timestamp,0}}) -> - false; - (_) -> true - end, Undef). - deprecated_not_in_obsolete(Config) when is_list(Config) -> ?line Server = ?config(xref_server, Config), ?line {ok,DeprecatedFunctions} = xref:q(Server, "DF"), -- cgit v1.2.3 From e0c0518a14b747cf02a0e5adfbd5b116a189422d Mon Sep 17 00:00:00 2001 From: Zandra Hird Date: Tue, 17 Mar 2015 09:10:56 +0100 Subject: update preloaded --- erts/preloaded/ebin/zlib.beam | Bin 14160 -> 14160 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 8783f189a4..9eaf8b9e59 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 6f9c07ae2694a2dc1f721fd10af22b8813d15bc1 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 12 Mar 2015 16:01:33 +0100 Subject: Fix a race condition when calling port_info/1 This variable hold the values returned by erlang:port_info/1 and shouldn't be static. Reported-by: Heinz Nikolaus Gies --- erts/emulator/beam/io.c | 2 +- erts/emulator/test/port_bif_SUITE.erl | 27 +++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 012a7d1a4b..a950998c13 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4484,7 +4484,7 @@ make_port_info_term(Eterm **hpp_start, int len; int start; static Eterm item[] = ERTS_PORT_INFO_1_ITEMS; - static Eterm value[sizeof(item)/sizeof(item[0])]; + Eterm value[sizeof(item)/sizeof(item[0])]; start = 0; len = sizeof(item)/sizeof(item[0]); diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index f439867e9c..0c5b09d45a 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -24,7 +24,7 @@ init_per_group/2,end_per_group/2, command/1, command_e_1/1, command_e_2/1, command_e_3/1, command_e_4/1, port_info1/1, port_info2/1, - port_info_os_pid/1, + port_info_os_pid/1, port_info_race/1, connect/1, control/1, echo_to_busy/1]). -export([do_command_e_1/1, do_command_e_2/1, do_command_e_4/1]). @@ -42,7 +42,8 @@ all() -> groups() -> [{command_e, [], [command_e_1, command_e_2, command_e_3, command_e_4]}, - {port_info, [], [port_info1, port_info2, port_info_os_pid]}]. + {port_info, [], + [port_info1, port_info2, port_info_os_pid, port_info_race]}]. init_per_suite(Config) -> Config. @@ -254,6 +255,28 @@ do_port_info_os_pid() -> true = erlang:port_close(P), ok. +port_info_race(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), + Top = self(), + P1 = open_port({spawn,Program}, [{packet,1}]), + P2 = open_port({spawn,Program}, [{packet,1}]), + Info1 = erlang:port_info(P1), + Info2 = erlang:port_info(P2), + F = fun Loop(Port, _, 0) -> + Top ! {ok,Port}; + Loop(Port, Info, N) -> + Info = erlang:port_info(Port), + Loop(Port, Info, N - 1) + end, + spawn_link(fun () -> F(P1, Info1, 1000) end), + spawn_link(fun () -> F(P2, Info2, 1000) end), + receive {ok,P1} -> ok end, + receive {ok,P2} -> ok end, + true = erlang:port_close(P1), + true = erlang:port_close(P2), + ok. + output_test(_, _, Input, Output) when Output > 16#1fffffff -> io:format("~p bytes received\n", [Input]); output_test(P, Bin, Input0, Output0) -> -- cgit v1.2.3 From 09db67d581e1e5858909aafec7d29226020cd227 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Wed, 18 Mar 2015 15:47:47 +0100 Subject: Fix thread name from driver api if opts was null the name was not set --- erts/emulator/beam/erl_drv_thread.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index 31b05d22af..240faa823d 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -604,10 +604,12 @@ erl_drv_thread_create(char *name, ethr_thr_opts ethr_opts = ETHR_THR_OPTS_DEFAULT_INITER; ethr_thr_opts *use_opts; - if (!opts) + if (!opts && !name) use_opts = NULL; else { - ethr_opts.suggested_stack_size = opts->suggested_stack_size; + if(opts) + ethr_opts.suggested_stack_size = opts->suggested_stack_size; + ethr_opts.name = name; use_opts = ðr_opts; } -- cgit v1.2.3 From 12fc63bcaf68b4a9e89ce91e1235aafb8bcdaee5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 13 Mar 2015 18:00:33 +0100 Subject: erts: Fix map bug in dec_term for 32-bit debug VM Adding ERTS_SWORD_MAX to a pointer does not work as a way to disable a bound check. Remove the hp_end from ErtsHeapFactory as it isn't really used anyway. --- erts/emulator/beam/erl_message.c | 1 - erts/emulator/beam/erl_message.h | 2 -- erts/emulator/beam/external.c | 1 - erts/emulator/beam/io.c | 1 - 4 files changed, 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index e4cbd8477d..43a03c793e 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1154,7 +1154,6 @@ Eterm* erts_produce_heap(ErtsHeapFactory* factory, Uint need, Uint xtra) } else { res = factory->hp; factory->hp += need; - ASSERT(factory->hp <= factory->hp_end); } return res; } diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index ece75a5ee4..6b8c3cebc7 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -71,8 +71,6 @@ struct erl_heap_fragment { typedef struct { Process* p; Eterm* hp; - Eterm* hp_end; - /* more to come... */ } ErtsHeapFactory; Eterm* erts_produce_heap(ErtsHeapFactory*, Uint need, Uint xtra); diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 65b4ae5412..9a5ef56c47 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3905,7 +3905,6 @@ dec_term_atom_common: factory.p = NULL; factory.hp = hp; /* We assume heap will suffice (see hashmap_over_estimated_heap_size) */ - factory.hp_end = hp + (ERTS_SWORD_MAX / sizeof(Eterm)); do { *hamt->objp = erts_hashmap_from_array(&factory, diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index b64854aac9..62254ca34d 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -5609,7 +5609,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) factory.p = NULL; factory.hp = hp; /* We assume heap will suffice (see hashmap_over_estimated_heap_size) */ - factory.hp_end = hp + (ERTS_SWORD_MAX / sizeof(Eterm)); mess = erts_hashmap_from_array(&factory, leafs, size, 1); -- cgit v1.2.3 From 401e2544564526039ac87b3ffe79eb8af30b37b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Mar 2015 11:06:44 +0100 Subject: erts: Ensure halfword has correct temp-heap for maps --- erts/emulator/beam/erl_map.c | 139 +++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 65 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 964c09e906..4183b532a9 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -1835,72 +1835,77 @@ Eterm* hashmap_iterator_prev(ErtsWStack* s) { const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) { Eterm *ptr, hdr; - Eterm th[2]; Uint ix,slot, lvl = 0; Uint32 hval,bp; + DeclareTmpHeapNoproc(th,2); + UseTmpHeapNoproc(2); for (;;) { - switch(primary_tag(node)) { - case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */ - ptr = list_val(node); - if (EQ(CAR(ptr), key)) { - return &(CDR(ptr)); - } - return NULL; - case TAG_PRIMARY_BOXED: - ptr = boxed_val(node); - hdr = *ptr; - ASSERT(is_header(hdr)); - - switch(hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_NODE_ARRAY: - ix = hashmap_index(hx); - hx = hashmap_shift_hash(th,hx,lvl,key); - node = ptr[ix+1]; - break; - case HAMT_SUBTAG_HEAD_ARRAY: - ix = hashmap_index(hx); - hx = hashmap_shift_hash(th,hx,lvl,key); - node = ptr[ix+2]; - break; - case HAMT_SUBTAG_NODE_BITMAP: - hval = MAP_HEADER_VAL(hdr); - ix = hashmap_index(hx); - bp = 1 << ix; - slot = hashmap_bitcount(hval & (bp - 1)); - - /* occupied */ - if (bp & hval) { - hx = hashmap_shift_hash(th,hx,lvl,key); - node = ptr[slot+1]; - break; - } - /* not occupied */ - return NULL; - case HAMT_SUBTAG_HEAD_BITMAP: - hval = MAP_HEADER_VAL(hdr); - ix = hashmap_index(hx); - bp = 1 << ix; - slot = hashmap_bitcount(hval & (bp - 1)); - - /* occupied */ - if (bp & hval) { - hx = hashmap_shift_hash(th,hx,lvl,key); - node = ptr[slot+2]; - break; - } - /* not occupied */ - return NULL; - default: - erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); - break; - } - break; - default: - erl_exit(1, "bad primary tag %p\r\n", node); - break; - } + switch(primary_tag(node)) { + case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */ + ptr = list_val(node); + UnUseTmpHeapNoproc(2); + if (EQ(CAR(ptr), key)) { + return &(CDR(ptr)); + } + return NULL; + case TAG_PRIMARY_BOXED: + ptr = boxed_val(node); + hdr = *ptr; + ASSERT(is_header(hdr)); + + switch(hdr & _HEADER_MAP_SUBTAG_MASK) { + case HAMT_SUBTAG_NODE_ARRAY: + ix = hashmap_index(hx); + hx = hashmap_shift_hash(th,hx,lvl,key); + node = ptr[ix+1]; + break; + case HAMT_SUBTAG_HEAD_ARRAY: + ix = hashmap_index(hx); + hx = hashmap_shift_hash(th,hx,lvl,key); + node = ptr[ix+2]; + break; + case HAMT_SUBTAG_NODE_BITMAP: + hval = MAP_HEADER_VAL(hdr); + ix = hashmap_index(hx); + bp = 1 << ix; + slot = hashmap_bitcount(hval & (bp - 1)); + + /* occupied */ + if (bp & hval) { + hx = hashmap_shift_hash(th,hx,lvl,key); + node = ptr[slot+1]; + break; + } + /* not occupied */ + UnUseTmpHeapNoproc(2); + return NULL; + case HAMT_SUBTAG_HEAD_BITMAP: + hval = MAP_HEADER_VAL(hdr); + ix = hashmap_index(hx); + bp = 1 << ix; + slot = hashmap_bitcount(hval & (bp - 1)); + + /* occupied */ + if (bp & hval) { + hx = hashmap_shift_hash(th,hx,lvl,key); + node = ptr[slot+2]; + break; + } + /* not occupied */ + UnUseTmpHeapNoproc(2); + return NULL; + default: + erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); + break; + } + break; + default: + erl_exit(1, "bad primary tag %p\r\n", node); + break; + } } + UnUseTmpHeapNoproc(2); return NULL; } @@ -2049,11 +2054,14 @@ unroll: Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, Uint *update_size, ErtsEStack *sp) { - Eterm node, fake, *ptr, hdr; + Eterm node, *ptr, hdr; Eterm res; Eterm *nhp = NULL; Uint32 ix, cix, bp, hval; Uint slot, n; + /* Needed for halfword */ + DeclareTmpHeapNoproc(fake,1); + UseTmpHeapNoproc(1); res = CONS(hp, key, value); hp += 2; @@ -2077,8 +2085,8 @@ Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, break; case TAG_PRIMARY_HEADER: /* subnodes, fake it */ - fake = node; - node = make_boxed(&fake); + *fake = node; + node = make_boxed(fake); case TAG_PRIMARY_BOXED: ptr = boxed_val(node); hdr = *ptr; @@ -2154,7 +2162,8 @@ Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, } while(!ESTACK_ISEMPTY(*sp)); - return res; + UnUseTmpHeapNoproc(1); + return res; } static Eterm hashmap_keys(Process* p, Eterm node) { -- cgit v1.2.3 From 7a9b4fe58c17c95c9be0ad13d33fce5042db24b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Mar 2015 14:45:17 +0100 Subject: erts: Add map decomposition wrappers * erts_internal:map_type/1 * erts_internal:map_hashmap_children/1 --- erts/preloaded/src/erts_internal.erl | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 2c5bd82cf0..5756d80424 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -30,7 +30,7 @@ -export([await_port_send_result/3]). -export([cmp_term/2]). --export([map_to_tuple_keys/1]). +-export([map_to_tuple_keys/1, map_type/1, map_hashmap_children/1]). -export([port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). @@ -178,3 +178,20 @@ cmp_term(_A,_B) -> map_to_tuple_keys(_M) -> erlang:nif_error(undefined). + +%% return the internal map type +-spec map_type(M) -> Type when + M :: map(), + Type :: 'flatmap' | 'hashmap' | 'hashmap_node'. + +map_type(_M) -> + erlang:nif_error(undefined). + +%% return the internal hashmap sub-nodes from +%% a hashmap node +-spec map_hashmap_children(M) -> Children when + M :: map(), %% hashmap node + Children :: [map() | nonempty_improper_list(term(),term())]. + +map_hashmap_children(_M) -> + erlang:nif_error(undefined). -- cgit v1.2.3 From c8bcf039d98ec880c5a1d2faf3b44c6033d86d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Mar 2015 14:47:53 +0100 Subject: erts: Update preloaded erts_internal.beam --- erts/preloaded/ebin/erts_internal.beam | Bin 4176 -> 4532 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index ba45e4e011..1fcfb53fb2 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ -- cgit v1.2.3 From 3c55267c6176a7183c59fc2ca6c1b278df080373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Mar 2015 16:08:56 +0100 Subject: erts: Do not treat errors as fatal in erl_printf_term --- erts/emulator/beam/erl_printf_term.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 8046f54a0c..ac5b139f8d 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -31,7 +31,7 @@ do { \ int res__ = erts_printf_char((FN), (ARG), (C)); \ if (res__ < 0) \ - abort(); \ + return res__; \ (CNT) += res__; \ } while (0) @@ -39,7 +39,7 @@ do { \ do { \ int res__ = erts_printf_string((FN), (ARG), (STR)); \ if (res__ < 0) \ - abort(); \ + return res__; \ (CNT) += res__; \ } while (0) @@ -47,7 +47,7 @@ do { \ do { \ int res__ = erts_printf_buf((FN), (ARG), (char*)(BUF), (LEN)); \ if (res__ < 0) \ - abort(); \ + return res__; \ (CNT) += res__; \ } while (0) @@ -55,7 +55,7 @@ do { \ do { \ int res__ = erts_printf_pointer((FN), (ARG), (void *) (PTR)); \ if (res__ < 0) \ - abort(); \ + return res__; \ (CNT) += res__; \ } while (0) @@ -63,7 +63,7 @@ do { \ do { \ int res__ = erts_printf_uword((FN), (ARG), (C), (P), (W), (I)); \ if (res__ < 0) \ - abort(); \ + return res__; \ (CNT) += res__; \ } while (0) @@ -71,7 +71,7 @@ do { \ do { \ int res__ = erts_printf_sword((FN), (ARG), (C), (P), (W), (I)); \ if (res__ < 0) \ - abort(); \ + return res__; \ (CNT) += res__; \ } while (0) @@ -79,7 +79,7 @@ do { \ do { \ int res__ = erts_printf_double((FN), (ARG), (C), (P), (W), (I)); \ if (res__ < 0) \ - abort(); \ + return res__; \ (CNT) += res__; \ } while (0) -- cgit v1.2.3 From feffe2deac13ad61477c7ecf63342815c750bfe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 Mar 2015 15:44:07 +0100 Subject: erts: Ensure maps uses _rel functions in halfword --- erts/emulator/beam/erl_map.c | 19 +++++++++++++------ erts/emulator/beam/erl_map.h | 13 ++++++++++--- 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 4183b532a9..bd6da0a6f1 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -165,7 +165,7 @@ erts_maps_get(Eterm key, Eterm map) #endif { Uint32 hx; - if (is_flatmap(map)) { + if (is_flatmap_rel(map, map_base)) { Eterm *ks, *vs; flatmap_t *mp; Uint n, i; @@ -189,16 +189,16 @@ erts_maps_get(Eterm key, Eterm map) } for (i = 0; i < n; i++) { - if (eq_rel(ks[i], NULL, key, map_base)) { + if (eq_rel(ks[i], map_base, key, NULL)) { return &vs[i]; } } return NULL; } - ASSERT(is_hashmap(map)); + ASSERT(is_hashmap_rel(map, map_base)); hx = hashmap_make_hash(key); - return erts_hashmap_get(hx, key, map); + return erts_hashmap_get_rel(hx, key, map, map_base); } BIF_RETTYPE maps_find_2(BIF_ALIST_2) { @@ -1833,7 +1833,13 @@ Eterm* hashmap_iterator_prev(ErtsWStack* s) { } } -const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) { +const Eterm * +#if HALFWORD_HEAP +erts_hashmap_get_rel(Uint32 hx, Eterm key, Eterm node, Eterm *map_base) +#else +erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) +#endif +{ Eterm *ptr, hdr; Uint ix,slot, lvl = 0; Uint32 hval,bp; @@ -1845,7 +1851,8 @@ const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) { case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */ ptr = list_val(node); UnUseTmpHeapNoproc(2); - if (EQ(CAR(ptr), key)) { + + if (eq_rel(CAR(ptr), map_base, key, NULL)) { return &(CDR(ptr)); } return NULL; diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index d075d87c83..1333a734a8 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -109,7 +109,6 @@ Eterm* hashmap_iterator_next(struct ErtsWStack_* s); Eterm* hashmap_iterator_prev(struct ErtsWStack_* s); int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp); Eterm erts_hashmap_from_array(ErtsHeapFactory*, Eterm *leafs, Uint n, int reject_dupkeys); -const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map); #define erts_hashmap_from_ks_and_vs(P, KS, VS, N) \ erts_hashmap_from_ks_and_vs_extra((P), (KS), (VS), (N), THE_NON_VALUE, THE_NON_VALUE); @@ -117,16 +116,24 @@ const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map); Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n, Eterm k, Eterm v); -#if HALFWORD_HEAP const Eterm * +#if HALFWORD_HEAP erts_maps_get_rel(Eterm key, Eterm map, Eterm *map_base); # define erts_maps_get(A, B) erts_maps_get_rel(A, B, NULL) #else -const Eterm * erts_maps_get(Eterm key, Eterm map); # define erts_maps_get_rel(A, B, B_BASE) erts_maps_get(A, B) #endif +const Eterm * +#if HALFWORD_HEAP +erts_hashmap_get_rel(Uint32 hx, Eterm key, Eterm node, Eterm *map_base); +# define erts_hashmap_get(Hx, K, M) erts_hashmap_get_rel(Hx, K, M, NULL) +#else +erts_hashmap_get(Uint32 hx, Eterm key, Eterm map); +# define erts_hashmap_get_rel(Hx, K, M, M_BASE) erts_hashmap_get(Hx, K, M) +#endif + /* hamt nodes v2.0 * * node :: leaf | array | bitmap -- cgit v1.2.3 From a8599e3fbeb4628268f8761cbb1102d24d552133 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 18 Mar 2015 18:34:24 +0100 Subject: erts: Fix bug in ESTACK and WSTACK The [ew]default field would get uninitialised when the stack was saved and later restored. Detected by valgrind. --- erts/emulator/beam/global.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index ef7a183d08..42daa2c9ef 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -426,6 +426,7 @@ do {\ memcpy((dst)->start, (s).start,_wsz*sizeof(Eterm));\ (dst)->sp = (dst)->start + _wsz;\ (dst)->end = (dst)->start + DEF_ESTACK_SIZE;\ + (dst)->edefault = NULL;\ (dst)->alloc_type = (s).alloc_type;\ } else\ *(dst) = (s);\ @@ -593,6 +594,7 @@ do {\ memcpy((dst)->wstart, s.wstart,_wsz*sizeof(UWord));\ (dst)->wsp = (dst)->wstart + _wsz;\ (dst)->wend = (dst)->wstart + DEF_WSTACK_SIZE;\ + (dst)->wdefault = NULL;\ (dst)->alloc_type = s.alloc_type;\ } else\ *(dst) = s;\ -- cgit v1.2.3 From 65c7116bb38013c3d829f21b00bf094606e46731 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 19 Mar 2015 15:40:29 +0100 Subject: erts: Fix bug in binary_to_term with more than one big map --- erts/emulator/beam/external.c | 2 +- erts/emulator/test/map_SUITE.erl | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 9a5ef56c47..458ebd8aa0 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3899,7 +3899,6 @@ dec_term_atom_common: /* Iterate through all the hamts and build tree nodes. */ if (hamt_list) { - struct dec_term_hamt_placeholder* hamt = hamt_list; ErtsHeapFactory factory; factory.p = NULL; @@ -3907,6 +3906,7 @@ dec_term_atom_common: /* We assume heap will suffice (see hashmap_over_estimated_heap_size) */ do { + struct dec_term_hamt_placeholder* hamt = hamt_list; *hamt->objp = erts_hashmap_from_array(&factory, hamt->leafs, hamt->size, diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index c421886431..91a5706320 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -1147,6 +1147,13 @@ t_map_encode_decode(Config) when is_list(Config) -> 97,55 % 55 :: integer() >>), + %% many maps in same binary + MapList = lists:foldl(fun(K, [M|_]=Acc) -> [M#{K => K} | Acc] end, + [#{}], + lists:seq(1,100)), + MapList = binary_to_term(term_to_binary(MapList)), + MapListR = lists:reverse(MapList), + MapListR = binary_to_term(term_to_binary(MapListR)), %% error cases %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>> -- cgit v1.2.3 From f958a56e0c7c672cd19f386b58f9abd54e128a6d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 19 Mar 2015 17:41:00 +0100 Subject: erts: Silence valgrind warning in nif_SUITE.c Hmm, seems like gcc (4.4.3 at least) can switch the order of an &&-expression and do the last condition first if it think it's more efficient (and without side effects I hope). Which led to valgrind complaining about 'prev_ret' being used uninitialized in this case. --- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 7b49f23d0a..5a3be84825 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1717,8 +1717,9 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER return enif_make_int(env, __LINE__); cnt = 0; + next_ret = 1; while(enif_map_iterator_get_pair(env,&iter_f,&key,&value)) { - if (cnt && !next_ret) + if (!next_ret) return enif_make_int(env, __LINE__); list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f); next_ret = enif_map_iterator_next(env,&iter_f); @@ -1731,8 +1732,9 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER return enif_make_int(env, __LINE__); cnt = 0; + prev_ret = 1; while(enif_map_iterator_get_pair(env,&iter_b,&key,&value)) { - if (cnt && !prev_ret) + if (!prev_ret) return enif_make_int(env, __LINE__); /* Test that iter_f can step "backwards" */ -- cgit v1.2.3 From 6487aac5977cf470bc6a2cd0964da2850ee38717 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 30 Oct 2014 23:57:01 +0100 Subject: Introduce a new time API The old time API is based on erlang:now/0. The major issue with erlang:now/0 is that it was intended to be used for so many unrelated things. This tied these unrelated operations together and unnecessarily caused performance, scalability as well as accuracy, and precision issues for operations that do not need to have such issues. The new API spreads different functionality over multiple functions in order to improve on this. The new API consists of a number of new BIFs: - erlang:convert_time_unit/3 - erlang:monotonic_time/0 - erlang:monotonic_time/1 - erlang:system_time/0 - erlang:system_time/1 - erlang:time_offset/0 - erlang:time_offset/1 - erlang:timestamp/0 - erlang:unique_integer/0 - erlang:unique_integer/1 - os:system_time/0 - os:system_time/1 and a number of extensions of existing BIFs: - erlang:monitor(time_offset, clock_service) - erlang:system_flag(time_offset, finalize) - erlang:system_info(os_monotonic_time_source) - erlang:system_info(time_offset) - erlang:system_info(time_warp_mode) - erlang:system_info(time_correction) - erlang:system_info(start_time) See the "Time and Time Correction in Erlang" chapter of the ERTS User's Guide for more information. --- erts/aclocal.m4 | 242 ++- erts/autoconf/win32.config.cache.static | 1 - erts/autoconf/win64.config.cache.static | 1 - erts/doc/src/Makefile | 2 + erts/doc/src/erl.xml | 47 +- erts/doc/src/erlang.xml | 775 +++++++-- erts/doc/src/time_correction.xml | 923 ++++++++--- erts/emulator/Makefile.in | 3 +- erts/emulator/beam/atom.names | 13 +- erts/emulator/beam/benchmark.h | 6 +- erts/emulator/beam/bif.c | 277 ++-- erts/emulator/beam/bif.h | 1 + erts/emulator/beam/bif.tab | 20 + erts/emulator/beam/big.c | 40 + erts/emulator/beam/big.h | 7 + erts/emulator/beam/erl_alloc.types | 1 + erts/emulator/beam/erl_bif_binary.c | 1 + erts/emulator/beam/erl_bif_ddll.c | 1 + erts/emulator/beam/erl_bif_info.c | 108 +- erts/emulator/beam/erl_bif_timer.c | 1 + erts/emulator/beam/erl_bif_trace.c | 1 + erts/emulator/beam/erl_bif_unique.c | 556 +++++++ erts/emulator/beam/erl_bif_unique.h | 131 ++ erts/emulator/beam/erl_gc.c | 1 + erts/emulator/beam/erl_init.c | 129 +- erts/emulator/beam/erl_lock_check.c | 7 +- erts/emulator/beam/erl_lock_count.c | 9 +- erts/emulator/beam/erl_lock_count.h | 2 +- erts/emulator/beam/erl_monitors.h | 3 +- erts/emulator/beam/erl_nif.c | 1 + erts/emulator/beam/erl_process.c | 58 +- erts/emulator/beam/erl_process.h | 6 +- erts/emulator/beam/erl_thr_progress.c | 24 +- erts/emulator/beam/erl_time.h | 272 +++- erts/emulator/beam/erl_time_sup.c | 1857 ++++++++++++++++------ erts/emulator/beam/global.h | 5 - erts/emulator/beam/io.c | 1 + erts/emulator/beam/sys.h | 107 +- erts/emulator/beam/time.c | 477 +++--- erts/emulator/beam/utils.c | 5 +- erts/emulator/sys/common/erl_check_io.c | 19 +- erts/emulator/sys/common/erl_check_io.h | 6 +- erts/emulator/sys/common/erl_poll.c | 221 ++- erts/emulator/sys/common/erl_poll.h | 6 +- erts/emulator/sys/ose/erl_poll.c | 69 +- erts/emulator/sys/ose/sys.c | 4 +- erts/emulator/sys/unix/erl_unix_sys.h | 120 +- erts/emulator/sys/unix/sys.c | 24 +- erts/emulator/sys/unix/sys_time.c | 398 ++++- erts/emulator/sys/win32/erl_poll.c | 60 +- erts/emulator/sys/win32/erl_win_sys.h | 53 +- erts/emulator/sys/win32/sys.c | 4 +- erts/emulator/sys/win32/sys_time.c | 199 ++- erts/emulator/test/Makefile | 1 + erts/emulator/test/long_timers_test.erl | 96 +- erts/emulator/test/monitor_SUITE.erl | 113 +- erts/emulator/test/time_SUITE.erl | 407 ++++- erts/emulator/test/unique_SUITE.erl | 390 +++++ erts/etc/common/erlexec.c | 18 +- erts/etc/common/heart.c | 10 +- erts/etc/unix/cerl.src | 4 +- erts/example/Makefile | 2 +- erts/example/time_compat.erl | 302 ++++ erts/include/internal/ethread_header_config.h.in | 15 + erts/preloaded/ebin/erlang.beam | Bin 97956 -> 100456 bytes erts/preloaded/ebin/erts_internal.beam | Bin 4164 -> 4688 bytes erts/preloaded/ebin/init.beam | Bin 48808 -> 48800 bytes erts/preloaded/src/erlang.erl | 136 +- erts/preloaded/src/erts_internal.erl | 30 + erts/vsn.mk | 2 +- 70 files changed, 7231 insertions(+), 1600 deletions(-) create mode 100644 erts/emulator/beam/erl_bif_unique.c create mode 100644 erts/emulator/beam/erl_bif_unique.h create mode 100644 erts/emulator/test/unique_SUITE.erl create mode 100644 erts/example/time_compat.erl (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index d78025b0be..989f697201 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -724,6 +724,117 @@ esac ])# AC_C_DOUBLE_MIDDLE_ENDIAN +AC_DEFUN(ERL_MONOTONIC_CLOCK, +[ + AC_CACHE_CHECK([for clock_gettime() with monotonic clock type], erl_cv_clock_gettime_monotonic, + [ + for clock_type in CLOCK_HIGHRES CLOCK_MONOTONIC CLOCK_MONOTONIC_PRECISE; do + AC_TRY_COMPILE([ +#include + ], + [ + struct timespec ts; + long long result; + clock_gettime($clock_type,&ts); + result = ((long long) ts.tv_sec) * 1000000000LL + + ((long long) ts.tv_nsec); + ], + erl_cv_clock_gettime_monotonic=$clock_type, + erl_cv_clock_gettime_monotonic=no) + test $erl_cv_clock_gettime_monotonic = no || break + done + ]) + + AC_CHECK_FUNC(clock_getres) + + AC_CHECK_FUNC(gethrtime) + + AC_CACHE_CHECK([for mach clock_get_time()], erl_cv_mach_clock_get_time, + [ + AC_TRY_COMPILE([ +#include +#include + ], + [ + kern_return_t res; + clock_serv_t clk_srv; + mach_timespec_t time_spec; + + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clk_srv); + res = clock_get_time(clk_srv, &time_spec); + mach_port_deallocate(mach_task_self(), clk_srv); + ], + erl_cv_mach_clock_get_time=yes, + erl_cv_mach_clock_get_time=no) + ]) + + case $erl_cv_clock_gettime_monotonic-$ac_cv_func_gethrtime-$erl_cv_mach_clock_get_time-$host_os in + *-*-*-win32) + erl_monotonic_clock_func=GetTickCount + ;; + CLOCK_*-*-*-linux*) + if test X$cross_compiling != Xyes; then + linux_kernel_vsn_=`uname -r` + case $linux_kernel_vsn_ in + [[0-1]].*|2.[[0-5]]|2.[[0-5]].*) + erl_monotonic_clock_func=times + ;; + *) + erl_monotonic_clock_func=clock_gettime + ;; + esac + else + case X$erl_xcomp_linux_clock_gettime_correction in + X) + AC_MSG_WARN([result clock_gettime guessed because of cross compilation]) + erl_monotonic_clock_func=clock_gettime + ;; + Xyes|Xno) + if test $erl_xcomp_linux_clock_gettime_correction = yes; then + erl_monotonic_clock_func=clock_gettime + else + erl_monotonic_clock_func=times + fi + ;; + *) + AC_MSG_ERROR([Bad erl_xcomp_linux_clock_gettime_correction value: $erl_xcomp_linux_clock_gettime_correction]) + ;; + esac + fi + ;; + no-no-no-linux*) + erl_monotonic_clock_func=times + ;; + CLOCK_*-*-*-*) + erl_monotonic_clock_func=clock_gettime + ;; + no-yes-*-*) + erl_monotonic_clock_func=gethrtime + ;; + no-no-yes-*) + erl_monotonic_clock_func=mach_clock_get_time + ;; + no-no-no-*) + erl_monotonic_clock_func=none + ;; + esac + + erl_monotonic_clock_lib= + erl_monotonic_clock_id= + case $erl_monotonic_clock_func in + clock_gettime) + erl_monotonic_clock_id="$erl_cv_clock_gettime_monotonic" + AC_CHECK_LIB(rt, clock_gettime, [erl_monotonic_clock_lib="-lrt"]) + ;; + mach_clock_get_time) + erl_monotonic_clock_id=SYSTEM_CLOCK + ;; + *) + ;; + esac + +]) + dnl ---------------------------------------------------------------------- dnl dnl LM_CHECK_THR_LIB @@ -1016,12 +1127,32 @@ AC_ARG_WITH(with_sparc_memory_order, LM_CHECK_THR_LIB ERL_INTERNAL_LIBS +ERL_MONOTONIC_CLOCK + +case $erl_monotonic_clock_func in + clock_gettime) + AC_DEFINE(ETHR_HAVE_CLOCK_GETTIME_MONOTONIC, [1], [Define if you have a clock_gettime() with a monotonic clock]) + ;; + mach_clock_get_time) + AC_DEFINE(ETHR_HAVE_MACH_CLOCK_GET_TIME, [1], [Define if you have a mach clock_get_time() with a monotonic clock]) + ;; + gethrtime) + AC_DEFINE(ETHR_HAVE_GETHRTIME, [1], [Define if you have a monotonic gethrtime()]) + ;; + *) + ;; +esac + +if test "x$erl_monotonic_clock_id" != "x"; then + AC_DEFINE_UNQUOTED(ETHR_MONOTONIC_CLOCK_ID, [$erl_monotonic_clock_id], [Define to the monotonic clock id to use]) +fi + ethr_have_native_atomics=no ethr_have_native_spinlock=no ETHR_THR_LIB_BASE="$THR_LIB_NAME" ETHR_THR_LIB_BASE_TYPE="$THR_LIB_TYPE" ETHR_DEFS="$THR_DEFS" -ETHR_X_LIBS="$THR_LIBS $ERTS_INTERNAL_X_LIBS" +ETHR_X_LIBS="$THR_LIBS $ERTS_INTERNAL_X_LIBS $erl_monotonic_clock_lib" ETHR_LIBS= ETHR_LIB_NAME= @@ -1660,7 +1791,6 @@ AC_SUBST(ETHR_X86_SSE2_ASM) ]) - dnl ---------------------------------------------------------------------- dnl dnl ERL_TIME_CORRECTION @@ -1676,93 +1806,33 @@ dnl work... dnl AC_DEFUN(ERL_TIME_CORRECTION, -[if test x$ac_cv_func_gethrtime = x; then - AC_CHECK_FUNC(gethrtime) -fi -if test x$clock_gettime_correction = xunknown; then - AC_TRY_COMPILE([#include ], - [struct timespec ts; - long long result; - clock_gettime(CLOCK_MONOTONIC,&ts); - result = ((long long) ts.tv_sec) * 1000000000LL + - ((long long) ts.tv_nsec);], - clock_gettime_compiles=yes, - clock_gettime_compiles=no) -else - clock_gettime_compiles=no -fi - - -AC_CACHE_CHECK([how to correct for time adjustments], erl_cv_time_correction, [ -case $clock_gettime_correction in - yes) - erl_cv_time_correction=clock_gettime;; - no|unknown) - case $ac_cv_func_gethrtime in - yes) - erl_cv_time_correction=hrtime ;; - no) - case $host_os in - linux*) - case $clock_gettime_correction in - unknown) - if test x$clock_gettime_compiles = xyes; then - if test X$cross_compiling != Xyes; then - linux_kernel_vsn_=`uname -r` - case $linux_kernel_vsn_ in - [[0-1]].*|2.[[0-5]]|2.[[0-5]].*) - erl_cv_time_correction=times ;; - *) - erl_cv_time_correction=clock_gettime;; - esac - else - case X$erl_xcomp_linux_clock_gettime_correction in - X) - erl_cv_time_correction=cross;; - Xyes|Xno) - if test $erl_xcomp_linux_clock_gettime_correction = yes; then - erl_cv_time_correction=clock_gettime - else - erl_cv_time_correction=times - fi;; - *) - AC_MSG_ERROR([Bad erl_xcomp_linux_clock_gettime_correction value: $erl_xcomp_linux_clock_gettime_correction]);; - esac - fi - else - erl_cv_time_correction=times - fi - ;; - *) - erl_cv_time_correction=times ;; - esac - ;; - *) - erl_cv_time_correction=none ;; - esac - ;; - esac - ;; -esac -]) -xrtlib="" -case $erl_cv_time_correction in +ERL_MONOTONIC_CLOCK + +case $erl_monotonic_clock_func in times) - AC_DEFINE(CORRECT_USING_TIMES,[], - [Define if you do not have a high-res. timer & want to use times() instead]) + AC_DEFINE(OS_MONOTONIC_TIME_USING_TIMES, [1], [Define if you want to implement erts_os_monotonic_time() using times()]) ;; - clock_gettime|cross) - if test $erl_cv_time_correction = cross; then - erl_cv_time_correction=clock_gettime - AC_MSG_WARN([result clock_gettime guessed because of cross compilation]) - fi - xrtlib="-lrt" - AC_DEFINE(GETHRTIME_WITH_CLOCK_GETTIME,[1], - [Define if you want to use clock_gettime to simulate gethrtime]) + mach_clock_get_time) + AC_DEFINE(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME, [1], [Define if you want to implement erts_os_monotonic_time() using mach clock_get_time()]) + ;; + clock_gettime) + AC_DEFINE(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME, [1], [Define if you want to implement erts_os_monotonic_time() using clock_gettime()]) + ;; + gethrtime) + AC_DEFINE(OS_MONOTONIC_TIME_USING_GETHRTIME, [1], [Define if you want to implement erts_os_monotonic_time() using gethrtime()]) + ;; + *) ;; esac + +xrtlib="$erl_monotonic_clock_lib" +if test "x$erl_monotonic_clock_id" != "x"; then + AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID_STR, ["$erl_monotonic_clock_id"], [Define as a string of monotonic clock id to use]) + AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID, [$erl_monotonic_clock_id], [Define to monotonic clock id to use]) +fi + dnl dnl Check if gethrvtime is working, and if to use procfs ioctl dnl or (yet to be written) write to the procfs ctl file. @@ -1835,6 +1905,7 @@ case X$erl_xcomp_gethrvtime_procfs_ioctl in esac ]) +LIBRT=$xrtlib case $erl_gethrvtime in procfs_ioctl) AC_DEFINE(HAVE_GETHRVTIME_PROCFS_IOCTL,[1], @@ -1894,7 +1965,6 @@ case $erl_gethrvtime in case $host_os in linux*) AC_MSG_RESULT([no; not stable]) - LIBRT=$xrtlib ;; *) AC_MSG_RESULT($erl_clock_gettime) @@ -1907,17 +1977,15 @@ case $erl_gethrvtime in cross) erl_clock_gettime=no AC_MSG_WARN([result no guessed because of cross compilation]) - LIBRT=$xrtlib ;; *) - LIBRT=$xrtlib ;; esac ;; esac - AC_SUBST(LIBRT) ;; esac +AC_SUBST(LIBRT) ])dnl dnl ---------------------------------------------------------------------- diff --git a/erts/autoconf/win32.config.cache.static b/erts/autoconf/win32.config.cache.static index b387db2b22..b3328e5414 100755 --- a/erts/autoconf/win32.config.cache.static +++ b/erts/autoconf/win32.config.cache.static @@ -221,7 +221,6 @@ ac_cv_type_size_t=${ac_cv_type_size_t=yes} ac_cv_type_uid_t=${ac_cv_type_uid_t=no} ac_cv_type_void_p=${ac_cv_type_void_p=yes} ac_cv_working_alloca_h=${ac_cv_working_alloca_h=no} -erl_cv_time_correction=${erl_cv_time_correction=none} erts_cv___after_morecore_hook_can_track_malloc=${erts_cv___after_morecore_hook_can_track_malloc=no} erts_cv_fwrite_unlocked=${erts_cv_fwrite_unlocked=no} erts_cv_have__end_symbol=${erts_cv_have__end_symbol=no} diff --git a/erts/autoconf/win64.config.cache.static b/erts/autoconf/win64.config.cache.static index a8a2bfb59c..c7d92c7000 100755 --- a/erts/autoconf/win64.config.cache.static +++ b/erts/autoconf/win64.config.cache.static @@ -262,7 +262,6 @@ ac_cv_type_signal=${ac_cv_type_signal=void} ac_cv_type_size_t=${ac_cv_type_size_t=yes} ac_cv_type_uid_t=${ac_cv_type_uid_t=no} ac_cv_working_alloca_h=${ac_cv_working_alloca_h=no} -erl_cv_time_correction=${erl_cv_time_correction=none} erts_cv___after_morecore_hook_can_track_malloc=${erts_cv___after_morecore_hook_can_track_malloc=no} erts_cv_fwrite_unlocked=${erts_cv_fwrite_unlocked=no} erts_cv_have__end_symbol=${erts_cv_have__end_symbol=no} diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile index e8b856c3ff..a83aa9b875 100644 --- a/erts/doc/src/Makefile +++ b/erts/doc/src/Makefile @@ -177,6 +177,8 @@ release_docs_spec: docs $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(ERL_TOP)/erts/example/time_compat.erl \ + "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index d11f6b0c6d..19a8e1f789 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -495,24 +495,35 @@ , not (). Note also that is used instead of on Windows.

- - -

Disable compensation for sudden changes of system time.

-

Normally, will not immediately reflect - sudden changes in the system time, in order to keep timers - (including ) working. Instead, the time - maintained by is slowly adjusted towards - the new system time. (Slowly means in one percent adjustments; - if the time is off by one minute, the time will be adjusted - in 100 minutes.)

-

When the option is given, this slow adjustment - will not take place. Instead will always - reflect the current system time. Note that timers are based - on . If the system time jumps, timers - then time out at the wrong time.

-

NOTE: You can check whether the adjustment is enabled or - disabled by calling - erlang:system_info(tolerant_timeofday).

+ + +

Enable or disable + time correction:

+ + true +

Enable time correction. This is the default if + time correction is supported on the specific platform.

+ + false +

Disable time correction.

+
+

For backwards compatibility, the boolean value can be omitted. + This is interpreted as +c false. +

+
+ + +

Set + time warp mode: +

+ + no_time_warp +

No Time Warp Mode (the default)

+ single_time_warp +

Single Time Warp Mode

+ multi_time_warp +

Multi Time Warp Mode

+
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 483d81cfb6..3cbfd372ce 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -58,7 +58,71 @@ -

See now/0.

+

See erlang:timestamp/0.

+
+
+ + + +

Currently supported time unit representations:

+ + PartsPerSecond :: integer() >= 1 +

Time unit expressed in parts per second. That is, + the time unit equals 1/PartsPerSecond second.

+ + seconds +

Symbolic representation of the time unit + represented by the integer 1.

+ + milli_seconds +

Symbolic representation of the time unit + represented by the integer 1000.

+ + micro_seconds +

Symbolic representation of the time unit + represented by the integer 1000000.

+ + nano_seconds +

Symbolic representation of the time unit + represented by the integer 1000000000.

+ + native +

Symbolic representation of the native time unit + used by the Erlang runtime system.

+ +

The native time unit is determined at + runtime system start, and will remain the same until + the runtime system terminates. If a runtime system + is stopped and then started again (even on the same + machine), the native time unit of the new + runtime system instance may differ from the + native time unit of the old runtime system + instance.

+ +

One can get an approximation of the native + time unit by calling erlang:convert_time_unit(1, + seconds, native). The result equals the number + of whole native time units per second. In case + the number of native time units per second does + not add up to a whole number, the result will be + rounded downwards.

+ + +

The value of the native time unit gives + you more or less no information at all about the + quality of time values. It sets an upper bound for + the resolution as well as for the precision, but it + gives absolutely no information at all about the + accuracy.

+
+
+ +
+ +

The time_unit/0 type may be extended. Use + erlang:convert_time_unit/3 + in order to convert time values between time units.

+
@@ -584,6 +648,22 @@
+ + + Convert time unit of a time value + +

Converts the Time value of time unit + FromUnit to the corresponding + ConvertedTime value of time unit + ToUnit. The result is rounded + using the floor function.

+ +

You may lose accuracy and precision when converting + between time units. In order to minimize such loss, collect all + data at native time unit and do the conversion on the end + result.

+
+
Compute crc32 (IEEE 802.3) checksum @@ -2191,14 +2271,15 @@ os_prompt%
- Return an almost unique reference + Return a unique reference -

Returns an almost unique reference.

-

The returned reference will re-occur after approximately 2^82 - calls; therefore it is unique enough for practical purposes.

-
-> make_ref().
-#Ref<0.0.0.135>
+

Return a unique + reference. The reference is unique among + connected nodes.

+

Known issue: When a node is restarted multiple + times with the same node name, references created + on a newer node can be mistaken for a reference + created on an older node with the same node name.

@@ -2499,97 +2580,178 @@ os_prompt% - + + + + + Start monitoring -

The calling process starts monitoring Item which is - an object of type Type.

-

Currently only processes can be monitored, i.e. the only - allowed Type is process, but other types may be - allowed in the future.

-

Item can be:

- - pid() - -

The pid of the process to monitor.

-
- {RegName, Node} - -

A tuple consisting of a registered name of a process and - a node name. The process residing on the node Node - with the registered name RegName will be monitored.

-
- RegName - -

The process locally registered as RegName will be - monitored.

-
-
- -

When a process is monitored by registered name, the process - that has the registered name at the time when - monitor/2 is called will be monitored. +

Send a monitor request of type Type to the + entity identified by Item. The caller of + monitor/2 will later be notified by a monitor message on the + following format if the monitored state is changed:

+ {Tag, MonitorRef, Type, Object, Info} +

The monitor request is an asynchronous signal. That is, it + takes time before the signal reach its destination.

+

Currently valid Types:

+ + process + +

Monitor the existence of the process identified by + Item. Currently valid + Items in combination with the + process Type:

+ + pid() + +

The process identifier of the process to monitor.

+
+ {RegisteredName, Node} + +

A tuple consisting of a registered name of a process and + a node name. The process residing on the node Node + with the registered name {RegisteredName, Node} will + be monitored.

+
+ RegisteredName + +

The process locally registered as RegisteredName + will become monitored.

+
+
+

When a process is monitored by registered name, the + process that has the registered name at the time when the + monitor request reach its destination will be monitored. The monitor will not be effected, if the registered name is - unregistered.

-
-

A 'DOWN' message will be sent to the monitoring - process if Item dies, if Item does not exist, - or if the connection is lost to the node which Item - resides on. A 'DOWN' message has the following pattern:

- -{'DOWN', MonitorRef, Type, Object, Info} -

where MonitorRef and Type are the same as - described above, and:

- - Object - -

A reference to the monitored object:

- - the pid of the monitored process, if Item was - specified as a pid. - {RegName, Node}, if Item was specified as - {RegName, Node}. - {RegName, Node}, if Item was specified as - RegName. Node will in this case be the - name of the local node (node()). - -
- Info - -

Either the exit reason of the process, noproc - (non-existing process), or noconnection (no - connection to Node).

-
-
- -

If/when monitor/2 is extended (e.g. to - handle other item types than process), other - possible values for Object, and Info in the - 'DOWN' message will be introduced.

-
-

The monitoring is turned off either when the 'DOWN' - message is sent, or when - demonitor/1 - is called.

-

If an attempt is made to monitor a process on an older node - (where remote process monitoring is not implemented or one - where remote process monitoring by registered name is not - implemented), the call fails with badarg.

-

Making several calls to monitor/2 for the same - Item is not an error; it results in as many, completely - independent, monitorings.

+ unregistered, or unregistered and later registered on another + process.

+

The monitor is triggered either when the monitored process + terminates, is non existing, or if the connection to it is + lost. In the case the connection to it is lost, we do not know + if it still exist or not. After this type of monitor has been + triggered, the monitor is automatically removed.

+

When the monitor is triggered a 'DOWN' message will + be sent to the monitoring process. A 'DOWN' message has + the following pattern:

+ {'DOWN', MonitorRef, Type, Object, Info} +

where MonitorRef and Type are the same as + described above, and:

+ + Object + +

equals:

+ + Item + If Item was specified by a + pid. + {RegisteredName, Node} + If Item was specified as + RegisteredName, or {RegisteredName, Node} + where Node corresponds to the node that the + monitored process resides on. + +
+ Info + +

Either the exit reason of the process, noproc + (non-existing process), or noconnection (no + connection to the node where the monitored process + resides).

+
+

The monitoring is turned off either when the 'DOWN' + message is sent, or when + demonitor/1 + is called.

+

If an attempt is made to monitor a process on an older node + (where remote process monitoring is not implemented or one + where remote process monitoring by registered name is not + implemented), the call fails with badarg.

+ +

The format of the 'DOWN' message changed in the 5.2 + version of the emulator (OTP release R9B) for monitor + by registered name. The Object element of + the 'DOWN' message could in earlier versions + sometimes be the pid of the monitored process and sometimes + be the registered name. Now the Object element is + always a tuple consisting of the registered name and + the node name. Processes on new nodes (emulator version 5.2 + or greater) will always get 'DOWN' messages on + the new format even if they are monitoring processes on old + nodes. Processes on old nodes will always get 'DOWN' + messages on the old format.

+
+ + time_offset + +

Monitor changes in + time offset + between + Erlang + monotonic time and + Erlang + system time. There is only one valid + Item in combination with the + time_offset Type, namely the atom + clock_service. Note that the atom clock_service is + not the registered name of a process. In this specific + case it serves as an identifier of the runtime system internal + clock service at current runtime system instance.

+ +

The monitor is triggered when the time offset is changed. + This either if the time offset value is changed, or if the + offset is changed from preliminary to final during + finalization + of the time offset when the + single + time warp mode is used. When a change from preliminary + to final time offset is made, the monitor will be triggered once + regardless of whether the time offset value was changed due to + the finalization or not.

+ +

If the runtime system is in + multi + time warp mode, the time offset will be changed when + the runtime system detects that the + OS system + time has changed. The runtime system will, however, + not detect this immediately when it happens. A task checking + the time offset is scheduled to execute at least once a minute, + so under normal operation this should be detected within a + minute, but during heavy load it might take longer time.

+ +

The monitor will not be automatically removed + after it has been triggered. That is, repeated changes of + the time offset will trigger the monitor repeatedly.

+ +

When the monitor is triggered a 'CHANGE' message will + be sent to the monitoring process. A 'CHANGE' message has + the following pattern:

+ {'CHANGE', MonitorRef, Type, Item, NewTimeOffset} +

where MonitorRef, Type, and + Item are the same as described above, and + NewTimeOffset is the new time offset.

+ +

When the 'CHANGE' message has been received you are + guaranteed not to retrieve the old time offset when calling + erlang:time_offset(). + Note that you may observe the change of the time offset + when calling erlang:time_offset() before you + get the 'CHANGE' message.

+ +
+ +

Making several calls to monitor/2 for the same + Item and/or Type is not + an error; it results in many, completely independent, + monitorings.

+

The monitor functionality is expected to be extended. That is, + other Types and Items + are expected to be supported in the future.

-

The format of the 'DOWN' message changed in the 5.2 - version of the emulator (OTP release R9B) for monitor by registered name. The Object element of - the 'DOWN' message could in earlier versions - sometimes be the pid of the monitored process and sometimes - be the registered name. Now the Object element is - always a tuple consisting of the registered name and - the node name. Processes on new nodes (emulator version 5.2 - or greater) will always get 'DOWN' messages on - the new format even if they are monitoring processes on old - nodes. Processes on old nodes will always get 'DOWN' - messages on the old format.

+

If/when monitor/2 is extended, other + possible values for Tag, Object, and + Info in the monitor message will be introduced.

@@ -2639,6 +2801,51 @@ os_prompt% option list is malformed.

+ + + Current Erlang monotonic time + +

Returns the current + Erlang + monotonic time in native + time unit. This + is a monotonically increasing time since some unspecified point in + time.

+ +

This is a + monotonically increasing time, but not a + strictly monotonically increasing + time. That is, consecutive calls to + erlang:monotonic_time/0 may produce the same result.

+ +

Different runtime system instances will use different + unspecified points in time as base for their Erlang monotonic clocks. + That is, it is pointless comparing monotonic times from + different runtime system instances. Different runtime system instances + may also place this unspecified point in time different relative + runtime system start. It may be placed in the future (time at start + will be a negative value), the past (time at start will be a + positive value), or the runtime system start (time at start will + be zero). The monotonic time as of runtime system start can be + retrieved by calling + erlang:system_info(start_time).

+
+
+ + + Current Erlang monotonic time + +

Returns the current + Erlang + monotonic time converted + into the Unit passed as argument.

+ +

Same as calling + erlang:convert_time_unit(erlang:monotonic_time(), + native, Unit) + however optimized for commonly used Units.

+
+
Stop execution with a given reason @@ -2734,6 +2941,13 @@ os_prompt% Elapsed time since 00:00 GMT +

This function is deprecated! Do not use it! + See the users guide chapter + Time and Time Correction + for more information. Specifically the + Dos and Dont's + section for information on what to use instead of erlang:now/0. +

Returns the tuple {MegaSecs, Secs, MicroSecs} which is the elapsed time since 00:00 GMT, January 1, 1970 (zero hour) on the assumption that the underlying OS supports this. @@ -2746,10 +2960,6 @@ os_prompt%

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.

-

If you do not need the return value to be unique and - monotonically increasing, use - os:timestamp/0 - instead to avoid some overhead.

@@ -5496,6 +5706,35 @@ ok

Returns the old value of the flag.

+ + + + Finalize the Time Offset + +

Finalizes the time offset + when the single + time warp mode is being used. If another time warp mode than + the "single time warp mode" is used, the time offset state will be left + unchanged.

+

Returns the old state identifier. That is, if:

+ +

preliminary is returned, finalization was + performed and the time offset is now final.

+ +

final is returned, the time offset was + already in the final state. This either due to another + erlang:system_flag(time_offset, finalize) call, or + due to the + no + time warp mode being used.

+ +

volatile is returned, the time offset + cannot be finalized due to the + multi + time warp mode being used.

+
+
+
@@ -5776,6 +6015,15 @@ ok + + + + + + + + + Information about the system

Returns various information about the current system @@ -6163,6 +6411,57 @@ ok documentation of versions in the system principles guide.

+ os_monotonic_time_source + +

Returns a list containing information about the source of + OS + monotonic time that is used by the runtime system.

+

In case [] is returned, no OS monotonic time is + available. The list contains two-tuples with Keys + as first element, and Values as second element. The + order if these tuples is undefined. Currently the following + tuples may be part of the list, but more tuples may be + introduced in the future:

+ + {function, Function} +

Function is the name of the funcion + used. This tuple always exist if OS monotonic time is + available to the runtime system.

+ + {clock_id, ClockId} +

This tuple only exist if Function + can be used with different clocks. ClockId + corresponds to the clock identifer used when calling + Function.

+ + {resolution, OsMonotonicTimeResolution} +

Highest possible resolution of current + OS monotonic time source as parts per second. If + no resolution information can be retreived from + the OS, OsMonotonicTimeResolution will be + set to the resolution of the time unit of + Functions return value. That is, the actual + resolution may be lower than + OsMonotonicTimeResolution. Also note that + the resolution does not say anything about the + accuracy, and that the precision might not align + with the resolution. You do, however, know that the + precision won't be higher than + OsMonotonicTimeResolution.

+ + {parallel, Parallel} +

Parallel equals yes if + Function is called in parallel from multiple + threads. If it is not called in parallel, because + calls needs to be serialized, Parallel equals + no.

+ + {time, OsMonotonicTime} +

OsMonotonicTime equals current OS + monotonic time in native + time unit.

+
+
port_parallelism

Returns the default port parallelism scheduling hint used. For more information see the @@ -6288,6 +6587,11 @@ ok

Returns true if the emulator has been compiled with smp support; otherwise, false.

+ start_time +

The Erlang monotonic + time in native + time unit at the + time when current Erlang runtime system instance started.

system_version

Returns a string containing version number and @@ -6311,12 +6615,64 @@ ok (driver_async()) as an integer.

+ time_correction +

Returns a boolean value indicating whether + time correction + is enabled or not. +

+ time_offset +

Returns the state of the time offset:

+ + preliminary +

The time offset is preliminary, and will be changed + at a later time when being finalized. The preliminary time offset + is used during the preliminary phase of the + single + time warp mode.

+ + final +

The time offset is final. This + either due to the use of the + no + time warp mode, or due to the time offset having + been finalized when using the + single + time warp mode.

+ + volatile +

The time offset is volatile. That is, it may + change at any time. This due to the + multi + time warp mode being used.

+
+
+ time_warp_mode +

Returns a value identifying the + time warp + mode being used:

+ + no_time_warp +

The no + time warp mode is being used.

+ + single_time_warp +

The single + time warp mode is being used.

+ + multi_time_warp +

The multi + time warp mode is being used.

+
+
tolerant_timeofday -

Returns whether compensation for sudden changes of system - time is enabled or disabled.

-

See also +c - command line flag.

+

Returns whether a pre erts-7.0 backwards compatible compensation + for sudden changes of system time is enabled or disabled. + Such compensation is enabled when the + time offset is + final, and + time correction + is enabled.

trace_control_word @@ -6595,7 +6951,44 @@ ok
+ + + Current Erlang system time + +

Returns current + Erlang system time + in native + time unit.

+

Calling erlang:system_time() is equivalent to: + erlang:monotonic_time() + + + erlang:time_offset().

+ +

This time is not a monotonically increasing time + in the general case. For more information, see the documentation of + time warp modes in the + ERTS User's Guide.

+
+
+ + + Current Erlang system time + +

Returns current + Erlang system time + converted into the Unit passed as argument.

+ +

Calling erlang:system_time(Unit) is equivalent to: + erlang:convert_time_unit(erlang:system_time(), + native, Unit).

+ +

This time is not a monotonically increasing time + in the general case. For more information, see the documentation of + time warp modes in the + ERTS User's Guide.

+
+
Encode a term to an Erlang external term format binary @@ -6671,6 +7064,88 @@ ok {9,42,44} + + + Current time offset + +

Returns the current time offset between + Erlang monotonic time + and + Erlang system time in + native time unit. + Current time offset added to an Erlang monotonic time gives + corresponding Erlang system time.

+ +

The time offset may or may not change during operation depending + on the time + warp mode used.

+ + +

A change in time offset may be observed at slightly + different points in time by different processes.

+ +

If the runtime system is in + multi + time warp mode, the time offset will be changed when + the runtime system detects that the + OS system + time has changed. The runtime system will, however, + not detect this immediately when it happens. A task checking + the time offset is scheduled to execute at least once a minute, + so under normal operation this should be detected within a + minute, but during heavy load it might take longer time.

+
+
+
+ + + Current time offset + +

Returns the current time offset between + Erlang monotonic time + and + Erlang system time + converted into the Unit passed as argument.

+ +

Same as calling + erlang:convert_time_unit(erlang:time_offset(), native, Unit) + however optimized for commonly used Units.

+
+
+ + + + Current Erlang System time + +

Returns current + Erlang system time + on the format {MegaSecs, Secs, MicroSecs}. This format is + the same that os:timestamp/0 + and the now deprecated erlang:now/0 + uses. The reason for the existence of erlang:timestamp() is + purely to simplify usage for existing code that assumes this timestamp + format. Current Erlang system time can more efficiently be retrieved in + the time unit of your choice using + erlang:system_time/1.

+ +

The erlang:timestamp() BIF is equivalent to:

+timestamp() -> + ErlangSystemTime = erlang:system_time(micro_seconds), + MegaSecs = ErlangSystemTime div 1000000000000, + Secs = ErlangSystemTime div 1000000 - MegaSecs*1000000, + MicroSecs = ErlangSystemTime rem 1000000, + {MegaSecs, Secs, MicroSecs}. +

It however use a native implementation which does + not build garbage on the heap and with slightly better + performance.

+ +

This time is not a monotonically increasing time + in the general case. For more information, see the documentation of + time warp modes in the + ERTS User's Guide.

+
+ +
Tail of a list @@ -7435,6 +7910,100 @@ ok a valid date and time.

+ + + Get a unique integer value + +

Generates and returns an + integer + unique on current runtime system instance. The same as calling + erlang:unique_integer([]).

+
+
+ + + Get a unique integer value + +

Generates and returns an + integer + unique on current runtime system + instance. The integer is unique in the + sense that this BIF, using the same set of + modifiers, will not return the same integer more + than once on the current runtime system instance. + Each integer value can of course be constructed + by other means.

+ +

By default, i.e. when [] is passed as + ModifierList, both negative and + positive integers will be returned. This is order + to be able to utilize the range of integers that do + not need to be heap allocated as much as possible. + By default the returned integers are also only + guaranteed to be unique, i.e., any integer returned + may be either smaller, or larger than previously + returned integers.

+ +

Currently valid Modifiers:

+ + + positive +

Return only positive integers.

+

Note that by passing the positive modifier + you will get heap allocated integers (big-nums) + quicker.

+
+ + monotonic +

Return + strictly + monotonically increasing integers + corresponding to creation time. That is, the integer + returned will always be larger than previously + returned integers on the current runtime system + instance.

+

These values can be used when ordering events + on the runtime system instance. That is, if both + X = erlang:unique_integer([monotonic]) and + Y = erlang:unique_integer([monotonic]) are + executed by different processes (or the same + process) on the same runtime system instance and + X < Y we know that X was created + before Y.

+

Strictly monotonically increasing values + are inherently quite expensive to generate and scales + poorly. This since the values needs to be + synchronized. That is, do not pass the monotonic + modifier unless you really need strictly monotonically + increasing values.

+
+ +
+ +

All currently valid Modifiers + can be combined. Repeated (valid) + Modifiers in the ModifierList + are ignored.

+ +

Note that the set of integers returned by + unique_integer/1 using diffrent sets of + Modifiers will overlap. + For example, by calling unique_integer([monotonic]), + and unique_integer([positive, monotonic]) + repeatedly, you will eventually see some integers being + returned by both calls.

+ +

Failures:

+ + badarg + if ModifierList is not a + proper list. + badarg + if Modifier is not a + valid modifier. + +
+
Remove a link, if there is one, to another process or port diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index 7f7c28fc30..3bc3d04186 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -21,8 +21,8 @@ - Time and time correction in Erlang - Patrik Nyblom + Time and Time Correction in Erlang + @@ -31,6 +31,176 @@ PA1 time_correction.xml
+ +
+ New Extended Time Functionality +

As of OTP 18 (ERTS version 7.0) the time functionality of + Erlang has been extended. This both includes a + new API + for time, as well as + time warp + modes which alters the behavior of the system when + system time changes.

+

The default + time warp mode has the same behavior as before, and the + old API will still work, so you are not required to change + anything unless you want to. However, you are strongly + encouraged to use the new API instead of the old API based + on erlang:now/0. + erlang:now/0 has been deprecated since it is and forever + will be a scalability bottleneck. By using the new API you will + automatically get scalability and performance improvements. This + will also enable you to use the + multi time warp mode + which improves accuracy, and precision of time measurements.

+
+ +
+ Some Terminology +

In order to make it easier to understand this document we first + define some terminology. This is a mixture of our own terminology + (Erlang/OS system time, Erlang/OS monotonic time, time warp) + and globally accepted terminology.

+ + +
+ Monotonically Increasing +

In a monotonically increasing sequence of values, all values + that have a predecessor are either larger than, or equal to its + predecessor.

+
+ + +
+ Strictly Monotonically Increasing +

In a strictly monotonically increasing sequence of values, + all values that have a predecessor are larger than its + predecessor.

+
+ + +
+ UT1 +

Universal Time. Based on the rotation of the earth. Conceptually + mean solar time at 0° longitude.

+
+ + +
+ UTC +

Coordinated Universal Time. UTC almost align with + UT1, however, UTC uses the + SI definition of a second which is not exactly of the same length + as the second used by UT1. This means that UTC slowly drifts from + UT1. In order to keep UTC relatively in sync with UT1, leap seconds + are inserted, and potentially also deleted. That is, an UTC day may + be 86400, 86401, or 86399 seconds long.

+
+ + +
+ POSIX Time +

Time since + Epoch. + Epoch is defined to be 00:00:00 UTC, + January 1, 1970. + A day in POSIX time + is defined to be exactly 86400 seconds long. Strangely enough + Epoch is defined to be a time in UTC, and UTC have another + definition of how long a day is. Quoting the Open Group + "POSIX time is therefore not necessarily UTC, despite its appearance". The effect of this is that when an UTC leap second is + inserted, POSIX time either stops for a second, or repeats the + last second. If an UTC leap second would be deleted (has never + happened yet), POSIX time would make a one second leap forward.

+
+ + +
+ OS System Time +

The operating systems view of + POSIX time. It can be + retrieved by calling + os:system_time(). + This may or may not be an accurate view of POSIX time. This time + may typically be adjusted both backwards and forwards without + limitation. That is, huge leaps both backwards and forwards in time + may be observed.

+
+ + +
+ OS Monotonic Time +

A monotonically increasing time provided by the operating + system. This time does not leap and have a relatively steady + frequency although not completely correct. However, it is not + uncommon that the OS monotonic time stops if the system is + suspended. This time typically increase since some unspecified + point in time that is not connected to + OS system time. Note that + this type of time is not necessarily provided by all operating + systems.

+
+ + +
+ Erlang System Time +

The Erlang runtime systems view of + POSIX time. It can be + retrieved by calling + erlang:system_time(). + This time may or may not be an accurate view of POSIX time, and may + or may not align with OS system + time. The time + warp mode determines how it behaves when OS system + time suddenly change.

+
+ + +
+ Erlang Monotonic Time +

A monotonically increasing time provided by the + Erlang runtime system. The Erlang monotonic time increase since + some unspecified point in time. It can be retrieved by calling + erlang:monotonic_time(). + The accuracy, and precision of Erlang monotonic time heavily + depends on the accuracy and precision of + OS monotonic time, + the accuracy and precision of + OS system time as well + as on the + time warp mode + used. On a system that is lacking OS monotonic time, the Erlang + monotonic time can only guarantee monotonicity and can more or less + not give any other guarantees. The frequency adjustments made to + the Erlang monotonic time depends on the time warp mode + used.

+ +

Internally in the runtime system the Erlang monotonic + time is the "time engine" that is used for more or less + everything that has anything to do with time. All timers + regardless of it is a receive ... after timer, BIF timer, + or a timer in the timer module are triggered + relative Erlang monotonic time. Even + Erlang system + time is based on Erlang monotonic time. + By adding current Erlang monotonic time with current time + offset you get current Erlang system time. Current time + offset can be retrieved by calling + erlang:time_offset/0. +

+
+ + +
+ Time Warp +

A time warp is a leap forwards or backwards in time.

+
+ +
+ +
+ Introduction +

Time is vital to an Erlang program and, more importantly, correct time is vital to an Erlang program. As Erlang is a language with soft real time properties and we have the possibility to express @@ -83,192 +253,587 @@ microsecond resolution or much less, but generally it has a drift that is not to be ignored.

-

So we have this monotonic ticking and we have the wall clock - time. Two unreliable times that together can give us an estimate of - an actual wall clock time that does not jump around and that - monotonically moves forward. If the tick counter has a high - resolution, this is fairly easy to do, if the counter has a low - resolution, it's more expensive, but still doable down to - frequencies of 50-60 Hz (of the tick counter).

- -

So the corrected time is the nearest approximation of an atomic - clock that is available on the computer. We want it to have the - following properties:

- - Monotonic - The clock should not move backwards - Intervals should be near the truth - We want the actual time (as measured by an atomic clock or - an astronomer) that passes between two time stamps, T1 and T2, to be as - near to T2 - T1 as possible. - Tight coupling to the wall clock - We want a timer that is to be fired when the wall clock - reaches a time in the future, to fire as near to that point in - time as possible - -

To meet all the criteria, we have to utilize both times in such a - way that Erlangs "corrected time" moves slightly slower or slightly - faster than the wall clock to get in sync with it. The word - "slightly" means a maximum of 1% difference to the wall clock time, - meaning that a sudden change in the wall clock of one minute, takes - 100 minutes to fix, by letting all "corrected time" move 1% slower - or faster.

- -

Needless to say, correcting for a faulty handling of daylight - saving time may be disturbing to a user comparing wall clock - time to for example calendar:now_to_local_time(erlang:now()). But - calendar:now_to_local_time/1 is not supposed to be used for presenting wall - clock time to the user.

- -

Time correction is not perfect, but it saves you from the havoc - of clocks jumping around, which would make timers in your program - fire far to late or far to early and could bring your whole system - to it's knees (or worse) just because someone detected a small error - in the wall clock time of the server where your program runs. So - while it might be confusing, it is still a really good feature of - Erlang and you should not throw it away using time functions which - may give you higher benchmark results, not unless you really know - what you're doing.

+
+
- What does time correction mean in my system? -

Time correction means that Erlang estimates a time from current - and previous settings of the wall clock, and it uses a fairly - exact tick counter to detect when the wall clock time has jumped - for some reason, slowly adjusting to the new value.

- -

In practice, this means that the difference between two calls - to time corrected functions, like erlang:now(), might differ up to - one percent from the corresponding calls to non time corrected - functions (like os:timestamp()). Furthermore, if comparing - calendar:local_time/0 to calendar:now_to_local_time(erlang:now()), - you might temporarily see a difference, depending on how well kept your - system is.

- -

It is important to understand that it is (to the program) - always unknown if it is the wall clock time that moves in the - wrong pace or the Erlang corrected time. The only way to determine - that, is to have an external source of universally correct time. If - some such source is available, the wall clock time can be kept - nearly perfect at all times, and no significant difference will be - detected between erlang:now/0's pace and the wall clock's.

- -

Still, the time correction will mean that your system keeps - it's real time characteristics very well, even when the wall clock - is unreliable.

+ Time Correction +

If time correction is enabled, the Erlang runtime system + will make use of both + OS system time + and OS monotonic time, + in order to make adjustments of the frequency of the Erlang + monotonic clock. Time correction will ensure that + Erlang monotonic time + will not warp, and that the frequency is relatively accurate. + The type of adjustments made to the frequency depends on the + time warp mode used. This will be discussed in more details in + the time warp modes + section below.

+ +

By default time correction will be enabled if support for + it on the specific platform exist. Support for it includes + both an OS monotonic time provided by the OS, and an + implementation in the Erlang runtime system utilizing the + OS monotonic time. You can check if your system has support + for OS monotonic time by calling + erlang:system_info(os_monotonic_time_source), + and you can check if time correction is enabled on your + system by calling + erlang:system_info(time_correction).

+ +

Time correction is enabled or disabled by passing the + +c [true|false] + command line argument to erl.

+ +

If time correction is disabled, Erlang monotonic time + may warp forwards, it may stop and even freeze for extended + periods of time, and there are no guarantees that the frequency + of the Erlang monotonic clock is accurate or stable.

+ +

You typically never want to disable time correction. + Previously there was a performance penalty associated with time + correction, but nowadays it is most often the other way around. + By disabling time correction you are likely to get bad scalability, + bad performance, and bad time measurements.

+ + +
- Where does Erlang use corrected time? -

For all functionality where real time characteristics are - desirable, time correction is used. This basically means:

- - erlang:now/0 - The infamous erlang:now/0 function uses time correction so - that differences between two "now-timestamps" will correspond to - other timeouts in the system. erlang:now/0 also holds other - properties, discussed later. - receive ... after - Timeouts on receive uses time correction to determine a - stable timeout interval. - The timer module - As the timer module uses other built in functions which - deliver corrected time, the timer module itself works with - corrected time. - erlang:start_timer/3 and erlang:send_after/3 - The timer BIF's work with corrected time, so that they - will not fire prematurely or too late due to changes in the wall - clock time. - - -

All other functionality in the system where erlang:now/0 or any - other time corrected functionality is used, will of course - automatically benefit from it, as long as it's not "optimized" to - use some other time stamp function (like os:timestamp/0).

- -

Modules like calendar and functions like erlang:localtime/0 use - the wall clock time as it is currently set on the system. They - will not use corrected time. However, if you use a now-value and - convert it to local time, you will get a corrected local time - value, which may or may not be what you want. Typically older code - tend to use erlang:now/0 as a wall clock time, which is usually - correct (at least when testing), but might surprise you when - compared to other times in the system.

+ Time Warp Safe Code +

Time warp safe code is code that is able to handle + a time warp of + Erlang system time. +

+ +

erlang:now/0 + behaves very bad when Erlang system time warps. When Erlang + system time do a time warp backwards, the values returned + from erlang:now/0 will freeze (if you disregard the + micro second increments made due to the actual call) until + OS system time reach the point of the last value returned by + erlang:now/0. This freeze might continue for very + long periods of time. It might take years, decades, + and even longer than this until the freeze stops.

+ +

All uses of erlang:now/0 are not necessarily + time warp unsafe. If you do not use it to get time, it + will be time warp safe. However all uses of + erlang:now/0 are suboptimal from a performance + and scalability perspective. So you really want to replace + the usage of it with other functionality. For examples + of how to replace the usage of erlang:now/0, + see the Dos and Donts + section.

+ +
- What is erlang:now/0 really? -

erlang:now/0 is a function designed to serve multiple purposes - (or a multi-headed beast if you're a VM designer). It is expected - to hold the following properties:

- - Monotonic - erlang:now() never jumps backwards - it always moves - forward - Interval correct - The interval between two erlang:now() calls is expected to - correspond to the correct time in real life (as defined by an - atomic clock, or better) - Absolute correctness - The erlang:now/0 value should be possible to convert to an - absolute and correct date-time, corresponding to the real world - date and time (the wall clock) - System correspondence - The erlang:now/0 value converted to a date-time is - expected to correspond to times given by other programs on the - system (or by functions like os:timestamp/0) - Unique - No two calls to erlang:now on one Erlang node should - return the same value - -

All these requirements are possible to uphold at the same - time if (and only if):

- - The wall clock time of the system is perfect - The system (Operating System) time needs to be perfectly - in sync with the actual time as defined by an atomic clock or - a better time source. A good installation using NTP, and that is - up to date before Erlang starts, will have properties that for - most users and programs will be near indistinguishable from the - perfect time. Note that any larger corrections to the time done - by hand, or after Erlang has started, will partly (or - temporarily) invalidate some of the properties, as the time is - no longer perfect. - Less than one call per microsecond to erlang:now/0 is - done - This means that at any microsecond interval in - time, there can be no more than one call to erlang:now/0 in the - system. However, for the system not to loose it's properties - completely, it's enough that it on average is no more than one - call per microsecond (in one Erlang node). - -

The uniqueness property of erlang:now/0 is the most limiting - property. It means that erlang:now() maintains a global state and - that there is a hard-to-check property of the system that needs to - be maintained. For most applications this is still not a problem, - but a future system might very well manage to violate the - frequency limit on the calls globally. The uniqueness property is - also quite useless, as there are globally unique references that - provide a much better unique value to programs. However the - property will need to be maintained unless a really subtle - backward compatibility issue is to be introduced.

+ Time Warp Modes + +

Current Erlang system + time is determined by adding current + Erlang monotonic time + with current + time offset. The + time offset is managed differently depending on which time + warp mode you use. The time warp mode is set by passing the + +C + [no_time_warp|single_time_warp|multi_time_warp] + command line argument to erl.

+ + +
+ No Time Warp Mode +

The time offset is determined at runtime system start + and will after this not change. This is the default behavior. + Not because it is the best mode (which it isn't). It is + default only because this is how the runtime system always + has behaved until ERTS version 7.0, and you have to ensure + that your Erlang code that may execute during a time warp is + time warp safe + before you can enable other modes.

+ +

Since the time offset is not allowed to change, time + correction needs to adjust the frequency of the Erlang + monotonic clock in order to smoothly align Erlang system + time with OS system time. A big downside of this approach + is that we on purpose will use a faulty frequency on the + Erlang monotonic clock if adjustments are needed. This + error may be as big as 1%. This error will show up in all + time measurements in the runtime system.

+ +

If time correction is not enabled, the Erlang monotonic + time will freeze when the OS system time leap backwards. + The freeze of the monotonic time will continue until + OS system time catch up. The freeze may continue for + a very long time. When OS system time leaps forwards, + Erlang monotonic time will also leap forward.

+
+ + +
+ Single Time Warp Mode +

This mode is more or less a backwards compatibility mode + as of its introduction.

+

On an embedded system it is not uncommon that the system + has no power supply at all, not even a battery, when it is + shut off. The system clock on such a system will typically + be way off when the system boots. If the + no time warp mode + is used, and the Erlang runtime system is started before + the OS system time has been corrected, the Erlang system + time may be wrong for a very long time, even centuries or + more.

+

If you for some reason need to use Erlang code that + is not + time warp safe, + and you need to start the Erlang runtime system before the OS + system time has been corrected, you may want to use the single + time warp mode. Note that there are limitations to when you can + execute time warp unsafe code using this mode. If it is possible + to only utilize time warp safe code, it is much better to use + the multi time warp + mode instead. +

+ +

Using the single time warp mode, the time offset is + handled in two phases:

+ + + Preliminary Phase + +

The preliminary phase starts when the runtime + system starts. A preliminary time offset based on + current OS system time is determined. This offset will + from now on be fixed during the whole preliminary phase.

+ +

If time correction is enabled, the Erlang + monotonic clock will only use the OS monotonic time as + time source during this phase. That is, during the + preliminary phase changes in OS system time will have + no effect on Erlang system time and/or Erlang + monotonic time what so ever.

+ +

If time correction is disabled, changes in OS system + time will effect the monotonic clock the same way as + when the no time warp + mode is used.

+
+ + Final Phase + + +

The final phase begin when the user finalize the time + offset by calling + erlang:system_flag(time_offset, finalize). + The finalization can only be performed once. +

+ +

During finalization, the time offset is adjusted and + fixated so that current Erlang system time align with + current OS system time. Since the time offset + may be changed, the Erlang system time may do + a time warp at this point. The time offset will from + now on be fixed until the runtime system terminates. + If time correction has been enabled, the time correction + also begins when this phase begins. When the system is + in the final phase it behaves exactly as in the + no time warp + mode.

+ +
+
+ +

In order for this to work properly there are two + requirements that the user needs to ensure are + satisfied:

+ + + Forward Time Warp +

The time warp made when finalizing the time offset + can only be done forwards without encountering problems. + This implies that the user has to ensure that the OS + system time is set to a time earlier or equal to actual + POSIX time before starting the Erlang runtime system. If + you are not completely sure the OS system time is correct, + set it to a time that is guaranteed to be earlier than + actual POSIX time before starting the Erlang runtime + system just to be safe.

+ + Finalize Correct OS System Time +

The OS system time needs to be correct when the + the user finalizes the time offset.

+
+ +

If these requirements are not fulfilled, the system + may behave very bad. +

+ +

Assuming that the requirements above are fulfilled, + time correction is enabled, and that the OS system time + is adjusted using some time adjustment protocol like NTP + or similar, only small adjustments of the Erlang monotonic + time should be needed in order to keep system times + aligned after finilization. As long as the system is not + suspended, the largest adjustments needed should be for + inserted (or deleted) leap seconds.

+ +

In order to be able to use this mode you have + to ensure that all Erlang code that will execute in + both phases are + time warp + safe.

+

Code that only execute in the final phase does not have + to be able to cope with the time warp.

+ +
+ + +
+ Multi Time Warp Mode + +

Multi time warp mode in combination with time + correction is the preferred configuration. This since, + on almost all platforms, the Erlang runtime system will have + better performance, will scale better, will behave better, + and since the accuracy, and precision of time measurements + will be better. Only Erlang runtime systems executing on + ancient platforms will benefit from another configuration.

+ +

The time offset may change at any time without limitations. + That is, Erlang system time may perform time warps both + forwards and backwards at any time. Since we align + the Erlang system time with the OS system time by changing + the time offset, we can enable a time correction that tries + to adjust the frequency of the Erlang monotonic clock to be as + correct as possible. This will make time measurements using + the Erlang monotonic time more accurate and precise.

+ +

If time correction is disabled, Erlang monotonic time + will leap forward if OS system time leaps forward. If the + OS system time leaps backwards, Erlang monotonic time will + stop briefly but it does not freeze for extended periods + of time. This since the time offset is changed in order to + align Erlang system time with OS system time.

+ +

In order to be able to use this mode you have + to ensure that all Erlang code that will execute on the + runtime system is + time warp + safe.

+
+ +
- Should I use erlang:now/0 or os:timestamp/0 -

The simple answer is to use erlang:now/0 for everything where - you want to keep real time characteristics, but use os:timestamp - for things like logs, user communication and debugging (typically - timer:ts uses os:timestamp, as it is a test tool, not a real world - application API). The benefit of using os:timestamp/0 is that it's - faster and does not involve any global state (unless the operating - system has one). The downside is that it will be vulnerable to wall - clock time changes.

+ The New Time API + +

The old time API is based on + erlang:now/0. + The major issue with erlang:now/0 is that it was + intended to be used for so many unrelated things. This + tied these unrelated operations together and unnecessarily + caused performance, scalability as well as accuracy, and + precision issues for operations that do not need to have + such issues. The new API spreads different functionality + over multiple functions in order to improve on this.

+ +

In order to be backwards compatible erlang:now/0 will + remain as is, but you are strongly discouraged from using + it. A lot of uses of erlang:now/0 will also + prevent you from using the new + multi time warp + mode which is an important part of this + new time functionality improvement.

+ +

Some of the new BIFs on some systems, perhaps surprisingly, + return negative integer values on a newly started run time + system. This is not a bug, but a memory usage optimization.

+ +

The new API consists of a number of new BIFs:

+ +

erlang:convert_time_unit/3

+

erlang:monotonic_time/0

+

erlang:monotonic_time/1

+

erlang:system_time/0

+

erlang:system_time/1

+

erlang:time_offset/0

+

erlang:time_offset/1

+

erlang:timestamp/0

+

erlang:unique_integer/0

+

erlang:unique_integer/1

+

os:system_time/0

+

os:system_time/1

+
+

and a number of extensions of existing BIFs:

+ +

erlang:monitor(time_offset, clock_service)

+

erlang:system_flag(time_offset, finalize)

+

erlang:system_info(os_monotonic_time_source)

+

erlang:system_info(time_offset)

+

erlang:system_info(time_warp_mode)

+

erlang:system_info(time_correction)

+

erlang:system_info(start_time)

+
+ + +
+ The New Erlang Monotonic Time +

The Erlang monotonic time as such is new as of ERTS + version 7.0. It has been introduced in order to be able + to detach time measurements such as elapsed time from + calender time. It is very common that one is interested + in measuring elapsed time or specifying a time relative + to another point in time without having any need to know + what the involved times are in UTC or any other + globally defined time scale. By introducing a time scale + that has a local definition of where it starts, it is + possible to manage time that do not concern calender + time on that time scale. Erlang monotonic time use + such a time scale with a locally defined start.

+ +

The introduction of Erlang monotonic time gives us + the possibility to adjust the two Erlang times (Erlang + monotonic time and Erlang system time) separately. By + doing this, accuracy of elapsed time does not have to + suffer just because the system time happened to be + wrong at some point in time. Separate adjustments + of the two times are only performed in the time warp + modes, and only fully separated in the + multi + time warp mode. All other modes than the + multi time warp mode are there for backwards + compatibility reasons, and when using these the + accuracy of Erlang monotonic time suffer since + the adjustments of Erlang monotonic time in these + modes are more or less tied to the Erlang system + time.

+ +

The adjustment of system time could have been made + smother than using a time warp approach, but we think + that would be a bad choice. Since we are able to + express and measure time that aren't connected to + calender time by the use of Erlang monotonic time, it + is better to expose the change in Erlang system time + immediately. This since it makes it possible for the + Erlang applications executing on the system to react + on the change in system time as soon as possible. This + is also more or less exactly how most OSes handle this + (OS monotonic time and OS system time). By adjusting + system time smoothly we would just hide the fact that + system time changed and make it harder for the Erlang + applications to react to the change in a sensible way.

+ +

In order to be able to react to a change in Erlang + system time you have to be able to detect that it + happened. The change in Erlang system time occurs when + current time offset is changed. We have therefore + introduced the possibility to monitor the time offset + using + erlang:monitor(time_offset, clock_service). A process monitoring the time + offset will be sent a message on the following format + when the time offset is changed:

+ {'CHANGE', MonitorReference, time_offset, clock_service, NewTimeOffset} +
+ + +
+ Unique Values +

Besides reporting time erlang:now/0 also + produce unique and strictly monotonically increasing + values. In order to detach this functionality from + time measurements we have introduced + erlang:unique_integer(). +

+
+ + +
+ Dos and Don'ts +

Previously erlang:now/0 was the only option for doing + quite a lot of things. We will look at a few different things + erlang:now/0 could be used for, and how you want to do + this using the new API:

+ + +
+ Retrieve Erlang System Time + +

+ use erlang:now/0 in order to retrieve current Erlang + system time. +

+
+ +

+ use + erlang:system_time/1 + in order to retrieve current Erlang system time on the + time unit + of your choice.

+

If you want the same format as returned by erlang:now/0, use + erlang:timestamp/0. +

+
+
+ + +
+ Measure Elapsed Time + +

+ take timestamps with erlang:now/0 and calculate + the difference in time with + timer:now_diff/2. +

+
+ +

+ take timestamps with + erlang:monotonic_time/0 + and calculate the time difference using ordinary subtraction. + The result will be in native + time unit. + If you want to convert the + result to another time unit you can do this using + erlang:convert_time_unit/3. +

+

Another easier way of doing this is to use + erlang:monotonic_time/1 + with desired time unit. However, you may lose accuracy, + and precision this way. +

+
+
+ + +
+ Determine Order of Events + +

+ determine the order of events by saving a timestamp + with erlang:now/0 when the event happens. +

+
+ +

+ determine the order of events by saving the integer + returned by + erlang:unique_integer([monotonic]) + when the event happens. These integers will be strictly + monotonically ordered on current runtime system instance + corresponding to creation time. +

+
+
+ + +
+ Determine Order of Events With Time of the Event + +

+ determine the order of events by saving a timestamp + with erlang:now/0 when the event happens. +

+
+ +

+ determine the order of events by saving a tuple + containing + monotonic time + and a strictly + monotonically increasing integer like this:

+ +Time = erlang:monotonic_time(), +UMI = erlang:unique_integer([monotonic]), +EventTag = {Time, UMI} +

These tuples will be strictly monotonically ordered + on the current runtime system instance according to + creation time. Note that it is important that the + monotonic time is in the first element (the most + significant element when comparing 2-tuples). Using + the monotonic time in the tuples, you can calculate time + between events.

+

If you are interested in the Erlang system time at the + time when the event occurred you can also save the time + offset before or after saving the events using + erlang:time_offset/0. + Erlang monotonic time added with the time + offset corresponds to Erlang system time.

+

If you are executing in a mode where time offset + may change and you want to be able to get the actual + Erlang system time when the event occurred you can + save the time offset as a third element in the tuple + (the least significant element when comparing 3-tuples).

+
+
+ + +
+ Create a Unique Name + +

+ use the values returned from erlang:now/0 + in order to create a name unique on the current + runtime system instance. +

+
+ +

+ use the value returned from + erlang:unique_integer/0 + in order to create a name unique on the current runtime system + instance. If you only want positive integers, you can use + erlang:unique_integer([positive]). +

+
+
+ + +
+ Seed Random Number Generation With a Unique Value + +

+ seed random number generation using erlang:now(). +

+
+ +

+ seed random number generation using a combination of + erlang:monotonic_time(), + erlang:time_offset(), + erlang:unique_integer(), and other functionality. +

+
+
+ +

To sum this section up: Don't use erlang:now/0!

+
+ +
- Turning off time correction -

If, for some reason, time correction causes trouble and you are - absolutely confident that the wall clock on the system is nearly - perfect, you can turn off time correction completely by giving the - +c option to erl. The probability for this being a - good idea, is very low.

+ Supporting Both New and Old OTP Releases +

Your code may be required to be able to run on a variety + of OTP installations of different OTP releases. If so, you + can not just use the new API out of the box, since it will + not be available on old pre OTP 18 releases. The solution + is not to avoid using the new API, since your + code then won't be able to benefit from the scalability + and accuracy improvements made. Instead you want to use the + new API when available, and fall back on erlang:now/0 + when it is not available. Fortunately almost all of the new + API can easily be implemented using existing primitives + (except for + erlang:system_info(start_time), and + erlang:system_info(os_monotonic_time_source)). + By wrapping the API with functions that fall back on + erlang:now/0 when the new API is not available, + and using these wrappers instead of using the API directly + the problem is solved. These wrappers can for example + be implemented as in + $ERL_TOP/erts/example/time_compat.erl.

- diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 7145824f91..560cab396d 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -764,7 +764,8 @@ RUN_OBJS = \ $(OBJDIR)/erl_bif_ddll.o $(OBJDIR)/erl_bif_guard.o \ $(OBJDIR)/erl_bif_info.o $(OBJDIR)/erl_bif_op.o \ $(OBJDIR)/erl_bif_os.o $(OBJDIR)/erl_bif_lists.o \ - $(OBJDIR)/erl_bif_trace.o $(OBJDIR)/erl_bif_wrap.o \ + $(OBJDIR)/erl_bif_trace.o $(OBJDIR)/erl_bif_unique.o \ + $(OBJDIR)/erl_bif_wrap.o \ $(OBJDIR)/erl_trace.o $(OBJDIR)/copy.o \ $(OBJDIR)/utils.o $(OBJDIR)/bif.o \ $(OBJDIR)/io.o $(OBJDIR)/erl_printf_term.o\ diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 5d06a32941..ced35be265 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -144,9 +144,11 @@ atom catchlevel atom cd atom cdr atom cflags +atom CHANGE='CHANGE' atom characters_to_binary_int atom characters_to_list_int atom clear +atom clock_service atom close atom closed atom code @@ -156,6 +158,7 @@ atom compat_rel atom compile atom compressed atom config_h +atom convert_time_unit atom connect atom connected atom connection_closed @@ -236,7 +239,7 @@ atom first atom firstline atom flags atom flush -atom flush_monitor_message +atom flush_monitor_messages atom force atom format_cpu_topology atom free @@ -344,6 +347,8 @@ atom message_queue_len atom messages atom meta atom meta_match_spec +atom micro_seconds +atom milli_seconds atom min_heap_size atom min_bin_vheap_size atom minor_version @@ -354,12 +359,15 @@ atom monitored_by atom monitor atom monitor_nodes atom monitors +atom monotonic atom more atom multi_scheduling atom multiline +atom nano_seconds atom name atom named_table atom namelist +atom native atom native_addresses atom Neq='=/=' atom Neqeq='/=' @@ -450,6 +458,7 @@ atom ports atom port_count atom port_limit atom port_op +atom positive atom print atom priority atom private @@ -509,6 +518,7 @@ atom schedulers_online atom scheme atom scientific atom scope +atom seconds atom sensitive atom sequential_tracer atom sequential_trace_token @@ -554,6 +564,7 @@ atom term_to_binary_trap atom this atom thread_pool_size atom threads +atom time_offset atom timeout atom timeout_value atom Times='*' diff --git a/erts/emulator/beam/benchmark.h b/erts/emulator/beam/benchmark.h index 766edaac42..7f267b7201 100644 --- a/erts/emulator/beam/benchmark.h +++ b/erts/emulator/beam/benchmark.h @@ -175,10 +175,10 @@ extern BM_TIMER_T start_time; #else /* !USE_PERFCTR (Assuming Solaris) */ -#define BM_TIMER_T hrtime_t -#define BM_START_TIMER(t) system_clock = sys_gethrtime() +#define BM_TIMER_T ErtsMonotonicTime +#define BM_START_TIMER(t) system_clock = ERTS_MONOTONIC_TO_NSEC(erts_os_monotonic_time()) #define BM_STOP_TIMER(t) do { \ - BM_TIMER_T tmp = (sys_gethrtime() - system_clock) - timer_time; \ + BM_TIMER_T tmp = (ERTS_MONOTONIC_TO_NSEC(erts_os_monotonic_time()) - system_clock) - timer_time; \ t##_time += (tmp > 0 ? tmp : 0); \ } while(0) diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 42dd160e38..ec5122292e 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -40,16 +40,21 @@ #define ERTS_PTAB_WANT_BIF_IMPL__ #include "erl_ptab.h" #include "erl_bits.h" +#include "erl_bif_unique.h" -static Export* flush_monitor_message_trap = NULL; +static Export* flush_monitor_messages_trap = NULL; static Export* set_cpu_topology_trap = NULL; static Export* await_proc_exit_trap = NULL; static Export* await_port_send_result_trap = NULL; Export* erts_format_cpu_topology_trap = NULL; +Export *erts_convert_time_unit_trap = NULL; static Export *await_sched_wall_time_mod_trap; static erts_smp_atomic32_t sched_wall_time; +static erts_smp_mtx_t ports_snapshot_mtx; +erts_smp_atomic_t erts_dead_ports_ptr; /* To store dying ports during snapshot */ + #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) /* @@ -391,7 +396,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) return res; } -static int demonitor(Process *c_p, Eterm ref) +static int demonitor(Process *c_p, Eterm ref, Eterm *multip) { ErtsMonitor *mon = NULL; /* The monitor entry to delete */ Process *rp; /* Local target process */ @@ -415,65 +420,73 @@ static int demonitor(Process *c_p, Eterm ref) goto done; } - if (mon->type != MON_ORIGIN) { - res = ERTS_DEMONITOR_BADARG; - goto done; - } - to = mon->pid; - - if (is_atom(to)) { - /* Monitoring a name at node to */ - ASSERT(is_node_name_atom(to)); - dep = erts_sysname_to_connected_dist_entry(to); - ASSERT(dep != erts_this_dist_entry); - if (dep) - deref_de = 1; - } else { - ASSERT(is_pid(to)); - dep = pid_dist_entry(to); - } - if (dep != erts_this_dist_entry) { - res = remote_demonitor(c_p, dep, ref, to); - /* remote_demonitor() unlocks link lock on c_p */ - unlock_link = 0; - } - else { /* Local monitor */ - if (deref_de) { - deref_de = 0; - erts_deref_dist_entry(dep); + switch (mon->type) { + case MON_TIME_OFFSET: + *multip = am_true; + erts_demonitor_time_offset(ref); + res = ERTS_DEMONITOR_TRUE; + break; + case MON_ORIGIN: + to = mon->pid; + *multip = am_false; + if (is_atom(to)) { + /* Monitoring a name at node to */ + ASSERT(is_node_name_atom(to)); + dep = erts_sysname_to_connected_dist_entry(to); + ASSERT(dep != erts_this_dist_entry); + if (dep) + deref_de = 1; + } else { + ASSERT(is_pid(to)); + dep = pid_dist_entry(to); } - dep = NULL; - rp = erts_pid2proc_opt(c_p, - ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, - to, - ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); + if (dep != erts_this_dist_entry) { + res = remote_demonitor(c_p, dep, ref, to); + /* remote_demonitor() unlocks link lock on c_p */ + unlock_link = 0; + } + else { /* Local monitor */ + if (deref_de) { + deref_de = 0; + erts_deref_dist_entry(dep); + } + dep = NULL; + rp = erts_pid2proc_opt(c_p, + ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, + to, + ERTS_PROC_LOCK_LINK, + ERTS_P2P_FLG_ALLOW_OTHER_X); + mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); #ifndef ERTS_SMP - ASSERT(mon); + ASSERT(mon); #else - if (!mon) - res = ERTS_DEMONITOR_FALSE; - else + if (!mon) + res = ERTS_DEMONITOR_FALSE; + else #endif - { - res = ERTS_DEMONITOR_TRUE; - erts_destroy_monitor(mon); - } - if (rp) { - ErtsMonitor *rmon; - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); - if (rp != c_p) - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (rmon != NULL) - erts_destroy_monitor(rmon); - } - else { - ERTS_SMP_ASSERT_IS_NOT_EXITING(c_p); - } + { + res = ERTS_DEMONITOR_TRUE; + erts_destroy_monitor(mon); + } + if (rp) { + ErtsMonitor *rmon; + rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); + if (rp != c_p) + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + if (rmon != NULL) + erts_destroy_monitor(rmon); + } + else { + ERTS_SMP_ASSERT_IS_NOT_EXITING(c_p); + } + } + break; + default: + res = ERTS_DEMONITOR_BADARG; + *multip = am_false; + break; } - done: if (unlock_link) @@ -490,7 +503,8 @@ static int demonitor(Process *c_p, Eterm ref) BIF_RETTYPE demonitor_1(BIF_ALIST_1) { - switch (demonitor(BIF_P, BIF_ARG_1)) { + Eterm multi; + switch (demonitor(BIF_P, BIF_ARG_1, &multi)) { case ERTS_DEMONITOR_FALSE: case ERTS_DEMONITOR_TRUE: BIF_RET(am_true); @@ -508,6 +522,7 @@ BIF_RETTYPE demonitor_1(BIF_ALIST_1) BIF_RETTYPE demonitor_2(BIF_ALIST_2) { Eterm res = am_true; + Eterm multi = am_false; int info = 0; int flush = 0; Eterm list = BIF_ARG_2; @@ -530,13 +545,18 @@ BIF_RETTYPE demonitor_2(BIF_ALIST_2) if (is_not_nil(list)) goto badarg; - switch (demonitor(BIF_P, BIF_ARG_1)) { + switch (demonitor(BIF_P, BIF_ARG_1, &multi)) { case ERTS_DEMONITOR_FALSE: if (info) res = am_false; - if (flush) - BIF_TRAP2(flush_monitor_message_trap, BIF_P, BIF_ARG_1, res); + if (flush) { + flush_messages: + BIF_TRAP3(flush_monitor_messages_trap, BIF_P, + BIF_ARG_1, multi, res); + } case ERTS_DEMONITOR_TRUE: + if (multi == am_true && flush) + goto flush_messages; BIF_RET(res); case ERTS_DEMONITOR_YIELD_TRUE: ERTS_BIF_YIELD_RETURN(BIF_P, am_true); @@ -744,7 +764,22 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2) int deref_de = 0; /* Only process monitors are implemented */ - if (BIF_ARG_1 != am_process) { + switch (BIF_ARG_1) { + case am_time_offset: { + Eterm ref; + if (BIF_ARG_2 != am_clock_service) + goto error; + ref = erts_make_ref(BIF_P); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); + erts_add_monitor(&ERTS_P_MONITORS(BIF_P), MON_TIME_OFFSET, + ref, am_clock_service, NIL); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); + erts_monitor_time_offset(BIF_P->common.id, ref); + BIF_RET(ref); + } + case am_process: + break; + default: goto error; } @@ -3446,91 +3481,6 @@ BIF_RETTYPE self_0(BIF_ALIST_0) /**********************************************************************/ -/* - New representation of refs in R9, see erl_term.h - - In the first data word, only the usual 18 bits are used. Ordinarily, - in "long refs" all words are used (in other words, practically never - wrap around), but for compatibility with older nodes, "short refs" - exist. Short refs come into being by being converted from the old - external format for refs (tag REFERENCE_EXT). Short refs are - converted back to the old external format. - - When converting a long ref to the external format in the case of - preparing for sending to an older node, the ref is truncated by only - using the first word (with 18 significant bits), and using the old tag - REFERENCE_EXT. - - When comparing refs or different size, only the parts up to the length - of the shorter operand are used. This has the desirable effect that a - long ref sent to an old node and back will be treated as equal to - the original, although some of the bits have been lost. - - The hash value for a ref always considers only the first word, since - in the above scenario, the original and the copy should have the same - hash value. -*/ - -static Uint32 reference0; /* Initialized in erts_init_bif */ -static Uint32 reference1; -static Uint32 reference2; -static erts_smp_spinlock_t make_ref_lock; -static erts_smp_mtx_t ports_snapshot_mtx; -erts_smp_atomic_t erts_dead_ports_ptr; /* To store dying ports during snapshot */ - -void -erts_make_ref_in_array(Uint32 ref[ERTS_MAX_REF_NUMBERS]) -{ - erts_smp_spin_lock(&make_ref_lock); - - reference0++; - if (reference0 >= MAX_REFERENCE) { - reference0 = 0; - reference1++; - if (reference1 == 0) { - reference2++; - } - } - - ref[0] = reference0; - ref[1] = reference1; - ref[2] = reference2; - - erts_smp_spin_unlock(&make_ref_lock); -} - -Eterm erts_make_ref_in_buffer(Eterm buffer[REF_THING_SIZE]) -{ - Eterm* hp = buffer; - Uint32 ref[ERTS_MAX_REF_NUMBERS]; - - erts_make_ref_in_array(ref); - write_ref_thing(hp, ref[0], ref[1], ref[2]); - return make_internal_ref(hp); -} - -Eterm erts_make_ref(Process *p) -{ - Eterm* hp; - Uint32 ref[ERTS_MAX_REF_NUMBERS]; - - ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p)); - - hp = HAlloc(p, REF_THING_SIZE); - - erts_make_ref_in_array(ref); - write_ref_thing(hp, ref[0], ref[1], ref[2]); - - return make_internal_ref(hp); -} - -BIF_RETTYPE make_ref_0(BIF_ALIST_0) -{ - return erts_make_ref(BIF_P); -} - -/**********************************************************************/ - /* return the time of day */ BIF_RETTYPE time_0(BIF_ALIST_0) @@ -4508,6 +4458,28 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) break; } #endif + } else if (BIF_ARG_1 == am_time_offset + && ERTS_IS_ATOM_STR("finalize", BIF_ARG_2)) { + ErtsTimeOffsetState res; + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + res = erts_finalize_time_offset(); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + switch (res) { + case ERTS_TIME_OFFSET_PRELIMINARY: { + DECL_AM(preliminary); + BIF_RET(AM_preliminary); + } + case ERTS_TIME_OFFSET_FINAL: { + DECL_AM(final); + BIF_RET(AM_final); + } + case ERTS_TIME_OFFSET_VOLATILE: { + DECL_AM(volatile); + BIF_RET(AM_volatile); + } + default: + ERTS_INTERNAL_ERROR("Unknown state"); + } } else if (ERTS_IS_ATOM_STR("scheduling_statistics", BIF_ARG_1)) { int what; if (ERTS_IS_ATOM_STR("disable", BIF_ARG_2)) @@ -4795,11 +4767,6 @@ void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a, void erts_init_bif(void) { - reference0 = 0; - reference1 = 0; - reference2 = 0; - - erts_smp_spinlock_init(&make_ref_lock, "make_ref"); erts_smp_mtx_init(&ports_snapshot_mtx, "ports_snapshot"); erts_smp_atomic_init_nob(&erts_dead_ports_ptr, (erts_aint_t) NULL); @@ -4816,9 +4783,13 @@ void erts_init_bif(void) #endif , &bif_return_trap); - flush_monitor_message_trap = erts_export_put(am_erlang, - am_flush_monitor_message, - 2); + flush_monitor_messages_trap = erts_export_put(am_erts_internal, + am_flush_monitor_messages, + 3); + + erts_convert_time_unit_trap = erts_export_put(am_erlang, + am_convert_time_unit, + 3); set_cpu_topology_trap = erts_export_put(am_erlang, am_set_cpu_topology, diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index 72c55ccb55..bd0f8cda2b 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -21,6 +21,7 @@ #define __BIF_H__ extern Export* erts_format_cpu_topology_trap; +extern Export *erts_convert_time_unit_trap; #define BIF_RETTYPE Eterm diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index e68b8e6274..db8feb681b 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -92,6 +92,8 @@ bif erlang:loaded/0 bif erlang:localtime/0 bif erlang:localtime_to_universaltime/2 bif erlang:make_ref/0 +bif erlang:unique_integer/0 +bif erlang:unique_integer/1 bif erlang:md5/1 bif erlang:md5_init/0 bif erlang:md5_update/2 @@ -104,6 +106,13 @@ ubif erlang:node/1 ubif erlang:node/0 bif erlang:nodes/1 bif erlang:now/0 +bif erlang:monotonic_time/0 +bif erlang:monotonic_time/1 +bif erlang:system_time/0 +bif erlang:system_time/1 +bif erlang:time_offset/0 +bif erlang:time_offset/1 +bif erlang:timestamp/0 bif erlang:open_port/2 @@ -158,6 +167,15 @@ bif erts_internal:check_process_code/2 bif erts_internal:map_to_tuple_keys/1 +bif erts_internal:time_unit/0 + +bif erts_internal:get_bif_timer_servers/0 +bif erts_internal:create_bif_timer/0 +bif erts_internal:access_bif_timer/1 + +bif erts_internal:monitor_process/2 +bif erts_internal:is_system_process/1 + # inet_db support bif erlang:port_set_data/2 bif erlang:port_get_data/1 @@ -347,6 +365,8 @@ bif os:getenv/0 bif os:getenv/1 bif os:getpid/0 bif os:timestamp/0 +bif os:system_time/0 +bif os:system_time/1 # # Bifs in the erl_ddll module (the module actually does not exist) diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index de7d370938..30e6a2c522 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1577,6 +1577,46 @@ Eterm erts_sint64_to_big(Sint64 x, Eterm **hpp) return make_big(hp); } +Eterm +erts_uint64_array_to_big(Uint **hpp, int neg, int len, Uint64 *array) +{ + Uint *headerp; + int i, pot_digits, digits; + + headerp = *hpp; + + pot_digits = digits = 0; + for (i = 0; i < len; i++) { +#if defined(ARCH_32) || HALFWORD_HEAP + Uint low_val = array[i] & ((Uint) 0xffffffff); + Uint high_val = (array[i] >> 32) & ((Uint) 0xffffffff); + BIG_DIGIT(headerp, pot_digits) = low_val; + pot_digits++; + if (low_val) + digits = pot_digits; + BIG_DIGIT(headerp, pot_digits) = high_val; + pot_digits++; + if (high_val) + digits = pot_digits; +#else + Uint val = array[i]; + BIG_DIGIT(headerp, pot_digits) = val; + pot_digits++; + if (val) + digits = pot_digits; +#endif + } + + if (neg) + *headerp = make_neg_bignum_header(digits); + else + *headerp = make_pos_bignum_header(digits); + + *hpp = headerp + 1 + digits; + + return make_big(headerp); +} + /* ** Convert a bignum to a double float */ diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index da31876d75..4e4611de16 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -104,6 +104,9 @@ typedef Uint dsize_t; /* Vector size type */ : ERTS_UINT64_BIG_HEAP_SIZE__((X) >= 0 ? (X) : -(Uint64)(X))) #define ERTS_UINT64_HEAP_SIZE(X) \ (IS_USMALL(0, (X)) ? 0 : ERTS_UINT64_BIG_HEAP_SIZE__((X))) +#define ERTS_MAX_SINT64_HEAP_SIZE (1 + 2) +#define ERTS_MAX_UINT64_HEAP_SIZE (1 + 2) +#define ERTS_UINT64_ARRAY_TO_BIG_MAX_HEAP_SZ(LEN) (2*(LEN)+1) #else @@ -111,6 +114,9 @@ typedef Uint dsize_t; /* Vector size type */ (IS_SSMALL((X)) ? 0 : (1 + 1)) #define ERTS_UINT64_HEAP_SIZE(X) \ (IS_USMALL(0, (X)) ? 0 : (1 + 1)) +#define ERTS_MAX_SINT64_HEAP_SIZE (1 + 1) +#define ERTS_MAX_UINT64_HEAP_SIZE (1 + 1) +#define ERTS_UINT64_ARRAY_TO_BIG_MAX_HEAP_SZ(LEN) ((LEN)+1) #endif @@ -156,6 +162,7 @@ int term_to_Uint(Eterm, Uint*); int term_to_UWord(Eterm, UWord*); int term_to_Sint(Eterm, Sint*); #if HAVE_INT64 +Eterm erts_uint64_array_to_big(Uint **, int, int, Uint64 *); int term_to_Uint64(Eterm, Uint64*); int term_to_Sint64(Eterm, Sint64*); #endif diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 21434eb117..4cd4ad100c 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -269,6 +269,7 @@ type BUSY_CALLER_TAB SHORT_LIVED SYSTEM busy_caller_table type BUSY_CALLER SHORT_LIVED SYSTEM busy_caller type PROC_SYS_TSK SHORT_LIVED PROCESSES proc_sys_task type PROC_SYS_TSK_QS SHORT_LIVED PROCESSES proc_sys_task_queues +type NEW_TIME_OFFSET SHORT_LIVED SYSTEM new_time_offset +if threads_no_smp # Need thread safe allocs, but std_alloc and fix_alloc are not; diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 3bf78adce7..68004a7725 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -36,6 +36,7 @@ #include "big.h" #include "erl_binary.h" #include "erl_bits.h" +#include "erl_bif_unique.h" /* diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index 56cd2ba04f..fc4f819f56 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -45,6 +45,7 @@ #include "big.h" #include "dist.h" #include "erl_version.h" +#include "erl_bif_unique.h" #include "dtrace-wrapper.h" #ifdef ERTS_SMP diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b90362d82c..80d49c7ce2 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -42,6 +42,7 @@ #include "erl_cpu_topology.h" #include "erl_async.h" #include "erl_thr_progress.h" +#include "erl_bif_unique.h" #define ERTS_PTAB_WANT_DEBUG_FUNCS__ #include "erl_ptab.h" #ifdef HIPE @@ -2099,6 +2100,46 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(am_opt); #endif BIF_RET(res); + } else if (BIF_ARG_1 == am_time_offset) { + switch (erts_time_offset_state()) { + case ERTS_TIME_OFFSET_PRELIMINARY: { + ERTS_DECL_AM(preliminary); + BIF_RET(AM_preliminary); + } + case ERTS_TIME_OFFSET_FINAL: { + ERTS_DECL_AM(final); + BIF_RET(AM_final); + } + case ERTS_TIME_OFFSET_VOLATILE: { + ERTS_DECL_AM(volatile); + BIF_RET(AM_volatile); + } + default: + ERTS_INTERNAL_ERROR("Invalid time offset state"); + } + } else if (ERTS_IS_ATOM_STR("os_monotonic_time_source", BIF_ARG_1)) { + BIF_RET(erts_monotonic_time_source(BIF_P)); + } else if (ERTS_IS_ATOM_STR("time_correction", BIF_ARG_1)) { + BIF_RET(erts_has_time_correction() ? am_true : am_false); + } else if (ERTS_IS_ATOM_STR("start_time", BIF_ARG_1)) { + BIF_RET(erts_get_monotonic_start_time(BIF_P)); + } else if (ERTS_IS_ATOM_STR("time_warp_mode", BIF_ARG_1)) { + switch (erts_time_warp_mode()) { + case ERTS_NO_TIME_WARP_MODE: { + ERTS_DECL_AM(no_time_warp); + BIF_RET(AM_no_time_warp); + } + case ERTS_SINGLE_TIME_WARP_MODE: { + ERTS_DECL_AM(single_time_warp); + BIF_RET(AM_single_time_warp); + } + case ERTS_MULTI_TIME_WARP_MODE: { + ERTS_DECL_AM(multi_time_warp); + BIF_RET(AM_multi_time_warp); + } + default: + ERTS_INTERNAL_ERROR("Invalid time warp mode"); + } } else if (BIF_ARG_1 == am_allocated_areas) { res = erts_allocated_areas(NULL, NULL, BIF_P); BIF_RET(res); @@ -2700,9 +2741,11 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(make_small(erts_db_get_max_tabs())); } else if (ERTS_IS_ATOM_STR("tolerant_timeofday",BIF_ARG_1)) { - BIF_RET(erts_disable_tolerant_timeofday - ? am_disabled - : am_enabled); + if (erts_has_time_correction() + && erts_time_offset_state() == ERTS_TIME_OFFSET_FINAL) { + BIF_RET(am_enabled); + } + BIF_RET(am_disabled); } else if (ERTS_IS_ATOM_STR("eager_check_io",BIF_ARG_1)) { BIF_RET(erts_eager_check_io ? am_true : am_false); @@ -3400,6 +3443,29 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) else if (ERTS_IS_ATOM_STR("mmap", BIF_ARG_1)) { BIF_RET(erts_mmap_debug_info(BIF_P)); } + else if (ERTS_IS_ATOM_STR("unique_monotonic_integer_state", BIF_ARG_1)) { + BIF_RET(erts_debug_get_unique_monotonic_integer_state(BIF_P)); + } + else if (ERTS_IS_ATOM_STR("min_unique_monotonic_integer", BIF_ARG_1)) { + Sint64 value = erts_get_min_unique_monotonic_integer(); + if (IS_SSMALL(value)) + BIF_RET(make_small(value)); + else { + Uint hsz = ERTS_SINT64_HEAP_SIZE(value); + Eterm *hp = HAlloc(BIF_P, hsz); + BIF_RET(erts_sint64_to_big(value, &hp)); + } + } + else if (ERTS_IS_ATOM_STR("min_unique_integer", BIF_ARG_1)) { + Sint64 value = erts_get_min_unique_integer(); + if (IS_SSMALL(value)) + BIF_RET(make_small(value)); + else { + Uint hsz = ERTS_SINT64_HEAP_SIZE(value); + Eterm *hp = HAlloc(BIF_P, hsz); + BIF_RET(erts_sint64_to_big(value, &hp)); + } + } } else if (is_tuple(BIF_ARG_1)) { Eterm* tp = tuple_val(BIF_ARG_1); @@ -3596,6 +3662,38 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) } break; } + case 3: { + if (ERTS_IS_ATOM_STR("check_time_config", tp[1])) { + int res, time_correction; + ErtsTimeWarpMode time_warp_mode; + if (tp[2] == am_true) + time_correction = !0; + else if (tp[2] == am_false) + time_correction = 0; + else + break; + if (ERTS_IS_ATOM_STR("no_time_warp", tp[3])) + time_warp_mode = ERTS_NO_TIME_WARP_MODE; + else if (ERTS_IS_ATOM_STR("single_time_warp", tp[3])) + time_warp_mode = ERTS_SINGLE_TIME_WARP_MODE; + else if (ERTS_IS_ATOM_STR("multi_time_warp", tp[3])) + time_warp_mode = ERTS_MULTI_TIME_WARP_MODE; + else + break; + res = erts_check_time_adj_support(time_correction, + time_warp_mode); + BIF_RET(res ? am_true : am_false); + } + else if (ERTS_IS_ATOM_STR("make_unique_integer", tp[1])) { + Eterm res = erts_debug_make_unique_integer(BIF_P, + tp[2], + tp[3]); + if (is_non_value(res)) + break; + BIF_RET(res); + } + break; + } default: break; } @@ -3897,6 +3995,10 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) } } } + else if (ERTS_IS_ATOM_STR("unique_monotonic_integer_state", BIF_ARG_1)) { + int res = erts_debug_set_unique_monotonic_integer_state(BIF_ARG_2); + BIF_RET(res ? am_true : am_false); + } } BIF_ERROR(BIF_P, BADARG); diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c index 03ac97283c..c9b02b48f5 100644 --- a/erts/emulator/beam/erl_bif_timer.c +++ b/erts/emulator/beam/erl_bif_timer.c @@ -27,6 +27,7 @@ #include "error.h" #include "big.h" #include "erl_thr_progress.h" +#include "erl_bif_unique.h" /**************************************************************************** ** BIF Timer support diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 06fbbea123..08796df912 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -38,6 +38,7 @@ #include "beam_bp.h" #include "erl_binary.h" #include "erl_thr_progress.h" +#include "erl_bif_unique.h" #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) diff --git a/erts/emulator/beam/erl_bif_unique.c b/erts/emulator/beam/erl_bif_unique.c new file mode 100644 index 0000000000..57b0bab72f --- /dev/null +++ b/erts/emulator/beam/erl_bif_unique.c @@ -0,0 +1,556 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2014. All Rights Reserved. + * + * The 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 "erl_alloc.h" +#include "export.h" +#include "bif.h" +#include "erl_bif_unique.h" + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Reference * +\* */ + +static union { + erts_atomic64_t count; + char align__[ERTS_CACHE_LINE_SIZE]; +} global_reference erts_align_attribute(ERTS_CACHE_LINE_SIZE); + + +/* + * ref[0] indicate thread creating reference as follows: + * + * - ref[0] == 0 => Non-scheduler thread; + * - else; ref[0] <= erts_no_schedulers => + * ordinary scheduler with id == ref[0]; + * - else; ref[0] <= erts_no_schedulers + * + erts_no_dirty_cpu_schedulers => + * dirty cpu scheduler with id == 'ref[0] - erts_no_schedulers'; + * - else => + * dirty io scheduler with id == 'ref[0] + * - erts_no_schedulers + * - erts_no_dirty_cpu_schedulers' + */ + +#ifdef DEBUG +static Uint32 max_thr_id; +#endif + +static void +init_reference(void) +{ +#ifdef DEBUG + max_thr_id = (Uint32) erts_no_schedulers; +#ifdef ERTS_DIRTY_SCHEDULERS + max_thr_id += (Uint32) erts_no_dirty_cpu_schedulers; + max_thr_id += (Uint32) erts_no_dirty_io_schedulers; +#endif +#endif + erts_atomic64_init_nob(&global_reference.count, 0); +} + +static ERTS_INLINE void +global_make_ref_in_array(Uint32 thr_id, Uint32 ref[ERTS_MAX_REF_NUMBERS]) +{ + Uint64 value; + + value = (Uint64) erts_atomic64_inc_read_mb(&global_reference.count); + + erts_set_ref_numbers(ref, thr_id, value); +} + +static ERTS_INLINE void +make_ref_in_array(Uint32 ref[ERTS_MAX_REF_NUMBERS]) +{ + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + if (esdp) + erts_sched_make_ref_in_array(esdp, ref); + else + global_make_ref_in_array(0, ref); +} + +void +erts_make_ref_in_array(Uint32 ref[ERTS_MAX_REF_NUMBERS]) +{ + make_ref_in_array(ref); +} + +Eterm erts_make_ref_in_buffer(Eterm buffer[REF_THING_SIZE]) +{ + Eterm* hp = buffer; + Uint32 ref[ERTS_MAX_REF_NUMBERS]; + + make_ref_in_array(ref); + write_ref_thing(hp, ref[0], ref[1], ref[2]); + return make_internal_ref(hp); +} + +Eterm erts_make_ref(Process *c_p) +{ + Eterm* hp; + Uint32 ref[ERTS_MAX_REF_NUMBERS]; + + ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(c_p)); + + hp = HAlloc(c_p, REF_THING_SIZE); + + make_ref_in_array(ref); + write_ref_thing(hp, ref[0], ref[1], ref[2]); + + return make_internal_ref(hp); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Unique Integer * +\* */ + +static struct { + union { + struct { + int left_shift; + int right_shift; + Uint64 mask; + Uint64 val0_max; + } o; + char align__[ERTS_CACHE_LINE_SIZE]; + } r; + union { + erts_atomic64_t val1; + char align__[ERTS_CACHE_LINE_SIZE]; + } w; +} unique_data erts_align_attribute(ERTS_CACHE_LINE_SIZE); + +static void +init_unique_integer(void) +{ + int bits; + unique_data.r.o.val0_max = (Uint64) erts_no_schedulers; +#ifdef ERTS_DIRTY_SCHEDULERS + unique_data.r.o.val0_max += (Uint64) erts_no_dirty_cpu_schedulers; + unique_data.r.o.val0_max += (Uint64) erts_no_dirty_io_schedulers; +#endif + bits = erts_fit_in_bits_int64(unique_data.r.o.val0_max); + unique_data.r.o.left_shift = bits; + unique_data.r.o.right_shift = 64 - bits; + unique_data.r.o.mask = (((Uint64) 1) << bits) - 1; + erts_atomic64_init_nob(&unique_data.w.val1, -1); +} + +#define ERTS_MAX_UNIQUE_INT_HEAP_SIZE ERTS_UINT64_ARRAY_TO_BIG_MAX_HEAP_SZ(2) + +static ERTS_INLINE Eterm +bld_unique_integer_term(Eterm **hpp, Uint *szp, + Uint64 val0, Uint64 val1, + int positive) +{ + Uint hsz; + Uint64 unique_val[2]; + + unique_val[0] = ((Uint64) val0); + unique_val[0] |= ((Uint64) val1) << unique_data.r.o.left_shift; + unique_val[1] = ((Uint64) val1) >> unique_data.r.o.right_shift; + unique_val[1] &= unique_data.r.o.mask; + + if (positive) { + unique_val[0]++; + if (unique_val[0] == 0) + unique_val[1]++; + } + else { + ASSERT(MIN_SMALL < 0); + if (unique_val[1] == 0 + && unique_val[0] < ((Uint64) -1*((Sint64) MIN_SMALL))) { + Sint64 s_unique_val = (Sint64) unique_val[0]; + s_unique_val += MIN_SMALL; + ASSERT(MIN_SMALL <= s_unique_val && s_unique_val < 0); + if (szp) + *szp = 0; + if (!hpp) + return THE_NON_VALUE; + return make_small((Sint) s_unique_val); + } + if (unique_val[0] < ((Uint64) -1*((Sint64) MIN_SMALL))) { + ASSERT(unique_val[1] != 0); + unique_val[1] -= 1; + } + unique_val[0] += MIN_SMALL; + } + + if (!unique_val[1]) { + if (unique_val[0] <= MAX_SMALL) { + if (szp) + *szp = 0; + if (!hpp) + return THE_NON_VALUE; + return make_small((Uint) unique_val[0]); + } + + if (szp) + *szp = ERTS_UINT64_HEAP_SIZE(unique_val[0]); + if (!hpp) + return THE_NON_VALUE; + return erts_uint64_to_big(unique_val[0], hpp); + } + else { + Eterm tmp, *tmp_hp, res; + DeclareTmpHeapNoproc(local_heap, 2*ERTS_MAX_UNIQUE_INT_HEAP_SIZE); + + UseTmpHeapNoproc(2*ERTS_MAX_UNIQUE_INT_HEAP_SIZE); + + tmp_hp = local_heap; + + tmp = erts_uint64_array_to_big(&tmp_hp, 0, 2, unique_val); + ASSERT(is_big(tmp)); + + hsz = big_arity(tmp) + 1; + + ASSERT(hsz <= ERTS_MAX_UNIQUE_INT_HEAP_SIZE); + + if (szp) + *szp = hsz; + + if (!hpp) + res = THE_NON_VALUE; + else { + int hix; + Eterm *hp = *hpp; + tmp_hp = big_val(tmp); + for (hix = 0; hix < hsz; hix++) + hp[hix] = tmp_hp[hix]; + + *hpp = hp + hsz; + res = make_big(hp); + } + + UnUseTmpHeapNoproc(2*ERTS_MAX_UNIQUE_INT_HEAP_SIZE); + + return res; + } +} + +static ERTS_INLINE Eterm unique_integer_bif(Process *c_p, int positive) +{ + ErtsSchedulerData *esdp; + Uint64 thr_id, unique; + Uint hsz; + Eterm *hp; + + esdp = ERTS_PROC_GET_SCHDATA(c_p); + thr_id = (Uint64) esdp->thr_id; + unique = esdp->unique++; + bld_unique_integer_term(NULL, &hsz, thr_id, unique, positive); + hp = hsz ? HAlloc(c_p, hsz) : NULL; + return bld_unique_integer_term(&hp, NULL, thr_id, unique, positive); +} + +Uint +erts_raw_unique_integer_heap_size(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]) +{ + Uint sz; + bld_unique_integer_term(NULL, &sz, val[0], val[1], 0); + return sz; +} + +Eterm +erts_raw_make_unique_integer(Eterm **hpp, Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]) +{ + return bld_unique_integer_term(hpp, NULL, val[0], val[1], 0); +} + +void +erts_raw_get_unique_integer(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]) +{ + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + if (esdp) { + val[0] = (Uint64) esdp->thr_id; + val[1] = esdp->unique++; + } + else { + val[0] = (Uint64) 0; + val[1] = (Uint64) erts_atomic64_inc_read_nob(&unique_data.w.val1); + } +} + + +Sint64 +erts_get_min_unique_integer(void) +{ + return (Sint64) MIN_SMALL; +} + +/* --- Debug --- */ + +Eterm +erts_debug_make_unique_integer(Process *c_p, Eterm etval0, Eterm etval1) +{ + Uint64 val0, val1; + Uint hsz; + Eterm res, *hp, *end_hp; + + if (!term_to_Uint64(etval0, &val0)) + return THE_NON_VALUE; + + if (!term_to_Uint64(etval1, &val1)) + return THE_NON_VALUE; + + bld_unique_integer_term(NULL, &hsz, val0, val1, 0); + + hp = HAlloc(c_p, hsz); + end_hp = hp + hsz; + + res = bld_unique_integer_term(&hp, NULL, val0, val1, 0); + if (hp != end_hp) + ERTS_INTERNAL_ERROR("Heap allocation error"); + + return res; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Strict Monotonic Counter * +\* */ + +static struct { + union { + erts_atomic64_t value; + char align__[ERTS_CACHE_LINE_SIZE]; + } w; +} raw_unique_monotonic_integer erts_align_attribute(ERTS_CACHE_LINE_SIZE); + +#if defined(ARCH_32) || HALFWORD_HEAP +# define ERTS_UNIQUE_MONOTONIC_OFFSET ERTS_SINT64_MIN +#else +# define ERTS_UNIQUE_MONOTONIC_OFFSET MIN_SMALL +#endif + +static void +init_unique_monotonic_integer(void) +{ + erts_atomic64_init_nob(&raw_unique_monotonic_integer.w.value, + (erts_aint64_t) -1); +} + +static ERTS_INLINE Uint64 +get_raw_unique_monotonic_integer(void) +{ + return (Uint64) erts_atomic64_inc_read_mb(&raw_unique_monotonic_integer.w.value); +} + +static ERTS_INLINE Uint +get_unique_monotonic_integer_heap_size(Uint64 raw, int positive) +{ + if (positive) { + Uint64 value = raw+1; + return ERTS_UINT64_HEAP_SIZE(value); + } + else { + Sint64 value = ((Sint64) raw) + ERTS_UNIQUE_MONOTONIC_OFFSET; + if (IS_SSMALL(value)) + return 0; +#if defined(ARCH_32) || HALFWORD_HEAP + return ERTS_SINT64_HEAP_SIZE(value); +#else + return ERTS_UINT64_HEAP_SIZE((Uint64) value); +#endif + } +} + +static ERTS_INLINE Eterm +make_unique_monotonic_integer_value(Eterm *hp, Uint hsz, Uint64 raw, int positive) +{ + Eterm res; +#ifdef DEBUG + Eterm *end_hp = hp + hsz; +#endif + + if (positive) { + Uint64 value = raw+1; + res = hsz ? erts_uint64_to_big(value, &hp) : make_small(value); + } + else { + Sint64 value = ((Sint64) raw) + ERTS_UNIQUE_MONOTONIC_OFFSET; + if (hsz == 0) + res = make_small(value); + else { +#if defined(ARCH_32) || HALFWORD_HEAP + res = erts_sint64_to_big(value, &hp); +#else + res = erts_uint64_to_big((Uint64) value, &hp); +#endif + } + } + + ASSERT(end_hp == hp); + + return res; +} + +static ERTS_INLINE Eterm +unique_monotonic_integer_bif(Process *c_p, int positive) +{ + Uint64 raw; + Uint hsz; + Eterm *hp; + + raw = get_raw_unique_monotonic_integer(); + hsz = get_unique_monotonic_integer_heap_size(raw, positive); + hp = hsz ? HAlloc(c_p, hsz) : NULL; + return make_unique_monotonic_integer_value(hp, hsz, raw, positive); +} + +Sint64 +erts_raw_get_unique_monotonic_integer(void) +{ + return get_raw_unique_monotonic_integer(); +} + +Uint +erts_raw_unique_monotonic_integer_heap_size(Sint64 raw) +{ + return get_unique_monotonic_integer_heap_size(raw, 0); +} + +Eterm +erts_raw_make_unique_monotonic_integer_value(Eterm **hpp, Sint64 raw) +{ + Uint hsz = get_unique_monotonic_integer_heap_size(raw, 0); + Eterm res = make_unique_monotonic_integer_value(*hpp, hsz, raw, 0); + *hpp += hsz; + return res; +} + +Sint64 +erts_get_min_unique_monotonic_integer(void) +{ + return ERTS_UNIQUE_MONOTONIC_OFFSET; +} + +/* --- Debug --- */ + +int +erts_debug_set_unique_monotonic_integer_state(Eterm et_value) +{ + Sint64 value; + + if (!term_to_Sint64(et_value, &value)) { + Uint64 uvalue; + if (!term_to_Uint64(et_value, &uvalue)) + return 0; + value = (Sint64) uvalue; + } + + erts_atomic64_set_mb(&raw_unique_monotonic_integer.w.value, + (erts_aint64_t) value); + return 1; +} + +Eterm +erts_debug_get_unique_monotonic_integer_state(Process *c_p) +{ + Uint64 value; + Eterm hsz, *hp; + + value = (Uint64) erts_atomic64_read_mb(&raw_unique_monotonic_integer.w.value); + + if (IS_USMALL(0, value)) + return make_small(value); + hsz = ERTS_UINT64_HEAP_SIZE(value); + hp = HAlloc(c_p, hsz); + return erts_uint64_to_big(value, &hp); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Initilazation * +\* */ + +void +erts_bif_unique_init(void) +{ + init_reference(); + init_unique_monotonic_integer(); + init_unique_integer(); +} + +void +erts_sched_bif_unique_init(ErtsSchedulerData *esdp) +{ + esdp->unique = (Uint64) 0; + esdp->ref = (Uint64) 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * The BIFs * +\* */ + + +BIF_RETTYPE make_ref_0(BIF_ALIST_0) +{ + BIF_RETTYPE res; + Eterm* hp; + + ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(BIF_P)); + + hp = HAlloc(BIF_P, REF_THING_SIZE); + + res = erts_sched_make_ref_in_buffer(ERTS_PROC_GET_SCHDATA(BIF_P), hp); + + BIF_RET(res); +} + +BIF_RETTYPE unique_integer_0(BIF_ALIST_0) +{ + BIF_RET(unique_integer_bif(BIF_P, 0)); +} + +BIF_RETTYPE unique_integer_1(BIF_ALIST_1) +{ + Eterm modlist = BIF_ARG_1; + int monotonic = 0; + int positive = 0; + BIF_RETTYPE res; + + while (is_list(modlist)) { + Eterm *consp = list_val(modlist); + switch (CAR(consp)) { + case am_monotonic: + monotonic = 1; + break; + case am_positive: + positive = 1; + break; + default: + BIF_ERROR(BIF_P, BADARG); + } + modlist = CDR(consp); + } + + if (is_not_nil(modlist)) + BIF_ERROR(BIF_P, BADARG); + + if (monotonic) + res = unique_monotonic_integer_bif(BIF_P, positive); + else + res = unique_integer_bif(BIF_P, positive); + + BIF_RET(res); +} diff --git a/erts/emulator/beam/erl_bif_unique.h b/erts/emulator/beam/erl_bif_unique.h new file mode 100644 index 0000000000..cd001172a1 --- /dev/null +++ b/erts/emulator/beam/erl_bif_unique.h @@ -0,0 +1,131 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2014. All Rights Reserved. + * + * The 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 ERTS_BIF_UNIQUE_H__ +#define ERTS_BIF_UNIQUE_H__ + +#include "erl_process.h" +#include "big.h" + +void erts_bif_unique_init(void); +void erts_sched_bif_unique_init(ErtsSchedulerData *esdp); + +/* reference */ +Eterm erts_make_ref(Process *); +Eterm erts_make_ref_in_buffer(Eterm buffer[REF_THING_SIZE]); +void erts_make_ref_in_array(Uint32 ref[ERTS_MAX_REF_NUMBERS]); + +/* strict monotonic counter */ + +#define ERTS_MAX_UNIQUE_MONOTONIC_INTEGER_HEAP_SIZE ERTS_MAX_UINT64_HEAP_SIZE + +/* + * Note that a raw value is an intermediate value that + * not necessarily correspond to the end result. + */ +Sint64 erts_raw_get_unique_monotonic_integer(void); +Uint erts_raw_unique_monotonic_integer_heap_size(Sint64 raw); +Eterm erts_raw_make_unique_monotonic_integer_value(Eterm **hpp, Sint64 raw); + +Sint64 erts_get_min_unique_monotonic_integer(void); + +int erts_debug_set_unique_monotonic_integer_state(Eterm et_value); +Eterm erts_debug_get_unique_monotonic_integer_state(Process *c_p); + +/* unique integer */ +#define ERTS_UNIQUE_INT_RAW_VALUES 2 +#define ERTS_MAX_UNIQUE_INT_HEAP_SIZE ERTS_UINT64_ARRAY_TO_BIG_MAX_HEAP_SZ(2) + +Uint erts_raw_unique_integer_heap_size(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]); +Eterm erts_raw_make_unique_integer(Eterm **hpp, Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]); +void erts_raw_get_unique_integer(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]); +Sint64 erts_get_min_unique_integer(void); + +Eterm erts_debug_make_unique_integer(Process *c_p, + Eterm etval0, + Eterm etval1); + + +ERTS_GLB_INLINE void erts_set_ref_numbers(Uint32 ref[ERTS_MAX_REF_NUMBERS], + Uint32 thr_id, Uint64 value); +ERTS_GLB_INLINE Uint32 erts_get_ref_numbers_thr_id(Uint32 ref[ERTS_MAX_REF_NUMBERS]); +ERTS_GLB_INLINE Uint64 erts_get_ref_numbers_value(Uint32 ref[ERTS_MAX_REF_NUMBERS]); +ERTS_GLB_INLINE void erts_sched_make_ref_in_array(ErtsSchedulerData *esdp, + Uint32 ref[ERTS_MAX_REF_NUMBERS]); +ERTS_GLB_INLINE Eterm erts_sched_make_ref_in_buffer(ErtsSchedulerData *esdp, + Eterm buffer[REF_THING_SIZE]); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE void +erts_set_ref_numbers(Uint32 ref[ERTS_MAX_REF_NUMBERS], Uint32 thr_id, Uint64 value) +{ + /* + * We cannot use thread id in the first 18-bit word since + * the hash/phash/phash2 BIFs only hash on this word. If + * we did, we would get really poor hash values. Instead + * we have to shuffle the bits a bit. + */ + ASSERT(thr_id == (thr_id & ((Uint32) 0x3ffff))); + ref[0] = (Uint32) (value & ((Uint64) 0x3ffff)); + ref[1] = (((Uint32) (value & ((Uint64) 0xfffc0000))) + | (thr_id & ((Uint32) 0x3ffff))); + ref[2] = (Uint32) ((value >> 32) & ((Uint64) 0xffffffff)); +} + +ERTS_GLB_INLINE Uint32 +erts_get_ref_numbers_thr_id(Uint32 ref[ERTS_MAX_REF_NUMBERS]) +{ + return ref[1] & ((Uint32) 0x3ffff); +} + +ERTS_GLB_INLINE Uint64 +erts_get_ref_numbers_value(Uint32 ref[ERTS_MAX_REF_NUMBERS]) +{ + return (((((Uint64) ref[2]) & ((Uint64) 0xffffffff)) << 32) + | (((Uint64) ref[1]) & ((Uint64) 0xfffc0000)) + | (((Uint64) ref[0]) & ((Uint64) 0x3ffff))); +} + +ERTS_GLB_INLINE void +erts_sched_make_ref_in_array(ErtsSchedulerData *esdp, + Uint32 ref[ERTS_MAX_REF_NUMBERS]) +{ + Uint64 value; + + ASSERT(esdp); + value = esdp->ref++; + erts_set_ref_numbers(ref, (Uint32) esdp->thr_id, value); +} + +ERTS_GLB_INLINE Eterm +erts_sched_make_ref_in_buffer(ErtsSchedulerData *esdp, + Eterm buffer[REF_THING_SIZE]) +{ + Eterm* hp = buffer; + Uint32 ref[ERTS_MAX_REF_NUMBERS]; + + erts_sched_make_ref_in_array(esdp, ref); + write_ref_thing(hp, ref[0], ref[1], ref[2]); + return make_internal_ref(hp); +} + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +#endif /* ERTS_BIF_UNIQUE_H__ */ diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 0db42d4325..fea9b16e90 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -37,6 +37,7 @@ #include "hipe_mode_switch.h" #endif #include "dtrace-wrapper.h" +#include "erl_bif_unique.h" #define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1 #define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20 diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 61f8385efc..0e128c9b99 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -45,6 +45,7 @@ #include "erl_thr_queue.h" #include "erl_async.h" #include "erl_ptab.h" +#include "erl_bif_unique.h" #ifdef HIPE #include "hipe_mode_switch.h" /* for hipe_mode_switch_init() */ @@ -134,7 +135,9 @@ static void erl_init(int ncpu, int legacy_proc_tab, int port_tab_sz, int port_tab_sz_ignore_files, - int legacy_port_tab); + int legacy_port_tab, + int time_correction, + ErtsTimeWarpMode time_warp_mode); static erts_atomic_t exiting; @@ -188,10 +191,6 @@ static int no_dirty_io_schedulers; Uint32 verbose; /* See erl_debug.h for information about verbose */ #endif -int erts_disable_tolerant_timeofday; /* Time correction can be disabled it is - * not and/or it is too slow. - */ - int erts_atom_table_size = ATOM_LIMIT; /* Maximum number of atoms */ int erts_modified_timing_level; @@ -269,6 +268,19 @@ this_rel_num(void) return this_rel; } +static ERTS_INLINE void +set_default_time_adj(int *time_correction_p, ErtsTimeWarpMode *time_warp_mode_p) +{ + *time_correction_p = 1; + *time_warp_mode_p = ERTS_NO_TIME_WARP_MODE; + if (!erts_check_time_adj_support(*time_correction_p, + *time_warp_mode_p)) { + *time_correction_p = 0; + ASSERT(erts_check_time_adj_support(*time_correction_p, + *time_warp_mode_p)); + } +} + /* * Common error printout function, all error messages * that don't go to the error logger go through here. @@ -284,13 +296,22 @@ static int early_init(int *argc, char **argv); void erts_short_init(void) { - int ncpu = early_init(NULL, NULL); + + int ncpu; + int time_correction; + ErtsTimeWarpMode time_warp_mode; + + set_default_time_adj(&time_correction, + &time_warp_mode); + ncpu = early_init(NULL, NULL); erl_init(ncpu, ERTS_DEFAULT_MAX_PROCESSES, 0, ERTS_DEFAULT_MAX_PORTS, 0, - 0); + 0, + time_correction, + time_warp_mode); erts_initialized = 1; } @@ -300,12 +321,15 @@ erl_init(int ncpu, int legacy_proc_tab, int port_tab_sz, int port_tab_sz_ignore_files, - int legacy_port_tab) + int legacy_port_tab, + int time_correction, + ErtsTimeWarpMode time_warp_mode) { init_benchmarking(); + erts_bif_unique_init(); erts_init_monitors(); - erts_init_time(); + erts_init_time(time_correction, time_warp_mode); erts_init_sys_common_misc(); erts_init_process(ncpu, proc_tab_sz, legacy_proc_tab); erts_init_scheduling(no_schedulers, @@ -509,9 +533,9 @@ void erts_usage(void) /* erts_fprintf(stderr, "-b func set the boot function (default boot)\n"); */ - erts_fprintf(stderr, "-c disable continuous date/time correction with\n"); - erts_fprintf(stderr, " respect to uptime\n"); - + erts_fprintf(stderr, "-c bool enable or disable time correction\n"); + erts_fprintf(stderr, "-C mode set time warp mode; valid modes are:\n"); + erts_fprintf(stderr, " no_time_warp|single_time_warp|multi_time_warp\n"); erts_fprintf(stderr, "-d don't write a crash dump for internally detected errors\n"); erts_fprintf(stderr, " (halt(String) will still produce a crash dump)\n"); erts_fprintf(stderr, "-fn[u|a|l] Control how filenames are interpreted\n"); @@ -681,7 +705,6 @@ early_init(int *argc, char **argv) /* erts_sched_compact_load = 1; erts_printf_eterm_func = erts_printf_term; - erts_disable_tolerant_timeofday = 0; display_items = 200; erts_backtrace_depth = DEFAULT_BACKTRACE_SIZE; erts_async_max_threads = ERTS_DEFAULT_NO_ASYNC_THREADS; @@ -1144,6 +1167,7 @@ early_init(int *argc, char **argv) /* /* Creates threads on Windows that depend on the arguments, so has to be after erl_sys_args */ erl_sys_init(); + erts_early_init_time_sup(); erts_ets_realloc_always_moves = 0; erts_ets_always_compress = 0; @@ -1187,7 +1211,11 @@ erl_start(int argc, char **argv) int port_tab_sz_ignore_files = 0; int legacy_proc_tab = 0; int legacy_port_tab = 0; + int time_correction; + ErtsTimeWarpMode time_warp_mode; + set_default_time_adj(&time_correction, + &time_warp_mode); envbufsz = sizeof(envbuf); if (erts_sys_getenv_raw(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0) @@ -1896,15 +1924,56 @@ erl_start(int argc, char **argv) } break; } - case 'c': - if (argv[i][2] == 0) { /* -c: documented option */ - erts_disable_tolerant_timeofday = 1; + case 'C': + arg = get_arg(argv[i]+2, argv[i+1], &i); + if (sys_strcmp(arg, "no_time_warp") == 0) + time_warp_mode = ERTS_NO_TIME_WARP_MODE; + else if (sys_strcmp(arg, "single_time_warp") == 0) + time_warp_mode = ERTS_SINGLE_TIME_WARP_MODE; + else if (sys_strcmp(arg, "multi_time_warp") == 0) + time_warp_mode = ERTS_MULTI_TIME_WARP_MODE; + else { + erts_fprintf(stderr, + "Invalid time warp mode: %s\n", arg); + erts_usage(); } + break; + case 'c': + if (sys_strcmp(argv[i]+2, "false") == 0) + goto time_correction_false; + else if (sys_strcmp(argv[i]+2, "true") == 0) + goto time_correction_true; #ifdef ERTS_OPCODE_COUNTER_SUPPORT else if (argv[i][2] == 'i') { /* -ci: undcoumented option*/ count_instructions = 1; } #endif + else if (argv[i][2] == '\0') { + if (i + 1 >= argc) + goto time_correction_false; + else { + if (sys_strcmp(argv[i+1], "false") == 0) { + (void) get_arg(argv[i]+2, argv[i+1], &i); + goto time_correction_false; + } + else if (sys_strcmp(argv[i+1], "true") == 0) { + (void) get_arg(argv[i]+2, argv[i+1], &i); + time_correction_true: + time_correction = 1; + break; + } + else { + time_correction_false: + time_correction = 0; + break; + } + } + } + else { + arg = get_arg(argv[i]+2, argv[i+1], &i); + erts_fprintf(stderr, "Invalid time correnction value: %s\n", arg); + erts_usage(); + } break; case 'W': arg = get_arg(argv[i]+2, argv[i+1], &i); @@ -1950,6 +2019,30 @@ erl_start(int argc, char **argv) i++; } + if (!erts_check_time_adj_support(time_correction, time_warp_mode)) { + char *time_correction_str = time_correction ? "Enabled" : "Disabled"; + char *time_warp_str = "undefined"; + switch (time_warp_mode) { + case ERTS_NO_TIME_WARP_MODE: + time_warp_str = "no"; + break; + case ERTS_SINGLE_TIME_WARP_MODE: + time_warp_str = "single"; + break; + case ERTS_MULTI_TIME_WARP_MODE: + time_warp_str = "multi"; + break; + default: + time_warp_str = "undefined"; + break; + } + erts_fprintf(stderr, "%s time correction with %s time warp mode " + "is not supported on this platform\n", + time_correction_str, + time_warp_str); + erts_usage(); + } + /* Output format on windows for sprintf defaults to three exponents. * We use two-exponent to mimic normal sprintf behaviour. */ @@ -1983,7 +2076,9 @@ erl_start(int argc, char **argv) legacy_proc_tab, port_tab_sz, port_tab_sz_ignore_files, - legacy_port_tab); + legacy_port_tab, + time_correction, + time_warp_mode); load_preloaded(); erts_end_staging_code_ix(); diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index b105ece6f1..261460d054 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -140,7 +140,6 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "async_enq_mtx", NULL }, #ifdef ERTS_SMP { "atom_tab", NULL }, - { "make_ref", NULL }, { "misc_op_list_pre_alloc_lock", "address" }, { "message_pre_alloc_lock", "address" }, { "ptimer_pre_alloc_lock", "address", }, @@ -168,6 +167,8 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "timer_wheel", NULL }, { "system_block", NULL }, { "timeofday", NULL }, + { "get_time", NULL }, + { "get_corrected_time", NULL }, { "breakpoints", NULL }, { "pollsets_lock", NULL }, { "pix_lock", "address" }, @@ -184,10 +185,8 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "efile_drv dtrace mutex", NULL }, #endif { "mtrace_buf", NULL }, -#ifdef __WIN32__ #ifdef ERTS_SMP - { "sys_gethrtime", NULL }, -#endif + { "os_monotonic_time", NULL }, #endif { "erts_alloc_hard_debug", NULL }, { "hard_dbg_mseg", NULL }, diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index cf6996ea06..ddeb56a6be 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -104,11 +104,10 @@ static void lcnt_clear_stats(erts_lcnt_lock_stats_t *stats) { } static void lcnt_time(erts_lcnt_time_t *time) { -#if 0 || defined(HAVE_GETHRTIME) - SysHrTime hr_time; - hr_time = sys_gethrtime(); - time->s = (unsigned long)(hr_time / 1000000000LL); - time->ns = (unsigned long)(hr_time - 1000000000LL*time->s); +#if 0 || defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) + ErtsMonotonicTime mtime = ERTS_MONOTONIC_TO_NSEC(erts_os_monotonic_time()); + time->s = (unsigned long) (mtime / 1000000000LL); + time->ns = (unsigned long) (mtime - 1000000000LL*time->s); #else SysTimeval tv; sys_gettimeofday(&tv); diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h index ffbb93da1b..09fadd7e9e 100644 --- a/erts/emulator/beam/erl_lock_count.h +++ b/erts/emulator/beam/erl_lock_count.h @@ -76,7 +76,7 @@ /* histogram */ #define ERTS_LCNT_HISTOGRAM_MAX_NS (((unsigned long)1LL << 28) - 1) -#if 0 || defined(HAVE_GETHRTIME) +#if 0 || defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) #define ERTS_LCNT_HISTOGRAM_SLOT_SIZE (30) #define ERTS_LCNT_HISTOGRAM_RSHIFT (0) #else diff --git a/erts/emulator/beam/erl_monitors.h b/erts/emulator/beam/erl_monitors.h index fb11dbbd22..9972890db7 100644 --- a/erts/emulator/beam/erl_monitors.h +++ b/erts/emulator/beam/erl_monitors.h @@ -82,6 +82,7 @@ /* Type tags for monitors */ #define MON_ORIGIN 1 #define MON_TARGET 3 +#define MON_TIME_OFFSET 7 /* Type tags for links */ #define LINK_PID 1 /* ...Or port */ @@ -103,7 +104,7 @@ typedef struct erts_monitor_or_link { typedef struct erts_monitor { struct erts_monitor *left, *right; Sint16 balance; - Uint16 type; /* MON_ORIGIN | MON_TARGET */ + Uint16 type; /* MON_ORIGIN | MON_TARGET | MON_TIME_OFFSET */ Eterm ref; Eterm pid; /* In case of distributed named monitor, this is the nodename atom in MON_ORIGIN process, otherwise a pid or diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index adc3520ebb..47d0af16bc 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -36,6 +36,7 @@ #include "erl_thr_progress.h" #include "dtrace-wrapper.h" #include "erl_process.h" +#include "erl_bif_unique.h" #if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP)) #define HAVE_USE_DTRACE 1 #endif diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index ea63d20dfa..81bc3d2429 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -43,6 +43,7 @@ #include "erl_async.h" #include "dtrace-wrapper.h" #include "erl_ptab.h" +#include "erl_bif_unique.h" #define ERTS_DELAYED_WAKEUP_INFINITY (~(Uint64) 0) @@ -702,8 +703,8 @@ init_sched_wall_time(ErtsSchedWallTime *swtp) static ERTS_INLINE Uint64 sched_wall_time_ts(void) { -#ifdef HAVE_GETHRTIME - return (Uint64) sys_gethrtime(); +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + return (Uint64) erts_os_monotonic_time(); #else Uint64 res; SysTimeval tv; @@ -2843,7 +2844,6 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) else #endif { - erts_aint_t dt; erts_smp_atomic32_set_relb(&function_calls, 0); *fcalls = 0; @@ -2868,6 +2868,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) goto sys_aux_work; while (spincount-- > 0) { + ErtsMonotonicTime current_time; sys_poll_aux_work: @@ -2877,8 +2878,9 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ASSERT(!erts_port_task_have_outstanding_io_tasks()); erl_sys_schedule(1); /* Might give us something to do */ - dt = erts_do_time_read_and_reset(); - if (dt) erts_bump_timer(dt); + current_time = erts_get_monotonic_time(); + if (current_time >= erts_next_timeout_time()) + erts_bump_timers(current_time); sys_aux_work: #ifndef ERTS_SMP @@ -2993,8 +2995,11 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erl_sys_schedule(0); - dt = erts_do_time_read_and_reset(); - if (dt) erts_bump_timer(dt); + { + ErtsMonotonicTime current_time = erts_get_monotonic_time(); + if (current_time >= erts_next_timeout_time()) + erts_bump_timers(current_time); + } #ifndef ERTS_SMP if (rq->len == 0 && !rq->misc.start) @@ -5276,6 +5281,9 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num, esdp->run_queue = runq; esdp->run_queue->scheduler = esdp; + esdp->thr_id = (Uint32) num; + erts_sched_bif_unique_init(esdp); + if (daww_ptr) { init_aux_work_data(&esdp->aux_work_data, esdp, *daww_ptr); #ifdef ERTS_SMP @@ -7717,6 +7725,8 @@ sched_dirty_cpu_thread_func(void *vesdp) callbacks.wait = NULL; callbacks.finalize_wait = NULL; + esdp->thr_id += erts_no_schedulers; + erts_thr_progress_register_unmanaged_thread(&callbacks); #ifdef ERTS_ENABLE_LOCK_CHECK { @@ -7778,6 +7788,8 @@ sched_dirty_io_thread_func(void *vesdp) callbacks.wait = NULL; callbacks.finalize_wait = NULL; + esdp->thr_id += erts_no_schedulers + erts_no_dirty_cpu_schedulers; + erts_thr_progress_register_unmanaged_thread(&callbacks); #ifdef ERTS_ENABLE_LOCK_CHECK { @@ -8897,7 +8909,6 @@ Process *schedule(Process *p, int calls) { Process *proxy_p = NULL; ErtsRunQueue *rq; - erts_aint_t dt; ErtsSchedulerData *esdp; int context_reds; int fcalls; @@ -9027,11 +9038,13 @@ Process *schedule(Process *p, int calls) ERTS_SMP_CHK_NO_PROC_LOCKS; - dt = erts_do_time_read_and_reset(); - if (dt) { - erts_smp_runq_unlock(rq); - erts_bump_timer(dt); - erts_smp_runq_lock(rq); + { + ErtsMonotonicTime current_time = erts_get_monotonic_time(); + if (current_time >= erts_next_timeout_time()) { + erts_smp_runq_unlock(rq); + erts_bump_timers(current_time); + erts_smp_runq_lock(rq); + } } BM_STOP_TIMER(system); @@ -9177,6 +9190,7 @@ Process *schedule(Process *p, int calls) else if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && (fcalls > input_reductions && prepare_for_sys_schedule(esdp, !0))) { + ErtsMonotonicTime current_time; /* * Schedule system-level activities. */ @@ -9189,8 +9203,10 @@ Process *schedule(Process *p, int calls) #endif erts_smp_runq_unlock(rq); erl_sys_schedule(1); - dt = erts_do_time_read_and_reset(); - if (dt) erts_bump_timer(dt); + + current_time = erts_get_monotonic_time(); + if (current_time >= erts_next_timeout_time()) + erts_bump_timers(current_time); #ifdef ERTS_SMP erts_smp_runq_lock(rq); @@ -11501,7 +11517,8 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) ErtsMonitor *rmon; Process *rp; - if (mon->type == MON_ORIGIN) { + switch (mon->type) { + case MON_ORIGIN: /* We are monitoring someone else, we need to demonitor that one.. */ if (is_atom(mon->pid)) { /* remote by name */ ASSERT(is_node_name_atom(mon->pid)); @@ -11564,7 +11581,8 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) } } } - } else { /* type == MON_TARGET */ + break; + case MON_TARGET: ASSERT(mon->type == MON_TARGET); ASSERT(is_pid(mon->pid) || is_internal_port(mon->pid)); if (is_internal_port(mon->pid)) { @@ -11623,6 +11641,12 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) } } } + break; + case MON_TIME_OFFSET: + erts_demonitor_time_offset(mon->ref); + break; + default: + ERTS_INTERNAL_ERROR("Invalid monitor type"); } done: /* As the monitors are previously removed from the process, diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index f50b217d4a..6ef56b1974 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -341,7 +341,7 @@ typedef struct { } ErtsRunQueueInfo; -#ifdef HAVE_GETHRTIME +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT # undef ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT # define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT 1 #endif @@ -588,6 +588,10 @@ struct ErtsSchedulerData_ { ErtsAuxWorkData aux_work_data; ErtsAtomCacheMap atom_cache_map; + Uint32 thr_id; + Uint64 unique; + Uint64 ref; + ErtsSchedAllocData alloc_data; Uint64 reductions; diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 664c479eb6..f111846041 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -1342,18 +1342,16 @@ erts_thr_progress_fatal_error_block(SWord timeout, ErtsThrPrgrData *tpd = perhaps_thr_prgr_data(NULL); erts_aint32_t bc; SWord time_left = timeout; - SysTimeval to; + ErtsMonotonicTime timeout_time; /* * Counting poll intervals may give us a too long timeout - * if cpu is busy. If we got tolerant time of day we use it - * to prevent this. + * if cpu is busy. We use timeout time to try to prevent + * this. In case we havn't got time correction this may + * however fail too... */ - if (!erts_disable_tolerant_timeofday) { - erts_get_timeval(&to); - to.tv_sec += timeout / 1000; - to.tv_sec += timeout % 1000; - } + timeout_time = erts_get_monotonic_time(); + timeout_time += ERTS_MSEC_TO_MONOTONIC((ErtsMonotonicTime) timeout); if (!tpd) { /* @@ -1378,14 +1376,8 @@ erts_thr_progress_fatal_error_block(SWord timeout, break; /* Succefully blocked all managed threads */ if (time_left <= 0) break; /* Timeout */ - if (!erts_disable_tolerant_timeofday) { - SysTimeval now; - erts_get_timeval(&now); - if (now.tv_sec > to.tv_sec) - break; /* Timeout */ - if (now.tv_sec == to.tv_sec && now.tv_usec >= to.tv_usec) - break; /* Timeout */ - } + if (timeout_time <= erts_get_monotonic_time()) + break; /* Timeout */ } } diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 4bbdcaa3e3..e461594e9c 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -20,11 +20,12 @@ #ifndef ERL_TIME_H__ #define ERL_TIME_H__ -#define ERTS_SHORT_TIME_T_MAX ERTS_AINT32_T_MAX -#define ERTS_SHORT_TIME_T_MIN ERTS_AINT32_T_MIN -typedef erts_aint32_t erts_short_time_t; +#if defined(DEBUG) || 0 +#define ERTS_TIME_ASSERT(B) ERTS_ASSERT(B) +#else +#define ERTS_TIME_ASSERT(B) ((void) 1) +#endif -extern erts_smp_atomic32_t do_time; /* set at clock interrupt */ extern SysTimeval erts_first_emu_time; /* @@ -34,7 +35,7 @@ typedef struct erl_timer { struct erl_timer* next; /* next entry tiw slot or chain */ struct erl_timer* prev; /* prev entry tiw slot or chain */ Uint slot; /* slot in timer wheel */ - Uint count; /* number of loops remaining */ + ErtsMonotonicTime timeout_pos; /* Timeout in absolute clock ticks */ int active; /* 1=activated, 0=deactivated */ /* called when timeout */ void (*timeout)(void*); @@ -70,36 +71,36 @@ void erts_create_smp_ptimer(ErtsSmpPTimer **timer_ref, void erts_cancel_smp_ptimer(ErtsSmpPTimer *ptimer); #endif +void erts_monitor_time_offset(Eterm id, Eterm ref); +int erts_demonitor_time_offset(Eterm ref); + +void erts_early_init_time_sup(void); +void erts_late_init_time_sup(void); + /* timer-wheel api */ -void erts_init_time(void); +void erts_init_time(int time_correction, ErtsTimeWarpMode time_warp_mode); void erts_set_timer(ErlTimer*, ErlTimeoutProc, ErlCancelProc, void*, Uint); void erts_cancel_timer(ErlTimer*); -void erts_bump_timer(erts_short_time_t); +void erts_bump_timers(ErtsMonotonicTime); Uint erts_timer_wheel_memory_size(void); Uint erts_time_left(ErlTimer *); -erts_short_time_t erts_next_time(void); #ifdef DEBUG void erts_p_slpq(void); #endif -ERTS_GLB_INLINE erts_short_time_t erts_do_time_read_and_reset(void); -ERTS_GLB_INLINE void erts_do_time_add(erts_short_time_t); +ErtsMonotonicTime erts_check_next_timeout_time(ErtsMonotonicTime); -#if ERTS_GLB_INLINE_INCL_FUNC_DEF +extern erts_atomic64_t erts_next_timeout__; -ERTS_GLB_INLINE erts_short_time_t erts_do_time_read_and_reset(void) -{ - erts_short_time_t time = erts_smp_atomic32_xchg_acqb(&do_time, 0); - if (time < 0) - erl_exit(ERTS_ABORT_EXIT, "Internal time management error\n"); - return time; -} +ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(void); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF -ERTS_GLB_INLINE void erts_do_time_add(erts_short_time_t elapsed) +ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(void) { - erts_smp_atomic32_add_relb(&do_time, elapsed); + return (ErtsMonotonicTime) erts_atomic64_read_acqb(&erts_next_timeout__); } #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ @@ -121,25 +122,228 @@ void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec); 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); +int erts_has_time_correction(void); +int erts_check_time_adj_support(int time_correction, + ErtsTimeWarpMode time_warp_mode); + +ErtsTimeWarpMode erts_time_warp_mode(void); + +typedef enum { + ERTS_TIME_OFFSET_PRELIMINARY, + ERTS_TIME_OFFSET_FINAL, + ERTS_TIME_OFFSET_VOLATILE +} ErtsTimeOffsetState; + +ErtsTimeOffsetState erts_time_offset_state(void); +ErtsTimeOffsetState erts_finalize_time_offset(void); +struct process; +Eterm erts_get_monotonic_start_time(struct process *c_p); +Eterm erts_monotonic_time_source(struct process*c_p); + +#ifdef SYS_CLOCK_RESOLUTION +#define ERTS_CLKTCK_RESOLUTION ((ErtsMonotonicTime) (SYS_CLOCK_RESOLUTION*1000)) +#else +#define ERTS_CLKTCK_RESOLUTION (erts_time_sup__.r.o.clktck_resolution) +#endif + +struct erts_time_sup_read_only__ { + ErtsMonotonicTime monotonic_time_unit; +#ifndef SYS_CLOCK_RESOLUTION + ErtsMonotonicTime clktck_resolution; +#endif +}; + +typedef struct { + union { + struct erts_time_sup_read_only__ o; + char align__[(((sizeof(struct erts_time_sup_read_only__) - 1) + / ASSUMED_CACHE_LINE_SIZE) + 1) + * ASSUMED_CACHE_LINE_SIZE]; + } r; +} ErtsTimeSupData; + +extern ErtsTimeSupData erts_time_sup__; -ERTS_GLB_INLINE int erts_cmp_timeval(SysTimeval *t1p, SysTimeval *t2p); +ERTS_GLB_INLINE Uint64 +erts_time_unit_conversion(Uint64 value, + Uint32 from_time_unit, + Uint32 to_time_unit); #if ERTS_GLB_INLINE_INCL_FUNC_DEF -ERTS_GLB_INLINE int -erts_cmp_timeval(SysTimeval *t1p, SysTimeval *t2p) +ERTS_GLB_INLINE Uint64 +erts_time_unit_conversion(Uint64 value, + Uint32 from_time_unit, + Uint32 to_time_unit) { - if (t1p->tv_sec == t2p->tv_sec) { - if (t1p->tv_usec < t2p->tv_usec) - return -1; - else if (t1p->tv_usec > t2p->tv_usec) - return 1; - return 0; - } - return t1p->tv_sec < t2p->tv_sec ? -1 : 1; + Uint64 high, low, result; + if (value <= ~((Uint64) 0)/to_time_unit) + return (value*to_time_unit)/from_time_unit; + + low = value & ((Uint64) 0xffffffff); + high = (value >> 32) & ((Uint64) 0xffffffff); + + low *= to_time_unit; + high *= to_time_unit; + + high += (low >> 32) & ((Uint64) 0xffffffff); + low &= ((Uint64) 0xffffffff); + + result = high % from_time_unit; + high /= from_time_unit; + high <<= 32; + + result <<= 32; + result += low; + result /= from_time_unit; + result += high; + + return result; } -#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT + +/* + * If the monotonic time unit is a compile time constant, + * it is assumed (and need) to be a power of 10. + */ + +#define ERTS_MONOTONIC_TIME_UNIT \ + ((ErtsMonotonicTime) ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT) + +#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000*1000*1000 +/* Nano-second time unit */ + +#define ERTS_MONOTONIC_TO_SEC__(NSEC) ((NSEC) / (1000*1000*1000)) +#define ERTS_MONOTONIC_TO_MSEC__(NSEC) ((NSEC) / (1000*1000)) +#define ERTS_MONOTONIC_TO_USEC__(NSEC) ((NSEC) / 1000) +#define ERTS_MONOTONIC_TO_NSEC__(NSEC) (NSEC) + +#define ERTS_SEC_TO_MONOTONIC__(SEC) (((ErtsMonotonicTime) (SEC))*(1000*1000*1000)) +#define ERTS_MSEC_TO_MONOTONIC__(MSEC) (((ErtsMonotonicTime) (MSEC))*(1000*1000)) +#define ERTS_USEC_TO_MONOTONIC__(USEC) (((ErtsMonotonicTime) (USEC))*1000) +#define ERTS_NSEC_TO_MONOTONIC__(NSEC) ((ErtsMonotonicTime) (NSEC)) + +#elif ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000*1000 +/* Micro-second time unit */ + +#define ERTS_MONOTONIC_TO_SEC__(USEC) ((USEC) / (1000*1000)) +#define ERTS_MONOTONIC_TO_MSEC__(USEC) ((USEC) / 1000) +#define ERTS_MONOTONIC_TO_USEC__(USEC) (USEC) +#define ERTS_MONOTONIC_TO_NSEC__(USEC) ((USEC)*1000) + +#define ERTS_SEC_TO_MONOTONIC__(SEC) (((ErtsMonotonicTime) (SEC))*(1000*1000)) +#define ERTS_MSEC_TO_MONOTONIC__(MSEC) (((ErtsMonotonicTime) (MSEC))*1000) +#define ERTS_USEC_TO_MONOTONIC__(USEC) ((ErtsMonotonicTime) (USEC)) +#define ERTS_NSEC_TO_MONOTONIC__(NSEC) (((ErtsMonotonicTime) (NSEC))/1000) + +#elif ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000 +/* Milli-second time unit */ + +#define ERTS_MONOTONIC_TO_SEC__(MSEC) ((USEC)/(1000)) +#define ERTS_MONOTONIC_TO_MSEC__(MSEC) (MSEC) +#define ERTS_MONOTONIC_TO_USEC__(MSEC) ((MSEC)*1000) +#define ERTS_MONOTONIC_TO_NSEC__(MSEC) ((MSEC)*(1000*1000)) + +#define ERTS_SEC_TO_MONOTONIC__(SEC) (((ErtsMonotonicTime) (SEC))*1000) +#define ERTS_MSEC_TO_MONOTONIC__(MSEC) ((ErtsMonotonicTime) (MSEC)) +#define ERTS_USEC_TO_MONOTONIC__(USEC) (((ErtsMonotonicTime) (USEC))/1000) +#define ERTS_NSEC_TO_MONOTONIC__(NSEC) (((ErtsMonotonicTime) (NSEC))/(1000*1000)) + +#else +#error Missing implementation for monotonic time unit +#endif + +#define ERTS_MONOTONIC_TO_CLKTCKS__(MON) \ + ((MON) / (ERTS_MONOTONIC_TIME_UNIT/ERTS_CLKTCK_RESOLUTION)) +#define ERTS_CLKTCKS_TO_MONOTONIC__(TCKS) \ + ((TCKS) * (ERTS_MONOTONIC_TIME_UNIT/ERTS_CLKTCK_RESOLUTION)) + +#else /* !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */ + +#define ERTS_MONOTONIC_TIME_UNIT (erts_time_sup__.r.o.monotonic_time_unit) + +#define ERTS_CONV_FROM_MON_UNIT___(M, TO) \ + ((ErtsMonotonicTime) \ + erts_time_unit_conversion((Uint64) (M), \ + (Uint32) ERTS_MONOTONIC_TIME_UNIT, \ + (Uint32) (TO))) + +#define ERTS_CONV_TO_MON_UNIT___(M, FROM) \ + ((ErtsMonotonicTime) \ + erts_time_unit_conversion((Uint64) (M), \ + (Uint32) (FROM), \ + (Uint32) ERTS_MONOTONIC_TIME_UNIT)) \ + +#define ERTS_MONOTONIC_TO_SEC__(M) \ + ERTS_CONV_FROM_MON_UNIT___((M), 1) +#define ERTS_MONOTONIC_TO_MSEC__(M) \ + ERTS_CONV_FROM_MON_UNIT___((M), 1000) +#define ERTS_MONOTONIC_TO_USEC__(M) \ + ERTS_CONV_FROM_MON_UNIT___((M), 1000*1000) +#define ERTS_MONOTONIC_TO_NSEC__(M) \ + ERTS_CONV_FROM_MON_UNIT___((M), 1000*1000*1000) + +#define ERTS_SEC_TO_MONOTONIC__(SEC) \ + ERTS_CONV_TO_MON_UNIT___((SEC), 1) +#define ERTS_MSEC_TO_MONOTONIC__(MSEC) \ + ERTS_CONV_TO_MON_UNIT___((MSEC), 1000) +#define ERTS_USEC_TO_MONOTONIC__(USEC) \ + ERTS_CONV_TO_MON_UNIT___((USEC), 1000*1000) +#define ERTS_NSEC_TO_MONOTONIC__(NSEC) \ + ERTS_CONV_TO_MON_UNIT___((NSEC), 1000*1000*1000) + +#define ERTS_MONOTONIC_TO_CLKTCKS__(MON) \ + ERTS_CONV_FROM_MON_UNIT___((MON), ERTS_CLKTCK_RESOLUTION) +#define ERTS_CLKTCKS_TO_MONOTONIC__(TCKS) \ + ERTS_CONV_TO_MON_UNIT___((TCKS), ERTS_CLKTCK_RESOLUTION) + +#endif /* !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */ + +#define ERTS_MSEC_TO_CLKTCKS__(MON) \ + ((MON) * (ERTS_CLKTCK_RESOLUTION/1000)) +#define ERTS_CLKTCKS_TO_MSEC__(TCKS) \ + ((TCKS) / (ERTS_CLKTCK_RESOLUTION/1000)) + +#define ERTS_MONOTONIC_TO_SEC(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_MONOTONIC_TO_SEC__((X))) +#define ERTS_MONOTONIC_TO_MSEC(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_MONOTONIC_TO_MSEC__((X))) +#define ERTS_MONOTONIC_TO_USEC(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_MONOTONIC_TO_USEC__((X))) +#define ERTS_MONOTONIC_TO_NSEC(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_MONOTONIC_TO_NSEC__((X))) +#define ERTS_SEC_TO_MONOTONIC(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_SEC_TO_MONOTONIC__((X))) +#define ERTS_MSEC_TO_MONOTONIC(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_MSEC_TO_MONOTONIC__((X))) +#define ERTS_USEC_TO_MONOTONIC(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_USEC_TO_MONOTONIC__((X))) +#define ERTS_NSEC_TO_MONOTONIC(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_NSEC_TO_MONOTONIC__((X))) + +#define ERTS_MONOTONIC_TO_CLKTCKS(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_MONOTONIC_TO_CLKTCKS__((X))) +#define ERTS_CLKTCKS_TO_MONOTONIC(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_CLKTCKS_TO_MONOTONIC__((X))) + +#define ERTS_MSEC_TO_CLKTCKS(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_MSEC_TO_CLKTCKS__((X))) +#define ERTS_CLKTCKS_TO_MSEC(X) \ + (ERTS_TIME_ASSERT((X) >= 0), \ + ERTS_CLKTCKS_TO_MSEC__((X))) + #endif /* ERL_TIME_H__ */ diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 3272a5326d..1534fb8058 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -18,60 +18,8 @@ */ /* -** Support routines for the timer wheel -** -** This code contains two strategies for dealing with -** date/time changes in the system. -** If the system has some kind of high resolution timer (HAVE_GETHRTIME), -** the high resolution timer is used to correct the time-of-day and the -** timeouts, the base source is the hrtimer, but at certain intervals the -** OS time-of-day is checked and if it is not within certain bounds, the -** delivered time gets slowly adjusted for each call until -** it corresponds to the system time (built-in adjtime...). -** The call gethrtime() is detected by autoconf on Unix, but other -** platforms may define it in erl_*_sys.h and implement -** their own high resolution timer. The high resolution timer -** strategy is (probably) best on all systems where the timer have -** a resolution higher or equal to gettimeofday (or what's implemented -** is sys_gettimeofday()). The actual resolution is the interesting thing, -** not the unit's thats used (i.e. on VxWorks, nanoseconds can be -** retrieved in terms of units, but the actual resolution is the same as -** for the clock ticks). -** If the systems best timer routine is kernel ticks returned from -** sys_times(), and the actual resolution of sys_gettimeofday() is -** better (like most unixes that does not have any realtime extensions), -** another strategy is used. The tolerant gettimeofday() corrects -** the value with respect to uptime (sys_times() return value) and checks -** for correction both when delivering timeticks and delivering nowtime. -** this strategy is slower, but accurate on systems without better timer -** routines. The kernel tick resolution is not enough to implement -** a gethrtime routine. On Linux and other non solaris unix-boxes the second -** strategy is used, on all other platforms we use the first. -** -** The following is expected (from sys.[ch] and erl_*_sys.h): -** -** 64 bit integers. So it is, and so it will be. -** -** sys_init_time(), will return the clock resolution in MS and -** that's about it. More could be added of course -** If the clock-rate is constant (i.e. 1 ms) one can define -** SYS_CLOCK_RESOLUTION (to 1), -** which makes erts_deliver_time/erts_time_remaining a bit faster. -** -** if HAVE_GETHRTIME is defined: -** sys_gethrtime() will return a SysHrTime (long long) representing -** nanoseconds, sys_init_hrtime() will do any initialization. -** else -** a long (64bit) integer type called Sint64 should be defined. -** -** sys_times() will return clock_ticks since start and -** fill in a SysTimes structure (struct tms). Instead of CLK_TCK, -** SYS_CLK_TCK is used to determine the resolution of kernel ticks. -** -** sys_gettimeofday() will take a SysTimeval (a struct timeval) as parameter -** and fill it in as gettimeofday(X,NULL). -** -*/ + * Support routines for the time + */ #ifdef HAVE_CONFIG_H # include "config.h" @@ -80,384 +28,1028 @@ #include "sys.h" #include "erl_vm.h" #include "global.h" - + static erts_smp_mtx_t erts_timeofday_mtx; - -static SysTimeval inittv; /* Used everywhere, the initial time-of-day */ +static erts_smp_mtx_t erts_get_time_mtx; static SysTimes t_start; /* Used in elapsed_time_both */ -static SysTimeval gtv; /* Used in wall_clock_elapsed_time_both */ -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() */ +static ErtsMonotonicTime prev_wall_clock_elapsed; /* Used in wall_clock_elapsed_time_both */ +static ErtsMonotonicTime previous_now; /* Used in get_now */ + +static ErtsMonitor *time_offset_monitors = NULL; +static Uint no_time_offset_monitors = 0; -union { - erts_smp_atomic_t time; - char align[ERTS_CACHE_LINE_SIZE]; -} approx erts_align_attribute(ERTS_CACHE_LINE_SIZE); +#ifdef DEBUG +static int time_sup_initialized = 0; +#endif + +#define ERTS_MONOTONIC_TIME_KILO \ + ((ErtsMonotonicTime) 1000) +#define ERTS_MONOTONIC_TIME_MEGA \ + (ERTS_MONOTONIC_TIME_KILO*ERTS_MONOTONIC_TIME_KILO) +#define ERTS_MONOTONIC_TIME_GIGA \ + (ERTS_MONOTONIC_TIME_MEGA*ERTS_MONOTONIC_TIME_KILO) +#define ERTS_MONOTONIC_TIME_TERA \ + (ERTS_MONOTONIC_TIME_GIGA*ERTS_MONOTONIC_TIME_KILO) static void -init_approx_time(void) +schedule_send_time_offset_changed_notifications(ErtsMonotonicTime new_offset); + +/* + * NOTE! ERTS_MONOTONIC_TIME_START *need* to be a multiple + * of ERTS_MONOTONIC_TIME_UNIT. + */ + +#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT + +#ifdef ARCH_32 +/* + * Want to use a big-num of arity 2 as long as possible (584 years + * in the nano-second time unit case). + */ +#define ERTS_MONOTONIC_TIME_START \ + (((((((ErtsMonotonicTime) 1) << 32)-1) \ + / ERTS_MONOTONIC_TIME_UNIT) \ + * ERTS_MONOTONIC_TIME_UNIT) \ + + ERTS_MONOTONIC_TIME_UNIT) + +#else /* ARCH_64 */ + +#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT <= 1000*1000 + +/* + * Using micro second time unit or lower. Start at zero since + * time will remain an immediate for a very long time anyway + * (18279 years in the micro second case)... + */ +#define ERTS_MONOTONIC_TIME_START ((ErtsMonotonicTime) 0) + +#else /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT > 1000*1000 */ + +/* + * Want to use an immediate as long as possible (36 years in the + * nano-second time unit case). +*/ +#define ERTS_MONOTONIC_TIME_START \ + ((((ErtsMonotonicTime) MIN_SMALL) \ + / ERTS_MONOTONIC_TIME_UNIT) \ + * ERTS_MONOTONIC_TIME_UNIT) + +#endif /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT > 1000*1000 */ + +#endif /* ARCH_64 */ + +#define ERTS_MONOTONIC_OFFSET_NATIVE ERTS_MONOTONIC_TIME_START +#define ERTS_MONOTONIC_OFFSET_NSEC ERTS_MONOTONIC_TO_NSEC__(ERTS_MONOTONIC_TIME_START) +#define ERTS_MONOTONIC_OFFSET_USEC ERTS_MONOTONIC_TO_USEC__(ERTS_MONOTONIC_TIME_START) +#define ERTS_MONOTONIC_OFFSET_MSEC ERTS_MONOTONIC_TO_MSEC__(ERTS_MONOTONIC_TIME_START) +#define ERTS_MONOTONIC_OFFSET_SEC ERTS_MONOTONIC_TO_SEC__(ERTS_MONOTONIC_TIME_START) + +#else /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */ + +/* + * Initialized in erts_init_time_sup()... + */ + +#define ERTS_MONOTONIC_TIME_START (time_sup.r.o.start) +#define ERTS_MONOTONIC_OFFSET_NATIVE (time_sup.r.o.start_offset.native) +#define ERTS_MONOTONIC_OFFSET_NSEC (time_sup.r.o.start_offset.nsec) +#define ERTS_MONOTONIC_OFFSET_USEC (time_sup.r.o.start_offset.usec) +#define ERTS_MONOTONIC_OFFSET_MSEC (time_sup.r.o.start_offset.msec) +#define ERTS_MONOTONIC_OFFSET_SEC (time_sup.r.o.start_offset.sec) + +#endif /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */ + +#define ERTS_MONOTONIC_TO_SYS_TIME_VAL(TVP, MT) \ + do { \ + ErtsMonotonicTime sec__, usec__; \ + sec__ = ERTS_MONOTONIC_TO_SEC((MT)); \ + usec__ = ERTS_MONOTONIC_TO_USEC((MT)) - sec__*1000000; \ + ASSERT(usec__ < 1000000); \ + (TVP)->tv_sec = sec__; \ + (TVP)->tv_usec = usec__; \ + } while (0) + +#define ERTS_MAX_SYSTEM_TIME_DIFF ERTS_MSEC_TO_MONOTONIC(10) +#define ERTS_SYSTEM_TIME_DIFF_EXCEED_LIMIT(ESYSTIME, OSSYSTIME) \ + (((Uint64) (ESYSTIME)) - (((Uint64) (OSSYSTIME)) \ + - ERTS_MAX_SYSTEM_TIME_DIFF) \ + > 2*ERTS_MAX_SYSTEM_TIME_DIFF) + +#define ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF (ERTS_MAX_SYSTEM_TIME_DIFF/2) +#define ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(500) + +struct time_sup_read_only__ { + ErtsMonotonicTime (*get_time)(void); + int correction; + ErtsTimeWarpMode warp_mode; +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + ErtsMonotonicTime moffset; + int os_monotonic_disable; + char *os_monotonic_func; + char *os_monotonic_clock_id; + int os_monotonic_locked; + Uint64 os_monotonic_resolution; +#endif +#if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT + ErtsMonotonicTime start; + struct { + ErtsMonotonicTime native; + ErtsMonotonicTime nsec; + ErtsMonotonicTime usec; + ErtsMonotonicTime msec; + ErtsMonotonicTime sec; + } start_offset; +#endif +}; + +typedef struct { +#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC + ErtsMonotonicTime drift; /* Correction for os monotonic drift */ +#endif + ErtsMonotonicTime error; /* Correction for error between system times */ +} ErtsMonotonicCorrection; + +typedef struct { + ErtsMonotonicTime erl_mtime; + ErtsMonotonicTime os_mtime; + ErtsMonotonicCorrection correction; +} ErtsMonotonicCorrectionInstance; + +#define ERTS_DRIFT_INTERVALS 5 +typedef struct { + struct { + struct { + ErtsMonotonicTime sys; + ErtsMonotonicTime mon; + } diff; + struct { + ErtsMonotonicTime sys; + ErtsMonotonicTime mon; + } time; + } intervals[ERTS_DRIFT_INTERVALS]; + struct { + ErtsMonotonicTime sys; + ErtsMonotonicTime mon; + } acc; + int ix; + int dirty_counter; +} ErtsMonotonicDriftData; + +typedef struct { + ErtsMonotonicCorrectionInstance prev; + ErtsMonotonicCorrectionInstance curr; +#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC + ErtsMonotonicDriftData drift; +#endif + ErtsMonotonicTime last_check; + int short_check_interval; +} ErtsMonotonicCorrectionData; + +struct time_sup_infrequently_changed__ { +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + struct { + erts_smp_rwmtx_t rwmtx; + ErlTimer timer; + ErtsMonotonicCorrectionData cdata; + } parmon; + ErtsMonotonicTime minit; +#endif + int finalized_offset; + SysTimeval inittv; /* Used everywhere, the initial time-of-day */ + ErtsMonotonicTime not_corrected_moffset; + erts_atomic64_t offset; +}; + +struct time_sup_frequently_changed__ { + ErtsMonotonicTime last_not_corrected_time; +}; + +static struct { + union { + struct time_sup_read_only__ o; + char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(struct time_sup_read_only__))]; + } r; + union { + struct time_sup_infrequently_changed__ c; + char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(struct time_sup_infrequently_changed__))]; + } inf; + union { + struct time_sup_frequently_changed__ c; + char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(struct time_sup_frequently_changed__))]; + } f; +} time_sup erts_align_attribute(ERTS_CACHE_LINE_SIZE); + +ErtsTimeSupData erts_time_sup__ erts_align_attribute(ERTS_CACHE_LINE_SIZE); + +/* + * 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) { - erts_smp_atomic_init_nob(&approx.time, 0); + SysTimeval tv; + sys_gettimeofday(&tv); + + return (erts_approx_time_t) tv.tv_sec; } -static ERTS_INLINE erts_approx_time_t -get_approx_time(void) +static ERTS_INLINE void +init_time_offset(ErtsMonotonicTime offset) { - return (erts_approx_time_t) erts_smp_atomic_read_nob(&approx.time); + erts_atomic64_init_nob(&time_sup.inf.c.offset, (erts_aint64_t) offset); } static ERTS_INLINE void -update_approx_time(SysTimeval *tv) +set_time_offset(ErtsMonotonicTime offset) { - 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_atomic64_set_relb(&time_sup.inf.c.offset, (erts_aint64_t) offset); } -/* - * 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) +static ERTS_INLINE ErtsMonotonicTime +get_time_offset(void) { - return get_approx_time(); + return (ErtsMonotonicTime) erts_atomic64_read_acqb(&time_sup.inf.c.offset); } -#ifdef HAVE_GETHRTIME -int erts_disable_tolerant_timeofday; +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + +/* + * Time correction adjustments made due to + * error between Erlang system time and OS + * system time: + * - Large adjustment ~1% + * - Small adjustment ~0.05% + */ +#define ERTS_TCORR_ERR_UNIT 2048 +#define ERTS_TCORR_ERR_LARGE_ADJ 20 +#define ERTS_TCORR_ERR_SMALL_ADJ 1 -static SysHrTime hr_init_time, hr_last_correction_check, - hr_correction, hr_last_time; +#define ERTS_INIT_SHORT_INTERVAL_COUNTER 10 +#define ERTS_LONG_TIME_CORRECTION_CHECK ERTS_SEC_TO_MONOTONIC(60) +#define ERTS_SHORT_TIME_CORRECTION_CHECK ERTS_SEC_TO_MONOTONIC(15) -static void init_tolerant_timeofday(void) +#define ERTS_TIME_DRIFT_MAX_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(100) +#define ERTS_TIME_DRIFT_MIN_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(5) + +static ERTS_INLINE ErtsMonotonicTime +calc_corrected_erl_mtime(ErtsMonotonicTime os_mtime, + ErtsMonotonicCorrectionInstance *cip, + ErtsMonotonicTime *os_mdiff_p) { - /* Should be in sys.c */ -#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) - if (sysconf(_SC_NPROCESSORS_CONF) > 1) { - char b[1024]; - int maj,min,build; - os_flavor(b,1024); - os_version(&maj,&min,&build); - if (!strcmp(b,"sunos") && maj <= 5 && min <= 7) { - erts_disable_tolerant_timeofday = 1; - } - } + ErtsMonotonicTime erl_mtime, diff = os_mtime - cip->os_mtime; + ERTS_TIME_ASSERT(diff >= 0); +#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC + diff += (cip->correction.drift*diff)/ERTS_MONOTONIC_TIME_UNIT; #endif - hr_init_time = sys_gethrtime(); - hr_last_correction_check = hr_last_time = hr_init_time; - hr_correction = 0; + erl_mtime = cip->erl_mtime; + erl_mtime += diff; + erl_mtime += cip->correction.error*(diff/ERTS_TCORR_ERR_UNIT); + if (os_mdiff_p) + *os_mdiff_p = diff; + return erl_mtime; } -static void get_tolerant_timeofday(SysTimeval *tv) +static ErtsMonotonicTime get_corrected_time(void) { - SysHrTime diff_time, curr; + ErtsMonotonicTime os_mtime; + ErtsMonotonicCorrectionData cdata; + ErtsMonotonicCorrectionInstance *cip; + + erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); + + os_mtime = erts_os_monotonic_time(); + + cdata = time_sup.inf.c.parmon.cdata; - if (erts_disable_tolerant_timeofday) { - sys_gettimeofday(tv); - return; + erts_smp_rwmtx_runlock(&time_sup.inf.c.parmon.rwmtx); + + if (os_mtime >= cdata.curr.os_mtime) + cip = &cdata.curr; + else { + if (os_mtime < cdata.prev.os_mtime) + erl_exit(ERTS_ABORT_EXIT, + "OS monotonic time stepped backwards\n"); + cip = &cdata.prev; } - *tv = inittv; - diff_time = ((curr = sys_gethrtime()) + hr_correction - hr_init_time) / 1000; - if (curr < hr_init_time) { - erl_exit(1,"Unexpected behaviour from operating system high " - "resolution timer"); + return calc_corrected_erl_mtime(os_mtime, cip, NULL); +} + +static void +check_time_correction(void *unused) +{ + ErtsMonotonicCorrectionData cdata; + ErtsMonotonicCorrection new_correction; + ErtsMonotonicCorrectionInstance *cip; + ErtsMonotonicTime mdiff, sdiff, os_mtime, erl_mtime, os_stime, erl_stime, time_offset; + Uint timeout; + SysTimeval tod; + int set_new_correction, begin_short_intervals = 0; + + erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); + + ASSERT(time_sup.inf.c.finalized_offset); + + os_mtime = erts_os_monotonic_time(); + sys_gettimeofday(&tod); + + cdata = time_sup.inf.c.parmon.cdata; + + erts_smp_rwmtx_runlock(&time_sup.inf.c.parmon.rwmtx); + + if (os_mtime < cdata.curr.os_mtime) + erl_exit(ERTS_ABORT_EXIT, + "OS monotonic time stepped backwards\n"); + cip = &cdata.curr; + + erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, &mdiff); + time_offset = get_time_offset(); + erl_stime = erl_mtime + time_offset; + + os_stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec); + os_stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec); + + sdiff = erl_stime - os_stime; + + new_correction = cip->correction; + + if (time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE + && (sdiff < -2*ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF + || 2*ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF < sdiff)) { + /* System time diff exeeded limits; change time offset... */ + time_offset -= sdiff; + sdiff = 0; + set_time_offset(time_offset); + schedule_send_time_offset_changed_notifications(time_offset); + begin_short_intervals = 1; + if (cdata.curr.correction.error == 0) + set_new_correction = 0; + else { + set_new_correction = 1; + new_correction.error = 0; + } + } + else if (cdata.curr.correction.error == 0) { + if (sdiff < -ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + set_new_correction = 1; + if (sdiff < -ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; + else + new_correction.error = ERTS_TCORR_ERR_SMALL_ADJ; + } + else if (sdiff > ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + set_new_correction = 1; + if (sdiff > ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; + else + new_correction.error = -ERTS_TCORR_ERR_SMALL_ADJ; + } + else { + set_new_correction = 0; + } + } + else if (cdata.curr.correction.error > 0) { + if (sdiff < 0) { + if (cdata.curr.correction.error == ERTS_TCORR_ERR_LARGE_ADJ + || -ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF <= sdiff) + set_new_correction = 0; + else { + new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; + set_new_correction = 1; + } + } + else if (sdiff > ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + set_new_correction = 1; + if (sdiff > ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; + else + new_correction.error = -ERTS_TCORR_ERR_SMALL_ADJ; + } + else { + set_new_correction = 1; + new_correction.error = 0; + } + } + else /* if (cdata.curr.correction.error < 0) */ { + if (0 < sdiff) { + if (cdata.curr.correction.error == -ERTS_TCORR_ERR_LARGE_ADJ + || sdiff <= ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + set_new_correction = 0; + else { + new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; + set_new_correction = 1; + } + set_new_correction = 0; + } + else if (sdiff < -ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + set_new_correction = 1; + if (sdiff < -ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; + else + new_correction.error = ERTS_TCORR_ERR_SMALL_ADJ; + } + else { + set_new_correction = 1; + new_correction.error = 0; + } } - if ((curr - hr_last_correction_check) / 1000 > 1000000) { - /* Check the correction need */ - SysHrTime tv_diff, diffdiff; - SysTimeval tmp; - int done = 0; - - sys_gettimeofday(&tmp); - tv_diff = ((SysHrTime) tmp.tv_sec) * 1000000 + tmp.tv_usec; - tv_diff -= ((SysHrTime) inittv.tv_sec) * 1000000 + inittv.tv_usec; - diffdiff = diff_time - tv_diff; - if (diffdiff > 10000) { - SysHrTime corr = (curr - hr_last_time) / 100; - if (corr / 1000 >= diffdiff) { - ++done; - hr_correction -= ((SysHrTime)diffdiff) * 1000; - } else { - hr_correction -= corr; +#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC + { + ErtsMonotonicDriftData *ddp = &time_sup.inf.c.parmon.cdata.drift; + int ix = ddp->ix; + ErtsMonotonicTime mtime_diff, old_os_mtime; + + old_os_mtime = ddp->intervals[ix].time.mon; + mtime_diff = os_mtime - old_os_mtime; + + if (mtime_diff >= ERTS_SEC_TO_MONOTONIC(10)) { + ErtsMonotonicTime drift_adj, drift_adj_diff, old_os_stime, + stime_diff, mtime_acc, stime_acc, avg_drift_adj; + + old_os_stime = ddp->intervals[ix].time.sys; + + mtime_acc = ddp->acc.mon; + stime_acc = ddp->acc.sys; + + avg_drift_adj = ((stime_acc - mtime_acc)*ERTS_MONOTONIC_TIME_UNIT) / mtime_acc; + + mtime_diff = os_mtime - old_os_mtime; + stime_diff = os_stime - old_os_stime; + drift_adj = ((stime_diff - mtime_diff)*ERTS_MONOTONIC_TIME_UNIT) / mtime_diff; + + ix++; + if (ix >= ERTS_DRIFT_INTERVALS) + ix = 0; + mtime_acc -= ddp->intervals[ix].diff.mon; + mtime_acc += mtime_diff; + stime_acc -= ddp->intervals[ix].diff.sys; + stime_acc += stime_diff; + + ddp->intervals[ix].diff.mon = mtime_diff; + ddp->intervals[ix].diff.sys = stime_diff; + ddp->intervals[ix].time.mon = os_mtime; + ddp->intervals[ix].time.sys = os_stime; + + ddp->ix = ix; + ddp->acc.mon = mtime_acc; + ddp->acc.sys = stime_acc; + + /* + * If calculated drift adjustment is if off by more than 20% from the + * average drift we interpret this as a discontinous leap in system + * time and ignore it. If it actually is a change in drift we will + * later detect this when the average drift change. + */ + drift_adj_diff = avg_drift_adj - drift_adj; + if (drift_adj_diff < -ERTS_TIME_DRIFT_MAX_ADJ_DIFF + || ERTS_TIME_DRIFT_MAX_ADJ_DIFF < drift_adj_diff) { + ddp->dirty_counter = ERTS_DRIFT_INTERVALS; + begin_short_intervals = 1; } - diff_time = (curr + hr_correction - hr_init_time) / 1000; - } else if (diffdiff < -10000) { - SysHrTime corr = (curr - hr_last_time) / 100; - if (corr / 1000 >= -diffdiff) { - ++done; - hr_correction -= ((SysHrTime)diffdiff) * 1000; - } else { - hr_correction += corr; + else { + if (ddp->dirty_counter <= 0) { + drift_adj = ((stime_acc - mtime_acc)*ERTS_MONOTONIC_TIME_UNIT) / mtime_acc; + } + if (ddp->dirty_counter >= 0) { + if (ddp->dirty_counter == 0) { + /* Force set new drift correction... */ + set_new_correction = 1; + } + ddp->dirty_counter--; + } + drift_adj_diff = drift_adj - new_correction.drift; + if (drift_adj_diff) { + if (drift_adj_diff > ERTS_TIME_DRIFT_MAX_ADJ_DIFF) + drift_adj_diff = ERTS_TIME_DRIFT_MAX_ADJ_DIFF; + else if (drift_adj_diff < -ERTS_TIME_DRIFT_MAX_ADJ_DIFF) + drift_adj_diff = -ERTS_TIME_DRIFT_MAX_ADJ_DIFF; + new_correction.drift += drift_adj_diff; + + if (drift_adj_diff < -ERTS_TIME_DRIFT_MIN_ADJ_DIFF + || ERTS_TIME_DRIFT_MIN_ADJ_DIFF < drift_adj_diff) { + set_new_correction = 1; + } + } } - diff_time = (curr + hr_correction - hr_init_time) / 1000; - } else { - ++done; } - if (done) { - hr_last_correction_check = curr; + } +#endif + + begin_short_intervals |= set_new_correction; + + if (begin_short_intervals) { + time_sup.inf.c.parmon.cdata.short_check_interval + = ERTS_INIT_SHORT_INTERVAL_COUNTER; + } + else if ((os_mtime - time_sup.inf.c.parmon.cdata.last_check + >= ERTS_SHORT_TIME_CORRECTION_CHECK - ERTS_MONOTONIC_TIME_UNIT) + && time_sup.inf.c.parmon.cdata.short_check_interval > 0) { + time_sup.inf.c.parmon.cdata.short_check_interval--; + } + time_sup.inf.c.parmon.cdata.last_check = os_mtime; + + if (new_correction.error == 0) + timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_LONG_TIME_CORRECTION_CHECK); + else { + ErtsMonotonicTime ecorr = new_correction.error; + if (sdiff < 0) + sdiff = -1*sdiff; + if (ecorr < 0) + ecorr = -1*ecorr; + if (sdiff > ecorr*(ERTS_LONG_TIME_CORRECTION_CHECK/ERTS_TCORR_ERR_UNIT)) + timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_LONG_TIME_CORRECTION_CHECK); + else { + timeout = ERTS_MONOTONIC_TO_MSEC((ERTS_TCORR_ERR_UNIT*sdiff)/ecorr); + if (timeout < 10) + timeout = 10; } } - tv->tv_sec += (int) (diff_time / ((SysHrTime) 1000000)); - tv->tv_usec += (int) (diff_time % ((SysHrTime) 1000000)); - if (tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec += 1; + + if (timeout > ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK) + && time_sup.inf.c.parmon.cdata.short_check_interval) { + timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK); } - hr_last_time = curr; -} + + if (set_new_correction) { + erts_smp_rwmtx_rwlock(&time_sup.inf.c.parmon.rwmtx); -#define correction (hr_correction/1000000) + os_mtime = erts_os_monotonic_time(); -#else /* !HAVE_GETHRTIME */ -#if !defined(CORRECT_USING_TIMES) -#define init_tolerant_timeofday() -#define get_tolerant_timeofday(tvp) sys_gettimeofday(tvp) -#else + /* Save previous correction instance */ + time_sup.inf.c.parmon.cdata.prev = *cip; -typedef Sint64 Milli; - -static clock_t init_ct; -static Sint64 ct_wrap; -static Milli init_tv_m; -static Milli correction_supress; -static Milli last_ct_diff; -static Milli last_cc; -static clock_t last_ct; - -/* sys_times() might need to be wrapped and the values shifted (right) - a bit to cope with newer linux (2.5.*) kernels, this has to be taken care - of dynamically to start with, a special version that uses - the times() return value as a high resolution timer can be made - to fully utilize the faster ticks, like on windows, but for now, we'll - settle with this silly workaround */ -#ifdef ERTS_WRAP_SYS_TIMES -#define KERNEL_TICKS() (sys_times_wrap() & \ - ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1)) -#else -SysTimes dummy_tms; + /* + * Current correction instance begin when + * OS monotonic time has increased one unit. + */ + os_mtime++; -#define KERNEL_TICKS() (sys_times(&dummy_tms) & \ - ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1)) + /* + * Erlang monotonic time corresponding to + * next OS monotonic time using previous + * correction. + */ + erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, NULL); -#endif + /* + * Save new current correction instance. + */ + time_sup.inf.c.parmon.cdata.curr.erl_mtime = erl_mtime; + time_sup.inf.c.parmon.cdata.curr.os_mtime = os_mtime; + time_sup.inf.c.parmon.cdata.curr.correction = new_correction; -static void init_tolerant_timeofday(void) -{ - last_ct = init_ct = KERNEL_TICKS(); - last_cc = 0; - init_tv_m = (((Milli) inittv.tv_sec) * 1000) + - (inittv.tv_usec / 1000); - ct_wrap = 0; - correction_supress = 0; + erts_smp_rwmtx_rwunlock(&time_sup.inf.c.parmon.rwmtx); + } + + erts_set_timer(&time_sup.inf.c.parmon.timer, + check_time_correction, + NULL, + NULL, + timeout); } +#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC -static void get_tolerant_timeofday(SysTimeval *tvp) +static void +init_check_time_correction(void *unused) { - clock_t current_ct; - SysTimeval current_tv; - Milli ct_diff; - Milli tv_diff; - Milli current_correction; - Milli act_correction; /* long shown to be too small */ - Milli max_adjust; - - if (erts_disable_tolerant_timeofday) { - sys_gettimeofday(tvp); - return; + ErtsMonotonicDriftData *ddp; + ErtsMonotonicTime old_mtime, old_stime, mtime, stime, mtime_diff, stime_diff; + int ix; + SysTimeval tod; + + ddp = &time_sup.inf.c.parmon.cdata.drift; + ix = ddp->ix; + old_mtime = ddp->intervals[0].time.mon; + old_stime = ddp->intervals[0].time.sys; + + mtime = erts_os_monotonic_time(); + sys_gettimeofday(&tod); + + stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec); + stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec); + + mtime_diff = mtime - old_mtime; + stime_diff = stime - old_stime; + if (100*stime_diff < 80*mtime_diff || 120*mtime_diff < 100*stime_diff ) { + /* Had a system time leap... pretend no drift... */ + stime_diff = mtime_diff; + } + + /* + * We use old time values in order to trigger + * a drift adjustment, and repeat this interval + * in all slots... + */ + for (ix = 0; ix < ERTS_DRIFT_INTERVALS; ix++) { + ddp->intervals[ix].diff.mon = mtime_diff; + ddp->intervals[ix].diff.sys = stime_diff; + ddp->intervals[ix].time.mon = old_mtime; + ddp->intervals[ix].time.sys = old_stime; } -#ifdef ERTS_WRAP_SYS_TIMES -#define TICK_MS (1000 / SYS_CLK_TCK_WRAP) + ddp->acc.sys = stime_diff*ERTS_DRIFT_INTERVALS; + ddp->acc.mon = mtime_diff*ERTS_DRIFT_INTERVALS; + ddp->ix = 0; + ddp->dirty_counter = ERTS_DRIFT_INTERVALS; + + check_time_correction(NULL); +} + +#endif + +static ErtsMonotonicTime +finalize_corrected_time_offset(SysTimeval *todp) +{ + ErtsMonotonicTime os_mtime; + ErtsMonotonicCorrectionData cdata; + ErtsMonotonicCorrectionInstance *cip; + + erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); + + os_mtime = erts_os_monotonic_time(); + sys_gettimeofday(todp); + + cdata = time_sup.inf.c.parmon.cdata; + + erts_smp_rwmtx_runlock(&time_sup.inf.c.parmon.rwmtx); + + if (os_mtime < cdata.curr.os_mtime) + erl_exit(ERTS_ABORT_EXIT, + "OS monotonic time stepped backwards\n"); + cip = &cdata.curr; + + return calc_corrected_erl_mtime(os_mtime, cip, NULL); +} + +static void +late_init_time_correction(void) +{ + if (time_sup.inf.c.finalized_offset) { + erts_set_timer(&time_sup.inf.c.parmon.timer, +#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC + init_check_time_correction, #else -#define TICK_MS (1000 / SYS_CLK_TCK) + check_time_correction, #endif - current_ct = KERNEL_TICKS(); - sys_gettimeofday(¤t_tv); - - /* I dont know if uptime can move some units backwards - on some systems, but I allow for small backward - jumps to avoid such problems if they exist...*/ - if (last_ct > 100 && current_ct < (last_ct - 100)) { - ct_wrap += ((Sint64) 1) << ((sizeof(clock_t) * 8) - 1); + NULL, + NULL, + ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK)); } - last_ct = current_ct; - ct_diff = ((ct_wrap + current_ct) - init_ct) * TICK_MS; +} - /* - * We will adjust the time in milliseconds and we allow for 1% - * adjustments, but if this function is called more often then every 100 - * millisecond (which is obviously possible), we will never adjust, so - * we accumulate small times by setting last_ct_diff iff max_adjust > 0 - */ - if ((max_adjust = (ct_diff - last_ct_diff)/100) > 0) - last_ct_diff = ct_diff; +#endif /* ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT */ - tv_diff = ((((Milli) current_tv.tv_sec) * 1000) + - (current_tv.tv_usec / 1000)) - init_tv_m; +static ErtsMonotonicTime get_not_corrected_time(void) +{ + SysTimeval tmp_tv; + ErtsMonotonicTime stime, mtime; - current_correction = ((ct_diff - tv_diff) / TICK_MS) * TICK_MS; /* trunc */ + erts_smp_mtx_lock(&erts_get_time_mtx); - /* - * We allow the current_correction value to wobble a little, as it - * suffers from the low resolution of the kernel ticks. - * if it hasn't changed more than one tick in either direction, - * we will keep the old value. - */ - if ((last_cc > current_correction + TICK_MS) || - (last_cc < current_correction - TICK_MS)) { - last_cc = current_correction; - } else { - current_correction = last_cc; - } - - /* - * As time goes, we try to get the actual correction to 0, - * that is, make erlangs time correspond to the systems dito. - * The act correction is what we seem to need (current_correction) - * minus the correction suppression. The correction supression - * will change slowly (max 1% of elapsed time) but in millisecond steps. - */ - act_correction = current_correction - correction_supress; - if (max_adjust > 0) { - /* - * Here we slowly adjust erlangs time to correspond with the - * system time by changing the correction_supress variable. - * It can change max_adjust milliseconds which is 1% of elapsed time - */ - if (act_correction > 0) { - if (current_correction - correction_supress > max_adjust) { - correction_supress += max_adjust; - } else { - correction_supress = current_correction; - } - act_correction = current_correction - correction_supress; - } else if (act_correction < 0) { - if (correction_supress - current_correction > max_adjust) { - correction_supress -= max_adjust; - } else { - correction_supress = current_correction; + sys_gettimeofday(&tmp_tv); + + stime = ERTS_SEC_TO_MONOTONIC(tmp_tv.tv_sec); + stime += ERTS_USEC_TO_MONOTONIC(tmp_tv.tv_usec); + + mtime = stime - time_sup.inf.c.not_corrected_moffset; + + if (mtime >= time_sup.f.c.last_not_corrected_time) + time_sup.f.c.last_not_corrected_time = mtime; + else { + mtime = time_sup.f.c.last_not_corrected_time; + + if (time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE) { + ErtsMonotonicTime new_offset = stime - mtime; + new_offset = ERTS_MONOTONIC_TO_USEC(new_offset); + new_offset = ERTS_USEC_TO_MONOTONIC(new_offset); + if (time_sup.inf.c.not_corrected_moffset != new_offset) { + time_sup.inf.c.not_corrected_moffset = new_offset; + set_time_offset(new_offset); + schedule_send_time_offset_changed_notifications(new_offset); } - act_correction = current_correction - correction_supress; } + } - /* - * The actual correction will correct the timeval so that system - * time warps gets smothed down. - */ - current_tv.tv_sec += act_correction / 1000; - current_tv.tv_usec += (act_correction % 1000) * 1000; - - if (current_tv.tv_usec >= 1000000) { - ++current_tv.tv_sec ; - current_tv.tv_usec -= 1000000; - } else if (current_tv.tv_usec < 0) { - --current_tv.tv_sec; - current_tv.tv_usec += 1000000; - } - *tvp = current_tv; -#undef TICK_MS + + ASSERT(stime == mtime + time_sup.inf.c.not_corrected_moffset); + + erts_smp_mtx_unlock(&erts_get_time_mtx); + + return mtime; } -#endif /* CORRECT_USING_TIMES */ -#endif /* !HAVE_GETHRTIME */ +int erts_check_time_adj_support(int time_correction, + ErtsTimeWarpMode time_warp_mode) +{ + if (!time_correction) + return 1; -/* -** Why this? Well, most platforms have a constant clock resolution of 1, -** we dont want the deliver_time/time_remaining routines to waste -** time dividing and multiplying by/with a variable that's always one. -** so the return value of sys_init_time is ignored on those platforms. -*/ - -#ifndef SYS_CLOCK_RESOLUTION -static int clock_resolution; -#define CLOCK_RESOLUTION clock_resolution + /* User wants time correction */ + +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + return !time_sup.r.o.os_monotonic_disable; #else -#define CLOCK_RESOLUTION SYS_CLOCK_RESOLUTION + return 0; #endif +} -/* -** The clock resolution should really be the resolution of the -** time function in use, which on most platforms -** 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 -** instead of something like select. -*/ - -static SysTimeval last_delivered; - -static void init_erts_deliver_time(const SysTimeval *inittv) +int +erts_has_time_correction(void) { - /* We set the initial values for deliver_time here */ - last_delivered = *inittv; - last_delivered.tv_usec = 1000 * (last_delivered.tv_usec / 1000); - /* ms resolution */ + return time_sup.r.o.correction; } -static void do_erts_deliver_time(const SysTimeval *current) +void +erts_early_init_time_sup(void) { - SysTimeval cur_time; - erts_time_t elapsed; - - /* calculate and deliver appropriate number of ticks */ - cur_time = *current; - cur_time.tv_usec = 1000 * (cur_time.tv_usec / 1000); /* ms resolution */ - elapsed = (1000 * (cur_time.tv_sec - last_delivered.tv_sec) + - (cur_time.tv_usec - last_delivered.tv_usec) / 1000) / - CLOCK_RESOLUTION; + ErtsSysInitTimeResult sys_init_time_res + = ERTS_SYS_INIT_TIME_RESULT_INITER; - /* Sometimes the time jump backwards, - resulting in a negative elapsed time. We compensate for - this by simply pretend as if the time stood still. :) */ + sys_init_time(&sys_init_time_res); - if (elapsed > 0) { + erts_time_sup__.r.o.monotonic_time_unit + = sys_init_time_res.os_monotonic_time_unit; - ASSERT(elapsed < ((erts_time_t) ERTS_SHORT_TIME_T_MAX)); +#ifndef SYS_CLOCK_RESOLUTION + erts_time_sup__.r.o.clktck_resolution + = sys_init_time_res.sys_clock_resolution; + erts_time_sup__.r.o.clktck_resolution *= 1000; +#endif - erts_do_time_add((erts_short_time_t) elapsed); - last_delivered = cur_time; - } +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + time_sup.r.o.os_monotonic_disable + = !sys_init_time_res.have_os_monotonic; + time_sup.r.o.os_monotonic_func + = sys_init_time_res.os_monotonic_info.func; + time_sup.r.o.os_monotonic_clock_id + = sys_init_time_res.os_monotonic_info.clock_id; + time_sup.r.o.os_monotonic_locked + = sys_init_time_res.os_monotonic_info.locked_use; + time_sup.r.o.os_monotonic_resolution + = sys_init_time_res.os_monotonic_info.resolution; +#endif } int -erts_init_time_sup(void) +erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) { +#if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT + ErtsMonotonicTime abs_start; +#endif + + ASSERT(ERTS_MONOTONIC_TIME_MIN < ERTS_MONOTONIC_TIME_MAX); + erts_smp_mtx_init(&erts_timeofday_mtx, "timeofday"); + erts_smp_mtx_init(&erts_get_time_mtx, "get_time"); + + time_sup.r.o.correction = time_correction; + time_sup.r.o.warp_mode = time_warp_mode; + + if (time_warp_mode == ERTS_SINGLE_TIME_WARP_MODE) + time_sup.inf.c.finalized_offset = 0; + else + time_sup.inf.c.finalized_offset = ~0; + +#if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT + +#ifdef ARCH_32 + time_sup.r.o.start = ((((ErtsMonotonicTime) 1) << 32)-1); + time_sup.r.o.start /= ERTS_MONOTONIC_TIME_UNIT; + time_sup.r.o.start *= ERTS_MONOTONIC_TIME_UNIT; + time_sup.r.o.start += ERTS_MONOTONIC_TIME_UNIT; + abs_start = time_sup.r.o.start; +#else /* ARCH_64 */ + if (ERTS_MONOTONIC_TIME_UNIT <= 1000*1000) + abs_start = time_sup.r.o.start = 0; + else { + time_sup.r.o.start = ((ErtsMonotonicTime) MIN_SMALL); + time_sup.r.o.start /= ERTS_MONOTONIC_TIME_UNIT; + time_sup.r.o.start *= ERTS_MONOTONIC_TIME_UNIT; + abs_start = -1*time_sup.r.o.start; + } +#endif - init_approx_time(); + time_sup.r.o.start_offset.native = time_sup.r.o.start; + time_sup.r.o.start_offset.nsec = (ErtsMonotonicTime) + erts_time_unit_conversion((Uint64) abs_start, + (Uint32) ERTS_MONOTONIC_TIME_UNIT, + (Uint32) 1000*1000*1000); + time_sup.r.o.start_offset.usec = (ErtsMonotonicTime) + erts_time_unit_conversion((Uint64) abs_start, + (Uint32) ERTS_MONOTONIC_TIME_UNIT, + (Uint32) 1000*1000); + time_sup.r.o.start_offset.msec = (ErtsMonotonicTime) + erts_time_unit_conversion((Uint64) abs_start, + (Uint32) ERTS_MONOTONIC_TIME_UNIT, + (Uint32) 1000); + time_sup.r.o.start_offset.sec = (ErtsMonotonicTime) + erts_time_unit_conversion((Uint64) abs_start, + (Uint32) ERTS_MONOTONIC_TIME_UNIT, + (Uint32) 1); + if (time_sup.r.o.start < 0) { + time_sup.r.o.start_offset.nsec *= -1; + time_sup.r.o.start_offset.usec *= -1; + time_sup.r.o.start_offset.msec *= -1; + time_sup.r.o.start_offset.sec *= -1; + } - last_emu_time.tv_sec = 0; - last_emu_time.tv_usec = 0; +#endif -#ifndef SYS_CLOCK_RESOLUTION - clock_resolution = sys_init_time(); + if (ERTS_MONOTONIC_TIME_UNIT < ERTS_CLKTCK_RESOLUTION) + ERTS_INTERNAL_ERROR("Too small monotonic time time unit"); + +#ifndef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + time_sup.r.o.correction = 0; #else - (void) sys_init_time(); -#endif - sys_gettimeofday(&inittv); + if (time_sup.r.o.os_monotonic_disable) + time_sup.r.o.correction = 0; + + if (time_sup.r.o.correction) { + ErtsMonotonicCorrectionData *cdatap; + erts_smp_rwmtx_opt_t rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; + ErtsMonotonicTime offset; + time_sup.inf.c.minit = erts_os_monotonic_time(); + sys_gettimeofday(&time_sup.inf.c.inittv); + time_sup.r.o.moffset = -1*time_sup.inf.c.minit; + offset = ERTS_SEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_sec); + offset += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); + init_time_offset(offset); + + rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ; + rwmtx_opts.lived = ERTS_SMP_RWMTX_LONG_LIVED; + + erts_smp_rwmtx_init_opt(&time_sup.inf.c.parmon.rwmtx, + &rwmtx_opts, "get_corrected_time"); + + cdatap = &time_sup.inf.c.parmon.cdata; -#ifdef HAVE_GETHRTIME - sys_init_hrtime(); +#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC + cdatap->drift.intervals[0].time.sys + = ERTS_SEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_sec); + cdatap->drift.intervals[0].time.sys + += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); + cdatap->drift.intervals[0].time.mon = time_sup.inf.c.minit; + cdatap->curr.correction.drift = 0; +#endif + cdatap->curr.correction.error = 0; + cdatap->curr.erl_mtime = 0; + cdatap->curr.os_mtime = time_sup.inf.c.minit; + cdatap->last_check = time_sup.inf.c.minit; + cdatap->short_check_interval = ERTS_INIT_SHORT_INTERVAL_COUNTER; + cdatap->prev = cdatap->curr; + + time_sup.r.o.get_time = get_corrected_time; + } + else #endif - init_tolerant_timeofday(); + { + ErtsMonotonicTime stime, offset; + time_sup.r.o.get_time = get_not_corrected_time; + stime = ERTS_SEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_sec); + stime += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); + offset = stime; + time_sup.inf.c.not_corrected_moffset = offset; + init_time_offset(offset); + time_sup.f.c.last_not_corrected_time = 0; + } + + prev_wall_clock_elapsed = 0; - init_erts_deliver_time(&inittv); - gtv = inittv; - then.tv_sec = then.tv_usec = 0; + previous_now = ERTS_MONOTONIC_TO_USEC(get_time_offset()); - erts_deliver_time(); +#ifdef DEBUG + time_sup_initialized = 1; +#endif - return CLOCK_RESOLUTION; + return ERTS_CLKTCK_RESOLUTION/1000; } + +void +erts_late_init_time_sup(void) +{ +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + /* Timer wheel must be initialized */ + if (time_sup.r.o.get_time == get_corrected_time) + late_init_time_correction(); +#endif +} + +ErtsTimeWarpMode erts_time_warp_mode(void) +{ + return time_sup.r.o.warp_mode; +} + +ErtsTimeOffsetState erts_time_offset_state(void) +{ + switch (time_sup.r.o.warp_mode) { + case ERTS_NO_TIME_WARP_MODE: + return ERTS_TIME_OFFSET_FINAL; + case ERTS_SINGLE_TIME_WARP_MODE: + if (time_sup.inf.c.finalized_offset) + return ERTS_TIME_OFFSET_FINAL; + return ERTS_TIME_OFFSET_PRELIMINARY; + case ERTS_MULTI_TIME_WARP_MODE: + return ERTS_TIME_OFFSET_VOLATILE; + default: + ERTS_INTERNAL_ERROR("Invalid time warp mode"); + return ERTS_TIME_OFFSET_VOLATILE; + } +} + +/* + * erts_finalize_time_offset() will only change time offset + * the first time it is called when the emulator has been + * started in "single time warp" mode. Returns previous + * state: + * * ERTS_TIME_OFFSET_PRELIMINARY - Finalization performed + * * ERTS_TIME_OFFSET_FINAL - Already finialized; nothing changed + * * ERTS_TIME_OFFSET_VOLATILE - Not supported, either in + * * no correction mode (or multi time warp mode; not yet implemented). + */ + +ErtsTimeOffsetState +erts_finalize_time_offset(void) +{ + switch (time_sup.r.o.warp_mode) { + case ERTS_NO_TIME_WARP_MODE: + return ERTS_TIME_OFFSET_FINAL; + case ERTS_MULTI_TIME_WARP_MODE: + return ERTS_TIME_OFFSET_VOLATILE; + case ERTS_SINGLE_TIME_WARP_MODE: { + ErtsTimeOffsetState res = ERTS_TIME_OFFSET_FINAL; + + erts_smp_mtx_lock(&erts_get_time_mtx); + + if (!time_sup.inf.c.finalized_offset) { + ErtsMonotonicTime mtime, new_offset; + SysTimeval tv; + +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + if (!time_sup.r.o.correction) +#endif + { + ErtsMonotonicTime stime; + sys_gettimeofday(&tv); + + stime = ERTS_SEC_TO_MONOTONIC(tv.tv_sec); + stime += ERTS_USEC_TO_MONOTONIC(tv.tv_usec); + + mtime = stime - time_sup.inf.c.not_corrected_moffset; + + if (mtime >= time_sup.f.c.last_not_corrected_time) { + time_sup.f.c.last_not_corrected_time = mtime; + new_offset = time_sup.inf.c.not_corrected_moffset; + } + else { + mtime = time_sup.f.c.last_not_corrected_time; + + ASSERT(time_sup.inf.c.not_corrected_moffset != stime - mtime); + new_offset = stime - mtime; + time_sup.inf.c.not_corrected_moffset = new_offset; + } + + } +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + else { + mtime = finalize_corrected_time_offset(&tv); + new_offset = ERTS_SEC_TO_MONOTONIC(tv.tv_sec); + new_offset += ERTS_USEC_TO_MONOTONIC(tv.tv_usec); + new_offset -= mtime; + + } +#endif + new_offset = ERTS_MONOTONIC_TO_USEC(new_offset); + new_offset = ERTS_USEC_TO_MONOTONIC(new_offset); + + set_time_offset(new_offset); + schedule_send_time_offset_changed_notifications(new_offset); + + time_sup.inf.c.finalized_offset = ~0; + res = ERTS_TIME_OFFSET_PRELIMINARY; + } + + erts_smp_mtx_unlock(&erts_get_time_mtx); + +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + if (res == ERTS_TIME_OFFSET_PRELIMINARY + && time_sup.r.o.get_time == get_corrected_time) { + late_init_time_correction(); + } +#endif + + return res; + } + default: + ERTS_INTERNAL_ERROR("Invalid time warp mode"); + return ERTS_TIME_OFFSET_VOLATILE; + } +} + /* info functions */ void @@ -498,23 +1090,16 @@ elapsed_time_both(UWord *ms_user, UWord *ms_sys, void wall_clock_elapsed_time_both(UWord *ms_total, UWord *ms_diff) { - UWord prev_total; - SysTimeval tv; + ErtsMonotonicTime now, elapsed; erts_smp_mtx_lock(&erts_timeofday_mtx); - get_tolerant_timeofday(&tv); + now = time_sup.r.o.get_time(); - *ms_total = 1000 * (tv.tv_sec - inittv.tv_sec) + - (tv.tv_usec - inittv.tv_usec) / 1000; - - prev_total = 1000 * (gtv.tv_sec - inittv.tv_sec) + - (gtv.tv_usec - inittv.tv_usec) / 1000; - *ms_diff = *ms_total - prev_total; - gtv = tv; - - /* must sync the machine's idea of time here */ - do_erts_deliver_time(&tv); + elapsed = ERTS_MONOTONIC_TO_MSEC(now); + *ms_total = (UWord) elapsed; + *ms_diff = (UWord) (elapsed - prev_wall_clock_elapsed); + prev_wall_clock_elapsed = elapsed; erts_smp_mtx_unlock(&erts_timeofday_mtx); } @@ -890,38 +1475,41 @@ univ_to_local(Sint *year, Sint *month, Sint *day, return 0; } - /* get a timestamp */ void get_now(Uint* megasec, Uint* sec, Uint* microsec) { - SysTimeval now; + ErtsMonotonicTime now_megasec, now_sec, now, mtime, time_offset; + mtime = time_sup.r.o.get_time(); + time_offset = get_time_offset(); + now = ERTS_MONOTONIC_TO_USEC(mtime + time_offset); + erts_smp_mtx_lock(&erts_timeofday_mtx); - - get_tolerant_timeofday(&now); - do_erts_deliver_time(&now); - - /* Make sure time is later than last */ - if (then.tv_sec > now.tv_sec || - (then.tv_sec == now.tv_sec && then.tv_usec >= now.tv_usec)) { - now = then; - now.tv_usec++; - } - /* Check for carry from above + general reasonability */ - if (now.tv_usec >= 1000000) { - now.tv_usec = 0; - now.tv_sec++; - } - then = now; + + /* Make sure now time is later than last time */ + if (now <= previous_now) + now = previous_now + 1; + + previous_now = now; erts_smp_mtx_unlock(&erts_timeofday_mtx); - - *megasec = (Uint) (now.tv_sec / 1000000); - *sec = (Uint) (now.tv_sec % 1000000); - *microsec = (Uint) (now.tv_usec); - update_approx_time(&now); + now_megasec = now / ERTS_MONOTONIC_TIME_TERA; + now_sec = now / ERTS_MONOTONIC_TIME_MEGA; + *megasec = (Uint) now_megasec; + *sec = (Uint) (now_sec - now_megasec*ERTS_MONOTONIC_TIME_MEGA); + *microsec = (Uint) (now - now_sec*ERTS_MONOTONIC_TIME_MEGA); + + ASSERT(((ErtsMonotonicTime) *megasec)*ERTS_MONOTONIC_TIME_TERA + + ((ErtsMonotonicTime) *sec)*ERTS_MONOTONIC_TIME_MEGA + + ((ErtsMonotonicTime) *microsec) == now); +} + +ErtsMonotonicTime +erts_get_monotonic_time(void) +{ + return time_sup.r.o.get_time(); } void @@ -934,102 +1522,465 @@ 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); } +#ifdef HAVE_ERTS_NOW_CPU +void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec) { + SysCpuTime t; + SysTimespec tp; -/* deliver elapsed *ticks* to the machine - takes a pointer - to a struct timeval representing current time (to save - a gettimeofday() where possible) or NULL */ + sys_get_proc_cputime(t, tp); + *microsec = (Uint)(tp.tv_nsec / 1000); + t = (tp.tv_sec / 1000000); + *megasec = (Uint)(t % 1000000); + *sec = (Uint)(tp.tv_sec % 1000000); +} +#endif -void erts_deliver_time(void) { - SysTimeval now; - - erts_smp_mtx_lock(&erts_timeofday_mtx); - - get_tolerant_timeofday(&now); - do_erts_deliver_time(&now); - - erts_smp_mtx_unlock(&erts_timeofday_mtx); +#include "big.h" - update_approx_time(&now); +void +erts_monitor_time_offset(Eterm id, Eterm ref) +{ + erts_smp_mtx_lock(&erts_get_time_mtx); + erts_add_monitor(&time_offset_monitors, MON_TIME_OFFSET, ref, id, NIL); + no_time_offset_monitors++; + erts_smp_mtx_unlock(&erts_get_time_mtx); +} + +int +erts_demonitor_time_offset(Eterm ref) +{ + int res; + ErtsMonitor *mon; + ASSERT(is_internal_ref(ref)); + erts_smp_mtx_lock(&erts_get_time_mtx); + mon = erts_remove_monitor(&time_offset_monitors, ref); + if (!mon) + res = 0; + else { + ASSERT(no_time_offset_monitors > 0); + no_time_offset_monitors--; + res = 1; + } + erts_smp_mtx_unlock(&erts_get_time_mtx); + if (res) + erts_destroy_monitor(mon); + return res; } -/* get *real* time (not ticks) remaining until next timeout - if there - isn't one, give a "long" time, that is guaranteed - to not cause overflow when we report elapsed time later on */ +typedef struct { + Eterm pid; + Eterm ref; + Eterm heap[REF_THING_SIZE]; +} ErtsTimeOffsetMonitorInfo; + +typedef struct { + Uint ix; + ErtsTimeOffsetMonitorInfo *to_mon_info; +} ErtsTimeOffsetMonitorContext; -void erts_time_remaining(SysTimeval *rem_time) +static void +save_time_offset_monitor(ErtsMonitor *mon, void *vcntxt) { - erts_time_t ticks; - SysTimeval cur_time; - erts_time_t elapsed; - - /* erts_next_time() returns no of ticks to next timeout or -1 if none */ - - ticks = (erts_time_t) erts_next_time(); - if (ticks == (erts_time_t) -1) { - /* timer queue empty */ - /* this will cause at most 100000000 ticks */ - rem_time->tv_sec = 100000; - rem_time->tv_usec = 0; - } else { - /* next timeout after ticks ticks */ - ticks *= CLOCK_RESOLUTION; - - erts_smp_mtx_lock(&erts_timeofday_mtx); - - get_tolerant_timeofday(&cur_time); - cur_time.tv_usec = 1000 * - (cur_time.tv_usec / 1000);/* ms resolution*/ - elapsed = 1000 * (cur_time.tv_sec - last_delivered.tv_sec) + - (cur_time.tv_usec - last_delivered.tv_usec) / 1000; - - erts_smp_mtx_unlock(&erts_timeofday_mtx); + ErtsTimeOffsetMonitorContext *cntxt; + Eterm *from_hp, *to_hp; + Uint mix; + int hix; + + cntxt = (ErtsTimeOffsetMonitorContext *) vcntxt; + mix = (cntxt->ix)++; + cntxt->to_mon_info[mix].pid = mon->pid; + to_hp = &cntxt->to_mon_info[mix].heap[0]; + + ASSERT(is_internal_ref(mon->ref)); + from_hp = internal_ref_val(mon->ref); + ASSERT(thing_arityval(*from_hp) + 1 == REF_THING_SIZE); + + for (hix = 0; hix < REF_THING_SIZE; hix++) + to_hp[hix] = from_hp[hix]; + + cntxt->to_mon_info[mix].ref + = make_internal_ref(&cntxt->to_mon_info[mix].heap[0]); + +} + +static void +send_time_offset_changed_notifications(void *new_offsetp) +{ + ErtsMonotonicTime new_offset; + ErtsTimeOffsetMonitorInfo *to_mon_info; + Uint no_monitors; + char *tmp = NULL; + +#ifdef ARCH_64 + new_offset = (ErtsMonotonicTime) new_offsetp; +#else + new_offset = *((ErtsMonotonicTime *) new_offsetp); + erts_free(ERTS_ALC_T_NEW_TIME_OFFSET, new_offsetp); +#endif + new_offset -= ERTS_MONOTONIC_OFFSET_NATIVE; + + erts_smp_mtx_lock(&erts_get_time_mtx); + + no_monitors = no_time_offset_monitors; + if (no_monitors) { + ErtsTimeOffsetMonitorContext cntxt; + Uint alloc_sz; - if (ticks <= elapsed) { /* Ooops, better hurry */ - rem_time->tv_sec = rem_time->tv_usec = 0; - return; + /* Monitor info array size */ + alloc_sz = no_monitors*sizeof(ErtsTimeOffsetMonitorInfo); + /* + template max size */ + alloc_sz += 6*sizeof(Eterm); /* 5-tuple */ + alloc_sz += ERTS_MAX_SINT64_HEAP_SIZE*sizeof(Eterm); /* max offset size */ + tmp = erts_alloc(ERTS_ALC_T_TMP, alloc_sz); + + to_mon_info = (ErtsTimeOffsetMonitorInfo *) tmp; + cntxt.ix = 0; + cntxt.to_mon_info = to_mon_info; + + erts_doforall_monitors(time_offset_monitors, + save_time_offset_monitor, + &cntxt); + + ASSERT(cntxt.ix == no_monitors); + } + + erts_smp_mtx_unlock(&erts_get_time_mtx); + + if (no_monitors) { + Eterm *hp, *patch_refp, new_offset_term, message_template; + Uint mix, hsz; + + /* Make message template */ + + hp = (Eterm *) (tmp + no_monitors*sizeof(ErtsTimeOffsetMonitorInfo)); + + hsz = 6; /* 5-tuple */ + hsz += REF_THING_SIZE; + hsz += ERTS_SINT64_HEAP_SIZE(new_offset); + + if (IS_SSMALL(new_offset)) + new_offset_term = make_small(new_offset); + else + new_offset_term = erts_sint64_to_big(new_offset, &hp); + message_template = TUPLE5(hp, + am_CHANGE, + THE_NON_VALUE, /* Patch point for ref */ + am_time_offset, + am_clock_service, + new_offset_term); + patch_refp = &hp[2]; + + ASSERT(*patch_refp == THE_NON_VALUE); + + for (mix = 0; mix < no_monitors; mix++) { + Process *rp = erts_proc_lookup(to_mon_info[mix].pid); + if (rp) { + Eterm ref = to_mon_info[mix].ref; + ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK; + erts_smp_proc_lock(rp, ERTS_PROC_LOCK_LINK); + if (erts_lookup_monitor(ERTS_P_MONITORS(rp), ref)) { + ErlHeapFragment *bp; + ErlOffHeap *ohp; + Eterm message; + + hp = erts_alloc_message_heap(hsz, &bp, &ohp, rp, &rp_locks); + *patch_refp = ref; + ASSERT(hsz == size_object(message_template)); + message = copy_struct(message_template, hsz, &hp, ohp); + erts_queue_message(rp, &rp_locks, bp, message, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); + } + erts_smp_proc_unlock(rp, rp_locks); + } } - rem_time->tv_sec = (ticks - elapsed) / 1000; - rem_time->tv_usec = 1000 * ((ticks - elapsed) % 1000); + + erts_free(ERTS_ALC_T_TMP, tmp); } } -void erts_get_timeval(SysTimeval *tv) +static void +schedule_send_time_offset_changed_notifications(ErtsMonotonicTime new_offset) { - erts_smp_mtx_lock(&erts_timeofday_mtx); - get_tolerant_timeofday(tv); - erts_smp_mtx_unlock(&erts_timeofday_mtx); - update_approx_time(tv); +#ifdef ARCH_64 + void *new_offsetp = (void *) new_offset; + ASSERT(sizeof(void *) == sizeof(ErtsMonotonicTime)); +#else + void *new_offsetp = erts_alloc(ERTS_ALC_T_NEW_TIME_OFFSET, + sizeof(ErtsMonotonicTime)); + *((ErtsMonotonicTime *) new_offsetp) = new_offset; +#endif + erts_schedule_misc_aux_work(1, + send_time_offset_changed_notifications, + new_offsetp); } -erts_time_t -erts_get_time(void) +static ERTS_INLINE Eterm +make_time_val(Process *c_p, ErtsMonotonicTime time_val) { - SysTimeval sys_tv; - - erts_smp_mtx_lock(&erts_timeofday_mtx); - - get_tolerant_timeofday(&sys_tv); - - erts_smp_mtx_unlock(&erts_timeofday_mtx); + Sint64 val = (Sint64) time_val; + Eterm *hp; + Uint sz; - update_approx_time(&sys_tv); + if (IS_SSMALL(val)) + return make_small(val); - return sys_tv.tv_sec; + sz = ERTS_SINT64_HEAP_SIZE(val); + hp = HAlloc(c_p, sz); + return erts_sint64_to_big(val, &hp); } -#ifdef HAVE_ERTS_NOW_CPU -void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec) { - SysCpuTime t; - SysTimespec tp; +Eterm +erts_get_monotonic_start_time(struct process *c_p) +{ + return make_time_val(c_p, ERTS_MONOTONIC_OFFSET_NATIVE); +} - sys_get_proc_cputime(t, tp); - *microsec = (Uint)(tp.tv_nsec / 1000); - t = (tp.tv_sec / 1000000); - *megasec = (Uint)(t % 1000000); - *sec = (Uint)(tp.tv_sec % 1000000); +static Eterm +bld_monotonic_time_source(Uint **hpp, Uint *szp, Sint64 os_mtime) +{ +#ifndef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + return NIL; +#else + int i = 0; + Eterm k[5]; + Eterm v[5]; + + if (time_sup.r.o.os_monotonic_disable) + return NIL; + + k[i] = erts_bld_atom(hpp, szp, "function"); + v[i++] = erts_bld_atom(hpp, szp, time_sup.r.o.os_monotonic_func); + + if (time_sup.r.o.os_monotonic_clock_id) { + k[i] = erts_bld_atom(hpp, szp, "clock_id"); + v[i++] = erts_bld_atom(hpp, szp, time_sup.r.o.os_monotonic_clock_id); + } + + if (time_sup.r.o.os_monotonic_resolution) { + k[i] = erts_bld_atom(hpp, szp, "resolution"); + v[i++] = erts_bld_uint64(hpp, szp, time_sup.r.o.os_monotonic_resolution); + } + + k[i] = erts_bld_atom(hpp, szp, "parallel"); + v[i++] = time_sup.r.o.os_monotonic_locked ? am_no : am_yes; + + k[i] = erts_bld_atom(hpp, szp, "time"); + v[i++] = erts_bld_sint64(hpp, szp, os_mtime); + + return erts_bld_2tup_list(hpp, szp, (Sint) i, k, v); +#endif } + +Eterm +erts_monotonic_time_source(struct process *c_p) +{ + Uint hsz = 0; + Eterm *hp = NULL; + Sint64 os_mtime = 0; +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + if (!time_sup.r.o.os_monotonic_disable) + os_mtime = (Sint64) erts_os_monotonic_time(); +#endif + + bld_monotonic_time_source(NULL, &hsz, os_mtime); + if (hsz) + hp = HAlloc(c_p, hsz); + return bld_monotonic_time_source(&hp, NULL, os_mtime); +} + + +#include "bif.h" + +static ERTS_INLINE Eterm +time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonotonicTime muloff) +{ + ErtsMonotonicTime result; + BIF_RETTYPE ret; + + if (val < 0) + goto trap_to_erlang_code; + + /* Convert to common user specified time units */ + switch (term) { + case am_seconds: + case make_small(1): + result = ERTS_MONOTONIC_TO_SEC(val) + muloff*ERTS_MONOTONIC_OFFSET_SEC; + ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result)); + break; + case am_milli_seconds: + case make_small(1000): + result = ERTS_MONOTONIC_TO_MSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_MSEC; + ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result)); + break; + case am_micro_seconds: + case make_small(1000*1000): + result = ERTS_MONOTONIC_TO_USEC(val) + muloff*ERTS_MONOTONIC_OFFSET_USEC; + ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result)); + break; +#ifdef ARCH_64 + case am_nano_seconds: + case make_small(1000*1000*1000): + result = ERTS_MONOTONIC_TO_NSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_NSEC; + ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result)); + break; #endif + default: { + Eterm value, native_res; +#ifndef ARCH_64 + Sint user_res; + if (term == am_nano_seconds) + goto to_nano_seconds; + if (term_to_Sint(term, &user_res)) { + if (user_res == 1000*1000*1000) { + to_nano_seconds: + result = (ERTS_MONOTONIC_TO_NSEC(val) + + muloff*ERTS_MONOTONIC_OFFSET_NSEC); + ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result)); + break; + } + if (user_res <= 0) + goto badarg; + } +#else + if (is_small(term)) { + if (signed_val(term) <= 0) + goto badarg; + } +#endif + else if (is_big(term)) { + if (big_sign(term)) + goto badarg; + } + else { + badarg: + ERTS_BIF_PREP_ERROR(ret, c_p, BADARG); + break; + } + + trap_to_erlang_code: + /* Do it in erlang code instead; pass along values to use... */ + value = make_time_val(c_p, val + muloff*ERTS_MONOTONIC_OFFSET_NATIVE); + native_res = make_time_val(c_p, ERTS_MONOTONIC_TIME_UNIT); + + ERTS_BIF_PREP_TRAP3(ret, erts_convert_time_unit_trap, c_p, + value, native_res, term); + + break; + } + } + + return ret; +} + +/* Built in functions */ + +BIF_RETTYPE monotonic_time_0(BIF_ALIST_0) +{ + ErtsMonotonicTime mtime = time_sup.r.o.get_time(); + mtime += ERTS_MONOTONIC_OFFSET_NATIVE; + BIF_RET(make_time_val(BIF_P, mtime)); +} + +BIF_RETTYPE monotonic_time_1(BIF_ALIST_1) +{ + BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, time_sup.r.o.get_time(), 1)); +} + +BIF_RETTYPE system_time_0(BIF_ALIST_0) +{ + ErtsMonotonicTime mtime, offset; + mtime = time_sup.r.o.get_time(); + offset = get_time_offset(); + BIF_RET(make_time_val(BIF_P, mtime + offset)); +} + +BIF_RETTYPE system_time_1(BIF_ALIST_0) +{ + ErtsMonotonicTime mtime, offset; + mtime = time_sup.r.o.get_time(); + offset = get_time_offset(); + BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, mtime + offset, 0)); +} + +BIF_RETTYPE erts_internal_time_unit_0(BIF_ALIST_0) +{ + BIF_RET(make_time_val(BIF_P, ERTS_MONOTONIC_TIME_UNIT)); +} + +BIF_RETTYPE time_offset_0(BIF_ALIST_0) +{ + ErtsMonotonicTime time_offset = get_time_offset(); + time_offset -= ERTS_MONOTONIC_OFFSET_NATIVE; + BIF_RET(make_time_val(BIF_P, time_offset)); +} + +BIF_RETTYPE time_offset_1(BIF_ALIST_1) +{ + BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, get_time_offset(), -1)); +} + + +BIF_RETTYPE timestamp_0(BIF_ALIST_0) +{ + Eterm *hp, res; + ErtsMonotonicTime stime, mtime, all_sec, offset; + Uint mega_sec, sec, micro_sec; + + mtime = time_sup.r.o.get_time(); + offset = get_time_offset(); + stime = ERTS_MONOTONIC_TO_USEC(mtime + offset); + all_sec = stime / ERTS_MONOTONIC_TIME_MEGA; + mega_sec = (Uint) (stime / ERTS_MONOTONIC_TIME_TERA); + sec = (Uint) (all_sec - (((ErtsMonotonicTime) mega_sec) + * ERTS_MONOTONIC_TIME_MEGA)); + micro_sec = (Uint) (stime - all_sec*ERTS_MONOTONIC_TIME_MEGA); + + ASSERT(((ErtsMonotonicTime) mega_sec)*ERTS_MONOTONIC_TIME_TERA + + ((ErtsMonotonicTime) sec)*ERTS_MONOTONIC_TIME_MEGA + + micro_sec == stime); + + /* + * Mega seconds is the only value that potentially + * ever could be a bignum. However, that wont happen + * during at least the next 4 million years... + * + * (System time will also have wrapped in the + * 64-bit integer before we get there...) + */ + + ASSERT(IS_USMALL(0, mega_sec)); + ASSERT(IS_USMALL(0, sec)); + ASSERT(IS_USMALL(0, micro_sec)); + + hp = HAlloc(BIF_P, 4); + res = TUPLE3(hp, + make_small(mega_sec), + make_small(sec), + make_small(micro_sec)); + BIF_RET(res); +} + +BIF_RETTYPE os_system_time_0(BIF_ALIST_0) +{ + ErtsMonotonicTime stime; + SysTimeval tod; + sys_gettimeofday(&tod); + stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec); + stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec); + BIF_RET(make_time_val(BIF_P, stime)); +} + +BIF_RETTYPE os_system_time_1(BIF_ALIST_0) +{ + ErtsMonotonicTime stime; + SysTimeval tod; + sys_gettimeofday(&tod); + stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec); + stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec); + BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, stime, 0)); +} diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 891046a8b5..e24aef3e3c 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -346,8 +346,6 @@ extern Uint display_items; /* no of items to display in traces etc */ extern int erts_backtrace_depth; extern erts_smp_atomic32_t erts_max_gen_gcs; -extern int erts_disable_tolerant_timeofday; - extern int bif_reductions; /* reductions + fcalls (when doing call_bif) */ extern int stackdump_on_exit; @@ -622,9 +620,6 @@ erts_bld_port_info(Eterm **hpp, void erts_bif_info_init(void); /* bif.c */ -Eterm erts_make_ref(Process *); -Eterm erts_make_ref_in_buffer(Eterm buffer[REF_THING_SIZE]); -void erts_make_ref_in_array(Uint32 ref[ERTS_MAX_REF_NUMBERS]); ERTS_GLB_INLINE Eterm erts_proc_store_ref(Process *c_p, Uint32 ref[ERTS_MAX_REF_NUMBERS]); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 9ae973e108..c0e44b239b 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -47,6 +47,7 @@ #include "external.h" #include "dtrace-wrapper.h" #include "erl_map.h" +#include "erl_bif_unique.h" extern ErlDrvEntry fd_driver_entry; #ifndef __OSE__ diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index c29d4b3777..7e64623686 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -21,6 +21,25 @@ #define __SYS_H__ +#if defined(DEBUG) || defined(ERTS_ENABLE_LOCK_CHECK) +# undef ERTS_CAN_INLINE +# define ERTS_CAN_INLINE 0 +# undef ERTS_INLINE +# define ERTS_INLINE +#endif + +#if ERTS_CAN_INLINE +#define ERTS_GLB_INLINE static ERTS_INLINE +#else +#define ERTS_GLB_INLINE +#endif + +#if ERTS_CAN_INLINE || defined(ERTS_DO_INCL_GLB_INLINE_FUNC_DEF) +# define ERTS_GLB_INLINE_INCL_FUNC_DEF 1 +#else +# define ERTS_GLB_INLINE_INCL_FUNC_DEF 0 +#endif + #if defined(VALGRIND) && !defined(NO_FPE_SIGNALS) # define NO_FPE_SIGNALS #endif @@ -132,24 +151,8 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType; # endif #endif -#if defined(DEBUG) || defined(ERTS_ENABLE_LOCK_CHECK) -# undef ERTS_CAN_INLINE -# define ERTS_CAN_INLINE 0 -# undef ERTS_INLINE -# define ERTS_INLINE -#endif - -#if ERTS_CAN_INLINE -#define ERTS_GLB_INLINE static ERTS_INLINE -#else -#define ERTS_GLB_INLINE -#endif - -#if ERTS_CAN_INLINE || defined(ERTS_DO_INCL_GLB_INLINE_FUNC_DEF) -# define ERTS_GLB_INLINE_INCL_FUNC_DEF 1 -#else -# define ERTS_GLB_INLINE_INCL_FUNC_DEF 0 -#endif +#define ERTS_MK_VSN_INT(Major, Minor, Build) \ + ((((Major) & 0x3ff) << 20) | (((Minor) & 0x3ff) << 10) | ((Build) & 0x3ff)) #ifndef ERTS_EXIT_AFTER_DUMP # define ERTS_EXIT_AFTER_DUMP exit @@ -359,17 +362,45 @@ typedef Sint SWord; typedef UWord BeamInstr; #ifndef HAVE_INT64 -#if SIZEOF_LONG == 8 -#define HAVE_INT64 1 +# if SIZEOF_LONG == 8 +# define HAVE_INT64 1 typedef unsigned long Uint64; typedef long Sint64; -#elif SIZEOF_LONG_LONG == 8 -#define HAVE_INT64 1 +# ifdef ULONG_MAX +# define ERTS_UINT64_MAX ULONG_MAX +# endif +# ifdef LONG_MAX +# define ERTS_SINT64_MAX LONG_MAX +# endif +# ifdef LONG_MIN +# define ERTS_SINT64_MIN LONG_MIN +# endif +# elif SIZEOF_LONG_LONG == 8 +# define HAVE_INT64 1 typedef unsigned long long Uint64; typedef long long Sint64; -#else -#define HAVE_INT64 0 +# ifdef ULLONG_MAX +# define ERTS_UINT64_MAX ULLONG_MAX +# endif +# ifdef LLONG_MAX +# define ERTS_SINT64_MAX LLONG_MAX +# endif +# ifdef LLONG_MIN +# define ERTS_SINT64_MIN LLONG_MIN +# endif +# else +# error "No 64-bit integer type found" +# endif +#endif + +#ifndef ERTS_UINT64_MAX +# define ERTS_UINT64_MAX (~((Uint64) 0)) #endif +#ifndef ERTS_SINT64_MAX +# define ERTS_SINT64_MAX ((Sint64) ((((Uint64) 1) << 63)-1)) +#endif +#ifndef ERTS_SINT64_MIN +# define ERTS_SINT64_MIN (-1*(((Sint64) 1) << 63)) #endif #if SIZEOF_LONG == 4 @@ -646,10 +677,31 @@ extern char *erts_default_arg0; extern char os_type[]; -extern int sys_init_time(void); +typedef enum { + ERTS_NO_TIME_WARP_MODE, + ERTS_SINGLE_TIME_WARP_MODE, + ERTS_MULTI_TIME_WARP_MODE +} ErtsTimeWarpMode; + +typedef struct { + int have_os_monotonic; + ErtsMonotonicTime os_monotonic_time_unit; + ErtsMonotonicTime sys_clock_resolution; + struct { + Uint64 resolution; + char *func; + char *clock_id; + int locked_use; + } os_monotonic_info; +} ErtsSysInitTimeResult; + +#define ERTS_SYS_INIT_TIME_RESULT_INITER \ + {0, (ErtsMonotonicTime) -1, (ErtsMonotonicTime) 1} + +extern void sys_init_time(ErtsSysInitTimeResult *); extern void erts_deliver_time(void); extern void erts_time_remaining(SysTimeval *); -extern int erts_init_time_sup(void); +extern int erts_init_time_sup(int, ErtsTimeWarpMode); extern void erts_sys_init_float(void); extern void erts_thread_init_float(void); extern void erts_thread_disable_fpe(void); @@ -700,7 +752,7 @@ extern char *erts_sys_ddll_error(int code); void erts_sys_schedule_interrupt(int set); #ifdef ERTS_SMP -void erts_sys_schedule_interrupt_timed(int set, erts_short_time_t msec); +void erts_sys_schedule_interrupt_timed(int, ErtsMonotonicTime); void erts_sys_main_thread(void); #endif @@ -739,6 +791,7 @@ int univ_to_local( int local_to_univ(Sint *year, Sint *month, Sint *day, Sint *hour, Sint *minute, Sint *second, int isdst); void get_now(Uint*, Uint*, Uint*); +ErtsMonotonicTime erts_get_monotonic_time(void); void get_sys_now(Uint*, Uint*, Uint*); void set_break_quit(void (*)(void), void (*)(void)); diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index 2fd8e0cf00..c9f8b68bca 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -83,6 +83,10 @@ #define ASSERT_NO_LOCKED_LOCKS #endif +#define ERTS_MONOTONIC_DAY ERTS_SEC_TO_MONOTONIC(60*60*24) +#define ERTS_CLKTCKS_DAY ERTS_MONOTONIC_TO_CLKTCKS(ERTS_MONOTONIC_DAY) + +static erts_smp_atomic32_t is_bumping; static erts_smp_mtx_t tiw_lock; @@ -91,18 +95,20 @@ static erts_smp_mtx_t tiw_lock; ** The individual timer cells in tiw are also protected by the same mutex. */ +/* timing wheel size NEED to be a power of 2 */ #ifdef SMALL_MEMORY -#define TIW_SIZE 8192 +#define TIW_SIZE (1 << 13) #else -#define TIW_SIZE 65536 /* timing wheel size (should be a power of 2) */ +#define TIW_SIZE (1 << 20) #endif static ErlTimer** tiw; /* the timing wheel, allocated in init_time() */ -static Uint tiw_pos; /* current position in wheel */ +static ErtsMonotonicTime tiw_pos; /* current position in wheel */ static Uint tiw_nto; /* number of timeouts in wheel */ -static Uint tiw_min; -static ErlTimer *tiw_min_ptr; - -/* END tiw_lock protected variables */ +static struct { + ErlTimer *head; + ErlTimer **tail; + Uint nto; +} tiw_at_once; /* Actual interval time chosen by sys_init_time() */ @@ -114,77 +120,97 @@ static int tiw_itime; /* Constant after init */ # define TIW_ITIME tiw_itime #endif -erts_smp_atomic32_t do_time; /* set at clock interrupt */ -static ERTS_INLINE erts_short_time_t do_time_read(void) -{ - return erts_smp_atomic32_read_acqb(&do_time); -} +static int true_next_timeout_time; +static ErtsMonotonicTime next_timeout_time; +erts_atomic64_t erts_next_timeout__; -static ERTS_INLINE erts_short_time_t do_time_update(void) +static ERTS_INLINE void +init_next_timeout(ErtsMonotonicTime time) { - return do_time_read(); + erts_atomic64_init_nob(&erts_next_timeout__, + (erts_aint64_t) time); } -static ERTS_INLINE void do_time_init(void) +static ERTS_INLINE void +set_next_timeout(ErtsMonotonicTime time, int true_timeout) { - erts_smp_atomic32_init_nob(&do_time, 0); + true_next_timeout_time = true_timeout; + next_timeout_time = time; + erts_atomic64_set_relb(&erts_next_timeout__, + (erts_aint64_t) time); } /* get the time (in units of TIW_ITIME) to the next timeout, or -1 if there are no timeouts */ -static erts_short_time_t next_time_internal(void) /* PRE: tiw_lock taken by caller */ +static ERTS_INLINE ErtsMonotonicTime +find_next_timeout(ErtsMonotonicTime curr_time, + ErtsMonotonicTime max_search_time) { - int i, tm, nto; - Uint32 min; - ErlTimer* p; - erts_short_time_t dt; - - if (tiw_nto == 0) - return -1; /* no timeouts in wheel */ + int start_ix, tiw_pos_ix; + ErlTimer *p; + int true_min_timeout; + ErtsMonotonicTime min_timeout, min_timeout_pos, slot_timeout_pos, timeout_limit; - if (tiw_min_ptr) { - min = tiw_min; - dt = do_time_read(); - return ((min >= dt) ? (min - dt) : 0); + ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&tiw_lock)); + + if (true_next_timeout_time) + return next_timeout_time; + + /* We never set next timeout beyond timeout_limit */ + timeout_limit = curr_time + ERTS_MONOTONIC_DAY; + + if (tiw_nto == 0) { /* no timeouts in wheel */ + true_min_timeout = true_next_timeout_time = 0; + min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(timeout_limit); + goto found_next; } - - /* start going through wheel to find next timeout */ - tm = nto = 0; - min = (Uint32) -1; /* max Uint32 */ - i = tiw_pos; + + /* + * Don't want others entering trying to bump + * timers while we are checking... + */ + set_next_timeout(timeout_limit, 0); + + true_min_timeout = 1; + slot_timeout_pos = tiw_pos; + min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time + max_search_time); + + start_ix = tiw_pos_ix = (int) (tiw_pos & (TIW_SIZE-1)); + do { - p = tiw[i]; - while (p != NULL) { - nto++; - if (p->count == 0) { - /* found next timeout */ - dt = do_time_read(); - /* p->count is zero */ - tiw_min_ptr = p; - tiw_min = tm; - return ((tm >= dt) ? (tm - dt) : 0); - } else { - /* keep shortest time in 'min' */ - if (tm + p->count*TIW_SIZE < min) { - min = tm + p->count*TIW_SIZE; - tiw_min_ptr = p; - tiw_min = min; - } + slot_timeout_pos++; + if (slot_timeout_pos >= min_timeout_pos) { + true_min_timeout = 0; + break; + } + + p = tiw[tiw_pos_ix]; + + while (p) { + ErtsMonotonicTime timeout_pos; + ASSERT(p != p->next); + timeout_pos = p->timeout_pos; + if (min_timeout_pos > timeout_pos) { + min_timeout_pos = timeout_pos; + if (min_timeout_pos <= slot_timeout_pos) + goto found_next; } p = p->next; } - /* when we have found all timeouts the shortest time will be in min */ - if (nto == tiw_nto) break; - tm++; - i = (i + 1) % TIW_SIZE; - } while (i != tiw_pos); - dt = do_time_read(); - if (min <= (Uint32) dt) - return 0; - if ((min - (Uint32) dt) > (Uint32) ERTS_SHORT_TIME_T_MAX) - return ERTS_SHORT_TIME_T_MAX; - return (erts_short_time_t) (min - (Uint32) dt); + + tiw_pos_ix++; + if (tiw_pos_ix == TIW_SIZE) + tiw_pos_ix = 0; + } while (start_ix != tiw_pos_ix); + +found_next: + + min_timeout = ERTS_CLKTCKS_TO_MONOTONIC(min_timeout_pos); + if (min_timeout != next_timeout_time) + set_next_timeout(min_timeout, true_min_timeout); + + return min_timeout; } static void remove_timer(ErlTimer *p) { @@ -212,72 +238,153 @@ static void remove_timer(ErlTimer *p) { tiw_nto--; } -/* Private export to erl_time_sup.c */ -erts_short_time_t erts_next_time(void) +ErtsMonotonicTime +erts_check_next_timeout_time(ErtsMonotonicTime max_search_time) { - erts_short_time_t ret; + ErtsMonotonicTime next, curr; + + curr = erts_get_monotonic_time(); erts_smp_mtx_lock(&tiw_lock); - (void)do_time_update(); - ret = next_time_internal(); + + next = find_next_timeout(curr, max_search_time); + erts_smp_mtx_unlock(&tiw_lock); - return ret; + + return next; } -static ERTS_INLINE void bump_timer_internal(erts_short_time_t dt) /* PRE: tiw_lock is write-locked */ +#ifndef DEBUG +#define ERTS_DBG_CHK_SAFE_TO_SKIP_TO(TO) ((void) 0) +#else +#define ERTS_DBG_CHK_SAFE_TO_SKIP_TO(TO) debug_check_safe_to_skip_to((TO)) +static void +debug_check_safe_to_skip_to(ErtsMonotonicTime skip_to_pos) { - Uint keep_pos; - Uint count; - ErlTimer *p, **prev, *timeout_head, **timeout_tail; - Uint dtime = (Uint) dt; + int slots, ix; + ErlTimer *tmr; + ErtsMonotonicTime tmp; + + ix = (int) (tiw_pos & (TIW_SIZE-1)); + tmp = skip_to_pos - tiw_pos; + ASSERT(tmp >= 0); + if (tmp < (ErtsMonotonicTime) TIW_SIZE) + slots = (int) tmp; + else + slots = TIW_SIZE; + + while (slots > 0) { + tmr = tiw[ix]; + while (tmr) { + ASSERT(tmr->timeout_pos > skip_to_pos); + tmr = tmr->next; + } + ix++; + if (ix == TIW_SIZE) + ix = 0; + slots--; + } +} +#endif + +void erts_bump_timers(ErtsMonotonicTime curr_time) +{ + int tiw_pos_ix, slots; + ErlTimer *p, *timeout_head, **timeout_tail; + ErtsMonotonicTime bump_to, tmp_slots; + + if (erts_smp_atomic32_cmpxchg_nob(&is_bumping, 1, 0) != 0) + return; /* Another thread is currently bumping... */ + + bump_to = ERTS_MONOTONIC_TO_CLKTCKS(curr_time); + + erts_smp_mtx_lock(&tiw_lock); + + if (tiw_pos >= bump_to) { + timeout_head = NULL; + goto done; + } + + /* Don't want others here while we are bumping... */ + set_next_timeout(curr_time + ERTS_MONOTONIC_DAY, 0); + + if (!tiw_at_once.head) { + timeout_head = NULL; + timeout_tail = &timeout_head; + } + else { + ASSERT(tiw_nto >= tiw_at_once.nto); + timeout_head = tiw_at_once.head; + timeout_tail = tiw_at_once.tail; + tiw_nto -= tiw_at_once.nto; + tiw_at_once.head = NULL; + tiw_at_once.tail = &tiw_at_once.head; + tiw_at_once.nto = 0; + } - /* no need to bump the position if there aren't any timeouts */ if (tiw_nto == 0) { - erts_smp_mtx_unlock(&tiw_lock); - return; + ERTS_DBG_CHK_SAFE_TO_SKIP_TO(bump_to); + tiw_pos = bump_to; + goto done; } - /* if do_time > TIW_SIZE we want to go around just once */ - count = (Uint)(dtime / TIW_SIZE) + 1; - keep_pos = (tiw_pos + dtime) % TIW_SIZE; - if (dtime > TIW_SIZE) dtime = TIW_SIZE; - - timeout_head = NULL; - timeout_tail = &timeout_head; - while (dtime > 0) { - /* this is to decrease the counters with the right amount */ - /* when dtime >= TIW_SIZE */ - if (tiw_pos == keep_pos) count--; - prev = &tiw[tiw_pos]; - while ((p = *prev) != NULL) { - ASSERT( p != p->next); - if (p->count < count) { /* we have a timeout */ - /* remove min time */ - if (tiw_min_ptr == p) { - tiw_min_ptr = NULL; - tiw_min = 0; - } + if (true_next_timeout_time) { + ErtsMonotonicTime skip_until_pos; + /* + * No need inspecting slots where we know no timeouts + * to trigger should reside. + */ + skip_until_pos = ERTS_MONOTONIC_TO_CLKTCKS(next_timeout_time); + if (skip_until_pos > bump_to) + skip_until_pos = bump_to; + + ERTS_DBG_CHK_SAFE_TO_SKIP_TO(skip_until_pos); + ASSERT(skip_until_pos > tiw_pos); + + tiw_pos = skip_until_pos - 1; + } + + tiw_pos_ix = (int) ((tiw_pos+1) & (TIW_SIZE-1)); + tmp_slots = (bump_to - tiw_pos); + if (tmp_slots < (ErtsMonotonicTime) TIW_SIZE) + slots = (int) tmp_slots; + else + slots = TIW_SIZE; + + while (slots > 0) { + p = tiw[tiw_pos_ix]; + while (p) { + ErlTimer *next = p->next; + ASSERT(p != next); + if (p->timeout_pos <= bump_to) { /* we have a timeout */ /* Remove from list */ remove_timer(p); *timeout_tail = p; /* Insert in timeout queue */ timeout_tail = &p->next; } - else { - /* no timeout, just decrease counter */ - p->count -= count; - prev = &p->next; - } + p = next; } - tiw_pos = (tiw_pos + 1) % TIW_SIZE; - dtime--; + tiw_pos_ix++; + if (tiw_pos_ix == TIW_SIZE) + tiw_pos_ix = 0; + slots--; } - tiw_pos = keep_pos; - if (tiw_min_ptr) - tiw_min -= dt; - + + ASSERT(tmp_slots >= (ErtsMonotonicTime) TIW_SIZE + || tiw_pos_ix == (int) ((bump_to+1) & (TIW_SIZE-1))); + + tiw_pos = bump_to; + + /* Search at most two seconds ahead... */ + (void) find_next_timeout(curr_time, ERTS_SEC_TO_MONOTONIC(2)); + +done: + erts_smp_mtx_unlock(&tiw_lock); + erts_smp_atomic32_set_nob(&is_bumping, 0); + /* Call timedout timers callbacks */ while (timeout_head) { p = timeout_head; @@ -288,6 +395,7 @@ static ERTS_INLINE void bump_timer_internal(erts_short_time_t dt) /* PRE: tiw_lo * accesses any field until the ->timeout * callback is called. */ + ASSERT(p->timeout_pos <= bump_to); p->next = NULL; p->prev = NULL; p->slot = 0; @@ -295,12 +403,6 @@ static ERTS_INLINE void bump_timer_internal(erts_short_time_t dt) /* PRE: tiw_lo } } -void erts_bump_timer(erts_short_time_t dt) /* dt is value from do_time */ -{ - erts_smp_mtx_lock(&tiw_lock); - bump_timer_internal(dt); -} - Uint erts_timer_wheel_memory_size(void) { @@ -310,13 +412,14 @@ erts_timer_wheel_memory_size(void) /* this routine links the time cells into a free list at the start and sets the time queue as empty */ void -erts_init_time(void) +erts_init_time(int time_correction, ErtsTimeWarpMode time_warp_mode) { + ErtsMonotonicTime mtime; int i, itime; /* system dependent init; must be done before do_time_init() if timer thread is enabled */ - itime = erts_init_time_sup(); + itime = erts_init_time_sup(time_correction, time_warp_mode); #ifdef TIW_ITIME_IS_CONSTANT if (itime != TIW_ITIME) { erl_exit(ERTS_ABORT_EXIT, "timer resolution mismatch %d != %d", itime, TIW_ITIME); @@ -325,16 +428,23 @@ erts_init_time(void) tiw_itime = itime; #endif + erts_smp_atomic32_init_nob(&is_bumping, 0); erts_smp_mtx_init(&tiw_lock, "timer_wheel"); tiw = (ErlTimer**) erts_alloc(ERTS_ALC_T_TIMER_WHEEL, TIW_SIZE * sizeof(ErlTimer*)); for(i = 0; i < TIW_SIZE; i++) tiw[i] = NULL; - do_time_init(); - tiw_pos = tiw_nto = 0; - tiw_min_ptr = NULL; - tiw_min = 0; + + mtime = erts_get_monotonic_time(); + tiw_pos = ERTS_MONOTONIC_TO_CLKTCKS(mtime); + tiw_nto = 0; + tiw_at_once.head = NULL; + tiw_at_once.tail = &tiw_at_once.head; + tiw_at_once.nto = 0; + init_next_timeout(mtime + ERTS_MONOTONIC_DAY); + + erts_late_init_time_sup(); } @@ -343,58 +453,62 @@ erts_init_time(void) /* ** Insert a process into the time queue, with a timeout 't' */ -static void -insert_timer(ErlTimer* p, Uint t) +static ErtsMonotonicTime +insert_timer(ErlTimer* p, ErtsMonotonicTime curr_time, ErtsMonotonicTime to) { - Uint tm; - Uint64 ticks; + ErtsMonotonicTime timeout_time, timeout_pos; - /* The current slot (tiw_pos) in timing wheel is the next slot to be - * be processed. Hence no extra time tick is needed. - * - * (x + y - 1)/y is precisely the "number of bins" formula. - */ - ticks = (t + (TIW_ITIME - 1)) / TIW_ITIME; + if (to == 0) { + timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time); + tiw_nto++; + tiw_at_once.nto++; + *tiw_at_once.tail = p; + p->next = NULL; + p->timeout_pos = timeout_pos; + timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(timeout_pos); + } + else { + int tm; + ErtsMonotonicTime ticks; - /* - * Ticks must be a Uint64, or the addition may overflow here, - * resulting in an incorrect value for p->count below. - */ - ticks += do_time_update(); /* Add backlog of unprocessed time */ - - /* calculate slot */ - tm = (ticks + tiw_pos) % TIW_SIZE; - p->slot = (Uint) tm; - p->count = (Uint) (ticks / TIW_SIZE); + ticks = ERTS_MSEC_TO_CLKTCKS(to); + timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time - 1) + 1 + ticks; + + /* calculate slot */ + tm = (int) (timeout_pos & (TIW_SIZE-1)); + p->slot = (Uint) tm; - /* insert at head of list at slot */ - p->next = tiw[tm]; - p->prev = NULL; - if (p->next != NULL) - p->next->prev = p; - tiw[tm] = p; + /* insert at head of list at slot */ + p->next = tiw[tm]; + p->prev = NULL; + if (p->next != NULL) + p->next->prev = p; + tiw[tm] = p; + tiw_nto++; - /* insert min time */ - if ((tiw_nto == 0) || ((tiw_min_ptr != NULL) && (ticks < tiw_min))) { - tiw_min = ticks; - tiw_min_ptr = p; - } - if ((tiw_min_ptr == p) && (ticks > tiw_min)) { - /* some other timer might be 'min' now */ - tiw_min = 0; - tiw_min_ptr = NULL; + timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(timeout_pos); + p->timeout_pos = timeout_pos; + + ASSERT(ERTS_MSEC_TO_MONOTONIC(to) <= timeout_time - curr_time); + ASSERT(timeout_time - curr_time + < ERTS_MSEC_TO_MONOTONIC(to) + ERTS_CLKTCKS_TO_MONOTONIC(1)); } - tiw_nto++; + if (timeout_time < next_timeout_time) + set_next_timeout(timeout_time, 1); + + return timeout_time; } void erts_set_timer(ErlTimer* p, ErlTimeoutProc timeout, ErlCancelProc cancel, void* arg, Uint t) { - - erts_deliver_time(); +#ifdef ERTS_SMP + ErtsMonotonicTime timeout_time; +#endif + ErtsMonotonicTime current_time = erts_get_monotonic_time(); erts_smp_mtx_lock(&tiw_lock); if (p->active) { /* XXX assert ? */ erts_smp_mtx_unlock(&tiw_lock); @@ -404,11 +518,15 @@ erts_set_timer(ErlTimer* p, ErlTimeoutProc timeout, ErlCancelProc cancel, p->cancel = cancel; p->arg = arg; p->active = 1; - insert_timer(p, t); +#ifdef ERTS_SMP + timeout_time = +#else + (void) +#endif + insert_timer(p, current_time, (ErtsMonotonicTime) t); erts_smp_mtx_unlock(&tiw_lock); #if defined(ERTS_SMP) - if (t <= (Uint) ERTS_SHORT_TIME_T_MAX) - erts_sys_schedule_interrupt_timed(1, (erts_short_time_t) t); + erts_sys_schedule_interrupt_timed(1, timeout_time); #endif } @@ -421,14 +539,8 @@ erts_cancel_timer(ErlTimer* p) return; } - /* is it the 'min' timer, remove min */ - if (p == tiw_min_ptr) { - tiw_min_ptr = NULL; - tiw_min = 0; - } - remove_timer(p); - p->slot = p->count = 0; + p->slot = 0; if (p->cancel != NULL) { erts_smp_mtx_unlock(&tiw_lock); @@ -447,8 +559,7 @@ erts_cancel_timer(ErlTimer* p) Uint erts_time_left(ErlTimer *p) { - Uint left; - erts_short_time_t dt; + ErtsMonotonicTime current_time, timeout_time; erts_smp_mtx_lock(&tiw_lock); @@ -457,45 +568,43 @@ erts_time_left(ErlTimer *p) return 0; } - if (p->slot < tiw_pos) - left = (p->count + 1) * TIW_SIZE + p->slot - tiw_pos; - else - left = p->count * TIW_SIZE + p->slot - tiw_pos; - dt = do_time_read(); - if (left < dt) - left = 0; - else - left -= dt; + timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(p->timeout_pos); erts_smp_mtx_unlock(&tiw_lock); - return (Uint) left * TIW_ITIME; + current_time = erts_get_monotonic_time(); + if (timeout_time <= current_time) + return 0; + return (Uint) ERTS_MONOTONIC_TO_MSEC(timeout_time - current_time); } #ifdef DEBUG void erts_p_slpq(void) { + ErtsMonotonicTime current_time = erts_get_monotonic_time(); int i; ErlTimer* p; erts_smp_mtx_lock(&tiw_lock); /* print the whole wheel, starting at the current position */ - erts_printf("\ntiw_pos = %d tiw_nto %d\n", tiw_pos, tiw_nto); + erts_printf("\ncurrent time = %bps tiw_pos = %d tiw_nto %d\n", + current_time, tiw_pos, tiw_nto); i = tiw_pos; if (tiw[i] != NULL) { erts_printf("%d:\n", i); for(p = tiw[i]; p != NULL; p = p->next) { - erts_printf(" (count %d, slot %d)\n", - p->count, p->slot); + erts_printf(" (timeout time %bps, slot %d)\n", + ERTS_CLKTCKS_TO_MONOTONIC(p->timeout_pos), + p->slot); } } - for(i = (i+1)%TIW_SIZE; i != tiw_pos; i = (i+1)%TIW_SIZE) { + for(i = ((i+1) & (TIW_SIZE-1)); i != (tiw_pos & (TIW_SIZE-1)); i = ((i+1) & (TIW_SIZE-1))) { if (tiw[i] != NULL) { erts_printf("%d:\n", i); for(p = tiw[i]; p != NULL; p = p->next) { - erts_printf(" (count %d, slot %d)\n", - p->count, p->slot); + erts_printf(" (timeout time %bps, slot %d)\n", + ERTS_CLKTCKS_TO_MONOTONIC(p->timeout_pos), p->slot); } } } diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index c505c44905..54f1a122c4 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -49,6 +49,7 @@ #include "beam_bp.h" #include "erl_ptab.h" #include "erl_check_io.h" +#include "erl_bif_unique.h" #undef M_TRIM_THRESHOLD #undef M_TOP_PAD @@ -4353,8 +4354,8 @@ erts_smp_ensure_later_interval_acqb(erts_interval_t *icp, Uint64 ic) */ Uint64 erts_timestamp_millis(void) { -#ifdef HAVE_GETHRTIME - return (Uint64) (sys_gethrtime() / 1000000); +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + return ERTS_MONOTONIC_TO_MSEC(erts_os_monotonic_time()); #else Uint64 res; SysTimeval tv; diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 0051b45b31..705f36c34c 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -1590,9 +1590,9 @@ ERTS_CIO_EXPORT(erts_check_io_interrupt)(int set) void ERTS_CIO_EXPORT(erts_check_io_interrupt_timed)(int set, - erts_short_time_t msec) + ErtsMonotonicTime timeout_time) { - ERTS_CIO_POLL_INTR_TMD(pollset.ps, set, msec); + ERTS_CIO_POLL_INTR_TMD(pollset.ps, set, timeout_time); } void @@ -1600,7 +1600,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) { ErtsPollResFd *pollres; int pollres_len; - SysTimeval wait_time; + ErtsMonotonicTime timeout_time; int poll_ret, i; erts_aint_t current_cio_time; @@ -1612,12 +1612,9 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) #endif /* Figure out timeout value */ - if (do_wait) { - erts_time_remaining(&wait_time); - } else { /* poll only */ - wait_time.tv_sec = 0; - wait_time.tv_usec = 0; - } + timeout_time = (do_wait + ? erts_check_next_timeout_time(ERTS_SEC_TO_MONOTONIC(10*60)) + : ERTS_POLL_NO_TIMEOUT /* poll only */); /* * No need for an atomic inc op when incrementing @@ -1640,14 +1637,12 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) erts_smp_atomic_set_nob(&pollset.in_poll_wait, 1); - poll_ret = ERTS_CIO_POLL_WAIT(pollset.ps, pollres, &pollres_len, &wait_time); + poll_ret = ERTS_CIO_POLL_WAIT(pollset.ps, pollres, &pollres_len, timeout_time); #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_check_exact(NULL, 0); /* No locks should be locked */ #endif - erts_deliver_time(); /* sync the machine's idea of time */ - #ifdef ERTS_BREAK_REQUESTED if (ERTS_BREAK_REQUESTED) erts_do_break_handling(); diff --git a/erts/emulator/sys/common/erl_check_io.h b/erts/emulator/sys/common/erl_check_io.h index d01297d55c..71355965aa 100644 --- a/erts/emulator/sys/common/erl_check_io.h +++ b/erts/emulator/sys/common/erl_check_io.h @@ -47,8 +47,8 @@ void erts_check_io_async_sig_interrupt_nkp(void); #endif void erts_check_io_interrupt_kp(int); void erts_check_io_interrupt_nkp(int); -void erts_check_io_interrupt_timed_kp(int, erts_short_time_t); -void erts_check_io_interrupt_timed_nkp(int, erts_short_time_t); +void erts_check_io_interrupt_timed_kp(int, ErtsMonotonicTime); +void erts_check_io_interrupt_timed_nkp(int, ErtsMonotonicTime); void erts_check_io_kp(int); void erts_check_io_nkp(int); void erts_init_check_io_kp(void); @@ -65,7 +65,7 @@ int erts_check_io_max_files(void); void erts_check_io_async_sig_interrupt(void); #endif void erts_check_io_interrupt(int); -void erts_check_io_interrupt_timed(int, erts_short_time_t); +void erts_check_io_interrupt_timed(int, ErtsMonotonicTime); void erts_check_io(int); void erts_init_check_io(void); diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index aa412a20c8..f4d4a85ca4 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -320,7 +320,7 @@ struct ErtsPollSet_ { #if defined(USE_THREADS) || ERTS_POLL_ASYNC_INTERRUPT_SUPPORT erts_atomic32_t wakeup_state; #endif - erts_smp_atomic32_t timeout; + erts_atomic64_t timeout_time; #ifdef ERTS_POLL_COUNT_AVOIDED_WAKEUPS erts_smp_atomic_t no_avoided_wakeups; erts_smp_atomic_t no_avoided_interrupts; @@ -384,6 +384,26 @@ static void check_poll_status(ErtsPollSet ps); static void print_misc_debug_info(void); #endif +static ERTS_INLINE void +init_timeout_time(ErtsPollSet ps) +{ + erts_atomic64_init_nob(&ps->timeout_time, + (erts_aint64_t) ERTS_MONOTONIC_TIME_MAX); +} + +static ERTS_INLINE void +set_timeout_time(ErtsPollSet ps, ErtsMonotonicTime time) +{ + erts_atomic64_set_relb(&ps->timeout_time, + (erts_aint64_t) time); +} + +static ERTS_INLINE ErtsMonotonicTime +get_timeout_time(ErtsPollSet ps) +{ + return (ErtsMonotonicTime) erts_atomic64_read_acqb(&ps->timeout_time); +} + #define ERTS_POLL_NOT_WOKEN 0 #define ERTS_POLL_WOKEN -1 #define ERTS_POLL_WOKEN_INTR 1 @@ -1993,44 +2013,153 @@ save_poll_result(ErtsPollSet ps, ErtsPollResFd pr[], int max_res, } } +static ERTS_INLINE ErtsMonotonicTime +get_timeout(ErtsPollSet ps, + int resolution, + ErtsMonotonicTime timeout_time) +{ + ErtsMonotonicTime timeout, save_timeout_time; + + if (timeout_time == ERTS_POLL_NO_TIMEOUT) { + save_timeout_time = ERTS_MONOTONIC_TIME_MIN; + timeout = 0; + } + else { + ErtsMonotonicTime diff_time, current_time; + current_time = erts_get_monotonic_time(); + diff_time = timeout_time - current_time; + if (diff_time <= 0) { + save_timeout_time = ERTS_MONOTONIC_TIME_MIN; + timeout = 0; + } + else { + save_timeout_time = current_time; + switch (resolution) { + case 1000: + /* Round up to nearest even milli second */ + timeout = ERTS_MONOTONIC_TO_MSEC(diff_time - 1) + 1; + if (timeout > (ErtsMonotonicTime) INT_MAX) + timeout = (ErtsMonotonicTime) INT_MAX; + save_timeout_time += ERTS_MSEC_TO_MONOTONIC(timeout); + break; + case 1000000: + /* Round up to nearest even micro second */ + timeout = ERTS_MONOTONIC_TO_USEC(diff_time - 1) + 1; + save_timeout_time += ERTS_USEC_TO_MONOTONIC(timeout); + break; + case 1000000000: + /* Round up to nearest even nano second */ + timeout = ERTS_MONOTONIC_TO_NSEC(diff_time - 1) + 1; + save_timeout_time += ERTS_NSEC_TO_MONOTONIC(timeout); + break; + default: + ERTS_INTERNAL_ERROR("Invalid resolution"); + timeout = 0; + save_timeout_time = 0; + break; + } + } + } + set_timeout_time(ps, save_timeout_time); + return timeout; +} + +#if ERTS_POLL_USE_SELECT + static ERTS_INLINE int -check_fd_events(ErtsPollSet ps, SysTimeval *tv, int max_res) +get_timeout_timeval(ErtsPollSet ps, + SysTimeval *tvp, + ErtsMonotonicTime timeout_time) +{ + ErtsMonotonicTime timeout = get_timeout(ps, + 1000*1000, + timeout_time); + + if (!timeout) { + tvp->tv_sec = 0; + tvp->tv_usec = 0; + + return 0; + } + else { + ErtsMonotonicTime sec = timeout/(1000*1000); + tvp->tv_sec = sec; + tvp->tv_usec = timeout - sec*(1000*1000); + + ASSERT(tvp->tv_sec >= 0); + ASSERT(tvp->tv_usec >= 0); + ASSERT(tvp->tv_usec < 1000*1000); + + return !0; + } + +} + +#endif + +#if ERTS_POLL_USE_KQUEUE + +static ERTS_INLINE int +get_timeout_timespec(ErtsPollSet ps, + struct timespec *tsp, + ErtsMonotonicTime timeout_time) +{ + ErtsMonotonicTime timeout = get_timeout(ps, + 1000*1000*1000, + timeout_time); + + if (!timeout) { + tsp->tv_sec = 0; + tsp->tv_nsec = 0; + return 0; + } + else { + ErtsMonotonicTime sec = timeout/(1000*1000*1000); + tsp->tv_sec = sec; + tsp->tv_nsec = timeout - sec*(1000*1000*1000); + + ASSERT(tsp->tv_sec >= 0); + ASSERT(tsp->tv_nsec >= 0); + ASSERT(tsp->tv_nsec < 1000*1000); + + return !0; + } +} + +#endif + +static ERTS_INLINE int +check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) { int res; if (erts_smp_atomic_read_nob(&ps->no_of_user_fds) == 0 - && tv->tv_usec == 0 && tv->tv_sec == 0) { + && timeout_time == ERTS_POLL_NO_TIMEOUT) { /* Nothing to poll and zero timeout; done... */ return 0; } else { - long timeout = tv->tv_sec*1000 + tv->tv_usec/1000; - if (timeout > ERTS_AINT32_T_MAX) - timeout = ERTS_AINT32_T_MAX; - ASSERT(timeout >= 0); - erts_smp_atomic32_set_relb(&ps->timeout, (erts_aint32_t) timeout); + int timeout; #if ERTS_POLL_USE_FALLBACK if (!(ps->fallback_used = ERTS_POLL_NEED_FALLBACK(ps))) { #if ERTS_POLL_USE_EPOLL /* --- epoll ------------------------------- */ - if (timeout > INT_MAX) - timeout = INT_MAX; if (max_res > ps->res_events_len) grow_res_events(ps, max_res); + timeout = (int) get_timeout(ps, 1000, timeout_time); #ifdef ERTS_SMP if (timeout) erts_thr_progress_prepare_wait(NULL); #endif - res = epoll_wait(ps->kp_fd, ps->res_events, max_res, (int)timeout); + res = epoll_wait(ps->kp_fd, ps->res_events, max_res, timeout); #elif ERTS_POLL_USE_KQUEUE /* --- kqueue ------------------------------ */ struct timespec ts; if (max_res > ps->res_events_len) grow_res_events(ps, max_res); + timeout = get_timeout_timespec(ps, &ts, timeout_time); #ifdef ERTS_SMP if (timeout) erts_thr_progress_prepare_wait(NULL); #endif - ts.tv_sec = tv->tv_sec; - ts.tv_nsec = tv->tv_usec*1000; res = kevent(ps->kp_fd, NULL, 0, ps->res_events, max_res, &ts); #endif /* ----------------------------------------- */ } @@ -2049,8 +2178,7 @@ check_fd_events(ErtsPollSet ps, SysTimeval *tv, int max_res) #if ERTS_POLL_USE_WAKEUP_PIPE nfds++; /* Wakeup pipe */ #endif - if (timeout > INT_MAX) - timeout = INT_MAX; + timeout = (int) get_timeout(ps, 1000, timeout_time); poll_res.dp_nfds = nfds < max_res ? nfds : max_res; if (poll_res.dp_nfds > ps->res_events_len) grow_res_events(ps, poll_res.dp_nfds); @@ -2059,33 +2187,33 @@ check_fd_events(ErtsPollSet ps, SysTimeval *tv, int max_res) if (timeout) erts_thr_progress_prepare_wait(NULL); #endif - poll_res.dp_timeout = (int) timeout; + poll_res.dp_timeout = timeout; res = ioctl(ps->kp_fd, DP_POLL, &poll_res); #elif ERTS_POLL_USE_POLL /* --- poll -------------------------------- */ - if (timeout > INT_MAX) - timeout = INT_MAX; + timeout = (int) get_timeout(ps, 1000, timeout_time); #ifdef ERTS_SMP if (timeout) erts_thr_progress_prepare_wait(NULL); #endif - res = poll(ps->poll_fds, ps->no_poll_fds, (int) timeout); + res = poll(ps->poll_fds, ps->no_poll_fds, timeout); #elif ERTS_POLL_USE_SELECT /* --- select ------------------------------ */ - SysTimeval to = *tv; + SysTimeval to; + timeout = get_timeout_timeval(ps, &to, timeout_time); ERTS_FD_COPY(&ps->input_fds, &ps->res_input_fds); ERTS_FD_COPY(&ps->output_fds, &ps->res_output_fds); #ifdef ERTS_SMP - if (to.tv_sec || to.tv_usec) + if (timeout) erts_thr_progress_prepare_wait(NULL); #endif res = ERTS_SELECT(ps->max_fd + 1, - &ps->res_input_fds, - &ps->res_output_fds, - NULL, - &to); + &ps->res_input_fds, + &ps->res_output_fds, + NULL, + &to); #ifdef ERTS_SMP - if (to.tv_sec || to.tv_usec) + if (timeout) erts_thr_progress_finalize_wait(NULL); if (res < 0 && errno == EBADF @@ -2108,10 +2236,10 @@ check_fd_events(ErtsPollSet ps, SysTimeval *tv, int max_res) handle_update_requests(ps); ERTS_POLLSET_UNLOCK(ps); res = ERTS_SELECT(ps->max_fd + 1, - &ps->res_input_fds, - &ps->res_output_fds, - NULL, - &to); + &ps->res_input_fds, + &ps->res_output_fds, + NULL, + &to); if (res == 0) { errno = EAGAIN; res = -1; @@ -2133,15 +2261,14 @@ int ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps, ErtsPollResFd pr[], int *len, - SysTimeval *utvp) + ErtsMonotonicTime timeout_time) { + ErtsMonotonicTime to; int res, no_fds; int ebadf = 0; #ifdef ERTS_SMP int ps_locked = 0; #endif - SysTimeval *tvp; - SysTimeval itv; no_fds = *len; #ifdef ERTS_POLL_MAX_RES @@ -2151,13 +2278,9 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps, *len = 0; - ASSERT(utvp); - - tvp = utvp; - #ifdef ERTS_POLL_DEBUG_PRINT - erts_printf("Entering erts_poll_wait(), timeout=%d\n", - (int) tvp->tv_sec*1000 + tvp->tv_usec/1000); + erts_printf("Entering erts_poll_wait(), timeout_time=%bps\n", + timeout_time); #endif if (ERTS_POLLSET_SET_POLLED_CHK(ps)) { @@ -2166,12 +2289,9 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps, goto done; } - if (is_woken(ps)) { - /* Use zero timeout */ - itv.tv_sec = 0; - itv.tv_usec = 0; - tvp = &itv; - } + to = (is_woken(ps) + ? ERTS_POLL_NO_TIMEOUT /* Use zero timeout */ + : timeout_time); #if ERTS_POLL_USE_UPDATE_REQUESTS_QUEUE if (ERTS_POLLSET_HAVE_UPDATE_REQUESTS(ps)) { @@ -2181,7 +2301,7 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps, } #endif - res = check_fd_events(ps, tvp, no_fds); + res = check_fd_events(ps, to, no_fds); woke_up(ps); @@ -2224,7 +2344,7 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps, #endif done: - erts_smp_atomic32_set_relb(&ps->timeout, ERTS_AINT32_T_MAX); + set_timeout_time(ps, ERTS_MONOTONIC_TIME_MAX); #ifdef ERTS_POLL_DEBUG_PRINT erts_printf("Leaving %s = erts_poll_wait()\n", res == 0 ? "0" : erl_errno_id(res)); @@ -2268,13 +2388,14 @@ ERTS_POLL_EXPORT(erts_poll_async_sig_interrupt)(ErtsPollSet ps) void ERTS_POLL_EXPORT(erts_poll_interrupt_timed)(ErtsPollSet ps, int set, - erts_short_time_t msec) + ErtsMonotonicTime timeout_time) { #if ERTS_POLL_ASYNC_INTERRUPT_SUPPORT || defined(ERTS_SMP) if (!set) reset_wakeup_state(ps); else { - if (erts_smp_atomic32_read_acqb(&ps->timeout) > (erts_aint32_t) msec) + ErtsMonotonicTime max_wait_time = get_timeout_time(ps); + if (max_wait_time > timeout_time) wake_poller(ps, 1, 0); #ifdef ERTS_POLL_COUNT_AVOIDED_WAKEUPS else { @@ -2431,7 +2552,7 @@ ERTS_POLL_EXPORT(erts_poll_create_pollset)(void) ps->internal_fd_limit = kp_fd + 1; ps->kp_fd = kp_fd; #endif - erts_smp_atomic32_init_nob(&ps->timeout, ERTS_AINT32_T_MAX); + init_timeout_time(ps); #ifdef ERTS_POLL_COUNT_AVOIDED_WAKEUPS erts_smp_atomic_init_nob(&ps->no_avoided_wakeups, 0); erts_smp_atomic_init_nob(&ps->no_avoided_interrupts, 0); diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index 2f1c05f401..d02ed2396b 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -29,6 +29,8 @@ #include "sys.h" +#define ERTS_POLL_NO_TIMEOUT ERTS_MONOTONIC_TIME_MIN + #if 0 #define ERTS_POLL_COUNT_AVOIDED_WAKEUPS #endif @@ -241,7 +243,7 @@ void ERTS_POLL_EXPORT(erts_poll_interrupt)(ErtsPollSet, int); void ERTS_POLL_EXPORT(erts_poll_interrupt_timed)(ErtsPollSet, int, - erts_short_time_t); + ErtsMonotonicTime); ErtsPollEvents ERTS_POLL_EXPORT(erts_poll_control)(ErtsPollSet, ErtsSysFdType, ErtsPollEvents, @@ -254,7 +256,7 @@ void ERTS_POLL_EXPORT(erts_poll_controlv)(ErtsPollSet, int ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet, ErtsPollResFd [], int *, - SysTimeval *); + ErtsMonotonicTime); int ERTS_POLL_EXPORT(erts_poll_max_fds)(void); void ERTS_POLL_EXPORT(erts_poll_info)(ErtsPollSet, ErtsPollInfo *); diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c index 7d2a3d1e0b..36ee2557e8 100644 --- a/erts/emulator/sys/ose/erl_poll.c +++ b/erts/emulator/sys/ose/erl_poll.c @@ -114,7 +114,7 @@ struct ErtsPollSet_ { Uint item_count; PROCESS interrupt; erts_atomic32_t wakeup_state; - erts_smp_atomic32_t timeout; + erts_atomic64_t timeout_time; #ifdef ERTS_SMP erts_smp_mtx_t mtx; #endif @@ -122,6 +122,26 @@ struct ErtsPollSet_ { static int max_fds = -1; +static ERTS_INLINE void +init_timeout_time(ErtsPollSet ps) +{ + erts_atomic64_init_nob(&ps->timeout_time, + (erts_aint64_t) ERTS_MONOTONIC_TIME_MAX); +} + +static ERTS_INLINE void +set_timeout_time(ErtsPollSet ps, ErtsMonotonicTime time) +{ + erts_atomic64_set_relb(&ps->timeout_time, + (erts_aint64_t) time); +} + +static ERTS_INLINE ErtsMonotonicTime +get_timeout_time(ErtsPollSet ps) +{ + return (ErtsMonotonicTime) erts_atomic64_read_acqb(&ps->timeout_time); +} + #define ERTS_POLL_NOT_WOKEN ((erts_aint32_t) (1 << 0)) #define ERTS_POLL_WOKEN_INTR ((erts_aint32_t) (1 << 1)) #define ERTS_POLL_WOKEN_TIMEDOUT ((erts_aint32_t) (1 << 2)) @@ -386,12 +406,14 @@ void erts_poll_interrupt(ErtsPollSet ps,int set) { } -void erts_poll_interrupt_timed(ErtsPollSet ps,int set,erts_short_time_t msec) { +void erts_poll_interrupt_timed(ErtsPollSet ps, + int set, + ErtsTimeoutTime timeout_time) { HARDTRACEF("erts_poll_interrupt_timed called!\n"); if (!set) reset_interrupt(ps); - else if (erts_smp_atomic32_read_acqb(&ps->timeout) > (erts_aint32_t) msec) + else if (get_timeout_time(ps) > timeout_time) set_interrupt(ps); } @@ -453,12 +475,14 @@ done: } int erts_poll_wait(ErtsPollSet ps, - ErtsPollResFd pr[], - int *len, - SysTimeval *utvp) { + ErtsPollResFd pr[], + int *len, + ErtsMonotonicTime timeout_time) +{ int res = ETIMEDOUT, no_fds, currid = 0; OSTIME timeout; union SIGNAL *sig; + ErtsMonotonicTime current_time, diff_time, timeout; // HARDTRACEF("%ux: In erts_poll_wait",ps); if (ps->interrupt == (PROCESS)0) ps->interrupt = current_process(); @@ -472,16 +496,29 @@ int erts_poll_wait(ErtsPollSet ps, *len = 0; - ASSERT(utvp); + /* erts_printf("Entering erts_poll_wait(), timeout_time=%bps\n", + timeout_time); */ - /* erts_printf("Entering erts_poll_wait(), timeout=%d\n", - (int) utvp->tv_sec*1000 + utvp->tv_usec/1000); */ - - timeout = utvp->tv_sec*1000 + utvp->tv_usec/1000; + if (timeout_time == ERTS_POLL_NO_TIMEOUT) { + no_timeout: + timeout = (OSTIME) 0; + save_timeout_time = ERTS_MONOTONIC_TIME_MIN; + } + else { + ErtsMonotonicTime current_time, diff_time; + current_time = erts_get_monotonic_time(); + diff_time = timeout_time - current_time; + if (diff_time <= 0) + goto no_timeout; + diff_time = (ERTS_MONOTONIC_TO_MSEC(diff_time - 1) + 1); + if (diff_time > INT_MAX) + diff_time = INT_MAX; + timeout = (OSTIME) diff_time; + save_timeout_time = current_time; + save_timeout_time += ERTS_MSEC_TO_MONOTONIC(diff_time); + } - if (timeout > ((time_t) ERTS_AINT32_T_MAX)) - timeout = ERTS_AINT32_T_MAX; - erts_smp_atomic32_set_relb(&ps->timeout, (erts_aint32_t) timeout); + set_timeout_time(ps, save_timeout_time); while (currid < no_fds) { if (timeout > 0) { @@ -627,7 +664,7 @@ int erts_poll_wait(ErtsPollSet ps, } erts_atomic32_set_nob(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN); - erts_smp_atomic32_set_nob(&ps->timeout, ERTS_AINT32_T_MAX); + set_timeout_time(ps, ERTS_MONOTONIC_TIME_MAX); *len = currid; @@ -690,7 +727,7 @@ ErtsPollSet erts_poll_create_pollset(void) ps->info = NULL; ps->interrupt = (PROCESS)0; erts_atomic32_init_nob(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN); - erts_smp_atomic32_init_nob(&ps->timeout, ERTS_AINT32_T_MAX); + init_timeout_time(ps); #ifdef ERTS_SMP erts_smp_mtx_init(&ps->mtx, "pollset"); #endif diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index 5b950a7dae..13a5b71496 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -298,9 +298,9 @@ erts_sys_schedule_interrupt(int set) #ifdef ERTS_SMP void -erts_sys_schedule_interrupt_timed(int set, erts_short_time_t msec) +erts_sys_schedule_interrupt_timed(int set, ErtsMonotonicTime timeout_time) { - ERTS_CHK_IO_INTR_TMD(set, msec); + ERTS_CHK_IO_INTR_TMD(set, timeout_time); } #endif diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 26ed2fb558..5417bb2687 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -114,11 +114,6 @@ /* * Make sure that MAXPATHLEN is defined. */ -#ifdef GETHRTIME_WITH_CLOCK_GETTIME -#undef HAVE_GETHRTIME -#define HAVE_GETHRTIME 1 -#endif - #ifndef MAXPATHLEN # ifdef PATH_MAX # define MAXPATHLEN PATH_MAX @@ -160,33 +155,112 @@ typedef struct timeval SysTimeval; typedef struct tms SysTimes; -extern int erts_ticks_per_sec; - -#define SYS_CLK_TCK (erts_ticks_per_sec) +#define SYS_CLK_TCK (erts_sys_time_data__.r.o.ticks_per_sec) #define sys_times(Arg) times(Arg) -#define ERTS_WRAP_SYS_TIMES 1 -extern int erts_ticks_per_sec_wrap; -#define SYS_CLK_TCK_WRAP (erts_ticks_per_sec_wrap) -extern clock_t sys_times_wrap(void); +#if SIZEOF_LONG == 8 +typedef long ErtsMonotonicTime; +#elif SIZEOF_LONG_LONG == 8 +typedef long long ErtsMonotonicTime; +#else +#error No signed 64-bit type found... +#endif + +#define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63) +#define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN) + +/* + * OS monotonic time + */ + +/* + * Most common with os monotonic time using nano second + * time unit. These defines are modified below if this + * isn't the case... + */ +#define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1 +#define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000*1000) + +#undef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ +#undef ERTS_HAVE_CORRECTED_OS_MONOTONIC + +#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) + +#if defined(__linux__) -#ifdef HAVE_GETHRTIME -#ifdef GETHRTIME_WITH_CLOCK_GETTIME -typedef long long SysHrTime; +#define ERTS_HAVE_CORRECTED_OS_MONOTONIC 1 +#define ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ 1 -extern SysHrTime sys_gethrtime(void); -#define sys_init_hrtime() /* Nothing */ +#else /* !defined(__linux__) */ -#else /* Real gethrtime (Solaris) */ +ErtsMonotonicTime erts_os_monotonic_time(void); -typedef hrtime_t SysHrTime; +#endif /* !defined(__linux__) */ + +#elif defined(OS_MONOTONIC_TIME_USING_GETHRTIME) + +#define erts_os_monotonic() ((ErtsMonotonicTime) gethrtime()) + +#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ + || defined(OS_MONOTONIC_TIME_USING_TIMES) + +#if defined(OS_MONOTONIC_TIME_USING_TIMES) +# undef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT +# define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000) +# define ERTS_HAVE_ERTS_OS_TIME_OFFSET_FINALIZE 1 +void erts_os_time_offset_finalize(void); +# define ERTS_HAVE_ERTS_OS_MONOTONIC_TIME_INIT +void erts_os_monotonic_time_init(void); +#endif -#define sys_gethrtime() gethrtime() -#define sys_init_hrtime() /* Nothing */ +ErtsMonotonicTime erts_os_monotonic_time(void); -#endif /* GETHRTIME_WITH_CLOCK_GETTIME */ -#endif /* HAVE_GETHRTIME */ +#else /* No OS monotonic available... */ + +#undef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT +#undef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT +#define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000) + +#endif + +struct erts_sys_time_read_only_data__ { +#ifdef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ + ErtsMonotonicTime (*os_monotonic_time)(void); +#endif + int ticks_per_sec; +}; + +typedef struct { + union { + struct erts_sys_time_read_only_data__ o; + char align__[(((sizeof(struct erts_sys_time_read_only_data__) - 1) + / ASSUMED_CACHE_LINE_SIZE) + 1) + * ASSUMED_CACHE_LINE_SIZE]; + } r; +} ErtsSysTimeData__; + +extern ErtsSysTimeData__ erts_sys_time_data__; + +#ifdef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ + +ERTS_GLB_INLINE ErtsMonotonicTime erts_os_monotonic_time(void); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE ErtsMonotonicTime +erts_os_monotonic_time(void) +{ + return (*erts_sys_time_data__.r.o.os_monotonic_time)(); +} + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +#endif /* ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ */ + +/* + * + */ #if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME)) typedef long long SysCpuTime; diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 0d677d5f34..70f549a37a 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -280,7 +280,7 @@ struct { int (*event)(ErlDrvPort, ErlDrvEvent, ErlDrvEventData); void (*check_io_as_interrupt)(void); void (*check_io_interrupt)(int); - void (*check_io_interrupt_tmd)(int, erts_short_time_t); + void (*check_io_interrupt_tmd)(int, ErtsMonotonicTime); void (*check_io)(int); Uint (*size)(void); Eterm (*info)(void *); @@ -386,9 +386,9 @@ erts_sys_schedule_interrupt(int set) #ifdef ERTS_SMP void -erts_sys_schedule_interrupt_timed(int set, erts_short_time_t msec) +erts_sys_schedule_interrupt_timed(int set, ErtsMonotonicTime timeout_time) { - ERTS_CHK_IO_INTR_TMD(set, msec); + ERTS_CHK_IO_INTR_TMD(set, timeout_time); } #endif @@ -984,24 +984,6 @@ static void unblock_signals(void) #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ #endif } -/************************** Time stuff **************************/ -#ifdef HAVE_GETHRTIME -#ifdef GETHRTIME_WITH_CLOCK_GETTIME - -SysHrTime sys_gethrtime(void) -{ - struct timespec ts; - long long result; - if (clock_gettime(CLOCK_MONOTONIC,&ts) != 0) { - erl_exit(1,"Fatal, could not get clock_monotonic value!, " - "errno = %d\n", errno); - } - result = ((long long) ts.tv_sec) * 1000000000LL + - ((long long) ts.tv_nsec); - return (SysHrTime) result; -} -#endif -#endif /************************** OS info *******************************/ diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index fcce54a2c4..9fdb1930b7 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -53,9 +53,40 @@ /******************* Routines for time measurement *********************/ -int erts_ticks_per_sec = 0; /* Will be SYS_CLK_TCK in erl_unix_sys.h */ -int erts_ticks_per_sec_wrap = 0; /* Will be SYS_CLK_TCK_WRAP */ -static int ticks_bsr = 0; /* Shift wrapped tick value this much to the right */ +#undef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ +#undef ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ + +#if defined(OS_MONOTONIC_TIME_USING_TIMES) + +#define ERTS_WRAP_SYS_TIMES 1 +#define ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ +#define ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ + +/* + * Not sure there is a need to use times() anymore, perhaps drop + * support for this soon... + * + * sys_times() might need to be wrapped and the values shifted (right) + * a bit to cope with faster ticks, this has to be taken care + * of dynamically to start with, a special version that uses + * the times() return value as a high resolution timer can be made + * to fully utilize the faster ticks, like on windows, but for now, we'll + * settle with this silly workaround + */ +#ifdef ERTS_WRAP_SYS_TIMES +static clock_t sys_times_wrap(void); +#define KERNEL_TICKS() (sys_times_wrap() & \ + ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1)) +#define ERTS_KERNEL_TICK_TO_USEC(TCKS) (((TCKS)*(1000*1000)) \ + / internal_state.r.o.ticks_per_sec_wrap) +#else + +#define KERNEL_TICKS() (sys_times(&internal_state.w.f.dummy_tms) & \ + ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1)) +#define ERTS_KERNEL_TICK_TO_USEC(TCKS) (((TCKS)*(1000*1000))/SYS_CLK_TCK) +#endif + +#endif /* * init timers, chose a tick length, and return it. @@ -63,37 +94,374 @@ static int ticks_bsr = 0; /* Shift wrapped tick value this much to the right */ * does almost everything. Other platforms have to * emulate Unix in this sense. */ -int sys_init_time(void) + +ErtsSysTimeData__ erts_sys_time_data__ erts_align_attribute(ERTS_CACHE_LINE_SIZE); + +#if defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) + +#define ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ + +ErtsMonotonicTime clock_gettime_monotonic_raw(void); +ErtsMonotonicTime clock_gettime_monotonic_verified(void); + +#endif /* defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */ + +#ifdef ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ +struct sys_time_internal_state_read_only__ { +#if defined(OS_MONOTONIC_TIME_USING_TIMES) + int ticks_bsr; + int ticks_per_sec_wrap; +#endif +}; +#endif + +#ifdef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ +struct sys_time_internal_state_write_freq__ { + erts_smp_mtx_t mtx; +#if defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) + ErtsMonotonicTime last_delivered; +#endif +#if defined(OS_MONOTONIC_TIME_USING_TIMES) + ErtsMonotonicTime last_tick_count; + ErtsMonotonicTime last_tick_wrap_count; + ErtsMonotonicTime last_tick_monotonic_time; + ErtsMonotonicTime last_timeofday_usec; +#ifndef ERTS_WRAP_SYS_TIMES + SysTimes dummy_tms; +#endif +#endif +}; +#endif + +#if defined(ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__) \ + || defined(ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__) +static struct { +#ifdef ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ + union { + struct sys_time_internal_state_read_only__ o; + char align__[(((sizeof(struct sys_time_internal_state_read_only__) - 1) + / ASSUMED_CACHE_LINE_SIZE) + 1) + * ASSUMED_CACHE_LINE_SIZE]; + } r; +#endif +#ifdef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ + union { + struct sys_time_internal_state_write_freq__ f; + char align__[(((sizeof(struct sys_time_internal_state_write_freq__) - 1) + / ASSUMED_CACHE_LINE_SIZE) + 1) + * ASSUMED_CACHE_LINE_SIZE]; + } w; +#endif +} internal_state erts_align_attribute(ERTS_CACHE_LINE_SIZE); +#endif + +void +sys_init_time(ErtsSysInitTimeResult *init_resp) { +#if !defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) + + init_resp->have_os_monotonic = 0; + +#else /* defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) */ + + int major, minor, build, vsn; + + init_resp->os_monotonic_info.resolution = (Uint64) 1000*1000*1000; +#if defined(HAVE_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID) + { + struct timespec ts; + if (clock_getres(MONOTONIC_CLOCK_ID, &ts) == 0 + && ts.tv_sec == 0 && ts.tv_nsec != 0) { + init_resp->os_monotonic_info.resolution /= ts.tv_nsec; + } + } +#endif + +#ifdef MONOTONIC_CLOCK_ID_STR + init_resp->os_monotonic_info.clock_id = MONOTONIC_CLOCK_ID_STR; +#else + init_resp->os_monotonic_info.clock_id = NULL; +#endif + + init_resp->os_monotonic_info.locked_use = 0; + +#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) + init_resp->os_monotonic_info.func = "clock_gettime"; +#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) + init_resp->os_monotonic_info.func = "clock_get_time"; +#elif defined(OS_MONOTONIC_TIME_USING_GETHRTIME) + init_resp->os_monotonic_info.func = "gethrtime"; +#elif defined(OS_MONOTONIC_TIME_USING_TIMES) + init_resp->os_monotonic_info.func = "times"; + init_resp->os_monotonic_info.locked_use = 1; + init_resp->os_monotonic_info.resolution = TICKS_PER_SEC(); +#else +# error Unknown erts_os_monotonic_time() implementation +#endif + + init_resp->have_os_monotonic = 1; + + os_version(&major, &minor, &build); + + vsn = ERTS_MK_VSN_INT(major, minor, build); + + +#if defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) + if (vsn >= ERTS_MK_VSN_INT(2, 6, 33)) + erts_sys_time_data__.r.o.os_monotonic_time = + clock_gettime_monotonic_raw; + else { + /* + * Linux versions prior to 2.6.33 have a + * known bug that sometimes cause monotonic + * time to take small steps backwards. + */ + erts_sys_time_data__.r.o.os_monotonic_time = + clock_gettime_monotonic_verified; + erts_smp_mtx_init(&internal_state.w.f.mtx, + "os_monotonic_time"); + internal_state.w.f.last_delivered + = clock_gettime_monotonic_raw(); + init_resp->os_monotonic_info.locked_use = 1; + } +#else /* !(defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)) */ + { + char flavor[1024]; + + os_flavor(flavor, sizeof(flavor)); + + if (sys_strcmp(flavor, "sunos") == 0) { + /* + * Don't trust hrtime on multi processors + * on SunOS prior to SunOS 5.8 + */ + if (vsn < ERTS_MK_VSN_INT(5, 8, 0)) { +#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) + if (sysconf(_SC_NPROCESSORS_CONF) > 1) +#endif + init_resp->have_os_monotonic = 0; + } + } + } +#endif /* !(defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)) */ + +#endif /* defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) */ + + init_resp->os_monotonic_time_unit = ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT; + init_resp->sys_clock_resolution = SYS_CLOCK_RESOLUTION; + /* - * This (erts_ticks_per_sec) is only for times() (CLK_TCK), - * the resolution is always one millisecond.. + * This (erts_sys_time_data__.r.o.ticks_per_sec) is only for + * times() (CLK_TCK), the resolution is always one millisecond.. */ - if ((erts_ticks_per_sec = TICKS_PER_SEC()) < 0) - erl_exit(1, "Can't get clock ticks/sec\n"); - if (erts_ticks_per_sec >= 1000) { + if ((erts_sys_time_data__.r.o.ticks_per_sec = TICKS_PER_SEC()) < 0) + erl_exit(ERTS_ABORT_EXIT, "Can't get clock ticks/sec\n"); + +#if defined(OS_MONOTONIC_TIME_USING_TIMES) + + if (erts_sys_time_data__.r.o.ticks_per_sec >= 1000) { /* Workaround for beta linux kernels, need to be done in runtime to make erlang run on both 2.4 and 2.5 kernels. In the future, the kernel ticks might as well be used as a high res timer instead, but that's for when the majority uses kernels with HZ == 1024 */ - ticks_bsr = 3; + internal_state.r.o.ticks_bsr = 3; } else { - ticks_bsr = 0; + internal_state.r.o.ticks_bsr = 0; } - erts_ticks_per_sec_wrap = (erts_ticks_per_sec >> ticks_bsr); - return SYS_CLOCK_RESOLUTION; + + internal_state.r.o.ticks_per_sec_wrap + = (erts_sys_time_data__.r.o.ticks_per_sec + >> internal_state.r.o.ticks_bsr); + + erts_smp_mtx_init(&internal_state.w.f.mtx, "os_monotonic_time"); + internal_state.w.f.last_tick_count = KERNEL_TICKS(); + internal_state.w.f.last_tick_wrap_count = 0; + internal_state.w.f.last_tick_monotonic_time + = ERTS_KERNEL_TICK_TO_USEC(internal_state.w.f.last_tick_count); + { + SysTimeval tv; + sys_gettimeofday(&tv); + internal_state.w.f.last_timeofday_usec = tv.tv_sec*(1000*1000); + internal_state.w.f.last_timeofday_usec += tv.tv_usec; + } + +#endif /* defined(OS_MONOTONIC_TIME_USING_TIMES) */ + +} + +#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) + +static ERTS_INLINE ErtsMonotonicTime +clock_gettime_monotonic(void) +{ + ErtsMonotonicTime mtime; + struct timespec ts; + + if (clock_gettime(MONOTONIC_CLOCK_ID,&ts) != 0) { + int err = errno; + char *errstr = err ? strerror(err) : "unknown"; + erl_exit(ERTS_ABORT_EXIT, + "clock_gettime(%s, _) failed: %s (%d)\n", + MONOTONIC_CLOCK_ID_STR, errstr, err); + + } + mtime = (ErtsMonotonicTime) ts.tv_sec; + mtime *= (ErtsMonotonicTime) 1000*1000*1000; + mtime += (ErtsMonotonicTime) ts.tv_nsec; + return mtime; +} + +#if defined(__linux__) + +ErtsMonotonicTime clock_gettime_monotonic_verified(void) +{ + ErtsMonotonicTime mtime; + + mtime = clock_gettime_monotonic(); + + erts_smp_mtx_lock(&internal_state.w.f.mtx); + if (mtime < internal_state.w.f.last_delivered) + mtime = internal_state.w.f.last_delivered; + else + internal_state.w.f.last_delivered = mtime; + erts_smp_mtx_unlock(&internal_state.w.f.mtx); + + return mtime; +} + +ErtsMonotonicTime clock_gettime_monotonic_raw(void) +{ + return clock_gettime_monotonic(); } -clock_t sys_times_wrap(void) +#else /* !defined(__linux__) */ + +ErtsMonotonicTime erts_os_monotonic_time(void) +{ + return clock_gettime_monotonic(); +} + +#endif /* !defined(__linux__) */ + +#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) + +#include +#include + +ErtsMonotonicTime erts_os_monotonic_time(void) +{ + ErtsMonotonicTime mtime; + kern_return_t res; + clock_serv_t clk_srv; + mach_timespec_t time_spec; + int err; + + host_get_clock_service(mach_host_self(), + MONOTONIC_CLOCK_ID, + &clk_srv); + errno = 0; + res = clock_get_time(clk_srv, &time_spec); + err = errno; + mach_port_deallocate(mach_task_self(), clk_srv); + if (res != KERN_SUCCESS) { + char *errstr = err ? strerror(err) : "unknown"; + erl_exit(ERTS_ABORT_EXIT, + "clock_get_time(%s, _) failed: %s (%d)\n", + MONOTONIC_CLOCK_ID_STR, errstr, err); + } + + mtime = (ErtsMonotonicTime) time_spec.tv_sec; + mtime *= (ErtsMonotonicTime) 1000*1000*1000; + mtime += (ErtsMonotonicTime) time_spec.tv_nsec; + return mtime; +} + +#elif defined(OS_MONOTONIC_TIME_USING_TIMES) + +static clock_t sys_times_wrap(void) { SysTimes dummy; - clock_t result = (sys_times(&dummy) >> ticks_bsr); + clock_t result = (sys_times(&dummy) >> internal_state.r.o.ticks_bsr); return result; } +void +erts_os_time_offset_finalize(void) +{ + erts_smp_mtx_lock(&internal_state.w.f.mtx); + internal_state.w.f.last_tick_wrap_count = 0; + erts_smp_mtx_unlock(&internal_state.w.f.mtx); +} + +#define ERTS_TIME_EXCEED_TICK_LIMIT(SYS_TIME, TCK_TIME) \ + (((Uint64) (SYS_TIME)) - (((Uint64) (TCK_TIME)) \ + - ERTS_KERNEL_TICK_TO_USEC(1)) \ + > ERTS_KERNEL_TICK_TO_USEC(2)) + +/* Returns monotonic time in micro seconds */ +ErtsMonotonicTime +erts_os_monotonic_time(void) +{ + SysTimeval tv; + ErtsMonotonicTime res; + ErtsMonotonicTime tick_count; + ErtsMonotonicTime tick_count_usec; + ErtsMonotonicTime tick_monotonic_time; + ErtsMonotonicTime timeofday_usec; + ErtsMonotonicTime timeofday_diff_usec; + + erts_smp_mtx_lock(&internal_state.w.f.mtx); + tick_count = (ErtsMonotonicTime) KERNEL_TICKS(); + sys_gettimeofday(&tv); + + if (internal_state.w.f.last_tick_count > tick_count) { + internal_state.w.f.last_tick_wrap_count + += (((ErtsMonotonicTime) 1) << ((sizeof(clock_t) * 8) - 1)); + } + internal_state.w.f.last_tick_count = tick_count; + tick_count += internal_state.w.f.last_tick_wrap_count; + + tick_count_usec = ERTS_KERNEL_TICK_TO_USEC(tick_count); + + timeofday_usec = (ErtsMonotonicTime) tv.tv_sec*(1000*1000); + timeofday_usec += (ErtsMonotonicTime) tv.tv_usec; + timeofday_diff_usec = timeofday_usec; + timeofday_diff_usec -= internal_state.w.f.last_timeofday_usec; + internal_state.w.f.last_timeofday_usec = timeofday_usec; + + if (timeofday_diff_usec < 0) { + /* timeofday jumped backwards use tick count only... */ + tick_monotonic_time = tick_count_usec; + } + else { + /* Use time diff from of timeofday if not off by too much... */ + tick_monotonic_time = internal_state.w.f.last_tick_monotonic_time; + tick_monotonic_time += timeofday_diff_usec; + + if (ERTS_TIME_EXCEED_TICK_LIMIT(tick_monotonic_time, tick_count_usec)) { + /* + * Value off by more than one tick from tick_count, i.e. + * timofday leaped one way or the other. We use + * tick_count_usec as is instead and unfortunately + * get lousy precision. + */ + tick_monotonic_time = tick_count_usec; + } + } + + if (internal_state.w.f.last_tick_monotonic_time < tick_monotonic_time) + internal_state.w.f.last_tick_monotonic_time = tick_monotonic_time; + + res = internal_state.w.f.last_tick_monotonic_time; + + erts_smp_mtx_unlock(&internal_state.w.f.mtx); + + return res; +} +#endif /* !defined(OS_MONOTONIC_TIME_USING_TIMES) */ #ifdef HAVE_GETHRVTIME_PROCFS_IOCTL diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 972170d465..5a62b00a68 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -285,7 +285,7 @@ struct ErtsPollSet_ { #ifdef ERTS_SMP erts_smp_mtx_t mtx; #endif - erts_smp_atomic32_t timeout; + erts_atomic64_t timeout_time; }; #ifdef ERTS_SMP @@ -363,6 +363,26 @@ do { \ wait_standby(PS); \ } while(0) +static ERTS_INLINE void +init_timeout_time(ErtsPollSet ps) +{ + erts_atomic64_init_nob(&ps->timeout_time, + (erts_aint64_t) ERTS_MONOTONIC_TIME_MAX); +} + +static ERTS_INLINE void +set_timeout_time(ErtsPollSet ps, ErtsMonotonicTime time) +{ + erts_atomic64_set_relb(&ps->timeout_time, + (erts_aint64_t) time); +} + +static ERTS_INLINE ErtsMonotonicTime +get_timeout_time(ErtsPollSet ps) +{ + return (ErtsMonotonicTime) erts_atomic64_read_acqb(&ps->timeout_time); +} + #define ERTS_POLL_NOT_WOKEN ((erts_aint32_t) 0) #define ERTS_POLL_WOKEN_IO_READY ((erts_aint32_t) 1) #define ERTS_POLL_WOKEN_INTR ((erts_aint32_t) 2) @@ -422,15 +442,29 @@ wakeup_cause(ErtsPollSet ps) } static ERTS_INLINE DWORD -poll_wait_timeout(ErtsPollSet ps, SysTimeval *tvp) +poll_wait_timeout(ErtsPollSet ps, ErtsMonotonicTime timeout_time) { - time_t timeout = tvp->tv_sec * 1000 + tvp->tv_usec / 1000; + ErtsMonotonicTime current_time, diff_time, timeout; - if (timeout <= 0) { + if (timeout_time == ERTS_POLL_NO_TIMEOUT) { + no_timeout: + set_timeout_time(ps, ERTS_MONOTONIC_TIME_MIN); woke_up(ps); return (DWORD) 0; } + current_time = erts_get_monotonic_time(); + diff_time = timeout_time - current_time; + if (diff_time <= 0) + goto no_timeout; + + /* Round up to nearest milli second */ + timeout = (ERTS_MONOTONIC_TO_MSEC(diff_time - 1) + 1); + if (timeout > INT_MAX) + timeout = INT_MAX; /* Also prevents DWORD overflow */ + + set_timeout_time(ps, current_time + ERTS_MSEC_TO_MONOTONIC(timeout)); + ResetEvent(ps->event_io_ready); /* * Since we don't know the internals of ResetEvent() we issue @@ -442,10 +476,6 @@ poll_wait_timeout(ErtsPollSet ps, SysTimeval *tvp) if (erts_atomic32_read_nob(&ps->wakeup_state) != ERTS_POLL_NOT_WOKEN) return (DWORD) 0; - if (timeout > ((time_t) ERTS_AINT32_T_MAX)) - timeout = ERTS_AINT32_T_MAX; /* Also prevents DWORD overflow */ - - erts_smp_atomic32_set_relb(&ps->timeout, (erts_aint32_t) timeout); return (DWORD) timeout; } @@ -1012,12 +1042,12 @@ void erts_poll_interrupt(ErtsPollSet ps, int set /* bool */) void erts_poll_interrupt_timed(ErtsPollSet ps, int set /* bool */, - erts_short_time_t msec) + ErtsMonotonicTime timeout_time) { - HARDTRACEF(("In erts_poll_interrupt_timed(%d,%ld)",set,msec)); + HARDTRACEF(("In erts_poll_interrupt_timed(%d,%ld)",set,timeout_time)); if (!set) reset_interrupt(ps); - else if (erts_smp_atomic32_read_acqb(&ps->timeout) > (erts_aint32_t) msec) + else if (get_timeout_time(ps) > timeout_time) set_interrupt(ps); HARDTRACEF(("Out erts_poll_interrupt_timed")); } @@ -1092,7 +1122,7 @@ void erts_poll_controlv(ErtsPollSet ps, int erts_poll_wait(ErtsPollSet ps, ErtsPollResFd pr[], int *len, - SysTimeval *tvp) + ErtsMonotonicTime timeout_time) { int no_fds; DWORD timeout; @@ -1149,7 +1179,7 @@ int erts_poll_wait(ErtsPollSet ps, no_fds = ERTS_POLL_MAX_RES; #endif - timeout = poll_wait_timeout(ps, tvp); + timeout = poll_wait_timeout(ps, timeout_time); /*HARDDEBUGF(("timeout = %ld",(long) timeout));*/ @@ -1242,7 +1272,7 @@ int erts_poll_wait(ErtsPollSet ps, erts_mtx_unlock(&w->mtx); } done: - erts_smp_atomic32_set_nob(&ps->timeout, ERTS_AINT32_T_MAX); + set_timeout_time(ps, ERTS_MONOTONIC_TIME_MAX); *len = num; ERTS_POLLSET_UNLOCK(ps); HARDTRACEF(("Out erts_poll_wait")); @@ -1326,7 +1356,7 @@ ErtsPollSet erts_poll_create_pollset(void) #ifdef ERTS_SMP erts_smp_mtx_init(&ps->mtx, "pollset"); #endif - erts_smp_atomic32_init_nob(&ps->timeout, ERTS_AINT32_T_MAX); + init_timeout_time(ps); HARDTRACEF(("Out erts_poll_create_pollset")); return ps; diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 838f0c61eb..f04bb6a0e5 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -120,9 +120,6 @@ /* * For erl_time_sup */ -#define HAVE_GETHRTIME - -#define sys_init_hrtime() /* Nothing */ #define SYS_CLK_TCK 1000 #define SYS_CLOCK_RESOLUTION 1 @@ -164,18 +161,58 @@ typedef struct { #if defined (__GNUC__) typedef unsigned long long Uint64; typedef long long Sint64; - -typedef long long SysHrTime; +# ifdef ULLONG_MAX +# define ERTS_UINT64_MAX ULLONG_MAX +# endif +# ifdef LLONG_MAX +# define ERTS_SINT64_MAX LLONG_MAX +# endif +# ifdef LLONG_MIN +# define ERTS_SINT64_MIN LLONG_MIN +# endif + +typedef long long ErtsMonotonicTime; #else typedef ULONGLONG Uint64; typedef LONGLONG Sint64; -typedef LONGLONG SysHrTime; +typedef LONGLONG ErtsMonotonicTime; #endif -extern int sys_init_time(void); +#define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63) +#define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN) + +#define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1 +#define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT 0 + +struct erts_sys_time_read_only_data__ { + ErtsMonotonicTime (*os_monotonic_time)(void); +}; + +typedef struct { + union { + struct erts_sys_time_read_only_data__ o; + char align__[(((sizeof(struct erts_sys_time_read_only_data__) - 1) + / ASSUMED_CACHE_LINE_SIZE) + 1) + * ASSUMED_CACHE_LINE_SIZE]; + } r; +} ErtsSysTimeData__; + +extern ErtsSysTimeData__ erts_sys_time_data__; + +ERTS_GLB_INLINE ErtsMonotonicTime erts_os_monotonic_time(void); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE ErtsMonotonicTime +erts_os_monotonic_time(void) +{ + return (*erts_sys_time_data__.r.o.os_monotonic_time)(); +} + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + extern void sys_gettimeofday(SysTimeval *tv); -extern SysHrTime sys_gethrtime(void); extern clock_t sys_times(SysTimes *buffer); extern char *win_build_environment(char *); diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 0ded6b274e..8eed1c6d9b 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -3258,9 +3258,9 @@ erts_sys_schedule_interrupt(int set) #ifdef ERTS_SMP void -erts_sys_schedule_interrupt_timed(int set, erts_short_time_t msec) +erts_sys_schedule_interrupt_timed(int set, ErtsMonotonicTime timeout_time) { - erts_check_io_interrupt_timed(set, msec); + erts_check_io_interrupt_timed(set, timeout_time); } #endif diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index b84c8f85ce..3a10125c81 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -61,11 +61,6 @@ (epoch) = ((ull.QuadPart / TICKS_PER_SECOND) - EPOCH_JULIAN_DIFF); \ } while(0) -static SysHrTime wrap = 0; -static DWORD last_tick_count = 0; -static erts_smp_mtx_t wrap_lock; -static ULONGLONG (WINAPI *pGetTickCount64)(void) = NULL; - /* Getting timezone information is a heavy operation, so we want to do this only once */ @@ -76,17 +71,161 @@ static int days_in_month[2][13] = { {0,31,28,31,30,31,30,31,31,30,31,30,31}, {0,31,29,31,30,31,30,31,31,30,31,30,31}}; -int -sys_init_time(void) +/* + * erts_os_monotonic_time() + */ + +struct sys_time_internal_state_read_only__ { + ULONGLONG (WINAPI *pGetTickCount64)(void); + BOOL (WINAPI *pQueryPerformanceCounter)(LARGE_INTEGER *); +}; + +struct sys_time_internal_state_write_freq__ { + erts_smp_mtx_t mtime_mtx; + ULONGLONG wrap; + ULONGLONG last_tick_count; +}; + +__declspec(align(ASSUMED_CACHE_LINE_SIZE)) struct { + union { + struct sys_time_internal_state_read_only__ o; + char align__[(((sizeof(struct sys_time_internal_state_read_only__) - 1) + / ASSUMED_CACHE_LINE_SIZE) + 1) + * ASSUMED_CACHE_LINE_SIZE]; + } r; + union { + struct sys_time_internal_state_write_freq__ f; + char align__[(((sizeof(struct sys_time_internal_state_write_freq__) - 1) + / ASSUMED_CACHE_LINE_SIZE) + 1) + * ASSUMED_CACHE_LINE_SIZE]; + } w; +} internal_state; + +__declspec(align(ASSUMED_CACHE_LINE_SIZE)) ErtsSysTimeData__ erts_sys_time_data__; + +static ErtsMonotonicTime +os_monotonic_time_qpc(void) { + LARGE_INTEGER pc; + + if (!(*internal_state.r.o.pQueryPerformanceCounter)(&pc)) + erl_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n"); + + return (ErtsMonotonicTime) pc.QuadPart; +} + +static ErtsMonotonicTime +os_monotonic_time_gtc32(void) +{ + ULONGLONG res, ticks; + + erts_smp_mtx_lock(&internal_state.w.f.mtime_mtx); + + ticks = (ULONGLONG) (GetTickCount() & 0x7FFFFFFF); + if (ticks < internal_state.w.f.last_tick_count) + internal_state.w.f.wrap += (ULONGLONG) LL_LITERAL(1) << 31; + internal_state.w.f.last_tick_count = ticks; + res = ticks + internal_state.w.f.wrap; + + erts_smp_mtx_unlock(&internal_state.w.f.mtime_mtx); + + return (ErtsMonotonicTime) res*1000; +} + +static ErtsMonotonicTime +os_monotonic_time_gtc64(void) +{ + ULONGLONG ticks = (*internal_state.r.o.pGetTickCount64)(); + return (ErtsMonotonicTime) ticks*1000; +} + +/* + * Init + */ + +void +sys_init_time(ErtsSysInitTimeResult *init_resp) +{ + ErtsMonotonicTime (*os_mtime_func)(void); + ErtsMonotonicTime time_unit; char kernel_dll_name[] = "kernel32"; HMODULE module; + init_resp->os_monotonic_info.clock_id = NULL; + module = GetModuleHandle(kernel_dll_name); - pGetTickCount64 = (module != NULL) ? - (ULONGLONG (WINAPI *)(void)) - GetProcAddress(module,"GetTickCount64") : - NULL; + if (!module) { + get_tick_count: + erts_smp_mtx_init(&internal_state.w.f.mtime_mtx, + "os_monotonic_time"); + internal_state.w.f.wrap = 0; + internal_state.w.f.last_tick_count = 0; + + init_resp->os_monotonic_info.func = "GetTickCount"; + init_resp->os_monotonic_info.locked_use = 1; + init_resp->os_monotonic_info.resolution = 1000; + time_unit = (ErtsMonotonicTime) 1000*1000; + os_mtime_func = os_monotonic_time_gtc32; + } + else { + int major, minor, build; + + os_version(&major, &minor, &build); + + if (major < 6) { + + get_tick_count64: + + internal_state.r.o.pGetTickCount64 + = ((ULONGLONG (WINAPI *)(void)) + GetProcAddress(module, "GetTickCount64")); + if (!internal_state.r.o.pGetTickCount64) + goto get_tick_count; + + init_resp->os_monotonic_info.func = "GetTickCount64"; + init_resp->os_monotonic_info.locked_use = 0; + init_resp->os_monotonic_info.resolution = 1000; + time_unit = (ErtsMonotonicTime) 1000*1000; + os_mtime_func = os_monotonic_time_gtc64; + } + else { /* Vista or newer... */ + + LARGE_INTEGER pf; + BOOL (WINAPI *QPF)(LARGE_INTEGER *); + + QPF = ((BOOL (WINAPI *)(LARGE_INTEGER *)) + GetProcAddress(module, "QueryPerformanceFrequency")); + if (!QPF) + goto get_tick_count64; + if (!(*QPF)(&pf)) + goto get_tick_count64; + /* + * We only use QueryPerformanceCounter() if + * its frequency is equal to, or larger than + * GHz in order to ensure that the user wont + * be able to observe faulty order between + * values retrieved on different threads. + */ + if (pf.QuadPart < (LONGLONG) 1000*1000*1000) + goto get_tick_count64; + internal_state.r.o.pQueryPerformanceCounter + = ((BOOL (WINAPI *)(LARGE_INTEGER *)) + GetProcAddress(module, "QueryPerformanceCounter")); + if (!internal_state.r.o.pQueryPerformanceCounter) + goto get_tick_count64; + + init_resp->os_monotonic_info.func = "QueryPerformanceCounter"; + init_resp->os_monotonic_info.locked_use = 0; + time_unit = (ErtsMonotonicTime) pf.QuadPart; + init_resp->os_monotonic_info.resolution = time_unit; + os_mtime_func = os_monotonic_time_qpc; + } + } + + erts_sys_time_data__.r.o.os_monotonic_time = os_mtime_func; + init_resp->os_monotonic_time_unit = time_unit; + init_resp->have_os_monotonic = 1; + init_resp->sys_clock_resolution = 1; if(GetTimeZoneInformation(&static_tzi) && static_tzi.StandardDate.wMonth != 0 && @@ -94,9 +233,6 @@ sys_init_time(void) have_static_tzi = 1; } - erts_smp_mtx_init(&wrap_lock, "sys_gethrtime"); - - return 1; } /* Returns a switchtimes for DST as UTC filetimes given data from a @@ -377,41 +513,6 @@ sys_gettimeofday(SysTimeval *tv) EPOCH_JULIAN_DIFF); } -extern int erts_initialized; -SysHrTime -sys_gethrtime(void) -{ - if (pGetTickCount64 != NULL) { - return ((SysHrTime) pGetTickCount64()) * LL_LITERAL(1000000); - } else { - DWORD ticks; - SysHrTime res; - erts_smp_mtx_lock(&wrap_lock); - ticks = (SysHrTime) (GetTickCount() & 0x7FFFFFFF); - if (ticks < (SysHrTime) last_tick_count) { - /* Detect a race that should no longer be here... */ - if ((((SysHrTime) last_tick_count) - ((SysHrTime) ticks)) > 1000) { - wrap += LL_LITERAL(1) << 31; - } else { - /* - * XXX Debug: Violates locking order, remove all this, - * after testing! - */ - erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - erts_dsprintf(dsbufp, "Did not wrap when last_tick %d " - "and tick %d", - last_tick_count, ticks); - erts_send_error_to_logger_nogl(dsbufp); - ticks = last_tick_count; - } - } - last_tick_count = ticks; - res = ((((LONGLONG) ticks) + wrap) * LL_LITERAL(1000000)); - erts_smp_mtx_unlock(&wrap_lock); - return res; - } -} - clock_t sys_times(SysTimes *buffer) { clock_t kernel_ticks = (GetTickCount() / diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index dfbe47786a..dd2e2cb504 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -108,6 +108,7 @@ MODULES= \ trace_call_time_SUITE \ scheduler_SUITE \ old_scheduler_SUITE \ + unique_SUITE \ z_SUITE \ old_mod \ long_timers_test \ diff --git a/erts/emulator/test/long_timers_test.erl b/erts/emulator/test/long_timers_test.erl index 28a4fba9f6..f381332b51 100644 --- a/erts/emulator/test/long_timers_test.erl +++ b/erts/emulator/test/long_timers_test.erl @@ -28,7 +28,7 @@ -define(MAX_TIMEOUT, 60). % Minutes --define(MAX_LATE, 10*1000). % Milliseconds +-define(MAX_LATE_MS, 10*1000). % Milliseconds -define(REG_NAME, '___LONG___TIMERS___TEST___SERVER___'). -define(DRV_NAME, timer_driver). @@ -75,7 +75,7 @@ check_result() -> erlang:demonitor(Mon), receive {'DOWN', Mon, _, _, _} -> ok after 0 -> ok end, stop_node(Node), - check(TORs, (timer:now_diff(End, Start) div 1000) - ?MAX_LATE, ok) + check(TORs, ms((End - Start) - max_late()), ok) end. check([#timeout_rec{timeout = Timeout, @@ -83,7 +83,7 @@ check([#timeout_rec{timeout = Timeout, timeout_diff = undefined} | TORs], NeedRes, _Ok) when Timeout < NeedRes -> - io:format("~p timeout = ~p failed! No timeout.~n", + io:format("~p timeout = ~p ms failed! No timeout.~n", [Type, Timeout]), check(TORs, NeedRes, failed); check([#timeout_rec{timeout_diff = undefined} | TORs], @@ -95,7 +95,7 @@ check([#timeout_rec{timeout = Timeout, timeout_diff = {error, Reason}} | TORs], NeedRes, _Ok) -> - io:format("~p timeout = ~p failed! exit reason ~p~n", + io:format("~p timeout = ~p ms failed! exit reason ~p~n", [Type, Timeout, Reason]), check(TORs, NeedRes, failed); check([#timeout_rec{timeout = Timeout, @@ -103,43 +103,77 @@ check([#timeout_rec{timeout = Timeout, timeout_diff = TimeoutDiff} | TORs], NeedRes, Ok) -> - case (0 =< TimeoutDiff) and (TimeoutDiff =< ?MAX_LATE) of - true -> - io:format("~p timeout = ~p succeded! timeout diff = ~p.~n", - [Type, Timeout, TimeoutDiff]), - check(TORs, NeedRes, Ok); - false -> - io:format("~p timeout = ~p failed! timeout diff = ~p.~n", - [Type, Timeout, TimeoutDiff]), - check(TORs, NeedRes, failed) - end; + {NewOk, SuccessStr} = case ((0 =< TimeoutDiff) + andalso (TimeoutDiff =< max_late())) of + true -> {Ok, "succeeded"}; + false -> {failed, "FAILED"} + end, + io:format("~s timeout = ~s ms ~s! timeout diff = ~s.~n", + [type_str(Type), + time_str(Timeout), + SuccessStr, + time_str(TimeoutDiff, erlang:convert_time_unit(1, seconds, native))]), + check(TORs, NeedRes, NewOk); check([], _NeedRes, Ok) -> Ok. +type_str(receive_after) -> "receive ... after"; +type_str(bif_timer) -> "BIF timer"; +type_str(driver) -> "driver". + +time_str(Time, Unit) -> + lists:flatten([time_str(Time), " ", unit_str(Unit)]). + +time_str(Time) -> + lists:reverse(conv_time_str(lists:reverse(integer_to_list(Time)))). + +conv_time_str([X,Y,Z,C|Cs]) when C /= $- -> + [X,Y,Z,$`|conv_time_str([C|Cs])]; +conv_time_str(Cs) -> + Cs. + +unit_str(1) -> "s"; +unit_str(1000) -> "ms"; +unit_str(1000000) -> "us"; +unit_str(1000000000) -> "ns"; +unit_str(Res) when is_integer(Res) -> ["/ ", integer_to_list(Res), " s"]; +unit_str(Res) -> Res. + +to_diff(Timeout, Start, Stop) -> + %% 'Timeout' in milli seconds + %% 'Start', 'Stop', and result in native unit + (Stop - Start) - erlang:convert_time_unit(Timeout, milli_seconds, native). + +ms(Time) -> + erlang:convert_time_unit(Time, native, milli_seconds). + +max_late() -> + erlang:convert_time_unit(?MAX_LATE_MS, milli_seconds, native). + receive_after(Timeout) -> - Start = now(), + Start = erlang:monotonic_time(), receive {get_result, ?REG_NAME} -> ?REG_NAME ! #timeout_rec{pid = self(), type = receive_after, timeout = Timeout} after Timeout -> - Stop = now(), + Stop = erlang:monotonic_time(), receive {get_result, ?REG_NAME} -> - TimeoutDiff = ((timer:now_diff(Stop, Start) div 1000) - - Timeout), ?REG_NAME ! #timeout_rec{pid = self(), type = receive_after, timeout = Timeout, - timeout_diff = TimeoutDiff} + timeout_diff = to_diff(Timeout, + Start, + Stop)} end end. driver(Timeout) -> Port = open_port({spawn, ?DRV_NAME},[]), link(Port), - Start = now(), + Start = erlang:monotonic_time(), erlang:port_command(Port, <>), receive {get_result, ?REG_NAME} -> @@ -147,38 +181,38 @@ driver(Timeout) -> type = driver, timeout = Timeout}; {Port,{data,[?TIMER]}} -> - Stop = now(), + Stop = erlang:monotonic_time(), unlink(Port), true = erlang:port_close(Port), receive {get_result, ?REG_NAME} -> - TimeoutDiff = ((timer:now_diff(Stop, Start) div 1000) - - Timeout), ?REG_NAME ! #timeout_rec{pid = self(), type = driver, timeout = Timeout, - timeout_diff = TimeoutDiff} + timeout_diff = to_diff(Timeout, + Start, + Stop)} end end. bif_timer(Timeout) -> Tmr = erlang:start_timer(Timeout, self(), ok), - Start = now(), + Start = erlang:monotonic_time(), receive {get_result, ?REG_NAME} -> ?REG_NAME ! #timeout_rec{pid = self(), type = bif_timer, timeout = Timeout}; {timeout, Tmr, ok} -> - Stop = now(), + Stop = erlang:monotonic_time(), receive {get_result, ?REG_NAME} -> - TimeoutDiff = ((timer:now_diff(Stop, Start) div 1000) - - Timeout), ?REG_NAME ! #timeout_rec{pid = self(), type = bif_timer, timeout = Timeout, - timeout_diff = TimeoutDiff} + timeout_diff = to_diff(Timeout, + Start, + Stop)} end end. @@ -189,7 +223,7 @@ test(Starter, DrvDir, StartDone) -> register(?REG_NAME, self()), {group_leader, GL} = process_info(whereis(net_kernel),group_leader), group_leader(GL, self()), - Start = now(), + Start = erlang:monotonic_time(), TORs = lists:map(fun (Min) -> TO = Min*60*1000, [#timeout_rec{pid = spawn_opt( @@ -222,7 +256,7 @@ test(Starter, DrvDir, StartDone) -> test_loop(TORs, Start) -> receive {get_result, ?REG_NAME, Pid} -> - End = now(), + End = erlang:monotonic_time(), Pid ! {result, ?REG_NAME, get_test_results(TORs), Start, End}, erl_ddll:unload_driver(?DRV_NAME), erl_ddll:stop(), diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index aec59867d8..07e2862b2a 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -26,7 +26,8 @@ case_1/1, case_1a/1, case_2/1, case_2a/1, mon_e_1/1, demon_e_1/1, demon_1/1, demon_2/1, demon_3/1, demonitor_flush/1, local_remove_monitor/1, remote_remove_monitor/1, mon_1/1, mon_2/1, - large_exit/1, list_cleanup/1, mixer/1, named_down/1, otp_5827/1]). + large_exit/1, list_cleanup/1, mixer/1, named_down/1, otp_5827/1, + monitor_time_offset/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -38,7 +39,8 @@ all() -> [case_1, case_1a, case_2, case_2a, mon_e_1, demon_e_1, demon_1, mon_1, mon_2, demon_2, demon_3, demonitor_flush, {group, remove_monitor}, large_exit, - list_cleanup, mixer, named_down, otp_5827]. + list_cleanup, mixer, named_down, otp_5827, + monitor_time_offset]. groups() -> [{remove_monitor, [], @@ -59,7 +61,7 @@ end_per_group(_GroupName, Config) -> init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> Dog=?t:timetrap(?t:minutes(15)), - [{watchdog, Dog}|Config]. + [{watchdog, Dog},{testcase, Func}|Config]. end_per_testcase(_Func, Config) -> Dog=?config(watchdog, Config), @@ -837,6 +839,89 @@ otp_5827(Config) when is_list(Config) -> ?line ?t:fail("erlang:monitor/2 hangs") end. +monitor_time_offset(Config) when is_list(Config) -> + {ok, Node} = start_node(Config, "+C single_time_warp"), + Me = self(), + PMs = lists:map(fun (_) -> + Pid = spawn(Node, + fun () -> + check_monitor_time_offset(Me) + end), + {Pid, erlang:monitor(process, Pid)} + end, + lists:seq(1, 100)), + lists:foreach(fun ({P, _M}) -> + P ! check_no_change_message + end, PMs), + lists:foreach(fun ({P, M}) -> + receive + {no_change_message_received, P} -> + ok; + {'DOWN', M, process, P, Reason} -> + ?t:fail(Reason) + end + end, PMs), + preliminary = rpc:call(Node, erlang, system_flag, [time_offset, finalize]), + lists:foreach(fun ({P, M}) -> + receive + {change_messages_received, P} -> + erlang:demonitor(M, [flush]); + {'DOWN', M, process, P, Reason} -> + ?t:fail(Reason) + end + end, PMs), + stop_node(Node), + ok. + +check_monitor_time_offset(Leader) -> + Mon1 = erlang:monitor(time_offset, clock_service), + Mon2 = erlang:monitor(time_offset, clock_service), + Mon3 = erlang:monitor(time_offset, clock_service), + Mon4 = erlang:monitor(time_offset, clock_service), + + erlang:demonitor(Mon2, [flush]), + + Mon5 = erlang:monitor(time_offset, clock_service), + Mon6 = erlang:monitor(time_offset, clock_service), + Mon7 = erlang:monitor(time_offset, clock_service), + + receive check_no_change_message -> ok end, + receive + {'CHANGE', _, time_offset, clock_service, _} -> + exit(unexpected_change_message_received) + after 0 -> + Leader ! {no_change_message_received, self()} + end, + receive after 100 -> ok end, + erlang:demonitor(Mon4, [flush]), + receive + {'CHANGE', Mon3, time_offset, clock_service, _} -> + ok + end, + receive + {'CHANGE', Mon6, time_offset, clock_service, _} -> + ok + end, + erlang:demonitor(Mon5, [flush]), + receive + {'CHANGE', Mon7, time_offset, clock_service, _} -> + ok + end, + receive + {'CHANGE', Mon1, time_offset, clock_service, _} -> + ok + end, + receive + {'CHANGE', _, time_offset, clock_service, _} -> + exit(unexpected_change_message_received) + after 1000 -> + ok + end, + Leader ! {change_messages_received, self()}. + +%% +%% ... +%% wait_for_m(_,_,0) -> exit(monitor_wait_timeout); @@ -959,3 +1044,25 @@ generate(_Fun, 0) -> []; generate(Fun, N) -> [Fun() | generate(Fun, N-1)]. + +start_node(Config) -> + start_node(Config, ""). + +start_node(Config, Args) -> + TestCase = ?config(testcase, Config), + PA = filename:dirname(code:which(?MODULE)), + ESTime = erlang:monotonic_time(1) + erlang:time_offset(1), + Unique = erlang:unique_integer([positive]), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(TestCase) + ++ "-" + ++ integer_to_list(ESTime) + ++ "-" + ++ integer_to_list(Unique)), + test_server:start_node(Name, + slave, + [{args, "-pa " ++ PA ++ " " ++ Args}]). + +stop_node(Node) -> + test_server:stop_node(Node). diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index a0a8a9c42c..43f7ac7f7c 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -34,7 +34,14 @@ bad_univ_to_local/1, bad_local_to_univ/1, univ_to_seconds/1, seconds_to_univ/1, consistency/1, - now_unique/1, now_update/1, timestamp/1]). + now_unique/1, now_update/1, timestamp/1, + time_warp_modes/1, + monotonic_time_monotonicity/1, + time_unit_conversion/1, + signed_time_unit_conversion/1, + erlang_timestamp/1]). + +-export([init_per_testcase/2, end_per_testcase/2]). -export([local_to_univ_utc/1]). @@ -56,6 +63,12 @@ -define(dst_timezone, 2). +init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> + [{testcase, Func}|Config]. + +end_per_testcase(_Func, Config) -> + ok. + suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> @@ -63,7 +76,12 @@ all() -> bad_univ_to_local, bad_local_to_univ, univ_to_seconds, seconds_to_univ, consistency, - {group, now}, timestamp]. + {group, now}, timestamp, + time_warp_modes, + monotonic_time_monotonicity, + time_unit_conversion, + signed_time_unit_conversion, + erlang_timestamp]. groups() -> [{now, [], [now_unique, now_update]}]. @@ -420,6 +438,368 @@ now_update1(N) when N > 0 -> now_update1(0) -> ?line test_server:fail(). +time_warp_modes(Config) when is_list(Config) -> + %% All time warp modes always supported in + %% combination with no time correction... + check_time_warp_mode(Config, false, no_time_warp), + check_time_warp_mode(Config, false, single_time_warp), + check_time_warp_mode(Config, false, multi_time_warp), + + erts_debug:set_internal_state(available_internal_state, true), + try + case erts_debug:get_internal_state({check_time_config, + true, no_time_warp}) of + false -> ok; + true -> check_time_warp_mode(Config, true, no_time_warp) + end, + case erts_debug:get_internal_state({check_time_config, + true, single_time_warp}) of + false -> ok; + true -> check_time_warp_mode(Config, true, single_time_warp) + end, + case erts_debug:get_internal_state({check_time_config, + true, multi_time_warp}) of + false -> ok; + true -> check_time_warp_mode(Config, true, multi_time_warp) + end + after + erts_debug:set_internal_state(available_internal_state, false) + end. + +check_time_warp_mode(Config, TimeCorrection, TimeWarpMode) -> + io:format("~n~n~n***** Testing TimeCorrection=~p TimeWarpMode=~p *****~n", + [TimeCorrection, TimeWarpMode]), + Mon = erlang:monitor(time_offset, clock_service), + _ = erlang:time_offset(), + Start = erlang:monotonic_time(1000), + MonotonicityTimeout = 2000, + {ok, Node} = start_node(Config, + "+c " ++ atom_to_list(TimeCorrection) + ++ " +C " ++ atom_to_list(TimeWarpMode)), + StartTime = rpc:call(Node, erlang, system_info, [start_time]), + Me = self(), + MonotincityTestStarted = make_ref(), + MonotincityTestDone = make_ref(), + spawn_link(Node, + fun () -> + Me ! MonotincityTestStarted, + cmp_times(erlang:start_timer(MonotonicityTimeout, + self(), + timeout), + erlang:monotonic_time()), + Me ! MonotincityTestDone + end), + receive MonotincityTestStarted -> ok end, + check_time_offset(Node, TimeWarpMode), + TimeWarpMode = rpc:call(Node, erlang, system_info, [time_warp_mode]), + TimeCorrection = rpc:call(Node, erlang, system_info, [time_correction]), + receive MonotincityTestDone -> ok end, + MonotonicTime = rpc:call(Node, erlang, monotonic_time, []), + MonotonicTimeUnit = rpc:call(Node, + erlang, + convert_time_unit, + [1, seconds, native]), + UpMilliSeconds = erlang:convert_time_unit(MonotonicTime - StartTime, + MonotonicTimeUnit, + milli_seconds), + io:format("UpMilliSeconds=~p~n", [UpMilliSeconds]), + End = erlang:monotonic_time(milli_seconds), + stop_node(Node), + try + true = (UpMilliSeconds > (98*MonotonicityTimeout) div 100), + true = (UpMilliSeconds < (102*(End-Start)) div 100) + catch + error:_ -> + io:format("Uptime inconsistency", []), + case {TimeCorrection, erlang:system_info(time_correction)} of + {true, true} -> + ?t:fail(uptime_inconsistency); + {true, false} -> + _ = erlang:time_offset(), + receive + {'CHANGE', Mon, time_offset, clock_service, _} -> + ignore + after 1000 -> + ?t:fail(uptime_inconsistency) + end; + _ -> + ignore + end + end, + erlang:demonitor(Mon, [flush]), + ok. + +check_time_offset(Node, no_time_warp) -> + final = rpc:call(Node, erlang, system_info, [time_offset]), + final = rpc:call(Node, erlang, system_flag, [time_offset, finalize]), + final = rpc:call(Node, erlang, system_info, [time_offset]); +check_time_offset(Node, single_time_warp) -> + preliminary = rpc:call(Node, erlang, system_info, [time_offset]), + preliminary = rpc:call(Node, erlang, system_flag, [time_offset, finalize]), + final = rpc:call(Node, erlang, system_info, [time_offset]), + final = rpc:call(Node, erlang, system_flag, [time_offset, finalize]); +check_time_offset(Node, multi_time_warp) -> + volatile = rpc:call(Node, erlang, system_info, [time_offset]), + volatile = rpc:call(Node, erlang, system_flag, [time_offset, finalize]), + volatile = rpc:call(Node, erlang, system_info, [time_offset]). + +monotonic_time_monotonicity(Config) when is_list(Config) -> + Done = erlang:start_timer(10000,self(),timeout), + cmp_times(Done, erlang:monotonic_time()). + +cmp_times(Done, X0) -> + X1 = erlang:monotonic_time(), + X2 = erlang:monotonic_time(), + X3 = erlang:monotonic_time(), + X4 = erlang:monotonic_time(), + X5 = erlang:monotonic_time(), + true = (X0 =< X1), + true = (X1 =< X2), + true = (X2 =< X3), + true = (X3 =< X4), + true = (X4 =< X5), + receive + {timeout, Done, timeout} -> + ok + after 0 -> + cmp_times(Done, X5) + end. + +-define(CHK_RES_CONVS_TIMEOUT, 400). + +time_unit_conversion(Config) when is_list(Config) -> + Mon = erlang:monitor(time_offset, clock_service), + start_check_res_convs(Mon, 1000000000000), + start_check_res_convs(Mon, 2333333333333), + start_check_res_convs(Mon, 5732678356789), + erlang:demonitor(Mon, [flush]). + +start_check_res_convs(Mon, Res) -> + io:format("Checking ~p time_unit~n", [Res]), + check_res_convs(Mon, + erlang:start_timer(?CHK_RES_CONVS_TIMEOUT, + self(), + timeout), + Res). + + +check_res_convs(Mon, Done, Res) -> + receive + {timeout, Done, timeout} -> + case Res div 10 of + 0 -> + ok; + NewRes -> + start_check_res_convs(Mon, NewRes) + end + after 0 -> + do_check_res_convs(Mon, Done, Res) + end. + +do_check_res_convs(Mon, Done, Res) -> + TStart = erlang:monotonic_time(), + T = erlang:monotonic_time(Res), + TEnd = erlang:monotonic_time(), + TMin = erlang:convert_time_unit(TStart, native, Res), + TMax = erlang:convert_time_unit(TEnd, native, Res), + %io:format("~p =< ~p =< ~p~n", [TMin, T, TEnd]), + true = (TMin =< T), + true = (TMax >= T), + check_time_offset_res_conv(Mon, Res), + check_res_convs(Mon, Done, Res). + + +check_time_offset_res_conv(Mon, Res) -> + TORes = erlang:time_offset(Res), + TO = erlang:time_offset(), + case erlang:convert_time_unit(TO, native, Res) of + TORes -> + ok; + TORes2 -> + case check_time_offset_change(Mon, TO, 1000) of + {TO, false} -> + ?t:fail({time_unit_conversion_inconsistency, + TO, TORes, TORes2}); + {_NewTO, true} -> + ?t:format("time_offset changed", []), + check_time_offset_res_conv(Mon, Res) + end + end. + +signed_time_unit_conversion(Config) when is_list(Config) -> + chk_strc(1000000000, 1000000), + chk_strc(1000000000, 1000), + chk_strc(1000000000, 1), + chk_strc(1000000, 1000), + chk_strc(1000000, 1), + chk_strc(1000, 1), + chk_strc(4711, 17), + chk_strc(1 bsl 10, 1), + chk_strc(1 bsl 16, 10), + chk_strc(1 bsl 17, 1 bsl 8), + chk_strc((1 bsl 17) + 1, (1 bsl 8) - 1), + chk_strc(1 bsl 17, 11), + ok. + +chk_strc(Res0, Res1) -> + case (Res0 /= Res1) andalso (Res0 =< 1000000) andalso (Res1 =< 1000000) of + true -> + {FromRes, ToRes} = case Res0 > Res1 of + true -> {Res0, Res1}; + false -> {Res1, Res0} + end, + MinFromValuesPerToValue = FromRes div ToRes, + MaxFromValuesPerToValue = ((FromRes-1) div ToRes)+1, + io:format("~p -> ~p [~p, ~p]~n", + [FromRes, ToRes, + MinFromValuesPerToValue, MaxFromValuesPerToValue]), + chk_values_per_value(FromRes, ToRes, + -10*FromRes, 10*FromRes, + MinFromValuesPerToValue, + MaxFromValuesPerToValue, + undefined, MinFromValuesPerToValue); + _ -> + ok + end, + chk_random_values(Res0, Res1), + chk_random_values(Res1, Res0), + ok. + +chk_random_values(FR, TR) -> +% case (FR rem TR == 0) orelse (TR rem FR == 0) of +% true -> + io:format("rand values ~p -> ~p~n", [FR, TR]), + random:seed(268438039, 268440479, 268439161), + Values = lists:map(fun (_) -> random:uniform(1 bsl 65) - (1 bsl 64) end, + lists:seq(1, 100000)), + CheckFun = fun (V) -> + CV = erlang:convert_time_unit(V, FR, TR), + case {(FR*CV) div TR =< V, + (FR*(CV+1)) div TR >= V} of + {true, true} -> + ok; + Failure -> + ?t:fail({Failure, CV, V, FR, TR}) + end + end, + lists:foreach(CheckFun, Values).%; +% false -> ok +% end. + + +chk_values_per_value(_FromRes, _ToRes, + EndValue, EndValue, + MinFromValuesPerToValue, MaxFromValuesPerToValue, + _ToValue, FromValueCount) -> +% io:format("~p [~p]~n", [EndValue, FromValueCount]), + case ((MinFromValuesPerToValue =< FromValueCount) + andalso (FromValueCount =< MaxFromValuesPerToValue)) of + false -> + ?t:fail({MinFromValuesPerToValue, + FromValueCount, + MaxFromValuesPerToValue}); + true -> + ok + end; +chk_values_per_value(FromRes, ToRes, Value, EndValue, + MinFromValuesPerToValue, MaxFromValuesPerToValue, + ToValue, FromValueCount) -> + case erlang:convert_time_unit(Value, FromRes, ToRes) of + ToValue -> + chk_values_per_value(FromRes, ToRes, + Value+1, EndValue, + MinFromValuesPerToValue, + MaxFromValuesPerToValue, + ToValue, FromValueCount+1); + NewToValue -> + case ((MinFromValuesPerToValue =< FromValueCount) + andalso (FromValueCount =< MaxFromValuesPerToValue)) of + false -> + ?t:fail({MinFromValuesPerToValue, + FromValueCount, + MaxFromValuesPerToValue}); + true -> +% io:format("~p -> ~p [~p]~n", +% [Value, NewToValue, FromValueCount]), + chk_values_per_value(FromRes, ToRes, + Value+1, EndValue, + MinFromValuesPerToValue, + MaxFromValuesPerToValue, + NewToValue, 1) + end + end. + +erlang_timestamp(Config) when is_list(Config) -> + Mon = erlang:monitor(time_offset, clock_service), + {TO, _} = check_time_offset_change(Mon, + erlang:time_offset(), + 0), + Done = erlang:start_timer(10000,self(),timeout), + ok = check_erlang_timestamp(Done, Mon, TO). + +check_erlang_timestamp(Done, Mon, TO) -> + receive + {timeout, Done, timeout} -> + erlang:demonitor(Mon, [flush]), + ok + after 0 -> + do_check_erlang_timestamp(Done, Mon, TO) + end. + +do_check_erlang_timestamp(Done, Mon, TO) -> + MinMon = erlang:monotonic_time(), + {MegaSec, Sec, MicroSec} = erlang:timestamp(), + MaxMon = erlang:monotonic_time(), + TsMin = erlang:convert_time_unit(MinMon+TO, + native, + micro_seconds), + TsMax = erlang:convert_time_unit(MaxMon+TO, + native, + micro_seconds), + TsTime = (MegaSec*1000000+Sec)*1000000+MicroSec, + case (TsMin =< TsTime) andalso (TsTime =< TsMax) of + true -> + NewTO = case erlang:time_offset() of + TO -> + TO; + _ -> + check_time_offset_change(Mon, TO, 0) + end, + check_erlang_timestamp(Done, Mon, NewTO); + false -> + io:format("TsMin=~p TsTime=~p TsMax=~p~n", [TsMin, TsTime, TsMax]), + ?t:format("Detected inconsistency; " + "checking for time_offset change...", []), + case check_time_offset_change(Mon, TO, 1000) of + {TO, false} -> + ?t:fail(timestamp_inconsistency); + {NewTO, true} -> + ?t:format("time_offset changed", []), + check_erlang_timestamp(Done, Mon, NewTO) + end + end. + +check_time_offset_change(Mon, TO, Wait) -> + process_changed_time_offset(Mon, TO, false, Wait). + +process_changed_time_offset(Mon, TO, Changed, Wait) -> + receive + {'CHANGE', Mon, time_offset, clock_service, NewTO} -> + process_changed_time_offset(Mon, NewTO, true, Wait) + after Wait -> + case erlang:time_offset() of + TO -> + {TO, Changed}; + _OtherTO -> + receive + {'CHANGE', Mon, time_offset, clock_service, NewTO} -> + process_changed_time_offset(Mon, NewTO, true, Wait) + end + end + end. + + + %% Returns the test data: a list of {Utc, Local} tuples. test_data() -> @@ -554,4 +934,25 @@ bad_dates() -> {{1996, 4, 30}, {12, 0, -1}}, % Sec {{1996, 4, 30}, {12, 0, 60}}]. - + +start_node(Config) -> + start_node(Config, ""). + +start_node(Config, Args) -> + TestCase = ?config(testcase, Config), + PA = filename:dirname(code:which(?MODULE)), + ESTime = erlang:monotonic_time(1) + erlang:time_offset(1), + Unique = erlang:unique_integer([positive]), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(TestCase) + ++ "-" + ++ integer_to_list(ESTime) + ++ "-" + ++ integer_to_list(Unique)), + test_server:start_node(Name, + slave, + [{args, "-pa " ++ PA ++ " " ++ Args}]). + +stop_node(Node) -> + test_server:stop_node(Node). diff --git a/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl new file mode 100644 index 0000000000..5ad6e59272 --- /dev/null +++ b/erts/emulator/test/unique_SUITE.erl @@ -0,0 +1,390 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The 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(unique_SUITE). + +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2,end_per_testcase/2]). +-export([unique_monotonic_integer_white_box/1, + unique_integer_white_box/1]). + +-include_lib("test_server/include/test_server.hrl"). + +%-define(P(V), V). +-define(P(V), print_ret_val(?FILE, ?LINE, V)). + +-define(PRINT(V), print_ret_val(?FILE, ?LINE, V)). + + +init_per_testcase(Case, Config) -> + ?line Dog=test_server:timetrap(test_server:minutes(2)), + [{watchdog, Dog}, {testcase, Case}|Config]. + +end_per_testcase(_, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [unique_monotonic_integer_white_box, + unique_integer_white_box]. + +groups() -> + []. + +init_per_suite(Config) -> + erts_debug:set_internal_state(available_internal_state, true), + Config. + +end_per_suite(_Config) -> + erts_debug:set_internal_state(available_internal_state, false), + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%% +%% +%% Unique counter white box test case +%% +%% + +unique_monotonic_integer_white_box(Config) when is_list(Config) -> + {ok, Node} = start_node(Config), + TestServer = self(), + Success = make_ref(), + %% Run this in a separate node, so we don't mess up + %% the system when moving the strict monotonic counter + %% around in a non-strict monotonic way... + Test = spawn(Node, + fun () -> + unique_monotonic_integer_white_box_test(TestServer, Success) + end), + Mon = erlang:monitor(process, Test), + receive + {'DOWN', Mon, process, Test, Error} -> + ?t:fail(Error); + Success -> + ok + end, + erlang:demonitor(Mon, [flush]), + stop_node(Node), + ok. + +set_unique_monotonic_integer_state(MinCounter, NextValue) -> + true = erts_debug:set_internal_state(unique_monotonic_integer_state, + NextValue-MinCounter-1). + + + +unique_monotonic_integer_white_box_test(TestServer, Success) -> + erts_debug:set_internal_state(available_internal_state, true), + + WordSize = erlang:system_info({wordsize, internal}), + SmallBits = WordSize*8 - 4, + + MinSmall = -1*(1 bsl (SmallBits-1)), + MaxSmall = (1 bsl (SmallBits-1))-1, + %% Make sure we got small sizes correct... + 0 = erts_debug:size(MinSmall), + false = 0 =:= erts_debug:size(MinSmall-1), + 0 = erts_debug:size(MaxSmall), + false = 0 =:= erts_debug:size(MaxSmall+1), + + ?PRINT({min_small, MinSmall}), + ?PRINT({max_small, MaxSmall}), + + MinSint64 = -1*(1 bsl 63), + MaxSint64 = (1 bsl 63)-1, + + ?PRINT({min_Sint64, MinSint64}), + ?PRINT({max_Sint64, MaxSint64}), + + MinCounter = erts_debug:get_internal_state(min_unique_monotonic_integer), + MaxCounter = MinCounter + (1 bsl 64) - 1, + + ?PRINT({min_counter, MinCounter}), + ?PRINT({max_counter, MaxCounter}), + + case WordSize of + 4 -> + MinCounter = MinSint64; + 8 -> + MinCounter = MinSmall + end, + + StartState = erts_debug:get_internal_state(unique_monotonic_integer_state), + + %% Verify that we get expected results over all internal limits... + + case MinCounter < MinSmall of + false -> + 8 = WordSize, + ok; + true -> + 4 = WordSize, + ?PRINT(over_min_small), + set_unique_monotonic_integer_state(MinCounter, MinSmall-2), + true = (?P(erlang:unique_integer([monotonic])) == MinSmall - 2), + true = (?P(erlang:unique_integer([monotonic])) == MinSmall - 1), + true = (?P(erlang:unique_integer([monotonic])) == MinSmall), + true = (?P(erlang:unique_integer([monotonic])) == MinSmall + 1), + true = (?P(erlang:unique_integer([monotonic])) == MinSmall + 2), + garbage_collect(), + ok + end, + + ?PRINT(over_zero), %% Not really an interesting limit, but... + set_unique_monotonic_integer_state(MinCounter, -2), + true = (?P(erlang:unique_integer([monotonic])) == -2), + true = (?P(erlang:unique_integer([monotonic])) == -1), + true = (?P(erlang:unique_integer([monotonic])) == 0), + true = (?P(erlang:unique_integer([monotonic])) == 1), + true = (?P(erlang:unique_integer([monotonic])) == 2), + garbage_collect(), + + ?PRINT(over_max_small), + set_unique_monotonic_integer_state(MinCounter, MaxSmall-2), + true = (?P(erlang:unique_integer([monotonic])) == MaxSmall - 2), + true = (?P(erlang:unique_integer([monotonic])) == MaxSmall - 1), + true = (?P(erlang:unique_integer([monotonic])) == MaxSmall), + true = (?P(erlang:unique_integer([monotonic])) == MaxSmall + 1), + true = (?P(erlang:unique_integer([monotonic])) == MaxSmall + 2), + garbage_collect(), + + case MaxCounter > MaxSint64 of + false -> + 4 = WordSize, + ok; + true -> + 8 = WordSize, + ?PRINT(over_max_sint64), + set_unique_monotonic_integer_state(MinCounter, MaxSint64-2), + true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 - 2), + true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 - 1), + true = (?P(erlang:unique_integer([monotonic])) == MaxSint64), + true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 + 1), + true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 + 2), + garbage_collect() + end, + + ?PRINT(over_max_min_counter), + set_unique_monotonic_integer_state(MinCounter, if MaxCounter == MaxSint64 -> + MaxCounter-2; + true -> + MinCounter-3 + end), + true = (?P(erlang:unique_integer([monotonic])) == MaxCounter - 2), + true = (?P(erlang:unique_integer([monotonic])) == MaxCounter - 1), + true = (?P(erlang:unique_integer([monotonic])) == MaxCounter), + true = (?P(erlang:unique_integer([monotonic])) == MinCounter), + true = (?P(erlang:unique_integer([monotonic])) == MinCounter + 1), + true = (?P(erlang:unique_integer([monotonic])) == MinCounter + 2), + garbage_collect(), + + %% Restore initial state and hope we didn't mess it up for the + %% system... + true = erts_debug:set_internal_state(unique_monotonic_integer_state, + StartState), + + TestServer ! Success. + +%% +%% +%% Unique integer white box test case +%% +%% + +-record(uniqint_info, {min_int, + max_int, + max_small, + schedulers, + sched_bits}). + +unique_integer_white_box(Config) when is_list(Config) -> + UinqintInfo = init_uniqint_info(), + #uniqint_info{min_int = MinInt, + max_int = MaxInt, + max_small = MaxSmall} = UinqintInfo, + io:format("****************************************************~n", []), + io:format("*** Around MIN_UNIQ_INT ~p ***~n", [MinInt]), + io:format("****************************************************~n", []), + check_unique_integer_around(MinInt, UinqintInfo), + io:format("****************************************************~n", []), + io:format("*** Around 0 ***~n", []), + io:format("****************************************************~n", []), + check_unique_integer_around(0, UinqintInfo), + io:format("****************************************************~n", []), + io:format("*** Around MAX_SMALL ~p ***~n", [MaxSmall]), + io:format("****************************************************~n", []), + check_unique_integer_around(MaxSmall, UinqintInfo), + io:format("****************************************************~n", []), + io:format("*** Around 2^64+MIN_UNIQ_INT ~p ***~n", [(1 bsl 64)+MinInt]), + io:format("****************************************************~n", []), + check_unique_integer_around((1 bsl 64)+MinInt, UinqintInfo), + io:format("****************************************************~n", []), + io:format("*** Around 2^64 ~p~n", [(1 bsl 64)]), + io:format("****************************************************~n", []), + check_unique_integer_around((1 bsl 64), UinqintInfo), + io:format("****************************************************~n", []), + io:format("*** Around 2^64-MIN_UNIQ_INT ~p ***~n", [(1 bsl 64)-MinInt]), + io:format("****************************************************~n", []), + check_unique_integer_around((1 bsl 64)-MinInt, UinqintInfo), + io:format("****************************************************~n", []), + io:format("*** Around MAX_UNIQ_INT ~p ***~n", [MaxInt]), + io:format("****************************************************~n", []), + check_unique_integer_around(MaxInt, UinqintInfo), + ok. + + +%%% Internal unique_integer_white_box/1 test case + +calc_sched_bits(NoScheds, Shift) when NoScheds < 1 bsl Shift -> + Shift; +calc_sched_bits(NoScheds, Shift) -> + calc_sched_bits(NoScheds, Shift+1). + +init_uniqint_info() -> + SmallBits = erlang:system_info({wordsize, internal})*8-4, + io:format("SmallBits=~p~n", [SmallBits]), + Schedulers = erlang:system_info(schedulers), + io:format("Schedulers=~p~n", [Schedulers]), + MinSmall = -1*(1 bsl (SmallBits-1)), + io:format("MinSmall=~p~n", [MinSmall]), + MaxSmall = (1 bsl (SmallBits-1))-1, + io:format("MaxSmall=~p~n", [MaxSmall]), + SchedBits = calc_sched_bits(Schedulers, 0), + io:format("SchedBits=~p~n", [SchedBits]), + MaxInt = ((((1 bsl 64) - 1) bsl SchedBits) bor Schedulers) + MinSmall, + io:format("MaxInt=~p~n", [MaxInt]), + #uniqint_info{min_int = MinSmall, + max_int = MaxInt, + max_small = MaxSmall, + schedulers = Schedulers, + sched_bits = SchedBits}. + +valid_uniqint(Int, #uniqint_info{min_int = MinInt} = UinqintInfo) when Int < MinInt -> + valid_uniqint(MinInt, UinqintInfo); +valid_uniqint(Int, #uniqint_info{min_int = MinInt, + sched_bits = SchedBits, + schedulers = Scheds}) -> + Int1 = Int - MinInt, + {Inc, ThreadNo} = case Int1 band ((1 bsl SchedBits) - 1) of + TN when TN > Scheds -> + {1, Scheds}; + TN -> + {0, TN} + end, + Counter = ((Int1 bsr SchedBits) + Inc) rem (1 bsl 64), + ((Counter bsl SchedBits) bor ThreadNo) + MinInt. + +smaller_valid_uniqint(Int, UinqintInfo) -> + Cand = Int-1, + case valid_uniqint(Cand, UinqintInfo) of + RI when RI < Int -> + RI; + _ -> + smaller_valid_uniqint(Cand, UinqintInfo) + end. + +int32_to_bigendian_list(Int) -> + 0 = Int bsr 32, + [(Int bsr 24) band 16#ff, + (Int bsr 16) band 16#ff, + (Int bsr 8) band 16#ff, + Int band 16#ff]. + +mk_uniqint(Int, #uniqint_info {min_int = MinInt, + sched_bits = SchedBits} = _UinqintInfo) -> + Int1 = Int - MinInt, + ThrId = Int1 band ((1 bsl SchedBits) - 1), + Value = (Int1 bsr SchedBits) band ((1 bsl 64) - 1), + 0 = Int1 bsr (SchedBits + 64), + NodeName = atom_to_list(node()), + Make = {make_unique_integer, ThrId, Value}, + %% erlang:display(Make), + Res = erts_debug:get_internal_state(Make), + %% erlang:display({uniq_int, Res}), + Res. + +check_uniqint(Int, UinqintInfo) -> + UniqInt = mk_uniqint(Int, UinqintInfo), + io:format("UniqInt=~p ", [UniqInt]), + case UniqInt =:= Int of + true -> + io:format("OK~n~n", []); + false -> + io:format("result UniqInt=~p FAILED~n", [UniqInt]), + exit(badres) + end. + +check_unique_integer_around(Int, #uniqint_info{min_int = MinInt, + max_int = MaxInt} = UinqintInfo) -> + {Start, End} = case {Int =< MinInt+100, Int >= MaxInt-100} of + {true, false} -> + {MinInt, MinInt+100}; + {false, false} -> + {smaller_valid_uniqint(Int-100, UinqintInfo), + valid_uniqint(Int+100, UinqintInfo)}; + {false, true} -> + {MaxInt-100, MaxInt} + end, + lists:foldl(fun (I, OldRefInt) -> + RefInt = valid_uniqint(I, UinqintInfo), + case OldRefInt =:= RefInt of + true -> + ok; + false -> + check_uniqint(RefInt, UinqintInfo) + end, + RefInt + end, + none, + lists:seq(Start, End)). + + +%% helpers + +print_ret_val(File, Line, Value) -> + io:format("~s:~p: ~p~n", [File, Line, Value]), + Value. + +start_node(Config) -> + start_node(Config, []). +start_node(Config, Opts) when is_list(Config), is_list(Opts) -> + ?line Pa = filename:dirname(code:which(?MODULE)), + ?line A = erlang:monotonic_time(1) + erlang:time_offset(1), + ?line B = erlang:unique_integer([positive]), + ?line Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(?config(testcase, Config)) + ++ "-" + ++ integer_to_list(A) + ++ "-" + ++ integer_to_list(B)), + ?line ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). + +stop_node(Node) -> + ?t:stop_node(Node). diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 5ebde8ca3c..d33ea59c1d 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -807,6 +807,7 @@ int main(int argc, char **argv) case 'a': case 'A': case 'b': + case 'C': case 'e': case 'i': case 'n': @@ -880,6 +881,19 @@ int main(int argc, char **argv) } add_Eargs(argv[i]); break; + case 'c': + argv[i][0] = '-'; + if (argv[i][2] == '\0' && i+1 < argc) { + if (sys_strcmp(argv[i+1], "true") == 0 + || sys_strcmp(argv[i+1], "false") == 0) { + add_Eargs(argv[i]); + add_Eargs(argv[i+1]); + i++; + break; + } + } + add_Eargs(argv[i]); + break; case 'M': { int x; for (x = 0; plusM_au_allocs[x]; x++) @@ -1149,8 +1163,8 @@ usage_aux(void) #endif "] " "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] " - "[-args_file FILENAME] [+A THREADS] [+a SIZE] [+B[c|d|i]] [+c] " - "[+h HEAP_SIZE_OPTION] [+K BOOLEAN] " + "[-args_file FILENAME] [+A THREADS] [+a SIZE] [+B[c|d|i]] [+c [BOOLEAN]] " + "[+C MODE] [+h HEAP_SIZE_OPTION] [+K BOOLEAN] " "[+l] [+M ] [+P MAX_PROCS] [+Q MAX_PORTS] " "[+R COMPAT_REL] " "[+r] [+rg READER_GROUPS_LIMIT] [+s SCHEDULER_OPTION] " diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index 2830641802..a4f34e21d0 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -109,7 +109,7 @@ # include # include # include -# if defined(CORRECT_USING_TIMES) +# if defined(OS_MONOTONIC_TIME_USING_TIMES) # include # include # endif @@ -1084,9 +1084,9 @@ time_t timestamp(time_t *res) return r; } -#elif defined(HAVE_GETHRTIME) || defined(GETHRTIME_WITH_CLOCK_GETTIME) +#elif defined(OS_MONOTONIC_TIME_USING_GETHRTIME) || defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) -#if defined(GETHRTIME_WITH_CLOCK_GETTIME) +#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) typedef long long SysHrTime; SysHrTime sys_gethrtime(void); @@ -1095,7 +1095,7 @@ SysHrTime sys_gethrtime(void) { struct timespec ts; long long result; - if (clock_gettime(CLOCK_MONOTONIC,&ts) != 0) { + if (clock_gettime(MONOTONIC_CLOCK_ID,&ts) != 0) { print_error("Fatal, could not get clock_monotonic value, terminating! " "errno = %d\n", errno); exit(1); @@ -1122,7 +1122,7 @@ time_t timestamp(time_t *res) return r; } -#elif defined(CORRECT_USING_TIMES) +#elif defined(OS_MONOTONIC_TIME_USING_TIMES) # ifdef NO_SYSCONF # include diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 78fefbea55..6f54475e5a 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -375,7 +375,9 @@ elif [ "x$GDB" = "xegdb" ]; then # Set annotation level for gdb in emacs 22 and higher. Seems to # be working with level 1 for emacs 22 and level 3 for emacs 23... emacs_major=`$EMACS --version | head -1 | sed 's,^[^0-9]*\([0-9]*\).*,\1,g'` - if [ '!' -z "$emacs_major" -a $emacs_major -gt 22 ]; then + if [ '!' -z "$emacs_major" -a $emacs_major -gt 23 ]; then + GDBARGS="-i=mi " + elif [ '!' -z "$emacs_major" -a $emacs_major -gt 22 ]; then GDBARGS="--annotate=3 " elif [ '!' -z "$emacs_major" -a $emacs_major -gt 21 ]; then GDBARGS="--annotate=1 " diff --git a/erts/example/Makefile b/erts/example/Makefile index 6e1a88b4da..b637bee033 100644 --- a/erts/example/Makefile +++ b/erts/example/Makefile @@ -30,7 +30,7 @@ CFLAGS += $(OUR_C_FLAGS) CXXFLAGS += $(OUR_C_FLAGS) TARGETS = pg_sync.beam pg_async.beam pg_sync.so pg_async.so \ -next_perm.so next_perm.beam +next_perm.so next_perm.beam time_compat.beam all: $(TARGETS) diff --git a/erts/example/time_compat.erl b/erts/example/time_compat.erl new file mode 100644 index 0000000000..d582117ceb --- /dev/null +++ b/erts/example/time_compat.erl @@ -0,0 +1,302 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The 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% +%% + +%% +%% If your code need to be able to execute on ERTS versions both +%% earlier and later than 7.0, the best approach is to use the new +%% time API introduced in ERTS 7.0 and implement a fallback +%% solution using the old primitives to be used on old ERTS +%% versions. This way your code can automatically take advantage +%% of the improvements in the API when available. This is an +%% example of how to implement such an API, but it can be used +%% as is if you want to. Just add this module to your project, +%% and call the API via this module instead of calling the +%% BIFs directly. +%% + +-module(time_compat). + +%% We don't want warnings about the use of erlang:now/0 in +%% this module. +-compile(nowarn_deprecated_function). +%% +%% We don't use +%% -compile({nowarn_deprecated_function, [{erlang, now, 0}]}). +%% since this will produce warnings when compiled on systems +%% where it has not yet been deprecated. +%% + +-export([monotonic_time/0, + monotonic_time/1, + erlang_system_time/0, + erlang_system_time/1, + os_system_time/0, + os_system_time/1, + time_offset/0, + time_offset/1, + convert_time_unit/3, + timestamp/0, + unique_integer/0, + unique_integer/1, + monitor/2, + system_info/1, + system_flag/2]). + +monotonic_time() -> + try + erlang:monotonic_time() + catch + error:undef -> + %% Use Erlang system time as monotonic time + erlang_system_time_fallback() + end. + +monotonic_time(Unit) -> + try + erlang:monotonic_time(Unit) + catch + error:badarg -> + erlang:error(badarg, [Unit]); + error:undef -> + %% Use Erlang system time as monotonic time + STime = erlang_system_time_fallback(), + try + convert_time_unit_fallback(STime, native, Unit) + catch + error:bad_time_unit -> erlang:error(badarg, [Unit]) + end + end. + +erlang_system_time() -> + try + erlang:system_time() + catch + error:undef -> + erlang_system_time_fallback() + end. + +erlang_system_time(Unit) -> + try + erlang:system_time(Unit) + catch + error:badarg -> + erlang:error(badarg, [Unit]); + error:undef -> + STime = erlang_system_time_fallback(), + try + convert_time_unit_fallback(STime, native, Unit) + catch + error:bad_time_unit -> erlang:error(badarg, [Unit]) + end + end. + +os_system_time() -> + try + os:system_time() + catch + error:undef -> + os_system_time_fallback() + end. + +os_system_time(Unit) -> + try + os:system_time(Unit) + catch + error:badarg -> + erlang:error(badarg, [Unit]); + error:undef -> + STime = os_system_time_fallback(), + try + convert_time_unit_fallback(STime, native, Unit) + catch + error:bad_time_unit -> erlang:error(badarg, [Unit]) + end + end. + +time_offset() -> + try + erlang:time_offset() + catch + error:undef -> + %% Erlang system time and Erlang monotonic + %% time are always aligned + 0 + end. + +time_offset(Unit) -> + try + erlang:time_offset(Unit) + catch + error:badarg -> + erlang:error(badarg, [Unit]); + error:undef -> + try + _ = integer_time_unit(Unit) + catch + error:bad_time_unit -> erlang:error(badarg, [Unit]) + end, + %% Erlang system time and Erlang monotonic + %% time are always aligned + 0 + end. + +convert_time_unit(Time, FromUnit, ToUnit) -> + try + erlang:convert_time_unit(Time, FromUnit, ToUnit) + catch + error:undef -> + try + convert_time_unit_fallback(Time, FromUnit, ToUnit) + catch + _:_ -> + erlang:error(badarg, [Time, FromUnit, ToUnit]) + end; + error:Error -> + erlang:error(Error, [Time, FromUnit, ToUnit]) + end. + +timestamp() -> + try + erlang:timestamp() + catch + error:undef -> + erlang:now() + end. + +unique_integer() -> + try + erlang:unique_integer() + catch + error:undef -> + {MS, S, US} = erlang:now(), + (MS*1000000+S)*1000000+US + end. + +unique_integer(Modifiers) -> + try + erlang:unique_integer(Modifiers) + catch + error:badarg -> + erlang:error(badarg, [Modifiers]); + error:undef -> + case is_valid_modifier_list(Modifiers) of + true -> + %% now() converted to an integer + %% fullfill the requirements of + %% all modifiers: unique, positive, + %% and monotonic... + {MS, S, US} = erlang:now(), + (MS*1000000+S)*1000000+US; + false -> + erlang:error(badarg, [Modifiers]) + end + end. + +monitor(Type, Item) -> + try + erlang:monitor(Type, Item) + catch + error:Error -> + case {Error, Type, Item} of + {badarg, time_offset, clock_service} -> + %% Time offset is final and will never change. + %% Return a dummy reference, there will never + %% be any need for 'CHANGE' messages... + make_ref(); + _ -> + erlang:error(Error, [Type, Item]) + end + end. + +system_info(Item) -> + try + erlang:system_info(Item) + catch + error:badarg -> + case Item of + time_correction -> + case erlang:system_info(tolerant_timeofday) of + enabled -> true; + disabled -> false + end; + time_warp_mode -> + no_time_warp; + time_offset -> + final; + NotSupArg when NotSupArg == os_monotonic_time_source; + NotSupArg == start_time -> + %% Cannot emulate this... + erlang:error(notsup, [NotSupArg]); + _ -> + erlang:error(badarg, [Item]) + end; + error:Error -> + erlang:error(Error, [Item]) + end. + +system_flag(Flag, Value) -> + try + erlang:system_flag(Flag, Value) + catch + error:Error -> + case {Error, Flag, Value} of + {badarg, time_offset, finalize} -> + %% Time offset is final + final; + _ -> + erlang:error(Error, [Flag, Value]) + end + end. + +%% +%% Internal functions +%% + +integer_time_unit(native) -> 1000*1000; +integer_time_unit(nano_seconds) -> 1000*1000*1000; +integer_time_unit(micro_seconds) -> 1000*1000; +integer_time_unit(milli_seconds) -> 1000; +integer_time_unit(seconds) -> 1; +integer_time_unit(I) when is_integer(I), I > 0 -> I; +integer_time_unit(BadRes) -> erlang:error(bad_time_unit, [BadRes]). + +erlang_system_time_fallback() -> + {MS, S, US} = erlang:now(), + (MS*1000000+S)*1000000+US. + +os_system_time_fallback() -> + {MS, S, US} = os:timestamp(), + (MS*1000000+S)*1000000+US. + +convert_time_unit_fallback(Time, FromUnit, ToUnit) -> + FU = integer_time_unit(FromUnit), + TU = integer_time_unit(ToUnit), + case Time < 0 of + true -> TU*Time - (FU - 1); + false -> TU*Time + end div FU. + +is_valid_modifier_list([positive|Ms]) -> + is_valid_modifier_list(Ms); +is_valid_modifier_list([monotonic|Ms]) -> + is_valid_modifier_list(Ms); +is_valid_modifier_list([]) -> + true; +is_valid_modifier_list(_) -> + false. diff --git a/erts/include/internal/ethread_header_config.h.in b/erts/include/internal/ethread_header_config.h.in index b36322490a..ffd9d350e2 100644 --- a/erts/include/internal/ethread_header_config.h.in +++ b/erts/include/internal/ethread_header_config.h.in @@ -237,3 +237,18 @@ /* Assumed cache-line size (in bytes) */ #undef ASSUMED_CACHE_LINE_SIZE + +/* Define if you have a clock_gettime() with a monotonic clock */ +#undef ETHR_HAVE_CLOCK_GETTIME_MONOTONIC + +/* Define if you have a monotonic gethrtime() */ +#undef ETHR_HAVE_GETHRTIME + +/* Define if you have a mach clock_get_time() with a monotonic clock */ +#undef ETHR_HAVE_MACH_CLOCK_GET_TIME + +/* Define to the monotonic clock id to use */ +#undef ETHR_MONOTONIC_CLOCK_ID + + + diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 7a3d94dd16..4ec388a7b9 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 9b364d8b21..705bb61247 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index a190129b2c..aadc9797cb 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 611a568014..79eb60f362 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -38,7 +38,6 @@ -export([integer_to_list/2]). -export([integer_to_binary/2]). --export([flush_monitor_message/2]). -export([set_cpu_topology/1, format_cpu_topology/1]). -export([await_proc_exit/3]). -export([memory/0, memory/1]). @@ -48,7 +47,7 @@ await_sched_wall_time_modifications/2, gather_gc_info_result/1]). --deprecated([hash/2]). +-deprecated([hash/2, now/0]). %% Get rid of autoimports of spawn to avoid clashes with ourselves. -compile({no_auto_import,[spawn_link/1]}). @@ -58,12 +57,21 @@ -compile({no_auto_import,[spawn_opt/5]}). -export_type([timestamp/0]). +-export_type([time_unit/0]). -type ext_binary() :: binary(). -type timestamp() :: {MegaSecs :: non_neg_integer(), Secs :: non_neg_integer(), MicroSecs :: non_neg_integer()}. +-type time_unit() :: + pos_integer() + | 'seconds' + | 'milli_seconds' + | 'micro_seconds' + | 'nano_seconds' + | 'native'. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Native code BIF stubs and their types %% (BIF's actually implemented in this module goes last in the file) @@ -104,7 +112,8 @@ -export([list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1]). -export([list_to_integer/1, list_to_integer/2]). -export([list_to_pid/1, list_to_tuple/1, loaded/0]). --export([localtime/0, make_ref/0, map_size/1, match_spec_test/3, md5/1, md5_final/1]). +-export([localtime/0, make_ref/0]). +-export([map_size/1, 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]). @@ -112,6 +121,11 @@ -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([monotonic_time/0, monotonic_time/1]). +-export([system_time/0, system_time/1]). +-export([convert_time_unit/3]). +-export([unique_integer/0, unique_integer/1]). +-export([time_offset/0, time_offset/1, timestamp/0]). -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]). @@ -1184,13 +1198,18 @@ md5_update(_Context, _Data) -> module_loaded(_Module) -> erlang:nif_error(undefined). +-type registered_name() :: atom(). + +-type registered_process_identifier() :: registered_name() | {registered_name(), node()}. + +-type monitor_process_identifier() :: pid() | registered_process_identifier(). + %% monitor/2 --spec monitor(Type, Item) -> MonitorRef when - Type :: process, - Item :: pid() | RegName | {RegName, Node}, - RegName :: module(), - Node :: node(), +-spec monitor(process, monitor_process_identifier()) -> MonitorRef when + MonitorRef :: reference(); + (time_offset, clock_service) -> MonitorRef when MonitorRef :: reference(). + monitor(_Type, _Item) -> erlang:nif_error(undefined). @@ -1292,6 +1311,90 @@ ports() -> posixtime_to_universaltime(_P1) -> erlang:nif_error(undefined). +-spec erlang:unique_integer(ModifierList) -> integer() when + ModifierList :: [Modifier], + Modifier :: positive | monotonic. + +unique_integer(_ModifierList) -> + erlang:nif_error(undefined). + +-spec erlang:unique_integer() -> integer(). + +unique_integer() -> + erlang:nif_error(undefined). + +-spec erlang:monotonic_time() -> integer(). + +monotonic_time() -> + erlang:nif_error(undefined). + +-spec erlang:monotonic_time(Unit) -> integer() when + Unit :: time_unit(). + +monotonic_time(_Unit) -> + erlang:nif_error(undefined). + +-spec erlang:system_time() -> integer(). + +system_time() -> + erlang:nif_error(undefined). + +-spec erlang:system_time(Unit) -> integer() when + Unit :: time_unit(). + +system_time(_Unit) -> + erlang:nif_error(undefined). + +-spec erlang:convert_time_unit(Time, FromUnit, ToUnit) -> ConvertedTime when + Time :: integer(), + ConvertedTime :: integer(), + FromUnit :: time_unit(), + ToUnit :: time_unit(). + +convert_time_unit(Time, FromUnit, ToUnit) -> + try + FU = case FromUnit of + native -> erts_internal:time_unit(); + nano_seconds -> 1000*1000*1000; + micro_seconds -> 1000*1000; + milli_seconds -> 1000; + seconds -> 1; + _ when FromUnit > 0 -> FromUnit + end, + TU = case ToUnit of + native -> erts_internal:time_unit(); + nano_seconds -> 1000*1000*1000; + micro_seconds -> 1000*1000; + milli_seconds -> 1000; + seconds -> 1; + _ when ToUnit > 0 -> ToUnit + end, + case Time < 0 of + true -> TU*Time - (FU - 1); + false -> TU*Time + end div FU + catch + _ : _ -> + erlang:error(badarg, [Time, FromUnit, ToUnit]) + end. + +-spec erlang:time_offset() -> integer(). + +time_offset() -> + erlang:nif_error(undefined). + +-spec erlang:time_offset(Unit) -> integer() when + Unit :: time_unit(). + +time_offset(_Unit) -> + erlang:nif_error(undefined). + +-spec erlang:timestamp() -> Timestamp when + Timestamp :: timestamp(). + +timestamp() -> + erlang:nif_error(undefined). + %% prepare_loading/2 -spec erlang:prepare_loading(Module, Code) -> PreparedCode | {error, Reason} when Module :: module(), @@ -2118,6 +2221,8 @@ subtract(_,_) -> (trace_control_word, TCW) -> OldTCW when TCW :: non_neg_integer(), OldTCW :: non_neg_integer(); + (time_offset, finalize) -> OldState when + OldState :: preliminary | final | volatile; %% These are deliberately not documented (internal_cpu_topology, term()) -> term(); (sequential_tracer, pid() | port() | false) -> pid() | port() | false; @@ -2254,6 +2359,7 @@ tuple_to_list(_Tuple) -> (multi_scheduling_blockers) -> [PID :: pid()]; (nif_version) -> string(); (otp_release) -> string(); + (os_monotonic_time_source) -> [{atom(),term()}]; (port_count) -> non_neg_integer(); (port_limit) -> pos_integer(); (process_count) -> pos_integer(); @@ -2271,10 +2377,14 @@ tuple_to_list(_Tuple) -> (scheduler_id) -> SchedulerId :: pos_integer(); (schedulers | schedulers_online) -> pos_integer(); (smp_support) -> boolean(); + (start_time) -> integer(); (system_version) -> string(); (system_architecture) -> string(); (threads) -> boolean(); (thread_pool_size) -> non_neg_integer(); + (time_correction) -> true | false; + (time_offset) -> preliminary | final | volatile; + (time_warp_mode) -> no_time_warp | single_time_warp | multi_time_warp; (tolerant_timeofday) -> enabled | disabled; (trace_control_word) -> non_neg_integer(); (update_cpu_info) -> changed | unchanged; @@ -3041,16 +3151,6 @@ integer_to_binary(I0, Base, R0) -> true -> integer_to_binary(I1, Base, R1) end. -%% erlang:flush_monitor_message/2 is for internal use only! -%% -%% 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 erlang:is_reference(Ref), - erlang:is_atom(Res) -> - receive {_, Ref, _, _, _} -> ok after 0 -> ok end, - Res. - -record(cpu, {node = -1, processor = -1, processor_node = -1, diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 2c5bd82cf0..30df75b406 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -38,6 +38,10 @@ -export([check_process_code/2]). +-export([flush_monitor_messages/3]). + +-export([time_unit/0]). + %% %% Await result of send to port %% @@ -178,3 +182,29 @@ cmp_term(_A,_B) -> map_to_tuple_keys(_M) -> erlang:nif_error(undefined). + +-spec erts_internal:flush_monitor_messages(Ref, Multi, Res) -> term() when + Ref :: reference(), + Multi :: boolean(), + Res :: term(). + +%% erlang:demonitor(Ref, [flush]) traps to +%% erts_internal:flush_monitor_messages(Ref, Res) when +%% it needs to flush monitor messages. +flush_monitor_messages(Ref, Multi, Res) when is_reference(Ref) -> + receive + {_, Ref, _, _, _} -> + case Multi of + false -> + Res; + _ -> + flush_monitor_messages(Ref, Multi, Res) + end + after 0 -> + Res + end. + +-spec erts_internal:time_unit() -> pos_integer(). + +time_unit() -> + erlang:nif_error(undefined). diff --git a/erts/vsn.mk b/erts/vsn.mk index d0dc8f7243..ab98bd4a17 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.3 +VSN = 7.0 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From fa7b2c00cbf9212c4a3551980939b92fc6606510 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 13 Feb 2015 03:18:16 +0100 Subject: Implement ethread events with timeout --- erts/aclocal.m4 | 44 +++ erts/emulator/beam/erl_process.c | 6 +- erts/emulator/beam/erl_threads.h | 23 ++ erts/include/internal/ethr_internal.h | 27 ++ erts/include/internal/ethread_header_config.h.in | 4 +- erts/include/internal/pthread/ethr_event.h | 59 +++- erts/include/internal/win/ethr_event.h | 2 + erts/lib_src/common/ethr_aux.c | 2 + erts/lib_src/pthread/ethr_event.c | 379 ++++++++++++++++++++--- erts/lib_src/pthread/ethread.c | 148 +++++++++ erts/lib_src/win/ethr_event.c | 53 +++- 11 files changed, 683 insertions(+), 64 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 989f697201..1e449bf874 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1460,6 +1460,50 @@ case "$THR_LIB_NAME" in AC_DEFINE(ETHR_HAVE_PTHREAD_ATTR_SETGUARDSIZE, 1, \ [Define if you have the pthread_attr_setguardsize function.])) + if test "x$erl_monotonic_clock_id" != "x"; then + AC_MSG_CHECKING(whether pthread_cond_timedwait() can use the monotonic clock $erl_monotonic_clock_id for timeout) + pthread_cond_timedwait_monotonic=no + AC_TRY_LINK([ + #if defined(ETHR_NEED_NPTL_PTHREAD_H) + # include + #elif defined(ETHR_HAVE_MIT_PTHREAD_H) + # include + #elif defined(ETHR_HAVE_PTHREAD_H) + # include + #endif + #ifdef ETHR_TIME_WITH_SYS_TIME + # include + # include + #else + # ifdef ETHR_HAVE_SYS_TIME_H + # include + # else + # include + # endif + #endif + #if defined(ETHR_HAVE_MACH_CLOCK_GET_TIME) + # include + # include + #endif + ], + [ + int res; + pthread_condattr_t attr; + pthread_cond_t cond; + struct timespec cond_timeout; + pthread_mutex_t mutex; + res = pthread_condattr_init(&attr); + res = pthread_condattr_setclock(&attr, ETHR_MONOTONIC_CLOCK_ID); + res = pthread_cond_init(&cond, &attr); + res = pthread_cond_timedwait(&cond, &mutex, &cond_timeout); + ], + [pthread_cond_timedwait_monotonic=yes]) + AC_MSG_RESULT([$pthread_cond_timedwait_monotonic]) + if test $pthread_cond_timedwait_monotonic = yes; then + AC_DEFINE(ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC, [1], [Define if pthread_cond_timedwait() can be used with a monotonic clock]) + fi + fi + linux_futex=no AC_MSG_CHECKING([for Linux futexes]) AC_TRY_LINK([ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 81bc3d2429..9dcaf2fdca 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2806,7 +2806,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ASSERT(flgs & ERTS_SSI_FLG_TSE_SLEEPING); ASSERT(flgs & ERTS_SSI_FLG_WAITING); do { - res = erts_tse_wait(ssi->event); + res = erts_tse_twait(ssi->event, -1); } while (res == EINTR); } } @@ -6594,7 +6594,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) int res; do { - res = erts_tse_wait(ssi->event); + res = erts_tse_twait(ssi->event, -1); } while (res == EINTR); } } @@ -6800,7 +6800,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) int res; do { - res = erts_tse_wait(ssi->event); + res = erts_tse_twait(ssi->event, -1); } while (res == EINTR); } } diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index 7214f3ea33..b9f34d4c12 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -651,6 +651,8 @@ ERTS_GLB_INLINE void erts_tse_set(erts_tse_t *ep); ERTS_GLB_INLINE void erts_tse_reset(erts_tse_t *ep); ERTS_GLB_INLINE int erts_tse_wait(erts_tse_t *ep); ERTS_GLB_INLINE int erts_tse_swait(erts_tse_t *ep, int spincount); +ERTS_GLB_INLINE int erts_tse_twait(erts_tse_t *ep, Sint64 tmo); +ERTS_GLB_INLINE int erts_tse_stwait(erts_tse_t *ep, int spincount, Sint64 tmo); ERTS_GLB_INLINE int erts_tse_is_tmp(erts_tse_t *ep); ERTS_GLB_INLINE void erts_thr_set_main_status(int, int); ERTS_GLB_INLINE int erts_thr_get_main_status(void); @@ -3473,6 +3475,27 @@ ERTS_GLB_INLINE int erts_tse_swait(erts_tse_t *ep, int spincount) #endif } +ERTS_GLB_INLINE int erts_tse_twait(erts_tse_t *ep, Sint64 tmo) +{ +#ifdef USE_THREADS + return ethr_event_twait(&((ethr_ts_event *) ep)->event, + (ethr_sint64_t) tmo); +#else + return ENOTSUP; +#endif +} + +ERTS_GLB_INLINE int erts_tse_stwait(erts_tse_t *ep, int spincount, Sint64 tmo) +{ +#ifdef USE_THREADS + return ethr_event_stwait(&((ethr_ts_event *) ep)->event, + spincount, + (ethr_sint64_t) tmo); +#else + return ENOTSUP; +#endif +} + ERTS_GLB_INLINE int erts_tse_is_tmp(erts_tse_t *ep) { #ifdef USE_THREADS diff --git a/erts/include/internal/ethr_internal.h b/erts/include/internal/ethr_internal.h index c9b1db5b46..65195145af 100644 --- a/erts/include/internal/ethr_internal.h +++ b/erts/include/internal/ethr_internal.h @@ -57,6 +57,33 @@ ETHR_PROTO_NORETURN__ ethr_abort__(void); int ethr_win_get_errno__(void); #endif +#ifdef ETHR_INCLUDE_MONOTONIC_CLOCK__ +#undef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME +#if defined(ETHR_HAVE_CLOCK_GETTIME_MONOTONIC) \ + || defined(ETHR_HAVE_MACH_CLOCK_GET_TIME) \ + || defined(ETHR_HAVE_GETHRTIME) +#ifdef ETHR_TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef ETHR_HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#ifdef ETHR_HAVE_MACH_CLOCK_GET_TIME +#include +#include +#endif +#define ETHR_HAVE_ETHR_GET_MONOTONIC_TIME +ethr_sint64_t ethr_get_monotonic_time(void); +int ethr_get_monotonic_time_is_broken(void); +#endif +#endif /* ETHR_INCLUDE_MONOTONIC_CLOCK__ */ + +void ethr_init_event__(void); + /* implemented in lib_src/common/ethread_aux.c */ int ethr_init_common__(ethr_init_data *id); int ethr_late_init_common__(ethr_late_init_data *lid); diff --git a/erts/include/internal/ethread_header_config.h.in b/erts/include/internal/ethread_header_config.h.in index ffd9d350e2..618fcba380 100644 --- a/erts/include/internal/ethread_header_config.h.in +++ b/erts/include/internal/ethread_header_config.h.in @@ -250,5 +250,5 @@ /* Define to the monotonic clock id to use */ #undef ETHR_MONOTONIC_CLOCK_ID - - +/* Define if pthread_cond_timedwait() can be used with a monotonic clock */ +#undef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC diff --git a/erts/include/internal/pthread/ethr_event.h b/erts/include/internal/pthread/ethr_event.h index d0a77990cc..f67bac858b 100644 --- a/erts/include/internal/pthread/ethr_event.h +++ b/erts/include/internal/pthread/ethr_event.h @@ -46,12 +46,12 @@ typedef struct { ethr_atomic32_t futex; } ethr_event; -#define ETHR_FUTEX__(FTX, OP, VAL) \ +#define ETHR_FUTEX__(FTX, OP, VAL, TIMEOUT) \ (-1 == syscall(__NR_futex, \ (void *) ethr_atomic32_addr((FTX)), \ (OP), \ (int) (VAL), \ - NULL, \ + (TIMEOUT), \ NULL, \ 0) \ ? errno : 0) @@ -64,7 +64,7 @@ ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) ethr_sint32_t val; val = ethr_atomic32_xchg_mb(&e->futex, ETHR_EVENT_ON__); if (val == ETHR_EVENT_OFF_WAITER__) { - int res = ETHR_FUTEX__(&e->futex, ETHR_FUTEX_WAKE__, 1); + int res = ETHR_FUTEX__(&e->futex, ETHR_FUTEX_WAKE__, 1, NULL); if (res != 0) ETHR_FATAL_ERROR__(res); } @@ -80,35 +80,58 @@ ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) #endif #elif defined(ETHR_PTHREADS) -/* --- Posix mutex/cond implementation of events ---------------------------- */ +/* --- Posix mutex/cond pipe/select implementation of events ---------------- */ + typedef struct { ethr_atomic32_t state; pthread_mutex_t mtx; pthread_cond_t cnd; + int fd[2]; } ethr_event; -#define ETHR_EVENT_OFF_WAITER__ -1L -#define ETHR_EVENT_OFF__ 1L -#define ETHR_EVENT_ON__ 0L +#define ETHR_EVENT_OFF_WAITER_SELECT__ ((ethr_sint32_t) -2) +#define ETHR_EVENT_OFF_WAITER__ ((ethr_sint32_t) -1) +#define ETHR_EVENT_OFF__ ((ethr_sint32_t) 1) +#define ETHR_EVENT_ON__ ((ethr_sint32_t) 0) + +#define ETHR_EVENT_IS_WAITING__(VAL) ((VAL) < 0) #if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__) +#ifndef ETHR_HAVE_PTHREAD_TIMED_COND_MONOTONIC +#include +#include +#endif + static void ETHR_INLINE ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) { ethr_sint32_t val; val = ethr_atomic32_xchg_mb(&e->state, ETHR_EVENT_ON__); - if (val == ETHR_EVENT_OFF_WAITER__) { - int res = pthread_mutex_lock(&e->mtx); - if (res != 0) - ETHR_FATAL_ERROR__(res); - res = pthread_cond_signal(&e->cnd); - if (res != 0) - ETHR_FATAL_ERROR__(res); - res = pthread_mutex_unlock(&e->mtx); - if (res != 0) - ETHR_FATAL_ERROR__(res); + if (ETHR_EVENT_IS_WAITING__(val)) { + int res; + if (val == ETHR_EVENT_OFF_WAITER_SELECT__) { + ssize_t wres; + int fd = e->fd[1]; + ETHR_ASSERT(fd >= 0); + do { + wres = write(fd, "!", 1); + } while (wres < 0 && errno == EINTR); + if (wres < 0 && errno != EAGAIN && errno != EWOULDBLOCK) + ETHR_FATAL_ERROR__(errno); + } + else { + res = pthread_mutex_lock(&e->mtx); + if (res != 0) + ETHR_FATAL_ERROR__(res); + res = pthread_cond_signal(&e->cnd); + if (res != 0) + ETHR_FATAL_ERROR__(res); + res = pthread_mutex_unlock(&e->mtx); + if (res != 0) + ETHR_FATAL_ERROR__(res); + } } } @@ -127,6 +150,8 @@ int ethr_event_init(ethr_event *e); int ethr_event_destroy(ethr_event *e); int ethr_event_wait(ethr_event *e); int ethr_event_swait(ethr_event *e, int spincount); +int ethr_event_twait(ethr_event *e, ethr_sint64_t timeout); +int ethr_event_stwait(ethr_event *e, int spincount, ethr_sint64_t timeout); #if !defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__) void ethr_event_set(ethr_event *e); void ethr_event_reset(ethr_event *e); diff --git a/erts/include/internal/win/ethr_event.h b/erts/include/internal/win/ethr_event.h index 6363174a74..95e681983f 100644 --- a/erts/include/internal/win/ethr_event.h +++ b/erts/include/internal/win/ethr_event.h @@ -58,6 +58,8 @@ int ethr_event_init(ethr_event *e); int ethr_event_destroy(ethr_event *e); int ethr_event_wait(ethr_event *e); int ethr_event_swait(ethr_event *e, int spincount); +int ethr_event_twait(ethr_event *e, ethr_sint64_t timeout); +int ethr_event_stwait(ethr_event *e, int spincount, ethr_sint64_t timeout); #if !defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__) void ethr_event_set(ethr_event *e); void ethr_event_reset(ethr_event *e); diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c index b77f2178f2..1ba51882c3 100644 --- a/erts/lib_src/common/ethr_aux.c +++ b/erts/lib_src/common/ethr_aux.c @@ -148,6 +148,8 @@ ethr_init_common__(ethr_init_data *id) { int res; + ethr_init_event__(); + #if defined(ETHR_X86_RUNTIME_CONF__) x86_init(); #endif diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c index 9434d60d0a..b35c599365 100644 --- a/erts/lib_src/pthread/ethr_event.c +++ b/erts/lib_src/pthread/ethr_event.c @@ -29,6 +29,9 @@ #endif #include "ethread.h" +#undef ETHR_INCLUDE_MONOTONIC_CLOCK__ +#define ETHR_INCLUDE_MONOTONIC_CLOCK__ +#include "ethr_internal.h" #if defined(ETHR_LINUX_FUTEX_IMPL__) /* --- Linux futex implementation of ethread events ------------------------- */ @@ -38,6 +41,12 @@ #define ETHR_YIELD_AFTER_BUSY_LOOPS 50 +void +ethr_init_event__(void) +{ + +} + int ethr_event_init(ethr_event *e) { @@ -52,21 +61,43 @@ ethr_event_destroy(ethr_event *e) } static ETHR_INLINE int -wait__(ethr_event *e, int spincount) +wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) { unsigned sc = spincount; int res; ethr_sint32_t val; int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; + ethr_sint64_t time = 0; /* SHUT UP annoying faulty warning... */ + struct timespec ts, *tsp; +#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME + ethr_sint64_t start = 0; /* SHUT UP annoying faulty warning... */ +#endif if (spincount < 0) ETHR_FATAL_ERROR__(EINVAL); + if (timeout < 0) { + tsp = NULL; + } + else { + tsp = &ts; + time = timeout; + if (spincount == 0) { + val = ethr_atomic32_read(&e->futex); + if (val == ETHR_EVENT_ON__) + goto return_event_on; + goto set_timeout; + } +#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME + start = ethr_get_monotonic_time(); +#endif + } + while (1) { while (1) { val = ethr_atomic32_read(&e->futex); if (val == ETHR_EVENT_ON__) - return 0; + goto return_event_on; if (sc == 0) break; sc--; @@ -79,44 +110,147 @@ wait__(ethr_event *e, int spincount) } } + if (timeout >= 0) { +#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME + time = timeout - (ethr_get_monotonic_time() - start); +#endif + set_timeout: + if (time <= 0) { + val = ethr_atomic32_read(&e->futex); + if (val == ETHR_EVENT_ON__) + goto return_event_on; + return ETIMEDOUT; + } + ts.tv_sec = time / (1000*1000*1000); + ts.tv_nsec = time % (1000*1000*1000); + } + if (val != ETHR_EVENT_OFF_WAITER__) { val = ethr_atomic32_cmpxchg(&e->futex, ETHR_EVENT_OFF_WAITER__, ETHR_EVENT_OFF__); if (val == ETHR_EVENT_ON__) - return 0; + goto return_event_on; ETHR_ASSERT(val == ETHR_EVENT_OFF__); } res = ETHR_FUTEX__(&e->futex, ETHR_FUTEX_WAIT__, - ETHR_EVENT_OFF_WAITER__); - if (res == EINTR) + ETHR_EVENT_OFF_WAITER__, + tsp); + switch (res) { + case EINTR: + case ETIMEDOUT: + return res; + case 0: + case EWOULDBLOCK: break; - if (res != 0 && res != EWOULDBLOCK) + default: ETHR_FATAL_ERROR__(res); + } } - return res; +return_event_on: + + ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); + + return 0; + } #elif defined(ETHR_PTHREADS) -/* --- Posix mutex/cond implementation of events ---------------------------- */ +/* --- Posix mutex/cond pipe/select implementation of events ---------------- */ + +#include +#include +#include +#include +#include +#include +#include + +static void +setup_nonblocking_pipe(ethr_event *e) +{ + int flgs; + int res; + + res = pipe(e->fd); + if (res != 0) + ETHR_FATAL_ERROR__(errno); + + ETHR_ASSERT(e->fd[0] >= 0 && e->fd[1] >= 0); + + flgs = fcntl(e->fd[0], F_GETFL, 0); + fcntl(e->fd[0], F_SETFL, flgs | O_NONBLOCK); + flgs = fcntl(e->fd[1], F_GETFL, 0); + fcntl(e->fd[1], F_SETFL, flgs | O_NONBLOCK); + + ETHR_MEMBAR(ETHR_StoreStore); +} + +#define ETHR_EVENT_INVALID_FD__ -1 +#define ETHR_EVENT_COND_TIMEDWAIT__ -2 + +#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC +static pthread_condattr_t monotonic_clock_cond_attr; +#endif +static pthread_condattr_t *monotonic_clock_cond_attr_p; + +#ifndef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME +# undef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC +#endif +#ifndef ETHR_MONOTONIC_CLOCK_ID +# undef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC +#endif + +void +ethr_init_event__(void) +{ + monotonic_clock_cond_attr_p = NULL; +#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC + if (!ethr_get_monotonic_time_is_broken() + && pthread_condattr_init(&monotonic_clock_cond_attr) == 0) { + if (pthread_condattr_setclock(&monotonic_clock_cond_attr, + ETHR_MONOTONIC_CLOCK_ID) == 0) + monotonic_clock_cond_attr_p = &monotonic_clock_cond_attr; + else + pthread_condattr_destroy(&monotonic_clock_cond_attr); + } +#endif +} int ethr_event_init(ethr_event *e) { int res; + ethr_atomic32_init(&e->state, ETHR_EVENT_OFF__); + res = pthread_mutex_init(&e->mtx, NULL); if (res != 0) return res; - res = pthread_cond_init(&e->cnd, NULL); + + res = pthread_cond_init(&e->cnd, monotonic_clock_cond_attr_p); if (res != 0) { pthread_mutex_destroy(&e->mtx); return res; } + +#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC + /* + * If ethr_get_monotonic_time() is broken we + * fall back on the pipe/select solution... + */ + if (monotonic_clock_cond_attr_p) { + e->fd[0] = e->fd[1] = ETHR_EVENT_COND_TIMEDWAIT__; + return 0; + } +#endif + + e->fd[0] = e->fd[1] = ETHR_EVENT_INVALID_FD__; + return 0; } @@ -124,6 +258,10 @@ int ethr_event_destroy(ethr_event *e) { int res; + if (e->fd[0] >= 0) { + close(e->fd[0]); + close(e->fd[1]); + } res = pthread_mutex_destroy(&e->mtx); if (res != 0) return res; @@ -134,12 +272,58 @@ ethr_event_destroy(ethr_event *e) } static ETHR_INLINE int -wait__(ethr_event *e, int spincount) +wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) { int sc = spincount; ethr_sint32_t val; int res, ulres; int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; + ethr_sint64_t time = 0; /* SHUT UP annoying faulty warning... */ +#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME + ethr_sint64_t start = 0; /* SHUT UP annoying faulty warning... */ +#endif +#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC + struct timespec cond_timeout; +#endif + + val = ethr_atomic32_read(&e->state); + if (val == ETHR_EVENT_ON__) + goto return_event_on; + + if (timeout < 0) { + if (spincount == 0) + goto set_event_off_waiter; + } + if (timeout == 0) + return ETIMEDOUT; + else { + time = timeout; + switch (e->fd[0]) { + case ETHR_EVENT_INVALID_FD__: +#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME + start = ethr_get_monotonic_time(); +#endif + setup_nonblocking_pipe(e); + break; +#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC + case ETHR_EVENT_COND_TIMEDWAIT__: + time += ethr_get_monotonic_time(); + cond_timeout.tv_sec = time / (1000*1000*1000); + cond_timeout.tv_nsec = time % (1000*1000*1000); + if (spincount == 0) + goto set_event_off_waiter; + break; +#endif + default: + /* Already initialized pipe... */ + if (spincount == 0) + goto set_select_timeout; +#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME + start = ethr_get_monotonic_time(); +#endif + break; + } + } if (spincount < 0) ETHR_FATAL_ERROR__(EINVAL); @@ -147,7 +331,8 @@ wait__(ethr_event *e, int spincount) while (1) { val = ethr_atomic32_read(&e->state); if (val == ETHR_EVENT_ON__) - return 0; + goto return_event_on; + if (sc == 0) break; sc--; @@ -160,40 +345,150 @@ wait__(ethr_event *e, int spincount) } } - if (val != ETHR_EVENT_OFF_WAITER__) { - val = ethr_atomic32_cmpxchg(&e->state, - ETHR_EVENT_OFF_WAITER__, - ETHR_EVENT_OFF__); - if (val == ETHR_EVENT_ON__) - return 0; - ETHR_ASSERT(val == ETHR_EVENT_OFF__); + if (timeout < 0 +#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC + || e->fd[0] == ETHR_EVENT_COND_TIMEDWAIT__ +#endif + ) { + + set_event_off_waiter: + + if (val != ETHR_EVENT_OFF_WAITER__) { + ethr_sint32_t act; + act = ethr_atomic32_cmpxchg(&e->state, + ETHR_EVENT_OFF_WAITER__, + val); + if (act == ETHR_EVENT_ON__) + goto return_event_on; + ETHR_ASSERT(act == val); + } + + res = pthread_mutex_lock(&e->mtx); + if (res != 0) + ETHR_FATAL_ERROR__(res); + + while (1) { + + val = ethr_atomic32_read(&e->state); + if (val == ETHR_EVENT_ON__) { + ETHR_ASSERT(res == 0); + ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); + break; + } + +#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC + if (timeout > 0) { + res = pthread_cond_timedwait(&e->cnd, &e->mtx, &cond_timeout); + if (res == EINTR || res == ETIMEDOUT) + break; + } + else +#endif + { + res = pthread_cond_wait(&e->cnd, &e->mtx); + if (res == EINTR) + break; + } + if (res != 0) + ETHR_FATAL_ERROR__(res); + } + + ulres = pthread_mutex_unlock(&e->mtx); + if (ulres != 0) + ETHR_FATAL_ERROR__(ulres); + } + else { + int fd; + int sres; + ssize_t rres; + fd_set rset; + fd_set eset; + struct timeval select_timeout; + +#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME + time -= ethr_get_monotonic_time() - start; + if (time <= 0) + return ETIMEDOUT; +#endif - ETHR_ASSERT(val == ETHR_EVENT_OFF_WAITER__ - || val == ETHR_EVENT_OFF__); + set_select_timeout: - res = pthread_mutex_lock(&e->mtx); - if (res != 0) - ETHR_FATAL_ERROR__(res); + ETHR_ASSERT(time > 0); - while (1) { + /* + * timeout in nano-second, but we can only wait + * for micro-seconds... + */ + time = ((time - 1) / 1000) + 1; + + select_timeout.tv_sec = time / (1000*1000); + select_timeout.tv_usec = time % (1000*1000); + + ETHR_ASSERT(val != ETHR_EVENT_ON__); + + fd = e->fd[0]; + + /* Cleanup pipe... */ + do { + char buf[64]; + rres = read(fd, buf, sizeof(buf)); + } while (rres > 0 || (rres < 0 && errno == EINTR)); + if (rres < 0 && errno != EAGAIN && errno != EWOULDBLOCK) + ETHR_FATAL_ERROR__(errno); + + /* + * Need to verify that state is still off + * after cleaning the pipe... + */ + if (val == ETHR_EVENT_OFF_WAITER_SELECT__) { + val = ethr_atomic32_read(&e->state); + if (val == ETHR_EVENT_ON__) + goto return_event_on; + } + else { + ethr_sint32_t act; + act = ethr_atomic32_cmpxchg(&e->state, + ETHR_EVENT_OFF_WAITER_SELECT__, + val); + if (act == ETHR_EVENT_ON__) + goto return_event_on; + ETHR_ASSERT(act == val); + } + + FD_ZERO(&rset); + FD_SET(fd, &rset); + FD_ZERO(&eset); + FD_SET(fd, &eset); + + sres = select(fd + 1, &rset, NULL, &eset, &select_timeout); + if (sres == 0) + res = ETIMEDOUT; + else { + res = EINTR; + if (sres < 0 && errno != EINTR) + ETHR_FATAL_ERROR__(errno); + /* else: + * Event is *probably* set, but it can be a + * lingering writer. That is, it is important + * that we verify that it actually is set. If + * it isn't, return EINTR (spurious wakeup). + */ + } val = ethr_atomic32_read(&e->state); if (val == ETHR_EVENT_ON__) - break; + goto return_event_on; - res = pthread_cond_wait(&e->cnd, &e->mtx); - if (res == EINTR) - break; - if (res != 0) - ETHR_FATAL_ERROR__(res); } - ulres = pthread_mutex_unlock(&e->mtx); - if (ulres != 0) - ETHR_FATAL_ERROR__(ulres); + return res; + +return_event_on: + + ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); - return res; /* 0 || EINTR */ + return 0; } #else @@ -215,11 +510,23 @@ ethr_event_set(ethr_event *e) int ethr_event_wait(ethr_event *e) { - return wait__(e, 0); + return wait__(e, 0, -1); } int ethr_event_swait(ethr_event *e, int spincount) { - return wait__(e, spincount); + return wait__(e, spincount, -1); +} + +int +ethr_event_twait(ethr_event *e, ethr_sint64_t timeout) +{ + return wait__(e, 0, timeout); +} + +int +ethr_event_stwait(ethr_event *e, int spincount, ethr_sint64_t timeout) +{ + return wait__(e, spincount, timeout); } diff --git a/erts/lib_src/pthread/ethread.c b/erts/lib_src/pthread/ethread.c index 79784c5b84..42d2abd3f1 100644 --- a/erts/lib_src/pthread/ethread.c +++ b/erts/lib_src/pthread/ethread.c @@ -49,6 +49,8 @@ #define ETHREAD_IMPL__ #include "ethread.h" +#undef ETHR_INCLUDE_MONOTONIC_CLOCK__ +#define ETHR_INCLUDE_MONOTONIC_CLOCK__ #include "ethr_internal.h" #ifndef ETHR_HAVE_ETHREAD_DEFINES @@ -232,6 +234,10 @@ ethr_x86_cpuid__(int *eax, int *ebx, int *ecx, int *edx) #endif /* ETHR_X86_RUNTIME_CONF__ */ +#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME +static void init_get_monotonic_time(void); +#endif + /* * -------------------------------------------------------------------------- * Exported functions @@ -254,6 +260,10 @@ ethr_init(ethr_init_data *id) goto error; #endif +#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME + init_get_monotonic_time(); +#endif + res = ethr_init_common__(id); if (res != 0) goto error; @@ -567,6 +577,144 @@ int ethr_sigwait(const sigset_t *set, int *sig) #endif /* #if ETHR_HAVE_ETHR_SIG_FUNCS */ +#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME + +static int broken_get_monotonic_time; + +#if defined(ETHR_HAVE_CLOCK_GETTIME_MONOTONIC) +# ifndef ETHR_MONOTONIC_CLOCK_ID +# error ETHR_MONOTONIC_CLOCK_ID should have been defined +# endif + +ethr_sint64_t +ethr_get_monotonic_time(void) +{ + ethr_sint64_t time; + struct timespec ts; + + if (broken_get_monotonic_time) + return (ethr_sint64_t) 0; + + if (0 != clock_gettime(ETHR_MONOTONIC_CLOCK_ID, &ts)) + ETHR_FATAL_ERROR__(errno); + + time = (ethr_sint64_t) ts.tv_sec; + time *= (ethr_sint64_t) 1000*1000*1000; + time += (ethr_sint64_t) ts.tv_nsec; + return time; +} + +#elif defined(ETHR_HAVE_MACH_CLOCK_GET_TIME) +# ifndef ETHR_MONOTONIC_CLOCK_ID +# error ETHR_MONOTONIC_CLOCK_ID should have been defined +# endif + +ethr_sint64_t +ethr_get_monotonic_time(void) +{ + ethr_sint64_t time; + kern_return_t res; + clock_serv_t clk_srv; + mach_timespec_t time_spec; + + if (broken_get_monotonic_time) + return (ethr_sint64_t) 0; + + errno = EFAULT; + host_get_clock_service(mach_host_self(), + ETHR_MONOTONIC_CLOCK_ID, + &clk_srv); + res = clock_get_time(clk_srv, &time_spec); + if (res != KERN_SUCCESS) + ETHR_FATAL_ERROR__(errno); + mach_port_deallocate(mach_task_self(), clk_srv); + + time = (ethr_sint64_t) time_spec.tv_sec; + time *= (ethr_sint64_t) 1000*1000*1000; + time += (ethr_sint64_t) time_spec.tv_nsec; + return time; +} + +#elif defined(ETHR_HAVE_GETHRTIME) + +ethr_sint64_t +ethr_get_monotonic_time(void) +{ + if (broken_get_monotonic_time) + return (ethr_sint64_t) 0; + return (ethr_sint64_t) gethrtime(); +} + +#else +#error missing monotonic clock +#endif + +int +ethr_get_monotonic_time_is_broken(void) +{ + return broken_get_monotonic_time; +} + +#include +#include +#include + +static void +init_get_monotonic_time(void) +{ + struct utsname uts; + int vsn[3]; + int i; + char *c; + + broken_get_monotonic_time = 0; + + (void) uname(&uts); + + for (c = uts.sysname; *c; c++) { + if (isupper((int) *c)) + *c = tolower((int) *c); + } + + c = uts.release; + for (i = 0; i < sizeof(vsn)/sizeof(int); i++) { + if (!isdigit((int) *c)) + vsn[i] = 0; + else { + char *c2 = c; + do { + c2++; + } while (isdigit((int) *c2)); + *c2 = '\0'; + vsn[i] = atoi(c); + c = c2; + c++; + } + } + + if (strcmp("linux", uts.sysname) == 0) { + if (vsn[0] < 2 + || (vsn[0] == 2 && vsn[1] < 6) + || (vsn[0] == 2 && vsn[1] == 6 && vsn[2] < 33)) { + broken_get_monotonic_time = 1; + } + } + else if (strcmp("sunos", uts.sysname) == 0) { + if ((vsn[0] < 5 + || (vsn[0] == 5 && vsn[1] < 8)) +#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) + && sysconf(_SC_NPROCESSORS_CONF) > 1 +#endif + ) { + broken_get_monotonic_time = 1; + } + } + +} + + +#endif /* ETHR_HAVE_ETHR_GET_MONOTONIC_TIME */ + ETHR_IMPL_NORETURN__ ethr_abort__(void) { diff --git a/erts/lib_src/win/ethr_event.c b/erts/lib_src/win/ethr_event.c index bc2f635c26..a0d506356d 100644 --- a/erts/lib_src/win/ethr_event.c +++ b/erts/lib_src/win/ethr_event.c @@ -25,9 +25,16 @@ #define ETHR_EVENT_IMPL__ #include "ethread.h" +#include "ethr_internal.h" /* --- Windows implementation of thread events ------------------------------ */ +void +ethr_init_event__(void) +{ + +} + int ethr_event_init(ethr_event *e) { @@ -58,11 +65,29 @@ ethr_event_reset(ethr_event *e) } static ETHR_INLINE int -wait(ethr_event *e, int spincount) +wait(ethr_event *e, int spincount, ethr_sint64_t timeout) { - DWORD code; + DWORD code, tmo; int sc, res, until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; + if (timeout < 0) + tmo = INFINITE; + else if (timeout == 0) { + ethr_sint32_t state = ethr_atomic32_read(&e->state); + if (state == ETHR_EVENT_ON__) { + ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); + return 0; + } + return ETIMEDOUT; + } + else { + /* + * Timeout in nano-seconds, but we can only + * wait for milli-seconds... + */ + tmo = (DWORD) (timeout - 1) / (1000*1000) + 1; + } + if (spincount < 0) ETHR_FATAL_ERROR__(EINVAL); @@ -72,8 +97,10 @@ wait(ethr_event *e, int spincount) ethr_sint32_t state; while (1) { state = ethr_atomic32_read(&e->state); - if (state == ETHR_EVENT_ON__) + if (state == ETHR_EVENT_ON__) { + ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); return 0; + } if (sc == 0) break; sc--; @@ -95,7 +122,9 @@ wait(ethr_event *e, int spincount) ETHR_ASSERT(state == ETHR_EVENT_OFF__); } - code = WaitForSingleObject(e->handle, INFINITE); + code = WaitForSingleObject(e->handle, tmo); + if (code == WAIT_TIMEOUT) + return ETIMEDOUT; if (code != WAIT_OBJECT_0) ETHR_FATAL_ERROR__(ethr_win_get_errno__()); } @@ -105,11 +134,23 @@ wait(ethr_event *e, int spincount) int ethr_event_wait(ethr_event *e) { - return wait(e, 0); + return wait(e, 0, -1); } int ethr_event_swait(ethr_event *e, int spincount) { - return wait(e, spincount); + return wait(e, spincount, -1); +} + +int +ethr_event_twait(ethr_event *e, ethr_sint64_t timeout) +{ + return wait(e, 0, timeout); +} + +int +ethr_event_stwait(ethr_event *e, int spincount, ethr_sint64_t timeout) +{ + return wait(e, spincount, timeout); } -- cgit v1.2.3 From 6b1921d767de5cd1a980234f83b36dbfa13d9fc7 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 13 Feb 2015 00:25:57 +0100 Subject: Erlang based BIF timer implementation for scalability --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/bif.c | 35 ++++- erts/emulator/beam/bif.tab | 5 - erts/emulator/beam/erl_alloc.types | 2 + erts/emulator/beam/erl_bif_info.c | 21 ++- erts/emulator/beam/erl_bif_timer.c | 157 ++++++++++++++++++++- erts/emulator/beam/erl_bif_timer.h | 1 + erts/emulator/beam/erl_db_hash.c | 51 ++++++- erts/emulator/beam/erl_gc.c | 71 ++++++---- erts/emulator/beam/erl_init.c | 14 +- erts/emulator/beam/erl_message.c | 2 +- erts/emulator/beam/erl_message.h | 14 +- erts/emulator/beam/erl_process.c | 36 ++++- erts/emulator/beam/erl_process.h | 22 ++- erts/emulator/beam/erl_time_sup.c | 1 + erts/emulator/beam/global.h | 8 +- erts/emulator/test/timer_bif_SUITE.erl | 18 ++- erts/preloaded/ebin/erlang.beam | Bin 100456 -> 105832 bytes erts/preloaded/ebin/erts_internal.beam | Bin 4688 -> 12368 bytes erts/preloaded/ebin/init.beam | Bin 48800 -> 49744 bytes erts/preloaded/src/erlang.erl | 191 +++++++++++++++++++++++-- erts/preloaded/src/erts_internal.erl | 251 +++++++++++++++++++++++++++++++++ erts/preloaded/src/init.erl | 32 ++++- 23 files changed, 848 insertions(+), 85 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index ced35be265..e2760c18b4 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -109,6 +109,7 @@ atom bag atom band atom big atom bif_return_trap +atom bif_timer_server atom binary atom binary_bin_to_list_trap atom binary_copy_trap diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index ec5122292e..a48f65d3a2 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -615,14 +615,12 @@ erts_queue_monitor_message(Process *p, } static BIF_RETTYPE -local_pid_monitor(Process *p, Eterm target) +local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int bool) { BIF_RETTYPE ret; - Eterm mon_ref; Process *rp; ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK; - mon_ref = erts_make_ref(p); ERTS_BIF_PREP_RET(ret, mon_ref); if (target == p->common.id) { return ret; @@ -635,12 +633,18 @@ local_pid_monitor(Process *p, Eterm target) if (!rp) { erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK); p_locks &= ~ERTS_PROC_LOCK_LINK; - erts_queue_monitor_message(p, &p_locks, - mon_ref, am_process, target, am_noproc); + if (bool) + ret = am_false; + else + erts_queue_monitor_message(p, &p_locks, + mon_ref, am_process, target, am_noproc); } else { ASSERT(rp != p); + if (bool) + ret = am_true; + erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, target, NIL); erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, mon_ref, p->common.id, NIL); @@ -785,7 +789,7 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2) if (is_internal_pid(target)) { local_pid: - ret = local_pid_monitor(BIF_P, target); + ret = local_pid_monitor(BIF_P, target, erts_make_ref(BIF_P), 0); } else if (is_external_pid(target)) { dep = external_pid_dist_entry(target); if (dep == erts_this_dist_entry) @@ -828,6 +832,25 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2) return ret; } +BIF_RETTYPE erts_internal_monitor_process_2(BIF_ALIST_2) +{ + if (is_not_internal_pid(BIF_ARG_1)) { + if (is_external_pid(BIF_ARG_1) + && (external_pid_dist_entry(BIF_ARG_1) + == erts_this_dist_entry)) { + BIF_RET(am_false); + } + goto badarg; + } + + if (is_not_internal_ref(BIF_ARG_2)) + goto badarg; + + BIF_RET(local_pid_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, 1)); + +badarg: + BIF_ERROR(BIF_P, BADARG); +} /**********************************************************************/ /* this is a combination of the spawn and link BIFs */ diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index db8feb681b..8ac1fba733 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -217,11 +217,6 @@ bif math:sqrt/1 bif math:atan2/2 bif math:pow/2 -bif erlang:start_timer/3 -bif erlang:send_after/3 -bif erlang:cancel_timer/1 -bif erlang:read_timer/1 - bif erlang:make_tuple/2 bif erlang:append_element/2 bif erlang:make_tuple/3 diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 4cd4ad100c..43279715b8 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -365,6 +365,7 @@ type AINFO_REQ STANDARD_LOW SYSTEM alloc_info_request type SCHED_WTIME_REQ STANDARD_LOW SYSTEM sched_wall_time_request type GC_INFO_REQ STANDARD_LOW SYSTEM gc_info_request type PORT_DATA_HEAP STANDARD_LOW SYSTEM port_data_heap +type BIF_TIMER_DATA LONG_LIVED_LOW SYSTEM bif_timer_data +else # "fullword" @@ -385,6 +386,7 @@ type AINFO_REQ SHORT_LIVED SYSTEM alloc_info_request type SCHED_WTIME_REQ SHORT_LIVED SYSTEM sched_wall_time_request type GC_INFO_REQ SHORT_LIVED SYSTEM gc_info_request type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap +type BIF_TIMER_DATA LONG_LIVED SYSTEM bif_timer_data +endif diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 80d49c7ce2..70062b2305 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -538,6 +538,7 @@ pi_locks(Eterm info) switch (info) { case am_status: case am_priority: + case am_trap_exit: return ERTS_PROC_LOCK_STATUS; case am_links: case am_monitors: @@ -590,7 +591,7 @@ static Eterm pi_args[] = { am_min_bin_vheap_size, am_current_location, am_current_stacktrace, -}; +}; #define ERTS_PI_ARGS ((int) (sizeof(pi_args)/sizeof(Eterm))) @@ -3701,6 +3702,24 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); } +BIF_RETTYPE erts_internal_is_system_process_1(BIF_ALIST_1) +{ + if (is_internal_pid(BIF_ARG_1)) { + Process *rp = erts_proc_lookup(BIF_ARG_1); + if (rp && (rp->static_flags & ERTS_STC_FLG_SYSTEM_PROC)) + BIF_RET(am_true); + BIF_RET(am_false); + } + + if (is_external_pid(BIF_ARG_1) + && external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry) { + BIF_RET(am_false); + } + + BIF_ERROR(BIF_P, BADARG); +} + + static erts_smp_atomic_t hipe_test_reschedule_flag; diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c index c9b02b48f5..8b444f2b01 100644 --- a/erts/emulator/beam/erl_bif_timer.c +++ b/erts/emulator/beam/erl_bif_timer.c @@ -490,8 +490,9 @@ setup_bif_timer(Uint32 xflags, return ref; } +BIF_RETTYPE old_send_after_3(BIF_ALIST_3); /* send_after(Time, Pid, Message) -> Ref */ -BIF_RETTYPE send_after_3(BIF_ALIST_3) +BIF_RETTYPE old_send_after_3(BIF_ALIST_3) { Eterm res; @@ -511,8 +512,9 @@ BIF_RETTYPE send_after_3(BIF_ALIST_3) } } +BIF_RETTYPE old_start_timer_3(BIF_ALIST_3); /* start_timer(Time, Pid, Message) -> Ref */ -BIF_RETTYPE start_timer_3(BIF_ALIST_3) +BIF_RETTYPE old_start_timer_3(BIF_ALIST_3) { Eterm res; @@ -532,8 +534,9 @@ BIF_RETTYPE start_timer_3(BIF_ALIST_3) } } +BIF_RETTYPE old_cancel_timer_1(BIF_ALIST_1); /* cancel_timer(Ref) -> false | RemainingTime */ -BIF_RETTYPE cancel_timer_1(BIF_ALIST_1) +BIF_RETTYPE old_cancel_timer_1(BIF_ALIST_1) { Eterm res; ErtsBifTimer *btm; @@ -570,8 +573,9 @@ BIF_RETTYPE cancel_timer_1(BIF_ALIST_1) BIF_RET(res); } +BIF_RETTYPE old_read_timer_1(BIF_ALIST_1); /* read_timer(Ref) -> false | RemainingTime */ -BIF_RETTYPE read_timer_1(BIF_ALIST_1) +BIF_RETTYPE old_read_timer_1(BIF_ALIST_1) { Eterm res; ErtsBifTimer *btm; @@ -653,7 +657,7 @@ erts_cancel_bif_timers(Process *p, ErtsProcLocks plocks) erts_smp_btm_rwunlock(); } -void erts_bif_timer_init(void) +static void erts_old_bif_timer_init(void) { int i; no_bif_timers = 0; @@ -704,3 +708,146 @@ erts_bif_timer_foreach(void (*func)(Eterm, Eterm, ErlHeapFragment *, void *), } } } + +typedef struct { + Uint ref_heap[REF_THING_SIZE]; + Eterm pid[1]; +} ErtsBifTimerServers; + +static ErtsBifTimerServers *bif_timer_servers; + +void erts_bif_timer_init(void) +{ + erts_old_bif_timer_init(); +} + +void +erts_bif_timer_start_servers(Eterm parent) +{ + Process *parent_proc; + Eterm *hp, btr_ref, arg_list_end; + ErlSpawnOpts so; + int i; + + bif_timer_servers = erts_alloc(ERTS_ALC_T_BIF_TIMER_DATA, + (sizeof(ErtsBifTimerServers) + + (sizeof(Eterm)*(erts_no_schedulers-1)))); + + so.flags = SPO_USE_ARGS|SPO_SYSTEM_PROC|SPO_PREFER_SCHED|SPO_OFF_HEAP_MSGS; + so.min_heap_size = H_MIN_SIZE; + so.min_vheap_size = BIN_VH_MIN_SIZE; + so.priority = PRIORITY_MAX; + so.max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs); + + /* + * Parent is "init" and schedulers have not yet been started, so it + * *should* be alive and well... + */ + ASSERT(is_internal_pid(parent)); + parent_proc = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, + internal_pid_index(parent)); + ASSERT(parent_proc); + ASSERT(parent_proc->common.id == parent); + ASSERT(!ERTS_PROC_IS_EXITING(parent_proc)); + + erts_smp_proc_lock(parent_proc, ERTS_PROC_LOCK_MAIN); + + hp = HAlloc(parent_proc, 2*erts_no_schedulers + 2 + REF_THING_SIZE); + + btr_ref = erts_make_ref_in_buffer(hp); + hp += REF_THING_SIZE; + + arg_list_end = CONS(hp, btr_ref, NIL); + hp += 2; + + for (i = 0; i < erts_no_schedulers; i++) { + int sched = i+1; + Eterm arg_list = CONS(hp, make_small(i+1), arg_list_end); + hp += 2; + + so.scheduler = sched; /* Preferred scheduler */ + + bif_timer_servers->pid[i] = erl_create_process(parent_proc, + am_erts_internal, + am_bif_timer_server, + arg_list, + &so); + } + + erts_smp_proc_unlock(parent_proc, ERTS_PROC_LOCK_MAIN); + + hp = internal_ref_val(btr_ref); + for (i = 0; i < REF_THING_SIZE; i++) + bif_timer_servers->ref_heap[i] = hp[i]; +} + +BIF_RETTYPE +erts_internal_get_bif_timer_servers_0(BIF_ALIST_0) +{ + int i; + Eterm *hp, res = NIL; + + hp = HAlloc(BIF_P, erts_no_schedulers*2); + for (i = erts_no_schedulers-1; i >= 0; i--) { + res = CONS(hp, bif_timer_servers->pid[i], res); + hp += 2; + } + BIF_RET(res); +} + +BIF_RETTYPE +erts_internal_access_bif_timer_1(BIF_ALIST_1) +{ + int ix; + Uint32 *rdp; + Eterm ref, pid, *hp, res; + + if (is_not_internal_ref(BIF_ARG_1)) { + if (is_not_ref(BIF_ARG_1)) + BIF_ERROR(BIF_P, BADARG); + BIF_RET(am_undefined); + } + + rdp = internal_ref_numbers(BIF_ARG_1); + ix = (int) erts_get_ref_numbers_thr_id(rdp); + if (ix < 1 || erts_no_schedulers < ix) + BIF_RET(am_undefined); + + pid = bif_timer_servers->pid[ix-1]; + ASSERT(is_internal_pid(pid)); + + hp = HAlloc(BIF_P, 3 /* 2-tuple */ + REF_THING_SIZE); + for (ix = 0; ix < REF_THING_SIZE; ix++) + hp[ix] = bif_timer_servers->ref_heap[ix]; + ref = make_internal_ref(&hp[0]); + hp += REF_THING_SIZE; + + res = TUPLE2(hp, ref, pid); + BIF_RET(res); +} + +BIF_RETTYPE +erts_internal_create_bif_timer_0(BIF_ALIST_0) +{ + ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(BIF_P); + Eterm *hp, btr_ref, t_ref, pid, res; + int ix; + + hp = HAlloc(BIF_P, 4 /* 3-tuple */ + 2*REF_THING_SIZE); + for (ix = 0; ix < REF_THING_SIZE; ix++) + hp[ix] = bif_timer_servers->ref_heap[ix]; + btr_ref = make_internal_ref(&hp[0]); + hp += REF_THING_SIZE; + + t_ref = erts_sched_make_ref_in_buffer(esdp, hp); + hp += REF_THING_SIZE; + + ASSERT(erts_get_ref_numbers_thr_id(internal_ref_numbers(t_ref)) + == (Uint32) esdp->no); + + pid = bif_timer_servers->pid[((int) esdp->no) - 1]; + + res = TUPLE3(hp, btr_ref, pid, t_ref); + + BIF_RET(res); +} diff --git a/erts/emulator/beam/erl_bif_timer.h b/erts/emulator/beam/erl_bif_timer.h index 1197c176f5..c2f5dfd3c3 100644 --- a/erts/emulator/beam/erl_bif_timer.h +++ b/erts/emulator/beam/erl_bif_timer.h @@ -33,4 +33,5 @@ void erts_cancel_bif_timers(Process *p, ErtsProcLocks plocks); void erts_bif_timer_init(void); void erts_bif_timer_foreach(void (*func)(Eterm,Eterm,ErlHeapFragment *,void *), void *arg); +void erts_bif_timer_start_servers(Eterm); #endif diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 06dac8f161..063808eb79 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -171,10 +171,53 @@ static ERTS_INLINE void add_fixed_deletion(DbTableHash* tb, int ix) #define MAX_HASH 0xEFFFFFFFUL #define INVALID_HASH 0xFFFFFFFFUL -/* optimised version of make_hash (normal case? atomic key) */ -#define MAKE_HASH(term) \ - ((is_atom(term) ? (atom_tab(atom_val(term))->slot.bucket.hvalue) : \ - make_hash2(term)) % MAX_HASH) +static ERTS_INLINE HashValue +MAKE_HASH(Eterm term) +{ + if (is_atom(term)) { + /* + * optimised version of make_hash, although poor hashvalue + * (normal case? atomic key) + */ + return atom_tab(atom_val(term))->slot.bucket.hvalue; + } + if (is_ref(term)) { + /* + * make_hash2() produce poor hash values + * for refs. + */ + int no; + Uint32 *ref; + HashValue hval; + if (is_internal_ref(term)) { + no = (int) internal_ref_no_of_numbers(term); + ref = internal_ref_numbers(term); + } + else { + no = (int) external_ref_no_of_numbers(term); + ref = external_ref_numbers(term); + } + switch (no) { + case 3: + ref_limit: + if (!ref[2]) + no = 2; + case 2: + if (!ref[1]) + no = 1; + case 1: + break; + default: + no = 3; + goto ref_limit; + } + hval = (HashValue) block_hash((byte *) ref, + no * sizeof(Uint32), + 0x08d12e65); + return hval % MAX_HASH; + } + return make_hash2(term) % MAX_HASH; +} #ifdef ERTS_SMP # define DB_HASH_LOCK_MASK (DB_HASH_LOCK_CNT-1) diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index fea9b16e90..9a05e5b23a 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -95,10 +95,10 @@ typedef struct { static Uint setup_rootset(Process*, Eterm*, int, Rootset*); static void cleanup_rootset(Rootset *rootset); -static Uint combined_message_size(Process* p); +static Uint combined_message_size(Process* p, int off_heap_msgs); static void remove_message_buffers(Process* p); -static int major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl); -static int minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl); +static int major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl, int off_heap_msgs); +static int minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl, int off_heap_msgs); static void do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj); static Eterm* sweep_rootset(Rootset *rootset, Eterm* htop, char* src, Uint src_size); static Eterm* sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size); @@ -401,7 +401,9 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) { Uint reclaimed_now = 0; int done = 0; + int off_heap_msgs; Uint ms1, s1, us1; + erts_aint32_t state; ErtsSchedulerData *esdp; #ifdef USE_VM_PROBES DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); @@ -418,7 +420,8 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) trace_gc(p, am_gc_start); } - erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC); + state = erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC); + off_heap_msgs = state & ERTS_PSFLG_OFF_HEAP_MSGS; if (erts_system_monitor_long_gc != 0) { get_now(&ms1, &s1, &us1); } @@ -444,11 +447,11 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) while (!done) { if ((FLAGS(p) & F_NEED_FULLSWEEP) != 0) { DTRACE2(gc_major_start, pidbuf, need); - done = major_collection(p, need, objv, nobj, &reclaimed_now); + done = major_collection(p, need, objv, nobj, &reclaimed_now, off_heap_msgs); DTRACE2(gc_major_end, pidbuf, reclaimed_now); } else { DTRACE2(gc_minor_start, pidbuf, need); - done = minor_collection(p, need, objv, nobj, &reclaimed_now); + done = minor_collection(p, need, objv, nobj, &reclaimed_now, off_heap_msgs); DTRACE2(gc_minor_end, pidbuf, reclaimed_now); } } @@ -831,7 +834,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals, } static int -minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) +minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl, int off_heap_msgs) { Uint mature = HIGH_WATER(p) - HEAP_START(p); @@ -870,20 +873,22 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) Uint size_after; Uint need_after; Uint stack_size = STACK_SZ_ON_HEAP(p); - Uint fragments = MBUF_SIZE(p) + combined_message_size(p); + Uint fragments = MBUF_SIZE(p) + combined_message_size(p, off_heap_msgs); Uint size_before = fragments + (HEAP_TOP(p) - HEAP_START(p)); Uint new_sz = next_heap_size(p, HEAP_SIZE(p) + fragments, 0); do_minor(p, new_sz, objv, nobj); - /* - * Copy newly received message onto the end of the new heap. - */ - ErtsGcQuickSanityCheck(p); - for (msgp = p->msg.first; msgp; msgp = msgp->next) { - if (msgp->data.attached) { - erts_move_msg_attached_data_to_heap(&p->htop, &p->off_heap, msgp); - ErtsGcQuickSanityCheck(p); + if (!off_heap_msgs) { + /* + * Copy newly received message onto the end of the new heap. + */ + ErtsGcQuickSanityCheck(p); + for (msgp = p->msg.first; msgp; msgp = msgp->next) { + if (msgp->data.attached) { + erts_move_msg_attached_data_to_heap(&p->htop, &p->off_heap, msgp); + ErtsGcQuickSanityCheck(p); + } } } ErtsGcQuickSanityCheck(p); @@ -1209,7 +1214,7 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) */ static int -major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) +major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl, int off_heap_msgs) { Rootset rootset; Roots* roots; @@ -1222,8 +1227,7 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) Uint oh_size = (char *) OLD_HTOP(p) - oh; Uint n; Uint new_sz; - Uint fragments = MBUF_SIZE(p) + combined_message_size(p); - ErlMessage *msgp; + Uint fragments = MBUF_SIZE(p) + combined_message_size(p, off_heap_msgs); size_before = fragments + (HEAP_TOP(p) - HEAP_START(p)); @@ -1433,13 +1437,16 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) ErtsGcQuickSanityCheck(p); - /* - * Copy newly received message onto the end of the new heap. - */ - for (msgp = p->msg.first; msgp; msgp = msgp->next) { - if (msgp->data.attached) { - erts_move_msg_attached_data_to_heap(&p->htop, &p->off_heap, msgp); - ErtsGcQuickSanityCheck(p); + if (!off_heap_msgs) { + ErlMessage *msgp; + /* + * Copy newly received message onto the end of the new heap. + */ + for (msgp = p->msg.first; msgp; msgp = msgp->next) { + if (msgp->data.attached) { + erts_move_msg_attached_data_to_heap(&p->htop, &p->off_heap, msgp); + ErtsGcQuickSanityCheck(p); + } } } @@ -1500,15 +1507,17 @@ adjust_after_fullsweep(Process *p, Uint size_before, int need, Eterm *objv, int * mbuf list. */ static Uint -combined_message_size(Process* p) +combined_message_size(Process* p, int off_heap_msgs) { - Uint sz = 0; + Uint sz; ErlMessage *msgp; - for (msgp = p->msg.first; msgp; msgp = msgp->next) { - if (msgp->data.attached) { + if (off_heap_msgs) + return 0; + + for (sz = 0, msgp = p->msg.first; msgp; msgp = msgp->next) { + if (msgp->data.attached) sz += erts_msg_attached_data_size(msgp); - } } return sz; } diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 0e128c9b99..be2c5ced9e 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -389,12 +389,13 @@ erl_init(int ncpu, erl_nif_init(); } -static void +static Eterm erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** argv) { int i; Eterm start_mod; Eterm args; + Eterm res; Eterm* hp; Process parent; ErlSpawnOpts so; @@ -424,10 +425,11 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** hp += 2; args = CONS(hp, env, args); - so.flags = 0; - (void) erl_create_process(&parent, start_mod, am_start, args, &so); + so.flags = SPO_SYSTEM_PROC; + res = erl_create_process(&parent, start_mod, am_start, args, &so); erts_smp_proc_unlock(&parent, ERTS_PROC_LOCK_MAIN); erts_cleanup_empty_process(&parent); + return res; } Eterm @@ -2086,7 +2088,11 @@ erl_start(int argc, char **argv) erts_initialized = 1; - erl_first_process_otp("otp_ring0", NULL, 0, boot_argc, boot_argv); + { + Eterm init = erl_first_process_otp("otp_ring0", NULL, 0, + boot_argc, boot_argv); + erts_bif_timer_start_servers(init); + } #ifdef ERTS_SMP erts_start_schedulers(); diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 8870fac7d9..ce91acb6a4 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -994,7 +994,7 @@ erts_send_message(Process* sender, #endif ); BM_SWAP_TIMER(send,system); - } else if (sender == receiver) { + } else if (sender == receiver && !(sender->flags & F_OFF_HEAP_MSGS)) { /* Drop message if receiver has a pending exit ... */ #ifdef ERTS_SMP ErtsProcLocks need_locks = (~(*receiver_locks) diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 0f3bb8d281..a2a7193ea9 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -198,15 +198,25 @@ do { \ if ((M)->data.attached) { \ Uint need__ = erts_msg_attached_data_size((M)); \ if ((ST) - (HT) >= need__) { \ - Uint *htop__ = (HT); \ + Uint *htop__; \ + move__attached__msg__data____: \ + htop__ = (HT); \ erts_move_msg_attached_data_to_heap(&htop__, &MSO((P)), (M));\ ASSERT(htop__ - (HT) <= need__); \ (HT) = htop__; \ } \ else { \ + int off_heap_msgs__ = (int) (P)->flags & F_OFF_HEAP_MSGS; \ + if (!off_heap_msgs__) \ + need__ = 0; \ { SWPO ; } \ - (FC) -= erts_garbage_collect((P), 0, NULL, 0); \ + (FC) -= erts_garbage_collect((P), need__, NULL, 0); \ { SWPI ; } \ + if (off_heap_msgs__) { \ + ASSERT((M)->data.attached); \ + ASSERT((ST) - (HT) >= need__); \ + goto move__attached__msg__data____; \ + } \ } \ ASSERT(!(M)->data.attached); \ } \ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 9dcaf2fdca..6e562e16c8 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -5844,6 +5844,13 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces int check_emigration_need; #endif +#ifdef ERTS_SMP + if ((p->static_flags & ERTS_STC_FLG_PREFER_SCHED) + && p->preferred_run_queue != RUNQ_READ_RQ(&p->run_queue)) { + RUNQ_SET_RQ(&p->run_queue, p->preferred_run_queue); + } +#endif + a = state; while (1) { @@ -5882,6 +5889,7 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces free_proxy_proc(proxy); erts_smp_runq_lock(c_rq); + return 0; #ifdef ERTS_DIRTY_SCHEDULERS @@ -10511,7 +10519,10 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). int ix = so->scheduler-1; ASSERT(0 <= ix && ix < erts_no_run_queues); rq = ERTS_RUNQ_IX(ix); - state |= ERTS_PSFLG_BOUND; + if (!(so->flags & SPO_PREFER_SCHED)) { + /* Unsupported feature... */ + state |= ERTS_PSFLG_BOUND; + } } prio = (erts_aint32_t) so->priority; } @@ -10519,6 +10530,9 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). state |= (((prio & ERTS_PSFLGS_PRIO_MASK) << ERTS_PSFLGS_ACT_PRIO_OFFSET) | ((prio & ERTS_PSFLGS_PRIO_MASK) << ERTS_PSFLGS_USR_PRIO_OFFSET)); + if (so->flags & SPO_OFF_HEAP_MSGS) + state |= ERTS_PSFLG_OFF_HEAP_MSGS; + if (!rq) rq = erts_get_runq_proc(parent); @@ -10542,11 +10556,25 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). heap_need = arg_size; p->flags = erts_default_process_flags; + if (so->flags & SPO_OFF_HEAP_MSGS) + p->flags |= F_OFF_HEAP_MSGS; +#ifdef ERTS_SMP + p->preferred_run_queue = NULL; +#endif + p->static_flags = 0; + if (so->flags & SPO_SYSTEM_PROC) + p->static_flags |= ERTS_STC_FLG_SYSTEM_PROC; if (so->flags & SPO_USE_ARGS) { p->min_heap_size = so->min_heap_size; p->min_vheap_size = so->min_vheap_size; p->max_gen_gcs = so->max_gen_gcs; + if (so->flags & SPO_PREFER_SCHED) { +#ifdef ERTS_SMP + p->preferred_run_queue = rq; +#endif + p->static_flags |= ERTS_STC_FLG_PREFER_SCHED; + } } else { p->min_heap_size = H_MIN_SIZE; p->min_vheap_size = BIN_VH_MIN_SIZE; @@ -10867,6 +10895,8 @@ void erts_init_empty_process(Process *p) p->parent = NIL; p->approx_started = 0; + p->static_flags = 0; + p->common.u.alive.started_interval = 0; #ifdef HIPE @@ -10892,6 +10922,7 @@ void erts_init_empty_process(Process *p) p->pending_suspenders = NULL; p->pending_exit.reason = THE_NON_VALUE; p->pending_exit.bp = NULL; + p->preferred_run_queue = NULL; erts_proc_lock_init(p); erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL); RUNQ_SET_RQ(&p->run_queue, ERTS_RUNQ_IX(0)); @@ -11805,6 +11836,9 @@ erts_do_exit_process(Process* p, Eterm reason) } #endif + if (p->static_flags & ERTS_STC_FLG_SYSTEM_PROC) + erl_exit(1, "System process %T terminated: %T\n", p->common.id, reason); + #ifdef ERTS_SMP ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); /* By locking all locks (main lock is already locked) when going diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 6ef56b1974..53a992e115 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -938,6 +938,8 @@ struct process { Eterm parent; /* Pid of process that created this process. */ erts_approx_time_t approx_started; /* Time when started. */ + Uint32 static_flags; /* Flags that do *not* change */ + /* This is the place, where all fields that differs between memory * architectures, have gone to. */ @@ -969,6 +971,7 @@ struct process { ErtsSchedulerData *scheduler_data; Eterm suspendee; ErtsPendingSuspend *pending_suspenders; + ErtsRunQueue *preferred_run_queue; erts_smp_atomic_t run_queue; #ifdef HIPE struct hipe_process_state_smp hipe_smp; @@ -1078,11 +1081,12 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15) #define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16) #define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17) +#define ERTS_PSFLG_OFF_HEAP_MSGS ERTS_PSFLG_BIT(18) #ifdef ERTS_DIRTY_SCHEDULERS -#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(18) -#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(19) -#define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q ERTS_PSFLG_BIT(20) -#define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q ERTS_PSFLG_BIT(21) +#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(19) +#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(20) +#define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q ERTS_PSFLG_BIT(21) +#define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q ERTS_PSFLG_BIT(22) #endif #define ERTS_PSFLGS_IN_PRQ_MASK (ERTS_PSFLG_IN_PRQ_MAX \ @@ -1097,6 +1101,12 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLGS_GET_PRQ_PRIO(PSFLGS) \ (((PSFLGS) >> ERTS_PSFLGS_USR_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK) +/* + * Static flags that do not change after process creation. + */ +#define ERTS_STC_FLG_SYSTEM_PROC (((Uint32) 1) << 0) +#define ERTS_STC_FLG_PREFER_SCHED (((Uint32) 1) << 1) + /* The sequential tracing token is a tuple of size 5: * * {Flags, Label, Serial, Sender} @@ -1124,6 +1134,9 @@ void erts_check_for_holes(Process* p); #define SPO_LINK 1 #define SPO_USE_ARGS 2 #define SPO_MONITOR 4 +#define SPO_OFF_HEAP_MSGS 8 +#define SPO_SYSTEM_PROC 16 +#define SPO_PREFER_SCHED 32 /* * The following struct contains options for a process to be spawned. @@ -1211,6 +1224,7 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; #define F_P2PNR_RESCHED (1 << 9) /* Process has been rescheduled via erts_pid2proc_not_running() */ #define F_FORCE_GC (1 << 10) /* Force gc at process in-scheduling */ #define F_DISABLE_GC (1 << 11) /* Disable GC */ +#define F_OFF_HEAP_MSGS (1 << 12) /* process trace_flags */ #define F_SENSITIVE (1 << 0) diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 1534fb8058..7dfa7d8743 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -1984,3 +1984,4 @@ BIF_RETTYPE os_system_time_1(BIF_ALIST_0) stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec); BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, stime, 0)); } + diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index e24aef3e3c..96c21d5320 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1154,7 +1154,9 @@ erts_alloc_message_heap_state(Uint size, state = erts_smp_atomic32_read_acqb(&receiver->state); if (statep) *statep = state; - if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) + if (state & (ERTS_PSFLG_OFF_HEAP_MSGS + | ERTS_PSFLG_EXITING + | ERTS_PSFLG_PENDING_EXIT)) goto allocate_in_mbuf; #endif @@ -1174,7 +1176,9 @@ erts_alloc_message_heap_state(Uint size, state = erts_smp_atomic32_read_nob(&receiver->state); if (statep) *statep = state; - if ((state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) + if ((state & (ERTS_PSFLG_OFF_HEAP_MSGS + | ERTS_PSFLG_EXITING + | ERTS_PSFLG_PENDING_EXIT)) || (receiver->flags & F_DISABLE_GC) || HEAP_LIMIT(receiver) - HEAP_TOP(receiver) <= size) { /* diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index c28224729d..da19be3424 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -238,6 +238,7 @@ cleanup(Config) when is_list(Config) -> ?line wait_until(fun () -> process_is_cleaned_up(P1) end), ?line T1 = erlang:start_timer(10000, P1, "hej"), ?line T2 = erlang:send_after(10000, P1, "hej"), + receive after 1000 -> ok end, ?line Mem = mem(), ?line false = erlang:read_timer(T1), ?line false = erlang:read_timer(T2), @@ -250,6 +251,7 @@ cleanup(Config) when is_list(Config) -> ?line true = is_integer(erlang:read_timer(T3)), ?line true = is_integer(erlang:read_timer(T4)), ?line wait_until(fun () -> process_is_cleaned_up(P2) end), + receive after 1000 -> ok end, ?line false = erlang:read_timer(T3), ?line false = erlang:read_timer(T4), ?line Mem = mem(), @@ -455,10 +457,18 @@ registered_process(Config) when is_list(Config) -> ?line ok. mem() -> - AA = erlang:system_info(allocated_areas), - {value,{bif_timer,Mem}} = lists:keysearch(bif_timer, 1, AA), - Mem. - + TSrvs = erts_internal:get_bif_timer_servers(), + lists:foldl(fun (Tab, Sz) -> + case lists:member(ets:info(Tab, owner), TSrvs) of + true -> + ets:info(Tab, memory) + Sz; + false -> + Sz + end + end, + 0, + ets:all())*erlang:system_info({wordsize,external}). + process_is_cleaned_up(P) when is_pid(P) -> undefined == erts_debug:get_internal_state({process_status, P}). diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 4ec388a7b9..303f3f47b6 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 705bb61247..68fb357eb4 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index aadc9797cb..7f79493b4d 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 79eb60f362..83010b17d2 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -89,7 +89,7 @@ -export([binary_to_list/3, binary_to_term/1, binary_to_term/2]). -export([bit_size/1, bitsize/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, +-export([cancel_timer/1, cancel_timer/2, check_old_code/1, check_process_code/2, check_process_code/3, crc32/1]). -export([crc32/2, crc32_combine/3, date/0, decode_packet/3]). -export([delete_element/2]). @@ -128,7 +128,7 @@ -export([time_offset/0, time_offset/1, timestamp/0]). -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([put/2, raise/3, read_timer/1, read_timer/2, 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]). @@ -427,8 +427,77 @@ call_on_load_function(_P1) -> -spec erlang:cancel_timer(TimerRef) -> Time | false when TimerRef :: reference(), Time :: non_neg_integer(). -cancel_timer(_TimerRef) -> - erlang:nif_error(undefined). +cancel_timer(TimerRef) -> + try + case erts_internal:access_bif_timer(TimerRef) of + undefined -> + false; + {BTR, TSrv} -> + Req = erlang:make_ref(), + TSrv ! {cancel_timeout, BTR, erlang:self(), + true, Req, TimerRef}, + receive + {cancel_timer, Req, Result} -> + Result + end + end + catch + _:_ -> erlang:error(badarg, [TimerRef]) + end. + +%% cancel_timer/2 +-spec erlang:cancel_timer(TimerRef, Options) -> Time | false | ok when + TimerRef :: reference(), + Option :: {async, boolean()} | {info, boolean()}, + Options :: [Option], + Time :: non_neg_integer(). +cancel_timer(TimerRef, Options) -> + try + {Async, Info} = get_cancel_timer_options(Options, false, true), + case erts_internal:access_bif_timer(TimerRef) of + undefined -> + case {Async, Info} of + {true, true} -> + erlang:self() ! {cancel_timer, TimerRef, false}, ok; + {false, true} -> + false; + _ -> + ok + end; + {BTR, TSrv} -> + case Async of + true -> + TSrv ! {cancel_timeout, BTR, erlang:self(), + Info, TimerRef, TimerRef}, + ok; + false -> + Req = erlang:make_ref(), + TSrv ! {cancel_timeout, BTR, erlang:self(), + true, Req, TimerRef}, + receive + {cancel_timer, Req, Result} -> + case Info of + true -> Result; + false -> ok + end + end + end + end + catch + _:_ -> erlang:error(badarg, [TimerRef, Options]) + end. + +get_cancel_timer_options([], Async, Info) -> + {Async, Info}; +get_cancel_timer_options([{async, Bool} | Opts], + _Async, Info) when Bool == true; + Bool == false -> + get_cancel_timer_options(Opts, Bool, Info); +get_cancel_timer_options([{info, Bool} | Opts], + Async, _Info) when Bool == true; + Bool == false -> + get_cancel_timer_options(Opts, Async, Bool). + %% check_old_code/1 -spec check_old_code(Module) -> boolean() when @@ -1462,8 +1531,53 @@ raise(_Class, _Reason, _Stacktrace) -> %% read_timer/1 -spec erlang:read_timer(TimerRef) -> non_neg_integer() | false when TimerRef :: reference(). -read_timer(_TimerRef) -> - erlang:nif_error(undefined). + +read_timer(TimerRef) -> + read_timer(TimerRef, []). + +%% read_timer/2 +-spec erlang:read_timer(TimerRef, Options) -> non_neg_integer() | false | ok when + TimerRef :: reference(), + Option :: {async, boolean()}, + Options :: [Option]. + +read_timer(TimerRef, Options) -> + try + Async = get_read_timer_options(Options, false), + case erts_internal:access_bif_timer(TimerRef) of + undefined -> + case Async of + true -> + erlang:self() ! {read_timer, TimerRef, false}, + ok; + false -> + false + end; + {BTR, TSrv} -> + case Async of + true -> + TSrv ! {read_timeout, BTR, erlang:self(), + TimerRef, TimerRef}, + ok; + false -> + Req = erlang:make_ref(), + TSrv ! {read_timeout, BTR, erlang:self(), + Req, TimerRef}, + receive + {read_timer, Req, Result} -> + Result + end + end + end + catch + _:_ -> erlang:error(badarg, [TimerRef]) + end. + +get_read_timer_options([], Async) -> + Async; +get_read_timer_options([{async, Bool} | Opts], + _Async) when Bool == true; Bool == false -> + get_read_timer_options(Opts, Bool). %% ref_to_list/1 -spec erlang:ref_to_list(Ref) -> string() when @@ -1509,8 +1623,36 @@ self() -> Dest :: pid() | atom(), Msg :: term(), TimerRef :: reference(). -send_after(_Time, _Dest, _Msg) -> - erlang:nif_error(undefined). + +send_after(0, Dest, Msg) -> + try + true = ((erlang:is_pid(Dest) + andalso erlang:node(Dest) == erlang:node()) + orelse (erlang:is_atom(Dest) + andalso Dest /= undefined)), + try Dest ! Msg catch _:_ -> ok end, + erlang:make_ref() + catch + _:_ -> + erlang:error(badarg, [0, Dest, Msg]) + end; +send_after(Time, Dest, Msg) -> + Now = erlang:monotonic_time(), + try + true = ((erlang:is_pid(Dest) + andalso erlang:node(Dest) == erlang:node()) + orelse (erlang:is_atom(Dest) + andalso Dest /= undefined)), + true = Time > 0, + true = Time < (1 bsl 32), % Maybe lift this restriction... + TO = Now + (erts_internal:time_unit()*Time) div 1000, + {BTR, TSrv, TRef} = erts_internal:create_bif_timer(), + TSrv ! {set_timeout, BTR, Dest, TO, TRef, Msg}, + TRef + catch + _:_ -> + erlang:error(badarg, [Time, Dest, Msg]) + end. %% seq_trace/2 -spec erlang:seq_trace(P1, P2) -> seq_trace_info_returns() | {term(), term(), term(), term(), term()} when @@ -1583,8 +1725,37 @@ split_binary(_Bin, _Pos) -> Dest :: pid() | atom(), Msg :: term(), TimerRef :: reference(). -start_timer(_Time, _Dest, _Msg) -> - erlang:nif_error(undefined). +start_timer(0, Dest, Msg) -> + try + true = ((erlang:is_pid(Dest) + andalso erlang:node(Dest) == erlang:node()) + orelse (erlang:is_atom(Dest) + andalso Dest /= undefined)), + TimerRef = erlang:make_ref(), + try Dest ! {timeout, TimerRef, Msg} catch _:_ -> ok end, + TimerRef + catch + _:_ -> + erlang:error(badarg, [0, Dest, Msg]) + end; +start_timer(Time, Dest, Msg) -> + Now = erlang:monotonic_time(), + try + true = ((erlang:is_pid(Dest) + andalso erlang:node(Dest) == erlang:node()) + orelse (erlang:is_atom(Dest) + andalso Dest /= undefined)), + true = Time > 0, + true = Time < (1 bsl 32), % Maybe lift this restriction... + TO = Now + (erts_internal:time_unit()*Time) div 1000, + {BTR, TSrv, TimerRef} = erts_internal:create_bif_timer(), + TSrv ! {set_timeout, BTR, Dest, TO, TimerRef, + {timeout, TimerRef, Msg}}, + TimerRef + catch + _:_ -> + erlang:error(badarg, [Time, Dest, Msg]) + end. %% suspend_process/2 -spec erlang:suspend_process(Suspendee, OptList) -> boolean() when diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 30df75b406..2c701d75e4 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -42,6 +42,14 @@ -export([time_unit/0]). +-export([bif_timer_server/2]). + +-export([get_bif_timer_servers/0, create_bif_timer/0, access_bif_timer/1]). + +-export([monitor_process/2]). + +-export([is_system_process/1]). + %% %% Await result of send to port %% @@ -208,3 +216,246 @@ flush_monitor_messages(Ref, Multi, Res) when is_reference(Ref) -> time_unit() -> erlang:nif_error(undefined). + +-spec erts_internal:get_bif_timer_servers() -> Pids when + Pid :: pid(), + Pids :: [Pid]. + +get_bif_timer_servers() -> + erlang:nif_error(undefined). + +-spec erts_internal:create_bif_timer() -> Res when + Res :: {reference(), pid(), reference()}. + +create_bif_timer() -> + erlang:nif_error(undefined). + +-spec erts_internal:access_bif_timer(Ref) -> Res when + Ref :: reference(), + Res :: {reference(), pid(), reference()}. + +access_bif_timer(_Ref) -> + erlang:nif_error(undefined). + +-spec erts_internal:monitor_process(Pid, Ref) -> boolean() when + Pid :: pid(), + Ref :: reference(). + +monitor_process(_Pid, _Ref) -> + erlang:nif_error(undefined). + +-spec erts_internal:is_system_process(Pid) -> boolean() when + Pid :: pid(). + +is_system_process(_Pid) -> + erlang:nif_error(undefined). + +%% +%% BIF timer servers +%% + +-record(tsrv_state, {rtab, + ttab, + btr, + unit, + next}). + +bif_timer_server(N, BTR) -> + try + tsrv_loop(tsrv_init_static_state(N, BTR), infinity) + catch + Type:Reason -> + erlang:display({'BIF_timer_server', + {Type, Reason}, + erlang:get_stacktrace()}), + exit(Reason) + end. + +tsrv_init_static_state(N, BTR) -> + process_flag(trap_exit, true), + NList = integer_to_list(N), + RTabName = list_to_atom("BIF_timer_reference_table_" ++ NList), + TTabName = list_to_atom("BIF_timer_time_table_" ++ NList), + #tsrv_state{rtab = ets:new(RTabName, + [set, private, {keypos, 2}]), + ttab = ets:new(TTabName, + [ordered_set, private, {keypos, 1}]), + btr = BTR, + unit = erts_internal:time_unit(), + next = infinity}. + + +tsrv_loop(#tsrv_state{unit = Unit} = StaticState, Nxt) -> + CallTime = erlang:monotonic_time(), + %% 'infinity' is greater than all integers... + NewNxt = case CallTime >= Nxt of + true -> + tsrv_handle_timeout(CallTime, StaticState); + false -> + TMO = try + (1000*(Nxt - CallTime - 1)) div Unit + 1 + catch + error:badarith when Nxt == infinity -> infinity + end, + receive + Msg -> + tsrv_handle_msg(Msg, StaticState, Nxt) + after TMO -> + Nxt + end + end, + tsrv_loop(StaticState, NewNxt). + +tsrv_handle_msg({set_timeout, BTR, Proc, Time, TRef, Msg}, + #tsrv_state{rtab = RTab, + ttab = TTab, + btr = BTR}, + Nxt) when erlang:is_integer(Time) -> + RcvTime = erlang:monotonic_time(), + case Time =< RcvTime of + true -> + try Proc ! Msg catch _:_ -> ok end, + Nxt; + false -> + Ins = case erlang:is_atom(Proc) of + true -> + true; + false -> + try + erts_internal:monitor_process(Proc, TRef) + catch + _:_ -> false + end + end, + case Ins of + false -> + Nxt; + true -> + TKey = {Time, TRef}, + true = ets:insert(RTab, TKey), + true = ets:insert(TTab, {TKey, Proc, Msg}), + case Time < Nxt of + true -> Time; + false -> Nxt + end + end + end; +tsrv_handle_msg({cancel_timeout, BTR, From, Reply, Req, TRef}, + #tsrv_state{rtab = RTab, + ttab = TTab, + unit = Unit, + btr = BTR}, + Nxt) -> + case ets:lookup(RTab, TRef) of + [] -> + case Reply of + false -> + ok; + _ -> + try From ! {cancel_timer, Req, false} catch _:_ -> ok end + end, + Nxt; + [{Time, TRef} = TKey] -> + ets:delete(RTab, TRef), + ets:delete(TTab, TKey), + erlang:demonitor(TRef), + case Reply of + false -> + ok; + _ -> + RcvTime = erlang:monotonic_time(), + RT = case Time =< RcvTime of + true -> + 0; + false -> + ((1000*(Time - RcvTime)) div Unit) + end, + try From ! {cancel_timer, Req, RT} catch _:_ -> ok end + end, + case Time =:= Nxt of + false -> + Nxt; + true -> + case ets:first(TTab) of + '$end_of_table' -> infinity; + {NextTime, _TRef} -> NextTime + end + end + end; +tsrv_handle_msg({read_timeout, BTR, From, Req, TRef}, + #tsrv_state{rtab = RTab, + unit = Unit, + btr = BTR}, + Nxt) -> + case ets:lookup(RTab, TRef) of + [] -> + try From ! {read_timer, Req, false} catch _:_ -> ok end; + [{Time, TRef}] -> + RcvTime = erlang:monotonic_time(), + RT = case Time =< RcvTime of + true -> 0; + false -> (1000*(Time - RcvTime)) div Unit + end, + try From ! {read_timer, Req, RT} catch _:_ -> ok end + end, + Nxt; +tsrv_handle_msg({'DOWN', TRef, process, _, _}, + #tsrv_state{rtab = RTab, + ttab = TTab}, + Nxt) -> + case ets:lookup(RTab, TRef) of + [] -> + Nxt; + [{Time, TRef} = TKey] -> + ets:delete(RTab, TRef), + ets:delete(TTab, TKey), + case Time =:= Nxt of + false -> + Nxt; + true -> + case ets:first(TTab) of + '$end_of_table' -> infinity; + {NextTime, _} -> NextTime + end + end + end; +tsrv_handle_msg({cancel_all_timeouts, BTR, From, Ref}, + #tsrv_state{rtab = RTab, + ttab = TTab, + btr = BTR}, + _Nxt) -> + tsrv_delete_monitor_objects(RTab), + ets:delete_all_objects(TTab), + try From ! {canceled_all_timeouts, Ref} catch _:_ -> ok end, + infinity; +tsrv_handle_msg(_GarbageMsg, _StaticState, Nxt) -> + Nxt. + +tsrv_delete_monitor_objects(RTab) -> + case ets:first(RTab) of + '$end_of_table' -> + ok; + TRef -> + erlang:demonitor(TRef), + ets:delete(RTab, TRef), + tsrv_delete_monitor_objects(RTab) + end. + +tsrv_handle_timeout(CallTime, #tsrv_state{rtab = RTab, + ttab = TTab} = S) -> + case ets:first(TTab) of + '$end_of_table' -> + infinity; + {Time, _TRef} when Time > CallTime -> + Time; + {_Time, TRef} = TKey -> + [{TKey, Proc, Msg}] = ets:lookup(TTab, TKey), + case erlang:is_pid(Proc) of + false -> ok; + _ -> erlang:demonitor(TRef) + end, + ets:delete(TTab, TKey), + ets:delete(RTab, TRef), + try Proc ! Msg catch _:_ -> ok end, + tsrv_handle_timeout(CallTime, S) + end. diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index e95e11b3e6..48c5c37717 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -522,6 +522,7 @@ shutdown_pids(Heart,BootPid,State) -> Timer = shutdown_timer(State#state.flags), catch shutdown(State#state.kernel,BootPid,Timer,State), kill_all_pids(Heart), % Even the shutdown timer. + cancel_all_bif_timeouts(), kill_all_ports(Heart), flush_timout(Timer). @@ -580,6 +581,30 @@ resend([ExitMsg|Exits]) -> resend(_) -> ok. + +cancel_all_bif_timeouts() -> + TSrvs = erts_internal:get_bif_timer_servers(), + Ref = make_ref(), + {BTR, _TSrv} = erts_internal:access_bif_timer(Ref), %% Cheat... + request_cancel_all_bif_timeouts(Ref, BTR, TSrvs), + wait_response_cancel_all_bif_timeouts(Ref, BTR, TSrvs), + ok. + +request_cancel_all_bif_timeouts(_Ref, _BTR, []) -> + ok; +request_cancel_all_bif_timeouts(Ref, BTR, [TSrv|TSrvs]) -> + TSrv ! {cancel_all_timeouts, BTR, self(), {Ref, TSrv}}, + request_cancel_all_bif_timeouts(Ref, BTR, TSrvs). + +wait_response_cancel_all_bif_timeouts(_Ref, _BTR, []) -> + ok; +wait_response_cancel_all_bif_timeouts(Ref, BTR, [TSrv|TSrvs]) -> + receive + {canceled_all_timeouts, {Ref, TSrv}} -> + wait_response_cancel_all_bif_timeouts(Ref, BTR, TSrvs) + end. + + %% %% Kill all existing pids in the system (except init and heart). kill_all_pids(Heart) -> @@ -591,12 +616,9 @@ kill_all_pids(Heart) -> kill_all_pids(Heart) % Continue until all are really killed. end. -%% All except zombies. -alive_processes() -> - [P || P <- processes(), erlang:is_process_alive(P)]. - +%% All except system processes. get_pids(Heart) -> - Pids = alive_processes(), + Pids = [P || P <- processes(), not erts_internal:is_system_process(P)], delete(Heart,self(),Pids). delete(Heart,Init,[Heart|Pids]) -> delete(Heart,Init,Pids); -- cgit v1.2.3 From 1d9350693fe2c4d1d6b2baa504aacd070e023a1a Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 13 Feb 2015 10:12:02 +0100 Subject: Multiple timer wheels --- erts/emulator/beam/erl_bif_timer.c | 2 +- erts/emulator/beam/erl_init.c | 1 + erts/emulator/beam/erl_process.c | 242 ++++++++++++++------ erts/emulator/beam/erl_process.h | 4 + erts/emulator/beam/erl_time.h | 30 ++- erts/emulator/beam/erl_time_sup.c | 1 + erts/emulator/beam/time.c | 387 ++++++++++++++++++-------------- erts/emulator/beam/utils.c | 2 +- erts/emulator/sys/common/erl_check_io.c | 6 +- 9 files changed, 429 insertions(+), 246 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c index 8b444f2b01..0bd8d20c34 100644 --- a/erts/emulator/beam/erl_bif_timer.c +++ b/erts/emulator/beam/erl_bif_timer.c @@ -481,7 +481,7 @@ setup_bif_timer(Uint32 xflags, tab_insert(btm); ASSERT(btm == tab_find(ref)); - btm->tm.active = 0; /* MUST be initalized */ + erts_init_timer(&btm->tm); erts_set_timer(&btm->tm, (ErlTimeoutProc) bif_timer_timeout, (ErlCancelProc) bif_timer_cleanup, diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index be2c5ced9e..6708324a29 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -340,6 +340,7 @@ erl_init(int ncpu, no_dirty_io_schedulers #endif ); + erts_late_init_time_sup(); erts_init_cpu_topology(); /* Must be after init_scheduling */ erts_init_gc(); /* Must be after init_scheduling */ erts_alloc_late_init(); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 6e562e16c8..45b6fc5fb3 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2187,7 +2187,7 @@ aux_work_timeout_late_init(void) { aux_work_tmo->initialized = 1; if (erts_atomic32_read_nob(&aux_work_tmo->refc)) { - aux_work_tmo->timer.data.active = 0; + erts_init_timer(&aux_work_tmo->timer.data); erts_set_timer(&aux_work_tmo->timer.data, aux_work_timeout, NULL, @@ -2220,7 +2220,6 @@ aux_work_timeout(void *unused) if (refc != 1 || 1 != erts_atomic32_cmpxchg_relb(&aux_work_tmo->refc, 0, 1)) { /* Setup next timeout... */ - aux_work_tmo->timer.data.active = 0; erts_set_timer(&aux_work_tmo->timer.data, aux_work_timeout, NULL, @@ -2239,7 +2238,7 @@ setup_aux_work_timer(void) else #endif { - aux_work_tmo->timer.data.active = 0; + erts_init_timer(&aux_work_tmo->timer.data); erts_set_timer(&aux_work_tmo->timer.data, aux_work_timeout, NULL, @@ -2641,6 +2640,13 @@ thr_prgr_fin_wait(void *vssi) static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp); +void +erts_interupt_aux_thread_timed(ErtsMonotonicTime timeout_time) +{ + /* TODO only poke when needed (based on timeout_time) */ + erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(-1)); +} + static void * aux_thread(void *unused) { @@ -2649,6 +2655,11 @@ aux_thread(void *unused) erts_aint32_t aux_work; ErtsThrPrgrCallbacks callbacks; int thr_prgr_active = 1; + ErtsTimerWheel *timer_wheel = erts_default_timer_wheel; + ErtsNextTimeoutRef nxt_tmo_ref = erts_get_next_timeout_reference(timer_wheel); + + if (!timer_wheel) + ERTS_INTERNAL_ERROR("Missing aux timer wheel"); #ifdef ERTS_ENABLE_LOCK_CHECK { @@ -2672,6 +2683,7 @@ aux_thread(void *unused) sched_prep_spin_wait(ssi); while (1) { + ErtsMonotonicTime current_time; erts_aint32_t flgs; aux_work = erts_atomic32_read_acqb(&ssi->aux_work); @@ -2683,28 +2695,56 @@ aux_thread(void *unused) erts_thr_progress_leader_update(NULL); } - if (!aux_work) { - if (thr_prgr_active) - erts_thr_progress_active(NULL, thr_prgr_active = 0); - erts_thr_progress_prepare_wait(NULL); + if (aux_work) { + current_time = erts_get_monotonic_time(); + if (current_time >= erts_next_timeout_time(nxt_tmo_ref)) { + if (!thr_prgr_active) + erts_thr_progress_active(NULL, thr_prgr_active = 1); + erts_bump_timers(timer_wheel, current_time); + } + } + else { + ErtsMonotonicTime timeout_time; + timeout_time = erts_check_next_timeout_time(timer_wheel, + ERTS_SEC_TO_MONOTONIC(10*60)); + current_time = erts_get_monotonic_time(); + if (current_time >= timeout_time) { + if (!thr_prgr_active) + erts_thr_progress_active(NULL, thr_prgr_active = 1); + } + else { + if (thr_prgr_active) + erts_thr_progress_active(NULL, thr_prgr_active = 0); + erts_thr_progress_prepare_wait(NULL); - ERTS_SCHED_FAIR_YIELD(); + ERTS_SCHED_FAIR_YIELD(); - flgs = sched_spin_wait(ssi, 0); + flgs = sched_spin_wait(ssi, 0); - if (flgs & ERTS_SSI_FLG_SLEEPING) { - ASSERT(flgs & ERTS_SSI_FLG_WAITING); - flgs = sched_set_sleeptype(ssi, ERTS_SSI_FLG_TSE_SLEEPING); if (flgs & ERTS_SSI_FLG_SLEEPING) { - int res; - ASSERT(flgs & ERTS_SSI_FLG_TSE_SLEEPING); ASSERT(flgs & ERTS_SSI_FLG_WAITING); - do { - res = erts_tse_wait(ssi->event); - } while (res == EINTR); + flgs = sched_set_sleeptype(ssi, ERTS_SSI_FLG_TSE_SLEEPING); + if (flgs & ERTS_SSI_FLG_SLEEPING) { + int res; + ASSERT(flgs & ERTS_SSI_FLG_TSE_SLEEPING); + ASSERT(flgs & ERTS_SSI_FLG_WAITING); + current_time = erts_get_monotonic_time(); + do { + Sint64 timeout; + if (current_time >= timeout_time) + break; + timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time + - current_time + - 1) + 1; + res = erts_tse_twait(ssi->event, timeout); + current_time = erts_get_monotonic_time(); + } while (res == EINTR); + } } + erts_thr_progress_finalize_wait(NULL); } - erts_thr_progress_finalize_wait(NULL); + if (current_time >= timeout_time) + erts_bump_timers(timer_wheel, current_time); } flgs = sched_prep_spin_wait(ssi); @@ -2771,6 +2811,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) sched_wall_time_change(esdp, thr_prgr_active); while (1) { + ErtsMonotonicTime current_time; aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work) { @@ -2784,34 +2825,65 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erts_thr_progress_leader_update(esdp); } - if (aux_work) + if (aux_work) { flgs = erts_smp_atomic32_read_acqb(&ssi->flags); + current_time = erts_get_monotonic_time(); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + erts_bump_timers(esdp->timer_wheel, current_time); + } + } else { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { - if (thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 0); - sched_wall_time_change(esdp, 0); + ErtsMonotonicTime timeout_time; + timeout_time = erts_check_next_timeout_time(esdp->timer_wheel, + ERTS_SEC_TO_MONOTONIC(10*60)); + current_time = erts_get_monotonic_time(); + if (current_time >= timeout_time) { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); } - erts_thr_progress_prepare_wait(esdp); } + else { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 0); + sched_wall_time_change(esdp, 0); + } + erts_thr_progress_prepare_wait(esdp); + } - ERTS_SCHED_FAIR_YIELD(); + ERTS_SCHED_FAIR_YIELD(); - flgs = sched_spin_wait(ssi, spincount); - if (flgs & ERTS_SSI_FLG_SLEEPING) { - ASSERT(flgs & ERTS_SSI_FLG_WAITING); - flgs = sched_set_sleeptype(ssi, ERTS_SSI_FLG_TSE_SLEEPING); + flgs = sched_spin_wait(ssi, spincount); if (flgs & ERTS_SSI_FLG_SLEEPING) { - int res; - ASSERT(flgs & ERTS_SSI_FLG_TSE_SLEEPING); ASSERT(flgs & ERTS_SSI_FLG_WAITING); - do { - res = erts_tse_twait(ssi->event, -1); - } while (res == EINTR); + flgs = sched_set_sleeptype(ssi, ERTS_SSI_FLG_TSE_SLEEPING); + if (flgs & ERTS_SSI_FLG_SLEEPING) { + int res; + ASSERT(flgs & ERTS_SSI_FLG_TSE_SLEEPING); + ASSERT(flgs & ERTS_SSI_FLG_WAITING); + current_time = erts_get_monotonic_time(); + do { + Sint64 timeout; + if (current_time >= timeout_time) + break; + timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time + - current_time + - 1) + 1; + res = erts_tse_twait(ssi->event, timeout); + current_time = erts_get_monotonic_time(); + } while (res == EINTR); + } } + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) + erts_thr_progress_finalize_wait(esdp); } - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) - erts_thr_progress_finalize_wait(esdp); + if (current_time >= timeout_time) + erts_bump_timers(esdp->timer_wheel, current_time); } if (!(flgs & ERTS_SSI_FLG_WAITING)) { @@ -2879,8 +2951,8 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erl_sys_schedule(1); /* Might give us something to do */ current_time = erts_get_monotonic_time(); - if (current_time >= erts_next_timeout_time()) - erts_bump_timers(current_time); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) + erts_bump_timers(esdp->timer_wheel, current_time); sys_aux_work: #ifndef ERTS_SMP @@ -2997,8 +3069,8 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) { ErtsMonotonicTime current_time = erts_get_monotonic_time(); - if (current_time >= erts_next_timeout_time()) - erts_bump_timers(current_time); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) + erts_bump_timers(esdp->timer_wheel, current_time); } #ifndef ERTS_SMP @@ -5269,6 +5341,10 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num, #else esdp->no = (Uint) num; #endif + + esdp->timer_wheel = erts_default_timer_wheel; + esdp->next_tmo_ref = erts_get_next_timeout_reference(esdp->timer_wheel); + esdp->ssi = ssi; esdp->current_process = NULL; esdp->current_port = NULL; @@ -6765,6 +6841,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) erts_smp_mtx_unlock(&schdlr_sspnd.mtx); while (1) { + ErtsMonotonicTime current_time; erts_aint32_t qmask; erts_aint32_t flgs; @@ -6789,30 +6866,64 @@ suspend_scheduler(ErtsSchedulerData *esdp) } } - if (!aux_work) { - if (thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 0); - sched_wall_time_change(esdp, 0); + if (aux_work) { + current_time = erts_get_monotonic_time(); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + erts_bump_timers(esdp->timer_wheel, current_time); } - erts_thr_progress_prepare_wait(esdp); - flgs = sched_spin_suspended(ssi, - ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT); - if (flgs == (ERTS_SSI_FLG_SLEEPING - | ERTS_SSI_FLG_WAITING - | ERTS_SSI_FLG_SUSPENDED)) { - flgs = sched_set_suspended_sleeptype(ssi); + } + else { + ErtsMonotonicTime timeout_time; + timeout_time = erts_check_next_timeout_time(esdp->timer_wheel, + ERTS_SEC_TO_MONOTONIC(60*60)); + current_time = erts_get_monotonic_time(); + + if (current_time >= timeout_time) { + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + } + else { + if (thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 0); + sched_wall_time_change(esdp, 0); + } + erts_thr_progress_prepare_wait(esdp); + flgs = sched_spin_suspended(ssi, + ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT); if (flgs == (ERTS_SSI_FLG_SLEEPING - | ERTS_SSI_FLG_TSE_SLEEPING | ERTS_SSI_FLG_WAITING | ERTS_SSI_FLG_SUSPENDED)) { - int res; - - do { - res = erts_tse_twait(ssi->event, -1); - } while (res == EINTR); + flgs = sched_set_suspended_sleeptype(ssi); + if (flgs == (ERTS_SSI_FLG_SLEEPING + | ERTS_SSI_FLG_TSE_SLEEPING + | ERTS_SSI_FLG_WAITING + | ERTS_SSI_FLG_SUSPENDED)) { + int res; + + current_time = erts_get_monotonic_time(); + do { + Sint64 timeout; + if (current_time >= timeout_time) + break; + timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time + - current_time + - 1) + 1; + res = erts_tse_twait(ssi->event, timeout); + current_time = erts_get_monotonic_time(); + } while (res == EINTR); + } } + erts_thr_progress_finalize_wait(esdp); } - erts_thr_progress_finalize_wait(esdp); + + if (current_time >= timeout_time) + erts_bump_timers(esdp->timer_wheel, current_time); } flgs = sched_prep_spin_suspended(ssi, (ERTS_SSI_FLG_WAITING @@ -7631,6 +7742,9 @@ sched_thread_func(void *vesdp) ErtsThrPrgrCallbacks callbacks; ErtsSchedulerData *esdp = vesdp; Uint no = esdp->no; + + esdp->timer_wheel = erts_create_timer_wheel((int) no); + esdp->next_tmo_ref = erts_get_next_timeout_reference(esdp->timer_wheel); #ifdef ERTS_SMP ERTS_SCHED_SLEEP_INFO_IX(no - 1)->event = erts_tse_fetch(); callbacks.arg = (void *) esdp->ssi; @@ -9048,9 +9162,9 @@ Process *schedule(Process *p, int calls) { ErtsMonotonicTime current_time = erts_get_monotonic_time(); - if (current_time >= erts_next_timeout_time()) { + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { erts_smp_runq_unlock(rq); - erts_bump_timers(current_time); + erts_bump_timers(esdp->timer_wheel, current_time); erts_smp_runq_lock(rq); } } @@ -9213,8 +9327,8 @@ Process *schedule(Process *p, int calls) erl_sys_schedule(1); current_time = erts_get_monotonic_time(); - if (current_time >= erts_next_timeout_time()) - erts_bump_timers(current_time); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) + erts_bump_timers(esdp->timer_wheel, current_time); #ifdef ERTS_SMP erts_smp_runq_lock(rq); @@ -10652,7 +10766,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). #ifdef ERTS_SMP p->common.u.alive.ptimer = NULL; #else - sys_memset(&p->common.u.alive.tm, 0, sizeof(ErlTimer)); + erts_init_timer(&p->common.u.alive.tm); #endif p->common.u.alive.reg = NULL; @@ -10845,7 +10959,7 @@ void erts_init_empty_process(Process *p) #ifdef ERTS_SMP p->common.u.alive.ptimer = NULL; #else - memset(&(p->common.u.alive.tm), 0, sizeof(ErlTimer)); + erts_init_timer(&p->common.u.alive.tm); #endif p->next = NULL; p->off_heap.first = NULL; diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 53a992e115..77a4b45b09 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -562,6 +562,8 @@ struct ErtsSchedulerData_ { Eterm* x_reg_array; /* X registers */ FloatDef* f_reg_array; /* Floating point registers. */ + ErtsTimerWheel *timer_wheel; + ErtsNextTimeoutRef next_tmo_ref; #ifdef ERTS_SMP ethr_tid tid; /* Thread id */ struct erl_bits_state erl_bits_state; /* erl_bits.c state */ @@ -2237,6 +2239,8 @@ extern int erts_disable_proc_not_running_opt; void erts_smp_notify_inc_runq(ErtsRunQueue *runq); +void erts_interupt_aux_thread_timed(ErtsMonotonicTime timeout_time); + #ifdef ERTS_SMP void erts_sched_finish_poke(ErtsSchedulerSleepInfo *, erts_aint32_t); ERTS_GLB_INLINE void erts_sched_poke(ErtsSchedulerSleepInfo *ssi); diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index e461594e9c..7933f4973d 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -26,6 +26,10 @@ #define ERTS_TIME_ASSERT(B) ((void) 1) #endif +typedef struct ErtsTimerWheel_ ErtsTimerWheel; +typedef erts_atomic64_t * ErtsNextTimeoutRef; +extern ErtsTimerWheel *erts_default_timer_wheel; + extern SysTimeval erts_first_emu_time; /* @@ -35,8 +39,8 @@ typedef struct erl_timer { struct erl_timer* next; /* next entry tiw slot or chain */ struct erl_timer* prev; /* prev entry tiw slot or chain */ Uint slot; /* slot in timer wheel */ + erts_smp_atomic_t wheel; ErtsMonotonicTime timeout_pos; /* Timeout in absolute clock ticks */ - int active; /* 1=activated, 0=deactivated */ /* called when timeout */ void (*timeout)(void*); /* called when cancel (may be NULL) */ @@ -63,7 +67,6 @@ union ErtsSmpPTimer_ { ErtsSmpPTimer *next; }; - void erts_create_smp_ptimer(ErtsSmpPTimer **timer_ref, Eterm id, ErlTimeoutProc timeout_func, @@ -79,28 +82,35 @@ void erts_late_init_time_sup(void); /* timer-wheel api */ +ErtsTimerWheel *erts_create_timer_wheel(int); +ErtsNextTimeoutRef erts_get_next_timeout_reference(ErtsTimerWheel *); void erts_init_time(int time_correction, ErtsTimeWarpMode time_warp_mode); void erts_set_timer(ErlTimer*, ErlTimeoutProc, ErlCancelProc, void*, Uint); void erts_cancel_timer(ErlTimer*); -void erts_bump_timers(ErtsMonotonicTime); -Uint erts_timer_wheel_memory_size(void); Uint erts_time_left(ErlTimer *); +void erts_bump_timers(ErtsTimerWheel *, ErtsMonotonicTime); +Uint erts_timer_wheel_memory_size(void); #ifdef DEBUG void erts_p_slpq(void); #endif -ErtsMonotonicTime erts_check_next_timeout_time(ErtsMonotonicTime); +ErtsMonotonicTime erts_check_next_timeout_time(ErtsTimerWheel *, + ErtsMonotonicTime); -extern erts_atomic64_t erts_next_timeout__; - -ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(void); +ERTS_GLB_INLINE void erts_init_timer(ErlTimer *p); +ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(ErtsNextTimeoutRef); #if ERTS_GLB_INLINE_INCL_FUNC_DEF -ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(void) +ERTS_GLB_INLINE void erts_init_timer(ErlTimer *p) +{ + erts_smp_atomic_init_nob(&p->wheel, (erts_aint_t) NULL); +} + +ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(ErtsNextTimeoutRef nxt_tmo_ref) { - return (ErtsMonotonicTime) erts_atomic64_read_acqb(&erts_next_timeout__); + return (ErtsMonotonicTime) erts_atomic64_read_acqb((erts_atomic64_t *) nxt_tmo_ref); } #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 7dfa7d8743..d47d1682d7 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -690,6 +690,7 @@ static void late_init_time_correction(void) { if (time_sup.inf.c.finalized_offset) { + erts_init_timer(&time_sup.inf.c.parmon.timer); erts_set_timer(&time_sup.inf.c.parmon.timer, #ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC init_check_time_correction, diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index c9f8b68bca..9f997e1d0b 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -86,9 +86,6 @@ #define ERTS_MONOTONIC_DAY ERTS_SEC_TO_MONOTONIC(60*60*24) #define ERTS_CLKTCKS_DAY ERTS_MONOTONIC_TO_CLKTCKS(ERTS_MONOTONIC_DAY) -static erts_smp_atomic32_t is_bumping; -static erts_smp_mtx_t tiw_lock; - /* BEGIN tiw_lock protected variables ** @@ -101,14 +98,6 @@ static erts_smp_mtx_t tiw_lock; #else #define TIW_SIZE (1 << 20) #endif -static ErlTimer** tiw; /* the timing wheel, allocated in init_time() */ -static ErtsMonotonicTime tiw_pos; /* current position in wheel */ -static Uint tiw_nto; /* number of timeouts in wheel */ -static struct { - ErlTimer *head; - ErlTimer **tail; - Uint nto; -} tiw_at_once; /* Actual interval time chosen by sys_init_time() */ @@ -120,23 +109,52 @@ static int tiw_itime; /* Constant after init */ # define TIW_ITIME tiw_itime #endif -static int true_next_timeout_time; -static ErtsMonotonicTime next_timeout_time; -erts_atomic64_t erts_next_timeout__; +struct ErtsTimerWheel_ { + ErlTimer *w[TIW_SIZE]; + ErtsMonotonicTime pos; + Uint nto; + struct { + ErlTimer *head; + ErlTimer **tail; + Uint nto; + } at_once; + int true_next_timeout_time; + ErtsMonotonicTime next_timeout_time; + erts_atomic64_t next_timeout; + erts_smp_atomic32_t is_bumping; + erts_smp_mtx_t lock; +}; + +ErtsTimerWheel *erts_default_timer_wheel; /* managed by aux thread */ + +static ERTS_INLINE ErtsTimerWheel * +get_timer_wheel(ErlTimer *p) +{ + return (ErtsTimerWheel *) erts_smp_atomic_read_acqb(&p->wheel); +} + +static ERTS_INLINE void +set_timer_wheel(ErlTimer *p, ErtsTimerWheel *tiw) +{ + erts_smp_atomic_set_relb(&p->wheel, (erts_aint_t) tiw); +} static ERTS_INLINE void -init_next_timeout(ErtsMonotonicTime time) +init_next_timeout(ErtsTimerWheel *tiw, + ErtsMonotonicTime time) { - erts_atomic64_init_nob(&erts_next_timeout__, + erts_atomic64_init_nob(&tiw->next_timeout, (erts_aint64_t) time); } static ERTS_INLINE void -set_next_timeout(ErtsMonotonicTime time, int true_timeout) +set_next_timeout(ErtsTimerWheel *tiw, + ErtsMonotonicTime time, + int true_timeout) { - true_next_timeout_time = true_timeout; - next_timeout_time = time; - erts_atomic64_set_relb(&erts_next_timeout__, + tiw->true_next_timeout_time = true_timeout; + tiw->next_timeout_time = time; + erts_atomic64_set_relb(&tiw->next_timeout, (erts_aint64_t) time); } @@ -144,7 +162,8 @@ set_next_timeout(ErtsMonotonicTime time, int true_timeout) or -1 if there are no timeouts */ static ERTS_INLINE ErtsMonotonicTime -find_next_timeout(ErtsMonotonicTime curr_time, +find_next_timeout(ErtsTimerWheel *tiw, + ErtsMonotonicTime curr_time, ErtsMonotonicTime max_search_time) { int start_ix, tiw_pos_ix; @@ -152,16 +171,16 @@ find_next_timeout(ErtsMonotonicTime curr_time, int true_min_timeout; ErtsMonotonicTime min_timeout, min_timeout_pos, slot_timeout_pos, timeout_limit; - ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&tiw_lock)); + ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&tiw->lock)); - if (true_next_timeout_time) - return next_timeout_time; + if (tiw->true_next_timeout_time) + return tiw->next_timeout_time; /* We never set next timeout beyond timeout_limit */ timeout_limit = curr_time + ERTS_MONOTONIC_DAY; - if (tiw_nto == 0) { /* no timeouts in wheel */ - true_min_timeout = true_next_timeout_time = 0; + if (tiw->nto == 0) { /* no timeouts in wheel */ + true_min_timeout = tiw->true_next_timeout_time = 0; min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(timeout_limit); goto found_next; } @@ -170,13 +189,13 @@ find_next_timeout(ErtsMonotonicTime curr_time, * Don't want others entering trying to bump * timers while we are checking... */ - set_next_timeout(timeout_limit, 0); + set_next_timeout(tiw, timeout_limit, 0); true_min_timeout = 1; - slot_timeout_pos = tiw_pos; + slot_timeout_pos = tiw->pos; min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time + max_search_time); - start_ix = tiw_pos_ix = (int) (tiw_pos & (TIW_SIZE-1)); + start_ix = tiw_pos_ix = (int) (tiw->pos & (TIW_SIZE-1)); do { slot_timeout_pos++; @@ -185,7 +204,7 @@ find_next_timeout(ErtsMonotonicTime curr_time, break; } - p = tiw[tiw_pos_ix]; + p = tiw->w[tiw_pos_ix]; while (p) { ErtsMonotonicTime timeout_pos; @@ -207,16 +226,18 @@ find_next_timeout(ErtsMonotonicTime curr_time, found_next: min_timeout = ERTS_CLKTCKS_TO_MONOTONIC(min_timeout_pos); - if (min_timeout != next_timeout_time) - set_next_timeout(min_timeout, true_min_timeout); + if (min_timeout != tiw->next_timeout_time) + set_next_timeout(tiw, min_timeout, true_min_timeout); return min_timeout; } -static void remove_timer(ErlTimer *p) { +static void +remove_timer(ErtsTimerWheel *tiw, ErlTimer *p) +{ /* first */ if (!p->prev) { - tiw[p->slot] = p->next; + tiw->w[p->slot] = p->next; if(p->next) p->next->prev = NULL; } else { @@ -233,40 +254,41 @@ static void remove_timer(ErlTimer *p) { p->next = NULL; p->prev = NULL; - /* Make sure cancel callback isn't called */ - p->active = 0; - tiw_nto--; + + set_timer_wheel(p, NULL); + tiw->nto--; } ErtsMonotonicTime -erts_check_next_timeout_time(ErtsMonotonicTime max_search_time) +erts_check_next_timeout_time(ErtsTimerWheel *tiw, + ErtsMonotonicTime max_search_time) { ErtsMonotonicTime next, curr; curr = erts_get_monotonic_time(); - erts_smp_mtx_lock(&tiw_lock); + erts_smp_mtx_lock(&tiw->lock); - next = find_next_timeout(curr, max_search_time); + next = find_next_timeout(tiw, curr, max_search_time); - erts_smp_mtx_unlock(&tiw_lock); + erts_smp_mtx_unlock(&tiw->lock); return next; } #ifndef DEBUG -#define ERTS_DBG_CHK_SAFE_TO_SKIP_TO(TO) ((void) 0) +#define ERTS_DBG_CHK_SAFE_TO_SKIP_TO(TIW, TO) ((void) 0) #else -#define ERTS_DBG_CHK_SAFE_TO_SKIP_TO(TO) debug_check_safe_to_skip_to((TO)) +#define ERTS_DBG_CHK_SAFE_TO_SKIP_TO(TIW, TO) debug_check_safe_to_skip_to((TIW), (TO)) static void -debug_check_safe_to_skip_to(ErtsMonotonicTime skip_to_pos) +debug_check_safe_to_skip_to(ErtsTimerWheel *tiw, ErtsMonotonicTime skip_to_pos) { int slots, ix; ErlTimer *tmr; ErtsMonotonicTime tmp; - ix = (int) (tiw_pos & (TIW_SIZE-1)); - tmp = skip_to_pos - tiw_pos; + ix = (int) (tiw->pos & (TIW_SIZE-1)); + tmp = skip_to_pos - tiw->pos; ASSERT(tmp >= 0); if (tmp < (ErtsMonotonicTime) TIW_SIZE) slots = (int) tmp; @@ -274,7 +296,7 @@ debug_check_safe_to_skip_to(ErtsMonotonicTime skip_to_pos) slots = TIW_SIZE; while (slots > 0) { - tmr = tiw[ix]; + tmr = tiw->w[ix]; while (tmr) { ASSERT(tmr->timeout_pos > skip_to_pos); tmr = tmr->next; @@ -287,79 +309,80 @@ debug_check_safe_to_skip_to(ErtsMonotonicTime skip_to_pos) } #endif -void erts_bump_timers(ErtsMonotonicTime curr_time) +void +erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time) { int tiw_pos_ix, slots; ErlTimer *p, *timeout_head, **timeout_tail; ErtsMonotonicTime bump_to, tmp_slots; - if (erts_smp_atomic32_cmpxchg_nob(&is_bumping, 1, 0) != 0) + if (erts_smp_atomic32_cmpxchg_nob(&tiw->is_bumping, 1, 0) != 0) return; /* Another thread is currently bumping... */ bump_to = ERTS_MONOTONIC_TO_CLKTCKS(curr_time); - erts_smp_mtx_lock(&tiw_lock); + erts_smp_mtx_lock(&tiw->lock); - if (tiw_pos >= bump_to) { + if (tiw->pos >= bump_to) { timeout_head = NULL; goto done; } /* Don't want others here while we are bumping... */ - set_next_timeout(curr_time + ERTS_MONOTONIC_DAY, 0); + set_next_timeout(tiw, curr_time + ERTS_MONOTONIC_DAY, 0); - if (!tiw_at_once.head) { + if (!tiw->at_once.head) { timeout_head = NULL; timeout_tail = &timeout_head; } else { - ASSERT(tiw_nto >= tiw_at_once.nto); - timeout_head = tiw_at_once.head; - timeout_tail = tiw_at_once.tail; - tiw_nto -= tiw_at_once.nto; - tiw_at_once.head = NULL; - tiw_at_once.tail = &tiw_at_once.head; - tiw_at_once.nto = 0; + ASSERT(tiw->nto >= tiw->at_once.nto); + timeout_head = tiw->at_once.head; + timeout_tail = tiw->at_once.tail; + tiw->nto -= tiw->at_once.nto; + tiw->at_once.head = NULL; + tiw->at_once.tail = &tiw->at_once.head; + tiw->at_once.nto = 0; } - if (tiw_nto == 0) { - ERTS_DBG_CHK_SAFE_TO_SKIP_TO(bump_to); - tiw_pos = bump_to; + if (tiw->nto == 0) { + ERTS_DBG_CHK_SAFE_TO_SKIP_TO(tiw, bump_to); + tiw->pos = bump_to; goto done; } - if (true_next_timeout_time) { + if (tiw->true_next_timeout_time) { ErtsMonotonicTime skip_until_pos; /* * No need inspecting slots where we know no timeouts * to trigger should reside. */ - skip_until_pos = ERTS_MONOTONIC_TO_CLKTCKS(next_timeout_time); + skip_until_pos = ERTS_MONOTONIC_TO_CLKTCKS(tiw->next_timeout_time); if (skip_until_pos > bump_to) skip_until_pos = bump_to; - ERTS_DBG_CHK_SAFE_TO_SKIP_TO(skip_until_pos); - ASSERT(skip_until_pos > tiw_pos); + ERTS_DBG_CHK_SAFE_TO_SKIP_TO(tiw, skip_until_pos); + ASSERT(skip_until_pos > tiw->pos); - tiw_pos = skip_until_pos - 1; + tiw->pos = skip_until_pos - 1; } - tiw_pos_ix = (int) ((tiw_pos+1) & (TIW_SIZE-1)); - tmp_slots = (bump_to - tiw_pos); + tiw_pos_ix = (int) ((tiw->pos+1) & (TIW_SIZE-1)); + tmp_slots = (bump_to - tiw->pos); if (tmp_slots < (ErtsMonotonicTime) TIW_SIZE) slots = (int) tmp_slots; else slots = TIW_SIZE; while (slots > 0) { - p = tiw[tiw_pos_ix]; + p = tiw->w[tiw_pos_ix]; while (p) { ErlTimer *next = p->next; ASSERT(p != next); if (p->timeout_pos <= bump_to) { /* we have a timeout */ /* Remove from list */ - remove_timer(p); + remove_timer(tiw, p); *timeout_tail = p; /* Insert in timeout queue */ timeout_tail = &p->next; } @@ -374,19 +397,21 @@ void erts_bump_timers(ErtsMonotonicTime curr_time) ASSERT(tmp_slots >= (ErtsMonotonicTime) TIW_SIZE || tiw_pos_ix == (int) ((bump_to+1) & (TIW_SIZE-1))); - tiw_pos = bump_to; + tiw->pos = bump_to; /* Search at most two seconds ahead... */ - (void) find_next_timeout(curr_time, ERTS_SEC_TO_MONOTONIC(2)); + (void) find_next_timeout(tiw, curr_time, ERTS_SEC_TO_MONOTONIC(2)); done: - erts_smp_mtx_unlock(&tiw_lock); + erts_smp_mtx_unlock(&tiw->lock); - erts_smp_atomic32_set_nob(&is_bumping, 0); + erts_smp_atomic32_set_nob(&tiw->is_bumping, 0); /* Call timedout timers callbacks */ while (timeout_head) { + ErlTimeoutProc timeout; + void *arg; p = timeout_head; timeout_head = p->next; /* Here comes hairy use of the timer fields! @@ -399,23 +424,61 @@ done: p->next = NULL; p->prev = NULL; p->slot = 0; - (*p->timeout)(p->arg); + timeout = p->timeout; + arg = p->arg; + (*timeout)(arg); } } Uint erts_timer_wheel_memory_size(void) { - return (Uint) TIW_SIZE * sizeof(ErlTimer*); +#ifdef ERTS_SMP + return sizeof(ErtsTimerWheel)*(1 + erts_no_schedulers); +#else + return sizeof(ErtsTimerWheel); +#endif } +ErtsTimerWheel * +erts_create_timer_wheel(int no) +{ + ErtsMonotonicTime mtime; + int i; + ErtsTimerWheel *tiw; + tiw = (ErtsTimerWheel *) erts_alloc(ERTS_ALC_T_TIMER_WHEEL, + sizeof(ErtsTimerWheel)); + for(i = 0; i < TIW_SIZE; i++) + tiw->w[i] = NULL; + + erts_smp_atomic32_init_nob(&tiw->is_bumping, 0); + erts_smp_mtx_init_x(&tiw->lock, "timer_wheel", make_small(no)); + + mtime = erts_get_monotonic_time(); + tiw->pos = ERTS_MONOTONIC_TO_CLKTCKS(mtime); + tiw->nto = 0; + tiw->at_once.head = NULL; + tiw->at_once.tail = &tiw->at_once.head; + tiw->at_once.nto = 0; + tiw->true_next_timeout_time = 0; + tiw->next_timeout_time = mtime + ERTS_MONOTONIC_DAY; + init_next_timeout(tiw, mtime + ERTS_MONOTONIC_DAY); + return tiw; +} + +ErtsNextTimeoutRef +erts_get_next_timeout_reference(ErtsTimerWheel *tiw) +{ + return (ErtsNextTimeoutRef) &tiw->next_timeout; +} + + /* this routine links the time cells into a free list at the start and sets the time queue as empty */ void erts_init_time(int time_correction, ErtsTimeWarpMode time_warp_mode) { - ErtsMonotonicTime mtime; - int i, itime; + int itime; /* system dependent init; must be done before do_time_init() if timer thread is enabled */ @@ -428,41 +491,39 @@ erts_init_time(int time_correction, ErtsTimeWarpMode time_warp_mode) tiw_itime = itime; #endif - erts_smp_atomic32_init_nob(&is_bumping, 0); - erts_smp_mtx_init(&tiw_lock, "timer_wheel"); - - tiw = (ErlTimer**) erts_alloc(ERTS_ALC_T_TIMER_WHEEL, - TIW_SIZE * sizeof(ErlTimer*)); - for(i = 0; i < TIW_SIZE; i++) - tiw[i] = NULL; - - mtime = erts_get_monotonic_time(); - tiw_pos = ERTS_MONOTONIC_TO_CLKTCKS(mtime); - tiw_nto = 0; - tiw_at_once.head = NULL; - tiw_at_once.tail = &tiw_at_once.head; - tiw_at_once.nto = 0; - init_next_timeout(mtime + ERTS_MONOTONIC_DAY); - - erts_late_init_time_sup(); + erts_default_timer_wheel = erts_create_timer_wheel(0); } +void +erts_set_timer(ErlTimer *p, ErlTimeoutProc timeout, + ErlCancelProc cancel, void *arg, Uint to) +{ + ErtsMonotonicTime timeout_time, timeout_pos; + ErtsMonotonicTime curr_time; + ErtsTimerWheel *tiw; + ErtsSchedulerData *esdp; + + curr_time = erts_get_monotonic_time(); + esdp = erts_get_scheduler_data(); + if (esdp) + tiw = esdp->timer_wheel; + else + tiw = erts_default_timer_wheel; + erts_smp_mtx_lock(&tiw->lock); + if (get_timer_wheel(p)) + ERTS_INTERNAL_ERROR("Double set timer"); -/* -** Insert a process into the time queue, with a timeout 't' -*/ -static ErtsMonotonicTime -insert_timer(ErlTimer* p, ErtsMonotonicTime curr_time, ErtsMonotonicTime to) -{ - ErtsMonotonicTime timeout_time, timeout_pos; + p->timeout = timeout; + p->cancel = cancel; + p->arg = arg; if (to == 0) { timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time); - tiw_nto++; - tiw_at_once.nto++; - *tiw_at_once.tail = p; + tiw->nto++; + tiw->at_once.nto++; + *tiw->at_once.tail = p; p->next = NULL; p->timeout_pos = timeout_pos; timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(timeout_pos); @@ -479,13 +540,13 @@ insert_timer(ErlTimer* p, ErtsMonotonicTime curr_time, ErtsMonotonicTime to) p->slot = (Uint) tm; /* insert at head of list at slot */ - p->next = tiw[tm]; + p->next = tiw->w[tm]; p->prev = NULL; if (p->next != NULL) p->next->prev = p; - tiw[tm] = p; + tiw->w[tm] = p; - tiw_nto++; + tiw->nto++; timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(timeout_pos); p->timeout_pos = timeout_pos; @@ -495,59 +556,45 @@ insert_timer(ErlTimer* p, ErtsMonotonicTime curr_time, ErtsMonotonicTime to) < ERTS_MSEC_TO_MONOTONIC(to) + ERTS_CLKTCKS_TO_MONOTONIC(1)); } - if (timeout_time < next_timeout_time) - set_next_timeout(timeout_time, 1); + if (timeout_time < tiw->next_timeout_time) + set_next_timeout(tiw, timeout_time, 1); - return timeout_time; -} + set_timer_wheel(p, tiw); + + erts_smp_mtx_unlock(&tiw->lock); -void -erts_set_timer(ErlTimer* p, ErlTimeoutProc timeout, ErlCancelProc cancel, - void* arg, Uint t) -{ -#ifdef ERTS_SMP - ErtsMonotonicTime timeout_time; -#endif - ErtsMonotonicTime current_time = erts_get_monotonic_time(); - erts_smp_mtx_lock(&tiw_lock); - if (p->active) { /* XXX assert ? */ - erts_smp_mtx_unlock(&tiw_lock); - return; - } - p->timeout = timeout; - p->cancel = cancel; - p->arg = arg; - p->active = 1; -#ifdef ERTS_SMP - timeout_time = -#else - (void) -#endif - insert_timer(p, current_time, (ErtsMonotonicTime) t); - erts_smp_mtx_unlock(&tiw_lock); #if defined(ERTS_SMP) - erts_sys_schedule_interrupt_timed(1, timeout_time); + if (tiw == erts_default_timer_wheel) + erts_interupt_aux_thread_timed(timeout_time); #endif + } void -erts_cancel_timer(ErlTimer* p) +erts_cancel_timer(ErlTimer *p) { - erts_smp_mtx_lock(&tiw_lock); - if (!p->active) { /* allow repeated cancel (drivers) */ - erts_smp_mtx_unlock(&tiw_lock); - return; - } - - remove_timer(p); - p->slot = 0; + ErtsTimerWheel *tiw; + ErlCancelProc cancel; + void *arg; - if (p->cancel != NULL) { - erts_smp_mtx_unlock(&tiw_lock); - (*p->cancel)(p->arg); + tiw = get_timer_wheel(p); + if (!tiw) return; + + erts_smp_mtx_lock(&tiw->lock); + if (tiw != get_timer_wheel(p)) + cancel = NULL; + else { + remove_timer(tiw, p); + p->slot = 0; + + cancel = p->cancel; + arg = p->arg; } - erts_smp_mtx_unlock(&tiw_lock); + erts_smp_mtx_unlock(&tiw->lock); + + if (cancel) + (*cancel)(arg); } /* @@ -559,18 +606,19 @@ erts_cancel_timer(ErlTimer* p) Uint erts_time_left(ErlTimer *p) { + ErtsTimerWheel *tiw; ErtsMonotonicTime current_time, timeout_time; - erts_smp_mtx_lock(&tiw_lock); - - if (!p->active) { - erts_smp_mtx_unlock(&tiw_lock); + tiw = get_timer_wheel(p); + if (!tiw) return 0; - } - timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(p->timeout_pos); - - erts_smp_mtx_unlock(&tiw_lock); + erts_smp_mtx_lock(&tiw->lock); + if (tiw != get_timer_wheel(p)) + timeout_time = ERTS_MONOTONIC_TIME_MIN; + else + timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(p->timeout_pos); + erts_smp_mtx_unlock(&tiw->lock); current_time = erts_get_monotonic_time(); if (timeout_time <= current_time) @@ -581,34 +629,35 @@ erts_time_left(ErlTimer *p) #ifdef DEBUG void erts_p_slpq(void) { + ErtsTimerWheel *tiw = erts_default_timer_wheel; ErtsMonotonicTime current_time = erts_get_monotonic_time(); int i; ErlTimer* p; - erts_smp_mtx_lock(&tiw_lock); + erts_smp_mtx_lock(&tiw->lock); /* print the whole wheel, starting at the current position */ erts_printf("\ncurrent time = %bps tiw_pos = %d tiw_nto %d\n", - current_time, tiw_pos, tiw_nto); - i = tiw_pos; - if (tiw[i] != NULL) { + current_time, tiw->pos, tiw->nto); + i = tiw->pos; + if (tiw->w[i] != NULL) { erts_printf("%d:\n", i); - for(p = tiw[i]; p != NULL; p = p->next) { + for(p = tiw->w[i]; p != NULL; p = p->next) { erts_printf(" (timeout time %bps, slot %d)\n", ERTS_CLKTCKS_TO_MONOTONIC(p->timeout_pos), p->slot); } } - for(i = ((i+1) & (TIW_SIZE-1)); i != (tiw_pos & (TIW_SIZE-1)); i = ((i+1) & (TIW_SIZE-1))) { - if (tiw[i] != NULL) { + for(i = ((i+1) & (TIW_SIZE-1)); i != (tiw->pos & (TIW_SIZE-1)); i = ((i+1) & (TIW_SIZE-1))) { + if (tiw->w[i] != NULL) { erts_printf("%d:\n", i); - for(p = tiw[i]; p != NULL; p = p->next) { + for(p = tiw->w[i]; p != NULL; p = p->next) { erts_printf(" (timeout time %bps, slot %d)\n", ERTS_CLKTCKS_TO_MONOTONIC(p->timeout_pos), p->slot); } } } - erts_smp_mtx_unlock(&tiw_lock); + erts_smp_mtx_unlock(&tiw->lock); } #endif /* DEBUG */ diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 54f1a122c4..da03960b59 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3777,7 +3777,7 @@ erts_create_smp_ptimer(ErtsSmpPTimer **timer_ref, res->timer.timeout_func = timeout_func; res->timer.timer_ref = timer_ref; res->timer.id = id; - res->timer.tm.active = 0; /* MUST be initalized */ + erts_init_timer(&res->timer.tm); ASSERT(!*timer_ref); diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 705f36c34c..7be17d20bb 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -1603,6 +1603,9 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) ErtsMonotonicTime timeout_time; int poll_ret, i; erts_aint_t current_cio_time; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + + ASSERT(esdp); restart: @@ -1613,7 +1616,8 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) /* Figure out timeout value */ timeout_time = (do_wait - ? erts_check_next_timeout_time(ERTS_SEC_TO_MONOTONIC(10*60)) + ? erts_check_next_timeout_time(esdp->timer_wheel, + ERTS_SEC_TO_MONOTONIC(10*60)) : ERTS_POLL_NO_TIMEOUT /* poll only */); /* -- cgit v1.2.3 From d33ac120455f520a74b3c5cdfc32136d25b3e771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 5 Feb 2015 11:36:37 +0100 Subject: otp_SUITE: Warn for calls to erlang:now/0 --- erts/test/otp_SUITE.erl | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 229d10ccee..ebfdf8a830 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -23,7 +23,7 @@ init_per_suite/1,end_per_suite/1]). -export([undefined_functions/1,deprecated_not_in_obsolete/1, obsolete_but_not_deprecated/1,call_to_deprecated/1, - call_to_size_1/1,strong_components/1, + call_to_size_1/1,call_to_now_0/1,strong_components/1, erl_file_encoding/1,xml_file_encoding/1,runtime_dependencies/1]). -include_lib("test_server/include/test_server.hrl"). @@ -35,7 +35,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [undefined_functions, deprecated_not_in_obsolete, obsolete_but_not_deprecated, call_to_deprecated, - call_to_size_1, strong_components, + call_to_size_1, call_to_now_0, strong_components, erl_file_encoding, xml_file_encoding, runtime_dependencies]. @@ -273,48 +273,58 @@ call_to_deprecated(Config) when is_list(Config) -> {comment,integer_to_list(length(DeprecatedCalls))++" calls to deprecated functions"}. call_to_size_1(Config) when is_list(Config) -> - Server = ?config(xref_server, Config), - %% Applications that do not call erlang:size/1: Apps = [asn1,compiler,debugger,kernel,observer,parsetools, runtime_tools,stdlib,tools,webtool], + not_recommended_calls(Config, Apps, {erlang,size,1}). + +call_to_now_0(Config) when is_list(Config) -> + %% Applications that do not call erlang:now/1: + Apps = [asn1,common_test,compiler,debugger,dialyzer, + gs,kernel,mnesia,observer,parsetools,reltool, + runtime_tools,sasl,stdlib,syntax_tools, + test_server,tools,webtool], + not_recommended_calls(Config, Apps, {erlang,now,0}). + +not_recommended_calls(Config, Apps, MFA) -> + Server = ?config(xref_server, Config), - Fs = [{erlang,size,1}], + Fs = [MFA], Q1 = io_lib:format("E || ~p : Fun", [Fs]), - ?line {ok,AllCallsToSize1} = xref:q(Server, lists:flatten(Q1)), + {ok,AllCallsToMFA} = xref:q(Server, lists:flatten(Q1)), Q2 = io_lib:format("E | ~p : App || ~p : Fun", [Apps,Fs]), - ?line {ok,CallsToSize1} = xref:q(Server, lists:flatten(Q2)), + {ok,CallsToMFA} = xref:q(Server, lists:flatten(Q2)), - case CallsToSize1 of + case CallsToMFA of [] -> ok; _ -> - io:format("These calls cause an error:~n"), + io:format("These calls are not allowed:\n"), foreach(fun ({MFA1,MFA2}) -> - io:format("~s calls soon to be deprecated ~s", + io:format("~s calls non-recommended ~s", [format_mfa(MFA1),format_mfa(MFA2)]) - end, CallsToSize1) + end, CallsToMFA) end, - %% Enumerate calls to erlang:size/1 from other applications than - %% the ones known not to call erlang:size/1: - case AllCallsToSize1--CallsToSize1 of + %% Enumerate calls to MFA from other applications than + %% the ones known not to call MFA: + case AllCallsToMFA--CallsToMFA of [] -> ok; Calls -> - io:format("~n~nThese calls do not cause an error (yet):~n"), + io:format("~n~nThese calls are allowed for now:\n"), foreach(fun ({MFA1,MFA2}) -> - io:format("~s calls soon to be deprecated ~s", + io:format("~s calls non-recommended ~s", [format_mfa(MFA1),format_mfa(MFA2)]) end, Calls) end, - case CallsToSize1 of + case CallsToMFA of [] -> ok; _ -> - ?line ?t:fail({length(CallsToSize1),calls_to_size_1}) + ?t:fail({length(CallsToMFA),calls_to_size_1}) end. strong_components(Config) when is_list(Config) -> -- cgit v1.2.3 From 6071cbf00680b94d107906e1c7f1e13f79676479 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 20 Mar 2015 16:36:34 +0100 Subject: erts: Fix hashmap overestimation Old overestimation assumed an average of k/3 nodes. This can be bad for large maps (with tight standard deviations) as the average vary between 0.3*k and up to almost 0.4*k. --- erts/emulator/beam/erl_map.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index bd6da0a6f1..bcbda65da0 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -2549,19 +2549,24 @@ static int int_sqrt_ceiling(Uint x) } } -Uint hashmap_over_estimated_heap_size(Uint n) +Uint hashmap_over_estimated_heap_size(Uint k) { - /* n is nr of key-value pairs. - Average nr of nodes is about n/3. - Standard deviation is about sqrt(n)/3. - Assuming normal probability distribution, - we overestimate nr of nodes by 14 std.devs, which gives a probability - for overrun of 1.0e-49 (same magnitude as a git SHA1 collision). + /* k is nr of key-value pairs. + N(k) is expected nr of nodes in hamt. + + Observation: + For uniformly distributed hash values, average of N varies between + 0.3*k and 0.4*k (with a beautiful sine curve) + and standard deviation of N is about sqrt(k)/3. + + Assuming normal probability distribution, we overestimate nr of nodes + by 15 std.devs above the average, which gives a probability for overrun + less than 1.0e-49 (same magnitude as a git SHA1 collision). */ - Uint nodes = (n + int_sqrt_ceiling(n)*14) / 3; - return (n*2 + /* leaf cons cells */ - n + /* leaf list terms */ - nodes*2); /* headers + parent refs */ + Uint max_nodes = 2*k/5 + (15/3)*int_sqrt_ceiling(k); + return (k*2 + /* leaf cons cells */ + k + /* leaf list terms */ + max_nodes*2); /* headers + parent boxed terms */ } -- cgit v1.2.3 From c1be2e5baed4794f7017dab9558afe8a737b63ee Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 20 Mar 2015 16:37:18 +0100 Subject: erts: Add test map_SUITE:t_hashmap_balance --- erts/emulator/beam/erl_bif_info.c | 20 +++++++++++ erts/emulator/test/map_SUITE.erl | 71 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index d750e34be3..0774ab51af 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3597,6 +3597,26 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) BIF_RET(erts_debug_reader_groups_map(BIF_P, (int) groups)); } + else if (ERTS_IS_ATOM_STR("internal_hash", tp[1])) { + Uint hash = (Uint) make_internal_hash(tp[2]); + Uint hsz = 0; + Eterm* hp; + erts_bld_uint(NULL, &hsz, hash); + hp = HAlloc(BIF_P,hsz); + return erts_bld_uint(&hp, NULL, hash); + } + else if (ERTS_IS_ATOM_STR("atom", tp[1])) { + Uint ix; + if (!term_to_Uint(tp[2], &ix)) + BIF_ERROR(BIF_P, BADARG); + while (ix >= atom_table_size()) { + char tmp[20]; + erts_snprintf(tmp, sizeof(tmp), "am%x", atom_table_size()); + erts_atom_put((byte *) tmp, strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1); + } + return make_atom(ix); + } + break; } default: diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 91a5706320..1da08beb8b 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -62,6 +62,7 @@ t_maps_without/1, %% misc + t_hashmap_balance/1, t_pdict/1, t_ets/1, t_dets/1, @@ -111,6 +112,7 @@ all() -> [ %% Other functions + t_hashmap_balance, t_pdict, t_ets, t_tracing @@ -1421,6 +1423,75 @@ t_maps_without(_Config) -> %% MISC + +%% Verify that the the number of nodes in hashmaps +%% of different types and sizes does not deviate too +%% much from the theoretical model. +t_hashmap_balance(_Config) -> + io:format("Integer keys\n", []), + hashmap_balance(fun(I) -> I end), + io:format("Float keys\n", []), + hashmap_balance(fun(I) -> float(I) end), + io:format("String keys\n", []), + hashmap_balance(fun(I) -> integer_to_list(I) end), + io:format("Binary (big) keys\n", []), + hashmap_balance(fun(I) -> <> end), + io:format("Binary (little) keys\n", []), + hashmap_balance(fun(I) -> <> end), + io:format("Atom keys\n", []), + erts_debug:set_internal_state(available_internal_state, true), + hashmap_balance(fun(I) -> erts_debug:get_internal_state({atom,I}) end), + erts_debug:set_internal_state(available_internal_state, false), + + ok. + +hashmap_balance(KeyFun) -> + F = fun(I, {M0,Max0}) -> + Key = KeyFun(I), + M1 = M0#{Key => Key}, + Max1 = case erts_internal:map_type(M1) of + hashmap -> + Nodes = hashmap_nodes(M1), + Avg = maps:size(M1) * 0.4, + StdDev = math:sqrt(maps:size(M1)) / 3, + SD_diff = abs(Nodes - Avg) / StdDev, + %%io:format("~p keys: ~p nodes avg=~p SD_diff=~p\n", + %% [maps:size(M1), Nodes, Avg, SD_diff]), + {MaxDiff0, _} = Max0, + case {Nodes > Avg, SD_diff > MaxDiff0} of + {true, true} -> {SD_diff, M1}; + _ -> Max0 + end; + + flatmap -> Max0 + end, + {M1, Max1} + end, + + {_,{MaxDiff,MaxMap}} = lists:foldl(F, + {#{}, {0, 0}}, + lists:seq(1,10000)), + io:format("Max std dev diff ~p for map of size ~p (nodes=~p, flatsize=~p)\n", + [MaxDiff, maps:size(MaxMap), hashmap_nodes(MaxMap), erts_debug:flat_size(MaxMap)]), + + true = (MaxDiff < 6), % The probability of this line failing is about 0.000000001 + % for a uniform hash. I've set the probability this "high" for now + % to detect flaws in our make_internal_hash. + % Hard limit is 15 (see hashmap_over_estimated_heap_size). + ok. + +hashmap_nodes(M) -> + Info = erts_debug:map_info(M), + lists:foldl(fun(Tpl,Acc) -> + case element(1,Tpl) of + bitmaps -> Acc + element(2,Tpl); + arrays -> Acc + element(2,Tpl); + _ -> Acc + end + end, + 0, + Info). + t_pdict(_Config) -> put(#{ a => b, b => a},#{ c => d}), -- cgit v1.2.3 From 50713b7d6179477d6018e6a9ca5610617e3de1fe Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Sat, 21 Mar 2015 11:59:34 +0100 Subject: Unbreak lcnt --- erts/emulator/beam/erl_init.c | 1 - erts/emulator/beam/erl_time.h | 1 - erts/emulator/beam/erl_time_sup.c | 3 +-- erts/emulator/beam/sys.h | 1 + erts/emulator/sys/unix/sys.c | 17 ++++++++++++++--- erts/emulator/sys/win32/sys.c | 26 ++++++++++++++++---------- 6 files changed, 32 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 6708324a29..8d38ff9c2c 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -1170,7 +1170,6 @@ early_init(int *argc, char **argv) /* /* Creates threads on Windows that depend on the arguments, so has to be after erl_sys_args */ erl_sys_init(); - erts_early_init_time_sup(); erts_ets_realloc_always_moves = 0; erts_ets_always_compress = 0; diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 7933f4973d..924b0b6091 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -77,7 +77,6 @@ void erts_cancel_smp_ptimer(ErtsSmpPTimer *ptimer); void erts_monitor_time_offset(Eterm id, Eterm ref); int erts_demonitor_time_offset(Eterm ref); -void erts_early_init_time_sup(void); void erts_late_init_time_sup(void); /* timer-wheel api */ diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index d47d1682d7..b809fa8316 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -765,8 +765,7 @@ erts_has_time_correction(void) return time_sup.r.o.correction; } -void -erts_early_init_time_sup(void) +void erts_init_sys_time_sup(void) { ErtsSysInitTimeResult sys_init_time_res = ERTS_SYS_INIT_TIME_RESULT_INITER; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 7e64623686..a108e819c6 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -698,6 +698,7 @@ typedef struct { #define ERTS_SYS_INIT_TIME_RESULT_INITER \ {0, (ErtsMonotonicTime) -1, (ErtsMonotonicTime) 1} +extern void erts_init_sys_time_sup(void); extern void sys_init_time(ErtsSysInitTimeResult *); extern void erts_deliver_time(void); extern void erts_time_remaining(SysTimeval *); diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 70f549a37a..58b01c094e 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -504,11 +504,14 @@ thr_create_prepare_child(void *vtcdp) void erts_sys_pre_init(void) { +#ifdef USE_THREADS + erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER; +#endif + erts_printf_add_cr_to_stdout = 1; erts_printf_add_cr_to_stderr = 1; + #ifdef USE_THREADS - { - erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER; eid.thread_create_child_func = thr_create_prepare_child; /* Before creation in parent */ @@ -525,6 +528,12 @@ erts_sys_pre_init(void) erts_thr_init(&eid); +#endif /* USE_THREADS */ + + erts_init_sys_time_sup(); + +#ifdef USE_THREADS + report_exit_list = NULL; #ifdef ERTS_ENABLE_LOCK_COUNT @@ -541,7 +550,7 @@ erts_sys_pre_init(void) erts_cnd_init(&chld_stat_cnd); children_alive = 0; #endif - } + #ifdef ERTS_SMP erts_smp_atomic32_init_nob(&erts_break_requested, 0); erts_smp_atomic32_init_nob(&erts_got_sigusr1, 0); @@ -554,7 +563,9 @@ erts_sys_pre_init(void) #if !CHLDWTHR && !defined(ERTS_SMP) children_died = 0; #endif + #endif /* USE_THREADS */ + erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0); { diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 8eed1c6d9b..ad636b7249 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -3157,25 +3157,31 @@ thr_create_prepare_child(void *vtcdp) void erts_sys_pre_init(void) { +#ifdef USE_THREADS + erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER; +#endif int_os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&int_os_version); check_supported_os_version(); + #ifdef USE_THREADS - { - erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER; + eid.thread_create_child_func = thr_create_prepare_child; + /* Before creation in parent */ + eid.thread_create_prepare_func = thr_create_prepare; + /* After creation in parent */ + eid.thread_create_parent_func = thr_create_cleanup; - eid.thread_create_child_func = thr_create_prepare_child; - /* Before creation in parent */ - eid.thread_create_prepare_func = thr_create_prepare; - /* After creation in parent */ - eid.thread_create_parent_func = thr_create_cleanup, + erts_thr_init(&eid); +#endif - erts_thr_init(&eid); + erts_init_sys_time_sup(); + +#ifdef USE_THREADS #ifdef ERTS_ENABLE_LOCK_COUNT - erts_lcnt_init(); + erts_lcnt_init(); #endif - } #endif + erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0); } -- cgit v1.2.3 From 137d68ce8d9f68c2f80f246e7b5f65e38e699323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erland=20Sch=C3=B6nbeck?= Date: Mon, 23 Mar 2015 13:26:39 +0100 Subject: =?UTF-8?q?Update=20with=20changes=20in=20new=20time=20api=20otp?= =?UTF-8?q?=5FSUITE:=20change=20filter=20f=C3=B6r=20diameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- erts/test/otp_SUITE.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 171f722357..2969383d66 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -220,12 +220,10 @@ gs_filter(Undef) -> diameter_filter(Undef) -> %% Filter away function calls that are catched. - filter(fun({{diameter_lib,_,_},{erlang,convert_time_resolution,3}}) -> + filter(fun({{diameter_lib,_,_},{erlang,convert_time_unit,3}}) -> false; ({{diameter_lib,_,_},{erlang,monotonic_time,0}}) -> false; - ({{diameter_lib,_,_},{erlang,time_resolution,0}}) -> - false; ({{diameter_lib,_,_},{erlang,unique_integer,0}}) -> false; ({{diameter_lib,_,_},{erlang,time_offset,0}}) -> -- cgit v1.2.3 From d07319f790eb38c867bd04a23de674e2393825ff Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Sun, 22 Mar 2015 20:20:28 +0100 Subject: Better support for poor os monotonic sources --- erts/aclocal.m4 | 4 +- erts/doc/src/erlang.xml | 9 + erts/emulator/Makefile.in | 3 +- erts/emulator/beam/erl_time.h | 17 +- erts/emulator/beam/erl_time_sup.c | 237 +++++++++++++++------ erts/emulator/beam/sys.h | 2 + .../sys/common/erl_os_monotonic_time_extender.c | 88 ++++++++ .../sys/common/erl_os_monotonic_time_extender.h | 65 ++++++ erts/emulator/sys/unix/erl_unix_sys.h | 7 +- erts/emulator/sys/unix/sys_time.c | 200 ++++++----------- erts/emulator/sys/win32/sys_time.c | 58 +++-- 11 files changed, 441 insertions(+), 249 deletions(-) create mode 100644 erts/emulator/sys/common/erl_os_monotonic_time_extender.c create mode 100644 erts/emulator/sys/common/erl_os_monotonic_time_extender.h (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 1e449bf874..6ba54e823e 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -745,9 +745,7 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, done ]) - AC_CHECK_FUNC(clock_getres) - - AC_CHECK_FUNC(gethrtime) + AC_CHECK_FUNCS([clock_getres gethrtime]) AC_CACHE_CHECK([for mach clock_get_time()], erl_cv_mach_clock_get_time, [ diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 3cbfd372ce..98c0ef5f81 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6449,6 +6449,15 @@ ok precision won't be higher than OsMonotonicTimeResolution.

+ {extended, Extended} +

Extended equals yes if + the range of time values has been extended; + otherwise, Extended equals no. The + range needs to be extended if Function + returns values that wrap fast. This typically + is the case when the return value is a 32-bit + value.

+ {parallel, Parallel}

Parallel equals yes if Function is called in parallel from multiple diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 560cab396d..658f1b861a 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -898,7 +898,8 @@ OS_OBJS += $(OBJDIR)/erl_mseg.o \ $(OBJDIR)/erl_mmap.o \ $(OBJDIR)/erl_$(ERLANG_OSTYPE)_sys_ddll.o \ $(OBJDIR)/erl_mtrace_sys_wrap.o \ - $(OBJDIR)/erl_sys_common_misc.o + $(OBJDIR)/erl_sys_common_misc.o \ + $(OBJDIR)/erl_os_monotonic_time_extender.o HIPE_ARCH64_OBJS=$(OBJDIR)/hipe_bif64.o diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 924b0b6091..35a1442dbd 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -219,6 +219,10 @@ erts_time_unit_conversion(Uint64 value, * it is assumed (and need) to be a power of 10. */ +#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT < 1000*1000 +# error Compile time time unit needs to be at least 1000000 +#endif + #define ERTS_MONOTONIC_TIME_UNIT \ ((ErtsMonotonicTime) ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT) @@ -248,19 +252,6 @@ erts_time_unit_conversion(Uint64 value, #define ERTS_USEC_TO_MONOTONIC__(USEC) ((ErtsMonotonicTime) (USEC)) #define ERTS_NSEC_TO_MONOTONIC__(NSEC) (((ErtsMonotonicTime) (NSEC))/1000) -#elif ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000 -/* Milli-second time unit */ - -#define ERTS_MONOTONIC_TO_SEC__(MSEC) ((USEC)/(1000)) -#define ERTS_MONOTONIC_TO_MSEC__(MSEC) (MSEC) -#define ERTS_MONOTONIC_TO_USEC__(MSEC) ((MSEC)*1000) -#define ERTS_MONOTONIC_TO_NSEC__(MSEC) ((MSEC)*(1000*1000)) - -#define ERTS_SEC_TO_MONOTONIC__(SEC) (((ErtsMonotonicTime) (SEC))*1000) -#define ERTS_MSEC_TO_MONOTONIC__(MSEC) ((ErtsMonotonicTime) (MSEC)) -#define ERTS_USEC_TO_MONOTONIC__(USEC) (((ErtsMonotonicTime) (USEC))/1000) -#define ERTS_NSEC_TO_MONOTONIC__(NSEC) (((ErtsMonotonicTime) (NSEC))/(1000*1000)) - #else #error Missing implementation for monotonic time unit #endif diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index b809fa8316..d961fae81a 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -21,6 +21,8 @@ * Support routines for the time */ +/* #define ERTS_TIME_CORRECTION_PRINT */ + #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -75,12 +77,12 @@ schedule_send_time_offset_changed_notifications(ErtsMonotonicTime new_offset); #else /* ARCH_64 */ -#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT <= 1000*1000 +#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT <= 10*1000*1000 /* * Using micro second time unit or lower. Start at zero since * time will remain an immediate for a very long time anyway - * (18279 years in the micro second case)... + * (1827 years in the 10 micro second case)... */ #define ERTS_MONOTONIC_TIME_START ((ErtsMonotonicTime) 0) @@ -99,11 +101,16 @@ schedule_send_time_offset_changed_notifications(ErtsMonotonicTime new_offset); #endif /* ARCH_64 */ -#define ERTS_MONOTONIC_OFFSET_NATIVE ERTS_MONOTONIC_TIME_START -#define ERTS_MONOTONIC_OFFSET_NSEC ERTS_MONOTONIC_TO_NSEC__(ERTS_MONOTONIC_TIME_START) -#define ERTS_MONOTONIC_OFFSET_USEC ERTS_MONOTONIC_TO_USEC__(ERTS_MONOTONIC_TIME_START) -#define ERTS_MONOTONIC_OFFSET_MSEC ERTS_MONOTONIC_TO_MSEC__(ERTS_MONOTONIC_TIME_START) -#define ERTS_MONOTONIC_OFFSET_SEC ERTS_MONOTONIC_TO_SEC__(ERTS_MONOTONIC_TIME_START) +#define ERTS_MONOTONIC_OFFSET_NATIVE \ + (ERTS_MONOTONIC_TIME_START - ERTS_MONOTONIC_TIME_UNIT) +#define ERTS_MONOTONIC_OFFSET_NSEC \ + ERTS_MONOTONIC_TO_NSEC__(ERTS_MONOTONIC_OFFSET_NATIVE) +#define ERTS_MONOTONIC_OFFSET_USEC \ + ERTS_MONOTONIC_TO_USEC__(ERTS_MONOTONIC_OFFSET_NATIVE) +#define ERTS_MONOTONIC_OFFSET_MSEC \ + ERTS_MONOTONIC_TO_MSEC__(ERTS_MONOTONIC_OFFSET_NATIVE) +#define ERTS_MONOTONIC_OFFSET_SEC \ + ERTS_MONOTONIC_TO_SEC__(ERTS_MONOTONIC_OFFSET_NATIVE) #else /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */ @@ -120,25 +127,6 @@ schedule_send_time_offset_changed_notifications(ErtsMonotonicTime new_offset); #endif /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */ -#define ERTS_MONOTONIC_TO_SYS_TIME_VAL(TVP, MT) \ - do { \ - ErtsMonotonicTime sec__, usec__; \ - sec__ = ERTS_MONOTONIC_TO_SEC((MT)); \ - usec__ = ERTS_MONOTONIC_TO_USEC((MT)) - sec__*1000000; \ - ASSERT(usec__ < 1000000); \ - (TVP)->tv_sec = sec__; \ - (TVP)->tv_usec = usec__; \ - } while (0) - -#define ERTS_MAX_SYSTEM_TIME_DIFF ERTS_MSEC_TO_MONOTONIC(10) -#define ERTS_SYSTEM_TIME_DIFF_EXCEED_LIMIT(ESYSTIME, OSSYSTIME) \ - (((Uint64) (ESYSTIME)) - (((Uint64) (OSSYSTIME)) \ - - ERTS_MAX_SYSTEM_TIME_DIFF) \ - > 2*ERTS_MAX_SYSTEM_TIME_DIFF) - -#define ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF (ERTS_MAX_SYSTEM_TIME_DIFF/2) -#define ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(500) - struct time_sup_read_only__ { ErtsMonotonicTime (*get_time)(void); int correction; @@ -150,6 +138,7 @@ struct time_sup_read_only__ { char *os_monotonic_clock_id; int os_monotonic_locked; Uint64 os_monotonic_resolution; + Uint64 os_monotonic_extended; #endif #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT ErtsMonotonicTime start; @@ -161,6 +150,10 @@ struct time_sup_read_only__ { ErtsMonotonicTime sec; } start_offset; #endif + struct { + ErtsMonotonicTime large_diff; + ErtsMonotonicTime small_diff; + } adj; }; typedef struct { @@ -338,13 +331,77 @@ static ErtsMonotonicTime get_corrected_time(void) return calc_corrected_erl_mtime(os_mtime, cip, NULL); } +#ifdef ERTS_TIME_CORRECTION_PRINT + +static ERTS_INLINE void +print_correction(int change, + ErtsMonotonicTime sdiff, + ErtsMonotonicTime old_ecorr, + ErtsMonotonicTime old_dcorr, + ErtsMonotonicTime new_ecorr, + ErtsMonotonicTime new_dcorr, + Uint tmo) +{ + ErtsMonotonicTime usec_sdiff; + if (sdiff < 0) + usec_sdiff = -1*ERTS_MONOTONIC_TO_USEC(-1*sdiff); + else + usec_sdiff = ERTS_MONOTONIC_TO_USEC(sdiff); + + if (!change) + fprintf(stderr, + "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppm] : " + "tmo = %lld msec\r\n", + (long long) usec_sdiff, + (long long) (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, + (long long) (1000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + (long long) tmo); + else + fprintf(stderr, + "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppm] " + "-> [ec=%lld ppm, dc=%lld ppm] : tmo = %lld msec\r\n", + (long long) usec_sdiff, + (long long) (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, + (long long) (1000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + (long long) (1000000*new_ecorr) / ERTS_TCORR_ERR_UNIT, + (long long) (1000000*new_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + (long long) tmo); + +} + +#endif + static void check_time_correction(void *unused) { +#ifndef ERTS_TIME_CORRECTION_PRINT +# define ERTS_PRINT_CORRECTION +#else +# ifdef ERTS_HAVE_CORRECTED_OS_MONOTONIC +# define ERTS_PRINT_CORRECTION \ + print_correction(set_new_correction, \ + sdiff, \ + cip->correction.error, \ + 0, \ + new_correction.error, \ + 0, \ + timeout) +# else +# define ERTS_PRINT_CORRECTION \ + print_correction(set_new_correction, \ + sdiff, \ + cip->correction.error, \ + cip->correction.drift, \ + new_correction.error, \ + new_correction.drift, \ + timeout) +# endif +#endif ErtsMonotonicCorrectionData cdata; ErtsMonotonicCorrection new_correction; ErtsMonotonicCorrectionInstance *cip; - ErtsMonotonicTime mdiff, sdiff, os_mtime, erl_mtime, os_stime, erl_stime, time_offset; + ErtsMonotonicTime mdiff, sdiff, os_mtime, erl_mtime, os_stime, + erl_stime, time_offset; Uint timeout; SysTimeval tod; int set_new_correction, begin_short_intervals = 0; @@ -377,8 +434,8 @@ check_time_correction(void *unused) new_correction = cip->correction; if (time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE - && (sdiff < -2*ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF - || 2*ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF < sdiff)) { + && (sdiff < -2*time_sup.r.o.adj.small_diff + || 2*time_sup.r.o.adj.small_diff < sdiff)) { /* System time diff exeeded limits; change time offset... */ time_offset -= sdiff; sdiff = 0; @@ -393,16 +450,16 @@ check_time_correction(void *unused) } } else if (cdata.curr.correction.error == 0) { - if (sdiff < -ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + if (sdiff < -time_sup.r.o.adj.small_diff) { set_new_correction = 1; - if (sdiff < -ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + if (sdiff < -time_sup.r.o.adj.large_diff) new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; else new_correction.error = ERTS_TCORR_ERR_SMALL_ADJ; } - else if (sdiff > ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + else if (sdiff > time_sup.r.o.adj.small_diff) { set_new_correction = 1; - if (sdiff > ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + if (sdiff > time_sup.r.o.adj.large_diff) new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; else new_correction.error = -ERTS_TCORR_ERR_SMALL_ADJ; @@ -414,16 +471,16 @@ check_time_correction(void *unused) else if (cdata.curr.correction.error > 0) { if (sdiff < 0) { if (cdata.curr.correction.error == ERTS_TCORR_ERR_LARGE_ADJ - || -ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF <= sdiff) + || -time_sup.r.o.adj.large_diff <= sdiff) set_new_correction = 0; else { new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; set_new_correction = 1; } } - else if (sdiff > ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + else if (sdiff > time_sup.r.o.adj.small_diff) { set_new_correction = 1; - if (sdiff > ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + if (sdiff > time_sup.r.o.adj.large_diff) new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; else new_correction.error = -ERTS_TCORR_ERR_SMALL_ADJ; @@ -436,7 +493,7 @@ check_time_correction(void *unused) else /* if (cdata.curr.correction.error < 0) */ { if (0 < sdiff) { if (cdata.curr.correction.error == -ERTS_TCORR_ERR_LARGE_ADJ - || sdiff <= ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + || sdiff <= time_sup.r.o.adj.large_diff) set_new_correction = 0; else { new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; @@ -444,9 +501,9 @@ check_time_correction(void *unused) } set_new_correction = 0; } - else if (sdiff < -ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + else if (sdiff < -time_sup.r.o.adj.small_diff) { set_new_correction = 1; - if (sdiff < -ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + if (sdiff < -time_sup.r.o.adj.large_diff) new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; else new_correction.error = ERTS_TCORR_ERR_SMALL_ADJ; @@ -475,11 +532,13 @@ check_time_correction(void *unused) mtime_acc = ddp->acc.mon; stime_acc = ddp->acc.sys; - avg_drift_adj = ((stime_acc - mtime_acc)*ERTS_MONOTONIC_TIME_UNIT) / mtime_acc; + avg_drift_adj = (((stime_acc - mtime_acc)*ERTS_MONOTONIC_TIME_UNIT) + / mtime_acc); mtime_diff = os_mtime - old_os_mtime; stime_diff = os_stime - old_os_stime; - drift_adj = ((stime_diff - mtime_diff)*ERTS_MONOTONIC_TIME_UNIT) / mtime_diff; + drift_adj = (((stime_diff - mtime_diff)*ERTS_MONOTONIC_TIME_UNIT) + / mtime_diff); ix++; if (ix >= ERTS_DRIFT_INTERVALS) @@ -499,10 +558,11 @@ check_time_correction(void *unused) ddp->acc.sys = stime_acc; /* - * If calculated drift adjustment is if off by more than 20% from the - * average drift we interpret this as a discontinous leap in system - * time and ignore it. If it actually is a change in drift we will - * later detect this when the average drift change. + * If calculated drift adjustment is if off by more than 20% + * from the average drift we interpret this as a discontinous + * leap in system time and ignore it. If it actually is a + * change in drift we will later detect this when the average + * drift change. */ drift_adj_diff = avg_drift_adj - drift_adj; if (drift_adj_diff < -ERTS_TIME_DRIFT_MAX_ADJ_DIFF @@ -512,7 +572,8 @@ check_time_correction(void *unused) } else { if (ddp->dirty_counter <= 0) { - drift_adj = ((stime_acc - mtime_acc)*ERTS_MONOTONIC_TIME_UNIT) / mtime_acc; + drift_adj = ((stime_acc - mtime_acc) + *ERTS_MONOTONIC_TIME_UNIT) / mtime_acc; } if (ddp->dirty_counter >= 0) { if (ddp->dirty_counter == 0) { @@ -573,7 +634,9 @@ check_time_correction(void *unused) && time_sup.inf.c.parmon.cdata.short_check_interval) { timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK); } - + + ERTS_PRINT_CORRECTION; + if (set_new_correction) { erts_smp_rwmtx_rwlock(&time_sup.inf.c.parmon.rwmtx); @@ -610,6 +673,8 @@ check_time_correction(void *unused) NULL, NULL, timeout); + +#undef ERTS_PRINT_CORRECTION } #ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC @@ -618,7 +683,8 @@ static void init_check_time_correction(void *unused) { ErtsMonotonicDriftData *ddp; - ErtsMonotonicTime old_mtime, old_stime, mtime, stime, mtime_diff, stime_diff; + ErtsMonotonicTime old_mtime, old_stime, mtime, stime, mtime_diff, + stime_diff; int ix; SysTimeval tod; @@ -690,6 +756,7 @@ static void late_init_time_correction(void) { if (time_sup.inf.c.finalized_offset) { + erts_init_timer(&time_sup.inf.c.parmon.timer); erts_set_timer(&time_sup.inf.c.parmon.timer, #ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC @@ -792,6 +859,8 @@ void erts_init_sys_time_sup(void) = sys_init_time_res.os_monotonic_info.locked_use; time_sup.r.o.os_monotonic_resolution = sys_init_time_res.os_monotonic_info.resolution; + time_sup.r.o.os_monotonic_extended + = sys_init_time_res.os_monotonic_info.extended; #endif } @@ -799,7 +868,7 @@ int erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) { #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT - ErtsMonotonicTime abs_start; + ErtsMonotonicTime abs_native_offset, native_offset; #endif ASSERT(ERTS_MONOTONIC_TIME_MIN < ERTS_MONOTONIC_TIME_MAX); @@ -822,42 +891,68 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) time_sup.r.o.start /= ERTS_MONOTONIC_TIME_UNIT; time_sup.r.o.start *= ERTS_MONOTONIC_TIME_UNIT; time_sup.r.o.start += ERTS_MONOTONIC_TIME_UNIT; - abs_start = time_sup.r.o.start; + native_offset = time_sup.r.o.start - ERTS_MONOTONIC_TIME_UNIT; + native_offset = native_offset; #else /* ARCH_64 */ - if (ERTS_MONOTONIC_TIME_UNIT <= 1000*1000) - abs_start = time_sup.r.o.start = 0; + if (ERTS_MONOTONIC_TIME_UNIT <= 10*1000*1000) { + time_sup.r.o.start = 0; + native_offset = -ERTS_MONOTONIC_TIME_UNIT; + abs_native_offset = ERTS_MONOTONIC_TIME_UNIT; + } else { time_sup.r.o.start = ((ErtsMonotonicTime) MIN_SMALL); time_sup.r.o.start /= ERTS_MONOTONIC_TIME_UNIT; time_sup.r.o.start *= ERTS_MONOTONIC_TIME_UNIT; - abs_start = -1*time_sup.r.o.start; + native_offset = time_sup.r.o.start - ERTS_MONOTONIC_TIME_UNIT; + abs_native_offset = -1*native_offset; } #endif - time_sup.r.o.start_offset.native = time_sup.r.o.start; + time_sup.r.o.start_offset.native = (time_sup.r.o.start + - ERTS_MONOTONIC_TIME_UNIT); time_sup.r.o.start_offset.nsec = (ErtsMonotonicTime) - erts_time_unit_conversion((Uint64) abs_start, + erts_time_unit_conversion((Uint64) abs_native_offset, (Uint32) ERTS_MONOTONIC_TIME_UNIT, (Uint32) 1000*1000*1000); time_sup.r.o.start_offset.usec = (ErtsMonotonicTime) - erts_time_unit_conversion((Uint64) abs_start, + erts_time_unit_conversion((Uint64) abs_native_offset, (Uint32) ERTS_MONOTONIC_TIME_UNIT, (Uint32) 1000*1000); time_sup.r.o.start_offset.msec = (ErtsMonotonicTime) - erts_time_unit_conversion((Uint64) abs_start, + erts_time_unit_conversion((Uint64) abs_native_offset, (Uint32) ERTS_MONOTONIC_TIME_UNIT, (Uint32) 1000); time_sup.r.o.start_offset.sec = (ErtsMonotonicTime) - erts_time_unit_conversion((Uint64) abs_start, + erts_time_unit_conversion((Uint64) abs_native_offset, (Uint32) ERTS_MONOTONIC_TIME_UNIT, (Uint32) 1); - if (time_sup.r.o.start < 0) { + if (native_offset < 0) { time_sup.r.o.start_offset.nsec *= -1; time_sup.r.o.start_offset.usec *= -1; time_sup.r.o.start_offset.msec *= -1; time_sup.r.o.start_offset.sec *= -1; } +#endif + + time_sup.r.o.adj.large_diff = erts_time_sup__.r.o.monotonic_time_unit; + time_sup.r.o.adj.large_diff *= 50; + time_sup.r.o.adj.large_diff /= time_sup.r.o.os_monotonic_resolution; + if (time_sup.r.o.adj.large_diff < ERTS_MSEC_TO_MONOTONIC(5)) + time_sup.r.o.adj.large_diff = ERTS_MSEC_TO_MONOTONIC(5); + time_sup.r.o.adj.small_diff = time_sup.r.o.adj.large_diff/10; + +#ifdef ERTS_TIME_CORRECTION_PRINT + fprintf(stderr, "start = %lld\n\r", (long long) ERTS_MONOTONIC_TIME_START); + fprintf(stderr, "native offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_NATIVE); + fprintf(stderr, "nsec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_NSEC); + fprintf(stderr, "usec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_USEC); + fprintf(stderr, "msec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_MSEC); + fprintf(stderr, "sec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_SEC); + fprintf(stderr, "large diff = %lld usec\r\n", + (long long) ERTS_MONOTONIC_TO_USEC(time_sup.r.o.adj.large_diff)); + fprintf(stderr, "small diff = %lld usec\r\n", + (long long) ERTS_MONOTONIC_TO_USEC(time_sup.r.o.adj.small_diff)); #endif if (ERTS_MONOTONIC_TIME_UNIT < ERTS_CLKTCK_RESOLUTION) @@ -878,6 +973,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) time_sup.r.o.moffset = -1*time_sup.inf.c.minit; offset = ERTS_SEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_sec); offset += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); + offset -= ERTS_MONOTONIC_TIME_UNIT; init_time_offset(offset); rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ; @@ -897,7 +993,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) cdatap->curr.correction.drift = 0; #endif cdatap->curr.correction.error = 0; - cdatap->curr.erl_mtime = 0; + cdatap->curr.erl_mtime = ERTS_MONOTONIC_TIME_UNIT; cdatap->curr.os_mtime = time_sup.inf.c.minit; cdatap->last_check = time_sup.inf.c.minit; cdatap->short_check_interval = ERTS_INIT_SHORT_INTERVAL_COUNTER; @@ -910,9 +1006,10 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) { ErtsMonotonicTime stime, offset; time_sup.r.o.get_time = get_not_corrected_time; + sys_gettimeofday(&time_sup.inf.c.inittv); stime = ERTS_SEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_sec); - stime += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); - offset = stime; + stime += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); + offset = stime - ERTS_MONOTONIC_TIME_UNIT; time_sup.inf.c.not_corrected_moffset = offset; init_time_offset(offset); time_sup.f.c.last_not_corrected_time = 0; @@ -937,6 +1034,7 @@ erts_late_init_time_sup(void) if (time_sup.r.o.get_time == get_corrected_time) late_init_time_correction(); #endif + erts_late_sys_init_time(); } ErtsTimeWarpMode erts_time_warp_mode(void) @@ -1737,7 +1835,7 @@ make_time_val(Process *c_p, ErtsMonotonicTime time_val) Eterm erts_get_monotonic_start_time(struct process *c_p) { - return make_time_val(c_p, ERTS_MONOTONIC_OFFSET_NATIVE); + return make_time_val(c_p, ERTS_MONOTONIC_TIME_START); } static Eterm @@ -1747,8 +1845,8 @@ bld_monotonic_time_source(Uint **hpp, Uint *szp, Sint64 os_mtime) return NIL; #else int i = 0; - Eterm k[5]; - Eterm v[5]; + Eterm k[6]; + Eterm v[6]; if (time_sup.r.o.os_monotonic_disable) return NIL; @@ -1761,10 +1859,11 @@ bld_monotonic_time_source(Uint **hpp, Uint *szp, Sint64 os_mtime) v[i++] = erts_bld_atom(hpp, szp, time_sup.r.o.os_monotonic_clock_id); } - if (time_sup.r.o.os_monotonic_resolution) { - k[i] = erts_bld_atom(hpp, szp, "resolution"); - v[i++] = erts_bld_uint64(hpp, szp, time_sup.r.o.os_monotonic_resolution); - } + k[i] = erts_bld_atom(hpp, szp, "resolution"); + v[i++] = erts_bld_uint64(hpp, szp, time_sup.r.o.os_monotonic_resolution); + + k[i] = erts_bld_atom(hpp, szp, "extended"); + v[i++] = time_sup.r.o.os_monotonic_extended ? am_yes : am_no; k[i] = erts_bld_atom(hpp, szp, "parallel"); v[i++] = time_sup.r.o.os_monotonic_locked ? am_no : am_yes; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index a108e819c6..d83bdf8d31 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -692,6 +692,7 @@ typedef struct { char *func; char *clock_id; int locked_use; + int extended; } os_monotonic_info; } ErtsSysInitTimeResult; @@ -700,6 +701,7 @@ typedef struct { extern void erts_init_sys_time_sup(void); extern void sys_init_time(ErtsSysInitTimeResult *); +extern void erts_late_sys_init_time(void); extern void erts_deliver_time(void); extern void erts_time_remaining(SysTimeval *); extern int erts_init_time_sup(int, ErtsTimeWarpMode); diff --git a/erts/emulator/sys/common/erl_os_monotonic_time_extender.c b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c new file mode 100644 index 0000000000..f3633b7267 --- /dev/null +++ b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c @@ -0,0 +1,88 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2015. All Rights Reserved. + * + * The 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 "erl_os_monotonic_time_extender.h" + +#ifdef USE_THREADS + +static void *os_monotonic_time_extender(void *vstatep) +{ + ErtsOsMonotonicTimeExtendState *state = (ErtsOsMonotonicTimeExtendState *) vstatep; + long sleep_time = state->check_interval*1000; + Uint32 (*raw_os_mtime)(void) = state->raw_os_monotonic_time; + Uint32 last_msb = 0; + + while (1) { + Uint32 msb = (*raw_os_mtime)() & (((Uint32) 1) << 31); + + if (msb != last_msb) { + int ix = ((int) (last_msb >> 31)) & 1; + Uint32 xtnd = (Uint32) erts_atomic32_read_nob(&state->extend[ix]); + erts_atomic32_set_nob(&state->extend[ix], (erts_aint32_t) (xtnd + 1)); + last_msb = msb; + } + erts_milli_sleep(sleep_time); + } + + erl_exit(ERTS_ABORT_EXIT, "os_monotonic_time_extender thread terminating"); + return NULL; +} + +static erts_tid_t os_monotonic_extender_tid; +#endif + +void +erts_init_os_monotonic_time_extender(ErtsOsMonotonicTimeExtendState *statep, + Uint32 (*raw_os_monotonic_time)(void), + int check_seconds) +{ +#ifdef USE_THREADS + statep->raw_os_monotonic_time = raw_os_monotonic_time; + erts_atomic32_init_nob(&statep->extend[0], (erts_aint32_t) 0); + erts_atomic32_init_nob(&statep->extend[1], (erts_aint32_t) 0); + statep->check_interval = check_seconds; + +#else + statep->extend[0] = (Uint32) 0; + statep->extend[1] = (Uint32) 0; + statep->last_msb = (ErtsMonotonicTime) 0; +#endif +} + +void +erts_late_init_os_monotonic_time_extender(ErtsOsMonotonicTimeExtendState *statep) +{ +#ifdef USE_THREADS + erts_thr_opts_t thr_opts = ERTS_THR_OPTS_DEFAULT_INITER; + thr_opts.detached = 1; + thr_opts.suggested_stack_size = 4; + +#if 0 + thr_opts.name = "os_monotonic_time_extender"; +#endif + + erts_thr_create(&os_monotonic_extender_tid, + os_monotonic_time_extender, + (void*) statep, + &thr_opts); +#endif +} diff --git a/erts/emulator/sys/common/erl_os_monotonic_time_extender.h b/erts/emulator/sys/common/erl_os_monotonic_time_extender.h new file mode 100644 index 0000000000..0f9e7c86ae --- /dev/null +++ b/erts/emulator/sys/common/erl_os_monotonic_time_extender.h @@ -0,0 +1,65 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2015. All Rights Reserved. + * + * The 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_OS_MONOTONIC_TIME_EXTENDER_H__ +#define ERL_OS_MONOTONIC_TIME_EXTENDER_H__ + +#include "sys.h" +#include "erl_threads.h" + +typedef struct { +#ifdef USE_THREADS + Uint32 (*raw_os_monotonic_time)(void); + erts_atomic32_t extend[2]; + int check_interval; +#else + Uint32 extend[2]; + ErtsMonotonicTime last_msb; +#endif +} ErtsOsMonotonicTimeExtendState; + +#ifdef USE_THREADS +# define ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(S, RT) ((void) 1) +# define ERTS_EXTEND_OS_MONOTONIC_TIME(S, RT) \ + ((((ErtsMonotonicTime) \ + erts_atomic32_read_nob(&((S)->extend[((int) ((RT) >> 31)) & 1]))) \ + << 32) \ + + (RT)) +#else +# define ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(S, RT) \ + do { \ + Uint32 msb__ = (RT) & (((Uint32) 1) << 31); \ + if (msb__ != (S)->last_msb) { \ + int ix__ = ((int) ((S)->last_msb >> 31)) & 1; \ + (S)->extend[ix__]++; \ + (S)->last_msb = msb; \ + } \ + } while (0) +# define ERTS_EXTEND_OS_MONOTONIC_TIME(S, RT) \ + ((((ErtsMonotonicTime) (S)->extend[((int) ((RT) >> 31)) & 1]) << 32) + (RT)) +#endif + +void +erts_init_os_monotonic_time_extender(ErtsOsMonotonicTimeExtendState *statep, + Uint32 (*raw_os_monotonic_time)(void), + int check_seconds); +void +erts_late_init_os_monotonic_time_extender(ErtsOsMonotonicTimeExtendState *statep); + +#endif diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 5417bb2687..303ebeee7c 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -206,12 +206,9 @@ ErtsMonotonicTime erts_os_monotonic_time(void); || defined(OS_MONOTONIC_TIME_USING_TIMES) #if defined(OS_MONOTONIC_TIME_USING_TIMES) +/* Time unit determined at runtime... */ # undef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT -# define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000) -# define ERTS_HAVE_ERTS_OS_TIME_OFFSET_FINALIZE 1 -void erts_os_time_offset_finalize(void); -# define ERTS_HAVE_ERTS_OS_MONOTONIC_TIME_INIT -void erts_os_monotonic_time_init(void); +# define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT 0 #endif ErtsMonotonicTime erts_os_monotonic_time(void); diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index 9fdb1930b7..9db727b111 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -33,6 +33,7 @@ #include "sys.h" #include "global.h" +#include "erl_os_monotonic_time_extender.h" #ifdef NO_SYSCONF # define TICKS_PER_SEC() HZ @@ -55,36 +56,19 @@ #undef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ #undef ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ +#undef ERTS_SYS_TIME_INTERNAL_STATE_READ_MOSTLY__ #if defined(OS_MONOTONIC_TIME_USING_TIMES) -#define ERTS_WRAP_SYS_TIMES 1 -#define ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ -#define ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ - -/* - * Not sure there is a need to use times() anymore, perhaps drop - * support for this soon... - * - * sys_times() might need to be wrapped and the values shifted (right) - * a bit to cope with faster ticks, this has to be taken care - * of dynamically to start with, a special version that uses - * the times() return value as a high resolution timer can be made - * to fully utilize the faster ticks, like on windows, but for now, we'll - * settle with this silly workaround - */ -#ifdef ERTS_WRAP_SYS_TIMES -static clock_t sys_times_wrap(void); -#define KERNEL_TICKS() (sys_times_wrap() & \ - ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1)) -#define ERTS_KERNEL_TICK_TO_USEC(TCKS) (((TCKS)*(1000*1000)) \ - / internal_state.r.o.ticks_per_sec_wrap) -#else +static Uint32 +get_tick_count(void) +{ + struct tms unused; + return (Uint32) times(&unused); +} -#define KERNEL_TICKS() (sys_times(&internal_state.w.f.dummy_tms) & \ - ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1)) -#define ERTS_KERNEL_TICK_TO_USEC(TCKS) (((TCKS)*(1000*1000))/SYS_CLK_TCK) -#endif +#define ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ +#define ERTS_SYS_TIME_INTERNAL_STATE_READ_MOSTLY__ #endif @@ -109,8 +93,15 @@ ErtsMonotonicTime clock_gettime_monotonic_verified(void); #ifdef ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ struct sys_time_internal_state_read_only__ { #if defined(OS_MONOTONIC_TIME_USING_TIMES) - int ticks_bsr; - int ticks_per_sec_wrap; + int times_shift; +#endif +}; +#endif + +#ifdef ERTS_SYS_TIME_INTERNAL_STATE_READ_MOSTLY__ +struct sys_time_internal_state_read_mostly__ { +#if defined(OS_MONOTONIC_TIME_USING_TIMES) + ErtsOsMonotonicTimeExtendState os_mtime_xtnd; #endif }; #endif @@ -121,15 +112,6 @@ struct sys_time_internal_state_write_freq__ { #if defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) ErtsMonotonicTime last_delivered; #endif -#if defined(OS_MONOTONIC_TIME_USING_TIMES) - ErtsMonotonicTime last_tick_count; - ErtsMonotonicTime last_tick_wrap_count; - ErtsMonotonicTime last_tick_monotonic_time; - ErtsMonotonicTime last_timeofday_usec; -#ifndef ERTS_WRAP_SYS_TIMES - SysTimes dummy_tms; -#endif -#endif }; #endif @@ -144,6 +126,14 @@ static struct { * ASSUMED_CACHE_LINE_SIZE]; } r; #endif +#ifdef ERTS_SYS_TIME_INTERNAL_STATE_READ_MOSTLY__ + union { + struct sys_time_internal_state_read_mostly__ m; + char align__[(((sizeof(struct sys_time_internal_state_read_mostly__) - 1) + / ASSUMED_CACHE_LINE_SIZE) + 1) + * ASSUMED_CACHE_LINE_SIZE]; + } wr; +#endif #ifdef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ union { struct sys_time_internal_state_write_freq__ f; @@ -193,8 +183,6 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_monotonic_info.func = "gethrtime"; #elif defined(OS_MONOTONIC_TIME_USING_TIMES) init_resp->os_monotonic_info.func = "times"; - init_resp->os_monotonic_info.locked_use = 1; - init_resp->os_monotonic_info.resolution = TICKS_PER_SEC(); #else # error Unknown erts_os_monotonic_time() implementation #endif @@ -247,7 +235,9 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) #endif /* defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) */ +#ifdef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT init_resp->os_monotonic_time_unit = ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT; +#endif init_resp->sys_clock_resolution = SYS_CLOCK_RESOLUTION; /* @@ -258,38 +248,40 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) erl_exit(ERTS_ABORT_EXIT, "Can't get clock ticks/sec\n"); #if defined(OS_MONOTONIC_TIME_USING_TIMES) +#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT +# error Time unit is supposed to be determined at runtime... +#endif + { + ErtsMonotonicTime resolution = erts_sys_time_data__.r.o.ticks_per_sec; + ErtsMonotonicTime time_unit = resolution; + int shift = 0; - if (erts_sys_time_data__.r.o.ticks_per_sec >= 1000) { - /* Workaround for beta linux kernels, need to be done in runtime - to make erlang run on both 2.4 and 2.5 kernels. In the future, - the kernel ticks might as - well be used as a high res timer instead, but that's for when the - majority uses kernels with HZ == 1024 */ - internal_state.r.o.ticks_bsr = 3; - } else { - internal_state.r.o.ticks_bsr = 0; - } + while (time_unit < 1000*1000) { + time_unit <<= 1; + shift++; + } - internal_state.r.o.ticks_per_sec_wrap - = (erts_sys_time_data__.r.o.ticks_per_sec - >> internal_state.r.o.ticks_bsr); + init_resp->os_monotonic_info.resolution = resolution; + init_resp->os_monotonic_time_unit = time_unit; + init_resp->os_monotonic_info.extended = 1; + internal_state.r.o.times_shift = shift; - erts_smp_mtx_init(&internal_state.w.f.mtx, "os_monotonic_time"); - internal_state.w.f.last_tick_count = KERNEL_TICKS(); - internal_state.w.f.last_tick_wrap_count = 0; - internal_state.w.f.last_tick_monotonic_time - = ERTS_KERNEL_TICK_TO_USEC(internal_state.w.f.last_tick_count); - { - SysTimeval tv; - sys_gettimeofday(&tv); - internal_state.w.f.last_timeofday_usec = tv.tv_sec*(1000*1000); - internal_state.w.f.last_timeofday_usec += tv.tv_usec; + erts_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd, + get_tick_count, + (1 << 29) / resolution); } - #endif /* defined(OS_MONOTONIC_TIME_USING_TIMES) */ } +void +erts_late_sys_init_time(void) +{ +#if defined(OS_MONOTONIC_TIME_USING_TIMES) + erts_late_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd); +#endif +} + #if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) static ERTS_INLINE ErtsMonotonicTime @@ -379,86 +371,14 @@ ErtsMonotonicTime erts_os_monotonic_time(void) #elif defined(OS_MONOTONIC_TIME_USING_TIMES) -static clock_t sys_times_wrap(void) -{ - SysTimes dummy; - clock_t result = (sys_times(&dummy) >> internal_state.r.o.ticks_bsr); - return result; -} - -void -erts_os_time_offset_finalize(void) -{ - erts_smp_mtx_lock(&internal_state.w.f.mtx); - internal_state.w.f.last_tick_wrap_count = 0; - erts_smp_mtx_unlock(&internal_state.w.f.mtx); -} - -#define ERTS_TIME_EXCEED_TICK_LIMIT(SYS_TIME, TCK_TIME) \ - (((Uint64) (SYS_TIME)) - (((Uint64) (TCK_TIME)) \ - - ERTS_KERNEL_TICK_TO_USEC(1)) \ - > ERTS_KERNEL_TICK_TO_USEC(2)) - -/* Returns monotonic time in micro seconds */ ErtsMonotonicTime erts_os_monotonic_time(void) { - SysTimeval tv; - ErtsMonotonicTime res; - ErtsMonotonicTime tick_count; - ErtsMonotonicTime tick_count_usec; - ErtsMonotonicTime tick_monotonic_time; - ErtsMonotonicTime timeofday_usec; - ErtsMonotonicTime timeofday_diff_usec; - - erts_smp_mtx_lock(&internal_state.w.f.mtx); - - tick_count = (ErtsMonotonicTime) KERNEL_TICKS(); - sys_gettimeofday(&tv); - - if (internal_state.w.f.last_tick_count > tick_count) { - internal_state.w.f.last_tick_wrap_count - += (((ErtsMonotonicTime) 1) << ((sizeof(clock_t) * 8) - 1)); - } - internal_state.w.f.last_tick_count = tick_count; - tick_count += internal_state.w.f.last_tick_wrap_count; - - tick_count_usec = ERTS_KERNEL_TICK_TO_USEC(tick_count); - - timeofday_usec = (ErtsMonotonicTime) tv.tv_sec*(1000*1000); - timeofday_usec += (ErtsMonotonicTime) tv.tv_usec; - timeofday_diff_usec = timeofday_usec; - timeofday_diff_usec -= internal_state.w.f.last_timeofday_usec; - internal_state.w.f.last_timeofday_usec = timeofday_usec; - - if (timeofday_diff_usec < 0) { - /* timeofday jumped backwards use tick count only... */ - tick_monotonic_time = tick_count_usec; - } - else { - /* Use time diff from of timeofday if not off by too much... */ - tick_monotonic_time = internal_state.w.f.last_tick_monotonic_time; - tick_monotonic_time += timeofday_diff_usec; - - if (ERTS_TIME_EXCEED_TICK_LIMIT(tick_monotonic_time, tick_count_usec)) { - /* - * Value off by more than one tick from tick_count, i.e. - * timofday leaped one way or the other. We use - * tick_count_usec as is instead and unfortunately - * get lousy precision. - */ - tick_monotonic_time = tick_count_usec; - } - } - - if (internal_state.w.f.last_tick_monotonic_time < tick_monotonic_time) - internal_state.w.f.last_tick_monotonic_time = tick_monotonic_time; - - res = internal_state.w.f.last_tick_monotonic_time; - - erts_smp_mtx_unlock(&internal_state.w.f.mtx); - - return res; + Uint32 ticks = get_tick_count(); + ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + ticks); + return ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + ticks) << internal_state.r.o.times_shift; } #endif /* !defined(OS_MONOTONIC_TIME_USING_TIMES) */ diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index 3a10125c81..5fa575fa3b 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -25,6 +25,7 @@ #endif #include "sys.h" #include "assert.h" +#include "erl_os_monotonic_time_extender.h" #define LL_LITERAL(X) ERTS_I64_LITERAL(X) @@ -80,6 +81,10 @@ struct sys_time_internal_state_read_only__ { BOOL (WINAPI *pQueryPerformanceCounter)(LARGE_INTEGER *); }; +struct sys_time_internal_state_read_mostly__ { + ErtsOsMonotonicTimeExtendState os_mtime_xtnd; +}; + struct sys_time_internal_state_write_freq__ { erts_smp_mtx_t mtime_mtx; ULONGLONG wrap; @@ -93,6 +98,12 @@ __declspec(align(ASSUMED_CACHE_LINE_SIZE)) struct { / ASSUMED_CACHE_LINE_SIZE) + 1) * ASSUMED_CACHE_LINE_SIZE]; } r; + union { + struct sys_time_internal_state_read_mostly__ m; + char align__[(((sizeof(struct sys_time_internal_state_read_mostly__) - 1) + / ASSUMED_CACHE_LINE_SIZE) + 1) + * ASSUMED_CACHE_LINE_SIZE]; + } wr; union { struct sys_time_internal_state_write_freq__ f; char align__[(((sizeof(struct sys_time_internal_state_write_freq__) - 1) @@ -114,29 +125,27 @@ os_monotonic_time_qpc(void) return (ErtsMonotonicTime) pc.QuadPart; } +static Uint32 +get_tick_count(void) +{ + return (Uint32) GetTickCount(); +} + static ErtsMonotonicTime os_monotonic_time_gtc32(void) { - ULONGLONG res, ticks; - - erts_smp_mtx_lock(&internal_state.w.f.mtime_mtx); - - ticks = (ULONGLONG) (GetTickCount() & 0x7FFFFFFF); - if (ticks < internal_state.w.f.last_tick_count) - internal_state.w.f.wrap += (ULONGLONG) LL_LITERAL(1) << 31; - internal_state.w.f.last_tick_count = ticks; - res = ticks + internal_state.w.f.wrap; - - erts_smp_mtx_unlock(&internal_state.w.f.mtime_mtx); - - return (ErtsMonotonicTime) res*1000; + Uint32 ticks = (Uint32) GetTickCount(); + ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + tick_count); + return ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + ticks) << 10; } static ErtsMonotonicTime os_monotonic_time_gtc64(void) { ULONGLONG ticks = (*internal_state.r.o.pGetTickCount64)(); - return (ErtsMonotonicTime) ticks*1000; + return (ErtsMonotonicTime) ticks << 10; } /* @@ -163,9 +172,14 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_monotonic_info.func = "GetTickCount"; init_resp->os_monotonic_info.locked_use = 1; - init_resp->os_monotonic_info.resolution = 1000; - time_unit = (ErtsMonotonicTime) 1000*1000; + /* 10-16 ms resolution according to MicroSoft documentation */ + init_resp->os_monotonic_info.resolution = 100; /* 10 ms */ + time_unit = (ErtsMonotonicTime) (1000 << 10); os_mtime_func = os_monotonic_time_gtc32; + init_resp->os_monotonic_info.extended = 1; + erts_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd, + get_tick_count, + 60*60*24*7); /* Check once a week */ } else { int major, minor, build; @@ -184,8 +198,9 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_monotonic_info.func = "GetTickCount64"; init_resp->os_monotonic_info.locked_use = 0; - init_resp->os_monotonic_info.resolution = 1000; - time_unit = (ErtsMonotonicTime) 1000*1000; + /* 10-16 ms resolution according to MicroSoft documentation */ + init_resp->os_monotonic_info.resolution = 100; /* 10 ms */ + time_unit = (ErtsMonotonicTime) (1000 << 10); os_mtime_func = os_monotonic_time_gtc64; } else { /* Vista or newer... */ @@ -235,6 +250,13 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) } +void +erts_late_sys_init_time(void) +{ + if (erts_sys_time_data__.r.o.os_monotonic_time == os_monotonic_time_gtc32) + erts_late_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd); +} + /* Returns a switchtimes for DST as UTC filetimes given data from a TIME_ZONE_INFORMATION, see sys_localtime_r for usage. */ static void -- cgit v1.2.3 From 12bf6b0c82762744506e153720076524401c8512 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 23 Mar 2015 17:07:45 +0100 Subject: erts: Rename to flatmap_from_validated_list from map_to_validated_list --- erts/emulator/beam/erl_map.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index bcbda65da0..26233a98c6 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -85,7 +85,7 @@ static Eterm hashmap_to_list(Process *p, Eterm map); static Eterm hashmap_keys(Process *p, Eterm map); static Eterm hashmap_values(Process *p, Eterm map); static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node); -static Eterm map_from_validated_list(Process *p, Eterm list, Uint size); +static Eterm flatmap_from_validated_list(Process *p, Eterm list, Uint size); static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size); static Eterm hashmap_from_unsorted_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int reject_dupkeys); static Eterm hashmap_from_sorted_unique_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int is_root); @@ -277,7 +277,7 @@ BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) { if (size > MAP_SMALL_MAP_LIMIT) { BIF_RET(hashmap_from_validated_list(BIF_P, BIF_ARG_1, size)); } else { - BIF_RET(map_from_validated_list(BIF_P, BIF_ARG_1, size)); + BIF_RET(flatmap_from_validated_list(BIF_P, BIF_ARG_1, size)); } } @@ -286,7 +286,7 @@ error: BIF_ERROR(BIF_P, BADARG); } -static Eterm map_from_validated_list(Process *p, Eterm list, Uint size) { +static Eterm flatmap_from_validated_list(Process *p, Eterm list, Uint size) { Eterm *kv, item = list; Eterm *hp, *thp,*vs, *ks, keys, res; flatmap_t *mp; -- cgit v1.2.3 From e52829ceb614b36c31a650dea455a463fc490698 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 23 Mar 2015 16:39:56 +0100 Subject: erts_sys_hrtime() for lcnt --- erts/emulator/beam/erl_lock_count.c | 13 +++---- erts/emulator/beam/sys.h | 1 - erts/emulator/sys/unix/erl_unix_sys.h | 12 +++++++ erts/emulator/sys/unix/sys.c | 12 +++---- erts/emulator/sys/unix/sys_time.c | 44 ++++++++++++++++++++--- erts/emulator/sys/win32/erl_win_sys.h | 10 ++++++ erts/emulator/sys/win32/sys_time.c | 68 ++++++++++++++++++++++++++++++----- 7 files changed, 131 insertions(+), 29 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index ddeb56a6be..c6d8f4df95 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -104,16 +104,13 @@ static void lcnt_clear_stats(erts_lcnt_lock_stats_t *stats) { } static void lcnt_time(erts_lcnt_time_t *time) { -#if 0 || defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) - ErtsMonotonicTime mtime = ERTS_MONOTONIC_TO_NSEC(erts_os_monotonic_time()); + /* + * erts_sys_hrtime() is the highest resolution + * we could find, it may or may not be monotonic... + */ + ErtsMonotonicTime mtime = erts_sys_hrtime(); time->s = (unsigned long) (mtime / 1000000000LL); time->ns = (unsigned long) (mtime - 1000000000LL*time->s); -#else - SysTimeval tv; - sys_gettimeofday(&tv); - time->s = tv.tv_sec; - time->ns = tv.tv_usec*1000LL; -#endif } static void lcnt_time_diff(erts_lcnt_time_t *d, erts_lcnt_time_t *t1, erts_lcnt_time_t *t0) { diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index d83bdf8d31..c51b665db9 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -708,7 +708,6 @@ extern int erts_init_time_sup(int, ErtsTimeWarpMode); extern void erts_sys_init_float(void); extern void erts_thread_init_float(void); extern void erts_thread_disable_fpe(void); - ERTS_GLB_INLINE int erts_block_fpe(void); ERTS_GLB_INLINE void erts_unblock_fpe(int); diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 303ebeee7c..a14a4b3eae 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -161,8 +161,10 @@ typedef struct tms SysTimes; #if SIZEOF_LONG == 8 typedef long ErtsMonotonicTime; +typedef long ErtsSysHrTime; #elif SIZEOF_LONG_LONG == 8 typedef long long ErtsMonotonicTime; +typedef long long ErtsSysHrTime; #else #error No signed 64-bit type found... #endif @@ -201,6 +203,7 @@ ErtsMonotonicTime erts_os_monotonic_time(void); #elif defined(OS_MONOTONIC_TIME_USING_GETHRTIME) #define erts_os_monotonic() ((ErtsMonotonicTime) gethrtime()) +#define erts_sys_hrtime() ((ErtsSysHrTime) gethrtime()) #elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ || defined(OS_MONOTONIC_TIME_USING_TIMES) @@ -221,6 +224,15 @@ ErtsMonotonicTime erts_os_monotonic_time(void); #endif +/* + * erts_sys_hrtime() is the highest resolution + * time function found. Time unit is nano-seconds. + * It may or may not be monotonic. + */ +#ifndef erts_sys_hrtime +extern ErtsSysHrTime erts_sys_hrtime(void); +#endif + struct erts_sys_time_read_only_data__ { #ifdef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ ErtsMonotonicTime (*os_monotonic_time)(void); diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 58b01c094e..3647ea6a88 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -528,18 +528,18 @@ erts_sys_pre_init(void) erts_thr_init(&eid); -#endif /* USE_THREADS */ - - erts_init_sys_time_sup(); - -#ifdef USE_THREADS - report_exit_list = NULL; #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_init(); #endif +#endif /* USE_THREADS */ + + erts_init_sys_time_sup(); + +#ifdef USE_THREADS + #if CHLDWTHR || defined(ERTS_SMP) erts_mtx_init(&chld_stat_mtx, "child_status"); #endif diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index 9db727b111..134e3a67b0 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -85,8 +85,8 @@ ErtsSysTimeData__ erts_sys_time_data__ erts_align_attribute(ERTS_CACHE_LINE_SIZE #define ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ -ErtsMonotonicTime clock_gettime_monotonic_raw(void); -ErtsMonotonicTime clock_gettime_monotonic_verified(void); +static ErtsMonotonicTime clock_gettime_monotonic_raw(void); +static ErtsMonotonicTime clock_gettime_monotonic_verified(void); #endif /* defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */ @@ -306,7 +306,7 @@ clock_gettime_monotonic(void) #if defined(__linux__) -ErtsMonotonicTime clock_gettime_monotonic_verified(void) +static ErtsMonotonicTime clock_gettime_monotonic_verified(void) { ErtsMonotonicTime mtime; @@ -322,7 +322,7 @@ ErtsMonotonicTime clock_gettime_monotonic_verified(void) return mtime; } -ErtsMonotonicTime clock_gettime_monotonic_raw(void) +static ErtsMonotonicTime clock_gettime_monotonic_raw(void) { return clock_gettime_monotonic(); } @@ -336,6 +336,12 @@ ErtsMonotonicTime erts_os_monotonic_time(void) #endif /* !defined(__linux__) */ +ErtsSysHrTime +erts_sys_hrtime(void) +{ + return (ErtsSysHrTime) clock_gettime_monotonic(); +} + #elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) #include @@ -369,6 +375,12 @@ ErtsMonotonicTime erts_os_monotonic_time(void) return mtime; } +ErtsSysHrTime +erts_sys_hrtime(void) +{ + return (ErtsSysHrTime) erts_os_monotonic_time(); +} + #elif defined(OS_MONOTONIC_TIME_USING_TIMES) ErtsMonotonicTime @@ -381,7 +393,29 @@ erts_os_monotonic_time(void) ticks) << internal_state.r.o.times_shift; } -#endif /* !defined(OS_MONOTONIC_TIME_USING_TIMES) */ +# define ERTS_NEED_ERTS_SYS_HRTIME_FALLBACK + +#else /* !defined(OS_MONOTONIC_TIME_USING_TIMES) */ +/* No os-monotonic-time */ +# define ERTS_NEED_ERTS_SYS_HRTIME_FALLBACK +#endif + +#ifdef ERTS_NEED_ERTS_SYS_HRTIME_FALLBACK + +ErtsSysHrTime +erts_sys_hrtime(void) +{ + ErtsSysHrTime time; + struct timeval tv; + gettimeofday(&tv); + time = (ErtsSysHrTime) tv.tv_sec; + time *= (ErtsSysHrTime) 1000*1000*1000; + time += ((ErtsSysHrTime) tv.tv_usec)*1000; + return time; +} + +#endif + #ifdef HAVE_GETHRVTIME_PROCFS_IOCTL diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index f04bb6a0e5..febf3b5cca 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -172,11 +172,13 @@ typedef long long Sint64; # endif typedef long long ErtsMonotonicTime; +typedef long long ErtsSysHrTime; #else typedef ULONGLONG Uint64; typedef LONGLONG Sint64; typedef LONGLONG ErtsMonotonicTime; +typedef LONGLONG ErtsSysHrTime; #endif #define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63) @@ -187,6 +189,7 @@ typedef LONGLONG ErtsMonotonicTime; struct erts_sys_time_read_only_data__ { ErtsMonotonicTime (*os_monotonic_time)(void); + ErtsSysHrTime (*sys_hrtime)(void); }; typedef struct { @@ -201,6 +204,7 @@ typedef struct { extern ErtsSysTimeData__ erts_sys_time_data__; ERTS_GLB_INLINE ErtsMonotonicTime erts_os_monotonic_time(void); +ERTS_GLB_INLINE ErtsSysHrTime erts_sys_hrtime(void); #if ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -210,6 +214,12 @@ erts_os_monotonic_time(void) return (*erts_sys_time_data__.r.o.os_monotonic_time)(); } +ERTS_GLB_INLINE ErtsSysHrTime +erts_sys_hrtime(void) +{ + return (*erts_sys_time_data__.r.o.sys_hrtime)(); +} + #endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ extern void sys_gettimeofday(SysTimeval *tv); diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index 5fa575fa3b..ea180f067f 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -26,6 +26,7 @@ #include "sys.h" #include "assert.h" #include "erl_os_monotonic_time_extender.h" +#include "erl_time.h" #define LL_LITERAL(X) ERTS_I64_LITERAL(X) @@ -79,6 +80,7 @@ static int days_in_month[2][13] = { struct sys_time_internal_state_read_only__ { ULONGLONG (WINAPI *pGetTickCount64)(void); BOOL (WINAPI *pQueryPerformanceCounter)(LARGE_INTEGER *); + Sint32 pcf; }; struct sys_time_internal_state_read_mostly__ { @@ -148,6 +150,42 @@ os_monotonic_time_gtc64(void) return (ErtsMonotonicTime) ticks << 10; } +static ErtsSysHrTime +sys_hrtime_qpc(void) +{ + LARGE_INTEGER pc; + + if (!(*internal_state.r.o.pQueryPerformanceCounter)(&pc)) + erl_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n"); + + ASSERT(pc.QuadPart > 0); + + return (ErtsSysHrTime) erts_time_unit_conversion((Uint64) pc.QuadPart, + internal_state.r.o.pcf, + (Uint32) 1000*1000*1000); +} + +static ErtsSysHrTime +sys_hrtime_gtc32(void) +{ + ErtsSysHrTime time; + Uint32 ticks = (Uint32) GetTickCount(); + ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + tick_count); + time = (ErtsSysHrTime) ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + ticks); + time *= (ErtsSysHrTime) (1000 * 1000); + return time; +} + +static ErtsSysHrTime +sys_hrtime_gtc64(void) +{ + ErtsSysHrTime time = (*internal_state.r.o.pGetTickCount64)(); + time *= (ErtsSysHrTime) (1000*1000); + return time; +} + /* * Init */ @@ -156,6 +194,7 @@ void sys_init_time(ErtsSysInitTimeResult *init_resp) { ErtsMonotonicTime (*os_mtime_func)(void); + ErtsSysHrTime (*sys_hrtime_func)(void) = NULL; ErtsMonotonicTime time_unit; char kernel_dll_name[] = "kernel32"; HMODULE module; @@ -180,6 +219,8 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) erts_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd, get_tick_count, 60*60*24*7); /* Check once a week */ + if (!sys_hrtime_func) + sys_hrtime_func = sys_hrtime_gtc32; } else { int major, minor, build; @@ -202,6 +243,8 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_monotonic_info.resolution = 100; /* 10 ms */ time_unit = (ErtsMonotonicTime) (1000 << 10); os_mtime_func = os_monotonic_time_gtc64; + if (!sys_hrtime_func) + sys_hrtime_func = sys_hrtime_gtc64; } else { /* Vista or newer... */ @@ -214,21 +257,28 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) goto get_tick_count64; if (!(*QPF)(&pf)) goto get_tick_count64; - /* - * We only use QueryPerformanceCounter() if - * its frequency is equal to, or larger than - * GHz in order to ensure that the user wont - * be able to observe faulty order between - * values retrieved on different threads. - */ - if (pf.QuadPart < (LONGLONG) 1000*1000*1000) - goto get_tick_count64; + internal_state.r.o.pQueryPerformanceCounter = ((BOOL (WINAPI *)(LARGE_INTEGER *)) GetProcAddress(module, "QueryPerformanceCounter")); if (!internal_state.r.o.pQueryPerformanceCounter) goto get_tick_count64; + if (pf.QuadPart < (((LONGLONG) 1) << 32)) { + internal_state.r.o.pcf = (Uint32) pf.QuadPart; + sys_hrtime_func = sys_hrtime_qpc; + } + + /* + * We only use QueryPerformanceCounter() for + * os-monotonic-time if its frequency is equal + * to, or larger than GHz in order to ensure + * that the user wont be able to observe faulty + * order between values retrieved on different threads. + */ + if (pf.QuadPart < (LONGLONG) 1000*1000*1000) + goto get_tick_count64; + init_resp->os_monotonic_info.func = "QueryPerformanceCounter"; init_resp->os_monotonic_info.locked_use = 0; time_unit = (ErtsMonotonicTime) pf.QuadPart; -- cgit v1.2.3 From 18464e275987eaa69c627b1a2784a9deeb216c03 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 24 Mar 2015 00:38:18 +0100 Subject: Fix zero timout timers --- erts/emulator/beam/time.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index 9f997e1d0b..3dfd3f79d4 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -524,6 +524,7 @@ erts_set_timer(ErlTimer *p, ErlTimeoutProc timeout, tiw->nto++; tiw->at_once.nto++; *tiw->at_once.tail = p; + tiw->at_once.tail = &p->next; p->next = NULL; p->timeout_pos = timeout_pos; timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(timeout_pos); -- cgit v1.2.3 From 052695858c07477db480d25d2d858540088d04c3 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 24 Mar 2015 10:27:20 +0100 Subject: Documentation adjustments --- erts/doc/src/erlang.xml | 33 ++++++++++++++++++++++----------- erts/doc/src/time_correction.xml | 29 +++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 98c0ef5f81..7cb417ac92 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -110,10 +110,17 @@

The value of the native time unit gives you more or less no information at all about the - quality of time values. It sets an upper bound for - the resolution as well as for the precision, but it - gives absolutely no information at all about the - accuracy.

+ quality of time values. It sets a limit for + the + resolution + as well as for the + precision + of time values, + but it gives absolutely no information at all about the + accuracy + of time values. The resolution of the native time + unit and the resolution of time values may differ + significantly.

@@ -6435,18 +6442,22 @@ ok Function.

{resolution, OsMonotonicTimeResolution} -

Highest possible resolution of current - OS monotonic time source as parts per second. If - no resolution information can be retreived from - the OS, OsMonotonicTimeResolution will be +

Highest possible + resolution + of current OS monotonic time source as parts per + second. If no resolution information can be retreived + from the OS, OsMonotonicTimeResolution will be set to the resolution of the time unit of Functions return value. That is, the actual resolution may be lower than OsMonotonicTimeResolution. Also note that the resolution does not say anything about the - accuracy, and that the precision might not align - with the resolution. You do, however, know that the - precision won't be higher than + accuracy, + and that the + precision + might not align with the resolution. You do, + however, know that the precision won't be + better than OsMonotonicTimeResolution.

{extended, Extended} diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index 3bc3d04186..ed658ff58c 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -114,6 +114,29 @@ happened yet), POSIX time would make a one second leap forward.

+ +
+ Time Resolution +

The shortest time interval that can be distinguished when + reading time values.

+
+ + +
+ Time Precision +

The shortest time interval that can be be distinguished + repeatedly and reliably when reading time values. Precision + is limited by the + resolution, but + resolution and precision might differ significantly.

+
+ + +
+ Time Accuracy +

The correctness of time values.

+
+
OS System Time @@ -162,8 +185,10 @@ Erlang runtime system. The Erlang monotonic time increase since some unspecified point in time. It can be retrieved by calling erlang:monotonic_time(). - The accuracy, and precision of Erlang monotonic time heavily - depends on the accuracy and precision of + The + accuracy, and + precision of Erlang + monotonic time heavily depends on the accuracy and precision of OS monotonic time, the accuracy and precision of OS system time as well -- cgit v1.2.3 From c20482023b70768bd84d25f1e34dbbc2fe09cf31 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 24 Mar 2015 15:28:11 +0100 Subject: Better OS system time implementation --- erts/aclocal.m4 | 95 ++++++++++++++- erts/doc/src/erlang.xml | 55 +++++++++ erts/doc/src/time_correction.xml | 14 ++- erts/emulator/beam/erl_bif_info.c | 2 + erts/emulator/beam/erl_time.h | 1 + erts/emulator/beam/erl_time_sup.c | 214 +++++++++++++++++++--------------- erts/emulator/beam/sys.h | 10 +- erts/emulator/sys/unix/erl_unix_sys.h | 4 + erts/emulator/sys/unix/sys_time.c | 167 +++++++++++++++++++++++--- erts/emulator/sys/win32/erl_win_sys.h | 4 + erts/emulator/sys/win32/sys_time.c | 73 +++++++++--- erts/example/time_compat.erl | 1 + erts/preloaded/ebin/erlang.beam | Bin 105832 -> 105852 bytes erts/preloaded/src/erlang.erl | 1 + 14 files changed, 507 insertions(+), 134 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 6ba54e823e..7a969547cf 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -747,7 +747,7 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, AC_CHECK_FUNCS([clock_getres gethrtime]) - AC_CACHE_CHECK([for mach clock_get_time()], erl_cv_mach_clock_get_time, + AC_CACHE_CHECK([for mach clock_get_time()], erl_cv_mach_clock_get_time_monotonic, [ AC_TRY_COMPILE([ #include @@ -762,13 +762,13 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, res = clock_get_time(clk_srv, &time_spec); mach_port_deallocate(mach_task_self(), clk_srv); ], - erl_cv_mach_clock_get_time=yes, - erl_cv_mach_clock_get_time=no) + erl_cv_mach_clock_get_time_monotonic=yes, + erl_cv_mach_clock_get_time_monotonic=no) ]) - case $erl_cv_clock_gettime_monotonic-$ac_cv_func_gethrtime-$erl_cv_mach_clock_get_time-$host_os in + case $erl_cv_clock_gettime_monotonic-$ac_cv_func_gethrtime-$erl_cv_mach_clock_get_time_monotonic-$host_os in *-*-*-win32) - erl_monotonic_clock_func=GetTickCount + erl_monotonic_clock_func=WindowsAPI ;; CLOCK_*-*-*-linux*) if test X$cross_compiling != Xyes; then @@ -833,6 +833,70 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, ]) +AC_DEFUN(ERL_WALL_CLOCK, +[ + AC_CACHE_CHECK([for clock_gettime() with wall clock type], erl_cv_clock_gettime_wall, + [ + for clock_type in CLOCK_REALTIME; do + AC_TRY_COMPILE([ +#include + ], + [ + struct timespec ts; + long long result; + clock_gettime($clock_type,&ts); + result = ((long long) ts.tv_sec) * 1000000000LL + + ((long long) ts.tv_nsec); + ], + erl_cv_clock_gettime_wall=$clock_type, + erl_cv_clock_gettime_wall=no) + test $erl_cv_clock_gettime_wall = no || break + done + ]) + + AC_CHECK_FUNCS([clock_getres gettimeofday]) + + AC_CACHE_CHECK([for mach clock_get_time()], erl_cv_mach_clock_get_time_wall, + [ + AC_TRY_COMPILE([ +#include +#include + ], + [ + kern_return_t res; + clock_serv_t clk_srv; + mach_timespec_t time_spec; + + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clk_srv); + res = clock_get_time(clk_srv, &time_spec); + mach_port_deallocate(mach_task_self(), clk_srv); + ], + erl_cv_mach_clock_get_time_wall=yes, + erl_cv_mach_clock_get_time_wall=no) + ]) + + erl_wall_clock_id= + case $erl_cv_clock_gettime_wall-$erl_cv_mach_clock_get_time_wall-$ac_cv_func_gettimeofday-$host_os in + *-*-*-win32) + erl_wall_clock_func=WindowsAPI + ;; + no-yes-*-*) + erl_wall_clock_func=mach_clock_get_time + erl_wall_clock_id=CALENDAR_CLOCK + ;; + CLOCK_*-*-*-*) + erl_wall_clock_func=clock_gettime + erl_wall_clock_id=$erl_cv_clock_gettime_wall + ;; + no-no-yes-*) + erl_wall_clock_func=gettimeofday + ;; + *) + erl_wall_clock_func=none + ;; + esac +]) + dnl ---------------------------------------------------------------------- dnl dnl LM_CHECK_THR_LIB @@ -1850,6 +1914,27 @@ dnl AC_DEFUN(ERL_TIME_CORRECTION, [ +ERL_WALL_CLOCK + +case $erl_wall_clock_func in + mach_clock_get_time) + AC_DEFINE(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME, [1], [Define if you want to implement erts_os_system_time() using mach clock_get_time()]) + ;; + clock_gettime) + AC_DEFINE(OS_SYSTEM_TIME_USING_CLOCK_GETTIME, [1], [Define if you want to implement erts_os_system_time() using clock_gettime()]) + ;; + gettimeofday) + AC_DEFINE(OS_SYSTEM_TIME_GETTIMEOFDAY, [1], [Define if you want to implement erts_os_system_time() using gettimeofday()]) + ;; + *) + ;; +esac + +if test "x$erl_wall_clock_id" != "x"; then + AC_DEFINE_UNQUOTED(WALL_CLOCK_ID_STR, ["$erl_wall_clock_id"], [Define as a string of wall clock id to use]) + AC_DEFINE_UNQUOTED(WALL_CLOCK_ID, [$erl_wall_clock_id], [Define to wall clock id to use]) +fi + ERL_MONOTONIC_CLOCK case $erl_monotonic_clock_func in diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 7cb417ac92..bd5efb712c 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6031,6 +6031,8 @@ ok + + Information about the system

Returns various information about the current system @@ -6482,6 +6484,59 @@ ok time unit.

+ os_system_time_source + +

Returns a list containing information about the source of + OS + system time that is used by the runtime system.

+

The list contains two-tuples with Keys + as first element, and Values as second element. The + order if these tuples is undefined. Currently the following + tuples may be part of the list, but more tuples may be + introduced in the future:

+ + {function, Function} +

Function is the name of the funcion + used.

+ + {clock_id, ClockId} +

This tuple only exist if Function + can be used with different clocks. ClockId + corresponds to the clock identifer used when calling + Function.

+ + {resolution, OsSystemTimeResolution} +

Highest possible + resolution + of current OS system time source as parts per + second. If no resolution information can be retreived + from the OS, OsSystemTimeResolution will be + set to the resolution of the time unit of + Functions return value. That is, the actual + resolution may be lower than + OsSystemTimeResolution. Also note that + the resolution does not say anything about the + accuracy, + and that the + precision + might not align with the resolution. You do, + however, know that the precision won't be + better than + OsSystemTimeResolution.

+ + {parallel, Parallel} +

Parallel equals yes if + Function is called in parallel from multiple + threads. If it is not called in parallel, because + calls needs to be serialized, Parallel equals + no.

+ + {time, OsSystemTime} +

OsSystemTime equals current OS + system time in native + time unit.

+
+
port_parallelism

Returns the default port parallelism scheduling hint used. For more information see the diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index ed658ff58c..979a37d7ff 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -147,7 +147,9 @@ This may or may not be an accurate view of POSIX time. This time may typically be adjusted both backwards and forwards without limitation. That is, huge leaps both backwards and forwards in time - may be observed.

+ may be observed. You can get information about the Erlang runtime + system's source of OS system time by calling + erlang:system_info(os_system_time_source).

@@ -161,7 +163,9 @@ point in time that is not connected to OS system time. Note that this type of time is not necessarily provided by all operating - systems.

+ systems. You can get information about the Erlang runtime + system's source of OS monotonic time by calling + erlang:system_info(os_monotonic_time_source).

@@ -597,6 +601,7 @@

erlang:monitor(time_offset, clock_service)

erlang:system_flag(time_offset, finalize)

erlang:system_info(os_monotonic_time_source)

+

erlang:system_info(os_system_time_source)

erlang:system_info(time_offset)

erlang:system_info(time_warp_mode)

erlang:system_info(time_correction)

@@ -852,8 +857,9 @@ EventTag = {Time, UMI} when it is not available. Fortunately almost all of the new API can easily be implemented using existing primitives (except for - erlang:system_info(start_time), and - erlang:system_info(os_monotonic_time_source)). + erlang:system_info(start_time), + erlang:system_info(os_monotonic_time_source), and + erlang:system_info(os_system_time_source)). By wrapping the API with functions that fall back on erlang:now/0 when the new API is not available, and using these wrappers instead of using the API directly diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 70062b2305..8491c01b75 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2120,6 +2120,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) } } else if (ERTS_IS_ATOM_STR("os_monotonic_time_source", BIF_ARG_1)) { BIF_RET(erts_monotonic_time_source(BIF_P)); + } else if (ERTS_IS_ATOM_STR("os_system_time_source", BIF_ARG_1)) { + BIF_RET(erts_system_time_source(BIF_P)); } else if (ERTS_IS_ATOM_STR("time_correction", BIF_ARG_1)) { BIF_RET(erts_has_time_correction() ? am_true : am_false); } else if (ERTS_IS_ATOM_STR("start_time", BIF_ARG_1)) { diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 35a1442dbd..7f8c4e0ed5 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -148,6 +148,7 @@ ErtsTimeOffsetState erts_finalize_time_offset(void); struct process; Eterm erts_get_monotonic_start_time(struct process *c_p); Eterm erts_monotonic_time_source(struct process*c_p); +Eterm erts_system_time_source(struct process*c_p); #ifdef SYS_CLOCK_RESOLUTION #define ERTS_CLKTCK_RESOLUTION ((ErtsMonotonicTime) (SYS_CLOCK_RESOLUTION*1000)) diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index d961fae81a..ef39f4b5f4 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -133,12 +133,17 @@ struct time_sup_read_only__ { ErtsTimeWarpMode warp_mode; #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT ErtsMonotonicTime moffset; - int os_monotonic_disable; - char *os_monotonic_func; - char *os_monotonic_clock_id; - int os_monotonic_locked; - Uint64 os_monotonic_resolution; - Uint64 os_monotonic_extended; + int os_monotonic_time_disable; + char *os_monotonic_time_func; + char *os_monotonic_time_clock_id; + int os_monotonic_time_locked; + Uint64 os_monotonic_time_resolution; + Uint64 os_monotonic_time_extended; + char *os_system_time_func; + char *os_system_time_clock_id; + int os_system_time_locked; + Uint64 os_system_time_resolution; + Uint64 os_system_time_extended; #endif #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT ErtsMonotonicTime start; @@ -209,7 +214,7 @@ struct time_sup_infrequently_changed__ { ErtsMonotonicTime minit; #endif int finalized_offset; - SysTimeval inittv; /* Used everywhere, the initial time-of-day */ + ErtsSystemTime sinit; ErtsMonotonicTime not_corrected_moffset; erts_atomic64_t offset; }; @@ -242,10 +247,8 @@ ErtsTimeSupData erts_time_sup__ erts_align_attribute(ERTS_CACHE_LINE_SIZE); erts_approx_time_t erts_get_approx_time(void) { - SysTimeval tv; - sys_gettimeofday(&tv); - - return (erts_approx_time_t) tv.tv_sec; + ErtsSystemTime stime = erts_os_system_time(); + return (erts_approx_time_t) ERTS_MONOTONIC_TO_SEC(stime); } static ERTS_INLINE void @@ -403,7 +406,6 @@ check_time_correction(void *unused) ErtsMonotonicTime mdiff, sdiff, os_mtime, erl_mtime, os_stime, erl_stime, time_offset; Uint timeout; - SysTimeval tod; int set_new_correction, begin_short_intervals = 0; erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); @@ -411,7 +413,7 @@ check_time_correction(void *unused) ASSERT(time_sup.inf.c.finalized_offset); os_mtime = erts_os_monotonic_time(); - sys_gettimeofday(&tod); + os_stime = erts_os_system_time(); cdata = time_sup.inf.c.parmon.cdata; @@ -426,9 +428,6 @@ check_time_correction(void *unused) time_offset = get_time_offset(); erl_stime = erl_mtime + time_offset; - os_stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec); - os_stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec); - sdiff = erl_stime - os_stime; new_correction = cip->correction; @@ -686,7 +685,6 @@ init_check_time_correction(void *unused) ErtsMonotonicTime old_mtime, old_stime, mtime, stime, mtime_diff, stime_diff; int ix; - SysTimeval tod; ddp = &time_sup.inf.c.parmon.cdata.drift; ix = ddp->ix; @@ -694,10 +692,7 @@ init_check_time_correction(void *unused) old_stime = ddp->intervals[0].time.sys; mtime = erts_os_monotonic_time(); - sys_gettimeofday(&tod); - - stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec); - stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec); + stime = erts_os_system_time(); mtime_diff = mtime - old_mtime; stime_diff = stime - old_stime; @@ -729,7 +724,7 @@ init_check_time_correction(void *unused) #endif static ErtsMonotonicTime -finalize_corrected_time_offset(SysTimeval *todp) +finalize_corrected_time_offset(ErtsSystemTime *stimep) { ErtsMonotonicTime os_mtime; ErtsMonotonicCorrectionData cdata; @@ -738,7 +733,7 @@ finalize_corrected_time_offset(SysTimeval *todp) erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); os_mtime = erts_os_monotonic_time(); - sys_gettimeofday(todp); + *stimep = erts_os_system_time(); cdata = time_sup.inf.c.parmon.cdata; @@ -774,15 +769,11 @@ late_init_time_correction(void) static ErtsMonotonicTime get_not_corrected_time(void) { - SysTimeval tmp_tv; ErtsMonotonicTime stime, mtime; erts_smp_mtx_lock(&erts_get_time_mtx); - sys_gettimeofday(&tmp_tv); - - stime = ERTS_SEC_TO_MONOTONIC(tmp_tv.tv_sec); - stime += ERTS_USEC_TO_MONOTONIC(tmp_tv.tv_usec); + stime = erts_os_system_time(); mtime = stime - time_sup.inf.c.not_corrected_moffset; @@ -820,7 +811,7 @@ int erts_check_time_adj_support(int time_correction, /* User wants time correction */ #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT - return !time_sup.r.o.os_monotonic_disable; + return !time_sup.r.o.os_monotonic_time_disable; #else return 0; #endif @@ -849,24 +840,33 @@ void erts_init_sys_time_sup(void) #endif #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT - time_sup.r.o.os_monotonic_disable - = !sys_init_time_res.have_os_monotonic; - time_sup.r.o.os_monotonic_func - = sys_init_time_res.os_monotonic_info.func; - time_sup.r.o.os_monotonic_clock_id - = sys_init_time_res.os_monotonic_info.clock_id; - time_sup.r.o.os_monotonic_locked - = sys_init_time_res.os_monotonic_info.locked_use; - time_sup.r.o.os_monotonic_resolution - = sys_init_time_res.os_monotonic_info.resolution; - time_sup.r.o.os_monotonic_extended - = sys_init_time_res.os_monotonic_info.extended; + time_sup.r.o.os_monotonic_time_disable + = !sys_init_time_res.have_os_monotonic_time; + time_sup.r.o.os_monotonic_time_func + = sys_init_time_res.os_monotonic_time_info.func; + time_sup.r.o.os_monotonic_time_clock_id + = sys_init_time_res.os_monotonic_time_info.clock_id; + time_sup.r.o.os_monotonic_time_locked + = sys_init_time_res.os_monotonic_time_info.locked_use; + time_sup.r.o.os_monotonic_time_resolution + = sys_init_time_res.os_monotonic_time_info.resolution; + time_sup.r.o.os_monotonic_time_extended + = sys_init_time_res.os_monotonic_time_info.extended; + time_sup.r.o.os_system_time_func + = sys_init_time_res.os_system_time_info.func; + time_sup.r.o.os_system_time_clock_id + = sys_init_time_res.os_system_time_info.clock_id; + time_sup.r.o.os_system_time_locked + = sys_init_time_res.os_system_time_info.locked_use; + time_sup.r.o.os_system_time_resolution + = sys_init_time_res.os_system_time_info.resolution; #endif } int erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) { + ErtsMonotonicTime resolution; #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT ErtsMonotonicTime abs_native_offset, native_offset; #endif @@ -935,11 +935,15 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) #endif + resolution = time_sup.r.o.os_monotonic_time_resolution; + if (resolution > time_sup.r.o.os_system_time_resolution) + resolution = time_sup.r.o.os_system_time_resolution; + time_sup.r.o.adj.large_diff = erts_time_sup__.r.o.monotonic_time_unit; time_sup.r.o.adj.large_diff *= 50; - time_sup.r.o.adj.large_diff /= time_sup.r.o.os_monotonic_resolution; - if (time_sup.r.o.adj.large_diff < ERTS_MSEC_TO_MONOTONIC(5)) - time_sup.r.o.adj.large_diff = ERTS_MSEC_TO_MONOTONIC(5); + time_sup.r.o.adj.large_diff /= resolution; + if (time_sup.r.o.adj.large_diff < ERTS_USEC_TO_MONOTONIC(500)) + time_sup.r.o.adj.large_diff = ERTS_USEC_TO_MONOTONIC(500); time_sup.r.o.adj.small_diff = time_sup.r.o.adj.large_diff/10; #ifdef ERTS_TIME_CORRECTION_PRINT @@ -961,7 +965,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) #ifndef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT time_sup.r.o.correction = 0; #else - if (time_sup.r.o.os_monotonic_disable) + if (time_sup.r.o.os_monotonic_time_disable) time_sup.r.o.correction = 0; if (time_sup.r.o.correction) { @@ -969,10 +973,9 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) erts_smp_rwmtx_opt_t rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; ErtsMonotonicTime offset; time_sup.inf.c.minit = erts_os_monotonic_time(); - sys_gettimeofday(&time_sup.inf.c.inittv); + time_sup.inf.c.sinit = erts_os_system_time(); time_sup.r.o.moffset = -1*time_sup.inf.c.minit; - offset = ERTS_SEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_sec); - offset += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); + offset = time_sup.inf.c.sinit; offset -= ERTS_MONOTONIC_TIME_UNIT; init_time_offset(offset); @@ -985,10 +988,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) cdatap = &time_sup.inf.c.parmon.cdata; #ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC - cdatap->drift.intervals[0].time.sys - = ERTS_SEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_sec); - cdatap->drift.intervals[0].time.sys - += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); + cdatap->drift.intervals[0].time.sys = time_sup.inf.c.sinit; cdatap->drift.intervals[0].time.mon = time_sup.inf.c.minit; cdatap->curr.correction.drift = 0; #endif @@ -1006,9 +1006,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) { ErtsMonotonicTime stime, offset; time_sup.r.o.get_time = get_not_corrected_time; - sys_gettimeofday(&time_sup.inf.c.inittv); - stime = ERTS_SEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_sec); - stime += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); + stime = time_sup.inf.c.sinit = erts_os_system_time(); offset = stime - ERTS_MONOTONIC_TIME_UNIT; time_sup.inf.c.not_corrected_moffset = offset; init_time_offset(offset); @@ -1085,17 +1083,12 @@ erts_finalize_time_offset(void) if (!time_sup.inf.c.finalized_offset) { ErtsMonotonicTime mtime, new_offset; - SysTimeval tv; #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT if (!time_sup.r.o.correction) #endif { - ErtsMonotonicTime stime; - sys_gettimeofday(&tv); - - stime = ERTS_SEC_TO_MONOTONIC(tv.tv_sec); - stime += ERTS_USEC_TO_MONOTONIC(tv.tv_usec); + ErtsMonotonicTime stime = erts_os_system_time(); mtime = stime - time_sup.inf.c.not_corrected_moffset; @@ -1114,11 +1107,9 @@ erts_finalize_time_offset(void) } #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT else { - mtime = finalize_corrected_time_offset(&tv); - new_offset = ERTS_SEC_TO_MONOTONIC(tv.tv_sec); - new_offset += ERTS_USEC_TO_MONOTONIC(tv.tv_usec); - new_offset -= mtime; - + ErtsSystemTime stime; + mtime = finalize_corrected_time_offset(&stime); + new_offset = stime - mtime; } #endif new_offset = ERTS_MONOTONIC_TO_USEC(new_offset); @@ -1613,13 +1604,16 @@ erts_get_monotonic_time(void) void get_sys_now(Uint* megasec, Uint* sec, Uint* microsec) { - SysTimeval now; - - sys_gettimeofday(&now); - - *megasec = (Uint) (now.tv_sec / 1000000); - *sec = (Uint) (now.tv_sec % 1000000); - *microsec = (Uint) (now.tv_usec); + ErtsSystemTime stime = erts_os_system_time(); + ErtsSystemTime ms, s, us; + + us = ERTS_MONOTONIC_TO_USEC(stime); + s = us / (1000*1000); + ms = s / (1000*1000); + + *megasec = (Uint) ms; + *sec = (Uint) (s - ms*(1000*1000)); + *microsec = (Uint) (us - s*(1000*1000)); } #ifdef HAVE_ERTS_NOW_CPU @@ -1848,25 +1842,28 @@ bld_monotonic_time_source(Uint **hpp, Uint *szp, Sint64 os_mtime) Eterm k[6]; Eterm v[6]; - if (time_sup.r.o.os_monotonic_disable) + if (time_sup.r.o.os_monotonic_time_disable) return NIL; k[i] = erts_bld_atom(hpp, szp, "function"); - v[i++] = erts_bld_atom(hpp, szp, time_sup.r.o.os_monotonic_func); + v[i++] = erts_bld_atom(hpp, szp, + time_sup.r.o.os_monotonic_time_func); - if (time_sup.r.o.os_monotonic_clock_id) { + if (time_sup.r.o.os_monotonic_time_clock_id) { k[i] = erts_bld_atom(hpp, szp, "clock_id"); - v[i++] = erts_bld_atom(hpp, szp, time_sup.r.o.os_monotonic_clock_id); + v[i++] = erts_bld_atom(hpp, szp, + time_sup.r.o.os_monotonic_time_clock_id); } k[i] = erts_bld_atom(hpp, szp, "resolution"); - v[i++] = erts_bld_uint64(hpp, szp, time_sup.r.o.os_monotonic_resolution); + v[i++] = erts_bld_uint64(hpp, szp, + time_sup.r.o.os_monotonic_time_resolution); k[i] = erts_bld_atom(hpp, szp, "extended"); - v[i++] = time_sup.r.o.os_monotonic_extended ? am_yes : am_no; + v[i++] = time_sup.r.o.os_monotonic_time_extended ? am_yes : am_no; k[i] = erts_bld_atom(hpp, szp, "parallel"); - v[i++] = time_sup.r.o.os_monotonic_locked ? am_no : am_yes; + v[i++] = time_sup.r.o.os_monotonic_time_locked ? am_no : am_yes; k[i] = erts_bld_atom(hpp, szp, "time"); v[i++] = erts_bld_sint64(hpp, szp, os_mtime); @@ -1882,7 +1879,7 @@ erts_monotonic_time_source(struct process *c_p) Eterm *hp = NULL; Sint64 os_mtime = 0; #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT - if (!time_sup.r.o.os_monotonic_disable) + if (!time_sup.r.o.os_monotonic_time_disable) os_mtime = (Sint64) erts_os_monotonic_time(); #endif @@ -1892,6 +1889,49 @@ erts_monotonic_time_source(struct process *c_p) return bld_monotonic_time_source(&hp, NULL, os_mtime); } +static Eterm +bld_system_time_source(Uint **hpp, Uint *szp, Sint64 os_stime) +{ + int i = 0; + Eterm k[5]; + Eterm v[5]; + + k[i] = erts_bld_atom(hpp, szp, "function"); + v[i++] = erts_bld_atom(hpp, szp, + time_sup.r.o.os_system_time_func); + + if (time_sup.r.o.os_system_time_clock_id) { + k[i] = erts_bld_atom(hpp, szp, "clock_id"); + v[i++] = erts_bld_atom(hpp, szp, + time_sup.r.o.os_system_time_clock_id); + } + + k[i] = erts_bld_atom(hpp, szp, "resolution"); + v[i++] = erts_bld_uint64(hpp, szp, + time_sup.r.o.os_system_time_resolution); + + k[i] = erts_bld_atom(hpp, szp, "parallel"); + v[i++] = am_yes; + + k[i] = erts_bld_atom(hpp, szp, "time"); + v[i++] = erts_bld_sint64(hpp, szp, os_stime); + + return erts_bld_2tup_list(hpp, szp, (Sint) i, k, v); +} + +Eterm +erts_system_time_source(struct process *c_p) +{ + Uint hsz = 0; + Eterm *hp = NULL; + Sint64 os_stime = (Sint64) erts_os_system_time(); + + bld_system_time_source(NULL, &hsz, os_stime); + if (hsz) + hp = HAlloc(c_p, hsz); + return bld_system_time_source(&hp, NULL, os_stime); +} + #include "bif.h" @@ -2066,21 +2106,13 @@ BIF_RETTYPE timestamp_0(BIF_ALIST_0) BIF_RETTYPE os_system_time_0(BIF_ALIST_0) { - ErtsMonotonicTime stime; - SysTimeval tod; - sys_gettimeofday(&tod); - stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec); - stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec); + ErtsSystemTime stime = erts_os_system_time(); BIF_RET(make_time_val(BIF_P, stime)); } BIF_RETTYPE os_system_time_1(BIF_ALIST_0) { - ErtsMonotonicTime stime; - SysTimeval tod; - sys_gettimeofday(&tod); - stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec); - stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec); + ErtsSystemTime stime = erts_os_system_time(); BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, stime, 0)); } diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index c51b665db9..27a55e0ec7 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -684,7 +684,7 @@ typedef enum { } ErtsTimeWarpMode; typedef struct { - int have_os_monotonic; + int have_os_monotonic_time; ErtsMonotonicTime os_monotonic_time_unit; ErtsMonotonicTime sys_clock_resolution; struct { @@ -693,7 +693,13 @@ typedef struct { char *clock_id; int locked_use; int extended; - } os_monotonic_info; + } os_monotonic_time_info; + struct { + Uint64 resolution; + char *func; + char *clock_id; + int locked_use; + } os_system_time_info; } ErtsSysInitTimeResult; #define ERTS_SYS_INIT_TIME_RESULT_INITER \ diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index a14a4b3eae..5394e94c8c 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -169,6 +169,10 @@ typedef long long ErtsSysHrTime; #error No signed 64-bit type found... #endif +typedef ErtsMonotonicTime ErtsSystemTime; + +ErtsSystemTime erts_os_system_time(void); + #define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63) #define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN) diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index 134e3a67b0..d6591a8296 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -150,44 +150,46 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) { #if !defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) - init_resp->have_os_monotonic = 0; + init_resp->have_os_monotonic_time = 0; #else /* defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) */ int major, minor, build, vsn; - init_resp->os_monotonic_info.resolution = (Uint64) 1000*1000*1000; + init_resp->os_monotonic_time_info.resolution = (Uint64) 1000*1000*1000; #if defined(HAVE_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID) { struct timespec ts; - if (clock_getres(MONOTONIC_CLOCK_ID, &ts) == 0 - && ts.tv_sec == 0 && ts.tv_nsec != 0) { - init_resp->os_monotonic_info.resolution /= ts.tv_nsec; + if (clock_getres(MONOTONIC_CLOCK_ID, &ts) == 0) { + if (ts.tv_sec == 0 && ts.tv_nsec != 0) + init_resp->os_monotonic_time_info.resolution /= ts.tv_nsec; + else if (ts.tv_sec >= 1) + init_resp->os_monotonic_time_info.resolution = 1; } } #endif #ifdef MONOTONIC_CLOCK_ID_STR - init_resp->os_monotonic_info.clock_id = MONOTONIC_CLOCK_ID_STR; + init_resp->os_monotonic_time_info.clock_id = MONOTONIC_CLOCK_ID_STR; #else - init_resp->os_monotonic_info.clock_id = NULL; + init_resp->os_monotonic_time_info.clock_id = NULL; #endif - init_resp->os_monotonic_info.locked_use = 0; + init_resp->os_monotonic_time_info.locked_use = 0; #if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) - init_resp->os_monotonic_info.func = "clock_gettime"; + init_resp->os_monotonic_time_info.func = "clock_gettime"; #elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) - init_resp->os_monotonic_info.func = "clock_get_time"; + init_resp->os_monotonic_time_info.func = "clock_get_time"; #elif defined(OS_MONOTONIC_TIME_USING_GETHRTIME) - init_resp->os_monotonic_info.func = "gethrtime"; + init_resp->os_monotonic_time_info.func = "gethrtime"; #elif defined(OS_MONOTONIC_TIME_USING_TIMES) - init_resp->os_monotonic_info.func = "times"; + init_resp->os_monotonic_time_info.func = "times"; #else # error Unknown erts_os_monotonic_time() implementation #endif - init_resp->have_os_monotonic = 1; + init_resp->have_os_monotonic_time = 1; os_version(&major, &minor, &build); @@ -210,7 +212,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) "os_monotonic_time"); internal_state.w.f.last_delivered = clock_gettime_monotonic_raw(); - init_resp->os_monotonic_info.locked_use = 1; + init_resp->os_monotonic_time_info.locked_use = 1; } #else /* !(defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)) */ { @@ -227,7 +229,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) if (sysconf(_SC_NPROCESSORS_CONF) > 1) #endif - init_resp->have_os_monotonic = 0; + init_resp->have_os_monotonic_time = 0; } } } @@ -261,9 +263,9 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) shift++; } - init_resp->os_monotonic_info.resolution = resolution; + init_resp->os_monotonic_time_info.resolution = resolution; init_resp->os_monotonic_time_unit = time_unit; - init_resp->os_monotonic_info.extended = 1; + init_resp->os_monotonic_time_info.extended = 1; internal_state.r.o.times_shift = shift; erts_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd, @@ -272,6 +274,38 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) } #endif /* defined(OS_MONOTONIC_TIME_USING_TIMES) */ +#ifdef WALL_CLOCK_ID_STR + init_resp->os_system_time_info.clock_id = WALL_CLOCK_ID_STR; +#else + init_resp->os_system_time_info.clock_id = NULL; +#endif + + init_resp->os_system_time_info.locked_use = 0; + init_resp->os_system_time_info.resolution = (Uint64) 1000*1000*1000; +#if defined(HAVE_CLOCK_GETRES) && defined(WALL_CLOCK_ID) + { + struct timespec ts; + if (clock_getres(WALL_CLOCK_ID, &ts) == 0) { + if (ts.tv_sec == 0 && ts.tv_nsec != 0) + init_resp->os_system_time_info.resolution /= ts.tv_nsec; + else if (ts.tv_sec >= 1) + init_resp->os_system_time_info.resolution = 1; + } + } +#endif + +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) + init_resp->os_system_time_info.func = "clock_gettime"; +#elif defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) + init_resp->os_system_time_info.func = "clock_get_time"; +#elif defined(OS_SYSTEM_TIME_GETTIMEOFDAY) + init_resp->os_system_time_info.func = "gettimeofday"; + init_resp->os_system_time_info.resolution = 1000*1000; + init_resp->os_system_time_info.clock_id = NULL; +#else +# error Missing erts_os_system_time() implmenentation +#endif + } void @@ -282,6 +316,105 @@ erts_late_sys_init_time(void) #endif } +static ERTS_INLINE ErtsSystemTime +adj_stime_time_unit(ErtsSystemTime stime, Uint32 res) +{ + if (res == ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT) + return stime; + if (res == (Uint32) 1000*1000*1000 + && ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000*1000) + return stime/1000; + if (res == (Uint32) 1000*1000 + && ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000*1000*1000) + return stime*1000; + return ((ErtsSystemTime) + erts_time_unit_conversion(stime, + (Uint32) res, + (Uint32) ERTS_MONOTONIC_TIME_UNIT)); +} + +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) + +ErtsSystemTime +erts_os_system_time(void) +{ + ErtsSystemTime stime; + struct timespec ts; + + if (clock_gettime(WALL_CLOCK_ID,&ts) != 0) { + int err = errno; + char *errstr = err ? strerror(err) : "unknown"; + erl_exit(ERTS_ABORT_EXIT, + "clock_gettime(%s, _) failed: %s (%d)\n", + WALL_CLOCK_ID_STR, errstr, err); + + } + + stime = (ErtsSystemTime) ts.tv_sec; + stime *= (ErtsSystemTime) 1000*1000*1000; + stime += (ErtsSystemTime) ts.tv_nsec; + return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000); +} + +#elif defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) + +ErtsSystemTime +erts_os_system_time(void) +{ + ErtsSystemTime stime; + kern_return_t res; + clock_serv_t clk_srv; + mach_timespec_t time_spec; + int err; + + host_get_clock_service(mach_host_self(), + WALL_CLOCK_ID, + &clk_srv); + errno = 0; + res = clock_get_time(clk_srv, &time_spec); + err = errno; + mach_port_deallocate(mach_task_self(), clk_srv); + if (res != KERN_SUCCESS) { + char *errstr = err ? strerror(err) : "unknown"; + erl_exit(ERTS_ABORT_EXIT, + "clock_get_time(%s, _) failed: %s (%d)\n", + MONOTONIC_CLOCK_ID_STR, errstr, err); + } + + stime = (ErtsSystemTime) time_spec.tv_sec; + stime *= (ErtsSystemTime) 1000*1000*1000; + stime += (ErtsSystemTime) time_spec.tv_nsec; + + return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000); +} + +#elif defined(OS_SYSTEM_TIME_GETTIMEOFDAY) + +ErtsSystemTime +erts_os_system_time(void) +{ + ErtsSystemTime stime; + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) { + int err = errno; + char *errstr = err ? strerror(err) : "unknown"; + erl_exit(ERTS_ABORT_EXIT, + "gettimeofday(_, NULL) failed: %s (%d)\n", + errstr, err); + } + + stime = (ErtsSystemTime) tv.tv_sec; + stime *= (ErtsSystemTime) 1000*1000; + stime += (ErtsSystemTime) tv.tv_usec; + + return adj_stime_time_unit(stime, (Uint32) 1000*1000); +} + +#else +# error Missing erts_os_system_time() implmenentation +#endif + #if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) static ERTS_INLINE ErtsMonotonicTime diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index febf3b5cca..d214ba002c 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -181,6 +181,10 @@ typedef LONGLONG ErtsMonotonicTime; typedef LONGLONG ErtsSysHrTime; #endif +typedef ErtsMonotonicTime ErtsSystemTime; + +ErtsSystemTime erts_os_system_time(void); + #define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63) #define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN) diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index ea180f067f..da9c4d2e29 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -73,6 +73,8 @@ static int days_in_month[2][13] = { {0,31,28,31,30,31,30,31,31,30,31,30,31}, {0,31,29,31,30,31,30,31,31,30,31,30,31}}; +#define ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT 10 + /* * erts_os_monotonic_time() */ @@ -81,6 +83,7 @@ struct sys_time_internal_state_read_only__ { ULONGLONG (WINAPI *pGetTickCount64)(void); BOOL (WINAPI *pQueryPerformanceCounter)(LARGE_INTEGER *); Sint32 pcf; + int using_get_tick_count_time_unit; }; struct sys_time_internal_state_read_mostly__ { @@ -199,7 +202,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) char kernel_dll_name[] = "kernel32"; HMODULE module; - init_resp->os_monotonic_info.clock_id = NULL; + init_resp->os_monotonic_time_info.clock_id = NULL; module = GetModuleHandle(kernel_dll_name); if (!module) { @@ -209,13 +212,15 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) internal_state.w.f.wrap = 0; internal_state.w.f.last_tick_count = 0; - init_resp->os_monotonic_info.func = "GetTickCount"; - init_resp->os_monotonic_info.locked_use = 1; + init_resp->os_monotonic_time_info.func = "GetTickCount"; + init_resp->os_monotonic_time_info.locked_use = 1; /* 10-16 ms resolution according to MicroSoft documentation */ - init_resp->os_monotonic_info.resolution = 100; /* 10 ms */ - time_unit = (ErtsMonotonicTime) (1000 << 10); + init_resp->os_monotonic_time_info.resolution = 100; /* 10 ms */ + time_unit = (ErtsMonotonicTime) 1000; + time_unit <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; + internal_state.r.o.using_get_tick_count_time_unit = 1; os_mtime_func = os_monotonic_time_gtc32; - init_resp->os_monotonic_info.extended = 1; + init_resp->os_monotonic_time_info.extended = 1; erts_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd, get_tick_count, 60*60*24*7); /* Check once a week */ @@ -237,11 +242,13 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) if (!internal_state.r.o.pGetTickCount64) goto get_tick_count; - init_resp->os_monotonic_info.func = "GetTickCount64"; - init_resp->os_monotonic_info.locked_use = 0; + init_resp->os_monotonic_time_info.func = "GetTickCount64"; + init_resp->os_monotonic_time_info.locked_use = 0; /* 10-16 ms resolution according to MicroSoft documentation */ - init_resp->os_monotonic_info.resolution = 100; /* 10 ms */ - time_unit = (ErtsMonotonicTime) (1000 << 10); + init_resp->os_monotonic_time_info.resolution = 100; /* 10 ms */ + time_unit = (ErtsMonotonicTime) 1000; + time_unit <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; + internal_state.r.o.using_get_tick_count_time_unit = 1; os_mtime_func = os_monotonic_time_gtc64; if (!sys_hrtime_func) sys_hrtime_func = sys_hrtime_gtc64; @@ -279,25 +286,30 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) if (pf.QuadPart < (LONGLONG) 1000*1000*1000) goto get_tick_count64; - init_resp->os_monotonic_info.func = "QueryPerformanceCounter"; - init_resp->os_monotonic_info.locked_use = 0; + init_resp->os_monotonic_time_info.func = "QueryPerformanceCounter"; + init_resp->os_monotonic_time_info.locked_use = 0; time_unit = (ErtsMonotonicTime) pf.QuadPart; - init_resp->os_monotonic_info.resolution = time_unit; + internal_state.r.o.using_get_tick_count_time_unit = 0; + init_resp->os_monotonic_time_info.resolution = time_unit; os_mtime_func = os_monotonic_time_qpc; } } erts_sys_time_data__.r.o.os_monotonic_time = os_mtime_func; init_resp->os_monotonic_time_unit = time_unit; - init_resp->have_os_monotonic = 1; + init_resp->have_os_monotonic_time = 1; init_resp->sys_clock_resolution = 1; + init_resp->os_system_time_info.func = "GetSystemTime"; + init_resp->os_system_time_info.clock_id = NULL; + init_resp->os_system_time_info.resolution = 100; + init_resp->os_system_time_info.locked_use = 0; + if(GetTimeZoneInformation(&static_tzi) && static_tzi.StandardDate.wMonth != 0 && static_tzi.DaylightDate.wMonth != 0) { have_static_tzi = 1; } - } void @@ -585,6 +597,37 @@ sys_gettimeofday(SysTimeval *tv) EPOCH_JULIAN_DIFF); } +ErtsSystemTime +erts_os_system_time(void) +{ + SYSTEMTIME t; + FILETIME ft; + ULARGE_INTEGER ull; + ErtsSystemTime stime; + + GetSystemTime(&t); + SystemTimeToFileTime(&t, &ft); + FILETIME_TO_ULI(ull,ft); + + /* now in 100 ns units */ + + stime = (ErtsSystemTime) ull.QuadPart; + stime -= (((ErtsSystemTime) EPOCH_JULIAN_DIFF) + * ((ErtsSystemTime) (10*1000*1000))); + stime /= (ErtsSystemTime) (10*1000); + + if (internal_state.r.o.using_get_tick_count_time_unit) { + stime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; + return stime; + } + + return ((ErtsSystemTime) + erts_time_unit_conversion(stime, + (Uint32) 1000, + (Uint32) ERTS_MONOTONIC_TIME_UNIT)); +} + + clock_t sys_times(SysTimes *buffer) { clock_t kernel_ticks = (GetTickCount() / diff --git a/erts/example/time_compat.erl b/erts/example/time_compat.erl index d582117ceb..90b7fbcc80 100644 --- a/erts/example/time_compat.erl +++ b/erts/example/time_compat.erl @@ -240,6 +240,7 @@ system_info(Item) -> time_offset -> final; NotSupArg when NotSupArg == os_monotonic_time_source; + NotSupArg == os_system_time_source; NotSupArg == start_time -> %% Cannot emulate this... erlang:error(notsup, [NotSupArg]); diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 303f3f47b6..fcfcafa6da 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 83010b17d2..c5dc40f5d1 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2531,6 +2531,7 @@ tuple_to_list(_Tuple) -> (nif_version) -> string(); (otp_release) -> string(); (os_monotonic_time_source) -> [{atom(),term()}]; + (os_system_time_source) -> [{atom(),term()}]; (port_count) -> non_neg_integer(); (port_limit) -> pos_integer(); (process_count) -> pos_integer(); -- cgit v1.2.3 From a1520d8bd2b467d5128998a5069611b9e6252653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 24 Mar 2015 17:05:58 +0100 Subject: erts: Fix comparison of exact terms Comparison of exact terms could cause faulty term tests. This was caused by a faulty (too small) internal type. Symptom: -1 = erts_internal:cmp_term(2147483648,0). %% wrong Correct: 1 = erts_internal:cmp_term(2147483648,0). Reported-by: Jesper Louis Andersen --- erts/emulator/beam/bif.c | 2 +- erts/emulator/beam/erl_map.c | 4 ++-- erts/emulator/test/map_SUITE.erl | 45 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 42dd160e38..5ac74032fa 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4659,7 +4659,7 @@ BIF_RETTYPE bump_reductions_1(BIF_ALIST_1) } BIF_RETTYPE erts_internal_cmp_term_2(BIF_ALIST_2) { - int res = CMP_TERM(BIF_ARG_1,BIF_ARG_2); + Sint res = CMP_TERM(BIF_ARG_1,BIF_ARG_2); /* ensure -1, 0, 1 result */ if (res < 0) { diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 5e740aacdd..a180047f6c 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -400,7 +400,7 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { Eterm *ks,*vs,*ks1,*vs1,*ks2,*vs2; map_t *mp1,*mp2,*mp_new; Uint n1,n2,i1,i2,need,unused_size=0; - int c = 0; + Sint c = 0; mp1 = (map_t*)map_val(BIF_ARG_1); mp2 = (map_t*)map_val(BIF_ARG_2); @@ -798,7 +798,7 @@ int erts_validate_and_sort_map(map_t* mp) Uint sz = map_get_size(mp); Uint ix,jx; Eterm tmp; - int c; + Sint c; /* sort */ diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 888ed8e272..e877f7a240 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -58,6 +58,7 @@ t_maps_without/1, %% misc + t_erts_internal_order/1, t_pdict/1, t_ets/1, t_dets/1, @@ -96,6 +97,7 @@ all() -> [ %% Other functions + t_erts_internal_order, t_pdict, t_ets, t_tracing @@ -960,6 +962,49 @@ t_maps_without(_Config) -> %% MISC + +t_erts_internal_order(_Config) when is_list(_Config) -> + + -1 = erts_internal:cmp_term(1,2), + 1 = erts_internal:cmp_term(2,1), + 0 = erts_internal:cmp_term(2,2), + + + -1 = erts_internal:cmp_term(1,a), + 1 = erts_internal:cmp_term(a,1), + 0 = erts_internal:cmp_term(a,a), + + -1 = erts_internal:cmp_term(1,1.0), + 1 = erts_internal:cmp_term(1.0,1), + 0 = erts_internal:cmp_term(1.0,1.0), + + -1 = erts_internal:cmp_term(1,1 bsl 65), + 1 = erts_internal:cmp_term(1 bsl 65,1), + 0 = erts_internal:cmp_term(1 bsl 65, 1 bsl 65), + + -1 = erts_internal:cmp_term(1 bsl 65,float(1)), + 1 = erts_internal:cmp_term(float(1),1 bsl 65), + -1 = erts_internal:cmp_term(1,float(1 bsl 65)), + 1 = erts_internal:cmp_term(float(1 bsl 65),1), + 0 = erts_internal:cmp_term(float(1 bsl 65), float(1 bsl 65)), + + %% reported errors + -1 = erts_internal:cmp_term(0,2147483648), + 0 = erts_internal:cmp_term(2147483648,2147483648), + 1 = erts_internal:cmp_term(2147483648,0), + + M = #{0 => 0,2147483648 => 0}, + true = M =:= binary_to_term(term_to_binary(M)), + + F1 = fun(_, _) -> 0 end, + F2 = fun(_, _) -> 1 end, + M0 = maps:from_list( [{-2147483649, 0}, {0,0}, {97, 0}, {false, 0}, {flower, 0}, {F1, 0}, {F2, 0}, {<<>>, 0}]), + M1 = maps:merge(M0, #{0 => 1}), + 8 = maps:size(M1), + 1 = maps:get(0,M1), + ok. + + t_pdict(_Config) -> put(#{ a => b, b => a},#{ c => d}), -- cgit v1.2.3 From 1f23b603f022c7345dd573bf917926a575ae030d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 24 Mar 2015 20:01:11 +0100 Subject: erts: Fix bug in binary_to_term for hamt when yielding Must save hamt_list in context. --- erts/emulator/beam/external.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 82c60840e5..c99b60ed09 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3878,6 +3878,7 @@ dec_term_atom_common: ctx->u.dc.next = next; ctx->u.dc.hp = hp; ctx->u.dc.maps_list = maps_list; + ctx->u.dc.hamt_list = hamt_list; ctx->reds = 0; return NULL; } -- cgit v1.2.3 From db54eaa94562b49c81b677948a8e9139ebdb010e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 24 Mar 2015 15:27:11 +0100 Subject: erts: Remove HAMT_SUBTAG_NODE_ARRAY This will also fix a bug in term_to_binary treating full nodes as tuples and emiting LIST_EXT for leafs. --- erts/emulator/beam/erl_map.c | 88 ++++++---------------------------------- erts/emulator/beam/erl_map.h | 6 --- erts/emulator/beam/external.c | 6 +-- erts/emulator/beam/utils.c | 5 +-- erts/emulator/test/map_SUITE.erl | 14 +++++++ 5 files changed, 29 insertions(+), 90 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 26233a98c6..ef806c2cfa 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -775,7 +775,7 @@ static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, sz = hashmap_bitcount(hdr); hp = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA); nhp = hp; - *hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr); + *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hdr); *hp++ = res; sz--; while (sz--) { *hp++ = ESTACK_POP(stack); } ASSERT((hp - nhp) < 18); @@ -824,7 +824,7 @@ static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, sz = hashmap_bitcount(hdr); hp = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA); nhp = hp; - *hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr); + *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hdr); *hp++ = res; sz--; while (sz--) { *hp++ = ESTACK_POP(stack); } @@ -846,7 +846,7 @@ static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, *hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_HEAD_ARRAY : MAP_HEADER_HAMT_HEAD_BITMAP(hdr); *hp++ = n; } else { - *hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_NODE_ARRAY : MAP_HEADER_HAMT_NODE_BITMAP(hdr); + *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hdr); } *hp++ = res; sz--; @@ -1163,8 +1163,8 @@ recurse: sp->abm = 1 << hashmap_index(ahx); sp->srcA = &nodeA; switch(hdrB & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++; - case HAMT_SUBTAG_NODE_ARRAY: + case HAMT_SUBTAG_HEAD_ARRAY: + sp->srcB++; sp->bbm = 0xffff; break; @@ -1189,16 +1189,16 @@ recurse: hdrA = *sp->srcA++; ASSERT(is_header(hdrA)); switch (hdrA & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_ARRAY: sp->srcA++; - case HAMT_SUBTAG_NODE_ARRAY: { + case HAMT_SUBTAG_HEAD_ARRAY: { + sp->srcA++; ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED); sp->abm = 0xffff; sp->srcB = boxed_val(nodeB); hdrB = *sp->srcB++; ASSERT(is_header(hdrB)); switch (hdrB & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++; - case HAMT_SUBTAG_NODE_ARRAY: + case HAMT_SUBTAG_HEAD_ARRAY: + sp->srcB++; sp->bbm = 0xffff; break; case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++; @@ -1218,8 +1218,8 @@ recurse: hdrB = *sp->srcB++; ASSERT(is_header(hdrB)); switch (hdrB & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++; - case HAMT_SUBTAG_NODE_ARRAY: + case HAMT_SUBTAG_HEAD_ARRAY: + sp->srcB++; sp->bbm = 0xffff; break; case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++; @@ -1296,8 +1296,7 @@ recurse: } else { nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(sp->ix), HALLOC_EXTRA); hp = nhp; - *hp++ = (sp->ix == 16 ? make_arityval(16) - : MAP_HEADER_HAMT_NODE_BITMAP(sp->abm | sp->bbm)); + *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(sp->abm | sp->bbm); } memcpy(hp, sp->array, sp->ix * sizeof(Eterm)); res = make_boxed(nhp); @@ -1748,7 +1747,6 @@ Eterm* hashmap_iterator_next(ErtsWStack* s) { switch(hdr & _HEADER_MAP_SUBTAG_MASK) { case HAMT_SUBTAG_HEAD_ARRAY: ptr++; - case HAMT_SUBTAG_NODE_ARRAY: sz = 16; break; case HAMT_SUBTAG_HEAD_BITMAP: @@ -1799,7 +1797,6 @@ Eterm* hashmap_iterator_prev(ErtsWStack* s) { switch(hdr & _HEADER_MAP_SUBTAG_MASK) { case HAMT_SUBTAG_HEAD_ARRAY: ptr++; - case HAMT_SUBTAG_NODE_ARRAY: sz = 16; break; case HAMT_SUBTAG_HEAD_BITMAP: @@ -1862,11 +1859,6 @@ erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) ASSERT(is_header(hdr)); switch(hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_NODE_ARRAY: - ix = hashmap_index(hx); - hx = hashmap_shift_hash(th,hx,lvl,key); - node = ptr[ix+1]; - break; case HAMT_SUBTAG_HEAD_ARRAY: ix = hashmap_index(hx); hx = hashmap_shift_hash(th,hx,lvl,key); @@ -1964,13 +1956,6 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, ASSERT(is_header(hdr)); switch(hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_NODE_ARRAY: - ix = hashmap_index(hx); - hx = hashmap_shift_hash(th,hx,lvl,key); - size += HAMT_NODE_ARRAY_SZ; - ESTACK_PUSH2(*sp, ix, node); - node = ptr[ix+1]; - break; case HAMT_SUBTAG_HEAD_ARRAY: ix = hashmap_index(hx); hx = hashmap_shift_hash(th,hx,lvl,key); @@ -2100,14 +2085,6 @@ Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, ASSERT(is_header(hdr)); switch(hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_NODE_ARRAY: - slot = (Uint) ESTACK_POP(*sp); - nhp = hp; - n = HAMT_NODE_ARRAY_SZ; - while(n--) { *hp++ = *ptr++; } - nhp[slot+1] = res; - res = make_hashmap(nhp); - break; case HAMT_SUBTAG_HEAD_ARRAY: slot = (Uint) ESTACK_POP(*sp); nhp = hp; @@ -2132,9 +2109,6 @@ Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, if (hval & bp) { ptr++; n--; } while(n--) { *hp++ = *ptr++; } - if ((hval | bp) == 0xffff) { - *nhp = make_arityval(16); - } res = make_hashmap(nhp); break; case HAMT_SUBTAG_HEAD_BITMAP: @@ -2230,13 +2204,6 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) { ASSERT(is_header(hdr)); switch(hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_NODE_ARRAY: - ix = hashmap_index(hx); - hx = hashmap_shift_hash(th,hx,lvl,key); - size += HAMT_NODE_ARRAY_SZ; - ESTACK_PUSH2(stack, ix, node); - node = ptr[ix+1]; - break; case HAMT_SUBTAG_HEAD_ARRAY: ix = hashmap_index(hx); hx = hashmap_shift_hash(th,hx,lvl,key); @@ -2351,24 +2318,6 @@ unroll: ASSERT(is_header(hdr)); switch(hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_NODE_ARRAY: - ix = (Uint) ESTACK_POP(stack); - nhp = hp; - if (res == THE_NON_VALUE) { - *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(0xffff ^ (1 << ix)); ptr++; - n = 16; - n -= ix; - while(ix--) { *hp++ = *ptr++; } - ptr++; n--; - while(n--) { *hp++ = *ptr++; } - res = make_hashmap(nhp); - } else { - n = HAMT_NODE_ARRAY_SZ; - while(n--) { *hp++ = *ptr++; } - nhp[ix+1] = res; - res = make_hashmap(nhp); - } - break; case HAMT_SUBTAG_HEAD_ARRAY: ix = (Uint) ESTACK_POP(stack); nhp = hp; @@ -2610,7 +2559,6 @@ BIF_RETTYPE erts_internal_map_type_1(BIF_ALIST_1) { case HAMT_SUBTAG_HEAD_ARRAY: case HAMT_SUBTAG_HEAD_BITMAP: BIF_RET(AM_hashmap); - case HAMT_SUBTAG_NODE_ARRAY: case HAMT_SUBTAG_NODE_BITMAP: BIF_RET(AM_hashmap_node); default: @@ -2637,10 +2585,6 @@ BIF_RETTYPE erts_internal_map_hashmap_children_1(BIF_ALIST_1) { ASSERT(is_header(hdr)); switch(hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_NODE_ARRAY: - sz = 16; - ptr += 1; - break; case HAMT_SUBTAG_NODE_BITMAP: sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); ptr += 1; @@ -2703,14 +2647,6 @@ static Eterm hashmap_info(Process *p, Eterm node) { hdr = *ptr; ASSERT(is_header(hdr)); switch(hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_NODE_ARRAY: - narray++; - sz = 16; - while(sz--) { - ESTACK_PUSH(stack, clvl + 1); - ESTACK_PUSH(stack, ptr[sz+1]); - } - break; case HAMT_SUBTAG_NODE_BITMAP: nbitmap++; sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 1333a734a8..9fc1a72b68 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -178,21 +178,15 @@ typedef struct hashmap_head_s { #define MAP_HEADER_HAMT_HEAD_BITMAP(Bmp) \ MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_HEAD_BITMAP,0x1,Bmp) -#define MAP_HEADER_HAMT_NODE_ARRAY \ - make_arityval(16) - #define MAP_HEADER_HAMT_NODE_BITMAP(Bmp) \ MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_NODE_BITMAP,0x0,Bmp) #define HAMT_HEAD_EMPTY_SZ (2) -#define HAMT_NODE_ARRAY_SZ (17) #define HAMT_HEAD_ARRAY_SZ (18) #define HAMT_NODE_BITMAP_SZ(n) (1 + n) #define HAMT_HEAD_BITMAP_SZ(n) (2 + n) #define _HEADER_MAP_SUBTAG_MASK (0xfc) /* 2 bits maps tag + 4 bits subtag + 2 ignore bits */ -/* SUBTAG_NODE_ARRAY is in fact a tuple with 16 elements */ -#define HAMT_SUBTAG_NODE_ARRAY (((16 << _HEADER_ARITY_OFFS) | ARITYVAL_SUBTAG) & _HEADER_MAP_SUBTAG_MASK) #define HAMT_SUBTAG_NODE_BITMAP ((MAP_HEADER_TAG_HAMT_NODE_BITMAP << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG) #define HAMT_SUBTAG_HEAD_ARRAY ((MAP_HEADER_TAG_HAMT_HEAD_ARRAY << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG) #define HAMT_SUBTAG_HEAD_BITMAP ((MAP_HEADER_TAG_HAMT_HEAD_BITMAP << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG) diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index c99b60ed09..b0b232f185 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2633,8 +2633,6 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, *ep++ = MAP_EXT; ptr++; put_int32(*ptr, ep); ep += 4; - /*fall through*/ - case HAMT_SUBTAG_NODE_ARRAY: node_sz = 16; break; case HAMT_SUBTAG_HEAD_BITMAP: @@ -4172,8 +4170,8 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, hdr = *ptr; ASSERT(is_header(hdr)); switch(hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_ARRAY: ptr++; - case HAMT_SUBTAG_NODE_ARRAY: + case HAMT_SUBTAG_HEAD_ARRAY: + ptr++; node_sz = 16; break; case HAMT_SUBTAG_HEAD_BITMAP: ptr++; diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 3549e18538..d98251addc 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1321,7 +1321,6 @@ make_hash2(Eterm term) } switch (hdr & _HEADER_MAP_SUBTAG_MASK) { case HAMT_SUBTAG_HEAD_ARRAY: - case HAMT_SUBTAG_NODE_ARRAY: i = 16; break; case HAMT_SUBTAG_HEAD_BITMAP: @@ -1724,7 +1723,6 @@ make_internal_hash(Eterm term) } switch (hdr & _HEADER_MAP_SUBTAG_MASK) { case HAMT_SUBTAG_HEAD_ARRAY: - case HAMT_SUBTAG_NODE_ARRAY: i = 16; break; case HAMT_SUBTAG_HEAD_BITMAP: @@ -2797,14 +2795,13 @@ tailrecur_ne: switch (hdr & _HEADER_MAP_SUBTAG_MASK) { case HAMT_SUBTAG_HEAD_ARRAY: aa++; bb++; - case HAMT_SUBTAG_NODE_ARRAY: sz = 16; break; case HAMT_SUBTAG_HEAD_BITMAP: aa++; bb++; case HAMT_SUBTAG_NODE_BITMAP: sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); - ASSERT(sz > 0 && sz < 16); + ASSERT(sz > 0 && sz < 17); break; default: erl_exit(1, "Unknown hashmap subsubtag\n"); diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 1da08beb8b..fea327445f 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -1149,6 +1149,20 @@ t_map_encode_decode(Config) when is_list(Config) -> 97,55 % 55 :: integer() >>), + %% Maps of different sizes + lists:foldl(fun(Key, M0) -> + M1 = M0#{Key => Key}, + case Key rem 17 of + 0 -> + M1 = binary_to_term(term_to_binary(M1)); + _ -> + ok + end, + M1 + end, + #{}, + lists:seq(1,10000)), + %% many maps in same binary MapList = lists:foldl(fun(K, [M|_]=Acc) -> [M#{K => K} | Acc] end, [#{}], -- cgit v1.2.3 From 8d31ecea8b68ef6e16d7d77c0160e36f078b98de Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 24 Mar 2015 15:03:46 +0100 Subject: erts: Optimize hashmap_get --- erts/emulator/beam/erl_map.c | 96 +++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 60 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index ef806c2cfa..3852366876 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -1837,75 +1837,51 @@ erts_hashmap_get_rel(Uint32 hx, Eterm key, Eterm node, Eterm *map_base) erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) #endif { - Eterm *ptr, hdr; - Uint ix,slot, lvl = 0; + Eterm *ptr, hdr, *res; + Uint ix, lvl = 0; Uint32 hval,bp; DeclareTmpHeapNoproc(th,2); UseTmpHeapNoproc(2); + ASSERT(is_boxed(node)); + ptr = boxed_val(node); + hdr = *ptr; + ASSERT(is_header(hdr)); + ASSERT(is_hashmap_header_head(hdr)); + ptr++; + for (;;) { - switch(primary_tag(node)) { - case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */ - ptr = list_val(node); - UnUseTmpHeapNoproc(2); - - if (eq_rel(CAR(ptr), map_base, key, NULL)) { - return &(CDR(ptr)); - } - return NULL; - case TAG_PRIMARY_BOXED: - ptr = boxed_val(node); - hdr = *ptr; - ASSERT(is_header(hdr)); - - switch(hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_ARRAY: - ix = hashmap_index(hx); - hx = hashmap_shift_hash(th,hx,lvl,key); - node = ptr[ix+2]; - break; - case HAMT_SUBTAG_NODE_BITMAP: - hval = MAP_HEADER_VAL(hdr); - ix = hashmap_index(hx); - bp = 1 << ix; - slot = hashmap_bitcount(hval & (bp - 1)); - - /* occupied */ - if (bp & hval) { - hx = hashmap_shift_hash(th,hx,lvl,key); - node = ptr[slot+1]; - break; - } - /* not occupied */ - UnUseTmpHeapNoproc(2); - return NULL; - case HAMT_SUBTAG_HEAD_BITMAP: - hval = MAP_HEADER_VAL(hdr); - ix = hashmap_index(hx); - bp = 1 << ix; - slot = hashmap_bitcount(hval & (bp - 1)); - - /* occupied */ - if (bp & hval) { - hx = hashmap_shift_hash(th,hx,lvl,key); - node = ptr[slot+2]; - break; - } - /* not occupied */ - UnUseTmpHeapNoproc(2); - return NULL; - default: - erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); - break; - } - break; - default: - erl_exit(1, "bad primary tag %p\r\n", node); + hval = MAP_HEADER_VAL(hdr); + ix = hashmap_index(hx); + if (hval != 0xffff) { + bp = 1 << ix; + if (!(bp & hval)) { + /* not occupied */ + res = NULL; break; + } + ix = hashmap_bitcount(hval & (bp - 1)); + } + node = ptr[ix+1]; + + if (is_list(node)) { /* LEAF NODE [K|V] */ + ptr = list_val(node); + + res = eq_rel(CAR(ptr), map_base, key, NULL) ? &(CDR(ptr)) : NULL; + break; } + + hx = hashmap_shift_hash(th,hx,lvl,key); + + ASSERT(is_boxed(node)); + ptr = boxed_val(node); + hdr = *ptr; + ASSERT(is_header(hdr)); + ASSERT(!is_hashmap_header_head(hdr)); } + UnUseTmpHeapNoproc(2); - return NULL; + return res; } Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, -- cgit v1.2.3 From c7a07bf984739bcc679d800e5383c01e1d07ffa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 4 Mar 2015 17:08:31 +0100 Subject: erts: Enable command line argument for initial pd size Use '+hpds size' to set initial process dictionary size for spawned processes. --- erts/emulator/beam/erl_init.c | 13 +++++++++++++ erts/emulator/beam/erl_process_dict.c | 2 +- erts/emulator/beam/erl_vm.h | 1 + erts/etc/common/erlexec.c | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 61f8385efc..743fd235bc 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -194,6 +194,8 @@ int erts_disable_tolerant_timeofday; /* Time correction can be disabled it is int erts_atom_table_size = ATOM_LIMIT; /* Maximum number of atoms */ +int erts_pd_initial_size = 10; + int erts_modified_timing_level; int erts_no_crash_dump = 0; /* Use -d to suppress crash dump. */ @@ -519,6 +521,8 @@ void erts_usage(void) H_DEFAULT_SIZE); erts_fprintf(stderr, "-hmbs size set minimum binary virtual heap size in words (default %d)\n", VH_DEFAULT_SIZE); + erts_fprintf(stderr, "-hpds size initial process dictionary size (default %d)\n", + erts_pd_initial_size); /* erts_fprintf(stderr, "-i module set the boot module (default init)\n"); */ @@ -1408,6 +1412,7 @@ erl_start(int argc, char **argv) * * h|ms - min_heap_size * h|mbs - min_bin_vheap_size + * h|pds - erts_pd_initial_size * */ if (has_prefix("mbs", sub_param)) { @@ -1425,6 +1430,14 @@ erl_start(int argc, char **argv) erts_usage(); } VERBOSE(DEBUG_SYSTEM, ("using minimum heap size %d\n", H_MIN_SIZE)); + } else if (has_prefix("pds", sub_param)) { + arg = get_arg(sub_param+3, argv[i+1], &i); + if ((erts_pd_initial_size = atoi(arg)) <= 0) { + erts_fprintf(stderr, "bad initial process dictionary size %s\n", arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, ("using initial process dictionary size %d\n", + erts_pd_initial_size)); } else { /* backward compatibility */ arg = get_arg(argv[i]+2, argv[i+1], &i); diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 23e5bf737f..af20b26b15 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -47,7 +47,7 @@ /* Hash constant macros */ #define MAX_HASH 1342177280UL -#define INITIAL_SIZE 10 +#define INITIAL_SIZE (erts_pd_initial_size) /* Hash utility macros */ #define HASH_RANGE(PDict) ((PDict)->homeSize + (PDict)->splitPosition) diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index b7de8208ad..6687b044ee 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -174,6 +174,7 @@ extern int H_MIN_SIZE; /* minimum (heap + stack) */ extern int BIN_VH_MIN_SIZE; /* minimum virtual (bin) heap */ extern int erts_atom_table_size;/* Atom table size */ +extern int erts_pd_initial_size;/* Initial Process dictionary table size */ #define ORIG_CREATION 0 #define INTERNAL_CREATION 255 diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 5ebde8ca3c..b68e109b43 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -143,6 +143,7 @@ static char *pluss_val_switches[] = { static char *plush_val_switches[] = { "ms", "mbs", + "pds", "", NULL }; -- cgit v1.2.3 From 89987ada3c997fd9f1e1f8c8ed73da0394bda4ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 4 Mar 2015 17:28:37 +0100 Subject: erts: Document option 'hpds' --- erts/doc/src/erl.xml | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index d11f6b0c6d..f08467bfc6 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -588,6 +588,11 @@

Sets the default binary virtual heap size of processes to the size .

+ + +

Sets the initial process dictionary size of processes to the size + .

+

Enables or disables the kernel poll functionality if -- cgit v1.2.3 From 59ec3041469226ef65894e5774ecac0fea1551cd Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 25 Mar 2015 11:30:28 +0100 Subject: erts: Fix bug in list_to_integer for very large strings list_to_integer(lists:duplicate(10000000,$0)). crashed due to overflow when calculating nr heap words. --- erts/emulator/beam/bif.c | 6 ++++-- erts/emulator/test/num_bif_SUITE.erl | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 42dd160e38..3013ceff21 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2775,7 +2775,7 @@ static int do_list_to_integer(Process *p, Eterm orig_list, Uint ui = 0; int skip = 0; int neg = 0; - int n = 0; + Sint n = 0; int m; int lg2; Eterm res; @@ -2855,7 +2855,9 @@ static int do_list_to_integer(Process *p, Eterm orig_list, else i = (Sint)ui; res = make_small(i); } else { - lg2 = (n+1)*230/69+1; + /* Convert from log10 to log2 by multiplying with 1/log10(2)=3.3219 + which we round up to (3 + 1/3) */ + lg2 = (n+1)*3 + (n+1)/3 + 1; m = (lg2+D_EXP-1)/D_EXP; /* number of digits */ m = BIG_NEED_SIZE(m); /* number of words + thing */ diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index 8cf8377c30..abe5b8eb91 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -441,7 +441,11 @@ t_string_to_integer(Config) when is_list(Config) -> {"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111z",16}, {"1z111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",16}, {"111z11111111",16}]), - + + %% log2 calculation overflow bug in do_integer_to_list (OTP-12624) + %% Would crash with segv + 0 = list_to_integer(lists:duplicate(10000000,$0)), + ok. test_sti(Num) -> -- cgit v1.2.3 From c157dce842bf78080c533472fcec74df01ed8fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 20 Mar 2015 18:01:50 +0100 Subject: erts: Combine flat and hash maps under one unifying tag --- erts/emulator/beam/beam_emu.c | 16 +-- erts/emulator/beam/copy.c | 57 ++++---- erts/emulator/beam/erl_db_util.c | 18 +-- erts/emulator/beam/erl_gc.h | 7 +- erts/emulator/beam/erl_map.c | 42 +++--- erts/emulator/beam/erl_map.h | 50 ++++--- erts/emulator/beam/erl_nif.c | 4 +- erts/emulator/beam/erl_printf_term.c | 139 ++++++++++--------- erts/emulator/beam/erl_term.c | 1 - erts/emulator/beam/erl_term.h | 99 +++++++------- erts/emulator/beam/external.c | 22 +-- erts/emulator/beam/io.c | 6 +- erts/emulator/beam/utils.c | 250 ++++++++++++++++++----------------- 13 files changed, 349 insertions(+), 362 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 6526e87e4c..8fcdc72895 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6602,8 +6602,8 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) keys = make_tuple(thp); *thp++ = make_arityval(n/2); - mp = (flatmap_t *)mhp; mhp += MAP_HEADER_SIZE; - mp->thing_word = MAP_HEADER; + mp = (flatmap_t *)mhp; mhp += MAP_HEADER_FLATMAP_SZ; + mp->thing_word = MAP_HEADER_FLATMAP; mp->size = n/2; mp->keys = keys; @@ -6684,7 +6684,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) * update list are new). */ - need = 2*(num_old+num_updates) + 1 + MAP_HEADER_SIZE; + need = 2*(num_old+num_updates) + 1 + MAP_HEADER_FLATMAP_SZ; if (HeapWordsLeft(p) < need) { Uint live = Arg(3); reg[live] = map; @@ -6723,8 +6723,8 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) res = make_flatmap(hp); mp = (flatmap_t *)hp; - hp += MAP_HEADER_SIZE; - mp->thing_word = MAP_HEADER; + hp += MAP_HEADER_FLATMAP_SZ; + mp->thing_word = MAP_HEADER_FLATMAP; mp->keys = make_tuple(kp-1); old_vals = flatmap_get_values(old_mp); @@ -6906,7 +6906,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) * Allocate the exact heap space needed. */ - need = num_old + MAP_HEADER_SIZE; + need = num_old + MAP_HEADER_FLATMAP_SZ; if (HeapWordsLeft(p) < need) { Uint live = Arg(3); reg[live] = map; @@ -6927,8 +6927,8 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) res = make_flatmap(hp); mp = (flatmap_t *)hp; - hp += MAP_HEADER_SIZE; - mp->thing_word = MAP_HEADER; + hp += MAP_HEADER_FLATMAP_SZ; + mp->thing_word = MAP_HEADER_FLATMAP; mp->size = num_old; mp->keys = old_mp->keys; diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 027b85b079..4d12dae787 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -127,8 +127,25 @@ Uint size_object(Eterm obj) obj = *bptr; break; } - case HASHMAP_SUBTAG: + case MAP_SUBTAG: switch (MAP_HEADER_TYPE(hdr)) { + case MAP_HEADER_TAG_FLATMAP_HEAD : + { + Uint n; + flatmap_t *mp; + mp = (flatmap_t*)flatmap_val_rel(obj,base); + ptr = (Eterm *)mp; + n = flatmap_get_size(mp) + 1; + sum += n + 2; + ptr += 2; /* hdr + size words */ + while (n--) { + obj = *ptr++; + if (!IS_CONST(obj)) { + ESTACK_PUSH(s, obj); + } + } + goto pop_next; + } case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : case MAP_HEADER_TAG_HAMT_NODE_BITMAP : @@ -183,25 +200,7 @@ Uint size_object(Eterm obj) goto pop_next; } break; - case MAP_SUBTAG: - { - Uint n; - flatmap_t *mp; - mp = (flatmap_t*)flatmap_val_rel(obj,base); - ptr = (Eterm *)mp; - n = flatmap_get_size(mp) + 1; - sum += n + 2; - ptr += 2; /* hdr + size words */ - while (n--) { - obj = *ptr++; - if (!IS_CONST(obj)) { - ESTACK_PUSH(s, obj); - } - } - goto pop_next; - } - break; - case BIN_MATCHSTATE_SUBTAG: + case BIN_MATCHSTATE_SUBTAG: erl_exit(ERTS_ABORT_EXIT, "size_object: matchstate term not allowed"); default: @@ -369,15 +368,6 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } } break; - case MAP_SUBTAG: - { - i = flatmap_get_size(objp) + 3; - *argp = make_flatmap_rel(htop, dst_base); - while (i--) { - *htop++ = *objp++; - } - } - break; case REFC_BINARY_SUBTAG: { ProcBin* pb; @@ -502,9 +492,16 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) *argp = make_external_rel(tp, dst_base); } break; - case HASHMAP_SUBTAG: + case MAP_SUBTAG: tp = htop; switch (MAP_HEADER_TYPE(hdr)) { + case MAP_HEADER_TAG_FLATMAP_HEAD : + i = flatmap_get_size(objp) + 3; + *argp = make_flatmap_rel(htop, dst_base); + while (i--) { + *htop++ = *objp++; + } + break; case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : *htop++ = *objp++; diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 9d699d4b22..0bf562d937 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2153,16 +2153,16 @@ restart: break; case matchMkFlatMap: n = *pc++; - ehp = HAllocX(build_proc, 1 + MAP_HEADER_SIZE + n, HEAP_XTRA); + ehp = HAllocX(build_proc, 1 + MAP_HEADER_FLATMAP_SZ + n, HEAP_XTRA); t = *ehp++ = *--esp; { flatmap_t *m = (flatmap_t *)ehp; - m->thing_word = MAP_HEADER; + m->thing_word = MAP_HEADER_FLATMAP; m->size = n; m->keys = t; } t = make_flatmap(ehp); - ehp += MAP_HEADER_SIZE; + ehp += MAP_HEADER_FLATMAP_SZ; while (n--) { *ehp++ = *--esp; } @@ -3540,14 +3540,10 @@ static DMCRet dmc_one_term(DMCContext *context, DMC_PUSH(*stack, c); break; case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): - n = flatmap_get_size(flatmap_val(c)); - DMC_PUSH(*text, matchPushM); - ++(context->stack_used); - DMC_PUSH(*text, n); - DMC_PUSH(*stack, c); - break; - case (_TAG_HEADER_HASHMAP >> _TAG_PRIMARY_SIZE): - n = hashmap_size(c); + if (is_flatmap(c)) + n = flatmap_get_size(flatmap_val(c)); + else + n = hashmap_size(c); DMC_PUSH(*text, matchPushM); ++(context->stack_used); DMC_PUSH(*text, n); diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index 8afcb060a1..bd6dcc9078 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -55,9 +55,10 @@ do { \ nelts = header_arity(HDR); \ switch ((HDR) & _HEADER_SUBTAG_MASK) { \ case SUB_BINARY_SUBTAG: nelts++; break; \ - case MAP_SUBTAG: nelts+=flatmap_get_size(PTR) + 1; break; \ - case HASHMAP_SUBTAG: nelts=hashmap_bitcount(MAP_HEADER_VAL(HDR)); \ - nelts += is_hashmap_header_head(HDR) ? 1 : 0; break; \ + case MAP_SUBTAG: \ + if (is_flatmap_header(HDR)) nelts+=flatmap_get_size(PTR) + 1; \ + else nelts += hashmap_bitcount(MAP_HEADER_VAL(HDR)); \ + break; \ case FUN_SUBTAG: nelts+=((ErlFunThing*)(PTR))->num_free+1; break; \ } \ gval = make_boxed(HTOP); \ diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 35446501d4..4af8e6f274 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -303,10 +303,10 @@ static Eterm flatmap_from_validated_list(Process *p, Eterm list, Uint size) { hp += size; mp = (flatmap_t*)hp; res = make_flatmap(mp); - hp += MAP_HEADER_SIZE; + hp += MAP_HEADER_FLATMAP_SZ; vs = hp; - mp->thing_word = MAP_HEADER; + mp->thing_word = MAP_HEADER_FLATMAP; mp->size = size; /* set later, might shrink*/ mp->keys = keys; @@ -438,10 +438,10 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) { ks = hp; hp += n; mp = (flatmap_t*)hp; - hp += MAP_HEADER_SIZE; + hp += MAP_HEADER_FLATMAP_SZ; vs = hp; - mp->thing_word = MAP_HEADER; + mp->thing_word = MAP_HEADER_FLATMAP; mp->size = n; mp->keys = keys; @@ -944,16 +944,16 @@ static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { n1 = flatmap_get_size(mp1); n2 = flatmap_get_size(mp2); - need = MAP_HEADER_SIZE + 1 + 2*(n1 + n2); + need = MAP_HEADER_FLATMAP_SZ + 1 + 2 * (n1 + n2); hp = HAlloc(p, need); thp = hp; tup = make_tuple(thp); ks = hp + 1; hp += 1 + n1 + n2; - mp_new = (flatmap_t*)hp; hp += MAP_HEADER_SIZE; + mp_new = (flatmap_t*)hp; hp += MAP_HEADER_FLATMAP_SZ; vs = hp; hp += n1 + n2; - mp_new->thing_word = MAP_HEADER; + mp_new->thing_word = MAP_HEADER_FLATMAP; mp_new->size = 0; mp_new->keys = tup; @@ -1344,12 +1344,12 @@ BIF_RETTYPE maps_new_0(BIF_ALIST_0) { Eterm tup; flatmap_t *mp; - hp = HAlloc(BIF_P, (MAP_HEADER_SIZE + 1)); + hp = HAlloc(BIF_P, (MAP_HEADER_FLATMAP_SZ + 1)); tup = make_tuple(hp); *hp++ = make_arityval(0); mp = (flatmap_t*)hp; - mp->thing_word = MAP_HEADER; + mp->thing_word = MAP_HEADER_FLATMAP; mp->size = 0; mp->keys = tup; @@ -1401,7 +1401,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { *thp++ = make_arityval(n - 1); *res = make_flatmap(mhp); - *mhp++ = MAP_HEADER; + *mhp++ = MAP_HEADER_FLATMAP; *mhp++ = n - 1; *mhp++ = tup; @@ -1478,9 +1478,9 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) * assume key-tuple will be intact */ - hp = HAlloc(p, MAP_HEADER_SIZE + n); + hp = HAlloc(p, MAP_HEADER_FLATMAP_SZ + n); shp = hp; - *hp++ = MAP_HEADER; + *hp++ = MAP_HEADER_FLATMAP; *hp++ = n; *hp++ = mp->keys; @@ -1502,7 +1502,7 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) } } - HRelease(p, shp + MAP_HEADER_SIZE + n, shp); + HRelease(p, shp + MAP_HEADER_FLATMAP_SZ + n, shp); return 0; found_key: @@ -1536,12 +1536,12 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { n = flatmap_get_size(mp); if (n == 0) { - hp = HAlloc(p, MAP_HEADER_SIZE + 1 + 2); + hp = HAlloc(p, MAP_HEADER_FLATMAP_SZ + 1 + 2); tup = make_tuple(hp); *hp++ = make_arityval(1); *hp++ = key; res = make_flatmap(hp); - *hp++ = MAP_HEADER; + *hp++ = MAP_HEADER_FLATMAP; *hp++ = 1; *hp++ = tup; *hp++ = value; @@ -1556,10 +1556,10 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { * assume key-tuple will be intact */ - hp = HAlloc(p, MAP_HEADER_SIZE + n); + hp = HAlloc(p, MAP_HEADER_FLATMAP_SZ + n); shp = hp; /* save hp, used if optimistic update fails */ res = make_flatmap(hp); - *hp++ = MAP_HEADER; + *hp++ = MAP_HEADER_FLATMAP; *hp++ = n; *hp++ = mp->keys; @@ -1591,7 +1591,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { /* the map will grow */ if (n >= MAP_SMALL_MAP_LIMIT) { - HRelease(p, shp + MAP_HEADER_SIZE + n, shp); + HRelease(p, shp + MAP_HEADER_FLATMAP_SZ + n, shp); ks = flatmap_get_keys(mp); vs = flatmap_get_values(mp); @@ -1608,7 +1608,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { hp = HAlloc(p, 3 + n + 1); res = make_flatmap(hp); - *hp++ = MAP_HEADER; + *hp++ = MAP_HEADER_FLATMAP; *hp++ = n + 1; *hp++ = tup; @@ -2258,10 +2258,10 @@ unroll: ks = hp; hp += n; mp = (flatmap_t*)hp; - hp += MAP_HEADER_SIZE; + hp += MAP_HEADER_FLATMAP_SZ; vs = hp; - mp->thing_word = MAP_HEADER; + mp->thing_word = MAP_HEADER_FLATMAP; mp->size = n; mp->keys = keys; diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 9fc1a72b68..2cc6768bfc 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -55,9 +55,9 @@ typedef struct flatmap_s { /* the head-node is a bitmap or array with an untagged size */ -#define hashmap_size(x) (((hashmap_head_t*) hashmap_val(x))->size) +#define hashmap_size(x) (((hashmap_head_t*) hashmap_val(x))->size) #define hashmap_size_rel(RTERM, BASE) hashmap_size(rterm2wterm(RTERM, BASE)) -#define hashmap_make_hash(Key) make_internal_hash(Key) +#define hashmap_make_hash(Key) make_internal_hash(Key) #define hashmap_restore_hash(Heap,Lvl,Key) \ (((Lvl) < 8) ? hashmap_make_hash(Key) >> (4*(Lvl)) : hashmap_make_hash(CONS(Heap, make_small((Lvl)>>3), (Key))) >> (4*((Lvl) & 7))) @@ -66,27 +66,15 @@ typedef struct flatmap_s { /* erl_term.h stuff */ -#define make_flatmap(x) make_boxed((Eterm*)(x)) -#define make_flatmap_rel(x, BASE) make_boxed_rel((Eterm*)(x),(BASE)) -#define is_flatmap(x) (is_boxed((x)) && is_flatmap_header(*boxed_val((x)))) -#define is_flatmap_rel(RTERM,BASE) is_flatmap(rterm2wterm(RTERM,BASE)) -#define is_not_flatmap(x) (!is_flatmap((x))) -#define is_flatmap_header(x) (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_MAP) -#define header_is_flatmap(x) ((((x) & (_HEADER_SUBTAG_MASK)) == MAP_SUBTAG)) -#define flatmap_val(x) (_unchecked_boxed_val((x))) -#define flatmap_val_rel(RTERM, BASE) flatmap_val(rterm2wterm(RTERM, BASE)) - -#define flatmap_get_values(x) (((Eterm *)(x)) + 3) -#define flatmap_get_keys(x) (((Eterm *)tuple_val(((flatmap_t *)(x))->keys)) + 1) -#define flatmap_get_size(x) (((flatmap_t*)(x))->size) +#define flatmap_get_values(x) (((Eterm *)(x)) + 3) +#define flatmap_get_keys(x) (((Eterm *)tuple_val(((flatmap_t *)(x))->keys)) + 1) +#define flatmap_get_size(x) (((flatmap_t*)(x))->size) #ifdef DEBUG #define MAP_SMALL_MAP_LIMIT (3) #else #define MAP_SMALL_MAP_LIMIT (32) #endif -#define MAP_HEADER _make_header(1,_TAG_HEADER_MAP) -#define MAP_HEADER_SIZE (sizeof(flatmap_t) / sizeof(Eterm)) struct ErtsWStack_; struct ErtsEStack_; @@ -170,7 +158,10 @@ typedef struct hashmap_head_s { #define is_hashmap_header_head(x) ((MAP_HEADER_TYPE(x) & (0x2))) #define MAKE_MAP_HEADER(Type,Arity,Val) \ - (_make_header(((((Uint16)(Val)) << MAP_HEADER_ARITY_SZ) | (Arity)) << MAP_HEADER_TAG_SZ | (Type) , _TAG_HEADER_HASHMAP)) + (_make_header(((((Uint16)(Val)) << MAP_HEADER_ARITY_SZ) | (Arity)) << MAP_HEADER_TAG_SZ | (Type) , _TAG_HEADER_MAP)) + +#define MAP_HEADER_FLATMAP \ + MAKE_MAP_HEADER(MAP_HEADER_TAG_FLATMAP_HEAD,0x1,0x0) #define MAP_HEADER_HAMT_HEAD_ARRAY \ MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_HEAD_ARRAY,0x1,0xffff) @@ -181,15 +172,22 @@ typedef struct hashmap_head_s { #define MAP_HEADER_HAMT_NODE_BITMAP(Bmp) \ MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_NODE_BITMAP,0x0,Bmp) -#define HAMT_HEAD_EMPTY_SZ (2) -#define HAMT_HEAD_ARRAY_SZ (18) -#define HAMT_NODE_BITMAP_SZ(n) (1 + n) -#define HAMT_HEAD_BITMAP_SZ(n) (2 + n) +#define MAP_HEADER_FLATMAP_SZ (sizeof(flatmap_t) / sizeof(Eterm)) + +#define HAMT_NODE_ARRAY_SZ (17) +#define HAMT_HEAD_ARRAY_SZ (18) +#define HAMT_NODE_BITMAP_SZ(n) (1 + n) +#define HAMT_HEAD_BITMAP_SZ(n) (2 + n) + +/* 2 bits maps tag + 4 bits subtag + 2 ignore bits */ +#define _HEADER_MAP_SUBTAG_MASK (0xfc) +/* 1 bit map tag + 1 ignore bit + 4 bits subtag + 2 ignore bits */ +#define _HEADER_MAP_HASHMAP_HEAD_MASK (0xbc) -#define _HEADER_MAP_SUBTAG_MASK (0xfc) /* 2 bits maps tag + 4 bits subtag + 2 ignore bits */ -#define HAMT_SUBTAG_NODE_BITMAP ((MAP_HEADER_TAG_HAMT_NODE_BITMAP << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG) -#define HAMT_SUBTAG_HEAD_ARRAY ((MAP_HEADER_TAG_HAMT_HEAD_ARRAY << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG) -#define HAMT_SUBTAG_HEAD_BITMAP ((MAP_HEADER_TAG_HAMT_HEAD_BITMAP << _HEADER_ARITY_OFFS) | HASHMAP_SUBTAG) +#define HAMT_SUBTAG_NODE_BITMAP ((MAP_HEADER_TAG_HAMT_NODE_BITMAP << _HEADER_ARITY_OFFS) | MAP_SUBTAG) +#define HAMT_SUBTAG_HEAD_ARRAY ((MAP_HEADER_TAG_HAMT_HEAD_ARRAY << _HEADER_ARITY_OFFS) | MAP_SUBTAG) +#define HAMT_SUBTAG_HEAD_BITMAP ((MAP_HEADER_TAG_HAMT_HEAD_BITMAP << _HEADER_ARITY_OFFS) | MAP_SUBTAG) +#define HAMT_SUBTAG_HEAD_FLATMAP ((MAP_HEADER_TAG_FLATMAP_HEAD << _HEADER_ARITY_OFFS) | MAP_SUBTAG) #define hashmap_index(hash) (((Uint32)hash) & 0xf) diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c7c8b3fee3..660f446a52 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1926,14 +1926,14 @@ int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size) ERL_NIF_TERM enif_make_new_map(ErlNifEnv* env) { - Eterm* hp = alloc_heap(env,MAP_HEADER_SIZE+1); + Eterm* hp = alloc_heap(env,MAP_HEADER_FLATMAP_SZ+1); Eterm tup; flatmap_t *mp; tup = make_tuple(hp); *hp++ = make_arityval(0); mp = (flatmap_t*)hp; - mp->thing_word = MAP_HEADER; + mp->thing_word = MAP_HEADER_FLATMAP; mp->size = 0; mp->keys = tup; diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index ac5b139f8d..e0dfbd31b8 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -565,77 +565,74 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, } break; case MAP_DEF: - { - Uint n; - Eterm *ks, *vs; - flatmap_t *mp = (flatmap_t *)flatmap_val(wobj); - n = flatmap_get_size(mp); - ks = flatmap_get_keys(mp); - vs = flatmap_get_values(mp); - - PRINT_CHAR(res, fn, arg, '#'); - PRINT_CHAR(res, fn, arg, '{'); - WSTACK_PUSH(s, PRT_CLOSE_TUPLE); - if (n > 0) { - n--; - WSTACK_PUSH5(s, vs[n], PRT_TERM, PRT_ASSOC, ks[n], PRT_TERM); - while (n--) { - WSTACK_PUSH6(s, PRT_COMMA, vs[n], PRT_TERM, PRT_ASSOC, - ks[n], PRT_TERM); - } - } - } - break; - case HASHMAP_DEF: - { - Uint n,mapval; - Eterm *head; - head = hashmap_val(wobj); - mapval = MAP_HEADER_VAL(*head); - switch (MAP_HEADER_TYPE(*head)) { - case MAP_HEADER_TAG_HAMT_HEAD_ARRAY: - case MAP_HEADER_TAG_HAMT_HEAD_BITMAP: - PRINT_STRING(res, fn, arg, "#<"); - PRINT_UWORD(res, fn, arg, 'x', 0, 1, mapval); - PRINT_STRING(res, fn, arg, ">{"); - WSTACK_PUSH(s,PRT_CLOSE_TUPLE); - n = hashmap_bitcount(mapval); - ASSERT(n < 17); - head += 2; - if (n > 0) { - n--; - WSTACK_PUSH(s, head[n]); - WSTACK_PUSH(s, PRT_TERM); - while (n--) { - WSTACK_PUSH(s, PRT_COMMA); - WSTACK_PUSH(s, head[n]); - WSTACK_PUSH(s, PRT_TERM); - } - } - break; - case MAP_HEADER_TAG_HAMT_NODE_BITMAP: - n = hashmap_bitcount(mapval); - head++; - PRINT_CHAR(res, fn, arg, '<'); - PRINT_UWORD(res, fn, arg, 'x', 0, 1, mapval); - PRINT_STRING(res, fn, arg, ">{"); - WSTACK_PUSH(s,PRT_CLOSE_TUPLE); - ASSERT(n < 17); - if (n > 0) { - n--; - WSTACK_PUSH(s, head[n]); - WSTACK_PUSH(s, PRT_TERM); - while (n--) { - WSTACK_PUSH(s, PRT_COMMA); - WSTACK_PUSH(s, head[n]); - WSTACK_PUSH(s, PRT_TERM); - } - } - break; - } - } - break; - default: + if (is_flatmap(wobj)) { + Uint n; + Eterm *ks, *vs; + flatmap_t *mp = (flatmap_t *)flatmap_val(wobj); + n = flatmap_get_size(mp); + ks = flatmap_get_keys(mp); + vs = flatmap_get_values(mp); + + PRINT_CHAR(res, fn, arg, '#'); + PRINT_CHAR(res, fn, arg, '{'); + WSTACK_PUSH(s, PRT_CLOSE_TUPLE); + if (n > 0) { + n--; + WSTACK_PUSH5(s, vs[n], PRT_TERM, PRT_ASSOC, ks[n], PRT_TERM); + while (n--) { + WSTACK_PUSH6(s, PRT_COMMA, vs[n], PRT_TERM, PRT_ASSOC, + ks[n], PRT_TERM); + } + } + } else { + Uint n, mapval; + Eterm *head; + head = hashmap_val(wobj); + mapval = MAP_HEADER_VAL(*head); + switch (MAP_HEADER_TYPE(*head)) { + case MAP_HEADER_TAG_HAMT_HEAD_ARRAY: + case MAP_HEADER_TAG_HAMT_HEAD_BITMAP: + PRINT_STRING(res, fn, arg, "#<"); + PRINT_UWORD(res, fn, arg, 'x', 0, 1, mapval); + PRINT_STRING(res, fn, arg, ">{"); + WSTACK_PUSH(s,PRT_CLOSE_TUPLE); + n = hashmap_bitcount(mapval); + ASSERT(n < 17); + head += 2; + if (n > 0) { + n--; + WSTACK_PUSH(s, head[n]); + WSTACK_PUSH(s, PRT_TERM); + while (n--) { + WSTACK_PUSH(s, PRT_COMMA); + WSTACK_PUSH(s, head[n]); + WSTACK_PUSH(s, PRT_TERM); + } + } + break; + case MAP_HEADER_TAG_HAMT_NODE_BITMAP: + n = hashmap_bitcount(mapval); + head++; + PRINT_CHAR(res, fn, arg, '<'); + PRINT_UWORD(res, fn, arg, 'x', 0, 1, mapval); + PRINT_STRING(res, fn, arg, ">{"); + WSTACK_PUSH(s,PRT_CLOSE_TUPLE); + ASSERT(n < 17); + if (n > 0) { + n--; + WSTACK_PUSH(s, head[n]); + WSTACK_PUSH(s, PRT_TERM); + while (n--) { + WSTACK_PUSH(s, PRT_COMMA); + WSTACK_PUSH(s, head[n]); + WSTACK_PUSH(s, PRT_TERM); + } + } + break; + } + } + break; + default: PRINT_STRING(res, fn, arg, "'); diff --git a/erts/emulator/beam/erl_term.c b/erts/emulator/beam/erl_term.c index d6fb88ea61..bc04d7b78e 100644 --- a/erts/emulator/beam/erl_term.c +++ b/erts/emulator/beam/erl_term.c @@ -90,7 +90,6 @@ unsigned tag_val_def(Wterm x) case (_TAG_HEADER_REFC_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; case (_TAG_HEADER_HEAP_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; case (_TAG_HEADER_SUB_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; - case (_TAG_HEADER_HASHMAP >> _TAG_PRIMARY_SIZE): return HASHMAP_DEF; } break; diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 1625a4ec15..095aa54ddd 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -141,29 +141,27 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #define HEAP_BINARY_SUBTAG (0x9 << _TAG_PRIMARY_SIZE) /* BINARY */ #define SUB_BINARY_SUBTAG (0xA << _TAG_PRIMARY_SIZE) /* BINARY */ /* _BINARY_XXX_MASK depends on 0xB being unused */ -#define HASHMAP_SUBTAG (0xB << _TAG_PRIMARY_SIZE) /* HASHMAP */ #define EXTERNAL_PID_SUBTAG (0xC << _TAG_PRIMARY_SIZE) /* EXTERNAL_PID */ #define EXTERNAL_PORT_SUBTAG (0xD << _TAG_PRIMARY_SIZE) /* EXTERNAL_PORT */ #define EXTERNAL_REF_SUBTAG (0xE << _TAG_PRIMARY_SIZE) /* EXTERNAL_REF */ #define MAP_SUBTAG (0xF << _TAG_PRIMARY_SIZE) /* MAP */ -#define _TAG_HEADER_ARITYVAL (TAG_PRIMARY_HEADER|ARITYVAL_SUBTAG) -#define _TAG_HEADER_FUN (TAG_PRIMARY_HEADER|FUN_SUBTAG) -#define _TAG_HEADER_POS_BIG (TAG_PRIMARY_HEADER|POS_BIG_SUBTAG) -#define _TAG_HEADER_NEG_BIG (TAG_PRIMARY_HEADER|NEG_BIG_SUBTAG) -#define _TAG_HEADER_FLOAT (TAG_PRIMARY_HEADER|FLOAT_SUBTAG) -#define _TAG_HEADER_EXPORT (TAG_PRIMARY_HEADER|EXPORT_SUBTAG) -#define _TAG_HEADER_REF (TAG_PRIMARY_HEADER|REF_SUBTAG) -#define _TAG_HEADER_REFC_BIN (TAG_PRIMARY_HEADER|REFC_BINARY_SUBTAG) -#define _TAG_HEADER_HEAP_BIN (TAG_PRIMARY_HEADER|HEAP_BINARY_SUBTAG) -#define _TAG_HEADER_SUB_BIN (TAG_PRIMARY_HEADER|SUB_BINARY_SUBTAG) -#define _TAG_HEADER_EXTERNAL_PID (TAG_PRIMARY_HEADER|EXTERNAL_PID_SUBTAG) -#define _TAG_HEADER_EXTERNAL_PORT (TAG_PRIMARY_HEADER|EXTERNAL_PORT_SUBTAG) -#define _TAG_HEADER_EXTERNAL_REF (TAG_PRIMARY_HEADER|EXTERNAL_REF_SUBTAG) +#define _TAG_HEADER_ARITYVAL (TAG_PRIMARY_HEADER|ARITYVAL_SUBTAG) +#define _TAG_HEADER_FUN (TAG_PRIMARY_HEADER|FUN_SUBTAG) +#define _TAG_HEADER_POS_BIG (TAG_PRIMARY_HEADER|POS_BIG_SUBTAG) +#define _TAG_HEADER_NEG_BIG (TAG_PRIMARY_HEADER|NEG_BIG_SUBTAG) +#define _TAG_HEADER_FLOAT (TAG_PRIMARY_HEADER|FLOAT_SUBTAG) +#define _TAG_HEADER_EXPORT (TAG_PRIMARY_HEADER|EXPORT_SUBTAG) +#define _TAG_HEADER_REF (TAG_PRIMARY_HEADER|REF_SUBTAG) +#define _TAG_HEADER_REFC_BIN (TAG_PRIMARY_HEADER|REFC_BINARY_SUBTAG) +#define _TAG_HEADER_HEAP_BIN (TAG_PRIMARY_HEADER|HEAP_BINARY_SUBTAG) +#define _TAG_HEADER_SUB_BIN (TAG_PRIMARY_HEADER|SUB_BINARY_SUBTAG) +#define _TAG_HEADER_EXTERNAL_PID (TAG_PRIMARY_HEADER|EXTERNAL_PID_SUBTAG) +#define _TAG_HEADER_EXTERNAL_PORT (TAG_PRIMARY_HEADER|EXTERNAL_PORT_SUBTAG) +#define _TAG_HEADER_EXTERNAL_REF (TAG_PRIMARY_HEADER|EXTERNAL_REF_SUBTAG) #define _TAG_HEADER_BIN_MATCHSTATE (TAG_PRIMARY_HEADER|BIN_MATCHSTATE_SUBTAG) -#define _TAG_HEADER_MAP (TAG_PRIMARY_HEADER|MAP_SUBTAG) -#define _TAG_HEADER_HASHMAP (TAG_PRIMARY_HEADER|HASHMAP_SUBTAG) +#define _TAG_HEADER_MAP (TAG_PRIMARY_HEADER|MAP_SUBTAG) #define _TAG_HEADER_MASK 0x3F @@ -302,7 +300,7 @@ _ET_DECLARE_CHECKED(Uint,atom_val,Eterm) #define is_header(x) (((x) & _TAG_PRIMARY_MASK) == TAG_PRIMARY_HEADER) //#define _unchecked_header_arity(x) ((x) >> _HEADER_ARITY_OFFS) #define _unchecked_header_arity(x) \ - (is_hashmap_header(x) ? MAP_HEADER_ARITY(x) : ((x) >> _HEADER_ARITY_OFFS)) + (is_map_header(x) ? MAP_HEADER_ARITY(x) : ((x) >> _HEADER_ARITY_OFFS)) _ET_DECLARE_CHECKED(Uint,header_arity,Eterm) #define header_arity(x) _ET_APPLY(header_arity,(x)) @@ -1001,7 +999,7 @@ _ET_DECLARE_CHECKED(struct erl_node_*,external_ref_node,Eterm) #define MAP_HEADER_ARITY_SZ (8) #define MAP_HEADER_VAL_SZ (16) -#define MAP_HEADER_TAG_FLAT (0x0) +#define MAP_HEADER_TAG_FLATMAP_HEAD (0x0) #define MAP_HEADER_TAG_HAMT_NODE_BITMAP (0x1) #define MAP_HEADER_TAG_HAMT_HEAD_ARRAY (0x2) #define MAP_HEADER_TAG_HAMT_HEAD_BITMAP (0x3) @@ -1010,17 +1008,27 @@ _ET_DECLARE_CHECKED(struct erl_node_*,external_ref_node,Eterm) #define MAP_HEADER_ARITY(Hdr) (((Hdr) >> (_HEADER_ARITY_OFFS + MAP_HEADER_TAG_SZ)) & (0xff)) #define MAP_HEADER_VAL(Hdr) (((Hdr) >> (_HEADER_ARITY_OFFS + MAP_HEADER_TAG_SZ + MAP_HEADER_ARITY_SZ)) & (0xffff)) -#define make_hashmap(x) make_boxed((Eterm*)(x)) -#define make_hashmap_rel make_boxed_rel -#define is_hashmap(x) (is_boxed((x)) && is_hashmap_header(*boxed_val((x)))) -#define is_not_hashmap(x) (!is_hashmap(x)) -#define is_hashmap_rel(RTERM,BASE) is_hashmap(rterm2wterm(RTERM,BASE)) -#define is_hashmap_header(x) (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_HASHMAP) -#define hashmap_val(x) _unchecked_boxed_val((x)) -#define hashmap_val_rel(RTERM, BASE) hashmap_val(rterm2wterm(RTERM, BASE)) - -#define is_map(x) (is_flatmap(x) || is_hashmap(x)) -#define is_map_rel(x,BASE) (is_flatmap_rel(x,BASE) || is_hashmap_rel(x,BASE)) +#define make_hashmap(x) make_boxed((Eterm*)(x)) +#define make_hashmap_rel make_boxed_rel +#define is_hashmap(x) (is_boxed((x)) && is_hashmap_header(*boxed_val((x)))) +#define is_not_hashmap(x) (!is_hashmap(x)) +#define is_hashmap_rel(RTERM,BASE) is_hashmap(rterm2wterm(RTERM,BASE)) +#define is_hashmap_header(x) (((x) & (_HEADER_MAP_HASHMAP_HEAD_MASK)) == HAMT_SUBTAG_HEAD_ARRAY) +#define hashmap_val(x) _unchecked_boxed_val((x)) +#define hashmap_val_rel(RTERM, BASE) hashmap_val(rterm2wterm(RTERM, BASE)) + +#define make_flatmap(x) make_boxed((Eterm*)(x)) +#define make_flatmap_rel(x, BASE) make_boxed_rel((Eterm*)(x),(BASE)) +#define is_flatmap(x) (is_boxed((x)) && is_flatmap_header(*boxed_val((x)))) +#define is_flatmap_rel(RTERM,BASE) is_flatmap(rterm2wterm(RTERM,BASE)) +#define is_not_flatmap(x) (!is_flatmap((x))) +#define is_flatmap_header(x) (((x) & (_HEADER_MAP_SUBTAG_MASK)) == HAMT_SUBTAG_HEAD_FLATMAP) +#define flatmap_val(x) (_unchecked_boxed_val((x))) +#define flatmap_val_rel(RTERM, BASE) flatmap_val(rterm2wterm(RTERM, BASE)) + +#define is_map_header(x) (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_MAP) +#define is_map(x) (is_boxed((x)) && is_map_header(*boxed_val(x))) +#define is_map_rel(RTERM,BASE) is_map(rterm2wterm(RTERM,BASE)) /* number tests */ @@ -1113,23 +1121,22 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint) #define BINARY_DEF 0x0 #define LIST_DEF 0x1 #define NIL_DEF 0x2 -#define HASHMAP_DEF 0x3 -#define MAP_DEF 0x4 -#define TUPLE_DEF 0x5 -#define PID_DEF 0x6 -#define EXTERNAL_PID_DEF 0x7 -#define PORT_DEF 0x8 -#define EXTERNAL_PORT_DEF 0x9 -#define EXPORT_DEF 0xa -#define FUN_DEF 0xb -#define REF_DEF 0xc -#define EXTERNAL_REF_DEF 0xd -#define ATOM_DEF 0xe -#define FLOAT_DEF 0xf -#define BIG_DEF 0x10 -#define SMALL_DEF 0x11 - -#define FIRST_VACANT_TAG_DEF 0x12 +#define MAP_DEF 0x3 +#define TUPLE_DEF 0x4 +#define PID_DEF 0x5 +#define EXTERNAL_PID_DEF 0x6 +#define PORT_DEF 0x7 +#define EXTERNAL_PORT_DEF 0x8 +#define EXPORT_DEF 0x9 +#define FUN_DEF 0xa +#define REF_DEF 0xb +#define EXTERNAL_REF_DEF 0xc +#define ATOM_DEF 0xd +#define FLOAT_DEF 0xe +#define BIG_DEF 0xf +#define SMALL_DEF 0x10 + +#define FIRST_VACANT_TAG_DEF 0x11 #if ET_DEBUG extern unsigned tag_val_def_debug(Wterm, const char*, unsigned); diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index b0b232f185..2a9189b51e 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2604,7 +2604,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, break; case MAP_DEF: - { + if (is_flatmap(obj)) { flatmap_t *mp = (flatmap_t*)flatmap_val(obj); Uint size = flatmap_get_size(mp); @@ -2618,11 +2618,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, WSTACK_PUSH4(s, (UWord)kptr, (UWord)vptr, ENC_MAP_PAIR, size); } - } - break; - - case HASHMAP_DEF: - { + } else { Eterm hdr; Uint node_sz; ptr = boxed_val(obj); @@ -2656,7 +2652,6 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, } } break; - case FLOAT_DEF: GET_DOUBLE(obj, f); if (dflags & DFLAG_NEW_FLOATS) { @@ -3607,7 +3602,7 @@ dec_term_atom_common: kptr = hp - 1; mp = (flatmap_t*)hp; - hp += MAP_HEADER_SIZE; + hp += MAP_HEADER_FLATMAP_SZ; hp += size; vptr = hp - 1; @@ -3893,7 +3888,7 @@ dec_term_atom_common: while (maps_list) { next = (Eterm *)(EXPAND_POINTER(*maps_list)); - *maps_list = MAP_HEADER; + *maps_list = MAP_HEADER_FLATMAP; if (!erts_validate_and_sort_flatmap((flatmap_t*)maps_list)) goto error; maps_list = next; @@ -4121,7 +4116,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, } break; case MAP_DEF: - { + if (is_flatmap(obj)) { flatmap_t *mp = (flatmap_t*)flatmap_val(obj); Uint size = flatmap_get_size(mp); Uint i; @@ -4158,11 +4153,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, ++ptr; } goto outer_loop; - } - break; - - case HASHMAP_DEF: - { + } else { Eterm *ptr; Eterm hdr; Uint node_sz; @@ -4190,7 +4181,6 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, } result += 1 + 4; /* tag + 4 bytes size */ } - break; case FLOAT_DEF: if (dflags & DFLAG_NEW_FLOATS) { diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 46f0eba5e0..1db3a9fba7 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -5354,7 +5354,7 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) if (ptr[0] > MAP_SMALL_MAP_LIMIT) { need += hashmap_over_estimated_heap_size(ptr[0]); } else { - need += MAP_HEADER_SIZE + 1 + 2*ptr[0]; + need += MAP_HEADER_FLATMAP_SZ + 1 + 2*ptr[0]; } depth -= 2*ptr[0]; if (depth < 0) ERTS_DDT_FAIL; @@ -5627,12 +5627,12 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) hp += 1 + size; mp = (flatmap_t*)hp; - mp->thing_word = MAP_HEADER; + mp->thing_word = MAP_HEADER_FLATMAP; mp->size = size; mp->keys = make_tuple(tp); mess = make_flatmap(mp); - hp += MAP_HEADER_SIZE + size; /* advance "heap" pointer */ + hp += MAP_HEADER_FLATMAP_SZ + size; tp += size; /* point at last key */ vp = hp - 1; /* point at last value */ diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 6edb466a36..cb4ef2b376 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1025,11 +1025,8 @@ tail_recur: break; } case MAP_DEF: - case HASHMAP_DEF: - { - hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term); - break; - } + hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term); + break; case TUPLE_DEF: { Eterm* ptr = tuple_val(term); @@ -1273,41 +1270,40 @@ make_hash2(Eterm term) } } break; - case MAP_SUBTAG: - { - flatmap_t *mp = (flatmap_t *)flatmap_val(term); - int i; - int size = flatmap_get_size(mp); - Eterm *ks = flatmap_get_keys(mp); - Eterm *vs = flatmap_get_values(mp); - UINT32_HASH(size, HCONST_16); - if (size == 0) { - goto hash2_common; - } - /* We want a portable hash function that is *independent* of - * the order in which keys and values are encountered. - * We therefore calculate context independent hashes for all . - * key-value pairs and then xor them together. - */ - ESTACK_PUSH(s, hash_xor_pairs); - ESTACK_PUSH(s, hash); - ESTACK_PUSH(s, HASH_MAP_TAIL); - hash = 0; - hash_xor_pairs = 0; - for (i = size - 1; i >= 0; i--) { - ESTACK_PUSH(s, HASH_MAP_PAIR); - ESTACK_PUSH(s, vs[i]); - ESTACK_PUSH(s, ks[i]); - } - goto hash2_common; - } - break; - case HASHMAP_SUBTAG: + case MAP_SUBTAG: { Eterm* ptr = boxed_val(term) + 1; Uint size; int i; switch (hdr & _HEADER_MAP_SUBTAG_MASK) { + case HAMT_SUBTAG_HEAD_FLATMAP: + { + flatmap_t *mp = (flatmap_t *)flatmap_val(term); + Eterm *ks = flatmap_get_keys(mp); + Eterm *vs = flatmap_get_values(mp); + size = flatmap_get_size(mp); + UINT32_HASH(size, HCONST_16); + if (size == 0) + goto hash2_common; + + /* We want a portable hash function that is *independent* of + * the order in which keys and values are encountered. + * We therefore calculate context independent hashes for all . + * key-value pairs and then xor them together. + */ + ESTACK_PUSH(s, hash_xor_pairs); + ESTACK_PUSH(s, hash); + ESTACK_PUSH(s, HASH_MAP_TAIL); + hash = 0; + hash_xor_pairs = 0; + for (i = size - 1; i >= 0; i--) { + ESTACK_PUSH(s, HASH_MAP_PAIR); + ESTACK_PUSH(s, vs[i]); + ESTACK_PUSH(s, ks[i]); + } + goto hash2_common; + } + case HAMT_SUBTAG_HEAD_ARRAY: case HAMT_SUBTAG_HEAD_BITMAP: size = *ptr++; @@ -1675,42 +1671,40 @@ make_internal_hash(Eterm term) } } break; - case MAP_SUBTAG: - { - flatmap_t *mp = (flatmap_t *)flatmap_val(term); - int i; - int size = flatmap_get_size(mp); - Eterm *ks = flatmap_get_keys(mp); - Eterm *vs = flatmap_get_values(mp); - UINT32_HASH(size, HCONST_16); - if (size == 0) { - goto pop_next; - } - /* We want a hash function that is *independent* of - * the order in which keys and values are encountered. - * We therefore calculate context independent hashes for all . - * key-value pairs and then xor them together. - */ - ESTACK_PUSH(s, hash_xor_pairs); - ESTACK_PUSH(s, hash); - ESTACK_PUSH(s, HASH_MAP_TAIL); - hash = 0; - hash_xor_pairs = 0; - for (i = size - 1; i >= 0; i--) { - ESTACK_PUSH(s, HASH_MAP_PAIR); - ESTACK_PUSH(s, vs[i]); - ESTACK_PUSH(s, ks[i]); - } - goto pop_next; - } - break; - case HASHMAP_SUBTAG: + + case MAP_SUBTAG: { Eterm* ptr = boxed_val(term) + 1; Uint size; int i; switch (hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_ARRAY: + case HAMT_SUBTAG_HEAD_FLATMAP: + { + flatmap_t *mp = (flatmap_t *)flatmap_val(term); + Eterm *ks = flatmap_get_keys(mp); + Eterm *vs = flatmap_get_values(mp); + size = flatmap_get_size(mp); + UINT32_HASH(size, HCONST_16); + if (size == 0) + goto pop_next; + + /* We want a hash function that is *independent* of + * the order in which keys and values are encountered. + * We therefore calculate context independent hashes for all . + * key-value pairs and then xor them together. + */ + ESTACK_PUSH(s, hash_xor_pairs); + ESTACK_PUSH(s, hash); + ESTACK_PUSH(s, HASH_MAP_TAIL); + hash = 0; + hash_xor_pairs = 0; + for (i = size - 1; i >= 0; i--) { + ESTACK_PUSH(s, HASH_MAP_PAIR); + ESTACK_PUSH(s, vs[i]); + ESTACK_PUSH(s, ks[i]); + } + goto pop_next; + } case HAMT_SUBTAG_HEAD_BITMAP: size = *ptr++; UINT32_HASH(size, HCONST_16); @@ -2170,11 +2164,8 @@ tail_recur: break; case MAP_DEF: - case HASHMAP_DEF: - { - hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term); - break; - } + hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term); + break; case TUPLE_DEF: { Eterm* ptr = tuple_val(term); @@ -2579,22 +2570,6 @@ tailrecur_ne: ++bb; goto term_array; } - case MAP_SUBTAG: - { - aa = flatmap_val_rel(a, a_base); - if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa) - goto not_equal; - bb = flatmap_val_rel(b,b_base); - sz = flatmap_get_size((flatmap_t*)aa); - - if (sz != flatmap_get_size((flatmap_t*)bb)) goto not_equal; - if (sz == 0) goto pop_next; - - aa += 2; - bb += 2; - sz += 1; /* increment for tuple-keys */ - goto term_array; - } case REFC_BINARY_SUBTAG: case HEAP_BINARY_SUBTAG: case SUB_BINARY_SUBTAG: @@ -2786,8 +2761,23 @@ tailrecur_ne: } break; /* not equal */ } - case HASHMAP_SUBTAG: - { + case MAP_SUBTAG: + if (is_flatmap_rel(a, a_base)) { + aa = flatmap_val_rel(a, a_base); + if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa) + goto not_equal; + bb = flatmap_val_rel(b,b_base); + sz = flatmap_get_size((flatmap_t*)aa); + + if (sz != flatmap_get_size((flatmap_t*)bb)) goto not_equal; + if (sz == 0) goto pop_next; + + aa += 2; + bb += 2; + sz += 1; /* increment for tuple-keys */ + goto term_array; + + } else { if (!is_boxed(b) || *boxed_val_rel(b,b_base) != hdr) goto not_equal; @@ -3124,44 +3114,56 @@ tailrecur_ne: ++aa; ++bb; goto term_array; - case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE) : - if (!is_flatmap_rel(b,b_base)) { - a_tag = MAP_DEF; - goto mixed_types; - } - aa = (Eterm *)flatmap_val_rel(a,a_base); - bb = (Eterm *)flatmap_val_rel(b,b_base); - - i = flatmap_get_size((flatmap_t*)aa); - if (i != flatmap_get_size((flatmap_t*)bb)) { - RETURN_NEQ((int)(i - flatmap_get_size((flatmap_t*)bb))); - } - if (i == 0) { - goto pop_next; - } - aa += 2; - bb += 2; - if (exact) { - i += 1; /* increment for tuple-keys */ - goto term_array; - } - else { - /* Value array */ - WSTACK_PUSH3(stack, (UWord)(bb+1), (UWord)(aa+1), TERM_ARRAY_OP_WORD(i)); - /* Switch back from 'exact' key compare */ - WSTACK_PUSH(stack, OP_WORD(SWITCH_EXACT_OFF_OP)); - /* Now do 'exact' compare of key tuples */ - a = *aa; - b = *bb; - exact = 1; - goto bodyrecur; - } - - case (_TAG_HEADER_HASHMAP >> _TAG_PRIMARY_SIZE) : + case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE) : { struct erts_cmp_hashmap_state* sp; + if (is_flatmap_header(ahdr)) { + if (!is_flatmap_rel(b,b_base)) { + if (is_hashmap_rel(b,b_base)) { + aa = (Eterm *)flatmap_val_rel(a,a_base); + i = flatmap_get_size((flatmap_t*)aa) - hashmap_size_rel(b,b_base); + ASSERT(i != 0); + RETURN_NEQ(i); + } + a_tag = MAP_DEF; + goto mixed_types; + } + aa = (Eterm *)flatmap_val_rel(a,a_base); + bb = (Eterm *)flatmap_val_rel(b,b_base); + + i = flatmap_get_size((flatmap_t*)aa); + if (i != flatmap_get_size((flatmap_t*)bb)) { + RETURN_NEQ((int)(i - flatmap_get_size((flatmap_t*)bb))); + } + if (i == 0) { + goto pop_next; + } + aa += 2; + bb += 2; + if (exact) { + i += 1; /* increment for tuple-keys */ + goto term_array; + } + else { + /* Value array */ + WSTACK_PUSH3(stack,(UWord)(bb+1),(UWord)(aa+1),TERM_ARRAY_OP_WORD(i)); + /* Switch back from 'exact' key compare */ + WSTACK_PUSH(stack,OP_WORD(SWITCH_EXACT_OFF_OP)); + /* Now do 'exact' compare of key tuples */ + a = *aa; + b = *bb; + exact = 1; + goto bodyrecur; + } + } if (!is_hashmap_rel(b,b_base)) { - a_tag = HASHMAP_DEF; + if (is_flatmap_rel(b,b_base)) { + bb = (Eterm *)flatmap_val_rel(b,b_base); + i = hashmap_size_rel(a,a_base) - flatmap_get_size((flatmap_t*)bb); + ASSERT(i != 0); + RETURN_NEQ(i); + } + a_tag = MAP_DEF; goto mixed_types; } i = hashmap_size_rel(a,a_base) - hashmap_size_rel(b,b_base); -- cgit v1.2.3 From 139edf3f40fcf746fe21318f03032c091b3c2fc1 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 24 Mar 2015 19:43:08 +0100 Subject: Fixes and cleanup --- erts/aclocal.m4 | 8 +- erts/emulator/beam/erl_time_sup.c | 48 ++-- erts/emulator/sys/unix/erl_unix_sys.h | 99 +++---- erts/emulator/sys/unix/sys_time.c | 491 ++++++++++++++++++++++++++-------- erts/emulator/sys/win32/erl_win_sys.h | 9 + erts/emulator/sys/win32/sys_time.c | 121 +++++++-- 6 files changed, 564 insertions(+), 212 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 7a969547cf..40ea980209 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -745,9 +745,9 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, done ]) - AC_CHECK_FUNCS([clock_getres gethrtime]) + AC_CHECK_FUNCS([clock_getres clock_get_attributes gethrtime]) - AC_CACHE_CHECK([for mach clock_get_time()], erl_cv_mach_clock_get_time_monotonic, + AC_CACHE_CHECK([for mach clock_get_time() with monotonic clock type], erl_cv_mach_clock_get_time_monotonic, [ AC_TRY_COMPILE([ #include @@ -854,9 +854,9 @@ AC_DEFUN(ERL_WALL_CLOCK, done ]) - AC_CHECK_FUNCS([clock_getres gettimeofday]) + AC_CHECK_FUNCS([clock_getres clock_get_attributes gettimeofday]) - AC_CACHE_CHECK([for mach clock_get_time()], erl_cv_mach_clock_get_time_wall, + AC_CACHE_CHECK([for mach clock_get_time() with wall clock type], erl_cv_mach_clock_get_time_wall, [ AC_TRY_COMPILE([ #include diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index ef39f4b5f4..8203436c85 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-2012. All Rights Reserved. + * Copyright Ericsson AB 1999-2015. All Rights Reserved. * * The 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,12 +139,12 @@ struct time_sup_read_only__ { int os_monotonic_time_locked; Uint64 os_monotonic_time_resolution; Uint64 os_monotonic_time_extended; +#endif char *os_system_time_func; char *os_system_time_clock_id; int os_system_time_locked; Uint64 os_system_time_resolution; Uint64 os_system_time_extended; -#endif #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT ErtsMonotonicTime start; struct { @@ -287,7 +287,7 @@ get_time_offset(void) #define ERTS_LONG_TIME_CORRECTION_CHECK ERTS_SEC_TO_MONOTONIC(60) #define ERTS_SHORT_TIME_CORRECTION_CHECK ERTS_SEC_TO_MONOTONIC(15) -#define ERTS_TIME_DRIFT_MAX_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(100) +#define ERTS_TIME_DRIFT_MAX_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(50) #define ERTS_TIME_DRIFT_MIN_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(5) static ERTS_INLINE ErtsMonotonicTime @@ -353,21 +353,21 @@ print_correction(int change, if (!change) fprintf(stderr, - "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppm] : " + "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppb] : " "tmo = %lld msec\r\n", (long long) usec_sdiff, (long long) (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, - (long long) (1000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + (long long) (1000000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, (long long) tmo); else fprintf(stderr, - "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppm] " - "-> [ec=%lld ppm, dc=%lld ppm] : tmo = %lld msec\r\n", + "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppb] " + "-> [ec=%lld ppm, dc=%lld ppb] : tmo = %lld msec\r\n", (long long) usec_sdiff, (long long) (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, - (long long) (1000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + (long long) (1000000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, (long long) (1000000*new_ecorr) / ERTS_TCORR_ERR_UNIT, - (long long) (1000000*new_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + (long long) (1000000000*new_dcorr) / ERTS_MONOTONIC_TIME_UNIT, (long long) tmo); } @@ -412,8 +412,7 @@ check_time_correction(void *unused) ASSERT(time_sup.inf.c.finalized_offset); - os_mtime = erts_os_monotonic_time(); - os_stime = erts_os_system_time(); + erts_os_times(&os_mtime, &os_stime); cdata = time_sup.inf.c.parmon.cdata; @@ -556,13 +555,6 @@ check_time_correction(void *unused) ddp->acc.mon = mtime_acc; ddp->acc.sys = stime_acc; - /* - * If calculated drift adjustment is if off by more than 20% - * from the average drift we interpret this as a discontinous - * leap in system time and ignore it. If it actually is a - * change in drift we will later detect this when the average - * drift change. - */ drift_adj_diff = avg_drift_adj - drift_adj; if (drift_adj_diff < -ERTS_TIME_DRIFT_MAX_ADJ_DIFF || ERTS_TIME_DRIFT_MAX_ADJ_DIFF < drift_adj_diff) { @@ -691,8 +683,7 @@ init_check_time_correction(void *unused) old_mtime = ddp->intervals[0].time.mon; old_stime = ddp->intervals[0].time.sys; - mtime = erts_os_monotonic_time(); - stime = erts_os_system_time(); + erts_os_times(&mtime, &stime); mtime_diff = mtime - old_mtime; stime_diff = stime - old_stime; @@ -732,8 +723,7 @@ finalize_corrected_time_offset(ErtsSystemTime *stimep) erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); - os_mtime = erts_os_monotonic_time(); - *stimep = erts_os_system_time(); + erts_os_times(&os_mtime, stimep); cdata = time_sup.inf.c.parmon.cdata; @@ -852,6 +842,7 @@ void erts_init_sys_time_sup(void) = sys_init_time_res.os_monotonic_time_info.resolution; time_sup.r.o.os_monotonic_time_extended = sys_init_time_res.os_monotonic_time_info.extended; +#endif time_sup.r.o.os_system_time_func = sys_init_time_res.os_system_time_info.func; time_sup.r.o.os_system_time_clock_id @@ -860,7 +851,6 @@ void erts_init_sys_time_sup(void) = sys_init_time_res.os_system_time_info.locked_use; time_sup.r.o.os_system_time_resolution = sys_init_time_res.os_system_time_info.resolution; -#endif } int @@ -935,9 +925,11 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) #endif - resolution = time_sup.r.o.os_monotonic_time_resolution; - if (resolution > time_sup.r.o.os_system_time_resolution) - resolution = time_sup.r.o.os_system_time_resolution; + resolution = time_sup.r.o.os_system_time_resolution; +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + if (resolution > time_sup.r.o.os_monotonic_time_resolution) + resolution = time_sup.r.o.os_monotonic_time_resolution; +#endif time_sup.r.o.adj.large_diff = erts_time_sup__.r.o.monotonic_time_unit; time_sup.r.o.adj.large_diff *= 50; @@ -972,8 +964,8 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) ErtsMonotonicCorrectionData *cdatap; erts_smp_rwmtx_opt_t rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; ErtsMonotonicTime offset; - time_sup.inf.c.minit = erts_os_monotonic_time(); - time_sup.inf.c.sinit = erts_os_system_time(); + erts_os_times(&time_sup.inf.c.minit, + &time_sup.inf.c.sinit); time_sup.r.o.moffset = -1*time_sup.inf.c.minit; offset = time_sup.inf.c.sinit; offset -= ERTS_MONOTONIC_TIME_UNIT; diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 5394e94c8c..c2cd870f80 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -171,61 +171,47 @@ typedef long long ErtsSysHrTime; typedef ErtsMonotonicTime ErtsSystemTime; -ErtsSystemTime erts_os_system_time(void); - #define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63) #define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN) /* - * OS monotonic time + * OS monotonic time and OS system time */ -/* - * Most common with os monotonic time using nano second - * time unit. These defines are modified below if this - * isn't the case... - */ -#define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1 -#define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000*1000) +#undef ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__ + +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) +# if defined(__linux__) +# define ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__ 1 +# endif +#endif + +ErtsSystemTime erts_os_system_time(void); +#undef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT +#undef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT #undef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ #undef ERTS_HAVE_CORRECTED_OS_MONOTONIC #if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) - -#if defined(__linux__) - -#define ERTS_HAVE_CORRECTED_OS_MONOTONIC 1 -#define ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ 1 - -#else /* !defined(__linux__) */ - -ErtsMonotonicTime erts_os_monotonic_time(void); - -#endif /* !defined(__linux__) */ - +# define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1 +# define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000*1000) +# if defined(__linux__) +# define ERTS_HAVE_CORRECTED_OS_MONOTONIC 1 +# define ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ 1 +# endif +#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) +# define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1 +# define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000*1000) #elif defined(OS_MONOTONIC_TIME_USING_GETHRTIME) - -#define erts_os_monotonic() ((ErtsMonotonicTime) gethrtime()) -#define erts_sys_hrtime() ((ErtsSysHrTime) gethrtime()) - -#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ - || defined(OS_MONOTONIC_TIME_USING_TIMES) - -#if defined(OS_MONOTONIC_TIME_USING_TIMES) +# define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1 +# define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000*1000) +#elif defined(OS_MONOTONIC_TIME_USING_TIMES) +# define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1 /* Time unit determined at runtime... */ -# undef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT # define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT 0 -#endif - -ErtsMonotonicTime erts_os_monotonic_time(void); - #else /* No OS monotonic available... */ - -#undef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT -#undef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT -#define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000) - +# define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000) #endif /* @@ -233,13 +219,14 @@ ErtsMonotonicTime erts_os_monotonic_time(void); * time function found. Time unit is nano-seconds. * It may or may not be monotonic. */ -#ifndef erts_sys_hrtime -extern ErtsSysHrTime erts_sys_hrtime(void); -#endif +ErtsSysHrTime erts_sys_hrtime(void); struct erts_sys_time_read_only_data__ { #ifdef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ ErtsMonotonicTime (*os_monotonic_time)(void); +#endif +#ifdef ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__ + void (*os_times)(ErtsMonotonicTime *, ErtsSystemTime *); #endif int ticks_per_sec; }; @@ -255,21 +242,43 @@ typedef struct { extern ErtsSysTimeData__ erts_sys_time_data__; +#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT + #ifdef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ +ERTS_GLB_INLINE +#endif +ErtsMonotonicTime erts_os_monotonic_time(void); -ERTS_GLB_INLINE ErtsMonotonicTime erts_os_monotonic_time(void); +#ifdef ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__ +ERTS_GLB_INLINE +#endif +void erts_os_times(ErtsMonotonicTime *, ErtsSystemTime *); #if ERTS_GLB_INLINE_INCL_FUNC_DEF +#ifdef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ + ERTS_GLB_INLINE ErtsMonotonicTime erts_os_monotonic_time(void) { return (*erts_sys_time_data__.r.o.os_monotonic_time)(); } +#endif /* ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ */ + +#ifdef ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__ + +ERTS_GLB_INLINE void +erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) +{ + return (*erts_sys_time_data__.r.o.os_times)(mtimep, stimep); +} + +#endif /* ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__ */ + #endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ -#endif /* ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ */ +#endif /* ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT */ /* * diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index d6591a8296..e764607c25 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -35,6 +35,20 @@ #include "global.h" #include "erl_os_monotonic_time_extender.h" +#undef ERTS_HAVE_ERTS_OS_TIMES_IMPL__ +#undef ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ + +#if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ + || defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) +# include +# include +# ifdef HAVE_CLOCK_GET_ATTRIBUTES +# define ERTS_HAVE_MACH_CLOCK_GETRES +static Sint64 +mach_clock_getres(clock_id_t clkid, char *clkid_str); +# endif +#endif + #ifdef NO_SYSCONF # define TICKS_PER_SEC() HZ #else @@ -87,6 +101,10 @@ ErtsSysTimeData__ erts_sys_time_data__ erts_align_attribute(ERTS_CACHE_LINE_SIZE static ErtsMonotonicTime clock_gettime_monotonic_raw(void); static ErtsMonotonicTime clock_gettime_monotonic_verified(void); +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) +static void clock_gettime_times_raw(ErtsMonotonicTime *, ErtsSystemTime *); +static void clock_gettime_times_verified(ErtsMonotonicTime *, ErtsSystemTime *); +#endif #endif /* defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */ @@ -167,6 +185,9 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_monotonic_time_info.resolution = 1; } } +#elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID) + init_resp->os_monotonic_time_info.resolution + = mach_clock_getres(MONOTONIC_CLOCK_ID, MONOTONIC_CLOCK_ID_STR); #endif #ifdef MONOTONIC_CLOCK_ID_STR @@ -197,9 +218,14 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) #if defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) - if (vsn >= ERTS_MK_VSN_INT(2, 6, 33)) + if (vsn >= ERTS_MK_VSN_INT(2, 6, 33)) { erts_sys_time_data__.r.o.os_monotonic_time = clock_gettime_monotonic_raw; +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) + erts_sys_time_data__.r.o.os_times = + clock_gettime_times_raw; +#endif + } else { /* * Linux versions prior to 2.6.33 have a @@ -208,6 +234,10 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) */ erts_sys_time_data__.r.o.os_monotonic_time = clock_gettime_monotonic_verified; +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) + erts_sys_time_data__.r.o.os_times = + clock_gettime_times_verified; +#endif erts_smp_mtx_init(&internal_state.w.f.mtx, "os_monotonic_time"); internal_state.w.f.last_delivered @@ -292,6 +322,9 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_system_time_info.resolution = 1; } } +#elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(WALL_CLOCK_ID) + init_resp->os_system_time_info.resolution + = mach_clock_getres(WALL_CLOCK_ID, WALL_CLOCK_ID_STR); #endif #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) @@ -303,7 +336,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_system_time_info.resolution = 1000*1000; init_resp->os_system_time_info.clock_id = NULL; #else -# error Missing erts_os_system_time() implmenentation +# error Missing erts_os_system_time() implementation #endif } @@ -333,117 +366,102 @@ adj_stime_time_unit(ErtsSystemTime stime, Uint32 res) (Uint32) ERTS_MONOTONIC_TIME_UNIT)); } -#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * POSIX clock_gettime() * +\* */ -ErtsSystemTime -erts_os_system_time(void) +#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) \ + || defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) + +static ERTS_INLINE ErtsMonotonicTime +timespec2montime(struct timespec *ts) +{ + ErtsMonotonicTime time; + time = (ErtsMonotonicTime) ts->tv_sec; + time *= (ErtsMonotonicTime) 1000*1000*1000; + time += (ErtsMonotonicTime) ts->tv_nsec; + return time; +} + +static ERTS_INLINE ErtsMonotonicTime +posix_clock_gettime(clockid_t id, char *name) { - ErtsSystemTime stime; struct timespec ts; - if (clock_gettime(WALL_CLOCK_ID,&ts) != 0) { + if (clock_gettime(id, &ts) != 0) { int err = errno; char *errstr = err ? strerror(err) : "unknown"; erl_exit(ERTS_ABORT_EXIT, "clock_gettime(%s, _) failed: %s (%d)\n", - WALL_CLOCK_ID_STR, errstr, err); - + name, errstr, err); } - - stime = (ErtsSystemTime) ts.tv_sec; - stime *= (ErtsSystemTime) 1000*1000*1000; - stime += (ErtsSystemTime) ts.tv_nsec; - return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000); + return timespec2montime(&ts); } -#elif defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) +#endif /* defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) \ + || defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ + +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) ErtsSystemTime erts_os_system_time(void) { ErtsSystemTime stime; - kern_return_t res; - clock_serv_t clk_srv; - mach_timespec_t time_spec; - int err; - - host_get_clock_service(mach_host_self(), - WALL_CLOCK_ID, - &clk_srv); - errno = 0; - res = clock_get_time(clk_srv, &time_spec); - err = errno; - mach_port_deallocate(mach_task_self(), clk_srv); - if (res != KERN_SUCCESS) { - char *errstr = err ? strerror(err) : "unknown"; - erl_exit(ERTS_ABORT_EXIT, - "clock_get_time(%s, _) failed: %s (%d)\n", - MONOTONIC_CLOCK_ID_STR, errstr, err); - } - - stime = (ErtsSystemTime) time_spec.tv_sec; - stime *= (ErtsSystemTime) 1000*1000*1000; - stime += (ErtsSystemTime) time_spec.tv_nsec; + stime = (ErtsSystemTime) posix_clock_gettime(WALL_CLOCK_ID, + WALL_CLOCK_ID_STR); +#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) + return stime; +#else return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000); +#endif } -#elif defined(OS_SYSTEM_TIME_GETTIMEOFDAY) - -ErtsSystemTime -erts_os_system_time(void) -{ - ErtsSystemTime stime; - struct timeval tv; - - if (gettimeofday(&tv, NULL) != 0) { - int err = errno; - char *errstr = err ? strerror(err) : "unknown"; - erl_exit(ERTS_ABORT_EXIT, - "gettimeofday(_, NULL) failed: %s (%d)\n", - errstr, err); - } - - stime = (ErtsSystemTime) tv.tv_sec; - stime *= (ErtsSystemTime) 1000*1000; - stime += (ErtsSystemTime) tv.tv_usec; +#endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ - return adj_stime_time_unit(stime, (Uint32) 1000*1000); -} +#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) -#else -# error Missing erts_os_system_time() implmenentation -#endif +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) -#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) +#define ERTS_HAVE_ERTS_OS_TIMES_IMPL__ -static ERTS_INLINE ErtsMonotonicTime -clock_gettime_monotonic(void) +static ERTS_INLINE void +posix_clock_gettime_times(ErtsMonotonicTime *mtimep, + ErtsSystemTime *stimep) { - ErtsMonotonicTime mtime; - struct timespec ts; + struct timespec mts, sts; + int mres, sres, merr, serr; - if (clock_gettime(MONOTONIC_CLOCK_ID,&ts) != 0) { - int err = errno; - char *errstr = err ? strerror(err) : "unknown"; + mres = clock_gettime(MONOTONIC_CLOCK_ID, &mts); + merr = errno; + sres = clock_gettime(WALL_CLOCK_ID, &sts); + serr = errno; + + if (mres != 0) { + char *errstr = merr ? strerror(merr) : "unknown"; erl_exit(ERTS_ABORT_EXIT, "clock_gettime(%s, _) failed: %s (%d)\n", - MONOTONIC_CLOCK_ID_STR, errstr, err); - + MONOTONIC_CLOCK_ID_STR, errstr, merr); } - mtime = (ErtsMonotonicTime) ts.tv_sec; - mtime *= (ErtsMonotonicTime) 1000*1000*1000; - mtime += (ErtsMonotonicTime) ts.tv_nsec; - return mtime; + if (sres != 0) { + char *errstr = serr ? strerror(serr) : "unknown"; + erl_exit(ERTS_ABORT_EXIT, + "clock_gettime(%s, _) failed: %s (%d)\n", + WALL_CLOCK_ID_STR, errstr, serr); + } + + *mtimep = timespec2montime(&mts); + *stimep = (ErtsSystemTime) timespec2montime(&sts); } +#endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ + #if defined(__linux__) static ErtsMonotonicTime clock_gettime_monotonic_verified(void) { - ErtsMonotonicTime mtime; - - mtime = clock_gettime_monotonic(); + ErtsMonotonicTime mtime = posix_clock_gettime(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR); erts_smp_mtx_lock(&internal_state.w.f.mtx); if (mtime < internal_state.w.f.last_delivered) @@ -455,66 +473,301 @@ static ErtsMonotonicTime clock_gettime_monotonic_verified(void) return mtime; } +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) + +static void clock_gettime_times_verified(ErtsMonotonicTime *mtimep, + ErtsSystemTime *stimep) +{ + posix_clock_gettime_times(mtimep, stimep); + + erts_smp_mtx_lock(&internal_state.w.f.mtx); + if (*mtimep < internal_state.w.f.last_delivered) + *mtimep = internal_state.w.f.last_delivered; + else + internal_state.w.f.last_delivered = *mtimep; + erts_smp_mtx_unlock(&internal_state.w.f.mtx); +} + +#endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ + static ErtsMonotonicTime clock_gettime_monotonic_raw(void) { - return clock_gettime_monotonic(); + return posix_clock_gettime(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR); } +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) + +static void clock_gettime_times_raw(ErtsMonotonicTime *mtimep, + ErtsSystemTime *stimep) +{ + posix_clock_gettime_times(mtimep, stimep); +} + +#endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ + #else /* !defined(__linux__) */ ErtsMonotonicTime erts_os_monotonic_time(void) { - return clock_gettime_monotonic(); + return posix_clock_gettime(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR); +} + +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) + +static void erts_os_times(ErtsMonotonicTime *mtimep, + ErtsSystemTime *stimep) +{ + posix_clock_gettime_times(mtimep, stimep); } +#endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ + #endif /* !defined(__linux__) */ +#define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ + ErtsSysHrTime erts_sys_hrtime(void) { - return (ErtsSysHrTime) clock_gettime_monotonic(); + return (ErtsSysHrTime) posix_clock_gettime(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR); } -#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) +#endif /* defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */ -#include -#include +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * MACH clock_get_time() * +\* */ -ErtsMonotonicTime erts_os_monotonic_time(void) +#if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ + || defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) + +#ifdef ERTS_HAVE_MACH_CLOCK_GETRES + +static Sint64 +mach_clock_getres(clock_id_t clkid, char *clkid_str) { - ErtsMonotonicTime mtime; - kern_return_t res; + mach_port_t task; + host_name_port_t host; + natural_t attr[1]; + kern_return_t kret; + clock_serv_t clk_srv; + mach_msg_type_number_t cnt; + + host = mach_host_self(); + kret = host_get_clock_service(host, clkid, &clk_srv); + if (kret != KERN_SUCCESS) { + erl_exit(ERTS_ABORT_EXIT, + "host_get_clock_service(_, %s, _) failed\n", + clkid_str); + } + + cnt = sizeof(attr); + kret = clock_get_attributes(clk_srv, CLOCK_GET_TIME_RES, (clock_attr_t) attr, &cnt); + if (kret != KERN_SUCCESS) { + erl_exit(ERTS_ABORT_EXIT, + "clock_get_attributes(%s, _) failed\n", + clkid_str); + } + task = mach_task_self(); + mach_port_deallocate(task, host); + mach_port_deallocate(task, clk_srv); + + return (Sint64) attr[0]; +} + +#endif /* ERTS_HAVE_MACH_CLOCK_GETRES */ + +static ERTS_INLINE Sint64 +mach_clock_gettime(clock_id_t clkid, char *clkid_str) +{ + Sint64 time; + mach_port_t task; + host_name_port_t host; + kern_return_t kret; clock_serv_t clk_srv; mach_timespec_t time_spec; - int err; - host_get_clock_service(mach_host_self(), - MONOTONIC_CLOCK_ID, - &clk_srv); + host = mach_host_self(); + kret = host_get_clock_service(host, clkid, &clk_srv); + if (kret != KERN_SUCCESS) { + erl_exit(ERTS_ABORT_EXIT, + "host_get_clock_service(_, %s, _) failed\n", + clkid_str); + } errno = 0; - res = clock_get_time(clk_srv, &time_spec); - err = errno; - mach_port_deallocate(mach_task_self(), clk_srv); - if (res != KERN_SUCCESS) { - char *errstr = err ? strerror(err) : "unknown"; + kret = clock_get_time(clk_srv, &time_spec); + if (kret != KERN_SUCCESS) { + erl_exit(ERTS_ABORT_EXIT, + "clock_get_time(%s, _) failed\n", + clkid_str); + } + task = mach_task_self(); + mach_port_deallocate(task, host); + mach_port_deallocate(task, clk_srv); + + time = (Sint64) time_spec.tv_sec; + time *= (Sint64) 1000*1000*1000; + time += (Sint64) time_spec.tv_nsec; + return time; +} + +#endif /* defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ + || defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) */ + +#if defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) + +#define ERTS_HAVE_ERTS_OS_TIMES_IMPL__ + +ErtsSystemTime +erts_os_system_time(void) +{ + ErtsSystemTime stime; + stime = (ErtsSystemTime) mach_clock_gettime(WALL_CLOCK_ID, + WALL_CLOCK_ID_STR); +#if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) + return stime; +#else + return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000); +#endif +} + +#endif /* defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) */ + +#if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) + +ErtsMonotonicTime +erts_os_monotonic_time(void) +{ + return (ErtsMonotonicTime) mach_clock_gettime(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR); +} + +#define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ + +ErtsSysHrTime +erts_sys_hrtime(void) +{ + return (ErtsMonotonicTime) mach_clock_gettime(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR); +} + +#if defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) + +#define ERTS_HAVE_ERTS_OS_TIMES_IMPL__ + +void +erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) +{ + ErtsMonotonicTime mtime; + ErtsSystemTime stime; + mach_port_t task; + host_name_port_t host; + kern_return_t mkret, skret; + clock_serv_t mclk_srv, sclk_srv; + mach_timespec_t mon_time_spec, sys_time_spec; + + host = mach_host_self(); + mkret = host_get_clock_service(host, MONOTONIC_CLOCK_ID, &mclk_srv); + skret = host_get_clock_service(host, WALL_CLOCK_ID, &sclk_srv); + if (mkret != KERN_SUCCESS) { + erl_exit(ERTS_ABORT_EXIT, + "host_get_clock_service(_, %s, _) failed\n", + MONOTONIC_CLOCK_ID); + } + if (skret != KERN_SUCCESS) { erl_exit(ERTS_ABORT_EXIT, - "clock_get_time(%s, _) failed: %s (%d)\n", - MONOTONIC_CLOCK_ID_STR, errstr, err); + "host_get_clock_service(_, %s, _) failed\n", + WALL_CLOCK_ID); } + mkret = clock_get_time(mclk_srv, &mon_time_spec); + skret = clock_get_time(sclk_srv, &sys_time_spec); + if (mkret != KERN_SUCCESS) { + erl_exit(ERTS_ABORT_EXIT, + "clock_get_time(%s, _) failed\n", + MONOTONIC_CLOCK_ID); + } + if (skret != KERN_SUCCESS) { + erl_exit(ERTS_ABORT_EXIT, + "clock_get_time(%s, _) failed\n", + WALL_CLOCK_ID); + } + task = mach_task_self(); + mach_port_deallocate(task, host); + mach_port_deallocate(task, mclk_srv); + mach_port_deallocate(task, sclk_srv); - mtime = (ErtsMonotonicTime) time_spec.tv_sec; + mtime = (ErtsMonotonicTime) mon_time_spec.tv_sec; mtime *= (ErtsMonotonicTime) 1000*1000*1000; - mtime += (ErtsMonotonicTime) time_spec.tv_nsec; - return mtime; + mtime += (ErtsMonotonicTime) mon_time_spec.tv_nsec; + stime = (ErtsSystemTime) sys_time_spec.tv_sec; + stime *= (ErtsSystemTime) 1000*1000*1000; + stime += (ErtsSystemTime) sys_time_spec.tv_nsec; + *mtimep = mtime; + *stimep = stime; +} + +#endif /* defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) */ + +#endif /* defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Solaris gethrtime() - OS monotonic time * +\* */ + +#if defined(OS_MONOTONIC_TIME_USING_GETHRTIME) + +ErtsMonotonicTime erts_os_monotonic_time(void) +{ + return (ErtsMonotonicTime) gethrtime(); } +#define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ + ErtsSysHrTime erts_sys_hrtime(void) { - return (ErtsSysHrTime) erts_os_monotonic_time(); + return (ErtsSysHrTime) gethrtime(); } -#elif defined(OS_MONOTONIC_TIME_USING_TIMES) +#endif /* defined(OS_MONOTONIC_TIME_USING_GETHRTIME) */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * gettimeofday() - OS system time * +\* */ + +#if defined(OS_SYSTEM_TIME_GETTIMEOFDAY) + +ErtsSystemTime +erts_os_system_time(void) +{ + ErtsSystemTime stime; + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) { + int err = errno; + char *errstr = err ? strerror(err) : "unknown"; + erl_exit(ERTS_ABORT_EXIT, + "gettimeofday(_, NULL) failed: %s (%d)\n", + errstr, err); + } + + stime = (ErtsSystemTime) tv.tv_sec; + stime *= (ErtsSystemTime) 1000*1000; + stime += (ErtsSystemTime) tv.tv_usec; + + return adj_stime_time_unit(stime, (Uint32) 1000*1000); +} + +#endif /* defined(OS_SYSTEM_TIME_GETTIMEOFDAY) */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * times() - OS monotonic time * +\* */ + +#if defined(OS_MONOTONIC_TIME_USING_TIMES) ErtsMonotonicTime erts_os_monotonic_time(void) @@ -526,29 +779,35 @@ erts_os_monotonic_time(void) ticks) << internal_state.r.o.times_shift; } -# define ERTS_NEED_ERTS_SYS_HRTIME_FALLBACK - -#else /* !defined(OS_MONOTONIC_TIME_USING_TIMES) */ -/* No os-monotonic-time */ -# define ERTS_NEED_ERTS_SYS_HRTIME_FALLBACK #endif -#ifdef ERTS_NEED_ERTS_SYS_HRTIME_FALLBACK +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Fallbacks * +\* */ + +#ifndef ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ ErtsSysHrTime erts_sys_hrtime(void) { - ErtsSysHrTime time; - struct timeval tv; - gettimeofday(&tv); - time = (ErtsSysHrTime) tv.tv_sec; - time *= (ErtsSysHrTime) 1000*1000*1000; - time += ((ErtsSysHrTime) tv.tv_usec)*1000; - return time; + return (ErtsSysHrTime) erts_os_system_time(); +} + +#endif + +#if !defined(ERTS_HAVE_ERTS_OS_TIMES_IMPL__) \ + && defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) + +void +erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) +{ + *mtimep = erts_os_monotonic_time(); + *stimep = erts_os_system_time(); } #endif +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifdef HAVE_GETHRVTIME_PROCFS_IOCTL diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index d214ba002c..7c3f35c2f2 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -193,6 +193,7 @@ ErtsSystemTime erts_os_system_time(void); struct erts_sys_time_read_only_data__ { ErtsMonotonicTime (*os_monotonic_time)(void); + void (*os_times)(ErtsMonotonicTime *, ErtsSystemTime*); ErtsSysHrTime (*sys_hrtime)(void); }; @@ -208,6 +209,8 @@ typedef struct { extern ErtsSysTimeData__ erts_sys_time_data__; ERTS_GLB_INLINE ErtsMonotonicTime erts_os_monotonic_time(void); +ERTS_GLB_INLINE void erts_os_times(ErtsMonotonicTime *, + ErtsSystemTime *); ERTS_GLB_INLINE ErtsSysHrTime erts_sys_hrtime(void); #if ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -218,6 +221,12 @@ erts_os_monotonic_time(void) return (*erts_sys_time_data__.r.o.os_monotonic_time)(); } +ERTS_GLB_INLINE void +erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) +{ + return (*erts_sys_time_data__.r.o.os_times)(mtimep, stimep); +} + ERTS_GLB_INLINE ErtsSysHrTime erts_sys_hrtime(void) { diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index da9c4d2e29..7fe61084ce 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -119,6 +119,24 @@ __declspec(align(ASSUMED_CACHE_LINE_SIZE)) struct { __declspec(align(ASSUMED_CACHE_LINE_SIZE)) ErtsSysTimeData__ erts_sys_time_data__; + +static ERTS_INLINE ErtsSystemTime +SystemTime2MilliSec(SYSTEMTIME *stp) +{ + ErtsSystemTime stime; + FILETIME ft; + ULARGE_INTEGER ull; + + SystemTimeToFileTime(stp, &ft); + FILETIME_TO_ULI(ull,ft); + /* now in 100 ns units */ + stime = (ErtsSystemTime) ull.QuadPart; + stime -= (((ErtsSystemTime) EPOCH_JULIAN_DIFF) + * ((ErtsSystemTime) (10*1000*1000))); + stime /= (ErtsSystemTime) (10*1000); /* ms */ + return stime; +} + static ErtsMonotonicTime os_monotonic_time_qpc(void) { @@ -130,6 +148,30 @@ os_monotonic_time_qpc(void) return (ErtsMonotonicTime) pc.QuadPart; } +static void +os_times_qpc(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) +{ + LARGE_INTEGER pc; + SYSTEMTIME st; + ErtsSystemTime stime; + BOOL qpcr; + + qpcr = (*internal_state.r.o.pQueryPerformanceCounter)(&pc); + GetSystemTime(&st); + + if (!qpcr) + erl_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n"); + + *mtimep = (ErtsMonotonicTime) pc.QuadPart; + + stime = SystemTime2MilliSec(&st); + + *stimep = ((ErtsSystemTime) + erts_time_unit_conversion((Uint64) stime, + (Uint32) 1000, + internal_state.r.o.pcf)); +} + static Uint32 get_tick_count(void) { @@ -139,18 +181,64 @@ get_tick_count(void) static ErtsMonotonicTime os_monotonic_time_gtc32(void) { + ErtsMonotonicTime mtime; Uint32 ticks = (Uint32) GetTickCount(); ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, - tick_count); - return ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, - ticks) << 10; + ticks); + mtime = ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + ticks); + mtime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; + return mtime; +} + +static void +os_times_gtc32(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) +{ + SYSTEMTIME st; + ErtsSystemTime stime, mtime; + Uint32 ticks; + + ticks = (Uint32) GetTickCount(); + GetSystemTime(&st); + + ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + ticks); + mtime = ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + ticks); + mtime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; + *mtimep = mtime; + + stime = SystemTime2MilliSec(&st); + stime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; + *stimep = stime; + } static ErtsMonotonicTime os_monotonic_time_gtc64(void) { ULONGLONG ticks = (*internal_state.r.o.pGetTickCount64)(); - return (ErtsMonotonicTime) ticks << 10; + ErtsMonotonicTime mtime = (ErtsMonotonicTime) ticks; + return mtime << ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; +} + +static void +os_times_gtc64(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) +{ + SYSTEMTIME st; + ErtsSystemTime stime, mtime; + ULONGLONG ticks; + + ticks = (*internal_state.r.o.pGetTickCount64)(); + GetSystemTime(&st); + + mtime = (ErtsMonotonicTime) ticks; + mtime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; + *mtimep = mtime; + + stime = SystemTime2MilliSec(&st); + stime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; + *stimep = stime; } static ErtsSysHrTime @@ -197,6 +285,7 @@ void sys_init_time(ErtsSysInitTimeResult *init_resp) { ErtsMonotonicTime (*os_mtime_func)(void); + void (*os_times_func)(ErtsMonotonicTime *, ErtsSystemTime *); ErtsSysHrTime (*sys_hrtime_func)(void) = NULL; ErtsMonotonicTime time_unit; char kernel_dll_name[] = "kernel32"; @@ -220,6 +309,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) time_unit <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; internal_state.r.o.using_get_tick_count_time_unit = 1; os_mtime_func = os_monotonic_time_gtc32; + os_times_func = os_times_gtc32; init_resp->os_monotonic_time_info.extended = 1; erts_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd, get_tick_count, @@ -250,6 +340,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) time_unit <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; internal_state.r.o.using_get_tick_count_time_unit = 1; os_mtime_func = os_monotonic_time_gtc64; + os_times_func = os_times_gtc64; if (!sys_hrtime_func) sys_hrtime_func = sys_hrtime_gtc64; } @@ -292,10 +383,12 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) internal_state.r.o.using_get_tick_count_time_unit = 0; init_resp->os_monotonic_time_info.resolution = time_unit; os_mtime_func = os_monotonic_time_qpc; + os_times_func = os_times_qpc; } } erts_sys_time_data__.r.o.os_monotonic_time = os_mtime_func; + erts_sys_time_data__.r.o.os_times = os_times_func; init_resp->os_monotonic_time_unit = time_unit; init_resp->have_os_monotonic_time = 1; init_resp->sys_clock_resolution = 1; @@ -600,21 +693,11 @@ sys_gettimeofday(SysTimeval *tv) ErtsSystemTime erts_os_system_time(void) { - SYSTEMTIME t; - FILETIME ft; - ULARGE_INTEGER ull; + SYSTEMTIME st; ErtsSystemTime stime; - GetSystemTime(&t); - SystemTimeToFileTime(&t, &ft); - FILETIME_TO_ULI(ull,ft); - - /* now in 100 ns units */ - - stime = (ErtsSystemTime) ull.QuadPart; - stime -= (((ErtsSystemTime) EPOCH_JULIAN_DIFF) - * ((ErtsSystemTime) (10*1000*1000))); - stime /= (ErtsSystemTime) (10*1000); + GetSystemTime(&st); + stime = SystemTime2MilliSec(&st); if (internal_state.r.o.using_get_tick_count_time_unit) { stime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT; @@ -622,9 +705,9 @@ erts_os_system_time(void) } return ((ErtsSystemTime) - erts_time_unit_conversion(stime, + erts_time_unit_conversion((Uint64) stime, (Uint32) 1000, - (Uint32) ERTS_MONOTONIC_TIME_UNIT)); + internal_state.r.o.pcf)); } -- cgit v1.2.3 From 24cdb324390d99924af9f66104c0941afb5a7b08 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 25 Mar 2015 14:48:57 +0100 Subject: Skip not updated test-cases --- erts/emulator/test/node_container_SUITE.erl | 3 +++ erts/emulator/test/timer_bif_SUITE.erl | 6 ++++++ 2 files changed, 9 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 3f9b339ed2..9c1839811a 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -686,6 +686,9 @@ timer_refc(doc) -> "as they should for data stored in bif timers."]; timer_refc(suite) -> []; timer_refc(Config) when is_list(Config) -> + {skipped, "Test needs to be UPDATED for new timer implementation"}. + +timer_refc_test(Config) when is_list(Config) -> ?line RNode = {get_nodename(), 1}, ?line RPid = mk_pid(RNode, 4711, 2), ?line RPort = mk_port(RNode, 4711), diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index da19be3424..56a1cef761 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -232,6 +232,9 @@ read_timer(Config) when is_list(Config) -> cleanup(doc) -> []; cleanup(suite) -> []; cleanup(Config) when is_list(Config) -> + {skipped, "Test needs to be UPDATED for new timer implementation"}. + +cleanup_test(Config) when is_list(Config) -> ?line Mem = mem(), %% Timer on dead process ?line P1 = spawn(fun () -> ok end), @@ -420,6 +423,9 @@ evil_recv_timeouts(TOs, N, M) -> registered_process(doc) -> []; registered_process(suite) -> []; registered_process(Config) when is_list(Config) -> + {skipped, "Test needs to be UPDATED for new timer implementation"}. + +registered_process_test(Config) when is_list(Config) -> ?line Mem = mem(), %% Cancel ?line T1 = erlang:start_timer(500, ?MODULE, "hej"), -- cgit v1.2.3 From 047811828ebac3c4909b8056b8268205c34b693c Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 25 Mar 2015 15:56:35 +0100 Subject: Fix erts_sys_hrtime() fallback --- erts/emulator/sys/unix/sys_time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index e764607c25..ea021a27cf 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -790,7 +790,7 @@ erts_os_monotonic_time(void) ErtsSysHrTime erts_sys_hrtime(void) { - return (ErtsSysHrTime) erts_os_system_time(); + return (ErtsSysHrTime) ERTS_MONOTONIC_TO_NSEC(erts_os_system_time()); } #endif -- cgit v1.2.3 From 73b1ce18d362e5d192c2d8e31514a9ca1337aa2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 25 Mar 2015 16:06:38 +0100 Subject: erts: GC needs the size even if the frag is not referenced --- erts/emulator/beam/erl_map.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 4af8e6f274..5d965f3116 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -1008,6 +1008,7 @@ static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { n = n1 + n2 - unused_size; *thp = make_arityval(n); + mp_new->size = n; /* Reshape map to a hashmap if the map exceeds the limit */ @@ -1043,8 +1044,6 @@ static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { return res; } - mp_new->size = n; - return make_flatmap(mp_new); } -- cgit v1.2.3 From 15ce2d396e5c3e5fe7c775a18764db1f7f589e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 25 Mar 2015 17:17:19 +0100 Subject: erts: Refactor Map - use multiple values ESTACK_PUSHN --- erts/emulator/beam/erl_map.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 5d965f3116..ab40f47919 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -710,8 +710,7 @@ static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, } } - ESTACK_PUSH(stack, res); - ESTACK_PUSH(stack, 1 << slot); + ESTACK_PUSH2(stack,res,1 << slot); /* all of the other nodes .. */ elems = n - 2; /* remove first and last elements */ @@ -755,14 +754,12 @@ static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, slot = maskval(v, d); bp = 1 << slot; /* no more collisions */ - ESTACK_PUSH(stack,res); - ESTACK_PUSH(stack,bp); + ESTACK_PUSH2(stack,res,bp); } else if (d == dn) { /* no collisions at all */ slot = maskval(v, d); bp = 1 << slot; - ESTACK_PUSH(stack,res); - ESTACK_PUSH(stack,hdr | bp); + ESTACK_PUSH2(stack,res,hdr | bp); } else { /* dn < n, we have a drop and we are done * build nodes and subtree */ @@ -786,8 +783,7 @@ static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, hdr = ESTACK_POP(stack); d--; } - ESTACK_PUSH(stack, res); - ESTACK_PUSH(stack, hdr); + ESTACK_PUSH2(stack,res,hdr); } vp = v; @@ -1945,8 +1941,7 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, slot = hashmap_bitcount(hval & (bp - 1)); n = hashmap_bitcount(hval); - ESTACK_PUSH(*sp, n); - ESTACK_PUSH3(*sp, bp, slot, node); + ESTACK_PUSH4(*sp, n, bp, slot, node); /* occupied */ if (bp & hval) { @@ -1969,8 +1964,7 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, slot = hashmap_bitcount(hval & (bp - 1)); n = hashmap_bitcount(hval); - ESTACK_PUSH(*sp, n); - ESTACK_PUSH3(*sp, bp, slot, node); + ESTACK_PUSH4(*sp, n, bp, slot, node); /* occupied */ if (bp & hval) { @@ -2004,8 +1998,7 @@ insert_subnodes: cix = hashmap_index(chx); while (cix == ix) { - ESTACK_PUSH(*sp, 0); - ESTACK_PUSH3(*sp, 1 << ix, 0, MAP_HEADER_HAMT_NODE_BITMAP(0)); + ESTACK_PUSH4(*sp, 0, 1 << ix, 0, MAP_HEADER_HAMT_NODE_BITMAP(0)); size += HAMT_NODE_BITMAP_SZ(1); hx = hashmap_shift_hash(th,hx,lvl,key); chx = hashmap_shift_hash(th,chx,clvl,ckey); @@ -2193,8 +2186,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) { slot = hashmap_bitcount(hval & (bp - 1)); n = hashmap_bitcount(hval); - ESTACK_PUSH(stack, n); - ESTACK_PUSH3(stack, bp, slot, node); + ESTACK_PUSH4(stack, n, bp, slot, node); /* occupied */ if (bp & hval) { @@ -2213,8 +2205,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) { slot = hashmap_bitcount(hval & (bp - 1)); n = hashmap_bitcount(hval); - ESTACK_PUSH(stack, n); - ESTACK_PUSH3(stack, bp, slot, node); + ESTACK_PUSH4(stack, n, bp, slot, node); /* occupied */ if (bp & hval) { -- cgit v1.2.3 From 5d5543fb1e1a30e4572e90bac2ec194a61ca4e30 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 25 Mar 2015 19:55:43 +0100 Subject: erts: Optimize term_to_binary size estimation for tuples and maps containing ascii strings (lists). --- erts/emulator/beam/external.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index b0b232f185..4cd57d8aeb 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -4109,8 +4109,9 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, } for (i = 1; i <= arity; ++i) { if (is_list(ptr[i])) { - if ((m = is_string(obj)) && (m < MAX_STRING_LEN)) { + if ((m = is_string(ptr[i])) && (m < MAX_STRING_LEN)) { result += m + 2 + 1; + continue; } else { result += 5; } @@ -4131,31 +4132,29 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, /* push values first */ ptr = flatmap_get_values(mp); - i = size; - while(i--) { + for (i = size; i; i--, ptr++) { if (is_list(*ptr)) { if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) { result += m + 2 + 1; + continue; } else { result += 5; } } ESTACK_PUSH(s,*ptr); - ++ptr; } ptr = flatmap_get_keys(mp); - i = size; - while(i--) { + for (i = size; i; i--, ptr++) { if (is_list(*ptr)) { if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) { result += m + 2 + 1; + continue; } else { result += 5; } } ESTACK_PUSH(s,*ptr); - ++ptr; } goto outer_loop; } -- cgit v1.2.3 From d09ec9ee97816bd987e6322797b0e28c27f8590a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 25 Mar 2015 20:50:12 +0100 Subject: erts: Fix bug in term_to_binary size estimation for hamt --- erts/emulator/beam/external.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 4cd57d8aeb..8f0e19d27d 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -4172,8 +4172,12 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, case HAMT_SUBTAG_HEAD_ARRAY: ptr++; node_sz = 16; + result += 1 + 4; /* tag + 4 bytes size */ break; - case HAMT_SUBTAG_HEAD_BITMAP: ptr++; + case HAMT_SUBTAG_HEAD_BITMAP: + ptr++; + result += 1 + 4; /* tag + 4 bytes size */ + /*fall through*/ case HAMT_SUBTAG_NODE_BITMAP: node_sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); ASSERT(node_sz < 17); @@ -4183,11 +4187,38 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, } ptr++; - ESTACK_RESERVE(s, node_sz); + ESTACK_RESERVE(s, node_sz*2); while(node_sz--) { - ESTACK_FAST_PUSH(s, *ptr++); + if (is_list(*ptr)) { + Eterm* leaf = list_val(*ptr); + if (is_not_list(CAR(leaf))) { + ESTACK_FAST_PUSH(s, CAR(leaf)); + } + else { + if ((m = is_string(CAR(leaf))) && (m < MAX_STRING_LEN)) { + result += m + 2 + 1; + } else { + result += 5; + ESTACK_FAST_PUSH(s, CAR(leaf)); + } + } + if (is_not_list(CDR(leaf))) { + ESTACK_FAST_PUSH(s, CDR(leaf)); + } + else { + if ((m = is_string(CDR(leaf))) && (m < MAX_STRING_LEN)) { + result += m + 2 + 1; + } else { + result += 5; + ESTACK_FAST_PUSH(s, CDR(leaf)); + } + } + } + else { + ESTACK_FAST_PUSH(s, *ptr); + } + ptr++; } - result += 1 + 4; /* tag + 4 bytes size */ } break; -- cgit v1.2.3 From 2e016f6cde89bf471269c89f0fd2bb40422ce204 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 26 Mar 2015 12:07:00 +0100 Subject: Misc fixes --- erts/emulator/beam/erl_time_sup.c | 2 +- erts/emulator/beam/time.c | 2 +- erts/emulator/sys/unix/sys_time.c | 11 +---------- erts/emulator/sys/win32/sys_time.c | 2 +- erts/preloaded/ebin/erlang.beam | Bin 105852 -> 105860 bytes erts/preloaded/ebin/erts_internal.beam | Bin 12368 -> 12408 bytes erts/preloaded/src/erlang.erl | 2 +- erts/preloaded/src/erts_internal.erl | 12 ++++++------ 8 files changed, 11 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 8203436c85..bbdedcc128 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -1693,7 +1693,7 @@ static void send_time_offset_changed_notifications(void *new_offsetp) { ErtsMonotonicTime new_offset; - ErtsTimeOffsetMonitorInfo *to_mon_info; + ErtsTimeOffsetMonitorInfo *to_mon_info = NULL; /* Shut up faulty warning */ Uint no_monitors; char *tmp = NULL; diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index 3dfd3f79d4..2bdda6c8af 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -576,7 +576,7 @@ erts_cancel_timer(ErlTimer *p) { ErtsTimerWheel *tiw; ErlCancelProc cancel; - void *arg; + void *arg = NULL; /* Shut up faulty warning... */ tiw = get_timer_wheel(p); if (!tiw) diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index ea021a27cf..d535457977 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -410,11 +410,7 @@ erts_os_system_time(void) stime = (ErtsSystemTime) posix_clock_gettime(WALL_CLOCK_ID, WALL_CLOCK_ID_STR); -#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) - return stime; -#else return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000); -#endif } #endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ @@ -516,8 +512,7 @@ ErtsMonotonicTime erts_os_monotonic_time(void) #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) -static void erts_os_times(ErtsMonotonicTime *mtimep, - ErtsSystemTime *stimep) +void erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) { posix_clock_gettime_times(mtimep, stimep); } @@ -627,11 +622,7 @@ erts_os_system_time(void) ErtsSystemTime stime; stime = (ErtsSystemTime) mach_clock_gettime(WALL_CLOCK_ID, WALL_CLOCK_ID_STR); -#if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) - return stime; -#else return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000); -#endif } #endif /* defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) */ diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index 7fe61084ce..b292d9279e 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -302,7 +302,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) internal_state.w.f.last_tick_count = 0; init_resp->os_monotonic_time_info.func = "GetTickCount"; - init_resp->os_monotonic_time_info.locked_use = 1; + init_resp->os_monotonic_time_info.locked_use = 0; /* 10-16 ms resolution according to MicroSoft documentation */ init_resp->os_monotonic_time_info.resolution = 100; /* 10 ms */ time_unit = (ErtsMonotonicTime) 1000; diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index fcfcafa6da..b2550b938a 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 68fb357eb4..1663f044bf 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index c5dc40f5d1..60ca02861a 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1732,7 +1732,7 @@ start_timer(0, Dest, Msg) -> orelse (erlang:is_atom(Dest) andalso Dest /= undefined)), TimerRef = erlang:make_ref(), - try Dest ! {timeout, TimerRef, Msg} catch _:_ -> ok end, + _ = try Dest ! {timeout, TimerRef, Msg} catch _:_ -> ok end, TimerRef catch _:_ -> diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 2c701d75e4..3580fb542d 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -232,7 +232,7 @@ create_bif_timer() -> -spec erts_internal:access_bif_timer(Ref) -> Res when Ref :: reference(), - Res :: {reference(), pid(), reference()}. + Res :: {reference(), pid()} | 'undefined'. access_bif_timer(_Ref) -> erlang:nif_error(undefined). @@ -352,7 +352,7 @@ tsrv_handle_msg({cancel_timeout, BTR, From, Reply, Req, TRef}, false -> ok; _ -> - try From ! {cancel_timer, Req, false} catch _:_ -> ok end + _ = try From ! {cancel_timer, Req, false} catch _:_ -> ok end end, Nxt; [{Time, TRef} = TKey] -> @@ -370,7 +370,7 @@ tsrv_handle_msg({cancel_timeout, BTR, From, Reply, Req, TRef}, false -> ((1000*(Time - RcvTime)) div Unit) end, - try From ! {cancel_timer, Req, RT} catch _:_ -> ok end + _ = try From ! {cancel_timer, Req, RT} catch _:_ -> ok end end, case Time =:= Nxt of false -> @@ -389,14 +389,14 @@ tsrv_handle_msg({read_timeout, BTR, From, Req, TRef}, Nxt) -> case ets:lookup(RTab, TRef) of [] -> - try From ! {read_timer, Req, false} catch _:_ -> ok end; + _ = try From ! {read_timer, Req, false} catch _:_ -> ok end; [{Time, TRef}] -> RcvTime = erlang:monotonic_time(), RT = case Time =< RcvTime of true -> 0; false -> (1000*(Time - RcvTime)) div Unit end, - try From ! {read_timer, Req, RT} catch _:_ -> ok end + _ = try From ! {read_timer, Req, RT} catch _:_ -> ok end end, Nxt; tsrv_handle_msg({'DOWN', TRef, process, _, _}, @@ -456,6 +456,6 @@ tsrv_handle_timeout(CallTime, #tsrv_state{rtab = RTab, end, ets:delete(TTab, TKey), ets:delete(RTab, TRef), - try Proc ! Msg catch _:_ -> ok end, + _ = try Proc ! Msg catch _:_ -> ok end, tsrv_handle_timeout(CallTime, S) end. -- cgit v1.2.3 From d43474b527573aed9003f65e201375de2ce0a0d5 Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Thu, 26 Mar 2015 15:07:05 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 56328 -> 56328 bytes erts/preloaded/ebin/erlang.beam | Bin 106120 -> 106120 bytes erts/preloaded/ebin/erts_internal.beam | Bin 12808 -> 12808 bytes erts/preloaded/ebin/init.beam | Bin 49752 -> 49756 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1460 -> 1468 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1340 -> 1340 bytes erts/preloaded/ebin/prim_file.beam | Bin 44892 -> 44904 bytes erts/preloaded/ebin/prim_inet.beam | Bin 73128 -> 73092 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23424 -> 23416 bytes erts/preloaded/ebin/zlib.beam | Bin 14160 -> 14176 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index c8ec111e57..df768f9ed6 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 6baa3f9a13..3478a80dd4 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index dca75fde95..9ed45b34bf 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index f196952ef2..7361139cde 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 4d22d8bace..4af9d233b5 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index efc8347b6e..7c0b49235e 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 6c49b5185e..00babefbb4 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index f58ee4b4d5..6640a29c62 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 73be297bbb..3d6f1548d0 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 9eaf8b9e59..3224546179 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From be6af527e25ed548a392c78ea54942555bed94c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 26 Mar 2015 15:20:44 +0100 Subject: erts: Fix missing case in make_internal_hash --- erts/emulator/beam/utils.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index cb4ef2b376..8fc8962e4f 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1705,6 +1705,7 @@ make_internal_hash(Eterm term) } goto pop_next; } + case HAMT_SUBTAG_HEAD_ARRAY: case HAMT_SUBTAG_HEAD_BITMAP: size = *ptr++; UINT32_HASH(size, HCONST_16); -- cgit v1.2.3 From 4d262c2339b2fd9c83a8e1fe005b3895da64fc6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 26 Mar 2015 15:19:21 +0100 Subject: erts: Strengthen Maps tests --- erts/emulator/test/map_SUITE.erl | 760 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 751 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 228832ac0a..33bf415480 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -21,10 +21,12 @@ ]). -export([ - t_build_and_match_literals/1, - t_update_literals/1,t_match_and_update_literals/1, + t_build_and_match_literals/1, t_build_and_match_literals_large/1, + t_update_literals/1, t_update_literals_large/1, + t_match_and_update_literals/1, t_match_and_update_literals_large/1, t_update_map_expressions/1, - t_update_assoc/1,t_update_exact/1, + t_update_assoc/1, t_update_assoc_large/1, + t_update_exact/1, t_update_exact_large/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, @@ -82,10 +84,12 @@ suite() -> []. all() -> [ - t_build_and_match_literals, - t_update_literals, t_match_and_update_literals, + t_build_and_match_literals, t_build_and_match_literals_large, + t_update_literals, t_update_literals_large, + t_match_and_update_literals, t_match_and_update_literals_large, t_update_map_expressions, - t_update_assoc,t_update_exact, + t_update_assoc, t_update_assoc_large, + t_update_exact, t_update_exact_large, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, t_map_equal, t_map_compare, @@ -164,6 +168,461 @@ t_build_and_match_literals(Config) when is_list(Config) -> {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), ok. +t_build_and_match_literals_large(Config) when is_list(Config) -> + % normal non-repeating + M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" }), + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M0, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M0, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M0, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M0, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M0, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M0, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M0, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M0, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M0, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M0, + + 60 = map_size(M0), + 60 = maps:size(M0), + + % with repeating + M1 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 10=>na0,20=>nb0,30=>"nc0","40"=>"nd0",<<"50">>=>"ne0",{["00"]}=>"n10", + 11=>na1,21=>nb1,31=>"nc1","41"=>"nd1",<<"51">>=>"ne1",{["01"]}=>"n11", + 12=>na2,22=>nb2,32=>"nc2","42"=>"nd2",<<"52">>=>"ne2",{["02"]}=>"n12", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + + 13=>na3,23=>nb3,33=>"nc3","43"=>"nd3",<<"53">>=>"ne3",{["03"]}=>"n13", + 14=>na4,24=>nb4,34=>"nc4","44"=>"nd4",<<"54">>=>"ne4",{["04"]}=>"n14", + + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" }), + + #{10:=na0,20:=nb0,30:="nc0","40":="nd0",<<"50">>:="ne0",{["00"]}:="n10"} = M1, + #{11:=na1,21:=nb1,31:="nc1","41":="nd1",<<"51">>:="ne1",{["01"]}:="n11"} = M1, + #{12:=na2,22:=nb2,32:="nc2","42":="nd2",<<"52">>:="ne2",{["02"]}:="n12"} = M1, + #{13:=na3,23:=nb3,33:="nc3","43":="nd3",<<"53">>:="ne3",{["03"]}:="n13"} = M1, + #{14:=na4,24:=nb4,34:="nc4","44":="nd4",<<"54">>:="ne4",{["04"]}:="n14"} = M1, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M1, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M1, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M1, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M1, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M1, + + 60 = map_size(M1), + 60 = maps:size(M1), + + % with floats + + M2 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9"}), + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2, + + #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2, + #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2, + #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2, + #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2, + #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2, + + #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2, + #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2, + #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2, + #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2, + #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2, + + 90 = map_size(M2), + 90 = maps:size(M2), + + % with bignums + M3 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9", + + 36893488147419103232=>big1, 73786976294838206464=>big2, + 147573952589676412928=>big3, 18446744073709551616=>big4, + 4294967296=>big5, 8589934592=>big6, + 4294967295=>big7, 67108863=>big8 + }), + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, + + #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3, + #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3, + #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3, + #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3, + #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3, + + #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, + + #{36893488147419103232:=big1,67108863:=big8,"45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{147573952589676412928:=big3,8589934592:=big6,"46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{4294967296:=big5,18446744073709551616:=big4,"47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{4294967295:=big7,73786976294838206464:=big2,"48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + + 98 = map_size(M3), + 98 = maps:size(M3), + + %% with maps + + M4 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9", + + #{ one => small, map => key } => "small map key 1", + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M4, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M4, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M4, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M4, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M4, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M4, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M4, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M4, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M4, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M4, + + #{ #{ one => small, map => key } := "small map key 1", + #{ second => small, map => key } := "small map key 2", + #{ third => small, map => key } := "small map key 3" } = M4, + + #{ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 2" } = M4, + + + #{ 15:=V1,25:=b5,35:=V2,"45":="d5",<<"55">>:=V3,{["05"]}:="15", + #{ one => small, map => key } := "small map key 1", + #{ second => small, map => key } := V4, + #{ third => small, map => key } := "small map key 3", + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := V5 } = M4, + + a5 = V1, + "c5" = V2, + "e5" = V3, + "small map key 2" = V4, + "large map key 1" = V5, + + 95 = map_size(M4), + 95 = maps:size(M4), + + % call for value + + M5 = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10", + 11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11", + 12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12", + 13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13", + 14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14", + + 15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15", + 16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16", + 17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17", + 18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18", + 19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19", + + 10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"), + 11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"), + 12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"), + 13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"), + 14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"), + + 15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"), + 16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"), + 17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"), + 18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"), + 19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"), + + #{ one => small, map => key } => id("small map key 1"), + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }), + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M5, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M5, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M5, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M5, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M5, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M5, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M5, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M5, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M5, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M5, + + #{ #{ one => small, map => key } := "small map key 1", + #{ second => small, map => key } := "small map key 2", + #{ third => small, map => key } := "small map key 3" } = M5, + + #{ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 2" } = M5, + + 95 = map_size(M5), + 95 = maps:size(M5), + + %% remember + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M0, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M0, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M0, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M0, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M0, + + #{10:=na0,20:=nb0,30:="nc0","40":="nd0",<<"50">>:="ne0",{["00"]}:="n10"} = M1, + #{11:=na1,21:=nb1,31:="nc1","41":="nd1",<<"51">>:="ne1",{["01"]}:="n11"} = M1, + #{12:=na2,22:=nb2,32:="nc2","42":="nd2",<<"52">>:="ne2",{["02"]}:="n12"} = M1, + #{13:=na3,23:=nb3,33:="nc3","43":="nd3",<<"53">>:="ne3",{["03"]}:="n13"} = M1, + #{14:=na4,24:=nb4,34:="nc4","44":="nd4",<<"54">>:="ne4",{["04"]}:="n14"} = M1, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M1, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M1, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M1, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M1, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M1, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2, + + #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2, + #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2, + #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2, + #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2, + #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, + + #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3, + #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3, + #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3, + #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3, + #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3, + + #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, + + #{36893488147419103232:=big1,67108863:=big8,"45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{147573952589676412928:=big3,8589934592:=big6,"46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{4294967296:=big5,18446744073709551616:=big4,"47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{4294967295:=big7,73786976294838206464:=big2,"48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + + ok. + t_map_size(Config) when is_list(Config) -> 0 = map_size(id(#{})), @@ -201,6 +660,64 @@ map_is_size(M,N) when map_size(M) =:= N -> true; map_is_size(_,_) -> false. % test map updates without matching +t_update_literals_large(Config) when is_list(Config) -> + Map = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10", + 11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11", + 12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12", + 13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13", + 14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14", + + 15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15", + 16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16", + 17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17", + 18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18", + 19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19", + + 10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"), + 11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"), + 12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"), + 13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"), + 14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"), + + 15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"), + 16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"), + 17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"), + 18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"), + 19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"), + + #{ one => small, map => key } => id("small map key 1"), + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }), + + #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [ + {"a","1"},{"b","2"},{"c","3"},{"d","4"} + ]), + ok. + t_update_literals(Config) when is_list(Config) -> Map = #{x=>1,y=>2,z=>3,q=>4}, #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [ @@ -208,13 +725,15 @@ t_update_literals(Config) when is_list(Config) -> ]), ok. + loop_update_literals_x_q(Map, []) -> Map; loop_update_literals_x_q(Map, [{X,Q}|Vs]) -> loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs). % test map updates with matching t_match_and_update_literals(Config) when is_list(Config) -> - Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1}, + Map = #{ x=>0,y=>"untouched",z=>"also untouched",q=>1, + #{ "one" => small, map => key } => "small map key 1" }, #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [ {1,2},{3,4},{5,6},{7,8} ]), @@ -228,8 +747,78 @@ t_match_and_update_literals(Config) when is_list(Config) -> #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number }, ok. +t_match_and_update_literals_large(Config) when is_list(Config) -> + Map = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10", + 11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11", + 12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12", + 13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13", + 14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14", + + 15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15", + 16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16", + 17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17", + 18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18", + 19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19", + + 10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"), + 11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"), + 12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"), + 13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"), + 14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"), + + 15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"), + 16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"), + 17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"), + 18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"), + 19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"), + + x=>0,y=>"untouched",z=>"also untouched",q=>1, + + #{ "one" => small, map => key } => id("small map key 1"), + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }), + + #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [ + {1,2},{3,4},{5,6},{7,8} + ]), + M0 = id(Map#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}), + M1 = id(Map#{}), + M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + M0 = M2, + + #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number }, + ok. + + loop_match_and_update_literals_x_q(Map, []) -> Map; -loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> +loop_match_and_update_literals_x_q(#{ q:=Q0, x:=X0, + #{ "one" => small, map => key } := "small map key 1" } = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). @@ -241,13 +830,15 @@ t_update_map_expressions(Config) when is_list(Config) -> #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 }, #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 }, + Ks = lists:seq($a,$z), + #{ "aa" := {$a,$a}, "ac":=41, "dc":=42 } = + (maps:from_list([{[K1,K2],{K1,K2}}|| K1 <- Ks, K2 <- Ks]))#{ "ac" := 41, "dc" => 42 }, %% Error cases, FIXME: should be 'badmap'? {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }), ok. - t_update_assoc(Config) when is_list(Config) -> M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), @@ -262,7 +853,75 @@ t_update_assoc(Config) when is_list(Config) -> %% Errors cases. BadMap = id(badmap), {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}), + ok. + + +t_update_assoc_large(Config) when is_list(Config) -> + M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9", + + #{ one => small, map => key } => "small map key 1", + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), + + M1 = M0#{1=>42,2=>100,4=>[a,b,c]}, + #{1:=42,2:=100,10.0:=fa0,4:=[a,b,c],25:=b5} = M1, + #{ 10:=43, 24:=b4, 15:=a5, 35:="c5", 2.0:=100, 13.0:=fa3, 4.0:=[a,b,c]} = + M0#{1.0=>float,10:=43,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, + + M2 = M0#{13.0=>new}, + #{10:=a0,20:=b0,13.0:=new,"40":="d0",<<"50">>:="e0"} = M2, + M2 = M0#{13.0:=wrong,13.0=>new}, + + %% Errors cases. + BadMap = id(badmap), + {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>M0}), ok. t_update_exact(Config) when is_list(Config) -> @@ -293,6 +952,89 @@ t_update_exact(Config) when is_list(Config) -> ok. +t_update_exact_large(Config) when is_list(Config) -> + M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9", + + #{ one => small, map => key } => "small map key 1", + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), + + + M1 = M0#{10:=42,<<"55">>:=100,10.0:=[a,b,c]}, + #{ 10:=42,<<"55">>:=100,{["05"]}:="15",10.0:=[a,b,c], + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1" } = M1, + + M1 = M0#{10:=wrong,10=>42,<<"55">>=>wrong,<<"55">>:=100,10.0:=[a,b,c]}, + + M2 = M0#{13.0:=new}, + #{10:=a0,20:=b0,13.0:=new} = M2, + M2 = M0#{13.0=>wrong,13.0:=new}, + + %% Errors cases. + {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + + ok. + + t_guard_bifs(Config) when is_list(Config) -> true = map_guard_head(#{a=>1}), false = map_guard_head([]), -- cgit v1.2.3 From 740f19448e936d9f03ceb50d27ec1a76749dda6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 27 Mar 2015 17:27:28 +0100 Subject: erts: Fix make_internal_hash for 0.0 vs -0.0 The internal_hash should produce the same hash value for identical terms, in this case 0.0 =:= -0.0. --- erts/emulator/beam/utils.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 8fc8962e4f..91f4accd30 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1894,10 +1894,12 @@ make_internal_hash(Eterm term) { FloatDef ff; GET_DOUBLE(term, ff); + if (ff.fd == 0.0) { + ff.fd = 0.0; /* ensure pos. 0.0 */ + } UINT32_HASH_2(ff.fw[0], ff.fw[1], HCONST_12); goto pop_next; } - default: erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term); } -- cgit v1.2.3 From 43b489d3901543251a6a1a49cb899b5b199864ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 27 Mar 2015 18:40:59 +0100 Subject: erts: Add tests for internal_hash --- erts/emulator/test/map_SUITE.erl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 228832ac0a..e28f8f2d0e 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -64,6 +64,7 @@ %% misc t_hashmap_balance/1, t_erts_internal_order/1, + t_erts_internal_hash/1, t_pdict/1, t_ets/1, t_dets/1, @@ -115,6 +116,7 @@ all() -> [ %% Other functions t_hashmap_balance, t_erts_internal_order, + t_erts_internal_hash, t_pdict, t_ets, t_tracing @@ -1549,6 +1551,18 @@ t_erts_internal_order(_Config) when is_list(_Config) -> 1 = maps:get(0,M1), ok. +t_erts_internal_hash(_Config) when is_list(_Config) -> + K1 = 0.0, + K2 = 0.0/-1, + + M1 = (maps:from_list([{I,I}||I<-lists:seq(1,32)]))#{ K1 => a, K2 => b }, + b = maps:get(K2,M1), + + M2 = (maps:from_list([{I,I}||I<-lists:seq(1,32)]))#{ K2 => a, K1 => b }, + b = maps:get(K1,M2), + + ok. + t_pdict(_Config) -> put(#{ a => b, b => a},#{ c => d}), -- cgit v1.2.3 From 9a36246ad24561619e182d488d4c8a6825011d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 27 Mar 2015 18:46:23 +0100 Subject: erts: Eliminate potential heap fragments after Map creation --- erts/emulator/beam/beam_emu.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 8fcdc72895..fdb84aae42 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6573,6 +6573,7 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) ptr = &Arg(4); if (n > 2*MAP_SMALL_MAP_LIMIT) { + Eterm res; if (HeapWordsLeft(p) < n) { erts_garbage_collect(p, n, reg, Arg(2)); } @@ -6589,7 +6590,15 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) p->htop = mhp; factory.p = p; - return erts_hashmap_from_array(&factory, thp, n/2, 0); + res = erts_hashmap_from_array(&factory, thp, n/2, 0); + if (p->mbuf) { + Uint live = Arg(2); + reg[live] = res; + erts_garbage_collect(p, 0, reg, live+1); + res = reg[live]; + E = p->stop; + } + return res; } if (HeapWordsLeft(p) < need) { -- cgit v1.2.3 From 3b9f9374aa4b667a2162ab2d9fbf933ae9b3decf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 30 Mar 2015 10:57:43 +0200 Subject: erts: Strengthen Maps tests --- erts/emulator/test/map_SUITE.erl | 199 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 190 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index a72c8dafe4..04c12d3e14 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -27,8 +27,11 @@ t_update_map_expressions/1, t_update_assoc/1, t_update_assoc_large/1, t_update_exact/1, t_update_exact_large/1, - t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, - t_guard_receive/1, t_guard_fun/1, + t_guard_bifs/1, + t_guard_sequence/1, t_guard_sequence_large/1, + t_guard_update/1, t_guard_update_large/1, + t_guard_receive/1, t_guard_receive_large/1, + t_guard_fun/1, t_list_comprehension/1, t_map_sort_literals/1, t_map_equal/1, @@ -91,8 +94,11 @@ all() -> [ t_update_map_expressions, t_update_assoc, t_update_assoc_large, t_update_exact, t_update_exact_large, - t_guard_bifs, t_guard_sequence, t_guard_update, - t_guard_receive,t_guard_fun, t_list_comprehension, + t_guard_bifs, + t_guard_sequence, t_guard_sequence_large, + t_guard_update, t_guard_update_large, + t_guard_receive, t_guard_receive_large, + t_guard_fun, t_list_comprehension, t_map_equal, t_map_compare, t_map_sort_literals, @@ -1066,12 +1072,82 @@ t_guard_sequence(Config) when is_list(Config) -> {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})), {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})), {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})), - + %% error case {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})), {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})), ok. +t_guard_sequence_large(Config) when is_list(Config) -> + M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00",03]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01",03]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02",03]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03",03]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04",03]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05",03]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06",03]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07",03]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08",03]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09",03]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9", + + #{ one => small, map => key } => "small map key 1", + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), + + {1, "a"} = map_guard_sequence_1(M0#{seq=>1,val=>id("a")}), + {2, "b"} = map_guard_sequence_1(M0#{seq=>2,val=>id("b")}), + {3, "c"} = map_guard_sequence_1(M0#{seq=>3,val=>id("c")}), + {4, "d"} = map_guard_sequence_1(M0#{seq=>4,val=>id("d")}), + {5, "e"} = map_guard_sequence_1(M0#{seq=>5,val=>id("e")}), + + {1,M1} = map_guard_sequence_2(M1 = id(M0#{a=>3})), + {2,M2} = map_guard_sequence_2(M2 = id(M0#{a=>4, b=>4})), + {3,gg,M3} = map_guard_sequence_2(M3 = id(M0#{a=>gg, b=>4})), + {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(M0#{a=>sc, b=>3, c=>sc2})), + {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(M0#{a=>kk, b=>other, c=>sc2})), + + {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(M0#{seq=>6,val=>id("e")})), + {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(M0#{b=>5})), + ok. + + map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val}; map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val}; map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val}; @@ -1091,6 +1167,66 @@ t_guard_update(Config) when is_list(Config) -> second = map_guard_update(#{y=>old}, #{x=>second,y=>old}), ok. +t_guard_update_large(Config) when is_list(Config) -> + M0 = id(#{ 70=>a0,80=>b0,90=>"c0","40"=>"d0",<<"50">>=>"e0",{["00",03]}=>"10", + 71=>a1,81=>b1,91=>"c1","41"=>"d1",<<"51">>=>"e1",{["01",03]}=>"11", + 72=>a2,82=>b2,92=>"c2","42"=>"d2",<<"52">>=>"e2",{["02",03]}=>"12", + 73=>a3,83=>b3,93=>"c3","43"=>"d3",<<"53">>=>"e3",{["03",03]}=>"13", + 74=>a4,84=>b4,94=>"c4","44"=>"d4",<<"54">>=>"e4",{["04",03]}=>"14", + + 75=>a5,85=>b5,95=>"c5","45"=>"d5",<<"55">>=>"e5",{["05",03]}=>"15", + 76=>a6,86=>b6,96=>"c6","46"=>"d6",<<"56">>=>"e6",{["06",03]}=>"16", + 77=>a7,87=>b7,97=>"c7","47"=>"d7",<<"57">>=>"e7",{["07",03]}=>"17", + 78=>a8,88=>b8,98=>"c8","48"=>"d8",<<"58">>=>"e8",{["08",03]}=>"18", + 79=>a9,89=>b9,99=>"c9","49"=>"d9",<<"59">>=>"e9",{["09",03]}=>"19", + + 70.0=>fa0,80.0=>fb0,90.0=>"fc0", + 71.0=>fa1,81.0=>fb1,91.0=>"fc1", + 72.0=>fa2,82.0=>fb2,92.0=>"fc2", + 73.0=>fa3,83.0=>fb3,93.0=>"fc3", + 74.0=>fa4,84.0=>fb4,94.0=>"fc4", + + 75.0=>fa5,85.0=>fb5,95.0=>"fc5", + 76.0=>fa6,86.0=>fb6,96.0=>"fc6", + 77.0=>fa7,87.0=>fb7,97.0=>"fc7", + 78.0=>fa8,88.0=>fb8,98.0=>"fc8", + 79.0=>fa9,89.0=>fb9,99.0=>"fc9", + + #{ one => small, map => key } => "small map key 1", + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), + + + error = map_guard_update(M0#{},M0#{}), + first = map_guard_update(M0#{},M0#{x=>first}), + second = map_guard_update(M0#{y=>old}, M0#{x=>second,y=>old}), + ok. + + map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first; map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second; map_guard_update(_, _) -> error. @@ -1120,6 +1256,42 @@ t_guard_receive(Config) when is_list(Config) -> done = call(Pid, done), ok. +-define(t_guard_receive_large_procs, 1500). + +t_guard_receive_large(Config) when is_list(Config) -> + M = lists:foldl(fun(_,#{procs := Ps } = M) -> + M#{ procs := Ps#{ spawn_link(fun() -> grecv_loop() end) => 0 }} + end, #{procs => #{}, done => 0}, lists:seq(1,?t_guard_receive_large_procs)), + lists:foreach(fun(Pid) -> + Pid ! {self(), hello} + end, maps:keys(maps:get(procs,M))), + ok = guard_receive_large_loop(M), + ok. + +guard_receive_large_loop(#{done := ?t_guard_receive_large_procs}) -> + ok; +guard_receive_large_loop(M) -> + receive + #{pid := Pid, msg := hello} -> + case M of + #{done := Count, procs := #{Pid := 150}} -> + Pid ! {self(), done}, + guard_receive_large_loop(M#{done := Count + 1}); + #{procs := #{Pid := Count} = Ps} -> + Pid ! {self(), hello}, + guard_receive_large_loop(M#{procs := Ps#{Pid := Count + 1}}) + end + end. + +grecv_loop() -> + receive + {_, done} -> + ok; + {Pid, hello} -> + Pid ! #{pid=>self(), msg=>hello}, + grecv_loop() + end. + call(Pid, M) -> Pid ! {self(), M}, receive {Pid, Res} -> Res end. @@ -1150,6 +1322,11 @@ guard_receive_loop() -> t_list_comprehension(Config) when is_list(Config) -> [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]], + + Ks = lists:seq($a,$z), + Ms = [#{[K1,K2]=>{K1,K2}} || K1 <- Ks, K2 <- Ks], + [#{"aa" := {$a,$a}},#{"ab":={$a,$b}}|_] = Ms, + [#{"zz" := {$z,$z}},#{"zy":={$z,$y}}|_] = lists:reverse(Ms), ok. t_guard_fun(Config) when is_list(Config) -> @@ -2045,19 +2222,23 @@ build_and_check(0, M0, _, Ks) -> {M0, Ks}; build_and_check(N, M0, F, Ks) -> K = build_key(F,N), M1 = maps:put(K,K,M0), - ok = check_keys_exist([K|Ks], M1), + ok = check_keys_exist([I||{I,_} <- [{K,M1}|Ks]], M1), M2 = maps:update(K,v,M1), v = maps:get(K,M2), - build_and_check(N-1,M1,F,[K|Ks]). + build_and_check(N-1,M1,F,[{K,M1}|Ks]). remove_and_check([],_) -> ok; -remove_and_check([K|Ks], M0) -> +remove_and_check([{K,Mc}|Ks], M0) -> K = maps:get(K,M0), true = maps:is_key(K,M0), + true = Mc =:= M0, + true = M0 == Mc, M1 = maps:remove(K,M0), + false = M1 =:= Mc, + false = Mc == M1, false = maps:is_key(K,M1), true = maps:is_key(K,M0), - ok = check_keys_exist(Ks,M1), + ok = check_keys_exist([I||{I,_} <- Ks],M1), error = maps:find(K,M1), remove_and_check(Ks, M1). -- cgit v1.2.3 From da14897a6363a5c39180e8e3edf0c1c8336cfeb5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 30 Mar 2015 17:02:05 +0200 Subject: erts: Suppress valgrind for bif_SUITE:erlang_halt which does a deliberate deref of null pointer which is caught by a SEGV signal handler to resume crash dumping. --- erts/emulator/beam/erl_bif_info.c | 20 +++++++++++++++----- erts/emulator/valgrind/suppress.patched.3.6.0 | 12 ++++++++++++ erts/emulator/valgrind/suppress.standard | 12 ++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b2658a1fd6..fa7de23f00 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3747,6 +3747,20 @@ BIF_RETTYPE erts_internal_is_system_process_1(BIF_ALIST_1) static erts_smp_atomic_t hipe_test_reschedule_flag; +#if defined(VALGRIND) && defined(__GNUC__) +/* Force noinline for valgrind suppression */ +static void broken_halt_test(Eterm bif_arg_2) __attribute__((noinline)); +#endif + +static void broken_halt_test(Eterm bif_arg_2) +{ + /* Ugly ugly code used by bif_SUITE:erlang_halt/1 */ +#if defined(ERTS_HAVE_TRY_CATCH) + erts_get_scheduler_data()->run_queue = NULL; +#endif + erl_exit(ERTS_DUMP_EXIT, "%T", bif_arg_2); +} + BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) { @@ -4040,11 +4054,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) } } else if (ERTS_IS_ATOM_STR("broken_halt", BIF_ARG_1)) { - /* Ugly ugly code used by bif_SUITE:erlang_halt/1 */ -#if defined(ERTS_HAVE_TRY_CATCH) - erts_get_scheduler_data()->run_queue = NULL; -#endif - erl_exit(ERTS_DUMP_EXIT, "%T", BIF_ARG_2); + broken_halt_test(BIF_ARG_2); } else if (ERTS_IS_ATOM_STR("unique_monotonic_integer_state", BIF_ARG_1)) { int res = erts_debug_set_unique_monotonic_integer_state(BIF_ARG_2); diff --git a/erts/emulator/valgrind/suppress.patched.3.6.0 b/erts/emulator/valgrind/suppress.patched.3.6.0 index f79e3ff634..16cecf2dba 100644 --- a/erts/emulator/valgrind/suppress.patched.3.6.0 +++ b/erts/emulator/valgrind/suppress.patched.3.6.0 @@ -362,3 +362,15 @@ fun:async_main ... } +{ +Deliberate invalid read by test case bif_SUITE:erlang_halt +Memcheck:Addr4 +... +fun:erts_print_scheduler_info +... +fun:erl_exit +fun:broken_halt_test +fun:erts_debug_set_internal_state_2 +fun:process_main +} + diff --git a/erts/emulator/valgrind/suppress.standard b/erts/emulator/valgrind/suppress.standard index b3c77119fb..a1f3f82364 100644 --- a/erts/emulator/valgrind/suppress.standard +++ b/erts/emulator/valgrind/suppress.standard @@ -330,3 +330,15 @@ fun:async_main ... } +{ +Deliberate invalid read by test case bif_SUITE:erlang_halt +Memcheck:Addr4 +... +fun:erts_print_scheduler_info +... +fun:erl_exit +fun:broken_halt_test +fun:erts_debug_set_internal_state_2 +fun:process_main +} + -- cgit v1.2.3 From e4da94bee94bdd7e0dbb2e5021ab4cc5f89f8256 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 30 Mar 2015 19:54:23 +0200 Subject: erts: Refactor encode_size_struct_int to handle the "start of list" case in one place and not seven. Note that this commit reverts (47d6fd3ccf35) back to using WSTACK and pushing raw pointers. We disable GC while yielding, so this should not be a problem. --- erts/emulator/beam/dist.c | 4 +- erts/emulator/beam/dist.h | 2 +- erts/emulator/beam/external.c | 187 ++++++++++++++++-------------------------- 3 files changed, 73 insertions(+), 120 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index bfecac1612..32f3cda4f5 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -712,7 +712,7 @@ void erts_dsend_context_dtor(Binary* ctx_bin) ErtsSendContext* ctx = ERTS_MAGIC_BIN_DATA(ctx_bin); switch (ctx->dss.phase) { case ERTS_DSIG_SEND_PHASE_MSG_SIZE: - DESTROY_SAVED_ESTACK(&ctx->dss.u.sc.estack); + DESTROY_SAVED_WSTACK(&ctx->dss.u.sc.wstack); break; case ERTS_DSIG_SEND_PHASE_MSG_ENCODE: DESTROY_SAVED_WSTACK(&ctx->dss.u.ec.wstack); @@ -1800,7 +1800,7 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx) erts_encode_dist_ext_size(ctx->ctl, ctx->flags, ctx->acmp, &ctx->data_size); if (is_value(ctx->msg)) { - ctx->u.sc.estack.start = NULL; + ctx->u.sc.wstack.wstart = NULL; ctx->u.sc.flags = ctx->flags; ctx->u.sc.level = 0; ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_SIZE; diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index 2a2ba0c83f..cd2cc0ef4a 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -282,7 +282,7 @@ typedef struct TTBSizeContext_ { int level; Uint result; Eterm obj; - ErtsEStack estack; + ErtsWStack wstack; } TTBSizeContext; typedef struct TTBEncodeContext_ { diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 2117dbec62..fe48298155 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1779,7 +1779,7 @@ static void ttb_context_destructor(Binary *context_bin) context->alive = 0; switch (context->state) { case TTBSize: - DESTROY_SAVED_ESTACK(&context->s.sc.estack); + DESTROY_SAVED_WSTACK(&context->s.sc.wstack); break; case TTBEncode: DESTROY_SAVED_WSTACK(&context->s.ec.wstack); @@ -1847,7 +1847,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla /* Setup enough to get started */ context->state = TTBSize; context->alive = 1; - context->s.sc.estack.start = NULL; + context->s.sc.wstack.wstart = NULL; context->s.sc.flags = flags; context->s.sc.level = level; } else { @@ -3962,51 +3962,35 @@ static int encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags, Sint *reds, Uint *res) { - DECLARE_ESTACK(s); + DECLARE_WSTACK(s); Uint m, i, arity; Uint result = 0; Sint r = 0; if (ctx) { - ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); + WSTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); r = *reds; - if (ctx->estack.start) { /* restore saved stack */ - ESTACK_RESTORE(s, &ctx->estack); + if (ctx->wstack.wstart) { /* restore saved stack */ + WSTACK_RESTORE(s, &ctx->wstack); result = ctx->result; obj = ctx->obj; } } - goto L_jump_start; +#define LIST_TAIL_OP ((0 << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER) +#define TERM_ARRAY_OP(N) (((N) << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER) +#define TERM_ARRAY_OP_DEC(OP) ((OP) - (1 << _TAG_PRIMARY_SIZE)) + + + for (;;) { + ASSERT(!is_header(obj)); - outer_loop: - while (!ESTACK_ISEMPTY(s)) { - obj = ESTACK_POP(s); - handle_popped_obj: - if (is_list(obj)) { - Eterm* cons = list_val(obj); - Eterm tl; - - tl = CDR(cons); - obj = CAR(cons); - ESTACK_PUSH(s, tl); - } else if (is_nil(obj)) { - result++; - goto outer_loop; - } else { - /* - * Other term (in the tail of a non-proper list or - * in a fun's environment). - */ - } - - L_jump_start: if (ctx && --r == 0) { *reds = r; ctx->obj = obj; ctx->result = result; - ESTACK_SAVE(s, &ctx->estack); + WSTACK_SAVE(s, &ctx->wstack); return -1; } switch (tag_val_def(obj)) { @@ -4089,69 +4073,43 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, result += m + 2 + 1; } else { result += 5; - goto handle_popped_obj; + WSTACK_PUSH2(s, (UWord)CDR(list_val(obj)), (UWord)LIST_TAIL_OP); + obj = CAR(list_val(obj)); + continue; /* big loop */ } break; case TUPLE_DEF: { Eterm* ptr = tuple_val(obj); - Uint i; arity = arityval(*ptr); if (arity <= 0xff) { result += 1 + 1; } else { result += 1 + 4; } - for (i = 1; i <= arity; ++i) { - if (is_list(ptr[i])) { - if ((m = is_string(ptr[i])) && (m < MAX_STRING_LEN)) { - result += m + 2 + 1; - continue; - } else { - result += 5; - } - } - ESTACK_PUSH(s,ptr[i]); + if (arity > 1) { + WSTACK_PUSH2(s, (UWord) (ptr + 2), + (UWord) TERM_ARRAY_OP(arity-1)); } - goto outer_loop; + else if (arity == 0) { + break; + } + obj = ptr[1]; + continue; /* big loop */ } - break; case MAP_DEF: if (is_flatmap(obj)) { flatmap_t *mp = (flatmap_t*)flatmap_val(obj); Uint size = flatmap_get_size(mp); - Uint i; - Eterm *ptr; result += 1 + 4; /* tag + 4 bytes size */ - /* push values first */ - ptr = flatmap_get_values(mp); - for (i = size; i; i--, ptr++) { - if (is_list(*ptr)) { - if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) { - result += m + 2 + 1; - continue; - } else { - result += 5; - } - } - ESTACK_PUSH(s,*ptr); + if (size) { + WSTACK_PUSH4(s, (UWord) flatmap_get_values(mp), + (UWord) TERM_ARRAY_OP(size), + (UWord) flatmap_get_keys(mp), + (UWord) TERM_ARRAY_OP(size)); } - - ptr = flatmap_get_keys(mp); - for (i = size; i; i--, ptr++) { - if (is_list(*ptr)) { - if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) { - result += m + 2 + 1; - continue; - } else { - result += 5; - } - } - ESTACK_PUSH(s,*ptr); - } - goto outer_loop; } else { Eterm *ptr; Eterm hdr; @@ -4178,35 +4136,13 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, } ptr++; - ESTACK_RESERVE(s, node_sz*2); + WSTACK_RESERVE(s, node_sz*2); while(node_sz--) { if (is_list(*ptr)) { - Eterm* leaf = list_val(*ptr); - if (is_not_list(CAR(leaf))) { - ESTACK_FAST_PUSH(s, CAR(leaf)); - } - else { - if ((m = is_string(CAR(leaf))) && (m < MAX_STRING_LEN)) { - result += m + 2 + 1; - } else { - result += 5; - ESTACK_FAST_PUSH(s, CAR(leaf)); - } - } - if (is_not_list(CDR(leaf))) { - ESTACK_FAST_PUSH(s, CDR(leaf)); - } - else { - if ((m = is_string(CDR(leaf))) && (m < MAX_STRING_LEN)) { - result += m + 2 + 1; - } else { - result += 5; - ESTACK_FAST_PUSH(s, CDR(leaf)); - } - } - } - else { - ESTACK_FAST_PUSH(s, *ptr); + WSTACK_FAST_PUSH(s, CAR(list_val(*ptr))); + WSTACK_FAST_PUSH(s, CDR(list_val(*ptr))); + } else { + WSTACK_FAST_PUSH(s, *ptr); } ptr++; } @@ -4262,25 +4198,13 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, result += 2 * (1 + 4); /* Index + Uniq */ result += 1 + (funp->num_free < 0x100 ? 1 : 4); } - for (i = 1; i < funp->num_free; i++) { - obj = funp->env[i]; - - if (is_not_list(obj)) { - /* Push any non-list terms on the stack */ - ESTACK_PUSH(s, obj); - } else { - /* Lists must be handled specially. */ - if ((m = is_string(obj)) && (m < MAX_STRING_LEN)) { - result += m + 2 + 1; - } else { - result += 5; - ESTACK_PUSH(s, obj); - } - } + if (funp->num_free > 1) { + WSTACK_PUSH2(s, (UWord) (funp->env + 1), + (UWord) TERM_ARRAY_OP(funp->num_free-1)); } if (funp->num_free != 0) { obj = funp->env[0]; - goto L_jump_start; + continue; /* big loop */ } break; } @@ -4303,11 +4227,40 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, erl_exit(1,"Internal data structure error (in encode_size_struct2)%x\n", obj); } + + if (WSTACK_ISEMPTY(s)) { + break; + } + obj = (Eterm) WSTACK_POP(s); + + if (is_header(obj)) { + switch (obj) { + case LIST_TAIL_OP: + obj = (Eterm) WSTACK_POP(s); + if (is_list(obj)) { + Eterm* cons = list_val(obj); + + WSTACK_PUSH2(s, (UWord)CDR(cons), (UWord)LIST_TAIL_OP); + obj = CAR(cons); + } + break; + + case TERM_ARRAY_OP(1): + obj = *(Eterm*)WSTACK_POP(s); + break; + default: { /* TERM_ARRAY_OP(N) when N > 1 */ + Eterm* ptr = (Eterm*) WSTACK_POP(s); + WSTACK_PUSH2(s, (UWord) (ptr+1), + (UWord) TERM_ARRAY_OP_DEC(obj)); + obj = *ptr; + } + } + } } - DESTROY_ESTACK(s); + WSTACK_DESTROY(s); if (ctx) { - ASSERT(ctx->estack.start == NULL); + ASSERT(ctx->wstack.wstart == NULL); *reds = r; } *res = result; -- cgit v1.2.3 From 450e5b610ac2de4817606c2966d9fb5a9850887a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 30 Mar 2015 21:45:42 +0200 Subject: erts: Optimize == and /= for unequal big maps Bail out as soon as we find a diff between maps if we are not interested in term order. --- erts/emulator/beam/erl_bif_lists.c | 2 +- erts/emulator/beam/erl_db_tree.c | 8 ++++---- erts/emulator/beam/erl_utils.h | 28 +++++++++++++++------------- erts/emulator/beam/utils.c | 9 +++++---- 4 files changed, 25 insertions(+), 22 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c index 820ed2385d..e006d57124 100644 --- a/erts/emulator/beam/erl_bif_lists.c +++ b/erts/emulator/beam/erl_bif_lists.c @@ -390,7 +390,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List) Eterm *tuple_ptr = tuple_val(term); if (pos <= arityval(*tuple_ptr)) { Eterm element = tuple_ptr[pos]; - if (CMP(Key, element) == 0) { + if (CMP_EQ(Key, element)) { return term; } } diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 577da35b75..d90af46659 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -1116,7 +1116,7 @@ static int db_select_tree(Process *p, DbTable *tbl, sc.all_objects = mpi.all_objects; if (!mpi.got_partial && mpi.some_limitation && - CMP(mpi.least,mpi.most) == 0) { + CMP_EQ(mpi.least,mpi.most)) { doit_select(tb,mpi.save_term,&sc,0 /* direction doesn't matter */); RET_TO_BIF(sc.accum,DB_ERROR_NONE); } @@ -1324,7 +1324,7 @@ static int db_select_count_tree(Process *p, DbTable *tbl, sc.all_objects = mpi.all_objects; if (!mpi.got_partial && mpi.some_limitation && - CMP(mpi.least,mpi.most) == 0) { + CMP_EQ(mpi.least,mpi.most)) { doit_select_count(tb,mpi.save_term,&sc,0 /* dummy */); RET_TO_BIF(erts_make_integer(sc.got,p),DB_ERROR_NONE); } @@ -1429,7 +1429,7 @@ static int db_select_chunk_tree(Process *p, DbTable *tbl, sc.all_objects = mpi.all_objects; if (!mpi.got_partial && mpi.some_limitation && - CMP(mpi.least,mpi.most) == 0) { + CMP_EQ(mpi.least,mpi.most)) { doit_select(tb,mpi.save_term,&sc, 0 /* direction doesn't matter */); if (sc.accum != NIL) { hp=HAlloc(p, 3); @@ -1673,7 +1673,7 @@ static int db_select_delete_tree(Process *p, DbTable *tbl, sc.mp = mpi.mp; if (!mpi.got_partial && mpi.some_limitation && - CMP(mpi.least,mpi.most) == 0) { + CMP_EQ(mpi.least,mpi.most)) { doit_select_delete(tb,mpi.save_term,&sc, 0 /* direction doesn't matter */); RET_TO_BIF(erts_make_integer(sc.accum,p),DB_ERROR_NONE); diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 7cb8972e29..6a28105cb9 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -167,24 +167,26 @@ int eq(Eterm, Eterm); #define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y)))) #if HALFWORD_HEAP -Sint erts_cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int); -#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,0) -#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,1) -#define CMP(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,0) -#define CMP_TERM(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,1) +Sint erts_cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int, int); +#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,0,0) +#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,1,0) +#define CMP(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,0,0) +#define CMP_TERM(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,1,0) +#define CMP_EQ_ONLY(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,0,1) #else -Sint cmp(Eterm, Eterm); -Sint erts_cmp(Eterm, Eterm, int); -#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp(A,B,0) -#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp(A,B,1) -#define CMP(A,B) erts_cmp(A,B,0) -#define CMP_TERM(A,B) erts_cmp(A,B,1) +Sint erts_cmp(Eterm, Eterm, int, int); +Sint cmp(Eterm a, Eterm b); +#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp(A,B,0,0) +#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp(A,B,1,0) +#define CMP(A,B) erts_cmp(A,B,0,0) +#define CMP_TERM(A,B) erts_cmp(A,B,1,0) +#define CMP_EQ_ONLY(A,B) erts_cmp(A,B,0,1) #endif #define cmp_lt(a,b) (CMP((a),(b)) < 0) #define cmp_le(a,b) (CMP((a),(b)) <= 0) -#define cmp_eq(a,b) (CMP((a),(b)) == 0) -#define cmp_ne(a,b) (CMP((a),(b)) != 0) +#define cmp_eq(a,b) (CMP_EQ_ONLY((a),(b)) == 0) +#define cmp_ne(a,b) (CMP_EQ_ONLY((a),(b)) != 0) #define cmp_ge(a,b) (CMP((a),(b)) >= 0) #define cmp_gt(a,b) (CMP((a),(b)) > 0) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 8fc8962e4f..889c217b0b 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2911,7 +2911,7 @@ static int cmp_atoms(Eterm a, Eterm b) */ Sint cmp(Eterm a, Eterm b) { - return erts_cmp(a, b, 0); + return erts_cmp(a, b, 0, 0); } #endif @@ -2920,9 +2920,10 @@ Sint cmp(Eterm a, Eterm b) * exact = 0 -> arith-based compare */ #if HALFWORD_HEAP -Sint erts_cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, int exact) +Sint erts_cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, + int exact, int eq_only) #else -Sint erts_cmp(Eterm a, Eterm b, int exact) +Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) #endif { #define PSTACK_TYPE struct erts_cmp_hashmap_state @@ -3742,7 +3743,7 @@ pop_next: return 0; not_equal: - if (!PSTACK_IS_EMPTY(hmap_stack)) { + if (!PSTACK_IS_EMPTY(hmap_stack) && !eq_only) { WSTACK_ROLLBACK(stack, PSTACK_TOP(hmap_stack)->wstack_rollback); goto pop_next; } -- cgit v1.2.3 From 8f7246a7c02a50561c171f3d91170a2af96eddbf Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 31 Mar 2015 12:13:57 +0200 Subject: erts: Optimize insert and delete for big maps Do fast path without bit count for full internal nodes. --- erts/emulator/beam/erl_map.c | 71 +++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 31 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index ab40f47919..5a70407e42 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -1937,26 +1937,31 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, case HAMT_SUBTAG_NODE_BITMAP: hval = MAP_HEADER_VAL(hdr); ix = hashmap_index(hx); - bp = 1 << ix; - slot = hashmap_bitcount(hval & (bp - 1)); - n = hashmap_bitcount(hval); + bp = 1 << ix; + if (hval == 0xffff) { + slot = ix; + n = 16; + } else { + slot = hashmap_bitcount(hval & (bp - 1)); + n = hashmap_bitcount(hval); + } + + ESTACK_PUSH4(*sp, n, bp, slot, node); + + if (!(bp & hval)) { /* not occupied */ + if (is_update) { + return 0; + } + size += HAMT_NODE_BITMAP_SZ(n+1); + goto unroll; + } + + hx = hashmap_shift_hash(th,hx,lvl,key); + node = ptr[slot+1]; + ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17); + size += HAMT_NODE_BITMAP_SZ(n); + break; - ESTACK_PUSH4(*sp, n, bp, slot, node); - - /* occupied */ - if (bp & hval) { - hx = hashmap_shift_hash(th,hx,lvl,key); - node = ptr[slot+1]; - ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17); - size += HAMT_NODE_BITMAP_SZ(n); - break; - } - /* not occupied */ - if (is_update) { - return 0; - } - size += HAMT_NODE_BITMAP_SZ(n+1); - goto unroll; case HAMT_SUBTAG_HEAD_BITMAP: hval = MAP_HEADER_VAL(hdr); ix = hashmap_index(hx); @@ -2183,21 +2188,25 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) { hval = MAP_HEADER_VAL(hdr); ix = hashmap_index(hx); bp = 1 << ix; - slot = hashmap_bitcount(hval & (bp - 1)); - n = hashmap_bitcount(hval); + if (hval == 0xffff) { + slot = ix; + n = 16; + } else if (bp & hval) { + slot = hashmap_bitcount(hval & (bp - 1)); + n = hashmap_bitcount(hval); + } else { + /* not occupied */ + goto not_found; + } ESTACK_PUSH4(stack, n, bp, slot, node); - /* occupied */ - if (bp & hval) { - hx = hashmap_shift_hash(th,hx,lvl,key); - node = ptr[slot+1]; - ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17); - size += HAMT_NODE_BITMAP_SZ(n); - break; - } - /* not occupied */ - goto not_found; + hx = hashmap_shift_hash(th,hx,lvl,key); + node = ptr[slot+1]; + ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17); + size += HAMT_NODE_BITMAP_SZ(n); + break; + case HAMT_SUBTAG_HEAD_BITMAP: hval = MAP_HEADER_VAL(hdr); ix = hashmap_index(hx); -- cgit v1.2.3 From 62870c998955e1498e71bfc90607885e96ecaa27 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 31 Mar 2015 12:24:04 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 93 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index af0d4d7377..a2b4ae49a4 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,98 @@

This document describes the changes made to the ERTS application.

+
Erts 6.4 + +
Fixed Bugs and Malfunctions + + +

+ Fix missing quotation in the LM_FIND_EMU_CC + autoconf macro which could cause build failures.

+

+ Own Id: OTP-12388

+
+ +

+ Fix erroneous printout of monitors in crashdump file.

+

+ Own Id: OTP-12537

+
+ +

+ The runtime system without SMP support could crash in the + BIF port_control/3 if the port that was being + accessed died during the call to the BIF.

+

+ Own Id: OTP-12544 Aux Id: Seq12777

+
+ +

+ Avoid corrupt oversized integer to be created from binary + matching. Instead throw system_limit exception which is + the correct behavior. A peculiar symptom of this bug was + that bitwise operations (band, bor, bxor) on such + oversized integers could return the empty list []. + Credit: Mikael Pettersson, Nico Kruber

+

+ Own Id: OTP-12556

+
+ +

+ A race condition when calling port_info/1 could + cause a memory fault has been fixed.

+

+ Own Id: OTP-12587

+
+ +

+ Fix comparison of exact terms. An overflow that could + cause faulty comparisons has been fixed. Comparison of + exact terms is exclusively used within Maps.

+

+ Own Id: OTP-12623

+
+ +

+ Fix bug in list_to_integer/1 for very long lists + that could cause VM crash.

+

+ Own Id: OTP-12624

+
+
+
+ + +
Improvements and New Features + + +

+ Introduced a runtime system internal 64-bit API for + atomic memory operations.

+

+ Own Id: OTP-12351

+
+ +

+ Add command line argument option for the initial size of + process dictionaries.

+

+ Use '+hpds <size>' to set initial process + dictionary size for spawned processes.

+

+ Own Id: OTP-12535 Aux Id: seq12809

+
+ +

+ Fix documentation on $char for Unicode

+

+ Own Id: OTP-12545

+
+
+
+ +
+
Erts 6.3.1
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index e4b071b090..abc9c0ba10 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.3.1 +VSN = 6.4 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From dbad08513df1ea3f3de47375a50d16551df6f208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 31 Mar 2015 14:49:02 +0200 Subject: erts: Fix size bug in maps:from_list/1 BIF The wrong size was imprinted on maps with deep hash key collisions. --- erts/emulator/beam/erl_map.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index ab40f47919..a8e8ad346b 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -89,7 +89,7 @@ static Eterm flatmap_from_validated_list(Process *p, Eterm list, Uint size); static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size); static Eterm hashmap_from_unsorted_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int reject_dupkeys); static Eterm hashmap_from_sorted_unique_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int is_root); -static Eterm hashmap_from_chunked_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int is_root); +static Eterm hashmap_from_chunked_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, Uint size, int is_root); static Eterm hashmap_info(Process *p, Eterm node); static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]); static int hxnodecmp(hxnode_t* a, hxnode_t* b); @@ -661,7 +661,7 @@ static Eterm hashmap_from_sorted_unique_array(ErtsHeapFactory* factory, ix++; } - res = hashmap_from_chunked_array(factory, hxns, elems, !lvl); + res = hashmap_from_chunked_array(factory, hxns, elems, n, !lvl); ERTS_FACTORY_HOLE_CHECK(factory); @@ -669,8 +669,8 @@ static Eterm hashmap_from_sorted_unique_array(ErtsHeapFactory* factory, } #define HALLOC_EXTRA 200 -static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, - hxnode_t *hxns, Uint n, int is_root) { +static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, hxnode_t *hxns, Uint n, + Uint size, int is_root) { Uint ix, d, dn, dc, slot, elems; Uint32 v, vp, vn, hdr; Uint bp, sz; @@ -840,7 +840,7 @@ static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, if (is_root) { *hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_HEAD_ARRAY : MAP_HEADER_HAMT_HEAD_BITMAP(hdr); - *hp++ = n; + *hp++ = size; } else { *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hdr); } -- cgit v1.2.3 From 2fc541ea6ecaf7dd8d98071ef82332fe67adfbef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 31 Mar 2015 15:08:40 +0200 Subject: erts: Try to test deep Maps collision Ensure maps:size/1 is correct. --- erts/emulator/test/map_SUITE.erl | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index a72c8dafe4..c7800a5540 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -644,6 +644,17 @@ t_map_size(Config) when is_list(Config) -> Ks = [build_key(fun(K) -> <<1,K:32,1>> end,I)||I<-lists:seq(1,100)], ok = build_and_check_size(Ks,0,#{}), + %% try deep collisions + %% statistically we get another subtree at 50k -> 100k elements + %% Try to be nice and don't use too much memory in the testcase, + + N = 500000, + Is = lists:seq(1,N), + N = map_size(maps:from_list([{I,I}||I<-Is])), + N = map_size(maps:from_list([{<>,I}||I<-Is])), + N = map_size(maps:from_list([{integer_to_list(I),I}||I<-Is])), + N = map_size(maps:from_list([{float(I),I}||I<-Is])), + %% Error cases. {'EXIT',{badarg,_}} = (catch map_size([])), {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), -- cgit v1.2.3 From ca94abe66652fb07c435ac7f690e447d6038c474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 31 Mar 2015 15:32:58 +0200 Subject: erts: Strengthen Maps merge tests --- erts/emulator/test/map_SUITE.erl | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index c7800a5540..062d5ef7a2 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -1654,6 +1654,40 @@ t_bif_map_merge(Config) when is_list(Config) -> #{4 := integer, 18446744073709551629 := wat, float := 3.3, int := 3, {1,2} := "tuple", "hi" := "hello again", <<"key">> := <<"value">>} = maps:merge(M0,M1), + %% try deep collisions + N = 150000, + Is = lists:seq(1,N), + M2 = maps:from_list([{I,I}||I<-Is]), + 150000 = maps:size(M2), + M3 = maps:from_list([{<>,I}||I<-Is]), + 150000 = maps:size(M3), + M4 = maps:merge(M2,M3), + 300000 = maps:size(M4), + M5 = maps:from_list([{integer_to_list(I),I}||I<-Is]), + 150000 = maps:size(M5), + M6 = maps:merge(M4,M5), + 450000 = maps:size(M6), + M7 = maps:from_list([{float(I),I}||I<-Is]), + 150000 = maps:size(M7), + M8 = maps:merge(M7,M6), + 600000 = maps:size(M8), + + #{ 1 := 1, "1" := 1, <<1:32>> := 1 } = M8, + #{ 10 := 10, "10" := 10, <<10:32>> := 10 } = M8, + #{ 100 := 100, "100" := 100, <<100:32>> := 100 } = M8, + #{ 1000 := 1000, "1000" := 1000, <<1000:32>> := 1000 } = M8, + #{ 10000 := 10000, "10000" := 10000, <<10000:32>> := 10000 } = M8, + #{ 100000 := 100000, "100000" := 100000, <<100000:32>> := 100000 } = M8, + + %% overlapping + M8 = maps:merge(M2,M8), + M8 = maps:merge(M3,M8), + M8 = maps:merge(M4,M8), + M8 = maps:merge(M5,M8), + M8 = maps:merge(M6,M8), + M8 = maps:merge(M7,M8), + M8 = maps:merge(M8,M8), + %% error case {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)), {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))), -- cgit v1.2.3 From 71a2a9e102fdcc904cf81dc6369663d79bbfc54a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 31 Mar 2015 18:09:51 +0200 Subject: erts: Remove unused tmp heap in make_internal_hash --- erts/emulator/beam/utils.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 91f4accd30..2dd81e3ca3 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1614,7 +1614,6 @@ make_internal_hash(Eterm term) Eterm tmp; DECLARE_ESTACK(s); - UseTmpHeapNoproc(2); hash = 0; for (;;) { switch (primary_tag(term)) { @@ -1919,7 +1918,6 @@ make_internal_hash(Eterm term) pop_next: if (ESTACK_ISEMPTY(s)) { DESTROY_ESTACK(s); - UnUseTmpHeapNoproc(2); return hash; } -- cgit v1.2.3 From 7556ab9ee0949c94e13d1ccfc6c7755637730be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 31 Mar 2015 18:20:31 +0200 Subject: erts: Use halfword secure tmp heap --- erts/emulator/beam/erl_map.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index ab40f47919..2e31718629 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -392,12 +392,11 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) { Eterm item = list; Eterm *hp; Eterm *kv, res; - Eterm tmp[2]; Uint32 sw, hx; Uint ix = 0; hxnode_t *hxns; ErtsHeapFactory factory; - + DeclareTmpHeap(tmp,2,p); ASSERT(size > 0); hp = HAlloc(p, (2 * size)); @@ -405,6 +404,7 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) { /* create tmp hx values and leaf ptrs */ hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, size * sizeof(hxnode_t)); + UseTmpHeap(2,p); while(is_list(item)) { res = CAR(list_val(item)); kv = tuple_val(res); @@ -417,6 +417,7 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) { ix++; item = CDR(list_val(item)); } + UnUseTmpHeap(2,p); factory.p = p; res = hashmap_from_unsorted_array(&factory, hxns, size, 0); @@ -625,9 +626,9 @@ static Eterm hashmap_from_sorted_unique_array(ErtsHeapFactory* factory, Uint i,ix,jx,elems; Uint32 sw, hx; Eterm val; - Eterm th[2]; hxnode_t *tmp; - + DeclareTmpHeapNoproc(th,2); + UseTmpHeapNoproc(2); ASSERT(lvl < 32); ix = 0; elems = 1; @@ -665,6 +666,7 @@ static Eterm hashmap_from_sorted_unique_array(ErtsHeapFactory* factory, ERTS_FACTORY_HOLE_CHECK(factory); + UnUseTmpHeapNoproc(2); return res; } @@ -1099,12 +1101,13 @@ static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { struct HashmapMergePStackType* sp = PSTACK_PUSH(s); Eterm *hp, *nhp; Eterm hdrA, hdrB; - Eterm th[2]; Uint32 ahx, bhx; Uint size; /* total key-value counter */ int keepA = 0; - unsigned lvl = 0; + unsigned int lvl = 0; + DeclareTmpHeap(th,2,p); Eterm res = THE_NON_VALUE; + UseTmpHeap(2,p); /* * Strategy: Do depth-first traversal of both trees (at the same time) @@ -1297,6 +1300,7 @@ recurse: res = make_boxed(nhp); } PSTACK_DESTROY(s); + UnUseTmpHeap(2,p); return res; } @@ -1315,8 +1319,9 @@ static int hash_cmp(Uint32 ha, Uint32 hb) int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp) { - Eterm th[2]; - unsigned lvl = 0; + unsigned int lvl = 0; + DeclareTmpHeapNoproc(th,2); + UseTmpHeapNoproc(2); if (ap && bp) { ASSERT(CMP_TERM(CAR(ap), CAR(bp)) != 0); @@ -1324,11 +1329,14 @@ int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp) Uint32 ha = hashmap_restore_hash(th, lvl, CAR(ap)); Uint32 hb = hashmap_restore_hash(th, lvl, CAR(bp)); int cmp = hash_cmp(ha, hb); - if (cmp) + if (cmp) { + UnUseTmpHeapNoproc(2); return cmp; + } lvl += 8; } } + UnUseTmpHeapNoproc(2); return ap ? -1 : 1; } @@ -1901,13 +1909,14 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, Uint *update_size, ErtsEStack *sp, int is_update) { Eterm *ptr; Eterm hdr, ckey; - Eterm th[2]; Uint32 ix, cix, bp, hval, chx; Uint slot, lvl = 0, clvl; Uint size = 0, n = 0; + DeclareTmpHeapNoproc(th,2); *update_size = 1; + UseTmpHeapNoproc(2); for (;;) { switch(primary_tag(node)) { case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */ @@ -1918,6 +1927,7 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, goto unroll; } if (is_update) { + UnUseTmpHeapNoproc(2); return 0; } goto insert_subnodes; @@ -1953,6 +1963,7 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, } /* not occupied */ if (is_update) { + UnUseTmpHeapNoproc(2); return 0; } size += HAMT_NODE_BITMAP_SZ(n+1); @@ -1976,6 +1987,7 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, } /* not occupied */ if (is_update) { + UnUseTmpHeapNoproc(2); return 0; } size += HAMT_HEAD_BITMAP_SZ(n+1); @@ -2009,6 +2021,7 @@ insert_subnodes: unroll: *sz = size + /* res cons */ 2; + UnUseTmpHeapNoproc(2); return 1; } @@ -2151,13 +2164,14 @@ static Eterm hashmap_values(Process* p, Eterm node) { static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) { Eterm *hp = NULL, *nhp = NULL, *hp_end = NULL; - Eterm th[2]; Eterm *ptr; Eterm hdr, res = map, node = map; Uint32 ix, bp, hval; Uint slot, lvl = 0; Uint size = 0, n = 0; DECLARE_ESTACK(stack); + DeclareTmpHeapNoproc(th,2); + UseTmpHeapNoproc(2); for (;;) { switch(primary_tag(node)) { @@ -2268,6 +2282,7 @@ unroll: erts_validate_and_sort_flatmap(mp); DESTROY_WSTACK(wstack); + UnUseTmpHeapNoproc(2); return make_flatmap(mp); } @@ -2408,6 +2423,7 @@ not_found: DESTROY_ESTACK(stack); ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); ERTS_HOLE_CHECK(p); + UnUseTmpHeapNoproc(2); return res; } -- cgit v1.2.3 From 7cf7387cfe0fdc43a8cf3b48d89b4adb313bee9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 31 Mar 2015 18:39:18 +0200 Subject: erts: Test deep Maps updates --- erts/emulator/test/map_SUITE.erl | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index a72c8dafe4..8bc8ef0a36 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -27,6 +27,7 @@ t_update_map_expressions/1, t_update_assoc/1, t_update_assoc_large/1, t_update_exact/1, t_update_exact_large/1, + t_update_deep/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, @@ -91,6 +92,7 @@ all() -> [ t_update_map_expressions, t_update_assoc, t_update_assoc_large, t_update_exact, t_update_exact_large, + t_update_deep, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, t_map_equal, t_map_compare, @@ -1036,6 +1038,27 @@ t_update_exact_large(Config) when is_list(Config) -> ok. +t_update_deep(Config) when is_list(Config) -> + N = 250000, + M0 = maps:from_list([{integer_to_list(I),a}||I<-lists:seq(1,N)]), + #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0, + + M1 = M0#{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b }, + #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0, + #{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b } = M1, + + M2 = M0#{ "1" => c, "10" => c, "100" => c, "1000" => c, "10000" => c }, + #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0, + #{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b } = M1, + #{ "1" := c, "10" := c, "100" := c, "1000" := c, "10000" := c } = M2, + + M3 = M2#{ "n1" => d, "n10" => d, "n100" => d, "n1000" => d, "n10000" => d }, + #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0, + #{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b } = M1, + #{ "1" := c, "10" := c, "100" := c, "1000" := c, "10000" := c } = M2, + #{ "1" := c, "10" := c, "100" := c, "1000" := c, "10000" := c } = M3, + #{ "n1" := d, "n10" := d, "n100" := d, "n1000" := d, "n10000" := d } = M3, + ok. t_guard_bifs(Config) when is_list(Config) -> true = map_guard_head(#{a=>1}), -- cgit v1.2.3 From 30ed5b1cc6aa0efe6ac099b66d33d46c7c0c6b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 7 Apr 2015 12:58:15 +0200 Subject: erts: Fix deep colliding hash values in maps:from_list/1 Reported-by: Jesper Louis Andersen --- erts/emulator/beam/erl_map.c | 30 +++++++++++++++++++++++++++++- erts/emulator/test/map_SUITE.erl | 10 +++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 20a17bcd24..3bb3622194 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -679,7 +679,35 @@ static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, hxnode_t *hxns DECLARE_ESTACK(stack); Eterm res = NIL, *hp = NULL, *nhp; - ASSERT(n > 1); + + /* if we get here with only one element then + * we have eight levels of collisions + */ + + if (n == 1) { + res = hxns[0].val; + v = hxns[0].hx; + for (d = 7; d > 0; d--) { + slot = maskval(v,d); + hp = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA); + hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << slot); + hp[1] = res; + res = make_hashmap(hp); + } + + slot = maskval(v,0); + hp = erts_produce_heap(factory, (is_root ? 3 : 2), 0); + + if (is_root) { + hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(1 << slot); + hp[1] = size; + hp[2] = res; + } else { + hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << slot); + hp[1] = res; + } + return make_hashmap(hp); + } /* push initial nodes on the stack, * this is the starting depth */ diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index dc6286fdb6..f061b177c5 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -1888,11 +1888,19 @@ t_bif_map_merge(Config) when is_list(Config) -> M8 = maps:merge(M7,M8), M8 = maps:merge(M8,M8), + %% maps:merge/2 and mixed + + Ks1 = [764492191,2361333849], %% deep collision + Ks2 = lists:seq(1,33), + M9 = maps:from_list([{K,K}||K <- Ks1]), + M10 = maps:from_list([{K,K}||K <- Ks2]), + M11 = maps:merge(M9,M10), + ok = check_keys_exist(Ks1 ++ Ks2, M11), + %% error case {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)), {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))), {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )), - ok. -- cgit v1.2.3 From 6492bf35902f476ef0eac972ef49c424fa6d8bc6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 7 Apr 2015 17:05:46 +0200 Subject: erts: Fix bug in binary_to_term for big maps with 32 bit hash-clash binary_to_term threw badarg as the "reject_dupkey" case in hashmap_from_unsored_array was always triggered when hash-clash was found as the first round in the loop compared the key with itself. --- erts/emulator/beam/erl_map.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 20a17bcd24..4acf830655 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -568,14 +568,14 @@ static Eterm hashmap_from_unsorted_array(ErtsHeapFactory* factory, while(ix < jx) { lx = ix; - while(ix < jx && EQ(CAR(list_val(hxns[ix].val)), CAR(list_val(hxns[lx].val)))) { + while(++ix < jx && EQ(CAR(list_val(hxns[ix].val)), + CAR(list_val(hxns[lx].val)))) { if (reject_dupkeys) return THE_NON_VALUE; if (hxns[ix].i > hxns[lx].i) { lx = ix; } - ix++; } hxns[cx].hx = hxns[lx].hx; hxns[cx].val = hxns[lx].val; -- cgit v1.2.3 From e3f21d4911117b80e80ea4a07f3532ad39ede16b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 7 Apr 2015 21:28:05 +0200 Subject: erts: Fix bug in map_from_list when keys clash in both value and hash Subtle bug in qsort callback. Cast from Sint to int does not retain sign. --- erts/emulator/beam/erl_map.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 4acf830655..14a8afe3a6 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -860,7 +860,12 @@ static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, hxnode_t *hxns #undef HALLOC_EXTRA static int hxnodecmpkey(hxnode_t *a, hxnode_t *b) { - return CMP_TERM(CAR(list_val(a->val)), CAR(list_val(b->val))); + Sint c = CMP_TERM(CAR(list_val(a->val)), CAR(list_val(b->val))); +#if ERTS_SIZEOF_ETERM <= SIZEOF_INT + return c; +#else + return c > 0 ? 1 : (c < 0 ? -1 : 0); +#endif } static int hxnodecmp(hxnode_t *a, hxnode_t *b) { -- cgit v1.2.3 From a4d1a73370dffa6ac96ee70693fd1bd335bd70fe Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 8 Apr 2015 17:02:15 +0200 Subject: erts: Fix ets bug in debug VM Symptom: ASSERT(segtab[seg_ix] == NULL) in alloc_seg() fails. Remedy: Make sure we set segment pointer to NULL in free_seg() even when we switch to smaller segtab. --- erts/emulator/beam/erl_db_hash.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 06dac8f161..b1d9eb84bc 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -2402,10 +2402,10 @@ static int alloc_seg(DbTableHash *tb) */ static int free_seg(DbTableHash *tb, int free_records) { - int seg_ix = (tb->nslots >> SEGSZ_EXP) - 1; + const int seg_ix = (tb->nslots >> SEGSZ_EXP) - 1; + struct segment** const segtab = SEGTAB(tb); + struct ext_segment* const top = (struct ext_segment*) segtab[seg_ix]; int bytes; - struct segment** segtab = SEGTAB(tb); - struct ext_segment* top = (struct ext_segment*) segtab[seg_ix]; int nrecords = 0; ASSERT(top != NULL); @@ -2468,7 +2468,7 @@ static int free_seg(DbTableHash *tb, int free_records) (void*)top, bytes); #ifdef DEBUG if (seg_ix > 0) { - if (seg_ix < tb->nsegs) SEGTAB(tb)[seg_ix] = NULL; + segtab[seg_ix] = NULL; } else { SET_SEGTAB(tb, NULL); } -- cgit v1.2.3 From 8da670fa275d77119e32bf642edb342b5dd64dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 9 Apr 2015 13:43:46 +0200 Subject: erts: Test maps:from_list/1 shrinking Repeated keys will shrink map to a flatmap if the number of pairs drops below the limit. --- erts/emulator/test/map_SUITE.erl | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index dc6286fdb6..fbe6ea2a38 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -2252,6 +2252,13 @@ t_bif_map_from_list(Config) when is_list(Config) -> maps:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2}, {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]), + %% repeated keys (large -> small) + Ps1 = [{a,I}|| I <- lists:seq(1,32)], + Ps2 = [{a,I}|| I <- lists:seq(33,64)], + + M = maps:from_list(Ps1 ++ [{b,1},{c,1}] ++ Ps2), + #{ a := 64, b := 1, c := 1 } = M, + %% error cases {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},b]))), {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},{b,b,3}]))), -- cgit v1.2.3 From 8091cba6c33b89a8f266ee2bd1b8f2621fd79d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 9 Apr 2015 13:58:32 +0200 Subject: erts: Cover maps:values/1 for large maps --- erts/emulator/test/map_SUITE.erl | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index fbe6ea2a38..01c7c8f4bd 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -2026,6 +2026,14 @@ t_bif_map_values(Config) when is_list(Config) -> true = is_members([number,3,"hello2",<<"value2">>],maps:values(M2)), true = is_members([number,3,"hello",<<"value">>],maps:values(M1)), + Vs = lists:seq(1000,20000), + M3 = maps:from_list([{K,K}||K<-Vs]), + M4 = maps:merge(M1,M3), + M5 = maps:merge(M2,M3), + true = is_members(Vs,maps:values(M3)), + true = is_members([number,3,"hello",<<"value">>]++Vs,maps:values(M4)), + true = is_members([number,3,"hello2",<<"value2">>]++Vs,maps:values(M5)), + %% error case {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)), {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(atom)), -- cgit v1.2.3 From fe150e807667cf3aa1ecdd865a1885bdc326b0f6 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Sat, 28 Mar 2015 14:53:07 -0400 Subject: Keep dirty schedulers from waking other schedulers Prevent dirty schedulers from checking regular run queues and trying to wake up regular schedulers. --- erts/emulator/beam/erl_process.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index f74a2ee54c..5f7770e65f 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -4960,9 +4960,11 @@ wakeup_other_check(ErtsRunQueue *rq, Uint32 flags) + ERTS_WAKEUP_OTHER_FIXED_INC); if (rq->wakeup_other > wakeup_other.limit) { #ifdef ERTS_DIRTY_SCHEDULERS - if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && rq->waiting) - wake_dirty_schedulers(rq, 1); - else + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) { + if (rq->waiting) { + wake_dirty_schedulers(rq, 1); + } + } else #endif { int empty_rqs = -- cgit v1.2.3 From 1a191c166c446b21f515429fc9987e5a7add5ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 10 Apr 2015 15:12:45 +0200 Subject: erts: Fix building of Map result from match_specs A faulty "box-value" entered into the heap which could cause a segmentation fault in the garbage collector if it was written on a heap fragment. --- erts/emulator/beam/erl_db_util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 0bf562d937..0fb1c397c9 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2153,8 +2153,8 @@ restart: break; case matchMkFlatMap: n = *pc++; - ehp = HAllocX(build_proc, 1 + MAP_HEADER_FLATMAP_SZ + n, HEAP_XTRA); - t = *ehp++ = *--esp; + ehp = HAllocX(build_proc, MAP_HEADER_FLATMAP_SZ + n, HEAP_XTRA); + t = *--esp; { flatmap_t *m = (flatmap_t *)ehp; m->thing_word = MAP_HEADER_FLATMAP; -- cgit v1.2.3 From d4bf1ea63a3ada92394378ce176b3c4aeba246d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 10 Apr 2015 15:17:24 +0200 Subject: erts: Remove code that was commented out --- erts/emulator/beam/erl_term.h | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 095aa54ddd..cff012d5d1 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -298,7 +298,6 @@ _ET_DECLARE_CHECKED(Uint,atom_val,Eterm) /* header (arityval or thing) access methods */ #define _make_header(sz,tag) ((Uint)(((Uint)(sz) << _HEADER_ARITY_OFFS) + (tag))) #define is_header(x) (((x) & _TAG_PRIMARY_MASK) == TAG_PRIMARY_HEADER) -//#define _unchecked_header_arity(x) ((x) >> _HEADER_ARITY_OFFS) #define _unchecked_header_arity(x) \ (is_map_header(x) ? MAP_HEADER_ARITY(x) : ((x) >> _HEADER_ARITY_OFFS)) _ET_DECLARE_CHECKED(Uint,header_arity,Eterm) -- cgit v1.2.3 From 2a0ff8b50a68d51e44fddf36867f390b64a4f2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 1 Apr 2015 15:30:12 +0200 Subject: erts: Ensure hashing of zero is consistent Erlang treats positive and negative zero as equal, meaning, true = 0.0 =:= 0.0/-1 However, Erlangs hash functions: hash, phash and phash2 did not reflect this behaviour. Meaning, the hash values produced by the different hash functions would not be identical for positive and negative zero. This commit ensures that hash value of positive zero is always produced regardless of the signedness of the zero float, i.e. true = erlang:phash2(0.0) =:= erlang:phash2(0.0/-1) --- erts/emulator/beam/utils.c | 29 ++++++++++++++++++----------- erts/emulator/test/hash_SUITE.erl | 27 ++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 2dd81e3ca3..2018518bf3 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -955,12 +955,14 @@ tail_recur: UINT32_HASH_RET(external_ref_numbers(term)[0],FUNNY_NUMBER9,FUNNY_NUMBER10); case FLOAT_DEF: { - FloatDef ff; - GET_DOUBLE(term, ff); - hash = hash*FUNNY_NUMBER6 + (ff.fw[0] ^ ff.fw[1]); - break; + FloatDef ff; + GET_DOUBLE(term, ff); + if (ff.fd == 0.0f) { + ff.fd = 0.0f; /* ensure pos. 0.0 */ + } + hash = hash*FUNNY_NUMBER6 + (ff.fw[0] ^ ff.fw[1]); + break; } - case MAKE_HASH_CDR_PRE_OP: term = (Eterm) WSTACK_POP(stack); if (is_not_list(term)) { @@ -1474,6 +1476,9 @@ make_hash2(Eterm term) { FloatDef ff; GET_DOUBLE(term, ff); + if (ff.fd == 0.0f) { + ff.fd = 0.0f; /* ensure pos. 0.0 */ + } #if defined(WORDS_BIGENDIAN) || defined(DOUBLE_MIDDLE_ENDIAN) UINT32_HASH_2(ff.fw[0], ff.fw[1], HCONST_12); #else @@ -1893,8 +1898,8 @@ make_internal_hash(Eterm term) { FloatDef ff; GET_DOUBLE(term, ff); - if (ff.fd == 0.0) { - ff.fd = 0.0; /* ensure pos. 0.0 */ + if (ff.fd == 0.0f) { + ff.fd = 0.0f; /* ensure pos. 0.0 */ } UINT32_HASH_2(ff.fw[0], ff.fw[1], HCONST_12); goto pop_next; @@ -2079,12 +2084,14 @@ tail_recur: break; case FLOAT_DEF: { - FloatDef ff; - GET_DOUBLE(term, ff); - hash = hash*FUNNY_NUMBER6 + (ff.fw[0] ^ ff.fw[1]); + FloatDef ff; + GET_DOUBLE(term, ff); + if (ff.fd == 0.0f) { + ff.fd = 0.0f; /* ensure pos. 0.0 */ + } + hash = hash*FUNNY_NUMBER6 + (ff.fw[0] ^ ff.fw[1]); } break; - case MAKE_HASH_CDR_PRE_OP: term = (Eterm) WSTACK_POP(stack); if (is_not_list(term)) { diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl index 647bb45049..5fa45f772d 100644 --- a/erts/emulator/test/hash_SUITE.erl +++ b/erts/emulator/test/hash_SUITE.erl @@ -73,6 +73,7 @@ config(priv_dir,_) -> init_per_group/2,end_per_group/2, test_basic/1,test_cmp/1,test_range/1,test_spread/1, test_phash2/1,otp_5292/1,bit_level_binaries/1,otp_7127/1, + test_hash_zero/1, end_per_testcase/2,init_per_testcase/2]). init_per_testcase(_Case, Config) -> Dog=test_server:timetrap(test_server:minutes(10)), @@ -86,7 +87,9 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [test_basic, test_cmp, test_range, test_spread, - test_phash2, otp_5292, bit_level_binaries, otp_7127]. + test_phash2, otp_5292, bit_level_binaries, otp_7127, + test_hash_zero + ]. groups() -> []. @@ -160,6 +163,8 @@ otp_7127(doc) -> otp_7127(Config) when is_list(Config) -> otp_7127_test(). +test_hash_zero(Config) when is_list(Config) -> + hash_zero_test(). -endif. @@ -591,6 +596,26 @@ otp_7127_test() -> 38990304 = erlang:phash2(<<"Scott9">>), ok. +hash_zero_test() -> + Zs = [0.0, -0.0, 0/-1, 0.0/-1, 0/-(1 bsl 65), + binary_to_term(<<131,70,0,0,0,0,0,0,0,0>>), %% +0.0 + binary_to_term(<<131,70,128,0,0,0,0,0,0,0>>)], %% -0.0 + ok = hash_zero_test(Zs,fun(T) -> erlang:phash2(T, 1 bsl 32) end), + ok = hash_zero_test(Zs,fun(T) -> erlang:phash(T, 1 bsl 32) end), + ok = hash_zero_test(Zs,fun(T) -> erlang:hash(T, (1 bsl 27) - 1) end), + ok. + +hash_zero_test([Z|Zs],F) -> + hash_zero_test(Zs,Z,F(Z),F). +hash_zero_test([Z|Zs],Z0,V,F) -> + true = Z0 =:= Z, %% assert exact equal + Z0 = Z, %% assert matching + V = F(Z), %% assert hash + hash_zero_test(Zs,Z0,V,F); +hash_zero_test([],_,_,_) -> + ok. + + %% %% Reference implementation of integer hashing %% -- cgit v1.2.3 From 641003f0dd87c0f50b06c022a1d162fc04e96795 Mon Sep 17 00:00:00 2001 From: Nick Mills Date: Mon, 16 Mar 2015 14:43:46 -0400 Subject: Use the correct union member inside efile_drv The `invoke_pwritev()` function was in some places using the union member intended for the `invoke_writev()` function. --- erts/emulator/drivers/common/efile_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 3b8e7acb6e..8d4992c3f0 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -1695,9 +1695,9 @@ static void invoke_pwritev(void *data) { d->result_ok = 0; d->again = 0; deq_error: - MUTEX_LOCK(d->c.writev.q_mtx); + MUTEX_LOCK(d->c.pwritev.q_mtx); driver_deq(d->c.pwritev.port, c->size); - MUTEX_UNLOCK(d->c.writev.q_mtx); + MUTEX_UNLOCK(d->c.pwritev.q_mtx); goto done; } else { @@ -1708,9 +1708,9 @@ static void invoke_pwritev(void *data) { ASSERT(written >= FILE_SEGMENT_WRITE); } - MUTEX_LOCK(d->c.writev.q_mtx); + MUTEX_LOCK(d->c.pwritev.q_mtx); driver_deq(d->c.pwritev.port, written); - MUTEX_UNLOCK(d->c.writev.q_mtx); + MUTEX_UNLOCK(d->c.pwritev.q_mtx); done: EF_FREE(iov); /* Free our copy of the vector, nothing to restore */ -- cgit v1.2.3 From 7f6e9ab38d14cba1386cb91307258302702617b8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 10 Apr 2015 17:12:56 +0200 Subject: erts: Cleanup code in invoke_pwritev --- erts/emulator/drivers/common/efile_drv.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 8d4992c3f0..518646649d 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -1616,12 +1616,12 @@ static void invoke_altname(void *data) } static void invoke_pwritev(void *data) { - struct t_data *d = (struct t_data *) data; + struct t_data* const d = (struct t_data *) data; + struct t_pwritev * const c = &d->c.pwritev; SysIOVec *iov0; SysIOVec *iov; int iovlen; int iovcnt; - struct t_pwritev *c = &d->c.pwritev; size_t p; int segment; size_t size, write_size, written; @@ -1695,9 +1695,9 @@ static void invoke_pwritev(void *data) { d->result_ok = 0; d->again = 0; deq_error: - MUTEX_LOCK(d->c.pwritev.q_mtx); - driver_deq(d->c.pwritev.port, c->size); - MUTEX_UNLOCK(d->c.pwritev.q_mtx); + MUTEX_LOCK(c->q_mtx); + driver_deq(c->port, c->size); + MUTEX_UNLOCK(c->q_mtx); goto done; } else { @@ -1708,9 +1708,9 @@ static void invoke_pwritev(void *data) { ASSERT(written >= FILE_SEGMENT_WRITE); } - MUTEX_LOCK(d->c.pwritev.q_mtx); - driver_deq(d->c.pwritev.port, written); - MUTEX_UNLOCK(d->c.pwritev.q_mtx); + MUTEX_LOCK(c->q_mtx); + driver_deq(c->port, written); + MUTEX_UNLOCK(c->q_mtx); done: EF_FREE(iov); /* Free our copy of the vector, nothing to restore */ -- cgit v1.2.3 From 9929a1fd151a78247f1d536f2a21843f310a4e3b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 10 Apr 2015 19:12:48 +0200 Subject: erts: Add etp_the_non_value for a correct (non)value regardless of build type. --- erts/emulator/beam/erl_init.c | 2 ++ erts/etc/unix/etp-commands.in | 8 ++------ 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 86d3416423..1346892e29 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -118,6 +118,8 @@ const int etp_big_endian = 1; #else const int etp_big_endian = 0; #endif +const Eterm etp_the_non_value = THE_NON_VALUE; + /* * Note about VxWorks: All variables must be initialized by executable code, * not by an initializer. Otherwise a new instance of the emulator will diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index c117a62a21..ab2c1126df 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -146,14 +146,10 @@ define etp-1 etp-immediate-1 ($arg0) else # (($arg0) & 0x3) == 0 - if (($arg0) == 0x0) + if (($arg0) == etp_the_non_value) printf "" else - if (($arg0) == 0x4) - printf "" - else - etp-cp-1 ($arg0) - end + etp-cp-1 ($arg0) end end end -- cgit v1.2.3 From 82b0a889fd2534af81e21c277657adf58699d73c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 20 Mar 2015 12:20:52 +0100 Subject: Remove support for put_map_exact without a source map Using the exact operator (':=') is only allowed when an existing map is being updated. Thus the following causes a compilation error: #{k:=v} Therefore there is no need to support the put_map_exact instruction without a source map. --- erts/emulator/beam/ops.tab | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index d3649080dc..ae3b7d08b8 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1478,7 +1478,7 @@ put_map_assoc F Src=s Dst Live Size Rest=* => \ update_map_assoc F Src Dst Live Size Rest put_map_assoc F Src Dst Live Size Rest=* => \ move Src x | update_map_assoc F x Dst Live Size Rest -put_map_exact F n Dst Live Size Rest=* => new_map F Dst Live Size Rest + put_map_exact F Src=s Dst Live Size Rest=* => \ update_map_exact F Src Dst Live Size Rest put_map_exact F Src Dst Live Size Rest=* => \ -- cgit v1.2.3 From 8c5a577ed55a607841989763d78b3950eebd5b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 20 Mar 2015 14:45:31 +0100 Subject: Correct transformation of put_map_assoc to new_map A put_map_assoc instruction with an empty source map should be converted to a simpler new_map instruction. The transformation didn't happen because an empty source map is no longer represented as a NIL term (as it was in the beginning before map literals were implemented). --- erts/emulator/beam/beam_load.c | 17 +++++++++++++++++ erts/emulator/beam/ops.tab | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 02689e5b19..f140bb54cc 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -36,6 +36,7 @@ #include "beam_catches.h" #include "erl_binary.h" #include "erl_zlib.h" +#include "erl_map.h" #ifdef HIPE #include "hipe_bif0.h" @@ -4050,6 +4051,22 @@ tuple_append_put(LoaderState* stp, GenOpArg Arity, GenOpArg Dst, return op; } +/* + * Predicate to test whether the given literal is an empty map. + */ + +static int +is_empty_map(LoaderState* stp, GenOpArg Lit) +{ + Eterm term; + + if (Lit.type != TAG_q) { + return 0; + } + term = stp->literals[Lit.val].term; + return is_flatmap(term) && flatmap_get_size(flatmap_val(term)) == 0; +} + /* * Replace a get_map_elements with one key to an instruction with one * element diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index ae3b7d08b8..2c4458ae74 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1473,7 +1473,8 @@ apply_last I P # Map instructions in R17. # -put_map_assoc F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map_assoc F Map Dst Live Size Rest=* | is_empty_map(Map) => \ + new_map F Dst Live Size Rest put_map_assoc F Src=s Dst Live Size Rest=* => \ update_map_assoc F Src Dst Live Size Rest put_map_assoc F Src Dst Live Size Rest=* => \ -- cgit v1.2.3 From ee91c4291212035b8e054072407c74afdd66f1d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 20 Mar 2015 15:07:24 +0100 Subject: Remove the fail label operand of the new_map instruction The new_map instruction cannot fail, and thus needs no fail label. --- erts/emulator/beam/beam_debug.c | 2 +- erts/emulator/beam/beam_emu.c | 8 ++++---- erts/emulator/beam/ops.tab | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 6bb987985d..38e54e9d1a 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -661,7 +661,7 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) case op_i_put_tuple_rI: case op_i_put_tuple_xI: case op_i_put_tuple_yI: - case op_new_map_jdII: + case op_new_map_dII: case op_update_map_assoc_jsdII: case op_update_map_exact_jsdII: case op_i_has_map_fields_fsI: diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index fdb84aae42..a264669e50 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2379,16 +2379,16 @@ void process_main(void) Goto(*I); } - OpCase(new_map_jdII): { + OpCase(new_map_dII): { Eterm res; x(0) = r(0); SWAPOUT; - res = new_map(c_p, reg, I); + res = new_map(c_p, reg, I-1); SWAPIN; r(0) = x(0); - StoreResult(res, Arg(1)); - Next(4+Arg(3)); + StoreResult(res, Arg(0)); + Next(3+Arg(2)); } OpCase(i_has_map_fields_fsI): { diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 2c4458ae74..7993dd9e25 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1473,8 +1473,8 @@ apply_last I P # Map instructions in R17. # -put_map_assoc F Map Dst Live Size Rest=* | is_empty_map(Map) => \ - new_map F Dst Live Size Rest +put_map_assoc j Map Dst Live Size Rest=* | is_empty_map(Map) => \ + new_map Dst Live Size Rest put_map_assoc F Src=s Dst Live Size Rest=* => \ update_map_assoc F Src Dst Live Size Rest put_map_assoc F Src Dst Live Size Rest=* => \ @@ -1485,7 +1485,7 @@ put_map_exact F Src=s Dst Live Size Rest=* => \ put_map_exact F Src Dst Live Size Rest=* => \ move Src x | update_map_exact F x Dst Live Size Rest -new_map j d I I +new_map d I I update_map_assoc j s d I I update_map_exact j s d I I -- cgit v1.2.3 From a1e409e082392fcb8dc8131b77851d72551711a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 23 Mar 2015 09:56:06 +0100 Subject: Run a clone of map_SUITE without optimizations Create a clone of map_SUITE named map_no_opt_SUITE to ensure that the loader can cope with unoptimized map instructions. --- erts/emulator/test/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index dd2e2cb504..4dc4b5027d 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -125,7 +125,8 @@ NO_OPT= bs_bincomp \ bs_match_tail \ bs_match_misc \ bs_utf \ - guard + guard \ + map NATIVE= hibernate -- cgit v1.2.3 From cd4c3e3bc699e73a7bada55a74333e5a09c7f9e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 23 Mar 2015 10:07:21 +0100 Subject: map_SUITE: Add tests of is_map/1 with literal maps To be sure that the compiler and BEAM virtual machine correctly handles literals maps, we must test it. --- erts/emulator/test/map_SUITE.erl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index ad8411cd68..490dd0c1ad 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -38,6 +38,7 @@ t_map_equal/1, t_map_compare/1, t_map_size/1, + t_is_map/1, %% Specific Map BIFs t_bif_map_get/1, @@ -114,7 +115,7 @@ all() -> [ %% erlang t_erlang_hash, t_map_encode_decode, - t_map_size, + t_map_size, t_is_map, %% non specific BIF related t_bif_build_and_check, @@ -680,6 +681,17 @@ build_and_check_size([],N,M) -> map_is_size(M,N) when map_size(M) =:= N -> true; map_is_size(_,_) -> false. +t_is_map(Config) when is_list(Config) -> + true = is_map(#{}), + true = is_map(#{a=>1}), + false = is_map({a,b}), + false = is_map(x), + if is_map(#{}) -> ok end, + if is_map(#{b=>1}) -> ok end, + if not is_map([1,2,3]) -> ok end, + if not is_map(x) -> ok end, + ok. + % test map updates without matching t_update_literals_large(Config) when is_list(Config) -> Map = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10", -- cgit v1.2.3 From 85afb5e3441e78cd0d572dc809d94cfc810d71ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 23 Mar 2015 10:24:56 +0100 Subject: Fully evaluate is_map/1 for literals at load-time The compiler will only emit is_map/1 instructions with literal argument if optimization is turned off. Therefore, the only reason for this commit is cleanliness. --- erts/emulator/beam/beam_load.c | 14 ++++++++++++++ erts/emulator/beam/ops.tab | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index f140bb54cc..60f4ab5280 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4051,6 +4051,20 @@ tuple_append_put(LoaderState* stp, GenOpArg Arity, GenOpArg Dst, return op; } +/* + * Predicate to test whether the given literal is a map. + */ + +static int +literal_is_map(LoaderState* stp, GenOpArg Lit) +{ + Eterm term; + + ASSERT(Lit.type == TAG_q); + term = stp->literals[Lit.val].term; + return is_map(term); +} + /* * Predicate to test whether the given literal is an empty map. */ diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 7993dd9e25..a5a89b3990 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1489,8 +1489,8 @@ new_map d I I update_map_assoc j s d I I update_map_exact j s d I I -is_map Fail Literal=q => move Literal x | is_map Fail x -is_map Fail c => jump Fail +is_map Fail Lit=q | literal_is_map(Lit) => +is_map Fail cq => jump Fail %macro: is_map IsMap -fail_action is_map f r -- cgit v1.2.3 From fea64943212cd96764b595c91b22da68bc72e999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 23 Mar 2015 11:49:19 +0100 Subject: erts/map_SUITE.erl: Add a test case that tests has_map_fields The has_map_fields instruction was not tested at all by erts/map_SUITE.erl --- erts/emulator/test/map_SUITE.erl | 44 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 490dd0c1ad..7b68e91dc5 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -75,7 +75,10 @@ t_pdict/1, t_ets/1, t_dets/1, - t_tracing/1 + t_tracing/1, + + %% instruction-level tests + t_has_map_fields/1 ]). -include_lib("stdlib/include/ms_transform.hrl"). @@ -132,7 +135,10 @@ all() -> [ t_erts_internal_hash, t_pdict, t_ets, - t_tracing + t_tracing, + + %% instruction-level tests + t_has_map_fields ]. groups() -> []. @@ -2715,5 +2721,39 @@ trace_collector(Msg,Parent) -> Parent ! Msg, Parent. +t_has_map_fields(Config) when is_list(Config) -> + true = has_map_fields_1(#{one=>1}), + true = has_map_fields_1(#{one=>1,two=>2}), + false = has_map_fields_1(#{two=>2}), + false = has_map_fields_1(#{}), + + true = has_map_fields_2(#{c=>1,b=>2,a=>3}), + true = has_map_fields_2(#{c=>1,b=>2,a=>3,x=>42}), + false = has_map_fields_2(#{b=>2,c=>1}), + false = has_map_fields_2(#{x=>y}), + false = has_map_fields_2(#{}), + + true = has_map_fields_3(#{c=>1,b=>2,a=>3}), + true = has_map_fields_3(#{c=>1,b=>2,a=>3,[]=>42}), + true = has_map_fields_3(#{b=>2,a=>3,[]=>42,42.0=>43}), + true = has_map_fields_3(#{a=>3,[]=>42,42.0=>43}), + true = has_map_fields_3(#{[]=>42,42.0=>43}), + false = has_map_fields_3(#{b=>2,c=>1}), + false = has_map_fields_3(#{[]=>y}), + false = has_map_fields_3(#{42.0=>x,a=>99}), + false = has_map_fields_3(#{}), + + ok. + +has_map_fields_1(#{one:=_}) -> true; +has_map_fields_1(#{}) -> false. + +has_map_fields_2(#{a:=_,b:=_,c:=_}) -> true; +has_map_fields_2(#{}) -> false. + +has_map_fields_3(#{a:=_,b:=_}) -> true; +has_map_fields_3(#{[]:=_,42.0:=_}) -> true; +has_map_fields_3(#{}) -> false. + %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. -- cgit v1.2.3 From c92cf260bb888c004fb02651670d19989dbc2b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 26 Mar 2015 13:16:45 +0100 Subject: De-optimize the has_map_fields instructions The has_map_fields instruction is infrequently used. Thus there is no need to have the fastest possible implementation; it is better to have an implementation that reduces the code size in the already big process_main() function. We can transform has_map_fields to a get_map_elements instruction, targeting the same unused x[0] register for all keys. That instruction will only be marginally slower than existing implementation. --- erts/emulator/beam/beam_debug.c | 1 - erts/emulator/beam/beam_emu.c | 96 ----------------------------------------- erts/emulator/beam/beam_load.c | 20 ++++++--- erts/emulator/beam/ops.tab | 29 ++----------- 4 files changed, 19 insertions(+), 127 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 38e54e9d1a..0367ca8aba 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -664,7 +664,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) case op_new_map_dII: case op_update_map_assoc_jsdII: case op_update_map_exact_jsdII: - case op_i_has_map_fields_fsI: case op_i_get_map_elements_fsI: { int n = unpacked[-1]; diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index a264669e50..f25b9f594d 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -701,8 +701,6 @@ void** beam_ops; #define IsMap(Src, Fail) if (!is_map(Src)) { Fail; } -#define HasMapField(Src, Key, Fail) if (has_not_map_field(Src, Key)) { Fail; } - #define GetMapElement(Src, Key, Dst, Fail) \ do { \ Eterm _res = get_map_element(Src, Key); \ @@ -960,7 +958,6 @@ static Eterm update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) NOINLINE; static Eterm update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) NOINLINE; -static int has_not_map_field(Eterm map, Eterm key); static Eterm get_map_element(Eterm map, Eterm key); /* @@ -2391,67 +2388,6 @@ void process_main(void) Next(3+Arg(2)); } - OpCase(i_has_map_fields_fsI): { - flatmap_t* mp; - Eterm map; - Eterm field; - Eterm *ks; - BeamInstr* fs; - Uint sz,n; - - GetArg1(1, map); - n = (Uint)Arg(2); - fs = &Arg(3); /* pattern fields */ - - /* get term from field? */ - if (is_hashmap(map)) { - Uint32 hx; - while(n--) { - field = *fs++; - hx = hashmap_make_hash(field); - if (!erts_hashmap_get(hx,field,map)) { - SET_I((BeamInstr *) Arg(0)); - goto has_map_fields_fail; - } - } - goto has_map_fields_ok; - } - - ASSERT(is_flatmap(map)); - - mp = (flatmap_t *)flatmap_val(map); - sz = flatmap_get_size(mp); - - if (sz == 0) { - SET_I((BeamInstr *) Arg(0)); - goto has_map_fields_fail; - } - - ks = flatmap_get_keys(mp); - - ASSERT(n>0); - - while(sz) { - field = (Eterm)*fs; - if (EQ(field,*ks)) { - n--; - fs++; - if (n == 0) break; - } - ks++; sz--; - } - - if (n) { - SET_I((BeamInstr *) Arg(0)); - goto has_map_fields_fail; - } -has_map_fields_ok: - I += 4 + Arg(2); -has_map_fields_fail: - ASSERT(VALID_INSTR(*I)); - Goto(*I); - } - #define PUT_TERM_REG(term, desc) \ do { \ switch ((desc) & _TAG_IMMED1_MASK) { \ @@ -6470,38 +6406,6 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) return make_fun(funp); } -static int has_not_map_field(Eterm map, Eterm key) -{ - Uint32 hx; - if (is_flatmap(map)) { - flatmap_t* mp; - Eterm* keys; - Uint i; - Uint n; - - mp = (flatmap_t *)flatmap_val(map); - keys = flatmap_get_keys(mp); - n = flatmap_get_size(mp); - if (is_immed(key)) { - for (i = 0; i < n; i++) { - if (keys[i] == key) { - return 0; - } - } - } else { - for (i = 0; i < n; i++) { - if (EQ(keys[i], key)) { - return 0; - } - } - } - return 1; - } - ASSERT(is_hashmap(map)); - hx = hashmap_make_hash(key); - return erts_hashmap_get(hx,key,map) ? 0 : 1; -} - static Eterm get_map_element(Eterm map, Eterm key) { Uint32 hx; diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 60f4ab5280..18e1e312ad 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4107,21 +4107,31 @@ gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src, } static GenOp* -gen_has_map_field(LoaderState* stp, GenOpArg Fail, GenOpArg Src, - GenOpArg Size, GenOpArg* Rest) +gen_has_map_fields(LoaderState* stp, GenOpArg Fail, GenOpArg Src, + GenOpArg Size, GenOpArg* Rest) { GenOp* op; + Uint i; + Uint n; ASSERT(Size.type == TAG_u); + n = Size.val; NEW_GENOP(stp, op); + GENOP_ARITY(op, 3 + 2*n); op->next = NULL; - op->op = genop_has_map_field_3; - op->arity = 4; + op->op = genop_get_map_elements_3; op->a[0] = Fail; op->a[1] = Src; - op->a[2] = Rest[0]; + op->a[2].type = TAG_u; + op->a[2].val = 2*n; + + for (i = 0; i < n; i++) { + op->a[3+2*i] = Rest[i]; + op->a[3+2*i+1].type = TAG_x; + op->a[3+2*i+1].val = 0; /* x(0); normally not used */ + } return op; } diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index a5a89b3990..abaa47217a 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1497,31 +1497,10 @@ is_map f r is_map f x is_map f y -## Transform has_map_field(s) #{ K1 := _, K2 := _ } - -has_map_field/3 - -has_map_fields Fail Src Size=u==1 Rest=* => gen_has_map_field(Fail,Src,Size,Rest) -has_map_fields Fail Src Size Rest=* => i_has_map_fields Fail Src Size Rest - -i_has_map_fields f s I - -has_map_field Fail Src=rxy Key=arxy => i_has_map_field Fail Src Key -has_map_field Fail Src Key => move Key x | i_has_map_field Fail Src x - -%macro: i_has_map_field HasMapField -fail_action -i_has_map_field f r a -i_has_map_field f x a -i_has_map_field f y a -i_has_map_field f r r -i_has_map_field f x r -i_has_map_field f y r -i_has_map_field f r x -i_has_map_field f x x -i_has_map_field f y x -i_has_map_field f r y -i_has_map_field f x y -i_has_map_field f y y +## Transform has_map_fields #{ K1 := _, K2 := _ } to has_map_elements + +has_map_fields Fail Src Size Rest=* => \ + gen_has_map_fields(Fail, Src, Size, Rest) ## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 } -- cgit v1.2.3 From 529af720ae7b663aa6a1a086b31ba9e3605ff21e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 23 Mar 2015 13:10:20 +0100 Subject: Sort maps keys in the loader The map instructions require that the keys in the instructions are sorted (for flatmaps). But that is an implementation detail that should not exposed outside of the BEAM virtual machine. Therefore, make the sorting of the keys the responsibility of the loader and not the compiler. Also note that the sort order for maps with numeric keys or keys with numeric components has changed in OTP 18. That means that code compiled for OTP 17 that operated on maps with map keys might not work in OTP 18 without the sorting in the loader (although it is unlikely to be an issue in practice). --- erts/emulator/beam/beam_load.c | 80 ++++++++++++++++++++++++++++++++++++++++ erts/emulator/beam/ops.tab | 24 ++++++++---- erts/emulator/test/map_SUITE.erl | 3 ++ 3 files changed, 100 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 18e1e312ad..1a6c6c16ad 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4081,6 +4081,86 @@ is_empty_map(LoaderState* stp, GenOpArg Lit) return is_flatmap(term) && flatmap_get_size(flatmap_val(term)) == 0; } +/* + * Pseudo predicate map_key_sort that will sort the Rest operand for + * map instructions as a side effect. + */ + +typedef struct SortGenOpArg { + Eterm term; /* Term to use for comparing */ + GenOpArg arg; /* Original data */ +} SortGenOpArg; + +static int +genopargtermcompare(SortGenOpArg* a, SortGenOpArg* b) +{ + return CMP_TERM(a->term, b->term); +} + +static int +map_key_sort(LoaderState* stp, GenOpArg Size, GenOpArg* Rest) +{ + SortGenOpArg* t; + unsigned size = Size.val; + unsigned i; + + if (size == 2) { + return 1; /* Already sorted. */ + } + + + t = (SortGenOpArg *) erts_alloc(ERTS_ALC_T_TMP, size*sizeof(SortGenOpArg)); + + /* + * Copy original data and sort keys to a temporary array. + */ + for (i = 0; i < size; i += 2) { + t[i].arg = Rest[i]; + switch (Rest[i].type) { + case TAG_a: + t[i].term = Rest[i].val; + ASSERT(is_atom(t[i].term)); + break; + case TAG_i: + t[i].term = make_small(Rest[i].val); + break; + case TAG_n: + t[i].term = NIL; + break; + case TAG_q: + t[i].term = stp->literals[Rest[i].val].term; + break; + default: + /* + * Not a literal key. Not allowed. Only a single + * variable key is allowed in each map instruction. + */ + erts_free(ERTS_ALC_T_TMP, (void *) t); + return 0; + } +#ifdef DEBUG + t[i+1].term = THE_NON_VALUE; +#endif + t[i+1].arg = Rest[i+1]; + } + + /* + * Sort the temporary array. + */ + qsort((void *) t, size / 2, 2 * sizeof(SortGenOpArg), + (int (*)(const void *, const void *)) genopargtermcompare); + + /* + * Copy back the sorted, original data. + */ + for (i = 0; i < size; i++) { + Rest[i] = t[i].arg; + } + + erts_free(ERTS_ALC_T_TMP, (void *) t); + return 1; +} + /* * Replace a get_map_elements with one key to an instruction with one * element diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index abaa47217a..92d9ccb5eb 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1473,16 +1473,24 @@ apply_last I P # Map instructions in R17. # -put_map_assoc j Map Dst Live Size Rest=* | is_empty_map(Map) => \ +sorted_put_map_assoc/5 +put_map_assoc F Map Dst Live Size Rest=* | map_key_sort(Size, Rest) => \ + sorted_put_map_assoc F Map Dst Live Size Rest + +sorted_put_map_exact/5 +put_map_exact F Map Dst Live Size Rest=* | map_key_sort(Size, Rest) => \ + sorted_put_map_exact F Map Dst Live Size Rest + +sorted_put_map_assoc j Map Dst Live Size Rest=* | is_empty_map(Map) => \ new_map Dst Live Size Rest -put_map_assoc F Src=s Dst Live Size Rest=* => \ +sorted_put_map_assoc F Src=s Dst Live Size Rest=* => \ update_map_assoc F Src Dst Live Size Rest -put_map_assoc F Src Dst Live Size Rest=* => \ +sorted_put_map_assoc F Src Dst Live Size Rest=* => \ move Src x | update_map_assoc F x Dst Live Size Rest -put_map_exact F Src=s Dst Live Size Rest=* => \ +sorted_put_map_exact F Src=s Dst Live Size Rest=* => \ update_map_exact F Src Dst Live Size Rest -put_map_exact F Src Dst Live Size Rest=* => \ +sorted_put_map_exact F Src Dst Live Size Rest=* => \ move Src x | update_map_exact F x Dst Live Size Rest new_map d I I @@ -1506,8 +1514,10 @@ has_map_fields Fail Src Size Rest=* => \ get_map_element/4 -get_map_elements Fail Src=rxy Size=u==2 Rest=* => gen_get_map_element(Fail,Src,Size,Rest) -get_map_elements Fail Src Size Rest=* => i_get_map_elements Fail Src Size Rest +get_map_elements Fail Src=rxy Size=u==2 Rest=* => \ + gen_get_map_element(Fail, Src, Size, Rest) +get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \ + i_get_map_elements Fail Src Size Rest i_get_map_elements f s I diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 7b68e91dc5..008fa0e11b 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -164,6 +164,9 @@ t_build_and_match_literals(Config) when is_list(Config) -> #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} = id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}), + #{[]:=a,42.0:=b,x:={x,y},[a,b]:=list} = + id(#{[]=>a,42.0=>b,x=>{x,y},[a,b]=>list}), + #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}), #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =) -- cgit v1.2.3 From 47e1ed4c0681a73c9d6bc8d24ece85dd77957034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 24 Mar 2015 15:06:25 +0100 Subject: beam_emu: Slightly optimize update_map_{assoc,exact} In the update loop for big maps, the E variable is restored for each turn of the loop. It only needs to be restored if a garbage collection has been performed. Also add a new test case that attempts to force several garbage collections while updating a map, to help us find bugs with incorrect restoration of the E variable after a garbage collection. --- erts/emulator/beam/beam_emu.c | 9 +++---- erts/emulator/test/map_SUITE.erl | 58 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index f25b9f594d..4e64dce95c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6572,10 +6572,9 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) reg[live] = res; erts_garbage_collect(p, 0, reg, live+1); res = reg[live]; + E = p->stop; } - E = p->stop; - new_p += 2; } return res; @@ -6781,7 +6780,6 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) res = map; E = p->stop; while(n--) { - /* assoc can't fail */ GET_TERM(new_p[0], new_key); GET_TERM(new_p[1], val); hx = hashmap_make_hash(new_key); @@ -6795,10 +6793,9 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) reg[live] = res; erts_garbage_collect(p, 0, reg, live+1); res = reg[live]; + E = p->stop; } - E = p->stop; - new_p += 2; } return res; @@ -6808,7 +6805,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) num_old = flatmap_get_size(old_mp); /* - * If the old map is empty, create a new map. + * If the old map is empty, fail. */ if (num_old == 0) { diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 008fa0e11b..72b8ad91ef 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -78,7 +78,8 @@ t_tracing/1, %% instruction-level tests - t_has_map_fields/1 + t_has_map_fields/1, + y_regs/1 ]). -include_lib("stdlib/include/ms_transform.hrl"). @@ -138,7 +139,8 @@ all() -> [ t_tracing, %% instruction-level tests - t_has_map_fields + t_has_map_fields, + y_regs ]. groups() -> []. @@ -2758,5 +2760,57 @@ has_map_fields_3(#{a:=_,b:=_}) -> true; has_map_fields_3(#{[]:=_,42.0:=_}) -> true; has_map_fields_3(#{}) -> false. +y_regs(Config) when is_list(Config) -> + Val = [length(Config)], + Map0 = y_regs_update(#{}, Val), + Map2 = y_regs_update(Map0, Val), + + Map3 = maps:from_list([{I,I*I} || I <- lists:seq(1, 100)]), + Map4 = y_regs_update(Map3, Val), + + true = is_map(Map2) andalso is_map(Map4), + + ok. + +y_regs_update(Map0, Val0) -> + Val1 = {t,Val0}, + K1 = id({key,1}), + K2 = id({key,2}), + Map1 = Map0#{K1=>K1, + a=>Val0,b=>Val0,c=>Val0,d=>Val0,e=>Val0, + f=>Val0,g=>Val0,h=>Val0,i=>Val0,j=>Val0, + k=>Val0,l=>Val0,m=>Val0,n=>Val0,o=>Val0, + p=>Val0,q=>Val0,r=>Val0,s=>Val0,t=>Val0, + u=>Val0,v=>Val0,w=>Val0,x=>Val0,y=>Val0, + z=>Val0, + aa=>Val0,ab=>Val0,ac=>Val0,ad=>Val0,ae=>Val0, + af=>Val0,ag=>Val0,ah=>Val0,ai=>Val0,aj=>Val0, + ak=>Val0,al=>Val0,am=>Val0,an=>Val0,ao=>Val0, + ap=>Val0,aq=>Val0,ar=>Val0,as=>Val0,at=>Val0, + au=>Val0,av=>Val0,aw=>Val0,ax=>Val0,ay=>Val0, + az=>Val0, + K2=>[a,b,c]}, + Map2 = Map1#{K1=>K1, + a:=Val1,b:=Val1,c:=Val1,d:=Val1,e:=Val1, + f:=Val1,g:=Val1,h:=Val1,i:=Val1,j:=Val1, + k:=Val1,l:=Val1,m:=Val1,n:=Val1,o:=Val1, + p:=Val1,q:=Val1,r:=Val1,s:=Val1,t:=Val1, + u:=Val1,v:=Val1,w:=Val1,x:=Val1,y:=Val1, + z:=Val1, + aa:=Val1,ab:=Val1,ac:=Val1,ad:=Val1,ae:=Val1, + af:=Val1,ag:=Val1,ah:=Val1,ai:=Val1,aj:=Val1, + ak:=Val1,al:=Val1,am:=Val1,an:=Val1,ao:=Val1, + ap:=Val1,aq:=Val1,ar:=Val1,as:=Val1,at:=Val1, + au:=Val1,av:=Val1,aw:=Val1,ax:=Val1,ay:=Val1, + az:=Val1, + K2=>[a,b,c]}, + + %% Traverse the maps to validate them. + _ = erlang:phash2({Map1,Map2}, 100000), + + _ = id({K1,K2,Val0,Val1}), %Force use of Y registers. + Map2. + + %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. -- cgit v1.2.3 From 5b8872c37f63b35e13464109e986ef3727588040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 25 Mar 2015 09:47:42 +0100 Subject: Optimize use of i_get_map_element/4 In the i_get_map_element/4 instruction, for literal keys other than atoms, the key would be put into x[0] instead of used directly in the instruction. The reason is that the original implementation of maps only supported atom keys. --- erts/emulator/beam/ops.tab | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 92d9ccb5eb..23f5b75b7a 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1521,21 +1521,21 @@ get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \ i_get_map_elements f s I -get_map_element Fail Src=rxy Key=ax Dst => i_get_map_element Fail Src Key Dst -get_map_element Fail Src=rxy Key=rycq Dst => \ +get_map_element Fail Src=rxy Key=cx Dst => i_get_map_element Fail Src Key Dst +get_map_element Fail Src=rxy Key=ry Dst => \ move Key x | i_get_map_element Fail Src x Dst get_map_element Fail Src Key Dst => jump Fail %macro: i_get_map_element GetMapElement -fail_action -i_get_map_element f r a r -i_get_map_element f x a r -i_get_map_element f y a r -i_get_map_element f r a x -i_get_map_element f x a x -i_get_map_element f y a x -i_get_map_element f r a y -i_get_map_element f x a y -i_get_map_element f y a y +i_get_map_element f r c r +i_get_map_element f x c r +i_get_map_element f y c r +i_get_map_element f r c x +i_get_map_element f x c x +i_get_map_element f y c x +i_get_map_element f r c y +i_get_map_element f x c y +i_get_map_element f y c y i_get_map_element f r x r i_get_map_element f x x r i_get_map_element f y x r -- cgit v1.2.3 From 5f1e301dfec48fccbf865a8b54af5908bebb77c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 25 Mar 2015 11:57:44 +0100 Subject: Teach the loader to pre-compute the hash value for single-key lookups Let the loader pre-compute the hash value when a single, literal key is matched as in: #{<<"some_key">>:=V} = Map In my measurements, this optimization resulted in a 30 percent speedup for short binary keys. Unfortunately, this optimizization makes no difference for small maps with less than 32 keys, since the hash value is not used. Still, there are the following use cases: * A map used instead of a record with more than 32 entries. I have seen some applications with huge records. * Lookup in JSON dictionaries represented as maps. The hash value will only be used when the map is a hash map (currently, that means at least 32 entries). --- erts/emulator/beam/beam_emu.c | 46 ++++++++++++++++++++++++++++++++++++++++++ erts/emulator/beam/beam_load.c | 42 +++++++++++++++++++++++++++++++++----- erts/emulator/beam/ops.tab | 26 +++++++++++------------- 3 files changed, 95 insertions(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 4e64dce95c..5f49032e23 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -710,6 +710,15 @@ void** beam_ops; Dst = _res; \ } while (0) +#define GetMapElementHash(Src, Key, Hx, Dst, Fail) \ + do { \ + Eterm _res = get_map_element_hash(Src, Key, Hx); \ + if (is_non_value(_res)) { \ + Fail; \ + } \ + Dst = _res; \ + } while (0) + #define IsFunction(X, Action) \ do { \ if ( !(is_any_fun(X)) ) { \ @@ -959,6 +968,7 @@ static Eterm update_map_assoc(Process* p, Eterm* reg, static Eterm update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) NOINLINE; static Eterm get_map_element(Eterm map, Eterm key); +static Eterm get_map_element_hash(Eterm map, Eterm key, Uint32 hx); /* * Functions not directly called by process_main(). OK to inline. @@ -6441,6 +6451,42 @@ static Eterm get_map_element(Eterm map, Eterm key) return vs ? *vs : THE_NON_VALUE; } +static Eterm get_map_element_hash(Eterm map, Eterm key, Uint32 hx) +{ + const Eterm *vs; + + if (is_flatmap(map)) { + flatmap_t *mp; + Eterm *ks; + Uint i; + Uint n; + + mp = (flatmap_t *)flatmap_val(map); + ks = flatmap_get_keys(mp); + vs = flatmap_get_values(mp); + n = flatmap_get_size(mp); + if (is_immed(key)) { + for (i = 0; i < n; i++) { + if (ks[i] == key) { + return vs[i]; + } + } + } else { + for (i = 0; i < n; i++) { + if (EQ(ks[i], key)) { + return vs[i]; + } + } + } + return THE_NON_VALUE; + } + + ASSERT(is_hashmap(map)); + ASSERT(hx == hashmap_make_hash(key)); + vs = erts_hashmap_get(hx, key, map); + return vs ? *vs : THE_NON_VALUE; +} + #define GET_TERM(term, dest) \ do { \ Eterm src = (Eterm)(term); \ diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 1a6c6c16ad..ee319d0a78 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4161,9 +4161,30 @@ map_key_sort(LoaderState* stp, GenOpArg Size, GenOpArg* Rest) return 1; } +static int +hash_genop_arg(LoaderState* stp, GenOpArg Key, Uint32* hx) +{ + switch (Key.type) { + case TAG_a: + *hx = hashmap_make_hash(Key.val); + return 1; + case TAG_i: + *hx = hashmap_make_hash(make_small(Key.val)); + return 1; + case TAG_n: + *hx = hashmap_make_hash(NIL); + return 1; + case TAG_q: + *hx = hashmap_make_hash(stp->literals[Key.val].term); + return 1; + default: + return 0; + } +} + /* * Replace a get_map_elements with one key to an instruction with one - * element + * element. */ static GenOp* @@ -4171,18 +4192,29 @@ gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src, GenOpArg Size, GenOpArg* Rest) { GenOp* op; + GenOpArg Key; + Uint32 hx = 0; ASSERT(Size.type == TAG_u); NEW_GENOP(stp, op); op->next = NULL; - op->op = genop_get_map_element_4; - op->arity = 4; - op->a[0] = Fail; op->a[1] = Src; op->a[2] = Rest[0]; - op->a[3] = Rest[1]; + + Key = Rest[0]; + if (hash_genop_arg(stp, Key, &hx)) { + op->arity = 5; + op->op = genop_i_get_map_element_hash_5; + op->a[3].type = TAG_u; + op->a[3].val = (BeamInstr) hx; + op->a[4] = Rest[1]; + } else { + op->arity = 4; + op->op = genop_i_get_map_element_4; + op->a[3] = Rest[1]; + } return op; } diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 23f5b75b7a..456f879ab5 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1512,8 +1512,6 @@ has_map_fields Fail Src Size Rest=* => \ ## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 } -get_map_element/4 - get_map_elements Fail Src=rxy Size=u==2 Rest=* => \ gen_get_map_element(Fail, Src, Size, Rest) get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \ @@ -1521,21 +1519,21 @@ get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \ i_get_map_elements f s I -get_map_element Fail Src=rxy Key=cx Dst => i_get_map_element Fail Src Key Dst -get_map_element Fail Src=rxy Key=ry Dst => \ +i_get_map_element Fail Src=rxy Key=ry Dst => \ move Key x | i_get_map_element Fail Src x Dst -get_map_element Fail Src Key Dst => jump Fail + +%macro: i_get_map_element_hash GetMapElementHash -fail_action +i_get_map_element_hash f r c I r +i_get_map_element_hash f x c I r +i_get_map_element_hash f y c I r +i_get_map_element_hash f r c I x +i_get_map_element_hash f x c I x +i_get_map_element_hash f y c I x +i_get_map_element_hash f r c I y +i_get_map_element_hash f x c I y +i_get_map_element_hash f y c I y %macro: i_get_map_element GetMapElement -fail_action -i_get_map_element f r c r -i_get_map_element f x c r -i_get_map_element f y c r -i_get_map_element f r c x -i_get_map_element f x c x -i_get_map_element f y c x -i_get_map_element f r c y -i_get_map_element f x c y -i_get_map_element f y c y i_get_map_element f r x r i_get_map_element f x x r i_get_map_element f y x r -- cgit v1.2.3 From 9b940b12210500e615ea05b447b0e1be34e70d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 25 Mar 2015 13:10:30 +0100 Subject: Pre-compute hash values for the general get_map_elements instruction See the previous commit for justification and use cases. --- erts/emulator/beam/beam_emu.c | 14 +++++++------- erts/emulator/beam/beam_load.c | 41 +++++++++++++++++++++++++++++++++++++++++ erts/emulator/beam/ops.tab | 2 +- 3 files changed, 49 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 5f49032e23..6fde14bc8d 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2427,7 +2427,7 @@ do { \ * i.e. that it follows a test is_map if needed. */ - n = (Uint)Arg(2) / 2; + n = (Uint)Arg(2) / 3; fs = &Arg(3); /* pattern fields and target registers */ if (is_flatmap(map)) { @@ -2450,12 +2450,11 @@ do { \ if (EQ((Eterm)*fs,*ks)) { PUT_TERM_REG(*vs, fs[1]); n--; - fs += 2; + fs += 3; /* no more values to fetch, we are done */ if (n == 0) break; } - ks++; sz--; - vs++; + ks++, sz--, vs++; } if (n) { @@ -2467,13 +2466,14 @@ do { \ Uint32 hx; ASSERT(is_hashmap(map)); while(n--) { - hx = hashmap_make_hash((Eterm)*fs); - if ((v = erts_hashmap_get(hx,(Eterm)*fs, map)) == NULL) { + hx = fs[2]; + ASSERT(hx == hashmap_make_hash((Eterm)fs[0])); + if ((v = erts_hashmap_get(hx, (Eterm)fs[0], map)) == NULL) { SET_I((BeamInstr *) Arg(0)); goto get_map_elements_fail; } PUT_TERM_REG(*v, fs[1]); - fs += 2; + fs += 3; } } diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index ee319d0a78..8d7beb4eb4 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4218,6 +4218,47 @@ gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src, return op; } +static GenOp* +gen_get_map_elements(LoaderState* stp, GenOpArg Fail, GenOpArg Src, + GenOpArg Size, GenOpArg* Rest) +{ + GenOp* op; + Uint32 hx; + Uint i; + GenOpArg* dst; +#ifdef DEBUG + int good_hash; +#endif + + ASSERT(Size.type == TAG_u); + + NEW_GENOP(stp, op); + op->op = genop_i_get_map_elements_3; + GENOP_ARITY(op, 3 + 3*(Size.val/2)); + op->next = NULL; + op->a[0] = Fail; + op->a[1] = Src; + op->a[2].type = TAG_u; + op->a[2].val = 3*(Size.val/2); + + dst = op->a+3; + for (i = 0; i < Size.val / 2; i++) { + dst[0] = Rest[2*i]; + dst[1] = Rest[2*i+1]; +#ifdef DEBUG + good_hash = +#endif + hash_genop_arg(stp, dst[0], &hx); +#ifdef DEBUG + ASSERT(good_hash); +#endif + dst[2].type = TAG_u; + dst[2].val = (BeamInstr) hx; + dst += 3; + } + return op; +} + static GenOp* gen_has_map_fields(LoaderState* stp, GenOpArg Fail, GenOpArg Src, GenOpArg Size, GenOpArg* Rest) diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 456f879ab5..9bdc9cb88d 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1515,7 +1515,7 @@ has_map_fields Fail Src Size Rest=* => \ get_map_elements Fail Src=rxy Size=u==2 Rest=* => \ gen_get_map_element(Fail, Src, Size, Rest) get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \ - i_get_map_elements Fail Src Size Rest + gen_get_map_elements(Fail, Src, Size, Rest) i_get_map_elements f s I -- cgit v1.2.3 From 61712dda57647804aec8d4f45da8ae1ccfad732c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 26 Mar 2015 10:59:16 +0100 Subject: Tigthen code for the i_get_map_elements/3 instruction --- erts/emulator/beam/beam_emu.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 6fde14bc8d..7e242640ed 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2439,28 +2439,27 @@ do { \ sz = flatmap_get_size(mp); if (sz == 0) { - SET_I((BeamInstr *) Arg(0)); - goto get_map_elements_fail; + ClauseFail(); } ks = flatmap_get_keys(mp); vs = flatmap_get_values(mp); while(sz) { - if (EQ((Eterm)*fs,*ks)) { + if (EQ((Eterm) fs[0], *ks)) { PUT_TERM_REG(*vs, fs[1]); n--; fs += 3; /* no more values to fetch, we are done */ - if (n == 0) break; + if (n == 0) { + I = fs; + Next(-1); + } } ks++, sz--, vs++; } - if (n) { - SET_I((BeamInstr *) Arg(0)); - goto get_map_elements_fail; - } + ClauseFail(); } else { const Eterm *v; Uint32 hx; @@ -2469,19 +2468,14 @@ do { \ hx = fs[2]; ASSERT(hx == hashmap_make_hash((Eterm)fs[0])); if ((v = erts_hashmap_get(hx, (Eterm)fs[0], map)) == NULL) { - SET_I((BeamInstr *) Arg(0)); - goto get_map_elements_fail; + ClauseFail(); } PUT_TERM_REG(*v, fs[1]); fs += 3; } + I = fs; + Next(-1); } - - - I += 4 + Arg(2); -get_map_elements_fail: - ASSERT(VALID_INSTR(*I)); - Goto(*I); } #undef PUT_TERM_REG -- cgit v1.2.3 From 44c5a955c8409cc98a5fbdec7898de46b9e5dfbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 31 Mar 2015 10:15:33 +0200 Subject: erl_term.h: Add is_not_map() macro For consistency with other data types, add the is_not_map() macro. --- erts/emulator/beam/erl_term.h | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index cff012d5d1..602aab46dc 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -1027,6 +1027,7 @@ _ET_DECLARE_CHECKED(struct erl_node_*,external_ref_node,Eterm) #define is_map_header(x) (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_MAP) #define is_map(x) (is_boxed((x)) && is_map_header(*boxed_val(x))) +#define is_not_map(x) (!is_map(x)) #define is_map_rel(RTERM,BASE) is_map(rterm2wterm(RTERM,BASE)) /* number tests */ -- cgit v1.2.3 From e6d3da55a2fe06730f3b92098ff8c13e16e3254b Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 3 Nov 2014 11:32:01 +0100 Subject: erts: Add high accuracy poll timeouts Different poll/select implementations have different ways to handle timeouts of < ms accuracy. Most have extended API like pselect or such, while others rely on using timerfds (epoll_wait). If no high accuracy timeout is available, we simply round up to nearest ms. If we do not roundup we will spin the last ms when waiting for a timeout which is not desirable. --- erts/configure.in | 6 +- erts/emulator/sys/common/erl_poll.c | 162 ++++++++++++++++++++++++++++++++++-- erts/emulator/sys/common/erl_poll.h | 8 ++ 3 files changed, 167 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 481dfe405e..2699ab6f29 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1768,6 +1768,10 @@ AC_CHECK_HEADER(sys/event.h, have_kernel_poll=kqueue) AC_CHECK_HEADER(sys/epoll.h, have_kernel_poll=epoll) AC_CHECK_HEADER(sys/devpoll.h, have_kernel_poll=/dev/poll) +dnl Check if we have timerfds to be used for high accuracy +dnl epoll_wait timeouts +AC_CHECK_HEADERS([sys/timerfd.h]) + dnl Check for kernel SCTP support AC_SUBST(LIBSCTP) if test "x$enable_sctp" != "xno" ; then @@ -2113,7 +2117,7 @@ AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlop gethrtime localtime_r gmtime_r inet_pton \ memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \ flockfile fstat strlcpy strlcat setsid posix2time time2posix \ - setlocale nl_langinfo poll mlockall]) + setlocale nl_langinfo poll mlockall ppoll]) AC_MSG_CHECKING([for isfinite]) AC_TRY_LINK([#include ], diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index f4d4a85ca4..950717881a 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -314,6 +314,9 @@ struct ErtsPollSet_ { #if ERTS_POLL_USE_WAKEUP_PIPE int wake_fds[2]; #endif +#if ERTS_POLL_USE_TIMERFD + int timer_fd; +#endif #if ERTS_POLL_USE_FALLBACK int fallback_used; #endif @@ -579,6 +582,75 @@ create_wakeup_pipe(ErtsPollSet ps) #endif /* ERTS_POLL_USE_WAKEUP_PIPE */ +/* + * --- timer fd ----------------------------------------------------------- + */ + +#if ERTS_POLL_USE_TIMERFD + +/* We use the timerfd when using epoll_wait to get high accuracy + timeouts, i.e. we want to sleep with < ms accuracy. */ + +static void +create_timerfd(ErtsPollSet ps) +{ + int do_wake = 0; + int timer_fd; + timer_fd = timerfd_create(CLOCK_MONOTONIC,0); + ERTS_POLL_EXPORT(erts_poll_control)(ps, + timer_fd, + ERTS_POLL_EV_IN, + 1, &do_wake); +#if ERTS_POLL_USE_FALLBACK + /* We depend on the wakeup pipe being handled by kernel poll */ + if (ps->fds_status[timer_fd].flags & ERTS_POLL_FD_FLG_INFLBCK) + fatal_error("%s:%d:create_wakeup_pipe(): Internal error\n", + __FILE__, __LINE__); +#endif + if (ps->internal_fd_limit <= timer_fd) + ps->internal_fd_limit = timer_fd + 1; + ps->timer_fd = timer_fd; +} + +static ERTS_INLINE void +timerfd_set(ErtsPollSet ps, struct itimerspec *its) +{ +#ifdef DEBUG + struct itimerspec old_its; + int res; + res = timerfd_settime(ps->timer_fd, 0, its, &old_its); + ASSERT(res == 0); + ASSERT(old_its.it_interval.tv_sec == 0 && + old_its.it_interval.tv_nsec == 0 && + old_its.it_value.tv_sec == 0 && + old_its.it_value.tv_nsec == 0); + +#else + timerfd_settime(ps->timer_fd, 0, its, NULL); +#endif +} + +static ERTS_INLINE int +timerfd_clear(ErtsPollSet ps, int res, int max_res) { + + struct itimerspec its; + /* we always have to clear the timer */ + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + its.it_value.tv_sec = 0; + its.it_value.tv_nsec = 0; + timerfd_settime(ps->timer_fd, 0, &its, NULL); + + /* only timeout fd triggered */ + if (res == 1 && ps->res_events[0].data.fd == ps->timer_fd) + return 0; + + return res; +} + +#endif /* ERTS_POLL_USE_TIMERFD */ + + /* * --- Poll set update requests ---------------------------------------------- */ @@ -1516,6 +1588,12 @@ poll_control(ErtsPollSet ps, int fd, ErtsPollEvents events, int on, int *do_wake new_events = ERTS_POLL_EV_NVAL; goto done; } +#endif +#if ERTS_POLL_USE_TIMERFD + if (fd == ps->timer_fd) { + new_events = ERTS_POLL_EV_NVAL; + goto done; + } #endif } @@ -1664,6 +1742,9 @@ save_kp_result(ErtsPollSet ps, ErtsPollResFd pr[], int max_res, int chk_fds_res) #if ERTS_POLL_USE_WAKEUP_PIPE int wake_fd = ps->wake_fds[0]; #endif +#if ERTS_POLL_USE_TIMERFD + int timer_fd = ps->timer_fd; +#endif for (i = 0; i < n; i++) { @@ -1678,6 +1759,11 @@ save_kp_result(ErtsPollSet ps, ErtsPollResFd pr[], int max_res, int chk_fds_res) cleanup_wakeup_pipe(ps); continue; } +#endif +#if ERTS_POLL_USE_TIMERFD + if (fd == timer_fd) { + continue; + } #endif ASSERT(!(ps->fds_status[fd].flags & ERTS_POLL_FD_FLG_INFLBCK)); /* epoll_wait() can repeat the same fd in result array... */ @@ -1752,6 +1838,11 @@ save_kp_result(ErtsPollSet ps, ErtsPollResFd pr[], int max_res, int chk_fds_res) cleanup_wakeup_pipe(ps); continue; } +#endif +#if ERTS_POLL_USE_TIMERFD + if (fd == timer_fd) { + continue; + } #endif revents = ERTS_POLL_EV_N2E(ps->res_events[i].events); pr[res].fd = fd; @@ -2097,7 +2188,7 @@ get_timeout_timeval(ErtsPollSet ps, #endif -#if ERTS_POLL_USE_KQUEUE +#if ERTS_POLL_USE_KQUEUE || (ERTS_POLL_USE_POLL && defined(HAVE_PPOLL)) || ERTS_POLL_USE_TIMERFD static ERTS_INLINE int get_timeout_timespec(ErtsPollSet ps, @@ -2120,7 +2211,7 @@ get_timeout_timespec(ErtsPollSet ps, ASSERT(tsp->tv_sec >= 0); ASSERT(tsp->tv_nsec >= 0); - ASSERT(tsp->tv_nsec < 1000*1000); + ASSERT(tsp->tv_nsec < 1000*1000*1000); return !0; } @@ -2128,6 +2219,22 @@ get_timeout_timespec(ErtsPollSet ps, #endif +#if ERTS_POLL_USE_TIMERFD + +static ERTS_INLINE int +get_timeout_itimerspec(ErtsPollSet ps, + struct itimerspec *itsp, + ErtsMonotonicTime timeout_time) +{ + + itsp->it_interval.tv_sec = 0; + itsp->it_interval.tv_nsec = 0; + + return get_timeout_timespec(ps, &itsp->it_value, timeout_time); +} + +#endif + static ERTS_INLINE int check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) { @@ -2145,12 +2252,29 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) #if ERTS_POLL_USE_EPOLL /* --- epoll ------------------------------- */ if (max_res > ps->res_events_len) grow_res_events(ps, max_res); +#if ERTS_POLL_USE_TIMERFD + { + struct itimerspec its; + timeout = get_timeout_itimerspec(ps, &its, timeout_time); + if (timeout) { +#ifdef ERTS_SMP + erts_thr_progress_prepare_wait(NULL); +#endif + timerfd_set(ps, &its); + res = epoll_wait(ps->kp_fd, ps->res_events, max_res, -1); + res = timerfd_clear(ps, res, max_res); + } else { + res = epoll_wait(ps->kp_fd, ps->res_events, max_res, 0); + } + } +#else /* !ERTS_POLL_USE_TIMERFD */ timeout = (int) get_timeout(ps, 1000, timeout_time); #ifdef ERTS_SMP if (timeout) erts_thr_progress_prepare_wait(NULL); #endif res = epoll_wait(ps->kp_fd, ps->res_events, max_res, timeout); +#endif /* !ERTS_POLL_USE_TIMERFD */ #elif ERTS_POLL_USE_KQUEUE /* --- kqueue ------------------------------ */ struct timespec ts; if (max_res > ps->res_events_len) @@ -2189,7 +2313,15 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) #endif poll_res.dp_timeout = timeout; res = ioctl(ps->kp_fd, DP_POLL, &poll_res); -#elif ERTS_POLL_USE_POLL /* --- poll -------------------------------- */ +#elif ERTS_POLL_USE_POLL && defined(HAVE_PPOLL) /* --- ppoll ---------------- */ + struct timespec ts; + timeout = get_timeout_timespec(ps, &ts, timeout_time); +#ifdef ERTS_SMP + if (timeout) + erts_thr_progress_prepare_wait(NULL); +#endif + res = ppoll(ps->poll_fds, ps->no_poll_fds, &ts, NULL); +#elif ERTS_POLL_USE_POLL /* --- poll --------------------------------- */ timeout = (int) get_timeout(ps, 1000, timeout_time); #ifdef ERTS_SMP if (timeout) @@ -2202,7 +2334,7 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) ERTS_FD_COPY(&ps->input_fds, &ps->res_input_fds); ERTS_FD_COPY(&ps->output_fds, &ps->res_output_fds); - + #ifdef ERTS_SMP if (timeout) erts_thr_progress_prepare_wait(NULL); @@ -2535,6 +2667,9 @@ ERTS_POLL_EXPORT(erts_poll_create_pollset)(void) #if ERTS_POLL_USE_WAKEUP_PIPE create_wakeup_pipe(ps); #endif +#if ERTS_POLL_USE_TIMERFD + create_timerfd(ps); +#endif #if ERTS_POLL_USE_FALLBACK if (kp_fd >= ps->fds_status_len) grow_fds_status(ps, kp_fd); @@ -2625,6 +2760,10 @@ ERTS_POLL_EXPORT(erts_poll_destroy_pollset)(ErtsPollSet ps) if (ps->wake_fds[1] >= 0) close(ps->wake_fds[1]); #endif +#if ERTS_POLL_USE_TIMERFD + if (ps->timer_fd >= 0) + close(ps->timer_fd); +#endif erts_smp_spin_lock(&pollsets_lock); if (ps == pollsets) @@ -2729,6 +2868,9 @@ ERTS_POLL_EXPORT(erts_poll_info)(ErtsPollSet ps, ErtsPollInfo *pip) #if ERTS_POLL_USE_WAKEUP_PIPE pip->poll_set_size++; /* Wakeup pipe */ #endif +#if ERTS_POLL_USE_TIMERFD + pip->poll_set_size++; /* timerfd */ +#endif pip->fallback_poll_set_size = #if !ERTS_POLL_USE_FALLBACK @@ -2857,14 +2999,18 @@ ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet ps, ev[fd] = 0; else { ev[fd] = ps->fds_status[fd].events; + if ( #if ERTS_POLL_USE_WAKEUP_PIPE - if (fd == ps->wake_fds[0] || fd == ps->wake_fds[1]) - ev[fd] |= ERTS_POLL_EV_NVAL; + fd == ps->wake_fds[0] || fd == ps->wake_fds[1] || +#endif +#if ERTS_POLL_USE_TIMERFD + fd == ps->timer_fd || #endif #if ERTS_POLL_USE_KERNEL_POLL - if (fd == ps->kp_fd) - ev[fd] |= ERTS_POLL_EV_NVAL; + fd == ps->kp_fd || #endif + 0) + ev[fd] |= ERTS_POLL_EV_NVAL; } } ERTS_POLLSET_UNLOCK(ps); diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index d02ed2396b..ae2d063805 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -98,6 +98,8 @@ # endif #endif +#define ERTS_POLL_USE_TIMERFD 0 + typedef Uint32 ErtsPollEvents; #undef ERTS_POLL_EV_E2N @@ -130,6 +132,12 @@ struct erts_sys_fd_type { #include +#ifdef HAVE_SYS_TIMERFD_H +#include +#undef ERTS_POLL_USE_TIMERFD +#define ERTS_POLL_USE_TIMERFD 1 +#endif + #define ERTS_POLL_EV_E2N(EV) \ ((__uint32_t) (EV)) #define ERTS_POLL_EV_N2E(EV) \ -- cgit v1.2.3 From 0a1e2b5e6c92cbea2d2e853facb8b4d6f058d541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 14 Apr 2015 13:29:08 +0200 Subject: erts: Refactor erts_queue_message --- erts/emulator/beam/bif.c | 6 +---- erts/emulator/beam/dist.c | 12 ++------- erts/emulator/beam/erl_alloc.c | 6 +---- erts/emulator/beam/erl_bif_ddll.c | 6 +---- erts/emulator/beam/erl_bif_timer.c | 6 +---- erts/emulator/beam/erl_gc.c | 6 +---- erts/emulator/beam/erl_message.c | 32 +++++++--------------- erts/emulator/beam/erl_message.h | 12 ++++++--- erts/emulator/beam/erl_nif.c | 6 +---- erts/emulator/beam/erl_process.c | 28 +++----------------- erts/emulator/beam/erl_time_sup.c | 6 +---- erts/emulator/beam/erl_trace.c | 54 +++++++------------------------------- erts/emulator/beam/io.c | 40 +++++----------------------- erts/emulator/beam/utils.c | 6 +---- 14 files changed, 48 insertions(+), 178 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 022150da55..4f2958c664 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -610,11 +610,7 @@ erts_queue_monitor_message(Process *p, ref_copy = copy_struct(ref, ref_size, &hp, ohp); tup = TUPLE5(hp, am_DOWN, ref_copy, type, item_copy, reason_copy); - erts_queue_message(p, p_locksp, bp, tup, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(p, p_locksp, bp, tup, NIL); } static BIF_RETTYPE diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 32f3cda4f5..142fcb3c00 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -388,11 +388,7 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp) Eterm tup; Eterm *hp = erts_alloc_message_heap(3,&bp,&ohp,rp,&rp_locks); tup = TUPLE2(hp, am_nodedown, name); - erts_queue_message(rp, &rp_locks, bp, tup, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, tup, NIL); } erts_smp_proc_unlock(rp, rp_locks); } @@ -3325,11 +3321,7 @@ send_nodes_mon_msg(Process *rp, } ASSERT(hend == hp); - erts_queue_message(rp, rp_locksp, bp, msg, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, rp_locksp, bp, msg, NIL); } static void diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index f2bceff4eb..e396395dde 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3180,11 +3180,7 @@ reply_alloc_info(void *vair) HRelease(rp, hp_end, hp); } - erts_queue_message(rp, &rp_locks, bp, msg, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, msg, NIL); if (air->req_sched == sched_id) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index fc4f819f56..7b35edc9c4 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -1731,11 +1731,7 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type, hp += REF_THING_SIZE; mess = TUPLE5(hp,type,r,am_driver,driver_name,tag); } - erts_queue_message(proc, &rp_locks, bp, mess, am_undefined -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(proc, &rp_locks, bp, mess, am_undefined); erts_smp_proc_unlock(proc, rp_locks); ERTS_SMP_CHK_NO_PROC_LOCKS; } diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c index 0bd8d20c34..ac4a5644ac 100644 --- a/erts/emulator/beam/erl_bif_timer.c +++ b/erts/emulator/beam/erl_bif_timer.c @@ -373,11 +373,7 @@ bif_timer_timeout(ErtsBifTimer* btm) message = TUPLE3(hp, am_timeout, ref, message); } - erts_queue_message(rp, &rp_locks, bp, message, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, message, NIL); erts_smp_proc_unlock(rp, rp_locks); } } diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 4a116c0740..0b18d2b9e8 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2657,11 +2657,7 @@ reply_gc_info(void *vgcirp) hpp = &hp; } - erts_queue_message(rp, &rp_locks, bp, msg, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, msg, NIL); if (gcirp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 22cbae10d1..247ea10764 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -369,11 +369,7 @@ erts_queue_dist_message(Process *rcvr, tok_label, tok_lastcnt, tok_serial); } #endif - erts_queue_message(rcvr, rcvr_locks, mbuf, msg, token -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rcvr, rcvr_locks, mbuf, msg, token); } else { /* Enqueue message on external format */ @@ -563,15 +559,15 @@ queue_message(Process *c_p, } void -erts_queue_message(Process* receiver, - ErtsProcLocks *receiver_locks, - ErlHeapFragment* bp, - Eterm message, - Eterm seq_trace_token #ifdef USE_VM_PROBES - , Eterm dt_utag +erts_queue_message_probe(Process* receiver, ErtsProcLocks *receiver_locks, + ErlHeapFragment* bp, + Eterm message, Eterm seq_trace_token, Eterm dt_utag) +#else +erts_queue_message(Process* receiver, ErtsProcLocks *receiver_locks, + ErlHeapFragment* bp, + Eterm message, Eterm seq_trace_token) #endif - ) { queue_message(NULL, receiver, @@ -1117,11 +1113,7 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, /* the trace token must in this case be updated by the caller */ seq_trace_output(token, save, SEQ_TRACE_SEND, to->common.id, NULL); temptoken = copy_struct(token, sz_token, &hp, &bp->off_heap); - erts_queue_message(to, to_locksp, bp, save, temptoken -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(to, to_locksp, bp, save, temptoken); } else { ErlOffHeap *ohp; sz_reason = size_object(reason); @@ -1138,11 +1130,7 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, ? from : copy_struct(from, sz_from, &hp, ohp)); save = TUPLE3(hp, am_EXIT, from_copy, mess); - erts_queue_message(to, to_locksp, bp, save, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(to, to_locksp, bp, save, NIL); } } diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 8713941769..8f9ea939e8 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -258,11 +258,17 @@ ErlHeapFragment* erts_resize_message_buffer(ErlHeapFragment *, Uint, Eterm *, Uint); void free_message_buffer(ErlHeapFragment *); void erts_queue_dist_message(Process*, ErtsProcLocks*, ErtsDistExternal *, Eterm); -void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*, Eterm, Eterm #ifdef USE_VM_PROBES - , Eterm dt_utag +void erts_queue_message_probe(Process*, ErtsProcLocks*, ErlHeapFragment*, + Eterm message, Eterm seq_trace_token, Eterm dt_utag); +#define erts_queue_message(RP,RL,BP,Msg,SEQ) \ + erts_queue_message_probe((RP),(RL),(BP),(Msg),(SEQ),NIL) +#else +void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*, + Eterm message, Eterm seq_trace_token); +#define erts_queue_message_probe(RP,RL,BP,Msg,SEQ,TAG) \ + erts_queue_message((RP),(RL),(BP),(Msg),(SEQ)) #endif -); void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm); Sint erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned); void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 660f446a52..776bbf6719 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -357,11 +357,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, if (flush_me) { flush_env(env); /* Needed for ERTS_HOLE_CHECK */ } - erts_queue_message(rp, &rp_locks, frags, msg, am_undefined -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, frags, msg, am_undefined); if (c_p == rp) rp_locks &= ~ERTS_PROC_LOCK_MAIN; if (rp_locks) diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index f74a2ee54c..6518314e30 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -1023,11 +1023,7 @@ reply_sched_wall_time(void *vswtrp) hpp = &hp; } - erts_queue_message(rp, &rp_locks, bp, msg, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, msg, NIL); if (swtrp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -9649,15 +9645,7 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result) ASSERT(hp_start + hsz == hp); #endif - erts_queue_message(rp, - &rp_locks, - bp, - msg, - NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, msg, NIL); if (c_p == rp) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -11366,11 +11354,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, hp = erts_alloc_message_heap(term_size, &bp, &ohp, to, to_locksp); mess = copy_struct(exit_term, term_size, &hp, ohp); - erts_queue_message(to, to_locksp, bp, mess, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(to, to_locksp, bp, mess, NIL); } else { ErlHeapFragment* bp; Eterm* hp; @@ -11386,11 +11370,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, /* the trace token must in this case be updated by the caller */ seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, NULL); temp_token = copy_struct(token, sz_token, &hp, &bp->off_heap); - erts_queue_message(to, to_locksp, bp, mess, temp_token -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(to, to_locksp, bp, mess, temp_token); } } diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index bbdedcc128..9a7466ff48 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -1773,11 +1773,7 @@ send_time_offset_changed_notifications(void *new_offsetp) *patch_refp = ref; ASSERT(hsz == size_object(message_template)); message = copy_struct(message_template, hsz, &hp, ohp); - erts_queue_message(rp, &rp_locks, bp, message, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, message, NIL); } erts_smp_proc_unlock(rp, rp_locks); } diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 2f9969b0e7..aaecc5a02e 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -130,14 +130,9 @@ do { \ enqueue_sys_msg_unlocked(SYS_MSG_TYPE_TRACE, (FPID), (TPID), (MSG), (BP)); \ } while(0) #else -#ifdef USE_VM_PROBES -#define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \ - erts_queue_message((TPROC), NULL, (BP), (MSG), NIL, NIL) -#else #define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \ erts_queue_message((TPROC), NULL, (BP), (MSG), NIL) #endif -#endif /* * NOTE that the ERTS_GET_TRACER_REF() returns from the function (!!!) @@ -636,11 +631,7 @@ profile_send(Eterm from, Eterm message) { hp = erts_alloc_message_heap(sz, &bp, &off_heap, profile_p, 0); msg = copy_struct(message, sz, &hp, &bp->off_heap); - erts_queue_message(profile_p, NULL, bp, msg, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(profile_p, NULL, bp, msg, NIL); } } @@ -1240,11 +1231,8 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SEQTRACE, NIL, NIL, mess, bp); erts_smp_mtx_unlock(&smq_mtx); #else - erts_queue_message(tracer, NULL, bp, mess, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); /* trace_token must be NIL here */ + /* trace_token must be NIL here */ + erts_queue_message(tracer, NULL, bp, mess, NIL); #endif } } @@ -2343,11 +2331,7 @@ monitor_long_schedule_proc(Process *p, BeamInstr *in_fp, BeamInstr *out_fp, Uint #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(monitor_p, NULL, bp, msg, NIL); #endif } void @@ -2408,11 +2392,7 @@ monitor_long_schedule_port(Port *pp, ErtsPortTaskType type, Uint time) #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, pp->common.id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(monitor_p, NULL, bp, msg, NIL); #endif } @@ -2483,11 +2463,7 @@ monitor_long_gc(Process *p, Uint time) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(monitor_p, NULL, bp, msg, NIL); #endif } @@ -2558,11 +2534,7 @@ monitor_large_heap(Process *p) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(monitor_p, NULL, bp, msg, NIL); #endif } @@ -2590,11 +2562,7 @@ monitor_generic(Process *p, Eterm type, Eterm spec) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(monitor_p, NULL, bp, msg, NIL); #endif } @@ -3389,11 +3357,7 @@ sys_msg_dispatcher_func(void *unused) } else { queue_proc_msg: - erts_queue_message(proc,&proc_locks,smqp->bp,smqp->msg,NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(proc,&proc_locks,smqp->bp,smqp->msg,NIL); #ifdef DEBUG_PRINTOUTS erts_fprintf(stderr, "delivered\n"); #endif diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 1db3a9fba7..8cb185cb2b 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1430,15 +1430,7 @@ queue_port_sched_op_reply(Process *rp, bp = erts_resize_message_buffer(bp, used_h_size, &msg, 1); } - erts_queue_message(rp, - rp_locksp, - bp, - msg, - NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, rp_locksp, bp, msg, NIL); } static void @@ -3086,11 +3078,7 @@ deliver_result(Eterm sender, Eterm pid, Eterm res) hp = erts_alloc_message_heap(sz_res + 3, &bp, &ohp, rp, &rp_locks); res = copy_struct(res, sz_res, &hp, ohp); tuple = TUPLE2(hp, sender, res); - erts_queue_message(rp, &rp_locks, bp, tuple, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, tuple, NIL); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); @@ -3186,11 +3174,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to, tuple = TUPLE2(hp, prt->common.id, tuple); hp += 3; - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) @@ -3357,11 +3341,7 @@ deliver_vec_message(Port* prt, /* Port */ tuple = TUPLE2(hp, prt->common.id, tuple); hp += 3; - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) erts_smp_proc_dec_refc(rp); @@ -5061,11 +5041,7 @@ void driver_report_exit(ErlDrvPort ix, int status) hp += 3; tuple = TUPLE2(hp, prt->common.id, tuple); - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) @@ -5665,11 +5641,7 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) HRelease(rp, hp_end, hp); } /* send message */ - erts_queue_message(rp, &rp_locks, bp, mess, am_undefined -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(rp, &rp_locks, bp, mess, am_undefined); } else { if (b2t.ix > b2t.used) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index aecfe04a75..f253aa5cd0 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2305,11 +2305,7 @@ static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len) erts_queue_error_logger_message(from, tuple3, bp); } #else - erts_queue_message(p, NULL /* only used for smp build */, bp, tuple3, NIL -#ifdef USE_VM_PROBES - , NIL -#endif - ); + erts_queue_message(p, NULL /* only used for smp build */, bp, tuple3, NIL); #endif return 0; } -- cgit v1.2.3 From c8a0eca92cdda27c3efdde261c9c32bd445a3794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 14 Apr 2015 15:24:18 +0200 Subject: erts: Refactor dtrace call probes --- erts/emulator/beam/beam_emu.c | 82 ++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 51 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index fdb84aae42..4e01d94b92 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1077,16 +1077,32 @@ init_emulator(void) DTRACE2(nif_return, process_name, mfa); \ } -#else /* USE_VM_PROBES */ - -#define DTRACE_LOCAL_CALL(p, m, f, a) do {} while (0) -#define DTRACE_GLOBAL_CALL(p, m, f, a) do {} while (0) -#define DTRACE_RETURN(p, m, f, a) do {} while (0) -#define DTRACE_BIF_ENTRY(p, m, f, a) do {} while (0) -#define DTRACE_BIF_RETURN(p, m, f, a) do {} while (0) -#define DTRACE_NIF_ENTRY(p, m, f, a) do {} while (0) -#define DTRACE_NIF_RETURN(p, m, f, a) do {} while (0) +#define DTRACE_GLOBAL_CALL_FROM_EXPORT(p,e) \ + do { \ + if (DTRACE_ENABLED(global_function_entry)) { \ + BeamInstr* fp = (BeamInstr *) (((Export *) (e))->addressv[erts_active_code_ix()]); \ + DTRACE_GLOBAL_CALL((p), (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]); \ + } \ + } while(0) + +#define DTRACE_RETURN_FROM_PC(p) \ + do { \ + BeamInstr* fp; \ + if (DTRACE_ENABLED(function_return) && (fp = find_function_from_pc((p)->cp))) { \ + DTRACE_RETURN((p), (Eterm)fp[0], (Eterm)fp[1], (Uint)fp[2]); \ + } \ + } while(0) +#else /* USE_VM_PROBES */ +#define DTRACE_LOCAL_CALL(p, m, f, a) do {} while (0) +#define DTRACE_GLOBAL_CALL(p, m, f, a) do {} while (0) +#define DTRACE_GLOBAL_CALL_FROM_EXPORT(p, e) do {} while (0) +#define DTRACE_RETURN(p, m, f, a) do {} while (0) +#define DTRACE_RETURN_FROM_PC(p) do {} while (0) +#define DTRACE_BIF_ENTRY(p, m, f, a) do {} while (0) +#define DTRACE_BIF_RETURN(p, m, f, a) do {} while (0) +#define DTRACE_NIF_ENTRY(p, m, f, a) do {} while (0) +#define DTRACE_NIF_RETURN(p, m, f, a) do {} while (0) #endif /* USE_VM_PROBES */ /* @@ -1523,12 +1539,7 @@ void process_main(void) * is not loaded, it points to code which will invoke the error handler * (see lb_call_error_handler below). */ -#ifdef USE_VM_CALL_PROBES - if (DTRACE_ENABLED(global_function_entry)) { - 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 + DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, Arg(0)); Dispatchx(); OpCase(i_move_call_ext_cre): { @@ -1538,12 +1549,7 @@ void process_main(void) /* FALL THROUGH */ OpCase(i_call_ext_e): SET_CP(c_p, I+2); -#ifdef USE_VM_CALL_PROBES - if (DTRACE_ENABLED(global_function_entry)) { - 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 + DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, Arg(0)); Dispatchx(); OpCase(i_move_call_ext_only_ecr): { @@ -1551,12 +1557,7 @@ void process_main(void) } /* FALL THROUGH */ OpCase(i_call_ext_only_e): -#ifdef USE_VM_CALL_PROBES - if (DTRACE_ENABLED(global_function_entry)) { - 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 + DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, Arg(0)); Dispatchx(); OpCase(init_y): { @@ -1590,18 +1591,9 @@ void process_main(void) Next(1); } - OpCase(return): { -#ifdef USE_VM_CALL_PROBES - BeamInstr* fptr; -#endif SET_I(c_p->cp); - -#ifdef USE_VM_CALL_PROBES - if (DTRACE_ENABLED(function_return) && (fptr = find_function_from_pc(c_p->cp))) { - DTRACE_RETURN(c_p, (Eterm)fptr[0], (Eterm)fptr[1], (Uint)fptr[2]); - } -#endif + DTRACE_RETURN_FROM_PC(c_p); /* * We must clear the CP to make sure that a stale value do not * create a false module dependcy preventing code upgrading. @@ -6087,13 +6079,7 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg) } else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) { save_calls(p, ep); } - -#ifdef USE_VM_CALL_PROBES - if (DTRACE_ENABLED(global_function_entry)) { - BeamInstr *fptr = (BeamInstr *) ep->addressv[erts_active_code_ix()]; - DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]); - } -#endif + DTRACE_GLOBAL_CALL_FROM_EXPORT(p, ep); return ep->addressv[erts_active_code_ix()]; } @@ -6142,13 +6128,7 @@ fixed_apply(Process* p, Eterm* reg, Uint arity) } else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) { save_calls(p, ep); } - -#ifdef USE_VM_CALL_PROBES - if (DTRACE_ENABLED(global_function_entry)) { - BeamInstr *fptr = (BeamInstr *) ep->addressv[erts_active_code_ix()]; - DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]); - } -#endif + DTRACE_GLOBAL_CALL_FROM_EXPORT(p, ep); return ep->addressv[erts_active_code_ix()]; } -- cgit v1.2.3 From 647c66952afa9036c060a7685071bb27e01c2151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 14 Apr 2015 18:07:05 +0200 Subject: erts: Use make_small for size terms on flat maps --- erts/emulator/beam/erl_bif_guard.c | 27 +++++++++++++-------------- erts/emulator/beam/erl_map.c | 8 +------- 2 files changed, 14 insertions(+), 21 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c index e7d84ebda1..a5a0c06ad6 100644 --- a/erts/emulator/beam/erl_bif_guard.c +++ b/erts/emulator/beam/erl_bif_guard.c @@ -459,25 +459,24 @@ Eterm erts_gc_byte_size_1(Process* p, Eterm* reg, Uint live) Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live) { Eterm arg = reg[live]; - Eterm* hp; - Uint size; if (is_flatmap(arg)) { flatmap_t *mp = (flatmap_t*)flatmap_val(arg); - size = flatmap_get_size(mp); + return make_small(flatmap_get_size(mp)); } else if (is_hashmap(arg)) { + Eterm* hp; + Uint size; size = hashmap_size(arg); - } else { - BIF_ERROR(p, BADARG); - } - if (IS_USMALL(0, size)) { - return make_small(size); + if (IS_USMALL(0, size)) { + return make_small(size); + } + if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) { + erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live); + } + hp = p->htop; + p->htop += BIG_UINT_HEAP_SIZE; + return uint_to_big(size, hp); } - if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) { - erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live); - } - hp = p->htop; - p->htop += BIG_UINT_HEAP_SIZE; - return uint_to_big(size, hp); + BIF_ERROR(p, BADARG); } Eterm erts_gc_abs_1(Process* p, Eterm* reg, Uint live) diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 98023bbb47..bbb7d5e5c6 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -102,14 +102,8 @@ static int hxnodecmpkey(hxnode_t* a, hxnode_t* b); BIF_RETTYPE map_size_1(BIF_ALIST_1) { if (is_flatmap(BIF_ARG_1)) { - Eterm *hp; - Uint hsz = 0; flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1); - Uint n = flatmap_get_size(mp); - - erts_bld_uint(NULL, &hsz, n); - hp = HAlloc(BIF_P, hsz); - BIF_RET(erts_bld_uint(&hp, NULL, n)); + BIF_RET(make_small(flatmap_get_size(mp))); } else if (is_hashmap(BIF_ARG_1)) { Eterm *head, *hp, res; Uint size, hsz=0; -- cgit v1.2.3 From 63e54ea5b37df65f8c040e4cd444b6740caf332d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Apr 2015 21:16:54 +0200 Subject: erts: Add map support to gdb etp command --- erts/etc/unix/etp-commands.in | 57 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index ab2c1126df..3ee092418e 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -351,7 +351,32 @@ define etp-boxed-1 etp-array-1 ((Eterm*)(($arg0)&~0x3)) ($arg1) ($arg1) \ 1 ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1) '}' else - etp-boxed-immediate-1 ($arg0) + if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3c) == 0x3c + # A map + if (((Eterm*)(($arg0) & ~0x3))[0] & 0xc0) == 0x0 + # Flat map + printf "#{Keys:" + etp-1 ((flatmap_t*)(($arg0)&~0x3))->keys (($arg1)+1) + printf " Values:{" + etp-array-1 ((Eterm*)(($arg0)&~0x3)+3) ($arg1) ($arg1) \ + 0 ((flatmap_t*)(($arg0)&~0x3))->size '}' + printf "}" + else + # Hashmap + printf "#<%x>{", (((((Eterm*)(($arg0)&~0x3))[0])>>(6+2+8))&0xffff) + if (((Eterm*)(($arg0) & ~0x3))[0] & 0xc0) >= 0x80 + # head bitmap/array + etp-bitmap-array-1 ((Eterm*)(($arg0)&~0x3)+2) ($arg1) ($arg1) \ + 0 (((((Eterm*)(($arg0)&~0x3))[0])>>(6+2+8))&0xffff) '}' + else + # node bitmap + etp-bitmap-array-1 ((Eterm*)(($arg0)&~0x3)+1) ($arg1) ($arg1) \ + 0 (((((Eterm*)(($arg0)&~0x3))[0])>>(6+2+8))&0xffff) '}' + end + end + else + etp-boxed-immediate-1 ($arg0) + end end end end @@ -474,6 +499,36 @@ define etp-array-1 end end +define etp-bitmap-array-1 +# Args: Eterm* p, int depth, int width, int pos, int bitmap, int end_char +# +# Reentrant +# +# Same as etp-array-1 with size = bitcount(bitmap) +# + if ($arg4) & 1 != 0 + if (($arg1) < $etp_max_depth) && (($arg2) < $etp_max_depth) + etp-1 (($arg0)[($arg3)]) (($arg1)+1) + if (($arg4) & (($arg4)-1)) != 0 + printf "," + end + etp-bitmap-array-1 ($arg0) ($arg1) (($arg2)+1) (($arg3)+1) (($arg4)>>1) ($arg5) + else + printf "...%c", ($arg5) + end + else + if ($arg4) == 0 + printf "%c", ($arg5) + else + etp-bitmap-array-1 $arg0 $arg1 $arg2 $arg3 (($arg4)>>1) $arg5 + + # WARNING: One might be tempted to optimize the bitcounting here + # by passing the bitmap argument as ($arg4 & ($arg4 - 1)). This is a very + # bad idea as arguments are passed as string substitution. + # The size of $arg4 would thus grow exponentially for each recursion. + end + end +end #define etpa-1 -- cgit v1.2.3 From 5bb8262bd4ae8dbcc7438e80de5cedac7ccee1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 31 Mar 2015 08:47:57 +0200 Subject: Raise more descriptive error messages for failed map operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to EEP-43 for maps, a 'badmap' exception should be generated when an attempt is made to update non-map term such as: <<>>#{a=>42} That was not implemented in the OTP 17. José Valim suggested that we should take the opportunity to improve the errors coming from map operations: http://erlang.org/pipermail/erlang-questions/2015-February/083588.html This commit implement better errors from map operations similar to his suggestion. When a map update operation (Map#{...}) or a BIF that expects a map is given a non-map term, the exception will be: {badmap,Term} This kind of exception is similar to the {badfun,Term} exception from operations that expect a fun. When a map operation requires a key that is not present in a map, the following exception will be raised: {badkey,Key} José Valim suggested that the exception should be {badkey,Key,Map}. We decided not to do that because the map could potentially be huge and cause problems if the error propagated through links to other processes. For BIFs, it could be argued that the exceptions could be simply 'badmap' and 'badkey', because the bad map and bad key can be found in the argument list for the BIF in the stack backtrace. However, for the map update operation (Map#{...}), the bad map or bad key will not be included in the stack backtrace, so that information must be included in the exception reason itself. For consistency, the BIFs should raise the same exceptions as update operation. If more than one key is missing, it is undefined which of keys that will be reported in the {badkey,Key} exception. --- erts/emulator/beam/atom.names | 2 +- erts/emulator/beam/beam_emu.c | 31 ++++- erts/emulator/beam/erl_bif_guard.c | 3 +- erts/emulator/beam/erl_map.c | 75 +++++++---- erts/emulator/beam/error.h | 10 +- erts/emulator/test/map_SUITE.erl | 162 ++++++++++++++--------- erts/emulator/test/map_SUITE_data/badmap_17.beam | Bin 0 -> 592 bytes erts/emulator/test/map_SUITE_data/badmap_17.erl | 26 ++++ 8 files changed, 215 insertions(+), 94 deletions(-) create mode 100644 erts/emulator/test/map_SUITE_data/badmap_17.beam create mode 100644 erts/emulator/test/map_SUITE_data/badmap_17.erl (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index ae3f30d82f..8fdcbb4058 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -104,7 +104,7 @@ atom await_sched_wall_time_modifications atom awaiting_load atom awaiting_unload atom backtrace backtrace_depth -atom badarg badarith badarity badfile badmatch badsig badfun +atom badarg badarith badarity badfile badfun badkey badmap badmatch badsig atom bag atom band atom big diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 7e242640ed..6a5128e7f8 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2493,7 +2493,13 @@ do { \ StoreResult(res, Arg(2)); Next(5+Arg(4)); } else { - goto badarg; + /* + * This can only happen if the code was compiled + * with the compiler in OTP 17. + */ + c_p->freason = BADMAP; + c_p->fvalue = map; + goto lb_Cl_error; } } @@ -2511,7 +2517,7 @@ do { \ StoreResult(res, Arg(2)); Next(5+Arg(4)); } else { - goto badarg; + goto lb_Cl_error; } } @@ -5261,7 +5267,9 @@ Eterm error_atom[NUMBER_EXIT_CODES] = { am_notalive, /* 14 */ am_system_limit, /* 15 */ am_try_clause, /* 16 */ - am_notsup /* 17 */ + am_notsup, /* 17 */ + am_badmap, /* 18 */ + am_badkey, /* 19 */ }; /* @@ -5517,6 +5525,8 @@ expand_error_value(Process* c_p, Uint freason, Eterm Value) { case (GET_EXC_INDEX(EXC_TRY_CLAUSE)): case (GET_EXC_INDEX(EXC_BADFUN)): case (GET_EXC_INDEX(EXC_BADARITY)): + case (GET_EXC_INDEX(EXC_BADMAP)): + case (GET_EXC_INDEX(EXC_BADKEY)): /* Some common exceptions: value -> {atom, value} */ ASSERT(is_value(Value)); hp = HAlloc(c_p, 3); @@ -6814,8 +6824,11 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) /* apparently the compiler does not emit is_map instructions, * bad compiler */ - if (is_not_hashmap(map)) + if (is_not_hashmap(map)) { + p->freason = BADMAP; + p->fvalue = map; return THE_NON_VALUE; + } res = map; E = p->stop; @@ -6825,8 +6838,11 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) hx = hashmap_make_hash(new_key); res = erts_hashmap_insert(p, hx, new_key, val, res, 1); - if (is_non_value(res)) + if (is_non_value(res)) { + p->fvalue = new_key; + p->freason = BADKEY; return res; + } if (p->mbuf) { Uint live = Arg(3); @@ -6849,6 +6865,9 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) */ if (num_old == 0) { + E = p->stop; + p->freason = BADKEY; + GET_TERM(new_p[0], p->fvalue); return THE_NON_VALUE; } @@ -6918,6 +6937,8 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) * update list did not previously exist. */ ASSERT(hp == p->htop + need); + p->freason = BADKEY; + p->fvalue = new_key; return THE_NON_VALUE; } #undef GET_TERM diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c index e7d84ebda1..069327ee9d 100644 --- a/erts/emulator/beam/erl_bif_guard.c +++ b/erts/emulator/beam/erl_bif_guard.c @@ -467,7 +467,8 @@ Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live) } else if (is_hashmap(arg)) { size = hashmap_size(arg); } else { - BIF_ERROR(p, BADARG); + p->fvalue = arg; + BIF_ERROR(p, BADMAP); } if (IS_USMALL(0, size)) { return make_small(size); diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 98023bbb47..3aebbfdaa3 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -122,7 +122,8 @@ BIF_RETTYPE map_size_1(BIF_ALIST_1) { BIF_RET(res); } - BIF_ERROR(BIF_P, BADARG); + BIF_P->fvalue = BIF_ARG_1; + BIF_ERROR(BIF_P, BADMAP); } /* maps:to_list/1 */ @@ -150,7 +151,8 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { return hashmap_to_list(BIF_P, BIF_ARG_1); } - BIF_ERROR(BIF_P, BADARG); + BIF_P->fvalue = BIF_ARG_1; + BIF_ERROR(BIF_P, BADMAP); } /* maps:find/2 @@ -217,34 +219,29 @@ BIF_RETTYPE maps_find_2(BIF_ALIST_2) { } BIF_RET(am_error); } - BIF_ERROR(BIF_P, BADARG); + BIF_P->fvalue = BIF_ARG_2; + BIF_ERROR(BIF_P, BADMAP); } /* maps:get/2 * return value if key *matches* a key in the map - * exception bad_key if none matches + * exception badkey if none matches */ BIF_RETTYPE maps_get_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { - Eterm *hp; - Eterm error; const Eterm *value; - char *s_error; value = erts_maps_get(BIF_ARG_1, BIF_ARG_2); if (value) { BIF_RET(*value); } - s_error = "bad_key"; - error = am_atom_put(s_error, sys_strlen(s_error)); - - hp = HAlloc(BIF_P, 3); - BIF_P->fvalue = TUPLE2(hp, error, BIF_ARG_1); - BIF_ERROR(BIF_P, EXC_ERROR_2); + BIF_P->fvalue = BIF_ARG_1; + BIF_ERROR(BIF_P, BADKEY); } - BIF_ERROR(BIF_P, BADARG); + BIF_P->fvalue = BIF_ARG_2; + BIF_ERROR(BIF_P, BADMAP); } /* maps:from_list/1 @@ -911,7 +908,8 @@ BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { BIF_RET(erts_maps_get(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); } - BIF_ERROR(BIF_P, BADARG); + BIF_P->fvalue = BIF_ARG_2; + BIF_ERROR(BIF_P, BADMAP); } /* maps:keys/1 */ @@ -939,7 +937,8 @@ BIF_RETTYPE maps_keys_1(BIF_ALIST_1) { } else if (is_hashmap(BIF_ARG_1)) { BIF_RET(hashmap_keys(BIF_P, BIF_ARG_1)); } - BIF_ERROR(BIF_P, BADARG); + BIF_P->fvalue = BIF_ARG_1; + BIF_ERROR(BIF_P, BADMAP); } /* maps:merge/2 */ @@ -951,6 +950,7 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { /* Will always become a tree */ BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_1, BIF_ARG_2, 0)); } + BIF_P->fvalue = BIF_ARG_2; } else if (is_hashmap(BIF_ARG_1)) { if (is_hashmap(BIF_ARG_2)) { BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2)); @@ -958,8 +958,11 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { /* Will always become a tree */ BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_2, BIF_ARG_1, 1)); } + BIF_P->fvalue = BIF_ARG_2; + } else { + BIF_P->fvalue = BIF_ARG_1; } - BIF_ERROR(BIF_P, BADARG); + BIF_ERROR(BIF_P, BADMAP); } static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { @@ -1398,7 +1401,8 @@ BIF_RETTYPE maps_put_3(BIF_ALIST_3) { if (is_map(BIF_ARG_3)) { BIF_RET(erts_maps_put(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3)); } - BIF_ERROR(BIF_P, BADARG); + BIF_P->fvalue = BIF_ARG_3; + BIF_ERROR(BIF_P, BADMAP); } /* maps:remove/3 */ @@ -1492,7 +1496,8 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { BIF_RET(res); } } - BIF_ERROR(BIF_P, BADARG); + BIF_P->fvalue = BIF_ARG_2; + BIF_ERROR(BIF_P, BADMAP); } int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) { @@ -1688,13 +1693,17 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { /* maps:update/3 */ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { - if (is_map(BIF_ARG_3)) { + if (is_not_map(BIF_ARG_3)) { + BIF_P->fvalue = BIF_ARG_3; + BIF_ERROR(BIF_P, BADMAP); + } else { Eterm res; if (erts_maps_update(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &res)) { BIF_RET(res); } + BIF_P->fvalue = BIF_ARG_1; + BIF_ERROR(BIF_P, BADKEY); } - BIF_ERROR(BIF_P, BADARG); } @@ -1723,7 +1732,8 @@ BIF_RETTYPE maps_values_1(BIF_ALIST_1) { } else if (is_hashmap(BIF_ARG_1)) { BIF_RET(hashmap_values(BIF_P, BIF_ARG_1)); } - BIF_ERROR(BIF_P, BADARG); + BIF_P->fvalue = BIF_ARG_1; + BIF_ERROR(BIF_P, BADMAP); } static Eterm hashmap_to_list(Process *p, Eterm node) { @@ -2546,8 +2556,12 @@ Uint hashmap_over_estimated_heap_size(Uint k) BIF_RETTYPE erts_debug_map_info_1(BIF_ALIST_1) { if (is_hashmap(BIF_ARG_1)) { BIF_RET(hashmap_info(BIF_P,BIF_ARG_1)); + } else if (is_flatmap(BIF_ARG_1)) { + BIF_ERROR(BIF_P, BADARG); + } else { + BIF_P->fvalue = BIF_ARG_1; + BIF_ERROR(BIF_P, BADMAP); } - BIF_ERROR(BIF_P, BADARG); } /* @@ -2560,8 +2574,12 @@ BIF_RETTYPE erts_internal_map_to_tuple_keys_1(BIF_ALIST_1) { if (is_flatmap(BIF_ARG_1)) { flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1); BIF_RET(mp->keys); + } else if (is_hashmap(BIF_ARG_1)) { + BIF_ERROR(BIF_P, BADARG); + } else { + BIF_P->fvalue = BIF_ARG_1; + BIF_ERROR(BIF_P, BADMAP); } - BIF_ERROR(BIF_P, BADARG); } /* @@ -2589,7 +2607,8 @@ BIF_RETTYPE erts_internal_map_type_1(BIF_ALIST_1) { erl_exit(1, "bad header"); } } - BIF_ERROR(BIF_P, BADARG); + BIF_P->fvalue = BIF_ARG_1; + BIF_ERROR(BIF_P, BADMAP); } /* @@ -2629,8 +2648,12 @@ BIF_RETTYPE erts_internal_map_hashmap_children_1(BIF_ALIST_1) { hp = HAlloc(BIF_P, 2*sz); while(sz--) { res = CONS(hp, *ptr++, res); hp += 2; } BIF_RET(res); + } else if (is_flatmap(BIF_ARG_1)) { + BIF_ERROR(BIF_P, BADARG); + } else { + BIF_P->fvalue = BIF_ARG_1; + BIF_ERROR(BIF_P, BADMAP); } - BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/error.h b/erts/emulator/beam/error.h index ddc2c1396d..e63967adb6 100644 --- a/erts/emulator/beam/error.h +++ b/erts/emulator/beam/error.h @@ -140,7 +140,13 @@ #define EXC_NOTSUP ((17 << 8) | EXC_ERROR) /* Not supported */ -#define NUMBER_EXIT_CODES 18 /* The number of exit code indices */ +#define EXC_BADMAP ((18 << 8) | EXC_ERROR) + /* Bad map */ + +#define EXC_BADKEY ((19 << 8) | EXC_ERROR) + /* Bad key in map */ + +#define NUMBER_EXIT_CODES 20 /* The number of exit code indices */ /* * Internal pseudo-error codes. @@ -152,6 +158,8 @@ */ #define BADARG EXC_BADARG #define BADARITH EXC_BADARITH +#define BADKEY EXC_BADKEY +#define BADMAP EXC_BADMAP #define BADMATCH EXC_BADMATCH #define SYSTEM_LIMIT EXC_SYSTEM_LIMIT diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 72b8ad91ef..39549282c0 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -79,7 +79,8 @@ %% instruction-level tests t_has_map_fields/1, - y_regs/1 + y_regs/1, + badmap_17/1 ]). -include_lib("stdlib/include/ms_transform.hrl"). @@ -140,7 +141,8 @@ all() -> [ %% instruction-level tests t_has_map_fields, - y_regs + y_regs, + badmap_17 ]. groups() -> []. @@ -676,9 +678,10 @@ t_map_size(Config) when is_list(Config) -> N = map_size(maps:from_list([{float(I),I}||I<-Is])), %% Error cases. - {'EXIT',{badarg,_}} = (catch map_size([])), - {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), - {'EXIT',{badarg,_}} = (catch map_size(1)), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},_}} = + (catch map_size(T)) + end), ok. build_and_check_size([K|Ks],N,M0) -> @@ -878,9 +881,11 @@ t_update_map_expressions(Config) when is_list(Config) -> #{ "aa" := {$a,$a}, "ac":=41, "dc":=42 } = (maps:from_list([{[K1,K2],{K1,K2}}|| K1 <- Ks, K2 <- Ks]))#{ "ac" := 41, "dc" => 42 }, - %% Error cases, FIXME: should be 'badmap'? - {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), - {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }), + %% Error cases. + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},_}} = + (catch (T)#{a:=42,b=>2}) + end), ok. t_update_assoc(Config) when is_list(Config) -> @@ -895,8 +900,10 @@ t_update_assoc(Config) when is_list(Config) -> M2 = M0#{3.0:=wrong,3.0=>new}, %% Errors cases. - BadMap = id(badmap), - {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},_}} = + (catch T#{nonexisting=>val}) + end), ok. @@ -963,9 +970,6 @@ t_update_assoc_large(Config) when is_list(Config) -> #{10:=a0,20:=b0,13.0:=new,"40":="d0",<<"50">>:="e0"} = M2, M2 = M0#{13.0:=wrong,13.0=>new}, - %% Errors cases. - BadMap = id(badmap), - {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>M0}), ok. t_update_exact(Config) when is_list(Config) -> @@ -987,12 +991,17 @@ t_update_exact(Config) when is_list(Config) -> 1 := update2, 1.0 := new_val2, 1.0 => new_val3, 1.0 => new_val4 }, - %% Errors cases. - {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), - {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), - {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},_}} = + (catch T#{nonexisting=>val}) + end), + Empty = id(#{}), + {'EXIT',{{badkey,nonexisting},_}} = (catch Empty#{nonexisting:=val}), + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), ok. @@ -1071,10 +1080,10 @@ t_update_exact_large(Config) when is_list(Config) -> M2 = M0#{13.0=>wrong,13.0:=new}, %% Errors cases. - {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), - {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), - {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), ok. @@ -1766,12 +1775,17 @@ t_bif_map_get(Config) when is_list(Config) -> "tuple hi" = maps:get({1,1.0}, M1), "v3" = maps:get(<<"k2">>, M1), - %% error case - {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,[])), - {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,<<>>)), - {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})), - {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{})), - {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{b=>1, c=>2})), + %% error cases + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},[{maps,get,_,_}|_]}} = + (catch maps:get(a, T)) + end), + + {'EXIT',{{badkey,{1,1}},[{maps,get,_,_}|_]}} = + (catch maps:get({1,1}, #{{1,1.0} => "tuple"})), + {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} = (catch maps:get(a, #{})), + {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} = + (catch maps:get(a, #{b=>1, c=>2})), ok. t_bif_map_find(Config) when is_list(Config) -> @@ -1804,8 +1818,10 @@ t_bif_map_find(Config) when is_list(Config) -> error = maps:find(1, #{ 1.0 => "float"}), error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key - {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id([]))), - {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id(<<>>))), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},[{maps,find,_,_}|_]}} = + (catch maps:find(a, T)) + end), ok. @@ -1830,8 +1846,10 @@ t_bif_map_is_key(Config) when is_list(Config) -> false = maps:is_key(1.0, maps:put(1, "number", M1)), %% error case - {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id([]))), - {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id(<<>>))), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},[{maps,is_key,_,_}|_]}} = + (catch maps:is_key(a, T)) + end), ok. t_bif_map_keys(Config) when is_list(Config) -> @@ -1845,11 +1863,10 @@ t_bif_map_keys(Config) when is_list(Config) -> [4,int,"hi",<<"key">>] = lists:sort(maps:keys(M1)), %% error case - {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(1 bsl 65 + 3)), - {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(154)), - {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(atom)), - {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys([])), - {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(<<>>)), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},[{maps,keys,_,_}|_]}} = + (catch maps:keys(T)) + end), ok. t_bif_map_new(Config) when is_list(Config) -> @@ -1921,9 +1938,14 @@ t_bif_map_merge(Config) when is_list(Config) -> ok = check_keys_exist(Ks1 ++ Ks2, M11), %% error case - {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)), - {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))), - {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} = + (catch maps:merge(#{}, T)), + {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} = + (catch maps:merge(T, #{})), + {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} = + (catch maps:merge(T, T)) + end), ok. @@ -1962,11 +1984,10 @@ t_bif_map_put(Config) when is_list(Config) -> true = is_members([number,wat,3,"hello",<<"other value">>],maps:values(M6)), %% error case - {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)), - {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)), - {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,atom)), - {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,[])), - {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},[{maps,put,_,_}|_]}} = + (catch maps:put(1, a, T)) + end), ok. is_members(Ks,Ls) when length(Ks) =/= length(Ls) -> false; @@ -2009,11 +2030,10 @@ t_bif_map_remove(Config) when is_list(Config) -> #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)), %% error case - {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,1 bsl 65 + 3)), - {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,154)), - {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,atom)), - {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,[])), - {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},[{maps,remove,_,_}|_]}} = + (catch maps:remove(a, T)) + end), ok. t_bif_map_update(Config) when is_list(Config) -> @@ -2036,10 +2056,10 @@ t_bif_map_update(Config) when is_list(Config) -> 4 := number, 18446744073709551629 := wazzup} = maps:update(18446744073709551629, wazzup, M0), %% error case - {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,{})), - {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,<<"value">>)), - {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(5,none,M0)), - + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},[{maps,update,_,_}|_]}} = + (catch maps:update(1, none, T)) + end), ok. @@ -2066,10 +2086,10 @@ t_bif_map_values(Config) when is_list(Config) -> true = is_members([number,3,"hello2",<<"value2">>]++Vs,maps:values(M5)), %% error case - {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)), - {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(atom)), - {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values([])), - {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(<<>>)), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},[{maps,values,_,_}|_]}} = + (catch maps:values(T)) + end), ok. t_erlang_hash(Config) when is_list(Config) -> @@ -2268,8 +2288,10 @@ t_bif_map_to_list(Config) when is_list(Config) -> <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10})), %% error cases - {'EXIT', {badarg,_}} = (catch maps:to_list(id(a))), - {'EXIT', {badarg,_}} = (catch maps:to_list(id(42))), + do_badmap(fun(T) -> + {'EXIT', {{badmap,T},_}} = + (catch maps:to_list(T)) + end), ok. @@ -2811,6 +2833,26 @@ y_regs_update(Map0, Val0) -> _ = id({K1,K2,Val0,Val1}), %Force use of Y registers. Map2. +do_badmap(Test) -> + Terms = [Test,fun erlang:abs/1,make_ref(),self(),0.0/id(-1), + <<0:1024>>,<<1:1>>,<<>>,<<1,2,3>>, + [],{a,b,c},[a,b],atom,10.0,42,(1 bsl 65) + 3], + [Test(T) || T <- Terms]. + +%% Test that a module compiled with the OTP 17 compiler will +%% generate the correct 'badmap' exception. +badmap_17(Config) -> + case ?MODULE of + map_SUITE -> do_badmap_17(Config); + _ -> {skip,"Run in map_SUITE"} + end. + +do_badmap_17(Config) -> + Mod = badmap_17, + DataDir = test_server:lookup_config(data_dir, Config), + Beam = filename:join(DataDir, Mod), + {module,Mod} = code:load_abs(Beam), + do_badmap(fun Mod:update/1). %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. diff --git a/erts/emulator/test/map_SUITE_data/badmap_17.beam b/erts/emulator/test/map_SUITE_data/badmap_17.beam new file mode 100644 index 0000000000..277fc34b94 Binary files /dev/null and b/erts/emulator/test/map_SUITE_data/badmap_17.beam differ diff --git a/erts/emulator/test/map_SUITE_data/badmap_17.erl b/erts/emulator/test/map_SUITE_data/badmap_17.erl new file mode 100644 index 0000000000..0ec65e0e33 --- /dev/null +++ b/erts/emulator/test/map_SUITE_data/badmap_17.erl @@ -0,0 +1,26 @@ +-module(badmap_17). +-export([update/1]). + +%% Compile this source file with OTP 17. + +update(Map) -> + try + update_1(Map), + error(update_did_not_fail) + catch + error:{badmap,Map} -> + ok + end, + try + update_2(Map), + error(update_did_not_fail) + catch + error:{badmap,Map} -> + ok + end. + +update_1(M) -> + M#{a=>42}. + +update_2(M) -> + M#{a:=42}. -- cgit v1.2.3 From 88b79b875731199a789bbda43d104ea9c8a1f235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 15 Apr 2015 15:43:51 +0200 Subject: erts: Optimize comparison operator for frequent immediates * small integers * atoms --- erts/emulator/beam/utils.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index aecfe04a75..4db2015eea 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2922,15 +2922,44 @@ Sint cmp(Eterm a, Eterm b) } #endif +#if HALFWORD_HEAP +static Sint erts_cmp_compound_rel_opt(Eterm a, Eterm* a_base, + Eterm b, Eterm* b_base, + int exact, int eq_only); +#else +static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only); +#endif + +#if HALFWORD_HEAP +Sint erts_cmp(Eterm a, Eterm* a_base, + Eterm b, Eterm* b_base, + int exact, int eq_only) +#else +Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) +#endif +{ + if (is_atom(a) && is_atom(b)) { + return cmp_atoms(a, b); + } else if (is_both_small(a, b)) { + return (signed_val(a) - signed_val(b)); + } +#if HALFWORD_HEAP + return erts_cmp_compound(a,a_base,b,b_base,exact,eq_only); +#else + return erts_cmp_compound(a,b,exact,eq_only); +#endif +} + + /* erts_cmp(Eterm a, Eterm b, int exact) * exact = 1 -> term-based compare * exact = 0 -> arith-based compare */ #if HALFWORD_HEAP -Sint erts_cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, +static Sint erts_cmp_compound_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, int exact, int eq_only) #else -Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) +static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only) #endif { #define PSTACK_TYPE struct erts_cmp_hashmap_state -- cgit v1.2.3 From 8846021fe1b1bcf79ec3a01037a8a656e161766c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 15 Apr 2015 16:55:55 +0200 Subject: erts: Fix isfinite for windows Add macro erts_isfinite as an OS independent way to check if a float is finite. --- erts/emulator/beam/erl_nif.c | 2 +- erts/emulator/sys/ose/erl_ose_sys.h | 2 ++ erts/emulator/sys/unix/erl_unix_sys.h | 2 ++ erts/emulator/sys/win32/erl_win_sys.h | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index ec82ef251e..8b6cb880a3 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -967,7 +967,7 @@ ERL_NIF_TERM enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i) ERL_NIF_TERM enif_make_double(ErlNifEnv* env, double d) { Eterm* hp; - if (!isfinite(d)) + if (!erts_isfinite(d)) return enif_make_badarg(env); hp = alloc_heap(env,FLOAT_SIZE_OBJECT); FloatDef f; diff --git a/erts/emulator/sys/ose/erl_ose_sys.h b/erts/emulator/sys/ose/erl_ose_sys.h index cd66d95c26..f6526a4714 100644 --- a/erts/emulator/sys/ose/erl_ose_sys.h +++ b/erts/emulator/sys/ose/erl_ose_sys.h @@ -112,6 +112,8 @@ extern clock_t sys_times(SysTimes *buffer); /* No use in having other resolutions than 1 Ms. */ #define SYS_CLOCK_RESOLUTION 1 +#define erts_isfinite finite + #ifdef NO_FPE_SIGNALS #define erts_get_current_fp_exception() NULL diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 26ed2fb558..dda50c486c 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -247,6 +247,8 @@ extern void sys_stop_cat(void); # define HAVE_ISFINITE #endif +#define erts_isfinite isfinite + #ifdef NO_FPE_SIGNALS #define erts_get_current_fp_exception() NULL diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 838f0c61eb..905dcb6b69 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -207,6 +207,8 @@ extern volatile int erl_fp_exception; int _finite(double x); #endif +#define erts_isfinite _finite + /*#define NO_FPE_SIGNALS*/ #define erts_get_current_fp_exception() NULL #define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0) -- cgit v1.2.3 From 1a9bb4615bebc4cc37bf436c7d5c6c8fd7b730e8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 15 Apr 2015 19:01:13 +0200 Subject: erts: Fix divide by zero compile error in nif_SUITE.c --- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index d5109f1e58..b30c484a6c 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1660,6 +1660,13 @@ static ERL_NIF_TERM call_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_T return enif_make_atom(env, "ok"); } +#if !defined(NAN) || !defined(INFINITY) +double zero(void) +{ + return 0.0; +} +#endif + static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { double val; @@ -1673,14 +1680,14 @@ static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_ #ifdef NAN val = NAN; #else - val = 0.0/0.0; + val = 0.0/zero(); #endif } else { /* Verify that enif_make_double raises a badarg for NaN and infinity */ #ifdef INFINITY val = INFINITY; #else - val = 1.0/0.0; + val = 1.0/zero(); #endif } res = enif_make_double(env, val); -- cgit v1.2.3 From 80683a598eda7bea94eb54f9f9557e020b2fdb2c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Apr 2015 13:33:45 +0200 Subject: erts: Fix compile warning in enif_make_double --- erts/emulator/beam/erl_nif.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 8b6cb880a3..6fa8164787 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -967,10 +967,11 @@ ERL_NIF_TERM enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i) ERL_NIF_TERM enif_make_double(ErlNifEnv* env, double d) { Eterm* hp; + FloatDef f; + if (!erts_isfinite(d)) return enif_make_badarg(env); hp = alloc_heap(env,FLOAT_SIZE_OBJECT); - FloatDef f; f.fd = d; PUT_DOUBLE(f, hp); return make_float(hp); -- cgit v1.2.3 From 9f903f6031ff40e415c8807aca19f699d0b553f1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 13 Apr 2015 18:30:18 +0200 Subject: erts: Clearify erl_nif documentation about badarg exception Also state that maximum atom length is 255 characters. --- erts/doc/src/erl_nif.xml | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index feba6daaa0..afeec69f02 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -899,26 +899,34 @@ typedef enum { Create an atom term

Create an atom term from the null-terminated C-string name with iso-latin-1 encoding. If the length of name exceeds the maximum length - allowed for an atom, enif_make_atom returns a badarg exception.

+ allowed for an atom (255 characters), enif_make_atom invokes + enif_make_badarg. +

ERL_NIF_TERMenif_make_atom_len(ErlNifEnv* env, const char* name, size_t len) Create an atom term

Create an atom term from the string name with length len. Null-characters are treated as any other characters. If len is greater than the maximum length - allowed for an atom, enif_make_atom returns a badarg exception.

+ allowed for an atom (255 characters), enif_make_atom invokes + enif_make_badarg. +

ERL_NIF_TERMenif_make_badarg(ErlNifEnv* env) Make a badarg exception. -

Make a badarg exception to be returned from a NIF, and set - an associated exception reason in env. If - enif_make_badarg is called, the term it returns must - be returned from the function that called it. No other return value - is allowed. Once a NIF or any function it calls invokes enif_make_badarg, - the runtime ensures that a badarg exception is raised when the NIF - returns, even if the NIF attempts to return a non-exception term instead. - Also, the term returned from enif_make_badarg may be passed only to - enif_is_exception and - not to any other NIF API function.

+

Make a badarg exception to be returned from a NIF, and associate + it with the environment env. Once a NIF or any function + it calls invokes enif_make_badarg, the runtime ensures that a + badarg exception is raised when the NIF returns, even if the NIF + attempts to return a non-exception term instead. + The return value from enif_make_badarg may only be used as + return value from the NIF that invoked it (direct or indirectly) + or be passed to + enif_is_exception, but + not to any other NIF API function.

+

In earlier versions (older than erts-7.0, OTP 18) the return value + from enif_make_badarg had to be returned from the NIF. This + requirement is now lifted as the return value from the NIF is ignored + if enif_make_badarg has been invoked.

ERL_NIF_TERMenif_make_binary(ErlNifEnv* env, ErlNifBinary* bin) Make a binary term. @@ -936,8 +944,9 @@ typedef enum { ERL_NIF_TERMenif_make_double(ErlNifEnv* env, double d) Create a floating-point term

Create a floating-point term from a double. If the double argument is - not finite or is NaN, enif_make_double returns a badarg exception.

-
+ not finite or is NaN, enif_make_double invokes + enif_make_badarg. +

intenif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding encode) Create an existing atom term @@ -946,7 +955,8 @@ typedef enum { encode. If the atom already exists store the term in *atom and return true, otherwise return false. If the length of name exceeds the maximum length - allowed for an atom, enif_make_existing_atom returns false.

+ allowed for an atom (255 characters), enif_make_existing_atom + returns false.

intenif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding encoding) Create an existing atom term @@ -955,8 +965,8 @@ typedef enum { encode. Null-characters are treated as any other characters. If the atom already exists store the term in *atom and return true, otherwise return false. If len is greater - than the maximum length allowed for an atom, enif_make_existing_atom_len - returns false.

+ than the maximum length allowed for an atom (255 characters), + enif_make_existing_atom_len returns false.

ERL_NIF_TERMenif_make_int(ErlNifEnv* env, int i) Create an integer term -- cgit v1.2.3 From 80e15112a6e31e053ad0670096c23bda2fc341e4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Apr 2015 15:18:33 +0200 Subject: erts: Add enif_has_pending_exception --- erts/doc/src/erl_nif.xml | 8 ++++++++ erts/emulator/beam/erl_nif.c | 5 +++++ erts/emulator/beam/erl_nif.h | 3 ++- erts/emulator/beam/erl_nif_api_funcs.h | 2 ++ erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 2 ++ 5 files changed, 19 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index afeec69f02..5c912e0fe3 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -806,6 +806,12 @@ typedef enum { and return true, or return false if term is not an unsigned integer or is outside the bounds of type unsigned long.

+ intenif_has_pending_exception(ErlNifEnv* env) + Check if an exception has been raised. +

Return true if a pending exception is associated + with the environment env. The only possible exception is currently + badarg (see enif_make_badarg).

+
intenif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin) Inspect the content of a binary

Initialize the structure pointed to by bin with @@ -923,6 +929,8 @@ typedef enum { or be passed to enif_is_exception, but not to any other NIF API function.

+

See also: enif_has_pending_exception. +

In earlier versions (older than erts-7.0, OTP 18) the return value from enif_make_badarg had to be returned from the NIF. This requirement is now lifted as the return value from the NIF is ignored diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 6fa8164787..0a05bdcf8b 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -747,6 +747,11 @@ Eterm enif_make_badarg(ErlNifEnv* env) BIF_ERROR(env->proc, BADARG); } +int enif_has_pending_exception(ErlNifEnv* env) +{ + return env->exception_thrown; +} + int enif_get_atom(ErlNifEnv* env, Eterm atom, char* buf, unsigned len, ErlNifCharEncoding encoding) { diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 849024453c..006547e1d0 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -46,9 +46,10 @@ ** remove enif_schedule_dirty_nif, enif_schedule_dirty_nif_finalizer, enif_dirty_nif_finalizer ** add ErlNifEntry options ** add ErlNifFunc flags +** 2.8: 18.0 add enif_has_pending_exception */ #define ERL_NIF_MAJOR_VERSION 2 -#define ERL_NIF_MINOR_VERSION 7 +#define ERL_NIF_MINOR_VERSION 8 /* * The emulator will refuse to load a nif-lib with a major version diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 630cefae93..bdcbb32c46 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -156,6 +156,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIte ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter)); ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[])); +ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! @@ -305,6 +306,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_map_iterator_prev ERL_NIF_API_FUNC_MACRO(enif_map_iterator_prev) # define enif_map_iterator_get_pair ERL_NIF_API_FUNC_MACRO(enif_map_iterator_get_pair) # define enif_schedule_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_nif) +# define enif_has_pending_exception ERL_NIF_API_FUNC_MACRO(enif_has_pending_exception) /* ** ADD NEW ENTRIES HERE (before this comment) diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index b30c484a6c..87f0e72d9f 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -892,6 +892,7 @@ static ERL_NIF_TERM check_is_exception(ErlNifEnv* env, int argc, const ERL_NIF_T ERL_NIF_TERM badarg = enif_make_badarg(env); if (enif_is_exception(env, error_atom)) return error_atom; if (!enif_is_exception(env, badarg)) return error_atom; + if (!enif_has_pending_exception(env)) return error_atom; return badarg; } @@ -1692,6 +1693,7 @@ static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_ } res = enif_make_double(env, val); assert(enif_is_exception(env, res)); + assert(enif_has_pending_exception(env)); if (strcmp(arg, "tuple") == 0) { return enif_make_tuple2(env, argv[0], res); } else { -- cgit v1.2.3 From 308b03e8afa14e03973330942e7aacf0cc925bf2 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Apr 2015 15:43:40 +0200 Subject: erts: Remove old docs about experimental NIF versions. --- erts/doc/src/erl_nif.xml | 28 ---------------------------- 1 file changed, 28 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 5c912e0fe3..4bad8b253c 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -66,34 +66,6 @@ -

The NIF concept is officially supported from R14B. NIF source code - written for earlier experimental versions might need adaption to run on R14B - or later versions:

- - No incompatible changes between R14B and R14A. - Incompatible changes between R14A and R13B04: - - Environment argument removed for enif_alloc, - enif_realloc, enif_free, enif_alloc_binary, - enif_realloc_binary, enif_release_binary, - enif_alloc_resource, enif_release_resource, - enif_is_identical and enif_compare. - Character encoding argument added to enif_get_atom - and enif_make_existing_atom. - Module argument added to enif_open_resource_type - while changing name spaces of resource types from global to module local. - - - Incompatible changes between R13B04 and R13B03: - - The function prototypes of the NIFs have changed to expect argc and argv - arguments. The arity of a NIF is by that no longer limited to 3. - enif_get_data renamed as enif_priv_data. - enif_make_string got a third argument for character encoding. - - - -

A minimal example of a NIF library can look like this:

-- cgit v1.2.3 From dd9ad8da73e15e89c8ab27efdd47a8bda8019957 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 15 Apr 2015 17:30:00 +0200 Subject: erts: Reject non-finite float terms in erl_drv_output_term --- erts/emulator/beam/io.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 012a7d1a4b..07dc0db3b9 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -5581,7 +5581,9 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) mess = make_float(hp); f.fd = *((double *) ptr[0]); - PUT_DOUBLE(f, hp); + if (!erts_isfinite(f.fd)) + ERTS_DDT_FAIL; + PUT_DOUBLE(f, hp); hp += FLOAT_SIZE_OBJECT; ptr++; break; -- cgit v1.2.3 From 69e0f3a87346a6238b65a00a08b1902821612b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 16 Apr 2015 17:25:59 +0200 Subject: erts: Remove instruction_count command option * We use compile directive icount instead --- erts/emulator/beam/erl_init.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 86d3416423..a4f1e0d7ee 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -1955,11 +1955,6 @@ erl_start(int argc, char **argv) goto time_correction_false; else if (sys_strcmp(argv[i]+2, "true") == 0) goto time_correction_true; -#ifdef ERTS_OPCODE_COUNTER_SUPPORT - else if (argv[i][2] == 'i') { /* -ci: undcoumented option*/ - count_instructions = 1; - } -#endif else if (argv[i][2] == '\0') { if (i + 1 >= argc) goto time_correction_false; -- cgit v1.2.3 From 544d962f3401e96d1bd9efd1209ab53ade9568b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 16 Apr 2015 17:34:00 +0200 Subject: erts: Assume counting opcodes are correctly generated * Assertion is only removed because we are in icount mode. --- erts/emulator/beam/beam_emu.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 6a3415d9f4..bb7b799950 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5137,8 +5137,6 @@ do { \ #ifndef NO_JUMP_TABLE #ifdef ERTS_OPCODE_COUNTER_SUPPORT - /* Are tables correctly generated by beam_makeops? */ - ERTS_CT_ASSERT(sizeof(counting_opcodes) == sizeof(opcodes)); #ifdef DEBUG counting_opcodes[op_catch_end_y] = LabelAddr(lb_catch_end_y); #endif -- cgit v1.2.3 From b3e121949ad524be44878d656e923dd187780eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 20 Apr 2015 10:01:06 +0200 Subject: erts: Fix halfword compare --- erts/emulator/beam/utils.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index ae62a7a708..edefc5170f 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2920,16 +2920,16 @@ Sint cmp(Eterm a, Eterm b) #if HALFWORD_HEAP static Sint erts_cmp_compound_rel_opt(Eterm a, Eterm* a_base, - Eterm b, Eterm* b_base, - int exact, int eq_only); + Eterm b, Eterm* b_base, + int exact, int eq_only); #else static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only); #endif #if HALFWORD_HEAP -Sint erts_cmp(Eterm a, Eterm* a_base, - Eterm b, Eterm* b_base, - int exact, int eq_only) +Sint erts_cmp_rel_opt(Eterm a, Eterm* a_base, + Eterm b, Eterm* b_base, + int exact, int eq_only) #else Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) #endif @@ -2940,7 +2940,7 @@ Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) return (signed_val(a) - signed_val(b)); } #if HALFWORD_HEAP - return erts_cmp_compound(a,a_base,b,b_base,exact,eq_only); + return erts_cmp_compound_rel_opt(a,a_base,b,b_base,exact,eq_only); #else return erts_cmp_compound(a,b,exact,eq_only); #endif @@ -2952,8 +2952,9 @@ Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) * exact = 0 -> arith-based compare */ #if HALFWORD_HEAP -static Sint erts_cmp_compound_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, - int exact, int eq_only) +static Sint erts_cmp_compound_rel_opt(Eterm a, Eterm* a_base, + Eterm b, Eterm* b_base, + int exact, int eq_only) #else static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only) #endif -- cgit v1.2.3 From 87574e845a84c1d6744ac533803ae4fbd2dda8b8 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Mon, 23 Mar 2015 15:20:30 +0100 Subject: erts/configure.in: handle more 'linux' spellings The configuration code which canonicalizes the operating system name into OPSYS requires Linux to be spelled 'linux' or 'Linux'. This is a problem in some build environments, e.g. RPM, which supply --build and --host using the longer 'linux-gnu' spelling. The effect is that OPSYS becomes 'noopsys' and some checks in erts/configure.in do not work as expected, e.g. the auto-enabling of HiPE may not happen. Fixed by matching on 'linux*' not just 'linux'. On ARM there are even longer variants such as 'linux-gnueabi' and 'linux-gnueabihf': these are also correctly mapped to 'linux' now. --- erts/configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 481dfe405e..873e1e30fe 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -631,7 +631,7 @@ fi case $chk_opsys_ in win32) OPSYS=win32;; solaris2.*|SunOS5.*) OPSYS=sol2;; - linux|Linux) OPSYS=linux;; + linux*|Linux) OPSYS=linux;; darwin|Darwin) OPSYS=darwin;; freebsd|FreeBSD) OPSYS=freebsd;; *) OPSYS=noopsys -- cgit v1.2.3 From 5dead30ea35a1cd97ebc8dd8f12e27f0a27348af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 20 Apr 2015 14:43:53 +0200 Subject: erts: Enable different abort signal from heart By using environment variable HEART_KILL_SIGNAL, heart can now use a different signal to kill the old running Erlang. By default the signal is SIGKILL but SIGABRT may also be used by setting environment variable: HEART_KILL_SIGNAL=SIGABRT --- erts/etc/common/heart.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index a4f34e21d0..0d1dcacf2c 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -117,11 +117,12 @@ #define HEART_COMMAND_ENV "HEART_COMMAND" #define ERL_CRASH_DUMP_SECONDS_ENV "ERL_CRASH_DUMP_SECONDS" +#define HEART_KILL_SIGNAL "HEART_KILL_SIGNAL" -#define MSG_HDR_SIZE 2 -#define MSG_HDR_PLUS_OP_SIZE 3 -#define MSG_BODY_SIZE 2048 -#define MSG_TOTAL_SIZE 2050 +#define MSG_HDR_SIZE (2) +#define MSG_HDR_PLUS_OP_SIZE (3) +#define MSG_BODY_SIZE (2048) +#define MSG_TOTAL_SIZE (2050) unsigned char cmd[MSG_BODY_SIZE]; @@ -555,14 +556,22 @@ kill_old_erlang(void){ static void kill_old_erlang(void){ pid_t pid; - int i; - int res; + int i, res; + int sig = SIGKILL; + char *sigenv = NULL; + + sigenv = get_env(HEART_KILL_SIGNAL); + if (sigenv && strcmp(sigenv, "SIGABRT") == 0) { + print_error("kill signal SIGABRT requested"); + sig = SIGABRT; + } + if(heart_beat_kill_pid != 0){ pid = (pid_t) heart_beat_kill_pid; - res = kill(pid,SIGKILL); + res = kill(pid,sig); for(i=0; i < 5 && res == 0; ++i){ sleep(1); - res = kill(pid,SIGKILL); + res = kill(pid,sig); } if(errno != ESRCH){ print_error("Unable to kill old process, " -- cgit v1.2.3 From 91fb6636f95f882b75170fb77c412ccc4a9b75f4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 20 Apr 2015 15:52:27 +0200 Subject: erts,hipe: Fix bug in binary matching of writable binary Seen symptom: Hipe compiled code with <> = Bin does sometimes not match even though Bin contains a valid utf8 character. There might be other possible binary matching symptoms, as the problem is not utf8 specific. Problem: A writable binary was not "emasculated" when the matching started (as it should) by the hipe compiled code. Fix: Add a new primop emasculate_binary(Bin) that is called when a matchstate is created. ToDo: There are probably room for optimization. For example only call emasculate_binary if ProcBin.flags is set. --- erts/emulator/hipe/hipe_bif0.c | 8 ++++++++ erts/emulator/hipe/hipe_bif0.tab | 1 + erts/emulator/hipe/hipe_bif_list.m4 | 2 ++ erts/emulator/hipe/hipe_native_bif.h | 3 +++ erts/emulator/hipe/hipe_primops.h | 1 + 5 files changed, 15 insertions(+) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 8af174170d..de2ea0ecde 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1028,6 +1028,14 @@ void hipe_emulate_fpe(Process* p) } #endif +void hipe_emasculate_binary(Eterm bin) +{ + ProcBin* pb = (ProcBin *) boxed_val(bin); + if (pb->thing_word == HEADER_PROC_BIN && pb->flags != 0) { + erts_emasculate_writable_binary(pb); + } +} + #if 0 /* XXX: unused */ /* * At least parts of this should be inlined in native code. diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab index 2514b1c3a5..620749a511 100644 --- a/erts/emulator/hipe/hipe_bif0.tab +++ b/erts/emulator/hipe/hipe_bif0.tab @@ -142,4 +142,5 @@ atom bs_get_utf16 atom bs_validate_unicode atom bs_validate_unicode_retract atom emulate_fpe +atom emasculate_binary diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 5f92b6bac4..58b20c7752 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -250,6 +250,8 @@ gc_bif_interface_0(nbif_check_get_msg, hipe_check_get_msg) nocons_nofail_primop_interface_0(nbif_emulate_fpe, hipe_emulate_fpe) #endif +noproc_primop_interface_1(nbif_emasculate_binary, hipe_emasculate_binary) + /* * SMP-specific stuff */ diff --git a/erts/emulator/hipe/hipe_native_bif.h b/erts/emulator/hipe/hipe_native_bif.h index 3f460a5a5c..574e20e2e4 100644 --- a/erts/emulator/hipe/hipe_native_bif.h +++ b/erts/emulator/hipe/hipe_native_bif.h @@ -98,6 +98,9 @@ AEXTERN(void,nbif_emulate_fpe,(Process*)); void hipe_emulate_fpe(Process*); #endif +AEXTERN(void,nbif_emasculate_binary,(Eterm)); +void hipe_emasculate_binary(Eterm); + /* * Stuff that is different in SMP and non-SMP. */ diff --git a/erts/emulator/hipe/hipe_primops.h b/erts/emulator/hipe/hipe_primops.h index 52b4681cfe..236f6d0a29 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_emasculate_binary, &nbif_emasculate_binary) PRIMOP_LIST(am_debug_native_called, &nbif_hipe_bifs_debug_native_called) #if defined(__sparc__) -- cgit v1.2.3 From 48c52c2fd67ea2579ce5f4b9dd0d491a54d0a5cb Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Tue, 21 Apr 2015 14:02:27 +0200 Subject: update java version --- erts/configure.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 481dfe405e..9d88d5849a 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -4734,11 +4734,11 @@ fi AC_CHECK_PROGS(JAVAC, $check_javac) if test -n "$JAVAC"; then dnl Make sure it's at least JDK 1.5 - AC_CACHE_CHECK(for JDK version 1.5, - ac_cv_prog_javac_ver_1_5, + AC_CACHE_CHECK(for JDK version 1.6, + ac_cv_prog_javac_ver_1_6, [ERL_TRY_LINK_JAVA([], [for (String i : args);], - ac_cv_prog_javac_ver_1_5=yes, ac_cv_prog_javac_ver_1_5=no)]) - if test $ac_cv_prog_javac_ver_1_5 = no; then + ac_cv_prog_javac_ver_1_6=yes, ac_cv_prog_javac_ver_1_5=no)]) + if test $ac_cv_prog_javac_ver_1_6 = no; then unset -v JAVAC fi fi -- cgit v1.2.3 From e40aab7e9fb9d622e9879efa43af2ce30b287450 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 21 Apr 2015 15:33:32 +0200 Subject: erts,hipe: Optimize away calls to emasculate_binary Only call emasculate_binary if ProcBin.flags is set, which means it's a writable binary. --- erts/emulator/hipe/hipe_bif0.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index de2ea0ecde..bb61e71b14 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1031,9 +1031,9 @@ void hipe_emulate_fpe(Process* p) void hipe_emasculate_binary(Eterm bin) { ProcBin* pb = (ProcBin *) boxed_val(bin); - if (pb->thing_word == HEADER_PROC_BIN && pb->flags != 0) { - erts_emasculate_writable_binary(pb); - } + ASSERT(pb->thing_word == HEADER_PROC_BIN); + ASSERT(pb->flags != 0); + erts_emasculate_writable_binary(pb); } #if 0 /* XXX: unused */ -- cgit v1.2.3 From f38aa5a70ff1244e0e3a88c4980b1e87274c0741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 20 Apr 2015 18:09:04 +0200 Subject: erts: Brute force float comparisons as well Increases float comparison speed by ~120% --- erts/emulator/beam/utils.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index edefc5170f..8f6335d5dd 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2938,6 +2938,11 @@ Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) return cmp_atoms(a, b); } else if (is_both_small(a, b)) { return (signed_val(a) - signed_val(b)); + } else if (is_float_rel(a, a_base) && is_float_rel(b, b_base)) { + FloatDef af, bf; + GET_DOUBLE_REL(a, af, a_base); + GET_DOUBLE_REL(b, bf, b_base); + return float_comp(af.fd, bf.fd); } #if HALFWORD_HEAP return erts_cmp_compound_rel_opt(a,a_base,b,b_base,exact,eq_only); -- cgit v1.2.3 From 086af2a9f8fffefd8fb8330cc45bdf1e698809df Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Fri, 27 Mar 2015 13:13:00 +0100 Subject: erts/hipe: unbreak arity 4 BIFs This fixes arity 4 BIF support in HiPE, following its introduction on master (OTP 18) via the nox/ets-update_counter-4 merge. - define standard_bif_interface_4, nbif_4_gc_after_bif, and nbif_4_simple_exception on ARM: unbreaks the build on ARM - remove bogus stack re-alignment from standard_bif_interface_4 on AMD64: for 4-arg BIFs the stack is already aligned, and the code would misalign the C stack which violates the ABI and may cause alignment faults in vectorized code - the nbif_4_simple_exception OPD name on PPC64 was incorrectly using the nbif_3_simple_exception OPD name: this would have caused a multiple definition error in the assembler or linker In addition there are a few cleanups: - fix standard_bif_interface_N comment on x86 - fix standard_bif_interface_4 comment on SPARC - separate nbif_N_simple_exception blocks by empty lines on PPC, like on ARM, to clearly show which things belong together - fix standard_bif_interface_N comment on ARM - fix standard_bif_interface_4 on AMD64 to match the indentation and spacing conventions of the rest of that file --- erts/emulator/hipe/hipe_amd64_bifs.m4 | 59 +++++++++++++++++------------------ erts/emulator/hipe/hipe_arm_asm.m4 | 5 +++ erts/emulator/hipe/hipe_arm_bifs.m4 | 36 ++++++++++++++++++++- erts/emulator/hipe/hipe_arm_glue.S | 12 +++++++ erts/emulator/hipe/hipe_ppc_glue.S | 6 +++- erts/emulator/hipe/hipe_sparc_bifs.m4 | 2 +- erts/emulator/hipe/hipe_x86_bifs.m4 | 2 +- 7 files changed, 88 insertions(+), 34 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4 index 7d94aa05b3..74cb9112ce 100644 --- a/erts/emulator/hipe/hipe_amd64_bifs.m4 +++ b/erts/emulator/hipe/hipe_amd64_bifs.m4 @@ -159,37 +159,36 @@ define(standard_bif_interface_4, ` #ifndef HAVE_$1 #`define' HAVE_$1 - TEXT - .align 4 - GLOBAL(ASYM($1)) + TEXT + .align 4 + GLOBAL(ASYM($1)) ASYM($1): - /* set up the parameters */ - movq P, %rdi - NBIF_ARG(%rsi,4,0) - NBIF_ARG(%rdx,4,1) - NBIF_ARG(%rcx,4,2) - NBIF_ARG(%r8,4,3) - - /* make the call on the C stack */ - SWITCH_ERLANG_TO_C - pushq %r8 - pushq %rcx - pushq %rdx - pushq %rsi - movq %rsp, %rsi /* Eterm* BIF__ARGS */ - sub $(8), %rsp /* stack frame 16-byte alignment */ - CALL_BIF($2) - add $(4*8 + 8), %rsp - TEST_GOT_MBUF - SWITCH_C_TO_ERLANG - - /* throw exception if failure, otherwise return */ - TEST_GOT_EXN - jz nbif_4_simple_exception - NBIF_RET(4) - HANDLE_GOT_MBUF(4) - SET_SIZE(ASYM($1)) - TYPE_FUNCTION(ASYM($1)) + /* set up the parameters */ + movq P, %rdi + NBIF_ARG(%rsi,4,0) + NBIF_ARG(%rdx,4,1) + NBIF_ARG(%rcx,4,2) + NBIF_ARG(%r8,4,3) + + /* make the call on the C stack */ + SWITCH_ERLANG_TO_C + pushq %r8 + pushq %rcx + pushq %rdx + pushq %rsi + movq %rsp, %rsi /* Eterm* BIF__ARGS */ + CALL_BIF($2) + add $(4*8), %rsp + TEST_GOT_MBUF + SWITCH_C_TO_ERLANG + + /* throw exception if failure, otherwise return */ + TEST_GOT_EXN + jz nbif_4_simple_exception + NBIF_RET(4) + HANDLE_GOT_MBUF(4) + SET_SIZE(ASYM($1)) + TYPE_FUNCTION(ASYM($1)) #endif') define(standard_bif_interface_0, diff --git a/erts/emulator/hipe/hipe_arm_asm.m4 b/erts/emulator/hipe/hipe_arm_asm.m4 index b2e3f83d1e..ca6aef2f8d 100644 --- a/erts/emulator/hipe/hipe_arm_asm.m4 +++ b/erts/emulator/hipe/hipe_arm_asm.m4 @@ -163,6 +163,10 @@ define(NBIF_ARG,`ifelse(eval($3 >= NR_ARG_REGS),0,`NBIF_REG_ARG($1,$3)',`NBIF_ST `/* #define NBIF_ARG_3_0 'NBIF_ARG(r1,3,0)` */' `/* #define NBIF_ARG_3_1 'NBIF_ARG(r2,3,1)` */' `/* #define NBIF_ARG_3_2 'NBIF_ARG(r3,3,2)` */' +`/* #define NBIF_ARG_4_0 'NBIF_ARG(r1,4,0)` */' +`/* #define NBIF_ARG_4_1 'NBIF_ARG(r2,4,1)` */' +`/* #define NBIF_ARG_4_2 'NBIF_ARG(r3,4,2)` */' +`/* #define NBIF_ARG_4_3 'NBIF_ARG(r4,4,3)` */' `/* #define NBIF_ARG_5_0 'NBIF_ARG(r1,5,0)` */' `/* #define NBIF_ARG_5_1 'NBIF_ARG(r2,5,1)` */' `/* #define NBIF_ARG_5_2 'NBIF_ARG(r3,5,2)` */' @@ -186,6 +190,7 @@ define(NBIF_RET,`NBIF_RET_N(eval(RET_POP($1)))')dnl `/* #define NBIF_RET_1 'NBIF_RET(1)` */' `/* #define NBIF_RET_2 'NBIF_RET(2)` */' `/* #define NBIF_RET_3 'NBIF_RET(3)` */' +`/* #define NBIF_RET_4 'NBIF_RET(4)` */' `/* #define NBIF_RET_5 'NBIF_RET(5)` */' dnl diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4 index 884240be9c..6abc7545e0 100644 --- a/erts/emulator/hipe/hipe_arm_bifs.m4 +++ b/erts/emulator/hipe/hipe_arm_bifs.m4 @@ -42,9 +42,10 @@ define(TEST_GOT_MBUF,`ldr r1, [P, #P_MBUF] /* `TEST_GOT_MBUF' */ * standard_bif_interface_1(nbif_name, cbif_name) * standard_bif_interface_2(nbif_name, cbif_name) * standard_bif_interface_3(nbif_name, cbif_name) + * standard_bif_interface_4(nbif_name, cbif_name) * standard_bif_interface_0(nbif_name, cbif_name) * - * Generate native interface for a BIF with 1-3 parameters and + * Generate native interface for a BIF with 0-4 parameters and * standard failure mode. */ define(standard_bif_interface_1, @@ -134,6 +135,39 @@ $1: .type $1, %function #endif') +define(standard_bif_interface_4, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + .global $1 +$1: + /* Set up C argument registers. */ + mov r0, P + NBIF_ARG(r1,4,0) + NBIF_ARG(r2,4,1) + NBIF_ARG(r3,4,2) + NBIF_ARG(r4,4,3) + + /* Save caller-save registers and call the C function. */ + SAVE_CONTEXT_BIF + str r1, [r0, #P_ARG0] /* Store BIF__ARGS in def_arg_reg[] */ + str r2, [r0, #P_ARG1] + str r3, [r0, #P_ARG2] + str r4, [r0, #P_ARG3] + add r1, r0, #P_ARG0 + CALL_BIF($2) + TEST_GOT_MBUF(4) + + /* Restore registers. Check for exception. */ + cmp r0, #THE_NON_VALUE + RESTORE_CONTEXT_BIF + beq nbif_4_simple_exception + NBIF_RET(4) + .ltorg + .size $1, .-$1 + .type $1, %function +#endif') + define(standard_bif_interface_0, ` #ifndef HAVE_$1 diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S index e7ff267606..edcabfd7a4 100644 --- a/erts/emulator/hipe/hipe_arm_glue.S +++ b/erts/emulator/hipe/hipe_arm_glue.S @@ -330,6 +330,12 @@ nbif_2_gc_after_bif: .type nbif_3_gc_after_bif, %function nbif_3_gc_after_bif: mov r1, #3 + b .gc_after_bif + + .global nbif_4_gc_after_bif + .type nbif_4_gc_after_bif, %function +nbif_4_gc_after_bif: + mov r1, #4 /*FALLTHROUGH*/ .gc_after_bif: str r1, [P, #P_NARITY] @@ -376,6 +382,12 @@ nbif_2_simple_exception: .type nbif_3_simple_exception, %function nbif_3_simple_exception: mov r1, #3 + b .nbif_simple_exception + + .global nbif_4_simple_exception + .type nbif_4_simple_exception, %function +nbif_4_simple_exception: + mov r1, #4 /*FALLTHROUGH*/ .nbif_simple_exception: ldr r0, [P, #P_FREASON] diff --git a/erts/emulator/hipe/hipe_ppc_glue.S b/erts/emulator/hipe/hipe_ppc_glue.S index b07f4bc9c8..109289116b 100644 --- a/erts/emulator/hipe/hipe_ppc_glue.S +++ b/erts/emulator/hipe/hipe_ppc_glue.S @@ -510,22 +510,26 @@ CSYM(nbif_4_gc_after_bif): CSYM(nbif_0_simple_exception): li r4, 0 b .nbif_simple_exception + OPD(nbif_1_simple_exception) GLOBAL(CSYM(nbif_1_simple_exception)) CSYM(nbif_1_simple_exception): li r4, 1 b .nbif_simple_exception + OPD(nbif_2_simple_exception) GLOBAL(CSYM(nbif_2_simple_exception)) CSYM(nbif_2_simple_exception): li r4, 2 b .nbif_simple_exception + OPD(nbif_3_simple_exception) GLOBAL(CSYM(nbif_3_simple_exception)) CSYM(nbif_3_simple_exception): li r4, 3 b .nbif_simple_exception - OPD(nbif_3_simple_exception) + + OPD(nbif_4_simple_exception) GLOBAL(CSYM(nbif_4_simple_exception)) CSYM(nbif_4_simple_exception): li r4, 4 diff --git a/erts/emulator/hipe/hipe_sparc_bifs.m4 b/erts/emulator/hipe/hipe_sparc_bifs.m4 index 8dfb28c8e0..1d0ff8c16e 100644 --- a/erts/emulator/hipe/hipe_sparc_bifs.m4 +++ b/erts/emulator/hipe/hipe_sparc_bifs.m4 @@ -54,7 +54,7 @@ define(HANDLE_GOT_MBUF,` * standard_bif_interface_1(nbif_name, cbif_name) * standard_bif_interface_2(nbif_name, cbif_name) * standard_bif_interface_3(nbif_name, cbif_name) - * standard_bif_interface_3(nbif_name, cbif_name) + * standard_bif_interface_4(nbif_name, cbif_name) * standard_bif_interface_0(nbif_name, cbif_name) * * Generate native interface for a BIF with 0-4 parameters and diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4 index b0064ee628..bf549c90e4 100644 --- a/erts/emulator/hipe/hipe_x86_bifs.m4 +++ b/erts/emulator/hipe/hipe_x86_bifs.m4 @@ -51,7 +51,7 @@ define(HANDLE_GOT_MBUF,` * standard_bif_interface_4(nbif_name, cbif_name) * standard_bif_interface_0(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-3 parameters and + * Generate native interface for a BIF with 0-4 parameters and * standard failure mode. */ define(standard_bif_interface_1, -- cgit v1.2.3 From 177d20a7329d24aec0f4a0cbbbd3fc803bcbba4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 17 Apr 2015 11:04:09 +0200 Subject: erts: Specialize compare instructions * i_is_lt for r, x registers and constants * i_is_ge for x registers and constants * i_is_exact_eq for r and x registers --- erts/emulator/beam/beam_emu.c | 3 +++ erts/emulator/beam/ops.tab | 49 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index bb7b799950..aad76e9a93 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -662,6 +662,9 @@ void** beam_ops; #define EqualImmed(X, Y, Action) if (X != Y) { Action; } #define NotEqualImmed(X, Y, Action) if (X == Y) { Action; } +#define EqualExact(X, Y, Action) if (!EQ(X,Y)) { Action; } +#define IsLessThan(X, Y, Action) if (CMP_GE(X, Y)) { Action; } +#define IsGreaterEqual(X, Y, Action) if (CMP_LT(X, Y)) { Action; } #define IsFloat(Src, Fail) if (is_not_float(Src)) { Fail; } diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 9bdc9cb88d..9330192d04 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -391,6 +391,51 @@ i_is_ne_exact_literal r f c i_is_ne_exact_literal x f c i_is_ne_exact_literal y f c +# +# Common Compare Specializations +# We don't do all of them since we want +# to keep the instruction set small-ish +# + +is_eq_exact Lbl S1=xy S2=r => is_eq_exact Lbl S2 S1 +is_eq_exact Lbl S1=rx S2=xy => i_is_eq_exact_spec Lbl S1 S2 +%macro: i_is_eq_exact_spec EqualExact -fail_action + +i_is_eq_exact_spec f x x +i_is_eq_exact_spec f x y +i_is_eq_exact_spec f r x +i_is_eq_exact_spec f r y +%cold +i_is_eq_exact_spec f r r +%hot + +is_lt Lbl S1=rxc S2=rxc => i_is_lt_spec Lbl S1 S2 + +%macro: i_is_lt_spec IsLessThan -fail_action + +i_is_lt_spec f x x +i_is_lt_spec f x r +i_is_lt_spec f x c +i_is_lt_spec f r x +i_is_lt_spec f r c +i_is_lt_spec f c x +i_is_lt_spec f c r +%cold +i_is_lt_spec f r r +i_is_lt_spec f c c +%hot + +is_ge Lbl S1=xc S2=xc => i_is_ge_spec Lbl S1 S2 + +%macro: i_is_ge_spec IsGreaterEqual -fail_action + +i_is_ge_spec f x x +i_is_ge_spec f x c +i_is_ge_spec f c x +%cold +i_is_ge_spec f c c +%hot + # # All other comparisons. # @@ -398,8 +443,8 @@ i_is_ne_exact_literal y f c is_eq_exact Lbl S1 S2 => i_fetch S1 S2 | i_is_eq_exact Lbl is_ne_exact Lbl S1 S2 => i_fetch S1 S2 | i_is_ne_exact Lbl -is_ge Lbl S1 S2 => i_fetch S1 S2 | i_is_ge Lbl is_lt Lbl S1 S2 => i_fetch S1 S2 | i_is_lt Lbl +is_ge Lbl S1 S2 => i_fetch S1 S2 | i_is_ge Lbl is_eq Lbl S1 S2 => i_fetch S1 S2 | i_is_eq Lbl is_ne Lbl S1 S2 => i_fetch S1 S2 | i_is_ne Lbl @@ -493,7 +538,6 @@ put_list s s d %hot %macro: i_fetch FetchArgs -pack -i_fetch c c i_fetch c r i_fetch c x i_fetch c y @@ -510,6 +554,7 @@ i_fetch y x i_fetch y y %cold +i_fetch c c i_fetch s s %hot -- cgit v1.2.3 From 63949dcd13e24bdc73336b0f5e88d4f06cce56c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 15 Sep 2014 17:58:33 +0200 Subject: erts: Add instruction move3 for xy and xx --- erts/emulator/beam/beam_emu.c | 3 ++- erts/emulator/beam/ops.tab | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index aad76e9a93..8b9e271068 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -562,7 +562,8 @@ void** beam_ops; Store(term, Dst); \ } while (0) -#define Move2(src1, dst1, src2, dst2) dst1 = (src1); dst2 = (src2) +#define Move2(S1, D1, S2, D2) D1 = (S1); D2 = (S2) +#define Move3(S1, D1, S2, D2, S3, D3) D1 = (S1); D2 = (S2); D3 = (S3) #define MoveGenDest(src, dstp) \ if ((dstp) == NULL) { r(0) = (src); } else { *(dstp) = src; } diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 9330192d04..66339f8ace 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -302,6 +302,9 @@ move X1=x Y1=y | move X2=x Y2=y => move2 X1 Y1 X2 Y2 move Y1=y X1=x | move Y2=y X2=x => move2 Y1 X1 Y2 X2 move X1=x X2=x | move X3=x X4=x => move2 X1 X2 X3 X4 +move2 X1=x Y1=y X2=x Y2=y | move X3=x Y3=y => move3 X1 Y1 X2 Y2 X3 Y3 +move2 Y1=y X1=x Y2=y X2=x | move Y3=y X3=x => move3 Y1 X1 Y2 X2 Y3 X3 + move C=aiq X=x==1 => move_x1 C move C=aiq X=x==2 => move_x2 C @@ -313,6 +316,11 @@ move2 x y x y move2 y x y x move2 x x x x +%macro: move3 Move3 +move3 x y x y x y +move3 y x y x y x +move3 x x x x x x + # The compiler almost never generates a "move Literal y(Y)" instruction, # so let's cheat if we encounter one. move S=n D=y => init D -- cgit v1.2.3 From d1321eaf9bd1e417561e3d70fd85749fe589143b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 22 Apr 2015 20:44:43 +0200 Subject: erts: Add move window instruction Move an entire region of x registers to the stack. This reduces the dispatch pressure of move instructions. Also introduce a move2 specialization for some common move patterns: move r y | move x y -> move2 : As above, moving regions to the stack move x r | move x y -> move2 : A seemingly common pattern --- erts/emulator/beam/beam_emu.c | 34 ++++++++++++++++++++++++++++++++++ erts/emulator/beam/ops.tab | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 8b9e271068..b9b2094e6c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1503,6 +1503,40 @@ void process_main(void) Next(2); } + OpCase(move_window3_xxxy): { + BeamInstr *next; + Eterm *y; + PreFetch(4, next); + y = (Eterm *)(((unsigned char *)E) + (Arg(3))); + y[0] = xb(Arg(0)); + y[1] = xb(Arg(1)); + y[2] = xb(Arg(2)); + NextPF(4, next); + } + OpCase(move_window4_xxxxy): { + BeamInstr *next; + Eterm *y; + PreFetch(5, next); + y = (Eterm *)(((unsigned char *)E) + (Arg(4))); + y[0] = xb(Arg(0)); + y[1] = xb(Arg(1)); + y[2] = xb(Arg(2)); + y[3] = xb(Arg(3)); + NextPF(5, next); + } + OpCase(move_window5_xxxxxy): { + BeamInstr *next; + Eterm *y; + PreFetch(6, next); + y = (Eterm *)(((unsigned char *)E) + (Arg(5))); + y[0] = xb(Arg(0)); + y[1] = xb(Arg(1)); + y[2] = xb(Arg(2)); + y[3] = xb(Arg(3)); + y[4] = xb(Arg(4)); + NextPF(6, next); + } + OpCase(i_move_call_only_fcr): { r(0) = Arg(1); } diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 66339f8ace..3f7e69e07e 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -298,12 +298,44 @@ move_jump f c move_jump f x move_jump f y + +# Movement to and from the stack is common +# Try to pack as much as we can into one instruction + +# Window move +move_window/5 +move_window/6 + +# x -> y + +move S1=r S2=y | move X1=x Y1=y => move2 S1 S2 X1 Y1 + +move X1=x Y1=y | move X2=x Y2=y | move X3=x Y3=y | succ(Y1,Y2) | succ(Y2,Y3) => \ + move_window X1 X2 X3 Y1 Y3 + +move_window X1=x X2=x X3=x Y1=y Y3=y | move X4=x Y4=y | succ(Y3,Y4) => \ + move_window X1 X2 X3 X4 Y1 Y4 + +move_window X1=x X2=x X3=x X4=x Y1=y Y4=y | move X5=x Y5=y | succ(Y4,Y5) => \ + move_window5 X1 X2 X3 X4 X5 Y1 + +move_window X1=x X2=x X3=x Y1=y Y3=y => move_window3 X1 X2 X3 Y1 +move_window X1=x X2=x X3=x X4=x Y1=y Y4=y => move_window4 X1 X2 X3 X4 Y1 + +move_window3 x x x y +move_window4 x x x x y +move_window5 x x x x x y + move X1=x Y1=y | move X2=x Y2=y => move2 X1 Y1 X2 Y2 move Y1=y X1=x | move Y2=y X2=x => move2 Y1 X1 Y2 X2 move X1=x X2=x | move X3=x X4=x => move2 X1 X2 X3 X4 +move S1=x S2=r | move X1=x Y1=y => move2 S1 S2 X1 Y1 +move S1=y S2=r | move X1=x Y1=y => move2 S1 S2 X1 Y1 + move2 X1=x Y1=y X2=x Y2=y | move X3=x Y3=y => move3 X1 Y1 X2 Y2 X3 Y3 move2 Y1=y X1=x Y2=y X2=x | move Y3=y X3=x => move3 Y1 X1 Y2 X2 Y3 X3 +move2 X1=x X2=x X3=x X4=x | move X5=x X6=x => move3 X1 X2 X3 X4 X5 X6 move C=aiq X=x==1 => move_x1 C move C=aiq X=x==2 => move_x2 C @@ -316,6 +348,11 @@ move2 x y x y move2 y x y x move2 x x x x +move2 x r x y +move2 r y x y +move2 y r x y + + %macro: move3 Move3 move3 x y x y x y move3 y x y x y x -- cgit v1.2.3 From 06a912c068f62e1e04ebb2aa2002d611b8cc31e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 23 Apr 2015 15:34:07 +0200 Subject: erts: Fix loader increment from minus instruction A type error caused the optimization to never kick in. --- erts/emulator/beam/beam_load.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 8d7beb4eb4..282aa71109 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -3172,7 +3172,11 @@ gen_increment_from_minus(LoaderState* stp, GenOpArg Reg, GenOpArg Integer, static int negation_is_small(LoaderState* stp, GenOpArg Int) { - return Int.type == TAG_i && IS_SSMALL(-Int.val); + /* Check for the rare case of overflow in BeamInstr (UWord) -> Sint + * Cast to the correct type before using IS_SSMALL (Sint) */ + return Int.type == TAG_i && + !(Int.val & ~((((BeamInstr)1) << ((sizeof(Sint)*8)-1))-1)) && + IS_SSMALL(-((Sint)Int.val)); } -- cgit v1.2.3 From 38384c6c5f66cf52d24ae6f48cc71bbe52611aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 23 Apr 2015 17:24:18 +0200 Subject: erts: Batch loads and stores for move_window May lessen load/store latency. --- erts/emulator/beam/beam_emu.c | 48 +++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index b9b2094e6c..beec8967fc 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1505,35 +1505,47 @@ void process_main(void) OpCase(move_window3_xxxy): { BeamInstr *next; - Eterm *y; + Eterm xt0, xt1, xt2; + Eterm *y = (Eterm *)(((unsigned char *)E) + (Arg(3))); PreFetch(4, next); - y = (Eterm *)(((unsigned char *)E) + (Arg(3))); - y[0] = xb(Arg(0)); - y[1] = xb(Arg(1)); - y[2] = xb(Arg(2)); + xt0 = xb(Arg(0)); + xt1 = xb(Arg(1)); + xt2 = xb(Arg(2)); + y[0] = xt0; + y[1] = xt1; + y[2] = xt2; NextPF(4, next); } OpCase(move_window4_xxxxy): { BeamInstr *next; - Eterm *y; + Eterm xt0, xt1, xt2, xt3; + Eterm *y = (Eterm *)(((unsigned char *)E) + (Arg(4))); PreFetch(5, next); - y = (Eterm *)(((unsigned char *)E) + (Arg(4))); - y[0] = xb(Arg(0)); - y[1] = xb(Arg(1)); - y[2] = xb(Arg(2)); - y[3] = xb(Arg(3)); + xt0 = xb(Arg(0)); + xt1 = xb(Arg(1)); + xt2 = xb(Arg(2)); + xt3 = xb(Arg(3)); + y[0] = xt0; + y[1] = xt1; + y[2] = xt2; + y[3] = xt3; NextPF(5, next); } OpCase(move_window5_xxxxxy): { BeamInstr *next; - Eterm *y; + Eterm xt0, xt1, xt2, xt3, xt4; + Eterm *y = (Eterm *)(((unsigned char *)E) + (Arg(5))); PreFetch(6, next); - y = (Eterm *)(((unsigned char *)E) + (Arg(5))); - y[0] = xb(Arg(0)); - y[1] = xb(Arg(1)); - y[2] = xb(Arg(2)); - y[3] = xb(Arg(3)); - y[4] = xb(Arg(4)); + xt0 = xb(Arg(0)); + xt1 = xb(Arg(1)); + xt2 = xb(Arg(2)); + xt3 = xb(Arg(3)); + xt4 = xb(Arg(4)); + y[0] = xt0; + y[1] = xt1; + y[2] = xt2; + y[3] = xt3; + y[4] = xt4; NextPF(6, next); } -- cgit v1.2.3 From daaefc2fa63eb0c3fc8f6183ec1169733c2f2ea8 Mon Sep 17 00:00:00 2001 From: Stefan Grundmann Date: Thu, 23 Apr 2015 17:40:51 +0000 Subject: Fix FreeBSD sendfile check (nbytes == 0 && d->c.sendfile.nbytes == 0) when efile_sendfile returns 0 and has EAGAIN set. FreeBSD sendfile(2) man page: When using a socket marked for non-blocking I/O, sendfile() may send fewer bytes than requested. In this case, the number of bytes successfully written is returned in *sbytes (if specified), and the error EAGAIN is returned. The number of bytes successfully written can be 0. If this happens and in a request handling either file:sendfile/2 or file:sendfile/5 with Bytes=0, the sendfile loop will stop prematurely and file:sendfile will return {ok, BytesSent} where BytesSent < DataAfterOffset, effectively breaking sendfile support on FreeBSD. --- erts/emulator/drivers/common/efile_drv.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 518646649d..b2cfe70f94 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -1938,6 +1938,8 @@ static void invoke_sendfile(void *data) d->result_ok = 1; if (d->c.sendfile.nbytes != 0) d->c.sendfile.nbytes -= nbytes; + } else if (nbytes == 0 && d->c.sendfile.nbytes == 0) { + d->result_ok = 1; } else d->result_ok = 0; } else { -- cgit v1.2.3 From 699ad918545ac7e716b77ea05a6a943434616020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 23 Apr 2015 19:06:03 +0200 Subject: erts: Specialize band instruction for common case * i_band specialization on x registers and constants --- erts/emulator/beam/beam_emu.c | 33 +++++++++++++++++++++++++++++++++ erts/emulator/beam/ops.tab | 2 ++ 2 files changed, 35 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index beec8967fc..aca340db6b 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2885,6 +2885,23 @@ do { \ goto do_big_arith2; } +#define DO_BIG_ARITH(Func,Arg1,Arg2) \ + do { \ + Uint live = Arg(1); \ + SWAPOUT; \ + reg[0] = r(0); \ + reg[live] = (Arg1); \ + reg[live+1] = (Arg2); \ + result = (Func)(c_p, reg, live); \ + r(0) = reg[0]; \ + SWAPIN; \ + ERTS_HOLE_CHECK(c_p); \ + if (is_value(result)) { \ + StoreBifResult(4,result); \ + } \ + goto lb_Cl_error; \ + } while(0) + OpCase(i_rem_jId): { Eterm result; @@ -2900,6 +2917,22 @@ do { \ } } + OpCase(i_band_jIxcd): + { + Eterm result; + + if (is_both_small(xb(Arg(2)), Arg(3))) { + /* + * No need to untag -- TAG & TAG == TAG. + */ + result = xb(Arg(2)) & Arg(3); + StoreBifResult(4, result); + } + DO_BIG_ARITH(ARITH_FUNC(band),xb(Arg(2)),Arg(3)); + } + +#undef DO_BIG_ARITH + OpCase(i_band_jId): { Eterm result; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 3f7e69e07e..3436b664d9 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1663,6 +1663,7 @@ gc_bif2 Fail I u$bif:erlang:rem/2 S1 S2 Dst=d => i_fetch S1 S2 | i_rem Fail I Ds gc_bif2 Fail I u$bif:erlang:bsl/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bsl Fail I Dst gc_bif2 Fail I u$bif:erlang:bsr/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bsr Fail I Dst +gc_bif2 Fail I u$bif:erlang:band/2 S1=x S2=c Dst=d => i_band Fail I S1 S2 Dst gc_bif2 Fail I u$bif:erlang:band/2 S1 S2 Dst=d => i_fetch S1 S2 | i_band Fail I Dst gc_bif2 Fail I u$bif:erlang:bor/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bor Fail I Dst gc_bif2 Fail I u$bif:erlang:bxor/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bxor Fail I Dst @@ -1686,6 +1687,7 @@ i_rem j I d i_bsl j I d i_bsr j I d +i_band j I x c d i_band j I d i_bor j I d i_bxor j I d -- cgit v1.2.3 From b58b4718012cfb848dad3106b2ab22c08997c639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 23 Apr 2015 19:21:34 +0200 Subject: erts: Specialize rem instruction for common case * i_rem specialization on x registers --- erts/emulator/beam/beam_emu.c | 13 +++++++++++++ erts/emulator/beam/ops.tab | 2 ++ 2 files changed, 15 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index aca340db6b..f369d7b632 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2902,6 +2902,19 @@ do { \ goto lb_Cl_error; \ } while(0) + OpCase(i_rem_jIxxd): + { + Eterm result; + + if (xb(Arg(3)) == SMALL_ZERO) { + goto badarith; + } else if (is_both_small(xb(Arg(2)), xb(Arg(3)))) { + result = make_small(signed_val(xb(Arg(2))) % signed_val(xb(Arg(3)))); + StoreBifResult(4, result); + } + DO_BIG_ARITH(ARITH_FUNC(int_rem),xb(Arg(2)),xb(Arg(3))); + } + OpCase(i_rem_jId): { Eterm result; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 3436b664d9..7f7b0baacc 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1658,6 +1658,7 @@ gc_bif2 Fail I u$bif:erlang:stimes/2 S1 S2 Dst=d => i_fetch S1 S2 | i_times Fail gc_bif2 Fail I u$bif:erlang:div/2 S1 S2 Dst=d => i_fetch S1 S2 | i_m_div Fail I Dst gc_bif2 Fail I u$bif:erlang:intdiv/2 S1 S2 Dst=d => i_fetch S1 S2 | i_int_div Fail I Dst +gc_bif2 Fail I u$bif:erlang:rem/2 S1=x S2=x Dst=d => i_rem Fail I S1 S2 Dst gc_bif2 Fail I u$bif:erlang:rem/2 S1 S2 Dst=d => i_fetch S1 S2 | i_rem Fail I Dst gc_bif2 Fail I u$bif:erlang:bsl/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bsl Fail I Dst @@ -1682,6 +1683,7 @@ i_minus j I d i_times j I d i_m_div j I d i_int_div j I d +i_rem j I x x d i_rem j I d i_bsl j I d -- cgit v1.2.3 From dd4d7667f5ab60c76567a6f3d814b3b64583280a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 24 Apr 2015 16:43:04 +0200 Subject: erts: Add move2 specialization for common move patterns Common pattern seen in SSL: move y x | move r x -> move2 move r x | move y x -> move2 Common pattern seen in SSL and Compiler: move x r | move x x -> move2 --- erts/emulator/beam/ops.tab | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 7f7b0baacc..22c7f14f0c 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -330,9 +330,13 @@ move X1=x Y1=y | move X2=x Y2=y => move2 X1 Y1 X2 Y2 move Y1=y X1=x | move Y2=y X2=x => move2 Y1 X1 Y2 X2 move X1=x X2=x | move X3=x X4=x => move2 X1 X2 X3 X4 +move S1=x S2=r | move S3=x S4=x => move2 S1 S2 S3 S4 move S1=x S2=r | move X1=x Y1=y => move2 S1 S2 X1 Y1 move S1=y S2=r | move X1=x Y1=y => move2 S1 S2 X1 Y1 +move Y1=y X1=x | move S1=r D1=x => move2 Y1 X1 S1 D1 +move S1=r D1=x | move Y1=y X1=x => move2 S1 D1 Y1 X1 + move2 X1=x Y1=y X2=x Y2=y | move X3=x Y3=y => move3 X1 Y1 X2 Y2 X3 Y3 move2 Y1=y X1=x Y2=y X2=x | move Y3=y X3=x => move3 Y1 X1 Y2 X2 Y3 X3 move2 X1=x X2=x X3=x X4=x | move X5=x X6=x => move3 X1 X2 X3 X4 X5 X6 @@ -348,10 +352,14 @@ move2 x y x y move2 y x y x move2 x x x x +move2 x r x x + move2 x r x y move2 r y x y move2 y r x y +move2 r x y x +move2 y x r x %macro: move3 Move3 move3 x y x y x y -- cgit v1.2.3 From 979d05fc326d0f6cf7c1c4a5182bb33942852dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 24 Apr 2015 17:42:24 +0200 Subject: erts: Specialize minus and plus instruction Seen on SSL application where substraction with x registers were prevalent: * i_minus specialization on x registers * i_plus specialization on x registers --- erts/emulator/beam/beam_emu.c | 71 ++++++++++++++++++++++++++++++------------- erts/emulator/beam/ops.tab | 4 +++ 2 files changed, 54 insertions(+), 21 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index f369d7b632..7dd7250e2f 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1393,7 +1393,39 @@ void process_main(void) ASSERT(c_p->freason != BADMATCH || is_value(c_p->fvalue)); goto find_func_info; } - + +#define DO_BIG_ARITH(Func,Arg1,Arg2) \ + do { \ + Uint live = Arg(1); \ + SWAPOUT; \ + reg[0] = r(0); \ + reg[live] = (Arg1); \ + reg[live+1] = (Arg2); \ + result = (Func)(c_p, reg, live); \ + r(0) = reg[0]; \ + SWAPIN; \ + ERTS_HOLE_CHECK(c_p); \ + if (is_value(result)) { \ + StoreBifResult(4,result); \ + } \ + goto lb_Cl_error; \ + } while(0) + + OpCase(i_plus_jIxxd): + { + Eterm result; + + if (is_both_small(xb(Arg(2)), xb(Arg(3)))) { + Sint i = signed_val(xb(Arg(2))) + signed_val(xb(Arg(3))); + ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i)); + if (MY_IS_SSMALL(i)) { + result = make_small(i); + StoreBifResult(4, result); + } + } + DO_BIG_ARITH(ARITH_FUNC(mixed_plus), xb(Arg(2)), xb(Arg(3))); + } + OpCase(i_plus_jId): { Eterm result; @@ -1405,12 +1437,26 @@ void process_main(void) result = make_small(i); STORE_ARITH_RESULT(result); } - } arith_func = ARITH_FUNC(mixed_plus); goto do_big_arith2; } + OpCase(i_minus_jIxxd): + { + Eterm result; + + if (is_both_small(xb(Arg(2)), xb(Arg(3)))) { + Sint i = signed_val(xb(Arg(2))) - signed_val(xb(Arg(3))); + ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i)); + if (MY_IS_SSMALL(i)) { + result = make_small(i); + StoreBifResult(4, result); + } + } + DO_BIG_ARITH(ARITH_FUNC(mixed_minus), xb(Arg(2)), xb(Arg(3))); + } + OpCase(i_minus_jId): { Eterm result; @@ -2885,23 +2931,6 @@ do { \ goto do_big_arith2; } -#define DO_BIG_ARITH(Func,Arg1,Arg2) \ - do { \ - Uint live = Arg(1); \ - SWAPOUT; \ - reg[0] = r(0); \ - reg[live] = (Arg1); \ - reg[live+1] = (Arg2); \ - result = (Func)(c_p, reg, live); \ - r(0) = reg[0]; \ - SWAPIN; \ - ERTS_HOLE_CHECK(c_p); \ - if (is_value(result)) { \ - StoreBifResult(4,result); \ - } \ - goto lb_Cl_error; \ - } while(0) - OpCase(i_rem_jIxxd): { Eterm result; @@ -2944,8 +2973,6 @@ do { \ DO_BIG_ARITH(ARITH_FUNC(band),xb(Arg(2)),Arg(3)); } -#undef DO_BIG_ARITH - OpCase(i_band_jId): { Eterm result; @@ -2961,6 +2988,8 @@ do { \ goto do_big_arith2; } +#undef DO_BIG_ARITH + do_big_arith2: { Eterm result; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 22c7f14f0c..ece038131e 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1660,7 +1660,9 @@ gc_bif2 p Live u$bif:erlang:sminus/2 Reg=d Int=i Dst | \ # GCing arithmetic instructions. # +gc_bif2 Fail I u$bif:erlang:splus/2 S1=x S2=x Dst=d => i_plus Fail I S1 S2 Dst gc_bif2 Fail I u$bif:erlang:splus/2 S1 S2 Dst=d => i_fetch S1 S2 | i_plus Fail I Dst +gc_bif2 Fail I u$bif:erlang:sminus/2 S1=x S2=x Dst=d => i_minus Fail I S1 S2 Dst gc_bif2 Fail I u$bif:erlang:sminus/2 S1 S2 Dst=d => i_fetch S1 S2 | i_minus Fail I Dst gc_bif2 Fail I u$bif:erlang:stimes/2 S1 S2 Dst=d => i_fetch S1 S2 | i_times Fail I Dst gc_bif2 Fail I u$bif:erlang:div/2 S1 S2 Dst=d => i_fetch S1 S2 | i_m_div Fail I Dst @@ -1686,7 +1688,9 @@ i_increment r I I d i_increment x I I d i_increment y I I d +i_plus j I x x d i_plus j I d +i_minus j I x x d i_minus j I d i_times j I d i_m_div j I d -- cgit v1.2.3 From 7e86d9dd50dc54a38e09bf9774f14276e7c55804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20de=20Bretagne?= Date: Fri, 3 Apr 2015 00:20:03 +0200 Subject: Fix cross compilation for Android --- erts/etc/common/run_erl_common.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/etc/common/run_erl_common.c b/erts/etc/common/run_erl_common.c index 20b78eb05e..ab420e3bee 100644 --- a/erts/etc/common/run_erl_common.c +++ b/erts/etc/common/run_erl_common.c @@ -32,6 +32,10 @@ #include #include +#ifdef __ANDROID__ +# include +#endif + #ifdef HAVE_SYSLOG_H # include #endif -- cgit v1.2.3 From 2652fc4c4a5be4cf3849f18d5e97398ec38f9d99 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Tue, 28 Apr 2015 19:22:12 +0200 Subject: erl_child_setup.c: fix Android breakage The Android support in erl_child_setup.c is broken: 1. The close fd loop compares an fd (integer i) with the address of the function __system_properties_fd rather than the value of calling that function. 2. This function is locally defined, but its name starts with two underscores which is reserved for the implementation. 3. This function is not used outside of this file, so should be static. 4. This function performs a fair amount of work (calls getenv and atoi), which would be repeated for each and every fd in the [from,to] range. 5. This function contains an unsed local variable 's'. Fixed by dropping the __ prefix, making the function static, dropping the unused local variable, and rewriting the close fd loop to call the function at most once. --- erts/emulator/sys/unix/erl_child_setup.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 5ad92dad02..d050748703 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -55,7 +55,7 @@ void sys_sigrelease(int sig) #endif /* !SIG_SIGSET */ #if defined(__ANDROID__) -int __system_properties_fd(void); +static int system_properties_fd(void); #endif /* __ANDROID__ */ #if defined(__ANDROID__) @@ -104,9 +104,12 @@ main(int argc, char *argv[]) #if defined(HAVE_CLOSEFROM) closefrom(from); #elif defined(__ANDROID__) - for (i = from; i <= to; i++) { - if (i!=__system_properties_fd) - (void) close(i); + if (from <= to) { + int spfd = system_properties_fd(); + for (i = from; i <= to; i++) { + if (i != spfd) + (void) close(i); + } } #else for (i = from; i <= to; i++) @@ -143,9 +146,9 @@ main(int argc, char *argv[]) } #if defined(__ANDROID__) -int __system_properties_fd(void) +static int system_properties_fd(void) { - int s, fd; + int fd; char *env; env = getenv("ANDROID_PROPERTY_WORKSPACE"); @@ -156,4 +159,3 @@ int __system_properties_fd(void) return fd; } #endif /* __ANDROID__ */ - -- cgit v1.2.3 From 871770f169f6a9a996d22cbd63128c53b7b4370c Mon Sep 17 00:00:00 2001 From: Johan Oudinet Date: Tue, 24 Mar 2015 15:54:31 +0100 Subject: erts: Fix incorrect use of AC_EGREP_CPP Using 'AC_EGREP_CPP(yes' without restraining the pattern always return true if it runs from a path containing the string 'yes'. --- erts/aclocal.m4 | 2 +- erts/configure.in | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 83f735d332..73fa5d3603 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -559,7 +559,7 @@ dnl AC_DEFUN(LM_SYS_MULTICAST, [AC_CACHE_CHECK([for multicast support], ac_cv_sys_multicast_support, -[AC_EGREP_CPP(yes, +[AC_EGREP_CPP(^yes$, [#include #include #include diff --git a/erts/configure.in b/erts/configure.in index 873e1e30fe..2d234e5f66 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1558,10 +1558,11 @@ if test "$have_gethostbyname_r" = yes; then [Define to flavour of gethostbyname_r])) ;; *) - AC_EGREP_CPP(yes,[#include - #ifdef __GLIBC__ - yes - #endif + AC_EGREP_CPP(^yes$,[ +#include +#ifdef __GLIBC__ +yes +#endif ], AC_DEFINE(HAVE_GETHOSTBYNAME_R, GHBN_R_GLIBC, [Define to flavour of gethostbyname_r])) ;; @@ -4293,10 +4294,10 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in SSL_INCLUDE="-I$dir/include" old_CPPFLAGS=$CPPFLAGS CPPFLAGS=$SSL_INCLUDE - AC_EGREP_CPP(yes,[ + AC_EGREP_CPP(^yes$,[ #include #if OPENSSL_VERSION_NUMBER >= 0x0090700fL - yes +yes #endif ],[ ssl_found=yes @@ -4491,10 +4492,10 @@ if test "x$SSL_APP" != "x" ; then AC_MSG_CHECKING(for OpenSSL kerberos 5 support) old_CPPFLAGS=$CPPFLAGS CPPFLAGS=$SSL_INCLUDE - AC_EGREP_CPP(yes,[ + AC_EGREP_CPP(^yes$,[ #include #ifndef OPENSSL_NO_KRB5 - yes +yes #endif ],[ AC_MSG_RESULT([yes]) -- cgit v1.2.3 From fe070a32f9f685ef4df1a5123e9328bb82e3947a Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 27 Oct 2014 13:46:57 +0100 Subject: emulator: Use module erl_anno --- erts/emulator/test/beam_literals_SUITE.erl | 17 ++++++++++------- erts/emulator/test/code_parallel_load_SUITE.erl | 10 ++++++---- erts/emulator/test/op_SUITE.erl | 12 ++++++++---- erts/emulator/test/trace_bif_SUITE.erl | 13 ++++++++----- 4 files changed, 32 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/beam_literals_SUITE.erl b/erts/emulator/test/beam_literals_SUITE.erl index 85236e4203..9ceb393034 100644 --- a/erts/emulator/test/beam_literals_SUITE.erl +++ b/erts/emulator/test/beam_literals_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2015. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -226,10 +226,11 @@ literal_type_tests(Config) when is_list(Config) -> %% Generate an Erlang module with all different type of type tests. ?line Tests = make_test([{T, L} || T <- type_tests(), L <- literals()]), ?line Mod = literal_test, - ?line Func = {function, 0, test, 0, [{clause,0,[],[],Tests}]}, - ?line Form = [{attribute,0,module,Mod}, - {attribute,0,compile,export_all}, - Func, {eof,0}], + Anno = erl_anno:new(0), + Func = {function, Anno, test, 0, [{clause,Anno,[],[],Tests}]}, + Form = [{attribute,Anno,module,Mod}, + {attribute,Anno,compile,export_all}, + Func, {eof,Anno}], %% Print generated code for inspection. ?line lists:foreach(fun (F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Form), @@ -261,7 +262,8 @@ test(T, L) -> {ok,Toks,_Line} = erl_scan:string(S), {ok,E} = erl_parse:parse_exprs(Toks), {value,Val,_Bs} = erl_eval:exprs(E, []), - {match,0,{atom,0,Val},hd(E)}. + Anno = erl_anno:new(0), + {match,Anno,{atom,Anno,Val},hd(E)}. test(T, A, L) -> S = lists:flatten(io_lib:format("begin io:format(\"~~p~n\", [{~p,~p,~p}]), if ~w(~w, ~w) -> true; true -> false end end. ", @@ -269,7 +271,8 @@ test(T, A, L) -> {ok,Toks,_Line} = erl_scan:string(S), {ok,E} = erl_parse:parse_exprs(Toks), {value,Val,_Bs} = erl_eval:exprs(E, []), - {match,0,{atom,0,Val},hd(E)}. + Anno = erl_anno:new(0), + {match,Anno,{atom,Anno,Val},hd(E)}. literals() -> [42, diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl index 428f1242ab..bcec8fa640 100644 --- a/erts/emulator/test/code_parallel_load_SUITE.erl +++ b/erts/emulator/test/code_parallel_load_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012-2013. All Rights Reserved. +%% Copyright Ericsson AB 2012-2014. All Rights Reserved. %% %% The 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,13 +190,15 @@ handle_cpc_responses(N, Tag, Module) -> 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] ++ + {attribute,a(1),module,Module}, + {attribute,a(2),export,[FA || {FA,_} <- FunForms]} + ] ++ [{attribute, a(3), A, V}|| {A, V} <- Attributes] ++ [ Function || {_, Function} <- FunForms], {ok, Module, Bin} = compile:forms(Forms), Bin. +a(L) -> + erl_anno:new(L). function_forms([]) -> []; function_forms([S|Ss]) -> diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl index ef4689b850..26f6837f19 100644 --- a/erts/emulator/test/op_SUITE.erl +++ b/erts/emulator/test/op_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2015. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -273,7 +273,8 @@ run_test_module(Cases, GuardsOk) -> ?line Bbts = lists:foldr(fun internal_bif/2, [Ok], Es), ?line Fun3 = make_function(bif_tests, Bbts), ?line Id = {function,1,id,1,[{clause,1,[{var,1,'I'}],[],[{var,1,'I'}]}]}, - ?line Module = make_module(op_tests, [Fun1,Fun2,Fun3,Id]), + Module0 = make_module(op_tests, [Fun1,Fun2,Fun3,Id]), + Module = erl_parse:new_anno(Module0), ?line lists:foreach(fun(F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Module), %% Compile, load, and run the generated module. @@ -365,13 +366,16 @@ make_module(Name, Funcs) -> make_function(Name, Body) -> {function,1,Name,0,[{clause,1,[],[],Body}]}. -eval(E) -> +eval(E0) -> + E = erl_parse:new_anno(E0), ?line case catch erl_eval:exprs(E, []) of {'EXIT',Reason} -> {'EXIT',Reason}; {value,Val,_Bs} -> Val end. -unvalue(V) -> erl_parse:abstract(V). +unvalue(V) -> + Abstr = erl_parse:abstract(V), + erl_parse:anno_to_term(Abstr). value({nil,_}) -> []; value({integer,_,X}) -> X; diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index 063e348836..0f68e7b27c 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2014. All Rights Reserved. %% %% The 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,13 +278,16 @@ trace_info_old_code(Config) when is_list(Config) -> ?line MFA = {M,F,0} = {test,foo,0}, ?line Fname = atom_to_list(M)++".erl", ?line AbsForms = - [{attribute,1,module,M}, % -module(M). - {attribute,2,export,[{F,0}]}, % -export([F/0]). - {function,3,F,0, % F() -> - [{clause,4,[],[],[{atom,4,F}]}]}], % F. + [{attribute,a(1),module,M}, % -module(M). + {attribute,a(2),export,[{F,0}]}, % -export([F/0]). + {function,a(3),F,0, % F() -> + [{clause,a(4),[],[],[{atom,a(4),F}]}]}], % F. %% ?line {ok,M,Mbin} = compile:forms(AbsForms), ?line {module,M} = code:load_binary(M, Fname, Mbin), ?line true = erlang:delete_module(M), ?line {traced,undefined} = erlang:trace_info(MFA, traced), ok. + +a(L) -> + erl_anno:new(L). -- cgit v1.2.3 From f1d838ddbe364c37e85c159255b52eb354a3a3ce Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Wed, 8 Apr 2015 15:41:47 +0200 Subject: Get the VTS mode working with private CT version of webtool OTP-12704 --- erts/etc/common/ct_run.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/common/ct_run.c b/erts/etc/common/ct_run.c index bb59b93998..9e67b94f30 100644 --- a/erts/etc/common/ct_run.c +++ b/erts/etc/common/ct_run.c @@ -239,7 +239,7 @@ int main(int argc, char** argv) */ if (ct_mode == VTS_MODE) { - PUSH4("-s", "webtool", "script_start", "vts"); + PUSH4("-s", "ct_webtool", "script_start", "vts"); if (browser[0] != '\0') PUSH(browser); PUSH3("-s", "ct_run", "script_start"); } -- cgit v1.2.3 From 2a3349420d33a298aa02b176100f385c0ab31c99 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 4 May 2015 15:05:41 +0200 Subject: erts: Add debug assertions for match state sanity --- erts/emulator/beam/erl_bits.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 53c21c40e1..f96cb02587 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -165,6 +165,26 @@ erts_bs_start_match_2(Process *p, Eterm Binary, Uint Max) return make_matchstate(ms); } +#ifdef DEBUG +# define CHECK_MATCH_BUFFER(MB) check_match_buffer(MB) + +static void check_match_buffer(ErlBinMatchBuffer* mb) +{ + Eterm realbin; + Uint byteoffs; + byte* bytes, bitoffs, bitsz; + ProcBin* pb; + ERTS_GET_REAL_BIN(mb->orig, realbin, byteoffs, bitoffs, bitsz); + bytes = binary_bytes(realbin) + byteoffs; + ERTS_ASSERT(mb->base >= bytes && mb->base <= (bytes + binary_size(mb->orig))); + pb = (ProcBin *) boxed_val(realbin); + if (pb->thing_word == HEADER_PROC_BIN) + ERTS_ASSERT(pb->flags == 0); +} +#else +# define CHECK_MATCH_BUFFER(MB) +#endif + Eterm erts_bs_get_integer_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuffer* mb) { @@ -185,6 +205,7 @@ erts_bs_get_integer_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuff return SMALL_ZERO; } + CHECK_MATCH_BUFFER(mb); if (mb->size - mb->offset < num_bits) { /* Asked for too many bits. */ return THE_NON_VALUE; } @@ -425,6 +446,7 @@ erts_bs_get_binary_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuffe { ErlSubBin* sb; + CHECK_MATCH_BUFFER(mb); if (mb->size - mb->offset < num_bits) { /* Asked for too many bits. */ return THE_NON_VALUE; } @@ -456,6 +478,7 @@ erts_bs_get_float_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuffer byte* fptr; FloatDef f; + CHECK_MATCH_BUFFER(mb); if (num_bits == 0) { f.fd = 0.0; hp = HeapOnlyAlloc(p, FLOAT_SIZE_OBJECT); @@ -509,6 +532,8 @@ erts_bs_get_binary_all_2(Process *p, ErlBinMatchBuffer* mb) { ErlSubBin* sb; Uint size; + + CHECK_MATCH_BUFFER(mb); size = mb->size-mb->offset; sb = (ErlSubBin *) HeapOnlyAlloc(p, ERL_SUB_BIN_SIZE); sb->thing_word = HEADER_SUB_BIN; @@ -1605,6 +1630,7 @@ erts_bs_get_unaligned_uint32(ErlBinMatchBuffer* mb) byte* LSB; byte* MSB; + CHECK_MATCH_BUFFER(mb); ASSERT((mb->offset & 7) != 0); ASSERT(mb->size - mb->offset >= 32); @@ -1664,6 +1690,8 @@ erts_bs_get_utf8(ErlBinMatchBuffer* mb) 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,9,9,9,9,9,9,9,9 }; + CHECK_MATCH_BUFFER(mb); + if ((remaining_bits = mb->size - mb->offset) < 8) { return THE_NON_VALUE; } @@ -1748,6 +1776,7 @@ erts_bs_get_utf16(ErlBinMatchBuffer* mb, Uint flags) return THE_NON_VALUE; } + CHECK_MATCH_BUFFER(mb); /* * Set up the pointer to the source bytes. */ -- cgit v1.2.3 From 6bef0d08142e961d562a7fce4d8b036a32b2b98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 4 May 2015 19:12:42 +0200 Subject: erts: Use a lockable allocator on 'sys_write_buf' sys_write_buf allocator type is used from async-threads and needs to be lockable. In the SMP case the temporary allocator is lockable but not in the Non-SMP case. To remedy this the binary-allocator is used for the Non-SMP case, which is lockable. --- erts/emulator/beam/erl_alloc.types | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index e2f8da38b9..074f864dee 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -419,7 +419,12 @@ type ENVIRONMENT TEMPORARY SYSTEM environment type PUTENV_STR SYSTEM SYSTEM putenv_string type PRT_REP_EXIT STANDARD SYSTEM port_report_exit type SYS_BLOCKING STANDARD SYSTEM sys_blocking + ++if smp type SYS_WRITE_BUF TEMPORARY SYSTEM sys_write_buf ++else +type SYS_WRITE_BUF BINARY SYSTEM sys_write_buf ++endif +endif -- cgit v1.2.3 From 428492feee831f610a7651fd98c9f4d75e34e726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 5 May 2015 16:40:41 +0200 Subject: erts: Fix configure.in Don't repeat cpu_sup in os_mon_programs - some OS gets confused, i.e. illumos. --- erts/configure.in | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index c41a5bec5a..e6ba48f169 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -4701,12 +4701,12 @@ AC_SUBST(os_mon_programs) AC_SUBST(CPU_SUP_LIBS) AC_CHECK_LIB(kstat, kstat_open, [ - os_mon_programs="$os_mon_programs cpu_sup" + use_cpu_sup=yes CPU_SUP_LIBS="$CPU_SUP_LIBS -lkstat" ]) AC_CHECK_LIB(kvm, kvm_open, [ - os_mon_programs="$os_mon_programs cpu_sup" + use_cpu_sup=yes CPU_SUP_LIBS="$CPU_SUP_LIBS -lkvm" ]) @@ -4714,14 +4714,17 @@ case $host_os in solaris2*) os_mon_programs="$os_mon_programs ferrule mod_syslog" ;; darwin*) - os_mon_programs="$os_mon_programs cpu_sup" ;; + use_cpu_sup=yes ;; openbsd*) - os_mon_programs="$os_mon_programs cpu_sup" ;; + use_cpu_sup=yes ;; linux*) - os_mon_programs="$os_mon_programs cpu_sup" ;; + use_cpu_sup=yes ;; esac - +if test "$use_cpu_sup" = "yes"; then + os_mon_programs="$os_mon_programs cpu_sup" +fi + AC_ARG_WITH(javac, AS_HELP_STRING([--with-javac=JAVAC], [specify Java compiler to use]) AS_HELP_STRING([--with-javac], [use a Java compiler if found (default)]) -- cgit v1.2.3 From 512f099b247b17b3145e90293167a4ba373b9471 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Wed, 6 May 2015 10:46:46 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 16 ++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index a2b4ae49a4..35e6e55e72 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,22 @@

This document describes the changes made to the ERTS application.

+
Erts 6.4.1 + +
Fixed Bugs and Malfunctions + + +

+ The VTS mode in Common Test has been modified to use a + private version of the Webtool application (ct_webtool).

+

+ Own Id: OTP-12704 Aux Id: OTP-10922

+
+
+
+ +
+
Erts 6.4
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index abc9c0ba10..9e5aa999e6 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.4 +VSN = 6.4.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 889dbf81def0986e4569841d64a7f3882808ee07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 May 2015 15:17:18 +0200 Subject: erts: Don't let the compiler optimize pos. zero fix --- erts/emulator/beam/erl_arith.c | 5 +++++ erts/emulator/beam/global.h | 3 +++ erts/emulator/beam/utils.c | 12 ++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_arith.c b/erts/emulator/beam/erl_arith.c index 5150a8a507..47d516534f 100644 --- a/erts/emulator/beam/erl_arith.c +++ b/erts/emulator/beam/erl_arith.c @@ -2048,3 +2048,8 @@ Eterm erts_gc_bnot(Process* p, Eterm* reg, Uint live) } return result; } + +/* Needed to remove compiler optimization */ +double erts_get_positive_zero_float() { + return 0.0f; +} diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 40b043d1cc..54fba9274f 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -867,6 +867,9 @@ void print_process_info(int, void *, Process*); void info(int, void *); void loaded(int, void *); +/* erl_arith.c */ +double erts_get_positive_zero_float(void); + /* config.c */ __decl_noreturn void __noreturn erl_exit(int n, char*, ...); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 8f6335d5dd..51de3528be 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -958,7 +958,8 @@ tail_recur: FloatDef ff; GET_DOUBLE(term, ff); if (ff.fd == 0.0f) { - ff.fd = 0.0f; /* ensure pos. 0.0 */ + /* ensure positive 0.0 */ + ff.fd = erts_get_positive_zero_float(); } hash = hash*FUNNY_NUMBER6 + (ff.fw[0] ^ ff.fw[1]); break; @@ -1477,7 +1478,8 @@ make_hash2(Eterm term) FloatDef ff; GET_DOUBLE(term, ff); if (ff.fd == 0.0f) { - ff.fd = 0.0f; /* ensure pos. 0.0 */ + /* ensure positive 0.0 */ + ff.fd = erts_get_positive_zero_float(); } #if defined(WORDS_BIGENDIAN) || defined(DOUBLE_MIDDLE_ENDIAN) UINT32_HASH_2(ff.fw[0], ff.fw[1], HCONST_12); @@ -1899,7 +1901,8 @@ make_internal_hash(Eterm term) FloatDef ff; GET_DOUBLE(term, ff); if (ff.fd == 0.0f) { - ff.fd = 0.0f; /* ensure pos. 0.0 */ + /* ensure positive 0.0 */ + ff.fd = erts_get_positive_zero_float(); } UINT32_HASH_2(ff.fw[0], ff.fw[1], HCONST_12); goto pop_next; @@ -2087,7 +2090,8 @@ tail_recur: FloatDef ff; GET_DOUBLE(term, ff); if (ff.fd == 0.0f) { - ff.fd = 0.0f; /* ensure pos. 0.0 */ + /* ensure positive 0.0 */ + ff.fd = erts_get_positive_zero_float(); } hash = hash*FUNNY_NUMBER6 + (ff.fw[0] ^ ff.fw[1]); } -- cgit v1.2.3 From 6f05fa95ad949298f5175fc3a25fea52a7a3788d Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Wed, 6 May 2015 15:40:15 +0200 Subject: erts: Increase buffer in erts_fp_check_init_error The current size is too small for 64-bit machines, causing messages to be truncated and making debugging more difficult. --- erts/emulator/sys/unix/sys_float.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c index 2ffa649767..c30ef7cce2 100644 --- a/erts/emulator/sys/unix/sys_float.c +++ b/erts/emulator/sys/unix/sys_float.c @@ -85,7 +85,7 @@ static void set_current_fp_exception(unsigned long pc) void erts_fp_check_init_error(volatile unsigned long *fpexnp) { - char buf[64]; + char buf[128]; snprintf(buf, sizeof buf, "ERTS_FP_CHECK_INIT at %p: detected unhandled FPE at %p\r\n", __builtin_return_address(0), (void*)*fpexnp); if (write(2, buf, strlen(buf)) <= 0) -- cgit v1.2.3 From b5ab6ded99afb0978dd4722d0d43b755555ac549 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Wed, 6 May 2015 15:44:54 +0200 Subject: erts: Disable float exceptions for clang/llvm Change erts/configure.in to force-disable FP exceptions if the VM is compiled by clang/llvm. clang/llvm generates FP code which does not work with FP exceptions (whether unmasked as used in the Erlang VM, or masked followed by tests of which are signalled). This is a known long-standing problem. --- erts/configure.in | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 873e1e30fe..795ce0639b 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2916,6 +2916,10 @@ else #include #include +#if defined(__clang__) || defined(__llvm__) +#error "Clang/LLVM generates broken code for FP exceptions" +#endif + volatile int erl_fp_exception; /* -- cgit v1.2.3 From edce22eb43c40359ccbd0924412cf13692744779 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 26 Mar 2015 22:46:29 +0100 Subject: Misc time improvements - Possibility to chose different clock sources - Improved mach clock usage - Improved linux clock_gettime() usage - ... --- erts/aclocal.m4 | 306 ++++++++++++++++---- erts/configure.in | 9 - erts/doc/src/time_correction.xml | 87 +++--- erts/emulator/beam/erl_gc.c | 21 +- erts/emulator/beam/erl_time.h | 9 +- erts/emulator/beam/erl_time_sup.c | 507 +++++++++++++++++++++++----------- erts/emulator/beam/sys.h | 10 +- erts/emulator/beam/time.c | 66 +++-- erts/emulator/sys/unix/erl_unix_sys.h | 6 +- erts/emulator/sys/unix/sys_time.c | 433 ++++++++++++++++++----------- erts/emulator/sys/win32/erl_poll.c | 1 + erts/emulator/sys/win32/erl_win_sys.h | 2 +- erts/emulator/sys/win32/sys_time.c | 16 +- 13 files changed, 988 insertions(+), 485 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 83f735d332..abc55967ec 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -60,7 +60,6 @@ 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)]) AC_ARG_VAR(erl_xcomp_linux_usable_sigaltstack, [have working sigaltstack(): yes|no (only used when cross compiling)]) @@ -726,9 +725,48 @@ esac AC_DEFUN(ERL_MONOTONIC_CLOCK, [ - AC_CACHE_CHECK([for clock_gettime() with monotonic clock type], erl_cv_clock_gettime_monotonic, + default_resolution_clock_gettime_monotonic="CLOCK_HIGHRES CLOCK_BOOTTIME CLOCK_MONOTONIC" + low_resolution_clock_gettime_monotonic="CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_FAST" + high_resolution_clock_gettime_monotonic="CLOCK_MONOTONIC_PRECISE" + + case "$1" in + high_resolution) + check_msg="high resolution " + prefer_resolution_clock_gettime_monotonic="$high_resolution_clock_gettime_monotonic" + ;; + low_resolution) + check_msg="low resolution " + prefer_resolution_clock_gettime_monotonic="$low_resolution_clock_gettime_monotonic" + ;; + custom_resolution) + check_msg="custom resolution " + prefer_resolution_clock_gettime_monotonic="$2" + ;; + *) + check_msg="" + prefer_resolution_clock_gettime_monotonic= + ;; + esac + + AC_CACHE_CHECK([for clock_gettime(CLOCK_MONOTONIC_RAW, _)], erl_cv_clock_gettime_monotonic_raw, [ - for clock_type in CLOCK_HIGHRES CLOCK_MONOTONIC CLOCK_MONOTONIC_PRECISE; do + AC_TRY_COMPILE([ +#include + ], + [ + struct timespec ts; + long long result; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + result = ((long long) ts.tv_sec) * 1000000000LL + + ((long long) ts.tv_nsec); + ], + erl_cv_clock_gettime_monotonic_raw=yes, + erl_cv_clock_gettime_monotonic_raw=no) + ]) + + AC_CACHE_CHECK([for clock_gettime() with ${check_msg}monotonic clock type], erl_cv_clock_gettime_monotonic_$1, + [ + for clock_type in $prefer_resolution_clock_gettime_monotonic $default_resolution_clock_gettime_monotonic $high_resolution_clock_gettime_monotonic $low_resolution_clock_gettime_monotonic; do AC_TRY_COMPILE([ #include ], @@ -739,12 +777,12 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, result = ((long long) ts.tv_sec) * 1000000000LL + ((long long) ts.tv_nsec); ], - erl_cv_clock_gettime_monotonic=$clock_type, - erl_cv_clock_gettime_monotonic=no) - test $erl_cv_clock_gettime_monotonic = no || break + erl_cv_clock_gettime_monotonic_$1=$clock_type, + erl_cv_clock_gettime_monotonic_$1=no) + test $erl_cv_clock_gettime_monotonic_$1 = no || break done ]) - + AC_CHECK_FUNCS([clock_getres clock_get_attributes gethrtime]) AC_CACHE_CHECK([for mach clock_get_time() with monotonic clock type], erl_cv_mach_clock_get_time_monotonic, @@ -766,39 +804,24 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, erl_cv_mach_clock_get_time_monotonic=no) ]) - case $erl_cv_clock_gettime_monotonic-$ac_cv_func_gethrtime-$erl_cv_mach_clock_get_time_monotonic-$host_os in + erl_corrected_monotonic_clock=no + case $erl_cv_clock_gettime_monotonic_$1-$ac_cv_func_gethrtime-$erl_cv_mach_clock_get_time_monotonic-$host_os in *-*-*-win32) erl_monotonic_clock_func=WindowsAPI ;; CLOCK_*-*-*-linux*) - if test X$cross_compiling != Xyes; then - linux_kernel_vsn_=`uname -r` - case $linux_kernel_vsn_ in - [[0-1]].*|2.[[0-5]]|2.[[0-5]].*) - erl_monotonic_clock_func=times - ;; - *) - erl_monotonic_clock_func=clock_gettime - ;; - esac - else - case X$erl_xcomp_linux_clock_gettime_correction in - X) - AC_MSG_WARN([result clock_gettime guessed because of cross compilation]) - erl_monotonic_clock_func=clock_gettime - ;; - Xyes|Xno) - if test $erl_xcomp_linux_clock_gettime_correction = yes; then - erl_monotonic_clock_func=clock_gettime - else - erl_monotonic_clock_func=times - fi - ;; - *) - AC_MSG_ERROR([Bad erl_xcomp_linux_clock_gettime_correction value: $erl_xcomp_linux_clock_gettime_correction]) - ;; - esac - fi + case $erl_cv_clock_gettime_monotonic_$1-$erl_cv_clock_gettime_monotonic_raw in + CLOCK_BOOTTIME-yes|CLOCK_MONOTONIC-yes) + erl_corrected_monotonic_clock=yes + ;; + *) + # We don't trust CLOCK_MONOTONIC to be NTP + # adjusted on linux systems that do not have + # CLOCK_MONOTONIC_RAW (although it seems to + # be...) + ;; + esac + erl_monotonic_clock_func=clock_gettime ;; no-no-no-linux*) erl_monotonic_clock_func=times @@ -817,16 +840,26 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, ;; esac + erl_monotonic_clock_low_resolution=no erl_monotonic_clock_lib= erl_monotonic_clock_id= case $erl_monotonic_clock_func in clock_gettime) - erl_monotonic_clock_id="$erl_cv_clock_gettime_monotonic" + erl_monotonic_clock_id=$erl_cv_clock_gettime_monotonic_$1 + for low_res_id in $low_resolution_clock_gettime_monotonic; do + if test $erl_monotonic_clock_id = $low_res_id; then + erl_monotonic_clock_low_resolution=yes + break + fi + done AC_CHECK_LIB(rt, clock_gettime, [erl_monotonic_clock_lib="-lrt"]) ;; mach_clock_get_time) erl_monotonic_clock_id=SYSTEM_CLOCK ;; + times) + erl_monotonic_clock_low_resolution=yes + ;; *) ;; esac @@ -835,9 +868,32 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, AC_DEFUN(ERL_WALL_CLOCK, [ - AC_CACHE_CHECK([for clock_gettime() with wall clock type], erl_cv_clock_gettime_wall, + default_resolution_clock_gettime_wall="CLOCK_REALTIME" + low_resolution_clock_gettime_wall="CLOCK_REALTIME_COARSE CLOCK_REALTIME_FAST" + high_resolution_clock_gettime_wall="CLOCK_REALTIME_PRECISE" + + case "$1" in + high_resolution) + check_msg="high resolution " + prefer_resolution_clock_gettime_wall="$high_resolution_clock_gettime_wall" + ;; + low_resolution) + check_msg="low resolution " + prefer_resolution_clock_gettime_wall="$low_resolution_clock_gettime_wall" + ;; + custom_resolution) + check_msg="custom resolution " + prefer_resolution_clock_gettime_wall="$2" + ;; + *) + check_msg="" + prefer_resolution_clock_gettime_wall= + ;; + esac + + AC_CACHE_CHECK([for clock_gettime() with ${check_msg}wall clock type], erl_cv_clock_gettime_wall_$1, [ - for clock_type in CLOCK_REALTIME; do + for clock_type in $prefer_resolution_clock_gettime_wall $default_resolution_clock_gettime_wall $high_resolution_clock_gettime_wall $low_resolution_clock_gettime_wall; do AC_TRY_COMPILE([ #include ], @@ -848,12 +904,12 @@ AC_DEFUN(ERL_WALL_CLOCK, result = ((long long) ts.tv_sec) * 1000000000LL + ((long long) ts.tv_nsec); ], - erl_cv_clock_gettime_wall=$clock_type, - erl_cv_clock_gettime_wall=no) - test $erl_cv_clock_gettime_wall = no || break + erl_cv_clock_gettime_wall_$1=$clock_type, + erl_cv_clock_gettime_wall_$1=no) + test $erl_cv_clock_gettime_wall_$1 = no || break done ]) - + AC_CHECK_FUNCS([clock_getres clock_get_attributes gettimeofday]) AC_CACHE_CHECK([for mach clock_get_time() with wall clock type], erl_cv_mach_clock_get_time_wall, @@ -875,10 +931,12 @@ AC_DEFUN(ERL_WALL_CLOCK, erl_cv_mach_clock_get_time_wall=no) ]) + erl_wall_clock_low_resolution=no erl_wall_clock_id= - case $erl_cv_clock_gettime_wall-$erl_cv_mach_clock_get_time_wall-$ac_cv_func_gettimeofday-$host_os in + case $erl_cv_clock_gettime_wall_$1-$erl_cv_mach_clock_get_time_wall-$ac_cv_func_gettimeofday-$host_os in *-*-*-win32) erl_wall_clock_func=WindowsAPI + erl_wall_clock_low_resolution=yes ;; no-yes-*-*) erl_wall_clock_func=mach_clock_get_time @@ -886,7 +944,13 @@ AC_DEFUN(ERL_WALL_CLOCK, ;; CLOCK_*-*-*-*) erl_wall_clock_func=clock_gettime - erl_wall_clock_id=$erl_cv_clock_gettime_wall + erl_wall_clock_id=$erl_cv_clock_gettime_wall_$1 + for low_res_id in $low_resolution_clock_gettime_wall; do + if test $erl_wall_clock_id = $low_res_id; then + erl_wall_clock_low_resolution=yes + break + fi + done ;; no-no-yes-*) erl_wall_clock_func=gettimeofday @@ -1401,7 +1465,7 @@ AC_ARG_WITH(with_sparc_memory_order, LM_CHECK_THR_LIB ERL_INTERNAL_LIBS -ERL_MONOTONIC_CLOCK +ERL_MONOTONIC_CLOCK(high_resolution) case $erl_monotonic_clock_func in clock_gettime) @@ -2128,20 +2192,89 @@ dnl ---------------------------------------------------------------------- dnl dnl ERL_TIME_CORRECTION dnl -dnl In the presence of a high resolution realtime timer Erlang can adapt -dnl its view of time relative to this timer. On solaris such a timer is -dnl available with the syscall gethrtime(). On other OS's a fallback -dnl solution using times() is implemented. (However on e.g. FreeBSD times() -dnl is implemented using gettimeofday so it doesn't make much sense to -dnl use it there...) On second thought, it seems to be safer to do it the -dnl other way around. I.e. only use times() on OS's where we know it will -dnl work... +dnl Check for primitives that can be used for implementing +dnl erts_os_monotonic_time() and erts_os_system_time() dnl AC_DEFUN(ERL_TIME_CORRECTION, [ -ERL_WALL_CLOCK +AC_ARG_WITH(clock-resolution, +AS_HELP_STRING([--with-clock-resolution=high|low|default], + [specify wanted clock resolution)])) + +AC_ARG_WITH(clock-gettime-realtime-id, +AS_HELP_STRING([--with-clock-gettime-realtime-id=CLOCKID], + [specify clock id to use with clock_gettime() for realtime time)])) + +AC_ARG_WITH(clock-gettime-monotonic-id, +AS_HELP_STRING([--with-clock-gettime-monotonic-id=CLOCKID], + [specify clock id to use with clock_gettime() for monotonic time)])) + +case "$with_clock_resolution" in + ""|no|yes) + with_clock_resolution=default;; + high|low|default) + ;; + *) + AC_MSG_ERROR([Invalid wanted clock resolution: $with_clock_resolution]) + ;; +esac + +case "$with_clock_gettime_realtime_id" in + ""|no) + with_clock_gettime_realtime_id=no + ;; + CLOCK_*CPUTIME*) + AC_MSG_ERROR([Invalid clock_gettime() realtime clock id: Refusing to use the cputime clock id $with_clock_gettime_realtime_id as realtime clock id]) + ;; + CLOCK_MONOTONIC*|CLOCK_BOOTTIME*|CLOCK_UPTIME*|CLOCK_HIGHRES*) + AC_MSG_ERROR([Invalid clock_gettime() realtime clock id: Refusing to use the monotonic clock id $with_clock_gettime_realtime_id as realtime clock id]) + ;; + CLOCK_*) + ;; + *) + AC_MSG_ERROR([Invalid clock_gettime() clock id: $with_clock_gettime_realtime_id]) + ;; +esac + +case "$with_clock_gettime_monotonic_id" in + ""|no) + with_clock_gettime_monotonic_id=no + ;; + CLOCK_*CPUTIME*) + AC_MSG_ERROR([Invalid clock_gettime() monotonic clock id: Refusing to use the cputime clock id $with_clock_gettime_monotonic_id as monotonic clock id]) + ;; + CLOCK_REALTIME*|CLOCK_TAI*) + AC_MSG_ERROR([Invalid clock_gettime() monotonic clock id: Refusing to use the realtime clock id $with_clock_gettime_monotonic_id as monotonic clock id]) + ;; + CLOCK_*) + ;; + *) + AC_MSG_ERROR([Invalid clock_gettime() clock id: $with_clock_gettime_monotonic_id]) + ;; +esac + +case "$with_clock_resolution-$with_clock_gettime_realtime_id" in + high-no) + ERL_WALL_CLOCK(high_resolution);; + low-no) + ERL_WALL_CLOCK(low_resolution);; + default-no) + ERL_WALL_CLOCK(default_resolution);; + *) + ERL_WALL_CLOCK(custom_resolution, $with_clock_gettime_realtime_id);; +esac + +case "$erl_wall_clock_func-$erl_wall_clock_id-$with_clock_gettime_realtime_id" in + *-*-no) + ;; + clock_gettime-$with_clock_gettime_realtime_id-$with_clock_gettime_realtime_id) + ;; + *) + AC_MSG_ERROR([$with_clock_gettime_realtime_id as clock id to clock_gettime() doesn't compile]) + ;; +esac case $erl_wall_clock_func in mach_clock_get_time) @@ -2162,7 +2295,26 @@ if test "x$erl_wall_clock_id" != "x"; then AC_DEFINE_UNQUOTED(WALL_CLOCK_ID, [$erl_wall_clock_id], [Define to wall clock id to use]) fi -ERL_MONOTONIC_CLOCK +case "$with_clock_resolution-$with_clock_gettime_monotonic_id" in + high-no) + ERL_MONOTONIC_CLOCK(high_resolution);; + low-no) + ERL_MONOTONIC_CLOCK(low_resolution);; + default-no) + ERL_MONOTONIC_CLOCK(default_resolution);; + *) + ERL_MONOTONIC_CLOCK(custom_resolution, $with_clock_gettime_monotonic_id);; +esac + +case "$erl_monotonic_clock_func-$erl_monotonic_clock_id-$with_clock_gettime_monotonic_id" in + *-*-no) + ;; + clock_gettime-$with_clock_gettime_monotonic_id-$with_clock_gettime_monotonic_id) + ;; + *) + AC_MSG_ERROR([$with_clock_gettime_monotonic_id as clock id to clock_gettime() doesn't compile]) + ;; +esac case $erl_monotonic_clock_func in times) @@ -2181,12 +2333,54 @@ case $erl_monotonic_clock_func in ;; esac +if test $erl_corrected_monotonic_clock = yes; then + AC_DEFINE(ERTS_HAVE_CORRECTED_OS_MONOTONIC_TIME, [1], [Define if OS monotonic clock is corrected]) +fi + +if test $erl_monotonic_clock_low_resolution = yes; then + AC_DEFINE(ERTS_HAVE_LOW_RESOLUTION_OS_MONOTONIC_LOW, [1], [Define if you have a low resolution OS monotonic clock]) +fi + xrtlib="$erl_monotonic_clock_lib" if test "x$erl_monotonic_clock_id" != "x"; then AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID_STR, ["$erl_monotonic_clock_id"], [Define as a string of monotonic clock id to use]) AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID, [$erl_monotonic_clock_id], [Define to monotonic clock id to use]) fi +if test $erl_cv_clock_gettime_monotonic_raw = yes; then + AC_DEFINE(HAVE_CLOCK_GETTIME_MONOTONIC_RAW, [1], [Define if you have clock_gettime(CLOCK_MONOTONIC_RAW, _)]) +fi + +ERL_MONOTONIC_CLOCK(high_resolution) + +case $$erl_monotonic_clock_low_resolution-$erl_monotonic_clock_func in + no-mach_clock_get_time) + monotonic_hrtime=yes + AC_DEFINE(SYS_HRTIME_USING_MACH_CLOCK_GET_TIME, [1], [Define if you want to implement erts_os_hrtime() using mach clock_get_time()]) + ;; + no-clock_gettime) + monotonic_hrtime=yes + AC_DEFINE(SYS_HRTIME_USING_CLOCK_GETTIME, [1], [Define if you want to implement erts_os_hrtime() using clock_gettime()]) + ;; + no-gethrtime) + monotonic_hrtime=yes + AC_DEFINE(SYS_HRTIME_USING_GETHRTIME, [1], [Define if you want to implement erts_os_hrtime() using gethrtime()]) + ;; + *) + monotonic_hrtime=no + ;; +esac + +if test $monotonic_hrtime = yes; then + AC_DEFINE(HAVE_MONOTONIC_ERTS_SYS_HRTIME, [1], [Define if you have a monotonic erts_os_hrtime() implementation]) +fi + +if test "x$erl_monotonic_clock_id" != "x"; then + AC_DEFINE_UNQUOTED(HRTIME_CLOCK_ID_STR, ["$erl_monotonic_clock_id"], [Define as a string of monotonic clock id to use]) + AC_DEFINE_UNQUOTED(HRTIME_CLOCK_ID, [$erl_monotonic_clock_id], [Define to monotonic clock id to use]) +fi + + dnl dnl Check if gethrvtime is working, and if to use procfs ioctl dnl or (yet to be written) write to the procfs ctl file. diff --git a/erts/configure.in b/erts/configure.in index 481dfe405e..67d4b713cd 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -317,15 +317,6 @@ if test X"$use_vm_probes" = X"yes"; then [Define to enable VM dynamic trace probes]) fi - -AC_ARG_ENABLE(clock-gettime, -AS_HELP_STRING([--enable-clock-gettime], - [use clock-gettime for time correction]), -[ case "$enableval" in - no) clock_gettime_correction=no ;; - *) clock_gettime_correction=yes ;; - esac ], clock_gettime_correction=unknown) - AC_ARG_WITH(assumed-cache-line-size, AS_HELP_STRING([--with-assumed-cache-line-size=SIZE], [specify assumed cache line size in bytes (valid values are powers of two between and including 16 and 8192; default is 64)])) diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index 979a37d7ff..8af98acc19 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -137,6 +137,14 @@

The correctness of time values.

+ +
+ Time Warp +

A time warp is a leap forwards or backwards in time. That + is, the difference of time values taken before and after the + time warp will not correspond to the actual elapsed time.

+
+
OS System Time @@ -146,7 +154,7 @@ os:system_time(). This may or may not be an accurate view of POSIX time. This time may typically be adjusted both backwards and forwards without - limitation. That is, huge leaps both backwards and forwards in time + limitation. That is, time warps may be observed. You can get information about the Erlang runtime system's source of OS system time by calling erlang:system_info(os_system_time_source).

@@ -159,12 +167,12 @@ system. This time does not leap and have a relatively steady frequency although not completely correct. However, it is not uncommon that the OS monotonic time stops if the system is - suspended. This time typically increase since some unspecified - point in time that is not connected to - OS system time. Note that - this type of time is not necessarily provided by all operating - systems. You can get information about the Erlang runtime - system's source of OS monotonic time by calling + suspended. This time typically increase since some + unspecified point in time that is not connected to + OS system time. Note + that this type of time is not necessarily provided by all + operating systems. You can get information about the Erlang + runtime system's source of OS monotonic time by calling erlang:system_info(os_monotonic_time_source).

@@ -177,9 +185,11 @@ erlang:system_time(). This time may or may not be an accurate view of POSIX time, and may or may not align with OS system - time. The time - warp mode determines how it behaves when OS system - time suddenly change.

+ time. The runtime system works towards aligning the two + system times. Depending on time + warp mode used, this may be achieved by letting the Erlang + system time perform a time + warp.

@@ -219,12 +229,6 @@

- -
- Time Warp -

A time warp is a leap forwards or backwards in time.

-
-
@@ -332,7 +336,7 @@
Time Warp Safe Code

Time warp safe code is code that is able to handle - a time warp of + a time warp of Erlang system time.

@@ -378,11 +382,11 @@

The time offset is determined at runtime system start and will after this not change. This is the default behavior. Not because it is the best mode (which it isn't). It is - default only because this is how the runtime system always - has behaved until ERTS version 7.0, and you have to ensure - that your Erlang code that may execute during a time warp is - time warp safe - before you can enable other modes.

+ default only because this is how the runtime system + always has behaved up until ERTS version 7.0, and you have to + ensure that your Erlang code that may execute during a time + warp is time warp + safe before you can enable other modes.

Since the time offset is not allowed to change, time correction needs to adjust the frequency of the Erlang @@ -422,9 +426,9 @@ system time has been corrected, you may want to use the single time warp mode. Note that there are limitations to when you can execute time warp unsafe code using this mode. If it is possible - to only utilize time warp safe code, it is much better to use - the multi time warp - mode instead. + to only utilize time warp safe code, it is much better + to use the multi time + warp mode instead.

Using the single time warp mode, the time offset is @@ -438,12 +442,14 @@ current OS system time is determined. This offset will from now on be fixed during the whole preliminary phase.

-

If time correction is enabled, the Erlang - monotonic clock will only use the OS monotonic time as - time source during this phase. That is, during the - preliminary phase changes in OS system time will have - no effect on Erlang system time and/or Erlang - monotonic time what so ever.

+

If time correction is enabled, adjustments to the + Erlang monotonic clock will be made to keep its + frequency as correct as possible, but no + adjustments will be made trying to align Erlang system + time and OS system time. That is, during the preliminary + Erlang system time and OS system time might diverge + from each other, and no attempt to prevent this will + be made.

If time correction is disabled, changes in OS system time will effect the monotonic clock the same way as @@ -462,15 +468,16 @@

During finalization, the time offset is adjusted and fixated so that current Erlang system time align with - current OS system time. Since the time offset - may be changed, the Erlang system time may do - a time warp at this point. The time offset will from - now on be fixed until the runtime system terminates. - If time correction has been enabled, the time correction - also begins when this phase begins. When the system is - in the final phase it behaves exactly as in the - no time warp - mode.

+ current OS system time. Since the time offset may + change during the finalization, the Erlang system time + may do a time warp at this point. The time offset will + from now on be fixed until the runtime system terminates. + If time correction has been enabled, the time + correction will from now on also make adjustments + in order to align Erlang system time with OS system + time. When the system is in the final phase it behaves + exactly as in the no + time warp mode.

diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 4a116c0740..307f9c93e0 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -404,7 +404,7 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) Uint reclaimed_now = 0; int done = 0; int off_heap_msgs; - Uint ms1, s1, us1; + ErtsMonotonicTime start_time = 0; /* Shut up faulty warning... */ erts_aint32_t state; ErtsSchedulerData *esdp; #ifdef USE_VM_PROBES @@ -424,9 +424,8 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) state = erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC); off_heap_msgs = state & ERTS_PSFLG_OFF_HEAP_MSGS; - if (erts_system_monitor_long_gc != 0) { - get_now(&ms1, &s1, &us1); - } + if (erts_system_monitor_long_gc != 0) + start_time = erts_get_monotonic_time(); ERTS_CHK_OFFHEAP(p); @@ -474,16 +473,14 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) } if (erts_system_monitor_long_gc != 0) { - Uint ms2, s2, us2; - Sint t; + ErtsMonotonicTime end_time; + Uint gc_time; if (erts_test_long_gc_sleep) while (0 != erts_milli_sleep(erts_test_long_gc_sleep)); - get_now(&ms2, &s2, &us2); - t = ms2 - ms1; - t = t*1000000 + s2 - s1; - t = t*1000 + ((Sint) (us2 - us1))/1000; - if (t > 0 && (Uint)t > erts_system_monitor_long_gc) { - monitor_long_gc(p, t); + end_time = erts_get_monotonic_time(); + gc_time = (Uint) ERTS_MONOTONIC_TO_MSEC(end_time - start_time); + if (gc_time && gc_time > erts_system_monitor_long_gc) { + monitor_long_gc(p, gc_time); } } if (erts_system_monitor_large_heap != 0) { diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index cb7764addc..5af3c21d40 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -26,6 +26,12 @@ #define ERTS_TIME_ASSERT(B) ((void) 1) #endif +typedef enum { + ERTS_NO_TIME_WARP_MODE, + ERTS_SINGLE_TIME_WARP_MODE, + ERTS_MULTI_TIME_WARP_MODE +} ErtsTimeWarpMode; + typedef struct ErtsTimerWheel_ ErtsTimerWheel; typedef erts_atomic64_t * ErtsNextTimeoutRef; extern ErtsTimerWheel *erts_default_timer_wheel; @@ -38,7 +44,6 @@ extern SysTimeval erts_first_emu_time; typedef struct erl_timer { struct erl_timer* next; /* next entry tiw slot or chain */ struct erl_timer* prev; /* prev entry tiw slot or chain */ - Uint slot; /* slot in timer wheel */ erts_smp_atomic_t wheel; ErtsMonotonicTime timeout_pos; /* Timeout in absolute clock ticks */ /* called when timeout */ @@ -46,6 +51,7 @@ typedef struct erl_timer { /* called when cancel (may be NULL) */ void (*cancel)(void*); void* arg; /* argument to timeout/cancel procs */ + int slot; /* slot in timer wheel */ } ErlTimer; typedef void (*ErlTimeoutProc)(void*); @@ -77,6 +83,7 @@ void erts_cancel_smp_ptimer(ErtsSmpPTimer *ptimer); void erts_monitor_time_offset(Eterm id, Eterm ref); int erts_demonitor_time_offset(Eterm ref); +int erts_init_time_sup(int, ErtsTimeWarpMode); void erts_late_init_time_sup(void); /* timer-wheel api */ diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index bbdedcc128..9d572c0b0a 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -133,6 +133,7 @@ struct time_sup_read_only__ { ErtsTimeWarpMode warp_mode; #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT ErtsMonotonicTime moffset; + int os_corrected_monotonic_time; int os_monotonic_time_disable; char *os_monotonic_time_func; char *os_monotonic_time_clock_id; @@ -159,12 +160,16 @@ struct time_sup_read_only__ { ErtsMonotonicTime large_diff; ErtsMonotonicTime small_diff; } adj; + struct { + ErtsMonotonicTime error; + ErtsMonotonicTime resolution; + int intervals; + int use_avg; + } drift_adj; }; typedef struct { -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC ErtsMonotonicTime drift; /* Correction for os monotonic drift */ -#endif ErtsMonotonicTime error; /* Correction for error between system times */ } ErtsMonotonicCorrection; @@ -174,7 +179,7 @@ typedef struct { ErtsMonotonicCorrection correction; } ErtsMonotonicCorrectionInstance; -#define ERTS_DRIFT_INTERVALS 5 +#define ERTS_MAX_DRIFT_INTERVALS 50 typedef struct { struct { struct { @@ -185,7 +190,7 @@ typedef struct { ErtsMonotonicTime sys; ErtsMonotonicTime mon; } time; - } intervals[ERTS_DRIFT_INTERVALS]; + } intervals[ERTS_MAX_DRIFT_INTERVALS]; struct { ErtsMonotonicTime sys; ErtsMonotonicTime mon; @@ -197,9 +202,7 @@ typedef struct { typedef struct { ErtsMonotonicCorrectionInstance prev; ErtsMonotonicCorrectionInstance curr; -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC ErtsMonotonicDriftData drift; -#endif ErtsMonotonicTime last_check; int short_check_interval; } ErtsMonotonicCorrectionData; @@ -213,10 +216,11 @@ struct time_sup_infrequently_changed__ { } parmon; ErtsMonotonicTime minit; #endif - int finalized_offset; ErtsSystemTime sinit; ErtsMonotonicTime not_corrected_moffset; - erts_atomic64_t offset; + erts_smp_atomic64_t offset; + ErtsMonotonicTime shadow_offset; + erts_smp_atomic32_t preliminary_offset; }; struct time_sup_frequently_changed__ { @@ -254,19 +258,19 @@ erts_get_approx_time(void) static ERTS_INLINE void init_time_offset(ErtsMonotonicTime offset) { - erts_atomic64_init_nob(&time_sup.inf.c.offset, (erts_aint64_t) offset); + erts_smp_atomic64_init_nob(&time_sup.inf.c.offset, (erts_aint64_t) offset); } static ERTS_INLINE void set_time_offset(ErtsMonotonicTime offset) { - erts_atomic64_set_relb(&time_sup.inf.c.offset, (erts_aint64_t) offset); + erts_smp_atomic64_set_relb(&time_sup.inf.c.offset, (erts_aint64_t) offset); } static ERTS_INLINE ErtsMonotonicTime get_time_offset(void) { - return (ErtsMonotonicTime) erts_atomic64_read_acqb(&time_sup.inf.c.offset); + return (ErtsMonotonicTime) erts_smp_atomic64_read_acqb(&time_sup.inf.c.offset); } @@ -290,16 +294,38 @@ get_time_offset(void) #define ERTS_TIME_DRIFT_MAX_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(50) #define ERTS_TIME_DRIFT_MIN_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(5) +/* + * Maximum drift of the OS monotonic clock expected. + * + * We use 1 milli second per second. If the monotonic + * clock drifts more than this we will fail to adjust for + * drift, and error correction will kick in instead. + * If it is larger than this, one could argue that the + * primitive is to poor to be used... + */ +#define ERTS_MAX_MONOTONIC_DRIFT ERTS_MSEC_TO_MONOTONIC(1) + +/* + * We assume that precision is 32 times worse than the + * resolution. This is a wild guess, but there are no + * practical way to determine actual precision. + */ +#define ERTS_ASSUMED_PRECISION_DROP 32 + +#define ERTS_MIN_MONOTONIC_DRIFT_MEASUREMENT \ + (ERTS_SHORT_TIME_CORRECTION_CHECK - 2*ERTS_MAX_MONOTONIC_DRIFT) + + static ERTS_INLINE ErtsMonotonicTime calc_corrected_erl_mtime(ErtsMonotonicTime os_mtime, ErtsMonotonicCorrectionInstance *cip, - ErtsMonotonicTime *os_mdiff_p) + ErtsMonotonicTime *os_mdiff_p, + int os_drift_corrected) { ErtsMonotonicTime erl_mtime, diff = os_mtime - cip->os_mtime; ERTS_TIME_ASSERT(diff >= 0); -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC - diff += (cip->correction.drift*diff)/ERTS_MONOTONIC_TIME_UNIT; -#endif + if (!os_drift_corrected) + diff += (cip->correction.drift*diff)/ERTS_MONOTONIC_TIME_UNIT; erl_mtime = cip->erl_mtime; erl_mtime += diff; erl_mtime += cip->correction.error*(diff/ERTS_TCORR_ERR_UNIT); @@ -308,7 +334,8 @@ calc_corrected_erl_mtime(ErtsMonotonicTime os_mtime, return erl_mtime; } -static ErtsMonotonicTime get_corrected_time(void) +static ERTS_INLINE ErtsMonotonicTime +read_corrected_time(int os_drift_corrected) { ErtsMonotonicTime os_mtime; ErtsMonotonicCorrectionData cdata; @@ -331,7 +358,18 @@ static ErtsMonotonicTime get_corrected_time(void) cip = &cdata.prev; } - return calc_corrected_erl_mtime(os_mtime, cip, NULL); + return calc_corrected_erl_mtime(os_mtime, cip, NULL, + os_drift_corrected); +} + +static ErtsMonotonicTime get_os_drift_corrected_time(void) +{ + return read_corrected_time(!0); +} + +static ErtsMonotonicTime get_corrected_time(void) +{ + return read_corrected_time(0); } #ifdef ERTS_TIME_CORRECTION_PRINT @@ -352,66 +390,42 @@ print_correction(int change, usec_sdiff = ERTS_MONOTONIC_TO_USEC(sdiff); if (!change) - fprintf(stderr, - "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppb] : " - "tmo = %lld msec\r\n", - (long long) usec_sdiff, - (long long) (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, - (long long) (1000000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, - (long long) tmo); + erts_fprintf(stderr, + "sdiff = %b64d usec : [ec=%b64d ppm, dc=%b64d ppb] : " + "tmo = %bpu msec\r\n", + usec_sdiff, + (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, + (1000000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + tmo); else - fprintf(stderr, - "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppb] " - "-> [ec=%lld ppm, dc=%lld ppb] : tmo = %lld msec\r\n", - (long long) usec_sdiff, - (long long) (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, - (long long) (1000000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, - (long long) (1000000*new_ecorr) / ERTS_TCORR_ERR_UNIT, - (long long) (1000000000*new_dcorr) / ERTS_MONOTONIC_TIME_UNIT, - (long long) tmo); - + erts_fprintf(stderr, + "sdiff = %b64d usec : [ec=%b64d ppm, dc=%b64d ppb] " + "-> [ec=%b64d ppm, dc=%b64d ppb] : tmo = %bpu msec\r\n", + usec_sdiff, + (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, + (1000000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + (1000000*new_ecorr) / ERTS_TCORR_ERR_UNIT, + (1000000000*new_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + tmo); } #endif static void -check_time_correction(void *unused) +check_time_correction(void *idap) { -#ifndef ERTS_TIME_CORRECTION_PRINT -# define ERTS_PRINT_CORRECTION -#else -# ifdef ERTS_HAVE_CORRECTED_OS_MONOTONIC -# define ERTS_PRINT_CORRECTION \ - print_correction(set_new_correction, \ - sdiff, \ - cip->correction.error, \ - 0, \ - new_correction.error, \ - 0, \ - timeout) -# else -# define ERTS_PRINT_CORRECTION \ - print_correction(set_new_correction, \ - sdiff, \ - cip->correction.error, \ - cip->correction.drift, \ - new_correction.error, \ - new_correction.drift, \ - timeout) -# endif -#endif + UWord init_drift_adj = (UWord) idap; ErtsMonotonicCorrectionData cdata; ErtsMonotonicCorrection new_correction; ErtsMonotonicCorrectionInstance *cip; ErtsMonotonicTime mdiff, sdiff, os_mtime, erl_mtime, os_stime, erl_stime, time_offset; Uint timeout; - int set_new_correction, begin_short_intervals = 0; + int os_drift_corrected = time_sup.r.o.os_corrected_monotonic_time; + int set_new_correction = 0, begin_short_intervals = 0; erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); - ASSERT(time_sup.inf.c.finalized_offset); - erts_os_times(&os_mtime, &os_stime); cdata = time_sup.inf.c.parmon.cdata; @@ -423,14 +437,23 @@ check_time_correction(void *unused) "OS monotonic time stepped backwards\n"); cip = &cdata.curr; - erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, &mdiff); + erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, &mdiff, + os_drift_corrected); time_offset = get_time_offset(); erl_stime = erl_mtime + time_offset; sdiff = erl_stime - os_stime; + if (time_sup.inf.c.shadow_offset) { + ERTS_TIME_ASSERT(time_sup.r.o.warp_mode == ERTS_SINGLE_TIME_WARP_MODE); + if (erts_smp_atomic32_read_nob(&time_sup.inf.c.preliminary_offset)) + sdiff += time_sup.inf.c.shadow_offset; + else + time_sup.inf.c.shadow_offset = 0; + } + new_correction = cip->correction; - + if (time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE && (sdiff < -2*time_sup.r.o.adj.small_diff || 2*time_sup.r.o.adj.small_diff < sdiff)) { @@ -440,9 +463,24 @@ check_time_correction(void *unused) set_time_offset(time_offset); schedule_send_time_offset_changed_notifications(time_offset); begin_short_intervals = 1; - if (cdata.curr.correction.error == 0) - set_new_correction = 0; - else { + if (cdata.curr.correction.error != 0) { + set_new_correction = 1; + new_correction.error = 0; + } + } + else if ((time_sup.r.o.warp_mode == ERTS_SINGLE_TIME_WARP_MODE + && erts_smp_atomic32_read_nob(&time_sup.inf.c.preliminary_offset)) + && (sdiff < -2*time_sup.r.o.adj.small_diff + || 2*time_sup.r.o.adj.small_diff < sdiff)) { + /* + * System time diff exeeded limits; change shadow offset + * and let OS system time leap away from Erlang system + * time. + */ + time_sup.inf.c.shadow_offset -= sdiff; + sdiff = 0; + begin_short_intervals = 1; + if (cdata.curr.correction.error != 0) { set_new_correction = 1; new_correction.error = 0; } @@ -462,16 +500,11 @@ check_time_correction(void *unused) else new_correction.error = -ERTS_TCORR_ERR_SMALL_ADJ; } - else { - set_new_correction = 0; - } } else if (cdata.curr.correction.error > 0) { if (sdiff < 0) { - if (cdata.curr.correction.error == ERTS_TCORR_ERR_LARGE_ADJ - || -time_sup.r.o.adj.large_diff <= sdiff) - set_new_correction = 0; - else { + if (cdata.curr.correction.error != ERTS_TCORR_ERR_LARGE_ADJ + && sdiff < -time_sup.r.o.adj.large_diff) { new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; set_new_correction = 1; } @@ -490,14 +523,11 @@ check_time_correction(void *unused) } else /* if (cdata.curr.correction.error < 0) */ { if (0 < sdiff) { - if (cdata.curr.correction.error == -ERTS_TCORR_ERR_LARGE_ADJ - || sdiff <= time_sup.r.o.adj.large_diff) - set_new_correction = 0; - else { + if (cdata.curr.correction.error != -ERTS_TCORR_ERR_LARGE_ADJ + && time_sup.r.o.adj.large_diff < sdiff) { new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; set_new_correction = 1; } - set_new_correction = 0; } else if (sdiff < -time_sup.r.o.adj.small_diff) { set_new_correction = 1; @@ -512,8 +542,7 @@ check_time_correction(void *unused) } } -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC - { + if (!os_drift_corrected) { ErtsMonotonicDriftData *ddp = &time_sup.inf.c.parmon.cdata.drift; int ix = ddp->ix; ErtsMonotonicTime mtime_diff, old_os_mtime; @@ -521,25 +550,26 @@ check_time_correction(void *unused) old_os_mtime = ddp->intervals[ix].time.mon; mtime_diff = os_mtime - old_os_mtime; - if (mtime_diff >= ERTS_SEC_TO_MONOTONIC(10)) { + if ((mtime_diff >= ERTS_MIN_MONOTONIC_DRIFT_MEASUREMENT) + | init_drift_adj) { ErtsMonotonicTime drift_adj, drift_adj_diff, old_os_stime, - stime_diff, mtime_acc, stime_acc, avg_drift_adj; + smtime_diff, stime_diff, mtime_acc, stime_acc, + avg_drift_adj, max_drift; old_os_stime = ddp->intervals[ix].time.sys; mtime_acc = ddp->acc.mon; stime_acc = ddp->acc.sys; - avg_drift_adj = (((stime_acc - mtime_acc)*ERTS_MONOTONIC_TIME_UNIT) + avg_drift_adj = (((stime_acc - mtime_acc) + * ERTS_MONOTONIC_TIME_UNIT) / mtime_acc); mtime_diff = os_mtime - old_os_mtime; stime_diff = os_stime - old_os_stime; - drift_adj = (((stime_diff - mtime_diff)*ERTS_MONOTONIC_TIME_UNIT) - / mtime_diff); - + smtime_diff = stime_diff - mtime_diff; ix++; - if (ix >= ERTS_DRIFT_INTERVALS) + if (ix >= time_sup.r.o.drift_adj.intervals) ix = 0; mtime_acc -= ddp->intervals[ix].diff.mon; mtime_acc += mtime_diff; @@ -555,24 +585,50 @@ check_time_correction(void *unused) ddp->acc.mon = mtime_acc; ddp->acc.sys = stime_acc; - drift_adj_diff = avg_drift_adj - drift_adj; - if (drift_adj_diff < -ERTS_TIME_DRIFT_MAX_ADJ_DIFF - || ERTS_TIME_DRIFT_MAX_ADJ_DIFF < drift_adj_diff) { - ddp->dirty_counter = ERTS_DRIFT_INTERVALS; + max_drift = ERTS_MAX_MONOTONIC_DRIFT; + max_drift *= ERTS_MONOTONIC_TO_SEC(mtime_diff); + + if (smtime_diff > time_sup.r.o.drift_adj.error + max_drift + || smtime_diff < -1*time_sup.r.o.drift_adj.error - max_drift) { + dirty_intervals: + /* + * We had a leap in system time. Mark array as + * dirty to ensure that dirty values are rotated + * out before we use it again... + */ + ddp->dirty_counter = time_sup.r.o.drift_adj.intervals; begin_short_intervals = 1; } - else { - if (ddp->dirty_counter <= 0) { - drift_adj = ((stime_acc - mtime_acc) - *ERTS_MONOTONIC_TIME_UNIT) / mtime_acc; + else if (ddp->dirty_counter > 0) { + if (init_drift_adj) { + new_correction.drift = ((smtime_diff + * ERTS_MONOTONIC_TIME_UNIT) + / mtime_diff); + set_new_correction = 1; } - if (ddp->dirty_counter >= 0) { - if (ddp->dirty_counter == 0) { - /* Force set new drift correction... */ - set_new_correction = 1; - } + ddp->dirty_counter--; + } + else { + if (ddp->dirty_counter == 0) { + /* Force set new drift correction... */ + set_new_correction = 1; ddp->dirty_counter--; } + + if (time_sup.r.o.drift_adj.use_avg) + drift_adj = (((stime_acc - mtime_acc) + * ERTS_MONOTONIC_TIME_UNIT) + / mtime_acc); + else + drift_adj = ((smtime_diff + * ERTS_MONOTONIC_TIME_UNIT) + / mtime_diff); + + drift_adj_diff = avg_drift_adj - drift_adj; + if (drift_adj_diff < -ERTS_TIME_DRIFT_MAX_ADJ_DIFF + || ERTS_TIME_DRIFT_MAX_ADJ_DIFF < drift_adj_diff) + goto dirty_intervals; + drift_adj_diff = drift_adj - new_correction.drift; if (drift_adj_diff) { if (drift_adj_diff > ERTS_TIME_DRIFT_MAX_ADJ_DIFF) @@ -580,7 +636,6 @@ check_time_correction(void *unused) else if (drift_adj_diff < -ERTS_TIME_DRIFT_MAX_ADJ_DIFF) drift_adj_diff = -ERTS_TIME_DRIFT_MAX_ADJ_DIFF; new_correction.drift += drift_adj_diff; - if (drift_adj_diff < -ERTS_TIME_DRIFT_MIN_ADJ_DIFF || ERTS_TIME_DRIFT_MIN_ADJ_DIFF < drift_adj_diff) { set_new_correction = 1; @@ -589,7 +644,6 @@ check_time_correction(void *unused) } } } -#endif begin_short_intervals |= set_new_correction; @@ -608,25 +662,34 @@ check_time_correction(void *unused) timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_LONG_TIME_CORRECTION_CHECK); else { ErtsMonotonicTime ecorr = new_correction.error; - if (sdiff < 0) - sdiff = -1*sdiff; + ErtsMonotonicTime abs_sdiff; + abs_sdiff = (sdiff < 0) ? -1*sdiff : sdiff; if (ecorr < 0) ecorr = -1*ecorr; - if (sdiff > ecorr*(ERTS_LONG_TIME_CORRECTION_CHECK/ERTS_TCORR_ERR_UNIT)) + if (abs_sdiff > ecorr*(ERTS_LONG_TIME_CORRECTION_CHECK/ERTS_TCORR_ERR_UNIT)) timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_LONG_TIME_CORRECTION_CHECK); else { - timeout = ERTS_MONOTONIC_TO_MSEC((ERTS_TCORR_ERR_UNIT*sdiff)/ecorr); + timeout = ERTS_MONOTONIC_TO_MSEC((ERTS_TCORR_ERR_UNIT*abs_sdiff)/ecorr); if (timeout < 10) timeout = 10; } } if (timeout > ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK) - && time_sup.inf.c.parmon.cdata.short_check_interval) { + && (time_sup.inf.c.parmon.cdata.short_check_interval + || time_sup.inf.c.parmon.cdata.drift.dirty_counter >= 0)) { timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK); } - ERTS_PRINT_CORRECTION; +#ifdef ERTS_TIME_CORRECTION_PRINT + print_correction(set_new_correction, + sdiff, + cip->correction.error, + cip->correction.drift, + new_correction.error, + new_correction.drift, + timeout); +#endif if (set_new_correction) { erts_smp_rwmtx_rwlock(&time_sup.inf.c.parmon.rwmtx); @@ -638,16 +701,17 @@ check_time_correction(void *unused) /* * Current correction instance begin when - * OS monotonic time has increased one unit. + * OS monotonic time has increased two units. */ - os_mtime++; + os_mtime += 2; /* * Erlang monotonic time corresponding to * next OS monotonic time using previous * correction. */ - erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, NULL); + erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, NULL, + os_drift_corrected); /* * Save new current correction instance. @@ -664,18 +728,60 @@ check_time_correction(void *unused) NULL, NULL, timeout); +} -#undef ERTS_PRINT_CORRECTION +static ErtsMonotonicTime get_os_corrected_time(void) +{ + ASSERT(time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE); + return erts_os_monotonic_time() + time_sup.r.o.moffset; } -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC +static void +check_time_offset(void *unused) +{ + ErtsMonotonicTime sdiff, os_mtime, erl_mtime, os_stime, + erl_stime, time_offset; + + ASSERT(time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE); + + erts_os_times(&os_mtime, &os_stime); + + erl_mtime = os_mtime + time_sup.r.o.moffset; + time_offset = get_time_offset(); + erl_stime = erl_mtime + time_offset; + + sdiff = erl_stime - os_stime; + + if ((sdiff < -2*time_sup.r.o.adj.small_diff + || 2*time_sup.r.o.adj.small_diff < sdiff)) { + /* System time diff exeeded limits; change time offset... */ +#ifdef ERTS_TIME_CORRECTION_PRINT + erts_fprintf(stderr, "sdiff = %b64d nsec -> 0 nsec\n", + ERTS_MONOTONIC_TO_NSEC(sdiff)); +#endif + time_offset -= sdiff; + sdiff = 0; + set_time_offset(time_offset); + schedule_send_time_offset_changed_notifications(time_offset); + } +#ifdef ERTS_TIME_CORRECTION_PRINT + else erts_fprintf(stderr, "sdiff = %b64d nsec\n", + ERTS_MONOTONIC_TO_NSEC(sdiff)); +#endif + + erts_set_timer(&time_sup.inf.c.parmon.timer, + check_time_offset, + NULL, + NULL, + ERTS_MONOTONIC_TO_MSEC(ERTS_LONG_TIME_CORRECTION_CHECK)); +} static void -init_check_time_correction(void *unused) +init_check_time_correction(void *quick_init_drift) { ErtsMonotonicDriftData *ddp; ErtsMonotonicTime old_mtime, old_stime, mtime, stime, mtime_diff, - stime_diff; + stime_diff, smtime_diff, max_drift; int ix; ddp = &time_sup.inf.c.parmon.cdata.drift; @@ -687,7 +793,13 @@ init_check_time_correction(void *unused) mtime_diff = mtime - old_mtime; stime_diff = stime - old_stime; - if (100*stime_diff < 80*mtime_diff || 120*mtime_diff < 100*stime_diff ) { + smtime_diff = stime_diff - mtime_diff; + + max_drift = ERTS_MAX_MONOTONIC_DRIFT; + max_drift *= ERTS_MONOTONIC_TO_SEC(mtime_diff); + + if (smtime_diff > time_sup.r.o.drift_adj.error + max_drift + || smtime_diff < -1*time_sup.r.o.drift_adj.error - max_drift) { /* Had a system time leap... pretend no drift... */ stime_diff = mtime_diff; } @@ -697,29 +809,28 @@ init_check_time_correction(void *unused) * a drift adjustment, and repeat this interval * in all slots... */ - for (ix = 0; ix < ERTS_DRIFT_INTERVALS; ix++) { + for (ix = 0; ix < time_sup.r.o.drift_adj.intervals; ix++) { ddp->intervals[ix].diff.mon = mtime_diff; ddp->intervals[ix].diff.sys = stime_diff; ddp->intervals[ix].time.mon = old_mtime; ddp->intervals[ix].time.sys = old_stime; } - ddp->acc.sys = stime_diff*ERTS_DRIFT_INTERVALS; - ddp->acc.mon = mtime_diff*ERTS_DRIFT_INTERVALS; + ddp->acc.sys = stime_diff*time_sup.r.o.drift_adj.intervals; + ddp->acc.mon = mtime_diff*time_sup.r.o.drift_adj.intervals; ddp->ix = 0; - ddp->dirty_counter = ERTS_DRIFT_INTERVALS; + ddp->dirty_counter = time_sup.r.o.drift_adj.intervals; - check_time_correction(NULL); + check_time_correction(quick_init_drift); } -#endif - static ErtsMonotonicTime finalize_corrected_time_offset(ErtsSystemTime *stimep) { ErtsMonotonicTime os_mtime; ErtsMonotonicCorrectionData cdata; ErtsMonotonicCorrectionInstance *cip; + int os_drift_corrected = time_sup.r.o.os_corrected_monotonic_time; erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); @@ -734,25 +845,38 @@ finalize_corrected_time_offset(ErtsSystemTime *stimep) "OS monotonic time stepped backwards\n"); cip = &cdata.curr; - return calc_corrected_erl_mtime(os_mtime, cip, NULL); + return calc_corrected_erl_mtime(os_mtime, cip, NULL, + os_drift_corrected); } static void late_init_time_correction(void) { - if (time_sup.inf.c.finalized_offset) { + Uint timeout; + Uint quick_init_drift_adj; + void (*check_func)(void *); - erts_init_timer(&time_sup.inf.c.parmon.timer); - erts_set_timer(&time_sup.inf.c.parmon.timer, -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC - init_check_time_correction, -#else - check_time_correction, -#endif - NULL, - NULL, - ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK)); - } + quick_init_drift_adj = + (Uint) ERTS_MONOTONIC_TO_USEC(time_sup.r.o.drift_adj.error) == 0; + + if (quick_init_drift_adj) + timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK/10); + else + timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK); + + if (!time_sup.r.o.os_corrected_monotonic_time) + check_func = init_check_time_correction; + else if (time_sup.r.o.get_time == get_os_corrected_time) + check_func = check_time_offset; + else + check_func = check_time_correction; + + erts_init_timer(&time_sup.inf.c.parmon.timer); + erts_set_timer(&time_sup.inf.c.parmon.timer, + check_func, + NULL, + (void *) quick_init_drift_adj, + timeout); } #endif /* ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT */ @@ -832,6 +956,8 @@ void erts_init_sys_time_sup(void) #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT time_sup.r.o.os_monotonic_time_disable = !sys_init_time_res.have_os_monotonic_time; + time_sup.r.o.os_corrected_monotonic_time = + sys_init_time_res.have_corrected_os_monotonic_time; time_sup.r.o.os_monotonic_time_func = sys_init_time_res.os_monotonic_time_info.func; time_sup.r.o.os_monotonic_time_clock_id @@ -856,7 +982,7 @@ void erts_init_sys_time_sup(void) int erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) { - ErtsMonotonicTime resolution; + ErtsMonotonicTime resolution, ilength, intervals, short_isecs; #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT ErtsMonotonicTime abs_native_offset, native_offset; #endif @@ -870,9 +996,10 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) time_sup.r.o.warp_mode = time_warp_mode; if (time_warp_mode == ERTS_SINGLE_TIME_WARP_MODE) - time_sup.inf.c.finalized_offset = 0; + erts_smp_atomic32_init_nob(&time_sup.inf.c.preliminary_offset, 1); else - time_sup.inf.c.finalized_offset = ~0; + erts_smp_atomic32_init_nob(&time_sup.inf.c.preliminary_offset, 0); + time_sup.inf.c.shadow_offset = 0; #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT @@ -882,7 +1009,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) time_sup.r.o.start *= ERTS_MONOTONIC_TIME_UNIT; time_sup.r.o.start += ERTS_MONOTONIC_TIME_UNIT; native_offset = time_sup.r.o.start - ERTS_MONOTONIC_TIME_UNIT; - native_offset = native_offset; + abs_native_offset = native_offset; #else /* ARCH_64 */ if (ERTS_MONOTONIC_TIME_UNIT <= 10*1000*1000) { time_sup.r.o.start = 0; @@ -938,17 +1065,66 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) time_sup.r.o.adj.large_diff = ERTS_USEC_TO_MONOTONIC(500); time_sup.r.o.adj.small_diff = time_sup.r.o.adj.large_diff/10; + time_sup.r.o.drift_adj.resolution = resolution; + + if (time_sup.r.o.os_corrected_monotonic_time) { + time_sup.r.o.drift_adj.use_avg = 0; + time_sup.r.o.drift_adj.intervals = 0; + time_sup.r.o.drift_adj.error = 0; + time_sup.inf.c.parmon.cdata.drift.dirty_counter = -1; + } + else { + /* + * Calculate length of the interval in seconds needed + * in order to get an error that is at most 1 micro second. + * If this interval is longer than the short time correction + * check interval we use the average of all values instead + * of the latest value. + */ + short_isecs = ERTS_MONOTONIC_TO_SEC(ERTS_SHORT_TIME_CORRECTION_CHECK); + ilength = ERTS_ASSUMED_PRECISION_DROP * ERTS_MONOTONIC_TIME_UNIT; + ilength /= (resolution * ERTS_USEC_TO_MONOTONIC(1)); + time_sup.r.o.drift_adj.use_avg = ilength > short_isecs; + + if (ilength == 0) + intervals = 5; + else { + intervals = ilength / short_isecs; + if (intervals > ERTS_MAX_DRIFT_INTERVALS) + intervals = ERTS_MAX_DRIFT_INTERVALS; + else if (intervals < 5) + intervals = 5; + } + time_sup.r.o.drift_adj.intervals = (int) intervals; + + /* + * drift_adj.error equals maximum assumed error + * over a short time interval. We use this value also + * when examining a large interval. In this case the + * error will be smaller, but we do not want to + * recalculate this over and over again. + */ + + time_sup.r.o.drift_adj.error = ERTS_MONOTONIC_TIME_UNIT; + time_sup.r.o.drift_adj.error *= ERTS_ASSUMED_PRECISION_DROP; + time_sup.r.o.drift_adj.error /= resolution * short_isecs; + } #ifdef ERTS_TIME_CORRECTION_PRINT - fprintf(stderr, "start = %lld\n\r", (long long) ERTS_MONOTONIC_TIME_START); - fprintf(stderr, "native offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_NATIVE); - fprintf(stderr, "nsec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_NSEC); - fprintf(stderr, "usec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_USEC); - fprintf(stderr, "msec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_MSEC); - fprintf(stderr, "sec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_SEC); - fprintf(stderr, "large diff = %lld usec\r\n", - (long long) ERTS_MONOTONIC_TO_USEC(time_sup.r.o.adj.large_diff)); - fprintf(stderr, "small diff = %lld usec\r\n", - (long long) ERTS_MONOTONIC_TO_USEC(time_sup.r.o.adj.small_diff)); + erts_fprintf(stderr, "resolution = %b64d\n", resolution); + erts_fprintf(stderr, "adj large diff = %b64d usec\n", + ERTS_MONOTONIC_TO_USEC(time_sup.r.o.adj.large_diff)); + erts_fprintf(stderr, "adj small diff = %b64d usec\n", + ERTS_MONOTONIC_TO_USEC(time_sup.r.o.adj.small_diff)); + if (!time_sup.r.o.os_corrected_monotonic_time) { + erts_fprintf(stderr, "drift intervals = %d\n", + time_sup.r.o.drift_adj.intervals); + erts_fprintf(stderr, "drift adj error = %b64d usec\n", + ERTS_MONOTONIC_TO_USEC(time_sup.r.o.drift_adj.error)); + erts_fprintf(stderr, "drift adj max diff = %b64d nsec\n", + ERTS_MONOTONIC_TO_NSEC(ERTS_TIME_DRIFT_MAX_ADJ_DIFF)); + erts_fprintf(stderr, "drift adj min diff = %b64d nsec\n", + ERTS_MONOTONIC_TO_NSEC(ERTS_TIME_DRIFT_MIN_ADJ_DIFF)); + } #endif if (ERTS_MONOTONIC_TIME_UNIT < ERTS_CLKTCK_RESOLUTION) @@ -967,6 +1143,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) erts_os_times(&time_sup.inf.c.minit, &time_sup.inf.c.sinit); time_sup.r.o.moffset = -1*time_sup.inf.c.minit; + time_sup.r.o.moffset += ERTS_MONOTONIC_TIME_UNIT; offset = time_sup.inf.c.sinit; offset -= ERTS_MONOTONIC_TIME_UNIT; init_time_offset(offset); @@ -979,11 +1156,9 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) cdatap = &time_sup.inf.c.parmon.cdata; -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC cdatap->drift.intervals[0].time.sys = time_sup.inf.c.sinit; cdatap->drift.intervals[0].time.mon = time_sup.inf.c.minit; cdatap->curr.correction.drift = 0; -#endif cdatap->curr.correction.error = 0; cdatap->curr.erl_mtime = ERTS_MONOTONIC_TIME_UNIT; cdatap->curr.os_mtime = time_sup.inf.c.minit; @@ -991,7 +1166,12 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) cdatap->short_check_interval = ERTS_INIT_SHORT_INTERVAL_COUNTER; cdatap->prev = cdatap->curr; - time_sup.r.o.get_time = get_corrected_time; + if (!time_sup.r.o.os_corrected_monotonic_time) + time_sup.r.o.get_time = get_corrected_time; + else if (time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE) + time_sup.r.o.get_time = get_os_corrected_time; + else + time_sup.r.o.get_time = get_os_drift_corrected_time; } else #endif @@ -1020,8 +1200,8 @@ void erts_late_init_time_sup(void) { #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT - /* Timer wheel must be initialized */ - if (time_sup.r.o.get_time == get_corrected_time) + /* Timer wheel must have beeen initialized */ + if (time_sup.r.o.get_time != get_not_corrected_time) late_init_time_correction(); #endif erts_late_sys_init_time(); @@ -1038,9 +1218,9 @@ ErtsTimeOffsetState erts_time_offset_state(void) case ERTS_NO_TIME_WARP_MODE: return ERTS_TIME_OFFSET_FINAL; case ERTS_SINGLE_TIME_WARP_MODE: - if (time_sup.inf.c.finalized_offset) - return ERTS_TIME_OFFSET_FINAL; - return ERTS_TIME_OFFSET_PRELIMINARY; + if (erts_smp_atomic32_read_nob(&time_sup.inf.c.preliminary_offset)) + return ERTS_TIME_OFFSET_PRELIMINARY; + return ERTS_TIME_OFFSET_FINAL; case ERTS_MULTI_TIME_WARP_MODE: return ERTS_TIME_OFFSET_VOLATILE; default: @@ -1073,7 +1253,7 @@ erts_finalize_time_offset(void) erts_smp_mtx_lock(&erts_get_time_mtx); - if (!time_sup.inf.c.finalized_offset) { + if (erts_smp_atomic32_read_nob(&time_sup.inf.c.preliminary_offset)) { ErtsMonotonicTime mtime, new_offset; #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT @@ -1110,19 +1290,12 @@ erts_finalize_time_offset(void) set_time_offset(new_offset); schedule_send_time_offset_changed_notifications(new_offset); - time_sup.inf.c.finalized_offset = ~0; + erts_smp_atomic32_set_nob(&time_sup.inf.c.preliminary_offset, 0); res = ERTS_TIME_OFFSET_PRELIMINARY; } erts_smp_mtx_unlock(&erts_get_time_mtx); -#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT - if (res == ERTS_TIME_OFFSET_PRELIMINARY - && time_sup.r.o.get_time == get_corrected_time) { - late_init_time_correction(); - } -#endif - return res; } default: diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 251b39508f..f7a21406f3 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -703,14 +703,9 @@ extern char *erts_default_arg0; extern char os_type[]; -typedef enum { - ERTS_NO_TIME_WARP_MODE, - ERTS_SINGLE_TIME_WARP_MODE, - ERTS_MULTI_TIME_WARP_MODE -} ErtsTimeWarpMode; - typedef struct { int have_os_monotonic_time; + int have_corrected_os_monotonic_time; ErtsMonotonicTime os_monotonic_time_unit; ErtsMonotonicTime sys_clock_resolution; struct { @@ -729,14 +724,13 @@ typedef struct { } ErtsSysInitTimeResult; #define ERTS_SYS_INIT_TIME_RESULT_INITER \ - {0, (ErtsMonotonicTime) -1, (ErtsMonotonicTime) 1} + {0, 0, (ErtsMonotonicTime) -1, (ErtsMonotonicTime) 1} extern void erts_init_sys_time_sup(void); extern void sys_init_time(ErtsSysInitTimeResult *); extern void erts_late_sys_init_time(void); extern void erts_deliver_time(void); extern void erts_time_remaining(SysTimeval *); -extern int erts_init_time_sup(int, ErtsTimeWarpMode); extern void erts_sys_init_float(void); extern void erts_thread_init_float(void); extern void erts_thread_disable_fpe(void); diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index 2bdda6c8af..1fffb5f357 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -115,7 +115,7 @@ struct ErtsTimerWheel_ { Uint nto; struct { ErlTimer *head; - ErlTimer **tail; + ErlTimer *tail; Uint nto; } at_once; int true_next_timeout_time; @@ -235,23 +235,39 @@ found_next: static void remove_timer(ErtsTimerWheel *tiw, ErlTimer *p) { - /* first */ - if (!p->prev) { - tiw->w[p->slot] = p->next; + if (p->slot >= 0) { + /* Timer in wheel... */ + ASSERT(p->slot < TIW_SIZE); + if (p->prev) + p->prev->next = p->next; + else { + ASSERT(tiw->w[p->slot] == p); + tiw->w[p->slot] = p->next; + } if(p->next) - p->next->prev = NULL; - } else { - p->prev->next = p->next; + p->next->prev = p->prev; } - - /* last */ - if (!p->next) { + else { + /* Timer in "at once" queue... */ + ASSERT(p->slot == -1); if (p->prev) - p->prev->next = NULL; - } else { - p->next->prev = p->prev; + p->prev->next = p->next; + else { + ASSERT(tiw->at_once.head == p); + tiw->at_once.head = p->next; + } + if (p->next) + p->next->prev = p->prev; + else { + ASSERT(tiw->at_once.tail == p); + tiw->at_once.tail = p->prev; + } + ASSERT(tiw->at_once.nto > 0); + tiw->at_once.nto--; } + p->slot = -2; + p->next = NULL; p->prev = NULL; @@ -337,11 +353,17 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time) } else { ASSERT(tiw->nto >= tiw->at_once.nto); - timeout_head = tiw->at_once.head; - timeout_tail = tiw->at_once.tail; + p = timeout_head = tiw->at_once.head; + while (1) { + set_timer_wheel(p, NULL); + if (!p->next) { + timeout_tail = &p->next; + break; + } + } tiw->nto -= tiw->at_once.nto; tiw->at_once.head = NULL; - tiw->at_once.tail = &tiw->at_once.head; + tiw->at_once.tail = NULL; tiw->at_once.nto = 0; } @@ -458,7 +480,7 @@ erts_create_timer_wheel(int no) tiw->pos = ERTS_MONOTONIC_TO_CLKTCKS(mtime); tiw->nto = 0; tiw->at_once.head = NULL; - tiw->at_once.tail = &tiw->at_once.head; + tiw->at_once.tail = NULL; tiw->at_once.nto = 0; tiw->true_next_timeout_time = 0; tiw->next_timeout_time = mtime + ERTS_MONOTONIC_DAY; @@ -523,10 +545,13 @@ erts_set_timer(ErlTimer *p, ErlTimeoutProc timeout, timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time); tiw->nto++; tiw->at_once.nto++; - *tiw->at_once.tail = p; - tiw->at_once.tail = &p->next; p->next = NULL; + p->prev = tiw->at_once.tail; + tiw->at_once.tail = p; + if (!tiw->at_once.head) + tiw->at_once.head = p; p->timeout_pos = timeout_pos; + p->slot = -1; timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(timeout_pos); } else { @@ -538,7 +563,7 @@ erts_set_timer(ErlTimer *p, ErlTimeoutProc timeout, /* calculate slot */ tm = (int) (timeout_pos & (TIW_SIZE-1)); - p->slot = (Uint) tm; + p->slot = tm; /* insert at head of list at slot */ p->next = tiw->w[tm]; @@ -587,7 +612,6 @@ erts_cancel_timer(ErlTimer *p) cancel = NULL; else { remove_timer(tiw, p); - p->slot = 0; cancel = p->cancel; arg = p->arg; diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 8fc5a3ca49..29a65f883c 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -177,10 +177,10 @@ typedef ErtsMonotonicTime ErtsSystemTime; /* * OS monotonic time and OS system time */ - #undef ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__ -#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) \ + && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) # if defined(__linux__) # define ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__ 1 # endif @@ -191,13 +191,11 @@ ErtsSystemTime erts_os_system_time(void); #undef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT #undef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT #undef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ -#undef ERTS_HAVE_CORRECTED_OS_MONOTONIC #if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) # define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1 # define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000*1000) # if defined(__linux__) -# define ERTS_HAVE_CORRECTED_OS_MONOTONIC 1 # define ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ 1 # endif #elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index d535457977..dc1822b21c 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -31,6 +31,7 @@ # undef _FILE_OFFSET_BITS #endif +#include #include "sys.h" #include "global.h" #include "erl_os_monotonic_time_extender.h" @@ -39,14 +40,11 @@ #undef ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ #if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ - || defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) -# include -# include -# ifdef HAVE_CLOCK_GET_ATTRIBUTES -# define ERTS_HAVE_MACH_CLOCK_GETRES -static Sint64 -mach_clock_getres(clock_id_t clkid, char *clkid_str); -# endif + || defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) \ + || defined(SYS_HRTIME_USING_MACH_CLOCK_GET_TIME) +# include +# include +# define ERTS_MACH_CLOCKS #endif #ifdef NO_SYSCONF @@ -99,20 +97,53 @@ ErtsSysTimeData__ erts_sys_time_data__ erts_align_attribute(ERTS_CACHE_LINE_SIZE #define ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ -static ErtsMonotonicTime clock_gettime_monotonic_raw(void); +static ErtsMonotonicTime clock_gettime_monotonic(void); static ErtsMonotonicTime clock_gettime_monotonic_verified(void); +#if defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) +static ErtsMonotonicTime clock_gettime_monotonic_raw(void); +#endif #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) -static void clock_gettime_times_raw(ErtsMonotonicTime *, ErtsSystemTime *); +static void clock_gettime_times(ErtsMonotonicTime *, ErtsSystemTime *); static void clock_gettime_times_verified(ErtsMonotonicTime *, ErtsSystemTime *); +#if defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) +static void clock_gettime_times_raw(ErtsMonotonicTime *, ErtsSystemTime *); +#endif #endif #endif /* defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */ +#ifdef ERTS_MACH_CLOCKS +# define ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ +typedef struct { + clock_id_t id; + clock_serv_t srv; + char *name; +} ErtsMachClock; + +typedef struct { + host_name_port_t host; + struct { + ErtsMachClock monotonic; + ErtsMachClock wall; + } clock; +} ErtsMachClocks; +static void mach_clocks_init(void); +static void mach_clocks_fini(void); +# ifdef HAVE_CLOCK_GET_ATTRIBUTES +# define ERTS_HAVE_MACH_CLOCK_GETRES +static Sint64 +mach_clock_getres(ErtsMachClock *clk); +# endif +#endif /* ERTS_MACH_CLOCKS */ + #ifdef ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ struct sys_time_internal_state_read_only__ { #if defined(OS_MONOTONIC_TIME_USING_TIMES) int times_shift; #endif +#ifdef ERTS_MACH_CLOCKS + ErtsMachClocks mach; +#endif }; #endif @@ -166,13 +197,23 @@ static struct { void sys_init_time(ErtsSysInitTimeResult *init_resp) { +#if defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) + int major, minor, build, vsn; +#endif +#if defined(ERTS_MACH_CLOCKS) + mach_clocks_init(); +#endif #if !defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) init_resp->have_os_monotonic_time = 0; #else /* defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) */ - int major, minor, build, vsn; +#ifdef ERTS_HAVE_CORRECTED_OS_MONOTONIC_TIME + init_resp->have_corrected_os_monotonic_time = 1; +#else + init_resp->have_corrected_os_monotonic_time = 0; +#endif init_resp->os_monotonic_time_info.resolution = (Uint64) 1000*1000*1000; #if defined(HAVE_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID) @@ -187,7 +228,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) } #elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID) init_resp->os_monotonic_time_info.resolution - = mach_clock_getres(MONOTONIC_CLOCK_ID, MONOTONIC_CLOCK_ID_STR); + = mach_clock_getres(&internal_state.r.o.mach.clock.monotonic); #endif #ifdef MONOTONIC_CLOCK_ID_STR @@ -220,29 +261,52 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) #if defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) if (vsn >= ERTS_MK_VSN_INT(2, 6, 33)) { erts_sys_time_data__.r.o.os_monotonic_time = - clock_gettime_monotonic_raw; + clock_gettime_monotonic; #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) erts_sys_time_data__.r.o.os_times = - clock_gettime_times_raw; + clock_gettime_times; #endif } else { /* * Linux versions prior to 2.6.33 have a - * known bug that sometimes cause monotonic - * time to take small steps backwards. + * known bug that sometimes cause the NTP + * adjusted monotonic clock to take small + * steps backwards. Use raw monotonic clock + * if it is present; otherwise, fall back + * on locked verification of values. */ - erts_sys_time_data__.r.o.os_monotonic_time = - clock_gettime_monotonic_verified; + init_resp->have_corrected_os_monotonic_time = 0; +#if defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) + /* We know that CLOCK_MONOTONIC_RAW is defined, + but we don't know if we got a kernel that + supports it. Support for CLOCK_MONOTONIC_RAW + appeared in kernel 2.6.28... */ + if (vsn >= ERTS_MK_VSN_INT(2, 6, 28)) { + erts_sys_time_data__.r.o.os_monotonic_time = + clock_gettime_monotonic_raw; #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) - erts_sys_time_data__.r.o.os_times = - clock_gettime_times_verified; + erts_sys_time_data__.r.o.os_times = + clock_gettime_times_raw; #endif - erts_smp_mtx_init(&internal_state.w.f.mtx, - "os_monotonic_time"); - internal_state.w.f.last_delivered - = clock_gettime_monotonic_raw(); - init_resp->os_monotonic_time_info.locked_use = 1; + init_resp->os_monotonic_time_info.clock_id = + "CLOCK_MONOTONIC_RAW"; + } + else +#endif /* defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) */ + { + erts_sys_time_data__.r.o.os_monotonic_time = + clock_gettime_monotonic_verified; +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) + erts_sys_time_data__.r.o.os_times = + clock_gettime_times_verified; +#endif + erts_smp_mtx_init(&internal_state.w.f.mtx, + "os_monotonic_time"); + internal_state.w.f.last_delivered + = clock_gettime_monotonic(); + init_resp->os_monotonic_time_info.locked_use = 1; + } } #else /* !(defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)) */ { @@ -324,7 +388,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) } #elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(WALL_CLOCK_ID) init_resp->os_system_time_info.resolution - = mach_clock_getres(WALL_CLOCK_ID, WALL_CLOCK_ID_STR); + = mach_clock_getres(&internal_state.r.o.mach.clock.wall); #endif #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) @@ -366,6 +430,10 @@ adj_stime_time_unit(ErtsSystemTime stime, Uint32 res) (Uint32) ERTS_MONOTONIC_TIME_UNIT)); } +#define ERTS_TimeSpec2Sint64(TS) \ + ((((Sint64) (TS)->tv_sec) * ((Sint64) 1000*1000*1000)) \ + + ((Sint64) (TS)->tv_nsec)) + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * POSIX clock_gettime() * \* */ @@ -373,17 +441,7 @@ adj_stime_time_unit(ErtsSystemTime stime, Uint32 res) #if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) \ || defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) -static ERTS_INLINE ErtsMonotonicTime -timespec2montime(struct timespec *ts) -{ - ErtsMonotonicTime time; - time = (ErtsMonotonicTime) ts->tv_sec; - time *= (ErtsMonotonicTime) 1000*1000*1000; - time += (ErtsMonotonicTime) ts->tv_nsec; - return time; -} - -static ERTS_INLINE ErtsMonotonicTime +static ERTS_INLINE Sint64 posix_clock_gettime(clockid_t id, char *name) { struct timespec ts; @@ -395,7 +453,7 @@ posix_clock_gettime(clockid_t id, char *name) "clock_gettime(%s, _) failed: %s (%d)\n", name, errstr, err); } - return timespec2montime(&ts); + return ERTS_TimeSpec2Sint64(&ts); } #endif /* defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) \ @@ -406,11 +464,10 @@ posix_clock_gettime(clockid_t id, char *name) ErtsSystemTime erts_os_system_time(void) { - ErtsSystemTime stime; - - stime = (ErtsSystemTime) posix_clock_gettime(WALL_CLOCK_ID, - WALL_CLOCK_ID_STR); - return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000); + Sint64 stime = posix_clock_gettime(WALL_CLOCK_ID, + WALL_CLOCK_ID_STR); + return adj_stime_time_unit((ErtsSystemTime) stime, + (Uint32) 1000*1000*1000); } #endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ @@ -422,32 +479,34 @@ erts_os_system_time(void) #define ERTS_HAVE_ERTS_OS_TIMES_IMPL__ static ERTS_INLINE void -posix_clock_gettime_times(ErtsMonotonicTime *mtimep, +posix_clock_gettime_times(clockid_t mid, char *mname, + ErtsMonotonicTime *mtimep, + clockid_t sid, char *sname, ErtsSystemTime *stimep) { struct timespec mts, sts; int mres, sres, merr, serr; - mres = clock_gettime(MONOTONIC_CLOCK_ID, &mts); + mres = clock_gettime(mid, &mts); merr = errno; - sres = clock_gettime(WALL_CLOCK_ID, &sts); + sres = clock_gettime(sid, &sts); serr = errno; if (mres != 0) { char *errstr = merr ? strerror(merr) : "unknown"; erl_exit(ERTS_ABORT_EXIT, "clock_gettime(%s, _) failed: %s (%d)\n", - MONOTONIC_CLOCK_ID_STR, errstr, merr); + mname, errstr, merr); } if (sres != 0) { char *errstr = serr ? strerror(serr) : "unknown"; erl_exit(ERTS_ABORT_EXIT, "clock_gettime(%s, _) failed: %s (%d)\n", - WALL_CLOCK_ID_STR, errstr, serr); + sname, errstr, serr); } - *mtimep = timespec2montime(&mts); - *stimep = (ErtsSystemTime) timespec2montime(&sts); + *mtimep = (ErtsMonotonicTime) ERTS_TimeSpec2Sint64(&mts); + *stimep = (ErtsSystemTime) ERTS_TimeSpec2Sint64(&sts); } #endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ @@ -456,8 +515,10 @@ posix_clock_gettime_times(ErtsMonotonicTime *mtimep, static ErtsMonotonicTime clock_gettime_monotonic_verified(void) { - ErtsMonotonicTime mtime = posix_clock_gettime(MONOTONIC_CLOCK_ID, - MONOTONIC_CLOCK_ID_STR); + ErtsMonotonicTime mtime; + + mtime = (ErtsMonotonicTime) posix_clock_gettime(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR); erts_smp_mtx_lock(&internal_state.w.f.mtx); if (mtime < internal_state.w.f.last_delivered) @@ -474,7 +535,12 @@ static ErtsMonotonicTime clock_gettime_monotonic_verified(void) static void clock_gettime_times_verified(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) { - posix_clock_gettime_times(mtimep, stimep); + posix_clock_gettime_times(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR, + mtimep, + WALL_CLOCK_ID, + WALL_CLOCK_ID_STR, + stimep); erts_smp_mtx_lock(&internal_state.w.f.mtx); if (*mtimep < internal_state.w.f.last_delivered) @@ -486,20 +552,50 @@ static void clock_gettime_times_verified(ErtsMonotonicTime *mtimep, #endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ +static ErtsMonotonicTime clock_gettime_monotonic(void) +{ + return (ErtsMonotonicTime) posix_clock_gettime(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR); +} + +#if defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) + static ErtsMonotonicTime clock_gettime_monotonic_raw(void) { - return posix_clock_gettime(MONOTONIC_CLOCK_ID, - MONOTONIC_CLOCK_ID_STR); + return (ErtsMonotonicTime) posix_clock_gettime(CLOCK_MONOTONIC_RAW, + "CLOCK_MONOTONIC_RAW"); } +#endif /* defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) */ + #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) +static void clock_gettime_times(ErtsMonotonicTime *mtimep, + ErtsSystemTime *stimep) +{ + posix_clock_gettime_times(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR, + mtimep, + WALL_CLOCK_ID, + WALL_CLOCK_ID_STR, + stimep); +} + +#if defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) + static void clock_gettime_times_raw(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) { - posix_clock_gettime_times(mtimep, stimep); + posix_clock_gettime_times(CLOCK_MONOTONIC_RAW, + "CLOCK_MONOTONIC_RAW", + mtimep, + WALL_CLOCK_ID, + WALL_CLOCK_ID_STR, + stimep); } +#endif /* defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) */ + #endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ #else /* !defined(__linux__) */ @@ -514,61 +610,116 @@ ErtsMonotonicTime erts_os_monotonic_time(void) void erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) { - posix_clock_gettime_times(mtimep, stimep); + posix_clock_gettime_times(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR, + mtimep, + WALL_CLOCK_ID, + WALL_CLOCK_ID_STR, + stimep); } #endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ #endif /* !defined(__linux__) */ -#define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ +#endif /* defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */ + +#if defined(SYS_HRTIME_USING_CLOCK_GETTIME) +# define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ ErtsSysHrTime erts_sys_hrtime(void) { - return (ErtsSysHrTime) posix_clock_gettime(MONOTONIC_CLOCK_ID, - MONOTONIC_CLOCK_ID_STR); + return (ErtsSysHrTime) posix_clock_gettime(HRTIME_CLOCK_ID, + HRTIME_CLOCK_ID_STR); } -#endif /* defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */ +#endif /* defined(SYS_HRTIME_USING_CLOCK_GETTIME) */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * MACH clock_get_time() * \* */ -#if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ - || defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) +#if defined(ERTS_MACH_CLOCKS) -#ifdef ERTS_HAVE_MACH_CLOCK_GETRES +static void +mach_clocks_fini(void) +{ + mach_port_t task = mach_task_self(); + mach_port_deallocate(task, internal_state.r.o.mach.host); +#if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) + mach_port_deallocate(task, internal_state.r.o.mach.clock.monotonic.srv); +#endif +#if defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) + mach_port_deallocate(task, internal_state.r.o.mach.clock.wall.srv); +#endif +} -static Sint64 -mach_clock_getres(clock_id_t clkid, char *clkid_str) +static void +mach_clocks_init(void) { - mach_port_t task; - host_name_port_t host; - natural_t attr[1]; kern_return_t kret; - clock_serv_t clk_srv; - mach_msg_type_number_t cnt; + host_name_port_t host; + clock_id_t id; + clock_serv_t *clck_srv_p; + char *name; + + host = internal_state.r.o.mach.host = mach_host_self(); - host = mach_host_self(); - kret = host_get_clock_service(host, clkid, &clk_srv); +#if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ + || defined(SYS_HRTIME_USING_MACH_CLOCK_GET_TIME) + id = internal_state.r.o.mach.clock.monotonic.id = MONOTONIC_CLOCK_ID; + name = internal_state.r.o.mach.clock.monotonic.name = MONOTONIC_CLOCK_ID_STR; + clck_srv_p = &internal_state.r.o.mach.clock.monotonic.srv; + kret = host_get_clock_service(host, id, clck_srv_p); if (kret != KERN_SUCCESS) { erl_exit(ERTS_ABORT_EXIT, "host_get_clock_service(_, %s, _) failed\n", - clkid_str); + name); } +#endif - cnt = sizeof(attr); - kret = clock_get_attributes(clk_srv, CLOCK_GET_TIME_RES, (clock_attr_t) attr, &cnt); +#if defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) + id = internal_state.r.o.mach.clock.wall.id = WALL_CLOCK_ID; + name = internal_state.r.o.mach.clock.wall.name = WALL_CLOCK_ID_STR; + clck_srv_p = &internal_state.r.o.mach.clock.wall.srv; + kret = host_get_clock_service(host, id, clck_srv_p); if (kret != KERN_SUCCESS) { + erl_exit(ERTS_ABORT_EXIT, + "host_get_clock_service(_, %s, _) failed\n", + name); + } +#endif + + if (atexit(mach_clocks_fini) != 0) { + int err = errno; + char *errstr = err ? strerror(err) : "unknown"; + erl_exit(ERTS_ABORT_EXIT, + "Failed to register mach_clocks_fini() " + "for call at exit: %s (%d)\n", + errstr, err); + } +} + +#ifdef ERTS_HAVE_MACH_CLOCK_GETRES + +static Sint64 +mach_clock_getres(ErtsMachClock *clk) +{ + kern_return_t kret; + natural_t attr[1]; + mach_msg_type_number_t cnt; + + cnt = sizeof(attr); + kret = clock_get_attributes(clk->srv, + CLOCK_GET_TIME_RES, + (clock_attr_t) attr, + &cnt); + if (kret != KERN_SUCCESS || cnt != 1) { erl_exit(ERTS_ABORT_EXIT, "clock_get_attributes(%s, _) failed\n", - clkid_str); + clk->name); } - task = mach_task_self(); - mach_port_deallocate(task, host); - mach_port_deallocate(task, clk_srv); return (Sint64) attr[0]; } @@ -576,41 +727,19 @@ mach_clock_getres(clock_id_t clkid, char *clkid_str) #endif /* ERTS_HAVE_MACH_CLOCK_GETRES */ static ERTS_INLINE Sint64 -mach_clock_gettime(clock_id_t clkid, char *clkid_str) +mach_clock_get_time(ErtsMachClock *clk) { - Sint64 time; - mach_port_t task; - host_name_port_t host; kern_return_t kret; - clock_serv_t clk_srv; mach_timespec_t time_spec; - host = mach_host_self(); - kret = host_get_clock_service(host, clkid, &clk_srv); - if (kret != KERN_SUCCESS) { - erl_exit(ERTS_ABORT_EXIT, - "host_get_clock_service(_, %s, _) failed\n", - clkid_str); - } - errno = 0; - kret = clock_get_time(clk_srv, &time_spec); - if (kret != KERN_SUCCESS) { - erl_exit(ERTS_ABORT_EXIT, - "clock_get_time(%s, _) failed\n", - clkid_str); - } - task = mach_task_self(); - mach_port_deallocate(task, host); - mach_port_deallocate(task, clk_srv); + kret = clock_get_time(clk->srv, &time_spec); + if (kret != KERN_SUCCESS) + erl_exit(ERTS_ABORT_EXIT, "clock_get_time(%s, _) failed\n", clk->name); - time = (Sint64) time_spec.tv_sec; - time *= (Sint64) 1000*1000*1000; - time += (Sint64) time_spec.tv_nsec; - return time; + return ERTS_TimeSpec2Sint64(&time_spec); } -#endif /* defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ - || defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) */ +#endif /* defined(ERTS_MACH_CLOCKS) */ #if defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) @@ -619,10 +748,9 @@ mach_clock_gettime(clock_id_t clkid, char *clkid_str) ErtsSystemTime erts_os_system_time(void) { - ErtsSystemTime stime; - stime = (ErtsSystemTime) mach_clock_gettime(WALL_CLOCK_ID, - WALL_CLOCK_ID_STR); - return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000); + Sint64 stime = mach_clock_get_time(&internal_state.r.o.mach.clock.wall); + return adj_stime_time_unit((ErtsSystemTime) stime, + (Uint32) 1000*1000*1000); } #endif /* defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) */ @@ -632,17 +760,8 @@ erts_os_system_time(void) ErtsMonotonicTime erts_os_monotonic_time(void) { - return (ErtsMonotonicTime) mach_clock_gettime(MONOTONIC_CLOCK_ID, - MONOTONIC_CLOCK_ID_STR); -} - -#define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ - -ErtsSysHrTime -erts_sys_hrtime(void) -{ - return (ErtsMonotonicTime) mach_clock_gettime(MONOTONIC_CLOCK_ID, - MONOTONIC_CLOCK_ID_STR); + return (ErtsMonotonicTime) + mach_clock_get_time(&internal_state.r.o.mach.clock.monotonic); } #if defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) @@ -652,58 +771,44 @@ erts_sys_hrtime(void) void erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) { - ErtsMonotonicTime mtime; - ErtsSystemTime stime; - mach_port_t task; - host_name_port_t host; kern_return_t mkret, skret; - clock_serv_t mclk_srv, sclk_srv; mach_timespec_t mon_time_spec, sys_time_spec; - host = mach_host_self(); - mkret = host_get_clock_service(host, MONOTONIC_CLOCK_ID, &mclk_srv); - skret = host_get_clock_service(host, WALL_CLOCK_ID, &sclk_srv); - if (mkret != KERN_SUCCESS) { - erl_exit(ERTS_ABORT_EXIT, - "host_get_clock_service(_, %s, _) failed\n", - MONOTONIC_CLOCK_ID); - } - if (skret != KERN_SUCCESS) { - erl_exit(ERTS_ABORT_EXIT, - "host_get_clock_service(_, %s, _) failed\n", - WALL_CLOCK_ID); - } - mkret = clock_get_time(mclk_srv, &mon_time_spec); - skret = clock_get_time(sclk_srv, &sys_time_spec); - if (mkret != KERN_SUCCESS) { + mkret = clock_get_time(internal_state.r.o.mach.clock.monotonic.srv, + &mon_time_spec); + skret = clock_get_time(internal_state.r.o.mach.clock.wall.srv, + &sys_time_spec); + + if (mkret != KERN_SUCCESS) erl_exit(ERTS_ABORT_EXIT, "clock_get_time(%s, _) failed\n", - MONOTONIC_CLOCK_ID); - } - if (skret != KERN_SUCCESS) { + internal_state.r.o.mach.clock.monotonic.name); + if (skret != KERN_SUCCESS) erl_exit(ERTS_ABORT_EXIT, "clock_get_time(%s, _) failed\n", - WALL_CLOCK_ID); - } - task = mach_task_self(); - mach_port_deallocate(task, host); - mach_port_deallocate(task, mclk_srv); - mach_port_deallocate(task, sclk_srv); + internal_state.r.o.mach.clock.wall.name); - mtime = (ErtsMonotonicTime) mon_time_spec.tv_sec; - mtime *= (ErtsMonotonicTime) 1000*1000*1000; - mtime += (ErtsMonotonicTime) mon_time_spec.tv_nsec; - stime = (ErtsSystemTime) sys_time_spec.tv_sec; - stime *= (ErtsSystemTime) 1000*1000*1000; - stime += (ErtsSystemTime) sys_time_spec.tv_nsec; - *mtimep = mtime; - *stimep = stime; + *mtimep = (ErtsMonotonicTime) ERTS_TimeSpec2Sint64(&mon_time_spec); + *stimep = (ErtsSystemTime) ERTS_TimeSpec2Sint64(&sys_time_spec); } #endif /* defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) */ #endif /* defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) */ +#if defined(SYS_HRTIME_USING_MACH_CLOCK_GET_TIME) + +#define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ + +ErtsSysHrTime +erts_sys_hrtime(void) +{ + return (ErtsSysHrTime) + mach_clock_get_time(&internal_state.r.o.mach.clock.monotonic); +} + +#endif /* defined(SYS_HRTIME_USING_MACH_CLOCK_GET_TIME) */ + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * Solaris gethrtime() - OS monotonic time * \* */ @@ -715,6 +820,10 @@ ErtsMonotonicTime erts_os_monotonic_time(void) return (ErtsMonotonicTime) gethrtime(); } +#endif /* defined(OS_MONOTONIC_TIME_USING_GETHRTIME) */ + +#if defined(SYS_HRTIME_USING_GETHRTIME) + #define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ ErtsSysHrTime @@ -723,7 +832,7 @@ erts_sys_hrtime(void) return (ErtsSysHrTime) gethrtime(); } -#endif /* defined(OS_MONOTONIC_TIME_USING_GETHRTIME) */ +#endif /* defined(SYS_HRTIME_USING_GETHRTIME) */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * gettimeofday() - OS system time * diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 5a62b00a68..937a678702 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -25,6 +25,7 @@ #include "sys.h" #include "erl_alloc.h" #include "erl_poll.h" +#include "erl_time.h" /* * Some debug macros diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 5181d6b584..9ddc0fbb41 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -224,7 +224,7 @@ erts_os_monotonic_time(void) ERTS_GLB_INLINE void erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) { - return (*erts_sys_time_data__.r.o.os_times)(mtimep, stimep); + (*erts_sys_time_data__.r.o.os_times)(mtimep, stimep); } ERTS_GLB_INLINE ErtsSysHrTime diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index b292d9279e..7da060a7a7 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -28,6 +28,9 @@ #include "erl_os_monotonic_time_extender.h" #include "erl_time.h" +/* Need to look more closely at qpc before use... */ +#define ERTS_DISABLE_USE_OF_QPC_FOR_MONOTONIC_TIME 1 + #define LL_LITERAL(X) ERTS_I64_LITERAL(X) /******************* Routines for time measurement *********************/ @@ -362,10 +365,11 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) if (!internal_state.r.o.pQueryPerformanceCounter) goto get_tick_count64; - if (pf.QuadPart < (((LONGLONG) 1) << 32)) { - internal_state.r.o.pcf = (Uint32) pf.QuadPart; - sys_hrtime_func = sys_hrtime_qpc; - } + if (pf.QuadPart > (((LONGLONG) 1) << 32)) + goto get_tick_count64; + + internal_state.r.o.pcf = (Uint32) pf.QuadPart; + sys_hrtime_func = sys_hrtime_qpc; /* * We only use QueryPerformanceCounter() for @@ -377,6 +381,9 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) if (pf.QuadPart < (LONGLONG) 1000*1000*1000) goto get_tick_count64; + if (ERTS_DISABLE_USE_OF_QPC_FOR_MONOTONIC_TIME) + goto get_tick_count64; + init_resp->os_monotonic_time_info.func = "QueryPerformanceCounter"; init_resp->os_monotonic_time_info.locked_use = 0; time_unit = (ErtsMonotonicTime) pf.QuadPart; @@ -391,6 +398,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) erts_sys_time_data__.r.o.os_times = os_times_func; init_resp->os_monotonic_time_unit = time_unit; init_resp->have_os_monotonic_time = 1; + init_resp->have_corrected_os_monotonic_time = 0; init_resp->sys_clock_resolution = 1; init_resp->os_system_time_info.func = "GetSystemTime"; -- cgit v1.2.3 From e135070d4c7135228756e6a43dbdd638b565a499 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Sun, 29 Mar 2015 01:29:07 +0100 Subject: Replace erlang:now() usage in system suite --- erts/test/erlexec_SUITE.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index f5ea8f160a..07966192c5 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -462,13 +462,10 @@ split_emu_clt([A|As], Emu, Misc, Extra, extra = Type) -> get_nodename(T) -> - {A, B, C} = now(), atom_to_list(T) ++ "-" ++ atom_to_list(?MODULE) ++ "-" - ++ integer_to_list(A) + ++ integer_to_list(erlang:system_time(seconds)) ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C). + ++ integer_to_list(erlang:unique_integer([positive])). -- cgit v1.2.3 From f0d51083a6cda851ed60578a7679e20b1fe5678a Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Sun, 29 Mar 2015 01:29:23 +0100 Subject: Replace erlang:now() usage in emulator suite --- erts/emulator/test/after_SUITE.erl | 29 +++++++++------------- erts/emulator/test/alloc_SUITE.erl | 21 +++++++--------- erts/emulator/test/binary_SUITE.erl | 4 ++- erts/emulator/test/busy_port_SUITE.erl | 8 +++--- erts/emulator/test/decode_packet_SUITE.erl | 4 ++- erts/emulator/test/distribution_SUITE.erl | 23 +++++++---------- erts/emulator/test/driver_SUITE.erl | 37 ++++++++++++---------------- erts/emulator/test/erl_link_SUITE.erl | 7 ++---- erts/emulator/test/estone_SUITE.erl | 19 ++++++-------- erts/emulator/test/float_SUITE.erl | 7 ++---- erts/emulator/test/gc_SUITE.erl | 10 ++++---- erts/emulator/test/map_SUITE.erl | 4 ++- erts/emulator/test/monitor_SUITE.erl | 6 ++--- erts/emulator/test/mtx_SUITE.erl | 6 ++--- erts/emulator/test/nif_SUITE.erl | 4 ++- erts/emulator/test/node_container_SUITE.erl | 24 ++++++------------ erts/emulator/test/old_scheduler_SUITE.erl | 24 +++++++++--------- erts/emulator/test/port_SUITE.erl | 6 ++--- erts/emulator/test/process_SUITE.erl | 20 ++++++--------- erts/emulator/test/scheduler_SUITE.erl | 25 +++++++++---------- erts/emulator/test/signal_SUITE.erl | 6 ++--- erts/emulator/test/smoke_test_SUITE.erl | 7 ++---- erts/emulator/test/system_info_SUITE.erl | 7 ++---- erts/emulator/test/time_SUITE.erl | 1 + erts/emulator/test/timer_bif_SUITE.erl | 5 ++-- erts/emulator/test/trace_call_time_SUITE.erl | 18 +++++++------- 26 files changed, 142 insertions(+), 190 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl index 7cc329cc69..699a1436ce 100644 --- a/erts/emulator/test/after_SUITE.erl +++ b/erts/emulator/test/after_SUITE.erl @@ -70,30 +70,23 @@ end_per_testcase(_Func, Config) -> t_after(Config) when is_list(Config) -> ?line spawn(fun frequent_process/0), ?line Period = test_server:minutes(1), - ?line Before = erlang:now(), + ?line Before = erlang:monotonic_time(), receive after Period -> - ?line After = erlang:now(), + ?line After = erlang:monotonic_time(), ?line report(Period, Before, After) end. - report(Period, Before, After) -> - ?line Elapsed = (element(1, After)*1000000000 - +element(2, After)*1000 - +element(3, After) div 1000) - - (element(1,Before)*1000000000 - + element(2,Before)*1000 + element(3,Before) div 1000), - ?line case Elapsed*100 / Period of - Percent when Percent > 100.10 -> - ?line test_server:fail({too_inaccurate, Percent}); - Percent when Percent < 100.0 -> - ?line test_server:fail({too_early, Percent}); - Percent -> - ?line Comment = io_lib:format("Elapsed/expected: ~.2f %", - [Percent]), - {comment, lists:flatten(Comment)} - end. + case erlang:convert_time_unit(After - Before, native, 100*1000) / Period of + Percent when Percent > 100.10 -> + test_server:fail({too_inaccurate, Percent}); + Percent when Percent < 100.0 -> + test_server:fail({too_early, Percent}); + Percent -> + Comment = io_lib:format("Elapsed/expected: ~.2f %", [Percent]), + {comment, lists:flatten(Comment)} + end. frequent_process() -> receive diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 35c44c229a..12a48cc484 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -241,18 +241,15 @@ receive_drv_result(Port, CaseName) -> start_node(Config) -> start_node(Config, []). start_node(Config, Opts) when is_list(Config), is_list(Opts) -> - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line {A, B, C} = now(), - ?line Name = list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ atom_to_list(?config(testcase, Config)) - ++ "-" - ++ integer_to_list(A) - ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C)), - ?line ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). + Pa = filename:dirname(code:which(?MODULE)), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(?config(testcase, Config)) + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive]))), + ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). stop_node(Node) -> ?t:stop_node(Node). diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 44e9e4f243..5911652447 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -520,7 +520,9 @@ external_size_1(Term, Size0, Limit) when Size0 < Limit -> external_size_1(_, _, _) -> ok. t_iolist_size(Config) when is_list(Config) -> - ?line Seed = now(), + ?line Seed = {erlang:monotonic_time(), + erlang:time_offset(), + erlang:unique_integer([positive])}, ?line io:format("Seed: ~p", [Seed]), ?line random:seed(Seed), ?line Base = <<0:(1 bsl 20)/unit:8>>, diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index 2ed5aaa0d0..d44a03516a 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -516,13 +516,13 @@ hs_busy_pcmd(Prt, Opts, StartFun, EndFun) -> P = spawn_link(fun () -> erlang:yield(), Tester ! {self(), doing_port_command}, - Start = now(), + Start = erlang:monotonic_time(micro_seconds), Res = try {return, port_command(Prt, [], Opts)} catch Exception:Error -> {Exception, Error} end, - End = now(), - Time = round(timer:now_diff(End, Start)/1000), + End = erlang:monotonic_time(micro_seconds), + Time = round((End - Start)/1000), Tester ! {self(), port_command_result, Res, Time} end), receive @@ -776,7 +776,7 @@ run_command(_M,spawn,{Args,Opts}) -> run_command(M,spawn,Args) -> run_command(M,spawn,{Args,[]}); run_command(Mod,Func,Args) -> - erlang:display({{Mod,Func,Args},now()}), + erlang:display({{Mod,Func,Args}, erlang:system_time(micro_seconds)}), apply(Mod,Func,Args). validate_scenario(Data,[{print,Var}|T]) -> diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl index 2baf91cf29..330ad299e5 100644 --- a/erts/emulator/test/decode_packet_SUITE.erl +++ b/erts/emulator/test/decode_packet_SUITE.erl @@ -52,7 +52,9 @@ end_per_group(_GroupName, Config) -> init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Seed = {S1,S2,S3} = now(), + Seed = {S1,S2,S3} = {erlang:monotonic_time(), + erlang:time_offset(), + erlang:unique_integer()}, random:seed(S1,S2,S3), io:format("*** SEED: ~p ***\n", [Seed]), Dog=?t:timetrap(?t:minutes(1)), diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index aa6cf2b881..33cb56c0b9 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -1337,10 +1337,7 @@ unwanted_cixs() -> get_conflicting_atoms(_CIX, 0) -> []; get_conflicting_atoms(CIX, N) -> - {A, B, C} = now(), - Atom = list_to_atom("atom" ++ integer_to_list(A*1000000000000 - + B*1000000 - + C)), + Atom = list_to_atom("atom" ++ integer_to_list(erlang:unique_integer([positive]))), case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of CIX -> [Atom|get_conflicting_atoms(CIX, N-1)]; @@ -1351,10 +1348,7 @@ get_conflicting_atoms(CIX, N) -> get_conflicting_unicode_atoms(_CIX, 0) -> []; get_conflicting_unicode_atoms(CIX, N) -> - {A, B, C} = now(), - Atom = string_to_atom([16#1f608] ++ "atom" ++ integer_to_list(A*1000000000000 - + B*1000000 - + C)), + Atom = string_to_atom([16#1f608] ++ "atom" ++ integer_to_list(erlang:unique_integer([positive]))), case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of CIX -> [Atom|get_conflicting_unicode_atoms(CIX, N-1)]; @@ -1967,8 +1961,7 @@ dmsg_bad_atom_cache_ref() -> %%% Utilities timestamp() -> - {A,B,C} = erlang:now(), - (C div 1000) + (B * 1000) + (A * 1000000000). + erlang:monotonic_time(milli_seconds). start_node(X) -> start_node(X, [], []). @@ -1992,7 +1985,9 @@ start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) -> ++ "-" ++ atom_to_list(?config(testcase, Config)) ++ "-" - ++ integer_to_list(timestamp()))), + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive])))), start_node(Name, Args, Rel). stop_node(Node) -> @@ -2109,7 +2104,7 @@ node_monitor(Master) -> Master ! {nodeup, node(), Node} end, Nodes0), - ?t:format("~p ~p: ~p~n", [node(), erlang:now(), Nodes0]), + ?t:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Nodes0]), node_monitor_loop(Master); false -> net_kernel:monitor_nodes(false, Opts), @@ -2130,7 +2125,7 @@ node_monitor_loop(Master) -> receive {nodeup, Node, _InfoList} = Msg -> Master ! {nodeup, node(), Node}, - ?t:format("~p ~p: ~p~n", [node(), erlang:now(), Msg]), + ?t:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), node_monitor_loop(Master); {nodedown, Node, InfoList} = Msg -> Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of @@ -2138,7 +2133,7 @@ node_monitor_loop(Master) -> _ -> undefined end, Master ! {nodedown, node(), Node, Reason}, - ?t:format("~p ~p: ~p~n", [node(), erlang:now(), Msg]), + ?t:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), node_monitor_loop(Master) end. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 623d62f876..e6beda1ccf 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -390,12 +390,12 @@ timer_measure(Config) when is_list(Config) -> try_timeouts(_, 0) -> ok; try_timeouts(Port, Timeout) -> - ?line TimeBefore = now(), + ?line TimeBefore = erlang:monotonic_time(), ?line erlang:port_command(Port, <>), receive {Port,{data,[?TIMER]}} -> ?line Elapsed = erl_millisecs() - erl_millisecs(TimeBefore), - io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed,Timeout]), + io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed, Timeout]), if Elapsed < Timeout -> ?line ?t:fail(too_short); @@ -455,7 +455,7 @@ timer_delay(Config) when is_list(Config) -> Name = 'timer_drv', ?line Port = start_driver(Config, Name, false), - ?line TimeBefore = now(), + ?line TimeBefore = erlang:monotonic_time(), Timeout0 = 350, ?line erlang:port_command(Port, <>), Timeout = Timeout0 + @@ -499,7 +499,7 @@ timer_change(Config) when is_list(Config) -> try_change_timer(_Port, 0) -> ok; try_change_timer(Port, Timeout) -> ?line Timeout_3 = Timeout*3, - ?line TimeBefore = now(), + ?line TimeBefore = erlang:monotonic_time(), ?line erlang:port_command(Port, <>), ?line erlang:port_command(Port, <>), receive @@ -2520,13 +2520,11 @@ uniform(N) -> end, random:uniform(N). -%% return millisecs from statistics source erl_millisecs() -> - {Ms, S, Us} = erlang:now(), - Ms * 1000000000 + S * 1000 + Us / 1000. + erl_millisecs(erlang:monotonic_time()). -erl_millisecs({Ms,S,Us}) -> - Ms * 1000000000 + S * 1000 + Us / 1000. +erl_millisecs(MonotonicTime) -> + (1000*MonotonicTime)/erlang:convert_time_unit(1,seconds,native). %% Start/stop drivers. start_driver(Config, Name, Binary) -> @@ -2575,18 +2573,15 @@ sleep(Ms) when is_integer(Ms), Ms >= 0 -> start_node(Config) when is_list(Config) -> - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line {A, B, C} = now(), - ?line Name = list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ atom_to_list(?config(testcase, Config)) - ++ "-" - ++ integer_to_list(A) - ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C)), - ?line ?t:start_node(Name, slave, [{args, "-pa "++Pa}]). + Pa = filename:dirname(code:which(?MODULE)), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(?config(testcase, Config)) + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive]))), + ?t:start_node(Name, slave, [{args, "-pa "++Pa}]). stop_node(Node) -> ?t:stop_node(Node). diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index 435c0872e6..02c1d84d59 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -1035,16 +1035,13 @@ get_names(N, T) when is_atom(T) -> get_names(0, _, Acc) -> Acc; get_names(N, T, Acc) -> - {A, B, C} = now(), get_names(N-1, T, [list_to_atom(atom_to_list(?MODULE) ++ "-" ++ atom_to_list(T) ++ "-" - ++ integer_to_list(A) + ++ integer_to_list(erlang:system_time(seconds)) ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C)) | Acc]). + ++ integer_to_list(erlang:unique_integer([positive]))) | Acc]). start_node(Name) -> ?line start_node(Name, ""). diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index 1de6d6fb56..9fe161cffc 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -339,7 +339,6 @@ micros() -> ]. macro(Ms,DataDir) -> - erlang:now(), %% compensate for old 4.3 firsttime clock bug :-( statistics(reductions), statistics(runtime), lists(500), %% fixup cache on first round @@ -369,10 +368,9 @@ run_micro(Top, M, DataDir) -> apply_micro(M) -> {GC0, Words0, _} = statistics(garbage_collection), statistics(reductions), - Before = erlang:now(), - + Before = erlang:monotonic_time(), Compensate = apply_micro(M#micro.function, M#micro.loops), - After = erlang:now(), + After = erlang:monotonic_time(), {GC1, Words1, _} = statistics(garbage_collection), {_, Reds} = statistics(reductions), Elapsed = subtr(Before, After), @@ -391,10 +389,7 @@ apply_micro(M) -> subtr(Before, After) -> - (element(1,After)*1000000000000 - +element(2,After)*1000000+element(3,After)) - - (element(1,Before)*1000000000000 - +element(2,Before)*1000000+element(3,Before)). + erlang:convert_time_unit(After-Before, native, micro_seconds). gci(Micros, Words, Gcs) -> ((256 * Gcs) / Micros) + (Words / Micros). @@ -633,10 +628,10 @@ tup_trav(T, P, End) -> %% Port I/O port_io(I) -> EstoneCat = get(estone_cat), - Before = erlang:now(), + Before = erlang:monotonic_time(), Pps = make_port_pids(5, I, EstoneCat), %% 5 ports send_procs(Pps, go), - After = erlang:now(), + After = erlang:monotonic_time(), wait_for_pids(Pps), subtr(Before, After). @@ -854,10 +849,10 @@ handle_call(_From, State, [abc]) -> %% Binary handling, creating, manipulating and sending binaries binary_h(I) -> - Before = erlang:now(), + Before = erlang:monotonic_time(), P = spawn(?MODULE, echo, [self()]), B = list_to_binary(lists:duplicate(2000, 5)), - After = erlang:now(), + After = erlang:monotonic_time(), Compensate = subtr(Before, After), binary_h_2(I, P, B), Compensate. diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index 4a45afa9e9..a07516b5a9 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -294,16 +294,13 @@ id(I) -> I. start_node(Config) when is_list(Config) -> ?line Pa = filename:dirname(code:which(?MODULE)), - ?line {A, B, C} = now(), ?line Name = list_to_atom(atom_to_list(?MODULE) ++ "-" ++ atom_to_list(?config(testcase, Config)) ++ "-" - ++ integer_to_list(A) + ++ integer_to_list(erlang:system_time(seconds)) ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C)), + ++ integer_to_list(erlang:unique_integer([positive]))), ?line ?t:start_node(Name, slave, [{args, "-pa "++Pa}]). stop_node(Node) -> diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl index 36889b6c36..1b92e3198e 100644 --- a/erts/emulator/test/gc_SUITE.erl +++ b/erts/emulator/test/gc_SUITE.erl @@ -77,7 +77,7 @@ grow_heap1(List, MaxLen, CurLen, up) -> grow_heap1([], _MaxLen, _, down) -> ok; grow_heap1([_|List], MaxLen, CurLen, down) -> - {_,_,C} = erlang:now(), + C=erlang:unique_integer([positive]), Num = C rem (length(List))+1, Elem = lists:nth(Num, List), NewList = lists:delete(Elem, List), @@ -136,7 +136,7 @@ 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), - {_,_,C}=erlang:now(), + C=erlang:unique_integer([positive]), Num=C rem (length(List))+1, Elem=lists:nth(Num, List), NewList=lists:delete(Elem, List), @@ -146,8 +146,8 @@ grow_stack_heap1([_|List], MaxLen, CurLen, down) -> %% Create an arbitrary element/term. make_arbit() -> - {AA,BB,CC}=erlang:now(), - A=AA+1, B=BB+1, C=CC+1, + {AA,BB,CC}=erlang:timestamp(), + A=AA+1, B=BB+1, C=(CC+erlang:unique_integer([positive])) rem 1000000 + 1, New = case C rem 9 of 0 -> make_string((B div C) +5); @@ -171,7 +171,7 @@ make_string(Length) -> make_string(_, 0, Acc) -> Acc; make_string(Alph, Length, Acc) -> - {_,_,C}=erlang:now(), + C=erlang:unique_integer([positive]), Pos=1+(Length*C rem length(Alph)), make_string(Alph, Length-1, [lists:nth(Pos,Alph)|Acc]). diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 228832ac0a..6e0a5a9668 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -521,7 +521,9 @@ t_map_equal(Config) when is_list(Config) -> t_map_compare(Config) when is_list(Config) -> - Seed = erlang:now(), + Seed = {erlang:monotonic_time(), + erlang:time_offset(), + erlang:unique_integer()}, io:format("seed = ~p\n", [Seed]), random:seed(Seed), repeat(100, fun(_) -> float_int_compare() end, []), diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 07e2862b2a..dc215b1529 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -763,12 +763,10 @@ named_down(doc) -> ["Test that DOWN message for a named monitor isn't" " delivered until name has been unregistered"]; named_down(suite) -> []; named_down(Config) when is_list(Config) -> - ?line {A,B,C} = now(), ?line Name = list_to_atom(atom_to_list(?MODULE) ++ "-named_down-" - ++ integer_to_list(A) - ++ "-" ++ integer_to_list(B) - ++ "-" ++ integer_to_list(C)), + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), ?line Prio = process_flag(priority,high), %% Spawn a bunch of high prio cpu bound processes to prevent %% normal prio processes from terminating during the next diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl index a492501959..8dcd21f303 100644 --- a/erts/emulator/test/mtx_SUITE.erl +++ b/erts/emulator/test/mtx_SUITE.erl @@ -441,10 +441,10 @@ hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) -> receive after infinity -> ok end end) | Ps0] end, - Start = now(), + Start = erlang:monotonic_time(), lists:foreach(fun (P) -> P ! go end, Ps), lists:foreach(fun (P) -> receive {done, P} -> ok end end, Ps), - Stop = now(), + Stop = erlang:monotonic_time(), lists:foreach(fun (P) -> unlink(P), exit(P, bang), @@ -453,7 +453,7 @@ hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) -> {'DOWN', M, process, P, _} -> ok end end, Ps), - Res = timer:now_diff(Stop, Start)/1000000, + Res = (Stop-Start)/erlang:convert_time_unit(1,seconds,native), Caller ! {?MODULE, self(), Res} end, TP = spawn_link(T), diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index b0624fb8c1..78cfc595fa 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1188,7 +1188,9 @@ send3(Config) when is_list(Config) -> %% Let a number of processes send random message blobs between each other %% using enif_send. Kill and spawn new ones randomly to keep a ~constant %% number of workers running. - Seed = now(), + Seed = {erlang:monotonic_time(), + erlang:time_offset(), + erlang:unique_integer()}, io:format("seed: ~p\n",[Seed]), random:seed(Seed), ets:new(nif_SUITE,[named_table,public]), diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 9c1839811a..2f505893b4 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -1120,26 +1120,18 @@ wait_until(Pred) -> false -> receive after 100 -> wait_until(Pred) end end. +get_nodefirstname_string() -> + atom_to_list(?MODULE) + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive])). get_nodefirstname() -> - {A, B, C} = now(), - list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ integer_to_list(A) - ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C)). + list_to_atom(get_nodefirstname_string()). get_nodename() -> - {A, B, C} = now(), - list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ integer_to_list(A) - ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C) + list_to_atom(get_nodefirstname_string() ++ "@" ++ hostname()). diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl index 262536a068..57f6928185 100644 --- a/erts/emulator/test/old_scheduler_SUITE.erl +++ b/erts/emulator/test/old_scheduler_SUITE.erl @@ -116,7 +116,7 @@ equal(Config) when is_list(Config) -> %% start controllers ?line Receiver = - spawn(fun() -> receiver(now(), Time, Self, Normal, Low) end), + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end), ?line Starter = spawn(fun() -> starter(Normal, Low, Receiver) end), @@ -154,7 +154,7 @@ many_low(Config) when is_list(Config) -> Time = 30, ?line Receiver = - spawn(fun() -> receiver(now(), Time, Self, Normal, Low) end), + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end), ?line Starter = spawn(fun() -> starter(Normal, Low, Receiver) end), ?line {NRs,NAvg,LRs,LAvg,Ratio} = @@ -185,7 +185,7 @@ few_low(Config) when is_list(Config) -> Time = 30, ?line Receiver = - spawn(fun() -> receiver(now(), Time, Self, Normal, Low) end), + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end), ?line Starter = spawn(fun() -> starter(Normal, Low, Receiver) end), ?line {NRs,NAvg,LRs,LAvg,Ratio} = @@ -220,7 +220,7 @@ max(Config) when is_list(Config) -> Time = 30, ?line Receiver1 = - spawn(fun() -> receiver(now(), Time, Self, Max, High) end), + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, High) end), ?line Starter1 = spawn(fun() -> starter(Max, High, Receiver1) end), ?line {M1Rs,M1Avg,HRs,HAvg,Ratio1} = @@ -238,7 +238,7 @@ max(Config) when is_list(Config) -> end, ?line Receiver2 = - spawn(fun() -> receiver(now(), Time, Self, Max, Normal) end), + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Normal) end), ?line Starter2 = spawn(fun() -> starter(Max, Normal, Receiver2) end), ?line {M2Rs,M2Avg,NRs,NAvg,Ratio2} = @@ -256,7 +256,7 @@ max(Config) when is_list(Config) -> end, ?line Receiver3 = - spawn(fun() -> receiver(now(), Time, Self, Max, Low) end), + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Low) end), ?line Starter3 = spawn(fun() -> starter(Max, Low, Receiver3) end), ?line {M3Rs,M3Avg,LRs,LAvg,Ratio3} = @@ -290,7 +290,7 @@ high(Config) when is_list(Config) -> Time = 30, ?line Receiver1 = - spawn(fun() -> receiver(now(), Time, Self, High, Normal) end), + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Normal) end), ?line Starter1 = spawn(fun() -> starter(High, Normal, Receiver1) end), ?line {H1Rs,H1Avg,NRs,NAvg,Ratio1} = @@ -308,7 +308,7 @@ high(Config) when is_list(Config) -> end, ?line Receiver2 = - spawn(fun() -> receiver(now(), Time, Self, High, Low) end), + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Low) end), ?line Starter2 = spawn(fun() -> starter(High, Low, Receiver2) end), ?line {H2Rs,H2Avg,LRs,LAvg,Ratio2} = @@ -337,12 +337,13 @@ receiver(T0, TimeSec, Main, {P1,P1N}, {P2,P2N}) -> %% uncomment lines below to get life sign (debug) receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 0) -> -% T = elapsed_ms(T0, now()), +% T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, milli_seconds), % erlang:display({round(T/1000),P1Rs,P2Rs}), receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 100000); receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, C) -> - Remain = Time - elapsed_ms(T0, now()), % test time remaining + Remain = Time - erlang:convert_time_unit(erlang:monotonic_time() - T0, + native, milli_seconds), % test time remaining Remain1 = if Remain < 0 -> 0; true -> @@ -409,6 +410,3 @@ flush_loop() -> ok end, flush_loop(). - -elapsed_ms({_MS0,S0,MuS0},{_MS1,S1,MuS1}) -> - round(((S1-S0)*1000)+((MuS1-MuS0)/1000)). diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 6bbf93b7d7..e61c330861 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1815,7 +1815,7 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> Parent = self(), ?t:format("SleepSecs = ~p~n", [SleepSecs]), PortProg = "sleep " ++ integer_to_list(SleepSecs), - Start = now(), + Start = erlang:monotonic_time(micro_seconds), NoProcs = case NoSchedsOnln of NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS -> NProcs; @@ -1887,12 +1887,12 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> receive {P, started, SIds} -> SIds end end, Procs), - StartedTime = timer:now_diff(now(), Start)/1000000, + StartedTime = (erlang:monotonic_time(micro_seconds) - 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, + DoneTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000, ?t:format("DoneTime = ~p~n", [DoneTime]), true = DoneTime > SleepSecs, ok = verify_multi_scheduling_blocked(), diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index bf31655066..105d39f126 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -379,16 +379,15 @@ eat_high(Low) -> process_flag(priority, high), receive after 1000 -> ok end, exit(Low, {you, are, dead}), - {_, Sec, _} = now(), - loop(Sec, Sec). + loop(erlang:monotonic_time() + erlang:convert_time_unit(5,seconds,native)). %% Busy loop for 5 seconds. -loop(OrigSec, CurrentSec) when CurrentSec < OrigSec+5 -> - {_, NewSec, _} = now(), - loop(OrigSec, NewSec); -loop(_, _) -> - ok. +loop(StopTime) -> + case StopTime >= erlang:monotonic_time() of + true -> ok; + false -> loop(StopTime) + end. %% Tries to send two different exit messages to a process. @@ -2450,16 +2449,13 @@ start_node(Config) -> start_node(Config, Args) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), - {A, B, C} = now(), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" ++ atom_to_list(?config(testcase, Config)) ++ "-" - ++ integer_to_list(A) - ++ "-" - ++ integer_to_list(B) + ++ integer_to_list(erlang:system_time(seconds)) ++ "-" - ++ integer_to_list(C)), + ++ integer_to_list(erlang:unique_integer([positive]))), ?t:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). stop_node(Node) -> diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 3906471f87..c5af12c6d1 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -1829,11 +1829,11 @@ do_it(Tracer, Low, Normal, High, Max) -> do_it(Tracer, Low, Normal, High, Max, RedsPerSchedLimit) -> OldPrio = process_flag(priority, max), go_work(Low, Normal, High, Max), - StartWait = now(), + StartWait = erlang:monotonic_time(milli_seconds), %% Give the emulator a chance to balance the load... wait_balance(5), - EndWait = now(), - BalanceWait = timer:now_diff(EndWait,StartWait) div 1000, + EndWait = erlang:monotonic_time(milli_seconds), + BalanceWait = EndWait-StartWait, erlang:display({balance_wait, BalanceWait}), Timeout = ?DEFAULT_TIMEOUT - ?t:minutes(4) - BalanceWait, Res = case Timeout < ?MIN_SCHEDULER_TEST_TIMEOUT of @@ -2027,17 +2027,14 @@ start_node(Config) -> start_node(Config, ""). start_node(Config, Args) when is_list(Config) -> - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line {A, B, C} = now(), - ?line Name = list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ atom_to_list(?config(testcase, Config)) - ++ "-" - ++ integer_to_list(A) - ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C)), + Pa = filename:dirname(code:which(?MODULE)), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(?config(testcase, Config)) + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive]))), ?line ?t:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). stop_node(Node) -> diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index 736dfe5b56..dcb10c947e 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -515,12 +515,10 @@ repeat(Fun, N) when is_integer(N) -> repeat(Fun, N-1). start_node(Config) -> - {A, B, C} = now(), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" ++ atom_to_list(?config(testcase, Config)) - ++ "-" ++ integer_to_list(A) - ++ "-" ++ integer_to_list(B) - ++ "-" ++ integer_to_list(C)), + ++ "-" ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), Pa = filename:dirname(code:which(?MODULE)), ?t:start_node(Name, slave, [{args, "-pa " ++ Pa}]). diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 10b7e16a74..4c50b8ba8c 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -167,16 +167,13 @@ start_node(Config) -> start_node(Config, Args) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), - {A, B, C} = now(), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" ++ atom_to_list(?config(testcase, Config)) ++ "-" - ++ integer_to_list(A) + ++ integer_to_list(erlang:system_time(seconds)) ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C)), + ++ integer_to_list(erlang:unique_integer([positive]))), Opts = [{args, "-pa "++Pa++" "++Args}], ?t:start_node(Name, slave, Opts). diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index f959714be7..0fd6696536 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -533,16 +533,13 @@ get_ets_limit(Config, EtsMax) -> start_node(Config, Envs) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), - {A, B, C} = now(), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" ++ atom_to_list(?config(testcase, Config)) ++ "-" - ++ integer_to_list(A) + ++ integer_to_list(erlang:system_time(seconds)) ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C)), + ++ integer_to_list(erlang:unique_integer([positive]))), ?t:start_node(Name, peer, [{args, "-pa "++Pa}, {env, Envs}]). stop_node(Node) -> diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index 43f7ac7f7c..55e1b7858c 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -18,6 +18,7 @@ %% -module(time_SUITE). +-compile({nowarn_deprecated_function, {erlang,now,0}}). %% "Time is on my side." -- The Rolling Stones diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index 56a1cef761..3701dd8437 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -502,9 +502,10 @@ get_msg() -> end. start_slave() -> - ?line {A, B, C} = now(), ?line Pa = filename:dirname(code:which(?MODULE)), - ?line Name = atom_to_list(?MODULE) ++ "-" ++ integer_to_list(A+B+C), + ?line Name = atom_to_list(?MODULE) + ++ "-" ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" ++ integer_to_list(erlang:unique_integer([positive])), {ok, Node} = ?t:start_node(Name, slave, [{args, "-pa " ++ Pa}]), Node. diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 3036d2957b..9c444ed682 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -326,10 +326,10 @@ combo(Config) when is_list(Config) -> %% ?line [3,2,1] = seq_r(1, 3, fun(X) -> X+1 end), - ?line T0 = now(), + ?line T0 = erlang:monotonic_time(), ?line with_bif(Nbc), - ?line T1 = now(), - ?line TimeB = timer:now_diff(T1,T0), + ?line T1 = erlang:monotonic_time(), + ?line TimeB = erlang:convert_time_unit(T1-T0, native, micro_seconds), %% ?line List = collect(100), @@ -695,17 +695,17 @@ setup(Opts) -> Pid. execute(Pids, Mfa) when is_list(Pids) -> - T0 = now(), + T0 = erlang:monotonic_time(), [P ! {self(), execute, Mfa} || P <- Pids], As = [receive {P, answer, Answer} -> Answer end || P <- Pids], - T1 = now(), - {As, timer:now_diff(T1,T0)}; + T1 = erlang:monotonic_time(), + {As, erlang:convert_time_unit(T1-T0, native, micro_seconds)}; execute(P, Mfa) -> - T0 = now(), + T0 = erlang:monotonic_time(), P ! {self(), execute, Mfa}, A = receive {P, answer, Answer} -> Answer end, - T1 = now(), - {A, timer:now_diff(T1,T0)}. + T1 = erlang:monotonic_time(), + {A, erlang:convert_time_unit(T1-T0, native, micro_seconds)}. -- cgit v1.2.3 From 5ee6157ad045bf74c07c6d12583fc9b5e58df390 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 1 Apr 2015 20:33:11 +0200 Subject: Add parallel time monotonicity test-case --- erts/emulator/test/time_SUITE.erl | 74 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index 55e1b7858c..d04a95b10e 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -38,6 +38,7 @@ now_unique/1, now_update/1, timestamp/1, time_warp_modes/1, monotonic_time_monotonicity/1, + monotonic_time_monotonicity_parallel/1, time_unit_conversion/1, signed_time_unit_conversion/1, erlang_timestamp/1]). @@ -80,6 +81,7 @@ all() -> {group, now}, timestamp, time_warp_modes, monotonic_time_monotonicity, + monotonic_time_monotonicity_parallel, time_unit_conversion, signed_time_unit_conversion, erlang_timestamp]. @@ -566,6 +568,78 @@ cmp_times(Done, X0) -> cmp_times(Done, X5) end. +-define(NR_OF_MONOTONIC_CALLS, 100000). + +monotonic_time_monotonicity_parallel(Config) when is_list(Config) -> + Me = self(), + Result = make_ref(), + Go = make_ref(), + UpAndRunning = make_ref(), + NoOnlnScheds = erlang:system_info(schedulers_online), + OffsetUI = erlang:unique_integer([monotonic]), + OffsetMT = erlang:monotonic_time(), + MinHSz = ?NR_OF_MONOTONIC_CALLS*(2 + + 3 + + erts_debug:flat_size(OffsetUI) + + erts_debug:flat_size(OffsetMT)), + Ps = lists:map( + fun (Sched) -> + spawn_opt( + fun () -> + Me ! {self(), UpAndRunning}, + receive Go -> ok end, + Res = fetch_monotonic(?NR_OF_MONOTONIC_CALLS, []), + Me ! {self(), Result, Sched, Res} + end, + [{scheduler, Sched}, + {priority, max}, + {min_heap_size, MinHSz}]) + end, + lists:seq(1, NoOnlnScheds)), + lists:foreach(fun (P) -> receive {P, UpAndRunning} -> ok end end, Ps), + lists:foreach(fun (P) -> P ! Go end, Ps), + TMs = recv_monotonics(Result, OffsetMT, OffsetUI, NoOnlnScheds, []), + true = check_monotonic_result(TMs, OffsetMT, OffsetUI, true). + +check_monotonic_result([{_Sched, _PrevUI, _MT, _PostUI}], + _OffsetMT, _OffsetUI, Res) -> + Res; +check_monotonic_result([{_ASched, _APrevUI, AMT, APostUI} = A, + {_BSched, BPrevUI, BMT, _BPostUI} = B | _] = L, + OffsetMT, OffsetUI, Res) -> + NewRes = case (AMT =< BMT) orelse (BPrevUI < APostUI) of + true -> + Res; + false -> + io:format("INCONSISTENCY: ~p ~p~n", [A, B]), + false + end, + check_monotonic_result(tl(L), OffsetMT, OffsetUI, NewRes). + +recv_monotonics(_Result, _OffsetMT, _OffsetUI, 0, Acc) -> + lists:keysort(2, Acc); +recv_monotonics(Result, OffsetMT, OffsetUI, N, Acc) -> + receive + {_, Result, Sched, Res} -> + CRes = convert_monotonic(Sched, OffsetMT, OffsetUI, Res, []), + recv_monotonics(Result, OffsetMT, OffsetUI, N-1, CRes ++ Acc) + end. + +convert_monotonic(_Sched, _OffsetMT, _OffsetUI, [{_MT, _UI}], Acc) -> + Acc; +convert_monotonic(Sched, OffsetMT, OffsetUI, + [{MT, UI}, {_PrevMT, PrevUI} | _] = L, Acc) -> + convert_monotonic(Sched, OffsetMT, OffsetUI, tl(L), + [{Sched, PrevUI-OffsetUI, MT-OffsetMT, UI-OffsetUI} + | Acc]). + +fetch_monotonic(0, Acc) -> + Acc; +fetch_monotonic(N, Acc) -> + MT = erlang:monotonic_time(), + UI = erlang:unique_integer([monotonic]), + fetch_monotonic(N-1, [{MT, UI} | Acc]). + -define(CHK_RES_CONVS_TIMEOUT, 400). time_unit_conversion(Config) when is_list(Config) -> -- cgit v1.2.3 From 6b5905e49c74c4034b55824ce4d1a62455f670bc Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 27 Apr 2015 14:54:47 +0200 Subject: Allow execution of estone suite on pre OTP-18 systems --- erts/emulator/test/estone_SUITE.erl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index 9fe161cffc..67a53d94b1 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -368,9 +368,9 @@ run_micro(Top, M, DataDir) -> apply_micro(M) -> {GC0, Words0, _} = statistics(garbage_collection), statistics(reductions), - Before = erlang:monotonic_time(), + Before = monotonic_time(), Compensate = apply_micro(M#micro.function, M#micro.loops), - After = erlang:monotonic_time(), + After = monotonic_time(), {GC1, Words1, _} = statistics(garbage_collection), {_, Reds} = statistics(reductions), Elapsed = subtr(Before, After), @@ -387,9 +387,13 @@ apply_micro(M) -> {kilo_reductions, Reds div 1000}, {gc_intensity, gci(Elapsed, GC1 - GC0, Words1 - Words0)}]. +monotonic_time() -> + try erlang:monotonic_time() catch error:undef -> erlang:now() end. -subtr(Before, After) -> - erlang:convert_time_unit(After-Before, native, micro_seconds). +subtr(Before, After) when is_integer(Before), is_integer(After) -> + erlang:convert_time_unit(After-Before, native, micro_seconds); +subtr({_,_,_}=Before, {_,_,_}=After) -> + timer:now_diff(After, Before). gci(Micros, Words, Gcs) -> ((256 * Gcs) / Micros) + (Words / Micros). @@ -628,10 +632,10 @@ tup_trav(T, P, End) -> %% Port I/O port_io(I) -> EstoneCat = get(estone_cat), - Before = erlang:monotonic_time(), + Before = monotonic_time(), Pps = make_port_pids(5, I, EstoneCat), %% 5 ports send_procs(Pps, go), - After = erlang:monotonic_time(), + After = monotonic_time(), wait_for_pids(Pps), subtr(Before, After). @@ -849,10 +853,10 @@ handle_call(_From, State, [abc]) -> %% Binary handling, creating, manipulating and sending binaries binary_h(I) -> - Before = erlang:monotonic_time(), + Before = monotonic_time(), P = spawn(?MODULE, echo, [self()]), B = list_to_binary(lists:duplicate(2000, 5)), - After = erlang:monotonic_time(), + After = monotonic_time(), Compensate = subtr(Before, After), binary_h_2(I, P, B), Compensate. -- cgit v1.2.3 From 7ba91b64862e29bfd579b04c73e2bccacde6a003 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 5 May 2015 20:03:17 +0200 Subject: Reusable red-black tree implementation --- erts/emulator/beam/erl_rbtree.h | 1740 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1740 insertions(+) create mode 100644 erts/emulator/beam/erl_rbtree.h (limited to 'erts') diff --git a/erts/emulator/beam/erl_rbtree.h b/erts/emulator/beam/erl_rbtree.h new file mode 100644 index 0000000000..ea0a8976bb --- /dev/null +++ b/erts/emulator/beam/erl_rbtree.h @@ -0,0 +1,1740 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2015. All Rights Reserved. + * + * The 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: A Red-Black (binary search) Tree implementation. The search, + * insert, and delete operations are all O(log n) operations + * on a Red-Black Tree. Red-Black Trees are described in + * "Introduction to Algorithms", by Thomas H. Cormen, Charles + * E. Leiserson, and Ronald L. Riverest. + * + * Use by defining mandatory defines as well as defines for + * API functions wanted, and include this header. + * + * Author: Rickard Green + * + * + * Mandatory defines: + * - ERTS_RBT_PREFIX - Prefix to use on functions. + * - ERTS_RBT_T - Type of a tree node. + * - ERTS_RBT_KEY_T - Type of key for a tree node. + * - ERTS_RBT_FLAGS_T - Type of flags for a tree node. + * - ERTS_RBT_INIT_EMPTY_TNODE(T) -Initialize an empty tree node. + * - ERTS_RBT_IS_RED(T) - Is tree node red? + * - ERTS_RBT_SET_RED(T) - Set tree node red. + * - ERTS_RBT_IS_BLACK(T) - Is tree node back? + * - ERTS_RBT_SET_BLACK(T) - Set tree node black. + * - ERTS_RBT_GET_FLAGS(T) - Get flags of tree node (incl colors). + * - ERTS_RBT_SET_FLAGS(T, F) - Set flags of tree note. + * - ERTS_RBT_GET_PARENT(T) - Get parent node. + * - ERTS_RBT_SET_PARENT(T, P) - Set parent node. + * - ERTS_RBT_GET_RIGHT(T) - Get right child node. + * - ERTS_RBT_SET_RIGHT(T, R) - Set right child node. + * - ERTS_RBT_GET_LEFT(T) - Get left child node. + * - ERTS_RBT_SET_LEFT(T, L) - Set left child node. + * - ERTS_RBT_GET_KEY(T) - Get key of node. + * - ERTS_RBT_IS_LT(KX, KY) - Is key KX less than key KY? + * - ERTS_RBT_IS_EQ(KX, KY) - Is key KX equal to key KY? + * + * Optional defines: + * + * - ERTS_RBT_UNDEF - Undefine all user defined ERTS_RBT_* + * defines after use. + * + * - ERTS_RBT_NO_API_INLINE - Do not inline API functions. + * + * Attached data management: + * - ERTS_RBT_UPDATE_ATTACHED_DATA_ROTATE(L, OP, NP) - Called + * when a rotate operation has been performed. If L (in int) + * is a non zero, a left rotation was performed; otherwise, + * a right rotation was performed. OR points to the old + * parent node and NP points to the new parent node. + * - ERTS_RBT_UPDATE_ATTACHED_DATA_DMOD(F, T) - Called when + * a delete operation modifies a tree node. A delete + * modification is either a removal or replacement of a + * node. F points to the parent of the tree node that was + * modified. T points to the next ancestor that will be + * modified. If T is NULL, no more removal and/or + * replacements will be made. One typically wants to update + * the attached data of each node between F and T. If T is + * NULL all the way up to the root. + * - ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT(OR, NR) - Called + * when the root node changes. OR points to the old + * root node and NP points to the new root node. + * + * Request implementation of API functions: + * - ERTS_RBT_WANT_DELETE + * - ERTS_RBT_WANT_INSERT + * - ERTS_RBT_WANT_LOOKUP_INSERT + * - ERTS_RBT_WANT_REPLACE + * - ERTS_RBT_WANT_LOOKUP + * - ERTS_RBT_WANT_SMALLEST + * - ERTS_RBT_WANT_LARGEST + * - ERTS_RBT_WANT_FOREACH + * - ERTS_RBT_WANT_FOREACH_DESTROY + * - ERTS_RBT_WANT_FOREACH_YIELDING + * - ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING + * - ERTS_RBT_WANT_FOREACH_SMALL + * - ERTS_RBT_WANT_FOREACH_LARGE + * - ERTS_RBT_WANT_FOREACH_SMALL_DESTROY + * - ERTS_RBT_WANT_FOREACH_LARGE_DESTROY + * - ERTS_RBT_WANT_FOREACH_SMALL_YIELDING + * - ERTS_RBT_WANT_FOREACH_LARGE_YIELDING + * - ERTS_RBT_WANT_FOREACH_SMALL_DESTROY_YIELDING + * - ERTS_RBT_WANT_FOREACH_LARGE_DESTROY_YIELDING + * - ERTS_RBT_WANT_DEBUG_PRINT + * + * The yield state data type will equal + * _rbt_yield_state_t. + * + * The yield state should be statically initialized by + * ERTS_RBT_YIELD_STAT_INITER. + * + * + * The following API functions are implemented if corresponding + * ERTS_RBT_WANT_ is defined: + * + * - void _rbt_delete( + * ERTS_RBT_T **tree, + * ERTS_RBT_T *element); + * Delete element from tree. + * + * - void _rbt_insert( + * ERTS_RBT_T **tree, + * ERTS_RBT_T *element); + * Insert element into tree. + * + * - ERTS_RBT_T * _rbt_lookup_insert( + * ERTS_RBT_T **tree, + * ERTS_RBT_T *element); + * Look up an element in the tree that compares as equal to the + * element passed as argument, and return the looked up element. + * If no element compared as equal, insert the element passed as + * argument into the tree, and return NULL. + * + * - void _rbt_replace( + * ERTS_RBT_T **tree, + * ERTS_RBT_T *old_element, + * ERTS_RBT_T *new_element); + * Replace old_element in the tree with new_element. Both elements + * *should* compare as equal. + * + * - ERTS_RBT_T * _rbt_lookup( + * ERTS_RBT_T *tree, + * ERTS_RBT_KEY_T key); + * Look up an element with a key that compares as equal to + * the key passed as argument. + * + * - ERTS_RBT_T * _rbt_smallest( + * ERTS_RBT_T *tree); + * Look up the element with the smallest key. + * + * - ERTS_RBT_T * _rbt_largest( + * ERTS_RBT_T *tree); + * Look up the element with the largest key. + * + * - void _rbt_foreach( + * ERTS_RBT_T *tree, + * void (*op)(ERTS_RBT_T *, void *), + * void *arg); + * Operate by calling the operator 'op' on each element. + * Order is undefined. + * + * 'arg' is passed as argument to 'op'. + * + * - void _rbt_foreach_destroy( + * ERTS_RBT_T *tree, + * void (*op)(ERTS_RBT_T *, void *), + * void *arg); + * Operate by calling the operator 'op' on each element. + * Order is undefined. Each element should be destroyed + * by 'op'. + * + * 'arg' is passed as argument to 'op'. + * + * - int _rbt_foreach_yielding( + * ERTS_RBT_T *tree, + * void (*op)(ERTS_RBT_T *, void *), + * void *arg, + * _rbt_yield_state_t *ystate, + * Sint ylimit); + * Operate by calling the operator 'op' on each element. + * Order is undefined. + * + * Yield when 'ylimit' elements has been processed. Zero is + * returned when yielding, and a non-zero value is returned when + * the whole tree has been processed. The tree should not be + * modified until all of it has been processed. + * + * 'arg' is passed as argument to 'op'. + * + * - int _rbt_foreach_destroy_yielding( + * ERTS_RBT_T *tree, + * void (*op)(ERTS_RBT_T *, void *), + * void *arg, + * _rbt_yield_state_t *ystate, + * Sint ylimit); + * Operate by calling the operator 'op' on each element. + * Order is undefined. Each element should be destroyed + * by 'op'. + * + * Yield when 'ylimit' elements has been processed. Zero is + * returned when yielding, and a non-zero value is returned when + * the whole tree has been processed. + * + * 'arg' is passed as argument to 'op'. + * + * - void _rbt_foreach_small( + * ERTS_RBT_T *tree, + * void (*op)(ERTS_RBT_T *, void *), + * void *arg); + * Operate by calling the operator 'op' on each element from + * smallest towards larger elements. + * + * 'arg' is passed as argument to 'op'. + * + * - void _rbt_foreach_large( + * ERTS_RBT_T *tree, + * void (*op)(ERTS_RBT_T *, void *), + * void *arg); + * Operate by calling the operator 'op' on each element from + * largest towards smaller elements. + * + * 'arg' is passed as argument to 'op'. + * + * - int _rbt_foreach_small_yielding( + * ERTS_RBT_T *tree, + * void (*op)(ERTS_RBT_T *, void *), + * void *arg, + * _rbt_yield_state_t *ystate, + * Sint ylimit); + * Operate by calling the operator 'op' on each element from + * smallest towards larger elements. + * + * Yield when 'ylimit' elements has been processed. Zero is + * returned when yielding, and a non-zero value is returned when + * the whole tree has been processed. The tree should not be + * modified until all of it has been processed. + * + * 'arg' is passed as argument to 'op'. + * + * - int _rbt_foreach_large_yielding( + * ERTS_RBT_T *tree, + * void (*op)(ERTS_RBT_T *, void *), + * void *arg, + * _rbt_yield_state_t *ystate, + * Sint ylimit); + * Operate by calling the operator 'op' on each element from + * largest towards smaller elements. + * + * Yield when 'ylimit' elements has been processed. Zero is + * returned when yielding, and a non-zero value is returned when + * the whole tree has been processed. The tree should not be + * modified until all of it has been processed. + * + * 'arg' is passed as argument to 'op'. + * + * - void _rbt_foreach_small_destroy( + * ERTS_RBT_T **tree, + * void (*op)(ERTS_RBT_T *, void *), + * void (*destr)(ERTS_RBT_T *, void *), + * void *arg); + * Operate by calling the operator 'op' on each element from + * smallest towards larger elements. + * + * Destroy elements by calling the destructor 'destr'. Elements + * are destroyed when not needed by the tree structure anymore. + * Note that elements are often *not* destroyed in another order + * than the order that the elements are operated on. + * + * 'arg' is passed as argument to 'op' and 'destroy'. + * + * - void _rbt_foreach_large_destroy( + * ERTS_RBT_T **tree, + * void (*op)(ERTS_RBT_T *, void *), + * void (*destr)(ERTS_RBT_T *, void *), + * void *arg); + * Operate by calling the operator 'op' on each element from + * largest towards smaller elements. + * + * Destroy elements by calling the destructor 'destr'. Elements + * are destroyed when not needed by the tree structure anymore. + * Note that elements are often destroyed in another order + * than the order that the elements are operated on. + * + * 'arg' is passed as argument to 'op' and 'destroy'. + * + * - int _rbt_foreach_small_destroy_yielding( + * ERTS_RBT_T **tree, + * void (*op)(ERTS_RBT_T *, void *), + * void (*destr)(ERTS_RBT_T *, void *), + * void *arg, + * _rbt_yield_state_t *ystate, + * Sint ylimit); + * Operate by calling the operator 'op' on each element from + * smallest towards larger elements. + * + * Destroy elements by calling the destructor 'destr'. Elements + * are destroyed when not needed by the tree structure anymore. + * Note that elements are often destroyed in another order + * than the order that the elements are operated on. + * + * Yield when 'ylimit' elements has been processed. Zero is + * returned when yielding, and a non-zero value is returned when + * the whole tree has been processed. The tree should not be + * modified until all of it has been processed. + * + * 'arg' is passed as argument to 'op' and 'destroy'. + * + * - int _rbt_foreach_large_destroy_yielding( + * ERTS_RBT_T **tree, + * void (*op)(ERTS_RBT_T *, void *), + * void (*destr)(ERTS_RBT_T *, void *), + * void *arg, + * _rbt_yield_state_t *ystate, + * Sint ylimit); + * Operate by calling the operator 'op' on each element from + * largest towards smaller elements. + * + * Destroy elements by calling the destructor 'destr'. Elements + * are destroyed when not needed by the tree structure anymore. + * Note that elements are often destroyed in another order + * than the order that the elements are operated on. + * + * Yield when 'ylimit' elements has been processed. Zero is + * returned when yielding, and a non-zero value is returned when + * the whole tree has been processed. The tree should not be + * modified until all of it has been processed. + * + * 'arg' is passed as argument to 'op' and 'destroy'. + * + * - void _rbt_debug_print( + * FILE *filep, + * ERTS_RBT_T *x, + * int indent, + * (void)(*print_node)(ERTS_RBT_T *)); + * Prints the tree. Note that this function is recursive. + * Should only be used for debuging. + */ + + +/* + * Check that we have all mandatory defines + */ +#ifndef ERTS_RBT_PREFIX +# error Missing definition of ERTS_RBT_PREFIX +#endif +#ifndef ERTS_RBT_T +# error Missing definition of ERTS_RBT_T +#endif +#ifndef ERTS_RBT_KEY_T +# error Missing definition of ERTS_RBT_KEY_T +#endif +#ifndef ERTS_RBT_FLAGS_T +# error Missing definition of ERTS_RBT_FLAGS_T +#endif +#ifndef ERTS_RBT_INIT_EMPTY_TNODE +# error Missing definition of ERTS_RBT_INIT_EMPTY_TNODE +#endif +#ifndef ERTS_RBT_IS_RED +# error Missing definition of ERTS_RBT_IS_RED +#endif +#ifndef ERTS_RBT_SET_RED +# error Missing definition of ERTS_RBT_SET_RED +#endif +#ifndef ERTS_RBT_IS_BLACK +# error Missing definition of ERTS_RBT_IS_BLACK +#endif +#ifndef ERTS_RBT_SET_BLACK +# error Missing definition of ERTS_RBT_SET_BLACK +#endif +#ifndef ERTS_RBT_GET_FLAGS +# error Missing definition of ERTS_RBT_GET_FLAGS +#endif +#ifndef ERTS_RBT_SET_FLAGS +# error Missing definition of ERTS_RBT_SET_FLAGS +#endif +#ifndef ERTS_RBT_GET_PARENT +# error Missing definition of ERTS_RBT_GET_PARENT +#endif +#ifndef ERTS_RBT_SET_PARENT +# error Missing definition of ERTS_RBT_SET_PARENT +#endif +#ifndef ERTS_RBT_GET_RIGHT +# error Missing definition of ERTS_RBT_GET_RIGHT +#endif +#ifndef ERTS_RBT_GET_LEFT +# error Missing definition of ERTS_RBT_GET_LEFT +#endif +#ifndef ERTS_RBT_IS_LT +# error Missing definition of ERTS_RBT_IS_LT +#endif +#ifndef ERTS_RBT_GET_KEY +# error Missing definition of ERTS_RBT_GET_KEY +#endif +#ifndef ERTS_RBT_IS_EQ +# error Missing definition of ERTS_RBT_IS_EQ +#endif + +#if defined(ERTS_RBT_HARD_DEBUG) || defined(DEBUG) +# ifndef ERTS_RBT_DEBUG +# define ERTS_RBT_DEBUG 1 +# endif +#endif + +#if defined(ERTS_RBT_HARD_DEBUG) && defined(__GNUC__) +#warning "* * * * * * * * * * * * * * * * * *" +#warning "* ERTS_RBT_HARD_DEBUG IS ENABLED! *" +#warning "* * * * * * * * * * * * * * * * * *" +#endif + +#undef ERTS_RBT_ASSERT +#if defined(ERTS_RBT_DEBUG) +#define ERTS_RBT_ASSERT(E) ERTS_ASSERT(E) +#else +#define ERTS_RBT_ASSERT(E) ((void) 1) +#endif + +#undef ERTS_RBT_API_INLINE__ +#if defined(ERTS_RBT_NO_API_INLINE) || defined(ERTS_RBT_DEBUG) +# define ERTS_RBT_API_INLINE__ +#else +# define ERTS_RBT_API_INLINE__ ERTS_INLINE +#endif + +#ifndef ERTS_RBT_YIELD_STAT_INITER +# define ERTS_RBT_YIELD_STAT_INITER {NULL, 0} +#endif + +#define ERTS_RBT_CONCAT_MACRO_VALUES___(X, Y) \ + X ## Y +#define ERTS_RBT_CONCAT_MACRO_VALUES__(X, Y) \ + ERTS_RBT_CONCAT_MACRO_VALUES___(X, Y) + +#undef ERTS_RBT_YIELD_STATE_T__ +#define ERTS_RBT_YIELD_STATE_T__ \ + ERTS_RBT_CONCAT_MACRO_VALUES__(ERTS_RBT_PREFIX, _rbt_yield_state_t) + +typedef struct { + ERTS_RBT_T *x; + int up; +} ERTS_RBT_YIELD_STATE_T__; + +#define ERTS_RBT_FUNC__(Name) \ + ERTS_RBT_CONCAT_MACRO_VALUES__(ERTS_RBT_PREFIX, _rbt_ ## Name) + +#undef ERTS_RBT_NEED_REPLACE__ +#undef ERTS_RBT_NEED_INSERT__ +#undef ERTS_RBT_NEED_ROTATE__ +#undef ERTS_RBT_NEED_FOREACH_UNORDERED__ +#undef ERTS_RBT_NEED_FOREACH_ORDERED__ +#undef ERTS_RBT_NEED_HDBG_CHECK_TREE__ +#undef ERTS_RBT_HDBG_CHECK_TREE__ + +#if defined(ERTS_RBT_WANT_REPLACE) || defined(ERTS_RBT_WANT_DELETE) +# define ERTS_RBT_NEED_REPLACE__ +#endif +#if defined(ERTS_RBT_WANT_INSERT) || defined(ERTS_RBT_WANT_LOOKUP_INSERT) +# define ERTS_RBT_NEED_INSERT__ +#endif +#if defined(ERTS_RBT_WANT_DELETE) || defined(ERTS_RBT_NEED_INSERT__) +# define ERTS_RBT_NEED_ROTATE__ +#endif +#if defined(ERTS_RBT_WANT_FOREACH) \ + || defined(ERTS_RBT_WANT_FOREACH_YIELDING) \ + || defined(ERTS_RBT_WANT_FOREACH_DESTROY) \ + || defined(ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING) +# define ERTS_RBT_NEED_FOREACH_UNORDERED__ +#endif +#if defined(ERTS_RBT_WANT_FOREACH_SMALL) \ + || defined(ERTS_RBT_WANT_FOREACH_LARGE) \ + || defined(ERTS_RBT_WANT_FOREACH_SMALL_YIELDING) \ + || defined(ERTS_RBT_WANT_FOREACH_LARGE_YIELDING) \ + || defined(ERTS_RBT_WANT_FOREACH_SMALL_DESTROY) \ + || defined(ERTS_RBT_WANT_FOREACH_LARGE_DESTROY) \ + || defined(ERTS_RBT_WANT_FOREACH_SMALL_DESTROY_YIELDING) \ + || defined(ERTS_RBT_WANT_FOREACH_LARGE_DESTROY_YIELDING) +# define ERTS_RBT_NEED_FOREACH_ORDERED__ +#endif +#if defined(ERTS_RBT_HARD_DEBUG) \ + && (defined(ERTS_RBT_WANT_DELETE) \ + || defined(ERTS_RBT_NEED_INSERT__)) +static void ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root); +# define ERTS_RBT_NEED_HDBG_CHECK_TREE__ +# define ERTS_RBT_HDBG_CHECK_TREE__(R) \ + ERTS_RBT_FUNC__(hdbg_check_tree)((R)) +#else +# define ERTS_RBT_HDBG_CHECK_TREE__(R) ((void) 1) +#endif + +#ifdef ERTS_RBT_NEED_ROTATE__ + +static ERTS_INLINE void +ERTS_RBT_FUNC__(left_rotate__)(ERTS_RBT_T **root, ERTS_RBT_T *x) +{ + ERTS_RBT_T *y, *l, *p; + + y = ERTS_RBT_GET_RIGHT(x); + l = ERTS_RBT_GET_LEFT(y); + ERTS_RBT_SET_RIGHT(x, l); + + if (l) + ERTS_RBT_SET_PARENT(l, x); + + p = ERTS_RBT_GET_PARENT(x); + ERTS_RBT_SET_PARENT(y, p); + + if (!p) { + ERTS_RBT_ASSERT(*root == x); + *root = y; +#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT + ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT(x, y); +#endif + } + else if (x == ERTS_RBT_GET_LEFT(p)) + ERTS_RBT_SET_LEFT(p, y); + else { + ERTS_RBT_ASSERT(x == ERTS_RBT_GET_RIGHT(p)); + ERTS_RBT_SET_RIGHT(p, y); + } + ERTS_RBT_SET_LEFT(y, x); + ERTS_RBT_SET_PARENT(x, y); + +#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_ROTATE + ERTS_RBT_UPDATE_ATTACHED_DATA_ROTATE(!0, x, y); +#endif + +} + +static ERTS_INLINE void +ERTS_RBT_FUNC__(right_rotate__)(ERTS_RBT_T **root, ERTS_RBT_T *x) +{ + ERTS_RBT_T *y, *r, *p; + + y = ERTS_RBT_GET_LEFT(x); + r = ERTS_RBT_GET_RIGHT(y); + ERTS_RBT_SET_LEFT(x, r); + + if (r) + ERTS_RBT_SET_PARENT(r, x); + + p = ERTS_RBT_GET_PARENT(x); + ERTS_RBT_SET_PARENT(y, p); + + if (!p) { + ERTS_RBT_ASSERT(*root == x); + *root = y; +#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT + ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT(x, y); +#endif + } + else if (x == ERTS_RBT_GET_RIGHT(p)) + ERTS_RBT_SET_RIGHT(p, y); + else { + ERTS_RBT_ASSERT(x == ERTS_RBT_GET_LEFT(p)); + ERTS_RBT_SET_LEFT(p, y); + } + + ERTS_RBT_SET_RIGHT(y, x); + ERTS_RBT_SET_PARENT(x, y); + +#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_ROTATE + ERTS_RBT_UPDATE_ATTACHED_DATA_ROTATE(0, x, y); +#endif + +} + +#endif /* ERTS_RBT_NEED_ROTATE__ */ + +#ifdef ERTS_RBT_NEED_REPLACE__ + +/* + * Replace node x with node y + */ +static ERTS_INLINE void +ERTS_RBT_FUNC__(replace__)(ERTS_RBT_T **root, ERTS_RBT_T *x, ERTS_RBT_T *y) +{ + ERTS_RBT_T *p, *r, *l; + ERTS_RBT_FLAGS_T f; + + p = ERTS_RBT_GET_PARENT(x); + if (!p) { + ERTS_RBT_ASSERT(*root == x); + *root = y; +#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT + ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT(x, y); +#endif + } + else if (x == ERTS_RBT_GET_LEFT(p)) + ERTS_RBT_SET_LEFT(p, y); + else { + ERTS_RBT_ASSERT(x == ERTS_RBT_GET_RIGHT(p)); + ERTS_RBT_SET_RIGHT(p, y); + } + l = ERTS_RBT_GET_LEFT(x); + if (l) { + ERTS_RBT_ASSERT(ERTS_RBT_GET_PARENT(l) == x); + ERTS_RBT_SET_PARENT(l, y); + } + r = ERTS_RBT_GET_RIGHT(x); + if (r) { + ERTS_RBT_ASSERT(ERTS_RBT_GET_PARENT(r) == x); + ERTS_RBT_SET_PARENT(r, y); + } + + f = ERTS_RBT_GET_FLAGS(x); + ERTS_RBT_SET_FLAGS(y, f); + ERTS_RBT_SET_PARENT(y, p); + ERTS_RBT_SET_RIGHT(y, r); + ERTS_RBT_SET_LEFT(y, l); +} + +#endif /* ERTS_RBT_NEED_REPLACE__ */ + +#ifdef ERTS_RBT_WANT_REPLACE + +static ERTS_RBT_API_INLINE__ void +ERTS_RBT_FUNC__(replace)(ERTS_RBT_T **root, ERTS_RBT_T *x, ERTS_RBT_T *y) +{ + ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(x), + ERTS_RBT_GET_KEY(y))); + + ERTS_RBT_FUNC__(replace__)(root, x, y); +} + +#endif /* ERTS_RBT_WANT_REPLACE */ + +#ifdef ERTS_RBT_WANT_DELETE + +/* + * Delete a node. + */ +static ERTS_RBT_API_INLINE__ void +ERTS_RBT_FUNC__(delete)(ERTS_RBT_T **root, ERTS_RBT_T *n) +{ + int spliced_is_black; + ERTS_RBT_T *p, *x, *y, *z = n; + ERTS_RBT_T null_x; /* null_x is used to get the fixup started when we + splice out a node without children. */ + + ERTS_RBT_HDBG_CHECK_TREE__(*root); + + ERTS_RBT_INIT_EMPTY_TNODE(&null_x); + + /* Remove node from tree... */ + + /* Find node to splice out */ + if (!ERTS_RBT_GET_LEFT(z) || !ERTS_RBT_GET_RIGHT(z)) + y = z; + else { + /* Set y to z:s successor */ + y = ERTS_RBT_GET_RIGHT(z); + while (1) { + ERTS_RBT_T *t = ERTS_RBT_GET_LEFT(y); + if (!t) + break; + y = t; + } + } + /* splice out y */ + x = ERTS_RBT_GET_LEFT(y); + if (!x) + x = ERTS_RBT_GET_RIGHT(y); + spliced_is_black = ERTS_RBT_IS_BLACK(y); + p = ERTS_RBT_GET_PARENT(y); + if (x) + ERTS_RBT_SET_PARENT(x, p); + else if (spliced_is_black) { + x = &null_x; + ERTS_RBT_SET_BLACK(x); + ERTS_RBT_SET_PARENT(x, p); + ERTS_RBT_SET_LEFT(y, x); + } + + if (!p) { + ERTS_RBT_ASSERT(*root == y); + *root = x; +#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT + ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT(y, x); +#endif + } + else { + if (y == ERTS_RBT_GET_LEFT(p)) + ERTS_RBT_SET_LEFT(p, x); + else { + ERTS_RBT_ASSERT(y == ERTS_RBT_GET_RIGHT(p)); + ERTS_RBT_SET_RIGHT(p, x); + } +#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_DMOD + if (p != z) + ERTS_RBT_UPDATE_ATTACHED_DATA_DMOD(p, y == z ? NULL : z); +#endif + } + if (y != z) { + /* We spliced out the successor of z; replace z by the successor */ + ERTS_RBT_FUNC__(replace__)(root, z, y); +#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_DMOD + ERTS_RBT_UPDATE_ATTACHED_DATA_DMOD(y, NULL); +#endif + } + + if (spliced_is_black) { + /* We removed a black node which makes the resulting tree + violate the Red-Black Tree properties. Fixup tree... */ + + p = ERTS_RBT_GET_PARENT(x); + while (ERTS_RBT_IS_BLACK(x) && p) { + ERTS_RBT_T *r, *l; + + /* + * x has an "extra black" which we move up the tree + * until we reach the root or until we can get rid of it. + * + * y is the sibbling of x, and p is their parent + */ + + if (x == ERTS_RBT_GET_LEFT(p)) { + y = ERTS_RBT_GET_RIGHT(p); + + ERTS_RBT_ASSERT(y); + + if (ERTS_RBT_IS_RED(y)) { + ERTS_RBT_ASSERT(ERTS_RBT_GET_RIGHT(y)); + ERTS_RBT_ASSERT(ERTS_RBT_GET_LEFT(y)); + + ERTS_RBT_SET_BLACK(y); + + ERTS_RBT_ASSERT(ERTS_RBT_IS_BLACK(p)); + + ERTS_RBT_SET_RED(p); + ERTS_RBT_FUNC__(left_rotate__)(root, p); + p = ERTS_RBT_GET_PARENT(x); + y = ERTS_RBT_GET_RIGHT(p); + } + + ERTS_RBT_ASSERT(y); + ERTS_RBT_ASSERT(ERTS_RBT_IS_BLACK(y)); + + l = ERTS_RBT_GET_LEFT(y); + r = ERTS_RBT_GET_RIGHT(y); + if ((!l || ERTS_RBT_IS_BLACK(l)) + && (!r || ERTS_RBT_IS_BLACK(r))) { + ERTS_RBT_SET_RED(y); + x = p; + p = ERTS_RBT_GET_PARENT(x); + } + else { + if (!r || ERTS_RBT_IS_BLACK(r)) { + ERTS_RBT_SET_BLACK(l); + ERTS_RBT_SET_RED(y); + ERTS_RBT_FUNC__(right_rotate__)(root, y); + p = ERTS_RBT_GET_PARENT(x); + y = ERTS_RBT_GET_RIGHT(p); + } + + ERTS_RBT_ASSERT(y); + + if (p && ERTS_RBT_IS_RED(p)) { + + ERTS_RBT_SET_BLACK(p); + ERTS_RBT_SET_RED(y); + } + + ERTS_RBT_ASSERT(ERTS_RBT_GET_RIGHT(y)); + + ERTS_RBT_SET_BLACK(ERTS_RBT_GET_RIGHT(y)); + ERTS_RBT_FUNC__(left_rotate__)(root, p); + x = *root; + break; + } + } + else { + ERTS_RBT_ASSERT(x == ERTS_RBT_GET_RIGHT(p)); + + y = ERTS_RBT_GET_LEFT(p); + + ERTS_RBT_ASSERT(y); + + if (ERTS_RBT_IS_RED(y)) { + ERTS_RBT_ASSERT(ERTS_RBT_GET_RIGHT(y)); + ERTS_RBT_ASSERT(ERTS_RBT_GET_LEFT(y)); + + ERTS_RBT_SET_BLACK(y); + ERTS_RBT_ASSERT(ERTS_RBT_IS_BLACK(p)); + ERTS_RBT_SET_RED(p); + ERTS_RBT_FUNC__(right_rotate__)(root, p); + + p = ERTS_RBT_GET_PARENT(x); + y = ERTS_RBT_GET_LEFT(p); + } + + ERTS_RBT_ASSERT(y); + ERTS_RBT_ASSERT(ERTS_RBT_IS_BLACK(y)); + + l = ERTS_RBT_GET_LEFT(y); + r = ERTS_RBT_GET_RIGHT(y); + + if ((!r || ERTS_RBT_IS_BLACK(r)) + && (!l || ERTS_RBT_IS_BLACK(l))) { + ERTS_RBT_SET_RED(y); + x = p; + p = ERTS_RBT_GET_PARENT(x); + } + else { + if (!l || ERTS_RBT_IS_BLACK(l)) { + ERTS_RBT_SET_BLACK(r); + ERTS_RBT_SET_RED(y); + ERTS_RBT_FUNC__(left_rotate__)(root, y); + + p = ERTS_RBT_GET_PARENT(x); + y = ERTS_RBT_GET_LEFT(p); + } + + ERTS_RBT_ASSERT(y); + + if (p && ERTS_RBT_IS_RED(p)) { + ERTS_RBT_SET_BLACK(p); + ERTS_RBT_SET_RED(y); + } + + ERTS_RBT_ASSERT(ERTS_RBT_GET_LEFT(y)); + + ERTS_RBT_SET_BLACK(ERTS_RBT_GET_LEFT(y)); + ERTS_RBT_FUNC__(right_rotate__)(root, p); + x = *root; + break; + } + } + } + + ERTS_RBT_SET_BLACK(x); + + x = &null_x; + p = ERTS_RBT_GET_PARENT(x); + + if (p) { + if (ERTS_RBT_GET_LEFT(p) == x) + ERTS_RBT_SET_LEFT(p, NULL); + else { + ERTS_RBT_ASSERT(ERTS_RBT_GET_RIGHT(p) == x); + ERTS_RBT_SET_RIGHT(p, NULL); + } + + ERTS_RBT_ASSERT(!ERTS_RBT_GET_LEFT(x)); + ERTS_RBT_ASSERT(!ERTS_RBT_GET_RIGHT(x)); + } + else if (*root == x) { + *root = NULL; + +#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT + ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT(x, NULL); +#endif + + ERTS_RBT_ASSERT(!ERTS_RBT_GET_LEFT(x)); + ERTS_RBT_ASSERT(!ERTS_RBT_GET_RIGHT(x)); + } + } + + ERTS_RBT_HDBG_CHECK_TREE__(*root); + +} + +#endif /* ERTS_RBT_WANT_DELETE */ + +#ifdef ERTS_RBT_NEED_INSERT__ + +static void +ERTS_RBT_FUNC__(insert_fixup__)(ERTS_RBT_T **root, ERTS_RBT_T *n) +{ + ERTS_RBT_T *x, *y; + + x = n; + + /* + * Rearrange the tree so that it satisfies the Red-Black Tree properties + */ + + ERTS_RBT_ASSERT(x != *root && ERTS_RBT_IS_RED(ERTS_RBT_GET_PARENT(x))); + do { + ERTS_RBT_T *p, *pp; + + /* + * x and its parent are both red. Move the red pair up the tree + * until we get to the root or until we can separate them. + */ + + p = ERTS_RBT_GET_PARENT(x); + pp = ERTS_RBT_GET_PARENT(p); + + ERTS_RBT_ASSERT(p && pp); + ERTS_RBT_ASSERT(ERTS_RBT_IS_RED(x)); + ERTS_RBT_ASSERT(ERTS_RBT_IS_BLACK(pp)); + + if (p == ERTS_RBT_GET_LEFT(pp)) { + y = ERTS_RBT_GET_RIGHT(pp); + if (y && ERTS_RBT_IS_RED(y)) { + ERTS_RBT_SET_BLACK(y); + ERTS_RBT_SET_BLACK(p); + ERTS_RBT_SET_RED(pp); + x = pp; + } + else { + + if (x == ERTS_RBT_GET_RIGHT(p)) { + x = p; + ERTS_RBT_FUNC__(left_rotate__)(root, x); + p = ERTS_RBT_GET_PARENT(x); + pp = ERTS_RBT_GET_PARENT(p); + + ERTS_RBT_ASSERT(p && pp); + } + + ERTS_RBT_ASSERT(x == ERTS_RBT_GET_LEFT(ERTS_RBT_GET_LEFT(pp))); + ERTS_RBT_ASSERT(ERTS_RBT_IS_RED(x)); + ERTS_RBT_ASSERT(ERTS_RBT_IS_RED(p)); + ERTS_RBT_ASSERT(ERTS_RBT_IS_BLACK(pp)); + ERTS_RBT_ASSERT(!y || ERTS_RBT_IS_BLACK(y)); + + + ERTS_RBT_SET_BLACK(p); + ERTS_RBT_SET_RED(pp); + ERTS_RBT_FUNC__(right_rotate__)(root, pp); + + + ERTS_RBT_ASSERT(ERTS_RBT_GET_LEFT(ERTS_RBT_GET_PARENT(x)) == x); + ERTS_RBT_ASSERT(ERTS_RBT_IS_RED(x)); + ERTS_RBT_ASSERT(ERTS_RBT_IS_RED( + ERTS_RBT_GET_RIGHT( + ERTS_RBT_GET_PARENT(x)))); + ERTS_RBT_ASSERT(!ERTS_RBT_GET_PARENT(x) + || ERTS_RBT_IS_BLACK(ERTS_RBT_GET_PARENT(x))); + break; + } + } + else { + ERTS_RBT_ASSERT(p == ERTS_RBT_GET_RIGHT(pp)); + + y = ERTS_RBT_GET_LEFT(pp); + if (y && ERTS_RBT_IS_RED(y)) { + ERTS_RBT_SET_BLACK(y); + ERTS_RBT_SET_BLACK(p); + ERTS_RBT_SET_RED(pp); + x = pp; + } + else { + + if (x == ERTS_RBT_GET_LEFT(p)) { + x = p; + ERTS_RBT_FUNC__(right_rotate__)(root, x); + p = ERTS_RBT_GET_PARENT(x); + pp = ERTS_RBT_GET_PARENT(p); + + ERTS_RBT_ASSERT(p && pp); + } + + ERTS_RBT_ASSERT(x == ERTS_RBT_GET_RIGHT(ERTS_RBT_GET_RIGHT(pp))); + ERTS_RBT_ASSERT(ERTS_RBT_IS_RED(x)); + ERTS_RBT_ASSERT(ERTS_RBT_IS_RED(p)); + ERTS_RBT_ASSERT(ERTS_RBT_IS_BLACK(pp)); + ERTS_RBT_ASSERT(!y || ERTS_RBT_IS_BLACK(y)); + + + ERTS_RBT_SET_BLACK(p); + ERTS_RBT_SET_RED(pp); + ERTS_RBT_FUNC__(left_rotate__)(root, pp); + + + ERTS_RBT_ASSERT(ERTS_RBT_GET_RIGHT(ERTS_RBT_GET_PARENT(x)) == x); + ERTS_RBT_ASSERT(ERTS_RBT_IS_RED(x)); + ERTS_RBT_ASSERT(ERTS_RBT_IS_RED( + ERTS_RBT_GET_LEFT( + ERTS_RBT_GET_PARENT(x)))); + ERTS_RBT_ASSERT(!ERTS_RBT_GET_PARENT(x) + || ERTS_RBT_IS_BLACK(ERTS_RBT_GET_PARENT(x))); + break; + } + } + } while (x != *root && ERTS_RBT_IS_RED(ERTS_RBT_GET_PARENT(x))); + + ERTS_RBT_SET_BLACK(*root); + +} + +static ERTS_INLINE ERTS_RBT_T * +ERTS_RBT_FUNC__(insert_aux__)(ERTS_RBT_T **root, ERTS_RBT_T *n, int lookup) +{ + ERTS_RBT_KEY_T kn = ERTS_RBT_GET_KEY(n); + + ERTS_RBT_HDBG_CHECK_TREE__(*root); + + ERTS_RBT_INIT_EMPTY_TNODE(n); + + if (!*root) { + ERTS_RBT_SET_BLACK(n); + *root = n; +#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT + ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT(NULL, n); +#endif + } + else { + ERTS_RBT_T *p, *x = *root; + + while (1) { + ERTS_RBT_KEY_T kx; + ERTS_RBT_T *c; + + kx = ERTS_RBT_GET_KEY(x); + + if (lookup && ERTS_RBT_IS_EQ(kn, kx)) { + + ERTS_RBT_HDBG_CHECK_TREE__(*root); + + return x; + } + + if (ERTS_RBT_IS_LT(kn, kx)) { + c = ERTS_RBT_GET_LEFT(x); + if (!c) { + ERTS_RBT_SET_PARENT(n, x); + ERTS_RBT_SET_LEFT(x, n); + p = x; + break; + } + } + else { + c = ERTS_RBT_GET_RIGHT(x); + if (!c) { + ERTS_RBT_SET_PARENT(n, x); + ERTS_RBT_SET_RIGHT(x, n); + p = x; + break; + } + } + + x = c; + } + + ERTS_RBT_ASSERT(p); + + ERTS_RBT_SET_RED(n); + if (ERTS_RBT_IS_RED(p)) + ERTS_RBT_FUNC__(insert_fixup__)(root, n); + } + + ERTS_RBT_HDBG_CHECK_TREE__(*root); + + return NULL; +} + +#endif /* ERTS_RBT_NEED_INSERT__ */ + +#ifdef ERTS_RBT_WANT_LOOKUP_INSERT + +static ERTS_RBT_API_INLINE__ ERTS_RBT_T * +ERTS_RBT_FUNC__(lookup_insert)(ERTS_RBT_T **root, ERTS_RBT_T *n) +{ + return ERTS_RBT_FUNC__(insert_aux__)(root, n, !0); +} + +#endif /* ERTS_RBT_WANT_LOOKUP_INSERT */ + +#ifdef ERTS_RBT_WANT_INSERT + +static ERTS_RBT_API_INLINE__ void +ERTS_RBT_FUNC__(insert)(ERTS_RBT_T **root, ERTS_RBT_T *n) +{ + (void) ERTS_RBT_FUNC__(insert_aux__)(root, n, 0); +} + +#endif /* ERTS_RBT_WANT_INSERT */ + +#ifdef ERTS_RBT_WANT_LOOKUP + +static ERTS_RBT_API_INLINE__ ERTS_RBT_T * +ERTS_RBT_FUNC__(lookup)(ERTS_RBT_T *root, ERTS_RBT_KEY_T key) +{ + ERTS_RBT_T *x = root; + + if (!x) + return NULL; + + while (1) { + ERTS_RBT_KEY_T kx = ERTS_RBT_GET_KEY(x); + ERTS_RBT_T *c; + + if (ERTS_RBT_IS_EQ(key, kx)) + return x; + + if (ERTS_RBT_IS_LT(key, kx)) { + c = ERTS_RBT_GET_LEFT(x); + if (!c) + return NULL; + } + else { + c = ERTS_RBT_GET_RIGHT(x); + if (!c) + return NULL; + } + + x = c; + } +} + +#endif /* ERTS_RBT_WANT_LOOKUP */ + +#ifdef ERTS_RBT_WANT_SMALLEST + +static ERTS_RBT_API_INLINE__ ERTS_RBT_T * +ERTS_RBT_FUNC__(smallest)(ERTS_RBT_T *root) +{ + ERTS_RBT_T *x = root; + + if (!x) + return NULL; + + while (1) { + ERTS_RBT_T *c = ERTS_RBT_GET_LEFT(x); + if (!c) + break; + x = c; + } + + return x; +} + +#endif /* ERTS_RBT_WANT_SMALLEST */ + +#ifdef ERTS_RBT_WANT_LARGEST + +static ERTS_RBT_API_INLINE__ ERTS_RBT_T * +ERTS_RBT_FUNC__(largest)(ERTS_RBT_T *root) +{ + ERTS_RBT_T *x = root; + + if (!x) + return NULL; + + while (1) { + ERTS_RBT_T *c = ERTS_RBT_GET_RIGHT(x); + if (!c) + break; + x = c; + } + + return x; +} + +#endif /* ERTS_RBT_WANT_LARGEST */ + +#ifdef ERTS_RBT_NEED_FOREACH_UNORDERED__ + +static ERTS_INLINE int +ERTS_RBT_FUNC__(foreach_unordered__)(ERTS_RBT_T **root, + int destroying, + void (*op)(ERTS_RBT_T *, void *), + void *arg, + int yielding, + ERTS_RBT_YIELD_STATE_T__ *ystate, + Sint ylimit) +{ + ERTS_RBT_T *c, *p, *x; + + ERTS_RBT_ASSERT(!yielding || ystate); + + if (yielding && ystate->x) { + x = ystate->x; + ERTS_RBT_ASSERT(ystate->up); + goto restart_up; + } + else { + x = *root; + if (!x) + return 0; + if (destroying) + *root = NULL; + } + + while (1) { + + while (1) { + + while (1) { + c = ERTS_RBT_GET_LEFT(x); + if (!c) + break; + x = c; + } + + c = ERTS_RBT_GET_RIGHT(x); + if (!c) + break; + x = c; + } + + while (1) { +#ifdef ERTS_RBT_DEBUG + int cdir; +#endif + if (yielding && ylimit-- <= 0) { + ystate->x = x; + ystate->up = 1; + return 1; + } + + restart_up: + + p = ERTS_RBT_GET_PARENT(x); + +#ifdef ERTS_RBT_DEBUG + ERTS_RBT_ASSERT(!destroying || !ERTS_RBT_GET_LEFT(x)); + ERTS_RBT_ASSERT(!destroying || !ERTS_RBT_GET_RIGHT(x)); + + if (p) { + if (x == ERTS_RBT_GET_LEFT(p)) { + cdir = -1; + if (destroying) + ERTS_RBT_SET_LEFT(p, NULL); + } + else { + ERTS_RBT_ASSERT(x == ERTS_RBT_GET_RIGHT(p)); + cdir = 1; + if (destroying) + ERTS_RBT_SET_RIGHT(p, NULL); + } + } +#endif + + (*op)(x, arg); + + if (!p) { + if (yielding) { + ystate->x = NULL; + ystate->up = 0; + } + return 0; /* Done */ + } + + c = ERTS_RBT_GET_RIGHT(p); + if (c && c != x) { + ERTS_RBT_ASSERT(cdir < 0); + + /* Go down tree of x's sibling... */ + x = c; + break; + } + + x = p; + } + } +} + +#endif /* ERTS_RBT_NEED_FOREACH_UNORDERED__ */ + +#ifdef ERTS_RBT_NEED_FOREACH_ORDERED__ + +static ERTS_INLINE int +ERTS_RBT_FUNC__(foreach_ordered__)(ERTS_RBT_T **root, + int from_small, + int destroying, + void (*op)(ERTS_RBT_T *, void *), + void (*destroy)(ERTS_RBT_T *, void *), + void *arg, + int yielding, + ERTS_RBT_YIELD_STATE_T__ *ystate, + Sint ylimit) +{ + ERTS_RBT_T *c, *p, *x; + + ERTS_RBT_ASSERT(!yielding || ystate); + ERTS_RBT_ASSERT(!destroying || destroy); + + if (yielding && ystate->x) { + x = ystate->x; + if (ystate->up) + goto restart_up; + else + goto restart_down; + } + else { + x = *root; + if (!x) + return 0; + if (destroying) + *root = NULL; + } + + while (1) { + + while (1) { + + while (1) { + c = from_small ? ERTS_RBT_GET_LEFT(x) : ERTS_RBT_GET_RIGHT(x); + if (!c) + break; + x = c; + } + + (*op)(x, arg); + + if (yielding && --ylimit <= 0) { + ystate->x = x; + ystate->up = 0; + return 1; + } + + restart_down: + + c = from_small ? ERTS_RBT_GET_RIGHT(x) : ERTS_RBT_GET_LEFT(x); + if (!c) + break; + x = c; + } + + while (1) { + p = ERTS_RBT_GET_PARENT(x); + + if (p) { + + c = from_small ? ERTS_RBT_GET_RIGHT(p) : ERTS_RBT_GET_LEFT(p); + if (!c || c != x) { + ERTS_RBT_ASSERT((from_small + ? ERTS_RBT_GET_LEFT(p) + : ERTS_RBT_GET_RIGHT(p)) == x); + + (*op)(p, arg); + + if (yielding && --ylimit <= 0) { + ystate->x = x; + ystate->up = 1; + return 1; + restart_up: + p = ERTS_RBT_GET_PARENT(x); + } + } + + if (c && c != x) { + ERTS_RBT_ASSERT((from_small + ? ERTS_RBT_GET_LEFT(p) + : ERTS_RBT_GET_RIGHT(p)) == x); + + /* Go down tree of x's sibling... */ + x = c; + break; + } + } + + if (destroying) { + +#ifdef ERTS_RBT_DEBUG + ERTS_RBT_ASSERT(!ERTS_RBT_GET_LEFT(x) + && !ERTS_RBT_GET_RIGHT(x)); + + if (p) { + if (x == ERTS_RBT_GET_LEFT(p)) + ERTS_RBT_SET_LEFT(p, NULL); + else { + ERTS_RBT_ASSERT(x == ERTS_RBT_GET_RIGHT(p)); + ERTS_RBT_SET_RIGHT(p, NULL); + } + } +#endif + + (*destroy)(x, arg); + } + + if (!p) { + if (yielding) { + ystate->x = NULL; + ystate->up = 0; + } + return 1; /* Done */ + } + x = p; + } + } +} + +#endif /* ERTS_RBT_NEED_FOREACH_ORDERED__ */ + +#ifdef ERTS_RBT_WANT_FOREACH + +static ERTS_RBT_API_INLINE__ void +ERTS_RBT_FUNC__(foreach)(ERTS_RBT_T *root, + void (*op)(ERTS_RBT_T *, void *), + void *arg) +{ + (void) ERTS_RBT_FUNC__(foreach_unordered__)(&root, 0, op, arg, + 0, NULL, 0); +} + +#endif /* ERTS_RBT_WANT_FOREACH */ + +#ifdef ERTS_RBT_WANT_FOREACH_SMALL + +static ERTS_RBT_API_INLINE__ void +ERTS_RBT_FUNC__(foreach_small)(ERTS_RBT_T *root, + void (*op)(ERTS_RBT_T *, void *), + void *arg) +{ + (void) ERTS_RBT_FUNC__(foreach_ordered__)(&root, 1, 0, + op, NULL, arg, + 0, NULL, 0); +} + +#endif /* ERTS_RBT_WANT_FOREACH_SMALL */ + +#ifdef ERTS_RBT_WANT_FOREACH_LARGE + +static ERTS_RBT_API_INLINE__ void +ERTS_RBT_FUNC__(foreach_large)(ERTS_RBT_T *root, + void (*op)(ERTS_RBT_T *, void *), + void *arg) +{ + (void) ERTS_RBT_FUNC__(foreach_ordered__)(&root, 0, 0, + op, NULL, arg, + 0, NULL, 0); +} + +#endif /* ERTS_RBT_WANT_FOREACH_LARGE */ + +#ifdef ERTS_RBT_WANT_FOREACH_YIELDING + +static ERTS_RBT_API_INLINE__ void +ERTS_RBT_FUNC__(foreach_yielding)(ERTS_RBT_T *root, + void (*op)(ERTS_RBT_T *, void *), + void *arg, + ERTS_RBT_YIELD_STATE_T__ *ystate, + Sint ylimit) +{ + (void) ERTS_RBT_FUNC__(foreach_unordered__)(*root, 0, op, arg, + 1, ystate, ylimit); +} + +#endif /* ERTS_RBT_WANT_FOREACH_YIELDING */ + +#ifdef ERTS_RBT_WANT_FOREACH_SMALL_YIELDING + +static ERTS_RBT_API_INLINE__ int +ERTS_RBT_FUNC__(foreach_small_yielding)(ERTS_RBT_T *root, + void (*op)(ERTS_RBT_T *, void *), + void *arg, + ERTS_RBT_YIELD_STATE_T__ *ystate, + Sint ylimit) +{ + return ERTS_RBT_FUNC__(foreach_ordered__)(&root, 1, 0, + op, NULL, arg, + 1, ystate, ylimit); +} + +#endif /* ERTS_RBT_WANT_FOREACH_SMALL_YIELDING */ + +#ifdef ERTS_RBT_WANT_FOREACH_LARGE_YIELDING + +static ERTS_RBT_API_INLINE__ int +ERTS_RBT_FUNC__(foreach_large_yielding)(ERTS_RBT_T *root, + void (*op)(ERTS_RBT_T *, void *), + void *arg, + ERTS_RBT_YIELD_STATE_T__ *ystate, + Sint ylimit) +{ + return ERTS_RBT_FUNC__(foreach_ordered__)(&root, 0, 0, + op, NULL, arg, + 1, ystate, ylimit); +} + +#endif /* ERTS_RBT_WANT_FOREACH_LARGE_YIELDING */ + +#ifdef ERTS_RBT_WANT_FOREACH_DESTROY + +static ERTS_RBT_API_INLINE__ void +ERTS_RBT_FUNC__(foreach_destroy)(ERTS_RBT_T **root, + void (*op)(ERTS_RBT_T *, void *), + void *arg) +{ + (void) ERTS_RBT_FUNC__(foreach_unordered__)(root, 1, op, arg, + 0, NULL, 0); +} + +#endif /* ERTS_RBT_WANT_FOREACH_DESTROY */ + +#ifdef ERTS_RBT_WANT_FOREACH_SMALL_DESTROY + +static ERTS_RBT_API_INLINE__ void +ERTS_RBT_FUNC__(foreach_small_destroy)(ERTS_RBT_T **root, + void (*op)(ERTS_RBT_T *, void *), + void (*destr)(ERTS_RBT_T *, void *), + void *arg) +{ + (void) ERTS_RBT_FUNC__(foreach_ordered__)(root, 1, 1, + op, destr, arg, + 0, NULL, 0); +} + +#endif /* ERTS_RBT_WANT_FOREACH_SMALL_DESTROY */ + +#ifdef ERTS_RBT_WANT_FOREACH_LARGE_DESTROY + +static ERTS_RBT_API_INLINE__ void +ERTS_RBT_FUNC__(foreach_large_destroy)(ERTS_RBT_T **root, + void (*op)(ERTS_RBT_T *, void *), + void (*destr)(ERTS_RBT_T *, void *), + void *arg) +{ + (void) ERTS_RBT_FUNC__(foreach_ordered__)(root, 0, 1, + op, destr, arg, + 0, NULL, 0); +} + +#endif /* ERTS_RBT_WANT_FOREACH_LARGE_DESTROY */ + +#ifdef ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING + +static ERTS_RBT_API_INLINE__ int +ERTS_RBT_FUNC__(foreach_destroy_yielding)(ERTS_RBT_T **root, + void (*op)(ERTS_RBT_T *, void *), + void *arg, + ERTS_RBT_YIELD_STATE_T__ *ystate, + Sint ylimit) +{ + return ERTS_RBT_FUNC__(foreach_unordered__)(root, 1, op, arg, + 1, ystate, ylimit); +} + +#endif /* ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING */ + +#ifdef ERTS_RBT_WANT_FOREACH_SMALL_DESTROY_YIELDING + +static ERTS_RBT_API_INLINE__ int +ERTS_RBT_FUNC__(foreach_small_destroy_yielding)(ERTS_RBT_T **root, + void (*op)(ERTS_RBT_T *, void *), + void (*destr)(ERTS_RBT_T *, void *), + void *arg, + ERTS_RBT_YIELD_STATE_T__ *ystate, + Sint ylimit) +{ + return ERTS_RBT_FUNC__(foreach_ordered__)(root, 1, 1, + op, destr, arg, + 1, ystate, ylimit); +} + +#endif /* ERTS_RBT_WANT_FOREACH_SMALL_DESTROY_YIELDING */ + +#ifdef ERTS_RBT_WANT_FOREACH_LARGE_DESTROY_YIELDING + +static ERTS_RBT_API_INLINE__ int +ERTS_RBT_FUNC__(foreach_large_destroy_yielding)(ERTS_RBT_T **root, + void (*op)(ERTS_RBT_T *, void *), + void (*destr)(ERTS_RBT_T *, void *), + void *arg, + ERTS_RBT_YIELD_STATE_T__ *ystate, + Sint ylimit) +{ + return ERTS_RBT_FUNC__(foreach_ordered__)(root, 0, 1, + op, destr, arg, + 1, ystate, ylimit); +} + +#endif /* ERTS_RBT_WANT_FOREACH_LARGE_DESTROY_YIELDING */ + +#ifdef ERTS_RBT_WANT_DEBUG_PRINT + +static void +ERTS_RBT_FUNC__(debug_print)(FILE *filep, ERTS_RBT_T *x, int indent, + void (*print_node)(ERTS_RBT_T *)) +{ + if (x) { + ERTS_RBT_FUNC__(debug_print)(filep, ERTS_RBT_GET_RIGHT(x), + indent+2, print_node); + erts_fprintf(filep, + "%*s[%s:%p:", + indent, "", + ERTS_RBT_IS_BLACK(x) ? "Black" : "Red", + x); + (*print_node)(x); + erts_fprintf(filep, "]\n"); + ERTS_RBT_FUNC__(debug_print)(filep, ERTS_RBT_GET_LEFT(x), + indent+2, print_node); + } +} + +#endif /* ERTS_RBT_WANT_DEBUG_PRINT */ + +#ifdef ERTS_RBT_NEED_HDBG_CHECK_TREE__ + +static void +ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root) +{ + int black_depth = -1, no_black = 0; + ERTS_RBT_T *c, *p, *x = root; + ERTS_RBT_KEY_T kx; + ERTS_RBT_KEY_T kc; + + if (!x) + return; + + ERTS_RBT_ASSERT(!ERTS_RBT_GET_PARENT(x)); + + while (1) { + + while (1) { + + while (1) { + + if (ERTS_RBT_IS_BLACK(x)) + no_black++; + else { + c = ERTS_RBT_GET_RIGHT(x); + ERTS_RBT_ASSERT(!c || ERTS_RBT_IS_BLACK(c)); + c = ERTS_RBT_GET_LEFT(x); + ERTS_RBT_ASSERT(!c || ERTS_RBT_IS_BLACK(c)); + } + + c = ERTS_RBT_GET_LEFT(x); + if (!c) + break; + + ERTS_RBT_ASSERT(x == ERTS_RBT_GET_PARENT(c)); + + kx = ERTS_RBT_GET_KEY(x); + kc = ERTS_RBT_GET_KEY(c); + + ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kc, kx) + || ERTS_RBT_IS_EQ(kc, kx)); + + x = c; + } + + c = ERTS_RBT_GET_RIGHT(x); + if (!c) { + if (black_depth < 0) + black_depth = no_black; + ERTS_RBT_ASSERT(black_depth == no_black); + break; + } + + ERTS_RBT_ASSERT(x == ERTS_RBT_GET_PARENT(c)); + + kx = ERTS_RBT_GET_KEY(x); + kc = ERTS_RBT_GET_KEY(c); + + ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kx, kc) + || ERTS_RBT_IS_EQ(kx, kc)); + x = c; + } + + while (1) { + p = ERTS_RBT_GET_PARENT(x); + + if (ERTS_RBT_IS_BLACK(x)) + no_black--; + + if (p) { + + ERTS_RBT_ASSERT(x == ERTS_RBT_GET_LEFT(p) + || x == ERTS_RBT_GET_RIGHT(p)); + + c = ERTS_RBT_GET_RIGHT(p); + if (c && c != x) { + ERTS_RBT_ASSERT(ERTS_RBT_GET_LEFT(p) == x); + + kx = ERTS_RBT_GET_KEY(x); + kc = ERTS_RBT_GET_KEY(c); + + ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kx, kc) + || ERTS_RBT_IS_EQ(kx, kc)); + /* Go down tree of x's sibling... */ + x = c; + break; + } + } + + if (!p) { + ERTS_RBT_ASSERT(root == x); + ERTS_RBT_ASSERT(no_black == 0); + return; /* Done */ + } + + x = p; + } + } +} + +#undef ERTS_RBT_PRINT_TREE__ + +#endif /* ERTS_RBT_NEED_HDBG_CHECK_TREE__ */ + +#undef ERTS_RBT_ASSERT +#undef ERTS_RBT_DEBUG +#undef ERTS_RBT_API_INLINE__ +#undef ERTS_RBT_YIELD_STATE_T__ +#undef ERTS_RBT_NEED_REPLACE__ +#undef ERTS_RBT_NEED_INSERT__ +#undef ERTS_RBT_NEED_ROTATE__ +#undef ERTS_RBT_NEED_FOREACH_UNORDERED__ +#undef ERTS_RBT_NEED_FOREACH_ORDERED__ +#undef ERTS_RBT_NEED_HDBG_CHECK_TREE__ +#undef ERTS_RBT_HDBG_CHECK_TREE__ + +#ifdef ERTS_RBT_UNDEF +# undef ERTS_RBT_PREFIX +# undef ERTS_RBT_T +# undef ERTS_RBT_KEY_T +# undef ERTS_RBT_FLAGS_T +# undef ERTS_RBT_INIT_EMPTY_TNODE +# undef ERTS_RBT_IS_RED +# undef ERTS_RBT_SET_RED +# undef ERTS_RBT_IS_BLACK +# undef ERTS_RBT_SET_BLACK +# undef ERTS_RBT_GET_FLAGS +# undef ERTS_RBT_SET_FLAGS +# undef ERTS_RBT_GET_PARENT +# undef ERTS_RBT_SET_PARENT +# undef ERTS_RBT_GET_RIGHT +# undef ERTS_RBT_SET_RIGHT +# undef ERTS_RBT_GET_LEFT +# undef ERTS_RBT_SET_LEFT +# undef ERTS_RBT_GET_KEY +# undef ERTS_RBT_IS_LT +# undef ERTS_RBT_IS_EQ +# undef ERTS_RBT_UNDEF +# undef ERTS_RBT_NO_API_INLINE +# undef ERTS_RBT_UPDATE_ATTACHED_DATA_ROTATE +# undef ERTS_RBT_UPDATE_ATTACHED_DATA_DMOD +# undef ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT +# undef ERTS_RBT_WANT_DELETE +# undef ERTS_RBT_WANT_INSERT +# undef ERTS_RBT_WANT_LOOKUP_INSERT +# undef ERTS_RBT_WANT_REPLACE +# undef ERTS_RBT_WANT_LOOKUP +# undef ERTS_RBT_WANT_SMALLEST +# undef ERTS_RBT_WANT_LARGEST +# undef ERTS_RBT_WANT_FOREACH +# undef ERTS_RBT_WANT_FOREACH_DESTROY +# undef ERTS_RBT_WANT_FOREACH_YIELDING +# undef ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING +# undef ERTS_RBT_WANT_FOREACH_SMALL +# undef ERTS_RBT_WANT_FOREACH_LARGE +# undef ERTS_RBT_WANT_FOREACH_SMALL_DESTROY +# undef ERTS_RBT_WANT_FOREACH_LARGE_DESTROY +# undef ERTS_RBT_WANT_FOREACH_SMALL_YIELDING +# undef ERTS_RBT_WANT_FOREACH_LARGE_YIELDING +# undef ERTS_RBT_WANT_FOREACH_SMALL_DESTROY_YIELDING +# undef ERTS_RBT_WANT_FOREACH_LARGE_DESTROY_YIELDING +# undef ERTS_RBT_WANT_DEBUG_PRINT +#endif -- cgit v1.2.3 From dc177967363d16c155042dbaa2c911f59f0efd55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 May 2015 19:29:34 +0200 Subject: erts: ETS ordered_set cannot use it's optimization with Maps The optimization cannot be used due to that the pattern cannot be ordered. --- erts/emulator/beam/erl_db_tree.c | 2 +- erts/emulator/beam/erl_db_util.c | 31 +++++++++++++++++++++++++++++++ erts/emulator/beam/erl_db_util.h | 1 + 3 files changed, 33 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index d90af46659..c7bccc78c3 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -2716,7 +2716,7 @@ static int key_given(DbTableTree *tb, Eterm pattern, TreeDbTerm **ret, *ret = this; return 1; } else if (partly_bound != NULL && key != am_Underscore && - db_is_variable(key) < 0) + db_is_variable(key) < 0 && !db_has_map(key)) *partly_bound = key; return 0; diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 0fb1c397c9..be988f0621 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -3347,6 +3347,37 @@ int db_is_variable(Eterm obj) return N; } +/* check if node is (or contains) a map + * return 1 if node contains a map + * return 0 otherwise + */ + +int db_has_map(Eterm node) { + DECLARE_ESTACK(s); + + ESTACK_PUSH(s,node); + while (!ESTACK_ISEMPTY(s)) { + node = ESTACK_POP(s); + if (is_list(node)) { + while (is_list(node)) { + ESTACK_PUSH(s,CAR(list_val(node))); + node = CDR(list_val(node)); + } + ESTACK_PUSH(s,node); /* Non wellformed list or [] */ + } else if (is_tuple(node)) { + Eterm *tuple = tuple_val(node); + int arity = arityval(*tuple); + while(arity--) { + ESTACK_PUSH(s,*(++tuple)); + } + } else if is_map(node) { + DESTROY_ESTACK(s); + return 1; + } + } + DESTROY_ESTACK(s); + return 0; +} /* check if obj is (or contains) a variable */ /* return 1 if obj contains a variable or underscore */ diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index ca206c7f58..b2d5a306cb 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -342,6 +342,7 @@ void* db_store_term(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj); void* db_store_term_comp(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj); Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p, DbTerm* obj, Uint pos, Eterm** hpp, Uint extra); +int db_has_map(Eterm obj); int db_has_variable(Eterm obj); int db_is_variable(Eterm obj); void db_do_update_element(DbUpdateHandle* handle, -- cgit v1.2.3 From 757d493cff18d0b52aeb1e32d01886551e16fd5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 May 2015 10:58:10 +0200 Subject: erts: Fix copy shallow for large Maps There is no need to take special care of Maps at all since header_arity(hdr) will take care of the normal case via its Map handling. --- erts/emulator/beam/copy.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 4d12dae787..850606dd86 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -608,11 +608,6 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) erts_refc_inc(&funp->fe->refc, 2); } goto off_heap_common; - - case MAP_SUBTAG: - *hp++ = *tp++; - sz--; - break; case EXTERNAL_PID_SUBTAG: case EXTERNAL_PORT_SUBTAG: case EXTERNAL_REF_SUBTAG: @@ -648,7 +643,6 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } } *hpp = hp; - return res; } -- cgit v1.2.3 From beb883ed443dfeb41f239363f36c511dd43c7eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 May 2015 11:58:23 +0200 Subject: erts: Fix ETS db_has_variable check for large Maps --- erts/emulator/beam/erl_db_util.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index be988f0621..a75267ca97 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -3411,6 +3411,11 @@ int db_has_variable(Eterm node) { while (size--) { ESTACK_PUSH(s, *(values++)); } + } else if (is_map(node)) { /* other map-nodes or map-heads */ + Eterm *ptr = hashmap_val(node); + int i = hashmap_bitcount(MAP_HEADER_VAL(*ptr)); + ptr += MAP_HEADER_ARITY(*ptr); + while(i--) { ESTACK_PUSH(s, *++ptr); } } break; case TAG_PRIMARY_IMMED1: -- cgit v1.2.3 From 8b85c571f7c31bda6695c8179ef286c30862ce54 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Fri, 17 Apr 2015 13:21:29 +0200 Subject: Remove 'imports' key from spec of get_module_info() --- erts/preloaded/src/erlang.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index fd11c101bc..629702fc1c 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1931,7 +1931,7 @@ element(_N, _Tuple) -> %% Not documented -spec erlang:get_module_info(Module, Item) -> ModuleInfo when Module :: atom(), - Item :: module | imports | exports | functions | attributes | compile | native_addresses | md5, + Item :: module | exports | functions | attributes | compile | native_addresses | md5, ModuleInfo :: atom() | [] | [{atom(), arity()}] | [{atom(), term()}] | [{atom(), arity(), integer()}]. get_module_info(_Module, _Item) -> erlang:nif_error(undefined). -- cgit v1.2.3 From 39ce03094edb3bc53ad671636355be53564a222d Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 14 Apr 2015 15:16:25 +0200 Subject: Gracefully handle empty md5 field in module_info --- erts/emulator/beam/beam_load.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 282aa71109..29d4dce3e2 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -5695,7 +5695,11 @@ md5_of_module(Process* p, /* Process whose heap to use. */ return THE_NON_VALUE; } code = modp->curr.code; - res = new_binary(p, (byte *) code[MI_MD5_PTR], MD5_SIZE); + if (code[MI_MD5_PTR] != 0) { + res = new_binary(p, (byte *) code[MI_MD5_PTR], MD5_SIZE); + } else { + res = am_undefined; + } return res; } -- cgit v1.2.3 From fc1029c74093fde14241ca664c9341fcc8be5fd3 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Thu, 16 Apr 2015 14:30:55 +0200 Subject: Add module_info entry for native code --- erts/emulator/beam/beam_bif_load.c | 29 ++--------------- erts/emulator/beam/beam_load.c | 53 ++++++++++++++++++++++++++++++++ erts/emulator/beam/beam_load.h | 2 +- erts/emulator/test/module_info_SUITE.erl | 3 ++ 4 files changed, 59 insertions(+), 28 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index df1983a83d..8c32fc7854 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -39,12 +39,9 @@ static void set_default_trace_pattern(Eterm module); static Eterm check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp); 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); - - BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1) { Module* modp; @@ -59,8 +56,8 @@ BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1) 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))) ? + res = (erts_is_module_native(modp->curr.code) || + erts_is_module_native(modp->old.code)) ? am_true : am_false; erts_runlock_old_code(code_ix); return res; @@ -1106,25 +1103,3 @@ beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks, Eterm module) } return NIL; } - -static int -is_native(BeamInstr* code) -{ - Uint i, num_functions = code[MI_NUM_FUNCTIONS]; - - /* Check NativeAdress of first real function in module - */ - for (i=0; icurr.code)) { + result = am_true; + } +#endif + return result; +} + +int +erts_is_module_native(BeamInstr* code) +{ + Uint i, num_functions; + + /* Check NativeAdress of first real function in module */ + if (code != NULL) { + num_functions = code[MI_NUM_FUNCTIONS]; + for (i=0; i ok. %% Test that the list of exported functions from this module is correct. +%% Verify that module_info(native) works. native(Config) when is_list(Config) -> ?line All = all_functions(), ?line case ?MODULE:module_info(native_addresses) of [] -> + ?line false = ?MODULE:module_info(native), {comment,"no native functions"}; L -> + ?line true = ?MODULE:module_info(native), %% Verify that all functions have unique addresses. ?line S0 = sofs:set(L, [{name,arity,addr}]), ?line S1 = sofs:projection({external,fun ?MODULE:native_proj/1}, S0), -- cgit v1.2.3 From e47490f83e561a45cee9e8f72f1e1f91f19c60b7 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Thu, 16 Apr 2015 11:28:22 +0200 Subject: Set module_info md5 for native modules properly Use the md5 of the native code chunk instead of the Beam code md5. --- erts/emulator/beam/beam_load.c | 20 +++++++++++++++++--- erts/emulator/test/code_SUITE.erl | 22 ++++++++++++---------- 2 files changed, 29 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index e2bd30bd6b..0d40201934 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -6225,6 +6225,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) LoaderState* stp; BeamInstr Funcs; BeamInstr Patchlist; + Eterm MD5Bin; Eterm* tp; BeamInstr* code = NULL; BeamInstr* ptrs; @@ -6253,12 +6254,15 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) goto error; } tp = tuple_val(Info); - if (tp[0] != make_arityval(2)) { + if (tp[0] != make_arityval(3)) { goto error; } Funcs = tp[1]; - Patchlist = tp[2]; - + Patchlist = tp[2]; + MD5Bin = tp[3]; + if (is_not_binary(MD5Bin) || (binary_size(MD5Bin) != MD5_SIZE)) { + goto error; + } if ((n = erts_list_length(Funcs)) < 0) { goto error; } @@ -6308,6 +6312,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) code_size = ((WORDS_PER_FUNCTION+1)*n + MI_FUNCTIONS + 2) * sizeof(BeamInstr); code_size += stp->chunks[ATTR_CHUNK].size; code_size += stp->chunks[COMPILE_CHUNK].size; + code_size += MD5_SIZE; code = erts_alloc_fnf(ERTS_ALC_T_CODE, code_size); if (!code) { goto error; @@ -6414,6 +6419,15 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) if (info == NULL) { goto error; } + { + byte *tmp = NULL; + byte *md5 = NULL; + if ((md5 = erts_get_aligned_binary_bytes(MD5Bin, &tmp)) != NULL) { + sys_memcpy(info, md5, MD5_SIZE); + code[MI_MD5_PTR] = (BeamInstr) info; + } + erts_free_aligned_binary_bytes(tmp); + } /* * Insert the module in the module table. diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index b0408cabe1..df7c8ed1d1 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -389,61 +389,63 @@ module_md5_ok(Code) -> make_stub(Config) when is_list(Config) -> catch erlang:purge_module(my_code_test), + MD5 = erlang:md5(<<>>), ?line Data = ?config(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), ?line {ok,my_code_test,Code} = compile:file(File, [binary]), - ?line my_code_test = code:make_stub_module(my_code_test, Code, {[],[]}), + ?line my_code_test = code:make_stub_module(my_code_test, Code, {[],[],MD5}), ?line true = erlang:delete_module(my_code_test), ?line true = erlang:purge_module(my_code_test), ?line my_code_test = code:make_stub_module(my_code_test, make_unaligned_sub_binary(Code), - {[],[]}), + {[],[],MD5}), ?line true = erlang:delete_module(my_code_test), ?line true = erlang:purge_module(my_code_test), ?line my_code_test = code:make_stub_module(my_code_test, zlib:gzip(Code), - {[],[]}), + {[],[],MD5}), ?line true = erlang:delete_module(my_code_test), ?line true = erlang:purge_module(my_code_test), %% Should fail. ?line {'EXIT',{badarg,_}} = - (catch code:make_stub_module(my_code_test, <<"bad">>, {[],[]})), + (catch code:make_stub_module(my_code_test, <<"bad">>, {[],[],MD5})), ?line {'EXIT',{badarg,_}} = (catch code:make_stub_module(my_code_test, bit_sized_binary(Code), - {[],[]})), + {[],[],MD5})), ?line {'EXIT',{badarg,_}} = (catch code:make_stub_module(my_code_test_with_wrong_name, - Code, {[],[]})), + Code, {[],[],MD5})), ok. make_stub_many_funs(Config) when is_list(Config) -> catch erlang:purge_module(many_funs), + MD5 = erlang:md5(<<>>), ?line Data = ?config(data_dir, Config), ?line File = filename:join(Data, "many_funs"), ?line {ok,many_funs,Code} = compile:file(File, [binary]), - ?line many_funs = code:make_stub_module(many_funs, Code, {[],[]}), + ?line many_funs = code:make_stub_module(many_funs, Code, {[],[],MD5}), ?line true = erlang:delete_module(many_funs), ?line true = erlang:purge_module(many_funs), ?line many_funs = code:make_stub_module(many_funs, make_unaligned_sub_binary(Code), - {[],[]}), + {[],[],MD5}), ?line true = erlang:delete_module(many_funs), ?line true = erlang:purge_module(many_funs), %% Should fail. ?line {'EXIT',{badarg,_}} = - (catch code:make_stub_module(many_funs, <<"bad">>, {[],[]})), + (catch code:make_stub_module(many_funs, <<"bad">>, {[],[],MD5})), ?line {'EXIT',{badarg,_}} = (catch code:make_stub_module(many_funs, bit_sized_binary(Code), - {[],[]})), + {[],[],MD5})), ok. constant_pools(Config) when is_list(Config) -> -- cgit v1.2.3 From 5f1ce6aefcc860906506ced26fe666faf3e82235 Mon Sep 17 00:00:00 2001 From: Nate Bartley Date: Thu, 7 May 2015 10:32:43 -0700 Subject: Fixing typo --- erts/doc/src/init.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index 09b5493341..c5a1a92b92 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -248,7 +248,7 @@ evaluation), Erlang stops with an error message. Here is an example that seeds the random number generator:

-% erl -eval '{X,Y,Z}' = now(), random:seed(X,Y,Z).'
+% erl -eval '{X,Y,Z} = now(), random:seed(X,Y,Z).'

This example uses Erlang as a hexadecimal calculator:

 % erl -noshell -eval 'R = 16#1F+16#A0, io:format("~.16B~n", [R])' \\
-- 
cgit v1.2.3


From b51bbf8b8a06e7bf18e0b837f50dad4a66a0fca7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Fri, 8 May 2015 16:13:09 +0200
Subject: erts: Make hashmap_get halfword safe

---
 erts/emulator/beam/erl_db_util.c | 2 +-
 erts/emulator/beam/erl_map.c     | 7 +++----
 2 files changed, 4 insertions(+), 5 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index a75267ca97..c6c3c55a7e 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -2039,7 +2039,7 @@ restart:
             break;
         case matchKey:
             t = (Eterm) *pc++;
-            tp = erts_maps_get_rel(t, make_flatmap_rel(ep, base), base);
+            tp = erts_maps_get_rel(t, make_boxed_rel(ep, base), base);
             if (!tp) {
                 FAIL();
             }
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index bb2a2bcdf9..57f06c369c 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -1884,7 +1884,7 @@ erts_hashmap_get(Uint32 hx, Eterm key, Eterm node)
     UseTmpHeapNoproc(2);
 
     ASSERT(is_boxed(node));
-    ptr = boxed_val(node);
+    ptr = boxed_val_rel(node, map_base);
     hdr = *ptr;
     ASSERT(is_header(hdr));
     ASSERT(is_hashmap_header_head(hdr));
@@ -1905,8 +1905,7 @@ erts_hashmap_get(Uint32 hx, Eterm key, Eterm node)
         node  = ptr[ix+1];
 
         if (is_list(node)) { /* LEAF NODE [K|V] */
-            ptr = list_val(node);
-
+            ptr = list_val_rel(node,map_base);
             res = eq_rel(CAR(ptr), map_base, key, NULL) ? &(CDR(ptr)) : NULL;
             break;
         }
@@ -1914,7 +1913,7 @@ erts_hashmap_get(Uint32 hx, Eterm key, Eterm node)
         hx = hashmap_shift_hash(th,hx,lvl,key);
 
         ASSERT(is_boxed(node));
-        ptr = boxed_val(node);
+        ptr = boxed_val_rel(node, map_base);
         hdr = *ptr;
         ASSERT(is_header(hdr));
         ASSERT(!is_hashmap_header_head(hdr));
-- 
cgit v1.2.3


From 9c78f149517dc02457d4c59e90bc9b03d411e28c Mon Sep 17 00:00:00 2001
From: Rickard Green 
Date: Tue, 5 May 2015 20:05:00 +0200
Subject: Optimized timer implementation

---
 erts/emulator/Makefile.in               |    2 +-
 erts/emulator/beam/atom.names           |    3 +
 erts/emulator/beam/beam_bif_load.c      |    4 +-
 erts/emulator/beam/beam_emu.c           |   60 +-
 erts/emulator/beam/bif.c                |   25 +-
 erts/emulator/beam/bif.h                |    1 +
 erts/emulator/beam/bif.tab              |   14 +-
 erts/emulator/beam/break.c              |   12 +-
 erts/emulator/beam/code_ix.c            |    4 +-
 erts/emulator/beam/erl_alloc.c          |   15 +-
 erts/emulator/beam/erl_alloc.types      |   19 +-
 erts/emulator/beam/erl_bif_info.c       |    8 +-
 erts/emulator/beam/erl_bif_timer.c      |  853 ---------
 erts/emulator/beam/erl_bif_timer.h      |   37 -
 erts/emulator/beam/erl_bif_trace.c      |    4 +-
 erts/emulator/beam/erl_db.c             |    8 +-
 erts/emulator/beam/erl_gc.c             |   56 +-
 erts/emulator/beam/erl_hl_timer.c       | 2901 +++++++++++++++++++++++++++++++
 erts/emulator/beam/erl_hl_timer.h       |   80 +
 erts/emulator/beam/erl_init.c           |   26 +-
 erts/emulator/beam/erl_lock_check.c     |    1 +
 erts/emulator/beam/erl_message.c        |    2 +-
 erts/emulator/beam/erl_message.h        |   14 +-
 erts/emulator/beam/erl_nif.c            |    4 +-
 erts/emulator/beam/erl_node_tables.c    |    2 +-
 erts/emulator/beam/erl_port.h           |   28 +-
 erts/emulator/beam/erl_port_task.c      |   19 +-
 erts/emulator/beam/erl_process.c        |  517 +++---
 erts/emulator/beam/erl_process.h        |   91 +-
 erts/emulator/beam/erl_process_lock.c   |  144 +-
 erts/emulator/beam/erl_process_lock.h   |   92 +-
 erts/emulator/beam/erl_ptab.c           |   19 +-
 erts/emulator/beam/erl_ptab.h           |  102 +-
 erts/emulator/beam/erl_thr_progress.c   |    5 +-
 erts/emulator/beam/erl_time.h           |  245 ++-
 erts/emulator/beam/erl_time_sup.c       |  283 ++-
 erts/emulator/beam/global.h             |    6 +-
 erts/emulator/beam/io.c                 |  114 +-
 erts/emulator/beam/register.c           |    9 +-
 erts/emulator/beam/sys.h                |    5 +-
 erts/emulator/beam/time.c               |  699 ++++----
 erts/emulator/beam/utils.c              |  145 +-
 erts/emulator/hipe/hipe_debug.c         |    6 +-
 erts/emulator/hipe/hipe_native_bif.c    |   36 +-
 erts/emulator/sys/common/erl_check_io.c |    5 +-
 erts/emulator/sys/common/erl_poll.c     |    2 +-
 erts/emulator/sys/ose/erl_poll.c        |    2 +-
 erts/emulator/sys/win32/erl_poll.c      |    2 +-
 erts/preloaded/ebin/erlang.beam         |  Bin 106120 -> 101588 bytes
 erts/preloaded/ebin/erts_internal.beam  |  Bin 12808 -> 5380 bytes
 erts/preloaded/ebin/init.beam           |  Bin 49756 -> 48760 bytes
 erts/preloaded/src/erlang.erl           |  205 +--
 erts/preloaded/src/erts_internal.erl    |  255 +--
 erts/preloaded/src/init.erl             |   25 -
 54 files changed, 4480 insertions(+), 2736 deletions(-)
 delete mode 100644 erts/emulator/beam/erl_bif_timer.c
 delete mode 100644 erts/emulator/beam/erl_bif_timer.h
 create mode 100644 erts/emulator/beam/erl_hl_timer.c
 create mode 100644 erts/emulator/beam/erl_hl_timer.h

(limited to 'erts')

diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index b4a17e76e7..659ea1b27f 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -779,7 +779,7 @@ RUN_OBJS = \
 	$(OBJDIR)/erl_fun.o             $(OBJDIR)/erl_bif_port.o \
 	$(OBJDIR)/erl_term.o 		$(OBJDIR)/erl_node_tables.o \
 	$(OBJDIR)/erl_monitors.o	$(OBJDIR)/erl_process_dump.o \
-	$(OBJDIR)/erl_bif_timer.o	$(OBJDIR)/erl_cpu_topology.o \
+	$(OBJDIR)/erl_hl_timer.o	$(OBJDIR)/erl_cpu_topology.o \
 	$(OBJDIR)/erl_drv_thread.o      $(OBJDIR)/erl_bif_chksum.o \
 	$(OBJDIR)/erl_bif_re.o		$(OBJDIR)/erl_unicode.o \
 	$(OBJDIR)/packet_parser.o	$(OBJDIR)/safe_hash.o \
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index ae3f30d82f..65cc5d0ceb 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -68,6 +68,7 @@ atom aborted
 atom abs_path
 atom absoluteURI
 atom ac
+atom accessor
 atom active
 atom all
 atom all_but_first
@@ -94,12 +95,14 @@ atom args
 atom arg0
 atom arity
 atom asn1
+atom async
 atom asynchronous
 atom atom
 atom atom_used
 atom attributes
 atom await_port_send_result
 atom await_proc_exit
+atom await_result
 atom await_sched_wall_time_modifications
 atom awaiting_load
 atom awaiting_unload
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index df1983a83d..fe0fa22751 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -371,7 +371,7 @@ staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking,
 	ASSERT(commiter_state.stager == NULL);
 	commiter_state.stager = c_p;
 	erts_schedule_thr_prgr_later_op(smp_code_ix_commiter, NULL, &commiter_state.lop);
-	erts_smp_proc_inc_refc(c_p);
+	erts_proc_inc_refc(c_p);
 	erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
 	/*
 	 * smp_code_ix_commiter() will do the rest "later"
@@ -398,7 +398,7 @@ static void smp_code_ix_commiter(void* null)
 	erts_resume(p, ERTS_PROC_LOCK_STATUS);
     }
     erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
-    erts_smp_proc_dec_refc(p);
+    erts_proc_dec_refc(p);
 }
 #endif /* ERTS_SMP */
 
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 8fcdc72895..4b2ab72a29 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -2024,44 +2024,32 @@ void process_main(void)
      }
      GetArg1(1, timeout_value);
      if (timeout_value != make_small(0)) {
-#if !defined(ARCH_64) || HALFWORD_HEAP
-	 Uint time_val;
-#endif
 
-	 if (is_small(timeout_value) && signed_val(timeout_value) > 0 &&
-#if defined(ARCH_64) && !HALFWORD_HEAP
-	     ((unsigned_val(timeout_value) >> 32) == 0)
-#else
-	     1
-#endif
-	     ) {
-	     /*
-	      * The timer routiner will set c_p->i to the value in
-	      * c_p->def_arg_reg[0].  Note that it is safe to use this
-	      * location because there are no living x registers in
-	      * a receive statement.
-	      * Note that for the halfword emulator, the two first elements
-	      * of the array are used.
-	      */
-	     BeamInstr** pi = (BeamInstr**) c_p->def_arg_reg;
-	     *pi = I+3;
-	     set_timer(c_p, unsigned_val(timeout_value));
-	 } else if (timeout_value == am_infinity) {
+	 if (timeout_value == am_infinity)
 	     c_p->flags |= F_TIMO;
-#if !defined(ARCH_64) || HALFWORD_HEAP
-	 } else if (term_to_Uint(timeout_value, &time_val)) {
-	     BeamInstr** pi = (BeamInstr**) c_p->def_arg_reg;
-	     *pi = I+3;
-	     set_timer(c_p, time_val);
-#endif
-	 } else {		/* Wrong time */
-	     OpCase(i_wait_error_locked): {
-		 erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
-		 /* Fall through */
+	 else {
+	     int tres = erts_set_proc_timer_term(c_p, timeout_value);
+	     if (tres == 0) {
+		 /*
+		  * The timer routiner will set c_p->i to the value in
+		  * c_p->def_arg_reg[0].  Note that it is safe to use this
+		  * location because there are no living x registers in
+		  * a receive statement.
+		  * Note that for the halfword emulator, the two first elements
+		  * of the array are used.
+		  */
+		 BeamInstr** pi = (BeamInstr**) c_p->def_arg_reg;
+		 *pi = I+3;
 	     }
+	     else { /* Wrong time */
+	     OpCase(i_wait_error_locked): {
+		     erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
+		     /* Fall through */
+		 }
 	     OpCase(i_wait_error): {
-		 c_p->freason = EXC_TIMEOUT_VALUE;
-		 goto find_func_info;
+		     c_p->freason = EXC_TIMEOUT_VALUE;
+		     goto find_func_info;
+		 }
 	     }
 	 }
 
@@ -2110,7 +2098,7 @@ void process_main(void)
      if ((c_p->flags & (F_INSLPQUEUE | F_TIMO)) == 0) {
 	 BeamInstr** p = (BeamInstr **) c_p->def_arg_reg;
 	 *p = I+3;
-	 set_timer(c_p, Arg(1));
+	 erts_set_proc_timer_uword(c_p, Arg(1));
      }
      goto wait2;
  }
@@ -5879,7 +5867,7 @@ build_stacktrace(Process* c_p, Eterm exc) {
      * (e.g. spawn_link(erlang, abs, [1])).
      */
     if (fi.current == NULL) {
-	erts_set_current_function(&fi, c_p->initial);
+	erts_set_current_function(&fi, c_p->u.initial);
 	args = am_true; /* Just in case */
     } else {
 	args = get_args_from_exc(exc);
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index cc20ec7440..0c9a1d079c 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -44,6 +44,7 @@
 #include "erl_bits.h"
 #include "erl_bif_unique.h"
 
+Export *erts_await_result;
 static Export* flush_monitor_messages_trap = NULL;
 static Export* set_cpu_topology_trap = NULL;
 static Export* await_proc_exit_trap = NULL;
@@ -835,26 +836,6 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2)
     return ret;
 }
 
-BIF_RETTYPE erts_internal_monitor_process_2(BIF_ALIST_2)
-{
-    if (is_not_internal_pid(BIF_ARG_1)) {
-	if (is_external_pid(BIF_ARG_1)
-	    && (external_pid_dist_entry(BIF_ARG_1)
-		== erts_this_dist_entry)) {
-	    BIF_RET(am_false);
-	}
-	goto badarg;
-    }
-
-    if (is_not_internal_ref(BIF_ARG_2))
-	goto badarg;
-
-    BIF_RET(local_pid_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, 1));
-
-badarg:
-    BIF_ERROR(BIF_P, BADARG);
-}
-
 /**********************************************************************/
 /* this is a combination of the spawn and link BIFs */
 
@@ -4906,6 +4887,10 @@ void erts_init_bif(void)
 #endif
 		     , &bif_return_trap);
 
+    erts_await_result = erts_export_put(am_erts_internal,
+					am_await_result,
+					1);
+
     erts_init_trap_export(&dsend_continue_trap_export,
 			  am_erts_internal, am_dsend_continue_trap, 1,
 			  dsend_continue_trap_1);
diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h
index d461c3f479..b877711544 100644
--- a/erts/emulator/beam/bif.h
+++ b/erts/emulator/beam/bif.h
@@ -20,6 +20,7 @@
 #ifndef __BIF_H__
 #define __BIF_H__
 
+extern Export *erts_await_result;
 extern Export* erts_format_cpu_topology_trap;
 extern Export *erts_convert_time_unit_trap;
 
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 471f687101..eadba3eaff 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -171,11 +171,6 @@ bif erts_internal:map_hashmap_children/1
 
 bif erts_internal:time_unit/0
 
-bif erts_internal:get_bif_timer_servers/0
-bif erts_internal:create_bif_timer/0
-bif erts_internal:access_bif_timer/1
-
-bif erts_internal:monitor_process/2
 bif erts_internal:is_system_process/1
 
 # inet_db support
@@ -220,6 +215,15 @@ bif math:sqrt/1
 bif math:atan2/2
 bif math:pow/2
 
+bif erlang:start_timer/3
+bif erlang:start_timer/4
+bif erlang:send_after/3
+bif erlang:send_after/4
+bif erlang:cancel_timer/1
+bif erlang:cancel_timer/2
+bif erlang:read_timer/1
+bif erlang:read_timer/2
+
 bif erlang:make_tuple/2
 bif erlang:append_element/2
 bif erlang:make_tuple/3
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index e2fa572546..02e65cb9c6 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -36,7 +36,7 @@
 #include "atom.h"
 #include "beam_load.h"
 #include "erl_instrument.h"
-#include "erl_bif_timer.h"
+#include "erl_hl_timer.h"
 #include "erl_thr_progress.h"
 
 /* Forward declarations -- should really appear somewhere else */
@@ -108,7 +108,7 @@ process_killer(void)
 		case 'k': {
 		    ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
 		    erts_aint32_t state;
-		    erts_smp_proc_inc_refc(rp);
+		    erts_proc_inc_refc(rp);
 		    erts_smp_proc_lock(rp, rp_locks);
 		    state = erts_smp_atomic32_read_acqb(&rp->state);
 		    if (state & (ERTS_PSFLG_FREE
@@ -131,7 +131,7 @@ process_killer(void)
 						     0);
 		    }
 		    erts_smp_proc_unlock(rp, rp_locks);
-		    erts_smp_proc_dec_refc(rp);
+		    erts_proc_dec_refc(rp);
 		}
 		case 'n': br = 1; break;
 		case 'r': return;
@@ -227,9 +227,9 @@ print_process_info(int to, void *to_arg, Process *p)
      * Display the initial function name
      */
     erts_print(to, to_arg, "Spawned as: %T:%T/%bpu\n",
-	       p->initial[INITIAL_MOD],
-	       p->initial[INITIAL_FUN],
-	       p->initial[INITIAL_ARI]);
+	       p->u.initial[INITIAL_MOD],
+	       p->u.initial[INITIAL_FUN],
+	       p->u.initial[INITIAL_ARI]);
     
     if (p->current != NULL) {
 	if (running) {
diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c
index 4344558348..d925709bd0 100644
--- a/erts/emulator/beam/code_ix.c
+++ b/erts/emulator/beam/code_ix.c
@@ -130,7 +130,7 @@ int erts_try_seize_code_write_permission(Process* c_p)
 	ASSERT(code_writing_process != c_p);
 	qitem = erts_alloc(ERTS_ALC_T_CODE_IX_LOCK_Q, sizeof(*qitem));
 	qitem->p = c_p;
-	erts_smp_proc_inc_refc(c_p);
+	erts_proc_inc_refc(c_p);
 	qitem->next = code_write_queue;
 	code_write_queue = qitem;
 	erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
@@ -151,7 +151,7 @@ void erts_release_code_write_permission(void)
 	}
 	erts_smp_proc_unlock(qitem->p, ERTS_PROC_LOCK_STATUS);
 	code_write_queue = qitem->next;
-	erts_smp_proc_dec_refc(qitem->p);
+	erts_proc_dec_refc(qitem->p);
 	erts_free(ERTS_ALC_T_CODE_IX_LOCK_Q, qitem);
     }
     code_writing_process = NULL;
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index f2bceff4eb..71902c2f9f 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -39,7 +39,7 @@
 #include "erl_instrument.h"
 #include "erl_mseg.h"
 #include "erl_monitors.h"
-#include "erl_bif_timer.h"
+#include "erl_hl_timer.h"
 #include "erl_cpu_topology.h"
 #include "erl_thr_queue.h"
 #if defined(ERTS_ALC_T_DRV_SEL_D_STATE) || defined(ERTS_ALC_T_DRV_EV_D_STATE)
@@ -575,6 +575,15 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
     fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_THR_Q_EL_SL)]
 	= sizeof(ErtsThrQElement_t);
 #endif
+    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_LL_PTIMER)]
+	= erts_timer_type_size(ERTS_ALC_T_LL_PTIMER);
+    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_HL_PTIMER)]
+	= erts_timer_type_size(ERTS_ALC_T_HL_PTIMER);
+    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_BIF_TIMER)]
+	= erts_timer_type_size(ERTS_ALC_T_BIF_TIMER);
+    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_ABIF_TIMER)]
+	= erts_timer_type_size(ERTS_ALC_T_ABIF_TIMER);
+
 #ifdef HARD_DEBUG
     hdbg_init();
 #endif
@@ -3190,7 +3199,7 @@ reply_alloc_info(void *vair)
 	rp_locks &= ~ERTS_PROC_LOCK_MAIN;
  
     erts_smp_proc_unlock(rp, rp_locks);
-    erts_smp_proc_dec_refc(rp);
+    erts_proc_dec_refc(rp);
 
     if (erts_smp_atomic32_dec_read_nob(&air->refc) == 0)
 	aireq_free(air);
@@ -3264,7 +3273,7 @@ erts_request_alloc_info(struct process *c_p,
     erts_smp_atomic32_init_nob(&air->refc,
 			       (erts_aint32_t) erts_no_schedulers);
 
-    erts_smp_proc_add_refc(c_p, (Sint32) erts_no_schedulers);
+    erts_proc_add_refc(c_p, (Sint) erts_no_schedulers);
 
 #ifdef ERTS_SMP
     if (erts_no_schedulers > 1)
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index e2f8da38b9..57c506458c 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -163,9 +163,13 @@ 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
+type	TIMER_SERVICE	LONG_LIVED	SYSTEM		timer_service
+type    LL_PTIMER	FIXED_SIZE	PROCESSES	ll_ptimer
+type    HL_PTIMER	FIXED_SIZE	PROCESSES	hl_ptimer
+type    BIF_TIMER	FIXED_SIZE	PROCESSES	bif_timer
+type    ABIF_TIMER	FIXED_SIZE	PROCESSES	accessor_bif_timer
+type    TIMER_REQUEST	SHORT_LIVED	PROCESSES	timer_request
+type    BTM_YIELD_STATE	SHORT_LIVED	PROCESSES	btm_yield_state
 type	REG_TABLE	STANDARD	SYSTEM		reg_tab
 type	FUN_TABLE	STANDARD	CODE		fun_tab
 type	DIST_TABLE	STANDARD	SYSTEM		dist_tab
@@ -324,8 +328,6 @@ type	ACTIVE_PROCS	STANDARD	PROCESSES	active_procs
 +endif
 
 +if smp
-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
@@ -365,7 +367,6 @@ type	AINFO_REQ	STANDARD_LOW	SYSTEM		alloc_info_request
 type	SCHED_WTIME_REQ	STANDARD_LOW	SYSTEM		sched_wall_time_request
 type	GC_INFO_REQ	STANDARD_LOW	SYSTEM		gc_info_request
 type	PORT_DATA_HEAP	STANDARD_LOW	SYSTEM		port_data_heap
-type	BIF_TIMER_DATA	LONG_LIVED_LOW	SYSTEM		bif_timer_data
 
 +else # "fullword"
 
@@ -386,7 +387,6 @@ type	AINFO_REQ	SHORT_LIVED	SYSTEM		alloc_info_request
 type	SCHED_WTIME_REQ	SHORT_LIVED	SYSTEM		sched_wall_time_request
 type	GC_INFO_REQ	SHORT_LIVED	SYSTEM		gc_info_request
 type	PORT_DATA_HEAP	STANDARD	SYSTEM		port_data_heap
-type	BIF_TIMER_DATA	LONG_LIVED	SYSTEM		bif_timer_data
 
 +endif
 
@@ -419,7 +419,12 @@ type	ENVIRONMENT	TEMPORARY	SYSTEM		environment
 type	PUTENV_STR	SYSTEM		SYSTEM		putenv_string
 type	PRT_REP_EXIT	STANDARD	SYSTEM		port_report_exit
 type	SYS_BLOCKING	STANDARD	SYSTEM		sys_blocking
+
++if smp
 type	SYS_WRITE_BUF	TEMPORARY	SYSTEM		sys_write_buf
++else
+type	SYS_WRITE_BUF	BINARY		SYSTEM		sys_write_buf
++endif
 
 +endif
 
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index b2658a1fd6..e30687fca5 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -1054,9 +1054,9 @@ process_info_aux(Process *BIF_P,
     case am_initial_call:
 	hp = HAlloc(BIF_P, 3+4);
 	res = TUPLE3(hp,
-		     rp->initial[INITIAL_MOD],
-		     rp->initial[INITIAL_FUN],
-		     make_small(rp->initial[INITIAL_ARI]));
+		     rp->u.initial[INITIAL_MOD],
+		     rp->u.initial[INITIAL_FUN],
+		     make_small(rp->u.initial[INITIAL_ARI]));
 	hp += 4;
 	break;
 
@@ -2129,6 +2129,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
 	BIF_RET(erts_has_time_correction() ? am_true : am_false);
     } else if (ERTS_IS_ATOM_STR("start_time", BIF_ARG_1)) {
 	BIF_RET(erts_get_monotonic_start_time(BIF_P));
+    } else if (ERTS_IS_ATOM_STR("end_time", BIF_ARG_1)) {
+	BIF_RET(erts_get_monotonic_end_time(BIF_P));
     } else if (ERTS_IS_ATOM_STR("time_warp_mode", BIF_ARG_1)) {
 	switch (erts_time_warp_mode()) {
 	case ERTS_NO_TIME_WARP_MODE: {
diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c
deleted file mode 100644
index 0bd8d20c34..0000000000
--- a/erts/emulator/beam/erl_bif_timer.c
+++ /dev/null
@@ -1,853 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * 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
- * 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 "erl_bif_timer.h"
-#include "global.h"
-#include "bif.h"
-#include "error.h"
-#include "big.h"
-#include "erl_thr_progress.h"
-#include "erl_bif_unique.h"
-
-/****************************************************************************
-** BIF Timer support
-****************************************************************************/
-
-#define BTM_FLG_SL_TIMER	(((Uint32) 1) << 0)
-#define BTM_FLG_CANCELED	(((Uint32) 1) << 1)
-#define BTM_FLG_HEAD		(((Uint32) 1) << 2)
-#define BTM_FLG_BYNAME		(((Uint32) 1) << 3)
-#define BTM_FLG_WRAP		(((Uint32) 1) << 4)
-
-struct ErtsBifTimer_ {
-    struct {
-	union {
-	    ErtsBifTimer **head;
-	    ErtsBifTimer *prev;
-	} u;
-	ErtsBifTimer *next;
-    } tab;
-    union {
-	Eterm name;
-	struct {
-	    ErtsBifTimer *prev;
-	    ErtsBifTimer *next;
-	    Process *ess;
-	} proc;
-    } receiver;
-    ErlTimer tm;
-    ErlHeapFragment* bp;
-    Uint32 flags;
-    Eterm message;
-    Uint32 ref_numbers[ERTS_REF_NUMBERS];
-};
-
-#ifdef SMALL_MEMORY
-#define TIMER_HASH_VEC_SZ	3331
-#define BTM_PREALC_SZ		10
-#else
-#define TIMER_HASH_VEC_SZ	10007
-#define BTM_PREALC_SZ		100
-#endif
-static ErtsBifTimer **bif_timer_tab;  
-static Uint no_bif_timers;
-
-
-static erts_smp_rwmtx_t bif_timer_lock;
-
-#define erts_smp_safe_btm_rwlock(P, L) \
-	safe_btm_lock((P), (L), 1)
-#define erts_smp_safe_btm_rlock(P, L) \
-	safe_btm_lock((P), (L), 0)
-#define erts_smp_btm_rwlock() \
-	erts_smp_rwmtx_rwlock(&bif_timer_lock)
-#define erts_smp_btm_tryrwlock() \
-	erts_smp_rwmtx_tryrwlock(&bif_timer_lock)
-#define erts_smp_btm_rwunlock() \
-	erts_smp_rwmtx_rwunlock(&bif_timer_lock)
-#define erts_smp_btm_rlock() \
-	erts_smp_rwmtx_rlock(&bif_timer_lock)
-#define erts_smp_btm_tryrlock() \
-	erts_smp_rwmtx_tryrlock(&bif_timer_lock)
-#define erts_smp_btm_runlock() \
-	erts_smp_rwmtx_runlock(&bif_timer_lock)
-#define erts_smp_btm_lock_init() \
-	erts_smp_rwmtx_init(&bif_timer_lock, "bif_timers")
-
-
-static ERTS_INLINE int
-safe_btm_lock(Process *c_p, ErtsProcLocks c_p_locks, int rw_lock)
-{
-    ASSERT(c_p && c_p_locks);
-#ifdef ERTS_SMP
-    if ((rw_lock ? erts_smp_btm_tryrwlock() : erts_smp_btm_tryrlock()) != EBUSY)
-	return 0;
-    erts_smp_proc_unlock(c_p, c_p_locks);
-    if (rw_lock)
-	erts_smp_btm_rwlock();
-    else
-	erts_smp_btm_rlock();
-    erts_smp_proc_lock(c_p, c_p_locks);
-    if (ERTS_PROC_IS_EXITING(c_p)) {
-	if (rw_lock)
-	    erts_smp_btm_rwunlock();
-	else
-	    erts_smp_btm_runlock();
-	return 1;
-    }
-#endif
-    return 0;
-}
-
-ERTS_SCHED_PREF_PALLOC_IMPL(btm_pre, ErtsBifTimer, BTM_PREALC_SZ)
-
-static ERTS_INLINE int
-get_index(Uint32 *ref_numbers, Uint32 len)
-{
-    Uint32 hash;
-    /* len can potentially be larger than ERTS_REF_NUMBERS
-       if it has visited another node... */
-    if (len > ERTS_REF_NUMBERS)
-	len = ERTS_REF_NUMBERS;
-
-#if ERTS_REF_NUMBERS != 3
-#error "ERTS_REF_NUMBERS changed. Update me..."
-#endif
-    switch (len) {
-    case 3: if (!ref_numbers[2]) len = 2;
-    case 2: if (!ref_numbers[1]) len = 1;
-    default:  break;
-    }
-
-    ASSERT(1 <= len && len <= ERTS_REF_NUMBERS);
-
-    hash = block_hash((byte *) ref_numbers, len * sizeof(Uint32), 0x08d12e65);
-    return (int) (hash % ((Uint32) TIMER_HASH_VEC_SZ));
-}
-
-static Eterm
-create_ref(Uint *hp, Uint32 *ref_numbers, Uint32 len)
-{
-    Uint32 *datap;
-    int i;
-
-
-    if (len > ERTS_MAX_REF_NUMBERS) {
-	/* Such large refs should no be able to appear in the emulator */
-	erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
-    }
-
-#if defined(ARCH_64) && !HALFWORD_HEAP
-    hp[0] = make_ref_thing_header(len/2 + 1);
-    datap = (Uint32 *) &hp[1];
-    *(datap++) = len;
-#else
-    hp[0] = make_ref_thing_header(len);
-    datap = (Uint32 *) &hp[1];
-#endif
-
-    for (i = 0; i < len; i++)
-	datap[i] = ref_numbers[i];
-
-    return make_internal_ref(hp);
-}
-
-static int
-eq_non_standard_ref_numbers(Uint32 *rn1, Uint32 len1, Uint32 *rn2, Uint32 len2)
-{
-#if defined(ARCH_64) && !HALFWORD_HEAP
-#define MAX_REF_HEAP_SZ (1+(ERTS_MAX_REF_NUMBERS/2+1))
-#else
-#define MAX_REF_HEAP_SZ (1+ERTS_MAX_REF_NUMBERS)
-#endif
-    DeclareTmpHeapNoproc(r1_hp,(MAX_REF_HEAP_SZ * 2));
-    Eterm *r2_hp = r1_hp +MAX_REF_HEAP_SZ;
-
-    return eq(create_ref(r1_hp, rn1, len1), create_ref(r2_hp, rn2, len2));
-#undef MAX_REF_HEAP_SZ
-}
-
-static ERTS_INLINE int
-eq_ref_numbers(Uint32 *rn1, Uint32 len1, Uint32 *rn2, Uint32 len2)
-{
-    int res;
-    if (len1 != ERTS_REF_NUMBERS || len2 != ERTS_REF_NUMBERS) {
-	/* Can potentially happen, but will never... */
-	return eq_non_standard_ref_numbers(rn1, len1, rn2, len2);
-    }
-
-#if ERTS_REF_NUMBERS != 3
-#error "ERTS_REF_NUMBERS changed. Update me..."
-#endif
-    res = rn1[0] == rn2[0] && rn1[1] == rn2[1] && rn1[2] == rn2[2];
-
-    ASSERT(res
-	   ? eq_non_standard_ref_numbers(rn1, len1, rn2, len2)
-	   : !eq_non_standard_ref_numbers(rn1, len1, rn2, len2));
-
-    return res;
-}
-
-static ERTS_INLINE ErtsBifTimer *
-tab_find(Eterm ref)
-{
-    Uint32 *ref_numbers = internal_ref_numbers(ref);
-    Uint32 ref_numbers_len = internal_ref_no_of_numbers(ref);
-    int ix = get_index(ref_numbers, ref_numbers_len);
-    ErtsBifTimer* btm;
-
-    for (btm = bif_timer_tab[ix]; btm; btm = btm->tab.next)
-	if (eq_ref_numbers(ref_numbers, ref_numbers_len,
-			   btm->ref_numbers, ERTS_REF_NUMBERS))
-	    return btm;
-    return NULL;
-}
-
-static ERTS_INLINE void
-tab_remove(ErtsBifTimer* btm)
-{
-    if (btm->flags & BTM_FLG_HEAD) {
-	*btm->tab.u.head = btm->tab.next;
-	if (btm->tab.next) {
-	    btm->tab.next->flags |= BTM_FLG_HEAD;
-	    btm->tab.next->tab.u.head = btm->tab.u.head;
-	}
-    }
-    else {
-	btm->tab.u.prev->tab.next = btm->tab.next;
-	if (btm->tab.next)
-	    btm->tab.next->tab.u.prev = btm->tab.u.prev;
-    }
-    btm->flags |= BTM_FLG_CANCELED;
-    ASSERT(no_bif_timers > 0);
-    no_bif_timers--;
-}
-
-static ERTS_INLINE void
-tab_insert(ErtsBifTimer* btm)
-{
-    int ix = get_index(btm->ref_numbers, ERTS_REF_NUMBERS);
-    ErtsBifTimer* btm_list = bif_timer_tab[ix];
-
-    if (btm_list) {
-	btm_list->flags &= ~BTM_FLG_HEAD;
-	btm_list->tab.u.prev = btm;
-    }
-
-    btm->flags |= BTM_FLG_HEAD;
-    btm->tab.u.head = &bif_timer_tab[ix];
-    btm->tab.next = btm_list;
-    bif_timer_tab[ix] = btm;
-    no_bif_timers++;
-}
-
-static ERTS_INLINE void
-link_proc(Process *p, ErtsBifTimer* btm)
-{
-    btm->receiver.proc.ess = p;
-    btm->receiver.proc.prev = NULL;
-    btm->receiver.proc.next = p->u.bif_timers;
-    if (p->u.bif_timers)	
-	p->u.bif_timers->receiver.proc.prev = btm;
-    p->u.bif_timers = btm;
-}
-
-static ERTS_INLINE void
-unlink_proc(ErtsBifTimer* btm)
-{
-    if (btm->receiver.proc.prev)
-	btm->receiver.proc.prev->receiver.proc.next = btm->receiver.proc.next;
-    else
-	btm->receiver.proc.ess->u.bif_timers = btm->receiver.proc.next;
-    if (btm->receiver.proc.next)
-	btm->receiver.proc.next->receiver.proc.prev = btm->receiver.proc.prev;
-}
-
-static void
-bif_timer_cleanup(ErtsBifTimer* btm)
-{
-    ASSERT(btm);
-
-    if (btm->bp)
-	free_message_buffer(btm->bp);
-
-    if (!btm_pre_free(btm)) {
-	if (btm->flags & BTM_FLG_SL_TIMER)
-	    erts_free(ERTS_ALC_T_SL_BIF_TIMER, (void *) btm);
-	else
-	    erts_free(ERTS_ALC_T_LL_BIF_TIMER, (void *) btm);
-    }
-}
-
-static void
-bif_timer_timeout(ErtsBifTimer* btm)
-{
-    ASSERT(btm);
-
-
-    erts_smp_btm_rwlock();
-
-    if (btm->flags & BTM_FLG_CANCELED) {
-    /*
-     * A concurrent cancel is ongoing. Do not send the timeout message,
-     * but cleanup here since the cancel call-back won't be called.
-     */
-#ifndef ERTS_SMP
-	ASSERT(0);
-#endif
-    }
-    else {
-	ErtsProcLocks rp_locks = 0;
-	Process* rp;
-
-	tab_remove(btm);
-
-	ASSERT(!erts_get_current_process());
-
-	if (btm->flags & BTM_FLG_BYNAME)
-	    rp = erts_whereis_process(NULL, 0, btm->receiver.name, 0, 0);
-	else {
-	    rp = btm->receiver.proc.ess;
-	    unlink_proc(btm);
-	}
-
-	if (rp) {
-	    Eterm message;
-	    ErlHeapFragment *bp;
-
-	    bp = btm->bp;
-	    btm->bp = NULL; /* Prevent cleanup of message buffer... */
-
-	    if (!(btm->flags & BTM_FLG_WRAP))
-		message = btm->message;
-	    else {
-#if ERTS_REF_NUMBERS != 3
-#error "ERTS_REF_NUMBERS changed. Update me..."
-#endif
-		Eterm ref;
-		Uint *hp;
-		Uint wrap_size = REF_THING_SIZE + 4;
-		message = btm->message;
-
-		if (!bp) {
-		    ErlOffHeap *ohp;
-		    ASSERT(is_immed(message));
-		    hp = erts_alloc_message_heap(wrap_size,
-						 &bp,
-						 &ohp,
-						 rp,
-						 &rp_locks);
-		} else {
-		    Eterm old_size = bp->used_size;
-		    bp = erts_resize_message_buffer(bp, old_size + wrap_size,
-						    &message, 1);
-		    hp = &bp->mem[0] + old_size;
-		}
-
-		write_ref_thing(hp,
-				btm->ref_numbers[0],
-				btm->ref_numbers[1],
-				btm->ref_numbers[2]);
-		ref = make_internal_ref(hp);
-		hp += REF_THING_SIZE;
-		message = TUPLE3(hp, am_timeout, ref, message);
-	    }
-
-	    erts_queue_message(rp, &rp_locks, bp, message, NIL
-#ifdef USE_VM_PROBES
-			       , NIL
-#endif
-			       );
-	    erts_smp_proc_unlock(rp, rp_locks);
-	}
-    }
-
-    erts_smp_btm_rwunlock();
-
-    bif_timer_cleanup(btm);
-}
-
-static Eterm
-setup_bif_timer(Uint32 xflags,
-		Process *c_p,
-		Eterm time,
-		Eterm receiver,
-		Eterm message)
-{
-    Process *rp;
-    ErtsBifTimer* btm;
-    Uint timeout;
-    Eterm ref;
-    Uint32 *ref_numbers;
-    
-    if (!term_to_Uint(time, &timeout))
-	return THE_NON_VALUE;
-#if defined(ARCH_64) && !HALFWORD_HEAP
-    if ((timeout >> 32) != 0)
-	return THE_NON_VALUE;
-#endif
-    if (is_not_internal_pid(receiver) && is_not_atom(receiver))
-	return THE_NON_VALUE;
-
-    ref = erts_make_ref(c_p);
-
-    if (is_atom(receiver))
-	rp = NULL;
-    else {
-	rp = erts_pid2proc(c_p, ERTS_PROC_LOCK_MAIN,
-			   receiver, ERTS_PROC_LOCK_MSGQ);
-	if (!rp)
-	    return ref;
-    }
-
-    if (timeout < ERTS_ALC_MIN_LONG_LIVED_TIME) {
-	if (timeout < 1000) {
-	    btm = btm_pre_alloc();
-	    if (!btm)
-		goto sl_timer_alloc;
-	    btm->flags = 0;
-	}
-	else {
-	sl_timer_alloc:
-	    btm = (ErtsBifTimer *) erts_alloc(ERTS_ALC_T_SL_BIF_TIMER,
-					      sizeof(ErtsBifTimer));
-	    btm->flags = BTM_FLG_SL_TIMER;
-	}
-    }
-    else {
-	btm = (ErtsBifTimer *) erts_alloc(ERTS_ALC_T_LL_BIF_TIMER,
-					 sizeof(ErtsBifTimer));
-	btm->flags = 0;
-    }
-
-    if (rp) {
-	link_proc(rp, btm);
-	erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ);
-    }
-    else {
-	ASSERT(is_atom(receiver));
-	btm->receiver.name = receiver;
-	btm->flags |= BTM_FLG_BYNAME;
-    }
-
-    btm->flags |= xflags;
-
-    ref_numbers = internal_ref_numbers(ref);
-    ASSERT(internal_ref_no_of_numbers(ref) == 3);
-#if ERTS_REF_NUMBERS != 3
-#error "ERTS_REF_NUMBERS changed. Update me..."
-#endif
-    btm->ref_numbers[0] = ref_numbers[0];
-    btm->ref_numbers[1] = ref_numbers[1];
-    btm->ref_numbers[2] = ref_numbers[2];
-
-    ASSERT(eq_ref_numbers(btm->ref_numbers, ERTS_REF_NUMBERS,
-			  ref_numbers, ERTS_REF_NUMBERS));
-
-    if (is_immed(message)) {
-	btm->bp = NULL;
-	btm->message = message;
-    }
-    else {
-	ErlHeapFragment* bp;
-	Eterm* hp;
-	Uint size;
-
-	size = size_object(message);
-	btm->bp = bp = new_message_buffer(size);
-	hp = bp->mem;
-	btm->message = copy_struct(message, size, &hp, &bp->off_heap);
-    }
-
-    tab_insert(btm);
-    ASSERT(btm == tab_find(ref));
-    erts_init_timer(&btm->tm);
-    erts_set_timer(&btm->tm,
-		  (ErlTimeoutProc) bif_timer_timeout,
-		  (ErlCancelProc) bif_timer_cleanup,
-		  (void *) btm,
-		  timeout);
-    return ref;
-}
-
-BIF_RETTYPE old_send_after_3(BIF_ALIST_3);
-/* send_after(Time, Pid, Message) -> Ref */
-BIF_RETTYPE old_send_after_3(BIF_ALIST_3)
-{
-    Eterm res;
-
-    if (erts_smp_safe_btm_rwlock(BIF_P, ERTS_PROC_LOCK_MAIN))
-	ERTS_BIF_EXITED(BIF_P);
-
-    res = setup_bif_timer(0, BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
-
-    erts_smp_btm_rwunlock();
-
-    if (is_non_value(res)) {
-	BIF_ERROR(BIF_P, BADARG);
-    }
-    else {
-	ASSERT(is_internal_ref(res));
-	BIF_RET(res);
-    }
-}
-
-BIF_RETTYPE old_start_timer_3(BIF_ALIST_3);
-/* start_timer(Time, Pid, Message) -> Ref */
-BIF_RETTYPE old_start_timer_3(BIF_ALIST_3)
-{
-    Eterm res;
-
-    if (erts_smp_safe_btm_rwlock(BIF_P, ERTS_PROC_LOCK_MAIN))
-	ERTS_BIF_EXITED(BIF_P);
-
-    res = setup_bif_timer(BTM_FLG_WRAP, BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
-
-    erts_smp_btm_rwunlock();
-
-    if (is_non_value(res)) {
-	BIF_ERROR(BIF_P, BADARG);
-    }
-    else {
-	ASSERT(is_internal_ref(res));
-	BIF_RET(res);
-    }
-}
-
-BIF_RETTYPE old_cancel_timer_1(BIF_ALIST_1);
-/* cancel_timer(Ref) -> false | RemainingTime */
-BIF_RETTYPE old_cancel_timer_1(BIF_ALIST_1)
-{
-    Eterm res;
-    ErtsBifTimer *btm;
-
-    if (is_not_internal_ref(BIF_ARG_1)) {
-	if (is_ref(BIF_ARG_1)) {
-	    BIF_RET(am_false);
-	}
-	BIF_ERROR(BIF_P, BADARG);
-    }
-
-    if (erts_smp_safe_btm_rwlock(BIF_P, ERTS_PROC_LOCK_MAIN))
-	ERTS_BIF_EXITED(BIF_P);
-
-    btm = tab_find(BIF_ARG_1);
-    if (!btm || btm->flags & BTM_FLG_CANCELED) {
-	erts_smp_btm_rwunlock();
-	res = am_false;
-    }
-    else {
-	Uint left = erts_time_left(&btm->tm);
-	if (!(btm->flags & BTM_FLG_BYNAME)) {
-	    erts_smp_proc_lock(btm->receiver.proc.ess, ERTS_PROC_LOCK_MSGQ);
-	    unlink_proc(btm);
-	    erts_smp_proc_unlock(btm->receiver.proc.ess, ERTS_PROC_LOCK_MSGQ);
-	}
-	tab_remove(btm);
-	ASSERT(!tab_find(BIF_ARG_1));
-	erts_cancel_timer(&btm->tm);
-	erts_smp_btm_rwunlock();
-	res = erts_make_integer(left, BIF_P);
-    }
-
-    BIF_RET(res);
-}
-
-BIF_RETTYPE old_read_timer_1(BIF_ALIST_1);
-/* read_timer(Ref) -> false | RemainingTime */
-BIF_RETTYPE old_read_timer_1(BIF_ALIST_1)
-{
-    Eterm res;
-    ErtsBifTimer *btm;
-
-    if (is_not_internal_ref(BIF_ARG_1)) {
-	if (is_ref(BIF_ARG_1)) {
-	    BIF_RET(am_false);
-	}
-	BIF_ERROR(BIF_P, BADARG);
-    }
-
-    if (erts_smp_safe_btm_rlock(BIF_P, ERTS_PROC_LOCK_MAIN))
-	ERTS_BIF_EXITED(BIF_P);
-
-    btm = tab_find(BIF_ARG_1);
-    if (!btm || btm->flags & BTM_FLG_CANCELED) {
-	res = am_false;
-    }
-    else {
-	Uint left = erts_time_left(&btm->tm);
-	res = erts_make_integer(left, BIF_P);
-    }
-
-    erts_smp_btm_runlock();
-
-    BIF_RET(res);
-}
-
-void
-erts_print_bif_timer_info(int to, void *to_arg)
-{
-    int i;
-    int lock = !ERTS_IS_CRASH_DUMPING;
-
-    if (lock)
-	erts_smp_btm_rlock();
-
-    for (i = 0; i < TIMER_HASH_VEC_SZ; i++) {
-	ErtsBifTimer *btm;
-	for (btm = bif_timer_tab[i]; btm; btm = btm->tab.next) {
-	    Eterm receiver = (btm->flags & BTM_FLG_BYNAME
-			      ? btm->receiver.name
-			      : btm->receiver.proc.ess->common.id);
-	    erts_print(to, to_arg, "=timer:%T\n", receiver);
-	    erts_print(to, to_arg, "Message: %T\n", btm->message);
-	    erts_print(to, to_arg, "Time left: %u\n",
-		       erts_time_left(&btm->tm));
-	}
-    }
-
-    if (lock)
-	erts_smp_btm_runlock();
-}
-
-
-void
-erts_cancel_bif_timers(Process *p, ErtsProcLocks plocks)
-{
-    ErtsBifTimer *btm;
-
-    if (erts_smp_btm_tryrwlock() == EBUSY) {
-	erts_smp_proc_unlock(p, plocks);
-	erts_smp_btm_rwlock();
-	erts_smp_proc_lock(p, plocks);
-    }
-
-    btm = p->u.bif_timers;
-    while (btm) {
-	ErtsBifTimer *tmp_btm;
-	ASSERT(!(btm->flags & BTM_FLG_CANCELED));
-	tab_remove(btm);
-	tmp_btm = btm;
-	btm = btm->receiver.proc.next;
-	erts_cancel_timer(&tmp_btm->tm);
-    }
-
-    p->u.bif_timers = NULL;
-
-    erts_smp_btm_rwunlock();
-}
-
-static void erts_old_bif_timer_init(void)
-{
-    int i;
-    no_bif_timers = 0;
-    init_btm_pre_alloc();
-    erts_smp_btm_lock_init();
-    bif_timer_tab = erts_alloc(ERTS_ALC_T_BIF_TIMER_TABLE,
-			       sizeof(ErtsBifTimer *)*TIMER_HASH_VEC_SZ);
-    for (i = 0; i < TIMER_HASH_VEC_SZ; ++i)
-	bif_timer_tab[i] = NULL;
-}
-
-Uint
-erts_bif_timer_memory_size(void)
-{
-    Uint res;
-    int lock = !ERTS_IS_CRASH_DUMPING;
-
-    if (lock)
-	erts_smp_btm_rlock();
-
-    res = (sizeof(ErtsBifTimer *)*TIMER_HASH_VEC_SZ
-	   + no_bif_timers*sizeof(ErtsBifTimer));
-
-    if (lock)
-	erts_smp_btm_runlock();
-
-    return res;
-}
-
-
-void
-erts_bif_timer_foreach(void (*func)(Eterm, Eterm, ErlHeapFragment *, void *),
-		       void *arg)
-{
-    int i;
-
-    ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
-
-    for (i = 0; i < TIMER_HASH_VEC_SZ; i++) {
-	ErtsBifTimer *btm;
-	for (btm = bif_timer_tab[i]; btm; btm = btm->tab.next) {
-	    (*func)((btm->flags & BTM_FLG_BYNAME
-		     ? btm->receiver.name
-		     : btm->receiver.proc.ess->common.id),
-		    btm->message,
-		    btm->bp,
-		    arg);
-	}
-    }
-}
-
-typedef struct {
-    Uint ref_heap[REF_THING_SIZE];
-    Eterm pid[1];
-} ErtsBifTimerServers;
-
-static ErtsBifTimerServers *bif_timer_servers;
-
-void erts_bif_timer_init(void)
-{
-    erts_old_bif_timer_init();
-}
-
-void
-erts_bif_timer_start_servers(Eterm parent)
-{
-    Process *parent_proc;
-    Eterm *hp, btr_ref, arg_list_end;
-    ErlSpawnOpts so;
-    int i;
-
-    bif_timer_servers = erts_alloc(ERTS_ALC_T_BIF_TIMER_DATA,
-				   (sizeof(ErtsBifTimerServers)
-				    + (sizeof(Eterm)*(erts_no_schedulers-1))));
-
-    so.flags = SPO_USE_ARGS|SPO_SYSTEM_PROC|SPO_PREFER_SCHED|SPO_OFF_HEAP_MSGS;
-    so.min_heap_size  = H_MIN_SIZE;
-    so.min_vheap_size = BIN_VH_MIN_SIZE;
-    so.priority       = PRIORITY_MAX;
-    so.max_gen_gcs    = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
-
-    /*
-     * Parent is "init" and schedulers have not yet been started, so it
-     * *should* be alive and well...
-     */
-    ASSERT(is_internal_pid(parent));
-    parent_proc = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
-							internal_pid_index(parent));
-    ASSERT(parent_proc);
-    ASSERT(parent_proc->common.id == parent);
-    ASSERT(!ERTS_PROC_IS_EXITING(parent_proc));
-
-    erts_smp_proc_lock(parent_proc, ERTS_PROC_LOCK_MAIN);
-
-    hp = HAlloc(parent_proc, 2*erts_no_schedulers + 2 + REF_THING_SIZE);
-
-    btr_ref = erts_make_ref_in_buffer(hp);
-    hp += REF_THING_SIZE;
-
-    arg_list_end = CONS(hp, btr_ref, NIL);
-    hp += 2;
-
-    for (i = 0; i < erts_no_schedulers; i++) {
-	int sched = i+1;
-	Eterm arg_list = CONS(hp, make_small(i+1), arg_list_end);
-	hp += 2;
-
-	so.scheduler = sched; /* Preferred scheduler */
-
-	bif_timer_servers->pid[i] = erl_create_process(parent_proc,
-						       am_erts_internal,
-						       am_bif_timer_server,
-						       arg_list,
-						       &so);
-    }
-
-    erts_smp_proc_unlock(parent_proc, ERTS_PROC_LOCK_MAIN);
-
-    hp = internal_ref_val(btr_ref);
-    for (i = 0; i < REF_THING_SIZE; i++)
-	bif_timer_servers->ref_heap[i] = hp[i];
-}
-
-BIF_RETTYPE
-erts_internal_get_bif_timer_servers_0(BIF_ALIST_0)
-{
-    int i;
-    Eterm *hp, res = NIL;
-
-    hp = HAlloc(BIF_P, erts_no_schedulers*2);
-    for (i = erts_no_schedulers-1; i >= 0; i--) {
-	res = CONS(hp, bif_timer_servers->pid[i], res);
-	hp += 2;
-    }
-    BIF_RET(res);
-}
-
-BIF_RETTYPE
-erts_internal_access_bif_timer_1(BIF_ALIST_1)
-{
-    int ix;
-    Uint32 *rdp;
-    Eterm ref, pid, *hp, res;
-
-    if (is_not_internal_ref(BIF_ARG_1)) {
-	if (is_not_ref(BIF_ARG_1))
-	    BIF_ERROR(BIF_P, BADARG);
-	BIF_RET(am_undefined);
-    }
-
-    rdp = internal_ref_numbers(BIF_ARG_1);
-    ix = (int) erts_get_ref_numbers_thr_id(rdp);
-    if (ix < 1 || erts_no_schedulers < ix)
-	BIF_RET(am_undefined);
-
-    pid = bif_timer_servers->pid[ix-1];
-    ASSERT(is_internal_pid(pid));
-
-    hp = HAlloc(BIF_P, 3 /* 2-tuple */ + REF_THING_SIZE);
-    for (ix = 0; ix < REF_THING_SIZE; ix++)
-	hp[ix] = bif_timer_servers->ref_heap[ix];
-    ref = make_internal_ref(&hp[0]);
-    hp += REF_THING_SIZE;
-
-    res = TUPLE2(hp, ref, pid);
-    BIF_RET(res);
-}
-
-BIF_RETTYPE
-erts_internal_create_bif_timer_0(BIF_ALIST_0)
-{
-    ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(BIF_P);
-    Eterm *hp, btr_ref, t_ref, pid, res;
-    int ix;
-
-    hp = HAlloc(BIF_P, 4 /* 3-tuple */ + 2*REF_THING_SIZE);
-    for (ix = 0; ix < REF_THING_SIZE; ix++)
-	hp[ix] = bif_timer_servers->ref_heap[ix];
-    btr_ref = make_internal_ref(&hp[0]);
-    hp += REF_THING_SIZE;
-
-    t_ref = erts_sched_make_ref_in_buffer(esdp, hp);
-    hp += REF_THING_SIZE;
-
-    ASSERT(erts_get_ref_numbers_thr_id(internal_ref_numbers(t_ref))
-	   == (Uint32) esdp->no);
-
-    pid = bif_timer_servers->pid[((int) esdp->no) - 1];
-    
-    res = TUPLE3(hp, btr_ref, pid, t_ref);
-
-    BIF_RET(res);
-}
diff --git a/erts/emulator/beam/erl_bif_timer.h b/erts/emulator/beam/erl_bif_timer.h
deleted file mode 100644
index c2f5dfd3c3..0000000000
--- a/erts/emulator/beam/erl_bif_timer.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * %CopyrightBegin%
- * 
- * Copyright Ericsson AB 2005-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 ERL_BIF_TIMER_H__
-#define ERL_BIF_TIMER_H__
-
-typedef struct ErtsBifTimer_ ErtsBifTimer;
-
-#include "sys.h"
-#include "erl_process.h"
-#include "erl_message.h"
-
-Uint erts_bif_timer_memory_size(void);
-void erts_print_bif_timer_info(int to, void *to_arg);
-void erts_cancel_bif_timers(Process *p, ErtsProcLocks plocks);
-void erts_bif_timer_init(void);
-void erts_bif_timer_foreach(void (*func)(Eterm,Eterm,ErlHeapFragment *,void *),
-			    void *arg);
-void erts_bif_timer_start_servers(Eterm);
-#endif
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index ac57205c47..13e0160648 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -359,7 +359,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
 	ASSERT(finish_bp.stager == NULL);
 	finish_bp.stager = p;
 	erts_schedule_thr_prgr_later_op(smp_bp_finisher, NULL, &finish_bp.lop);
-	erts_smp_proc_inc_refc(p);
+	erts_proc_inc_refc(p);
 	erts_suspend(p, ERTS_PROC_LOCK_MAIN, NULL);
 	ERTS_BIF_YIELD_RETURN(p, make_small(matches));
     }
@@ -393,7 +393,7 @@ static void smp_bp_finisher(void* null)
 	    erts_resume(p, ERTS_PROC_LOCK_STATUS);
 	}
 	erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
-	erts_smp_proc_dec_refc(p);
+	erts_proc_dec_refc(p);
     }
 }
 #endif /* ERTS_SMP */
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index fff892ae54..2e2cb98354 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -620,7 +620,7 @@ BIF_RETTYPE ets_safe_fixtable_2(BIF_ALIST_2)
     erts_fprintf(stderr,
 		"ets:safe_fixtable(%T,%T); Process: %T, initial: %T:%T/%bpu\n",
 		BIF_ARG_1, BIF_ARG_2, BIF_P->common.id,
-		BIF_P->initial[0], BIF_P->initial[1], BIF_P->initial[2]);
+		BIF_P->u.initial[0], BIF_P->u.initial[1], BIF_P->u.initial[2]);
 #endif
     kind = (BIF_ARG_2 == am_true) ? LCK_READ : LCK_WRITE_REC; 
 
@@ -1247,7 +1247,7 @@ BIF_RETTYPE ets_rename_2(BIF_ALIST_2)
     erts_fprintf(stderr,
 		"ets:rename(%T,%T); Process: %T, initial: %T:%T/%bpu\n",
 		BIF_ARG_1, BIF_ARG_2, BIF_P->common.id,
-		BIF_P->initial[0], BIF_P->initial[1], BIF_P->initial[2]);
+		BIF_P->u.initial[0], BIF_P->u.initial[1], BIF_P->u.initial[2]);
 #endif
 
 
@@ -1563,7 +1563,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
     erts_fprintf(stderr,
 		"ets:new(%T,%T)=%T; Process: %T, initial: %T:%T/%bpu\n",
 		 BIF_ARG_1, BIF_ARG_2, ret, BIF_P->common.id,
-		 BIF_P->initial[0], BIF_P->initial[1], BIF_P->initial[2]);
+		 BIF_P->u.initial[0], BIF_P->u.initial[1], BIF_P->u.initial[2]);
 	erts_fprintf(stderr, "ets: new: meta_pid_to_tab common.memory_size = %ld\n",
 		     erts_smp_atomic_read_nob(&meta_pid_to_tab->common.memory_size));
 	erts_fprintf(stderr, "ets: new: meta_pid_to_fixed_tab common.memory_size = %ld\n",
@@ -1696,7 +1696,7 @@ BIF_RETTYPE ets_delete_1(BIF_ALIST_1)
     erts_fprintf(stderr,
 		"ets:delete(%T); Process: %T, initial: %T:%T/%bpu\n",
 		BIF_ARG_1, BIF_P->common.id,
-		BIF_P->initial[0], BIF_P->initial[1], BIF_P->initial[2]);
+		BIF_P->u.initial[0], BIF_P->u.initial[1], BIF_P->u.initial[2]);
 #endif
 
     CHECK_TABLES();
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 307f9c93e0..6a986b31ed 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -97,10 +97,10 @@ typedef struct {
 
 static Uint setup_rootset(Process*, Eterm*, int, Rootset*);
 static void cleanup_rootset(Rootset *rootset);
-static Uint combined_message_size(Process* p, int off_heap_msgs);
+static Uint combined_message_size(Process* p);
 static void remove_message_buffers(Process* p);
-static int major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl, int off_heap_msgs);
-static int minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl, int off_heap_msgs);
+static int major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl);
+static int minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl);
 static void do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj);
 static Eterm* sweep_rootset(Rootset *rootset, Eterm* htop, char* src, Uint src_size);
 static Eterm* sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size);
@@ -403,9 +403,7 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
 {
     Uint reclaimed_now = 0;
     int done = 0;
-    int off_heap_msgs;
     ErtsMonotonicTime start_time = 0; /* Shut up faulty warning... */
-    erts_aint32_t state;
     ErtsSchedulerData *esdp;
 #ifdef USE_VM_PROBES
     DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
@@ -422,10 +420,9 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
         trace_gc(p, am_gc_start);
     }
 
-    state = erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
-    off_heap_msgs = state & ERTS_PSFLG_OFF_HEAP_MSGS;
+    (void) erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
     if (erts_system_monitor_long_gc != 0)
-	start_time = erts_get_monotonic_time();
+	start_time = erts_get_monotonic_time(esdp);
 
     ERTS_CHK_OFFHEAP(p);
 
@@ -448,11 +445,11 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
     while (!done) {
 	if ((FLAGS(p) & F_NEED_FULLSWEEP) != 0) {
 	    DTRACE2(gc_major_start, pidbuf, need);
-	    done = major_collection(p, need, objv, nobj, &reclaimed_now, off_heap_msgs);
+	    done = major_collection(p, need, objv, nobj, &reclaimed_now);
 	    DTRACE2(gc_major_end, pidbuf, reclaimed_now);
 	} else {
 	    DTRACE2(gc_minor_start, pidbuf, need);
-	    done = minor_collection(p, need, objv, nobj, &reclaimed_now, off_heap_msgs);
+	    done = minor_collection(p, need, objv, nobj, &reclaimed_now);
 	    DTRACE2(gc_minor_end, pidbuf, reclaimed_now);
 	}
     }
@@ -477,7 +474,7 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
 	Uint gc_time;
 	if (erts_test_long_gc_sleep)
 	    while (0 != erts_milli_sleep(erts_test_long_gc_sleep));
-	end_time = erts_get_monotonic_time();
+	end_time = erts_get_monotonic_time(esdp);
 	gc_time = (Uint) ERTS_MONOTONIC_TO_MSEC(end_time - start_time);
 	if (gc_time && gc_time > erts_system_monitor_long_gc) {
 	    monitor_long_gc(p, gc_time);
@@ -833,7 +830,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
 }
 
 static int
-minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl, int off_heap_msgs)
+minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
 {
     Uint mature = HIGH_WATER(p) - HEAP_START(p);
 
@@ -872,22 +869,20 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl, int of
 	Uint size_after;
 	Uint need_after;
 	Uint stack_size = STACK_SZ_ON_HEAP(p);
-	Uint fragments = MBUF_SIZE(p) + combined_message_size(p, off_heap_msgs);
+	Uint fragments = MBUF_SIZE(p) + combined_message_size(p);
 	Uint size_before = fragments + (HEAP_TOP(p) - HEAP_START(p));
 	Uint new_sz = next_heap_size(p, HEAP_SIZE(p) + fragments, 0);
 
         do_minor(p, new_sz, objv, nobj);
 
-	if (!off_heap_msgs) {
-	    /*
-	     * Copy newly received message onto the end of the new heap.
-	     */
-	    ErtsGcQuickSanityCheck(p);
-	    for (msgp = p->msg.first; msgp; msgp = msgp->next) {
-		if (msgp->data.attached) {
-		    erts_move_msg_attached_data_to_heap(&p->htop, &p->off_heap, msgp);
-		    ErtsGcQuickSanityCheck(p);
-		}
+	/*
+	 * Copy newly received message onto the end of the new heap.
+	 */
+	ErtsGcQuickSanityCheck(p);
+	for (msgp = p->msg.first; msgp; msgp = msgp->next) {
+	    if (msgp->data.attached) {
+		erts_move_msg_attached_data_to_heap(&p->htop, &p->off_heap, msgp);
+		ErtsGcQuickSanityCheck(p);
 	    }
 	}
 	ErtsGcQuickSanityCheck(p);
@@ -1213,7 +1208,7 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
  */
 
 static int
-major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl, int off_heap_msgs)
+major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
 {
     Rootset rootset;
     Roots* roots;
@@ -1226,7 +1221,7 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl, int of
     Uint oh_size = (char *) OLD_HTOP(p) - oh;
     Uint n;
     Uint new_sz;
-    Uint fragments = MBUF_SIZE(p) + combined_message_size(p, off_heap_msgs);
+    Uint fragments = MBUF_SIZE(p) + combined_message_size(p);
 
     size_before = fragments + (HEAP_TOP(p) - HEAP_START(p));
 
@@ -1436,7 +1431,7 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl, int of
 
     ErtsGcQuickSanityCheck(p);
 
-    if (!off_heap_msgs) {
+    {
 	ErlMessage *msgp;
 	/*
 	 * Copy newly received message onto the end of the new heap.
@@ -1506,14 +1501,11 @@ adjust_after_fullsweep(Process *p, Uint size_before, int need, Eterm *objv, int
  * mbuf list.
  */
 static Uint
-combined_message_size(Process* p, int off_heap_msgs)
+combined_message_size(Process* p)
 {
     Uint sz;
     ErlMessage *msgp;
 
-    if (off_heap_msgs)
-	return 0;
-
     for (sz = 0, msgp = p->msg.first; msgp; msgp = msgp->next) {
 	if (msgp->data.attached)
 	    sz += erts_msg_attached_data_size(msgp);
@@ -2666,7 +2658,7 @@ reply_gc_info(void *vgcirp)
     if (rp_locks)
 	erts_smp_proc_unlock(rp, rp_locks);
 
-    erts_smp_proc_dec_refc(rp);
+    erts_proc_dec_refc(rp);
 
     if (erts_smp_atomic32_dec_read_nob(&gcirp->refc) == 0)
 	gcireq_free(vgcirp);
@@ -2690,7 +2682,7 @@ erts_gc_info_request(Process *c_p)
     erts_smp_atomic32_init_nob(&gcirp->refc,
 			       (erts_aint32_t) erts_no_schedulers);
 
-    erts_smp_proc_add_refc(c_p, (Sint32) erts_no_schedulers);
+    erts_proc_add_refc(c_p, (Sint) erts_no_schedulers);
 
 #ifdef ERTS_SMP
     if (erts_no_schedulers > 1)
diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c
new file mode 100644
index 0000000000..2245799819
--- /dev/null
+++ b/erts/emulator/beam/erl_hl_timer.c
@@ -0,0 +1,2901 @@
+/*
+ * %CopyrightBegin%
+ * 
+ * Copyright Ericsson AB 2015. All Rights Reserved.
+ * 
+ * The 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: High level timers implementing BIF timers
+ *              as well as process and port timers.
+ *
+ * Author: 	Rickard Green
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "sys.h"
+#include "global.h"
+#include "bif.h"
+#include "erl_bif_unique.h"
+#define ERTS_WANT_TIMER_WHEEL_API
+#include "erl_time.h"
+#include "erl_hl_timer.h"
+
+#define ERTS_TMR_CHECK_CANCEL_ON_CREATE 0
+
+#if 0
+#  define ERTS_HLT_HARD_DEBUG
+#endif
+#if 0
+#  define ERTS_HLT_DEBUG
+#endif
+
+#if defined(ERTS_HLT_HARD_DEBUG) || defined(DEBUG)
+#  if defined(ERTS_HLT_HARD_DEBUG)
+#    undef ERTS_RBT_HARD_DEBUG
+#    define ERTS_RBT_HARD_DEBUG 1
+#  endif
+#  ifndef ERTS_HLT_DEBUG
+#    define ERTS_HLT_DEBUG 1
+#  endif
+#endif
+
+#undef ERTS_HLT_ASSERT
+#if defined(ERTS_HLT_DEBUG)
+#  define ERTS_HLT_ASSERT(E) ERTS_ASSERT(E)
+#  undef ERTS_RBT_DEBUG
+#  define ERTS_RBT_DEBUG
+#else
+#  define ERTS_HLT_ASSERT(E) ((void) 1)
+#endif
+
+#if defined(ERTS_HLT_HARD_DEBUG) && defined(__GNUC__)
+#warning "* * * * * * * * * * * * * * * * * *"
+#warning "* ERTS_HLT_HARD_DEBUG IS ENABLED! *"
+#warning "* * * * * * * * * * * * * * * * * *"
+#endif
+
+#ifdef ERTS_HLT_HARD_DEBUG
+#  define ERTS_HLT_HDBG_CHK_SRV(SRV) hdbg_chk_srv((SRV))
+static void hdbg_chk_srv(ErtsHLTimerService *srv);
+#else
+#  define ERTS_HLT_HDBG_CHK_SRV(SRV) ((void) 1)
+#endif
+
+#if ERTS_REF_NUMBERS != 3
+#error "ERTS_REF_NUMBERS changed. Update me..."
+#endif
+
+#define ERTS_BIF_TIMER_SHORT_TIME 5000
+
+#ifdef ERTS_SMP
+#  define ERTS_HLT_SMP_MEMBAR_LoadLoad_LoadStore \
+    ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore)
+#else
+#  define ERTS_HLT_SMP_MEMBAR_LoadLoad_LoadStore
+#endif
+
+/* Bit 0 to 9 contains scheduler id (see mask below) */
+#define ERTS_TMR_ROFLG_HLT		(((Uint32) 1) << 10)
+#define ERTS_TMR_ROFLG_BIF_TMR		(((Uint32) 1) << 11)
+#define ERTS_TMR_ROFLG_ABIF_TMR		(((Uint32) 1) << 12)
+#define ERTS_TMR_ROFLG_PRE_ALC		(((Uint32) 1) << 13)
+#define ERTS_TMR_ROFLG_REG_NAME		(((Uint32) 1) << 14)
+#define ERTS_TMR_ROFLG_PROC		(((Uint32) 1) << 15)
+#define ERTS_TMR_ROFLG_PORT		(((Uint32) 1) << 16)
+
+#define ERTS_TMR_ROFLG_SID_MASK	\
+    (ERTS_TMR_ROFLG_HLT - (Uint32) 1)
+
+#define ERTS_TMR_STATE_ACTIVE		((erts_aint32_t) 0)
+#define ERTS_TMR_STATE_CANCELED		((erts_aint32_t) 1)
+#define ERTS_TMR_STATE_TIMED_OUT	((erts_aint32_t) 2)
+
+typedef struct ErtsHLTimer_ ErtsHLTimer;
+
+#define ERTS_HLT_PFLG_RED		(((UWord) 1) << 0)
+#define ERTS_HLT_PFLG_SAME_TIME		(((UWord) 1) << 1)
+
+#define ERTS_HLT_PFLGS_MASK \
+    (ERTS_HLT_PFLG_RED|ERTS_HLT_PFLG_SAME_TIME)
+
+#define ERTS_HLT_PFIELD_NOT_IN_TABLE	(~((UWord) 0))
+
+typedef struct {
+    UWord parent; /* parent pointer and flags... */
+    union {
+	struct {
+	    ErtsHLTimer *right;
+	    ErtsHLTimer *left;
+	} t;
+	struct {
+	    ErtsHLTimer *prev;
+	    ErtsHLTimer *next;
+	} l;
+    } u;
+    ErtsHLTimer *same_time;
+} ErtsHLTimerTimeTree;
+
+typedef struct {
+    UWord parent; /* parent pointer and flags... */
+    ErtsHLTimer *right;
+    ErtsHLTimer *left;
+} ErtsHLTimerTree;
+
+typedef struct {
+    Uint32 roflgs;
+    erts_smp_atomic32_t refc;
+    union {
+	erts_atomic_t next;
+    } u;
+} ErtsTmrHead;
+
+struct ErtsHLTimer_ {
+    ErtsTmrHead head; /* NEED to be first! */
+    union {
+	ErtsThrPrgrLaterOp cleanup;
+	ErtsHLTimerTimeTree tree;
+    } time;
+    ErtsMonotonicTime timeout;
+    union {
+	Process *proc;
+	Port *port;
+	Eterm name;
+    } receiver;
+
+#ifdef ERTS_HLT_HARD_DEBUG
+    int pending_timeout;
+#endif
+
+    erts_smp_atomic32_t state;
+
+    /* BIF timer only fields follow... */
+    struct {
+	Uint32 refn[ERTS_REF_NUMBERS];
+	ErtsHLTimerTree proc_tree;
+	ErtsHLTimerTree tree;
+	Eterm message;
+	ErlHeapFragment *bp;
+    } btm;
+    struct {
+	Eterm accessor;
+	ErtsHLTimerTree tree;
+    } abtm;
+};
+
+#define ERTS_HL_PTIMER_SIZE offsetof(ErtsHLTimer, btm)
+#define ERTS_BIF_TIMER_SIZE offsetof(ErtsHLTimer, abtm)
+#define ERTS_ABIF_TIMER_SIZE sizeof(ErtsHLTimer)
+
+typedef struct {
+    ErtsTmrHead head; /* NEED to be first! */
+    void *p;
+    ErtsTWheelTimer tw_tmr;
+} ErtsTWTimer;
+
+typedef union {
+    ErtsTmrHead head;
+    ErtsHLTimer hlt;
+    ErtsTWTimer twt;
+} ErtsTimer;
+
+#ifdef SMALL_MEMORY
+#define BIF_TIMER_PREALC_SZ	10
+#define PTIMER_PREALC_SZ	10
+#else
+#define BIF_TIMER_PREALC_SZ	100
+#define PTIMER_PREALC_SZ	100
+#endif
+
+ERTS_SCHED_PREF_PALLOC_IMPL(bif_timer_pre,
+			    ErtsHLTimer,
+			    BIF_TIMER_PREALC_SZ)
+
+ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(tw_timer,
+				 ErtsTWTimer,
+				 PTIMER_PREALC_SZ,
+				 ERTS_ALC_T_LL_PTIMER)
+
+#ifdef ERTS_HLT_DEBUG
+#define ERTS_TMR_TIMEOUT_YIELD_LIMIT 5
+#else
+#define ERTS_TMR_TIMEOUT_YIELD_LIMIT 100
+#endif
+#define ERTS_TMR_CANCELED_TIMER_LIMIT 100
+#define ERTS_TMR_CANCELED_TIMER_SMALL_LIMIT 5
+
+#define ERTS_TMR_TIMEOUT_YIELD_STATE_T same_time_list_yield_state_t
+#define ERTS_TMR_YIELDING_TIMEOUT_STATE_INITER {NULL, {0}}
+typedef struct {
+    int dummy;
+} ERTS_TMR_TIMEOUT_YIELD_STATE_T;
+
+typedef struct {
+    ErtsTmrHead marker;
+    erts_atomic_t last;
+} ErtsHLTCncldTmrQTail;
+
+#ifdef ERTS_SMP
+
+typedef struct {
+    /*
+     * This structure needs to be cache line aligned for best
+     * performance.
+     */
+    union {
+	/*
+	 * Modified by threads returning canceled
+	 * timers to this timer service.
+	 */
+	ErtsHLTCncldTmrQTail data;
+	char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(
+		sizeof(ErtsHLTCncldTmrQTail))];
+    } tail;
+    /*
+     * Everything below this point is *only* accessed by the
+     * thread managing this timer service.
+     */
+    struct {
+	ErtsTimer *first;
+	ErtsTimer *unref_end;
+	struct {
+	    ErtsThrPrgrVal thr_progress;
+	    int thr_progress_reached;
+	    ErtsTimer *unref_end;
+	} next;
+	int used_marker;
+    } head;
+} ErtsHLTCncldTmrQ;
+
+#endif /* ERTS_SMP */
+
+typedef struct {
+    ErtsHLTimer *root;
+    ERTS_TMR_TIMEOUT_YIELD_STATE_T state;
+} ErtsYieldingTimeoutState;
+
+struct ErtsHLTimerService_ {
+#ifdef ERTS_SMP
+    ErtsHLTCncldTmrQ canceled_queue;
+#endif
+    ErtsHLTimer *time_tree;
+    ErtsHLTimer *btm_tree;
+    ErtsHLTimer *next_timeout;
+    ErtsYieldingTimeoutState yield;
+    ErtsTWheelTimer service_timer;
+};
+
+static ERTS_INLINE int
+refn_is_lt(Uint32 *x, Uint32 *y)
+{
+    /* !0 if x < y */
+    if (x[2] < y[2])
+	return 1;
+    if (x[2] != y[2])
+	return 0;
+    if (x[1] < y[1])
+	return 1;
+    if (x[1] != y[1])
+	return 0;
+    return x[0] < y[0];
+}
+
+#define ERTS_RBT_PREFIX time
+#define ERTS_RBT_T ErtsHLTimer
+#define ERTS_RBT_KEY_T ErtsMonotonicTime
+#define ERTS_RBT_FLAGS_T UWord
+#define ERTS_RBT_INIT_EMPTY_TNODE(T)					\
+    do {								\
+	(T)->time.tree.parent = (UWord) NULL;				\
+	(T)->time.tree.u.t.right = NULL;				\
+	(T)->time.tree.u.t.left = NULL;					\
+    } while (0)
+#define ERTS_RBT_IS_RED(T)						\
+    ((int) ((T)->time.tree.parent & ERTS_HLT_PFLG_RED))
+#define ERTS_RBT_SET_RED(T)						\
+    ((T)->time.tree.parent |= ERTS_HLT_PFLG_RED)
+#define ERTS_RBT_IS_BLACK(T)						\
+    (!ERTS_RBT_IS_RED((T)))
+#define ERTS_RBT_SET_BLACK(T)						\
+    ((T)->time.tree.parent &= ~ERTS_HLT_PFLG_RED)
+#define ERTS_RBT_GET_FLAGS(T)						\
+    ((T)->time.tree.parent & ERTS_HLT_PFLGS_MASK)
+#define ERTS_RBT_SET_FLAGS(T, F)					\
+    do {								\
+	ERTS_HLT_ASSERT((((UWord) (F)) & ~ERTS_HLT_PFLGS_MASK) == 0);	\
+	(T)->time.tree.parent &= ~ERTS_HLT_PFLGS_MASK;			\
+	(T)->time.tree.parent |= (F);					\
+    } while (0)
+#define ERTS_RBT_GET_PARENT(T)						\
+    ((ErtsHLTimer *) ((T)->time.tree.parent & ~ERTS_HLT_PFLGS_MASK))
+#define ERTS_RBT_SET_PARENT(T, P)					\
+    do {								\
+	ERTS_HLT_ASSERT((((UWord) (P)) & ERTS_HLT_PFLGS_MASK) == 0);	\
+	(T)->time.tree.parent &= ERTS_HLT_PFLGS_MASK;			\
+	(T)->time.tree.parent |= (UWord) (P);				\
+    } while (0)
+#define ERTS_RBT_GET_RIGHT(T) ((T)->time.tree.u.t.right)
+#define ERTS_RBT_SET_RIGHT(T, R) ((T)->time.tree.u.t.right = (R))
+#define ERTS_RBT_GET_LEFT(T) ((T)->time.tree.u.t.left)
+#define ERTS_RBT_SET_LEFT(T, L) ((T)->time.tree.u.t.left = (L))
+#define ERTS_RBT_GET_KEY(T) ((T)->timeout)
+#define ERTS_RBT_IS_LT(KX, KY) ((KX) < (KY))
+#define ERTS_RBT_IS_EQ(KX, KY) ((KX) == (KY))
+#define ERTS_RBT_WANT_DELETE
+#define ERTS_RBT_WANT_SMALLEST
+#define ERTS_RBT_WANT_LOOKUP_INSERT
+#define ERTS_RBT_WANT_REPLACE
+#ifdef ERTS_HLT_HARD_DEBUG
+#  define ERTS_RBT_WANT_FOREACH
+#  define ERTS_RBT_WANT_LOOKUP
+#endif
+#define ERTS_RBT_UNDEF
+
+#include "erl_rbtree.h"
+
+/* Use circular list for timers at same time */
+
+static ERTS_INLINE void
+same_time_list_insert(ErtsHLTimer **root, ErtsHLTimer *tmr)
+{
+    ErtsHLTimer *first = *root;
+    if (!first) {
+	ERTS_HLT_ASSERT((((UWord) root) & ERTS_HLT_PFLG_SAME_TIME) == 0);
+	tmr->time.tree.parent = ((UWord) root) | ERTS_HLT_PFLG_SAME_TIME;
+	tmr->time.tree.u.l.next = tmr;
+	tmr->time.tree.u.l.prev = tmr;
+	*root = tmr;
+    }
+    else {
+	tmr->time.tree.parent = ERTS_HLT_PFLG_SAME_TIME;
+	tmr->time.tree.u.l.next = first;
+	tmr->time.tree.u.l.prev = first->time.tree.u.l.prev;
+	first->time.tree.u.l.prev = tmr;
+	tmr->time.tree.u.l.prev->time.tree.u.l.next = tmr;
+    }
+}
+
+static ERTS_INLINE void
+same_time_list_delete(ErtsHLTimer *tmr)
+{
+    ErtsHLTimer **root, *next;
+    
+    root = (ErtsHLTimer **) (tmr->time.tree.parent & ~ERTS_HLT_PFLG_SAME_TIME);
+    next = tmr->time.tree.u.l.next;
+
+    ERTS_HLT_ASSERT((tmr->time.tree.parent
+		     == (((UWord) root) | ERTS_HLT_PFLG_SAME_TIME))
+		    || (tmr->time.tree.parent
+			== ERTS_HLT_PFLG_SAME_TIME));
+
+    if (next == tmr) {
+	ERTS_HLT_ASSERT(root && *root == tmr);
+	ERTS_HLT_ASSERT(tmr->time.tree.u.l.prev == tmr);
+	*root = NULL;
+    }
+    else {
+	if (root) {
+	    ERTS_HLT_ASSERT(*root == tmr);
+	    *root = next;
+	    next->time.tree.parent = ((UWord) root) | ERTS_HLT_PFLG_SAME_TIME;
+	}
+	tmr->time.tree.u.l.next->time.tree.u.l.prev = tmr->time.tree.u.l.prev;
+	tmr->time.tree.u.l.prev->time.tree.u.l.next = next;
+    }
+}
+
+static ERTS_INLINE void
+same_time_list_new_root(ErtsHLTimer **root)
+{
+    ErtsHLTimer *tmr = *root;
+    if (tmr) {
+	ERTS_HLT_ASSERT(root);
+	tmr->time.tree.parent = ((UWord) root) | ERTS_HLT_PFLG_SAME_TIME;
+    }
+}
+
+static ERTS_INLINE int
+same_time_list_foreach_destroy_yielding(ErtsHLTimer **root,
+					void (*op)(ErtsHLTimer *, void *),
+					void *arg,
+					ERTS_TMR_TIMEOUT_YIELD_STATE_T *ys,
+					Sint ylimit)
+{
+    Sint ycnt = ylimit;
+    ErtsHLTimer *end, *tmr = *root;
+    if (!tmr)
+	return 0;
+
+    ERTS_HLT_ASSERT(tmr->time.tree.parent
+		    == (((UWord) root) | ERTS_HLT_PFLG_SAME_TIME));
+
+    end = tmr->time.tree.u.l.prev;
+    end->time.tree.u.l.next = NULL;
+
+    while (1) {
+	ErtsHLTimer *op_tmr = tmr;
+
+	ERTS_HLT_ASSERT((tmr->time.tree.parent
+			 == (((UWord) root) | ERTS_HLT_PFLG_SAME_TIME))
+			|| (tmr->time.tree.parent
+			    == ERTS_HLT_PFLG_SAME_TIME));
+
+	tmr = tmr->time.tree.u.l.next;
+	(*op)(op_tmr, arg);
+	if (!tmr) {
+	    *root = NULL;
+	    return 0;
+	}
+	if (--ycnt <= 0) {
+	    /* Make new circle of timers left to process... */
+	    *root = tmr;
+	    end->time.tree.u.l.next = tmr;
+	    tmr->time.tree.u.l.prev = end;
+	    tmr->time.tree.parent = ((UWord) root) | ERTS_HLT_PFLG_SAME_TIME;
+	    return 1;
+	}
+    }    
+}
+
+#ifdef ERTS_HLT_HARD_DEBUG
+
+static ERTS_INLINE void
+same_time_list_foreach(ErtsHLTimer *root,
+		       void (*op)(ErtsHLTimer *, void *),
+		       void *arg)
+{
+    if (root) {
+	ErtsHLTimer *tmr = root;
+	do {
+	    (*op)(tmr, arg);
+	    tmr = tmr->time.tree.u.l.next;
+	} while (root != tmr);
+    }
+}
+
+static ERTS_INLINE ErtsHLTimer *
+same_time_list_lookup(ErtsHLTimer *root, ErtsHLTimer *x)
+{
+    if (root) {
+	ErtsHLTimer *tmr = root;
+	do {
+	    if (tmr == x)
+		return tmr;
+	    tmr = tmr->time.tree.u.l.next;
+	} while (root != tmr);
+    }
+    return NULL;
+}
+
+#endif /* ERTS_HLT_HARD_DEBUG */
+
+#define ERTS_RBT_PREFIX btm
+#define ERTS_RBT_T ErtsHLTimer
+#define ERTS_RBT_KEY_T Uint32 *
+#define ERTS_RBT_FLAGS_T UWord
+#define ERTS_RBT_INIT_EMPTY_TNODE(T)					\
+    do {								\
+	(T)->btm.tree.parent = (UWord) NULL;				\
+	(T)->btm.tree.right = NULL;					\
+	(T)->btm.tree.left = NULL;					\
+    } while (0)
+#define ERTS_RBT_IS_RED(T)						\
+    ((int) ((T)->btm.tree.parent & ERTS_HLT_PFLG_RED))
+#define ERTS_RBT_SET_RED(T)						\
+    ((T)->btm.tree.parent |= ERTS_HLT_PFLG_RED)
+#define ERTS_RBT_IS_BLACK(T)						\
+    (!ERTS_RBT_IS_RED((T)))
+#define ERTS_RBT_SET_BLACK(T)						\
+    ((T)->btm.tree.parent &= ~ERTS_HLT_PFLG_RED)
+#define ERTS_RBT_GET_FLAGS(T)						\
+    ((T)->btm.tree.parent & ERTS_HLT_PFLGS_MASK)
+#define ERTS_RBT_SET_FLAGS(T, F)					\
+    do {								\
+	ERTS_HLT_ASSERT((((UWord) (F)) & ~ERTS_HLT_PFLGS_MASK) == 0);	\
+	(T)->btm.tree.parent &= ~ERTS_HLT_PFLGS_MASK;			\
+	(T)->btm.tree.parent |= (F);					\
+    } while (0)
+#define ERTS_RBT_GET_PARENT(T)						\
+    ((ErtsHLTimer *) ((T)->btm.tree.parent & ~ERTS_HLT_PFLGS_MASK))
+#define ERTS_RBT_SET_PARENT(T, P)					\
+    do {								\
+	ERTS_HLT_ASSERT((((UWord) (P)) & ERTS_HLT_PFLGS_MASK) == 0);	\
+	(T)->btm.tree.parent &= ERTS_HLT_PFLGS_MASK;			\
+	(T)->btm.tree.parent |= (UWord) (P);				\
+    } while (0)
+#define ERTS_RBT_GET_RIGHT(T) ((T)->btm.tree.right)
+#define ERTS_RBT_SET_RIGHT(T, R) ((T)->btm.tree.right = (R))
+#define ERTS_RBT_GET_LEFT(T) ((T)->btm.tree.left)
+#define ERTS_RBT_SET_LEFT(T, L) ((T)->btm.tree.left = (L))
+#define ERTS_RBT_GET_KEY(T) ((T)->btm.refn)
+#define ERTS_RBT_IS_LT(KX, KY) refn_is_lt((KX), (KY))
+#define ERTS_RBT_IS_EQ(KX, KY) \
+    (((KX)[0] == (KY)[0]) & ((KX)[1] == (KY)[1]) & ((KX)[2] == (KY)[2]))
+#define ERTS_RBT_WANT_DELETE
+#define ERTS_RBT_WANT_INSERT
+#define ERTS_RBT_WANT_LOOKUP
+#define ERTS_RBT_WANT_FOREACH
+#define ERTS_RBT_UNDEF
+
+#include "erl_rbtree.h"
+
+#define ERTS_RBT_PREFIX proc_btm
+#define ERTS_RBT_T ErtsHLTimer
+#define ERTS_RBT_KEY_T Uint32 *
+#define ERTS_RBT_FLAGS_T UWord
+#define ERTS_RBT_INIT_EMPTY_TNODE(T)					\
+    do {								\
+	(T)->btm.proc_tree.parent = (UWord) NULL;			\
+	(T)->btm.proc_tree.right = NULL;				\
+	(T)->btm.proc_tree.left = NULL;					\
+    } while (0)
+#define ERTS_RBT_IS_RED(T)						\
+    ((int) ((T)->btm.proc_tree.parent & ERTS_HLT_PFLG_RED))
+#define ERTS_RBT_SET_RED(T)						\
+    ((T)->btm.proc_tree.parent |= ERTS_HLT_PFLG_RED)
+#define ERTS_RBT_IS_BLACK(T)						\
+    (!ERTS_RBT_IS_RED((T)))
+#define ERTS_RBT_SET_BLACK(T)						\
+    ((T)->btm.proc_tree.parent &= ~ERTS_HLT_PFLG_RED)
+#define ERTS_RBT_GET_FLAGS(T)						\
+    ((T)->btm.proc_tree.parent & ERTS_HLT_PFLGS_MASK)
+#define ERTS_RBT_SET_FLAGS(T, F)					\
+    do {								\
+	ERTS_HLT_ASSERT((((UWord) (F)) & ~ERTS_HLT_PFLGS_MASK) == 0);	\
+	(T)->btm.proc_tree.parent &= ~ERTS_HLT_PFLGS_MASK;		\
+	(T)->btm.proc_tree.parent |= (F);				\
+    } while (0)
+#define ERTS_RBT_GET_PARENT(T)						\
+    ((ErtsHLTimer *) ((T)->btm.proc_tree.parent & ~ERTS_HLT_PFLGS_MASK))
+#define ERTS_RBT_SET_PARENT(T, P)					\
+    do {								\
+	ERTS_HLT_ASSERT((((UWord) (P)) & ERTS_HLT_PFLGS_MASK) == 0);	\
+	(T)->btm.proc_tree.parent &= ERTS_HLT_PFLGS_MASK;		\
+	(T)->btm.proc_tree.parent |= (UWord) (P);			\
+    } while (0)
+#define ERTS_RBT_GET_RIGHT(T) ((T)->btm.proc_tree.right)
+#define ERTS_RBT_SET_RIGHT(T, R) ((T)->btm.proc_tree.right = (R))
+#define ERTS_RBT_GET_LEFT(T) ((T)->btm.proc_tree.left)
+#define ERTS_RBT_SET_LEFT(T, L) ((T)->btm.proc_tree.left = (L))
+#define ERTS_RBT_GET_KEY(T) ((T)->btm.refn)
+#define ERTS_RBT_IS_LT(KX, KY) refn_is_lt((KX), (KY))
+#define ERTS_RBT_IS_EQ(KX, KY) \
+    (((KX)[0] == (KY)[0]) & ((KX)[1] == (KY)[1]) & ((KX)[2] == (KY)[2]))
+#define ERTS_RBT_WANT_DELETE
+#define ERTS_RBT_WANT_INSERT
+#define ERTS_RBT_WANT_LOOKUP
+#define ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING
+#define ERTS_RBT_UNDEF
+
+#include "erl_rbtree.h"
+
+#define ERTS_RBT_PREFIX abtm
+#define ERTS_RBT_T ErtsHLTimer
+#define ERTS_RBT_KEY_T Uint32 *
+#define ERTS_RBT_FLAGS_T UWord
+#define ERTS_RBT_INIT_EMPTY_TNODE(T)					\
+    do {								\
+	(T)->abtm.tree.parent = (UWord) NULL;				\
+	(T)->abtm.tree.right = NULL;					\
+	(T)->abtm.tree.left = NULL;					\
+    } while (0)
+#define ERTS_RBT_IS_RED(T)						\
+    ((int) ((T)->abtm.tree.parent & ERTS_HLT_PFLG_RED))
+#define ERTS_RBT_SET_RED(T)						\
+    ((T)->abtm.tree.parent |= ERTS_HLT_PFLG_RED)
+#define ERTS_RBT_IS_BLACK(T)						\
+    (!ERTS_RBT_IS_RED((T)))
+#define ERTS_RBT_SET_BLACK(T)						\
+    ((T)->abtm.tree.parent &= ~ERTS_HLT_PFLG_RED)
+#define ERTS_RBT_GET_FLAGS(T)						\
+    ((T)->abtm.tree.parent & ERTS_HLT_PFLGS_MASK)
+#define ERTS_RBT_SET_FLAGS(T, F)					\
+    do {								\
+	ERTS_HLT_ASSERT((((UWord) (F)) & ~ERTS_HLT_PFLGS_MASK) == 0);	\
+	(T)->abtm.tree.parent &= ~ERTS_HLT_PFLGS_MASK;			\
+	(T)->abtm.tree.parent |= (F);					\
+    } while (0)
+#define ERTS_RBT_GET_PARENT(T)						\
+    ((ErtsHLTimer *) ((T)->abtm.tree.parent & ~ERTS_HLT_PFLGS_MASK))
+#define ERTS_RBT_SET_PARENT(T, P)					\
+    do {								\
+	ERTS_HLT_ASSERT((((UWord) (P)) & ERTS_HLT_PFLGS_MASK) == 0);	\
+	(T)->abtm.tree.parent &= ERTS_HLT_PFLGS_MASK;			\
+	(T)->abtm.tree.parent |= (UWord) (P);				\
+    } while (0)
+#define ERTS_RBT_GET_RIGHT(T) ((T)->abtm.tree.right)
+#define ERTS_RBT_SET_RIGHT(T, R) ((T)->abtm.tree.right = (R))
+#define ERTS_RBT_GET_LEFT(T) ((T)->abtm.tree.left)
+#define ERTS_RBT_SET_LEFT(T, L) ((T)->abtm.tree.left = (L))
+#define ERTS_RBT_GET_KEY(T) ((T)->btm.refn)
+#define ERTS_RBT_IS_LT(KX, KY) refn_is_lt((KX), (KY))
+#define ERTS_RBT_IS_EQ(KX, KY) \
+    (((KX)[0] == (KY)[0]) & ((KX)[1] == (KY)[1]) & ((KX)[2] == (KY)[2]))
+#define ERTS_RBT_WANT_DELETE
+#define ERTS_RBT_WANT_INSERT
+#define ERTS_RBT_WANT_LOOKUP
+#define ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING
+#define ERTS_RBT_UNDEF
+
+#include "erl_rbtree.h"
+
+#ifdef ERTS_SMP
+static void init_canceled_queue(ErtsHLTCncldTmrQ *cq);
+#endif
+
+void
+erts_hl_timer_init(void)
+{
+    init_tw_timer_alloc();
+    init_bif_timer_pre_alloc();
+}
+
+ErtsHLTimerService *
+erts_create_timer_service(void)
+{
+    ErtsYieldingTimeoutState init_yield = ERTS_TMR_YIELDING_TIMEOUT_STATE_INITER;
+    ErtsHLTimerService *srv;
+
+    srv = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_TIMER_SERVICE,
+					     sizeof(ErtsHLTimerService));
+    srv->time_tree = NULL;
+    srv->btm_tree = NULL;
+    srv->next_timeout = NULL;
+    srv->yield = init_yield;
+    erts_twheel_init_timer(&srv->service_timer);
+
+#ifdef ERTS_SMP
+    init_canceled_queue(&srv->canceled_queue);
+#endif
+
+    return srv;
+}
+
+size_t
+erts_timer_type_size(ErtsAlcType_t type)
+{
+    switch (type) {
+    case ERTS_ALC_T_LL_PTIMER: return sizeof(ErtsTWTimer);
+    case ERTS_ALC_T_HL_PTIMER: return ERTS_HL_PTIMER_SIZE;
+    case ERTS_ALC_T_BIF_TIMER: return ERTS_BIF_TIMER_SIZE;
+    case ERTS_ALC_T_ABIF_TIMER: return ERTS_ABIF_TIMER_SIZE;
+    default: ERTS_INTERNAL_ERROR("Unknown type");
+    }
+    return 0;
+}
+
+static ERTS_INLINE ErtsMonotonicTime
+get_timeout_pos(ErtsMonotonicTime now, ErtsMonotonicTime msec)
+{
+    ErtsMonotonicTime timeout_pos;
+    if (msec <= 0)
+	return ERTS_MONOTONIC_TO_CLKTCKS(now);
+    timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(now-1);
+    timeout_pos += ERTS_MSEC_TO_CLKTCKS(msec) + 1;
+    return timeout_pos;
+}
+
+static ERTS_INLINE Sint64
+get_time_left(ErtsSchedulerData *esdp, ErtsMonotonicTime timeout_pos)
+{
+    ErtsMonotonicTime now = erts_get_monotonic_time(esdp);
+
+    now = ERTS_MONOTONIC_TO_CLKTCKS(now-1)+1;
+    if (timeout_pos <= now)
+	return (Sint64) 0;
+    return (Sint64) ERTS_CLKTCKS_TO_MSEC(timeout_pos - now);
+}
+
+static ERTS_INLINE int
+proc_timeout_common(Process *proc, void *tmr)
+{
+    if (tmr == (void *) erts_smp_atomic_cmpxchg_mb(&proc->common.timer,
+						   ERTS_PTMR_TIMEDOUT,
+						   (erts_aint_t) tmr)) {
+	erts_aint32_t state = erts_smp_atomic32_read_acqb(&proc->state);
+	if (!(state & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_EXITING)))
+	    erts_schedule_process(proc, state, 0);
+	return 1;
+    }
+    return 0;
+}
+
+static ERTS_INLINE int
+port_timeout_common(Port *port, void *tmr)
+{
+    if (tmr == (void *) erts_smp_atomic_cmpxchg_mb(&port->common.timer,
+						   ERTS_PTMR_TIMEDOUT,
+						   (erts_aint_t) tmr)) {
+	erts_port_task_schedule(port->common.id,
+				&port->timeout_task,
+				ERTS_PORT_TASK_TIMEOUT);
+	return 1;
+    }
+    return 0;
+}
+
+/*
+ * Basic timer wheel timer stuff
+ */
+
+static void
+scheduled_tw_timer_destroy(void *vtmr)
+{
+    tw_timer_free((ErtsTWTimer *) vtmr);
+}
+
+static void
+schedule_tw_timer_destroy(ErtsTWTimer *tmr)
+{
+    /*
+     * Reference to process/port can be
+     * dropped at once...
+     */
+    if (tmr->head.roflgs & ERTS_TMR_ROFLG_PROC)
+	erts_proc_dec_refc((Process *) tmr->p);
+    else
+	erts_port_dec_refc((Port *) tmr->p);
+
+    erts_schedule_thr_prgr_later_cleanup_op(
+	scheduled_tw_timer_destroy,
+	(void *) tmr,
+	&tmr->tw_tmr.u.cleanup,
+	sizeof(ErtsTWTimer));
+}
+
+static ERTS_INLINE void
+tw_timer_dec_refc(ErtsTWTimer *tmr)
+{
+    if (erts_smp_atomic32_dec_read_relb(&tmr->head.refc) == 0) {
+	ERTS_HLT_SMP_MEMBAR_LoadLoad_LoadStore;
+	schedule_tw_timer_destroy(tmr);
+    }
+}
+
+static void
+tw_proc_timeout(void *vtwtp)
+{
+    ErtsTWTimer *twtp = (ErtsTWTimer *) vtwtp;
+    Process *proc = (Process *) twtp->p;
+    if (proc_timeout_common(proc, vtwtp))
+	tw_timer_dec_refc(twtp);
+    tw_timer_dec_refc(twtp);
+}
+
+static void
+tw_port_timeout(void *vtwtp)
+{
+    ErtsTWTimer *twtp = (ErtsTWTimer *) vtwtp;
+    Port *port = (Port *) twtp->p;
+    if (port_timeout_common(port, vtwtp))
+	tw_timer_dec_refc(twtp);
+    tw_timer_dec_refc(twtp);
+}
+
+static void
+tw_ptimer_cancel(void *vtwtp)
+{
+    tw_timer_dec_refc((ErtsTWTimer *) vtwtp);    
+}
+
+static void
+cancel_tw_timer(ErtsSchedulerData *esdp, ErtsTWTimer *tmr)
+{
+    ERTS_HLT_ASSERT((tmr->head.roflgs & ERTS_TMR_ROFLG_SID_MASK)
+		    == (Uint32) esdp->no);
+    erts_twheel_cancel_timer(esdp->timer_wheel, &tmr->tw_tmr);
+}
+
+static ErtsTWTimer *
+create_tw_timer(ErtsSchedulerData *esdp, 
+		void *p, int is_proc,
+		ErtsMonotonicTime timeout_pos)
+{
+    ErtsTWTimer *tmr;
+    void (*timeout_func)(void *);
+
+    tmr = tw_timer_alloc();
+    erts_twheel_init_timer(&tmr->tw_tmr);
+
+    tmr->head.roflgs = (Uint32) esdp->no;
+    ERTS_HLT_ASSERT((tmr->head.roflgs
+		     & ~ERTS_TMR_ROFLG_SID_MASK) == 0);
+    tmr->p = p;
+    if (is_proc) {
+	tmr->head.roflgs |= ERTS_TMR_ROFLG_PROC;
+	timeout_func = tw_proc_timeout;
+	erts_proc_inc_refc((Process *) p);
+    }
+    else {
+	tmr->head.roflgs |= ERTS_TMR_ROFLG_PORT;
+	timeout_func = tw_port_timeout;
+	erts_port_inc_refc((Port *) p);
+    }
+
+    erts_smp_atomic32_init_nob(&tmr->head.refc, 2);
+
+    erts_twheel_set_timer(esdp->timer_wheel,
+			  &tmr->tw_tmr,
+			  timeout_func,
+			  tw_ptimer_cancel,
+			  tmr,
+			  timeout_pos);
+
+    return tmr;
+}
+
+/*
+ * Basic high level timer stuff
+ */
+
+static ERTS_INLINE void
+hl_timer_destroy(ErtsHLTimer *tmr)
+{
+    Uint32 roflgs = tmr->head.roflgs;
+    if (!(roflgs & ERTS_TMR_ROFLG_BIF_TMR))
+	erts_free(ERTS_ALC_T_HL_PTIMER, tmr);
+    else {
+	if (tmr->btm.bp)
+	    free_message_buffer(tmr->btm.bp);
+	if (roflgs & ERTS_TMR_ROFLG_PRE_ALC)
+	    bif_timer_pre_free(tmr);
+	else if (roflgs & ERTS_TMR_ROFLG_ABIF_TMR)
+	    erts_free(ERTS_ALC_T_ABIF_TIMER, tmr);
+	else
+	    erts_free(ERTS_ALC_T_BIF_TIMER, tmr);
+    }
+}
+
+static void
+scheduled_hl_timer_destroy(void *vtmr)
+{
+    hl_timer_destroy((ErtsHLTimer *) vtmr);
+}
+
+static void
+schedule_hl_timer_destroy(ErtsHLTimer *tmr, Uint32 roflgs)
+{
+    UWord size;
+
+    /*
+     * Reference to process/port can be dropped
+     * at once...
+     */
+
+    ERTS_HLT_ASSERT(erts_smp_atomic32_read_nob(&tmr->head.refc) == 0);
+
+    if (roflgs & ERTS_TMR_ROFLG_REG_NAME) {
+	ERTS_HLT_ASSERT(is_atom(tmr->receiver.name));
+    }
+    else if (roflgs & ERTS_TMR_ROFLG_PROC) {
+	ERTS_HLT_ASSERT(tmr->receiver.proc);
+	erts_proc_dec_refc(tmr->receiver.proc);
+    }
+    else if (roflgs & ERTS_TMR_ROFLG_PORT) {
+	ERTS_HLT_ASSERT(tmr->receiver.port);
+	erts_port_dec_refc(tmr->receiver.port);
+    }
+
+    if (!(roflgs & ERTS_TMR_ROFLG_BIF_TMR))
+	size = ERTS_HL_PTIMER_SIZE;
+    else {
+	/*
+	 * Message buffer can be dropped at
+	 * once...
+	 */
+	if (tmr->btm.bp) {
+	    free_message_buffer(tmr->btm.bp);
+	    tmr->btm.bp = NULL;
+	}
+	size = sizeof(ErtsHLTimer);
+    }
+
+    erts_schedule_thr_prgr_later_cleanup_op(
+	scheduled_hl_timer_destroy, tmr,
+	&tmr->time.cleanup, size);
+}
+
+static ERTS_INLINE void
+hl_timer_pre_dec_refc(ErtsHLTimer *tmr)
+{
+#ifdef ERTS_HLT_DEBUG
+    erts_aint_t refc;
+    refc = erts_smp_atomic32_dec_read_nob(&tmr->head.refc);
+    ERTS_HLT_ASSERT(refc > 0);
+#else
+    erts_smp_atomic32_dec_nob(&tmr->head.refc);
+#endif
+}
+
+static ERTS_INLINE void
+hl_timer_dec_refc(ErtsHLTimer *tmr, Uint32 roflgs)
+{
+    if (erts_smp_atomic32_dec_read_relb(&tmr->head.refc) == 0) {
+	ERTS_HLT_SMP_MEMBAR_LoadLoad_LoadStore;
+	schedule_hl_timer_destroy(tmr, roflgs);
+    }
+}
+
+static void hlt_service_timeout(void *vesdp);
+#ifdef ERTS_SMP
+static void handle_canceled_queue(ErtsSchedulerData *esdp,
+				  ErtsHLTCncldTmrQ *cq,
+				  int use_limit,
+				  int ops_limit,
+				  int *need_thr_progress,
+				  ErtsThrPrgrVal *thr_prgr_p,
+				  int *need_more_work);
+#endif
+
+static ERTS_INLINE void
+check_canceled_queue(ErtsSchedulerData *esdp, ErtsHLTimerService *srv)
+{
+#if defined(ERTS_SMP) && ERTS_TMR_CHECK_CANCEL_ON_CREATE
+    ErtsHLTCncldTmrQ *cq = &srv->canceled_queue;
+    if (cq->head.first != cq->head.unref_end)
+	handle_canceled_queue(esdp, cq, 1,
+			      ERTS_TMR_CANCELED_TIMER_SMALL_LIMIT,
+			      NULL, NULL, NULL);
+#endif
+}
+
+static void
+hlt_delete_abtm(ErtsHLTimer *tmr)
+{
+    Process *proc;
+
+    ERTS_HLT_ASSERT(tmr->head.roflgs & ERTS_TMR_ROFLG_ABIF_TMR);
+
+    proc = erts_proc_lookup(tmr->abtm.accessor);
+
+    if (proc) {
+	int deref = 0;
+	erts_smp_proc_lock(proc, ERTS_PROC_LOCK_BTM);
+	if (tmr->abtm.tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) {
+	    abtm_rbt_delete(&proc->accessor_bif_timers, tmr);
+	    deref = 1;
+	    tmr->abtm.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+	}
+	erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_BTM);
+	if (deref)
+	    hl_timer_pre_dec_refc(tmr);
+    }
+}
+
+static ErtsHLTimer *
+create_hl_timer(ErtsSchedulerData *esdp,
+		ErtsMonotonicTime timeout_pos,
+		int short_time, int is_bif_tmr,
+		void *rcvrp, Eterm rcvr, Eterm acsr,
+		Eterm msg, Uint32 *refn)
+{
+    ErtsHLTimerService *srv = esdp->timer_service;
+    ErtsHLTimer *tmr, *st_tmr;
+    erts_aint32_t refc;
+    Uint32 roflgs;
+    int is_abif_tmr = is_bif_tmr && is_value(acsr) && acsr != rcvr;
+
+    check_canceled_queue(esdp, srv);
+
+    ERTS_HLT_ASSERT((esdp->no & ~ERTS_TMR_ROFLG_SID_MASK) == 0);
+
+    roflgs = ((Uint32) esdp->no) | ERTS_TMR_ROFLG_HLT;
+
+    if (!is_bif_tmr)
+	tmr = erts_alloc(ERTS_ALC_T_HL_PTIMER,
+			 ERTS_HL_PTIMER_SIZE);
+    else if (short_time) {
+	tmr = bif_timer_pre_alloc();
+	if (!tmr)
+	    goto alloc_bif_timer;
+	roflgs |= ERTS_TMR_ROFLG_PRE_ALC;
+    }
+    else {
+    alloc_bif_timer:
+	if (is_abif_tmr)
+	    tmr = erts_alloc(ERTS_ALC_T_ABIF_TIMER,
+			     ERTS_ABIF_TIMER_SIZE);
+	else
+	    tmr = erts_alloc(ERTS_ALC_T_BIF_TIMER,
+			     ERTS_BIF_TIMER_SIZE);
+    }
+
+    tmr->timeout = timeout_pos;
+
+    if (!is_bif_tmr) {
+	if (is_internal_pid(rcvr)) {
+	    erts_proc_inc_refc((Process *) rcvrp);
+	    tmr->receiver.proc = (Process *) rcvrp;
+	    roflgs |= ERTS_TMR_ROFLG_PROC;
+	}
+	else {
+	    erts_port_inc_refc((Port *) rcvrp);
+	    ERTS_HLT_ASSERT(is_internal_port(rcvr));
+	    tmr->receiver.port = (Port *) rcvrp;
+	    roflgs |= ERTS_TMR_ROFLG_PORT;
+	}
+	refc = 2;
+    }
+    else {
+	Uint hsz;
+
+	roflgs |= ERTS_TMR_ROFLG_BIF_TMR;
+	if (is_internal_pid(rcvr)) {
+	    roflgs |= ERTS_TMR_ROFLG_PROC;
+	    tmr->receiver.proc = (Process *) rcvrp;
+	    refc = 2;
+	}
+	else {
+	    ERTS_HLT_ASSERT(is_atom(rcvr));
+	    roflgs |= ERTS_TMR_ROFLG_REG_NAME;
+	    tmr->receiver.name = rcvr;
+	    refc = 1;
+	}
+
+	hsz = is_immed(msg) ? ((Uint) 0) : size_object(msg);
+	if (!hsz) {
+	    tmr->btm.message = msg;
+	    tmr->btm.bp = NULL;
+	}
+	else {
+	    ErlHeapFragment *bp = new_message_buffer(hsz);
+	    Eterm *hp = bp->mem;
+	    tmr->btm.message = copy_struct(msg, hsz, &hp, &bp->off_heap);
+	    tmr->btm.bp = bp;
+	}
+	tmr->btm.refn[0] = refn[0];
+	tmr->btm.refn[1] = refn[1];
+	tmr->btm.refn[2] = refn[2];
+
+	tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+	if (is_abif_tmr) {
+	    Process *aproc;
+	    roflgs |= ERTS_TMR_ROFLG_ABIF_TMR;
+	    tmr->abtm.accessor = acsr;
+	    aproc = erts_proc_lookup(acsr);
+	    if (!aproc)
+		tmr->abtm.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+	    else {
+		refc++;
+		erts_smp_proc_lock(aproc, ERTS_PROC_LOCK_BTM);
+		abtm_rbt_insert(&aproc->accessor_bif_timers, tmr);
+		erts_smp_proc_unlock(aproc, ERTS_PROC_LOCK_BTM);
+	    }
+	}
+    }
+
+    tmr->head.roflgs = roflgs;
+    erts_smp_atomic32_init_nob(&tmr->head.refc, refc);
+    erts_smp_atomic32_init_nob(&tmr->state, ERTS_TMR_STATE_ACTIVE);
+
+    ERTS_HLT_HDBG_CHK_SRV(srv);
+
+    if (!srv->next_timeout
+	|| tmr->timeout < srv->next_timeout->timeout) {
+	if (srv->next_timeout)
+	    erts_twheel_cancel_timer(esdp->timer_wheel,
+				     &srv->service_timer);
+	erts_twheel_set_timer(esdp->timer_wheel,
+			      &srv->service_timer,
+			      hlt_service_timeout,
+			      NULL,
+			      (void *) esdp,
+			      tmr->timeout);
+	srv->next_timeout = tmr;
+    }
+
+    st_tmr = time_rbt_lookup_insert(&srv->time_tree, tmr);
+    tmr->time.tree.same_time = st_tmr;
+    if (st_tmr)
+	same_time_list_insert(&st_tmr->time.tree.same_time, tmr);
+
+    if (is_bif_tmr)
+	btm_rbt_insert(&srv->btm_tree, tmr);
+
+#ifdef ERTS_HLT_HARD_DEBUG
+    tmr->pending_timeout = 0;
+#endif
+
+    ERTS_HLT_HDBG_CHK_SRV(srv);
+
+    return tmr;
+}
+
+static ERTS_INLINE void
+hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs)
+{
+    ErtsProcLocks proc_locks = ERTS_PROC_LOCKS_MSG_SEND;
+    Process *proc;
+    int dec_refc = 0;
+    Uint32 is_reg_name = (roflgs & ERTS_TMR_ROFLG_REG_NAME);
+    ERTS_HLT_ASSERT(roflgs & ERTS_TMR_ROFLG_BIF_TMR);
+
+    if (tmr->head.roflgs & ERTS_TMR_ROFLG_ABIF_TMR)
+	hlt_delete_abtm(tmr);
+
+    if (is_reg_name) {
+	Eterm pid;
+	ERTS_HLT_ASSERT(is_atom(tmr->receiver.name));
+	pid = erts_whereis_name_to_id(NULL, tmr->receiver.name);
+	proc = erts_proc_lookup(pid);
+    }
+    else {
+	ERTS_HLT_ASSERT(roflgs & ERTS_TMR_ROFLG_PROC);
+	ERTS_HLT_ASSERT(tmr->receiver.proc);
+
+	proc = tmr->receiver.proc;
+	proc_locks |= ERTS_PROC_LOCK_BTM;
+    }
+    if (proc) {
+	erts_smp_proc_lock(proc, proc_locks);
+	/*
+	 * If process is exiting, let it clean up
+	 * the btm tree by itself (it may be in
+	 * the middle of tree destruction).
+	 */
+	if (!ERTS_PROC_IS_EXITING(proc)) {
+	    erts_queue_message(proc,
+			       &proc_locks,
+			       tmr->btm.bp,
+			       tmr->btm.message,
+			       NIL
+#ifdef USE_VM_PROBES
+			       , NIL
+#endif
+		);
+	    erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_SEND);
+	    proc_locks &= ~ERTS_PROC_LOCKS_MSG_SEND;
+	    tmr->btm.bp = NULL;
+	    if (tmr->btm.proc_tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) {
+		proc_btm_rbt_delete(&proc->bif_timers, tmr);
+		tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+		dec_refc = 1;
+	    }
+	}
+	if (proc_locks)
+	    erts_smp_proc_unlock(proc, proc_locks);
+	if (dec_refc)
+	    hl_timer_pre_dec_refc(tmr);
+    }
+}
+
+static ERTS_INLINE void
+hlt_proc_timeout(ErtsHLTimer *tmr)
+{
+    if (proc_timeout_common(tmr->receiver.proc, (void *) tmr))
+	hl_timer_dec_refc(tmr, tmr->head.roflgs);
+}
+
+static ERTS_INLINE void
+hlt_port_timeout(ErtsHLTimer *tmr)
+{
+    if (port_timeout_common(tmr->receiver.port, (void *) tmr))
+	hl_timer_dec_refc(tmr, tmr->head.roflgs);
+}
+
+static void hlt_timeout(ErtsHLTimer *tmr, void *vsrv)
+{
+    ErtsHLTimerService *srv = (ErtsHLTimerService *) vsrv;
+    Uint32 roflgs;
+    erts_aint32_t state;
+
+    ERTS_HLT_HDBG_CHK_SRV(srv);
+
+    roflgs = tmr->head.roflgs;
+    ERTS_HLT_ASSERT(roflgs & ERTS_TMR_ROFLG_HLT);
+
+    state = erts_smp_atomic32_cmpxchg_acqb(&tmr->state,
+					   ERTS_TMR_STATE_TIMED_OUT,
+					   ERTS_TMR_STATE_ACTIVE);
+
+    ERTS_HLT_ASSERT(state == ERTS_TMR_STATE_CANCELED
+		    || state == ERTS_TMR_STATE_ACTIVE);
+
+    if (state == ERTS_TMR_STATE_ACTIVE) {
+
+	if (roflgs & ERTS_TMR_ROFLG_BIF_TMR)
+	    hlt_bif_timer_timeout(tmr, roflgs);
+	else if (roflgs & ERTS_TMR_ROFLG_PROC)
+	    hlt_proc_timeout(tmr);
+	else {
+	    ERTS_HLT_ASSERT(roflgs & ERTS_TMR_ROFLG_PORT);
+	    hlt_port_timeout(tmr);
+	}
+    }
+
+    tmr->time.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+    if ((roflgs & ERTS_TMR_ROFLG_BIF_TMR)
+	&& tmr->btm.tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) {
+	btm_rbt_delete(&srv->btm_tree, tmr);
+	tmr->btm.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+    }
+
+    ERTS_HLT_HDBG_CHK_SRV(srv);
+
+    hl_timer_dec_refc(tmr, roflgs);
+}
+
+#ifdef ERTS_HLT_HARD_DEBUG
+static void
+set_pending_timeout(ErtsHLTimer *tmr, void *unused)
+{
+    tmr->pending_timeout = -1;
+}
+#endif
+
+static void
+hlt_service_timeout(void *vesdp)
+{
+    ErtsSchedulerData *esdp = (ErtsSchedulerData *) vesdp;
+    ErtsHLTimerService *srv = esdp->timer_service;
+    ErtsHLTimer *tmr = srv->next_timeout;
+    int yield;
+
+    ERTS_HLT_HDBG_CHK_SRV(srv);
+
+    ERTS_HLT_ASSERT(esdp == erts_get_scheduler_data());
+
+    ERTS_HLT_ASSERT(!srv->yield.root || srv->yield.root == tmr);
+    ERTS_HLT_ASSERT(tmr);
+    ERTS_HLT_ASSERT(tmr->timeout <= erts_get_monotonic_time(esdp));
+
+    if (!srv->yield.root) {
+	ERTS_HLT_ASSERT(tmr->time.tree.parent
+			!= ERTS_HLT_PFIELD_NOT_IN_TABLE);
+	time_rbt_delete(&srv->time_tree, tmr);
+	tmr->time.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+#ifdef ERTS_HLT_HARD_DEBUG
+	tmr->pending_timeout = 1;
+	if (tmr->time.tree.same_time)
+	    same_time_list_foreach(tmr->time.tree.same_time, set_pending_timeout, NULL);
+#endif
+    }
+
+    if (!tmr->time.tree.same_time && !srv->yield.root)
+	yield = 0;
+    else {
+	yield = same_time_list_foreach_destroy_yielding(
+	    &tmr->time.tree.same_time, hlt_timeout, (void *) srv,
+	    &srv->yield.state, ERTS_TMR_TIMEOUT_YIELD_LIMIT);
+    }
+
+    if (yield)
+	srv->yield.root = tmr;
+    else {
+	srv->yield.root = NULL;
+	hlt_timeout(tmr, (void *) srv);
+
+	tmr = time_rbt_smallest(srv->time_tree);
+	srv->next_timeout = tmr;
+    }
+
+    ERTS_HLT_HDBG_CHK_SRV(srv);
+
+    if (tmr)
+	erts_twheel_set_timer(esdp->timer_wheel,
+			      &srv->service_timer,
+			      hlt_service_timeout,
+			      NULL,
+			      vesdp,
+			      tmr->timeout);
+}
+
+static void
+hlt_delete_timer(ErtsSchedulerData *esdp, ErtsHLTimer *tmr)
+{
+    ErtsHLTimerService *srv = esdp->timer_service;
+
+    ERTS_HLT_HDBG_CHK_SRV(srv);
+
+    if (tmr->head.roflgs & ERTS_TMR_ROFLG_BIF_TMR) {
+
+	if (tmr->btm.tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) {
+	    btm_rbt_delete(&srv->btm_tree, tmr);
+	    tmr->btm.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+	}
+
+	if (tmr->head.roflgs & ERTS_TMR_ROFLG_ABIF_TMR)
+	    hlt_delete_abtm(tmr);
+    }
+
+    if (tmr->time.tree.parent == ERTS_HLT_PFIELD_NOT_IN_TABLE) {
+	/* Already removed... */
+	ERTS_HLT_HDBG_CHK_SRV(srv);
+	return;
+    }
+
+    if (tmr->time.tree.parent & ERTS_HLT_PFLG_SAME_TIME) {
+	same_time_list_delete(tmr);
+    }
+    else if (tmr->time.tree.same_time) {
+	ErtsHLTimer *st_container;
+
+	ERTS_HLT_ASSERT((tmr->time.tree.parent & ERTS_HLT_PFLG_SAME_TIME) == 0);
+	st_container = tmr->time.tree.same_time->time.tree.u.l.prev;
+
+	ERTS_HLT_ASSERT(st_container);
+	ERTS_HLT_ASSERT(st_container->time.tree.parent
+			 & ERTS_HLT_PFLG_SAME_TIME);
+	ERTS_HLT_ASSERT(tmr->timeout == st_container->timeout);
+
+	same_time_list_delete(st_container);
+	st_container->time.tree.same_time = tmr->time.tree.same_time;
+	same_time_list_new_root(&st_container->time.tree.same_time);
+
+	time_rbt_replace(&srv->time_tree, tmr, st_container);
+	ERTS_HLT_ASSERT((st_container->time.tree.parent
+			 & ERTS_HLT_PFLG_SAME_TIME) == 0);
+
+	if (srv->next_timeout == tmr)
+	    srv->next_timeout = st_container;
+    }
+    else {
+	ERTS_HLT_ASSERT((tmr->time.tree.parent & ERTS_HLT_PFLG_SAME_TIME) == 0);
+	time_rbt_delete(&srv->time_tree, tmr);
+	if (tmr == srv->next_timeout) {
+	    ErtsHLTimer *smlst;
+	    erts_twheel_cancel_timer(esdp->timer_wheel,
+				     &srv->service_timer);
+	    smlst = time_rbt_smallest(srv->time_tree);
+	    srv->next_timeout = smlst;
+	    if (smlst) {
+		ERTS_HLT_ASSERT(smlst->timeout > tmr->timeout);
+		erts_twheel_set_timer(esdp->timer_wheel,
+				      &srv->service_timer,
+				      hlt_service_timeout,
+				      NULL,
+				      (void *) esdp,
+				      smlst->timeout);
+	    }
+	}
+    }
+    tmr->time.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+
+    hl_timer_dec_refc(tmr, tmr->head.roflgs);
+
+    ERTS_HLT_HDBG_CHK_SRV(srv);
+}
+
+/*
+ * Pass canceled timers back to originating scheduler
+ */
+
+static ERTS_INLINE void
+cleanup_sched_local_canceled_timer(ErtsSchedulerData *esdp,
+				   ErtsTimer *tmr)
+{
+    Uint32 roflgs = tmr->head.roflgs;
+    ERTS_HLT_ASSERT(esdp == erts_get_scheduler_data());
+    ERTS_HLT_ASSERT((tmr->head.roflgs & ERTS_TMR_ROFLG_SID_MASK)
+		    == (Uint32) esdp->no);
+    if (roflgs & ERTS_TMR_ROFLG_HLT) {
+	hlt_delete_timer(esdp, &tmr->hlt);
+	hl_timer_dec_refc(&tmr->hlt, roflgs);
+    }
+    else {
+	cancel_tw_timer(esdp, &tmr->twt);
+	tw_timer_dec_refc(&tmr->twt);
+    }
+}
+
+#ifdef ERTS_SMP
+
+static void
+init_canceled_queue(ErtsHLTCncldTmrQ *cq)
+{
+    erts_atomic_init_nob(&cq->tail.data.marker.u.next, ERTS_AINT_NULL);
+    erts_atomic_init_nob(&cq->tail.data.last,
+			 (erts_aint_t) &cq->tail.data.marker);
+    cq->head.first = (ErtsTimer *) &cq->tail.data.marker;
+    cq->head.unref_end = (ErtsTimer *) &cq->tail.data.marker;
+    cq->head.next.thr_progress = erts_thr_progress_current();
+    cq->head.next.thr_progress_reached = 1;
+    cq->head.next.unref_end = (ErtsTimer *) &cq->tail.data.marker;
+    cq->head.used_marker = 1;
+}
+
+static ERTS_INLINE int
+cq_enqueue(ErtsHLTCncldTmrQ *cq, ErtsTimer *tmr, int cinit)
+{
+    erts_aint_t itmp;
+    ErtsTimer *enq, *this = tmr;
+
+    erts_atomic_init_nob(&this->head.u.next, ERTS_AINT_NULL);
+    /* Enqueue at end of list... */
+
+    enq = (ErtsTimer *) erts_atomic_read_nob(&cq->tail.data.last);
+    itmp = erts_atomic_cmpxchg_relb(&enq->head.u.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->head.u.next));
+	ASSERT(((erts_aint_t) enq)
+	       == erts_atomic_xchg_relb(&cq->tail.data.last,
+					(erts_aint_t) this));
+#else
+	erts_atomic_set_relb(&cq->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;
+
+	while (1) {
+	    erts_aint_t itmp2;
+	    erts_atomic_set_nob(&this->head.u.next, itmp);
+	    itmp2 = erts_atomic_cmpxchg_relb(&enq->head.u.next,
+					     (erts_aint_t) this,
+					     itmp);
+	    if (itmp == itmp2)
+		return 0; /* inserted this */
+	    if ((i & 1) == 0)
+		itmp = itmp2;
+	    else {
+		enq = (ErtsTimer *) itmp2;
+		itmp = erts_atomic_read_acqb(&enq->head.u.next);
+		ASSERT(itmp != ERTS_AINT_NULL);
+	    }
+	    i++;
+	}
+    }
+}
+
+static ERTS_INLINE erts_aint_t
+check_insert_marker(ErtsHLTCncldTmrQ *cq, erts_aint_t ilast)
+{
+    if (!cq->head.used_marker
+	&& cq->head.unref_end == (ErtsTimer *) ilast) {
+	erts_aint_t itmp;
+	ErtsTimer *last = (ErtsTimer *) ilast;
+
+	erts_atomic_init_nob(&cq->tail.data.marker.u.next, ERTS_AINT_NULL);
+	itmp = erts_atomic_cmpxchg_relb(&last->head.u.next,
+					(erts_aint_t) &cq->tail.data.marker,
+					ERTS_AINT_NULL);
+	if (itmp == ERTS_AINT_NULL) {
+	    ilast = (erts_aint_t) &cq->tail.data.marker;
+	    cq->head.used_marker = !0;
+	    erts_atomic_set_relb(&cq->tail.data.last, ilast);
+	}
+    }
+    return ilast;
+}
+
+static ERTS_INLINE ErtsTimer *
+cq_dequeue(ErtsHLTCncldTmrQ *cq)
+{
+    ErtsTimer *tmr;
+
+    if (cq->head.first == cq->head.unref_end)
+	return NULL;
+
+    tmr = cq->head.first;
+    if (tmr == (ErtsTimer *) &cq->tail.data.marker) {
+	ASSERT(cq->head.used_marker);
+	cq->head.used_marker = 0;
+	tmr = (ErtsTimer *) erts_atomic_read_nob(&tmr->head.u.next);
+	if (tmr == cq->head.unref_end) {
+	    cq->head.first = tmr;
+	    return NULL;
+	}
+    }
+
+    cq->head.first = (ErtsTimer *) erts_atomic_read_nob(&tmr->head.u.next);
+
+    ASSERT(cq->head.first);
+
+    return tmr;
+}
+
+static int
+cq_check_incoming(ErtsSchedulerData *esdp, ErtsHLTCncldTmrQ *cq)
+{
+    erts_aint_t ilast = erts_atomic_read_nob(&cq->tail.data.last);
+    if (((ErtsTimer *) ilast) == (ErtsTimer *) &cq->tail.data.marker
+	&& cq->head.first == (ErtsTimer *) &cq->tail.data.marker) {
+	/* Nothing more to do... */
+	return 0;
+    }
+
+    if (cq->head.next.thr_progress_reached
+	|| erts_thr_progress_has_reached(cq->head.next.thr_progress)) {
+	cq->head.next.thr_progress_reached = 1;
+	/* Move unreferenced end pointer forward... */
+
+	ERTS_HLT_SMP_MEMBAR_LoadLoad_LoadStore;
+
+	cq->head.unref_end = cq->head.next.unref_end;
+
+	ilast = check_insert_marker(cq, ilast);
+
+	if (cq->head.unref_end != (ErtsTimer *) ilast) {
+	    cq->head.next.unref_end = (ErtsTimer *) ilast;
+	    cq->head.next.thr_progress = erts_thr_progress_later(esdp);
+	    cq->head.next.thr_progress_reached = 0;
+	}
+    }
+    return 1;
+}
+
+static ERTS_INLINE void
+store_earliest_thr_prgr(ErtsThrPrgrVal *prev_val, ErtsHLTCncldTmrQ *cq)
+{
+    if (!cq->head.next.thr_progress_reached
+	&& (*prev_val == ERTS_THR_PRGR_INVALID
+	    || erts_thr_progress_cmp(cq->head.next.thr_progress,
+				     *prev_val) < 0)) {
+	*prev_val = cq->head.next.thr_progress;
+    }
+}
+
+static void
+handle_canceled_queue(ErtsSchedulerData *esdp,
+		      ErtsHLTCncldTmrQ *cq,
+		      int use_limit,
+		      int ops_limit,
+		      int *need_thr_progress,
+		      ErtsThrPrgrVal *thr_prgr_p,
+		      int *need_more_work)
+{
+    int need_thr_prgr = 0;
+    int need_mr_wrk = 0;
+    int have_checked_incoming = 0;
+    int ops = 0;
+
+    ERTS_HLT_ASSERT(cq == &esdp->timer_service->canceled_queue);
+
+    while (1) {
+	ErtsTimer *tmr = cq_dequeue(cq);
+
+	if (tmr)
+	    cleanup_sched_local_canceled_timer(esdp, tmr);
+	else {
+	    if (have_checked_incoming)
+		break;
+	    need_thr_prgr = cq_check_incoming(esdp, cq);
+	    if (need_thr_progress) {
+		*need_thr_progress |= need_thr_prgr;
+		if (need_thr_prgr)
+		    store_earliest_thr_prgr(thr_prgr_p, cq);
+	    }
+	    have_checked_incoming = 1;
+	    continue;
+	}
+
+	if (use_limit && ++ops >= ops_limit) {
+	    if (cq->head.first != cq->head.unref_end) {
+		need_mr_wrk = 1;
+		if (need_more_work)
+		    *need_more_work |= 1;
+	    }
+	    break;
+	}
+    }
+
+    if (need_thr_progress && !(need_thr_prgr | need_mr_wrk)) {
+	need_thr_prgr = cq_check_incoming(esdp, cq);
+	*need_thr_progress |= need_thr_prgr;
+	if (need_thr_prgr)
+	    store_earliest_thr_prgr(thr_prgr_p, cq);
+    }
+}
+
+void
+erts_handle_canceled_timers(void *vesdp,
+			    int *need_thr_progress,
+			    ErtsThrPrgrVal *thr_prgr_p,
+			    int *need_more_work)
+{
+    ErtsSchedulerData *esdp = (ErtsSchedulerData *) vesdp;
+    ERTS_HLT_ASSERT(esdp == erts_get_scheduler_data());
+
+    handle_canceled_queue(esdp, &esdp->timer_service->canceled_queue,
+			  1, ERTS_TMR_CANCELED_TIMER_LIMIT,
+			  need_thr_progress, thr_prgr_p,
+			  need_more_work);
+}
+
+#endif /* ERTS_SMP */
+
+static void
+queue_canceled_timer(ErtsSchedulerData *esdp, int rsched_id, ErtsTimer *tmr)
+{
+#ifdef ERTS_SMP
+    ErtsHLTCncldTmrQ *cq;
+    cq = &ERTS_SCHEDULER_IX(rsched_id-1)->timer_service->canceled_queue;
+    if (cq_enqueue(cq, tmr, rsched_id - (int) esdp->no))
+	erts_notify_canceled_timer(esdp, rsched_id);
+#else
+    ERTS_INTERNAL_ERROR("Unexpected enqueue of canceled timer");
+#endif
+}
+
+static void
+continue_cancel_ptimer(ErtsSchedulerData *esdp, ErtsTimer *tmr)
+{
+#ifdef ERTS_SMP
+    Uint32 sid = (tmr->head.roflgs & ERTS_TMR_ROFLG_SID_MASK);
+
+    if (esdp->no != sid)
+	queue_canceled_timer(esdp, sid, tmr);
+    else
+#endif
+	cleanup_sched_local_canceled_timer(esdp, tmr);
+}
+
+/*
+ * BIF timer specific
+ */
+
+Uint erts_bif_timer_memory_size(void)
+{
+    return (Uint) 0;
+}
+
+static BIF_RETTYPE
+setup_bif_timer(Process *c_p, ErtsMonotonicTime timeout_pos,
+		int short_time, Eterm rcvr, Eterm acsr,
+		Eterm msg, int wrap)
+{
+    BIF_RETTYPE ret;
+    Eterm ref, tmo_msg, *hp;
+    ErtsHLTimer *tmr;
+    ErtsSchedulerData *esdp;
+    DeclareTmpHeap(tmp_hp, 4, c_p);
+
+    if (is_not_internal_pid(rcvr) && is_not_atom(rcvr))
+	goto badarg;
+
+    esdp = ERTS_PROC_GET_SCHDATA(c_p);
+
+    hp = HAlloc(c_p, REF_THING_SIZE);
+    ref = erts_sched_make_ref_in_buffer(esdp, hp);
+
+    ASSERT(erts_get_ref_numbers_thr_id(
+	       internal_ref_numbers(ref)) == (Uint32) esdp->no);
+
+    UseTmpHeap(4, c_p);
+
+    tmo_msg = wrap ? TUPLE3(tmp_hp, am_timeout, ref, msg) : msg;
+
+    tmr = create_hl_timer(esdp, timeout_pos, short_time, 1, NULL, 
+			  rcvr, acsr, tmo_msg, internal_ref_numbers(ref));
+
+    UnUseTmpHeap(4, c_p);
+
+    if (is_internal_pid(rcvr)) {
+	Process *proc = erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
+					  rcvr, ERTS_PROC_LOCK_BTM,
+					  ERTS_P2P_FLG_INC_REFC);
+	if (!proc) {
+	    hlt_delete_timer(esdp, tmr);
+	    hl_timer_destroy(tmr);
+	}
+	else {
+	    proc_btm_rbt_insert(&proc->bif_timers, tmr);
+	    erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_BTM);
+	    tmr->receiver.proc = proc;
+	}
+    }
+
+    ERTS_BIF_PREP_RET(ret, ref);
+    return ret;
+
+badarg:
+
+    ERTS_BIF_PREP_ERROR(ret, c_p, BADARG);
+    return ret;
+}
+
+static int
+cancel_bif_timer(ErtsHLTimer *tmr)
+{
+    erts_aint_t state;
+    Uint32 roflgs;
+    int res;
+
+    state = erts_smp_atomic32_cmpxchg_acqb(&tmr->state,
+					   ERTS_TMR_STATE_CANCELED,
+					   ERTS_TMR_STATE_ACTIVE);
+    if (state != ERTS_TMR_STATE_ACTIVE)
+	return 0;
+
+    res = -1;
+
+    roflgs = tmr->head.roflgs;
+    if (roflgs & ERTS_TMR_ROFLG_PROC) {
+	Process *proc = tmr->receiver.proc;
+	ERTS_HLT_ASSERT(!(tmr->head.roflgs & ERTS_TMR_ROFLG_REG_NAME));
+
+	erts_smp_proc_lock(proc, ERTS_PROC_LOCK_BTM);
+	/*
+	 * If process is exiting, let it clean up
+	 * the btm tree by itself (it may be in
+	 * the middle of tree destruction).
+	 */
+	if (!ERTS_PROC_IS_EXITING(proc)
+	    && tmr->btm.proc_tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) {
+	    proc_btm_rbt_delete(&proc->bif_timers, tmr);
+	    tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+	    res = 1;
+	}
+	erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_BTM);
+    }
+
+    return res;
+}
+
+static ERTS_INLINE Eterm
+access_sched_local_btm(Process *c_p, Eterm pid,
+		       Eterm tref, Uint32 *trefn,
+		       Uint32 *rrefn,
+		       int async, int cancel,
+		       int return_res,
+		       int info)
+{
+    ErtsSchedulerData *esdp;
+    ErtsHLTimerService *srv;
+    ErtsHLTimer *tmr;
+    Sint64 time_left;
+    Process *proc;
+    ErtsProcLocks proc_locks;
+
+    time_left = -1;
+
+    if (!c_p)
+	esdp = erts_get_scheduler_data();
+    else {
+	esdp = ERTS_PROC_GET_SCHDATA(c_p);
+	ERTS_HLT_ASSERT(esdp == erts_get_scheduler_data());
+    }
+
+    ERTS_HLT_ASSERT(erts_get_ref_numbers_thr_id(trefn)
+		    == (Uint32) esdp->no);
+
+    srv = esdp->timer_service;
+
+    tmr = btm_rbt_lookup(srv->btm_tree, trefn);
+    if (tmr) {
+	if (!cancel) {
+	    erts_aint32_t state = erts_smp_atomic32_read_acqb(&tmr->state);
+	    if (state == ERTS_TMR_STATE_ACTIVE)
+		time_left = get_time_left(esdp, tmr->timeout);
+	}
+	else {
+	    int cncl_res = cancel_bif_timer(tmr);
+	    if (cncl_res) {
+
+		time_left = get_time_left(esdp, tmr->timeout);
+
+		if (cncl_res > 0)
+		    hl_timer_dec_refc(tmr, tmr->head.roflgs);
+
+		hlt_delete_timer(esdp, tmr);
+	    }
+	}
+    }
+
+    if (!info)
+	return am_ok;
+
+    if (return_res) {
+	ERTS_HLT_ASSERT(c_p);
+	if (time_left < 0)
+	    return am_false;
+	else if (time_left <= (Sint64) MAX_SMALL)
+	    return make_small((Sint) time_left);
+	else {
+	    Uint hsz = ERTS_SINT64_HEAP_SIZE(time_left);
+	    Eterm *hp = HAlloc(c_p, hsz);
+	    return erts_sint64_to_big(time_left, &hp);
+	}
+    }
+
+    if (c_p) {
+	proc = c_p;
+	proc_locks = ERTS_PROC_LOCK_MAIN;
+    }
+    else {
+	proc = erts_proc_lookup(pid);
+	proc_locks = 0;
+    }
+
+    if (proc) {
+	Uint hsz;
+	ErlOffHeap *ohp;
+	ErlHeapFragment* bp;
+	Eterm *hp, msg, ref, result;
+#ifdef ERTS_HLT_DEBUG
+	Eterm *hp_end;
+#endif
+
+	hsz = 3; /* 2-tuple */
+	if (!async)
+	    hsz += REF_THING_SIZE;
+	else {
+	    if (is_non_value(tref))
+		hsz += REF_THING_SIZE;
+	    hsz += 1; /* upgrade to 3-tuple */
+	}
+	if (time_left > (Sint64) MAX_SMALL)
+	    hsz += ERTS_SINT64_HEAP_SIZE(time_left);
+
+	hp = erts_alloc_message_heap(hsz,
+				     &bp,
+				     &ohp,
+				     proc,
+				     &proc_locks);
+
+#ifdef ERTS_HLT_DEBUG
+	hp_end = hp + hsz;
+#endif
+
+	if (time_left < 0)
+	    result = am_false;
+	else if (time_left <= (Sint64) MAX_SMALL)
+	    result = make_small((Sint) time_left);
+	else
+	    result = erts_sint64_to_big(time_left, &hp);
+
+	if (!async) {
+	    write_ref_thing(hp,
+			    rrefn[0],
+			    rrefn[1],
+			    rrefn[2]);
+	    ref = make_internal_ref(hp);
+	    hp += REF_THING_SIZE;
+	    msg = TUPLE2(hp, ref, result);
+
+	    ERTS_HLT_ASSERT(hp + 3 == hp_end);
+	}
+	else {
+	    Eterm tag = cancel ? am_cancel_timer : am_read_timer;
+	    if (is_value(tref))
+		ref = tref;
+	    else {
+		write_ref_thing(hp,
+				trefn[0],
+				trefn[1],
+				trefn[2]);
+		ref = make_internal_ref(hp);
+		hp += REF_THING_SIZE;
+	    }
+	    msg = TUPLE3(hp, tag, ref, result);
+
+	    ERTS_HLT_ASSERT(hp + 4 == hp_end);
+
+	}
+	erts_queue_message(proc, &proc_locks, bp,
+			   msg, NIL
+#ifdef USE_VM_PROBES
+			   , NIL
+#endif
+	    );
+
+	if (c_p)
+	    proc_locks &= ~ERTS_PROC_LOCK_MAIN;
+	if (proc_locks)
+	    erts_smp_proc_unlock(proc, proc_locks);
+    }
+
+    return am_ok;
+}
+
+#define ERTS_BTM_REQ_FLG_ASYNC		(((Uint32) 1) << 0)
+#define ERTS_BTM_REQ_FLG_CANCEL		(((Uint32) 1) << 1)
+#define ERTS_BTM_REQ_FLG_INFO		(((Uint32) 1) << 2)
+
+typedef struct {
+    Eterm pid;
+    Uint32 trefn[ERTS_REF_NUMBERS];
+    Uint32 rrefn[ERTS_REF_NUMBERS];
+    Uint32 flags;
+} ErtsBifTimerRequest;
+
+static void
+bif_timer_access_request(void *vreq)
+{
+    ErtsBifTimerRequest *req = (ErtsBifTimerRequest *) vreq;
+    int async = (int) (req->flags & ERTS_BTM_REQ_FLG_ASYNC);
+    int cancel = (int) (req->flags & ERTS_BTM_REQ_FLG_CANCEL);
+    int info = (int) (req->flags & ERTS_BTM_REQ_FLG_INFO);
+    (void) access_sched_local_btm(NULL, req->pid, THE_NON_VALUE,
+				  req->trefn, req->rrefn, async,
+				  cancel, 0, info);
+    erts_free(ERTS_ALC_T_TIMER_REQUEST, vreq);
+}
+
+static int
+try_access_sched_remote_btm(ErtsSchedulerData *esdp,
+			    Process *c_p, Uint32 sid,
+			    Eterm tref, Uint32 *trefn,
+			    int async, int cancel,
+			    int info, Eterm *resp)
+{
+    ErtsHLTimer *tmr;
+    Sint64 time_left;
+
+    ERTS_HLT_ASSERT(c_p);
+
+    /*
+     * Check if the timer is aimed at current
+     * process of if this process is an accessor
+     * of the timer...
+     */
+    erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_BTM);
+    tmr = proc_btm_rbt_lookup(c_p->bif_timers, trefn);
+    if (!tmr)
+	tmr = abtm_rbt_lookup(c_p->accessor_bif_timers, trefn);
+    erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_BTM);
+    if (!tmr)
+	return 0;
+
+    if (!cancel) {
+	erts_aint32_t state = erts_smp_atomic32_read_acqb(&tmr->state);
+	if (state == ERTS_TMR_STATE_ACTIVE)
+	    time_left = get_time_left(esdp, tmr->timeout);
+	else
+	    time_left = -1;
+    }
+    else {
+	int cncl_res = cancel_bif_timer(tmr);
+	if (!cncl_res)
+	    time_left = -1;
+	else {
+	    time_left = get_time_left(esdp, tmr->timeout);
+	    if (cncl_res > 0)
+		queue_canceled_timer(esdp, sid, (ErtsTimer *) tmr);
+	}
+    }
+
+    if (!info) {
+	*resp = am_ok;
+	return 1;
+    }
+
+    if (!async) {
+	if (time_left < 0)
+	    *resp = am_false;
+	else if (time_left <= (Sint64) MAX_SMALL)
+	    *resp = make_small((Sint) time_left);
+	else {
+	    Uint hsz = ERTS_SINT64_HEAP_SIZE(time_left);
+	    Eterm *hp = HAlloc(c_p, hsz);
+	    *resp = erts_sint64_to_big(time_left, &hp);
+	}
+    }
+    else {
+	Eterm tag, res, msg;
+	ErlOffHeap *ohp;
+	ErlHeapFragment* bp;
+	Uint hsz;
+	Eterm *hp;
+	ErtsProcLocks proc_locks = ERTS_PROC_LOCK_MAIN;
+
+	hsz = 4;
+	if (time_left > (Sint64) MAX_SMALL)
+	    hsz += ERTS_SINT64_HEAP_SIZE(time_left);
+
+	hp = erts_alloc_message_heap(hsz,
+				     &bp,
+				     &ohp,
+				     c_p,
+				     &proc_locks);
+	if (cancel)
+	    tag = am_cancel_timer;
+	else
+	    tag = am_read_timer;
+
+	if (time_left < 0)
+	    res = am_false;
+	else if (time_left <= (Sint64) MAX_SMALL)
+	    res = make_small((Sint) time_left);
+	else
+	    res = erts_sint64_to_big(time_left, &hp);
+
+	msg = TUPLE3(hp, tag, tref, res);
+
+	erts_queue_message(c_p, &proc_locks, bp,
+			   msg, NIL
+#ifdef USE_VM_PROBES
+			   , NIL
+#endif
+	    );
+
+	proc_locks &= ~ERTS_PROC_LOCK_MAIN;
+	if (proc_locks)
+	    erts_smp_proc_unlock(c_p, proc_locks);
+
+	*resp = am_ok;
+    }
+    return 1;
+}
+
+static BIF_RETTYPE
+access_bif_timer(Process *c_p, Eterm tref, int cancel, int async, int info)
+{
+    BIF_RETTYPE ret;
+    ErtsSchedulerData *esdp;
+    Uint32 sid;
+    Uint32 *trefn;
+    Eterm res;
+
+    if (is_not_internal_ref(tref)) {
+	if (is_not_ref(tref))
+	    goto badarg;
+	else
+	    goto no_timer;
+    }
+
+    esdp = ERTS_PROC_GET_SCHDATA(c_p);
+
+    trefn = internal_ref_numbers(tref);
+    sid = erts_get_ref_numbers_thr_id(trefn);
+    if (sid < 1 || erts_no_schedulers < sid)
+	goto no_timer;
+
+    if (sid == (Uint32) esdp->no) {
+	res = access_sched_local_btm(c_p, c_p->common.id,
+				     tref, trefn, NULL,
+				     async, cancel, !async,
+				     info);
+	ERTS_BIF_PREP_RET(ret, res);
+    }
+    else if (try_access_sched_remote_btm(esdp, c_p, sid,
+					 tref, trefn,
+					 async, cancel,
+					 info, &res)) {
+	ERTS_BIF_PREP_RET(ret, res);
+    }
+    else {
+	/*
+	 * Schedule access for execution on
+	 * remote scheduler...
+	 */
+	ErtsBifTimerRequest *req = erts_alloc(ERTS_ALC_T_TIMER_REQUEST,
+					      sizeof(ErtsBifTimerRequest));
+
+	req->flags = 0;
+	if (cancel)
+	    req->flags |= ERTS_BTM_REQ_FLG_CANCEL;
+	if (async)
+	    req->flags |= ERTS_BTM_REQ_FLG_ASYNC;
+	if (info)
+	    req->flags |= ERTS_BTM_REQ_FLG_INFO;
+
+	req->pid = c_p->common.id;
+
+	req->trefn[0] = trefn[0];
+	req->trefn[1] = trefn[1];
+	req->trefn[2] = trefn[2];
+
+	if (async)
+	    ERTS_BIF_PREP_RET(ret, am_ok);
+	else {
+	    Eterm *hp, rref;
+	    Uint32 *rrefn;
+
+	    hp = HAlloc(c_p, REF_THING_SIZE);
+	    rref = erts_sched_make_ref_in_buffer(esdp, hp);
+	    rrefn = internal_ref_numbers(rref);
+
+	    req->rrefn[0] = rrefn[0];
+	    req->rrefn[1] = rrefn[1];
+	    req->rrefn[2] = rrefn[2];
+
+	    erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
+
+	    if (ERTS_PROC_PENDING_EXIT(c_p))
+		ERTS_VBUMP_ALL_REDS(c_p);
+	    else {
+		/*
+		 * Caller needs to wait for a message containing
+		 * the ref that we just created. No such message
+		 * can exist in callers message queue at this time.
+		 * We therefore move the save pointer of the
+		 * callers message queue to the end of the queue.
+		 *
+		 * NOTE: It is of vital importance that the caller
+		 *       immediately do a receive unconditionaly
+		 *       waiting for the message with the reference;
+		 *       otherwise, next receive will *not* work
+		 *       as expected!
+		 */
+		ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
+		c_p->msg.save = c_p->msg.last;
+	    }
+	    erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
+
+	    ERTS_BIF_PREP_TRAP1(ret, erts_await_result, c_p, rref);
+	}
+
+	erts_schedule_misc_aux_work(sid,
+				    bif_timer_access_request,
+				    (void *) req);
+    }
+
+    return ret;
+
+badarg:
+    ERTS_BIF_PREP_ERROR(ret, c_p, BADARG);
+    return ret;
+
+no_timer:
+    ERTS_BIF_PREP_RET(ret, am_false);
+    return ret;
+    
+}
+
+static ERTS_INLINE int
+bool_arg(Eterm val, int *argp)
+{
+    switch (val) {
+    case am_true: *argp = 1; return 1;
+    case am_false: *argp = 0; return 1;
+    default: return 0;
+    }
+}
+
+static ERTS_INLINE int
+parse_bif_timer_options(Eterm option_list, int *async, int *info,
+			int *abs, Eterm *accessor)
+{
+    Eterm list = option_list;
+
+    if (async)
+	*async = 0;
+    if (info)
+	*info = 0;
+    if (abs)
+	*abs = 0;
+    if (accessor)
+	*accessor = THE_NON_VALUE;
+
+    while (is_list(list)) {
+	Eterm *consp, *tp, opt;
+
+	consp = list_val(list);
+	opt = CAR(consp);
+	if (is_not_tuple(opt))
+	    return 0;
+
+	tp = tuple_val(opt);
+	if (arityval(tp[0]) != 2)
+	    return 0;
+
+	switch (tp[1]) {
+	case am_async:
+	    if (!async || !bool_arg(tp[2], async))
+		return 0;
+	    break;
+	case am_info:
+	    if (!info || !bool_arg(tp[2], info))
+		return 0;
+	    break;
+	case am_abs:
+	    if (!abs || !bool_arg(tp[2], abs))
+		return 0;
+	    break;
+	case am_accessor:
+	    if (!accessor || is_not_internal_pid(tp[2]))
+		return 0;
+	    *accessor = tp[2];
+	    break;
+	default:
+	    return 0;
+	}
+
+	list = CDR(consp);	
+    }
+
+    if (is_not_nil(list))
+	return 0;
+    return 1;
+}
+
+static void
+exit_cancel_bif_timer(ErtsHLTimer *tmr, void *vesdp)
+{
+    ErtsSchedulerData *esdp = (ErtsSchedulerData *) vesdp;
+    Uint32 sid, roflgs;
+    erts_aint_t state;
+
+    state = erts_smp_atomic32_cmpxchg_acqb(&tmr->state,
+					   ERTS_TMR_STATE_CANCELED,
+					   ERTS_TMR_STATE_ACTIVE);
+
+    roflgs = tmr->head.roflgs;
+    sid = roflgs & ERTS_TMR_ROFLG_SID_MASK;
+
+    ERTS_HLT_ASSERT(sid == erts_get_ref_numbers_thr_id(tmr->btm.refn));
+    ERTS_HLT_ASSERT(tmr->btm.proc_tree.parent
+		    != ERTS_HLT_PFIELD_NOT_IN_TABLE);
+
+    tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+
+    if (sid == (Uint32) esdp->no) {
+	if (state == ERTS_TMR_STATE_ACTIVE)
+	    hlt_delete_timer(esdp, tmr);
+	hl_timer_dec_refc(tmr, roflgs);
+    }
+    else {
+	if (state == ERTS_TMR_STATE_ACTIVE)
+	    queue_canceled_timer(esdp, sid, (ErtsTimer *) tmr);
+	else
+	    hl_timer_dec_refc(tmr, roflgs);
+    }
+}
+
+#ifdef ERTS_HLT_DEBUG
+#  define ERTS_BTM_MAX_DESTROY_LIMIT 2
+#else
+#  define ERTS_BTM_MAX_DESTROY_LIMIT 50
+#endif
+
+typedef struct {
+    ErtsBifTimers *bif_timers;
+    union {
+	proc_btm_rbt_yield_state_t proc_btm_yield_state;
+	abtm_rbt_yield_state_t abtm_yield_state;
+    } u;
+} ErtsBifTimerYieldState;
+
+int erts_cancel_bif_timers(Process *p, ErtsBifTimers *btm, void **vyspp)
+{
+    ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(p);
+    ErtsBifTimerYieldState ys = {btm, {ERTS_RBT_YIELD_STAT_INITER}};
+    ErtsBifTimerYieldState *ysp;
+    int res;
+
+    ysp = (ErtsBifTimerYieldState *) *vyspp;
+    if (!ysp)
+	ysp = &ys;
+
+    res = proc_btm_rbt_foreach_destroy_yielding(&ysp->bif_timers,
+						exit_cancel_bif_timer,
+						(void *) esdp,
+						&ysp->u.proc_btm_yield_state,
+						ERTS_BTM_MAX_DESTROY_LIMIT);
+
+    if (res == 0) {
+	if (ysp != &ys)
+	    erts_free(ERTS_ALC_T_BTM_YIELD_STATE, ysp);
+	*vyspp = NULL;
+    }
+    else {
+
+	if (ysp == &ys) {
+	    ysp = erts_alloc(ERTS_ALC_T_BTM_YIELD_STATE,
+			     sizeof(ErtsBifTimerYieldState));
+	    sys_memcpy((void *) ysp, (void *) &ys,
+		       sizeof(ErtsBifTimerYieldState));
+	}
+
+	*vyspp = (void *) ysp;
+    }
+
+    return res;
+}
+
+static void
+detach_bif_timer(ErtsHLTimer *tmr, void *vesdp)
+{
+    tmr->abtm.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
+    hl_timer_dec_refc(tmr, tmr->head.roflgs);
+}
+
+int erts_detach_accessor_bif_timers(Process *p, ErtsBifTimers *btm, void **vyspp)
+{
+    ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(p);
+    ErtsBifTimerYieldState ys = {btm, {ERTS_RBT_YIELD_STAT_INITER}};
+    ErtsBifTimerYieldState *ysp;
+    int res;
+
+    ysp = (ErtsBifTimerYieldState *) *vyspp;
+    if (!ysp)
+	ysp = &ys;
+
+    res = abtm_rbt_foreach_destroy_yielding(&ysp->bif_timers,
+					    detach_bif_timer,
+					    (void *) esdp,
+					    &ysp->u.abtm_yield_state,
+					    ERTS_BTM_MAX_DESTROY_LIMIT);
+
+    if (res == 0) {
+	if (ysp != &ys)
+	    erts_free(ERTS_ALC_T_BTM_YIELD_STATE, ysp);
+	*vyspp = NULL;
+    }
+    else {
+
+	if (ysp == &ys) {
+	    ysp = erts_alloc(ERTS_ALC_T_BTM_YIELD_STATE,
+			     sizeof(ErtsBifTimerYieldState));
+	    sys_memcpy((void *) ysp, (void *) &ys,
+		       sizeof(ErtsBifTimerYieldState));
+	}
+
+	*vyspp = (void *) ysp;
+    }
+
+    return res;
+}
+
+static ERTS_INLINE int
+parse_timeout_pos(ErtsSchedulerData *esdp, Eterm arg,
+		  ErtsMonotonicTime *conv_arg, int abs,
+		  ErtsMonotonicTime *tposp, int *stimep)
+{
+    ErtsMonotonicTime t;
+
+    if (!term_to_Sint64(arg, &t)) {
+	ERTS_HLT_ASSERT(!is_small(arg));
+	if (!is_big(arg))
+	    return -1;
+
+	if (abs || !big_sign(arg))
+	    return 1;
+
+	return -1;
+    }
+
+    if (conv_arg)
+	*conv_arg = t;
+
+    if (abs) {
+	t += -1*ERTS_MONOTONIC_OFFSET_MSEC; /* external to internal */
+	if (t < ERTS_MONOTONIC_TO_MSEC(ERTS_MONOTONIC_BEGIN))
+	    return 1;
+	if (t > ERTS_MONOTONIC_TO_MSEC(ERTS_MONOTONIC_END))
+	    return 1;
+	*stimep = (t - ERTS_MONOTONIC_TO_MSEC(esdp->last_monotonic_time)
+		   < ERTS_BIF_TIMER_SHORT_TIME);
+	*tposp = ERTS_MSEC_TO_CLKTCKS(t);
+    }
+    else {
+	ErtsMonotonicTime now, ticks;
+	
+	if (t < 0)
+	    return -1;
+
+	ticks = ERTS_MSEC_TO_CLKTCKS(t);
+
+	if (ERTS_CLKTCK_RESOLUTION > 1000 && ticks < 0)
+	    return 1;
+
+	ERTS_HLT_ASSERT(ticks >= 0);
+
+	now = erts_get_monotonic_time(esdp);
+	ticks += ERTS_MONOTONIC_TO_CLKTCKS(now-1);
+	ticks += 1;
+
+	if (ticks < ERTS_MONOTONIC_TO_CLKTCKS(ERTS_MONOTONIC_BEGIN))
+	    return 1;
+	if (ticks > ERTS_MONOTONIC_TO_CLKTCKS(ERTS_MONOTONIC_END))
+	    return 1;
+
+	*stimep = (t < ERTS_BIF_TIMER_SHORT_TIME);
+	*tposp = ticks;
+    }
+
+    return 0;
+}
+
+/*
+ *
+ * The BIF timer BIFs...
+ */
+
+BIF_RETTYPE send_after_3(BIF_ALIST_3)
+{
+    ErtsMonotonicTime timeout_pos;
+    int short_time, tres;
+
+    tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
+			     0, &timeout_pos, &short_time);
+    if (tres != 0)
+	BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT);
+
+    return setup_bif_timer(BIF_P, timeout_pos, short_time,
+			   BIF_ARG_2, BIF_ARG_2, BIF_ARG_3, 0);
+}
+
+BIF_RETTYPE send_after_4(BIF_ALIST_4)
+{
+    ErtsMonotonicTime timeout_pos;
+    Eterm accessor;
+    int short_time, abs, tres;
+
+    if (!parse_bif_timer_options(BIF_ARG_4, NULL, NULL, &abs, &accessor))
+	BIF_ERROR(BIF_P, BADARG);
+    
+    tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
+			     abs, &timeout_pos, &short_time);
+    if (tres != 0)
+	BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT);
+
+    return setup_bif_timer(BIF_P, timeout_pos, short_time,
+			   BIF_ARG_2, accessor, BIF_ARG_3, 0);
+}
+
+BIF_RETTYPE start_timer_3(BIF_ALIST_3)
+{
+    ErtsMonotonicTime timeout_pos;
+    int short_time, tres;
+
+    tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
+			     0, &timeout_pos, &short_time);
+    if (tres != 0)
+	BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT);
+
+    return setup_bif_timer(BIF_P, timeout_pos, short_time,
+			   BIF_ARG_2, BIF_ARG_2, BIF_ARG_3, !0);
+}
+
+BIF_RETTYPE start_timer_4(BIF_ALIST_4)
+{
+    ErtsMonotonicTime timeout_pos;
+    Eterm accessor;
+    int short_time, abs, tres;
+
+    if (!parse_bif_timer_options(BIF_ARG_4, NULL, NULL, &abs, &accessor))
+	BIF_ERROR(BIF_P, BADARG);
+
+    tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
+			     abs, &timeout_pos, &short_time);
+    if (tres != 0)
+	BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT);
+
+    return setup_bif_timer(BIF_P, timeout_pos, short_time,
+			   BIF_ARG_2, accessor, BIF_ARG_3, !0);
+}
+
+BIF_RETTYPE cancel_timer_1(BIF_ALIST_1)
+{
+    return access_bif_timer(BIF_P, BIF_ARG_1, 1, 0, 1);
+}
+
+BIF_RETTYPE cancel_timer_2(BIF_ALIST_2)
+{
+    BIF_RETTYPE ret;
+    int async, info;
+
+    if (parse_bif_timer_options(BIF_ARG_2, &async, &info, NULL, NULL))
+	return access_bif_timer(BIF_P, BIF_ARG_1, 1, async, info);
+
+    ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
+    return ret;
+}
+
+BIF_RETTYPE read_timer_1(BIF_ALIST_1)
+{
+    return access_bif_timer(BIF_P, BIF_ARG_1, 0, 0, 1);
+}
+
+BIF_RETTYPE read_timer_2(BIF_ALIST_2)
+{
+    BIF_RETTYPE ret;
+    int async;
+
+    if (parse_bif_timer_options(BIF_ARG_2, &async, NULL, NULL, NULL))
+	return access_bif_timer(BIF_P, BIF_ARG_1, 0, async, 1);
+
+    ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
+    return ret;
+}
+
+/*
+ * Process and Port timer functionality.
+ *
+ * NOTE! These are only allowed to be called by a
+ *       scheduler thread that currently is
+ *       executing the process or port.
+ */
+
+static ERTS_INLINE void
+set_proc_timer_common(Process *c_p, ErtsSchedulerData *esdp, Sint64 tmo,
+		      ErtsMonotonicTime timeout_pos, int short_time)
+{
+    void *tmr;
+    check_canceled_queue(esdp, esdp->timer_service);
+
+    if (tmo == 0)
+	c_p->flags |= F_TIMO;
+    else {
+
+	c_p->flags |= F_INSLPQUEUE;
+	c_p->flags &= ~F_TIMO;
+
+	if (tmo < ERTS_TIMER_WHEEL_MSEC)
+	    tmr = (void *) create_tw_timer(esdp, (void *) c_p, 1, timeout_pos);
+	else
+	    tmr = (void *) create_hl_timer(esdp, timeout_pos,
+					   short_time, 0, (void *) c_p,
+					   c_p->common.id, NIL, NIL, NULL);
+	erts_smp_atomic_set_relb(&c_p->common.timer, (erts_aint_t) tmr);
+    }
+}
+
+int
+erts_set_proc_timer_term(Process *c_p, Eterm etmo)
+{
+    ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p);
+    ErtsMonotonicTime tmo, timeout_pos;
+    int short_time, tres;
+
+    ERTS_HLT_ASSERT(erts_smp_atomic_read_nob(&c_p->common.timer)
+		    == ERTS_PTMR_NONE);
+
+    tres = parse_timeout_pos(esdp, etmo, &tmo, 0,
+			     &timeout_pos, &short_time);
+    if (tres != 0)
+	return tres;
+
+    if ((tmo >> 32) != 0)
+	return 1;
+
+    set_proc_timer_common(c_p, esdp, tmo, timeout_pos, short_time);
+    return 0;
+}
+
+void
+erts_set_proc_timer_uword(Process *c_p, UWord tmo)
+{
+    ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p);
+
+    ERTS_HLT_ASSERT(erts_smp_atomic_read_nob(&c_p->common.timer)
+		    == ERTS_PTMR_NONE);
+
+#ifndef ARCH_32
+    ERTS_HLT_ASSERT((tmo >> 32) == (UWord) 0);
+#endif
+
+    if (tmo == 0)
+	c_p->flags |= F_TIMO;
+    else {
+	ErtsMonotonicTime timeout_pos;
+	timeout_pos = get_timeout_pos(erts_get_monotonic_time(esdp),
+				      (ErtsMonotonicTime) tmo);
+	set_proc_timer_common(c_p, esdp, (ErtsMonotonicTime) tmo,
+			      timeout_pos,
+			      tmo < ERTS_BIF_TIMER_SHORT_TIME);
+    }
+}
+
+void
+erts_cancel_proc_timer(Process *c_p)
+{
+    erts_aint_t tval;
+    tval = erts_smp_atomic_xchg_acqb(&c_p->common.timer,
+				     ERTS_PTMR_NONE);
+    c_p->flags &= ~(F_INSLPQUEUE|F_TIMO);
+    if (tval == ERTS_PTMR_NONE)
+	return;
+    if (tval == ERTS_PTMR_TIMEDOUT) {
+	erts_smp_atomic_set_nob(&c_p->common.timer, ERTS_PTMR_NONE);
+	return;
+    }
+    continue_cancel_ptimer(ERTS_PROC_GET_SCHDATA(c_p),
+			   (ErtsTimer *) tval);
+}
+
+void
+erts_set_port_timer(Port *c_prt, Sint64 tmo)
+{
+    void *tmr;
+    ErtsSchedulerData *esdp = erts_get_scheduler_data();
+    ErtsMonotonicTime timeout_pos;
+
+    if (erts_smp_atomic_read_nob(&c_prt->common.timer) != ERTS_PTMR_NONE)
+	erts_cancel_port_timer(c_prt);
+
+    check_canceled_queue(esdp, esdp->timer_service);
+
+    timeout_pos = get_timeout_pos(erts_get_monotonic_time(esdp), tmo);
+
+    if (tmo < ERTS_TIMER_WHEEL_MSEC)
+	tmr = (void *) create_tw_timer(esdp, (void *) c_prt, 0,
+				       timeout_pos);
+    else
+	tmr = (void *) create_hl_timer(esdp, timeout_pos, 0, 0,
+				       (void *) c_prt,
+				       c_prt->common.id, NIL, NIL,
+				       NULL);
+    erts_smp_atomic_set_relb(&c_prt->common.timer, (erts_aint_t) tmr);
+}
+
+void
+erts_cancel_port_timer(Port *c_prt)
+{
+    erts_aint_t tval;
+    tval = erts_smp_atomic_xchg_acqb(&c_prt->common.timer,
+				     ERTS_PTMR_NONE);
+    if (tval == ERTS_PTMR_NONE)
+	return;
+    if (tval == ERTS_PTMR_TIMEDOUT) {
+	while (!erts_port_task_is_scheduled(&c_prt->timeout_task))
+	    erts_thr_yield();
+	erts_port_task_abort(&c_prt->timeout_task);
+	erts_smp_atomic_set_nob(&c_prt->common.timer, ERTS_PTMR_NONE);
+	return;
+    }
+    continue_cancel_ptimer(erts_get_scheduler_data(),
+			   (ErtsTimer *) tval);
+}
+
+Sint64
+erts_read_port_timer(Port *c_prt)
+{
+    ErtsTimer *tmr;
+    erts_aint_t itmr;
+    ErtsMonotonicTime timeout_pos;
+
+    itmr = erts_smp_atomic_read_acqb(&c_prt->common.timer);
+    if (itmr == ERTS_PTMR_NONE)
+	return (Sint64) -1;
+    if (itmr == ERTS_PTMR_TIMEDOUT)
+	return (Sint64) 0;
+    tmr = (ErtsTimer *) itmr;
+    if (tmr->head.roflgs & ERTS_TMR_ROFLG_HLT)
+	timeout_pos = tmr->hlt.timeout;
+    else
+	timeout_pos = tmr->twt.tw_tmr.timeout_pos;
+    return get_time_left(NULL, timeout_pos);
+}
+
+/*
+ * Debug stuff...
+ */
+
+typedef struct {
+    int to;
+    void *to_arg;
+    ErtsMonotonicTime now;
+} ErtsBTMPrint;
+
+static void
+btm_print(ErtsHLTimer *tmr, void *vbtmp)
+{
+    ErtsBTMPrint *btmp = (ErtsBTMPrint *) vbtmp;
+    ErtsMonotonicTime left;
+    Eterm receiver;
+
+    if (tmr->timeout <= btmp->now)
+	left = 0;
+    left = ERTS_CLKTCKS_TO_MSEC(tmr->timeout - btmp->now);
+
+    receiver = ((tmr->head.roflgs & ERTS_TMR_ROFLG_REG_NAME)
+		? tmr->receiver.name
+		: tmr->receiver.proc->common.id);
+
+    erts_print(btmp->to, btmp->to_arg,
+	       "=timer:%T\n"
+	       "Message: %T\n"
+	       "Time left: %b64d\n",
+	       receiver,
+	       tmr->btm.message,
+	       (Sint64) left);
+}
+
+void
+erts_print_bif_timer_info(int to, void *to_arg)
+{
+    ErtsBTMPrint btmp;
+    int six;
+
+    if (!ERTS_IS_CRASH_DUMPING)
+	ERTS_INTERNAL_ERROR("Not crash dumping");
+
+    btmp.to = to;
+    btmp.to_arg = to_arg;
+    btmp.now = erts_get_monotonic_time(NULL);
+    btmp.now = ERTS_MONOTONIC_TO_CLKTCKS(btmp.now);
+
+    for (six = 0; six < erts_no_schedulers; six++) {
+	ErtsHLTimerService *srv =
+	    erts_aligned_scheduler_data[six].esd.timer_service;
+	btm_rbt_foreach(srv->btm_tree, btm_print, (void *) &btmp);
+    }
+}
+
+typedef struct {
+    void (*func)(Eterm,
+		 Eterm,
+		 ErlHeapFragment *,
+		 void *);
+    void *arg;
+} ErtsBTMForeachDebug;
+
+static void
+debug_btm_foreach(ErtsHLTimer *tmr, void *vbtmfd)
+{
+    if (erts_smp_atomic32_read_nob(&tmr->state) == ERTS_TMR_STATE_ACTIVE) {
+	ErtsBTMForeachDebug *btmfd = (ErtsBTMForeachDebug *) vbtmfd;
+	(*btmfd->func)(((tmr->head.roflgs & ERTS_TMR_ROFLG_REG_NAME)
+			? tmr->receiver.name
+			: tmr->receiver.proc->common.id),
+		       tmr->btm.message,
+		       tmr->btm.bp,
+		       btmfd->arg);
+    }
+}
+
+void
+erts_debug_bif_timer_foreach(void (*func)(Eterm,
+					  Eterm,
+					  ErlHeapFragment *,
+					  void *),
+			     void *arg)
+{
+    ErtsBTMForeachDebug btmfd;
+    int six;
+
+    btmfd.func = func;
+    btmfd.arg = arg;
+
+    if (!erts_smp_thr_progress_is_blocking())
+	ERTS_INTERNAL_ERROR("Not blocking thread progress");
+
+    for (six = 0; six < erts_no_schedulers; six++) {
+	ErtsHLTimerService *srv =
+	    erts_aligned_scheduler_data[six].esd.timer_service;
+	btm_rbt_foreach(srv->btm_tree,
+			debug_btm_foreach,
+			(void *) &btmfd);
+    }
+}
+
+#ifdef ERTS_HLT_HARD_DEBUG
+
+typedef struct {
+    ErtsHLTimerService *srv;
+    int found_root;
+    ErtsHLTimer **rootpp;
+} ErtsHdbgHLT;
+
+static void
+st_hdbg_func(ErtsHLTimer *tmr, void *vhdbg)
+{
+    ErtsHdbgHLT *hdbg = (ErtsHdbgHLT *) vhdbg;
+    ErtsHLTimer **rootpp;
+    ERTS_HLT_ASSERT(tmr->time.tree.parent & ERTS_HLT_PFLG_SAME_TIME);
+    if (tmr->time.tree.parent == ERTS_HLT_PFLG_SAME_TIME) {
+	ERTS_HLT_ASSERT(tmr != *hdbg->rootpp);
+    }
+    else {
+	rootpp = (ErtsHLTimer **) (tmr->time.tree.parent
+				   & ~ERTS_HLT_PFLG_SAME_TIME);
+	ERTS_HLT_ASSERT(rootpp == hdbg->rootpp);
+	ERTS_HLT_ASSERT(tmr == *rootpp);
+	ERTS_HLT_ASSERT(!hdbg->found_root);
+	hdbg->found_root = 1;
+    }
+    ERTS_HLT_ASSERT(tmr->time.tree.u.l.next->time.tree.u.l.prev == tmr);
+    ERTS_HLT_ASSERT(tmr->time.tree.u.l.prev->time.tree.u.l.next == tmr);
+    ERTS_HLT_ASSERT(btm_rbt_lookup(hdbg->srv->btm_tree, tmr->btm.refn) == tmr);
+}
+
+static void
+tt_hdbg_func(ErtsHLTimer *tmr, void *vhdbg)
+{
+    ErtsHdbgHLT *hdbg = (ErtsHdbgHLT *) vhdbg;
+    ErtsHLTimer *prnt;
+    ERTS_HLT_ASSERT((tmr->time.tree.parent & ERTS_HLT_PFLG_SAME_TIME) == 0);
+    prnt = (ErtsHLTimer *) (tmr->time.tree.parent & ~ERTS_HLT_PFLGS_MASK);
+    if (prnt) {
+	ERTS_HLT_ASSERT(prnt->time.tree.u.t.left == tmr
+			|| prnt->time.tree.u.t.right == tmr);
+    }
+    else {
+	ERTS_HLT_ASSERT(!hdbg->found_root);
+	hdbg->found_root = 1;
+	ERTS_HLT_ASSERT(tmr == *hdbg->rootpp);
+    }
+    if (tmr->time.tree.u.t.left) {
+	prnt = (ErtsHLTimer *) (tmr->time.tree.u.t.left->time.tree.parent
+				& ~ERTS_HLT_PFLGS_MASK);
+	ERTS_HLT_ASSERT(tmr == prnt);
+    }
+    if (tmr->time.tree.u.t.right) {
+	prnt = (ErtsHLTimer *) (tmr->time.tree.u.t.right->time.tree.parent
+				& ~ERTS_HLT_PFLGS_MASK);
+	ERTS_HLT_ASSERT(tmr == prnt);
+    }
+    ERTS_HLT_ASSERT(btm_rbt_lookup(hdbg->srv->btm_tree, tmr->btm.refn) == tmr);
+    if (tmr->time.tree.same_time) {
+	ErtsHdbgHLT st_hdbg;
+	st_hdbg.srv = hdbg->srv;
+	st_hdbg.found_root = 0;
+	st_hdbg.rootpp = &tmr->time.tree.same_time;
+	same_time_list_foreach(tmr->time.tree.same_time, st_hdbg_func, (void *) &st_hdbg);
+	ERTS_HLT_ASSERT(st_hdbg.found_root);
+    }
+}
+
+static void
+bt_hdbg_func(ErtsHLTimer *tmr, void *vhdbg)
+{
+    ErtsHdbgHLT *hdbg = (ErtsHdbgHLT *) vhdbg;
+    ErtsHLTimer *prnt;
+    ERTS_HLT_ASSERT((tmr->btm.tree.parent & ERTS_HLT_PFLG_SAME_TIME) == 0);
+    prnt = (ErtsHLTimer *) (tmr->btm.tree.parent & ~ERTS_HLT_PFLGS_MASK);
+    if (prnt) {
+	ERTS_HLT_ASSERT(prnt->btm.tree.left == tmr
+			|| prnt->btm.tree.right == tmr);
+    }
+    else {
+	ERTS_HLT_ASSERT(!hdbg->found_root);
+	hdbg->found_root = 1;
+	ERTS_HLT_ASSERT(tmr == *hdbg->rootpp);
+    }
+    if (tmr->btm.tree.left) {
+	prnt = (ErtsHLTimer *) (tmr->btm.tree.left->btm.tree.parent
+				& ~ERTS_HLT_PFLGS_MASK);
+	ERTS_HLT_ASSERT(tmr == prnt);
+    }
+    if (tmr->btm.tree.right) {
+	prnt = (ErtsHLTimer *) (tmr->btm.tree.right->btm.tree.parent
+				& ~ERTS_HLT_PFLGS_MASK);
+	ERTS_HLT_ASSERT(tmr == prnt);
+    }
+    if (tmr->pending_timeout) {
+	if (tmr->pending_timeout > 0) /* container > 0 */
+	    ERTS_HLT_ASSERT(tmr->time.tree.parent == ERTS_HLT_PFIELD_NOT_IN_TABLE);
+	else {
+	    ERTS_HLT_ASSERT(tmr->time.tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE);
+	    ERTS_HLT_ASSERT(tmr->time.tree.parent & ERTS_HLT_PFLG_SAME_TIME);
+	}
+    }
+    else {
+	ErtsHLTimer *ttmr = time_rbt_lookup(hdbg->srv->time_tree, tmr->timeout);
+	ERTS_HLT_ASSERT(ttmr);
+	if (ttmr != tmr) {
+	    ERTS_HLT_ASSERT(ttmr->time.tree.same_time);
+	    ERTS_HLT_ASSERT(tmr == same_time_list_lookup(ttmr->time.tree.same_time, tmr));
+	}
+    }
+}
+
+static void
+hdbg_chk_srv(ErtsHLTimerService *srv)
+{
+    if (srv->time_tree) {
+	ErtsHdbgHLT hdbg;
+	hdbg.srv = srv;
+	hdbg.found_root = 0;
+	hdbg.rootpp = &srv->time_tree;
+	time_rbt_foreach(srv->time_tree, tt_hdbg_func, (void *) &hdbg);
+	ERTS_HLT_ASSERT(hdbg.found_root);
+    }
+    if (srv->btm_tree) {
+	ErtsHdbgHLT hdbg;
+	hdbg.srv = srv;
+	hdbg.found_root = 0;
+	hdbg.rootpp = &srv->btm_tree;
+	btm_rbt_foreach(srv->btm_tree, bt_hdbg_func, (void *) &hdbg);
+	ERTS_HLT_ASSERT(hdbg.found_root);
+    }
+}
+
+#endif /* ERTS_HLT_HARD_DEBUG */
diff --git a/erts/emulator/beam/erl_hl_timer.h b/erts/emulator/beam/erl_hl_timer.h
new file mode 100644
index 0000000000..30889a71da
--- /dev/null
+++ b/erts/emulator/beam/erl_hl_timer.h
@@ -0,0 +1,80 @@
+/*
+ * %CopyrightBegin%
+ * 
+ * Copyright Ericsson AB 2015. All Rights Reserved.
+ * 
+ * The 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_HL_TIMER_H__
+#define ERL_HL_TIMER_H__
+
+typedef struct ErtsHLTimer_ ErtsBifTimers;
+typedef struct ErtsHLTimerService_ ErtsHLTimerService;
+
+#include "sys.h"
+#include "erl_process.h"
+#define ERL_PORT_GET_PORT_TYPE_ONLY__
+#include "erl_port.h"
+#undef ERL_PORT_GET_PORT_TYPE_ONLY__
+#include "erl_message.h"
+#include "erl_alloc_types.h"
+
+#define ERTS_PTMR_NONE ((erts_aint_t) NULL)
+#define ERTS_PTMR_TIMEDOUT (ERTS_PTMR_NONE + ((erts_aint_t) 1))
+
+#define ERTS_PTMR_INIT(P) \
+    erts_smp_atomic_init_nob(&(P)->common.timer, ERTS_PTMR_NONE)
+#define ERTS_PTMR_IS_SET(P) \
+    (ERTS_PTMR_NONE != erts_smp_atomic_read_nob(&(P)->common.timer))
+#define ERTS_PTMR_IS_TIMED_OUT(P) \
+    (ERTS_PTMR_TIMEDOUT == erts_smp_atomic_read_nob(&(P)->common.timer))
+
+#define ERTS_PTMR_CLEAR(P)					\
+    do {							\
+	ASSERT(ERTS_PTMR_IS_TIMED_OUT((P)));			\
+	erts_smp_atomic_set_nob(&(P)->common.timer,		\
+				ERTS_PTMR_NONE);		\
+    } while (0)
+
+size_t erts_timer_type_size(ErtsAlcType_t type);
+int erts_set_proc_timer_term(Process *, Eterm);
+void erts_set_proc_timer_uword(Process *, UWord);
+void erts_cancel_proc_timer(Process *);
+void erts_set_port_timer(Port *, Sint64);
+void erts_cancel_port_timer(Port *);
+Sint64 erts_read_port_timer(Port *);
+int erts_cancel_bif_timers(Process *, ErtsBifTimers *, void **);
+int erts_detach_accessor_bif_timers(Process *, ErtsBifTimers *, void **);
+ErtsHLTimerService *erts_create_timer_service(void);
+void erts_hl_timer_init(void);
+
+#ifdef ERTS_SMP
+void
+erts_handle_canceled_timers(void *vesdp,
+			    int *need_thr_progress,
+			    ErtsThrPrgrVal *thr_prgr_p,
+			    int *need_more_work);
+#endif
+
+Uint erts_bif_timer_memory_size(void);
+void erts_print_bif_timer_info(int to, void *to_arg);
+
+void erts_debug_bif_timer_foreach(void (*func)(Eterm,
+					       Eterm,
+					       ErlHeapFragment *,
+					       void *),
+				  void *arg);
+
+#endif /* ERL_HL_TIMER_H__ */
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 86d3416423..9769f36e42 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -35,7 +35,7 @@
 #include "dist.h"
 #include "erl_mseg.h"
 #include "erl_threads.h"
-#include "erl_bif_timer.h"
+#include "erl_hl_timer.h"
 #include "erl_instrument.h"
 #include "erl_printf_term.h"
 #include "erl_misc_utils.h"
@@ -46,6 +46,8 @@
 #include "erl_async.h"
 #include "erl_ptab.h"
 #include "erl_bif_unique.h"
+#define ERTS_WANT_TIMER_WHEEL_API
+#include "erl_time.h"
 
 #ifdef HIPE
 #include "hipe_mode_switch.h"	/* for hipe_mode_switch_init() */
@@ -363,7 +365,6 @@ erl_init(int ncpu,
     erts_init_binary(); /* Must be after init_emulator() */
     erts_bp_init();
     init_db(); /* Must be after init_emulator */
-    erts_bif_timer_init();
     erts_init_node_tables();
     init_dist();
     erl_drv_thr_init();
@@ -2098,11 +2099,8 @@ erl_start(int argc, char **argv)
 
     erts_initialized = 1;
 
-    {
-	Eterm init = erl_first_process_otp("otp_ring0", NULL, 0,
-					   boot_argc, boot_argv);
-	erts_bif_timer_start_servers(init);
-    }
+    (void) erl_first_process_otp("otp_ring0", NULL, 0,
+				 boot_argc, boot_argv);
 
 #ifdef ERTS_SMP
     erts_start_schedulers();
@@ -2110,13 +2108,17 @@ erl_start(int argc, char **argv)
 
     erts_sys_main_thread(); /* May or may not return! */
 #else
-    erts_thr_set_main_status(1, 1);
+    {
+	ErtsSchedulerData *esdp = erts_get_scheduler_data();
+	erts_thr_set_main_status(1, 1);
 #if ERTS_USE_ASYNC_READY_Q
-    erts_get_scheduler_data()->aux_work_data.async_ready.queue
-	= erts_get_async_ready_queue(1);
+	esdp->aux_work_data.async_ready.queue
+	    = erts_get_async_ready_queue(1);
 #endif
-    set_main_stack_size();
-    process_main();
+	set_main_stack_size();
+	erts_sched_init_time_sup(esdp);
+	process_main();
+    }
 #endif
 }
 
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 261460d054..617ce84895 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -91,6 +91,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
     {   "driver_list",                          NULL                    },
     {	"proc_link",				"pid"			},
     {	"proc_msgq",				"pid"			},
+    {	"proc_btm",				"pid"			},
     {	"dist_entry",				"address"		},
     {	"dist_entry_links",			"address"		},
     {   "code_write_permission",                NULL                    },
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 22cbae10d1..43a03c793e 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -994,7 +994,7 @@ erts_send_message(Process* sender,
 #endif
 	    );
         BM_SWAP_TIMER(send,system);
-    } else if (sender == receiver && !(sender->flags & F_OFF_HEAP_MSGS)) {
+    } else if (sender == receiver) {
 	/* Drop message if receiver has a pending exit ... */
 #ifdef ERTS_SMP
 	ErtsProcLocks need_locks = (~(*receiver_locks)
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 8713941769..6b8c3cebc7 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -213,25 +213,15 @@ do {									\
     if ((M)->data.attached) {						\
 	Uint need__ = erts_msg_attached_data_size((M));			\
  	if ((ST) - (HT) >= need__) {					\
-	    Uint *htop__;						\
-	move__attached__msg__data____:					\
-	    htop__ = (HT);						\
+	    Uint *htop__ = (HT);					\
 	    erts_move_msg_attached_data_to_heap(&htop__, &MSO((P)), (M));\
 	    ASSERT(htop__ - (HT) <= need__);				\
 	    (HT) = htop__;						\
 	}								\
 	else {								\
-	    int off_heap_msgs__ = (int) (P)->flags & F_OFF_HEAP_MSGS;	\
-	    if (!off_heap_msgs__)					\
-		need__ = 0;						\
 	    { SWPO ; }							\
-	    (FC) -= erts_garbage_collect((P), need__, NULL, 0);		\
+	    (FC) -= erts_garbage_collect((P), 0, NULL, 0);		\
 	    { SWPI ; }							\
-	    if (off_heap_msgs__) {					\
-		ASSERT((M)->data.attached);				\
-		ASSERT((ST) - (HT) >= need__);				\
-		goto move__attached__msg__data____;			\
-	    }								\
 	}								\
 	ASSERT(!(M)->data.attached);					\
     }									\
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 660f446a52..62f018bd41 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -335,7 +335,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
     rp = (scheduler
 	  ? erts_proc_lookup(receiver)
 	  : erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
-			      receiver, rp_locks, ERTS_P2P_FLG_SMP_INC_REFC));
+			      receiver, rp_locks, ERTS_P2P_FLG_INC_REFC));
     if (rp == NULL) {
 	ASSERT(env == NULL || receiver != c_p->common.id);
 	return 0;
@@ -367,7 +367,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
     if (rp_locks)
 	erts_smp_proc_unlock(rp, rp_locks);
     if (!scheduler)
-	erts_smp_proc_dec_refc(rp);
+	erts_proc_dec_refc(rp);
     if (flush_me) {
 	cache_env(env);
     }
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index c6d136f951..bcf6311079 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -1469,7 +1469,7 @@ setup_reference_table(void)
     erts_db_foreach_table(insert_ets_table, NULL);
 
     /* Insert all bif timers */
-    erts_bif_timer_foreach(insert_bif_timer, NULL);
+    erts_debug_bif_timer_foreach(insert_bif_timer, NULL);
 
     /* Insert node table (references to dist) */
     hash_foreach(&erts_node_table, insert_erl_node, NULL);
diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h
index ad3f104a68..3920fae2d9 100644
--- a/erts/emulator/beam/erl_port.h
+++ b/erts/emulator/beam/erl_port.h
@@ -350,6 +350,7 @@ int erts_lc_is_port_locked(Port *);
 ERTS_GLB_INLINE void erts_port_inc_refc(Port *prt);
 ERTS_GLB_INLINE void erts_port_dec_refc(Port *prt);
 ERTS_GLB_INLINE void erts_port_add_refc(Port *prt, Sint32 add_refc);
+ERTS_GLB_INLINE Sint erts_port_read_refc(Port *prt);
 
 ERTS_GLB_INLINE int erts_smp_port_trylock(Port *prt);
 ERTS_GLB_INLINE void erts_smp_port_lock(Port *prt);
@@ -359,37 +360,26 @@ ERTS_GLB_INLINE void erts_smp_port_unlock(Port *prt);
 
 ERTS_GLB_INLINE void erts_port_inc_refc(Port *prt)
 {
-#ifdef ERTS_SMP
-    erts_ptab_inc_refc(&prt->common);
-#else
-    erts_atomic32_inc_nob(&prt->refc);
-#endif
+    erts_ptab_atmc_inc_refc(&prt->common);
 }
 
 ERTS_GLB_INLINE void erts_port_dec_refc(Port *prt)
 {
-#ifdef ERTS_SMP
-    int referred = erts_ptab_dec_test_refc(&prt->common);
+    int referred = erts_ptab_atmc_dec_test_refc(&prt->common);
     if (!referred)
 	erts_port_free(prt);
-#else
-    int refc = erts_atomic32_dec_read_nob(&prt->refc);
-    if (refc == 0)
-	erts_port_free(prt);	
-#endif
 }
 
 ERTS_GLB_INLINE void erts_port_add_refc(Port *prt, Sint32 add_refc)
 {
-#ifdef ERTS_SMP
-    int referred = erts_ptab_add_test_refc(&prt->common, add_refc);
+    int referred = erts_ptab_atmc_add_test_refc(&prt->common, add_refc);
     if (!referred)
 	erts_port_free(prt);
-#else
-    int refc = erts_atomic32_add_read_nob(&prt->refc, add_refc);
-    if (refc == 0)
-	erts_port_free(prt);	
-#endif
+}
+
+ERTS_GLB_INLINE Sint erts_port_read_refc(Port *prt)
+{
+    return erts_ptab_atmc_read_refc(&prt->common);
 }
 
 ERTS_GLB_INLINE int
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index 2aa0a27197..c701737e26 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -1646,6 +1646,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
     erts_aint32_t state;
     int active;
     Uint64 start_time = 0;
+    ErtsSchedulerData *esdp = runq->scheduler;
 
     ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
 
@@ -1662,7 +1663,6 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
     *curr_port_pp = pp;
     
     if (erts_sched_stat.enabled) {
-	ErtsSchedulerData *esdp = erts_get_scheduler_data();
 	Uint old = ERTS_PORT_SCHED_ID(pp, esdp->no);
 	int migrated = old && old != esdp->no;
 
@@ -1718,11 +1718,16 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
 	switch (ptp->type) {
 	case ERTS_PORT_TASK_TIMEOUT:
 	    reset_handle(ptp);
-	    reds = ERTS_PORT_REDS_TIMEOUT;
-	    if (!(state & ERTS_PORT_SFLGS_DEAD)) {
-                DTRACE_DRIVER(driver_timeout, pp);
-		(*pp->drv_ptr->timeout)((ErlDrvData) pp->drv_data);
-            }
+	    if (!ERTS_PTMR_IS_TIMED_OUT(pp))
+		reds = 0;
+	    else {
+		ERTS_PTMR_CLEAR(pp);
+		reds = ERTS_PORT_REDS_TIMEOUT;
+		if (!(state & ERTS_PORT_SFLGS_DEAD)) {
+		    DTRACE_DRIVER(driver_timeout, pp);
+		    (*pp->drv_ptr->timeout)((ErlDrvData) pp->drv_data);
+		}
+	    }
 	    break;
 	case ERTS_PORT_TASK_INPUT:
 	    reds = ERTS_PORT_REDS_INPUT;
@@ -1879,7 +1884,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
     runq->scheduler->reductions += reds;
 
     ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
-    ERTS_PORT_REDUCTIONS_EXECUTED(runq, reds);
+    ERTS_PORT_REDUCTIONS_EXECUTED(esdp, runq, reds);
 
     return res;
 }
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index f74a2ee54c..588bc75a43 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -44,8 +44,10 @@
 #include "dtrace-wrapper.h"
 #include "erl_ptab.h"
 #include "erl_bif_unique.h"
+#define ERTS_WANT_TIMER_WHEEL_API
+#include "erl_time.h"
 
-
+#define ERTS_CHECK_TIME_REDS CONTEXT_REDS
 #define ERTS_DELAYED_WAKEUP_INFINITY (~(Uint64) 0)
 #define ERTS_DELAYED_WAKEUP_REDUCTIONS ((Uint64) CONTEXT_REDS/2)
 
@@ -463,7 +465,7 @@ static int stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg);
 static void aux_work_timeout(void *unused);
 static void aux_work_timeout_early_init(int no_schedulers);
 static void aux_work_timeout_late_init(void);
-static void setup_aux_work_timer(void);
+static void setup_aux_work_timer(ErtsSchedulerData *esdp);
 
 static int execute_sys_tasks(Process *c_p,
 			     erts_aint32_t *statep,
@@ -493,6 +495,8 @@ dbg_chk_aux_work_val(erts_aint32_t value)
     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_CNCLD_TMRS;
+    valid |= ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR;
     valid |= ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP;
 #endif
 #if HAVE_ERTS_MSEG
@@ -610,13 +614,11 @@ erts_pre_init_process(void)
 #endif
 }
 
-#ifdef ERTS_SMP
 static void
 release_process(void *vproc)
 {
-    erts_smp_proc_dec_refc((Process *) vproc);
+    erts_proc_dec_refc((Process *) vproc);
 }
-#endif
 
 /* initialize the scheduler */
 void
@@ -632,16 +634,18 @@ erts_init_process(int ncpu, int proc_tab_size, int legacy_proc_tab)
 
     erts_ptab_init_table(&erts_proc,
 			 ERTS_ALC_T_PROC_TABLE,
-#ifdef ERTS_SMP
 			 release_process,
-#else
-			 NULL,
-#endif
 			 (ErtsPTabElementCommon *) &erts_invalid_process.common,
 			 proc_tab_size,
 			 sizeof(Process),
 			 "process_table",
-			 legacy_proc_tab);
+			 legacy_proc_tab,
+#ifdef ERTS_SMP
+			 1
+#else
+			 0
+#endif
+	);
 
     last_reductions = 0;
     last_exact_reductions = 0;
@@ -1035,7 +1039,7 @@ reply_sched_wall_time(void *vswtrp)
     if (rp_locks)
 	erts_smp_proc_unlock(rp, rp_locks);
 
-    erts_smp_proc_dec_refc(rp);
+    erts_proc_dec_refc(rp);
 
     if (erts_smp_atomic32_dec_read_nob(&swtrp->refc) == 0)
 	swtreq_free(vswtrp);
@@ -1067,7 +1071,7 @@ erts_sched_wall_time_request(Process *c_p, int set, int enable)
     erts_smp_atomic32_init_nob(&swtrp->refc,
 			       (erts_aint32_t) erts_no_schedulers);
 
-    erts_smp_proc_add_refc(c_p, (Sint32) erts_no_schedulers);
+    erts_proc_add_refc(c_p, (Sint32) erts_no_schedulers);
 
 #ifdef ERTS_SMP
     if (erts_no_schedulers > 1)
@@ -1128,7 +1132,7 @@ erts_psd_set_init(Process *p, ErtsProcLocks plocks, int ix, void *data)
     xplocks &= ~plocks;
     if (xplocks && erts_smp_proc_trylock(p, xplocks) == EBUSY) {
 	if (xplocks & ERTS_PROC_LOCK_MAIN) {
-	    erts_smp_proc_inc_refc(p);
+	    erts_proc_inc_refc(p);
 	    erts_smp_proc_unlock(p, plocks);
 	    erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL);
 	    refc = 1;
@@ -1144,7 +1148,7 @@ erts_psd_set_init(Process *p, ErtsProcLocks plocks, int ix, void *data)
     if (xplocks)
 	erts_smp_proc_unlock(p, xplocks);
     if (refc)
-	erts_smp_proc_dec_refc(p);
+	erts_proc_dec_refc(p);
     ASSERT(p->psd);
     if (p->psd != psd)
 	erts_free(ERTS_ALC_T_PSD, psd);
@@ -1771,6 +1775,101 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, i
     return aux_work & ~ERTS_SSI_AUX_WORK_DD_THR_PRGR;
 }
 
+/*
+ * Canceled timers
+ */
+
+void
+erts_notify_canceled_timer(ErtsSchedulerData *esdp, int rsid)
+{
+    ASSERT(esdp && esdp == erts_get_scheduler_data());
+    if (esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp))
+	schedule_aux_work_wakeup(&esdp->aux_work_data,
+				 rsid,
+				 ERTS_SSI_AUX_WORK_CNCLD_TMRS);
+    else
+	set_aux_work_flags_wakeup_relb(ERTS_SCHED_SLEEP_INFO_IX(rsid-1),
+				       ERTS_SSI_AUX_WORK_CNCLD_TMRS);
+}
+
+static ERTS_INLINE erts_aint32_t
+handle_canceled_timers(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
+{
+    ErtsSchedulerSleepInfo *ssi = awdp->ssi;
+    int need_thr_progress = 0;
+    ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID;
+    int more_work = 0;
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+    ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
+#endif
+    unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_CNCLD_TMRS);
+    erts_handle_canceled_timers((void *) awdp->esdp,
+				&need_thr_progress,
+				&wakeup,
+				&more_work);
+    if (more_work) {
+	if (set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_CNCLD_TMRS)
+	    & ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR) {
+	    unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR);
+	    aux_work &= ~ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR;
+	}
+	return aux_work;
+    }
+
+    if (need_thr_progress) {
+	if (wakeup == ERTS_THR_PRGR_INVALID)
+	    wakeup = erts_thr_progress_later(awdp->esdp);
+	awdp->cncld_tmrs.thr_prgr = wakeup;
+	set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR);
+	haw_thr_prgr_soft_wakeup(awdp, wakeup);
+    }
+    return aux_work & ~ERTS_SSI_AUX_WORK_CNCLD_TMRS;
+}
+
+static ERTS_INLINE erts_aint32_t
+handle_canceled_timers_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
+{
+    ErtsSchedulerSleepInfo *ssi;
+    int need_thr_progress;
+    int more_work;
+    ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID;
+    ErtsThrPrgrVal current = haw_thr_prgr_current(awdp);
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+    ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
+#endif
+    if (!erts_thr_progress_has_reached_this(current, awdp->cncld_tmrs.thr_prgr))
+	return aux_work & ~ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR;
+
+    ssi = awdp->ssi;
+    need_thr_progress = 0;
+    more_work = 0;
+
+    erts_handle_canceled_timers((void *) awdp->esdp,
+				&need_thr_progress,
+				&wakeup,
+				&more_work);
+    if (more_work) {
+	set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_CNCLD_TMRS);
+	unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR);
+	return ((aux_work & ~ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR)
+		| ERTS_SSI_AUX_WORK_CNCLD_TMRS);
+    }
+
+    if (need_thr_progress) {
+	if (wakeup == ERTS_THR_PRGR_INVALID)
+	    wakeup = erts_thr_progress_later(awdp->esdp);
+	awdp->cncld_tmrs.thr_prgr = wakeup;
+	haw_thr_prgr_soft_wakeup(awdp, wakeup);
+    }
+    else {
+	unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR);
+    }
+
+    return aux_work & ~ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR;
+}
+
 /*
  * Handle scheduled thread progress later operations.
  */
@@ -1869,7 +1968,7 @@ completed_dealloc(void *vproc)
 {
     if (erts_atomic32_dec_read_mb(&completed_dealloc_count) == 0) {
 	erts_resume((Process *) vproc, (ErtsProcLocks) 0);
-	erts_smp_proc_dec_refc((Process *) vproc);
+	erts_proc_dec_refc((Process *) vproc);
     }
 }
 
@@ -1918,7 +2017,7 @@ erts_debug_wait_deallocations(Process *c_p)
 				      count,
 				      0)) {
 	erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
-	erts_smp_proc_inc_refc(c_p);
+	erts_proc_inc_refc(c_p);
 	/* scheduler threads */
 	erts_schedule_multi_misc_aux_work(0,
 					  erts_no_schedulers,
@@ -2033,7 +2132,7 @@ static ERTS_INLINE erts_aint32_t
 handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
 {
     unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_SET_TMO);
-    setup_aux_work_timer();
+    setup_aux_work_timer(awdp->esdp);
     return aux_work & ~ERTS_SSI_AUX_WORK_SET_TMO;
 }
 
@@ -2093,6 +2192,11 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
 #ifdef ERTS_SMP
     HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP,
 		    handle_thr_prgr_later_op);
+    HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_CNCLD_TMRS,
+		    handle_canceled_timers);
+    /* CNCLD_TMRS must be before CNCLD_TMRS_THR_PRGR */
+    HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR,
+		    handle_canceled_timers_thr_prgr);
 #endif
 
 #if ERTS_USE_ASYNC_READY_Q
@@ -2142,8 +2246,8 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
 
 typedef struct {
     union {
-	ErlTimer data;
-	char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErlTimer))];
+	ErtsTWheelTimer data;
+	char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsTWheelTimer))];
     } timer;
 
     int initialized;
@@ -2153,6 +2257,22 @@ typedef struct {
 
 static ErtsAuxWorkTmo *aux_work_tmo;
 
+static ERTS_INLINE void
+start_aux_work_timer(ErtsSchedulerData *esdp)
+{
+    ErtsMonotonicTime tmo = erts_get_monotonic_time(esdp);
+    tmo = ERTS_MONOTONIC_TO_CLKTCKS(tmo-1);
+    tmo += ERTS_MSEC_TO_CLKTCKS(1000) + 1;
+    erts_twheel_init_timer(&aux_work_tmo->timer.data);
+    ASSERT(esdp);
+    erts_twheel_set_timer(esdp->timer_wheel,
+			  &aux_work_tmo->timer.data,
+			  aux_work_timeout,
+			  NULL,
+			  (void *) esdp,
+			  tmo);
+}
+
 static void
 aux_work_timeout_early_init(int no_schedulers)
 {
@@ -2185,18 +2305,12 @@ void
 aux_work_timeout_late_init(void)
 {
     aux_work_tmo->initialized = 1;
-    if (erts_atomic32_read_nob(&aux_work_tmo->refc)) {
-	erts_init_timer(&aux_work_tmo->timer.data);
-	erts_set_timer(&aux_work_tmo->timer.data,
-		       aux_work_timeout,
-		       NULL,
-		       NULL,
-		       1000);
-    }
+    if (erts_atomic32_read_nob(&aux_work_tmo->refc))
+	start_aux_work_timer(erts_get_scheduler_data());
 }
 
 static void
-aux_work_timeout(void *unused)
+aux_work_timeout(void *vesdp)
 {
     erts_aint32_t refc;
     int i;
@@ -2219,31 +2333,18 @@ aux_work_timeout(void *unused)
     if (refc != 1
 	|| 1 != erts_atomic32_cmpxchg_relb(&aux_work_tmo->refc, 0, 1)) {
 	/* Setup next timeout... */
-	erts_set_timer(&aux_work_tmo->timer.data,
-		       aux_work_timeout,
-		       NULL,
-		       NULL,
-		       1000);
+	start_aux_work_timer((ErtsSchedulerData *) vesdp);
     }
 }
 
 static void
-setup_aux_work_timer(void)
+setup_aux_work_timer(ErtsSchedulerData *esdp)
 {
-#ifndef ERTS_SMP
-    if (!erts_get_scheduler_data())
+    if (!esdp || !esdp->timer_wheel)
 	set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(0),
 				      ERTS_SSI_AUX_WORK_SET_TMO);
     else
-#endif
-    {
-	erts_init_timer(&aux_work_tmo->timer.data);
-	erts_set_timer(&aux_work_tmo->timer.data,
-		       aux_work_timeout,
-		       NULL,
-		       NULL,
-		       1000);
-    }
+	start_aux_work_timer(esdp);
 }
 
 erts_aint32_t
@@ -2274,7 +2375,7 @@ erts_set_aux_work_timeout(int ix, erts_aint32_t type, int enable)
 	if (refc == 1) {
 	    erts_atomic32_inc_acqb(&aux_work_tmo->refc);
 	    if (aux_work_tmo->initialized) 
-		setup_aux_work_timer();
+		setup_aux_work_timer(erts_get_scheduler_data());
 	}
     }
     return old;
@@ -2653,11 +2754,6 @@ aux_thread(void *unused)
     erts_aint32_t aux_work;
     ErtsThrPrgrCallbacks callbacks;
     int thr_prgr_active = 1;
-    ErtsTimerWheel *timer_wheel = erts_default_timer_wheel;
-    ErtsNextTimeoutRef nxt_tmo_ref = erts_get_next_timeout_reference(timer_wheel);
-
-    if (!timer_wheel)
-	ERTS_INTERNAL_ERROR("Missing aux timer wheel");
 
 #ifdef ERTS_ENABLE_LOCK_CHECK
     {
@@ -2681,7 +2777,6 @@ aux_thread(void *unused)
     sched_prep_spin_wait(ssi);
 
     while (1) {
-	ErtsMonotonicTime current_time;
 	erts_aint32_t flgs;
 
 	aux_work = erts_atomic32_read_acqb(&ssi->aux_work);
@@ -2693,56 +2788,28 @@ aux_thread(void *unused)
 		erts_thr_progress_leader_update(NULL);
 	}
 
-	if (aux_work) {
-	    current_time = erts_get_monotonic_time();
-	    if (current_time >= erts_next_timeout_time(nxt_tmo_ref)) {
-		if (!thr_prgr_active)
-		    erts_thr_progress_active(NULL, thr_prgr_active = 1);
-		erts_bump_timers(timer_wheel, current_time);
-	    }
-	}
-	else {
-	    ErtsMonotonicTime timeout_time;
-	    timeout_time = erts_check_next_timeout_time(timer_wheel,
-							ERTS_SEC_TO_MONOTONIC(10*60));
-	    current_time = erts_get_monotonic_time();
-	    if (current_time >= timeout_time) {
-		if (!thr_prgr_active)
-		    erts_thr_progress_active(NULL, thr_prgr_active = 1);
-	    }
-	    else {
-		if (thr_prgr_active)
-		    erts_thr_progress_active(NULL, thr_prgr_active = 0);
-		erts_thr_progress_prepare_wait(NULL);
+	if (!aux_work) {
+	    if (thr_prgr_active)
+		erts_thr_progress_active(NULL, thr_prgr_active = 0);
+	    erts_thr_progress_prepare_wait(NULL);
 
-		ERTS_SCHED_FAIR_YIELD();
+	    ERTS_SCHED_FAIR_YIELD();
 
-		flgs = sched_spin_wait(ssi, 0);
+	    flgs = sched_spin_wait(ssi, 0);
 
+	    if (flgs & ERTS_SSI_FLG_SLEEPING) {
+		ASSERT(flgs & ERTS_SSI_FLG_WAITING);
+		flgs = sched_set_sleeptype(ssi, ERTS_SSI_FLG_TSE_SLEEPING);
 		if (flgs & ERTS_SSI_FLG_SLEEPING) {
+		    int res;
+		    ASSERT(flgs & ERTS_SSI_FLG_TSE_SLEEPING);
 		    ASSERT(flgs & ERTS_SSI_FLG_WAITING);
-		    flgs = sched_set_sleeptype(ssi, ERTS_SSI_FLG_TSE_SLEEPING);
-		    if (flgs & ERTS_SSI_FLG_SLEEPING) {
-			int res;
-			ASSERT(flgs & ERTS_SSI_FLG_TSE_SLEEPING);
-			ASSERT(flgs & ERTS_SSI_FLG_WAITING);
-			current_time = erts_get_monotonic_time();
-			do {
-			    Sint64 timeout;
-			    if (current_time >= timeout_time)
-				break;
-			    timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time
-							     - current_time
-							     - 1) + 1;
-			    res = erts_tse_twait(ssi->event, timeout);
-			    current_time = erts_get_monotonic_time();
-			} while (res == EINTR);
-		    }
+		    do {
+			res = erts_tse_wait(ssi->event);
+		    } while (res == EINTR);
 		}
-		erts_thr_progress_finalize_wait(NULL);
 	    }
-	    if (current_time >= timeout_time)
-		erts_bump_timers(timer_wheel, current_time);
+	    erts_thr_progress_finalize_wait(NULL);
 	}
 
 	flgs = sched_prep_spin_wait(ssi);
@@ -2825,7 +2892,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
 
 	    if (aux_work) {
 		flgs = erts_smp_atomic32_read_acqb(&ssi->flags);
-		current_time = erts_get_monotonic_time();
+		current_time = erts_get_monotonic_time(esdp);
 		if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) {
 		    if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) {
 			erts_thr_progress_active(esdp, thr_prgr_active = 1);
@@ -2836,9 +2903,8 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
 	    }
 	    else {
 		ErtsMonotonicTime timeout_time;
-		timeout_time = erts_check_next_timeout_time(esdp->timer_wheel,
-							    ERTS_SEC_TO_MONOTONIC(10*60));
-		current_time = erts_get_monotonic_time();
+		timeout_time = erts_check_next_timeout_time(esdp);
+		current_time = erts_get_monotonic_time(esdp);
 		if (current_time >= timeout_time) {
 		    if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) {
 			erts_thr_progress_active(esdp, thr_prgr_active = 1);
@@ -2864,7 +2930,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
 			    int res;
 			    ASSERT(flgs & ERTS_SSI_FLG_TSE_SLEEPING);
 			    ASSERT(flgs & ERTS_SSI_FLG_WAITING);
-			    current_time = erts_get_monotonic_time();
+			    current_time = erts_get_monotonic_time(esdp);
 			    do {
 				Sint64 timeout;
 				if (current_time >= timeout_time)
@@ -2873,7 +2939,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
 								 - current_time
 								 - 1) + 1;
 				res = erts_tse_twait(ssi->event, timeout);
-				current_time = erts_get_monotonic_time();
+				current_time = erts_get_monotonic_time(esdp);
 			    } while (res == EINTR);
 			}
 		    }
@@ -2948,7 +3014,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
 	    ASSERT(!erts_port_task_have_outstanding_io_tasks());
 	    erl_sys_schedule(1); /* Might give us something to do */
 
-	    current_time = erts_get_monotonic_time();
+	    current_time = erts_get_monotonic_time(esdp);
 	    if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref))
 		erts_bump_timers(esdp->timer_wheel, current_time);
 
@@ -3066,7 +3132,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
 	erl_sys_schedule(0);
 
 	{
-	    ErtsMonotonicTime current_time = erts_get_monotonic_time();
+	    ErtsMonotonicTime current_time = erts_get_monotonic_time(esdp);
 	    if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref))
 		erts_bump_timers(esdp->timer_wheel, current_time);
 	}
@@ -5275,6 +5341,7 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp)
     awdp->dd.thr_prgr = ERTS_THR_PRGR_VAL_WAITING;
     awdp->dd.completed_callback = NULL;
     awdp->dd.completed_arg = NULL;
+    awdp->cncld_tmrs.thr_prgr = ERTS_THR_PRGR_VAL_WAITING;
     awdp->later_op.thr_prgr = ERTS_THR_PRGR_VAL_FIRST;
     awdp->later_op.size = 0;
     awdp->later_op.first = NULL;
@@ -5340,9 +5407,6 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num,
     esdp->no = (Uint) num;
 #endif
 
-    esdp->timer_wheel = erts_default_timer_wheel;
-    esdp->next_tmo_ref = erts_get_next_timeout_reference(esdp->timer_wheel);
-
     esdp->ssi = ssi;
     esdp->current_process = NULL;
     esdp->current_port = NULL;
@@ -5355,6 +5419,9 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num,
     esdp->run_queue = runq;
     esdp->run_queue->scheduler = esdp;
 
+    esdp->last_monotonic_time = 0;
+    esdp->check_time_reds = 0;
+
     esdp->thr_id = (Uint32) num;
     erts_sched_bif_unique_init(esdp);
 
@@ -5918,13 +5985,6 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces
     int check_emigration_need;
 #endif
 
-#ifdef ERTS_SMP
-    if ((p->static_flags & ERTS_STC_FLG_PREFER_SCHED)
-	&& p->preferred_run_queue != RUNQ_READ_RQ(&p->run_queue)) {
-	RUNQ_SET_RQ(&p->run_queue, p->preferred_run_queue);
-    }
-#endif
-
     a = state;
 
     while (1) {
@@ -6732,6 +6792,7 @@ suspend_scheduler(ErtsSchedulerData *esdp)
 	}
     }
 
+    (void) erts_get_monotonic_time(esdp);
     erts_smp_runq_lock(esdp->run_queue);
     non_empty_runq(esdp->run_queue);
 
@@ -6865,7 +6926,7 @@ suspend_scheduler(ErtsSchedulerData *esdp)
 		}
 
 		if (aux_work) {
-		    current_time = erts_get_monotonic_time();
+		    current_time = erts_get_monotonic_time(esdp);
 		    if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) {
 			if (!thr_prgr_active) {
 			    erts_thr_progress_active(esdp, thr_prgr_active = 1);
@@ -6876,9 +6937,8 @@ suspend_scheduler(ErtsSchedulerData *esdp)
 		}
 		else {
 		    ErtsMonotonicTime timeout_time;
-		    timeout_time = erts_check_next_timeout_time(esdp->timer_wheel,
-								ERTS_SEC_TO_MONOTONIC(60*60));
-		    current_time = erts_get_monotonic_time();
+		    timeout_time = erts_check_next_timeout_time(esdp);
+		    current_time = erts_get_monotonic_time(esdp);
 
 		    if (current_time >= timeout_time) {
 			if (!thr_prgr_active) {
@@ -6904,7 +6964,7 @@ suspend_scheduler(ErtsSchedulerData *esdp)
 					 | ERTS_SSI_FLG_SUSPENDED)) {
 				int res;
 
-				current_time = erts_get_monotonic_time();
+				current_time = erts_get_monotonic_time(esdp);
 				do {
 				    Sint64 timeout;
 				    if (current_time >= timeout_time)
@@ -6913,7 +6973,7 @@ suspend_scheduler(ErtsSchedulerData *esdp)
 								     - current_time
 								     - 1) + 1;
 				    res = erts_tse_twait(ssi->event, timeout);
-				    current_time = erts_get_monotonic_time();
+				    current_time = erts_get_monotonic_time(esdp);
 				} while (res == EINTR);
 			    }
 			}
@@ -7741,8 +7801,8 @@ sched_thread_func(void *vesdp)
     ErtsSchedulerData *esdp = vesdp;
     Uint no = esdp->no;
 
-    esdp->timer_wheel = erts_create_timer_wheel((int) no);
-    esdp->next_tmo_ref = erts_get_next_timeout_reference(esdp->timer_wheel);
+    erts_sched_init_time_sup(esdp);
+
 #ifdef ERTS_SMP
     ERTS_SCHED_SLEEP_INFO_IX(no - 1)->event = erts_tse_fetch();
     callbacks.arg = (void *) esdp->ssi;
@@ -9111,7 +9171,7 @@ Process *schedule(Process *p, int calls)
 	schedule_out_process(rq, state, p, proxy_p); /* Returns with rq locked! */
 	proxy_p = NULL;
 
-	ERTS_PROC_REDUCTIONS_EXECUTED(rq,
+	ERTS_PROC_REDUCTIONS_EXECUTED(esdp, rq,
 				      (int) ERTS_PSFLGS_GET_USR_PRIO(state),
 				      reds,
 				      actual_reds);
@@ -9128,12 +9188,9 @@ Process *schedule(Process *p, int calls)
 	    ASSERT(esdp->free_process == p);
 	    esdp->free_process = NULL;
 #else
-	    state = erts_smp_atomic32_read_nob(&p->state);
-	    if (!(state & ERTS_PSFLG_IN_RUNQ))
-		erts_free_proc(p);
+	    erts_proc_dec_refc(p);
 #endif
 	}
-
 #ifdef ERTS_SMP
 	ASSERT(!esdp->free_process);
 #endif
@@ -9141,13 +9198,13 @@ Process *schedule(Process *p, int calls)
 
 	ERTS_SMP_CHK_NO_PROC_LOCKS;
 
-	{
-	    ErtsMonotonicTime current_time = erts_get_monotonic_time();
-	    if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) {
-		erts_smp_runq_unlock(rq);
-		erts_bump_timers(esdp->timer_wheel, current_time);
-		erts_smp_runq_lock(rq);
-	    }
+	if (esdp->check_time_reds >= ERTS_CHECK_TIME_REDS)
+	    (void) erts_get_monotonic_time(esdp);
+
+	if (esdp->last_monotonic_time >= erts_next_timeout_time(esdp->next_tmo_ref)) {
+	    erts_smp_runq_unlock(rq);
+	    erts_bump_timers(esdp->timer_wheel, esdp->last_monotonic_time);
+	    erts_smp_runq_lock(rq);
 	}
 	BM_STOP_TIMER(system);
 
@@ -9307,7 +9364,7 @@ Process *schedule(Process *p, int calls)
 	    erts_smp_runq_unlock(rq);
 	    erl_sys_schedule(1);
 
-	    current_time = erts_get_monotonic_time();
+	    current_time = erts_get_monotonic_time(esdp);
 	    if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref))
 		erts_bump_timers(esdp->timer_wheel, current_time);
 
@@ -9448,13 +9505,8 @@ Process *schedule(Process *p, int calls)
 				      | ERTS_PSFLG_PENDING_EXIT
 				      | ERTS_PSFLG_ACTIVE_SYS))
 			    == ERTS_PSFLG_SUSPENDED)) {
-			if (state & ERTS_PSFLG_FREE) {
-#ifdef ERTS_SMP
-			    erts_smp_proc_dec_refc(p);
-#else
-			    erts_free_proc(p);
-#endif
-			}
+			if (state & ERTS_PSFLG_FREE)
+			    erts_proc_dec_refc(p);
 			if (proxy_p) {
 			    free_proxy_proc(proxy_p);
 			    proxy_p = NULL;
@@ -9597,6 +9649,20 @@ Process *schedule(Process *p, int calls)
 	/* Never run a suspended process */
 	ASSERT(!(ERTS_PSFLG_SUSPENDED & erts_smp_atomic32_read_nob(&p->state)));
 
+	ASSERT(erts_proc_read_refc(p) > 0);
+
+	if (ERTS_PTMR_IS_TIMED_OUT(p)) {
+	    BeamInstr** pi;
+#ifdef ERTS_SMP
+	    ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore);
+#endif
+	    pi = (BeamInstr **) p->def_arg_reg;
+	    p->i = *pi;
+	    p->flags &= ~F_INSLPQUEUE;
+	    p->flags |= F_TIMO;
+	    ERTS_PTMR_CLEAR(p);
+	}
+
 	return p;
     }
 }
@@ -10513,6 +10579,8 @@ erts_free_proc(Process *p)
 #ifdef ERTS_SMP
     erts_proc_lock_fin(p);
 #endif
+    ASSERT(erts_smp_atomic32_read_nob(&p->state) & ERTS_PSFLG_FREE);
+    ASSERT(0 == erts_proc_read_refc(p));
     erts_free(ERTS_ALC_T_PROC, (void *) p);
 }
 
@@ -10565,6 +10633,8 @@ alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
 	return NULL;
     }
 
+    ASSERT(erts_proc_read_refc(p) > 0);
+
     ASSERT(internal_pid_serial(p->common.id) <= ERTS_MAX_PID_SERIAL);
     
     p->approx_started = erts_get_approx_time();
@@ -10614,10 +10684,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
 	    int ix = so->scheduler-1;
 	    ASSERT(0 <= ix && ix < erts_no_run_queues);
 	    rq = ERTS_RUNQ_IX(ix);
-	    if (!(so->flags & SPO_PREFER_SCHED)) {
-		/* Unsupported feature... */
-		state |= ERTS_PSFLG_BOUND;
-	    }
+	    /* Unsupported feature... */
+	    state |= ERTS_PSFLG_BOUND;
 	}
 	prio = (erts_aint32_t) so->priority;
     }
@@ -10625,9 +10693,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
     state |= (((prio & ERTS_PSFLGS_PRIO_MASK) << ERTS_PSFLGS_ACT_PRIO_OFFSET)
 	      | ((prio & ERTS_PSFLGS_PRIO_MASK) << ERTS_PSFLGS_USR_PRIO_OFFSET));
 
-    if (so->flags & SPO_OFF_HEAP_MSGS)
-	state |= ERTS_PSFLG_OFF_HEAP_MSGS;
-
     if (!rq)
 	rq = erts_get_runq_proc(parent);
 
@@ -10651,12 +10716,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
     heap_need = arg_size;
 
     p->flags = erts_default_process_flags;
-    if (so->flags & SPO_OFF_HEAP_MSGS)
-	p->flags |= F_OFF_HEAP_MSGS;
 
-#ifdef ERTS_SMP
-    p->preferred_run_queue = NULL;
-#endif
     p->static_flags = 0;
     if (so->flags & SPO_SYSTEM_PROC)
 	p->static_flags |= ERTS_STC_FLG_SYSTEM_PROC;
@@ -10664,12 +10724,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
 	p->min_heap_size  = so->min_heap_size;
 	p->min_vheap_size = so->min_vheap_size;
 	p->max_gen_gcs    = so->max_gen_gcs;
-	if (so->flags & SPO_PREFER_SCHED) {
-#ifdef ERTS_SMP
-	    p->preferred_run_queue = rq;
-#endif
-	    p->static_flags |= ERTS_STC_FLG_PREFER_SCHED;
-	}
     } else {
 	p->min_heap_size  = H_MIN_SIZE;
 	p->min_vheap_size = BIN_VH_MIN_SIZE;
@@ -10678,9 +10732,9 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
     p->schedule_count = 0;
     ASSERT(p->min_heap_size == erts_next_heap_size(p->min_heap_size, 0));
     
-    p->initial[INITIAL_MOD] = mod;
-    p->initial[INITIAL_FUN] = func;
-    p->initial[INITIAL_ARI] = (Uint) arity;
+    p->u.initial[INITIAL_MOD] = mod;
+    p->u.initial[INITIAL_FUN] = func;
+    p->u.initial[INITIAL_ARI] = (Uint) arity;
 
     /*
      * Must initialize binary lists here before copying binaries to process.
@@ -10721,7 +10775,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
 
     /* No need to initialize p->fcalls. */
 
-    p->current = p->initial+INITIAL_MOD;
+    p->current = p->u.initial+INITIAL_MOD;
 
     p->i = (BeamInstr *) beam_apply;
     p->cp = (BeamInstr *) beam_apply+1;
@@ -10744,11 +10798,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
     p->ftrace = NIL;
     p->reds = 0;
 
-#ifdef ERTS_SMP
-    p->common.u.alive.ptimer = NULL;
-#else
-    erts_init_timer(&p->common.u.alive.tm);
-#endif
+    ERTS_PTMR_INIT(p);
 
     p->common.u.alive.reg = NULL;
     ERTS_P_LINKS(p) = NULL;
@@ -10779,7 +10829,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
     p->msg_inq.last = &p->msg_inq.first;
     p->msg_inq.len = 0;
 #endif
-    p->u.bif_timers = NULL;
+    p->bif_timers = NULL;
+    p->accessor_bif_timers = NULL;
     p->mbuf = NULL;
     p->mbuf_sz = 0;
     p->psd = NULL;
@@ -10937,11 +10988,7 @@ void erts_init_empty_process(Process *p)
     p->bin_old_vheap = 0;
     p->sys_task_qs = NULL;
     p->bin_vheap_mature = 0;
-#ifdef ERTS_SMP
-    p->common.u.alive.ptimer = NULL;
-#else
-    erts_init_timer(&p->common.u.alive.tm);
-#endif
+    ERTS_PTMR_INIT(p);
     p->next = NULL;
     p->off_heap.first = NULL;
     p->off_heap.overhead = 0;
@@ -10962,14 +11009,15 @@ void erts_init_empty_process(Process *p)
     p->msg.last = &p->msg.first;
     p->msg.save = &p->msg.first;
     p->msg.len = 0;
-    p->u.bif_timers = NULL;
+    p->bif_timers = NULL;
+    p->accessor_bif_timers = NULL;
     p->dictionary = NULL;
     p->seq_trace_clock = 0;
     p->seq_trace_lastcnt = 0;
     p->seq_trace_token = NIL;
-    p->initial[0] = 0;
-    p->initial[1] = 0;
-    p->initial[2] = 0;
+    p->u.initial[0] = 0;
+    p->u.initial[1] = 0;
+    p->u.initial[2] = 0;
     p->catches = 0;
     p->cp = NULL;
     p->i = NULL;
@@ -11017,7 +11065,6 @@ void erts_init_empty_process(Process *p)
     p->pending_suspenders = NULL;
     p->pending_exit.reason = THE_NON_VALUE;
     p->pending_exit.bp = NULL;
-    p->preferred_run_queue = NULL;
     erts_proc_lock_init(p);
     erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
     RUNQ_SET_RQ(&p->run_queue, ERTS_RUNQ_IX(0));
@@ -11057,7 +11104,8 @@ erts_debug_verify_clean_empty_process(Process* p)
     ASSERT(p->suspend_monitors == NULL);
     ASSERT(p->msg.first == NULL);
     ASSERT(p->msg.len == 0);
-    ASSERT(p->u.bif_timers == NULL);
+    ASSERT(p->bif_timers == NULL);
+    ASSERT(p->accessor_bif_timers == NULL);
     ASSERT(p->dictionary == NULL);
     ASSERT(p->catches == 0);
     ASSERT(p->cp == NULL);
@@ -11221,7 +11269,6 @@ set_proc_exiting(Process *p,
      */
     p->freason = EXTAG_EXIT;
     KILL_CATCHES(p);
-    cancel_timer(p);
     p->i = (BeamInstr *) beam_exit;
 
     if (enqueue)
@@ -11919,6 +11966,7 @@ erts_do_exit_process(Process* p, Eterm reason)
 {
     p->arity = 0;		/* No live registers */
     p->fvalue = reason;
+    
 
 #ifdef USE_VM_PROBES
     if (DTRACE_ENABLED(process_exit)) {
@@ -11973,20 +12021,20 @@ erts_do_exit_process(Process* p, Eterm reason)
     ASSERT((ERTS_TRACE_FLAGS(p) & F_INITIAL_TRACE_FLAGS)
 	   == F_INITIAL_TRACE_FLAGS);
 
-    cancel_timer(p);		/* Always cancel timer just in case */
-
-    if (p->u.bif_timers)
-	erts_cancel_bif_timers(p, ERTS_PROC_LOCKS_ALL);
+    ASSERT(erts_proc_read_refc(p) > 0);
+    if (ERTS_PTMR_IS_SET(p)) {
+	erts_cancel_proc_timer(p);
+	ASSERT(erts_proc_read_refc(p) > 0);
+    }
 
     erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
 
     /*
-     * The p->u.bif_timers of this process can *not* be used anymore;
+     * p->u.initial of this process can *not* be used anymore;
      * will be overwritten by misc termination data.
      */
     p->u.terminate = NULL;
 
-
     erts_continue_exit_process(p);
 }
 
@@ -12011,6 +12059,27 @@ erts_continue_exit_process(Process *p)
 
     ASSERT(ERTS_PROC_IS_EXITING(p));
 
+    ASSERT(erts_proc_read_refc(p) > 0);
+    if (p->bif_timers) {
+	if (erts_cancel_bif_timers(p, p->bif_timers, &p->u.terminate)) {
+	    ASSERT(erts_proc_read_refc(p) > 0);
+	    goto yield;
+	}
+	ASSERT(erts_proc_read_refc(p) > 0);
+	p->bif_timers = NULL;
+    }
+
+    if (p->accessor_bif_timers) {
+	if (erts_detach_accessor_bif_timers(p,
+					    p->accessor_bif_timers,
+					    &p->u.terminate)) {
+	    ASSERT(erts_proc_read_refc(p) > 0);
+	    goto yield;
+	}
+	ASSERT(erts_proc_read_refc(p) > 0);
+	p->accessor_bif_timers = NULL;
+    }
+
 #ifdef ERTS_SMP
     if (p->flags & F_HAVE_BLCKD_MSCHED) {
 	ErtsSchedSuspendResult ssr;
@@ -12104,6 +12173,8 @@ erts_continue_exit_process(Process *p)
 
 	p->scheduler_data->current_process = NULL;
 	p->scheduler_data->free_process = p;
+#else
+	erts_proc_inc_refc(p); /* Decremented in schedule() */
 #endif
 
 	/* Time of death! */
@@ -12122,29 +12193,23 @@ erts_continue_exit_process(Process *p)
     {
 	/* Inactivate and notify free */
 	erts_aint32_t n, e, a = erts_smp_atomic32_read_nob(&p->state);
-#ifdef ERTS_SMP
 	int refc_inced = 0;
-#endif
 	while (1) {
 	    n = e = a;
 	    ASSERT(a & ERTS_PSFLG_EXITING);
 	    n |= ERTS_PSFLG_FREE;
 	    n &= ~ERTS_PSFLG_ACTIVE;
-#ifdef ERTS_SMP
 	    if ((n & ERTS_PSFLG_IN_RUNQ) && !refc_inced) {
-		erts_smp_proc_inc_refc(p);
+		erts_proc_inc_refc(p);
 		refc_inced = 1;
 	    }
-#endif
 	    a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e);
 	    if (a == e)
 		break;
 	}
 
-#ifdef ERTS_SMP
 	if (refc_inced && !(n & ERTS_PSFLG_IN_RUNQ))
-	    erts_smp_proc_dec_refc(p);
-#endif
+	    erts_proc_dec_refc(p);
     }
     
     dep = ((p->flags & F_DISTRIBUTION)
@@ -12235,64 +12300,6 @@ erts_continue_exit_process(Process *p)
 
 }
 
-/* Callback for process timeout */
-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;
-
-    state = erts_smp_atomic32_read_acqb(&p->state);
-    if (!(state & ERTS_PSFLG_ACTIVE))
-	schedule_process(p, state, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
-}
-
-
-void
-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->common.u.alive.ptimer);
-#else
-    erts_cancel_timer(&p->common.u.alive.tm);
-#endif
-}
-
-/*
- * Insert a process into the time queue, with a timeout 'timeout' in ms.
- */
-void
-set_timer(Process* p, Uint timeout)
-{
-    ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p));
-
-    /* check for special case timeout=0 DONT ADD TO time queue */
-    if (timeout == 0) {
-	p->flags |= F_TIMO;
-	return;
-    }
-    p->flags |= F_INSLPQUEUE;
-    p->flags &= ~F_TIMO;
-
-#ifdef ERTS_SMP
-    erts_create_smp_ptimer(&p->common.u.alive.ptimer,
-			   p->common.id,
-			   (ErlTimeoutProc) timeout_proc,
-			   timeout);
-#else
-    erts_set_timer(&p->common.u.alive.tm,
-		  (ErlTimeoutProc) timeout_proc,
-		  NULL,
-		  (void*) p,
-		  timeout);
-#endif
-}
-
 /*
  * Stack dump functions follow.
  */
@@ -12450,6 +12457,10 @@ erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp) {
                 erts_print(to, to_arg, "FIX_ALLOC_LOWER_LIM"); break;
             case ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP:
                 erts_print(to, to_arg, "THR_PRGR_LATER_OP"); break;
+            case ERTS_SSI_AUX_WORK_CNCLD_TMRS:
+                erts_print(to, to_arg, "CANCELED_TIMERS"); break;
+            case ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR:
+                erts_print(to, to_arg, "CANCELED_TIMERS_THR_PRGR"); break;
             case ERTS_SSI_AUX_WORK_ASYNC_READY:
                 erts_print(to, to_arg, "ASYNC_READY"); break;
             case ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN:
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 743711cc3b..b1c30e7652 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -52,7 +52,7 @@ typedef struct process Process;
 #include "erl_node_container_utils.h"
 #include "erl_node_tables.h"
 #include "erl_monitors.h"
-#include "erl_bif_timer.h"
+#include "erl_hl_timer.h"
 #include "erl_time.h"
 #include "erl_atom_table.h"
 #include "external.h"
@@ -278,16 +278,18 @@ typedef enum {
 #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_REAP_PORTS		(((erts_aint32_t) 1) << 13)
-
-#define ERTS_SSI_AUX_WORK_MAX                                           14
+#define ERTS_SSI_AUX_WORK_CNCLD_TMRS		(((erts_aint32_t) 1) << 6)
+#define ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR	(((erts_aint32_t) 1) << 7)
+#define ERTS_SSI_AUX_WORK_ASYNC_READY		(((erts_aint32_t) 1) << 8)
+#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN	(((erts_aint32_t) 1) << 9)
+#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR		(((erts_aint32_t) 1) << 10)
+#define ERTS_SSI_AUX_WORK_MISC			(((erts_aint32_t) 1) << 11)
+#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN	(((erts_aint32_t) 1) << 12)
+#define ERTS_SSI_AUX_WORK_SET_TMO		(((erts_aint32_t) 1) << 13)
+#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK	(((erts_aint32_t) 1) << 14)
+#define ERTS_SSI_AUX_WORK_REAP_PORTS		(((erts_aint32_t) 1) << 15)
+
+#define ERTS_SSI_AUX_WORK_MAX                                           16
 
 typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo;
 
@@ -463,19 +465,21 @@ typedef union {
 
 extern ErtsAlignedRunQueue *erts_aligned_run_queues;
 
-#define ERTS_PROC_REDUCTIONS_EXECUTED(RQ, PRIO, REDS, AREDS)	\
+#define ERTS_PROC_REDUCTIONS_EXECUTED(SD, RQ, PRIO, REDS, AREDS)\
 do {								\
     (RQ)->procs.reductions += (AREDS);				\
     (RQ)->procs.prio_info[(PRIO)].reds += (REDS);		\
     (RQ)->check_balance_reds -= (REDS);				\
     (RQ)->wakeup_other_reds += (AREDS);				\
+    (SD)->check_time_reds += (AREDS);				\
 } while (0)
 
-#define ERTS_PORT_REDUCTIONS_EXECUTED(RQ, REDS)			\
+#define ERTS_PORT_REDUCTIONS_EXECUTED(SD, RQ, REDS)		\
 do {								\
     (RQ)->ports.info.reds += (REDS);				\
     (RQ)->check_balance_reds -= (REDS);				\
     (RQ)->wakeup_other_reds += (REDS);				\
+    (SD)->check_time_reds += (REDS);				\
 } while (0)
 
 typedef struct {
@@ -514,6 +518,9 @@ typedef struct {
 	void (*completed_callback)(void *);
 	void (*completed_arg)(void *);
     } dd;
+    struct {
+	ErtsThrPrgrVal thr_prgr;
+    } cncld_tmrs;
     struct {
 	ErtsThrPrgrVal thr_prgr;
 	UWord size;
@@ -566,6 +573,7 @@ struct ErtsSchedulerData_ {
 
     ErtsTimerWheel *timer_wheel;
     ErtsNextTimeoutRef next_tmo_ref;
+    ErtsHLTimerService *timer_service;
 #ifdef ERTS_SMP
     ethr_tid tid;		/* Thread id */
     struct erl_bits_state erl_bits_state; /* erl_bits.c state */
@@ -592,6 +600,9 @@ struct ErtsSchedulerData_ {
     ErtsAuxWorkData aux_work_data;
     ErtsAtomCacheMap atom_cache_map;
 
+    ErtsMonotonicTime last_monotonic_time;
+    int check_time_reds;
+
     Uint32 thr_id;
     Uint64 unique;
     Uint64 ref;
@@ -913,10 +924,8 @@ struct process {
 
     ErlMessageQueue msg;	/* Message queue */
 
-    union {
-	ErtsBifTimer *bif_timers;	/* Bif timers aiming at this process */
-	void *terminate;
-    } u;
+    ErtsBifTimers *bif_timers;	/* Bif timers aiming at this process */
+    ErtsBifTimers *accessor_bif_timers;	/* Accessor bif timers */
 
     ProcDict  *dictionary;       /* Process dictionary, may be NULL */
 
@@ -927,9 +936,12 @@ struct process {
 #ifdef USE_VM_PROBES
     Eterm dt_utag;              /* Place to store the dynamc trace user tag */
     Uint dt_utag_flags;         /* flag field for the dt_utag */
-#endif       
-    BeamInstr initial[3];	/* Initial module(0), function(1), arity(2), often used instead
+#endif
+    union {
+	void *terminate;
+	BeamInstr initial[3];	/* Initial module(0), function(1), arity(2), often used instead
 				   of pointer to funcinfo instruction, hence the BeamInstr datatype */
+    } u;
     BeamInstr* current;		/* Current Erlang function, part of the funcinfo:
 				 * module(0), function(1), arity(2)
 				 * (module and functions are tagged atoms;
@@ -975,7 +987,6 @@ struct process {
     ErtsSchedulerData *scheduler_data;
     Eterm suspendee;
     ErtsPendingSuspend *pending_suspenders;
-    ErtsRunQueue *preferred_run_queue;
     erts_smp_atomic_t run_queue;
 #ifdef HIPE
     struct hipe_process_state_smp hipe_smp;
@@ -1085,15 +1096,14 @@ void erts_check_for_holes(Process* p);
 #define ERTS_PSFLG_RUNNING_SYS		ERTS_PSFLG_BIT(15)
 #define ERTS_PSFLG_PROXY		ERTS_PSFLG_BIT(16)
 #define ERTS_PSFLG_DELAYED_SYS		ERTS_PSFLG_BIT(17)
-#define ERTS_PSFLG_OFF_HEAP_MSGS	ERTS_PSFLG_BIT(18)
 #ifdef ERTS_DIRTY_SCHEDULERS
-#define ERTS_PSFLG_DIRTY_CPU_PROC	ERTS_PSFLG_BIT(19)
-#define ERTS_PSFLG_DIRTY_IO_PROC	ERTS_PSFLG_BIT(20)
-#define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q	ERTS_PSFLG_BIT(21)
-#define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q	ERTS_PSFLG_BIT(22)
-#define ERTS_PSFLG_MAX  (ERTS_PSFLGS_ZERO_BIT_OFFSET + 23)
+#define ERTS_PSFLG_DIRTY_CPU_PROC	ERTS_PSFLG_BIT(18)
+#define ERTS_PSFLG_DIRTY_IO_PROC	ERTS_PSFLG_BIT(19)
+#define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q	ERTS_PSFLG_BIT(20)
+#define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q	ERTS_PSFLG_BIT(21)
+#define ERTS_PSFLG_MAX  (ERTS_PSFLGS_ZERO_BIT_OFFSET + 22)
 #else
-#define ERTS_PSFLG_MAX  (ERTS_PSFLGS_ZERO_BIT_OFFSET + 19)
+#define ERTS_PSFLG_MAX  (ERTS_PSFLGS_ZERO_BIT_OFFSET + 18)
 #endif
 
 #define ERTS_PSFLGS_IN_PRQ_MASK 	(ERTS_PSFLG_IN_PRQ_MAX		\
@@ -1112,7 +1122,6 @@ void erts_check_for_holes(Process* p);
  * Static flags that do not change after process creation.
  */
 #define ERTS_STC_FLG_SYSTEM_PROC	(((Uint32) 1) << 0)
-#define ERTS_STC_FLG_PREFER_SCHED	(((Uint32) 1) << 1)
 
 /* The sequential tracing token is a tuple of size 5:
  *
@@ -1141,9 +1150,7 @@ void erts_check_for_holes(Process* p);
 #define SPO_LINK 1
 #define SPO_USE_ARGS 2
 #define SPO_MONITOR 4
-#define SPO_OFF_HEAP_MSGS 8
-#define SPO_SYSTEM_PROC 16
-#define SPO_PREFER_SCHED 32
+#define SPO_SYSTEM_PROC 8
 
 /*
  * The following struct contains options for a process to be spawned.
@@ -1231,7 +1238,6 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags;
 #define F_P2PNR_RESCHED      (1 <<  9) /* Process has been rescheduled via erts_pid2proc_not_running() */
 #define F_FORCE_GC           (1 << 10) /* Force gc at process in-scheduling */
 #define F_DISABLE_GC         (1 << 11) /* Disable GC */
-#define F_OFF_HEAP_MSGS      (1 << 12)
 
 /* process trace_flags */
 #define F_SENSITIVE          (1 << 0)
@@ -1267,8 +1273,6 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags;
 #  define F_INITIAL_TRACE_FLAGS 0
 #endif
 
-
-
 #define TRACEE_FLAGS ( F_TRACE_PROCS | F_TRACE_CALLS \
 		     | F_TRACE_SOS |  F_TRACE_SOS1| F_TRACE_RECEIVE  \
 		     | F_TRACE_SOL | F_TRACE_SOL1 | F_TRACE_SEND \
@@ -1302,12 +1306,14 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags;
 #define ERTS_XSIG_FLG_IGN_KILL		(((Uint32) 1) << 0)
 #define ERTS_XSIG_FLG_NO_IGN_NORMAL	(((Uint32) 1) << 1)
 
-#define CANCEL_TIMER(p) \
-    do { \
-	if ((p)->flags & (F_INSLPQUEUE)) \
-	    cancel_timer(p); \
-	else \
-	    (p)->flags &= ~F_TIMO; \
+#define CANCEL_TIMER(P)					\
+    do {						\
+	if ((P)->flags & (F_INSLPQUEUE|F_TIMO)) {	\
+	    if ((P)->flags & F_INSLPQUEUE)		\
+		erts_cancel_proc_timer((P));		\
+	    else					\
+		(P)->flags &= ~F_TIMO;			\
+	}						\
     } while (0)
 
 #if defined(ERTS_DIRTY_SCHEDULERS) && defined(ERTS_SMP)
@@ -1594,6 +1600,9 @@ Eterm erts_multi_scheduling_blockers(Process *);
 void erts_start_schedulers(void);
 void erts_alloc_notify_delayed_dealloc(int);
 void erts_alloc_ensure_handle_delayed_dealloc_call(int);
+#ifdef ERTS_SMP
+void erts_notify_canceled_timer(ErtsSchedulerData *, int);
+#endif
 void erts_smp_notify_check_children_needed(void);
 #endif
 #if ERTS_USE_ASYNC_READY_Q
@@ -1628,8 +1637,6 @@ void erts_schedule_misc_op(void (*)(void *), void *);
 Eterm erl_create_process(Process*, Eterm, Eterm, Eterm, ErlSpawnOpts*);
 void erts_do_exit_process(Process*, Eterm);
 void erts_continue_exit_process(Process *);
-void set_timer(Process*, Uint);
-void cancel_timer(Process*);
 /* Begin System profile */
 Uint erts_runnable_process_count(void);
 /* End System profile */
diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c
index 82cc68222d..fff267ff2a 100644
--- a/erts/emulator/beam/erl_process_lock.c
+++ b/erts/emulator/beam/erl_process_lock.c
@@ -103,6 +103,7 @@ static struct {
     Sint16 proc_lock_main;
     Sint16 proc_lock_link;
     Sint16 proc_lock_msgq;
+    Sint16 proc_lock_btm;
     Sint16 proc_lock_status;
 } lc_id;
 #endif
@@ -145,6 +146,7 @@ erts_init_proc_lock(int cpus)
     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_btm		= erts_lc_get_lock_order_id("proc_btm");
     lc_id.proc_lock_status	= erts_lc_get_lock_order_id("proc_status");
 #endif
 }
@@ -707,7 +709,7 @@ proc_safelock(int is_managed,
 	    need_locks1 |= unlock_locks;
 	    if (!is_managed && !have_locks1) {
 		refc1 = 1;
-		erts_smp_proc_inc_refc(p1);
+		erts_proc_inc_refc(p1);
 	    }
 	    erts_smp_proc_unlock(p1, unlock_locks);
 	}
@@ -717,7 +719,7 @@ proc_safelock(int is_managed,
 	    need_locks2 |= unlock_locks;
 	    if (!is_managed && !have_locks2) {
 		refc2 = 1;
-		erts_smp_proc_inc_refc(p2);
+		erts_proc_inc_refc(p2);
 	    }
 	    erts_smp_proc_unlock(p2, unlock_locks);
 	}
@@ -798,9 +800,9 @@ proc_safelock(int is_managed,
 
     if (!is_managed) {
 	if (refc1)
-	    erts_smp_proc_dec_refc(p1);
+	    erts_proc_dec_refc(p1);
 	if (refc2)
-	    erts_smp_proc_dec_refc(p2);
+	    erts_proc_dec_refc(p2);
     }
 }
 
@@ -861,8 +863,8 @@ erts_pid2proc_opt(Process *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);
+	    if (flags & ERTS_P2P_FLG_INC_REFC)
+		erts_proc_inc_refc(c_p);
 	    return c_p;
 	}
     }
@@ -875,8 +877,8 @@ erts_pid2proc_opt(Process *c_p,
 	if (proc->common.id != pid)
 	    proc = NULL;
 	else if (!need_locks) {
-	    if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
-		erts_smp_proc_inc_refc(proc);
+	    if (flags & ERTS_P2P_FLG_INC_REFC)
+		erts_proc_inc_refc(proc);
 	}
 	else {
 	    int busy;
@@ -916,8 +918,8 @@ erts_pid2proc_opt(Process *c_p,
 #endif
 
 	    if (!busy) {
-		if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
-		    erts_smp_proc_inc_refc(proc);
+		if (flags & ERTS_P2P_FLG_INC_REFC)
+		    erts_proc_inc_refc(proc);
 
 #if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
 	    	/* all is great */
@@ -932,8 +934,8 @@ erts_pid2proc_opt(Process *c_p,
 		    proc = ERTS_PROC_LOCK_BUSY;
 		else {
 		    int managed;
-		    if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
-			erts_smp_proc_inc_refc(proc);
+		    if (flags & ERTS_P2P_FLG_INC_REFC)
+			erts_proc_inc_refc(proc);
 
 #if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
 		    erts_lcnt_proc_lock_unaquire(&proc->lock, lcnt_locks);
@@ -941,7 +943,7 @@ erts_pid2proc_opt(Process *c_p,
 
 		    managed = dhndl == ERTS_THR_PRGR_DHANDLE_MANAGED;
 		    if (!managed) {
-			erts_smp_proc_inc_refc(proc);
+			erts_proc_inc_refc(proc);
 			erts_thr_progress_unmanaged_continue(dhndl);
 			dec_refc_proc = proc;
 
@@ -978,14 +980,14 @@ erts_pid2proc_opt(Process *c_p,
 
 	erts_smp_proc_unlock(proc, need_locks);
 
-	if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
+	if (flags & ERTS_P2P_FLG_INC_REFC)
 	    dec_refc_proc = proc;
 	proc = NULL;
 
     }
 
     if (dec_refc_proc)
-	erts_smp_proc_dec_refc(dec_refc_proc);
+	erts_proc_dec_refc(dec_refc_proc);
 
 #if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_PROC_LOCK_DEBUG)
     ERTS_LC_ASSERT(!proc
@@ -1037,6 +1039,11 @@ erts_proc_lock_init(Process *p)
     ethr_mutex_lock(&p->lock.msgq.mtx);
 #ifdef ERTS_ENABLE_LOCK_CHECK
     erts_lc_trylock(1, &p->lock.msgq.lc);
+#endif
+    erts_mtx_init_x(&p->lock.btm, "proc_btm", p->common.id, do_lock_count);
+    ethr_mutex_lock(&p->lock.btm.mtx);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+    erts_lc_trylock(1, &p->lock.btm.lc);
 #endif
     erts_mtx_init_x(&p->lock.status, "proc_status", p->common.id,
 		    do_lock_count);
@@ -1045,7 +1052,6 @@ erts_proc_lock_init(Process *p)
     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);
@@ -1064,6 +1070,7 @@ erts_proc_lock_fin(Process *p)
     erts_mtx_destroy(&p->lock.main);
     erts_mtx_destroy(&p->lock.link);
     erts_mtx_destroy(&p->lock.msgq);
+    erts_mtx_destroy(&p->lock.btm);
     erts_mtx_destroy(&p->lock.status);
 #endif
 #if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP)
@@ -1079,17 +1086,20 @@ void erts_lcnt_proc_lock_init(Process *p) {
     if (p->common.id != ERTS_INVALID_PID) {
 	erts_lcnt_init_lock_x(&(p->lock.lcnt_main),   "proc_main",   ERTS_LCNT_LT_PROCLOCK, p->common.id);
 	erts_lcnt_init_lock_x(&(p->lock.lcnt_msgq),   "proc_msgq",   ERTS_LCNT_LT_PROCLOCK, p->common.id);
+	erts_lcnt_init_lock_x(&(p->lock.lcnt_btm),    "proc_btm",   ERTS_LCNT_LT_PROCLOCK, p->common.id);
 	erts_lcnt_init_lock_x(&(p->lock.lcnt_link),   "proc_link",   ERTS_LCNT_LT_PROCLOCK, p->common.id);
 	erts_lcnt_init_lock_x(&(p->lock.lcnt_status), "proc_status", ERTS_LCNT_LT_PROCLOCK, p->common.id);
     } else {
 	erts_lcnt_init_lock(&(p->lock.lcnt_main),   "proc_main",   ERTS_LCNT_LT_PROCLOCK);
 	erts_lcnt_init_lock(&(p->lock.lcnt_msgq),   "proc_msgq",   ERTS_LCNT_LT_PROCLOCK);
+	erts_lcnt_init_lock(&(p->lock.lcnt_btm),    "proc_btm",   ERTS_LCNT_LT_PROCLOCK);
 	erts_lcnt_init_lock(&(p->lock.lcnt_link),   "proc_link",   ERTS_LCNT_LT_PROCLOCK);
 	erts_lcnt_init_lock(&(p->lock.lcnt_status), "proc_status", ERTS_LCNT_LT_PROCLOCK);
     }
     } else {
 	sys_memzero(&(p->lock.lcnt_main), sizeof(p->lock.lcnt_main));
 	sys_memzero(&(p->lock.lcnt_msgq), sizeof(p->lock.lcnt_msgq));
+	sys_memzero(&(p->lock.lcnt_btm), sizeof(p->lock.lcnt_btm));
 	sys_memzero(&(p->lock.lcnt_link), sizeof(p->lock.lcnt_link));
 	sys_memzero(&(p->lock.lcnt_status), sizeof(p->lock.lcnt_status));
     }
@@ -1099,6 +1109,7 @@ void erts_lcnt_proc_lock_init(Process *p) {
 void erts_lcnt_proc_lock_destroy(Process *p) {
     erts_lcnt_destroy_lock(&(p->lock.lcnt_main));
     erts_lcnt_destroy_lock(&(p->lock.lcnt_msgq));
+    erts_lcnt_destroy_lock(&(p->lock.lcnt_btm));
     erts_lcnt_destroy_lock(&(p->lock.lcnt_link));
     erts_lcnt_destroy_lock(&(p->lock.lcnt_status));
 }
@@ -1111,6 +1122,9 @@ void erts_lcnt_proc_lock(erts_proc_lock_t *lock, ErtsProcLocks locks) {
     if (locks & ERTS_PROC_LOCK_MSGQ) {
         erts_lcnt_lock(&(lock->lcnt_msgq));
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+        erts_lcnt_lock(&(lock->lcnt_btm));
+    }
     if (locks & ERTS_PROC_LOCK_LINK) {
 	erts_lcnt_lock(&(lock->lcnt_link));
     }
@@ -1128,6 +1142,9 @@ void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks, cha
     if (locks & ERTS_PROC_LOCK_MSGQ) {
         erts_lcnt_lock_post_x(&(lock->lcnt_msgq), file, line);
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+        erts_lcnt_lock_post_x(&(lock->lcnt_btm), file, line);
+    }
     if (locks & ERTS_PROC_LOCK_LINK) {
 	erts_lcnt_lock_post_x(&(lock->lcnt_link), file, line);
     }
@@ -1145,6 +1162,9 @@ void erts_lcnt_proc_lock_unaquire(erts_proc_lock_t *lock, ErtsProcLocks locks) {
     if (locks & ERTS_PROC_LOCK_MSGQ) {
         erts_lcnt_lock_unaquire(&(lock->lcnt_msgq));
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+        erts_lcnt_lock_unaquire(&(lock->lcnt_btm));
+    }
     if (locks & ERTS_PROC_LOCK_LINK) {
 	erts_lcnt_lock_unaquire(&(lock->lcnt_link));
     }
@@ -1162,6 +1182,9 @@ void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks) {
     if (locks & ERTS_PROC_LOCK_MSGQ) {
         erts_lcnt_unlock(&(lock->lcnt_msgq));
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+        erts_lcnt_unlock(&(lock->lcnt_btm));
+    }
     if (locks & ERTS_PROC_LOCK_LINK) {
 	erts_lcnt_unlock(&(lock->lcnt_link));
     }
@@ -1178,6 +1201,9 @@ void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res
     if (locks & ERTS_PROC_LOCK_MSGQ) {
         erts_lcnt_trylock(&(lock->lcnt_msgq), res);
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+        erts_lcnt_trylock(&(lock->lcnt_btm), res);
+    }
     if (locks & ERTS_PROC_LOCK_LINK) {
 	erts_lcnt_trylock(&(lock->lcnt_link), res);
     }
@@ -1235,6 +1261,10 @@ erts_proc_lc_lock(Process *p, ErtsProcLocks locks, char *file, unsigned int line
 	lck.id = lc_id.proc_lock_msgq;
 	erts_lc_lock_x(&lck,file,line);
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+	lck.id = lc_id.proc_lock_btm;
+	erts_lc_lock_x(&lck,file,line);
+    }
     if (locks & ERTS_PROC_LOCK_STATUS) {
 	lck.id = lc_id.proc_lock_status;
 	erts_lc_lock_x(&lck,file,line);
@@ -1260,6 +1290,10 @@ erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked,
 	lck.id = lc_id.proc_lock_msgq;
 	erts_lc_trylock_x(locked, &lck, file, line);
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+	lck.id = lc_id.proc_lock_btm;
+	erts_lc_trylock_x(locked, &lck, file, line);
+    }
     if (locks & ERTS_PROC_LOCK_STATUS) {
 	lck.id = lc_id.proc_lock_status;
 	erts_lc_trylock_x(locked, &lck, file, line);
@@ -1276,6 +1310,10 @@ erts_proc_lc_unlock(Process *p, ErtsProcLocks locks)
 	lck.id = lc_id.proc_lock_status;
 	erts_lc_unlock(&lck);
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+	lck.id = lc_id.proc_lock_btm;
+	erts_lc_unlock(&lck);
+    }
     if (locks & ERTS_PROC_LOCK_MSGQ) {
 	lck.id = lc_id.proc_lock_msgq;
 	erts_lc_unlock(&lck);
@@ -1303,6 +1341,10 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
 	lck.id = lc_id.proc_lock_status;
 	erts_lc_might_unlock(&lck);
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+	lck.id = lc_id.proc_lock_btm;
+	erts_lc_might_unlock(&lck);
+    }
     if (locks & ERTS_PROC_LOCK_MSGQ) {
 	lck.id = lc_id.proc_lock_msgq;
 	erts_lc_might_unlock(&lck);
@@ -1322,6 +1364,8 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
 	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_BTM)
+	erts_lc_might_unlock(&p->lock.btm.lc);
     if (locks & ERTS_PROC_LOCK_STATUS)
 	erts_lc_might_unlock(&p->lock.status.lc);
 #endif
@@ -1347,6 +1391,10 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file,
 	lck.id = lc_id.proc_lock_msgq;
 	erts_lc_require_lock(&lck, file, line);
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+	lck.id = lc_id.proc_lock_btm;
+	erts_lc_require_lock(&lck, file, line);
+    }
     if (locks & ERTS_PROC_LOCK_STATUS) {
 	lck.id = lc_id.proc_lock_status;
 	erts_lc_require_lock(&lck, file, line);
@@ -1358,6 +1406,8 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file,
 	erts_lc_require_lock(&p->lock.link.lc, file, line);
     if (locks & ERTS_PROC_LOCK_MSGQ)
 	erts_lc_require_lock(&p->lock.msgq.lc, file, line);
+    if (locks & ERTS_PROC_LOCK_BTM)
+	erts_lc_require_lock(&p->lock.btm.lc, file, line);
     if (locks & ERTS_PROC_LOCK_STATUS)
 	erts_lc_require_lock(&p->lock.status.lc, file, line);
 #endif
@@ -1374,6 +1424,10 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
 	lck.id = lc_id.proc_lock_status;
 	erts_lc_unrequire_lock(&lck);
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+	lck.id = lc_id.proc_lock_btm;
+	erts_lc_unrequire_lock(&lck);
+    }
     if (locks & ERTS_PROC_LOCK_MSGQ) {
 	lck.id = lc_id.proc_lock_msgq;
 	erts_lc_unrequire_lock(&lck);
@@ -1393,6 +1447,8 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
 	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_BTM)
+	erts_lc_unrequire_lock(&p->lock.btm.lc);
     if (locks & ERTS_PROC_LOCK_STATUS)
 	erts_lc_unrequire_lock(&p->lock.status.lc);
 #endif
@@ -1414,6 +1470,8 @@ erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks)
 	    lck.id = lc_id.proc_lock_link;
 	else if (locks & ERTS_PROC_LOCK_MSGQ)
 	    lck.id = lc_id.proc_lock_msgq;
+	else if (locks & ERTS_PROC_LOCK_BTM)
+	    lck.id = lc_id.proc_lock_btm;
 	else if (locks & ERTS_PROC_LOCK_STATUS)
 	    lck.id = lc_id.proc_lock_status;
 	else
@@ -1448,7 +1506,8 @@ 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_lc_lock_t have_locks[5] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
+				    ERTS_PROC_LC_EMPTY_LOCK_INIT,
 				    ERTS_PROC_LC_EMPTY_LOCK_INIT,
 				    ERTS_PROC_LC_EMPTY_LOCK_INIT,
 				    ERTS_PROC_LC_EMPTY_LOCK_INIT};
@@ -1464,18 +1523,24 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks)
 	have_locks[have_locks_len].id = lc_id.proc_lock_msgq;
 	have_locks[have_locks_len++].extra = p->common.id;
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+	have_locks[have_locks_len].id = lc_id.proc_lock_btm;
+	have_locks[have_locks_len++].extra = p->common.id;
+    }
     if (locks & ERTS_PROC_LOCK_STATUS) {
 	have_locks[have_locks_len].id = lc_id.proc_lock_status;
 	have_locks[have_locks_len++].extra = p->common.id;
     }
 #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
-    erts_lc_lock_t have_locks[4];
+    erts_lc_lock_t have_locks[5];
     if (locks & ERTS_PROC_LOCK_MAIN)
 	have_locks[have_locks_len++] = p->lock.main.lc;
     if (locks & ERTS_PROC_LOCK_LINK)
 	have_locks[have_locks_len++] = p->lock.link.lc;
     if (locks & ERTS_PROC_LOCK_MSGQ)
 	have_locks[have_locks_len++] = p->lock.msgq.lc;
+    if (locks & ERTS_PROC_LOCK_BTM)
+	have_locks[have_locks_len++] = p->lock.btm.lc;
     if (locks & ERTS_PROC_LOCK_STATUS)
 	have_locks[have_locks_len++] = p->lock.status.lc;
 #endif
@@ -1488,11 +1553,11 @@ 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_lc_lock_t have_locks[5] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
 				    ERTS_PROC_LC_EMPTY_LOCK_INIT,
 				    ERTS_PROC_LC_EMPTY_LOCK_INIT,
 				    ERTS_PROC_LC_EMPTY_LOCK_INIT};
-    erts_lc_lock_t have_not_locks[4] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
+    erts_lc_lock_t have_not_locks[5] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
 					ERTS_PROC_LC_EMPTY_LOCK_INIT,
 					ERTS_PROC_LC_EMPTY_LOCK_INIT,
 					ERTS_PROC_LC_EMPTY_LOCK_INIT};
@@ -1521,6 +1586,14 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
 	have_not_locks[have_not_locks_len].id = lc_id.proc_lock_msgq;
 	have_not_locks[have_not_locks_len++].extra = p->common.id;
     }
+    if (locks & ERTS_PROC_LOCK_BTM) {
+	have_locks[have_locks_len].id = lc_id.proc_lock_btm;
+	have_locks[have_locks_len++].extra = p->common.id;
+    }
+    else {
+	have_not_locks[have_not_locks_len].id = lc_id.proc_lock_btm;
+	have_not_locks[have_not_locks_len++].extra = p->common.id;
+    }
     if (locks & ERTS_PROC_LOCK_STATUS) {
 	have_locks[have_locks_len].id = lc_id.proc_lock_status;
 	have_locks[have_locks_len++].extra = p->common.id;
@@ -1530,8 +1603,8 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
 	have_not_locks[have_not_locks_len++].extra = p->common.id;
     }
 #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
-    erts_lc_lock_t have_locks[4];
-    erts_lc_lock_t have_not_locks[4];
+    erts_lc_lock_t have_locks[5];
+    erts_lc_lock_t have_not_locks[5];
 
     if (locks & ERTS_PROC_LOCK_MAIN)
 	have_locks[have_locks_len++] = p->lock.main.lc;
@@ -1545,6 +1618,10 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
 	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_BTM)
+	have_locks[have_locks_len++] = p->lock.btm.lc;
+    else
+	have_not_locks[have_not_locks_len++] = p->lock.btm.lc;
     if (locks & ERTS_PROC_LOCK_STATUS)
 	have_locks[have_locks_len++] = p->lock.status.lc;
     else
@@ -1558,10 +1635,10 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
 ErtsProcLocks
 erts_proc_lc_my_proc_locks(Process *p)
 {
-    int resv[4];
+    int resv[5];
     ErtsProcLocks res = 0;
 #if ERTS_PROC_LOCK_OWN_IMPL
-    erts_lc_lock_t locks[4] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
+    erts_lc_lock_t locks[5] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
 						 p->common.id,
 						 ERTS_LC_FLG_LT_PROCLOCK),
 			       ERTS_LC_LOCK_INIT(lc_id.proc_lock_link,
@@ -1570,17 +1647,21 @@ erts_proc_lc_my_proc_locks(Process *p)
 			       ERTS_LC_LOCK_INIT(lc_id.proc_lock_msgq,
 						 p->common.id,
 						 ERTS_LC_FLG_LT_PROCLOCK),
+			       ERTS_LC_LOCK_INIT(lc_id.proc_lock_btm,
+						 p->common.id,
+						 ERTS_LC_FLG_LT_PROCLOCK),
 			       ERTS_LC_LOCK_INIT(lc_id.proc_lock_status,
 						 p->common.id,
 						 ERTS_LC_FLG_LT_PROCLOCK)};
 #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
-    erts_lc_lock_t locks[4] = {p->lock.main.lc,
+    erts_lc_lock_t locks[5] = {p->lock.main.lc,
 			       p->lock.link.lc,
 			       p->lock.msgq.lc,
+			       p->lock.btm.lc,
 			       p->lock.status.lc};
 #endif
 
-    erts_lc_have_locks(resv, locks, 4);
+    erts_lc_have_locks(resv, locks, 5);
     if (resv[0])
 	res |= ERTS_PROC_LOCK_MAIN;
     if (resv[1])
@@ -1588,6 +1669,8 @@ erts_proc_lc_my_proc_locks(Process *p)
     if (resv[2])
 	res |= ERTS_PROC_LOCK_MSGQ;
     if (resv[3])
+	res |= ERTS_PROC_LOCK_BTM;
+    if (resv[4])
 	res |= ERTS_PROC_LOCK_STATUS;
 
     return res;
@@ -1596,13 +1679,14 @@ erts_proc_lc_my_proc_locks(Process *p)
 void
 erts_proc_lc_chk_no_proc_locks(char *file, int line)
 {
-    int resv[4];
-    int ids[4] = {lc_id.proc_lock_main,
+    int resv[5];
+    int ids[5] = {lc_id.proc_lock_main,
 		  lc_id.proc_lock_link,
 		  lc_id.proc_lock_msgq,
+		  lc_id.proc_lock_btm,
 		  lc_id.proc_lock_status};
-    erts_lc_have_lock_ids(resv, ids, 4);
-    if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3])) {
+    erts_lc_have_lock_ids(resv, ids, 5);
+    if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3] || resv[4])) {
 	erts_lc_fail("%s:%d: Thread has process locks locked when expected "
 		     "not to have any process locks locked",
 		     file, line);
diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h
index 052d992d3f..8957e7773b 100644
--- a/erts/emulator/beam/erl_process_lock.h
+++ b/erts/emulator/beam/erl_process_lock.h
@@ -65,7 +65,7 @@
 
 #endif
 
-#define ERTS_PROC_LOCK_MAX_BIT 3
+#define ERTS_PROC_LOCK_MAX_BIT 4
 
 typedef erts_aint32_t ErtsProcLocks;
 
@@ -81,17 +81,18 @@ typedef struct erts_proc_lock_t_ {
     erts_lcnt_lock_t lcnt_main;
     erts_lcnt_lock_t lcnt_link;
     erts_lcnt_lock_t lcnt_msgq;
+    erts_lcnt_lock_t lcnt_btm;
     erts_lcnt_lock_t lcnt_status;
 #endif
 #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
     erts_mtx_t main;
     erts_mtx_t link;
     erts_mtx_t msgq;
+    erts_mtx_t btm;
     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
@@ -120,10 +121,16 @@ typedef struct erts_proc_lock_t_ {
  * Message queue lock:
  *   Protects the following fields in the process structure:
  *   * msg_inq
- *   * bif_timers
  */
 #define ERTS_PROC_LOCK_MSGQ		(((ErtsProcLocks) 1) << 2)
 
+/*
+ * Bif timer lock:
+ *   Protects the following fields in the process structure:
+ *   * bif_timers
+ */
+#define ERTS_PROC_LOCK_BTM		(((ErtsProcLocks) 1) << 3)
+
 /*
  * Status lock:
  *   Protects the following fields in the process structure:
@@ -463,6 +470,9 @@ erts_smp_proc_raw_trylock__(Process *p, ErtsProcLocks locks)
     if (locks & ERTS_PROC_LOCK_MSGQ)
 	if (erts_mtx_trylock(&p->lock.msgq) == EBUSY)
 	    goto busy_msgq;
+    if (locks & ERTS_PROC_LOCK_BTM)
+	if (erts_mtx_trylock(&p->lock.btm) == EBUSY)
+	    goto busy_btm;
     if (locks & ERTS_PROC_LOCK_STATUS)
 	if (erts_mtx_trylock(&p->lock.status) == EBUSY)
 	    goto busy_status;
@@ -470,6 +480,9 @@ erts_smp_proc_raw_trylock__(Process *p, ErtsProcLocks locks)
     return 0;
 
 busy_status:
+    if (locks & ERTS_PROC_LOCK_BTM)
+	erts_mtx_unlock(&p->lock.btm);
+busy_btm:
     if (locks & ERTS_PROC_LOCK_MSGQ)
 	erts_mtx_unlock(&p->lock.msgq);
 busy_msgq:
@@ -549,6 +562,8 @@ erts_smp_proc_lock__(Process *p,
 	erts_mtx_lock(&p->lock.link);
     if (locks & ERTS_PROC_LOCK_MSGQ)
 	erts_mtx_lock(&p->lock.msgq);
+    if (locks & ERTS_PROC_LOCK_BTM)
+	erts_mtx_lock(&p->lock.btm);
     if (locks & ERTS_PROC_LOCK_STATUS)
 	erts_mtx_lock(&p->lock.status);
 
@@ -638,6 +653,8 @@ erts_smp_proc_unlock__(Process *p,
 
     if (locks & ERTS_PROC_LOCK_STATUS)
 	erts_mtx_unlock(&p->lock.status);
+    if (locks & ERTS_PROC_LOCK_BTM)
+	erts_mtx_unlock(&p->lock.btm);
     if (locks & ERTS_PROC_LOCK_MSGQ)
 	erts_mtx_unlock(&p->lock.msgq);
     if (locks & ERTS_PROC_LOCK_LINK)
@@ -752,9 +769,10 @@ ERTS_GLB_INLINE void erts_smp_proc_lock(Process *, ErtsProcLocks);
 ERTS_GLB_INLINE void erts_smp_proc_unlock(Process *, ErtsProcLocks);
 ERTS_GLB_INLINE int erts_smp_proc_trylock(Process *, ErtsProcLocks);
 
-ERTS_GLB_INLINE void erts_smp_proc_inc_refc(Process *);
-ERTS_GLB_INLINE void erts_smp_proc_dec_refc(Process *);
-ERTS_GLB_INLINE void erts_smp_proc_add_refc(Process *, Sint32);
+ERTS_GLB_INLINE void erts_proc_inc_refc(Process *);
+ERTS_GLB_INLINE void erts_proc_dec_refc(Process *);
+ERTS_GLB_INLINE void erts_proc_add_refc(Process *, Sint);
+ERTS_GLB_INLINE Sint erts_proc_read_refc(Process *);
 
 #if ERTS_GLB_INLINE_INCL_FUNC_DEF
 
@@ -814,28 +832,59 @@ erts_smp_proc_trylock(Process *p, ErtsProcLocks locks)
 #endif
 }
 
-ERTS_GLB_INLINE void erts_smp_proc_inc_refc(Process *p)
+ERTS_GLB_INLINE void erts_proc_inc_refc(Process *p)
 {
+    ASSERT(!(erts_smp_atomic32_read_nob(&p->state) & ERTS_PSFLG_PROXY));
 #ifdef ERTS_SMP
+    erts_ptab_atmc_inc_refc(&p->common);
+#else
     erts_ptab_inc_refc(&p->common);
 #endif
 }
 
-ERTS_GLB_INLINE void erts_smp_proc_dec_refc(Process *p)
+ERTS_GLB_INLINE void erts_proc_dec_refc(Process *p)
 {
+    Sint referred;
+    ASSERT(!(erts_smp_atomic32_read_nob(&p->state) & ERTS_PSFLG_PROXY));
 #ifdef ERTS_SMP
-    int referred = erts_ptab_dec_test_refc(&p->common);
-    if (!referred)
-	erts_free_proc(p);
+    referred = erts_ptab_atmc_dec_test_refc(&p->common);
+#else
+    referred = erts_ptab_dec_test_refc(&p->common);
 #endif
+    if (!referred) {
+	ASSERT(ERTS_PROC_IS_EXITING(p));
+	ASSERT(ERTS_AINT_NULL
+	       == erts_ptab_pix2intptr_ddrb(&erts_proc,
+					    internal_pid_index(p->common.id)));
+	erts_free_proc(p);
+    }
 }
 
-ERTS_GLB_INLINE void erts_smp_proc_add_refc(Process *p, Sint32 add_refc)
+ERTS_GLB_INLINE void erts_proc_add_refc(Process *p, Sint add_refc)
 {
+    Sint referred;
+    ASSERT(!(erts_smp_atomic32_read_nob(&p->state) & ERTS_PSFLG_PROXY));
 #ifdef ERTS_SMP
-    int referred = erts_ptab_add_test_refc(&p->common, add_refc);
-    if (!referred)
+    referred = erts_ptab_atmc_add_test_refc(&p->common, add_refc);
+#else
+    referred = erts_ptab_add_test_refc(&p->common, add_refc);
+#endif
+    if (!referred) {
+	ASSERT(ERTS_PROC_IS_EXITING(p));
+	ASSERT(ERTS_AINT_NULL
+	       == erts_ptab_pix2intptr_ddrb(&erts_proc,
+					    internal_pid_index(p->common.id)));
 	erts_free_proc(p);
+    }
+}
+
+ERTS_GLB_INLINE Sint erts_proc_read_refc(Process *p)
+{
+    ASSERT(!(erts_smp_atomic32_read_nob(&p->state) & ERTS_PSFLG_PROXY));
+#ifdef ERTS_SMP
+    return erts_ptab_atmc_read_refc(&p->common);
+#else
+    return erts_ptab_read_refc(&p->common);
 #endif
 }
 
@@ -868,7 +917,7 @@ void erts_proc_safelock(Process *a_proc,
 
 #define ERTS_P2P_FLG_ALLOW_OTHER_X	(1 <<  0)
 #define ERTS_P2P_FLG_TRY_LOCK		(1 <<  1)
-#define ERTS_P2P_FLG_SMP_INC_REFC	(1 <<  2)
+#define ERTS_P2P_FLG_INC_REFC		(1 <<  2)
 
 #define ERTS_PROC_LOCK_BUSY ((Process *) &erts_invalid_process)
 
@@ -928,11 +977,14 @@ erts_pid2proc_opt(Process *c_p_unused,
 		  int flags)
 {
     Process *proc = erts_proc_lookup_raw(pid);
-    return ((!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
-	     && proc
-	     && ERTS_PROC_IS_EXITING(proc))
-	    ? NULL
-	    : proc);
+    if (!proc)
+	return NULL;
+    if (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
+	&& ERTS_PROC_IS_EXITING(proc))
+	return NULL;
+    if (flags & ERTS_P2P_FLG_INC_REFC)
+	erts_proc_inc_refc(proc);
+    return proc;
 }
 #endif /* !ERTS_SMP */
 
diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c
index 02943ee683..c688db98d8 100644
--- a/erts/emulator/beam/erl_ptab.c
+++ b/erts/emulator/beam/erl_ptab.c
@@ -360,7 +360,8 @@ erts_ptab_init_table(ErtsPTab *ptab,
 		     int size,
 		     UWord element_size,
 		     char *name,
-		     int legacy)
+		     int legacy,
+		     int atomic_refc)
 {
     size_t tab_sz, alloc_sz;
     Uint32 bits, cl, cli, ix, ix_per_cache_line, tab_cache_lines; 
@@ -415,6 +416,8 @@ erts_ptab_init_table(ErtsPTab *ptab,
     ptab->r.o.invalid_data = erts_ptab_id2data(ptab, invalid_element->id);
     ptab->r.o.release_element = release_element;
 
+    ptab->r.o.atomic_refc = atomic_refc;
+
     if (legacy) {
 	ptab->r.o.free_id_data = NULL;
 	ptab->r.o.dix_cl_mask = 0;
@@ -533,9 +536,10 @@ erts_ptab_new_element(ErtsPTab *ptab,
 
 	init_ptab_el(init_arg, (Eterm) data);
 
-#ifdef ERTS_SMP
-	erts_smp_atomic32_init_nob(&ptab_el->refc, 1);
-#endif
+	if (ptab->r.o.atomic_refc)
+	    erts_atomic_init_nob(&ptab_el->refc.atmc, 1);
+	else
+	    ptab_el->refc.sint = 1;
 
 	pix = erts_ptab_data2pix(ptab, (Eterm) data);
 
@@ -608,9 +612,10 @@ erts_ptab_new_element(ErtsPTab *ptab,
 
 	init_ptab_el(init_arg, data);
 
-#ifdef ERTS_SMP
-	erts_smp_atomic32_init_nob(&ptab_el->refc, 1);
-#endif
+	if (ptab->r.o.atomic_refc)
+	    erts_atomic_init_nob(&ptab_el->refc.atmc, 1);
+	else
+	    ptab_el->refc.sint = 1;
 
 	/* Move into slot reserved */
 #ifdef DEBUG
diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h
index 876241159b..102d41e07f 100644
--- a/erts/emulator/beam/erl_ptab.h
+++ b/erts/emulator/beam/erl_ptab.h
@@ -51,11 +51,13 @@
 
 typedef struct {
     Eterm id;
-#ifdef ERTS_SMP
-    erts_atomic32_t refc;
-#endif
+    union {
+	erts_atomic_t atmc;
+	Sint sint;
+    } refc;
     Eterm tracer_proc;
     Uint trace_flags;
+    erts_smp_atomic_t timer;
     union {
 	/* --- While being alive --- */
 	struct {
@@ -63,11 +65,6 @@ typedef struct {
 	    struct reg_proc *reg;
 	    ErtsLink *links;
 	    ErtsMonitor *monitors;
-#ifdef ERTS_SMP
-	    ErtsSmpPTimer *ptimer;
-#else
-	    ErlTimer tm;
-#endif
 	} alive;
 
 	/* --- While being released --- */
@@ -111,6 +108,7 @@ typedef struct {
     Eterm invalid_data;
     void (*release_element)(void *);
     UWord element_size;
+    int atomic_refc;
 } ErtsPTabReadOnlyData;
 
 typedef struct {
@@ -181,7 +179,8 @@ void erts_ptab_init_table(ErtsPTab *ptab,
 			  int size,
 			  UWord element_size,
 			  char *name,
-			  int legacy);
+			  int legacy,
+			  int atomic_refc);
 int erts_ptab_new_element(ErtsPTab *ptab,
 			  ErtsPTabElementCommon *ptab_el,
 			  void *init_arg,
@@ -206,9 +205,15 @@ ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_ddrb(ErtsPTab *ptab, int ix);
 ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_rb(ErtsPTab *ptab, int ix);
 ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_acqb(ErtsPTab *ptab, int ix);
 ERTS_GLB_INLINE void erts_ptab_inc_refc(ErtsPTabElementCommon *ptab_el);
-ERTS_GLB_INLINE int erts_ptab_dec_test_refc(ErtsPTabElementCommon *ptab_el);
-ERTS_GLB_INLINE int erts_ptab_add_test_refc(ErtsPTabElementCommon *ptab_el,
-					    Sint32 add_refc);
+ERTS_GLB_INLINE Sint erts_ptab_dec_test_refc(ErtsPTabElementCommon *ptab_el);
+ERTS_GLB_INLINE Sint erts_ptab_add_test_refc(ErtsPTabElementCommon *ptab_el,
+					     Sint add_refc);
+ERTS_GLB_INLINE Sint erts_ptab_read_refc(ErtsPTabElementCommon *ptab_el);
+ERTS_GLB_INLINE void erts_ptab_atmc_inc_refc(ErtsPTabElementCommon *ptab_el);
+ERTS_GLB_INLINE Sint erts_ptab_atmc_dec_test_refc(ErtsPTabElementCommon *ptab_el);
+ERTS_GLB_INLINE Sint erts_ptab_atmc_add_test_refc(ErtsPTabElementCommon *ptab_el,
+						  Sint add_refc);
+ERTS_GLB_INLINE Sint erts_ptab_atmc_read_refc(ErtsPTabElementCommon *ptab_el);
 ERTS_GLB_INLINE void erts_ptab_rlock(ErtsPTab *ptab);
 ERTS_GLB_INLINE int erts_ptab_tryrlock(ErtsPTab *ptab);
 ERTS_GLB_INLINE void erts_ptab_runlock(ErtsPTab *ptab);
@@ -365,50 +370,65 @@ ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_acqb(ErtsPTab *ptab, int ix)
     return erts_smp_atomic_read_acqb(&ptab->r.o.tab[ix]);
 }
 
-ERTS_GLB_INLINE void erts_ptab_inc_refc(ErtsPTabElementCommon *ptab_el)
+ERTS_GLB_INLINE void erts_ptab_atmc_inc_refc(ErtsPTabElementCommon *ptab_el)
 {
-#ifdef ERTS_SMP
 #ifdef ERTS_ENABLE_LOCK_CHECK
-    erts_aint32_t refc = erts_atomic32_inc_read_nob(&ptab_el->refc);
-    ERTS_SMP_LC_ASSERT(refc > 1);
+    erts_aint_t refc = erts_atomic_inc_read_nob(&ptab_el->refc.atmc);
+    ERTS_LC_ASSERT(refc > 1);
 #else
-    erts_atomic32_inc_nob(&ptab_el->refc);
-#endif
+    erts_atomic_inc_nob(&ptab_el->refc.atmc);
 #endif
 }
 
-ERTS_GLB_INLINE int erts_ptab_dec_test_refc(ErtsPTabElementCommon *ptab_el)
+ERTS_GLB_INLINE Sint erts_ptab_atmc_dec_test_refc(ErtsPTabElementCommon *ptab_el)
 {
-#ifdef ERTS_SMP
-    erts_aint32_t refc = erts_atomic32_dec_read_nob(&ptab_el->refc);
+    erts_aint_t refc = erts_atomic_dec_read_relb(&ptab_el->refc.atmc);
     ERTS_SMP_LC_ASSERT(refc >= 0);
-    return (int) refc;
-#else
-    return 0;
+#ifdef ERTS_SMP
+    if (refc == 0)
+	ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore);
 #endif
+    return (Sint) refc;
 }
 
-ERTS_GLB_INLINE int erts_ptab_add_test_refc(ErtsPTabElementCommon *ptab_el,
-					    Sint32 add_refc)
+ERTS_GLB_INLINE Sint erts_ptab_atmc_add_test_refc(ErtsPTabElementCommon *ptab_el,
+						  Sint add_refc)
 {
-#ifdef ERTS_SMP
-    erts_aint32_t refc;
+    erts_aint_t refc = erts_atomic_add_read_mb(&ptab_el->refc.atmc,
+					       (erts_aint_t) add_refc);
+    ERTS_SMP_LC_ASSERT(refc >= 0);
+    return (Sint) refc;
+}
 
-#ifndef ERTS_ENABLE_LOCK_CHECK
-    if (add_refc >= 0) {
-	erts_atomic32_add_nob(&ptab_el->refc,
-			      (erts_aint32_t) add_refc);
-	return 1;
-    }
-#endif
+ERTS_GLB_INLINE Sint erts_ptab_atmc_read_refc(ErtsPTabElementCommon *ptab_el)
+{
+    return (Sint) erts_atomic_read_nob(&ptab_el->refc.atmc);
+}
+
+ERTS_GLB_INLINE void erts_ptab_inc_refc(ErtsPTabElementCommon *ptab_el)
+{
+    ptab_el->refc.sint++;
+    ASSERT(ptab_el->refc.sint > 1);
+}
 
-    refc = erts_atomic32_add_read_nob(&ptab_el->refc,
-				      (erts_aint32_t) add_refc);
+ERTS_GLB_INLINE Sint erts_ptab_dec_test_refc(ErtsPTabElementCommon *ptab_el)
+{
+    Sint refc = --ptab_el->refc.sint;
     ERTS_SMP_LC_ASSERT(refc >= 0);
-    return (int) refc;
-#else
-    return 0;
-#endif
+    return refc;
+}
+
+ERTS_GLB_INLINE Sint erts_ptab_add_test_refc(ErtsPTabElementCommon *ptab_el,
+					     Sint add_refc)
+{
+    ptab_el->refc.sint += add_refc;
+    ERTS_SMP_LC_ASSERT(ptab_el->refc.sint >= 0);
+    return (Sint) ptab_el->refc.sint;
+}
+
+ERTS_GLB_INLINE Sint erts_ptab_read_refc(ErtsPTabElementCommon *ptab_el)
+{
+    return ptab_el->refc.sint;
 }
 
 ERTS_GLB_INLINE void erts_ptab_rlock(ErtsPTab *ptab)
diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c
index 4c9b00d2ee..78e0964e8b 100644
--- a/erts/emulator/beam/erl_thr_progress.c
+++ b/erts/emulator/beam/erl_thr_progress.c
@@ -1360,6 +1360,7 @@ erts_thr_progress_fatal_error_wait(SWord timeout) {
     erts_aint32_t bc;
     SWord time_left = timeout;
     ErtsMonotonicTime timeout_time;
+    ErtsSchedulerData *esdp = erts_get_scheduler_data();
 
     /*
      * Counting poll intervals may give us a too long timeout
@@ -1367,7 +1368,7 @@ erts_thr_progress_fatal_error_wait(SWord timeout) {
      * this. In case we havn't got time correction this may
      * however fail too...
      */
-    timeout_time = erts_get_monotonic_time();
+    timeout_time = erts_get_monotonic_time(esdp);
     timeout_time += ERTS_MSEC_TO_MONOTONIC((ErtsMonotonicTime) timeout);
 
     while (1) {
@@ -1378,7 +1379,7 @@ erts_thr_progress_fatal_error_wait(SWord timeout) {
 	    break; /* Succefully blocked all managed threads */
 	if (time_left <= 0)
 	    break; /* Timeout */
-	if (timeout_time <= erts_get_monotonic_time())
+	if (timeout_time <= erts_get_monotonic_time(esdp))
 	    break; /* Timeout */
     }
 }
diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h
index 5af3c21d40..4560cd23af 100644
--- a/erts/emulator/beam/erl_time.h
+++ b/erts/emulator/beam/erl_time.h
@@ -20,6 +20,13 @@
 #ifndef ERL_TIME_H__
 #define ERL_TIME_H__
 
+/* timer wheel size NEED to be a power of 2 */
+#ifdef SMALL_MEMORY
+#define ERTS_TIW_SIZE (1 << 13)
+#else
+#define ERTS_TIW_SIZE (1 << 16)
+#endif
+
 #if defined(DEBUG) || 0
 #define ERTS_TIME_ASSERT(B) ERTS_ASSERT(B)
 #else
@@ -33,52 +40,10 @@ typedef enum {
 } ErtsTimeWarpMode;
 
 typedef struct ErtsTimerWheel_ ErtsTimerWheel;
-typedef erts_atomic64_t * ErtsNextTimeoutRef;
-extern ErtsTimerWheel *erts_default_timer_wheel;
+typedef ErtsMonotonicTime * ErtsNextTimeoutRef;
 
 extern SysTimeval erts_first_emu_time;
 
-/*
-** Timer entry:
-*/
-typedef struct erl_timer {
-    struct erl_timer* next;	/* next entry tiw slot or chain */
-    struct erl_timer* prev;	/* prev entry tiw slot or chain */
-    erts_smp_atomic_t wheel;
-    ErtsMonotonicTime timeout_pos; /* Timeout in absolute clock ticks */
-    /* called when timeout */
-    void (*timeout)(void*);
-    /* called when cancel (may be NULL) */
-    void (*cancel)(void*);
-    void* arg;        /* argument to timeout/cancel procs */
-    int slot;			/* slot in timer wheel */
-} ErlTimer;
-
-typedef void (*ErlTimeoutProc)(void*);
-typedef void (*ErlCancelProc)(void*);
-
-#ifdef ERTS_SMP
-/*
- * Process and port timer
- */
-typedef union ErtsSmpPTimer_ ErtsSmpPTimer;
-union ErtsSmpPTimer_ {
-    struct {
-	ErlTimer tm;
-	Eterm id;
-	void (*timeout_func)(void*);
-	ErtsSmpPTimer **timer_ref;
-	Uint32 flags;
-    } timer;
-    ErtsSmpPTimer *next;
-};
-
-void erts_create_smp_ptimer(ErtsSmpPTimer **timer_ref,
-			    Eterm id,
-			    ErlTimeoutProc timeout_func,
-			    Uint timeout);
-void erts_cancel_smp_ptimer(ErtsSmpPTimer *ptimer);
-#endif
 
 void erts_monitor_time_offset(Eterm id, Eterm ref);
 int erts_demonitor_time_offset(Eterm ref);
@@ -86,14 +51,8 @@ int erts_demonitor_time_offset(Eterm ref);
 int erts_init_time_sup(int, ErtsTimeWarpMode);
 void erts_late_init_time_sup(void);
 
-/* timer-wheel api */
-
-ErtsTimerWheel *erts_create_timer_wheel(int);
 ErtsNextTimeoutRef erts_get_next_timeout_reference(ErtsTimerWheel *);
 void erts_init_time(int time_correction, ErtsTimeWarpMode time_warp_mode);
-void erts_set_timer(ErlTimer*, ErlTimeoutProc, ErlCancelProc, void*, Uint);
-void erts_cancel_timer(ErlTimer*);
-Uint erts_time_left(ErlTimer *);
 void erts_bump_timers(ErtsTimerWheel *, ErtsMonotonicTime);
 Uint erts_timer_wheel_memory_size(void);
 
@@ -101,27 +60,6 @@ Uint erts_timer_wheel_memory_size(void);
 void erts_p_slpq(void);
 #endif
 
-ErtsMonotonicTime erts_check_next_timeout_time(ErtsTimerWheel *,
-					       ErtsMonotonicTime);
-
-ERTS_GLB_INLINE void erts_init_timer(ErlTimer *p);
-ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(ErtsNextTimeoutRef);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-ERTS_GLB_INLINE void erts_init_timer(ErlTimer *p)
-{
-    erts_smp_atomic_init_nob(&p->wheel, (erts_aint_t) NULL);
-}
-
-ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(ErtsNextTimeoutRef nxt_tmo_ref)
-{
-    return (ErtsMonotonicTime) erts_atomic64_read_acqb((erts_atomic64_t *) nxt_tmo_ref);
-}
-
-#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
-
-
 /* time_sup */
 
 #if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME_CPU_TIME))
@@ -154,6 +92,7 @@ ErtsTimeOffsetState erts_time_offset_state(void);
 ErtsTimeOffsetState erts_finalize_time_offset(void); 
 struct process;
 Eterm erts_get_monotonic_start_time(struct process *c_p);
+Eterm erts_get_monotonic_end_time(struct process *c_p);
 Eterm erts_monotonic_time_source(struct process*c_p);
 Eterm erts_system_time_source(struct process*c_p);
 
@@ -163,8 +102,20 @@ Eterm erts_system_time_source(struct process*c_p);
 #define ERTS_CLKTCK_RESOLUTION (erts_time_sup__.r.o.clktck_resolution)
 #endif
 
+#define ERTS_TIMER_WHEEL_MSEC (ERTS_TIW_SIZE/(ERTS_CLKTCK_RESOLUTION/1000))
+
 struct erts_time_sup_read_only__ {
     ErtsMonotonicTime monotonic_time_unit;
+#if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT
+    ErtsMonotonicTime start;
+    struct {
+	ErtsMonotonicTime native;
+	ErtsMonotonicTime nsec;
+	ErtsMonotonicTime usec;
+	ErtsMonotonicTime msec;
+	ErtsMonotonicTime sec;
+    } start_offset;
+#endif
 #ifndef SYS_CLOCK_RESOLUTION
     ErtsMonotonicTime clktck_resolution;
 #endif
@@ -220,6 +171,16 @@ erts_time_unit_conversion(Uint64 value,
 
 #endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
 
+/*
+ * Range of monotonic time internally
+ */
+
+#define ERTS_MONOTONIC_BEGIN						\
+    ERTS_MONOTONIC_TIME_UNIT
+#define ERTS_MONOTONIC_END						\
+    ((ERTS_MONOTONIC_TIME_MAX / ERTS_MONOTONIC_TIME_UNIT)		\
+     * ERTS_MONOTONIC_TIME_UNIT)
+
 #if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT
 
 /*
@@ -231,9 +192,6 @@ erts_time_unit_conversion(Uint64 value,
 #  error Compile time time unit needs to be at least 1000000
 #endif
 
-#define ERTS_MONOTONIC_TIME_UNIT \
-    ((ErtsMonotonicTime) ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT)
-
 #if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000*1000*1000
 /* Nano-second time unit */
 
@@ -264,6 +222,66 @@ erts_time_unit_conversion(Uint64 value,
 #error Missing implementation for monotonic time unit
 #endif
 
+#define ERTS_MONOTONIC_TIME_UNIT \
+    ((ErtsMonotonicTime) ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT)
+
+/*
+ * NOTE! ERTS_MONOTONIC_TIME_START_EXTERNAL *need* to be a multiple
+ *       of ERTS_MONOTONIC_TIME_UNIT.
+ */
+
+#ifdef ARCH_32
+/*
+ * Want to use a big-num of arity 2 as long as possible (584 years
+ * in the nano-second time unit case).
+ */
+#define ERTS_MONOTONIC_TIME_START_EXTERNAL		\
+    (((((((ErtsMonotonicTime) 1) << 32)-1)	\
+       / ERTS_MONOTONIC_TIME_UNIT)		\
+      * ERTS_MONOTONIC_TIME_UNIT)		\
+     + ERTS_MONOTONIC_TIME_UNIT)
+
+#else /* ARCH_64 */
+
+#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT <= 10*1000*1000
+
+/*
+ * Using micro second time unit or lower. Start at zero since
+ * time will remain an immediate for a very long time anyway
+ * (1827 years in the 10 micro second case)...
+ */
+#define ERTS_MONOTONIC_TIME_START_EXTERNAL ((ErtsMonotonicTime) 0)
+
+#else /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT > 10*1000*1000 */
+
+/*
+ * Want to use an immediate as long as possible (36 years in the
+ * nano-second time unit case).
+*/
+#define ERTS_MONOTONIC_TIME_START_EXTERNAL 		\
+    ((((ErtsMonotonicTime) MIN_SMALL)		\
+      / ERTS_MONOTONIC_TIME_UNIT)		\
+     * ERTS_MONOTONIC_TIME_UNIT)
+
+#endif /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT > 1000*1000 */
+
+#endif /* ARCH_64 */
+
+/*
+ * Offsets from internal monotonic time to external monotonic time
+ */
+
+#define ERTS_MONOTONIC_OFFSET_NATIVE \
+    (ERTS_MONOTONIC_TIME_START_EXTERNAL - ERTS_MONOTONIC_BEGIN)
+#define ERTS_MONOTONIC_OFFSET_NSEC					\
+    ERTS_MONOTONIC_TO_NSEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
+#define ERTS_MONOTONIC_OFFSET_USEC					\
+    ERTS_MONOTONIC_TO_USEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
+#define ERTS_MONOTONIC_OFFSET_MSEC					\
+    ERTS_MONOTONIC_TO_MSEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
+#define ERTS_MONOTONIC_OFFSET_SEC					\
+    ERTS_MONOTONIC_TO_SEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
+
 #define ERTS_MONOTONIC_TO_CLKTCKS__(MON) \
     ((MON) / (ERTS_MONOTONIC_TIME_UNIT/ERTS_CLKTCK_RESOLUTION))
 #define ERTS_CLKTCKS_TO_MONOTONIC__(TCKS) \
@@ -271,8 +289,23 @@ erts_time_unit_conversion(Uint64 value,
 
 #else /* !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */
 
+/*
+ * Initialized in erts_init_sys_time_sup()
+ */
 #define ERTS_MONOTONIC_TIME_UNIT (erts_time_sup__.r.o.monotonic_time_unit)
 
+/*
+ * Offsets from internal monotonic time to external monotonic time
+ *
+ * Initialized in erts_init_time_sup()...
+ */
+#define ERTS_MONOTONIC_TIME_START_EXTERNAL (erts_time_sup__.r.o.start)
+#define ERTS_MONOTONIC_OFFSET_NATIVE (erts_time_sup__.r.o.start_offset.native)
+#define ERTS_MONOTONIC_OFFSET_NSEC (erts_time_sup__.r.o.start_offset.nsec)
+#define ERTS_MONOTONIC_OFFSET_USEC (erts_time_sup__.r.o.start_offset.usec)
+#define ERTS_MONOTONIC_OFFSET_MSEC (erts_time_sup__.r.o.start_offset.msec)
+#define ERTS_MONOTONIC_OFFSET_SEC (erts_time_sup__.r.o.start_offset.sec)
+
 #define ERTS_CONV_FROM_MON_UNIT___(M, TO)				\
     ((ErtsMonotonicTime)						\
      erts_time_unit_conversion((Uint64) (M),				\
@@ -310,6 +343,10 @@ erts_time_unit_conversion(Uint64 value,
 
 #endif /* !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */
 
+#define ERTS_MONOTONIC_TIME_END_EXTERNAL				\
+    (ERTS_MONOTONIC_TIME_START_EXTERNAL					\
+     + (ERTS_MONOTONIC_END - ERTS_MONOTONIC_BEGIN))
+
 #define ERTS_MSEC_TO_CLKTCKS__(MON) \
     ((MON) * (ERTS_CLKTCK_RESOLUTION/1000))
 #define ERTS_CLKTCKS_TO_MSEC__(TCKS) \
@@ -355,3 +392,65 @@ erts_time_unit_conversion(Uint64 value,
      ERTS_CLKTCKS_TO_MSEC__((X)))
 
 #endif /* ERL_TIME_H__ */
+
+/* timer-wheel api */
+#if defined(ERTS_WANT_TIMER_WHEEL_API) && !defined(ERTS_GOT_TIMER_WHEEL_API)
+#define ERTS_GOT_TIMER_WHEEL_API
+
+#include "erl_thr_progress.h"
+#include "erl_process.h"
+
+void erts_sched_init_time_sup(ErtsSchedulerData *esdp);
+
+
+#define ERTS_TWHEEL_SLOT_AT_ONCE -1
+#define ERTS_TWHEEL_SLOT_INACTIVE -2
+
+/*
+** Timer entry:
+*/
+typedef struct erl_timer {
+    struct erl_timer* next;	/* next entry tiw slot or chain */
+    struct erl_timer* prev;	/* prev entry tiw slot or chain */
+    union {
+	struct {
+	    void (*timeout)(void*); /* called when timeout */
+	    void (*cancel)(void*);  /* called when cancel (may be NULL) */
+	    void* arg;              /* argument to timeout/cancel procs */
+	} func;
+	ErtsThrPrgrLaterOp cleanup;
+    } u;
+    ErtsMonotonicTime timeout_pos; /* Timeout in absolute clock ticks */
+    int slot;
+} ErtsTWheelTimer;
+
+typedef void (*ErlTimeoutProc)(void*);
+typedef void (*ErlCancelProc)(void*);
+
+void erts_twheel_set_timer(ErtsTimerWheel *tiw,
+			   ErtsTWheelTimer *p, ErlTimeoutProc timeout,
+			   ErlCancelProc cancel, void *arg,
+			   ErtsMonotonicTime timeout_pos);
+void erts_twheel_cancel_timer(ErtsTimerWheel *tiw, ErtsTWheelTimer *p);
+ErtsTimerWheel *erts_create_timer_wheel(ErtsSchedulerData *esdp);
+
+ErtsMonotonicTime erts_check_next_timeout_time(ErtsSchedulerData *);
+
+ERTS_GLB_INLINE void erts_twheel_init_timer(ErtsTWheelTimer *p);
+ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(ErtsNextTimeoutRef);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void erts_twheel_init_timer(ErtsTWheelTimer *p)
+{
+    p->slot = ERTS_TWHEEL_SLOT_INACTIVE;
+}
+
+ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(ErtsNextTimeoutRef nxt_tmo_ref)
+{
+    return *((ErtsMonotonicTime *) nxt_tmo_ref);
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#endif /* timer wheel api */
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index 9d572c0b0a..1139cb9c97 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -30,6 +30,8 @@
 #include "sys.h"
 #include "erl_vm.h"
 #include "global.h"
+#define ERTS_WANT_TIMER_WHEEL_API
+#include "erl_time.h"
  
 static erts_smp_mtx_t erts_timeofday_mtx;
 static erts_smp_mtx_t erts_get_time_mtx;
@@ -57,76 +59,6 @@ static int time_sup_initialized = 0;
 static void
 schedule_send_time_offset_changed_notifications(ErtsMonotonicTime new_offset);
 
-/*
- * NOTE! ERTS_MONOTONIC_TIME_START *need* to be a multiple
- *       of ERTS_MONOTONIC_TIME_UNIT.
- */
-
-#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT
-
-#ifdef ARCH_32
-/*
- * Want to use a big-num of arity 2 as long as possible (584 years
- * in the nano-second time unit case).
- */
-#define ERTS_MONOTONIC_TIME_START		\
-    (((((((ErtsMonotonicTime) 1) << 32)-1)	\
-       / ERTS_MONOTONIC_TIME_UNIT)		\
-      * ERTS_MONOTONIC_TIME_UNIT)		\
-     + ERTS_MONOTONIC_TIME_UNIT)
-
-#else /* ARCH_64 */
-
-#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT <= 10*1000*1000
-
-/*
- * Using micro second time unit or lower. Start at zero since
- * time will remain an immediate for a very long time anyway
- * (1827 years in the 10 micro second case)...
- */
-#define ERTS_MONOTONIC_TIME_START ((ErtsMonotonicTime) 0)
-
-#else /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT > 1000*1000 */
-
-/*
- * Want to use an immediate as long as possible (36 years in the
- * nano-second time unit case).
-*/
-#define ERTS_MONOTONIC_TIME_START 		\
-    ((((ErtsMonotonicTime) MIN_SMALL)		\
-      / ERTS_MONOTONIC_TIME_UNIT)		\
-     * ERTS_MONOTONIC_TIME_UNIT)
-
-#endif /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT > 1000*1000 */
-
-#endif /* ARCH_64 */
-
-#define ERTS_MONOTONIC_OFFSET_NATIVE \
-    (ERTS_MONOTONIC_TIME_START - ERTS_MONOTONIC_TIME_UNIT)
-#define ERTS_MONOTONIC_OFFSET_NSEC					\
-    ERTS_MONOTONIC_TO_NSEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
-#define ERTS_MONOTONIC_OFFSET_USEC					\
-    ERTS_MONOTONIC_TO_USEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
-#define ERTS_MONOTONIC_OFFSET_MSEC					\
-    ERTS_MONOTONIC_TO_MSEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
-#define ERTS_MONOTONIC_OFFSET_SEC					\
-    ERTS_MONOTONIC_TO_SEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
-
-#else /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */
-
-/*
- * Initialized in erts_init_time_sup()...
- */
-
-#define ERTS_MONOTONIC_TIME_START (time_sup.r.o.start)
-#define ERTS_MONOTONIC_OFFSET_NATIVE (time_sup.r.o.start_offset.native)
-#define ERTS_MONOTONIC_OFFSET_NSEC (time_sup.r.o.start_offset.nsec)
-#define ERTS_MONOTONIC_OFFSET_USEC (time_sup.r.o.start_offset.usec)
-#define ERTS_MONOTONIC_OFFSET_MSEC (time_sup.r.o.start_offset.msec)
-#define ERTS_MONOTONIC_OFFSET_SEC (time_sup.r.o.start_offset.sec)
-
-#endif /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */
-
 struct time_sup_read_only__ {
     ErtsMonotonicTime (*get_time)(void);
     int correction;
@@ -146,16 +78,6 @@ struct time_sup_read_only__ {
     int os_system_time_locked;
     Uint64 os_system_time_resolution;
     Uint64 os_system_time_extended;
-#if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT
-    ErtsMonotonicTime start;
-    struct {
-	ErtsMonotonicTime native;
-	ErtsMonotonicTime nsec;
-	ErtsMonotonicTime usec;
-	ErtsMonotonicTime msec;
-	ErtsMonotonicTime sec;
-    } start_offset;
-#endif
     struct {
 	ErtsMonotonicTime large_diff;
 	ErtsMonotonicTime small_diff;
@@ -211,7 +133,7 @@ struct time_sup_infrequently_changed__ {
 #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
     struct {
 	erts_smp_rwmtx_t rwmtx;
-	ErlTimer timer;
+	ErtsTWheelTimer timer;
 	ErtsMonotonicCorrectionData cdata;
     } parmon;
     ErtsMonotonicTime minit;
@@ -273,6 +195,17 @@ get_time_offset(void)
     return (ErtsMonotonicTime) erts_smp_atomic64_read_acqb(&time_sup.inf.c.offset);
 }
 
+static ERTS_INLINE void
+update_last_mtime(ErtsSchedulerData *esdp, ErtsMonotonicTime mtime)
+{
+    if (!esdp)
+	esdp = erts_get_scheduler_data();
+    if (esdp) {
+	ASSERT(mtime >= esdp->last_monotonic_time);
+	esdp->last_monotonic_time = mtime;
+	esdp->check_time_reds = 0;
+    }
+}
 
 #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
 
@@ -411,15 +344,26 @@ print_correction(int change,
 
 #endif
 
+static ERTS_INLINE ErtsMonotonicTime
+get_timeout_pos(ErtsMonotonicTime now, ErtsMonotonicTime tmo)
+{
+    ErtsMonotonicTime tpos;
+    tpos = ERTS_MONOTONIC_TO_CLKTCKS(now - 1);
+    tpos += ERTS_MSEC_TO_CLKTCKS(tmo);
+    tpos += 1;
+    return tpos;
+}
+
 static void
-check_time_correction(void *idap)
+check_time_correction(void *vesdp)
 {
-    UWord init_drift_adj = (UWord) idap;
+    int init_drift_adj = !vesdp;
+    ErtsSchedulerData *esdp = (ErtsSchedulerData *) vesdp;
     ErtsMonotonicCorrectionData cdata;
     ErtsMonotonicCorrection new_correction;
     ErtsMonotonicCorrectionInstance *cip;
     ErtsMonotonicTime mdiff, sdiff, os_mtime, erl_mtime, os_stime,
-	erl_stime, time_offset;
+	erl_stime, time_offset, timeout_pos;
     Uint timeout;
     int os_drift_corrected = time_sup.r.o.os_corrected_monotonic_time;
     int set_new_correction = 0, begin_short_intervals = 0;
@@ -681,6 +625,8 @@ check_time_correction(void *idap)
 	timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK);
     }
 
+    timeout_pos = get_timeout_pos(erl_mtime, timeout);
+
 #ifdef ERTS_TIME_CORRECTION_PRINT
     print_correction(set_new_correction,
 		     sdiff,
@@ -723,11 +669,15 @@ check_time_correction(void *idap)
 	erts_smp_rwmtx_rwunlock(&time_sup.inf.c.parmon.rwmtx);
     }
 
-    erts_set_timer(&time_sup.inf.c.parmon.timer,
-		   check_time_correction,
-		   NULL,
-		   NULL,
-		   timeout);
+    if (!esdp)
+	esdp = erts_get_scheduler_data();
+
+    erts_twheel_set_timer(esdp->timer_wheel,
+			  &time_sup.inf.c.parmon.timer,
+			  check_time_correction,
+			  NULL,
+			  (void *) esdp,
+			  timeout_pos);
 }
 
 static ErtsMonotonicTime get_os_corrected_time(void)
@@ -737,10 +687,11 @@ static ErtsMonotonicTime get_os_corrected_time(void)
 }
 
 static void
-check_time_offset(void *unused)
+check_time_offset(void *vesdp)
 {
+    ErtsSchedulerData *esdp = (ErtsSchedulerData *) vesdp;
     ErtsMonotonicTime sdiff, os_mtime, erl_mtime, os_stime,
-	erl_stime, time_offset;
+	erl_stime, time_offset, timeout, timeout_pos;
 
     ASSERT(time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE);
 
@@ -769,15 +720,19 @@ check_time_offset(void *unused)
 		      ERTS_MONOTONIC_TO_NSEC(sdiff));
 #endif
 
-    erts_set_timer(&time_sup.inf.c.parmon.timer,
-		   check_time_offset,
-		   NULL,
-		   NULL,
-		   ERTS_MONOTONIC_TO_MSEC(ERTS_LONG_TIME_CORRECTION_CHECK));
+    timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_LONG_TIME_CORRECTION_CHECK);
+    timeout_pos = get_timeout_pos(erl_mtime, timeout);
+
+    erts_twheel_set_timer(esdp->timer_wheel,
+			  &time_sup.inf.c.parmon.timer,
+			  check_time_offset,
+			  NULL,
+			  vesdp,
+			  timeout_pos);
 }
 
 static void
-init_check_time_correction(void *quick_init_drift)
+init_check_time_correction(void *vesdp)
 {
     ErtsMonotonicDriftData *ddp;
     ErtsMonotonicTime old_mtime, old_stime, mtime, stime, mtime_diff,
@@ -821,7 +776,7 @@ init_check_time_correction(void *quick_init_drift)
     ddp->ix = 0;
     ddp->dirty_counter = time_sup.r.o.drift_adj.intervals;
 
-    check_time_correction(quick_init_drift);
+    check_time_correction(vesdp);
 }
 
 static ErtsMonotonicTime
@@ -850,14 +805,14 @@ finalize_corrected_time_offset(ErtsSystemTime *stimep)
 }
 
 static void
-late_init_time_correction(void)
+late_init_time_correction(ErtsSchedulerData *esdp)
 {
-    Uint timeout;
-    Uint quick_init_drift_adj;
+    int quick_init_drift_adj;
     void (*check_func)(void *);
+    ErtsMonotonicTime timeout, timeout_pos;
 
     quick_init_drift_adj =
-	(Uint) ERTS_MONOTONIC_TO_USEC(time_sup.r.o.drift_adj.error) == 0;
+	ERTS_MONOTONIC_TO_USEC(time_sup.r.o.drift_adj.error) == 0;
 
     if (quick_init_drift_adj)
 	timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK/10);
@@ -866,17 +821,25 @@ late_init_time_correction(void)
 
     if (!time_sup.r.o.os_corrected_monotonic_time)
 	check_func = init_check_time_correction;
-    else if (time_sup.r.o.get_time == get_os_corrected_time)
+    else if (time_sup.r.o.get_time == get_os_corrected_time) {
+	quick_init_drift_adj = 0;
 	check_func = check_time_offset;
+    }
     else
 	check_func = check_time_correction;
 
-    erts_init_timer(&time_sup.inf.c.parmon.timer);
-    erts_set_timer(&time_sup.inf.c.parmon.timer,
-		   check_func,
-		   NULL,
-		   (void *) quick_init_drift_adj,
-		   timeout);
+    timeout_pos = get_timeout_pos(erts_get_monotonic_time(esdp),
+				  timeout);
+
+    erts_twheel_init_timer(&time_sup.inf.c.parmon.timer);
+    erts_twheel_set_timer(esdp->timer_wheel,
+			  &time_sup.inf.c.parmon.timer,
+			  check_func,
+			  NULL,
+			  (quick_init_drift_adj
+			   ? NULL
+			   : esdp),
+			  timeout_pos);
 }
 
 #endif /* ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT */
@@ -987,6 +950,8 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
     ErtsMonotonicTime abs_native_offset, native_offset;
 #endif
 
+    erts_hl_timer_init();
+
     ASSERT(ERTS_MONOTONIC_TIME_MIN < ERTS_MONOTONIC_TIME_MAX);
 
     erts_smp_mtx_init(&erts_timeofday_mtx, "timeofday");
@@ -1003,51 +968,55 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
 
 #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT
 
+    /*
+     * NOTE! erts_time_sup__.r.o.start *need* to be a multiple
+     *       of ERTS_MONOTONIC_TIME_UNIT.
+     */
+
 #ifdef ARCH_32
-    time_sup.r.o.start = ((((ErtsMonotonicTime) 1) << 32)-1);
-    time_sup.r.o.start /= ERTS_MONOTONIC_TIME_UNIT;
-    time_sup.r.o.start *= ERTS_MONOTONIC_TIME_UNIT;
-    time_sup.r.o.start += ERTS_MONOTONIC_TIME_UNIT;
-    native_offset = time_sup.r.o.start - ERTS_MONOTONIC_TIME_UNIT;
+    erts_time_sup__.r.o.start = ((((ErtsMonotonicTime) 1) << 32)-1);
+    erts_time_sup__.r.o.start /= ERTS_MONOTONIC_TIME_UNIT;
+    erts_time_sup__.r.o.start *= ERTS_MONOTONIC_TIME_UNIT;
+    erts_time_sup__.r.o.start += ERTS_MONOTONIC_TIME_UNIT;
+    native_offset = erts_time_sup__.r.o.start - ERTS_MONOTONIC_BEGIN;
     abs_native_offset = native_offset;
 #else /* ARCH_64 */
     if (ERTS_MONOTONIC_TIME_UNIT <= 10*1000*1000) {
-	time_sup.r.o.start = 0;
-	native_offset = -ERTS_MONOTONIC_TIME_UNIT;
-	abs_native_offset = ERTS_MONOTONIC_TIME_UNIT;
+	erts_time_sup__.r.o.start = 0;
+	native_offset = -ERTS_MONOTONIC_BEGIN;
+	abs_native_offset = ERTS_MONOTONIC_BEGIN;
     }
     else {
-	time_sup.r.o.start = ((ErtsMonotonicTime) MIN_SMALL);
-	time_sup.r.o.start /= ERTS_MONOTONIC_TIME_UNIT;
-	time_sup.r.o.start *= ERTS_MONOTONIC_TIME_UNIT;
-	native_offset = time_sup.r.o.start - ERTS_MONOTONIC_TIME_UNIT;
+	erts_time_sup__.r.o.start = ((ErtsMonotonicTime) MIN_SMALL);
+	erts_time_sup__.r.o.start /= ERTS_MONOTONIC_TIME_UNIT;
+	erts_time_sup__.r.o.start *= ERTS_MONOTONIC_TIME_UNIT;
+	native_offset = erts_time_sup__.r.o.start - ERTS_MONOTONIC_BEGIN;
 	abs_native_offset = -1*native_offset;
     }
 #endif
 
-    time_sup.r.o.start_offset.native = (time_sup.r.o.start
-					- ERTS_MONOTONIC_TIME_UNIT);
-    time_sup.r.o.start_offset.nsec = (ErtsMonotonicTime)
+    erts_time_sup__.r.o.start_offset.native = native_offset;
+    erts_time_sup__.r.o.start_offset.nsec = (ErtsMonotonicTime)
 	erts_time_unit_conversion((Uint64) abs_native_offset,
 				  (Uint32) ERTS_MONOTONIC_TIME_UNIT,
 				  (Uint32) 1000*1000*1000);
-    time_sup.r.o.start_offset.usec = (ErtsMonotonicTime)
+    erts_time_sup__.r.o.start_offset.usec = (ErtsMonotonicTime)
 	erts_time_unit_conversion((Uint64) abs_native_offset,
 				  (Uint32) ERTS_MONOTONIC_TIME_UNIT,
 				  (Uint32) 1000*1000);
-    time_sup.r.o.start_offset.msec = (ErtsMonotonicTime)
+    erts_time_sup__.r.o.start_offset.msec = (ErtsMonotonicTime)
 	erts_time_unit_conversion((Uint64) abs_native_offset,
 				  (Uint32) ERTS_MONOTONIC_TIME_UNIT,
 				  (Uint32) 1000);
-    time_sup.r.o.start_offset.sec = (ErtsMonotonicTime)
+    erts_time_sup__.r.o.start_offset.sec = (ErtsMonotonicTime)
 	erts_time_unit_conversion((Uint64) abs_native_offset,
 				  (Uint32) ERTS_MONOTONIC_TIME_UNIT,
 				  (Uint32) 1);
     if (native_offset < 0) {
-	time_sup.r.o.start_offset.nsec *= -1;
-	time_sup.r.o.start_offset.usec *= -1;
-	time_sup.r.o.start_offset.msec *= -1;
-	time_sup.r.o.start_offset.sec *= -1;
+	erts_time_sup__.r.o.start_offset.nsec *= -1;
+	erts_time_sup__.r.o.start_offset.usec *= -1;
+	erts_time_sup__.r.o.start_offset.msec *= -1;
+	erts_time_sup__.r.o.start_offset.sec *= -1;
     }
 
 #endif
@@ -1143,9 +1112,9 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
 	erts_os_times(&time_sup.inf.c.minit,
 		      &time_sup.inf.c.sinit);
 	time_sup.r.o.moffset = -1*time_sup.inf.c.minit;
-	time_sup.r.o.moffset += ERTS_MONOTONIC_TIME_UNIT;
+	time_sup.r.o.moffset += ERTS_MONOTONIC_BEGIN;
 	offset = time_sup.inf.c.sinit;
-	offset -= ERTS_MONOTONIC_TIME_UNIT;
+	offset -= ERTS_MONOTONIC_BEGIN;
 	init_time_offset(offset);
 
 	rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
@@ -1160,7 +1129,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
 	cdatap->drift.intervals[0].time.mon = time_sup.inf.c.minit;
 	cdatap->curr.correction.drift = 0;
 	cdatap->curr.correction.error = 0;
-	cdatap->curr.erl_mtime = ERTS_MONOTONIC_TIME_UNIT;
+	cdatap->curr.erl_mtime = ERTS_MONOTONIC_BEGIN;
 	cdatap->curr.os_mtime = time_sup.inf.c.minit;
 	cdatap->last_check = time_sup.inf.c.minit;
 	cdatap->short_check_interval = ERTS_INIT_SHORT_INTERVAL_COUNTER;
@@ -1179,7 +1148,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
 	ErtsMonotonicTime stime, offset;
 	time_sup.r.o.get_time = get_not_corrected_time;
 	stime = time_sup.inf.c.sinit = erts_os_system_time();
-	offset = stime - ERTS_MONOTONIC_TIME_UNIT;
+	offset = stime - ERTS_MONOTONIC_BEGIN;
 	time_sup.inf.c.not_corrected_moffset = offset;
 	init_time_offset(offset);
 	time_sup.f.c.last_not_corrected_time = 0;
@@ -1199,14 +1168,24 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
 void
 erts_late_init_time_sup(void)
 {
-#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
-    /* Timer wheel must have beeen initialized */
-    if (time_sup.r.o.get_time != get_not_corrected_time)
-	late_init_time_correction();
-#endif
     erts_late_sys_init_time();
 }
 
+void
+erts_sched_init_time_sup(ErtsSchedulerData *esdp)
+{
+    esdp->timer_wheel = erts_create_timer_wheel(esdp);
+    esdp->next_tmo_ref = erts_get_next_timeout_reference(esdp->timer_wheel);
+    esdp->timer_service = erts_create_timer_service();
+#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
+    if (esdp->no == 1) {
+	/* A timer wheel to use must have beeen initialized */
+	if (time_sup.r.o.get_time != get_not_corrected_time)
+	    late_init_time_correction(esdp);
+    }
+#endif	
+}
+
 ErtsTimeWarpMode erts_time_warp_mode(void)
 {
     return time_sup.r.o.warp_mode;
@@ -1349,6 +1328,7 @@ wall_clock_elapsed_time_both(UWord *ms_total, UWord *ms_diff)
     erts_smp_mtx_lock(&erts_timeofday_mtx);
 
     now = time_sup.r.o.get_time();
+    update_last_mtime(NULL, now);
 
     elapsed = ERTS_MONOTONIC_TO_MSEC(now);
     *ms_total = (UWord) elapsed;
@@ -1737,6 +1717,7 @@ get_now(Uint* megasec, Uint* sec, Uint* microsec)
     
     mtime = time_sup.r.o.get_time();
     time_offset = get_time_offset();
+    update_last_mtime(NULL, mtime);
     now = ERTS_MONOTONIC_TO_USEC(mtime + time_offset);
 
     erts_smp_mtx_lock(&erts_timeofday_mtx);
@@ -1761,9 +1742,11 @@ get_now(Uint* megasec, Uint* sec, Uint* microsec)
 }
 
 ErtsMonotonicTime
-erts_get_monotonic_time(void)
+erts_get_monotonic_time(ErtsSchedulerData *esdp)
 {
-    return time_sup.r.o.get_time();
+    ErtsMonotonicTime mtime = time_sup.r.o.get_time();
+    update_last_mtime(esdp, mtime);
+    return mtime;    
 }
 
 void
@@ -1994,7 +1977,13 @@ make_time_val(Process *c_p, ErtsMonotonicTime time_val)
 Eterm
 erts_get_monotonic_start_time(struct process *c_p)
 {
-    return make_time_val(c_p, ERTS_MONOTONIC_TIME_START);
+    return make_time_val(c_p, ERTS_MONOTONIC_TIME_START_EXTERNAL);
+}
+
+Eterm
+erts_get_monotonic_end_time(struct process *c_p)
+{
+    return make_time_val(c_p, ERTS_MONOTONIC_TIME_END_EXTERNAL);
 }
 
 static Eterm
@@ -2186,13 +2175,16 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto
 BIF_RETTYPE monotonic_time_0(BIF_ALIST_0)
 {
     ErtsMonotonicTime mtime = time_sup.r.o.get_time();
+    update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime);
     mtime += ERTS_MONOTONIC_OFFSET_NATIVE;
     BIF_RET(make_time_val(BIF_P, mtime));
 }
 
 BIF_RETTYPE monotonic_time_1(BIF_ALIST_1)
 {
-    BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, time_sup.r.o.get_time(), 1));
+    ErtsMonotonicTime mtime = time_sup.r.o.get_time();
+    update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime);
+    BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, mtime, 1));
 }
 
 BIF_RETTYPE system_time_0(BIF_ALIST_0)
@@ -2200,6 +2192,7 @@ BIF_RETTYPE system_time_0(BIF_ALIST_0)
     ErtsMonotonicTime mtime, offset;
     mtime = time_sup.r.o.get_time();
     offset = get_time_offset();
+    update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime);
     BIF_RET(make_time_val(BIF_P, mtime + offset));
 }
 
@@ -2208,6 +2201,7 @@ BIF_RETTYPE system_time_1(BIF_ALIST_0)
     ErtsMonotonicTime mtime, offset;
     mtime = time_sup.r.o.get_time();
     offset = get_time_offset();
+    update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime);
     BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, mtime + offset, 0));
 }
 
@@ -2237,6 +2231,7 @@ BIF_RETTYPE timestamp_0(BIF_ALIST_0)
 
     mtime = time_sup.r.o.get_time();
     offset = get_time_offset();
+    update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime);
     stime = ERTS_MONOTONIC_TO_USEC(mtime + offset);
     all_sec = stime / ERTS_MONOTONIC_TIME_MEGA;
     mega_sec = (Uint) (stime / ERTS_MONOTONIC_TIME_TERA);
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 634fe533d0..44d4ef18dd 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1305,8 +1305,7 @@ erts_alloc_message_heap_state(Uint size,
     state = erts_smp_atomic32_read_acqb(&receiver->state);
     if (statep)
 	*statep = state;
-    if (state & (ERTS_PSFLG_OFF_HEAP_MSGS
-		 | ERTS_PSFLG_EXITING
+    if (state & (ERTS_PSFLG_EXITING
 		 | ERTS_PSFLG_PENDING_EXIT))
 	goto allocate_in_mbuf;
 #endif
@@ -1327,8 +1326,7 @@ erts_alloc_message_heap_state(Uint size,
 	state = erts_smp_atomic32_read_nob(&receiver->state);
 	if (statep)
 	    *statep = state;
-	if ((state & (ERTS_PSFLG_OFF_HEAP_MSGS
-		      | ERTS_PSFLG_EXITING
+	if ((state & (ERTS_PSFLG_EXITING
 		      | ERTS_PSFLG_PENDING_EXIT))
 	    || (receiver->flags & F_DISABLE_GC)
 	    || HEAP_LIMIT(receiver) - HEAP_TOP(receiver) <= size) {
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 1db3a9fba7..3e5e97ab15 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -48,6 +48,7 @@
 #include "dtrace-wrapper.h"
 #include "erl_map.h"
 #include "erl_bif_unique.h"
+#include "erl_hl_timer.h"
 
 extern ErlDrvEntry fd_driver_entry;
 #ifndef __OSE__
@@ -379,11 +380,7 @@ static Port *create_port(char *name,
     prt->dist_entry = NULL;
     ERTS_PORT_INIT_CONNECTED(prt, pid);
     prt->common.u.alive.reg = NULL;
-#ifdef ERTS_SMP
-    prt->common.u.alive.ptimer = NULL;
-#else
-    sys_memset(&prt->common.u.alive.tm, 0, sizeof(ErlTimer));
-#endif
+    ERTS_PTMR_INIT(prt);
     erts_port_task_handle_init(&prt->timeout_task);
     prt->psd = NULL;
     prt->drv_data = (SWord) 0;
@@ -463,11 +460,7 @@ erts_port_free(Port *prt)
 			    | ERTS_PORT_SFLG_FREE));
     ASSERT(state & ERTS_PORT_SFLG_PORT_DEBUG);
 
-#ifdef ERTS_SMP
-    ERTS_LC_ASSERT(erts_atomic32_read_nob(&prt->common.refc) == 0);
-#else
-    ERTS_LC_ASSERT(erts_atomic32_read_nob(&prt->refc) == 0);
-#endif
+    ERTS_LC_ASSERT(erts_atomic_read_nob(&prt->common.refc.atmc) == 0);
 
     erts_port_task_fini_sched(&prt->sched);
 
@@ -736,11 +729,7 @@ erts_open_driver(erts_driver_t* driver,	/* Pointer to driver. */
 	/*
 	 * Must clean up the port.
 	 */
-#ifdef ERTS_SMP
-	erts_cancel_smp_ptimer(port->common.u.alive.ptimer);
-#else
-	erts_cancel_timer(&(port->common.u.alive.tm));
-#endif
+	erts_cancel_port_timer(port);
 	stopq(port);
 	if (port->linebuf != NULL) {
 	    erts_free(ERTS_ALC_T_LINEBUF,
@@ -2806,7 +2795,8 @@ void erts_init_io(int port_tab_size,
 			 port_tab_size,
 			 common_element_size, /* Doesn't need to be excact */
 			 "port_table",
-			 legacy_port_tab);
+			 legacy_port_tab,
+			 1);
 
     erts_smp_atomic_init_nob(&erts_bytes_out, 0);
     erts_smp_atomic_init_nob(&erts_bytes_in, 0);
@@ -3073,7 +3063,7 @@ deliver_result(Eterm sender, Eterm pid, Eterm res)
 
     rp = (scheduler
 	  ? erts_proc_lookup(pid)
-	  : erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC));
+	  : erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_INC_REFC));
 
     if (rp) {
 	Eterm tuple;
@@ -3095,7 +3085,7 @@ deliver_result(Eterm sender, Eterm pid, Eterm res)
 	if (rp_locks)
 	    erts_smp_proc_unlock(rp, rp_locks);
 	if (!scheduler)
-	    erts_smp_proc_dec_refc(rp);
+	    erts_proc_dec_refc(rp);
 
     }
 }
@@ -3139,7 +3129,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
 
     rp = (scheduler
 	  ? erts_proc_lookup(to)
-	  : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC));
+	  : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_INC_REFC));
 
     if (!rp)
 	return;
@@ -3194,7 +3184,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
     if (rp_locks)
 	erts_smp_proc_unlock(rp, rp_locks);
     if (!scheduler)
-	erts_smp_proc_dec_refc(rp);
+	erts_proc_dec_refc(rp);
 }
 
 /* 
@@ -3280,7 +3270,7 @@ deliver_vec_message(Port* prt,			/* Port */
 
     rp = (scheduler
 	  ? erts_proc_lookup(to)
-	  : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC));
+	  : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_INC_REFC));
     if (!rp)
 	return;
 
@@ -3364,7 +3354,7 @@ deliver_vec_message(Port* prt,			/* Port */
 		       );
     erts_smp_proc_unlock(rp, rp_locks);
     if (!scheduler)
-	erts_smp_proc_dec_refc(rp);
+	erts_proc_dec_refc(rp);
 }
 
 
@@ -3454,11 +3444,8 @@ terminate_port(Port *prt)
 	send_closed_port_id = NIL;
     }
 
-#ifdef ERTS_SMP
-    erts_cancel_smp_ptimer(prt->common.u.alive.ptimer);
-#else
-    erts_cancel_timer(&prt->common.u.alive.tm);
-#endif
+    if (ERTS_PTMR_IS_SET(prt))
+	erts_cancel_port_timer(prt);
 
     drv = prt->drv_ptr;
     if ((drv != NULL) && (drv->stop != NULL)) {
@@ -5005,24 +4992,6 @@ erts_free_port_names(ErtsPortNames *pnp)
     erts_free(ERTS_ALC_T_PORT_NAMES, pnp);
 }
 
-static void schedule_port_timeout(Port *p)
-{
-    /*
-     * Scheduling of port timeouts can be done without port locking, but
-     * since the task handle is stored in the port structure and the ptimer
-     * structure is protected by the port lock we require the port to be
-     * locked for now...
-     *
-     * TODO: Implement scheduling of port timeouts without locking
-     *       the port.
-     * /Rickard
-     */
-    ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
-    erts_port_task_schedule(p->common.id,
-			    &p->timeout_task,
-			    ERTS_PORT_TASK_TIMEOUT);
-}
-
 ErlDrvTermData driver_mk_term_nil(void)
 {
     return driver_term_nil;
@@ -5051,7 +5020,7 @@ void driver_report_exit(ErlDrvPort ix, int status)
 
    rp = (scheduler
 	 ? erts_proc_lookup(pid)
-	 : erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC));
+	 : erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_INC_REFC));
    if (!rp)
        return;
 
@@ -5069,7 +5038,7 @@ void driver_report_exit(ErlDrvPort ix, int status)
 
    erts_smp_proc_unlock(rp, rp_locks);
    if (!scheduler)
-       erts_smp_proc_dec_refc(rp);
+       erts_proc_dec_refc(rp);
 }
 
 #define ERTS_B2T_STATES_DEF_STATES_SZ 5
@@ -5385,7 +5354,7 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
     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));
+	  : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_INC_REFC));
     if (!rp) {
 	res = 0;
 	goto done;
@@ -5682,14 +5651,12 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
 	    HRelease(rp, hp_end, hp);
 	}
     }
-#ifdef ERTS_SMP
     if (rp) {
 	if (rp_locks)
 	    erts_smp_proc_unlock(rp, rp_locks);
 	if (!scheduler)
-	    erts_smp_proc_dec_refc(rp);
+	    erts_proc_dec_refc(rp);
     }
-#endif
     cleanup_b2t_states(&b2t);
     DESTROY_ESTACK(stack);
     return res;
@@ -6635,18 +6602,6 @@ int driver_pushq(ErlDrvPort ix, char* buffer, ErlDrvSizeT len)
     return code;
 }
 
-static ERTS_INLINE void
-drv_cancel_timer(Port *prt)
-{
-#ifdef ERTS_SMP
-    erts_cancel_smp_ptimer(prt->common.u.alive.ptimer);
-#else
-    erts_cancel_timer(&prt->common.u.alive.tm);
-#endif
-    if (erts_port_task_is_scheduled(&prt->timeout_task))
-	erts_port_task_abort(&prt->timeout_task);
-}
-
 int driver_set_timer(ErlDrvPort ix, unsigned long t)
 {
     Port* prt = erts_drvport2port(ix);
@@ -6658,19 +6613,8 @@ int driver_set_timer(ErlDrvPort ix, unsigned long t)
 
     if (prt->drv_ptr->timeout == NULL)
 	return -1;
-    drv_cancel_timer(prt);
-#ifdef ERTS_SMP
-    erts_create_smp_ptimer(&prt->common.u.alive.ptimer,
-			   prt->common.id,
-			   (ErlTimeoutProc) schedule_port_timeout,
-			   t);
-#else
-    erts_set_timer(&prt->common.u.alive.tm,
-		  (ErlTimeoutProc) schedule_port_timeout,
-		  NULL,
-		  prt,
-		  t);
-#endif
+
+    erts_set_port_timer(prt, (Sint64) t);
     return 0;
 }
 
@@ -6680,28 +6624,28 @@ int driver_cancel_timer(ErlDrvPort ix)
     if (prt == ERTS_INVALID_ERL_DRV_PORT)
 	return -1;
     ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
-    drv_cancel_timer(prt);
+    erts_cancel_port_timer(prt);
     return 0;
 }
 
-
 int
 driver_read_timer(ErlDrvPort ix, unsigned long* t)
 {
     Port* prt = erts_drvport2port(ix);
+    Sint64 left;
 
     ERTS_SMP_CHK_NO_PROC_LOCKS;
 
     if (prt == ERTS_INVALID_ERL_DRV_PORT)
 	return -1;
     ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
-#ifdef ERTS_SMP
-    *t = (prt->common.u.alive.ptimer
-	  ? erts_time_left(&prt->common.u.alive.ptimer->timer.tm)
-	  : 0);
-#else
-    *t = erts_time_left(&prt->common.u.alive.tm);
-#endif
+
+    left = erts_read_port_timer(prt);
+    if (left < 0)
+	left = 0;
+
+    *t = (unsigned long) left;
+
     return 0;
 }
 
diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c
index c626cb2780..4d557b3a17 100644
--- a/erts/emulator/beam/register.c
+++ b/erts/emulator/beam/register.c
@@ -269,7 +269,10 @@ erts_whereis_name_to_id(Process *c_p, Eterm name)
 #ifdef ERTS_SMP
     ErtsProcLocks c_p_locks = c_p ? ERTS_PROC_LOCK_MAIN : 0;
 
-    ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(c_p);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+    if (c_p) ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(c_p);
+#endif
+
     reg_safe_read_lock(c_p, &c_p_locks);
     if (c_p && !c_p_locks)
         erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
@@ -380,8 +383,6 @@ erts_whereis_name(Process *c_p,
 			erts_smp_proc_unlock(rp->p, need_locks);
 		    *proc = NULL;
 		}
-		if (*proc && (flags & ERTS_P2P_FLG_SMP_INC_REFC))
-		    erts_smp_proc_inc_refc(rp->p);
 	    }
 #else
 	    if (rp->p
@@ -390,6 +391,8 @@ erts_whereis_name(Process *c_p,
 	    else
 		*proc = NULL;
 #endif
+	    if (*proc && (flags & ERTS_P2P_FLG_INC_REFC))
+		erts_proc_inc_refc(*proc);
 	}
     }
 
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index f7a21406f3..54059ee9a5 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -776,8 +776,6 @@ extern char *erts_sys_ddll_error(int code);
 /*
  * System interfaces for startup.
  */
-#include "erl_time.h"
-
 void erts_sys_schedule_interrupt(int set);
 #ifdef ERTS_SMP
 void erts_sys_schedule_interrupt_timed(int, ErtsMonotonicTime);
@@ -819,7 +817,8 @@ int univ_to_local(
 int local_to_univ(Sint *year, Sint *month, Sint *day, 
 		  Sint *hour, Sint *minute, Sint *second, int isdst);
 void get_now(Uint*, Uint*, Uint*);
-ErtsMonotonicTime erts_get_monotonic_time(void);
+struct ErtsSchedulerData_;
+ErtsMonotonicTime erts_get_monotonic_time(struct ErtsSchedulerData_ *);
 void get_sys_now(Uint*, Uint*, Uint*);
 void set_break_quit(void (*)(void), void (*)(void));
 
diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c
index 1fffb5f357..ea19d8b362 100644
--- a/erts/emulator/beam/time.c
+++ b/erts/emulator/beam/time.c
@@ -76,6 +76,11 @@
 #include "sys.h"
 #include "erl_vm.h"
 #include "global.h"
+#define ERTS_WANT_TIMER_WHEEL_API
+#include "erl_time.h"
+
+#define ERTS_MONOTONIC_DAY ERTS_SEC_TO_MONOTONIC(60*60*24)
+#define ERTS_CLKTCKS_DAY ERTS_MONOTONIC_TO_CLKTCKS(ERTS_MONOTONIC_DAY)
 
 #ifdef ERTS_ENABLE_LOCK_CHECK
 #define ASSERT_NO_LOCKED_LOCKS		erts_lc_check_exact(NULL, 0)
@@ -83,20 +88,24 @@
 #define ASSERT_NO_LOCKED_LOCKS
 #endif
 
-#define ERTS_MONOTONIC_DAY ERTS_SEC_TO_MONOTONIC(60*60*24)
-#define ERTS_CLKTCKS_DAY ERTS_MONOTONIC_TO_CLKTCKS(ERTS_MONOTONIC_DAY)
-
+#if 0
+#  define ERTS_TW_DEBUG
+#endif
+#if defined(DEBUG) && !defined(ERTS_TW_DEBUG)
+#  define ERTS_TW_DEBUG
+#endif
 
-/* BEGIN tiw_lock protected variables 
-**
-** The individual timer cells in tiw are also protected by the same mutex.
-*/
+#undef ERTS_TW_ASSERT
+#if defined(ERTS_TW_DEBUG)
+#  define ERTS_TW_ASSERT(E) ERTS_ASSERT(E)
+#else
+#  define ERTS_TW_ASSERT(E) ((void) 1)
+#endif
 
-/* timing wheel size NEED to be a power of 2 */
-#ifdef SMALL_MEMORY
-#define TIW_SIZE (1 << 13)
+#ifdef ERTS_TW_DEBUG
+#  define ERTS_TWHEEL_BUMP_YIELD_LIMIT 5
 #else
-#define TIW_SIZE (1 << 20)
+#  define ERTS_TWHEEL_BUMP_YIELD_LIMIT 100
 #endif
 
 /* Actual interval time chosen by sys_init_time() */
@@ -110,54 +119,22 @@ static int tiw_itime; /* Constant after init */
 #endif
 
 struct ErtsTimerWheel_ {
-    ErlTimer *w[TIW_SIZE];
+    ErtsTWheelTimer *w[ERTS_TIW_SIZE];
     ErtsMonotonicTime pos;
     Uint nto;
     struct {
-	ErlTimer *head;
-	ErlTimer *tail;
+	ErtsTWheelTimer *head;
+	ErtsTWheelTimer *tail;
 	Uint nto;
     } at_once;
+    int yield_slot;
+    int yield_slots_left;
+    int yield_start_pos;
+    ErtsTWheelTimer sentinel;
     int true_next_timeout_time;
     ErtsMonotonicTime next_timeout_time;
-    erts_atomic64_t next_timeout;
-    erts_smp_atomic32_t is_bumping;
-    erts_smp_mtx_t lock;
 };
 
-ErtsTimerWheel *erts_default_timer_wheel; /* managed by aux thread */
-
-static ERTS_INLINE ErtsTimerWheel *
-get_timer_wheel(ErlTimer *p)
-{
-    return (ErtsTimerWheel *) erts_smp_atomic_read_acqb(&p->wheel);
-}
-
-static ERTS_INLINE void
-set_timer_wheel(ErlTimer *p, ErtsTimerWheel *tiw)
-{
-    erts_smp_atomic_set_relb(&p->wheel, (erts_aint_t) tiw);
-}
-
-static ERTS_INLINE void
-init_next_timeout(ErtsTimerWheel *tiw,
-		  ErtsMonotonicTime time)
-{
-    erts_atomic64_init_nob(&tiw->next_timeout,
-			   (erts_aint64_t) time);
-}
-
-static ERTS_INLINE void
-set_next_timeout(ErtsTimerWheel *tiw,
-		 ErtsMonotonicTime time,
-		 int true_timeout)
-{
-    tiw->true_next_timeout_time = true_timeout;
-    tiw->next_timeout_time = time;
-    erts_atomic64_set_relb(&tiw->next_timeout,
-			   (erts_aint64_t) time);
-}
-
 /* get the time (in units of TIW_ITIME) to the next timeout,
    or -1 if there are no timeouts                     */
 
@@ -167,35 +144,23 @@ find_next_timeout(ErtsTimerWheel *tiw,
 		  ErtsMonotonicTime max_search_time)
 {
     int start_ix, tiw_pos_ix;
-    ErlTimer *p;
+    ErtsTWheelTimer *p;
     int true_min_timeout;
-    ErtsMonotonicTime min_timeout, min_timeout_pos, slot_timeout_pos, timeout_limit;
-
-    ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&tiw->lock));
+    ErtsMonotonicTime min_timeout, min_timeout_pos, slot_timeout_pos;
 
     if (tiw->true_next_timeout_time)
 	return tiw->next_timeout_time;
 
-    /* We never set next timeout beyond timeout_limit */
-    timeout_limit = curr_time + ERTS_MONOTONIC_DAY;
-
     if (tiw->nto == 0) { /* no timeouts in wheel */
-	true_min_timeout = tiw->true_next_timeout_time = 0;
-	min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(timeout_limit);
+	true_min_timeout = 0;
+	min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time + ERTS_MONOTONIC_DAY);
 	goto found_next;
     }
 
-    /*
-     * Don't want others entering trying to bump
-     * timers while we are checking...
-     */
-    set_next_timeout(tiw, timeout_limit, 0);
-
-    true_min_timeout = 1;
     slot_timeout_pos = tiw->pos;
     min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time + max_search_time);
 
-    start_ix = tiw_pos_ix = (int) (tiw->pos & (TIW_SIZE-1));
+    start_ix = tiw_pos_ix = (int) (tiw->pos & (ERTS_TIW_SIZE-1));
 
     do {
 	slot_timeout_pos++;
@@ -206,93 +171,125 @@ find_next_timeout(ErtsTimerWheel *tiw,
 
 	p = tiw->w[tiw_pos_ix];
 
-	while (p) {
-	    ErtsMonotonicTime timeout_pos;
-	    ASSERT(p != p->next);
-	    timeout_pos = p->timeout_pos;
-	    if (min_timeout_pos > timeout_pos) {
-		min_timeout_pos = timeout_pos;
-		if (min_timeout_pos <= slot_timeout_pos)
-		    goto found_next;
-	    }
-	    p = p->next;
+	if (p) {
+	    ErtsTWheelTimer *end = p;
+
+	    do  {
+		ErtsMonotonicTime timeout_pos;
+		timeout_pos = p->timeout_pos;
+		if (min_timeout_pos > timeout_pos) {
+		    true_min_timeout = 1;
+		    min_timeout_pos = timeout_pos;
+		    if (min_timeout_pos <= slot_timeout_pos)
+			goto found_next;
+		}
+		p = p->next;
+	    } while (p != end);
 	}
 
 	tiw_pos_ix++;
-	if (tiw_pos_ix == TIW_SIZE)
+	if (tiw_pos_ix == ERTS_TIW_SIZE)
 	    tiw_pos_ix = 0;
     } while (start_ix != tiw_pos_ix);
 
 found_next:
 
     min_timeout = ERTS_CLKTCKS_TO_MONOTONIC(min_timeout_pos);
-    if (min_timeout != tiw->next_timeout_time)
-	set_next_timeout(tiw, min_timeout, true_min_timeout);
+    tiw->next_timeout_time = min_timeout;
+    tiw->true_next_timeout_time = true_min_timeout;
 
     return min_timeout;
 }
 
-static void
-remove_timer(ErtsTimerWheel *tiw, ErlTimer *p)
+static ERTS_INLINE void
+insert_timer_into_slot(ErtsTimerWheel *tiw, int slot, ErtsTWheelTimer *p)
 {
-    if (p->slot >= 0) {
-	/* Timer in wheel... */
-	ASSERT(p->slot < TIW_SIZE);
-	if (p->prev)
-	    p->prev->next = p->next;
-	else {
-	    ASSERT(tiw->w[p->slot] == p);
-	    tiw->w[p->slot] = p->next;
+    ERTS_TW_ASSERT(slot >= 0);
+    ERTS_TW_ASSERT(slot < ERTS_TIW_SIZE);
+    p->slot = slot;
+    if (!tiw->w[slot]) {
+	tiw->w[slot] = p;
+	p->next = p;
+	p->prev = p;
+    }
+    else {
+	ErtsTWheelTimer *next, *prev;
+	next = tiw->w[slot];
+	prev = next->prev;
+	p->next = next;
+	p->prev = prev;
+	prev->next = p;
+	next->prev = p;
+    }
+}
+
+static ERTS_INLINE void
+remove_timer(ErtsTimerWheel *tiw, ErtsTWheelTimer *p)
+{
+    int slot = p->slot;
+    ERTS_TW_ASSERT(slot != ERTS_TWHEEL_SLOT_INACTIVE);
+
+    if (slot >= 0) {
+	/*
+	 * Timer in wheel or in circular
+	 * list of timers currently beeing
+	 * triggered (referred by sentinel).
+	 */
+	ERTS_TW_ASSERT(slot < ERTS_TIW_SIZE);
+
+	if (p->next == p) {
+	    ERTS_TW_ASSERT(tiw->w[slot] == p);
+	    tiw->w[slot] = NULL;
 	}
-	if(p->next)
+	else {
+	    if (tiw->w[slot] == p)
+		tiw->w[slot] = p->next;
+	    p->prev->next = p->next;
 	    p->next->prev = p->prev;
+	}
     }
     else {
 	/* Timer in "at once" queue... */
-	ASSERT(p->slot == -1);
+	ERTS_TW_ASSERT(slot == ERTS_TWHEEL_SLOT_AT_ONCE);
 	if (p->prev)
 	    p->prev->next = p->next;
 	else {
-	    ASSERT(tiw->at_once.head == p);
+	    ERTS_TW_ASSERT(tiw->at_once.head == p);
 	    tiw->at_once.head = p->next;
 	}
 	if (p->next)
 	    p->next->prev = p->prev;
 	else {
-	    ASSERT(tiw->at_once.tail == p);
+	    ERTS_TW_ASSERT(tiw->at_once.tail == p);
 	    tiw->at_once.tail = p->prev;
 	}
-	ASSERT(tiw->at_once.nto > 0);
+	ERTS_TW_ASSERT(tiw->at_once.nto > 0);
 	tiw->at_once.nto--;
     }
 
-    p->slot = -2;
+    p->slot = ERTS_TWHEEL_SLOT_INACTIVE;
 
+#if 0
     p->next = NULL;
     p->prev = NULL;
+#endif
 
-    set_timer_wheel(p, NULL);
     tiw->nto--;
 }
 
 ErtsMonotonicTime
-erts_check_next_timeout_time(ErtsTimerWheel *tiw,
-			     ErtsMonotonicTime max_search_time)
+erts_check_next_timeout_time(ErtsSchedulerData *esdp)
 {
-    ErtsMonotonicTime next, curr;
-
-    curr = erts_get_monotonic_time();
-
-    erts_smp_mtx_lock(&tiw->lock);
-
-    next = find_next_timeout(tiw, curr, max_search_time);
-
-    erts_smp_mtx_unlock(&tiw->lock);
-
-    return next;
+    /*
+     * Called before a scheduler is about to wait. We wont
+     * check more than 10 minutes into the future.
+     */
+    return find_next_timeout(esdp->timer_wheel,
+			     erts_get_monotonic_time(esdp),
+			     ERTS_SEC_TO_MONOTONIC(10*60));
 }
 
-#ifndef DEBUG
+#ifndef ERTS_TW_DEBUG
 #define ERTS_DBG_CHK_SAFE_TO_SKIP_TO(TIW, TO) ((void) 0)
 #else
 #define ERTS_DBG_CHK_SAFE_TO_SKIP_TO(TIW, TO) debug_check_safe_to_skip_to((TIW), (TO))
@@ -300,198 +297,255 @@ static void
 debug_check_safe_to_skip_to(ErtsTimerWheel *tiw, ErtsMonotonicTime skip_to_pos)
 {
     int slots, ix;
-    ErlTimer *tmr;
+    ErtsTWheelTimer *tmr;
     ErtsMonotonicTime tmp;
 
-    ix = (int) (tiw->pos & (TIW_SIZE-1));
+    ix = (int) (tiw->pos & (ERTS_TIW_SIZE-1));
     tmp = skip_to_pos - tiw->pos;
-    ASSERT(tmp >= 0);
-    if (tmp < (ErtsMonotonicTime) TIW_SIZE)
+    ERTS_TW_ASSERT(tmp >= 0);
+    if (tmp < (ErtsMonotonicTime) ERTS_TIW_SIZE)
 	slots = (int) tmp;
     else
-	slots = TIW_SIZE;
+	slots = ERTS_TIW_SIZE;
 
      while (slots > 0) {
-	tmr = tiw->w[ix];
-	while (tmr) {
-	    ASSERT(tmr->timeout_pos > skip_to_pos);
-	    tmr = tmr->next;
-	}
-	ix++;
-	if (ix == TIW_SIZE)
-	    ix = 0;
-	slots--;
+	 tmr = tiw->w[ix];
+	 if (tmr) {
+	     ErtsTWheelTimer *end = tmr;
+	     do {
+		 ERTS_TW_ASSERT(tmr->timeout_pos > skip_to_pos);
+		 tmr = tmr->next;
+	     } while (tmr != end);
+	 }
+	 ix++;
+	 if (ix == ERTS_TIW_SIZE)
+	     ix = 0;
+	 slots--;
     }
 }
 #endif
 
+static ERTS_INLINE void
+timeout_timer(ErtsTWheelTimer *p)
+{
+    ErlTimeoutProc timeout;
+    void *arg;
+#if 0
+    p->next = NULL;
+    p->prev = NULL;
+#endif
+    p->slot = ERTS_TWHEEL_SLOT_INACTIVE;
+    timeout = p->u.func.timeout;
+    arg = p->u.func.arg;
+    (*timeout)(arg);
+}
+
 void
 erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time)
 {
-    int tiw_pos_ix, slots;
-    ErlTimer *p, *timeout_head, **timeout_tail;
-    ErtsMonotonicTime bump_to, tmp_slots;
-
-    if (erts_smp_atomic32_cmpxchg_nob(&tiw->is_bumping, 1, 0) != 0)
-	return; /* Another thread is currently bumping... */
+    int tiw_pos_ix, slots, yielded_slot_restarted, yield_count;
+    ErtsMonotonicTime bump_to, tmp_slots, old_pos;
 
-    bump_to = ERTS_MONOTONIC_TO_CLKTCKS(curr_time);
+    yield_count = ERTS_TWHEEL_BUMP_YIELD_LIMIT;
 
-    erts_smp_mtx_lock(&tiw->lock);
+    /*
+     * In order to be fair we always continue with work
+     * where we left off when restarting after a yield.
+     */
 
-    if (tiw->pos >= bump_to) {
-	timeout_head = NULL;
-	goto done;
+    if (tiw->yield_slot >= 0) {
+	yielded_slot_restarted = 1;
+	tiw_pos_ix = tiw->yield_slot;
+	slots = tiw->yield_slots_left;
+	bump_to = tiw->pos;
+	old_pos = tiw->yield_start_pos;
+	goto restart_yielded_slot;
     }
 
-    /* Don't want others here while we are bumping... */
-    set_next_timeout(tiw, curr_time + ERTS_MONOTONIC_DAY, 0);
+    do {
+
+	yielded_slot_restarted = 0;
+
+	bump_to = ERTS_MONOTONIC_TO_CLKTCKS(curr_time);
 
-    if (!tiw->at_once.head) {
-	timeout_head = NULL;
-	timeout_tail = &timeout_head;
-    }
-    else {
-	ASSERT(tiw->nto >= tiw->at_once.nto);
-	p = timeout_head = tiw->at_once.head;
 	while (1) {
-	    set_timer_wheel(p, NULL);
-	    if (!p->next) {
-		timeout_tail = &p->next;
-		break;
+	    ErtsTWheelTimer *p;
+
+	    old_pos = tiw->pos;
+
+	    if (tiw->nto == 0) {
+	    empty_wheel:
+		ERTS_DBG_CHK_SAFE_TO_SKIP_TO(tiw, bump_to);
+		tiw->true_next_timeout_time = 0;
+		tiw->next_timeout_time = curr_time + ERTS_MONOTONIC_DAY;
+		tiw->pos = bump_to;
+		tiw->yield_slot = ERTS_TWHEEL_SLOT_INACTIVE;
+		return;
 	    }
-	}
-	tiw->nto -= tiw->at_once.nto;
-	tiw->at_once.head = NULL;
-	tiw->at_once.tail = NULL;
-	tiw->at_once.nto = 0;
-    }
 
-    if (tiw->nto == 0) {
-	ERTS_DBG_CHK_SAFE_TO_SKIP_TO(tiw, bump_to);
-	tiw->pos = bump_to;
-	goto done;
-    }
+	    p = tiw->at_once.head;
+	    while (p) {
+		if (--yield_count <= 0) {
+		    ERTS_TW_ASSERT(tiw->nto > 0);
+		    ERTS_TW_ASSERT(tiw->at_once.nto > 0);
+		    tiw->yield_slot = ERTS_TWHEEL_SLOT_AT_ONCE;
+		    tiw->true_next_timeout_time = 1;
+		    tiw->next_timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(old_pos);
+		    return;
+		}
+
+		ERTS_TW_ASSERT(tiw->nto > 0);
+		ERTS_TW_ASSERT(tiw->at_once.nto > 0);
+		tiw->nto--;
+		tiw->at_once.nto--;
+		tiw->at_once.head = p->next;
+		if (p->next)
+		    p->next->prev = NULL;
+		else
+		    tiw->at_once.tail = NULL;
+
+		timeout_timer(p);
+
+		p = tiw->at_once.head;
+	    }
 
-    if (tiw->true_next_timeout_time) {
-	ErtsMonotonicTime skip_until_pos;
-	/*
-	 * No need inspecting slots where we know no timeouts
-	 * to trigger should reside.
-	 */
+	    if (tiw->pos >= bump_to)
+		break;
 
-	skip_until_pos = ERTS_MONOTONIC_TO_CLKTCKS(tiw->next_timeout_time);
-	if (skip_until_pos > bump_to)
-	    skip_until_pos = bump_to;
+	    if (tiw->nto == 0)
+		goto empty_wheel;
 
-	ERTS_DBG_CHK_SAFE_TO_SKIP_TO(tiw, skip_until_pos);
-	ASSERT(skip_until_pos > tiw->pos);
+	    if (tiw->true_next_timeout_time) {
+		ErtsMonotonicTime skip_until_pos;
+		/*
+		 * No need inspecting slots where we know no timeouts
+		 * to trigger should reside.
+		 */
 
-	tiw->pos = skip_until_pos - 1;
-    }
+		skip_until_pos = ERTS_MONOTONIC_TO_CLKTCKS(tiw->next_timeout_time);
+		if (skip_until_pos > bump_to)
+		    skip_until_pos = bump_to;
 
-    tiw_pos_ix = (int) ((tiw->pos+1) & (TIW_SIZE-1));
-    tmp_slots = (bump_to - tiw->pos);
-    if (tmp_slots < (ErtsMonotonicTime) TIW_SIZE)
-	slots = (int) tmp_slots;
-    else
-	slots = TIW_SIZE;
- 
-    while (slots > 0) {
-	p = tiw->w[tiw_pos_ix];
-	while (p) {
-	    ErlTimer *next = p->next;
-	    ASSERT(p != next);
-	    if (p->timeout_pos <= bump_to) { /* we have a timeout */
-		/* Remove from list */
-		remove_timer(tiw, p);
-		*timeout_tail = p;	/* Insert in timeout queue */
-		timeout_tail = &p->next;
+		skip_until_pos--;
+
+		if (skip_until_pos > tiw->pos) {
+		    ERTS_DBG_CHK_SAFE_TO_SKIP_TO(tiw, skip_until_pos);
+
+		    tiw->pos = skip_until_pos;
+		}
+	    }
+
+	    tiw_pos_ix = (int) ((tiw->pos+1) & (ERTS_TIW_SIZE-1));
+	    tmp_slots = (bump_to - tiw->pos);
+	    if (tmp_slots < (ErtsMonotonicTime) ERTS_TIW_SIZE)
+		slots = (int) tmp_slots;
+	    else
+		slots = ERTS_TIW_SIZE;
+
+	    tiw->pos = bump_to;
+
+	    while (slots > 0) {
+
+		p = tiw->w[tiw_pos_ix];
+		if (p) {
+		    if (p->next == p) {
+			ERTS_TW_ASSERT(tiw->sentinel.next == &tiw->sentinel);
+			ERTS_TW_ASSERT(tiw->sentinel.prev == &tiw->sentinel);
+		    }
+		    else {
+			tiw->sentinel.next = p->next;
+			tiw->sentinel.prev = p->prev;
+			tiw->sentinel.next->prev = &tiw->sentinel;
+			tiw->sentinel.prev->next = &tiw->sentinel;
+		    }
+		    tiw->w[tiw_pos_ix] = NULL;
+
+		    while (1) {
+
+			if (p->timeout_pos > bump_to) {
+			    /* Very unusual case... */
+			    ++yield_count;
+			    insert_timer_into_slot(tiw, tiw_pos_ix, p);
+			}
+			else {
+			    /* Normal case... */
+			    timeout_timer(p);
+			    tiw->nto--;
+			}
+
+		    restart_yielded_slot:
+
+			p = tiw->sentinel.next;
+			if (p == &tiw->sentinel) {
+			    ERTS_TW_ASSERT(tiw->sentinel.prev == &tiw->sentinel);
+			    break;
+			}
+
+			if (--yield_count <= 0) {
+			    tiw->true_next_timeout_time = 1;
+			    tiw->next_timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(old_pos);
+			    tiw->yield_slot = tiw_pos_ix;
+			    tiw->yield_slots_left = slots;
+			    tiw->yield_start_pos = old_pos;
+			    return; /* Yield! */
+			}
+
+			tiw->sentinel.next = p->next;
+			p->next->prev = &tiw->sentinel;
+		    }
+		}
+		tiw_pos_ix++;
+		if (tiw_pos_ix == ERTS_TIW_SIZE)
+		    tiw_pos_ix = 0;
+		slots--;
 	    }
-	    p = next;
 	}
-	tiw_pos_ix++;
-	if (tiw_pos_ix == TIW_SIZE)
-	    tiw_pos_ix = 0;
-	slots--;
-    }
 
-    ASSERT(tmp_slots >= (ErtsMonotonicTime) TIW_SIZE
-	   || tiw_pos_ix == (int) ((bump_to+1) & (TIW_SIZE-1)));
+    } while (yielded_slot_restarted);
 
-    tiw->pos = bump_to;
+    tiw->yield_slot = ERTS_TWHEEL_SLOT_INACTIVE;
+    tiw->true_next_timeout_time = 0;
+    tiw->next_timeout_time = curr_time + ERTS_MONOTONIC_DAY;
 
     /* Search at most two seconds ahead... */
     (void) find_next_timeout(tiw, curr_time, ERTS_SEC_TO_MONOTONIC(2));
-
-done:
-
-    erts_smp_mtx_unlock(&tiw->lock);
-    
-    erts_smp_atomic32_set_nob(&tiw->is_bumping, 0);
-
-    /* Call timedout timers callbacks */
-    while (timeout_head) {
-	ErlTimeoutProc timeout;
-	void *arg;
-	p = timeout_head;
-	timeout_head = p->next;
-	/* Here comes hairy use of the timer fields!
-	 * They are reset without having the lock.
-	 * It is assumed that no code but this will
-	 * accesses any field until the ->timeout
-	 * callback is called.
-	 */
-	ASSERT(p->timeout_pos <= bump_to);
-	p->next = NULL;
-	p->prev = NULL;
-	p->slot = 0;
-	timeout = p->timeout;
-	arg = p->arg;
-	(*timeout)(arg);
-    }
 }
 
 Uint
 erts_timer_wheel_memory_size(void)
 {
-#ifdef ERTS_SMP
-    return sizeof(ErtsTimerWheel)*(1 + erts_no_schedulers);
-#else
-    return sizeof(ErtsTimerWheel);
-#endif
+    return sizeof(ErtsTimerWheel)*erts_no_schedulers;
 }
 
 ErtsTimerWheel *
-erts_create_timer_wheel(int no)
+erts_create_timer_wheel(ErtsSchedulerData *esdp)
 {
     ErtsMonotonicTime mtime;
     int i;
     ErtsTimerWheel *tiw;
-    tiw = (ErtsTimerWheel *) erts_alloc(ERTS_ALC_T_TIMER_WHEEL,
-					sizeof(ErtsTimerWheel));
-    for(i = 0; i < TIW_SIZE; i++)
+    tiw = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_TIMER_WHEEL,
+					     sizeof(ErtsTimerWheel));
+    for(i = 0; i < ERTS_TIW_SIZE; i++)
 	tiw->w[i] = NULL;
 
-    erts_smp_atomic32_init_nob(&tiw->is_bumping, 0);
-    erts_smp_mtx_init_x(&tiw->lock, "timer_wheel", make_small(no));
-
-    mtime = erts_get_monotonic_time();
+    mtime = erts_get_monotonic_time(esdp);
     tiw->pos = ERTS_MONOTONIC_TO_CLKTCKS(mtime);
     tiw->nto = 0;
     tiw->at_once.head = NULL;
     tiw->at_once.tail = NULL;
     tiw->at_once.nto = 0;
+    tiw->yield_slot = ERTS_TWHEEL_SLOT_INACTIVE;
     tiw->true_next_timeout_time = 0;
     tiw->next_timeout_time = mtime + ERTS_MONOTONIC_DAY;
-    init_next_timeout(tiw, mtime + ERTS_MONOTONIC_DAY);
+    tiw->sentinel.next = &tiw->sentinel;
+    tiw->sentinel.prev = &tiw->sentinel;
     return tiw;
 }
 
 ErtsNextTimeoutRef
 erts_get_next_timeout_reference(ErtsTimerWheel *tiw)
 {
-    return (ErtsNextTimeoutRef) &tiw->next_timeout;
+    return (ErtsNextTimeoutRef) &tiw->next_timeout_time;
 }
 
 
@@ -512,155 +566,83 @@ erts_init_time(int time_correction, ErtsTimeWarpMode time_warp_mode)
 #else
     tiw_itime = itime;
 #endif
-
-    erts_default_timer_wheel = erts_create_timer_wheel(0);
 }
 
 void
-erts_set_timer(ErlTimer *p, ErlTimeoutProc timeout,
-	       ErlCancelProc cancel, void *arg, Uint to)
+erts_twheel_set_timer(ErtsTimerWheel *tiw,
+		      ErtsTWheelTimer *p, ErlTimeoutProc timeout,
+		      ErlCancelProc cancel, void *arg,
+		      ErtsMonotonicTime timeout_pos)
 {
-    ErtsMonotonicTime timeout_time, timeout_pos;
-    ErtsMonotonicTime curr_time;
-    ErtsTimerWheel *tiw;
-    ErtsSchedulerData *esdp;
-
-    curr_time = erts_get_monotonic_time();
-    esdp = erts_get_scheduler_data();
-    if (esdp)
-	tiw = esdp->timer_wheel;
-    else
-	tiw = erts_default_timer_wheel;
+    ErtsMonotonicTime timeout_time;
 
-    erts_smp_mtx_lock(&tiw->lock);
+    p->u.func.timeout = timeout;
+    p->u.func.cancel = cancel;
+    p->u.func.arg = arg;
 
-    if (get_timer_wheel(p))
-	ERTS_INTERNAL_ERROR("Double set timer");
+    ERTS_TW_ASSERT(p->slot == ERTS_TWHEEL_SLOT_INACTIVE);
 
-    p->timeout = timeout;
-    p->cancel = cancel;
-    p->arg = arg;
-
-    if (to == 0) {
-	timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time);
+    if (timeout_pos <= tiw->pos) {
 	tiw->nto++;
 	tiw->at_once.nto++;
 	p->next = NULL;
 	p->prev = tiw->at_once.tail;
-	tiw->at_once.tail = p;
-	if (!tiw->at_once.head)
+	if (tiw->at_once.tail) {
+	    ERTS_TW_ASSERT(tiw->at_once.head);
+	    tiw->at_once.tail->next = p;
+	}
+	else {	
+	    ERTS_TW_ASSERT(!tiw->at_once.head);
 	    tiw->at_once.head = p;
-	p->timeout_pos = timeout_pos;
-	p->slot = -1;
-	timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(timeout_pos);
+	}
+	tiw->at_once.tail = p;
+	p->timeout_pos = tiw->pos;
+	p->slot = ERTS_TWHEEL_SLOT_AT_ONCE;
+	timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(tiw->pos);
     }
     else {
-	int tm;
-	ErtsMonotonicTime ticks;
-
-	ticks = ERTS_MSEC_TO_CLKTCKS(to);
-	timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time - 1) + 1 + ticks;
+	int slot;
 
 	/* calculate slot */
-	tm = (int) (timeout_pos & (TIW_SIZE-1));
-	p->slot = tm;
-  
-	/* insert at head of list at slot */
-	p->next = tiw->w[tm];
-	p->prev = NULL;
-	if (p->next != NULL)
-	    p->next->prev = p;
-	tiw->w[tm] = p;
+	slot = (int) (timeout_pos & (ERTS_TIW_SIZE-1));
+
+	insert_timer_into_slot(tiw, slot, p);
 
 	tiw->nto++;
 
 	timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(timeout_pos);
 	p->timeout_pos = timeout_pos;
-
-	ASSERT(ERTS_MSEC_TO_MONOTONIC(to) <= timeout_time - curr_time);
-	ASSERT(timeout_time - curr_time
-	       < ERTS_MSEC_TO_MONOTONIC(to) + ERTS_CLKTCKS_TO_MONOTONIC(1));
     }
 
-    if (timeout_time < tiw->next_timeout_time)
-	set_next_timeout(tiw, timeout_time, 1);
-
-    set_timer_wheel(p, tiw);
-
-    erts_smp_mtx_unlock(&tiw->lock);
-
-#if defined(ERTS_SMP)
-    if (tiw == erts_default_timer_wheel)
-	erts_interupt_aux_thread_timed(timeout_time);
-#endif
-
+    if (timeout_time < tiw->next_timeout_time) {
+	tiw->true_next_timeout_time = 1;
+	tiw->next_timeout_time = timeout_time;
+    }
 }
 
 void
-erts_cancel_timer(ErlTimer *p)
+erts_twheel_cancel_timer(ErtsTimerWheel *tiw, ErtsTWheelTimer *p)
 {
-    ErtsTimerWheel *tiw;
-    ErlCancelProc cancel;
-    void *arg = NULL; /* Shut up faulty warning... */
-
-    tiw = get_timer_wheel(p);
-    if (!tiw)
-	return;
-    
-    erts_smp_mtx_lock(&tiw->lock);
-    if (tiw != get_timer_wheel(p))
-	cancel = NULL;
-    else {
+    if (p->slot != ERTS_TWHEEL_SLOT_INACTIVE) {
+	ErlCancelProc cancel;
+	void *arg;
 	remove_timer(tiw, p);
-
-	cancel = p->cancel;
-	arg = p->arg;
+	cancel = p->u.func.cancel;
+	arg = p->u.func.arg;
+	if (cancel)
+	    (*cancel)(arg);
     }
-    erts_smp_mtx_unlock(&tiw->lock);
-
-    if (cancel)
-	(*cancel)(arg);
 }
 
-/*
-  Returns the amount of time left in ms until the timer 'p' is triggered.
-  0 is returned if 'p' isn't active.
-  0 is returned also if the timer is overdue (i.e., would have triggered
-  immediately if it hadn't been cancelled).
-*/
-Uint
-erts_time_left(ErlTimer *p)
-{
-    ErtsTimerWheel *tiw;
-    ErtsMonotonicTime current_time, timeout_time;
-
-    tiw = get_timer_wheel(p);
-    if (!tiw)
-	return 0;
-
-    erts_smp_mtx_lock(&tiw->lock);
-    if (tiw != get_timer_wheel(p))
-	timeout_time = ERTS_MONOTONIC_TIME_MIN;
-    else
-	timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(p->timeout_pos);
-    erts_smp_mtx_unlock(&tiw->lock);
-
-    current_time = erts_get_monotonic_time();
-    if (timeout_time <= current_time)
-	return 0;
-    return (Uint) ERTS_MONOTONIC_TO_MSEC(timeout_time - current_time);
-}
-
-#ifdef DEBUG
+#ifdef ERTS_TW_DEBUG
 void erts_p_slpq(void)
 {
-    ErtsTimerWheel *tiw = erts_default_timer_wheel;
-    ErtsMonotonicTime current_time = erts_get_monotonic_time();
+    erts_printf("Not yet implemented...\n");
+#if 0
+    ErtsMonotonicTime current_time = erts_get_monotonic_time(NULL);
     int i;
-    ErlTimer* p;
+    ErtsTWheelTimer* p;
   
-    erts_smp_mtx_lock(&tiw->lock);
-
     /* print the whole wheel, starting at the current position */
     erts_printf("\ncurrent time = %bps tiw_pos = %d tiw_nto %d\n",
 		current_time, tiw->pos, tiw->nto);
@@ -673,7 +655,7 @@ void erts_p_slpq(void)
 			p->slot);
 	}
     }
-    for(i = ((i+1) & (TIW_SIZE-1)); i != (tiw->pos & (TIW_SIZE-1)); i = ((i+1) & (TIW_SIZE-1))) {
+    for(i = ((i+1) & (ERTS_TIW_SIZE-1)); i != (tiw->pos & (ERTS_TIW_SIZE-1)); i = ((i+1) & (ERTS_TIW_SIZE-1))) {
 	if (tiw->w[i] != NULL) {
 	    erts_printf("%d:\n", i);
 	    for(p = tiw->w[i]; p != NULL; p = p->next) {
@@ -682,7 +664,6 @@ void erts_p_slpq(void)
 	    }
 	}
     }
-
-    erts_smp_mtx_unlock(&tiw->lock);
+#endif
 }
-#endif /* DEBUG */
+#endif /* ERTS_TW_DEBUG */
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index cb4ef2b376..4cbc4b0caf 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -50,6 +50,8 @@
 #include "erl_ptab.h"
 #include "erl_check_io.h"
 #include "erl_bif_unique.h"
+#define ERTS_WANT_TIMER_WHEEL_API
+#include "erl_time.h"
 #ifdef HIPE
 #  include "hipe_mode_switch.h"
 #endif
@@ -4397,145 +4399,6 @@ is_string(Eterm list)
     return 0;
 }
 
-#ifdef ERTS_SMP
-
-/*
- * Process and Port timers in smp case
- */
-
-ERTS_SCHED_PREF_PRE_ALLOC_IMPL(ptimer_pre, ErtsSmpPTimer, 1000)
-
-#define ERTS_PTMR_FLGS_ALLCD_SIZE \
-  2
-#define ERTS_PTMR_FLGS_ALLCD_MASK \
-  ((((Uint32) 1) << ERTS_PTMR_FLGS_ALLCD_SIZE) - 1)
-
-#define ERTS_PTMR_FLGS_PREALLCD	((Uint32) 1)
-#define ERTS_PTMR_FLGS_SLALLCD	((Uint32) 2)
-#define ERTS_PTMR_FLGS_LLALLCD	((Uint32) 3)
-#define ERTS_PTMR_FLG_CANCELLED	(((Uint32) 1) << (ERTS_PTMR_FLGS_ALLCD_SIZE+0))
-
-static void
-init_ptimers(void)
-{
-    init_ptimer_pre_alloc();
-}
-
-static ERTS_INLINE void
-free_ptimer(ErtsSmpPTimer *ptimer)
-{
-    switch (ptimer->timer.flags & ERTS_PTMR_FLGS_ALLCD_MASK) {
-    case ERTS_PTMR_FLGS_PREALLCD:
-	(void) ptimer_pre_free(ptimer);
-	break;
-    case ERTS_PTMR_FLGS_SLALLCD:
-	erts_free(ERTS_ALC_T_SL_PTIMER, (void *) ptimer);
-	break;
-    case ERTS_PTMR_FLGS_LLALLCD:
-	erts_free(ERTS_ALC_T_LL_PTIMER, (void *) ptimer);
-	break;
-    default:
-	erl_exit(ERTS_ABORT_EXIT,
-		 "Internal error: Bad ptimer alloc type\n");
-	break;
-    }
-}
-
-/* Callback for process timeout cancelled */
-static void
-ptimer_cancelled(ErtsSmpPTimer *ptimer)
-{
-    free_ptimer(ptimer);
-}
-
-/* Callback for process timeout */
-static void
-ptimer_timeout(ErtsSmpPTimer *ptimer)
-{
-    if (is_internal_pid(ptimer->timer.id)) {
-	Process *p;
-	p = erts_pid2proc_opt(NULL,
-			      0,
-			      ptimer->timer.id,
-			      ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS,
-			      ERTS_P2P_FLG_ALLOW_OTHER_X);
-	if (p) {
-	    if (!ERTS_PROC_IS_EXITING(p)
-		&& !(ptimer->timer.flags & ERTS_PTMR_FLG_CANCELLED)) {
-		ASSERT(*ptimer->timer.timer_ref == ptimer);
-		*ptimer->timer.timer_ref = NULL;
-		(*ptimer->timer.timeout_func)(p);
-	    }
-	    erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
-	}
-    }
-    else {
-	Port *p;
-	ASSERT(is_internal_port(ptimer->timer.id));
-	p = erts_id2port_sflgs(ptimer->timer.id,
-			       NULL,
-			       0,
-			       ERTS_PORT_SFLGS_DEAD);
-	if (p) {
-	    if (!(ptimer->timer.flags & ERTS_PTMR_FLG_CANCELLED)) {
-		ASSERT(*ptimer->timer.timer_ref == ptimer);
-		*ptimer->timer.timer_ref = NULL;
-		(*ptimer->timer.timeout_func)(p);
-	    }
-	    erts_port_release(p);
-	}
-    }
-    free_ptimer(ptimer);
-}
-
-void
-erts_create_smp_ptimer(ErtsSmpPTimer **timer_ref,
-		       Eterm id,
-		       ErlTimeoutProc timeout_func,
-		       Uint timeout)
-{
-    ErtsSmpPTimer *res = ptimer_pre_alloc();
-    if (res)
-	res->timer.flags = ERTS_PTMR_FLGS_PREALLCD;
-    else {
-	if (timeout < ERTS_ALC_MIN_LONG_LIVED_TIME) {
-	    res = erts_alloc(ERTS_ALC_T_SL_PTIMER, sizeof(ErtsSmpPTimer));
-	    res->timer.flags = ERTS_PTMR_FLGS_SLALLCD;
-	}
-	else {
-	    res = erts_alloc(ERTS_ALC_T_LL_PTIMER, sizeof(ErtsSmpPTimer));
-	    res->timer.flags = ERTS_PTMR_FLGS_LLALLCD;
-	}
-    }
-    res->timer.timeout_func = timeout_func;
-    res->timer.timer_ref = timer_ref;
-    res->timer.id = id;
-    erts_init_timer(&res->timer.tm);
-
-    ASSERT(!*timer_ref);
-
-    *timer_ref = res;
-
-    erts_set_timer(&res->timer.tm,
-		  (ErlTimeoutProc) ptimer_timeout,
-		  (ErlCancelProc) ptimer_cancelled,
-		  (void*) res,
-		  timeout);
-}
-
-void
-erts_cancel_smp_ptimer(ErtsSmpPTimer *ptimer)
-{
-    if (ptimer) {
-	ASSERT(*ptimer->timer.timer_ref == ptimer);
-	*ptimer->timer.timer_ref = NULL;
-	ptimer->timer.flags |= ERTS_PTMR_FLG_CANCELLED;
-	erts_cancel_timer(&ptimer->timer.tm);
-    }
-}
-
-#endif
-
 static int trim_threshold;
 static int top_pad;
 static int mmap_threshold;
@@ -4545,9 +4408,7 @@ Uint tot_bin_allocated;
 
 void erts_init_utils(void)
 {
-#ifdef ERTS_SMP
-    init_ptimers();
-#endif
+
 }
 
 void erts_init_utils_mem(void) 
diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c
index 61406b92af..2804d46249 100644
--- a/erts/emulator/hipe/hipe_debug.c
+++ b/erts/emulator/hipe/hipe_debug.c
@@ -213,9 +213,9 @@ void hipe_print_pcb(Process *p)
     U("seq..clock ", seq_trace_clock);
     U("seq..astcnt", seq_trace_lastcnt);
     U("seq..token ", seq_trace_token);
-    U("intial[0]  ", initial[0]);
-    U("intial[1]  ", initial[1]);
-    U("intial[2]  ", initial[2]);
+    U("intial[0]  ", u.initial[0]);
+    U("intial[1]  ", u.initial[1]);
+    U("intial[2]  ", u.initial[2]);
     P("current    ", current);
     P("cp         ", cp);
     P("i          ", i);
diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c
index 7e8632b50d..85d945823e 100644
--- a/erts/emulator/hipe/hipe_native_bif.c
+++ b/erts/emulator/hipe/hipe_native_bif.c
@@ -102,7 +102,8 @@ BIF_RETTYPE hipe_set_timeout(BIF_ALIST_1)
      * p->def_arg_reg[0] and p->i are both defined and used.
      * If a message arrives, BEAM resumes at p->i.
      * If a timeout fires, BEAM resumes at p->def_arg_reg[0].
-     * (See set_timer() and timeout_proc() in erl_process.c.)
+     * (See erts_set_proc_timer() and proc_timeout_common() in
+     * erl_hl_timer.c.)
      *
      * Here we set p->def_arg_reg[0] to hipe_beam_pc_resume.
      * Assuming our caller invokes suspend immediately after
@@ -135,28 +136,21 @@ BIF_RETTYPE hipe_set_timeout(BIF_ALIST_1)
      */
     if (p->flags & (F_INSLPQUEUE | F_TIMO))
 	return NIL;	/* caller had better call nbif_suspend ASAP! */
-    if (is_small(timeout_value) && signed_val(timeout_value) >= 0 &&
-#if defined(ARCH_64)
-	(unsigned_val(timeout_value) >> 32) == 0
-#else
-	1
-#endif
-	) {
-	set_timer(p, unsigned_val(timeout_value));
-    } else if (timeout_value == am_infinity) {
+
+    if (timeout_value == am_infinity) {
 	/* p->flags |= F_TIMO; */	/* XXX: nbif_suspend_msg_timeout */
-#if !defined(ARCH_64)
-    } else if (term_to_Uint(timeout_value, &time_val)) {
-	set_timer(p, time_val);
-#endif
-    } else {
+    }
+    else {
+	int tres = erts_set_proc_timer_term(p, timeout_value);
+	if (tres != 0) { /* Wrong time */
 #ifdef ERTS_SMP
-	if (p->hipe_smp.have_receive_locks) {
-	    p->hipe_smp.have_receive_locks = 0;
-	    erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
-	}
+	    if (p->hipe_smp.have_receive_locks) {
+		p->hipe_smp.have_receive_locks = 0;
+		erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
+	    }
 #endif
-	BIF_ERROR(p, EXC_TIMEOUT_VALUE);
+	    BIF_ERROR(p, EXC_TIMEOUT_VALUE);
+	}
     }
     return NIL;	/* caller had better call nbif_suspend ASAP! */
 }
@@ -170,7 +164,7 @@ void hipe_select_msg(Process *p)
     msgp = PEEK_MESSAGE(p);
     UNLINK_MESSAGE(p, msgp);	/* decrements global 'erts_proc_tot_mem' variable */
     JOIN_MESSAGE(p);
-    CANCEL_TIMER(p);		/* calls erl_cancel_timer() */
+    CANCEL_TIMER(p);		/* calls erts_cancel_proc_timer() */
     free_message(msgp);
 }
 
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 7be17d20bb..d1d6696090 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -38,6 +38,8 @@
 #include "erl_check_io.h"
 #include "erl_thr_progress.h"
 #include "dtrace-wrapper.h"
+#define ERTS_WANT_TIMER_WHEEL_API
+#include "erl_time.h"
 
 #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
 #else
@@ -1616,8 +1618,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
 
     /* Figure out timeout value */
     timeout_time = (do_wait
-		    ? erts_check_next_timeout_time(esdp->timer_wheel,
-						   ERTS_SEC_TO_MONOTONIC(10*60))
+		    ? erts_check_next_timeout_time(esdp)
 		    : ERTS_POLL_NO_TIMEOUT /* poll only */);
 
     /*
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index f4d4a85ca4..446cca77ff 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -2026,7 +2026,7 @@ get_timeout(ErtsPollSet ps,
     }
     else {
 	ErtsMonotonicTime diff_time, current_time;
-	current_time = erts_get_monotonic_time();
+	current_time = erts_get_monotonic_time(NULL);
 	diff_time = timeout_time - current_time;
 	if (diff_time <= 0) {
 	    save_timeout_time = ERTS_MONOTONIC_TIME_MIN;
diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c
index 36ee2557e8..3d4ac0365f 100644
--- a/erts/emulator/sys/ose/erl_poll.c
+++ b/erts/emulator/sys/ose/erl_poll.c
@@ -506,7 +506,7 @@ int erts_poll_wait(ErtsPollSet ps,
     }
     else {
 	ErtsMonotonicTime current_time, diff_time;
-	current_time = erts_get_monotonic_time();
+	current_time = erts_get_monotonic_time(NULL);
 	diff_time = timeout_time - current_time;
 	if (diff_time <= 0)
 	    goto no_timeout;
diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c
index 937a678702..9196561944 100644
--- a/erts/emulator/sys/win32/erl_poll.c
+++ b/erts/emulator/sys/win32/erl_poll.c
@@ -454,7 +454,7 @@ poll_wait_timeout(ErtsPollSet ps, ErtsMonotonicTime timeout_time)
 	return (DWORD) 0;
     }
 
-    current_time = erts_get_monotonic_time();
+    current_time = erts_get_monotonic_time(NULL);
     diff_time = timeout_time - current_time;
     if (diff_time <= 0)
 	goto no_timeout;
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 3478a80dd4..ce61199567 100644
Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index 9ed45b34bf..bee3477b2e 100644
Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 7361139cde..f3abb5c1c7 100644
Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index fd11c101bc..6aea2c08e4 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -129,10 +129,11 @@
 -export([process_display/2]).
 -export([process_flag/3, process_info/1, processes/0, purge_module/1]).
 -export([put/2, raise/3, read_timer/1, read_timer/2, ref_to_list/1, register/2]).
--export([registered/0, resume_process/1, round/1, self/0, send_after/3]).
+-export([send_after/3, send_after/4, start_timer/3, start_timer/4]).
+-export([registered/0, resume_process/1, round/1, self/0]).
 -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([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]).
@@ -427,23 +428,9 @@ call_on_load_function(_P1) ->
 -spec erlang:cancel_timer(TimerRef) -> Time | false when
       TimerRef :: reference(),
       Time :: non_neg_integer().
-cancel_timer(TimerRef) ->
-    try
-	case erts_internal:access_bif_timer(TimerRef) of
-	    undefined ->
-		false;
-	    {BTR, TSrv} ->
-		Req = erlang:make_ref(),
-		TSrv ! {cancel_timeout, BTR, erlang:self(),
-			true, Req, TimerRef},
-		receive
-		    {cancel_timer, Req, Result} ->
-			Result
-		end
-	end
-    catch
-	_:_ -> erlang:error(badarg, [TimerRef])
-    end.
+
+cancel_timer(_TimerRef) ->
+    erlang:nif_error(undefined).
 
 %% cancel_timer/2
 -spec erlang:cancel_timer(TimerRef, Options) -> Time | false | ok when
@@ -451,53 +438,9 @@ cancel_timer(TimerRef) ->
       Option :: {async, boolean()} | {info, boolean()},
       Options :: [Option],
       Time :: non_neg_integer().
-cancel_timer(TimerRef, Options) ->
-    try
-	{Async, Info} = get_cancel_timer_options(Options, false, true),
-	case erts_internal:access_bif_timer(TimerRef) of
-	    undefined ->
-		case {Async, Info} of
-		    {true, true} ->
-			erlang:self() ! {cancel_timer, TimerRef, false}, ok;
-		    {false, true} ->
-			false;
-		    _ ->
-			ok
-		end;
-	    {BTR, TSrv} ->
-		case Async of
-		    true ->
-			TSrv ! {cancel_timeout, BTR, erlang:self(),
-				Info, TimerRef, TimerRef},
-			ok;
-		    false ->
-			Req = erlang:make_ref(),
-			TSrv ! {cancel_timeout, BTR, erlang:self(),
-				true, Req, TimerRef},
-			receive
-			    {cancel_timer, Req, Result} ->
-				case Info of
-				    true -> Result;
-				    false -> ok
-				end
-			end
-		end
-	end
-    catch
-	_:_ -> erlang:error(badarg, [TimerRef, Options])
-    end.
 
-get_cancel_timer_options([], Async, Info) ->
-    {Async, Info};
-get_cancel_timer_options([{async, Bool} | Opts],
-			 _Async, Info) when Bool == true;
-					    Bool == false ->
-    get_cancel_timer_options(Opts, Bool, Info);
-get_cancel_timer_options([{info, Bool} | Opts],
-			 Async, _Info) when Bool == true;
-					    Bool == false ->
-    get_cancel_timer_options(Opts, Async, Bool).
-    
+cancel_timer(_TimerRef, _Options) ->
+    erlang:nif_error(undefined).
 
 %% check_old_code/1
 -spec check_old_code(Module) -> boolean() when
@@ -1538,8 +1481,8 @@ raise(_Class, _Reason, _Stacktrace) ->
 -spec erlang:read_timer(TimerRef) -> non_neg_integer() | false when
       TimerRef :: reference().
 
-read_timer(TimerRef) ->
-    read_timer(TimerRef, []).
+read_timer(_TimerRef) ->
+    erlang:nif_error(undefined).
 
 %% read_timer/2
 -spec erlang:read_timer(TimerRef, Options) -> non_neg_integer() | false | ok when
@@ -1547,43 +1490,8 @@ read_timer(TimerRef) ->
       Option :: {async, boolean()},
       Options :: [Option].
 
-read_timer(TimerRef, Options) ->
-    try
-	Async = get_read_timer_options(Options, false),
-	case erts_internal:access_bif_timer(TimerRef) of
-	    undefined ->
-		case Async of
-		    true ->
-			erlang:self() ! {read_timer, TimerRef, false},
-			ok;
-		    false ->
-			false
-		end;
-	    {BTR, TSrv} ->
-		case Async of
-		    true ->
-			TSrv ! {read_timeout, BTR, erlang:self(),
-				TimerRef, TimerRef},
-			ok;
-		    false ->
-			Req = erlang:make_ref(),
-			TSrv ! {read_timeout, BTR, erlang:self(),
-				Req, TimerRef},
-			receive
-			    {read_timer, Req, Result} ->
-				Result
-			end
-		end
-	end
-    catch
-	_:_ -> erlang:error(badarg, [TimerRef])
-    end.
-
-get_read_timer_options([], Async) ->
-    Async;
-get_read_timer_options([{async, Bool} | Opts],
-		       _Async) when Bool == true; Bool == false ->
-    get_read_timer_options(Opts, Bool).
+read_timer(_TimerRef, _Options) ->
+    erlang:nif_error(undefined).
 
 %% ref_to_list/1
 -spec erlang:ref_to_list(Ref) -> string() when
@@ -1630,35 +1538,20 @@ self() ->
       Msg :: term(),
       TimerRef :: reference().
 
-send_after(0, Dest, Msg) ->
-    try
-	true = ((erlang:is_pid(Dest)
-		 andalso erlang:node(Dest) == erlang:node())
-		orelse (erlang:is_atom(Dest)
-			andalso Dest /= undefined)),
-	try Dest ! Msg catch _:_ -> ok end,
-	erlang:make_ref()
-    catch
-	_:_ ->
-	    erlang:error(badarg, [0, Dest, Msg])
-    end;
-send_after(Time, Dest, Msg) ->
-    Now = erlang:monotonic_time(),
-    try
-	true = ((erlang:is_pid(Dest)
-		 andalso erlang:node(Dest) == erlang:node())
-		orelse (erlang:is_atom(Dest)
-			andalso Dest /= undefined)),
-	true = Time > 0,
-	true = Time < (1 bsl 32), % Maybe lift this restriction...
-	TO = Now + (erts_internal:time_unit()*Time) div 1000,
-	{BTR, TSrv, TRef} = erts_internal:create_bif_timer(),
-	TSrv ! {set_timeout, BTR, Dest, TO, TRef, Msg},
-	TRef
-    catch
-	_:_ ->
-	    erlang:error(badarg, [Time, Dest, Msg])
-    end.
+send_after(_Time, _Dest, _Msg) ->
+    erlang:nif_error(undefined).
+
+%% send_after/4
+-spec erlang:send_after(Time, Dest, Msg, Options) -> TimerRef when
+      Time :: integer(),
+      Dest :: pid() | atom(),
+      Msg :: term(),
+      Options :: [Option],
+      Option :: {abs, boolean()} | {accessor, pid()},
+      TimerRef :: reference().
+
+send_after(_Time, _Dest, _Msg, _Options) ->
+    erlang:nif_error(undefined).
 
 %% seq_trace/2
 -spec erlang:seq_trace(P1, P2) -> seq_trace_info_returns() | {term(), term(), term(), term(), term()} when
@@ -1731,37 +1624,21 @@ split_binary(_Bin, _Pos) ->
       Dest :: pid() | atom(),
       Msg :: term(),
       TimerRef :: reference().
-start_timer(0, Dest, Msg) ->
-    try
-	true = ((erlang:is_pid(Dest)
-		 andalso erlang:node(Dest) == erlang:node())
-		orelse (erlang:is_atom(Dest)
-			andalso Dest /= undefined)),
-	TimerRef = erlang:make_ref(),
-	_ = try Dest ! {timeout, TimerRef, Msg} catch _:_ -> ok end,
-	TimerRef
-    catch
-	_:_ ->
-	    erlang:error(badarg, [0, Dest, Msg])
-    end;
-start_timer(Time, Dest, Msg) ->
-    Now = erlang:monotonic_time(),
-    try
-	true = ((erlang:is_pid(Dest)
-		 andalso erlang:node(Dest) == erlang:node())
-		orelse (erlang:is_atom(Dest)
-			andalso Dest /= undefined)),
-	true = Time > 0,
-	true = Time < (1 bsl 32), % Maybe lift this restriction...
-	TO = Now + (erts_internal:time_unit()*Time) div 1000,
-	{BTR, TSrv, TimerRef} = erts_internal:create_bif_timer(),
-	TSrv ! {set_timeout, BTR, Dest, TO, TimerRef,
-		{timeout, TimerRef, Msg}},
-	TimerRef
-    catch
-	_:_ ->
-	    erlang:error(badarg, [Time, Dest, Msg])
-    end.
+
+start_timer(_Time, _Dest, _Msg) ->
+    erlang:nif_error(undefined).
+
+%% start_timer/4
+-spec erlang:start_timer(Time, Dest, Msg, Options) -> TimerRef when
+      Time :: integer(),
+      Dest :: pid() | atom(),
+      Msg :: term(),
+      Options :: [Option],
+      Option :: {abs, boolean()} | {accessor, pid()},
+      TimerRef :: reference().
+
+start_timer(_Time, _Dest, _Msg, _Options) ->
+    erlang:nif_error(undefined).
 
 %% suspend_process/2
 -spec erlang:suspend_process(Suspendee, OptList) -> boolean() when
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index e489001532..65a1f1ed3a 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -40,13 +40,9 @@
 
 -export([flush_monitor_messages/3]).
 
--export([time_unit/0]).
-
--export([bif_timer_server/2]).
-
--export([get_bif_timer_servers/0, create_bif_timer/0, access_bif_timer/1]).
+-export([await_result/1]).
 
--export([monitor_process/2]).
+-export([time_unit/0]).
 
 -export([is_system_process/1]).
 
@@ -60,6 +56,16 @@ await_port_send_result(Ref, Busy, Ok) ->
 	{Ref, _} -> Ok
     end.
 
+%%
+%% Await result...
+%%
+
+await_result(Ref) when is_reference(Ref) ->
+    receive
+	{Ref, Result} ->
+	    Result
+    end.
+
 %%
 %% Statically linked port NIFs
 %%
@@ -234,245 +240,8 @@ flush_monitor_messages(Ref, Multi, Res) when is_reference(Ref) ->
 time_unit() ->
     erlang:nif_error(undefined).
 
--spec erts_internal:get_bif_timer_servers() -> Pids when
-      Pid :: pid(),
-      Pids :: [Pid].
-
-get_bif_timer_servers() ->
-    erlang:nif_error(undefined).
-
--spec erts_internal:create_bif_timer() -> Res when
-      Res :: {reference(), pid(), reference()}.
-
-create_bif_timer() ->
-    erlang:nif_error(undefined).
-
--spec erts_internal:access_bif_timer(Ref) -> Res when
-      Ref :: reference(),
-      Res :: {reference(), pid()} | 'undefined'.
-
-access_bif_timer(_Ref) ->
-    erlang:nif_error(undefined).
-
--spec erts_internal:monitor_process(Pid, Ref) -> boolean() when
-      Pid :: pid(),
-      Ref :: reference().
-
-monitor_process(_Pid, _Ref) ->
-    erlang:nif_error(undefined).
-
 -spec erts_internal:is_system_process(Pid) -> boolean() when
       Pid :: pid().
 
 is_system_process(_Pid) ->
     erlang:nif_error(undefined).
-
-%%
-%% BIF timer servers
-%%
-
--record(tsrv_state, {rtab,
-		     ttab,
-		     btr,
-		     unit,
-		     next}).
-
-bif_timer_server(N, BTR) ->
-    try
-	tsrv_loop(tsrv_init_static_state(N, BTR), infinity)
-    catch
-	Type:Reason ->
-	    erlang:display({'BIF_timer_server',
-			    {Type, Reason},
-			    erlang:get_stacktrace()}),
-	    exit(Reason)
-    end.
-
-tsrv_init_static_state(N, BTR) ->
-    process_flag(trap_exit, true),
-    NList = integer_to_list(N),
-    RTabName = list_to_atom("BIF_timer_reference_table_" ++ NList),
-    TTabName = list_to_atom("BIF_timer_time_table_" ++ NList),
-    #tsrv_state{rtab = ets:new(RTabName,
-			       [set, private, {keypos, 2}]),
-		ttab = ets:new(TTabName,
-			       [ordered_set, private, {keypos, 1}]),
-		btr = BTR,
-		unit = erts_internal:time_unit(),
-		next = infinity}.
-    
-
-tsrv_loop(#tsrv_state{unit = Unit} = StaticState, Nxt) ->
-    CallTime = erlang:monotonic_time(),
-    %% 'infinity' is greater than all integers...
-    NewNxt = case CallTime >= Nxt of
-		 true ->
-		     tsrv_handle_timeout(CallTime, StaticState);
-		 false ->
-		     TMO = try
-			       (1000*(Nxt - CallTime - 1)) div Unit + 1
-			   catch
-			       error:badarith when Nxt == infinity -> infinity
-			   end,
-		     receive
-			 Msg ->
-			     tsrv_handle_msg(Msg, StaticState, Nxt)
-		     after TMO ->
-			     Nxt
-		     end
-	     end,
-    tsrv_loop(StaticState, NewNxt).
-
-tsrv_handle_msg({set_timeout, BTR, Proc, Time, TRef, Msg},
-		#tsrv_state{rtab = RTab,
-			    ttab = TTab,
-			    btr = BTR},
-		Nxt) when erlang:is_integer(Time) ->
-    RcvTime = erlang:monotonic_time(),
-    case Time =< RcvTime of
-	true ->
-	    try Proc ! Msg catch _:_ -> ok end,
-	    Nxt;
-	false ->
-	    Ins = case erlang:is_atom(Proc) of
-		      true ->
-			  true;
-		      false ->
-			  try
-			      erts_internal:monitor_process(Proc, TRef)
-			  catch
-			      _:_ -> false
-			  end
-		  end,
-	    case Ins of
-		false ->
-		    Nxt;
-		true ->
-		    TKey = {Time, TRef},
-		    true = ets:insert(RTab, TKey),
-		    true = ets:insert(TTab, {TKey, Proc, Msg}),
-		    case Time < Nxt of
-			true -> Time;
-			false -> Nxt
-		    end
-	    end
-    end;
-tsrv_handle_msg({cancel_timeout, BTR, From, Reply, Req, TRef},
-		#tsrv_state{rtab = RTab,
-			    ttab = TTab,
-			    unit = Unit,
-			    btr = BTR},
-		Nxt) ->
-    case ets:lookup(RTab, TRef) of
-	[] ->
-	    case Reply of
-		false ->
-		    ok;
-		_ ->
-		    _ = try From ! {cancel_timer, Req, false} catch _:_ -> ok end
-	    end,
-	    Nxt;
-	[{Time, TRef} = TKey] ->
-	    ets:delete(RTab, TRef),
-	    ets:delete(TTab, TKey),
-	    erlang:demonitor(TRef),
-	    case Reply of
-		false ->
-		    ok;
-		_ ->
-		    RcvTime = erlang:monotonic_time(),
-		    RT = case Time =< RcvTime of
-			     true ->
-				 0;
-			     false ->
-				 ((1000*(Time - RcvTime)) div Unit)
-			 end,
-		    _ = try From ! {cancel_timer, Req, RT} catch _:_ -> ok end
-	    end,
-	    case Time =:= Nxt of
-		false ->
-		    Nxt;
-		true ->
-		    case ets:first(TTab) of
-			'$end_of_table' -> infinity;
-			{NextTime, _TRef} -> NextTime
-		    end
-	    end
-    end;
-tsrv_handle_msg({read_timeout, BTR, From, Req, TRef},
-		#tsrv_state{rtab = RTab,
-			    unit = Unit,
-			    btr = BTR},
-		Nxt) ->
-    case ets:lookup(RTab, TRef) of
-	[] ->
-	    _ = try From ! {read_timer, Req, false} catch _:_ -> ok end;
-	[{Time, TRef}] ->
-	    RcvTime = erlang:monotonic_time(),
-	    RT = case Time =< RcvTime of
-		     true -> 0;
-		     false -> (1000*(Time - RcvTime)) div Unit
-		 end,
-	    _ = try From ! {read_timer, Req, RT} catch _:_ -> ok end
-    end,
-    Nxt;
-tsrv_handle_msg({'DOWN', TRef, process, _, _},
-		#tsrv_state{rtab = RTab,
-			    ttab = TTab},
-		Nxt) ->
-    case ets:lookup(RTab, TRef) of
-	[] ->
-	    Nxt;
-	[{Time, TRef} = TKey] ->
-	    ets:delete(RTab, TRef),
-	    ets:delete(TTab, TKey),
-	    case Time =:= Nxt of
-		false ->
-		    Nxt;
-		true ->
-		    case ets:first(TTab) of
-			'$end_of_table' -> infinity;
-			{NextTime, _} -> NextTime
-		    end
-	    end
-    end;
-tsrv_handle_msg({cancel_all_timeouts, BTR, From, Ref},
-		#tsrv_state{rtab = RTab,
-			    ttab = TTab,
-			    btr = BTR},
-		_Nxt) ->
-    tsrv_delete_monitor_objects(RTab),
-    ets:delete_all_objects(TTab),
-    try From ! {canceled_all_timeouts, Ref} catch _:_ -> ok end,
-    infinity;
-tsrv_handle_msg(_GarbageMsg, _StaticState, Nxt) ->
-    Nxt.
-
-tsrv_delete_monitor_objects(RTab) ->
-    case ets:first(RTab) of
-	'$end_of_table' ->
-	    ok;
-	TRef ->
-	    erlang:demonitor(TRef),
-	    ets:delete(RTab, TRef),
-	    tsrv_delete_monitor_objects(RTab)
-    end.
-
-tsrv_handle_timeout(CallTime, #tsrv_state{rtab = RTab,
-					  ttab = TTab} = S) ->
-    case ets:first(TTab) of
-	'$end_of_table' ->
-	    infinity;
-	{Time, _TRef} when Time > CallTime ->
-	    Time;
-	{_Time, TRef} = TKey ->
-	    [{TKey, Proc, Msg}] = ets:lookup(TTab, TKey),
-	    case erlang:is_pid(Proc) of
-		false -> ok;
-		_ -> erlang:demonitor(TRef)
-	    end,
-	    ets:delete(TTab, TKey),
-	    ets:delete(RTab, TRef),
-	    _ = try Proc ! Msg catch _:_ -> ok end,
-	    tsrv_handle_timeout(CallTime, S)
-    end.
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 48c5c37717..bb56c9ff73 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -522,7 +522,6 @@ shutdown_pids(Heart,BootPid,State) ->
     Timer = shutdown_timer(State#state.flags),
     catch shutdown(State#state.kernel,BootPid,Timer,State),
     kill_all_pids(Heart), % Even the shutdown timer.
-    cancel_all_bif_timeouts(),
     kill_all_ports(Heart),
     flush_timout(Timer).
 
@@ -581,30 +580,6 @@ resend([ExitMsg|Exits]) ->
 resend(_) ->
     ok.
 
-
-cancel_all_bif_timeouts() ->
-    TSrvs = erts_internal:get_bif_timer_servers(),
-    Ref = make_ref(),
-    {BTR, _TSrv} = erts_internal:access_bif_timer(Ref), %% Cheat...
-    request_cancel_all_bif_timeouts(Ref, BTR, TSrvs),
-    wait_response_cancel_all_bif_timeouts(Ref, BTR, TSrvs),
-    ok.
-
-request_cancel_all_bif_timeouts(_Ref, _BTR, []) ->
-    ok;
-request_cancel_all_bif_timeouts(Ref, BTR, [TSrv|TSrvs]) ->
-    TSrv ! {cancel_all_timeouts, BTR, self(), {Ref, TSrv}},
-    request_cancel_all_bif_timeouts(Ref, BTR, TSrvs).
-
-wait_response_cancel_all_bif_timeouts(_Ref, _BTR, []) ->
-    ok;
-wait_response_cancel_all_bif_timeouts(Ref, BTR, [TSrv|TSrvs]) ->
-    receive
-	{canceled_all_timeouts, {Ref, TSrv}} ->
-	    wait_response_cancel_all_bif_timeouts(Ref, BTR, TSrvs)
-    end.
-
-
 %%
 %% Kill all existing pids in the system (except init and heart).
 kill_all_pids(Heart) ->
-- 
cgit v1.2.3


From b09e84bb0b44ee9bce74e00b8741eeda705716b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Fri, 8 May 2015 09:28:32 +0200
Subject: erts: Simple test of erts_debug:size/1 of Maps

---
 erts/emulator/test/erts_debug_SUITE.erl | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl
index e5c904cfb9..bc5928436f 100644
--- a/erts/emulator/test/erts_debug_SUITE.erl
+++ b/erts/emulator/test/erts_debug_SUITE.erl
@@ -71,6 +71,11 @@ test_size(Config) when is_list(Config) ->
     4 = do_test_size(#{}),
     32 = do_test_size(#{b => 2,c => 3,txt => "hello world"}),
 
+    true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,256)])) >= map_size_lower_bound(256),
+    true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,4096)])) >= map_size_lower_bound(4096),
+    true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,254)])) >= map_size_lower_bound(254),
+    true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,239)])) >= map_size_lower_bound(239),
+
     %% Test internal consistency of sizes, but without testing
     %% exact sizes.
     Const = id(42),
@@ -92,14 +97,14 @@ test_size(Config) when is_list(Config) ->
 
     %% Test shared data structures.
     do_test_size([ConsCell1|ConsCell1],
-		 3*ConsCellSz,
-		 2*ConsCellSz),
+        	 3*ConsCellSz,
+        	 2*ConsCellSz),
     do_test_size(fun() -> {ConsCell1,ConsCell2} end,
-		 FunSz2 + 2*ConsCellSz,
-		 FunSz2 + ConsCellSz),
+        	 FunSz2 + 2*ConsCellSz,
+        	 FunSz2 + ConsCellSz),
     do_test_size({SimplestFun,SimplestFun},
-		 2*FunSz0+do_test_size({a,b}),
-		 FunSz0+do_test_size({a,b})),
+        	 2*FunSz0+do_test_size({a,b}),
+        	 FunSz0+do_test_size({a,b})),
 
     M = id(#{ "atom" => first, i => 0}),
     do_test_size([M,M#{ "atom" := other },M#{i := 42}],54,32),
@@ -113,6 +118,13 @@ do_test_size(Term, FlatSz, Sz) ->
     FlatSz = erts_debug:flat_size(Term),
     Sz = erts_debug:size(Term).
 
+map_size_lower_bound(N) ->
+    %% this est. is a bit lower that actual lower bound
+    %% number of internal nodes
+    T = (N - 1) div 15,
+    %% total words
+    2 + 17 * T + 2 * N.
+
 flat_size_big(Config) when is_list(Config) ->
     %% Build a term whose external size only fits in a big num (on 32-bit CPU).
     flat_size_big_1(16#11111111111111117777777777777777888889999, 0, 16#FFFFFFF).
-- 
cgit v1.2.3


From e7335bb1197294ef6f5fb3ad73bb22b9343603be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Fri, 8 May 2015 09:28:46 +0200
Subject: erts: Fix erts_debug:size/1 for large Maps

---
 erts/emulator/beam/erl_map.c | 28 ++++++++++++----------------
 1 file changed, 12 insertions(+), 16 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index bb2a2bcdf9..256d6a8e81 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -2586,12 +2586,12 @@ BIF_RETTYPE erts_internal_map_type_1(BIF_ALIST_1) {
     DECL_AM(hashmap);
     DECL_AM(hashmap_node);
     DECL_AM(flatmap);
-    if (is_flatmap(BIF_ARG_1)) {
-	BIF_RET(AM_flatmap);
-    } else if (is_hashmap(BIF_ARG_1)) {
+    if (is_map(BIF_ARG_1)) {
         Eterm hdr = *(boxed_val(BIF_ARG_1));
         ASSERT(is_header(hdr));
         switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
+            case HAMT_SUBTAG_HEAD_FLATMAP:
+                BIF_RET(AM_flatmap);
             case HAMT_SUBTAG_HEAD_ARRAY:
             case HAMT_SUBTAG_HEAD_BITMAP:
                 BIF_RET(AM_hashmap);
@@ -2612,23 +2612,22 @@ BIF_RETTYPE erts_internal_map_type_1(BIF_ALIST_1) {
  */
 
 BIF_RETTYPE erts_internal_map_hashmap_children_1(BIF_ALIST_1) {
-    if (is_hashmap(BIF_ARG_1)) {
+    if (is_map(BIF_ARG_1)) {
         Eterm node = BIF_ARG_1;
         Eterm *ptr, hdr, *hp, res = NIL;
         Uint  sz = 0;
         ptr = boxed_val(node);
         hdr = *ptr;
-
         ASSERT(is_header(hdr));
 
         switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
-            case HAMT_SUBTAG_NODE_BITMAP:
-                sz   = hashmap_bitcount(MAP_HEADER_VAL(hdr));
-                ptr += 1;
-                break;
+            case HAMT_SUBTAG_HEAD_FLATMAP:
+                BIF_ERROR(BIF_P, BADARG);
             case HAMT_SUBTAG_HEAD_BITMAP:
-                sz   = hashmap_bitcount(MAP_HEADER_VAL(hdr));
-                ptr += 2;
+                ptr++;
+            case HAMT_SUBTAG_NODE_BITMAP:
+                ptr++;
+                sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
                 break;
             case HAMT_SUBTAG_HEAD_ARRAY:
                 sz   = 16;
@@ -2642,12 +2641,9 @@ BIF_RETTYPE erts_internal_map_hashmap_children_1(BIF_ALIST_1) {
         hp = HAlloc(BIF_P, 2*sz);
         while(sz--) { res = CONS(hp, *ptr++, res); hp += 2; }
         BIF_RET(res);
-    } else if (is_flatmap(BIF_ARG_1)) {
-	BIF_ERROR(BIF_P, BADARG);
-    } else {
-	BIF_P->fvalue = BIF_ARG_1;
-	BIF_ERROR(BIF_P, BADMAP);
     }
+    BIF_P->fvalue = BIF_ARG_1;
+    BIF_ERROR(BIF_P, BADMAP);
 }
 
 
-- 
cgit v1.2.3


From 2dc1ca3bc664177a7d985c3e190464285e9b75fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Valim?= 
Date: Fri, 9 May 2014 17:08:43 +0200
Subject: Add erts_send_error_term_to_logger

This function allows us to send format and args to the
logger which can then be formatted and customized from
Erlang land.
---
 erts/emulator/beam/sys.h   |   1 +
 erts/emulator/beam/utils.c | 190 +++++++++++++++++++++++++++++++--------------
 2 files changed, 132 insertions(+), 59 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 54059ee9a5..cd53069872 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -657,6 +657,7 @@ erts_dsprintf_buf_t *erts_create_logger_dsbuf(void);
 int erts_send_info_to_logger(Eterm, erts_dsprintf_buf_t *);
 int erts_send_warning_to_logger(Eterm, erts_dsprintf_buf_t *);
 int erts_send_error_to_logger(Eterm, erts_dsprintf_buf_t *);
+int erts_send_error_term_to_logger(Eterm, erts_dsprintf_buf_t *, Eterm);
 int erts_send_info_to_logger_str(Eterm, char *); 
 int erts_send_warning_to_logger_str(Eterm, char *);
 int erts_send_error_to_logger_str(Eterm, char *);
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index e21e916de5..e3034c1e24 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -2222,97 +2222,157 @@ tail_recur:
 #undef MAKE_HASH_CDR_POST_OP
 }
 
-static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len)
+static Eterm
+do_allocate_logger_message(Eterm gleader, Eterm **hp, ErlOffHeap **ohp,
+			   ErlHeapFragment **bp, Process **p, Uint sz)
 {
-    /* error_logger ! 
-       {notify,{info_msg,gleader,{emulator,"~s~n",[]}}} |
-       {notify,{error,gleader,{emulator,"~s~n",[]}}} |
-       {notify,{warning_msg,gleader,{emulator,"~s~n",[}]}} */
-    Eterm* hp;
-    Uint sz;
     Uint gl_sz;
-    Eterm gl;
-    Eterm list,plist,format,tuple1,tuple2,tuple3;
-    ErlOffHeap *ohp;
-    ErlHeapFragment *bp = NULL;
-#if !defined(ERTS_SMP)
-    Process *p;
-#endif
-
-    ASSERT(is_atom(tag));
-
-    if (len <= 0) {
-	return -1;
-    }
+    gl_sz = IS_CONST(gleader) ? 0 : size_object(gleader);
+    sz = sz + gl_sz;
 
 #ifndef ERTS_SMP
 #ifdef USE_THREADS
-    p = NULL;
     if (erts_get_scheduler_data()) /* Must be scheduler thread */
 #endif
     {
-	p = erts_whereis_process(NULL, 0, am_error_logger, 0, 0);
-	if (p) {
-	    erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
+	*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|ERTS_PSFLG_RUNNING_SYS))
-		p = NULL;
+		*p = NULL;
 	}
     }
 
-    if (!p) {
-	/* buf *always* points to a null terminated string */
-	erts_fprintf(stderr, "(no error logger present) %T: \"%s\"\n",
-		     tag, buf);
-	return 0;
+    if (!*p) {
+	return NIL;
     }
-    /* So we have an error logger, lets build the message */
-#endif
-    gl_sz = IS_CONST(gleader) ? 0 : size_object(gleader);
-    sz = len * 2 /* message list */+ 2 /* cons surrounding message list */
-	+ gl_sz + 
-	3 /*outer 2-tuple*/ + 4 /* middle 3-tuple */ + 4 /*inner 3-tuple */ +
-	8 /* "~s~n" */;
 
-#ifndef ERTS_SMP
-    if (sz <= HeapWordsLeft(p)) {
-	ohp = &MSO(p);
-	hp = HEAP_TOP(p);
-	HEAP_TOP(p) += sz;
+    /* So we have an error logger, lets build the message */
+    if (sz <= HeapWordsLeft(*p)) {
+	*ohp = &MSO(*p);
+	*hp = HEAP_TOP(*p);
+	HEAP_TOP(*p) += sz;
     } else {
 #endif
-	bp = new_message_buffer(sz);
-	ohp = &bp->off_heap;
-	hp = bp->mem;
+	*bp = new_message_buffer(sz);
+	*ohp = &(*bp)->off_heap;
+	*hp = (*bp)->mem;
 #ifndef ERTS_SMP
     }
 #endif
-    gl = (is_nil(gleader)
+
+    return (is_nil(gleader)
 	  ? am_noproc
 	  : (IS_CONST(gleader)
 	     ? gleader
-	     : copy_struct(gleader,gl_sz,&hp,ohp)));
-    list = buf_to_intlist(&hp, buf, len, NIL);
-    plist = CONS(hp,list,NIL);
-    hp += 2;
-    format = buf_to_intlist(&hp, "~s~n", 4, NIL);
-    tuple1 = TUPLE3(hp, am_emulator, format, plist);
-    hp += 4;
-    tuple2 = TUPLE3(hp, tag, gl, tuple1);
-    hp += 4;
-    tuple3 = TUPLE2(hp, am_notify, tuple2);
+	     : copy_struct(gleader,gl_sz,hp,*ohp)));
+}
+
+static void do_send_logger_message(Eterm *hp, ErlOffHeap *ohp, ErlHeapFragment *bp,
+				   Process *p, Eterm message)
+{
 #ifdef HARDDEBUG
-    erts_fprintf(stderr, "%T\n", tuple3);
+    erts_fprintf(stderr, "%T\n", message);
 #endif
 #ifdef ERTS_SMP
     {
 	Eterm from = erts_get_current_pid();
 	if (is_not_internal_pid(from))
 	    from = NIL;
-	erts_queue_error_logger_message(from, tuple3, bp);
+	erts_queue_error_logger_message(from, message, bp);
     }
 #else
-    erts_queue_message(p, NULL /* only used for smp build */, bp, tuple3, NIL);
+    erts_queue_message(p, NULL /* only used for smp build */, bp, message, NIL);
 #endif
+}
+
+/* error_logger !
+   {notify,{info_msg,gleader,{emulator,format,[args]}}} |
+   {notify,{error,gleader,{emulator,format,[args]}}} |
+   {notify,{warning_msg,gleader,{emulator,format,[args}]}} */
+static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len)
+{
+    Uint sz;
+    Eterm gl;
+    Eterm list,args,format,tuple1,tuple2,tuple3;
+
+    Eterm *hp = NULL;
+    ErlOffHeap *ohp = NULL;
+    ErlHeapFragment *bp = NULL;
+    Process *p = NULL;
+
+    ASSERT(is_atom(tag));
+
+    if (len <= 0) {
+	return -1;
+    }
+
+    sz = len * 2 /* message list */ + 2 /* cons surrounding message list */
+	+ 3 /*outer 2-tuple*/ + 4 /* middle 3-tuple */ + 4 /*inner 3-tuple */
+	+ 8 /* "~s~n" */;
+
+    /* gleader size is accounted and allocated next */
+    gl = do_allocate_logger_message(gleader, &hp, &ohp, &bp, &p, sz);
+
+    if(is_nil(gl)) {
+       /* buf *always* points to a null terminated string */
+       erts_fprintf(stderr, "(no error logger present) %T: \"%s\"\n",
+                    tag, buf);
+       return 0;
+    }
+
+    list = buf_to_intlist(&hp, buf, len, NIL);
+    args = CONS(hp,list,NIL);
+    hp += 2;
+    format = buf_to_intlist(&hp, "~s~n", 4, NIL);
+    tuple1 = TUPLE3(hp, am_emulator, format, args);
+    hp += 4;
+    tuple2 = TUPLE3(hp, tag, gl, tuple1);
+    hp += 4;
+    tuple3 = TUPLE2(hp, am_notify, tuple2);
+
+    do_send_logger_message(hp, ohp, bp, p, tuple3);
+    return 0;
+}
+
+static int do_send_term_to_logger(Eterm tag, Eterm gleader,
+				  char *buf, int len, Eterm args)
+{
+    Uint sz;
+    Eterm gl;
+    Uint args_sz;
+    Eterm format,tuple1,tuple2,tuple3;
+
+    Eterm *hp = NULL;
+    ErlOffHeap *ohp = NULL;
+    ErlHeapFragment *bp = NULL;
+    Process *p = NULL;
+
+    ASSERT(is_atom(tag));
+
+    args_sz = size_object(args);
+    sz = len * 2 /* format */ + args_sz
+	+ 3 /*outer 2-tuple*/ + 4 /* middle 3-tuple */ + 4 /*inner 3-tuple */;
+
+    /* gleader size is accounted and allocated next */
+    gl = do_allocate_logger_message(gleader, &hp, &ohp, &bp, &p, sz);
+
+    if(is_nil(gl)) {
+       /* buf *always* points to a null terminated string */
+       erts_fprintf(stderr, "(no error logger present) %T: \"%s\" %T\n",
+                    tag, buf, args);
+       return 0;
+    }
+
+    format = buf_to_intlist(&hp, buf, len, NIL);
+    args = copy_struct(args, args_sz, &hp, ohp);
+    tuple1 = TUPLE3(hp, am_emulator, format, args);
+    hp += 4;
+    tuple2 = TUPLE3(hp, tag, gl, tuple1);
+    hp += 4;
+    tuple3 = TUPLE2(hp, am_notify, tuple2);
+
+    do_send_logger_message(hp, ohp, bp, p, tuple3);
     return 0;
 }
 
@@ -2340,6 +2400,12 @@ send_error_to_logger(Eterm gleader, char *buf, int len)
     return do_send_to_logger(am_error, gleader, buf, len);
 }
 
+static ERTS_INLINE int
+send_error_term_to_logger(Eterm gleader, char *buf, int len, Eterm args)
+{
+    return do_send_term_to_logger(am_error, gleader, buf, len, args);
+}
+
 #define LOGGER_DSBUF_INC_SZ 256
 
 static erts_dsprintf_buf_t *
@@ -2414,6 +2480,12 @@ erts_send_error_to_logger(Eterm gleader, erts_dsprintf_buf_t *dsbufp)
     return res;
 }
 
+int
+erts_send_error_term_to_logger(Eterm gleader, erts_dsprintf_buf_t *dsbufp, Eterm args)
+{
+    return send_error_term_to_logger(gleader, dsbufp->str, dsbufp->str_len, args);
+}
+
 int
 erts_send_info_to_logger_str(Eterm gleader, char *str)
 {
-- 
cgit v1.2.3


From 42d25222fa7395ad9fb58de2c964afa4caefb5b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Valim?= 
Date: Fri, 9 May 2014 18:07:07 +0200
Subject: Send format and args on process exit to error_logger

Previously, the emulator would generate a whole string
with values and call the error_logger passing "~s~n".
This commit changes it to a format string containing ~p
with the respective values as arguments.
---
 erts/emulator/beam/beam_emu.c | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 9fe02c3724..a21622f424 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -5577,18 +5577,35 @@ next_catch(Process* c_p, Eterm *reg) {
 static void
 terminate_proc(Process* c_p, Eterm Value)
 {
+    Eterm *hp;
+    Eterm Args = NIL;
+
     /* Add a stacktrace if this is an error. */
     if (GET_EXC_CLASS(c_p->freason) == EXTAG_ERROR) {
         Value = add_stacktrace(c_p, Value, c_p->ftrace);
     }
     /* EXF_LOG is a primary exception flag */
     if (c_p->freason & EXF_LOG) {
+	int alive = erts_is_alive;
 	erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
-	erts_dsprintf(dsbufp, "Error in process %T ", c_p->common.id);
-	if (erts_is_alive)
-	    erts_dsprintf(dsbufp, "on node %T ", erts_this_node->sysname);
-	erts_dsprintf(dsbufp,"with exit value: %0.*T\n", display_items, Value);
-	erts_send_error_to_logger(c_p->group_leader, dsbufp);
+
+        /* Build the format message */
+	erts_dsprintf(dsbufp, "Error in process ~p ");
+	if (alive)
+	    erts_dsprintf(dsbufp, "on node ~p ");
+	erts_dsprintf(dsbufp, "with exit value:~n~p~n");
+
+        /* Build the args in reverse order */
+	hp = HAlloc(c_p, 2);
+	Args = CONS(hp, Value, Args);
+	if (alive) {
+	    hp = HAlloc(c_p, 2);
+	    Args = CONS(hp, erts_this_node->sysname, Args);
+	}
+	hp = HAlloc(c_p, 2);
+	Args = CONS(hp, c_p->common.id, Args);
+
+	erts_send_error_term_to_logger(c_p->group_leader, dsbufp, Args);
     }
     /*
      * If we use a shared heap, the process will be garbage-collected.
-- 
cgit v1.2.3


From 5917fd5468a952fce7ebda8b57a6218fbb7bb878 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 
Date: Tue, 12 May 2015 11:55:34 +0200
Subject: erts: Fix erts_send_error_term_to_logger memory leak

---
 erts/emulator/beam/utils.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index e3034c1e24..965de748c9 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -2483,7 +2483,10 @@ erts_send_error_to_logger(Eterm gleader, erts_dsprintf_buf_t *dsbufp)
 int
 erts_send_error_term_to_logger(Eterm gleader, erts_dsprintf_buf_t *dsbufp, Eterm args)
 {
-    return send_error_term_to_logger(gleader, dsbufp->str, dsbufp->str_len, args);
+    int res;
+    res = send_error_term_to_logger(gleader, dsbufp->str, dsbufp->str_len, args);
+    destroy_logger_dsbuf(dsbufp);
+    return res;
 }
 
 int
-- 
cgit v1.2.3


From 849a2ed2db2bd54422ec9284468f80cc86978974 Mon Sep 17 00:00:00 2001
From: Rickard Green 
Date: Mon, 11 May 2015 17:56:03 +0200
Subject: Timer fixes, documentation, and test cases

---
 erts/doc/src/erlang.xml                  | 249 ++++++++++++++++++----
 erts/emulator/beam/erl_alloc.c           |  16 ++
 erts/emulator/beam/erl_hl_timer.c        |  66 +++---
 erts/emulator/beam/time.c                |  60 +++---
 erts/emulator/test/after_SUITE.erl       |  29 ++-
 erts/emulator/test/system_info_SUITE.erl |  31 +++
 erts/emulator/test/timer_bif_SUITE.erl   | 346 +++++++++++++++++++++++++------
 erts/preloaded/ebin/erlang.beam          | Bin 101588 -> 101812 bytes
 erts/preloaded/src/erlang.erl            |  41 ++--
 9 files changed, 657 insertions(+), 181 deletions(-)

(limited to 'erts')

diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index ba5f80a9c1..6ca57566aa 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -536,29 +536,69 @@
       
     
     
-      
+      
       Cancel a timer
       
-        

Cancels a timer, where TimerRef was returned by - either - erlang:send_after/3 - or - erlang:start_timer/3. - If the timer is there to be removed, the function returns - the time in milliseconds left until the timer would have expired, - otherwise false (which means that TimerRef was - never a timer, that it has already been cancelled, or that it - has already delivered its message).

+

Cancels a timer. TimerRef needs to refer to + a timer that was created by either + erlang:send_after(), + or erlang:start_timer().

+

Currently available Options:

+ + {async, Async} + +

Asynchronous request for cancellation. Async + defaults to false. That is the operation will be + performed synchronously. When Async is set to + true the cancel operation will be performed + asynchronously. That is, cancel_timer() will send + a request for cancellation to the timer service that + manages the timer, and then return ok.

+ {info, Info} + +

Request information about the Result of the + cancellation. Info defaults to true. That + is information will be given. When Info is set to + false no information about the result of the cancel + operation will be given. When the operation is performed + synchronously the Result will returned from + cancel_timer(). When the operation is performed + asynchronously, a message on the form + {cancel_timer, TimerRef, Result} + will be sent to the caller of cancel_timer() when + the operation has been performed.

+
+

When the Result equals false a timer + corresponding to TimerRef could not be found. This + can be either because the timer had expired, been canceled, or because + TimerRef do not correspond to a timer. When the + Result is an integer, it represents + the time in milli seconds left before the timer will expire.

+

The timer service that manages the timer may be co-located + with another scheduler than the scheduler that the calling process + is executing on. In this case communication with the timer + service will be performed using asynchronous signals. If the calling + process is in critical path and can do other things while waiting + for the result of this operation, you want to use the {async, true} + option.

See also - erlang:send_after/3, - erlang:start_timer/3, + erlang:send_after/4, + erlang:start_timer/4, and - erlang:read_timer/1.

+ erlang:read_timer/2.

Note: Cancelling a timer does not guarantee that the message has not already been delivered to the message queue.

- + + + Cancel a timer + +

Cancels a timer. The same as calling + erlang:cancel_timer(TimerRef, + [{async, false}, {info, true}]).

+
+
Check if a module has old code @@ -4505,23 +4545,54 @@ os_prompt%
- - Number of milliseconds remaining for a timer - -

TimerRef is a timer reference returned by - erlang:send_after/3 - or - erlang:start_timer/3. - If the timer is active, the function returns the time in - milliseconds left until the timer will expire, otherwise - false (which means that TimerRef was never a - timer, that it has been cancelled, or that it has already - delivered its message).

+ + Read the state of a timer + +

Read the state of a timer. TimerRef + needs to refer to a timer that was created by either + erlang:send_after(), + or erlang:start_timer().

+

Currently available Options:

+ + {async, Async} + +

Asynchronous request. Async defaults to false. That + is the operation will be performed synchronously, and the Result + will returned from read_timer(). When Async is set to + true, read_timer() will send a request for the + Result to a timer service that manages the timer and then + return ok. A message on the format + {read_timer, TimerRef, Result} + will be sent to the caller of read_timer() when + the operation has been processed.

+
+

When the Result equals false a timer + corresponding to TimerRef could not be found. This + can be either because the timer had expired, been canceled, or because + TimerRef do not correspond to a timer. When the + Result is an integer, it represents + the time in milli seconds left before the timer will expire.

+

The timer service that manages the timer may be co-located + with another scheduler than the scheduler that the calling process + is executing on. In this case communication with the timer + service will be performed using asynchronous signals. If the calling + process is in critical path and can do other things while waiting + for the result of this operation, you want to use the {async, true} + option.

See also - erlang:send_after/3, - erlang:start_timer/3, + erlang:send_after/4, + erlang:start_timer/4, and - erlang:cancel_timer/1.

+ erlang:cancel_timer/2.

+
+
+ + + Read the state of a timer + +

Read the state of a timer. The same as calling + erlang:read_timer(TimerRef, + [{async, false}]).

@@ -4669,6 +4740,63 @@ true + + + Start a timer + +

Starts a timer. When the timer expires, the message + Msg will be sent to + Dest.

+

If Dest is a pid() it has to + be a pid() of a local process, dead or alive.

+

Currently available Options:

+ + {abs, Abs} + +

Absolute timeout. When Abs is false + the Time value will be interpreted + as a time in milli-seconds relative current + Erlang + monotonic time. When Abs is true the + Time value will be interpreted as an absolute + Erlang monotonic time of milli second time unit. Abs + defaults to false.

+
+
+

The absolute time when the timer is set to expire needs + to be in the range between + erlang:system_info(start_time) + and + erlang:system_info(end_time). + If a negative relative time is specified the time is not + allowed to be negative.

+

If Dest is an atom(), 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.

+

If Dest is a pid(), the timer will be automatically + canceled if the process referred to by the pid() 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 Dest is an atom().

+

See also + erlang:send_timer/4, + erlang:cancel_timer/2, + and + erlang:read_timer/2.

+

Failure: badarg if the arguments does not satisfy + the requirements specified above.

+
+
+ + + Start a timer + +

Starts a timer. The same as calling + erlang:send_after(Time, + Dest, Msg, [{abs, false}]).

+
+
0 <= Time <= 4294967295 @@ -4690,9 +4818,9 @@ true automatically canceled when Dest is an atom.

See also erlang:start_timer/3, - erlang:cancel_timer/1, + erlang:cancel_timer/2, and - erlang:read_timer/1.

+ erlang:read_timer/2.

Failure: badarg if the arguments does not satisfy the requirements specified above.

@@ -5100,15 +5228,35 @@ true
- - 0 <= Time <= 4294967295 + Start a timer -

Starts a timer which will send the message - {timeout, TimerRef, Msg} to Dest - after Time milliseconds.

-

If Dest is a pid() it has to be a pid() of a local process, dead or alive.

-

The Time value can, in the current implementation, not be greater than 4294967295.

+

Starts a timer. When the timer expires, the message + {timeout, TimerRef, Msg} + will be sent to Dest.

+

If Dest is a pid() it has to + be a pid() of a local process, dead or alive.

+

Currently available Options:

+ + {abs, Abs} + +

Absolute timeout. When Abs is false + the Time value will be interpreted + as a time in milli-seconds relative current + Erlang + monotonic time. When Abs is true the + Time value will be interpreted as an absolute + Erlang monotonic time of milli second time unit. Abs + defaults to false.

+
+
+

The absolute time when the timer is set to expire needs + to be in the range between + erlang:system_info(start_time) + and + erlang:system_info(end_time). + If a negative relative time is specified the time is not + allowed to be negative.

If Dest is an atom(), 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 @@ -5119,14 +5267,23 @@ true erts version 5.4.11. Note that timers will not be automatically canceled when Dest is an atom().

See also - erlang:send_after/3, - erlang:cancel_timer/1, + erlang:send_after/4, + erlang:cancel_timer/2, and - erlang:read_timer/1.

+ erlang:read_timer/2.

Failure: badarg if the arguments does not satisfy the requirements specified above.

+ + + Start a timer + +

Starts a timer. The same as calling + erlang:start_timer(Time, + Dest, Msg, [{abs, false}]).

+
+
Information about context switches @@ -6236,6 +6393,14 @@ ok (i.e. system_info(dynamic_trace) returns dtrace or systemtap).

+ end_time +

The last Erlang monotonic + time in native + time unit that + can be represented internally in the current Erlang runtime system + instance. The time between the + start time and + the end time is at least a quarter of a millennium.

elib_malloc

This option will be removed in a future release. diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 71902c2f9f..7d099997d8 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -2331,6 +2331,22 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) &size.processes_used, fi, ERTS_ALC_T_MSG_REF); + add_fix_values(&size.processes, + &size.processes_used, + fi, + ERTS_ALC_T_LL_PTIMER); + add_fix_values(&size.processes, + &size.processes_used, + fi, + ERTS_ALC_T_HL_PTIMER); + add_fix_values(&size.processes, + &size.processes_used, + fi, + ERTS_ALC_T_BIF_TIMER); + add_fix_values(&size.processes, + &size.processes_used, + fi, + ERTS_ALC_T_ABIF_TIMER); } if (want.atom || want.atom_used) { diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 2245799819..f1806d25be 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -850,8 +850,6 @@ hl_timer_destroy(ErtsHLTimer *tmr) if (!(roflgs & ERTS_TMR_ROFLG_BIF_TMR)) erts_free(ERTS_ALC_T_HL_PTIMER, tmr); else { - if (tmr->btm.bp) - free_message_buffer(tmr->btm.bp); if (roflgs & ERTS_TMR_ROFLG_PRE_ALC) bif_timer_pre_free(tmr); else if (roflgs & ERTS_TMR_ROFLG_ABIF_TMR) @@ -898,10 +896,6 @@ schedule_hl_timer_destroy(ErtsHLTimer *tmr, Uint32 roflgs) * Message buffer can be dropped at * once... */ - if (tmr->btm.bp) { - free_message_buffer(tmr->btm.bp); - tmr->btm.bp = NULL; - } size = sizeof(ErtsHLTimer); } @@ -1121,6 +1115,7 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs) { ErtsProcLocks proc_locks = ERTS_PROC_LOCKS_MSG_SEND; Process *proc; + int queued_message = 0; int dec_refc = 0; Uint32 is_reg_name = (roflgs & ERTS_TMR_ROFLG_REG_NAME); ERTS_HLT_ASSERT(roflgs & ERTS_TMR_ROFLG_BIF_TMR); @@ -1159,6 +1154,7 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs) #endif ); erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_SEND); + queued_message = 1; proc_locks &= ~ERTS_PROC_LOCKS_MSG_SEND; tmr->btm.bp = NULL; if (tmr->btm.proc_tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) { @@ -1172,6 +1168,8 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs) if (dec_refc) hl_timer_pre_dec_refc(tmr); } + if (!queued_message && tmr->btm.bp) + free_message_buffer(tmr->btm.bp); } static ERTS_INLINE void @@ -1689,6 +1687,8 @@ setup_bif_timer(Process *c_p, ErtsMonotonicTime timeout_pos, rcvr, ERTS_PROC_LOCK_BTM, ERTS_P2P_FLG_INC_REFC); if (!proc) { + if (tmr->btm.bp) + free_message_buffer(tmr->btm.bp); hlt_delete_timer(esdp, tmr); hl_timer_destroy(tmr); } @@ -1721,6 +1721,9 @@ cancel_bif_timer(ErtsHLTimer *tmr) if (state != ERTS_TMR_STATE_ACTIVE) return 0; + if (tmr->btm.bp) + free_message_buffer(tmr->btm.bp); + res = -1; roflgs = tmr->head.roflgs; @@ -1834,18 +1837,25 @@ access_sched_local_btm(Process *c_p, Eterm pid, if (!async) hsz += REF_THING_SIZE; else { - if (is_non_value(tref)) + if (is_non_value(tref) || proc != c_p) hsz += REF_THING_SIZE; hsz += 1; /* upgrade to 3-tuple */ } if (time_left > (Sint64) MAX_SMALL) hsz += ERTS_SINT64_HEAP_SIZE(time_left); - hp = erts_alloc_message_heap(hsz, - &bp, - &ohp, - proc, - &proc_locks); + if (proc == c_p) { + bp = NULL; + ohp = NULL; + hp = HAlloc(c_p, hsz); + } + else { + hp = erts_alloc_message_heap(hsz, + &bp, + &ohp, + proc, + &proc_locks); + } #ifdef ERTS_HLT_DEBUG hp_end = hp + hsz; @@ -1871,7 +1881,7 @@ access_sched_local_btm(Process *c_p, Eterm pid, } else { Eterm tag = cancel ? am_cancel_timer : am_read_timer; - if (is_value(tref)) + if (is_value(tref) && proc == c_p) ref = tref; else { write_ref_thing(hp, @@ -1987,8 +1997,6 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp, } else { Eterm tag, res, msg; - ErlOffHeap *ohp; - ErlHeapFragment* bp; Uint hsz; Eterm *hp; ErtsProcLocks proc_locks = ERTS_PROC_LOCK_MAIN; @@ -1997,11 +2005,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp, if (time_left > (Sint64) MAX_SMALL) hsz += ERTS_SINT64_HEAP_SIZE(time_left); - hp = erts_alloc_message_heap(hsz, - &bp, - &ohp, - c_p, - &proc_locks); + hp = HAlloc(c_p, hsz); if (cancel) tag = am_cancel_timer; else @@ -2016,7 +2020,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp, msg = TUPLE3(hp, tag, tref, res); - erts_queue_message(c_p, &proc_locks, bp, + erts_queue_message(c_p, &proc_locks, NULL, msg, NIL #ifdef USE_VM_PROBES , NIL @@ -2166,7 +2170,7 @@ parse_bif_timer_options(Eterm option_list, int *async, int *info, if (async) *async = 0; if (info) - *info = 0; + *info = 1; if (abs) *abs = 0; if (accessor) @@ -2235,13 +2239,19 @@ exit_cancel_bif_timer(ErtsHLTimer *tmr, void *vesdp) tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; if (sid == (Uint32) esdp->no) { - if (state == ERTS_TMR_STATE_ACTIVE) + if (state == ERTS_TMR_STATE_ACTIVE) { + if (tmr->btm.bp) + free_message_buffer(tmr->btm.bp); hlt_delete_timer(esdp, tmr); + } hl_timer_dec_refc(tmr, roflgs); } else { - if (state == ERTS_TMR_STATE_ACTIVE) + if (state == ERTS_TMR_STATE_ACTIVE) { + if (tmr->btm.bp) + free_message_buffer(tmr->btm.bp); queue_canceled_timer(esdp, sid, (ErtsTimer *) tmr); + } else hl_timer_dec_refc(tmr, roflgs); } @@ -2415,7 +2425,7 @@ BIF_RETTYPE send_after_3(BIF_ALIST_3) tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL, 0, &timeout_pos, &short_time); if (tres != 0) - BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT); + BIF_ERROR(BIF_P, BADARG); return setup_bif_timer(BIF_P, timeout_pos, short_time, BIF_ARG_2, BIF_ARG_2, BIF_ARG_3, 0); @@ -2433,7 +2443,7 @@ BIF_RETTYPE send_after_4(BIF_ALIST_4) tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL, abs, &timeout_pos, &short_time); if (tres != 0) - BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT); + BIF_ERROR(BIF_P, BADARG); return setup_bif_timer(BIF_P, timeout_pos, short_time, BIF_ARG_2, accessor, BIF_ARG_3, 0); @@ -2447,7 +2457,7 @@ BIF_RETTYPE start_timer_3(BIF_ALIST_3) tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL, 0, &timeout_pos, &short_time); if (tres != 0) - BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT); + BIF_ERROR(BIF_P, BADARG); return setup_bif_timer(BIF_P, timeout_pos, short_time, BIF_ARG_2, BIF_ARG_2, BIF_ARG_3, !0); @@ -2465,7 +2475,7 @@ BIF_RETTYPE start_timer_4(BIF_ALIST_4) tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL, abs, &timeout_pos, &short_time); if (tres != 0) - BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT); + BIF_ERROR(BIF_P, BADARG); return setup_bif_timer(BIF_P, timeout_pos, short_time, BIF_ARG_2, accessor, BIF_ARG_3, !0); diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index ea19d8b362..8bffdedb2b 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -135,39 +135,40 @@ struct ErtsTimerWheel_ { ErtsMonotonicTime next_timeout_time; }; -/* get the time (in units of TIW_ITIME) to the next timeout, - or -1 if there are no timeouts */ - static ERTS_INLINE ErtsMonotonicTime -find_next_timeout(ErtsTimerWheel *tiw, - ErtsMonotonicTime curr_time, - ErtsMonotonicTime max_search_time) +find_next_timeout(ErtsSchedulerData *esdp, + ErtsTimerWheel *tiw, + int search_all, + ErtsMonotonicTime curr_time, /* When !search_all */ + ErtsMonotonicTime max_search_time) /* When !search_all */ { int start_ix, tiw_pos_ix; ErtsTWheelTimer *p; - int true_min_timeout; + int true_min_timeout = 0; ErtsMonotonicTime min_timeout, min_timeout_pos, slot_timeout_pos; - if (tiw->true_next_timeout_time) - return tiw->next_timeout_time; - if (tiw->nto == 0) { /* no timeouts in wheel */ - true_min_timeout = 0; - min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time + ERTS_MONOTONIC_DAY); + if (!search_all) + min_timeout_pos = tiw->pos; + else { + curr_time = erts_get_monotonic_time(esdp); + tiw->pos = min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time); + } + min_timeout_pos += ERTS_MONOTONIC_TO_CLKTCKS(ERTS_MONOTONIC_DAY); goto found_next; } - slot_timeout_pos = tiw->pos; - min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time + max_search_time); + slot_timeout_pos = min_timeout_pos = tiw->pos; + if (search_all) + min_timeout_pos += ERTS_MONOTONIC_TO_CLKTCKS(ERTS_MONOTONIC_DAY); + else + min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time + max_search_time); start_ix = tiw_pos_ix = (int) (tiw->pos & (ERTS_TIW_SIZE-1)); do { - slot_timeout_pos++; - if (slot_timeout_pos >= min_timeout_pos) { - true_min_timeout = 0; + if (++slot_timeout_pos >= min_timeout_pos) break; - } p = tiw->w[tiw_pos_ix]; @@ -269,24 +270,16 @@ remove_timer(ErtsTimerWheel *tiw, ErtsTWheelTimer *p) p->slot = ERTS_TWHEEL_SLOT_INACTIVE; -#if 0 - p->next = NULL; - p->prev = NULL; -#endif - tiw->nto--; } ErtsMonotonicTime erts_check_next_timeout_time(ErtsSchedulerData *esdp) { - /* - * Called before a scheduler is about to wait. We wont - * check more than 10 minutes into the future. - */ - return find_next_timeout(esdp->timer_wheel, - erts_get_monotonic_time(esdp), - ERTS_SEC_TO_MONOTONIC(10*60)); + ErtsTimerWheel *tiw = esdp->timer_wheel; + if (tiw->true_next_timeout_time) + return tiw->next_timeout_time; + return find_next_timeout(esdp, tiw, 1, 0, 0); } #ifndef ERTS_TW_DEBUG @@ -330,14 +323,11 @@ timeout_timer(ErtsTWheelTimer *p) { ErlTimeoutProc timeout; void *arg; -#if 0 - p->next = NULL; - p->prev = NULL; -#endif p->slot = ERTS_TWHEEL_SLOT_INACTIVE; timeout = p->u.func.timeout; arg = p->u.func.arg; (*timeout)(arg); + ASSERT_NO_LOCKED_LOCKS; } void @@ -508,7 +498,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time) tiw->next_timeout_time = curr_time + ERTS_MONOTONIC_DAY; /* Search at most two seconds ahead... */ - (void) find_next_timeout(tiw, curr_time, ERTS_SEC_TO_MONOTONIC(2)); + (void) find_next_timeout(NULL, tiw, 0, curr_time, ERTS_SEC_TO_MONOTONIC(2)); } Uint diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl index 699a1436ce..c855481489 100644 --- a/erts/emulator/test/after_SUITE.erl +++ b/erts/emulator/test/after_SUITE.erl @@ -27,7 +27,8 @@ init_per_group/2,end_per_group/2, t_after/1, receive_after/1, receive_after_big/1, receive_after_errors/1, receive_var_zero/1, receive_zero/1, - multi_timeout/1, receive_after_32bit/1]). + multi_timeout/1, receive_after_32bit/1, + receive_after_blast/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -40,7 +41,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [t_after, receive_after, receive_after_big, receive_after_errors, receive_var_zero, receive_zero, - multi_timeout, receive_after_32bit]. + multi_timeout, receive_after_32bit, receive_after_blast]. groups() -> []. @@ -244,4 +245,26 @@ recv_after_32bit(I, T) when I rem 2 =:= 0 -> receive after T -> exit(timeout) end; recv_after_32bit(_, _) -> receive after 16#ffffFFFF -> exit(timeout) end. - + +blaster() -> + receive + {go, TimeoutTime} -> + Tmo = TimeoutTime - erlang:monotonic_time(milli_seconds), + receive after Tmo -> ok end + end. + +spawn_blasters(0) -> + []; +spawn_blasters(N) -> + [spawn_monitor(fun () -> blaster() end)|spawn_blasters(N-1)]. + +receive_after_blast(Config) when is_list(Config) -> + PMs = spawn_blasters(10000), + TimeoutTime = erlang:monotonic_time(milli_seconds) + 5000, + lists:foreach(fun ({P, _}) -> P ! {go, TimeoutTime} end, PMs), + lists:foreach(fun ({P, M}) -> + receive + {'DOWN', M, process, P, normal} -> + ok + end + end, PMs). diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index 0fd6696536..e3ac2d5d83 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -264,6 +264,37 @@ memory_test(_Config) -> []), cmp_memory(MWs, "unlink procs"), + mem_workers_call(MWs, + fun () -> + lists:foreach( + fun (P) -> + Tmr = erlang:start_timer(1 bsl 34, + P, + hello), + Tmrs = case get('BIF_TMRS') of + undefined -> []; + Rs -> Rs + end, + true = is_reference(Tmr), + put('BIF_TMRS', [Tmr|Tmrs]) + end, Ps) + end, + []), + cmp_memory(MWs, "start BIF timer procs"), + + mem_workers_call(MWs, + fun () -> + lists:foreach(fun (Tmr) -> + true = is_reference(Tmr), + true = is_integer(erlang:cancel_timer(Tmr)) + end, get('BIF_TMRS')), + put('BIF_TMRS', undefined), + garbage_collect() + end, + []), + erts_debug:set_internal_state(wait, deallocations), + cmp_memory(MWs, "cancel BIF timer procs"), + DMs = mem_workers_call(MWs, fun () -> lists:map(fun (P) -> diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index 3701dd8437..d406456f98 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -26,11 +26,17 @@ cancel_timer_1/1, start_timer_big/1, send_after_big/1, start_timer_e/1, send_after_e/1, cancel_timer_e/1, - read_timer_trivial/1, read_timer/1, - cleanup/1, evil_timers/1, registered_process/1]). + read_timer_trivial/1, read_timer/1, read_timer_async/1, + cleanup/1, evil_timers/1, registered_process/1, same_time_yielding/1, + same_time_yielding_with_cancel/1, same_time_yielding_with_cancel_other/1, + same_time_yielding_with_cancel_other_accessor/1, auto_cancel_yielding/1]). -include_lib("test_server/include/test_server.hrl"). +-define(SHORT_TIMEOUT, 5000). %% Bif timers as short as this may be pre-allocated +-define(TIMEOUT_YIELD_LIMIT, 100). +-define(AUTO_CANCEL_YIELD_LIMIT, 100). + init_per_testcase(_Case, Config) -> ?line Dog=test_server:timetrap(test_server:seconds(30)), case catch erts_debug:get_internal_state(available_internal_state) of @@ -45,6 +51,7 @@ end_per_testcase(_Case, Config) -> ok. init_per_suite(Config) -> + erts_debug:set_internal_state(available_internal_state, true), Config. end_per_suite(_Config) -> @@ -56,8 +63,12 @@ all() -> [start_timer_1, send_after_1, send_after_2, cancel_timer_1, start_timer_e, send_after_e, cancel_timer_e, start_timer_big, send_after_big, - read_timer_trivial, read_timer, cleanup, evil_timers, - registered_process]. + read_timer_trivial, read_timer, read_timer_async, + cleanup, evil_timers, registered_process, + same_time_yielding, same_time_yielding_with_cancel, + same_time_yielding_with_cancel_other, + same_time_yielding_with_cancel_other_accessor, + auto_cancel_yielding]. groups() -> []. @@ -162,7 +173,7 @@ cancel_timer_1(Config) when is_list(Config) -> start_timer_e(doc) -> ["Error cases for start_timer/3"]; start_timer_e(Config) when is_list(Config) -> ?line {'EXIT', _} = (catch erlang:start_timer(-4, self(), hej)), - ?line {'EXIT', _} = (catch erlang:start_timer(4728472847827482, + ?line {'EXIT', _} = (catch erlang:start_timer(1 bsl 64, self(), hej)), ?line {'EXIT', _} = (catch erlang:start_timer(4.5, self(), hej)), @@ -180,7 +191,7 @@ send_after_e(doc) -> ["Error cases for send_after/3"]; send_after_e(suite) -> []; send_after_e(Config) when is_list(Config) -> ?line {'EXIT', _} = (catch erlang:send_after(-4, self(), hej)), - ?line {'EXIT', _} = (catch erlang:send_after(4728472847827482, + ?line {'EXIT', _} = (catch erlang:send_after(1 bsl 64, self(), hej)), ?line {'EXIT', _} = (catch erlang:send_after(4.5, self(), hej)), @@ -213,44 +224,79 @@ read_timer_trivial(Config) when is_list(Config) -> read_timer(doc) -> ["Test that read_timer/1 seems to return the correct values."]; read_timer(suite) -> []; read_timer(Config) when is_list(Config) -> - ?line Big = 1 bsl 31, - ?line R = erlang:send_after(Big, self(), hej_hopp), + process_flag(scheduler, 1), + Big = 1 bsl 31, + R = erlang:send_after(Big, self(), hej_hopp), + + receive after 200 -> ok end, % Delay and clear reductions. + Left = erlang:read_timer(R), + Left2 = erlang:cancel_timer(R), + case Left == Left2 of + true -> ok; + false -> Left = Left2 + 1 + end, + false = erlang:read_timer(R), - ?line receive after 200 -> ok end, % Delay and clear reductions. - ?line Left = erlang:read_timer(R), - ?line Left = erlang:cancel_timer(R), - ?line false = erlang:read_timer(R), + case Big - Left of + Diff when Diff >= 200, Diff < 10000 -> + ok; + _Diff -> + test_server:fail({big, Big, Left}) + end, + process_flag(scheduler, 0), + ok. - ?line case Big - Left of - Diff when Diff >= 200, Diff < 10000 -> - ok; - _Diff -> - test_server:fail({big, Big, Left}) - end, +read_timer_async(doc) -> ["Test that read_timer/1 seems to return the correct values."]; +read_timer_async(suite) -> []; +read_timer_async(Config) when is_list(Config) -> + process_flag(scheduler, 1), + Big = 1 bsl 33, + R = erlang:send_after(Big, self(), hej_hopp), + + %% Access from another scheduler + process_flag(scheduler, erlang:system_info(schedulers_online)), + + receive after 200 -> ok end, % Delay and clear reductions. + ok = erlang:read_timer(R, [{async, true}]), + ok = erlang:cancel_timer(R, [{async, true}, {info, true}]), + ok = erlang:read_timer(R, [{async, true}]), + + {read_timer, R, Left} = receive_one(), + {cancel_timer, R, Left2} = receive_one(), + case Left == Left2 of + true -> ok; + false -> Left = Left2 + 1 + end, + {read_timer, R, false} = receive_one(), + + case Big - Left of + Diff when Diff >= 200, Diff < 10000 -> + ok; + _Diff -> + test_server:fail({big, Big, Left}) + end, + process_flag(scheduler, 0), ok. cleanup(doc) -> []; cleanup(suite) -> []; cleanup(Config) when is_list(Config) -> - {skipped, "Test needs to be UPDATED for new timer implementation"}. - -cleanup_test(Config) when is_list(Config) -> ?line Mem = mem(), %% Timer on dead process ?line P1 = spawn(fun () -> ok end), ?line wait_until(fun () -> process_is_cleaned_up(P1) end), - ?line T1 = erlang:start_timer(10000, P1, "hej"), - ?line T2 = erlang:send_after(10000, P1, "hej"), + ?line T1 = erlang:start_timer(?SHORT_TIMEOUT*2, P1, "hej"), + ?line T2 = erlang:send_after(?SHORT_TIMEOUT*2, P1, "hej"), receive after 1000 -> ok end, ?line Mem = mem(), ?line false = erlang:read_timer(T1), ?line false = erlang:read_timer(T2), ?line Mem = mem(), %% Process dies before timeout - ?line P2 = spawn(fun () -> receive after 500 -> ok end end), - ?line T3 = erlang:start_timer(10000, P2, "hej"), - ?line T4 = erlang:send_after(10000, P2, "hej"), - ?line true = Mem < mem(), + ?line P2 = spawn(fun () -> receive after (?SHORT_TIMEOUT div 10) -> ok end end), + ?line T3 = erlang:start_timer(?SHORT_TIMEOUT*2, P2, "hej"), + ?line T4 = erlang:send_after(?SHORT_TIMEOUT*2, P2, "hej"), + ?line true = mem_larger_than(Mem), ?line true = is_integer(erlang:read_timer(T3)), ?line true = is_integer(erlang:read_timer(T4)), ?line wait_until(fun () -> process_is_cleaned_up(P2) end), @@ -259,21 +305,22 @@ cleanup_test(Config) when is_list(Config) -> ?line false = erlang:read_timer(T4), ?line Mem = mem(), %% Cancel timer - ?line P3 = spawn(fun () -> receive after 20000 -> ok end end), - ?line T5 = erlang:start_timer(10000, P3, "hej"), - ?line T6 = erlang:send_after(10000, P3, "hej"), - ?line true = Mem < mem(), + ?line P3 = spawn(fun () -> receive after ?SHORT_TIMEOUT*4 -> ok end end), + ?line T5 = erlang:start_timer(?SHORT_TIMEOUT*2, P3, "hej"), + ?line T6 = erlang:send_after(?SHORT_TIMEOUT*2, P3, "hej"), + ?line true = mem_larger_than(Mem), ?line true = is_integer(erlang:cancel_timer(T5)), ?line true = is_integer(erlang:cancel_timer(T6)), ?line false = erlang:read_timer(T5), ?line false = erlang:read_timer(T6), ?line exit(P3, kill), + ?line wait_until(fun () -> process_is_cleaned_up(P3) end), ?line Mem = mem(), %% Timeout ?line Ref = make_ref(), - ?line T7 = erlang:start_timer(500, self(), Ref), - ?line T8 = erlang:send_after(500, self(), Ref), - ?line true = Mem < mem(), + ?line T7 = erlang:start_timer(?SHORT_TIMEOUT+1, self(), Ref), + ?line T8 = erlang:send_after(?SHORT_TIMEOUT+1, self(), Ref), + ?line true = mem_larger_than(Mem), ?line true = is_integer(erlang:read_timer(T7)), ?line true = is_integer(erlang:read_timer(T8)), ?line receive {timeout, T7, Ref} -> ok end, @@ -423,15 +470,12 @@ evil_recv_timeouts(TOs, N, M) -> registered_process(doc) -> []; registered_process(suite) -> []; registered_process(Config) when is_list(Config) -> - {skipped, "Test needs to be UPDATED for new timer implementation"}. - -registered_process_test(Config) when is_list(Config) -> ?line Mem = mem(), %% Cancel - ?line T1 = erlang:start_timer(500, ?MODULE, "hej"), - ?line T2 = erlang:send_after(500, ?MODULE, "hej"), + ?line T1 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, "hej"), + ?line T2 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, "hej"), ?line undefined = whereis(?MODULE), - ?line true = Mem < mem(), + ?line true = mem_larger_than(Mem), ?line true = is_integer(erlang:cancel_timer(T1)), ?line true = is_integer(erlang:cancel_timer(T2)), ?line false = erlang:read_timer(T1), @@ -439,10 +483,10 @@ registered_process_test(Config) when is_list(Config) -> ?line Mem = mem(), %% Timeout register after start ?line Ref1 = make_ref(), - ?line T3 = erlang:start_timer(500, ?MODULE, Ref1), - ?line T4 = erlang:send_after(500, ?MODULE, Ref1), + ?line T3 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref1), + ?line T4 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref1), ?line undefined = whereis(?MODULE), - ?line true = Mem < mem(), + ?line true = mem_larger_than(Mem), ?line true = is_integer(erlang:read_timer(T3)), ?line true = is_integer(erlang:read_timer(T4)), ?line true = register(?MODULE, self()), @@ -451,9 +495,9 @@ registered_process_test(Config) when is_list(Config) -> ?line Mem = mem(), %% Timeout register before start ?line Ref2 = make_ref(), - ?line T5 = erlang:start_timer(500, ?MODULE, Ref2), - ?line T6 = erlang:send_after(500, ?MODULE, Ref2), - ?line true = Mem < mem(), + ?line T5 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref2), + ?line T6 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref2), + ?line true = mem_larger_than(Mem), ?line true = is_integer(erlang:read_timer(T5)), ?line true = is_integer(erlang:read_timer(T6)), ?line receive {timeout, T5, Ref2} -> ok end, @@ -462,19 +506,135 @@ registered_process_test(Config) when is_list(Config) -> ?line true = unregister(?MODULE), ?line ok. -mem() -> - TSrvs = erts_internal:get_bif_timer_servers(), - lists:foldl(fun (Tab, Sz) -> - case lists:member(ets:info(Tab, owner), TSrvs) of - true -> - ets:info(Tab, memory) + Sz; - false -> - Sz - end - end, - 0, - ets:all())*erlang:system_info({wordsize,external}). - +same_time_yielding(Config) when is_list(Config) -> + Mem = mem(), + SchdlrsOnln = erlang:system_info(schedulers_online), + Tmo = erlang:monotonic_time(milli_seconds) + 3000, + Tmrs = lists:map(fun (I) -> + process_flag(scheduler, (I rem SchdlrsOnln) + 1), + erlang:start_timer(Tmo, self(), hej, [{abs, true}]) + end, + lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)), + true = mem_larger_than(Mem), + lists:foreach(fun (Tmr) -> receive {timeout, Tmr, hej} -> ok end end, Tmrs), + Done = erlang:monotonic_time(milli_seconds), + true = Done >= Tmo, + case erlang:system_info(build_type) of + opt -> true = Done < Tmo + 200; + _ -> true = Done < Tmo + 1000 + end, + Mem = mem(), + ok. + +same_time_yielding_with_cancel(Config) when is_list(Config) -> + same_time_yielding_with_cancel_test(false, false). + +same_time_yielding_with_cancel_other(Config) when is_list(Config) -> + same_time_yielding_with_cancel_test(true, false). + +same_time_yielding_with_cancel_other_accessor(Config) when is_list(Config) -> + same_time_yielding_with_cancel_test(true, true). + +do_cancel_tmrs(Tmo, Tmrs, Tester) -> + BeginCancel = erlang:convert_time_unit(Tmo, + milli_seconds, + micro_seconds) - 100, + busy_wait_until(fun () -> + erlang:monotonic_time(micro_seconds) >= BeginCancel + end), + lists:foreach(fun (Tmr) -> + erlang:cancel_timer(Tmr, + [{async, true}, + {info, true}]) + end, Tmrs), + case Tester == self() of + true -> ok; + false -> forward_msgs(Tester) + end. + +same_time_yielding_with_cancel_test(Other, Accessor) -> + Mem = mem(), + SchdlrsOnln = erlang:system_info(schedulers_online), + Tmo = erlang:monotonic_time(milli_seconds) + 3000, + Tester = self(), + Cancelor = case Other of + false -> + Tester; + true -> + spawn(fun () -> + receive + {timers, Tmrs} -> + do_cancel_tmrs(Tmo, Tmrs, Tester) + end + end) + end, + Opts = case Accessor of + false -> [{abs, true}]; + true -> [{accessor, Cancelor}, {abs, true}] + end, + Tmrs = lists:map(fun (I) -> + process_flag(scheduler, (I rem SchdlrsOnln) + 1), + erlang:start_timer(Tmo, self(), hej, Opts) + end, + lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)), + true = mem_larger_than(Mem), + case Other of + false -> + do_cancel_tmrs(Tmo, Tmrs, Tester); + true -> + Cancelor ! {timers, Tmrs} + end, + {Tmos, Cncls} = lists:foldl(fun (Tmr, {T, C}) -> + receive + {timeout, Tmr, hej} -> + receive + {cancel_timer, Tmr, Info} -> + false = Info, + {T+1, C} + end; + {cancel_timer, Tmr, false} -> + receive + {timeout, Tmr, hej} -> + {T+1, C} + end; + {cancel_timer, Tmr, TimeLeft} -> + true = is_integer(TimeLeft), + {T, C+1} + end + end, + {0, 0}, + Tmrs), + io:format("Timeouts: ~p Cancels: ~p~n", [Tmos, Cncls]), + Mem = mem(), + case Other of + true -> exit(Cancelor, bang); + false -> ok + end, + {comment, + "Timeouts: " ++ integer_to_list(Tmos) ++ " Cancels: " + ++ integer_to_list(Cncls)}. + +auto_cancel_yielding(Config) when is_list(Config) -> + Mem = mem(), + SchdlrsOnln = erlang:system_info(schedulers_online), + P = spawn(fun () -> + lists:foreach( + fun (I) -> + process_flag(scheduler, (I rem SchdlrsOnln)+1), + erlang:start_timer((1 bsl 28)+I*10, self(), hej) + end, + lists:seq(1, + ((?AUTO_CANCEL_YIELD_LIMIT*3+1) + *SchdlrsOnln))), + receive after infinity -> ok end + end), + true = mem_larger_than(Mem), + exit(P, bang), + wait_until(fun () -> process_is_cleaned_up(P) end), + receive after 1000 -> ok end, + Mem = mem(), + ok. + process_is_cleaned_up(P) when is_pid(P) -> undefined == erts_debug:get_internal_state({process_status, P}). @@ -484,6 +644,19 @@ wait_until(Pred) when is_function(Pred) -> _ -> receive after 50 -> ok end, wait_until(Pred) end. +busy_wait_until(Pred) when is_function(Pred) -> + case catch Pred() of + true -> ok; + _ -> busy_wait_until(Pred) + end. + +forward_msgs(To) -> + receive + Msg -> + To ! Msg + end, + forward_msgs(To). + get(Time, Msg) -> receive Msg -> @@ -566,5 +739,58 @@ type(X) when is_port(X) -> {port, node(X)}; type(X) when is_binary(X) -> binary; type(X) when is_atom(X) -> atom; type(_) -> unknown. - + +mem_larger_than(no_fix_alloc) -> + true; +mem_larger_than(Mem) -> + mem() > Mem. + +mem() -> + erts_debug:set_internal_state(wait, deallocations), + erts_debug:set_internal_state(wait, deallocations), + case mem_get() of + {-1, -1} -> no_fix_alloc; + {A, U} -> io:format("mem = ~p ~p~n", [A, U]), U + end. + +mem_get() -> + % Bif timer memory + Ref = make_ref(), + erlang:system_info({memory_internal, Ref, [fix_alloc]}), + mem_recv(erlang:system_info(schedulers), Ref, {0, 0}). + +mem_recv(0, _Ref, AU) -> + AU; +mem_recv(N, Ref, AU) -> + receive + {Ref, _, IL} -> + mem_recv(N-1, Ref, mem_parse_ilists(IL, AU)) + end. + + +mem_parse_ilists([], AU) -> + AU; +mem_parse_ilists([I|Is], AU) -> + mem_parse_ilists(Is, mem_parse_ilist(I, AU)). + +mem_parse_ilist({fix_alloc, false}, _) -> + {-1, -1}; +mem_parse_ilist({fix_alloc, _, IDL}, {A, U}) -> + case lists:keyfind(fix_types, 1, IDL) of + {fix_types, TL} -> + {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0), + {ThisA + A, ThisU + U}; + {fix_types, Mask, TL} -> + {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0), + {(ThisA + A) band Mask , (ThisU + U) band Mask} + end. + +mem_get_btm_aus([], A, U) -> + {A, U}; +mem_get_btm_aus([{BtmType, BtmA, BtmU} | Types], + A, U) when BtmType == bif_timer; + BtmType == accessor_bif_timer -> + mem_get_btm_aus(Types, BtmA+A, BtmU+U); +mem_get_btm_aus([_|Types], A, U) -> + mem_get_btm_aus(Types, A, U). diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index ce61199567..aa9d8ae4fe 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 6aea2c08e4..ba7878bd2c 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -425,19 +425,23 @@ call_on_load_function(_P1) -> erlang:nif_error(undefined). %% cancel_timer/1 --spec erlang:cancel_timer(TimerRef) -> Time | false when +-spec erlang:cancel_timer(TimerRef) -> Result when TimerRef :: reference(), - Time :: non_neg_integer(). + Time :: non_neg_integer(), + Result :: Time | false. cancel_timer(_TimerRef) -> erlang:nif_error(undefined). %% cancel_timer/2 --spec erlang:cancel_timer(TimerRef, Options) -> Time | false | ok when +-spec erlang:cancel_timer(TimerRef, Options) -> Result | ok when TimerRef :: reference(), - Option :: {async, boolean()} | {info, boolean()}, + Async :: boolean(), + Info :: boolean(), + Option :: {async, Async} | {info, Info}, Options :: [Option], - Time :: non_neg_integer(). + Time :: non_neg_integer(), + Result :: Time | false. cancel_timer(_TimerRef, _Options) -> erlang:nif_error(undefined). @@ -1478,17 +1482,22 @@ raise(_Class, _Reason, _Stacktrace) -> erlang:nif_error(undefined). %% read_timer/1 --spec erlang:read_timer(TimerRef) -> non_neg_integer() | false when - TimerRef :: reference(). +-spec erlang:read_timer(TimerRef) -> Result when + TimerRef :: reference(), + Time :: non_neg_integer(), + Result :: Time | false. read_timer(_TimerRef) -> erlang:nif_error(undefined). %% read_timer/2 --spec erlang:read_timer(TimerRef, Options) -> non_neg_integer() | false | ok when +-spec erlang:read_timer(TimerRef, Options) -> Result | ok when TimerRef :: reference(), - Option :: {async, boolean()}, - Options :: [Option]. + Async :: boolean(), + Option :: {async, Async}, + Options :: [Option], + Time :: non_neg_integer(), + Result :: Time | false. read_timer(_TimerRef, _Options) -> erlang:nif_error(undefined). @@ -1547,7 +1556,8 @@ send_after(_Time, _Dest, _Msg) -> Dest :: pid() | atom(), Msg :: term(), Options :: [Option], - Option :: {abs, boolean()} | {accessor, pid()}, + Abs :: boolean(), + Option :: {abs, Abs}, %% | {accessor, Accessor} undocumented feature for now, TimerRef :: reference(). send_after(_Time, _Dest, _Msg, _Options) -> @@ -1634,7 +1644,8 @@ start_timer(_Time, _Dest, _Msg) -> Dest :: pid() | atom(), Msg :: term(), Options :: [Option], - Option :: {abs, boolean()} | {accessor, pid()}, + Abs :: boolean(), + Option :: {abs, Abs}, %% | {accessor, Accessor} undocumented feature for now, TimerRef :: reference(). start_timer(_Time, _Dest, _Msg, _Options) -> @@ -3589,7 +3600,11 @@ blocks_size([], Acc) -> get_fix_proc([{ProcType, A1, U1}| Rest], {A0, U0}) when ProcType == proc; ProcType == monitor_sh; ProcType == nlink_sh; - ProcType == msg_ref -> + ProcType == msg_ref; + ProcType == ll_ptimer; + ProcType == hl_ptimer; + ProcType == bif_timer; + ProcType == accessor_bif_timer -> get_fix_proc(Rest, {A0+A1, U0+U1}); get_fix_proc([_|Rest], Acc) -> get_fix_proc(Rest, Acc); -- cgit v1.2.3 From 1419d6d5cd7bcca836d352e73c662b1e336d130e Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 12 May 2015 17:38:53 +0200 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 56328 -> 56328 bytes erts/preloaded/ebin/erlang.beam | Bin 101812 -> 101808 bytes erts/preloaded/ebin/erts_internal.beam | Bin 5380 -> 5380 bytes erts/preloaded/ebin/init.beam | Bin 48760 -> 48768 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1468 -> 1468 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1340 -> 1340 bytes erts/preloaded/ebin/prim_file.beam | Bin 44904 -> 44904 bytes erts/preloaded/ebin/prim_inet.beam | Bin 73092 -> 73092 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23416 -> 23416 bytes erts/preloaded/ebin/zlib.beam | Bin 14176 -> 14176 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index df768f9ed6..df12c6f8e0 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index aa9d8ae4fe..c0fca6aafa 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index bee3477b2e..0e0811af3f 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index f3abb5c1c7..851513b2e9 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 4af9d233b5..33c112f4de 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 7c0b49235e..ebca6e7eea 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 00babefbb4..e8817d183e 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 6640a29c62..6729f06b79 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 3d6f1548d0..969239be98 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 3224546179..281f668f8c 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From e09dd66dc4d89c62ddfd8c19791f9678d5d787c6 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 12 May 2015 18:18:55 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 502 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 502 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 35e6e55e72..c85cbe543d 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,508 @@

This document describes the changes made to the ERTS application.

+
Erts 7.0 + +
Fixed Bugs and Malfunctions + + +

+ Fix issuing with spaces and quoting in the arguments when + using erlang:open_port spawn_executable on windows. The + behavior now mimics how unix works. This change implies a + backwards incompatibility for how spawn_executable works + on windows.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11905

+
+ +

+ Fix global call trace when hipe compiled code call beam + compiled functions. Tracing of beam functions should now + alway work regardless who the caller is.

+

+ Own Id: OTP-11939

+
+ +

+ Correct cache alignment for ETS write_concurrency + locks to improve performance by reduced false sharing. + May increase memory footprint for tables with + write_concurrency.

+

+ Own Id: OTP-11974

+
+ +

+ All possibly blocking operations in the fd/spawn and + terminal driver have been converted to non-blocking + operations. Before this fix it was possible for the VM to + be blocked for a long time if the entity consuming + stdout/stderr did not consume it fast enough.

+

+ Own Id: OTP-12239

+
+ +

+ Add missing overhead for offheap binaries created from + external format. This fix can improve the garbage + collection of large binaries originating from + binary_to_term or messages from remote nodes.

+

+ Own Id: OTP-12554

+
+ +

+ Ensure hashing of zero is consistent

+

Erlang treats positive and negative zero as + equal:

+

+ true = 0.0 =:= 0.0/-1

+

However, Erlangs hash functions: hash, phash and + phash2 did not reflect this behaviour. The hash values + produced by the different hash functions would not be + identical for positive and negative zero.

This + change ensures that hash value of positive zero is always + produced regardless of the signedness of the zero float, + i.e.,

+

+ true = erlang:phash2(0.0) =:= + erlang:phash2(0.0/-1)

+

+ Own Id: OTP-12641

+
+ +

+ Ensure NIF term creation disallows illegal floating point + values and too long atoms. Such values will cause a NIF + to throw badarg exception when it returns.

+

+ Own Id: OTP-12655

+
+ +

+ Fixed building of Map results from match_specs

+

+ A faulty "box-value" entered into the heap which could + cause a segmentation fault in the garbage collector if it + was written on a heap fragment.

+

+ Own Id: OTP-12656

+
+ +

+ Fix hipe bug when matching a "writable" binary. The bug + has been seen to sometimes cause a failed binary matching + of a correct utf8 character, but other symptoms are also + possible.

+

+ Own Id: OTP-12667

+
+ +

+ Keep dirty schedulers from waking other schedulers.

+

+ Own Id: OTP-12685

+
+ +

+ Disable floating point exceptions if the VM is compiled + by clang/llvm. This is a known long-standing problem in + clang/llvm.

+

+ Own Id: OTP-12717

+
+ +

+ Fix bug in file:sendfile for FreeBSD causing not + the entire file to be sent.

+

+ Own Id: OTP-12720

+
+
+
+ + +
Improvements and New Features + + +

+ Add md5 and module entries to + ?MODULE:module_info/0/1 and remove obsolete entry + 'import'.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11940

+
+ +

+ Debug function erlang:display/1 shows content of + binaries and bitstrings, not only the length.

+

+ Own Id: OTP-11941

+
+ +

The time functionality of Erlang has been extended. + This both includes a new + API for time, as well as time warp + modes which alters the behavior of the system + when system time changes. You are strongly encouraged + to use the new API instead of the old API based on + erlang:now/0. + erlang:now/0 has been deprecated since it is and + forever will be a scalability bottleneck. For more + information see the Time and Time + Correction chapter of the ERTS User's + Guide.

+

Besides the API changes and time warp modes a lot of + scalability and performance improvements regarding time + management has been made internally in the runtime + system. Examples of such improvements are scheduler + specific timer wheels, scheduler specific BIF timer + management, parallel retrieval of monotonic time and + system time on systems with primitives that are not + buggy.

+

+ Own Id: OTP-11997

+
+ +

erlang:function_exported(M, F, A) will now + return true if M:F/A refers to a BIF.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12099

+
+ +

+ New BIF: erlang:get_keys/0, lists all keys + associated with the process dictionary. Note: + erlang:get_keys/0 is auto-imported.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12151 Aux Id: seq12521

+
+ +

+ Make distributed send of large messages yield to improve + real-time characteristics.

+

+ Own Id: OTP-12232

+
+ +

+ Use high accuracy poll timeouts

+

+ Where available, use poll/select API's that can handle + time resolutions less than 1ms. In the cases where such + API's are not available the timeout is rounded up to the + nearest ms.

+

+ Own Id: OTP-12236

+
+ +

+ The internal group to user_drv protocol has been changed + to be synchronous in order to guarantee that output sent + to a process implementing the user_drv protocol is + printed before replying. This protocol is used by the + standard_output device and the ssh application when + acting as a client.

+

+ This change changes the previous unlimited buffer when + printing to standard_io and other devices that end up in + user_drv to 1KB.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12240

+
+ +

The previously introduced "eager check I/O" feature is + now enabled by default.

+

Eager check I/O can be disabled using the erl + command line argument: +secio false

+

Characteristics impact compared to previous + default:

Lower latency and smoother + management of externally triggered I/O operations. + A slightly reduced priority of externally triggered + I/O operations. +

+ Own Id: OTP-12254 Aux Id: OTP-12117

+
+ +

+ Properly support maps in match_specs

+

+ Own Id: OTP-12270

+
+ +

+ The notice that a crashdump has been written has been + moved to be printed before the crashdump is generated + instead of afterwords. The wording of the notice has also + been changed.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12292

+
+ +

+ New function ets:take/2. Works the same as + ets:delete/2 but also returns the deleted + object(s).

+

+ Own Id: OTP-12309

+
+ +

+ Tracing with cpu_timestamp option has been enabled on + Linux.

+

+ Own Id: OTP-12366

+
+ +

+ ets:info/1,2 now contains information about whether + write_concurrency or read_concurrency is enabled.

+

+ Own Id: OTP-12376

+
+ +

+ Improved usage of gcc's builtins for atomic memory + access. These are used when no other implementation of + atomic memory operations is available. For example, when + compiling for ARM when libatomic_ops is not + available.

+

+ The largest improvement will be seen when compiling with + a gcc with support for the __atomic_* + builtins (using a gcc of at least version 4.7), + but also when only the legacy __sync_* builtins + are available (using a gcc of at least version + 4.1) an improvement can be seen.

+

+ For more information see the "Atomic + Memory Operations and the VM" section of + $ERL_TOP/HOWTO/INSTALL.md.

+

+ Own Id: OTP-12383

+
+ +

+ Introduce math:log2/1 function to math module.

+

+ Own Id: OTP-12411

+
+ +

+ Remove perfctr support

+

+ Development of perfctr in the linux kernel ceased in + 2010. The perfctr support code in the Erlang VM is thus + effectively dead code and therefor removed.

+

+ Own Id: OTP-12508

+
+ +

zlib:inflateChunk/2 has been added. It works + like zlib:inflate/2, but decompresses no more data + than will fit in the buffer configured by + zlib:setBufSize/2.

+

+ Own Id: OTP-12548

+
+ +

+ Use linear search for small select_val arrays

+

+ Own Id: OTP-12555

+
+ +

+ New BIF ets:update_counter/4 with a default object as + argument, which will be inserted in the table if the key + was not found.

+

+ Own Id: OTP-12563

+
+ +

+ Export missing types from zlib module

+

+ Own Id: OTP-12584

+
+ +

+ Use persistent hashmaps for large Maps

Maps will use a + persistent hashmap implementation when the number of + pairs in a Map becomes sufficiently large. The change + will occur when a Map reaches 33 pairs in size but this + limit might change in the future.

+

The most significant impact for the user by this + change is speed, and to a lesser degree memory + consumption and introspection of Maps. Memory consumption + size is probalistic but lesser than gb_trees or + dict for instance. Any other impacts will be + transparent for the user except for the following + changes.

+

Semantics of Maps have changed in two incompatible + ways compared to the experimental implementation in OTP + 17:

Hashing of maps is done different by + erlang:phash2/1,2, erlang:phash/1 and + erlang:hash/2. Comparing two maps + with ==, /=, =<, <, >= and >, is done + different if the keys contain floating point + numbers. +

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12585

+
+ +

+ Scalability improvement for erlang:make_ref/0, + and other functionality that create references. Each + scheduler now manage its own set of references. By this + no communication at all is needed when creating + references.

+

+ Previous implementation generated a strictly + monotonically increasing sequence of references + corresponding to creation time on the runtime system + instance. This is not the case with current + implementation. You can only expect reference to be + unique. The Erlang/OTP documentation has never mentioned + anything else but the uniqueness property, so this change + is fully compatible. The only reason we've + marked this as a potential incompatibility is since an + early draft for an Erlang specification mentions strict + monotonicity as a property.

+

+ If you need to create data with a strict monotonicity + property use erlang:unique_integer([monotonic]). + Do not use the deprecated erlang:now().

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12610

+
+ +

+ Enable different abort signal from heart

+

By using environment variable HEART_KILL_SIGNAL, heart + can now use a different signal to kill the old running + Erlang.

+

By default the signal is SIGKILL but SIGABRT may also + be used by setting environment variable: + HEART_KILL_SIGNAL=SIGABRT

+

+ Own Id: OTP-12613 Aux Id: seq12826

+
+ +

+ Update autconf to latest version 2015-03-04

+

+ Own Id: OTP-12646

+
+ +

+ Optimization of timers internally in the VM. This include + process timers (receive ... after), port timers + (driver_set_timer()) as well as BIF timers + (erlang:send_after()/erlang:start_timer()).

+

+ Each scheduler thread now has its own lock-free timer + service instead of one locked central service. This + dramatically improves performance of timer management on + systems with a large amount of schedulers and timers.

+

+ The timer service internal data structure has also been + optimized to be able to handle more timers than before. + That is, each timer service is by its self able to handle + more timers without dramatic performance loss than the + old centralized timer service.

+

+ The API of BIF timers has also been extended. Timeout + values are for example no longer limited to 32-bit + integers. For more information see the documentation of + erlang:start_timer/4, + erlang:send_after/4, + erlang:cancel_timer/2, + and erlang:read_timer/2.

+

+ Own Id: OTP-12650 Aux Id: OTP-11997

+
+ +

+ Specialize instructions from common assembler patterns

+

Specialize common instructions of rem, + band, minus and plus in the beam + loader. This will reduce the number of fetches and thus + lessen the instruction dispatch pressure during runtime + and speed up those operations in some common cases.

+

Specialize move patterns from x-registers to the stack + with a new move_window instruction. This change + will reduce instruction dispatch pressure.

+

+ Own Id: OTP-12690

+
+ +

+ Fix cross compilation for Android.

+

+ Own Id: OTP-12693

+
+ +

+ Fix incorrect use of autoconf macro AC_EGREP_CPP, which + could cause faulty configuration if run from a path + containing the string 'yes'.

+

+ Own Id: OTP-12706

+
+ +

+ Minimal Java version is now 1.6

+

+ Own Id: OTP-12718

+
+ +

+ Send format and args on process exit to error_logger

+

+ Previously, the emulator would generate a whole string + with values and call the error_logger passing + "~s~n". This changes it to a format string + containing ~p with the respective values as + arguments.

+

+ Own Id: OTP-12735

+
+
+
+ +
+
Erts 6.4.1
Fixed Bugs and Malfunctions -- cgit v1.2.3 From 25bd6312491fc9153a16f74b5d1d39609426ae60 Mon Sep 17 00:00:00 2001 From: Rory Byrne Date: Wed, 15 Apr 2015 17:28:09 +0100 Subject: Fix gen_tcp:shutdown/2 by making it asynchronous If the driver queue is empty, or the user is requesting a 'read' shutdown, then the shutdown() syscall is performed synchronously, as per the old version of shutdown/2. However, if the user is requesting a 'write' or 'read_write' shutdown, and there is data in the driver queue for the socket, then the shutdown() syscall is delayed and handled asynchronously when the driver queue is written out. This version of shutdown solves a number of issues with the old version. The two main solutions it offers are: * It doesn't block when the TCP peer is idle or slow. This is the expected behaviour when shutdown() is called: the caller needs to be able to continue reading from the socket, not be prevented from doing so. * It doesn't truncate the output. The current version of gen_tcp:shutdown/2 will truncate any outbound data in the driver queue after about 10 seconds if the TCP peer is idle of slow. Worse yet, it doesn't even inform anyone that the data has been truncated: 'ok' is returned to the caller; and a FIN rather than an RST is sent to the TCP peer. For a detailed description of all the problems with the old version of shutdown, please see the EEP Light that was written to justify this patch. --- erts/emulator/drivers/common/inet_drv.c | 58 +++++++++++++++++++++++++++++++-- erts/preloaded/src/prim_inet.erl | 21 +----------- 2 files changed, 56 insertions(+), 23 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 5196eb51c6..e001f31932 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -293,6 +293,10 @@ static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD); static unsigned long zero_value = 0; static unsigned long one_value = 1; +#define TCP_SHUT_WR SD_SEND +#define TCP_SHUT_RD SD_RECEIVE +#define TCP_SHUT_RDWR SD_BOTH + #elif defined (__OSE__) /* @@ -421,6 +425,10 @@ typedef unsigned long u_long; inet_driver_select((d), (flags), (onoff)); \ } while(0) +#define TCP_SHUT_WR SHUT_WR +#define TCP_SHUT_RD SHUT_RD +#define TCP_SHUT_RDWR SHUT_RDWR + #else /* !__OSE__ && !__WIN32__ */ #include @@ -691,6 +699,9 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) inet_driver_select((d)->port, (ErlDrvEvent)(long)(d)->event, (flags), (onoff)); \ } while(0) +#define TCP_SHUT_WR SHUT_WR +#define TCP_SHUT_RD SHUT_RD +#define TCP_SHUT_RDWR SHUT_RDWR #endif /* !__WIN32__ && !__OSE__ */ @@ -820,6 +831,10 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define TCP_ADDF_CLOSE_SENT 2 /* Close sent (active mode only) */ #define TCP_ADDF_DELAYED_CLOSE_RECV 4 /* If receive fails, report {error,closed} (passive mode) */ #define TCP_ADDF_DELAYED_CLOSE_SEND 8 /* If send fails, report {error,closed} (passive mode) */ +#define TCP_ADDF_PENDING_SHUT_WR 16 /* Call shutdown(sock, SHUT_WR) when queue empties */ +#define TCP_ADDF_PENDING_SHUT_RDWR 32 /* Call shutdown(sock, SHUT_RDWR) when queue empties */ +#define TCP_ADDF_PENDING_SHUTDOWN \ + (TCP_ADDF_PENDING_SHUT_WR | TCP_ADDF_PENDING_SHUT_RDWR) /* *_REQ_* replies */ #define INET_REP_ERROR 0 @@ -1407,6 +1422,8 @@ static int tcp_sendv(tcp_descriptor* desc, ErlIOVec* ev); static int tcp_recv(tcp_descriptor* desc, int request_len); static int tcp_deliver(tcp_descriptor* desc, int len); +static int tcp_shutdown_error(tcp_descriptor* desc, int err); + static int tcp_inet_output(tcp_descriptor* desc, HANDLE event); static int tcp_inet_input(tcp_descriptor* desc, HANDLE event); @@ -9473,10 +9490,18 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_error(EINVAL, rbuf, rsize); } how = buf[0]; - if (sock_shutdown(INETP(desc)->s, how) == 0) { + if (how != TCP_SHUT_RD && driver_sizeq(desc->inet.port) > 0) { + if (how == TCP_SHUT_WR) { + desc->tcp_add_flags |= TCP_ADDF_PENDING_SHUT_WR; + } else if (how == TCP_SHUT_RDWR) { + desc->tcp_add_flags |= TCP_ADDF_PENDING_SHUT_RDWR; + } return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); - } else { + } + if (IS_SOCKET_ERROR(sock_shutdown(INETP(desc)->s, how))) { return ctl_error(sock_errno(), rbuf, rsize); + } else { + return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } } default: @@ -9613,6 +9638,8 @@ static void tcp_inet_commandv(ErlDrvData e, ErlIOVec* ev) else inet_reply_error(INETP(desc), ENOTCONN); } + else if (desc->tcp_add_flags & TCP_ADDF_PENDING_SHUTDOWN) + tcp_shutdown_error(desc, EPIPE); else if (tcp_sendv(desc, ev) == 0) inet_reply_ok(INETP(desc)); DEBUGF(("tcp_inet_commandv(%ld) }\r\n", (long)desc->inet.port)); @@ -10506,7 +10533,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) return ret; } -static int tcp_send_error(tcp_descriptor* desc, int err) +static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err) { /* * If the port is busy, we must do some clean-up before proceeding. @@ -10563,6 +10590,16 @@ static int tcp_send_error(tcp_descriptor* desc, int err) return -1; } +static int tcp_send_error(tcp_descriptor* desc, int err) +{ + return tcp_send_or_shutdown_error(desc, err); +} + +static int tcp_shutdown_error(tcp_descriptor* desc, int err) +{ + return tcp_send_or_shutdown_error(desc, err); +} + /* ** Send non-blocking vector data */ @@ -10763,6 +10800,19 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len) return 0; } +/* shutdown on the socket: +** Assume caller has confirmed TCP_ADDF_PENDING_SHUTDOWN is set. +*/ +static void tcp_shutdown_async(tcp_descriptor* desc) +{ + int how; + + how = (desc->tcp_add_flags & TCP_ADDF_PENDING_SHUT_WR) ? + TCP_SHUT_WR : TCP_SHUT_RDWR; + if (IS_SOCKET_ERROR(sock_shutdown(INETP(desc)->s, how))) + tcp_shutdown_error(desc, sock_errno()); +} + #ifdef __OSE__ static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event) @@ -10891,6 +10941,8 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) if ((iov = driver_peekq(ix, &vsize)) == NULL) { sock_select(INETP(desc), FD_WRITE, 0); send_empty_out_q_msgs(INETP(desc)); + if (desc->tcp_add_flags & TCP_ADDF_PENDING_SHUTDOWN) + tcp_shutdown_async(desc); goto done; } vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize; diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 79ff013c77..622e1be869 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -127,37 +127,18 @@ drv2protocol(_) -> undefined. %% TODO: shutdown equivalent for SCTP %% shutdown(S, read) when is_port(S) -> - shutdown_2(S, 0); + shutdown_1(S, 0); shutdown(S, write) when is_port(S) -> shutdown_1(S, 1); shutdown(S, read_write) when is_port(S) -> shutdown_1(S, 2). shutdown_1(S, How) -> - case subscribe(S, [subs_empty_out_q]) of - {ok,[{subs_empty_out_q,N}]} when N > 0 -> - shutdown_pend_loop(S, N); %% wait for pending output to be sent - _Other -> ok - end, - shutdown_2(S, How). - -shutdown_2(S, How) -> case ctl_cmd(S, ?TCP_REQ_SHUTDOWN, [How]) of {ok, []} -> ok; {error,_}=Error -> Error end. -shutdown_pend_loop(S, N0) -> - receive - {empty_out_q,S} -> ok - after ?INET_CLOSE_TIMEOUT -> - case getstat(S, [send_pend]) of - {ok,[{send_pend,N0}]} -> ok; - {ok,[{send_pend,N}]} -> shutdown_pend_loop(S, N); - _ -> ok - end - end. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% CLOSE(insock()) -> ok -- cgit v1.2.3 From cce681109c05ffa8cb88e83eb70ccf73c13d0c75 Mon Sep 17 00:00:00 2001 From: Jesper Louis Andersen Date: Tue, 12 May 2015 12:44:14 +0200 Subject: Correct usage of sizeof() for pointer types Given some pointer *x, calling sizeof(x) will give us the size of the pointer (4/8 bytes) not the size fo the underlying dereferenced structure. Use coccinelle to search for these occurrences in the source code, and correct them one by one. In the case of erl_node_tables.c, the erts_snprintf() calls used a much too small buffer. - run_erl.c: Use the size of the signal type, not its pointer. - erl_node_tables.c: Use the size of the _BUFFER in erts_snprintf() to make sure we can use the full space. --- erts/emulator/beam/erl_node_tables.c | 4 ++-- erts/etc/ose/run_erl.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index c6d136f951..bc775cd663 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -705,7 +705,7 @@ erts_set_this_node(Eterm sysname, Uint creation) erts_this_node->sysname = sysname; erts_this_node->creation = creation; erts_this_node_sysname = erts_this_node_sysname_BUFFER; - erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname), + erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname_BUFFER), "%T", sysname); (void) hash_put(&erts_node_table, (void *) erts_this_node); @@ -794,7 +794,7 @@ void erts_init_node_tables(void) erts_this_node->creation = 0; erts_this_node->dist_entry = erts_this_dist_entry; erts_this_node_sysname = erts_this_node_sysname_BUFFER; - erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname), + erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname_BUFFER), "%T", erts_this_node->sysname); (void) hash_put(&erts_node_table, (void *) erts_this_node); diff --git a/erts/etc/ose/run_erl.c b/erts/etc/ose/run_erl.c index 8bc49a485e..a6499f2bf3 100644 --- a/erts/etc/ose/run_erl.c +++ b/erts/etc/ose/run_erl.c @@ -615,7 +615,7 @@ int run_erl(int argc,char **argv) { returns */ PROCESS main_pid; hunt_in_block("run_erl","main",&main_pid); - sig = alloc(sizeof(sig),ERTS_SIGNAL_RUN_ERL_DAEMON); + sig = alloc(sizeof(*sig),ERTS_SIGNAL_RUN_ERL_DAEMON); send(&sig,main_pid); sig = receive(sigsel); pid = sender(&sig); -- cgit v1.2.3 From 9a81b28598fadc44bf506354c9227e41aac786f6 Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Wed, 13 May 2015 09:40:16 +0200 Subject: Revert "Prepare release" This reverts commit e09dd66dc4d89c62ddfd8c19791f9678d5d787c6. --- erts/doc/src/notes.xml | 502 ------------------------------------------------- 1 file changed, 502 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index c85cbe543d..35e6e55e72 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,508 +30,6 @@

This document describes the changes made to the ERTS application.

-
Erts 7.0 - -
Fixed Bugs and Malfunctions - - -

- Fix issuing with spaces and quoting in the arguments when - using erlang:open_port spawn_executable on windows. The - behavior now mimics how unix works. This change implies a - backwards incompatibility for how spawn_executable works - on windows.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-11905

-
- -

- Fix global call trace when hipe compiled code call beam - compiled functions. Tracing of beam functions should now - alway work regardless who the caller is.

-

- Own Id: OTP-11939

-
- -

- Correct cache alignment for ETS write_concurrency - locks to improve performance by reduced false sharing. - May increase memory footprint for tables with - write_concurrency.

-

- Own Id: OTP-11974

-
- -

- All possibly blocking operations in the fd/spawn and - terminal driver have been converted to non-blocking - operations. Before this fix it was possible for the VM to - be blocked for a long time if the entity consuming - stdout/stderr did not consume it fast enough.

-

- Own Id: OTP-12239

-
- -

- Add missing overhead for offheap binaries created from - external format. This fix can improve the garbage - collection of large binaries originating from - binary_to_term or messages from remote nodes.

-

- Own Id: OTP-12554

-
- -

- Ensure hashing of zero is consistent

-

Erlang treats positive and negative zero as - equal:

-

- true = 0.0 =:= 0.0/-1

-

However, Erlangs hash functions: hash, phash and - phash2 did not reflect this behaviour. The hash values - produced by the different hash functions would not be - identical for positive and negative zero.

This - change ensures that hash value of positive zero is always - produced regardless of the signedness of the zero float, - i.e.,

-

- true = erlang:phash2(0.0) =:= - erlang:phash2(0.0/-1)

-

- Own Id: OTP-12641

-
- -

- Ensure NIF term creation disallows illegal floating point - values and too long atoms. Such values will cause a NIF - to throw badarg exception when it returns.

-

- Own Id: OTP-12655

-
- -

- Fixed building of Map results from match_specs

-

- A faulty "box-value" entered into the heap which could - cause a segmentation fault in the garbage collector if it - was written on a heap fragment.

-

- Own Id: OTP-12656

-
- -

- Fix hipe bug when matching a "writable" binary. The bug - has been seen to sometimes cause a failed binary matching - of a correct utf8 character, but other symptoms are also - possible.

-

- Own Id: OTP-12667

-
- -

- Keep dirty schedulers from waking other schedulers.

-

- Own Id: OTP-12685

-
- -

- Disable floating point exceptions if the VM is compiled - by clang/llvm. This is a known long-standing problem in - clang/llvm.

-

- Own Id: OTP-12717

-
- -

- Fix bug in file:sendfile for FreeBSD causing not - the entire file to be sent.

-

- Own Id: OTP-12720

-
-
-
- - -
Improvements and New Features - - -

- Add md5 and module entries to - ?MODULE:module_info/0/1 and remove obsolete entry - 'import'.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-11940

-
- -

- Debug function erlang:display/1 shows content of - binaries and bitstrings, not only the length.

-

- Own Id: OTP-11941

-
- -

The time functionality of Erlang has been extended. - This both includes a new - API for time, as well as time warp - modes which alters the behavior of the system - when system time changes. You are strongly encouraged - to use the new API instead of the old API based on - erlang:now/0. - erlang:now/0 has been deprecated since it is and - forever will be a scalability bottleneck. For more - information see the Time and Time - Correction chapter of the ERTS User's - Guide.

-

Besides the API changes and time warp modes a lot of - scalability and performance improvements regarding time - management has been made internally in the runtime - system. Examples of such improvements are scheduler - specific timer wheels, scheduler specific BIF timer - management, parallel retrieval of monotonic time and - system time on systems with primitives that are not - buggy.

-

- Own Id: OTP-11997

-
- -

erlang:function_exported(M, F, A) will now - return true if M:F/A refers to a BIF.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-12099

-
- -

- New BIF: erlang:get_keys/0, lists all keys - associated with the process dictionary. Note: - erlang:get_keys/0 is auto-imported.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-12151 Aux Id: seq12521

-
- -

- Make distributed send of large messages yield to improve - real-time characteristics.

-

- Own Id: OTP-12232

-
- -

- Use high accuracy poll timeouts

-

- Where available, use poll/select API's that can handle - time resolutions less than 1ms. In the cases where such - API's are not available the timeout is rounded up to the - nearest ms.

-

- Own Id: OTP-12236

-
- -

- The internal group to user_drv protocol has been changed - to be synchronous in order to guarantee that output sent - to a process implementing the user_drv protocol is - printed before replying. This protocol is used by the - standard_output device and the ssh application when - acting as a client.

-

- This change changes the previous unlimited buffer when - printing to standard_io and other devices that end up in - user_drv to 1KB.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-12240

-
- -

The previously introduced "eager check I/O" feature is - now enabled by default.

-

Eager check I/O can be disabled using the erl - command line argument: +secio false

-

Characteristics impact compared to previous - default:

Lower latency and smoother - management of externally triggered I/O operations. - A slightly reduced priority of externally triggered - I/O operations. -

- Own Id: OTP-12254 Aux Id: OTP-12117

-
- -

- Properly support maps in match_specs

-

- Own Id: OTP-12270

-
- -

- The notice that a crashdump has been written has been - moved to be printed before the crashdump is generated - instead of afterwords. The wording of the notice has also - been changed.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-12292

-
- -

- New function ets:take/2. Works the same as - ets:delete/2 but also returns the deleted - object(s).

-

- Own Id: OTP-12309

-
- -

- Tracing with cpu_timestamp option has been enabled on - Linux.

-

- Own Id: OTP-12366

-
- -

- ets:info/1,2 now contains information about whether - write_concurrency or read_concurrency is enabled.

-

- Own Id: OTP-12376

-
- -

- Improved usage of gcc's builtins for atomic memory - access. These are used when no other implementation of - atomic memory operations is available. For example, when - compiling for ARM when libatomic_ops is not - available.

-

- The largest improvement will be seen when compiling with - a gcc with support for the __atomic_* - builtins (using a gcc of at least version 4.7), - but also when only the legacy __sync_* builtins - are available (using a gcc of at least version - 4.1) an improvement can be seen.

-

- For more information see the "Atomic - Memory Operations and the VM" section of - $ERL_TOP/HOWTO/INSTALL.md.

-

- Own Id: OTP-12383

-
- -

- Introduce math:log2/1 function to math module.

-

- Own Id: OTP-12411

-
- -

- Remove perfctr support

-

- Development of perfctr in the linux kernel ceased in - 2010. The perfctr support code in the Erlang VM is thus - effectively dead code and therefor removed.

-

- Own Id: OTP-12508

-
- -

zlib:inflateChunk/2 has been added. It works - like zlib:inflate/2, but decompresses no more data - than will fit in the buffer configured by - zlib:setBufSize/2.

-

- Own Id: OTP-12548

-
- -

- Use linear search for small select_val arrays

-

- Own Id: OTP-12555

-
- -

- New BIF ets:update_counter/4 with a default object as - argument, which will be inserted in the table if the key - was not found.

-

- Own Id: OTP-12563

-
- -

- Export missing types from zlib module

-

- Own Id: OTP-12584

-
- -

- Use persistent hashmaps for large Maps

Maps will use a - persistent hashmap implementation when the number of - pairs in a Map becomes sufficiently large. The change - will occur when a Map reaches 33 pairs in size but this - limit might change in the future.

-

The most significant impact for the user by this - change is speed, and to a lesser degree memory - consumption and introspection of Maps. Memory consumption - size is probalistic but lesser than gb_trees or - dict for instance. Any other impacts will be - transparent for the user except for the following - changes.

-

Semantics of Maps have changed in two incompatible - ways compared to the experimental implementation in OTP - 17:

Hashing of maps is done different by - erlang:phash2/1,2, erlang:phash/1 and - erlang:hash/2. Comparing two maps - with ==, /=, =<, <, >= and >, is done - different if the keys contain floating point - numbers. -

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-12585

-
- -

- Scalability improvement for erlang:make_ref/0, - and other functionality that create references. Each - scheduler now manage its own set of references. By this - no communication at all is needed when creating - references.

-

- Previous implementation generated a strictly - monotonically increasing sequence of references - corresponding to creation time on the runtime system - instance. This is not the case with current - implementation. You can only expect reference to be - unique. The Erlang/OTP documentation has never mentioned - anything else but the uniqueness property, so this change - is fully compatible. The only reason we've - marked this as a potential incompatibility is since an - early draft for an Erlang specification mentions strict - monotonicity as a property.

-

- If you need to create data with a strict monotonicity - property use erlang:unique_integer([monotonic]). - Do not use the deprecated erlang:now().

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-12610

-
- -

- Enable different abort signal from heart

-

By using environment variable HEART_KILL_SIGNAL, heart - can now use a different signal to kill the old running - Erlang.

-

By default the signal is SIGKILL but SIGABRT may also - be used by setting environment variable: - HEART_KILL_SIGNAL=SIGABRT

-

- Own Id: OTP-12613 Aux Id: seq12826

-
- -

- Update autconf to latest version 2015-03-04

-

- Own Id: OTP-12646

-
- -

- Optimization of timers internally in the VM. This include - process timers (receive ... after), port timers - (driver_set_timer()) as well as BIF timers - (erlang:send_after()/erlang:start_timer()).

-

- Each scheduler thread now has its own lock-free timer - service instead of one locked central service. This - dramatically improves performance of timer management on - systems with a large amount of schedulers and timers.

-

- The timer service internal data structure has also been - optimized to be able to handle more timers than before. - That is, each timer service is by its self able to handle - more timers without dramatic performance loss than the - old centralized timer service.

-

- The API of BIF timers has also been extended. Timeout - values are for example no longer limited to 32-bit - integers. For more information see the documentation of - erlang:start_timer/4, - erlang:send_after/4, - erlang:cancel_timer/2, - and erlang:read_timer/2.

-

- Own Id: OTP-12650 Aux Id: OTP-11997

-
- -

- Specialize instructions from common assembler patterns

-

Specialize common instructions of rem, - band, minus and plus in the beam - loader. This will reduce the number of fetches and thus - lessen the instruction dispatch pressure during runtime - and speed up those operations in some common cases.

-

Specialize move patterns from x-registers to the stack - with a new move_window instruction. This change - will reduce instruction dispatch pressure.

-

- Own Id: OTP-12690

-
- -

- Fix cross compilation for Android.

-

- Own Id: OTP-12693

-
- -

- Fix incorrect use of autoconf macro AC_EGREP_CPP, which - could cause faulty configuration if run from a path - containing the string 'yes'.

-

- Own Id: OTP-12706

-
- -

- Minimal Java version is now 1.6

-

- Own Id: OTP-12718

-
- -

- Send format and args on process exit to error_logger

-

- Previously, the emulator would generate a whole string - with values and call the error_logger passing - "~s~n". This changes it to a format string - containing ~p with the respective values as - arguments.

-

- Own Id: OTP-12735

-
-
-
- -
-
Erts 6.4.1
Fixed Bugs and Malfunctions -- cgit v1.2.3 From 4c4d7fa40e5fb59854724ce74b8aa3546525cb90 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Fri, 17 Apr 2015 22:04:31 +0200 Subject: Map error logger warnings to warning messages by default Also fix and document the broken +We option. --- erts/doc/src/erl.xml | 9 +++++---- erts/emulator/beam/erl_init.c | 9 +++++---- erts/etc/common/erlexec.c | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index ea94a4e82b..98d05dc7de 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1322,13 +1322,14 @@

Verbose.

- +

Sets the mapping of warning messages for . Messages sent to the error logger using one of the warning - routines can be mapped either to errors (default), warnings - (), or info reports (). The current - mapping can be retrieved using + routines can be mapped either to errors (), + warnings (), or info reports + (). The default is warnings. + The current mapping can be retrieved using . See error_logger(3) for further information.

diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 988ff0e2b5..33417833a9 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -625,7 +625,7 @@ void erts_usage(void) erts_fprintf(stderr, "-v turn on chatty mode (GCs will be reported etc)\n"); - erts_fprintf(stderr, "-W set error logger warnings mapping,\n"); + erts_fprintf(stderr, "-W set error logger warnings mapping,\n"); erts_fprintf(stderr, " see error_logger documentation for details\n"); erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n"); erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024); @@ -1253,7 +1253,7 @@ erl_start(int argc, char **argv) verbose = DEBUG_DEFAULT; #endif - erts_error_logger_warnings = am_error; + erts_error_logger_warnings = am_warning; while (i < argc) { if (argv[i][0] != '-') { @@ -1991,11 +1991,12 @@ erl_start(int argc, char **argv) case 'i': erts_error_logger_warnings = am_info; break; + case 'e': + erts_error_logger_warnings = am_error; + break; case 'w': erts_error_logger_warnings = am_warning; break; - case 'e': /* The default */ - erts_error_logger_warnings = am_error; default: erts_fprintf(stderr, "unrecognized warning_map option %s\n", arg); erts_usage(); diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 23226909a7..d6544a2829 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -1172,7 +1172,7 @@ usage_aux(void) "[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] " "[+SP PERCENTAGE_SCHEDULERS:PERCENTAGE_SCHEDULERS_ONLINE] " "[+T LEVEL] [+V] [+v] " - "[+W] [+z MISC_OPTION] [args ...]\n"); + "[+W] [+z MISC_OPTION] [args ...]\n"); exit(1); } -- cgit v1.2.3 From d909a5c2cad677e3c6accee9ef1515584d2f4be5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 29 Apr 2015 18:35:23 +0200 Subject: erts: Fix typo in etp-carrier-blocks --- erts/etc/unix/etp-commands.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 3ee092418e..c51b9e94ed 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -3634,7 +3634,7 @@ define etp-carrier-blocks printf "Free blocks: %u\n", $etp_fblk_cnt end if $etp_error_cnt - printf "%u ERRORs reported above\n", $etp-error-cnt + printf "%u ERRORs reported above\n", $etp_error_cnt end end -- cgit v1.2.3 From b7c9657a141dd002a08256ba272cb125fec2b16d Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Sun, 17 May 2015 17:37:13 -0400 Subject: Avoid timer wheel work in dirty schedulers Dirty schedulers are limited in the type of work they can perform. Make sure they do not participate in any work or make any function calls related to timer wheels. --- erts/emulator/beam/erl_process.c | 149 ++++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 57 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index af8db519d4..c5bfcd3fb4 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2887,22 +2887,29 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) } if (aux_work) { - flgs = erts_smp_atomic32_read_acqb(&ssi->flags); - current_time = erts_get_monotonic_time(esdp); - if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); - sched_wall_time_change(esdp, 1); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); + current_time = erts_get_monotonic_time(esdp); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + erts_bump_timers(esdp->timer_wheel, current_time); } - erts_bump_timers(esdp->timer_wheel, current_time); } } else { ErtsMonotonicTime timeout_time; - timeout_time = erts_check_next_timeout_time(esdp); - current_time = erts_get_monotonic_time(esdp); - if (current_time >= timeout_time) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { + int do_timeout = 0; + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + timeout_time = erts_check_next_timeout_time(esdp); + current_time = erts_get_monotonic_time(esdp); + do_timeout = (current_time >= timeout_time); + } else + timeout_time = ERTS_MONOTONIC_TIME_MAX; + if (do_timeout) { + if (!thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } @@ -2926,23 +2933,28 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) int res; ASSERT(flgs & ERTS_SSI_FLG_TSE_SLEEPING); ASSERT(flgs & ERTS_SSI_FLG_WAITING); - current_time = erts_get_monotonic_time(esdp); + current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : + erts_get_monotonic_time(esdp); do { Sint64 timeout; if (current_time >= timeout_time) break; - timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time - - current_time - - 1) + 1; + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time + - current_time + - 1) + 1; + } else + timeout = -1; res = erts_tse_twait(ssi->event, timeout); - current_time = erts_get_monotonic_time(esdp); + current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : + erts_get_monotonic_time(esdp); } while (res == EINTR); } } if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) erts_thr_progress_finalize_wait(esdp); } - if (current_time >= timeout_time) + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && current_time >= timeout_time) erts_bump_timers(esdp->timer_wheel, current_time); } @@ -3010,9 +3022,11 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ASSERT(!erts_port_task_have_outstanding_io_tasks()); erl_sys_schedule(1); /* Might give us something to do */ - current_time = erts_get_monotonic_time(esdp); - if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) - erts_bump_timers(esdp->timer_wheel, current_time); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + current_time = erts_get_monotonic_time(esdp); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) + erts_bump_timers(esdp->timer_wheel, current_time); + } sys_aux_work: #ifndef ERTS_SMP @@ -3021,15 +3035,18 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work) { - if (!working) - sched_wall_time_change(esdp, working = 1); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (!working) + sched_wall_time_change(esdp, working = 1); #ifdef ERTS_SMP - if (!thr_prgr_active) - erts_thr_progress_active(esdp, thr_prgr_active = 1); + if (!thr_prgr_active) + erts_thr_progress_active(esdp, thr_prgr_active = 1); #endif + } aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); #ifdef ERTS_SMP - if (aux_work && erts_thr_progress_update(esdp)) + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && aux_work && + erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); #endif } @@ -3127,7 +3144,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erl_sys_schedule(0); - { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { ErtsMonotonicTime current_time = erts_get_monotonic_time(esdp); if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) erts_bump_timers(esdp->timer_wheel, current_time); @@ -6790,7 +6807,8 @@ suspend_scheduler(ErtsSchedulerData *esdp) } } - (void) erts_get_monotonic_time(esdp); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) + (void) erts_get_monotonic_time(esdp); erts_smp_runq_lock(esdp->run_queue); non_empty_runq(esdp->run_queue); @@ -6906,7 +6924,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) & ERTS_RUNQ_FLGS_QMASK); aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work|qmask) { - if (!thr_prgr_active) { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } @@ -6914,7 +6932,8 @@ suspend_scheduler(ErtsSchedulerData *esdp) aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); - if (aux_work && erts_thr_progress_update(esdp)) + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && aux_work && + erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); if (qmask) { erts_smp_runq_lock(esdp->run_queue); @@ -6924,32 +6943,40 @@ suspend_scheduler(ErtsSchedulerData *esdp) } if (aux_work) { - current_time = erts_get_monotonic_time(esdp); - if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { - if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); - sched_wall_time_change(esdp, 1); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + current_time = erts_get_monotonic_time(esdp); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + erts_bump_timers(esdp->timer_wheel, current_time); } - erts_bump_timers(esdp->timer_wheel, current_time); } } else { ErtsMonotonicTime timeout_time; - timeout_time = erts_check_next_timeout_time(esdp); - current_time = erts_get_monotonic_time(esdp); - - if (current_time >= timeout_time) { + int do_timeout = 0; + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + timeout_time = erts_check_next_timeout_time(esdp); + current_time = erts_get_monotonic_time(esdp); + do_timeout = (current_time >= timeout_time); + } else + timeout_time = ERTS_MONOTONIC_TIME_MAX; + if (do_timeout) { if (!thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } } - else { - if (thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 0); - sched_wall_time_change(esdp, 0); + else { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 0); + sched_wall_time_change(esdp, 0); + } + erts_thr_progress_prepare_wait(esdp); } - erts_thr_progress_prepare_wait(esdp); flgs = sched_spin_suspended(ssi, ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT); if (flgs == (ERTS_SSI_FLG_SLEEPING @@ -6962,23 +6989,29 @@ suspend_scheduler(ErtsSchedulerData *esdp) | ERTS_SSI_FLG_SUSPENDED)) { int res; - current_time = erts_get_monotonic_time(esdp); + current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : + erts_get_monotonic_time(esdp); do { Sint64 timeout; if (current_time >= timeout_time) break; - timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time - - current_time - - 1) + 1; + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time + - current_time + - 1) + 1; + } else + timeout = -1; res = erts_tse_twait(ssi->event, timeout); - current_time = erts_get_monotonic_time(esdp); + current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : + erts_get_monotonic_time(esdp); } while (res == EINTR); } } - erts_thr_progress_finalize_wait(esdp); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) + erts_thr_progress_finalize_wait(esdp); } - if (current_time >= timeout_time) + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && current_time >= timeout_time) erts_bump_timers(esdp->timer_wheel, current_time); } @@ -9196,13 +9229,15 @@ Process *schedule(Process *p, int calls) ERTS_SMP_CHK_NO_PROC_LOCKS; - if (esdp->check_time_reds >= ERTS_CHECK_TIME_REDS) - (void) erts_get_monotonic_time(esdp); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (esdp->check_time_reds >= ERTS_CHECK_TIME_REDS) + (void) erts_get_monotonic_time(esdp); - if (esdp->last_monotonic_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { - erts_smp_runq_unlock(rq); - erts_bump_timers(esdp->timer_wheel, esdp->last_monotonic_time); - erts_smp_runq_lock(rq); + if (esdp->last_monotonic_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { + erts_smp_runq_unlock(rq); + erts_bump_timers(esdp->timer_wheel, esdp->last_monotonic_time); + erts_smp_runq_lock(rq); + } } BM_STOP_TIMER(system); -- cgit v1.2.3 From e5899f39d043409e2a48f34c845ad28cad8a28e6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 18 May 2015 19:25:09 +0200 Subject: erts: Fix warning about const pointer to make_boxed and make_list --- erts/emulator/beam/erl_term.c | 4 ++-- erts/emulator/beam/erl_term.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_term.c b/erts/emulator/beam/erl_term.c index bc04d7b78e..565528193e 100644 --- a/erts/emulator/beam/erl_term.c +++ b/erts/emulator/beam/erl_term.c @@ -128,10 +128,10 @@ FUNTY checked_##FUN(ARGTY x, const char *file, unsigned line) \ return _unchecked_##FUN(x); \ } -ET_DEFINE_CHECKED(Eterm,make_boxed,Eterm*,_is_taggable_pointer); +ET_DEFINE_CHECKED(Eterm,make_boxed,const Eterm*,_is_taggable_pointer); ET_DEFINE_CHECKED(int,is_boxed,Eterm,!is_header); ET_DEFINE_CHECKED(Eterm*,boxed_val,Wterm,_boxed_precond); -ET_DEFINE_CHECKED(Eterm,make_list,Eterm*,_is_taggable_pointer); +ET_DEFINE_CHECKED(Eterm,make_list,const Eterm*,_is_taggable_pointer); ET_DEFINE_CHECKED(int,is_not_list,Eterm,!is_header); ET_DEFINE_CHECKED(Eterm*,list_val,Wterm,_list_precond); ET_DEFINE_CHECKED(Uint,unsigned_val,Eterm,is_small); diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 602aab46dc..7b15b34da1 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -198,7 +198,7 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #endif #define _is_aligned(x) (((Uint)(x) & 0x3) == 0) #define _unchecked_make_boxed(x) ((Uint) COMPRESS_POINTER(x) + TAG_PRIMARY_BOXED) -_ET_DECLARE_CHECKED(Eterm,make_boxed,Eterm*) +_ET_DECLARE_CHECKED(Eterm,make_boxed,const Eterm*) #define make_boxed(x) _ET_APPLY(make_boxed,(x)) #if 1 #define _is_not_boxed(x) ((x) & (_TAG_PRIMARY_MASK-TAG_PRIMARY_BOXED)) @@ -214,7 +214,7 @@ _ET_DECLARE_CHECKED(Eterm*,boxed_val,Wterm) /* cons cell ("list") access methods */ #define _unchecked_make_list(x) ((Uint) COMPRESS_POINTER(x) + TAG_PRIMARY_LIST) -_ET_DECLARE_CHECKED(Eterm,make_list,Eterm*) +_ET_DECLARE_CHECKED(Eterm,make_list,const Eterm*) #define make_list(x) _ET_APPLY(make_list,(x)) #if 1 #define _unchecked_is_not_list(x) ((x) & (_TAG_PRIMARY_MASK-TAG_PRIMARY_LIST)) -- cgit v1.2.3 From 56c9154f2a0efb8a6d1347b19b797ac17b6608d4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 18 May 2015 19:34:45 +0200 Subject: erts: Fix calculation of reclaimed data during full gc The old code did not take take the old-heap into acount. --- erts/emulator/beam/erl_gc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 1785fc27be..14e7dde778 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1223,7 +1223,8 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) Uint new_sz; Uint fragments = MBUF_SIZE(p) + combined_message_size(p); - size_before = fragments + (HEAP_TOP(p) - HEAP_START(p)); + size_before = fragments + (HEAP_TOP(p) - HEAP_START(p)) + + (OLD_HTOP(p) - OLD_HEAP(p)); /* * Do a fullsweep GC. First figure out the size of the heap -- cgit v1.2.3 From 4034b89a07a97766dba5e6213b1eb4d76ba6df9e Mon Sep 17 00:00:00 2001 From: Zandra Hird Date: Wed, 20 May 2015 15:35:49 +0200 Subject: Revert "Map error logger warnings to warning messages by default" This reverts commit 4c4d7fa40e5fb59854724ce74b8aa3546525cb90. This pr is causing some test failures that were missed at first. --- erts/doc/src/erl.xml | 9 ++++----- erts/emulator/beam/erl_init.c | 9 ++++----- erts/etc/common/erlexec.c | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 98d05dc7de..ea94a4e82b 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1322,14 +1322,13 @@

Verbose.

- +

Sets the mapping of warning messages for . Messages sent to the error logger using one of the warning - routines can be mapped either to errors (), - warnings (), or info reports - (). The default is warnings. - The current mapping can be retrieved using + routines can be mapped either to errors (default), warnings + (), or info reports (). The current + mapping can be retrieved using . See error_logger(3) for further information.

diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 33417833a9..988ff0e2b5 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -625,7 +625,7 @@ void erts_usage(void) erts_fprintf(stderr, "-v turn on chatty mode (GCs will be reported etc)\n"); - erts_fprintf(stderr, "-W set error logger warnings mapping,\n"); + erts_fprintf(stderr, "-W set error logger warnings mapping,\n"); erts_fprintf(stderr, " see error_logger documentation for details\n"); erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n"); erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024); @@ -1253,7 +1253,7 @@ erl_start(int argc, char **argv) verbose = DEBUG_DEFAULT; #endif - erts_error_logger_warnings = am_warning; + erts_error_logger_warnings = am_error; while (i < argc) { if (argv[i][0] != '-') { @@ -1991,12 +1991,11 @@ erl_start(int argc, char **argv) case 'i': erts_error_logger_warnings = am_info; break; - case 'e': - erts_error_logger_warnings = am_error; - break; case 'w': erts_error_logger_warnings = am_warning; break; + case 'e': /* The default */ + erts_error_logger_warnings = am_error; default: erts_fprintf(stderr, "unrecognized warning_map option %s\n", arg); erts_usage(); diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index d6544a2829..23226909a7 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -1172,7 +1172,7 @@ usage_aux(void) "[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] " "[+SP PERCENTAGE_SCHEDULERS:PERCENTAGE_SCHEDULERS_ONLINE] " "[+T LEVEL] [+V] [+v] " - "[+W] [+z MISC_OPTION] [args ...]\n"); + "[+W] [+z MISC_OPTION] [args ...]\n"); exit(1); } -- cgit v1.2.3 From 9c34c2544981ff2df6ddf94b36f4a9db5d6da6b5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 19 May 2015 20:08:36 +0200 Subject: erts: Add maps to send_term_SUITE --- erts/emulator/test/send_term_SUITE.erl | 5 +- .../test/send_term_SUITE_data/ext_terms.bin | Bin 476 -> 914 bytes .../emulator/test/send_term_SUITE_data/ext_terms.h | 126 +++++++++++---------- 3 files changed, 73 insertions(+), 58 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index 8e1f8df43a..4deae63ce7 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -279,7 +279,10 @@ generate_external_terms_files(BaseDir) -> {4444444444444444,-44444, [[[[[[[[[[[5]]]]]]]]]]], make_ref()}, {444444444444444444444,-44444, {{{{{{{{{{{{6}}}}}}}}}}}}, make_ref()}, {444444444444444,-44444, {{{{{{{{{{{{7}}}}}}}}}}}}, make_ref()}, - {4444444444444444444,-44444, {{{{{{{{{{{{8}}}}}}}}}}}}, make_ref()}], + {4444444444444444444,-44444, {{{{{{{{{{{{8}}}}}}}}}}}}, make_ref()}, + #{}, + #{1 => 11, 2 => 22, 3 => 33}, + maps:from_list([{K,K*11} || K <- lists:seq(1,100)])], ok = file:write_file(filename:join([BaseDir, "send_term_SUITE_data", "ext_terms.bin"]), diff --git a/erts/emulator/test/send_term_SUITE_data/ext_terms.bin b/erts/emulator/test/send_term_SUITE_data/ext_terms.bin index b239284323..5ff0b2ccf1 100644 Binary files a/erts/emulator/test/send_term_SUITE_data/ext_terms.bin and b/erts/emulator/test/send_term_SUITE_data/ext_terms.bin differ diff --git a/erts/emulator/test/send_term_SUITE_data/ext_terms.h b/erts/emulator/test/send_term_SUITE_data/ext_terms.h index 08134f3b05..5585585ec3 100644 --- a/erts/emulator/test/send_term_SUITE_data/ext_terms.h +++ b/erts/emulator/test/send_term_SUITE_data/ext_terms.h @@ -24,9 +24,9 @@ #ifndef EXT_TERMS_H__ #define EXT_TERMS_H__ static struct { - unsigned char ext[162]; + unsigned char ext[637]; int ext_size; - unsigned char cext[162]; + unsigned char cext[637]; int cext_size; } ext_terms[] = { {{131,104,3,98,0,0,18,103,98,255,255,237,153,108,0,0,0,2,100,0,7,97,110,95,97,116,111,109,107,0,6,97,32,108,105,115,116,106}, @@ -37,26 +37,26 @@ static struct { 46, {131,108,0,0,0,4,110,9,0,0,0,160,222,197,173,201,53,54,110,7,1,199,113,21,183,140,242,3,107,0,6,98,108,117,112,112,33,100,0,5,98,108,105,112,112,106}, 46}, - {{131,104,5,103,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,0,0,0,38,0,0,0,0,3,104,2,114,0,3,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,40,0,0,0,0,0,0,0,0,102,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,0,0,0,1,3,103,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,0,0,0,37,0,0,0,0,3,102,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,0,0,0,1,3,114,0,3,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,59,0,0,0,0,0,0,0,0}, - 162, - {131,80,0,0,0,161,120,156,203,96,77,79,97,224,77,140,207,203,79,73,117,72,207,47,74,74,76,103,96,96,80,3,98,6,230,12,166,34,6,102,116,89,102,160,140,6,3,20,164,97,209,203,200,12,52,145,39,17,85,80,21,108,96,26,166,4,35,51,216,14,20,97,144,21,214,48,43,0,1,209,36,52}, - 82}, - {{131,104,5,104,0,106,106,112,0,0,0,79,0,21,87,190,182,1,38,106,214,65,228,1,52,27,227,2,212,0,0,0,1,0,0,0,0,100,0,15,115,101,110,100,95,116,101,114,109,95,83,85,73,84,69,97,1,98,0,184,11,180,103,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,0,0,0,37,0,0,0,0,3,109,0,0,0,31,104,101,106,32,104,111,112,112,32,116,114,97,108,108,97,108,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97}, - 123, - {131,80,0,0,0,122,120,156,203,96,205,96,200,202,42,96,96,96,240,103,16,13,223,183,141,81,45,235,154,227,19,70,19,233,199,76,87,128,130,140,64,204,144,194,192,95,156,154,151,18,95,146,90,148,27,31,28,234,25,226,154,200,152,196,176,131,123,75,122,10,3,79,98,94,126,74,170,67,122,126,81,82,98,58,80,173,42,72,3,115,46,144,144,207,72,205,82,200,200,47,40,80,40,41,74,204,201,73,204,73,68,5,0,18,237,35,68}, - 117}, + {{131,104,5,103,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,41,0,0,0,0,2,104,2,114,0,3,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,2,0,0,0,42,0,0,0,3,0,0,0,0,102,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,0,2,103,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,40,0,0,0,0,3,102,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,0,3,114,0,3,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,3,0,0,0,56,0,0,0,3,0,0,0,0}, + 204, + {131,80,0,0,0,203,120,156,203,96,77,79,97,16,73,140,207,203,79,73,117,72,205,169,48,54,201,200,171,52,50,210,53,76,98,96,96,208,4,98,6,166,12,166,34,6,102,28,138,152,128,10,180,128,152,25,164,50,13,183,73,12,76,64,107,132,179,19,115,114,48,229,52,64,242,204,105,56,229,25,152,193,246,99,147,5,89,107,1,179,30,0,103,37,46,144}, + 96}, + {{131,104,5,104,0,106,106,112,0,0,0,86,0,123,56,104,225,98,55,108,63,185,201,160,64,191,31,210,203,0,0,0,2,0,0,0,0,100,0,15,115,101,110,100,95,116,101,114,109,95,83,85,73,84,69,97,2,98,3,217,195,71,103,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,40,0,0,0,0,3,109,0,0,0,31,104,101,106,32,104,111,112,112,32,116,114,97,108,108,97,108,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97}, + 130, + {131,80,0,0,0,129,120,156,203,96,205,96,200,202,42,96,96,96,8,99,168,182,200,120,152,100,158,99,191,243,228,2,135,253,242,151,78,3,5,153,128,152,33,133,129,191,56,53,47,37,190,36,181,40,55,62,56,212,51,196,53,145,41,137,249,230,97,247,244,20,6,225,236,196,156,156,84,135,212,156,10,99,147,140,188,74,35,35,93,195,36,160,22,13,144,62,230,92,32,33,159,145,154,165,144,145,95,80,160,80,82,4,84,154,152,147,136,10,0,219,221,39,33}, + 123}, {{131,108,0,0,0,4,110,10,0,28,199,113,166,118,185,145,86,105,9,110,5,1,28,103,24,89,10,107,0,2,98,33,100,0,10,98,108,105,112,112,112,112,112,112,112,106}, 46, {131,108,0,0,0,4,110,10,0,28,199,113,166,118,185,145,86,105,9,110,5,1,28,103,24,89,10,107,0,2,98,33,100,0,10,98,108,105,112,112,112,112,112,112,112,106}, 46}, - {{131,104,5,98,0,0,18,103,103,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,0,0,0,38,0,0,0,0,3,104,2,114,0,3,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,40,0,0,0,0,0,0,0,0,102,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,0,0,0,1,3,98,255,255,237,153,108,0,0,0,2,100,0,7,97,110,95,97,116,111,109,107,0,6,97,32,108,105,115,116,106}, - 120, - {131,80,0,0,0,119,120,156,203,96,77,98,96,16,74,79,79,97,224,77,140,207,203,79,73,117,72,207,47,74,74,76,103,96,96,80,3,98,6,230,12,166,34,6,102,116,89,102,160,140,6,3,20,164,97,209,203,200,156,244,255,255,219,153,57,64,38,83,10,3,123,98,94,124,98,73,126,110,54,3,91,162,66,78,102,113,73,22,0,167,192,30,158}, - 93}, - {{131,104,4,103,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,0,0,0,38,0,0,0,0,3,104,2,114,0,3,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,40,0,0,0,0,0,0,0,0,102,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,0,0,0,1,3,103,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,0,0,0,0,0,0,0,0,3,102,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,0,0,0,1,3}, - 131, - {131,80,0,0,0,130,120,156,203,96,73,79,97,224,77,140,207,203,79,73,117,72,207,47,74,74,76,103,96,96,80,3,98,6,230,12,166,34,6,102,116,89,102,160,140,6,3,20,164,97,209,203,200,12,52,145,39,17,85,16,12,152,211,48,37,24,153,1,215,214,30,50}, - 72}, + {{131,104,5,98,0,0,18,103,103,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,41,0,0,0,0,2,104,2,114,0,3,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,2,0,0,0,42,0,0,0,3,0,0,0,0,102,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,0,2,98,255,255,237,153,108,0,0,0,2,100,0,7,97,110,95,97,116,111,109,107,0,6,97,32,108,105,115,116,106}, + 141, + {131,80,0,0,0,140,120,156,203,96,77,98,96,16,74,79,79,97,16,73,140,207,203,79,73,117,72,205,169,48,54,201,200,171,52,50,210,53,4,202,49,104,2,49,3,83,6,83,17,3,51,14,69,76,64,5,90,64,204,12,82,153,134,219,36,6,166,164,255,255,223,206,204,1,177,82,24,216,19,243,226,19,75,242,115,179,25,216,18,21,114,50,139,75,178,0,77,99,35,202}, + 100}, + {{131,104,4,103,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,41,0,0,0,0,2,104,2,114,0,3,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,2,0,0,0,42,0,0,0,3,0,0,0,0,102,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,0,2,103,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,0,0,0,0,0,3,102,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,0,3}, + 166, + {131,80,0,0,0,165,120,156,203,96,73,79,97,16,73,140,207,203,79,73,117,72,205,169,48,54,201,200,171,52,50,210,53,76,98,96,96,208,4,98,6,166,12,166,34,6,102,28,138,152,128,10,180,128,152,25,164,50,13,183,73,12,76,64,107,132,179,19,115,114,176,200,129,0,115,26,110,121,102,0,219,33,38,209}, + 84}, {{131,104,3,98,0,0,18,103,98,255,255,237,153,108,0,0,0,2,100,0,7,97,110,95,97,116,111,109,107,0,6,97,32,108,105,115,116,106}, 38, {131,104,3,98,0,0,18,103,98,255,255,237,153,108,0,0,0,2,100,0,7,97,110,95,97,116,111,109,107,0,6,97,32,108,105,115,116,106}, @@ -65,46 +65,58 @@ static struct { 33, {131,104,3,98,0,0,18,103,98,255,255,237,153,108,0,0,0,2,100,0,4,97,116,111,109,107,0,4,108,105,115,116,106}, 33}, - {{131,104,4,103,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,0,0,0,38,0,0,0,0,3,104,2,114,0,3,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,40,0,0,0,0,0,0,0,0,102,100,0,13,97,95,110,111,100,101,64,103,111,114,98,97,103,0,0,0,1,3,103,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,0,0,0,0,0,0,0,0,3,102,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,0,0,0,1,3}, - 131, - {131,80,0,0,0,130,120,156,203,96,73,79,97,224,77,140,207,203,79,73,117,72,207,47,74,74,76,103,96,96,80,3,98,6,230,12,166,34,6,102,116,89,102,160,140,6,3,20,164,97,209,203,200,12,52,145,39,17,85,16,12,152,211,48,37,24,153,1,215,214,30,50}, + {{131,104,4,103,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,41,0,0,0,0,2,104,2,114,0,3,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,2,0,0,0,42,0,0,0,3,0,0,0,0,102,100,0,20,97,95,110,111,100,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,0,2,103,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,0,0,0,0,0,3,102,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,0,0,0,0,3}, + 166, + {131,80,0,0,0,165,120,156,203,96,73,79,97,16,73,140,207,203,79,73,117,72,205,169,48,54,201,200,171,52,50,210,53,76,98,96,96,208,4,98,6,166,12,166,34,6,102,28,138,152,128,10,180,128,152,25,164,50,13,183,73,12,76,64,107,132,179,19,115,114,176,200,129,0,115,26,110,121,102,0,219,33,38,209}, + 84}, + {{131,104,4,110,8,0,28,199,17,175,172,214,173,61,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,0,114,0,3,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,3,0,0,0,57,0,0,0,3,0,0,0,0}, + 81, + {131,80,0,0,0,80,120,156,203,96,201,227,96,144,57,46,184,126,205,181,181,182,73,255,255,7,165,100,48,98,133,12,69,12,204,41,12,194,217,137,57,57,169,14,169,57,21,198,38,25,121,149,70,70,186,134,73,204,12,12,12,150,64,12,162,25,0,231,161,20,138}, + 71}, + {{131,104,4,110,9,0,28,199,241,98,116,219,231,23,24,98,255,255,82,100,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,107,0,1,1,106,106,106,106,106,106,106,106,106,106,114,0,3,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,3,0,0,0,58,0,0,0,3,0,0,0,0}, + 122, + {131,80,0,0,0,121,120,156,203,96,201,227,100,144,57,254,49,169,228,246,115,113,137,164,255,255,131,82,114,24,24,24,24,73,34,178,25,24,25,179,224,160,136,129,57,133,65,56,59,49,39,39,213,33,53,167,194,216,36,35,175,210,200,72,215,48,137,25,168,214,10,136,65,52,3,0,142,142,25,0}, + 80}, + {{131,104,4,110,8,0,28,199,129,17,222,251,42,6,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,2,114,0,3,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,3,0,0,0,59,0,0,0,3,0,0,0,0}, + 83, + {131,80,0,0,0,82,120,156,203,96,201,227,96,144,57,222,40,120,239,183,22,91,210,255,255,65,41,25,140,216,97,34,83,17,3,115,10,131,112,118,98,78,78,170,67,106,78,133,177,73,70,94,165,145,145,174,97,18,51,3,3,131,53,16,131,104,6,0,233,167,20,95}, 72}, - {{131,104,4,110,8,0,28,199,17,175,172,214,173,61,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,0,114,0,3,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,60,0,0,0,0,0,0,0,0}, - 74, - {131,80,0,0,0,73,120,156,203,96,201,227,96,144,57,46,184,126,205,181,181,182,73,255,255,7,165,100,48,98,133,12,69,12,204,41,12,60,137,121,249,41,169,14,233,249,69,73,137,233,204,12,12,12,54,12,80,0,0,73,17,18,208}, - 63}, - {{131,104,4,110,9,0,28,199,241,98,116,219,231,23,24,98,255,255,82,100,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,107,0,1,1,106,106,106,106,106,106,106,106,106,106,114,0,3,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,61,0,0,0,0,0,0,0,0}, - 115, - {131,80,0,0,0,114,120,156,203,96,201,227,100,144,57,254,49,169,228,246,115,113,137,164,255,255,131,82,114,24,24,24,24,73,34,178,25,24,25,179,224,160,136,129,57,133,129,39,49,47,63,37,213,33,61,191,40,41,49,157,25,168,200,150,1,10,0,208,188,23,70}, + {{131,104,4,110,9,0,28,199,113,221,139,146,14,239,240,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,3,114,0,3,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,3,0,0,0,60,0,0,0,3,0,0,0,0}, + 84, + {131,80,0,0,0,83,120,156,203,96,201,227,100,144,57,94,120,183,123,18,223,251,15,73,255,255,7,165,100,48,98,135,137,204,69,12,204,41,12,194,217,137,57,57,169,14,169,57,21,198,38,25,121,149,70,70,186,134,73,204,12,12,12,54,64,12,162,25,0,106,11,22,31}, + 73}, + {{131,104,4,110,9,0,28,199,177,214,190,98,202,104,2,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,4,114,0,3,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,3,0,0,0,61,0,0,0,3,0,0,0,0}, + 84, + {131,80,0,0,0,83,120,156,203,96,201,227,100,144,57,190,241,218,190,164,83,25,76,73,255,255,7,165,100,48,98,135,137,44,69,12,204,41,12,194,217,137,57,57,169,14,169,57,21,198,38,25,121,149,70,70,186,134,73,204,12,12,12,182,64,12,162,25,0,74,151,21,164}, + 73}, + {{131,104,4,110,7,0,28,199,85,220,50,202,15,98,255,255,82,100,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,107,0,1,5,106,106,106,106,106,106,106,106,106,106,114,0,3,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,3,0,0,0,62,0,0,0,3,0,0,0,0}, + 120, + {131,80,0,0,0,119,120,156,203,96,201,99,103,144,57,30,122,199,232,20,127,210,255,255,65,41,57,12,12,12,140,36,17,217,12,140,172,89,112,80,196,192,156,194,32,156,157,152,147,147,234,144,154,83,97,108,146,145,87,105,100,164,107,152,196,12,84,107,7,196,32,154,1,0,225,225,23,138}, + 78}, + {{131,104,4,110,9,0,28,199,241,98,116,219,231,23,24,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,6,114,0,3,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,3,0,0,0,63,0,0,0,3,0,0,0,0}, + 84, + {131,80,0,0,0,83,120,156,203,96,201,227,100,144,57,254,49,169,228,246,115,113,137,164,255,255,131,82,50,24,177,195,68,182,34,6,230,20,6,225,236,196,156,156,84,135,212,156,10,99,147,140,188,74,35,35,93,195,36,102,6,6,6,123,32,6,209,12,0,64,205,21,133}, + 73}, + {{131,104,4,110,7,0,28,199,59,73,56,148,1,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,7,114,0,3,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,3,0,0,0,64,0,0,0,3,0,0,0,0}, + 82, + {131,80,0,0,0,81,120,156,203,96,201,99,103,144,57,110,237,105,49,133,49,233,255,255,160,148,12,70,236,48,145,189,136,129,57,133,65,56,59,49,39,39,213,33,53,167,194,216,36,35,175,210,200,72,215,48,137,153,129,129,193,1,136,65,52,3,0,137,143,19,30}, 71}, - {{131,104,4,110,8,0,28,199,129,17,222,251,42,6,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,2,114,0,3,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,62,0,0,0,0,0,0,0,0}, - 76, - {131,80,0,0,0,75,120,156,203,96,201,227,96,144,57,222,40,120,239,183,22,91,210,255,255,65,41,25,140,216,97,34,83,17,3,115,10,3,79,98,94,126,74,170,67,122,126,81,82,98,58,51,3,3,131,29,3,20,0,0,76,82,18,165}, - 64}, - {{131,104,4,110,9,0,28,199,113,221,139,146,14,239,240,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,3,114,0,3,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,63,0,0,0,0,0,0,0,0}, - 77, - {131,80,0,0,0,76,120,156,203,96,201,227,100,144,57,94,120,183,123,18,223,251,15,73,255,255,7,165,100,48,98,135,137,204,69,12,204,41,12,60,137,121,249,41,169,14,233,249,69,73,137,233,204,12,12,12,246,12,80,0,0,192,110,20,101}, - 65}, - {{131,104,4,110,9,0,28,199,177,214,190,98,202,104,2,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,4,114,0,3,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,64,0,0,0,0,0,0,0,0}, - 77, - {131,80,0,0,0,76,120,156,203,96,201,227,100,144,57,190,241,218,190,164,83,25,76,73,255,255,7,165,100,48,98,135,137,44,69,12,204,41,12,60,137,121,249,41,169,14,233,249,69,73,137,233,204,12,12,12,14,12,80,0,0,164,94,19,234}, - 65}, - {{131,104,4,110,7,0,28,199,85,220,50,202,15,98,255,255,82,100,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,108,0,0,0,1,107,0,1,5,106,106,106,106,106,106,106,106,106,106,114,0,3,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,65,0,0,0,0,0,0,0,0}, - 113, - {131,80,0,0,0,112,120,156,203,96,201,99,103,144,57,30,122,199,232,20,127,210,255,255,65,41,57,12,12,12,140,36,17,217,12,140,172,89,112,80,196,192,156,194,192,147,152,151,159,146,234,144,158,95,148,148,152,206,12,84,228,200,0,5,0,46,116,21,208}, - 69}, - {{131,104,4,110,9,0,28,199,241,98,116,219,231,23,24,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,6,114,0,3,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,66,0,0,0,0,0,0,0,0}, - 77, - {131,80,0,0,0,76,120,156,203,96,201,227,100,144,57,254,49,169,228,246,115,113,137,164,255,255,131,82,50,24,177,195,68,182,34,6,230,20,6,158,196,188,252,148,84,135,244,252,162,164,196,116,102,6,6,6,39,6,40,0,0,155,123,19,203}, - 65}, - {{131,104,4,110,7,0,28,199,59,73,56,148,1,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,7,114,0,3,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,67,0,0,0,0,0,0,0,0}, - 75, - {131,80,0,0,0,74,120,156,203,96,201,99,103,144,57,110,237,105,49,133,49,233,255,255,160,148,12,70,236,48,145,189,136,129,57,133,129,39,49,47,63,37,213,33,61,191,40,41,49,157,153,129,129,193,153,1,10,0,245,21,17,100}, - 62}, - {{131,104,4,110,8,0,28,199,17,175,172,214,173,61,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,8,114,0,3,100,0,12,97,110,111,100,101,64,103,111,114,98,97,103,3,0,0,0,68,0,0,0,0,0,0,0,0}, - 76, - {131,80,0,0,0,75,120,156,203,96,201,227,96,144,57,46,184,126,205,181,181,182,73,255,255,7,165,100,48,98,135,137,28,69,12,204,41,12,60,137,121,249,41,169,14,233,249,69,73,137,233,204,12,12,12,46,12,80,0,0,112,226,19,66}, - 64} + {{131,104,4,110,8,0,28,199,17,175,172,214,173,61,98,255,255,82,100,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,104,1,97,8,114,0,3,100,0,19,107,97,108,108,101,64,101,108,120,51,52,104,110,121,50,50,45,49,98,3,0,0,0,65,0,0,0,3,0,0,0,0}, + 83, + {131,80,0,0,0,82,120,156,203,96,201,227,96,144,57,46,184,126,205,181,181,182,73,255,255,7,165,100,48,98,135,137,28,69,12,204,41,12,194,217,137,57,57,169,14,169,57,21,198,38,25,121,149,70,70,186,134,73,204,12,12,12,142,64,12,162,25,0,18,103,20,252}, + 72}, + {{131,116,0,0,0,0}, + 6, + {131,116,0,0,0,0}, + 6}, + {{131,116,0,0,0,3,97,1,97,11,97,2,97,22,97,3,97,33}, + 18, + {131,116,0,0,0,3,97,1,97,11,97,2,97,22,97,3,97,33}, + 18}, + {{131,116,0,0,0,100,97,48,98,0,0,2,16,97,62,98,0,0,2,170,97,11,97,121,97,39,98,0,0,1,173,97,83,98,0,0,3,145,97,63,98,0,0,2,181,97,34,98,0,0,1,118,97,68,98,0,0,2,236,97,26,98,0,0,1,30,97,78,98,0,0,3,90,97,52,98,0,0,2,60,97,15,97,165,97,64,98,0,0,2,192,97,75,98,0,0,3,57,97,81,98,0,0,3,123,97,71,98,0,0,3,13,97,20,97,220,97,50,98,0,0,2,38,97,17,97,187,97,25,98,0,0,1,19,97,65,98,0,0,2,203,97,98,98,0,0,4,54,97,79,98,0,0,3,101,97,13,97,143,97,44,98,0,0,1,228,97,8,97,88,97,99,98,0,0,4,65,97,36,98,0,0,1,140,97,67,98,0,0,2,225,97,7,97,77,97,66,98,0,0,2,214,97,85,98,0,0,3,167,97,76,98,0,0,3,68,97,1,97,11,97,32,98,0,0,1,96,97,69,98,0,0,2,247,97,37,98,0,0,1,151,97,35,98,0,0,1,129,97,84,98,0,0,3,156,97,3,97,33,97,82,98,0,0,3,134,97,45,98,0,0,1,239,97,55,98,0,0,2,93,97,6,97,66,97,2,97,22,97,94,98,0,0,4,10,97,49,98,0,0,2,27,97,41,98,0,0,1,195,97,91,98,0,0,3,233,97,87,98,0,0,3,189,97,33,98,0,0,1,107,97,42,98,0,0,1,206,97,74,98,0,0,3,46,97,60,98,0,0,2,148,97,43,98,0,0,1,217,97,10,97,110,97,70,98,0,0,3,2,97,9,97,99,97,72,98,0,0,3,24,97,86,98,0,0,3,178,97,19,97,209,97,56,98,0,0,2,104,97,95,98,0,0,4,21,97,57,98,0,0,2,115,97,51,98,0,0,2,49,97,14,97,154,97,5,97,55,97,54,98,0,0,2,82,97,18,97,198,97,61,98,0,0,2,159,97,31,98,0,0,1,85,97,22,97,242,97,29,98,0,0,1,63,97,97,98,0,0,4,43,97,21,97,231,97,89,98,0,0,3,211,97,27,98,0,0,1,41,97,24,98,0,0,1,8,97,47,98,0,0,2,5,97,100,98,0,0,4,76,97,40,98,0,0,1,184,97,96,98,0,0,4,32,97,73,98,0,0,3,35,97,90,98,0,0,3,222,97,30,98,0,0,1,74,97,58,98,0,0,2,126,97,80,98,0,0,3,112,97,88,98,0,0,3,200,97,59,98,0,0,2,137,97,77,98,0,0,3,79,97,23,97,253,97,28,98,0,0,1,52,97,46,98,0,0,1,250,97,92,98,0,0,3,244,97,53,98,0,0,2,71,97,93,98,0,0,3,255,97,16,97,176,97,38,98,0,0,1,162,97,4,97,44,97,12,97,132}, + 637, + {131,80,0,0,2,124,120,156,21,143,123,100,87,113,24,135,207,126,219,218,165,86,171,109,181,182,118,107,181,181,123,171,181,181,218,90,91,91,171,93,90,91,173,221,219,158,93,24,145,40,145,49,70,98,140,68,68,70,196,196,136,137,24,137,136,68,68,70,140,68,68,34,34,35,34,117,158,191,158,247,115,121,223,239,57,55,130,32,152,228,224,120,16,68,146,57,33,150,217,204,45,10,195,49,234,41,23,66,68,223,163,193,224,57,123,53,111,210,172,250,65,134,42,155,115,86,6,169,210,172,99,27,75,156,116,124,69,187,65,45,221,98,134,86,145,68,42,159,56,100,94,192,118,94,176,219,27,41,52,234,188,99,60,68,76,53,93,86,167,72,226,46,165,230,95,137,167,159,9,195,70,246,233,44,112,202,141,47,196,209,73,147,227,71,122,221,122,66,135,104,38,42,252,139,92,171,99,180,152,255,102,191,234,1,249,98,142,139,214,22,137,38,143,30,199,59,148,25,252,164,198,246,8,155,104,34,194,78,46,251,106,34,149,186,153,20,217,121,205,144,27,223,233,19,47,201,211,188,66,177,120,79,155,102,57,117,46,220,167,68,115,157,68,174,114,218,32,66,2,19,156,113,76,231,146,120,70,10,31,56,106,125,154,81,95,75,163,86,117,157,195,162,146,173,60,36,150,26,170,149,61,236,224,13,245,142,143,200,241,122,111,248,149,191,200,114,108,0,15,148,144,198,55,6,188,190,70,166,65,17,233,34,158,10,23,99,153,180,214,193,1,205,85,198,84,185,156,117,33,159,65,241,153,108,179,54,142,185,48,203,121,205,107,244,139,183,28,215,156,167,83,213,197,46,254,178,199,118,21,229,226,15,195,6,27,28,177,214,202,136,234,31,201,172,80,96,254,152,24,74,217,194,237,255,248,120,145,79}, + 418} }; -#define NO_OF_EXT_TERMS 19 +#define NO_OF_EXT_TERMS 22 #endif -- cgit v1.2.3 From db7446a0235f5379ed230a51cc1d55475549f36b Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 21 May 2015 00:00:56 +0200 Subject: Update runtime dependencies --- erts/preloaded/src/erts.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index 345a6ae3be..cf9a06599a 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -35,7 +35,7 @@ {registered, []}, {applications, []}, {env, []}, - {runtime_dependencies, ["stdlib-2.0", "kernel-3.0", "sasl-2.4"]} + {runtime_dependencies, ["stdlib-2.5", "kernel-4.0", "sasl-2.4"]} ]}. %% vim: ft=erlang -- cgit v1.2.3 From 37d63e9b8a0a96125ada858205286a58a5bed1ae Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 13 May 2015 16:34:57 -0400 Subject: Add architecture ppc64le architecture as a ppc64 ppc64le is a new arch/ABI that runs on IBM POWER processor starting at POWER8 processor, and it should be listed as a ppc64, mainly for hipe enablement. For more information about this new arch/ABI, please check the documents at: https://www-03.ibm.com/technologyconnect/tgcm/TGCMServlet.wss?alias=OpenPOWER --- erts/configure.in | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 62515fe081..39d3c51e3f 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -649,6 +649,7 @@ case $chk_arch_ in powerpc) ARCH=ppc;; ppc) ARCH=ppc;; ppc64) ARCH=ppc64;; + ppc64le) ARCH=ppc64;; "Power Macintosh") ARCH=ppc;; armv5b) ARCH=arm;; armv5teb) ARCH=arm;; -- cgit v1.2.3 From 441842ce023bf8ef5dc84f2d5061b0b7c79c8130 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Fri, 17 Apr 2015 22:04:31 +0200 Subject: Map error logger warnings to warning messages by default Also fix and document the broken +We option. --- erts/doc/src/erl.xml | 9 +++++---- erts/emulator/beam/erl_init.c | 9 +++++---- erts/etc/common/erlexec.c | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index ea94a4e82b..98d05dc7de 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1322,13 +1322,14 @@

Verbose.

- +

Sets the mapping of warning messages for . Messages sent to the error logger using one of the warning - routines can be mapped either to errors (default), warnings - (), or info reports (). The current - mapping can be retrieved using + routines can be mapped either to errors (), + warnings (), or info reports + (). The default is warnings. + The current mapping can be retrieved using . See error_logger(3) for further information.

diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 86d3416423..a905cbf81e 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -622,7 +622,7 @@ void erts_usage(void) erts_fprintf(stderr, "-v turn on chatty mode (GCs will be reported etc)\n"); - erts_fprintf(stderr, "-W set error logger warnings mapping,\n"); + erts_fprintf(stderr, "-W set error logger warnings mapping,\n"); erts_fprintf(stderr, " see error_logger documentation for details\n"); erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n"); erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024); @@ -1250,7 +1250,7 @@ erl_start(int argc, char **argv) verbose = DEBUG_DEFAULT; #endif - erts_error_logger_warnings = am_error; + erts_error_logger_warnings = am_warning; while (i < argc) { if (argv[i][0] != '-') { @@ -1993,11 +1993,12 @@ erl_start(int argc, char **argv) case 'i': erts_error_logger_warnings = am_info; break; + case 'e': + erts_error_logger_warnings = am_error; + break; case 'w': erts_error_logger_warnings = am_warning; break; - case 'e': /* The default */ - erts_error_logger_warnings = am_error; default: erts_fprintf(stderr, "unrecognized warning_map option %s\n", arg); erts_usage(); diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 23226909a7..d6544a2829 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -1172,7 +1172,7 @@ usage_aux(void) "[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] " "[+SP PERCENTAGE_SCHEDULERS:PERCENTAGE_SCHEDULERS_ONLINE] " "[+T LEVEL] [+V] [+v] " - "[+W] [+z MISC_OPTION] [args ...]\n"); + "[+W] [+z MISC_OPTION] [args ...]\n"); exit(1); } -- cgit v1.2.3 From 933df3caf2b62303e8b5f36af2eafc6a45a24330 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 22 May 2015 21:59:48 +0200 Subject: Fix statistics reported about fix alloc types --- erts/emulator/beam/erl_alloc_util.c | 61 +++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 2f277690e4..b92533f228 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -718,7 +718,7 @@ static void make_name_atoms(Allctr_t *allctr); static Block_t *create_carrier(Allctr_t *, Uint, UWord); static void destroy_carrier(Allctr_t *, Block_t *, Carrier_t **); static void mbc_free(Allctr_t *allctr, void *p, Carrier_t **busy_pcrr_pp); -static void dealloc_block(Allctr_t *, void *, int); +static void dealloc_block(Allctr_t *, void *, ErtsAlcFixList_t *, int); /* internal data... */ @@ -1067,17 +1067,21 @@ typedef struct { } ErtsAllctrFixDDBlock_t; #endif +#define ERTS_ALC_FIX_NO_UNUSE (((ErtsAlcType_t) 1) << ERTS_ALC_N_BITS) + static ERTS_INLINE void dealloc_fix_block(Allctr_t *allctr, ErtsAlcType_t type, void *ptr, + ErtsAlcFixList_t *fix, int dec_cc_on_redirect) { #ifdef ERTS_SMP /* May be redirected... */ - ((ErtsAllctrFixDDBlock_t *) ptr)->fix_type = type; + ASSERT((type & ERTS_ALC_FIX_NO_UNUSE) == 0); + ((ErtsAllctrFixDDBlock_t *) ptr)->fix_type = type | ERTS_ALC_FIX_NO_UNUSE; #endif - dealloc_block(allctr, ptr, dec_cc_on_redirect); + dealloc_block(allctr, ptr, fix, dec_cc_on_redirect); } static ERTS_INLINE void @@ -1123,8 +1127,7 @@ fix_cpool_check_shrink(Allctr_t *allctr, if (fix->u.cpool.min_list_size > fix->list_size) fix->u.cpool.min_list_size = fix->list_size; - fix->u.cpool.allocated--; - dealloc_fix_block(allctr, type, p, 0); + dealloc_fix_block(allctr, type, p, fix, 0); } } } @@ -1170,7 +1173,8 @@ static ERTS_INLINE void fix_cpool_free(Allctr_t *allctr, ErtsAlcType_t type, void *p, - Carrier_t **busy_pcrr_pp) + Carrier_t **busy_pcrr_pp, + int unuse) { ErtsAlcFixList_t *fix; @@ -1178,8 +1182,9 @@ fix_cpool_free(Allctr_t *allctr, && type <= ERTS_ALC_N_MAX_A_FIXED_SIZE); fix = &allctr->fix[type - ERTS_ALC_N_MIN_A_FIXED_SIZE]; - - fix->u.cpool.used--; + + if (unuse) + fix->u.cpool.used--; if ((!busy_pcrr_pp || !*busy_pcrr_pp) && !fix->u.cpool.shrink_list @@ -1237,8 +1242,7 @@ fix_cpool_alloc_shrink(Allctr_t *allctr, erts_aint32_t flgs) fix->list = *((void **) ptr); fix->list_size--; fix->u.cpool.shrink_list--; - fix->u.cpool.allocated--; - dealloc_fix_block(allctr, type, ptr, 0); + dealloc_fix_block(allctr, type, ptr, fix, 0); } if (fix->u.cpool.min_list_size > fix->list_size) fix->u.cpool.min_list_size = fix->list_size; @@ -1399,7 +1403,7 @@ fix_nocpool_alloc_shrink(Allctr_t *allctr, erts_aint32_t flgs) ptr = fix->list; fix->list = *((void **) ptr); fix->list_size--; - dealloc_block(allctr, ptr, 0); + dealloc_block(allctr, ptr, NULL, 0); fix->u.nocpool.allocated--; } if (fix->list_size != 0) { @@ -1746,11 +1750,13 @@ handle_delayed_fix_dealloc(Allctr_t *allctr, void *ptr) type = ((ErtsAllctrFixDDBlock_t *) ptr)->fix_type; - ASSERT(ERTS_ALC_N_MIN_A_FIXED_SIZE <= type - && type <= ERTS_ALC_N_MAX_A_FIXED_SIZE); + ASSERT(ERTS_ALC_N_MIN_A_FIXED_SIZE + <= (type & ~ERTS_ALC_FIX_NO_UNUSE)); + ASSERT((type & ~ERTS_ALC_FIX_NO_UNUSE) + <= ERTS_ALC_N_MAX_A_FIXED_SIZE); if (!ERTS_ALC_IS_CPOOL_ENABLED(allctr)) - fix_nocpool_free(allctr, type, ptr); + fix_nocpool_free(allctr, (type & ~ERTS_ALC_FIX_NO_UNUSE), ptr); else { Block_t *blk = UMEM2BLK(ptr); Carrier_t *busy_pcrr_p; @@ -1765,7 +1771,9 @@ handle_delayed_fix_dealloc(Allctr_t *allctr, void *ptr) NULL, &busy_pcrr_p); if (used_allctr == allctr) { doit: - fix_cpool_free(allctr, type, ptr, &busy_pcrr_p); + fix_cpool_free(allctr, (type & ~ERTS_ALC_FIX_NO_UNUSE), + ptr, &busy_pcrr_p, + !(type & ERTS_ALC_FIX_NO_UNUSE)); clear_busy_pool_carrier(allctr, busy_pcrr_p); } else { @@ -1885,7 +1893,7 @@ handle_delayed_dealloc(Allctr_t *allctr, if (fix) handle_delayed_fix_dealloc(allctr, ptr); else - dealloc_block(allctr, ptr, 1); + dealloc_block(allctr, ptr, NULL, 1); } } @@ -1991,15 +1999,24 @@ erts_alcu_check_delayed_dealloc(Allctr_t *allctr, ERTS_ALCU_DD_OPS_LIM_LOW, NULL, NULL, NULL) static void -dealloc_block(Allctr_t *allctr, void *ptr, int dec_cc_on_redirect) +dealloc_block(Allctr_t *allctr, void *ptr, ErtsAlcFixList_t *fix, int dec_cc_on_redirect) { Block_t *blk = UMEM2BLK(ptr); ERTS_SMP_LC_ASSERT(!allctr->thread_safe || erts_lc_mtx_is_locked(&allctr->mutex)); - if (IS_SBC_BLK(blk)) + if (IS_SBC_BLK(blk)) { destroy_carrier(allctr, blk, NULL); +#ifdef ERTS_SMP + if (fix && ERTS_ALC_IS_CPOOL_ENABLED(allctr)) { + ErtsAlcType_t type = ((ErtsAllctrFixDDBlock_t *) ptr)->fix_type; + if (!(type & ERTS_ALC_FIX_NO_UNUSE)) + fix->u.cpool.used--; + fix->u.cpool.allocated--; + } +#endif + } #ifndef ERTS_SMP else mbc_free(allctr, ptr, NULL); @@ -2012,6 +2029,12 @@ dealloc_block(Allctr_t *allctr, void *ptr, int dec_cc_on_redirect) used_allctr = get_used_allctr(allctr, ERTS_ALC_TS_PREF_LOCK_NO, ptr, NULL, &busy_pcrr_p); if (used_allctr == allctr) { + if (fix) { + ErtsAlcType_t type = ((ErtsAllctrFixDDBlock_t *) ptr)->fix_type; + if (!(type & ERTS_ALC_FIX_NO_UNUSE)) + fix->u.cpool.used--; + fix->u.cpool.allocated--; + } mbc_free(allctr, ptr, &busy_pcrr_p); clear_busy_pool_carrier(allctr, busy_pcrr_p); } @@ -5215,7 +5238,7 @@ do_erts_alcu_free(ErtsAlcType_t type, void *extra, void *p, if (allctr->fix) { if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) - fix_cpool_free(allctr, type, p, busy_pcrr_pp); + fix_cpool_free(allctr, type, p, busy_pcrr_pp, 1); else fix_nocpool_free(allctr, type, p); } -- cgit v1.2.3 From 9e2a3c9f5666676038b98092756e3560f285d4c5 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 13 May 2015 15:58:36 +0200 Subject: Doc fixes --- erts/doc/src/erlang.xml | 358 +++++++++++++++++++++++++----------------------- 1 file changed, 189 insertions(+), 169 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 6ca57566aa..3fea64cef5 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -539,55 +539,94 @@ Cancel a timer -

Cancels a timer. TimerRef needs to refer to - a timer that was created by either - erlang:send_after(), - or erlang:start_timer().

-

Currently available Options:

+

+ Cancels a timer that has been created by either + erlang:start_timer(), + or erlang:send_after(). + TimerRef identifies the timer, and + was returned by the BIF that created the timer. +

+

Currently available Options:

{async, Async} -

Asynchronous request for cancellation. Async - defaults to false. That is the operation will be - performed synchronously. When Async is set to - true the cancel operation will be performed - asynchronously. That is, cancel_timer() will send - a request for cancellation to the timer service that - manages the timer, and then return ok.

+

+ Asynchronous request for cancellation. Async + defaults to false which will cause the + cancellation to be performed synchronously. When + Async is set to true, the cancel + operation will be performed asynchronously. That is, + erlang:cancel_timer() will send an asynchronous + request for cancellation to the timer service that + manages the timer, and then return ok. +

+
{info, Info} -

Request information about the Result of the - cancellation. Info defaults to true. That - is information will be given. When Info is set to - false no information about the result of the cancel - operation will be given. When the operation is performed - synchronously the Result will returned from - cancel_timer(). When the operation is performed - asynchronously, a message on the form - {cancel_timer, TimerRef, Result} - will be sent to the caller of cancel_timer() when - the operation has been performed.

+

+ Request information about the Result + of the cancellation. Info defaults to true + which means that the Result will + be given. When Info is set to false, no + information about the result of the cancellation + will be given. When the operation is performed

+ + synchronously + +

+ If Info is true, the Result will + returned by erlang:cancel_timer(); otherwise, + ok will be returned. +

+
+ asynchronously + +

+ If Info is true, a message on the form + {cancel_timer, TimerRef, + Result} will be sent to the + caller of erlang:cancel_timer() when the + cancellation operation has been performed; otherwise, + no message will be sent. +

+
+
+
-

When the Result equals false a timer - corresponding to TimerRef could not be found. This - can be either because the timer had expired, been canceled, or because - TimerRef do not correspond to a timer. When the - Result is an integer, it represents - the time in milli seconds left before the timer will expire.

-

The timer service that manages the timer may be co-located - with another scheduler than the scheduler that the calling process - is executing on. In this case communication with the timer - service will be performed using asynchronous signals. If the calling - process is in critical path and can do other things while waiting - for the result of this operation, you want to use the {async, true} - option.

+

+ More Options may be added in the future. +

+

+ When the Result equals false, a + timer corresponding to TimerRef could not + be found. This can be either because the timer had expired, + already had been canceled, or because TimerRef + never has corresponded to a timer. If the timer has expired, + the timeout message has been sent, but it does not tell you + whether or not it has arrived at its destination yet. When the + Result is an integer, it represents the + time in milli-seconds left until the timer will expire. +

+ +

+ The timer service that manages the timer may be co-located + with another scheduler than the scheduler that the calling + process is executing on. If this is the case, communication + with the timer service will take much longer time than if it + is located locally. If the calling process is in critical + path, and can do other things while waiting for the result + of this operation, or is not interested in the result of + the operation, you want to use the {async, true} + option. If using the {async, false} option, the calling + process will be blocked until the operation has been + performed. +

+

See also erlang:send_after/4, erlang:start_timer/4, and erlang:read_timer/2.

-

Note: Cancelling a timer does not guarantee that the message - has not already been delivered to the message queue.

@@ -596,7 +635,7 @@

Cancels a timer. The same as calling erlang:cancel_timer(TimerRef, - [{async, false}, {info, true}]).

+ []).

@@ -4548,37 +4587,60 @@ os_prompt% Read the state of a timer -

Read the state of a timer. TimerRef - needs to refer to a timer that was created by either - erlang:send_after(), - or erlang:start_timer().

+

+ Read the state of a timer that has been created by either + erlang:start_timer(), + or erlang:send_after(). + TimerRef identifies the timer, and + was returned by the BIF that created the timer. +

Currently available Options:

{async, Async} -

Asynchronous request. Async defaults to false. That - is the operation will be performed synchronously, and the Result - will returned from read_timer(). When Async is set to - true, read_timer() will send a request for the - Result to a timer service that manages the timer and then - return ok. A message on the format - {read_timer, TimerRef, Result} - will be sent to the caller of read_timer() when - the operation has been processed.

+

+ Asynchronous request for state information. Async + defaults to false which will cause the operation + to be performed synchronously. In this case, the Result + will be returned by erlang:read_timer(). When + Async is set to true, erlang:read_timer() + will send an asynchronous request for the state information + to the timer service that manages the timer, and then return + ok. A message on the format {read_timer, + TimerRef, Result} will be + sent to the caller of erlang:read_timer() when the + operation has been processed. +

+
-

When the Result equals false a timer - corresponding to TimerRef could not be found. This - can be either because the timer had expired, been canceled, or because - TimerRef do not correspond to a timer. When the - Result is an integer, it represents - the time in milli seconds left before the timer will expire.

-

The timer service that manages the timer may be co-located - with another scheduler than the scheduler that the calling process - is executing on. In this case communication with the timer - service will be performed using asynchronous signals. If the calling - process is in critical path and can do other things while waiting - for the result of this operation, you want to use the {async, true} - option.

+

+ More Options may be added in the future. +

+

+ When the Result equals false, a + timer corresponding to TimerRef could not + be found. This can be either because the timer had expired, + had been canceled, or because TimerRef + never has corresponded to a timer. If the timer has expired, + the timeout message has been sent, but it does not tell you + whether or not it has arrived at its destination yet. When the + Result is an integer, it represents the + time in milli-seconds left until the timer will expire. +

+ +

+ The timer service that manages the timer may be co-located + with another scheduler than the scheduler that the calling + process is executing on. If this is the case, communication + with the timer service will take much longer time than if it + is located locally. If the calling process is in critical + path, and can do other things while waiting for the result + of this operation you want to use the {async, true} + option. If using the {async, false} option, the calling + process will be blocked until the operation has been + performed. +

+

See also erlang:send_after/4, erlang:start_timer/4, @@ -4592,7 +4654,7 @@ os_prompt%

Read the state of a timer. The same as calling erlang:read_timer(TimerRef, - [{async, false}]).

+ []).

@@ -4744,48 +4806,14 @@ true Start a timer -

Starts a timer. When the timer expires, the message - Msg will be sent to - Dest.

-

If Dest is a pid() it has to - be a pid() of a local process, dead or alive.

-

Currently available Options:

- - {abs, Abs} - -

Absolute timeout. When Abs is false - the Time value will be interpreted - as a time in milli-seconds relative current - Erlang - monotonic time. When Abs is true the - Time value will be interpreted as an absolute - Erlang monotonic time of milli second time unit. Abs - defaults to false.

-
-
-

The absolute time when the timer is set to expire needs - to be in the range between - erlang:system_info(start_time) - and - erlang:system_info(end_time). - If a negative relative time is specified the time is not - allowed to be negative.

-

If Dest is an atom(), 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.

-

If Dest is a pid(), the timer will be automatically - canceled if the process referred to by the pid() 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 Dest is an atom().

-

See also - erlang:send_timer/4, - erlang:cancel_timer/2, - and - erlang:read_timer/2.

-

Failure: badarg if the arguments does not satisfy - the requirements specified above.

+

+ Starts a timer. When the timer expires, the message + Msg will be sent to the process + identified by Dest. Appart from + the format of the message sent to + Dest when the timer expires + erlang:send_after/4 works exactly as + erlang:start_timer/4.

@@ -4793,36 +4821,8 @@ true Start a timer

Starts a timer. The same as calling - erlang:send_after(Time, - Dest, Msg, [{abs, false}]).

-
-
- - - 0 <= Time <= 4294967295 - Start a timer - -

Starts a timer which will send the message Msg - to Dest after Time milliseconds.

-

If Dest is a pid() it has to be a pid() of a local process, dead or alive.

-

The Time value can, in the current implementation, not be greater than 4294967295.

-

If Dest is an atom(), 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.

- -

If Dest is a pid(), the timer will be automatically - canceled if the process referred to by the pid() 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 Dest is an atom.

-

See also - erlang:start_timer/3, - erlang:cancel_timer/2, - and - erlang:read_timer/2.

-

Failure: badarg if the arguments does not satisfy - the requirements specified above.

+ erlang:send_after(Time, + Dest, Msg, []).

@@ -5231,41 +5231,59 @@ true Start a timer -

Starts a timer. When the timer expires, the message +

+ Starts a timer. When the timer expires, the message {timeout, TimerRef, Msg} - will be sent to Dest.

-

If Dest is a pid() it has to - be a pid() of a local process, dead or alive.

-

Currently available Options:

+ will be sent to the process identified by + Dest. +

+

Currently available Options:

{abs, Abs} -

Absolute timeout. When Abs is false - the Time value will be interpreted - as a time in milli-seconds relative current - Erlang - monotonic time. When Abs is true the - Time value will be interpreted as an absolute - Erlang monotonic time of milli second time unit. Abs - defaults to false.

+

+ Absolute Time value. Abs + defaults to false which means that the + Time value will be interpreted + as a time in milli-seconds relative current + Erlang + monotonic time. When Abs is set to + true, the Time value will + be interpreted as an absolute Erlang monotonic time of + milli-seconds + time unit. +

-

The absolute time when the timer is set to expire needs - to be in the range between - erlang:system_info(start_time) - and - erlang:system_info(end_time). - If a negative relative time is specified the time is not - allowed to be negative.

-

If Dest is an atom(), 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.

-

If Dest is a pid(), the timer will be automatically - canceled if the process referred to by the pid() 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 Dest is an atom().

+

+ More Options may be added in the future. +

+

+ The absolute point in time that the timer is set to expire on + has to be in the interval + [erlang:system_info(start_time), + erlang:system_info(end_time)]. + Further, if a relative time is specified, the Time value + is not allowed to be negative. +

+

+ If Dest is a pid(), it has to + be a pid() of a process created on the current + runtime system instance. This process may or may not + have terminated. If Dest is an + atom(), it will be interpreted as the name of a + locally registered process. The process referred to by the + name is looked up at the time of timer expiration. No error + is given if the name does not refer to a process. +

+

+ If Dest is a pid(), the timer will + be automatically canceled if the process referred to by the + pid() 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 + Dest is an atom(). +

See also erlang:send_after/4, erlang:cancel_timer/2, @@ -5281,7 +5299,7 @@ true

Starts a timer. The same as calling erlang:start_timer(Time, - Dest, Msg, [{abs, false}]).

+ Dest, Msg, []).

@@ -6845,7 +6863,9 @@ ok

The Erlang monotonic time in native time unit at the - time when current Erlang runtime system instance started.

+ time when current Erlang runtime system instance started. See also + erlang:system_info(end_time). +

system_version

Returns a string containing version number and -- cgit v1.2.3 From 79729c7d95cfc2b163c55071589e83019247c5a1 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 13 May 2015 16:28:59 +0200 Subject: Fix bug causing timeout to overwrite exit instruction --- erts/emulator/beam/erl_process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index af8db519d4..0a8897320d 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -9649,7 +9649,7 @@ Process *schedule(Process *p, int calls) ASSERT(erts_proc_read_refc(p) > 0); - if (ERTS_PTMR_IS_TIMED_OUT(p)) { + if (!(state & ERTS_PSFLG_EXITING) && ERTS_PTMR_IS_TIMED_OUT(p)) { BeamInstr** pi; #ifdef ERTS_SMP ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); -- cgit v1.2.3 From 528954b7ec9642f9ec987707352d558f9fd41446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sat, 23 May 2015 00:56:21 +0200 Subject: erts: Fix garbage collect literals in code purge During code purging and check_process_code, the checking of the binary reference embedded in the match binary state was omitted for the tracing tests. This would cause the binary match state to reference deallocated memory. --- erts/emulator/beam/beam_bif_load.c | 11 ++++++++++- erts/emulator/beam/erl_gc.c | 29 ++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index df1983a83d..ef42bb20d3 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -33,6 +33,7 @@ #include "beam_catches.h" #include "erl_binary.h" #include "erl_nif.h" +#include "erl_bits.h" #include "erl_thr_progress.h" static void set_default_trace_pattern(Eterm module); @@ -940,7 +941,15 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) break; case TAG_PRIMARY_HEADER: if (!header_is_transparent(val)) { - Eterm* new_p = p + thing_arityval(val); + Eterm* new_p; + if (header_is_bin_matchstate(val)) { + ErlBinMatchState *ms = (ErlBinMatchState*) p; + ErlBinMatchBuffer *mb = &(ms->mb); + if (in_area(EXPAND_POINTER(mb->orig), mod_start, mod_size)) { + return 1; + } + } + new_p = p + thing_arityval(val); ASSERT(start <= new_p && new_p < end); p = new_p; } diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 0db42d4325..3856fc0a6a 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -677,7 +677,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals, Uint area_size; Eterm* old_htop; Uint n; - struct erl_off_heap_header** prev; + struct erl_off_heap_header** prev = NULL; if (p->flags & F_DISABLE_GC) return; @@ -786,10 +786,10 @@ erts_garbage_collect_literals(Process* p, Eterm* literals, */ if (oh) { - prev = &MSO(p).first; - while (*prev) { - prev = &(*prev)->next; - } + prev = &MSO(p).first; + while (*prev) { + prev = &(*prev)->next; + } } /* @@ -818,6 +818,10 @@ erts_garbage_collect_literals(Process* p, Eterm* literals, oh = oh->next; } + if (prev) { + *prev = NULL; + } + /* * We no longer need this temporary area. */ @@ -1869,6 +1873,21 @@ sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, char* src, Uint sr if (!header_is_thing(gval)) { heap_ptr++; } else { + if (header_is_bin_matchstate(gval)) { + ErlBinMatchState *ms = (ErlBinMatchState*) heap_ptr; + ErlBinMatchBuffer *mb = &(ms->mb); + Eterm* origptr; + origptr = &(mb->orig); + ptr = boxed_val(*origptr); + val = *ptr; + if (IS_MOVED_BOXED(val)) { + *origptr = val; + mb->base = binary_bytes(*origptr); + } else if (in_area(ptr, src, src_size)) { + MOVE_BOXED(ptr,val,htop,origptr); + mb->base = binary_bytes(*origptr); + } + } heap_ptr += (thing_arityval(gval)+1); } break; -- cgit v1.2.3 From 873af0c953610d24bb2050c5810678aff1cc1006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 25 May 2015 17:03:26 +0200 Subject: erts: Refactor monitor_SUITE:mixer/1 Remove ?line macro. --- erts/emulator/test/monitor_SUITE.erl | 173 ++++++++++++++++------------------- 1 file changed, 81 insertions(+), 92 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index dc215b1529..7e64f85f16 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -665,98 +665,87 @@ list_cleanup(Config) when is_list(Config) -> mixer(doc) -> "Test mixing of internal and external monitors."; mixer(Config) when is_list(Config) -> - ?line PA = filename:dirname(code:which(?MODULE)), - ?line NN = [j0,j1,j2,j3], -% ?line NN = [j0,j1], - ?line NL0 = [begin - {ok, J} = test_server:start_node - (X, slave, [{args, "-pa " ++ PA}]), - J - end || X <- NN], - ?line NL1 = lists:duplicate(2,node()) ++ NL0, - ?line Perm = perm(NL1), - ?line lists:foreach( - fun(NL) -> - ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ], - ?line [ask_jeeves(P,{monitor_process,self()}) || P <- Js], - ?line {monitored_by,MB} = - process_info(self(),monitored_by), - ?line MBL = lists:sort(MB), - ?line JsL = lists:sort(Js), - ?line MBL = JsL, - ?line {monitors,[]} = process_info(self(),monitors), - ?line [tell_jeeves(P,{exit,flaff}) || P <- Js], - ?line wait_for_m([],[],200) - end, - Perm), - ?line lists:foreach( - fun(NL) -> - ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ], - ?line Rs = [begin - {monitor_process,Ref} = - ask_jeeves(P,{monitor_process,self()}), - {P,Ref} - end - || P <- Js], - ?line {monitored_by,MB} = - process_info(self(),monitored_by), - ?line MBL = lists:sort(MB), - ?line JsL = lists:sort(Js), - ?line MBL = JsL, - ?line {monitors,[]} = process_info(self(),monitors), - ?line [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs], - ?line wait_for_m([],[],200), - ?line [tell_jeeves(P,{exit,flaff}) || P <- Js] - end, - Perm), - ?line lists:foreach( - fun(NL) -> - ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ], - ?line [ask_jeeves(P,{monitor_process,self()}) || P <- Js], - ?line [erlang:monitor(process,P) || P <- Js], - ?line {monitored_by,MB} = - process_info(self(),monitored_by), - ?line MBL = lists:sort(MB), - ?line JsL = lists:sort(Js), - ?line MBL = JsL, - ?line {monitors,M} = - process_info(self(),monitors), - ?line ML = lists:sort([P||{process,P} <- M]), - ?line ML = JsL, - ?line [begin - tell_jeeves(P,{exit,flaff}), - receive {'DOWN',_,process,P,_} -> ok end - end || P <- Js], - ?line wait_for_m([],[],200) - end, - Perm), - ?line lists:foreach( - fun(NL) -> - ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ], - ?line Rs = [begin - {monitor_process,Ref} = - ask_jeeves(P,{monitor_process,self()}), - {P,Ref} - end - || P <- Js], - ?line R2s = [{P,erlang:monitor(process,P)} || P <- Js], - ?line {monitored_by,MB} = - process_info(self(),monitored_by), - ?line MBL = lists:sort(MB), - ?line JsL = lists:sort(Js), - ?line MBL = JsL, - ?line {monitors,M} = - process_info(self(),monitors), - ?line ML = lists:sort([P||{process,P} <- M]), - ?line ML = JsL, - ?line [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs], - ?line wait_for_m(lists:sort(M),[],200), - ?line [erlang:demonitor(Ref) || {_P,Ref} <- R2s], - ?line wait_for_m([],[],200), - ?line [tell_jeeves(P,{exit,flaff}) || P <- Js] - end, - Perm), - [test_server:stop_node(K) || K <- NL0 ], + PA = filename:dirname(code:which(?MODULE)), + NN = [j0,j1,j2,j3], + % NN = [j0,j1], + NL0 = [begin + {ok, J} = test_server:start_node(X,slave,[{args, "-pa " ++ PA}]), + J + end || X <- NN], + NL1 = lists:duplicate(2,node()) ++ NL0, + Perm = perm(NL1), + lists:foreach( + fun(NL) -> + Js = [start_jeeves({[],M}) || M <- (NL ++ NL)], + [ask_jeeves(P,{monitor_process,self()}) || P <- Js], + {monitored_by,MB} = process_info(self(),monitored_by), + MBL = lists:sort(MB), + JsL = lists:sort(Js), + MBL = JsL, + {monitors,[]} = process_info(self(),monitors), + [tell_jeeves(P,{exit,flaff}) || P <- Js], + wait_for_m([],[],200) + end, + Perm), + lists:foreach( + fun(NL) -> + Js = [start_jeeves({[],M}) || M <- (NL ++ NL)], + Rs = [begin + {monitor_process,Ref} = ask_jeeves(P,{monitor_process,self()}), + {P,Ref} + end || P <- Js], + {monitored_by,MB} = process_info(self(),monitored_by), + MBL = lists:sort(MB), + JsL = lists:sort(Js), + MBL = JsL, + {monitors,[]} = process_info(self(),monitors), + [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs], + wait_for_m([],[],200), + [tell_jeeves(P,{exit,flaff}) || P <- Js] + end, + Perm), + lists:foreach( + fun(NL) -> + Js = [start_jeeves({[],M}) || M <- (NL ++ NL)], + [ask_jeeves(P,{monitor_process,self()}) || P <- Js], + [erlang:monitor(process,P) || P <- Js], + {monitored_by,MB} = process_info(self(),monitored_by), + MBL = lists:sort(MB), + JsL = lists:sort(Js), + MBL = JsL, + {monitors,M} = process_info(self(),monitors), + ML = lists:sort([P||{process,P} <- M]), + ML = JsL, + [begin + tell_jeeves(P,{exit,flaff}), + receive {'DOWN',_,process,P,_} -> ok end + end || P <- Js], + wait_for_m([],[],200) + end, + Perm), + lists:foreach( + fun(NL) -> + Js = [start_jeeves({[],M}) || M <- (NL ++ NL)], + Rs = [begin + {monitor_process,Ref} = ask_jeeves(P,{monitor_process,self()}), + {P,Ref} + end || P <- Js], + R2s = [{P,erlang:monitor(process,P)} || P <- Js], + {monitored_by,MB} = process_info(self(),monitored_by), + MBL = lists:sort(MB), + JsL = lists:sort(Js), + MBL = JsL, + {monitors,M} = process_info(self(),monitors), + ML = lists:sort([P||{process,P} <- M]), + ML = JsL, + [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs], + wait_for_m(lists:sort(M),[],200), + [erlang:demonitor(Ref) || {_P,Ref} <- R2s], + wait_for_m([],[],200), + [tell_jeeves(P,{exit,flaff}) || P <- Js] + end, + Perm), + [test_server:stop_node(K) || K <- NL0], ok. named_down(doc) -> ["Test that DOWN message for a named monitor isn't" -- cgit v1.2.3 From 5cf8e2c92d4856dd83bef94cc0a85bba96a5a3de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 25 May 2015 17:08:45 +0200 Subject: erts: Relax monitor_SUITE:mixer/1 --- erts/emulator/test/monitor_SUITE.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 7e64f85f16..7326dfceb1 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -666,8 +666,7 @@ mixer(doc) -> "Test mixing of internal and external monitors."; mixer(Config) when is_list(Config) -> PA = filename:dirname(code:which(?MODULE)), - NN = [j0,j1,j2,j3], - % NN = [j0,j1], + NN = [j0,j1,j2], NL0 = [begin {ok, J} = test_server:start_node(X,slave,[{args, "-pa " ++ PA}]), J -- cgit v1.2.3 From f3a4db8368719fa31374c28ab35c2584add79bce Mon Sep 17 00:00:00 2001 From: Wasif Malik and Johannes Huning Date: Fri, 28 Nov 2014 17:50:10 +0100 Subject: os_mon: Implement cpu_sup:util/0,1 for FreeBSD Authors: Wasif Malik and Johannes Huning --- erts/configure.in | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 39d3c51e3f..ce0cef871f 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -4710,6 +4710,8 @@ case $host_os in use_cpu_sup=yes ;; linux*) use_cpu_sup=yes ;; + freebsd*) + use_cpu_sup=yes ;; esac if test "$use_cpu_sup" = "yes"; then -- cgit v1.2.3 From 2f38cce79ae1bf318cdc8884bd74d1407778d91d Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Thu, 30 Apr 2015 15:46:52 -0400 Subject: Fix for enif_schedule_nif and exceptions Fix a place where part of the implementation of enif_schedule_nif was not using the ErlNifEnv exception_thrown field when it should have been. Also make the result of enif_schedule_nif return false when passed to enif_is_exception, and add an assertion for this to the nif_SUITE.c tests. --- erts/emulator/beam/erl_nif.c | 5 +++-- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 426a00304e..7876785a76 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -445,7 +445,7 @@ int enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term) int enif_is_exception(ErlNifEnv* env, ERL_NIF_TERM term) { - return term == THE_NON_VALUE; + return env->exception_thrown && term == THE_NON_VALUE; } int enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term) @@ -1843,6 +1843,7 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) NifExport* ep; ERL_NIF_TERM result; + ASSERT(!env->exception_thrown); ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep); ep->fp = NULL; @@ -1855,7 +1856,7 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) * which case we need to restore the original NIF MFA. */ if (ep->fp == NULL) - restore_nif_mfa(proc, ep, is_non_value(result) && proc->freason != TRAP); + restore_nif_mfa(proc, ep, env->exception_thrown); return result; } diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 3cc9f51ef8..58d9a9a88c 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1536,9 +1536,12 @@ static ERL_NIF_TERM nif_sched1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + ERL_NIF_TERM result; if (argc != 2) return enif_make_atom(env, "false"); - return enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv); + result = enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv); + assert(!enif_is_exception(env, result)); + return result; } #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -- cgit v1.2.3 From 884afd8efb8672df2889df98b5b94f3dbb53952c Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Mon, 27 Apr 2015 23:39:37 -0400 Subject: Enhance enif_has_pending_exception Sverker Eriksson came up with the following idea: to handle a future ability for NIFs to raise more than just badarg exceptions, modify the recently-added enif_has_pending_exception function to take a second argument: a pointer to ERL_NIF_TERM. If this argument is a null pointer, ignore it. Otherwise, if the first argument, an ErlNifEnv*, has an associated exception, set the pointed-to ERL_NIF_TERM of the second argument to the value of the exception term. Add new tests and documentation for this modification. --- erts/doc/src/erl_nif.xml | 12 +++++++++--- erts/emulator/beam/erl_nif.c | 5 ++++- erts/emulator/beam/erl_nif_api_funcs.h | 2 +- erts/emulator/test/nif_SUITE.erl | 18 ++++++++++-------- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 25 +++++++++++++++++-------- 5 files changed, 41 insertions(+), 21 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 4bad8b253c..155aee2f42 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -778,11 +778,17 @@ typedef enum { and return true, or return false if term is not an unsigned integer or is outside the bounds of type unsigned long.

- intenif_has_pending_exception(ErlNifEnv* env) + intenif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason) Check if an exception has been raised.

Return true if a pending exception is associated - with the environment env. The only possible exception is currently - badarg (see enif_make_badarg).

+ with the environment env. If reason is a null pointer, ignore it. + Otherwise, if there's a pending exception associated with env, set the ERL_NIF_TERM + to which reason points to the value of the exception's term. For example, if + enif_make_badarg is called to set a + pending badarg exception, a subsequent call to enif_has_pending_exception(env, &reason) + will set reason to the atom badarg, then return true.

+

See also: enif_make_badarg.

+
intenif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin) Inspect the content of a binary diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 7876785a76..36da6519f7 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -741,8 +741,11 @@ Eterm enif_make_badarg(ErlNifEnv* env) BIF_ERROR(env->proc, BADARG); } -int enif_has_pending_exception(ErlNifEnv* env) +int enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason) { + if (env->exception_thrown && reason != NULL) { + *reason = am_badarg; + } return env->exception_thrown; } diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index bdcbb32c46..fe25c803ec 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -156,7 +156,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIte ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter)); ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[])); -ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env)); +ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env, ERL_NIF_TERM* reason)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index c35c71dd5b..434d1c38d0 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1387,7 +1387,7 @@ is_checks(Config) when is_list(Config) -> self(), hd(erlang:ports()), [], [1,9,9,8], {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551616.2e2), try - ?line error = check_is_exception(), + ?line check_is_exception(), ?line throw(expected_badarg) catch error:badarg -> @@ -1599,9 +1599,9 @@ dirty_nif_exception(Config) when is_list(Config) -> N when is_integer(N) -> ensure_lib_loaded(Config), try - %% this checks that the expected exception - %% occurs when the NIF returns the result - %% of enif_make_badarg directly + %% this checks that the expected exception occurs when the + %% dirty NIF returns the result of enif_make_badarg + %% directly call_dirty_nif_exception(1), ?t:fail(expected_badarg) catch @@ -1611,10 +1611,9 @@ dirty_nif_exception(Config) when is_list(Config) -> ok end, try - %% this checks that the expected exception - %% occurs when the NIF calls enif_make_badarg - %% at some point but then returns a value that - %% isn't an exception + %% this checks that the expected exception occurs when the + %% dirty NIF calls enif_make_badarg at some point but then + %% returns a value that isn't an exception call_dirty_nif_exception(0), ?t:fail(expected_badarg) catch @@ -1631,6 +1630,9 @@ dirty_nif_exception(Config) when is_list(Config) -> nif_exception(Config) when is_list(Config) -> ensure_lib_loaded(Config), try + %% this checks that the expected exception occurs when the NIF + %% calls enif_make_badarg at some point but then tries to return a + %% value that isn't an exception call_nif_exception(), ?t:fail(expected_badarg) catch diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 58d9a9a88c..95e361690f 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -883,16 +883,19 @@ static ERL_NIF_TERM check_is(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] * * This function is separate from check_is because it calls enif_make_badarg * and so it must return the badarg exception as its return value. Thus, the - * badarg exception indicates success. Failure is indicated by returning an - * error atom. + * badarg exception indicates success. */ static ERL_NIF_TERM check_is_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + ERL_NIF_TERM badarg, exc_term; ERL_NIF_TERM error_atom = enif_make_atom(env, "error"); - ERL_NIF_TERM badarg = enif_make_badarg(env); - if (enif_is_exception(env, error_atom)) return error_atom; - if (!enif_is_exception(env, badarg)) return error_atom; - if (!enif_has_pending_exception(env)) return error_atom; + ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg"); + assert(!enif_is_exception(env, error_atom)); + badarg = enif_make_badarg(env); + assert(enif_is_exception(env, badarg)); + assert(enif_has_pending_exception(env, NULL)); + assert(enif_has_pending_exception(env, &exc_term)); + assert(enif_is_identical(exc_term, badarg_atom)); return badarg; } @@ -1621,7 +1624,7 @@ static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL for (i = 1; i < 255; i++) args[i] = enif_make_int(env, i); return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, - call_dirty_nif_exception, 255, argv); + call_dirty_nif_exception, 255, args); } case 2: { int return_badarg_directly; @@ -1660,7 +1663,13 @@ static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL */ static ERL_NIF_TERM call_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + ERL_NIF_TERM exc_term; + ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg"); + /* ignore return value */ enif_make_badarg(env); + assert(enif_has_pending_exception(env, NULL)); + assert(enif_has_pending_exception(env, &exc_term)); + assert(enif_is_identical(badarg_atom, exc_term)); return enif_make_atom(env, "ok"); } @@ -1696,7 +1705,7 @@ static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_ } res = enif_make_double(env, val); assert(enif_is_exception(env, res)); - assert(enif_has_pending_exception(env)); + assert(enif_has_pending_exception(env, NULL)); if (strcmp(arg, "tuple") == 0) { return enif_make_tuple2(env, argv[0], res); } else { -- cgit v1.2.3 From 17735c9f3879145f43a3e4be0369b7117b1b7b84 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Wed, 6 May 2015 23:15:04 -0400 Subject: Add enif_raise_exception Add enif_raise_exception function to allow NIFs to raise error exceptions holding any Erlang terms. This does not replace or deprecate the enif_make_badarg function, though, because raising badarg errors is so idiomatic in NIFs. Reimplement enif_make_badarg on top of enif_raise_exception. Add new tests for enif_raise_exception for both normal and dirty NIFs. Add documentation for enif_raise_exception. --- erts/doc/src/erl_nif.xml | 23 ++++++++-- erts/emulator/beam/erl_nif.c | 63 +++++++++++++++++---------- erts/emulator/beam/erl_nif_api_funcs.h | 2 + erts/emulator/test/nif_SUITE.erl | 34 ++++++++++++--- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 51 ++++++++++++++-------- 5 files changed, 123 insertions(+), 50 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 155aee2f42..104d90c937 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -787,7 +787,8 @@ typedef enum { enif_make_badarg is called to set a pending badarg exception, a subsequent call to enif_has_pending_exception(env, &reason) will set reason to the atom badarg, then return true.

-

See also: enif_make_badarg.

+

See also: enif_make_badarg + and enif_raise_exception.

intenif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin) @@ -902,12 +903,13 @@ typedef enum { it calls invokes enif_make_badarg, the runtime ensures that a badarg exception is raised when the NIF returns, even if the NIF attempts to return a non-exception term instead. - The return value from enif_make_badarg may only be used as - return value from the NIF that invoked it (direct or indirectly) + The return value from enif_make_badarg may be used only as the + return value from the NIF that invoked it (directly or indirectly) or be passed to enif_is_exception, but not to any other NIF API function.

-

See also: enif_has_pending_exception. +

See also: enif_has_pending_exception + and enif_raise_exception

In earlier versions (older than erts-7.0, OTP 18) the return value from enif_make_badarg had to be returned from the NIF. This @@ -1174,6 +1176,19 @@ typedef enum { reload or upgrade.

Was previously named enif_get_data.

+ ERL_NIF_TERMenif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason) + Raise a NIF error exception +

Create an error exception with the term reason to be returned from a NIF, + and associate it with the environment env. Once a NIF or any function it calls + invokes enif_raise_exception, the runtime ensures that the exception it creates + is raised when the NIF returns, even if the NIF attempts to return a non-exception + term instead. The return value from enif_raise_exception may be used only as + the return value from the NIF that invoked it (directly or indirectly) or be passed + to enif_is_exception, but + not to any other NIF API function.

+

See also: enif_has_pending_exception + and enif_make_badarg.

+
intenif_realloc_binary(ErlNifBinary* bin, size_t size) Change the size of a binary.

Change the size of a binary bin. The source binary diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 36da6519f7..8c222a6db7 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -736,15 +736,21 @@ Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, Eterm enif_make_badarg(ErlNifEnv* env) +{ + return enif_raise_exception(env, am_badarg); +} + +Eterm enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason) { env->exception_thrown = 1; - BIF_ERROR(env->proc, BADARG); + env->proc->fvalue = reason; + BIF_ERROR(env->proc, EXC_ERROR); } int enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason) { if (env->exception_thrown && reason != NULL) { - *reason = am_badarg; + *reason = env->proc->fvalue; } return env->exception_thrown; } @@ -1541,12 +1547,13 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent) * NIF exports need a few more items than the Export struct provides, * including the erl_module_nif* and a NIF function pointer, so the * NifExport below adds those. The Export member must be first in the - * struct. The saved_mfa, saved_argc, nif_level, alloced_argv_sz and argv - * members are used to track the MFA and arguments of the top NIF in case a - * chain of one or more enif_schedule_nif() calls results in an exception, - * since in that case the original MFA and registers have to be restored - * before returning to Erlang to ensure stacktrace information associated - * with the exception is correct. + * struct. The saved_mfa, exception_thrown, saved_argc, rootset_extra, and + * rootset members are used to track the MFA, any pending exception, and + * arguments of the top NIF in case a chain of one or more + * enif_schedule_nif() calls results in an exception, since in that case + * the original MFA and registers have to be restored before returning to + * Erlang to ensure stacktrace information associated with the exception is + * correct. */ typedef ERL_NIF_TERM (*NativeFunPtr)(ErlNifEnv*, int, const ERL_NIF_TERM[]); @@ -1555,25 +1562,28 @@ typedef struct { struct erl_module_nif* m; NativeFunPtr fp; Eterm saved_mfa[3]; + int exception_thrown; int saved_argc; - int alloced_argv_sz; - Eterm argv[1]; + int rootset_extra; + Eterm rootset[1]; } NifExport; /* * If a process has saved arguments, they need to be part of the GC * rootset. The function below is called from setup_rootset() in - * erl_gc.c. This function is declared in erl_process.h. + * erl_gc.c. This function is declared in erl_process.h. Any exception term + * saved in the NifExport is also made part of the GC rootset here; it + * always resides in rootset[0]. */ int erts_setup_nif_gc(Process* proc, Eterm** objv, int* nobj) { NifExport* ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); - int gc = (ep && ep->saved_argc > 0); + int gc = ep && (ep->saved_argc > 0 || ep->rootset[0] != NIL); if (gc) { - *objv = ep->argv; - *nobj = ep->saved_argc; + *objv = ep->rootset; + *nobj = 1 + ep->saved_argc; } return gc; } @@ -1585,14 +1595,14 @@ static NifExport* allocate_nif_sched_data(Process* proc, int argc) { NifExport* ep; - size_t argv_extra, total; + size_t total; int i; - argv_extra = argc > 1 ? sizeof(Eterm)*(argc-1) : 0; - total = sizeof(NifExport) + argv_extra; + total = sizeof(NifExport) + argc*sizeof(Eterm); ep = erts_alloc(ERTS_ALC_T_NIF_TRAP_EXPORT, total); sys_memset((void*) ep, 0, total); - ep->alloced_argv_sz = argc; + ep->rootset_extra = argc; + ep->rootset[0] = NIL; for (i=0; iexp.addressv[i] = &ep->exp.code[3]; } @@ -1633,15 +1643,22 @@ init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirec ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); if (!ep) ep = allocate_nif_sched_data(proc, argc); - else if (need_save && ep->alloced_argv_sz < argc) { + else if (need_save && ep->rootset_extra < argc) { NifExport* new_ep = allocate_nif_sched_data(proc, argc); destroy_nif_export(ep); ep = new_ep; } + if (env->exception_thrown) { + ep->exception_thrown = 1; + ep->rootset[0] = env->proc->fvalue; + } else { + ep->exception_thrown = 0; + ep->rootset[0] = NIL; + } ERTS_VBUMP_ALL_REDS(proc); for (i = 0; i < argc; i++) { if (need_save) - ep->argv[i] = reg[i]; + ep->rootset[i+1] = reg[i]; reg[i] = (Eterm) argv[i]; } if (need_save) { @@ -1677,7 +1694,7 @@ restore_nif_mfa(Process* proc, NifExport* ep, int exception) proc->current[2] = ep->saved_mfa[2]; if (exception) for (i = 0; i < ep->saved_argc; i++) - reg[i] = ep->argv[i]; + reg[i] = ep->rootset[i+1]; ep->saved_argc = 0; ep->saved_mfa[0] = THE_NON_VALUE; } @@ -1702,6 +1719,7 @@ dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data)); ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep); + ASSERT(!ep->exception_thrown); if (ep->fp) restore_nif_mfa(proc, ep, 0); return argv[0]; @@ -1719,9 +1737,10 @@ dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data)); ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep); + ASSERT(ep->exception_thrown); if (ep->fp) restore_nif_mfa(proc, ep, 1); - return enif_make_badarg(env); + return enif_raise_exception(env, ep->rootset[0]); } /* diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index fe25c803ec..e38a016958 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -157,6 +157,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIte ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[])); ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env, ERL_NIF_TERM* reason)); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_raise_exception, (ErlNifEnv *env, ERL_NIF_TERM reason)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! @@ -307,6 +308,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_map_iterator_get_pair ERL_NIF_API_FUNC_MACRO(enif_map_iterator_get_pair) # define enif_schedule_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_nif) # define enif_has_pending_exception ERL_NIF_API_FUNC_MACRO(enif_has_pending_exception) +# define enif_raise_exception ERL_NIF_API_FUNC_MACRO(enif_raise_exception) /* ** ADD NEW ENTRIES HERE (before this comment) diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 434d1c38d0..778f6fd087 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -39,8 +39,9 @@ get_length/1, make_atom/1, make_string/1, reverse_list_test/1, otp_9828/1, otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1, - dirty_nif_exception/1, nif_schedule/1, - nif_exception/1, nif_nan_and_inf/1, nif_atom_too_long/1 + dirty_nif_exception/1, call_dirty_nif_exception/1, nif_schedule/1, + nif_exception/1, call_nif_exception/1, + nif_nan_and_inf/1, nif_atom_too_long/1 ]). -export([many_args_100/100]). @@ -1621,7 +1622,10 @@ dirty_nif_exception(Config) when is_list(Config) -> [{?MODULE,call_dirty_nif_exception,[0],_}|_] = erlang:get_stacktrace(), ok - end + end, + %% this checks that a dirty NIF can raise various terms as + %% exceptions + ok = nif_raise_exceptions(call_dirty_nif_exception) catch error:badarg -> {skipped,"No dirty scheduler support"} @@ -1633,12 +1637,15 @@ nif_exception(Config) when is_list(Config) -> %% this checks that the expected exception occurs when the NIF %% calls enif_make_badarg at some point but then tries to return a %% value that isn't an exception - call_nif_exception(), + call_nif_exception(0), ?t:fail(expected_badarg) catch error:badarg -> ok - end. + end, + %% this checks that a NIF can raise various terms as exceptions + ok = nif_raise_exceptions(call_nif_exception), + ok. nif_nan_and_inf(Config) when is_list(Config) -> ensure_lib_loaded(Config), @@ -1760,7 +1767,20 @@ check(Exp,Got,Line) -> io:format("CHECK at ~p: Expected ~p but got ~p\n",[Line,Exp,Got]), Got end. - + +nif_raise_exceptions(NifFunc) -> + ExcTerms = [{error, test}, "a string", <<"a binary">>, + 42, [1,2,3,4,5], [{p,1},{p,2},{p,3}]], + lists:foldl(fun(Term, ok) -> + try + erlang:apply(?MODULE,NifFunc,[Term]), + ?t:fail({expected,Term}) + catch + error:Term -> + [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(), + ok + end + end, ok, ExcTerms). %% The NIFs: lib_version() -> undefined. @@ -1816,7 +1836,7 @@ call_dirty_nif(_,_,_) -> ?nif_stub. send_from_dirty_nif(_) -> ?nif_stub. call_dirty_nif_exception(_) -> ?nif_stub. call_dirty_nif_zero_args() -> ?nif_stub. -call_nif_exception() -> ?nif_stub. +call_nif_exception(_) -> ?nif_stub. call_nif_nan_or_inf(_) -> ?nif_stub. call_nif_atom_too_long(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 95e361690f..0191e098db 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1618,13 +1618,18 @@ static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL { switch (argc) { case 1: { - ERL_NIF_TERM args[255]; - int i; - args[0] = argv[0]; - for (i = 1; i < 255; i++) - args[i] = enif_make_int(env, i); - return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, - call_dirty_nif_exception, 255, args); + int arg; + if (enif_get_int(env, argv[0], &arg) && arg < 2) { + ERL_NIF_TERM args[255]; + int i; + args[0] = argv[0]; + for (i = 1; i < 255; i++) + args[i] = enif_make_int(env, i); + return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, + call_dirty_nif_exception, 255, args); + } else { + return enif_raise_exception(env, argv[0]); + } } case 2: { int return_badarg_directly; @@ -1657,20 +1662,32 @@ static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL #endif /* - * Call enif_make_badarg, but don't return its return value. Instead, - * return ok. Result should still be a badarg exception for the erlang - * caller. + * If argv[0] is the integer 0, call enif_make_badarg, but don't return its + * return value. Instead, return ok. Result should still be a badarg + * exception for the erlang caller. + * + * For any other value of argv[0], use it as an exception term and return + * the exception. */ static ERL_NIF_TERM call_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM exc_term; ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg"); - - /* ignore return value */ enif_make_badarg(env); - assert(enif_has_pending_exception(env, NULL)); - assert(enif_has_pending_exception(env, &exc_term)); - assert(enif_is_identical(badarg_atom, exc_term)); - return enif_make_atom(env, "ok"); + int arg; + + if (enif_get_int(env, argv[0], &arg) && arg == 0) { + /* ignore return value */ enif_make_badarg(env); + assert(enif_has_pending_exception(env, NULL)); + assert(enif_has_pending_exception(env, &exc_term)); + assert(enif_is_identical(badarg_atom, exc_term)); + return enif_make_atom(env, "ok"); + } else { + ERL_NIF_TERM exc_retval = enif_raise_exception(env, argv[0]); + assert(enif_has_pending_exception(env, NULL)); + assert(enif_has_pending_exception(env, &exc_term)); + assert(enif_is_identical(argv[0], exc_term)); + return exc_retval; + } } #if !defined(NAN) || !defined(INFINITY) @@ -1925,7 +1942,7 @@ static ErlNifFunc nif_funcs[] = {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND}, #endif - {"call_nif_exception", 0, call_nif_exception}, + {"call_nif_exception", 1, call_nif_exception}, {"call_nif_nan_or_inf", 1, call_nif_nan_or_inf}, {"call_nif_atom_too_long", 1, call_nif_atom_too_long}, {"is_map_nif", 1, is_map_nif}, -- cgit v1.2.3 From 951b8c9076cc9a5283483633052587984274b8aa Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 27 May 2015 17:53:59 +0200 Subject: erts: Rename ErlNifMapIteratorEntry enums To differentiate between first/last map entry and head/tail which is before/after first/last map entry. --- erts/emulator/beam/erl_nif.c | 8 ++++---- erts/emulator/beam/erl_nif.h | 8 ++++++-- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 426a00304e..4df34e4a06 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -2024,8 +2024,8 @@ int enif_map_iterator_create(ErlNifEnv *env, size_t offset; switch (entry) { - case ERL_NIF_MAP_ITERATOR_HEAD: offset = 0; break; - case ERL_NIF_MAP_ITERATOR_TAIL: offset = flatmap_get_size(mp) - 1; break; + case ERL_NIF_MAP_ITERATOR_FIRST: offset = 0; break; + case ERL_NIF_MAP_ITERATOR_LAST: offset = flatmap_get_size(mp) - 1; break; default: goto error; } @@ -2048,12 +2048,12 @@ int enif_map_iterator_create(ErlNifEnv *env, WSTACK_INIT(iter->u.hash.wstack, ERTS_ALC_T_NIF); switch (entry) { - case ERL_NIF_MAP_ITERATOR_HEAD: + case ERL_NIF_MAP_ITERATOR_FIRST: iter->idx = 1; hashmap_iterator_init(&iter->u.hash.wstack->ws, map, 0); iter->u.hash.kv = hashmap_iterator_next(&iter->u.hash.wstack->ws); break; - case ERL_NIF_MAP_ITERATOR_TAIL: + case ERL_NIF_MAP_ITERATOR_LAST: iter->idx = hashmap_size(map); hashmap_iterator_init(&iter->u.hash.wstack->ws, map, 1); iter->u.hash.kv = hashmap_iterator_prev(&iter->u.hash.wstack->ws); diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index c4fdfd4187..af806736fd 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -218,8 +218,12 @@ typedef struct /* All fields all internal and may change */ } ErlNifMapIterator; typedef enum { - ERL_NIF_MAP_ITERATOR_HEAD = 1, - ERL_NIF_MAP_ITERATOR_TAIL = 2 + ERL_NIF_MAP_ITERATOR_FIRST = 1, + ERL_NIF_MAP_ITERATOR_LAST = 2, + + /* deprecated synonyms (undocumented in 17 and 18-rc) */ + ERL_NIF_MAP_ITERATOR_HEAD = ERL_NIF_MAP_ITERATOR_FIRST, + ERL_NIF_MAP_ITERATOR_TAIL = ERL_NIF_MAP_ITERATOR_LAST } ErlNifMapIteratorEntry; #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 3cc9f51ef8..d964ae338e 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1802,7 +1802,7 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER if (argc != 1 && !enif_is_map(env, map)) return enif_make_int(env, __LINE__); - if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_HEAD)) + if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_FIRST)) return enif_make_int(env, __LINE__); cnt = 0; @@ -1817,7 +1817,7 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER if (cnt && next_ret) return enif_make_int(env, __LINE__); - if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_TAIL)) + if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_LAST)) return enif_make_int(env, __LINE__); cnt = 0; -- cgit v1.2.3 From 7f3a057a179af274f24667ecbb7b8f3d40b667b6 Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Mon, 25 May 2015 21:22:46 +0100 Subject: Change hipe_bifs:system_crc/1 to hipe_bifs:system_crc/0 The macro HIPE_SYSTEM_CRC used to contain a hidden cookie from the VM that generated hipe_literals.hrl. This means that BEAM files containing that macro would be tied to a particular version of the VM. Change hipe_bifs:system_crc such that it doesn't require a hidden cookie to return the desired value. --- erts/emulator/hipe/hipe_bif0.c | 6 ++---- erts/emulator/hipe/hipe_bif0.tab | 2 +- erts/emulator/hipe/hipe_mkliterals.c | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 099f4f90de..a4311d22e2 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1745,13 +1745,11 @@ BIF_RETTYPE hipe_bifs_check_crc_1(BIF_ALIST_1) BIF_RET(am_false); } -BIF_RETTYPE hipe_bifs_system_crc_1(BIF_ALIST_1) +BIF_RETTYPE hipe_bifs_system_crc_0(BIF_ALIST_0) { Uint crc; - if (!term_to_Uint(BIF_ARG_1, &crc)) - BIF_ERROR(BIF_P, BADARG); - crc ^= (HIPE_SYSTEM_CRC ^ HIPE_LITERALS_CRC); + crc = HIPE_SYSTEM_CRC; BIF_RET(Uint_to_term(crc, BIF_P)); } diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab index a3e04802df..4271a78de3 100644 --- a/erts/emulator/hipe/hipe_bif0.tab +++ b/erts/emulator/hipe/hipe_bif0.tab @@ -74,7 +74,7 @@ bif hipe_bifs:set_native_address_in_fe/2 bif hipe_bifs:find_na_or_make_stub/2 bif hipe_bifs:check_crc/1 -bif hipe_bifs:system_crc/1 +bif hipe_bifs:system_crc/0 bif hipe_bifs:get_rts_param/1 #bif hipe_bifs:tuple_to_float/1 diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index ed355ce264..49e8d39360 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -648,8 +648,7 @@ static int do_e(FILE *fp, const char* this_exe) fprintf(fp, "-define(HIPE_SYSTEM_CRC, %u).\n", system_crc); } else { - fprintf(fp, "-define(HIPE_SYSTEM_CRC, hipe_bifs:system_crc(%u)).\n", - literals_crc); + fprintf(fp, "-define(HIPE_SYSTEM_CRC, hipe_bifs:system_crc()).\n"); } return 0; } -- cgit v1.2.3 From 264c4d037618a5ca8f55001855cb422d0bb5d136 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 27 May 2015 17:55:08 +0200 Subject: erts: Add docs for map functions in nif API --- erts/doc/src/erl_nif.xml | 129 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 121 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 4bad8b253c..2cc06de0e0 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -461,8 +461,9 @@ ok independent environment with all its terms is valid until you explicitly invalidates it with enif_free_env or enif_send.

-

All elements of a list/tuple must belong to the same environment as the - list/tuple itself. Terms can be copied between environments with +

All contained terms of a list/tuple/map must belong to the same + environment as the list/tuple/map itself. Terms can be copied between + environments with enif_make_copy.

ErlNifFunc @@ -729,7 +730,18 @@ typedef enum { return true, or return false if term is not an integer or is outside the bounds of type long int.

- intenif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp) + intenif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size) + Read the size of a map term +

Set *size to the number of key-value pairs in the map term and + return true, or return false if term is not a map.

+
+ intenif_get_map_value(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value) + Get the value of a key in a map +

Set *value to the value associated with key in the + map map and return true. Return false if map is not a map + or if map does not contain key.

+
+ intenif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp) Get the pointer to a resource object

Set *objp to point to the resource object referred to by term.

Return true on success or false if term is not a handle to a resource object @@ -817,6 +829,10 @@ typedef enum { Determine if a term is an exception

Return true if term is an exception.

+ intenif_is_map(ErlNifEnv* env, ERL_NIF_TERM term) + Determine if a term is a map +

Return true if term is a map, false otherwise.

+
intenif_is_number(ErlNifEnv* env, ERL_NIF_TERM term) Determine if a term is a number (integer or float)

Return true if term is a number.

@@ -986,12 +1002,13 @@ typedef enum {

Create an ordinary list containing the elements of array arr of length cnt. An empty list is returned if cnt is 0.

- intenif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM term, ERL_NIF_TERM *list) - Create the reverse list of the list term. -

Set *list to the reverse list of the list term and return true, - or return false if term is not a list. This function should only be used on + intenif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in, ERL_NIF_TERM *list_out) + Create the reverse of a list +

Set *list_out to the reverse list of the list list_in and return true, + or return false if list_in is not a list. This function should only be used on short lists as a copy will be created of the list which will not be released until after the - nif returns.

+ nif returns.

+

The list_in term must belong to the environment env.

ERL_NIF_TERMenif_make_long(ErlNifEnv* env, long int i) Create an integer term from a long int @@ -1007,6 +1024,36 @@ typedef enum { reallocated.

Return a pointer to the raw binary data and set *termp to the binary term.

+ ERL_NIF_TERMenif_make_new_map(ErlNifEnv* env) + Make an empty map term +

Make an empty map term.

+
+ intenif_make_map_put(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out) + Insert key-value pair in map +

Make a copy of map map_in and insert key with + value. If key already exists in map_in, the old + associated value is replaced by value. If successful set + *map_out to the new map and return true. Return false if + map_in is not a map.

+

The map_in term must belong to the environment env.

+
+ intenif_make_map_update(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM new_value, ERL_NIF_TERM* map_out) + Replace value for key in map +

Make a copy of map map_in and replace the old associated + value for key with new_value. If successful set + *map_out to the new map and return true. Return false if + map_in is not a map or if it does no contain key.

+

The map_in term must belong to the environment env.

+
+ intenif_make_map_remove(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out) + Remove key from map +

If map map_in contains key, make a copy of + map_in in *map_out and remove key and associated + value. If map map_in does not contain key, set + *map_out to map_in. Return true for success or false if + map_in is not a map.

+

The map_in term must belong to the environment env.

+
ERL_NIF_TERMenif_make_pid(ErlNifEnv* env, const ErlNifPid* pid) Make a pid term

Make a pid term from *pid.

@@ -1108,6 +1155,72 @@ typedef enum { Create an integer term from an unsigned long int

Create an integer term from an unsigned long int.

+ intenif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry) + Create a map iterator +

Create an iterator for the map map by initializing the + structure pointed to by iter. The entry argument determines + the start position of the iterator: ERL_NIF_MAP_ITERATOR_FIRST or + ERL_NIF_MAP_ITERATOR_LAST. Return true on success or false if + map is not a map.

+

A map iterator is only useful during the lifetime of the environment + env that the map belongs to. The iterator must be destroyed by + calling + enif_map_iterator_destroy.

+ +ERL_NIF_TERM key, value; +ErlNifMapIterator iter; +enif_map_iterator_create(env, my_map, ERL_NIF_MAP_ITERATOR_FIRST); + +while (enif_map_iterator_get_pair(env, &iter, &key, &value)) { + do_something(key,value); + enif_map_iterator_next(env, &iter); +} +enif_map_iterator_destroy(env, &iter); + +

The key-value pairs of a map have no defined iteration + order. The only guarantee is that the iteration order of a single map + instance is preserved during the lifetime of the environment that the map + belongs to.

+
+
+
+ voidenif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter) + Destroy a map iterator +

Destroy a map iterator created by + enif_map_iterator_create. +

+
+ intenif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value) + Get key and value at current map iterator position +

Get key and value terms at current map iterator position. + On success set *key and *value and return true. + Return false if the iterator is positioned at head (before first entry) + or tail (beyond last entry).

+
+ intenif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) + Check if map iterator is positioned before first +

Return true if map iterator iter is positioned + before first entry.

+
+ intenif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) + Check if map iterator is positioned after last +

Return true if map iterator iter is positioned + after last entry.

+
+ intenif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) + Increment map iterator to point to next entry +

Increment map iterator to point to next key-value entry. + Return true if the iterator is now positioned at a valid key-value entry, + or false if the iterator is positioned at the tail (beyond the last + entry).

+
+ intenif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) + Decrement map iterator to point to previous entry +

Decrement map iterator to point to previous key-value entry. + Return true if the iterator is now positioned at a valid key-value entry, + or false if the iterator is positioned at the head (before the first + entry).

+
ErlNifMutex *enif_mutex_create(char *name)

Same as erl_drv_mutex_create. -- cgit v1.2.3 From 7d60e4c592399c2e375a10338015e168a09d16a3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 28 May 2015 12:30:15 +0200 Subject: erts: Fix alphabetic order in erl_nif doc enif_make_reverse_list was at the wrong place --- erts/doc/src/erl_nif.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 2cc06de0e0..6a4fe5f8b7 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -1002,14 +1002,6 @@ typedef enum {

Create an ordinary list containing the elements of array arr of length cnt. An empty list is returned if cnt is 0.

- intenif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in, ERL_NIF_TERM *list_out) - Create the reverse of a list -

Set *list_out to the reverse list of the list list_in and return true, - or return false if list_in is not a list. This function should only be used on - short lists as a copy will be created of the list which will not be released until after the - nif returns.

-

The list_in term must belong to the environment env.

-
ERL_NIF_TERMenif_make_long(ErlNifEnv* env, long int i) Create an integer term from a long int

Create an integer term from a long int.

@@ -1097,6 +1089,14 @@ typedef enum { enif_release_resource.

+ intenif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in, ERL_NIF_TERM *list_out) + Create the reverse of a list +

Set *list_out to the reverse list of the list list_in and return true, + or return false if list_in is not a list. This function should only be used on + short lists as a copy will be created of the list which will not be released until after the + nif returns.

+

The list_in term must belong to the environment env.

+
ERL_NIF_TERMenif_make_string(ErlNifEnv* env, const char* string, ErlNifCharEncoding encoding) Create a string.

Create a list containing the characters of the -- cgit v1.2.3 From a519a52617a6a5bbdb8b17bdd892ab012cf8080b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 28 May 2015 12:33:11 +0200 Subject: erts: Cleanup fsummary lines in erl_nif docs by removing all full stop. --- erts/doc/src/erl_nif.xml | 64 ++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 32 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 6a4fe5f8b7..e915c3b693 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -565,11 +565,11 @@ typedef enum { void *enif_alloc(size_t size) - Allocate dynamic memory. + Allocate dynamic memory

Allocate memory of size bytes. Return NULL if allocation failed.

intenif_alloc_binary(size_t size, ErlNifBinary* bin) - Create a new binary. + Create a new binary

Allocate a new binary of size size bytes. Initialize the structure pointed to by bin to refer to the allocated binary. The binary must either be released by @@ -596,7 +596,7 @@ typedef enum {

Allocate a memory managed resource object of type type and size size bytes.

voidenif_clear_env(ErlNifEnv* env) - Clear an environment for reuse. + Clear an environment for reuse

Free all terms in an environment and clear it for reuse. The environment must have been allocated with enif_alloc_env.

@@ -684,14 +684,14 @@ typedef enum { size-1.

intenif_get_atom_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len, ErlNifCharEncoding encode) - Get the length of atom term. + Get the length of atom term

Set *len to the length (number of bytes excluding terminating null character) of the atom term with encoding encode. Return true on success or false if term is not an atom.

intenif_get_double(ErlNifEnv* env, ERL_NIF_TERM term, double* dp) - Read a floating-point number term. + Read a floating-point number term

Set *dp to the floating point value of term. Return true on success or false if term is not a float.

@@ -720,12 +720,12 @@ typedef enum { non-empty list.

intenif_get_list_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len) - Get the length of list term. + Get the length of list term

Set *len to the length of list term and return true, or return false if term is not a list.

intenif_get_long(ErlNifEnv* env, ERL_NIF_TERM term, long int* ip) - Read an long integer term. + Read an long integer term

Set *ip to the long integer value of term and return true, or return false if term is not an integer or is outside the bounds of type long int.

@@ -750,7 +750,7 @@ typedef enum { intenif_get_string(ErlNifEnv* env, ERL_NIF_TERM list, char* buf, unsigned size, ErlNifCharEncoding encode) - Get a C-string from a list. + Get a C-string from a list

Write a null-terminated string, in the buffer pointed to by buf with size size, consisting of the characters in the string list. The characters are written using encoding @@ -763,7 +763,7 @@ typedef enum { size is less than 1.

intenif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM term, int* arity, const ERL_NIF_TERM** array) - Inspect the elements of a tuple. + Inspect the elements of a tuple

If term is a tuple, set *array to point to an array containing the elements of the tuple and set *arity to the number of elements. Note that the array @@ -773,25 +773,25 @@ typedef enum { tuple.

intenif_get_uint(ErlNifEnv* env, ERL_NIF_TERM term, unsigned int* ip) - Read an unsigned integer term. + Read an unsigned integer term

Set *ip to the unsigned integer value of term and return true, or return false if term is not an unsigned integer or is outside the bounds of type unsigned int.

intenif_get_uint64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifUInt64* ip) - Read an unsigned 64-bit integer term. + Read an unsigned 64-bit integer term

Set *ip to the unsigned integer value of term and return true, or return false if term is not an unsigned integer or is outside the bounds of an unsigned 64-bit integer.

intenif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM term, unsigned long* ip) - Read an unsigned integer term. + Read an unsigned integer term

Set *ip to the unsigned long integer value of term and return true, or return false if term is not an unsigned integer or is outside the bounds of type unsigned long.

intenif_has_pending_exception(ErlNifEnv* env) - Check if an exception has been raised. + Check if an exception has been raised

Return true if a pending exception is associated with the environment env. The only possible exception is currently badarg (see enif_make_badarg).

@@ -906,7 +906,7 @@ typedef enum {

ERL_NIF_TERMenif_make_badarg(ErlNifEnv* env) - Make a badarg exception. + Make a badarg exception

Make a badarg exception to be returned from a NIF, and associate it with the environment env. Once a NIF or any function it calls invokes enif_make_badarg, the runtime ensures that a @@ -925,14 +925,14 @@ typedef enum { if enif_make_badarg has been invoked.

ERL_NIF_TERMenif_make_binary(ErlNifEnv* env, ErlNifBinary* bin) - Make a binary term. + Make a binary term

Make a binary term from bin. Any ownership of the binary data will be transferred to the created term and bin should be considered read-only for the rest of the NIF call and then as released.

ERL_NIF_TERMenif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term) - Make a copy of a term. + Make a copy of a term

Make a copy of term src_term. The copy will be created in environment dst_env. The source term may be located in any environment.

@@ -973,7 +973,7 @@ typedef enum {

Create an integer term from a signed 64-bit integer.

ERL_NIF_TERMenif_make_list(ErlNifEnv* env, unsigned cnt, ...) - Create a list term. + Create a list term

Create an ordinary list term of length cnt. Expects cnt number of arguments (after cnt) of type ERL_NIF_TERM as the elements of the list. An empty list is returned if cnt is 0.

@@ -987,18 +987,18 @@ typedef enum { ERL_NIF_TERMenif_make_list7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7) ERL_NIF_TERMenif_make_list8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8) ERL_NIF_TERMenif_make_list9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9) - Create a list term. + Create a list term

Create an ordinary list term with length indicated by the function name. Prefer these functions (macros) over the variadic enif_make_list to get a compile time error if the number of arguments does not match.

ERL_NIF_TERMenif_make_list_cell(ErlNifEnv* env, ERL_NIF_TERM head, ERL_NIF_TERM tail) - Create a list cell. + Create a list cell

Create a list cell [head | tail].

ERL_NIF_TERMenif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt) - Create a list term from an array. + Create a list term from an array

Create an ordinary list containing the elements of array arr of length cnt. An empty list is returned if cnt is 0.

@@ -1051,7 +1051,7 @@ typedef enum {

Make a pid term from *pid.

ERL_NIF_TERMenif_make_ref(ErlNifEnv* env) - Create a reference. + Create a reference

Create a reference like erlang:make_ref/0.

ERL_NIF_TERMenif_make_resource(ErlNifEnv* env, void* obj) @@ -1098,19 +1098,19 @@ typedef enum {

The list_in term must belong to the environment env.

ERL_NIF_TERMenif_make_string(ErlNifEnv* env, const char* string, ErlNifCharEncoding encoding) - Create a string. + Create a string

Create a list containing the characters of the null-terminated string string with encoding encoding.

ERL_NIF_TERMenif_make_string_len(ErlNifEnv* env, const char* string, size_t len, ErlNifCharEncoding encoding) - Create a string. + Create a string

Create a list containing the characters of the string string with length len and encoding encoding. Null-characters are treated as any other characters.

ERL_NIF_TERMenif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, size_t pos, size_t size) - Make a subbinary term. + Make a subbinary term

Make a subbinary of binary bin_term, starting at zero-based position pos with a length of size bytes. bin_term must be a binary or bitstring and @@ -1118,7 +1118,7 @@ typedef enum { bytes in bin_term.

ERL_NIF_TERMenif_make_tuple(ErlNifEnv* env, unsigned cnt, ...) - Create a tuple term. + Create a tuple term

Create a tuple term of arity cnt. Expects cnt number of arguments (after cnt) of type ERL_NIF_TERM as the elements of the tuple.

@@ -1132,14 +1132,14 @@ typedef enum { ERL_NIF_TERMenif_make_tuple7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7) ERL_NIF_TERMenif_make_tuple8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8) ERL_NIF_TERMenif_make_tuple9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9) - Create a tuple term. + Create a tuple term

Create a tuple term with length indicated by the function name. Prefer these functions (macros) over the variadic enif_make_tuple to get a compile time error if the number of arguments does not match.

ERL_NIF_TERMenif_make_tuple_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt) - Create a tuple term from an array. + Create a tuple term from an array

Create a tuple containing the elements of array arr of length cnt.

@@ -1282,18 +1282,18 @@ enif_map_iterator_destroy(env, &iter);

Was previously named enif_get_data.

intenif_realloc_binary(ErlNifBinary* bin, size_t size) - Change the size of a binary. + Change the size of a binary

Change the size of a binary bin. The source binary may be read-only, in which case it will be left untouched and a mutable copy is allocated and assigned to *bin. Return true on success, false if memory allocation failed.

voidenif_release_binary(ErlNifBinary* bin) - Release a binary. + Release a binary

Release a binary obtained from enif_alloc_binary.

voidenif_release_resource(void* obj) - Release a resource object. + Release a resource object

Remove a reference to resource object objobtained from enif_alloc_resource. The resource object will be destructed when the last reference is removed. @@ -1369,12 +1369,12 @@ enif_map_iterator_destroy(env, &iter); ErlNifPid *enif_self(ErlNifEnv* caller_env, ErlNifPid* pid) - Get the pid of the calling process. + Get the pid of the calling process

Initialize the pid variable *pid to represent the calling process. Return pid.

intenif_send(ErlNifEnv* env, ErlNifPid* to_pid, ErlNifEnv* msg_env, ERL_NIF_TERM msg) - Send a message to a process. + Send a message to a process

Send a message to a process.

env -- cgit v1.2.3 From 957f619382923be72835500f56e75d8bbe553892 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 26 May 2015 20:12:08 +0200 Subject: erts: Fix magic binary alignment on 32-bit Caused bus error on 32-bit sparc from unaligned 64-bit word in binary_to_term trap context. Also add _UNALIGNED_ magic macros to avoid double alignment padding in NIF resources. --- erts/emulator/beam/erl_binary.h | 18 ++++++++++++++--- erts/emulator/beam/erl_bits.c | 8 ++++++++ erts/emulator/beam/erl_nif.c | 26 +++++++++++++++--------- erts/emulator/beam/global.h | 45 +++++++++++++++++++++++++++++++++-------- 4 files changed, 77 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index 8d264d166e..6b96787d40 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -194,6 +194,9 @@ ERTS_GLB_INLINE Binary *erts_bin_nrml_alloc(Uint size); ERTS_GLB_INLINE Binary *erts_bin_realloc_fnf(Binary *bp, Uint size); ERTS_GLB_INLINE Binary *erts_bin_realloc(Binary *bp, Uint size); ERTS_GLB_INLINE void erts_bin_free(Binary *bp); +ERTS_GLB_INLINE Binary *erts_create_magic_binary_x(Uint size, + void (*destructor)(Binary *), + int unaligned); ERTS_GLB_INLINE Binary *erts_create_magic_binary(Uint size, void (*destructor)(Binary *)); @@ -332,21 +335,30 @@ erts_bin_free(Binary *bp) } ERTS_GLB_INLINE Binary * -erts_create_magic_binary(Uint size, void (*destructor)(Binary *)) +erts_create_magic_binary_x(Uint size, void (*destructor)(Binary *), + int unaligned) { - Uint bsize = ERTS_MAGIC_BIN_SIZE(size); + Uint bsize = unaligned ? ERTS_MAGIC_BIN_UNALIGNED_SIZE(size) + : ERTS_MAGIC_BIN_SIZE(size); Binary* bptr = erts_alloc_fnf(ERTS_ALC_T_BINARY, bsize); ASSERT(bsize > size); if (!bptr) erts_alloc_n_enomem(ERTS_ALC_T2N(ERTS_ALC_T_BINARY), bsize); ERTS_CHK_BIN_ALIGNMENT(bptr); bptr->flags = BIN_FLAG_MAGIC; - bptr->orig_size = ERTS_MAGIC_BIN_ORIG_SIZE(size); + bptr->orig_size = unaligned ? ERTS_MAGIC_BIN_UNALIGNED_ORIG_SIZE(size) + : ERTS_MAGIC_BIN_ORIG_SIZE(size); erts_refc_init(&bptr->refc, 0); ERTS_MAGIC_BIN_DESTRUCTOR(bptr) = destructor; return bptr; } +ERTS_GLB_INLINE Binary * +erts_create_magic_binary(Uint size, void (*destructor)(Binary *)) +{ + return erts_create_magic_binary_x(size, destructor, 0); +} + #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ #endif /* !__ERL_BINARY_H */ diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index b8ae93fa58..2e29bf8895 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -107,6 +107,14 @@ erts_bits_destroy_state(ERL_BITS_PROTO_0) void erts_init_bits(void) { + ERTS_CT_ASSERT(offsetof(Binary,orig_bytes) % 8 == 0); + ERTS_CT_ASSERT(offsetof(ErtsMagicBinary,u.aligned.data) % 8 == 0); + ERTS_CT_ASSERT(ERTS_MAGIC_BIN_BYTES_TO_ALIGN == + (offsetof(ErtsMagicBinary,u.aligned.data) + - offsetof(ErtsMagicBinary,u.unaligned.data))); + ERTS_CT_ASSERT(offsetof(ErtsBinary,driver.binary.orig_bytes) + == offsetof(Binary,orig_bytes)); + erts_smp_atomic_init_nob(&bits_bufs_size, 0); #if defined(ERTS_SMP) /* erl_process.c calls erts_bits_init_state() on all state instances */ diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 426a00304e..f42ccf23c2 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1199,7 +1199,11 @@ typedef struct enif_resource_t struct enif_resource_type_t* type; #ifdef DEBUG erts_refc_t nif_refc; +# ifdef ARCH_32 + byte align__[4]; +# endif #endif + char data[1]; }ErlNifResource; @@ -1375,7 +1379,7 @@ static void rollback_opened_resource_types(void) static void nif_resource_dtor(Binary* bin) { - ErlNifResource* resource = (ErlNifResource*) ERTS_MAGIC_BIN_DATA(bin); + ErlNifResource* resource = (ErlNifResource*) ERTS_MAGIC_BIN_UNALIGNED_DATA(bin); ErlNifResourceType* type = resource->type; ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == &nif_resource_dtor); @@ -1396,8 +1400,10 @@ static void nif_resource_dtor(Binary* bin) void* enif_alloc_resource(ErlNifResourceType* type, size_t size) { - Binary* bin = erts_create_magic_binary(SIZEOF_ErlNifResource(size), &nif_resource_dtor); - ErlNifResource* resource = ERTS_MAGIC_BIN_DATA(bin); + Binary* bin = erts_create_magic_binary_x(SIZEOF_ErlNifResource(size), + &nif_resource_dtor, + 1); /* unaligned */ + ErlNifResource* resource = ERTS_MAGIC_BIN_UNALIGNED_DATA(bin); ASSERT(type->owner && type->next && type->prev); /* not allowed in load/upgrade */ resource->type = type; @@ -1412,7 +1418,7 @@ void* enif_alloc_resource(ErlNifResourceType* type, size_t size) void enif_release_resource(void* obj) { ErlNifResource* resource = DATA_TO_RESOURCE(obj); - ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_DATA(resource); + ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource); ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == &nif_resource_dtor); #ifdef DEBUG @@ -1426,7 +1432,7 @@ void enif_release_resource(void* obj) void enif_keep_resource(void* obj) { ErlNifResource* resource = DATA_TO_RESOURCE(obj); - ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_DATA(resource); + ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource); ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == &nif_resource_dtor); #ifdef DEBUG @@ -1438,7 +1444,7 @@ void enif_keep_resource(void* obj) ERL_NIF_TERM enif_make_resource(ErlNifEnv* env, void* obj) { ErlNifResource* resource = DATA_TO_RESOURCE(obj); - ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_DATA(resource); + ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource); Eterm* hp = alloc_heap(env,PROC_BIN_SIZE); return erts_mk_magic_binary_term(&hp, &MSO(env->proc), &bin->binary); } @@ -1467,7 +1473,7 @@ int enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* typ return 0; / * Or should we allow "resource binaries" as handles? * / }*/ mbin = pb->val; - resource = (ErlNifResource*) ERTS_MAGIC_BIN_DATA(mbin); + resource = (ErlNifResource*) ERTS_MAGIC_BIN_UNALIGNED_DATA(mbin); if (ERTS_MAGIC_BIN_DESTRUCTOR(mbin) != &nif_resource_dtor || resource->type != type) { return 0; @@ -1479,8 +1485,8 @@ int enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* typ size_t enif_sizeof_resource(void* obj) { ErlNifResource* resource = DATA_TO_RESOURCE(obj); - Binary* bin = &ERTS_MAGIC_BIN_FROM_DATA(resource)->binary; - return ERTS_MAGIC_BIN_DATA_SIZE(bin) - offsetof(ErlNifResource,data); + Binary* bin = &ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource)->binary; + return ERTS_MAGIC_BIN_UNALIGNED_DATA_SIZE(bin) - offsetof(ErlNifResource,data); } @@ -2689,6 +2695,8 @@ erts_unload_nif(struct erl_module_nif* lib) void erl_nif_init() { + ERTS_CT_ASSERT((offsetof(ErlNifResource,data) % 8) == ERTS_MAGIC_BIN_BYTES_TO_ALIGN); + resource_type_list.next = &resource_type_list; resource_type_list.prev = &resource_type_list; resource_type_list.dtor = NULL; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 340c7033ab..ee1f70b748 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -230,9 +230,23 @@ typedef struct { ERTS_INTERNAL_BINARY_FIELDS SWord orig_size; void (*destructor)(Binary *); - char magic_bin_data[1]; + union { + struct { + ERTS_BINARY_STRUCT_ALIGNMENT + char data[1]; + } aligned; + struct { + char data[1]; + } unaligned; + } u; } ErtsMagicBinary; +#ifdef ARCH_32 +#define ERTS_MAGIC_BIN_BYTES_TO_ALIGN 4 +#else +#define ERTS_MAGIC_BIN_BYTES_TO_ALIGN 0 +#endif + typedef union { Binary binary; ErtsMagicBinary magic_binary; @@ -252,15 +266,30 @@ typedef union { #define ERTS_MAGIC_BIN_DESTRUCTOR(BP) \ ((ErtsBinary *) (BP))->magic_binary.destructor #define ERTS_MAGIC_BIN_DATA(BP) \ - ((void *) ((ErtsBinary *) (BP))->magic_binary.magic_bin_data) -#define ERTS_MAGIC_BIN_DATA_SIZE(BP) \ - ((BP)->orig_size - sizeof(void (*)(Binary *))) + ((void *) ((ErtsBinary *) (BP))->magic_binary.u.aligned.data) +#define ERTS_MAGIC_DATA_OFFSET \ + (offsetof(ErtsMagicBinary,u.aligned.data) - offsetof(Binary,orig_bytes)) #define ERTS_MAGIC_BIN_ORIG_SIZE(Sz) \ - (sizeof(void (*)(Binary *)) + (Sz)) + (ERTS_MAGIC_DATA_OFFSET + (Sz)) #define ERTS_MAGIC_BIN_SIZE(Sz) \ - (offsetof(ErtsMagicBinary,magic_bin_data) + (Sz)) -#define ERTS_MAGIC_BIN_FROM_DATA(DATA) \ - ((ErtsBinary*)((char*)(DATA) - offsetof(ErtsMagicBinary,magic_bin_data))) + (offsetof(ErtsMagicBinary,u.aligned.data) + (Sz)) + +/* On 32-bit arch these macro variants will save memory + by not forcing 8-byte alignment for the magic payload. +*/ +#define ERTS_MAGIC_BIN_UNALIGNED_DATA(BP) \ + ((void *) ((ErtsBinary *) (BP))->magic_binary.u.unaligned.data) +#define ERTS_MAGIC_UNALIGNED_DATA_OFFSET \ + (offsetof(ErtsMagicBinary,u.unaligned.data) - offsetof(Binary,orig_bytes)) +#define ERTS_MAGIC_BIN_UNALIGNED_DATA_SIZE(BP) \ + ((BP)->orig_size - ERTS_MAGIC_UNALIGNED_DATA_OFFSET) +#define ERTS_MAGIC_BIN_UNALIGNED_ORIG_SIZE(Sz) \ + (ERTS_MAGIC_UNALIGNED_DATA_OFFSET + (Sz)) +#define ERTS_MAGIC_BIN_UNALIGNED_SIZE(Sz) \ + (offsetof(ErtsMagicBinary,u.unaligned.data) + (Sz)) +#define ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(DATA) \ + ((ErtsBinary*)((char*)(DATA) - offsetof(ErtsMagicBinary,u.unaligned.data))) + #define Binary2ErlDrvBinary(B) (&((ErtsBinary *) (B))->driver.binary) #define ErlDrvBinary2Binary(D) ((Binary *) \ -- cgit v1.2.3 From 512c321bdaf25b685124405e287a3839be467149 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 2 Jun 2015 19:23:25 +0200 Subject: erts: Add save/restore for PSTACK --- erts/emulator/beam/global.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index ee1f70b748..07806c823c 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -777,6 +777,15 @@ ErtsPStack s = { (byte*)PSTK_DEF_STACK(s), /* pstart */ \ ERTS_ALC_T_ESTACK /* alloc_type */ \ } +#define PSTACK_CHANGE_ALLOCATOR(s,t) \ +do { \ + if (s.pstart != (byte*)PSTK_DEF_STACK(s)) { \ + erl_exit(1, "Internal error - trying to change allocator " \ + "type of active pstack\n"); \ + } \ + s.alloc_type = (t); \ + } while (0) + #define PSTACK_DESTROY(s) \ do { \ if (s.pstart != (byte*)PSTK_DEF_STACK(s)) { \ @@ -786,6 +795,8 @@ do { \ #define PSTACK_IS_EMPTY(s) (s.psp < s.pstart) +#define PSTACK_COUNT(s) (((PSTACK_TYPE*)s.psp + 1) - (PSTACK_TYPE*)s.pstart) + #define PSTACK_TOP(s) (ASSERT(!PSTACK_IS_EMPTY(s)), (PSTACK_TYPE*)(s.psp)) #define PSTACK_PUSH(s) \ @@ -796,6 +807,37 @@ do { \ #define PSTACK_POP(s) ((PSTACK_TYPE*) (s.psp -= sizeof(PSTACK_TYPE))) +/* + * Do not free the stack after this, it may have pointers into what + * was saved in 'dst'. + */ +#define PSTACK_SAVE(s,dst)\ +do {\ + if (s.pstart == (byte*)PSTK_DEF_STACK(s)) {\ + UWord _pbytes = PSTACK_COUNT(s) * sizeof(PSTACK_TYPE);\ + (dst)->pstart = erts_alloc(s.alloc_type,\ + sizeof(PSTK_DEF_STACK(s)));\ + memcpy((dst)->pstart, s.pstart, _pbytes);\ + (dst)->psp = (dst)->pstart + _pbytes - sizeof(PSTACK_TYPE);\ + (dst)->pend = (dst)->pstart + sizeof(PSTK_DEF_STACK(s));\ + (dst)->alloc_type = s.alloc_type;\ + } else\ + *(dst) = s;\ + } while (0) + +/* + * Use on empty stack, only the allocator can be changed before this. + * The src stack is reset to NULL. + */ +#define PSTACK_RESTORE(s, src) \ +do { \ + ASSERT(s.pstart == (byte*)PSTK_DEF_STACK(s)); \ + s = *(src); /* struct copy */ \ + (src)->pstart = NULL; \ + ASSERT(s.psp >= (s.pstart - sizeof(PSTACK_TYPE))); \ + ASSERT(s.psp < s.pend); \ +} while (0) + /* binary.c */ -- cgit v1.2.3 From 22431f529e872cd4a4a51ad68292eedc57261e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 2 Jun 2015 17:59:03 +0200 Subject: erts: Remove ?line macro in trace_meta_SUITE:on_and_off_test/1 --- erts/emulator/test/trace_meta_SUITE.erl | 64 ++++++++++++++++----------------- 1 file changed, 31 insertions(+), 33 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl index 45987cc319..fcdfe99bc3 100644 --- a/erts/emulator/test/trace_meta_SUITE.erl +++ b/erts/emulator/test/trace_meta_SUITE.erl @@ -237,39 +237,37 @@ return_test() -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% on_and_off_test() -> - ?line Pid = setup(), - ?line 1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[meta]), - ?line LocalTail = fun() -> - local_tail(1) - end, - ?line [1,0] = lambda_slave(LocalTail), - ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), - ?line 0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]), - ?line [1,0] = lambda_slave(LocalTail), - ?line ?NM, - ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[meta]), - ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), - ?line 1 = erlang:trace_pattern({erlang,phash2,2},[],[meta]), - ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), - ?line ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(), - ?line shutdown(), - ?line erlang:trace_pattern({'_','_','_'},false,[meta]), - ?line N = erlang:trace_pattern({erlang,'_','_'},true,[meta]), - ?line case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of - N -> - ok; - Else -> - exit({number_mismatch, {expected, N}, {got, Else}}) - end, - ?line case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of - N -> - ok; - Else2 -> - exit({number_mismatch, {expected, N}, {got, Else2}}) - end, - ?line ?NM, + Pid = setup(), + 1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[meta]), + LocalTail = fun() -> + local_tail(1) + end, + [1,0] = lambda_slave(LocalTail), + ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), + 0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]), + [1,0] = lambda_slave(LocalTail), + ?NM, + 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[meta]), + [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), + ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), + 1 = erlang:trace_pattern({erlang,phash2,2},[],[meta]), + [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), + ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), + ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(), + shutdown(), + erlang:trace_pattern({'_','_','_'},false,[meta]), + N = erlang:trace_pattern({erlang,'_','_'},true,[meta]), + case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of + N -> ok; + Else -> + exit({number_mismatch, {expected, N}, {got, Else}}) + end, + case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of + N -> ok; + Else2 -> + exit({number_mismatch, {expected, N}, {got, Else2}}) + end, + ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From b53422050d05bdcbc8d5fdf4b033aa3772c19bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 3 Jun 2015 11:24:39 +0200 Subject: erts: Relax trace_meta_SUITE:on_and_off_test/1 --- erts/emulator/test/trace_meta_SUITE.erl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl index fcdfe99bc3..4f485af0da 100644 --- a/erts/emulator/test/trace_meta_SUITE.erl +++ b/erts/emulator/test/trace_meta_SUITE.erl @@ -267,9 +267,16 @@ on_and_off_test() -> Else2 -> exit({number_mismatch, {expected, N}, {got, Else2}}) end, - ?NM, + %% ?NM, + %% Can't check for erlang:*/* stuff since common_test or test_server + %% will likely call list_to_binary in the logger. just drain any potential + %% message + ok = flush(), ok. - + +flush() -> + receive _ -> flush() after 0 -> ok end. + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% stack_grow_test() -> -- cgit v1.2.3 From f0078df03af2d873b598738b9e80ceb5f6df6019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 4 Jun 2015 11:53:47 +0200 Subject: erts: Remove remaining ?line macros in trace_meta_SUITE Also, fixed some confusing indentations and whitespace errors. This commit only has whitespace changes and removal of ?line macros. --- erts/emulator/test/trace_meta_SUITE.erl | 537 ++++++++++++++++---------------- 1 file changed, 264 insertions(+), 273 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl index 4f485af0da..25a59e0b07 100644 --- a/erts/emulator/test/trace_meta_SUITE.erl +++ b/erts/emulator/test/trace_meta_SUITE.erl @@ -72,7 +72,7 @@ config(priv_dir,_) -> info/1, tracer/1, combo/1, nosilent/1]). init_per_testcase(_Case, Config) -> - ?line Dog=test_server:timetrap(test_server:minutes(5)), + Dog=test_server:timetrap(test_server:minutes(5)), [{watchdog, Dog}|Config]. end_per_testcase(_Case, Config) -> @@ -179,59 +179,59 @@ nosilent(Config) when is_list(Config) -> %%% basic_test() -> - ?line Pid = setup(), - ?line erlang:trace_pattern({?MODULE,'_','_'},[],[meta]), - ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), - ?line erlang:trace_pattern({?MODULE,'_','_'},false,[meta]), - ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?NM, - ?line [1,1,1,0] = lambda_slave(fun() -> - exported_wrap(1) - end), - ?line ?NM, - ?line erlang:trace_pattern({?MODULE,'_','_'},[],[meta]), - ?line [1,1,1,0] = lambda_slave(fun() -> - exported_wrap(1) - end), - ?line ?CTT(Pid,{?MODULE,_,_}) = receive_next(), %% The fun - ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), - ?line erlang:trace_pattern({?MODULE,'_','_'},false,[meta]), - ?line shutdown(), - ?line ?NM, + Pid = setup(), + erlang:trace_pattern({?MODULE,'_','_'},[],[meta]), + [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), + ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), + erlang:trace_pattern({?MODULE,'_','_'},false,[meta]), + [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), + ?NM, + [1,1,1,0] = lambda_slave(fun() -> + exported_wrap(1) + end), + ?NM, + erlang:trace_pattern({?MODULE,'_','_'},[],[meta]), + [1,1,1,0] = lambda_slave(fun() -> + exported_wrap(1) + end), + ?CTT(Pid,{?MODULE,_,_}) = receive_next(), %% The fun + ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), + erlang:trace_pattern({?MODULE,'_','_'},false,[meta]), + shutdown(), + ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% return_test() -> - ?line Pid = setup(), - ?line erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}], + Pid = setup(), + erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}], [meta]), - ?line erlang:trace_pattern({erlang,phash2,'_'},[{'_',[],[{return_trace}]}], + erlang:trace_pattern({erlang,phash2,'_'},[{'_',[],[{return_trace}]}], [meta]), - ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(), - ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), - ?line ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(), - ?line ?RFT(Pid,{erlang,phash2,2},0) = receive_next(), - ?line ?RFT(Pid,{?MODULE,local_tail,1},[1,0]) = receive_next(), - ?line ?RFT(Pid,{?MODULE,local2,1},[1,0]) = receive_next(), - ?line ?RFT(Pid,{?MODULE,local,1},[1,1,0]) = receive_next(), - ?line ?RFT(Pid,{?MODULE,exported,1},[1,1,1,0]) = receive_next(), - ?line ?RFT(Pid,{?MODULE,exported_wrap,1},[1,1,1,0]) = receive_next(), - ?line shutdown(), - ?line ?NM, + [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), + ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(), + ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), + ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(), + ?RFT(Pid,{erlang,phash2,2},0) = receive_next(), + ?RFT(Pid,{?MODULE,local_tail,1},[1,0]) = receive_next(), + ?RFT(Pid,{?MODULE,local2,1},[1,0]) = receive_next(), + ?RFT(Pid,{?MODULE,local,1},[1,1,0]) = receive_next(), + ?RFT(Pid,{?MODULE,exported,1},[1,1,1,0]) = receive_next(), + ?RFT(Pid,{?MODULE,exported_wrap,1},[1,1,1,0]) = receive_next(), + shutdown(), + ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -280,11 +280,11 @@ flush() -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% stack_grow_test() -> - ?line Pid = setup(), - ?line 1 = erlang:trace_pattern({?MODULE,loop,4}, + Pid = setup(), + 1 = erlang:trace_pattern({?MODULE,loop,4}, [{'_',[],[{return_trace}]}],[meta]), - ?line Num = 1 bsl 15, - ?line Surface = + Num = 1 bsl 15, + Surface = fun (This, ?RFT(P,{?MODULE,loop,4},N), N) when P == Pid-> if N == Num -> ?NM, @@ -293,7 +293,7 @@ stack_grow_test() -> This(This, receive_next(), N+1) end end, - ?line Dive = + Dive = fun (This, ?CTT(P,{?MODULE,loop,[{hej,hopp},[a,b,c],4.5,N]}), N) when P == Pid-> if N == 0 -> @@ -302,272 +302,263 @@ stack_grow_test() -> This(This, receive_next(), N-1) end end, - ?line apply_slave(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]), -% ?line apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]), -% ?line List = collect(test_server:seconds(5)), - ?line ok = Dive(Dive, receive_next(), Num), - ?line ?NM, + apply_slave(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]), +% apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]), +% List = collect(test_server:seconds(5)), + ok = Dive(Dive, receive_next(), Num), + ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% info_test() -> - ?line setup(), - ?line Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]}, - {'_',[],[]}], - ?line Self = self(), - ?line GoOn = make_ref(), - - ?line Pid = + setup(), + Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]}, {'_',[],[]}], + Self = self(), + GoOn = make_ref(), + Pid = spawn_link( fun () -> erlang:trace_pattern({?MODULE,exported_wrap,1}, - Prog, [{meta, Self}]), + Prog, [{meta, Self}]), Self ! {self(), GoOn} end), - ?line receive {Pid, GoOn} -> ok end, - ?line {traced,false} = erlang:trace_info({?MODULE,exported_wrap,1}, traced), - ?line {match_spec, false} = + receive {Pid, GoOn} -> ok end, + {traced,false} = erlang:trace_info({?MODULE,exported_wrap,1}, traced), + {match_spec, false} = erlang:trace_info({?MODULE,exported_wrap,1}, match_spec), - ?line {meta, Self} = erlang:trace_info({?MODULE,exported_wrap,1}, meta), - ?line {meta_match_spec, MMS} = + {meta, Self} = erlang:trace_info({?MODULE,exported_wrap,1}, meta), + {meta_match_spec, MMS} = erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec), - ?line case MMS of - Prog -> - ok; - Wrong -> - exit({bad_result, {erlang,trace_info, - [{?MODULE,exported_wrap,1}, - meta_match_spec]}, - {expected, Prog}, {got, Wrong}}) - end, - ?line erlang:garbage_collect(self()), - ?line receive - after 1 -> - ok - end, - ?line io:format("~p~n",[MMS]), - ?line {meta_match_spec,MMS2} = + case MMS of + Prog -> + ok; + Wrong -> + exit({bad_result, {erlang,trace_info, + [{?MODULE,exported_wrap,1}, + meta_match_spec]}, + {expected, Prog}, {got, Wrong}}) + end, + erlang:garbage_collect(self()), + receive + after 1 -> + ok + end, + io:format("~p~n",[MMS]), + {meta_match_spec,MMS2} = erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec), - ?line io:format("~p~n",[MMS2]), - ?line case MMS2 of - Prog -> - ok; - Wrong2 -> - exit({bad_result, {erlang,trace_info, - [{?MODULE,exported_wrap,1}, - meta_match_spec]}, - {expected, Prog}, {got, Wrong2}}) - end, - ?line {all, [_|_]=L} = erlang:trace_info({?MODULE,exported_wrap,1}, all), - ?line {value, {meta, Self}} = - lists:keysearch(meta, 1, L), - ?line {value, {meta_match_spec, MMS}} = - lists:keysearch(meta_match_spec, 1, L), - - ?line erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [meta]), - ?line {meta_match_spec, []} = + io:format("~p~n",[MMS2]), + case MMS2 of + Prog -> + ok; + Wrong2 -> + exit({bad_result, {erlang,trace_info, + [{?MODULE,exported_wrap,1}, + meta_match_spec]}, + {expected, Prog}, {got, Wrong2}}) + end, + {all, [_|_]=L} = erlang:trace_info({?MODULE,exported_wrap,1}, all), + {value, {meta, Self}} = lists:keysearch(meta, 1, L), + {value, {meta_match_spec, MMS}} = lists:keysearch(meta_match_spec, 1, L), + + erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [meta]), + {meta_match_spec, []} = erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec), - - ?line erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [meta]), - ?line {meta, false} = erlang:trace_info({?MODULE,exported_wrap,1}, meta), - ?line {meta_match_spec, false} = + + erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [meta]), + {meta, false} = erlang:trace_info({?MODULE,exported_wrap,1}, meta), + {meta_match_spec, false} = erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec), - ?line {all, false} = erlang:trace_info({?MODULE,exported_wrap,1}, all), - - ?line shutdown(), - ?line ?NM, + {all, false} = erlang:trace_info({?MODULE,exported_wrap,1}, all), + shutdown(), + ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% tracer_test() -> - ?line Slave = setup(), - ?line Self = self(), - - ?line MatchSpec = [{'_',[],[{return_trace}]}], - ?line Tracer1 = spawn_link(fun () -> relay_n(3, Self) end), - ?line Setter = - spawn_link( - fun () -> - erlang:trace_pattern({?MODULE,receiver,1}, - MatchSpec, - [{meta,Tracer1}]), - erlang:trace_pattern({erlang,phash2,2}, - MatchSpec, - [{meta,Tracer1}]), - Self ! {self(), done} - end), - ?line receive {Setter, done} -> ok end, - ?line Ref = make_ref(), - ?line apply_slave_async(?MODULE, receiver, [Ref]), - ?line {Tracer1,?CTT(Slave,{?MODULE,receiver,[Ref]})} = receive_next(100), - ?line {Tracer1,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(100), - ?line {Tracer1,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(100), + Slave = setup(), + Self = self(), + + MatchSpec = [{'_',[],[{return_trace}]}], + Tracer1 = spawn_link(fun () -> relay_n(3, Self) end), + Setter = spawn_link( + fun () -> + erlang:trace_pattern({?MODULE,receiver,1}, + MatchSpec, + [{meta,Tracer1}]), + erlang:trace_pattern({erlang,phash2,2}, + MatchSpec, + [{meta,Tracer1}]), + Self ! {self(), done} + end), + receive {Setter, done} -> ok end, + Ref = make_ref(), + apply_slave_async(?MODULE, receiver, [Ref]), + {Tracer1,?CTT(Slave,{?MODULE,receiver,[Ref]})} = receive_next(100), + {Tracer1,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(100), + {Tracer1,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(100), %% Initiate a return_trace that will fail since the tracer just stopped - ?line Slave ! Ref, - ?line receive_no_next(100), + Slave ! Ref, + receive_no_next(100), %% The breakpoint has not been hit since the tracer stopped - ?line {meta,Tracer1} = + {meta,Tracer1} = erlang:trace_info({?MODULE,receiver,1}, meta), - ?line {meta_match_spec, MatchSpec} = + {meta_match_spec, MatchSpec} = erlang:trace_info({?MODULE,receiver,1}, meta_match_spec), - ?line {meta,Tracer1} = + {meta,Tracer1} = erlang:trace_info({erlang,phash2,2}, meta), - ?line {meta_match_spec, MatchSpec} = + {meta_match_spec, MatchSpec} = erlang:trace_info({erlang,phash2,2}, meta_match_spec), %% Initiate trace messages that will fail - ?line Ref2 = make_ref(), - ?line apply_slave_async(?MODULE, receiver, [Ref2]), - ?line Slave ! Ref2, - ?line receive_no_next(100), - ?line {meta,[]} = + Ref2 = make_ref(), + apply_slave_async(?MODULE, receiver, [Ref2]), + Slave ! Ref2, + receive_no_next(100), + {meta,[]} = erlang:trace_info({?MODULE,receiver,1}, meta), - ?line {meta_match_spec, MatchSpec} = + {meta_match_spec, MatchSpec} = erlang:trace_info({?MODULE,receiver,1}, meta_match_spec), - ?line {meta,[]} = + {meta,[]} = erlang:trace_info({erlang,phash2,2}, meta), - ?line {meta_match_spec, MatchSpec} = + {meta_match_spec, MatchSpec} = erlang:trace_info({erlang,phash2,2}, meta_match_spec), %% Change tracer - ?line Tracer2 = spawn_link(fun () -> relay_n(4, Self) end), - ?line erlang:trace_pattern({?MODULE,receiver,1}, - MatchSpec, - [{meta,Tracer2}]), - ?line erlang:trace_pattern({erlang,phash2,2}, - MatchSpec, - [{meta,Tracer2}]), - ?line Ref3 = make_ref(), - ?line apply_slave_async(?MODULE, receiver, [Ref3]), - ?line {Tracer2,?CTT(Slave,{?MODULE,receiver,[Ref3]})} = receive_next(), - ?line {Tracer2,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(), - ?line {Tracer2,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(), + Tracer2 = spawn_link(fun () -> relay_n(4, Self) end), + erlang:trace_pattern({?MODULE,receiver,1}, + MatchSpec, + [{meta,Tracer2}]), + erlang:trace_pattern({erlang,phash2,2}, + MatchSpec, + [{meta,Tracer2}]), + Ref3 = make_ref(), + apply_slave_async(?MODULE, receiver, [Ref3]), + {Tracer2,?CTT(Slave,{?MODULE,receiver,[Ref3]})} = receive_next(), + {Tracer2,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(), + {Tracer2,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(), %% Change tracer between call trace and return trace - ?line Tracer3 = spawn_link(fun () -> relay_n(4, Self) end), - ?line erlang:trace_pattern({?MODULE,receiver,1}, - MatchSpec, - [{meta,Tracer3}]), - ?line erlang:trace_pattern({erlang,phash2,2}, - MatchSpec, - [{meta,Tracer3}]), - ?line Slave ! Ref3, + Tracer3 = spawn_link(fun () -> relay_n(4, Self) end), + erlang:trace_pattern({?MODULE,receiver,1}, + MatchSpec, + [{meta,Tracer3}]), + erlang:trace_pattern({erlang,phash2,2}, + MatchSpec, + [{meta,Tracer3}]), + Slave ! Ref3, %% The return trace should still come from Tracer2 - ?line {Tracer2,?RFT(Slave,{?MODULE,receiver,1},Ref3)} = receive_next(), - ?line Ref4 = make_ref(), + {Tracer2,?RFT(Slave,{?MODULE,receiver,1},Ref3)} = receive_next(), + Ref4 = make_ref(), %% Now should Tracer3 be used - ?line apply_slave_async(?MODULE, receiver, [Ref4]), - ?line Slave ! Ref4, - ?line {Tracer3,?CTT(Slave,{?MODULE,receiver,[Ref4]})} = receive_next(), - ?line {Tracer3,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(), - ?line {Tracer3,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(), - ?line {Tracer3,?RFT(Slave,{?MODULE,receiver,1},Ref4)} = receive_next(), + apply_slave_async(?MODULE, receiver, [Ref4]), + Slave ! Ref4, + {Tracer3,?CTT(Slave,{?MODULE,receiver,[Ref4]})} = receive_next(), + {Tracer3,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(), + {Tracer3,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(), + {Tracer3,?RFT(Slave,{?MODULE,receiver,1},Ref4)} = receive_next(), %% The breakpoint has not been hit since the tracer stopped - ?line {meta,Tracer3} = - erlang:trace_info({?MODULE,receiver,1}, meta), - ?line {meta_match_spec, MatchSpec} = + {meta,Tracer3} = erlang:trace_info({?MODULE,receiver,1}, meta), + {meta_match_spec, MatchSpec} = erlang:trace_info({?MODULE,receiver,1}, meta_match_spec), - ?line {meta,Tracer3} = + {meta,Tracer3} = erlang:trace_info({erlang,phash2,2}, meta), - ?line {meta_match_spec, MatchSpec} = + {meta_match_spec, MatchSpec} = erlang:trace_info({erlang,phash2,2}, meta_match_spec), - - ?line shutdown(), - ?line ?NM, + shutdown(), + ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% combo_test() -> - ?line Slave = setup(), - ?line Self = self(), - - ?line MatchSpec = [{'_',[],[{return_trace}]}], - ?line Flags = lists:sort([call, return_to]), - ?line LocalTracer = spawn_link(fun () -> relay_n(6, Self) end), - ?line MetaTracer = spawn_link(fun () -> relay_n(4, Self) end), - ?line 1 = erlang:trace_pattern({?MODULE,receiver,1}, - MatchSpec, - [local,{meta,MetaTracer}]), - ?line 1 = erlang:trace_pattern({erlang,phash2,2}, - MatchSpec, - [local,{meta,MetaTracer}]), - ?line 1 = erlang:trace(Slave, true, - [{tracer,LocalTracer} | Flags]), + Slave = setup(), + Self = self(), + + MatchSpec = [{'_',[],[{return_trace}]}], + Flags = lists:sort([call, return_to]), + LocalTracer = spawn_link(fun () -> relay_n(6, Self) end), + MetaTracer = spawn_link(fun () -> relay_n(4, Self) end), + 1 = erlang:trace_pattern({?MODULE,receiver,1}, + MatchSpec, + [local,{meta,MetaTracer}]), + 1 = erlang:trace_pattern({erlang,phash2,2}, + MatchSpec, + [local,{meta,MetaTracer}]), + 1 = erlang:trace(Slave, true, + [{tracer,LocalTracer} | Flags]), %% - ?line {all, TraceInfo1} = + {all, TraceInfo1} = erlang:trace_info({?MODULE,receiver,1}, all), - ?line {meta,MetaTracer} = + {meta,MetaTracer} = erlang:trace_info({?MODULE,receiver,1}, meta), - ?line {value,{meta,MetaTracer}} = + {value,{meta,MetaTracer}} = lists:keysearch(meta, 1, TraceInfo1), - ?line {meta_match_spec,MatchSpec} = + {meta_match_spec,MatchSpec} = erlang:trace_info({?MODULE,receiver,1}, meta_match_spec), - ?line {value,{meta_match_spec,MatchSpec}} = + {value,{meta_match_spec,MatchSpec}} = lists:keysearch(meta_match_spec, 1, TraceInfo1), - ?line {traced,local} = + {traced,local} = erlang:trace_info({?MODULE,receiver,1}, traced), - ?line {value,{traced,local}} = + {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfo1), - ?line {match_spec,MatchSpec} = + {match_spec,MatchSpec} = erlang:trace_info({?MODULE,receiver,1}, match_spec), - ?line {value,{match_spec,MatchSpec}} = + {value,{match_spec,MatchSpec}} = lists:keysearch(match_spec, 1, TraceInfo1), %% - ?line {all, TraceInfo2} = + {all, TraceInfo2} = erlang:trace_info({erlang,phash2,2}, all), - ?line {meta,MetaTracer} = + {meta,MetaTracer} = erlang:trace_info({erlang,phash2,2}, meta), - ?line {value,{meta,MetaTracer}} = + {value,{meta,MetaTracer}} = lists:keysearch(meta, 1, TraceInfo2), - ?line {meta_match_spec,MatchSpec} = + {meta_match_spec,MatchSpec} = erlang:trace_info({erlang,phash2,2}, meta_match_spec), - ?line {value,{meta_match_spec,MatchSpec}} = + {value,{meta_match_spec,MatchSpec}} = lists:keysearch(meta_match_spec, 1, TraceInfo2), - ?line {traced,local} = + {traced,local} = erlang:trace_info({erlang,phash2,2}, traced), - ?line {value,{traced,local}} = + {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfo2), - ?line {match_spec,MatchSpec} = + {match_spec,MatchSpec} = erlang:trace_info({erlang,phash2,2}, match_spec), - ?line {value,{match_spec,MatchSpec}} = + {value,{match_spec,MatchSpec}} = lists:keysearch(match_spec, 1, TraceInfo2), %% - ?line {flags,Flags1} = erlang:trace_info(Slave, flags), - ?line Flags = lists:sort(Flags1), - ?line {tracer,LocalTracer} = erlang:trace_info(Slave, tracer), + {flags,Flags1} = erlang:trace_info(Slave, flags), + Flags = lists:sort(Flags1), + {tracer,LocalTracer} = erlang:trace_info(Slave, tracer), %% - ?line Ref = make_ref(), - ?line apply_slave_async(?MODULE, receiver, [Ref]), - ?line Slave ! Ref, - ?line ?CTT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(MetaTracer), - ?line ?CTT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(MetaTracer), - ?line ?RFT(Slave,{erlang,phash2,2},0) = receive_next_bytag(MetaTracer), - ?line ?RFT(Slave,{?MODULE,receiver,1},Ref) = receive_next_bytag(MetaTracer), - ?line ?CT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(LocalTracer), - ?line ?CT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(LocalTracer), - ?line case {receive_next_bytag(LocalTracer), + Ref = make_ref(), + apply_slave_async(?MODULE, receiver, [Ref]), + Slave ! Ref, + ?CTT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(MetaTracer), + ?CTT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(MetaTracer), + ?RFT(Slave,{erlang,phash2,2},0) = receive_next_bytag(MetaTracer), + ?RFT(Slave,{?MODULE,receiver,1},Ref) = receive_next_bytag(MetaTracer), + ?CT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(LocalTracer), + ?CT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(LocalTracer), + case {receive_next_bytag(LocalTracer), receive_next_bytag(LocalTracer)} of {?RF(Slave,{erlang,phash2,2},0), ?RT(Slave,{?MODULE,receiver,1})} -> - ?line ok; + ok; {?RT(Slave,{?MODULE,receiver,1}), ?RF(Slave,{erlang,phash2,2},0)} -> - ?line ok; + ok; Error1 -> ?t:fail({unexpected_message, Error1}) end, - ?line case {receive_next_bytag(LocalTracer), + case {receive_next_bytag(LocalTracer), receive_next_bytag(LocalTracer)} of {?RF(Slave,{?MODULE,receiver,1},Ref), ?RT(Slave,{?MODULE,slave,1})} -> - ?line ok; + ok; {?RT(Slave,{?MODULE,slave,1}), ?RF(Slave,{?MODULE,receiver,1},Ref)} -> - ?line ok; + ok; Error2 -> ?t:fail({unexpected_message, Error2}) end, - - ?line shutdown(), - ?line ?NM, + shutdown(), + ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -575,38 +566,38 @@ combo_test() -> %% Setup silent local call tracing, and start it using meta trace. nosilent_test() -> - ?line Pid = setup(), - ?line Trigger = {?MODULE,id,1}, - ?line TriggerMS = [{[start],[],[{silent,false}]}, - {[stop],[],[{silent,true},{return_trace}]}], - ?line 1 = erlang:trace(Pid, true, [call,silent,return_to]), - ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]), - ?line 1 = erlang:trace_pattern({?MODULE,local2,1}, - [{'_',[],[{return_trace}]}], - [local]), - ?line 1 = erlang:trace_pattern({?MODULE,slave,1},false,[local]), - ?line 1 = erlang:trace_pattern(Trigger,false,[local]), - ?line 1 = erlang:trace_pattern(Trigger,TriggerMS,[meta]), - ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), - ?line receive_no_next(17), - ?line start = apply_slave(?MODULE, id, [start]), - ?line ?CTT(Pid,{?MODULE,id,[start]}) = receive_next(), - ?line [2,2,2,0] = apply_slave(?MODULE,exported_wrap,[2]), - ?line ?CT(Pid,{?MODULE,exported_wrap,[2]}) = receive_next(), - ?line ?CT(Pid,{?MODULE,exported,[2]}) = receive_next(), - ?line ?CT(Pid,{?MODULE,local,[2]}) = receive_next(), - ?line ?CT(Pid,{?MODULE,local2,[2]}) = receive_next(), - ?line ?CT(Pid,{?MODULE,local_tail,[2]}) = receive_next(), - ?line ?RF(Pid,{?MODULE,local2,1}, [2,0]) = receive_next(), - ?line ?RT(Pid,{?MODULE,local,1}) = receive_next(), - ?line ?RT(Pid,{?MODULE,exported,1}) = receive_next(), - ?line ?RT(Pid,{?MODULE,slave,1}) = receive_next(), - ?line stop = apply_slave(?MODULE, id, [stop]), - ?line ?CTT(Pid,{?MODULE,id,[stop]}) = receive_next(), - ?line ?RFT(Pid,{?MODULE,id,1}, stop) = receive_next(), - ?line [3,3,3,0] = apply_slave(?MODULE,exported_wrap,[3]), - ?line receive_no_next(17), - ?line shutdown(), + Pid = setup(), + Trigger = {?MODULE,id,1}, + TriggerMS = [{[start],[],[{silent,false}]}, + {[stop],[],[{silent,true},{return_trace}]}], + 1 = erlang:trace(Pid, true, [call,silent,return_to]), + erlang:trace_pattern({?MODULE,'_','_'},[],[local]), + 1 = erlang:trace_pattern({?MODULE,local2,1}, + [{'_',[],[{return_trace}]}], + [local]), + 1 = erlang:trace_pattern({?MODULE,slave,1},false,[local]), + 1 = erlang:trace_pattern(Trigger,false,[local]), + 1 = erlang:trace_pattern(Trigger,TriggerMS,[meta]), + [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), + receive_no_next(17), + start = apply_slave(?MODULE, id, [start]), + ?CTT(Pid,{?MODULE,id,[start]}) = receive_next(), + [2,2,2,0] = apply_slave(?MODULE,exported_wrap,[2]), + ?CT(Pid,{?MODULE,exported_wrap,[2]}) = receive_next(), + ?CT(Pid,{?MODULE,exported,[2]}) = receive_next(), + ?CT(Pid,{?MODULE,local,[2]}) = receive_next(), + ?CT(Pid,{?MODULE,local2,[2]}) = receive_next(), + ?CT(Pid,{?MODULE,local_tail,[2]}) = receive_next(), + ?RF(Pid,{?MODULE,local2,1}, [2,0]) = receive_next(), + ?RT(Pid,{?MODULE,local,1}) = receive_next(), + ?RT(Pid,{?MODULE,exported,1}) = receive_next(), + ?RT(Pid,{?MODULE,slave,1}) = receive_next(), + stop = apply_slave(?MODULE, id, [stop]), + ?CTT(Pid,{?MODULE,id,[stop]}) = receive_next(), + ?RFT(Pid,{?MODULE,id,1}, stop) = receive_next(), + [3,3,3,0] = apply_slave(?MODULE,exported_wrap,[3]), + receive_no_next(17), + shutdown(), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -650,9 +641,9 @@ slave(Sync) -> Sync ! sync, receive {From,apply, M, F, A} -> - ?line ?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]), - ?line Res = apply(M,F,A), - ?line ?dbgformat("done Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]), + ?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]), + Res = apply(M,F,A), + ?dbgformat("done Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]), From ! {apply, Res}, erlang:trace_pattern({?MODULE,slave,1},false,[meta]), slave(From); -- cgit v1.2.3 From 34b858c3e329918d530da882b594d2db727d3856 Mon Sep 17 00:00:00 2001 From: Rory Byrne Date: Thu, 14 May 2015 20:07:39 +0100 Subject: Fix add_multi_timer() in inet_drv Fix the sorting logic in add_multi_timer() and expand the test case coverage around this area. --- erts/emulator/drivers/common/inet_drv.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index e001f31932..10ef20fc82 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -12172,6 +12172,8 @@ static MultiTimerData *add_multi_timer(MultiTimerData **first, ErlDrvPort port, void (*timeout_fun)(ErlDrvData drv_data, ErlDrvTermData caller)) { +#define eq_mega(a, b) ((a)->when.megasecs == (b)->when.megasecs) +#define eq_sec(a, b) ((a)->when.secs == (b)->when.secs) MultiTimerData *mtd, *p, *s; mtd = ALLOC(sizeof(MultiTimerData)); absolute_timeout(timeout, &(mtd->when)); @@ -12183,23 +12185,17 @@ static MultiTimerData *add_multi_timer(MultiTimerData **first, ErlDrvPort port, break; } } - if (!p || p->when.megasecs > mtd->when.megasecs) { - goto found; - } - for (; p!= NULL; s = p, p = p->next) { + for (; p!= NULL && eq_mega(p, mtd); s = p, p = p->next) { if (p->when.secs >= mtd->when.secs) { break; } } - if (!p || p->when.secs > mtd->when.secs) { - goto found; - } - for (; p!= NULL; s = p, p = p->next) { + for (; p!= NULL && eq_mega(p, mtd) && eq_sec(p, mtd); s = p, p = p->next) { if (p->when.microsecs >= mtd->when.microsecs) { break; } } - found: + if (!p) { if (!s) { *first = mtd; @@ -12225,6 +12221,8 @@ static MultiTimerData *add_multi_timer(MultiTimerData **first, ErlDrvPort port, } return mtd; } +#undef eq_mega +#undef eq_sec -- cgit v1.2.3 From 9b683f4bf98500f76a33f098879ee733790baa1f Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Thu, 28 May 2015 10:57:55 +0200 Subject: Reorder scheduler information in crashdumps To make it easier to parse stack trace information from tools --- erts/emulator/beam/erl_process.c | 66 +++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 32 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b64a7f8902..4940ffc4a0 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -12509,38 +12509,6 @@ erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp) { erts_print(to, to_arg, "%T", esdp->current_port->common.id); erts_print(to, to_arg, "\n"); - p = esdp->current_process; - erts_print(to, to_arg, "Current Process: "); - if (esdp->current_process && !(ERTS_TRACE_FLAGS(p) & F_SENSITIVE)) { - flg = erts_smp_atomic32_read_dirty(&p->state); - erts_print(to, to_arg, "%T\n", p->common.id); - - erts_print(to, to_arg, "Current Process State: "); - erts_dump_process_state(to, to_arg, flg); - - erts_print(to, to_arg, "Current Process Internal State: "); - erts_dump_extended_process_state(to, to_arg, flg); - - erts_print(to, to_arg, "Current Process Program counter: %p (", p->i); - print_function_from_pc(to, to_arg, p->i); - erts_print(to, to_arg, ")\n"); - erts_print(to, to_arg, "Current Process CP: %p (", p->cp); - print_function_from_pc(to, to_arg, p->cp); - erts_print(to, to_arg, ")\n"); - - /* Getting this stacktrace can segfault if we are very very - unlucky if called while a process is being garbage collected. - Therefore we only call this on other schedulers if we either - have protection against segfaults, or we know that the process - is not garbage collecting. It *should* always be safe to call - on a process owned by us, even if it is currently being garbage - collected. - */ - erts_print(to, to_arg, "Current Process Limited Stack Trace:\n"); - erts_limited_stack_trace(to, to_arg, p); - } else - erts_print(to, to_arg, "\n"); - for (i = 0; i < ERTS_NO_PROC_PRIO_LEVELS; i++) { erts_print(to, to_arg, "Run Queue "); switch (i) { @@ -12627,6 +12595,40 @@ erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp) { } } erts_print(to, to_arg, "\n"); + + /* This *MUST* to be the last information in scheduler block */ + p = esdp->current_process; + erts_print(to, to_arg, "Current Process: "); + if (esdp->current_process && !(ERTS_TRACE_FLAGS(p) & F_SENSITIVE)) { + flg = erts_smp_atomic32_read_dirty(&p->state); + erts_print(to, to_arg, "%T\n", p->common.id); + + erts_print(to, to_arg, "Current Process State: "); + erts_dump_process_state(to, to_arg, flg); + + erts_print(to, to_arg, "Current Process Internal State: "); + erts_dump_extended_process_state(to, to_arg, flg); + + erts_print(to, to_arg, "Current Process Program counter: %p (", p->i); + print_function_from_pc(to, to_arg, p->i); + erts_print(to, to_arg, ")\n"); + erts_print(to, to_arg, "Current Process CP: %p (", p->cp); + print_function_from_pc(to, to_arg, p->cp); + erts_print(to, to_arg, ")\n"); + + /* Getting this stacktrace can segfault if we are very very + unlucky if called while a process is being garbage collected. + Therefore we only call this on other schedulers if we either + have protection against segfaults, or we know that the process + is not garbage collecting. It *should* always be safe to call + on a process owned by us, even if it is currently being garbage + collected. + */ + erts_print(to, to_arg, "Current Process Limited Stack Trace:\n"); + erts_limited_stack_trace(to, to_arg, p); + } else + erts_print(to, to_arg, "\n"); + } /* -- cgit v1.2.3 From 03ae2f86e6a6a4b013fc89a814221fbfe1fe5cf3 Mon Sep 17 00:00:00 2001 From: Tomas Abrahamsson Date: Fri, 5 Jun 2015 18:38:40 +0200 Subject: Add forgotten argument to example in erl_nif doc In the documentation for erl_nif, in the map iterator example, the iterator argument was forgotten in the call to function enif_map_iterator_create. This is now fixed. --- erts/doc/src/erl_nif.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index f64381c99d..412c0e02ac 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -1177,7 +1177,7 @@ typedef enum { ERL_NIF_TERM key, value; ErlNifMapIterator iter; -enif_map_iterator_create(env, my_map, ERL_NIF_MAP_ITERATOR_FIRST); +enif_map_iterator_create(env, my_map, &iter, ERL_NIF_MAP_ITERATOR_FIRST); while (enif_map_iterator_get_pair(env, &iter, &key, &value)) { do_something(key,value); -- cgit v1.2.3 From d79f77985100d2f90a0021f7ebae20ec7c99e93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 5 Jun 2015 16:00:30 +0200 Subject: erts: Fix faulty list optimization in make_internal_hash Reported-by: Rory Byrne --- erts/emulator/beam/utils.c | 8 +++-- erts/emulator/test/map_SUITE.erl | 67 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 965de748c9..cecd88197e 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1140,7 +1140,7 @@ make_hash2(Eterm term) ERTS_UNDEF(hash_xor_pairs, 0); -/* (HCONST * {2, ..., 16}) mod 2^32 */ +/* (HCONST * {2, ..., 22}) mod 2^32 */ #define HCONST_2 0x3c6ef372UL #define HCONST_3 0xdaa66d2bUL #define HCONST_4 0x78dde6e4UL @@ -1161,6 +1161,7 @@ make_hash2(Eterm term) #define HCONST_19 0xbe1e08bbUL #define HCONST_20 0x5c558274UL #define HCONST_21 0xfa8cfc2dUL +#define HCONST_22 0x98c475e6UL #define HASH_MAP_TAIL (_make_header(1,_TAG_HEADER_REF)) #define HASH_MAP_PAIR (_make_header(2,_TAG_HEADER_REF)) @@ -1645,8 +1646,9 @@ make_internal_hash(Eterm term) break; ptr = list_val(term); } - if (c > 0) - UINT32_HASH(sh, HCONST_4); + if (c > 0) + UINT32_HASH_2(sh, (Uint32)c, HCONST_22); + if (is_list(term)) { tmp = CDR(ptr); CONST_HASH(HCONST_17); /* Hash CAR in cons cell */ diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 527b6987fa..b739250aad 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -2615,13 +2615,76 @@ t_erts_internal_order(_Config) when is_list(_Config) -> t_erts_internal_hash(_Config) when is_list(_Config) -> K1 = 0.0, K2 = 0.0/-1, + M = maps:from_list([{I,I}||I<-lists:seq(1,32)]), - M1 = (maps:from_list([{I,I}||I<-lists:seq(1,32)]))#{ K1 => a, K2 => b }, + M1 = M#{ K1 => a, K2 => b }, b = maps:get(K2,M1), - M2 = (maps:from_list([{I,I}||I<-lists:seq(1,32)]))#{ K2 => a, K1 => b }, + M2 = M#{ K2 => a, K1 => b }, b = maps:get(K1,M2), + %% test previously faulty hash list optimization + + M3 = M#{[0] => a, [0,0] => b, [0,0,0] => c, [0,0,0,0] => d}, + a = maps:get([0],M3), + b = maps:get([0,0],M3), + c = maps:get([0,0,0],M3), + d = maps:get([0,0,0,0],M3), + + M4 = M#{{[0]} => a, {[0,0]} => b, {[0,0,0]} => c, {[0,0,0,0]} => d}, + a = maps:get({[0]},M4), + b = maps:get({[0,0]},M4), + c = maps:get({[0,0,0]},M4), + d = maps:get({[0,0,0,0]},M4), + + M5 = M3#{[0,0,0] => e, [0,0,0,0] => f, [0,0,0,0,0] => g, + [0,0,0,0,0,0] => h, [0,0,0,0,0,0,0] => i, + [0,0,0,0,0,0,0,0] => j, [0,0,0,0,0,0,0,0,0] => k}, + + a = maps:get([0],M5), + b = maps:get([0,0],M5), + e = maps:get([0,0,0],M5), + f = maps:get([0,0,0,0],M5), + g = maps:get([0,0,0,0,0],M5), + h = maps:get([0,0,0,0,0,0],M5), + i = maps:get([0,0,0,0,0,0,0],M5), + j = maps:get([0,0,0,0,0,0,0,0],M5), + k = maps:get([0,0,0,0,0,0,0,0,0],M5), + + M6 = M4#{{[0,0,0]} => e, {[0,0,0,0]} => f, {[0,0,0,0,0]} => g, + {[0,0,0,0,0,0]} => h, {[0,0,0,0,0,0,0]} => i, + {[0,0,0,0,0,0,0,0]} => j, {[0,0,0,0,0,0,0,0,0]} => k}, + + a = maps:get({[0]},M6), + b = maps:get({[0,0]},M6), + e = maps:get({[0,0,0]},M6), + f = maps:get({[0,0,0,0]},M6), + g = maps:get({[0,0,0,0,0]},M6), + h = maps:get({[0,0,0,0,0,0]},M6), + i = maps:get({[0,0,0,0,0,0,0]},M6), + j = maps:get({[0,0,0,0,0,0,0,0]},M6), + k = maps:get({[0,0,0,0,0,0,0,0,0]},M6), + + M7 = maps:merge(M5,M6), + + a = maps:get([0],M7), + b = maps:get([0,0],M7), + e = maps:get([0,0,0],M7), + f = maps:get([0,0,0,0],M7), + g = maps:get([0,0,0,0,0],M7), + h = maps:get([0,0,0,0,0,0],M7), + i = maps:get([0,0,0,0,0,0,0],M7), + j = maps:get([0,0,0,0,0,0,0,0],M7), + k = maps:get([0,0,0,0,0,0,0,0,0],M7), + a = maps:get({[0]},M7), + b = maps:get({[0,0]},M7), + e = maps:get({[0,0,0]},M7), + f = maps:get({[0,0,0,0]},M7), + g = maps:get({[0,0,0,0,0]},M7), + h = maps:get({[0,0,0,0,0,0]},M7), + i = maps:get({[0,0,0,0,0,0,0]},M7), + j = maps:get({[0,0,0,0,0,0,0,0]},M7), + k = maps:get({[0,0,0,0,0,0,0,0,0]},M7), ok. t_pdict(_Config) -> -- cgit v1.2.3 From eea59350ddc1f8c2d86e10f5d38f0cda2f9081f3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 8 Jun 2015 14:52:41 +0200 Subject: erts: Refactor arg swapping for maps:merge --- erts/emulator/beam/erl_map.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index a1bd39dbc8..5802ec76ba 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -80,7 +80,7 @@ typedef struct { static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB); static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args); -static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB); +static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB, int swap_args); static Eterm hashmap_to_list(Process *p, Eterm map); static Eterm hashmap_keys(Process *p, Eterm map); static Eterm hashmap_values(Process *p, Eterm map); @@ -947,7 +947,7 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { BIF_P->fvalue = BIF_ARG_2; } else if (is_hashmap(BIF_ARG_1)) { if (is_hashmap(BIF_ARG_2)) { - BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2)); + BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2, 0)); } else if (is_flatmap(BIF_ARG_2)) { /* Will always become a tree */ BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_2, BIF_ARG_1, 1)); @@ -1113,12 +1113,12 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args) erts_free(ERTS_ALC_T_TMP, (void *) hxns); ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); - return swap_args ? hashmap_merge(p, tree, res) : hashmap_merge(p, res, tree); + return hashmap_merge(p, res, tree, swap_args); } #define HALLOC_EXTRA 200 -static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { +static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB, int swap_args) { #define PSTACK_TYPE struct HashmapMergePStackType struct HashmapMergePStackType { Eterm *srcA, *srcB; @@ -1133,7 +1133,7 @@ static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { Eterm hdrA, hdrB; Uint32 ahx, bhx; Uint size; /* total key-value counter */ - int keepA = 0; + int keepA = swap_args; unsigned int lvl = 0; DeclareTmpHeap(th,2,p); Eterm res = THE_NON_VALUE; -- cgit v1.2.3 From a6038dac4b387a0f18be7d18467bbf8f83e8c765 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 8 Jun 2015 16:40:39 +0200 Subject: Fix lost aux work flags when setting multiple flags All flags was not always set when setting multiple aux work flags at once. This scenario is fortunately quite uncommon and only caused further delay in memory deallocations. --- erts/emulator/beam/erl_process.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index ea63d20dfa..f4228e0026 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -1193,11 +1193,11 @@ set_aux_work_flags_wakeup_nob(ErtsSchedulerSleepInfo *ssi, ERTS_DBG_CHK_SSI_AUX_WORK(ssi); old_flgs = erts_atomic32_read_nob(&ssi->aux_work); - if ((old_flgs & flgs) == 0) { + if ((old_flgs & flgs) != flgs) { old_flgs = erts_atomic32_read_bor_nob(&ssi->aux_work, flgs); - if ((old_flgs & flgs) == 0) { + if ((old_flgs & flgs) != flgs) { #ifdef ERTS_SMP erts_sched_poke(ssi); #else @@ -1217,7 +1217,7 @@ set_aux_work_flags_wakeup_relb(ErtsSchedulerSleepInfo *ssi, old_flgs = erts_atomic32_read_bor_relb(&ssi->aux_work, flgs); - if ((old_flgs & flgs) == 0) { + if ((old_flgs & flgs) != flgs) { #ifdef ERTS_SMP erts_sched_poke(ssi); #else -- cgit v1.2.3 From 2430a12be964410acaff3fc499419bda7038e76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 9 Jun 2015 16:17:00 +0200 Subject: erts: Tweak statistics_SUITE:scheduler_wall_time/1 --- erts/emulator/test/statistics_SUITE.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index c428be6c5a..16cee81158 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -308,7 +308,7 @@ scheduler_wall_time(Config) when is_list(Config) -> try Schedulers = erlang:system_info(schedulers_online), %% Let testserver and everyone else finish their work - timer:sleep(500), + timer:sleep(1500), %% Empty load EmptyLoad = get_load(), {false, _} = {lists:any(fun(Load) -> Load > 50 end, EmptyLoad),EmptyLoad}, @@ -347,7 +347,7 @@ scheduler_wall_time(Config) when is_list(Config) -> [exit(Pid, kill) || Pid <- [P1|HalfHogs++LastHogs]], AfterLoad = get_load(), - {false,_} = {lists:any(fun(Load) -> Load > 5 end, AfterLoad),AfterLoad}, + {false,_} = {lists:any(fun(Load) -> Load > 25 end, AfterLoad),AfterLoad}, true = erlang:system_flag(scheduler_wall_time, false) after erlang:system_flag(scheduler_wall_time, false) @@ -355,7 +355,7 @@ scheduler_wall_time(Config) when is_list(Config) -> get_load() -> Start = erlang:statistics(scheduler_wall_time), - timer:sleep(500), + timer:sleep(1500), End = erlang:statistics(scheduler_wall_time), lists:reverse(lists:sort(load_percentage(lists:sort(Start),lists:sort(End)))). -- cgit v1.2.3 From f71ce89e2ca069b8526f27aee333deea9209a8b6 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 2 Jun 2015 19:50:47 +0200 Subject: Fix test cases --- erts/emulator/beam/erl_bif_info.c | 9 +- erts/emulator/beam/erl_process.c | 156 +++++++++++++++++++++++--------- erts/emulator/beam/erl_process.h | 21 ++++- erts/emulator/test/long_timers_test.erl | 4 +- erts/emulator/test/timer_bif_SUITE.erl | 3 +- 5 files changed, 139 insertions(+), 54 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index f74aea80a7..ae8a5c266a 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -4050,7 +4050,14 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) } else if (ERTS_IS_ATOM_STR("wait", BIF_ARG_1)) { if (ERTS_IS_ATOM_STR("deallocations", BIF_ARG_2)) { - if (erts_debug_wait_deallocations(BIF_P)) { + int flag = ERTS_DEBUG_WAIT_COMPLETED_DEALLOCATIONS; + if (erts_debug_wait_completed(BIF_P, flag)) { + ERTS_BIF_YIELD_RETURN(BIF_P, am_ok); + } + } + if (ERTS_IS_ATOM_STR("timer_cancellations", BIF_ARG_2)) { + int flag = ERTS_DEBUG_WAIT_COMPLETED_TIMER_CANCELLATIONS; + if (erts_debug_wait_completed(BIF_P, flag)) { ERTS_BIF_YIELD_RETURN(BIF_P, am_ok); } } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 0a8897320d..42aad63f5c 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -508,6 +508,7 @@ dbg_chk_aux_work_val(erts_aint32_t value) #ifdef ERTS_SSI_AUX_WORK_REAP_PORTS valid |= ERTS_SSI_AUX_WORK_REAP_PORTS; #endif + valid |= ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED; if (~valid & value) erl_exit(ERTS_ABORT_EXIT, @@ -1193,11 +1194,11 @@ set_aux_work_flags_wakeup_nob(ErtsSchedulerSleepInfo *ssi, ERTS_DBG_CHK_SSI_AUX_WORK(ssi); old_flgs = erts_atomic32_read_nob(&ssi->aux_work); - if ((old_flgs & flgs) == 0) { + if ((old_flgs & flgs) != flgs) { old_flgs = erts_atomic32_read_bor_nob(&ssi->aux_work, flgs); - if ((old_flgs & flgs) == 0) { + if ((old_flgs & flgs) != flgs) { #ifdef ERTS_SMP erts_sched_poke(ssi); #else @@ -1217,7 +1218,7 @@ set_aux_work_flags_wakeup_relb(ErtsSchedulerSleepInfo *ssi, old_flgs = erts_atomic32_read_bor_relb(&ssi->aux_work, flgs); - if ((old_flgs & flgs) == 0) { + if ((old_flgs & flgs) != flgs) { #ifdef ERTS_SMP erts_sched_poke(ssi); #else @@ -1715,11 +1716,6 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waitin awdp->dd.thr_prgr = wakeup; haw_thr_prgr_soft_wakeup(awdp, wakeup); } - else if (awdp->dd.completed_callback) { - awdp->dd.completed_callback(awdp->dd.completed_arg); - awdp->dd.completed_callback = NULL; - awdp->dd.completed_arg = NULL; - } return aux_work & ~ERTS_SSI_AUX_WORK_DD; } @@ -1761,11 +1757,6 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, i } else { unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD_THR_PRGR); - if (awdp->dd.completed_callback) { - awdp->dd.completed_callback(awdp->dd.completed_arg); - awdp->dd.completed_callback = NULL; - awdp->dd.completed_arg = NULL; - } } return aux_work & ~ERTS_SSI_AUX_WORK_DD_THR_PRGR; @@ -1955,78 +1946,142 @@ erts_schedule_thr_prgr_later_cleanup_op(void (*later_func)(void *), #endif } -#ifdef ERTS_SMP +static ERTS_INLINE erts_aint32_t +handle_debug_wait_completed(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) +{ + ErtsSchedulerSleepInfo *ssi = awdp->ssi; + erts_aint32_t saved_aux_work, flags; + +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif + + flags = awdp->debug.wait_completed.flags; + + if (aux_work & flags) + return aux_work; -static erts_atomic32_t completed_dealloc_count; + saved_aux_work = erts_atomic32_read_acqb(&ssi->aux_work); + + if (saved_aux_work & flags) + return aux_work & ~ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED; + + awdp->debug.wait_completed.callback(awdp->debug.wait_completed.arg); + + awdp->debug.wait_completed.flags = 0; + awdp->debug.wait_completed.callback = NULL; + awdp->debug.wait_completed.arg = NULL; + + unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED); + + return aux_work & ~ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED; +} + +static erts_atomic32_t debug_wait_completed_count; +static int debug_wait_completed_flags; static void -completed_dealloc(void *vproc) +thr_debug_wait_completed(void *vproc) { - if (erts_atomic32_dec_read_mb(&completed_dealloc_count) == 0) { + if (erts_atomic32_dec_read_mb(&debug_wait_completed_count) == 0) { erts_resume((Process *) vproc, (ErtsProcLocks) 0); erts_proc_dec_refc((Process *) vproc); } } static void -setup_completed_dealloc(void *vproc) +setup_thr_debug_wait_completed(void *vproc) { ErtsSchedulerData *esdp = erts_get_scheduler_data(); - ErtsAuxWorkData *awdp = (esdp - ? &esdp->aux_work_data - : aux_thread_aux_work_data); - erts_alloc_fix_alloc_shrink(awdp->sched_id, 0); - set_aux_work_flags_wakeup_nob(awdp->ssi, ERTS_SSI_AUX_WORK_DD); - awdp->dd.completed_callback = completed_dealloc; - awdp->dd.completed_arg = vproc; + ErtsAuxWorkData *awdp; + erts_aint32_t wait_flags, aux_work_flags; +#ifdef ERTS_SMP + awdp = esdp ? &esdp->aux_work_data : aux_thread_aux_work_data; +#else + awdp = &esdp->aux_work_data; +#endif + + wait_flags = 0; + aux_work_flags = ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED; + + if (debug_wait_completed_flags & ERTS_DEBUG_WAIT_COMPLETED_DEALLOCATIONS) { + erts_alloc_fix_alloc_shrink(awdp->sched_id, 0); + wait_flags |= (ERTS_SSI_AUX_WORK_DD + | ERTS_SSI_AUX_WORK_DD_THR_PRGR + | ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP); +#ifdef ERTS_SMP + aux_work_flags |= ERTS_SSI_AUX_WORK_DD; +#endif + } + + if (debug_wait_completed_flags & ERTS_DEBUG_WAIT_COMPLETED_TIMER_CANCELLATIONS) { + wait_flags |= (ERTS_SSI_AUX_WORK_CNCLD_TMRS + | ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR + | ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP); +#ifdef ERTS_SMP + if (awdp->esdp && !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)) + aux_work_flags |= ERTS_SSI_AUX_WORK_CNCLD_TMRS; +#endif + } + + set_aux_work_flags_wakeup_nob(awdp->ssi, aux_work_flags); + + awdp->debug.wait_completed.flags = wait_flags; + awdp->debug.wait_completed.callback = thr_debug_wait_completed; + awdp->debug.wait_completed.arg = vproc; } static void -prep_setup_completed_dealloc(void *vproc) +prep_setup_thr_debug_wait_completed(void *vproc) { - erts_aint32_t count = (erts_aint32_t) (erts_no_schedulers+1); - if (erts_atomic32_dec_read_mb(&completed_dealloc_count) == count) { + erts_aint32_t count = (erts_aint32_t) erts_no_schedulers; +#ifdef ERTS_SMP + count += 1; /* aux thread */ +#endif + if (erts_atomic32_dec_read_mb(&debug_wait_completed_count) == count) { /* scheduler threads */ erts_schedule_multi_misc_aux_work(0, erts_no_schedulers, - setup_completed_dealloc, + setup_thr_debug_wait_completed, vproc); +#ifdef ERTS_SMP /* aux_thread */ erts_schedule_misc_aux_work(0, - setup_completed_dealloc, + setup_thr_debug_wait_completed, vproc); +#endif } } -#endif /* ERTS_SMP */ int -erts_debug_wait_deallocations(Process *c_p) +erts_debug_wait_completed(Process *c_p, int flags) { -#ifndef ERTS_SMP - erts_alloc_fix_alloc_shrink(1, 0); - return 1; -#else /* Only one process at a time can do this */ - erts_aint32_t count = (erts_aint32_t) (2*(erts_no_schedulers+1)); - if (0 == erts_atomic32_cmpxchg_mb(&completed_dealloc_count, + erts_aint32_t count = (erts_aint32_t) (2*erts_no_schedulers); +#ifdef ERTS_SMP + count += 2; /* aux thread */ +#endif + if (0 == erts_atomic32_cmpxchg_mb(&debug_wait_completed_count, count, 0)) { + debug_wait_completed_flags = flags; erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL); erts_proc_inc_refc(c_p); /* scheduler threads */ erts_schedule_multi_misc_aux_work(0, erts_no_schedulers, - prep_setup_completed_dealloc, + prep_setup_thr_debug_wait_completed, (void *) c_p); +#ifdef ERTS_SMP /* aux_thread */ erts_schedule_misc_aux_work(0, - prep_setup_completed_dealloc, + prep_setup_thr_debug_wait_completed, (void *) c_p); +#endif return 1; } return 0; -#endif } @@ -2227,6 +2282,14 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting) HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_REAP_PORTS, handle_reap_ports); + /* + * ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED *need* to be + * the last flag checked! + */ + + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED, + handle_debug_wait_completed); + ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); #ifdef ERTS_SMP @@ -5337,8 +5400,6 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp) awdp->latest_wakeup = ERTS_THR_PRGR_VAL_FIRST; awdp->misc.thr_prgr = ERTS_THR_PRGR_VAL_WAITING; awdp->dd.thr_prgr = ERTS_THR_PRGR_VAL_WAITING; - awdp->dd.completed_callback = NULL; - awdp->dd.completed_arg = NULL; awdp->cncld_tmrs.thr_prgr = ERTS_THR_PRGR_VAL_WAITING; awdp->later_op.thr_prgr = ERTS_THR_PRGR_VAL_FIRST; awdp->later_op.size = 0; @@ -5369,6 +5430,9 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp) awdp->delayed_wakeup.sched2jix[i] = -1; } #endif + awdp->debug.wait_completed.flags = 0; + awdp->debug.wait_completed.callback = NULL; + awdp->debug.wait_completed.arg = NULL; } static void @@ -5677,11 +5741,11 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online init_swtreq_alloc(); #endif + erts_atomic32_init_nob(&debug_wait_completed_count, 0); /* debug only */ + debug_wait_completed_flags = 0; #ifdef ERTS_SMP - erts_atomic32_init_nob(&completed_dealloc_count, 0); /* debug only */ - aux_thread_aux_work_data = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA, sizeof(ErtsAuxWorkData)); @@ -12459,6 +12523,8 @@ erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp) { erts_print(to, to_arg, "MSEG_CACHE_CHECK"); break; case ERTS_SSI_AUX_WORK_REAP_PORTS: erts_print(to, to_arg, "REAP_PORTS"); break; + case ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED: + erts_print(to, to_arg, "DEBUG_WAIT_COMPLETED"); break; default: erts_print(to, to_arg, "UNKNOWN(%d)", flg); break; } diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index b1c30e7652..6578d33a11 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -270,6 +270,9 @@ typedef enum { /* * Keep ERTS_SSI_AUX_WORK flags in expected frequency order relative * eachother. Most frequent - lowest bit number. + * + * ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED *need* to be highest bit + * and last flag checked... */ #define ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP (((erts_aint32_t) 1) << 0) @@ -288,8 +291,9 @@ typedef enum { #define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 13) #define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 14) #define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 15) +#define ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED (((erts_aint32_t) 1) << 16) -#define ERTS_SSI_AUX_WORK_MAX 16 +#define ERTS_SSI_AUX_WORK_MAX 17 typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo; @@ -515,8 +519,6 @@ typedef struct { #ifdef ERTS_SMP struct { ErtsThrPrgrVal thr_prgr; - void (*completed_callback)(void *); - void (*completed_arg)(void *); } dd; struct { ErtsThrPrgrVal thr_prgr; @@ -545,6 +547,13 @@ typedef struct { ErtsDelayedAuxWorkWakeupJob *job; } delayed_wakeup; #endif + struct { + struct { + erts_aint32_t flags; + void (*callback)(void *); + void *arg; + } wait_completed; + } debug; } ErtsAuxWorkData; #ifdef ERTS_DIRTY_SCHEDULERS @@ -1692,7 +1701,11 @@ Eterm erts_get_reader_groups_map(Process *c_p); Eterm erts_debug_reader_groups_map(Process *c_p, int groups); Uint erts_debug_nbalance(void); -int erts_debug_wait_deallocations(Process *c_p); + +#define ERTS_DEBUG_WAIT_COMPLETED_DEALLOCATIONS (1 << 0) +#define ERTS_DEBUG_WAIT_COMPLETED_TIMER_CANCELLATIONS (1 << 1) + +int erts_debug_wait_completed(Process *c_p, int flags); Uint erts_process_memory(Process *c_p); diff --git a/erts/emulator/test/long_timers_test.erl b/erts/emulator/test/long_timers_test.erl index f381332b51..8dd960ffd4 100644 --- a/erts/emulator/test/long_timers_test.erl +++ b/erts/emulator/test/long_timers_test.erl @@ -28,7 +28,7 @@ -define(MAX_TIMEOUT, 60). % Minutes --define(MAX_LATE_MS, 10*1000). % Milliseconds +-define(MAX_LATE_MS, 15*1000). % Milliseconds -define(REG_NAME, '___LONG___TIMERS___TEST___SERVER___'). -define(DRV_NAME, timer_driver). @@ -196,8 +196,8 @@ driver(Timeout) -> end. bif_timer(Timeout) -> - Tmr = erlang:start_timer(Timeout, self(), ok), Start = erlang:monotonic_time(), + Tmr = erlang:start_timer(Timeout, self(), ok), receive {get_result, ?REG_NAME} -> ?REG_NAME ! #timeout_rec{pid = self(), diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index d406456f98..3939ae9575 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -631,7 +631,6 @@ auto_cancel_yielding(Config) when is_list(Config) -> true = mem_larger_than(Mem), exit(P, bang), wait_until(fun () -> process_is_cleaned_up(P) end), - receive after 1000 -> ok end, Mem = mem(), ok. @@ -747,7 +746,7 @@ mem_larger_than(Mem) -> mem() > Mem. mem() -> - erts_debug:set_internal_state(wait, deallocations), + erts_debug:set_internal_state(wait, timer_cancellations), erts_debug:set_internal_state(wait, deallocations), case mem_get() of {-1, -1} -> no_fix_alloc; -- cgit v1.2.3 From c7f308d1bfaf1f2f6f597ebda6679da03e3e732e Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 2 Jun 2015 21:48:19 +0200 Subject: Aux work flag descriptions --- erts/emulator/beam/erl_process.c | 82 +++++++++++++++++++------------------ erts/emulator/beam/erl_process.h | 87 +++++++++++++++++++++++++++++----------- 2 files changed, 107 insertions(+), 62 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 42aad63f5c..3cca13ae25 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -165,6 +165,9 @@ Uint erts_no_dirty_cpu_schedulers; Uint erts_no_dirty_io_schedulers; #endif +static char *erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_NO_FLAGS] = {0}; +int erts_aux_work_no_flags = ERTS_SSI_AUX_WORK_NO_FLAGS; + #define ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_VERY_LAZY (4*1024*1024) #define ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_LAZY (512*1024) #define ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_MEDIUM (64*1024) @@ -567,6 +570,41 @@ erts_pre_init_process(void) erts_tsd_key_create(&sched_data_key, "erts_sched_data_key"); #endif + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP_IX] + = "DELAYED_AW_WAKEUP"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_DD_IX] + = "DD"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_DD_THR_PRGR_IX] + = "DD_THR_PRGR"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC_IX] + = "FIX_ALLOC_DEALLOC"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM_IX] + = "FIX_ALLOC_LOWER_LIM"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP_IX] + = "THR_PRGR_LATER_OP"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_CNCLD_TMRS_IX] + = "CNCLD_TMRS"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR_IX] + = "CNCLD_TMRS_THR_PRGR"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_ASYNC_READY_IX] + = "ASYNC_READY"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN_IX] + = "ASYNC_READY_CLEAN"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX] + = "MISC_THR_PRGR"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MISC_IX] + = "MISC"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_CHECK_CHILDREN_IX] + = "CHECK_CHILDREN"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_SET_TMO_IX] + = "SET_TMO"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX] + = "MSEG_CACHE_CHECK"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_REAP_PORTS_IX] + = "REAP_PORTS"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED_IX] + = "DEBUG_WAIT_COMPLETED"; + #ifdef ERTS_ENABLE_LOCK_CHECK { int ix; @@ -12487,47 +12525,13 @@ erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp) { flg = erts_atomic32_read_dirty(&esdp->ssi->aux_work); erts_print(to, to_arg, "Scheduler Sleep Info Aux Work: "); - for (i = 0; i < ERTS_SSI_AUX_WORK_MAX && flg; i++) { + for (i = 0; i < ERTS_SSI_AUX_WORK_NO_FLAGS && flg; i++) { erts_aint32_t chk = (1 << i); if (flg & chk) { - switch (chk) { - case ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP: - erts_print(to, to_arg, "DELAYED_AW_WAKEUP"); break; - case ERTS_SSI_AUX_WORK_DD: - erts_print(to, to_arg, "DELAYED_DEALLOC"); break; - case ERTS_SSI_AUX_WORK_DD_THR_PRGR: - erts_print(to, to_arg, "DELAYED_DEALLOC_THR_PRGR"); break; - case ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC: - erts_print(to, to_arg, "FIX_ALLOC_DEALLOC"); break; - case ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM: - erts_print(to, to_arg, "FIX_ALLOC_LOWER_LIM"); break; - case ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP: - erts_print(to, to_arg, "THR_PRGR_LATER_OP"); break; - case ERTS_SSI_AUX_WORK_CNCLD_TMRS: - erts_print(to, to_arg, "CANCELED_TIMERS"); break; - case ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR: - erts_print(to, to_arg, "CANCELED_TIMERS_THR_PRGR"); break; - case ERTS_SSI_AUX_WORK_ASYNC_READY: - erts_print(to, to_arg, "ASYNC_READY"); break; - case ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN: - erts_print(to, to_arg, "ASYNC_READY_CLEAN"); break; - case ERTS_SSI_AUX_WORK_MISC_THR_PRGR: - erts_print(to, to_arg, "MISC_THR_PRGR"); break; - case ERTS_SSI_AUX_WORK_MISC: - erts_print(to, to_arg, "MISC"); break; - case ERTS_SSI_AUX_WORK_CHECK_CHILDREN: - erts_print(to, to_arg, "CHECK_CHILDREN"); break; - case ERTS_SSI_AUX_WORK_SET_TMO: - erts_print(to, to_arg, "SET_TMO"); break; - case ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK: - erts_print(to, to_arg, "MSEG_CACHE_CHECK"); break; - case ERTS_SSI_AUX_WORK_REAP_PORTS: - erts_print(to, to_arg, "REAP_PORTS"); break; - case ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED: - erts_print(to, to_arg, "DEBUG_WAIT_COMPLETED"); break; - default: - erts_print(to, to_arg, "UNKNOWN(%d)", flg); break; - } + if (erts_aux_work_flag_descr[i]) + erts_print(to, to_arg, "%s", erts_aux_work_flag_descr[i]); + else + erts_print(to, to_arg, "1<<%d", i); if (flg > chk) erts_print(to, to_arg, " | "); flg -= chk; diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 6578d33a11..c06ac7affa 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -268,32 +268,73 @@ typedef enum { | ERTS_SSI_FLG_SUSPENDED) /* - * Keep ERTS_SSI_AUX_WORK flags in expected frequency order relative - * eachother. Most frequent - lowest bit number. + * Keep ERTS_SSI_AUX_WORK flags ordered in expected frequency + * order relative eachother. Most frequent at lowest at lowest + * index. * - * ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED *need* to be highest bit - * and last flag checked... + * ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED_IX *need* to be + * highest index... + * + * Remember to update description in erts_pre_init_process() + * when adding new flags... */ -#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_CNCLD_TMRS (((erts_aint32_t) 1) << 6) -#define ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR (((erts_aint32_t) 1) << 7) -#define ERTS_SSI_AUX_WORK_ASYNC_READY (((erts_aint32_t) 1) << 8) -#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 9) -#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 10) -#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 11) -#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 12) -#define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 13) -#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 14) -#define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 15) -#define ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED (((erts_aint32_t) 1) << 16) - -#define ERTS_SSI_AUX_WORK_MAX 17 +typedef enum { + ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP_IX, + ERTS_SSI_AUX_WORK_DD_IX, + ERTS_SSI_AUX_WORK_DD_THR_PRGR_IX, + ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC_IX, + ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM_IX, + ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP_IX, + ERTS_SSI_AUX_WORK_CNCLD_TMRS_IX, + ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR_IX, + ERTS_SSI_AUX_WORK_ASYNC_READY_IX, + ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN_IX, + ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX, + ERTS_SSI_AUX_WORK_MISC_IX, + ERTS_SSI_AUX_WORK_CHECK_CHILDREN_IX, + ERTS_SSI_AUX_WORK_SET_TMO_IX, + ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX, + ERTS_SSI_AUX_WORK_REAP_PORTS_IX, + ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED_IX, /* SHOULD be last flag index */ + + ERTS_SSI_AUX_WORK_NO_FLAGS /* Not a flag index... */ +} ErtsSsiAuxWorkFlagIndex; + +#define ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP_IX) +#define ERTS_SSI_AUX_WORK_DD \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_DD_IX) +#define ERTS_SSI_AUX_WORK_DD_THR_PRGR \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_DD_THR_PRGR_IX) +#define ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC_IX) +#define ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM_IX) +#define ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP_IX) +#define ERTS_SSI_AUX_WORK_CNCLD_TMRS \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_CNCLD_TMRS_IX) +#define ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR_IX) +#define ERTS_SSI_AUX_WORK_ASYNC_READY \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_ASYNC_READY_IX) +#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN_IX) +#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX) +#define ERTS_SSI_AUX_WORK_MISC \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_IX) +#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_CHECK_CHILDREN_IX) +#define ERTS_SSI_AUX_WORK_SET_TMO \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_SET_TMO_IX) +#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX) +#define ERTS_SSI_AUX_WORK_REAP_PORTS \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_REAP_PORTS_IX) +#define ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED_IX) typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo; -- cgit v1.2.3 From 46efe0d85bc2f55dd432f403f20d4841b07023ed Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 4 Jun 2015 16:45:41 +0200 Subject: Disable accessor timer option --- erts/emulator/beam/erl_alloc.c | 4 +++ erts/emulator/beam/erl_alloc.types | 2 +- erts/emulator/beam/erl_hl_timer.c | 57 ++++++++++++++++++++++++++++----- erts/emulator/beam/erl_process.c | 8 +++++ erts/emulator/beam/erl_process.h | 2 ++ erts/emulator/test/timer_bif_SUITE.erl | 9 +++--- erts/preloaded/ebin/erlang.beam | Bin 101808 -> 101808 bytes erts/preloaded/src/erlang.erl | 4 +-- 8 files changed, 71 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index dcae5509ec..d11f24220a 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -581,8 +581,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) = erts_timer_type_size(ERTS_ALC_T_HL_PTIMER); fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_BIF_TIMER)] = erts_timer_type_size(ERTS_ALC_T_BIF_TIMER); +#ifdef ERTS_BTM_ACCESSOR_SUPPORT fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_ABIF_TIMER)] = erts_timer_type_size(ERTS_ALC_T_ABIF_TIMER); +#endif #ifdef HARD_DEBUG hdbg_init(); @@ -2343,10 +2345,12 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) &size.processes_used, fi, ERTS_ALC_T_BIF_TIMER); +#ifdef ERTS_BTM_ACCESSOR_SUPPORT add_fix_values(&size.processes, &size.processes_used, fi, ERTS_ALC_T_ABIF_TIMER); +#endif } if (want.atom || want.atom_used) { diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 57c506458c..2721e13250 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -167,7 +167,7 @@ type TIMER_SERVICE LONG_LIVED SYSTEM timer_service type LL_PTIMER FIXED_SIZE PROCESSES ll_ptimer type HL_PTIMER FIXED_SIZE PROCESSES hl_ptimer type BIF_TIMER FIXED_SIZE PROCESSES bif_timer -type ABIF_TIMER FIXED_SIZE PROCESSES accessor_bif_timer +# type ABIF_TIMER FIXED_SIZE PROCESSES accessor_bif_timer type TIMER_REQUEST SHORT_LIVED PROCESSES timer_request type BTM_YIELD_STATE SHORT_LIVED PROCESSES btm_yield_state type REG_TABLE STANDARD SYSTEM reg_tab diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 51cd843935..0cb75a8b5f 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -93,11 +93,13 @@ static void hdbg_chk_srv(ErtsHLTimerService *srv); /* Bit 0 to 9 contains scheduler id (see mask below) */ #define ERTS_TMR_ROFLG_HLT (((Uint32) 1) << 10) #define ERTS_TMR_ROFLG_BIF_TMR (((Uint32) 1) << 11) -#define ERTS_TMR_ROFLG_ABIF_TMR (((Uint32) 1) << 12) -#define ERTS_TMR_ROFLG_PRE_ALC (((Uint32) 1) << 13) -#define ERTS_TMR_ROFLG_REG_NAME (((Uint32) 1) << 14) -#define ERTS_TMR_ROFLG_PROC (((Uint32) 1) << 15) -#define ERTS_TMR_ROFLG_PORT (((Uint32) 1) << 16) +#define ERTS_TMR_ROFLG_PRE_ALC (((Uint32) 1) << 12) +#define ERTS_TMR_ROFLG_REG_NAME (((Uint32) 1) << 13) +#define ERTS_TMR_ROFLG_PROC (((Uint32) 1) << 14) +#define ERTS_TMR_ROFLG_PORT (((Uint32) 1) << 15) +#ifdef ERTS_BTM_ACCESSOR_SUPPORT +#define ERTS_TMR_ROFLG_ABIF_TMR (((Uint32) 1) << 16) +#endif #define ERTS_TMR_ROFLG_SID_MASK \ (ERTS_TMR_ROFLG_HLT - (Uint32) 1) @@ -172,15 +174,21 @@ struct ErtsHLTimer_ { Eterm message; ErlHeapFragment *bp; } btm; +#ifdef ERTS_BTM_ACCESSOR_SUPPORT struct { Eterm accessor; ErtsHLTimerTree tree; } abtm; +#endif }; #define ERTS_HL_PTIMER_SIZE offsetof(ErtsHLTimer, btm) +#ifdef ERTS_BTM_ACCESSOR_SUPPORT #define ERTS_BIF_TIMER_SIZE offsetof(ErtsHLTimer, abtm) #define ERTS_ABIF_TIMER_SIZE sizeof(ErtsHLTimer) +#else +#define ERTS_BIF_TIMER_SIZE sizeof(ErtsHLTimer) +#endif typedef struct { ErtsTmrHead head; /* NEED to be first! */ @@ -584,6 +592,8 @@ same_time_list_lookup(ErtsHLTimer *root, ErtsHLTimer *x) #include "erl_rbtree.h" +#ifdef ERTS_BTM_ACCESSOR_SUPPORT + #define ERTS_RBT_PREFIX abtm #define ERTS_RBT_T ErtsHLTimer #define ERTS_RBT_KEY_T Uint32 * @@ -634,6 +644,8 @@ same_time_list_lookup(ErtsHLTimer *root, ErtsHLTimer *x) #include "erl_rbtree.h" +#endif /* ERTS_BTM_ACCESSOR_SUPPORT */ + #ifdef ERTS_SMP static void init_canceled_queue(ErtsHLTCncldTmrQ *cq); #endif @@ -673,7 +685,9 @@ erts_timer_type_size(ErtsAlcType_t type) case ERTS_ALC_T_LL_PTIMER: return sizeof(ErtsTWTimer); case ERTS_ALC_T_HL_PTIMER: return ERTS_HL_PTIMER_SIZE; case ERTS_ALC_T_BIF_TIMER: return ERTS_BIF_TIMER_SIZE; +#ifdef ERTS_BTM_ACCESSOR_SUPPORT case ERTS_ALC_T_ABIF_TIMER: return ERTS_ABIF_TIMER_SIZE; +#endif default: ERTS_INTERNAL_ERROR("Unknown type"); } return 0; @@ -852,8 +866,10 @@ hl_timer_destroy(ErtsHLTimer *tmr) else { if (roflgs & ERTS_TMR_ROFLG_PRE_ALC) bif_timer_pre_free(tmr); +#ifdef ERTS_BTM_ACCESSOR_SUPPORT else if (roflgs & ERTS_TMR_ROFLG_ABIF_TMR) erts_free(ERTS_ALC_T_ABIF_TIMER, tmr); +#endif else erts_free(ERTS_ALC_T_BIF_TIMER, tmr); } @@ -948,6 +964,8 @@ check_canceled_queue(ErtsSchedulerData *esdp, ErtsHLTimerService *srv) #endif } +#ifdef ERTS_BTM_ACCESSOR_SUPPORT + static void hlt_delete_abtm(ErtsHLTimer *tmr) { @@ -971,6 +989,8 @@ hlt_delete_abtm(ErtsHLTimer *tmr) } } +#endif + static ErtsHLTimer * create_hl_timer(ErtsSchedulerData *esdp, ErtsMonotonicTime timeout_pos, @@ -982,7 +1002,9 @@ create_hl_timer(ErtsSchedulerData *esdp, ErtsHLTimer *tmr, *st_tmr; erts_aint32_t refc; Uint32 roflgs; +#ifdef ERTS_BTM_ACCESSOR_SUPPORT int is_abif_tmr = is_bif_tmr && is_value(acsr) && acsr != rcvr; +#endif check_canceled_queue(esdp, srv); @@ -1001,10 +1023,12 @@ create_hl_timer(ErtsSchedulerData *esdp, } else { alloc_bif_timer: +#ifdef ERTS_BTM_ACCESSOR_SUPPORT if (is_abif_tmr) tmr = erts_alloc(ERTS_ALC_T_ABIF_TIMER, ERTS_ABIF_TIMER_SIZE); else +#endif tmr = erts_alloc(ERTS_ALC_T_BIF_TIMER, ERTS_BIF_TIMER_SIZE); } @@ -1057,6 +1081,7 @@ create_hl_timer(ErtsSchedulerData *esdp, tmr->btm.refn[2] = refn[2]; tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; +#ifdef ERTS_BTM_ACCESSOR_SUPPORT if (is_abif_tmr) { Process *aproc; roflgs |= ERTS_TMR_ROFLG_ABIF_TMR; @@ -1071,6 +1096,7 @@ create_hl_timer(ErtsSchedulerData *esdp, erts_smp_proc_unlock(aproc, ERTS_PROC_LOCK_BTM); } } +#endif } tmr->head.roflgs = roflgs; @@ -1120,8 +1146,10 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs) Uint32 is_reg_name = (roflgs & ERTS_TMR_ROFLG_REG_NAME); ERTS_HLT_ASSERT(roflgs & ERTS_TMR_ROFLG_BIF_TMR); +#ifdef ERTS_BTM_ACCESSOR_SUPPORT if (tmr->head.roflgs & ERTS_TMR_ROFLG_ABIF_TMR) hlt_delete_abtm(tmr); +#endif if (is_reg_name) { Eterm pid; @@ -1300,8 +1328,10 @@ hlt_delete_timer(ErtsSchedulerData *esdp, ErtsHLTimer *tmr) tmr->btm.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; } +#ifdef ERTS_BTM_ACCESSOR_SUPPORT if (tmr->head.roflgs & ERTS_TMR_ROFLG_ABIF_TMR) hlt_delete_abtm(tmr); +#endif } if (tmr->time.tree.parent == ERTS_HLT_PFIELD_NOT_IN_TABLE) { @@ -1943,8 +1973,10 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp, */ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_BTM); tmr = proc_btm_rbt_lookup(c_p->bif_timers, trefn); +#ifdef ERTS_BTM_ACCESSOR_SUPPORT if (!tmr) tmr = abtm_rbt_lookup(c_p->accessor_bif_timers, trefn); +#endif erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_BTM); if (!tmr) return 0; @@ -2184,11 +2216,13 @@ parse_bif_timer_options(Eterm option_list, int *async, int *info, if (!abs || !bool_arg(tp[2], abs)) return 0; break; +#ifdef ERTS_BTM_ACCESSOR_SUPPORT case am_accessor: if (!accessor || is_not_internal_pid(tp[2])) return 0; *accessor = tp[2]; break; +#endif default: return 0; } @@ -2250,7 +2284,9 @@ typedef struct { ErtsBifTimers *bif_timers; union { proc_btm_rbt_yield_state_t proc_btm_yield_state; +#ifdef ERTS_BTM_ACCESSOR_SUPPORT abtm_rbt_yield_state_t abtm_yield_state; +#endif } u; } ErtsBifTimerYieldState; @@ -2291,6 +2327,8 @@ int erts_cancel_bif_timers(Process *p, ErtsBifTimers *btm, void **vyspp) return res; } +#ifdef ERTS_BTM_ACCESSOR_SUPPORT + static void detach_bif_timer(ErtsHLTimer *tmr, void *vesdp) { @@ -2335,6 +2373,8 @@ int erts_detach_accessor_bif_timers(Process *p, ErtsBifTimers *btm, void **vyspp return res; } +#endif /* ERTS_BTM_ACCESSOR_SUPPORT */ + static ERTS_INLINE int parse_timeout_pos(ErtsSchedulerData *esdp, Eterm arg, ErtsMonotonicTime *conv_arg, int abs, @@ -2525,7 +2565,8 @@ set_proc_timer_common(Process *c_p, ErtsSchedulerData *esdp, Sint64 tmo, else tmr = (void *) create_hl_timer(esdp, timeout_pos, short_time, 0, (void *) c_p, - c_p->common.id, NIL, NIL, NULL); + c_p->common.id, THE_NON_VALUE, + NIL, NULL); erts_smp_atomic_set_relb(&c_p->common.timer, (erts_aint_t) tmr); } } @@ -2613,8 +2654,8 @@ erts_set_port_timer(Port *c_prt, Sint64 tmo) else tmr = (void *) create_hl_timer(esdp, timeout_pos, 0, 0, (void *) c_prt, - c_prt->common.id, NIL, NIL, - NULL); + c_prt->common.id, THE_NON_VALUE, + NIL, NULL); erts_smp_atomic_set_relb(&c_prt->common.timer, (erts_aint_t) tmr); } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 3cca13ae25..410f0214b9 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -10922,7 +10922,9 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->msg_inq.len = 0; #endif p->bif_timers = NULL; +#ifdef ERTS_BTM_ACCESSOR_SUPPORT p->accessor_bif_timers = NULL; +#endif p->mbuf = NULL; p->mbuf_sz = 0; p->psd = NULL; @@ -11102,7 +11104,9 @@ void erts_init_empty_process(Process *p) p->msg.save = &p->msg.first; p->msg.len = 0; p->bif_timers = NULL; +#ifdef ERTS_BTM_ACCESSOR_SUPPORT p->accessor_bif_timers = NULL; +#endif p->dictionary = NULL; p->seq_trace_clock = 0; p->seq_trace_lastcnt = 0; @@ -11197,7 +11201,9 @@ erts_debug_verify_clean_empty_process(Process* p) ASSERT(p->msg.first == NULL); ASSERT(p->msg.len == 0); ASSERT(p->bif_timers == NULL); +#ifdef ERTS_BTM_ACCESSOR_SUPPORT ASSERT(p->accessor_bif_timers == NULL); +#endif ASSERT(p->dictionary == NULL); ASSERT(p->catches == 0); ASSERT(p->cp == NULL); @@ -12153,6 +12159,7 @@ erts_continue_exit_process(Process *p) p->bif_timers = NULL; } +#ifdef ERTS_BTM_ACCESSOR_SUPPORT if (p->accessor_bif_timers) { if (erts_detach_accessor_bif_timers(p, p->accessor_bif_timers, @@ -12163,6 +12170,7 @@ erts_continue_exit_process(Process *p) ASSERT(erts_proc_read_refc(p) > 0); p->accessor_bif_timers = NULL; } +#endif #ifdef ERTS_SMP if (p->flags & F_HAVE_BLCKD_MSCHED) { diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index c06ac7affa..1da1239609 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -975,7 +975,9 @@ struct process { ErlMessageQueue msg; /* Message queue */ ErtsBifTimers *bif_timers; /* Bif timers aiming at this process */ +#ifdef ERTS_BTM_ACCESSOR_SUPPORT ErtsBifTimers *accessor_bif_timers; /* Accessor bif timers */ +#endif ProcDict *dictionary; /* Process dictionary, may be NULL */ diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index 3939ae9575..f41fc7552e 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -29,7 +29,8 @@ read_timer_trivial/1, read_timer/1, read_timer_async/1, cleanup/1, evil_timers/1, registered_process/1, same_time_yielding/1, same_time_yielding_with_cancel/1, same_time_yielding_with_cancel_other/1, - same_time_yielding_with_cancel_other_accessor/1, auto_cancel_yielding/1]). +% same_time_yielding_with_cancel_other_accessor/1, + auto_cancel_yielding/1]). -include_lib("test_server/include/test_server.hrl"). @@ -67,7 +68,7 @@ all() -> cleanup, evil_timers, registered_process, same_time_yielding, same_time_yielding_with_cancel, same_time_yielding_with_cancel_other, - same_time_yielding_with_cancel_other_accessor, +% same_time_yielding_with_cancel_other_accessor, auto_cancel_yielding]. groups() -> @@ -532,8 +533,8 @@ same_time_yielding_with_cancel(Config) when is_list(Config) -> same_time_yielding_with_cancel_other(Config) when is_list(Config) -> same_time_yielding_with_cancel_test(true, false). -same_time_yielding_with_cancel_other_accessor(Config) when is_list(Config) -> - same_time_yielding_with_cancel_test(true, true). +%same_time_yielding_with_cancel_other_accessor(Config) when is_list(Config) -> +% same_time_yielding_with_cancel_test(true, true). do_cancel_tmrs(Tmo, Tmrs, Tester) -> BeginCancel = erlang:convert_time_unit(Tmo, diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index c0fca6aafa..b99dc7339a 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index ea8a911a2c..9d9208d1b5 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1557,7 +1557,7 @@ send_after(_Time, _Dest, _Msg) -> Msg :: term(), Options :: [Option], Abs :: boolean(), - Option :: {abs, Abs}, %% | {accessor, Accessor} undocumented feature for now, + Option :: {abs, Abs}, TimerRef :: reference(). send_after(_Time, _Dest, _Msg, _Options) -> @@ -1645,7 +1645,7 @@ start_timer(_Time, _Dest, _Msg) -> Msg :: term(), Options :: [Option], Abs :: boolean(), - Option :: {abs, Abs}, %% | {accessor, Accessor} undocumented feature for now, + Option :: {abs, Abs}, TimerRef :: reference(). start_timer(_Time, _Dest, _Msg, _Options) -> -- cgit v1.2.3 From ea84ab6c03994f8d6d9f07d8740f0547f8a3cb51 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 4 Jun 2015 22:08:33 +0200 Subject: Callback timer --- erts/emulator/beam/erl_hl_timer.c | 372 +++++++++++++++++++++++++++++++------- erts/emulator/beam/erl_hl_timer.h | 11 +- erts/emulator/beam/erl_time.h | 8 + erts/emulator/beam/time.c | 38 ++++ 4 files changed, 362 insertions(+), 67 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 0cb75a8b5f..8eacb921fe 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -81,6 +81,13 @@ static void hdbg_chk_srv(ErtsHLTimerService *srv); #error "ERTS_REF_NUMBERS changed. Update me..." #endif +typedef enum { + ERTS_TMR_BIF, + ERTS_TMR_PROC, + ERTS_TMR_PORT, + ERTS_TMR_CALLBACK +} ErtsTmrType; + #define ERTS_BIF_TIMER_SHORT_TIME 5000 #ifdef ERTS_SMP @@ -97,8 +104,9 @@ static void hdbg_chk_srv(ErtsHLTimerService *srv); #define ERTS_TMR_ROFLG_REG_NAME (((Uint32) 1) << 13) #define ERTS_TMR_ROFLG_PROC (((Uint32) 1) << 14) #define ERTS_TMR_ROFLG_PORT (((Uint32) 1) << 15) +#define ERTS_TMR_ROFLG_CALLBACK (((Uint32) 1) << 16) #ifdef ERTS_BTM_ACCESSOR_SUPPORT -#define ERTS_TMR_ROFLG_ABIF_TMR (((Uint32) 1) << 16) +#define ERTS_TMR_ROFLG_ABIF_TMR (((Uint32) 1) << 17) #endif #define ERTS_TMR_ROFLG_SID_MASK \ @@ -143,6 +151,7 @@ typedef struct { Uint32 roflgs; erts_smp_atomic32_t refc; union { + void *arg; erts_atomic_t next; } u; } ErtsTmrHead; @@ -158,6 +167,7 @@ struct ErtsHLTimer_ { Process *proc; Port *port; Eterm name; + void (*callback)(void *); } receiver; #ifdef ERTS_HLT_HARD_DEBUG @@ -192,7 +202,10 @@ struct ErtsHLTimer_ { typedef struct { ErtsTmrHead head; /* NEED to be first! */ - void *p; + union { + void *p; + void (*callback)(void *); + } u; ErtsTWheelTimer tw_tmr; } ErtsTWTimer; @@ -348,8 +361,8 @@ refn_is_lt(Uint32 *x, Uint32 *y) #define ERTS_RBT_WANT_SMALLEST #define ERTS_RBT_WANT_LOOKUP_INSERT #define ERTS_RBT_WANT_REPLACE +#define ERTS_RBT_WANT_FOREACH #ifdef ERTS_HLT_HARD_DEBUG -# define ERTS_RBT_WANT_FOREACH # define ERTS_RBT_WANT_LOOKUP #endif #define ERTS_RBT_UNDEF @@ -460,8 +473,6 @@ same_time_list_foreach_destroy_yielding(ErtsHLTimer **root, } } -#ifdef ERTS_HLT_HARD_DEBUG - static ERTS_INLINE void same_time_list_foreach(ErtsHLTimer *root, void (*op)(ErtsHLTimer *, void *), @@ -476,6 +487,8 @@ same_time_list_foreach(ErtsHLTimer *root, } } +#ifdef ERTS_HLT_HARD_DEBUG + static ERTS_INLINE ErtsHLTimer * same_time_list_lookup(ErtsHLTimer *root, ErtsHLTimer *x) { @@ -761,9 +774,9 @@ schedule_tw_timer_destroy(ErtsTWTimer *tmr) * dropped at once... */ if (tmr->head.roflgs & ERTS_TMR_ROFLG_PROC) - erts_proc_dec_refc((Process *) tmr->p); - else - erts_port_dec_refc((Port *) tmr->p); + erts_proc_dec_refc((Process *) tmr->u.p); + else if (tmr->head.roflgs & ERTS_TMR_ROFLG_PORT) + erts_port_dec_refc((Port *) tmr->u.p); erts_schedule_thr_prgr_later_cleanup_op( scheduled_tw_timer_destroy, @@ -785,7 +798,7 @@ static void tw_proc_timeout(void *vtwtp) { ErtsTWTimer *twtp = (ErtsTWTimer *) vtwtp; - Process *proc = (Process *) twtp->p; + Process *proc = (Process *) twtp->u.p; if (proc_timeout_common(proc, vtwtp)) tw_timer_dec_refc(twtp); tw_timer_dec_refc(twtp); @@ -795,7 +808,7 @@ static void tw_port_timeout(void *vtwtp) { ErtsTWTimer *twtp = (ErtsTWTimer *) vtwtp; - Port *port = (Port *) twtp->p; + Port *port = (Port *) twtp->u.p; if (port_timeout_common(port, vtwtp)) tw_timer_dec_refc(twtp); tw_timer_dec_refc(twtp); @@ -815,13 +828,26 @@ cancel_tw_timer(ErtsSchedulerData *esdp, ErtsTWTimer *tmr) erts_twheel_cancel_timer(esdp->timer_wheel, &tmr->tw_tmr); } +static void +tw_callback_timeout(void *vtwtp) +{ + ErtsTWTimer *twtp = (ErtsTWTimer *) vtwtp; + void (*callback)(void *) = twtp->u.callback; + void *arg = twtp->head.u.arg; + tw_timer_dec_refc(twtp); + (*callback)(arg); +} + static ErtsTWTimer * create_tw_timer(ErtsSchedulerData *esdp, - void *p, int is_proc, + ErtsTmrType type, void *p, + void (*callback)(void *), void *arg, ErtsMonotonicTime timeout_pos) { ErtsTWTimer *tmr; void (*timeout_func)(void *); + void (*cancel_func)(void *); + erts_aint32_t refc; tmr = tw_timer_alloc(); erts_twheel_init_timer(&tmr->tw_tmr); @@ -829,24 +855,48 @@ create_tw_timer(ErtsSchedulerData *esdp, tmr->head.roflgs = (Uint32) esdp->no; ERTS_HLT_ASSERT((tmr->head.roflgs & ~ERTS_TMR_ROFLG_SID_MASK) == 0); - tmr->p = p; - if (is_proc) { + + switch (type) { + + case ERTS_TMR_PROC: + tmr->u.p = p; tmr->head.roflgs |= ERTS_TMR_ROFLG_PROC; timeout_func = tw_proc_timeout; + cancel_func = tw_ptimer_cancel; erts_proc_inc_refc((Process *) p); - } - else { + refc = 2; + break; + + case ERTS_TMR_PORT: + tmr->u.p = p; tmr->head.roflgs |= ERTS_TMR_ROFLG_PORT; timeout_func = tw_port_timeout; + cancel_func = tw_ptimer_cancel; erts_port_inc_refc((Port *) p); + refc = 2; + break; + + case ERTS_TMR_CALLBACK: + tmr->head.u.arg = arg; + tmr->u.callback = callback; + + tmr->head.roflgs |= ERTS_TMR_ROFLG_CALLBACK; + timeout_func = tw_callback_timeout; + cancel_func = NULL; + refc = 1; + break; + + default: + ERTS_INTERNAL_ERROR("Unsupported timer type"); + return NULL; } - erts_smp_atomic32_init_nob(&tmr->head.refc, 2); + erts_smp_atomic32_init_nob(&tmr->head.refc, refc); erts_twheel_set_timer(esdp->timer_wheel, &tmr->tw_tmr, timeout_func, - tw_ptimer_cancel, + cancel_func, tmr, timeout_pos); @@ -994,17 +1044,15 @@ hlt_delete_abtm(ErtsHLTimer *tmr) static ErtsHLTimer * create_hl_timer(ErtsSchedulerData *esdp, ErtsMonotonicTime timeout_pos, - int short_time, int is_bif_tmr, + int short_time, ErtsTmrType type, void *rcvrp, Eterm rcvr, Eterm acsr, - Eterm msg, Uint32 *refn) + Eterm msg, Uint32 *refn, + void (*callback)(void *), void *arg) { ErtsHLTimerService *srv = esdp->timer_service; ErtsHLTimer *tmr, *st_tmr; erts_aint32_t refc; Uint32 roflgs; -#ifdef ERTS_BTM_ACCESSOR_SUPPORT - int is_abif_tmr = is_bif_tmr && is_value(acsr) && acsr != rcvr; -#endif check_canceled_queue(esdp, srv); @@ -1012,45 +1060,69 @@ create_hl_timer(ErtsSchedulerData *esdp, roflgs = ((Uint32) esdp->no) | ERTS_TMR_ROFLG_HLT; - if (!is_bif_tmr) + if (type != ERTS_TMR_BIF) { + tmr = erts_alloc(ERTS_ALC_T_HL_PTIMER, ERTS_HL_PTIMER_SIZE); - else if (short_time) { - tmr = bif_timer_pre_alloc(); - if (!tmr) - goto alloc_bif_timer; - roflgs |= ERTS_TMR_ROFLG_PRE_ALC; - } - else { - alloc_bif_timer: -#ifdef ERTS_BTM_ACCESSOR_SUPPORT - if (is_abif_tmr) - tmr = erts_alloc(ERTS_ALC_T_ABIF_TIMER, - ERTS_ABIF_TIMER_SIZE); - else -#endif - tmr = erts_alloc(ERTS_ALC_T_BIF_TIMER, - ERTS_BIF_TIMER_SIZE); - } + tmr->timeout = timeout_pos; - tmr->timeout = timeout_pos; + switch (type) { + + case ERTS_TMR_PROC: + ERTS_HLT_ASSERT(is_internal_pid(rcvr)); - if (!is_bif_tmr) { - if (is_internal_pid(rcvr)) { erts_proc_inc_refc((Process *) rcvrp); tmr->receiver.proc = (Process *) rcvrp; roflgs |= ERTS_TMR_ROFLG_PROC; - } - else { - erts_port_inc_refc((Port *) rcvrp); + refc = 2; + break; + + case ERTS_TMR_PORT: ERTS_HLT_ASSERT(is_internal_port(rcvr)); + erts_port_inc_refc((Port *) rcvrp); tmr->receiver.port = (Port *) rcvrp; roflgs |= ERTS_TMR_ROFLG_PORT; + refc = 2; + break; + + case ERTS_TMR_CALLBACK: + roflgs |= ERTS_TMR_ROFLG_CALLBACK; + tmr->receiver.callback = callback; + tmr->head.u.arg = arg; + refc = 1; + break; + + default: + ERTS_INTERNAL_ERROR("Unsupported timer type"); + return NULL; } - refc = 2; + } - else { + else { /* ERTS_TMR_BIF */ Uint hsz; +#ifdef ERTS_BTM_ACCESSOR_SUPPORT + int is_abif_tmr = is_value(acsr) && acsr != rcvr; +#endif + + if (short_time) { + tmr = bif_timer_pre_alloc(); + if (!tmr) + goto alloc_bif_timer; + roflgs |= ERTS_TMR_ROFLG_PRE_ALC; + } + else { + alloc_bif_timer: +#ifdef ERTS_BTM_ACCESSOR_SUPPORT + if (is_abif_tmr) + tmr = erts_alloc(ERTS_ALC_T_ABIF_TIMER, + ERTS_ABIF_TIMER_SIZE); + else +#endif + tmr = erts_alloc(ERTS_ALC_T_BIF_TIMER, + ERTS_BIF_TIMER_SIZE); + } + + tmr->timeout = timeout_pos; roflgs |= ERTS_TMR_ROFLG_BIF_TMR; if (is_internal_pid(rcvr)) { @@ -1081,6 +1153,7 @@ create_hl_timer(ErtsSchedulerData *esdp, tmr->btm.refn[2] = refn[2]; tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; + #ifdef ERTS_BTM_ACCESSOR_SUPPORT if (is_abif_tmr) { Process *aproc; @@ -1097,6 +1170,8 @@ create_hl_timer(ErtsSchedulerData *esdp, } } #endif + + btm_rbt_insert(&srv->btm_tree, tmr); } tmr->head.roflgs = roflgs; @@ -1124,9 +1199,6 @@ create_hl_timer(ErtsSchedulerData *esdp, if (st_tmr) same_time_list_insert(&st_tmr->time.tree.same_time, tmr); - if (is_bif_tmr) - btm_rbt_insert(&srv->btm_tree, tmr); - #ifdef ERTS_HLT_HARD_DEBUG tmr->pending_timeout = 0; #endif @@ -1231,10 +1303,13 @@ static void hlt_timeout(ErtsHLTimer *tmr, void *vsrv) hlt_bif_timer_timeout(tmr, roflgs); else if (roflgs & ERTS_TMR_ROFLG_PROC) hlt_proc_timeout(tmr); - else { - ERTS_HLT_ASSERT(roflgs & ERTS_TMR_ROFLG_PORT); + else if (roflgs & ERTS_TMR_ROFLG_PORT) hlt_port_timeout(tmr); + else { + ERTS_HLT_ASSERT(roflgs & ERTS_TMR_ROFLG_CALLBACK); + (*tmr->receiver.callback)(tmr->head.u.arg); } + } tmr->time.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; @@ -1700,8 +1775,9 @@ setup_bif_timer(Process *c_p, ErtsMonotonicTime timeout_pos, tmo_msg = wrap ? TUPLE3(tmp_hp, am_timeout, ref, msg) : msg; - tmr = create_hl_timer(esdp, timeout_pos, short_time, 1, NULL, - rcvr, acsr, tmo_msg, internal_ref_numbers(ref)); + tmr = create_hl_timer(esdp, timeout_pos, short_time, + ERTS_TMR_BIF, NULL, rcvr, acsr, tmo_msg, + internal_ref_numbers(ref), NULL, NULL); UnUseTmpHeap(4, c_p); @@ -2538,6 +2614,80 @@ BIF_RETTYPE read_timer_2(BIF_ALIST_2) return ret; } +static void +start_callback_timer(ErtsSchedulerData *esdp, + int twt, + ErtsMonotonicTime timeout_pos, + void (*callback)(void *), + void *arg) + +{ + if (twt) + create_tw_timer(esdp, ERTS_TMR_CALLBACK, NULL, + callback, arg, timeout_pos); + else + create_hl_timer(esdp, timeout_pos, 0, + ERTS_TMR_CALLBACK, NULL, + NIL, THE_NON_VALUE, NIL, + NULL, callback, arg); +} + +typedef struct { + int twt; + ErtsMonotonicTime timeout_pos; + void (*callback)(void *); + void *arg; +} ErtsStartCallbackTimerRequest; + +static void +scheduled_start_callback_timer(void *vsctr) +{ + ErtsStartCallbackTimerRequest *sctr + = (ErtsStartCallbackTimerRequest *) vsctr; + + start_callback_timer(erts_get_scheduler_data(), + sctr->twt, + sctr->timeout_pos, + sctr->callback, + sctr->arg); + + erts_free(ERTS_ALC_T_TIMER_REQUEST, vsctr); +} + +void +erts_start_timer_callback(ErtsMonotonicTime tmo, + void (*callback)(void *), + void *arg) +{ + ErtsSchedulerData *esdp; + ErtsMonotonicTime timeout_pos; + int twt; + + esdp = erts_get_scheduler_data(); + timeout_pos = get_timeout_pos(erts_get_monotonic_time(esdp), + tmo); + twt = tmo < ERTS_TIMER_WHEEL_MSEC; + + if (esdp) + start_callback_timer(esdp, + twt, + timeout_pos, + callback, + arg); + else { + ErtsStartCallbackTimerRequest *sctr; + sctr = erts_alloc(ERTS_ALC_T_TIMER_REQUEST, + sizeof(ErtsStartCallbackTimerRequest)); + sctr->twt = twt; + sctr->timeout_pos = timeout_pos; + sctr->callback = callback; + sctr->arg = arg; + erts_schedule_misc_aux_work(1, + scheduled_start_callback_timer, + (void *) sctr); + } +} + /* * Process and Port timer functionality. * @@ -2561,12 +2711,13 @@ set_proc_timer_common(Process *c_p, ErtsSchedulerData *esdp, Sint64 tmo, c_p->flags &= ~F_TIMO; if (tmo < ERTS_TIMER_WHEEL_MSEC) - tmr = (void *) create_tw_timer(esdp, (void *) c_p, 1, timeout_pos); + tmr = (void *) create_tw_timer(esdp, ERTS_TMR_PROC, (void *) c_p, + NULL, NULL, timeout_pos); else - tmr = (void *) create_hl_timer(esdp, timeout_pos, - short_time, 0, (void *) c_p, + tmr = (void *) create_hl_timer(esdp, timeout_pos, short_time, + ERTS_TMR_PROC, (void *) c_p, c_p->common.id, THE_NON_VALUE, - NIL, NULL); + NIL, NULL, NULL, NULL); erts_smp_atomic_set_relb(&c_p->common.timer, (erts_aint_t) tmr); } } @@ -2649,13 +2800,12 @@ erts_set_port_timer(Port *c_prt, Sint64 tmo) timeout_pos = get_timeout_pos(erts_get_monotonic_time(esdp), tmo); if (tmo < ERTS_TIMER_WHEEL_MSEC) - tmr = (void *) create_tw_timer(esdp, (void *) c_prt, 0, - timeout_pos); + tmr = (void *) create_tw_timer(esdp, ERTS_TMR_PORT, (void *) c_prt, + NULL, NULL, timeout_pos); else - tmr = (void *) create_hl_timer(esdp, timeout_pos, 0, 0, - (void *) c_prt, - c_prt->common.id, THE_NON_VALUE, - NIL, NULL); + tmr = (void *) create_hl_timer(esdp, timeout_pos, 0, ERTS_TMR_PORT, + (void *) c_prt, c_prt->common.id, + THE_NON_VALUE, NIL, NULL, NULL, NULL); erts_smp_atomic_set_relb(&c_prt->common.timer, (erts_aint_t) tmr); } @@ -2800,6 +2950,98 @@ erts_debug_bif_timer_foreach(void (*func)(Eterm, } } +typedef struct { + void (*tclbk)(void *); + void (*func)(void *, + ErtsMonotonicTime, + void *); + void *arg; +} ErtsDebugForeachCallbackTimer; + +static void +debug_callback_timer_foreach_list(ErtsHLTimer *tmr, void *vdfct) +{ + ErtsDebugForeachCallbackTimer *dfct + = (ErtsDebugForeachCallbackTimer *) vdfct; + + if ((tmr->head.roflgs & ERTS_TMR_ROFLG_CALLBACK) + && (tmr->receiver.callback && dfct->tclbk)) + (*dfct->func)(dfct->arg, + tmr->timeout, + tmr->head.u.arg); +} + +static void +debug_callback_timer_foreach(ErtsHLTimer *tmr, void *vdfct) +{ + ErtsDebugForeachCallbackTimer *dfct + = (ErtsDebugForeachCallbackTimer *) vdfct; + + if (tmr->time.tree.same_time) + same_time_list_foreach(tmr->time.tree.same_time, + debug_callback_timer_foreach_list, + vdfct); + + if ((tmr->head.roflgs & ERTS_TMR_ROFLG_CALLBACK) + && (tmr->receiver.callback && dfct->tclbk)) + (*dfct->func)(dfct->arg, + tmr->timeout, + tmr->head.u.arg); +} + +static void +debug_tw_callback_timer(void *vdfct, + ErtsMonotonicTime timeout_pos, + void *vtwtp) +{ + ErtsTWTimer *twtp = (ErtsTWTimer *) vtwtp; + ErtsDebugForeachCallbackTimer *dfct + = (ErtsDebugForeachCallbackTimer *) vdfct; + + if (twtp->u.callback == dfct->tclbk) + (*dfct->func)(dfct->arg, + timeout_pos, + twtp->head.u.arg); +} + +void +erts_debug_callback_timer_foreach(void (*tclbk)(void *), + void (*func)(void *, + ErtsMonotonicTime, + void *), + void *arg) +{ + int six; + ErtsDebugForeachCallbackTimer dfct; + + dfct.tclbk = tclbk; + dfct.func = func; + dfct.arg = arg; + + if (!erts_smp_thr_progress_is_blocking()) + ERTS_INTERNAL_ERROR("Not blocking thread progress"); + + for (six = 0; six < erts_no_schedulers; six++) { + ErtsHLTimerService *srv = + erts_aligned_scheduler_data[six].esd.timer_service; + ErtsTimerWheel *twheel = + erts_aligned_scheduler_data[six].esd.timer_wheel; + + erts_twheel_debug_foreach(twheel, + tw_callback_timeout, + debug_tw_callback_timer, + (void *) &dfct); + + if (srv->yield.root) + debug_callback_timer_foreach(srv->yield.root, + (void *) &dfct); + + time_rbt_foreach(srv->btm_tree, + debug_callback_timer_foreach, + (void *) &dfct); + } +} + #ifdef ERTS_HLT_HARD_DEBUG typedef struct { diff --git a/erts/emulator/beam/erl_hl_timer.h b/erts/emulator/beam/erl_hl_timer.h index 30889a71da..24c57fc873 100644 --- a/erts/emulator/beam/erl_hl_timer.h +++ b/erts/emulator/beam/erl_hl_timer.h @@ -59,7 +59,9 @@ int erts_cancel_bif_timers(Process *, ErtsBifTimers *, void **); int erts_detach_accessor_bif_timers(Process *, ErtsBifTimers *, void **); ErtsHLTimerService *erts_create_timer_service(void); void erts_hl_timer_init(void); - +void erts_start_timer_callback(ErtsMonotonicTime, + void (*)(void *), + void *); #ifdef ERTS_SMP void erts_handle_canceled_timers(void *vesdp, @@ -76,5 +78,10 @@ void erts_debug_bif_timer_foreach(void (*func)(Eterm, ErlHeapFragment *, void *), void *arg); - +void +erts_debug_callback_timer_foreach(void (*tclbk)(void *), + void (*func)(void *, + ErtsMonotonicTime, + void *), + void *arg); #endif /* ERL_HL_TIMER_H__ */ diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 4560cd23af..e64b86496a 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -453,4 +453,12 @@ ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(ErtsNextTimeoutRef nxt_ #endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ +void +erts_twheel_debug_foreach(ErtsTimerWheel *tiw, + void (*tclbk)(void *), + void (*func)(void *, + ErtsMonotonicTime, + void *), + void *arg); + #endif /* timer wheel api */ diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index 8bffdedb2b..fe7c5826f5 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -529,6 +529,9 @@ erts_create_timer_wheel(ErtsSchedulerData *esdp) tiw->next_timeout_time = mtime + ERTS_MONOTONIC_DAY; tiw->sentinel.next = &tiw->sentinel; tiw->sentinel.prev = &tiw->sentinel; + tiw->sentinel.u.func.timeout = NULL; + tiw->sentinel.u.func.cancel = NULL; + tiw->sentinel.u.func.arg = NULL; return tiw; } @@ -624,6 +627,41 @@ erts_twheel_cancel_timer(ErtsTimerWheel *tiw, ErtsTWheelTimer *p) } } +void +erts_twheel_debug_foreach(ErtsTimerWheel *tiw, + void (*tclbk)(void *), + void (*func)(void *, + ErtsMonotonicTime, + void *), + void *arg) +{ + ErtsTWheelTimer *tmr; + int ix; + + tmr = tiw->sentinel.next; + while (tmr != &tiw->sentinel) { + if (tmr->u.func.timeout == tclbk) + (*func)(arg, tmr->timeout_pos, tmr->u.func.arg); + tmr = tmr->next; + } + + for (tmr = tiw->at_once.head; tmr; tmr = tmr->next) { + if (tmr->u.func.timeout == tclbk) + (*func)(arg, tmr->timeout_pos, tmr->u.func.arg); + } + + for (ix = 0; ix < ERTS_TIW_SIZE; ix++) { + tmr = tiw->w[ix]; + if (tmr) { + do { + if (tmr->u.func.timeout == tclbk) + (*func)(arg, tmr->timeout_pos, tmr->u.func.arg); + tmr = tmr->next; + } while (tmr != tiw->w[ix]); + } + } +} + #ifdef ERTS_TW_DEBUG void erts_p_slpq(void) { -- cgit v1.2.3 From 014865c474c4e8229cae556d9dff22ba75d6a5bb Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 28 May 2015 10:06:34 +0200 Subject: Only read lock node table when reading info --- erts/emulator/beam/erl_node_tables.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index bcf6311079..900fdb3ab5 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -556,14 +556,14 @@ erts_node_table_size(void) #endif int lock = !ERTS_IS_CRASH_DUMPING; if (lock) - erts_smp_rwmtx_rwlock(&erts_node_table_rwmtx); + erts_smp_rwmtx_rlock(&erts_node_table_rwmtx); #ifdef DEBUG hash_get_info(&hi, &erts_node_table); ASSERT(node_entries == hi.objs); #endif res = hash_table_sz(&erts_node_table) + node_entries*sizeof(ErlNode); if (lock) - erts_smp_rwmtx_rwunlock(&erts_node_table_rwmtx); + erts_smp_rwmtx_runlock(&erts_node_table_rwmtx); return res; } @@ -572,10 +572,10 @@ erts_node_table_info(int to, void *to_arg) { int lock = !ERTS_IS_CRASH_DUMPING; if (lock) - erts_smp_rwmtx_rwlock(&erts_node_table_rwmtx); + erts_smp_rwmtx_rlock(&erts_node_table_rwmtx); hash_info(to, to_arg, &erts_node_table); if (lock) - erts_smp_rwmtx_rwunlock(&erts_node_table_rwmtx); + erts_smp_rwmtx_runlock(&erts_node_table_rwmtx); } @@ -674,13 +674,13 @@ void erts_print_node_info(int to, pnd.no_total = 0; if (lock) - erts_smp_rwmtx_rwlock(&erts_node_table_rwmtx); + erts_smp_rwmtx_rlock(&erts_node_table_rwmtx); hash_foreach(&erts_node_table, print_node, (void *) &pnd); if (pnd.no_sysname != 0) { erts_print(to, to_arg, "\n"); } if (lock) - erts_smp_rwmtx_rwunlock(&erts_node_table_rwmtx); + erts_smp_rwmtx_runlock(&erts_node_table_rwmtx); if(no_sysname) *no_sysname = pnd.no_sysname; -- cgit v1.2.3 From 4f63e427f1345b40b484ef16f6ff5e922bf14dac Mon Sep 17 00:00:00 2001 From: Rory Byrne Date: Mon, 20 Apr 2015 10:57:53 +0100 Subject: Add 'show_econnreset' TCP socket option An ECONNRESET is a socket error which tells us that a TCP peer has sent an RST. The RST indicates that they have aborted the connection and that the payload we have received should not be considered complete. Up until now, the implementation of TCP in inet_drv.c has hidden the receipt of the RST from the user, treating it as though it was just a FIN terminating the read side of the socket. There are many cases where user code needs to be able to distinguish between a socket that was closed normally and one that was aborted. Setting the option {show_econnreset, true} enables the user to receive ECONNRESET errors on both active and passive sockets. A connected socket returned from gen_tcp:accept/1 will inherit the show_econnreset setting of the listening socket. By default this option is set to {show_econnreset, false}. Note that this patch only enables the reporting of ECONNRESET when the socket is being read from. It does not report ECONNRESET (or EPIPE) when the user tries to write to a connection when an RST has already been received. Currently the TCP implementation in inet_drv.c hides all such send errors from the user in favour of returning {error, close}. A separate patch will be needed to enable the reporting of such errors. --- erts/emulator/drivers/common/inet_drv.c | 47 +++++++++++++++++++++++++++++++-- erts/preloaded/src/prim_inet.erl | 3 +++ 2 files changed, 48 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index e001f31932..b5903f0c07 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -835,6 +835,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define TCP_ADDF_PENDING_SHUT_RDWR 32 /* Call shutdown(sock, SHUT_RDWR) when queue empties */ #define TCP_ADDF_PENDING_SHUTDOWN \ (TCP_ADDF_PENDING_SHUT_WR | TCP_ADDF_PENDING_SHUT_RDWR) +#define TCP_ADDF_SHOW_ECONNRESET 64 /* Tell user about incoming RST */ /* *_REQ_* replies */ #define INET_REP_ERROR 0 @@ -879,6 +880,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_LOPT_MSGQ_HIWTRMRK 36 /* set local msgq high watermark */ #define INET_LOPT_MSGQ_LOWTRMRK 37 /* set local msgq low watermark */ #define INET_LOPT_NETNS 38 /* Network namespace pathname */ +#define INET_LOPT_TCP_SHOW_ECONNRESET 39 /* tell user about incoming RST */ /* SCTP options: a separate range, from 100: */ #define SCTP_OPT_RTOINFO 100 #define SCTP_OPT_ASSOCINFO 101 @@ -6255,6 +6257,16 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) continue; #endif + case INET_LOPT_TCP_SHOW_ECONNRESET: + if (desc->sprotocol == IPPROTO_TCP) { + tcp_descriptor* tdesc = (tcp_descriptor*) desc; + if (ival) + tdesc->tcp_add_flags |= TCP_ADDF_SHOW_ECONNRESET; + else + tdesc->tcp_add_flags &= ~TCP_ADDF_SHOW_ECONNRESET; + } + continue; + case INET_OPT_REUSEADDR: #ifdef __WIN32__ continue; /* Bjorn says */ @@ -7234,6 +7246,17 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, continue; #endif + case INET_LOPT_TCP_SHOW_ECONNRESET: + if (desc->sprotocol == IPPROTO_TCP) { + tcp_descriptor* tdesc = (tcp_descriptor*) desc; + *ptr++ = opt; + ival = !!(tdesc->tcp_add_flags & TCP_ADDF_SHOW_ECONNRESET); + put_int32(ival, ptr); + } else { + TRUNCATE_TO(0,ptr); + } + continue; + case INET_OPT_PRIORITY: #ifdef SO_PRIORITY type = SO_PRIORITY; @@ -9077,6 +9100,11 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s, copy_desc->low = desc->low; copy_desc->send_timeout = desc->send_timeout; copy_desc->send_timeout_close = desc->send_timeout_close; + + if (desc->tcp_add_flags & TCP_ADDF_SHOW_ECONNRESET) + copy_desc->tcp_add_flags |= TCP_ADDF_SHOW_ECONNRESET; + else + copy_desc->tcp_add_flags &= ~TCP_ADDF_SHOW_ECONNRESET; /* The new port will be linked and connected to the original caller */ port = driver_create_port(port, owner, "tcp_inet", (ErlDrvData) copy_desc); @@ -10014,7 +10042,10 @@ static int tcp_recv(tcp_descriptor* desc, int request_len) int err = sock_errno(); if (err == ECONNRESET) { DEBUGF((" => detected close (connreset)\r\n")); - return tcp_recv_closed(desc); + if (desc->tcp_add_flags & TCP_ADDF_SHOW_ECONNRESET) + return tcp_recv_error(desc, err); + else + return tcp_recv_closed(desc); } if (err == ERRNO_BLOCK) { DEBUGF((" => would block\r\n")); @@ -10226,7 +10257,19 @@ static void tcp_inet_event(ErlDrvData e, ErlDrvEvent event) if (netEv.lNetworkEvents & FD_CLOSE) { /* error in err = netEv.iErrorCode[FD_CLOSE_BIT] */ DEBUGF(("Detected close in %s, line %d\r\n", __FILE__, __LINE__)); - tcp_recv_closed(desc); + if (desc->tcp_add_flags & TCP_ADDF_SHOW_ECONNRESET) { + err = netEv.iErrorCode[FD_CLOSE_BIT]; + if (err == ECONNRESET) + tcp_recv_error(desc, err); + else if (err == ECONNABORTED && IS_CONNECTED(INETP(desc))) { + /* translate this error to ECONNRESET */ + tcp_recv_error(desc, ECONNRESET); + } + else + tcp_recv_closed(desc); + } + else + tcp_recv_closed(desc); } DEBUGF(("tcp_inet_event(%ld) }\r\n", (long)desc->inet.port)); return; diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 622e1be869..fb0f67aa8a 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -1140,6 +1140,7 @@ enc_opt(delay_send) -> ?INET_LOPT_TCP_DELAY_SEND; enc_opt(packet_size) -> ?INET_LOPT_PACKET_SIZE; enc_opt(read_packets) -> ?INET_LOPT_READ_PACKETS; enc_opt(netns) -> ?INET_LOPT_NETNS; +enc_opt(show_econnreset) -> ?INET_LOPT_TCP_SHOW_ECONNRESET; enc_opt(raw) -> ?INET_OPT_RAW; % Names of SCTP opts: enc_opt(sctp_rtoinfo) -> ?SCTP_OPT_RTOINFO; @@ -1197,6 +1198,7 @@ dec_opt(?INET_LOPT_TCP_DELAY_SEND) -> delay_send; dec_opt(?INET_LOPT_PACKET_SIZE) -> packet_size; dec_opt(?INET_LOPT_READ_PACKETS) -> read_packets; dec_opt(?INET_LOPT_NETNS) -> netns; +dec_opt(?INET_LOPT_TCP_SHOW_ECONNRESET) -> show_econnreset; dec_opt(?INET_OPT_RAW) -> raw; dec_opt(I) when is_integer(I) -> undefined. @@ -1296,6 +1298,7 @@ type_opt_1(delay_send) -> bool; type_opt_1(packet_size) -> uint; type_opt_1(read_packets) -> uint; type_opt_1(netns) -> binary; +type_opt_1(show_econnreset) -> bool; %% %% SCTP options (to be set). If the type is a record type, the corresponding %% record signature is returned, otherwise, an "elementary" type tag -- cgit v1.2.3 From 0098eed847516cc6760d961421c83527816e35ae Mon Sep 17 00:00:00 2001 From: Rory Byrne Date: Sat, 2 May 2015 20:34:24 +0100 Subject: Apply 'show_econnreset' socket option to send errors as well Up till now all send errors have been translated into a generic {error, closed}. This patch allows {error, econnreset} to be returned on send errors when it is detected that the TCP peer has sent an RST. --- erts/emulator/drivers/common/inet_drv.c | 84 +++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index b5903f0c07..a8c58a8149 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -836,6 +836,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define TCP_ADDF_PENDING_SHUTDOWN \ (TCP_ADDF_PENDING_SHUT_WR | TCP_ADDF_PENDING_SHUT_RDWR) #define TCP_ADDF_SHOW_ECONNRESET 64 /* Tell user about incoming RST */ +#define TCP_ADDF_DELAYED_ECONNRESET 128 /* An ECONNRESET error occured on send or shutdown */ +#define TCP_ADDF_SHUTDOWN_WR_DONE 256 /* A shutdown(sock, SHUT_WR) or SHUT_RDWR was made */ /* *_REQ_* replies */ #define INET_REP_ERROR 0 @@ -6140,7 +6142,12 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) desc->active_count = 0; if ((desc->stype == SOCK_STREAM) && (desc->active != INET_PASSIVE) && (desc->state == INET_STATE_CLOSED)) { - tcp_closed_message((tcp_descriptor *) desc); + tcp_descriptor *tdesc = (tcp_descriptor *) desc; + if (tdesc->tcp_add_flags & TCP_ADDF_DELAYED_ECONNRESET) { + tdesc->tcp_add_flags &= ~TCP_ADDF_DELAYED_ECONNRESET; + tcp_error_message(tdesc, ECONNRESET); + } + tcp_closed_message(tdesc); if (desc->exitf) { driver_exit(desc->port, 0); return 0; /* Give up on this socket, descriptor lost */ @@ -9466,7 +9473,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, if (desc->tcp_add_flags & TCP_ADDF_DELAYED_CLOSE_RECV) { desc->tcp_add_flags &= ~(TCP_ADDF_DELAYED_CLOSE_RECV| TCP_ADDF_DELAYED_CLOSE_SEND); - return ctl_reply(INET_REP_ERROR, "closed", 6, rbuf, rsize); + if (desc->tcp_add_flags & TCP_ADDF_DELAYED_ECONNRESET) { + desc->tcp_add_flags &= ~TCP_ADDF_DELAYED_ECONNRESET; + return ctl_reply(INET_REP_ERROR, "econnreset", 10, rbuf, rsize); + } else + return ctl_reply(INET_REP_ERROR, "closed", 6, rbuf, rsize); } return ctl_error(ENOTCONN, rbuf, rsize); } @@ -9527,6 +9538,8 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } if (IS_SOCKET_ERROR(sock_shutdown(INETP(desc)->s, how))) { + if (how != TCP_SHUT_RD) + desc->tcp_add_flags |= TCP_ADDF_SHUTDOWN_WR_DONE; return ctl_error(sock_errno(), rbuf, rsize); } else { return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); @@ -9661,7 +9674,13 @@ static void tcp_inet_commandv(ErlDrvData e, ErlIOVec* ev) if (!IS_CONNECTED(INETP(desc))) { if (desc->tcp_add_flags & TCP_ADDF_DELAYED_CLOSE_SEND) { desc->tcp_add_flags &= ~TCP_ADDF_DELAYED_CLOSE_SEND; - inet_reply_error_am(INETP(desc), am_closed); + if (desc->tcp_add_flags & TCP_ADDF_DELAYED_ECONNRESET) { + /* Don't clear flag. Leave it enabled for the next receive + * operation. + */ + inet_reply_error(INETP(desc), ECONNRESET); + } else + inet_reply_error_am(INETP(desc), am_closed); } else inet_reply_error(INETP(desc), ENOTCONN); @@ -10578,6 +10597,9 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err) { + int show_econnreset = (err == ECONNRESET + && desc->tcp_add_flags & TCP_ADDF_SHOW_ECONNRESET); + /* * If the port is busy, we must do some clean-up before proceeding. */ @@ -10593,14 +10615,21 @@ static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err) /* * We used to handle "expected errors" differently from unexpected ones. - * Now we handle all errors in the same way. We just have to distinguish - * between passive and active sockets. + * Now we handle all errors in the same way (unless the show_econnreset + * socket option is enabled). We just have to distinguish between passive + * and active sockets. */ DEBUGF(("driver_failure_eof(%ld) in %s, line %d\r\n", (long)desc->inet.port, __FILE__, __LINE__)); if (desc->inet.active) { - tcp_closed_message(desc); - inet_reply_error_am(INETP(desc), am_closed); + if (show_econnreset) { + tcp_error_message(desc, err); + tcp_closed_message(desc); + inet_reply_error(INETP(desc), err); + } else { + tcp_closed_message(desc); + inet_reply_error_am(INETP(desc), am_closed); + } if (desc->inet.exitf) driver_exit(desc->inet.port, 0); else @@ -10612,7 +10641,10 @@ static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err) erl_inet_close(INETP(desc)); if (desc->inet.caller) { - inet_reply_error_am(INETP(desc), am_closed); + if (show_econnreset) + inet_reply_error(INETP(desc), err); + else + inet_reply_error_am(INETP(desc), am_closed); } else { /* No blocking send op to reply to right now. @@ -10629,12 +10661,46 @@ static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err) * in the receive operation. */ desc->tcp_add_flags |= TCP_ADDF_DELAYED_CLOSE_RECV; + + if (show_econnreset) { + /* Return {error, econnreset} instead of {error, closed} + * on send or receive operations. + */ + desc->tcp_add_flags |= TCP_ADDF_DELAYED_ECONNRESET; + } } return -1; } static int tcp_send_error(tcp_descriptor* desc, int err) { + /* EPIPE errors usually occur in one of three ways: + * 1. We write to a socket when we've already shutdown() the write side. On + * Windows the error returned for this is ESHUTDOWN rather than EPIPE. + * 2. The TCP peer sends us an RST through no fault of our own (perhaps + * by aborting the connection using SO_LINGER) and we then attempt + * to write to the socket. On Linux and Windows we would actually + * receive an ECONNRESET error for this, but on the BSDs, Darwin, + * Illumos and presumably Solaris, it's an EPIPE. + * 3. We cause the TCP peer to send us an RST by writing to a socket + * after we receive a FIN from them. Our first write will be + * successful, but if the they have closed the connection (rather + * than just shutting down the write side of it) this will cause their + * OS to send us an RST. Then, when we attempt to write to the socket + * a second time, we will get an EPIPE error. On Windows we get an + * ECONNABORTED. + * + * What we are going to do here is to treat all EPIPE messages that aren't + * of type 1 as ECONNRESET errors. This will allow users who have the + * show_econnreset socket option enabled to receive {error, econnreset} on + * both send and recv operations to indicate that an RST has been received. + */ +#ifdef __WIN_32__ + if (err == ECONNABORTED) + err = ECONNRESET; +#endif + if (err == EPIPE && !(desc->tcp_add_flags & TCP_ADDF_SHUTDOWN_WR_DONE)) + err = ECONNRESET; return tcp_send_or_shutdown_error(desc, err); } @@ -10854,6 +10920,8 @@ static void tcp_shutdown_async(tcp_descriptor* desc) TCP_SHUT_WR : TCP_SHUT_RDWR; if (IS_SOCKET_ERROR(sock_shutdown(INETP(desc)->s, how))) tcp_shutdown_error(desc, sock_errno()); + else + desc->tcp_add_flags |= TCP_ADDF_SHUTDOWN_WR_DONE; } #ifdef __OSE__ -- cgit v1.2.3 From eed21b4fd99c8ab71dcefd3802104186af1cb6b0 Mon Sep 17 00:00:00 2001 From: Rory Byrne Date: Thu, 16 Apr 2015 18:01:23 +0100 Subject: Fix socket option {linger, {true, 0}} to abort TCP connections Up until now, if {linger, {true, 0}} is set on the socket and there is data in the port driver queue, the connection is not aborted until the port queue is empty and close() is called on the underlying file descriptor. This bug allows an idle TCP client to prevent a server from terminating the connection and freeing resources. This patch fixes the problem by discarding the port queue if the socket is closed when {linger, {true, 0}} is set. --- erts/emulator/drivers/common/inet_drv.c | 10 ++++++++++ erts/preloaded/src/prim_inet.erl | 13 +++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index a8c58a8149..80cb705896 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -838,6 +838,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define TCP_ADDF_SHOW_ECONNRESET 64 /* Tell user about incoming RST */ #define TCP_ADDF_DELAYED_ECONNRESET 128 /* An ECONNRESET error occured on send or shutdown */ #define TCP_ADDF_SHUTDOWN_WR_DONE 256 /* A shutdown(sock, SHUT_WR) or SHUT_RDWR was made */ +#define TCP_ADDF_LINGER_ZERO 512 /* Discard driver queue on port close */ /* *_REQ_* replies */ #define INET_REP_ERROR 0 @@ -6318,6 +6319,13 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) arg_sz = sizeof(li_val); DEBUGF(("inet_set_opts(%ld): s=%d, SO_LINGER=%d,%d", (long)desc->port, desc->s, li_val.l_onoff,li_val.l_linger)); + if (desc->sprotocol == IPPROTO_TCP) { + tcp_descriptor* tdesc = (tcp_descriptor*) desc; + if (li_val.l_onoff && li_val.l_linger == 0) + tdesc->tcp_add_flags |= TCP_ADDF_LINGER_ZERO; + else + tdesc->tcp_add_flags &= ~TCP_ADDF_LINGER_ZERO; + } break; case INET_OPT_PRIORITY: @@ -9699,6 +9707,8 @@ static void tcp_inet_flush(ErlDrvData e) /* Discard send queue to avoid hanging port (OTP-7615) */ tcp_clear_output(desc); } + if (desc->tcp_add_flags & TCP_ADDF_LINGER_ZERO) + tcp_clear_output(desc); } static void tcp_inet_process_exit(ErlDrvData e, ErlDrvMonitor *monitorp) diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index fb0f67aa8a..5e0b38aa68 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -146,11 +146,16 @@ shutdown_1(S, How) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% close(S) when is_port(S) -> - case subscribe(S, [subs_empty_out_q]) of - {ok, [{subs_empty_out_q,N}]} when N > 0 -> - close_pend_loop(S, N); %% wait for pending output to be sent + case getopt(S, linger) of + {ok,{true,0}} -> + close_port(S); _ -> - close_port(S) + case subscribe(S, [subs_empty_out_q]) of + {ok, [{subs_empty_out_q,N}]} when N > 0 -> + close_pend_loop(S, N); %% wait for pending output to be sent + _ -> + close_port(S) + end end. close_pend_loop(S, N) -> -- cgit v1.2.3 From 3219822bb549a9d0d40ff29463f0bfe7d1eedd37 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Sun, 26 Apr 2015 16:29:53 +0200 Subject: Fix segfault in module_info for deleted modules Add a check to protect from segfault when erlang:get_module_info/1/2 is called on a deleted module (i.e. with no current code). Also refactor erts_module_info_0/1 to avoid repeated calls to erts_active_code_ix() and remove some obsolete comments. Add test for module_info on deleted modules. --- erts/emulator/beam/beam_load.c | 181 +++++++-------------- erts/emulator/test/module_info_SUITE.erl | 21 ++- .../module_info_SUITE_data/module_info_test.erl | 24 +++ 3 files changed, 106 insertions(+), 120 deletions(-) create mode 100644 erts/emulator/test/module_info_SUITE_data/module_info_test.erl (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 0d40201934..41ce1659d4 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -525,13 +525,16 @@ static void new_literal_patch(LoaderState* stp, int pos); static void new_string_patch(LoaderState* stp, int pos); static Uint new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size); static int genopargcompare(GenOpArg* a, GenOpArg* b); -static Eterm exported_from_module(Process* p, Eterm mod); -static Eterm functions_in_module(Process* p, Eterm mod); -static Eterm attributes_for_module(Process* p, Eterm mod); -static Eterm compilation_info_for_module(Process* p, Eterm mod); -static Eterm md5_of_module(Process* p, Eterm mod); -static Eterm has_native(Process* p, Eterm mod); -static Eterm native_addresses(Process* p, Eterm mod); +static Eterm get_module_info(Process* p, ErtsCodeIndex code_ix, + BeamInstr* code, Eterm module, Eterm what); +static Eterm exported_from_module(Process* p, ErtsCodeIndex code_ix, + Eterm mod); +static Eterm functions_in_module(Process* p, BeamInstr* code); +static Eterm attributes_for_module(Process* p, BeamInstr* code); +static Eterm compilation_info_for_module(Process* p, BeamInstr* code); +static Eterm md5_of_module(Process* p, BeamInstr* code); +static Eterm has_native(BeamInstr* code); +static Eterm native_addresses(Process* p, BeamInstr* code); int patch_funentries(Eterm Patchlist); int patch(Eterm Addresses, Uint fe); static int safe_mul(UWord a, UWord b, UWord* resp); @@ -5389,6 +5392,9 @@ new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size) Eterm erts_module_info_0(Process* p, Eterm module) { + Module* modp; + ErtsCodeIndex code_ix = erts_active_code_ix(); + BeamInstr* code; Eterm *hp; Eterm list = NIL; Eterm tup; @@ -5397,12 +5403,18 @@ erts_module_info_0(Process* p, Eterm module) return THE_NON_VALUE; } - if (erts_get_module(module, erts_active_code_ix()) == NULL) { + modp = erts_get_module(module, code_ix); + if (modp == NULL) { return THE_NON_VALUE; } + code = modp->curr.code; + if (code == NULL) { + return THE_NON_VALUE; + } + #define BUILD_INFO(What) \ - tup = erts_module_info_1(p, module, What); \ + tup = get_module_info(p, code_ix, code, module, What); \ hp = HAlloc(p, 5); \ tup = TUPLE2(hp, What, tup); \ hp += 3; \ @@ -5422,23 +5434,48 @@ erts_module_info_0(Process* p, Eterm module) Eterm erts_module_info_1(Process* p, Eterm module, Eterm what) +{ + Module* modp; + ErtsCodeIndex code_ix = erts_active_code_ix(); + BeamInstr* code; + + if (is_not_atom(module)) { + return THE_NON_VALUE; + } + + modp = erts_get_module(module, code_ix); + if (modp == NULL) { + return THE_NON_VALUE; + } + + code = modp->curr.code; + if (code == NULL) { + return THE_NON_VALUE; + } + + return get_module_info(p, code_ix, code, module, what); +} + +static Eterm +get_module_info(Process* p, ErtsCodeIndex code_ix, BeamInstr* code, + Eterm module, Eterm what) { if (what == am_module) { return module; } else if (what == am_md5) { - return md5_of_module(p, module); + return md5_of_module(p, code); } else if (what == am_exports) { - return exported_from_module(p, module); + return exported_from_module(p, code_ix, module); } else if (what == am_functions) { - return functions_in_module(p, module); + return functions_in_module(p, code); } else if (what == am_attributes) { - return attributes_for_module(p, module); + return attributes_for_module(p, code); } else if (what == am_compile) { - return compilation_info_for_module(p, module); + return compilation_info_for_module(p, code); } else if (what == am_native_addresses) { - return native_addresses(p, module); + return native_addresses(p, code); } else if (what == am_native) { - return has_native(p, module); + return has_native(code); } return THE_NON_VALUE; } @@ -5446,16 +5483,12 @@ erts_module_info_1(Process* p, Eterm module, Eterm what) /* * Builds a list of all functions in the given module: * [{Name, Arity},...] - * - * Returns a tagged term, or 0 on error. */ Eterm functions_in_module(Process* p, /* Process whose heap to use. */ - Eterm mod) /* Tagged atom for module. */ + BeamInstr* code) { - Module* modp; - BeamInstr* code; int i; Uint num_functions; Uint need; @@ -5463,15 +5496,6 @@ functions_in_module(Process* p, /* Process whose heap to use. */ Eterm* hp_end; Eterm result = NIL; - if (is_not_atom(mod)) { - return THE_NON_VALUE; - } - - modp = erts_get_module(mod, erts_active_code_ix()); - if (modp == NULL) { - return THE_NON_VALUE; - } - code = modp->curr.code; num_functions = code[MI_NUM_FUNCTIONS]; need = 5*num_functions; hp = HAlloc(p, need); @@ -5503,23 +5527,12 @@ functions_in_module(Process* p, /* Process whose heap to use. */ */ static Eterm -has_native(Process* p, Eterm mod) +has_native(BeamInstr *code) { Eterm result = am_false; #ifdef HIPE - Module* modp; - - if (is_not_atom(mod)) { - return THE_NON_VALUE; - } - - modp = erts_get_module(mod, erts_active_code_ix()); - if (modp == NULL) { - return THE_NON_VALUE; - } - - if (erts_is_module_native(modp->curr.code)) { - result = am_true; + if (erts_is_module_native(code)) { + result = am_true; } #endif return result; @@ -5548,15 +5561,11 @@ erts_is_module_native(BeamInstr* code) /* * Builds a list of all functions including native addresses. * [{Name,Arity,NativeAddress},...] - * - * Returns a tagged term, or 0 on error. */ static Eterm -native_addresses(Process* p, Eterm mod) +native_addresses(Process* p, BeamInstr* code) { - Module* modp; - BeamInstr* code; int i; Eterm* hp; Uint num_functions; @@ -5564,16 +5573,6 @@ native_addresses(Process* p, Eterm mod) Eterm* hp_end; Eterm result = NIL; - if (is_not_atom(mod)) { - return THE_NON_VALUE; - } - - modp = erts_get_module(mod, erts_active_code_ix()); - if (modp == NULL) { - return THE_NON_VALUE; - } - - code = modp->curr.code; num_functions = code[MI_NUM_FUNCTIONS]; need = (6+BIG_UINT_HEAP_SIZE)*num_functions; hp = HAlloc(p, need); @@ -5600,25 +5599,18 @@ native_addresses(Process* p, Eterm mod) /* * Builds a list of all exported functions in the given module: * [{Name, Arity},...] - * - * Returns a tagged term, or 0 on error. */ Eterm exported_from_module(Process* p, /* Process whose heap to use. */ + ErtsCodeIndex code_ix, Eterm mod) /* Tagged atom for module. */ { int i; Eterm* hp = NULL; Eterm* hend = NULL; Eterm result = NIL; - ErtsCodeIndex code_ix; - - if (is_not_atom(mod)) { - return THE_NON_VALUE; - } - code_ix = erts_active_code_ix(); for (i = 0; i < export_list_size(code_ix); i++) { Export* ep = export_list(i,code_ix); @@ -5648,31 +5640,17 @@ exported_from_module(Process* p, /* Process whose heap to use. */ /* * Returns a list of all attributes for the module. - * - * Returns a tagged term, or 0 on error. */ Eterm attributes_for_module(Process* p, /* Process whose heap to use. */ - Eterm mod) /* Tagged atom for module. */ - + BeamInstr* code) { - Module* modp; - BeamInstr* code; Eterm* hp; byte* ext; Eterm result = NIL; Eterm* end; - if (is_not_atom(mod)) { - return THE_NON_VALUE; - } - - modp = erts_get_module(mod, erts_active_code_ix()); - if (modp == NULL) { - return THE_NON_VALUE; - } - code = modp->curr.code; ext = (byte *) code[MI_ATTR_PTR]; if (ext != NULL) { hp = HAlloc(p, code[MI_ATTR_SIZE_ON_HEAP]); @@ -5688,30 +5666,17 @@ attributes_for_module(Process* p, /* Process whose heap to use. */ /* * Returns a list containing compilation information. - * - * Returns a tagged term, or 0 on error. */ Eterm compilation_info_for_module(Process* p, /* Process whose heap to use. */ - Eterm mod) /* Tagged atom for module. */ + BeamInstr* code) { - Module* modp; - BeamInstr* code; Eterm* hp; byte* ext; Eterm result = NIL; Eterm* end; - if (is_not_atom(mod)) { - return THE_NON_VALUE; - } - - modp = erts_get_module(mod, erts_active_code_ix()); - if (modp == NULL) { - return THE_NON_VALUE; - } - code = modp->curr.code; ext = (byte *) code[MI_COMPILE_PTR]; if (ext != NULL) { hp = HAlloc(p, code[MI_COMPILE_SIZE_ON_HEAP]); @@ -5727,33 +5692,13 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */ /* * Returns the MD5 checksum for a module - * - * Returns a tagged term, or 0 on error. */ Eterm md5_of_module(Process* p, /* Process whose heap to use. */ - Eterm mod) /* Tagged atom for module. */ + BeamInstr* code) { - Module* modp; - BeamInstr* code; - Eterm res = NIL; - - if (is_not_atom(mod)) { - return THE_NON_VALUE; - } - - modp = erts_get_module(mod, erts_active_code_ix()); - if (modp == NULL) { - return THE_NON_VALUE; - } - code = modp->curr.code; - if (code[MI_MD5_PTR] != 0) { - res = new_binary(p, (byte *) code[MI_MD5_PTR], MD5_SIZE); - } else { - res = am_undefined; - } - return res; + return new_binary(p, (byte *) code[MI_MD5_PTR], MD5_SIZE); } /* diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index 1125cf3072..362e210d20 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -24,7 +24,7 @@ -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, - exports/1,functions/1,native/1,info/1]). + exports/1,functions/1,deleted/1,native/1,info/1]). %%-compile(native). @@ -53,7 +53,7 @@ end_per_group(_GroupName, Config) -> modules() -> - [exports, functions, native, info]. + [exports, functions, deleted, native, info]. init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> Dog = ?t:timetrap(?t:minutes(3)), @@ -93,6 +93,23 @@ functions(Config) when is_list(Config) -> ?line All = lists:sort(?MODULE:module_info(functions)), ok. +%% Test that deleted modules cause badarg +deleted(Config) when is_list(Config) -> + ?line Data = ?config(data_dir, Config), + ?line File = filename:join(Data, "module_info_test"), + ?line {ok,module_info_test,Code} = compile:file(File, [binary]), + ?line {module,module_info_test} = erlang:load_module(module_info_test, Code), + ?line 17 = module_info_test:f(), + ?line [_|_] = erlang:get_module_info(module_info_test, attributes), + ?line [_|_] = erlang:get_module_info(module_info_test), + ?line erlang:delete_module(module_info_test), + ?line {'EXIT',{undef, _}} = (catch module_info_test:f()), + ?line {'EXIT',{badarg, _}} = + (catch erlang:get_module_info(module_info_test, attributes)), + ?line {'EXIT',{badarg, _}} = + (catch erlang:get_module_info(module_info_test)), + ok. + %% Test that the list of exported functions from this module is correct. %% Verify that module_info(native) works. native(Config) when is_list(Config) -> diff --git a/erts/emulator/test/module_info_SUITE_data/module_info_test.erl b/erts/emulator/test/module_info_SUITE_data/module_info_test.erl new file mode 100644 index 0000000000..f045f38464 --- /dev/null +++ b/erts/emulator/test/module_info_SUITE_data/module_info_test.erl @@ -0,0 +1,24 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. All Rights Reserved. +%% +%% The 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(module_info_test). +-export([f/0]). + +f() -> + 17. -- cgit v1.2.3 From 20c59a72536521f6c4ffea9e86fe931b6f8ec9ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 10 Jun 2015 15:09:57 +0200 Subject: erts: Remove ?line macros from module_info_SUITE --- erts/emulator/test/module_info_SUITE.erl | 81 +++++++++++++++----------------- 1 file changed, 39 insertions(+), 42 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index 362e210d20..f55755f0f9 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -51,7 +51,6 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. - modules() -> [exports, functions, deleted, native, info]. @@ -81,60 +80,58 @@ all_functions() -> %% Test that the list of exported functions from this module is correct. exports(Config) when is_list(Config) -> - ?line All = all_exported(), - ?line All = lists:sort(?MODULE:module_info(exports)), - ?line (catch ?MODULE:foo()), - ?line All = lists:sort(?MODULE:module_info(exports)), + All = all_exported(), + All = lists:sort(?MODULE:module_info(exports)), + (catch ?MODULE:foo()), + All = lists:sort(?MODULE:module_info(exports)), ok. %% Test that the list of exported functions from this module is correct. functions(Config) when is_list(Config) -> - ?line All = all_functions(), - ?line All = lists:sort(?MODULE:module_info(functions)), + All = all_functions(), + All = lists:sort(?MODULE:module_info(functions)), ok. %% Test that deleted modules cause badarg deleted(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), - ?line File = filename:join(Data, "module_info_test"), - ?line {ok,module_info_test,Code} = compile:file(File, [binary]), - ?line {module,module_info_test} = erlang:load_module(module_info_test, Code), - ?line 17 = module_info_test:f(), - ?line [_|_] = erlang:get_module_info(module_info_test, attributes), - ?line [_|_] = erlang:get_module_info(module_info_test), - ?line erlang:delete_module(module_info_test), - ?line {'EXIT',{undef, _}} = (catch module_info_test:f()), - ?line {'EXIT',{badarg, _}} = - (catch erlang:get_module_info(module_info_test, attributes)), - ?line {'EXIT',{badarg, _}} = - (catch erlang:get_module_info(module_info_test)), + Data = ?config(data_dir, Config), + File = filename:join(Data, "module_info_test"), + {ok,module_info_test,Code} = compile:file(File, [binary]), + {module,module_info_test} = erlang:load_module(module_info_test, Code), + 17 = module_info_test:f(), + [_|_] = erlang:get_module_info(module_info_test, attributes), + [_|_] = erlang:get_module_info(module_info_test), + erlang:delete_module(module_info_test), + {'EXIT',{undef, _}} = (catch module_info_test:f()), + {'EXIT',{badarg, _}} = (catch erlang:get_module_info(module_info_test,attributes)), + {'EXIT',{badarg, _}} = (catch erlang:get_module_info(module_info_test)), ok. %% Test that the list of exported functions from this module is correct. %% Verify that module_info(native) works. native(Config) when is_list(Config) -> - ?line All = all_functions(), - ?line case ?MODULE:module_info(native_addresses) of - [] -> - ?line false = ?MODULE:module_info(native), - {comment,"no native functions"}; - L -> - ?line true = ?MODULE:module_info(native), - %% Verify that all functions have unique addresses. - ?line S0 = sofs:set(L, [{name,arity,addr}]), - ?line S1 = sofs:projection({external,fun ?MODULE:native_proj/1}, S0), - ?line S2 = sofs:relation_to_family(S1), - ?line S3 = sofs:family_specification(fun ?MODULE:native_filter/1, S2), - ?line 0 = sofs:no_elements(S3), - ?line S4 = sofs:range(S1), - - %% Verify that the set of function with native addresses - %% is a subset of all functions in the module. - ?line AllSet = sofs:set(All, [{name,arity}]), - ?line true = sofs:is_subset(S4, AllSet), - - {comment,integer_to_list(sofs:no_elements(S0))++" native functions"} - end. + All = all_functions(), + case ?MODULE:module_info(native_addresses) of + [] -> + false = ?MODULE:module_info(native), + {comment,"no native functions"}; + L -> + true = ?MODULE:module_info(native), + %% Verify that all functions have unique addresses. + S0 = sofs:set(L, [{name,arity,addr}]), + S1 = sofs:projection({external,fun ?MODULE:native_proj/1}, S0), + S2 = sofs:relation_to_family(S1), + S3 = sofs:family_specification(fun ?MODULE:native_filter/1, S2), + 0 = sofs:no_elements(S3), + S4 = sofs:range(S1), + + %% Verify that the set of function with native addresses + %% is a subset of all functions in the module. + AllSet = sofs:set(All, [{name,arity}]), + true = sofs:is_subset(S4, AllSet), + + {comment,integer_to_list(sofs:no_elements(S0))++" native functions"} + end. native_proj({Name,Arity,Addr}) -> {Addr,{Name,Arity}}. -- cgit v1.2.3 From b135c712271a30b96278c68e2fdaf8635e88926d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 10 Jun 2015 15:17:50 +0200 Subject: erts: Add test for module_info on purged modules --- erts/emulator/test/module_info_SUITE.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index f55755f0f9..25ba6d1787 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -101,7 +101,15 @@ deleted(Config) when is_list(Config) -> 17 = module_info_test:f(), [_|_] = erlang:get_module_info(module_info_test, attributes), [_|_] = erlang:get_module_info(module_info_test), - erlang:delete_module(module_info_test), + + %% first delete it + true = erlang:delete_module(module_info_test), + {'EXIT',{undef, _}} = (catch module_info_test:f()), + {'EXIT',{badarg, _}} = (catch erlang:get_module_info(module_info_test,attributes)), + {'EXIT',{badarg, _}} = (catch erlang:get_module_info(module_info_test)), + + %% then purge it + true = erlang:purge_module(module_info_test), {'EXIT',{undef, _}} = (catch module_info_test:f()), {'EXIT',{badarg, _}} = (catch erlang:get_module_info(module_info_test,attributes)), {'EXIT',{badarg, _}} = (catch erlang:get_module_info(module_info_test)), -- cgit v1.2.3 From fe1c0d26d4e6180b79fc8497b827ac2ef1f471d5 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 5 Jun 2015 17:25:22 +0200 Subject: Delayed node table GC --- erts/doc/src/erl.xml | 12 ++ erts/doc/src/erlang.xml | 10 ++ erts/emulator/beam/dist.c | 2 +- erts/emulator/beam/erl_bif_info.c | 20 +++ erts/emulator/beam/erl_init.c | 36 ++++- erts/emulator/beam/erl_node_tables.c | 202 +++++++++++++++++++++++----- erts/emulator/beam/erl_node_tables.h | 16 ++- erts/emulator/test/node_container_SUITE.erl | 47 ++++--- erts/etc/common/erlexec.c | 1 + erts/preloaded/ebin/erlang.beam | Bin 101808 -> 101840 bytes erts/preloaded/src/erlang.erl | 1 + 11 files changed, 288 insertions(+), 59 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index ea94a4e82b..00da503469 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1350,6 +1350,18 @@ give lower latency and higher throughput at the expense of higher memory usage.

+ +zdntgc time + +

Set the delayed node table garbage collection time + (delayed_node_table_gc) + in seconds. Valid values are either infinity or + an integer in the range [0-100000000]. Default is 60.

+

Node table entries that are not referred will linger + in the table for at least the amount of time that this + parameter determines. The lingering prevents repeated + deletions and insertions in the tables from occurring. +

+
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 3fea64cef5..50a26781c4 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6222,6 +6222,7 @@ ok + Information about the system

Returns various information about the current system @@ -6291,6 +6292,15 @@ ok compiled; otherwise, false.

+ delayed_node_table_gc + +

Returns the amount of time in seconds that garbage collection + of an entry in a node table will be delayed. This limit can be set + on startup by passing the + +zdntgc command line + flag to erl. For more information see the documentation of the + command line flag.

+
dirty_cpu_schedulers

Returns the number of dirty CPU scheduler threads used by diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 142fcb3c00..cfdede793c 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -2522,7 +2522,7 @@ info_dist_entry(int to, void *arg, DistEntry *dep, int visible, int connected) erts_print(to, arg, "Name: %T", dep->sysname); #ifdef DEBUG - erts_print(to, arg, " (refc=%d)", erts_refc_read(&dep->refc, 1)); + erts_print(to, arg, " (refc=%d)", erts_refc_read(&dep->refc, 0)); #endif erts_print(to, arg, "\n"); if (!connected && is_nil(dep->cid)) { diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index ae8a5c266a..23fc4f915e 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2687,6 +2687,15 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) hp = hsz ? HAlloc(BIF_P, hsz) : NULL; res = erts_bld_uint(&hp, NULL, erts_dist_buf_busy_limit); BIF_RET(res); + } else if (ERTS_IS_ATOM_STR("delayed_node_table_gc", BIF_ARG_1)) { + Uint hsz = 0; + Uint dntgc = erts_delayed_node_table_gc(); + if (dntgc == ERTS_NODE_TAB_DELAY_GC_INFINITY) + BIF_RET(am_infinity); + (void) erts_bld_uint(NULL, &hsz, dntgc); + hp = hsz ? HAlloc(BIF_P, hsz) : NULL; + res = erts_bld_uint(&hp, NULL, dntgc); + BIF_RET(res); } else if (ERTS_IS_ATOM_STR("ethread_info", BIF_ARG_1)) { BIF_RET(erts_get_ethread_info(BIF_P)); } @@ -4069,6 +4078,17 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) int res = erts_debug_set_unique_monotonic_integer_state(BIF_ARG_2); BIF_RET(res ? am_true : am_false); } + else if (ERTS_IS_ATOM_STR("node_tab_delayed_delete", BIF_ARG_1)) { + /* node_container_SUITE */ + Sint64 msecs; + if (term_to_Sint64(BIF_ARG_2, &msecs)) { + /* Negative value restore original value... */ + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_debug_test_node_tab_delayed_delete(msecs); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + BIF_RET(am_ok); + } + } } BIF_ERROR(BIF_P, BADARG); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 988ff0e2b5..a2d2a3befc 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -141,7 +141,8 @@ static void erl_init(int ncpu, int port_tab_sz_ignore_files, int legacy_port_tab, int time_correction, - ErtsTimeWarpMode time_warp_mode); + ErtsTimeWarpMode time_warp_mode, + int node_tab_delete_delay); static erts_atomic_t exiting; @@ -314,7 +315,8 @@ erts_short_init(void) 0, 0, time_correction, - time_warp_mode); + time_warp_mode, + ERTS_NODE_TAB_DELAY_GC_DEFAULT); erts_initialized = 1; } @@ -326,7 +328,8 @@ erl_init(int ncpu, int port_tab_sz_ignore_files, int legacy_port_tab, int time_correction, - ErtsTimeWarpMode time_warp_mode) + ErtsTimeWarpMode time_warp_mode, + int node_tab_delete_delay) { init_benchmarking(); @@ -367,7 +370,7 @@ erl_init(int ncpu, erts_init_binary(); /* Must be after init_emulator() */ erts_bp_init(); init_db(); /* Must be after init_emulator */ - erts_init_node_tables(); + erts_init_node_tables(node_tab_delete_delay); init_dist(); erl_drv_thr_init(); erts_init_async(); @@ -629,6 +632,9 @@ void erts_usage(void) erts_fprintf(stderr, " see error_logger documentation for details\n"); erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n"); erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024); + erts_fprintf(stderr, "-zdntgc time set delayed node table gc in seconds\n"); + erts_fprintf(stderr, " valid values are infinity or intergers in the range [0-%d]\n", + ERTS_NODE_TAB_DELAY_GC_MAX); erts_fprintf(stderr, "\n"); erts_fprintf(stderr, "Note that if the emulator is started with erlexec (typically\n"); erts_fprintf(stderr, "from the erl script), these flags should be specified with +.\n"); @@ -1219,6 +1225,7 @@ erl_start(int argc, char **argv) int legacy_port_tab = 0; int time_correction; ErtsTimeWarpMode time_warp_mode; + int node_tab_delete_delay = ERTS_NODE_TAB_DELAY_GC_DEFAULT; set_default_time_adj(&time_correction, &time_warp_mode); @@ -2004,9 +2011,9 @@ erl_start(int argc, char **argv) case 'z': { char *sub_param = argv[i]+2; - int new_limit; if (has_prefix("dbbl", sub_param)) { + int new_limit; arg = get_arg(sub_param+4, argv[i+1], &i); new_limit = atoi(arg); if (new_limit < 1 || INT_MAX/1024 < new_limit) { @@ -2015,6 +2022,22 @@ erl_start(int argc, char **argv) } else { erts_dist_buf_busy_limit = new_limit*1024; } + } + else if (has_prefix("dntgc", sub_param)) { + long secs; + + arg = get_arg(sub_param+5, argv[i+1], &i); + if (sys_strcmp(arg, "infinity") == 0) + secs = ERTS_NODE_TAB_DELAY_GC_INFINITY; + else { + errno = 0; + secs = strtol(arg, NULL, 10); + if (errno != 0 || secs < 0 || ERTS_NODE_TAB_DELAY_GC_MAX < secs) { + erts_fprintf(stderr, "Invalid delayed node table gc: %ld\n", secs); + erts_usage(); + } + } + node_tab_delete_delay = (int) secs; } else { erts_fprintf(stderr, "bad -z option %s\n", argv[i]); erts_usage(); @@ -2088,7 +2111,8 @@ erl_start(int argc, char **argv) port_tab_sz_ignore_files, legacy_port_tab, time_correction, - time_warp_mode); + time_warp_mode, + node_tab_delete_delay); load_preloaded(); erts_end_staging_code_ix(); diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 900fdb3ab5..c2acaea0bc 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -51,6 +51,9 @@ static Uint dist_entries; static int references_atoms_need_init = 1; +static ErtsMonotonicTime orig_node_tab_delete_delay; +static ErtsMonotonicTime node_tab_delete_delay; + /* -- The distribution table ---------------------------------------------- */ #ifdef DEBUG @@ -290,21 +293,46 @@ DistEntry *erts_find_dist_entry(Eterm sysname) return res; } -void erts_delete_dist_entry(DistEntry *dep) +static void try_delete_dist_entry(void *vdep) +{ + DistEntry *dep = (DistEntry *) vdep; + erts_aint_t refc; + + erts_smp_rwmtx_rwlock(&erts_dist_table_rwmtx); + /* + * Another thread might have looked up this dist entry after + * we decided to delete it (refc became zero). If so, the other + * thread incremented refc twice. Once for the new reference + * and once for this thread. + * + * If refc reach -1, noone has used the entry since we + * set up the timer. Delete the entry. + * + * If refc reach 0, the entry is currently not in use + * but has been used since we set up the timer. Set up a + * new timer. + * + * If refc > 0, the entry is in use. Keep the entry. + */ + refc = erts_refc_dectest(&dep->refc, -1); + if (refc == -1) + (void) hash_erase(&erts_dist_table, (void *) dep); + erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx); + + if (refc == 0) + erts_schedule_delete_dist_entry(dep); +} + +void erts_schedule_delete_dist_entry(DistEntry *dep) { ASSERT(dep != erts_this_dist_entry); - if(dep != erts_this_dist_entry) { - erts_smp_rwmtx_rwlock(&erts_dist_table_rwmtx); - /* - * Another thread might have looked up this dist entry after - * we decided to delete it (refc became zero). If so, the other - * thread incremented refc twice. Once for the new reference - * and once for this thread. Therefore, delete dist entry if - * refc is 0 or -1 after a decrement. - */ - if (erts_refc_dectest(&dep->refc, -1) <= 0) - (void) hash_erase(&erts_dist_table, (void *) dep); - erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx); + if (dep != erts_this_dist_entry) { + if (node_tab_delete_delay == 0) + try_delete_dist_entry((void *) dep); + else if (node_tab_delete_delay > 0) + erts_start_timer_callback(node_tab_delete_delay, + try_delete_dist_entry, + (void *) dep); } } @@ -609,21 +637,46 @@ ErlNode *erts_find_or_insert_node(Eterm sysname, Uint creation) return res; } -void erts_delete_node(ErlNode *enp) +static void try_delete_node(void *venp) +{ + ErlNode *enp = (ErlNode *) venp; + erts_aint_t refc; + + erts_smp_rwmtx_rwlock(&erts_node_table_rwmtx); + /* + * Another thread might have looked up this node after we + * decided to delete it (refc became zero). If so, the other + * thread incremented refc twice. Once for the new reference + * and once for this thread. + * + * If refc reach -1, noone has used the entry since we + * set up the timer. Delete the entry. + * + * If refc reach 0, the entry is currently not in use + * but has been used since we set up the timer. Set up a + * new timer. + * + * If refc > 0, the entry is in use. Keep the entry. + */ + refc = erts_refc_dectest(&enp->refc, -1); + if (refc == -1) + (void) hash_erase(&erts_node_table, (void *) enp); + erts_smp_rwmtx_rwunlock(&erts_node_table_rwmtx); + + if (refc == 0) + erts_schedule_delete_node(enp); +} + +void erts_schedule_delete_node(ErlNode *enp) { ASSERT(enp != erts_this_node); - if(enp != erts_this_node) { - erts_smp_rwmtx_rwlock(&erts_node_table_rwmtx); - /* - * Another thread might have looked up this node after we - * decided to delete it (refc became zero). If so, the other - * thread incremented refc twice. Once for the new reference - * and once for this thread. Therefore, delete node if refc - * is 0 or -1 after a decrement. - */ - if (erts_refc_dectest(&enp->refc, -1) <= 0) - (void) hash_erase(&erts_node_table, (void *) enp); - erts_smp_rwmtx_rwunlock(&erts_node_table_rwmtx); + if (enp != erts_this_node) { + if (node_tab_delete_delay == 0) + try_delete_node((void *) enp); + else if (node_tab_delete_delay > 0) + erts_start_timer_callback(node_tab_delete_delay, + try_delete_node, + (void *) enp); } } @@ -651,7 +704,7 @@ static void print_node(void *venp, void *vpndp) erts_print(pndp->to, pndp->to_arg, " %d", enp->creation); #ifdef DEBUG erts_print(pndp->to, pndp->to_arg, " (refc=%ld)", - erts_refc_read(&enp->refc, 1)); + erts_refc_read(&enp->refc, 0)); #endif pndp->no_sysname++; } @@ -714,11 +767,28 @@ erts_set_this_node(Eterm sysname, Uint creation) } -void erts_init_node_tables(void) +Uint +erts_delayed_node_table_gc(void) +{ + if (node_tab_delete_delay < 0) + return (Uint) ERTS_NODE_TAB_DELAY_GC_INFINITY; + if (node_tab_delete_delay == 0) + return (Uint) 0; + return (Uint) ((node_tab_delete_delay-1)/1000 + 1); +} + +void erts_init_node_tables(int dd_sec) { erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; HashFunctions f; + if (dd_sec == ERTS_NODE_TAB_DELAY_GC_INFINITY) + node_tab_delete_delay = (ErtsMonotonicTime) -1; + else + node_tab_delete_delay = ((ErtsMonotonicTime) dd_sec)*1000; + + orig_node_tab_delete_delay = node_tab_delete_delay; + rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ; rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED; @@ -847,6 +917,7 @@ static Eterm AM_dist_references; static Eterm AM_node_references; static Eterm AM_system; static Eterm AM_timer; +static Eterm AM_delayed_delete_timer; static void setup_reference_table(void); static Eterm reference_table_term(Uint **hpp, Uint *szp); @@ -881,8 +952,10 @@ typedef struct dist_referrer_ { int heap_ref; int node_ref; int ctrl_ref; + int system_ref; Eterm id; Uint creation; + Uint id_heap[ID_HEAP_SIZE]; } DistReferrer; typedef struct { @@ -931,6 +1004,7 @@ erts_get_node_and_dist_references(struct process *proc) INIT_AM(node_references); INIT_AM(timer); INIT_AM(system); + INIT_AM(delayed_delete_timer); references_atoms_need_init = 0; } @@ -988,17 +1062,25 @@ insert_dist_referrer(ReferredDist *referred_dist, sizeof(DistReferrer)); drp->next = referred_dist->referrers; referred_dist->referrers = drp; - drp->id = id; + if(IS_CONST(id)) + drp->id = id; + else { + Uint *hp = &drp->id_heap[0]; + ASSERT(is_tuple(id)); + drp->id = copy_struct(id, size_object(id), &hp, NULL); + } drp->creation = creation; drp->heap_ref = 0; drp->node_ref = 0; drp->ctrl_ref = 0; + drp->system_ref = 0; } switch (type) { case NODE_REF: drp->node_ref++; break; case CTRL_REF: drp->ctrl_ref++; break; case HEAP_REF: drp->heap_ref++; break; + case SYSTEM_REF: drp->system_ref++; break; default: ASSERT(0); } } @@ -1260,6 +1342,33 @@ insert_sys_msg(Eterm from, Eterm to, Eterm msg, ErlHeapFragment *bp) } #endif +static void +insert_delayed_delete_node(void *state, + ErtsMonotonicTime timeout_pos, + void *vnp) +{ + DeclareTmpHeapNoproc(heap,3); + UseTmpHeapNoproc(3); + insert_node((ErlNode *) vnp, + SYSTEM_REF, + TUPLE2(&heap[0], AM_system, AM_delayed_delete_timer)); + UnUseTmpHeapNoproc(3); +} + +static void +insert_delayed_delete_dist_entry(void *state, + ErtsMonotonicTime timeout_pos, + void *vdep) +{ + DeclareTmpHeapNoproc(heap,3); + UseTmpHeapNoproc(3); + insert_dist_entry((DistEntry *) vdep, + SYSTEM_REF, + TUPLE2(&heap[0], AM_system, AM_delayed_delete_timer), + 0); + UnUseTmpHeapNoproc(3); +} + static void setup_reference_table(void) { @@ -1288,6 +1397,13 @@ setup_reference_table(void) /* Go through the hole system, and build a table of all references to ErlNode and DistEntry structures */ + erts_debug_callback_timer_foreach(try_delete_node, + insert_delayed_delete_node, + NULL); + erts_debug_callback_timer_foreach(try_delete_dist_entry, + insert_delayed_delete_dist_entry, + NULL); + UseTmpHeapNoproc(3); insert_node(erts_this_node, SYSTEM_REF, @@ -1601,7 +1717,7 @@ reference_table_term(Uint **hpp, Uint *szp) tup = MK_2TUP(referred_nodes[i].node->sysname, MK_UINT(referred_nodes[i].node->creation)); - tup = MK_3TUP(tup, MK_UINT(erts_refc_read(&referred_nodes[i].node->refc, 1)), nril); + tup = MK_3TUP(tup, MK_UINT(erts_refc_read(&referred_nodes[i].node->refc, 0)), nril); nl = MK_CONS(tup, nl); } @@ -1624,6 +1740,10 @@ reference_table_term(Uint **hpp, Uint *szp) tup = MK_2TUP(AM_heap, MK_UINT(drp->heap_ref)); drl = MK_CONS(tup, drl); } + if(drp->system_ref) { + tup = MK_2TUP(AM_system, MK_UINT(drp->system_ref)); + drl = MK_CONS(tup, drl); + } if (is_internal_pid(drp->id)) { ASSERT(!drp->node_ref); @@ -1633,6 +1753,14 @@ reference_table_term(Uint **hpp, Uint *szp) ASSERT(drp->ctrl_ref && !drp->node_ref); tup = MK_2TUP(AM_port, drp->id); } + else if (is_tuple(drp->id)) { + Eterm *t; + ASSERT(drp->system_ref && !drp->node_ref + && !drp->ctrl_ref && !drp->heap_ref); + t = tuple_val(drp->id); + ASSERT(2 == arityval(t[0])); + tup = MK_2TUP(t[1], t[2]); + } else { ASSERT(!drp->ctrl_ref && drp->node_ref); ASSERT(is_atom(drp->id)); @@ -1650,7 +1778,7 @@ reference_table_term(Uint **hpp, Uint *szp) /* DistList = [{Dist, Refc, ReferenceIdList}] */ tup = MK_3TUP(referred_dists[i].dist->sysname, - MK_UINT(erts_refc_read(&referred_dists[i].dist->refc, 1)), + MK_UINT(erts_refc_read(&referred_dists[i].dist->refc, 0)), dril); dl = MK_CONS(tup, dl); } @@ -1705,3 +1833,15 @@ delete_reference_table(void) } } +void +erts_debug_test_node_tab_delayed_delete(Sint64 millisecs) +{ + erts_smp_thr_progress_block(); + + if (millisecs < 0) + node_tab_delete_delay = orig_node_tab_delete_delay; + else + node_tab_delete_delay = millisecs; + + erts_smp_thr_progress_unblock(); +} diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index af60071ea5..54c5cd1d11 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -46,6 +46,10 @@ #define ERTS_PORT_TASK_ONLY_BASIC_TYPES__ #include "erl_port_task.h" #undef ERTS_PORT_TASK_ONLY_BASIC_TYPES__ + +#define ERTS_NODE_TAB_DELAY_GC_DEFAULT (60) +#define ERTS_NODE_TAB_DELAY_GC_MAX (100*1000*1000) +#define ERTS_NODE_TAB_DELAY_GC_INFINITY (ERTS_NODE_TAB_DELAY_GC_MAX+1) #define ERST_INTERNAL_CHANNEL_NO 0 @@ -166,20 +170,21 @@ extern DistEntry *erts_this_dist_entry; extern ErlNode *erts_this_node; extern char *erts_this_node_sysname; /* must match erl_node_tables.c */ +Uint erts_delayed_node_table_gc(void); DistEntry *erts_channel_no_to_dist_entry(Uint); DistEntry *erts_sysname_to_connected_dist_entry(Eterm); DistEntry *erts_find_or_insert_dist_entry(Eterm); DistEntry *erts_find_dist_entry(Eterm); -void erts_delete_dist_entry(DistEntry *); +void erts_schedule_delete_dist_entry(DistEntry *); Uint erts_dist_table_size(void); void erts_dist_table_info(int, void *); void erts_set_dist_entry_not_connected(DistEntry *); void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint); ErlNode *erts_find_or_insert_node(Eterm, Uint); -void erts_delete_node(ErlNode *); +void erts_schedule_delete_node(ErlNode *); void erts_set_this_node(Eterm, Uint); Uint erts_node_table_size(void); -void erts_init_node_tables(void); +void erts_init_node_tables(int); void erts_node_table_info(int, void *); void erts_print_node_info(int, void *, Eterm, int*, int*); Eterm erts_get_node_and_dist_references(struct process *); @@ -204,7 +209,7 @@ erts_deref_dist_entry(DistEntry *dep) { ASSERT(dep); if (erts_refc_dectest(&dep->refc, 0) == 0) - erts_delete_dist_entry(dep); + erts_schedule_delete_dist_entry(dep); } ERTS_GLB_INLINE void @@ -212,7 +217,7 @@ erts_deref_node_entry(ErlNode *np) { ASSERT(np); if (erts_refc_dectest(&np->refc, 0) == 0) - erts_delete_node(np); + erts_schedule_delete_node(np); } ERTS_GLB_INLINE void @@ -253,5 +258,6 @@ erts_smp_de_links_unlock(DistEntry *dep) #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ +void erts_debug_test_node_tab_delayed_delete(Sint64 millisecs); #endif diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 2f505893b4..479e6f200a 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -73,6 +73,8 @@ init_per_suite(Config) -> Config. end_per_suite(_Config) -> + erts_debug:set_internal_state(available_internal_state, true), + erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value available_internal_state(false). init_per_group(_GroupName, Config) -> @@ -419,6 +421,8 @@ node_table_gc(doc) -> ["Tests that node tables are garbage collected."]; node_table_gc(suite) -> []; node_table_gc(Config) when is_list(Config) -> + erts_debug:set_internal_state(available_internal_state, true), + erts_debug:set_internal_state(node_tab_delayed_delete, 0), ?line PreKnown = nodes(known), ?line ?t:format("PreKnown = ~p~n", [PreKnown]), ?line make_node_garbage(0, 200000, 1000, []), @@ -428,6 +432,7 @@ node_table_gc(Config) when is_list(Config) -> ?line ?t:format("PostAreas = ~p~n", [PostAreas]), ?line true = length(PostKnown) =< length(PreKnown), ?line nc_refc_check(node()), + erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value ?line ok. make_node_garbage(N, L, I, Ps) when N < L -> @@ -579,6 +584,8 @@ node_controller_refc(doc) -> "as they should for entities controlling a connections."]; node_controller_refc(suite) -> []; node_controller_refc(Config) when is_list(Config) -> + erts_debug:set_internal_state(available_internal_state, true), + erts_debug:set_internal_state(node_tab_delayed_delete, 0), ?line NodeFirstName = get_nodefirstname(), ?line ?line {ok, Node} = start_node(NodeFirstName), ?line true = lists:member(Node, nodes()), @@ -606,6 +613,7 @@ node_controller_refc(Config) when is_list(Config) -> ?line false = get_dist_references(Node), ?line false = lists:member(Node, nodes(known)), ?line nc_refc_check(node()), + erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value ?line ok. %% @@ -686,9 +694,6 @@ timer_refc(doc) -> "as they should for data stored in bif timers."]; timer_refc(suite) -> []; timer_refc(Config) when is_list(Config) -> - {skipped, "Test needs to be UPDATED for new timer implementation"}. - -timer_refc_test(Config) when is_list(Config) -> ?line RNode = {get_nodename(), 1}, ?line RPid = mk_pid(RNode, 4711, 2), ?line RPort = mk_port(RNode, 4711), @@ -992,23 +997,32 @@ check_nd_refc({ThisNodeName, ThisCreation}, NodeRefs, DistRefs, Fail) -> check_refc(ThisNodeName,ThisCreation,Table,EntryList) when is_list(EntryList) -> lists:foreach( fun ({Entry, Refc, ReferrerList}) -> - FoundRefs = + {DelayedDeleteTimer, + FoundRefs} = lists:foldl( - fun ({_Referrer, ReferencesList}, A1) -> - A1 + lists:foldl(fun ({_T,Rs},A2) -> - A2+Rs - end, - 0, - ReferencesList) + fun ({Referrer, ReferencesList}, {DDT, A1}) -> + {case Referrer of + {system,delayed_delete_timer} -> + true; + _ -> + DDT + end, + A1 + lists:foldl(fun ({_T,Rs},A2) -> + A2+Rs + end, + 0, + ReferencesList)} end, - 0, + {false, 0}, ReferrerList), - %% Reference count equals found references ? - case Refc =:= FoundRefs of - true -> + %% Reference count equals found references? + case {Refc, FoundRefs, DelayedDeleteTimer} of + {X, X, _} -> + ok; + {0, 1, true} -> ok; - false -> + _ -> exit({invalid_reference_count, Table, Entry}) end, @@ -1016,7 +1030,8 @@ check_refc(ThisNodeName,ThisCreation,Table,EntryList) when is_list(EntryList) -> case {Entry, Refc} of {ThisNodeName, 0} -> ok; {{ThisNodeName, ThisCreation}, 0} -> ok; - {_, 0} -> exit({not_referred_entry_in_table, Table, Entry}); + {_, 0} when DelayedDeleteTimer == false -> + exit({not_referred_entry_in_table, Table, Entry}); {_, _} -> ok end diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 23226909a7..d098100a51 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -157,6 +157,7 @@ static char *plusr_val_switches[] = { /* +z arguments with values */ static char *plusz_val_switches[] = { "dbbl", + "dntgc", NULL }; diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index b99dc7339a..863a5e61ef 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 9d9208d1b5..cf941ea6ca 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2392,6 +2392,7 @@ tuple_to_list(_Tuple) -> CpuTopology :: cpu_topology(); (creation) -> integer(); (debug_compiled) -> boolean(); + (delayed_node_table_gc) -> infinity | non_neg_integer(); (dirty_cpu_schedulers) -> non_neg_integer(); (dirty_cpu_schedulers_online) -> non_neg_integer(); (dirty_io_schedulers) -> non_neg_integer(); -- cgit v1.2.3 From e6fce01ceb4e9ad27aade58f69857a5d9eb0d734 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 10 Jun 2015 20:13:33 +0200 Subject: Fix error checking for +zdntgc flag --- erts/emulator/beam/erl_init.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index a2d2a3befc..574a49dc7a 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -2030,10 +2030,12 @@ erl_start(int argc, char **argv) if (sys_strcmp(arg, "infinity") == 0) secs = ERTS_NODE_TAB_DELAY_GC_INFINITY; else { + char *endptr; errno = 0; - secs = strtol(arg, NULL, 10); - if (errno != 0 || secs < 0 || ERTS_NODE_TAB_DELAY_GC_MAX < secs) { - erts_fprintf(stderr, "Invalid delayed node table gc: %ld\n", secs); + secs = strtol(arg, &endptr, 10); + if (errno != 0 || *arg == '\0' || *endptr != '\0' + || secs < 0 || ERTS_NODE_TAB_DELAY_GC_MAX < secs) { + erts_fprintf(stderr, "Invalid delayed node table gc: %s\n", arg); erts_usage(); } } -- cgit v1.2.3 From d1fbf82a0a43f91e2bbf95060dc09a1573e487c2 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 8 Feb 2014 01:39:57 +0100 Subject: Document abstract format of type-related trees --- erts/doc/src/absform.xml | 163 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 835a4fc692..12cb06c151 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -80,6 +80,28 @@ If F is a record declaration , then Rep(F) = . For Rep(V), see below. + If F is a type attribute (i.e. or + ) + where each + is a variable, then Rep(F) = + . + For Rep(T), see below. + If F is a type spec (i.e. or + ) + , + where each is a fun type clause with an + argument sequence of the same length , then + Rep(F) = + . + For Rep(Tc_i), see below. + If F is a type spec (i.e. or + ) + , + where each is a fun type clause with an + argument sequence of the same length , then + Rep(F) = + . + For Rep(Tc_i), see below. If F is a wild attribute , then Rep(F) = .

@@ -89,6 +111,132 @@ Rep(F) = . +

+ Type clauses + + If T is a fun type clause + Ret]]>, where each + and are types, then + Rep(T) = + . + + If T is a bounded fun type clause , + where is an unbounded fun type clause and + is a type guard sequence, then Rep(T) = + . + +
+ +
+ Type guards + + If G is a constraint , where + is an atom and each is a + type, then Rep(G) = + . + + If G is a type definition , + where is a variable and + is a type, then Rep(G) = + . + +
+ +
+ Types + + If T is a type definition , + where is a variable and + is a type, then Rep(T) = + . + If T is a type union , + where each is a type, then Rep(T) = + . + If T is a type range , + where and are types, then + Rep(T) = . + If T is a binary operation , + where is an arithmetic or bitwise binary operator + and and are types, then + Rep(T) = . + If T is , where is an + arithmetic or bitwise unary operator and is a + type, then Rep(T) = . + If T is a fun type , then Rep(T) = + . + If T is a parenthesized type , then + Rep(T) = , i.e. parenthesized + types are distinguished from their bodies. It should be noted though + that parenthesized types that are immediate subtrees of operator + expressions and binary types are peeled off. + If T is a variable , then Rep(T) = + , where is an atom + with a printname consisting of the same characters as + . + If T is an atomic literal L and L is not a string literal, then + Rep(T) = Rep(L). + If T is a tuple or map type (i.e. + or ), then Rep(T) = + . + If T is a type , where each + is a type, then Rep(T) = + . + If T is a remote type , where + each is a type and and + , then Rep(T) = + . + + If T is the nil type , then Rep(T) = + . + If T is a list type , where + is a type, then Rep(T) = + . + If T is a non-empty list type , where + is a type, then Rep(T) = + . + If T is a map type , where each + is a map pair type, then Rep(T) = + . + If T is a map pair type V]]>, where + and are types, + then Rep(T) = + . + If T is a tuple type , where + each is a type, then Rep(T) = + . + If T is a record type , where + is an atom, then Rep(T) = + . + If T is a record type , + where is an atom, then Rep(T) = + . + + If T is a record field type , + where is an atom, then Rep(T) = + . + If T is a record field type >]]>, then Rep(T) = + . + + If T is a binary type >]]>, where + is a type, then Rep(T) = + . + If T is a binary type >]]>, + where is a type, then Rep(T) = + . + If T is a binary type >]]>, + where and is a type, then + Rep(T) = + . + + If T is a fun type Ret)]]>, then + Rep(T) = . + + If T is a fun type , where + is an unbounded fun type clause, + then Rep(T) = . + +
+
Record fields

Each field in a record declaration may have an optional @@ -98,6 +246,21 @@ Rep(V) = . If V is , then Rep(V) = . + If V is , where is + an atom and is a type and it does not contain + syntactically, then Rep(V) = + . + Note that if is an annotated type, it will be wrapped in + parentheses. + If V is , where is + an atom and is a type, then Rep(V) = + . + + If V is , where + is an atom, is an expression and + is a type, then Rep(V) = + . +

-- cgit v1.2.3 From 417f9960371607e6d618d9dda108787558a9cef5 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 10 Jun 2015 16:30:53 +0200 Subject: Update the documentation of the abstract format The parenthesized type with tag 'paren_type' is no longer created by the Erlang Parser as of OTP 18.0. The tag 'user_type' is used for user defined types as of OTP 18.0. In releases before commit 7ad783 'type' is used. --- erts/doc/src/absform.xml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 12cb06c151..e1a8c2e517 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -4,7 +4,7 @@
- 20012013 + 20012015 Ericsson AB. All Rights Reserved. @@ -164,11 +164,6 @@ type, then Rep(T) = . If T is a fun type , then Rep(T) = . - If T is a parenthesized type , then - Rep(T) = , i.e. parenthesized - types are distinguished from their bodies. It should be noted though - that parenthesized types that are immediate subtrees of operator - expressions and binary types are peeled off. If T is a variable , then Rep(T) = , where is an atom with a printname consisting of the same characters as @@ -180,7 +175,7 @@ . If T is a type , where each is a type, then Rep(T) = - . + . If T is a remote type , where each is a type and and , then Rep(T) = -- cgit v1.2.3 From 485187dfdb5ec628efed755c88331ca296bc33dc Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Mon, 15 Jun 2015 12:01:45 +0200 Subject: Update prim_inet.beam --- erts/preloaded/ebin/prim_inet.beam | Bin 73092 -> 72748 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 6729f06b79..5a188be3ba 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ -- cgit v1.2.3 From c09561dc51be736943a32862a41a3eaef2e41b83 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 3 Jun 2015 16:19:30 +0200 Subject: erts: Yield in maps:merge --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/erl_init.c | 1 + erts/emulator/beam/erl_map.c | 255 ++++++++++++++++++++++++++++++------------ erts/emulator/beam/global.h | 13 ++- 4 files changed, 196 insertions(+), 74 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 5ec1409adf..74b42c647e 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -350,6 +350,7 @@ atom message atom message_binary atom message_queue_len atom messages +atom merge_trap atom meta atom meta_match_spec atom micro_seconds diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 988ff0e2b5..98ab9f598f 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -379,6 +379,7 @@ erl_init(int ncpu, erts_init_bif_re(); erts_init_unicode(); /* after RE to get access to PCRE unicode */ erts_init_external(); + erts_init_map(); erts_delay_trap = erts_export_put(am_erlang, am_delay_trap, 2); erts_late_init_process(); #if HAVE_ERTS_MSEG diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 5802ec76ba..c1ad5658d9 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -32,6 +32,7 @@ #include "erl_process.h" #include "error.h" #include "bif.h" +#include "erl_binary.h" #include "erl_map.h" @@ -79,8 +80,12 @@ typedef struct { static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB); -static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args); -static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB, int swap_args); +static BIF_RETTYPE map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args); +struct HashmapMergeContext_; +static BIF_RETTYPE hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB, int swap_args, + struct HashmapMergeContext_*); +static Export hashmap_merge_trap_export; +static BIF_RETTYPE maps_merge_trap_1(BIF_ALIST_1); static Eterm hashmap_to_list(Process *p, Eterm map); static Eterm hashmap_keys(Process *p, Eterm map); static Eterm hashmap_values(Process *p, Eterm map); @@ -95,6 +100,15 @@ static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]); static int hxnodecmp(hxnode_t* a, hxnode_t* b); static int hxnodecmpkey(hxnode_t* a, hxnode_t* b); + +void erts_init_map(void) { + erts_init_trap_export(&hashmap_merge_trap_export, + am_maps, am_merge_trap, 1, + &maps_merge_trap_1); + return; +} + + /* erlang:map_size/1 * the corresponding instruction is implemented in: * beam/erl_bif_guard.c @@ -942,15 +956,15 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { BIF_RET(flatmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2)); } else if (is_hashmap(BIF_ARG_2)) { /* Will always become a tree */ - BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_1, BIF_ARG_2, 0)); + return map_merge_mixed(BIF_P, BIF_ARG_1, BIF_ARG_2, 0); } BIF_P->fvalue = BIF_ARG_2; } else if (is_hashmap(BIF_ARG_1)) { if (is_hashmap(BIF_ARG_2)) { - BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2, 0)); + return hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2, 0, NULL); } else if (is_flatmap(BIF_ARG_2)) { /* Will always become a tree */ - BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_2, BIF_ARG_1, 1)); + return map_merge_mixed(BIF_P, BIF_ARG_2, BIF_ARG_1, 1); } BIF_P->fvalue = BIF_ARG_2; } else { @@ -1113,30 +1127,63 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args) erts_free(ERTS_ALC_T_TMP, (void *) hxns); ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); - return hashmap_merge(p, res, tree, swap_args); + return hashmap_merge(p, res, tree, swap_args, NULL); +} + +#define PSTACK_TYPE struct HashmapMergePStackType +struct HashmapMergePStackType { + Eterm *srcA, *srcB; + Uint32 abm, bbm, rbm; /* node bitmaps */ + int keepA; + int ix; + Eterm array[16]; +}; + +typedef struct HashmapMergeContext_ { + Uint size; /* total key-value counter */ + unsigned int lvl; + Eterm nodeA, nodeB; + Eterm res; + Eterm trap_bin; + ErtsPStack pstack; +#ifdef DEBUG + Eterm dbg_map_A, dbg_map_B; +#endif +} HashmapMergeContext; + +static void hashmap_merge_ctx_destructor(Binary* ctx_bin) +{ + HashmapMergeContext* ctx = (HashmapMergeContext*) ERTS_MAGIC_BIN_DATA(ctx_bin); + ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(ctx_bin) == hashmap_merge_ctx_destructor); + + PSTACK_DESTROY_SAVED(&ctx->pstack); +} + +BIF_RETTYPE maps_merge_trap_1(BIF_ALIST_1) { + Binary* ctx_bin = ((ProcBin *) binary_val(BIF_ARG_1))->val; + + ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(ctx_bin) == hashmap_merge_ctx_destructor); + + return hashmap_merge(BIF_P, NIL, NIL, 0, + (HashmapMergeContext*) ERTS_MAGIC_BIN_DATA(ctx_bin)); } #define HALLOC_EXTRA 200 +#define MAP_MERGE_LOOP_FACTOR 8 -static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB, int swap_args) { +static BIF_RETTYPE hashmap_merge(Process *p, Eterm map_A, Eterm map_B, + int swap_args, HashmapMergeContext* ctx) { #define PSTACK_TYPE struct HashmapMergePStackType - struct HashmapMergePStackType { - Eterm *srcA, *srcB; - Uint32 abm, bbm, rbm; /* node bitmaps */ - int keepA; - int ix; - Eterm array[16]; - }; PSTACK_DECLARE(s, 4); - struct HashmapMergePStackType* sp = PSTACK_PUSH(s); - Eterm *hp, *nhp; - Eterm hdrA, hdrB; + HashmapMergeContext local_ctx; + struct HashmapMergePStackType* sp; Uint32 ahx, bhx; - Uint size; /* total key-value counter */ - int keepA = swap_args; - unsigned int lvl = 0; + Eterm hdrA, hdrB; + Eterm *hp, *nhp; + Eterm trap_ret; + Sint initial_reds = (Sint) (ERTS_BIF_REDS_LEFT(p) * MAP_MERGE_LOOP_FACTOR); + Sint reds = initial_reds; DeclareTmpHeap(th,2,p); - Eterm res = THE_NON_VALUE; UseTmpHeap(2,p); /* @@ -1144,52 +1191,72 @@ static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB, int swap_args) * and merge each pair of nodes. */ - { - hashmap_head_t* a = (hashmap_head_t*) hashmap_val(nodeA); - hashmap_head_t* b = (hashmap_head_t*) hashmap_val(nodeB); - size = a->size + b->size; + PSTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); + + if (ctx == NULL) { /* first call */ + hashmap_head_t* a = (hashmap_head_t*) hashmap_val(map_A); + hashmap_head_t* b = (hashmap_head_t*) hashmap_val(map_B); + + sp = PSTACK_PUSH(s); + sp->keepA = swap_args; + local_ctx.size = a->size + b->size; + local_ctx.lvl = 0; + local_ctx.nodeA = map_A; + local_ctx.nodeB = map_B; + local_ctx.res = THE_NON_VALUE; + #ifdef DEBUG + local_ctx.dbg_map_A = map_A; + local_ctx.dbg_map_B = map_B; + local_ctx.trap_bin = THE_NON_VALUE; + #endif + ctx = &local_ctx; + } + else { + PSTACK_RESTORE(s, &ctx->pstack); + sp = PSTACK_TOP(s); + goto resume_from_trap; } recurse: - if (primary_tag(nodeA) == TAG_PRIMARY_BOXED && - primary_tag(nodeB) == TAG_PRIMARY_LIST) { + if (primary_tag(ctx->nodeA) == TAG_PRIMARY_BOXED && + primary_tag(ctx->nodeB) == TAG_PRIMARY_LIST) { /* Avoid implementing this combination by switching places */ - Eterm tmp = nodeA; - nodeA = nodeB; - nodeB = tmp; - keepA = !keepA; + Eterm tmp = ctx->nodeA; + ctx->nodeA = ctx->nodeB; + ctx->nodeB = tmp; + sp->keepA = !sp->keepA; } - switch (primary_tag(nodeA)) { + switch (primary_tag(ctx->nodeA)) { case TAG_PRIMARY_LIST: { - sp->srcA = list_val(nodeA); - switch (primary_tag(nodeB)) { + Eterm keyA = CAR(list_val(ctx->nodeA)); + switch (primary_tag(ctx->nodeB)) { case TAG_PRIMARY_LIST: { /* LEAF + LEAF */ - sp->srcB = list_val(nodeB); + Eterm keyB = CAR(list_val(ctx->nodeB)); - if (EQ(CAR(sp->srcA), CAR(sp->srcB))) { - --size; - res = keepA ? nodeA : nodeB; + if (EQ(keyA, keyB)) { + --ctx->size; + ctx->res = sp->keepA ? ctx->nodeA : ctx->nodeB; } else { - ahx = hashmap_restore_hash(th, lvl, CAR(sp->srcA)); - bhx = hashmap_restore_hash(th, lvl, CAR(sp->srcB)); + ahx = hashmap_restore_hash(th, ctx->lvl, keyA); + bhx = hashmap_restore_hash(th, ctx->lvl, keyB); sp->abm = 1 << hashmap_index(ahx); sp->bbm = 1 << hashmap_index(bhx); - sp->srcA = &nodeA; - sp->srcB = &nodeB; + sp->srcA = &ctx->nodeA; + sp->srcB = &ctx->nodeB; } break; } case TAG_PRIMARY_BOXED: { /* LEAF + NODE */ - sp->srcB = boxed_val(nodeB); + sp->srcB = boxed_val(ctx->nodeB); ASSERT(is_header(*sp->srcB)); hdrB = *sp->srcB++; - ahx = hashmap_restore_hash(th, lvl, CAR(sp->srcA)); + ahx = hashmap_restore_hash(th, ctx->lvl, keyA); sp->abm = 1 << hashmap_index(ahx); - sp->srcA = &nodeA; + sp->srcA = &ctx->nodeA; switch(hdrB & _HEADER_MAP_SUBTAG_MASK) { case HAMT_SUBTAG_HEAD_ARRAY: sp->srcB++; @@ -1202,26 +1269,26 @@ recurse: break; default: - erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK); + erl_exit(ERTS_ABORT_EXIT, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK); break; } break; } default: - erl_exit(1, "bad primary tag %ld\r\n", nodeB); + erl_exit(ERTS_ABORT_EXIT, "bad primary tag %ld\r\n", ctx->nodeB); } break; } case TAG_PRIMARY_BOXED: { /* NODE + NODE */ - sp->srcA = boxed_val(nodeA); + sp->srcA = boxed_val(ctx->nodeA); hdrA = *sp->srcA++; ASSERT(is_header(hdrA)); switch (hdrA & _HEADER_MAP_SUBTAG_MASK) { case HAMT_SUBTAG_HEAD_ARRAY: { sp->srcA++; - ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED); + ASSERT(primary_tag(ctx->nodeB) == TAG_PRIMARY_BOXED); sp->abm = 0xffff; - sp->srcB = boxed_val(nodeB); + sp->srcB = boxed_val(ctx->nodeB); hdrB = *sp->srcB++; ASSERT(is_header(hdrB)); switch (hdrB & _HEADER_MAP_SUBTAG_MASK) { @@ -1240,9 +1307,9 @@ recurse: } case HAMT_SUBTAG_HEAD_BITMAP: sp->srcA++; case HAMT_SUBTAG_NODE_BITMAP: { - ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED); + ASSERT(primary_tag(ctx->nodeB) == TAG_PRIMARY_BOXED); sp->abm = MAP_HEADER_VAL(hdrA); - sp->srcB = boxed_val(nodeB); + sp->srcB = boxed_val(ctx->nodeB); hdrB = *sp->srcB++; ASSERT(is_header(hdrB)); switch (hdrB & _HEADER_MAP_SUBTAG_MASK) { @@ -1256,40 +1323,44 @@ recurse: break; default: - erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK); + erl_exit(ERTS_ABORT_EXIT, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK); } break; } default: - erl_exit(1, "bad primary tag %ld\r\n", nodeA); + erl_exit(ERTS_ABORT_EXIT, "bad primary tag %ld\r\n", ctx->nodeA); } break; } default: - erl_exit(1, "bad primary tag %ld\r\n", nodeA); + erl_exit(ERTS_ABORT_EXIT, "bad primary tag %ld\r\n", ctx->nodeA); } for (;;) { - if (is_value(res)) { /* We have a complete (sub-)tree or leaf */ - if (lvl == 0) + if (is_value(ctx->res)) { /* We have a complete (sub-)tree or leaf */ + if (ctx->lvl == 0) break; /* Pop from stack and continue build parent node */ - lvl--; + ctx->lvl--; sp = PSTACK_POP(s); - sp->array[sp->ix++] = res; - res = THE_NON_VALUE; + sp->array[sp->ix++] = ctx->res; + ctx->res = THE_NON_VALUE; if (sp->rbm) { sp->srcA++; sp->srcB++; - keepA = sp->keepA; } } else { /* Start build a node */ sp->ix = 0; sp->rbm = sp->abm | sp->bbm; - ASSERT(!(sp->rbm == 0 && lvl > 0)); + ASSERT(!(sp->rbm == 0 && ctx->lvl > 0)); } + if (--reds <= 0) { + goto trap; + } +resume_from_trap: + while (sp->rbm) { Uint32 next = sp->rbm & (sp->rbm-1); Uint32 bit = sp->rbm ^ next; @@ -1297,13 +1368,12 @@ recurse: if (sp->abm & bit) { if (sp->bbm & bit) { /* Bit clash. Push and resolve by recursive merge */ - if (sp->rbm) { - sp->keepA = keepA; - } - nodeA = *sp->srcA; - nodeB = *sp->srcB; - lvl++; + int keepA = sp->keepA; + ctx->nodeA = *sp->srcA; + ctx->nodeB= *sp->srcB; + ctx->lvl++; sp = PSTACK_PUSH(s); + sp->keepA = keepA; goto recurse; } else { sp->array[sp->ix++] = *sp->srcA++; @@ -1315,23 +1385,62 @@ recurse: } ASSERT(sp->ix == hashmap_bitcount(sp->abm | sp->bbm)); - if (lvl == 0) { + if (ctx->lvl == 0) { nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(sp->ix), HALLOC_EXTRA); hp = nhp; *hp++ = (sp->ix == 16 ? MAP_HEADER_HAMT_HEAD_ARRAY : MAP_HEADER_HAMT_HEAD_BITMAP(sp->abm | sp->bbm)); - *hp++ = size; + *hp++ = ctx->size; } else { nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(sp->ix), HALLOC_EXTRA); hp = nhp; *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(sp->abm | sp->bbm); } - memcpy(hp, sp->array, sp->ix * sizeof(Eterm)); - res = make_boxed(nhp); + sys_memcpy(hp, sp->array, sp->ix * sizeof(Eterm)); + ctx->res = make_boxed(nhp); + } + + /* Done */ + + if (ctx != &local_ctx) { + ASSERT(ctx->trap_bin != THE_NON_VALUE); + ASSERT(p->flags & F_DISABLE_GC); + erts_set_gc_state(p, 1); + } + else { + ASSERT(ctx->trap_bin == THE_NON_VALUE); + ASSERT(!(p->flags & F_DISABLE_GC)); } PSTACK_DESTROY(s); UnUseTmpHeap(2,p); - return res; + BUMP_REDS(p, (initial_reds - reds) / MAP_MERGE_LOOP_FACTOR); + return ctx->res; + +trap: /* Yield */ + + if (ctx == &local_ctx) { + Binary* ctx_b = erts_create_magic_binary(sizeof(HashmapMergeContext), + hashmap_merge_ctx_destructor); + ctx = ERTS_MAGIC_BIN_DATA(ctx_b); + sys_memcpy(ctx, &local_ctx, sizeof(HashmapMergeContext)); + hp = HAlloc(p, PROC_BIN_SIZE); + ASSERT(ctx->trap_bin == THE_NON_VALUE); + ctx->trap_bin = erts_mk_magic_binary_term(&hp, &MSO(p), ctx_b); + + erts_set_gc_state(p, 0); + } + else { + ASSERT(ctx->trap_bin != THE_NON_VALUE); + ASSERT(p->flags & F_DISABLE_GC); + } + + PSTACK_SAVE(s, &ctx->pstack); + + BUMP_ALL_REDS(p); + ERTS_BIF_PREP_TRAP1(trap_ret, &hashmap_merge_trap_export, + p, ctx->trap_bin); + UnUseTmpHeap(2,p); + return trap_ret; } static int hash_cmp(Uint32 ha, Uint32 hb) diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 07806c823c..14d42599a1 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -817,7 +817,7 @@ do {\ UWord _pbytes = PSTACK_COUNT(s) * sizeof(PSTACK_TYPE);\ (dst)->pstart = erts_alloc(s.alloc_type,\ sizeof(PSTK_DEF_STACK(s)));\ - memcpy((dst)->pstart, s.pstart, _pbytes);\ + sys_memcpy((dst)->pstart, s.pstart, _pbytes);\ (dst)->psp = (dst)->pstart + _pbytes - sizeof(PSTACK_TYPE);\ (dst)->pend = (dst)->pstart + sizeof(PSTK_DEF_STACK(s));\ (dst)->alloc_type = s.alloc_type;\ @@ -838,6 +838,14 @@ do { \ ASSERT(s.psp < s.pend); \ } while (0) +#define PSTACK_DESTROY_SAVED(pstack)\ +do {\ + if ((pstack)->pstart) {\ + erts_free((pstack)->alloc_type, (pstack)->pstart);\ + (pstack)->pstart = NULL;\ + }\ +} while(0) + /* binary.c */ @@ -1126,6 +1134,9 @@ Sint erts_binary_set_loop_limit(Sint limit); /* external.c */ void erts_init_external(void); +/* erl_map.c */ +void erts_init_map(void); + /* erl_unicode.c */ void erts_init_unicode(void); Sint erts_unicode_set_loop_limit(Sint limit); -- cgit v1.2.3 From f7ef9fb1679fcd46c48ec5f8a968f7e053b3d4ed Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 8 Jun 2015 15:50:46 +0200 Subject: erts: Optimize maps:merge to be better at reusing entire hashmap sub-trees. Sub-tree reuse is detected in three cases: 1. The sub-tree top node does not exist at all in the other map. Already implemented before this commit. 2. The exact same sub-tree exist in both maps. Must calculate nr of keys in tree to get total size right. 3. We detect that a sub-tree only contains stuff from one of the maps. There is still one case we don't detect. If A and B leafs have equal keys we could also compare the values. If values are equal, further node reuse could propagate up toward the root (by 'mix'==0). The downside would be potentially expensive value comparisons. --- erts/emulator/beam/erl_map.c | 300 ++++++++++++++++++++++--------------------- 1 file changed, 154 insertions(+), 146 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index c1ad5658d9..3e78731d20 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -86,6 +86,7 @@ static BIF_RETTYPE hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB, int swap_ struct HashmapMergeContext_*); static Export hashmap_merge_trap_export; static BIF_RETTYPE maps_merge_trap_1(BIF_ALIST_1); +static Uint hashmap_subtree_size(Eterm node); static Eterm hashmap_to_list(Process *p, Eterm map); static Eterm hashmap_keys(Process *p, Eterm map); static Eterm hashmap_values(Process *p, Eterm map); @@ -1132,18 +1133,18 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args) #define PSTACK_TYPE struct HashmapMergePStackType struct HashmapMergePStackType { + Eterm nodeA, nodeB; Eterm *srcA, *srcB; - Uint32 abm, bbm, rbm; /* node bitmaps */ - int keepA; + Uint32 abm, bbm, rbm; /* node bitmaps */ + int mix; /* &1: there are unique A stuff in node + * &2: there are unique B stuff in node */ int ix; - Eterm array[16]; + Eterm array[16]; /* temp node construction area */ }; typedef struct HashmapMergeContext_ { Uint size; /* total key-value counter */ unsigned int lvl; - Eterm nodeA, nodeB; - Eterm res; Eterm trap_bin; ErtsPStack pstack; #ifdef DEBUG @@ -1177,7 +1178,8 @@ static BIF_RETTYPE hashmap_merge(Process *p, Eterm map_A, Eterm map_B, PSTACK_DECLARE(s, 4); HashmapMergeContext local_ctx; struct HashmapMergePStackType* sp; - Uint32 ahx, bhx; + Uint32 hx; + Eterm res = THE_NON_VALUE; Eterm hdrA, hdrB; Eterm *hp, *nhp; Eterm trap_ret; @@ -1198,12 +1200,11 @@ static BIF_RETTYPE hashmap_merge(Process *p, Eterm map_A, Eterm map_B, hashmap_head_t* b = (hashmap_head_t*) hashmap_val(map_B); sp = PSTACK_PUSH(s); - sp->keepA = swap_args; + sp->srcA = swap_args ? &map_B : &map_A; + sp->srcB = swap_args ? &map_A : &map_B; + sp->mix = 0; local_ctx.size = a->size + b->size; local_ctx.lvl = 0; - local_ctx.nodeA = map_A; - local_ctx.nodeB = map_B; - local_ctx.res = THE_NON_VALUE; #ifdef DEBUG local_ctx.dbg_map_A = map_A; local_ctx.dbg_map_B = map_B; @@ -1219,133 +1220,97 @@ static BIF_RETTYPE hashmap_merge(Process *p, Eterm map_A, Eterm map_B, recurse: - if (primary_tag(ctx->nodeA) == TAG_PRIMARY_BOXED && - primary_tag(ctx->nodeB) == TAG_PRIMARY_LIST) { - /* Avoid implementing this combination by switching places */ - Eterm tmp = ctx->nodeA; - ctx->nodeA = ctx->nodeB; - ctx->nodeB = tmp; - sp->keepA = !sp->keepA; - } - - switch (primary_tag(ctx->nodeA)) { - case TAG_PRIMARY_LIST: { - Eterm keyA = CAR(list_val(ctx->nodeA)); - switch (primary_tag(ctx->nodeB)) { - case TAG_PRIMARY_LIST: { /* LEAF + LEAF */ - Eterm keyB = CAR(list_val(ctx->nodeB)); - - if (EQ(keyA, keyB)) { - --ctx->size; - ctx->res = sp->keepA ? ctx->nodeA : ctx->nodeB; - } else { - ahx = hashmap_restore_hash(th, ctx->lvl, keyA); - bhx = hashmap_restore_hash(th, ctx->lvl, keyB); - sp->abm = 1 << hashmap_index(ahx); - sp->bbm = 1 << hashmap_index(bhx); + sp->nodeA = *sp->srcA; + sp->nodeB = *sp->srcB; - sp->srcA = &ctx->nodeA; - sp->srcB = &ctx->nodeB; - } - break; - } - case TAG_PRIMARY_BOXED: { /* LEAF + NODE */ - sp->srcB = boxed_val(ctx->nodeB); - ASSERT(is_header(*sp->srcB)); - hdrB = *sp->srcB++; - - ahx = hashmap_restore_hash(th, ctx->lvl, keyA); - sp->abm = 1 << hashmap_index(ahx); - sp->srcA = &ctx->nodeA; - switch(hdrB & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_ARRAY: - sp->srcB++; - sp->bbm = 0xffff; - break; + if (sp->nodeA == sp->nodeB) { + res = sp->nodeA; + ctx->size -= is_list(sp->nodeB) ? 1 : hashmap_subtree_size(sp->nodeB); + } + else { + if (is_list(sp->nodeA)) { /* A is LEAF */ + Eterm keyA = CAR(list_val(sp->nodeA)); + + if (is_list(sp->nodeB)) { /* LEAF + LEAF */ + Eterm keyB = CAR(list_val(sp->nodeB)); + + if (EQ(keyA, keyB)) { + --ctx->size; + res = sp->nodeB; + sp->mix = 2; /* We assume values differ. + + Don't spend time comparing big values. + - Might waste some heap space for internal + nodes that could otherwise be reused. */ + goto merge_nodes; + } + } + hx = hashmap_restore_hash(th, ctx->lvl, keyA); + sp->abm = 1 << hashmap_index(hx); + /* keep srcA pointing at the leaf */ + } + else { /* A is NODE */ + sp->srcA = boxed_val(sp->nodeA); + hdrA = *sp->srcA++; + ASSERT(is_header(hdrA)); + switch (hdrA & _HEADER_MAP_SUBTAG_MASK) { + case HAMT_SUBTAG_HEAD_ARRAY: { + sp->srcA++; + sp->abm = 0xffff; + break; + } + case HAMT_SUBTAG_HEAD_BITMAP: sp->srcA++; + case HAMT_SUBTAG_NODE_BITMAP: { + sp->abm = MAP_HEADER_VAL(hdrA); + break; + } + default: + erl_exit(ERTS_ABORT_EXIT, "bad header %ld\r\n", hdrA); + } + } - case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++; - case HAMT_SUBTAG_NODE_BITMAP: - sp->bbm = MAP_HEADER_VAL(hdrB); - break; + if (is_list(sp->nodeB)) { /* B is LEAF */ + Eterm keyB = CAR(list_val(sp->nodeB)); - default: - erl_exit(ERTS_ABORT_EXIT, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK); - break; - } - break; - } - default: - erl_exit(ERTS_ABORT_EXIT, "bad primary tag %ld\r\n", ctx->nodeB); - } - break; - } - case TAG_PRIMARY_BOXED: { /* NODE + NODE */ - sp->srcA = boxed_val(ctx->nodeA); - hdrA = *sp->srcA++; - ASSERT(is_header(hdrA)); - switch (hdrA & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_ARRAY: { - sp->srcA++; - ASSERT(primary_tag(ctx->nodeB) == TAG_PRIMARY_BOXED); - sp->abm = 0xffff; - sp->srcB = boxed_val(ctx->nodeB); - hdrB = *sp->srcB++; - ASSERT(is_header(hdrB)); - switch (hdrB & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_ARRAY: - sp->srcB++; - sp->bbm = 0xffff; - break; - case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++; - case HAMT_SUBTAG_NODE_BITMAP: - sp->bbm = MAP_HEADER_VAL(hdrB); - break; - default: - erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK); - } - break; - } - case HAMT_SUBTAG_HEAD_BITMAP: sp->srcA++; - case HAMT_SUBTAG_NODE_BITMAP: { - ASSERT(primary_tag(ctx->nodeB) == TAG_PRIMARY_BOXED); - sp->abm = MAP_HEADER_VAL(hdrA); - sp->srcB = boxed_val(ctx->nodeB); - hdrB = *sp->srcB++; - ASSERT(is_header(hdrB)); - switch (hdrB & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_ARRAY: + hx = hashmap_restore_hash(th, ctx->lvl, keyB); + sp->bbm = 1 << hashmap_index(hx); + /* keep srcB pointing at the leaf */ + } + else { /* B is NODE */ + sp->srcB = boxed_val(sp->nodeB); + hdrB = *sp->srcB++; + ASSERT(is_header(hdrB)); + switch (hdrB & _HEADER_MAP_SUBTAG_MASK) { + case HAMT_SUBTAG_HEAD_ARRAY: { sp->srcB++; - sp->bbm = 0xffff; - break; - case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++; - case HAMT_SUBTAG_NODE_BITMAP: - sp->bbm = MAP_HEADER_VAL(hdrB); - break; - - default: - erl_exit(ERTS_ABORT_EXIT, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK); - } - break; - } - default: - erl_exit(ERTS_ABORT_EXIT, "bad primary tag %ld\r\n", ctx->nodeA); - } - break; - } - default: - erl_exit(ERTS_ABORT_EXIT, "bad primary tag %ld\r\n", ctx->nodeA); + sp->bbm = 0xffff; + break; + } + case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++; + case HAMT_SUBTAG_NODE_BITMAP: { + sp->bbm = MAP_HEADER_VAL(hdrB); + break; + } + default: + erl_exit(ERTS_ABORT_EXIT, "bad header %ld\r\n", hdrB); + } + } } +merge_nodes: + for (;;) { - if (is_value(ctx->res)) { /* We have a complete (sub-)tree or leaf */ + if (is_value(res)) { /* We have a complete (sub-)tree or leaf */ + int child_mix; if (ctx->lvl == 0) break; /* Pop from stack and continue build parent node */ ctx->lvl--; + child_mix = sp->mix; sp = PSTACK_POP(s); - sp->array[sp->ix++] = ctx->res; - ctx->res = THE_NON_VALUE; + sp->array[sp->ix++] = res; + sp->mix |= child_mix; + res = THE_NON_VALUE; if (sp->rbm) { sp->srcA++; sp->srcB++; @@ -1368,40 +1333,69 @@ resume_from_trap: if (sp->abm & bit) { if (sp->bbm & bit) { /* Bit clash. Push and resolve by recursive merge */ - int keepA = sp->keepA; - ctx->nodeA = *sp->srcA; - ctx->nodeB= *sp->srcB; + Eterm* srcA = sp->srcA; + Eterm* srcB = sp->srcB; ctx->lvl++; sp = PSTACK_PUSH(s); - sp->keepA = keepA; + sp->srcA = srcA; + sp->srcB = srcB; + sp->mix = 0; goto recurse; } else { sp->array[sp->ix++] = *sp->srcA++; + sp->mix |= 1; } } else { ASSERT(sp->bbm & bit); sp->array[sp->ix++] = *sp->srcB++; + sp->mix |= 2; } } - ASSERT(sp->ix == hashmap_bitcount(sp->abm | sp->bbm)); - if (ctx->lvl == 0) { - nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(sp->ix), HALLOC_EXTRA); - hp = nhp; - *hp++ = (sp->ix == 16 ? MAP_HEADER_HAMT_HEAD_ARRAY - : MAP_HEADER_HAMT_HEAD_BITMAP(sp->abm | sp->bbm)); - *hp++ = ctx->size; - } else { - nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(sp->ix), HALLOC_EXTRA); - hp = nhp; - *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(sp->abm | sp->bbm); - } - sys_memcpy(hp, sp->array, sp->ix * sizeof(Eterm)); - ctx->res = make_boxed(nhp); + switch (sp->mix) { + case 0: /* Nodes A and B contain the *EXACT* same sub-trees + => fall through and reuse nodeA */ + + case 1: /* Only unique A stuff => reuse nodeA */ + res = sp->nodeA; + break; + + case 2: /* Only unique B stuff => reuse nodeB */ + res = sp->nodeB; + break; + + case 3: /* We have a mix => must build new node */ + ASSERT(sp->ix == hashmap_bitcount(sp->abm | sp->bbm)); + if (ctx->lvl == 0) { + nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(sp->ix), HALLOC_EXTRA); + hp = nhp; + *hp++ = (sp->ix == 16 ? MAP_HEADER_HAMT_HEAD_ARRAY + : MAP_HEADER_HAMT_HEAD_BITMAP(sp->abm | sp->bbm)); + *hp++ = ctx->size; + } else { + nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(sp->ix), HALLOC_EXTRA); + hp = nhp; + *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(sp->abm | sp->bbm); + } + sys_memcpy(hp, sp->array, sp->ix * sizeof(Eterm)); + res = make_boxed(nhp); + break; + default: + erl_exit(ERTS_ABORT_EXIT, "strange mix %d\r\n", sp->mix); + } } /* Done */ +#ifdef DEBUG + { + Eterm *head = hashmap_val(res); + Uint size = head[1]; + Uint real_size = hashmap_subtree_size(res); + ASSERT(size == real_size); + } +#endif + if (ctx != &local_ctx) { ASSERT(ctx->trap_bin != THE_NON_VALUE); ASSERT(p->flags & F_DISABLE_GC); @@ -1414,7 +1408,7 @@ resume_from_trap: PSTACK_DESTROY(s); UnUseTmpHeap(2,p); BUMP_REDS(p, (initial_reds - reds) / MAP_MERGE_LOOP_FACTOR); - return ctx->res; + return res; trap: /* Yield */ @@ -1443,6 +1437,19 @@ trap: /* Yield */ return trap_ret; } +static Uint hashmap_subtree_size(Eterm node) { + DECLARE_WSTACK(stack); + Uint size = 0; + + hashmap_iterator_init(&stack, node, 0); + while (hashmap_iterator_next(&stack)) { + size++; + } + DESTROY_WSTACK(stack); + return size; +} + + static int hash_cmp(Uint32 ha, Uint32 hb) { int i; @@ -1865,10 +1872,11 @@ void hashmap_iterator_init(ErtsWStack* s, Eterm node, int reverse) { sz = 16; break; case HAMT_SUBTAG_HEAD_BITMAP: - sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); + case HAMT_SUBTAG_NODE_BITMAP: + sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); break; default: - erl_exit(1, "bad header"); + erl_exit(ERTS_ABORT_EXIT, "bad header"); } WSTACK_PUSH3((*s), (UWord)THE_NON_VALUE, /* end marker */ @@ -1905,7 +1913,7 @@ Eterm* hashmap_iterator_next(ErtsWStack* s) { ASSERT(sz < 17); break; default: - erl_exit(1, "bad header"); + erl_exit(ERTS_ABORT_EXIT, "bad header"); } idx++; -- cgit v1.2.3 From 02d0ce034598297565f2b35ecc3d1af121787f33 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 2 Jun 2015 19:23:49 +0200 Subject: erts: Remove hashmap probabilistic heap overestimation by adding a dynamic heap factory. "binary_to_term" is now a hybrid solution with both a call to decoded_size() to calculate needed heap space AND possible dynamic allocation of more heap space if needed for big maps. The heap size returned from decoded_size() is guaranteed to be sufficient for all term heap data except for hashmap nodes. All hashmap nodes are created at the end of dec_term() by invoking the heap factory interface that may allocate more heap space on process heap or in fragments. With this commit it is no longer guaranteed that a message is confined to only one heap fragment. --- erts/emulator/beam/beam_emu.c | 3 +- erts/emulator/beam/beam_load.c | 184 +++++++++----------- erts/emulator/beam/big.h | 1 + erts/emulator/beam/copy.c | 13 +- erts/emulator/beam/dist.c | 4 +- erts/emulator/beam/erl_bif_info.c | 58 ++++--- erts/emulator/beam/erl_db_hash.c | 1 - erts/emulator/beam/erl_db_util.c | 25 ++- erts/emulator/beam/erl_gc.c | 93 +++++----- erts/emulator/beam/erl_map.c | 27 +-- erts/emulator/beam/erl_map.h | 14 +- erts/emulator/beam/erl_message.c | 307 +++++++++++++++++++++++++++++----- erts/emulator/beam/erl_message.h | 85 ++++++---- erts/emulator/beam/erl_process_lock.c | 2 + erts/emulator/beam/external.c | 291 +++++++++++++++----------------- erts/emulator/beam/external.h | 9 +- erts/emulator/beam/io.c | 247 ++++++++++++++------------- 17 files changed, 807 insertions(+), 557 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index a21622f424..2d4bf4240c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6653,8 +6653,9 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) p->htop = mhp; - factory.p = p; + erts_factory_proc_init(&factory, p); res = erts_hashmap_from_array(&factory, thp, n/2, 0); + erts_factory_close(&factory); if (p->mbuf) { Uint live = Arg(2); reg[live] = res; diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 0d40201934..8a8ad0d7a3 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -205,10 +205,7 @@ typedef struct { typedef struct { Eterm term; /* The tagged term (in the heap). */ - Uint heap_size; /* (Exact) size on the heap. */ - SWord offset; /* Offset from temporary location to final. */ - ErlOffHeap off_heap; /* Start of linked list of ProcBins. */ - Eterm* heap; /* Heap for term. */ + ErlHeapFragment* heap_frags; } Literal; /* @@ -477,6 +474,8 @@ typedef struct LoaderState { static void free_loader_state(Binary* magic); +static ErlHeapFragment* new_literal_fragment(Uint size); +static void free_literal_fragment(ErlHeapFragment*); static void loader_state_dtor(Binary* magic); static Eterm insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, Eterm group_leader, Eterm module, @@ -883,6 +882,28 @@ free_loader_state(Binary* magic) } } +static ErlHeapFragment* new_literal_fragment(Uint size) +{ + ErlHeapFragment* bp; + bp = (ErlHeapFragment*) ERTS_HEAP_ALLOC(ERTS_ALC_T_PREPARED_CODE, + ERTS_HEAP_FRAG_SIZE(size)); + ERTS_INIT_HEAP_FRAG(bp, size); + return bp; +} + +static void free_literal_fragment(ErlHeapFragment* bp) +{ + ASSERT(bp != NULL); + do { + ErlHeapFragment* next_bp = bp->next; + + erts_cleanup_offheap(&bp->off_heap); + ERTS_HEAP_FREE(ERTS_ALC_T_PREPARED_CODE, (void *) bp, + ERTS_HEAP_FRAG_SIZE(bp->size)); + bp = next_bp; + }while (bp != NULL); +} + /* * This destructor function can safely be called multiple times. */ @@ -922,10 +943,9 @@ loader_state_dtor(Binary* magic) if (stp->literals != 0) { int i; for (i = 0; i < stp->num_literals; i++) { - if (stp->literals[i].heap != 0) { - erts_free(ERTS_ALC_T_PREPARED_CODE, - (void *) stp->literals[i].heap); - stp->literals[i].heap = 0; + if (stp->literals[i].heap_frags != 0) { + free_literal_fragment(stp->literals[i].heap_frags); + stp->literals[i].heap_frags = 0; } } erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->literals); @@ -1450,6 +1470,7 @@ read_lambda_table(LoaderState* stp) return 0; } + static int read_literal_table(LoaderState* stp) { @@ -1471,7 +1492,7 @@ read_literal_table(LoaderState* stp) stp->allocated_literals = stp->num_literals; for (i = 0; i < stp->num_literals; i++) { - stp->literals[i].heap = 0; + stp->literals[i].heap_frags = 0; } for (i = 0; i < stp->num_literals; i++) { @@ -1479,28 +1500,38 @@ read_literal_table(LoaderState* stp) Sint heap_size; byte* p; Eterm val; - Eterm* hp; + ErtsHeapFactory factory; GetInt(stp, 4, sz); /* Size of external term format. */ GetString(stp, p, sz); if ((heap_size = erts_decode_ext_size(p, sz)) < 0) { LoadError1(stp, "literal %d: bad external format", i); } - 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; - val = erts_decode_ext(&hp, &stp->literals[i].off_heap, &p); - stp->literals[i].heap_size = hp - stp->literals[i].heap; - if (stp->literals[i].heap_size > heap_size) { - erl_exit(1, "overrun by %d word(s) for literal heap, term %d", - stp->literals[i].heap_size - heap_size, i); - } - if (is_non_value(val)) { - LoadError1(stp, "literal %d: bad external format", i); - } - stp->literals[i].term = val; - stp->total_literal_size += stp->literals[i].heap_size; + + if (heap_size > 0) { + erts_factory_message_init(&factory, NULL, NULL, + new_literal_fragment(heap_size)); + factory.alloc_type = ERTS_ALC_T_PREPARED_CODE; + val = erts_decode_ext(&factory, &p); + + if (is_non_value(val)) { + LoadError1(stp, "literal %d: bad external format", i); + } + erts_factory_close(&factory); + stp->literals[i].heap_frags = factory.heap_frags; + stp->total_literal_size += erts_used_frag_sz(factory.heap_frags); + } + else { + erts_factory_dummy_init(&factory); + val = erts_decode_ext(&factory, &p); + if (is_non_value(val)) { + LoadError1(stp, "literal %d: bad external format", i); + } + ASSERT(is_immed(val)); + stp->literals[i].heap_frags = NULL; + } + stp->literals[i].term = val; + } erts_free(ERTS_ALC_T_TMP, uncompressed); return 1; @@ -4370,8 +4401,9 @@ freeze_code(LoaderState* stp) Uint* low; Uint* high; LiteralPatch* lp; - struct erl_off_heap_header* off_heap = 0; - struct erl_off_heap_header** off_heap_last = &off_heap; + ErlOffHeap code_off_heap; + + ERTS_INIT_OFF_HEAP(&code_off_heap); low = (Uint *) (code+stp->ci); high = low + stp->total_literal_size; @@ -4379,73 +4411,21 @@ freeze_code(LoaderState* stp) code[MI_LITERALS_END] = (BeamInstr) high; ptr = low; for (i = 0; i < stp->num_literals; i++) { - SWord offset; - struct erl_off_heap_header* t_off_heap; - - sys_memcpy(ptr, stp->literals[i].heap, - stp->literals[i].heap_size*sizeof(Eterm)); - offset = ptr - stp->literals[i].heap; - stp->literals[i].offset = offset; - high = ptr + stp->literals[i].heap_size; - while (ptr < high) { - Eterm val = *ptr; - switch (primary_tag(val)) { - case TAG_PRIMARY_LIST: - case TAG_PRIMARY_BOXED: - *ptr++ = offset_ptr(val, offset); - break; - case TAG_PRIMARY_HEADER: - if (header_is_transparent(val)) { - ptr++; - } else { - if (thing_subtag(val) == REFC_BINARY_SUBTAG) { - struct erl_off_heap_header* oh; - - oh = (struct erl_off_heap_header*) ptr; - if (oh->next) { - Eterm** uptr = (Eterm **) (void *) &oh->next; - *uptr += offset; - } - } - ptr += 1 + thing_arityval(val); - } - break; - default: - ptr++; - break; - } - } - ASSERT(ptr == high); - - /* - * Re-link the off_heap list for this term onto the - * off_heap list for the entire module. - */ - t_off_heap = stp->literals[i].off_heap.first; - if (t_off_heap) { - t_off_heap = (struct erl_off_heap_header *) - offset_ptr((UWord) t_off_heap, offset); - while (t_off_heap) { - *off_heap_last = t_off_heap; - off_heap_last = &t_off_heap->next; - t_off_heap = t_off_heap->next; - } - } + if (stp->literals[i].heap_frags) { + move_multi_frags(&ptr, &code_off_heap, stp->literals[i].heap_frags, + &stp->literals[i].term, 1); + } + else ASSERT(is_immed(stp->literals[i].term)); } - code[MI_LITERALS_OFF_HEAP] = (BeamInstr) off_heap; + code[MI_LITERALS_OFF_HEAP] = (BeamInstr) code_off_heap.first; lp = stp->literal_patches; while (lp != 0) { BeamInstr* op_ptr; - Uint literal; Literal* lit; op_ptr = code + lp->pos; lit = &stp->literals[op_ptr[0]]; - literal = lit->term; - if (is_boxed(literal) || is_list(literal)) { - literal = offset_ptr(literal, lit->offset); - } - op_ptr[0] = literal; + op_ptr[0] = lit->term; lp = lp->next; } literal_end += stp->total_literal_size; @@ -5376,13 +5356,9 @@ new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size) stp->total_literal_size += heap_size; lit = stp->literals + stp->num_literals; - lit->offset = 0; - lit->heap_size = heap_size; - 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; - *hpp = lit->heap; + lit->heap_frags = new_literal_fragment(heap_size); + lit->term = make_boxed(lit->heap_frags->mem); + *hpp = lit->heap_frags->mem; return stp->num_literals++; } @@ -5659,10 +5635,8 @@ attributes_for_module(Process* p, /* Process whose heap to use. */ { Module* modp; BeamInstr* code; - Eterm* hp; byte* ext; Eterm result = NIL; - Eterm* end; if (is_not_atom(mod)) { return THE_NON_VALUE; @@ -5675,13 +5649,12 @@ attributes_for_module(Process* p, /* Process whose heap to use. */ code = modp->curr.code; ext = (byte *) code[MI_ATTR_PTR]; if (ext != NULL) { - hp = HAlloc(p, code[MI_ATTR_SIZE_ON_HEAP]); - end = hp + code[MI_ATTR_SIZE_ON_HEAP]; - result = erts_decode_ext(&hp, &MSO(p), &ext); + ErtsHeapFactory factory; + erts_factory_proc_prealloc_init(&factory, p, code[MI_ATTR_SIZE_ON_HEAP]); + result = erts_decode_ext(&factory, &ext); if (is_value(result)) { - ASSERT(hp <= end); + erts_factory_close(&factory); } - HRelease(p,end,hp); } return result; } @@ -5698,10 +5671,8 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */ { Module* modp; BeamInstr* code; - Eterm* hp; byte* ext; Eterm result = NIL; - Eterm* end; if (is_not_atom(mod)) { return THE_NON_VALUE; @@ -5714,13 +5685,12 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */ code = modp->curr.code; ext = (byte *) code[MI_COMPILE_PTR]; if (ext != NULL) { - hp = HAlloc(p, code[MI_COMPILE_SIZE_ON_HEAP]); - end = hp + code[MI_COMPILE_SIZE_ON_HEAP]; - result = erts_decode_ext(&hp, &MSO(p), &ext); + ErtsHeapFactory factory; + erts_factory_proc_prealloc_init(&factory, p, code[MI_COMPILE_SIZE_ON_HEAP]); + result = erts_decode_ext(&factory, &ext); if (is_value(result)) { - ASSERT(hp <= end); + erts_factory_close(&factory); } - HRelease(p,end,hp); } return result; } diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 4e4611de16..5b5550da43 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -85,6 +85,7 @@ typedef Uint dsize_t; /* Vector size type */ /* The heap size needed for a bignum */ #define BIG_NEED_SIZE(x) ((x) + 1) +#define BIG_NEED_FOR_BITS(bits) BIG_NEED_SIZE(((bits)-1)/D_EXP + 1) #define BIG_UINT_HEAP_SIZE (1 + 1) /* always, since sizeof(Uint) <= sizeof(Eterm) */ diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 850606dd86..cddadb346c 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -34,7 +34,7 @@ #include "erl_bits.h" #include "dtrace-wrapper.h" -static void move_one_frag(Eterm** hpp, Eterm* src, Uint src_sz, ErlOffHeap*); +static void move_one_frag(Eterm** hpp, ErlHeapFragment*, ErlOffHeap*); /* * Copy object "obj" to process p. @@ -661,8 +661,7 @@ void move_multi_frags(Eterm** hpp, ErlOffHeap* off_heap, ErlHeapFragment* first, unsigned i; for (bp=first; bp!=NULL; bp=bp->next) { - move_one_frag(hpp, bp->mem, bp->used_size, off_heap); - OH_OVERHEAD(off_heap, bp->off_heap.overhead); + move_one_frag(hpp, bp, off_heap); } hp_end = *hpp; for (hp=hp_start; hpmem; + Eterm* end = ptr + frag->used_size; Eterm dummy_ref; Eterm* hp = *hpp; @@ -732,5 +731,7 @@ move_one_frag(Eterm** hpp, Eterm* src, Uint src_sz, ErlOffHeap* off_heap) } } *hpp = hp; + OH_OVERHEAD(off_heap, frag->off_heap.overhead); + frag->off_heap.first = NULL; } diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 142fcb3c00..ae85659522 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -1149,6 +1149,7 @@ int erts_net_message(Port *prt, DeclareTmpHeapNoproc(ctl_default,DIST_CTL_DEFAULT_SIZE); Eterm* ctl = ctl_default; ErlOffHeap off_heap; + ErtsHeapFactory factory; Eterm* hp; Sint type; Eterm token; @@ -1225,7 +1226,8 @@ int erts_net_message(Port *prt, } hp = ctl; - arg = erts_decode_dist_ext(&hp, &off_heap, &ede); + erts_factory_static_init(&factory, ctl, ctl_len, &off_heap); + arg = erts_decode_dist_ext(&factory, &ede); if (is_non_value(arg)) { #ifdef ERTS_DIST_MSG_DBG erts_fprintf(stderr, "DIST MSG DEBUG: erts_dist_ext_size(CTL) failed:\n"); diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index f74aea80a7..15173bd05e 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1106,19 +1106,18 @@ process_info_aux(Process *BIF_P, heap_need += mq[i].copy_struct_size; } else { - mq[i].copy_struct_size = 0; - if (mp->data.attached) - heap_need += erts_msg_attached_data_size(mp); + mq[i].copy_struct_size = mp->data.attached ? + erts_msg_attached_data_size(mp) : 0; } i++; } - hp = HAlloc(BIF_P, heap_need); - hp_end = hp + heap_need; - ASSERT(i == n); - for (i--; i >= 0; i--) { - Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp); - if (rp != BIF_P) { + if (rp != BIF_P) { + hp = HAlloc(BIF_P, heap_need); + hp_end = hp + heap_need; + ASSERT(i == n); + for (i--; i >= 0; i--) { + Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp); if (is_value(msg)) { if (mq[i].copy_struct_size) msg = copy_struct(msg, @@ -1152,9 +1151,9 @@ process_info_aux(Process *BIF_P, } else { /* Make our copy of the message */ - ASSERT(size_object(msg) == hfp->used_size); + ASSERT(size_object(msg) == erts_used_frag_sz(hfp)); msg = copy_struct(msg, - hfp->used_size, + erts_used_frag_sz(hfp), &hp, &MSO(BIF_P)); } @@ -1164,27 +1163,38 @@ process_info_aux(Process *BIF_P, remove_bad_messages = 1; continue; } + res = CONS(hp, msg, res); + hp += 2; } - else { + HRelease(BIF_P, hp_end, hp+3); + } + else { + for (i--; i >= 0; i--) { + ErtsHeapFactory factory; + Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp); + + erts_factory_proc_prealloc_init(&factory, BIF_P, + mq[i].copy_struct_size+2); if (mq[i].msgp->data.attached) { /* Decode it on the heap */ - erts_move_msg_attached_data_to_heap(&hp, - &MSO(BIF_P), + erts_move_msg_attached_data_to_heap(&factory, mq[i].msgp); msg = ERL_MESSAGE_TERM(mq[i].msgp); ASSERT(!mq[i].msgp->data.attached); - if (is_non_value(msg)) { - /* Bad distribution message; ignore */ - remove_bad_messages = 1; - continue; - } - } + } + if (is_value(msg)) { + hp = erts_produce_heap(&factory, 2, 0); + res = CONS(hp, msg, res); + } + else { + /* Bad distribution message; ignore */ + remove_bad_messages = 1; + continue; + } + erts_factory_close(&factory); } - - res = CONS(hp, msg, res); - hp += 2; + hp = HAlloc(BIF_P, 3); } - HRelease(BIF_P, hp_end, hp+3); erts_free(ERTS_ALC_T_TMP, mq); if (remove_bad_messages) { ErlMessage **mpp; diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 383ee7c430..6e50e9c5b4 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -1049,7 +1049,6 @@ static int db_get_element_hash(Process *p, DbTable *tbl, Eterm copy = db_copy_element_from_ets(&tb->common, p, &b->dbterm, ndex, &hp, 2); elem_list = CONS(hp, copy, elem_list); - hp += 2; } b = b->next; } diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index c6c3c55a7e..d47ff03a30 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2175,11 +2175,12 @@ restart: { ErtsHeapFactory factory; Uint ix; - factory.p = build_proc; for (ix = 0; ix < 2*n; ix++){ ehp[ix] = esp[ix]; } + erts_factory_proc_init(&factory, build_proc); t = erts_hashmap_from_array(&factory, ehp, n, 0); + erts_factory_close(&factory); } *esp++ = t; break; @@ -3192,6 +3193,7 @@ Eterm db_copy_from_comp(DbTableCommon* tb, DbTerm* bp, Eterm** hpp, { Eterm* hp = *hpp; int i, arity = arityval(bp->tpl[0]); + ErtsHeapFactory factory; hp[0] = bp->tpl[0]; *hpp += arity + 1; @@ -3199,17 +3201,23 @@ Eterm db_copy_from_comp(DbTableCommon* tb, DbTerm* bp, Eterm** hpp, hp[tb->keypos] = copy_struct_rel(bp->tpl[tb->keypos], size_object_rel(bp->tpl[tb->keypos], bp->tpl), hpp, off_heap, bp->tpl, NULL); + + erts_factory_static_init(&factory, *hpp, bp->size - (arity+1), off_heap); + for (i=arity; i>0; i--) { if (i != tb->keypos) { if (is_immed(bp->tpl[i])) { hp[i] = bp->tpl[i]; } else { - hp[i] = erts_decode_ext_ets(hpp, off_heap, + hp[i] = erts_decode_ext_ets(&factory, elem2ext(bp->tpl, i)); } } } + *hpp = factory.hp; + erts_factory_close(&factory); + ASSERT((*hpp - hp) <= bp->size); #ifdef DEBUG_CLONE ASSERT(eq_rel(make_tuple(hp),NULL,make_tuple(bp->debug_clone),bp->debug_clone)); @@ -3228,12 +3236,13 @@ Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p, if (tb->compress && pos != tb->keypos) { byte* ext = elem2ext(obj->tpl, pos); Sint sz = erts_decode_ext_size_ets(ext, db_alloced_size_comp(obj)) + extra; - Eterm* hp = HAlloc(p, sz); - Eterm* endp = hp + sz; - Eterm copy = erts_decode_ext_ets(&hp, &MSO(p), ext); - *hpp = hp; - hp += extra; - HRelease(p, endp, hp); + Eterm copy; + ErtsHeapFactory factory; + + erts_factory_proc_prealloc_init(&factory, p, sz); + copy = erts_decode_ext_ets(&factory, ext); + *hpp = erts_produce_heap(&factory, extra, 0); + erts_factory_close(&factory); #ifdef DEBUG_CLONE ASSERT(eq_rel(copy, NULL, obj->debug_clone[pos], obj->debug_clone)); #endif diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 14e7dde778..a456689b5c 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -108,8 +108,7 @@ static Eterm* sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, char* src, Uint src_size); static Eterm* collect_heap_frags(Process* p, Eterm* heap, Eterm* htop, Eterm* objv, int nobj); -static Uint adjust_after_fullsweep(Process *p, Uint size_before, - int need, Eterm *objv, int nobj); +static void adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj); static void shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj); static void grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj); static void sweep_off_heap(Process *p, int fullsweep); @@ -868,29 +867,37 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) ErlMessage *msgp; Uint size_after; Uint need_after; - Uint stack_size = STACK_SZ_ON_HEAP(p); - Uint fragments = MBUF_SIZE(p) + combined_message_size(p); - Uint size_before = fragments + (HEAP_TOP(p) - HEAP_START(p)); - Uint new_sz = next_heap_size(p, HEAP_SIZE(p) + fragments, 0); + const Uint stack_size = STACK_SZ_ON_HEAP(p); + const Uint size_before = MBUF_SIZE(p) + (HEAP_TOP(p) - HEAP_START(p)); + Uint new_sz = HEAP_SIZE(p) + MBUF_SIZE(p) + combined_message_size(p); + new_sz = next_heap_size(p, new_sz, 0); do_minor(p, new_sz, objv, nobj); - /* + size_after = HEAP_TOP(p) - HEAP_START(p); + *recl += (size_before - size_after); + + /* * Copy newly received message onto the end of the new heap. */ - ErtsGcQuickSanityCheck(p); - for (msgp = p->msg.first; msgp; msgp = msgp->next) { - if (msgp->data.attached) { - erts_move_msg_attached_data_to_heap(&p->htop, &p->off_heap, msgp); - ErtsGcQuickSanityCheck(p); - } - } + ErtsGcQuickSanityCheck(p); + for (msgp = p->msg.first; msgp; msgp = msgp->next) { + if (msgp->data.attached) { + ErtsHeapFactory factory; + erts_factory_proc_prealloc_init(&factory, p, + erts_msg_attached_data_size(msgp)); + erts_move_msg_attached_data_to_heap(&factory, msgp); + erts_factory_close(&factory); + ErtsGcQuickSanityCheck(p); + } + } ErtsGcQuickSanityCheck(p); GEN_GCS(p)++; - size_after = HEAP_TOP(p) - HEAP_START(p); - need_after = size_after + need + stack_size; - *recl += (size_before - size_after); + need_after = ((HEAP_TOP(p) - HEAP_START(p)) + + erts_used_frag_sz(MBUF(p)) + + need + + stack_size); /* * Excessively large heaps should be shrunk, but @@ -925,6 +932,7 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) } ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0)); + ASSERT(MBUF(p) == NULL); return 1; /* We are done. */ } @@ -933,6 +941,7 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) * The heap size turned out to be just right. We are done. */ ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0)); + ASSERT(MBUF(p) == NULL); return 1; } } @@ -1212,7 +1221,9 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) { Rootset rootset; Roots* roots; - Uint size_before; + const Uint size_before = ((HEAP_TOP(p) - HEAP_START(p)) + + (OLD_HTOP(p) - OLD_HEAP(p)) + + MBUF_SIZE(p)); Eterm* n_heap; Eterm* n_htop; char* src = (char *) HEAP_START(p); @@ -1221,25 +1232,15 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) Uint oh_size = (char *) OLD_HTOP(p) - oh; Uint n; Uint new_sz; - Uint fragments = MBUF_SIZE(p) + combined_message_size(p); - - size_before = fragments + (HEAP_TOP(p) - HEAP_START(p)) - + (OLD_HTOP(p) - OLD_HEAP(p)); /* * Do a fullsweep GC. First figure out the size of the heap * to receive all live data. */ - new_sz = HEAP_SIZE(p) + fragments + (OLD_HTOP(p) - OLD_HEAP(p)); - /* - * We used to do - * - * new_sz += STACK_SZ_ON_HEAP(p); - * - * here for no obvious reason. (The stack size is already counted once - * in HEAP_SIZE(p).) - */ + new_sz = (HEAP_SIZE(p) + MBUF_SIZE(p) + + combined_message_size(p) + + (OLD_HTOP(p) - OLD_HEAP(p))); new_sz = next_heap_size(p, new_sz, 0); /* @@ -1432,20 +1433,27 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) ErtsGcQuickSanityCheck(p); + *recl += size_before - (HEAP_TOP(p) - HEAP_START(p)); + { ErlMessage *msgp; + /* * Copy newly received message onto the end of the new heap. */ - for (msgp = p->msg.first; msgp; msgp = msgp->next) { + for (msgp = p->msg.first; msgp; msgp = msgp->next) { if (msgp->data.attached) { - erts_move_msg_attached_data_to_heap(&p->htop, &p->off_heap, msgp); + ErtsHeapFactory factory; + erts_factory_proc_prealloc_init(&factory, p, + erts_msg_attached_data_size(msgp)); + erts_move_msg_attached_data_to_heap(&factory, msgp); + erts_factory_close(&factory); ErtsGcQuickSanityCheck(p); } } } - *recl += adjust_after_fullsweep(p, size_before, need, objv, nobj); + adjust_after_fullsweep(p, need, objv, nobj); #ifdef HARDDEBUG disallow_heap_frag_ref_in_heap(p); @@ -1456,21 +1464,17 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) return 1; /* We are done. */ } -static Uint -adjust_after_fullsweep(Process *p, Uint size_before, int need, Eterm *objv, int nobj) +static void +adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj) { - Uint wanted, sz, size_after, need_after; + Uint wanted, sz, need_after; Uint stack_size = STACK_SZ_ON_HEAP(p); - Uint reclaimed_now; - - size_after = (HEAP_TOP(p) - HEAP_START(p)); - reclaimed_now = (size_before - size_after); /* * Resize the heap if needed. */ - need_after = size_after + need + stack_size; + need_after = (HEAP_TOP(p) - HEAP_START(p)) + need + stack_size; if (HEAP_SIZE(p) < need_after) { /* Too small - grow to match requested need */ sz = next_heap_size(p, need_after, 0); @@ -1493,8 +1497,6 @@ adjust_after_fullsweep(Process *p, Uint size_before, int need, Eterm *objv, int shrink_new_heap(p, sz, objv, nobj); } } - - return reclaimed_now; } /* @@ -1942,7 +1944,8 @@ collect_heap_frags(Process* p, Eterm* n_hstart, Eterm* n_htop, * until next GC. */ qb = MBUF(p); - while (qb != NULL) { + while (qb != NULL) { + ASSERT(!qb->off_heap.first); /* process fragments use the MSO(p) list */ frag_size = qb->used_size * sizeof(Eterm); if (frag_size != 0) { frag_begin = (char *) qb->mem; diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index a1bd39dbc8..09fc9a2ad6 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -410,8 +410,9 @@ static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) { } UnUseTmpHeap(2,p); - factory.p = p; + erts_factory_proc_init(&factory, p); res = hashmap_from_unsorted_array(&factory, hxns, size, 0); + erts_factory_close(&factory); erts_free(ERTS_ALC_T_TMP, (void *) hxns); ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); @@ -515,8 +516,9 @@ Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n hxns[i].i = i; } - factory.p = p; + erts_factory_proc_init(&factory, p); res = hashmap_from_unsorted_array(&factory, hxns, sz, 0); + erts_factory_close(&factory); erts_free(ERTS_ALC_T_TMP, (void *) hxns); ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); @@ -1063,8 +1065,9 @@ static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { hxns[i].i = i; } - factory.p = p; + erts_factory_proc_init(&factory, p); res = hashmap_from_unsorted_array(&factory, hxns, n, 0); + erts_factory_close(&factory); erts_free(ERTS_ALC_T_TMP, (void *) hxns); ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); @@ -1107,8 +1110,9 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args) hxns[i].i = i; } - factory.p = p; + erts_factory_proc_init(&factory, p); res = hashmap_from_unsorted_array(&factory, hxns, n, 0); + erts_factory_close(&factory); erts_free(ERTS_ALC_T_TMP, (void *) hxns); ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); @@ -2504,6 +2508,9 @@ int erts_validate_and_sort_flatmap(flatmap_t* mp) return 1; } +#if 0 /* Can't get myself to remove this beautiful piece of code + for probabilistic overestimation of nr of nodes in a hashmap */ + /* Really rough estimate of sqrt(x) * Guaranteed not to be less than sqrt(x) */ @@ -2525,7 +2532,10 @@ static int int_sqrt_ceiling(Uint x) } } -Uint hashmap_over_estimated_heap_size(Uint k) +/* May not be enough if hashing is broken (not uniform) + * or if hell freezes over. + */ +Uint hashmap_overestimated_node_count(Uint k) { /* k is nr of key-value pairs. N(k) is expected nr of nodes in hamt. @@ -2539,12 +2549,9 @@ Uint hashmap_over_estimated_heap_size(Uint k) by 15 std.devs above the average, which gives a probability for overrun less than 1.0e-49 (same magnitude as a git SHA1 collision). */ - Uint max_nodes = 2*k/5 + (15/3)*int_sqrt_ceiling(k); - return (k*2 + /* leaf cons cells */ - k + /* leaf list terms */ - max_nodes*2); /* headers + parent boxed terms */ + return 2*k/5 + 1 + (15/3)*int_sqrt_ceiling(k); } - +#endif BIF_RETTYPE erts_debug_map_info_1(BIF_ALIST_1) { if (is_hashmap(BIF_ARG_1)) { diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 2cc6768bfc..b5941c5c9a 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -91,7 +91,6 @@ Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, Uint *upsz, struct ErtsEStack_ *sp); int erts_validate_and_sort_flatmap(flatmap_t* map); -Uint hashmap_over_estimated_heap_size(Uint n); void hashmap_iterator_init(struct ErtsWStack_* s, Eterm node, int reverse); Eterm* hashmap_iterator_next(struct ErtsWStack_* s); Eterm* hashmap_iterator_prev(struct ErtsWStack_* s); @@ -191,5 +190,18 @@ typedef struct hashmap_head_s { #define hashmap_index(hash) (((Uint32)hash) & 0xf) +/* hashmap heap size: + [one cons cell + one list term in parent node] per key + [one header + one boxed term in parent node] per inner node + [one header + one size word] for root node +*/ +#define HASHMAP_HEAP_SIZE(KEYS,NODES) ((KEYS)*3 + (NODES)*2) +#ifdef DEBUG +# define HASHMAP_ESTIMATED_NODE_COUNT(KEYS) (KEYS) +#else +# define HASHMAP_ESTIMATED_NODE_COUNT(KEYS) (2*(KEYS)/5) +#endif +#define HASHMAP_ESTIMATED_HEAP_SIZE(KEYS) \ + HASHMAP_HEAP_SIZE(KEYS,HASHMAP_ESTIMATED_NODE_COUNT(KEYS)) #endif diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index ccfc2e6458..13e084ce10 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -237,8 +237,7 @@ erts_msg_distext2heap(Process *pp, Eterm msg; Uint tok_sz = 0; Eterm *hp = NULL; - Eterm *hp_end = NULL; - ErlOffHeap *ohp; + ErtsHeapFactory factory; Sint sz; *bpp = NULL; @@ -250,36 +249,26 @@ erts_msg_distext2heap(Process *pp, tok_sz = heap_frag->used_size; sz += tok_sz; } - if (pp) + if (pp) { + ErlOffHeap *ohp; hp = erts_alloc_message_heap(sz, bpp, &ohp, pp, plcksp); + } else { *bpp = new_message_buffer(sz); hp = (*bpp)->mem; - ohp = &(*bpp)->off_heap; } - hp_end = hp + sz; - msg = erts_decode_dist_ext(&hp, ohp, dist_extp); + erts_factory_message_init(&factory, pp, hp, *bpp); + msg = erts_decode_dist_ext(&factory, dist_extp); if (is_non_value(msg)) goto decode_error; if (is_not_nil(*tokenp)) { ErlHeapFragment *heap_frag = erts_dist_ext_trailer(dist_extp); - *tokenp = copy_struct(*tokenp, tok_sz, &hp, ohp); + hp = erts_produce_heap(&factory, tok_sz, 0); + *tokenp = copy_struct(*tokenp, tok_sz, &hp, factory.off_heap); erts_cleanup_offheap(&heap_frag->off_heap); } erts_free_dist_ext_copy(dist_extp); - if (hp_end != hp) { - if (!(*bpp)) { - HRelease(pp, hp_end, hp); - } - else { - Uint final_size = hp - &(*bpp)->mem[0]; - Eterm brefs[2] = {msg, *tokenp}; - ASSERT(sz - (hp_end - hp) == final_size); - *bpp = erts_resize_message_buffer(*bpp, final_size, &brefs[0], 2); - msg = brefs[0]; - *tokenp = brefs[1]; - } - } + erts_factory_close(&factory); return msg; decode_error: @@ -288,13 +277,7 @@ erts_msg_distext2heap(Process *pp, erts_cleanup_offheap(&heap_frag->off_heap); } erts_free_dist_ext_copy(dist_extp); - if (*bpp) { - free_message_buffer(*bpp); - *bpp = NULL; - } - else if (hp) { - HRelease(pp, hp_end, hp); - } + *bpp = NULL; return THE_NON_VALUE; } @@ -851,10 +834,11 @@ erts_msg_attached_data_size_aux(ErlMessage *msg) } void -erts_move_msg_attached_data_to_heap(Eterm **hpp, ErlOffHeap *ohp, ErlMessage *msg) +erts_move_msg_attached_data_to_heap(ErtsHeapFactory* factory, + ErlMessage *msg) { if (is_value(ERL_MESSAGE_TERM(msg))) - erts_move_msg_mbuf_to_heap(hpp, ohp, msg); + erts_move_msg_mbuf_to_heap(&factory->hp, factory->off_heap, msg); else if (msg->data.dist_ext) { ASSERT(msg->data.dist_ext->heap_size >= 0); if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) { @@ -862,12 +846,11 @@ erts_move_msg_attached_data_to_heap(Eterm **hpp, ErlOffHeap *ohp, ErlMessage *ms heap_frag = erts_dist_ext_trailer(msg->data.dist_ext); ERL_MESSAGE_TOKEN(msg) = copy_struct(ERL_MESSAGE_TOKEN(msg), heap_frag->used_size, - hpp, - ohp); + &factory->hp, + factory->off_heap); erts_cleanup_offheap(&heap_frag->off_heap); } - ERL_MESSAGE_TERM(msg) = erts_decode_dist_ext(hpp, - ohp, + ERL_MESSAGE_TERM(msg) = erts_decode_dist_ext(factory, msg->data.dist_ext); erts_free_dist_ext_copy(msg->data.dist_ext); msg->data.dist_ext = NULL; @@ -1134,15 +1117,263 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, } } +void erts_factory_proc_init(ErtsHeapFactory* factory, + Process* p) +{ + erts_factory_proc_prealloc_init(factory, p, HEAP_LIMIT(p) - HEAP_TOP(p)); +} + +void erts_factory_proc_prealloc_init(ErtsHeapFactory* factory, + Process* p, + Sint size) +{ + factory->mode = FACTORY_HALLOC; + factory->p = p; + factory->hp_start = HAlloc(p, size); + factory->hp = factory->hp_start; + factory->hp_end = factory->hp_start + size; + factory->off_heap = &p->off_heap; + factory->off_heap_saved.first = p->off_heap.first; + factory->off_heap_saved.overhead = p->off_heap.overhead; + factory->heap_frags_saved = p->mbuf; + factory->heap_frags = NULL; /* not used */ + factory->alloc_type = 0; /* not used */ +} + +void erts_factory_message_init(ErtsHeapFactory* factory, + Process* rp, + Eterm* hp, + ErlHeapFragment* bp) +{ + if (bp) { + factory->mode = FACTORY_HEAP_FRAGS; + factory->p = NULL; + factory->hp_start = bp->mem; + factory->hp = hp ? hp : bp->mem; + factory->hp_end = bp->mem + bp->alloc_size; + factory->off_heap = &bp->off_heap; + factory->heap_frags = bp; + factory->heap_frags_saved = bp; + factory->alloc_type = ERTS_ALC_T_HEAP_FRAG; + ASSERT(!bp->next); + } + else { + factory->mode = FACTORY_HALLOC; + factory->p = rp; + factory->hp_start = hp; + factory->hp = hp; + factory->hp_end = HEAP_TOP(rp); + factory->off_heap = &rp->off_heap; + factory->heap_frags_saved = rp->mbuf; + factory->heap_frags = NULL; /* not used */ + factory->alloc_type = 0; /* not used */ + } + factory->off_heap_saved.first = factory->off_heap->first; + factory->off_heap_saved.overhead = factory->off_heap->overhead; + + ASSERT(factory->hp >= factory->hp_start && factory->hp <= factory->hp_end); +} + +void erts_factory_static_init(ErtsHeapFactory* factory, + Eterm* hp, + Uint size, + ErlOffHeap* off_heap) +{ + factory->mode = FACTORY_STATIC; + factory->hp_start = hp; + factory->hp = hp; + factory->hp_end = hp + size; + factory->off_heap = off_heap; + factory->off_heap_saved.first = factory->off_heap->first; + factory->off_heap_saved.overhead = factory->off_heap->overhead; +} + +/* When we know the term is an immediate and need no heap. +*/ +void erts_factory_dummy_init(ErtsHeapFactory* factory) +{ + factory->mode = FACTORY_CLOSED; +} + +static void reserve_heap(ErtsHeapFactory*, Uint need, Uint xtra); + Eterm* erts_produce_heap(ErtsHeapFactory* factory, Uint need, Uint xtra) { Eterm* res; - if (factory->p) { - res = HAllocX(factory->p, need, xtra); - } else { - res = factory->hp; - factory->hp += need; + + ASSERT((unsigned int)factory->mode > (unsigned int)FACTORY_CLOSED); + if (factory->hp + need > factory->hp_end) { + reserve_heap(factory, need, xtra); } + res = factory->hp; + factory->hp += need; return res; } +Eterm* erts_reserve_heap(ErtsHeapFactory* factory, Uint need) +{ + ASSERT((unsigned int)factory->mode > (unsigned int)FACTORY_CLOSED); + if (factory->hp + need > factory->hp_end) { + reserve_heap(factory, need, 200); + } + return factory->hp; +} + +static void reserve_heap(ErtsHeapFactory* factory, Uint need, Uint xtra) +{ + ErlHeapFragment* bp; + + switch (factory->mode) { + case FACTORY_HALLOC: + HRelease(factory->p, factory->hp_end, factory->hp); + factory->hp = HAllocX(factory->p, need, xtra); + factory->hp_end = factory->hp + need; + return; + + case FACTORY_HEAP_FRAGS: + bp = factory->heap_frags; + + if (bp) { + ASSERT(factory->hp > bp->mem); + ASSERT(factory->hp <= factory->hp_end); + ASSERT(factory->hp_end == bp->mem + bp->alloc_size); + + bp->used_size = factory->hp - bp->mem; + } + bp = (ErlHeapFragment*) ERTS_HEAP_ALLOC(factory->alloc_type, + ERTS_HEAP_FRAG_SIZE(need+xtra)); + bp->next = factory->heap_frags; + factory->heap_frags = bp; + bp->alloc_size = need + xtra; + bp->used_size = need; + bp->off_heap.first = NULL; + bp->off_heap.overhead = 0; + + factory->hp = bp->mem; + factory->hp_end = bp->mem + bp->alloc_size; + return; + + case FACTORY_STATIC: + case FACTORY_CLOSED: + default: + ASSERT(!"Invalid factory mode"); + } +} + +void erts_factory_close(ErtsHeapFactory* factory) +{ + ErlHeapFragment* bp; + + switch (factory->mode) { + case FACTORY_HALLOC: + HRelease(factory->p, factory->hp_end, factory->hp); + break; + + case FACTORY_HEAP_FRAGS: + bp = factory->heap_frags; + + if (bp) { + ASSERT(factory->hp >= bp->mem); + ASSERT(factory->hp <= factory->hp_end); + ASSERT(factory->hp_end == bp->mem + bp->alloc_size); + + bp->used_size = factory->hp - bp->mem; + } + break; + case FACTORY_STATIC: break; + case FACTORY_CLOSED: break; + default: + ASSERT(!"Invalid factory mode"); + } + factory->mode = FACTORY_CLOSED; +} + +void erts_factory_undo(ErtsHeapFactory* factory) +{ + ErlHeapFragment* bp; + struct erl_off_heap_header *hdr, **hdr_nextp; + + switch (factory->mode) { + case FACTORY_HALLOC: + case FACTORY_STATIC: + /* Cleanup off-heap + */ + hdr_nextp = NULL; + for (hdr = factory->off_heap->first; + hdr != factory->off_heap_saved.first; + hdr = hdr->next) { + + hdr_nextp = &hdr->next; + } + + if (hdr_nextp != NULL) { + *hdr_nextp = NULL; + erts_cleanup_offheap(factory->off_heap); + factory->off_heap->first = factory->off_heap_saved.first; + factory->off_heap->overhead = factory->off_heap_saved.overhead; + } + + if (factory->mode == FACTORY_HALLOC) { + /* Free heap frags + */ + bp = factory->p->mbuf; + if (bp != factory->heap_frags_saved) { + do { + ErlHeapFragment *next_bp = bp->next; + ASSERT(bp->off_heap.first == NULL); + ERTS_HEAP_FREE(ERTS_ALC_T_HEAP_FRAG, (void *) bp, + ERTS_HEAP_FRAG_SIZE(bp->alloc_size)); + bp = next_bp; + } while (bp != factory->heap_frags_saved); + + factory->p->mbuf = bp; + } + + /* Rollback heap top + */ + if (factory->heap_frags_saved == NULL) { /* No heap frags when we started */ + ASSERT(factory->hp_start >= HEAP_START(factory->p)); + ASSERT(factory->hp_start <= HEAP_LIMIT(factory->p)); + + HEAP_TOP(factory->p) = factory->hp_start; + } + else { + ASSERT(factory->heap_frags_saved == factory->p->mbuf); + if (factory->hp_start == factory->heap_frags_saved->mem) { + factory->p->mbuf = factory->p->mbuf->next; + ERTS_HEAP_FREE(ERTS_ALC_T_HEAP_FRAG, factory->heap_frags_saved, + ERTS_HEAP_FRAG_SIZE(factory->heap_frags_saved->alloc_size)); + } + else if (factory->hp_start != factory->hp_end) { + unsigned remains = factory->hp_start - factory->heap_frags_saved->mem; + ASSERT(remains > 0 && remains < factory->heap_frags_saved->used_size); + factory->heap_frags_saved->used_size = remains; + } + } + } + break; + + case FACTORY_HEAP_FRAGS: + bp = factory->heap_frags; + do { + ErlHeapFragment* next_bp = bp->next; + + erts_cleanup_offheap(&bp->off_heap); + ERTS_HEAP_FREE(factory->alloc_type, (void *) bp, + ERTS_HEAP_FRAG_SIZE(bp->size)); + bp = next_bp; + }while (bp != NULL); + break; + + case FACTORY_CLOSED: break; + default: + ASSERT(!"Invalid factory mode"); + } + factory->mode = FACTORY_CLOSED; +#ifdef DEBUG + factory->p = NULL; + factory->hp = NULL; + factory->heap_frags = NULL; +#endif +} + diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 1e1dafee90..39946f2a14 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -51,6 +51,44 @@ typedef struct erl_off_heap { (OHP)->first = NULL; \ (OHP)->overhead = 0; \ } while (0) + +typedef struct { + enum { + FACTORY_CLOSED = 0, + FACTORY_HALLOC, + FACTORY_HEAP_FRAGS, + FACTORY_STATIC + } mode; + Process* p; + Eterm* hp_start; + Eterm* hp; + Eterm* hp_end; + struct erl_heap_fragment* heap_frags; + struct erl_heap_fragment* heap_frags_saved; + ErlOffHeap* off_heap; + ErlOffHeap off_heap_saved; + Uint32 alloc_type; +} ErtsHeapFactory; + +void erts_factory_proc_init(ErtsHeapFactory*, Process*); +void erts_factory_proc_prealloc_init(ErtsHeapFactory*, Process*, Sint size); +void erts_factory_message_init(ErtsHeapFactory*, Process*, Eterm* hp, struct erl_heap_fragment*); +void erts_factory_static_init(ErtsHeapFactory*, Eterm* hp, Uint size, ErlOffHeap*); +void erts_factory_dummy_init(ErtsHeapFactory*); + +Eterm* erts_produce_heap(ErtsHeapFactory*, Uint need, Uint xtra); +Eterm* erts_reserve_heap(ErtsHeapFactory*, Uint need); +void erts_factory_close(ErtsHeapFactory*); +void erts_factory_undo(ErtsHeapFactory*); + +#ifdef CHECK_FOR_HOLES +# define ERTS_FACTORY_HOLE_CHECK(f) do { \ + /*if ((f)->p) erts_check_for_holes((f)->p);*/ \ + } while (0) +#else +# define ERTS_FACTORY_HOLE_CHECK(p) +#endif + #include "external.h" #include "erl_process.h" @@ -68,21 +106,6 @@ struct erl_heap_fragment { Eterm mem[1]; /* Data */ }; -typedef struct { - Process* p; - Eterm* hp; -} ErtsHeapFactory; - -Eterm* erts_produce_heap(ErtsHeapFactory*, Uint need, Uint xtra); -#ifdef CHECK_FOR_HOLES -# define ERTS_FACTORY_HOLE_CHECK(f) do { \ - if ((f)->p) erts_check_for_holes((f)->p); \ - } while (0) -#else -# define ERTS_FACTORY_HOLE_CHECK(p) -#endif - - typedef struct erl_mesg { struct erl_mesg* next; /* Next message */ union { @@ -139,7 +162,7 @@ typedef struct { *(p)->msg.last = (mp); \ (p)->msg.last = &(mp)->next; \ (p)->msg.len++; \ -} while(0) +} while (0) #ifdef ERTS_SMP @@ -212,17 +235,23 @@ do { \ do { \ if ((M)->data.attached) { \ Uint need__ = erts_msg_attached_data_size((M)); \ + { SWPO ; } \ if ((ST) - (HT) >= need__) { \ - Uint *htop__ = (HT); \ - erts_move_msg_attached_data_to_heap(&htop__, &MSO((P)), (M));\ - ASSERT(htop__ - (HT) <= need__); \ - (HT) = htop__; \ + ErtsHeapFactory factory__; \ + erts_factory_proc_prealloc_init(&factory__, (P), need__); \ + erts_move_msg_attached_data_to_heap(&factory__, (M)); \ + erts_factory_close(&factory__); \ + if ((P)->mbuf != NULL) { \ + /* Heap was exhausted by messages. This is a rare case */ \ + /* that can currently (OTP 18) only happen if hamts are */ \ + /* far exceeding the estimated heap size. Do GC. */ \ + (FC) -= erts_garbage_collect((P), 0, NULL, 0); \ + } \ } \ else { \ - { SWPO ; } \ (FC) -= erts_garbage_collect((P), 0, NULL, 0); \ - { SWPI ; } \ } \ + { SWPI ; } \ ASSERT(!(M)->data.attached); \ } \ } while (0) @@ -266,23 +295,21 @@ void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp); void erts_move_msg_mbuf_to_heap(Eterm**, ErlOffHeap*, ErlMessage *); Uint erts_msg_attached_data_size_aux(ErlMessage *msg); -void erts_move_msg_attached_data_to_heap(Eterm **, ErlOffHeap *, ErlMessage *); - +void erts_move_msg_attached_data_to_heap(ErtsHeapFactory*, ErlMessage *); Eterm erts_msg_distext2heap(Process *, ErtsProcLocks *, ErlHeapFragment **, Eterm *, ErtsDistExternal *); void erts_cleanup_offheap(ErlOffHeap *offheap); -ERTS_GLB_INLINE Uint erts_msg_used_frag_sz(const ErlMessage *msg); +ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment*); ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErlMessage *msg); #if ERTS_GLB_INLINE_INCL_FUNC_DEF -ERTS_GLB_INLINE Uint erts_msg_used_frag_sz(const ErlMessage *msg) +ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment* bp) { - const ErlHeapFragment *bp; Uint sz = 0; - for (bp = msg->data.heap_frag; bp!=NULL; bp=bp->next) { + for ( ; bp!=NULL; bp=bp->next) { sz += bp->used_size; } return sz; @@ -292,7 +319,7 @@ ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErlMessage *msg) { ASSERT(msg->data.attached); if (is_value(ERL_MESSAGE_TERM(msg))) - return erts_msg_used_frag_sz(msg); + return erts_used_frag_sz(msg->data.heap_frag); else if (msg->data.dist_ext->heap_size < 0) return erts_msg_attached_data_size_aux(msg); else { diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index fff267ff2a..b28bc498f6 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -1003,7 +1003,9 @@ erts_pid2proc_opt(Process *c_p, void erts_proc_lock_init(Process *p) { +#if ERTS_PROC_LOCK_OWN_IMPL || defined(ERTS_PROC_LOCK_DEBUG) int i; +#endif #if ERTS_PROC_LOCK_OWN_IMPL /* We always start with all locks locked */ #if ERTS_PROC_LOCK_ATOMIC_IMPL diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index fe48298155..0a69172980 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -94,9 +94,9 @@ static Uint is_external_string(Eterm obj, int* p_is_string); static byte* enc_atom(ErtsAtomCacheMap *, Eterm, byte*, Uint32); static byte* enc_pid(ErtsAtomCacheMap *, Eterm, byte*, Uint32); struct B2TContext_t; -static byte* dec_term(ErtsDistExternal *, Eterm**, byte*, ErlOffHeap*, Eterm*, struct B2TContext_t*); +static byte* dec_term(ErtsDistExternal*, ErtsHeapFactory*, byte*, Eterm*, struct B2TContext_t*); static byte* dec_atom(ErtsDistExternal *, byte*, Eterm*); -static byte* dec_pid(ErtsDistExternal *, Eterm**, byte*, ErlOffHeap*, Eterm*); +static byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, byte*, Eterm*); static Sint decoded_size(byte *ep, byte* endp, int internal_tags, struct B2TContext_t*); static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1); @@ -930,8 +930,7 @@ Sint erts_decode_ext_size_ets(byte *ext, Uint size) ** on return hpp is updated to point after allocated data */ Eterm -erts_decode_dist_ext(Eterm** hpp, - ErlOffHeap* off_heap, +erts_decode_dist_ext(ErtsHeapFactory* factory, ErtsDistExternal *edep) { Eterm obj; @@ -951,7 +950,7 @@ erts_decode_dist_ext(Eterm** hpp, goto error; ep++; } - ep = dec_term(edep, hpp, ep, off_heap, &obj, NULL); + ep = dec_term(edep, factory, ep, &obj, NULL); if (!ep) goto error; @@ -960,19 +959,22 @@ erts_decode_dist_ext(Eterm** hpp, return obj; error: + erts_factory_undo(factory); bad_dist_ext(edep); return THE_NON_VALUE; } -Eterm erts_decode_ext(Eterm **hpp, ErlOffHeap *off_heap, byte **ext) +Eterm erts_decode_ext(ErtsHeapFactory* factory, byte **ext) { Eterm obj; byte *ep = *ext; - if (*ep++ != VERSION_MAGIC) + if (*ep++ != VERSION_MAGIC) { + erts_factory_undo(factory); return THE_NON_VALUE; - ep = dec_term(NULL, hpp, ep, off_heap, &obj, NULL); + } + ep = dec_term(NULL, factory, ep, &obj, NULL); if (!ep) { #ifdef DEBUG bin_write(ERTS_PRINT_STDERR,NULL,*ext,500); @@ -983,10 +985,10 @@ Eterm erts_decode_ext(Eterm **hpp, ErlOffHeap *off_heap, byte **ext) return obj; } -Eterm erts_decode_ext_ets(Eterm **hpp, ErlOffHeap *off_heap, byte *ext) +Eterm erts_decode_ext_ets(ErtsHeapFactory* factory, byte *ext) { Eterm obj; - ext = dec_term(NULL, hpp, ext, off_heap, &obj, NULL); + ext = dec_term(NULL, factory, ext, &obj, NULL); ASSERT(ext); return obj; } @@ -995,9 +997,8 @@ Eterm erts_decode_ext_ets(Eterm **hpp, ErlOffHeap *off_heap, byte *ext) BIF_RETTYPE erts_debug_dist_ext_to_term_2(BIF_ALIST_2) { + ErtsHeapFactory factory; Eterm res; - Eterm *hp; - Eterm *hendp; Sint hsz; ErtsDistExternal ede; Eterm *tp; @@ -1044,12 +1045,9 @@ BIF_RETTYPE erts_debug_dist_ext_to_term_2(BIF_ALIST_2) if (hsz < 0) goto badarg; - hp = HAlloc(BIF_P, (Uint) hsz); - hendp = hp + hsz; - - res = erts_decode_dist_ext(&hp, &MSO(BIF_P), &ede); - - HRelease(BIF_P, hendp, hp); + erts_factory_proc_prealloc_init(&factory, BIF_P, hsz); + res = erts_decode_dist_ext(&factory, &ede); + erts_factory_close(&factory); if (is_value(res)) BIF_RET(res); @@ -1177,13 +1175,11 @@ typedef struct { byte* ep; Eterm res; Eterm* next; - Eterm* hp_start; - Eterm* hp; - Eterm* hp_end; + ErtsHeapFactory factory; int remaining_n; char* remaining_bytes; Eterm* maps_list; - struct dec_term_hamt_placeholder* hamt_list; + ErtsPStack hamt_array; } B2TDecodeContext; typedef struct { @@ -1307,10 +1303,12 @@ binary2term_abort(ErtsBinary2TermState *state) } static ERTS_INLINE Eterm -binary2term_create(ErtsDistExternal *edep, ErtsBinary2TermState *state, Eterm **hpp, ErlOffHeap *ohp) +binary2term_create(ErtsDistExternal *edep, ErtsBinary2TermState *state, + ErtsHeapFactory* factory) { Eterm res; - if (!dec_term(edep, hpp, state->extp, ohp, &res, NULL)) + + if (!dec_term(edep, factory, state->extp, &res, NULL)) res = THE_NON_VALUE; if (state->exttmp) { state->exttmp = 0; @@ -1343,9 +1341,9 @@ erts_binary2term_abort(ErtsBinary2TermState *state) } Eterm -erts_binary2term_create(ErtsBinary2TermState *state, Eterm **hpp, ErlOffHeap *ohp) +erts_binary2term_create(ErtsBinary2TermState *state, ErtsHeapFactory* factory) { - return binary2term_create(NULL,state, hpp, ohp); + return binary2term_create(NULL,state, factory); } static void b2t_destroy_context(B2TContext* context) @@ -1354,8 +1352,21 @@ static void b2t_destroy_context(B2TContext* context) ERTS_ALC_T_EXT_TERM_DATA); context->aligned_alloc = NULL; binary2term_abort(&context->b2ts); - if (context->state == B2TUncompressChunk) { + switch (context->state) { + case B2TUncompressChunk: erl_zlib_inflate_finish(&context->u.uc.stream); + break; + case B2TDecode: + case B2TDecodeList: + case B2TDecodeTuple: + case B2TDecodeString: + case B2TDecodeBinary: + if (context->u.dc.hamt_array.pstart) { + erts_free(context->u.dc.hamt_array.alloc_type, + context->u.dc.hamt_array.pstart); + } + break; + default:; } } @@ -1506,11 +1517,9 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar ctx->u.dc.ep = ctx->b2ts.extp; ctx->u.dc.res = (Eterm) (UWord) NULL; ctx->u.dc.next = &ctx->u.dc.res; - ctx->u.dc.hp_start = HAlloc(p, ctx->heap_size); - ctx->u.dc.hp = ctx->u.dc.hp_start; - ctx->u.dc.hp_end = ctx->u.dc.hp_start + ctx->heap_size; + erts_factory_proc_prealloc_init(&ctx->u.dc.factory, p, ctx->heap_size); ctx->u.dc.maps_list = NULL; - ctx->u.dc.hamt_list = NULL; + ctx->u.dc.hamt_array.pstart = NULL; ctx->state = B2TDecode; /*fall through*/ case B2TDecode: @@ -1520,11 +1529,10 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar case B2TDecodeBinary: { ErtsDistExternal fakedep; fakedep.flags = ctx->flags; - dec_term(&fakedep, NULL, NULL, &MSO(p), NULL, ctx); + dec_term(&fakedep, NULL, NULL, NULL, ctx); break; } case B2TDecodeFail: - HRelease(p, ctx->u.dc.hp_end, ctx->u.dc.hp_start); /*fall through*/ case B2TBadArg: BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION); @@ -1549,11 +1557,11 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar case B2TDone: b2t_destroy_context(ctx); - if (ctx->u.dc.hp > ctx->u.dc.hp_end) { + if (ctx->u.dc.factory.hp > ctx->u.dc.factory.hp_end) { erl_exit(1, ":%s, line %d: heap overrun by %d words(s)\n", - __FILE__, __LINE__, ctx->u.dc.hp - ctx->u.dc.hp_end); + __FILE__, __LINE__, ctx->u.dc.factory.hp - ctx->u.dc.factory.hp_end); } - HRelease(p, ctx->u.dc.hp_end, ctx->u.dc.hp); + erts_factory_close(&ctx->u.dc.factory); if (!is_first_call) { erts_set_gc_state(p, 1); @@ -2247,7 +2255,7 @@ static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint creation) } static byte* -dec_pid(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, Eterm* objp) +dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep, Eterm* objp) { Eterm sysname; Uint data; @@ -2286,15 +2294,15 @@ dec_pid(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, Ete if(node == erts_this_node) { *objp = make_internal_pid(data); } else { - ExternalThing *etp = (ExternalThing *) *hpp; - *hpp += EXTERNAL_THING_HEAD_SIZE + 1; + ExternalThing *etp = (ExternalThing *) factory->hp; + factory->hp += EXTERNAL_THING_HEAD_SIZE + 1; etp->header = make_external_pid_header(1); - etp->next = off_heap->first; + etp->next = factory->off_heap->first; etp->node = node; etp->data.ui[0] = data; - off_heap->first = (struct erl_off_heap_header*) etp; + factory->off_heap->first = (struct erl_off_heap_header*) etp; *objp = make_external_pid(etp); } return ep; @@ -2905,69 +2913,43 @@ is_external_string(Eterm list, int* p_is_string) return len; } -/* Assumes that the ones to undo are preluding the list. */ -static void -undo_offheap_in_area(ErlOffHeap* off_heap, Eterm* start, Eterm* end) -{ - const Uint area_sz = (end - start) * sizeof(Eterm); - struct erl_off_heap_header* hdr; - struct erl_off_heap_header** hdr_nextp = NULL; - - for (hdr = off_heap->first; ; hdr=hdr->next) { - if (!in_area(hdr, start, area_sz)) { - if (hdr_nextp != NULL) { - *hdr_nextp = NULL; - erts_cleanup_offheap(off_heap); - off_heap->first = hdr; - } - break; - } - hdr_nextp = &hdr->next; - } - - /* Assert that the ones to undo were indeed preluding the list. */ -#ifdef DEBUG - for (hdr = off_heap->first; hdr != NULL; hdr = hdr->next) { - ASSERT(!in_area(hdr, start, area_sz)); - } -#endif /* DEBUG */ -} -struct dec_term_hamt_placeholder +struct dec_term_hamt { - struct dec_term_hamt_placeholder* next; Eterm* objp; /* write result here */ Uint size; /* nr of leafs */ - Eterm leafs[1]; + Eterm* leaf_array; }; -#define DEC_TERM_HAMT_PLACEHOLDER_SIZE \ - (offsetof(struct dec_term_hamt_placeholder, leafs) / sizeof(Eterm)) /* Decode term from external format into *objp. -** On failure return NULL and *hpp will be unchanged. +** On failure calls erts_factory_undo() and returns NULL */ static byte* -dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, - Eterm* objp, B2TContext* ctx) +dec_term(ErtsDistExternal *edep, + ErtsHeapFactory* factory, + byte* ep, + Eterm* objp, + B2TContext* ctx) { - Eterm* hp_saved; +#define PSTACK_TYPE struct dec_term_hamt + PSTACK_DECLARE(hamt_array, 5); int n; ErtsAtomEncoding char_enc; register Eterm* hp; /* Please don't take the address of hp */ Eterm *maps_list; /* for preprocessing of small maps */ - struct dec_term_hamt_placeholder* hamt_list; /* for preprocessing of big maps */ Eterm* next; SWord reds; +#ifdef DEBUG + Eterm* dbg_resultp = ctx ? &ctx->u.dc.res : objp; +#endif if (ctx) { - hp_saved = ctx->u.dc.hp_start; reds = ctx->reds; next = ctx->u.dc.next; ep = ctx->u.dc.ep; - hpp = &ctx->u.dc.hp; + factory = &ctx->u.dc.factory; maps_list = ctx->u.dc.maps_list; - hamt_list = ctx->u.dc.hamt_list; if (ctx->state != B2TDecode) { int n_limit = reds; @@ -3012,7 +2994,7 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, break; case B2TDecodeString: - hp = *hpp; + hp = factory->hp; hp[-1] = make_list(hp); /* overwrite the premature NIL */ while (n-- > 0) { hp[0] = make_small(*ep++); @@ -3020,7 +3002,7 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, hp += 2; } hp[-1] = NIL; - *hpp = hp; + factory->hp = hp; break; case B2TDecodeBinary: @@ -3042,16 +3024,18 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, return NULL; } } + PSTACK_CHANGE_ALLOCATOR(hamt_array, ERTS_ALC_T_SAVED_ESTACK); + if (ctx->u.dc.hamt_array.pstart) { + PSTACK_RESTORE(hamt_array, &ctx->u.dc.hamt_array); + } } else { - hp_saved = *hpp; reds = ERTS_SWORD_MAX; next = objp; *next = (Eterm) (UWord) NULL; maps_list = NULL; - hamt_list = NULL; } - hp = *hpp; + hp = factory->hp; while (next != NULL) { @@ -3288,9 +3272,9 @@ dec_term_atom_common: break; } case PID_EXT: - *hpp = hp; - ep = dec_pid(edep, hpp, ep, off_heap, objp); - hp = *hpp; + factory->hp = hp; + ep = dec_pid(edep, factory, ep, objp); + hp = factory->hp; if (ep == NULL) { goto error; } @@ -3324,11 +3308,11 @@ dec_term_atom_common: hp += EXTERNAL_THING_HEAD_SIZE + 1; etp->header = make_external_port_header(1); - etp->next = off_heap->first; + etp->next = factory->off_heap->first; etp->node = node; etp->data.ui[0] = num; - off_heap->first = (struct erl_off_heap_header*)etp; + factory->off_heap->first = (struct erl_off_heap_header*)etp; *objp = make_external_port(etp); } @@ -3408,10 +3392,10 @@ dec_term_atom_common: #else etp->header = make_external_ref_header(ref_words); #endif - etp->next = off_heap->first; + etp->next = factory->off_heap->first; etp->node = node; - off_heap->first = (struct erl_off_heap_header*)etp; + factory->off_heap->first = (struct erl_off_heap_header*)etp; *objp = make_external_ref(etp); ref_num = &(etp->data.ui32[0]); } @@ -3451,9 +3435,9 @@ dec_term_atom_common: hp += PROC_BIN_SIZE; pb->thing_word = HEADER_PROC_BIN; pb->size = n; - pb->next = off_heap->first; - off_heap->first = (struct erl_off_heap_header*)pb; - OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm)); + pb->next = factory->off_heap->first; + factory->off_heap->first = (struct erl_off_heap_header*)pb; + OH_OVERHEAD(factory->off_heap, pb->size / sizeof(Eterm)); pb->val = dbin; pb->bytes = (byte*) dbin->orig_bytes; pb->flags = 0; @@ -3503,9 +3487,9 @@ dec_term_atom_common: pb = (ProcBin *) hp; pb->thing_word = HEADER_PROC_BIN; pb->size = n; - pb->next = off_heap->first; - off_heap->first = (struct erl_off_heap_header*)pb; - OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm)); + pb->next = factory->off_heap->first; + factory->off_heap->first = (struct erl_off_heap_header*)pb; + OH_OVERHEAD(factory->off_heap, pb->size / sizeof(Eterm)); pb->val = dbin; pb->bytes = (byte*) dbin->orig_bytes; pb->flags = 0; @@ -3557,9 +3541,9 @@ dec_term_atom_common: if ((ep = dec_atom(edep, ep, &name)) == NULL) { goto error; } - *hpp = hp; - ep = dec_term(edep, hpp, ep, off_heap, &temp, NULL); - hp = *hpp; + factory->hp = hp; + ep = dec_term(edep, factory, ep, &temp, NULL); + hp = factory->hp; if (ep == NULL) { goto error; } @@ -3631,15 +3615,11 @@ dec_term_atom_common: } } else { /* Make hamt */ - struct dec_term_hamt_placeholder* holder = - (struct dec_term_hamt_placeholder*) hp; - - holder->next = hamt_list; - hamt_list = holder; - holder->objp = objp; - holder->size = size; + struct dec_term_hamt* hamt = PSTACK_PUSH(hamt_array); - hp += DEC_TERM_HAMT_PLACEHOLDER_SIZE; + hamt->objp = objp; + hamt->size = size; + hamt->leaf_array = hp; for (n = size; n; n--) { CDR(hp) = (Eterm) COMPRESS_POINTER(next); @@ -3681,9 +3661,9 @@ dec_term_atom_common: if ((ep = dec_atom(edep, ep, &module)) == NULL) { goto error; } - *hpp = hp; + factory->hp = hp; /* Index */ - if ((ep = dec_term(edep, hpp, ep, off_heap, &temp, NULL)) == NULL) { + if ((ep = dec_term(edep, factory, ep, &temp, NULL)) == NULL) { goto error; } if (!is_small(temp)) { @@ -3692,7 +3672,7 @@ dec_term_atom_common: old_index = unsigned_val(temp); /* Uniq */ - if ((ep = dec_term(edep, hpp, ep, off_heap, &temp, NULL)) == NULL) { + if ((ep = dec_term(edep, factory, ep, &temp, NULL)) == NULL) { goto error; } if (!is_small(temp)) { @@ -3704,8 +3684,8 @@ dec_term_atom_common: * It is safe to link the fun into the fun list only when * no more validity tests can fail. */ - funp->next = off_heap->first; - off_heap->first = (struct erl_off_heap_header*)funp; + funp->next = factory->off_heap->first; + factory->off_heap->first = (struct erl_off_heap_header*)funp; funp->fe = erts_put_fun_entry2(module, old_uniq, old_index, uniq, index, arity); @@ -3716,7 +3696,7 @@ dec_term_atom_common: } funp->native_address = funp->fe->native_address; #endif - hp = *hpp; + hp = factory->hp; /* Environment */ for (i = num_free-1; i >= 0; i--) { @@ -3742,14 +3722,14 @@ dec_term_atom_common: ep += 4; hp += ERL_FUN_SIZE; hp += num_free; - *hpp = hp; + factory->hp = hp; funp->thing_word = HEADER_FUN; funp->num_free = num_free; *objp = make_fun(funp); /* Creator pid */ if (*ep != PID_EXT - || (ep = dec_pid(edep, hpp, ++ep, off_heap, + || (ep = dec_pid(edep, factory, ++ep, &funp->creator))==NULL) { goto error; } @@ -3760,7 +3740,7 @@ dec_term_atom_common: } /* Index */ - if ((ep = dec_term(edep, hpp, ep, off_heap, &temp, NULL)) == NULL) { + if ((ep = dec_term(edep, factory, ep, &temp, NULL)) == NULL) { goto error; } if (!is_small(temp)) { @@ -3769,7 +3749,7 @@ dec_term_atom_common: old_index = unsigned_val(temp); /* Uniq */ - if ((ep = dec_term(edep, hpp, ep, off_heap, &temp, NULL)) == NULL) { + if ((ep = dec_term(edep, factory, ep, &temp, NULL)) == NULL) { goto error; } if (!is_small(temp)) { @@ -3780,8 +3760,8 @@ dec_term_atom_common: * It is safe to link the fun into the fun list only when * no more validity tests can fail. */ - funp->next = off_heap->first; - off_heap->first = (struct erl_off_heap_header*)funp; + funp->next = factory->off_heap->first; + factory->off_heap->first = (struct erl_off_heap_header*)funp; old_uniq = unsigned_val(temp); funp->fe = erts_put_fun_entry(module, old_uniq, old_index); @@ -3789,7 +3769,7 @@ dec_term_atom_common: #ifdef HIPE funp->native_address = funp->fe->native_address; #endif - hp = *hpp; + hp = factory->hp; /* Environment */ for (i = num_free-1; i >= 0; i--) { @@ -3823,9 +3803,9 @@ dec_term_atom_common: erts_refc_inc(&pb->val->refc, 1); hp += PROC_BIN_SIZE; - pb->next = off_heap->first; - off_heap->first = (struct erl_off_heap_header*)pb; - OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm)); + pb->next = factory->off_heap->first; + factory->off_heap->first = (struct erl_off_heap_header*)pb; + OH_OVERHEAD(factory->off_heap, pb->size / sizeof(Eterm)); pb->flags = 0; *objp = make_binary(pb); break; @@ -3841,9 +3821,9 @@ dec_term_atom_common: erts_refc_inc(&pb->val->refc, 1); hp += PROC_BIN_SIZE; - pb->next = off_heap->first; - off_heap->first = (struct erl_off_heap_header*)pb; - OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm)); + pb->next = factory->off_heap->first; + factory->off_heap->first = (struct erl_off_heap_header*)pb; + OH_OVERHEAD(factory->off_heap, pb->size / sizeof(Eterm)); pb->flags = 0; sub = (ErlSubBin*)hp; @@ -3869,9 +3849,11 @@ dec_term_atom_common: if (next || ctx->state != B2TDecode) { ctx->u.dc.ep = ep; ctx->u.dc.next = next; - ctx->u.dc.hp = hp; + ctx->u.dc.factory.hp = hp; ctx->u.dc.maps_list = maps_list; - ctx->u.dc.hamt_list = hamt_list; + if (!PSTACK_IS_EMPTY(hamt_array)) { + PSTACK_SAVE(hamt_array, &ctx->u.dc.hamt_array); + } ctx->reds = 0; return NULL; } @@ -3894,40 +3876,36 @@ dec_term_atom_common: maps_list = next; } - /* Iterate through all the hamts and build tree nodes. + ASSERT(hp <= factory->hp_end + || (factory->mode == FACTORY_CLOSED && is_immed(*dbg_resultp))); + factory->hp = hp; + /* + * From here on factory may produce (more) heap fragments */ - if (hamt_list) { - ErtsHeapFactory factory; - factory.p = NULL; - factory.hp = hp; - /* We assume heap will suffice (see hashmap_over_estimated_heap_size) */ + if (!PSTACK_IS_EMPTY(hamt_array)) { + do { + struct dec_term_hamt* hamt = PSTACK_TOP(hamt_array); - do { - struct dec_term_hamt_placeholder* hamt = hamt_list; - *hamt->objp = erts_hashmap_from_array(&factory, - hamt->leafs, + *hamt->objp = erts_hashmap_from_array(factory, + hamt->leaf_array, hamt->size, 1); if (is_non_value(*hamt->objp)) - goto error; + goto error_hamt; - hamt_list = hamt->next; - - /* Yes, we waste a couple of heap words per hamt - for the temporary placeholder */ - *(Eterm*)hamt = make_pos_bignum_header(DEC_TERM_HAMT_PLACEHOLDER_SIZE-1); - } while (hamt_list); - - hp = factory.hp; + (void) PSTACK_POP(hamt_array); + } while (!PSTACK_IS_EMPTY(hamt_array)); + PSTACK_DESTROY(hamt_array); } + ASSERT((Eterm*)EXPAND_POINTER(*dbg_resultp) != NULL); + if (ctx) { ctx->state = B2TDone; ctx->reds = reds; } - *hpp = hp; return ep; error: @@ -3935,11 +3913,12 @@ error: * Must unlink all off-heap objects that may have been * linked into the process. */ - if (hp < *hpp) { /* Sometimes we used hp and sometimes *hpp */ - hp = *hpp; /* the largest must be the freshest */ + if (factory->hp < hp) { /* Sometimes we used hp and sometimes factory->hp */ + factory->hp = hp; /* the largest must be the freshest */ } - undo_offheap_in_area(off_heap, hp_saved, hp); - *hpp = hp_saved; +error_hamt: + erts_factory_undo(factory); + PSTACK_DESTROY(hamt_array); if (ctx) { ctx->state = B2TDecodeFail; ctx->reds = reds; @@ -4465,7 +4444,7 @@ init_done: if (n <= MAP_SMALL_MAP_LIMIT) { heap_size += 3 + n + 1 + n; } else { - heap_size += hashmap_over_estimated_heap_size(n); + heap_size += HASHMAP_ESTIMATED_HEAP_SIZE(n); } break; case STRING_EXT: diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index 50fcfa04d6..508ab8dc17 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -148,6 +148,7 @@ typedef struct { byte *extp; int exttmp; Uint extsize; + Uint heap_size; } ErtsBinary2TermState; @@ -185,18 +186,18 @@ void erts_destroy_dist_ext_copy(ErtsDistExternal *); int erts_prepare_dist_ext(ErtsDistExternal *, byte *, Uint, DistEntry *, ErtsAtomCache *); Sint erts_decode_dist_ext_size(ErtsDistExternal *); -Eterm erts_decode_dist_ext(Eterm **, ErlOffHeap *, ErtsDistExternal *); +Eterm erts_decode_dist_ext(ErtsHeapFactory* factory, ErtsDistExternal *); Sint erts_decode_ext_size(byte*, Uint); Sint erts_decode_ext_size_ets(byte*, Uint); -Eterm erts_decode_ext(Eterm **, ErlOffHeap *, byte**); -Eterm erts_decode_ext_ets(Eterm **, ErlOffHeap *, byte*); +Eterm erts_decode_ext(ErtsHeapFactory*, byte**); +Eterm erts_decode_ext_ets(ErtsHeapFactory*, byte*); Eterm erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags); Sint erts_binary2term_prepare(ErtsBinary2TermState *, byte *, Sint); void erts_binary2term_abort(ErtsBinary2TermState *); -Eterm erts_binary2term_create(ErtsBinary2TermState *, Eterm **hpp, ErlOffHeap *); +Eterm erts_binary2term_create(ErtsBinary2TermState *, ErtsHeapFactory*); int erts_debug_max_atom_out_cache_index(void); int erts_debug_atom_to_out_cache_index(Eterm); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index ccc7da265e..07a82b42d2 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1395,31 +1395,26 @@ finalize_force_imm_drv_call(ErtsTryImmDrvCallState *sp) static ERTS_INLINE void queue_port_sched_op_reply(Process *rp, ErtsProcLocks *rp_locksp, - Eterm *hp_start, - Eterm *hp, - Uint h_size, - ErlHeapFragment* bp, + ErtsHeapFactory* factory, Uint32 *ref_num, Eterm msg) { - Eterm ref = make_internal_ref(hp); + Eterm* hp = erts_produce_heap(factory, ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE, 0); + Eterm ref; + + ref= make_internal_ref(hp); write_ref_thing(hp, ref_num[0], ref_num[1], ref_num[2]); hp += REF_THING_SIZE; msg = TUPLE2(hp, ref, msg); hp += 3; - if (!bp) { - HRelease(rp, hp_start + h_size, hp); - } - else { - Uint used_h_size = hp - hp_start; - ASSERT(h_size >= used_h_size); - if (h_size > used_h_size) - bp = erts_resize_message_buffer(bp, used_h_size, &msg, 1); - } + /* SVERK: We used to call erts_resize_message_buffer here + * to maybe realloc message. + */ + erts_factory_close(factory); - erts_queue_message(rp, rp_locksp, bp, msg, NIL); + erts_queue_message(rp, rp_locksp, factory->heap_frags, msg, NIL); } static void @@ -1429,9 +1424,10 @@ port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg) if (rp) { ErlOffHeap *ohp; ErlHeapFragment* bp; + ErtsHeapFactory factory; Eterm msg_copy; Uint hsz, msg_sz; - Eterm *hp, *hp_start; + Eterm *hp; ErtsProcLocks rp_locks = 0; hsz = ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE; @@ -1442,22 +1438,22 @@ port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg) hsz += msg_sz; } - hp_start = hp = erts_alloc_message_heap(hsz, + hp = erts_alloc_message_heap(hsz, &bp, &ohp, rp, &rp_locks); + erts_factory_message_init(&factory, rp, hp, bp); if (is_immed(msg)) msg_copy = msg; - else + else { msg_copy = copy_struct(msg, msg_sz, &hp, ohp); + factory.hp = hp; + } queue_port_sched_op_reply(rp, &rp_locks, - hp_start, - hp, - hsz, - bp, + &factory, ref_num, msg_copy); @@ -3896,12 +3892,13 @@ port_sig_control(Port *prt, if (res == ERTS_PORT_OP_DONE) { Eterm msg; - Eterm *hp, *hp_start; + Eterm *hp; ErlHeapFragment *bp; ErlOffHeap *ohp; + ErtsHeapFactory factory; Process *rp; ErtsProcLocks rp_locks = 0; - Uint hsz; + Uint hsz, rsz; int control_flags; rp = erts_proc_lookup_raw(sigdp->caller); @@ -3910,17 +3907,19 @@ port_sig_control(Port *prt, control_flags = prt->control_flags; - hsz = ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE; - hsz += port_control_result_size(control_flags, + rsz = port_control_result_size(control_flags, resp_bufp, &resp_size, &resp_buf[0]); + hsz = rsz + ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE; + - hp_start = hp = erts_alloc_message_heap(hsz, + hp = erts_alloc_message_heap(hsz, &bp, &ohp, rp, &rp_locks); + erts_factory_message_init(&factory, rp, hp, bp); msg = write_port_control_result(control_flags, resp_bufp, @@ -3929,13 +3928,11 @@ port_sig_control(Port *prt, &hp, bp, ohp); + factory.hp = hp; queue_port_sched_op_reply(rp, &rp_locks, - hp_start, - hp, - hsz, - bp, + &factory, sigdp->ref, msg); @@ -4222,8 +4219,6 @@ port_sig_call(Port *prt, if (res == ERTS_PORT_OP_DONE) { Eterm msg; Eterm *hp; - ErlHeapFragment *bp; - ErlOffHeap *ohp; Process *rp; ErtsProcLocks rp_locks = 0; Sint hsz; @@ -4234,29 +4229,31 @@ port_sig_call(Port *prt, hsz = erts_decode_ext_size((byte *) resp_bufp, resp_size); if (hsz >= 0) { - Eterm *hp_start; + ErlHeapFragment* bp; + ErlOffHeap* ohp; + ErtsHeapFactory factory; byte *endp; hsz += 3; /* ok tuple */ hsz += ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE; - hp_start = hp = erts_alloc_message_heap(hsz, - &bp, - &ohp, - rp, - &rp_locks); + hp = erts_alloc_message_heap(hsz, + &bp, + &ohp, + rp, + &rp_locks); endp = (byte *) resp_bufp; - msg = erts_decode_ext(&hp, ohp, &endp); + erts_factory_message_init(&factory, rp, hp, bp); + msg = erts_decode_ext(&factory, &endp); if (is_value(msg)) { + hp = erts_produce_heap(&factory, + 3, + ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE); msg = TUPLE2(hp, am_ok, msg); - hp += 3; queue_port_sched_op_reply(rp, &rp_locks, - hp_start, - hp, - hsz, - bp, + &factory, sigdp->ref, msg); @@ -4264,8 +4261,6 @@ port_sig_call(Port *prt, erts_smp_proc_unlock(rp, rp_locks); goto done; } - if (bp) - free_message_buffer(bp); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); } @@ -4342,10 +4337,11 @@ erts_port_call(Process* c_p, try_call_res = try_imm_drv_call(&try_call_state); switch (try_call_res) { case ERTS_TRY_IMM_DRV_CALL_OK: { - Eterm *hp, *hp_end; + ErtsHeapFactory factory; Sint hsz; unsigned ret_flags = 0U; Eterm term; + Eterm* hp; res = call_driver_call(c_p->common.id, prt, @@ -4365,15 +4361,14 @@ erts_port_call(Process* c_p, if (hsz < 0) return ERTS_PORT_OP_BADARG; hsz += 3; - hp = HAlloc(c_p, hsz); - hp_end = hp + hsz; + erts_factory_proc_prealloc_init(&factory, c_p, hsz); endp = (byte *) resp_bufp; - term = erts_decode_ext(&hp, &MSO(c_p), &endp); + term = erts_decode_ext(&factory, &endp); if (term == THE_NON_VALUE) return ERTS_PORT_OP_BADARG; + hp = erts_produce_heap(&factory,3,0); *retvalp = TUPLE2(hp, am_ok, term); - hp += 3; - HRelease(c_p, hp_end, hp); + erts_factory_close(&factory); if (resp_bufp != &resp_buf[0] && !(ret_flags & DRIVER_CALL_KEEP_BUFFER)) driver_free(resp_bufp); @@ -4508,12 +4503,11 @@ port_sig_info(Port *prt, prt, sigdp->u.info.item); if (is_value(value)) { + ErtsHeapFactory factory; + erts_factory_message_init(&factory, NULL, hp, bp); queue_port_sched_op_reply(rp, &rp_locks, - hp_start, - hp, - hsz, - bp, + &factory, sigdp->ref, value); } @@ -5106,22 +5100,22 @@ cleanup_b2t_states(struct b2t_states__ *b2tsp) static int driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) { +#define HEAP_EXTRA 200 #define ERTS_DDT_FAIL do { res = -1; goto done; } while (0) Uint need = 0; int depth = 0; int res; - Eterm *hp = NULL, *hp_start = NULL, *hp_end = NULL; ErlDrvTermData* ptr; ErlDrvTermData* ptr_end; DECLARE_ESTACK(stack); Eterm mess = NIL; /* keeps compiler happy */ Process* rp = NULL; - ErlHeapFragment *bp = NULL; - ErlOffHeap *ohp; + ErtsHeapFactory factory; ErtsProcLocks rp_locks = 0; struct b2t_states__ b2t; int scheduler = 1; /* Silence erroneous warning... */ + factory.mode = FACTORY_CLOSED; init_b2t_states(&b2t); /* @@ -5285,9 +5279,10 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) #ifdef DEBUG b2t.org_ext[b2t.ix] = ext; #endif - hsz = erts_binary2term_prepare(&b2t.state[b2t.ix++], ext, size); + hsz = erts_binary2term_prepare(&b2t.state[b2t.ix], ext, size); if (hsz < 0) ERTS_DDT_FAIL; /* Invalid data */ + b2t.state[b2t.ix++].heap_size = hsz; need += hsz; ptr += 2; depth++; @@ -5297,7 +5292,7 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) ERTS_DDT_CHK_ENOUGH_ARGS(1); if ((int) ptr[0] < 0) ERTS_DDT_FAIL; if (ptr[0] > MAP_SMALL_MAP_LIMIT) { - need += hashmap_over_estimated_heap_size(ptr[0]); + need += HASHMAP_ESTIMATED_HEAP_SIZE(ptr[0]); } else { need += MAP_HEADER_FLATMAP_SZ + 1 + 2*ptr[0]; } @@ -5336,8 +5331,14 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) goto done; } - hp_start = hp = erts_alloc_message_heap(need, &bp, &ohp, rp, &rp_locks); - hp_end = hp + need; + + /* We could try to copy directly to destination heap + * if we knew there is no big maps. + + erts_alloc_message_heap(need, &bp, &ohp, rp, &rp_locks); + */ + erts_factory_message_init(&factory, NULL, NULL, + new_message_buffer(need)); /* * Interpret the instructions and build the term. @@ -5358,13 +5359,15 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) case ERL_DRV_INT: /* signed int argument */ #if HALFWORD_HEAP - mess = erts_bld_sint64(&hp, NULL, (Sint64)ptr[0]); + erts_reserve_heap(&factory, BIG_NEED_SIZE(2)); + mess = erts_bld_sint64(&factory.hp, NULL, (Sint64)ptr[0]); #else + erts_reserve_heap(&factory, BIG_UINT_HEAP_SIZE); if (IS_SSMALL((Sint)ptr[0])) mess = make_small((Sint)ptr[0]); else { - mess = small_to_big((Sint)ptr[0], hp); - hp += BIG_UINT_HEAP_SIZE; + mess = small_to_big((Sint)ptr[0], factory.hp); + factory.hp += BIG_UINT_HEAP_SIZE; } #endif ptr++; @@ -5372,25 +5375,29 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) case ERL_DRV_UINT: /* unsigned int argument */ #if HALFWORD_HEAP - mess = erts_bld_uint64(&hp, NULL, (Uint64)ptr[0]); + erts_reserve_heap(&factory, BIG_NEED_FOR_BITS(64)); + mess = erts_bld_uint64(&factory.hp, NULL, (Uint64)ptr[0]); #else + erts_reserve_heap(&factory, BIG_UINT_HEAP_SIZE); if (IS_USMALL(0, (Uint)ptr[0])) mess = make_small((Uint)ptr[0]); else { - mess = uint_to_big((Uint)ptr[0], hp); - hp += BIG_UINT_HEAP_SIZE; + mess = uint_to_big((Uint)ptr[0], factory.hp); + factory.hp += BIG_UINT_HEAP_SIZE; } #endif ptr++; break; case ERL_DRV_INT64: /* pointer to unsigned 64-bit int argument */ - mess = erts_bld_sint64(&hp, NULL, *((Sint64 *) ptr[0])); + erts_reserve_heap(&factory, BIG_NEED_FOR_BITS(64)); + mess = erts_bld_sint64(&factory.hp, NULL, *((Sint64 *) ptr[0])); ptr++; break; case ERL_DRV_UINT64: /* pointer to unsigned 64-bit int argument */ - mess = erts_bld_uint64(&hp, NULL, *((Uint64 *) ptr[0])); + erts_reserve_heap(&factory, BIG_NEED_FOR_BITS(64)); + mess = erts_bld_uint64(&factory.hp, NULL, *((Uint64 *) ptr[0])); ptr++; break; @@ -5407,8 +5414,8 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) size); if (size <= ERL_ONHEAP_BIN_LIMIT) { - ErlHeapBin* hbp = (ErlHeapBin *) hp; - hp += heap_bin_size(size); + ErlHeapBin* hbp = (ErlHeapBin *) erts_produce_heap(&factory, + heap_bin_size(size), HEAP_EXTRA); hbp->thing_word = header_heap_bin(size); hbp->size = size; if (size > 0) { @@ -5417,18 +5424,18 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) mess = make_binary(hbp); } else { - ProcBin* pb = (ProcBin *) hp; + ProcBin* pb = (ProcBin *) erts_produce_heap(&factory, + PROC_BIN_SIZE, HEAP_EXTRA); driver_binary_inc_refc(b); /* caller will free binary */ pb->thing_word = HEADER_PROC_BIN; pb->size = size; - pb->next = ohp->first; - ohp->first = (struct erl_off_heap_header*)pb; + pb->next = factory.off_heap->first; + factory.off_heap->first = (struct erl_off_heap_header*)pb; pb->val = ErlDrvBinary2Binary(b); pb->bytes = ((byte*) b->orig_bytes) + offset; pb->flags = 0; mess = make_binary(pb); - hp += PROC_BIN_SIZE; - OH_OVERHEAD(ohp, pb->size / sizeof(Eterm)); + OH_OVERHEAD(factory.off_heap, pb->size / sizeof(Eterm)); } ptr += 3; break; @@ -5441,8 +5448,9 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) size); if (size <= ERL_ONHEAP_BIN_LIMIT) { - ErlHeapBin* hbp = (ErlHeapBin *) hp; - hp += heap_bin_size(size); + ErlHeapBin* hbp = (ErlHeapBin *) erts_produce_heap(&factory, + heap_bin_size(size), + HEAP_EXTRA); hbp->thing_word = header_heap_bin(size); hbp->size = size; if (size > 0) { @@ -5457,16 +5465,16 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) ASSERT(bufp); erts_refc_init(&bp->refc, 1); sys_memcpy((void *) bp->orig_bytes, (void *) bufp, size); - pbp = (ProcBin *) hp; - hp += PROC_BIN_SIZE; + pbp = (ProcBin *) erts_produce_heap(&factory, + PROC_BIN_SIZE, HEAP_EXTRA); pbp->thing_word = HEADER_PROC_BIN; pbp->size = size; - pbp->next = ohp->first; - ohp->first = (struct erl_off_heap_header*)pbp; + pbp->next = factory.off_heap->first; + factory.off_heap->first = (struct erl_off_heap_header*)pbp; pbp->val = bp; pbp->bytes = (byte*) bp->orig_bytes; pbp->flags = 0; - OH_OVERHEAD(ohp, pbp->size / sizeof(Eterm)); + OH_OVERHEAD(factory.off_heap, pbp->size / sizeof(Eterm)); mess = make_binary(pbp); } ptr += 2; @@ -5475,13 +5483,15 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) case ERL_DRV_STRING: /* char*, length */ erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) ptr[1]); - mess = buf_to_intlist(&hp, (char*)ptr[0], ptr[1], NIL); + erts_reserve_heap(&factory, 2*ptr[1]); + mess = buf_to_intlist(&factory.hp, (char*)ptr[0], ptr[1], NIL); ptr += 2; break; case ERL_DRV_STRING_CONS: /* char*, length */ mess = ESTACK_POP(stack); - mess = buf_to_intlist(&hp, (char*)ptr[0], ptr[1], mess); + erts_reserve_heap(&factory, 2*ptr[1]); + mess = buf_to_intlist(&factory.hp, (char*)ptr[0], ptr[1], mess); ptr += 2; break; @@ -5490,11 +5500,12 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) mess = ESTACK_POP(stack); i--; + erts_reserve_heap(&factory, 2*i); while(i > 0) { Eterm hd = ESTACK_POP(stack); - mess = CONS(hp, hd, mess); - hp += 2; + mess = CONS(factory.hp, hd, mess); + factory.hp += 2; i--; } ptr++; @@ -5503,13 +5514,12 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) case ERL_DRV_TUPLE: { /* int */ int size = (int)ptr[0]; - Eterm* tp = hp; + Eterm* tp = erts_produce_heap(&factory, size+1, HEAP_EXTRA); *tp = make_arityval(size); mess = make_tuple(tp); tp += size; /* point at last element */ - hp = tp+1; /* advance "heap" pointer */ while(size--) { *tp-- = ESTACK_POP(stack); @@ -5525,20 +5535,22 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) case ERL_DRV_FLOAT: { /* double * */ FloatDef f; + Eterm* fp = erts_produce_heap(&factory, FLOAT_SIZE_OBJECT, HEAP_EXTRA); - mess = make_float(hp); + mess = make_float(fp); f.fd = *((double *) ptr[0]); if (!erts_isfinite(f.fd)) ERTS_DDT_FAIL; - PUT_DOUBLE(f, hp); - hp += FLOAT_SIZE_OBJECT; + PUT_DOUBLE(f, fp); ptr++; break; } case ERL_DRV_EXT2TERM: /* char *ext, int size */ ASSERT(b2t.org_ext[b2t.ix] == (byte *) ptr[0]); - mess = erts_binary2term_create(&b2t.state[b2t.ix++], &hp, ohp); + + erts_reserve_heap(&factory, b2t.state[b2t.ix].heap_size); + mess = erts_binary2term_create(&b2t.state[b2t.ix++], &factory); if (mess == THE_NON_VALUE) ERTS_DDT_FAIL; ptr += 2; @@ -5548,41 +5560,32 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) int size = (int)ptr[0]; if (size > MAP_SMALL_MAP_LIMIT) { int ix = 2*size; - ErtsHeapFactory factory; - Eterm* leafs = hp; - - hp += 2*size; - while(ix--) { *--hp = ESTACK_POP(stack); } + Eterm* leafs; - hp += 2*size; - factory.p = NULL; - factory.hp = hp; - /* We assume heap will suffice (see hashmap_over_estimated_heap_size) */ + erts_produce_heap(&factory, ix, HEAP_EXTRA); + leafs = factory.hp; + while(ix--) { *--leafs = ESTACK_POP(stack); } mess = erts_hashmap_from_array(&factory, leafs, size, 1); - if (is_non_value(mess)) ERTS_DDT_FAIL; - - hp = factory.hp; } else { - Eterm* tp = hp; Eterm* vp; flatmap_t *mp; + Eterm* tp = erts_produce_heap(&factory, + 2*size + 1 + MAP_HEADER_FLATMAP_SZ, + HEAP_EXTRA); *tp = make_arityval(size); - hp += 1 + size; - mp = (flatmap_t*)hp; + mp = (flatmap_t*) (tp + 1 + size); mp->thing_word = MAP_HEADER_FLATMAP; mp->size = size; mp->keys = make_tuple(tp); mess = make_flatmap(mp); - hp += MAP_HEADER_FLATMAP_SZ + size; - tp += size; /* point at last key */ - vp = hp - 1; /* point at last value */ + vp = factory.hp - 1; /* point at last value */ while(size--) { *vp-- = ESTACK_POP(stack); @@ -5605,25 +5608,16 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) if (res > 0) { mess = ESTACK_POP(stack); /* get resulting value */ - if (bp) - bp = erts_resize_message_buffer(bp, hp - hp_start, &mess, 1); - else { - ASSERT(hp); - HRelease(rp, hp_end, hp); - } + erts_factory_close(&factory); /* send message */ - erts_queue_message(rp, &rp_locks, bp, mess, am_undefined); + erts_queue_message(rp, &rp_locks, factory.heap_frags, mess, am_undefined); } else { if (b2t.ix > b2t.used) b2t.used = b2t.ix; for (b2t.ix = 0; b2t.ix < b2t.used; b2t.ix++) erts_binary2term_abort(&b2t.state[b2t.ix]); - if (bp) - free_message_buffer(bp); - else if (hp) { - HRelease(rp, hp_end, hp); - } + erts_factory_undo(&factory); } if (rp) { if (rp_locks) @@ -5635,6 +5629,7 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) DESTROY_ESTACK(stack); return res; #undef ERTS_DDT_FAIL +#undef HEAP_EXTRA } static ERTS_INLINE int -- cgit v1.2.3 From 7c1ed37b4aa173181c4d45a2108f5eef4c36c41e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 1 Jun 2015 19:43:13 +0200 Subject: erts: Optimize driver_deliver_term Try write directly to process heap (as before) if the term is guaranteed not to contain any big maps that may break the initial size estimation. --- erts/emulator/beam/io.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 07a82b42d2..fb030d61f9 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -5108,12 +5108,16 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) ErlDrvTermData* ptr; ErlDrvTermData* ptr_end; DECLARE_ESTACK(stack); - Eterm mess = NIL; /* keeps compiler happy */ + Eterm mess; Process* rp = NULL; ErtsHeapFactory factory; ErtsProcLocks rp_locks = 0; struct b2t_states__ b2t; - int scheduler = 1; /* Silence erroneous warning... */ + int scheduler; + int is_heap_need_limited = 1; + + ERTS_UNDEF(mess,NIL); + ERTS_UNDEF(scheduler,1); factory.mode = FACTORY_CLOSED; init_b2t_states(&b2t); @@ -5286,6 +5290,9 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) need += hsz; ptr += 2; depth++; + if (size > MAP_SMALL_MAP_LIMIT*3) { /* may contain big map */ + is_heap_need_limited = 0; + } break; } case ERL_DRV_MAP: { /* int */ @@ -5293,6 +5300,7 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) if ((int) ptr[0] < 0) ERTS_DDT_FAIL; if (ptr[0] > MAP_SMALL_MAP_LIMIT) { need += HASHMAP_ESTIMATED_HEAP_SIZE(ptr[0]); + is_heap_need_limited = 0; } else { need += MAP_HEADER_FLATMAP_SZ + 1 + 2*ptr[0]; } @@ -5331,14 +5339,17 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) goto done; } - - /* We could try to copy directly to destination heap - * if we knew there is no big maps. - - erts_alloc_message_heap(need, &bp, &ohp, rp, &rp_locks); - */ - erts_factory_message_init(&factory, NULL, NULL, - new_message_buffer(need)); + /* Try copy directly to destination heap if we know there are no big maps */ + if (is_heap_need_limited) { + ErlOffHeap *ohp; + ErlHeapFragment* bp; + Eterm* hp = erts_alloc_message_heap(need, &bp, &ohp, rp, &rp_locks); + erts_factory_message_init(&factory, rp, hp, bp); + } + else { + erts_factory_message_init(&factory, NULL, NULL, + new_message_buffer(need)); + } /* * Interpret the instructions and build the term. -- cgit v1.2.3 From f5138184479bd16c0ee0a5e583479378451401c8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 10 Jun 2015 20:59:05 +0200 Subject: erts: Add erts_factory_trim_and_close --- erts/emulator/beam/erl_message.c | 25 +++++++++++++++++++++---- erts/emulator/beam/erl_message.h | 1 + erts/emulator/beam/io.c | 6 +----- 3 files changed, 23 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 13e084ce10..f806d2c498 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -93,9 +93,6 @@ erts_resize_message_buffer(ErlHeapFragment *bp, Uint size, #endif ErlHeapFragment* nbp; - /* ToDo: Make use of 'used_size' to avoid realloc - when shrinking just a few words */ - #ifdef DEBUG { Uint off_sz = size < bp->used_size ? size : bp->used_size; @@ -110,8 +107,10 @@ erts_resize_message_buffer(ErlHeapFragment *bp, Uint size, } #endif - if (size == bp->used_size) + if (size >= (bp->used_size - bp->used_size / 16)) { + bp->used_size = size; return bp; + } #ifdef HARD_DEBUG dbg_brefs = erts_alloc(ERTS_ALC_T_UNDEF, sizeof(Eterm *)*brefs_size); @@ -1288,6 +1287,24 @@ void erts_factory_close(ErtsHeapFactory* factory) factory->mode = FACTORY_CLOSED; } +void erts_factory_trim_and_close(ErtsHeapFactory* factory, + Eterm *brefs, Uint brefs_size) +{ + if (factory->mode == FACTORY_HEAP_FRAGS) { + ErlHeapFragment* bp = factory->heap_frags; + if (bp->next == NULL) { + Uint used_sz = factory->hp - bp->mem; + ASSERT(used_sz <= bp->alloc_size); + factory->heap_frags = erts_resize_message_buffer(bp, used_sz, + brefs, brefs_size); + factory->mode = FACTORY_CLOSED; + return; + } + /*else we don't trim multi fragmented messages for now */ + } + erts_factory_close(factory); +} + void erts_factory_undo(ErtsHeapFactory* factory) { ErlHeapFragment* bp; diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 39946f2a14..705ba5e506 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -79,6 +79,7 @@ void erts_factory_dummy_init(ErtsHeapFactory*); Eterm* erts_produce_heap(ErtsHeapFactory*, Uint need, Uint xtra); Eterm* erts_reserve_heap(ErtsHeapFactory*, Uint need); void erts_factory_close(ErtsHeapFactory*); +void erts_factory_trim_and_close(ErtsHeapFactory*,Eterm *brefs, Uint brefs_size); void erts_factory_undo(ErtsHeapFactory*); #ifdef CHECK_FOR_HOLES diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index fb030d61f9..23f208c2eb 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1407,12 +1407,8 @@ queue_port_sched_op_reply(Process *rp, hp += REF_THING_SIZE; msg = TUPLE2(hp, ref, msg); - hp += 3; - /* SVERK: We used to call erts_resize_message_buffer here - * to maybe realloc message. - */ - erts_factory_close(factory); + erts_factory_trim_and_close(factory, &msg, 1); erts_queue_message(rp, rp_locksp, factory->heap_frags, msg, NIL); } -- cgit v1.2.3 From bfd575ddf985408494d4d3d4933eda2c9ee18326 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 19 May 2015 22:48:21 +0200 Subject: ETS busy wait option Conflicts: erts/emulator/beam/erl_init.c erts/etc/common/erlexec.c --- erts/emulator/beam/erl_db.c | 43 +++++++++++++++++++++++++++++++++++++++- erts/emulator/beam/erl_db.h | 13 +++++++++++- erts/emulator/beam/erl_db_hash.c | 2 ++ erts/emulator/beam/erl_init.c | 41 +++++++++++++++++++++++++++++++++----- erts/etc/common/erlexec.c | 1 + 5 files changed, 93 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 2e2cb98354..844272c699 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -279,6 +279,8 @@ static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock, erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; if (use_frequent_read_lock) rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ; + if (erts_ets_rwmtx_spin_count >= 0) + rwmtx_opt.main_spincount = erts_ets_rwmtx_spin_count; #endif #ifdef ERTS_SMP erts_smp_rwmtx_init_opt_x(&tb->common.rwlock, &rwmtx_opt, @@ -2856,10 +2858,11 @@ BIF_RETTYPE ets_match_spec_run_r_3(BIF_ALIST_3) ** External interface (NOT BIF's) */ +int erts_ets_rwmtx_spin_count = -1; /* Init the db */ -void init_db(void) +void init_db(ErtsDbSpinCount db_spin_count) { DbTable init_tb; int i; @@ -2868,10 +2871,48 @@ void init_db(void) size_t size; #ifdef ERTS_SMP + int max_spin_count = (1 << 15) - 1; /* internal limit */ 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; + switch (db_spin_count) { + case ERTS_DB_SPNCNT_NONE: + erts_ets_rwmtx_spin_count = 0; + break; + case ERTS_DB_SPNCNT_VERY_LOW: + erts_ets_rwmtx_spin_count = 100; + break; + case ERTS_DB_SPNCNT_LOW: + erts_ets_rwmtx_spin_count = 200; + erts_ets_rwmtx_spin_count += erts_no_schedulers * 50; + if (erts_ets_rwmtx_spin_count > 1000) + erts_ets_rwmtx_spin_count = 1000; + break; + case ERTS_DB_SPNCNT_HIGH: + erts_ets_rwmtx_spin_count = 2000; + erts_ets_rwmtx_spin_count += erts_no_schedulers * 100; + if (erts_ets_rwmtx_spin_count > 15000) + erts_ets_rwmtx_spin_count = 15000; + break; + case ERTS_DB_SPNCNT_VERY_HIGH: + erts_ets_rwmtx_spin_count = 15000; + erts_ets_rwmtx_spin_count += erts_no_schedulers * 500; + if (erts_ets_rwmtx_spin_count > max_spin_count) + erts_ets_rwmtx_spin_count = max_spin_count; + break; + case ERTS_DB_SPNCNT_EXTREMELY_HIGH: + erts_ets_rwmtx_spin_count = max_spin_count; + break; + case ERTS_DB_SPNCNT_NORMAL: + default: + erts_ets_rwmtx_spin_count = -1; + break; + } + + if (erts_ets_rwmtx_spin_count >= 0) + rwmtx_opt.main_spincount = erts_ets_rwmtx_spin_count; + meta_main_tab_locks = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_DB_TABLES, sizeof(erts_meta_main_tab_lock_t) diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h index 5b4681fc90..5039b71108 100644 --- a/erts/emulator/beam/erl_db.h +++ b/erts/emulator/beam/erl_db.h @@ -61,7 +61,17 @@ union db_table { "ERL_MAX_ETS_TABLES" */ #define ERL_MAX_ETS_TABLES_ENV "ERL_MAX_ETS_TABLES" -void init_db(void); +typedef enum { + ERTS_DB_SPNCNT_NONE, + ERTS_DB_SPNCNT_VERY_LOW, + ERTS_DB_SPNCNT_LOW, + ERTS_DB_SPNCNT_NORMAL, + ERTS_DB_SPNCNT_HIGH, + ERTS_DB_SPNCNT_VERY_HIGH, + ERTS_DB_SPNCNT_EXTREMELY_HIGH +} ErtsDbSpinCount; + +void init_db(ErtsDbSpinCount); int erts_db_process_exiting(Process *, ErtsProcLocks); void db_info(int, void *, int); void erts_db_foreach_table(void (*)(DbTable *, void *), void *); @@ -69,6 +79,7 @@ void erts_db_foreach_offheap(DbTable *, void (*func)(ErlOffHeap *, void *), void *); +extern int erts_ets_rwmtx_spin_count; extern int user_requested_db_max_tabs; /* set in erl_init */ extern int erts_ets_realloc_always_moves; /* set in erl_init */ extern int erts_ets_always_compress; /* set in erl_init */ diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 383ee7c430..f6ea57e513 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -670,6 +670,8 @@ int db_create_hash(Process *p, DbTable *tbl) int i; if (tb->common.type & DB_FREQ_READ) rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ; + if (erts_ets_rwmtx_spin_count >= 0) + rwmtx_opt.main_spincount = erts_ets_rwmtx_spin_count; tb->locks = (DbTableHashFineLocks*) erts_db_alloc_fnf(ERTS_ALC_T_DB_SEG, /* Other type maybe? */ (DbTable *) tb, sizeof(DbTableHashFineLocks)); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 574a49dc7a..c676682463 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -142,7 +142,8 @@ static void erl_init(int ncpu, int legacy_port_tab, int time_correction, ErtsTimeWarpMode time_warp_mode, - int node_tab_delete_delay); + int node_tab_delete_delay, + ErtsDbSpinCount db_spin_count); static erts_atomic_t exiting; @@ -316,7 +317,8 @@ erts_short_init(void) 0, time_correction, time_warp_mode, - ERTS_NODE_TAB_DELAY_GC_DEFAULT); + ERTS_NODE_TAB_DELAY_GC_DEFAULT, + ERTS_DB_SPNCNT_NORMAL); erts_initialized = 1; } @@ -329,7 +331,8 @@ erl_init(int ncpu, int legacy_port_tab, int time_correction, ErtsTimeWarpMode time_warp_mode, - int node_tab_delete_delay) + int node_tab_delete_delay, + ErtsDbSpinCount db_spin_count) { init_benchmarking(); @@ -369,7 +372,7 @@ erl_init(int ncpu, erts_ptab_init(); /* Must be after init_emulator() */ erts_init_binary(); /* Must be after init_emulator() */ erts_bp_init(); - init_db(); /* Must be after init_emulator */ + init_db(db_spin_count); /* Must be after init_emulator */ erts_init_node_tables(node_tab_delete_delay); init_dist(); erl_drv_thr_init(); @@ -635,6 +638,10 @@ void erts_usage(void) erts_fprintf(stderr, "-zdntgc time set delayed node table gc in seconds\n"); erts_fprintf(stderr, " valid values are infinity or intergers in the range [0-%d]\n", ERTS_NODE_TAB_DELAY_GC_MAX); +#if 0 + erts_fprintf(stderr, "-zebwt val set ets busy wait threshold, valid values are:\n"); + erts_fprintf(stderr, " none|very_short|short|medium|long|very_long|extremely_long\n"); +#endif erts_fprintf(stderr, "\n"); erts_fprintf(stderr, "Note that if the emulator is started with erlexec (typically\n"); erts_fprintf(stderr, "from the erl script), these flags should be specified with +.\n"); @@ -1226,6 +1233,7 @@ erl_start(int argc, char **argv) int time_correction; ErtsTimeWarpMode time_warp_mode; int node_tab_delete_delay = ERTS_NODE_TAB_DELAY_GC_DEFAULT; + ErtsDbSpinCount db_spin_count = ERTS_DB_SPNCNT_NORMAL; set_default_time_adj(&time_correction, &time_warp_mode); @@ -2040,6 +2048,28 @@ erl_start(int argc, char **argv) } } node_tab_delete_delay = (int) secs; + } + else if (has_prefix("ebwt", sub_param)) { + arg = get_arg(sub_param+4, argv[i+1], &i); + if (sys_strcmp(arg, "none") == 0) + db_spin_count = ERTS_DB_SPNCNT_NONE; + else if (sys_strcmp(arg, "very_short") == 0) + db_spin_count = ERTS_DB_SPNCNT_VERY_LOW; + else if (sys_strcmp(arg, "short") == 0) + db_spin_count = ERTS_DB_SPNCNT_LOW; + else if (sys_strcmp(arg, "medium") == 0) + db_spin_count = ERTS_DB_SPNCNT_NORMAL; + else if (sys_strcmp(arg, "long") == 0) + db_spin_count = ERTS_DB_SPNCNT_HIGH; + else if (sys_strcmp(arg, "very_long") == 0) + db_spin_count = ERTS_DB_SPNCNT_VERY_HIGH; + else if (sys_strcmp(arg, "extremely_long") == 0) + db_spin_count = ERTS_DB_SPNCNT_EXTREMELY_HIGH; + else { + erts_fprintf(stderr, + "Invalid ets busy wait threshold: %s\n", arg); + erts_usage(); + } } else { erts_fprintf(stderr, "bad -z option %s\n", argv[i]); erts_usage(); @@ -2114,7 +2144,8 @@ erl_start(int argc, char **argv) legacy_port_tab, time_correction, time_warp_mode, - node_tab_delete_delay); + node_tab_delete_delay, + db_spin_count); load_preloaded(); erts_end_staging_code_ix(); diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index d098100a51..dcf8ba2edd 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -158,6 +158,7 @@ static char *plusr_val_switches[] = { static char *plusz_val_switches[] = { "dbbl", "dntgc", + "ebwt", NULL }; -- cgit v1.2.3 From d18dd1ba7f7c874b1cbc2806822d134ea1546781 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 15 Jun 2015 20:13:15 +0200 Subject: Unbreak global inlining --- erts/emulator/beam/sys.h | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index cd53069872..78eb0bfe2b 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -20,6 +20,22 @@ #ifndef __SYS_H__ #define __SYS_H__ +#ifdef ERTS_INLINE +# ifndef ERTS_CAN_INLINE +# define ERTS_CAN_INLINE 1 +# endif +#else +# if defined(__GNUC__) +# define ERTS_CAN_INLINE 1 +# define ERTS_INLINE __inline__ +# elif defined(__WIN32__) +# define ERTS_CAN_INLINE 1 +# define ERTS_INLINE __inline +# else +# define ERTS_CAN_INLINE 0 +# define ERTS_INLINE +# endif +#endif #if defined(DEBUG) || defined(ERTS_ENABLE_LOCK_CHECK) # undef ERTS_CAN_INLINE @@ -94,23 +110,6 @@ typedef int ErtsSysFdType; typedef ERTS_SYS_FD_TYPE ErtsSysFdType; #endif -#ifdef ERTS_INLINE -# ifndef ERTS_CAN_INLINE -# define ERTS_CAN_INLINE 1 -# endif -#else -# if defined(__GNUC__) -# define ERTS_CAN_INLINE 1 -# define ERTS_INLINE __inline__ -# elif defined(__WIN32__) -# define ERTS_CAN_INLINE 1 -# define ERTS_INLINE __inline -# else -# define ERTS_CAN_INLINE 0 -# define ERTS_INLINE -# endif -#endif - #if !defined(__GNUC__) # define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) 0 #elif !defined(__GNUC_MINOR__) -- cgit v1.2.3 From d80f10c1586718fc0097fa156fd6e9f8ec0f6597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Jun 2015 10:56:57 +0200 Subject: erts: Refactor LCNT --- erts/emulator/beam/erl_bif_info.c | 80 +++----- erts/emulator/beam/erl_lock_count.c | 348 +++++++++++++++++----------------- erts/emulator/beam/erl_lock_count.h | 42 ++-- erts/emulator/beam/erl_process_lock.c | 186 +++++++----------- erts/emulator/beam/io.c | 54 +++--- 5 files changed, 321 insertions(+), 389 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 397c68e199..8a675302b2 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -4296,59 +4296,41 @@ BIF_RETTYPE erts_debug_lock_counters_1(BIF_ALIST_1) BIF_RET(am_ok); } else if (is_tuple(BIF_ARG_1)) { - Eterm* tp = tuple_val(BIF_ARG_1); + Eterm* ptr = tuple_val(BIF_ARG_1); + + if ((arityval(ptr[0]) == 2) && (ptr[2] == am_false || ptr[2] == am_true)) { + int lock_opt = 0, enable = (ptr[2] == am_true) ? 1 : 0; + if (ERTS_IS_ATOM_STR("copy_save", ptr[1])) { + lock_opt = ERTS_LCNT_OPT_COPYSAVE; + } else if (ERTS_IS_ATOM_STR("process_locks", ptr[1])) { + lock_opt = ERTS_LCNT_OPT_PROCLOCK; + } else if (ERTS_IS_ATOM_STR("port_locks", ptr[1])) { + lock_opt = ERTS_LCNT_OPT_PORTLOCK; + } else if (ERTS_IS_ATOM_STR("suspend", ptr[1])) { + lock_opt = ERTS_LCNT_OPT_SUSPEND; + } else if (ERTS_IS_ATOM_STR("location", ptr[1])) { + lock_opt = ERTS_LCNT_OPT_LOCATION; + } else { + BIF_ERROR(BIF_P, BADARG); + } - switch (arityval(tp[0])) { - case 2: { - int opt = 0; - int val = 0; - if (ERTS_IS_ATOM_STR("copy_save", tp[1])) { - opt = ERTS_LCNT_OPT_COPYSAVE; - } else if (ERTS_IS_ATOM_STR("process_locks", tp[1])) { - opt = ERTS_LCNT_OPT_PROCLOCK; - } else if (ERTS_IS_ATOM_STR("port_locks", tp[1])) { - opt = ERTS_LCNT_OPT_PORTLOCK; - } else if (ERTS_IS_ATOM_STR("suspend", tp[1])) { - opt = ERTS_LCNT_OPT_SUSPEND; - } else if (ERTS_IS_ATOM_STR("location", tp[1])) { - opt = ERTS_LCNT_OPT_LOCATION; - } else { - BIF_ERROR(BIF_P, BADARG); - } - if (tp[2] == am_true) { - val = 1; - } else if (tp[2] == am_false) { - val = 0; - } else { - BIF_ERROR(BIF_P, BADARG); - } + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); - - if (val) { - res = erts_lcnt_set_rt_opt(opt) ? am_true : am_false; - } else { - res = erts_lcnt_clear_rt_opt(opt) ? am_true : am_false; - } + if (enable) res = erts_lcnt_set_rt_opt(lock_opt) ? am_true : am_false; + else res = erts_lcnt_clear_rt_opt(lock_opt) ? am_true : am_false; + #ifdef ERTS_SMP - if (res != tp[2]) { - if (opt == ERTS_LCNT_OPT_PORTLOCK) { - erts_lcnt_enable_io_lock_count(val); - } else if (opt == ERTS_LCNT_OPT_PROCLOCK) { - erts_lcnt_enable_proc_lock_count(val); - } - } + if (res != ptr[2] && lock_opt == ERTS_LCNT_OPT_PORTLOCK) { + erts_lcnt_enable_io_lock_count(enable); + } else if (res != ptr[2] && lock_opt == ERTS_LCNT_OPT_PROCLOCK) { + erts_lcnt_enable_proc_lock_count(enable); + } #endif - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_RET(res); - break; - } - - default: - break; - } + erts_smp_thr_progress_unblock(); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + BIF_RET(res); + } } #endif diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index c6d8f4df95..c4956d8569 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -20,7 +20,7 @@ /* * Description: Statistics for locks. * - * Author: Björn-Egil Dahlberg + * Author: Björn-Egil Dahlberg * Date: 2008-07-03 */ @@ -49,7 +49,7 @@ const char *str_undefined = "undefined"; static ethr_tsd_key lcnt_thr_data_key; static int lcnt_n_thr; -static erts_lcnt_thread_data_t *lcnt_thread_data[4096]; +static erts_lcnt_thread_data_t *lcnt_thread_data[2048]; /* local functions */ @@ -83,12 +83,12 @@ static ERTS_INLINE int lcnt_log2(Uint64 v) { static char* lcnt_lock_type(Uint16 flag) { switch(flag & ERTS_LCNT_LT_ALL) { - case ERTS_LCNT_LT_SPINLOCK: return "spinlock"; - case ERTS_LCNT_LT_RWSPINLOCK: return "rw_spinlock"; - case ERTS_LCNT_LT_MUTEX: return "mutex"; - case ERTS_LCNT_LT_RWMUTEX: return "rw_mutex"; - case ERTS_LCNT_LT_PROCLOCK: return "proclock"; - default: return ""; + case ERTS_LCNT_LT_SPINLOCK: return "spinlock"; + case ERTS_LCNT_LT_RWSPINLOCK: return "rw_spinlock"; + case ERTS_LCNT_LT_MUTEX: return "mutex"; + case ERTS_LCNT_LT_RWMUTEX: return "rw_mutex"; + case ERTS_LCNT_LT_PROCLOCK: return "proclock"; + default: return ""; } } @@ -116,15 +116,15 @@ static void lcnt_time(erts_lcnt_time_t *time) { static void lcnt_time_diff(erts_lcnt_time_t *d, erts_lcnt_time_t *t1, erts_lcnt_time_t *t0) { long ds; long dns; - + ds = t1->s - t0->s; dns = t1->ns - t0->ns; - + /* the difference should not be able to get bigger than 1 sec in ns*/ - + if (dns < 0) { - ds -= 1; - dns += 1000000000LL; + ds -= 1; + dns += 1000000000LL; } ASSERT(ds >= 0); @@ -145,7 +145,7 @@ static void lcnt_time_add(erts_lcnt_time_t *t, erts_lcnt_time_t *d) { static erts_lcnt_thread_data_t *lcnt_thread_data_alloc(void) { erts_lcnt_thread_data_t *eltd; - + eltd = (erts_lcnt_thread_data_t*)malloc(sizeof(erts_lcnt_thread_data_t)); if (!eltd) { ERTS_INTERNAL_ERROR("Lock counter failed to allocate memory!"); @@ -164,7 +164,6 @@ static erts_lcnt_thread_data_t *lcnt_get_thread_data(void) { return (erts_lcnt_thread_data_t *)ethr_tsd_get(lcnt_thr_data_key); } - /* debug */ #if 0 @@ -183,15 +182,15 @@ static void print_lock_x(erts_lcnt_lock_t *lock, Uint16 flag, char *action) { type = lcnt_lock_type(lock->flag); r_state = ethr_atomic_read(&lock->r_state); w_state = ethr_atomic_read(&lock->w_state); - + if (lock->flag & flag) { erts_fprintf(stderr,"%10s [%24s] [r/w state %4ld/%4ld] %2s id %T\r\n", - action, - lock->name, - r_state, - w_state, - type, - lock->id); + action, + lock->name, + r_state, + w_state, + type, + lock->id); } } #endif @@ -201,18 +200,18 @@ static erts_lcnt_lock_stats_t *lcnt_get_lock_stats(erts_lcnt_lock_t *lock, char erts_lcnt_lock_stats_t *stats = NULL; if (erts_lcnt_rt_options & ERTS_LCNT_OPT_LOCATION) { - for (i = 0; i < lock->n_stats; i++) { - if ((lock->stats[i].file == file) && (lock->stats[i].line == line)) { - return &(lock->stats[i]); - } - } - if (lock->n_stats < ERTS_LCNT_MAX_LOCK_LOCATIONS) { - stats = &lock->stats[lock->n_stats]; - lock->n_stats++; - stats->file = file; - stats->line = line; - return stats; - } + for (i = 0; i < lock->n_stats; i++) { + if ((lock->stats[i].file == file) && (lock->stats[i].line == line)) { + return &(lock->stats[i]); + } + } + if (lock->n_stats < ERTS_LCNT_MAX_LOCK_LOCATIONS) { + stats = &lock->stats[lock->n_stats]; + lock->n_stats++; + stats->file = file; + stats->line = line; + return stats; + } } return &lock->stats[0]; } @@ -222,53 +221,48 @@ static void lcnt_update_stats_hist(erts_lcnt_hist_t *hist, erts_lcnt_time_t *tim unsigned long r; if (time_wait->s > 0 || time_wait->ns > ERTS_LCNT_HISTOGRAM_MAX_NS) { - idx = ERTS_LCNT_HISTOGRAM_SLOT_SIZE - 1; + idx = ERTS_LCNT_HISTOGRAM_SLOT_SIZE - 1; } else { - r = time_wait->ns >> ERTS_LCNT_HISTOGRAM_RSHIFT; - if (r) idx = lcnt_log2(r); - else idx = 0; + r = time_wait->ns >> ERTS_LCNT_HISTOGRAM_RSHIFT; + if (r) idx = lcnt_log2(r); + else idx = 0; } hist->ns[idx]++; } static void lcnt_update_stats(erts_lcnt_lock_stats_t *stats, int lock_in_conflict, - erts_lcnt_time_t *time_wait) { - + erts_lcnt_time_t *time_wait) { + ethr_atomic_inc(&stats->tries); if (lock_in_conflict) - ethr_atomic_inc(&stats->colls); + ethr_atomic_inc(&stats->colls); if (time_wait) { - lcnt_time_add(&(stats->timer), time_wait); - stats->timer_n++; - lcnt_update_stats_hist(&stats->hist,time_wait); + lcnt_time_add(&(stats->timer), time_wait); + stats->timer_n++; + lcnt_update_stats_hist(&stats->hist,time_wait); } } -/* - * interface - */ +/* interface */ void erts_lcnt_init() { erts_lcnt_thread_data_t *eltd = NULL; - + /* init lock */ if (ethr_mutex_init(&lcnt_data_lock) != 0) abort(); /* init tsd */ lcnt_n_thr = 0; - ethr_tsd_key_create(&lcnt_thr_data_key,"lcnt_data"); lcnt_lock(); - erts_lcnt_rt_options = ERTS_LCNT_OPT_PROCLOCK | ERTS_LCNT_OPT_LOCATION; - + erts_lcnt_rt_options = ERTS_LCNT_OPT_LOCATION | ERTS_LCNT_OPT_PROCLOCK; eltd = lcnt_thread_data_alloc(); - ethr_tsd_set(lcnt_thr_data_key, eltd); - + /* init lcnt structure */ erts_lcnt_data = (erts_lcnt_data_t*)malloc(sizeof(erts_lcnt_data_t)); if (!erts_lcnt_data) { @@ -293,7 +287,7 @@ void erts_lcnt_late_init() { erts_lcnt_lock_list_t *erts_lcnt_list_init(void) { erts_lcnt_lock_list_t *list; - + list = (erts_lcnt_lock_list_t*)malloc(sizeof(erts_lcnt_lock_list_t)); if (!list) { ERTS_INTERNAL_ERROR("Lock counter failed to allocate memory!"); @@ -307,14 +301,14 @@ erts_lcnt_lock_list_t *erts_lcnt_list_init(void) { /* only do this on the list with the deleted locks! */ void erts_lcnt_list_clear(erts_lcnt_lock_list_t *list) { erts_lcnt_lock_t *lock = NULL, - *next = NULL; + *next = NULL; lock = list->head; - + while(lock != NULL) { - next = lock->next; - free(lock); - lock = next; + next = lock->next; + free(lock); + lock = next; } list->head = NULL; @@ -327,26 +321,25 @@ void erts_lcnt_list_insert(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock) tail = list->tail; if (tail) { - tail->next = lock; - lock->prev = tail; + tail->next = lock; + lock->prev = tail; } else { - list->head = lock; - lock->prev = NULL; - ASSERT(!lock->next); + list->head = lock; + lock->prev = NULL; + ASSERT(!lock->next); } lock->next = NULL; list->tail = lock; - + list->n++; } void erts_lcnt_list_delete(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock) { - if (lock->next) lock->next->prev = lock->prev; if (lock->prev) lock->prev->next = lock->next; if (list->head == lock) list->head = lock->next; if (list->tail == lock) list->tail = lock->prev; - + lock->prev = NULL; lock->next = NULL; list->n--; @@ -364,12 +357,9 @@ void erts_lcnt_init_lock(erts_lcnt_lock_t *lock, char *name, Uint16 flag ) { void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id) { int i; - if (!name) { - lock->flag = 0; - return; - } + if (name == NULL) { ERTS_LCNT_CLEAR_FLAG(lock); return; } lcnt_lock(); - + lock->next = NULL; lock->prev = NULL; lock->flag = flag; @@ -378,46 +368,55 @@ void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eter ethr_atomic_init(&lock->r_state, 0); ethr_atomic_init(&lock->w_state, 0); - #ifdef DEBUG ethr_atomic_init(&lock->flowstate, 0); #endif - + lock->n_stats = 1; for (i = 0; i < ERTS_LCNT_MAX_LOCK_LOCATIONS; i++) { - lcnt_clear_stats(&lock->stats[i]); + lcnt_clear_stats(&lock->stats[i]); } erts_lcnt_list_insert(erts_lcnt_data->current_locks, lock); lcnt_unlock(); } - +/* init empty, instead of zero struct */ +void erts_lcnt_init_lock_empty(erts_lcnt_lock_t *lock) { + lock->next = NULL; + lock->prev = NULL; + lock->flag = 0; + lock->name = NULL; + lock->id = NIL; + ethr_atomic_init(&lock->r_state, 0); + ethr_atomic_init(&lock->w_state, 0); +#ifdef DEBUG + ethr_atomic_init(&lock->flowstate, 0); +#endif + lock->n_stats = 0; + sys_memzero(lock->stats, sizeof(lock->stats)); +} +/* destroy lock */ void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) { - erts_lcnt_lock_t *deleted_lock; - - if (!ERTS_LCNT_LOCK_TYPE(lock)) return; - + if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return; lcnt_lock(); if (erts_lcnt_rt_options & ERTS_LCNT_OPT_COPYSAVE) { - /* copy structure and insert the copy */ - - deleted_lock = (erts_lcnt_lock_t*)malloc(sizeof(erts_lcnt_lock_t)); + erts_lcnt_lock_t *deleted_lock; + /* copy structure and insert the copy */ + deleted_lock = (erts_lcnt_lock_t*)malloc(sizeof(erts_lcnt_lock_t)); if (!deleted_lock) { ERTS_INTERNAL_ERROR("Lock counter failed to allocate memory!"); } - memcpy(deleted_lock, lock, sizeof(erts_lcnt_lock_t)); - - deleted_lock->next = NULL; - deleted_lock->prev = NULL; - - erts_lcnt_list_insert(erts_lcnt_data->deleted_locks, deleted_lock); + memcpy(deleted_lock, lock, sizeof(erts_lcnt_lock_t)); + deleted_lock->next = NULL; + deleted_lock->prev = NULL; + erts_lcnt_list_insert(erts_lcnt_data->deleted_locks, deleted_lock); } /* delete original */ erts_lcnt_list_delete(erts_lcnt_data->current_locks, lock); - lock->flag = 0; - + ERTS_LCNT_CLEAR_FLAG(lock); + lcnt_unlock(); } @@ -426,16 +425,15 @@ void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) { void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option) { erts_aint_t r_state = 0, w_state = 0; erts_lcnt_thread_data_t *eltd; - + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; - if (!ERTS_LCNT_LOCK_TYPE(lock)) return; + if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return; eltd = lcnt_get_thread_data(); - ASSERT(eltd); - + w_state = ethr_atomic_read(&lock->w_state); - + if (option & ERTS_LCNT_LO_WRITE) { r_state = ethr_atomic_read(&lock->r_state); ethr_atomic_inc( &lock->w_state); @@ -443,48 +441,47 @@ void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option) { if (option & ERTS_LCNT_LO_READ) { ethr_atomic_inc( &lock->r_state); } - + /* we cannot acquire w_lock if either w or r are taken */ /* we cannot acquire r_lock if w_lock is taken */ - + if ((w_state > 0) || (r_state > 0)) { - eltd->lock_in_conflict = 1; - if (eltd->timer_set == 0) { - lcnt_time(&eltd->timer); - } - eltd->timer_set++; + eltd->lock_in_conflict = 1; + if (eltd->timer_set == 0) { + lcnt_time(&eltd->timer); + } + eltd->timer_set++; } else { - eltd->lock_in_conflict = 0; + eltd->lock_in_conflict = 0; } } void erts_lcnt_lock(erts_lcnt_lock_t *lock) { erts_aint_t w_state; erts_lcnt_thread_data_t *eltd; - + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; - if (!ERTS_LCNT_LOCK_TYPE(lock)) return; + if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return; w_state = ethr_atomic_read(&lock->w_state); ethr_atomic_inc(&lock->w_state); - eltd = lcnt_get_thread_data(); ASSERT(eltd); if (w_state > 0) { - eltd->lock_in_conflict = 1; - /* only set the timer if nobody else has it - * This should only happen when proc_locks aquires several locks - * 'atomicly'. All other locks will block the thread if w_state > 0 - * i.e. locked. - */ - if (eltd->timer_set == 0) { - lcnt_time(&eltd->timer); - } - eltd->timer_set++; + eltd->lock_in_conflict = 1; + /* only set the timer if nobody else has it + * This should only happen when proc_locks aquires several locks + * 'atomicly'. All other locks will block the thread if w_state > 0 + * i.e. locked. + */ + if (eltd->timer_set == 0) { + lcnt_time(&eltd->timer); + } + eltd->timer_set++; } else { - eltd->lock_in_conflict = 0; + eltd->lock_in_conflict = 0; } } @@ -493,16 +490,19 @@ void erts_lcnt_lock(erts_lcnt_lock_t *lock) { void erts_lcnt_lock_unaquire(erts_lcnt_lock_t *lock) { /* should check if this thread was "waiting" */ if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; - if (!ERTS_LCNT_LOCK_TYPE(lock)) return; + if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return; ethr_atomic_dec(&lock->w_state); } -/* erts_lcnt_lock_post - * used when we get a lock (i.e. directly after a lock operation) +/* + * erts_lcnt_lock_post + * + * Used when we get a lock (i.e. directly after a lock operation) * if the timer was set then we had to wait for the lock * lock_post will calculate the wait time. */ + void erts_lcnt_lock_post(erts_lcnt_lock_t *lock) { erts_lcnt_lock_post_x(lock, (char*)str_undefined, 0); } @@ -517,31 +517,31 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; - if (!ERTS_LCNT_LOCK_TYPE(lock)) return; - + if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return; + #ifdef DEBUG if (!(lock->flag & (ERTS_LCNT_LT_RWMUTEX | ERTS_LCNT_LT_RWSPINLOCK))) { - flowstate = ethr_atomic_read(&lock->flowstate); - ASSERT(flowstate == 0); - ethr_atomic_inc(&lock->flowstate); + flowstate = ethr_atomic_read(&lock->flowstate); + ASSERT(flowstate == 0); + ethr_atomic_inc(&lock->flowstate); } #endif - + eltd = lcnt_get_thread_data(); - + ASSERT(eltd); /* if lock was in conflict, time it */ stats = lcnt_get_lock_stats(lock, file, line); if (eltd->timer_set) { - lcnt_time(&timer); - - lcnt_time_diff(&time_wait, &timer, &(eltd->timer)); - lcnt_update_stats(stats, eltd->lock_in_conflict, &time_wait); - eltd->timer_set--; - ASSERT(eltd->timer_set >= 0); + lcnt_time(&timer); + + lcnt_time_diff(&time_wait, &timer, &(eltd->timer)); + lcnt_update_stats(stats, eltd->lock_in_conflict, &time_wait); + eltd->timer_set--; + ASSERT(eltd->timer_set >= 0); } else { - lcnt_update_stats(stats, eltd->lock_in_conflict, NULL); + lcnt_update_stats(stats, eltd->lock_in_conflict, NULL); } } @@ -550,27 +550,28 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line void erts_lcnt_unlock_opt(erts_lcnt_lock_t *lock, Uint16 option) { if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; - if (!ERTS_LCNT_LOCK_TYPE(lock)) return; + if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return; if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_dec(&lock->w_state); if (option & ERTS_LCNT_LO_READ ) ethr_atomic_dec(&lock->r_state); } void erts_lcnt_unlock(erts_lcnt_lock_t *lock) { -#ifdef DEBUG - erts_aint_t w_state; - erts_aint_t flowstate; -#endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; - if (!ERTS_LCNT_LOCK_TYPE(lock)) return; + if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return; #ifdef DEBUG - /* flowstate */ - flowstate = ethr_atomic_read(&lock->flowstate); - ASSERT(flowstate == 1); - ethr_atomic_dec(&lock->flowstate); - - /* write state */ - w_state = ethr_atomic_read(&lock->w_state); - ASSERT(w_state > 0); + { + erts_aint_t w_state; + erts_aint_t flowstate; + + /* flowstate */ + flowstate = ethr_atomic_read(&lock->flowstate); + ASSERT(flowstate == 1); + ethr_atomic_dec(&lock->flowstate); + + /* write state */ + w_state = ethr_atomic_read(&lock->w_state); + ASSERT(w_state > 0); + } #endif ethr_atomic_dec(&lock->w_state); } @@ -579,35 +580,34 @@ void erts_lcnt_unlock(erts_lcnt_lock_t *lock) { void erts_lcnt_trylock_opt(erts_lcnt_lock_t *lock, int res, Uint16 option) { if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; - if (!ERTS_LCNT_LOCK_TYPE(lock)) return; + if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return; /* Determine lock_state via res instead of state */ if (res != EBUSY) { - if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_inc(&lock->w_state); - if (option & ERTS_LCNT_LO_READ ) ethr_atomic_inc(&lock->r_state); - lcnt_update_stats(&(lock->stats[0]), 0, NULL); + if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_inc(&lock->w_state); + if (option & ERTS_LCNT_LO_READ ) ethr_atomic_inc(&lock->r_state); + lcnt_update_stats(&(lock->stats[0]), 0, NULL); } else { ethr_atomic_inc(&lock->stats[0].tries); ethr_atomic_inc(&lock->stats[0].colls); } } - + void erts_lcnt_trylock(erts_lcnt_lock_t *lock, int res) { /* Determine lock_state via res instead of state */ -#ifdef DEBUG - erts_aint_t flowstate; -#endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; - if (!ERTS_LCNT_LOCK_TYPE(lock)) return; + if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return; if (res != EBUSY) { - #ifdef DEBUG - flowstate = ethr_atomic_read(&lock->flowstate); - ASSERT(flowstate == 0); - ethr_atomic_inc( &lock->flowstate); + { + erts_aint_t flowstate; + flowstate = ethr_atomic_read(&lock->flowstate); + ASSERT(flowstate == 0); + ethr_atomic_inc( &lock->flowstate); + } #endif - ethr_atomic_inc(&lock->w_state); - lcnt_update_stats(&(lock->stats[0]), 0, NULL); + ethr_atomic_inc(&lock->w_state); + lcnt_update_stats(&(lock->stats[0]), 0, NULL); } else { ethr_atomic_inc(&lock->stats[0].tries); ethr_atomic_inc(&lock->stats[0].colls); @@ -662,13 +662,13 @@ void erts_lcnt_clear_counters(void) { lcnt_lock(); list = erts_lcnt_data->current_locks; - + for (lock = list->head; lock != NULL; lock = lock->next) { - for( i = 0; i < ERTS_LCNT_MAX_LOCK_LOCATIONS; i++) { - stats = &lock->stats[i]; - lcnt_clear_stats(stats); - } - lock->n_stats = 1; + for( i = 0; i < ERTS_LCNT_MAX_LOCK_LOCATIONS; i++) { + stats = &lock->stats[i]; + lcnt_clear_stats(stats); + } + lock->n_stats = 1; } /* empty deleted locks in lock list */ @@ -681,14 +681,14 @@ void erts_lcnt_clear_counters(void) { erts_lcnt_data_t *erts_lcnt_get_data(void) { erts_lcnt_time_t timer_stop; - + lcnt_lock(); - + lcnt_time(&timer_stop); lcnt_time_diff(&(erts_lcnt_data->duration), &timer_stop, &timer_start); - + lcnt_unlock(); - + return erts_lcnt_data; } diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h index 09fadd7e9e..051f55271d 100644 --- a/erts/emulator/beam/erl_lock_count.h +++ b/erts/emulator/beam/erl_lock_count.h @@ -20,7 +20,7 @@ /* * Description: Statistics for locks. * - * Author: Björn-Egil Dahlberg + * Author: Björn-Egil Dahlberg * Date: 2008-07-03 * Abstract: * Locks statistics internal representation. @@ -95,30 +95,35 @@ #define ERTS_LCNT_LO_WRITE (((Uint16) 1) << 7) #define ERTS_LCNT_LO_READ_WRITE ( ERTS_LCNT_LO_READ \ - | ERTS_LCNT_LO_WRITE ) + | ERTS_LCNT_LO_WRITE ) #define ERTS_LCNT_LT_ALL ( ERTS_LCNT_LT_SPINLOCK \ - | ERTS_LCNT_LT_RWSPINLOCK \ - | ERTS_LCNT_LT_MUTEX \ - | ERTS_LCNT_LT_RWMUTEX \ - | ERTS_LCNT_LT_PROCLOCK ) + | ERTS_LCNT_LT_RWSPINLOCK \ + | ERTS_LCNT_LT_MUTEX \ + | ERTS_LCNT_LT_RWMUTEX \ + | ERTS_LCNT_LT_PROCLOCK ) + +#define ERTS_LCNT_LOCK_TYPE(lock) ((lock)->flag & ERTS_LCNT_LT_ALL) +#define ERTS_LCNT_IS_LOCK_INVALID(lock) (!((lock)->flag & ERTS_LCNT_LT_ALL)) +#define ERTS_LCNT_CLEAR_FLAG(lock) ((lock)->flag = 0) + /* runtime options */ -#define ERTS_LCNT_OPT_SUSPEND (((Uint16) 1) << 0) -#define ERTS_LCNT_OPT_LOCATION (((Uint16) 1) << 1) -#define ERTS_LCNT_OPT_PROCLOCK (((Uint16) 1) << 2) -#define ERTS_LCNT_OPT_COPYSAVE (((Uint16) 1) << 3) -#define ERTS_LCNT_OPT_PORTLOCK (((Uint16) 1) << 4) +#define ERTS_LCNT_OPT_SUSPEND (((Uint16) 1) << 0) +#define ERTS_LCNT_OPT_LOCATION (((Uint16) 1) << 1) +#define ERTS_LCNT_OPT_PROCLOCK (((Uint16) 1) << 2) +#define ERTS_LCNT_OPT_PORTLOCK (((Uint16) 1) << 3) +#define ERTS_LCNT_OPT_COPYSAVE (((Uint16) 1) << 4) typedef struct { unsigned long s; unsigned long ns; } erts_lcnt_time_t; - + extern erts_lcnt_time_t timer_start; typedef struct { - Uint32 ns[ERTS_LCNT_HISTOGRAM_SLOT_SIZE]; /* log2 array of nano seconds occurences */ + Uint32 ns[ERTS_LCNT_HISTOGRAM_SLOT_SIZE]; /* log2 array of nano seconds occurences */ } erts_lcnt_hist_t; typedef struct erts_lcnt_lock_stats_s { @@ -129,10 +134,10 @@ typedef struct erts_lcnt_lock_stats_s { char *file; /* which file the lock was taken */ unsigned int line; /* line number in file */ - + ethr_atomic_t tries; /* n tries to get lock */ ethr_atomic_t colls; /* n collisions of tries to get lock */ - + unsigned long timer_n; /* #times waited for lock */ erts_lcnt_time_t timer; /* total wait time for lock */ erts_lcnt_hist_t hist; @@ -155,7 +160,7 @@ typedef struct erts_lcnt_lock_s { /* statistics */ unsigned int n_stats; erts_lcnt_lock_stats_t stats[ERTS_LCNT_MAX_LOCK_LOCATIONS]; /* first entry is "undefined"*/ - + /* chains for list handling */ /* data is hold by lcnt_lock */ struct erts_lcnt_lock_s *prev; @@ -167,7 +172,7 @@ typedef struct { erts_lcnt_lock_t *tail; unsigned long n; } erts_lcnt_lock_list_t; - + typedef struct { erts_lcnt_time_t duration; /* time since last clear */ erts_lcnt_lock_list_t *current_locks; @@ -205,6 +210,7 @@ void erts_lcnt_list_delete(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock); /* lock operations (global) */ void erts_lcnt_init_lock(erts_lcnt_lock_t *lock, char *name, Uint16 flag); void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id); +void erts_lcnt_init_lock_empty(erts_lcnt_lock_t *lock); void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock); void erts_lcnt_lock(erts_lcnt_lock_t *lock); @@ -226,7 +232,5 @@ void erts_lcnt_clear_counters(void); char *erts_lcnt_lock_type(Uint16 type); erts_lcnt_data_t *erts_lcnt_get_data(void); -#define ERTS_LCNT_LOCK_TYPE(lockp) ((lockp)->flag & ERTS_LCNT_LT_ALL) - #endif /* ifdef ERTS_ENABLE_LOCK_COUNT */ #endif /* ifndef ERTS_LOCK_COUNT_H__ */ diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index b28bc498f6..0548c6137c 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -110,6 +110,9 @@ static struct { erts_pix_lock_t erts_pix_locks[ERTS_NO_OF_PIX_LOCKS]; +#ifdef ERTS_ENABLE_LOCK_COUNT +static void lcnt_enable_proc_lock_count(Process *proc, int enable); +#endif void erts_init_proc_lock(int cpus) @@ -1083,30 +1086,32 @@ erts_proc_lock_fin(Process *p) /* --- Process lock counting ----------------------------------------------- */ #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->common.id != ERTS_INVALID_PID) { - erts_lcnt_init_lock_x(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK, p->common.id); - erts_lcnt_init_lock_x(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK, p->common.id); - erts_lcnt_init_lock_x(&(p->lock.lcnt_btm), "proc_btm", ERTS_LCNT_LT_PROCLOCK, p->common.id); - erts_lcnt_init_lock_x(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK, p->common.id); - erts_lcnt_init_lock_x(&(p->lock.lcnt_status), "proc_status", ERTS_LCNT_LT_PROCLOCK, p->common.id); - } else { - erts_lcnt_init_lock(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK); - erts_lcnt_init_lock(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK); - erts_lcnt_init_lock(&(p->lock.lcnt_btm), "proc_btm", ERTS_LCNT_LT_PROCLOCK); - erts_lcnt_init_lock(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK); - erts_lcnt_init_lock(&(p->lock.lcnt_status), "proc_status", ERTS_LCNT_LT_PROCLOCK); - } - } else { - sys_memzero(&(p->lock.lcnt_main), sizeof(p->lock.lcnt_main)); - sys_memzero(&(p->lock.lcnt_msgq), sizeof(p->lock.lcnt_msgq)); - sys_memzero(&(p->lock.lcnt_btm), sizeof(p->lock.lcnt_btm)); - sys_memzero(&(p->lock.lcnt_link), sizeof(p->lock.lcnt_link)); - sys_memzero(&(p->lock.lcnt_status), sizeof(p->lock.lcnt_status)); - } + +void erts_lcnt_enable_proc_lock_count(int enable) { + int ix, max = erts_ptab_max(&erts_proc); + Process *proc = NULL; + for (ix = 0; ix < max; ++ix) { + if ((proc = erts_pix2proc(ix)) != NULL) + lcnt_enable_proc_lock_count(proc, enable); + } /* for all processes */ } - + +void erts_lcnt_proc_lock_init(Process *p) { + if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) { + erts_lcnt_init_lock_empty(&(p->lock.lcnt_main)); + erts_lcnt_init_lock_empty(&(p->lock.lcnt_msgq)); + erts_lcnt_init_lock_empty(&(p->lock.lcnt_btm)); + erts_lcnt_init_lock_empty(&(p->lock.lcnt_link)); + erts_lcnt_init_lock_empty(&(p->lock.lcnt_status)); + } else { /* now the common case */ + Eterm pid = (p->common.id != ERTS_INVALID_PID) ? p->common.id : NIL; + erts_lcnt_init_lock_x(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK, pid); + erts_lcnt_init_lock_x(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK, pid); + erts_lcnt_init_lock_x(&(p->lock.lcnt_btm), "proc_btm", ERTS_LCNT_LT_PROCLOCK, pid); + erts_lcnt_init_lock_x(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK, pid); + erts_lcnt_init_lock_x(&(p->lock.lcnt_status),"proc_status",ERTS_LCNT_LT_PROCLOCK, pid); + } /* the lock names should really be aligned to four characters */ +} /* logic reversed */ void erts_lcnt_proc_lock_destroy(Process *p) { erts_lcnt_destroy_lock(&(p->lock.lcnt_main)); @@ -1116,28 +1121,31 @@ void erts_lcnt_proc_lock_destroy(Process *p) { erts_lcnt_destroy_lock(&(p->lock.lcnt_status)); } -void erts_lcnt_proc_lock(erts_proc_lock_t *lock, ErtsProcLocks locks) { - if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { - if (locks & ERTS_PROC_LOCK_MAIN) { - erts_lcnt_lock(&(lock->lcnt_main)); - } - if (locks & ERTS_PROC_LOCK_MSGQ) { - erts_lcnt_lock(&(lock->lcnt_msgq)); - } - if (locks & ERTS_PROC_LOCK_BTM) { - erts_lcnt_lock(&(lock->lcnt_btm)); - } - if (locks & ERTS_PROC_LOCK_LINK) { - erts_lcnt_lock(&(lock->lcnt_link)); - } - if (locks & ERTS_PROC_LOCK_STATUS) { - erts_lcnt_lock(&(lock->lcnt_status)); +static void lcnt_enable_proc_lock_count(Process *proc, int enable) { + if (enable) { + if (!ERTS_LCNT_LOCK_TYPE(&(proc->lock.lcnt_main))) { + erts_lcnt_proc_lock_init(proc); + } } + else { + if (ERTS_LCNT_LOCK_TYPE(&(proc->lock.lcnt_main))) { + erts_lcnt_proc_lock_destroy(proc); + } } } -void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks, char *file, unsigned int line) { - if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { +void erts_lcnt_proc_lock(erts_proc_lock_t *lock, ErtsProcLocks locks) { + if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return; + if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_lock(&(lock->lcnt_main)); } + if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_lock(&(lock->lcnt_msgq)); } + if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_lock(&(lock->lcnt_btm)); } + if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_lock(&(lock->lcnt_link)); } + if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_lock(&(lock->lcnt_status)); } +} + +void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks, + char *file, unsigned int line) { + if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return; if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_lock_post_x(&(lock->lcnt_main), file, line); } @@ -1153,90 +1161,34 @@ void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks, cha if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_lock_post_x(&(lock->lcnt_status), file, line); } - } } void erts_lcnt_proc_lock_unaquire(erts_proc_lock_t *lock, ErtsProcLocks locks) { - if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { - if (locks & ERTS_PROC_LOCK_MAIN) { - erts_lcnt_lock_unaquire(&(lock->lcnt_main)); - } - if (locks & ERTS_PROC_LOCK_MSGQ) { - erts_lcnt_lock_unaquire(&(lock->lcnt_msgq)); - } - if (locks & ERTS_PROC_LOCK_BTM) { - erts_lcnt_lock_unaquire(&(lock->lcnt_btm)); - } - if (locks & ERTS_PROC_LOCK_LINK) { - erts_lcnt_lock_unaquire(&(lock->lcnt_link)); - } - if (locks & ERTS_PROC_LOCK_STATUS) { - erts_lcnt_lock_unaquire(&(lock->lcnt_status)); - } - } + if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return; + if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_lock_unaquire(&(lock->lcnt_main)); } + if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_lock_unaquire(&(lock->lcnt_msgq)); } + if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_lock_unaquire(&(lock->lcnt_btm)); } + if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_lock_unaquire(&(lock->lcnt_link)); } + if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_lock_unaquire(&(lock->lcnt_status)); } } void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks) { - if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { - if (locks & ERTS_PROC_LOCK_MAIN) { - erts_lcnt_unlock(&(lock->lcnt_main)); - } - if (locks & ERTS_PROC_LOCK_MSGQ) { - erts_lcnt_unlock(&(lock->lcnt_msgq)); - } - if (locks & ERTS_PROC_LOCK_BTM) { - erts_lcnt_unlock(&(lock->lcnt_btm)); - } - if (locks & ERTS_PROC_LOCK_LINK) { - erts_lcnt_unlock(&(lock->lcnt_link)); - } - if (locks & ERTS_PROC_LOCK_STATUS) { - erts_lcnt_unlock(&(lock->lcnt_status)); - } - } + if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return; + if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_unlock(&(lock->lcnt_main)); } + if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_unlock(&(lock->lcnt_msgq)); } + if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_unlock(&(lock->lcnt_btm)); } + if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_unlock(&(lock->lcnt_link)); } + if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_unlock(&(lock->lcnt_status)); } } void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res) { - if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { - if (locks & ERTS_PROC_LOCK_MAIN) { - erts_lcnt_trylock(&(lock->lcnt_main), res); - } - if (locks & ERTS_PROC_LOCK_MSGQ) { - erts_lcnt_trylock(&(lock->lcnt_msgq), res); - } - if (locks & ERTS_PROC_LOCK_BTM) { - erts_lcnt_trylock(&(lock->lcnt_btm), res); - } - if (locks & ERTS_PROC_LOCK_LINK) { - erts_lcnt_trylock(&(lock->lcnt_link), res); - } - if (locks & ERTS_PROC_LOCK_STATUS) { - erts_lcnt_trylock(&(lock->lcnt_status), res); - } - } -} - - -void erts_lcnt_enable_proc_lock_count(int enable) -{ - int i, max = erts_ptab_max(&erts_proc); - - for (i = 0; i < max; ++i) { - Process* p = erts_pix2proc(i); - if (p) { - if (enable) { - if (!ERTS_LCNT_LOCK_TYPE(&(p->lock.lcnt_main))) { - erts_lcnt_proc_lock_init(p); - } - } else { - if (ERTS_LCNT_LOCK_TYPE(&(p->lock.lcnt_main))) { - erts_lcnt_proc_lock_destroy(p); - } - } - } - } -} - -#endif /* ifdef ERTS_ENABLE_LOCK_COUNT */ + if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return; + if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_trylock(&(lock->lcnt_main), res); } + if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_trylock(&(lock->lcnt_msgq), res); } + if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_trylock(&(lock->lcnt_btm), res); } + if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_trylock(&(lock->lcnt_link), res); } + if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_trylock(&(lock->lcnt_status), res); } +} /* reversed logic */ +#endif /* ERTS_ENABLE_LOCK_COUNT */ /* --- Process lock checking ----------------------------------------------- */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 23f208c2eb..42f0bc88f2 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1534,12 +1534,10 @@ erts_schedule_proc2port_signal(Process *c_p, } static ERTS_INLINE void -send_badsig(Port *prt) -{ +send_badsig(Port *prt) { ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; Process* rp; Eterm connected = ERTS_PORT_GET_CONNECTED(prt); - ERTS_SMP_CHK_NO_PROC_LOCKS; ERTS_LC_ASSERT(erts_get_scheduler_id()); @@ -1559,15 +1557,13 @@ send_badsig(Port *prt) 0); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); - } -} + } /* exit sent */ +} /* send_badsig */ static void -badsig_received(int bang_op, - Port *prt, +badsig_received(int bang_op, Port *prt, erts_aint32_t state, - int bad_output_value) -{ + int bad_output_value) { /* * if (bang_op) * we are part of a "Prt ! Something" operation @@ -1583,12 +1579,12 @@ badsig_received(int bang_op, } if (bang_op) send_badsig(prt); - } -} + } /* not invalid */ +} /* behaved accordingly */ static int -port_badsig(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp) -{ +port_badsig(Port *prt, erts_aint32_t state, int op, + ErtsProc2PortSigData *sigdp) { if (op == ERTS_PROC2PORT_SIG_EXEC) badsig_received(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BANG_OP, prt, @@ -1597,16 +1593,14 @@ port_badsig(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp) if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg); return ERTS_PORT_REDS_BADSIG; -} - - -/* - * bad_port_signal() will +} /* port_badsig */ +/* bad_port_signal() will * - preserve signal order of signals. * - send a 'badsig' exit signal to connected process if 'from' is an * internal pid and the port is alive when the bad signal reaches * it. */ + static ErtsPortOpResult bad_port_signal(Process *c_p, int flags, @@ -2804,7 +2798,6 @@ void erts_init_io(int port_tab_size, } #if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP) - static ERTS_INLINE void lcnt_enable_drv_lock_count(erts_driver_t *dp, int enable) { if (dp->lock) { @@ -2844,25 +2837,26 @@ static ERTS_INLINE void lcnt_enable_port_lock_count(Port *prt, int enable) } } -void erts_lcnt_enable_io_lock_count(int enable) -{ +void erts_lcnt_enable_io_lock_count(int enable) { erts_driver_t *dp; - int i, max = erts_ptab_max(&erts_port); + int ix, max = erts_ptab_max(&erts_port); + Port *prt; - for (i = 0; i < max; i++) { - Port *prt = erts_pix2port(i); - if (prt) + for (ix = 0; ix < max; ix++) { + if ((prt = erts_pix2port(ix)) != NULL) { lcnt_enable_port_lock_count(prt, enable); - } + } + } /* for all ports */ lcnt_enable_drv_lock_count(&vanilla_driver, enable); lcnt_enable_drv_lock_count(&spawn_driver, enable); lcnt_enable_drv_lock_count(&fd_driver, enable); - for (dp = driver_list; dp; dp = dp->next) + /* enable lock counting in all drivers */ + for (dp = driver_list; dp; dp = dp->next) { lcnt_enable_drv_lock_count(dp, enable); -} -#endif - + } +} /* enable/disable lock counting of ports */ +#endif /* defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP) */ /* * Buffering of data when using line oriented I/O on ports */ -- cgit v1.2.3 From 22fda4c723eefe73c2eb34f13db326a53bfeba1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Jun 2015 23:03:55 +0200 Subject: erts: Do not preallocate too large event pool A pool size of 4000 is too excessive for the common case. Change ERTS_TS_EV_ALLOC_DEFAULT_POOL_SIZE to 2048 Change ERTS_TS_EV_ALLOC_POOL_SIZE to 32 --- erts/lib_src/common/ethr_aux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c index 1ba51882c3..5b82a081ad 100644 --- a/erts/lib_src/common/ethr_aux.c +++ b/erts/lib_src/common/ethr_aux.c @@ -40,8 +40,8 @@ #include #endif -#define ERTS_TS_EV_ALLOC_DEFAULT_POOL_SIZE 4000 -#define ERTS_TS_EV_ALLOC_POOL_SIZE 25 +#define ERTS_TS_EV_ALLOC_DEFAULT_POOL_SIZE 2048 +#define ERTS_TS_EV_ALLOC_POOL_SIZE 32 erts_cpu_info_t *ethr_cpu_info__; -- cgit v1.2.3 From e14ca38380885e50a134b8c4297c44aec73ccb5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Jun 2015 10:07:23 +0200 Subject: Revert "Add thread index to allocator enomem dump slogan" This reverts commit 5d5f9c1857029d7e8e1de141e29d20dd3de929be. --- erts/doc/src/crash_dump.xml | 28 +++++++++++++--------------- erts/emulator/beam/erl_alloc.c | 4 ++-- 2 files changed, 15 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index 8291bf38b7..65a3d6af88 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -88,22 +88,20 @@ operating system.

"<A>: Cannot allocate <N> - bytes of memory (of type "<T>", thread - <I>em>)." - The system has run out of memory. <A> - is the allocator that failed to allocate memory, <N> is the - number of bytes that <A> tried to allocate, <T> is the - memory block type that the memory was needed for, and <I> is the - thread identifier. The most common case is that a process stores huge - amounts of data. In this case <T> is most often - , , - , or . - For more information on allocators see - erts_alloc(3). + bytes of memory (of type "<T>")." - The system + has run out of memory. <A> is the allocator that failed + to allocate memory, <N> is the number of bytes that + <A> tried to allocate, and <T> is the memory block + type that the memory was needed for. The most common case is + that a process stores huge amounts of data. In this case + <T> is most often , , + , or . For more information on + allocators see + erts_alloc(3).
"<A>: Cannot reallocate <N> - bytes of memory (of type "<T>", thread - <I>em>)." - Same as above with the exception that memory - was being reallocated instead of being allocated when the system ran - out of memory. + bytes of memory (of type "<T>")." - Same as + above with the exception that memory was being reallocated + instead of being allocated when the system ran out of memory. "Unexpected op code N" - Error in compiled code, file damaged or error in the compiler. "Module Name undefined" "Function diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index d11f24220a..c9ac024743 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1884,8 +1884,8 @@ erts_alc_fatal_error(int error, int func, ErtsAlcType_t n, ...) size = va_arg(argp, Uint); va_end(argp); erl_exit(1, - "%s: Cannot %s %lu bytes of memory (of type \"%s\", thread %d).\n", - allctr_str, op, size, t_str, ERTS_ALC_GET_THR_IX()); + "%s: Cannot %s %lu bytes of memory (of type \"%s\").\n", + allctr_str, op, size, t_str); break; } case ERTS_ALC_E_NOALLCTR: -- cgit v1.2.3 From 92e6fb7f31ad8977144a1cfbcee05895839dbc62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Jun 2015 10:10:50 +0200 Subject: Revert "Add run queue index to process dump info" This reverts commit 345af4a0c8d68b9369c3556fa6d911854c123d3f. --- erts/doc/src/crash_dump.xml | 3 --- erts/emulator/beam/break.c | 1 - 2 files changed, 4 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index 65a3d6af88..e13d468ee6 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -304,9 +304,6 @@ Last scheduled in for | Current call The current function of the process. These fields will not always exist. - Run queue - The identifier of the scheduler run queue in which the process is - running. Spawned by The parent of the process, i.e. the process which executed or . diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 02e65cb9c6..3cb605834f 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -242,7 +242,6 @@ print_process_info(int to, void *to_arg, Process *p) p->current[1], p->current[2]); } - erts_print(to, to_arg, "Run queue: %d\n", erts_get_runq_proc(p)->ix); erts_print(to, to_arg, "Spawned by: %T\n", p->parent); approx_started = (time_t) p->approx_started; -- cgit v1.2.3 From eb15bb261a09c7e87707da042887ccfd7ef58417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Jun 2015 10:12:52 +0200 Subject: Revert "Add missing error string to syslog logging in epmd" This reverts commit e2c11e89563f0c11794c91193b29bce00ca9c740. --- erts/epmd/src/epmd.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 447aae47aa..2fd9845d1a 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -498,11 +498,7 @@ static void dbg_gen_printf(int onsyslog,int perr,int from_level, #ifdef HAVE_SYSLOG_H if (onsyslog) { - int len; - len = erts_vsnprintf(buf, DEBUG_BUFFER_SIZE, format, args); - if (perr != 0 && len < sizeof(buf)) { - erts_snprintf(buf+len, sizeof(buf)-len, ": %s", strerror(perr)); - } + erts_vsnprintf(buf, DEBUG_BUFFER_SIZE, format, args); syslog(LOG_ERR,"epmd: %s",buf); } #endif -- cgit v1.2.3 From af376d145f2d32420590e15c99031433b8cb49f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Jun 2015 10:14:41 +0200 Subject: Revert "Demote rare debug slogan of message discarding to debug build" This reverts commit 38bd20f4f58e8025bd3ffc718cb7e40a4bde6396. --- erts/emulator/beam/bif.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 2b782f4484..bfca861830 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1914,7 +1914,6 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) } else if (is_external_pid(to)) { dep = external_pid_dist_entry(to); if(dep == erts_this_dist_entry) { -#if DEBUG erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); erts_dsprintf(dsbufp, "Discarding message %T from %T to %T in an old " @@ -1925,7 +1924,6 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) external_pid_creation(to), erts_this_node->creation); erts_send_error_to_logger(p->group_leader, dsbufp); -#endif return 0; } return remote_send(p, dep, to, to, msg, ctx); @@ -1959,7 +1957,6 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) } else if (is_external_port(to) && (external_port_dist_entry(to) == erts_this_dist_entry)) { -#if DEBUG erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); erts_dsprintf(dsbufp, "Discarding message %T from %T to %T in an old " @@ -1970,7 +1967,6 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) external_port_creation(to), erts_this_node->creation); erts_send_error_to_logger(p->group_leader, dsbufp); -#endif return 0; } else if (is_internal_port(to)) { int ret_val; -- cgit v1.2.3 From 8e70bbb3a94b09600bf7ac9d3b18713502a64625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Jun 2015 23:11:04 +0200 Subject: Revert "lcnt: Let runq locks reflect actual call location" This reverts commit efefd4bfda3156c6c19a61d7aa3d2f50a026d0e5. Conflicts: erts/emulator/beam/erl_process.h --- erts/emulator/beam/erl_process.h | 37 ------------------------------------- 1 file changed, 37 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 1da1239609..241a82d6c2 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -2057,14 +2057,10 @@ ERTS_GLB_INLINE Eterm erts_get_current_pid(void); ERTS_GLB_INLINE Uint erts_get_scheduler_id(void); ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_proc(Process *p); ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_current(ErtsSchedulerData *esdp); -#ifndef ERTS_ENABLE_LOCK_COUNT ERTS_GLB_INLINE void erts_smp_runq_lock(ErtsRunQueue *rq); -#endif ERTS_GLB_INLINE int erts_smp_runq_trylock(ErtsRunQueue *rq); ERTS_GLB_INLINE void erts_smp_runq_unlock(ErtsRunQueue *rq); -#ifndef ERTS_ENABLE_LOCK_COUNT ERTS_GLB_INLINE void erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq); -#endif ERTS_GLB_INLINE void erts_smp_xrunq_unlock(ErtsRunQueue *rq, ErtsRunQueue *xrq); ERTS_GLB_INLINE void erts_smp_runqs_lock(ErtsRunQueue *rq1, ErtsRunQueue *rq2); ERTS_GLB_INLINE void erts_smp_runqs_unlock(ErtsRunQueue *rq1, ErtsRunQueue *rq2); @@ -2142,12 +2138,6 @@ erts_smp_runq_lock(ErtsRunQueue *rq) #endif } -#ifdef ERTS_ENABLE_LOCK_COUNT - -#define erts_smp_runq_lock(rq) erts_smp_mtx_lock_x(&(rq)->mtx, __FILE__, __LINE__) - -#endif - ERTS_GLB_INLINE int erts_smp_runq_trylock(ErtsRunQueue *rq) { @@ -2166,31 +2156,6 @@ erts_smp_runq_unlock(ErtsRunQueue *rq) #endif } -#ifdef ERTS_ENABLE_LOCK_COUNT - -#define erts_smp_xrunq_lock(rq, xrq) erts_smp_xrunq_lock_x((rq), (xrq), __FILE__, __LINE__) - -ERTS_GLB_INLINE void -erts_smp_xrunq_lock_x(ErtsRunQueue *rq, ErtsRunQueue *xrq, char* file, int line) -{ -#ifdef ERTS_SMP - ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&rq->mtx)); - if (xrq != rq) { - if (erts_smp_mtx_trylock(&xrq->mtx) == EBUSY) { - if (rq < xrq) - erts_smp_mtx_lock_x(&xrq->mtx, file, line); - else { - erts_smp_mtx_unlock(&rq->mtx); - erts_smp_mtx_lock_x(&xrq->mtx, file, line); - erts_smp_mtx_lock_x(&rq->mtx, file, line); - } - } - } -#endif -} - -#else - ERTS_GLB_INLINE void erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq) { @@ -2210,8 +2175,6 @@ erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq) #endif } -#endif - ERTS_GLB_INLINE void erts_smp_xrunq_unlock(ErtsRunQueue *rq, ErtsRunQueue *xrq) { -- cgit v1.2.3 From 74f0d7c8ea47b363136c86de5d7ea78a48c40570 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 17 Jun 2015 00:01:14 +0200 Subject: Save IO bytes in scheduler specific data --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/dist.c | 5 +- erts/emulator/beam/erl_alloc.types | 1 + erts/emulator/beam/erl_bif_info.c | 23 ++--- erts/emulator/beam/erl_port.h | 3 +- erts/emulator/beam/erl_process.c | 3 + erts/emulator/beam/erl_process.h | 5 ++ erts/emulator/beam/io.c | 154 ++++++++++++++++++++++++++++++--- erts/preloaded/ebin/erts_internal.beam | Bin 5380 -> 5964 bytes erts/preloaded/src/erts_internal.erl | 19 +++- 10 files changed, 177 insertions(+), 37 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 74b42c647e..0cf21b4635 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -265,6 +265,7 @@ atom get_seq_token atom get_tcw atom getenv atom gather_gc_info_result +atom gather_io_bytes atom gather_sched_wall_time_result atom getting_linked atom getting_unlinked diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index ae46174a14..1354cee267 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -2088,6 +2088,7 @@ erts_dist_command(Port *prt, int reds_limit) DistEntry *dep = prt->dist_entry; Uint (*send)(Port *prt, ErtsDistOutputBuf *obuf); erts_aint32_t sched_flags; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); @@ -2142,12 +2143,12 @@ erts_dist_command(Port *prt, int reds_limit) ErtsDistOutputBuf *fob; size = (*send)(prt, foq.first); + esdp->io.out += (Uint64) size; #ifdef ERTS_RAW_DIST_MSG_DBG erts_fprintf(stderr, ">> "); 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; @@ -2227,12 +2228,12 @@ erts_dist_command(Port *prt, int reds_limit) ASSERT(&oq.first->data[0] <= oq.first->extp && oq.first->extp < oq.first->ext_endp); size = (*send)(prt, oq.first); + esdp->io.out += (Uint64) size; #ifdef ERTS_RAW_DIST_MSG_DBG erts_fprintf(stderr, ">> "); 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; diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 2721e13250..92c397ffd3 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -274,6 +274,7 @@ type BUSY_CALLER SHORT_LIVED SYSTEM busy_caller type PROC_SYS_TSK SHORT_LIVED PROCESSES proc_sys_task type PROC_SYS_TSK_QS SHORT_LIVED PROCESSES proc_sys_task_queues type NEW_TIME_OFFSET SHORT_LIVED SYSTEM new_time_offset +type IOB_REQ SHORT_LIVED SYSTEM io_bytes_request +if threads_no_smp # Need thread safe allocs, but std_alloc and fix_alloc are not; diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 397c68e199..ba070b0ddf 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -60,6 +60,7 @@ static Export* alloc_info_trap = NULL; static Export* alloc_sizes_trap = NULL; +static Export* gather_io_bytes_trap = NULL; static Export *gather_sched_wall_time_res_trap; static Export *gather_gc_info_res_trap; @@ -3220,7 +3221,6 @@ BIF_RETTYPE process_display_2(BIF_ALIST_2) BIF_RET(am_true); } - /* this is a general call which return some possibly useful information */ BIF_RETTYPE statistics_1(BIF_ALIST_1) @@ -3293,23 +3293,8 @@ BIF_RETTYPE statistics_1(BIF_ALIST_1) res = TUPLE2(hp, b1, b2); BIF_RET(res); } else if (BIF_ARG_1 == am_io) { - Eterm r1, r2; - Eterm in, out; - Uint hsz = 9; - Uint bytes_in = (Uint) erts_smp_atomic_read_nob(&erts_bytes_in); - Uint bytes_out = (Uint) erts_smp_atomic_read_nob(&erts_bytes_out); - - (void) erts_bld_uint(NULL, &hsz, bytes_in); - (void) erts_bld_uint(NULL, &hsz, bytes_out); - hp = HAlloc(BIF_P, hsz); - in = erts_bld_uint(&hp, NULL, bytes_in); - out = erts_bld_uint(&hp, NULL, bytes_out); - - r1 = TUPLE2(hp, am_input, in); - hp += 3; - r2 = TUPLE2(hp, am_output, out); - hp += 3; - BIF_RET(TUPLE2(hp, r1, r2)); + Eterm ref = erts_request_io_bytes(BIF_P); + BIF_TRAP2(gather_io_bytes_trap, BIF_P, ref, make_small(erts_no_schedulers)); } else if (ERTS_IS_ATOM_STR("run_queues", BIF_ARG_1)) { Eterm res, *hp, **hpp; @@ -4388,6 +4373,8 @@ erts_bif_info_init(void) = erts_export_put(am_erlang, am_gather_sched_wall_time_result, 1); gather_gc_info_res_trap = erts_export_put(am_erlang, am_gather_gc_info_result, 1); + gather_io_bytes_trap + = erts_export_put(am_erts_internal, am_gather_io_bytes, 2); process_info_init(); os_info_init(); } diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index 3920fae2d9..66cbb67a0d 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -266,8 +266,7 @@ erts_prtsd_set(Port *prt, int ix, void *data) #endif -extern erts_smp_atomic_t erts_bytes_out; /* no bytes written out */ -extern erts_smp_atomic_t erts_bytes_in; /* no bytes sent into the system */ +Eterm erts_request_io_bytes(Process *c_p); /* port status flags */ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 43e84a1be1..88c1b5c121 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -5542,6 +5542,9 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num, esdp->thr_id = (Uint32) num; erts_sched_bif_unique_init(esdp); + esdp->io.out = (Uint64) 0; + esdp->io.in = (Uint64) 0; + if (daww_ptr) { init_aux_work_data(&esdp->aux_work_data, esdp, *daww_ptr); #ifdef ERTS_SMP diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 1da1239609..27b6fcef50 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -659,6 +659,11 @@ struct ErtsSchedulerData_ { ErtsSchedAllocData alloc_data; + struct { + Uint64 out; + Uint64 in; + } io; + Uint64 reductions; ErtsSchedWallTime sched_wall_time; ErtsGCInfo gc_info; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 23f208c2eb..9099442a6f 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -65,8 +65,6 @@ static erts_smp_tsd_key_t driver_list_last_error_key; /* Save last DDLL error o per thread basis (for BC interfaces) */ ErtsPTab erts_port erts_align_attribute(ERTS_CACHE_LINE_SIZE); /* The port table */ -erts_smp_atomic_t erts_bytes_out; /* No bytes sent out of the system */ -erts_smp_atomic_t erts_bytes_in; /* No bytes gotten into the system */ const ErlDrvTermData driver_term_nil = (ErlDrvTermData)NIL; @@ -80,6 +78,9 @@ int erts_port_synchronous_ops = 0; int erts_port_schedule_all_ops = 0; int erts_port_parallelism = 0; +static erts_atomic64_t bytes_in; +static erts_atomic64_t bytes_out; + static void deliver_result(Eterm sender, Eterm pid, Eterm res); static int init_driver(erts_driver_t *, ErlDrvEntry *, DE_Handle *); static void terminate_port(Port *p); @@ -1681,6 +1682,7 @@ call_driver_outputv(int bang_op, if (bang_op && from != ERTS_PORT_GET_CONNECTED(prt)) send_badsig(prt); else { + ErtsSchedulerData *esdp = erts_get_scheduler_data(); ErlDrvSizeT size = evp->size; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt) @@ -1698,7 +1700,10 @@ call_driver_outputv(int bang_op, prt->caller = NIL; prt->bytes_out += size; - erts_smp_atomic_add_nob(&erts_bytes_out, size); + if (esdp) + esdp->io.out += (Uint64) size; + else + erts_atomic64_add_nob(&bytes_out, (erts_aint64_t) size); } } @@ -1778,7 +1783,7 @@ call_driver_output(int bang_op, if (bang_op && from != ERTS_PORT_GET_CONNECTED(prt)) send_badsig(prt); else { - + ErtsSchedulerData *esdp = erts_get_scheduler_data(); ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt) || ERTS_IS_CRASH_DUMPING); @@ -1794,7 +1799,10 @@ call_driver_output(int bang_op, prt->caller = NIL; prt->bytes_out += size; - erts_smp_atomic_add_nob(&erts_bytes_out, size); + if (esdp) + esdp->io.out += (Uint64) size; + else + erts_atomic64_add_nob(&bytes_out, (erts_aint64_t) size); } } @@ -2741,6 +2749,9 @@ void erts_init_io(int port_tab_size, drv_list_rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ; drv_list_rwmtx_opts.lived = ERTS_SMP_RWMTX_LONG_LIVED; + erts_atomic64_init_nob(&bytes_in, 0); + erts_atomic64_init_nob(&bytes_out, 0); + common_element_size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port)); common_element_size += ERTS_ALC_DATA_ALIGN_SIZE(sizeof(ErtsPortTaskBusyPortQ)); common_element_size += 10; /* name */ @@ -2782,9 +2793,6 @@ void erts_init_io(int port_tab_size, legacy_port_tab, 1); - erts_smp_atomic_init_nob(&erts_bytes_out, 0); - erts_smp_atomic_init_nob(&erts_bytes_in, 0); - sys_init_io(); erts_smp_tsd_set(driver_list_lock_status_key, (void *) 1); @@ -4573,6 +4581,102 @@ erts_port_info(Process* c_p, port_sig_info); } +typedef struct { + Uint sched_id; + Eterm pid; + Uint32 refn[ERTS_REF_NUMBERS]; + erts_smp_atomic32_t refc; +} ErtsIOBytesReq; + +static void +reply_io_bytes(void *vreq) +{ + ErtsIOBytesReq *req = (ErtsIOBytesReq *) vreq; + Process *rp; + + rp = erts_proc_lookup(req->pid); + if (rp) { + ErlOffHeap *ohp = NULL; + ErlHeapFragment *bp = NULL; + ErtsProcLocks rp_locks; + Eterm ref, msg, ein, eout, *hp; + Uint64 in, out; + Uint hsz; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + Uint sched_id = esdp->no; + in = esdp->io.in; + out = esdp->io.out; + if (req->sched_id != sched_id) + rp_locks = 0; + else { + in += (Uint64) erts_atomic64_read_nob(&bytes_in); + out += (Uint64) erts_atomic64_read_nob(&bytes_out); + rp_locks = ERTS_PROC_LOCK_MAIN; + } + + hsz = 5 /* 4-tuple */ + REF_THING_SIZE; + + erts_bld_uint64(NULL, &hsz, in); + erts_bld_uint64(NULL, &hsz, out); + + hp = erts_alloc_message_heap(hsz, &bp, &ohp, rp, &rp_locks); + + ref = make_internal_ref(hp); + write_ref_thing(hp, req->refn[0], req->refn[1], req->refn[2]); + hp += REF_THING_SIZE; + + ein = erts_bld_uint64(&hp, NULL, in); + eout = erts_bld_uint64(&hp, NULL, out); + + msg = TUPLE4(hp, ref, make_small(sched_id), ein, eout); + erts_queue_message(rp, &rp_locks, bp, msg, NIL); + + if (req->sched_id == sched_id) + rp_locks &= ~ERTS_PROC_LOCK_MAIN; + if (rp_locks) + erts_smp_proc_unlock(rp, rp_locks); + } + + if (erts_smp_atomic32_dec_read_nob(&req->refc) == 0) + erts_free(ERTS_ALC_T_IOB_REQ, req); +} + +Eterm +erts_request_io_bytes(Process *c_p) +{ + Uint *hp; + Eterm ref; + Uint32 *refn; + ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p); + ErtsIOBytesReq *req = erts_alloc(ERTS_ALC_T_IOB_REQ, + sizeof(ErtsIOBytesReq)); + + hp = HAlloc(c_p, REF_THING_SIZE); + ref = erts_sched_make_ref_in_buffer(esdp, hp); + refn = internal_ref_numbers(ref); + + req->sched_id = esdp->no; + req->pid = c_p->common.id; + req->refn[0] = refn[0]; + req->refn[1] = refn[1]; + req->refn[2] = refn[2]; + erts_smp_atomic32_init_nob(&req->refc, + (erts_aint32_t) erts_no_schedulers); + +#ifdef ERTS_SMP + if (erts_no_schedulers > 1) + erts_schedule_multi_misc_aux_work(1, + erts_no_schedulers, + reply_io_bytes, + (void *) req); +#endif + + reply_io_bytes((void *) req); + + return ref; +} + + typedef struct { int to; void *arg; @@ -5111,6 +5215,7 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) struct b2t_states__ b2t; int scheduler; int is_heap_need_limited = 1; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); ERTS_UNDEF(mess,NIL); ERTS_UNDEF(scheduler,1); @@ -5418,7 +5523,10 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) Uint size = ptr[1]; Uint offset = ptr[2]; - erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) size); + if (esdp) + esdp->io.in += (Uint64) size; + else + erts_atomic64_add_nob(&bytes_in, (erts_aint64_t) size); if (size <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hbp = (ErlHeapBin *) erts_produce_heap(&factory, @@ -5452,7 +5560,10 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) byte *bufp = (byte *) ptr[0]; Uint size = (Uint) ptr[1]; - erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) size); + if (esdp) + esdp->io.in += (Uint64) size; + else + erts_atomic64_add_nob(&bytes_in, (erts_aint64_t) size); if (size <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hbp = (ErlHeapBin *) erts_produce_heap(&factory, @@ -5489,7 +5600,10 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) } case ERL_DRV_STRING: /* char*, length */ - erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) ptr[1]); + if (esdp) + esdp->io.in += (Uint64) ptr[1]; + else + erts_atomic64_add_nob(&bytes_in, (erts_aint64_t) ptr[1]); erts_reserve_heap(&factory, 2*ptr[1]); mess = buf_to_intlist(&factory.hp, (char*)ptr[0], ptr[1], NIL); ptr += 2; @@ -5765,6 +5879,7 @@ int driver_output_binary(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen, { erts_aint32_t state; Port* prt = erts_drvport2port_state(ix, &state); + ErtsSchedulerData *esdp = erts_get_scheduler_data(); ERTS_SMP_CHK_NO_PROC_LOCKS; @@ -5775,7 +5890,10 @@ int driver_output_binary(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen, return 0; prt->bytes_in += (hlen + len); - erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) (hlen + len)); + if (esdp) + esdp->io.in += (Uint64) (hlen + len); + else + erts_atomic64_add_nob(&bytes_in, (erts_aint64_t) (hlen + len)); if (state & ERTS_PORT_SFLG_DISTRIBUTION) { return erts_net_message(prt, prt->dist_entry, @@ -5800,6 +5918,7 @@ int driver_output2(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen, { erts_aint32_t state; Port* prt = erts_drvport2port_state(ix, &state); + ErtsSchedulerData *esdp = erts_get_scheduler_data(); ERTS_SMP_CHK_NO_PROC_LOCKS; @@ -5811,7 +5930,10 @@ int driver_output2(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen, return 0; prt->bytes_in += (hlen + len); - erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) (hlen + len)); + if (esdp) + esdp->io.in += (Uint64) (hlen + len); + else + erts_atomic64_add_nob(&bytes_in, (erts_aint64_t) (hlen + len)); if (state & ERTS_PORT_SFLG_DISTRIBUTION) { if (len == 0) return erts_net_message(prt, @@ -5851,6 +5973,7 @@ int driver_outputv(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen, ErlDrvBinary** binv; Port* prt; erts_aint32_t state; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); ERTS_SMP_CHK_NO_PROC_LOCKS; @@ -5889,7 +6012,10 @@ int driver_outputv(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen, /* XXX handle distribution !!! */ prt->bytes_in += (hlen + size); - erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) (hlen + size)); + if (esdp) + esdp->io.in += (Uint64) (hlen + size); + else + erts_atomic64_add_nob(&bytes_in, (erts_aint64_t) (hlen + size)); deliver_vec_message(prt, ERTS_PORT_GET_CONNECTED(prt), hbuf, hlen, binv, iov, n, size); return 0; diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 0e0811af3f..dc8c711e1a 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 65a1f1ed3a..cf8edefd7d 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -40,7 +40,7 @@ -export([flush_monitor_messages/3]). --export([await_result/1]). +-export([await_result/1, gather_io_bytes/2]). -export([time_unit/0]). @@ -66,6 +66,23 @@ await_result(Ref) when is_reference(Ref) -> Result end. +%% +%% statistics(io) end up in gather_io_bytes/2 +%% + +gather_io_bytes(Ref, No) when is_reference(Ref), + is_integer(No), + No > 0 -> + gather_io_bytes(Ref, No, 0, 0). + +gather_io_bytes(_Ref, 0, InAcc, OutAcc) -> + {{input, InAcc}, {output, OutAcc}}; +gather_io_bytes(Ref, No, InAcc, OutAcc) -> + receive + {Ref, _SchedId, In, Out} -> + gather_io_bytes(Ref, No-1, InAcc + In, OutAcc + Out) + end. + %% %% Statically linked port NIFs %% -- cgit v1.2.3 From db193ba5dac15b1341a622307adbaf55002531b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 17 Jun 2015 11:23:11 +0200 Subject: erts: Fix erts_debug:df/1 in debug Sentinels in select_tuple_arity instructions are not proper tuple arities and thus cannot be checked in debug. Print them as small integers instead. --- erts/emulator/beam/beam_debug.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 0367ca8aba..78ddecafc3 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -616,24 +616,28 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) case op_i_select_tuple_arity_rfI: case op_i_select_tuple_arity_xfI: case op_i_select_tuple_arity_yfI: - { - int n = ap[-1]; - int ix = n; - - while (ix--) { - Uint arity = arityval(ap[0]); - erts_print(to, to_arg, "{%d} ", arity, ap[1]); - ap++; - size++; - } - ix = n; - while (ix--) { - erts_print(to, to_arg, "f(" HEXF ") ", ap[0]); - ap++; - size++; - } - } - break; + { + int n = ap[-1]; + int ix = n - 1; /* without sentinel */ + + while (ix--) { + Uint arity = arityval(ap[0]); + erts_print(to, to_arg, "{%d} ", arity, ap[1]); + ap++; + size++; + } + /* print sentinel */ + erts_print(to, to_arg, "{%T} ", ap[0], ap[1]); + ap++; + size++; + ix = n; + while (ix--) { + erts_print(to, to_arg, "f(" HEXF ") ", ap[0]); + ap++; + size++; + } + } + break; case op_i_jump_on_val_rfII: case op_i_jump_on_val_xfII: case op_i_jump_on_val_yfII: -- cgit v1.2.3 From daa9b4d4522f397e418610cf7746441c684bc44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Jun 2015 10:00:41 +0200 Subject: erts: Add brackets to statement --- erts/emulator/sys/unix/erl_child_setup.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index d050748703..e1d2f66b7e 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -107,14 +107,16 @@ main(int argc, char *argv[]) if (from <= to) { int spfd = system_properties_fd(); for (i = from; i <= to; i++) { - if (i != spfd) + if (i != spfd) { (void) close(i); + } } } -#else - for (i = from; i <= to; i++) +#else /* !__ANDROID__ */ + for (i = from; i <= to; i++) { (void) close(i); -#endif + } +#endif /* HAVE_CLOSEFROM */ if (!(argv[CS_ARGV_WD_IX][0] == '.' && argv[CS_ARGV_WD_IX][1] == '\0') && chdir(argv[CS_ARGV_WD_IX]) < 0) -- cgit v1.2.3 From 7d5f466812b3bc87752ede825ee9d94df730ca54 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 17 Jun 2015 16:47:21 +0200 Subject: erts: Refactor growth of fd tables to have one common implementation for both _kp and _nkp. --- erts/emulator/sys/common/erl_check_io.c | 17 ++++++------ erts/emulator/sys/common/erl_poll.c | 49 +++++++++++++++++---------------- erts/emulator/sys/common/erl_poll.h | 2 +- 3 files changed, 36 insertions(+), 32 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index d1d6696090..3097f7b7dd 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -41,8 +41,7 @@ #define ERTS_WANT_TIMER_WHEEL_API #include "erl_time.h" -#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS -#else +#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS # include "safe_hash.h" # define DRV_EV_STATE_HTAB_SIZE 1024 #endif @@ -413,14 +412,16 @@ static void grow_drv_ev_state(int min_ix) { int i; + int old_len; 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; - erts_smp_mtx_lock(&drv_ev_state_grow_lock); - if (erts_smp_atomic_read_nob(&drv_ev_state_len) <= min_ix) { + old_len = erts_smp_atomic_read_nob(&drv_ev_state_len); + if (min_ix >= old_len) { + new_len = erts_poll_new_table_len(old_len, min_ix + 1); + if (new_len > max_fds) + new_len = max_fds; + for (i=0; i> 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; + int new_len; + + ASSERT(need_len > old_len); + if (need_len < ERTS_FD_TABLE_MIN_LENGTH) { + new_len = ERTS_FD_TABLE_MIN_LENGTH; } + else { + new_len = old_len; + do { + if (new_len < ERTS_FD_TABLE_EXP_THRESHOLD) + new_len *= 2; + else + new_len += ERTS_FD_TABLE_EXP_THRESHOLD; + + } while (new_len < need_len); + } + ASSERT(new_len >= need_len); return new_len; } - +#endif #if ERTS_POLL_USE_KERNEL_POLL static void @@ -737,7 +740,7 @@ grow_res_events(ErtsPollSet ps, int new_len) #elif ERTS_POLL_USE_KQUEUE struct kevent #endif - ) * ERTS_POLL_EXPORT(erts_poll_get_table_len)(new_len); + ) * erts_poll_new_table_len(ps->res_events_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); @@ -751,7 +754,7 @@ static void grow_poll_fds(ErtsPollSet ps, int min_ix) { int i; - int new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(min_ix + 1); + int new_len = erts_poll_new_table_len(ps->poll_fds_len, min_ix + 1); if (new_len > max_fds) new_len = max_fds; ps->poll_fds = (ps->poll_fds_len @@ -800,7 +803,7 @@ static void grow_fds_status(ErtsPollSet ps, int min_fd) { int i; - int new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(min_fd + 1); + int new_len = erts_poll_new_table_len(ps->fds_status_len, min_fd + 1); ASSERT(min_fd < max_fds); if (new_len > max_fds) new_len = max_fds; diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index ae2d063805..ad8f714d6e 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -275,6 +275,6 @@ void ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet, ErtsPollEvents [], int); -int ERTS_POLL_EXPORT(erts_poll_get_table_len)(int); +int erts_poll_new_table_len(int old_len, int need_len); #endif /* #ifndef ERL_POLL_H__ */ -- cgit v1.2.3 From 738c34d4bb8f1a3811acd00af8c6c12107f8315b Mon Sep 17 00:00:00 2001 From: Bruce Yinhe Date: Thu, 18 Jun 2015 11:31:02 +0200 Subject: Change license text to APLv2 --- erts/AUTHORS | 19 +++++----- erts/Makefile.in | 19 +++++----- erts/aclocal.m4 | 21 +++++------ erts/autoconf/configure.vxworks | 21 +++++------ erts/autoconf/vxworks/sed.general | 21 +++++------ erts/autoconf/vxworks/sed.vxworks_cpu32 | 21 +++++------ erts/autoconf/vxworks/sed.vxworks_ppc32 | 21 +++++------ erts/autoconf/vxworks/sed.vxworks_ppc603 | 21 +++++------ .../autoconf/vxworks/sed.vxworks_ppc603_nolongcall | 21 +++++------ erts/autoconf/vxworks/sed.vxworks_ppc860 | 21 +++++------ erts/autoconf/vxworks/sed.vxworks_simlinux | 19 +++++----- erts/autoconf/vxworks/sed.vxworks_simso | 19 +++++----- erts/autoconf/vxworks/sed.vxworks_sparc | 21 +++++------ erts/configure.in | 19 +++++----- erts/doc/Makefile | 21 +++++------ erts/doc/src/Makefile | 19 +++++----- erts/doc/src/absform.xml | 21 +++++------ erts/doc/src/alt_dist.xml | 19 +++++----- erts/doc/src/book.xml | 21 +++++------ erts/doc/src/communication.xml | 19 +++++----- erts/doc/src/crash_dump.xml | 19 +++++----- erts/doc/src/driver.xml | 21 +++++------ erts/doc/src/driver_entry.xml | 19 +++++----- erts/doc/src/epmd.xml | 21 +++++------ erts/doc/src/erl.xml | 19 +++++----- erts/doc/src/erl_dist_protocol.xml | 21 +++++------ erts/doc/src/erl_driver.xml | 19 +++++----- erts/doc/src/erl_ext_dist.xml | 21 +++++------ erts/doc/src/erl_nif.xml | 21 +++++------ erts/doc/src/erl_prim_loader.xml | 21 +++++------ erts/doc/src/erlang.xml | 19 +++++----- erts/doc/src/erlc.xml | 19 +++++----- erts/doc/src/erlsrv.xml | 19 +++++----- erts/doc/src/erts_alloc.xml | 19 +++++----- erts/doc/src/escript.xml | 21 +++++------ erts/doc/src/inet_cfg.xml | 19 +++++----- erts/doc/src/init.xml | 21 +++++------ erts/doc/src/match_spec.xml | 19 +++++----- erts/doc/src/notes.xml | 21 +++++------ erts/doc/src/notes_history.xml | 21 +++++------ erts/doc/src/part.xml | 21 +++++------ erts/doc/src/part_notes.xml | 21 +++++------ erts/doc/src/part_notes_history.xml | 21 +++++------ erts/doc/src/ref_man.xml | 21 +++++------ erts/doc/src/run_erl.xml | 21 +++++------ erts/doc/src/start.xml | 21 +++++------ erts/doc/src/start_erl.xml | 21 +++++------ erts/doc/src/time_correction.xml | 21 +++++------ erts/doc/src/tty.xml | 19 +++++----- erts/doc/src/werl.xml | 21 +++++------ erts/doc/src/zlib.xml | 19 +++++----- erts/emulator/Makefile | 21 +++++------ erts/emulator/Makefile.in | 19 +++++----- erts/emulator/beam/atom.c | 19 +++++----- erts/emulator/beam/atom.h | 19 +++++----- erts/emulator/beam/atom.names | 19 +++++----- erts/emulator/beam/beam_bif_load.c | 19 +++++----- erts/emulator/beam/beam_bp.c | 19 +++++----- erts/emulator/beam/beam_bp.h | 19 +++++----- erts/emulator/beam/beam_catches.c | 19 +++++----- erts/emulator/beam/beam_catches.h | 19 +++++----- erts/emulator/beam/beam_debug.c | 19 +++++----- erts/emulator/beam/beam_emu.c | 19 +++++----- erts/emulator/beam/beam_load.c | 19 +++++----- erts/emulator/beam/beam_load.h | 19 +++++----- erts/emulator/beam/beam_ranges.c | 19 +++++----- erts/emulator/beam/benchmark.c | 21 +++++------ erts/emulator/beam/benchmark.h | 21 +++++------ erts/emulator/beam/bif.c | 19 +++++----- erts/emulator/beam/bif.h | 19 +++++----- erts/emulator/beam/bif.tab | 21 +++++------ erts/emulator/beam/big.c | 19 +++++----- erts/emulator/beam/big.h | 19 +++++----- erts/emulator/beam/binary.c | 19 +++++----- erts/emulator/beam/break.c | 19 +++++----- erts/emulator/beam/code_ix.c | 19 +++++----- erts/emulator/beam/code_ix.h | 19 +++++----- erts/emulator/beam/copy.c | 19 +++++----- erts/emulator/beam/dist.c | 19 +++++----- erts/emulator/beam/dist.h | 19 +++++----- erts/emulator/beam/dtrace-wrapper.h | 19 +++++----- erts/emulator/beam/elib_memmove.c | 21 +++++------ erts/emulator/beam/erl_afit_alloc.c | 21 +++++------ erts/emulator/beam/erl_afit_alloc.h | 21 +++++------ erts/emulator/beam/erl_alloc.c | 19 +++++----- erts/emulator/beam/erl_alloc.h | 19 +++++----- erts/emulator/beam/erl_alloc.types | 19 +++++----- erts/emulator/beam/erl_alloc_util.c | 19 +++++----- erts/emulator/beam/erl_alloc_util.h | 19 +++++----- erts/emulator/beam/erl_ao_firstfit_alloc.c | 21 +++++------ erts/emulator/beam/erl_ao_firstfit_alloc.h | 21 +++++------ erts/emulator/beam/erl_arith.c | 19 +++++----- erts/emulator/beam/erl_async.c | 19 +++++----- erts/emulator/beam/erl_async.h | 19 +++++----- erts/emulator/beam/erl_bestfit_alloc.c | 21 +++++------ erts/emulator/beam/erl_bestfit_alloc.h | 21 +++++------ erts/emulator/beam/erl_bif_binary.c | 19 +++++----- erts/emulator/beam/erl_bif_chksum.c | 19 +++++----- erts/emulator/beam/erl_bif_ddll.c | 21 +++++------ erts/emulator/beam/erl_bif_guard.c | 19 +++++----- erts/emulator/beam/erl_bif_info.c | 19 +++++----- erts/emulator/beam/erl_bif_lists.c | 19 +++++----- erts/emulator/beam/erl_bif_op.c | 19 +++++----- erts/emulator/beam/erl_bif_os.c | 21 +++++------ erts/emulator/beam/erl_bif_port.c | 19 +++++----- erts/emulator/beam/erl_bif_re.c | 19 +++++----- erts/emulator/beam/erl_bif_trace.c | 19 +++++----- erts/emulator/beam/erl_bif_unique.c | 19 +++++----- erts/emulator/beam/erl_bif_unique.h | 19 +++++----- erts/emulator/beam/erl_binary.h | 19 +++++----- erts/emulator/beam/erl_bits.c | 19 +++++----- erts/emulator/beam/erl_bits.h | 19 +++++----- erts/emulator/beam/erl_cpu_topology.c | 19 +++++----- erts/emulator/beam/erl_cpu_topology.h | 19 +++++----- erts/emulator/beam/erl_db.c | 19 +++++----- erts/emulator/beam/erl_db.h | 21 +++++------ erts/emulator/beam/erl_db_hash.c | 19 +++++----- erts/emulator/beam/erl_db_hash.h | 21 +++++------ erts/emulator/beam/erl_db_tree.c | 19 +++++----- erts/emulator/beam/erl_db_tree.h | 21 +++++------ erts/emulator/beam/erl_db_util.c | 19 +++++----- erts/emulator/beam/erl_db_util.h | 19 +++++----- erts/emulator/beam/erl_debug.c | 19 +++++----- erts/emulator/beam/erl_debug.h | 19 +++++----- erts/emulator/beam/erl_driver.h | 19 +++++----- erts/emulator/beam/erl_drv_nif.h | 19 +++++----- erts/emulator/beam/erl_drv_thread.c | 19 +++++----- erts/emulator/beam/erl_fun.c | 19 +++++----- erts/emulator/beam/erl_fun.h | 19 +++++----- erts/emulator/beam/erl_gc.c | 19 +++++----- erts/emulator/beam/erl_gc.h | 19 +++++----- erts/emulator/beam/erl_goodfit_alloc.c | 19 +++++----- erts/emulator/beam/erl_goodfit_alloc.h | 19 +++++----- erts/emulator/beam/erl_hl_timer.c | 21 +++++------ erts/emulator/beam/erl_hl_timer.h | 21 +++++------ erts/emulator/beam/erl_init.c | 19 +++++----- erts/emulator/beam/erl_instrument.c | 19 +++++----- erts/emulator/beam/erl_instrument.h | 21 +++++------ erts/emulator/beam/erl_lock_check.c | 19 +++++----- erts/emulator/beam/erl_lock_check.h | 21 +++++------ erts/emulator/beam/erl_lock_count.c | 19 +++++----- erts/emulator/beam/erl_lock_count.h | 19 +++++----- erts/emulator/beam/erl_map.c | 19 +++++----- erts/emulator/beam/erl_map.h | 21 +++++------ erts/emulator/beam/erl_math.c | 21 +++++------ erts/emulator/beam/erl_message.c | 19 +++++----- erts/emulator/beam/erl_message.h | 19 +++++----- erts/emulator/beam/erl_monitors.c | 21 +++++------ erts/emulator/beam/erl_monitors.h | 21 +++++------ erts/emulator/beam/erl_mtrace.c | 19 +++++----- erts/emulator/beam/erl_mtrace.h | 21 +++++------ erts/emulator/beam/erl_nif.c | 19 +++++----- erts/emulator/beam/erl_nif.h | 19 +++++----- erts/emulator/beam/erl_nif_api_funcs.h | 19 +++++----- erts/emulator/beam/erl_node_container_utils.h | 19 +++++----- erts/emulator/beam/erl_node_tables.c | 19 +++++----- erts/emulator/beam/erl_node_tables.h | 19 +++++----- erts/emulator/beam/erl_port.h | 19 +++++----- erts/emulator/beam/erl_port_task.c | 19 +++++----- erts/emulator/beam/erl_port_task.h | 21 +++++------ erts/emulator/beam/erl_printf_term.c | 19 +++++----- erts/emulator/beam/erl_printf_term.h | 21 +++++------ erts/emulator/beam/erl_process.c | 19 +++++----- erts/emulator/beam/erl_process.h | 19 +++++----- erts/emulator/beam/erl_process_dict.c | 21 +++++------ erts/emulator/beam/erl_process_dict.h | 21 +++++------ erts/emulator/beam/erl_process_dump.c | 19 +++++----- erts/emulator/beam/erl_process_lock.c | 19 +++++----- erts/emulator/beam/erl_process_lock.h | 21 +++++------ erts/emulator/beam/erl_ptab.c | 19 +++++----- erts/emulator/beam/erl_ptab.h | 19 +++++----- erts/emulator/beam/erl_rbtree.h | 21 +++++------ erts/emulator/beam/erl_sched_spec_pre_alloc.c | 19 +++++----- erts/emulator/beam/erl_sched_spec_pre_alloc.h | 19 +++++----- erts/emulator/beam/erl_smp.h | 19 +++++----- erts/emulator/beam/erl_sock.h | 21 +++++------ erts/emulator/beam/erl_sys_driver.h | 21 +++++------ erts/emulator/beam/erl_term.c | 19 +++++----- erts/emulator/beam/erl_term.h | 19 +++++----- erts/emulator/beam/erl_thr_progress.c | 19 +++++----- erts/emulator/beam/erl_thr_progress.h | 19 +++++----- erts/emulator/beam/erl_thr_queue.c | 19 +++++----- erts/emulator/beam/erl_thr_queue.h | 19 +++++----- erts/emulator/beam/erl_threads.h | 19 +++++----- erts/emulator/beam/erl_time.h | 21 +++++------ erts/emulator/beam/erl_time_sup.c | 19 +++++----- erts/emulator/beam/erl_trace.c | 19 +++++----- erts/emulator/beam/erl_trace.h | 19 +++++----- erts/emulator/beam/erl_unicode.c | 19 +++++----- erts/emulator/beam/erl_unicode.h | 21 +++++------ erts/emulator/beam/erl_unicode_normalize.h | 35 +++++++++--------- erts/emulator/beam/erl_utils.h | 19 +++++----- erts/emulator/beam/erl_vm.h | 19 +++++----- erts/emulator/beam/erl_zlib.c | 21 +++++------ erts/emulator/beam/erl_zlib.h | 21 +++++------ erts/emulator/beam/erlang_dtrace.d | 19 +++++----- erts/emulator/beam/error.h | 19 +++++----- erts/emulator/beam/export.c | 19 +++++----- erts/emulator/beam/export.h | 19 +++++----- erts/emulator/beam/external.c | 19 +++++----- erts/emulator/beam/external.h | 19 +++++----- erts/emulator/beam/global.h | 19 +++++----- erts/emulator/beam/hash.c | 21 +++++------ erts/emulator/beam/hash.h | 21 +++++------ erts/emulator/beam/index.c | 21 +++++------ erts/emulator/beam/index.h | 21 +++++------ erts/emulator/beam/io.c | 19 +++++----- erts/emulator/beam/module.c | 19 +++++----- erts/emulator/beam/module.h | 19 +++++----- erts/emulator/beam/ops.tab | 21 +++++------ erts/emulator/beam/packet_parser.c | 21 +++++------ erts/emulator/beam/packet_parser.h | 21 +++++------ erts/emulator/beam/register.c | 19 +++++----- erts/emulator/beam/register.h | 21 +++++------ erts/emulator/beam/safe_hash.c | 21 +++++------ erts/emulator/beam/safe_hash.h | 21 +++++------ erts/emulator/beam/sys.h | 19 +++++----- erts/emulator/beam/time.c | 21 +++++------ erts/emulator/beam/utils.c | 19 +++++----- erts/emulator/beam/version.h | 21 +++++------ erts/emulator/drivers/common/efile_drv.c | 19 +++++----- erts/emulator/drivers/common/erl_efile.h | 19 +++++----- erts/emulator/drivers/common/gzio.h | 21 +++++------ erts/emulator/drivers/common/gzio_zutil.h | 21 +++++------ erts/emulator/drivers/common/inet_drv.c | 19 +++++----- erts/emulator/drivers/common/ram_file_drv.c | 19 +++++----- erts/emulator/drivers/common/zlib_drv.c | 19 +++++----- erts/emulator/drivers/ose/ose_efile.c | 19 +++++----- erts/emulator/drivers/ose/ose_signal_drv.c | 19 +++++----- erts/emulator/drivers/ose/ttsl_drv.c | 19 +++++----- erts/emulator/drivers/unix/bin_drv.c | 21 +++++------ erts/emulator/drivers/unix/multi_drv.c | 21 +++++------ erts/emulator/drivers/unix/sig_drv.c | 21 +++++------ erts/emulator/drivers/unix/ttsl_drv.c | 19 +++++----- erts/emulator/drivers/unix/unix_efile.c | 19 +++++----- erts/emulator/drivers/vxworks/vxworks_resolv.c | 21 +++++------ erts/emulator/drivers/win32/registry_drv.c | 21 +++++------ erts/emulator/drivers/win32/ttsl_drv.c | 21 +++++------ erts/emulator/drivers/win32/win_con.c | 21 +++++------ erts/emulator/drivers/win32/win_con.h | 21 +++++------ erts/emulator/drivers/win32/win_efile.c | 19 +++++----- erts/emulator/hipe/TODO | 19 +++++----- erts/emulator/hipe/elf64ppc.x | 21 +++++------ erts/emulator/hipe/hipe_amd64.c | 19 +++++----- erts/emulator/hipe/hipe_amd64.h | 19 +++++----- erts/emulator/hipe/hipe_amd64.tab | 19 +++++----- erts/emulator/hipe/hipe_amd64_asm.m4 | 19 +++++----- erts/emulator/hipe/hipe_amd64_bifs.m4 | 19 +++++----- erts/emulator/hipe/hipe_amd64_gc.h | 19 +++++----- erts/emulator/hipe/hipe_amd64_glue.S | 19 +++++----- erts/emulator/hipe/hipe_amd64_glue.h | 19 +++++----- erts/emulator/hipe/hipe_amd64_primops.h | 19 +++++----- erts/emulator/hipe/hipe_arch.h | 19 +++++----- erts/emulator/hipe/hipe_arm.c | 19 +++++----- erts/emulator/hipe/hipe_arm.h | 19 +++++----- erts/emulator/hipe/hipe_arm.tab | 19 +++++----- erts/emulator/hipe/hipe_arm_asm.m4 | 19 +++++----- erts/emulator/hipe/hipe_arm_bifs.m4 | 19 +++++----- erts/emulator/hipe/hipe_arm_gc.h | 19 +++++----- erts/emulator/hipe/hipe_arm_glue.S | 19 +++++----- erts/emulator/hipe/hipe_arm_glue.h | 19 +++++----- erts/emulator/hipe/hipe_arm_primops.h | 19 +++++----- erts/emulator/hipe/hipe_bif0.c | 19 +++++----- erts/emulator/hipe/hipe_bif0.h | 19 +++++----- erts/emulator/hipe/hipe_bif0.tab | 19 +++++----- erts/emulator/hipe/hipe_bif1.c | 21 +++++------ erts/emulator/hipe/hipe_bif1.h | 19 +++++----- erts/emulator/hipe/hipe_bif1.tab | 21 +++++------ erts/emulator/hipe/hipe_bif2.c | 19 +++++----- erts/emulator/hipe/hipe_bif2.tab | 21 +++++------ erts/emulator/hipe/hipe_bif64.c | 19 +++++----- erts/emulator/hipe/hipe_bif64.h | 19 +++++----- erts/emulator/hipe/hipe_bif64.tab | 19 +++++----- erts/emulator/hipe/hipe_bif_list.m4 | 19 +++++----- erts/emulator/hipe/hipe_debug.c | 19 +++++----- erts/emulator/hipe/hipe_debug.h | 19 +++++----- erts/emulator/hipe/hipe_gbif_list.h | 19 +++++----- erts/emulator/hipe/hipe_gc.c | 19 +++++----- erts/emulator/hipe/hipe_gc.h | 19 +++++----- erts/emulator/hipe/hipe_mkliterals.c | 19 +++++----- erts/emulator/hipe/hipe_mode_switch.c | 19 +++++----- erts/emulator/hipe/hipe_mode_switch.h | 19 +++++----- erts/emulator/hipe/hipe_native_bif.c | 19 +++++----- erts/emulator/hipe/hipe_native_bif.h | 19 +++++----- erts/emulator/hipe/hipe_ops.tab | 19 +++++----- erts/emulator/hipe/hipe_ppc.c | 19 +++++----- erts/emulator/hipe/hipe_ppc.h | 19 +++++----- erts/emulator/hipe/hipe_ppc.tab | 19 +++++----- erts/emulator/hipe/hipe_ppc64.tab | 21 +++++------ erts/emulator/hipe/hipe_ppc_asm.m4 | 19 +++++----- erts/emulator/hipe/hipe_ppc_bifs.m4 | 19 +++++----- erts/emulator/hipe/hipe_ppc_gc.h | 19 +++++----- erts/emulator/hipe/hipe_ppc_glue.S | 19 +++++----- erts/emulator/hipe/hipe_ppc_glue.h | 19 +++++----- erts/emulator/hipe/hipe_ppc_primops.h | 19 +++++----- erts/emulator/hipe/hipe_primops.h | 19 +++++----- erts/emulator/hipe/hipe_process.h | 19 +++++----- erts/emulator/hipe/hipe_risc_gc.h | 19 +++++----- erts/emulator/hipe/hipe_risc_glue.h | 19 +++++----- erts/emulator/hipe/hipe_risc_stack.c | 19 +++++----- erts/emulator/hipe/hipe_signal.h | 19 +++++----- erts/emulator/hipe/hipe_sparc.c | 19 +++++----- erts/emulator/hipe/hipe_sparc.h | 19 +++++----- erts/emulator/hipe/hipe_sparc.tab | 19 +++++----- erts/emulator/hipe/hipe_sparc_asm.m4 | 19 +++++----- erts/emulator/hipe/hipe_sparc_bifs.m4 | 19 +++++----- erts/emulator/hipe/hipe_sparc_gc.h | 19 +++++----- erts/emulator/hipe/hipe_sparc_glue.S | 19 +++++----- erts/emulator/hipe/hipe_sparc_glue.h | 19 +++++----- erts/emulator/hipe/hipe_sparc_primops.h | 19 +++++----- erts/emulator/hipe/hipe_stack.c | 19 +++++----- erts/emulator/hipe/hipe_stack.h | 19 +++++----- erts/emulator/hipe/hipe_x86.c | 19 +++++----- erts/emulator/hipe/hipe_x86.h | 19 +++++----- erts/emulator/hipe/hipe_x86.tab | 19 +++++----- erts/emulator/hipe/hipe_x86_abi.txt | 19 +++++----- erts/emulator/hipe/hipe_x86_asm.m4 | 19 +++++----- erts/emulator/hipe/hipe_x86_bifs.m4 | 19 +++++----- erts/emulator/hipe/hipe_x86_gc.h | 19 +++++----- erts/emulator/hipe/hipe_x86_glue.S | 19 +++++----- erts/emulator/hipe/hipe_x86_glue.h | 19 +++++----- erts/emulator/hipe/hipe_x86_primops.h | 19 +++++----- erts/emulator/hipe/hipe_x86_signal.c | 19 +++++----- erts/emulator/hipe/hipe_x86_stack.c | 19 +++++----- erts/emulator/internal_doc/dec.erl | 40 ++++++++++----------- erts/emulator/internal_doc/erl_ext_dist.txt | 19 +++++----- erts/emulator/pcre/pcre.mk | 19 +++++----- erts/emulator/sys/common/erl_check_io.c | 21 +++++------ erts/emulator/sys/common/erl_check_io.h | 21 +++++------ erts/emulator/sys/common/erl_mmap.c | 19 +++++----- erts/emulator/sys/common/erl_mmap.h | 19 +++++----- erts/emulator/sys/common/erl_mseg.c | 19 +++++----- erts/emulator/sys/common/erl_mseg.h | 19 +++++----- erts/emulator/sys/common/erl_mtrace_sys_wrap.c | 21 +++++------ .../sys/common/erl_os_monotonic_time_extender.c | 21 +++++------ .../sys/common/erl_os_monotonic_time_extender.h | 21 +++++------ erts/emulator/sys/common/erl_poll.c | 19 +++++----- erts/emulator/sys/common/erl_poll.h | 21 +++++------ erts/emulator/sys/common/erl_sys_common_misc.c | 21 +++++------ erts/emulator/sys/common/erl_util_queue.h | 19 +++++----- erts/emulator/sys/ose/driver_int.h | 19 +++++----- erts/emulator/sys/ose/erl_main.c | 19 +++++----- erts/emulator/sys/ose/erl_ose_sys.h | 19 +++++----- erts/emulator/sys/ose/erl_ose_sys_ddll.c | 19 +++++----- erts/emulator/sys/ose/erl_poll.c | 19 +++++----- erts/emulator/sys/ose/sys.c | 19 +++++----- erts/emulator/sys/ose/sys_float.c | 19 +++++----- erts/emulator/sys/ose/sys_time.c | 19 +++++----- erts/emulator/sys/unix/driver_int.h | 21 +++++------ erts/emulator/sys/unix/erl_child_setup.c | 21 +++++------ erts/emulator/sys/unix/erl_main.c | 21 +++++------ erts/emulator/sys/unix/erl_unix_sys.h | 21 +++++------ erts/emulator/sys/unix/erl_unix_sys_ddll.c | 21 +++++------ erts/emulator/sys/unix/sys.c | 19 +++++----- erts/emulator/sys/unix/sys_float.c | 19 +++++----- erts/emulator/sys/unix/sys_time.c | 21 +++++------ erts/emulator/sys/win32/dosmap.c | 21 +++++------ erts/emulator/sys/win32/driver_int.h | 21 +++++------ erts/emulator/sys/win32/erl_main.c | 21 +++++------ erts/emulator/sys/win32/erl_poll.c | 19 +++++----- erts/emulator/sys/win32/erl_win32_sys_ddll.c | 21 +++++------ erts/emulator/sys/win32/erl_win_dyn_driver.h | 21 +++++------ erts/emulator/sys/win32/erl_win_sys.h | 21 +++++------ erts/emulator/sys/win32/sys.c | 19 +++++----- erts/emulator/sys/win32/sys_env.c | 19 +++++----- erts/emulator/sys/win32/sys_float.c | 21 +++++------ erts/emulator/sys/win32/sys_interrupt.c | 21 +++++------ erts/emulator/sys/win32/sys_time.c | 21 +++++------ erts/emulator/test/Makefile | 19 +++++----- erts/emulator/test/a_SUITE.erl | 21 +++++------ erts/emulator/test/after_SUITE.erl | 21 +++++------ erts/emulator/test/alloc_SUITE.erl | 21 +++++------ erts/emulator/test/alloc_SUITE_data/Makefile.src | 21 +++++------ .../test/alloc_SUITE_data/allocator_test.h | 21 +++++------ erts/emulator/test/alloc_SUITE_data/basic.c | 21 +++++------ erts/emulator/test/alloc_SUITE_data/bucket_index.c | 21 +++++------ erts/emulator/test/alloc_SUITE_data/bucket_mask.c | 21 +++++------ erts/emulator/test/alloc_SUITE_data/coalesce.c | 21 +++++------ erts/emulator/test/alloc_SUITE_data/cpool.c | 19 +++++----- .../test/alloc_SUITE_data/mseg_clear_cache.c | 21 +++++------ erts/emulator/test/alloc_SUITE_data/rbtree.c | 21 +++++------ erts/emulator/test/alloc_SUITE_data/realloc_copy.c | 21 +++++------ .../test/alloc_SUITE_data/testcase_driver.c | 21 +++++------ .../test/alloc_SUITE_data/testcase_driver.h | 21 +++++------ erts/emulator/test/alloc_SUITE_data/threads.c | 21 +++++------ erts/emulator/test/beam_SUITE.erl | 19 +++++----- erts/emulator/test/beam_literals_SUITE.erl | 21 +++++------ erts/emulator/test/bif_SUITE.erl | 19 +++++----- erts/emulator/test/big_SUITE.erl | 21 +++++------ erts/emulator/test/big_SUITE_data/literal_test.erl | 21 +++++------ erts/emulator/test/binary_SUITE.erl | 19 +++++----- erts/emulator/test/bs_bincomp_SUITE.erl | 21 +++++------ erts/emulator/test/bs_bit_binaries_SUITE.erl | 21 +++++------ erts/emulator/test/bs_construct_SUITE.erl | 21 +++++------ erts/emulator/test/bs_match_bin_SUITE.erl | 21 +++++------ erts/emulator/test/bs_match_int_SUITE.erl | 21 +++++------ erts/emulator/test/bs_match_misc_SUITE.erl | 21 +++++------ erts/emulator/test/bs_match_tail_SUITE.erl | 21 +++++------ erts/emulator/test/bs_utf_SUITE.erl | 21 +++++------ erts/emulator/test/busy_port_SUITE.erl | 19 +++++----- .../test/busy_port_SUITE_data/Makefile.src | 21 +++++------ .../test/busy_port_SUITE_data/hard_busy_drv.c | 21 +++++------ .../test/busy_port_SUITE_data/hs_busy_drv.c | 21 +++++------ .../test/busy_port_SUITE_data/scheduling_drv.c | 21 +++++------ .../test/busy_port_SUITE_data/soft_busy_drv.c | 21 +++++------ erts/emulator/test/call_trace_SUITE.erl | 21 +++++------ erts/emulator/test/code_SUITE.erl | 21 +++++------ .../test/code_SUITE_data/another_code_test.erl | 21 +++++------ erts/emulator/test/code_SUITE_data/cpbugx.erl | 21 +++++------ .../test/code_SUITE_data/fun_confusion.erl | 21 +++++------ erts/emulator/test/code_SUITE_data/literals.erl | 21 +++++------ erts/emulator/test/code_SUITE_data/many_funs.erl | 21 +++++------ .../emulator/test/code_SUITE_data/my_code_test.erl | 21 +++++------ erts/emulator/test/code_SUITE_data/versions.erl | 19 +++++----- erts/emulator/test/code_parallel_load_SUITE.erl | 19 +++++----- erts/emulator/test/crypto_SUITE.erl | 21 +++++------ erts/emulator/test/crypto_reference.erl | 21 +++++------ erts/emulator/test/ddll_SUITE.erl | 21 +++++------ erts/emulator/test/decode_packet_SUITE.erl | 19 +++++----- erts/emulator/test/dgawd_handler.erl | 21 +++++------ erts/emulator/test/distribution_SUITE.erl | 19 +++++----- erts/emulator/test/distribution_SUITE_data/run.erl | 21 +++++------ erts/emulator/test/driver_SUITE.erl | 21 +++++------ .../test/driver_SUITE_data/async_blast_drv.c | 19 +++++----- erts/emulator/test/driver_SUITE_data/caller_drv.c | 21 +++++------ erts/emulator/test/driver_SUITE_data/chkio_drv.c | 21 +++++------ .../test/driver_SUITE_data/consume_timeslice_drv.c | 19 +++++----- .../invalid_extended_marker_drv.c | 21 +++++------ .../test/driver_SUITE_data/io_ready_exit_drv.c | 21 +++++------ .../emulator/test/driver_SUITE_data/ioq_exit_drv.c | 19 +++++----- .../test/driver_SUITE_data/larger_major_vsn_drv.c | 21 +++++------ .../test/driver_SUITE_data/larger_minor_vsn_drv.c | 21 +++++------ .../test/driver_SUITE_data/missing_callback_drv.c | 21 +++++------ erts/emulator/test/driver_SUITE_data/monitor_drv.c | 21 +++++------ .../emulator/test/driver_SUITE_data/otp_6879_drv.c | 21 +++++------ .../emulator/test/driver_SUITE_data/otp_9302_drv.c | 19 +++++----- .../peek_non_existing_queue_drv.c | 21 +++++------ .../test/driver_SUITE_data/smaller_major_vsn_drv.c | 21 +++++------ .../test/driver_SUITE_data/smaller_minor_vsn_drv.c | 21 +++++------ .../test/driver_SUITE_data/sys_info_base_drv.c | 19 +++++----- .../test/driver_SUITE_data/sys_info_curr_drv.c | 21 +++++------ .../test/driver_SUITE_data/sys_info_drv_impl.c | 21 +++++------ .../test/driver_SUITE_data/sys_info_drv_impl.h | 21 +++++------ .../test/driver_SUITE_data/sys_info_prev_drv.c | 19 +++++----- .../test/driver_SUITE_data/thr_alloc_drv.c | 21 +++++------ .../emulator/test/driver_SUITE_data/thr_free_drv.c | 19 +++++----- .../test/driver_SUITE_data/thr_msg_blast_drv.c | 19 +++++----- .../test/driver_SUITE_data/vsn_mismatch_drv_impl.c | 21 +++++------ .../zero_extended_marker_garb_drv.c | 21 +++++------ erts/emulator/test/efile_SUITE.erl | 21 +++++------ erts/emulator/test/erl_drv_thread_SUITE.erl | 21 +++++------ .../test/erl_drv_thread_SUITE_data/Makefile.src | 21 +++++------ .../test/erl_drv_thread_SUITE_data/basic.c | 21 +++++------ .../test/erl_drv_thread_SUITE_data/rwlock.c | 21 +++++------ .../erl_drv_thread_SUITE_data/testcase_driver.c | 21 +++++------ .../erl_drv_thread_SUITE_data/testcase_driver.h | 21 +++++------ erts/emulator/test/erl_drv_thread_SUITE_data/tsd.c | 21 +++++------ erts/emulator/test/erl_link_SUITE.erl | 21 +++++------ erts/emulator/test/erts_debug_SUITE.erl | 21 +++++------ erts/emulator/test/estone_SUITE.erl | 21 +++++------ erts/emulator/test/evil_SUITE.erl | 21 +++++------ erts/emulator/test/exception_SUITE.erl | 21 +++++------ erts/emulator/test/float_SUITE.erl | 21 +++++------ erts/emulator/test/float_SUITE_data/fp_drv.c | 21 +++++------ .../emulator/test/float_SUITE_data/has_fpe_bug.erl | 21 +++++------ erts/emulator/test/fun_SUITE.erl | 19 +++++----- erts/emulator/test/fun_r13_SUITE.erl | 19 +++++----- erts/emulator/test/gc_SUITE.erl | 21 +++++------ erts/emulator/test/guard_SUITE.erl | 19 +++++----- erts/emulator/test/hash_SUITE.erl | 21 +++++------ erts/emulator/test/hibernate_SUITE.erl | 21 +++++------ erts/emulator/test/ignore_cores.erl | 19 +++++----- erts/emulator/test/list_bif_SUITE.erl | 21 +++++------ erts/emulator/test/long_timers_test.erl | 21 +++++------ erts/emulator/test/map_SUITE.erl | 21 +++++------ erts/emulator/test/match_spec_SUITE.erl | 21 +++++------ erts/emulator/test/module_info_SUITE.erl | 21 +++++------ .../module_info_SUITE_data/module_info_test.erl | 22 ++++++------ erts/emulator/test/monitor_SUITE.erl | 21 +++++------ erts/emulator/test/mtx_SUITE.erl | 19 +++++----- erts/emulator/test/mtx_SUITE_data/Makefile.src | 21 +++++------ erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c | 19 +++++----- erts/emulator/test/nested_SUITE.erl | 21 +++++------ erts/emulator/test/nif_SUITE.erl | 19 +++++----- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 19 +++++----- erts/emulator/test/nif_SUITE_data/nif_mod.c | 19 +++++----- erts/emulator/test/nif_SUITE_data/nif_mod.erl | 21 +++++------ .../emulator/test/nif_SUITE_data/testcase_driver.h | 21 +++++------ erts/emulator/test/node_container_SUITE.erl | 21 +++++------ erts/emulator/test/nofrag_SUITE.erl | 21 +++++------ erts/emulator/test/num_bif_SUITE.erl | 21 +++++------ erts/emulator/test/old_mod.erl | 21 +++++------ erts/emulator/test/old_scheduler_SUITE.erl | 21 +++++------ erts/emulator/test/op_SUITE.erl | 21 +++++------ erts/emulator/test/port_SUITE.erl | 19 +++++----- erts/emulator/test/port_SUITE_data/dead_port.c | 19 +++++----- erts/emulator/test/port_SUITE_data/port_test.erl | 21 +++++------ erts/emulator/test/port_bif_SUITE.erl | 21 +++++------ erts/emulator/test/process_SUITE.erl | 19 +++++----- erts/emulator/test/pseudoknot_SUITE.erl | 21 +++++------ erts/emulator/test/random_iolist.erl | 21 +++++------ erts/emulator/test/receive_SUITE.erl | 19 +++++----- erts/emulator/test/ref_SUITE.erl | 21 +++++------ erts/emulator/test/register_SUITE.erl | 21 +++++------ erts/emulator/test/save_calls_SUITE.erl | 21 +++++------ erts/emulator/test/scheduler_SUITE.erl | 19 +++++----- erts/emulator/test/send_term_SUITE.erl | 42 +++++++++++----------- .../emulator/test/send_term_SUITE_data/ext_terms.h | 21 +++++------ .../test/send_term_SUITE_data/send_term_drv.c | 21 +++++------ erts/emulator/test/sensitive_SUITE.erl | 21 +++++------ erts/emulator/test/signal_SUITE.erl | 21 +++++------ erts/emulator/test/smoke_test_SUITE.erl | 19 +++++----- erts/emulator/test/statistics_SUITE.erl | 19 +++++----- erts/emulator/test/system_info_SUITE.erl | 19 +++++----- erts/emulator/test/system_profile_SUITE.erl | 21 +++++------ erts/emulator/test/time_SUITE.erl | 21 +++++------ erts/emulator/test/timer_bif_SUITE.erl | 21 +++++------ erts/emulator/test/trace_SUITE.erl | 19 +++++----- erts/emulator/test/trace_bif_SUITE.erl | 21 +++++------ erts/emulator/test/trace_call_count_SUITE.erl | 21 +++++------ erts/emulator/test/trace_call_time_SUITE.erl | 19 +++++----- erts/emulator/test/trace_local_SUITE.erl | 21 +++++------ .../trace_local_SUITE_data/trace_local_dummy.erl | 21 +++++------ erts/emulator/test/trace_meta_SUITE.erl | 21 +++++------ erts/emulator/test/trace_nif_SUITE.erl | 21 +++++------ erts/emulator/test/trace_port_SUITE.erl | 21 +++++------ erts/emulator/test/tuple_SUITE.erl | 21 +++++------ erts/emulator/test/unique_SUITE.erl | 21 +++++------ erts/emulator/test/z_SUITE.erl | 21 +++++------ erts/emulator/utils/beam_makeops | 19 +++++----- erts/emulator/utils/beam_strip | 21 +++++------ erts/emulator/utils/count | 19 +++++----- erts/emulator/utils/loaded | 19 +++++----- erts/emulator/utils/make_alloc_types | 42 +++++++++++----------- erts/emulator/utils/make_compiler_flags | 19 +++++----- erts/emulator/utils/make_driver_tab | 21 +++++------ erts/emulator/utils/make_preload | 21 +++++------ erts/emulator/utils/make_tables | 19 +++++----- erts/emulator/utils/make_version | 21 +++++------ erts/emulator/utils/mkver.c | 21 +++++------ erts/emulator/zlib/zlib.mk | 19 +++++----- erts/epmd/Makefile | 21 +++++------ erts/epmd/epmd.mk | 21 +++++------ erts/epmd/src/Makefile | 21 +++++------ erts/epmd/src/Makefile.in | 21 +++++------ erts/epmd/src/epmd.c | 19 +++++----- erts/epmd/src/epmd.h | 21 +++++------ erts/epmd/src/epmd_cli.c | 21 +++++------ erts/epmd/src/epmd_int.h | 19 +++++----- erts/epmd/src/epmd_srv.c | 19 +++++----- erts/epmd/test/Makefile | 19 +++++----- erts/epmd/test/epmd_SUITE.erl | 19 +++++----- erts/etc/Makefile | 21 +++++------ erts/etc/common/Makefile | 21 +++++------ erts/etc/common/Makefile.in | 19 +++++----- erts/etc/common/ct_run.c | 19 +++++----- erts/etc/common/dialyzer.c | 21 +++++------ erts/etc/common/erlc.c | 19 +++++----- erts/etc/common/erlexec.c | 19 +++++----- erts/etc/common/escript.c | 19 +++++----- erts/etc/common/heart.c | 21 +++++------ erts/etc/common/inet_gethost.c | 19 +++++----- erts/etc/common/run_erl_common.c | 19 +++++----- erts/etc/common/run_erl_common.h | 19 +++++----- erts/etc/common/run_erl_vsn.h | 19 +++++----- erts/etc/common/safe_string.c | 19 +++++----- erts/etc/common/safe_string.h | 19 +++++----- erts/etc/common/to_erl_common.c | 19 +++++----- erts/etc/common/to_erl_common.h | 19 +++++----- erts/etc/common/typer.c | 21 +++++------ erts/etc/ose/run_erl.c | 19 +++++----- erts/etc/ose/run_erl.h | 19 +++++----- erts/etc/ose/run_erl_main.c | 19 +++++----- erts/etc/unix/Install.src | 19 +++++----- erts/etc/unix/Makefile | 19 +++++----- erts/etc/unix/README | 19 +++++----- erts/etc/unix/RELNOTES | 19 +++++----- erts/etc/unix/cerl.src | 19 +++++----- erts/etc/unix/dyn_erl.c | 21 +++++------ erts/etc/unix/erl.src.src | 21 +++++------ erts/etc/unix/etp-commands.in | 21 +++++------ erts/etc/unix/etp-thr.py | 19 +++++----- erts/etc/unix/etp_commands.erl | 21 +++++------ erts/etc/unix/etp_commands.mk | 21 +++++------ erts/etc/unix/format_man_pages | 21 +++++------ erts/etc/unix/run_erl.c | 21 +++++------ erts/etc/unix/setuid_socket_wrap.c | 21 +++++------ erts/etc/unix/start.src | 21 +++++------ erts/etc/unix/start_erl.src | 21 +++++------ erts/etc/unix/to_erl.c | 21 +++++------ erts/etc/win32/Install.c | 19 +++++----- erts/etc/win32/Makefile | 21 +++++------ erts/etc/win32/Nmakefile.start_erl | 21 +++++------ erts/etc/win32/beam.rc | 21 +++++------ erts/etc/win32/cygwin_tools/erl | 21 +++++------ erts/etc/win32/cygwin_tools/erlc | 21 +++++------ erts/etc/win32/cygwin_tools/javac.sh | 21 +++++------ erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh | 21 +++++------ erts/etc/win32/cygwin_tools/make_local_ini.sh | 21 +++++------ erts/etc/win32/cygwin_tools/mingw/ar.sh | 21 +++++------ erts/etc/win32/cygwin_tools/mingw/cc.sh | 21 +++++------ erts/etc/win32/cygwin_tools/mingw/coffix.c | 21 +++++------ erts/etc/win32/cygwin_tools/mingw/emu_cc.sh | 21 +++++------ erts/etc/win32/cygwin_tools/mingw/ld.sh | 21 +++++------ erts/etc/win32/cygwin_tools/mingw/mc.sh | 21 +++++------ erts/etc/win32/cygwin_tools/mingw/rc.sh | 21 +++++------ erts/etc/win32/cygwin_tools/vc/ar.sh | 21 +++++------ erts/etc/win32/cygwin_tools/vc/cc.sh | 21 +++++------ erts/etc/win32/cygwin_tools/vc/cc_wrap.c | 21 +++++------ erts/etc/win32/cygwin_tools/vc/coffix.c | 21 +++++------ erts/etc/win32/cygwin_tools/vc/emu_cc.sh | 21 +++++------ erts/etc/win32/cygwin_tools/vc/ld.sh | 21 +++++------ erts/etc/win32/cygwin_tools/vc/ld_wrap.c | 21 +++++------ erts/etc/win32/cygwin_tools/vc/mc.sh | 21 +++++------ erts/etc/win32/cygwin_tools/vc/rc.sh | 19 +++++----- erts/etc/win32/erl.c | 21 +++++------ erts/etc/win32/erl.rc | 21 +++++------ erts/etc/win32/erl_log.c | 21 +++++------ erts/etc/win32/erlsrv/erlsrv_global.h | 21 +++++------ erts/etc/win32/erlsrv/erlsrv_interactive.c | 21 +++++------ erts/etc/win32/erlsrv/erlsrv_interactive.h | 21 +++++------ erts/etc/win32/erlsrv/erlsrv_main.c | 21 +++++------ erts/etc/win32/erlsrv/erlsrv_registry.c | 21 +++++------ erts/etc/win32/erlsrv/erlsrv_registry.h | 21 +++++------ erts/etc/win32/erlsrv/erlsrv_service.c | 21 +++++------ erts/etc/win32/erlsrv/erlsrv_service.h | 21 +++++------ erts/etc/win32/erlsrv/erlsrv_util.c | 21 +++++------ erts/etc/win32/erlsrv/erlsrv_util.h | 21 +++++------ erts/etc/win32/init_file.c | 21 +++++------ erts/etc/win32/init_file.h | 21 +++++------ erts/etc/win32/msys_tools/erl | 21 +++++------ erts/etc/win32/msys_tools/erlc | 21 +++++------ erts/etc/win32/msys_tools/javac.sh | 21 +++++------ erts/etc/win32/msys_tools/make_bootstrap_ini.sh | 21 +++++------ erts/etc/win32/msys_tools/make_local_ini.sh | 21 +++++------ erts/etc/win32/msys_tools/vc/ar.sh | 21 +++++------ erts/etc/win32/msys_tools/vc/cc.sh | 21 +++++------ erts/etc/win32/msys_tools/vc/coffix.c | 21 +++++------ erts/etc/win32/msys_tools/vc/emu_cc.sh | 21 +++++------ erts/etc/win32/msys_tools/vc/ld.sh | 21 +++++------ erts/etc/win32/msys_tools/vc/mc.sh | 21 +++++------ erts/etc/win32/msys_tools/vc/rc.sh | 21 +++++------ erts/etc/win32/nsis/Makefile | 21 +++++------ erts/etc/win32/nsis/dll_version_helper.sh | 21 +++++------ erts/etc/win32/nsis/erlang20.nsi | 19 +++++----- erts/etc/win32/nsis/find_redist.sh | 21 +++++------ erts/etc/win32/port_entry.c | 21 +++++------ erts/etc/win32/resource.h | 21 +++++------ erts/etc/win32/start_erl.c | 21 +++++------ erts/etc/win32/win_erlexec.c | 21 +++++------ erts/example/Makefile | 21 +++++------ erts/example/matrix_nif.c | 19 +++++----- erts/example/matrix_nif.erl | 21 +++++------ erts/example/next_perm.cc | 21 +++++------ erts/example/next_perm.erl | 21 +++++------ erts/example/pg_async.c | 21 +++++------ erts/example/pg_async.erl | 21 +++++------ erts/example/pg_async2.c | 21 +++++------ erts/example/pg_async2.erl | 21 +++++------ erts/example/pg_encode.c | 21 +++++------ erts/example/pg_encode.h | 21 +++++------ erts/example/pg_encode2.c | 21 +++++------ erts/example/pg_encode2.h | 21 +++++------ erts/example/pg_sync.c | 21 +++++------ erts/example/pg_sync.erl | 21 +++++------ erts/example/time_compat.erl | 21 +++++------ erts/include/erl_fixed_size_int_types.h | 21 +++++------ erts/include/erl_int_sizes_config.h.in | 19 +++++----- erts/include/erl_memory_trace_parser.h | 21 +++++------ erts/include/erl_native_features_config.h.in | 19 +++++----- erts/include/internal/README | 19 +++++----- erts/include/internal/erl_errno.h | 21 +++++------ erts/include/internal/erl_memory_trace_protocol.h | 21 +++++------ erts/include/internal/erl_misc_utils.h | 21 +++++------ erts/include/internal/erl_printf.h | 21 +++++------ erts/include/internal/erl_printf_format.h | 21 +++++------ erts/include/internal/erts_internal.mk.in | 21 +++++------ erts/include/internal/ethr_atomics.h | 19 +++++----- erts/include/internal/ethr_internal.h | 19 +++++----- erts/include/internal/ethr_mutex.h | 19 +++++----- erts/include/internal/ethr_optimized_fallbacks.h | 19 +++++----- erts/include/internal/ethread.h | 19 +++++----- erts/include/internal/ethread.mk.in | 21 +++++------ erts/include/internal/ethread_header_config.h.in | 19 +++++----- erts/include/internal/ethread_inline.h | 19 +++++----- erts/include/internal/gcc/ethr_atomic.h | 19 +++++----- erts/include/internal/gcc/ethr_dw_atomic.h | 19 +++++----- erts/include/internal/gcc/ethr_membar.h | 21 +++++------ erts/include/internal/gcc/ethread.h | 19 +++++----- erts/include/internal/i386/atomic.h | 19 +++++----- erts/include/internal/i386/ethr_dw_atomic.h | 19 +++++----- erts/include/internal/i386/ethr_membar.h | 19 +++++----- erts/include/internal/i386/ethread.h | 21 +++++------ erts/include/internal/i386/rwlock.h | 19 +++++----- erts/include/internal/i386/spinlock.h | 19 +++++----- erts/include/internal/libatomic_ops/ethr_atomic.h | 19 +++++----- .../internal/libatomic_ops/ethr_dw_atomic.h | 19 +++++----- erts/include/internal/libatomic_ops/ethr_membar.h | 19 +++++----- erts/include/internal/libatomic_ops/ethread.h | 19 +++++----- erts/include/internal/ose/ethr_event.h | 19 +++++----- erts/include/internal/ppc32/atomic.h | 19 +++++----- erts/include/internal/ppc32/ethr_membar.h | 19 +++++----- erts/include/internal/ppc32/ethread.h | 21 +++++------ erts/include/internal/ppc32/rwlock.h | 19 +++++----- erts/include/internal/ppc32/spinlock.h | 19 +++++----- erts/include/internal/pthread/ethr_event.h | 19 +++++----- erts/include/internal/sparc32/atomic.h | 19 +++++----- erts/include/internal/sparc32/ethr_membar.h | 19 +++++----- erts/include/internal/sparc32/ethread.h | 21 +++++------ erts/include/internal/sparc32/rwlock.h | 19 +++++----- erts/include/internal/sparc32/spinlock.h | 19 +++++----- erts/include/internal/sparc64/ethread.h | 21 +++++------ erts/include/internal/tile/atomic.h | 19 +++++----- erts/include/internal/tile/ethr_membar.h | 19 +++++----- erts/include/internal/tile/ethread.h | 21 +++++------ erts/include/internal/win/ethr_atomic.h | 19 +++++----- erts/include/internal/win/ethr_dw_atomic.h | 19 +++++----- erts/include/internal/win/ethr_event.h | 19 +++++----- erts/include/internal/win/ethr_membar.h | 19 +++++----- erts/include/internal/win/ethread.h | 19 +++++----- erts/include/internal/x86_64/ethread.h | 21 +++++------ erts/lib/internal/README | 19 +++++----- erts/lib_src/Makefile | 21 +++++------ erts/lib_src/Makefile.in | 19 +++++----- erts/lib_src/common/erl_memory_trace_parser.c | 21 +++++------ erts/lib_src/common/erl_misc_utils.c | 19 +++++----- erts/lib_src/common/erl_printf.c | 21 +++++------ erts/lib_src/common/erl_printf_format.c | 21 +++++------ erts/lib_src/common/ethr_atomics.c | 19 +++++----- erts/lib_src/common/ethr_aux.c | 19 +++++----- erts/lib_src/common/ethr_cbf.c | 19 +++++----- erts/lib_src/common/ethr_mutex.c | 19 +++++----- erts/lib_src/ose/ethr_event.c | 19 +++++----- erts/lib_src/ose/ethread.c | 19 +++++----- erts/lib_src/pthread/ethr_event.c | 19 +++++----- erts/lib_src/pthread/ethr_x86_sse2_asm.c | 19 +++++----- erts/lib_src/pthread/ethread.c | 19 +++++----- erts/lib_src/utils/make_atomics_api | 38 ++++++++++---------- erts/lib_src/win/ethr_event.c | 19 +++++----- erts/lib_src/win/ethread.c | 19 +++++----- erts/preloaded/Makefile | 21 +++++------ erts/preloaded/src/Makefile | 21 +++++------ erts/preloaded/src/add_abstract_code | 19 +++++----- erts/preloaded/src/erl_prim_loader.erl | 19 +++++----- erts/preloaded/src/erlang.erl | 19 +++++----- erts/preloaded/src/erts.app.src | 21 +++++------ erts/preloaded/src/erts_internal.erl | 19 +++++----- erts/preloaded/src/init.erl | 19 +++++----- erts/preloaded/src/otp_ring0.erl | 19 +++++----- erts/preloaded/src/prim_eval.S | 19 +++++----- erts/preloaded/src/prim_eval.erl | 19 +++++----- erts/preloaded/src/prim_file.erl | 19 +++++----- erts/preloaded/src/prim_inet.erl | 21 +++++------ erts/preloaded/src/prim_zip.erl | 19 +++++----- erts/preloaded/src/zip_internal.hrl | 21 +++++------ erts/preloaded/src/zlib.erl | 19 +++++----- erts/start_scripts/Makefile | 19 +++++----- erts/start_scripts/no_dot_erlang.rel.src | 19 +++++----- erts/start_scripts/start_all_example.rel.src | 21 +++++------ erts/start_scripts/start_clean.rel.src | 21 +++++------ erts/start_scripts/start_sasl.rel.src | 21 +++++------ erts/test/Makefile | 19 +++++----- erts/test/erl_print_SUITE.erl | 19 +++++----- erts/test/erl_print_SUITE_data/Makefile.src | 21 +++++------ erts/test/erl_print_SUITE_data/character_test.h | 21 +++++------ erts/test/erl_print_SUITE_data/erl_print_tests.c | 42 +++++++++++----------- erts/test/erl_print_SUITE_data/integer_64_test.h | 21 +++++------ erts/test/erl_print_SUITE_data/integer_test.h | 21 +++++------ erts/test/erl_print_SUITE_data/snprintf_test.h | 21 +++++------ erts/test/erl_print_SUITE_data/string_test.h | 21 +++++------ erts/test/erlc_SUITE.erl | 19 +++++----- erts/test/erlc_SUITE_data/include/erl_test.hrl | 21 +++++------ erts/test/erlc_SUITE_data/src/erl_test_bad.erl | 21 +++++------ .../src/erl_test_missing_header.erl | 19 +++++----- erts/test/erlc_SUITE_data/src/erl_test_ok.erl | 21 +++++------ erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl | 21 +++++------ erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl | 21 +++++------ erts/test/erlexec_SUITE.erl | 19 +++++----- erts/test/erlexec_SUITE_data/Makefile.src | 21 +++++------ erts/test/erlexec_SUITE_data/erlexec_tests.c | 19 +++++----- erts/test/ethread_SUITE.erl | 19 +++++----- erts/test/ethread_SUITE_data/Makefile.src | 21 +++++------ erts/test/ethread_SUITE_data/ethread_tests.c | 19 +++++----- erts/test/ignore_cores.erl | 19 +++++----- erts/test/install_SUITE.erl | 19 +++++----- erts/test/nt_SUITE.erl | 19 +++++----- erts/test/nt_SUITE_data/Makefile.src | 21 +++++------ erts/test/nt_SUITE_data/nt_info.c | 21 +++++------ erts/test/otp_SUITE.erl | 19 +++++----- erts/test/run_erl_SUITE.erl | 19 +++++----- erts/test/run_erl_SUITE_data/defuncter.pl | 21 +++++------ erts/test/run_erl_SUITE_data/run_erl_test.pl | 21 +++++------ erts/test/upgrade_SUITE.erl | 19 +++++----- erts/test/upgrade_SUITE_data/start.src | 21 +++++------ erts/test/utils/gccifier.c | 21 +++++------ erts/test/utils/gccifier.sh | 19 +++++----- erts/test/z_SUITE.erl | 19 +++++----- erts/vsn.mk | 19 +++++----- 798 files changed, 8430 insertions(+), 7630 deletions(-) (limited to 'erts') diff --git a/erts/AUTHORS b/erts/AUTHORS index dcf92c34da..d8746f65b2 100644 --- a/erts/AUTHORS +++ b/erts/AUTHORS @@ -3,16 +3,17 @@ Copyright Ericsson AB 1999-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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - 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. + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. %CopyrightEnd% diff --git a/erts/Makefile.in b/erts/Makefile.in index 47298cccba..feed00dad5 100644 --- a/erts/Makefile.in +++ b/erts/Makefile.in @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2006-2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 352a0ce43c..01541aff72 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -3,16 +3,17 @@ dnl %CopyrightBegin% dnl dnl Copyright Ericsson AB 1998-2015. 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 -dnl compliance with the License. You should have received a copy of the -dnl Erlang Public License along with this software. If not, it can be -dnl retrieved online at http://www.erlang.org/. -dnl -dnl Software distributed under the License is distributed on an "AS IS" -dnl basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -dnl the License for the specific language governing rights and limitations -dnl under the License. +dnl Licensed under the Apache License, Version 2.0 (the "License"); +dnl you may not use this file except in compliance with the License. +dnl You may obtain a copy of the License at +dnl +dnl http://www.apache.org/licenses/LICENSE-2.0 +dnl +dnl Unless required by applicable law or agreed to in writing, software +dnl distributed under the License is distributed on an "AS IS" BASIS, +dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +dnl See the License for the specific language governing permissions and +dnl limitations under the License. dnl dnl %CopyrightEnd% dnl diff --git a/erts/autoconf/configure.vxworks b/erts/autoconf/configure.vxworks index 23a93faa31..96dd1f8401 100755 --- a/erts/autoconf/configure.vxworks +++ b/erts/autoconf/configure.vxworks @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general index c82b7348cf..5adee4db45 100644 --- a/erts/autoconf/vxworks/sed.general +++ b/erts/autoconf/vxworks/sed.general @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 1997-2013. All Rights Reserved. # -# The 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/autoconf/vxworks/sed.vxworks_cpu32 b/erts/autoconf/vxworks/sed.vxworks_cpu32 index 163dd78897..e3d54246ab 100644 --- a/erts/autoconf/vxworks/sed.vxworks_cpu32 +++ b/erts/autoconf/vxworks/sed.vxworks_cpu32 @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc32 b/erts/autoconf/vxworks/sed.vxworks_ppc32 index f00daef74c..012760e127 100644 --- a/erts/autoconf/vxworks/sed.vxworks_ppc32 +++ b/erts/autoconf/vxworks/sed.vxworks_ppc32 @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2006-2013. All Rights Reserved. # -# The 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc603 b/erts/autoconf/vxworks/sed.vxworks_ppc603 index 40296b8f07..55af52571b 100644 --- a/erts/autoconf/vxworks/sed.vxworks_ppc603 +++ b/erts/autoconf/vxworks/sed.vxworks_ppc603 @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2000-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall b/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall index 2550432d1e..e0c0891aeb 100644 --- a/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall +++ b/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc860 b/erts/autoconf/vxworks/sed.vxworks_ppc860 index cd7c9ff797..8828339e34 100644 --- a/erts/autoconf/vxworks/sed.vxworks_ppc860 +++ b/erts/autoconf/vxworks/sed.vxworks_ppc860 @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/autoconf/vxworks/sed.vxworks_simlinux b/erts/autoconf/vxworks/sed.vxworks_simlinux index 67ddbf1575..950fb79ec5 100644 --- a/erts/autoconf/vxworks/sed.vxworks_simlinux +++ b/erts/autoconf/vxworks/sed.vxworks_simlinux @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2008-2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/autoconf/vxworks/sed.vxworks_simso b/erts/autoconf/vxworks/sed.vxworks_simso index 1af88cef31..6f2796f04e 100644 --- a/erts/autoconf/vxworks/sed.vxworks_simso +++ b/erts/autoconf/vxworks/sed.vxworks_simso @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2005-2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/autoconf/vxworks/sed.vxworks_sparc b/erts/autoconf/vxworks/sed.vxworks_sparc index ae26f234d2..e67c34af26 100644 --- a/erts/autoconf/vxworks/sed.vxworks_sparc +++ b/erts/autoconf/vxworks/sed.vxworks_sparc @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/configure.in b/erts/configure.in index ce0cef871f..22ca7ec17d 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -4,16 +4,17 @@ dnl %CopyrightBegin% dnl dnl Copyright Ericsson AB 1997-2015. 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 -dnl compliance with the License. You should have received a copy of the -dnl Erlang Public License along with this software. If not, it can be -dnl retrieved online at http://www.erlang.org/. +dnl Licensed under the Apache License, Version 2.0 (the "License"); +dnl you may not use this file except in compliance with the License. +dnl You may obtain a copy of the License at dnl -dnl Software distributed under the License is distributed on an "AS IS" -dnl basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -dnl the License for the specific language governing rights and limitations -dnl under the License. +dnl http://www.apache.org/licenses/LICENSE-2.0 +dnl +dnl Unless required by applicable law or agreed to in writing, software +dnl distributed under the License is distributed on an "AS IS" BASIS, +dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +dnl See the License for the specific language governing permissions and +dnl limitations under the License. dnl dnl %CopyrightEnd% diff --git a/erts/doc/Makefile b/erts/doc/Makefile index 8ea3793d90..d415e544f3 100644 --- a/erts/doc/Makefile +++ b/erts/doc/Makefile @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile index a83aa9b875..83f4c58560 100644 --- a/erts/doc/src/Makefile +++ b/erts/doc/src/Makefile @@ -3,16 +3,17 @@ # # 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index e1a8c2e517..547d5e583d 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml index e4912576f7..2263302707 100644 --- a/erts/doc/src/alt_dist.xml +++ b/erts/doc/src/alt_dist.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/book.xml b/erts/doc/src/book.xml index dc02edc5c6..12eda03ee5 100644 --- a/erts/doc/src/book.xml +++ b/erts/doc/src/book.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/communication.xml b/erts/doc/src/communication.xml index 02040c9edb..3deea3e4af 100644 --- a/erts/doc/src/communication.xml +++ b/erts/doc/src/communication.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index e13d468ee6..61c9159823 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/driver.xml b/erts/doc/src/driver.xml index 616703fdef..a68e87d3b3 100644 --- a/erts/doc/src/driver.xml +++ b/erts/doc/src/driver.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml index b34ca136f3..349ce4bbc1 100644 --- a/erts/doc/src/driver_entry.xml +++ b/erts/doc/src/driver_entry.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml index 25f819ab50..28fcc8f7af 100644 --- a/erts/doc/src/epmd.xml +++ b/erts/doc/src/epmd.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index f41b6e6149..b0322b7d43 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml index 890293d802..e1a58856f3 100644 --- a/erts/doc/src/erl_dist_protocol.xml +++ b/erts/doc/src/erl_dist_protocol.xml @@ -9,16 +9,17 @@ Ericsson AB, All Rights Reserved - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. The Initial Developer of the Original Code is Ericsson AB. diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 77fc906aca..1f7fe0f961 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml index a6e7dddbed..caf1e812c4 100644 --- a/erts/doc/src/erl_ext_dist.xml +++ b/erts/doc/src/erl_ext_dist.xml @@ -9,16 +9,17 @@ Ericsson AB, All Rights Reserved - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. The Initial Developer of the Original Code is Ericsson AB. diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 412c0e02ac..23c3d5fcee 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml index 171f84decc..d05f0d9aea 100644 --- a/erts/doc/src/erl_prim_loader.xml +++ b/erts/doc/src/erl_prim_loader.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 50a26781c4..37f0aa289e 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml index c3fc3b1686..9fc5864413 100644 --- a/erts/doc/src/erlc.xml +++ b/erts/doc/src/erlc.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv.xml index 71cee714a5..ccb8b2dd76 100644 --- a/erts/doc/src/erlsrv.xml +++ b/erts/doc/src/erlsrv.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 1ade41f1aa..376cae4a95 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml index 9159d68f60..46110333f9 100644 --- a/erts/doc/src/escript.xml +++ b/erts/doc/src/escript.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/inet_cfg.xml b/erts/doc/src/inet_cfg.xml index d40bc5f9ee..5caf232a62 100644 --- a/erts/doc/src/inet_cfg.xml +++ b/erts/doc/src/inet_cfg.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index c5a1a92b92..fe26df61f7 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml index b4cc8e9f78..08dad8cc10 100644 --- a/erts/doc/src/match_spec.xml +++ b/erts/doc/src/match_spec.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 35e6e55e72..2574d45184 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/notes_history.xml b/erts/doc/src/notes_history.xml index 4420311912..0886ae4039 100644 --- a/erts/doc/src/notes_history.xml +++ b/erts/doc/src/notes_history.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/part.xml b/erts/doc/src/part.xml index 7b17b5b551..2f5eca93db 100644 --- a/erts/doc/src/part.xml +++ b/erts/doc/src/part.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/part_notes.xml b/erts/doc/src/part_notes.xml index b5c8f0af09..83bb479715 100644 --- a/erts/doc/src/part_notes.xml +++ b/erts/doc/src/part_notes.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/part_notes_history.xml b/erts/doc/src/part_notes_history.xml index a99fa4a17f..055d1681d5 100644 --- a/erts/doc/src/part_notes_history.xml +++ b/erts/doc/src/part_notes_history.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/ref_man.xml b/erts/doc/src/ref_man.xml index 8ed7090a61..ac589f8cb5 100644 --- a/erts/doc/src/ref_man.xml +++ b/erts/doc/src/ref_man.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/run_erl.xml b/erts/doc/src/run_erl.xml index 28e94c6da8..0a5b2c6136 100644 --- a/erts/doc/src/run_erl.xml +++ b/erts/doc/src/run_erl.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/start.xml b/erts/doc/src/start.xml index e9a5714f93..386fbe6e88 100644 --- a/erts/doc/src/start.xml +++ b/erts/doc/src/start.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/start_erl.xml b/erts/doc/src/start_erl.xml index fe808f7737..62610b43b0 100644 --- a/erts/doc/src/start_erl.xml +++ b/erts/doc/src/start_erl.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index 8af98acc19..e7d2aedf96 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/tty.xml b/erts/doc/src/tty.xml index db15195f65..cd46d1203c 100644 --- a/erts/doc/src/tty.xml +++ b/erts/doc/src/tty.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/werl.xml b/erts/doc/src/werl.xml index 49cc45e745..9e7ad584eb 100644 --- a/erts/doc/src/werl.xml +++ b/erts/doc/src/werl.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index 1f10ddef6d..0a641346d9 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -8,16 +8,17 @@ Ericsson AB. All Rights Reserved. - The 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - 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. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/erts/emulator/Makefile b/erts/emulator/Makefile index 2db3b349b6..550e6e6f5b 100644 --- a/erts/emulator/Makefile +++ b/erts/emulator/Makefile @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 659ea1b27f..a919f0e3ac 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 1996-2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c index 84d2d5e3ed..fe91134ef4 100644 --- a/erts/emulator/beam/atom.c +++ b/erts/emulator/beam/atom.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/atom.h b/erts/emulator/beam/atom.h index 5904ae0f7e..ead56c83d8 100644 --- a/erts/emulator/beam/atom.h +++ b/erts/emulator/beam/atom.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 0cf21b4635..f9a2f3e33e 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 1996-2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index c769428266..0e192b1ebd 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 4e711c89e0..016d0aaa32 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2000-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index b061401863..97d0539ac7 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2000-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/beam_catches.c b/erts/emulator/beam/beam_catches.c index d374d0469e..c1fd17c65d 100644 --- a/erts/emulator/beam/beam_catches.c +++ b/erts/emulator/beam/beam_catches.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2000-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/beam_catches.h b/erts/emulator/beam/beam_catches.h index 51ef463b2f..59ee64d033 100644 --- a/erts/emulator/beam/beam_catches.h +++ b/erts/emulator/beam/beam_catches.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2000-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 78ddecafc3..c756de8c8e 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1998-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 2d4bf4240c..8b409e139b 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 006ba5d0db..b70e5b9a2d 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index 46b0c60ab0..d5af634fad 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c index cb6470638f..19079ba150 100644 --- a/erts/emulator/beam/beam_ranges.c +++ b/erts/emulator/beam/beam_ranges.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/benchmark.c b/erts/emulator/beam/benchmark.c index b16fe6b271..44f5c760c2 100644 --- a/erts/emulator/beam/benchmark.c +++ b/erts/emulator/beam/benchmark.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/benchmark.h b/erts/emulator/beam/benchmark.h index 7fc3933f3d..0fb0b93f12 100644 --- a/erts/emulator/beam/benchmark.h +++ b/erts/emulator/beam/benchmark.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index bfca861830..4e3a1cef69 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index b877711544..c6ed60376a 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index eadba3eaff..4f0656d174 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 1996-2013. All Rights Reserved. # -# The 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index a4ea9c59ca..044bf6a34e 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 5b5550da43..4aa9724ae3 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index cc0b3b9b6c..e670fbf31c 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 3cb605834f..64c8bc5e58 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c index d925709bd0..209d008d18 100644 --- a/erts/emulator/beam/code_ix.c +++ b/erts/emulator/beam/code_ix.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/code_ix.h b/erts/emulator/beam/code_ix.h index 16ad900228..5f00b409ef 100644 --- a/erts/emulator/beam/code_ix.h +++ b/erts/emulator/beam/code_ix.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2012-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index cddadb346c..8849dadd00 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 1354cee267..23897a49ae 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index cd2cc0ef4a..fb777d9ac1 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/dtrace-wrapper.h b/erts/emulator/beam/dtrace-wrapper.h index 6ec0c91e21..d343dc5ab0 100644 --- a/erts/emulator/beam/dtrace-wrapper.h +++ b/erts/emulator/beam/dtrace-wrapper.h @@ -4,16 +4,17 @@ * Copyright Dustin Sallings, Michal Ptaszek, 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/elib_memmove.c b/erts/emulator/beam/elib_memmove.c index d2fe8649ed..d4ca30158e 100644 --- a/erts/emulator/beam/elib_memmove.c +++ b/erts/emulator/beam/elib_memmove.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c index eca4e3b3bb..47dafa53c0 100644 --- a/erts/emulator/beam/erl_afit_alloc.c +++ b/erts/emulator/beam/erl_afit_alloc.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_afit_alloc.h b/erts/emulator/beam/erl_afit_alloc.h index b90ac8f7c5..ef050ff50e 100644 --- a/erts/emulator/beam/erl_afit_alloc.h +++ b/erts/emulator/beam/erl_afit_alloc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index c9ac024743..55c164bf11 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2002-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index d3109b9432..f540bae20d 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2002-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 92c397ffd3..b1d511ab78 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2003-2014. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index b92533f228..236ee35d18 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2002-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index eee920e66c..df1f0aa65a 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2002-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c index 396aa88e0b..7c2a5c3323 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.c +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.h b/erts/emulator/beam/erl_ao_firstfit_alloc.h index 25b344c6a8..4200f20622 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.h +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_arith.c b/erts/emulator/beam/erl_arith.c index 47d516534f..b8c5ef9b09 100644 --- a/erts/emulator/beam/erl_arith.c +++ b/erts/emulator/beam/erl_arith.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index bc06d41720..f071898046 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2000-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_async.h b/erts/emulator/beam/erl_async.h index 95374a8fc9..65538bcef0 100644 --- a/erts/emulator/beam/erl_async.h +++ b/erts/emulator/beam/erl_async.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c index 59c14899a2..fb853b65ab 100644 --- a/erts/emulator/beam/erl_bestfit_alloc.c +++ b/erts/emulator/beam/erl_bestfit_alloc.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bestfit_alloc.h b/erts/emulator/beam/erl_bestfit_alloc.h index 870439e886..b315518b88 100644 --- a/erts/emulator/beam/erl_bestfit_alloc.h +++ b/erts/emulator/beam/erl_bestfit_alloc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 934904d58e..134aa2d396 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c index 4302fe8f79..e3074d6309 100644 --- a/erts/emulator/beam/erl_bif_chksum.c +++ b/erts/emulator/beam/erl_bif_chksum.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index 7b35edc9c4..28bec6325c 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c index 6226ec2d04..4a9a6a5fcd 100644 --- a/erts/emulator/beam/erl_bif_guard.c +++ b/erts/emulator/beam/erl_bif_guard.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 7eb31fb80e..b44382cde8 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c index e006d57124..5583dcb371 100644 --- a/erts/emulator/beam/erl_bif_lists.c +++ b/erts/emulator/beam/erl_bif_lists.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c index 37dd6457db..c9192fc420 100644 --- a/erts/emulator/beam/erl_bif_op.c +++ b/erts/emulator/beam/erl_bif_op.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_os.c b/erts/emulator/beam/erl_bif_os.c index e07c622928..2333ca0851 100644 --- a/erts/emulator/beam/erl_bif_os.c +++ b/erts/emulator/beam/erl_bif_os.c @@ -3,16 +3,17 @@ * * 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 - * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 7ce950e090..3ff54c7a60 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index 448c6f6f6d..86951f32b0 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 13e0160648..03f51132b1 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_unique.c b/erts/emulator/beam/erl_bif_unique.c index 57b0bab72f..5eca09c5a6 100644 --- a/erts/emulator/beam/erl_bif_unique.c +++ b/erts/emulator/beam/erl_bif_unique.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bif_unique.h b/erts/emulator/beam/erl_bif_unique.h index cd001172a1..37d5d91c39 100644 --- a/erts/emulator/beam/erl_bif_unique.h +++ b/erts/emulator/beam/erl_bif_unique.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index 6b96787d40..ea01bf08f0 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2000-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 2e29bf8895..01734c55d7 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_bits.h b/erts/emulator/beam/erl_bits.h index 388d943755..8b7807fbd9 100644 --- a/erts/emulator/beam/erl_bits.h +++ b/erts/emulator/beam/erl_bits.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c index f594cb9392..8395f6ecc6 100644 --- a/erts/emulator/beam/erl_cpu_topology.c +++ b/erts/emulator/beam/erl_cpu_topology.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h index b502258dae..f3fbfc6da0 100644 --- a/erts/emulator/beam/erl_cpu_topology.h +++ b/erts/emulator/beam/erl_cpu_topology.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 844272c699..878ee32b47 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h index 5039b71108..a589af784c 100644 --- a/erts/emulator/beam/erl_db.h +++ b/erts/emulator/beam/erl_db.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index dce0a3d621..81b0c4465c 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h index f12cd363b0..66d8ec71d9 100644 --- a/erts/emulator/beam/erl_db_hash.h +++ b/erts/emulator/beam/erl_db_hash.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1998-2014. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index c7bccc78c3..465aa566ad 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1998-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_db_tree.h b/erts/emulator/beam/erl_db_tree.h index 7bc235e135..6098387f5d 100644 --- a/erts/emulator/beam/erl_db_tree.h +++ b/erts/emulator/beam/erl_db_tree.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index d47ff03a30..dab357a079 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1998-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index b2d5a306cb..1ccdc0305b 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1998-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index 50bdc79506..77a1e3d7cb 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1998-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_debug.h b/erts/emulator/beam/erl_debug.h index af51212281..4905e64f07 100644 --- a/erts/emulator/beam/erl_debug.h +++ b/erts/emulator/beam/erl_debug.h @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index e498ac70ec..6b406d069c 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h index 4e8c6dc68b..e2385f63f4 100644 --- a/erts/emulator/beam/erl_drv_nif.h +++ b/erts/emulator/beam/erl_drv_nif.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index 240faa823d..e0404eb5c9 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2007-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c index 88947b5536..4268e2d40a 100644 --- a/erts/emulator/beam/erl_fun.c +++ b/erts/emulator/beam/erl_fun.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2000-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h index b673ef6b3c..0024b1ff71 100644 --- a/erts/emulator/beam/erl_fun.h +++ b/erts/emulator/beam/erl_fun.h @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 71ca2713b2..d2604f1595 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2002-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index bd6dcc9078..ecd1bf4d22 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2007-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c index e9d8249ee1..f89f8723d9 100644 --- a/erts/emulator/beam/erl_goodfit_alloc.c +++ b/erts/emulator/beam/erl_goodfit_alloc.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_goodfit_alloc.h b/erts/emulator/beam/erl_goodfit_alloc.h index 385de0da23..ababdbd0a1 100644 --- a/erts/emulator/beam/erl_goodfit_alloc.h +++ b/erts/emulator/beam/erl_goodfit_alloc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 8eacb921fe..907491f616 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2015. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_hl_timer.h b/erts/emulator/beam/erl_hl_timer.h index 24c57fc873..0931bb8965 100644 --- a/erts/emulator/beam/erl_hl_timer.h +++ b/erts/emulator/beam/erl_hl_timer.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2015. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index ac1f00d2d8..d9c3b0dcf4 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1997-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c index da85b86c87..12a72ad839 100644 --- a/erts/emulator/beam/erl_instrument.c +++ b/erts/emulator/beam/erl_instrument.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_instrument.h b/erts/emulator/beam/erl_instrument.h index 37b9b67139..cb3b1920d3 100644 --- a/erts/emulator/beam/erl_instrument.h +++ b/erts/emulator/beam/erl_instrument.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 617ce84895..3b3b247020 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_lock_check.h b/erts/emulator/beam/erl_lock_check.h index 3f7f417e61..66251ef4e8 100644 --- a/erts/emulator/beam/erl_lock_check.h +++ b/erts/emulator/beam/erl_lock_check.h @@ -3,16 +3,17 @@ * * 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 - * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index c4956d8569..bd00480ba2 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h index 051f55271d..4cc6a5c695 100644 --- a/erts/emulator/beam/erl_lock_count.h +++ b/erts/emulator/beam/erl_lock_count.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 95a10daa67..a91e36e3c5 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% * diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index b5941c5c9a..c391de3f11 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2014. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_math.c b/erts/emulator/beam/erl_math.c index 9b864628db..b46cc37495 100644 --- a/erts/emulator/beam/erl_math.c +++ b/erts/emulator/beam/erl_math.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index f806d2c498..ef52823287 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 705ba5e506..fbdf3fb0e2 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_monitors.c b/erts/emulator/beam/erl_monitors.c index 244a2b26db..7dfa01c8ac 100644 --- a/erts/emulator/beam/erl_monitors.c +++ b/erts/emulator/beam/erl_monitors.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_monitors.h b/erts/emulator/beam/erl_monitors.h index 9972890db7..901ed8940b 100644 --- a/erts/emulator/beam/erl_monitors.h +++ b/erts/emulator/beam/erl_monitors.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c index fa1bde1c87..fdaf02f37a 100644 --- a/erts/emulator/beam/erl_mtrace.c +++ b/erts/emulator/beam/erl_mtrace.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_mtrace.h b/erts/emulator/beam/erl_mtrace.h index 204543ddb0..b34eab3c4a 100644 --- a/erts/emulator/beam/erl_mtrace.h +++ b/erts/emulator/beam/erl_mtrace.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 27f6c6f00d..add4a66f90 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index af806736fd..7d880126f8 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index e38a016958..f93152c921 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_node_container_utils.h b/erts/emulator/beam/erl_node_container_utils.h index 17f6b32bb1..211b1a0090 100644 --- a/erts/emulator/beam/erl_node_container_utils.h +++ b/erts/emulator/beam/erl_node_container_utils.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 0950d7e7ef..2fb790b953 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index 54c5cd1d11..694ac84232 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index 66cbb67a0d..acd68ef0ad 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2012-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index c701737e26..5c38db1cbc 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h index 406cd3c492..335f7a77d5 100644 --- a/erts/emulator/beam/erl_port_task.h +++ b/erts/emulator/beam/erl_port_task.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index e0dfbd31b8..267c0b3ff4 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_printf_term.h b/erts/emulator/beam/erl_printf_term.h index f92c99d713..87618a36d7 100644 --- a/erts/emulator/beam/erl_printf_term.h +++ b/erts/emulator/beam/erl_printf_term.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 88c1b5c121..9ae16b1898 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index a6c3790991..20ffe7ea7c 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 00761f2d0e..8606371bdf 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h index 8fad2a67ab..cc53800eb5 100644 --- a/erts/emulator/beam/erl_process_dict.h +++ b/erts/emulator/beam/erl_process_dict.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index 36bb6b2f0e..25f0b1ed38 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index 0548c6137c..0bee2c848c 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index 8957e7773b..788348e613 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -3,16 +3,17 @@ * * 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 - * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c index c688db98d8..f7997df051 100644 --- a/erts/emulator/beam/erl_ptab.c +++ b/erts/emulator/beam/erl_ptab.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2012-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h index 102d41e07f..8fd961e3ce 100644 --- a/erts/emulator/beam/erl_ptab.h +++ b/erts/emulator/beam/erl_ptab.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2012-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_rbtree.h b/erts/emulator/beam/erl_rbtree.h index ea0a8976bb..5fefaea978 100644 --- a/erts/emulator/beam/erl_rbtree.h +++ b/erts/emulator/beam/erl_rbtree.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2015. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c index a490aec734..caec24bc03 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.h b/erts/emulator/beam/erl_sched_spec_pre_alloc.h index 9144c73acd..4d07b0f674 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.h +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.h @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_smp.h b/erts/emulator/beam/erl_smp.h index 6c40edeb3e..5fc5e989a6 100644 --- a/erts/emulator/beam/erl_smp.h +++ b/erts/emulator/beam/erl_smp.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_sock.h b/erts/emulator/beam/erl_sock.h index 7ae6116dc5..7be6062115 100644 --- a/erts/emulator/beam/erl_sock.h +++ b/erts/emulator/beam/erl_sock.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_sys_driver.h b/erts/emulator/beam/erl_sys_driver.h index dab4a94a9b..4031eef0aa 100644 --- a/erts/emulator/beam/erl_sys_driver.h +++ b/erts/emulator/beam/erl_sys_driver.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_term.c b/erts/emulator/beam/erl_term.c index 565528193e..89459fb278 100644 --- a/erts/emulator/beam/erl_term.c +++ b/erts/emulator/beam/erl_term.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2000-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 7b15b34da1..90e35151b0 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2000-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 78e0964e8b..7148b756e7 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2011-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h index cf11c4e114..b89cc4c267 100644 --- a/erts/emulator/beam/erl_thr_progress.h +++ b/erts/emulator/beam/erl_thr_progress.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2011-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_thr_queue.c b/erts/emulator/beam/erl_thr_queue.c index f8ca87ddcc..3a91ca9dbe 100644 --- a/erts/emulator/beam/erl_thr_queue.c +++ b/erts/emulator/beam/erl_thr_queue.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2011-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_thr_queue.h b/erts/emulator/beam/erl_thr_queue.h index 13af758b3f..27a6d03224 100644 --- a/erts/emulator/beam/erl_thr_queue.h +++ b/erts/emulator/beam/erl_thr_queue.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2011-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index dc20ac207f..5347979372 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index e64b86496a..36a3d52264 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index e550c999b8..7f8f560681 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-2015. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index aaecc5a02e..e1b03a057f 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index 4f2c70d6e7..7405490f76 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2012-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index f8e1431a53..551717139d 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_unicode.h b/erts/emulator/beam/erl_unicode.h index 1b63b797c2..4c25d89b7c 100644 --- a/erts/emulator/beam/erl_unicode.h +++ b/erts/emulator/beam/erl_unicode.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_unicode_normalize.h b/erts/emulator/beam/erl_unicode_normalize.h index fb0a111ca2..16c62db50e 100644 --- a/erts/emulator/beam/erl_unicode_normalize.h +++ b/erts/emulator/beam/erl_unicode_normalize.h @@ -1,21 +1,22 @@ /* -* %CopyrightBegin% -* -* Copyright Ericsson AB 1999-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% -*/ + * %CopyrightBegin% + * + * Copyright Ericsson AB 1999-2010. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ /* * This file is automatically generated by dec.erl, do not edit manually */ diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 6a28105cb9..6dab3bf297 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2012-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 3a9fb1e07b..8948ca2ea9 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_zlib.c b/erts/emulator/beam/erl_zlib.c index 8e33144f96..e44e86c39c 100644 --- a/erts/emulator/beam/erl_zlib.c +++ b/erts/emulator/beam/erl_zlib.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erl_zlib.h b/erts/emulator/beam/erl_zlib.h index 160166c66b..2ca1e3735e 100644 --- a/erts/emulator/beam/erl_zlib.h +++ b/erts/emulator/beam/erl_zlib.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d index e3ebbb84f4..c682f76e3a 100644 --- a/erts/emulator/beam/erlang_dtrace.d +++ b/erts/emulator/beam/erlang_dtrace.d @@ -4,16 +4,17 @@ * Copyright Dustin Sallings, Michal Ptaszek, 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/error.h b/erts/emulator/beam/error.h index e63967adb6..cba8672c68 100644 --- a/erts/emulator/beam/error.h +++ b/erts/emulator/beam/error.h @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c index b0f08d8245..2420df36b5 100644 --- a/erts/emulator/beam/export.c +++ b/erts/emulator/beam/export.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h index 61a54de59f..a8bc9d2f66 100644 --- a/erts/emulator/beam/export.h +++ b/erts/emulator/beam/export.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 0a69172980..c6d7e3fcc5 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index 508ab8dc17..d12051c6b4 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 14d42599a1..ec9296d034 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c index afaf32f8ce..e0fde337f2 100644 --- a/erts/emulator/beam/hash.c +++ b/erts/emulator/beam/hash.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/hash.h b/erts/emulator/beam/hash.h index 6dd66fc9b3..87fdb360e3 100644 --- a/erts/emulator/beam/hash.h +++ b/erts/emulator/beam/hash.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c index 79c3ecf1b3..06d0b5123d 100644 --- a/erts/emulator/beam/index.c +++ b/erts/emulator/beam/index.c @@ -3,16 +3,17 @@ * * 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 - * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/index.h b/erts/emulator/beam/index.h index 537bc11056..14fab41026 100644 --- a/erts/emulator/beam/index.h +++ b/erts/emulator/beam/index.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 75d80b1a58..900616c981 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c index daa6e136c5..86dd3b5aac 100644 --- a/erts/emulator/beam/module.c +++ b/erts/emulator/beam/module.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h index 5235528e98..c8a6351b04 100644 --- a/erts/emulator/beam/module.h +++ b/erts/emulator/beam/module.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index ece038131e..1d32e72247 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 1997-2013. All Rights Reserved. # -# The 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c index db0e78b1a7..2dd421a9e9 100644 --- a/erts/emulator/beam/packet_parser.c +++ b/erts/emulator/beam/packet_parser.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/packet_parser.h b/erts/emulator/beam/packet_parser.h index 1c3a9aa3da..ff158ff8b8 100644 --- a/erts/emulator/beam/packet_parser.h +++ b/erts/emulator/beam/packet_parser.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c index 4d557b3a17..7ade8bca0f 100644 --- a/erts/emulator/beam/register.c +++ b/erts/emulator/beam/register.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/register.h b/erts/emulator/beam/register.h index 7170463375..144536f34b 100644 --- a/erts/emulator/beam/register.h +++ b/erts/emulator/beam/register.h @@ -3,16 +3,17 @@ * * 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 - * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/safe_hash.c b/erts/emulator/beam/safe_hash.c index 3326e5cc2a..3f039c8dfd 100644 --- a/erts/emulator/beam/safe_hash.c +++ b/erts/emulator/beam/safe_hash.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/safe_hash.h b/erts/emulator/beam/safe_hash.h index c691126ef9..a11370813c 100644 --- a/erts/emulator/beam/safe_hash.h +++ b/erts/emulator/beam/safe_hash.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 78eb0bfe2b..bb871b05ba 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index fe7c5826f5..0ab6661c9f 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index cecd88197e..342e91e983 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/beam/version.h b/erts/emulator/beam/version.h index 3952c751b7..004725eaf5 100644 --- a/erts/emulator/beam/version.h +++ b/erts/emulator/beam/version.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index b2cfe70f94..8aff6c1865 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h index 5a8e3bc5db..be5a891486 100644 --- a/erts/emulator/drivers/common/erl_efile.h +++ b/erts/emulator/drivers/common/erl_efile.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1997-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/common/gzio.h b/erts/emulator/drivers/common/gzio.h index ea50d922ec..a6fe2fb6f5 100644 --- a/erts/emulator/drivers/common/gzio.h +++ b/erts/emulator/drivers/common/gzio.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/common/gzio_zutil.h b/erts/emulator/drivers/common/gzio_zutil.h index 854205cc2c..9eefb86637 100644 --- a/erts/emulator/drivers/common/gzio_zutil.h +++ b/erts/emulator/drivers/common/gzio_zutil.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 119d6c097d..549de6503c 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1997-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/common/ram_file_drv.c b/erts/emulator/drivers/common/ram_file_drv.c index 7f7cd7cd91..9cb44d0b7e 100644 --- a/erts/emulator/drivers/common/ram_file_drv.c +++ b/erts/emulator/drivers/common/ram_file_drv.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/common/zlib_drv.c b/erts/emulator/drivers/common/zlib_drv.c index f7b2d91d23..364048174c 100644 --- a/erts/emulator/drivers/common/zlib_drv.c +++ b/erts/emulator/drivers/common/zlib_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/ose/ose_efile.c b/erts/emulator/drivers/ose/ose_efile.c index 035ff81a9b..c8337a95d5 100644 --- a/erts/emulator/drivers/ose/ose_efile.c +++ b/erts/emulator/drivers/ose/ose_efile.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/ose/ose_signal_drv.c b/erts/emulator/drivers/ose/ose_signal_drv.c index 4929b53856..2be9462a47 100644 --- a/erts/emulator/drivers/ose/ose_signal_drv.c +++ b/erts/emulator/drivers/ose/ose_signal_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2013-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/ose/ttsl_drv.c b/erts/emulator/drivers/ose/ttsl_drv.c index 8af2ce6af3..f759b47984 100644 --- a/erts/emulator/drivers/ose/ttsl_drv.c +++ b/erts/emulator/drivers/ose/ttsl_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/unix/bin_drv.c b/erts/emulator/drivers/unix/bin_drv.c index 1827187d57..21fb398907 100644 --- a/erts/emulator/drivers/unix/bin_drv.c +++ b/erts/emulator/drivers/unix/bin_drv.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/unix/multi_drv.c b/erts/emulator/drivers/unix/multi_drv.c index 724d325ed5..7f8c2d9a0d 100644 --- a/erts/emulator/drivers/unix/multi_drv.c +++ b/erts/emulator/drivers/unix/multi_drv.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/unix/sig_drv.c b/erts/emulator/drivers/unix/sig_drv.c index aab5d63a40..e6f2ecc494 100644 --- a/erts/emulator/drivers/unix/sig_drv.c +++ b/erts/emulator/drivers/unix/sig_drv.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index a5960716f2..0f773b69fb 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index 878beb055b..06ba986044 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1997-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/vxworks/vxworks_resolv.c b/erts/emulator/drivers/vxworks/vxworks_resolv.c index 8fcbb736f7..a7de53c692 100644 --- a/erts/emulator/drivers/vxworks/vxworks_resolv.c +++ b/erts/emulator/drivers/vxworks/vxworks_resolv.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/win32/registry_drv.c b/erts/emulator/drivers/win32/registry_drv.c index e908c956ae..4e396aa8d9 100644 --- a/erts/emulator/drivers/win32/registry_drv.c +++ b/erts/emulator/drivers/win32/registry_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1997-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/win32/ttsl_drv.c b/erts/emulator/drivers/win32/ttsl_drv.c index 851c336a11..4bd766a8a8 100644 --- a/erts/emulator/drivers/win32/ttsl_drv.c +++ b/erts/emulator/drivers/win32/ttsl_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c index 7b9cadd32b..0d63d46698 100644 --- a/erts/emulator/drivers/win32/win_con.c +++ b/erts/emulator/drivers/win32/win_con.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/win32/win_con.h b/erts/emulator/drivers/win32/win_con.h index d46af86ca5..3f4e89a195 100644 --- a/erts/emulator/drivers/win32/win_con.h +++ b/erts/emulator/drivers/win32/win_con.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2007-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index 7e4043fc1b..d95f29f49d 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1997-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/TODO b/erts/emulator/hipe/TODO index 624ab560e7..dd9760ea1c 100644 --- a/erts/emulator/hipe/TODO +++ b/erts/emulator/hipe/TODO @@ -3,16 +3,17 @@ Copyright Ericsson AB 2004-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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - 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. + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. %CopyrightEnd% diff --git a/erts/emulator/hipe/elf64ppc.x b/erts/emulator/hipe/elf64ppc.x index 299eed8192..3cd4010cb3 100644 --- a/erts/emulator/hipe/elf64ppc.x +++ b/erts/emulator/hipe/elf64ppc.x @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_amd64.c b/erts/emulator/hipe/hipe_amd64.c index 63646825b2..1bb336637d 100644 --- a/erts/emulator/hipe/hipe_amd64.c +++ b/erts/emulator/hipe/hipe_amd64.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_amd64.h b/erts/emulator/hipe/hipe_amd64.h index bf41d238dd..09b55a1243 100644 --- a/erts/emulator/hipe/hipe_amd64.h +++ b/erts/emulator/hipe/hipe_amd64.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_amd64.tab b/erts/emulator/hipe/hipe_amd64.tab index e039d74525..70287034a0 100644 --- a/erts/emulator/hipe/hipe_amd64.tab +++ b/erts/emulator/hipe/hipe_amd64.tab @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2004-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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/hipe/hipe_amd64_asm.m4 b/erts/emulator/hipe/hipe_amd64_asm.m4 index ca55d5bf3b..ecb25ec5c8 100644 --- a/erts/emulator/hipe/hipe_amd64_asm.m4 +++ b/erts/emulator/hipe/hipe_amd64_asm.m4 @@ -4,16 +4,17 @@ changecom(`/*', `*/')dnl * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4 index 74cb9112ce..602e71c195 100644 --- a/erts/emulator/hipe/hipe_amd64_bifs.m4 +++ b/erts/emulator/hipe/hipe_amd64_bifs.m4 @@ -4,16 +4,17 @@ changecom(`/*', `*/')dnl * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_amd64_gc.h b/erts/emulator/hipe/hipe_amd64_gc.h index c5a6fee6fe..e7a156c910 100644 --- a/erts/emulator/hipe/hipe_amd64_gc.h +++ b/erts/emulator/hipe/hipe_amd64_gc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S index 3cb0a2875b..6fb93bce53 100644 --- a/erts/emulator/hipe/hipe_amd64_glue.S +++ b/erts/emulator/hipe/hipe_amd64_glue.S @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_amd64_glue.h b/erts/emulator/hipe/hipe_amd64_glue.h index 36508467fa..e54b8b3a3f 100644 --- a/erts/emulator/hipe/hipe_amd64_glue.h +++ b/erts/emulator/hipe/hipe_amd64_glue.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_amd64_primops.h b/erts/emulator/hipe/hipe_amd64_primops.h index 55cb0eadb8..23d8202e46 100644 --- a/erts/emulator/hipe/hipe_amd64_primops.h +++ b/erts/emulator/hipe/hipe_amd64_primops.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_arch.h b/erts/emulator/hipe/hipe_arch.h index b45209b3f7..21dabc7cb6 100644 --- a/erts/emulator/hipe/hipe_arch.h +++ b/erts/emulator/hipe/hipe_arch.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c index c0c6305c68..01512e9aa1 100644 --- a/erts/emulator/hipe/hipe_arm.c +++ b/erts/emulator/hipe/hipe_arm.c @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_arm.h b/erts/emulator/hipe/hipe_arm.h index b9cd1a750c..a0e07cd6ce 100644 --- a/erts/emulator/hipe/hipe_arm.h +++ b/erts/emulator/hipe/hipe_arm.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_arm.tab b/erts/emulator/hipe/hipe_arm.tab index 49b89d6748..bcac2cd79d 100644 --- a/erts/emulator/hipe/hipe_arm.tab +++ b/erts/emulator/hipe/hipe_arm.tab @@ -3,16 +3,17 @@ # # 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/hipe/hipe_arm_asm.m4 b/erts/emulator/hipe/hipe_arm_asm.m4 index ca6aef2f8d..8ce8600f97 100644 --- a/erts/emulator/hipe/hipe_arm_asm.m4 +++ b/erts/emulator/hipe/hipe_arm_asm.m4 @@ -4,16 +4,17 @@ changecom(`/*', `*/')dnl * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4 index 6abc7545e0..3efe961964 100644 --- a/erts/emulator/hipe/hipe_arm_bifs.m4 +++ b/erts/emulator/hipe/hipe_arm_bifs.m4 @@ -4,16 +4,17 @@ changecom(`/*', `*/')dnl * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_arm_gc.h b/erts/emulator/hipe/hipe_arm_gc.h index 787c6fef3e..dfffd6c630 100644 --- a/erts/emulator/hipe/hipe_arm_gc.h +++ b/erts/emulator/hipe/hipe_arm_gc.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S index edcabfd7a4..e4ce9b75ef 100644 --- a/erts/emulator/hipe/hipe_arm_glue.S +++ b/erts/emulator/hipe/hipe_arm_glue.S @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_arm_glue.h b/erts/emulator/hipe/hipe_arm_glue.h index 165f73320d..39bc27ec5f 100644 --- a/erts/emulator/hipe/hipe_arm_glue.h +++ b/erts/emulator/hipe/hipe_arm_glue.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_arm_primops.h b/erts/emulator/hipe/hipe_arm_primops.h index 2a1a87b862..0e8f84dfb9 100644 --- a/erts/emulator/hipe/hipe_arm_primops.h +++ b/erts/emulator/hipe/hipe_arm_primops.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index a4311d22e2..efbe951ce5 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_bif0.h b/erts/emulator/hipe/hipe_bif0.h index c512d66f9d..5527c6c3ed 100644 --- a/erts/emulator/hipe/hipe_bif0.h +++ b/erts/emulator/hipe/hipe_bif0.h @@ -4,16 +4,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab index 4271a78de3..e3328c7d2c 100644 --- a/erts/emulator/hipe/hipe_bif0.tab +++ b/erts/emulator/hipe/hipe_bif0.tab @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2001-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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/hipe/hipe_bif1.c b/erts/emulator/hipe/hipe_bif1.c index ecb34df412..a2682992a4 100644 --- a/erts/emulator/hipe/hipe_bif1.c +++ b/erts/emulator/hipe/hipe_bif1.c @@ -4,16 +4,17 @@ * * 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 - * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_bif1.h b/erts/emulator/hipe/hipe_bif1.h index 89241fb835..80cdeb4a82 100644 --- a/erts/emulator/hipe/hipe_bif1.h +++ b/erts/emulator/hipe/hipe_bif1.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_bif1.tab b/erts/emulator/hipe/hipe_bif1.tab index eb445d56f7..6c4e05afdb 100644 --- a/erts/emulator/hipe/hipe_bif1.tab +++ b/erts/emulator/hipe/hipe_bif1.tab @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2001-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c index 054911e822..2328e69886 100644 --- a/erts/emulator/hipe/hipe_bif2.c +++ b/erts/emulator/hipe/hipe_bif2.c @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_bif2.tab b/erts/emulator/hipe/hipe_bif2.tab index 1b659cfa90..a29e1fbdbb 100644 --- a/erts/emulator/hipe/hipe_bif2.tab +++ b/erts/emulator/hipe/hipe_bif2.tab @@ -3,16 +3,17 @@ # # 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 -# 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/hipe/hipe_bif64.c b/erts/emulator/hipe/hipe_bif64.c index baaf5af2cd..b97cf2b7ee 100644 --- a/erts/emulator/hipe/hipe_bif64.c +++ b/erts/emulator/hipe/hipe_bif64.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_bif64.h b/erts/emulator/hipe/hipe_bif64.h index 6d494886ec..856b4003dc 100644 --- a/erts/emulator/hipe/hipe_bif64.h +++ b/erts/emulator/hipe/hipe_bif64.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_bif64.tab b/erts/emulator/hipe/hipe_bif64.tab index 228318af39..3062ab5848 100644 --- a/erts/emulator/hipe/hipe_bif64.tab +++ b/erts/emulator/hipe/hipe_bif64.tab @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2004-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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 370061bca1..b3bd5e4357 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c index 2804d46249..a48578c468 100644 --- a/erts/emulator/hipe/hipe_debug.c +++ b/erts/emulator/hipe/hipe_debug.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_debug.h b/erts/emulator/hipe/hipe_debug.h index a28597000a..bf10d5a2a8 100644 --- a/erts/emulator/hipe/hipe_debug.h +++ b/erts/emulator/hipe/hipe_debug.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_gbif_list.h b/erts/emulator/hipe/hipe_gbif_list.h index 69dbab7ab9..dc93741a1d 100644 --- a/erts/emulator/hipe/hipe_gbif_list.h +++ b/erts/emulator/hipe/hipe_gbif_list.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c index b10263f6e2..2c747771ac 100644 --- a/erts/emulator/hipe/hipe_gc.c +++ b/erts/emulator/hipe/hipe_gc.c @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_gc.h b/erts/emulator/hipe/hipe_gc.h index 0d5614c9cf..e373324cdd 100644 --- a/erts/emulator/hipe/hipe_gc.h +++ b/erts/emulator/hipe/hipe_gc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index 49e8d39360..aa12df2932 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 8c73312d45..968452a641 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -4,16 +4,17 @@ * * Copyright Ericsson AB 2001-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h index b8de12fcbb..bc863a4f36 100644 --- a/erts/emulator/hipe/hipe_mode_switch.h +++ b/erts/emulator/hipe/hipe_mode_switch.h @@ -4,16 +4,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index 85d945823e..98bda43f0e 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_native_bif.h b/erts/emulator/hipe/hipe_native_bif.h index 574e20e2e4..0e1a75f7eb 100644 --- a/erts/emulator/hipe/hipe_native_bif.h +++ b/erts/emulator/hipe/hipe_native_bif.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_ops.tab b/erts/emulator/hipe/hipe_ops.tab index 50c3a4ae2f..d021c72ac9 100644 --- a/erts/emulator/hipe/hipe_ops.tab +++ b/erts/emulator/hipe/hipe_ops.tab @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2001-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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/hipe/hipe_ppc.c b/erts/emulator/hipe/hipe_ppc.c index 1eaa9f6855..3f86de626d 100644 --- a/erts/emulator/hipe/hipe_ppc.c +++ b/erts/emulator/hipe/hipe_ppc.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_ppc.h b/erts/emulator/hipe/hipe_ppc.h index e9d3e6564b..777c9384d5 100644 --- a/erts/emulator/hipe/hipe_ppc.h +++ b/erts/emulator/hipe/hipe_ppc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_ppc.tab b/erts/emulator/hipe/hipe_ppc.tab index 38b7f46d3a..5bf2320cbe 100644 --- a/erts/emulator/hipe/hipe_ppc.tab +++ b/erts/emulator/hipe/hipe_ppc.tab @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2004-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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/hipe/hipe_ppc64.tab b/erts/emulator/hipe/hipe_ppc64.tab index 0a390a3bb8..301a9752f7 100644 --- a/erts/emulator/hipe/hipe_ppc64.tab +++ b/erts/emulator/hipe/hipe_ppc64.tab @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/hipe/hipe_ppc_asm.m4 b/erts/emulator/hipe/hipe_ppc_asm.m4 index e5a56de687..ea74923d1f 100644 --- a/erts/emulator/hipe/hipe_ppc_asm.m4 +++ b/erts/emulator/hipe/hipe_ppc_asm.m4 @@ -4,16 +4,17 @@ changecom(`/*', `*/')dnl * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4 index b173b896b8..28518827ec 100644 --- a/erts/emulator/hipe/hipe_ppc_bifs.m4 +++ b/erts/emulator/hipe/hipe_ppc_bifs.m4 @@ -4,16 +4,17 @@ changecom(`/*', `*/')dnl * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_ppc_gc.h b/erts/emulator/hipe/hipe_ppc_gc.h index 823ba0ad06..0854d9e950 100644 --- a/erts/emulator/hipe/hipe_ppc_gc.h +++ b/erts/emulator/hipe/hipe_ppc_gc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_ppc_glue.S b/erts/emulator/hipe/hipe_ppc_glue.S index 109289116b..6e2022603c 100644 --- a/erts/emulator/hipe/hipe_ppc_glue.S +++ b/erts/emulator/hipe/hipe_ppc_glue.S @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_ppc_glue.h b/erts/emulator/hipe/hipe_ppc_glue.h index f9c4460e60..93ca39fcb3 100644 --- a/erts/emulator/hipe/hipe_ppc_glue.h +++ b/erts/emulator/hipe/hipe_ppc_glue.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_ppc_primops.h b/erts/emulator/hipe/hipe_ppc_primops.h index 7dba0afc88..ce3dda9eb3 100644 --- a/erts/emulator/hipe/hipe_ppc_primops.h +++ b/erts/emulator/hipe/hipe_ppc_primops.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_primops.h b/erts/emulator/hipe/hipe_primops.h index 236f6d0a29..adf7b1f382 100644 --- a/erts/emulator/hipe/hipe_primops.h +++ b/erts/emulator/hipe/hipe_primops.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_process.h b/erts/emulator/hipe/hipe_process.h index 86655ad42c..60d09ea1c9 100644 --- a/erts/emulator/hipe/hipe_process.h +++ b/erts/emulator/hipe/hipe_process.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_risc_gc.h b/erts/emulator/hipe/hipe_risc_gc.h index 947eb5956b..770ec93459 100644 --- a/erts/emulator/hipe/hipe_risc_gc.h +++ b/erts/emulator/hipe/hipe_risc_gc.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_risc_glue.h b/erts/emulator/hipe/hipe_risc_glue.h index dbb7086dae..51db7eac01 100644 --- a/erts/emulator/hipe/hipe_risc_glue.h +++ b/erts/emulator/hipe/hipe_risc_glue.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_risc_stack.c b/erts/emulator/hipe/hipe_risc_stack.c index bea3a0fecd..fd04421381 100644 --- a/erts/emulator/hipe/hipe_risc_stack.c +++ b/erts/emulator/hipe/hipe_risc_stack.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_signal.h b/erts/emulator/hipe/hipe_signal.h index 4eacf52b5d..b2f461797d 100644 --- a/erts/emulator/hipe/hipe_signal.h +++ b/erts/emulator/hipe/hipe_signal.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_sparc.c b/erts/emulator/hipe/hipe_sparc.c index fea3b623a9..bd95bc5d98 100644 --- a/erts/emulator/hipe/hipe_sparc.c +++ b/erts/emulator/hipe/hipe_sparc.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_sparc.h b/erts/emulator/hipe/hipe_sparc.h index 2d92ca3ca8..0b74b2db26 100644 --- a/erts/emulator/hipe/hipe_sparc.h +++ b/erts/emulator/hipe/hipe_sparc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_sparc.tab b/erts/emulator/hipe/hipe_sparc.tab index c620c73c67..2f528c0607 100644 --- a/erts/emulator/hipe/hipe_sparc.tab +++ b/erts/emulator/hipe/hipe_sparc.tab @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2004-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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/hipe/hipe_sparc_asm.m4 b/erts/emulator/hipe/hipe_sparc_asm.m4 index 8020104e40..227a1658b4 100644 --- a/erts/emulator/hipe/hipe_sparc_asm.m4 +++ b/erts/emulator/hipe/hipe_sparc_asm.m4 @@ -4,16 +4,17 @@ changecom(`/*', `*/')dnl * * Copyright Ericsson AB 2007-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_sparc_bifs.m4 b/erts/emulator/hipe/hipe_sparc_bifs.m4 index 1d0ff8c16e..a7bbe5b2cc 100644 --- a/erts/emulator/hipe/hipe_sparc_bifs.m4 +++ b/erts/emulator/hipe/hipe_sparc_bifs.m4 @@ -4,16 +4,17 @@ changecom(`/*', `*/')dnl * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_sparc_gc.h b/erts/emulator/hipe/hipe_sparc_gc.h index b870ddd59e..99e55df1ce 100644 --- a/erts/emulator/hipe/hipe_sparc_gc.h +++ b/erts/emulator/hipe/hipe_sparc_gc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_sparc_glue.S b/erts/emulator/hipe/hipe_sparc_glue.S index 094a87fd58..7e47ffb52f 100644 --- a/erts/emulator/hipe/hipe_sparc_glue.S +++ b/erts/emulator/hipe/hipe_sparc_glue.S @@ -4,16 +4,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_sparc_glue.h b/erts/emulator/hipe/hipe_sparc_glue.h index 1404c0d4c0..95ce1e5b4a 100644 --- a/erts/emulator/hipe/hipe_sparc_glue.h +++ b/erts/emulator/hipe/hipe_sparc_glue.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_sparc_primops.h b/erts/emulator/hipe/hipe_sparc_primops.h index 413371e5f0..e42bcbad62 100644 --- a/erts/emulator/hipe/hipe_sparc_primops.h +++ b/erts/emulator/hipe/hipe_sparc_primops.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_stack.c b/erts/emulator/hipe/hipe_stack.c index 53c316ba52..e088b45c57 100644 --- a/erts/emulator/hipe/hipe_stack.c +++ b/erts/emulator/hipe/hipe_stack.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h index 4cfdb54dd8..f575b97ce3 100644 --- a/erts/emulator/hipe/hipe_stack.h +++ b/erts/emulator/hipe/hipe_stack.h @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_x86.c b/erts/emulator/hipe/hipe_x86.c index 998905ea63..19b70afced 100644 --- a/erts/emulator/hipe/hipe_x86.c +++ b/erts/emulator/hipe/hipe_x86.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_x86.h b/erts/emulator/hipe/hipe_x86.h index f29117d0c4..30dc5666ae 100644 --- a/erts/emulator/hipe/hipe_x86.h +++ b/erts/emulator/hipe/hipe_x86.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_x86.tab b/erts/emulator/hipe/hipe_x86.tab index fb33d0a6b9..55fb03cde8 100644 --- a/erts/emulator/hipe/hipe_x86.tab +++ b/erts/emulator/hipe/hipe_x86.tab @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2004-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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/hipe/hipe_x86_abi.txt b/erts/emulator/hipe/hipe_x86_abi.txt index aa04a12611..b74fcef127 100644 --- a/erts/emulator/hipe/hipe_x86_abi.txt +++ b/erts/emulator/hipe/hipe_x86_abi.txt @@ -3,16 +3,17 @@ Copyright Ericsson AB 2001-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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - 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. + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. %CopyrightEnd% diff --git a/erts/emulator/hipe/hipe_x86_asm.m4 b/erts/emulator/hipe/hipe_x86_asm.m4 index 436feca506..3457574622 100644 --- a/erts/emulator/hipe/hipe_x86_asm.m4 +++ b/erts/emulator/hipe/hipe_x86_asm.m4 @@ -4,16 +4,17 @@ changecom(`/*', `*/')dnl * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4 index bf549c90e4..0c0a8da6eb 100644 --- a/erts/emulator/hipe/hipe_x86_bifs.m4 +++ b/erts/emulator/hipe/hipe_x86_bifs.m4 @@ -4,16 +4,17 @@ changecom(`/*', `*/')dnl * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_x86_gc.h b/erts/emulator/hipe/hipe_x86_gc.h index ac6b4f70bb..a8770ef4f9 100644 --- a/erts/emulator/hipe/hipe_x86_gc.h +++ b/erts/emulator/hipe/hipe_x86_gc.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_x86_glue.S b/erts/emulator/hipe/hipe_x86_glue.S index f124e36a26..cf382c480d 100644 --- a/erts/emulator/hipe/hipe_x86_glue.S +++ b/erts/emulator/hipe/hipe_x86_glue.S @@ -4,16 +4,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_x86_glue.h b/erts/emulator/hipe/hipe_x86_glue.h index 4b6e495b9a..6c123e8938 100644 --- a/erts/emulator/hipe/hipe_x86_glue.h +++ b/erts/emulator/hipe/hipe_x86_glue.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_x86_primops.h b/erts/emulator/hipe/hipe_x86_primops.h index 111b1fa8bd..3e057059bc 100644 --- a/erts/emulator/hipe/hipe_x86_primops.h +++ b/erts/emulator/hipe/hipe_x86_primops.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index f5668013e2..bb8a3f041f 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -4,16 +4,17 @@ * * Copyright Ericsson AB 2001-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/hipe/hipe_x86_stack.c b/erts/emulator/hipe/hipe_x86_stack.c index 7f1c2f7d41..6629713a05 100644 --- a/erts/emulator/hipe/hipe_x86_stack.c +++ b/erts/emulator/hipe/hipe_x86_stack.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/internal_doc/dec.erl b/erts/emulator/internal_doc/dec.erl index dc995989fb..e64e37a7d9 100644 --- a/erts/emulator/internal_doc/dec.erl +++ b/erts/emulator/internal_doc/dec.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2000-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -64,18 +65,17 @@ dec() -> "*~n" "* Copyright Ericsson AB 1999-2010. All Rights Reserved.~n" "*~n" - "* The contents of this file are subject to the Erlang Public License,~n" - "* Version 1.1, (the \"License\"); you may not use this file except in~n" - "* compliance with the License. You should have received a copy of the~n" - "* Erlang Public License along with this software. If not, it can be~n" - "* retrieved online at http://www.erlang.org/.~n" + "* Licensed under the Apache License, Version 2.0 (the \"License\");~n" + "* you may not use this file except in compliance with the License.~n" + "* You may obtain a copy of the License at~n" + "*~n" + "* http://www.apache.org/licenses/LICENSE-2.0~n" "*~n" - "* Software distributed under the License is distributed on an " - "\"AS IS\"~n" - "* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See~n" - "* the License for the specific language governing rights and " - "limitations~n" - "* under the License.~n" + "* Unless required by applicable law or agreed to in writing, software~n" + "* distributed under the License is distributed on an \"AS IS\" BASIS,~n" + "* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.~n" + "* See the License for the specific language governing permissions and~n" + "* limitations under the License.~n" "*~n" "* %CopyrightEnd%~n" "*/~n" diff --git a/erts/emulator/internal_doc/erl_ext_dist.txt b/erts/emulator/internal_doc/erl_ext_dist.txt index 1cbbbcc7b8..23b7d0d8e5 100644 --- a/erts/emulator/internal_doc/erl_ext_dist.txt +++ b/erts/emulator/internal_doc/erl_ext_dist.txt @@ -3,16 +3,17 @@ 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - 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. + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. %CopyrightEnd% diff --git a/erts/emulator/pcre/pcre.mk b/erts/emulator/pcre/pcre.mk index 54412f6b1d..b844b77214 100644 --- a/erts/emulator/pcre/pcre.mk +++ b/erts/emulator/pcre/pcre.mk @@ -3,16 +3,17 @@ # # 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 3097f7b7dd..105b129065 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_check_io.h b/erts/emulator/sys/common/erl_check_io.h index 71355965aa..c8675a7d9d 100644 --- a/erts/emulator/sys/common/erl_check_io.h +++ b/erts/emulator/sys/common/erl_check_io.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 3f6813e1a5..754047829f 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2002-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 778a8e0e80..66619c5161 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 94a381e168..0d51aad863 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2002-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index 2284b3f8f1..ba04e919fc 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2002-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_mtrace_sys_wrap.c b/erts/emulator/sys/common/erl_mtrace_sys_wrap.c index 408aa7e016..a8c575835a 100644 --- a/erts/emulator/sys/common/erl_mtrace_sys_wrap.c +++ b/erts/emulator/sys/common/erl_mtrace_sys_wrap.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_os_monotonic_time_extender.c b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c index f3633b7267..b79485241f 100644 --- a/erts/emulator/sys/common/erl_os_monotonic_time_extender.c +++ b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2015. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_os_monotonic_time_extender.h b/erts/emulator/sys/common/erl_os_monotonic_time_extender.h index 0f9e7c86ae..8089c9aed9 100644 --- a/erts/emulator/sys/common/erl_os_monotonic_time_extender.h +++ b/erts/emulator/sys/common/erl_os_monotonic_time_extender.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2015. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 68d3c30bd4..4f0a54957b 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index ad8f714d6e..19ce582154 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c index e63f0bda54..e292741bfa 100644 --- a/erts/emulator/sys/common/erl_sys_common_misc.c +++ b/erts/emulator/sys/common/erl_sys_common_misc.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/common/erl_util_queue.h b/erts/emulator/sys/common/erl_util_queue.h index 47925e2264..b50e062dd5 100644 --- a/erts/emulator/sys/common/erl_util_queue.h +++ b/erts/emulator/sys/common/erl_util_queue.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/ose/driver_int.h b/erts/emulator/sys/ose/driver_int.h index 2c9ac955d8..4a5b7171d1 100644 --- a/erts/emulator/sys/ose/driver_int.h +++ b/erts/emulator/sys/ose/driver_int.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/ose/erl_main.c b/erts/emulator/sys/ose/erl_main.c index 23a9bc93a4..877e85f43a 100644 --- a/erts/emulator/sys/ose/erl_main.c +++ b/erts/emulator/sys/ose/erl_main.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/ose/erl_ose_sys.h b/erts/emulator/sys/ose/erl_ose_sys.h index f6526a4714..d0cd3180bf 100644 --- a/erts/emulator/sys/ose/erl_ose_sys.h +++ b/erts/emulator/sys/ose/erl_ose_sys.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% * diff --git a/erts/emulator/sys/ose/erl_ose_sys_ddll.c b/erts/emulator/sys/ose/erl_ose_sys_ddll.c index ebd80deeaf..5051f7fcc1 100644 --- a/erts/emulator/sys/ose/erl_ose_sys_ddll.c +++ b/erts/emulator/sys/ose/erl_ose_sys_ddll.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c index 3d4ac0365f..5cee582a00 100644 --- a/erts/emulator/sys/ose/erl_poll.c +++ b/erts/emulator/sys/ose/erl_poll.c @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index 13a5b71496..bcd0ffa0b6 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/ose/sys_float.c b/erts/emulator/sys/ose/sys_float.c index d9d6bb7c04..3d9abc6bd1 100644 --- a/erts/emulator/sys/ose/sys_float.c +++ b/erts/emulator/sys/ose/sys_float.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/ose/sys_time.c b/erts/emulator/sys/ose/sys_time.c index 7e96f68424..5dac75956a 100644 --- a/erts/emulator/sys/ose/sys_time.c +++ b/erts/emulator/sys/ose/sys_time.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/unix/driver_int.h b/erts/emulator/sys/unix/driver_int.h index a7ee8087ab..a6b9085245 100644 --- a/erts/emulator/sys/unix/driver_int.h +++ b/erts/emulator/sys/unix/driver_int.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index e1d2f66b7e..a3c5c20641 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/unix/erl_main.c b/erts/emulator/sys/unix/erl_main.c index b26f93f77e..c417bea622 100644 --- a/erts/emulator/sys/unix/erl_main.c +++ b/erts/emulator/sys/unix/erl_main.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 1942b631fc..8d4e98bf3a 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% * diff --git a/erts/emulator/sys/unix/erl_unix_sys_ddll.c b/erts/emulator/sys/unix/erl_unix_sys_ddll.c index 2659d623c7..daed5af1b6 100644 --- a/erts/emulator/sys/unix/erl_unix_sys_ddll.c +++ b/erts/emulator/sys/unix/erl_unix_sys_ddll.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 7d52650a70..b036b20b7b 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c index c30ef7cce2..1ef9e5eef7 100644 --- a/erts/emulator/sys/unix/sys_float.c +++ b/erts/emulator/sys/unix/sys_float.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index dc1822b21c..2e1914f564 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/dosmap.c b/erts/emulator/sys/win32/dosmap.c index 15416a66c5..a98065c666 100644 --- a/erts/emulator/sys/win32/dosmap.c +++ b/erts/emulator/sys/win32/dosmap.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/driver_int.h b/erts/emulator/sys/win32/driver_int.h index 97e188816e..b931da52f5 100644 --- a/erts/emulator/sys/win32/driver_int.h +++ b/erts/emulator/sys/win32/driver_int.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/erl_main.c b/erts/emulator/sys/win32/erl_main.c index 5471bffb52..21d9a58da6 100644 --- a/erts/emulator/sys/win32/erl_main.c +++ b/erts/emulator/sys/win32/erl_main.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 9196561944..466f4a3b48 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2007-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/erl_win32_sys_ddll.c b/erts/emulator/sys/win32/erl_win32_sys_ddll.c index 338f0d7386..9a5557e93d 100644 --- a/erts/emulator/sys/win32/erl_win32_sys_ddll.c +++ b/erts/emulator/sys/win32/erl_win32_sys_ddll.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h index 4010d939e5..5e62320be4 100644 --- a/erts/emulator/sys/win32/erl_win_dyn_driver.h +++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index a9e37e47a7..7e8dd8a4ca 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1997-2014. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index cf587af4ac..fce76db28f 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c index 9f977ad6c8..67b6e55377 100644 --- a/erts/emulator/sys/win32/sys_env.c +++ b/erts/emulator/sys/win32/sys_env.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/sys_float.c b/erts/emulator/sys/win32/sys_float.c index 0e19746cf5..86e822da5b 100644 --- a/erts/emulator/sys/win32/sys_float.c +++ b/erts/emulator/sys/win32/sys_float.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1997-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/sys_interrupt.c b/erts/emulator/sys/win32/sys_interrupt.c index 36c43e93da..d6178de03c 100644 --- a/erts/emulator/sys/win32/sys_interrupt.c +++ b/erts/emulator/sys/win32/sys_interrupt.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index 7da060a7a7..9e5f78703a 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1997-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 4dc4b5027d..77614d455c 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -3,16 +3,17 @@ # # 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl index 17579be416..16f060fe34 100644 --- a/erts/emulator/test/a_SUITE.erl +++ b/erts/emulator/test/a_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl index c855481489..5017a83185 100644 --- a/erts/emulator/test/after_SUITE.erl +++ b/erts/emulator/test/after_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 12a48cc484..7c7ddde5d4 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2003-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% diff --git a/erts/emulator/test/alloc_SUITE_data/Makefile.src b/erts/emulator/test/alloc_SUITE_data/Makefile.src index 1d4286e671..a441fe946b 100644 --- a/erts/emulator/test/alloc_SUITE_data/Makefile.src +++ b/erts/emulator/test/alloc_SUITE_data/Makefile.src @@ -1,13 +1,14 @@ -# ``The 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. +# ``Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # The Initial Developer of the Original Code is Ericsson Utvecklings AB. # Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/alloc_SUITE_data/allocator_test.h b/erts/emulator/test/alloc_SUITE_data/allocator_test.h index 2d6c5f9dc7..1d6b2f4907 100644 --- a/erts/emulator/test/alloc_SUITE_data/allocator_test.h +++ b/erts/emulator/test/alloc_SUITE_data/allocator_test.h @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/alloc_SUITE_data/basic.c b/erts/emulator/test/alloc_SUITE_data/basic.c index 0c27665712..323a24a11f 100644 --- a/erts/emulator/test/alloc_SUITE_data/basic.c +++ b/erts/emulator/test/alloc_SUITE_data/basic.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/alloc_SUITE_data/bucket_index.c b/erts/emulator/test/alloc_SUITE_data/bucket_index.c index 32fd16fc10..c13f229049 100644 --- a/erts/emulator/test/alloc_SUITE_data/bucket_index.c +++ b/erts/emulator/test/alloc_SUITE_data/bucket_index.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/alloc_SUITE_data/bucket_mask.c b/erts/emulator/test/alloc_SUITE_data/bucket_mask.c index 34979cacf1..8d6166771e 100644 --- a/erts/emulator/test/alloc_SUITE_data/bucket_mask.c +++ b/erts/emulator/test/alloc_SUITE_data/bucket_mask.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/alloc_SUITE_data/coalesce.c b/erts/emulator/test/alloc_SUITE_data/coalesce.c index 9da49a0d14..0a5e0c5b0e 100644 --- a/erts/emulator/test/alloc_SUITE_data/coalesce.c +++ b/erts/emulator/test/alloc_SUITE_data/coalesce.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/alloc_SUITE_data/cpool.c b/erts/emulator/test/alloc_SUITE_data/cpool.c index 276ed7be04..75c2bc13ae 100644 --- a/erts/emulator/test/alloc_SUITE_data/cpool.c +++ b/erts/emulator/test/alloc_SUITE_data/cpool.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c b/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c index 7d5608f890..9c03f3a331 100644 --- a/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c +++ b/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/alloc_SUITE_data/rbtree.c b/erts/emulator/test/alloc_SUITE_data/rbtree.c index 49df2f0245..8d4d5535a8 100644 --- a/erts/emulator/test/alloc_SUITE_data/rbtree.c +++ b/erts/emulator/test/alloc_SUITE_data/rbtree.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/alloc_SUITE_data/realloc_copy.c b/erts/emulator/test/alloc_SUITE_data/realloc_copy.c index 12454c75e4..e405f06225 100644 --- a/erts/emulator/test/alloc_SUITE_data/realloc_copy.c +++ b/erts/emulator/test/alloc_SUITE_data/realloc_copy.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c index 5c4b11454f..bc674c56b7 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h index 66d567cb44..5d17eaec64 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/alloc_SUITE_data/threads.c b/erts/emulator/test/alloc_SUITE_data/threads.c index 1247e5d7dd..edad24ee6b 100644 --- a/erts/emulator/test/alloc_SUITE_data/threads.c +++ b/erts/emulator/test/alloc_SUITE_data/threads.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl index 60339ea422..706a4a1c16 100644 --- a/erts/emulator/test/beam_SUITE.erl +++ b/erts/emulator/test/beam_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1998-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/beam_literals_SUITE.erl b/erts/emulator/test/beam_literals_SUITE.erl index 9ceb393034..9f14ca26e5 100644 --- a/erts/emulator/test/beam_literals_SUITE.erl +++ b/erts/emulator/test/beam_literals_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1999-2015. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index fc9bdae0a0..d6a771e7b9 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl index 3193d56e2a..e8f881f2a4 100644 --- a/erts/emulator/test/big_SUITE.erl +++ b/erts/emulator/test/big_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/big_SUITE_data/literal_test.erl b/erts/emulator/test/big_SUITE_data/literal_test.erl index dc0adeb1ca..1620693bfa 100644 --- a/erts/emulator/test/big_SUITE_data/literal_test.erl +++ b/erts/emulator/test/big_SUITE_data/literal_test.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 5911652447..96ba2f64d4 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/bs_bincomp_SUITE.erl b/erts/emulator/test/bs_bincomp_SUITE.erl index f1c2dff560..dcd13c19df 100644 --- a/erts/emulator/test/bs_bincomp_SUITE.erl +++ b/erts/emulator/test/bs_bincomp_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/bs_bit_binaries_SUITE.erl b/erts/emulator/test/bs_bit_binaries_SUITE.erl index 7f2cd1e881..a07fd7609c 100644 --- a/erts/emulator/test/bs_bit_binaries_SUITE.erl +++ b/erts/emulator/test/bs_bit_binaries_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 1a8724d63b..cadb30e1a4 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/bs_match_bin_SUITE.erl b/erts/emulator/test/bs_match_bin_SUITE.erl index 96e69dbc0b..ba79643e69 100644 --- a/erts/emulator/test/bs_match_bin_SUITE.erl +++ b/erts/emulator/test/bs_match_bin_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/bs_match_int_SUITE.erl b/erts/emulator/test/bs_match_int_SUITE.erl index ce03ecb548..368f71978d 100644 --- a/erts/emulator/test/bs_match_int_SUITE.erl +++ b/erts/emulator/test/bs_match_int_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% diff --git a/erts/emulator/test/bs_match_misc_SUITE.erl b/erts/emulator/test/bs_match_misc_SUITE.erl index b0904acbe9..e875dc859c 100644 --- a/erts/emulator/test/bs_match_misc_SUITE.erl +++ b/erts/emulator/test/bs_match_misc_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/bs_match_tail_SUITE.erl b/erts/emulator/test/bs_match_tail_SUITE.erl index 1397f2069c..58b0d3fef6 100644 --- a/erts/emulator/test/bs_match_tail_SUITE.erl +++ b/erts/emulator/test/bs_match_tail_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/bs_utf_SUITE.erl b/erts/emulator/test/bs_utf_SUITE.erl index 4ab7d674a6..0625c22163 100644 --- a/erts/emulator/test/bs_utf_SUITE.erl +++ b/erts/emulator/test/bs_utf_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index d44a03516a..6a2588aadd 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/busy_port_SUITE_data/Makefile.src b/erts/emulator/test/busy_port_SUITE_data/Makefile.src index f7937bc8d3..0f2842e515 100644 --- a/erts/emulator/test/busy_port_SUITE_data/Makefile.src +++ b/erts/emulator/test/busy_port_SUITE_data/Makefile.src @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 1997-2013. All Rights Reserved. # -# The 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c b/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c index 52c41f8ca5..f83fa1eeaa 100644 --- a/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c +++ b/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c b/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c index 7807b47e3d..be913cf56e 100644 --- a/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c +++ b/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c b/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c index 2008b33a72..40e42b6ac2 100644 --- a/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c +++ b/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c b/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c index 30bcd86d1d..3c5bafb451 100644 --- a/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c +++ b/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index ef1f2aa04c..064404a038 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index df7c8ed1d1..9f318a38be 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/code_SUITE_data/another_code_test.erl b/erts/emulator/test/code_SUITE_data/another_code_test.erl index 1c9b5bdb5b..f6f9e32996 100644 --- a/erts/emulator/test/code_SUITE_data/another_code_test.erl +++ b/erts/emulator/test/code_SUITE_data/another_code_test.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/code_SUITE_data/cpbugx.erl b/erts/emulator/test/code_SUITE_data/cpbugx.erl index fb617c1973..ea01ce411b 100644 --- a/erts/emulator/test/code_SUITE_data/cpbugx.erl +++ b/erts/emulator/test/code_SUITE_data/cpbugx.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2008-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/code_SUITE_data/fun_confusion.erl b/erts/emulator/test/code_SUITE_data/fun_confusion.erl index 16000861df..8d42937d3c 100644 --- a/erts/emulator/test/code_SUITE_data/fun_confusion.erl +++ b/erts/emulator/test/code_SUITE_data/fun_confusion.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/code_SUITE_data/literals.erl b/erts/emulator/test/code_SUITE_data/literals.erl index 658427095e..9802d9d3f9 100644 --- a/erts/emulator/test/code_SUITE_data/literals.erl +++ b/erts/emulator/test/code_SUITE_data/literals.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2007-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/code_SUITE_data/many_funs.erl b/erts/emulator/test/code_SUITE_data/many_funs.erl index 0f9c3a85a4..e832f271d0 100644 --- a/erts/emulator/test/code_SUITE_data/many_funs.erl +++ b/erts/emulator/test/code_SUITE_data/many_funs.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2007-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/code_SUITE_data/my_code_test.erl b/erts/emulator/test/code_SUITE_data/my_code_test.erl index 5039b7f937..57d867a5ac 100644 --- a/erts/emulator/test/code_SUITE_data/my_code_test.erl +++ b/erts/emulator/test/code_SUITE_data/my_code_test.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1999-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/code_SUITE_data/versions.erl b/erts/emulator/test/code_SUITE_data/versions.erl index 7a6fd8847d..0e2d92c8f1 100644 --- a/erts/emulator/test/code_SUITE_data/versions.erl +++ b/erts/emulator/test/code_SUITE_data/versions.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl index bcec8fa640..b7ac0420cd 100644 --- a/erts/emulator/test/code_parallel_load_SUITE.erl +++ b/erts/emulator/test/code_parallel_load_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2012-2014. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/crypto_SUITE.erl b/erts/emulator/test/crypto_SUITE.erl index a82bd4fe38..3622592586 100644 --- a/erts/emulator/test/crypto_SUITE.erl +++ b/erts/emulator/test/crypto_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/crypto_reference.erl b/erts/emulator/test/crypto_reference.erl index b91535a50e..7797eacd75 100644 --- a/erts/emulator/test/crypto_reference.erl +++ b/erts/emulator/test/crypto_reference.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2008-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl index ffb68ed4ee..cabd6472d4 100644 --- a/erts/emulator/test/ddll_SUITE.erl +++ b/erts/emulator/test/ddll_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl index 330ad299e5..6a5ca20ac3 100644 --- a/erts/emulator/test/decode_packet_SUITE.erl +++ b/erts/emulator/test/decode_packet_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/dgawd_handler.erl b/erts/emulator/test/dgawd_handler.erl index 27085b7b7e..bba69ef87e 100644 --- a/erts/emulator/test/dgawd_handler.erl +++ b/erts/emulator/test/dgawd_handler.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 33cb56c0b9..d71cedbdc5 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/distribution_SUITE_data/run.erl b/erts/emulator/test/distribution_SUITE_data/run.erl index e2137a1ec5..f5169e160c 100644 --- a/erts/emulator/test/distribution_SUITE_data/run.erl +++ b/erts/emulator/test/distribution_SUITE_data/run.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index e6beda1ccf..4211c49848 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% diff --git a/erts/emulator/test/driver_SUITE_data/async_blast_drv.c b/erts/emulator/test/driver_SUITE_data/async_blast_drv.c index 72be107168..a1008afcae 100644 --- a/erts/emulator/test/driver_SUITE_data/async_blast_drv.c +++ b/erts/emulator/test/driver_SUITE_data/async_blast_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2011-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/driver_SUITE_data/caller_drv.c b/erts/emulator/test/driver_SUITE_data/caller_drv.c index 2731f9b317..3bb20d86f5 100644 --- a/erts/emulator/test/driver_SUITE_data/caller_drv.c +++ b/erts/emulator/test/driver_SUITE_data/caller_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/chkio_drv.c b/erts/emulator/test/driver_SUITE_data/chkio_drv.c index faf1040276..614b68e865 100644 --- a/erts/emulator/test/driver_SUITE_data/chkio_drv.c +++ b/erts/emulator/test/driver_SUITE_data/chkio_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c b/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c index 578a210ab2..192ac02d3e 100644 --- a/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c +++ b/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2012-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/driver_SUITE_data/invalid_extended_marker_drv.c b/erts/emulator/test/driver_SUITE_data/invalid_extended_marker_drv.c index 59145447f8..4bc5b85338 100644 --- a/erts/emulator/test/driver_SUITE_data/invalid_extended_marker_drv.c +++ b/erts/emulator/test/driver_SUITE_data/invalid_extended_marker_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings 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 1e107309df..11ccd2574e 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 @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings 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 e2b338f801..e2221b9e17 100644 --- a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c +++ b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2007-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/driver_SUITE_data/larger_major_vsn_drv.c b/erts/emulator/test/driver_SUITE_data/larger_major_vsn_drv.c index 4eb0e6fa57..3e46d89288 100644 --- a/erts/emulator/test/driver_SUITE_data/larger_major_vsn_drv.c +++ b/erts/emulator/test/driver_SUITE_data/larger_major_vsn_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/larger_minor_vsn_drv.c b/erts/emulator/test/driver_SUITE_data/larger_minor_vsn_drv.c index 396deb9bef..d7336ed59b 100644 --- a/erts/emulator/test/driver_SUITE_data/larger_minor_vsn_drv.c +++ b/erts/emulator/test/driver_SUITE_data/larger_minor_vsn_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings 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 851f2c745b..e7480d2e00 100644 --- a/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c +++ b/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson AB. Portions * created by Ericsson are Copyright 2008, Ericsson Utvecklings AB. All diff --git a/erts/emulator/test/driver_SUITE_data/monitor_drv.c b/erts/emulator/test/driver_SUITE_data/monitor_drv.c index 81dfb65191..1418250484 100644 --- a/erts/emulator/test/driver_SUITE_data/monitor_drv.c +++ b/erts/emulator/test/driver_SUITE_data/monitor_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/otp_6879_drv.c b/erts/emulator/test/driver_SUITE_data/otp_6879_drv.c index ff44145ca7..594a996ab9 100644 --- a/erts/emulator/test/driver_SUITE_data/otp_6879_drv.c +++ b/erts/emulator/test/driver_SUITE_data/otp_6879_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c index ad29d17f06..fdf8e4c0ad 100644 --- a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c +++ b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2011-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/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 cbee1c3dce..685cda3e07 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 @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c b/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c index 6b9d4745ba..d50546b919 100644 --- a/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c +++ b/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/smaller_minor_vsn_drv.c b/erts/emulator/test/driver_SUITE_data/smaller_minor_vsn_drv.c index 42b1d2a187..082694de82 100644 --- a/erts/emulator/test/driver_SUITE_data/smaller_minor_vsn_drv.c +++ b/erts/emulator/test/driver_SUITE_data/smaller_minor_vsn_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c index 964034f5a6..c69961928e 100644 --- a/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c +++ b/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c @@ -1,13 +1,14 @@ -/* ``The 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/. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c index 6d2c47fdaf..a847dbb6ad 100644 --- a/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c +++ b/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.c b/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.c index c6c70a2075..7c22e2c365 100644 --- a/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.c +++ b/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.h b/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.h index 5a6ddb15cf..35e60c0d86 100644 --- a/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.h +++ b/erts/emulator/test/driver_SUITE_data/sys_info_drv_impl.h @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c index 2271d7027b..33a4794fba 100644 --- a/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c +++ b/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c @@ -1,13 +1,14 @@ -/* ``The 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/. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/thr_alloc_drv.c b/erts/emulator/test/driver_SUITE_data/thr_alloc_drv.c index 500725a7cd..f1f2b438f0 100644 --- a/erts/emulator/test/driver_SUITE_data/thr_alloc_drv.c +++ b/erts/emulator/test/driver_SUITE_data/thr_alloc_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/thr_free_drv.c b/erts/emulator/test/driver_SUITE_data/thr_free_drv.c index 439fe6a184..54205f190e 100644 --- a/erts/emulator/test/driver_SUITE_data/thr_free_drv.c +++ b/erts/emulator/test/driver_SUITE_data/thr_free_drv.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c b/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c index 5a9112afa3..f7a7cc2b8e 100644 --- a/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c +++ b/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/driver_SUITE_data/vsn_mismatch_drv_impl.c b/erts/emulator/test/driver_SUITE_data/vsn_mismatch_drv_impl.c index 53b0a029ce..6750b8a78b 100644 --- a/erts/emulator/test/driver_SUITE_data/vsn_mismatch_drv_impl.c +++ b/erts/emulator/test/driver_SUITE_data/vsn_mismatch_drv_impl.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/driver_SUITE_data/zero_extended_marker_garb_drv.c b/erts/emulator/test/driver_SUITE_data/zero_extended_marker_garb_drv.c index ed705e565f..e7f4edde08 100644 --- a/erts/emulator/test/driver_SUITE_data/zero_extended_marker_garb_drv.c +++ b/erts/emulator/test/driver_SUITE_data/zero_extended_marker_garb_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index f79bb761d1..4d8d89db9b 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl index 84a82cced0..2cd569ce4f 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE.erl +++ b/erts/emulator/test/erl_drv_thread_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2007-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/Makefile.src b/erts/emulator/test/erl_drv_thread_SUITE_data/Makefile.src index 216707e8a5..169fad9231 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE_data/Makefile.src +++ b/erts/emulator/test/erl_drv_thread_SUITE_data/Makefile.src @@ -1,13 +1,14 @@ -# ``The 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. +# ``Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # The Initial Developer of the Original Code is Ericsson Utvecklings AB. # Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/basic.c b/erts/emulator/test/erl_drv_thread_SUITE_data/basic.c index fca2c1dbea..8ceb9eff08 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE_data/basic.c +++ b/erts/emulator/test/erl_drv_thread_SUITE_data/basic.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/rwlock.c b/erts/emulator/test/erl_drv_thread_SUITE_data/rwlock.c index 064f52c16b..98d0162b55 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE_data/rwlock.c +++ b/erts/emulator/test/erl_drv_thread_SUITE_data/rwlock.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c b/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c index 2cd3209231..7f5faf9121 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c +++ b/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.h b/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.h index 18d5229780..bffe4f9f27 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.h +++ b/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.h @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/tsd.c b/erts/emulator/test/erl_drv_thread_SUITE_data/tsd.c index 3809c915e0..4c952767c2 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE_data/tsd.c +++ b/erts/emulator/test/erl_drv_thread_SUITE_data/tsd.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index 02c1d84d59..a7a45046ca 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2001-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl index bc5928436f..3dd77eb920 100644 --- a/erts/emulator/test/erts_debug_SUITE.erl +++ b/erts/emulator/test/erts_debug_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index 67a53d94b1..dc8f0aaee9 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2002-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% diff --git a/erts/emulator/test/evil_SUITE.erl b/erts/emulator/test/evil_SUITE.erl index f982b9d4ff..484d2a8bf5 100644 --- a/erts/emulator/test/evil_SUITE.erl +++ b/erts/emulator/test/evil_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index 09a7a87a9a..11caea3698 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index a07516b5a9..c1a76b8af4 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/float_SUITE_data/fp_drv.c b/erts/emulator/test/float_SUITE_data/fp_drv.c index 82d18d6440..5919dd8e2f 100644 --- a/erts/emulator/test/float_SUITE_data/fp_drv.c +++ b/erts/emulator/test/float_SUITE_data/fp_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson AB. Portions * created by Ericsson are Copyright 2008, Ericsson AB. All Rights diff --git a/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl b/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl index 31af2b2698..79ab74dfff 100644 --- a/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl +++ b/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 2968f5bebb..b18f9f5c6b 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/fun_r13_SUITE.erl b/erts/emulator/test/fun_r13_SUITE.erl index 76ddf9fec9..7ab5e65cb3 100644 --- a/erts/emulator/test/fun_r13_SUITE.erl +++ b/erts/emulator/test/fun_r13_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2007-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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl index 1b92e3198e..1e155e7b09 100644 --- a/erts/emulator/test/gc_SUITE.erl +++ b/erts/emulator/test/gc_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/guard_SUITE.erl b/erts/emulator/test/guard_SUITE.erl index a5df9b59a0..b3a85c6423 100644 --- a/erts/emulator/test/guard_SUITE.erl +++ b/erts/emulator/test/guard_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl index 5fa45f772d..2ea49467b8 100644 --- a/erts/emulator/test/hash_SUITE.erl +++ b/erts/emulator/test/hash_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2000-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl index 31938e6c65..4ac8c272db 100644 --- a/erts/emulator/test/hibernate_SUITE.erl +++ b/erts/emulator/test/hibernate_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/ignore_cores.erl b/erts/emulator/test/ignore_cores.erl index 8b1ac0fe6c..13f34cd10f 100644 --- a/erts/emulator/test/ignore_cores.erl +++ b/erts/emulator/test/ignore_cores.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2008-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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/list_bif_SUITE.erl b/erts/emulator/test/list_bif_SUITE.erl index 45a44d8b43..9e930822cf 100644 --- a/erts/emulator/test/list_bif_SUITE.erl +++ b/erts/emulator/test/list_bif_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/long_timers_test.erl b/erts/emulator/test/long_timers_test.erl index 8dd960ffd4..9415e1cced 100644 --- a/erts/emulator/test/long_timers_test.erl +++ b/erts/emulator/test/long_timers_test.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index b739250aad..1a89101916 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -2,16 +2,17 @@ %% %% Copyright Ericsson AB 2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index d3c884689f..c6cc414bba 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1999-2014. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index 25ba6d1787..7c2101ca05 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/module_info_SUITE_data/module_info_test.erl b/erts/emulator/test/module_info_SUITE_data/module_info_test.erl index f045f38464..6eaf21b9c9 100644 --- a/erts/emulator/test/module_info_SUITE_data/module_info_test.erl +++ b/erts/emulator/test/module_info_SUITE_data/module_info_test.erl @@ -3,17 +3,17 @@ %% %% Copyright Ericsson AB 2015. All Rights Reserved. %% -%% The 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. -%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %CopyrightEnd% %% diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 7326dfceb1..4db17969c0 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl index 8dcd21f303..87dace4721 100644 --- a/erts/emulator/test/mtx_SUITE.erl +++ b/erts/emulator/test/mtx_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2010-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/mtx_SUITE_data/Makefile.src b/erts/emulator/test/mtx_SUITE_data/Makefile.src index 04412280c0..dc880118f1 100644 --- a/erts/emulator/test/mtx_SUITE_data/Makefile.src +++ b/erts/emulator/test/mtx_SUITE_data/Makefile.src @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2010-2013. All Rights Reserved. # -# The 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c b/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c index 7c8137dc83..1911291448 100644 --- a/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c +++ b/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/nested_SUITE.erl b/erts/emulator/test/nested_SUITE.erl index 2cd67ebaae..7cfa837ee5 100644 --- a/erts/emulator/test/nested_SUITE.erl +++ b/erts/emulator/test/nested_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 778f6fd087..af2b955184 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2010-2014. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 1639e47d61..98e1efe18f 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c index 11b5d0cc35..9c78c0e04d 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.c +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl index 6634624698..e65d4577c7 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/nif_SUITE_data/testcase_driver.h b/erts/emulator/test/nif_SUITE_data/testcase_driver.h index 98339e4746..e32e63069a 100644 --- a/erts/emulator/test/nif_SUITE_data/testcase_driver.h +++ b/erts/emulator/test/nif_SUITE_data/testcase_driver.h @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 479e6f200a..fecaad5232 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl index 57eec87e63..3660a58c56 100644 --- a/erts/emulator/test/nofrag_SUITE.erl +++ b/erts/emulator/test/nofrag_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index abe5b8eb91..f07f79b83d 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/old_mod.erl b/erts/emulator/test/old_mod.erl index 124842390a..1586a024d8 100644 --- a/erts/emulator/test/old_mod.erl +++ b/erts/emulator/test/old_mod.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2003-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl index 57f6928185..97c99fe07b 100644 --- a/erts/emulator/test/old_scheduler_SUITE.erl +++ b/erts/emulator/test/old_scheduler_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2004-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl index 26f6837f19..6eda78a57b 100644 --- a/erts/emulator/test/op_SUITE.erl +++ b/erts/emulator/test/op_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1999-2015. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index e61c330861..3d0509a28c 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/port_SUITE_data/dead_port.c b/erts/emulator/test/port_SUITE_data/dead_port.c index ea91546b0c..c859dbc402 100644 --- a/erts/emulator/test/port_SUITE_data/dead_port.c +++ b/erts/emulator/test/port_SUITE_data/dead_port.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2001-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/test/port_SUITE_data/port_test.erl b/erts/emulator/test/port_SUITE_data/port_test.erl index 56abfd5ded..b07038e73d 100644 --- a/erts/emulator/test/port_SUITE_data/port_test.erl +++ b/erts/emulator/test/port_SUITE_data/port_test.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index 0c5b09d45a..b65a22a528 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 105d39f126..4c311e1f06 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/pseudoknot_SUITE.erl b/erts/emulator/test/pseudoknot_SUITE.erl index 5a7cdcecd5..58ef3cd563 100644 --- a/erts/emulator/test/pseudoknot_SUITE.erl +++ b/erts/emulator/test/pseudoknot_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2001-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/random_iolist.erl b/erts/emulator/test/random_iolist.erl index 8f21b5a3b3..9a0f034e72 100644 --- a/erts/emulator/test/random_iolist.erl +++ b/erts/emulator/test/random_iolist.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2008-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl index 2e7ac1f50c..ccae0df72e 100644 --- a/erts/emulator/test/receive_SUITE.erl +++ b/erts/emulator/test/receive_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2010-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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/ref_SUITE.erl b/erts/emulator/test/ref_SUITE.erl index e13dfa1575..1042c23d65 100644 --- a/erts/emulator/test/ref_SUITE.erl +++ b/erts/emulator/test/ref_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/register_SUITE.erl b/erts/emulator/test/register_SUITE.erl index 9953df3458..5ecca0f547 100644 --- a/erts/emulator/test/register_SUITE.erl +++ b/erts/emulator/test/register_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2010-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl index ddf8b2d919..544d841f16 100644 --- a/erts/emulator/test/save_calls_SUITE.erl +++ b/erts/emulator/test/save_calls_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1999-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index c5af12c6d1..986a73ebb1 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index 4deae63ce7..670865cd3f 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2005-2014. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -357,16 +358,17 @@ write_bytes(IoDev, Prefix, [B|Bs], N) -> write_bytes(IoDev, ",", Bs, N+1). write_license(IoDev) -> - S = "/* ``The contents of this file are subject to the Erlang Public License,~n" - " * Version 1.1, (the \"License\"); you may not use this file except in~n" - " * compliance with the License. You should have received a copy of the~n" - " * Erlang Public License along with this software. If not, it can be~n" - " * retrieved via the world wide web at http://www.erlang.org/.~n" - " * ~n" - " * Software distributed under the License is distributed on an \"AS IS\"~n" - " * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See~n" - " * the License for the specific language governing rights and limitations~n" - " * under the License.~n" + S = "/* ``Licensed under the Apache License, Version 2.0 (the \"License\");~n" + " * you may not use this file except in compliance with the License.~n" + " * You may obtain a copy of the License at~n" + " * ~n" + " * http://www.apache.org/licenses/LICENSE-2.0~n" + " * ~n" + " * Unless required by applicable law or agreed to in writing, software~n" + " * distributed under the License is distributed on an \"AS IS\" BASIS,~n" + " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.~n" + " * See the License for the specific language governing permissions and~n" + " * limitations under the License.~n" " * ~n" " * The Initial Developer of the Original Code is Ericsson AB.~n" " * Portions created by Ericsson are Copyright 2007, Ericsson AB.~n" diff --git a/erts/emulator/test/send_term_SUITE_data/ext_terms.h b/erts/emulator/test/send_term_SUITE_data/ext_terms.h index 5585585ec3..83277078c6 100644 --- a/erts/emulator/test/send_term_SUITE_data/ext_terms.h +++ b/erts/emulator/test/send_term_SUITE_data/ext_terms.h @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson AB. * Portions created by Ericsson are Copyright 2007, Ericsson AB. diff --git a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c index 381a4f20d5..abb13d4603 100644 --- a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c +++ b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c @@ -1,13 +1,14 @@ -/* ``The 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. +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/erts/emulator/test/sensitive_SUITE.erl b/erts/emulator/test/sensitive_SUITE.erl index e073eab596..2e51712737 100644 --- a/erts/emulator/test/sensitive_SUITE.erl +++ b/erts/emulator/test/sensitive_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2007-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index dcb10c947e..4aa690fb0f 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 4c50b8ba8c..5bb98e5ad9 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index 16cee81158..56ecf4195a 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index e3ac2d5d83..bee42c07d9 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl index a387c08ef9..e4b6511d1f 100644 --- a/erts/emulator/test/system_profile_SUITE.erl +++ b/erts/emulator/test/system_profile_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index d04a95b10e..33076c7461 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index f41fc7552e..51d59f09f3 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1998-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 4d7598cf1f..6eae182e45 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index 0f68e7b27c..a12c41a3aa 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1998-2014. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/trace_call_count_SUITE.erl b/erts/emulator/test/trace_call_count_SUITE.erl index 2ac58493ff..c7881bbd70 100644 --- a/erts/emulator/test/trace_call_count_SUITE.erl +++ b/erts/emulator/test/trace_call_count_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 9c444ed682..f359e1bd80 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 1bed49aad2..7431099340 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2000-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl b/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl index be9bea209a..a5947de4aa 100644 --- a/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl +++ b/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl index 25a59e0b07..3b105ec6fe 100644 --- a/erts/emulator/test/trace_meta_SUITE.erl +++ b/erts/emulator/test/trace_meta_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/trace_nif_SUITE.erl b/erts/emulator/test/trace_nif_SUITE.erl index a7484a22fd..1cd50350e3 100644 --- a/erts/emulator/test/trace_nif_SUITE.erl +++ b/erts/emulator/test/trace_nif_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2010-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index 99df8da107..d6346f3af0 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/tuple_SUITE.erl b/erts/emulator/test/tuple_SUITE.erl index f627eea07f..f1f077be6b 100644 --- a/erts/emulator/test/tuple_SUITE.erl +++ b/erts/emulator/test/tuple_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl index 5ad6e59272..6fa634b886 100644 --- a/erts/emulator/test/unique_SUITE.erl +++ b/erts/emulator/test/unique_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2014. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index b0c6224dfe..f4d9030255 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 0b7c16f606..9a8c3585e6 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -4,16 +4,17 @@ # # 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 -# 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/utils/beam_strip b/erts/emulator/utils/beam_strip index 1ce0fea180..0e7bc46b63 100755 --- a/erts/emulator/utils/beam_strip +++ b/erts/emulator/utils/beam_strip @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 2001-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/utils/count b/erts/emulator/utils/count index 617f5c25e8..c4dd42c949 100755 --- a/erts/emulator/utils/count +++ b/erts/emulator/utils/count @@ -4,16 +4,17 @@ %% %% Copyright Ericsson AB 1998-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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/utils/loaded b/erts/emulator/utils/loaded index d124a64a78..ab77ffdb6c 100644 --- a/erts/emulator/utils/loaded +++ b/erts/emulator/utils/loaded @@ -4,16 +4,17 @@ %% %% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/emulator/utils/make_alloc_types b/erts/emulator/utils/make_alloc_types index 53051b7692..88f537ea09 100755 --- a/erts/emulator/utils/make_alloc_types +++ b/erts/emulator/utils/make_alloc_types @@ -5,16 +5,17 @@ # # Copyright Ericsson AB 2003-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% @@ -190,16 +191,17 @@ print DST "/* * * Copyright Ericsson AB ", (1900 + (localtime)[5]), ". All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the \"License\"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an \"AS IS\" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ diff --git a/erts/emulator/utils/make_compiler_flags b/erts/emulator/utils/make_compiler_flags index ca1bc47113..d75ea0817e 100755 --- a/erts/emulator/utils/make_compiler_flags +++ b/erts/emulator/utils/make_compiler_flags @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 1999-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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/utils/make_driver_tab b/erts/emulator/utils/make_driver_tab index 5c68143d58..3203c7110a 100755 --- a/erts/emulator/utils/make_driver_tab +++ b/erts/emulator/utils/make_driver_tab @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 1999-2013. All Rights Reserved. # -# The 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/utils/make_preload b/erts/emulator/utils/make_preload index fade9829ca..62c4419589 100755 --- a/erts/emulator/utils/make_preload +++ b/erts/emulator/utils/make_preload @@ -4,16 +4,17 @@ # # 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 -# 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/utils/make_tables b/erts/emulator/utils/make_tables index 597a201e5a..233e95f176 100755 --- a/erts/emulator/utils/make_tables +++ b/erts/emulator/utils/make_tables @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 1999-2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/utils/make_version b/erts/emulator/utils/make_version index 0ba1c77930..3461dc1637 100755 --- a/erts/emulator/utils/make_version +++ b/erts/emulator/utils/make_version @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 1999-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/emulator/utils/mkver.c b/erts/emulator/utils/mkver.c index 844014e8f5..96cd315a95 100644 --- a/erts/emulator/utils/mkver.c +++ b/erts/emulator/utils/mkver.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/emulator/zlib/zlib.mk b/erts/emulator/zlib/zlib.mk index ff5ffa5328..53d7badd64 100644 --- a/erts/emulator/zlib/zlib.mk +++ b/erts/emulator/zlib/zlib.mk @@ -6,16 +6,17 @@ # # 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 -# 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/epmd/Makefile b/erts/epmd/Makefile index 4c1af393ac..25a33462ee 100644 --- a/erts/epmd/Makefile +++ b/erts/epmd/Makefile @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/epmd/epmd.mk b/erts/epmd/epmd.mk index a73f4bc077..08245b784e 100644 --- a/erts/epmd/epmd.mk +++ b/erts/epmd/epmd.mk @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/epmd/src/Makefile b/erts/epmd/src/Makefile index 7d586c7438..3e09a40566 100644 --- a/erts/epmd/src/Makefile +++ b/erts/epmd/src/Makefile @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in index 0c7787a3b1..b6e3ba7762 100644 --- a/erts/epmd/src/Makefile.in +++ b/erts/epmd/src/Makefile.in @@ -3,16 +3,17 @@ # # 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 -# 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 2fd9845d1a..132bda725c 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -4,16 +4,17 @@ * * Copyright Ericsson AB 1998-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/epmd/src/epmd.h b/erts/epmd/src/epmd.h index 5d6e9ac165..10483bb5a2 100644 --- a/erts/epmd/src/epmd.h +++ b/erts/epmd/src/epmd.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1998-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c index bd30bc35d9..a8fe865d9a 100644 --- a/erts/epmd/src/epmd_cli.c +++ b/erts/epmd/src/epmd_cli.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1998-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index 52badd7086..26100afc93 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -4,16 +4,17 @@ * * Copyright Ericsson AB 1998-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 26e42adb19..8c8d7304f2 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -4,16 +4,17 @@ * * Copyright Ericsson AB 1998-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/epmd/test/Makefile b/erts/epmd/test/Makefile index 45c8be6809..7c5302a2f1 100644 --- a/erts/epmd/test/Makefile +++ b/erts/epmd/test/Makefile @@ -3,16 +3,17 @@ # # 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 -# 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index a752abf33b..e8bbfdbb18 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1998-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/etc/Makefile b/erts/etc/Makefile index 5b54ef9c3e..9a14cee89c 100644 --- a/erts/etc/Makefile +++ b/erts/etc/Makefile @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 1999-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/common/Makefile b/erts/etc/common/Makefile index 73ab79d145..bbf51d0efd 100644 --- a/erts/etc/common/Makefile +++ b/erts/etc/common/Makefile @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index 0cf965f915..8e55fa78c9 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 1996-2014. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/common/ct_run.c b/erts/etc/common/ct_run.c index 9e67b94f30..548514ee6c 100644 --- a/erts/etc/common/ct_run.c +++ b/erts/etc/common/ct_run.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c index 09afb25182..c45626606c 100644 --- a/erts/etc/common/dialyzer.c +++ b/erts/etc/common/dialyzer.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c index 055064abc4..f9d909e01c 100644 --- a/erts/etc/common/erlc.c +++ b/erts/etc/common/erlc.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1997-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 50f4f0e8a6..cde0b25a2a 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c index c92fedee4b..7fd02ed436 100644 --- a/erts/etc/common/escript.c +++ b/erts/etc/common/escript.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2007-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index 0d1dcacf2c..01ef840b5d 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c index 9ec4192667..e298c5e7f7 100644 --- a/erts/etc/common/inet_gethost.c +++ b/erts/etc/common/inet_gethost.c @@ -3,16 +3,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/run_erl_common.c b/erts/etc/common/run_erl_common.c index ab420e3bee..c03d5e3ae2 100644 --- a/erts/etc/common/run_erl_common.c +++ b/erts/etc/common/run_erl_common.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/run_erl_common.h b/erts/etc/common/run_erl_common.h index 14207ee4de..cecf7521f9 100644 --- a/erts/etc/common/run_erl_common.h +++ b/erts/etc/common/run_erl_common.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/run_erl_vsn.h b/erts/etc/common/run_erl_vsn.h index f6ac753bde..2c3e67e81c 100644 --- a/erts/etc/common/run_erl_vsn.h +++ b/erts/etc/common/run_erl_vsn.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/safe_string.c b/erts/etc/common/safe_string.c index b2f8814408..cdcdbf16f0 100644 --- a/erts/etc/common/safe_string.c +++ b/erts/etc/common/safe_string.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/safe_string.h b/erts/etc/common/safe_string.h index ff063fe641..f9d2b2023a 100644 --- a/erts/etc/common/safe_string.h +++ b/erts/etc/common/safe_string.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/to_erl_common.c b/erts/etc/common/to_erl_common.c index ab706fffe0..8aa94ccfa4 100644 --- a/erts/etc/common/to_erl_common.c +++ b/erts/etc/common/to_erl_common.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/to_erl_common.h b/erts/etc/common/to_erl_common.h index 9967db94b8..a139da418f 100644 --- a/erts/etc/common/to_erl_common.h +++ b/erts/etc/common/to_erl_common.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/common/typer.c b/erts/etc/common/typer.c index b45867f845..0aa0996808 100644 --- a/erts/etc/common/typer.c +++ b/erts/etc/common/typer.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/ose/run_erl.c b/erts/etc/ose/run_erl.c index a6499f2bf3..6775525297 100644 --- a/erts/etc/ose/run_erl.c +++ b/erts/etc/ose/run_erl.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/ose/run_erl.h b/erts/etc/ose/run_erl.h index 128f551670..bdc8b6c355 100644 --- a/erts/etc/ose/run_erl.h +++ b/erts/etc/ose/run_erl.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/ose/run_erl_main.c b/erts/etc/ose/run_erl_main.c index 2d92924ff2..8895c773a1 100644 --- a/erts/etc/ose/run_erl_main.c +++ b/erts/etc/ose/run_erl_main.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src index 8eb1db75bd..6634ae31d3 100644 --- a/erts/etc/unix/Install.src +++ b/erts/etc/unix/Install.src @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 1996-2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/unix/Makefile b/erts/etc/unix/Makefile index c137a31ec2..04ae11de3b 100644 --- a/erts/etc/unix/Makefile +++ b/erts/etc/unix/Makefile @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/unix/README b/erts/etc/unix/README index 45b4aec2da..6bda610a03 100644 --- a/erts/etc/unix/README +++ b/erts/etc/unix/README @@ -3,16 +3,17 @@ 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - 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. + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. %CopyrightEnd% diff --git a/erts/etc/unix/RELNOTES b/erts/etc/unix/RELNOTES index d1a110fce3..629867d2ae 100644 --- a/erts/etc/unix/RELNOTES +++ b/erts/etc/unix/RELNOTES @@ -3,16 +3,17 @@ 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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - 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. + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. %CopyrightEnd% diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 59cf29d381..2a806bb2f1 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 2003-2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/unix/dyn_erl.c b/erts/etc/unix/dyn_erl.c index 984935417e..4eebfae50a 100644 --- a/erts/etc/unix/dyn_erl.c +++ b/erts/etc/unix/dyn_erl.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/unix/erl.src.src b/erts/etc/unix/erl.src.src index ce5d2b5def..94c6f9f854 100644 --- a/erts/etc/unix/erl.src.src +++ b/erts/etc/unix/erl.src.src @@ -4,16 +4,17 @@ # # 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 -# 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index c51b9e94ed..19d67de92f 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2005-2014. All Rights Reserved. # -# The 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/unix/etp-thr.py b/erts/etc/unix/etp-thr.py index 64fb858d20..16bc7f4016 100644 --- a/erts/etc/unix/etp-thr.py +++ b/erts/etc/unix/etp-thr.py @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/unix/etp_commands.erl b/erts/etc/unix/etp_commands.erl index 66cb76edbc..fe16a71876 100644 --- a/erts/etc/unix/etp_commands.erl +++ b/erts/etc/unix/etp_commands.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2005-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/etc/unix/etp_commands.mk b/erts/etc/unix/etp_commands.mk index 1d9a269b68..def6f7bda0 100644 --- a/erts/etc/unix/etp_commands.mk +++ b/erts/etc/unix/etp_commands.mk @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2005-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/unix/format_man_pages b/erts/etc/unix/format_man_pages index 93dcdcd8fa..7abe65cecb 100644 --- a/erts/etc/unix/format_man_pages +++ b/erts/etc/unix/format_man_pages @@ -5,16 +5,17 @@ # # 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 -# 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index 049e83f9e4..c8414030ca 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/unix/setuid_socket_wrap.c b/erts/etc/unix/setuid_socket_wrap.c index 3f0657770c..59ed8eae6f 100644 --- a/erts/etc/unix/setuid_socket_wrap.c +++ b/erts/etc/unix/setuid_socket_wrap.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/unix/start.src b/erts/etc/unix/start.src index 8479be0987..377f5e85c8 100644 --- a/erts/etc/unix/start.src +++ b/erts/etc/unix/start.src @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/unix/start_erl.src b/erts/etc/unix/start_erl.src index ea8022c449..b889101783 100644 --- a/erts/etc/unix/start_erl.src +++ b/erts/etc/unix/start_erl.src @@ -5,16 +5,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c index 38a94ed9c3..82d3218964 100644 --- a/erts/etc/unix/to_erl.c +++ b/erts/etc/unix/to_erl.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1996-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c index 9d85d642ab..82bae947d4 100644 --- a/erts/etc/win32/Install.c +++ b/erts/etc/win32/Install.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/Makefile b/erts/etc/win32/Makefile index cc9021da70..12c04fc9a5 100644 --- a/erts/etc/win32/Makefile +++ b/erts/etc/win32/Makefile @@ -3,16 +3,17 @@ # # 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 -# 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/Nmakefile.start_erl b/erts/etc/win32/Nmakefile.start_erl index 5bf9fd78d5..cf83713bab 100644 --- a/erts/etc/win32/Nmakefile.start_erl +++ b/erts/etc/win32/Nmakefile.start_erl @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/beam.rc b/erts/etc/win32/beam.rc index cd7db67d4d..9e137ecd62 100644 --- a/erts/etc/win32/beam.rc +++ b/erts/etc/win32/beam.rc @@ -3,16 +3,17 @@ // // 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. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // // %CopyrightEnd% // diff --git a/erts/etc/win32/cygwin_tools/erl b/erts/etc/win32/cygwin_tools/erl index 576825c4be..51a7be5584 100755 --- a/erts/etc/win32/cygwin_tools/erl +++ b/erts/etc/win32/cygwin_tools/erl @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/erlc b/erts/etc/win32/cygwin_tools/erlc index a18ec27bf4..588b53b1be 100755 --- a/erts/etc/win32/cygwin_tools/erlc +++ b/erts/etc/win32/cygwin_tools/erlc @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/javac.sh b/erts/etc/win32/cygwin_tools/javac.sh index f9ee24593f..f2f97ef152 100755 --- a/erts/etc/win32/cygwin_tools/javac.sh +++ b/erts/etc/win32/cygwin_tools/javac.sh @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh b/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh index 20fe143890..0f87f3d077 100755 --- a/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh +++ b/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 2003-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/make_local_ini.sh b/erts/etc/win32/cygwin_tools/make_local_ini.sh index 8e29573dc4..0bcb362ffe 100755 --- a/erts/etc/win32/cygwin_tools/make_local_ini.sh +++ b/erts/etc/win32/cygwin_tools/make_local_ini.sh @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 2003-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/mingw/ar.sh b/erts/etc/win32/cygwin_tools/mingw/ar.sh index 5b8f58e5de..2ebfe4e435 100755 --- a/erts/etc/win32/cygwin_tools/mingw/ar.sh +++ b/erts/etc/win32/cygwin_tools/mingw/ar.sh @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/mingw/cc.sh b/erts/etc/win32/cygwin_tools/mingw/cc.sh index ae284893fa..5993f70686 100755 --- a/erts/etc/win32/cygwin_tools/mingw/cc.sh +++ b/erts/etc/win32/cygwin_tools/mingw/cc.sh @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/mingw/coffix.c b/erts/etc/win32/cygwin_tools/mingw/coffix.c index 5dff030a69..383c422008 100644 --- a/erts/etc/win32/cygwin_tools/mingw/coffix.c +++ b/erts/etc/win32/cygwin_tools/mingw/coffix.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh b/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh index f3865c8cae..8da91dd0ef 100755 --- a/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh +++ b/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/mingw/ld.sh b/erts/etc/win32/cygwin_tools/mingw/ld.sh index 145bd2fad9..16caf0b6d2 100755 --- a/erts/etc/win32/cygwin_tools/mingw/ld.sh +++ b/erts/etc/win32/cygwin_tools/mingw/ld.sh @@ -5,16 +5,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/mingw/mc.sh b/erts/etc/win32/cygwin_tools/mingw/mc.sh index 873149172a..4462bfc5d3 100755 --- a/erts/etc/win32/cygwin_tools/mingw/mc.sh +++ b/erts/etc/win32/cygwin_tools/mingw/mc.sh @@ -5,16 +5,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/mingw/rc.sh b/erts/etc/win32/cygwin_tools/mingw/rc.sh index 37296f9e9f..b8a2d2fbcf 100755 --- a/erts/etc/win32/cygwin_tools/mingw/rc.sh +++ b/erts/etc/win32/cygwin_tools/mingw/rc.sh @@ -5,16 +5,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/vc/ar.sh b/erts/etc/win32/cygwin_tools/vc/ar.sh index 24d275b01a..e0bd1bd5ca 100755 --- a/erts/etc/win32/cygwin_tools/vc/ar.sh +++ b/erts/etc/win32/cygwin_tools/vc/ar.sh @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/vc/cc.sh b/erts/etc/win32/cygwin_tools/vc/cc.sh index 4939465d08..48a579d5f0 100755 --- a/erts/etc/win32/cygwin_tools/vc/cc.sh +++ b/erts/etc/win32/cygwin_tools/vc/cc.sh @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/vc/cc_wrap.c b/erts/etc/win32/cygwin_tools/vc/cc_wrap.c index 18ecc31c17..b42e0e1037 100644 --- a/erts/etc/win32/cygwin_tools/vc/cc_wrap.c +++ b/erts/etc/win32/cygwin_tools/vc/cc_wrap.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/cygwin_tools/vc/coffix.c b/erts/etc/win32/cygwin_tools/vc/coffix.c index dee0132a61..0633c6ddea 100644 --- a/erts/etc/win32/cygwin_tools/vc/coffix.c +++ b/erts/etc/win32/cygwin_tools/vc/coffix.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1999-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/cygwin_tools/vc/emu_cc.sh b/erts/etc/win32/cygwin_tools/vc/emu_cc.sh index 6c179aed00..fb6ee2d7a2 100755 --- a/erts/etc/win32/cygwin_tools/vc/emu_cc.sh +++ b/erts/etc/win32/cygwin_tools/vc/emu_cc.sh @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/vc/ld.sh b/erts/etc/win32/cygwin_tools/vc/ld.sh index 406c63ffee..ff538122b2 100755 --- a/erts/etc/win32/cygwin_tools/vc/ld.sh +++ b/erts/etc/win32/cygwin_tools/vc/ld.sh @@ -5,16 +5,17 @@ # # Copyright Ericsson AB 2002-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/vc/ld_wrap.c b/erts/etc/win32/cygwin_tools/vc/ld_wrap.c index 7fb3c145ee..000c13befd 100644 --- a/erts/etc/win32/cygwin_tools/vc/ld_wrap.c +++ b/erts/etc/win32/cygwin_tools/vc/ld_wrap.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/cygwin_tools/vc/mc.sh b/erts/etc/win32/cygwin_tools/vc/mc.sh index 676b072655..2de5cbba9b 100755 --- a/erts/etc/win32/cygwin_tools/vc/mc.sh +++ b/erts/etc/win32/cygwin_tools/vc/mc.sh @@ -5,16 +5,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/cygwin_tools/vc/rc.sh b/erts/etc/win32/cygwin_tools/vc/rc.sh index 054c672e64..414ffa0448 100755 --- a/erts/etc/win32/cygwin_tools/vc/rc.sh +++ b/erts/etc/win32/cygwin_tools/vc/rc.sh @@ -5,16 +5,17 @@ # # Copyright Ericsson AB 2002-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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/erl.c b/erts/etc/win32/erl.c index 772b668586..59693955a5 100644 --- a/erts/etc/win32/erl.c +++ b/erts/etc/win32/erl.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/erl.rc b/erts/etc/win32/erl.rc index 88213d48f2..e8848e7969 100644 --- a/erts/etc/win32/erl.rc +++ b/erts/etc/win32/erl.rc @@ -3,16 +3,17 @@ // // 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. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // // %CopyrightEnd% // diff --git a/erts/etc/win32/erl_log.c b/erts/etc/win32/erl_log.c index 85cc49e0e3..2a873dffac 100644 --- a/erts/etc/win32/erl_log.c +++ b/erts/etc/win32/erl_log.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/erlsrv/erlsrv_global.h b/erts/etc/win32/erlsrv/erlsrv_global.h index f25e09ea45..f535599cf1 100644 --- a/erts/etc/win32/erlsrv/erlsrv_global.h +++ b/erts/etc/win32/erlsrv/erlsrv_global.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.c b/erts/etc/win32/erlsrv/erlsrv_interactive.c index 260f0d9b97..d2236ac9f7 100644 --- a/erts/etc/win32/erlsrv/erlsrv_interactive.c +++ b/erts/etc/win32/erlsrv/erlsrv_interactive.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1998-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.h b/erts/etc/win32/erlsrv/erlsrv_interactive.h index bc6e55fdef..a83f5a4b85 100644 --- a/erts/etc/win32/erlsrv/erlsrv_interactive.h +++ b/erts/etc/win32/erlsrv/erlsrv_interactive.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/erlsrv/erlsrv_main.c b/erts/etc/win32/erlsrv/erlsrv_main.c index 6d8e208fc8..caca18de00 100644 --- a/erts/etc/win32/erlsrv/erlsrv_main.c +++ b/erts/etc/win32/erlsrv/erlsrv_main.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.c b/erts/etc/win32/erlsrv/erlsrv_registry.c index ad50da89a4..f95f4ef074 100644 --- a/erts/etc/win32/erlsrv/erlsrv_registry.c +++ b/erts/etc/win32/erlsrv/erlsrv_registry.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.h b/erts/etc/win32/erlsrv/erlsrv_registry.h index 4be10e9ff2..3aa265686a 100644 --- a/erts/etc/win32/erlsrv/erlsrv_registry.h +++ b/erts/etc/win32/erlsrv/erlsrv_registry.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/erlsrv/erlsrv_service.c b/erts/etc/win32/erlsrv/erlsrv_service.c index 2e56c579a2..d9fa165355 100644 --- a/erts/etc/win32/erlsrv/erlsrv_service.c +++ b/erts/etc/win32/erlsrv/erlsrv_service.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 1998-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/erlsrv/erlsrv_service.h b/erts/etc/win32/erlsrv/erlsrv_service.h index c46689d83e..c87292325c 100644 --- a/erts/etc/win32/erlsrv/erlsrv_service.h +++ b/erts/etc/win32/erlsrv/erlsrv_service.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/erlsrv/erlsrv_util.c b/erts/etc/win32/erlsrv/erlsrv_util.c index 4b1ba071e8..800395ff12 100644 --- a/erts/etc/win32/erlsrv/erlsrv_util.c +++ b/erts/etc/win32/erlsrv/erlsrv_util.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/erlsrv/erlsrv_util.h b/erts/etc/win32/erlsrv/erlsrv_util.h index 6881906a52..1afcd1dd7e 100644 --- a/erts/etc/win32/erlsrv/erlsrv_util.h +++ b/erts/etc/win32/erlsrv/erlsrv_util.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/init_file.c b/erts/etc/win32/init_file.c index d452afa65c..93d82b1823 100644 --- a/erts/etc/win32/init_file.c +++ b/erts/etc/win32/init_file.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/init_file.h b/erts/etc/win32/init_file.h index ae40e88520..404b5fd03b 100644 --- a/erts/etc/win32/init_file.h +++ b/erts/etc/win32/init_file.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2003-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/msys_tools/erl b/erts/etc/win32/msys_tools/erl index 525253fd84..110d48c769 100644 --- a/erts/etc/win32/msys_tools/erl +++ b/erts/etc/win32/msys_tools/erl @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/msys_tools/erlc b/erts/etc/win32/msys_tools/erlc index 3f53ef7f4f..b50090b6de 100644 --- a/erts/etc/win32/msys_tools/erlc +++ b/erts/etc/win32/msys_tools/erlc @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/msys_tools/javac.sh b/erts/etc/win32/msys_tools/javac.sh index 2d884bc2c8..5b51648a19 100644 --- a/erts/etc/win32/msys_tools/javac.sh +++ b/erts/etc/win32/msys_tools/javac.sh @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/msys_tools/make_bootstrap_ini.sh b/erts/etc/win32/msys_tools/make_bootstrap_ini.sh index b61965f546..1797f67c78 100644 --- a/erts/etc/win32/msys_tools/make_bootstrap_ini.sh +++ b/erts/etc/win32/msys_tools/make_bootstrap_ini.sh @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 2003-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/msys_tools/make_local_ini.sh b/erts/etc/win32/msys_tools/make_local_ini.sh index 6c5d84c4f5..11f722e7f8 100644 --- a/erts/etc/win32/msys_tools/make_local_ini.sh +++ b/erts/etc/win32/msys_tools/make_local_ini.sh @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 2003-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/msys_tools/vc/ar.sh b/erts/etc/win32/msys_tools/vc/ar.sh index f4c61e1d92..4c98e3cc29 100644 --- a/erts/etc/win32/msys_tools/vc/ar.sh +++ b/erts/etc/win32/msys_tools/vc/ar.sh @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/msys_tools/vc/cc.sh b/erts/etc/win32/msys_tools/vc/cc.sh index 38b3d2ee81..ad05e5375b 100644 --- a/erts/etc/win32/msys_tools/vc/cc.sh +++ b/erts/etc/win32/msys_tools/vc/cc.sh @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/msys_tools/vc/coffix.c b/erts/etc/win32/msys_tools/vc/coffix.c index 1773b222fe..4f21cfc389 100644 --- a/erts/etc/win32/msys_tools/vc/coffix.c +++ b/erts/etc/win32/msys_tools/vc/coffix.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/msys_tools/vc/emu_cc.sh b/erts/etc/win32/msys_tools/vc/emu_cc.sh index 68ce4e359f..01f75b2468 100644 --- a/erts/etc/win32/msys_tools/vc/emu_cc.sh +++ b/erts/etc/win32/msys_tools/vc/emu_cc.sh @@ -4,16 +4,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/msys_tools/vc/ld.sh b/erts/etc/win32/msys_tools/vc/ld.sh index 0fcbf6f7d9..11b2fc077b 100644 --- a/erts/etc/win32/msys_tools/vc/ld.sh +++ b/erts/etc/win32/msys_tools/vc/ld.sh @@ -5,16 +5,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/msys_tools/vc/mc.sh b/erts/etc/win32/msys_tools/vc/mc.sh index 27d985f73e..e9ea9ff9a9 100644 --- a/erts/etc/win32/msys_tools/vc/mc.sh +++ b/erts/etc/win32/msys_tools/vc/mc.sh @@ -5,16 +5,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/msys_tools/vc/rc.sh b/erts/etc/win32/msys_tools/vc/rc.sh index dfa9e324db..1b3b1c85bd 100644 --- a/erts/etc/win32/msys_tools/vc/rc.sh +++ b/erts/etc/win32/msys_tools/vc/rc.sh @@ -5,16 +5,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/nsis/Makefile b/erts/etc/win32/nsis/Makefile index 7bcecaa264..49d835170a 100644 --- a/erts/etc/win32/nsis/Makefile +++ b/erts/etc/win32/nsis/Makefile @@ -3,16 +3,17 @@ # # 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 -# 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/nsis/dll_version_helper.sh b/erts/etc/win32/nsis/dll_version_helper.sh index e75edf3738..86e36f62c9 100755 --- a/erts/etc/win32/nsis/dll_version_helper.sh +++ b/erts/etc/win32/nsis/dll_version_helper.sh @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 2007-2013. All Rights Reserved. # -# The 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/nsis/erlang20.nsi b/erts/etc/win32/nsis/erlang20.nsi index 3333c4a9aa..bf6ba0b9a6 100644 --- a/erts/etc/win32/nsis/erlang20.nsi +++ b/erts/etc/win32/nsis/erlang20.nsi @@ -9,16 +9,17 @@ ; ; 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/. +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at ; -; 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. +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. ; ; %CopyrightEnd% ; diff --git a/erts/etc/win32/nsis/find_redist.sh b/erts/etc/win32/nsis/find_redist.sh index 7c449c9e4e..c0895c9dd5 100755 --- a/erts/etc/win32/nsis/find_redist.sh +++ b/erts/etc/win32/nsis/find_redist.sh @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 2007-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/etc/win32/port_entry.c b/erts/etc/win32/port_entry.c index 30b54035e0..5681a2a548 100644 --- a/erts/etc/win32/port_entry.c +++ b/erts/etc/win32/port_entry.c @@ -3,16 +3,17 @@ * * 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 - * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/resource.h b/erts/etc/win32/resource.h index 697931952a..32d8b8885d 100644 --- a/erts/etc/win32/resource.h +++ b/erts/etc/win32/resource.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/start_erl.c b/erts/etc/win32/start_erl.c index 0ca12f09c9..a4437c2f6b 100644 --- a/erts/etc/win32/start_erl.c +++ b/erts/etc/win32/start_erl.c @@ -3,16 +3,17 @@ * * 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 - * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/etc/win32/win_erlexec.c b/erts/etc/win32/win_erlexec.c index c622e6eeee..f2460197e6 100644 --- a/erts/etc/win32/win_erlexec.c +++ b/erts/etc/win32/win_erlexec.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/example/Makefile b/erts/example/Makefile index b637bee033..cc5bcce191 100644 --- a/erts/example/Makefile +++ b/erts/example/Makefile @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/example/matrix_nif.c b/erts/example/matrix_nif.c index 404329e36c..dfe446e879 100644 --- a/erts/example/matrix_nif.c +++ b/erts/example/matrix_nif.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/example/matrix_nif.erl b/erts/example/matrix_nif.erl index 9008977e01..d56b358247 100644 --- a/erts/example/matrix_nif.erl +++ b/erts/example/matrix_nif.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/example/next_perm.cc b/erts/example/next_perm.cc index d7c8f1ad97..c7b7096e7b 100644 --- a/erts/example/next_perm.cc +++ b/erts/example/next_perm.cc @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/example/next_perm.erl b/erts/example/next_perm.erl index 40a5c24d35..d414470f3a 100644 --- a/erts/example/next_perm.erl +++ b/erts/example/next_perm.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/example/pg_async.c b/erts/example/pg_async.c index 7ffb4bb1f3..cd6bc9e0c2 100644 --- a/erts/example/pg_async.c +++ b/erts/example/pg_async.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/example/pg_async.erl b/erts/example/pg_async.erl index 10506bfe9f..20ee94f61a 100644 --- a/erts/example/pg_async.erl +++ b/erts/example/pg_async.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/example/pg_async2.c b/erts/example/pg_async2.c index 368f9d32d0..9eb3ec9d54 100644 --- a/erts/example/pg_async2.c +++ b/erts/example/pg_async2.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/example/pg_async2.erl b/erts/example/pg_async2.erl index 4803abf508..082852f617 100644 --- a/erts/example/pg_async2.erl +++ b/erts/example/pg_async2.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/example/pg_encode.c b/erts/example/pg_encode.c index 34ca1fe46c..e1ec4abb1d 100644 --- a/erts/example/pg_encode.c +++ b/erts/example/pg_encode.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/example/pg_encode.h b/erts/example/pg_encode.h index 4477c0c079..df3f8fcaaa 100644 --- a/erts/example/pg_encode.h +++ b/erts/example/pg_encode.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/example/pg_encode2.c b/erts/example/pg_encode2.c index a0e99ba3b3..cdf8e71e44 100644 --- a/erts/example/pg_encode2.c +++ b/erts/example/pg_encode2.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/example/pg_encode2.h b/erts/example/pg_encode2.h index 4477c0c079..df3f8fcaaa 100644 --- a/erts/example/pg_encode2.h +++ b/erts/example/pg_encode2.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/example/pg_sync.c b/erts/example/pg_sync.c index 6eaa6138e6..88096671a5 100644 --- a/erts/example/pg_sync.c +++ b/erts/example/pg_sync.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/example/pg_sync.erl b/erts/example/pg_sync.erl index 58cc149e12..76fb27332e 100644 --- a/erts/example/pg_sync.erl +++ b/erts/example/pg_sync.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/example/time_compat.erl b/erts/example/time_compat.erl index 90b7fbcc80..54f6ba0790 100644 --- a/erts/example/time_compat.erl +++ b/erts/example/time_compat.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2014. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/include/erl_fixed_size_int_types.h b/erts/include/erl_fixed_size_int_types.h index 3bbc37aea7..dfaea5650b 100644 --- a/erts/include/erl_fixed_size_int_types.h +++ b/erts/include/erl_fixed_size_int_types.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/erl_int_sizes_config.h.in b/erts/include/erl_int_sizes_config.h.in index 14d7c6a702..b18f5ebc00 100644 --- a/erts/include/erl_int_sizes_config.h.in +++ b/erts/include/erl_int_sizes_config.h.in @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/erl_memory_trace_parser.h b/erts/include/erl_memory_trace_parser.h index 3b6f76d2fd..426ff05061 100644 --- a/erts/include/erl_memory_trace_parser.h +++ b/erts/include/erl_memory_trace_parser.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/erl_native_features_config.h.in b/erts/include/erl_native_features_config.h.in index d1674cb256..2bd1d96229 100644 --- a/erts/include/erl_native_features_config.h.in +++ b/erts/include/erl_native_features_config.h.in @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/README b/erts/include/internal/README index f7b78a3468..fca0c5e489 100644 --- a/erts/include/internal/README +++ b/erts/include/internal/README @@ -3,16 +3,17 @@ Copyright Ericsson AB 2004-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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - 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. + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. %CopyrightEnd% diff --git a/erts/include/internal/erl_errno.h b/erts/include/internal/erl_errno.h index 2e095e9f64..33bfbe3d51 100644 --- a/erts/include/internal/erl_errno.h +++ b/erts/include/internal/erl_errno.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/erl_memory_trace_protocol.h b/erts/include/internal/erl_memory_trace_protocol.h index bda1f65c87..b86e2de278 100644 --- a/erts/include/internal/erl_memory_trace_protocol.h +++ b/erts/include/internal/erl_memory_trace_protocol.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/erl_misc_utils.h b/erts/include/internal/erl_misc_utils.h index 507e1726f4..7ab7a26838 100644 --- a/erts/include/internal/erl_misc_utils.h +++ b/erts/include/internal/erl_misc_utils.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/erl_printf.h b/erts/include/internal/erl_printf.h index 5bc93a979b..3846828fb9 100644 --- a/erts/include/internal/erl_printf.h +++ b/erts/include/internal/erl_printf.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/erl_printf_format.h b/erts/include/internal/erl_printf_format.h index 62fe4120e4..efd926be99 100644 --- a/erts/include/internal/erl_printf_format.h +++ b/erts/include/internal/erl_printf_format.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/erts_internal.mk.in b/erts/include/internal/erts_internal.mk.in index 489531372c..76aab59c38 100644 --- a/erts/include/internal/erts_internal.mk.in +++ b/erts/include/internal/erts_internal.mk.in @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/include/internal/ethr_atomics.h b/erts/include/internal/ethr_atomics.h index 612894b8c1..f366c2d0ad 100644 --- a/erts/include/internal/ethr_atomics.h +++ b/erts/include/internal/ethr_atomics.h @@ -12,16 +12,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ethr_internal.h b/erts/include/internal/ethr_internal.h index 65195145af..d4ded6ff05 100644 --- a/erts/include/internal/ethr_internal.h +++ b/erts/include/internal/ethr_internal.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h index 6c931e0cd4..f76c4262ca 100644 --- a/erts/include/internal/ethr_mutex.h +++ b/erts/include/internal/ethr_mutex.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ethr_optimized_fallbacks.h b/erts/include/internal/ethr_optimized_fallbacks.h index 45399d18e6..6ef4e2bace 100644 --- a/erts/include/internal/ethr_optimized_fallbacks.h +++ b/erts/include/internal/ethr_optimized_fallbacks.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index e598017ada..f9c203e97c 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-2015. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ethread.mk.in b/erts/include/internal/ethread.mk.in index 13071711e1..89924a3215 100644 --- a/erts/include/internal/ethread.mk.in +++ b/erts/include/internal/ethread.mk.in @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2004-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/include/internal/ethread_header_config.h.in b/erts/include/internal/ethread_header_config.h.in index a9727568a2..9cabd0591a 100644 --- a/erts/include/internal/ethread_header_config.h.in +++ b/erts/include/internal/ethread_header_config.h.in @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-2015. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ethread_inline.h b/erts/include/internal/ethread_inline.h index c09a67619a..3ba910d993 100644 --- a/erts/include/internal/ethread_inline.h +++ b/erts/include/internal/ethread_inline.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/gcc/ethr_atomic.h b/erts/include/internal/gcc/ethr_atomic.h index 62eed78f76..231eaa6927 100644 --- a/erts/include/internal/gcc/ethr_atomic.h +++ b/erts/include/internal/gcc/ethr_atomic.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-2015. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/gcc/ethr_dw_atomic.h b/erts/include/internal/gcc/ethr_dw_atomic.h index c2c8f85b7b..675912d86e 100644 --- a/erts/include/internal/gcc/ethr_dw_atomic.h +++ b/erts/include/internal/gcc/ethr_dw_atomic.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2011-2015. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/gcc/ethr_membar.h b/erts/include/internal/gcc/ethr_membar.h index d2d36907f3..07960ce040 100644 --- a/erts/include/internal/gcc/ethr_membar.h +++ b/erts/include/internal/gcc/ethr_membar.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2011-2015. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/gcc/ethread.h b/erts/include/internal/gcc/ethread.h index be3e1da90e..12b41f8704 100644 --- a/erts/include/internal/gcc/ethread.h +++ b/erts/include/internal/gcc/ethread.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-2015. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/i386/atomic.h b/erts/include/internal/i386/atomic.h index fc1b619935..6a6435e58d 100644 --- a/erts/include/internal/i386/atomic.h +++ b/erts/include/internal/i386/atomic.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/i386/ethr_dw_atomic.h b/erts/include/internal/i386/ethr_dw_atomic.h index 9fb89bbe43..e8c4119ef0 100644 --- a/erts/include/internal/i386/ethr_dw_atomic.h +++ b/erts/include/internal/i386/ethr_dw_atomic.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/i386/ethr_membar.h b/erts/include/internal/i386/ethr_membar.h index 92d9de7f3f..97ae5eda2c 100644 --- a/erts/include/internal/i386/ethr_membar.h +++ b/erts/include/internal/i386/ethr_membar.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/i386/ethread.h b/erts/include/internal/i386/ethread.h index 80e4dc7b99..23dcd1dc19 100644 --- a/erts/include/internal/i386/ethread.h +++ b/erts/include/internal/i386/ethread.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/i386/rwlock.h b/erts/include/internal/i386/rwlock.h index 1a8cd7da0c..9859338eab 100644 --- a/erts/include/internal/i386/rwlock.h +++ b/erts/include/internal/i386/rwlock.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/i386/spinlock.h b/erts/include/internal/i386/spinlock.h index a84fba91b1..e010684d14 100644 --- a/erts/include/internal/i386/spinlock.h +++ b/erts/include/internal/i386/spinlock.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/libatomic_ops/ethr_atomic.h b/erts/include/internal/libatomic_ops/ethr_atomic.h index 734cdf0890..828210036c 100644 --- a/erts/include/internal/libatomic_ops/ethr_atomic.h +++ b/erts/include/internal/libatomic_ops/ethr_atomic.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/libatomic_ops/ethr_dw_atomic.h b/erts/include/internal/libatomic_ops/ethr_dw_atomic.h index 4dd9f41e96..ce9b251cbe 100644 --- a/erts/include/internal/libatomic_ops/ethr_dw_atomic.h +++ b/erts/include/internal/libatomic_ops/ethr_dw_atomic.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/libatomic_ops/ethr_membar.h b/erts/include/internal/libatomic_ops/ethr_membar.h index b8530a0094..7d2b807586 100644 --- a/erts/include/internal/libatomic_ops/ethr_membar.h +++ b/erts/include/internal/libatomic_ops/ethr_membar.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/libatomic_ops/ethread.h b/erts/include/internal/libatomic_ops/ethread.h index d65ee19b04..e34f43bb46 100644 --- a/erts/include/internal/libatomic_ops/ethread.h +++ b/erts/include/internal/libatomic_ops/ethread.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ose/ethr_event.h b/erts/include/internal/ose/ethr_event.h index 000a600813..c18f30aa4a 100644 --- a/erts/include/internal/ose/ethr_event.h +++ b/erts/include/internal/ose/ethr_event.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ppc32/atomic.h b/erts/include/internal/ppc32/atomic.h index b558626b09..572b0e5191 100644 --- a/erts/include/internal/ppc32/atomic.h +++ b/erts/include/internal/ppc32/atomic.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ppc32/ethr_membar.h b/erts/include/internal/ppc32/ethr_membar.h index ff5cc86bfb..fe77721cd9 100644 --- a/erts/include/internal/ppc32/ethr_membar.h +++ b/erts/include/internal/ppc32/ethr_membar.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ppc32/ethread.h b/erts/include/internal/ppc32/ethread.h index e41c83c5da..25e85c58bd 100644 --- a/erts/include/internal/ppc32/ethread.h +++ b/erts/include/internal/ppc32/ethread.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ppc32/rwlock.h b/erts/include/internal/ppc32/rwlock.h index 311f000b69..aee232f79d 100644 --- a/erts/include/internal/ppc32/rwlock.h +++ b/erts/include/internal/ppc32/rwlock.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/ppc32/spinlock.h b/erts/include/internal/ppc32/spinlock.h index 4c95ec9efb..829db6a135 100644 --- a/erts/include/internal/ppc32/spinlock.h +++ b/erts/include/internal/ppc32/spinlock.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/pthread/ethr_event.h b/erts/include/internal/pthread/ethr_event.h index f67bac858b..74cfa68e16 100644 --- a/erts/include/internal/pthread/ethr_event.h +++ b/erts/include/internal/pthread/ethr_event.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/sparc32/atomic.h b/erts/include/internal/sparc32/atomic.h index fe1daaa9cf..0b535242ad 100644 --- a/erts/include/internal/sparc32/atomic.h +++ b/erts/include/internal/sparc32/atomic.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/sparc32/ethr_membar.h b/erts/include/internal/sparc32/ethr_membar.h index 6eb0c5a1d6..6133de5eb7 100644 --- a/erts/include/internal/sparc32/ethr_membar.h +++ b/erts/include/internal/sparc32/ethr_membar.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/sparc32/ethread.h b/erts/include/internal/sparc32/ethread.h index 5ad92d3da7..513d9f8773 100644 --- a/erts/include/internal/sparc32/ethread.h +++ b/erts/include/internal/sparc32/ethread.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/sparc32/rwlock.h b/erts/include/internal/sparc32/rwlock.h index 8b6f2e9c57..44de6113b7 100644 --- a/erts/include/internal/sparc32/rwlock.h +++ b/erts/include/internal/sparc32/rwlock.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/sparc32/spinlock.h b/erts/include/internal/sparc32/spinlock.h index d4e36e09cf..695fa112b6 100644 --- a/erts/include/internal/sparc32/spinlock.h +++ b/erts/include/internal/sparc32/spinlock.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/sparc64/ethread.h b/erts/include/internal/sparc64/ethread.h index 65fd58d492..5f518e5596 100644 --- a/erts/include/internal/sparc64/ethread.h +++ b/erts/include/internal/sparc64/ethread.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2007-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/tile/atomic.h b/erts/include/internal/tile/atomic.h index 1f1553c346..1a2881442e 100644 --- a/erts/include/internal/tile/atomic.h +++ b/erts/include/internal/tile/atomic.h @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/tile/ethr_membar.h b/erts/include/internal/tile/ethr_membar.h index 7cb4f3cf9a..ccb420d558 100644 --- a/erts/include/internal/tile/ethr_membar.h +++ b/erts/include/internal/tile/ethr_membar.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/tile/ethread.h b/erts/include/internal/tile/ethread.h index 7f579b50e7..577d275965 100644 --- a/erts/include/internal/tile/ethread.h +++ b/erts/include/internal/tile/ethread.h @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/win/ethr_atomic.h b/erts/include/internal/win/ethr_atomic.h index e11f1abf47..f17526a94d 100644 --- a/erts/include/internal/win/ethr_atomic.h +++ b/erts/include/internal/win/ethr_atomic.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/win/ethr_dw_atomic.h b/erts/include/internal/win/ethr_dw_atomic.h index a3e7ffc3aa..8bed4fb26e 100644 --- a/erts/include/internal/win/ethr_dw_atomic.h +++ b/erts/include/internal/win/ethr_dw_atomic.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/win/ethr_event.h b/erts/include/internal/win/ethr_event.h index 95e681983f..bf110e10f9 100644 --- a/erts/include/internal/win/ethr_event.h +++ b/erts/include/internal/win/ethr_event.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/win/ethr_membar.h b/erts/include/internal/win/ethr_membar.h index a17f2459fc..9cba6b605d 100644 --- a/erts/include/internal/win/ethr_membar.h +++ b/erts/include/internal/win/ethr_membar.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/win/ethread.h b/erts/include/internal/win/ethread.h index 8be35e810e..2fda028825 100644 --- a/erts/include/internal/win/ethread.h +++ b/erts/include/internal/win/ethread.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/include/internal/x86_64/ethread.h b/erts/include/internal/x86_64/ethread.h index 59c3980535..8887b8d77f 100644 --- a/erts/include/internal/x86_64/ethread.h +++ b/erts/include/internal/x86_64/ethread.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib/internal/README b/erts/lib/internal/README index f5b7ac27ab..9beba10bc2 100644 --- a/erts/lib/internal/README +++ b/erts/lib/internal/README @@ -3,16 +3,17 @@ Copyright Ericsson AB 2004-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/. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - 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. + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. %CopyrightEnd% diff --git a/erts/lib_src/Makefile b/erts/lib_src/Makefile index f94e47a856..632b8a0b09 100644 --- a/erts/lib_src/Makefile +++ b/erts/lib_src/Makefile @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2004-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in index d0ebab49d8..74e32ccdce 100644 --- a/erts/lib_src/Makefile.in +++ b/erts/lib_src/Makefile.in @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2004-2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/lib_src/common/erl_memory_trace_parser.c b/erts/lib_src/common/erl_memory_trace_parser.c index 625c140cf9..a81068089e 100644 --- a/erts/lib_src/common/erl_memory_trace_parser.c +++ b/erts/lib_src/common/erl_memory_trace_parser.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c index 7833dd8219..1262c50718 100644 --- a/erts/lib_src/common/erl_misc_utils.c +++ b/erts/lib_src/common/erl_misc_utils.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2006-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c index a38017b62f..387a104a7a 100644 --- a/erts/lib_src/common/erl_printf.c +++ b/erts/lib_src/common/erl_printf.c @@ -3,16 +3,17 @@ * * 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 - * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c index d6ae76f14c..307680505c 100644 --- a/erts/lib_src/common/erl_printf_format.c +++ b/erts/lib_src/common/erl_printf_format.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-2013. All Rights Reserved. * - * The 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/common/ethr_atomics.c b/erts/lib_src/common/ethr_atomics.c index c52166a7ec..42c078377d 100644 --- a/erts/lib_src/common/ethr_atomics.c +++ b/erts/lib_src/common/ethr_atomics.c @@ -12,16 +12,17 @@ * * 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 - * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c index 5b82a081ad..0cbb1b2fb8 100644 --- a/erts/lib_src/common/ethr_aux.c +++ b/erts/lib_src/common/ethr_aux.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-2014. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/common/ethr_cbf.c b/erts/lib_src/common/ethr_cbf.c index 04feceec89..e79ec2b40c 100644 --- a/erts/lib_src/common/ethr_cbf.c +++ b/erts/lib_src/common/ethr_cbf.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/common/ethr_mutex.c b/erts/lib_src/common/ethr_mutex.c index 4e56efaf8b..72aa34ec1c 100644 --- a/erts/lib_src/common/ethr_mutex.c +++ b/erts/lib_src/common/ethr_mutex.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-2013. All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/ose/ethr_event.c b/erts/lib_src/ose/ethr_event.c index 87294c98ea..24ea191c4b 100644 --- a/erts/lib_src/ose/ethr_event.c +++ b/erts/lib_src/ose/ethr_event.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c index 53628382b1..dc16acdd08 100644 --- a/erts/lib_src/ose/ethread.c +++ b/erts/lib_src/ose/ethread.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c index b35c599365..ba664236f6 100644 --- a/erts/lib_src/pthread/ethr_event.c +++ b/erts/lib_src/pthread/ethr_event.c @@ -3,16 +3,17 @@ * * 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/pthread/ethr_x86_sse2_asm.c b/erts/lib_src/pthread/ethr_x86_sse2_asm.c index 6cbe73cf16..7ce5a6d98a 100644 --- a/erts/lib_src/pthread/ethr_x86_sse2_asm.c +++ b/erts/lib_src/pthread/ethr_x86_sse2_asm.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/pthread/ethread.c b/erts/lib_src/pthread/ethread.c index c0b1dad0b6..ef11559654 100644 --- a/erts/lib_src/pthread/ethread.c +++ b/erts/lib_src/pthread/ethread.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/utils/make_atomics_api b/erts/lib_src/utils/make_atomics_api index 74736c5a2d..4b37e3fa74 100755 --- a/erts/lib_src/utils/make_atomics_api +++ b/erts/lib_src/utils/make_atomics_api @@ -6,16 +6,17 @@ %% %% 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 -%% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -1419,16 +1420,17 @@ comments() -> * * Copyright Ericsson AB ", Years, ". All Rights Reserved. * - * The 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/win/ethr_event.c b/erts/lib_src/win/ethr_event.c index a0d506356d..c88c8784a2 100644 --- a/erts/lib_src/win/ethr_event.c +++ b/erts/lib_src/win/ethr_event.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2009-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/lib_src/win/ethread.c b/erts/lib_src/win/ethread.c index fe5d4a327f..22b0b4040c 100644 --- a/erts/lib_src/win/ethread.c +++ b/erts/lib_src/win/ethread.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2010-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/preloaded/Makefile b/erts/preloaded/Makefile index 31fdeb96c5..fbe62d57bb 100644 --- a/erts/preloaded/Makefile +++ b/erts/preloaded/Makefile @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2008-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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 4ea2d41075..52034a0881 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 2008-2013. All Rights Reserved. # -# The 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/preloaded/src/add_abstract_code b/erts/preloaded/src/add_abstract_code index 211a60c930..4f479db2e8 100644 --- a/erts/preloaded/src/add_abstract_code +++ b/erts/preloaded/src/add_abstract_code @@ -6,16 +6,17 @@ %% %% Copyright Ericsson AB 2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 6b86a427ba..9f6cba33bd 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1996-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index cf941ea6ca..291356c7b1 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1996-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index cf9a06599a..8442aaf7e8 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index cf8edefd7d..7ed4efea4b 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2012-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index bb56c9ff73..c4e37b76f1 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1996-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/otp_ring0.erl b/erts/preloaded/src/otp_ring0.erl index 2ccf142f30..3158fc7d21 100644 --- a/erts/preloaded/src/otp_ring0.erl +++ b/erts/preloaded/src/otp_ring0.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2000-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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/prim_eval.S b/erts/preloaded/src/prim_eval.S index 958a79a1da..1b7b00a7c9 100644 --- a/erts/preloaded/src/prim_eval.S +++ b/erts/preloaded/src/prim_eval.S @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/prim_eval.erl b/erts/preloaded/src/prim_eval.erl index ec5af8c138..732e22468e 100644 --- a/erts/preloaded/src/prim_eval.erl +++ b/erts/preloaded/src/prim_eval.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 34679404a2..c87b2645ec 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2000-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 5e0b38aa68..4d04e1dacb 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2000-2013. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/prim_zip.erl b/erts/preloaded/src/prim_zip.erl index 1d5ab52a24..c4b949afcb 100644 --- a/erts/preloaded/src/prim_zip.erl +++ b/erts/preloaded/src/prim_zip.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/zip_internal.hrl b/erts/preloaded/src/zip_internal.hrl index a8f7b1f1b7..d5cf52fae4 100644 --- a/erts/preloaded/src/zip_internal.hrl +++ b/erts/preloaded/src/zip_internal.hrl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2008-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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index 5d9f90ec58..473ad649c7 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2003-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile index 14d5d46195..8025681924 100644 --- a/erts/start_scripts/Makefile +++ b/erts/start_scripts/Makefile @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 1997-2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/start_scripts/no_dot_erlang.rel.src b/erts/start_scripts/no_dot_erlang.rel.src index 6208572c00..bcc9fa9e8a 100644 --- a/erts/start_scripts/no_dot_erlang.rel.src +++ b/erts/start_scripts/no_dot_erlang.rel.src @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2013-2014. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/start_scripts/start_all_example.rel.src b/erts/start_scripts/start_all_example.rel.src index 2a1cabe7bb..2c4deb4485 100644 --- a/erts/start_scripts/start_all_example.rel.src +++ b/erts/start_scripts/start_all_example.rel.src @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/start_scripts/start_clean.rel.src b/erts/start_scripts/start_clean.rel.src index e229721e36..25519deb17 100644 --- a/erts/start_scripts/start_clean.rel.src +++ b/erts/start_scripts/start_clean.rel.src @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/start_scripts/start_sasl.rel.src b/erts/start_scripts/start_sasl.rel.src index e68a34af76..9cf417ade5 100644 --- a/erts/start_scripts/start_sasl.rel.src +++ b/erts/start_scripts/start_sasl.rel.src @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% -%% The 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/Makefile b/erts/test/Makefile index 6fbc19fcae..5263d8cd4f 100644 --- a/erts/test/Makefile +++ b/erts/test/Makefile @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 1997-2014. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl index a49d8f069f..3b0c083702 100644 --- a/erts/test/erl_print_SUITE.erl +++ b/erts/test/erl_print_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/erl_print_SUITE_data/Makefile.src b/erts/test/erl_print_SUITE_data/Makefile.src index fdffed3b2d..e6ea5cc6b9 100644 --- a/erts/test/erl_print_SUITE_data/Makefile.src +++ b/erts/test/erl_print_SUITE_data/Makefile.src @@ -3,16 +3,17 @@ # # 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 -# 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/test/erl_print_SUITE_data/character_test.h b/erts/test/erl_print_SUITE_data/character_test.h index 9c66618a71..9ff032cb07 100644 --- a/erts/test/erl_print_SUITE_data/character_test.h +++ b/erts/test/erl_print_SUITE_data/character_test.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/test/erl_print_SUITE_data/erl_print_tests.c b/erts/test/erl_print_SUITE_data/erl_print_tests.c index acb213cd3a..fb23dc35a6 100644 --- a/erts/test/erl_print_SUITE_data/erl_print_tests.c +++ b/erts/test/erl_print_SUITE_data/erl_print_tests.c @@ -3,16 +3,17 @@ * * 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 - * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ @@ -502,16 +503,17 @@ main(int argc, char *argv[]) " * %%CopyrightBegin%%\n" " * Copyright Ericsson AB 1996-2009. All Rights Reserved.\n" " * \n" - " * The contents of this file are subject to the Erlang Public License,\n" - " * Version 1.1, (the \"License\"); you may not use this file except in\n" - " * compliance with the License. You should have received a copy of the\n" - " * Erlang Public License along with this software. If not, it can be\n" - " * retrieved online at http://www.erlang.org/.\n" - " * \n" - " * Software distributed under the License is distributed on an \"AS IS\"\n" - " * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See\n" - " * the License for the specific language governing rights and limitations\n" - " * under the License.\n" + " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" + " * you may not use this file except in compliance with the License.\n" + " * You may obtain a copy of the License at\n" + " * \n" + " * http://www.apache.org/licenses/LICENSE-2.0\n" + " * \n" + " * Unless required by applicable law or agreed to in writing, software\n" + " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + " * See the License for the specific language governing permissions and\n" + " * limitations under the License.\n" " * %%CopyrightEnd%%\n" " */\n" "\n"); diff --git a/erts/test/erl_print_SUITE_data/integer_64_test.h b/erts/test/erl_print_SUITE_data/integer_64_test.h index 0df09ded44..0c3e7b98a8 100644 --- a/erts/test/erl_print_SUITE_data/integer_64_test.h +++ b/erts/test/erl_print_SUITE_data/integer_64_test.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/test/erl_print_SUITE_data/integer_test.h b/erts/test/erl_print_SUITE_data/integer_test.h index 94c8d59897..b91f3622d6 100644 --- a/erts/test/erl_print_SUITE_data/integer_test.h +++ b/erts/test/erl_print_SUITE_data/integer_test.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/test/erl_print_SUITE_data/snprintf_test.h b/erts/test/erl_print_SUITE_data/snprintf_test.h index 0849b60562..c612a65521 100644 --- a/erts/test/erl_print_SUITE_data/snprintf_test.h +++ b/erts/test/erl_print_SUITE_data/snprintf_test.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/test/erl_print_SUITE_data/string_test.h b/erts/test/erl_print_SUITE_data/string_test.h index 32249ab6e9..0e257888e6 100644 --- a/erts/test/erl_print_SUITE_data/string_test.h +++ b/erts/test/erl_print_SUITE_data/string_test.h @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2005-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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index 5002836954..c21064fd3f 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/erlc_SUITE_data/include/erl_test.hrl b/erts/test/erlc_SUITE_data/include/erl_test.hrl index fd89cb2f60..e7d096d2c1 100644 --- a/erts/test/erlc_SUITE_data/include/erl_test.hrl +++ b/erts/test/erlc_SUITE_data/include/erl_test.hrl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/erlc_SUITE_data/src/erl_test_bad.erl b/erts/test/erlc_SUITE_data/src/erl_test_bad.erl index fb62f835ca..b8c4ee2786 100644 --- a/erts/test/erlc_SUITE_data/src/erl_test_bad.erl +++ b/erts/test/erlc_SUITE_data/src/erl_test_bad.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl index 4d6c42c803..f043fbebc4 100644 --- a/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl +++ b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/erlc_SUITE_data/src/erl_test_ok.erl b/erts/test/erlc_SUITE_data/src/erl_test_ok.erl index 50fa063a94..a82eda95b3 100644 --- a/erts/test/erlc_SUITE_data/src/erl_test_ok.erl +++ b/erts/test/erlc_SUITE_data/src/erl_test_ok.erl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl b/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl index 409718e24c..de17b903d6 100644 --- a/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl +++ b/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl b/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl index a96085ac2d..9433dcb90d 100644 --- a/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl +++ b/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl @@ -3,16 +3,17 @@ %% %% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index 07966192c5..9279872d25 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/erlexec_SUITE_data/Makefile.src b/erts/test/erlexec_SUITE_data/Makefile.src index 2a8decaa4b..145aaedd64 100644 --- a/erts/test/erlexec_SUITE_data/Makefile.src +++ b/erts/test/erlexec_SUITE_data/Makefile.src @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/test/erlexec_SUITE_data/erlexec_tests.c b/erts/test/erlexec_SUITE_data/erlexec_tests.c index 1d1ca881d9..569bf7bcc4 100644 --- a/erts/test/erlexec_SUITE_data/erlexec_tests.c +++ b/erts/test/erlexec_SUITE_data/erlexec_tests.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2008-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index 24075286a8..4a40dbb11e 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/ethread_SUITE_data/Makefile.src b/erts/test/ethread_SUITE_data/Makefile.src index ad2556f327..e8b9c79576 100644 --- a/erts/test/ethread_SUITE_data/Makefile.src +++ b/erts/test/ethread_SUITE_data/Makefile.src @@ -3,16 +3,17 @@ # # 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 -# 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/test/ethread_SUITE_data/ethread_tests.c b/erts/test/ethread_SUITE_data/ethread_tests.c index 1d8083ef1f..12f7f3db7a 100644 --- a/erts/test/ethread_SUITE_data/ethread_tests.c +++ b/erts/test/ethread_SUITE_data/ethread_tests.c @@ -3,16 +3,17 @@ * * Copyright Ericsson AB 2004-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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/test/ignore_cores.erl b/erts/test/ignore_cores.erl index 8b1ac0fe6c..13f34cd10f 100644 --- a/erts/test/ignore_cores.erl +++ b/erts/test/ignore_cores.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2008-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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl index f1d8dc2587..b380b064bd 100644 --- a/erts/test/install_SUITE.erl +++ b/erts/test/install_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/nt_SUITE.erl b/erts/test/nt_SUITE.erl index b255195a00..dbae8df7fe 100644 --- a/erts/test/nt_SUITE.erl +++ b/erts/test/nt_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 1998-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/nt_SUITE_data/Makefile.src b/erts/test/nt_SUITE_data/Makefile.src index b26666252e..26da26b195 100644 --- a/erts/test/nt_SUITE_data/Makefile.src +++ b/erts/test/nt_SUITE_data/Makefile.src @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/test/nt_SUITE_data/nt_info.c b/erts/test/nt_SUITE_data/nt_info.c index 33cf046bb6..41d9a44c18 100644 --- a/erts/test/nt_SUITE_data/nt_info.c +++ b/erts/test/nt_SUITE_data/nt_info.c @@ -3,16 +3,17 @@ * * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index c416e031c2..69a0d19719 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2000-2015. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl index de05e6f206..328477d870 100644 --- a/erts/test/run_erl_SUITE.erl +++ b/erts/test/run_erl_SUITE.erl @@ -3,16 +3,17 @@ %% %% 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 -%% 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/test/run_erl_SUITE_data/defuncter.pl b/erts/test/run_erl_SUITE_data/defuncter.pl index 261f1b8061..666d4cca41 100644 --- a/erts/test/run_erl_SUITE_data/defuncter.pl +++ b/erts/test/run_erl_SUITE_data/defuncter.pl @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/test/run_erl_SUITE_data/run_erl_test.pl b/erts/test/run_erl_SUITE_data/run_erl_test.pl index 2155225e7f..b9e3f0a363 100644 --- a/erts/test/run_erl_SUITE_data/run_erl_test.pl +++ b/erts/test/run_erl_SUITE_data/run_erl_test.pl @@ -3,16 +3,17 @@ # # 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index 4858bfdfb7..8a91cf5b7e 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2014. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% -module(upgrade_SUITE). diff --git a/erts/test/upgrade_SUITE_data/start.src b/erts/test/upgrade_SUITE_data/start.src index 70d1a322c9..7098a6919a 100644 --- a/erts/test/upgrade_SUITE_data/start.src +++ b/erts/test/upgrade_SUITE_data/start.src @@ -4,16 +4,17 @@ # # Copyright Ericsson AB 2014. All Rights Reserved. # -# The 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. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/test/utils/gccifier.c b/erts/test/utils/gccifier.c index 7e4ffc7281..ca022eb390 100644 --- a/erts/test/utils/gccifier.c +++ b/erts/test/utils/gccifier.c @@ -3,16 +3,17 @@ * * 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 - * 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% * diff --git a/erts/test/utils/gccifier.sh b/erts/test/utils/gccifier.sh index 978aecf424..24b4d2f335 100755 --- a/erts/test/utils/gccifier.sh +++ b/erts/test/utils/gccifier.sh @@ -4,16 +4,17 @@ # # 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 -# 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl index 056561d3db..7f3260e4cb 100644 --- a/erts/test/z_SUITE.erl +++ b/erts/test/z_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% -%% The 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/erts/vsn.mk b/erts/vsn.mk index ab98bd4a17..1012f5eafd 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -3,16 +3,17 @@ # # Copyright Ericsson AB 1997-2013. All Rights Reserved. # -# The 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # -- cgit v1.2.3 From a99dd7b0f7fd2f99d08527898ca7564024751748 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 18 Jun 2015 11:45:36 +0200 Subject: Minor doc fixes --- erts/doc/src/time_correction.xml | 2 ++ erts/example/time_compat.erl | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index 8af98acc19..87b8c9d8fc 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -613,6 +613,7 @@

erlang:system_info(time_warp_mode)

erlang:system_info(time_correction)

erlang:system_info(start_time)

+

erlang:system_info(end_time)

@@ -865,6 +866,7 @@ EventTag = {Time, UMI}
API can easily be implemented using existing primitives (except for erlang:system_info(start_time), + erlang:system_info(end_time), erlang:system_info(os_monotonic_time_source), and erlang:system_info(os_system_time_source)). By wrapping the API with functions that fall back on diff --git a/erts/example/time_compat.erl b/erts/example/time_compat.erl index 90b7fbcc80..b566fb939f 100644 --- a/erts/example/time_compat.erl +++ b/erts/example/time_compat.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2014. All Rights Reserved. +%% Copyright Ericsson AB 2014-2015. All Rights Reserved. %% %% The 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,9 @@ %% versions. This way your code can automatically take advantage %% of the improvements in the API when available. This is an %% example of how to implement such an API, but it can be used -%% as is if you want to. Just add this module to your project, -%% and call the API via this module instead of calling the -%% BIFs directly. +%% as is if you want to. Just add (a preferrably renamed version of) +%% this module to your project, and call the API via this module +%% instead of calling the BIFs directly. %% -module(time_compat). @@ -241,7 +241,8 @@ system_info(Item) -> final; NotSupArg when NotSupArg == os_monotonic_time_source; NotSupArg == os_system_time_source; - NotSupArg == start_time -> + NotSupArg == start_time; + NotSupArg == end_time -> %% Cannot emulate this... erlang:error(notsup, [NotSupArg]); _ -> -- cgit v1.2.3 From 0a54d9988e7803e7d190f504838c6fe83f79bc8f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 18 Jun 2015 19:07:12 +0200 Subject: erts: Fix timer wheel initialization bug for non smp Init esdp->timer_wheel as NULL to please setup_aux_work_timer(). --- erts/emulator/beam/erl_process.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 88c1b5c121..fa37d0a9c9 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -5496,6 +5496,7 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num, ErtsRunQueue* runq, char** daww_ptr, size_t daww_sz) { + esdp->timer_wheel = NULL; #ifdef ERTS_SMP erts_bits_init_state(&esdp->erl_bits_state); esdp->match_pseudo_process = NULL; -- cgit v1.2.3 From 0dddfad1163ea34a6db338c6a75164665fbfdbdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sat, 20 Jun 2015 00:01:47 +0200 Subject: erts: Fix erl_poll on darwin --- erts/emulator/sys/common/erl_poll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 68d3c30bd4..da91e7338a 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -776,7 +776,7 @@ grow_poll_fds(ErtsPollSet ps, int min_ix) static void grow_select_fds(int fd, ERTS_fd_set* fds) { - int new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(fd + 1); + int new_len = erts_poll_new_table_len(fds->sz, fd + 1); if (new_len > max_fds) new_len = max_fds; new_len = ERTS_FD_SIZE(new_len); -- cgit v1.2.3 From a988faecec41db09c819fd948ef294ee5282e5b5 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Mon, 22 Jun 2015 15:48:15 +0200 Subject: Fix documentation of ERL_DRV_ERROR_ERRNO --- erts/doc/src/driver_entry.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml index b34ca136f3..e035857dfe 100644 --- a/erts/doc/src/driver_entry.xml +++ b/erts/doc/src/driver_entry.xml @@ -210,7 +210,7 @@ typedef struct erl_drv_entry { number >= 0 or a pointer, or if the driver can't be started, one of three error codes should be returned:

ERL_DRV_ERROR_GENERAL - general error, no error code

-

ERL_DRV_ERROR_ERRNO - error with error code in erl_errno

+

ERL_DRV_ERROR_ERRNO - error with error code in errno

ERL_DRV_ERROR_BADARG - error, badarg

If an error code is returned, the port isn't started.

-- cgit v1.2.3 From 0375ffb60c9ce2d8e65333bbe79aabfa7395aea2 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 22 Jun 2015 17:49:07 +0200 Subject: erts: Expand test map_SUITE:t_bif_merge_and_check with merge of randomized maps. --- erts/emulator/test/map_SUITE.erl | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 527b6987fa..4a8240a9b4 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -2390,6 +2390,9 @@ check_keys_exist([K|Ks],M) -> check_keys_exist(Ks,M). t_bif_merge_and_check(Config) when is_list(Config) -> + + io:format("rand:export_seed() -> ~p\n",[rand:export_seed()]), + %% simple disjunct ones %% make sure all keys are unique Kss = [[a,b,c,d], @@ -2437,8 +2440,49 @@ t_bif_merge_and_check(Config) when is_list(Config) -> M41 = maps:merge(M4,M1), ok = check_key_values(KVs1 ++ [{d,5}] ++ KVs, M41), + [begin Ma = random_map(SzA, a), + Mb = random_map(SzB, b), + ok = merge_maps(Ma, Mb) + end || SzA <- [3,10,20,100,200,1000], SzB <- [3,10,20,100,200,1000]], + ok. +% Generate random map with an average of Sz number of pairs: K -> {V,K} +random_map(Sz, V) -> + random_map_insert(#{}, 0, V, Sz*2). + +random_map_insert(M0, K0, _, Sz) when K0 > Sz -> + M0; +random_map_insert(M0, K0, V, Sz) -> + Key = K0 + rand:uniform(3), + random_map_insert(M0#{Key => {V,Key}}, Key, V, Sz). + + +merge_maps(A, B) -> + AB = maps:merge(A, B), + %%io:format("A=~p\nB=~p\n",[A,B]), + maps_foreach(fun(K,VB) -> VB = maps:get(K, AB) + end, B), + maps_foreach(fun(K,VA) -> + case {maps:get(K, AB),maps:find(K, B)} of + {VA, error} -> ok; + {VB, {ok, VB}} -> ok + end + end, A), + + maps_foreach(fun(K,V) -> + case {maps:find(K, A),maps:find(K, B)} of + {{ok, V}, error} -> ok; + {error, {ok, V}} -> ok; + {{ok,_}, {ok, V}} -> ok + end + end, AB), + ok. + +maps_foreach(Fun, Map) -> + maps:fold(fun(K,V,_) -> Fun(K,V) end, void, Map). + + check_key_values([],_) -> ok; check_key_values([{K,V}|KVs],M) -> V = maps:get(K,M), -- cgit v1.2.3 From 37f143e9e16e89d753b0e5e4415968dbcd5f6b65 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 22 Jun 2015 20:27:52 +0200 Subject: Fix node/dist refc count --- erts/emulator/beam/erl_hl_timer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 907491f616..51a0d68247 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -2966,7 +2966,7 @@ debug_callback_timer_foreach_list(ErtsHLTimer *tmr, void *vdfct) = (ErtsDebugForeachCallbackTimer *) vdfct; if ((tmr->head.roflgs & ERTS_TMR_ROFLG_CALLBACK) - && (tmr->receiver.callback && dfct->tclbk)) + && (tmr->receiver.callback == dfct->tclbk)) (*dfct->func)(dfct->arg, tmr->timeout, tmr->head.u.arg); @@ -2984,7 +2984,7 @@ debug_callback_timer_foreach(ErtsHLTimer *tmr, void *vdfct) vdfct); if ((tmr->head.roflgs & ERTS_TMR_ROFLG_CALLBACK) - && (tmr->receiver.callback && dfct->tclbk)) + && (tmr->receiver.callback == dfct->tclbk)) (*dfct->func)(dfct->arg, tmr->timeout, tmr->head.u.arg); @@ -3037,7 +3037,7 @@ erts_debug_callback_timer_foreach(void (*tclbk)(void *), debug_callback_timer_foreach(srv->yield.root, (void *) &dfct); - time_rbt_foreach(srv->btm_tree, + time_rbt_foreach(srv->time_tree, debug_callback_timer_foreach, (void *) &dfct); } -- cgit v1.2.3 From 7bbb207b30360c60fb9965359635e1cd0ad70c77 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 22 Jun 2015 22:11:16 +0200 Subject: erts: Fix race in poller thread wake up --- erts/emulator/sys/common/erl_poll.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 0a58a625b2..4ac02d21d3 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -410,7 +410,7 @@ static ERTS_INLINE int is_interrupted_reset(ErtsPollSet ps) { #if defined(USE_THREADS) || ERTS_POLL_ASYNC_INTERRUPT_SUPPORT - return (erts_atomic32_xchg_nob(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN) + return (erts_atomic32_xchg_acqb(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN) == ERTS_POLL_WOKEN_INTR); #else return 0; @@ -421,7 +421,7 @@ static ERTS_INLINE void woke_up(ErtsPollSet ps) { #if defined(USE_THREADS) || ERTS_POLL_ASYNC_INTERRUPT_SUPPORT - erts_aint32_t wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state); + erts_aint32_t wakeup_state = erts_atomic32_read_acqb(&ps->wakeup_state); if (wakeup_state == ERTS_POLL_NOT_WOKEN) (void) erts_atomic32_cmpxchg_nob(&ps->wakeup_state, ERTS_POLL_WOKEN, @@ -448,14 +448,9 @@ wake_poller(ErtsPollSet ps, int interrupted, int async_signal_safe) wakeup_state = erts_atomic32_cmpxchg_relb(&ps->wakeup_state, ERTS_POLL_WOKEN, ERTS_POLL_NOT_WOKEN); - else { - /* - * We might unnecessarily write to the pipe, however, - * that isn't problematic. - */ - wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state); - erts_atomic32_set_relb(&ps->wakeup_state, ERTS_POLL_WOKEN_INTR); - } + else + wakeup_state = erts_atomic32_xchg_relb(&ps->wakeup_state, + ERTS_POLL_WOKEN_INTR); wake = wakeup_state == ERTS_POLL_NOT_WOKEN; } /* -- cgit v1.2.3 From 2955ddebc32837b66d9bacb4e925ad0ed0033168 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 23 Jun 2015 10:24:26 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 703 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 703 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 2574d45184..2d96ed6105 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,709 @@

This document describes the changes made to the ERTS application.

+
Erts 7.0 + +
Fixed Bugs and Malfunctions + + +

+ Fix issuing with spaces and quoting in the arguments when + using erlang:open_port spawn_executable on windows. The + behavior now mimics how unix works. This change implies a + backwards incompatibility for how spawn_executable works + on windows.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11905

+
+ +

+ Fix global call trace when hipe compiled code call beam + compiled functions. Tracing of beam functions should now + alway work regardless who the caller is.

+

+ Own Id: OTP-11939

+
+ +

+ Correct cache alignment for ETS write_concurrency + locks to improve performance by reduced false sharing. + May increase memory footprint for tables with + write_concurrency.

+

+ Own Id: OTP-11974

+
+ +

+ All possibly blocking operations in the fd/spawn and + terminal driver have been converted to non-blocking + operations. Before this fix it was possible for the VM to + be blocked for a long time if the entity consuming + stdout/stderr did not consume it fast enough.

+

+ Own Id: OTP-12239

+
+ +

+ Add missing overhead for offheap binaries created from + external format. This fix can improve the garbage + collection of large binaries originating from + binary_to_term or messages from remote nodes.

+

+ Own Id: OTP-12554

+
+ +

+ Ensure hashing of zero is consistent

+

Erlang treats positive and negative zero as + equal:

+

+ true = 0.0 =:= 0.0/-1

+

However, Erlangs hash functions: hash, phash and + phash2 did not reflect this behaviour. The hash values + produced by the different hash functions would not be + identical for positive and negative zero.

This + change ensures that hash value of positive zero is always + produced regardless of the signedness of the zero float, + i.e.,

+

+ true = erlang:phash2(0.0) =:= + erlang:phash2(0.0/-1)

+

+ Own Id: OTP-12641

+
+ +

+ Ensure NIF term creation disallows illegal floating point + values and too long atoms. Such values will cause a NIF + to throw badarg exception when it returns.

+

+ Own Id: OTP-12655

+
+ +

+ Fixed building of Map results from match_specs

+

+ A faulty "box-value" entered into the heap which could + cause a segmentation fault in the garbage collector if it + was written on a heap fragment.

+

+ Own Id: OTP-12656

+
+ +

+ Fix hipe bug when matching a "writable" binary. The bug + has been seen to sometimes cause a failed binary matching + of a correct utf8 character, but other symptoms are also + possible.

+

+ Own Id: OTP-12667

+
+ +

+ Keep dirty schedulers from waking other schedulers.

+

+ Own Id: OTP-12685

+
+ +

+ Disable floating point exceptions if the VM is compiled + by clang/llvm. This is a known long-standing problem in + clang/llvm.

+

+ Own Id: OTP-12717

+
+ +

+ Fix bug in file:sendfile for FreeBSD causing not + the entire file to be sent.

+

+ Own Id: OTP-12720

+
+ +

+ Fix the broken Android support in erl_child_setup.c

+

+ Own Id: OTP-12751

+
+ +

+ Faulty statistics reported by the fix_alloc + allocator.

+

+ Own Id: OTP-12766

+
+ +

+ Fix two erts_snprintf() calls to correct sizes.

+

+ - run_erl.c (ose): Use the size of the signal type, not + its pointer. - erl_node_tables.c: Use the size of the + _BUFFER in erts_snprintf() to make sure we can use the + full space.

+

+ Own Id: OTP-12771

+
+ +

+ Delayed memory allocations could be delayed an + unnecessarily long time.

+

+ Own Id: OTP-12812

+
+ +

+ Make sure that timeouts on a pool of acceptors are + released in the correct order.

+

+ Own Id: OTP-12817

+
+ +

+ Fix segmentation fault in module_info for deleted modules

+

+ Own Id: OTP-12820

+
+ +

Fix garbage collection of literals in code purge

+

During code purging and check_process_code, the + checking of the binary reference embedded in the match + binary state was omitted for the tracing tests. This + would cause the binary match state to reference + deallocated memory.

+

+ Own Id: OTP-12821

+
+ +

+ A bug has been corrected for gen_tcp:close so when + {linger,{true,0}} is in effect it does not wait for data + in the driver queue to transfer out before closing the + port. Bug fix by Rory Byrne.

+

+ Own Id: OTP-12840

+
+ +

+ The documentation of the driver callback start() + erroneously stated that a return value of + ERL_DRV_ERROR_ERRNO caused the error value to be + passed via erl_errno when it should have been + errno.

+

+ Own Id: OTP-12855

+
+
+
+ + +
Improvements and New Features + + +

+ Add md5 and module entries to + ?MODULE:module_info/0/1 and remove obsolete entry + 'import'.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-11940

+
+ +

+ Debug function erlang:display/1 shows content of + binaries and bitstrings, not only the length.

+

+ Own Id: OTP-11941

+
+ +

The time functionality of Erlang has been extended. + This both includes a new + API for time, as well as time warp + modes which alters the behavior of the system + when system time changes. You are strongly encouraged + to use the new API instead of the old API based on + erlang:now/0. + erlang:now/0 has been deprecated since it is and + forever will be a scalability bottleneck. For more + information see the Time and Time + Correction chapter of the ERTS User's + Guide.

+

Besides the API changes and time warp modes a lot of + scalability and performance improvements regarding time + management has been made internally in the runtime + system. Examples of such improvements are scheduler + specific timer wheels, scheduler specific BIF timer + management, parallel retrieval of monotonic time and + system time on systems with primitives that are not + buggy.

+

+ Own Id: OTP-11997

+
+ +

erlang:function_exported(M, F, A) will now + return true if M:F/A refers to a BIF.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12099

+
+ +

+ New BIF: erlang:get_keys/0, lists all keys + associated with the process dictionary. Note: + erlang:get_keys/0 is auto-imported.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12151 Aux Id: seq12521

+
+ +

+ Make distributed send of large messages yield to improve + real-time characteristics.

+

+ Own Id: OTP-12232

+
+ +

+ Use high accuracy poll timeouts

+

+ Where available, use poll/select API's that can handle + time resolutions less than 1ms. In the cases where such + API's are not available the timeout is rounded up to the + nearest ms.

+

+ Own Id: OTP-12236

+
+ +

+ The internal group to user_drv protocol has been changed + to be synchronous in order to guarantee that output sent + to a process implementing the user_drv protocol is + printed before replying. This protocol is used by the + standard_output device and the ssh application when + acting as a client.

+

+ This change changes the previous unlimited buffer when + printing to standard_io and other devices that end up in + user_drv to 1KB.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12240

+
+ +

The previously introduced "eager check I/O" feature is + now enabled by default.

+

Eager check I/O can be disabled using the erl + command line argument: +secio false

+

Characteristics impact compared to previous + default:

Lower latency and smoother + management of externally triggered I/O operations. + A slightly reduced priority of externally triggered + I/O operations. +

+ Own Id: OTP-12254 Aux Id: OTP-12117

+
+ +

+ Properly support maps in match_specs

+

+ Own Id: OTP-12270

+
+ +

+ The notice that a crashdump has been written has been + moved to be printed before the crashdump is generated + instead of afterwords. The wording of the notice has also + been changed.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12292

+
+ +

+ New function ets:take/2. Works the same as + ets:delete/2 but also returns the deleted + object(s).

+

+ Own Id: OTP-12309

+
+ +

+ Tracing with cpu_timestamp option has been enabled on + Linux.

+

+ Own Id: OTP-12366

+
+ +

+ ets:info/1,2 now contains information about whether + write_concurrency or read_concurrency is enabled.

+

+ Own Id: OTP-12376

+
+ +

+ Improved usage of gcc's builtins for atomic memory + access. These are used when no other implementation of + atomic memory operations is available. For example, when + compiling for ARM when libatomic_ops is not + available.

+

+ The largest improvement will be seen when compiling with + a gcc with support for the __atomic_* + builtins (using a gcc of at least version 4.7), + but also when only the legacy __sync_* builtins + are available (using a gcc of at least version + 4.1) an improvement can be seen.

+

+ For more information see the "Atomic + Memory Operations and the VM" section of + $ERL_TOP/HOWTO/INSTALL.md.

+

+ Own Id: OTP-12383

+
+ +

+ Introduce math:log2/1 function to math module.

+

+ Own Id: OTP-12411

+
+ +

The documentation of the Abstract Format (in the ERTS + User's Guide) has been updated with types and + specification. (Thanks to Anthony Ramine.)

The + explicit representation of parentheses used in types of + the abstract format has been removed. Instead the new + functions erl_parse:type_inop_prec() and + erl_parse:type_preop_prec() can be used for + inserting parentheses where needed.

+

+ Own Id: OTP-12492

+
+ +

+ Remove perfctr support

+

+ Development of perfctr in the linux kernel ceased in + 2010. The perfctr support code in the Erlang VM is thus + effectively dead code and therefor removed.

+

+ Own Id: OTP-12508

+
+ +

zlib:inflateChunk/2 has been added. It works + like zlib:inflate/2, but decompresses no more data + than will fit in the buffer configured by + zlib:setBufSize/2.

+

+ Own Id: OTP-12548

+
+ +

+ Use linear search for small select_val arrays

+

+ Own Id: OTP-12555

+
+ +

+ New BIF ets:update_counter/4 with a default object as + argument, which will be inserted in the table if the key + was not found.

+

+ Own Id: OTP-12563

+
+ +

+ Export missing types from zlib module

+

+ Own Id: OTP-12584

+
+ +

+ Use persistent hashmaps for large Maps

Maps will use a + persistent hashmap implementation when the number of + pairs in a Map becomes sufficiently large. The change + will occur when a Map reaches 33 pairs in size but this + limit might change in the future.

+

The most significant impact for the user by this + change is speed, and to a lesser degree memory + consumption and introspection of Maps. Memory consumption + size is probalistic but lesser than gb_trees or + dict for instance. Any other impacts will be + transparent for the user except for the following + changes.

+

Semantics of Maps have changed in two incompatible + ways compared to the experimental implementation in OTP + 17:

Hashing of maps is done different by + erlang:phash2/1,2, erlang:phash/1 and + erlang:hash/2. Comparing two maps + with ==, /=, =<, <, >= and >, is done + different if the keys contain floating point + numbers. +

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12585

+
+ +

+ Scalability improvement for erlang:make_ref/0, + and other functionality that create references. Each + scheduler now manage its own set of references. By this + no communication at all is needed when creating + references.

+

+ Previous implementation generated a strictly + monotonically increasing sequence of references + corresponding to creation time on the runtime system + instance. This is not the case with current + implementation. You can only expect reference to be + unique. The Erlang/OTP documentation has never mentioned + anything else but the uniqueness property, so this change + is fully compatible. The only reason we've + marked this as a potential incompatibility is since an + early draft for an Erlang specification mentions strict + monotonicity as a property.

+

+ If you need to create data with a strict monotonicity + property use erlang:unique_integer([monotonic]). + Do not use the deprecated erlang:now().

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12610

+
+ +

+ Enable different abort signal from heart

+

By using environment variable HEART_KILL_SIGNAL, heart + can now use a different signal to kill the old running + Erlang.

+

By default the signal is SIGKILL but SIGABRT may also + be used by setting environment variable: + HEART_KILL_SIGNAL=SIGABRT

+

+ Own Id: OTP-12613 Aux Id: seq12826

+
+ +

+ Update autconf to latest version 2015-03-04

+

+ Own Id: OTP-12646

+
+ +

+ Optimization of timers internally in the VM. This include + process timers (receive ... after), port timers + (driver_set_timer()) as well as BIF timers + (erlang:send_after()/erlang:start_timer()).

+

+ Each scheduler thread now has its own lock-free timer + service instead of one locked central service. This + dramatically improves performance of timer management on + systems with a large amount of schedulers and timers.

+

+ The timer service internal data structure has also been + optimized to be able to handle more timers than before. + That is, each timer service is by its self able to handle + more timers without dramatic performance loss than the + old centralized timer service.

+

+ The API of BIF timers has also been extended. Timeout + values are for example no longer limited to 32-bit + integers. For more information see the documentation of + erlang:start_timer/4, + erlang:send_after/4, + erlang:cancel_timer/2, + and erlang:read_timer/2.

+

+ Characteristics impact: Calls to the synchronous versions + of erlang:cancel_timer(), and + erlang:read_timer() may take substantially longer + time to complete than before. This occur when the timer + that is accessed is managed by a remote scheduler. You + typically want to use the new asynchronous option in + order to avoid blocking the calling process.

+

+ Own Id: OTP-12650 Aux Id: OTP-11997

+
+ +

+ Specialize instructions from common assembler patterns

+

Specialize common instructions of rem, + band, minus and plus in the beam + loader. This will reduce the number of fetches and thus + lessen the instruction dispatch pressure during runtime + and speed up those operations in some common cases.

+

Specialize move patterns from x-registers to the stack + with a new move_window instruction. This change + will reduce instruction dispatch pressure.

+

+ Own Id: OTP-12690

+
+ +

+ Fix cross compilation for Android.

+

+ Own Id: OTP-12693

+
+ +

+ Fix incorrect use of autoconf macro AC_EGREP_CPP, which + could cause faulty configuration if run from a path + containing the string 'yes'.

+

+ Own Id: OTP-12706

+
+ +

+ Minimal Java version is now 1.6

+

+ Own Id: OTP-12715

+
+ +

+ Send format and args on process exit to error_logger

+

+ Previously, the emulator would generate a whole string + with values and call the error_logger passing + "~s~n". This changes it to a format string + containing ~p with the respective values as + arguments.

+

+ Own Id: OTP-12735

+
+ +

+ Map error logger warnings to warning messages by default.

+

+ Own Id: OTP-12755

+
+ +

+ Configure architecture ppc64le architecture as a ppc64

+

+ Own Id: OTP-12761

+
+ +

+ Add function enif_raise_exception to allow a NIF + to raise an error exception with any type of reason.

+

+ Own Id: OTP-12770

+
+ +

+ Optimized node table statistics retrieval.

+

+ Own Id: OTP-12777

+
+ +

+ Map beam error logger warnings to warning messages by + default. Previously these messages were mapped to the + error channel by default.

+

+ Own Id: OTP-12781

+
+ +

+ gen_tcp:shutdown/2 is now asynchronous

+

+ This solves the following problems with the old + implementation:

+

+ It doesn't block when the TCP peer is idle or slow. This + is the expected behaviour when shutdown() is called: the + caller needs to be able to continue reading from the + socket, not be prevented from doing so.

+

+ It doesn't truncate the output. The current version of + gen_tcp:shutdown/2 will truncate any outbound data in the + driver queue after about 10 seconds if the TCP peer is + idle of slow. Worse yet, it doesn't even inform anyone + that the data has been truncated: 'ok' is returned to the + caller; and a FIN rather than an RST is sent to the TCP + peer.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12797

+
+ +

+ Introduced delayed node table GC. This in order to avoid + oscillation of entries in and out of the tables. The + oscillation caused unnecessary lock contention on the + table locks. The delay length can be set by passing the + +zdntgc + command line argument.

+

+ Characteristics impact: The tables can grow to very large + sizes with unused entries if the node is get huge amounts + of short lived connections from other nodes. This problem + can be alleviated by shortening the length of the delay + using the +zdntgc command line argument.

+

+ Own Id: OTP-12802

+
+ +

Improved implementation of erlang:statistics(io) + in order to reduce contention between schedulers.

+

Characteristics impact: The actual call to + erlang:statistics(io) takes longer time to + complete, but the overall impact on the system is + improved.

+

+ Own Id: OTP-12842

+
+ +

+ There are many cases where user code needs to be able to + distinguish between a socket that was closed normally and + one that was aborted. Setting the option + {show_econnreset, true} enables the user to receive + ECONNRESET errors on both active and passive sockets.

+

+ Own Id: OTP-12843

+
+ +

+ Do not preallocate too large event pool

+

+ A default pool size of 4000 is too excessive for the + common case. This corresponds directly to the number of + threads in the system. Change + ERTS_TS_EV_ALLOC_DEFAULT_POOL_SIZE to 2048. Change + ERTS_TS_EV_ALLOC_POOL_SIZE to 32.

+

+ Own Id: OTP-12849

+
+
+
+ +
+
Erts 6.4.1
Fixed Bugs and Malfunctions -- cgit v1.2.3 From 1c86a620d74f4f9383c4956dafd3e2486300dc0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 17 Jun 2015 17:33:29 +0200 Subject: erts: Remove HALFWORD_HEAP definition --- erts/emulator/beam/beam_emu.c | 13 +- erts/emulator/beam/beam_load.c | 17 +- erts/emulator/beam/bif.c | 11 +- erts/emulator/beam/big.c | 20 +-- erts/emulator/beam/big.h | 9 +- erts/emulator/beam/copy.c | 32 ---- erts/emulator/beam/dist.c | 14 +- erts/emulator/beam/erl_alloc.c | 142 +-------------- erts/emulator/beam/erl_alloc_util.c | 38 +--- erts/emulator/beam/erl_alloc_util.h | 3 - erts/emulator/beam/erl_bif_binary.c | 3 +- erts/emulator/beam/erl_bif_info.c | 6 +- erts/emulator/beam/erl_bif_re.c | 4 +- erts/emulator/beam/erl_bif_unique.c | 6 +- erts/emulator/beam/erl_bits.c | 4 +- erts/emulator/beam/erl_db.c | 11 +- erts/emulator/beam/erl_db_hash.c | 3 - erts/emulator/beam/erl_db_tree.c | 3 - erts/emulator/beam/erl_db_util.c | 245 +------------------------- erts/emulator/beam/erl_db_util.h | 3 - erts/emulator/beam/erl_gc.c | 29 +-- erts/emulator/beam/erl_init.c | 5 - erts/emulator/beam/erl_lock_check.c | 3 - erts/emulator/beam/erl_map.c | 8 - erts/emulator/beam/erl_map.h | 14 +- erts/emulator/beam/erl_message.h | 3 - erts/emulator/beam/erl_node_container_utils.h | 2 +- erts/emulator/beam/erl_node_tables.c | 16 -- erts/emulator/beam/erl_node_tables.h | 2 +- erts/emulator/beam/erl_printf_term.c | 6 +- erts/emulator/beam/erl_process.c | 27 +-- erts/emulator/beam/erl_term.h | 82 ++------- erts/emulator/beam/erl_utils.h | 16 +- erts/emulator/beam/erl_vm.h | 6 +- erts/emulator/beam/external.c | 50 +----- erts/emulator/beam/global.h | 25 +-- erts/emulator/beam/io.c | 20 +-- erts/emulator/beam/sys.h | 53 ------ erts/emulator/beam/utils.c | 41 +---- erts/emulator/drivers/common/efile_drv.c | 4 +- erts/emulator/sys/common/erl_mmap.c | 3 +- erts/emulator/sys/common/erl_mseg.c | 46 ----- erts/emulator/sys/common/erl_mseg.h | 3 - erts/emulator/utils/beam_makeops | 4 - 44 files changed, 97 insertions(+), 958 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 8b409e139b..0c6181077c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -756,7 +756,7 @@ void** beam_ops; #define IsBitstring(Src, Fail) \ if (is_not_binary(Src)) { Fail; } -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) #define BsSafeMul(A, B, Fail, Target) \ do { Uint64 _res = (A) * (B); \ if (_res / B != A) { Fail; } \ @@ -1247,9 +1247,6 @@ void process_main(void) PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); -#if HALFWORD_HEAP - ASSERT(erts_get_scheduler_data()->num_tmp_heap_used == 0); -#endif ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); c_p = schedule(c_p, reds_used); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); @@ -2131,8 +2128,6 @@ void process_main(void) * c_p->def_arg_reg[0]. Note that it is safe to use this * location because there are no living x registers in * a receive statement. - * Note that for the halfword emulator, the two first elements - * of the array are used. */ BeamInstr** pi = (BeamInstr**) c_p->def_arg_reg; *pi = I+3; @@ -4535,11 +4530,11 @@ do { \ _integer = get_int32(_mb->base + _mb->offset/8); } _mb->offset += 32; -#if !defined(ARCH_64) || HALFWORD_HEAP +#if !defined(ARCH_64) if (IS_USMALL(0, _integer)) { #endif _result = make_small(_integer); -#if !defined(ARCH_64) || HALFWORD_HEAP +#if !defined(ARCH_64) } else { TestHeap(BIG_UINT_HEAP_SIZE, Arg(1)); _result = uint_to_big((Uint) _integer, HTOP); @@ -5129,7 +5124,7 @@ do { \ neg_o_reds = -c_p->def_arg_reg[4]; FCALLS = c_p->fcalls; SWAPIN; - switch( c_p->def_arg_reg[3] ) { /* Halfword wont work with hipe yet! */ + switch( c_p->def_arg_reg[3] ) { case HIPE_MODE_SWITCH_RES_RETURN: ASSERT(is_value(reg[0])); MoveReturn(reg[0], r(0)); diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index b70e5b9a2d..10baab506a 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -1894,15 +1894,14 @@ load_code(LoaderState* stp) */ { Eterm* hp; -/* XXX:PaN - Halfword should use ARCH_64 variant instead */ -#if !defined(ARCH_64) || HALFWORD_HEAP +#if !defined(ARCH_64) Uint high, low; # endif last_op->a[arg].val = new_literal(stp, &hp, FLOAT_SIZE_OBJECT); hp[0] = HEADER_FLONUM; last_op->a[arg].type = TAG_q; -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) GetInt(stp, 8, hp[1]); # else GetInt(stp, 4, high); @@ -3272,14 +3271,14 @@ gen_literal_timeout(LoaderState* stp, GenOpArg Fail, GenOpArg Time) op->a[1].type = TAG_u; if (Time.type == TAG_i && (timeout = Time.val) >= 0 && -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) (timeout >> 32) == 0 #else 1 #endif ) { op->a[1].val = timeout; -#if !defined(ARCH_64) || HALFWORD_HEAP +#if !defined(ARCH_64) } else if (Time.type == TAG_q) { Eterm big; @@ -3296,7 +3295,7 @@ gen_literal_timeout(LoaderState* stp, GenOpArg Fail, GenOpArg Time) } #endif } else { -#if !defined(ARCH_64) || HALFWORD_HEAP +#if !defined(ARCH_64) error: #endif op->op = genop_i_wait_error_0; @@ -3319,14 +3318,14 @@ gen_literal_timeout_locked(LoaderState* stp, GenOpArg Fail, GenOpArg Time) op->a[1].type = TAG_u; if (Time.type == TAG_i && (timeout = Time.val) >= 0 && -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) (timeout >> 32) == 0 #else 1 #endif ) { op->a[1].val = timeout; -#if !defined(ARCH_64) || HALFWORD_HEAP +#if !defined(ARCH_64) } else if (Time.type == TAG_q) { Eterm big; @@ -3343,7 +3342,7 @@ gen_literal_timeout_locked(LoaderState* stp, GenOpArg Fail, GenOpArg Time) } #endif } else { -#if !defined(ARCH_64) || HALFWORD_HEAP +#if !defined(ARCH_64) error: #endif op->op = genop_i_wait_error_locked_0; diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 4e3a1cef69..5ec1840c7b 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4117,16 +4117,9 @@ BIF_RETTYPE make_fun_3(BIF_ALIST_3) if (arity < 0) { goto error; } -#if HALFWORD_HEAP - hp = HAlloc(BIF_P, 3); - hp[0] = HEADER_EXPORT; - /* Yes, May be misaligned, but X86_64 will fix it... */ - *((Export **) (hp+1)) = erts_export_get_or_make_stub(BIF_ARG_1, BIF_ARG_2, (Uint) arity); -#else hp = HAlloc(BIF_P, 2); hp[0] = HEADER_EXPORT; hp[1] = (Eterm) erts_export_get_or_make_stub(BIF_ARG_1, BIF_ARG_2, (Uint) arity); -#endif BIF_RET(make_export(hp)); } @@ -4631,7 +4624,7 @@ BIF_RETTYPE hash_2(BIF_ALIST_2) if ((range = signed_val(BIF_ARG_2)) <= 0) { /* [1..MAX_SMALL] */ BIF_ERROR(BIF_P, BADARG); } -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) if (range > ((1L << 27) - 1)) BIF_ERROR(BIF_P, BADARG); #endif @@ -4703,7 +4696,7 @@ BIF_RETTYPE phash2_2(BIF_ALIST_2) /* * Return either a small or a big. Use the heap for bigs if there is room. */ -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) BIF_RET(make_small(final_hash)); #else if (IS_USMALL(0, final_hash)) { diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 044bf6a34e..1e6582a7ca 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1487,20 +1487,8 @@ Eterm uint_to_big(Uint x, Eterm *y) Eterm uword_to_big(UWord x, Eterm *y) { -#if HALFWORD_HEAP - Uint upper = x >> 32; - Uint lower = x & 0xFFFFFFFFUL; - if (upper == 0) { - *y = make_pos_bignum_header(1); - } else { - *y = make_pos_bignum_header(2); - BIG_DIGIT(y, 1) = upper; - } - BIG_DIGIT(y, 0) = lower; -#else *y = make_pos_bignum_header(1); BIG_DIGIT(y, 0) = x; -#endif return make_big(y); } @@ -1525,7 +1513,7 @@ Eterm small_to_big(Sint x, Eterm *y) Eterm erts_uint64_to_big(Uint64 x, Eterm **hpp) { Eterm *hp = *hpp; -#if defined(ARCH_32) || HALFWORD_HEAP +#if defined(ARCH_32) if (x >= (((Uint64) 1) << 32)) { *hp = make_pos_bignum_header(2); BIG_DIGIT(hp, 0) = (Uint) (x & ((Uint) 0xffffffff)); @@ -1555,7 +1543,7 @@ Eterm erts_sint64_to_big(Sint64 x, Eterm **hpp) neg = 1; ux = -(Uint64)x; } -#if defined(ARCH_32) || HALFWORD_HEAP +#if defined(ARCH_32) if (ux >= (((Uint64) 1) << 32)) { if (neg) *hp = make_neg_bignum_header(2); @@ -1588,7 +1576,7 @@ erts_uint64_array_to_big(Uint **hpp, int neg, int len, Uint64 *array) pot_digits = digits = 0; for (i = 0; i < len; i++) { -#if defined(ARCH_32) || HALFWORD_HEAP +#if defined(ARCH_32) Uint low_val = array[i] & ((Uint) 0xffffffff); Uint high_val = (array[i] >> 32) & ((Uint) 0xffffffff); BIG_DIGIT(headerp, pot_digits) = low_val; @@ -1651,8 +1639,6 @@ big_to_double(Wterm x, double* resp) /* * Logic has been copied from erl_bif_guard.c and slightly * modified to use a static instead of dynamic heap - * - * HALFWORD: Return relative term with 'heap' as base. */ Eterm double_to_big(double x, Eterm *heap, Uint hsz) diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 4aa9724ae3..94f9bce10e 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -35,7 +35,7 @@ typedef Uint ErtsDigit; -#if ((SIZEOF_VOID_P == 4) || HALFWORD_HEAP) && defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG == 8) +#if (SIZEOF_VOID_P == 4) && defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG == 8) /* Assume 32-bit machine with long long support */ typedef Uint64 ErtsDoubleDigit; typedef Uint16 ErtsHalfDigit; @@ -90,13 +90,9 @@ typedef Uint dsize_t; /* Vector size type */ #define BIG_UINT_HEAP_SIZE (1 + 1) /* always, since sizeof(Uint) <= sizeof(Eterm) */ -#if HALFWORD_HEAP -#define BIG_UWORD_HEAP_SIZE(UW) (((UW) >> (sizeof(Uint) * 8)) ? 3 : 2) -#else #define BIG_UWORD_HEAP_SIZE(UW) BIG_UINT_HEAP_SIZE -#endif -#if defined(ARCH_32) || HALFWORD_HEAP +#if defined(ARCH_32) #define ERTS_UINT64_BIG_HEAP_SIZE__(X) \ ((X) >= (((Uint64) 1) << 32) ? (1 + 2) : (1 + 1)) @@ -178,4 +174,3 @@ Eterm erts_sint64_to_big(Sint64, Eterm **); Eterm erts_chars_to_integer(Process *, char*, Uint, const int); #endif - diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 8849dadd00..94048f4c59 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -69,11 +69,7 @@ copy_object(Eterm obj, Process* to) * Return the "flat" size of the object. */ -#if HALFWORD_HEAP -Uint size_object_rel(Eterm obj, Eterm* base) -#else Uint size_object(Eterm obj) -#endif { Uint sum = 0; Eterm* ptr; @@ -227,12 +223,7 @@ Uint size_object(Eterm obj) /* * Copy a structure to a heap. */ -#if HALFWORD_HEAP -Eterm copy_struct_rel(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, - Eterm* src_base, Eterm* dst_base) -#else Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) -#endif { char* hstart; Uint hsize; @@ -287,13 +278,10 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) break; case TAG_PRIMARY_LIST: objp = list_val_rel(obj,src_base); - #if !HALFWORD_HEAP || defined(DEBUG) if (in_area(objp,hstart,hsize)) { - ASSERT(!HALFWORD_HEAP); hp++; break; } - #endif argp = hp++; /* Fall through */ @@ -309,17 +297,9 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } else { CAR(htop) = elem; - #if HALFWORD_HEAP - CDR(htop) = CDR(objp); - *tailp = make_list_rel(htop,dst_base); - htop += 2; - goto L_copy; - #else tailp = &CDR(htop); htop += 2; - #endif } - ASSERT(!HALFWORD_HEAP || tp < hp || tp >= hbot); *tp = make_list_rel(tailp - 1, dst_base); obj = CDR(objp); if (!is_list(obj)) { @@ -337,13 +317,10 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } case TAG_PRIMARY_BOXED: - #if !HALFWORD_HEAP || defined(DEBUG) if (in_area(boxed_val_rel(obj,src_base),hstart,hsize)) { - ASSERT(!HALFWORD_HEAP); hp++; break; } - #endif argp = hp++; L_copy_boxed: @@ -563,21 +540,12 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) * * NOTE: Assumes that term is a tuple (ptr is an untagged tuple ptr). */ -#if HALFWORD_HEAP -Eterm copy_shallow_rel(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, - Eterm* src_base) -#else Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) -#endif { Eterm* tp = ptr; Eterm* hp = *hpp; const Eterm res = make_tuple(hp); -#if HALFWORD_HEAP - const Sint offs = COMPRESS_POINTER(hp - (tp - src_base)); -#else const Sint offs = (hp - tp) * sizeof(Eterm); -#endif while (sz--) { Eterm val = *tp++; diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 23897a49ae..efd5109269 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -732,19 +732,11 @@ Eterm erts_dsend_export_trap_context(Process* p, ErtsSendContext* ctx) Binary* ctx_bin = erts_create_magic_binary(sizeof(struct exported_ctx), erts_dsend_context_dtor); struct exported_ctx* dst = ERTS_MAGIC_BIN_DATA(ctx_bin); - Uint ctl_size = !HALFWORD_HEAP ? 0 : (arityval(ctx->ctl_heap[0]) + 1); - Eterm* hp = HAlloc(p, ctl_size + PROC_BIN_SIZE); + Eterm* hp = HAlloc(p, PROC_BIN_SIZE); sys_memcpy(&dst->ctx, ctx, sizeof(ErtsSendContext)); ASSERT(ctx->dss.ctl == make_tuple(ctx->ctl_heap)); -#if !HALFWORD_HEAP dst->ctx.dss.ctl = make_tuple(dst->ctx.ctl_heap); -#else - /* Must put control tuple in low mem */ - sys_memcpy(hp, ctx->ctl_heap, ctl_size*sizeof(Eterm)); - dst->ctx.dss.ctl = make_tuple(hp); - hp += ctl_size; -#endif if (ctx->dss.acmp) { sys_memcpy(&dst->acm, ctx->dss.acmp, sizeof(ErtsAtomCacheMap)); dst->ctx.dss.acmp = &dst->acm; @@ -2061,9 +2053,9 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf) } -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) #define ERTS_PORT_REDS_MASK__ 0x003fffffffffffffL -#elif defined(ARCH_32) || HALFWORD_HEAP +#elif defined(ARCH_32) #define ERTS_PORT_REDS_MASK__ 0x003fffff #else # error "Ohh come on ... !?!" diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 55c164bf11..d68f22d573 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -123,10 +123,6 @@ typedef union { static ErtsAllocatorState_t std_alloc_state; static ErtsAllocatorState_t ll_alloc_state; -#if HALFWORD_HEAP -static ErtsAllocatorState_t std_low_alloc_state; -static ErtsAllocatorState_t ll_low_alloc_state; -#endif static ErtsAllocatorState_t sl_alloc_state; static ErtsAllocatorState_t temp_alloc_state; static ErtsAllocatorState_t eheap_alloc_state; @@ -150,24 +146,10 @@ typedef struct { #define ERTS_ALC_INFO_A_MSEG_ALLOC (ERTS_ALC_A_MAX + 2) #define ERTS_ALC_INFO_A_MAX ERTS_ALC_INFO_A_MSEG_ALLOC -#if !HALFWORD_HEAP ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(aireq, - ErtsAllocInfoReq, - 5, - ERTS_ALC_T_AINFO_REQ) -#else -static ERTS_INLINE ErtsAllocInfoReq * -aireq_alloc(void) -{ - return erts_alloc(ERTS_ALC_T_AINFO_REQ, sizeof(ErtsAllocInfoReq)); -} - -static ERTS_INLINE void -aireq_free(ErtsAllocInfoReq *ptr) -{ - erts_free(ERTS_ALC_T_AINFO_REQ, ptr); -} -#endif + ErtsAllocInfoReq, + 5, + ERTS_ALC_T_AINFO_REQ) ErtsAlcType_t erts_fix_core_allocator_ix; @@ -229,10 +211,6 @@ typedef struct { struct au_init ets_alloc; struct au_init driver_alloc; struct au_init fix_alloc; -#if HALFWORD_HEAP - struct au_init std_low_alloc; - struct au_init ll_low_alloc; -#endif } erts_alc_hndl_args_init_t; #define ERTS_AU_INIT__ {0, 0, 1, GOODFIT, DEFAULT_ALLCTR_INIT, {1,1,1,1}} @@ -259,10 +237,6 @@ set_default_sl_alloc_opts(struct au_init *ip) #endif ip->init.util.ts = ERTS_ALC_MTA_SHORT_LIVED; ip->init.util.rsbcst = 80; -#if HALFWORD_HEAP - ip->init.util.force = 1; - ip->init.util.low_mem = 1; -#endif ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL; } @@ -328,10 +302,6 @@ set_default_temp_alloc_opts(struct au_init *ip) ip->init.util.ts = ERTS_ALC_MTA_TEMPORARY; ip->init.util.rsbcst = 90; ip->init.util.rmbcmt = 100; -#if HALFWORD_HEAP - ip->init.util.force = 1; - ip->init.util.low_mem = 1; -#endif } static void @@ -350,10 +320,6 @@ set_default_eheap_alloc_opts(struct au_init *ip) #endif ip->init.util.ts = ERTS_ALC_MTA_EHEAP; ip->init.util.rsbcst = 50; -#if HALFWORD_HEAP - ip->init.util.force = 1; - ip->init.util.low_mem = 1; -#endif ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL_EHEAP_ALLOC; } @@ -560,12 +526,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_PROC)] = sizeof(Process); -#if !HALFWORD_HEAP fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MONITOR_SH)] = ERTS_MONITOR_SH_SIZE * sizeof(Uint); fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NLINK_SH)] = ERTS_LINK_SH_SIZE * sizeof(Uint); -#endif fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_DRV_EV_D_STATE)] = sizeof(ErtsDrvEventDataState); fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_DRV_SEL_D_STATE)] @@ -745,24 +709,6 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) erts_allctrs[ERTS_ALC_A_SYSTEM].free = erts_sys_free; erts_allctrs_info[ERTS_ALC_A_SYSTEM].enabled = 1; -#if HALFWORD_HEAP - /* Init low memory variants by cloning */ - init.std_low_alloc = init.std_alloc; - init.std_low_alloc.init.util.name_prefix = "std_low_"; - init.std_low_alloc.init.util.alloc_no = ERTS_ALC_A_STANDARD_LOW; - init.std_low_alloc.init.util.force = 1; - init.std_low_alloc.init.util.low_mem = 1; - - init.ll_low_alloc = init.ll_alloc; - init.ll_low_alloc.init.util.name_prefix = "ll_low_"; - init.ll_low_alloc.init.util.alloc_no = ERTS_ALC_A_LONG_LIVED_LOW; - init.ll_low_alloc.init.util.force = 1; - init.ll_low_alloc.init.util.low_mem = 1; - - set_au_allocator(ERTS_ALC_A_STANDARD_LOW, &init.std_low_alloc, ncpu); - set_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW, &init.ll_low_alloc, ncpu); -#endif /* HALFWORD */ - set_au_allocator(ERTS_ALC_A_TEMPORARY, &init.temp_alloc, ncpu); set_au_allocator(ERTS_ALC_A_SHORT_LIVED, &init.sl_alloc, ncpu); set_au_allocator(ERTS_ALC_A_STANDARD, &init.std_alloc, ncpu); @@ -805,14 +751,6 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) start_au_allocator(ERTS_ALC_A_LONG_LIVED, &init.ll_alloc, &ll_alloc_state); -#if HALFWORD_HEAP - start_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW, - &init.ll_low_alloc, - &ll_low_alloc_state); - start_au_allocator(ERTS_ALC_A_STANDARD_LOW, - &init.std_low_alloc, - &std_low_alloc_state); -#endif start_au_allocator(ERTS_ALC_A_EHEAP, &init.eheap_alloc, &eheap_alloc_state); @@ -836,9 +774,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) erts_mtrace_install_wrapper_functions(); extra_block_size += erts_instr_init(init.instr.stat, init.instr.map); -#if !HALFWORD_HEAP init_aireq_alloc(); -#endif #ifdef DEBUG extra_block_size += install_debug_functions(); @@ -1238,9 +1174,6 @@ get_acul_value(struct au_init *auip, char *param_end, char** argv, int* ip) if (sys_strcmp(value, "de") == 0) { switch (auip->init.util.alloc_no) { case ERTS_ALC_A_LONG_LIVED: -#if HALFWORD_HEAP - case ERTS_ALC_A_LONG_LIVED_LOW: -#endif return ERTS_ALC_DEFAULT_ENABLED_ACUL_LL_ALLOC; case ERTS_ALC_A_EHEAP: return ERTS_ALC_DEFAULT_ENABLED_ACUL_EHEAP_ALLOC; @@ -1957,48 +1890,6 @@ alcu_size(ErtsAlcType_t ai, ErtsAlcUFixInfo_t *fi, int fisz) return res; } -#if HALFWORD_HEAP -static ERTS_INLINE int -alcu_is_low(ErtsAlcType_t ai) -{ - int is_low = 0; - ASSERT(erts_allctrs_info[ai].enabled); - ASSERT(erts_allctrs_info[ai].alloc_util); - - if (!erts_allctrs_info[ai].thr_spec) { - Allctr_t *allctr = erts_allctrs_info[ai].extra; - is_low = allctr->mseg_opt.low_mem; - } - else { - ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[ai]; - int i; -# ifdef DEBUG - int found_one = 0; -# endif - - ASSERT(tspec->enabled); - - for (i = tspec->size - 1; i >= 0; i--) { - Allctr_t *allctr = tspec->allctr[i]; - if (allctr) { -# ifdef DEBUG - if (!found_one) { - is_low = allctr->mseg_opt.low_mem; - found_one = 1; - } - else ASSERT(is_low == allctr->mseg_opt.low_mem); -# else - is_low = allctr->mseg_opt.low_mem; - break; -# endif - } - } - ASSERT(found_one); - } - return is_low; -} -#endif /* HALFWORD */ - static ERTS_INLINE void add_fix_values(UWord *ap, UWord *up, ErtsAlcUFixInfo_t *fi, ErtsAlcType_t type) { @@ -2028,9 +1919,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) int code; int ets; int maximum; -#if HALFWORD_HEAP - int low; -#endif } want = {0}; struct { UWord total; @@ -2043,9 +1931,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) UWord code; UWord ets; UWord maximum; -#if HALFWORD_HEAP - UWord low; -#endif } size = {0}; Eterm atoms[sizeof(size)/sizeof(UWord)]; UWord *uintps[sizeof(size)/sizeof(UWord)]; @@ -2104,11 +1989,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) atoms[length] = am_maximum; uintps[length++] = &size.maximum; } -#if HALFWORD_HEAP - want.low = 1; - atoms[length] = am_low; - uintps[length++] = &size.low; -#endif } else { DeclareTmpHeapNoproc(tmp_heap,2); @@ -2202,15 +2082,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) return am_badarg; } break; -#if HALFWORD_HEAP - case am_low: - if (!want.low) { - want.low = 1; - atoms[length] = am_low; - uintps[length++] = &size.low; - } - break; -#endif default: UnUseTmpHeapNoproc(2); return am_badarg; @@ -2288,11 +2159,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) if (save) *save = asz; size.total += asz; -#if HALFWORD_HEAP - if (alcu_is_low(ai)) { - size.low += asz; - } -#endif } } } @@ -2319,7 +2185,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) &size.processes_used, fi, ERTS_ALC_T_PROC); -#if !HALFWORD_HEAP add_fix_values(&size.processes, &size.processes_used, fi, @@ -2329,7 +2194,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) &size.processes_used, fi, ERTS_ALC_T_NLINK_SH); -#endif add_fix_values(&size.processes, &size.processes_used, fi, diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 236ee35d18..db4c30b9eb 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -2074,7 +2074,7 @@ mbc_alloc_block(Allctr_t *allctr, Uint size, Uint *blk_szp) if (!blk) { blk = create_carrier(allctr, get_blk_sz, CFLG_MBC); -#if !HALFWORD_HEAP && !ERTS_SUPER_ALIGNED_MSEG_ONLY +#if !ERTS_SUPER_ALIGNED_MSEG_ONLY if (!blk) { /* Emergency! We couldn't create the carrier as we wanted. Try to place it in a sys_alloced sbc. */ @@ -3511,8 +3511,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) int is_mseg = 0; #endif - if (HALFWORD_HEAP - || (ERTS_SUPER_ALIGNED_MSEG_ONLY && (flags & CFLG_MBC)) + if ((ERTS_SUPER_ALIGNED_MSEG_ONLY && (flags & CFLG_MBC)) || !allow_sys_alloc_carriers) { flags |= CFLG_FORCE_MSEG; flags &= ~CFLG_FORCE_SYS_ALLOC; @@ -3749,11 +3748,6 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) DEBUG_SAVE_ALIGNMENT(new_crr); return new_blk; } -#if HALFWORD_HEAP - /* Old carrier unchanged; restore stat */ - STAT_MSEG_SBC_ALLOC(allctr, old_crr_sz, old_blk_sz); - return NULL; -#endif create_flags |= CFLG_FORCE_SYS_ALLOC; /* since mseg_realloc() failed */ } @@ -3942,9 +3936,6 @@ static struct { Eterm e; Eterm t; Eterm ramv; -#if HALFWORD_HEAP - Eterm low; -#endif Eterm sbct; #if HAVE_ERTS_MSEG Eterm asbcst; @@ -4035,9 +4026,6 @@ init_atoms(Allctr_t *allctr) AM_INIT(e); AM_INIT(t); AM_INIT(ramv); -#if HALFWORD_HEAP - AM_INIT(low); -#endif AM_INIT(sbct); #if HAVE_ERTS_MSEG AM_INIT(asbcst); @@ -4643,9 +4631,6 @@ info_options(Allctr_t *allctr, "option e: true\n" "option t: %s\n" "option ramv: %s\n" -#if HALFWORD_HEAP - "option low: %s\n" -#endif "option sbct: %beu\n" #if HAVE_ERTS_MSEG "option asbcst: %bpu\n" @@ -4664,9 +4649,6 @@ info_options(Allctr_t *allctr, "option acul: %d\n", topt, allctr->ramv ? "true" : "false", -#if HALFWORD_HEAP - allctr->mseg_opt.low_mem ? "true" : "false", -#endif allctr->sbc_threshold, #if HAVE_ERTS_MSEG allctr->mseg_opt.abs_shrink_th, @@ -4729,9 +4711,6 @@ info_options(Allctr_t *allctr, add_2tup(hpp, szp, &res, am.sbct, bld_uint(hpp, szp, allctr->sbc_threshold)); -#if HALFWORD_HEAP - add_2tup(hpp, szp, &res, am.low, allctr->mseg_opt.low_mem ? am_true : am_false); -#endif add_2tup(hpp, szp, &res, am.ramv, allctr->ramv ? am_true : am_false); add_2tup(hpp, szp, &res, am.t, (allctr->t ? am_true : am_false)); add_2tup(hpp, szp, &res, am.e, am_true); @@ -5416,11 +5395,7 @@ do_erts_alcu_realloc(ErtsAlcType_t type, Block_t *new_blk; if(IS_SBC_BLK(blk)) { do_carrier_resize: -#if HALFWORD_HEAP - new_blk = resize_carrier(allctr, blk, size, CFLG_SBC | CFLG_FORCE_MSEG); -#else new_blk = resize_carrier(allctr, blk, size, CFLG_SBC); -#endif res = new_blk ? BLK2UMEM(new_blk) : NULL; } else if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE) @@ -5714,11 +5689,8 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) #ifdef ERTS_SMP if (init->tspec || init->tpref) allctr->mseg_opt.sched_spec = 1; -#endif -# if HALFWORD_HEAP - allctr->mseg_opt.low_mem = init->low_mem; -# endif -#endif +#endif /* ERTS_SMP */ +#endif /* HAVE_ERTS_MSEG */ allctr->name_prefix = init->name_prefix; if (!allctr->name_prefix) @@ -5875,7 +5847,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) CFLG_MBC | CFLG_FORCE_SIZE | CFLG_NO_CPOOL -#if !HALFWORD_HEAP && !ERTS_SUPER_ALIGNED_MSEG_ONLY +#if !ERTS_SUPER_ALIGNED_MSEG_ONLY | CFLG_FORCE_SYS_ALLOC #endif | CFLG_MAIN_CARRIER); diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index df1f0aa65a..792f8c63ac 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -45,7 +45,6 @@ typedef struct { int tspec; int tpref; int ramv; - int low_mem; /* HALFWORD only */ UWord sbct; UWord asbcst; UWord rsbcst; @@ -90,7 +89,6 @@ typedef struct { 0, /* (bool) tspec: thread specific */\ 0, /* (bool) tpref: thread preferred */\ 0, /* (bool) ramv: realloc always moves */\ - 0, /* (bool) low_mem: HALFWORD only */\ 512*1024, /* (bytes) sbct: sbc threshold */\ 2*1024*2024, /* (amount) asbcst: abs sbc shrink threshold */\ 20, /* (%) rsbcst: rel sbc shrink threshold */\ @@ -125,7 +123,6 @@ typedef struct { 0, /* (bool) tspec: thread specific */\ 0, /* (bool) tpref: thread preferred */\ 0, /* (bool) ramv: realloc always moves */\ - 0, /* (bool) low_mem: HALFWORD only */\ 64*1024, /* (bytes) sbct: sbc threshold */\ 2*1024*2024, /* (amount) asbcst: abs sbc shrink threshold */\ 20, /* (%) rsbcst: rel sbc shrink threshold */\ diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 134aa2d396..f834802764 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -2550,7 +2550,6 @@ BIF_RETTYPE binary_referenced_byte_size_1(BIF_ALIST_1) } pb = (ProcBin *) binary_val(bin); if (pb->thing_word == HEADER_PROC_BIN) { - /* XXX:PaN - Halfword - orig_size is a long, we should handle that */ res = erts_make_integer((Uint) pb->val->orig_size, BIF_P); } else { /* heap binary */ res = erts_make_integer((Uint) ((ErlHeapBin *) pb)->size, BIF_P); @@ -2568,7 +2567,7 @@ BIF_RETTYPE binary_referenced_byte_size_1(BIF_ALIST_1) #endif static int get_need(Uint u) { -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) if (u > 0xFFFFFFFFUL) { if (u > 0xFFFFFFFFFFFFUL) { if (u > 0xFFFFFFFFFFFFFFUL) { diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b44382cde8..a3ff537aa1 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -73,7 +73,7 @@ static char otp_version[] = ERLANG_OTP_VERSION; static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE "%s" " [erts-" ERLANG_VERSION "]" -#if !HEAP_ON_C_STACK && !HALFWORD_HEAP +#if !HEAP_ON_C_STACK " [no-c-stack-objects]" #endif #ifndef OTP_RELEASE @@ -84,12 +84,8 @@ static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE #endif #endif #ifdef ARCH_64 -#if HALFWORD_HEAP - " [64-bit halfword]" -#else " [64-bit]" #endif -#endif #ifdef ERTS_SMP " [smp:%beu:%beu]" #endif diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index 86951f32b0..7f7cd376ac 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -100,7 +100,7 @@ Sint erts_re_set_loop_limit(Sint limit) static int term_to_int(Eterm term, int *sp) { -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) if (is_small(term)) { Uint x = signed_val(term); @@ -151,7 +151,7 @@ static int term_to_int(Eterm term, int *sp) static Eterm make_signed_integer(int x, Process *p) { -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) return make_small(x); #else Eterm* hp; diff --git a/erts/emulator/beam/erl_bif_unique.c b/erts/emulator/beam/erl_bif_unique.c index 5eca09c5a6..c4a39b8897 100644 --- a/erts/emulator/beam/erl_bif_unique.c +++ b/erts/emulator/beam/erl_bif_unique.c @@ -338,7 +338,7 @@ static struct { } w; } raw_unique_monotonic_integer erts_align_attribute(ERTS_CACHE_LINE_SIZE); -#if defined(ARCH_32) || HALFWORD_HEAP +#if defined(ARCH_32) # define ERTS_UNIQUE_MONOTONIC_OFFSET ERTS_SINT64_MIN #else # define ERTS_UNIQUE_MONOTONIC_OFFSET MIN_SMALL @@ -368,7 +368,7 @@ get_unique_monotonic_integer_heap_size(Uint64 raw, int positive) Sint64 value = ((Sint64) raw) + ERTS_UNIQUE_MONOTONIC_OFFSET; if (IS_SSMALL(value)) return 0; -#if defined(ARCH_32) || HALFWORD_HEAP +#if defined(ARCH_32) return ERTS_SINT64_HEAP_SIZE(value); #else return ERTS_UINT64_HEAP_SIZE((Uint64) value); @@ -393,7 +393,7 @@ make_unique_monotonic_integer_value(Eterm *hp, Uint hsz, Uint64 raw, int positiv if (hsz == 0) res = make_small(value); else { -#if defined(ARCH_32) || HALFWORD_HEAP +#if defined(ARCH_32) res = erts_sint64_to_big(value, &hp); #else res = erts_uint64_to_big((Uint64) value, &hp); diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 01734c55d7..11d83686a3 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -282,7 +282,7 @@ erts_bs_get_integer_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuff * Simply shift whole bytes into the result. */ switch (BYTE_OFFSET(n)) { -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) case 7: w = (w << 8) | *bp++; case 6: w = (w << 8) | *bp++; case 5: w = (w << 8) | *bp++; @@ -387,7 +387,7 @@ erts_bs_get_integer_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuff case 3: v32 = LSB[0] + (LSB[1]<<8) + (LSB[2]<<16); goto big_small; -#if !defined(ARCH_64) || HALFWORD_HEAP +#if !defined(ARCH_64) case 4: v32 = (LSB[0] + (LSB[1]<<8) + (LSB[2]<<16) + (LSB[3]<<24)); if (!IS_USMALL(sgn, v32)) { diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 878ee32b47..f3da28b65a 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -1774,15 +1774,9 @@ BIF_RETTYPE ets_delete_1(BIF_ALIST_1) * (it looks like an continuation pointer), but that is will crash the * emulator if this BIF is call traced. */ -#if HALFWORD_HEAP - Eterm *hp = HAlloc(BIF_P, 3); - hp[0] = make_pos_bignum_header(2); - *((UWord *) (UWord) (hp+1)) = (UWord) tb; -#else Eterm *hp = HAlloc(BIF_P, 2); hp[0] = make_pos_bignum_header(1); hp[1] = (Eterm) tb; -#endif BIF_TRAP1(&ets_delete_continue_exp, BIF_P, make_big(hp)); } else { @@ -3652,11 +3646,8 @@ static BIF_RETTYPE ets_delete_trap(BIF_ALIST_1) Eterm* ptr = big_val(cont); DbTable *tb = *((DbTable **) (UWord) (ptr + 1)); -#if HALFWORD_HEAP - ASSERT(*ptr == make_pos_bignum_header(2)); -#else ASSERT(*ptr == make_pos_bignum_header(1)); -#endif + db_lock(tb, LCK_WRITE); trap = free_table_cont(p, tb, 0, 1); db_unlock(tb, LCK_WRITE); diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 81b0c4465c..fc1aa05ed2 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -2885,9 +2885,6 @@ Ldone: handle->dbterm = &b->dbterm; handle->flags = flags; handle->new_size = b->dbterm.size; -#if HALFWORD_HEAP - handle->abs_vec = NULL; -#endif handle->lck = lck; return 1; } diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 465aa566ad..c21eac6f25 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -2589,9 +2589,6 @@ db_lookup_dbterm_tree(Process *p, DbTable *tbl, Eterm key, Eterm obj, handle->flags = flags; handle->bp = (void**) pp; handle->new_size = (*pp)->dbterm.size; -#if HALFWORD_HEAP - handle->abs_vec = NULL; -#endif return 1; } diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index dab357a079..0086aa8121 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -239,11 +239,7 @@ typedef enum { matchCall2, matchCall3, matchPushV, -#if HALFWORD_HEAP - matchPushVGuard, /* First guard-only variable reference */ -#endif - matchPushVResult, /* First variable reference in result, or (if HALFWORD) - in guard if also referenced in result */ + matchPushVResult, /* First variable reference in result */ matchPushExpr, /* Push the whole expression we're matching ('$_') */ matchPushArrayAsList, /* Only when parameter is an Array and not an erlang term (DCOMP_TRACE) */ @@ -310,9 +306,6 @@ DMC_DECLARE_STACK_TYPE(unsigned); typedef struct DMCVariable { int is_bound; int is_in_body; -#if HALFWORD_HEAP - int first_guard_label; /* to maybe change from PushVGuard to PushVResult */ -#endif } DMCVariable; typedef struct DMCHeap { @@ -415,7 +408,7 @@ cleanup_match_pseudo_process(ErtsMatchPseudoProcess *mpsp, int keep_heap) else { int i; for (i = 0; i < ERTS_DEFAULT_MS_HEAP_SIZE; i++) { -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) mpsp->default_heap[i] = (Eterm) 0xdeadbeefdeadbeef; #else mpsp->default_heap[i] = (Eterm) 0xdeadbeef; @@ -1755,60 +1748,6 @@ static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity) return ret; } - -#if HALFWORD_HEAP -struct heap_checkpoint_t -{ - Process *p; - Eterm* htop; - ErlHeapFragment* mbuf; - unsigned used_size; - ErlOffHeap off_heap; -}; - -static void heap_checkpoint_init(Process* p, struct heap_checkpoint_t* hcp) -{ - hcp->p = p; - hcp->htop = HEAP_TOP(p); - hcp->mbuf = MBUF(p); - hcp->used_size = hcp->mbuf ? hcp->mbuf->used_size : 0; - hcp->off_heap = MSO(p); -} - -static void heap_checkpoint_revert(struct heap_checkpoint_t* hcp) -{ - struct erl_off_heap_header* oh = MSO(hcp->p).first; - - if (oh != hcp->off_heap.first) { - ASSERT(oh != NULL); - if (hcp->off_heap.first) { - while (oh->next != hcp->off_heap.first) { - oh = oh->next; - } - oh->next = NULL; - } - erts_cleanup_offheap(&MSO(hcp->p)); - MSO(hcp->p) = hcp->off_heap; - } - if (MBUF(hcp->p) != hcp->mbuf) { - ErlHeapFragment* hf = MBUF(hcp->p); - ASSERT(hf != NULL); - if (hcp->mbuf) { - while (hf->next != hcp->mbuf) { - hf = hf->next; - } - hf->next = NULL; - } - free_message_buffer(MBUF(hcp->p)); - MBUF(hcp->p) = hcp->mbuf; - } - if (hcp->mbuf != NULL && hcp->mbuf->used_size != hcp->used_size) { - hcp->mbuf->used_size = hcp->used_size; - } - HEAP_TOP(hcp->p) = hcp->htop; -} -#endif /* HALFWORD_HEAP */ - static ERTS_INLINE Eterm copy_object_rel(Process* p, Eterm term, Eterm* base) { if (!is_immed(term)) { @@ -1855,17 +1794,12 @@ Eterm db_prog_match(Process *c_p, Binary *bprog, Eterm bif_args[3]; int fail_label; int atomic_trace; -#if HALFWORD_HEAP - struct heap_checkpoint_t c_p_checkpoint = {}; -#endif #ifdef DMC_DEBUG Uint *heap_fence; Uint *stack_fence; Uint save_op; #endif /* DMC_DEBUG */ - ASSERT(base==NULL || HALFWORD_HEAP); - mpsp = get_match_pseudo_process(c_p, prog->heap_size); psp = &mpsp->process; @@ -1916,11 +1850,7 @@ Eterm db_prog_match(Process *c_p, Binary *bprog, do_catch != 0 */ *return_flags = 0U; - variables = mpsp->u.variables; -#if HALFWORD_HEAP - c_p_checkpoint.p = NULL; -#endif restart: ep = &term; @@ -1931,7 +1861,6 @@ restart: fail_label = -1; build_proc = psp; esdp->current_process = psp; - ASSERT_HALFWORD(!c_p_checkpoint.p); #ifdef DEBUG ASSERT(variables == mpsp->u.variables); @@ -2236,31 +2165,10 @@ restart: esp -= 2; esp[-1] = t; break; - - #if HALFWORD_HEAP - case matchPushVGuard: - if (!base) goto case_matchPushV; - /* Build NULL-based copy on pseudo heap for easy disposal */ - n = *pc++; - ASSERT(is_value(variables[n].term)); - ASSERT(!variables[n].proc); - variables[n].term = copy_object_rel(psp, variables[n].term, base); - *esp++ = variables[n].term; - #ifdef DEBUG - variables[n].proc = psp; - variables[n].base = NULL; - #endif - break; - #endif case matchPushVResult: if (!(in_flags & ERTS_PAM_COPY_RESULT)) goto case_matchPushV; /* Build (NULL-based) copy on callers heap */ - #if HALFWORD_HEAP - if (!do_catch && !c_p_checkpoint.p) { - heap_checkpoint_init(c_p, &c_p_checkpoint); - } - #endif n = *pc++; ASSERT(is_value(variables[n].term)); ASSERT(!variables[n].proc); @@ -2299,7 +2207,6 @@ restart: } break; case matchPushArrayAsList: - ASSERT_HALFWORD(base == NULL); n = arity; /* Only happens when 'term' is an array */ tp = termp; ehp = HAllocX(build_proc, n*2, HEAP_XTRA); @@ -2315,7 +2222,6 @@ restart: break; case matchPushArrayAsListU: /* This instruction is NOT efficient. */ - ASSERT_HALFWORD(base == NULL); *esp++ = dpm_array_to_list(build_proc, termp, arity); break; case matchTrue: @@ -2619,13 +2525,6 @@ restart: } } fail: -#if HALFWORD_HEAP - if (c_p_checkpoint.p) { - /* Dispose garbage built by guards on caller heap */ - heap_checkpoint_revert(&c_p_checkpoint); - c_p_checkpoint.p = NULL; - } -#endif *return_flags = 0U; if (fail_label >= 0) { /* We failed during a "TryMeElse", lets restart, with the next match @@ -2788,13 +2687,6 @@ Wterm db_do_read_element(DbUpdateHandle* handle, Sint position) { Eterm elem = handle->dbterm->tpl[position]; if (!is_header(elem)) { -#if HALFWORD_HEAP - if (!is_immed(elem) - && !handle->tb->common.compress - && !(handle->abs_vec && handle->abs_vec[position])) { - return rterm2wterm(elem, handle->dbterm->tpl); - } -#endif return elem; } @@ -2822,9 +2714,6 @@ void db_do_update_element(DbUpdateHandle* handle, Eterm* oldp; Uint newval_sz; Uint oldval_sz; -#if HALFWORD_HEAP - Eterm* old_base; -#endif if (is_both_immed(newval,oldval)) { handle->dbterm->tpl[position] = newval; @@ -2841,15 +2730,8 @@ void db_do_update_element(DbUpdateHandle* handle, handle->dbterm); handle->flags |= DB_MUST_RESIZE; oldval = handle->dbterm->tpl[position]; - #if HALFWORD_HEAP - old_base = NULL; - #endif } else { - #if HALFWORD_HEAP - ASSERT(!handle->abs_vec); - old_base = handle->dbterm->tpl; - #endif if (is_boxed(newval)) { newp = boxed_val(newval); switch (*newp & _TAG_HEADER_MASK) { @@ -2879,13 +2761,6 @@ void db_do_update_element(DbUpdateHandle* handle, } } } -#if HALFWORD_HEAP - else { - old_base = (handle->tb->common.compress - || (handle->abs_vec && handle->abs_vec[position])) ? - NULL : handle->dbterm->tpl; - } -#endif /* Not possible for simple memcpy or dbterm is already non-contiguous, */ /* need to realloc... */ @@ -2900,19 +2775,6 @@ both_size_set: /* write new value in old dbterm, finalize will make a flat copy */ handle->dbterm->tpl[position] = newval; handle->flags |= DB_MUST_RESIZE; - -#if HALFWORD_HEAP - if (old_base && newval_sz > 0) { - ASSERT(!handle->tb->common.compress); - if (!handle->abs_vec) { - int i = header_arity(handle->dbterm->tpl[0]); - handle->abs_vec = erts_alloc(ERTS_ALC_T_TMP, (i+1)*sizeof(char)); - sys_memset(handle->abs_vec, 0, i+1); - /* abs_vec[0] not used */ - } - handle->abs_vec[position] = 1; - } -#endif } static ERTS_INLINE byte* db_realloc_term(DbTableCommon* tb, void* old, @@ -3160,26 +3022,6 @@ void db_finalize_resize(DbUpdateHandle* handle, Uint offset) tmp_offheap.first = NULL; - #if HALFWORD_HEAP - if (handle->abs_vec) { - int i, arity = header_arity(handle->dbterm->tpl[0]); - - top[0] = tpl[0]; - top += arity + 1; - for (i=1; i<=arity; i++) { - Eterm* src_base = handle->abs_vec[i] ? NULL : tpl; - - newDbTerm->tpl[i] = copy_struct_rel(tpl[i], - size_object_rel(tpl[i],src_base), - &top, &tmp_offheap, src_base, - newDbTerm->tpl); - } - newDbTerm->first_oh = tmp_offheap.first; - ASSERT((byte*)top <= (newp + alloc_sz)); - erts_free(ERTS_ALC_T_TMP, handle->abs_vec); - } - else - #endif /* HALFWORD_HEAP */ { copy_struct_rel(make_tuple_rel(tpl,tpl), handle->new_size, &top, &tmp_offheap, tpl, top); @@ -3599,26 +3441,10 @@ static DMCRet dmc_one_term(DMCContext *context, { Eterm* ref_val = internal_ref_val(c); DMC_PUSH(*text, matchEqRef); -#if HALFWORD_HEAP - { - union { - UWord u; - Uint t[2]; - } fiddle; - ASSERT(thing_arityval(ref_val[0]) == 3); - fiddle.t[0] = ref_val[0]; - fiddle.t[1] = ref_val[1]; - DMC_PUSH(*text, fiddle.u); - fiddle.t[0] = ref_val[2]; - fiddle.t[1] = ref_val[3]; - DMC_PUSH(*text, fiddle.u); - } -#else n = thing_arityval(ref_val[0]); for (i = 0; i <= n; ++i) { DMC_PUSH(*text, ref_val[i]); } -#endif break; } case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): @@ -3627,52 +3453,18 @@ static DMCRet dmc_one_term(DMCContext *context, Eterm* bval = big_val(c); n = thing_arityval(bval[0]); DMC_PUSH(*text, matchEqBig); -#if HALFWORD_HEAP - { - union { - UWord u; - Uint t[2]; - } fiddle; - ASSERT(n >= 1); - fiddle.t[0] = bval[0]; - fiddle.t[1] = bval[1]; - DMC_PUSH(*text, fiddle.u); - for (i = 2; i <= n; ++i) { - fiddle.t[0] = bval[i]; - if (++i <= n) { - fiddle.t[1] = bval[i]; - } else { - fiddle.t[1] = (Uint) 0; - } - DMC_PUSH(*text, fiddle.u); - } - } -#else for (i = 0; i <= n; ++i) { DMC_PUSH(*text, (Uint) bval[i]); } -#endif break; } case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): DMC_PUSH(*text,matchEqFloat); -#if HALFWORD_HEAP - { - union { - UWord u; - Uint t[2]; - } fiddle; - fiddle.t[0] = float_val(c)[1]; - fiddle.t[1] = float_val(c)[2]; - DMC_PUSH(*text, fiddle.u); - } -#else DMC_PUSH(*text, (Uint) float_val(c)[1]); #ifdef ARCH_64 DMC_PUSH(*text, (Uint) 0); #else DMC_PUSH(*text, (Uint) float_val(c)[2]); -#endif #endif break; default: /* BINARY, FUN, VECTOR, or EXTERNAL */ @@ -3997,24 +3789,8 @@ static void dmc_add_pushv_variant(DMCContext *context, DMCHeap *heap, MatchOps instr = matchPushV; ASSERT(n < heap->vars_used && v->is_bound); - if (context->is_guard) { - #if HALFWORD_HEAP - if (!v->first_guard_label) { - v->first_guard_label = DMC_STACK_NUM(*text); - ASSERT(v->first_guard_label); - instr = matchPushVGuard; /* may be changed to PushVResult below */ - } - #endif - } - else { /* body */ - #if HALFWORD_HEAP - if (v->first_guard_label) { - /* Avoid double-copy, copy to result heap at first encounter in guard */ - DMC_POKE(*text, v->first_guard_label, matchPushVResult); - v->is_in_body = 1; - } - #endif - if (!v->is_in_body) { + if (!context->is_guard) { + if(!v->is_in_body) { instr = matchPushVResult; v->is_in_body = 1; } @@ -5450,7 +5226,7 @@ Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog, obj = db_alloc_tmp_uncompressed(tb, obj); base = NULL; } - else base = HALFWORD_HEAP ? obj->tpl : NULL; + else base = NULL; res = db_prog_match(c_p, bprog, make_tuple_rel(obj->tpl,base), base, NULL, 0, ERTS_PAM_COPY_RESULT|ERTS_PAM_CONTIGUOUS_TUPLE, &dummy); @@ -5573,7 +5349,7 @@ void db_match_dis(Binary *bp) first = 0; else erts_printf(", "); -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) erts_printf("0x%016bex", rt->data.ui[ri]); #else erts_printf("0x%08bex", rt->data.ui[ri]); @@ -5597,7 +5373,7 @@ void db_match_dis(Binary *bp) first = 0; else erts_printf(", "); -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) erts_printf("0x%016bex", *et); #else erts_printf("0x%08bex", *et); @@ -5720,13 +5496,6 @@ void db_match_dis(Binary *bp) ++t; erts_printf("PushV\t%beu\n", n); break; - #if HALFWORD_HEAP - case matchPushVGuard: - n = (Uint) *++t; - ++t; - erts_printf("PushVGuard\t%beu\n", n); - break; - #endif case matchPushVResult: n = (Uint) *++t; ++t; diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 1ccdc0305b..c2128888c5 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -90,9 +90,6 @@ typedef struct { Uint new_size; int flags; void* lck; -#if HALFWORD_HEAP - unsigned char* abs_vec; /* [i] true if dbterm->tpl[i] is absolute Eterm */ -#endif } DbUpdateHandle; diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index d2604f1595..6c335a9fe6 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -128,7 +128,7 @@ static void disallow_heap_frag_ref_in_old_heap(Process* p); static void disallow_heap_frag_ref(Process* p, Eterm* n_htop, Eterm* objv, int nobj); #endif -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) # define MAX_HEAP_SIZES 154 #else # define MAX_HEAP_SIZES 59 @@ -147,26 +147,10 @@ typedef struct { erts_smp_atomic32_t refc; } ErtsGCInfoReq; -#if !HALFWORD_HEAP ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(gcireq, - ErtsGCInfoReq, - 5, - ERTS_ALC_T_GC_INFO_REQ) -#else -static ERTS_INLINE ErtsGCInfoReq * -gcireq_alloc(void) -{ - return erts_alloc(ERTS_ALC_T_GC_INFO_REQ, - sizeof(ErtsGCInfoReq)); -} - -static ERTS_INLINE void -gcireq_free(ErtsGCInfoReq *ptr) -{ - erts_free(ERTS_ALC_T_GC_INFO_REQ, ptr); -} -#endif - + ErtsGCInfoReq, + 5, + ERTS_ALC_T_GC_INFO_REQ) /* * Initialize GC global data. */ @@ -208,7 +192,7 @@ erts_init_gc(void) } - /* for 32 bit we want max_heap_size to be MAX(32bit) / 4 [words] (and halfword) + /* for 32 bit we want max_heap_size to be MAX(32bit) / 4 [words] * for 64 bit we want max_heap_size to be MAX(52bit) / 8 [words] */ @@ -232,10 +216,7 @@ erts_init_gc(void) init_gc_info(&esdp->gc_info); } -#if !HALFWORD_HEAP init_gcireq_alloc(); -#endif - } /* diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index d9c3b0dcf4..5c209a4af2 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -91,11 +91,6 @@ const int etp_arch_bits = 32; #else # error "Not 64-bit, nor 32-bit arch" #endif -#if HALFWORD_HEAP -const int etp_halfword = 1; -#else -const int etp_halfword = 0; -#endif #ifdef HIPE const int etp_hipe = 1; #else diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 3b3b247020..84bee976ff 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -156,9 +156,6 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "instr", NULL }, { "alcu_allocator", "index" }, { "mseg", NULL }, -#if HALFWORD_HEAP - { "pmmap", NULL }, -#endif #ifdef ERTS_SMP { "port_task_pre_alloc_lock", "address" }, { "proclist_pre_alloc_lock", "address" }, diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index a91e36e3c5..6d496ea5ee 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -170,11 +170,7 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { */ const Eterm * -#if HALFWORD_HEAP -erts_maps_get_rel(Eterm key, Eterm map, Eterm *map_base) -#else erts_maps_get(Eterm key, Eterm map) -#endif { Uint32 hx; if (is_flatmap_rel(map, map_base)) { @@ -1993,11 +1989,7 @@ Eterm* hashmap_iterator_prev(ErtsWStack* s) { } const Eterm * -#if HALFWORD_HEAP -erts_hashmap_get_rel(Uint32 hx, Eterm key, Eterm node, Eterm *map_base) -#else erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) -#endif { Eterm *ptr, hdr, *res; Uint ix, lvl = 0; diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index c391de3f11..2f7c55829f 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -105,22 +105,12 @@ Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint Eterm k, Eterm v); const Eterm * -#if HALFWORD_HEAP -erts_maps_get_rel(Eterm key, Eterm map, Eterm *map_base); -# define erts_maps_get(A, B) erts_maps_get_rel(A, B, NULL) -#else erts_maps_get(Eterm key, Eterm map); -# define erts_maps_get_rel(A, B, B_BASE) erts_maps_get(A, B) -#endif +#define erts_maps_get_rel(A, B, B_BASE) erts_maps_get(A, B) const Eterm * -#if HALFWORD_HEAP -erts_hashmap_get_rel(Uint32 hx, Eterm key, Eterm node, Eterm *map_base); -# define erts_hashmap_get(Hx, K, M) erts_hashmap_get_rel(Hx, K, M, NULL) -#else erts_hashmap_get(Uint32 hx, Eterm key, Eterm map); -# define erts_hashmap_get_rel(Hx, K, M, M_BASE) erts_hashmap_get(Hx, K, M) -#endif +#define erts_hashmap_get_rel(Hx, K, M, M_BASE) erts_hashmap_get(Hx, K, M) /* hamt nodes v2.0 * diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index fbdf3fb0e2..f37b430d27 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -32,9 +32,6 @@ struct external_thing_; struct erl_off_heap_header { Eterm thing_word; Uint size; -#if HALFWORD_HEAP - void* dummy_ptr_padding__; -#endif struct erl_off_heap_header* next; }; diff --git a/erts/emulator/beam/erl_node_container_utils.h b/erts/emulator/beam/erl_node_container_utils.h index 211b1a0090..0a4b18b748 100644 --- a/erts/emulator/beam/erl_node_container_utils.h +++ b/erts/emulator/beam/erl_node_container_utils.h @@ -255,7 +255,7 @@ extern ErtsPTab erts_port; * Refs * \* */ -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) #define internal_ref_no_of_numbers(x) \ (internal_ref_data((x))[0]) diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 2fb790b953..62a44f7129 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -1207,23 +1207,11 @@ insert_offheap(ErlOffHeap *oh, int type, Eterm id) break; } if (insert_bin) { -#if HALFWORD_HEAP - UWord val = (UWord) u.pb->val; - DeclareTmpHeapNoproc(id_heap,BIG_UINT_HEAP_SIZE*2); /* extra place allocated */ -#else DeclareTmpHeapNoproc(id_heap,BIG_UINT_HEAP_SIZE); -#endif Uint *hp = &id_heap[0]; InsertedBin *nib; -#if HALFWORD_HEAP - int actual_need = BIG_UWORD_HEAP_SIZE(val); - ASSERT(actual_need <= (BIG_UINT_HEAP_SIZE*2)); - UseTmpHeapNoproc(actual_need); - a.id = erts_bld_uword(&hp, NULL, (UWord) val); -#else UseTmpHeapNoproc(BIG_UINT_HEAP_SIZE); a.id = erts_bld_uint(&hp, NULL, (Uint) u.pb->val); -#endif erts_match_prog_foreach_offheap(u.pb->val, insert_offheap2, (void *) &a); @@ -1231,11 +1219,7 @@ insert_offheap(ErlOffHeap *oh, int type, Eterm id) nib->bin_val = u.pb->val; nib->next = inserted_bins; inserted_bins = nib; -#if HALFWORD_HEAP - UnUseTmpHeapNoproc(actual_need); -#else UnUseTmpHeapNoproc(BIG_UINT_HEAP_SIZE); -#endif } } break; diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index 694ac84232..64278d2ea0 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -66,7 +66,7 @@ #define ERTS_DE_QFLGS_ALL (ERTS_DE_QFLG_BUSY \ | ERTS_DE_QFLG_EXIT) -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) #define ERTS_DIST_OUTPUT_BUF_DBG_PATTERN ((Uint) 0xf713f713f713f713UL) #else #define ERTS_DIST_OUTPUT_BUF_DBG_PATTERN ((Uint) 0xf713f713) diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 267c0b3ff4..95b3572372 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -261,7 +261,7 @@ static char *format_binary(Uint16 x, char *b) { static int print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, - Eterm* obj_base) /* ignored if !HALFWORD_HEAP */ + Eterm* obj_base) { DECLARE_WSTACK(s); int res; @@ -344,11 +344,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, PRINT_CHAR(res, fn, arg, '>'); goto L_done; } -#if HALFWORD_HEAP - wobj = is_immed(obj) ? (Wterm)obj : rterm2wterm(obj, obj_base); -#else wobj = (Wterm)obj; -#endif switch (tag_val_def(wobj)) { case NIL_DEF: PRINT_STRING(res, fn, arg, "[]"); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 7b3d12ce09..a21c1e5582 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -119,7 +119,7 @@ #define RUNQ_SET_RQ(X, RQ) erts_smp_atomic_set_nob((X), (erts_aint_t) (RQ)) #ifdef DEBUG -# if defined(ARCH_64) && !HALFWORD_HEAP +# if defined(ARCH_64) # define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \ (RUNQ_SET_RQ((RQP), (0xdeadbeefdead0003LL | ((N) << 4))) # define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \ @@ -971,25 +971,10 @@ typedef struct { erts_smp_atomic32_t refc; } ErtsSchedWallTimeReq; -#if !HALFWORD_HEAP ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(swtreq, - ErtsSchedWallTimeReq, - 5, - ERTS_ALC_T_SCHED_WTIME_REQ) -#else -static ERTS_INLINE ErtsSchedWallTimeReq * -swtreq_alloc(void) -{ - return erts_alloc(ERTS_ALC_T_SCHED_WTIME_REQ, - sizeof(ErtsSchedWallTimeReq)); -} - -static ERTS_INLINE void -swtreq_free(ErtsSchedWallTimeReq *ptr) -{ - erts_free(ERTS_ALC_T_SCHED_WTIME_REQ, ptr); -} -#endif + ErtsSchedWallTimeReq, + 5, + ERTS_ALC_T_SCHED_WTIME_REQ) static void reply_sched_wall_time(void *vswtrp) @@ -5797,9 +5782,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online #endif init_misc_aux_work(); -#if !HALFWORD_HEAP init_swtreq_alloc(); -#endif erts_atomic32_init_nob(&debug_wait_completed_count, 0); /* debug only */ debug_wait_completed_flags = 0; @@ -8286,7 +8269,7 @@ add_pend_suspend(Process *suspendee, sizeof(ErtsPendingSuspend)); psp->next = NULL; #ifdef DEBUG -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) psp->end = (ErtsPendingSuspend *) 0xdeaddeaddeaddead; #else psp->end = (ErtsPendingSuspend *) 0xdeaddead; diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 90e35151b0..46900edfc7 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -21,33 +21,12 @@ #ifndef __ERL_TERM_H #define __ERL_TERM_H -#include "sys.h" /* defines HALFWORD_HEAP */ - typedef UWord Wterm; /* Full word terms */ -#if HALFWORD_HEAP -# define HEAP_ON_C_STACK 0 -# if HALFWORD_ASSERT -# ifdef ET_DEBUG -# undef ET_DEBUG -# endif -# define ET_DEBUG 1 -# endif -# if 1 -# define CHECK_POINTER_MASK 0xFFFFFFFF00000000UL -# define COMPRESS_POINTER(APointer) ((Eterm) (UWord) (APointer)) -# define EXPAND_POINTER(AnEterm) ((UWord) (AnEterm)) -# else -# define CHECK_POINTER_MASK 0x0UL -# define COMPRESS_POINTER(AnUint) (AnUint) -# define EXPAND_POINTER(APointer) (APointer) -# endif -#else -# define HEAP_ON_C_STACK 1 -# define CHECK_POINTER_MASK 0x0UL -# define COMPRESS_POINTER(AnUint) (AnUint) -# define EXPAND_POINTER(APointer) (APointer) -#endif +#define HEAP_ON_C_STACK 1 +#define CHECK_POINTER_MASK 0x0UL +#define COMPRESS_POINTER(AnUint) (AnUint) +#define EXPAND_POINTER(APointer) (APointer) struct erl_node_; /* Declared in erl_node_tables.h */ @@ -190,13 +169,10 @@ struct erl_node_; /* Declared in erl_node_tables.h */ /* boxed object access methods */ -#if HALFWORD_HEAP -#define _is_taggable_pointer(x) (((UWord)(x) & (CHECK_POINTER_MASK | 0x3)) == 0) -#define _boxed_precond(x) (is_boxed(x)) -#else + #define _is_taggable_pointer(x) (((Uint)(x) & 0x3) == 0) #define _boxed_precond(x) (is_boxed(x)) -#endif + #define _is_aligned(x) (((Uint)(x) & 0x3) == 0) #define _unchecked_make_boxed(x) ((Uint) COMPRESS_POINTER(x) + TAG_PRIMARY_BOXED) _ET_DECLARE_CHECKED(Eterm,make_boxed,const Eterm*) @@ -226,11 +202,7 @@ _ET_DECLARE_CHECKED(int,is_not_list,Eterm) #define is_list(x) (((x) & _TAG_PRIMARY_MASK) == TAG_PRIMARY_LIST) #define is_not_list(x) (!is_list((x))) #endif -#if HALFWORD_HEAP #define _list_precond(x) (is_list(x)) -#else -#define _list_precond(x) (is_list(x)) -#endif #define _unchecked_list_val(x) ((Eterm*) EXPAND_POINTER((x) - TAG_PRIMARY_LIST)) _ET_DECLARE_CHECKED(Eterm*,list_val,Wterm) #define list_val(x) _ET_APPLY(list_val,(x)) @@ -250,7 +222,7 @@ _ET_DECLARE_CHECKED(Eterm*,list_val,Wterm) #define byte_offset_ptr(x,offs) _unchecked_byte_offset_ptr(x,offs) /*XXX*/ /* fixnum ("small") access methods */ -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) #define SMALL_BITS (64-4) #define SMALL_DIGITS (17) #else @@ -396,11 +368,7 @@ _ET_DECLARE_CHECKED(Eterm*,fun_val,Wterm) _ET_DECLARE_CHECKED(Eterm*,export_val,Wterm) #define export_val(x) _ET_APPLY(export_val,(x)) #define is_export_header(x) ((x) == HEADER_EXPORT) -#if HALFWORD_HEAP -#define HEADER_EXPORT _make_header(2,_TAG_HEADER_EXPORT) -#else #define HEADER_EXPORT _make_header(1,_TAG_HEADER_EXPORT) -#endif /* bignum access methods */ #define make_pos_bignum_header(sz) _make_header((sz),_TAG_HEADER_POS_BIG) @@ -424,7 +392,7 @@ _ET_DECLARE_CHECKED(Eterm*,big_val,Wterm) #define big_val(x) _ET_APPLY(big_val,(x)) /* flonum ("float") access methods */ -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) #define HEADER_FLONUM _make_header(1,_TAG_HEADER_FLOAT) #else #define HEADER_FLONUM _make_header(2,_TAG_HEADER_FLOAT) @@ -445,12 +413,12 @@ typedef union float_def byte fb[sizeof(ieee754_8)]; Uint16 fs[sizeof(ieee754_8) / sizeof(Uint16)]; Uint32 fw[sizeof(ieee754_8) / sizeof(Uint32)]; -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) Uint fdw; #endif } FloatDef; -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) #define FLOAT_VAL_GET_DOUBLE(fval, f) (f).fdw = *((fval)+1) @@ -727,7 +695,7 @@ _ET_DECLARE_CHECKED(struct erl_node_*,internal_port_node,Eterm) #define ERTS_MAX_REF_NUMBERS 3 #define ERTS_REF_NUMBERS ERTS_MAX_REF_NUMBERS -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) # define ERTS_REF_WORDS (ERTS_REF_NUMBERS/2 + 1) # define ERTS_REF_32BIT_WORDS (ERTS_REF_NUMBERS+1) #else @@ -749,7 +717,7 @@ typedef struct { #define make_ref_thing_header(DW) \ _make_header((DW)+REF_THING_HEAD_SIZE-1,_TAG_HEADER_REF) -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) /* * Ref layout on a 64-bit little endian machine: @@ -1159,17 +1127,9 @@ extern unsigned tag_val_def(Wterm); #define FLOAT_BIG _NUMBER_CODE(FLOAT_DEF,BIG_DEF) #define FLOAT_FLOAT _NUMBER_CODE(FLOAT_DEF,FLOAT_DEF) -#if HALFWORD_HEAP -#define ptr2rel(PTR,BASE) ((Eterm*)((char*)(PTR) - (char*)(BASE))) -#define rterm2wterm(REL,BASE) ((Wterm)(REL) + (Wterm)(BASE)) - -#else /* HALFWORD_HEAP */ - #define ptr2rel(PTR,BASE) (PTR) #define rterm2wterm(REL,BASE) (REL) -#endif /* !HALFWORD_HEAP */ - #define make_list_rel(PTR, BASE) make_list(ptr2rel(PTR,BASE)) #define make_boxed_rel(PTR, BASE) make_boxed(ptr2rel(PTR,BASE)) #define make_fun_rel make_boxed_rel @@ -1216,25 +1176,7 @@ extern unsigned tag_val_def(Wterm); #define external_node_rel(RTERM,BASE) external_node(rterm2wterm(RTERM,BASE)) - -#if HALFWORD_HEAP -ERTS_GLB_INLINE int is_same(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base); - -#if ERTS_GLB_INLINE_INCL_FUNC_DEF -ERTS_GLB_INLINE int is_same(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base) -{ - /* If bases differ, assume a and b are on different "heaps", - ie can only be same if immed */ - ASSERT(a_base == b_base || is_immed(a) || is_immed(b) - || rterm2wterm(a,a_base) != rterm2wterm(b,b_base)); - - return a == b && (a_base == b_base || is_immed(a)); -} -#endif - -#else /* !HALFWORD_HEAP */ #define is_same(A,A_BASE,B,B_BASE) ((A)==(B)) -#endif #endif /* __ERL_TERM_H */ diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 6dab3bf297..1732579d04 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -157,24 +157,11 @@ void erts_init_utils_mem(void); erts_dsprintf_buf_t *erts_create_tmp_dsbuf(Uint); void erts_destroy_tmp_dsbuf(erts_dsprintf_buf_t *); -#if HALFWORD_HEAP -int eq_rel(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base); -# define eq(A,B) eq_rel(A,NULL,B,NULL) -#else int eq(Eterm, Eterm); -# define eq_rel(A,A_BASE,B,B_BASE) eq(A,B) -#endif +#define eq_rel(A,A_BASE,B,B_BASE) eq(A,B) #define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y)))) -#if HALFWORD_HEAP -Sint erts_cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int, int); -#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,0,0) -#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,1,0) -#define CMP(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,0,0) -#define CMP_TERM(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,1,0) -#define CMP_EQ_ONLY(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,0,1) -#else Sint erts_cmp(Eterm, Eterm, int, int); Sint cmp(Eterm a, Eterm b); #define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp(A,B,0,0) @@ -182,7 +169,6 @@ Sint cmp(Eterm a, Eterm b); #define CMP(A,B) erts_cmp(A,B,0,0) #define CMP_TERM(A,B) erts_cmp(A,B,1,0) #define CMP_EQ_ONLY(A,B) erts_cmp(A,B,0,1) -#endif #define cmp_lt(a,b) (CMP((a),(b)) < 0) #define cmp_le(a,b) (CMP((a),(b)) <= 0) diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 8948ca2ea9..2d4141ac8d 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -117,12 +117,8 @@ #define HeapWordsLeft(p) (HEAP_LIMIT(p) - HEAP_TOP(p)) #if defined(DEBUG) || defined(CHECK_FOR_HOLES) -#if HALFWORD_HEAP -# define ERTS_HOLE_MARKER (0xdeadbeef) -#else # define ERTS_HOLE_MARKER (((0xdeadbeef << 24) << 8) | 0xdeadbeef) -#endif -#endif +#endif /* egil: 32-bit ? */ /* * Allocate heap memory on the ordinary heap, NEVER in a heap diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index c6d7e3fcc5..f7b372d294 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2339,10 +2339,6 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Eterm val; FloatDef f; Sint r = 0; -#if HALFWORD_HEAP - UWord wobj; -#endif - if (ctx) { WSTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); @@ -2362,11 +2358,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, outer_loop: while (!WSTACK_ISEMPTY(s)) { -#if HALFWORD_HEAP - obj = (Eterm) (wobj = WSTACK_POP(s)); -#else obj = WSTACK_POP(s); -#endif + switch (val = WSTACK_POP(s)) { case ENC_TERM: break; @@ -2384,11 +2377,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, break; case ENC_PATCH_FUN_SIZE: { -#if HALFWORD_HEAP - byte* size_p = (byte *) wobj; -#else byte* size_p = (byte *) obj; -#endif put_int32(ep - size_p, size_p); } goto outer_loop; @@ -2435,21 +2424,13 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, case ENC_LAST_ARRAY_ELEMENT: /* obj is the tuple */ { -#if HALFWORD_HEAP - Eterm* ptr = (Eterm *) wobj; -#else Eterm* ptr = (Eterm *) obj; -#endif obj = *ptr; } break; default: /* ENC_LAST_ARRAY_ELEMENT+1 and upwards */ { -#if HALFWORD_HEAP - Eterm* ptr = (Eterm *) wobj; -#else Eterm* ptr = (Eterm *) obj; -#endif obj = *ptr++; WSTACK_PUSH2(s, val-1, (UWord)ptr); } @@ -3049,7 +3030,7 @@ dec_term(ErtsDistExternal *edep, Sint sn = get_int32(ep); ep += 4; -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) *objp = make_small(sn); #else if (MY_IS_SSMALL(sn)) { @@ -3371,7 +3352,7 @@ dec_term_atom_common: RefThing *rtp = (RefThing *) hp; ref_num = (Uint32 *) (hp + REF_THING_HEAD_SIZE); -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) hp += REF_THING_HEAD_SIZE + ref_words/2 + 1; rtp->header = make_ref_thing_header(ref_words/2 + 1); #else @@ -3382,13 +3363,13 @@ dec_term_atom_common: } else { ExternalThing *etp = (ExternalThing *) hp; -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) hp += EXTERNAL_THING_HEAD_SIZE + ref_words/2 + 1; #else hp += EXTERNAL_THING_HEAD_SIZE + ref_words; #endif -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) etp->header = make_external_ref_header(ref_words/2 + 1); #else etp->header = make_external_ref_header(ref_words); @@ -3401,7 +3382,7 @@ dec_term_atom_common: ref_num = &(etp->data.ui32[0]); } -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) *(ref_num++) = ref_words /* 32-bit arity */; #endif ref_num[0] = r0; @@ -3409,7 +3390,7 @@ dec_term_atom_common: ref_num[i] = get_int32(ep); ep += 4; } -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) if ((1 + ref_words) % 2) ref_num[ref_words] = 0; #endif @@ -3561,12 +3542,7 @@ dec_term_atom_common: } *objp = make_export(hp); *hp++ = HEADER_EXPORT; -#if HALFWORD_HEAP - *((UWord *) (UWord) hp) = (UWord) erts_export_get_or_make_stub(mod, name, arity); - hp += 2; -#else *hp++ = (Eterm) erts_export_get_or_make_stub(mod, name, arity); -#endif break; } break; @@ -4192,11 +4168,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, case EXPORT_DEF: { Export* ep = *((Export **) (export_val(obj) + 1)); -#if HALFWORD_HEAP - result += 2; -#else result += 1; -#endif result += encode_size_struct2(acmp, ep->code[0], dflags); result += encode_size_struct2(acmp, ep->code[1], dflags); result += encode_size_struct2(acmp, make_small(ep->code[2]), dflags); @@ -4311,7 +4283,7 @@ init_done: switch (tag) { case INTEGER_EXT: SKIP(4); -#if !defined(ARCH_64) || HALFWORD_HEAP +#if !defined(ARCH_64) heap_size += BIG_UINT_HEAP_SIZE; #endif break; @@ -4400,7 +4372,7 @@ init_done: ep += 2; atom_extra_skip = 1 + 4*id_words; /* In case it is an external ref */ -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) heap_size += EXTERNAL_THING_HEAD_SIZE + id_words/2 + 1; #else heap_size += EXTERNAL_THING_HEAD_SIZE + id_words; @@ -4486,11 +4458,7 @@ init_done: break; case EXPORT_EXT: terms += 3; -#if HALFWORD_HEAP - heap_size += 3; -#else heap_size += 2; -#endif break; case NEW_FUN_EXT: { diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index ec9296d034..1d2d75bc1e 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -310,9 +310,6 @@ typedef union { typedef struct proc_bin { Eterm thing_word; /* Subtag REFC_BINARY_SUBTAG. */ Uint size; /* Binary size in bytes. */ -#if HALFWORD_HEAP - void* dummy_ptr_padding__; -#endif struct erl_off_heap_header *next; Binary *val; /* Pointer to Binary structure. */ byte *bytes; /* Pointer to the actual data bytes. */ @@ -959,28 +956,14 @@ void erl_error(char*, va_list); /* copy.c */ Eterm copy_object(Eterm, Process*); -#if HALFWORD_HEAP -Uint size_object_rel(Eterm, Eterm*); -# define size_object(A) size_object_rel(A,NULL) - -Eterm copy_struct_rel(Eterm, Uint, Eterm**, ErlOffHeap*, Eterm* src_base, Eterm* dst_base); -# define copy_struct(OBJ,SZ,HPP,OH) copy_struct_rel(OBJ,SZ,HPP,OH, NULL,NULL) - -Eterm copy_shallow_rel(Eterm*, Uint, Eterm**, ErlOffHeap*, Eterm* src_base); -# define copy_shallow(A,B,C,D) copy_shallow_rel(A,B,C,D,NULL) - -#else /* !HALFWORD_HEAP */ - Uint size_object(Eterm); -# define size_object_rel(A,B) size_object(A) +#define size_object_rel(A,B) size_object(A) Eterm copy_struct(Eterm, Uint, Eterm**, ErlOffHeap*); -# define copy_struct_rel(OBJ,SZ,HPP,OH, SB,DB) copy_struct(OBJ,SZ,HPP,OH) +#define copy_struct_rel(OBJ,SZ,HPP,OH, SB,DB) copy_struct(OBJ,SZ,HPP,OH) Eterm copy_shallow(Eterm*, Uint, Eterm**, ErlOffHeap*); -# define copy_shallow_rel(A,B,C,D, BASE) copy_shallow(A,B,C,D) - -#endif +#define copy_shallow_rel(A,B,C,D, BASE) copy_shallow(A,B,C,D) void move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first, @@ -1180,7 +1163,7 @@ void bin_write(int, void*, byte*, size_t); int intlist_to_buf(Eterm, char*, int); /* most callers pass plain char*'s */ struct Sint_buf { -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) char s[22]; #else char s[12]; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 900616c981..c365b8859b 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1105,7 +1105,7 @@ io_list_vec_len(Eterm obj, int* vsize, Uint* csize, Uint p_v_size = 0; Uint p_c_size = 0; Uint p_in_clist = 0; - Uint total; /* Uint due to halfword emulator */ + Uint total; goto L_jump_start; /* avoid a push */ @@ -5248,25 +5248,17 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) break; case ERL_DRV_INT: /* signed int argument */ ERTS_DDT_CHK_ENOUGH_ARGS(1); -#if HALFWORD_HEAP - erts_bld_sint64(NULL, &need, (Sint64)ptr[0]); -#else /* check for bignum */ if (!IS_SSMALL((Sint)ptr[0])) need += BIG_UINT_HEAP_SIZE; /* use small_to_big */ -#endif ptr++; depth++; break; case ERL_DRV_UINT: /* unsigned int argument */ ERTS_DDT_CHK_ENOUGH_ARGS(1); -#if HALFWORD_HEAP - erts_bld_uint64(NULL, &need, (Uint64)ptr[0]); -#else /* check for bignum */ if (!IS_USMALL(0, (Uint)ptr[0])) need += BIG_UINT_HEAP_SIZE; /* use small_to_big */ -#endif ptr++; depth++; break; @@ -5465,10 +5457,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) break; case ERL_DRV_INT: /* signed int argument */ -#if HALFWORD_HEAP - erts_reserve_heap(&factory, BIG_NEED_SIZE(2)); - mess = erts_bld_sint64(&factory.hp, NULL, (Sint64)ptr[0]); -#else erts_reserve_heap(&factory, BIG_UINT_HEAP_SIZE); if (IS_SSMALL((Sint)ptr[0])) mess = make_small((Sint)ptr[0]); @@ -5476,15 +5464,10 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) mess = small_to_big((Sint)ptr[0], factory.hp); factory.hp += BIG_UINT_HEAP_SIZE; } -#endif ptr++; break; case ERL_DRV_UINT: /* unsigned int argument */ -#if HALFWORD_HEAP - erts_reserve_heap(&factory, BIG_NEED_FOR_BITS(64)); - mess = erts_bld_uint64(&factory.hp, NULL, (Uint64)ptr[0]); -#else erts_reserve_heap(&factory, BIG_UINT_HEAP_SIZE); if (IS_USMALL(0, (Uint)ptr[0])) mess = make_small((Uint)ptr[0]); @@ -5492,7 +5475,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) mess = uint_to_big((Uint)ptr[0], factory.hp); factory.hp += BIG_UINT_HEAP_SIZE; } -#endif ptr++; break; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index bb871b05ba..14eeb0ee8f 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -285,62 +285,11 @@ __decl_noreturn void __noreturn erl_assert_error(const char* expr, const char *f #else #error Neither 32 nor 64 bit architecture #endif -#if defined(ARCH_64) && defined(HALFWORD_HEAP_EMULATOR) -# define HALFWORD_HEAP 1 -# define HALFWORD_ASSERT 0 -# define ASSERT_HALFWORD(COND) ASSERT(COND) -# undef ERTS_SIZEOF_TERM -# define ERTS_SIZEOF_TERM 4 -#else -# define HALFWORD_HEAP 0 -# define HALFWORD_ASSERT 0 -# define ASSERT_HALFWORD(COND) -#endif #if SIZEOF_VOID_P != SIZEOF_SIZE_T #error sizeof(void*) != sizeof(size_t) #endif -#if HALFWORD_HEAP - -#if SIZEOF_INT == 4 -typedef unsigned int Eterm; -typedef unsigned int Uint; -typedef int Sint; -#define ERTS_UINT_MAX UINT_MAX -#define ERTS_SIZEOF_ETERM SIZEOF_INT -#define ErtsStrToSint strtol -#else -#error Found no appropriate type to use for 'Eterm', 'Uint' and 'Sint' -#endif - -#if SIZEOF_VOID_P == SIZEOF_LONG -typedef unsigned long UWord; -typedef long SWord; -#define SWORD_CONSTANT(Const) Const##L -#define UWORD_CONSTANT(Const) Const##UL -#define ERTS_UWORD_MAX ULONG_MAX -#define ERTS_SWORD_MAX LONG_MAX -#elif SIZEOF_VOID_P == SIZEOF_INT -typedef unsigned int UWord; -typedef int SWord; -#define SWORD_CONSTANT(Const) Const -#define UWORD_CONSTANT(Const) Const##U -#define ERTS_UWORD_MAX UINT_MAX -#define ERTS_SWORD_MAX INT_MAX -#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG -typedef unsigned long long UWord; -typedef long long SWord; -#define SWORD_CONSTANT(Const) Const##LL -#define UWORD_CONSTANT(Const) Const##ULL -#define ERTS_UWORD_MAX ULLONG_MAX -#define ERTS_SWORD_MAX LLONG_MAX -#else -#error Found no appropriate type to use for 'Eterm', 'Uint' and 'Sint' -#endif - -#else /* !HALFWORD_HEAP */ - #if SIZEOF_VOID_P == SIZEOF_LONG typedef unsigned long Eterm; typedef unsigned long Uint; @@ -383,8 +332,6 @@ typedef Uint UWord; typedef Sint SWord; #define ERTS_UINT_MAX ERTS_UWORD_MAX -#endif /* HALFWORD_HEAP */ - typedef UWord BeamInstr; #ifndef HAVE_INT64 diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 342e91e983..28d1b38f75 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -898,7 +898,7 @@ tail_recur: Uint y2 = y1 < 0 ? -(Uint)y1 : y1; UINT32_HASH_STEP(y2, FUNNY_NUMBER2); -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) if (y2 >> 32) UINT32_HASH_STEP(y2 >> 32, FUNNY_NUMBER2); #endif @@ -1019,7 +1019,7 @@ tail_recur: } d = BIG_DIGIT(ptr, k); k = sizeof(ErtsDigit); -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) if (!(d >> 32)) k /= 2; #endif @@ -1989,7 +1989,7 @@ tail_recur: (atom_tab(atom_val(term))->slot.bucket.hvalue); break; case SMALL_DEF: -#if defined(ARCH_64) && !HALFWORD_HEAP +#if defined(ARCH_64) { Sint y1 = signed_val(term); Uint y2 = y1 < 0 ? -(Uint)y1 : y1; @@ -2598,11 +2598,7 @@ erts_destroy_tmp_dsbuf(erts_dsprintf_buf_t *dsbufp) * Test for equality of two terms. * Returns 0 if not equal, or a non-zero value otherwise. */ -#if HALFWORD_HEAP -int eq_rel(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base) -#else int eq(Eterm a, Eterm b) -#endif { DECLARE_WSTACK(stack); Sint sz; @@ -2992,7 +2988,6 @@ static int cmp_atoms(Eterm a, Eterm b) bb->name+3, bb->len-3); } -#if !HALFWORD_HEAP /* cmp(Eterm a, Eterm b) * For compatibility with HiPE - arith-based compare. */ @@ -3000,23 +2995,10 @@ Sint cmp(Eterm a, Eterm b) { return erts_cmp(a, b, 0, 0); } -#endif -#if HALFWORD_HEAP -static Sint erts_cmp_compound_rel_opt(Eterm a, Eterm* a_base, - Eterm b, Eterm* b_base, - int exact, int eq_only); -#else static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only); -#endif -#if HALFWORD_HEAP -Sint erts_cmp_rel_opt(Eterm a, Eterm* a_base, - Eterm b, Eterm* b_base, - int exact, int eq_only) -#else Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) -#endif { if (is_atom(a) && is_atom(b)) { return cmp_atoms(a, b); @@ -3028,11 +3010,7 @@ Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) GET_DOUBLE_REL(b, bf, b_base); return float_comp(af.fd, bf.fd); } -#if HALFWORD_HEAP - return erts_cmp_compound_rel_opt(a,a_base,b,b_base,exact,eq_only); -#else return erts_cmp_compound(a,b,exact,eq_only); -#endif } @@ -3040,13 +3018,7 @@ Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) * exact = 1 -> term-based compare * exact = 0 -> arith-based compare */ -#if HALFWORD_HEAP -static Sint erts_cmp_compound_rel_opt(Eterm a, Eterm* a_base, - Eterm b, Eterm* b_base, - int exact, int eq_only) -#else static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only) -#endif { #define PSTACK_TYPE struct erts_cmp_hashmap_state struct erts_cmp_hashmap_state { @@ -3551,13 +3523,8 @@ tailrecur_ne: { FloatDef f1, f2; Eterm big; -#if HALFWORD_HEAP - Wterm aw = is_immed(a) ? a : rterm2wterm(a,a_base); - Wterm bw = is_immed(b) ? b : rterm2wterm(b,b_base); -#else Eterm aw = a; Eterm bw = b; -#endif #define MAX_LOSSLESS_FLOAT ((double)((1LL << 53) - 2)) #define MIN_LOSSLESS_FLOAT ((double)(((1LL << 53) - 2)*-1)) #define BIG_ARITY_FLOAT_MAX (1024 / D_EXP) /* arity of max float as a bignum */ @@ -4369,7 +4336,7 @@ iolist_size(const int yield_support, ErtsIOListState *state, Eterm obj, ErlDrvSi { int res, init_yield_count, yield_count; Eterm* objp; - Uint size = (Uint) *sizep; /* Intentionally Uint due to halfword heap */ + Uint size = (Uint) *sizep; DECLARE_ESTACK(s); if (!yield_support) diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 8aff6c1865..0317a95e8b 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -895,7 +895,7 @@ static void reply_Uint_posix_error(file_descriptor *desc, Uint num, TRACE_C('N'); response[0] = FILE_RESP_NUMERR; -#if SIZEOF_VOID_P == 4 || HALFWORD_HEAP +#if SIZEOF_VOID_P == 4 put_int32(0, response+1); #else put_int32(num>>32, response+1); @@ -964,7 +964,7 @@ static int reply_Uint(file_descriptor *desc, Uint result) { TRACE_C('R'); tmp[0] = FILE_RESP_NUMBER; -#if SIZEOF_VOID_P == 4 || HALFWORD_HEAP +#if SIZEOF_VOID_P == 4 put_int32(0, tmp+1); #else put_int32(result>>32, tmp+1); diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 754047829f..e6d0e1e281 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -2453,7 +2453,6 @@ Eterm erts_mmap_debug_info(Process* p) UWord values[4]; Eterm *hp, *hp_end; Uint may_need; - const Uint PTR_BIG_SZ = HALFWORD_HEAP ? 3 : 2; erts_smp_mtx_lock(&mmap_state.mtx); values[0] = (UWord)mmap_state.sa.bot; @@ -2464,7 +2463,7 @@ Eterm erts_mmap_debug_info(Process* p) sua_list = build_free_seg_list(p, &mmap_state.sua.map); erts_smp_mtx_unlock(&mmap_state.mtx); - may_need = 4*(2+3+PTR_BIG_SZ) + 2*(2+3); + may_need = 4*(2+3+2) + 2*(2+3); hp = HAlloc(p, may_need); hp_end = hp + may_need; diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 0d51aad863..8073d5b908 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -107,9 +107,6 @@ const ErtsMsegOpt_t erts_mseg_default_opt = { 0, /* Absolute shrink threshold */ 0, /* Relative shrink threshold */ 0 /* Scheduler specific */ -#if HALFWORD_HEAP - ,0 /* need low memory */ -#endif }; @@ -184,12 +181,7 @@ struct ErtsMsegAllctr_t_ { MemKind* mk_list; -#if HALFWORD_HEAP - MemKind low_mem; - MemKind hi_mem; -#else MemKind the_mem; -#endif Uint max_cache_size; Uint abs_max_cache_bad_fit; @@ -309,11 +301,6 @@ mseg_create(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, UWord *sizep) #endif void *seg; Uint32 mmap_flags = 0; -#if HALFWORD_HEAP - mmap_flags |= ((mk == &ma->low_mem) - ? ERTS_MMAPFLG_SUPERCARRIER_ONLY - : ERTS_MMAPFLG_OS_ONLY); -#endif if (MSEG_FLG_IS_2POW(flags)) mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; @@ -334,11 +321,6 @@ static ERTS_INLINE void mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void *seg_p, UWord size) { Uint32 mmap_flags = 0; -#if HALFWORD_HEAP - mmap_flags |= ((mk == &ma->low_mem) - ? ERTS_MMAPFLG_SUPERCARRIER_ONLY - : ERTS_MMAPFLG_OS_ONLY); -#endif if (MSEG_FLG_IS_2POW(flags)) mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; @@ -360,11 +342,6 @@ mseg_recreate(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void *old_seg, UWor #endif void *new_seg; Uint32 mmap_flags = 0; -#if HALFWORD_HEAP - mmap_flags |= ((mk == &ma->low_mem) - ? ERTS_MMAPFLG_SUPERCARRIER_ONLY - : ERTS_MMAPFLG_OS_ONLY); -#endif if (MSEG_FLG_IS_2POW(flags)) mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; @@ -768,11 +745,7 @@ void erts_mseg_clear_cache(void) { static ERTS_INLINE MemKind* memkind(ErtsMsegAllctr_t *ma, const ErtsMsegOpt_t *opt) { -#if HALFWORD_HEAP - return opt->low_mem ? &ma->low_mem : &ma->hi_mem; -#else return &ma->the_mem; -#endif } static void * @@ -1326,12 +1299,7 @@ erts_mseg_info(int ix, ERTS_MSEG_LOCK(ma); ERTS_DBG_MA_CHK_THR_ACCESS(ma); -#if HALFWORD_HEAP - values[n++] = info_memkind(ma, &ma->low_mem, print_to_p, print_to_arg, begin_max_per, hpp, szp); - values[n++] = info_memkind(ma, &ma->hi_mem, print_to_p, print_to_arg, begin_max_per, hpp, szp); -#else values[n++] = info_memkind(ma, &ma->the_mem, print_to_p, print_to_arg, begin_max_per, hpp, szp); -#endif if (hpp || szp) res = bld_2tup_list(hpp, szp, n, atoms, values); @@ -1488,15 +1456,6 @@ erts_mseg_init(ErtsMsegInit_t *init) erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms"); -#if HALFWORD_HEAP - if (sizeof(void *) != 8) - erl_exit(-1,"Halfword emulator cannot be run in 32bit mode"); - - init->mmap.virtual_range.start = (char *) sbrk(0); - init->mmap.virtual_range.end = (char *) 0x100000000UL; - init->mmap.sco = 0; -#endif - erts_mmap_init(&init->mmap); if (!IS_2POW(GET_PAGE_SIZE)) @@ -1531,12 +1490,7 @@ erts_mseg_init(ErtsMsegInit_t *init) ma->mk_list = NULL; -#if HALFWORD_HEAP - mem_kind_init(ma, &ma->low_mem, "low memory"); - mem_kind_init(ma, &ma->hi_mem, "high memory"); -#else mem_kind_init(ma, &ma->the_mem, "all memory"); -#endif sys_memzero((void *) &ma->calls, sizeof(ErtsMsegCalls)); } diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index ba04e919fc..656484702d 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -87,9 +87,6 @@ typedef struct { UWord abs_shrink_th; UWord rel_shrink_th; int sched_spec; -#if HALFWORD_HEAP - int low_mem; -#endif } ErtsMsegOpt_t; extern const ErtsMsegOpt_t erts_mseg_default_opt; diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 9a8c3585e6..e90ed94187 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -605,11 +605,7 @@ sub emulator_output { print "#ifdef ARCH_64\n"; print "# define BEAM_WIDE_MASK 0xFFFFUL\n"; print "# define BEAM_LOOSE_MASK 0x1FFFUL\n"; - print "#if HALFWORD_HEAP\n"; - print "# define BEAM_TIGHT_MASK 0x1FFCUL\n"; - print "#else\n"; print "# define BEAM_TIGHT_MASK 0x1FF8UL\n"; - print "#endif\n"; print "# define BEAM_WIDE_SHIFT 32\n"; print "# define BEAM_LOOSE_SHIFT 16\n"; print "# define BEAM_TIGHT_SHIFT 16\n"; -- cgit v1.2.3 From 49e744df7a31e329c03567f3bd7cfb72e4a555d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 17 Jun 2015 21:03:53 +0200 Subject: erts: Remove halfword in erl_driver.h --- erts/emulator/beam/erl_driver.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 6b406d069c..ef2d41e7a2 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -57,10 +57,6 @@ # define SIZEOF_LONG_LONG_SAVED__ SIZEOF_LONG_LONG # undef SIZEOF_LONG_LONG #endif -#ifdef HALFWORD_HEAP_EMULATOR -# define HALFWORD_HEAP_EMULATOR_SAVED__ HALFWORD_HEAP_EMULATOR -# undef HALFWORD_HEAP_EMULATOR -#endif #include "erl_int_sizes_config.h" #if defined(SIZEOF_CHAR_SAVED__) && SIZEOF_CHAR_SAVED__ != SIZEOF_CHAR # error SIZEOF_CHAR mismatch @@ -78,11 +74,6 @@ # error SIZEOF_LONG_LONG mismatch #endif -/* This is OK to override by the NIF/driver implementor */ -#if defined(HALFWORD_HEAP_EMULATOR_SAVED__) && !defined(HALFWORD_HEAP_EMULATOR) -#define HALFWORD_HEAP_EMULATOR HALFWORD_HEAP_EMULATOR_SAVED__ -#endif - #include "erl_drv_nif.h" #include -- cgit v1.2.3 From 16b83562b0d2e9f9892744dcbf7894ef7d18a82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 17 Jun 2015 21:08:52 +0200 Subject: erts: Remove halfword in erl_nif.h --- erts/emulator/beam/erl_nif.h | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 7d880126f8..5da6bb877a 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -86,10 +86,6 @@ # define SIZEOF_LONG_LONG_SAVED__ SIZEOF_LONG_LONG # undef SIZEOF_LONG_LONG #endif -#ifdef HALFWORD_HEAP_EMULATOR -# define HALFWORD_HEAP_EMULATOR_SAVED__ HALFWORD_HEAP_EMULATOR -# undef HALFWORD_HEAP_EMULATOR -#endif #include "erl_int_sizes_config.h" #ifdef __cplusplus @@ -109,16 +105,11 @@ typedef long long ErlNifSInt64; #error No 64-bit integer type #endif -#ifdef HALFWORD_HEAP_EMULATOR -# define ERL_NIF_VM_VARIANT "beam.halfword" -typedef unsigned int ERL_NIF_TERM; -#else -# define ERL_NIF_VM_VARIANT "beam.vanilla" -# if SIZEOF_LONG == SIZEOF_VOID_P +#define ERL_NIF_VM_VARIANT "beam.vanilla" +#if SIZEOF_LONG == SIZEOF_VOID_P typedef unsigned long ERL_NIF_TERM; -# elif SIZEOF_LONG_LONG == SIZEOF_VOID_P +#elif SIZEOF_LONG_LONG == SIZEOF_VOID_P typedef unsigned long long ERL_NIF_TERM; -# endif #endif typedef ERL_NIF_TERM ERL_NIF_UINT; -- cgit v1.2.3 From f3a0dc2a3b2ef161b83994a374ef2a447328098e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 11:20:53 +0200 Subject: erts: Remove halfword in lib_src --- erts/include/erl_int_sizes_config.h.in | 3 --- erts/include/internal/erl_printf_format.h | 9 --------- erts/lib_src/common/erl_printf_format.c | 12 ++---------- 3 files changed, 2 insertions(+), 22 deletions(-) (limited to 'erts') diff --git a/erts/include/erl_int_sizes_config.h.in b/erts/include/erl_int_sizes_config.h.in index b18f5ebc00..88c74cdeff 100644 --- a/erts/include/erl_int_sizes_config.h.in +++ b/erts/include/erl_int_sizes_config.h.in @@ -35,6 +35,3 @@ /* The size of a pointer. */ #undef SIZEOF_VOID_P - -/* Define if building a halfword-heap 64bit emulator (needed for NIF's) */ -#undef HALFWORD_HEAP_EMULATOR diff --git a/erts/include/internal/erl_printf_format.h b/erts/include/internal/erl_printf_format.h index efd926be99..c41c5ba820 100644 --- a/erts/include/internal/erl_printf_format.h +++ b/erts/include/internal/erl_printf_format.h @@ -44,7 +44,6 @@ typedef long long ErlPfSWord; #error Found no appropriate type to use for 'Eterm', 'Uint' and 'Sint' #endif - typedef int (*fmtfn_t)(void*, char*, size_t); extern int erts_printf_format(fmtfn_t, void*, char*, va_list); @@ -57,17 +56,9 @@ extern int erts_printf_uword(fmtfn_t, void*, char, int, int, ErlPfUWord); extern int erts_printf_sword(fmtfn_t, void*, char, int, int, ErlPfSWord); extern int erts_printf_double(fmtfn_t, void *, char, int, int, double); -#ifdef HALFWORD_HEAP_EMULATOR -# if SIZEOF_INT != 4 -# error Unsupported integer size for HALFWORD_HEAP_EMULATOR -# endif -typedef unsigned int ErlPfEterm; -#else typedef ErlPfUWord ErlPfEterm; -#endif extern int (*erts_printf_eterm_func)(fmtfn_t, void*, ErlPfEterm, long, ErlPfEterm*); - #endif /* ERL_PRINTF_FORMAT_H__ */ diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c index 307680505c..9e70f4e882 100644 --- a/erts/lib_src/common/erl_printf_format.c +++ b/erts/lib_src/common/erl_printf_format.c @@ -78,15 +78,7 @@ #endif #ifndef ERTS_SIZEOF_ETERM -# ifdef HALFWORD_HEAP_EMULATOR -# if SIZEOF_VOID_P == 8 -# define ERTS_SIZEOF_ETERM 4 -# else -# error "HALFWORD_HEAP_EMULATOR only allowed on 64-bit architecture" -# endif -# else -# define ERTS_SIZEOF_ETERM SIZEOF_VOID_P -# endif +#define ERTS_SIZEOF_ETERM SIZEOF_VOID_P #endif #if defined(__GNUC__) @@ -821,7 +813,7 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) } break; case FMTC_T: /* Eterm */ - case FMTC_R: { /* Eterm, Eterm* base (base ignored if !HALFWORD_HEAP) */ + case FMTC_R: { /* Eterm, Eterm* base */ long prec; ErlPfEterm eterm; ErlPfEterm* eterm_base; -- cgit v1.2.3 From c5ee304b5e73a5e5a9ac38c1180971baa051824b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 11:28:09 +0200 Subject: erts: Remove halfword from configure --- erts/configure.in | 44 -------------------------------------------- 1 file changed, 44 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 22ca7ec17d..293ff825f8 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -143,14 +143,6 @@ AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]), *) enable_dirty_schedulers=yes ;; esac ], enable_dirty_schedulers=no) -AC_ARG_ENABLE(halfword-emulator, -AS_HELP_STRING([--enable-halfword-emulator], - [enable halfword emulator (only for 64bit builds). Note: Halfword emulator is marked as deprecated and scheduled for removal in future major release.]), -[ case "$enableval" in - no) enable_halfword_emualtor=no ;; - *) enable_halfword_emulator=yes ;; - esac ], enable_halfword_emulator=unknown) - AC_ARG_ENABLE(smp-support, AS_HELP_STRING([--enable-smp-support], [enable smp support]) AS_HELP_STRING([--disable-smp-support], [disable smp support]), @@ -787,36 +779,6 @@ esac AC_SUBST(LIBCARBON) -dnl Check if we should/can build a halfword emulator - -AC_MSG_CHECKING(if we are building a halfword emulator (32bit heap on 64bit machine)) -if test "$enable_halfword_emulator" = "yes"; then - if test "$ARCH" = "amd64"; then - AC_DEFINE(HALFWORD_HEAP_EMULATOR, [1], - [Define if building a halfword-heap 64bit emulator]) - ENABLE_ALLOC_TYPE_VARS="$ENABLE_ALLOC_TYPE_VARS halfword" - AC_MSG_RESULT([yes]) - - test -f "$ERL_TOP/erts/CONF_INFO" || - echo "" > "$ERL_TOP/erts/CONF_INFO" - cat >> $ERL_TOP/erts/CONF_INFO < Date: Thu, 18 Jun 2015 14:30:28 +0200 Subject: erts: Remove halfword basic relative heap operations --- erts/emulator/beam/big.c | 2 +- erts/emulator/beam/copy.c | 48 +++--- erts/emulator/beam/erl_binary.h | 7 +- erts/emulator/beam/erl_db_hash.c | 2 +- erts/emulator/beam/erl_db_tree.c | 10 +- erts/emulator/beam/erl_db_util.c | 60 ++++---- erts/emulator/beam/erl_db_util.h | 2 +- erts/emulator/beam/erl_map.c | 16 +- erts/emulator/beam/erl_map.h | 9 +- erts/emulator/beam/erl_node_container_utils.h | 2 - erts/emulator/beam/erl_printf_term.c | 10 +- erts/emulator/beam/erl_term.h | 56 ------- erts/emulator/beam/io.c | 4 +- erts/emulator/beam/utils.c | 213 +++++++++++++------------- 14 files changed, 188 insertions(+), 253 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 1e6582a7ca..8662398dcf 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1669,7 +1669,7 @@ double_to_big(double x, Eterm *heap, Uint hsz) sz = BIG_NEED_SIZE(ds); /* number of words including arity */ hp = heap; - res = make_big_rel(hp, heap); + res = make_big(hp); xp = (ErtsDigit*) (hp + 1); ASSERT(ds < hsz); diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 94048f4c59..38c40f4e7d 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -80,7 +80,7 @@ Uint size_object(Eterm obj) switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: sum += 2; - ptr = list_val_rel(obj,base); + ptr = list_val(obj); obj = *ptr++; if (!IS_CONST(obj)) { ESTACK_PUSH(s, obj); @@ -89,11 +89,11 @@ Uint size_object(Eterm obj) break; case TAG_PRIMARY_BOXED: { - Eterm hdr = *boxed_val_rel(obj,base); + Eterm hdr = *boxed_val(obj); ASSERT(is_header(hdr)); switch (hdr & _TAG_HEADER_MASK) { case ARITYVAL_SUBTAG: - ptr = tuple_val_rel(obj,base); + ptr = tuple_val(obj); arity = header_arity(hdr); sum += arity + 1; if (arity == 0) { /* Empty tuple -- unusual. */ @@ -109,7 +109,7 @@ Uint size_object(Eterm obj) break; case FUN_SUBTAG: { - Eterm* bptr = fun_val_rel(obj,base); + Eterm* bptr = fun_val(obj); ErlFunThing* funp = (ErlFunThing *) bptr; unsigned eterms = 1 /* creator */ + funp->num_free; unsigned sz = thing_arityval(hdr); @@ -130,7 +130,7 @@ Uint size_object(Eterm obj) { Uint n; flatmap_t *mp; - mp = (flatmap_t*)flatmap_val_rel(obj,base); + mp = (flatmap_t*)flatmap_val(obj); ptr = (Eterm *)mp; n = flatmap_get_size(mp) + 1; sum += n + 2; @@ -149,7 +149,7 @@ Uint size_object(Eterm obj) { Eterm *head; Uint sz; - head = hashmap_val_rel(obj, base); + head = hashmap_val(obj); sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); sum += 1 + sz + header_arity(hdr); head += 1 + header_arity(hdr); @@ -188,11 +188,11 @@ Uint size_object(Eterm obj) } else { extra_bytes = 0; } - hdr = *binary_val_rel(real_bin,base); + hdr = *binary_val(real_bin); if (thing_subtag(hdr) == REFC_BINARY_SUBTAG) { sum += PROC_BIN_SIZE; } else { - sum += heap_bin_size(binary_size_rel(obj,base)+extra_bytes); + sum += heap_bin_size(binary_size(obj)+extra_bytes); } goto pop_next; } @@ -259,7 +259,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: argp = &res; - objp = list_val_rel(obj,src_base); + objp = list_val(obj); goto L_copy_list; case TAG_PRIMARY_BOXED: argp = &res; goto L_copy_boxed; default: @@ -277,7 +277,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) hp++; break; case TAG_PRIMARY_LIST: - objp = list_val_rel(obj,src_base); + objp = list_val(obj); if (in_area(objp,hstart,hsize)) { hp++; break; @@ -300,12 +300,12 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) tailp = &CDR(htop); htop += 2; } - *tp = make_list_rel(tailp - 1, dst_base); + *tp = make_list(tailp - 1); obj = CDR(objp); if (!is_list(obj)) { break; } - objp = list_val_rel(obj,src_base); + objp = list_val(obj); } switch (primary_tag(obj)) { case TAG_PRIMARY_IMMED1: *tailp = obj; goto L_copy; @@ -317,21 +317,21 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } case TAG_PRIMARY_BOXED: - if (in_area(boxed_val_rel(obj,src_base),hstart,hsize)) { + if (in_area(boxed_val(obj),hstart,hsize)) { hp++; break; } argp = hp++; L_copy_boxed: - objp = boxed_val_rel(obj, src_base); + objp = boxed_val(obj); hdr = *objp; switch (hdr & _TAG_HEADER_MASK) { case ARITYVAL_SUBTAG: { int const_flag = 1; /* assume constant tuple */ i = arityval(hdr); - *argp = make_tuple_rel(htop, dst_base); + *argp = make_tuple(htop); tp = htop; /* tp is pointer to new arity value */ *htop++ = *objp++; /* copy arity value */ while (i--) { @@ -360,7 +360,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) while (i--) { *tp++ = *objp++; } - *argp = make_binary_rel(hbot, dst_base); + *argp = make_binary(hbot); pb = (ProcBin*) hbot; erts_refc_inc(&pb->val->refc, 2); pb->next = off_heap->first; @@ -387,7 +387,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) extra_bytes = 0; } real_size = size+extra_bytes; - objp = binary_val_rel(real_bin,src_base); + objp = binary_val(real_bin); if (thing_subtag(*objp) == HEAP_BINARY_SUBTAG) { ErlHeapBin* from = (ErlHeapBin *) objp; ErlHeapBin* to; @@ -417,7 +417,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) off_heap->first = (struct erl_off_heap_header*) to; OH_OVERHEAD(off_heap, to->size / sizeof(Eterm)); } - *argp = make_binary_rel(hbot, dst_base); + *argp = make_binary(hbot); if (extra_bytes != 0) { ErlSubBin* res; hbot -= ERL_SUB_BIN_SIZE; @@ -429,7 +429,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) res->offs = 0; res->is_writable = 0; res->orig = *argp; - *argp = make_binary_rel(hbot, dst_base); + *argp = make_binary(hbot); } break; } @@ -447,7 +447,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) funp->next = off_heap->first; off_heap->first = (struct erl_off_heap_header*) funp; erts_refc_inc(&funp->fe->refc, 2); - *argp = make_fun_rel(tp, dst_base); + *argp = make_fun(tp); } break; case EXTERNAL_PID_SUBTAG: @@ -467,7 +467,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) off_heap->first = (struct erl_off_heap_header*)etp; erts_refc_inc(&etp->node->refc, 2); - *argp = make_external_rel(tp, dst_base); + *argp = make_external(tp); } break; case MAP_SUBTAG: @@ -475,7 +475,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) switch (MAP_HEADER_TYPE(hdr)) { case MAP_HEADER_TAG_FLATMAP_HEAD : i = flatmap_get_size(objp) + 3; - *argp = make_flatmap_rel(htop, dst_base); + *argp = make_flatmap(htop); while (i--) { *htop++ = *objp++; } @@ -486,7 +486,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) case MAP_HEADER_TAG_HAMT_NODE_BITMAP : i = 1 + hashmap_bitcount(MAP_HEADER_VAL(hdr)); while (i--) { *htop++ = *objp++; } - *argp = make_hashmap_rel(tp, dst_base); + *argp = make_hashmap(tp); break; default: erl_exit(ERTS_ABORT_EXIT, "copy_struct: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); @@ -499,7 +499,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) i = thing_arityval(hdr)+1; hbot -= i; tp = hbot; - *argp = make_boxed_rel(hbot, dst_base); + *argp = make_boxed(hbot); while (i--) { *tp++ = *objp++; } diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index ea01bf08f0..d24d041530 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -72,7 +72,6 @@ typedef struct erl_heap_bin { */ #define binary_size(Bin) (binary_val(Bin)[1]) -#define binary_size_rel(Bin,BasePtr) (binary_val_rel(Bin,BasePtr)[1]) #define binary_bitsize(Bin) \ ((*binary_val(Bin) == HEADER_SUB_BIN) ? \ @@ -100,7 +99,7 @@ typedef struct erl_heap_bin { #define ERTS_GET_BINARY_BYTES_REL(Bin,Bytep,Bitoffs,Bitsize,BasePtr) \ do { \ - Eterm* _real_bin = binary_val_rel(Bin,BasePtr); \ + Eterm* _real_bin = binary_val(Bin); \ Uint _offs = 0; \ Bitoffs = Bitsize = 0; \ if (*_real_bin == HEADER_SUB_BIN) { \ @@ -108,7 +107,7 @@ do { \ _offs = _sb->offs; \ Bitoffs = _sb->bitoffs; \ Bitsize = _sb->bitsize; \ - _real_bin = binary_val_rel(_sb->orig,BasePtr); \ + _real_bin = binary_val(_sb->orig); \ } \ if (*_real_bin == HEADER_PROC_BIN) { \ Bytep = ((ProcBin *) _real_bin)->bytes + _offs; \ @@ -135,7 +134,7 @@ do { \ #define ERTS_GET_REAL_BIN_REL(Bin, RealBin, ByteOffset, BitOffset, BitSize, BasePtr) \ do { \ - ErlSubBin* _sb = (ErlSubBin *) binary_val_rel(Bin,BasePtr); \ + ErlSubBin* _sb = (ErlSubBin *) binary_val(Bin); \ if (_sb->thing_word == HEADER_SUB_BIN) { \ RealBin = _sb->orig; \ ByteOffset = _sb->offs; \ diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index fc1aa05ed2..80563e1f02 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -2193,7 +2193,7 @@ static void db_print_hash(int to, void *to_arg, int show, DbTable *tbl) erts_print(to, to_arg, "key=%R", key, list->dbterm.tpl); } else { - Eterm obj = make_tuple_rel(list->dbterm.tpl,list->dbterm.tpl); + Eterm obj = make_tuple(list->dbterm.tpl); erts_print(to, to_arg, "%R", obj, list->dbterm.tpl); } if (list->next != 0) diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index c21eac6f25..3b698a6adf 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -2745,7 +2745,7 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) return cmp_rel(a,NULL,b,b_base); } aa = list_val(a); - bb = list_val_rel(b,b_base); + bb = list_val(b); while (1) { if ((j = do_cmp_partly_bound(*aa++, *bb++, b_base, done)) != 0 || *done) return j; @@ -2754,20 +2754,20 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) if (is_not_list(*aa) || is_not_list(*bb)) return do_cmp_partly_bound(*aa, *bb, b_base, done); aa = list_val(*aa); - bb = list_val_rel(*bb,b_base); + bb = list_val(*bb); } case TAG_PRIMARY_BOXED: if ((b & _TAG_PRIMARY_MASK) != TAG_PRIMARY_BOXED) { return cmp_rel(a,NULL,b,b_base); } a_hdr = ((*boxed_val(a)) & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE; - b_hdr = ((*boxed_val_rel(b,b_base)) & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE; + b_hdr = ((*boxed_val(b)) & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE; if (a_hdr != b_hdr) { return cmp_rel(a, NULL, b, b_base); } if (a_hdr == (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE)) { aa = tuple_val(a); - bb = tuple_val_rel(b, b_base); + bb = tuple_val(b); /* compare the arities */ i = arityval(*aa); /* get the arity*/ if (i < arityval(*bb)) return(-1); @@ -3154,7 +3154,7 @@ static void do_dump_tree2(DbTableTree* tb, int to, void *to_arg, int show, } else { prefix = ""; - term = make_tuple_rel(t->dbterm.tpl,t->dbterm.tpl); + term = make_tuple(t->dbterm.tpl); } erts_print(to, to_arg, "%*s%s%R (addr = %p, bal = %d)\n", offset, "", prefix, term, t->dbterm.tpl, diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 0086aa8121..432ca297e9 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1903,9 +1903,9 @@ restart: variables[n].term = dpm_array_to_list(psp, termp, arity); break; case matchTuple: /* *ep is a tuple of arity n */ - if (!is_tuple_rel(*ep,base)) + if (!is_tuple(*ep)) FAIL(); - ep = tuple_val_rel(*ep,base); + ep = tuple_val(*ep); n = *pc++; if (arityval(*ep) != n) FAIL(); @@ -1913,9 +1913,9 @@ restart: break; case matchPushT: /* *ep is a tuple of arity n, push ptr to first element */ - if (!is_tuple_rel(*ep,base)) + if (!is_tuple(*ep)) FAIL(); - tp = tuple_val_rel(*ep,base); + tp = tuple_val(*ep); n = *pc++; if (arityval(*tp) != n) FAIL(); @@ -1925,51 +1925,51 @@ restart: case matchList: if (!is_list(*ep)) FAIL(); - ep = list_val_rel(*ep,base); + ep = list_val(*ep); break; case matchPushL: if (!is_list(*ep)) FAIL(); - *sp++ = list_val_rel(*ep,base); + *sp++ = list_val(*ep); ++ep; break; case matchMap: - if (!is_map_rel(*ep, base)) { + if (!is_map(*ep)) { FAIL(); } n = *pc++; - if (is_flatmap_rel(*ep,base)) { - if (flatmap_get_size(flatmap_val_rel(*ep, base)) < n) { + if (is_flatmap(*ep)) { + if (flatmap_get_size(flatmap_val(*ep)) < n) { FAIL(); } } else { - ASSERT(is_hashmap_rel(*ep,base)); - if (hashmap_size_rel(*ep, base) < n) { + ASSERT(is_hashmap(*ep)); + if (hashmap_size(*ep) < n) { FAIL(); } } - ep = flatmap_val_rel(*ep, base); + ep = flatmap_val(*ep); break; case matchPushM: - if (!is_map_rel(*ep, base)) { + if (!is_map(*ep)) { FAIL(); } n = *pc++; - if (is_flatmap_rel(*ep,base)) { - if (flatmap_get_size(flatmap_val_rel(*ep, base)) < n) { + if (is_flatmap(*ep)) { + if (flatmap_get_size(flatmap_val(*ep)) < n) { FAIL(); } } else { - ASSERT(is_hashmap_rel(*ep,base)); - if (hashmap_size_rel(*ep, base) < n) { + ASSERT(is_hashmap(*ep)); + if (hashmap_size(*ep) < n) { FAIL(); } } - *sp++ = flatmap_val_rel(*ep++, base); + *sp++ = flatmap_val(*ep++); break; case matchKey: t = (Eterm) *pc++; - tp = erts_maps_get_rel(t, make_boxed_rel(ep, base), base); + tp = erts_maps_get(t, make_boxed(ep)); if (!tp) { FAIL(); } @@ -2001,18 +2001,18 @@ restart: ++ep; break; case matchEqFloat: - if (!is_float_rel(*ep,base)) + if (!is_float(*ep)) FAIL(); - if (memcmp(float_val_rel(*ep,base) + 1, pc, sizeof(double))) + if (memcmp(float_val(*ep) + 1, pc, sizeof(double))) FAIL(); pc += TermWords(2); ++ep; break; case matchEqRef: { Eterm* epc = (Eterm*)pc; - if (!is_ref_rel(*ep,base)) + if (!is_ref(*ep)) FAIL(); - if (!eq_rel(make_internal_ref_rel(epc, epc), epc, *ep, base)) { + if (!eq_rel(make_internal_ref(epc), epc, *ep, base)) { FAIL(); } i = thing_arityval(*epc); @@ -2021,9 +2021,9 @@ restart: break; } case matchEqBig: - if (!is_big_rel(*ep,base)) + if (!is_big(*ep)) FAIL(); - tp = big_val_rel(*ep,base); + tp = big_val(*ep); { Eterm *epc = (Eterm *) pc; if (*tp != *epc) @@ -2193,8 +2193,8 @@ restart: sz = size_object_rel(term, base); top = HAllocX(build_proc, sz, HEAP_XTRA); if (in_flags & ERTS_PAM_CONTIGUOUS_TUPLE) { - ASSERT(is_tuple_rel(term,base)); - *esp++ = copy_shallow_rel(tuple_val_rel(term,base), sz, + ASSERT(is_tuple(term)); + *esp++ = copy_shallow_rel(tuple_val(term), sz, &top, &MSO(build_proc), base); } else { @@ -2741,7 +2741,7 @@ void db_do_update_element(DbUpdateHandle* handle, case _TAG_HEADER_HEAP_BIN: newval_sz = header_arity(*newp) + 1; if (is_boxed(oldval)) { - oldp = boxed_val_rel(oldval,old_base); + oldp = boxed_val(oldval); switch (*oldp & _TAG_HEADER_MASK) { case _TAG_HEADER_POS_BIG: case _TAG_HEADER_NEG_BIG: @@ -3023,7 +3023,7 @@ void db_finalize_resize(DbUpdateHandle* handle, Uint offset) tmp_offheap.first = NULL; { - copy_struct_rel(make_tuple_rel(tpl,tpl), handle->new_size, &top, + copy_struct_rel(make_tuple(tpl), handle->new_size, &top, &tmp_offheap, tpl, top); newDbTerm->first_oh = tmp_offheap.first; ASSERT((byte*)top == (newp + alloc_sz)); @@ -5228,7 +5228,7 @@ Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog, } else base = NULL; - res = db_prog_match(c_p, bprog, make_tuple_rel(obj->tpl,base), base, NULL, 0, + res = db_prog_match(c_p, bprog, make_tuple(obj->tpl), base, NULL, 0, ERTS_PAM_COPY_RESULT|ERTS_PAM_CONTIGUOUS_TUPLE, &dummy); if (is_value(res) && hpp!=NULL) { diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index c2128888c5..d07e61e599 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -306,7 +306,7 @@ ERTS_GLB_INLINE Eterm db_copy_object_from_ets(DbTableCommon* tb, DbTerm* bp, ERTS_GLB_INLINE int db_eq(DbTableCommon* tb, Eterm a, DbTerm* b) { if (!tb->compress) { - return eq_rel(a, NULL, make_tuple_rel(b->tpl,b->tpl), b->tpl); + return eq_rel(a, NULL, make_tuple(b->tpl), b->tpl); } else { return db_eq_comp(tb, a, b); diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 6d496ea5ee..500234c436 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -173,19 +173,19 @@ const Eterm * erts_maps_get(Eterm key, Eterm map) { Uint32 hx; - if (is_flatmap_rel(map, map_base)) { + if (is_flatmap(map)) { Eterm *ks, *vs; flatmap_t *mp; Uint n, i; - mp = (flatmap_t *)flatmap_val_rel(map, map_base); + mp = (flatmap_t *)flatmap_val(map); n = flatmap_get_size(mp); if (n == 0) { return NULL; } - ks = (Eterm *)tuple_val_rel(mp->keys, map_base) + 1; + ks = (Eterm *)tuple_val(mp->keys) + 1; vs = flatmap_get_values(mp); if (is_immed(key)) { @@ -203,10 +203,10 @@ erts_maps_get(Eterm key, Eterm map) } return NULL; } - ASSERT(is_hashmap_rel(map, map_base)); + ASSERT(is_hashmap(map)); hx = hashmap_make_hash(key); - return erts_hashmap_get_rel(hx, key, map, map_base); + return erts_hashmap_get(hx, key, map); } BIF_RETTYPE maps_find_2(BIF_ALIST_2) { @@ -1998,7 +1998,7 @@ erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) UseTmpHeapNoproc(2); ASSERT(is_boxed(node)); - ptr = boxed_val_rel(node, map_base); + ptr = boxed_val(node); hdr = *ptr; ASSERT(is_header(hdr)); ASSERT(is_hashmap_header_head(hdr)); @@ -2019,7 +2019,7 @@ erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) node = ptr[ix+1]; if (is_list(node)) { /* LEAF NODE [K|V] */ - ptr = list_val_rel(node,map_base); + ptr = list_val(node); res = eq_rel(CAR(ptr), map_base, key, NULL) ? &(CDR(ptr)) : NULL; break; } @@ -2027,7 +2027,7 @@ erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) hx = hashmap_shift_hash(th,hx,lvl,key); ASSERT(is_boxed(node)); - ptr = boxed_val_rel(node, map_base); + ptr = boxed_val(node); hdr = *ptr; ASSERT(is_header(hdr)); ASSERT(!is_hashmap_header_head(hdr)); diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 2f7c55829f..be6f791a4e 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -57,7 +57,6 @@ typedef struct flatmap_s { #define hashmap_size(x) (((hashmap_head_t*) hashmap_val(x))->size) -#define hashmap_size_rel(RTERM, BASE) hashmap_size(rterm2wterm(RTERM, BASE)) #define hashmap_make_hash(Key) make_internal_hash(Key) #define hashmap_restore_hash(Heap,Lvl,Key) \ @@ -104,13 +103,9 @@ Eterm erts_hashmap_from_array(ErtsHeapFactory*, Eterm *leafs, Uint n, int rejec Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n, Eterm k, Eterm v); -const Eterm * -erts_maps_get(Eterm key, Eterm map); -#define erts_maps_get_rel(A, B, B_BASE) erts_maps_get(A, B) +const Eterm *erts_maps_get(Eterm key, Eterm map); -const Eterm * -erts_hashmap_get(Uint32 hx, Eterm key, Eterm map); -#define erts_hashmap_get_rel(Hx, K, M, M_BASE) erts_hashmap_get(Hx, K, M) +const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map); /* hamt nodes v2.0 * diff --git a/erts/emulator/beam/erl_node_container_utils.h b/erts/emulator/beam/erl_node_container_utils.h index 0a4b18b748..b6a3531e28 100644 --- a/erts/emulator/beam/erl_node_container_utils.h +++ b/erts/emulator/beam/erl_node_container_utils.h @@ -328,8 +328,6 @@ extern ErtsPTab erts_port; : external_ref_channel_no((x))) #define is_ref(x) (is_internal_ref((x)) \ || is_external_ref((x))) -#define is_ref_rel(x,Base) (is_internal_ref_rel((x),Base) \ - || is_external_ref_rel((x),Base)) #define is_not_ref(x) (!is_ref(x)) #endif diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 95b3572372..7a40f775f2 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -123,7 +123,7 @@ is_printable_string(Eterm list, Eterm* base) int c; while(is_list(list)) { - Eterm* consp = list_val_rel(list, base); + Eterm* consp = list_val(list); Eterm hd = CAR(consp); if (!is_byte(hd)) @@ -308,7 +308,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, obj = (Eterm) popped.word; L_print_one_cons: { - Eterm* cons = list_val_rel(obj, obj_base); + Eterm* cons = list_val(obj); Eterm tl; obj = CAR(cons); @@ -423,7 +423,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, if (is_printable_string(obj, obj_base)) { int c; PRINT_CHAR(res, fn, arg, '"'); - nobj = list_val_rel(obj, obj_base); + nobj = list_val(obj); while (1) { if ((*dcount)-- <= 0) goto L_done; @@ -437,7 +437,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, } if (is_not_list(*nobj)) break; - nobj = list_val_rel(*nobj, obj_base); + nobj = list_val(*nobj); } PRINT_CHAR(res, fn, arg, '"'); } else { @@ -468,7 +468,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, } else { byte* bytep; - Uint bytesize = binary_size_rel(obj,obj_base); + Uint bytesize = binary_size(obj); Uint bitoffs; Uint bitsize; byte octet; diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 46900edfc7..ad4ef3c01a 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -977,27 +977,20 @@ _ET_DECLARE_CHECKED(struct erl_node_*,external_ref_node,Eterm) #define MAP_HEADER_VAL(Hdr) (((Hdr) >> (_HEADER_ARITY_OFFS + MAP_HEADER_TAG_SZ + MAP_HEADER_ARITY_SZ)) & (0xffff)) #define make_hashmap(x) make_boxed((Eterm*)(x)) -#define make_hashmap_rel make_boxed_rel #define is_hashmap(x) (is_boxed((x)) && is_hashmap_header(*boxed_val((x)))) #define is_not_hashmap(x) (!is_hashmap(x)) -#define is_hashmap_rel(RTERM,BASE) is_hashmap(rterm2wterm(RTERM,BASE)) #define is_hashmap_header(x) (((x) & (_HEADER_MAP_HASHMAP_HEAD_MASK)) == HAMT_SUBTAG_HEAD_ARRAY) #define hashmap_val(x) _unchecked_boxed_val((x)) -#define hashmap_val_rel(RTERM, BASE) hashmap_val(rterm2wterm(RTERM, BASE)) #define make_flatmap(x) make_boxed((Eterm*)(x)) -#define make_flatmap_rel(x, BASE) make_boxed_rel((Eterm*)(x),(BASE)) #define is_flatmap(x) (is_boxed((x)) && is_flatmap_header(*boxed_val((x)))) -#define is_flatmap_rel(RTERM,BASE) is_flatmap(rterm2wterm(RTERM,BASE)) #define is_not_flatmap(x) (!is_flatmap((x))) #define is_flatmap_header(x) (((x) & (_HEADER_MAP_SUBTAG_MASK)) == HAMT_SUBTAG_HEAD_FLATMAP) #define flatmap_val(x) (_unchecked_boxed_val((x))) -#define flatmap_val_rel(RTERM, BASE) flatmap_val(rterm2wterm(RTERM, BASE)) #define is_map_header(x) (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_MAP) #define is_map(x) (is_boxed((x)) && is_map_header(*boxed_val(x))) #define is_not_map(x) (!is_map(x)) -#define is_map_rel(RTERM,BASE) is_map(rterm2wterm(RTERM,BASE)) /* number tests */ @@ -1127,55 +1120,6 @@ extern unsigned tag_val_def(Wterm); #define FLOAT_BIG _NUMBER_CODE(FLOAT_DEF,BIG_DEF) #define FLOAT_FLOAT _NUMBER_CODE(FLOAT_DEF,FLOAT_DEF) -#define ptr2rel(PTR,BASE) (PTR) -#define rterm2wterm(REL,BASE) (REL) - -#define make_list_rel(PTR, BASE) make_list(ptr2rel(PTR,BASE)) -#define make_boxed_rel(PTR, BASE) make_boxed(ptr2rel(PTR,BASE)) -#define make_fun_rel make_boxed_rel -#define make_binary_rel make_boxed_rel -#define make_tuple_rel make_boxed_rel -#define make_external_rel make_boxed_rel -#define make_internal_ref_rel make_boxed_rel -#define make_big_rel make_boxed_rel - -#define binary_val_rel(RTERM, BASE) binary_val(rterm2wterm(RTERM, BASE)) -#define list_val_rel(RTERM, BASE) list_val(rterm2wterm(RTERM, BASE)) -#define boxed_val_rel(RTERM, BASE) boxed_val(rterm2wterm(RTERM, BASE)) -#define tuple_val_rel(RTERM, BASE) tuple_val(rterm2wterm(RTERM, BASE)) -#define export_val_rel(RTERM, BASE) export_val(rterm2wterm(RTERM, BASE)) -#define fun_val_rel(RTERM, BASE) fun_val(rterm2wterm(RTERM, BASE)) -#define big_val_rel(RTERM,BASE) big_val(rterm2wterm(RTERM,BASE)) -#define float_val_rel(RTERM,BASE) float_val(rterm2wterm(RTERM,BASE)) -#define internal_ref_val_rel(RTERM,BASE) internal_ref_val(rterm2wterm(RTERM,BASE)) - -#define external_thing_ptr_rel(RTERM, BASE) external_thing_ptr(rterm2wterm(RTERM, BASE)) -#define external_data_words_rel(RTERM,BASE) external_data_words(rterm2wterm(RTERM,BASE)) - -#define external_port_node_rel(RTERM,BASE) external_port_node(rterm2wterm(RTERM,BASE)) -#define external_port_data_rel(RTERM,BASE) external_port_data(rterm2wterm(RTERM,BASE)) - -#define is_external_pid_rel(RTERM,BASE) is_external_pid(rterm2wterm(RTERM,BASE)) -#define external_pid_node_rel(RTERM,BASE) external_pid_node(rterm2wterm(RTERM,BASE)) -#define external_pid_data_rel(RTERM,BASE) external_pid_data(rterm2wterm(RTERM,BASE)) - -#define is_binary_rel(RTERM,BASE) is_binary(rterm2wterm(RTERM,BASE)) -#define is_float_rel(RTERM,BASE) is_float(rterm2wterm(RTERM,BASE)) -#define is_fun_rel(RTERM,BASE) is_fun(rterm2wterm(RTERM,BASE)) -#define is_big_rel(RTERM,BASE) is_big(rterm2wterm(RTERM,BASE)) -#define is_export_rel(RTERM,BASE) is_export(rterm2wterm(RTERM,BASE)) -#define is_tuple_rel(RTERM,BASE) is_tuple(rterm2wterm(RTERM,BASE)) - -#define GET_DOUBLE_REL(RTERM, f, BASE) GET_DOUBLE(rterm2wterm(RTERM,BASE), f) - -#define ref_thing_ptr_rel(RTERM,BASE) ref_thing_ptr(rterm2wterm(RTERM,BASE)) -#define is_internal_ref_rel(RTERM,BASE) is_internal_ref(rterm2wterm(RTERM,BASE)) -#define is_external_rel(RTERM,BASE) is_external(rterm2wterm(RTERM,BASE)) -#define is_external_port_rel(RTERM,BASE) is_external_port(rterm2wterm(RTERM,BASE)) -#define is_external_ref_rel(RTERM,BASE) is_external_ref(rterm2wterm(RTERM,BASE)) - -#define external_node_rel(RTERM,BASE) external_node(rterm2wterm(RTERM,BASE)) - #define is_same(A,A_BASE,B,B_BASE) ((A)==(B)) #endif /* __ERL_TERM_H */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index c365b8859b..6b4b90cb06 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4091,10 +4091,10 @@ erts_port_control(Process* c_p, binp = NULL; if (is_binary(data) && binary_bitoffset(data) == 0) { - Eterm *ebinp = binary_val_rel(data, NULL); + Eterm *ebinp = binary_val(data); ASSERT(!tmp_alloced); if (*ebinp == HEADER_SUB_BIN) - ebinp = binary_val_rel(((ErlSubBin *) ebinp)->orig, NULL); + ebinp = binary_val(((ErlSubBin *) ebinp)->orig); if (*ebinp != HEADER_PROC_BIN) copy = 1; else { diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 28d1b38f75..9f919fa2ab 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2612,8 +2612,8 @@ tailrecur_ne: switch (primary_tag(a)) { case TAG_PRIMARY_LIST: if (is_list(b)) { - Eterm* aval = list_val_rel(a, a_base); - Eterm* bval = list_val_rel(b, b_base); + Eterm* aval = list_val(a); + Eterm* bval = list_val(b); while (1) { Eterm atmp = CAR(aval); Eterm btmp = CAR(bval); @@ -2633,22 +2633,22 @@ tailrecur_ne: b = btmp; goto tailrecur_ne; } - aval = list_val_rel(atmp, a_base); - bval = list_val_rel(btmp, b_base); + aval = list_val(atmp); + bval = list_val(btmp); } } break; /* not equal */ case TAG_PRIMARY_BOXED: { - Eterm hdr = *boxed_val_rel(a,a_base); + Eterm hdr = *boxed_val(a); switch (hdr & _TAG_HEADER_MASK) { case ARITYVAL_SUBTAG: { - aa = tuple_val_rel(a, a_base); - if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa) + aa = tuple_val(a); + if (!is_boxed(b) || *boxed_val(b) != *aa) goto not_equal; - bb = tuple_val_rel(b,b_base); + bb = tuple_val(b); if ((sz = arityval(*aa)) == 0) goto pop_next; ++aa; ++bb; @@ -2667,11 +2667,11 @@ tailrecur_ne: Uint a_bitoffs; Uint b_bitoffs; - if (!is_binary_rel(b,b_base)) { + if (!is_binary(b)) { goto not_equal; } - a_size = binary_size_rel(a,a_base); - b_size = binary_size_rel(b,b_base); + a_size = binary_size(a); + b_size = binary_size(b); if (a_size != b_size) { goto not_equal; } @@ -2687,9 +2687,9 @@ tailrecur_ne: } case EXPORT_SUBTAG: { - if (is_export_rel(b,b_base)) { - Export* a_exp = *((Export **) (export_val_rel(a,a_base) + 1)); - Export* b_exp = *((Export **) (export_val_rel(b,b_base) + 1)); + if (is_export(b)) { + Export* a_exp = *((Export **) (export_val(a) + 1)); + Export* b_exp = *((Export **) (export_val(b) + 1)); if (a_exp == b_exp) goto pop_next; } break; /* not equal */ @@ -2699,10 +2699,10 @@ tailrecur_ne: ErlFunThing* f1; ErlFunThing* f2; - if (!is_fun_rel(b,b_base)) + if (!is_fun(b)) goto not_equal; - f1 = (ErlFunThing *) fun_val_rel(a,a_base); - f2 = (ErlFunThing *) fun_val_rel(b,b_base); + f1 = (ErlFunThing *) fun_val(a); + f2 = (ErlFunThing *) fun_val(b); if (f1->fe->module != f2->fe->module || f1->fe->old_index != f2->fe->old_index || f1->fe->old_uniq != f2->fe->old_uniq || @@ -2720,15 +2720,15 @@ tailrecur_ne: ExternalThing *ap; ExternalThing *bp; - if(!is_external_rel(b,b_base)) + if(!is_external(b)) goto not_equal; - ap = external_thing_ptr_rel(a,a_base); - bp = external_thing_ptr_rel(b,b_base); + ap = external_thing_ptr(a); + bp = external_thing_ptr(b); if(ap->header == bp->header && ap->node == bp->node) { - ASSERT(1 == external_data_words_rel(a,a_base)); - ASSERT(1 == external_data_words_rel(b,b_base)); + ASSERT(1 == external_data_words(a)); + ASSERT(1 == external_data_words(b)); if (ap->data.ui[0] == bp->data.ui[0]) goto pop_next; } @@ -2749,11 +2749,11 @@ tailrecur_ne: ExternalThing* athing; ExternalThing* bthing; - if(!is_external_ref_rel(b,b_base)) + if(!is_external_ref(b)) goto not_equal; - athing = external_thing_ptr_rel(a,a_base); - bthing = external_thing_ptr_rel(b,b_base); + athing = external_thing_ptr(a); + bthing = external_thing_ptr(b); if(athing->node != bthing->node) goto not_equal; @@ -2765,12 +2765,12 @@ tailrecur_ne: goto ref_common; case REF_SUBTAG: - if (!is_internal_ref_rel(b,b_base)) + if (!is_internal_ref(b)) goto not_equal; { - RefThing* athing = ref_thing_ptr_rel(a,a_base); - RefThing* bthing = ref_thing_ptr_rel(b,b_base); + RefThing* athing = ref_thing_ptr(a); + RefThing* bthing = ref_thing_ptr(b); alen = internal_thing_ref_no_of_numbers(athing); blen = internal_thing_ref_no_of_numbers(bthing); anum = internal_thing_ref_numbers(athing); @@ -2820,10 +2820,10 @@ tailrecur_ne: { int i; - if (!is_big_rel(b,b_base)) + if (!is_big(b)) goto not_equal; - aa = big_val_rel(a,a_base); - bb = big_val_rel(b,b_base); + aa = big_val(a); + bb = big_val(b); if (*aa != *bb) goto not_equal; i = BIG_ARITY(aa); @@ -2838,19 +2838,19 @@ tailrecur_ne: FloatDef af; FloatDef bf; - if (is_float_rel(b,b_base)) { - GET_DOUBLE_REL(a, af, a_base); - GET_DOUBLE_REL(b, bf, b_base); + if (is_float(b)) { + GET_DOUBLE(a, af); + GET_DOUBLE(b, bf); if (af.fd == bf.fd) goto pop_next; } break; /* not equal */ } case MAP_SUBTAG: - if (is_flatmap_rel(a, a_base)) { - aa = flatmap_val_rel(a, a_base); - if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa) + if (is_flatmap(a)) { + aa = flatmap_val(a); + if (!is_boxed(b) || *boxed_val(b) != *aa) goto not_equal; - bb = flatmap_val_rel(b,b_base); + bb = flatmap_val(b); sz = flatmap_get_size((flatmap_t*)aa); if (sz != flatmap_get_size((flatmap_t*)bb)) goto not_equal; @@ -2862,11 +2862,11 @@ tailrecur_ne: goto term_array; } else { - if (!is_boxed(b) || *boxed_val_rel(b,b_base) != hdr) + if (!is_boxed(b) || *boxed_val(b) != hdr) goto not_equal; - aa = hashmap_val_rel(a, a_base) + 1; - bb = hashmap_val_rel(b, b_base) + 1; + aa = hashmap_val(a) + 1; + bb = hashmap_val(b) + 1; switch (hdr & _HEADER_MAP_SUBTAG_MASK) { case HAMT_SUBTAG_HEAD_ARRAY: aa++; bb++; @@ -3004,10 +3004,10 @@ Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) return cmp_atoms(a, b); } else if (is_both_small(a, b)) { return (signed_val(a) - signed_val(b)); - } else if (is_float_rel(a, a_base) && is_float_rel(b, b_base)) { + } else if (is_float(a) && is_float(b)) { FloatDef af, bf; - GET_DOUBLE_REL(a, af, a_base); - GET_DOUBLE_REL(b, bf, b_base); + GET_DOUBLE(a, af); + GET_DOUBLE(b, bf); return float_comp(af.fd, bf.fd); } return erts_cmp_compound(a,b,exact,eq_only); @@ -3111,9 +3111,9 @@ tailrecur_ne: if (is_internal_port(b)) { bnode = erts_this_node; bdata = internal_port_data(b); - } else if (is_external_port_rel(b,b_base)) { - bnode = external_port_node_rel(b,b_base); - bdata = external_port_data_rel(b,b_base); + } else if (is_external_port(b)) { + bnode = external_port_node(b); + bdata = external_port_data(b); } else { a_tag = PORT_DEF; goto mixed_types; @@ -3129,9 +3129,9 @@ tailrecur_ne: if (is_internal_pid(b)) { bnode = erts_this_node; bdata = internal_pid_data(b); - } else if (is_external_pid_rel(b,b_base)) { - bnode = external_pid_node_rel(b,b_base); - bdata = external_pid_data_rel(b,b_base); + } else if (is_external_pid(b)) { + bnode = external_pid_node(b); + bdata = external_pid_data(b); } else { a_tag = PID_DEF; goto mixed_types; @@ -3164,8 +3164,8 @@ tailrecur_ne: a_tag = LIST_DEF; goto mixed_types; } - aa = list_val_rel(a,a_base); - bb = list_val_rel(b,b_base); + aa = list_val(a); + bb = list_val(b); while (1) { Eterm atmp = CAR(aa); Eterm btmp = CAR(bb); @@ -3185,20 +3185,20 @@ tailrecur_ne: b = btmp; goto tailrecur_ne; } - aa = list_val_rel(atmp,a_base); - bb = list_val_rel(btmp,b_base); + aa = list_val(atmp); + bb = list_val(btmp); } case TAG_PRIMARY_BOXED: { - Eterm ahdr = *boxed_val_rel(a,a_base); + Eterm ahdr = *boxed_val(a); switch ((ahdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { case (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE): - if (!is_tuple_rel(b,b_base)) { + if (!is_tuple(b)) { a_tag = TUPLE_DEF; goto mixed_types; } - aa = tuple_val_rel(a,a_base); - bb = tuple_val_rel(b,b_base); + aa = tuple_val(a); + bb = tuple_val(b); /* compare the arities */ i = arityval(ahdr); /* get the arity*/ if (i != arityval(*bb)) { @@ -3214,18 +3214,18 @@ tailrecur_ne: { struct erts_cmp_hashmap_state* sp; if (is_flatmap_header(ahdr)) { - if (!is_flatmap_rel(b,b_base)) { - if (is_hashmap_rel(b,b_base)) { - aa = (Eterm *)flatmap_val_rel(a,a_base); - i = flatmap_get_size((flatmap_t*)aa) - hashmap_size_rel(b,b_base); + if (!is_flatmap(b)) { + if (is_hashmap(b)) { + aa = (Eterm *)flatmap_val(a); + i = flatmap_get_size((flatmap_t*)aa) - hashmap_size(b); ASSERT(i != 0); RETURN_NEQ(i); } a_tag = MAP_DEF; goto mixed_types; } - aa = (Eterm *)flatmap_val_rel(a,a_base); - bb = (Eterm *)flatmap_val_rel(b,b_base); + aa = (Eterm *)flatmap_val(a); + bb = (Eterm *)flatmap_val(b); i = flatmap_get_size((flatmap_t*)aa); if (i != flatmap_get_size((flatmap_t*)bb)) { @@ -3252,21 +3252,21 @@ tailrecur_ne: goto bodyrecur; } } - if (!is_hashmap_rel(b,b_base)) { - if (is_flatmap_rel(b,b_base)) { - bb = (Eterm *)flatmap_val_rel(b,b_base); - i = hashmap_size_rel(a,a_base) - flatmap_get_size((flatmap_t*)bb); + if (!is_hashmap(b)) { + if (is_flatmap(b)) { + bb = (Eterm *)flatmap_val(b); + i = hashmap_size(a) - flatmap_get_size((flatmap_t*)bb); ASSERT(i != 0); RETURN_NEQ(i); } a_tag = MAP_DEF; goto mixed_types; } - i = hashmap_size_rel(a,a_base) - hashmap_size_rel(b,b_base); + i = hashmap_size(a) - hashmap_size(b); if (i) { RETURN_NEQ(i); } - if (hashmap_size_rel(a,a_base) == 0) { + if (hashmap_size(a) == 0) { goto pop_next; } @@ -3301,31 +3301,31 @@ tailrecur_ne: goto bodyrecur; } case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - if (!is_float_rel(b,b_base)) { + if (!is_float(b)) { a_tag = FLOAT_DEF; goto mixed_types; } else { FloatDef af; FloatDef bf; - GET_DOUBLE_REL(a, af, a_base); - GET_DOUBLE_REL(b, bf, b_base); + GET_DOUBLE(a, af); + GET_DOUBLE(b, bf); ON_CMP_GOTO(float_comp(af.fd, bf.fd)); } case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - if (!is_big_rel(b,b_base)) { + if (!is_big(b)) { a_tag = BIG_DEF; goto mixed_types; } - ON_CMP_GOTO(big_comp(rterm2wterm(a,a_base), rterm2wterm(b,b_base))); + ON_CMP_GOTO(big_comp(a, b)); case (_TAG_HEADER_EXPORT >> _TAG_PRIMARY_SIZE): - if (!is_export_rel(b,b_base)) { + if (!is_export(b)) { a_tag = EXPORT_DEF; goto mixed_types; } else { - Export* a_exp = *((Export **) (export_val_rel(a,a_base) + 1)); - Export* b_exp = *((Export **) (export_val_rel(b,b_base) + 1)); + Export* a_exp = *((Export **) (export_val(a) + 1)); + Export* b_exp = *((Export **) (export_val(b) + 1)); if ((j = cmp_atoms(a_exp->code[0], b_exp->code[0])) != 0) { RETURN_NEQ(j); @@ -3337,12 +3337,12 @@ tailrecur_ne: } break; case (_TAG_HEADER_FUN >> _TAG_PRIMARY_SIZE): - if (!is_fun_rel(b,b_base)) { + if (!is_fun(b)) { a_tag = FUN_DEF; goto mixed_types; } else { - ErlFunThing* f1 = (ErlFunThing *) fun_val_rel(a,a_base); - ErlFunThing* f2 = (ErlFunThing *) fun_val_rel(b,b_base); + ErlFunThing* f1 = (ErlFunThing *) fun_val(a); + ErlFunThing* f2 = (ErlFunThing *) fun_val(b); Sint diff; diff = cmpbytes(atom_tab(atom_val(f1->fe->module))->name, @@ -3374,29 +3374,29 @@ tailrecur_ne: if (is_internal_pid(b)) { bnode = erts_this_node; bdata = internal_pid_data(b); - } else if (is_external_pid_rel(b,b_base)) { - bnode = external_pid_node_rel(b,b_base); - bdata = external_pid_data_rel(b,b_base); + } else if (is_external_pid(b)) { + bnode = external_pid_node(b); + bdata = external_pid_data(b); } else { a_tag = EXTERNAL_PID_DEF; goto mixed_types; } - anode = external_pid_node_rel(a,a_base); - adata = external_pid_data_rel(a,a_base); + anode = external_pid_node(a); + adata = external_pid_data(a); goto pid_common; case (_TAG_HEADER_EXTERNAL_PORT >> _TAG_PRIMARY_SIZE): if (is_internal_port(b)) { bnode = erts_this_node; bdata = internal_port_data(b); - } else if (is_external_port_rel(b,b_base)) { - bnode = external_port_node_rel(b,b_base); - bdata = external_port_data_rel(b,b_base); + } else if (is_external_port(b)) { + bnode = external_port_node(b); + bdata = external_port_data(b); } else { a_tag = EXTERNAL_PORT_DEF; goto mixed_types; } - anode = external_port_node_rel(a,a_base); - adata = external_port_data_rel(a,a_base); + anode = external_port_node(a); + adata = external_port_data(a); goto port_common; case (_TAG_HEADER_REF >> _TAG_PRIMARY_SIZE): /* @@ -3404,14 +3404,13 @@ tailrecur_ne: * (32-bit words), *not* ref data words. */ - - if (is_internal_ref_rel(b,b_base)) { - RefThing* bthing = ref_thing_ptr_rel(b,b_base); + if (is_internal_ref(b)) { + RefThing* bthing = ref_thing_ptr(b); bnode = erts_this_node; bnum = internal_thing_ref_numbers(bthing); blen = internal_thing_ref_no_of_numbers(bthing); - } else if(is_external_ref_rel(b,b_base)) { - ExternalThing* bthing = external_thing_ptr_rel(b,b_base); + } else if(is_external_ref(b)) { + ExternalThing* bthing = external_thing_ptr(b); bnode = bthing->node; bnum = external_thing_ref_numbers(bthing); blen = external_thing_ref_no_of_numbers(bthing); @@ -3420,7 +3419,7 @@ tailrecur_ne: goto mixed_types; } { - RefThing* athing = ref_thing_ptr_rel(a,a_base); + RefThing* athing = ref_thing_ptr(a); anode = erts_this_node; anum = internal_thing_ref_numbers(athing); alen = internal_thing_ref_no_of_numbers(athing); @@ -3453,13 +3452,13 @@ tailrecur_ne: RETURN_NEQ((Sint32) (anum[i] - bnum[i])); goto pop_next; case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE): - if (is_internal_ref_rel(b,b_base)) { - RefThing* bthing = ref_thing_ptr_rel(b,b_base); + if (is_internal_ref(b)) { + RefThing* bthing = ref_thing_ptr(b); bnode = erts_this_node; bnum = internal_thing_ref_numbers(bthing); blen = internal_thing_ref_no_of_numbers(bthing); - } else if (is_external_ref_rel(b,b_base)) { - ExternalThing* bthing = external_thing_ptr_rel(b,b_base); + } else if (is_external_ref(b)) { + ExternalThing* bthing = external_thing_ptr(b); bnode = bthing->node; bnum = external_thing_ref_numbers(bthing); blen = external_thing_ref_no_of_numbers(bthing); @@ -3468,7 +3467,7 @@ tailrecur_ne: goto mixed_types; } { - ExternalThing* athing = external_thing_ptr_rel(a,a_base); + ExternalThing* athing = external_thing_ptr(a); anode = athing->node; anum = external_thing_ref_numbers(athing); alen = external_thing_ref_no_of_numbers(athing); @@ -3476,13 +3475,13 @@ tailrecur_ne: goto ref_common; default: /* Must be a binary */ - ASSERT(is_binary_rel(a,a_base)); - if (!is_binary_rel(b,b_base)) { + ASSERT(is_binary(a)); + if (!is_binary(b)) { a_tag = BINARY_DEF; goto mixed_types; } else { - Uint a_size = binary_size_rel(a,a_base); - Uint b_size = binary_size_rel(b,b_base); + Uint a_size = binary_size(a); + Uint b_size = binary_size(b); Uint a_bitsize; Uint b_bitsize; Uint a_bitoffs; @@ -3596,7 +3595,7 @@ tailrecur_ne: } } else { big = double_to_big(f2.fd, big_buf, sizeof(big_buf)/sizeof(Eterm)); - j = big_comp(aw, rterm2wterm(big,big_buf)); + j = big_comp(aw, big); } if (_NUMBER_CODE(a_tag, b_tag) == FLOAT_BIG) { j = -j; -- cgit v1.2.3 From 17bcc73e511eee06ca64d51edb401f8340fe9abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 14:59:28 +0200 Subject: erts: Remove halfword pointer compression * Removed COMPRESS_POINTER and EXPAND_POINTER --- erts/emulator/beam/beam_bif_load.c | 6 ++--- erts/emulator/beam/beam_emu.c | 41 ++++++++++++++++------------------- erts/emulator/beam/erl_bif_op.c | 2 +- erts/emulator/beam/erl_gc.c | 4 ++-- erts/emulator/beam/erl_process.c | 2 +- erts/emulator/beam/erl_process_dump.c | 6 ++--- erts/emulator/beam/erl_term.h | 16 ++++++-------- erts/emulator/beam/external.c | 36 +++++++++++++++--------------- 8 files changed, 54 insertions(+), 59 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 0e192b1ebd..11508a1b39 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -913,7 +913,7 @@ any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) switch (primary_tag(val)) { case TAG_PRIMARY_BOXED: case TAG_PRIMARY_LIST: - if (in_area(EXPAND_POINTER(val), mod_start, mod_size)) { + if (in_area(val, mod_start, mod_size)) { return 1; } break; @@ -933,7 +933,7 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) switch (primary_tag(val)) { case TAG_PRIMARY_BOXED: case TAG_PRIMARY_LIST: - if (in_area(EXPAND_POINTER(val), mod_start, mod_size)) { + if (in_area(val, mod_start, mod_size)) { return 1; } break; @@ -943,7 +943,7 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) if (header_is_bin_matchstate(val)) { ErlBinMatchState *ms = (ErlBinMatchState*) p; ErlBinMatchBuffer *mb = &(ms->mb); - if (in_area(EXPAND_POINTER(mb->orig), mod_start, mod_size)) { + if (in_area(mb->orig, mod_start, mod_size)) { return 1; } } diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 0c6181077c..f4111c19f1 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -620,46 +620,45 @@ void** beam_ops; #define GetTupleElement(Src, Element, Dest) \ do { \ - tmp_arg1 = (Eterm) COMPRESS_POINTER(((unsigned char *) tuple_val(Src)) + \ - (Element)); \ - (Dest) = (*(Eterm *) EXPAND_POINTER(tmp_arg1)); \ + tmp_arg1 = (Eterm) (((unsigned char *) tuple_val(Src)) + (Element));\ + (Dest) = (*(Eterm *) tmp_arg1); \ } while (0) #define ExtractNextElement(Dest) \ tmp_arg1 += sizeof(Eterm); \ - (Dest) = (* (Eterm *) (((unsigned char *) EXPAND_POINTER(tmp_arg1)))) + (Dest) = (* (Eterm *) (((unsigned char *) tmp_arg1))) #define ExtractNextElement2(Dest) \ do { \ Eterm* ene_dstp = &(Dest); \ - ene_dstp[0] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[1]; \ - ene_dstp[1] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[2]; \ + ene_dstp[0] = ((Eterm *) tmp_arg1)[1]; \ + ene_dstp[1] = ((Eterm *) tmp_arg1)[2]; \ tmp_arg1 += sizeof(Eterm) + sizeof(Eterm); \ } while (0) #define ExtractNextElement3(Dest) \ do { \ Eterm* ene_dstp = &(Dest); \ - ene_dstp[0] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[1]; \ - ene_dstp[1] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[2]; \ - ene_dstp[2] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[3]; \ + ene_dstp[0] = ((Eterm *) tmp_arg1)[1]; \ + ene_dstp[1] = ((Eterm *) tmp_arg1)[2]; \ + ene_dstp[2] = ((Eterm *) tmp_arg1)[3]; \ tmp_arg1 += 3*sizeof(Eterm); \ } while (0) #define ExtractNextElement4(Dest) \ do { \ Eterm* ene_dstp = &(Dest); \ - ene_dstp[0] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[1]; \ - ene_dstp[1] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[2]; \ - ene_dstp[2] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[3]; \ - ene_dstp[3] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[4]; \ + ene_dstp[0] = ((Eterm *) tmp_arg1)[1]; \ + ene_dstp[1] = ((Eterm *) tmp_arg1)[2]; \ + ene_dstp[2] = ((Eterm *) tmp_arg1)[3]; \ + ene_dstp[3] = ((Eterm *) tmp_arg1)[4]; \ tmp_arg1 += 4*sizeof(Eterm); \ } while (0) #define ExtractElement(Element, Dest) \ do { \ tmp_arg1 += (Element); \ - (Dest) = (* (Eterm *) EXPAND_POINTER(tmp_arg1)); \ + (Dest) = (* (Eterm *) tmp_arg1); \ } while (0) #define EqualImmed(X, Y, Action) if (X != Y) { Action; } @@ -698,8 +697,7 @@ void** beam_ops; #define IsArity(Pointer, Arity, Fail) \ if (*(Eterm *) \ - EXPAND_POINTER(tmp_arg1 = (Eterm) \ - COMPRESS_POINTER(tuple_val(Pointer))) != (Arity)) \ + (tmp_arg1 = (Eterm) (tuple_val(Pointer))) != (Arity)) \ { \ Fail; \ } @@ -742,8 +740,7 @@ void** beam_ops; do { \ if (is_not_tuple(Src) || \ *(Eterm *) \ - EXPAND_POINTER(tmp_arg1 = \ - (Eterm) COMPRESS_POINTER(tuple_val(Src))) != Arity) { \ + (tmp_arg1 = (Eterm) (tuple_val(Src))) != Arity) { \ Fail; \ } \ } while (0) @@ -3218,7 +3215,7 @@ do { \ SWAPIN; if (next != NULL) { r(0) = reg[0]; - SET_CP(c_p, (BeamInstr *) EXPAND_POINTER(E[0])); + SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(0)); SET_I(next); Dispatch(); @@ -3267,7 +3264,7 @@ do { \ SWAPIN; if (next != NULL) { r(0) = reg[0]; - SET_CP(c_p, (BeamInstr *) EXPAND_POINTER(E[0])); + SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(1)); SET_I(next); Dispatch(); @@ -3299,7 +3296,7 @@ do { \ SWAPIN; if (next != NULL) { r(0) = reg[0]; - SET_CP(c_p, (BeamInstr *) EXPAND_POINTER(E[0])); + SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(0)); SET_I(next); Dispatchfun(); @@ -3347,7 +3344,7 @@ do { \ SWAPIN; if (next != NULL) { r(0) = reg[0]; - SET_CP(c_p, (BeamInstr *) EXPAND_POINTER(E[0])); + SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(1)); SET_I(next); Dispatchfun(); diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c index c9192fc420..d53a9e11ca 100644 --- a/erts/emulator/beam/erl_bif_op.c +++ b/erts/emulator/beam/erl_bif_op.c @@ -258,7 +258,7 @@ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2) BIF_RET(am_true); } } else if (is_export(arg1)) { - Export* exp = (Export *) EXPAND_POINTER((export_val(arg1))[1]); + Export* exp = (Export *) (export_val(arg1)[1]); if (exp->code[2] == (Uint) arity) { BIF_RET(am_true); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 6c335a9fe6..734f120e09 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1662,7 +1662,7 @@ disallow_heap_frag_ref_in_old_heap(Process* p) val = *hp++; switch (primary_tag(val)) { case TAG_PRIMARY_BOXED: - ptr = (Eterm *) EXPAND_POINTER(val); + ptr = (Eterm *) val; if (!in_area(ptr, old_heap, old_heap_size)) { if (in_area(ptr, new_heap, new_heap_size)) { abort(); @@ -1675,7 +1675,7 @@ disallow_heap_frag_ref_in_old_heap(Process* p) } break; case TAG_PRIMARY_LIST: - ptr = (Eterm *) EXPAND_POINTER(val); + ptr = (Eterm *) val; if (!in_area(ptr, old_heap, old_heap_size)) { if (in_area(ptr, new_heap, new_heap_size)) { abort(); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a21c1e5582..135f09c04d 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -12501,7 +12501,7 @@ stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg) } if (is_CP(x)) { - erts_print(to, to_arg, "Return addr %p (", (Eterm *) EXPAND_POINTER(x)); + erts_print(to, to_arg, "Return addr %p (", (Eterm *) x); print_function_from_pc(to, to_arg, cp_val(x)); erts_print(to, to_arg, ")\n"); yreg = 0; diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index 25f0b1ed38..3b8ae11e94 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -365,7 +365,7 @@ heap_dump(int to, void *to_arg, Eterm x) while (x != OUR_NIL) { if (is_CP(x)) { - next = (Eterm *) EXPAND_POINTER(x); + next = (Eterm *) x; } else if (is_list(x)) { ptr = list_val(x); if (ptr[0] != OUR_NIL) { @@ -378,7 +378,7 @@ heap_dump(int to, void *to_arg, Eterm x) ptr[1] = make_small(0); } x = ptr[0]; - ptr[0] = (Eterm) COMPRESS_POINTER(next); + ptr[0] = (Eterm) next; next = ptr + 1; continue; } @@ -408,7 +408,7 @@ heap_dump(int to, void *to_arg, Eterm x) ptr[0] = OUR_NIL; } else { x = ptr[arity]; - ptr[0] = (Eterm) COMPRESS_POINTER(next); + ptr[0] = (Eterm) next; next = ptr + arity - 1; continue; } diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index ad4ef3c01a..cc0b187240 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -25,8 +25,6 @@ typedef UWord Wterm; /* Full word terms */ #define HEAP_ON_C_STACK 1 #define CHECK_POINTER_MASK 0x0UL -#define COMPRESS_POINTER(AnUint) (AnUint) -#define EXPAND_POINTER(APointer) (APointer) struct erl_node_; /* Declared in erl_node_tables.h */ @@ -174,7 +172,7 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #define _boxed_precond(x) (is_boxed(x)) #define _is_aligned(x) (((Uint)(x) & 0x3) == 0) -#define _unchecked_make_boxed(x) ((Uint) COMPRESS_POINTER(x) + TAG_PRIMARY_BOXED) +#define _unchecked_make_boxed(x) ((Uint)(x) + TAG_PRIMARY_BOXED) _ET_DECLARE_CHECKED(Eterm,make_boxed,const Eterm*) #define make_boxed(x) _ET_APPLY(make_boxed,(x)) #if 1 @@ -185,12 +183,12 @@ _ET_DECLARE_CHECKED(int,is_boxed,Eterm) #else #define is_boxed(x) (((x) & _TAG_PRIMARY_MASK) == TAG_PRIMARY_BOXED) #endif -#define _unchecked_boxed_val(x) ((Eterm*) EXPAND_POINTER(((x) - TAG_PRIMARY_BOXED))) +#define _unchecked_boxed_val(x) ((Eterm*) ((x) - TAG_PRIMARY_BOXED)) _ET_DECLARE_CHECKED(Eterm*,boxed_val,Wterm) #define boxed_val(x) _ET_APPLY(boxed_val,(x)) /* cons cell ("list") access methods */ -#define _unchecked_make_list(x) ((Uint) COMPRESS_POINTER(x) + TAG_PRIMARY_LIST) +#define _unchecked_make_list(x) ((Uint)(x) + TAG_PRIMARY_LIST) _ET_DECLARE_CHECKED(Eterm,make_list,const Eterm*) #define make_list(x) _ET_APPLY(make_list,(x)) #if 1 @@ -203,7 +201,7 @@ _ET_DECLARE_CHECKED(int,is_not_list,Eterm) #define is_not_list(x) (!is_list((x))) #endif #define _list_precond(x) (is_list(x)) -#define _unchecked_list_val(x) ((Eterm*) EXPAND_POINTER((x) - TAG_PRIMARY_LIST)) +#define _unchecked_list_val(x) ((Eterm*) ((x) - TAG_PRIMARY_LIST)) _ET_DECLARE_CHECKED(Eterm*,list_val,Wterm) #define list_val(x) _ET_APPLY(list_val,(x)) @@ -214,7 +212,7 @@ _ET_DECLARE_CHECKED(Eterm*,list_val,Wterm) #define CDR(x) ((x)[1]) /* generic tagged pointer (boxed or list) access methods */ -#define _unchecked_ptr_val(x) ((Eterm*) EXPAND_POINTER((x) & ~((Uint) 0x3))) +#define _unchecked_ptr_val(x) ((Eterm*) ((x) & ~((Uint) 0x3))) #define ptr_val(x) _unchecked_ptr_val((x)) /*XXX*/ #define _unchecked_offset_ptr(x,offs) ((x)+((offs)*sizeof(Eterm))) #define offset_ptr(x,offs) _unchecked_offset_ptr(x,offs) /*XXX*/ @@ -1009,14 +1007,14 @@ _ET_DECLARE_CHECKED(struct erl_node_*,external_ref_node,Eterm) #error "fix yer arch, like" #endif -#define _unchecked_make_cp(x) ((Eterm) COMPRESS_POINTER(x)) +#define _unchecked_make_cp(x) ((Eterm)(x)) _ET_DECLARE_CHECKED(Eterm,make_cp,BeamInstr*) #define make_cp(x) _ET_APPLY(make_cp,(x)) #define is_not_CP(x) ((x) & _CPMASK) #define is_CP(x) (!is_not_CP(x)) -#define _unchecked_cp_val(x) ((BeamInstr*) EXPAND_POINTER(x)) +#define _unchecked_cp_val(x) ((BeamInstr*) (x)) _ET_DECLARE_CHECKED(BeamInstr*,cp_val,Eterm) #define cp_val(x) _ET_APPLY(cp_val,(x)) diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index f7b372d294..c3e93d1ad2 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2958,7 +2958,7 @@ dec_term(ErtsDistExternal *edep, case B2TDecodeList: objp = next - 2; while (n > 0) { - objp[0] = (Eterm) COMPRESS_POINTER(next); + objp[0] = (Eterm) next; objp[1] = make_list(next); next = objp; objp -= 2; @@ -2969,7 +2969,7 @@ dec_term(ErtsDistExternal *edep, case B2TDecodeTuple: objp = next - 1; while (n-- > 0) { - objp[0] = (Eterm) COMPRESS_POINTER(next); + objp[0] = (Eterm) next; next = objp; objp--; } @@ -3022,7 +3022,7 @@ dec_term(ErtsDistExternal *edep, while (next != NULL) { objp = next; - next = (Eterm *) EXPAND_POINTER(*objp); + next = (Eterm *) *objp; switch (*ep++) { case INTEGER_EXT: @@ -3153,7 +3153,7 @@ dec_term_atom_common: reds -= n; } while (n-- > 0) { - objp[0] = (Eterm) COMPRESS_POINTER(next); + objp[0] = (Eterm) next; next = objp; objp--; } @@ -3171,8 +3171,8 @@ dec_term_atom_common: *objp = make_list(hp); hp += 2 * n; objp = hp - 2; - objp[0] = (Eterm) COMPRESS_POINTER((objp+1)); - objp[1] = (Eterm) COMPRESS_POINTER(next); + objp[0] = (Eterm) (objp+1); + objp[1] = (Eterm) next; next = objp; objp -= 2; n--; @@ -3185,7 +3185,7 @@ dec_term_atom_common: reds -= n; } while (n > 0) { - objp[0] = (Eterm) COMPRESS_POINTER(next); + objp[0] = (Eterm) next; objp[1] = make_list(next); next = objp; objp -= 2; @@ -3576,7 +3576,7 @@ dec_term_atom_common: * The list of maps is for later validation. */ - mp->thing_word = (Eterm) COMPRESS_POINTER(maps_list); + mp->thing_word = (Eterm) maps_list; maps_list = (Eterm *) mp; mp->size = size; @@ -3584,8 +3584,8 @@ dec_term_atom_common: *objp = make_flatmap(mp); for (n = size; n; n--) { - *vptr = (Eterm) COMPRESS_POINTER(next); - *kptr = (Eterm) COMPRESS_POINTER(vptr); + *vptr = (Eterm) next; + *kptr = (Eterm) vptr; next = kptr; vptr--; kptr--; @@ -3599,8 +3599,8 @@ dec_term_atom_common: hamt->leaf_array = hp; for (n = size; n; n--) { - CDR(hp) = (Eterm) COMPRESS_POINTER(next); - CAR(hp) = (Eterm) COMPRESS_POINTER(&CDR(hp)); + CDR(hp) = (Eterm) next; + CAR(hp) = (Eterm) &CDR(hp); next = &CAR(hp); hp += 2; } @@ -3677,11 +3677,11 @@ dec_term_atom_common: /* Environment */ for (i = num_free-1; i >= 0; i--) { - funp->env[i] = (Eterm) COMPRESS_POINTER(next); + funp->env[i] = (Eterm) next; next = funp->env + i; } /* Creator */ - funp->creator = (Eterm) COMPRESS_POINTER(next); + funp->creator = (Eterm) next; next = &(funp->creator); break; } @@ -3750,7 +3750,7 @@ dec_term_atom_common: /* Environment */ for (i = num_free-1; i >= 0; i--) { - funp->env[i] = (Eterm) COMPRESS_POINTER(next); + funp->env[i] = (Eterm) next; next = funp->env + i; } break; @@ -3846,11 +3846,11 @@ dec_term_atom_common: */ while (maps_list) { - next = (Eterm *)(EXPAND_POINTER(*maps_list)); + next = (Eterm *) *maps_list; *maps_list = MAP_HEADER_FLATMAP; if (!erts_validate_and_sort_flatmap((flatmap_t*)maps_list)) goto error; - maps_list = next; + maps_list = next; } ASSERT(hp <= factory->hp_end @@ -3876,7 +3876,7 @@ dec_term_atom_common: PSTACK_DESTROY(hamt_array); } - ASSERT((Eterm*)EXPAND_POINTER(*dbg_resultp) != NULL); + ASSERT((Eterm*)*dbg_resultp != NULL); if (ctx) { ctx->state = B2TDone; -- cgit v1.2.3 From 287db3cf7ecb1bb23664cf872508675461a4be56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 15:19:20 +0200 Subject: erts: Remove halfword heap relative comparisions * Removed cmp_rel, cmp_rel_term and eq_rel --- erts/emulator/beam/erl_db_hash.c | 7 ++----- erts/emulator/beam/erl_db_tree.c | 13 ++++++------- erts/emulator/beam/erl_db_util.c | 10 +++++----- erts/emulator/beam/erl_db_util.h | 4 ++-- erts/emulator/beam/erl_map.c | 4 ++-- erts/emulator/beam/erl_utils.h | 3 --- 6 files changed, 17 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 80563e1f02..e4f4a7beb0 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -460,9 +460,6 @@ static ERTS_INLINE void try_shrink(DbTableHash* tb) } } -#define EQ_REL(x,y,y_base) \ - (is_same(x,NULL,y,y_base) || (is_not_both_immed((x),(y)) && eq_rel((x),NULL,(y),y_base))) - /* Is this a live object (not pseodo-deleted) with the specified key? */ static ERTS_INLINE int has_live_key(DbTableHash* tb, HashDbTerm* b, @@ -472,7 +469,7 @@ static ERTS_INLINE int has_live_key(DbTableHash* tb, HashDbTerm* b, else { Eterm itemKey = GETKEY(tb, b->dbterm.tpl); ASSERT(!is_header(itemKey)); - return EQ_REL(key, itemKey, b->dbterm.tpl); + return EQ(key, itemKey); } } @@ -485,7 +482,7 @@ static ERTS_INLINE int has_key(DbTableHash* tb, HashDbTerm* b, else { Eterm itemKey = GETKEY(tb, b->dbterm.tpl); ASSERT(!is_header(itemKey)); - return EQ_REL(key, itemKey, b->dbterm.tpl); + return EQ(key, itemKey); } } diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 3b698a6adf..d9eac550af 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -576,8 +576,7 @@ static int db_prev_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret) static ERTS_INLINE Sint cmp_key(DbTableTree* tb, Eterm key, Eterm* key_base, TreeDbTerm* obj) { - return cmp_rel(key, key_base, - GETKEY(tb,obj->dbterm.tpl), obj->dbterm.tpl); + return CMP(key, GETKEY(tb,obj->dbterm.tpl)); } static ERTS_INLINE int cmp_key_eq(DbTableTree* tb, Eterm key, Eterm* key_base, @@ -585,7 +584,7 @@ static ERTS_INLINE int cmp_key_eq(DbTableTree* tb, Eterm key, Eterm* key_base, { Eterm obj_key = GETKEY(tb,obj->dbterm.tpl); return is_same(key, key_base, obj_key, obj->dbterm.tpl) - || cmp_rel(key, key_base, obj_key, obj->dbterm.tpl) == 0; + || CMP(key, obj_key) == 0; } static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail) @@ -2742,7 +2741,7 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) switch (a & _TAG_PRIMARY_MASK) { case TAG_PRIMARY_LIST: if (!is_list(b)) { - return cmp_rel(a,NULL,b,b_base); + return CMP(a,b); } aa = list_val(a); bb = list_val(b); @@ -2758,12 +2757,12 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) } case TAG_PRIMARY_BOXED: if ((b & _TAG_PRIMARY_MASK) != TAG_PRIMARY_BOXED) { - return cmp_rel(a,NULL,b,b_base); + return CMP(a,b); } a_hdr = ((*boxed_val(a)) & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE; b_hdr = ((*boxed_val(b)) & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE; if (a_hdr != b_hdr) { - return cmp_rel(a, NULL, b, b_base); + return CMP(a,b); } if (a_hdr == (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE)) { aa = tuple_val(a); @@ -2781,7 +2780,7 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) } /* Drop through */ default: - return cmp_rel(a, NULL, b, b_base); + return CMP(a,b); } } diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 432ca297e9..7bf45bd586 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1990,13 +1990,13 @@ restart: break; case matchCmp: n = *pc++; - if (!eq_rel(variables[n].term, base, *ep, base)) + if (!EQ(variables[n].term, *ep)) FAIL(); ++ep; break; case matchEqBin: t = (Eterm) *pc++; - if (!eq_rel(t,NULL,*ep,base)) + if (!EQ(t,*ep)) FAIL(); ++ep; break; @@ -2012,7 +2012,7 @@ restart: Eterm* epc = (Eterm*)pc; if (!is_ref(*ep)) FAIL(); - if (!eq_rel(make_internal_ref(epc), epc, *ep, base)) { + if (!EQ(make_internal_ref(epc), *ep)) { FAIL(); } i = thing_arityval(*epc); @@ -3063,7 +3063,7 @@ Eterm db_copy_from_comp(DbTableCommon* tb, DbTerm* bp, Eterm** hpp, ASSERT((*hpp - hp) <= bp->size); #ifdef DEBUG_CLONE - ASSERT(eq_rel(make_tuple(hp),NULL,make_tuple(bp->debug_clone),bp->debug_clone)); + ASSERT(EQ(make_tuple(hp),make_tuple(bp->debug_clone))); #endif return make_tuple(hp); } @@ -3087,7 +3087,7 @@ Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p, *hpp = erts_produce_heap(&factory, extra, 0); erts_factory_close(&factory); #ifdef DEBUG_CLONE - ASSERT(eq_rel(copy, NULL, obj->debug_clone[pos], obj->debug_clone)); + ASSERT(EQ(copy, obj->debug_clone[pos])); #endif return copy; } diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index d07e61e599..36c15496b9 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -287,7 +287,7 @@ ERTS_GLB_INLINE Eterm db_copy_key(Process* p, DbTable* tb, DbTerm* obj) Uint size = size_object_rel(key, obj->tpl); Eterm* hp = HAlloc(p, size); Eterm res = copy_struct_rel(key, size, &hp, &MSO(p), obj->tpl, NULL); - ASSERT(eq_rel(res,NULL,key,obj->tpl)); + ASSERT(EQ(res,key)); return res; } } @@ -306,7 +306,7 @@ ERTS_GLB_INLINE Eterm db_copy_object_from_ets(DbTableCommon* tb, DbTerm* bp, ERTS_GLB_INLINE int db_eq(DbTableCommon* tb, Eterm a, DbTerm* b) { if (!tb->compress) { - return eq_rel(a, NULL, make_tuple(b->tpl), b->tpl); + return EQ(a, make_tuple(b->tpl)); } else { return db_eq_comp(tb, a, b); diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 500234c436..8f376fe595 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -197,7 +197,7 @@ erts_maps_get(Eterm key, Eterm map) } for (i = 0; i < n; i++) { - if (eq_rel(ks[i], map_base, key, NULL)) { + if (EQ(ks[i], key)) { return &vs[i]; } } @@ -2020,7 +2020,7 @@ erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) if (is_list(node)) { /* LEAF NODE [K|V] */ ptr = list_val(node); - res = eq_rel(CAR(ptr), map_base, key, NULL) ? &(CDR(ptr)) : NULL; + res = EQ(CAR(ptr), key) ? &(CDR(ptr)) : NULL; break; } diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 1732579d04..4058d63eaf 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -158,14 +158,11 @@ erts_dsprintf_buf_t *erts_create_tmp_dsbuf(Uint); void erts_destroy_tmp_dsbuf(erts_dsprintf_buf_t *); int eq(Eterm, Eterm); -#define eq_rel(A,A_BASE,B,B_BASE) eq(A,B) #define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y)))) Sint erts_cmp(Eterm, Eterm, int, int); Sint cmp(Eterm a, Eterm b); -#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp(A,B,0,0) -#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp(A,B,1,0) #define CMP(A,B) erts_cmp(A,B,0,0) #define CMP_TERM(A,B) erts_cmp(A,B,1,0) #define CMP_EQ_ONLY(A,B) erts_cmp(A,B,0,1) -- cgit v1.2.3 From 256c60d885b6fe0e8a715fa9bfdd371ad14c8857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 16:01:10 +0200 Subject: erts: Remove halfword object manipulation * Remove macros size_object_rel, copy_struct_rel and copy_shallow_rel --- erts/emulator/beam/erl_db_tree.c | 36 ++++++++++++++++++------------------ erts/emulator/beam/erl_db_util.c | 33 +++++++++++++++------------------ erts/emulator/beam/erl_db_util.h | 6 +++--- erts/emulator/beam/global.h | 7 ------- 4 files changed, 36 insertions(+), 46 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index d9eac550af..0c0218f5f9 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -1002,9 +1002,9 @@ static int db_select_continue_tree(Process *p, } key = GETKEY(tb, sc.lastobj); - sz = size_object_rel(key,sc.lastobj); + sz = size_object(key); hp = HAlloc(p, 9 + sz); - key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL); + key = copy_struct(key, sz, &hp, &MSO(p)); continuation = TUPLE8 (hp, tptr[1], @@ -1045,9 +1045,9 @@ static int db_select_continue_tree(Process *p, } } /* Not done yet, let's trap. */ - sz = size_object_rel(key,sc.lastobj); + sz = size_object(key); hp = HAlloc(p, 9 + sz); - key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL); + key = copy_struct(key, sz, &hp, &MSO(p)); continuation = TUPLE8 (hp, tptr[1], @@ -1152,9 +1152,9 @@ static int db_select_tree(Process *p, DbTable *tbl, } key = GETKEY(tb, sc.lastobj); - sz = size_object_rel(key, sc.lastobj); + sz = size_object(key); hp = HAlloc(p, 9 + sz + PROC_BIN_SIZE); - key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL); + key = copy_struct(key, sz, &hp, &MSO(p)); if (mpi.all_objects) (mpi.mp)->flags |= BIN_FLAG_ALL_OBJECTS; mpb=db_make_mp_binary(p,mpi.mp,&hp); @@ -1250,7 +1250,7 @@ static int db_select_count_continue_tree(Process *p, RET_TO_BIF(make_small(sc.got),DB_ERROR_NONE); } /* Not done yet, let's trap. */ - sz = size_object_rel(key, sc.lastobj); + sz = size_object(key); if (IS_USMALL(0, sc.got)) { hp = HAlloc(p, sz + 6); egot = make_small(sc.got); @@ -1260,7 +1260,7 @@ static int db_select_count_continue_tree(Process *p, egot = uint_to_big(sc.got, hp); hp += BIG_UINT_HEAP_SIZE; } - key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL); + key = copy_struct(key, sz, &hp, &MSO(p)); continuation = TUPLE5 (hp, tptr[1], @@ -1346,7 +1346,7 @@ static int db_select_count_tree(Process *p, DbTable *tbl, } key = GETKEY(tb, sc.lastobj); - sz = size_object_rel(key, sc.lastobj); + sz = size_object(key); if (IS_USMALL(0, sc.got)) { hp = HAlloc(p, sz + PROC_BIN_SIZE + 6); egot = make_small(sc.got); @@ -1356,7 +1356,7 @@ static int db_select_count_tree(Process *p, DbTable *tbl, egot = uint_to_big(sc.got, hp); hp += BIG_UINT_HEAP_SIZE; } - key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL); + key = copy_struct(key, sz, &hp, &MSO(p)); if (mpi.all_objects) (mpi.mp)->flags |= BIN_FLAG_ALL_OBJECTS; mpb = db_make_mp_binary(p,mpi.mp,&hp); @@ -1482,9 +1482,9 @@ static int db_select_chunk_tree(Process *p, DbTable *tbl, } key = GETKEY(tb, sc.lastobj); - sz = size_object_rel(key, sc.lastobj); + sz = size_object(key); hp = HAlloc(p, 9 + sz + PROC_BIN_SIZE); - key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL); + key = copy_struct(key, sz, &hp, &MSO(p)); if (mpi.all_objects) (mpi.mp)->flags |= BIN_FLAG_ALL_OBJECTS; mpb = db_make_mp_binary(p,mpi.mp,&hp); @@ -1507,9 +1507,9 @@ static int db_select_chunk_tree(Process *p, DbTable *tbl, } key = GETKEY(tb, sc.lastobj); - sz = size_object_rel(key, sc.lastobj); + sz = size_object(key); hp = HAlloc(p, 9 + sz + PROC_BIN_SIZE); - key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastobj, NULL); + key = copy_struct(key, sz, &hp, &MSO(p)); if (mpi.all_objects) (mpi.mp)->flags |= BIN_FLAG_ALL_OBJECTS; @@ -1598,7 +1598,7 @@ static int db_select_delete_continue_tree(Process *p, RET_TO_BIF(erts_make_integer(sc.accum,p),DB_ERROR_NONE); } /* Not done yet, let's trap. */ - sz = size_object_rel(key, sc.lastterm->dbterm.tpl); + sz = size_object(key); if (IS_USMALL(0, sc.accum)) { hp = HAlloc(p, sz + 6); eaccsum = make_small(sc.accum); @@ -1608,7 +1608,7 @@ static int db_select_delete_continue_tree(Process *p, eaccsum = uint_to_big(sc.accum, hp); hp += BIG_UINT_HEAP_SIZE; } - key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastterm->dbterm.tpl, NULL); + key = copy_struct(key, sz, &hp, &MSO(p)); continuation = TUPLE5 (hp, tptr[1], @@ -1695,7 +1695,7 @@ static int db_select_delete_tree(Process *p, DbTable *tbl, } key = GETKEY(tb, (sc.lastterm)->dbterm.tpl); - sz = size_object_rel(key, sc.lastterm->dbterm.tpl); + sz = size_object(key); if (IS_USMALL(0, sc.accum)) { hp = HAlloc(p, sz + PROC_BIN_SIZE + 6); eaccsum = make_small(sc.accum); @@ -1705,7 +1705,7 @@ static int db_select_delete_tree(Process *p, DbTable *tbl, eaccsum = uint_to_big(sc.accum, hp); hp += BIG_UINT_HEAP_SIZE; } - key = copy_struct_rel(key, sz, &hp, &MSO(p), sc.lastterm->dbterm.tpl, NULL); + key = copy_struct(key, sz, &hp, &MSO(p)); mpb = db_make_mp_binary(p,mpi.mp,&hp); continuation = TUPLE5 diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 7bf45bd586..41b32fc0e7 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1751,9 +1751,9 @@ static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity) static ERTS_INLINE Eterm copy_object_rel(Process* p, Eterm term, Eterm* base) { if (!is_immed(term)) { - Uint sz = size_object_rel(term, base); + Uint sz = size_object(term); Eterm* top = HAllocX(p, sz, HEAP_XTRA); - return copy_struct_rel(term, sz, &top, &MSO(p), base, NULL); + return copy_struct(term, sz, &top, &MSO(p)); } return term; } @@ -2190,16 +2190,14 @@ restart: if (in_flags & ERTS_PAM_COPY_RESULT) { Uint sz; Eterm* top; - sz = size_object_rel(term, base); + sz = size_object(term); top = HAllocX(build_proc, sz, HEAP_XTRA); if (in_flags & ERTS_PAM_CONTIGUOUS_TUPLE) { ASSERT(is_tuple(term)); - *esp++ = copy_shallow_rel(tuple_val(term), sz, - &top, &MSO(build_proc), base); + *esp++ = copy_shallow(tuple_val(term), sz, &top, &MSO(build_proc)); } else { - *esp++ = copy_struct_rel(term, sz, &top, &MSO(build_proc), - base, NULL); + *esp++ = copy_struct(term, sz, &top, &MSO(build_proc)); } } else { @@ -2767,7 +2765,7 @@ void db_do_update_element(DbUpdateHandle* handle, newval_sz = is_immed(newval) ? 0 : size_object(newval); new_size_set: - oldval_sz = is_immed(oldval) ? 0 : size_object_rel(oldval,old_base); + oldval_sz = is_immed(oldval) ? 0 : size_object(oldval); both_size_set: handle->new_size = handle->new_size - oldval_sz + newval_sz; @@ -2873,7 +2871,7 @@ static void* copy_to_comp(DbTableCommon* tb, Eterm obj, DbTerm* dest, tpl[arity + 1] = alloc_size; tmp_offheap.first = NULL; - tpl[tb->keypos] = copy_struct_rel(key, size_object(key), &top.ep, &tmp_offheap, NULL, tpl); + tpl[tb->keypos] = copy_struct(key, size_object(key), &top.ep, &tmp_offheap); dest->first_oh = tmp_offheap.first; for (i=1; i<=arity; i++) { if (i != tb->keypos) { @@ -2892,7 +2890,7 @@ static void* copy_to_comp(DbTableCommon* tb, Eterm obj, DbTerm* dest, Eterm* dbg_top = erts_alloc(ERTS_ALC_T_DB_TERM, dest->size * sizeof(Eterm)); dest->debug_clone = dbg_top; tmp_offheap.first = dest->first_oh; - copy_struct_rel(obj, dest->size, &dbg_top, &tmp_offheap, NULL, dbg_top); + copy_struct(obj, dest->size, &dbg_top, &tmp_offheap); dest->first_oh = tmp_offheap.first; ASSERT(dbg_top == dest->debug_clone + dest->size); } @@ -2939,7 +2937,7 @@ void* db_store_term(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj) newp->size = size; top = newp->tpl; tmp_offheap.first = NULL; - copy_struct_rel(obj, size, &top, &tmp_offheap, NULL, top); + copy_struct(obj, size, &top, &tmp_offheap); newp->first_oh = tmp_offheap.first; #ifdef DEBUG_CLONE newp->debug_clone = NULL; @@ -3023,8 +3021,7 @@ void db_finalize_resize(DbUpdateHandle* handle, Uint offset) tmp_offheap.first = NULL; { - copy_struct_rel(make_tuple(tpl), handle->new_size, &top, - &tmp_offheap, tpl, top); + copy_struct(make_tuple(tpl), handle->new_size, &top, &tmp_offheap); newDbTerm->first_oh = tmp_offheap.first; ASSERT((byte*)top == (newp + alloc_sz)); } @@ -3041,9 +3038,9 @@ Eterm db_copy_from_comp(DbTableCommon* tb, DbTerm* bp, Eterm** hpp, hp[0] = bp->tpl[0]; *hpp += arity + 1; - hp[tb->keypos] = copy_struct_rel(bp->tpl[tb->keypos], - size_object_rel(bp->tpl[tb->keypos], bp->tpl), - hpp, off_heap, bp->tpl, NULL); + hp[tb->keypos] = copy_struct(bp->tpl[tb->keypos], + size_object(bp->tpl[tb->keypos]), + hpp, off_heap); erts_factory_static_init(&factory, *hpp, bp->size - (arity+1), off_heap); @@ -3092,9 +3089,9 @@ Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p, return copy; } else { - Uint sz = size_object_rel(obj->tpl[pos], obj->tpl); + Uint sz = size_object(obj->tpl[pos]); *hpp = HAlloc(p, sz + extra); - return copy_struct_rel(obj->tpl[pos], sz, hpp, &MSO(p), obj->tpl, NULL); + return copy_struct(obj->tpl[pos], sz, hpp, &MSO(p)); } } diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 36c15496b9..10899bb3e7 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -284,9 +284,9 @@ ERTS_GLB_INLINE Eterm db_copy_key(Process* p, DbTable* tb, DbTerm* obj) Eterm key = GETKEY(tb, obj->tpl); if IS_CONST(key) return key; else { - Uint size = size_object_rel(key, obj->tpl); + Uint size = size_object(key); Eterm* hp = HAlloc(p, size); - Eterm res = copy_struct_rel(key, size, &hp, &MSO(p), obj->tpl, NULL); + Eterm res = copy_struct(key, size, &hp, &MSO(p)); ASSERT(EQ(res,key)); return res; } @@ -299,7 +299,7 @@ ERTS_GLB_INLINE Eterm db_copy_object_from_ets(DbTableCommon* tb, DbTerm* bp, return db_copy_from_comp(tb, bp, hpp, off_heap); } else { - return copy_shallow_rel(bp->tpl, bp->size, hpp, off_heap, bp->tpl); + return copy_shallow(bp->tpl, bp->size, hpp, off_heap); } } diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 1d2d75bc1e..3559f0cd13 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -955,16 +955,9 @@ void erl_error(char*, va_list); /* copy.c */ Eterm copy_object(Eterm, Process*); - Uint size_object(Eterm); -#define size_object_rel(A,B) size_object(A) - Eterm copy_struct(Eterm, Uint, Eterm**, ErlOffHeap*); -#define copy_struct_rel(OBJ,SZ,HPP,OH, SB,DB) copy_struct(OBJ,SZ,HPP,OH) - Eterm copy_shallow(Eterm*, Uint, Eterm**, ErlOffHeap*); -#define copy_shallow_rel(A,B,C,D, BASE) copy_shallow(A,B,C,D) - void move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first, Eterm* refs, unsigned nrefs); -- cgit v1.2.3 From ba020bd06c34eaf5b450495a852f31357ef042b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 16:53:49 +0200 Subject: erts: Remove halfword copy_object_rel Near duplication of copy_object but with base ptr that is no longer used. --- erts/emulator/beam/erl_db_util.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 41b32fc0e7..ae9a853411 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1748,17 +1748,6 @@ static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity) return ret; } -static ERTS_INLINE Eterm copy_object_rel(Process* p, Eterm term, Eterm* base) -{ - if (!is_immed(term)) { - Uint sz = size_object(term); - Eterm* top = HAllocX(p, sz, HEAP_XTRA); - return copy_struct(term, sz, &top, &MSO(p)); - } - return term; -} - - /* ** Execution of the match program, this is Pam. ** May return THE_NON_VALUE, which is a bailout. @@ -2167,12 +2156,11 @@ restart: break; case matchPushVResult: if (!(in_flags & ERTS_PAM_COPY_RESULT)) goto case_matchPushV; - /* Build (NULL-based) copy on callers heap */ n = *pc++; ASSERT(is_value(variables[n].term)); ASSERT(!variables[n].proc); - variables[n].term = copy_object_rel(c_p, variables[n].term, base); + variables[n].term = copy_object(variables[n].term, c_p); *esp++ = variables[n].term; #ifdef DEBUG variables[n].proc = c_p; -- cgit v1.2.3 From d6712b1c54de471beb1784bb329ea217767f70ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 17:03:48 +0200 Subject: erts: Reinstate copy_object over-allocation optimization --- erts/emulator/beam/copy.c | 35 ++++++++++++++++++----------------- erts/emulator/beam/erl_db_util.c | 2 +- erts/emulator/beam/global.h | 4 +++- 3 files changed, 22 insertions(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 38c40f4e7d..3de2c10203 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -40,29 +40,30 @@ static void move_one_frag(Eterm** hpp, ErlHeapFragment*, ErlOffHeap*); /* * Copy object "obj" to process p. */ -Eterm -copy_object(Eterm obj, Process* to) -{ - Uint size = size_object(obj); - Eterm* hp = HAlloc(to, size); - Eterm res; +Eterm copy_object_x(Eterm obj, Process* to, Uint extra) { + if (!is_immed(obj)) { + Uint size = size_object(obj); + Eterm* hp = HAllocX(to, size, extra); + Eterm res; #ifdef USE_VM_PROBES - if (DTRACE_ENABLED(copy_object)) { - DTRACE_CHARBUF(proc_name, 64); + if (DTRACE_ENABLED(copy_object)) { + DTRACE_CHARBUF(proc_name, 64); - erts_snprintf(proc_name, sizeof(DTRACE_CHARBUF_NAME(proc_name)), - "%T", to->common.id); - DTRACE2(copy_object, proc_name, size); - } + erts_snprintf(proc_name, sizeof(DTRACE_CHARBUF_NAME(proc_name)), + "%T", to->common.id); + DTRACE2(copy_object, proc_name, size); + } #endif - res = copy_struct(obj, size, &hp, &to->off_heap); + res = copy_struct(obj, size, &hp, &to->off_heap); #ifdef DEBUG - if (eq(obj, res) == 0) { - erl_exit(ERTS_ABORT_EXIT, "copy not equal to source\n"); - } + if (eq(obj, res) == 0) { + erl_exit(ERTS_ABORT_EXIT, "copy not equal to source\n"); + } #endif - return res; + return res; + } + return obj; } /* diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index ae9a853411..8047711e6f 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2160,7 +2160,7 @@ restart: n = *pc++; ASSERT(is_value(variables[n].term)); ASSERT(!variables[n].proc); - variables[n].term = copy_object(variables[n].term, c_p); + variables[n].term = copy_object_x(variables[n].term, c_p, HEAP_XTRA); *esp++ = variables[n].term; #ifdef DEBUG variables[n].proc = c_p; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 3559f0cd13..870afb414f 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -954,7 +954,9 @@ __decl_noreturn void __noreturn erl_exit_flush_async(int n, char*, ...); void erl_error(char*, va_list); /* copy.c */ -Eterm copy_object(Eterm, Process*); +Eterm copy_object_x(Eterm, Process*, Uint); +#define copy_object(Term, Proc) copy_object_x(Term,Proc,0) + Uint size_object(Eterm); Eterm copy_struct(Eterm, Uint, Eterm**, ErlOffHeap*); Eterm copy_shallow(Eterm*, Uint, Eterm**, ErlOffHeap*); -- cgit v1.2.3 From 64aa348700cc380f3525be01d3c815f6ecb398cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 17:33:14 +0200 Subject: erts: Remove halfword is_same bases macro Keep is_same macro for readability but remove base pointers. --- erts/emulator/beam/erl_db_tree.c | 6 +++--- erts/emulator/beam/erl_term.h | 2 +- erts/emulator/beam/utils.c | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 0c0218f5f9..d7c4e7e5ba 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -583,7 +583,7 @@ static ERTS_INLINE int cmp_key_eq(DbTableTree* tb, Eterm key, Eterm* key_base, TreeDbTerm* obj) { Eterm obj_key = GETKEY(tb,obj->dbterm.tpl); - return is_same(key, key_base, obj_key, obj->dbterm.tpl) + return is_same(key, obj_key) || CMP(key, obj_key) == 0; } @@ -2735,7 +2735,7 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) *done = 1; return 0; } - if (is_same(a,NULL,b,b_base)) + if (is_same(a,b)) return 0; switch (a & _TAG_PRIMARY_MASK) { @@ -2748,7 +2748,7 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) while (1) { if ((j = do_cmp_partly_bound(*aa++, *bb++, b_base, done)) != 0 || *done) return j; - if (is_same(*aa, NULL, *bb, b_base)) + if (is_same(*aa, *bb)) return 0; if (is_not_list(*aa) || is_not_list(*bb)) return do_cmp_partly_bound(*aa, *bb, b_base, done); diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index cc0b187240..95ced40af7 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -1118,7 +1118,7 @@ extern unsigned tag_val_def(Wterm); #define FLOAT_BIG _NUMBER_CODE(FLOAT_DEF,BIG_DEF) #define FLOAT_FLOAT _NUMBER_CODE(FLOAT_DEF,FLOAT_DEF) -#define is_same(A,A_BASE,B,B_BASE) ((A)==(B)) +#define is_same(A,B) ((A)==(B)) #endif /* __ERL_TERM_H */ diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 9f919fa2ab..acd6cf1072 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2606,7 +2606,7 @@ int eq(Eterm a, Eterm b) Eterm* bb; tailrecur: - if (is_same(a, a_base, b, b_base)) goto pop_next; + if (is_same(a, b)) goto pop_next; tailrecur_ne: switch (primary_tag(a)) { @@ -2617,7 +2617,7 @@ tailrecur_ne: while (1) { Eterm atmp = CAR(aval); Eterm btmp = CAR(bval); - if (!is_same(atmp,a_base,btmp,b_base)) { + if (!is_same(atmp,btmp)) { WSTACK_PUSH2(stack,(UWord) CDR(bval),(UWord) CDR(aval)); a = atmp; b = btmp; @@ -2625,7 +2625,7 @@ tailrecur_ne: } atmp = CDR(aval); btmp = CDR(bval); - if (is_same(atmp,a_base,btmp,b_base)) { + if (is_same(atmp,btmp)) { goto pop_next; } if (is_not_list(atmp) || is_not_list(btmp)) { @@ -2899,7 +2899,7 @@ term_array: /* arrays in 'aa' and 'bb', length in 'sz' */ Eterm* bp = bb; Sint i = sz; for (;;) { - if (!is_same(*ap,a_base,*bp,b_base)) break; + if (!is_same(*ap,*bp)) break; if (--i == 0) goto pop_next; ++ap; ++bp; @@ -3085,7 +3085,7 @@ static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only) bodyrecur: j = 0; tailrecur: - if (is_same(a,a_base,b,b_base)) { /* Equal values or pointers. */ + if (is_same(a,b)) { /* Equal values or pointers. */ goto pop_next; } tailrecur_ne: @@ -3169,7 +3169,7 @@ tailrecur_ne: while (1) { Eterm atmp = CAR(aa); Eterm btmp = CAR(bb); - if (!is_same(atmp,a_base,btmp,b_base)) { + if (!is_same(atmp,btmp)) { WSTACK_PUSH2(stack,(UWord) CDR(bb),(UWord) CDR(aa)); a = atmp; b = btmp; @@ -3177,7 +3177,7 @@ tailrecur_ne: } atmp = CDR(aa); btmp = CDR(bb); - if (is_same(atmp,a_base,btmp,b_base)) { + if (is_same(atmp,btmp)) { goto pop_next; } if (is_not_list(atmp) || is_not_list(btmp)) { @@ -3643,7 +3643,7 @@ term_array: /* arrays in 'aa' and 'bb', length in 'i' */ while (--i) { a = *aa++; b = *bb++; - if (!is_same(a,a_base, b,b_base)) { + if (!is_same(a, b)) { if (is_atom(a) && is_atom(b)) { if ((j = cmp_atoms(a, b)) != 0) { goto not_equal; -- cgit v1.2.3 From af8bb5e30b021b7b8e80ed96f3f3e16c18b865e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 17:43:59 +0200 Subject: erts: Remove halfword BINARY RELs * ERTS_GET_BINARY_BYTES_REL * ERTS_GET_REAL_BIN_REL --- erts/emulator/beam/copy.c | 2 +- erts/emulator/beam/erl_binary.h | 8 +------- erts/emulator/beam/erl_printf_term.c | 2 +- erts/emulator/beam/utils.c | 8 ++++---- 4 files changed, 7 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 3de2c10203..ec769c3b49 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -179,7 +179,7 @@ Uint size_object(Eterm obj) Uint bitoffs; Uint extra_bytes; Eterm hdr; - ERTS_GET_REAL_BIN_REL(obj, real_bin, offset, bitoffs, bitsize, base); + ERTS_GET_REAL_BIN(obj, real_bin, offset, bitoffs, bitsize); if ((bitsize + bitoffs) > 8) { sum += ERL_SUB_BIN_SIZE; extra_bytes = 2; diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index d24d041530..e181b5555d 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -94,10 +94,7 @@ typedef struct erl_heap_bin { * Bitsize: output variable (Uint) */ -#define ERTS_GET_BINARY_BYTES(Bin,Bytep,Bitoffs,Bitsize) \ - ERTS_GET_BINARY_BYTES_REL(Bin,Bytep,Bitoffs,Bitsize,NULL) - -#define ERTS_GET_BINARY_BYTES_REL(Bin,Bytep,Bitoffs,Bitsize,BasePtr) \ +#define ERTS_GET_BINARY_BYTES(Bin,Bytep,Bitoffs,Bitsize) \ do { \ Eterm* _real_bin = binary_val(Bin); \ Uint _offs = 0; \ @@ -130,9 +127,6 @@ do { \ */ #define ERTS_GET_REAL_BIN(Bin, RealBin, ByteOffset, BitOffset, BitSize) \ - ERTS_GET_REAL_BIN_REL(Bin, RealBin, ByteOffset, BitOffset, BitSize, NULL) - -#define ERTS_GET_REAL_BIN_REL(Bin, RealBin, ByteOffset, BitOffset, BitSize, BasePtr) \ do { \ ErlSubBin* _sb = (ErlSubBin *) binary_val(Bin); \ if (_sb->thing_word == HEADER_SUB_BIN) { \ diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 7a40f775f2..5aef2c76a4 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -472,7 +472,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, Uint bitoffs; Uint bitsize; byte octet; - ERTS_GET_BINARY_BYTES_REL(obj, bytep, bitoffs, bitsize, obj_base); + ERTS_GET_BINARY_BYTES(obj, bytep, bitoffs, bitsize); if (bitsize || !bytesize || !is_printable_ascii(bytep, bytesize, bitoffs)) { diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index acd6cf1072..a741e2e2e6 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2675,8 +2675,8 @@ tailrecur_ne: if (a_size != b_size) { goto not_equal; } - ERTS_GET_BINARY_BYTES_REL(a, a_ptr, a_bitoffs, a_bitsize, a_base); - ERTS_GET_BINARY_BYTES_REL(b, b_ptr, b_bitoffs, b_bitsize, b_base); + ERTS_GET_BINARY_BYTES(a, a_ptr, a_bitoffs, a_bitsize); + ERTS_GET_BINARY_BYTES(b, b_ptr, b_bitoffs, b_bitsize); if ((a_bitsize | b_bitsize | a_bitoffs | b_bitoffs) == 0) { if (sys_memcmp(a_ptr, b_ptr, a_size) == 0) goto pop_next; } else if (a_bitsize == b_bitsize) { @@ -3490,8 +3490,8 @@ tailrecur_ne: int cmp; byte* a_ptr; byte* b_ptr; - ERTS_GET_BINARY_BYTES_REL(a, a_ptr, a_bitoffs, a_bitsize, a_base); - ERTS_GET_BINARY_BYTES_REL(b, b_ptr, b_bitoffs, b_bitsize, b_base); + ERTS_GET_BINARY_BYTES(a, a_ptr, a_bitoffs, a_bitsize); + ERTS_GET_BINARY_BYTES(b, b_ptr, b_bitoffs, b_bitsize); if ((a_bitsize | b_bitsize | a_bitoffs | b_bitoffs) == 0) { min_size = (a_size < b_size) ? a_size : b_size; if ((cmp = sys_memcmp(a_ptr, b_ptr, min_size)) != 0) { -- cgit v1.2.3 From 0cec9781bc01c7e51ecfcb2fc4f356f2cc59b03c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 17:50:53 +0200 Subject: erts: Remove halfword specific allocator types --- erts/emulator/beam/erl_alloc.types | 36 ------------------------------------ 1 file changed, 36 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index b1d511ab78..1d5dec4807 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -87,11 +87,6 @@ allocator EHEAP true eheap_alloc allocator ETS true ets_alloc allocator FIXED_SIZE true fix_alloc -+if halfword -allocator LONG_LIVED_LOW true ll_low_alloc -allocator STANDARD_LOW true std_low_alloc -+endif - +else # Non smp build allocator TEMPORARY false temp_alloc @@ -102,11 +97,6 @@ allocator EHEAP false eheap_alloc allocator ETS false ets_alloc allocator FIXED_SIZE false fix_alloc -+if halfword -allocator LONG_LIVED_LOW false ll_low_alloc -allocator STANDARD_LOW false std_low_alloc -+endif - +endif allocator BINARY true binary_alloc @@ -349,29 +339,6 @@ type SSB SHORT_LIVED PROCESSES ssb +endif - -+if halfword - -type DDLL_PROCESS STANDARD_LOW SYSTEM ddll_processes -type MONITOR_LH STANDARD_LOW PROCESSES monitor_lh -type NLINK_LH STANDARD_LOW PROCESSES nlink_lh -type CODE LONG_LIVED_LOW CODE code -type DB_HEIR_DATA STANDARD_LOW ETS db_heir_data -type DB_MS_PSDO_PROC LONG_LIVED_LOW ETS db_match_pseudo_proc -type SCHDLR_DATA LONG_LIVED_LOW SYSTEM scheduler_data -type LL_TEMP_TERM LONG_LIVED_LOW SYSTEM ll_temp_term - -type NIF_TRAP_EXPORT STANDARD_LOW CODE nif_trap_export_entry -type EXPORT LONG_LIVED_LOW CODE export_entry -type MONITOR_SH STANDARD_LOW PROCESSES monitor_sh -type NLINK_SH STANDARD_LOW PROCESSES nlink_sh -type AINFO_REQ STANDARD_LOW SYSTEM alloc_info_request -type SCHED_WTIME_REQ STANDARD_LOW SYSTEM sched_wall_time_request -type GC_INFO_REQ STANDARD_LOW SYSTEM gc_info_request -type PORT_DATA_HEAP STANDARD_LOW SYSTEM port_data_heap - -+else # "fullword" - type DDLL_PROCESS STANDARD SYSTEM ddll_processes type MONITOR_LH STANDARD PROCESSES monitor_lh type NLINK_LH STANDARD PROCESSES nlink_lh @@ -390,9 +357,6 @@ type SCHED_WTIME_REQ SHORT_LIVED SYSTEM sched_wall_time_request type GC_INFO_REQ SHORT_LIVED SYSTEM gc_info_request type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap -+endif - - # # Types used by system specific code # -- cgit v1.2.3 From a5c3feee7bfa77eb385334272505ed562c7ef0f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 17:56:58 +0200 Subject: erts: Remove halfword specific tests --- erts/emulator/test/alloc_SUITE.erl | 14 ++------------ erts/emulator/test/driver_SUITE.erl | 12 +----------- erts/emulator/test/system_info_SUITE.erl | 2 -- 3 files changed, 3 insertions(+), 25 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 7c7ddde5d4..3ad1b88b2d 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -113,13 +113,10 @@ cpool(doc) -> []; cpool(Cfg) -> ?line drv_case(Cfg). erts_mmap(Config) when is_list(Config) -> - case {?t:os_type(), is_halfword_vm()} of - {{unix, _}, false} -> + case ?t:os_type() of + {unix, _} -> [erts_mmap_do(Config, SCO, SCRPM, SCRFSD) || SCO <-[true,false], SCRFSD <-[1234,0], SCRPM <- [true,false]]; - - {_,true} -> - {skipped, "No supercarrier support on halfword vm"}; {SkipOs,_} -> ?line {skipped, lists:flatten(["Not run on " @@ -254,10 +251,3 @@ start_node(Config, Opts) when is_list(Config), is_list(Opts) -> stop_node(Node) -> ?t:stop_node(Node). - -is_halfword_vm() -> - case {erlang:system_info({wordsize, internal}), - erlang:system_info({wordsize, external})} of - {4, 8} -> true; - {WS, WS} -> false - end. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 4211c49848..b72d6cbe52 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -1877,10 +1877,7 @@ mseg_alloc_cached_segments() -> mseg_alloc_cached_segments(mseg_inst_info(0)). mseg_alloc_cached_segments(MsegAllocInfo) -> - MemName = case is_halfword_vm() of - true -> "high memory"; - false -> "all memory" - end, + MemName = "all memory", ?line [{memkind,DrvMem}] = lists:filter(fun(E) -> case E of {memkind, [{name, MemName} | _]} -> true; @@ -1899,13 +1896,6 @@ mseg_inst_info(I) -> erlang:system_info({allocator,mseg_alloc})), Value. -is_halfword_vm() -> - case {erlang:system_info({wordsize, internal}), - erlang:system_info({wordsize, external})} of - {4, 8} -> true; - {WS, WS} -> false - end. - driver_alloc_sbct() -> {_, _, _, As} = erlang:system_info(allocator), case lists:keysearch(driver_alloc, 1, As) of diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index bee42c07d9..51122e5d55 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -185,8 +185,6 @@ wordsize(Config) when is_list(Config) -> {comment, "True 32-bit emulator"}; {8,8} -> {comment, "True 64-bit emulator"}; - {8,4} -> - {comment, "Halfword 64-bit emulator"}; Other -> exit({unexpected_wordsizes,Other}) end. -- cgit v1.2.3 From 1d76e08836a0ccd9ebc15bfcff41d5e52ccd4073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Jun 2015 18:15:29 +0200 Subject: erts: Remove halfword valgrind suppress file --- erts/emulator/valgrind/suppress.halfword | 56 -------------------------------- 1 file changed, 56 deletions(-) delete mode 100644 erts/emulator/valgrind/suppress.halfword (limited to 'erts') diff --git a/erts/emulator/valgrind/suppress.halfword b/erts/emulator/valgrind/suppress.halfword deleted file mode 100644 index 8fe448d897..0000000000 --- a/erts/emulator/valgrind/suppress.halfword +++ /dev/null @@ -1,56 +0,0 @@ -# Extra suppressions specific for the halfword emulator. - -# --- Suppress all offheap binaries --- -# Valgrinds leak check does not recognize pointers that are stored -# at unaligned addresses. In halfword emulator we store 64-bit pointers -# to offheap data on 32-bit aligned heaps. -# We solve this by suppressing allocation of all offheap structures -# that are not referenced by other tables (ie binaries). - -{ -Halfword erts_bin_nrml_alloc -Memcheck:Leak -... -fun:erts_bin_nrml_alloc -... -} - -{ -Halfword erts_bin_realloc -Memcheck:Leak -... -fun:erts_bin_realloc -... -} - -{ -Halfword erts_bin_realloc_fnf -Memcheck:Leak -... -fun:erts_bin_realloc_fnf -... -} - -{ -Halfword erts_bin_drv_alloc -Memcheck:Leak -... -fun:erts_bin_drv_alloc -... -} - -{ -Halfword erts_bin_drv_alloc_fnf -Memcheck:Leak -... -fun:erts_bin_drv_alloc_fnf -... -} - -{ -Halfword erts_create_magic_binary -Memcheck:Leak -... -fun:erts_create_magic_binary -... -} -- cgit v1.2.3 From 9c2f061abe085733824d6e2a659d500813c296ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 22 Jun 2015 09:43:56 +0200 Subject: erts: Remove halfword relative printf --- erts/emulator/beam/erl_db_hash.c | 4 ++-- erts/emulator/beam/erl_db_tree.c | 7 +++---- erts/emulator/beam/erl_printf_term.c | 15 +++++---------- erts/emulator/beam/erl_printf_term.h | 3 +-- erts/include/internal/erl_printf_format.h | 2 +- erts/lib_src/common/erl_printf_format.c | 15 +++++---------- 6 files changed, 17 insertions(+), 29 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index e4f4a7beb0..616d9557c2 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -2187,11 +2187,11 @@ static void db_print_hash(int to, void *to_arg, int show, DbTable *tbl) erts_print(to, to_arg, "*"); if (tb->common.compress) { Eterm key = GETKEY(tb, list->dbterm.tpl); - erts_print(to, to_arg, "key=%R", key, list->dbterm.tpl); + erts_print(to, to_arg, "key=%T", key); } else { Eterm obj = make_tuple(list->dbterm.tpl); - erts_print(to, to_arg, "%R", obj, list->dbterm.tpl); + erts_print(to, to_arg, "%T", obj); } if (list->next != 0) erts_print(to, to_arg, ","); diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index d7c4e7e5ba..b8791d9f8e 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -2796,7 +2796,7 @@ static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key, Eterm* bk_ erts_fprintf(stderr," > "); else erts_fprintf(stderr," == "); - erts_fprintf(stderr,"%R\n", bound_key, bk_base); + erts_fprintf(stderr,"%T\n", bound_key); #endif return ret; } @@ -3155,9 +3155,8 @@ static void do_dump_tree2(DbTableTree* tb, int to, void *to_arg, int show, prefix = ""; term = make_tuple(t->dbterm.tpl); } - erts_print(to, to_arg, "%*s%s%R (addr = %p, bal = %d)\n", - offset, "", prefix, term, t->dbterm.tpl, - t, t->balance); + erts_print(to, to_arg, "%*s%s%T (addr = %p, bal = %d)\n", + offset, "", prefix, term, t, t->balance); } do_dump_tree2(tb, to, to_arg, show, t->left, offset + 4); } diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 5aef2c76a4..bbb868b76a 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -117,8 +117,7 @@ do { \ /* return 0 if list is not a non-empty flat list of printable characters */ static int -is_printable_string(Eterm list, Eterm* base) -{ +is_printable_string(Eterm list) { int len = 0; int c; @@ -260,9 +259,7 @@ static char *format_binary(Uint16 x, char *b) { #endif static int -print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, - Eterm* obj_base) -{ +print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) { DECLARE_WSTACK(s); int res; int i; @@ -420,7 +417,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, PRINT_CHAR(res, fn, arg, '>'); break; case LIST_DEF: - if (is_printable_string(obj, obj_base)) { + if (is_printable_string(obj)) { int c; PRINT_CHAR(res, fn, arg, '"'); nobj = list_val(obj); @@ -644,13 +641,11 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, int -erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision, - ErlPfEterm* term_base) -{ +erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision) { int res; ERTS_CT_ASSERT(sizeof(ErlPfEterm) == sizeof(Eterm)); - res = print_term(fn, arg, (Eterm)term, &precision, (Eterm*)term_base); + res = print_term(fn, arg, (Eterm)term, &precision); if (res < 0) return res; if (precision <= 0) diff --git a/erts/emulator/beam/erl_printf_term.h b/erts/emulator/beam/erl_printf_term.h index 87618a36d7..4618457f27 100644 --- a/erts/emulator/beam/erl_printf_term.h +++ b/erts/emulator/beam/erl_printf_term.h @@ -22,6 +22,5 @@ #define ERL_PRINTF_TERM_H__ #include "erl_printf_format.h" -int erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision, - ErlPfEterm* term_base); +int erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision); #endif diff --git a/erts/include/internal/erl_printf_format.h b/erts/include/internal/erl_printf_format.h index c41c5ba820..953022017a 100644 --- a/erts/include/internal/erl_printf_format.h +++ b/erts/include/internal/erl_printf_format.h @@ -58,7 +58,7 @@ extern int erts_printf_double(fmtfn_t, void *, char, int, int, double); typedef ErlPfUWord ErlPfEterm; -extern int (*erts_printf_eterm_func)(fmtfn_t, void*, ErlPfEterm, long, ErlPfEterm*); +extern int (*erts_printf_eterm_func)(fmtfn_t, void*, ErlPfEterm, long); #endif /* ERL_PRINTF_FORMAT_H__ */ diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c index 9e70f4e882..e7d5d4413e 100644 --- a/erts/lib_src/common/erl_printf_format.c +++ b/erts/lib_src/common/erl_printf_format.c @@ -94,7 +94,7 @@ #endif #define FMTC_d 0x0000 -#define FMTC_R 0x0001 +/*empty 0x0001 was RELATIVE */ #define FMTC_o 0x0002 #define FMTC_u 0x0003 #define FMTC_x 0x0004 @@ -158,7 +158,7 @@ static char heX[] = "0123456789ABCDEF"; #define SIGN(X) ((X) > 0 ? 1 : ((X) < 0 ? -1 : 0)) #define USIGN(X) ((X) == 0 ? 0 : 1) -int (*erts_printf_eterm_func)(fmtfn_t, void*, ErlPfEterm, long, ErlPfEterm*) = NULL; +int (*erts_printf_eterm_func)(fmtfn_t, void*, ErlPfEterm, long) = NULL; static int noop_fn(void *vfp, char* buf, size_t len) @@ -637,7 +637,6 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) case 'p': ptr++; fmt |= FMTC_p; break; case 'n': ptr++; fmt |= FMTC_n; break; case 'T': ptr++; fmt |= FMTC_T; break; - case 'R': ptr++; fmt |= FMTC_R; break; case '%': FMT(fn,arg,ptr,1,count); ptr++; @@ -812,11 +811,9 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) default: *va_arg(ap,int*) = count; break; } break; - case FMTC_T: /* Eterm */ - case FMTC_R: { /* Eterm, Eterm* base */ + case FMTC_T: { /* Eterm */ long prec; ErlPfEterm eterm; - ErlPfEterm* eterm_base; if (!erts_printf_eterm_func) return -EINVAL; @@ -827,16 +824,14 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) else prec = (long) precision; eterm = va_arg(ap, ErlPfEterm); - eterm_base = ((fmt & FMTC_MASK) == FMTC_R) ? - va_arg(ap, ErlPfEterm*) : NULL; if (width > 0 && !(fmt & FMTF_adj)) { - res = (*erts_printf_eterm_func)(noop_fn, NULL, eterm, prec, eterm_base); + res = (*erts_printf_eterm_func)(noop_fn, NULL, eterm, prec); if (res < 0) return res; if (width > res) BLANKS(fn, arg, width - res, count); } - res = (*erts_printf_eterm_func)(fn, arg, eterm, prec, eterm_base); + res = (*erts_printf_eterm_func)(fn, arg, eterm, prec); if (res < 0) return res; count += res; -- cgit v1.2.3 From 109f5a0e23de7174f34fddcfaac855d35125e70b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 22 Jun 2015 11:52:48 +0200 Subject: erts: Remove halfword CHECK_POINTER_MASK --- erts/emulator/beam/erl_term.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 95ced40af7..0d8e981da4 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -24,8 +24,6 @@ typedef UWord Wterm; /* Full word terms */ #define HEAP_ON_C_STACK 1 -#define CHECK_POINTER_MASK 0x0UL - struct erl_node_; /* Declared in erl_node_tables.h */ /* -- cgit v1.2.3 From 437dd11b324e17e3c7cf83c8e4f70fd19b51d05f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 22 Jun 2015 14:51:04 +0200 Subject: erts: Remove halfword bases in ETS --- erts/emulator/beam/erl_db.c | 2 +- erts/emulator/beam/erl_db_tree.c | 167 ++++++++++++++++----------------------- erts/emulator/beam/erl_db_util.c | 17 ++-- erts/emulator/beam/erl_db_util.h | 2 +- 4 files changed, 75 insertions(+), 113 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index f3da28b65a..9ec14ab5ae 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -2834,7 +2834,7 @@ BIF_RETTYPE ets_match_spec_run_r_3(BIF_ALIST_3) BIF_TRAP3(bif_export[BIF_ets_match_spec_run_r_3], BIF_P,lst,BIF_ARG_2,ret); } - res = db_prog_match(BIF_P, mp, CAR(list_val(lst)), NULL, NULL, 0, + res = db_prog_match(BIF_P, mp, CAR(list_val(lst)), NULL, 0, ERTS_PAM_COPY_RESULT, &dummy); if (is_value(res)) { hp = HAlloc(BIF_P, 2); diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index b8791d9f8e..311b69d114 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -280,7 +280,7 @@ struct select_delete_context { /* ** Forward declarations */ -static TreeDbTerm *linkout_tree(DbTableTree *tb, Eterm key, Eterm* key_base); +static TreeDbTerm *linkout_tree(DbTableTree *tb, Eterm key); static TreeDbTerm *linkout_object_tree(DbTableTree *tb, Eterm object); static int do_free_tree_cont(DbTableTree *tb, int num_left); @@ -291,15 +291,15 @@ static int delsub(TreeDbTerm **this); static TreeDbTerm *slot_search(Process *p, DbTableTree *tb, Sint slot); static TreeDbTerm *find_node(DbTableTree *tb, Eterm key); static TreeDbTerm **find_node2(DbTableTree *tb, Eterm key); -static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack*, Eterm key, Eterm* kbase); -static TreeDbTerm *find_prev(DbTableTree *tb, DbTreeStack*, Eterm key, Eterm* kbase); +static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack*, Eterm key); +static TreeDbTerm *find_prev(DbTableTree *tb, DbTreeStack*, Eterm key); static TreeDbTerm *find_next_from_pb_key(DbTableTree *tb, DbTreeStack*, Eterm key); static TreeDbTerm *find_prev_from_pb_key(DbTableTree *tb, DbTreeStack*, Eterm key); static void traverse_backwards(DbTableTree *tb, DbTreeStack*, - Eterm lastkey, Eterm* lk_base, + Eterm lastkey, int (*doit)(DbTableTree *tb, TreeDbTerm *, void *, @@ -307,7 +307,7 @@ static void traverse_backwards(DbTableTree *tb, void *context); static void traverse_forward(DbTableTree *tb, DbTreeStack*, - Eterm lastkey, Eterm* lk_base, + Eterm lastkey, int (*doit)(DbTableTree *tb, TreeDbTerm *, void *, @@ -315,8 +315,8 @@ static void traverse_forward(DbTableTree *tb, void *context); static int key_given(DbTableTree *tb, Eterm pattern, TreeDbTerm **ret, Eterm *partly_bound_key); -static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key, Eterm* bk_base); -static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done); +static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key); +static Sint do_cmp_partly_bound(Eterm a, Eterm b, int *done); static int analyze_pattern(DbTableTree *tb, Eterm pattern, struct mp_info *mpi); @@ -517,7 +517,7 @@ static int db_next_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret) if (is_atom(key) && key == am_EOT) return DB_ERROR_BADKEY; stack = get_any_stack(tb); - this = find_next(tb, stack, key, NULL); + this = find_next(tb, stack, key); release_stack(tb,stack); if (this == NULL) { *ret = am_EOT; @@ -563,7 +563,7 @@ static int db_prev_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret) if (is_atom(key) && key == am_EOT) return DB_ERROR_BADKEY; stack = get_any_stack(tb); - this = find_prev(tb, stack, key, NULL); + this = find_prev(tb, stack, key); release_stack(tb,stack); if (this == NULL) { *ret = am_EOT; @@ -573,18 +573,13 @@ static int db_prev_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret) return DB_ERROR_NONE; } -static ERTS_INLINE Sint cmp_key(DbTableTree* tb, Eterm key, Eterm* key_base, - TreeDbTerm* obj) -{ +static ERTS_INLINE Sint cmp_key(DbTableTree* tb, Eterm key, TreeDbTerm* obj) { return CMP(key, GETKEY(tb,obj->dbterm.tpl)); } -static ERTS_INLINE int cmp_key_eq(DbTableTree* tb, Eterm key, Eterm* key_base, - TreeDbTerm* obj) -{ +static ERTS_INLINE int cmp_key_eq(DbTableTree* tb, Eterm key, TreeDbTerm* obj) { Eterm obj_key = GETKEY(tb,obj->dbterm.tpl); - return is_same(key, obj_key) - || CMP(key, obj_key) == 0; + return is_same(key, obj_key) || CMP(key, obj_key) == 0; } static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail) @@ -618,7 +613,7 @@ static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail) (*this)->balance = 0; (*this)->left = (*this)->right = NULL; break; - } else if ((c = cmp_key(tb, key, NULL, *this)) < 0) { + } else if ((c = cmp_key(tb, key, *this)) < 0) { /* go lefts */ dstack[dpos++] = DIR_LEFT; tstack[tpos++] = this; @@ -773,7 +768,7 @@ static int db_erase_tree(DbTable *tbl, Eterm key, Eterm *ret) *ret = am_true; - if ((res = linkout_tree(tb, key, NULL)) != NULL) { + if ((res = linkout_tree(tb, key)) != NULL) { free_term(tb, res); } return DB_ERROR_NONE; @@ -969,15 +964,15 @@ static int db_select_continue_tree(Process *p, stack = get_any_stack(tb); if (chunk_size) { if (reverse) { - traverse_backwards(tb, stack, lastkey, NULL, &doit_select_chunk, &sc); + traverse_backwards(tb, stack, lastkey, &doit_select_chunk, &sc); } else { - traverse_forward(tb, stack, lastkey, NULL, &doit_select_chunk, &sc); + traverse_forward(tb, stack, lastkey, &doit_select_chunk, &sc); } } else { if (reverse) { - traverse_forward(tb, stack, lastkey, NULL, &doit_select, &sc); + traverse_forward(tb, stack, lastkey, &doit_select, &sc); } else { - traverse_backwards(tb, stack, lastkey, NULL, &doit_select, &sc); + traverse_backwards(tb, stack, lastkey, &doit_select, &sc); } } release_stack(tb,stack); @@ -1025,8 +1020,8 @@ static int db_select_continue_tree(Process *p, key = GETKEY(tb, sc.lastobj); if (chunk_size) { if (end_condition != NIL && - ((!reverse && cmp_partly_bound(end_condition,key,sc.lastobj) < 0) || - (reverse && cmp_partly_bound(end_condition,key,sc.lastobj) > 0))) { + ((!reverse && cmp_partly_bound(end_condition,key) < 0) || + (reverse && cmp_partly_bound(end_condition,key) > 0))) { /* done anyway */ if (!sc.got) { RET_TO_BIF(am_EOT, DB_ERROR_NONE); @@ -1038,8 +1033,8 @@ static int db_select_continue_tree(Process *p, } } else { if (end_condition != NIL && - ((!reverse && cmp_partly_bound(end_condition,key,sc.lastobj) > 0) || - (reverse && cmp_partly_bound(end_condition,key,sc.lastobj) < 0))) { + ((!reverse && cmp_partly_bound(end_condition,key) > 0) || + (reverse && cmp_partly_bound(end_condition,key) < 0))) { /* done anyway */ RET_TO_BIF(sc.accum,DB_ERROR_NONE); } @@ -1074,7 +1069,6 @@ static int db_select_tree(Process *p, DbTable *tbl, struct select_context sc; struct mp_info mpi; Eterm lastkey = THE_NON_VALUE; - Eterm* lk_base = NULL; Eterm key; Eterm continuation; unsigned sz; @@ -1126,20 +1120,18 @@ static int db_select_tree(Process *p, DbTable *tbl, if (mpi.some_limitation) { if ((this = find_prev_from_pb_key(tb, stack, mpi.least)) != NULL) { lastkey = GETKEY(tb, this->dbterm.tpl); - lk_base = this->dbterm.tpl; } sc.end_condition = mpi.most; } - traverse_forward(tb, stack, lastkey, lk_base, &doit_select, &sc); + traverse_forward(tb, stack, lastkey, &doit_select, &sc); } else { if (mpi.some_limitation) { if ((this = find_next_from_pb_key(tb, stack, mpi.most)) != NULL) { lastkey = GETKEY(tb, this->dbterm.tpl); - lk_base = this->dbterm.tpl; } sc.end_condition = mpi.least; } - traverse_backwards(tb, stack, lastkey, lk_base, &doit_select, &sc); + traverse_backwards(tb, stack, lastkey, &doit_select, &sc); } release_stack(tb,stack); #ifdef HARDDEBUG @@ -1235,7 +1227,7 @@ static int db_select_count_continue_tree(Process *p, } stack = get_any_stack(tb); - traverse_backwards(tb, stack, lastkey, NULL, &doit_select_count, &sc); + traverse_backwards(tb, stack, lastkey, &doit_select_count, &sc); release_stack(tb,stack); BUMP_REDS(p, 1000 - sc.max); @@ -1245,7 +1237,7 @@ static int db_select_count_continue_tree(Process *p, } key = GETKEY(tb, sc.lastobj); if (end_condition != NIL && - (cmp_partly_bound(end_condition,key,sc.lastobj) > 0)) { + (cmp_partly_bound(end_condition,key) > 0)) { /* done anyway */ RET_TO_BIF(make_small(sc.got),DB_ERROR_NONE); } @@ -1283,7 +1275,6 @@ static int db_select_count_tree(Process *p, DbTable *tbl, struct select_count_context sc; struct mp_info mpi; Eterm lastkey = THE_NON_VALUE; - Eterm* lk_base = NULL; Eterm key; Eterm continuation; unsigned sz; @@ -1333,12 +1324,11 @@ static int db_select_count_tree(Process *p, DbTable *tbl, if (mpi.some_limitation) { if ((this = find_next_from_pb_key(tb, stack, mpi.most)) != NULL) { lastkey = GETKEY(tb, this->dbterm.tpl); - lk_base = this->dbterm.tpl; } sc.end_condition = mpi.least; } - traverse_backwards(tb, stack, lastkey, lk_base, &doit_select_count, &sc); + traverse_backwards(tb, stack, lastkey, &doit_select_count, &sc); release_stack(tb,stack); BUMP_REDS(p, 1000 - sc.max); if (sc.max > 0) { @@ -1387,7 +1377,6 @@ static int db_select_chunk_tree(Process *p, DbTable *tbl, struct select_context sc; struct mp_info mpi; Eterm lastkey = THE_NON_VALUE; - Eterm* lk_base = NULL; Eterm key; Eterm continuation; unsigned sz; @@ -1444,20 +1433,18 @@ static int db_select_chunk_tree(Process *p, DbTable *tbl, if (mpi.some_limitation) { if ((this = find_next_from_pb_key(tb, stack, mpi.most)) != NULL) { lastkey = GETKEY(tb, this->dbterm.tpl); - lk_base = this->dbterm.tpl; } sc.end_condition = mpi.least; } - traverse_backwards(tb, stack, lastkey, lk_base, &doit_select_chunk, &sc); + traverse_backwards(tb, stack, lastkey, &doit_select_chunk, &sc); } else { if (mpi.some_limitation) { if ((this = find_prev_from_pb_key(tb, stack, mpi.least)) != NULL) { lastkey = GETKEY(tb, this->dbterm.tpl); - lk_base = this->dbterm.tpl; } sc.end_condition = mpi.most; } - traverse_forward(tb, stack, lastkey, lk_base, &doit_select_chunk, &sc); + traverse_forward(tb, stack, lastkey, &doit_select_chunk, &sc); } release_stack(tb,stack); @@ -1585,7 +1572,7 @@ static int db_select_delete_continue_tree(Process *p, sc.keypos = tb->common.keypos; ASSERT(!erts_smp_atomic_read_nob(&tb->is_stack_busy)); - traverse_backwards(tb, &tb->static_stack, lastkey, NULL, &doit_select_delete, &sc); + traverse_backwards(tb, &tb->static_stack, lastkey, &doit_select_delete, &sc); BUMP_REDS(p, 1000 - sc.max); @@ -1594,7 +1581,7 @@ static int db_select_delete_continue_tree(Process *p, } key = GETKEY(tb, (sc.lastterm)->dbterm.tpl); if (end_condition != NIL && - cmp_partly_bound(end_condition,key,sc.lastterm->dbterm.tpl) > 0) { /* done anyway */ + cmp_partly_bound(end_condition,key) > 0) { /* done anyway */ RET_TO_BIF(erts_make_integer(sc.accum,p),DB_ERROR_NONE); } /* Not done yet, let's trap. */ @@ -1629,7 +1616,6 @@ static int db_select_delete_tree(Process *p, DbTable *tbl, struct select_delete_context sc; struct mp_info mpi; Eterm lastkey = THE_NON_VALUE; - Eterm* lk_base = NULL; Eterm key; Eterm continuation; unsigned sz; @@ -1682,12 +1668,11 @@ static int db_select_delete_tree(Process *p, DbTable *tbl, if (mpi.some_limitation) { if ((this = find_next_from_pb_key(tb, &tb->static_stack, mpi.most)) != NULL) { lastkey = GETKEY(tb, this->dbterm.tpl); - lk_base = this->dbterm.tpl; } sc.end_condition = mpi.least; } - traverse_backwards(tb, &tb->static_stack, lastkey, lk_base, &doit_select_delete, &sc); + traverse_backwards(tb, &tb->static_stack, lastkey, &doit_select_delete, &sc); BUMP_REDS(p, 1000 - sc.max); if (sc.max > 0) { @@ -1733,7 +1718,7 @@ static int db_take_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret) TreeDbTerm *this; *ret = NIL; - this = linkout_tree(tb, key, NULL); + this = linkout_tree(tb, key); if (this) { Eterm copy, *hp, *hend; @@ -1844,9 +1829,7 @@ do_db_tree_foreach_offheap(TreeDbTerm *tdbt, do_db_tree_foreach_offheap(tdbt->right, func, arg); } -static TreeDbTerm *linkout_tree(DbTableTree *tb, - Eterm key, Eterm* key_base) -{ +static TreeDbTerm *linkout_tree(DbTableTree *tb, Eterm key) { TreeDbTerm **tstack[STACK_NEED]; int tpos = 0; int dstack[STACK_NEED+1]; @@ -1868,7 +1851,7 @@ static TreeDbTerm *linkout_tree(DbTableTree *tb, for (;;) { if (!*this) { /* Failure */ return NULL; - } else if ((c = cmp_key(tb, key, key_base, *this)) < 0) { + } else if ((c = cmp_key(tb, key, *this)) < 0) { dstack[dpos++] = DIR_LEFT; tstack[tpos++] = this; this = &((*this)->left); @@ -1932,7 +1915,7 @@ static TreeDbTerm *linkout_object_tree(DbTableTree *tb, for (;;) { if (!*this) { /* Failure */ return NULL; - } else if ((c = cmp_key(tb,key,NULL,*this)) < 0) { + } else if ((c = cmp_key(tb,key,*this)) < 0) { dstack[dpos++] = DIR_LEFT; tstack[tpos++] = this; this = &((*this)->left); @@ -2318,15 +2301,13 @@ done: * Find next and previous in sort order */ -static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack* stack, - Eterm key, Eterm* key_base) -{ +static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack* stack, Eterm key) { TreeDbTerm *this; TreeDbTerm *tmp; Sint c; if(( this = TOP_NODE(stack)) != NULL) { - if (!cmp_key_eq(tb,key,key_base,this)) { + if (!cmp_key_eq(tb,key,this)) { /* Start from the beginning */ stack->pos = stack->slot = 0; } @@ -2336,7 +2317,7 @@ static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack* stack, return NULL; for (;;) { PUSH_NODE(stack, this); - if (( c = cmp_key(tb,key,key_base,this) ) > 0) { + if (( c = cmp_key(tb,key,this) ) > 0) { if (this->right == NULL) /* We are at the previos and the element does not exist */ @@ -2376,15 +2357,13 @@ static TreeDbTerm *find_next(DbTableTree *tb, DbTreeStack* stack, return this; } -static TreeDbTerm *find_prev(DbTableTree *tb, DbTreeStack* stack, - Eterm key, Eterm* key_base) -{ +static TreeDbTerm *find_prev(DbTableTree *tb, DbTreeStack* stack, Eterm key) { TreeDbTerm *this; TreeDbTerm *tmp; Sint c; if(( this = TOP_NODE(stack)) != NULL) { - if (!cmp_key_eq(tb,key,key_base,this)) { + if (!cmp_key_eq(tb,key,this)) { /* Start from the beginning */ stack->pos = stack->slot = 0; } @@ -2394,7 +2373,7 @@ static TreeDbTerm *find_prev(DbTableTree *tb, DbTreeStack* stack, return NULL; for (;;) { PUSH_NODE(stack, this); - if (( c = cmp_key(tb,key,key_base,this) ) < 0) { + if (( c = cmp_key(tb,key,this) ) < 0) { if (this->left == NULL) /* We are at the next and the element does not exist */ @@ -2447,8 +2426,7 @@ static TreeDbTerm *find_next_from_pb_key(DbTableTree *tb, DbTreeStack* stack, return NULL; for (;;) { PUSH_NODE(stack, this); - if (( c = cmp_partly_bound(key,GETKEY(tb, this->dbterm.tpl), - this->dbterm.tpl) ) >= 0) { + if (( c = cmp_partly_bound(key,GETKEY(tb, this->dbterm.tpl))) >= 0) { if (this->right == NULL) { do { tmp = POP_NODE(stack); @@ -2481,8 +2459,7 @@ static TreeDbTerm *find_prev_from_pb_key(DbTableTree *tb, DbTreeStack* stack, return NULL; for (;;) { PUSH_NODE(stack, this); - if (( c = cmp_partly_bound(key,GETKEY(tb, this->dbterm.tpl), - this->dbterm.tpl) ) <= 0) { + if (( c = cmp_partly_bound(key,GETKEY(tb, this->dbterm.tpl))) <= 0) { if (this->left == NULL) { do { tmp = POP_NODE(stack); @@ -2513,10 +2490,10 @@ static TreeDbTerm *find_node(DbTableTree *tb, Eterm key) DbTreeStack* stack = get_static_stack(tb); if(!stack || EMPTY_NODE(stack) - || !cmp_key_eq(tb, key, NULL, (this=TOP_NODE(stack)))) { + || !cmp_key_eq(tb, key, (this=TOP_NODE(stack)))) { this = tb->root; - while (this != NULL && (res = cmp_key(tb,key,NULL,this)) != 0) { + while (this != NULL && (res = cmp_key(tb,key,this)) != 0) { if (res < 0) this = this->left; else @@ -2538,7 +2515,7 @@ static TreeDbTerm **find_node2(DbTableTree *tb, Eterm key) Sint res; this = &tb->root; - while ((*this) != NULL && (res = cmp_key(tb, key, NULL, *this)) != 0) { + while ((*this) != NULL && (res = cmp_key(tb, key, *this)) != 0) { if (res < 0) this = &((*this)->left); else @@ -2618,7 +2595,7 @@ db_finalize_dbterm_tree(int cret, DbUpdateHandle *handle) */ static void traverse_backwards(DbTableTree *tb, DbTreeStack* stack, - Eterm lastkey, Eterm* lk_base, + Eterm lastkey, int (*doit)(DbTableTree *, TreeDbTerm *, void *, @@ -2637,16 +2614,15 @@ static void traverse_backwards(DbTableTree *tb, this = this->right; } this = TOP_NODE(stack); - next = find_prev(tb, stack, GETKEY(tb, this->dbterm.tpl), - this->dbterm.tpl); + next = find_prev(tb, stack, GETKEY(tb, this->dbterm.tpl)); if (!((*doit)(tb, this, context, 0))) return; } else { - next = find_prev(tb, stack, lastkey, lk_base); + next = find_prev(tb, stack, lastkey); } while ((this = next) != NULL) { - next = find_prev(tb, stack, GETKEY(tb, this->dbterm.tpl), this->dbterm.tpl); + next = find_prev(tb, stack, GETKEY(tb, this->dbterm.tpl)); if (!((*doit)(tb, this, context, 0))) return; } @@ -2657,7 +2633,7 @@ static void traverse_backwards(DbTableTree *tb, */ static void traverse_forward(DbTableTree *tb, DbTreeStack* stack, - Eterm lastkey, Eterm* lk_base, + Eterm lastkey, int (*doit)(DbTableTree *, TreeDbTerm *, void *, @@ -2676,15 +2652,15 @@ static void traverse_forward(DbTableTree *tb, this = this->left; } this = TOP_NODE(stack); - next = find_next(tb, stack, GETKEY(tb, this->dbterm.tpl), this->dbterm.tpl); + next = find_next(tb, stack, GETKEY(tb, this->dbterm.tpl)); if (!((*doit)(tb, this, context, 1))) return; } else { - next = find_next(tb, stack, lastkey, lk_base); + next = find_next(tb, stack, lastkey); } while ((this = next) != NULL) { - next = find_next(tb, stack, GETKEY(tb, this->dbterm.tpl), this->dbterm.tpl); + next = find_next(tb, stack, GETKEY(tb, this->dbterm.tpl)); if (!((*doit)(tb, this, context, 1))) return; } @@ -2721,7 +2697,7 @@ static int key_given(DbTableTree *tb, Eterm pattern, TreeDbTerm **ret, -static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) +static Sint do_cmp_partly_bound(Eterm a, Eterm b, int *done) { Eterm* aa; Eterm* bb; @@ -2746,12 +2722,12 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) aa = list_val(a); bb = list_val(b); while (1) { - if ((j = do_cmp_partly_bound(*aa++, *bb++, b_base, done)) != 0 || *done) + if ((j = do_cmp_partly_bound(*aa++, *bb++, done)) != 0 || *done) return j; if (is_same(*aa, *bb)) return 0; if (is_not_list(*aa) || is_not_list(*bb)) - return do_cmp_partly_bound(*aa, *bb, b_base, done); + return do_cmp_partly_bound(*aa, *bb, done); aa = list_val(*aa); bb = list_val(*bb); } @@ -2772,7 +2748,7 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) if (i < arityval(*bb)) return(-1); if (i > arityval(*bb)) return(1); while (i--) { - if ((j = do_cmp_partly_bound(*++aa, *++bb, b_base, done)) != 0 + if ((j = do_cmp_partly_bound(*++aa, *++bb, done)) != 0 || *done) return j; } @@ -2784,10 +2760,9 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) } } -static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key, Eterm* bk_base) -{ +static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key) { int done = 0; - Sint ret = do_cmp_partly_bound(partly_bound_key, bound_key, bk_base, &done); + Sint ret = do_cmp_partly_bound(partly_bound_key, bound_key, &done); #ifdef HARDDEBUG erts_fprintf(stderr,"\ncmp_partly_bound: %T", partly_bound_key); if (ret < 0) @@ -3013,12 +2988,10 @@ static int doit_select(DbTableTree *tb, TreeDbTerm *this, void *ptr, if (sc->end_condition != NIL && ((forward && cmp_partly_bound(sc->end_condition, - GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl), - this->dbterm.tpl) < 0) || + GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) < 0) || (!forward && cmp_partly_bound(sc->end_condition, - GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl), - this->dbterm.tpl) > 0))) { + GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) > 0))) { return 0; } ret = db_match_dbterm(&tb->common,sc->p,sc->mp,sc->all_objects, @@ -3050,8 +3023,7 @@ static int doit_select_count(DbTableTree *tb, TreeDbTerm *this, void *ptr, /* Always backwards traversing */ if (sc->end_condition != NIL && (cmp_partly_bound(sc->end_condition, - GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl), - this->dbterm.tpl) > 0)) { + GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) > 0)) { return 0; } ret = db_match_dbterm(&tb->common, sc->p, sc->mp, 0, @@ -3077,12 +3049,10 @@ static int doit_select_chunk(DbTableTree *tb, TreeDbTerm *this, void *ptr, if (sc->end_condition != NIL && ((forward && cmp_partly_bound(sc->end_condition, - GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl), - this->dbterm.tpl) < 0) || + GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) < 0) || (!forward && cmp_partly_bound(sc->end_condition, - GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl), - this->dbterm.tpl) > 0))) { + GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) > 0))) { return 0; } @@ -3120,14 +3090,13 @@ static int doit_select_delete(DbTableTree *tb, TreeDbTerm *this, void *ptr, if (sc->end_condition != NIL && cmp_partly_bound(sc->end_condition, - GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl), - this->dbterm.tpl) > 0) + GETKEY_WITH_POS(sc->keypos, this->dbterm.tpl)) > 0) return 0; ret = db_match_dbterm(&tb->common, sc->p, sc->mp, 0, &this->dbterm, NULL, 0); if (ret == am_true) { key = GETKEY(sc->tb, this->dbterm.tpl); - linkout_tree(sc->tb, key, this->dbterm.tpl); + linkout_tree(sc->tb, key); sc->erase_lastterm = 1; ++sc->accum; } diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 8047711e6f..0bf9558ac9 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -367,7 +367,6 @@ typedef struct MatchVariable { Eterm term; #ifdef DEBUG Process* proc; - Eterm* base; #endif } MatchVariable; @@ -1233,7 +1232,7 @@ Eterm erts_match_set_run(Process *p, Binary *mpsp, { Eterm ret; - ret = db_prog_match(p, mpsp, NIL, NULL, args, num_args, + ret = db_prog_match(p, mpsp, NIL, args, num_args, in_flags, return_flags); #if defined(HARDDEBUG) if (is_non_value(ret)) { @@ -1258,7 +1257,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, + ret = db_prog_match(p, mpsp, args, NULL, num_args, ERTS_PAM_COPY_RESULT, return_flags); #if defined(HARDDEBUG) @@ -1755,7 +1754,7 @@ static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity) ** i.e. 'DCOMP_TRACE' was specified */ Eterm db_prog_match(Process *c_p, Binary *bprog, - Eterm term, Eterm* base, + Eterm term, Eterm *termp, int arity, enum erts_pam_run_flags in_flags, @@ -1856,7 +1855,6 @@ restart: for (i=0; inum_bindings; i++) { variables[i].term = THE_NON_VALUE; variables[i].proc = NULL; - variables[i].base = base; } #endif @@ -2156,7 +2154,7 @@ restart: break; case matchPushVResult: if (!(in_flags & ERTS_PAM_COPY_RESULT)) goto case_matchPushV; - /* Build (NULL-based) copy on callers heap */ + /* Build copy on callers heap */ n = *pc++; ASSERT(is_value(variables[n].term)); ASSERT(!variables[n].proc); @@ -2164,14 +2162,12 @@ restart: *esp++ = variables[n].term; #ifdef DEBUG variables[n].proc = c_p; - variables[n].base = NULL; #endif break; case matchPushV: case_matchPushV: n = *pc++; ASSERT(is_value(variables[n].term)); - ASSERT(!variables[n].base); *esp++ = variables[n].term; break; case matchPushExpr: @@ -5204,16 +5200,13 @@ Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog, int all, DbTerm* obj, Eterm** hpp, Uint extra) { Uint32 dummy; - Eterm* base; Eterm res; if (tb->compress) { obj = db_alloc_tmp_uncompressed(tb, obj); - base = NULL; } - else base = NULL; - res = db_prog_match(c_p, bprog, make_tuple(obj->tpl), base, NULL, 0, + res = db_prog_match(c_p, bprog, make_tuple(obj->tpl), NULL, 0, ERTS_PAM_COPY_RESULT|ERTS_PAM_CONTIGUOUS_TUPLE, &dummy); if (is_value(res) && hpp!=NULL) { diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 10899bb3e7..c0966655cd 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -432,7 +432,7 @@ Binary *db_match_compile(Eterm *matchexpr, Eterm *guards, Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog, int all, DbTerm* obj, Eterm** hpp, Uint extra); -Eterm db_prog_match(Process *p, Binary *prog, Eterm term, Eterm* base, +Eterm db_prog_match(Process *p, Binary *prog, Eterm term, Eterm *termp, int arity, enum erts_pam_run_flags in_flags, Uint32 *return_flags /* Zeroed on enter */); -- cgit v1.2.3 From 298210017aedaf01b2eb477f4375af4d30822c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 22 Jun 2015 16:53:23 +0200 Subject: erts: Remove halfword MemKind mseg --- erts/emulator/sys/common/erl_mseg.c | 332 +++++++++++++++--------------------- 1 file changed, 135 insertions(+), 197 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 8073d5b908..7eb8a4a460 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -99,8 +99,6 @@ static const int debruijn[32] = { static int atoms_initialized; -typedef struct mem_kind_t MemKind; - const ErtsMsegOpt_t erts_mseg_default_opt = { 1, /* Use cache */ 1, /* Preserv data */ @@ -139,7 +137,14 @@ struct cache_t_ { typedef struct ErtsMsegAllctr_t_ ErtsMsegAllctr_t; -struct mem_kind_t { +struct ErtsMsegAllctr_t_ { + int ix; + + int is_init_done; + int is_thread_safe; + erts_mtx_t mtx; + + int is_cache_check_scheduled; cache_t cache[MAX_CACHE_SIZE]; cache_t cache_unpowered_node; @@ -165,24 +170,6 @@ struct mem_kind_t { } max_ever; } segments; - ErtsMsegAllctr_t *ma; - const char* name; - MemKind* next; -};/*MemKind*/ - -struct ErtsMsegAllctr_t_ { - int ix; - - int is_init_done; - int is_thread_safe; - erts_mtx_t mtx; - - int is_cache_check_scheduled; - - MemKind* mk_list; - - MemKind the_mem; - Uint max_cache_size; Uint abs_max_cache_bad_fit; Uint rel_max_cache_bad_fit; @@ -294,7 +281,7 @@ schedule_cache_check(ErtsMsegAllctr_t *ma) { /* #define ERTS_PRINT_ERTS_MMAP */ static ERTS_INLINE void * -mseg_create(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, UWord *sizep) +mseg_create(ErtsMsegAllctr_t *ma, Uint flags, UWord *sizep) { #ifdef ERTS_PRINT_ERTS_MMAP UWord req_size = *sizep; @@ -318,7 +305,7 @@ mseg_create(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, UWord *sizep) } static ERTS_INLINE void -mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void *seg_p, UWord size) { +mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, void *seg_p, UWord size) { Uint32 mmap_flags = 0; if (MSEG_FLG_IS_2POW(flags)) @@ -335,7 +322,7 @@ mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void *seg_p, UWord s } static ERTS_INLINE void * -mseg_recreate(ErtsMsegAllctr_t *ma, Uint flags, MemKind* mk, void *old_seg, UWord old_size, UWord *sizep) +mseg_recreate(ErtsMsegAllctr_t *ma, Uint flags, void *old_seg, UWord old_size, UWord *sizep) { #ifdef ERTS_PRINT_ERTS_MMAP UWord req_size = *sizep; @@ -369,11 +356,8 @@ do { \ || erts_smp_thr_progress_is_blocking() \ || ERTS_IS_CRASH_DUMPING); \ } while (0) -#define ERTS_DBG_MK_CHK_THR_ACCESS(MK) \ - ERTS_DBG_MA_CHK_THR_ACCESS((MK)->ma) #else #define ERTS_DBG_MA_CHK_THR_ACCESS(MA) -#define ERTS_DBG_MK_CHK_THR_ACCESS(MK) #endif /* Cache interface */ @@ -386,10 +370,10 @@ static ERTS_INLINE void mseg_cache_clear_node(cache_t *c) { c->prev = c; } -static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, UWord size, Uint flags) { +static ERTS_INLINE int cache_bless_segment(ErtsMsegAllctr_t *ma, void *seg, UWord size, Uint flags) { cache_t *c; - ERTS_DBG_MK_CHK_THR_ACCESS(mk); + ERTS_DBG_MA_CHK_THR_ACCESS(ma); ASSERT(!MSEG_FLG_IS_2POW(flags) || (MSEG_FLG_IS_2POW(flags) && MAP_IS_ALIGNED(seg) && IS_2POW(size))); @@ -398,11 +382,11 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, UWord size, U * Large blocks has no such cache and it is up to mseg to cache them to speed things up. */ - if (!erts_circleq_is_empty(&(mk->cache_free))) { + if (!erts_circleq_is_empty(&(ma->cache_free))) { /* We have free slots, use one to cache the segment */ - c = erts_circleq_head(&(mk->cache_free)); + c = erts_circleq_head(&(ma->cache_free)); erts_circleq_remove(c); c->seg = seg; @@ -414,29 +398,28 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, UWord size, U ASSERT(ix < CACHE_AREAS); ASSERT((1 << (ix + MSEG_ALIGN_BITS)) == size); - erts_circleq_push_head(&(mk->cache_powered_node[ix]), c); + erts_circleq_push_head(&(ma->cache_powered_node[ix]), c); } else - erts_circleq_push_head(&(mk->cache_unpowered_node), c); + erts_circleq_push_head(&(ma->cache_unpowered_node), c); - mk->cache_size++; - ASSERT(mk->cache_size <= mk->ma->max_cache_size); + ma->cache_size++; return 1; - } else if (!MSEG_FLG_IS_2POW(flags) && !erts_circleq_is_empty(&(mk->cache_unpowered_node))) { + } else if (!MSEG_FLG_IS_2POW(flags) && !erts_circleq_is_empty(&(ma->cache_unpowered_node))) { /* No free slots. * Evict oldest slot from unpowered cache so we can cache an unpowered (sbc) segment */ - c = erts_circleq_tail(&(mk->cache_unpowered_node)); + c = erts_circleq_tail(&(ma->cache_unpowered_node)); erts_circleq_remove(c); - mseg_destroy(mk->ma, ERTS_MSEG_FLG_NONE, mk, c->seg, c->size); + mseg_destroy(ma, ERTS_MSEG_FLG_NONE, c->seg, c->size); mseg_cache_clear_node(c); c->seg = seg; c->size = size; - erts_circleq_push_head(&(mk->cache_unpowered_node), c); + erts_circleq_push_head(&(ma->cache_unpowered_node), c); return 1; } else if (!MSEG_FLG_IS_2POW(flags)) { @@ -450,20 +433,20 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, UWord size, U int i; for( i = 0; i < CACHE_AREAS; i++) { - if (erts_circleq_is_empty(&(mk->cache_powered_node[i]))) + if (erts_circleq_is_empty(&(ma->cache_powered_node[i]))) continue; - c = erts_circleq_tail(&(mk->cache_powered_node[i])); + c = erts_circleq_tail(&(ma->cache_powered_node[i])); erts_circleq_remove(c); - mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, c->seg, c->size); + mseg_destroy(ma, ERTS_MSEG_FLG_2POW, c->seg, c->size); mseg_cache_clear_node(c); c->seg = seg; c->size = size; - erts_circleq_push_head(&(mk->cache_unpowered_node), c); + erts_circleq_push_head(&(ma->cache_unpowered_node), c); return 1; } @@ -472,11 +455,11 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, UWord size, U return 0; } -static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flags) { +static ERTS_INLINE void *cache_get_segment(ErtsMsegAllctr_t *ma, UWord *size_p, Uint flags) { UWord size = *size_p; - ERTS_DBG_MK_CHK_THR_ACCESS(mk); + ERTS_DBG_MA_CHK_THR_ACCESS(ma); if (MSEG_FLG_IS_2POW(flags)) { @@ -489,10 +472,10 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag for( i = ix; i < CACHE_AREAS; i++) { - if (erts_circleq_is_empty(&(mk->cache_powered_node[i]))) + if (erts_circleq_is_empty(&(ma->cache_powered_node[i]))) continue; - c = erts_circleq_head(&(mk->cache_powered_node[i])); + c = erts_circleq_head(&(ma->cache_powered_node[i])); erts_circleq_remove(c); ASSERT(IS_2POW(c->size)); @@ -501,31 +484,31 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag csize = c->size; seg = (char*) c->seg; - mk->cache_size--; - mk->cache_hits++; + ma->cache_size--; + ma->cache_hits++; /* link to free cache list */ mseg_cache_clear_node(c); - erts_circleq_push_head(&(mk->cache_free), c); + erts_circleq_push_head(&(ma->cache_free), c); - ASSERT(!(mk->cache_size < 0)); + ASSERT(!(ma->cache_size < 0)); if (csize != size) - mseg_destroy(mk->ma, ERTS_MSEG_FLG_2POW, mk, seg + size, csize - size); + mseg_destroy(ma, ERTS_MSEG_FLG_2POW, seg + size, csize - size); return seg; } } - else if (!erts_circleq_is_empty(&(mk->cache_unpowered_node))) { + else if (!erts_circleq_is_empty(&(ma->cache_unpowered_node))) { void *seg; cache_t *c; cache_t *best = NULL; UWord bdiff = 0; UWord csize; - UWord bad_max_abs = mk->ma->abs_max_cache_bad_fit; - UWord bad_max_rel = mk->ma->rel_max_cache_bad_fit; + UWord bad_max_abs = ma->abs_max_cache_bad_fit; + UWord bad_max_rel = ma->rel_max_cache_bad_fit; - erts_circleq_foreach(c, &(mk->cache_unpowered_node)) { + erts_circleq_foreach(c, &(ma->cache_unpowered_node)) { csize = c->size; if (csize >= size) { if (((csize - size)*100 < bad_max_rel*size) && (csize - size) < bad_max_abs ) { @@ -534,11 +517,11 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag erts_circleq_remove(c); - mk->cache_size--; - mk->cache_hits++; + ma->cache_size--; + ma->cache_hits++; mseg_cache_clear_node(c); - erts_circleq_push_head(&(mk->cache_free), c); + erts_circleq_push_head(&(ma->cache_free), c); *size_p = csize; @@ -561,7 +544,7 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag ASSERT(best->seg); ASSERT(best->size > 0); - mk->cache_hits++; + ma->cache_hits++; /* Use current cache placement for remaining segment space */ @@ -585,7 +568,7 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, UWord *size_p, Uint flag * using callbacks from aux-work in the scheduler. */ -static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, Uint flags, cache_t *head) { +static ERTS_INLINE Uint mseg_drop_one_cache_size(ErtsMsegAllctr_t *ma, Uint flags, cache_t *head) { cache_t *c = NULL; c = erts_circleq_tail(head); @@ -594,19 +577,19 @@ static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, Uint flags if (erts_mtrace_enabled) erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg); - mseg_destroy(mk->ma, flags, mk, c->seg, c->size); + mseg_destroy(ma, flags, c->seg, c->size); mseg_cache_clear_node(c); - erts_circleq_push_head(&(mk->cache_free), c); + erts_circleq_push_head(&(ma->cache_free), c); - mk->segments.current.watermark--; - mk->cache_size--; + ma->segments.current.watermark--; + ma->cache_size--; - ASSERT( mk->cache_size >= 0 ); + ASSERT(ma->cache_size >= 0); - return mk->cache_size; + return ma->cache_size; } -static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, Uint flags, cache_t *head) { +static ERTS_INLINE Uint mseg_drop_cache_size(ErtsMsegAllctr_t *ma, Uint flags, cache_t *head) { cache_t *c = NULL; while (!erts_circleq_is_empty(head)) { @@ -617,58 +600,52 @@ static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, Uint flags, ca if (erts_mtrace_enabled) erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg); - mseg_destroy(mk->ma, flags, mk, c->seg, c->size); + mseg_destroy(ma, flags, c->seg, c->size); mseg_cache_clear_node(c); - erts_circleq_push_head(&(mk->cache_free), c); - - mk->segments.current.watermark--; - mk->cache_size--; + erts_circleq_push_head(&(ma->cache_free), c); + ma->segments.current.watermark--; + ma->cache_size--; } - ASSERT( mk->cache_size >= 0 ); + ASSERT(ma->cache_size >= 0); - return mk->cache_size; + return ma->cache_size; } -/* mseg_check_memkind_cache - * - Check if we can empty some cached segments in this - * MemKind. +/* mseg_check_cache + * - Check if we can empty some cached segments in this allocator */ -static Uint mseg_check_memkind_cache(MemKind *mk) { +static Uint mseg_check_cache(ErtsMsegAllctr_t *ma) { int i; - ERTS_DBG_MK_CHK_THR_ACCESS(mk); + ERTS_DBG_MA_CHK_THR_ACCESS(ma); for (i = 0; i < CACHE_AREAS; i++) { - if (!erts_circleq_is_empty(&(mk->cache_powered_node[i]))) - return mseg_drop_one_memkind_cache_size(mk, ERTS_MSEG_FLG_2POW, &(mk->cache_powered_node[i])); + if (!erts_circleq_is_empty(&(ma->cache_powered_node[i]))) + return mseg_drop_one_cache_size(ma, ERTS_MSEG_FLG_2POW, &(ma->cache_powered_node[i])); } - if (!erts_circleq_is_empty(&(mk->cache_unpowered_node))) - return mseg_drop_one_memkind_cache_size(mk, ERTS_MSEG_FLG_NONE, &(mk->cache_unpowered_node)); + if (!erts_circleq_is_empty(&(ma->cache_unpowered_node))) + return mseg_drop_one_cache_size(ma, ERTS_MSEG_FLG_NONE, &(ma->cache_unpowered_node)); return 0; } /* mseg_cache_check * - Check if we have some cache we can purge - * in any of the memkinds. */ static void mseg_cache_check(ErtsMsegAllctr_t *ma) { - MemKind* mk; Uint empty_cache = 1; ERTS_MSEG_LOCK(ma); - for (mk = ma->mk_list; mk; mk = mk->next) { - if (mseg_check_memkind_cache(mk)) - empty_cache = 0; - } + if (mseg_check_cache(ma)) + empty_cache = 0; /* If all MemKinds caches are empty, * remove aux-work callback @@ -686,7 +663,7 @@ static void mseg_cache_check(ErtsMsegAllctr_t *ma) { /* erts_mseg_cache_check * - This is a callback that is scheduled as aux-work from * schedulers and is called at some interval if we have a cache - * on this mseg-allocator and memkind. + * on this mseg-allocator. * - Purpose: Empty cache slowly so we don't collect mapped areas * and bloat memory. */ @@ -696,42 +673,32 @@ void erts_mseg_cache_check(void) { } -/* *_mseg_clear_*_cache +/* mseg_clear_cache * Remove cached segments from the allocator completely */ -static void mseg_clear_memkind_cache(MemKind *mk) { + +static void mseg_clear_cache(ErtsMsegAllctr_t *ma) { int i; + ERTS_MSEG_LOCK(ma); + ERTS_DBG_MA_CHK_THR_ACCESS(ma); /* drop pow2 caches */ for (i = 0; i < CACHE_AREAS; i++) { - if (erts_circleq_is_empty(&(mk->cache_powered_node[i]))) + if (erts_circleq_is_empty(&(ma->cache_powered_node[i]))) continue; - mseg_drop_memkind_cache_size(mk, ERTS_MSEG_FLG_2POW, &(mk->cache_powered_node[i])); - ASSERT(erts_circleq_is_empty(&(mk->cache_powered_node[i]))); + mseg_drop_cache_size(ma, ERTS_MSEG_FLG_2POW, &(ma->cache_powered_node[i])); + ASSERT(erts_circleq_is_empty(&(ma->cache_powered_node[i]))); } /* drop varied caches */ - if (!erts_circleq_is_empty(&(mk->cache_unpowered_node))) - mseg_drop_memkind_cache_size(mk, ERTS_MSEG_FLG_NONE, &(mk->cache_unpowered_node)); + if (!erts_circleq_is_empty(&(ma->cache_unpowered_node))) + mseg_drop_cache_size(ma, ERTS_MSEG_FLG_NONE, &(ma->cache_unpowered_node)); - ASSERT(erts_circleq_is_empty(&(mk->cache_unpowered_node))); - ASSERT(mk->cache_size == 0); -} - -static void mseg_clear_cache(ErtsMsegAllctr_t *ma) { - MemKind* mk; - - ERTS_MSEG_LOCK(ma); - ERTS_DBG_MA_CHK_THR_ACCESS(ma); - - - for (mk = ma->mk_list; mk; mk = mk->next) { - mseg_clear_memkind_cache(mk); - } + ASSERT(erts_circleq_is_empty(&(ma->cache_unpowered_node))); + ASSERT(ma->cache_size == 0); INC_CC(ma, clear_cache); - ERTS_MSEG_UNLOCK(ma); } @@ -740,21 +707,12 @@ void erts_mseg_clear_cache(void) { mseg_clear_cache(ERTS_MSEG_ALLCTR_IX(0)); } - - -static ERTS_INLINE MemKind* memkind(ErtsMsegAllctr_t *ma, - const ErtsMsegOpt_t *opt) -{ - return &ma->the_mem; -} - static void * mseg_alloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, UWord *size_p, Uint flags, const ErtsMsegOpt_t *opt) { UWord size; void *seg; - MemKind* mk = memkind(ma, opt); INC_CC(ma, alloc); @@ -768,10 +726,10 @@ mseg_alloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, UWord *size_p, } } - if (opt->cache && mk->cache_size > 0 && (seg = cache_get_segment(mk, &size, flags)) != NULL) + if (opt->cache && ma->cache_size > 0 && (seg = cache_get_segment(ma, &size, flags)) != NULL) goto done; - seg = mseg_create(ma, flags, mk, &size); + seg = mseg_create(ma, flags, &size); if (!seg) *size_p = 0; @@ -781,7 +739,7 @@ done: if (erts_mtrace_enabled) erts_mtrace_crr_alloc(seg, atype, ERTS_MTRACE_SEGMENT_ID, size); - ERTS_MSEG_ALLOC_STAT(mk,size); + ERTS_MSEG_ALLOC_STAT(ma,size); } return seg; @@ -792,11 +750,9 @@ static void mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, UWord size, Uint flags, const ErtsMsegOpt_t *opt) { - MemKind* mk = memkind(ma, opt); - - ERTS_MSEG_DEALLOC_STAT(mk,size); + ERTS_MSEG_DEALLOC_STAT(ma,size); - if (opt->cache && cache_bless_segment(mk, seg, size, flags)) { + if (opt->cache && cache_bless_segment(ma, seg, size, flags)) { schedule_cache_check(ma); goto done; } @@ -804,7 +760,7 @@ mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, UWord size, if (erts_mtrace_enabled) erts_mtrace_crr_free(atype, SEGTYPE, seg); - mseg_destroy(ma, flags, mk, seg, size); + mseg_destroy(ma, flags, seg, size); done: @@ -815,7 +771,6 @@ static void * mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, UWord old_size, UWord *new_size_p, Uint flags, const ErtsMsegOpt_t *opt) { - MemKind* mk; void *new_seg; UWord new_size; @@ -834,7 +789,6 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, return NULL; } - mk = memkind(ma, opt); new_seg = seg; if (!MSEG_FLG_IS_2POW(flags)) @@ -849,7 +803,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, if (new_size > old_size) { if (opt->preserv) { - new_seg = mseg_recreate(ma, flags, mk, (void *) seg, old_size, &new_size); + new_seg = mseg_recreate(ma, flags, (void *) seg, old_size, &new_size); if (!new_seg) new_size = old_size; } @@ -869,7 +823,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, new_size = old_size; } else { - new_seg = mseg_recreate(ma, flags, mk, (void *) seg, old_size, &new_size); + new_seg = mseg_recreate(ma, flags, (void *) seg, old_size, &new_size); if (!new_seg) new_size = old_size; } @@ -883,7 +837,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, ASSERT(!MSEG_FLG_IS_2POW(flags) || IS_2POW(new_size)); *new_size_p = new_size; - ERTS_MSEG_REALLOC_STAT(mk, old_size, new_size); + ERTS_MSEG_REALLOC_STAT(ma, old_size, new_size); return new_seg; } @@ -1153,63 +1107,63 @@ info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp } static Eterm -info_status(ErtsMsegAllctr_t *ma, MemKind* mk, int *print_to_p, void *print_to_arg, +info_status(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, int begin_new_max_period, Uint **hpp, Uint *szp) { Eterm res = THE_NON_VALUE; - if (mk->segments.max_ever.no < mk->segments.max.no) - mk->segments.max_ever.no = mk->segments.max.no; - if (mk->segments.max_ever.sz < mk->segments.max.sz) - mk->segments.max_ever.sz = mk->segments.max.sz; + if (ma->segments.max_ever.no < ma->segments.max.no) + ma->segments.max_ever.no = ma->segments.max.no; + if (ma->segments.max_ever.sz < ma->segments.max.sz) + ma->segments.max_ever.sz = ma->segments.max.sz; if (print_to_p) { int to = *print_to_p; void *arg = print_to_arg; - erts_print(to, arg, "cached_segments: %beu\n", mk->cache_size); - erts_print(to, arg, "cache_hits: %beu\n", mk->cache_hits); + erts_print(to, arg, "cached_segments: %beu\n", ma->cache_size); + erts_print(to, arg, "cache_hits: %beu\n", ma->cache_hits); erts_print(to, arg, "segments: %beu %beu %beu\n", - mk->segments.current.no, mk->segments.max.no, mk->segments.max_ever.no); + ma->segments.current.no, ma->segments.max.no, ma->segments.max_ever.no); erts_print(to, arg, "segments_size: %beu %beu %beu\n", - mk->segments.current.sz, mk->segments.max.sz, mk->segments.max_ever.sz); + ma->segments.current.sz, ma->segments.max.sz, ma->segments.max_ever.sz); erts_print(to, arg, "segments_watermark: %beu\n", - mk->segments.current.watermark); + ma->segments.current.watermark); } if (hpp || szp) { res = NIL; add_2tup(hpp, szp, &res, am.segments_watermark, - bld_unstable_uint(hpp, szp, mk->segments.current.watermark)); + bld_unstable_uint(hpp, szp, ma->segments.current.watermark)); add_4tup(hpp, szp, &res, am.segments_size, - bld_unstable_uint(hpp, szp, mk->segments.current.sz), - bld_unstable_uint(hpp, szp, mk->segments.max.sz), - bld_unstable_uint(hpp, szp, mk->segments.max_ever.sz)); + bld_unstable_uint(hpp, szp, ma->segments.current.sz), + bld_unstable_uint(hpp, szp, ma->segments.max.sz), + bld_unstable_uint(hpp, szp, ma->segments.max_ever.sz)); add_4tup(hpp, szp, &res, am.segments, - bld_unstable_uint(hpp, szp, mk->segments.current.no), - bld_unstable_uint(hpp, szp, mk->segments.max.no), - bld_unstable_uint(hpp, szp, mk->segments.max_ever.no)); + bld_unstable_uint(hpp, szp, ma->segments.current.no), + bld_unstable_uint(hpp, szp, ma->segments.max.no), + bld_unstable_uint(hpp, szp, ma->segments.max_ever.no)); add_2tup(hpp, szp, &res, am.cache_hits, - bld_unstable_uint(hpp, szp, mk->cache_hits)); + bld_unstable_uint(hpp, szp, ma->cache_hits)); add_2tup(hpp, szp, &res, am.cached_segments, - bld_unstable_uint(hpp, szp, mk->cache_size)); + bld_unstable_uint(hpp, szp, ma->cache_size)); } if (begin_new_max_period) { - mk->segments.max.no = mk->segments.current.no; - mk->segments.max.sz = mk->segments.current.sz; + ma->segments.max.no = ma->segments.current.no; + ma->segments.max.sz = ma->segments.current.sz; } return res; } -static Eterm info_memkind(ErtsMsegAllctr_t *ma, MemKind* mk, int *print_to_p, void *print_to_arg, +static Eterm info_memkind(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, int begin_max_per, Uint **hpp, Uint *szp) { Eterm res = THE_NON_VALUE; @@ -1217,15 +1171,15 @@ static Eterm info_memkind(ErtsMsegAllctr_t *ma, MemKind* mk, int *print_to_p, vo Eterm values[3]; if (print_to_p) { - erts_print(*print_to_p, print_to_arg, "memory kind: %s\n", mk->name); + erts_print(*print_to_p, print_to_arg, "memory kind: %s\n", "all memory"); } if (hpp || szp) { atoms[0] = am.name; atoms[1] = am.status; atoms[2] = am.calls; - values[0] = erts_bld_string(hpp, szp, mk->name); + values[0] = erts_bld_string(hpp, szp, "all memory"); } - values[1] = info_status(ma, mk, print_to_p, print_to_arg, begin_max_per, hpp, szp); + values[1] = info_status(ma, print_to_p, print_to_arg, begin_max_per, hpp, szp); values[2] = info_calls(ma, print_to_p, print_to_arg, hpp, szp); if (hpp || szp) @@ -1234,7 +1188,6 @@ static Eterm info_memkind(ErtsMsegAllctr_t *ma, MemKind* mk, int *print_to_p, vo return res; } - static Eterm info_version(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp) { @@ -1299,7 +1252,7 @@ erts_mseg_info(int ix, ERTS_MSEG_LOCK(ma); ERTS_DBG_MA_CHK_THR_ACCESS(ma); - values[n++] = info_memkind(ma, &ma->the_mem, print_to_p, print_to_arg, begin_max_per, hpp, szp); + values[n++] = info_memkind(ma, print_to_p, print_to_arg, begin_max_per, hpp, szp); if (hpp || szp) res = bld_2tup_list(hpp, szp, n, atoms, values); @@ -1376,13 +1329,10 @@ Uint erts_mseg_no(const ErtsMsegOpt_t *opt) { ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_OPT(opt); - MemKind* mk; - Uint n = 0; + Uint n; ERTS_MSEG_LOCK(ma); ERTS_DBG_MA_CHK_THR_ACCESS(ma); - for (mk=ma->mk_list; mk; mk=mk->next) { - n += mk->segments.current.no; - } + n = ma->segments.current.no; ERTS_MSEG_UNLOCK(ma); return n; } @@ -1394,16 +1344,16 @@ erts_mseg_unit_size(void) } -static void mem_kind_init(ErtsMsegAllctr_t *ma, MemKind* mk, const char* name) +static void mem_cache_init(ErtsMsegAllctr_t *ma) { int i; /* Clear all cache headers */ - mseg_cache_clear_node(&(mk->cache_free)); - mseg_cache_clear_node(&(mk->cache_unpowered_node)); + mseg_cache_clear_node(&(ma->cache_free)); + mseg_cache_clear_node(&(ma->cache_unpowered_node)); for (i = 0; i < CACHE_AREAS; i++) { - mseg_cache_clear_node(&(mk->cache_powered_node[i])); + mseg_cache_clear_node(&(ma->cache_powered_node[i])); } /* Populate cache free list */ @@ -1411,25 +1361,20 @@ static void mem_kind_init(ErtsMsegAllctr_t *ma, MemKind* mk, const char* name) ASSERT(ma->max_cache_size <= MAX_CACHE_SIZE); for (i = 0; i < ma->max_cache_size; i++) { - mseg_cache_clear_node(&(mk->cache[i])); - erts_circleq_push_head(&(mk->cache_free), &(mk->cache[i])); + mseg_cache_clear_node(&(ma->cache[i])); + erts_circleq_push_head(&(ma->cache_free), &(ma->cache[i])); } - mk->cache_size = 0; - mk->cache_hits = 0; - - mk->segments.current.watermark = 0; - mk->segments.current.no = 0; - mk->segments.current.sz = 0; - mk->segments.max.no = 0; - mk->segments.max.sz = 0; - mk->segments.max_ever.no = 0; - mk->segments.max_ever.sz = 0; - - mk->ma = ma; - mk->name = name; - mk->next = ma->mk_list; - ma->mk_list = mk; + ma->cache_size = 0; + ma->cache_hits = 0; + + ma->segments.current.watermark = 0; + ma->segments.current.no = 0; + ma->segments.current.sz = 0; + ma->segments.max.no = 0; + ma->segments.max.sz = 0; + ma->segments.max_ever.no = 0; + ma->segments.max_ever.sz = 0; } void @@ -1488,9 +1433,7 @@ erts_mseg_init(ErtsMsegInit_t *init) if (ma->max_cache_size > MAX_CACHE_SIZE) ma->max_cache_size = MAX_CACHE_SIZE; - ma->mk_list = NULL; - - mem_kind_init(ma, &ma->the_mem, "all memory"); + mem_cache_init(ma); sys_memzero((void *) &ma->calls, sizeof(ErtsMsegCalls)); } @@ -1499,13 +1442,8 @@ erts_mseg_init(ErtsMsegInit_t *init) static ERTS_INLINE Uint tot_cache_size(ErtsMsegAllctr_t *ma) { - MemKind* mk; - Uint sz = 0; ERTS_DBG_MA_CHK_THR_ACCESS(ma); - for (mk=ma->mk_list; mk; mk=mk->next) { - sz += mk->cache_size; - } - return sz; + return ma->cache_size; } /* -- cgit v1.2.3 From 5c994d828c05744919144e2977bf13ec37e450f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 22 Jun 2015 17:55:54 +0200 Subject: erts: Remove halfword etp-commands --- erts/etc/unix/etp-commands.in | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 19d67de92f..7b554e71f2 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -774,7 +774,7 @@ define etp-pid-1 # set $etp_pid_1 = (Eterm)($arg0) if ($etp_pid_1 & 0xF) == 0x3 - if (etp_arch_bits == 64 && etp_halfword == 0) + if (etp_arch_bits == 64) if (etp_big_endian) set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 36) & 0x0fffffff) else @@ -834,7 +834,7 @@ define etp-port-1 # set $etp_port_1 = (Eterm)($arg0) if ($etp_port_1 & 0xF) == 0x7 - if (etp_arch_bits == 64 && etp_halfword == 0) + if (etp_arch_bits == 64) if (etp_big_endian) set $etp_port_data = (unsigned) ((((Uint64) $etp_port_1) >> 36) & 0x0fffffff) else @@ -1577,7 +1577,7 @@ define etp-term-dump-pid # set $etp_pid_1 = (Eterm)($arg0) if ($etp_pid_1 & 0xF) == 0x3 - if (etp_arch_bits == 64 && etp_halfword == 0) + if (etp_arch_bits == 64) if (etp_big_endian) set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 36) & 0x0fffffff) else @@ -1622,7 +1622,7 @@ end define etp-pid2pix-1 # Args: Eterm # - if (etp_arch_bits == 64 && etp_halfword == 0) + if (etp_arch_bits == 64) if (etp_big_endian) set $etp_pix = (int) (((Uint64) $arg0) & 0x0fffffff) else @@ -1965,7 +1965,7 @@ end define etp-port-id2pix-1 # Args: Eterm # - if (etp_arch_bits == 64 && etp_halfword == 0) + if (etp_arch_bits == 64) if (etp_big_endian) set $etp_pix = (int) (((Uint64) $arg0) & 0x0fffffff) elser @@ -2493,12 +2493,6 @@ define etp-system-info printf "Little\n" end printf "Word size: %d-bit\n", etp_arch_bits - printf "Halfword: " - if (etp_halfword) - printf "yes\n" - else - printf "no\n" - end printf "HiPE support: " if (etp_hipe) printf "yes\n" -- cgit v1.2.3 From 0dedcdefde3f5601453ab12faf2a65b616118333 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 25 Jun 2015 14:28:54 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 26 ++++++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 35e6e55e72..adc73ceae0 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,32 @@

This document describes the changes made to the ERTS application.

+
Erts 6.4.1.1 + +
Fixed Bugs and Malfunctions + + +

Fix garbage collection of literals in code purge

+

During code purging and check_process_code, the + checking of the binary reference embedded in the match + binary state was omitted for the tracing tests. This + would cause the binary match state to reference + deallocated memory.

+

+ Own Id: OTP-12821

+
+ +

+ Fix a rare hanging of the VM seen to happen just after + emulator start. Bug exists since R14.

+

+ Own Id: OTP-12859 Aux Id: seq12882

+
+
+
+ +
+
Erts 6.4.1
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 9e5aa999e6..b806cb0b7a 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.4.1 +VSN = 6.4.1.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 37a9aa5f35466838af383d53490b040142468673 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 26 Jun 2015 14:22:14 +0200 Subject: erts: Fix ETS race between object deleter and table unfixer causing the delete marked object to be left in the table after safe_fixtable(_,false) has returned. This is not super serious as the delete marked object is quite benign and will be deleted at the next unfix operation. --- erts/emulator/beam/erl_db_hash.c | 60 +++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 23 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 81b0c4465c..98a2e2842a 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -148,8 +148,11 @@ static ERTS_INLINE Uint hash_to_ix(DbTableHash* tb, HashValue hval) } /* Remember a slot containing a pseudo-deleted item (INVALID_HASH) -*/ -static ERTS_INLINE void add_fixed_deletion(DbTableHash* tb, int ix) + * Return false if we got raced by unfixing thread + * and the object should be deleted for real. + */ +static ERTS_INLINE int add_fixed_deletion(DbTableHash* tb, int ix, + erts_aint_t fixated_by_me) { erts_aint_t was_next; erts_aint_t exp_next; @@ -160,12 +163,18 @@ static ERTS_INLINE void add_fixed_deletion(DbTableHash* tb, int ix) fixd->slot = ix; was_next = erts_smp_atomic_read_acqb(&tb->fixdel); do { /* Lockless atomic insertion in linked list: */ - exp_next = was_next; + if (NFIXED(tb) <= fixated_by_me) { + erts_db_free(ERTS_ALC_T_DB_FIX_DEL, (DbTable*)tb, + fixd, sizeof(FixedDeletion)); + return 0; /* raced by unfixer */ + } + exp_next = was_next; fixd->next = (FixedDeletion*) exp_next; - was_next = erts_smp_atomic_cmpxchg_relb(&tb->fixdel, - (erts_aint_t) fixd, - exp_next); + was_next = erts_smp_atomic_cmpxchg_mb(&tb->fixdel, + (erts_aint_t) fixd, + exp_next); }while (was_next != exp_next); + return 1; } @@ -607,8 +616,8 @@ void db_unfix_table_hash(DbTableHash *tb) || (erts_smp_lc_rwmtx_is_rlocked(&tb->common.rwlock) && !tb->common.is_thread_safe)); restart: - fixdel = (FixedDeletion*) erts_smp_atomic_xchg_acqb(&tb->fixdel, - (erts_aint_t) NULL); + fixdel = (FixedDeletion*) erts_smp_atomic_xchg_mb(&tb->fixdel, + (erts_aint_t) NULL); while (fixdel != NULL) { FixedDeletion *fx = fixdel; int ix = fx->slot; @@ -1142,9 +1151,9 @@ int db_erase_hash(DbTable *tbl, Eterm key, Eterm *ret) while(b != 0) { if (has_live_key(tb,b,key,hval)) { --nitems_diff; - if (nitems_diff == -1 && IS_FIXED(tb)) { + if (nitems_diff == -1 && IS_FIXED(tb) + && add_fixed_deletion(tb, ix, 0)) { /* Pseudo remove (no need to keep several of same key) */ - add_fixed_deletion(tb, ix); b->hvalue = INVALID_HASH; } else { *bp = b->next; @@ -1196,9 +1205,8 @@ static int db_erase_object_hash(DbTable *tbl, Eterm object, Eterm *ret) ++nkeys; if (db_eq(&tb->common,object, &b->dbterm)) { --nitems_diff; - if (nkeys==1 && IS_FIXED(tb)) { /* Pseudo remove */ - add_fixed_deletion(tb,ix); - b->hvalue = INVALID_HASH; + if (nkeys==1 && IS_FIXED(tb) && add_fixed_deletion(tb,ix,0)) { + b->hvalue = INVALID_HASH; /* Pseudo remove */ bp = &b->next; b = b->next; } else { @@ -1820,14 +1828,17 @@ static int db_select_delete_hash(Process *p, int did_erase = 0; if (db_match_dbterm(&tb->common, p, mpi.mp, 0, &(*current)->dbterm, NULL, 0) == am_true) { + HashDbTerm *del; if (NFIXED(tb) > fixated_by_me) { /* fixated by others? */ if (slot_ix != last_pseudo_delete) { - add_fixed_deletion(tb, slot_ix); - last_pseudo_delete = slot_ix; + if (!add_fixed_deletion(tb, slot_ix, fixated_by_me)) + goto do_erase; + last_pseudo_delete = slot_ix; } (*current)->hvalue = INVALID_HASH; } else { - HashDbTerm *del = *current; + do_erase: + del = *current; *current = (*current)->next; free_term(tb, del); did_erase = 1; @@ -1931,14 +1942,17 @@ static int db_select_delete_continue_hash(Process *p, int did_erase = 0; if (db_match_dbterm(&tb->common, p, mp, 0, &(*current)->dbterm, NULL, 0) == am_true) { + HashDbTerm *del; if (NFIXED(tb) > fixated_by_me) { /* fixated by others? */ if (slot_ix != last_pseudo_delete) { - add_fixed_deletion(tb, slot_ix); + if (!add_fixed_deletion(tb, slot_ix, fixated_by_me)) + goto do_erase; last_pseudo_delete = slot_ix; } (*current)->hvalue = INVALID_HASH; } else { - HashDbTerm *del = *current; + do_erase: + del = *current; *current = (*current)->next; free_term(tb, del); did_erase = 1; @@ -2089,9 +2103,9 @@ static int db_take_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret) *ret = get_term_list(p, tb, key, hval, b, &bend); while (b != bend) { --nitems_diff; - if (nitems_diff == -1 && IS_FIXED(tb)) { + if (nitems_diff == -1 && IS_FIXED(tb) + && add_fixed_deletion(tb, ix, 0)) { /* Pseudo remove (no need to keep several of same key) */ - add_fixed_deletion(tb, ix); bp = &b->next; b->hvalue = INVALID_HASH; b = b->next; @@ -2131,7 +2145,7 @@ int db_mark_all_deleted_hash(DbTable *tbl) for (i = 0; i < NACTIVE(tb); i++) { if ((list = BUCKET(tb,i)) != NULL) { - add_fixed_deletion(tb, i); + add_fixed_deletion(tb, i, 0); do { list->hvalue = INVALID_HASH; list = list->next; @@ -2908,8 +2922,8 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle) ASSERT((&b->dbterm == handle->dbterm) == !(tb->common.compress && handle->flags & DB_MUST_RESIZE)); if (handle->flags & DB_NEW_OBJECT && cret != DB_ERROR_NONE) { - if (IS_FIXED(tb)) { - add_fixed_deletion(tb, hash_to_ix(tb, b->hvalue)); + if (IS_FIXED(tb) && add_fixed_deletion(tb, hash_to_ix(tb, b->hvalue), + 0)) { b->hvalue = INVALID_HASH; } else { *bp = b->next; -- cgit v1.2.3 From c97e4fb7f534d4c206e0446ddeeec0f47c57f81c Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 30 Jun 2015 11:53:41 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 16 ++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 2d96ed6105..64de3aa622 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,22 @@

This document describes the changes made to the ERTS application.

+
Erts 7.0.1 + +
Fixed Bugs and Malfunctions + + +

+ Fix a rare hanging of the VM seen to happen just after + emulator start. Bug exists since R14.

+

+ Own Id: OTP-12859 Aux Id: seq12882

+
+
+
+ +
+
Erts 7.0
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 1012f5eafd..985834a801 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 7.0 +VSN = 7.0.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 3bda47f2f26dd417fbd65d5f46b2ab8411a2a41f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 1 Jul 2015 15:09:58 +0200 Subject: erts: Remove halfword !HEAP_ON_C_STACK --- erts/emulator/beam/beam_emu.c | 9 +----- erts/emulator/beam/erl_arith.c | 11 ++----- erts/emulator/beam/erl_bif_info.c | 9 ------ erts/emulator/beam/erl_debug.c | 25 --------------- erts/emulator/beam/erl_debug.h | 6 ---- erts/emulator/beam/erl_process.c | 3 -- erts/emulator/beam/erl_process.h | 6 ---- erts/emulator/beam/erl_term.h | 1 - erts/emulator/beam/erl_trace.c | 24 -------------- erts/emulator/beam/erl_vm.h | 8 ----- erts/emulator/beam/global.h | 67 ++++----------------------------------- erts/emulator/beam/io.c | 35 ++------------------ 12 files changed, 13 insertions(+), 191 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index f4111c19f1..806b31654c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1165,11 +1165,7 @@ void process_main(void) */ register Eterm tmp_arg1 REG_tmp_arg1 = NIL; register Eterm tmp_arg2 REG_tmp_arg2 = NIL; -#if HEAP_ON_C_STACK - Eterm tmp_big[2]; /* Temporary buffer for small bignums if HEAP_ON_C_STACK. */ -#else - Eterm *tmp_big; /* Temporary buffer for small bignums if !HEAP_ON_C_STACK. */ -#endif + Eterm tmp_big[2]; /* * X registers and floating point registers are located in @@ -1261,9 +1257,6 @@ void process_main(void) reg = ERTS_PROC_GET_SCHDATA(c_p)->x_reg_array; freg = ERTS_PROC_GET_SCHDATA(c_p)->f_reg_array; -#if !HEAP_ON_C_STACK - tmp_big = ERTS_PROC_GET_SCHDATA(c_p)->beam_emu_tmp_heap; -#endif ERL_BITS_RELOAD_STATEP(c_p); { int reds; diff --git a/erts/emulator/beam/erl_arith.c b/erts/emulator/beam/erl_arith.c index b8c5ef9b09..3671025d22 100644 --- a/erts/emulator/beam/erl_arith.c +++ b/erts/emulator/beam/erl_arith.c @@ -42,15 +42,8 @@ # define MAX(x, y) (((x) > (y)) ? (x) : (y)) #endif -#if !HEAP_ON_C_STACK -# define DECLARE_TMP(VariableName,N,P) \ - Eterm *VariableName = ((ERTS_PROC_GET_SCHDATA(P)->erl_arith_tmp_heap) + (2 * N)) -#else -# define DECLARE_TMP(VariableName,N,P) \ - Eterm VariableName[2] -#endif -# define ARG_IS_NOT_TMP(Arg,Tmp) ((Arg) != make_big((Tmp))) - +#define DECLARE_TMP(VariableName,N,P) Eterm VariableName[2] +#define ARG_IS_NOT_TMP(Arg,Tmp) ((Arg) != make_big((Tmp))) static Eterm shift(Process* p, Eterm arg1, Eterm arg2, int right); diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index a3ff537aa1..9a132ee007 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -73,9 +73,6 @@ static char otp_version[] = ERLANG_OTP_VERSION; static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE "%s" " [erts-" ERLANG_VERSION "]" -#if !HEAP_ON_C_STACK - " [no-c-stack-objects]" -#endif #ifndef OTP_RELEASE #ifdef ERLANG_GIT_VERSION " [source-" ERLANG_GIT_VERSION "]" @@ -667,18 +664,12 @@ static Eterm pi_1_keys[] = { #define ERTS_PI_1_NO_OF_KEYS (sizeof(pi_1_keys)/sizeof(Eterm)) static Eterm pi_1_keys_list; -#if HEAP_ON_C_STACK static Eterm pi_1_keys_list_heap[2*ERTS_PI_1_NO_OF_KEYS]; -#endif static void process_info_init(void) { -#if HEAP_ON_C_STACK Eterm *hp = &pi_1_keys_list_heap[0]; -#else - Eterm *hp = erts_alloc(ERTS_ALC_T_LL_TEMP_TERM,sizeof(Eterm)*2*ERTS_PI_1_NO_OF_KEYS); -#endif int i; pi_1_keys_list = NIL; diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index 77a1e3d7cb..434b6c6d7a 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -628,29 +628,4 @@ void print_memory_info(Process *p) } erts_printf("+-----------------%s-%s-%s-%s-+\n",dashes,dashes,dashes,dashes); } -#if !HEAP_ON_C_STACK && defined(DEBUG) -Eterm *erts_debug_allocate_tmp_heap(int size, Process *p) -{ - ErtsSchedulerData *sd = ((p == NULL) ? erts_get_scheduler_data() : ERTS_PROC_GET_SCHDATA(p)); - int offset = sd->num_tmp_heap_used; - - ASSERT(offset+size <= TMP_HEAP_SIZE); - return (sd->tmp_heap)+offset; -} -void erts_debug_use_tmp_heap(int size, Process *p) -{ - ErtsSchedulerData *sd = ((p == NULL) ? erts_get_scheduler_data() : ERTS_PROC_GET_SCHDATA(p)); - - sd->num_tmp_heap_used += size; - ASSERT(sd->num_tmp_heap_used <= TMP_HEAP_SIZE); -} -void erts_debug_unuse_tmp_heap(int size, Process *p) -{ - ErtsSchedulerData *sd = ((p == NULL) ? erts_get_scheduler_data() : ERTS_PROC_GET_SCHDATA(p)); - - sd->num_tmp_heap_used -= size; - ASSERT(sd->num_tmp_heap_used >= 0); -} #endif -#endif - diff --git a/erts/emulator/beam/erl_debug.h b/erts/emulator/beam/erl_debug.h index 4905e64f07..f4259e7dae 100644 --- a/erts/emulator/beam/erl_debug.h +++ b/erts/emulator/beam/erl_debug.h @@ -92,10 +92,4 @@ extern void print_tagged_memory(Eterm *start, Eterm *end); extern void print_untagged_memory(Eterm *start, Eterm *end); extern void print_memory(Process *p); extern void print_memory_info(Process *p); -#if defined(DEBUG) && !HEAP_ON_C_STACK -extern Eterm *erts_debug_allocate_tmp_heap(int, Process *); -extern void erts_debug_use_tmp_heap(int, Process *); -extern void erts_debug_unuse_tmp_heap(int, Process *); -#endif - #endif /* _ERL_DEBUG_H_ */ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 135f09c04d..338be6d8e1 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -5495,9 +5495,6 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num, esdp->f_reg_array = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_BEAM_REGISTER, MAX_REG * sizeof(FloatDef)); -#if !HEAP_ON_C_STACK - esdp->num_tmp_heap_used = 0; -#endif #ifdef ERTS_DIRTY_SCHEDULERS if (ERTS_RUNQ_IX_IS_DIRTY(runq->ix)) { esdp->no = 0; diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 20ffe7ea7c..ffbf3e2bff 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -631,12 +631,6 @@ struct ErtsSchedulerData_ { void *match_pseudo_process; /* erl_db_util.c:db_prog_match() */ Process *free_process; ErtsThrPrgrData thr_progress_data; -#endif -#if !HEAP_ON_C_STACK - Eterm tmp_heap[TMP_HEAP_SIZE]; - int num_tmp_heap_used; - Eterm beam_emu_tmp_heap[BEAM_EMU_TMP_HEAP_SIZE]; - Eterm erl_arith_tmp_heap[ERL_ARITH_TMP_HEAP_SIZE]; #endif ErtsSchedulerSleepInfo *ssi; Process *current_process; diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 0d8e981da4..b928e4ca1e 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -23,7 +23,6 @@ typedef UWord Wterm; /* Full word terms */ -#define HEAP_ON_C_STACK 1 struct erl_node_; /* Declared in erl_node_tables.h */ /* diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index e1b03a057f..7baa3b1a54 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1677,12 +1677,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, args = transformed_args; if (is_internal_port(*tracer_pid)) { -#if HEAP_ON_C_STACK Eterm local_heap[64+MAX_ARG]; -#else - Eterm *local_heap = erts_alloc(ERTS_ALC_T_TEMP_TERM, - sizeof(Eterm)*(64+MAX_ARG)); -#endif hp = local_heap; if (!erts_is_valid_tracer_port(*tracer_pid)) { @@ -1696,9 +1691,6 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, #ifdef ERTS_SMP if (is_not_nil(tracee)) erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR); -#endif -#if !HEAP_ON_C_STACK - erts_free(ERTS_ALC_T_TEMP_TERM,local_heap); #endif UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); return 0; @@ -1727,9 +1719,6 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, ERTS_PAM_TMP_RESULT, &return_flags); if (is_non_value(pam_result)) { erts_match_set_release_result(p); -#if !HEAP_ON_C_STACK - erts_free(ERTS_ALC_T_TEMP_TERM,local_heap); -#endif UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); return 0; } @@ -1738,9 +1727,6 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, /* Meta trace */ if (pam_result == am_false) { erts_match_set_release_result(p); -#if !HEAP_ON_C_STACK - erts_free(ERTS_ALC_T_TEMP_TERM,local_heap); -#endif UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); return return_flags; } @@ -1748,17 +1734,11 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, /* Non-meta trace */ if (*tracee_flags & F_TRACE_SILENT) { erts_match_set_release_result(p); -#if !HEAP_ON_C_STACK - erts_free(ERTS_ALC_T_TEMP_TERM,local_heap); -#endif UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); return 0; } if (pam_result == am_false) { erts_match_set_release_result(p); -#if !HEAP_ON_C_STACK - erts_free(ERTS_ALC_T_TEMP_TERM,local_heap); -#endif UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); return return_flags; } @@ -1802,9 +1782,6 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, send_to_port(p, mess, tracer_pid, tracee_flags); erts_smp_mtx_unlock(&smq_mtx); erts_match_set_release_result(p); -#if !HEAP_ON_C_STACK - erts_free(ERTS_ALC_T_TEMP_TERM,local_heap); -#endif UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); return *tracer_pid == NIL ? 0 : return_flags; @@ -1823,7 +1800,6 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, #ifdef DEBUG Eterm* limit; #endif - ASSERT(is_internal_pid(*tracer_pid)); tracer = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 2d4141ac8d..cfc978b5a5 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -40,14 +40,6 @@ #define MAX_ARG 255 /* Max number of arguments allowed */ #define MAX_REG 1024 /* Max number of x(N) registers used */ -/* Scheduler stores data for temporary heaps if - !HEAP_ON_C_STACK. Macros (*TmpHeap*) in global.h selects if we put temporary - heap data on the C stack or if we use the buffers in the scheduler data. */ -#define TMP_HEAP_SIZE 128 /* Number of Eterm in the schedulers - small heap for transient heap data */ -#define ERL_ARITH_TMP_HEAP_SIZE 4 /* as does erl_arith... */ -#define BEAM_EMU_TMP_HEAP_SIZE 2 /* and beam_emu... */ - /* * The new arithmetic operations need some extra X registers in the register array. * so does the gc_bif's (i_gc_bif3 need 3 extra). diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 870afb414f..b4d02dd1dd 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1444,69 +1444,16 @@ erts_alloc_message_heap(Uint size, #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ -#if !HEAP_ON_C_STACK -# if defined(DEBUG) -# define DeclareTmpHeap(VariableName,Size,Process) \ - Eterm *VariableName = erts_debug_allocate_tmp_heap(Size,Process) -# define DeclareTypedTmpHeap(Type,VariableName,Process) \ - Type *VariableName = (Type *) erts_debug_allocate_tmp_heap(sizeof(Type)/sizeof(Eterm),Process) -# define DeclareTmpHeapNoproc(VariableName,Size) \ - Eterm *VariableName = erts_debug_allocate_tmp_heap(Size,NULL) -# define UseTmpHeap(Size,Proc) \ - do { \ - erts_debug_use_tmp_heap((Size),(Proc)); \ - } while (0) -# define UnUseTmpHeap(Size,Proc) \ - do { \ - erts_debug_unuse_tmp_heap((Size),(Proc)); \ - } while (0) -# define UseTmpHeapNoproc(Size) \ - do { \ - erts_debug_use_tmp_heap(Size,NULL); \ - } while (0) -# define UnUseTmpHeapNoproc(Size) \ - do { \ - erts_debug_unuse_tmp_heap(Size,NULL); \ - } while (0) -# else -# define DeclareTmpHeap(VariableName,Size,Process) \ - Eterm *VariableName = (ERTS_PROC_GET_SCHDATA(Process)->tmp_heap)+(ERTS_PROC_GET_SCHDATA(Process)->num_tmp_heap_used) -# define DeclareTypedTmpHeap(Type,VariableName,Process) \ - Type *VariableName = (Type *) (ERTS_PROC_GET_SCHDATA(Process)->tmp_heap)+(ERTS_PROC_GET_SCHDATA(Process)->num_tmp_heap_used) -# define DeclareTmpHeapNoproc(VariableName,Size) \ - Eterm *VariableName = (erts_get_scheduler_data()->tmp_heap)+(erts_get_scheduler_data()->num_tmp_heap_used) -# define UseTmpHeap(Size,Proc) \ - do { \ - ERTS_PROC_GET_SCHDATA(Proc)->num_tmp_heap_used += (Size); \ - } while (0) -# define UnUseTmpHeap(Size,Proc) \ - do { \ - ERTS_PROC_GET_SCHDATA(Proc)->num_tmp_heap_used -= (Size); \ - } while (0) -# define UseTmpHeapNoproc(Size) \ - do { \ - erts_get_scheduler_data()->num_tmp_heap_used += (Size); \ - } while (0) -# define UnUseTmpHeapNoproc(Size) \ - do { \ - erts_get_scheduler_data()->num_tmp_heap_used -= (Size); \ - } while (0) - - -# endif - -#else -# define DeclareTmpHeap(VariableName,Size,Process) \ +#define DeclareTmpHeap(VariableName,Size,Process) \ Eterm VariableName[Size] -# define DeclareTypedTmpHeap(Type,VariableName,Process) \ +#define DeclareTypedTmpHeap(Type,VariableName,Process) \ Type VariableName[1] -# define DeclareTmpHeapNoproc(VariableName,Size) \ +#define DeclareTmpHeapNoproc(VariableName,Size) \ Eterm VariableName[Size] -# define UseTmpHeap(Size,Proc) /* Nothing */ -# define UnUseTmpHeap(Size,Proc) /* Nothing */ -# define UseTmpHeapNoproc(Size) /* Nothing */ -# define UnUseTmpHeapNoproc(Size) /* Nothing */ -#endif /* HEAP_ON_C_STACK */ +#define UseTmpHeap(Size,Proc) /* Nothing */ +#define UnUseTmpHeap(Size,Proc) /* Nothing */ +#define UseTmpHeapNoproc(Size) /* Nothing */ +#define UnUseTmpHeapNoproc(Size) /* Nothing */ ERTS_GLB_INLINE void dtrace_pid_str(Eterm pid, char *process_buf); ERTS_GLB_INLINE void dtrace_proc_str(Process *process, char *process_buf); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 6b4b90cb06..915d0f4328 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -6791,7 +6791,7 @@ int driver_monitor_process(ErlDrvPort drvport, { Port *prt; int ret; -#if !HEAP_ON_C_STACK || (defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)) +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) ErtsSchedulerData *sched = erts_get_scheduler_data(); #endif @@ -6802,16 +6802,6 @@ int driver_monitor_process(ErlDrvPort drvport, /* Now (in SMP) we should have either the port lock (if we have a scheduler) or the port data lock (if we're a driver thread) */ ERTS_SMP_LC_ASSERT((sched != NULL || prt->port_data_lock)); - -#if !HEAP_ON_C_STACK - if (!sched) { - /* Need a separate allocation for the ref :( */ - Eterm *buf = erts_alloc(ERTS_ALC_T_TEMP_TERM, - sizeof(Eterm)*REF_THING_SIZE); - ret = do_driver_monitor_process(prt,buf,process,monitor); - erts_free(ERTS_ALC_T_TEMP_TERM,buf); - } else -#endif { DeclareTmpHeapNoproc(buf,REF_THING_SIZE); UseTmpHeapNoproc(REF_THING_SIZE); @@ -6864,7 +6854,7 @@ int driver_demonitor_process(ErlDrvPort drvport, { Port *prt; int ret; -#if !HEAP_ON_C_STACK || (defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)) +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) ErtsSchedulerData *sched = erts_get_scheduler_data(); #endif @@ -6875,15 +6865,6 @@ int driver_demonitor_process(ErlDrvPort drvport, /* Now we should have either the port lock (if we have a scheduler) or the port data lock (if we're a driver thread) */ ERTS_SMP_LC_ASSERT((sched != NULL || prt->port_data_lock)); -#if !HEAP_ON_C_STACK - if (!sched) { - /* Need a separate allocation for the ref :( */ - Eterm *buf = erts_alloc(ERTS_ALC_T_TEMP_TERM, - sizeof(Eterm)*REF_THING_SIZE); - ret = do_driver_demonitor_process(prt,buf,monitor); - erts_free(ERTS_ALC_T_TEMP_TERM,buf); - } else -#endif { DeclareTmpHeapNoproc(buf,REF_THING_SIZE); UseTmpHeapNoproc(REF_THING_SIZE); @@ -6919,7 +6900,7 @@ ErlDrvTermData driver_get_monitored_process(ErlDrvPort drvport, { Port *prt; ErlDrvTermData ret; -#if !HEAP_ON_C_STACK || (defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)) +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) ErtsSchedulerData *sched = erts_get_scheduler_data(); #endif @@ -6930,16 +6911,6 @@ ErlDrvTermData driver_get_monitored_process(ErlDrvPort drvport, /* Now we should have either the port lock (if we have a scheduler) or the port data lock (if we're a driver thread) */ ERTS_SMP_LC_ASSERT((sched != NULL || prt->port_data_lock)); - -#if !HEAP_ON_C_STACK - if (!sched) { - /* Need a separate allocation for the ref :( */ - Eterm *buf = erts_alloc(ERTS_ALC_T_TEMP_TERM, - sizeof(Eterm)*REF_THING_SIZE); - ret = do_driver_get_monitored_process(prt,buf,monitor); - erts_free(ERTS_ALC_T_TEMP_TERM,buf); - } else -#endif { DeclareTmpHeapNoproc(buf,REF_THING_SIZE); UseTmpHeapNoproc(REF_THING_SIZE); -- cgit v1.2.3 From 0d69dcf52f190c29ba1778bc61f030e6379f4379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 2 Jul 2015 10:10:20 +0200 Subject: Correct disassembly of the i_get_map_elements instruction The emulator would crash. --- erts/emulator/beam/beam_debug.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index c756de8c8e..8a35ad17c6 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -669,7 +669,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) case op_new_map_dII: case op_update_map_assoc_jsdII: case op_update_map_exact_jsdII: - case op_i_get_map_elements_fsI: { int n = unpacked[-1]; @@ -693,6 +692,32 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) } } break; + case op_i_get_map_elements_fsI: + { + int n = unpacked[-1]; + + while (n > 0) { + if (n % 3 == 1) { + erts_print(to, to_arg, " %X", ap[0]); + } else if (!is_header(ap[0])) { + erts_print(to, to_arg, " %T", (Eterm) ap[0]); + } else { + switch ((ap[0] >> 2) & 0x03) { + case R_REG_DEF: + erts_print(to, to_arg, " x(0)"); + break; + case X_REG_DEF: + erts_print(to, to_arg, " x(%d)", ap[0] >> 4); + break; + case Y_REG_DEF: + erts_print(to, to_arg, " y(%d)", ap[0] >> 4); + break; + } + } + ap++, size++, n--; + } + } + break; } erts_print(to, to_arg, "\n"); -- cgit v1.2.3 From 16317f73f79265eba8e0cef4adaea6f6858d389b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 2 Jul 2015 10:19:56 +0200 Subject: Add a smoke test of erts_debug:df/1 Run erts_debug:df/1 for all loaded modules. On my reasonably fast, modern computer this test case runs in approximately 10 seconds. To avoid spending many minutes running this test case on older computers, limit the running time to 20 seconds. While we are at it, remove all ?line macros. --- erts/emulator/test/erts_debug_SUITE.erl | 47 +++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl index 3dd77eb920..35677f9953 100644 --- a/erts/emulator/test/erts_debug_SUITE.erl +++ b/erts/emulator/test/erts_debug_SUITE.erl @@ -139,23 +139,48 @@ flat_size_big_1(Term, Size0, Limit) when Size0 < Limit -> flat_size_big_1(_, _, _) -> ok. df(Config) when is_list(Config) -> - ?line P0 = pps(), - ?line PrivDir = ?config(priv_dir, Config), - ?line ok = file:set_cwd(PrivDir), - ?line erts_debug:df(?MODULE), - ?line Beam = filename:join(PrivDir, ?MODULE_STRING++".dis"), - ?line {ok,Bin} = file:read_file(Beam), - ?line ok = io:put_chars(binary_to_list(Bin)), - ?line ok = file:delete(Beam), - ?line true = (P0 == pps()), + P0 = pps(), + PrivDir = ?config(priv_dir, Config), + ok = file:set_cwd(PrivDir), + + AllLoaded = [M || {M,_} <- code:all_loaded()], + {Pid,Ref} = spawn_monitor(fun() -> df_smoke(AllLoaded) end), + receive + {'DOWN',Ref,process,Pid,Status} -> + normal = Status + after 20*1000 -> + %% Not finished (i.e. a slow computer). Stop now. + Pid ! stop, + receive + {'DOWN',Ref,process,Pid,Status} -> + normal = Status, + io:format("...") + end + end, + io:nl(), + _ = [_ = file:delete(atom_to_list(M) ++ ".dis") || + M <- AllLoaded], + + true = (P0 == pps()), ok. +df_smoke([M|Ms]) -> + io:format("~p", [M]), + erts_debug:df(M), + receive + stop -> + ok + after 0 -> + df_smoke(Ms) + end; +df_smoke([]) -> ok. + pps() -> {erlang:ports()}. instructions(Config) when is_list(Config) -> - ?line Is = erts_debug:instructions(), - ?line _ = [list_to_atom(I) || I <- Is], + Is = erts_debug:instructions(), + _ = [list_to_atom(I) || I <- Is], ok. id(I) -> -- cgit v1.2.3 From 0258bf34f58b4a2357150b61033f214e8c642315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 2 Jul 2015 15:09:41 +0200 Subject: erts: Fix configure pthread_getname --- erts/aclocal.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 01541aff72..bf48c832b3 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1900,7 +1900,7 @@ case "$THR_LIB_NAME" in #define _DARWIN_C_SOURCE #include ], [char buff[256]; pthread_getname_np(pthread_self(), buff, 256);], - pthread_getname=normal) + pthread_getname=linux) AC_TRY_LINK([#define __USE_GNU #define _DARWIN_C_SOURCE #include ], -- cgit v1.2.3 From dfe1c58c504531361fa3f8ed874238a9ff552640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 29 Jun 2015 13:24:31 +0200 Subject: beam_makeops: Eliminate crash because of unsafe packing Consider an hypothetical instruction: do_something x x c The loader would crash if we tried to load an instance of the instruction with the last operand referencing a literal: {do_something,{x,0},{x,1},{literal,{a,b,c}}} Teach beam_makeops to turn off packing for such unsafe instructions. --- erts/emulator/utils/beam_makeops | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'erts') diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index e90ed94187..7c94546ea4 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -1057,6 +1057,7 @@ sub do_pack { my($packable_args) = 0; my @is_packable; # Packability (boolean) for each argument. my $wide_packing = 0; + my(@orig_args) = @args; # # Count the number of packable arguments. If we encounter any 's' or 'd' @@ -1077,6 +1078,18 @@ sub do_pack { } } elsif ($arg =~ /^[sd]/) { return ('', '', @args); + } elsif ($arg =~ /^[scq]/ and $packable_args > 0) { + # When packing, this operand will be picked up from the + # code array, put onto the packing stack, and later put + # back into a different location in the code. The problem + # is that if this operand is a literal, the original + # location in the code would have been remembered in a + # literal patch. For packing to work, we would have to + # adjust the position in the literal patch. For the + # moment, adding additional instructions to the packing + # engine to handle this does not seem worth it, so we will + # just turn off packing. + return ('', '', @args); } else { push @is_packable, 0; } -- cgit v1.2.3 From 54189af4a519b72883dde3263135baf6aba0f81f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 1 Jul 2015 12:18:54 +0200 Subject: beam_emu.c: Remove unused MoveGenDest macro --- erts/emulator/beam/beam_emu.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 806b31654c..0f9bfa1871 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -566,9 +566,6 @@ void** beam_ops; #define Move2(S1, D1, S2, D2) D1 = (S1); D2 = (S2) #define Move3(S1, D1, S2, D2, S3, D3) D1 = (S1); D2 = (S2); D3 = (S3) -#define MoveGenDest(src, dstp) \ - if ((dstp) == NULL) { r(0) = (src); } else { *(dstp) = src; } - #define MoveReturn(Src, Dest) \ (Dest) = (Src); \ I = c_p->cp; \ -- cgit v1.2.3 From 45f469ca089096c8a59ca53d948d05d1a998386e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 30 Mar 2012 11:05:16 +0200 Subject: Change the meaning of 'x' in a transformation The purpose of this series of commits is to improve code generation for the Clang compiler. As a first step we want to change the meaning of 'x' in a transformation such as: operation Literal=q => move Literal x | operation x Currently, a plain 'x' means reg[0] or x(0), which is the first element in the X register array. That element is distinct from r(0) which is a variable in process_main(). Therefore, since r(0) and x(0) are currently distinct it is fine to use x(0) as a scratch register. However, in the next commit we will eliminate the separate variable for storing the contents of X register zero (thus, x(0) and r(0) will point to the same location in the X register array). Therefore, we must use another scratch register in transformation. Redefine a plain 'x' in a transformation to mean x(1023). Also define SCRATCH_X_REG so that we can refer to the register by name from C code. --- erts/emulator/beam/beam_load.c | 2 +- erts/emulator/utils/beam_makeops | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 10baab506a..f5f1147261 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4322,7 +4322,7 @@ gen_has_map_fields(LoaderState* stp, GenOpArg Fail, GenOpArg Src, for (i = 0; i < n; i++) { op->a[3+2*i] = Rest[i]; op->a[3+2*i+1].type = TAG_x; - op->a[3+2*i+1].val = 0; /* x(0); normally not used */ + op->a[3+2*i+1].val = SCRATCH_X_REG; /* Ignore result */ } return op; } diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 7c94546ea4..6e1741af85 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -601,6 +601,7 @@ sub emulator_output { print "#define MAX_GENERIC_OPCODE ", $num_file_opcodes-1, "\n"; print "#define NUM_GENERIC_OPS ", scalar(@gen_opname), "\n"; print "#define NUM_SPECIFIC_OPS ", scalar(@op_to_name), "\n"; + print "#define SCRATCH_X_REG 1023\n"; print "\n"; print "#ifdef ARCH_64\n"; print "# define BEAM_WIDE_MASK 0xFFFFUL\n"; @@ -1341,7 +1342,10 @@ sub tr_parse_op { } # Get an optional value. (In destination.) + $type_val = $type eq 'x' ? 1023 : 0; if (/^=(.*)/) { + error("value not allowed in source: $op") + if $src; $type_val = $1; $_ = ''; } @@ -1360,11 +1364,6 @@ sub tr_parse_op { if $var && $type; } - # Test that source has no values. - if ($src) { - error("value not allowed in source: $op") - if $type_val; - } ($var,$type,$type_val,$cond,$cond_val); } -- cgit v1.2.3 From 1f996cc46a8c935b8f4e33a4a96804f9fb852da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 30 Mar 2012 11:38:37 +0200 Subject: Store r(0) and x(0) in the same location As part of improving code generation for clang, we want to eliminate the special variable that stores the content of X register zero most of the time. In a future, that will allow us to eliminate the special case of handling r(0) for most instructions, thus reducing the code size and allow other simplifcations. Therefore, in this commit, eliminate the variable that is used to store r(0) and make r(0) as synonym for x(0). I have chosen to keep the r(0) define to keep the size of the diff managable. --- erts/emulator/beam/beam_emu.c | 100 +++--------------------------------------- 1 file changed, 7 insertions(+), 93 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 0f9bfa1871..9a34d5f11a 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -99,10 +99,7 @@ do { \ do { \ int i_; \ int Arity_ = PC[-1]; \ - if (Arity_ > 0) { \ - CHECK_TERM(r(0)); \ - } \ - for (i_ = 1; i_ < Arity_; i_++) { \ + for (i_ = 0; i_ < Arity_; i_++) { \ CHECK_TERM(x(i_)); \ } \ } while (0) @@ -293,7 +290,7 @@ void** beam_ops; #define Ib(N) (N) #define x(N) reg[N] #define y(N) E[N] -#define r(N) x##N +#define r(N) x(N) /* * Makes sure that there are StackNeed + HeapNeed + 1 words available @@ -309,12 +306,10 @@ void** beam_ops; needed = (StackNeed) + 1; \ if (E - HTOP < (needed + (HeapNeed))) { \ SWAPOUT; \ - reg[0] = r(0); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ FCALLS -= erts_garbage_collect(c_p, needed + (HeapNeed), reg, (M)); \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - r(0) = reg[0]; \ SWAPIN; \ } \ E -= needed; \ @@ -363,12 +358,10 @@ void** beam_ops; unsigned need = (Nh); \ if ((E - HTOP < need) || (MSO(c_p).overhead + (VNh) >= BIN_VHEAP_SZ(c_p))) {\ SWAPOUT; \ - reg[0] = r(0); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)); \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - r(0) = reg[0]; \ SWAPIN; \ } \ HEAP_SPACE_VERIFIED(need); \ @@ -386,12 +379,10 @@ void** beam_ops; unsigned need = (Nh); \ if (E - HTOP < need) { \ SWAPOUT; \ - reg[0] = r(0); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)); \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - r(0) = reg[0]; \ SWAPIN; \ } \ HEAP_SPACE_VERIFIED(need); \ @@ -408,15 +399,11 @@ void** beam_ops; unsigned need = (Nh); \ if (E - HTOP < need) { \ SWAPOUT; \ - reg[0] = r(0); \ reg[Live] = Extra; \ PROCESS_MAIN_CHK_LOCKS(c_p); \ FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)+1); \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - if (Live > 0) { \ - r(0) = reg[0]; \ - } \ Extra = reg[Live]; \ SWAPIN; \ } \ @@ -439,7 +426,6 @@ void** beam_ops; #define MakeFun(FunP, NumFree) \ do { \ SWAPOUT; \ - reg[0] = r(0); \ r(0) = new_fun(c_p, reg, (ErlFunEntry *) FunP, NumFree); \ SWAPIN; \ } while (0) @@ -992,7 +978,6 @@ init_emulator(void) */ #if defined(__GNUC__) && defined(sparc) && !defined(DEBUG) -# define REG_x0 asm("%l0") # define REG_xregs asm("%l1") # define REG_htop asm("%l2") # define REG_stop asm("%l3") @@ -1001,7 +986,6 @@ init_emulator(void) # define REG_tmp_arg1 asm("%l6") # define REG_tmp_arg2 asm("%l7") #else -# define REG_x0 # define REG_xregs # define REG_htop # define REG_stop @@ -1126,11 +1110,6 @@ void process_main(void) ERTS_DECLARE_DUMMY(Eterm pid); #endif - /* - * X register zero; also called r(0) - */ - register Eterm x0 REG_x0 = NIL; - /* Pointer to X registers: x(1)..x(N); reg[0] is used when doing GC, * in all other cases x0 is used. */ @@ -1262,7 +1241,7 @@ void process_main(void) int i; argp = c_p->arg_reg; - for (i = c_p->arity - 1; i > 0; i--) { + for (i = c_p->arity - 1; i >= 0; i--) { reg[i] = argp[i]; CHECK_TERM(reg[i]); } @@ -1286,12 +1265,6 @@ void process_main(void) } next = (BeamInstr *) *I; - r(0) = c_p->arg_reg[0]; -#ifdef HARDDEBUG - if (c_p->arity > 0) { - CHECK_TERM(r(0)); - } -#endif SWAPIN; ASSERT(VALID_INSTR(next)); @@ -1365,11 +1338,9 @@ void process_main(void) live = Arg(2); SWAPOUT; - reg[0] = r(0); reg[live] = increment_reg_val; reg[live+1] = make_small(increment_val); result = erts_gc_mixed_plus(c_p, reg, live); - r(0) = reg[0]; SWAPIN; ERTS_HOLE_CHECK(c_p); if (is_value(result)) { @@ -1383,11 +1354,9 @@ void process_main(void) do { \ Uint live = Arg(1); \ SWAPOUT; \ - reg[0] = r(0); \ reg[live] = (Arg1); \ reg[live+1] = (Arg2); \ result = (Func)(c_p, reg, live); \ - r(0) = reg[0]; \ SWAPIN; \ ERTS_HOLE_CHECK(c_p); \ if (is_value(result)) { \ @@ -1706,7 +1675,6 @@ void process_main(void) PRE_BIF_SWAPOUT(c_p); c_p->fcalls = FCALLS - 1; - reg[0] = r(0); result = erl_send(c_p, r(0), x(1)); PreFetch(0, next); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); @@ -1714,7 +1682,6 @@ void process_main(void) PROCESS_MAIN_CHK_LOCKS(c_p); if (c_p->mbuf || MSO(c_p).overhead >= BIN_VHEAP_SZ(c_p)) { result = erts_gc_after_bif_call(c_p, result, reg, 2); - r(0) = reg[0]; E = c_p->stop; } HTOP = HEAP_TOP(c_p); @@ -1727,7 +1694,6 @@ void process_main(void) SET_CP(c_p, I+1); SET_I(c_p->i); SWAPIN; - r(0) = reg[0]; Dispatch(); } goto find_func_info; @@ -1935,13 +1901,11 @@ void process_main(void) ErtsMoveMsgAttachmentIntoProc(msgp, c_p, E, HTOP, FCALLS, { SWAPOUT; - reg[0] = r(0); PROCESS_MAIN_CHK_LOCKS(c_p); }, { ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); - r(0) = reg[0]; SWAPIN; }); if (is_non_value(ERL_MESSAGE_TERM(msgp))) { @@ -2448,11 +2412,9 @@ void process_main(void) OpCase(new_map_dII): { Eterm res; - x(0) = r(0); SWAPOUT; res = new_map(c_p, reg, I-1); SWAPIN; - r(0) = x(0); StoreResult(res, Arg(0)); Next(3+Arg(2)); } @@ -2543,12 +2505,10 @@ do { \ Eterm map; GetArg1(1, map); - x(0) = r(0); SWAPOUT; res = update_map_assoc(c_p, reg, map, I); SWAPIN; if (is_value(res)) { - r(0) = x(0); StoreResult(res, Arg(2)); Next(5+Arg(4)); } else { @@ -2567,12 +2527,10 @@ do { \ Eterm map; GetArg1(1, map); - x(0) = r(0); SWAPOUT; res = update_map_exact(c_p, reg, map, I); SWAPIN; if (is_value(res)) { - r(0) = x(0); StoreResult(res, Arg(2)); Next(5+Arg(4)); } else { @@ -2659,7 +2617,6 @@ do { \ Uint live = (Uint) Arg(3); GetArg1(2, arg); - reg[0] = r(0); reg[live] = arg; bf = (GcBifFunction) Arg(1); c_p->fcalls = FCALLS; @@ -2671,7 +2628,6 @@ do { \ ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); SWAPIN; - r(0) = reg[0]; ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; if (is_value(result)) { @@ -2694,7 +2650,6 @@ do { \ Eterm result; Uint live = (Uint) Arg(2); - reg[0] = r(0); reg[live++] = tmp_arg1; reg[live] = tmp_arg2; bf = (GcBifFunction) Arg(1); @@ -2707,7 +2662,6 @@ do { \ ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); SWAPIN; - r(0) = reg[0]; ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; if (is_value(result)) { @@ -2732,7 +2686,6 @@ do { \ Uint live = (Uint) Arg(3); GetArg1(2, arg); - reg[0] = r(0); reg[live++] = arg; reg[live++] = tmp_arg1; reg[live] = tmp_arg2; @@ -2746,7 +2699,6 @@ do { \ ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); SWAPIN; - r(0) = reg[0]; ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; if (is_value(result)) { @@ -2835,7 +2787,6 @@ do { \ PreFetch(1, next); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - reg[0] = r(0); result = (*bf)(c_p, reg, I); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); @@ -2857,7 +2808,6 @@ do { \ SET_CP(c_p, I+2); SET_I(c_p->i); SWAPIN; - r(0) = reg[0]; Dispatch(); } @@ -2967,11 +2917,9 @@ do { \ Uint live = Arg(1); SWAPOUT; - reg[0] = r(0); reg[live] = tmp_arg1; reg[live+1] = tmp_arg2; result = arith_func(c_p, reg, live); - r(0) = reg[0]; SWAPIN; ERTS_HOLE_CHECK(c_p); if (is_value(result)) { @@ -3166,10 +3114,8 @@ do { \ } else { Uint live = Arg(2); SWAPOUT; - reg[0] = r(0); reg[live] = bnot_val; bnot_val = erts_gc_bnot(c_p, reg, live); - r(0) = reg[0]; SWAPIN; ERTS_HOLE_CHECK(c_p); if (is_nil(bnot_val)) { @@ -3189,7 +3135,6 @@ do { \ next = apply(c_p, r(0), x(1), x(2), reg); SWAPIN; if (next != NULL) { - r(0) = reg[0]; SET_CP(c_p, I+1); SET_I(next); Dispatch(); @@ -3204,7 +3149,6 @@ do { \ next = apply(c_p, r(0), x(1), x(2), reg); SWAPIN; if (next != NULL) { - r(0) = reg[0]; SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(0)); SET_I(next); @@ -3220,7 +3164,6 @@ do { \ next = apply(c_p, r(0), x(1), x(2), reg); SWAPIN; if (next != NULL) { - r(0) = reg[0]; SET_I(next); Dispatch(); } @@ -3231,12 +3174,10 @@ do { \ OpCase(apply_I): { BeamInstr *next; - reg[0] = r(0); SWAPOUT; next = fixed_apply(c_p, reg, Arg(0)); SWAPIN; if (next != NULL) { - r(0) = reg[0]; SET_CP(c_p, I+2); SET_I(next); Dispatch(); @@ -3248,12 +3189,10 @@ do { \ OpCase(apply_last_IP): { BeamInstr *next; - reg[0] = r(0); SWAPOUT; next = fixed_apply(c_p, reg, Arg(0)); SWAPIN; if (next != NULL) { - r(0) = reg[0]; SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(1)); SET_I(next); @@ -3270,7 +3209,6 @@ do { \ next = apply_fun(c_p, r(0), x(1), reg); SWAPIN; if (next != NULL) { - r(0) = reg[0]; SET_CP(c_p, I+1); SET_I(next); Dispatchfun(); @@ -3285,7 +3223,6 @@ do { \ next = apply_fun(c_p, r(0), x(1), reg); SWAPIN; if (next != NULL) { - r(0) = reg[0]; SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(0)); SET_I(next); @@ -3301,7 +3238,6 @@ do { \ next = apply_fun(c_p, r(0), x(1), reg); SWAPIN; if (next != NULL) { - r(0) = reg[0]; SET_I(next); Dispatchfun(); } @@ -3312,12 +3248,9 @@ do { \ BeamInstr *next; SWAPOUT; - reg[0] = r(0); - next = call_fun(c_p, Arg(0), reg, THE_NON_VALUE); SWAPIN; if (next != NULL) { - r(0) = reg[0]; SET_CP(c_p, I+2); SET_I(next); Dispatchfun(); @@ -3329,11 +3262,9 @@ do { \ BeamInstr *next; SWAPOUT; - reg[0] = r(0); next = call_fun(c_p, Arg(0), reg, THE_NON_VALUE); SWAPIN; if (next != NULL) { - r(0) = reg[0]; SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(1)); SET_I(next); @@ -3418,10 +3349,9 @@ do { \ */ argp = c_p->arg_reg; - for (i = c_p->arity - 1; i > 0; i--) { + for (i = c_p->arity - 1; i >= 0; i--) { argp[i] = reg[i]; } - c_p->arg_reg[0] = r(0); SWAPOUT; c_p->i = I; goto do_schedule1; @@ -3531,7 +3461,6 @@ do { \ /* Fall through here */ find_func_info: { - reg[0] = r(0); SWAPOUT; I = handle_error(c_p, I, reg, NULL); goto post_error_handling; @@ -3549,9 +3478,7 @@ do { \ * code[4]: Not used */ SWAPOUT; - reg[0] = r(0); I = call_error_handler(c_p, I-3, reg, am_undefined_function); - r(0) = reg[0]; SWAPIN; if (I) { Goto(*I); @@ -3560,14 +3487,12 @@ do { \ /* Fall through */ OpCase(error_action_code): { handle_error: - reg[0] = r(0); SWAPOUT; I = handle_error(c_p, NULL, reg, NULL); post_error_handling: if (I == 0) { goto do_schedule; } else { - r(0) = reg[0]; ASSERT(!is_value(r(0))); if (c_p->mbuf) { erts_garbage_collect(c_p, 0, reg+1, 3); @@ -3610,7 +3535,6 @@ do { \ NifF* fp = vbf = (NifF*) I[1]; struct enif_environment_t env; erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2]); - reg[0] = r(0); nif_bif_result = (*fp)(&env, bif_nif_arity, reg); if (env.exception_thrown) nif_bif_result = THE_NON_VALUE; @@ -3649,7 +3573,7 @@ do { \ bif_nif_arity = I[-1]; ASSERT(bif_nif_arity <= 4); ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); - reg[0] = r(0); + ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); { Eterm (*bf)(Process*, Eterm*, BeamInstr*) = vbf; ASSERT(!ERTS_PROC_IS_EXITING(c_p)); @@ -3679,7 +3603,6 @@ do { \ Goto(*I); } else if (c_p->freason == TRAP) { SET_I(c_p->i); - r(0) = reg[0]; if (c_p->flags & F_HIBERNATE_SCHED) { c_p->flags &= ~F_HIBERNATE_SCHED; goto do_schedule; @@ -4126,10 +4049,8 @@ do { \ Uint res; SWAPOUT; - reg[0] = r(0); reg[live] = tmp_arg2; res = erts_bs_append(c_p, reg, live, tmp_arg1, Arg(1), Arg(3)); - r(0) = reg[0]; SWAPIN; if (is_non_value(res)) { /* c_p->freason is already set (may be either BADARG or SYSTEM_LIMIT). */ @@ -4706,7 +4627,7 @@ do { \ Uint hole_size; OpCase(bs_context_to_binary_r): { - context_to_binary_context = x0; + context_to_binary_context = r(0); I -= 2; goto do_context_to_binary; } @@ -4737,7 +4658,7 @@ do { \ Next(2); OpCase(i_bs_get_binary_all_reuse_rfI): { - context_to_binary_context = x0; + context_to_binary_context = r(0); goto do_bs_get_binary_all_reuse; } @@ -4883,9 +4804,7 @@ do { \ BeamInstr 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); @@ -5103,7 +5022,6 @@ do { \ SWAPOUT; c_p->fcalls = FCALLS; c_p->def_arg_reg[4] = -neg_o_reds; - reg[0] = r(0); c_p = hipe_mode_switch(c_p, cmd, reg); reg = ERTS_PROC_GET_SCHDATA(c_p)->x_reg_array; freg = ERTS_PROC_GET_SCHDATA(c_p)->f_reg_array; @@ -5120,7 +5038,6 @@ do { \ /*fall through*/ case HIPE_MODE_SWITCH_RES_CALL_BEAM: SET_I(c_p->i); - r(0) = reg[0]; Dispatch(); case HIPE_MODE_SWITCH_RES_CALL_CLOSURE: /* This can be used to call any function value, but currently it's @@ -5131,7 +5048,6 @@ do { \ next = call_fun(c_p, c_p->arity - 1, reg, THE_NON_VALUE); SWAPIN; if (next != NULL) { - r(0) = reg[0]; SET_I(next); Dispatchfun(); } @@ -5191,9 +5107,7 @@ do { \ OpCase(i_debug_breakpoint): { SWAPOUT; - reg[0] = r(0); I = call_error_handler(c_p, I-3, reg, am_breakpoint); - r(0) = reg[0]; SWAPIN; if (I) { Goto(*I); -- cgit v1.2.3 From e1019cbba7a66788a068b6df1a1caf2d643ef65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 30 Mar 2012 11:54:04 +0200 Subject: Eliminate R_REG_DEF --- erts/emulator/beam/beam_debug.c | 22 ++-------------------- erts/emulator/beam/beam_emu.c | 14 -------------- erts/emulator/beam/beam_load.c | 8 ++------ erts/emulator/beam/erl_term.h | 2 -- 4 files changed, 4 insertions(+), 42 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 8a35ad17c6..00d0b27178 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -452,21 +452,13 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) case 's': /* Any source (tagged constant or register) */ tag = beam_reg_tag(*ap); if (tag == X_REG_DEF) { - if (reg_index(*ap) == 0) { - erts_print(to, to_arg, "x[0]"); - } else { - erts_print(to, to_arg, "x(%d)", reg_index(*ap)); - } + erts_print(to, to_arg, "x(%d)", reg_index(*ap)); ap++; break; } else if (tag == Y_REG_DEF) { erts_print(to, to_arg, "y(%d)", reg_index(*ap) - CP_SIZE); ap++; break; - } else if (tag == R_REG_DEF) { - erts_print(to, to_arg, "x(0)"); - ap++; - break; } /*FALLTHROUGH*/ case 'a': /* Tagged atom */ @@ -483,18 +475,11 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) case 'd': /* Destination (x(0), x(N), y(N)) */ switch (beam_reg_tag(*ap)) { case X_REG_DEF: - if (reg_index(*ap) == 0) { - erts_print(to, to_arg, "x[0]"); - } else { - erts_print(to, to_arg, "x(%d)", reg_index(*ap)); - } + erts_print(to, to_arg, "x(%d)", reg_index(*ap)); break; case Y_REG_DEF: erts_print(to, to_arg, "y(%d)", reg_index(*ap) - CP_SIZE); break; - case R_REG_DEF: - erts_print(to, to_arg, "x(0)"); - break; } ap++; break; @@ -677,9 +662,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) erts_print(to, to_arg, " %T", (Eterm) ap[0]); } else { switch ((ap[0] >> 2) & 0x03) { - case R_REG_DEF: - erts_print(to, to_arg, " x(0)"); - break; case X_REG_DEF: erts_print(to, to_arg, " x(%d)", ap[0] >> 4); break; diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 9a34d5f11a..059c443450 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -160,8 +160,6 @@ do { \ stb_reg = (DestDesc); \ CHECK_TERM(Result); \ switch (beam_reg_tag(stb_reg)) { \ - case R_REG_DEF: \ - r(0) = (Result); break; \ case X_REG_DEF: \ xb(x_reg_offset(stb_reg)) = (Result); break; \ default: \ @@ -186,8 +184,6 @@ do { \ stb_next = (BeamInstr *) *I; \ CHECK_TERM(Result); \ switch (beam_reg_tag(stb_reg)) { \ - case R_REG_DEF: \ - r(0) = (Result); Goto(stb_next); \ case X_REG_DEF: \ xb(x_reg_offset(stb_reg)) = (Result); Goto(stb_next); \ default: \ @@ -521,7 +517,6 @@ void** beam_ops; do { \ tr = Arg(pos); \ switch (beam_reg_tag(tr)) { \ - case R_REG_DEF: tr = r(0); break; \ case X_REG_DEF: tr = xb(x_reg_offset(tr)); break; \ case Y_REG_DEF: ASSERT(y_reg_offset(tr) >= 1); tr = yb(y_reg_offset(tr)); break; \ } \ @@ -2391,9 +2386,6 @@ void process_main(void) do { Eterm term = *I++; switch (term & _TAG_IMMED1_MASK) { - case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: - *hp++ = r(0); - break; case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: *hp++ = x(term >> _TAG_IMMED1_SIZE); break; @@ -2422,9 +2414,6 @@ void process_main(void) #define PUT_TERM_REG(term, desc) \ do { \ switch ((desc) & _TAG_IMMED1_MASK) { \ - case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ - r(0) = (term); \ - break; \ case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ x((desc) >> _TAG_IMMED1_SIZE) = (term); \ break; \ @@ -6502,9 +6491,6 @@ static Eterm get_map_element_hash(Eterm map, Eterm key, Uint32 hx) do { \ Eterm src = (Eterm)(term); \ switch (src & _TAG_IMMED1_MASK) { \ - case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ - dest = x(0); \ - break; \ case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ dest = x(src >> _TAG_IMMED1_SIZE); \ break; \ diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index f5f1147261..495e92d600 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -2167,7 +2167,7 @@ load_code(LoaderState* stp) case 's': /* Any source (tagged constant or register) */ switch (tag) { case TAG_r: - code[ci++] = make_rreg(); + code[ci++] = make_xreg(0); break; case TAG_x: code[ci++] = make_xreg(tmp_op->a[arg].val); @@ -2193,7 +2193,7 @@ load_code(LoaderState* stp) case 'd': /* Destination (x(0), x(N), y(N) */ switch (tag) { case TAG_r: - code[ci++] = make_rreg(); + code[ci++] = make_xreg(0); break; case TAG_x: code[ci++] = make_xreg(tmp_op->a[arg].val); @@ -2357,10 +2357,6 @@ load_code(LoaderState* stp) ci++; break; case TAG_r: - CodeNeed(1); - code[ci++] = (R_REG_DEF << _TAG_PRIMARY_SIZE) | - TAG_PRIMARY_HEADER; - break; case TAG_x: CodeNeed(1); code[ci++] = (tmp_op->a[arg].val << _TAG_IMMED1_SIZE) | diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index b928e4ca1e..cff2430547 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -1037,11 +1037,9 @@ _ET_DECLARE_CHECKED(Uint,catch_val,Eterm) #define X_REG_DEF 0 #define Y_REG_DEF 1 -#define R_REG_DEF 2 #define beam_reg_tag(x) ((x) & 3) -#define make_rreg() R_REG_DEF #define make_xreg(ix) (((ix) * sizeof(Eterm)) | X_REG_DEF) #define make_yreg(ix) (((ix) * sizeof(Eterm)) | Y_REG_DEF) -- cgit v1.2.3 From f0923b143ecfdacfe4873fc1e96c8ec69726a4d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 16 Jun 2015 06:39:25 +0200 Subject: Allow X and Y registers to be overloaded with any literal Consider the try_case_end instruction: try_case_end s The 's' operand type means that the operand can either be a literal of one of the types atom, integer, or empty list, or a register. That worked well before R12. In R12 additional types of literals where introduced. Because of way the overloading was done, an 's' operand cannot handle the new types of literals. Therefore, code such as the following is necessary in ops.tab to avoid giving an 's' operand a literal: try_case_end Literal=q => move Literal x | try_case_end x While this work, it is error-prone in that it is easy to forget to add that kind of rule. It would also be complicated in case we wanted to introduce a new kind of addition operator such as: i_plus jssd Since there are two 's' operands, two scratch registers and two 'move' instructions would be needed. Therefore, we'll need to find a smarter way to find tag register operands. We will overload the pid and port tags for X and Y register, respectively. That works because pids and port are immediate values (fit in one word), and there are no literals for pids and ports. --- erts/emulator/beam/beam_debug.c | 77 +++++++++++----------- erts/emulator/beam/beam_emu.c | 139 +++++++++++++++++++++------------------ erts/emulator/beam/beam_load.c | 22 ++++--- erts/emulator/beam/erl_term.c | 6 +- erts/emulator/beam/erl_term.h | 50 +++++++------- erts/emulator/beam/ops.tab | 29 +------- erts/emulator/utils/beam_makeops | 3 +- 7 files changed, 155 insertions(+), 171 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 00d0b27178..243ccb9afd 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -432,31 +432,33 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) while (*sign) { switch (*sign) { case 'r': /* x(0) */ - erts_print(to, to_arg, "x(0)"); + erts_print(to, to_arg, "r(0)"); break; case 'x': /* x(N) */ - if (reg_index(ap[0]) == 0) { - erts_print(to, to_arg, "x[0]"); - } else { - erts_print(to, to_arg, "x(%d)", reg_index(ap[0])); + { + Uint n = ap[0] / sizeof(Eterm); + erts_print(to, to_arg, "x(%d)", n); + ap++; } - ap++; break; case 'y': /* y(N) */ - erts_print(to, to_arg, "y(%d)", reg_index(ap[0]) - CP_SIZE); - ap++; + { + Uint n = ap[0] / sizeof(Eterm) - CP_SIZE; + erts_print(to, to_arg, "y(%d)", n); + ap++; + } break; case 'n': /* Nil */ erts_print(to, to_arg, "[]"); break; case 's': /* Any source (tagged constant or register) */ - tag = beam_reg_tag(*ap); - if (tag == X_REG_DEF) { - erts_print(to, to_arg, "x(%d)", reg_index(*ap)); + tag = loader_tag(*ap); + if (tag == LOADER_X_REG) { + erts_print(to, to_arg, "x(%d)", loader_x_reg_index(*ap)); ap++; break; - } else if (tag == Y_REG_DEF) { - erts_print(to, to_arg, "y(%d)", reg_index(*ap) - CP_SIZE); + } else if (tag == LOADER_Y_REG) { + erts_print(to, to_arg, "y(%d)", loader_y_reg_index(*ap) - CP_SIZE); ap++; break; } @@ -473,12 +475,14 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) ap++; break; case 'd': /* Destination (x(0), x(N), y(N)) */ - switch (beam_reg_tag(*ap)) { - case X_REG_DEF: - erts_print(to, to_arg, "x(%d)", reg_index(*ap)); + switch (loader_tag(*ap)) { + case LOADER_X_REG: + erts_print(to, to_arg, "x(%d)", + loader_x_reg_index(*ap)); break; - case Y_REG_DEF: - erts_print(to, to_arg, "y(%d)", reg_index(*ap) - CP_SIZE); + case LOADER_Y_REG: + erts_print(to, to_arg, "y(%d)", + loader_y_reg_index(*ap) - CP_SIZE); break; } ap++; @@ -546,7 +550,7 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) ap++; break; case 'l': /* fr(N) */ - erts_print(to, to_arg, "fr(%d)", reg_index(ap[0])); + erts_print(to, to_arg, "fr(%d)", loader_reg_index(ap[0])); ap++; break; default: @@ -658,17 +662,16 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) int n = unpacked[-1]; while (n > 0) { - if (!is_header(ap[0])) { + switch (loader_tag(ap[0])) { + case LOADER_X_REG: + erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0])); + break; + case LOADER_Y_REG: + erts_print(to, to_arg, " x(%d)", loader_y_reg_index(ap[0])); + break; + default: erts_print(to, to_arg, " %T", (Eterm) ap[0]); - } else { - switch ((ap[0] >> 2) & 0x03) { - case X_REG_DEF: - erts_print(to, to_arg, " x(%d)", ap[0] >> 4); - break; - case Y_REG_DEF: - erts_print(to, to_arg, " y(%d)", ap[0] >> 4); - break; - } + break; } ap++, size++, n--; } @@ -681,18 +684,16 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) while (n > 0) { if (n % 3 == 1) { erts_print(to, to_arg, " %X", ap[0]); - } else if (!is_header(ap[0])) { - erts_print(to, to_arg, " %T", (Eterm) ap[0]); } else { - switch ((ap[0] >> 2) & 0x03) { - case R_REG_DEF: - erts_print(to, to_arg, " x(0)"); + switch (loader_tag(ap[0])) { + case LOADER_X_REG: + erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0])); break; - case X_REG_DEF: - erts_print(to, to_arg, " x(%d)", ap[0] >> 4); + case LOADER_Y_REG: + erts_print(to, to_arg, " y(%d)", loader_y_reg_index(ap[0])); break; - case Y_REG_DEF: - erts_print(to, to_arg, " y(%d)", ap[0] >> 4); + default: + erts_print(to, to_arg, " %T", (Eterm) ap[0]); break; } } diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 059c443450..a74da2d872 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -154,17 +154,19 @@ do { \ * Store a result into a register given a destination descriptor. */ -#define StoreResult(Result, DestDesc) \ - do { \ - Eterm stb_reg; \ - stb_reg = (DestDesc); \ - CHECK_TERM(Result); \ - switch (beam_reg_tag(stb_reg)) { \ - case X_REG_DEF: \ - xb(x_reg_offset(stb_reg)) = (Result); break; \ - default: \ - yb(y_reg_offset(stb_reg)) = (Result); break; \ - } \ +#define StoreResult(Result, DestDesc) \ + do { \ + Eterm stb_reg; \ + stb_reg = (DestDesc); \ + CHECK_TERM(Result); \ + switch (loader_tag(stb_reg)) { \ + case LOADER_X_REG: \ + x(loader_x_reg_index(stb_reg)) = (Result); \ + break; \ + default: \ + y(loader_y_reg_index(stb_reg)) = (Result); \ + break; \ + } \ } while (0) #define StoreSimpleDest(Src, Dest) Dest = (Src) @@ -175,20 +177,22 @@ do { \ * be just before the next instruction. */ -#define StoreBifResult(Dst, Result) \ - do { \ - BeamInstr* stb_next; \ - Eterm stb_reg; \ - stb_reg = Arg(Dst); \ - I += (Dst) + 2; \ - stb_next = (BeamInstr *) *I; \ - CHECK_TERM(Result); \ - switch (beam_reg_tag(stb_reg)) { \ - case X_REG_DEF: \ - xb(x_reg_offset(stb_reg)) = (Result); Goto(stb_next); \ - default: \ - yb(y_reg_offset(stb_reg)) = (Result); Goto(stb_next); \ - } \ +#define StoreBifResult(Dst, Result) \ + do { \ + BeamInstr* stb_next; \ + Eterm stb_reg; \ + stb_reg = Arg(Dst); \ + I += (Dst) + 2; \ + stb_next = (BeamInstr *) *I; \ + CHECK_TERM(Result); \ + switch (loader_tag(stb_reg)) { \ + case LOADER_X_REG: \ + x(loader_x_reg_index(stb_reg)) = (Result); \ + Goto(stb_next); \ + default: \ + y(loader_y_reg_index(stb_reg)) = (Result); \ + Goto(stb_next); \ + } \ } while (0) #define ClauseFail() goto jump_f @@ -513,14 +517,19 @@ void** beam_ops; ASSERT(VALID_INSTR(Dst)); \ Goto(Dst) -#define GetR(pos, tr) \ - do { \ - tr = Arg(pos); \ - switch (beam_reg_tag(tr)) { \ - case X_REG_DEF: tr = xb(x_reg_offset(tr)); break; \ - case Y_REG_DEF: ASSERT(y_reg_offset(tr) >= 1); tr = yb(y_reg_offset(tr)); break; \ - } \ - CHECK_TERM(tr); \ +#define GetR(pos, tr) \ + do { \ + tr = Arg(pos); \ + switch (loader_tag(tr)) { \ + case LOADER_X_REG: \ + tr = x(loader_x_reg_index(tr)); \ + break; \ + case LOADER_Y_REG: \ + ASSERT(loader_y_reg_index(tr) >= 1); \ + tr = y(loader_y_reg_index(tr)); \ + break; \ + } \ + CHECK_TERM(tr); \ } while (0) #define GetArg1(N, Dst) GetR((N), Dst) @@ -2385,12 +2394,12 @@ void process_main(void) do { Eterm term = *I++; - switch (term & _TAG_IMMED1_MASK) { - case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: - *hp++ = x(term >> _TAG_IMMED1_SIZE); + switch (loader_tag(term)) { + case LOADER_X_REG: + *hp++ = x(loader_x_reg_index(term)); break; - case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: - *hp++ = y(term >> _TAG_IMMED1_SIZE); + case LOADER_Y_REG: + *hp++ = y(loader_y_reg_index(term)); break; default: *hp++ = term; @@ -2411,19 +2420,19 @@ void process_main(void) Next(3+Arg(2)); } -#define PUT_TERM_REG(term, desc) \ -do { \ - switch ((desc) & _TAG_IMMED1_MASK) { \ - case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ - x((desc) >> _TAG_IMMED1_SIZE) = (term); \ - break; \ - case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ - y((desc) >> _TAG_IMMED1_SIZE) = (term); \ - break; \ - default: \ - ASSERT(0); \ - break; \ - } \ +#define PUT_TERM_REG(term, desc) \ +do { \ + switch (loader_tag(desc)) { \ + case LOADER_X_REG: \ + x(loader_x_reg_index(desc)) = (term); \ + break; \ + case LOADER_Y_REG: \ + y(loader_y_reg_index(desc)) = (term); \ + break; \ + default: \ + ASSERT(0); \ + break; \ + } \ } while(0) OpCase(i_get_map_elements_fsI): { @@ -6487,20 +6496,20 @@ static Eterm get_map_element_hash(Eterm map, Eterm key, Uint32 hx) return vs ? *vs : THE_NON_VALUE; } -#define GET_TERM(term, dest) \ -do { \ - Eterm src = (Eterm)(term); \ - switch (src & _TAG_IMMED1_MASK) { \ - case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ - dest = x(src >> _TAG_IMMED1_SIZE); \ - break; \ - case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ - dest = y(src >> _TAG_IMMED1_SIZE); \ - break; \ - default: \ - dest = src; \ - break; \ - } \ +#define GET_TERM(term, dest) \ +do { \ + Eterm src = (Eterm)(term); \ + switch (loader_tag(src)) { \ + case LOADER_X_REG: \ + dest = x(loader_x_reg_index(src)); \ + break; \ + case LOADER_Y_REG: \ + dest = y(loader_y_reg_index(src)); \ + break; \ + default: \ + dest = src; \ + break; \ + } \ } while(0) diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 495e92d600..3cd80c4ec2 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -2167,13 +2167,13 @@ load_code(LoaderState* stp) case 's': /* Any source (tagged constant or register) */ switch (tag) { case TAG_r: - code[ci++] = make_xreg(0); + code[ci++] = make_loader_x_reg(0); break; case TAG_x: - code[ci++] = make_xreg(tmp_op->a[arg].val); + code[ci++] = make_loader_x_reg(tmp_op->a[arg].val); break; case TAG_y: - code[ci++] = make_yreg(tmp_op->a[arg].val); + code[ci++] = make_loader_y_reg(tmp_op->a[arg].val); break; case TAG_i: code[ci++] = (BeamInstr) make_small((Uint)tmp_op->a[arg].val); @@ -2184,6 +2184,10 @@ load_code(LoaderState* stp) case TAG_n: code[ci++] = NIL; break; + case TAG_q: + new_literal_patch(stp, ci); + code[ci++] = tmp_op->a[arg].val; + break; default: LoadError1(stp, "bad tag %d for general source", tmp_op->a[arg].type); @@ -2193,13 +2197,13 @@ load_code(LoaderState* stp) case 'd': /* Destination (x(0), x(N), y(N) */ switch (tag) { case TAG_r: - code[ci++] = make_xreg(0); + code[ci++] = make_loader_x_reg(0); break; case TAG_x: - code[ci++] = make_xreg(tmp_op->a[arg].val); + code[ci++] = make_loader_x_reg(tmp_op->a[arg].val); break; case TAG_y: - code[ci++] = make_yreg(tmp_op->a[arg].val); + code[ci++] = make_loader_y_reg(tmp_op->a[arg].val); break; default: LoadError1(stp, "bad tag %d for destination", @@ -2359,13 +2363,11 @@ load_code(LoaderState* stp) case TAG_r: case TAG_x: CodeNeed(1); - code[ci++] = (tmp_op->a[arg].val << _TAG_IMMED1_SIZE) | - (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER; + code[ci++] = make_loader_x_reg(tmp_op->a[arg].val); break; case TAG_y: CodeNeed(1); - code[ci++] = (tmp_op->a[arg].val << _TAG_IMMED1_SIZE) | - (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER; + code[ci++] = make_loader_y_reg(tmp_op->a[arg].val); break; case TAG_n: CodeNeed(1); diff --git a/erts/emulator/beam/erl_term.c b/erts/emulator/beam/erl_term.c index 89459fb278..3a5fbcc284 100644 --- a/erts/emulator/beam/erl_term.c +++ b/erts/emulator/beam/erl_term.c @@ -173,9 +173,7 @@ ET_DEFINE_CHECKED(Uint,external_thing_data_words,ExternalThing*,is_thing_ptr); ET_DEFINE_CHECKED(Eterm,make_cp,UWord *,_is_taggable_pointer); ET_DEFINE_CHECKED(UWord *,cp_val,Eterm,is_CP); ET_DEFINE_CHECKED(Uint,catch_val,Eterm,is_catch); -ET_DEFINE_CHECKED(Uint,x_reg_offset,Uint,_is_xreg); -ET_DEFINE_CHECKED(Uint,y_reg_offset,Uint,_is_yreg); -ET_DEFINE_CHECKED(Uint,x_reg_index,Uint,_is_xreg); -ET_DEFINE_CHECKED(Uint,y_reg_index,Uint,_is_yreg); +ET_DEFINE_CHECKED(Uint,loader_x_reg_index,Uint,_is_loader_x_reg); +ET_DEFINE_CHECKED(Uint,loader_y_reg_index,Uint,_is_loader_y_reg); #endif /* ET_DEBUG */ diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index cff2430547..089eecf024 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -1027,42 +1027,40 @@ _ET_DECLARE_CHECKED(Uint,catch_val,Eterm) /* * Overloaded tags. * - * SMALL = 15 - * ATOM/NIL=7 + * In the loader, we want to tag a term in a way so that it can + * be any literal (atom/integer/float/tuple/list/binary) or a + * register. * - * Note that the two least significant bits in SMALL/ATOM/NIL always are 3; - * thus, we can distinguish register from literals by looking at only these - * two bits. + * We can achive that by overloading the PID and PORT tags to + * mean X and Y registers. That works because there are no + * pid or port literals. */ -#define X_REG_DEF 0 -#define Y_REG_DEF 1 +#define _LOADER_TAG_XREG _TAG_IMMED1_PID +#define _LOADER_TAG_YREG _TAG_IMMED1_PORT +#define _LOADER_TAG_SIZE _TAG_IMMED1_SIZE +#define _LOADER_MASK _TAG_IMMED1_MASK -#define beam_reg_tag(x) ((x) & 3) +#define LOADER_X_REG _LOADER_TAG_XREG +#define LOADER_Y_REG _LOADER_TAG_YREG -#define make_xreg(ix) (((ix) * sizeof(Eterm)) | X_REG_DEF) -#define make_yreg(ix) (((ix) * sizeof(Eterm)) | Y_REG_DEF) +#define make_loader_x_reg(R) (((R) << _LOADER_TAG_SIZE) | _LOADER_TAG_XREG) +#define make_loader_y_reg(R) (((R) << _LOADER_TAG_SIZE) | _LOADER_TAG_YREG) -#define _is_xreg(x) (beam_reg_tag(x) == X_REG_DEF) -#define _is_yreg(x) (beam_reg_tag(x) == Y_REG_DEF) +#define loader_reg_index(R) ((R) >> _LOADER_TAG_SIZE) -#define _unchecked_x_reg_offset(R) ((R) - X_REG_DEF) -_ET_DECLARE_CHECKED(Uint,x_reg_offset,Uint) -#define x_reg_offset(R) _ET_APPLY(x_reg_offset,(R)) +#define loader_tag(T) ((T) & _LOADER_MASK) -#define _unchecked_y_reg_offset(R) ((R) - Y_REG_DEF) -_ET_DECLARE_CHECKED(Uint,y_reg_offset,Uint) -#define y_reg_offset(R) _ET_APPLY(y_reg_offset,(R)) +#define _is_loader_x_reg(x) (loader_tag(x) == _LOADER_TAG_XREG) +#define _is_loader_y_reg(x) (loader_tag(x) == _LOADER_TAG_YREG) -#define reg_index(R) ((R) / sizeof(Eterm)) +#define _unchecked_loader_x_reg_index(R) ((R) >> _LOADER_TAG_SIZE) +_ET_DECLARE_CHECKED(Uint,loader_x_reg_index,Uint) +#define loader_x_reg_index(R) _ET_APPLY(loader_x_reg_index,(R)) -#define _unchecked_x_reg_index(R) ((R) >> 2) -_ET_DECLARE_CHECKED(Uint,x_reg_index,Uint) -#define x_reg_index(R) _ET_APPLY(x_reg_index,(R)) - -#define _unchecked_y_reg_index(R) ((R) >> 2) -_ET_DECLARE_CHECKED(Uint,y_reg_index,Uint) -#define y_reg_index(R) _ET_APPLY(y_reg_index,(R)) +#define _unchecked_loader_y_reg_index(R) ((R) >> _LOADER_TAG_SIZE) +_ET_DECLARE_CHECKED(Uint,loader_y_reg_index,Uint) +#define loader_y_reg_index(R) _ET_APPLY(loader_y_reg_index,(R)) /* * Backwards compatibility definitions: diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 1d32e72247..4fa6a087e2 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -237,12 +237,10 @@ try Y F => catch Y F try_case Y => try_end Y try_end y -try_case_end Literal=q => move Literal x | try_case_end x try_case_end s # Destructive set tuple element -set_tuple_element Lit=q Tuple Pos => move Lit x | set_tuple_element x Tuple Pos set_tuple_element s d P # Get tuple element @@ -273,8 +271,8 @@ is_number Fail Literal=q => move Literal x | is_number Fail x jump f -case_end Literal=cq => move Literal x | case_end x -badmatch Literal=cq => move Literal x | badmatch x +case_end Literal=c => move Literal x | case_end x +badmatch Literal=c => move Literal x | badmatch x case_end r case_end x @@ -849,7 +847,6 @@ is_boolean f y is_function2 Fail=f acq Arity => jump Fail is_function2 Fail=f Fun a => jump Fail -is_function2 Fail Fun Literal=q => move Literal x | is_function2 Fail Fun x is_function2 f s s %macro: is_function2 IsFunction2 -fail_action @@ -1122,11 +1119,8 @@ bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => i_get Src Dst bif2 Jump=j u$bif:erlang:element/2 S1=s S2=rxy Dst=d => gen_element(Jump, S1, S2, Dst) -bif1 Fail Bif Literal=q Dst => move Literal x | bif1 Fail Bif x Dst bif1 p Bif S1 Dst => bif1_body Bif S1 Dst -bif1_body Bif Literal=q Dst => move Literal x | bif1_body Bif x Dst - bif2 p Bif S1 S2 Dst => i_fetch S1 S2 | i_bif2_body Bif Dst bif2 Fail Bif S1 S2 Dst => i_fetch S1 S2 | i_bif2 Fail Bif Dst @@ -1439,8 +1433,6 @@ i_bs_private_append j I d # Storing integers into binaries. # -bs_put_integer Fail=j Sz=s Unit=u Flags=u Literal=q => \ - move Literal x | bs_put_integer Fail Sz Unit Flags x bs_put_integer Fail=j Sz=sq Unit=u Flags=u Src=s => \ gen_put_integer(Fail, Sz, Unit, Flags, Src) @@ -1454,32 +1446,22 @@ i_new_bs_put_integer_imm j I I s # Utf8/utf16/utf32 support. (R12B-5) # -bs_utf8_size Fail Literal=q Dst=d => \ - move Literal x | bs_utf8_size Fail x Dst bs_utf8_size j Src=s Dst=d => i_bs_utf8_size Src Dst i_bs_utf8_size s d -bs_utf16_size Fail Literal=q Dst=d => \ - move Literal x | bs_utf16_size Fail x Dst bs_utf16_size j Src=s Dst=d => i_bs_utf16_size Src Dst i_bs_utf16_size s d -bs_put_utf8 Fail=j Flags=u Literal=q => \ - move Literal x | bs_put_utf8 Fail Flags x bs_put_utf8 Fail u Src=s => i_bs_put_utf8 Fail Src i_bs_put_utf8 j s -bs_put_utf16 Fail=j Flags=u Literal=q => \ - move Literal x | bs_put_utf16 Fail Flags x bs_put_utf16 Fail Flags=u Src=s => i_bs_put_utf16 Fail Flags Src i_bs_put_utf16 j I s -bs_put_utf32 Fail=j Flags=u Literal=q => \ - move Literal x | bs_put_utf32 Fail Flags x bs_put_utf32 Fail=j Flags=u Src=s => \ i_bs_validate_unicode Fail Src | bs_put_integer Fail i=32 u=1 Flags Src @@ -1490,9 +1472,6 @@ i_bs_validate_unicode j s # bs_put_float Fail Sz=q Unit Flags Val => badarg Fail -bs_put_float Fail=j Sz Unit=u Flags=u Literal=q => \ - move Literal x | bs_put_float Fail Sz Unit Flags x - bs_put_float Fail=j Sz=s Unit=u Flags=u Src=s => \ gen_put_float(Fail, Sz, Unit, Flags, Src) @@ -1506,8 +1485,6 @@ i_new_bs_put_float_imm j I I s # Storing binaries into binaries. # -bs_put_binary Fail Sz Unit Flags Literal=q => \ - move Literal x | bs_put_binary Fail Sz Unit Flags x bs_put_binary Fail=j Sz=s Unit=u Flags=u Src=s => \ gen_put_binary(Fail, Sz, Unit, Flags, Src) @@ -1731,8 +1708,6 @@ gc_bif2 Fail I Bif S1 S2 Dst => \ gc_bif3 Fail I Bif S1 S2 S3 Dst => \ gen_guard_bif3(Fail, I, Bif, S1, S2, S3, Dst) -i_gc_bif1 Fail Bif V=q Live D => move V x | i_gc_bif1 Fail Bif x Live D - i_gc_bif1 j I s I d ii_gc_bif2/6 diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 6e1741af85..00b0df5b73 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -180,7 +180,8 @@ sub define_type_bit { define_type_bit('c', $type_bit{'i'} | $type_bit{'a'} | $type_bit{'n'} | $type_bit{'q'}); define_type_bit('s', $type_bit{'d'} | $type_bit{'i'} | - $type_bit{'a'} | $type_bit{'n'}); + $type_bit{'a'} | $type_bit{'n'} | + $type_bit{'q'}); define_type_bit('j', $type_bit{'f'} | $type_bit{'p'}); # Aliases (for matching purposes). -- cgit v1.2.3 From 135c9821e8806defa455a47208fb897fff1a3ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 23 Jun 2015 08:53:50 +0200 Subject: Make the 'r' operand type optional The 'r' type is now mandatory. That means in order to handle both of the following instructions: move x(0) y(7) move x(1) y(7) we would need to define two specific operations in ops.tab: move r y move x y We want to make 'r' operands optional. That is, if we have only this specific instruction: move x y it will match both of the following instructions: move x(0) y(7) move x(1) y(7) Make 'r' optional allows us to save code space when we don't want to make handling of x(0) a special case, but we can still use 'r' to optimize commonly used instructions. --- erts/emulator/beam/beam_debug.c | 6 - erts/emulator/beam/beam_emu.c | 355 ++++++------------------------ erts/emulator/beam/beam_load.c | 66 ++++-- erts/emulator/beam/erl_vm.h | 1 + erts/emulator/beam/ops.tab | 456 +++++++++++++-------------------------- erts/emulator/utils/beam_makeops | 16 +- 6 files changed, 282 insertions(+), 618 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 243ccb9afd..30e3765502 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -569,7 +569,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) unpacked = ap; ap = addr + size; switch (op) { - case op_i_select_val_lins_rfI: case op_i_select_val_lins_xfI: case op_i_select_val_lins_yfI: { @@ -589,7 +588,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) } } break; - case op_i_select_val_bins_rfI: case op_i_select_val_bins_xfI: case op_i_select_val_bins_yfI: { @@ -603,7 +601,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) } } break; - case op_i_select_tuple_arity_rfI: case op_i_select_tuple_arity_xfI: case op_i_select_tuple_arity_yfI: { @@ -628,7 +625,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) } } break; - case op_i_jump_on_val_rfII: case op_i_jump_on_val_xfII: case op_i_jump_on_val_yfII: { @@ -640,7 +636,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) } } break; - case op_i_jump_on_val_zero_rfI: case op_i_jump_on_val_zero_xfI: case op_i_jump_on_val_zero_yfI: { @@ -652,7 +647,6 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) } } break; - case op_i_put_tuple_rI: case op_i_put_tuple_xI: case op_i_put_tuple_yI: case op_new_map_dII: diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index a74da2d872..e5179830de 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -556,12 +556,12 @@ void** beam_ops; #define Move2(S1, D1, S2, D2) D1 = (S1); D2 = (S2) #define Move3(S1, D1, S2, D2, S3, D3) D1 = (S1); D2 = (S2); D3 = (S3) -#define MoveReturn(Src, Dest) \ - (Dest) = (Src); \ - I = c_p->cp; \ - ASSERT(VALID_INSTR(*c_p->cp)); \ - c_p->cp = 0; \ - CHECK_TERM(r(0)); \ +#define MoveReturn(Src) \ + x(0) = (Src); \ + I = c_p->cp; \ + ASSERT(VALID_INSTR(*c_p->cp)); \ + c_p->cp = 0; \ + CHECK_TERM(r(0)); \ Goto(*I) #define DeallocateReturn(Deallocate) \ @@ -573,26 +573,26 @@ void** beam_ops; Goto(*I); \ } while (0) -#define MoveDeallocateReturn(Src, Dest, Deallocate) \ - (Dest) = (Src); \ +#define MoveDeallocateReturn(Src, Deallocate) \ + x(0) = (Src); \ DeallocateReturn(Deallocate) -#define MoveCall(Src, Dest, CallDest, Size) \ - (Dest) = (Src); \ +#define MoveCall(Src, CallDest, Size) \ + x(0) = (Src); \ SET_CP(c_p, I+Size+1); \ - SET_I((BeamInstr *) CallDest); \ + SET_I((BeamInstr *) CallDest); \ Dispatch(); -#define MoveCallLast(Src, Dest, CallDest, Deallocate) \ - (Dest) = (Src); \ - RESTORE_CP(E); \ - E = ADD_BYTE_OFFSET(E, (Deallocate)); \ - SET_I((BeamInstr *) CallDest); \ +#define MoveCallLast(Src, CallDest, Deallocate) \ + x(0) = (Src); \ + RESTORE_CP(E); \ + E = ADD_BYTE_OFFSET(E, (Deallocate)); \ + SET_I((BeamInstr *) CallDest); \ Dispatch(); -#define MoveCallOnly(Src, Dest, CallDest) \ - (Dest) = (Src); \ - SET_I((BeamInstr *) CallDest); \ +#define MoveCallOnly(Src, CallDest) \ + x(0) = (Src); \ + SET_I((BeamInstr *) CallDest); \ Dispatch(); #define MoveJump(Src) \ @@ -676,8 +676,8 @@ void** beam_ops; if (is_not_list(Src)) { Fail; } \ A(Need, Alive) -#define IsNonemptyListTestHeap(Src, Need, Alive, Fail) \ - if (is_not_list(Src)) { Fail; } \ +#define IsNonemptyListTestHeap(Need, Alive, Fail) \ + if (is_not_list(x(0))) { Fail; } \ TestHeap(Need, Alive) #define IsTuple(X, Action) if (is_not_tuple(X)) Action @@ -1316,17 +1316,13 @@ void process_main(void) Uint live; Eterm result; - OpCase(i_increment_yIId): - increment_reg_val = yb(Arg(0)); - goto do_increment; - OpCase(i_increment_xIId): increment_reg_val = xb(Arg(0)); goto do_increment; - OpCase(i_increment_rIId): - increment_reg_val = r(0); - I--; + OpCase(i_increment_yIId): + increment_reg_val = yb(Arg(0)); + goto do_increment; do_increment: increment_val = Arg(1); @@ -1335,7 +1331,6 @@ void process_main(void) ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i)); if (MY_IS_SSMALL(i)) { result = make_small(i); - store_result: StoreBifResult(3, result); } } @@ -1348,7 +1343,7 @@ void process_main(void) SWAPIN; ERTS_HOLE_CHECK(c_p); if (is_value(result)) { - goto store_result; + StoreBifResult(3, result); } ASSERT(c_p->freason != BADMATCH || is_value(c_p->fvalue)); goto find_func_info; @@ -1464,47 +1459,37 @@ void process_main(void) { Eterm is_eq_exact_lit_val; - OpCase(i_is_eq_exact_literal_xfc): - is_eq_exact_lit_val = xb(Arg(0)); - I++; + OpCase(i_is_eq_exact_literal_fxc): + is_eq_exact_lit_val = xb(Arg(1)); goto do_is_eq_exact_literal; - OpCase(i_is_eq_exact_literal_yfc): - is_eq_exact_lit_val = yb(Arg(0)); - I++; + OpCase(i_is_eq_exact_literal_fyc): + is_eq_exact_lit_val = yb(Arg(1)); goto do_is_eq_exact_literal; - OpCase(i_is_eq_exact_literal_rfc): - is_eq_exact_lit_val = r(0); - do_is_eq_exact_literal: - if (!eq(Arg(1), is_eq_exact_lit_val)) { + if (!eq(Arg(2), is_eq_exact_lit_val)) { ClauseFail(); } - Next(2); + Next(3); } { Eterm is_ne_exact_lit_val; - OpCase(i_is_ne_exact_literal_xfc): - is_ne_exact_lit_val = xb(Arg(0)); - I++; + OpCase(i_is_ne_exact_literal_fxc): + is_ne_exact_lit_val = xb(Arg(1)); goto do_is_ne_exact_literal; - OpCase(i_is_ne_exact_literal_yfc): - is_ne_exact_lit_val = yb(Arg(0)); - I++; + OpCase(i_is_ne_exact_literal_fyc): + is_ne_exact_lit_val = yb(Arg(1)); goto do_is_ne_exact_literal; - OpCase(i_is_ne_exact_literal_rfc): - is_ne_exact_lit_val = r(0); - do_is_ne_exact_literal: - if (eq(Arg(1), is_ne_exact_lit_val)) { + if (eq(Arg(2), is_ne_exact_lit_val)) { ClauseFail(); } - Next(2); + Next(3); } OpCase(move_window3_xxxy): { @@ -1553,7 +1538,7 @@ void process_main(void) NextPF(6, next); } - OpCase(i_move_call_only_fcr): { + OpCase(i_move_call_only_fc): { r(0) = Arg(1); } /* FALL THROUGH */ @@ -1563,7 +1548,7 @@ void process_main(void) Dispatch(); } - OpCase(i_move_call_last_fPcr): { + OpCase(i_move_call_last_fPc): { r(0) = Arg(2); } /* FALL THROUGH */ @@ -1575,7 +1560,7 @@ void process_main(void) Dispatch(); } - OpCase(i_move_call_crf): { + OpCase(i_move_call_cf): { r(0) = Arg(0); I++; } @@ -1587,7 +1572,7 @@ void process_main(void) Dispatch(); } - OpCase(i_move_call_ext_last_ePcr): { + OpCase(i_move_call_ext_last_ePc): { r(0) = Arg(2); } /* FALL THROUGH */ @@ -1603,7 +1588,7 @@ void process_main(void) DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, Arg(0)); Dispatchx(); - OpCase(i_move_call_ext_cre): { + OpCase(i_move_call_ext_ce): { r(0) = Arg(0); I++; } @@ -1613,7 +1598,7 @@ void process_main(void) DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, Arg(0)); Dispatchx(); - OpCase(i_move_call_ext_only_ecr): { + OpCase(i_move_call_ext_only_ec): { r(0) = Arg(1); } /* FALL THROUGH */ @@ -1707,29 +1692,23 @@ void process_main(void) Eterm element_index; Eterm element_tuple; - OpCase(i_element_xjsd): - element_tuple = xb(Arg(0)); - I++; + OpCase(i_element_jxsd): + element_tuple = xb(Arg(1)); goto do_element; - OpCase(i_element_yjsd): - element_tuple = yb(Arg(0)); - I++; + OpCase(i_element_jysd): + element_tuple = yb(Arg(1)); goto do_element; - OpCase(i_element_rjsd): - element_tuple = r(0); - /* Fall through */ - do_element: - GetArg1(1, element_index); + GetArg1(2, element_index); if (is_small(element_index) && is_tuple(element_tuple)) { Eterm* tp = tuple_val(element_tuple); if ((signed_val(element_index) >= 1) && (signed_val(element_index) <= arityval(*tp))) { Eterm result = tp[signed_val(element_index)]; - StoreBifResult(2, result); + StoreBifResult(3, result); } } } @@ -1743,29 +1722,24 @@ void process_main(void) { Eterm fast_element_tuple; - OpCase(i_fast_element_rjId): - fast_element_tuple = r(0); + OpCase(i_fast_element_jxId): + fast_element_tuple = xb(Arg(1)); + goto do_fast_element; + + OpCase(i_fast_element_jyId): + fast_element_tuple = yb(Arg(1)); + goto do_fast_element; do_fast_element: if (is_tuple(fast_element_tuple)) { Eterm* tp = tuple_val(fast_element_tuple); - Eterm pos = Arg(1); /* Untagged integer >= 1 */ + Eterm pos = Arg(2); /* Untagged integer >= 1 */ if (pos <= arityval(*tp)) { Eterm result = tp[pos]; - StoreBifResult(2, result); + StoreBifResult(3, result); } } goto badarg; - - OpCase(i_fast_element_xjId): - fast_element_tuple = xb(Arg(0)); - I++; - goto do_fast_element; - - OpCase(i_fast_element_yjId): - fast_element_tuple = yb(Arg(0)); - I++; - goto do_fast_element; } OpCase(catch_yf): @@ -1871,7 +1845,7 @@ void process_main(void) * Pick up the next message and place it in x(0). * If no message, jump to a wait or wait_timeout instruction. */ - OpCase(i_loop_rec_fr): + OpCase(i_loop_rec_f): { BeamInstr *next; ErlMessage* msgp; @@ -2183,10 +2157,6 @@ void process_main(void) select_val2 = xb(Arg(0)); goto do_select_tuple_arity2; - OpCase(i_select_tuple_arity2_rfAAff): - select_val2 = r(0); - I--; - do_select_tuple_arity2: if (is_not_tuple(select_val2)) { goto select_val2_fail; @@ -2202,10 +2172,6 @@ void process_main(void) select_val2 = xb(Arg(0)); goto do_select_val2; - OpCase(i_select_val2_rfccff): - select_val2 = r(0); - I--; - do_select_val2: if (select_val2 == Arg(2)) { I += 3; @@ -2229,10 +2195,6 @@ void process_main(void) select_val = yb(Arg(0)); goto do_select_tuple_arity; - OpCase(i_select_tuple_arity_rfI): - select_val = r(0); - I--; - do_select_tuple_arity: if (is_tuple(select_val)) { select_val = *tuple_val(select_val); @@ -2249,10 +2211,6 @@ void process_main(void) select_val = yb(Arg(0)); goto do_linear_search; - OpCase(i_select_val_lins_rfI): - select_val = r(0); - I--; - do_linear_search: { BeamInstr *vs = &Arg(3); int ix = 0; @@ -2279,10 +2237,6 @@ void process_main(void) select_val = yb(Arg(0)); goto do_binary_search; - OpCase(i_select_val_bins_rfI): - select_val = r(0); - I--; - do_binary_search: { struct Pairs { @@ -2343,10 +2297,6 @@ void process_main(void) jump_on_val_zero_index = xb(Arg(0)); goto do_jump_on_val_zero_index; - OpCase(i_jump_on_val_zero_rfI): - jump_on_val_zero_index = r(0); - I--; - do_jump_on_val_zero_index: if (is_small(jump_on_val_zero_index)) { jump_on_val_zero_index = signed_val(jump_on_val_zero_index); @@ -2371,10 +2321,6 @@ void process_main(void) jump_on_val_index = xb(Arg(0)); goto do_jump_on_val_index; - OpCase(i_jump_on_val_rfII): - jump_on_val_index = r(0); - I--; - do_jump_on_val_index: if (is_small(jump_on_val_index)) { jump_on_val_index = (Uint) (signed_val(jump_on_val_index) - Arg(3)); @@ -3441,18 +3387,8 @@ do { \ { Eterm badmatch_val; - OpCase(badmatch_y): - badmatch_val = yb(Arg(0)); - goto do_badmatch; - OpCase(badmatch_x): badmatch_val = xb(Arg(0)); - goto do_badmatch; - - OpCase(badmatch_r): - badmatch_val = r(0); - - do_badmatch: c_p->fvalue = badmatch_val; c_p->freason = BADMATCH; } @@ -3627,16 +3563,6 @@ do { \ OpCase(case_end_x): case_end_val = xb(Arg(0)); - goto do_case_end; - - OpCase(case_end_y): - case_end_val = yb(Arg(0)); - goto do_case_end; - - OpCase(case_end_r): - case_end_val = r(0); - - do_case_end: c_p->fvalue = case_end_val; c_p->freason = EXC_CASE_CLAUSE; goto find_func_info; @@ -3692,11 +3618,6 @@ do { \ goto do_bs_init_bits; } - OpCase(i_bs_init_bits_fail_rjId): { - num_bits_term = r(0); - alloc = 0; - goto do_bs_init_bits; - } OpCase(i_bs_init_bits_fail_yjId): { num_bits_term = yb(Arg(0)); I++; @@ -3823,12 +3744,6 @@ do { \ goto do_bs_init; } - OpCase(i_bs_init_fail_rjId): { - tmp_arg1 = r(0); - tmp_arg2 = 0; - goto do_bs_init; - } - OpCase(i_bs_init_fail_yjId): { tmp_arg1 = yb(Arg(0)); tmp_arg2 = 0; @@ -4202,9 +4117,6 @@ do { \ Uint slots; Eterm context; - OpCase(i_bs_start_match2_rfIId): { - context = r(0); - do_start_match: slots = Arg(2); if (!is_boxed(context)) { @@ -4251,7 +4163,7 @@ do { \ ClauseFail(); } NextPF(4, next); - } + OpCase(i_bs_start_match2_xfIId): { context = xb(Arg(0)); I++; @@ -4264,18 +4176,6 @@ do { \ } } - OpCase(bs_test_zero_tail2_fr): { - BeamInstr *next; - ErlBinMatchBuffer *_mb; - - PreFetch(1, next); - _mb = (ErlBinMatchBuffer*) ms_matchbuffer(r(0)); - if (_mb->size != _mb->offset) { - ClauseFail(); - } - NextPF(1, next); - } - OpCase(bs_test_zero_tail2_fx): { BeamInstr *next; ErlBinMatchBuffer *_mb; @@ -4288,16 +4188,6 @@ do { \ NextPF(2, next); } - OpCase(bs_test_tail_imm2_frI): { - BeamInstr *next; - ErlBinMatchBuffer *_mb; - PreFetch(2, next); - _mb = ms_matchbuffer(r(0)); - if (_mb->size - _mb->offset != Arg(1)) { - ClauseFail(); - } - NextPF(2, next); - } OpCase(bs_test_tail_imm2_fxI): { BeamInstr *next; ErlBinMatchBuffer *_mb; @@ -4309,16 +4199,6 @@ do { \ NextPF(3, next); } - OpCase(bs_test_unit_frI): { - BeamInstr *next; - ErlBinMatchBuffer *_mb; - PreFetch(2, next); - _mb = ms_matchbuffer(r(0)); - if ((_mb->size - _mb->offset) % Arg(1)) { - ClauseFail(); - } - NextPF(2, next); - } OpCase(bs_test_unit_fxI): { BeamInstr *next; ErlBinMatchBuffer *_mb; @@ -4330,16 +4210,6 @@ do { \ NextPF(3, next); } - OpCase(bs_test_unit8_fr): { - BeamInstr *next; - ErlBinMatchBuffer *_mb; - PreFetch(1, next); - _mb = ms_matchbuffer(r(0)); - if ((_mb->size - _mb->offset) & 7) { - ClauseFail(); - } - NextPF(1, next); - } OpCase(bs_test_unit8_fx): { BeamInstr *next; ErlBinMatchBuffer *_mb; @@ -4354,19 +4224,11 @@ do { \ { Eterm bs_get_integer8_context; - OpCase(i_bs_get_integer_8_rfd): { - bs_get_integer8_context = r(0); - goto do_bs_get_integer_8; - } - OpCase(i_bs_get_integer_8_xfd): { - bs_get_integer8_context = xb(Arg(0)); - I++; - } - - do_bs_get_integer_8: { ErlBinMatchBuffer *_mb; Eterm _result; + bs_get_integer8_context = xb(Arg(0)); + I++; _mb = ms_matchbuffer(bs_get_integer8_context); if (_mb->size - _mb->offset < 8) { ClauseFail(); @@ -4384,15 +4246,10 @@ do { \ { Eterm bs_get_integer_16_context; - OpCase(i_bs_get_integer_16_rfd): - bs_get_integer_16_context = r(0); - goto do_bs_get_integer_16; - OpCase(i_bs_get_integer_16_xfd): bs_get_integer_16_context = xb(Arg(0)); I++; - do_bs_get_integer_16: { ErlBinMatchBuffer *_mb; Eterm _result; @@ -4413,17 +4270,10 @@ do { \ { Eterm bs_get_integer_32_context; - OpCase(i_bs_get_integer_32_rfId): - bs_get_integer_32_context = r(0); - goto do_bs_get_integer_32; - - OpCase(i_bs_get_integer_32_xfId): bs_get_integer_32_context = xb(Arg(0)); I++; - - do_bs_get_integer_32: { ErlBinMatchBuffer *_mb; Uint32 _integer; @@ -4452,13 +4302,6 @@ do { \ } } - /* Operands: Size Live Fail Flags Dst */ - OpCase(i_bs_get_integer_imm_rIIfId): { - tmp_arg1 = r(0); - /* Operands: Size Live Fail Flags Dst */ - goto do_bs_get_integer_imm_test_heap; - } - /* Operands: x(Reg) Size Live Fail Flags Dst */ OpCase(i_bs_get_integer_imm_xIIfId): { tmp_arg1 = xb(Arg(0)); @@ -4481,15 +4324,6 @@ do { \ goto do_bs_get_integer_imm; } - /* Operands: Size Fail Flags Dst */ - OpCase(i_bs_get_integer_small_imm_rIfId): { - tmp_arg1 = r(0); - tmp_arg2 = Arg(0); - I++; - /* Operands: Fail Flags Dst */ - goto do_bs_get_integer_imm; - } - /* Operands: x(Reg) Size Fail Flags Dst */ OpCase(i_bs_get_integer_small_imm_xIfId): { tmp_arg1 = xb(Arg(0)); @@ -4563,11 +4397,6 @@ do { \ Eterm get_utf8_context; /* Operands: MatchContext Fail Dst */ - OpCase(i_bs_get_utf8_rfd): { - get_utf8_context = r(0); - goto do_bs_get_utf8; - } - OpCase(i_bs_get_utf8_xfd): { get_utf8_context = xb(Arg(0)); I++; @@ -4578,7 +4407,7 @@ do { \ * Operands: Fail Dst */ - do_bs_get_utf8: { + { Eterm result = erts_bs_get_utf8(ms_matchbuffer(get_utf8_context)); if (is_non_value(result)) { ClauseFail(); @@ -4591,12 +4420,7 @@ do { \ Eterm get_utf16_context; /* Operands: MatchContext Fail Flags Dst */ - OpCase(i_bs_get_utf16_rfId): { - get_utf16_context = r(0); - goto do_bs_get_utf16; - } - - OpCase(i_bs_get_utf16_xfId): { + OpCase(i_bs_get_utf16_xfId): { get_utf16_context = xb(Arg(0)); I++; } @@ -4605,7 +4429,7 @@ do { \ * get_utf16_context = match_context * Operands: Fail Flags Dst */ - do_bs_get_utf16: { + { Eterm result = erts_bs_get_utf16(ms_matchbuffer(get_utf16_context), Arg(1)); if (is_non_value(result)) { @@ -4624,26 +4448,10 @@ do { \ Uint orig; Uint hole_size; - OpCase(bs_context_to_binary_r): { - context_to_binary_context = r(0); - I -= 2; - goto do_context_to_binary; - } - - /* Unfortunately, inlining can generate this instruction. */ - OpCase(bs_context_to_binary_y): { - context_to_binary_context = yb(Arg(0)); - goto do_context_to_binary0; - } - - OpCase(bs_context_to_binary_x): { + OpCase(bs_context_to_binary_x): context_to_binary_context = xb(Arg(0)); - - do_context_to_binary0: I--; - } - do_context_to_binary: if (is_boxed(context_to_binary_context) && header_is_bin_matchstate(*boxed_val(context_to_binary_context))) { ErlBinMatchState* ms; @@ -4655,17 +4463,11 @@ do { \ } Next(2); - OpCase(i_bs_get_binary_all_reuse_rfI): { - context_to_binary_context = r(0); - goto do_bs_get_binary_all_reuse; - } - OpCase(i_bs_get_binary_all_reuse_xfI): { context_to_binary_context = xb(Arg(0)); I++; } - do_bs_get_binary_all_reuse: mb = ms_matchbuffer(context_to_binary_context); size = mb->size - mb->offset; if (size % Arg(1) != 0) { @@ -4693,16 +4495,11 @@ do { \ { Eterm match_string_context; - OpCase(i_bs_match_string_rfII): { - match_string_context = r(0); - goto do_bs_match_string; - } OpCase(i_bs_match_string_xfII): { match_string_context = xb(Arg(0)); I++; } - do_bs_match_string: { BeamInstr *next; byte* bytes; @@ -4730,14 +4527,6 @@ do { \ } } - OpCase(i_bs_save2_rI): { - BeamInstr *next; - ErlBinMatchState *_ms; - PreFetch(1, next); - _ms = (ErlBinMatchState*) boxed_val((Eterm) r(0)); - _ms->save_offset[Arg(0)] = _ms->mb.offset; - NextPF(1, next); - } OpCase(i_bs_save2_xI): { BeamInstr *next; ErlBinMatchState *_ms; @@ -4747,14 +4536,6 @@ do { \ NextPF(2, next); } - OpCase(i_bs_restore2_rI): { - BeamInstr *next; - ErlBinMatchState *_ms; - PreFetch(1, next); - _ms = (ErlBinMatchState*) boxed_val((Eterm) r(0)); - _ms->mb.offset = _ms->save_offset[Arg(0)]; - NextPF(1, next); - } OpCase(i_bs_restore2_xI): { BeamInstr *next; ErlBinMatchState *_ms; @@ -5030,7 +4811,9 @@ do { \ switch( c_p->def_arg_reg[3] ) { case HIPE_MODE_SWITCH_RES_RETURN: ASSERT(is_value(reg[0])); - MoveReturn(reg[0], r(0)); + SET_I(c_p->cp); + c_p->cp = 0; + Goto(*I); case HIPE_MODE_SWITCH_RES_CALL_EXPORTED: c_p->i = c_p->hipe.u.callee_exp->addressv[erts_active_code_ix()]; /*fall through*/ diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 3cd80c4ec2..3671baa552 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -1847,9 +1847,7 @@ load_code(LoaderState* stp) case TAG_o: break; case TAG_x: - if (last_op->a[arg].val == 0) { - last_op->a[arg].type = TAG_r; - } else if (last_op->a[arg].val >= MAX_REG) { + if (last_op->a[arg].val >= MAX_REG) { LoadError1(stp, "invalid x register number: %u", last_op->a[arg].val); } @@ -2055,7 +2053,42 @@ load_code(LoaderState* stp) if (((opc[specific].mask[0] & mask[0]) == mask[0]) && ((opc[specific].mask[1] & mask[1]) == mask[1]) && ((opc[specific].mask[2] & mask[2]) == mask[2])) { - break; + + if (!opc[specific].involves_r) { + break; /* No complications - match */ + } + + /* + * The specific operation uses the 'r' operand, + * which is shorthand for x(0). Now things + * get complicated. First we must check whether + * all operands that should be of type 'r' use + * x(0) (as opposed to some other X register). + */ + for (arg = 0; arg < arity; arg++) { + if (opc[specific].involves_r & (1 << arg) && + tmp_op->a[arg].type == TAG_x) { + if (tmp_op->a[arg].val != 0) { + break; /* Other X register than 0 */ + } + } + } + + if (arg == arity) { + /* + * All 'r' operands use x(0) in the generic + * operation. That means a match. Now we + * will need to rewrite the generic instruction + * to actually use 'r' instead of 'x(0)'. + */ + for (arg = 0; arg < arity; arg++) { + if (opc[specific].involves_r & (1 << arg) && + tmp_op->a[arg].type == TAG_x) { + tmp_op->a[arg].type = TAG_r; + } + } + break; /* Match */ + } } specific++; } @@ -2166,9 +2199,6 @@ load_code(LoaderState* stp) break; case 's': /* Any source (tagged constant or register) */ switch (tag) { - case TAG_r: - code[ci++] = make_loader_x_reg(0); - break; case TAG_x: code[ci++] = make_loader_x_reg(tmp_op->a[arg].val); break; @@ -2196,9 +2226,6 @@ load_code(LoaderState* stp) break; case 'd': /* Destination (x(0), x(N), y(N) */ switch (tag) { - case TAG_r: - code[ci++] = make_loader_x_reg(0); - break; case TAG_x: code[ci++] = make_loader_x_reg(tmp_op->a[arg].val); break; @@ -2360,7 +2387,6 @@ load_code(LoaderState* stp) stp->labels[tmp_op->a[arg].val].patches = ci; ci++; break; - case TAG_r: case TAG_x: CodeNeed(1); code[ci++] = make_loader_x_reg(tmp_op->a[arg].val); @@ -2455,7 +2481,6 @@ load_code(LoaderState* stp) stp->on_load = ci; break; case op_bs_put_string_II: - case op_i_bs_match_string_rfII: case op_i_bs_match_string_xfII: new_string_patch(stp, ci-1); break; @@ -2693,17 +2718,17 @@ gen_element(LoaderState* stp, GenOpArg Fail, GenOpArg Index, op->next = NULL; if (Index.type == TAG_i && Index.val > 0 && - (Tuple.type == TAG_r || Tuple.type == TAG_x || Tuple.type == TAG_y)) { + (Tuple.type == TAG_x || Tuple.type == TAG_y)) { op->op = genop_i_fast_element_4; - op->a[0] = Tuple; - op->a[1] = Fail; + op->a[0] = Fail; + op->a[1] = Tuple; op->a[2].type = TAG_u; op->a[2].val = Index.val; op->a[3] = Dst; } else { op->op = genop_i_element_4; - op->a[0] = Tuple; - op->a[1] = Fail; + op->a[0] = Fail; + op->a[1] = Tuple; op->a[2] = Index; op->a[3] = Dst; } @@ -4798,7 +4823,8 @@ transform_engine(LoaderState* st) if (var[i].type != instr->a[ap].type) goto restart; switch (var[i].type) { - case TAG_r: case TAG_n: break; + case TAG_n: + break; default: if (var[i].val != instr->a[ap].val) goto restart; @@ -5865,7 +5891,7 @@ make_stub(BeamInstr* fp, Eterm mod, Eterm func, Uint arity, Uint native, BeamIns fp[4] = arity; #ifdef HIPE if (native) { - fp[5] = BeamOpCode(op_move_return_nr); + fp[5] = BeamOpCode(op_move_return_n); hipe_mfa_save_orig_beam_op(mod, func, arity, fp+5); } #endif @@ -6301,7 +6327,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) #ifdef HIPE op = (Eterm) BeamOpCode(op_hipe_trap_call); /* Might be changed later. */ #else - op = (Eterm) BeamOpCode(op_move_return_nr); + op = (Eterm) BeamOpCode(op_move_return_n); #endif fp = make_stub(fp, Mod, func, arity, (Uint)native_address, op); } diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index cfc978b5a5..98f27a1725 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -141,6 +141,7 @@ typedef struct op_entry { char* name; /* Name of instruction. */ Uint32 mask[3]; /* Signature mask. */ + unsigned involves_r; /* Needs special attention when matching. */ int sz; /* Number of loaded words. */ char* pack; /* Instructions for packing engine. */ char* sign; /* Signature string. */ diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 4fa6a087e2..335f8e2db5 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -39,8 +39,8 @@ too_old_compiler | never() => # necessary.) Since the instructions don't work correctly in R12B, simply # refuse to load the module. -func_info M=a a==am_module_info A=u==0 | label L | move n r => too_old_compiler -func_info M=a a==am_module_info A=u==1 | label L | move n r => too_old_compiler +func_info M=a a==am_module_info A=u==0 | label L | move n x==0 => too_old_compiler +func_info M=a a==am_module_info A=u==1 | label L | move n x==0 => too_old_compiler # The undocumented and unsupported guard BIF is_constant/1 was removed # in R13. The is_constant/2 operation is marked as obsolete in genop.tab, @@ -77,14 +77,14 @@ return # there is no line/1 instruction between the move and the call. # -move S r | line Loc | call_ext Ar Func => \ - line Loc | move S r | call_ext Ar Func -move S r | line Loc | call_ext_last Ar Func=u$is_bif D => \ - line Loc | move S r | call_ext_last Ar Func D -move S r | line Loc | call_ext_only Ar Func=u$is_bif => \ - line Loc | move S r | call_ext_only Ar Func -move S r | line Loc | call Ar Func => \ - line Loc | move S r | call Ar Func +move S X0=x==0 | line Loc | call_ext Ar Func => \ + line Loc | move S X0 | call_ext Ar Func +move S X0=x==0 | line Loc | call_ext_last Ar Func=u$is_bif D => \ + line Loc | move S X0 | call_ext_last Ar Func D +move S X0=x==0 | line Loc | call_ext_only Ar Func=u$is_bif => \ + line Loc | move S X0 | call_ext_only Ar Func +move S X0=x==0 | line Loc | call Ar Func => \ + line Loc | move S X0 | call Ar Func # # A tail-recursive call to an external function (non-BIF) will @@ -167,31 +167,24 @@ is_tuple Fail=f S | select_tuple_arity S=d Fail=f Size=u Rest=* => \ select_tuple_arity S=d Fail=f Size=u Rest=* => \ gen_select_tuple_arity(S, Fail, Size, Rest) -i_select_val_bins r f I i_select_val_bins x f I i_select_val_bins y f I -i_select_val_lins r f I i_select_val_lins x f I i_select_val_lins y f I -i_select_val2 r f c c f f i_select_val2 x f c c f f i_select_val2 y f c c f f -i_select_tuple_arity r f I i_select_tuple_arity x f I i_select_tuple_arity y f I -i_select_tuple_arity2 r f A A f f i_select_tuple_arity2 x f A A f f i_select_tuple_arity2 y f A A f f -i_jump_on_val_zero r f I i_jump_on_val_zero x f I i_jump_on_val_zero y f I -i_jump_on_val r f I I i_jump_on_val x f I I i_jump_on_val y f I I @@ -203,30 +196,13 @@ is_ne_exact L1 S1 S2 | jump Fail | label L2 | same_label(L1, L2) => \ %macro: get_list GetList -pack get_list x x x get_list x x y -get_list x x r get_list x y x get_list x y y -get_list x y r -get_list x r x -get_list x r y get_list y x x get_list y x y -get_list y x r get_list y y x get_list y y y -get_list y y r -get_list y r x -get_list y r y - -get_list r x x -get_list r x y -get_list r x r -get_list r y x -get_list r y y -get_list r y r -get_list r r x -get_list r r y # Old-style catch. catch y f @@ -247,21 +223,15 @@ set_tuple_element s d P %macro: i_get_tuple_element GetTupleElement -pack i_get_tuple_element x P x -i_get_tuple_element r P x i_get_tuple_element y P x -i_get_tuple_element x P r -i_get_tuple_element y P r %cold -i_get_tuple_element r P r i_get_tuple_element x P y -i_get_tuple_element r P y i_get_tuple_element y P y %hot %macro: is_number IsNumber -fail_action %cold -is_number f r is_number f x is_number f y %hot @@ -271,16 +241,12 @@ is_number Fail Literal=q => move Literal x | is_number Fail x jump f -case_end Literal=c => move Literal x | case_end x -badmatch Literal=c => move Literal x | badmatch x +case_end NotInX=cy => move NotInX x | case_end x +badmatch NotInX=cy => move NotInX x | badmatch x -case_end r case_end x -case_end y -badmatch r badmatch x -badmatch y if_end raise s s @@ -289,7 +255,7 @@ raise s s badarg j system_limit j -move C=cxy r | jump Lbl => move_jump Lbl C +move C=cxy x==0 | jump Lbl => move_jump Lbl C %macro: move_jump MoveJump -nonext move_jump f n @@ -307,8 +273,6 @@ move_window/6 # x -> y -move S1=r S2=y | move X1=x Y1=y => move2 S1 S2 X1 Y1 - move X1=x Y1=y | move X2=x Y2=y | move X3=x Y3=y | succ(Y1,Y2) | succ(Y2,Y3) => \ move_window X1 X2 X3 Y1 Y3 @@ -329,12 +293,13 @@ move X1=x Y1=y | move X2=x Y2=y => move2 X1 Y1 X2 Y2 move Y1=y X1=x | move Y2=y X2=x => move2 Y1 X1 Y2 X2 move X1=x X2=x | move X3=x X4=x => move2 X1 X2 X3 X4 -move S1=x S2=r | move S3=x S4=x => move2 S1 S2 S3 S4 -move S1=x S2=r | move X1=x Y1=y => move2 S1 S2 X1 Y1 -move S1=y S2=r | move X1=x Y1=y => move2 S1 S2 X1 Y1 +move X1=x X2=x | move X3=x Y1=y => move2 X1 X2 X3 Y1 + +move S1=x S2=x | move X1=x Y1=y => move2 S1 S2 X1 Y1 +move S1=y S2=x | move X1=x Y1=y => move2 S1 S2 X1 Y1 -move Y1=y X1=x | move S1=r D1=x => move2 Y1 X1 S1 D1 -move S1=r D1=x | move Y1=y X1=x => move2 S1 D1 Y1 X1 +move Y1=y X1=x | move S1=x D1=x => move2 Y1 X1 S1 D1 +move S1=x D1=x | move Y1=y X1=x => move2 S1 D1 Y1 X1 move2 X1=x Y1=y X2=x Y2=y | move X3=x Y3=y => move3 X1 Y1 X2 Y2 X3 Y3 move2 Y1=y X1=x Y2=y X2=x | move Y3=y X3=x => move3 Y1 X1 Y2 X2 Y3 X3 @@ -351,14 +316,13 @@ move2 x y x y move2 y x y x move2 x x x x -move2 x r x x +move2 x x x y -move2 x r x y -move2 r y x y -move2 y r x y +move2 x y x y +move2 y x x y -move2 r x y x -move2 y x r x +move2 x x y x +move2 y x x x %macro: move3 Move3 move3 x y x y x y @@ -373,20 +337,14 @@ move S=c D=y => move S x | move x D %macro:move Move -pack -gen_dest move x x move x y -move x r move y x -move y r -move r x -move r y -move c r move c x move n x -move n r move y y # Receive operations. -loop_rec Fail Src | smp_mark_target_label(Fail) => i_loop_rec Fail Src +loop_rec Fail x==0 | smp_mark_target_label(Fail) => i_loop_rec Fail label L | wait_timeout Fail Src | smp_already_locked(L) => label L | i_wait_timeout_locked Fail Src wait_timeout Fail Src => i_wait_timeout Fail Src @@ -401,7 +359,7 @@ label L | timeout | smp_already_locked(L) => label L | timeout_locked remove_message timeout timeout_locked -i_loop_rec f r +i_loop_rec f loop_rec_end f wait f wait_locked f @@ -419,29 +377,25 @@ send # Optimized comparisons with one immediate/literal operand. # -is_eq_exact Lbl R=rxy C=ian => i_is_eq_exact_immed Lbl R C -is_eq_exact Lbl R=rxy C=q => i_is_eq_exact_literal R Lbl C +is_eq_exact Lbl R=xy C=ian => i_is_eq_exact_immed Lbl R C +is_eq_exact Lbl R=xy C=q => i_is_eq_exact_literal Lbl R C -is_ne_exact Lbl R=rxy C=ian => i_is_ne_exact_immed Lbl R C -is_ne_exact Lbl R=rxy C=q => i_is_ne_exact_literal R Lbl C +is_ne_exact Lbl R=xy C=ian => i_is_ne_exact_immed Lbl R C +is_ne_exact Lbl R=xy C=q => i_is_ne_exact_literal Lbl R C %macro: i_is_eq_exact_immed EqualImmed -fail_action -i_is_eq_exact_immed f r c i_is_eq_exact_immed f x c i_is_eq_exact_immed f y c -i_is_eq_exact_literal r f c -i_is_eq_exact_literal x f c -i_is_eq_exact_literal y f c +i_is_eq_exact_literal f x c +i_is_eq_exact_literal f y c %macro: i_is_ne_exact_immed NotEqualImmed -fail_action -i_is_ne_exact_immed f r c i_is_ne_exact_immed f x c i_is_ne_exact_immed f y c -i_is_ne_exact_literal r f c -i_is_ne_exact_literal x f c -i_is_ne_exact_literal y f c +i_is_ne_exact_literal f x c +i_is_ne_exact_literal f y c # # Common Compare Specializations @@ -449,31 +403,21 @@ i_is_ne_exact_literal y f c # to keep the instruction set small-ish # -is_eq_exact Lbl S1=xy S2=r => is_eq_exact Lbl S2 S1 -is_eq_exact Lbl S1=rx S2=xy => i_is_eq_exact_spec Lbl S1 S2 +is_eq_exact Lbl S1=y S2=x => is_eq_exact Lbl S2 S1 +is_eq_exact Lbl S1=x S2=xy => i_is_eq_exact_spec Lbl S1 S2 %macro: i_is_eq_exact_spec EqualExact -fail_action i_is_eq_exact_spec f x x i_is_eq_exact_spec f x y -i_is_eq_exact_spec f r x -i_is_eq_exact_spec f r y -%cold -i_is_eq_exact_spec f r r -%hot -is_lt Lbl S1=rxc S2=rxc => i_is_lt_spec Lbl S1 S2 +is_lt Lbl S1=xc S2=xc => i_is_lt_spec Lbl S1 S2 %macro: i_is_lt_spec IsLessThan -fail_action i_is_lt_spec f x x -i_is_lt_spec f x r i_is_lt_spec f x c -i_is_lt_spec f r x -i_is_lt_spec f r c i_is_lt_spec f c x -i_is_lt_spec f c r %cold -i_is_lt_spec f r r i_is_lt_spec f c c %hot @@ -523,7 +467,6 @@ i_put_tuple Dst Arity Puts=* | put S => \ i_put_tuple/2 %macro:i_put_tuple PutTuple -pack -goto:do_put_tuple -i_put_tuple r I i_put_tuple x I i_put_tuple y I @@ -540,48 +483,25 @@ put_list x n x put_list y n x put_list x x x put_list y x x -put_list x x r -put_list y r r put_list y y x put_list x y x -put_list r x x -put_list r y x -put_list r x r -put_list y y r -put_list y r x -put_list r n x - -put_list x r x -put_list x y r -put_list y x r -put_list y x x -put_list x r r +put_list y x x # put_list SrcReg Constant Dst -put_list r c r -put_list r c x -put_list r c y -put_list x c r put_list x c x put_list x c y -put_list y c r put_list y c x put_list y c y # put_list Constant SrcReg Dst -put_list c r r -put_list c r x -put_list c r y -put_list c x r put_list c x x put_list c x y -put_list c y r put_list c y x put_list c y y @@ -590,18 +510,12 @@ put_list s s d %hot %macro: i_fetch FetchArgs -pack -i_fetch c r i_fetch c x i_fetch c y -i_fetch r c -i_fetch r x -i_fetch r y i_fetch x c -i_fetch x r i_fetch x x i_fetch x y i_fetch y c -i_fetch y r i_fetch y x i_fetch y y @@ -629,27 +543,27 @@ return_trace # Note: There is no 'move_return y r', since there never are any y registers # when we do move_return (if we have y registers, we must do move_deallocate_return). -move S r | return => move_return S r +move S x==0 | return => move_return S %macro: move_return MoveReturn -nonext -move_return x r -move_return c r -move_return n r +move_return x +move_return c +move_return n -move S r | deallocate D | return => move_deallocate_return S r D +move S x==0 | deallocate D | return => move_deallocate_return S D %macro: move_deallocate_return MoveDeallocateReturn -pack -nonext -move_deallocate_return x r Q -move_deallocate_return y r Q -move_deallocate_return c r Q -move_deallocate_return n r Q +move_deallocate_return x Q +move_deallocate_return y Q +move_deallocate_return c Q +move_deallocate_return n Q deallocate D | return => deallocate_return D %macro: deallocate_return DeallocateReturn -nonext deallocate_return Q -test_heap Need u==1 | put_list Y=y r r => test_heap_1_put_list Need Y +test_heap Need u==1 | put_list Y=y x==0 x==0 => test_heap_1_put_list Need Y %macro: test_heap_1_put_list TestHeapPutList -pack test_heap_1_put_list I y @@ -658,18 +572,16 @@ test_heap_1_put_list I y is_tuple Fail Literal=q => move Literal x | is_tuple Fail x is_tuple Fail=f c => jump Fail -is_tuple Fail=f S=rxy | test_arity Fail=f S=rxy Arity => is_tuple_of_arity Fail S Arity +is_tuple Fail=f S=xy | test_arity Fail=f S=xy Arity => is_tuple_of_arity Fail S Arity %macro:is_tuple_of_arity IsTupleOfArity -fail_action is_tuple_of_arity f x A is_tuple_of_arity f y A -is_tuple_of_arity f r A %macro: is_tuple IsTuple -fail_action is_tuple f x is_tuple f y -is_tuple f r test_arity Fail Literal=q Arity => move Literal x | test_arity Fail x Arity test_arity Fail=f c Arity => jump Fail @@ -677,7 +589,6 @@ test_arity Fail=f c Arity => jump Fail %macro: test_arity IsArity -fail_action test_arity f x A test_arity f y A -test_arity f r A is_tuple_of_arity Fail=f Reg Arity | get_tuple_element Reg P=u==0 Dst=xy => \ is_tuple_of_arity Fail Reg Arity | extract_next_element Dst | original_reg Reg P @@ -726,46 +637,40 @@ is_integer Fail=f i => is_integer Fail=f an => jump Fail is_integer Fail Literal=q => move Literal x | is_integer Fail x -is_integer Fail=f S=rx | allocate Need Regs => is_integer_allocate Fail S Need Regs +is_integer Fail=f S=x | allocate Need Regs => is_integer_allocate Fail S Need Regs %macro: is_integer_allocate IsIntegerAllocate -fail_action is_integer_allocate f x I I -is_integer_allocate f r I I %macro: is_integer IsInteger -fail_action is_integer f x is_integer f y -is_integer f r is_list Fail=f n => is_list Fail Literal=q => move Literal x | is_list Fail x is_list Fail=f c => jump Fail %macro: is_list IsList -fail_action -is_list f r is_list f x %cold is_list f y %hot -is_nonempty_list Fail=f S=rx | allocate Need Rs => is_nonempty_list_allocate Fail S Need Rs +is_nonempty_list Fail=f S=x | allocate Need Rs => is_nonempty_list_allocate Fail S Need Rs %macro:is_nonempty_list_allocate IsNonemptyListAllocate -fail_action -pack is_nonempty_list_allocate f x I t -is_nonempty_list_allocate f r I t -is_nonempty_list F=f r | test_heap I1 I2 => is_non_empty_list_test_heap F r I1 I2 +is_nonempty_list F=f x==0 | test_heap I1 I2 => is_non_empty_list_test_heap F I1 I2 %macro: is_non_empty_list_test_heap IsNonemptyListTestHeap -fail_action -pack -is_non_empty_list_test_heap f r I t +is_non_empty_list_test_heap f I t %macro: is_nonempty_list IsNonemptyList -fail_action is_nonempty_list f x is_nonempty_list f y -is_nonempty_list f r %macro: is_atom IsAtom -fail_action is_atom f x -is_atom f r %cold is_atom f y %hot @@ -773,7 +678,6 @@ is_atom Fail=f a => is_atom Fail=f niq => jump Fail %macro: is_float IsFloat -fail_action -is_float f r is_float f x %cold is_float f y @@ -787,12 +691,10 @@ is_nil Fail=f qia => jump Fail %macro: is_nil IsNil -fail_action is_nil f x is_nil f y -is_nil f r is_binary Fail Literal=q => move Literal x | is_binary Fail x is_binary Fail=f c => jump Fail %macro: is_binary IsBinary -fail_action -is_binary f r is_binary f x %cold is_binary f y @@ -804,7 +706,6 @@ is_bitstr Fail Term => is_bitstring Fail Term is_bitstring Fail Literal=q => move Literal x | is_bitstring Fail x is_bitstring Fail=f c => jump Fail %macro: is_bitstring IsBitstring -fail_action -is_bitstring f r is_bitstring f x %cold is_bitstring f y @@ -812,7 +713,6 @@ is_bitstring f y is_reference Fail=f cq => jump Fail %macro: is_reference IsRef -fail_action -is_reference f r is_reference f x %cold is_reference f y @@ -820,7 +720,6 @@ is_reference f y is_pid Fail=f cq => jump Fail %macro: is_pid IsPid -fail_action -is_pid f r is_pid f x %cold is_pid f y @@ -828,7 +727,6 @@ is_pid f y is_port Fail=f cq => jump Fail %macro: is_port IsPort -fail_action -is_port f r is_port f x %cold is_port f y @@ -840,7 +738,6 @@ is_boolean Fail=f ac => jump Fail %cold %macro: is_boolean IsBoolean -fail_action -is_boolean f r is_boolean f x is_boolean f y %hot @@ -986,76 +883,76 @@ call_ext_only u==3 u$func:erlang:hibernate/3 => i_hibernate %unless USE_VM_PROBES call_ext Arity u$func:erlang:dt_get_tag/0 => \ - move a=am_undefined r + move a=am_undefined x=0 call_ext_last Arity u$func:erlang:dt_get_tag/0 D => \ - move a=am_undefined r | deallocate D | return + move a=am_undefined x=0 | deallocate D | return call_ext_only Arity u$func:erlang:dt_get_tag/0 => \ - move a=am_undefined r | return - -move Any r | call_ext Arity u$func:erlang:dt_put_tag/1 => \ - move a=am_undefined r -move Any r | call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \ - move a=am_undefined r | deallocate D | return -move Any r | call_ext_only Arity u$func:erlang:dt_put_tag/1 => \ - move a=am_undefined r | return + move a=am_undefined x=0 | return + +move Any x==0 | call_ext Arity u$func:erlang:dt_put_tag/1 => \ + move a=am_undefined x=0 +move Any x==0 | call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \ + move a=am_undefined x=0 | deallocate D | return +move Any x==0 | call_ext_only Arity u$func:erlang:dt_put_tag/1 => \ + move a=am_undefined x=0 | return call_ext Arity u$func:erlang:dt_put_tag/1 => \ - move a=am_undefined r + move a=am_undefined x=0 call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \ - move a=am_undefined r | deallocate D | return + move a=am_undefined x=0 | deallocate D | return call_ext_only Arity u$func:erlang:dt_put_tag/1 => \ - move a=am_undefined r | return + move a=am_undefined x=0 | return call_ext Arity u$func:erlang:dt_get_tag_data/0 => \ - move a=am_undefined r + move a=am_undefined x=0 call_ext_last Arity u$func:erlang:dt_get_tag_data/0 D => \ - move a=am_undefined r | deallocate D | return + move a=am_undefined x=0 | deallocate D | return call_ext_only Arity u$func:erlang:dt_get_tag_data/0 => \ - move a=am_undefined r | return - -move Any r | call_ext Arity u$func:erlang:dt_spread_tag/1 => \ - move a=am_true r -move Any r | call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \ - move a=am_true r | deallocate D | return -move Any r | call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \ - move a=am_true r | return + move a=am_undefined x=0 | return + +move Any x==0 | call_ext Arity u$func:erlang:dt_spread_tag/1 => \ + move a=am_true x=0 +move Any x==0 | call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \ + move a=am_true x=0 | deallocate D | return +move Any x==0 | call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \ + move a=am_true x=0 | return call_ext Arity u$func:erlang:dt_spread_tag/1 => \ - move a=am_true r + move a=am_true x=0 call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \ - move a=am_true r | deallocate D | return + move a=am_true x=0 | deallocate D | return call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \ - move a=am_true r | return - -move Any r | call_ext Arity u$func:erlang:dt_restore_tag/1 => \ - move a=am_true r -move Any r | call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \ - move a=am_true r | deallocate D | return -move Any r | call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \ - move a=am_true r | return + move a=am_true x=0 | return + +move Any x==0 | call_ext Arity u$func:erlang:dt_restore_tag/1 => \ + move a=am_true x=0 +move Any x==0 | call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \ + move a=am_true x=0 | deallocate D | return +move Any x==0 | call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \ + move a=am_true x=0 | return call_ext Arity u$func:erlang:dt_restore_tag/1 => \ - move a=am_true r + move a=am_true x=0 call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \ - move a=am_true r | deallocate D | return + move a=am_true x=0 | deallocate D | return call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \ - move a=am_true r | return - -move Any r | call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \ - move Any r -move Any r | call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \ - move Any r | deallocate D | return -move Any r | call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \ - move Any r | return + move a=am_true x=0 | return + +move Any x==0 | call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \ + move Any x=0 +move Any x==0 | call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \ + move Any x=0 | deallocate D | return +move Any x==0 | call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \ + move Any x=0 | return call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \ deallocate D | return call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \ return -move Any r | call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => \ - move Any r -move Any r | call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \ - move Any r | deallocate D | return -move Any r | call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \ - move Any r | return +move Any x==0 | call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => \ + move Any x=0 +move Any x==0 | call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \ + move Any x=0 | deallocate D | return +move Any x==0 | call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \ + move Any x=0 | return call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \ deallocate D | return @@ -1063,7 +960,7 @@ call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \ return # Can happen after one of the transformations above. -move Discarded r | move Something r => move Something r +move Discarded x==0 | move Something x==0 => move Something x=0 %endif @@ -1088,9 +985,9 @@ call_ext_only Ar=u Bif=u$is_bif => \ # with call instructions. # -move S=c r | call_ext Ar=u Func=u$is_not_bif => i_move_call_ext S r Func -move S=c r | call_ext_last Ar=u Func=u$is_not_bif D => i_move_call_ext_last Func D S r -move S=c r | call_ext_only Ar=u Func=u$is_not_bif => i_move_call_ext_only Func S r +move S=c x==0 | call_ext Ar=u Func=u$is_not_bif => i_move_call_ext S Func +move S=c x==0 | call_ext_last Ar=u Func=u$is_not_bif D => i_move_call_ext_last Func D S +move S=c x==0 | call_ext_only Ar=u Func=u$is_not_bif => i_move_call_ext_only Func S call_ext Ar Func => i_call_ext Func call_ext_last Ar Func D => i_call_ext_last Func D @@ -1117,7 +1014,7 @@ bif0 u$bif:erlang:node/0 Dst=d => node Dst bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => i_get Src Dst -bif2 Jump=j u$bif:erlang:element/2 S1=s S2=rxy Dst=d => gen_element(Jump, S1, S2, Dst) +bif2 Jump=j u$bif:erlang:element/2 S1=s S2=xy Dst=d => gen_element(Jump, S1, S2, Dst) bif1 p Bif S1 Dst => bif1_body Bif S1 Dst @@ -1127,24 +1024,20 @@ bif2 Fail Bif S1 S2 Dst => i_fetch S1 S2 | i_bif2 Fail Bif Dst i_get s d %macro: self Self -self r self x self y %macro: node Node -node r node x %cold node y %hot -i_fast_element r j I d -i_fast_element x j I d -i_fast_element y j I d +i_fast_element j x I d +i_fast_element j y I d -i_element r j s d -i_element x j s d -i_element y j s d +i_element j x s d +i_element j y s d bif1 f b s d bif1_body b s d @@ -1155,37 +1048,37 @@ i_bif2_body b d # Internal calls. # -move S=c r | call Ar P=f => i_move_call S r P -move S=s r | call Ar P=f => move_call S r P +move S=c x==0 | call Ar P=f => i_move_call S P +move S=s x==0 | call Ar P=f => move_call S P -i_move_call c r f +i_move_call c f %macro:move_call MoveCall -arg_f -size -nonext -move_call/3 +move_call/2 -move_call x r f -move_call y r f +move_call x f +move_call y f -move S=c r | call_last Ar P=f D => i_move_call_last P D S r -move S r | call_last Ar P=f D => move_call_last S r P D +move S=c x==0 | call_last Ar P=f D => i_move_call_last P D S +move S x==0 | call_last Ar P=f D => move_call_last S P D -i_move_call_last f P c r +i_move_call_last f P c %macro:move_call_last MoveCallLast -arg_f -nonext -pack -move_call_last/4 -move_call_last x r f Q -move_call_last y r f Q +move_call_last/3 +move_call_last x f Q +move_call_last y f Q -move S=c r | call_only Ar P=f => i_move_call_only P S r -move S=x r | call_only Ar P=f => move_call_only S r P +move S=c x==0 | call_only Ar P=f => i_move_call_only P S +move S=x x==0 | call_only Ar P=f => move_call_only S P -i_move_call_only f c r +i_move_call_only f c %macro:move_call_only MoveCallOnly -arg_f -nonext -move_call_only/3 +move_call_only/2 -move_call_only x r f +move_call_only x f call Ar Func => i_call Func call_last Ar Func D => i_call_last Func D @@ -1199,9 +1092,9 @@ i_call_ext e i_call_ext_last e P i_call_ext_only e -i_move_call_ext c r e -i_move_call_ext_last e P c r -i_move_call_ext_only e c r +i_move_call_ext c e +i_move_call_ext_last e P c +i_move_call_ext_only e c # Fun calls. @@ -1221,7 +1114,6 @@ i_make_fun I t %macro: is_function IsFunction -fail_action is_function f x is_function f y -is_function f r is_function Fail=f c => jump Fail func_info M F A => i_func_info u M F A @@ -1233,126 +1125,102 @@ func_info M F A => i_func_info u M F A %cold bs_start_match2 Fail=f ica X Y D => jump Fail bs_start_match2 Fail Bin X Y D => i_bs_start_match2 Bin Fail X Y D -i_bs_start_match2 r f I I d i_bs_start_match2 x f I I d i_bs_start_match2 y f I I d bs_save2 Reg Index => gen_bs_save(Reg, Index) -i_bs_save2 r I i_bs_save2 x I bs_restore2 Reg Index => gen_bs_restore(Reg, Index) -i_bs_restore2 r I i_bs_restore2 x I # Matching integers bs_match_string Fail Ms Bits Val => i_bs_match_string Ms Fail Bits Val -i_bs_match_string r f I I i_bs_match_string x f I I # Fetching integers from binaries. -bs_get_integer2 Fail=f Ms=rx Live=u Sz=sq Unit=u Flags=u Dst=d => \ +bs_get_integer2 Fail=f Ms=x Live=u Sz=sq Unit=u Flags=u Dst=d => \ gen_get_integer2(Fail, Ms, Live, Sz, Unit, Flags, Dst) -i_bs_get_integer_small_imm r I f I d i_bs_get_integer_small_imm x I f I d -i_bs_get_integer_imm r I I f I d i_bs_get_integer_imm x I I f I d i_bs_get_integer f I I d -i_bs_get_integer_8 r f d i_bs_get_integer_8 x f d -i_bs_get_integer_16 r f d i_bs_get_integer_16 x f d -i_bs_get_integer_32 r f I d i_bs_get_integer_32 x f I d # Fetching binaries from binaries. -bs_get_binary2 Fail=f Ms=rx Live=u Sz=sq Unit=u Flags=u Dst=d => \ +bs_get_binary2 Fail=f Ms=x Live=u Sz=sq Unit=u Flags=u Dst=d => \ gen_get_binary2(Fail, Ms, Live, Sz, Unit, Flags, Dst) %macro: i_bs_get_binary_imm2 BsGetBinaryImm_2 -fail_action -gen_dest %macro: i_bs_get_binary2 BsGetBinary_2 -fail_action -gen_dest %macro: i_bs_get_binary_all2 BsGetBinaryAll_2 -fail_action -gen_dest -i_bs_get_binary_imm2 f r I I I d i_bs_get_binary_imm2 f x I I I d -i_bs_get_binary2 f r I s I d i_bs_get_binary2 f x I s I d -i_bs_get_binary_all2 f r I I d i_bs_get_binary_all2 f x I I d -i_bs_get_binary_all_reuse r f I i_bs_get_binary_all_reuse x f I # Fetching float from binaries. -bs_get_float2 Fail=f Ms=rx Live=u Sz=s Unit=u Flags=u Dst=d => \ +bs_get_float2 Fail=f Ms=x Live=u Sz=s Unit=u Flags=u Dst=d => \ gen_get_float2(Fail, Ms, Live, Sz, Unit, Flags, Dst) -bs_get_float2 Fail=f Ms=rx Live=u Sz=q Unit=u Flags=u Dst=d => jump Fail +bs_get_float2 Fail=f Ms=x Live=u Sz=q Unit=u Flags=u Dst=d => jump Fail %macro: i_bs_get_float2 BsGetFloat2 -fail_action -gen_dest -i_bs_get_float2 f r I s I d i_bs_get_float2 f x I s I d # Miscellanous -bs_skip_bits2 Fail=f Ms=rx Sz=s Unit=u Flags=u => \ - gen_skip_bits2(Fail, Ms, Sz, Unit, Flags) -bs_skip_bits2 Fail=f Ms=rx Sz=q Unit=u Flags=u => \ +bs_skip_bits2 Fail=f Ms=x Sz=sq Unit=u Flags=u => \ gen_skip_bits2(Fail, Ms, Sz, Unit, Flags) %macro: i_bs_skip_bits_imm2 BsSkipBitsImm2 -fail_action -i_bs_skip_bits_imm2 f r I i_bs_skip_bits_imm2 f x I %macro: i_bs_skip_bits2 BsSkipBits2 -fail_action -i_bs_skip_bits2 f r x I -i_bs_skip_bits2 f r y I i_bs_skip_bits2 f x x I -i_bs_skip_bits2 f x r I i_bs_skip_bits2 f x y I %macro: i_bs_skip_bits_all2 BsSkipBitsAll2 -fail_action -i_bs_skip_bits_all2 f r I i_bs_skip_bits_all2 f x I -bs_test_tail2 Fail=f Ms=rx Bits=u==0 => bs_test_zero_tail2 Fail Ms -bs_test_tail2 Fail=f Ms=rx Bits=u => bs_test_tail_imm2 Fail Ms Bits -bs_test_zero_tail2 f r +bs_test_tail2 Fail=f Ms=x Bits=u==0 => bs_test_zero_tail2 Fail Ms +bs_test_tail2 Fail=f Ms=x Bits=u => bs_test_tail_imm2 Fail Ms Bits bs_test_zero_tail2 f x -bs_test_tail_imm2 f r I bs_test_tail_imm2 f x I bs_test_unit F Ms Unit=u==8 => bs_test_unit8 F Ms -bs_test_unit f r I bs_test_unit f x I -bs_test_unit8 f r bs_test_unit8 f x -bs_context_to_binary r +# An y register operand for bs_context_to_binary is rare, +# but can happen because of inlining. + +bs_context_to_binary Y=y => move Y x | bs_context_to_binary x + bs_context_to_binary x -bs_context_to_binary y # # Utf8/utf16/utf32 support. (R12B-5) # -bs_get_utf8 Fail=f Ms=rx u u Dst=d => i_bs_get_utf8 Ms Fail Dst -i_bs_get_utf8 r f d +bs_get_utf8 Fail=f Ms=x u u Dst=d => i_bs_get_utf8 Ms Fail Dst i_bs_get_utf8 x f d -bs_skip_utf8 Fail=f Ms=rx u u => i_bs_get_utf8 Ms Fail x +bs_skip_utf8 Fail=f Ms=x u u => i_bs_get_utf8 Ms Fail x -bs_get_utf16 Fail=f Ms=rx u Flags=u Dst=d => i_bs_get_utf16 Ms Fail Flags Dst -bs_skip_utf16 Fail=f Ms=rx u Flags=u => i_bs_get_utf16 Ms Fail Flags x +bs_get_utf16 Fail=f Ms=x u Flags=u Dst=d => i_bs_get_utf16 Ms Fail Flags Dst +bs_skip_utf16 Fail=f Ms=x u Flags=u => i_bs_get_utf16 Ms Fail Flags x -i_bs_get_utf16 r f I d i_bs_get_utf16 x f I d -bs_get_utf32 Fail=f Ms=rx Live=u Flags=u Dst=d => \ +bs_get_utf32 Fail=f Ms=x Live=u Flags=u Dst=d => \ bs_get_integer2 Fail Ms Live i=32 u=1 Flags Dst | \ i_fetch Dst Ms | \ i_bs_validate_unicode_retract Fail -bs_skip_utf32 Fail=f Ms=rx Live=u Flags=u => \ +bs_skip_utf32 Fail=f Ms=x Live=u Flags=u => \ bs_get_integer2 Fail Ms Live i=32 u=1 Flags x | \ i_fetch x Ms | \ i_bs_validate_unicode_retract Fail @@ -1379,9 +1247,8 @@ bs_init2 Fail Sz=u Words Regs Flags Dst => \ bs_init2 Fail Sz Words=u==0 Regs Flags Dst => \ i_bs_init_fail Sz Fail Regs Dst bs_init2 Fail Sz Words Regs Flags Dst => \ - i_fetch Sz r | i_bs_init_fail_heap Words Fail Regs Dst + i_fetch Sz x=0 | i_bs_init_fail_heap Words Fail Regs Dst -i_bs_init_fail r j I d i_bs_init_fail x j I d i_bs_init_fail y j I d @@ -1402,9 +1269,8 @@ bs_init_bits Fail Sz=u Words Regs Flags Dst => i_bs_init_bits_heap Sz Words Reg bs_init_bits Fail Sz Words=u==0 Regs Flags Dst => \ i_bs_init_bits_fail Sz Fail Regs Dst bs_init_bits Fail Sz Words Regs Flags Dst => \ - i_fetch Sz r | i_bs_init_bits_fail_heap Words Fail Regs Dst + i_fetch Sz x=0 | i_bs_init_bits_fail_heap Words Fail Regs Dst -i_bs_init_bits_fail r j I d i_bs_init_bits_fail x j I d i_bs_init_bits_fail y j I d @@ -1577,7 +1443,6 @@ is_map Fail Lit=q | literal_is_map(Lit) => is_map Fail cq => jump Fail %macro: is_map IsMap -fail_action -is_map f r is_map f x is_map f y @@ -1588,35 +1453,25 @@ has_map_fields Fail Src Size Rest=* => \ ## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 } -get_map_elements Fail Src=rxy Size=u==2 Rest=* => \ +get_map_elements Fail Src=xy Size=u==2 Rest=* => \ gen_get_map_element(Fail, Src, Size, Rest) get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \ gen_get_map_elements(Fail, Src, Size, Rest) i_get_map_elements f s I -i_get_map_element Fail Src=rxy Key=ry Dst => \ +i_get_map_element Fail Src=xy Key=y Dst => \ move Key x | i_get_map_element Fail Src x Dst %macro: i_get_map_element_hash GetMapElementHash -fail_action -i_get_map_element_hash f r c I r -i_get_map_element_hash f x c I r -i_get_map_element_hash f y c I r -i_get_map_element_hash f r c I x i_get_map_element_hash f x c I x i_get_map_element_hash f y c I x -i_get_map_element_hash f r c I y i_get_map_element_hash f x c I y i_get_map_element_hash f y c I y %macro: i_get_map_element GetMapElement -fail_action -i_get_map_element f r x r -i_get_map_element f x x r -i_get_map_element f y x r -i_get_map_element f r x x i_get_map_element f x x x i_get_map_element f y x x -i_get_map_element f r x y i_get_map_element f x x y i_get_map_element f y x y @@ -1625,9 +1480,9 @@ i_get_map_element f y x y # the i_increment/4 instruction (in bodies, not in guards). # -gc_bif2 p Live u$bif:erlang:splus/2 Int=i Reg=d Dst => \ +gc_bif2 p Live u$bif:erlang:splus/2 Int=i Reg=x Dst => \ gen_increment(Reg, Int, Live, Dst) -gc_bif2 p Live u$bif:erlang:splus/2 Reg=d Int=i Dst => \ +gc_bif2 p Live u$bif:erlang:splus/2 Reg=x Int=i Dst => \ gen_increment(Reg, Int, Live, Dst) gc_bif2 p Live u$bif:erlang:sminus/2 Reg=d Int=i Dst | \ @@ -1662,7 +1517,6 @@ gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst gc_bif1 Fail I u$bif:erlang:sminus/1 Src Dst=d => i_fetch i Src | i_minus Fail I Dst gc_bif1 Fail I u$bif:erlang:splus/1 Src Dst=d => i_fetch i Src | i_plus Fail I Dst -i_increment r I I d i_increment x I I d i_increment y I I d diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 00b0df5b73..e61096355c 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -176,7 +176,7 @@ sub define_type_bit { } # Composed types. - define_type_bit('d', $type_bit{'x'} | $type_bit{'y'} | $type_bit{'r'}); + define_type_bit('d', $type_bit{'x'} | $type_bit{'y'}); define_type_bit('c', $type_bit{'i'} | $type_bit{'a'} | $type_bit{'n'} | $type_bit{'q'}); define_type_bit('s', $type_bit{'d'} | $type_bit{'i'} | @@ -528,12 +528,16 @@ sub emulator_output { my(@bits) = (0) x ($max_spec_operands/2); my($i); + my $involves_r = 0; for ($i = 0; $i < $max_spec_operands && defined $args[$i]; $i++) { my $t = $args[$i]; - if (defined $type_bit{$t}) { - my $shift = $max_genop_types * ($i % 2); - $bits[int($i/2)] |= $type_bit{$t} << $shift; + my $bits = $type_bit{$t}; + if ($t eq 'r') { + $bits |= $type_bit{'x'}; + $involves_r |= 1 << $i; } + my $shift = $max_genop_types * ($i % 2); + $bits[int($i/2)] |= $bits << $shift; } printf "/* %3d */ ", $spec_opnum; @@ -545,7 +549,7 @@ sub emulator_output { $sep = ","; } $init .= "}"; - &init_item($print_name, $init, $size, $pack, $sign, 0); + init_item($print_name, $init, $involves_r, $size, $pack, $sign, 0); $op_to_name[$spec_opnum] = $instr; $spec_opnum++; } @@ -1310,6 +1314,8 @@ sub tr_parse_op { foreach (split('', $type)) { &error("bad type in $op") unless defined $type_bit{$_} or $type eq '*'; + $_ eq 'r' and + error("$op: 'r' is not allowed in transformations") } } -- cgit v1.2.3 From c76b297463feee133adad510128d312536150392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 16 Jun 2015 10:06:20 +0200 Subject: Eliminate the use of i_fetch in arithmetic instructions The i_fetch instruction fetches two operands and places them in the tmp_arg1 and tmp_arg2 variables. The next instruction (such as i_plus) does not have to handle different types of operands, but can get get them simply from the tmp_arg* variables. Thus, i_fetch was introduced as a way to temper a potentail combinatorial explosion. Unfortunately, clang will generate terrible code because of the tmp_arg1 and tmp_arg2 variables being live across multiple instructions. Note that Clang has no way to predict the control flow from one instruction to another. Clang must assume that any instruction can jump to any other instruction. Somehow GCC manages to cope with this situation much better. Therefore, to improve the quality of the code generated by clang, we must eliminate all uses of the tmp_arg1 and tmp_arg2 variables. This commit eliminates the use of i_fetch in combination with the arithmetic and logical instructions. While we are touching the code for the bsr and bsl instructions, also move the tmp_big[] array from top scope of process main into the block that encloses the bsr and bsl instructions. --- erts/emulator/beam/beam_emu.c | 319 ++++++++++++++++++------------------------ erts/emulator/beam/ops.tab | 93 +++++++----- 2 files changed, 200 insertions(+), 212 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index e5179830de..82cc030878 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1145,7 +1145,6 @@ void process_main(void) */ register Eterm tmp_arg1 REG_tmp_arg1 = NIL; register Eterm tmp_arg2 REG_tmp_arg2 = NIL; - Eterm tmp_big[2]; /* * X registers and floating point registers are located in @@ -1158,8 +1157,6 @@ void process_main(void) */ int neg_o_reds = 0; - Eterm (*arith_func)(Process* p, Eterm* reg, Uint live); - #ifdef ERTS_OPCODE_COUNTER_SUPPORT static void* counting_opcodes[] = { DEFINE_COUNTING_OPCODES }; #else @@ -1307,9 +1304,6 @@ void process_main(void) #endif #include "beam_hot.h" -#define STORE_ARITH_RESULT(res) StoreBifResult(2, (res)); -#define ARITH_FUNC(name) erts_gc_##name - { Eterm increment_reg_val; Eterm increment_val; @@ -1349,81 +1343,71 @@ void process_main(void) goto find_func_info; } -#define DO_BIG_ARITH(Func,Arg1,Arg2) \ - do { \ - Uint live = Arg(1); \ - SWAPOUT; \ - reg[live] = (Arg1); \ - reg[live+1] = (Arg2); \ - result = (Func)(c_p, reg, live); \ - SWAPIN; \ - ERTS_HOLE_CHECK(c_p); \ - if (is_value(result)) { \ - StoreBifResult(4,result); \ - } \ - goto lb_Cl_error; \ - } while(0) +#define DO_OUTLINED_ARITH_2(name, Op1, Op2) \ + do { \ + Eterm result; \ + Uint live = Arg(1); \ + \ + SWAPOUT; \ + reg[live] = Op1; \ + reg[live+1] = Op2; \ + result = erts_gc_##name(c_p, reg, live); \ + SWAPIN; \ + ERTS_HOLE_CHECK(c_p); \ + if (is_value(result)) { \ + StoreBifResult(4, result); \ + } \ + goto lb_Cl_error; \ + } while (0) - OpCase(i_plus_jIxxd): { + Eterm PlusOp1, PlusOp2; Eterm result; - if (is_both_small(xb(Arg(2)), xb(Arg(3)))) { - Sint i = signed_val(xb(Arg(2))) + signed_val(xb(Arg(3))); - ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i)); - if (MY_IS_SSMALL(i)) { - result = make_small(i); - StoreBifResult(4, result); - } - } - DO_BIG_ARITH(ARITH_FUNC(mixed_plus), xb(Arg(2)), xb(Arg(3))); - } + OpCase(i_plus_jIxxd): + PlusOp1 = xb(Arg(2)); + PlusOp2 = xb(Arg(3)); + goto do_plus; - OpCase(i_plus_jId): - { - Eterm result; + OpCase(i_plus_jIssd): + GetArg2(2, PlusOp1, PlusOp2); + goto do_plus; - if (is_both_small(tmp_arg1, tmp_arg2)) { - Sint i = signed_val(tmp_arg1) + signed_val(tmp_arg2); + do_plus: + if (is_both_small(PlusOp1, PlusOp2)) { + Sint i = signed_val(PlusOp1) + signed_val(PlusOp2); ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i)); if (MY_IS_SSMALL(i)) { result = make_small(i); - STORE_ARITH_RESULT(result); + StoreBifResult(4, result); } } - arith_func = ARITH_FUNC(mixed_plus); - goto do_big_arith2; + DO_OUTLINED_ARITH_2(mixed_plus, PlusOp1, PlusOp2); } - OpCase(i_minus_jIxxd): { + Eterm MinusOp1, MinusOp2; Eterm result; - if (is_both_small(xb(Arg(2)), xb(Arg(3)))) { - Sint i = signed_val(xb(Arg(2))) - signed_val(xb(Arg(3))); - ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i)); - if (MY_IS_SSMALL(i)) { - result = make_small(i); - StoreBifResult(4, result); - } - } - DO_BIG_ARITH(ARITH_FUNC(mixed_minus), xb(Arg(2)), xb(Arg(3))); - } + OpCase(i_minus_jIxxd): + MinusOp1 = xb(Arg(2)); + MinusOp2 = xb(Arg(3)); + goto do_minus; - OpCase(i_minus_jId): - { - Eterm result; + OpCase(i_minus_jIssd): + GetArg2(2, MinusOp1, MinusOp2); + goto do_minus; - if (is_both_small(tmp_arg1, tmp_arg2)) { - Sint i = signed_val(tmp_arg1) - signed_val(tmp_arg2); + do_minus: + if (is_both_small(MinusOp1, MinusOp2)) { + Sint i = signed_val(MinusOp1) - signed_val(MinusOp2); ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i)); if (MY_IS_SSMALL(i)) { result = make_small(i); - STORE_ARITH_RESULT(result); + StoreBifResult(4, result); } } - arith_func = ARITH_FUNC(mixed_minus); - goto do_big_arith2; + DO_OUTLINED_ARITH_2(mixed_minus, MinusOp1, MinusOp2); } OpCase(i_is_lt_f): @@ -2767,109 +2751,81 @@ do { \ * Arithmetic operations. */ - OpCase(i_times_jId): + OpCase(i_times_jIssd): { - arith_func = ARITH_FUNC(mixed_times); - goto do_big_arith2; + Eterm Op1, Op2; + GetArg2(2, Op1, Op2); + DO_OUTLINED_ARITH_2(mixed_times, Op1, Op2); } - OpCase(i_m_div_jId): + OpCase(i_m_div_jIssd): { - arith_func = ARITH_FUNC(mixed_div); - goto do_big_arith2; + Eterm Op1, Op2; + GetArg2(2, Op1, Op2); + DO_OUTLINED_ARITH_2(mixed_div, Op1, Op2); } - OpCase(i_int_div_jId): + OpCase(i_int_div_jIssd): { - Eterm result; + Eterm Op1, Op2; - if (tmp_arg2 == SMALL_ZERO) { + GetArg2(2, Op1, Op2); + if (Op2 == SMALL_ZERO) { goto badarith; - } else if (is_both_small(tmp_arg1, tmp_arg2)) { - Sint ires = signed_val(tmp_arg1) / signed_val(tmp_arg2); + } else if (is_both_small(Op1, Op2)) { + Sint ires = signed_val(Op1) / signed_val(Op2); if (MY_IS_SSMALL(ires)) { - result = make_small(ires); - STORE_ARITH_RESULT(result); + Eterm result = make_small(ires); + StoreBifResult(4, result); } } - arith_func = ARITH_FUNC(int_div); - goto do_big_arith2; + DO_OUTLINED_ARITH_2(int_div, Op1, Op2); } - OpCase(i_rem_jIxxd): { - Eterm result; + Eterm RemOp1, RemOp2; - if (xb(Arg(3)) == SMALL_ZERO) { - goto badarith; - } else if (is_both_small(xb(Arg(2)), xb(Arg(3)))) { - result = make_small(signed_val(xb(Arg(2))) % signed_val(xb(Arg(3)))); + OpCase(i_rem_jIxxd): + RemOp1 = xb(Arg(2)); + RemOp2 = xb(Arg(3)); + goto do_rem; + + OpCase(i_rem_jIssd): + GetArg2(2, RemOp1, RemOp2); + goto do_rem; + + do_rem: + if (RemOp2 == SMALL_ZERO) { + goto badarith; + } else if (is_both_small(RemOp1, RemOp2)) { + Eterm result = make_small(signed_val(RemOp1) % signed_val(RemOp2)); StoreBifResult(4, result); + } else { + DO_OUTLINED_ARITH_2(int_rem, RemOp1, RemOp2); } - DO_BIG_ARITH(ARITH_FUNC(int_rem),xb(Arg(2)),xb(Arg(3))); } - OpCase(i_rem_jId): { - Eterm result; - - if (tmp_arg2 == SMALL_ZERO) { - goto badarith; - } else if (is_both_small(tmp_arg1, tmp_arg2)) { - result = make_small(signed_val(tmp_arg1) % signed_val(tmp_arg2)); - STORE_ARITH_RESULT(result); - } else { - arith_func = ARITH_FUNC(int_rem); - goto do_big_arith2; - } - } + Eterm BandOp1, BandOp2; OpCase(i_band_jIxcd): - { - Eterm result; + BandOp1 = xb(Arg(2)); + BandOp2 = Arg(3); + goto do_band; + + OpCase(i_band_jIssd): + GetArg2(2, BandOp1, BandOp2); + goto do_band; - if (is_both_small(xb(Arg(2)), Arg(3))) { + do_band: + if (is_both_small(BandOp1, BandOp2)) { /* * No need to untag -- TAG & TAG == TAG. */ - result = xb(Arg(2)) & Arg(3); + Eterm result = BandOp1 & BandOp2; StoreBifResult(4, result); } - DO_BIG_ARITH(ARITH_FUNC(band),xb(Arg(2)),Arg(3)); - } - - OpCase(i_band_jId): - { - Eterm result; - - if (is_both_small(tmp_arg1, tmp_arg2)) { - /* - * No need to untag -- TAG & TAG == TAG. - */ - result = tmp_arg1 & tmp_arg2; - STORE_ARITH_RESULT(result); - } - arith_func = ARITH_FUNC(band); - goto do_big_arith2; - } - -#undef DO_BIG_ARITH - - do_big_arith2: - { - Eterm result; - Uint live = Arg(1); - - SWAPOUT; - reg[live] = tmp_arg1; - reg[live+1] = tmp_arg2; - result = arith_func(c_p, reg, live); - SWAPIN; - ERTS_HOLE_CHECK(c_p); - if (is_value(result)) { - STORE_ARITH_RESULT(result); - } - goto lb_Cl_error; + DO_OUTLINED_ARITH_2(band, BandOp1, BandOp2); } /* @@ -2890,97 +2846,102 @@ do { \ goto find_func_info; } - OpCase(i_bor_jId): + OpCase(i_bor_jIssd): { - Eterm result; + Eterm Op1, Op2; - if (is_both_small(tmp_arg1, tmp_arg2)) { + GetArg2(2, Op1, Op2); + if (is_both_small(Op1, Op2)) { /* * No need to untag -- TAG | TAG == TAG. */ - result = tmp_arg1 | tmp_arg2; - STORE_ARITH_RESULT(result); + Eterm result = Op1 | Op2; + StoreBifResult(4, result); } - arith_func = ARITH_FUNC(bor); - goto do_big_arith2; + DO_OUTLINED_ARITH_2(bor, Op1, Op2); } - OpCase(i_bxor_jId): + OpCase(i_bxor_jIssd): { - Eterm result; + Eterm Op1, Op2; - if (is_both_small(tmp_arg1, tmp_arg2)) { + GetArg2(2, Op1, Op2); + if (is_both_small(Op1, Op2)) { /* * We could extract the tag from one argument, but a tag extraction * could mean a shift. Therefore, play it safe here. */ - result = make_small(signed_val(tmp_arg1) ^ signed_val(tmp_arg2)); - STORE_ARITH_RESULT(result); + Eterm result = make_small(signed_val(Op1) ^ signed_val(Op2)); + StoreBifResult(4, result); } - arith_func = ARITH_FUNC(bxor); - goto do_big_arith2; + DO_OUTLINED_ARITH_2(bxor, Op1, Op2); } { + Eterm Op1, Op2; Sint i; Sint ires; Eterm* bigp; + Eterm tmp_big[2]; - OpCase(i_bsr_jId): - if (is_small(tmp_arg2)) { - i = -signed_val(tmp_arg2); - if (is_small(tmp_arg1)) { + OpCase(i_bsr_jIssd): + GetArg2(2, Op1, Op2); + if (is_small(Op2)) { + i = -signed_val(Op2); + if (is_small(Op1)) { goto small_shift; - } else if (is_big(tmp_arg1)) { + } else if (is_big(Op1)) { if (i == 0) { - StoreBifResult(2, tmp_arg1); + StoreBifResult(4, Op1); } goto big_shift; } - } else if (is_big(tmp_arg2)) { + } else if (is_big(Op2)) { /* * N bsr NegativeBigNum == N bsl MAX_SMALL * N bsr PositiveBigNum == N bsl MIN_SMALL */ - tmp_arg2 = make_small(bignum_header_is_neg(*big_val(tmp_arg2)) ? + Op2 = make_small(bignum_header_is_neg(*big_val(Op2)) ? MAX_SMALL : MIN_SMALL); goto do_bsl; } goto badarith; - OpCase(i_bsl_jId): + OpCase(i_bsl_jIssd): + GetArg2(2, Op1, Op2); + do_bsl: - if (is_small(tmp_arg2)) { - i = signed_val(tmp_arg2); + if (is_small(Op2)) { + i = signed_val(Op2); - if (is_small(tmp_arg1)) { + if (is_small(Op1)) { small_shift: - ires = signed_val(tmp_arg1); + ires = signed_val(Op1); if (i == 0 || ires == 0) { - StoreBifResult(2, tmp_arg1); + StoreBifResult(4, Op1); } else if (i < 0) { /* Right shift */ i = -i; if (i >= SMALL_BITS-1) { - tmp_arg1 = (ires < 0) ? SMALL_MINUS_ONE : SMALL_ZERO; + Op1 = (ires < 0) ? SMALL_MINUS_ONE : SMALL_ZERO; } else { - tmp_arg1 = make_small(ires >> i); + Op1 = make_small(ires >> i); } - StoreBifResult(2, tmp_arg1); + StoreBifResult(4, Op1); } else if (i < SMALL_BITS-1) { /* Left shift */ if ((ires > 0 && ((~(Uint)0 << ((SMALL_BITS-1)-i)) & ires) == 0) || ((~(Uint)0 << ((SMALL_BITS-1)-i)) & ~ires) == 0) { - tmp_arg1 = make_small(ires << i); - StoreBifResult(2, tmp_arg1); + Op1 = make_small(ires << i); + StoreBifResult(4, Op1); } } - tmp_arg1 = small_to_big(ires, tmp_big); + Op1 = small_to_big(ires, tmp_big); big_shift: if (i > 0) { /* Left shift. */ - ires = big_size(tmp_arg1) + (i / D_EXP); + ires = big_size(Op1) + (i / D_EXP); } else { /* Right shift. */ - ires = big_size(tmp_arg1); + ires = big_size(Op1); if (ires <= (-i / D_EXP)) ires = 3; /* ??? */ else @@ -2998,14 +2959,14 @@ do { \ c_p->freason = SYSTEM_LIMIT; goto lb_Cl_error; } - TestHeapPreserve(ires+1, Arg(1), tmp_arg1); + TestHeapPreserve(ires+1, Arg(1), Op1); bigp = HTOP; - tmp_arg1 = big_lshift(tmp_arg1, i, bigp); - if (is_big(tmp_arg1)) { + Op1 = big_lshift(Op1, i, bigp); + if (is_big(Op1)) { HTOP += bignum_header_arity(*HTOP) + 1; } HEAP_SPACE_VERIFIED(0); - if (is_nil(tmp_arg1)) { + if (is_nil(Op1)) { /* * This result must have been only slight larger * than allowed since it wasn't caught by the @@ -3015,25 +2976,25 @@ do { \ goto lb_Cl_error; } ERTS_HOLE_CHECK(c_p); - StoreBifResult(2, tmp_arg1); + StoreBifResult(4, Op1); } - } else if (is_big(tmp_arg1)) { + } else if (is_big(Op1)) { if (i == 0) { - StoreBifResult(2, tmp_arg1); + StoreBifResult(4, Op1); } goto big_shift; } - } else if (is_big(tmp_arg2)) { - if (bignum_header_is_neg(*big_val(tmp_arg2))) { + } else if (is_big(Op2)) { + if (bignum_header_is_neg(*big_val(Op2))) { /* * N bsl NegativeBigNum is either 0 or -1, depending on * the sign of N. Since we don't believe this case * is common, do the calculation with the minimum * amount of code. */ - tmp_arg2 = make_small(MIN_SMALL); + Op2 = make_small(MIN_SMALL); goto do_bsl; - } else if (is_small(tmp_arg1) || is_big(tmp_arg1)) { + } else if (is_small(Op1) || is_big(Op1)) { /* * N bsl PositiveBigNum is too large to represent. */ diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 335f8e2db5..e4a757ec8b 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1475,68 +1475,95 @@ i_get_map_element f y x x i_get_map_element f x x y i_get_map_element f y x y +# +# Convert the plus operations to a generic plus instruction. +# +gen_plus/5 +gen_minus/5 + +gc_bif1 Fail Live u$bif:erlang:splus/1 Src Dst => \ + gen_plus Fail Live Src i Dst +gc_bif2 Fail Live u$bif:erlang:splus/2 S1 S2 Dst => \ + gen_plus Fail Live S1 S2 Dst + +gc_bif1 Fail Live u$bif:erlang:sminus/1 Src Dst => \ + gen_minus Fail Live i Src Dst +gc_bif2 Fail Live u$bif:erlang:sminus/2 S1 S2 Dst => \ + gen_minus Fail Live S1 S2 Dst + # # Optimize addition and subtraction of small literals using # the i_increment/4 instruction (in bodies, not in guards). # -gc_bif2 p Live u$bif:erlang:splus/2 Int=i Reg=x Dst => \ +gen_plus p Live Int=i Reg=d Dst => \ gen_increment(Reg, Int, Live, Dst) -gc_bif2 p Live u$bif:erlang:splus/2 Reg=x Int=i Dst => \ +gen_plus p Live Reg=d Int=i Dst => \ gen_increment(Reg, Int, Live, Dst) -gc_bif2 p Live u$bif:erlang:sminus/2 Reg=d Int=i Dst | \ - negation_is_small(Int) => \ +gen_minus p Live Reg=d Int=i Dst | negation_is_small(Int) => \ gen_increment_from_minus(Reg, Int, Live, Dst) # # GCing arithmetic instructions. # -gc_bif2 Fail I u$bif:erlang:splus/2 S1=x S2=x Dst=d => i_plus Fail I S1 S2 Dst -gc_bif2 Fail I u$bif:erlang:splus/2 S1 S2 Dst=d => i_fetch S1 S2 | i_plus Fail I Dst -gc_bif2 Fail I u$bif:erlang:sminus/2 S1=x S2=x Dst=d => i_minus Fail I S1 S2 Dst -gc_bif2 Fail I u$bif:erlang:sminus/2 S1 S2 Dst=d => i_fetch S1 S2 | i_minus Fail I Dst -gc_bif2 Fail I u$bif:erlang:stimes/2 S1 S2 Dst=d => i_fetch S1 S2 | i_times Fail I Dst -gc_bif2 Fail I u$bif:erlang:div/2 S1 S2 Dst=d => i_fetch S1 S2 | i_m_div Fail I Dst +gen_plus Fail Live S1 S2 Dst => i_plus Fail Live S1 S2 Dst -gc_bif2 Fail I u$bif:erlang:intdiv/2 S1 S2 Dst=d => i_fetch S1 S2 | i_int_div Fail I Dst -gc_bif2 Fail I u$bif:erlang:rem/2 S1=x S2=x Dst=d => i_rem Fail I S1 S2 Dst -gc_bif2 Fail I u$bif:erlang:rem/2 S1 S2 Dst=d => i_fetch S1 S2 | i_rem Fail I Dst +gen_minus Fail Live S1 S2 Dst => i_minus Fail Live S1 S2 Dst -gc_bif2 Fail I u$bif:erlang:bsl/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bsl Fail I Dst -gc_bif2 Fail I u$bif:erlang:bsr/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bsr Fail I Dst +gc_bif2 Fail Live u$bif:erlang:stimes/2 S1 S2 Dst => \ + i_times Fail Live S1 S2 Dst -gc_bif2 Fail I u$bif:erlang:band/2 S1=x S2=c Dst=d => i_band Fail I S1 S2 Dst -gc_bif2 Fail I u$bif:erlang:band/2 S1 S2 Dst=d => i_fetch S1 S2 | i_band Fail I Dst -gc_bif2 Fail I u$bif:erlang:bor/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bor Fail I Dst -gc_bif2 Fail I u$bif:erlang:bxor/2 S1 S2 Dst=d => i_fetch S1 S2 | i_bxor Fail I Dst +gc_bif2 Fail Live u$bif:erlang:div/2 S1 S2 Dst => \ + i_m_div Fail Live S1 S2 Dst +gc_bif2 Fail Live u$bif:erlang:intdiv/2 S1 S2 Dst => \ + i_int_div Fail Live S1 S2 Dst -gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst +gc_bif2 Fail Live u$bif:erlang:rem/2 S1 S2 Dst => \ + i_rem Fail Live S1 S2 Dst + +gc_bif2 Fail Live u$bif:erlang:bsl/2 S1 S2 Dst => \ + i_bsl Fail Live S1 S2 Dst +gc_bif2 Fail Live u$bif:erlang:bsr/2 S1 S2 Dst => \ + i_bsr Fail Live S1 S2 Dst + +gc_bif2 Fail Live u$bif:erlang:band/2 S1 S2 Dst => \ + i_band Fail Live S1 S2 Dst -gc_bif1 Fail I u$bif:erlang:sminus/1 Src Dst=d => i_fetch i Src | i_minus Fail I Dst -gc_bif1 Fail I u$bif:erlang:splus/1 Src Dst=d => i_fetch i Src | i_plus Fail I Dst +gc_bif2 Fail Live u$bif:erlang:bor/2 S1 S2 Dst => \ + i_bor Fail Live S1 S2 Dst + +gc_bif2 Fail Live u$bif:erlang:bxor/2 S1 S2 Dst => \ + i_bxor Fail Live S1 S2 Dst + +gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst i_increment x I I d i_increment y I I d i_plus j I x x d -i_plus j I d +i_plus j I s s d + i_minus j I x x d -i_minus j I d -i_times j I d -i_m_div j I d -i_int_div j I d +i_minus j I s s d + +i_times j I s s d + +i_m_div j I s s d +i_int_div j I s s d + i_rem j I x x d -i_rem j I d +i_rem j I s s d -i_bsl j I d -i_bsr j I d +i_bsl j I s s d +i_bsr j I s s d i_band j I x c d -i_band j I d -i_bor j I d -i_bxor j I d +i_band j I s s d + +i_bor j I s s d +i_bxor j I s s d i_int_bnot j s I d -- cgit v1.2.3 From 3c583d590589b64ab543102f2542bda0337ce2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 17 Jun 2015 06:52:50 +0200 Subject: Eliminate the use of i_fetch for relational operators --- erts/emulator/beam/beam_emu.c | 39 ++----------------------- erts/emulator/beam/ops.tab | 67 ++++++++++++++----------------------------- 2 files changed, 25 insertions(+), 81 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 82cc030878..b63fa038d2 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -651,6 +651,9 @@ void** beam_ops; #define EqualImmed(X, Y, Action) if (X != Y) { Action; } #define NotEqualImmed(X, Y, Action) if (X == Y) { Action; } #define EqualExact(X, Y, Action) if (!EQ(X,Y)) { Action; } +#define NotEqualExact(X, Y, Action) if (EQ(X,Y)) { Action; } +#define Equal(X, Y, Action) if (!CMP_EQ(X,Y)) { Action; } +#define NotEqual(X, Y, Action) if (!CMP_NE(X,Y)) { Action; } #define IsLessThan(X, Y, Action) if (CMP_GE(X, Y)) { Action; } #define IsGreaterEqual(X, Y, Action) if (CMP_LT(X, Y)) { Action; } @@ -1410,36 +1413,6 @@ void process_main(void) DO_OUTLINED_ARITH_2(mixed_minus, MinusOp1, MinusOp2); } - OpCase(i_is_lt_f): - if (CMP_GE(tmp_arg1, tmp_arg2)) { - ClauseFail(); - } - Next(1); - - OpCase(i_is_ge_f): - if (CMP_LT(tmp_arg1, tmp_arg2)) { - ClauseFail(); - } - Next(1); - - OpCase(i_is_eq_f): - if (CMP_NE(tmp_arg1, tmp_arg2)) { - ClauseFail(); - } - Next(1); - - OpCase(i_is_ne_f): - if (CMP_EQ(tmp_arg1, tmp_arg2)) { - ClauseFail(); - } - Next(1); - - OpCase(i_is_eq_exact_f): - if (!EQ(tmp_arg1, tmp_arg2)) { - ClauseFail(); - } - Next(1); - { Eterm is_eq_exact_lit_val; @@ -3276,12 +3249,6 @@ do { \ NextPF(3, next); } - OpCase(i_is_ne_exact_f): - if (EQ(tmp_arg1, tmp_arg2)) { - ClauseFail(); - } - Next(1); - OpCase(normal_exit): { SWAPOUT; c_p->freason = EXC_NORMAL; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index e4a757ec8b..57fb1ea026 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -397,59 +397,36 @@ i_is_ne_exact_immed f y c i_is_ne_exact_literal f x c i_is_ne_exact_literal f y c -# -# Common Compare Specializations -# We don't do all of them since we want -# to keep the instruction set small-ish -# - -is_eq_exact Lbl S1=y S2=x => is_eq_exact Lbl S2 S1 -is_eq_exact Lbl S1=x S2=xy => i_is_eq_exact_spec Lbl S1 S2 -%macro: i_is_eq_exact_spec EqualExact -fail_action - -i_is_eq_exact_spec f x x -i_is_eq_exact_spec f x y - -is_lt Lbl S1=xc S2=xc => i_is_lt_spec Lbl S1 S2 - -%macro: i_is_lt_spec IsLessThan -fail_action - -i_is_lt_spec f x x -i_is_lt_spec f x c -i_is_lt_spec f c x +is_eq_exact Lbl Y=y X=x => is_eq_exact Lbl X Y +%macro: is_eq_exact EqualExact -fail_action +is_eq_exact f x x +is_eq_exact f x y +is_eq_exact f s s + +%macro: is_lt IsLessThan -fail_action +is_lt f x x +is_lt f x c +is_lt f c x %cold -i_is_lt_spec f c c +is_lt f s s %hot -is_ge Lbl S1=xc S2=xc => i_is_ge_spec Lbl S1 S2 - -%macro: i_is_ge_spec IsGreaterEqual -fail_action - -i_is_ge_spec f x x -i_is_ge_spec f x c -i_is_ge_spec f c x +%macro: is_ge IsGreaterEqual -fail_action +is_ge f x x +is_ge f x c +is_ge f c x %cold -i_is_ge_spec f c c +is_ge f s s %hot -# -# All other comparisons. -# - -is_eq_exact Lbl S1 S2 => i_fetch S1 S2 | i_is_eq_exact Lbl -is_ne_exact Lbl S1 S2 => i_fetch S1 S2 | i_is_ne_exact Lbl +%macro: is_ne_exact NotEqualExact -fail_action +is_ne_exact f s s -is_lt Lbl S1 S2 => i_fetch S1 S2 | i_is_lt Lbl -is_ge Lbl S1 S2 => i_fetch S1 S2 | i_is_ge Lbl -is_eq Lbl S1 S2 => i_fetch S1 S2 | i_is_eq Lbl -is_ne Lbl S1 S2 => i_fetch S1 S2 | i_is_ne Lbl +%macro: is_eq Equal -fail_action +is_eq f s s -i_is_eq_exact f -i_is_ne_exact f -i_is_lt f -i_is_ge f -i_is_eq f -i_is_ne f +%macro: is_ne NotEqual -fail_action +is_ne f s s # # Putting things. -- cgit v1.2.3 From 30204739a047ab96d2b7d59ae461d4cbb2131509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 17 Jun 2015 10:34:39 +0200 Subject: Eliminate the use of i_fetch for BIF instructions --- erts/emulator/beam/beam_emu.c | 68 ++++++++++++++++++++++++------------------ erts/emulator/beam/beam_load.c | 28 ++++++----------- erts/emulator/beam/ops.tab | 21 +++++++------ 3 files changed, 58 insertions(+), 59 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index b63fa038d2..d260c74255 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2513,12 +2513,10 @@ do { \ { typedef Eterm (*GcBifFunction)(Process*, Eterm*, Uint); GcBifFunction bf; - Eterm arg; Eterm result; Uint live = (Uint) Arg(3); - GetArg1(2, arg); - reg[live] = arg; + GetArg1(2, x(live)); bf = (GcBifFunction) Arg(1); c_p->fcalls = FCALLS; SWAPOUT; @@ -2538,12 +2536,12 @@ do { \ SET_I((BeamInstr *) Arg(0)); Goto(*I); } - reg[0] = arg; + x(0) = x(live); I = handle_error(c_p, I, reg, translate_gc_bif((void *) bf)); goto post_error_handling; } - OpCase(i_gc_bif2_jIId): /* Note, one less parameter than the i_gc_bif1 + OpCase(i_gc_bif2_jIIssd): /* Note, one less parameter than the i_gc_bif1 and i_gc_bif3 */ { typedef Eterm (*GcBifFunction)(Process*, Eterm*, Uint); @@ -2551,8 +2549,13 @@ do { \ Eterm result; Uint live = (Uint) Arg(2); - reg[live++] = tmp_arg1; - reg[live] = tmp_arg2; + GetArg2(3, x(live), x(live+1)); + /* + * XXX This calling convention does not make sense. 'live' + * should point out the first argument, not the second + * (i.e. 'live' should not be incremented below). + */ + live++; bf = (GcBifFunction) Arg(1); c_p->fcalls = FCALLS; SWAPOUT; @@ -2566,30 +2569,34 @@ do { \ ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; if (is_value(result)) { - StoreBifResult(3, result); + StoreBifResult(5, result); } if (Arg(0) != 0) { SET_I((BeamInstr *) Arg(0)); Goto(*I); } - reg[0] = tmp_arg1; - reg[1] = tmp_arg2; + live--; + x(0) = x(live); + x(1) = x(live+1); I = handle_error(c_p, I, reg, translate_gc_bif((void *) bf)); goto post_error_handling; } - OpCase(i_gc_bif3_jIsId): + OpCase(i_gc_bif3_jIIssd): { typedef Eterm (*GcBifFunction)(Process*, Eterm*, Uint); GcBifFunction bf; - Eterm arg; Eterm result; - Uint live = (Uint) Arg(3); + Uint live = (Uint) Arg(2); - GetArg1(2, arg); - reg[live++] = arg; - reg[live++] = tmp_arg1; - reg[live] = tmp_arg2; + x(live) = x(SCRATCH_X_REG); + GetArg2(3, x(live+1), x(live+2)); + /* + * XXX This calling convention does not make sense. 'live' + * should point out the first argument, not the third + * (i.e. 'live' should not be incremented below). + */ + live += 2; bf = (GcBifFunction) Arg(1); c_p->fcalls = FCALLS; SWAPOUT; @@ -2603,15 +2610,16 @@ do { \ ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; if (is_value(result)) { - StoreBifResult(4, result); + StoreBifResult(5, result); } if (Arg(0) != 0) { SET_I((BeamInstr *) Arg(0)); Goto(*I); } - reg[0] = arg; - reg[1] = tmp_arg1; - reg[2] = tmp_arg2; + live -= 2; + x(0) = x(live); + x(1) = x(live+1); + x(2) = x(live+2); I = handle_error(c_p, I, reg, translate_gc_bif((void *) bf)); goto post_error_handling; } @@ -2619,12 +2627,13 @@ do { \ /* * Guards bifs and, or, xor in guards. */ - OpCase(i_bif2_fbd): + OpCase(i_bif2_fbssd): { - Eterm tmp_reg[2] = {tmp_arg1, tmp_arg2}; + Eterm tmp_reg[2]; Eterm (*bf)(Process*, Eterm*); Eterm result; + GetArg2(2, tmp_reg[0], tmp_reg[1]); bf = (BifFunction) Arg(1); c_p->fcalls = FCALLS; PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2636,7 +2645,7 @@ do { \ ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; if (is_value(result)) { - StoreBifResult(2, result); + StoreBifResult(4, result); } SET_I((BeamInstr *) Arg(0)); Goto(*I); @@ -2645,12 +2654,13 @@ do { \ /* * Guards bifs and, or, xor, relational operators in body. */ - OpCase(i_bif2_body_bd): + OpCase(i_bif2_body_bssd): { - Eterm tmp_reg[2] = {tmp_arg1, tmp_arg2}; + Eterm tmp_reg[2]; Eterm (*bf)(Process*, Eterm*); Eterm result; + GetArg2(1, tmp_reg[0], tmp_reg[1]); bf = (BifFunction) Arg(0); PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); @@ -2661,10 +2671,10 @@ do { \ ERTS_HOLE_CHECK(c_p); if (is_value(result)) { ASSERT(!is_CP(result)); - StoreBifResult(1, result); + StoreBifResult(3, result); } - reg[0] = tmp_arg1; - reg[1] = tmp_arg2; + reg[0] = tmp_reg[0]; + reg[1] = tmp_reg[1]; SWAPOUT; I = handle_error(c_p, I, reg, bf); goto post_error_handling; diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 3671baa552..c90576333a 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -3921,9 +3921,7 @@ gen_make_fun2(LoaderState* stp, GenOpArg idx) /* * Rewrite gc_bifs with one parameter (the common case). Utilized * in ops.tab to rewrite instructions calling bif's in guards - * to use a garbage collecting implementation. The instructions - * are sometimes once again rewritten to handle literals (putting the - * parameter in the mostly unused r[0] before the instruction is executed). + * to use a garbage collecting implementation. */ static GenOp* gen_guard_bif1(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, @@ -3978,10 +3976,6 @@ gen_guard_bif1(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, /* * This is used by the ops.tab rule that rewrites gc_bifs with two parameters. - * The instruction returned is then again rewritten to an i_load instruction - * followed by i_gc_bif2_jIId, to handle literals properly. - * As opposed to the i_gc_bif1_jIsId, the instruction i_gc_bif2_jIId is - * always rewritten, regardless of if there actually are any literals. */ static GenOp* gen_guard_bif2(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, @@ -4008,23 +4002,19 @@ gen_guard_bif2(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, op->a[2].val = stp->import[Bif.val].arity; return op; } - op->op = genop_ii_gc_bif2_6; + op->op = genop_i_gc_bif2_6; op->arity = 6; op->a[0] = Fail; op->a[1].type = TAG_u; - op->a[2] = S1; - op->a[3] = S2; - op->a[4] = Live; + op->a[2] = Live; + op->a[3] = S1; + op->a[4] = S2; op->a[5] = Dst; return op; } /* * This is used by the ops.tab rule that rewrites gc_bifs with three parameters. - * The instruction returned is then again rewritten to a move instruction that - * uses r[0] for temp storage, followed by an i_load instruction, - * followed by i_gc_bif3_jIsId, to handle literals properly. Rewriting - * always occur, as with the gc_bif2 counterpart. */ static GenOp* gen_guard_bif3(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, @@ -4055,10 +4045,10 @@ gen_guard_bif3(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, op->arity = 7; op->a[0] = Fail; op->a[1].type = TAG_u; - op->a[2] = S1; - op->a[3] = S2; - op->a[4] = S3; - op->a[5] = Live; + op->a[2] = Live; + op->a[3] = S1; + op->a[4] = S2; + op->a[5] = S3; op->a[6] = Dst; op->next = NULL; return op; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 57fb1ea026..492d4d240a 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -995,8 +995,8 @@ bif2 Jump=j u$bif:erlang:element/2 S1=s S2=xy Dst=d => gen_element(Jump, S1, S2, bif1 p Bif S1 Dst => bif1_body Bif S1 Dst -bif2 p Bif S1 S2 Dst => i_fetch S1 S2 | i_bif2_body Bif Dst -bif2 Fail Bif S1 S2 Dst => i_fetch S1 S2 | i_bif2 Fail Bif Dst +bif2 p Bif S1 S2 Dst => i_bif2_body Bif S1 S2 Dst +bif2 Fail Bif S1 S2 Dst => i_bif2 Fail Bif S1 S2 Dst i_get s d @@ -1018,8 +1018,8 @@ i_element j y s d bif1 f b s d bif1_body b s d -i_bif2 f b d -i_bif2_body b d +i_bif2 f b s s d +i_bif2_body b s s d # # Internal calls. @@ -1568,17 +1568,16 @@ gc_bif3 Fail I Bif S1 S2 S3 Dst => \ i_gc_bif1 j I s I d -ii_gc_bif2/6 - -ii_gc_bif2 Fail Bif S1 S2 Live D => i_fetch S1 S2 | i_gc_bif2 Fail Bif Live D - -i_gc_bif2 j I I d +i_gc_bif2 j I I s s d ii_gc_bif3/7 -ii_gc_bif3 Fail Bif S1 S2 S3 Live D => move S1 x | i_fetch S2 S3 | i_gc_bif3 Fail Bif x Live D +# A specific instruction can only have 6 operands, so we must +# pass one of the arguments in an x register. +ii_gc_bif3 Fail Bif Live S1 S2 S3 Dst => \ + move S1 x | i_gc_bif3 Fail Bif Live S2 S3 Dst -i_gc_bif3 j I s I d +i_gc_bif3 j I I s s d # # The following instruction is specially handled in beam_load.c -- cgit v1.2.3 From bede3941be8629efa4d91755c085a91b1416d432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 17 Jun 2015 15:31:36 +0200 Subject: Eliminate use of i_fetch for bit syntax instructions --- erts/emulator/beam/beam_emu.c | 207 +++++++++++++++++++++-------------------- erts/emulator/beam/beam_load.c | 27 ++---- erts/emulator/beam/ops.tab | 29 +++--- 3 files changed, 129 insertions(+), 134 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index d260c74255..26e28832cf 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3548,11 +3548,10 @@ do { \ goto do_bs_init_bits_known; } - OpCase(i_bs_init_bits_fail_heap_IjId): { - /* tmp_arg1 was fetched by an i_fetch instruction */ - num_bits_term = tmp_arg1; - alloc = Arg(0); - I++; + OpCase(i_bs_init_bits_fail_heap_sIjId): { + GetArg1(0, num_bits_term); + alloc = Arg(1); + I += 2; goto do_bs_init_bits; } @@ -3675,46 +3674,48 @@ do { \ } { - OpCase(i_bs_init_fail_heap_IjId): { - /* tmp_arg1 was fetched by an i_fetch instruction */ - tmp_arg2 = Arg(0); - I++; + Eterm BsOp1, BsOp2; + + OpCase(i_bs_init_fail_heap_sIjId): { + GetArg1(0, BsOp1); + BsOp2 = Arg(1); + I += 2; goto do_bs_init; } OpCase(i_bs_init_fail_yjId): { - tmp_arg1 = yb(Arg(0)); - tmp_arg2 = 0; + BsOp1 = yb(Arg(0)); + BsOp2 = 0; I++; goto do_bs_init; } OpCase(i_bs_init_fail_xjId): { - tmp_arg1 = xb(Arg(0)); - tmp_arg2 = 0; + BsOp1 = xb(Arg(0)); + BsOp2 = 0; I++; } /* FALL THROUGH */ do_bs_init: - if (is_small(tmp_arg1)) { - Sint size = signed_val(tmp_arg1); + if (is_small(BsOp1)) { + Sint size = signed_val(BsOp1); if (size < 0) { goto badarg; } - tmp_arg1 = (Eterm) size; + BsOp1 = (Eterm) size; } else { Uint bytes; - if (!term_to_Uint(tmp_arg1, &bytes)) { + if (!term_to_Uint(BsOp1, &bytes)) { c_p->freason = bytes; goto lb_Cl_error; } if ((bytes >> (8*sizeof(Uint)-3)) != 0) { goto system_limit; } - tmp_arg1 = (Eterm) bytes; + BsOp1 = (Eterm) bytes; } - if (tmp_arg1 <= ERL_ONHEAP_BIN_LIMIT) { + if (BsOp1 <= ERL_ONHEAP_BIN_LIMIT) { goto do_heap_bin_alloc; } else { goto do_proc_bin_alloc; @@ -3722,15 +3723,15 @@ do { \ OpCase(i_bs_init_heap_IIId): { - tmp_arg1 = Arg(0); - tmp_arg2 = Arg(1); + BsOp1 = Arg(0); + BsOp2 = Arg(1); I++; goto do_proc_bin_alloc; } OpCase(i_bs_init_IId): { - tmp_arg1 = Arg(0); - tmp_arg2 = 0; + BsOp1 = Arg(0); + BsOp2 = 0; } /* FALL THROUGH */ do_proc_bin_alloc: { @@ -3739,13 +3740,13 @@ do { \ erts_bin_offset = 0; erts_writable_bin = 0; - TestBinVHeap(tmp_arg1 / sizeof(Eterm), - tmp_arg2 + PROC_BIN_SIZE + ERL_SUB_BIN_SIZE, Arg(1)); + TestBinVHeap(BsOp1 / sizeof(Eterm), + BsOp2 + PROC_BIN_SIZE + ERL_SUB_BIN_SIZE, Arg(1)); /* * Allocate the binary struct itself. */ - bptr = erts_bin_nrml_alloc(tmp_arg1); + bptr = erts_bin_nrml_alloc(BsOp1); erts_refc_init(&bptr->refc, 1); erts_current_bin = (byte *) bptr->orig_bytes; @@ -3755,28 +3756,28 @@ do { \ pb = (ProcBin *) HTOP; HTOP += PROC_BIN_SIZE; pb->thing_word = HEADER_PROC_BIN; - pb->size = tmp_arg1; + pb->size = BsOp1; pb->next = MSO(c_p).first; MSO(c_p).first = (struct erl_off_heap_header*) pb; pb->val = bptr; pb->bytes = (byte*) bptr->orig_bytes; pb->flags = 0; - OH_OVERHEAD(&(MSO(c_p)), tmp_arg1 / sizeof(Eterm)); + OH_OVERHEAD(&(MSO(c_p)), BsOp1 / sizeof(Eterm)); StoreBifResult(2, make_binary(pb)); } OpCase(i_bs_init_heap_bin_heap_IIId): { - tmp_arg1 = Arg(0); - tmp_arg2 = Arg(1); + BsOp1 = Arg(0); + BsOp2 = Arg(1); I++; goto do_heap_bin_alloc; } OpCase(i_bs_init_heap_bin_IId): { - tmp_arg1 = Arg(0); - tmp_arg2 = 0; + BsOp1 = Arg(0); + BsOp2 = 0; } /* Fall through */ do_heap_bin_alloc: @@ -3784,33 +3785,36 @@ do { \ ErlHeapBin* hb; Uint bin_need; - bin_need = heap_bin_size(tmp_arg1); + bin_need = heap_bin_size(BsOp1); erts_bin_offset = 0; erts_writable_bin = 0; - TestHeap(bin_need+tmp_arg2+ERL_SUB_BIN_SIZE, Arg(1)); + TestHeap(bin_need+BsOp2+ERL_SUB_BIN_SIZE, Arg(1)); hb = (ErlHeapBin *) HTOP; HTOP += bin_need; - hb->thing_word = header_heap_bin(tmp_arg1); - hb->size = tmp_arg1; + hb->thing_word = header_heap_bin(BsOp1); + hb->size = BsOp1; erts_current_bin = (byte *) hb->data; - tmp_arg1 = make_binary(hb); - StoreBifResult(2, tmp_arg1); + BsOp1 = make_binary(hb); + StoreBifResult(2, BsOp1); } } - OpCase(i_bs_add_jId): { - Uint Unit = Arg(1); - if (is_both_small(tmp_arg1, tmp_arg2)) { - Sint Arg1 = signed_val(tmp_arg1); - Sint Arg2 = signed_val(tmp_arg2); + OpCase(bs_add_jssId): { + Eterm Op1, Op2; + Uint Unit = Arg(3); + + GetArg2(1, Op1, Op2); + if (is_both_small(Op1, Op2)) { + Sint Arg1 = signed_val(Op1); + Sint Arg2 = signed_val(Op2); if (Arg1 >= 0 && Arg2 >= 0) { - BsSafeMul(Arg2, Unit, goto system_limit, tmp_arg1); - tmp_arg1 += Arg1; + BsSafeMul(Arg2, Unit, goto system_limit, Op1); + Op1 += Arg1; store_bs_add_result: - if (MY_IS_SSMALL((Sint) tmp_arg1)) { - tmp_arg1 = make_small(tmp_arg1); + if (MY_IS_SSMALL((Sint) Op1)) { + Op1 = make_small(Op1); } else { /* * May generate a heap fragment, but in this @@ -3822,10 +3826,10 @@ do { \ * references (such as the heap). */ SWAPOUT; - tmp_arg1 = erts_make_integer(tmp_arg1, c_p); + Op1 = erts_make_integer(Op1, c_p); HTOP = HEAP_TOP(c_p); } - StoreBifResult(2, tmp_arg1); + StoreBifResult(4, Op1); } goto badarg; } else { @@ -3848,16 +3852,16 @@ do { \ * an Uint, the reason is SYSTEM_LIMIT. */ - if (!term_to_Uint(tmp_arg1, &a)) { + if (!term_to_Uint(Op1, &a)) { if (a == BADARG) { goto badarg; } - if (!term_to_Uint(tmp_arg2, &b)) { + if (!term_to_Uint(Op2, &b)) { c_p->freason = b; goto lb_Cl_error; } goto system_limit; - } else if (!term_to_Uint(tmp_arg2, &b)) { + } else if (!term_to_Uint(Op2, &b)) { c_p->freason = b; goto lb_Cl_error; } @@ -3867,8 +3871,8 @@ do { \ */ BsSafeMul(b, Unit, goto system_limit, c); - tmp_arg1 = a + c; - if (tmp_arg1 < a) { + Op1 = a + c; + if (Op1 < a) { /* * If the result is less than one of the * arguments, there must have been an overflow. @@ -3890,46 +3894,43 @@ do { \ } /* - * tmp_arg1 = Number of bytes to build - * tmp_arg2 = Source binary - * Operands: Fail ExtraHeap Live Unit Dst + * x(SCRATCH_X_REG); + * Operands: Fail ExtraHeap Live Unit Size Dst */ - OpCase(i_bs_append_jIIId): { + OpCase(i_bs_append_jIIIsd): { Uint live = Arg(2); Uint res; + Eterm Size; + GetArg1(4, Size); SWAPOUT; - reg[live] = tmp_arg2; - res = erts_bs_append(c_p, reg, live, tmp_arg1, Arg(1), Arg(3)); + reg[live] = x(SCRATCH_X_REG); + res = erts_bs_append(c_p, reg, live, Size, Arg(1), Arg(3)); SWAPIN; if (is_non_value(res)) { /* c_p->freason is already set (may be either BADARG or SYSTEM_LIMIT). */ goto lb_Cl_error; } - StoreBifResult(4, res); + StoreBifResult(5, res); } /* - * tmp_arg1 = Number of bytes to build - * tmp_arg2 = Source binary - * Operands: Fail Unit Dst + * Operands: Fail Size Src Unit Dst */ - OpCase(i_bs_private_append_jId): { + OpCase(i_bs_private_append_jIssd): { Eterm res; + Eterm Size, Src; - res = erts_bs_private_append(c_p, tmp_arg2, tmp_arg1, Arg(1)); + GetArg2(2, Size, Src); + res = erts_bs_private_append(c_p, Src, Size, Arg(1)); if (is_non_value(res)) { /* c_p->freason is already set (may be either BADARG or SYSTEM_LIMIT). */ goto lb_Cl_error; } - StoreBifResult(2, res); + StoreBifResult(4, res); } - /* - * tmp_arg1 = Initial size of writable binary - * Operands: Live Dst - */ OpCase(bs_init_writable): { SWAPOUT; r(0) = erts_bs_init_writable(c_p, r(0)); @@ -4024,26 +4025,29 @@ do { \ /* * Only used for validating a value matched out. - * - * tmp_arg1 = Integer to validate - * tmp_arg2 = Match context */ - OpCase(i_bs_validate_unicode_retract_j): { - /* - * There is no need to untag the integer, but it IS necessary - * to make sure it is small (a bignum pointer could fall in - * the valid range). - */ - if (is_not_small(tmp_arg1) || tmp_arg1 > make_small(0x10FFFFUL) || - (make_small(0xD800UL) <= tmp_arg1 && - tmp_arg1 <= make_small(0xDFFFUL))) { - ErlBinMatchBuffer *mb = ms_matchbuffer(tmp_arg2); + OpCase(i_bs_validate_unicode_retract_jss): { + Eterm i; /* Integer to validate */ - mb->offset -= 32; - goto badarg; - } - Next(1); - } + /* + * There is no need to untag the integer, but it IS necessary + * to make sure it is small (a bignum pointer could fall in + * the valid range). + */ + + GetArg1(1, i); + if (is_not_small(i) || i > make_small(0x10FFFFUL) || + (make_small(0xD800UL) <= i && i <= make_small(0xDFFFUL))) { + Eterm ms; /* Match context */ + ErlBinMatchBuffer* mb; + + GetArg1(2, ms); + mb = ms_matchbuffer(ms); + mb->offset -= 32; + goto badarg; + } + Next(3); + } /* * Matching of binaries. @@ -4292,35 +4296,36 @@ do { \ } /* - * tmp_arg1 = Match context - * tmp_arg2 = Size field - * Operands: Fail Live FlagsAndUnit Dst + * Operands: Fail Live FlagsAndUnit Ms Sz Dst */ - OpCase(i_bs_get_integer_fIId): { + OpCase(i_bs_get_integer_fIIssd): { Uint flags; Uint size; + Eterm Ms; + Eterm Sz; ErlBinMatchBuffer* mb; Eterm result; flags = Arg(2); - BsGetFieldSize(tmp_arg2, (flags >> 3), ClauseFail(), size); + GetArg2(3, Ms, Sz); + BsGetFieldSize(Sz, (flags >> 3), ClauseFail(), size); if (size >= SMALL_BITS) { Uint wordsneeded; - /* check bits size before potential gc. + /* Check bits size before potential gc. * We do not want a gc and then realize we don't need - * the allocated space (i.e. if the op fails) + * the allocated space (i.e. if the op fails). * - * remember to reacquire the matchbuffer after gc. + * Remember to re-acquire the matchbuffer after gc. */ - mb = ms_matchbuffer(tmp_arg1); + mb = ms_matchbuffer(Ms); if (mb->size - mb->offset < size) { ClauseFail(); } wordsneeded = 1+WSIZE(NBYTES((Uint) size)); - TestHeapPreserve(wordsneeded, Arg(1), tmp_arg1); + TestHeapPreserve(wordsneeded, Arg(1), Ms); } - mb = ms_matchbuffer(tmp_arg1); + mb = ms_matchbuffer(Ms); LIGHT_SWAPOUT; result = erts_bs_get_integer_2(c_p, size, flags, mb); LIGHT_SWAPIN; @@ -4328,7 +4333,7 @@ do { \ if (is_non_value(result)) { ClauseFail(); } - StoreBifResult(3, result); + StoreBifResult(5, result); } { diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index c90576333a..871c2b1f55 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -2854,23 +2854,16 @@ gen_get_integer2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live, goto generic; } } else { - GenOp* op2; - NEW_GENOP(stp, op2); - - op->op = genop_i_fetch_2; - op->arity = 2; - op->a[0] = Ms; - op->a[1] = Size; - op->next = op2; - - op2->op = genop_i_bs_get_integer_4; - op2->arity = 4; - op2->a[0] = Fail; - op2->a[1] = Live; - op2->a[2].type = TAG_u; - op2->a[2].val = (Unit.val << 3) | Flags.val; - op2->a[3] = Dst; - op2->next = NULL; + op->op = genop_i_bs_get_integer_6; + op->arity = 6; + op->a[0] = Fail; + op->a[1] = Live; + op->a[2].type = TAG_u; + op->a[2].val = (Unit.val << 3) | Flags.val; + op->a[3] = Ms; + op->a[4] = Size; + op->a[5] = Dst; + op->next = NULL; return op; } op->next = NULL; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 492d4d240a..637cef0a22 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1122,7 +1122,7 @@ bs_get_integer2 Fail=f Ms=x Live=u Sz=sq Unit=u Flags=u Dst=d => \ i_bs_get_integer_small_imm x I f I d i_bs_get_integer_imm x I I f I d -i_bs_get_integer f I I d +i_bs_get_integer f I I s s d i_bs_get_integer_8 x f d i_bs_get_integer_16 x f d i_bs_get_integer_32 x f I d @@ -1195,14 +1195,12 @@ i_bs_get_utf16 x f I d bs_get_utf32 Fail=f Ms=x Live=u Flags=u Dst=d => \ bs_get_integer2 Fail Ms Live i=32 u=1 Flags Dst | \ - i_fetch Dst Ms | \ - i_bs_validate_unicode_retract Fail + i_bs_validate_unicode_retract Fail Dst Ms bs_skip_utf32 Fail=f Ms=x Live=u Flags=u => \ bs_get_integer2 Fail Ms Live i=32 u=1 Flags x | \ - i_fetch x Ms | \ - i_bs_validate_unicode_retract Fail + i_bs_validate_unicode_retract Fail x Ms -i_bs_validate_unicode_retract j +i_bs_validate_unicode_retract j s s %hot # @@ -1224,12 +1222,12 @@ bs_init2 Fail Sz=u Words Regs Flags Dst => \ bs_init2 Fail Sz Words=u==0 Regs Flags Dst => \ i_bs_init_fail Sz Fail Regs Dst bs_init2 Fail Sz Words Regs Flags Dst => \ - i_fetch Sz x=0 | i_bs_init_fail_heap Words Fail Regs Dst + i_bs_init_fail_heap Sz Words Fail Regs Dst i_bs_init_fail x j I d i_bs_init_fail y j I d -i_bs_init_fail_heap I j I d +i_bs_init_fail_heap s I j I d i_bs_init I I d i_bs_init_heap_bin I I d @@ -1246,31 +1244,30 @@ bs_init_bits Fail Sz=u Words Regs Flags Dst => i_bs_init_bits_heap Sz Words Reg bs_init_bits Fail Sz Words=u==0 Regs Flags Dst => \ i_bs_init_bits_fail Sz Fail Regs Dst bs_init_bits Fail Sz Words Regs Flags Dst => \ - i_fetch Sz x=0 | i_bs_init_bits_fail_heap Words Fail Regs Dst + i_bs_init_bits_fail_heap Sz Words Fail Regs Dst i_bs_init_bits_fail x j I d i_bs_init_bits_fail y j I d -i_bs_init_bits_fail_heap I j I d +i_bs_init_bits_fail_heap s I j I d i_bs_init_bits I I d i_bs_init_bits_heap I I I d bs_add Fail S1=i==0 S2 Unit=u==1 D => move S2 D -bs_add Fail S1 S2 Unit D => i_fetch S1 S2 | i_bs_add Fail Unit D -i_bs_add j I d +bs_add j s s I d bs_append Fail Size Extra Live Unit Bin Flags Dst => \ - i_fetch Size Bin | i_bs_append Fail Extra Live Unit Dst + move Bin x | i_bs_append Fail Extra Live Unit Size Dst bs_private_append Fail Size Unit Bin Flags Dst => \ - i_fetch Size Bin | i_bs_private_append Fail Unit Dst + i_bs_private_append Fail Unit Size Bin Dst bs_init_writable -i_bs_append j I I I d -i_bs_private_append j I d +i_bs_append j I I I s d +i_bs_private_append j I s s d # # Storing integers into binaries. -- cgit v1.2.3 From bf2b06d3edd2c855071529be17c0c35e778a88fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 17 Jun 2015 16:33:19 +0200 Subject: Remove the i_fetch instruction --- erts/emulator/beam/beam_emu.c | 2 -- erts/emulator/beam/ops.tab | 15 --------------- 2 files changed, 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 26e28832cf..fe3d8aea28 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -148,8 +148,6 @@ do { \ ASSERT(VALID_INSTR(* (Eterm *)(ip))); \ I = (ip) -#define FetchArgs(S1, S2) tmp_arg1 = (S1); tmp_arg2 = (S2) - /* * Store a result into a register given a destination descriptor. */ diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 637cef0a22..631e10215c 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -486,21 +486,6 @@ put_list c y y put_list s s d %hot -%macro: i_fetch FetchArgs -pack -i_fetch c x -i_fetch c y -i_fetch x c -i_fetch x x -i_fetch x y -i_fetch y c -i_fetch y x -i_fetch y y - -%cold -i_fetch c c -i_fetch s s -%hot - # # Some more only used by the emulator # -- cgit v1.2.3 From 227269c6b37b88fe106d27a5c0ba148c31472822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 17 Jun 2015 19:30:20 +0200 Subject: Eliminate use of tmp_arg1 and tmp_arg2 in bit syntax --- erts/emulator/beam/beam_emu.c | 81 +++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 45 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index fe3d8aea28..89234a9a80 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -989,7 +989,6 @@ init_emulator(void) # define REG_I asm("%l4") # define REG_fcalls asm("%l5") # define REG_tmp_arg1 asm("%l6") -# define REG_tmp_arg2 asm("%l7") #else # define REG_xregs # define REG_htop @@ -997,7 +996,6 @@ init_emulator(void) # define REG_I # define REG_fcalls # define REG_tmp_arg1 -# define REG_tmp_arg2 #endif #ifdef USE_VM_PROBES @@ -1145,7 +1143,6 @@ void process_main(void) * Temporaries used for picking up arguments for instructions. */ register Eterm tmp_arg1 REG_tmp_arg1 = NIL; - register Eterm tmp_arg2 REG_tmp_arg2 = NIL; /* * X registers and floating point registers are located in @@ -4242,55 +4239,49 @@ do { \ } } - /* Operands: x(Reg) Size Live Fail Flags Dst */ - OpCase(i_bs_get_integer_imm_xIIfId): { - tmp_arg1 = xb(Arg(0)); - I++; - /* Operands: Size Live Fail Flags Dst */ - goto do_bs_get_integer_imm_test_heap; - } + { + Eterm Ms, Sz; - /* - * tmp_arg1 = match context - * Operands: Size Live Fail Flags Dst - */ - do_bs_get_integer_imm_test_heap: { - Uint wordsneeded; - tmp_arg2 = Arg(0); - wordsneeded = 1+WSIZE(NBYTES(tmp_arg2)); - TestHeapPreserve(wordsneeded, Arg(1), tmp_arg1); - I += 2; - /* Operands: Fail Flags Dst */ - goto do_bs_get_integer_imm; - } + /* Operands: x(Reg) Size Live Fail Flags Dst */ + OpCase(i_bs_get_integer_imm_xIIfId): { + Uint wordsneeded; + Ms = xb(Arg(0)); + Sz = Arg(1); + wordsneeded = 1+WSIZE(NBYTES(Sz)); + TestHeapPreserve(wordsneeded, Arg(2), Ms); + I += 3; + /* Operands: Fail Flags Dst */ + goto do_bs_get_integer_imm; + } - /* Operands: x(Reg) Size Fail Flags Dst */ + /* Operands: x(Reg) Size Fail Flags Dst */ OpCase(i_bs_get_integer_small_imm_xIfId): { - tmp_arg1 = xb(Arg(0)); - tmp_arg2 = Arg(1); - I += 2; - /* Operands: Fail Flags Dst */ - goto do_bs_get_integer_imm; - } + Ms = xb(Arg(0)); + Sz = Arg(1); + I += 2; + /* Operands: Fail Flags Dst */ + goto do_bs_get_integer_imm; + } - /* - * tmp_arg1 = match context - * tmp_arg2 = size of field - * Operands: Fail Flags Dst - */ + /* + * Ms = match context + * Sz = size of field + * Operands: Fail Flags Dst + */ do_bs_get_integer_imm: { - ErlBinMatchBuffer* mb; - Eterm result; + ErlBinMatchBuffer* mb; + Eterm result; - mb = ms_matchbuffer(tmp_arg1); - LIGHT_SWAPOUT; - result = erts_bs_get_integer_2(c_p, tmp_arg2, Arg(1), mb); - LIGHT_SWAPIN; - HEAP_SPACE_VERIFIED(0); - if (is_non_value(result)) { - ClauseFail(); + mb = ms_matchbuffer(Ms); + LIGHT_SWAPOUT; + result = erts_bs_get_integer_2(c_p, Sz, Arg(1), mb); + LIGHT_SWAPIN; + HEAP_SPACE_VERIFIED(0); + if (is_non_value(result)) { + ClauseFail(); + } + StoreBifResult(2, result); } - StoreBifResult(2, result); } /* -- cgit v1.2.3 From dddd225934cfe79c052e9013a8a2c1d6a29c8ae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 23 Jun 2015 15:24:07 +0200 Subject: Remove the last use of tmp_arg1 --- erts/emulator/beam/beam_emu.c | 102 +++++++++++++++++++----------------------- erts/emulator/beam/ops.tab | 53 ++++++---------------- 2 files changed, 59 insertions(+), 96 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 89234a9a80..7dbf14de02 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -603,47 +603,39 @@ void** beam_ops; H = CAR(tmp_ptr); \ T = CDR(tmp_ptr); } while (0) -#define GetTupleElement(Src, Element, Dest) \ - do { \ - tmp_arg1 = (Eterm) (((unsigned char *) tuple_val(Src)) + (Element));\ - (Dest) = (*(Eterm *) tmp_arg1); \ - } while (0) - -#define ExtractNextElement(Dest) \ - tmp_arg1 += sizeof(Eterm); \ - (Dest) = (* (Eterm *) (((unsigned char *) tmp_arg1))) - -#define ExtractNextElement2(Dest) \ - do { \ - Eterm* ene_dstp = &(Dest); \ - ene_dstp[0] = ((Eterm *) tmp_arg1)[1]; \ - ene_dstp[1] = ((Eterm *) tmp_arg1)[2]; \ - tmp_arg1 += sizeof(Eterm) + sizeof(Eterm); \ - } while (0) - -#define ExtractNextElement3(Dest) \ - do { \ - Eterm* ene_dstp = &(Dest); \ - ene_dstp[0] = ((Eterm *) tmp_arg1)[1]; \ - ene_dstp[1] = ((Eterm *) tmp_arg1)[2]; \ - ene_dstp[2] = ((Eterm *) tmp_arg1)[3]; \ - tmp_arg1 += 3*sizeof(Eterm); \ +#define GetTupleElement(Src, Element, Dest) \ + do { \ + Eterm* src; \ + src = ADD_BYTE_OFFSET(tuple_val(Src), (Element)); \ + (Dest) = *src; \ } while (0) -#define ExtractNextElement4(Dest) \ - do { \ - Eterm* ene_dstp = &(Dest); \ - ene_dstp[0] = ((Eterm *) tmp_arg1)[1]; \ - ene_dstp[1] = ((Eterm *) tmp_arg1)[2]; \ - ene_dstp[2] = ((Eterm *) tmp_arg1)[3]; \ - ene_dstp[3] = ((Eterm *) tmp_arg1)[4]; \ - tmp_arg1 += 4*sizeof(Eterm); \ +#define GetTupleElement2(Src, Element, Dest) \ + do { \ + Eterm* src; \ + Eterm* dst; \ + Eterm E1, E2; \ + src = ADD_BYTE_OFFSET(tuple_val(Src), (Element)); \ + dst = &(Dest); \ + E1 = src[0]; \ + E2 = src[1]; \ + dst[0] = E1; \ + dst[1] = E2; \ } while (0) -#define ExtractElement(Element, Dest) \ - do { \ - tmp_arg1 += (Element); \ - (Dest) = (* (Eterm *) tmp_arg1); \ +#define GetTupleElement3(Src, Element, Dest) \ + do { \ + Eterm* src; \ + Eterm* dst; \ + Eterm E1, E2, E3; \ + src = ADD_BYTE_OFFSET(tuple_val(Src), (Element)); \ + dst = &(Dest); \ + E1 = src[0]; \ + E2 = src[1]; \ + E3 = src[2]; \ + dst[0] = E1; \ + dst[1] = E2; \ + dst[2] = E3; \ } while (0) #define EqualImmed(X, Y, Action) if (X != Y) { Action; } @@ -683,11 +675,9 @@ void** beam_ops; #define IsTuple(X, Action) if (is_not_tuple(X)) Action -#define IsArity(Pointer, Arity, Fail) \ - if (*(Eterm *) \ - (tmp_arg1 = (Eterm) (tuple_val(Pointer))) != (Arity)) \ - { \ - Fail; \ +#define IsArity(Pointer, Arity, Fail) \ + if (*tuple_val(Pointer) != (Arity)) { \ + Fail; \ } #define IsMap(Src, Fail) if (!is_map(Src)) { Fail; } @@ -724,14 +714,21 @@ void** beam_ops; } \ } while (0) -#define IsTupleOfArity(Src, Arity, Fail) \ - do { \ - if (is_not_tuple(Src) || \ - *(Eterm *) \ - (tmp_arg1 = (Eterm) (tuple_val(Src))) != Arity) { \ - Fail; \ - } \ +#ifdef DEBUG +#define IsTupleOfArity(Src, Arityval, Fail) \ + do { \ + if (!(is_tuple(Src) && *tuple_val(Src) == Arityval)) { \ + Fail; \ + } \ } while (0) +#else +#define IsTupleOfArity(Src, Arityval, Fail) \ + do { \ + if (!(is_boxed(Src) && *tuple_val(Src) == Arityval)) { \ + Fail; \ + } \ + } while (0) +#endif #define IsBoolean(X, Fail) if ((X) != am_true && (X) != am_false) { Fail; } @@ -988,14 +985,12 @@ init_emulator(void) # define REG_stop asm("%l3") # define REG_I asm("%l4") # define REG_fcalls asm("%l5") -# define REG_tmp_arg1 asm("%l6") #else # define REG_xregs # define REG_htop # define REG_stop # define REG_I # define REG_fcalls -# define REG_tmp_arg1 #endif #ifdef USE_VM_PROBES @@ -1139,11 +1134,6 @@ void process_main(void) */ register Sint FCALLS REG_fcalls = 0; - /* - * Temporaries used for picking up arguments for instructions. - */ - register Eterm tmp_arg1 REG_tmp_arg1 = NIL; - /* * X registers and floating point registers are located in * scheduler specific data. diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 631e10215c..853ba82f83 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -230,6 +230,12 @@ i_get_tuple_element x P y i_get_tuple_element y P y %hot +%macro: i_get_tuple_element2 GetTupleElement2 -pack +i_get_tuple_element2 x P x + +%macro: i_get_tuple_element3 GetTupleElement3 -pack +i_get_tuple_element3 x P x + %macro: is_number IsNumber -fail_action %cold is_number f x @@ -552,48 +558,15 @@ test_arity Fail=f c Arity => jump Fail test_arity f x A test_arity f y A -is_tuple_of_arity Fail=f Reg Arity | get_tuple_element Reg P=u==0 Dst=xy => \ - is_tuple_of_arity Fail Reg Arity | extract_next_element Dst | original_reg Reg P - -test_arity Fail Reg Arity | get_tuple_element Reg P=u==0 Dst=xy => \ - test_arity Fail Reg Arity | extract_next_element Dst | original_reg Reg P - -original_reg Reg P1 | get_tuple_element Reg P2 Dst=xy | succ(P1, P2) => \ - extract_next_element Dst | original_reg Reg P2 - -get_tuple_element Reg P Dst => i_get_tuple_element Reg P Dst | original_reg Reg P - -original_reg Reg Pos => - -original_reg/2 - -extract_next_element D1=xy | original_reg Reg P1 | get_tuple_element Reg P2 D2=xy | \ -succ(P1, P2) | succ(D1, D2) => \ - extract_next_element2 D1 | original_reg Reg P2 - -extract_next_element2 D1=xy | original_reg Reg P1 | get_tuple_element Reg P2 D2=xy | \ -succ(P1, P2) | succ2(D1, D2) => \ - extract_next_element3 D1 | original_reg Reg P2 - -#extract_next_element3 D1=xy | original_reg Reg P1 | get_tuple_element Reg P2 D2=xy | \ -#succ(P1, P2) | succ3(D1, D2) => \ -# extract_next_element4 D1 | original_reg Reg P2 - -%macro: extract_next_element ExtractNextElement -pack -extract_next_element x -extract_next_element y - -%macro: extract_next_element2 ExtractNextElement2 -pack -extract_next_element2 x -extract_next_element2 y +get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \ + get_tuple_element Reg=x P3 D3=x | \ + succ(P1, P2) | succ(P2, P3) | \ + succ(D1, D2) | succ(D2, D3) => i_get_tuple_element3 Reg P1 D1 -%macro: extract_next_element3 ExtractNextElement3 -pack -extract_next_element3 x -extract_next_element3 y +get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \ + succ(P1, P2) | succ(D1, D2) => i_get_tuple_element2 Reg P1 D1 -#%macro: extract_next_element4 ExtractNextElement4 -pack -#extract_next_element4 x -#extract_next_element4 y +get_tuple_element Reg P Dst => i_get_tuple_element Reg P Dst is_integer Fail=f i => is_integer Fail=f an => jump Fail -- cgit v1.2.3 From d252130b8e880f2f7f826217fe806da76fbcbb7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 10 Apr 2012 12:28:11 +0200 Subject: Rewrite the hipe_mode_switch instructions The 'cmd' variable that were shared by several hipe_mode_switch instructions would cause clang to produce sub-optimal code, probably because it considered the instructions as part of of loop that needed to be optimized. What would was that 'cmd' would be assigned to the ESI register (lower 32 bits of the RSI register). It would use ESI for other purposes in instructions, but at the end of every instruction it would set ESI to 1 just in case the next instruction happened to be hipe_trap_return. This can be seen clearly if this commit is omitted and the define HIPE_MODE_SWITCH_CMD_RETURN in hipe/hipe_mode_switch.h is changed from 1 to some other number such as 42. You will see that 42 is assigned to ESI at the end of every instruction. Eliminate this problem by elimininating the shared 'cmd' variable. --- erts/emulator/beam/beam_emu.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 7dbf14de02..362c6e4826 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4681,7 +4681,12 @@ do { \ #ifdef HIPE { - unsigned cmd; +#define HIPE_MODE_SWITCH(Cmd) \ + SWAPOUT; \ + c_p->fcalls = FCALLS; \ + c_p->def_arg_reg[4] = -neg_o_reds; \ + c_p = hipe_mode_switch(c_p, Cmd, reg); \ + goto L_post_hipe_mode_switch OpCase(hipe_trap_call): { /* @@ -4695,38 +4700,31 @@ do { \ */ ASSERT(I[-5] == (Uint) OpCode(i_func_info_IaaI)); c_p->hipe.u.ncallee = (void(*)(void)) I[-4]; - cmd = HIPE_MODE_SWITCH_CMD_CALL | (I[-1] << 8); ++hipe_trap_count; - goto L_hipe_mode_switch; + HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_CALL | (I[-1] << 8)); } OpCase(hipe_trap_call_closure): { ASSERT(I[-5] == (Uint) OpCode(i_func_info_IaaI)); c_p->hipe.u.ncallee = (void(*)(void)) I[-4]; - cmd = HIPE_MODE_SWITCH_CMD_CALL_CLOSURE | (I[-1] << 8); ++hipe_trap_count; - goto L_hipe_mode_switch; + HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_CALL_CLOSURE | (I[-1] << 8)); } OpCase(hipe_trap_return): { - cmd = HIPE_MODE_SWITCH_CMD_RETURN; - goto L_hipe_mode_switch; + HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_RETURN); } OpCase(hipe_trap_throw): { - cmd = HIPE_MODE_SWITCH_CMD_THROW; - goto L_hipe_mode_switch; + HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_THROW); } OpCase(hipe_trap_resume): { - cmd = HIPE_MODE_SWITCH_CMD_RESUME; - goto L_hipe_mode_switch; + HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_RESUME); } - L_hipe_mode_switch: - /* XXX: this abuse of def_arg_reg[] is horrid! */ - SWAPOUT; - c_p->fcalls = FCALLS; - c_p->def_arg_reg[4] = -neg_o_reds; - c_p = hipe_mode_switch(c_p, cmd, reg); +#undef HIPE_MODE_SWITCH + + L_post_hipe_mode_switch: reg = ERTS_PROC_GET_SCHDATA(c_p)->x_reg_array; freg = ERTS_PROC_GET_SCHDATA(c_p)->f_reg_array; ERL_BITS_RELOAD_STATEP(c_p); + /* XXX: this abuse of def_arg_reg[] is horrid! */ neg_o_reds = -c_p->def_arg_reg[4]; FCALLS = c_p->fcalls; SWAPIN; -- cgit v1.2.3 From 1f73d45327bb13a615f2f0a8d9d4888ddacb95a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 23 Jun 2015 09:38:23 +0200 Subject: Add back frequently used x(0) instructions --- erts/emulator/beam/beam_emu.c | 10 ++++++++++ erts/emulator/beam/ops.tab | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 362c6e4826..bac7057abe 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1298,6 +1298,11 @@ void process_main(void) Uint live; Eterm result; + OpCase(i_increment_rIId): + increment_reg_val = x(0); + I--; + goto do_increment; + OpCase(i_increment_xIId): increment_reg_val = xb(Arg(0)); goto do_increment; @@ -1357,6 +1362,11 @@ void process_main(void) PlusOp2 = xb(Arg(3)); goto do_plus; + OpCase(i_plus_jIxyd): + PlusOp1 = xb(Arg(2)); + PlusOp2 = yb(Arg(3)); + goto do_plus; + OpCase(i_plus_jIssd): GetArg2(2, PlusOp1, PlusOp2); goto do_plus; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 853ba82f83..4ab08faae3 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -204,6 +204,14 @@ get_list y x y get_list y y x get_list y y y +# The following get_list instructions using x(0) are frequently used. +get_list r x x +get_list r r y +get_list x r x +get_list r x y +get_list r y r +get_list r x r + # Old-style catch. catch y f catch_end y @@ -348,6 +356,14 @@ move c x move n x move y y +# The following move instructions using x(0) are frequently used. + +move x r +move r x +move y r +move c r +move r y + # Receive operations. loop_rec Fail x==0 | smp_mark_target_label(Fail) => i_loop_rec Fail @@ -390,6 +406,7 @@ is_ne_exact Lbl R=xy C=ian => i_is_ne_exact_immed Lbl R C is_ne_exact Lbl R=xy C=q => i_is_ne_exact_literal Lbl R C %macro: i_is_eq_exact_immed EqualImmed -fail_action +i_is_eq_exact_immed f r c i_is_eq_exact_immed f x c i_is_eq_exact_immed f y c @@ -478,15 +495,21 @@ put_list x c x put_list x c y put_list y c x -put_list y c y # put_list Constant SrcReg Dst put_list c x x -put_list c x y - put_list c y x -put_list c y y + +# The following put_list instructions using x(0) are frequently used. + +put_list y r r +put_list x r r +put_list r n r +put_list r n x +put_list r x x +put_list r x r +put_list x x r %cold put_list s s d @@ -544,10 +567,12 @@ is_tuple Fail=f S=xy | test_arity Fail=f S=xy Arity => is_tuple_of_arity Fail S %macro:is_tuple_of_arity IsTupleOfArity -fail_action +is_tuple_of_arity f r A is_tuple_of_arity f x A is_tuple_of_arity f y A %macro: is_tuple IsTuple -fail_action +is_tuple f r is_tuple f x is_tuple f y @@ -593,6 +618,7 @@ is_list f y is_nonempty_list Fail=f S=x | allocate Need Rs => is_nonempty_list_allocate Fail S Need Rs %macro:is_nonempty_list_allocate IsNonemptyListAllocate -fail_action -pack +is_nonempty_list_allocate f r I t is_nonempty_list_allocate f x I t is_nonempty_list F=f x==0 | test_heap I1 I2 => is_non_empty_list_test_heap F I1 I2 @@ -1440,6 +1466,7 @@ gen_minus p Live Reg=d Int=i Dst | negation_is_small(Int) => \ # GCing arithmetic instructions. # +gen_plus Fail Live Y=y X=x Dst => i_plus Fail Live X Y Dst gen_plus Fail Live S1 S2 Dst => i_plus Fail Live S1 S2 Dst gen_minus Fail Live S1 S2 Dst => i_minus Fail Live S1 S2 Dst @@ -1471,10 +1498,12 @@ gc_bif2 Fail Live u$bif:erlang:bxor/2 S1 S2 Dst => \ gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst +i_increment r I I d i_increment x I I d i_increment y I I d i_plus j I x x d +i_plus j I x y d i_plus j I s s d i_minus j I x x d -- cgit v1.2.3 From 3dc2bee53b4d36f41821a6ab512cf01c958c11f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 23 Jun 2015 17:31:49 +0200 Subject: Introduce specialized versions of move2 Currently, move2/2 does the two moves sequentially to ensure that the instruction will always work correctly. We can do better than that. If the two move instructions have any registers in common, we can introduce simpler and slightly more efficient instructions to handle those cases: move_shift/3 move_dup/3 For the remaining cases when the the move instructions have no common registers, the move2/4 instruction can perform the moves in parallel which is probably slightly more efficient. For clarity's sake, we will remain the instruction to move2_par/4. --- erts/emulator/beam/beam_emu.c | 18 ++++++++++- erts/emulator/beam/ops.tab | 71 +++++++++++++++++++++++++++++++------------ 2 files changed, 68 insertions(+), 21 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index bac7057abe..2c10e7ae7c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -551,7 +551,23 @@ void** beam_ops; Store(term, Dst); \ } while (0) -#define Move2(S1, D1, S2, D2) D1 = (S1); D2 = (S2) +#define Move2Par(S1, D1, S2, D2) \ + do { \ + Eterm V1, V2; \ + V1 = (S1); V2 = (S2); D1 = V1; D2 = V2; \ + } while (0) + +#define MoveShift(Src, SD, D) \ + do { \ + Eterm V; \ + V = Src; D = SD; SD = V; \ + } while (0) + +#define MoveDup(Src, D1, D2) \ + do { \ + D1 = D2 = (Src); \ + } while (0) + #define Move3(S1, D1, S2, D2, S3, D3) D1 = (S1); D2 = (S2); D3 = (S3) #define MoveReturn(Src) \ diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 4ab08faae3..97525c3b72 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -303,21 +303,40 @@ move_window3 x x x y move_window4 x x x x y move_window5 x x x x x y -move X1=x Y1=y | move X2=x Y2=y => move2 X1 Y1 X2 Y2 -move Y1=y X1=x | move Y2=y X2=x => move2 Y1 X1 Y2 X2 -move X1=x X2=x | move X3=x X4=x => move2 X1 X2 X3 X4 +move Src=x D1=x | move Src=x D2=x => move_dup Src D1 D2 +move Src=x SD=x | move SD=x D=x => move_dup Src SD D +move Src=x D1=x | move Src=x D2=y => move_dup Src D1 D2 +move Src=y SD=x | move SD=x D=y => move_dup Src SD D +move Src=x SD=x | move SD=x D=y => move_dup Src SD D +move Src=y SD=x | move SD=x D=x => move_dup Src SD D -move X1=x X2=x | move X3=x Y1=y => move2 X1 X2 X3 Y1 +move SD=x D=x | move Src=xy SD=x => move_shift Src SD D +move SD=y D=x | move Src=x SD=y => move_shift Src SD D +move SD=x D=y | move Src=x SD=x => move_shift Src SD D -move S1=x S2=x | move X1=x Y1=y => move2 S1 S2 X1 Y1 -move S1=y S2=x | move X1=x Y1=y => move2 S1 S2 X1 Y1 +# The transformations above guarantee that the source for +# the second move is not the same as the destination for +# the first move. That means that we can do the moves in +# parallel (fetch both values, then store them) which could +# be faster. -move Y1=y X1=x | move S1=x D1=x => move2 Y1 X1 S1 D1 -move S1=x D1=x | move Y1=y X1=x => move2 S1 D1 Y1 X1 +move X1=x Y1=y | move X2=x Y2=y => move2_par X1 Y1 X2 Y2 +move Y1=y X1=x | move Y2=y X2=x => move2_par Y1 X1 Y2 X2 -move2 X1=x Y1=y X2=x Y2=y | move X3=x Y3=y => move3 X1 Y1 X2 Y2 X3 Y3 -move2 Y1=y X1=x Y2=y X2=x | move Y3=y X3=x => move3 Y1 X1 Y2 X2 Y3 X3 -move2 X1=x X2=x X3=x X4=x | move X5=x X6=x => move3 X1 X2 X3 X4 X5 X6 +move X1=x X2=x | move X3=x X4=x => move2_par X1 X2 X3 X4 + +move X1=x X2=x | move X3=x Y1=y => move2_par X1 X2 X3 Y1 + +move S1=x S2=x | move X1=x Y1=y => move2_par S1 S2 X1 Y1 + +move S1=y S2=x | move X1=x Y1=y => move2_par S1 S2 X1 Y1 + +move Y1=y X1=x | move S1=x D1=x => move2_par Y1 X1 S1 D1 +move S1=x D1=x | move Y1=y X1=x => move2_par S1 D1 Y1 X1 + +move2_par X1=x Y1=y X2=x Y2=y | move X3=x Y3=y => move3 X1 Y1 X2 Y2 X3 Y3 +move2_par Y1=y X1=x Y2=y X2=x | move Y3=y X3=x => move3 Y1 X1 Y2 X2 Y3 X3 +move2_par X1=x X2=x X3=x X4=x | move X5=x X6=x => move3 X1 X2 X3 X4 X5 X6 move C=aiq X=x==1 => move_x1 C move C=aiq X=x==2 => move_x2 C @@ -325,18 +344,30 @@ move C=aiq X=x==2 => move_x2 C move_x1 c move_x2 c -%macro: move2 Move2 -pack -move2 x y x y -move2 y x y x -move2 x x x x +%macro: move_shift MoveShift -pack +move_shift x x x +move_shift y x x +move_shift x y x +move_shift x x y + +%macro: move_dup MoveDup -pack +move_dup x x x +move_dup x x y +move_dup y x x +move_dup y x y + +%macro: move2_par Move2Par -pack + +move2_par x y x y +move2_par y x y x +move2_par x x x x -move2 x x x y +move2_par x x x y -move2 x y x y -move2 y x x y +move2_par y x x y -move2 x x y x -move2 y x x x +move2_par x x y x +move2_par y x x x %macro: move3 Move3 move3 x y x y x y -- cgit v1.2.3 From 62473daf8169a04a07409f344d938bc51a4536c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 25 Jun 2015 13:21:14 +0200 Subject: Introduce swap_temp/3 and swap/2 Sequences of three move instructionst that effectively swap the contents of two registers are fairly common. We can replace them with a swap_temp/3 instruction. The third operand is the temporary register to be used for swapping, since the temporary register may actually be used. If swap_temp/3 instruction is followed by a call, the temporary register will often (but not always) be killed by the call. If it is killed, we can replace the swap_temp/3 instruction with a slightly cheaper swap/2 instruction. --- erts/emulator/beam/beam_emu.c | 14 ++++++++++++++ erts/emulator/beam/beam_load.c | 6 ++++++ erts/emulator/beam/ops.tab | 28 ++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 2c10e7ae7c..4e6e7aa48a 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -545,6 +545,20 @@ void** beam_ops; HTOP += 2; \ } while (0) +#define Swap(R1, R2) \ + do { \ + Eterm V = R1; \ + R1 = R2; \ + R2 = V; \ + } while (0) + +#define SwapTemp(R1, R2, Tmp) \ + do { \ + Eterm V = R1; \ + R1 = R2; \ + R2 = Tmp = V; \ + } while (0) + #define Move(Src, Dst, Store) \ do { \ Eterm term = (Src); \ diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 871c2b1f55..1e3690fcd6 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -2702,6 +2702,12 @@ same_label(LoaderState* stp, GenOpArg Target, GenOpArg Label) Target.val == Label.val; } +static int +is_killed(LoaderState* stp, GenOpArg Reg, GenOpArg Live) +{ + return Reg.type == TAG_x && Live.type == TAG_u && + Live.val <= Reg.val; +} /* * Generate an instruction for element/2. diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 97525c3b72..386de0a658 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -303,6 +303,34 @@ move_window3 x x x y move_window4 x x x x y move_window5 x x x x x y +# Swap registers. +move R1=x Tmp=x | move R2=xy R1 | move Tmp R2 => swap_temp R1 R2 Tmp + +swap_temp R1 R2 Tmp | line Loc | apply Live | is_killed(Tmp, Live) => \ + swap R1 R2 | line Loc | apply Live + +swap_temp R1 R2 Tmp | line Loc | call Live Addr | is_killed(Tmp, Live) => \ + swap R1 R2 | line Loc | call Live Addr +swap_temp R1 R2 Tmp | call_only Live Addr | \ + is_killed(Tmp, Live) => swap R1 R2 | call_only Live Addr +swap_temp R1 R2 Tmp | call_last Live Addr D | \ + is_killed(Tmp, Live) => swap R1 R2 | call_last Live Addr D + +swap_temp R1 R2 Tmp | line Loc | call_ext Live Addr | is_killed(Tmp, Live) => \ + swap R1 R2 | line Loc | call_ext Live Addr +swap_temp R1 R2 Tmp | line Loc | call_ext_only Live Addr | \ + is_killed(Tmp, Live) => swap R1 R2 | line Loc | call_ext_only Live Addr +swap_temp R1 R2 Tmp | line Loc | call_ext_last Live Addr D | \ + is_killed(Tmp, Live) => swap R1 R2 | line Loc | call_ext_last Live Addr D + +%macro: swap_temp SwapTemp -pack +swap_temp x x x +swap_temp x y x + +%macro: swap Swap -pack +swap x x +swap x y + move Src=x D1=x | move Src=x D2=x => move_dup Src D1 D2 move Src=x SD=x | move SD=x D=x => move_dup Src SD D move Src=x D1=x | move Src=x D2=y => move_dup Src D1 D2 -- cgit v1.2.3 From 5e83d2ae89c731bd7e4c32b550150336008d2974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 25 Jun 2015 16:18:18 +0200 Subject: Use a cheaper tag scheme for 'd' operands Since 'd' operands can only either an X register or an Y register, we only need a single bit to distinguish them. Furthermore, we can pre-multiply the register number with the word size to speed up address calculation. --- erts/emulator/beam/beam_debug.c | 13 ++++------ erts/emulator/beam/beam_emu.c | 57 ++++++++++++++++++----------------------- erts/emulator/beam/beam_load.c | 4 +-- erts/emulator/beam/beam_load.h | 1 + 4 files changed, 33 insertions(+), 42 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 30e3765502..ea1f2cd012 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -475,15 +475,12 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) ap++; break; case 'd': /* Destination (x(0), x(N), y(N)) */ - switch (loader_tag(*ap)) { - case LOADER_X_REG: - erts_print(to, to_arg, "x(%d)", - loader_x_reg_index(*ap)); - break; - case LOADER_Y_REG: + if (*ap & 1) { erts_print(to, to_arg, "y(%d)", - loader_y_reg_index(*ap) - CP_SIZE); - break; + *ap / sizeof(Eterm) - CP_SIZE); + } else { + erts_print(to, to_arg, "x(%d)", + *ap / sizeof(Eterm)); } ap++; break; diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 4e6e7aa48a..60a399a5ac 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -148,23 +148,21 @@ do { \ ASSERT(VALID_INSTR(* (Eterm *)(ip))); \ I = (ip) +/* + * Register target (X or Y register). + */ +#define REG_TARGET(Target) (*(((Target) & 1) ? &yb(Target-1) : &xb(Target))) + /* * Store a result into a register given a destination descriptor. */ -#define StoreResult(Result, DestDesc) \ - do { \ - Eterm stb_reg; \ - stb_reg = (DestDesc); \ - CHECK_TERM(Result); \ - switch (loader_tag(stb_reg)) { \ - case LOADER_X_REG: \ - x(loader_x_reg_index(stb_reg)) = (Result); \ - break; \ - default: \ - y(loader_y_reg_index(stb_reg)) = (Result); \ - break; \ - } \ +#define StoreResult(Result, DestDesc) \ + do { \ + Eterm stb_reg; \ + stb_reg = (DestDesc); \ + CHECK_TERM(Result); \ + REG_TARGET(stb_reg) = (Result); \ } while (0) #define StoreSimpleDest(Src, Dest) Dest = (Src) @@ -175,22 +173,16 @@ do { \ * be just before the next instruction. */ -#define StoreBifResult(Dst, Result) \ - do { \ - BeamInstr* stb_next; \ - Eterm stb_reg; \ - stb_reg = Arg(Dst); \ - I += (Dst) + 2; \ - stb_next = (BeamInstr *) *I; \ - CHECK_TERM(Result); \ - switch (loader_tag(stb_reg)) { \ - case LOADER_X_REG: \ - x(loader_x_reg_index(stb_reg)) = (Result); \ - Goto(stb_next); \ - default: \ - y(loader_y_reg_index(stb_reg)) = (Result); \ - Goto(stb_next); \ - } \ +#define StoreBifResult(Dst, Result) \ + do { \ + BeamInstr* stb_next; \ + Eterm stb_reg; \ + stb_reg = Arg(Dst); \ + I += (Dst) + 2; \ + stb_next = (BeamInstr *) *I; \ + CHECK_TERM(Result); \ + REG_TARGET(stb_reg) = (Result); \ + Goto(stb_next); \ } while (0) #define ClauseFail() goto jump_f @@ -3277,7 +3269,8 @@ do { \ Eterm* p; PreFetch(3, next); - GetArg2(0, element, tuple); + GetArg1(0, element); + tuple = REG_TARGET(Arg(1)); ASSERT(is_tuple(tuple)); p = (Eterm *) ((unsigned char *) tuple_val(tuple) + Arg(2)); *p = element; @@ -4605,7 +4598,7 @@ do { \ BeamInstr *next; PreFetch(2, next); - GetR(0, targ1); + targ1 = REG_TARGET(Arg(0)); /* Arg(0) == HEADER_FLONUM */ GET_DOUBLE(targ1, *(FloatDef*)ADD_BYTE_OFFSET(freg, fr)); NextPF(2, next); @@ -4625,7 +4618,7 @@ do { \ Eterm fr = Arg(1); BeamInstr *next; - GetR(0, targ1); + targ1 = REG_TARGET(Arg(0)); PreFetch(2, next); if (is_small(targ1)) { fb(fr) = (double) signed_val(targ1); diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 1e3690fcd6..a9d47eb7b0 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -2227,10 +2227,10 @@ load_code(LoaderState* stp) case 'd': /* Destination (x(0), x(N), y(N) */ switch (tag) { case TAG_x: - code[ci++] = make_loader_x_reg(tmp_op->a[arg].val); + code[ci++] = tmp_op->a[arg].val * sizeof(Eterm); break; case TAG_y: - code[ci++] = make_loader_y_reg(tmp_op->a[arg].val); + code[ci++] = tmp_op->a[arg].val * sizeof(Eterm) + 1; break; default: LoadError1(stp, "bad tag %d for destination", diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index d5af634fad..eedb5ee4cd 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -52,6 +52,7 @@ extern BeamInstr* em_call_error_handler; extern BeamInstr* em_apply_bif; extern BeamInstr* em_call_nif; + /* * The following variables keep a sorted list of address ranges for * each module. It allows us to quickly find a function given an -- cgit v1.2.3 From 0a4750f91c837d6a68b5a9ad58ffb1fe52b67d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 29 Jun 2015 10:19:53 +0200 Subject: beam_makeops: Eliminate unnecessary masking when packing 3 operands When packing 3 operands into one word, there would be an unnecessary mask operation when extracting the last operand. --- erts/emulator/utils/beam_makeops | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index e61096355c..974b29e9d8 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -48,7 +48,7 @@ $pack_shift[4] = ['0', 'BEAM_LOOSE_SHIFT', # Only for 64 bit wordsize '(3*BEAM_LOOSE_SHIFT)']; $pack_mask[2] = ['BEAM_LOOSE_MASK', $WHOLE_WORD]; -$pack_mask[3] = ['BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK']; +$pack_mask[3] = ['BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK', $WHOLE_WORD]; $pack_mask[4] = ['BEAM_LOOSE_MASK', # Only for 64 bit wordsize 'BEAM_LOOSE_MASK', 'BEAM_LOOSE_MASK', -- cgit v1.2.3 From a52bd8a9ebece53c67024e3ad17a899c22cea2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 29 Jun 2015 15:56:44 +0200 Subject: Ensure that the move_call_ext_{last,only} instructions are used Update transformations to ensure that the move_call_ext_last and move_call_ext_last are used. --- erts/emulator/beam/ops.tab | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 386de0a658..18202a4200 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -76,17 +76,6 @@ return # with the following call instruction, we need to make sure that # there is no line/1 instruction between the move and the call. # - -move S X0=x==0 | line Loc | call_ext Ar Func => \ - line Loc | move S X0 | call_ext Ar Func -move S X0=x==0 | line Loc | call_ext_last Ar Func=u$is_bif D => \ - line Loc | move S X0 | call_ext_last Ar Func D -move S X0=x==0 | line Loc | call_ext_only Ar Func=u$is_bif => \ - line Loc | move S X0 | call_ext_only Ar Func -move S X0=x==0 | line Loc | call Ar Func => \ - line Loc | move S X0 | call Ar Func - -# # A tail-recursive call to an external function (non-BIF) will # never be saved on the stack, so there is no reason to keep # the line instruction. (The compiler did not remove the line @@ -94,10 +83,14 @@ move S X0=x==0 | line Loc | call Ar Func => \ # BIFs and ordinary Erlang functions.) # -line Loc | call_ext_last Ar Func=u$is_not_bif D => \ - call_ext_last Ar Func D -line Loc | call_ext_only Ar Func=u$is_not_bif => \ - call_ext_only Ar Func +move S X0=x==0 | line Loc | call_ext Ar Func => \ + line Loc | move S X0 | call_ext Ar Func +move S X0=x==0 | line Loc | call_ext_last Ar Func=u$is_not_bif D => \ + move S X0 | call_ext_last Ar Func D +move S X0=x==0 | line Loc | call_ext_only Ar Func=u$is_not_bif => \ + move S X0 | call_ext_only Ar Func +move S X0=x==0 | line Loc | call Ar Func => \ + line Loc | move S X0 | call Ar Func line Loc | func_info M F A => func_info M F A | line Loc -- cgit v1.2.3 From fad052472def54fff1268b21313b15bd666437c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Sun, 21 Jun 2015 18:09:52 +0200 Subject: Teach beam_makeops to pack operands for move3 and move_window It is currently only possible to pack up to 4 operands. However, the move_window4 instrucion has 5 operands and move_window5 and move3 instrucations have 6 operands. Teach beam_makeops to pack instructions with 5 or 6 operands. Also rewrite the move_window instructions in beam_emu.c to macros to allow their operands to get packed. --- erts/emulator/beam/beam_emu.c | 88 +++++++++++++++++++--------------------- erts/emulator/beam/ops.tab | 6 ++- erts/emulator/utils/beam_makeops | 30 ++++++++++---- 3 files changed, 68 insertions(+), 56 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 60a399a5ac..a3b38adc4b 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -576,6 +576,48 @@ void** beam_ops; #define Move3(S1, D1, S2, D2, S3, D3) D1 = (S1); D2 = (S2); D3 = (S3) +#define MoveWindow3(S1, S2, S3, D) \ + do { \ + Eterm xt0, xt1, xt2; \ + Eterm *y = &D; \ + xt0 = S1; \ + xt1 = S2; \ + xt2 = S3; \ + y[0] = xt0; \ + y[1] = xt1; \ + y[2] = xt2; \ + } while (0) + +#define MoveWindow4(S1, S2, S3, S4, D) \ + do { \ + Eterm xt0, xt1, xt2, xt3; \ + Eterm *y = &D; \ + xt0 = S1; \ + xt1 = S2; \ + xt2 = S3; \ + xt3 = S4; \ + y[0] = xt0; \ + y[1] = xt1; \ + y[2] = xt2; \ + y[3] = xt3; \ + } while (0) + +#define MoveWindow5(S1, S2, S3, S4, S5, D) \ + do { \ + Eterm xt0, xt1, xt2, xt3, xt4; \ + Eterm *y = &D; \ + xt0 = S1; \ + xt1 = S2; \ + xt2 = S3; \ + xt3 = S4; \ + xt4 = S5; \ + y[0] = xt0; \ + y[1] = xt1; \ + y[2] = xt2; \ + y[3] = xt3; \ + y[4] = xt4; \ + } while (0) + #define MoveReturn(Src) \ x(0) = (Src); \ I = c_p->cp; \ @@ -1466,52 +1508,6 @@ void process_main(void) Next(3); } - OpCase(move_window3_xxxy): { - BeamInstr *next; - Eterm xt0, xt1, xt2; - Eterm *y = (Eterm *)(((unsigned char *)E) + (Arg(3))); - PreFetch(4, next); - xt0 = xb(Arg(0)); - xt1 = xb(Arg(1)); - xt2 = xb(Arg(2)); - y[0] = xt0; - y[1] = xt1; - y[2] = xt2; - NextPF(4, next); - } - OpCase(move_window4_xxxxy): { - BeamInstr *next; - Eterm xt0, xt1, xt2, xt3; - Eterm *y = (Eterm *)(((unsigned char *)E) + (Arg(4))); - PreFetch(5, next); - xt0 = xb(Arg(0)); - xt1 = xb(Arg(1)); - xt2 = xb(Arg(2)); - xt3 = xb(Arg(3)); - y[0] = xt0; - y[1] = xt1; - y[2] = xt2; - y[3] = xt3; - NextPF(5, next); - } - OpCase(move_window5_xxxxxy): { - BeamInstr *next; - Eterm xt0, xt1, xt2, xt3, xt4; - Eterm *y = (Eterm *)(((unsigned char *)E) + (Arg(5))); - PreFetch(6, next); - xt0 = xb(Arg(0)); - xt1 = xb(Arg(1)); - xt2 = xb(Arg(2)); - xt3 = xb(Arg(3)); - xt4 = xb(Arg(4)); - y[0] = xt0; - y[1] = xt1; - y[2] = xt2; - y[3] = xt3; - y[4] = xt4; - NextPF(6, next); - } - OpCase(i_move_call_only_fc): { r(0) = Arg(1); } diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 18202a4200..bce6bacd5c 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -292,6 +292,10 @@ move_window X1=x X2=x X3=x X4=x Y1=y Y4=y | move X5=x Y5=y | succ(Y4,Y5) => \ move_window X1=x X2=x X3=x Y1=y Y3=y => move_window3 X1 X2 X3 Y1 move_window X1=x X2=x X3=x X4=x Y1=y Y4=y => move_window4 X1 X2 X3 X4 Y1 +%macro: move_window3 MoveWindow3 -pack +%macro: move_window4 MoveWindow4 -pack +%macro: move_window5 MoveWindow5 -pack + move_window3 x x x y move_window4 x x x x y move_window5 x x x x x y @@ -390,7 +394,7 @@ move2_par y x x y move2_par x x y x move2_par y x x x -%macro: move3 Move3 +%macro: move3 Move3 -pack move3 x y x y x y move3 y x y x y x move3 x x x x x x diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 974b29e9d8..44977ecffc 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -54,6 +54,11 @@ $pack_mask[4] = ['BEAM_LOOSE_MASK', # Only for 64 bit wordsize 'BEAM_LOOSE_MASK', $WHOLE_WORD]; +# Mapping from packagable arguments to number of packed arguments per +# word. Initialized after the wordsize is known. + +my @args_per_word; + # There are two types of instructions: generic and specific. # The generic instructions are those generated by the Beam compiler. # Corresponding to each generic instruction, there is generally a @@ -236,6 +241,20 @@ while (@ARGV && $ARGV[0] =~ /^-(.*)/) { die "$0: Bad option: -$_\n"; } +# +# Initialize number of arguments per packed word. +# + +$args_per_word[2] = 2; +$args_per_word[3] = 3; +$args_per_word[4] = 2; +$args_per_word[5] = 3; +$args_per_word[6] = 3; + +if ($wordsize == 64) { + $args_per_word[4] = 4; +} + # # Parse the input files. # @@ -736,7 +755,7 @@ sub init_item { print "${sep}NULL"; } elsif (/^\{/) { print "$sep$_"; - } elsif (/^-?\d/) { + } elsif (/^-?\d+$/) { print "$sep$_"; } else { print "$sep\"$_\""; @@ -1105,7 +1124,6 @@ sub do_pack { # Get out of here if too few or too many arguments. # return ('', '', @args) if $packable_args < 2; - &error("too many packable arguments") if $packable_args > 4; my($size) = 0; my($pack_prefix) = ''; @@ -1113,14 +1131,8 @@ sub do_pack { # beginning). my($up) = ''; # Pack commands (storing back while # moving forward). - my $args_per_word; - if ($packable_args < 4 or $wordsize == 64) { - $args_per_word = $packable_args; - } else { - # 4 packable argument, 32 bit wordsize. Need 2 words. - $args_per_word = 2; - } + my $args_per_word = $args_per_word[$packable_args]; my @shift; my @mask; my @instr; -- cgit v1.2.3 From 6d6fce257cf666113e982c0887121739dff5bf1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Sun, 1 Apr 2012 09:28:34 +0200 Subject: Eliminate prefetch for conditional instructions Not pre-fetching in conditional instructions (instructions that use -fail_action) seems to improve performance slightly. The reason for that is that conditional instructions may jump to the failure label, wasting the pre-fetched instruction. Another reason is that that many conditional instructions do a function call, and the register pressure is therefore high. Avoiding the pre-fetch may reduce the register pressure and pontentially result in more efficient code. --- erts/emulator/beam/ops.tab | 2 +- erts/emulator/utils/beam_makeops | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index bce6bacd5c..3d52da77d3 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -477,7 +477,7 @@ i_is_ne_exact_literal f x c i_is_ne_exact_literal f y c is_eq_exact Lbl Y=y X=x => is_eq_exact Lbl X Y -%macro: is_eq_exact EqualExact -fail_action +%macro: is_eq_exact EqualExact -fail_action -pack is_eq_exact f x x is_eq_exact f x y is_eq_exact f s s diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 44977ecffc..06515c6346 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -917,6 +917,7 @@ sub basic_generator { my($var_decls) = ''; my($gen_dest_arg) = 'StoreSimpleDest'; my($i); + my($no_prefetch) = 0; # The following argument types should be included as macro arguments. my(%incl_arg) = ('c' => 1, @@ -1015,6 +1016,7 @@ sub basic_generator { # $flags =~ /-fail_action/ and do { + $no_prefetch = 1; if (!defined $fail_type) { my($i); for ($i = 0; $i < @f_types; $i++) { @@ -1061,6 +1063,12 @@ sub basic_generator { "I += $size + 1;", "goto $goto;", "}"); + } elsif ($no_prefetch) { + $code = join("\n", + "{ $var_decls", + $macro_code, + "Next($size);", + "}", ""); } else { $code = join("\n", "{ $var_decls", -- cgit v1.2.3 From 7bfff20c48cdcabf1eae4b193a374a72977b9d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 2 Jul 2015 14:50:47 +0200 Subject: Eliminate the variable temp_bits at the top scope of process_main() --- erts/emulator/beam/beam_emu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index a3b38adc4b..1ab8c71685 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -819,6 +819,7 @@ void** beam_ops; #define BsGetFieldSize(Bits, Unit, Fail, Target) \ do { \ Sint _signed_size; Uint _uint_size; \ + Uint temp_bits; \ if (is_small(Bits)) { \ _signed_size = signed_val(Bits); \ if (_signed_size < 0) { Fail; } \ @@ -833,6 +834,7 @@ void** beam_ops; #define BsGetUncheckedFieldSize(Bits, Unit, Fail, Target) \ do { \ Sint _signed_size; Uint _uint_size; \ + Uint temp_bits; \ if (is_small(Bits)) { \ _signed_size = signed_val(Bits); \ if (_signed_size < 0) { Fail; } \ @@ -1219,8 +1221,6 @@ void process_main(void) #endif #endif - Uint temp_bits; /* Temporary used by BsSkipBits2 & BsGetInteger2 */ - Eterm pt_arity; /* Used by do_put_tuple */ Uint64 start_time = 0; /* Monitor long schedule */ -- cgit v1.2.3 From e820b8534fd440aaf4991f3c57b8990ce836c8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Sat, 4 Jul 2015 08:58:33 +0200 Subject: Speed up list matching The combination is_non_empty_list followed by get_list is extremly common (but not in estone_SUITE, which is why it has not been noticed before). Therefore it is worthwile to introduce a combined instruction. --- erts/emulator/beam/beam_emu.c | 11 +++++++++++ erts/emulator/beam/ops.tab | 7 +++++++ 2 files changed, 18 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 1ab8c71685..989066a1ca 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -737,6 +737,17 @@ void** beam_ops; if (is_not_list(x(0))) { Fail; } \ TestHeap(Need, Alive) +#define IsNonemptyListGetList(Src, H, T, Fail) \ + if (is_not_list(Src)) { \ + Fail; \ + } else { \ + Eterm* tmp_ptr = list_val(Src); \ + Eterm hd, tl; \ + hd = CAR(tmp_ptr); \ + tl = CDR(tmp_ptr); \ + H = hd; T = tl; \ + } + #define IsTuple(X, Action) if (is_not_tuple(X)) Action #define IsArity(Pointer, Arity, Fail) \ diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 3d52da77d3..46fefb88af 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -682,6 +682,13 @@ is_nonempty_list F=f x==0 | test_heap I1 I2 => is_non_empty_list_test_heap F I1 %macro: is_non_empty_list_test_heap IsNonemptyListTestHeap -fail_action -pack is_non_empty_list_test_heap f I t +is_nonempty_list Fail=f S=x | get_list S D1=x D2=x => \ + is_nonempty_list_get_list Fail S D1 D2 + +%macro: is_nonempty_list_get_list IsNonemptyListGetList -fail_action -pack +is_nonempty_list_get_list f r x x +is_nonempty_list_get_list f x x x + %macro: is_nonempty_list IsNonemptyList -fail_action is_nonempty_list f x is_nonempty_list f y -- cgit v1.2.3 From c5755ae5ded3ba27dc5d884303ead232abc77a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Sat, 4 Jul 2015 17:10:14 +0200 Subject: Slightly tweak the peformance for get_list Fetch the head and tail parts to temporary variables before writing them to their destinations. That should allow the CPU to perform the moves in parallel, which might improve performance. --- erts/emulator/beam/beam_emu.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 989066a1ca..a296713a0a 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -662,10 +662,14 @@ void** beam_ops; SET_I((BeamInstr *) Arg(0)); \ Goto(*I); -#define GetList(Src, H, T) do { \ - Eterm* tmp_ptr = list_val(Src); \ - H = CAR(tmp_ptr); \ - T = CDR(tmp_ptr); } while (0) +#define GetList(Src, H, T) \ + do { \ + Eterm* tmp_ptr = list_val(Src); \ + Eterm hd, tl; \ + hd = CAR(tmp_ptr); \ + tl = CDR(tmp_ptr); \ + H = hd; T = tl; \ + } while (0) #define GetTupleElement(Src, Element, Dest) \ do { \ -- cgit v1.2.3 From 1ffb2647ffcc29170b461c31c018c5d2b046beae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 6 Jul 2015 11:27:45 +0200 Subject: Improve unpacking performance on x86_64 When unpacking operands on 64-bit CPUs, use a smarter mask to help the compiler optimize the code. It turns out that on x86_64, if we use the mask 0xFFFFUL (selecting the 16 least significant bits), the compiler can combine a move and a mask operation into the single insruction 'movzwl', which will eliminate one instruction. --- erts/emulator/utils/beam_makeops | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 06515c6346..f805e7cc64 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -629,8 +629,8 @@ sub emulator_output { print "\n"; print "#ifdef ARCH_64\n"; print "# define BEAM_WIDE_MASK 0xFFFFUL\n"; - print "# define BEAM_LOOSE_MASK 0x1FFFUL\n"; - print "# define BEAM_TIGHT_MASK 0x1FF8UL\n"; + print "# define BEAM_LOOSE_MASK 0xFFFFUL\n"; + print "# define BEAM_TIGHT_MASK 0xFFFFUL\n"; print "# define BEAM_WIDE_SHIFT 32\n"; print "# define BEAM_LOOSE_SHIFT 16\n"; print "# define BEAM_TIGHT_SHIFT 16\n"; -- cgit v1.2.3 From 6ef7131e497afa22d6e87e3c8082ee861a56781b Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 6 Jul 2015 20:24:17 +0200 Subject: Test case testing crash in tracer port --- erts/emulator/test/trace_port_SUITE.erl | 45 +++++++++++++++++++++- .../emulator/test/trace_port_SUITE_data/echo_drv.c | 12 ++++++ 2 files changed, 55 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index 99df8da107..67f2441b5b 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -34,7 +34,8 @@ fake_schedule_after_getting_linked/1, fake_schedule_after_getting_unlinked/1, gc/1, - default_tracer/1]). + default_tracer/1, + tracer_port_crash/1]). -include_lib("test_server/include/test_server.hrl"). @@ -44,7 +45,7 @@ test_cases() -> fake_schedule_after_register, fake_schedule_after_getting_linked, fake_schedule_after_getting_unlinked, gc, - default_tracer]. + default_tracer, tracer_port_crash]. suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -472,6 +473,42 @@ default_tracer(Config) when is_list(Config) -> ?line M = N, ok. +tracer_port_crash(Config) when is_list(Config) -> + case test_server:is_native(?MODULE) orelse + test_server:is_native(lists) of + true -> + {skip,"Native code"}; + false -> + Tr = start_tracer(Config), + Port = get(tracer_port), + Tracee = spawn(fun () -> + register(trace_port_linker, self()), + link(Port), + receive go -> ok end, + lists:reverse([1,b,c]), + receive die -> ok end + end), + Tr ! {unlink_tracer_port, self()}, + receive {unlinked_tracer_port, Tr} -> ok end, + port_control(Port, $c, []), %% Make port commands crash tracer port... + trace_func({lists,reverse,1}, []), + trace_pid(Tracee, true, [call]), + trace_info(Tracee, flags), + trace_info(self(), tracer), + Tracee ! go, + receive after 1000 -> ok end, + case whereis(trace_port_linker) of + undefined -> + ok; + Id -> +% erts_debug:set_internal_state(available_internal_state, true), +% erts_debug:set_internal_state(abort, {trace_port_linker, Id}) + ?t:fail({trace_port_linker, Id}) + end, + undefined = process_info(Tracee), + ok + end. + %%% Help functions. huge_data() -> huge_data(16384). @@ -630,6 +667,10 @@ tracer_loop(RelayTo, Port) -> {Port,{data,Msg}} -> RelayTo ! binary_to_term(Msg), tracer_loop(RelayTo, Port); + {unlink_tracer_port, From} -> + unlink(Port), + From ! {unlinked_tracer_port, self()}, + tracer_loop(RelayTo, Port); Other -> exit({bad_message,Other}) end. diff --git a/erts/emulator/test/trace_port_SUITE_data/echo_drv.c b/erts/emulator/test/trace_port_SUITE_data/echo_drv.c index a8d4ede4fe..e40b9193ea 100644 --- a/erts/emulator/test/trace_port_SUITE_data/echo_drv.c +++ b/erts/emulator/test/trace_port_SUITE_data/echo_drv.c @@ -1,5 +1,6 @@ #include #include "erl_driver.h" +#include @@ -14,6 +15,7 @@ enum e_heavy { typedef struct _erl_drv_data { ErlDrvPort erlang_port; enum e_heavy heavy; + int crash; } EchoDrvData; static EchoDrvData echo_drv_data, *echo_drv_data_p; @@ -78,6 +80,7 @@ static EchoDrvData *echo_drv_start(ErlDrvPort port, char *command) echo_drv_data_p = &echo_drv_data; echo_drv_data_p->erlang_port = port; echo_drv_data_p->heavy = heavy_off; + echo_drv_data_p->crash = 0; return echo_drv_data_p; } @@ -87,6 +90,12 @@ static void echo_drv_stop(EchoDrvData *data_p) { static void echo_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) { EchoDrvData* data_p = (EchoDrvData *) drv_data; + + if (data_p->crash) { + driver_failure_posix(data_p->erlang_port, EINTR); + return; + } + driver_output(data_p->erlang_port, buf, len); switch (data_p->heavy) { case heavy_off: @@ -100,6 +109,7 @@ static void echo_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) { data_p->heavy = heavy_off; break; } + } static void echo_drv_finish() { @@ -115,6 +125,8 @@ static ErlDrvSSizeT echo_drv_control(ErlDrvData drv_data, case 'h': data_p->heavy = heavy_set; break; + case 'c': + data_p->crash = 1; } return 0; } -- cgit v1.2.3 From e98fb1920ad053d2db4594c5d33cdfcfcdc6d771 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 6 Jul 2015 20:25:28 +0200 Subject: Teach non-smp VM how to deal with trace port crash --- erts/emulator/beam/beam_emu.c | 44 +++++++++++++++++++++++++++++++++++++++- erts/emulator/beam/erl_process.c | 16 +++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 8bfb7d2ad2..a4e9fe1cba 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2082,6 +2082,22 @@ void process_main(void) OpCase(wait_f): wait2: { +#ifndef ERTS_SMP + if (ERTS_PROC_IS_EXITING(c_p)) { + /* + * I non smp case: + * + * Currently executing process might be sent an exit + * signal if it is traced by a port that it also is + * linked to, and the port terminates during the + * trace. In this case we do *not* want to clear + * the active flag, which will make the process hang + * in limbo forever. + */ + SWAPOUT; + goto do_schedule; + } +#endif c_p->i = (BeamInstr *) Arg(0); /* L1 */ SWAPOUT; c_p->arity = 0; @@ -6110,6 +6126,23 @@ erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* re int arity; Eterm tmp; +#ifndef ERTS_SMP + if (ERTS_PROC_IS_EXITING(c_p)) { + /* + * I non smp case: + * + * Currently executing process might be sent an exit + * signal if it is traced by a port that it also is + * linked to, and the port terminates during the + * trace. In this case we do *not* want to clear + * the active flag, which will make the process hang + * in limbo forever. Get out of here and terminate + * the process... + */ + return -1; + } +#endif + if (is_not_atom(module) || is_not_atom(function)) { /* * No need to test args here -- done below. @@ -6186,7 +6219,16 @@ 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); -#ifdef ERTS_SMP +#ifndef ERTS_SMP + if (ERTS_PROC_IS_EXITING(c_p)) { + /* + * See comment in the begining of the function... + * + * This second test is needed since gc might be traced. + */ + return -1; + } +#else /* ERTS_SMP */ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); if (!c_p->msg.len) #endif diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index ea63d20dfa..b6ad8575cf 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11082,6 +11082,22 @@ set_proc_exiting(Process *p, cancel_timer(p); p->i = (BeamInstr *) beam_exit; +#ifndef ERTS_SMP + if (state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)) { + /* + * I non smp case: + * + * Currently executing process might be sent an exit + * signal if it is traced by a port that it also is + * linked to, and the port terminates during the + * trace. In this case we want schedule out the + * process as quickly as possible in order to detect + * the event as fast as possible. + */ + ERTS_VBUMP_ALL_REDS(p); + } +#endif + if (enqueue) add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), state, -- cgit v1.2.3 From fb8d80f91ebfc328c3229a5908c3b4d527bfc1e6 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 6 Jul 2015 21:14:34 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 23 +++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index adc73ceae0..5682b9254c 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,29 @@

This document describes the changes made to the ERTS application.

+
Erts 6.4.1.2 + +
Fixed Bugs and Malfunctions + + +

+ A process could end up in an inconsistent half exited + state in the runtime system without SMP support. This + could occur if the processes was traced by a port that it + also was linked to, and the port terminated abnormally + while handling a trace message for the process.

+

+ This bug has always existed in the runtime system without + SMP support, but never in the runtime system with SMP + support.

+

+ Own Id: OTP-12889 Aux Id: seq12885

+
+
+
+ +
+
Erts 6.4.1.1
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index b806cb0b7a..35f40995d5 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.4.1.1 +VSN = 6.4.1.2 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From f7da0720b17556a9e3df108463643ba10ab1b3a0 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 7 Jul 2015 15:58:43 +0200 Subject: Add the --enable-gettimeofday-as-os-system-time configure switch Forces usage of gettimeofday() for OS system time --- erts/aclocal.m4 | 57 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 01541aff72..dd5d3979a7 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -2202,7 +2202,7 @@ AC_DEFUN(ERL_TIME_CORRECTION, AC_ARG_WITH(clock-resolution, AS_HELP_STRING([--with-clock-resolution=high|low|default], - [specify wanted clock resolution)])) + [specify wanted clock resolution])) AC_ARG_WITH(clock-gettime-realtime-id, AS_HELP_STRING([--with-clock-gettime-realtime-id=CLOCKID], @@ -2212,6 +2212,14 @@ AC_ARG_WITH(clock-gettime-monotonic-id, AS_HELP_STRING([--with-clock-gettime-monotonic-id=CLOCKID], [specify clock id to use with clock_gettime() for monotonic time)])) +AC_ARG_ENABLE(gettimeofday-as-os-system-time, + AS_HELP_STRING([--enable-gettimeofday-as-os-system-time], + [Force usage of gettimeofday() for OS system time]), +[ case "$enableval" in + yes) force_gettimeofday_os_system_time=yes ;; + *) force_gettimeofday_os_system_time=no ;; + esac ], force_gettimeofday_os_system_time=no) + case "$with_clock_resolution" in ""|no|yes) with_clock_resolution=default;; @@ -2222,6 +2230,17 @@ case "$with_clock_resolution" in ;; esac +if test "$force_gettimeofday_os_system_time" = "yes"; then + + AC_CHECK_FUNCS([gettimeofday]) + if test "$ac_cv_func_gettimeofday" = "yes"; then + AC_DEFINE(OS_SYSTEM_TIME_GETTIMEOFDAY, [1], [Define if you want to implement erts_os_system_time() using gettimeofday()]) + else + AC_MSG_ERROR([No gettimeofday() available]) + fi + +else # $force_gettimeofday_os_system_time != yes + case "$with_clock_gettime_realtime_id" in ""|no) with_clock_gettime_realtime_id=no @@ -2239,23 +2258,6 @@ case "$with_clock_gettime_realtime_id" in ;; esac -case "$with_clock_gettime_monotonic_id" in - ""|no) - with_clock_gettime_monotonic_id=no - ;; - CLOCK_*CPUTIME*) - AC_MSG_ERROR([Invalid clock_gettime() monotonic clock id: Refusing to use the cputime clock id $with_clock_gettime_monotonic_id as monotonic clock id]) - ;; - CLOCK_REALTIME*|CLOCK_TAI*) - AC_MSG_ERROR([Invalid clock_gettime() monotonic clock id: Refusing to use the realtime clock id $with_clock_gettime_monotonic_id as monotonic clock id]) - ;; - CLOCK_*) - ;; - *) - AC_MSG_ERROR([Invalid clock_gettime() clock id: $with_clock_gettime_monotonic_id]) - ;; -esac - case "$with_clock_resolution-$with_clock_gettime_realtime_id" in high-no) ERL_WALL_CLOCK(high_resolution);; @@ -2296,6 +2298,25 @@ if test "x$erl_wall_clock_id" != "x"; then AC_DEFINE_UNQUOTED(WALL_CLOCK_ID, [$erl_wall_clock_id], [Define to wall clock id to use]) fi +fi # $force_gettimeofday_os_system_time != yes + +case "$with_clock_gettime_monotonic_id" in + ""|no) + with_clock_gettime_monotonic_id=no + ;; + CLOCK_*CPUTIME*) + AC_MSG_ERROR([Invalid clock_gettime() monotonic clock id: Refusing to use the cputime clock id $with_clock_gettime_monotonic_id as monotonic clock id]) + ;; + CLOCK_REALTIME*|CLOCK_TAI*) + AC_MSG_ERROR([Invalid clock_gettime() monotonic clock id: Refusing to use the realtime clock id $with_clock_gettime_monotonic_id as monotonic clock id]) + ;; + CLOCK_*) + ;; + *) + AC_MSG_ERROR([Invalid clock_gettime() clock id: $with_clock_gettime_monotonic_id]) + ;; +esac + case "$with_clock_resolution-$with_clock_gettime_monotonic_id" in high-no) ERL_MONOTONIC_CLOCK(high_resolution);; -- cgit v1.2.3 From b3b9d321ce0622e1d774ad54e9700e22edde8abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 8 Jul 2015 16:13:30 +0200 Subject: Fix crash when disassembling modules with BIFs In a debug-compiled emulator, running erts_debug:df(io) would trigger an assertion failure: 1> erts_debug:df(io). beam/beam_debug.c:301:erts_debug_disassemble_1() Assertion failed: (((funcinfo[0]) & 0x3F) == ((0x0 << 4) | ((0x2 << 2) | 0x3))) Aborted (core dumped) It turns out that the assertion is wrong. It should have been updated in 64ccd8c9b7a7 which made it possible to have stubs for BIFs in the BEAM code for a module. The faulty assertion was only found when when 16317f73f79265 added a smoke test of the BEAM disassembler. --- erts/emulator/beam/beam_debug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 8a35ad17c6..c774a70d4c 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -298,8 +298,8 @@ erts_debug_disassemble_1(BIF_ALIST_1) (void) erts_bld_uword(NULL, &hsz, (BeamInstr) code_ptr); hp = HAlloc(p, hsz); addr = erts_bld_uword(&hp, NULL, (BeamInstr) code_ptr); - ASSERT(is_atom(funcinfo[0])); - ASSERT(is_atom(funcinfo[1])); + ASSERT(is_atom(funcinfo[0]) || funcinfo[0] == NIL); + ASSERT(is_atom(funcinfo[1]) || funcinfo[1] == NIL); mfa = TUPLE3(hp, (Eterm) funcinfo[0], (Eterm) funcinfo[1], make_small((Eterm) funcinfo[2])); hp += 4; return TUPLE3(hp, addr, bin, mfa); -- cgit v1.2.3 From 9b44550e9f1b8bf49d447152625f5b3999649034 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 7 Jul 2015 21:27:51 +0200 Subject: Avoid unnecessary copying of data when retrieving corrected monotonic time --- erts/emulator/beam/erl_time_sup.c | 85 +++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 44 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 7f8f560681..7327e0b48c 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -124,7 +124,11 @@ typedef struct { typedef struct { ErtsMonotonicCorrectionInstance prev; - ErtsMonotonicCorrectionInstance curr; + ErtsMonotonicCorrectionInstance curr; +} ErtsMonotonicCorrectionInstances; + +typedef struct { + ErtsMonotonicCorrectionInstances insts; ErtsMonotonicDriftData drift; ErtsMonotonicTime last_check; int short_check_interval; @@ -272,27 +276,24 @@ static ERTS_INLINE ErtsMonotonicTime read_corrected_time(int os_drift_corrected) { ErtsMonotonicTime os_mtime; - ErtsMonotonicCorrectionData cdata; - ErtsMonotonicCorrectionInstance *cip; + ErtsMonotonicCorrectionInstance ci; erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); os_mtime = erts_os_monotonic_time(); - cdata = time_sup.inf.c.parmon.cdata; - - erts_smp_rwmtx_runlock(&time_sup.inf.c.parmon.rwmtx); - - if (os_mtime >= cdata.curr.os_mtime) - cip = &cdata.curr; + if (os_mtime >= time_sup.inf.c.parmon.cdata.insts.curr.os_mtime) + ci = time_sup.inf.c.parmon.cdata.insts.curr; else { - if (os_mtime < cdata.prev.os_mtime) + if (os_mtime < time_sup.inf.c.parmon.cdata.insts.prev.os_mtime) erl_exit(ERTS_ABORT_EXIT, "OS monotonic time stepped backwards\n"); - cip = &cdata.prev; + ci = time_sup.inf.c.parmon.cdata.insts.prev; } - return calc_corrected_erl_mtime(os_mtime, cip, NULL, + erts_smp_rwmtx_runlock(&time_sup.inf.c.parmon.rwmtx); + + return calc_corrected_erl_mtime(os_mtime, &ci, NULL, os_drift_corrected); } @@ -360,9 +361,8 @@ check_time_correction(void *vesdp) { int init_drift_adj = !vesdp; ErtsSchedulerData *esdp = (ErtsSchedulerData *) vesdp; - ErtsMonotonicCorrectionData cdata; ErtsMonotonicCorrection new_correction; - ErtsMonotonicCorrectionInstance *cip; + ErtsMonotonicCorrectionInstance ci; ErtsMonotonicTime mdiff, sdiff, os_mtime, erl_mtime, os_stime, erl_stime, time_offset, timeout_pos; Uint timeout; @@ -373,16 +373,15 @@ check_time_correction(void *vesdp) erts_os_times(&os_mtime, &os_stime); - cdata = time_sup.inf.c.parmon.cdata; + ci = time_sup.inf.c.parmon.cdata.insts.curr; erts_smp_rwmtx_runlock(&time_sup.inf.c.parmon.rwmtx); - if (os_mtime < cdata.curr.os_mtime) + if (os_mtime < ci.os_mtime) erl_exit(ERTS_ABORT_EXIT, "OS monotonic time stepped backwards\n"); - cip = &cdata.curr; - erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, &mdiff, + erl_mtime = calc_corrected_erl_mtime(os_mtime, &ci, &mdiff, os_drift_corrected); time_offset = get_time_offset(); erl_stime = erl_mtime + time_offset; @@ -397,7 +396,7 @@ check_time_correction(void *vesdp) time_sup.inf.c.shadow_offset = 0; } - new_correction = cip->correction; + new_correction = ci.correction; if (time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE && (sdiff < -2*time_sup.r.o.adj.small_diff @@ -408,7 +407,7 @@ check_time_correction(void *vesdp) set_time_offset(time_offset); schedule_send_time_offset_changed_notifications(time_offset); begin_short_intervals = 1; - if (cdata.curr.correction.error != 0) { + if (ci.correction.error != 0) { set_new_correction = 1; new_correction.error = 0; } @@ -425,12 +424,12 @@ check_time_correction(void *vesdp) time_sup.inf.c.shadow_offset -= sdiff; sdiff = 0; begin_short_intervals = 1; - if (cdata.curr.correction.error != 0) { + if (ci.correction.error != 0) { set_new_correction = 1; new_correction.error = 0; } } - else if (cdata.curr.correction.error == 0) { + else if (ci.correction.error == 0) { if (sdiff < -time_sup.r.o.adj.small_diff) { set_new_correction = 1; if (sdiff < -time_sup.r.o.adj.large_diff) @@ -446,9 +445,9 @@ check_time_correction(void *vesdp) new_correction.error = -ERTS_TCORR_ERR_SMALL_ADJ; } } - else if (cdata.curr.correction.error > 0) { + else if (ci.correction.error > 0) { if (sdiff < 0) { - if (cdata.curr.correction.error != ERTS_TCORR_ERR_LARGE_ADJ + if (ci.correction.error != ERTS_TCORR_ERR_LARGE_ADJ && sdiff < -time_sup.r.o.adj.large_diff) { new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; set_new_correction = 1; @@ -466,9 +465,9 @@ check_time_correction(void *vesdp) new_correction.error = 0; } } - else /* if (cdata.curr.correction.error < 0) */ { + else /* if (ci.correction.error < 0) */ { if (0 < sdiff) { - if (cdata.curr.correction.error != -ERTS_TCORR_ERR_LARGE_ADJ + if (ci.correction.error != -ERTS_TCORR_ERR_LARGE_ADJ && time_sup.r.o.adj.large_diff < sdiff) { new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; set_new_correction = 1; @@ -631,8 +630,8 @@ check_time_correction(void *vesdp) #ifdef ERTS_TIME_CORRECTION_PRINT print_correction(set_new_correction, sdiff, - cip->correction.error, - cip->correction.drift, + ci.correction.error, + ci.correction.drift, new_correction.error, new_correction.drift, timeout); @@ -644,7 +643,7 @@ check_time_correction(void *vesdp) os_mtime = erts_os_monotonic_time(); /* Save previous correction instance */ - time_sup.inf.c.parmon.cdata.prev = *cip; + time_sup.inf.c.parmon.cdata.insts.prev = ci; /* * Current correction instance begin when @@ -657,15 +656,15 @@ check_time_correction(void *vesdp) * next OS monotonic time using previous * correction. */ - erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, NULL, + erl_mtime = calc_corrected_erl_mtime(os_mtime, &ci, NULL, os_drift_corrected); /* * Save new current correction instance. */ - time_sup.inf.c.parmon.cdata.curr.erl_mtime = erl_mtime; - time_sup.inf.c.parmon.cdata.curr.os_mtime = os_mtime; - time_sup.inf.c.parmon.cdata.curr.correction = new_correction; + time_sup.inf.c.parmon.cdata.insts.curr.erl_mtime = erl_mtime; + time_sup.inf.c.parmon.cdata.insts.curr.os_mtime = os_mtime; + time_sup.inf.c.parmon.cdata.insts.curr.correction = new_correction; erts_smp_rwmtx_rwunlock(&time_sup.inf.c.parmon.rwmtx); } @@ -784,24 +783,22 @@ static ErtsMonotonicTime finalize_corrected_time_offset(ErtsSystemTime *stimep) { ErtsMonotonicTime os_mtime; - ErtsMonotonicCorrectionData cdata; - ErtsMonotonicCorrectionInstance *cip; + ErtsMonotonicCorrectionInstance ci; int os_drift_corrected = time_sup.r.o.os_corrected_monotonic_time; erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); erts_os_times(&os_mtime, stimep); - cdata = time_sup.inf.c.parmon.cdata; + ci = time_sup.inf.c.parmon.cdata.insts.curr; erts_smp_rwmtx_runlock(&time_sup.inf.c.parmon.rwmtx); - if (os_mtime < cdata.curr.os_mtime) + if (os_mtime < ci.os_mtime) erl_exit(ERTS_ABORT_EXIT, "OS monotonic time stepped backwards\n"); - cip = &cdata.curr; - return calc_corrected_erl_mtime(os_mtime, cip, NULL, + return calc_corrected_erl_mtime(os_mtime, &ci, NULL, os_drift_corrected); } @@ -1128,13 +1125,13 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) cdatap->drift.intervals[0].time.sys = time_sup.inf.c.sinit; cdatap->drift.intervals[0].time.mon = time_sup.inf.c.minit; - cdatap->curr.correction.drift = 0; - cdatap->curr.correction.error = 0; - cdatap->curr.erl_mtime = ERTS_MONOTONIC_BEGIN; - cdatap->curr.os_mtime = time_sup.inf.c.minit; + cdatap->insts.curr.correction.drift = 0; + cdatap->insts.curr.correction.error = 0; + cdatap->insts.curr.erl_mtime = ERTS_MONOTONIC_BEGIN; + cdatap->insts.curr.os_mtime = time_sup.inf.c.minit; cdatap->last_check = time_sup.inf.c.minit; cdatap->short_check_interval = ERTS_INIT_SHORT_INTERVAL_COUNTER; - cdatap->prev = cdatap->curr; + cdatap->insts.prev = cdatap->insts.curr; if (!time_sup.r.o.os_corrected_monotonic_time) time_sup.r.o.get_time = get_corrected_time; -- cgit v1.2.3 From 4a864c1cbe16a42f3f5190881187e3c9849e985f Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 7 Jul 2015 22:32:38 +0200 Subject: Prefer monotonic time that stop during suspend --- erts/aclocal.m4 | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index dd5d3979a7..0714ce6030 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -726,9 +726,15 @@ esac AC_DEFUN(ERL_MONOTONIC_CLOCK, [ - default_resolution_clock_gettime_monotonic="CLOCK_HIGHRES CLOCK_BOOTTIME CLOCK_MONOTONIC" - low_resolution_clock_gettime_monotonic="CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_FAST" - high_resolution_clock_gettime_monotonic="CLOCK_MONOTONIC_PRECISE" + if test "$3" = "yes"; then + default_resolution_clock_gettime_monotonic="CLOCK_HIGHRES CLOCK_BOOTTIME CLOCK_MONOTONIC" + low_resolution_clock_gettime_monotonic="CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_FAST" + high_resolution_clock_gettime_monotonic="CLOCK_MONOTONIC_PRECISE" + else + default_resolution_clock_gettime_monotonic="CLOCK_HIGHRES CLOCK_UPTIME CLOCK_MONOTONIC" + low_resolution_clock_gettime_monotonic="CLOCK_MONOTONIC_COARSE CLOCK_UPTIME_FAST" + high_resolution_clock_gettime_monotonic="CLOCK_UPTIME_PRECISE" + fi case "$1" in high_resolution) @@ -1466,7 +1472,7 @@ AC_ARG_WITH(with_sparc_memory_order, LM_CHECK_THR_LIB ERL_INTERNAL_LIBS -ERL_MONOTONIC_CLOCK(high_resolution) +ERL_MONOTONIC_CLOCK(high_resolution, undefined, no) case $erl_monotonic_clock_func in clock_gettime) @@ -2212,6 +2218,16 @@ AC_ARG_WITH(clock-gettime-monotonic-id, AS_HELP_STRING([--with-clock-gettime-monotonic-id=CLOCKID], [specify clock id to use with clock_gettime() for monotonic time)])) +AC_ARG_ENABLE(prefer-elapsed-monotonic-time-during-suspend, + AS_HELP_STRING([--enable-prefer-elapsed-monotonic-time-during-suspend], + [Prefer an OS monotonic time source with elapsed time during suspend]) + AS_HELP_STRING([--disable-prefer-elapsed-monotonic-time-during-suspend], + [Do not prefer an OS monotonic time source with elapsed time during suspend]), +[ case "$enableval" in + yes) prefer_elapsed_monotonic_time_during_suspend=yes ;; + *) prefer_elapsed_monotonic_time_during_suspend=no ;; + esac ], prefer_elapsed_monotonic_time_during_suspend=no) + AC_ARG_ENABLE(gettimeofday-as-os-system-time, AS_HELP_STRING([--enable-gettimeofday-as-os-system-time], [Force usage of gettimeofday() for OS system time]), @@ -2319,13 +2335,13 @@ esac case "$with_clock_resolution-$with_clock_gettime_monotonic_id" in high-no) - ERL_MONOTONIC_CLOCK(high_resolution);; + ERL_MONOTONIC_CLOCK(high_resolution, undefined, $prefer_elapsed_monotonic_time_during_suspend);; low-no) - ERL_MONOTONIC_CLOCK(low_resolution);; + ERL_MONOTONIC_CLOCK(low_resolution, undefined, $prefer_elapsed_monotonic_time_during_suspend);; default-no) - ERL_MONOTONIC_CLOCK(default_resolution);; + ERL_MONOTONIC_CLOCK(default_resolution, undefined, $prefer_elapsed_monotonic_time_during_suspend);; *) - ERL_MONOTONIC_CLOCK(custom_resolution, $with_clock_gettime_monotonic_id);; + ERL_MONOTONIC_CLOCK(custom_resolution, $with_clock_gettime_monotonic_id, $prefer_elapsed_monotonic_time_during_suspend);; esac case "$erl_monotonic_clock_func-$erl_monotonic_clock_id-$with_clock_gettime_monotonic_id" in @@ -2373,7 +2389,7 @@ if test $erl_cv_clock_gettime_monotonic_raw = yes; then AC_DEFINE(HAVE_CLOCK_GETTIME_MONOTONIC_RAW, [1], [Define if you have clock_gettime(CLOCK_MONOTONIC_RAW, _)]) fi -ERL_MONOTONIC_CLOCK(high_resolution) +ERL_MONOTONIC_CLOCK(high_resolution, undefined, no) case $$erl_monotonic_clock_low_resolution-$erl_monotonic_clock_func in no-mach_clock_get_time) -- cgit v1.2.3 From a782ed0ca14952dd83560ad7c5a43888bef19cdb Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 8 Jul 2015 20:05:20 +0200 Subject: Fix calculation of end time --- erts/emulator/beam/erl_time.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 36a3d52264..43e543e035 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -345,8 +345,10 @@ erts_time_unit_conversion(Uint64 value, #endif /* !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */ #define ERTS_MONOTONIC_TIME_END_EXTERNAL \ - (ERTS_MONOTONIC_TIME_START_EXTERNAL \ - + (ERTS_MONOTONIC_END - ERTS_MONOTONIC_BEGIN)) + (ERTS_MONOTONIC_TIME_START_EXTERNAL < 0 \ + ? (ERTS_MONOTONIC_TIME_START_EXTERNAL \ + + (ERTS_MONOTONIC_END - ERTS_MONOTONIC_BEGIN)) \ + : (ERTS_MONOTONIC_END - ERTS_MONOTONIC_TIME_START_EXTERNAL)) #define ERTS_MSEC_TO_CLKTCKS__(MON) \ ((MON) * (ERTS_CLKTCK_RESOLUTION/1000)) -- cgit v1.2.3 From 5d6aea209c3dafa1bfdb35ea559809185b060bfa Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Wed, 8 Jul 2015 20:31:29 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 77 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 64de3aa622..ab6291614c 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,82 @@

This document describes the changes made to the ERTS application.

+
Erts 7.0.2 + +
Fixed Bugs and Malfunctions + + +

+ A process could end up in an inconsistent half exited + state in the runtime system without SMP support. This + could occur if the processes was traced by a port that it + also was linked to, and the port terminated abnormally + while handling a trace message for the process.

+

+ This bug has always existed in the runtime system without + SMP support, but never in the runtime system with SMP + support.

+

+ Own Id: OTP-12889 Aux Id: seq12885

+
+ +

+ Removed unnecessary copying of data when retrieving + corrected Erlang monotonic time.

+

+ Own Id: OTP-12894

+
+ +

+ Changed default OS monotonic clock source chosen at build + time. This in order to improve performance. The behavior + will now on most systems be that (both OS and Erlang) + monotonic time stops when the system is suspended.

+

+ If you prefer that monotonic time elapse during suspend + of the machine, you can pass the command line argument + --enable-prefer-elapsed-monotonic-time-during-suspend + to configure when building Erlang/OTP. The + configuration stage will try to find such a clock source, + but might not be able to find it. Note that there might + be a performance penalty associated with such a clock + source.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-12895

+
+ +

+ erlang:system_info(end_time) returned a faulty + value on 32-bit architectures.

+

+ Own Id: OTP-12896

+
+
+
+ + +
Improvements and New Features + + +

+ The configure command line argument + --enable-gettimeofday-as-os-system-time has been + added which force usage of gettimeofday() for OS + system time. This will improve performance of + os:system_time() and os:timestamp() on + MacOS X, at the expense of worse accuracy, resolution and + precision of Erlang monotonic time, Erlang system time, + and OS system time.

+

+ Own Id: OTP-12892

+
+
+
+ +
+
Erts 7.0.1
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 985834a801..478f581f13 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 7.0.1 +VSN = 7.0.2 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From c431a065ba515d27830f01c852f70940efb3003b Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 2 Jul 2015 11:13:32 +0200 Subject: ose: Remove all code related to the OSE port The OSE port is no longer supported and this commit removed it and any changes related to it. The things that were general improvements have been left in the code. --- erts/aclocal.m4 | 48 +- erts/configure.in | 25 +- erts/doc/src/driver_entry.xml | 8 +- erts/doc/src/erl_driver.xml | 6 +- erts/doc/src/erlang.xml | 2 +- erts/doc/src/run_erl.xml | 2 +- erts/emulator/Makefile.in | 45 - erts/emulator/beam/atom.names | 7 - erts/emulator/beam/erl_alloc.types | 15 - erts/emulator/beam/erl_async.c | 1 - erts/emulator/beam/erl_driver.h | 10 - erts/emulator/beam/erl_port_task.c | 2 +- erts/emulator/beam/erl_process.c | 68 +- erts/emulator/beam/erl_process.h | 7 - erts/emulator/beam/erl_trace.c | 6 - erts/emulator/beam/io.c | 4 - erts/emulator/beam/sys.h | 4 +- erts/emulator/drivers/common/efile_drv.c | 6 - erts/emulator/drivers/common/inet_drv.c | 573 +-------- erts/emulator/drivers/ose/ose_efile.c | 1125 ----------------- erts/emulator/drivers/ose/ose_signal_drv.c | 897 -------------- erts/emulator/drivers/ose/ttsl_drv.c | 69 -- erts/emulator/sys/common/erl_check_io.c | 24 - erts/emulator/sys/common/erl_poll.h | 22 +- erts/emulator/sys/ose/beam.lmconf | 26 - erts/emulator/sys/ose/driver_int.h | 42 - erts/emulator/sys/ose/erl_main.c | 54 - erts/emulator/sys/ose/erl_ose_sys.h | 356 ------ erts/emulator/sys/ose/erl_ose_sys_ddll.c | 127 -- erts/emulator/sys/ose/erl_poll.c | 818 ------------ erts/emulator/sys/ose/erts.sig | 17 - erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf | 182 --- erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf | 242 ---- erts/emulator/sys/ose/sys.c | 1847 ---------------------------- erts/emulator/sys/ose/sys_float.c | 845 ------------- erts/emulator/sys/ose/sys_time.c | 57 - erts/emulator/test/emulator.spec.ose | 2 - erts/epmd/src/Makefile.in | 37 +- erts/epmd/src/epmd.c | 2 +- erts/epmd/src/epmd_int.h | 18 +- erts/epmd/src/epmd_srv.c | 7 +- erts/etc/common/Makefile.in | 83 +- erts/etc/common/run_erl_common.c | 696 ----------- erts/etc/common/run_erl_common.h | 97 -- erts/etc/common/run_erl_vsn.h | 30 - erts/etc/common/safe_string.c | 123 -- erts/etc/common/safe_string.h | 65 - erts/etc/common/to_erl_common.c | 717 ----------- erts/etc/common/to_erl_common.h | 29 - erts/etc/ose/etc.lmconf | 20 - erts/etc/ose/run_erl.c | 664 ---------- erts/etc/ose/run_erl.h | 30 - erts/etc/ose/run_erl_main.c | 80 -- erts/etc/unix/run_erl.c | 610 ++++++++- erts/etc/unix/run_erl.h | 31 + erts/etc/unix/safe_string.c | 124 ++ erts/etc/unix/safe_string.h | 66 + erts/etc/unix/to_erl.c | 591 ++++++++- erts/include/internal/ethr_mutex.h | 8 +- erts/include/internal/ethread.h | 96 +- erts/include/internal/ose/ethr_event.h | 114 -- erts/lib_src/common/erl_misc_utils.c | 6 - erts/lib_src/common/ethr_aux.c | 15 - erts/lib_src/common/ethr_mutex.c | 2 +- erts/lib_src/ose/ethr_event.c | 220 ---- erts/lib_src/ose/ethread.c | 833 ------------- 66 files changed, 1436 insertions(+), 11569 deletions(-) delete mode 100644 erts/emulator/drivers/ose/ose_efile.c delete mode 100644 erts/emulator/drivers/ose/ose_signal_drv.c delete mode 100644 erts/emulator/drivers/ose/ttsl_drv.c delete mode 100644 erts/emulator/sys/ose/beam.lmconf delete mode 100644 erts/emulator/sys/ose/driver_int.h delete mode 100644 erts/emulator/sys/ose/erl_main.c delete mode 100644 erts/emulator/sys/ose/erl_ose_sys.h delete mode 100644 erts/emulator/sys/ose/erl_ose_sys_ddll.c delete mode 100644 erts/emulator/sys/ose/erl_poll.c delete mode 100644 erts/emulator/sys/ose/erts.sig delete mode 100644 erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf delete mode 100644 erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf delete mode 100644 erts/emulator/sys/ose/sys.c delete mode 100644 erts/emulator/sys/ose/sys_float.c delete mode 100644 erts/emulator/sys/ose/sys_time.c delete mode 100644 erts/emulator/test/emulator.spec.ose delete mode 100644 erts/etc/common/run_erl_common.c delete mode 100644 erts/etc/common/run_erl_common.h delete mode 100644 erts/etc/common/run_erl_vsn.h delete mode 100644 erts/etc/common/safe_string.c delete mode 100644 erts/etc/common/safe_string.h delete mode 100644 erts/etc/common/to_erl_common.c delete mode 100644 erts/etc/common/to_erl_common.h delete mode 100644 erts/etc/ose/etc.lmconf delete mode 100644 erts/etc/ose/run_erl.c delete mode 100644 erts/etc/ose/run_erl.h delete mode 100644 erts/etc/ose/run_erl_main.c create mode 100644 erts/etc/unix/run_erl.h create mode 100644 erts/etc/unix/safe_string.c create mode 100644 erts/etc/unix/safe_string.h delete mode 100644 erts/include/internal/ose/ethr_event.h delete mode 100644 erts/lib_src/ose/ethr_event.c delete mode 100644 erts/lib_src/ose/ethread.c (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 01541aff72..fc2078025e 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -74,21 +74,6 @@ AC_ARG_VAR(erl_xcomp_clock_gettime_cpu_time, [clock_gettime() can be used for re AC_ARG_VAR(erl_xcomp_after_morecore_hook, [__after_morecore_hook can track malloc()s core memory usage: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_dlsym_brk_wrappers, [dlsym(RTLD_NEXT, _) brk wrappers can track malloc()s core memory usage: yes|no (only used when cross compiling)]) -dnl Cross compilation variables for OSE -AC_ARG_VAR(erl_xcomp_ose_ldflags_pass1, [Linker flags for the OSE module (pass 1) (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_ldflags_pass2, [Linker flags for the OSE module (pass 2) (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_OSEROOT, [OSE installation root directory (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_STRIP, [Strip utility shipped with the OSE distribution(only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)]) -AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file]) -AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file]) - ]) AC_DEFUN(ERL_XCOMP_SYSROOT_INIT, @@ -503,8 +488,6 @@ AC_CACHE_VAL(ac_cv_sys_ipv6_support, #ifdef __WIN32__ #include #include -#elif __OSE__ -#error "no ipv6" #else #include #endif], @@ -517,8 +500,6 @@ else #ifdef __WIN32__ #include #include -#elif __OSE__ -#error "no ipv6" #else #include #endif], @@ -985,12 +966,6 @@ if test "X$host_os" = "Xwin32"; then THR_LIBS= THR_LIB_NAME=win32_threads THR_LIB_TYPE=win32_threads -elif test "X$host_os" = "Xose"; then - AC_MSG_RESULT(yes) - THR_DEFS="-DOSE_THREADS" - THR_LIBS= - THR_LIB_NAME=ose_threads - THR_LIB_TYPE=ose_threads else AC_MSG_RESULT(no) THR_DEFS= @@ -1577,22 +1552,9 @@ case "$THR_LIB_NAME" in fi ;; - pthread|ose_threads) - case "$THR_LIB_NAME" in - pthread) - ETHR_THR_LIB_BASE_DIR=pthread - AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads]) - ;; - ose_threads) - AC_DEFINE(ETHR_OSE_THREADS, 1, - [Define if you have OSE style threads]) - ETHR_THR_LIB_BASE_DIR=ose - AC_CHECK_HEADER(ose_spi/ose_spi.h, - AC_DEFINE(HAVE_OSE_SPI_H, 1, - [Define if you have the "ose_spi/ose_spi.h" header file.])) - ;; - esac - if test "x$THR_LIB_NAME" = "xpthread"; then + pthread) + ETHR_THR_LIB_BASE_DIR=pthread + AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads]) case $host_os in openbsd*) # The default stack size is insufficient for our needs @@ -1651,7 +1613,6 @@ case "$THR_LIB_NAME" in *) ;; esac - fi dnl We sometimes need ETHR_DEFS in order to find certain headers dnl (at least for pthread.h on osf1). saved_cppflags="$CPPFLAGS" @@ -1696,7 +1657,6 @@ case "$THR_LIB_NAME" in dnl dnl Check for functions dnl - if test "x$THR_LIB_NAME" = "xpthread"; then AC_CHECK_FUNC(pthread_spin_lock, \ [ethr_have_native_spinlock=yes \ AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \ @@ -1916,8 +1876,6 @@ case "$THR_LIB_NAME" in esac CFLAGS=$old_CFLAGS - fi ## test "x$THR_LIB_NAME" = "xpthread" - if test "X$disable_native_ethr_impls" = "Xyes"; then ethr_have_native_atomics=no else diff --git a/erts/configure.in b/erts/configure.in index 293ff825f8..6950915a6a 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -897,10 +897,7 @@ dnl what the user say. This might not be the right way to do it, but dnl for now that is the way we do it. USER_LD=$LD USER_LDFLAGS="$LDFLAGS" -case $host in - *ose) ;; - *) LD='$(CC)' ;; -esac +LD='$(CC)' AC_SUBST(LD) LDFLAG_RUNTIME_LIBRARY_PATH="$CFLAG_RUNTIME_LIBRARY_PATH" @@ -915,8 +912,6 @@ dnl This is the os flavour, should be unix, ose, vxworks or win32 case $host in win32) ERLANG_OSTYPE=win32 ;; - *ose) - ERLANG_OSTYPE=ose ;; *) ERLANG_OSTYPE=unix ;; esac @@ -1225,7 +1220,7 @@ case "$enable_threads"-"$found_threads" in AC_MSG_RESULT(yes; enabled by user) ;; unknown-yes) case $host_os in - solaris*|linux*|darwin*|win32|ose) + solaris*|linux*|darwin*|win32) emu_threads=yes AC_MSG_RESULT(yes; default on this platform) ;; @@ -1307,7 +1302,7 @@ else enable_child_waiter_thread=no fi ;; - win32|ose) + win32) # Child waiter thread cannot be enabled disable_child_waiter_thread=yes enable_child_waiter_thread=no @@ -2071,7 +2066,7 @@ AC_CHECK_FUNCS([getipnodebyname getipnodebyaddr gethostbyname2]) AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlopen \ pread pwrite memmove strerror strerror_r strncasecmp \ gethrtime localtime_r gmtime_r inet_pton \ - memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \ + mmap mremap memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \ flockfile fstat strlcpy strlcat setsid posix2time time2posix \ setlocale nl_langinfo poll mlockall ppoll]) @@ -2123,17 +2118,6 @@ case $host_os in AC_CHECK_FUNCS([writev]) ;; esac -case $host_os in - *ose) - AC_MSG_CHECKING([for mmap]) - AC_MSG_RESULT(not using for OSE) - AC_MSG_CHECKING([for mremap]) - AC_MSG_RESULT(not using for OSE) ;; - *) - AC_CHECK_FUNCS([mmap mremap]) ;; -esac - - AC_CHECK_DECLS([posix2time, time2posix],,,[#include ]) disable_vfork=false @@ -4887,7 +4871,6 @@ AC_OUTPUT( Makefile:Makefile.in ../make/$host/otp.mk:../make/otp.mk.in ../make/$host/otp_ded.mk:../make/otp_ded.mk.in - ../make/$host/ose_lm.mk:../make/ose_lm.mk.in dnl dnl The ones below should be moved to their respective lib dnl diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml index 30772c68fe..32fc9e13a4 100644 --- a/erts/doc/src/driver_entry.xml +++ b/erts/doc/src/driver_entry.xml @@ -246,14 +246,10 @@ typedef struct erl_drv_entry { something that the WaitForMultipleObjects API function understands). (Some trickery in the emulator allows more than the built-in limit of 64 Events to be used.)

-

On Enea OSE the event is one or more signals that can - be retrieved using erl_drv_ose_get_signal.

To use this with threads and asynchronous routines, create a - pipe on unix, an Event on Windows or a unique signal number on - Enea OSE. When the routine + pipe on unix and an Event on Windows. When the routine completes, write to the pipe (use SetEvent on - Windows or send a message to the emulator process on Enea OSE), - this will make the emulator call + Windows), this will make the emulator call ready_input or ready_output.

Spurious events may happen. That is, calls to ready_input or ready_output even though no real events are signaled. In diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 1f7fe0f961..3bf18ae8d0 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -1044,9 +1044,7 @@ typedef struct ErlIOVec { select/poll can use). On windows, the Win32 API function WaitForMultipleObjects is used. This places other restrictions on the event object. - Refer to the Win32 SDK documentation. - On Enea OSE, the receive function is used. See the for more details.

+ Refer to the Win32 SDK documentation.

The on parameter should be 1 for setting events and 0 for clearing them.

The mode argument is a bitwise-or combination of @@ -1058,7 +1056,7 @@ typedef struct ErlIOVec { ready_output.

-

Some OS (Windows and Enea OSE) do not differentiate between read and write events. +

Some OS (Windows) do not differentiate between read and write events. The call-back for a fired event then only depends on the value of mode.

ERL_DRV_USE specifies if we are using the event object or if we want to close it. diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 37f0aa289e..e77532463e 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1054,7 +1054,7 @@ Print a term on standard output

Prints a text representation of Term on the standard - output. On OSE the term is printed to the ramlog.

+ output.

This BIF is intended for debugging only.

diff --git a/erts/doc/src/run_erl.xml b/erts/doc/src/run_erl.xml index 0a5b2c6136..faec3c68c1 100644 --- a/erts/doc/src/run_erl.xml +++ b/erts/doc/src/run_erl.xml @@ -59,7 +59,7 @@ first argument to run_erl on the command line. pipe_dir This is where to put the named pipe, usually - on Unix or on OSE. It shall be suffixed by a (slash), + . It shall be suffixed by a (slash), i.e. not , but . log_dir This is where the log files are written. There will be one diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index a919f0e3ac..c5080d5b5d 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -23,10 +23,6 @@ include ../vsn.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk -include $(TARGET)/gen_git_version.mk -ifeq ($(findstring ose,$(TARGET)),ose) -include $(ERL_TOP)/make/$(TARGET)/ose_lm.mk -endif - ENABLE_ALLOC_TYPE_VARS = @ENABLE_ALLOC_TYPE_VARS@ HIPE_ENABLED=@HIPE_ENABLED@ DTRACE_ENABLED=@DTRACE_ENABLED@ @@ -245,9 +241,7 @@ HCC = @HCC@ LD = @LD@ DEXPORT = @DEXPORT@ RANLIB = @RANLIB@ -ifneq ($(findstring ose,$(TARGET)),ose) STRIP = strip -endif PERL = @PERL@ RM = @RM@ MKDIR = @MKDIR@ @@ -684,14 +678,6 @@ $(OBJDIR)/%.o: $(TTF_DIR)/%.c $(OBJDIR)/%.o: sys/$(ERLANG_OSTYPE)/%.c $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ -ifeq ($(findstring ose,$(TARGET)),ose) -$(OBJDIR)/ose_confd.o: $(OSE_CONFD) - $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ - -$(OBJDIR)/crt0_lm.o: $(CRT0_LM) - $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ -endif - $(OBJDIR)/%.o: sys/common/%.c $(V_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@ @@ -809,29 +795,6 @@ OS_OBJS = \ $(OBJDIR)/sys_env.o \ $(OBJDIR)/dosmap.o -else -ifeq ($(findstring ose,$(TARGET)),ose) -OS_OBJS = \ - $(OBJDIR)/sys.o \ - $(OBJDIR)/driver_tab.o \ - $(OBJDIR)/ose_efile.o \ - $(OBJDIR)/gzio.o \ - $(OBJDIR)/elib_memmove.o - -OS_OBJS += $(OBJDIR)/ose_confd.o \ - $(OBJDIR)/crt0_lm.o - -OS_OBJS += $(OBJDIR)/sys_float.o \ - $(OBJDIR)/sys_time.o - -DRV_OBJS = \ - $(OBJDIR)/efile_drv.o \ - $(OBJDIR)/ose_signal_drv.o \ - $(OBJDIR)/inet_drv.o \ - $(OBJDIR)/zlib_drv.o \ - $(OBJDIR)/ram_file_drv.o \ - $(OBJDIR)/ttsl_drv.o - else OS_OBJS = \ $(OBJDIR)/sys.o \ @@ -849,7 +812,6 @@ DRV_OBJS = \ $(OBJDIR)/ram_file_drv.o \ $(OBJDIR)/ttsl_drv.o endif -endif ifneq ($(STATIC_NIFS),no) STATIC_NIF_LIBS = $(STATIC_NIFS) @@ -1021,12 +983,6 @@ $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) $(STATIC_NIF_LIBS) \ $(STATIC_DRIVER_LIBS) $(LIBS) -else -ifeq ($(findstring ose,$(TARGET)),ose) -$(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(LCF) - $(call build-ose-load-module, $@, $(INIT_OBJS) $(OBJS), $(STATIC_NIF_LIBS) \ - $(STATIC_DRIVER_LIBS) $(LIBS), $(BEAM_LMCONF)) - else $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(ld_verbose)$(PURIFY) $(LD) -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \ @@ -1034,7 +990,6 @@ $(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(STATIC_NIF_LIBS) $(STATIC_DRIVER_LIBS) $(LIBS) endif -endif # ---------------------------------------------------------------------- # Dependencies diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index f9a2f3e33e..6328b3d18f 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -437,13 +437,6 @@ atom orelse atom os_pid atom os_type atom os_version -atom ose_bg_proc -atom ose_int_proc -atom ose_phantom -atom ose_pri_proc -atom ose_process_prio -atom ose_process_type -atom ose_ti_proc atom out atom out_exited atom out_exiting diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 1d5dec4807..4804fb407d 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -394,21 +394,6 @@ type SYS_WRITE_BUF BINARY SYSTEM sys_write_buf +endif -+if ose - -type SYS_READ_BUF TEMPORARY SYSTEM sys_read_buf -type FD_TAB LONG_LIVED SYSTEM fd_tab -type FD_ENTRY_BUF STANDARD SYSTEM fd_entry_buf -type FD_SIG_LIST SHORT_LIVED SYSTEM fd_sig_list -type DRV_EV STANDARD SYSTEM driver_event -type CS_PROG_PATH LONG_LIVED SYSTEM cs_prog_path -type ENVIRONMENT TEMPORARY SYSTEM environment -type PUTENV_STR SYSTEM SYSTEM putenv_string -type PRT_REP_EXIT STANDARD SYSTEM port_report_exit - -+endif - - +if win32 type DRV_DATA_BUF SYSTEM SYSTEM drv_data_buf diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index f071898046..be0bc0cfec 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -167,7 +167,6 @@ async_ready_q(Uint sched_id) #endif - void erts_init_async(void) { diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index ef2d41e7a2..e71b87803b 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -690,16 +690,6 @@ EXTERN char *driver_dl_error(void); EXTERN int erl_drv_putenv(char *key, char *value); EXTERN int erl_drv_getenv(char *key, char *value, size_t *value_size); -#ifdef __OSE__ -typedef ErlDrvUInt ErlDrvOseEventId; -EXTERN union SIGNAL *erl_drv_ose_get_signal(ErlDrvEvent ev); -EXTERN ErlDrvEvent erl_drv_ose_event_alloc(SIGSELECT sig, ErlDrvOseEventId handle, - ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig), void *extra); -EXTERN void erl_drv_ose_event_free(ErlDrvEvent ev); -EXTERN void erl_drv_ose_event_fetch(ErlDrvEvent ev, SIGSELECT *sig, - ErlDrvOseEventId *handle, void **extra); -#endif - #endif /* !ERL_DRIVER_TYPES_ONLY */ #ifdef WIN32_DYNAMIC_ERL_DRIVER diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 5c38db1cbc..2c09834d19 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -1734,7 +1734,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) reds = ERTS_PORT_REDS_INPUT; ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0); DTRACE_DRIVER(driver_ready_input, pp); - /* NOTE some windows/ose drivers use ->ready_input + /* NOTE some windows drivers use ->ready_input for input and output */ (*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data, ptp->u.alive.td.io.event); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 135f09c04d..5b13f74dcb 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -58,11 +58,7 @@ #define ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST (CONTEXT_REDS/10) -#ifndef ERTS_SCHED_MIN_SPIN #define ERTS_SCHED_SPIN_UNTIL_YIELD 100 -#else -#define ERTS_SCHED_SPIN_UNTIL_YIELD 1 -#endif #define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_LONG 40 #define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_VERY_LONG 1000 @@ -152,12 +148,7 @@ extern BeamInstr beam_apply[]; extern BeamInstr beam_exit[]; extern BeamInstr beam_continue_exit[]; -#ifdef __OSE__ -/* Eager check I/O not supported on OSE yet. */ -int erts_eager_check_io = 0; -#else int erts_eager_check_io = 1; -#endif int erts_sched_compact_load; int erts_sched_balance_util = 0; Uint erts_no_schedulers; @@ -2521,19 +2512,10 @@ try_set_sys_scheduling(void) #endif static ERTS_INLINE int -prepare_for_sys_schedule(ErtsSchedulerData *esdp, int non_blocking) +prepare_for_sys_schedule(int non_blocking) { if (non_blocking && erts_eager_check_io) { #ifdef ERTS_SMP -#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 - if (esdp->no != 1) { - /* If we are not scheduler 1 and ERTS_SCHED_ONLY_POLL_SCHED_1 is used - then we make sure to wake scheduler 1 */ - ErtsRunQueue *rq = ERTS_RUNQ_IX(0); - wake_scheduler(rq); - return 0; - } -#endif return try_set_sys_scheduling(); #else return 1; @@ -2543,16 +2525,6 @@ prepare_for_sys_schedule(ErtsSchedulerData *esdp, int non_blocking) #ifdef ERTS_SMP while (!erts_port_task_have_outstanding_io_tasks() && try_set_sys_scheduling()) { -#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 - if (esdp->no != 1) { - /* If we are not scheduler 1 and ERTS_SCHED_ONLY_POLL_SCHED_1 is used - then we make sure to wake scheduler 1 */ - ErtsRunQueue *rq = ERTS_RUNQ_IX(0); - clear_sys_scheduling(); - wake_scheduler(rq); - return 0; - } -#endif if (!erts_port_task_have_outstanding_io_tasks()) return 1; clear_sys_scheduling(); @@ -2876,8 +2848,6 @@ aux_thread(void *unused) erts_thr_progress_active(NULL, thr_prgr_active = 0); erts_thr_progress_prepare_wait(NULL); - ERTS_SCHED_FAIR_YIELD(); - flgs = sched_spin_wait(ssi, 0); if (flgs & ERTS_SSI_FLG_SLEEPING) { @@ -2945,7 +2915,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * be waiting in erl_sys_schedule() */ - if (ERTS_SCHEDULER_IS_DIRTY(esdp) || !prepare_for_sys_schedule(esdp, 0)) { + if (ERTS_SCHEDULER_IS_DIRTY(esdp) || !prepare_for_sys_schedule(0)) { sched_waiting(esdp->no, rq); @@ -3010,8 +2980,6 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erts_thr_progress_prepare_wait(esdp); } - ERTS_SCHED_FAIR_YIELD(); - flgs = sched_spin_wait(ssi, spincount); if (flgs & ERTS_SSI_FLG_SLEEPING) { ASSERT(flgs & ERTS_SSI_FLG_WAITING); @@ -3082,13 +3050,8 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) #ifdef ERTS_DIRTY_SCHEDULERS ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); #endif - -#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1 - ASSERT(esdp->no == 1); -#endif sched_waiting_sys(esdp->no, rq); - erts_smp_runq_unlock(rq); ASSERT(working); @@ -3158,7 +3121,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * Got to check that we still got I/O tasks; otherwise * we have to continue checking for I/O... */ - if (!prepare_for_sys_schedule(esdp, 0)) { + if (!prepare_for_sys_schedule(0)) { spincount *= ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT; goto tse_wait; } @@ -3180,7 +3143,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * Got to check that we still got I/O tasks; otherwise * we have to wait in erl_sys_schedule() after all... */ - if (!prepare_for_sys_schedule(esdp, 0)) { + if (!prepare_for_sys_schedule(0)) { /* * Not allowed to wait in erl_sys_schedule; * do tse wait instead... @@ -5301,17 +5264,11 @@ erts_early_init_scheduling(int no_schedulers) wakeup_other.threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM; wakeup_other.type = ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT; #endif -#ifndef ERTS_SCHED_MIN_SPIN sched_busy_wait.sys_schedule = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM; sched_busy_wait.tse = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM * ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT); sched_busy_wait.aux_work = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM * ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_MEDIUM); -#else - sched_busy_wait.sys_schedule = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE; - sched_busy_wait.tse = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE; - sched_busy_wait.aux_work = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE; -#endif } int @@ -8187,18 +8144,12 @@ erts_start_schedulers(void) erts_snprintf(opts.name, 16, "%lu_scheduler", actual + 1); -#ifdef __OSE__ - /* This should be done in the bind strategy */ - opts.coreNo = (actual+1) % ose_num_cpus(); -#endif - res = ethr_thr_create(&esdp->tid, sched_thread_func, (void*)esdp, &opts); if (res != 0) { break; } } - erts_no_schedulers = actual; #ifdef ERTS_DIRTY_SCHEDULERS @@ -8227,10 +8178,6 @@ erts_start_schedulers(void) erts_snprintf(opts.name, 16, "aux"); -#ifdef __OSE__ - opts.coreNo = 0; -#endif /* __OSE__ */ - res = ethr_thr_create(&aux_tid, aux_thread, NULL, &opts); if (res != 0) erl_exit(1, "Failed to create aux thread\n"); @@ -8250,7 +8197,6 @@ erts_start_schedulers(void) actual, actual == 1 ? " was" : "s were"); erts_send_error_to_logger_nogl(dsbufp); } - } #endif /* ERTS_SMP */ @@ -9391,12 +9337,10 @@ Process *schedule(Process *p, int calls) int leader_update = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : erts_thr_progress_update(esdp); aux_work = erts_atomic32_read_acqb(&esdp->ssi->aux_work); - if (aux_work | leader_update | ERTS_SCHED_FAIR) { + if (aux_work | leader_update) { erts_smp_runq_unlock(rq); if (leader_update) erts_thr_progress_leader_update(esdp); - else if (ERTS_SCHED_FAIR) - ERTS_SCHED_FAIR_YIELD(); if (aux_work) handle_aux_work(&esdp->aux_work_data, aux_work, 0); erts_smp_runq_lock(rq); @@ -9472,7 +9416,7 @@ Process *schedule(Process *p, int calls) } else if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && (fcalls > input_reductions && - prepare_for_sys_schedule(esdp, !0))) { + prepare_for_sys_schedule(!0))) { ErtsMonotonicTime current_time; /* * Schedule system-level activities. diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 20ffe7ea7c..caac89c087 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -691,13 +691,6 @@ extern ErtsAlignedSchedulerData *erts_aligned_dirty_io_scheduler_data; extern ErtsSchedulerData *erts_scheduler_data; #endif -#ifdef ERTS_SCHED_FAIR -#define ERTS_SCHED_FAIR_YIELD() ETHR_YIELD() -#else -#define ERTS_SCHED_FAIR 0 -#define ERTS_SCHED_FAIR_YIELD() -#endif - #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) int erts_smp_lc_runq_is_locked(ErtsRunQueue *); #endif diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index e1b03a057f..3a74d752fe 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -3297,8 +3297,6 @@ sys_msg_dispatcher_func(void *unused) if (erts_thr_progress_update(NULL)) erts_thr_progress_leader_update(NULL); - ERTS_SCHED_FAIR_YIELD(); - #ifdef DEBUG_PRINTOUTS print_msg_type(smqp); #endif @@ -3453,9 +3451,6 @@ static void init_sys_msg_dispatcher(void) { erts_smp_thr_opts_t thr_opts = ERTS_SMP_THR_OPTS_DEFAULT_INITER; -#ifdef __OSE__ - thr_opts.coreNo = 0; -#endif thr_opts.detached = 1; thr_opts.name = "sys_msg_dispatcher"; init_smq_element_alloc(); @@ -3463,7 +3458,6 @@ init_sys_msg_dispatcher(void) sys_message_queue_end = NULL; erts_smp_cnd_init(&smq_cnd); erts_smp_mtx_init(&smq_mtx, "sys_msg_q"); - erts_smp_thr_create(&sys_msg_dispatcher_tid, sys_msg_dispatcher_func, NULL, diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 6b4b90cb06..d754fc634e 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -52,9 +52,7 @@ #include "erl_hl_timer.h" extern ErlDrvEntry fd_driver_entry; -#ifndef __OSE__ extern ErlDrvEntry vanilla_driver_entry; -#endif extern ErlDrvEntry spawn_driver_entry; extern ErlDrvEntry *driver_tab[]; /* table of static drivers, only used during initialization */ @@ -2794,9 +2792,7 @@ void erts_init_io(int port_tab_size, erts_smp_rwmtx_rwlock(&erts_driver_list_lock); init_driver(&fd_driver, &fd_driver_entry, NULL); -#ifndef __OSE__ init_driver(&vanilla_driver, &vanilla_driver_entry, NULL); -#endif init_driver(&spawn_driver, &spawn_driver_entry, NULL); erts_init_static_drivers(); for (dp = driver_tab; *dp != NULL; dp++) diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 14eeb0ee8f..34011147d9 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -74,9 +74,7 @@ #if defined (__WIN32__) # include "erl_win_sys.h" -#elif defined (__OSE__) -# include "erl_ose_sys.h" -#else +#else # include "erl_unix_sys.h" #ifndef UNIX # define UNIX 1 diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 0317a95e8b..2cb4662fc3 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -101,15 +101,9 @@ # include "config.h" #endif -#ifndef __OSE__ #include #include #include -#else -#include "ctype.h" -#include "sys/types.h" -#include "stdlib.h" -#endif /* Need (NON)BLOCKING macros for sendfile */ #ifndef WANT_NONBLOCKING diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 549de6503c..31b4b22081 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -94,10 +94,6 @@ typedef unsigned long long llu_t; #define INT16_MAX (32767) #endif -#ifdef __OSE__ -#include "inet.h" -#endif - #ifdef __WIN32__ #define STRNCASECMP strncasecmp @@ -298,139 +294,7 @@ static unsigned long one_value = 1; #define TCP_SHUT_RD SD_RECEIVE #define TCP_SHUT_RDWR SD_BOTH -#elif defined (__OSE__) - -/* - * Some notes about how inet (currently only tcp) works on OSE. - * The driver uses OSE signals to communicate with the one_inet - * process. Because of the difference in how signals and file descriptors - * work the whole select/deselect mechanic is very different. - * In ose when a sock_select is done a function is called. That function - * notes the changes that the driver want to do, but does not act on it. - * later when the function returns the new desired state is compared - * to the previous state and the apprioriate actions are taken. The action - * is usually to either request more data from the stack or stop requesting - * data. - * - * One thing to note is that the driver never does select/deselect. It always - * listens for the signals. Flow of data is regulated by sending or not sending - * signals to the ose inet process. - * - * The interesting functions to look at are: - * * inet_driver_select : called when sock_select is called - * * tcp_inet_ose_dispatch_signal : checks state changes and sends new signals - * * tcp_inet_drv_output_ose : ready output callback, reads signals and calls - * dispatch_signal - * * tcp_inet_drv_input_ose : ready input callback. - */ - -#include "efs.h" -#include "sys/socket.h" -#include "sys/uio.h" -#include "sfk/sys/sfk_uio.h" -#include "netinet/in.h" -#include "netinet/tcp.h" -#include "netdb.h" -#include "ose_spi/socket.sig" - - -static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int max_sz); - -#define INVALID_SOCKET -1 -#define INVALID_EVENT -1 -#define SOCKET_ERROR -1 - -#define SOCKET int -#define HANDLE int -#define FD_READ ERL_DRV_READ -#define FD_WRITE ERL_DRV_WRITE -#define FD_CLOSE 0 -#define FD_CONNECT (1<<4) -#define FD_ACCEPT (1<<5) -#define SOCK_FD_ERROR (1<<6) - -#define sock_connect(s, addr, len) connect((s), (addr), (len)) -#define sock_listen(s, b) listen((s), (b)) -#define sock_bind(s, addr, len) bind((s), (addr), (len)) -#define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) -#define sock_setopt(s,t,n,v,l) setsockopt((s),(t),(n),(v),(l)) -#define sock_name(s, addr, len) getsockname((s), (addr), (len)) -#define sock_peer(s, addr, len) getpeername((s), (addr), (len)) -#define sock_ntohs(x) ntohs((x)) -#define sock_ntohl(x) ntohl((x)) -#define sock_htons(x) htons((x)) -#define sock_htonl(x) htonl((x)) - -#define sock_accept(s, addr, len) accept((s), (addr), (len)) -#define sock_send(s,buf,len,flag) inet_send((s),(buf),(len),(flag)) -#define sock_sendto(s,buf,blen,flag,addr,alen) \ - sendto((s),(buf),(blen),(flag),(addr),(alen)) -#define sock_sendv(s, vec, size, np, flag) \ - (*(np) = writev_fallback((s), (struct iovec*)(vec), (size), (*(np)))) -#define sock_sendmsg(s,msghdr,flag) sendmsg((s),(msghdr),(flag)) - -#define sock_open(af, type, proto) socket((af), (type), (proto)) -#define sock_close(s) close((s)) -#define sock_dup(s) dup((s)) -#define sock_shutdown(s, how) shutdown((s), (how)) - -#define sock_hostname(buf, len) gethostname((buf), (len)) -#define sock_getservbyname(name,proto) getservbyname((name), (proto)) -#define sock_getservbyport(port,proto) getservbyport((port), (proto)) - -#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag)) -#define sock_recvfrom(s,buf,blen,flag,addr,alen) \ - recvfrom((s),(buf),(blen),(flag),(addr),(alen)) -#define sock_recvmsg(s,msghdr,flag) recvmsg((s),(msghdr),(flag)) - -#define sock_errno() errno -#define sock_create_event(d) ((d)->s) /* return file descriptor */ -#define sock_close_event(e) /* do nothing */ - -#ifndef WANT_NONBLOCKING -#define WANT_NONBLOCKING -#endif -#include "sys.h" - -typedef unsigned long u_long; -#define IN_CLASSA(a) ((((in_addr_t)(a)) & 0x80000000) == 0) -#define IN_CLASSA_NET 0xff000000 -#define IN_CLASSA_NSHIFT 24 -#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) -#define IN_CLASSA_MAX 128 - -#define IN_CLASSB(a) ((((in_addr_t)(a)) & 0xc0000000) == 0x80000000) -#define IN_CLASSB_NET 0xffff0000 -#define IN_CLASSB_NSHIFT 16 -#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) -#define IN_CLASSB_MAX 65536 - -#define IN_CLASSC(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xc0000000) -#define IN_CLASSC_NET 0xffffff00 -#define IN_CLASSC_NSHIFT 8 -#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) - -#define IN_CLASSD(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xe0000000) -#define IN_MULTICAST(a) IN_CLASSD(a) - -#define IN_EXPERIMENTAL(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xe0000000) -#define IN_BADCLASS(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xf0000000) - -#define sock_select(d, flags, onoff) do { \ - ASSERT(!(d)->is_ignored); \ - (d)->event_mask = (onoff) ? \ - ((d)->event_mask | (flags)) : \ - ((d)->event_mask & ~(flags)); \ - DEBUGF(("(%s / %d) sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX, s=%d\r\n", \ - __FILE__, __LINE__, (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask, (d)->s)); \ - inet_driver_select((d), (flags), (onoff)); \ - } while(0) - -#define TCP_SHUT_WR SHUT_WR -#define TCP_SHUT_RD SHUT_RD -#define TCP_SHUT_RDWR SHUT_RDWR - -#else /* !__OSE__ && !__WIN32__ */ +#else /* !__WIN32__ */ #include #ifdef NETDB_H_NEEDS_IN_H @@ -704,7 +568,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define TCP_SHUT_RD SHUT_RD #define TCP_SHUT_RDWR SHUT_RDWR -#endif /* !__WIN32__ && !__OSE__ */ +#endif /* !__WIN32__ */ #ifdef HAVE_SOCKLEN_T # define SOCKLEN_T socklen_t @@ -1166,13 +1030,6 @@ typedef struct { char *netns; /* Socket network namespace name as full file path */ #endif -#ifdef __OSE__ - int select_state; /* state to keep track of whether we - should trigger another read/write - request at end of ready_input/output */ - ErlDrvEvent events[6]; -#endif - } inet_descriptor; @@ -1188,10 +1045,8 @@ static void tcp_inet_stop(ErlDrvData); static void tcp_inet_command(ErlDrvData, char*, ErlDrvSizeT); static void tcp_inet_commandv(ErlDrvData, ErlIOVec*); static void tcp_inet_flush(ErlDrvData drv_data); -#ifndef __OSE__ static void tcp_inet_drv_input(ErlDrvData, ErlDrvEvent); static void tcp_inet_drv_output(ErlDrvData data, ErlDrvEvent event); -#endif static ErlDrvData tcp_inet_start(ErlDrvPort, char* command); static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData, unsigned int, char*, ErlDrvSizeT, char**, ErlDrvSizeT); @@ -1204,71 +1059,6 @@ static void tcp_inet_event(ErlDrvData, ErlDrvEvent); static void find_dynamic_functions(void); #endif -#ifdef __OSE__ -/* The structure of the signal used for requesting asynchronous - * notification from the stack. Under normal circumstances the network stack - * shouldn't overwrite the value set in the fd field by the sender - * of the request */ -struct OseAsyncSig { - struct FmEvent event; - int fd; -}; - -union SIGNAL { - SIGSELECT signo; - struct OseAsyncSig async; -}; - -static ErlDrvSSizeT tcp_inet_ctl_ose(ErlDrvData e, unsigned int cmd, - char* buf, ErlDrvSizeT len, - char** rbuf, ErlDrvSizeT rsize); -static void tcp_inet_commandv_ose(ErlDrvData e, ErlIOVec* ev); -static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event); -static void tcp_inet_drv_input_ose(ErlDrvData data, ErlDrvEvent event); -static ErlDrvOseEventId inet_resolve_signal(union SIGNAL *sig); - -#ifdef INET_DRV_DEBUG - -static char *read_req = "SO_EVENT_READ_REQUEST"; -static char *read_rep = "SO_EVENT_READ_REPLY"; -static char *write_req = "SO_EVENT_WRITE_REQUEST"; -static char *write_rep = "SO_EVENT_WRITE_REPLY"; -static char *eof_req = "SO_EVENT_EOF_REQUEST"; -static char *eof_rep = "SO_EVENT_EOF_REPLY"; -static char *accept_req = "SO_EVENT_ACCEPT_REQUEST"; -static char *accept_rep = "SO_EVENT_ACCEPT_REPLY"; -static char *connect_req = "SO_EVENT_CONNECT_REQUEST"; -static char *connect_rep = "SO_EVENT_CONNECT_REPLY"; -static char *error_req = "SO_EVENT_ERROR_REQUEST"; -static char *error_rep = "SO_EVENT_ERROR_REPLY"; -static char signo_tmp[32]; - -static char *signo_to_string(SIGSELECT signo) { - switch (signo) { - case SO_EVENT_READ_REQUEST: { return read_req; } - case SO_EVENT_READ_REPLY: { return read_rep; } - case SO_EVENT_WRITE_REQUEST: { return write_req; } - case SO_EVENT_WRITE_REPLY: { return write_rep; } - case SO_EVENT_EOF_REQUEST: { return eof_req; } - case SO_EVENT_EOF_REPLY: { return eof_rep; } - case SO_EVENT_ACCEPT_REQUEST: { return accept_req; } - case SO_EVENT_ACCEPT_REPLY: { return accept_rep; } - case SO_EVENT_CONNECT_REQUEST: { return connect_req; } - case SO_EVENT_CONNECT_REPLY: { return connect_rep; } - case SO_EVENT_ERROR_REQUEST: { return error_req; } - case SO_EVENT_ERROR_REPLY: { return error_rep; } - } - - snprintf(signo_tmp,32,"0x%x",signo); - - return signo_tmp; -} - -#endif - -#endif /* __OSE__ */ - - static struct erl_drv_entry tcp_inet_driver_entry = { tcp_inet_init, /* inet_init will add this driver !! */ @@ -1278,9 +1068,6 @@ static struct erl_drv_entry tcp_inet_driver_entry = #ifdef __WIN32__ tcp_inet_event, NULL, -#elif defined(__OSE__) - tcp_inet_drv_input_ose, /*ready_input*/ - tcp_inet_drv_output_ose, /*ready_output*/ #else tcp_inet_drv_input, tcp_inet_drv_output, @@ -1288,17 +1075,9 @@ static struct erl_drv_entry tcp_inet_driver_entry = "tcp_inet", NULL, NULL, -#ifdef __OSE__ - tcp_inet_ctl_ose, -#else tcp_inet_ctl, -#endif tcp_inet_timeout, -#ifdef __OSE__ - tcp_inet_commandv_ose, -#else tcp_inet_commandv, -#endif NULL, tcp_inet_flush, NULL, @@ -1450,14 +1229,6 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event); /* convert descriptor pointer to inet_descriptor pointer */ #define INETP(d) (&(d)->inet) -#ifdef __OSE__ -static void inet_driver_select(inet_descriptor* desc, - int flags, int onoff); -static void tcp_inet_ose_dispatch_signals(tcp_descriptor *desc, - int prev_select_state, - union SIGNAL *sig); -#endif - static int async_ref = 0; /* async reference id generator */ #define NEW_ASYNC_ID() ((async_ref++) & 0xffff) @@ -4353,16 +4124,6 @@ static void desc_close(inet_descriptor* desc) desc->forced_events = 0; desc->send_would_block = 0; #endif -#ifdef __OSE__ - if (desc->events[0]) { - driver_select(desc->port,desc->events[0],FD_READ|FD_WRITE|ERL_DRV_USE,0); - driver_select(desc->port,desc->events[1],FD_READ|FD_WRITE|ERL_DRV_USE,0); - driver_select(desc->port,desc->events[2],FD_READ|FD_WRITE|ERL_DRV_USE,0); - driver_select(desc->port,desc->events[3],FD_READ|FD_WRITE|ERL_DRV_USE,0); - driver_select(desc->port,desc->events[4],FD_READ|FD_WRITE|ERL_DRV_USE,0); - driver_select(desc->port,desc->events[5],FD_READ|FD_WRITE|ERL_DRV_USE,0); - } -#else /* * We should close the fd here, but the other driver might still * be selecting on it. @@ -4372,7 +4133,6 @@ static void desc_close(inet_descriptor* desc) ERL_DRV_USE, 0); else inet_stop_select((ErlDrvEvent)(long)desc->event,NULL); -#endif desc->event = INVALID_EVENT; /* closed by stop_select callback */ desc->s = INVALID_SOCKET; desc->event_mask = 0; @@ -4414,65 +4174,6 @@ static int erl_inet_close(inet_descriptor* desc) return 0; } -#ifdef __OSE__ -static void inet_select_init(inet_descriptor* desc) -{ - desc->events[0] = - erl_drv_ose_event_alloc(SO_EVENT_READ_REPLY, - desc->s, - inet_resolve_signal, - NULL); - driver_select(desc->port, desc->events[0], - ERL_DRV_READ|ERL_DRV_USE, 1); - - desc->events[1] = - erl_drv_ose_event_alloc(SO_EVENT_EOF_REPLY, - desc->s, - inet_resolve_signal, - NULL); - driver_select(desc->port, desc->events[1], - ERL_DRV_READ|ERL_DRV_USE, 1); - - desc->events[2] = - erl_drv_ose_event_alloc(SO_EVENT_ACCEPT_REPLY, - desc->s, - inet_resolve_signal, - NULL); - driver_select(desc->port, desc->events[2], - ERL_DRV_READ|ERL_DRV_USE, 1); - - /* trigger tcp_inet_input */ - desc->events[3] = - erl_drv_ose_event_alloc(SO_EVENT_WRITE_REPLY, - desc->s, - inet_resolve_signal, - NULL); - driver_select(desc->port, desc->events[3], - ERL_DRV_WRITE|ERL_DRV_USE, 1); - - desc->events[4] = - erl_drv_ose_event_alloc(SO_EVENT_CONNECT_REPLY, - desc->s, - inet_resolve_signal, - NULL); - driver_select(desc->port, desc->events[4], - ERL_DRV_WRITE|ERL_DRV_USE, 1); - - desc->events[5] = - erl_drv_ose_event_alloc(SO_EVENT_ERROR_REPLY, - desc->s, - inet_resolve_signal, - NULL); - driver_select(desc->port, desc->events[5], - ERL_DRV_WRITE|ERL_DRV_USE, 1); - - /* Issue a select on error event before any other select to be sure we are - prepared to receive error notifications from the stack, even in the - situations when select isn't issued */ - sock_select(desc, SOCK_FD_ERROR, 1); -} -#endif - static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, char** rbuf, ErlDrvSizeT rsize) { @@ -4555,9 +4256,6 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, #ifdef __WIN32__ driver_select(desc->port, desc->event, ERL_DRV_READ, 1); #endif -#ifdef __OSE__ - inet_select_init(desc); -#endif desc->state = INET_STATE_OPEN; desc->stype = type; @@ -4581,13 +4279,7 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, if (name.sa.sa_family != domain) return ctl_error(EINVAL, rbuf, rsize); } -#ifdef __OSE__ - /* for fdopen duplicating the sd will allow to uniquely identify - the signal from OSE with erlang port */ - desc->s = sock_dup(s); -#else desc->s = s; -#endif if ((desc->event = sock_create_event(desc)) == INVALID_EVENT) return ctl_error(sock_errno(), rbuf, rsize); @@ -4605,12 +4297,6 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, sz = sizeof(name); if (!IS_SOCKET_ERROR(sock_peer(s, (struct sockaddr*) &name, &sz))) { desc->state = INET_STATE_CONNECTED; -#ifdef __OSE__ - /* since we are dealing with different descriptors (i.e. inet and - socket) the select part should be initialized with the right - values */ - inet_select_init(desc); -#endif } } @@ -8404,15 +8090,6 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) #ifdef HAVE_SETNS desc->netns = NULL; #endif -#ifdef __OSE__ - desc->select_state = 0; - desc->events[0] = NULL; - desc->events[1] = NULL; - desc->events[2] = NULL; - desc->events[3] = NULL; - desc->events[4] = NULL; - desc->events[5] = NULL; -#endif return (ErlDrvData)desc; } @@ -9139,10 +8816,6 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s, copy_desc->inet.port = port; copy_desc->inet.dport = driver_mk_port(port); -#ifdef __OSE__ - inet_select_init(©_desc->inet); -#endif - *err = 0; return copy_desc; } @@ -9204,23 +8877,6 @@ static void tcp_inet_stop(ErlDrvData e) inet_stop(INETP(desc)); } -#ifdef __OSE__ - -static ErlDrvSSizeT tcp_inet_ctl_ose(ErlDrvData e, unsigned int cmd, - char* buf, ErlDrvSizeT len, - char** rbuf, ErlDrvSizeT rsize) { - - tcp_descriptor* desc = (tcp_descriptor*)e; - int prev_select_state = INETP(desc)->select_state; - - ErlDrvSSizeT res = tcp_inet_ctl(e,cmd,buf,len,rbuf,rsize); - - tcp_inet_ose_dispatch_signals((tcp_descriptor*)e,prev_select_state,NULL); - - return res; -} -#endif - /* TCP requests from Erlang */ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, ErlDrvSizeT len, @@ -9662,17 +9318,6 @@ static void tcp_inet_command(ErlDrvData e, char *buf, ErlDrvSizeT len) DEBUGF(("tcp_inet_command(%ld) }\r\n", (long)desc->inet.port)); } -#ifdef __OSE__ - -static void tcp_inet_commandv_ose(ErlDrvData e, ErlIOVec* ev) { - int prev_select_state = INETP((tcp_descriptor*)e)->select_state; - tcp_inet_commandv(e, ev); - tcp_inet_ose_dispatch_signals((tcp_descriptor*)e,prev_select_state,NULL); -} - -#endif - - static void tcp_inet_commandv(ErlDrvData e, ErlIOVec* ev) { tcp_descriptor* desc = (tcp_descriptor*)e; @@ -9745,22 +9390,6 @@ static void inet_stop_select(ErlDrvEvent event, void* _) { #ifdef __WIN32__ WSACloseEvent((HANDLE)event); -#elif defined(__OSE__) - ErlDrvOseEventId id; - union SIGNAL *sig; - erl_drv_ose_event_fetch(event, NULL, &id,NULL); - DEBUGF(("inet_stop_select(?#?) {s=%d\n",id)); - sock_close((int)id); - /* On socket close all the signals waiting to be processed as part of the - select should be deallocated */ - while((sig = erl_drv_ose_get_signal(event))) { - DEBUGF(("inet_stop_select(?#?): Freeing signal %s\n", - signo_to_string(sig->signo))); - free_buf(&sig); - } - erl_drv_ose_event_free(event); - DEBUGF(("inet_stop_select(?#?) }\n")); - #else sock_close((SOCKET)(long)event); #endif @@ -10309,146 +9938,7 @@ static void tcp_inet_event(ErlDrvData e, ErlDrvEvent event) return; } -#elif defined(__OSE__) /* !__WIN32__ */ -/* The specific resolve signal function. It will return the socket descriptor - for which the select was issued */ -static ErlDrvOseEventId inet_resolve_signal(union SIGNAL *sig) { - DEBUGF(("%s(?#?): s=%d got signal %s, status = %d, extra = %d, sender = 0x%x\n", - __FUNCTION__,sig->async.fd,signo_to_string(sig->signo), - sig->async.event.status, - sig->async.event.extra,sender(&sig))); - if (sig->signo == SO_EVENT_READ_REPLY || - sig->signo == SO_EVENT_ACCEPT_REPLY || - sig->signo == SO_EVENT_EOF_REPLY || - sig->signo == SO_EVENT_WRITE_REPLY || - sig->signo == SO_EVENT_ERROR_REPLY || - sig->signo == SO_EVENT_CONNECT_REPLY ) { - return sig->async.fd; - } - - return -1; -} - -static void inet_driver_select(inet_descriptor* desc, - int flags, int onoff) { - ASSERT(!desc->is_ignored); - - if(onoff) { - desc->select_state |= flags; - } else { - desc->select_state &= ~flags; - } -} - -static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int max_sz) -{ - size_t data_len = 0; - size_t sent = 0; - ssize_t n; - int i; - - for(i = 0; i < iovcnt; i++) - { - data_len = iov[i].iov_len; -tryagain: - n = sock_send(fd, iov[i].iov_base, data_len, 0); - if (IS_SOCKET_ERROR(n)) { - /* If buffer length is greater than the amount stack is able to - * send out then try to send at least max_sz (this comes with - * SO_EVENT_WRITE_REPLY signal*/ - if ((errno == EMSGSIZE) && (max_sz > 0) && (data_len > max_sz)) { - data_len = max_sz; - goto tryagain; - } - break; - } - sent += n; - } - return sent; -} - -#define OSE_EVENT_REQ(TCP_DESC,EVENT) do { \ - union SIGNAL *sig = alloc(sizeof(struct OseAsyncSig), EVENT); \ - sig->async.fd = INETP(TCP_DESC)->s; \ - ose_request_event(INETP(TCP_DESC)->s, &sig, 1); \ - DEBUGF(("%s(%ld): s=%d sent %s\r\n",__FUNCTION__, \ - INETP(TCP_DESC)->port,INETP(TCP_DESC)->s,signo_to_string(EVENT))); \ - } while(0) - -static void tcp_inet_ose_dispatch_signals(tcp_descriptor *desc, - int prev_select_state, - union SIGNAL *sig) { - if (sig) { - DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) {s=%d resend\r\n", - (long)INETP(desc)->port,INETP(desc)->s)); - /* We are reacting to a signal, which means that if - the select_state for that signal is still activated - we should send a new signal */ - switch (sig->signo) { - case SO_EVENT_READ_REPLY: { - if (INETP(desc)->select_state & FD_READ) - OSE_EVENT_REQ(desc,SO_EVENT_READ_REQUEST); - break; - } - case SO_EVENT_WRITE_REPLY: { - if (INETP(desc)->select_state & FD_WRITE) - OSE_EVENT_REQ(desc,SO_EVENT_WRITE_REQUEST); - break; - } - case SO_EVENT_CONNECT_REPLY: { - if (INETP(desc)->select_state & FD_CONNECT) - OSE_EVENT_REQ(desc,SO_EVENT_CONNECT_REQUEST); - break; - } - case SO_EVENT_ACCEPT_REPLY: { - if (INETP(desc)->select_state & FD_ACCEPT) - OSE_EVENT_REQ(desc,SO_EVENT_ACCEPT_REQUEST); - break; - } - case SO_EVENT_ERROR_REPLY: { - if (INETP(desc)->select_state & SOCK_FD_ERROR) - OSE_EVENT_REQ(desc,SO_EVENT_ERROR_REQUEST); - break; - } - - } - DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) }\r\n", - (long)INETP(desc)->port)); - } - - if (INETP(desc)->select_state != prev_select_state) { - /* If the select state has changed we have to issue signals for - the state parts that have changed. */ - int xor_select_state = INETP(desc)->select_state ^ prev_select_state; - DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) {s=%d select change\r\n", - (long)INETP(desc)->port,INETP(desc)->s)); - if ((xor_select_state & FD_READ) && - (INETP(desc)->select_state & FD_READ)) { - OSE_EVENT_REQ(desc,SO_EVENT_READ_REQUEST); - } - if ((xor_select_state & FD_WRITE) && - (INETP(desc)->select_state & FD_WRITE)) { - OSE_EVENT_REQ(desc,SO_EVENT_WRITE_REQUEST); - } - if ((xor_select_state & FD_CONNECT) && - (INETP(desc)->select_state & FD_CONNECT)) { - OSE_EVENT_REQ(desc,SO_EVENT_CONNECT_REQUEST); - } - if ((xor_select_state & FD_ACCEPT) && - (INETP(desc)->select_state & FD_ACCEPT)) { - OSE_EVENT_REQ(desc,SO_EVENT_ACCEPT_REQUEST); - } - if ((xor_select_state & SOCK_FD_ERROR) && - (INETP(desc)->select_state & SOCK_FD_ERROR)) { - OSE_EVENT_REQ(desc,SO_EVENT_ERROR_REQUEST); - } - - DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) }\r\n", - (long)INETP(desc)->port)); - } -} - -#endif /* __OSE__ */ +#endif /* __WIN32__ */ /* socket has input: @@ -10935,49 +10425,6 @@ static void tcp_shutdown_async(tcp_descriptor* desc) desc->tcp_add_flags |= TCP_ADDF_SHUTDOWN_WR_DONE; } -#ifdef __OSE__ - -static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event) -{ - union SIGNAL *event_sig = erl_drv_ose_get_signal(event); - - while (event_sig) { - int prev_select_state = INETP((tcp_descriptor*)data)->select_state; - int res = tcp_inet_output((tcp_descriptor*)data, (HANDLE)event_sig); - if (res != -1) { - tcp_inet_ose_dispatch_signals((tcp_descriptor*)data, - prev_select_state,event_sig); - free_buf(&event_sig); - event_sig = erl_drv_ose_get_signal(event); - } else { - /* NOTE: here the event object could have been deallocated!!!! - inet_stop_select is called when doing driver_select(ERL_DRV_USE,0) - */ - free_buf(&event_sig); - return; - } - } -} - -static void tcp_inet_drv_input_ose(ErlDrvData data, ErlDrvEvent event) -{ - union SIGNAL *event_sig = erl_drv_ose_get_signal(event); - - while (event_sig) { - int prev_select_state = INETP((tcp_descriptor*)data)->select_state; - int res = tcp_inet_input((tcp_descriptor*)data, (HANDLE)event); - if (res != -1) { - tcp_inet_ose_dispatch_signals((tcp_descriptor*)data, prev_select_state, - event_sig); - free_buf(&event_sig); - event_sig = erl_drv_ose_get_signal(event); - } else { - free_buf(&event_sig); - return; - } - } -} -#else static void tcp_inet_drv_output(ErlDrvData data, ErlDrvEvent event) { (void)tcp_inet_output((tcp_descriptor*)data, (HANDLE)event); @@ -10987,7 +10434,6 @@ static void tcp_inet_drv_input(ErlDrvData data, ErlDrvEvent event) { (void)tcp_inet_input((tcp_descriptor*)data, (HANDLE)event); } -#endif /* socket ready for ouput: ** 1. INET_STATE_CONNECTING => non block connect ? @@ -11053,13 +10499,6 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) ssize_t n; SysIOVec* iov; -#ifdef __OSE__ - /* For large size buffers case the amount of data that the stack is - able to send out (received in the .extra field) should be passed - down to writev_fallback */ - n = event ? ((union SIGNAL*)event)->async.event.extra : 0; -#endif - if ((iov = driver_peekq(ix, &vsize)) == NULL) { sock_select(INETP(desc), FD_WRITE, 0); send_empty_out_q_msgs(INETP(desc)); @@ -11087,12 +10526,6 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) sizes > (max 32 bit signed int) */ size_t howmuch = 0x7FFFFFFF; /* max signed 32 bit */ int x; -#ifdef __OSE__ - /* For EWOULDBLOCK sock_sendv returns 0 so we have to be sure it - wasn't the case */ - if(sock_errno() == ERRNO_BLOCK) - goto done; -#endif for(x = 0; x < vsize && iov[x].iov_len == 0; ++x) ; if (x < vsize) { diff --git a/erts/emulator/drivers/ose/ose_efile.c b/erts/emulator/drivers/ose/ose_efile.c deleted file mode 100644 index c8337a95d5..0000000000 --- a/erts/emulator/drivers/ose/ose_efile.c +++ /dev/null @@ -1,1125 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 1997-2012. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -/* - * Purpose: Provides file and directory operations for OSE. - */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#if defined(HAVE_POSIX_FALLOCATE) && !defined(__sun) && !defined(__sun__) -#define _XOPEN_SOURCE 600 -#endif -#if !defined(_GNU_SOURCE) && defined(HAVE_LINUX_FALLOC_H) -#define _GNU_SOURCE -#endif -#include "sys.h" -#include "erl_driver.h" -#include "erl_efile.h" -#if defined(DARWIN) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE) -#include "fcntl.h" -#endif -#include "ose.h" -#include "unistd.h" -#include "sys/stat.h" -#include "dirent.h" -#include "sys/time.h" -#include "time.h" -#include "assert.h" - -/* Find a definition of MAXIOV, that is used in the code later. */ -#if defined IOV_MAX -#define MAXIOV IOV_MAX -#elif defined UIO_MAXIOV -#define MAXIOV UIO_MAXIOV -#else -#define MAXIOV 16 -#endif - -/* - * Macros for testing file types. - */ - -#define ISDIR(st) (((st).st_mode & S_IFMT) == S_IFDIR) -#define ISREG(st) (((st).st_mode & S_IFMT) == S_IFREG) -#define ISDEV(st) \ - (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) -#define ISLNK(st) (((st).st_mode & S_IFLNK) == S_IFLNK) -#ifdef NO_UMASK -#define FILE_MODE 0644 -#define DIR_MODE 0755 -#else -#define FILE_MODE 0666 -#define DIR_MODE 0777 -#endif - -#define IS_DOT_OR_DOTDOT(s) \ - (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0'))) - -/* - * Macros for handling local file descriptors - * and mutexes. - * - * Handling of files like this is necessary because OSE - * does not allow seeking after the end of a file. So - * what we do it emulate this by keeping track of the size - * of the file and where the file's positions is. If a - * write happens after eof then we pad it. - * - * Given time this should be rewritten to get rid of the - * mutex and use the port lock to protect the data. This - * could be done be done by adapting the efile api for some - * calls to allow some meta-data to be associated with the - * open file. - */ - -#define L_FD_IS_VALID(fd_data) ((fd_data)->beyond_eof > 0) -#define L_FD_INVALIDATE(fd_data) (fd_data)->beyond_eof = 0 -#define L_FD_CUR(fd_data) (fd_data)->pos -#define L_FD_OFFS_BEYOND_EOF(fd_data, offs) \ - (((fd_data)->size > offs) ? 0 : 1) - -#define L_FD_FAIL -1 -#define L_FD_SUCCESS 1 -#define L_FD_PAD_SIZE 255 - -struct fd_meta { - ErlDrvMutex *meta_mtx; - struct fd_data *fd_data_list; -}; - -struct fd_data { - int fd; - struct fd_data *next; - struct fd_data *prev; - int pos; - int beyond_eof; - size_t size; -#ifdef DEBUG - PROCESS owner; -#endif -}; - -static int l_invalidate_local_fd(int fd); -static int l_pad_file(struct fd_data *fd_data, off_t offset); -static int check_error(int result, Efile_error* errInfo); -static struct fd_data* l_new_fd(void); -static int l_remove_local_fd(int fd); -static struct fd_data* l_find_local_fd(int fd); -static int l_update_local_fd(int fd, int pos, int size); - -static struct fd_meta* fdm = NULL; - - -/***************************************************************************/ - -static int -l_remove_local_fd(int fd) -{ - struct fd_data *fd_data; - fd_data = l_find_local_fd(fd); - - if (fd_data == NULL) { - return L_FD_FAIL; - } -#ifdef DEBUG - assert(fd_data->owner == current_process()); -#endif - erl_drv_mutex_lock(fdm->meta_mtx); - /* head ? */ - if (fd_data == fdm->fd_data_list) { - if (fd_data->next != NULL) { - /* remove link to head */ - fd_data->next->prev = NULL; - /* set new head */ - fdm->fd_data_list = fd_data->next; - } - else { - /* head is lonely */ - fdm->fd_data_list = NULL; - } - } - else { /* not head */ - if (fd_data->prev == NULL) { - erl_drv_mutex_unlock(fdm->meta_mtx); - return L_FD_FAIL; - } - else { - if (fd_data->next != NULL) { - fd_data->next->prev = fd_data->prev; - fd_data->prev->next = fd_data->next; - } - else { - fd_data->prev->next = NULL; - } - } - } - - /* scramble values */ - fd_data->beyond_eof = -1; - fd_data->next = NULL; - fd_data->prev = NULL; - fd_data->fd = -1; - - /* unlock and clean */ - driver_free(fd_data); - erl_drv_mutex_unlock(fdm->meta_mtx); - - return L_FD_SUCCESS; -} - -/***************************************************************************/ - -static int -l_invalidate_local_fd(int fd) { - struct fd_data *fd_data; - - if ((fd_data = l_find_local_fd(fd)) == NULL) { - return L_FD_FAIL; - } - - fd_data->beyond_eof = 0; - return L_FD_SUCCESS; -} - -/****************************************************************************/ - -static struct fd_data* -l_find_local_fd(int fd) { - struct fd_data *fd_data; - - fd_data = NULL; - erl_drv_mutex_lock(fdm->meta_mtx); - for (fd_data = fdm->fd_data_list; fd_data != NULL; ) { - if (fd_data->fd == fd) { -#ifdef DEBUG - assert(fd_data->owner == current_process()); -#endif - break; - } - fd_data = fd_data->next; - } - erl_drv_mutex_unlock(fdm->meta_mtx); - return fd_data; -} - -/***************************************************************************/ - -static struct fd_data* -l_new_fd(void) { - struct fd_data *fd_data; - - fd_data = driver_alloc(sizeof(struct fd_data)); - if (fd_data == NULL) { - return NULL; - } - erl_drv_mutex_lock(fdm->meta_mtx); - if (fdm->fd_data_list == NULL) { - fdm->fd_data_list = fd_data; - fdm->fd_data_list->prev = NULL; - fdm->fd_data_list->next = NULL; - } - else { - fd_data->next = fdm->fd_data_list; - fdm->fd_data_list = fd_data; - fdm->fd_data_list->prev = NULL; - } -#ifdef DEBUG - fd_data->owner = current_process(); -#endif - erl_drv_mutex_unlock(fdm->meta_mtx); - return fd_data; -} - -/***************************************************************************/ - -static int -l_update_local_fd(int fd, int pos, int size) { - struct fd_data *fd_data = NULL; - - fd_data = l_find_local_fd(fd); - /* new fd to handle? */ - if (fd_data == NULL) { - fd_data = l_new_fd(); - if (fd_data == NULL) { - /* out of memory */ - return L_FD_FAIL; - } - } - fd_data->size = size; - fd_data->pos = pos; - fd_data->fd = fd; - fd_data->beyond_eof = 1; - - return L_FD_SUCCESS; -} - -/***************************************************************************/ - -static int -l_pad_file(struct fd_data *fd_data, off_t offset) { - int size_dif; - int written = 0; - int ret_val = L_FD_SUCCESS; - char padding[L_FD_PAD_SIZE]; - - size_dif = (offset - fd_data->size); - memset(&padding, '\0', L_FD_PAD_SIZE); - - while (size_dif > 0) { - written = write(fd_data->fd, padding, - (size_dif < L_FD_PAD_SIZE) ? - size_dif : L_FD_PAD_SIZE); - if (written < 0 && errno != EINTR && errno != EAGAIN) { - ret_val = -1; - break; - } - size_dif -= written; - } - L_FD_INVALIDATE(fd_data); - return ret_val; -} - -/***************************************************************************/ - -static int -check_error(int result, Efile_error *errInfo) { - if (result < 0) { - errInfo->posix_errno = errInfo->os_errno = errno; - return 0; - } - return 1; -} - -/***************************************************************************/ - -int -efile_init() { - fdm = driver_alloc(sizeof(struct fd_meta)); - if (fdm == NULL) { - return L_FD_FAIL; - } - fdm->meta_mtx = erl_drv_mutex_create("ose_efile local fd mutex\n"); - erl_drv_mutex_lock(fdm->meta_mtx); - fdm->fd_data_list = NULL; - erl_drv_mutex_unlock(fdm->meta_mtx); - return L_FD_SUCCESS; -} - -/***************************************************************************/ - -int -efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ - char* name) /* Name of directory to create. */ -{ -#ifdef NO_MKDIR_MODE - return check_error(mkdir(name), errInfo); -#else - int res = mkdir(name, DIR_MODE); - if (res < 0 && errno == EINVAL) { - errno = ENOENT; - } - return check_error(res, errInfo); -#endif -} - -/***************************************************************************/ - -int -efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ - char* name) /* Name of directory to delete. */ -{ - if (rmdir(name) == 0) { - return 1; - } - if (errno == ENOTEMPTY) { - errno = EEXIST; - } - if (errno == EEXIST || errno == EINVAL) { - int saved_errno = errno; - struct stat file_stat; - struct stat cwd_stat; - - if(stat(name, &file_stat) != 0) { - errno = ENOENT; - return check_error(-1, errInfo); - } - /* - * The error code might be wrong if this is the current directory. - */ - if (stat(name, &file_stat) == 0 && stat(".", &cwd_stat) == 0 && - file_stat.st_ino == cwd_stat.st_ino && - file_stat.st_dev == cwd_stat.st_dev) { - saved_errno = EACCES; - } - errno = saved_errno; - } - return check_error(-1, errInfo); -} - -/***************************************************************************/ - -int -efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ - char* name) /* Name of file to delete. */ -{ - struct stat statbuf; - - if (stat(name, &statbuf) >= 0) { - /* Do not let unlink() remove directories */ - if (ISDIR(statbuf)) { - errno = EPERM; - return check_error(-1, errInfo); - } - - if (unlink(name) == 0) { - return 1; - } - - if (errno == EISDIR) { - errno = EPERM; - return check_error(-1, errInfo); - } - } - else { - if (errno == EINVAL) { - errno = ENOENT; - return check_error(-1, errInfo); - } - } - return check_error(-1, errInfo); -} - -/* - *--------------------------------------------------------------------------- - * - * Changes the name of an existing file or directory, from src to dst. - * If src and dst refer to the same file or directory, does nothing - * and returns success. Otherwise if dst already exists, it will be - * deleted and replaced by src subject to the following conditions: - * If src is a directory, dst may be an empty directory. - * If src is a file, dst may be a file. - * In any other situation where dst already exists, the rename will - * fail. - * - * Results: - * If the directory was successfully created, returns 1. - * Otherwise the return value is 0 and errno is set to - * indicate the error. Some possible values for errno are: - * - * EACCES: src or dst parent directory can't be read and/or written. - * EEXIST: dst is a non-empty directory. - * EINVAL: src is a root directory or dst is a subdirectory of src. - * EISDIR: dst is a directory, but src is not. - * ENOENT: src doesn't exist, or src or dst is "". - * ENOTDIR: src is a directory, but dst is not. - * EXDEV: src and dst are on different filesystems. - * - * Side effects: - * The implementation of rename may allow cross-filesystem renames, - * but the caller should be prepared to emulate it with copy and - * delete if errno is EXDEV. - * - *--------------------------------------------------------------------------- - */ - -int -efile_rename(Efile_error* errInfo, /* Where to return error codes. */ - char* src, /* Original name. */ - char* dst) /* New name. */ -{ - - /* temporary fix AFM does not recognize ./ - * in destination remove pending on adaption of AFM fix - */ - - char *dot_str; - if (dst != NULL) { - dot_str = strchr(dst, '.'); - if (dot_str && dot_str == dst && dot_str[1] == '/') { - dst = dst+2; - } - } - - if (rename(src, dst) == 0) { - return 1; - } - if (errno == ENOTEMPTY) { - errno = EEXIST; - } - if (errno == EINVAL) { - struct stat file_stat; - - if (stat(dst, &file_stat)== 0) { - if (ISDIR(file_stat)) { - errno = EISDIR; - } - else if (ISREG(file_stat)) { - errno = ENOTDIR; - } - else { - errno = EINVAL; - } - } - else { - errno = EINVAL; - } - } - - if (strcmp(src, "/") == 0) { - errno = EINVAL; - } - return check_error(-1, errInfo); -} - -/***************************************************************************/ - -int -efile_chdir(Efile_error* errInfo, /* Where to return error codes. */ - char* name) /* Name of directory to make current. */ -{ - return check_error(chdir(name), errInfo); -} - -/***************************************************************************/ - -int -efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */ - int drive, /* 0 - current, 1 - A, 2 - B etc. */ - char* buffer, /* Where to return the current - directory. */ - size_t size) /* Size of buffer. */ -{ - if (drive == 0) { - if (getcwd(buffer, size) == NULL) - return check_error(-1, errInfo); - - return 1; - } - - /* - * Drives other than 0 is not supported on Unix. - */ - - errno = ENOTSUP; - return check_error(-1, errInfo); -} - -/***************************************************************************/ - -int -efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ - char* name, /* Name of directory to open. */ - EFILE_DIR_HANDLE* p_dir_handle, /* Pointer to directory - handle of - open directory.*/ - char* buffer, /* Pointer to buffer for - one filename. */ - size_t *size) /* in-out Size of buffer, length - of name. */ -{ - DIR *dp; /* Pointer to directory structure. */ - struct dirent* dirp; /* Pointer to directory entry. */ - - /* - * If this is the first call, we must open the directory. - */ - - if (*p_dir_handle == NULL) { - dp = opendir(name); - if (dp == NULL) - return check_error(-1, errInfo); - *p_dir_handle = (EFILE_DIR_HANDLE) dp; - } - - /* - * Retrieve the name of the next file using the directory handle. - */ - - dp = *((DIR **)((void *)p_dir_handle)); - for (;;) { - dirp = readdir(dp); - if (dirp == NULL) { - closedir(dp); - return 0; - } - if (IS_DOT_OR_DOTDOT(dirp->d_name)) - continue; - buffer[0] = '\0'; - strncat(buffer, dirp->d_name, (*size)-1); - *size = strlen(dirp->d_name); - return 1; - } -} - -/***************************************************************************/ - -int -efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ - char* name, /* Name of directory to open. */ - int flags, /* Flags to user for opening. */ - int* pfd, /* Where to store the file - descriptor. */ - Sint64 *pSize) /* Where to store the size of the - file. */ -{ - struct stat statbuf; - int fd; - int mode; /* Open mode. */ - - if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) { - errno = EISDIR; - return check_error(-1, errInfo); - } - - switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) { - case EFILE_MODE_READ: - mode = O_RDONLY; - break; - case EFILE_MODE_WRITE: - if (flags & EFILE_NO_TRUNCATE) - mode = O_WRONLY | O_CREAT; - else - mode = O_WRONLY | O_CREAT | O_TRUNC; - break; - case EFILE_MODE_READ_WRITE: - mode = O_RDWR | O_CREAT; - break; - default: - errno = EINVAL; - return check_error(-1, errInfo); - } - - - if (flags & EFILE_MODE_APPEND) { - mode &= ~O_TRUNC; - mode |= O_APPEND; - } - - if (flags & EFILE_MODE_EXCL) { - mode |= O_EXCL; - } - - fd = open(name, mode, FILE_MODE); - - if (!check_error(fd, errInfo)) - return 0; - - *pfd = fd; - if (pSize) { - *pSize = statbuf.st_size; - } - return 1; -} - -/***************************************************************************/ - -int -efile_may_openfile(Efile_error* errInfo, char *name) { - struct stat statbuf; /* Information about the file */ - int result; - - result = stat(name, &statbuf); - if (!check_error(result, errInfo)) - return 0; - if (!ISREG(statbuf)) { - errno = EISDIR; - return check_error(-1, errInfo); - } - return 1; -} - -/***************************************************************************/ - -void -efile_closefile(int fd) -{ - if (l_find_local_fd(fd) != NULL) { - l_remove_local_fd(fd); - } - close(fd); -} - -/***************************************************************************/ - -int -efile_fdatasync(Efile_error *errInfo, /* Where to return error codes. */ - int fd) /* File descriptor for file to sync data. */ -{ - return efile_fsync(errInfo, fd); -} - -/***************************************************************************/ - -int -efile_fsync(Efile_error *errInfo, /* Where to return error codes. */ - int fd) /* File descriptor for file to sync. */ -{ - return check_error(fsync(fd), errInfo); -} - -/***************************************************************************/ - -int -efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, - char* name, int info_for_link) -{ - struct stat statbuf; /* Information about the file */ - int result; - - result = stat(name, &statbuf); - if (!check_error(result, errInfo)) { - return 0; - } - -#if SIZEOF_OFF_T == 4 - pInfo->size_high = 0; -#else - pInfo->size_high = (Uint32)(statbuf.st_size >> 32); -#endif - pInfo->size_low = (Uint32)statbuf.st_size; - -#ifdef NO_ACCESS - /* Just look at read/write access for owner. */ - - pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1; - -#else - pInfo->access = FA_NONE; - if (access(name, R_OK) == 0) - pInfo->access |= FA_READ; - if (access(name, W_OK) == 0) - pInfo->access |= FA_WRITE; - -#endif - - if (ISDEV(statbuf)) - pInfo->type = FT_DEVICE; - else if (ISDIR(statbuf)) - pInfo->type = FT_DIRECTORY; - else if (ISREG(statbuf)) - pInfo->type = FT_REGULAR; - else if (ISLNK(statbuf)) - pInfo->type = FT_SYMLINK; - else - pInfo->type = FT_OTHER; - - pInfo->accessTime = statbuf.st_atime; - pInfo->modifyTime = statbuf.st_mtime; - pInfo->cTime = statbuf.st_ctime; - - pInfo->mode = statbuf.st_mode; - pInfo->links = statbuf.st_nlink; - pInfo->major_device = statbuf.st_dev; - pInfo->inode = statbuf.st_ino; - pInfo->uid = statbuf.st_uid; - pInfo->gid = statbuf.st_gid; - - return 1; -} - -/***************************************************************************/ - -int -efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) -{ - /* - * On some systems chown will always fail for a non-root user unless - * POSIX_CHOWN_RESTRICTED is not set. Others will succeed as long as - * you don't try to chown a file to someone besides youself. - */ - if (pInfo->mode != -1) { - mode_t newMode = pInfo->mode & (S_ISUID | S_ISGID | - S_IRWXU | S_IRWXG | S_IRWXO); - if (chmod(name, newMode)) { - newMode &= ~(S_ISUID | S_ISGID); - if (chmod(name, newMode)) { - return check_error(-1, errInfo); - } - } - } - - return 1; -} - -/***************************************************************************/ - -int -efile_write(Efile_error* errInfo, /* Where to return error codes. */ - int flags, /* Flags given when file was - opened. */ - int fd, /* File descriptor to write to. */ - char* buf, /* Buffer to write. */ - size_t count) /* Number of bytes to write. */ -{ - ssize_t written; /* Bytes written in last operation. */ - struct fd_data *fd_data; - - if ((fd_data = l_find_local_fd(fd)) != NULL) { - if (L_FD_IS_VALID(fd_data)) { - /* we are beyond eof and need to pad*/ - if (l_pad_file(fd_data, L_FD_CUR(fd_data)) < 0) { - return check_error(-1, errInfo); - } - } - } - - while (count > 0) { - if ((written = write(fd, buf, count)) < 0) { - if (errno != EINTR) { - return check_error(-1, errInfo); - } - else { - written = 0; - } - } - ASSERT(written <= count); - buf += written; - count -= written; - } - return 1; -} - -/***************************************************************************/ - -int -efile_writev(Efile_error* errInfo, /* Where to return error codes */ - int flags, /* Flags given when file was - * opened */ - int fd, /* File descriptor to write to */ - SysIOVec* iov, /* Vector of buffer structs. - * The structs may be changed i.e. - * due to incomplete writes */ - int iovcnt) /* Number of structs in vector */ -{ - struct fd_data *fd_data; - int cnt = 0; /* Buffers so far written */ - - ASSERT(iovcnt >= 0); - if ((fd_data = l_find_local_fd(fd)) != NULL) { - if (L_FD_IS_VALID(fd_data)) { - /* we are beyond eof and need to pad*/ - if (l_pad_file(fd_data, L_FD_CUR(fd_data)) < 0) { - return check_error(-1, errInfo); - } - } - } - while (cnt < iovcnt) { - if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) { - /* Empty buffer - skip */ - cnt++; - } - else { /* Non-empty buffer */ - ssize_t w; /* Bytes written in this call */ - do { - w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len); - } while (w < 0 && errno == EINTR); - - ASSERT(w <= iov[cnt].iov_len || w == -1); - - if (w < 0) { - return check_error(-1, errInfo); - } - /* Move forward to next buffer to write */ - for (; cnt < iovcnt && w > 0; cnt++) { - if (iov[cnt].iov_base && iov[cnt].iov_len > 0) { - if (w < iov[cnt].iov_len) { - /* Adjust the buffer for next write */ - iov[cnt].iov_len -= w; - iov[cnt].iov_base += w; - w = 0; - break; - } - else { - w -= iov[cnt].iov_len; - } - } - } - ASSERT(w == 0); - } /* else Non-empty buffer */ - } /* while (cnt< iovcnt) */ - return 1; -} - -/***************************************************************************/ - -int -efile_read(Efile_error* errInfo, /* Where to return error codes. */ - int flags, /* Flags given when file was opened. */ - int fd, /* File descriptor to read from. */ - char* buf, /* Buffer to read into. */ - size_t count, /* Number of bytes to read. */ - size_t *pBytesRead) /* Where to return number of - bytes read. */ -{ - ssize_t n; - struct fd_data *fd_data; - - if ((fd_data = l_find_local_fd(fd)) != NULL) { - if (L_FD_IS_VALID(fd_data)) { - *pBytesRead = 0; - return 1; - } - } - for (;;) { - if ((n = read(fd, buf, count)) >= 0) { - break; - } - else if (errno != EINTR) { - return check_error(-1, errInfo); - } - } - if (fd_data != NULL && L_FD_IS_VALID(fd_data)) { - L_FD_INVALIDATE(fd_data); - } - *pBytesRead = (size_t) n; - return 1; -} - -/* pread() and pwrite() */ -/* Some unix systems, notably Solaris has these syscalls */ -/* It is especially nice for i.e. the dets module to have support */ -/* for this, even if the underlying OS dosn't support it, it is */ -/* reasonably easy to work around by first calling seek, and then */ -/* calling read(). */ -/* This later strategy however changes the file pointer, which pread() */ -/* does not do. We choose to ignore this and say that the location */ -/* of the file pointer is undefined after a call to any of the p functions*/ - - -int -efile_pread(Efile_error* errInfo, /* Where to return error codes. */ - int fd, /* File descriptor to read from. */ - Sint64 offset, /* Offset in bytes from BOF. */ - char* buf, /* Buffer to read into. */ - size_t count, /* Number of bytes to read. */ - size_t *pBytesRead) /* Where to return - number of bytes read. */ -{ - int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); - if (res) { - return efile_read(errInfo, 0, fd, buf, count, pBytesRead); - } else { - return res; - } -} - - -/***************************************************************************/ - -int -efile_pwrite(Efile_error* errInfo, /* Where to return error codes. */ - int fd, /* File descriptor to write to. */ - char* buf, /* Buffer to write. */ - size_t count, /* Number of bytes to write. */ - Sint64 offset) /* where to write it */ -{ - int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); - - if (res) { - return efile_write(errInfo, 0, fd, buf, count); - } else { - return res; - } -} - -/***************************************************************************/ - -int -efile_seek(Efile_error* errInfo, /* Where to return error codes. */ - int fd, /* File descriptor to do the seek on. */ - Sint64 offset, /* Offset in bytes from the given - origin. */ - int origin, /* Origin of seek (SEEK_SET, SEEK_CUR, - SEEK_END). */ - Sint64 *new_location) /* Resulting new location in file. */ -{ - off_t off, result; - off = (off_t) offset; - - switch (origin) { - case EFILE_SEEK_SET: - origin = SEEK_SET; - break; - case EFILE_SEEK_CUR: - origin = SEEK_CUR; - break; - case EFILE_SEEK_END: - origin = SEEK_END; - break; - default: - errno = EINVAL; - return check_error(-1, errInfo); - } - - if (off != offset) { - errno = EINVAL; - return check_error(-1, errInfo); - } - - errno = 0; - result = lseek(fd, off, origin); - - if (result >= 0) { - l_invalidate_local_fd(fd); - } - - if (result < 0) - { - if (errno == ENOSYS) { - int size, cur_pos; - - if (off < 0) { - errno = EINVAL; - return check_error(-1, errInfo); - } - - cur_pos = lseek(fd, 0, SEEK_CUR); - size = lseek(fd, 0, SEEK_END); - - if (origin == SEEK_SET) { - result = offset; - } - else if (origin == SEEK_CUR) { - result = offset + cur_pos; - } - else if (origin == SEEK_END) { - result = size + offset; - } - - /* sanity check our result */ - if (size > result) { - return check_error(-1, errInfo); - } - - /* store the data localy */ - l_update_local_fd(fd, result, size); - - /* reset the original file position */ - if (origin != SEEK_END) { - lseek(fd, cur_pos, SEEK_SET); - } - } - else if (errno == 0) { - errno = EINVAL; - } - } - - if (new_location) { - *new_location = result; - } - - return 1; -} - -/***************************************************************************/ - -int -efile_truncate_file(Efile_error* errInfo, int *fd, int flags) -{ - off_t offset; - struct fd_data *fd_data; - - if ((fd_data = l_find_local_fd(*fd)) != NULL && L_FD_IS_VALID(fd_data)) { - offset = L_FD_CUR(fd_data); - } - else { - offset = lseek(*fd, 0, SEEK_CUR); - } - - return check_error(((offset >= 0) && - (ftruncate(*fd, offset) == 0)) ? 1 : -1, errInfo); -} - -/***************************************************************************/ - -int -efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) -{ - errno = ENOTSUP; - return check_error(-1, errInfo); -} - -/***************************************************************************/ - -int -efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size) -{ - errno = ENOTSUP; - return check_error(-1, errInfo); -} - -/***************************************************************************/ - -int -efile_link(Efile_error* errInfo, char* old, char* new) -{ - errno = ENOTSUP; - return check_error(-1, errInfo); -} - -/***************************************************************************/ - -int -efile_symlink(Efile_error* errInfo, char* old, char* new) -{ - errno = ENOTSUP; - return check_error(-1, errInfo); -} - -/***************************************************************************/ - -int -efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, - Sint64 length, int advise) -{ - return check_error(posix_fadvise(fd, offset, length, advise), errInfo); -} - -/***************************************************************************/ - -static int -call_posix_fallocate(int fd, Sint64 offset, Sint64 length) -{ - int ret; - - /* - * On Linux and Solaris for example, posix_fallocate() returns - * a positive error number on error and it does not set errno. - * On FreeBSD however (9.0 at least), it returns -1 on error - * and it sets errno. - */ - do { - ret = posix_fallocate(fd, (off_t) offset, (off_t) length); - if (ret > 0) { - errno = ret; - ret = -1; - } - } while (ret != 0 && errno == EINTR); - - return ret; -} - -/***************************************************************************/ - -int -efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length) -{ - return check_error(call_posix_fallocate(fd, offset, length), errInfo); -} diff --git a/erts/emulator/drivers/ose/ose_signal_drv.c b/erts/emulator/drivers/ose/ose_signal_drv.c deleted file mode 100644 index 2be9462a47..0000000000 --- a/erts/emulator/drivers/ose/ose_signal_drv.c +++ /dev/null @@ -1,897 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2013-2013. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "errno.h" -#include "stdio.h" -#include "string.h" -#include "stddef.h" - -#include "sys.h" -#include "erl_driver.h" -#include "ose.h" - - -#ifdef HAVE_OSE_SPI_H -#include "ose_spi/ose_spi.h" -#endif - -#define DEBUG_ATTACH 0 -#define DEBUG_HUNT 0 -#define DEBUG_SEND 0 -#define DEBUG_LISTEN 0 - -#if 0 -#define DEBUGP(FMT,...) printf(FMT, __VA_ARGS__) -#else -#define DEBUGP(FMT,...) -#endif - -#if DEBUG_ATTACH -#define DEBUGP_ATTACH(...) DEBUGP( __VA_ARGS__) -#else -#define DEBUGP_ATTACH(...) -#endif - -#if DEBUG_HUNT -#define DEBUGP_HUNT(...) DEBUGP( __VA_ARGS__) -#else -#define DEBUGP_HUNT(...) -#endif - -#if DEBUG_LISTEN -#define DEBUGP_LISTEN(...) DEBUGP( __VA_ARGS__) -#else -#define DEBUGP_LISTEN(...) -#endif - -#if DEBUG_SEND -#define DEBUGP_SEND(...) DEBUGP( __VA_ARGS__) -#else -#define DEBUGP_SEND(...) -#endif - - -#define DRIVER_NAME "ose_signal_drv" -#define GET_SPID 1 -#define GET_NAME 2 -#define HUNT 100 -#define DEHUNT 101 -#define ATTACH 102 -#define DETACH 103 -#define SEND 104 -#define SEND_W_S 105 -#define LISTEN 106 -#define OPEN 200 - -#define REF_SEGMENT_SIZE 8 - -struct async { - SIGSELECT signo; - ErlDrvTermData port; - ErlDrvTermData proc; - PROCESS spid; - PROCESS target; - Uint32 ref; -}; - -/** - * OSE signals - **/ -union SIGNAL { - SIGSELECT signo; - struct async async; -}; - -/** - * The driver's context - **/ -typedef struct _driver_context { - ErlDrvPort port; - PROCESS spid; - ErlDrvEvent perm_events[2]; - ErlDrvEvent *events; - Uint32 event_cnt; - Uint32 ref; - Uint32 *outstanding_refs; - Uint32 outstanding_refs_max; - Uint32 outstanding_refs_cnt; -} driver_context_t; - -/** - * Global variables - **/ -static ErlDrvTermData a_ok; -static ErlDrvTermData a_error; -static ErlDrvTermData a_enomem; -static ErlDrvTermData a_enoent; -static ErlDrvTermData a_badarg; -static ErlDrvTermData a_mailbox_up; -static ErlDrvTermData a_mailbox_down; -static ErlDrvTermData a_ose_drv_reply; -static ErlDrvTermData a_message; -static PROCESS proxy_proc; - - -/** - * Serialize/unserialize unsigned 32-bit values - **/ -static char *put_u32(unsigned int value, char *ptr) { - *ptr++ = (value & 0xff000000) >> 24; - *ptr++ = (value & 0x00ff0000) >> 16; - *ptr++ = (value & 0x0000ff00) >> 8; - *ptr++ = (value & 0xff); - - return ptr; -} - -static unsigned int get_u32(char *ptr) { - unsigned int result = 0; - result += (ptr[0] & 0xff) << 24; - result += (ptr[1] & 0xff) << 16; - result += (ptr[2] & 0xff) << 8; - result += (ptr[3] & 0xff); - - return result; -} - - -/* Stolen from efile_drv.c */ - -/* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */ -#define EV_CHAR_P(ev, p, q) \ - (((char *)(ev)->iov[(q)].iov_base) + (p)) - -/* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */ -#define EV_GET_CHAR(ev, p, pp, qp) ev_get_char(ev, p ,pp, qp) -static int -ev_get_char(ErlIOVec *ev, char *p, int *pp, int *qp) { - if (*(pp)+1 <= (ev)->iov[*(qp)].iov_len) { - *(p) = *EV_CHAR_P(ev, *(pp), *(qp)); - if (*(pp)+1 < (ev)->iov[*(qp)].iov_len) - *(pp) = *(pp)+1; - else { - (*(qp))++; - *pp = 0; - } - return !0; - } - return 0; -} - -/* Uint32 EV_UINT32(ErlIOVec *ev, int p, int q)*/ -#define EV_UINT32(ev, p, q) \ - ((Uint32) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p))) - -/* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */ -#define EV_GET_UINT32(ev, p, pp, qp) ev_get_uint32(ev,(Uint32*)(p),pp,qp) -static int -ev_get_uint32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) { - if (*(pp)+4 <= (ev)->iov[*(qp)].iov_len) { - *(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24) - | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16) - | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8) - | (EV_UINT32(ev, *(pp)+3, *(qp))); - if (*(pp)+4 < (ev)->iov[*(qp)].iov_len) - *(pp) = *(pp)+4; - else { - (*(qp))++; - *pp = 0; - } - return !0; - } - return 0; -} - -/** - * Convinience macros - **/ -#define send_response(port,output) erl_drv_send_term(driver_mk_port(port),\ - driver_caller(port), output, sizeof(output) / sizeof(output[0])); - -void iov_memcpy(void *dest,ErlIOVec *ev,int ind,int off); -void iov_memcpy(void *dest,ErlIOVec *ev,int ind,int off) { - int i; - memcpy(dest,ev->iov[ind].iov_base+off,ev->iov[ind].iov_len-off); - for (i = ind+1; i < ev->vsize; i++) - memcpy(dest,ev->iov[i].iov_base,ev->iov[i].iov_len); -} - -/** - * Reference handling - **/ - -static int add_reference(driver_context_t *ctxt, Uint32 ref) { - - /* - * Premature optimizations may be evil, but they sure are fun. - */ - - if (ctxt->outstanding_refs == NULL) { - /* First ref to be ignored */ - ctxt->outstanding_refs = driver_alloc(REF_SEGMENT_SIZE*sizeof(Uint32)); - if (!ctxt->outstanding_refs) - return 1; - - memset(ctxt->outstanding_refs,0,REF_SEGMENT_SIZE*sizeof(Uint32)); - ctxt->outstanding_refs_max += REF_SEGMENT_SIZE; - ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref; - } else if (ctxt->outstanding_refs_cnt == ctxt->outstanding_refs_max) { - /* Expand ref array */ - Uint32 *new_array; - ctxt->outstanding_refs_max += REF_SEGMENT_SIZE; - new_array = driver_realloc(ctxt->outstanding_refs, - ctxt->outstanding_refs_max*sizeof(Uint32)); - - if (!new_array) { - ctxt->outstanding_refs_max -= REF_SEGMENT_SIZE; - return 1; - } - - ctxt->outstanding_refs = new_array; - - memset(ctxt->outstanding_refs+ctxt->outstanding_refs_cnt,0, - REF_SEGMENT_SIZE*sizeof(Uint32)); - ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref; - - } else { - /* Find an empty slot: - * First we try current index, - * then we scan for a slot. - */ - if (!ctxt->outstanding_refs[ctxt->outstanding_refs_cnt]) { - ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref; - } else { - int i; - ASSERT(ctxt->outstanding_refs_cnt < ctxt->outstanding_refs_max); - for (i = 0; i < ctxt->outstanding_refs_max; i++) - if (!ctxt->outstanding_refs[i]) - break; - ASSERT(ctxt->outstanding_refs[i] == 0); - ctxt->outstanding_refs[i] = ref; - ctxt->outstanding_refs_cnt++; - } - } - return 0; -} - -/* Return 0 if removed, 1 if does not exist, */ -static int remove_reference(driver_context_t *ctxt, Uint32 ref) { - int i,j; - - if (ctxt->outstanding_refs_max == 0 && ctxt->outstanding_refs_cnt == 0) { - ASSERT(ctxt->outstanding_refs == NULL); - return 1; - } - - for (i = 0; i < ctxt->outstanding_refs_max; i++) { - if (ctxt->outstanding_refs[i] == ref) { - ctxt->outstanding_refs[i] = 0; - ctxt->outstanding_refs_cnt--; - i = -1; - break; - } - } - - if (i != -1) - return 1; - - if (ctxt->outstanding_refs_cnt == 0) { - driver_free(ctxt->outstanding_refs); - ctxt->outstanding_refs = NULL; - ctxt->outstanding_refs_max = 0; - } else if (ctxt->outstanding_refs_cnt == (ctxt->outstanding_refs_max - REF_SEGMENT_SIZE)) { - Uint32 *new_array; - for (i = 0, j = 0; i < ctxt->outstanding_refs_cnt; i++) { - if (ctxt->outstanding_refs[i] == 0) { - for (j = i+1; j < ctxt->outstanding_refs_max; j++) - if (ctxt->outstanding_refs[j]) { - ctxt->outstanding_refs[i] = ctxt->outstanding_refs[j]; - ctxt->outstanding_refs[j] = 0; - break; - } - } - } - ctxt->outstanding_refs_max -= REF_SEGMENT_SIZE; - new_array = driver_realloc(ctxt->outstanding_refs, - ctxt->outstanding_refs_max*sizeof(Uint32)); - if (!new_array) { - ctxt->outstanding_refs_max += REF_SEGMENT_SIZE; - return 2; - } - - ctxt->outstanding_refs = new_array; - - } - - return 0; -} - -/** - * The OSE proxy process. This only handles ERTS_SIGNAL_OSE_DRV_ATTACH. - * The process is needed because signals triggered by attach ignore - * redir tables. - * - * We have one global proxy process to save memory. An attempt to make each - * port phantom into a proxy was made, but that used way to much memory. - */ -static OS_PROCESS(driver_proxy_process) { - SIGSELECT sigs[] = {1,ERTS_SIGNAL_OSE_DRV_ATTACH}; - PROCESS master = 0; - - while (1) { - union SIGNAL *sig = receive(sigs); - - if (sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) { - - /* The first message is used to determine who to send messages to. */ - if (master == 0) - master = sender(&sig); - - if (sig->async.target == 0) { - PROCESS from = sender(&sig); - restore(sig); - DEBUGP_ATTACH("0x%x: got attach 0x%x, sending to 0x%x\n", - current_process(),from,master); - sig->async.target = from; - send(&sig,master); - } else { - PROCESS target = sig->async.target; - restore(sig); - sig->async.target = 0; - DEBUGP_ATTACH("0x%x: doing attach on 0x%x\n",current_process(),target); - attach(&sig,target); - } - } - } -} - - -/** - * Init routine for the driver - **/ -static int drv_init(void) { - - a_ok = driver_mk_atom("ok"); - a_error = driver_mk_atom("error"); - a_enomem = driver_mk_atom("enomem"); - a_enoent = driver_mk_atom("enoent"); - a_badarg = driver_mk_atom("badarg"); - a_mailbox_up = driver_mk_atom("mailbox_up"); - a_mailbox_down = driver_mk_atom("mailbox_down"); - a_ose_drv_reply = driver_mk_atom("ose_drv_reply"); - a_message = driver_mk_atom("message"); - - proxy_proc = create_process(get_ptype(current_process()), - "ose_signal_driver_proxy", - driver_proxy_process, 10000, - get_pri(current_process()), - 0, 0, NULL, 0, 0); - -#ifdef DEBUG - efs_clone(proxy_proc); -#endif - start(proxy_proc); - - return 0; -} - -/* Signal resolution callback */ -static ErlDrvOseEventId resolve_signal(union SIGNAL* osig) { - union SIGNAL *sig = osig; - if (sig->signo == ERTS_SIGNAL_OSE_DRV_HUNT || - sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) { - return sig->async.spid; - } - DEBUGP("%p: Got signal %d sent to %p from 0x%p\n", - current_process(),sig->signo,addressee(&sig),sender(&sig)); - return addressee(&sig); -} - - -/** - * Start routine for the driver - **/ -static ErlDrvData drv_start(ErlDrvPort port, char *command) -{ - driver_context_t *ctxt = driver_alloc(sizeof(driver_context_t)); - - ctxt->perm_events[0] = NULL; - ctxt->perm_events[1] = NULL; - - ctxt->spid = 0; - ctxt->port = port; - ctxt->event_cnt = 0; - ctxt->events = NULL; - ctxt->ref = 0; - ctxt->outstanding_refs = NULL; - ctxt->outstanding_refs_max = 0; - ctxt->outstanding_refs_cnt = 0; - - - /* Set the communication protocol to Erlang to be binary */ - set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); - - /* Everything ok */ - return (ErlDrvData)ctxt; -} - -/** - * Stop routine for the driver - **/ -static void drv_stop(ErlDrvData driver_data) -{ - driver_context_t *ctxt = (driver_context_t *)driver_data; - int i; - - /* HUNT + ATTACH */ - if (ctxt->perm_events[0]) - driver_select(ctxt->port, ctxt->perm_events[0], - ERL_DRV_USE|ERL_DRV_READ, 0); - if (ctxt->perm_events[1]) - driver_select(ctxt->port, ctxt->perm_events[1], - ERL_DRV_USE|ERL_DRV_READ, 0); - - for (i = 0; i < ctxt->event_cnt; i++) { - driver_select(ctxt->port, ctxt->events[i], ERL_DRV_USE|ERL_DRV_READ, 0); - } - - if (ctxt->spid != 0) - kill_proc(ctxt->spid); - DEBUGP("0x%x: stopped\n",ctxt->spid); - if (ctxt->events) - driver_free(ctxt->events); - if (ctxt->outstanding_refs) - driver_free(ctxt->outstanding_refs); - - driver_free(ctxt); -} - -/** - * Output from Erlang - **/ -static void outputv(ErlDrvData driver_data, ErlIOVec *ev) -{ - driver_context_t *ctxt = (driver_context_t *)driver_data; - int p = 0, q = 1; - char cmd; - - if (! EV_GET_CHAR(ev,&cmd,&p,&q)) { - ErlDrvTermData output[] = { - ERL_DRV_ATOM, a_ose_drv_reply, - ERL_DRV_PORT, driver_mk_port(ctxt->port), - ERL_DRV_ATOM, a_badarg, - ERL_DRV_TUPLE, 3}; - send_response(ctxt->port, output); - return; - } - - /* Command is in the buffer's first byte */ - switch(cmd) { - - case OPEN: { - char *name = driver_alloc(ev->size - 1+1); - struct OS_redir_entry redir[2]; - - redir[0].sig = 1; - redir[0].pid = current_process(); - - iov_memcpy(name,ev,q,p); - name[ev->size-1] = '\0'; - - ctxt->spid = create_process(OS_PHANTOM, name, NULL, 0, - 0, 0, 0, redir, 0, 0); - - DEBUGP("0x%x: open\n",ctxt->spid); - - ctxt->perm_events[1] = - erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH,(int)ctxt->spid, - resolve_signal, NULL); - driver_select(ctxt->port,ctxt->perm_events[1],ERL_DRV_READ|ERL_DRV_USE,1); - - ctxt->perm_events[0] = - erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_HUNT,(int)ctxt->spid, - resolve_signal, NULL); - driver_select(ctxt->port,ctxt->perm_events[0],ERL_DRV_READ|ERL_DRV_USE,1); - - start(ctxt->spid); - - { - ErlDrvTermData output[] = { - ERL_DRV_ATOM, a_ose_drv_reply, - ERL_DRV_PORT, driver_mk_port(ctxt->port), - ERL_DRV_ATOM, a_ok, - ERL_DRV_TUPLE, 3}; - - send_response(ctxt->port, output); - } - - break; - - } - - case ATTACH: - case HUNT: - { - union SIGNAL *sig = alloc(sizeof(union SIGNAL), - cmd == HUNT ? ERTS_SIGNAL_OSE_DRV_HUNT:ERTS_SIGNAL_OSE_DRV_ATTACH); - - sig->async.port = driver_mk_port(ctxt->port); - sig->async.proc = driver_caller(ctxt->port); - sig->async.spid = ctxt->spid; - sig->async.ref = ++ctxt->ref; - - if (add_reference(ctxt,ctxt->ref)) { - ErlDrvTermData output[] = { - ERL_DRV_ATOM, a_ose_drv_reply, - ERL_DRV_PORT, driver_mk_port(ctxt->port), - ERL_DRV_ATOM, a_enomem, - ERL_DRV_TUPLE, 3}; - send_response(ctxt->port, output); - free_buf(&sig); - } else { - ErlDrvTermData output[] = { - ERL_DRV_ATOM, a_ose_drv_reply, - ERL_DRV_PORT, driver_mk_port(ctxt->port), - ERL_DRV_PORT, driver_mk_port(ctxt->port), - ERL_DRV_INT, (ErlDrvUInt)ctxt->ref, - ERL_DRV_TUPLE, 2, - ERL_DRV_TUPLE, 3}; - send_response(ctxt->port, output); - - if (cmd == HUNT) { - char *huntname = driver_alloc(sizeof(char)*((ev->size-1)+1)); - - iov_memcpy(huntname,ev,q,p); - huntname[ev->size-1] = '\0'; - - DEBUGP_HUNT("0x%x: hunt %s -> %u (%u,%u)\n", - ctxt->spid,huntname,ctxt->ref, - ctxt->outstanding_refs_cnt, - ctxt->outstanding_refs_max); - - hunt(huntname, 0, NULL, &sig); - - driver_free(huntname); - } else { - EV_GET_UINT32(ev,&sig->async.target,&p,&q); - DEBUGP_ATTACH("0x%x: attach %u -> %u (%u,%u)\n", - ctxt->spid,sig->async.target, - ctxt->ref, - ctxt->outstanding_refs_cnt, - ctxt->outstanding_refs_max); - - send(&sig,proxy_proc); - } - - } - - break; - } - - case DETACH: - case DEHUNT: - { - - Uint32 ref; - - EV_GET_UINT32(ev,&ref,&p,&q); - if (cmd == DETACH) { - DEBUGP_ATTACH("0x%x: detach %u (%u,%u)\n",ctxt->spid,ref, - ctxt->outstanding_refs_cnt, - ctxt->outstanding_refs_max); - } else { - DEBUGP_HUNT("0x%x: dehunt %u (%u,%u)\n",ctxt->spid,ref, - ctxt->outstanding_refs_cnt, - ctxt->outstanding_refs_max); - } - - if (remove_reference(ctxt,ref)) { - ErlDrvTermData output[] = { - ERL_DRV_ATOM, a_ose_drv_reply, - ERL_DRV_PORT, driver_mk_port(ctxt->port), - ERL_DRV_ATOM, a_error, - ERL_DRV_ATOM, a_enoent, - ERL_DRV_TUPLE, 2, - ERL_DRV_TUPLE, 3}; - - send_response(ctxt->port, output); - } else { - ErlDrvTermData output[] = { - ERL_DRV_ATOM, a_ose_drv_reply, - ERL_DRV_PORT, driver_mk_port(ctxt->port), - ERL_DRV_ATOM, a_ok, - ERL_DRV_TUPLE, 3}; - - send_response(ctxt->port, output); - } - - break; - } - - case SEND: - case SEND_W_S: - { - PROCESS spid; - PROCESS sender; - SIGSELECT signo; - OSBUFSIZE size = ev->size-9; - union SIGNAL *sig; - - EV_GET_UINT32(ev,&spid,&p,&q); - - if (cmd == SEND_W_S) { - EV_GET_UINT32(ev,&sender,&p,&q); - size -= 4; - } else { - sender = ctxt->spid; - } - - EV_GET_UINT32(ev,&signo,&p,&q); - - sig = alloc(size + sizeof(SIGSELECT),signo); - - if (cmd == SEND_W_S) { - DEBUGP_SEND("0x%x: send_w_s(%u,%u,%u)\n",ctxt->spid,spid,signo,sender); - } else { - DEBUGP_SEND("0x%x: send(%u,%u)\n",ctxt->spid,spid,signo); - } - - iov_memcpy(((char *)&sig->signo) + sizeof(SIGSELECT),ev,q,p); - - send_w_s(&sig, sender, spid); - - break; - } - - case LISTEN: - { - int i,j,event_cnt = (ev->size - 1)/4; - ErlDrvEvent *events = NULL; - SIGSELECT signo,tmp_signo; - - if (event_cnt == 0) { - for (i = 0; i < ctxt->event_cnt; i++) - driver_select(ctxt->port,ctxt->events[i],ERL_DRV_READ|ERL_DRV_USE,0); - if (ctxt->events) - driver_free(ctxt->events); - } else { - events = driver_alloc(sizeof(ErlDrvEvent)*event_cnt); - EV_GET_UINT32(ev,&signo,&p,&q); - for (i = 0, j = 0; i < event_cnt || j < ctxt->event_cnt; ) { - - if (ctxt->events) - erl_drv_ose_event_fetch(ctxt->events[j],&tmp_signo,NULL,NULL); - - if (signo == tmp_signo) { - events[i++] = ctxt->events[j++]; - EV_GET_UINT32(ev,&signo,&p,&q); - } else if (signo < tmp_signo || !ctxt->events) { - /* New signal to select on */ - events[i] = erl_drv_ose_event_alloc(signo,(int)ctxt->spid, - resolve_signal, NULL); - driver_select(ctxt->port,events[i++],ERL_DRV_READ|ERL_DRV_USE,1); - EV_GET_UINT32(ev,&signo,&p,&q); - } else { - /* Remove old signal to select on */ - driver_select(ctxt->port,ctxt->events[j++],ERL_DRV_READ|ERL_DRV_USE,0); - } - } - if (ctxt->events) - driver_free(ctxt->events); - } - ctxt->events = events; - ctxt->event_cnt = event_cnt; - - { - ErlDrvTermData output[] = { - ERL_DRV_ATOM, a_ose_drv_reply, - ERL_DRV_PORT, driver_mk_port(ctxt->port), - ERL_DRV_ATOM, a_ok, - ERL_DRV_TUPLE, 3}; - send_response(ctxt->port, output); - } - break; - } - - default: - { - DEBUGP("Warning: 'ose_signal_drv' unknown command '%d'\n", cmd); - break; - } - } -} - -/** - * Handler for when OSE signal arrives - **/ -static void ready_input(ErlDrvData driver_data, ErlDrvEvent event) -{ - driver_context_t *ctxt = (driver_context_t *)driver_data; - union SIGNAL *sig = erl_drv_ose_get_signal(event); - - while (sig != NULL) { - - switch(sig->signo) - { - /* Remote process is available */ - case ERTS_SIGNAL_OSE_DRV_HUNT: - { - const PROCESS spid = sender(&sig); - - if (remove_reference(ctxt,sig->async.ref)) { - DEBUGP_HUNT("0x%x: Got hunt from 0x%x -> %u (CANCELLED) (%u,%u)\n", - ctxt->spid,spid,sig->async.ref, - ctxt->outstanding_refs_cnt, - ctxt->outstanding_refs_max); - /* Already removed by dehunt */ - } else { - ErlDrvTermData reply[] = { - ERL_DRV_ATOM, a_mailbox_up, - ERL_DRV_PORT, sig->async.port, - ERL_DRV_PORT, sig->async.port, - ERL_DRV_UINT, (ErlDrvUInt)sig->async.ref, - ERL_DRV_TUPLE, 2, - ERL_DRV_UINT, (ErlDrvUInt)spid, - ERL_DRV_TUPLE, 4}; - DEBUGP_HUNT("0x%x: Got hunt from 0x%x -> %u (%u,%u)\n", - ctxt->spid,spid,sig->async.ref, - ctxt->outstanding_refs_cnt, - ctxt->outstanding_refs_max); - erl_drv_send_term(sig->async.port, sig->async.proc, reply, - sizeof(reply) / sizeof(reply[0])); - } - break; - } - - /* Remote process is down */ - case ERTS_SIGNAL_OSE_DRV_ATTACH: - { - PROCESS spid = sig->async.target; - - if (remove_reference(ctxt,sig->async.ref)) { - DEBUGP_ATTACH("0x%x: Got attach from 0x%x -> %u (CANCELLED) (%u,%u)\n", - ctxt->spid,spid,sig->async.ref, - ctxt->outstanding_refs_cnt, - ctxt->outstanding_refs_max); - /* Already removed by detach */ - } else { - ErlDrvTermData reply[] = { - ERL_DRV_ATOM, a_mailbox_down, - ERL_DRV_PORT, sig->async.port, - ERL_DRV_PORT, sig->async.port, - ERL_DRV_UINT, sig->async.ref, - ERL_DRV_TUPLE, 2, - ERL_DRV_UINT, (ErlDrvUInt)spid, - ERL_DRV_TUPLE, 4}; - DEBUGP_ATTACH("0x%x: Got attach from 0x%x -> %u (%u,%u)\n", - ctxt->spid,spid,sig->async.ref, - ctxt->outstanding_refs_cnt, - ctxt->outstanding_refs_max); - erl_drv_send_term(sig->async.port, sig->async.proc, reply, - sizeof(reply) / sizeof(reply[0])); - } - break; - } - - /* Received user defined signal */ - default: - { - const PROCESS spid = sender(&sig); - const OSBUFSIZE size = sigsize(&sig) - sizeof(SIGSELECT); - const char *sig_data = ((char *)&sig->signo) + sizeof(SIGSELECT); - - ErlDrvTermData reply[] = { - ERL_DRV_ATOM, a_message, - ERL_DRV_PORT, driver_mk_port(ctxt->port), - ERL_DRV_UINT, (ErlDrvUInt)spid, - ERL_DRV_UINT, (ErlDrvUInt)ctxt->spid, - ERL_DRV_UINT, (ErlDrvUInt)sig->signo, - ERL_DRV_BUF2BINARY, (ErlDrvTermData)sig_data, (ErlDrvUInt)size, - ERL_DRV_TUPLE, 4, - ERL_DRV_TUPLE, 3}; - - DEBUGP_SEND("0x%x: Got 0x%u\r\n", spid, sig->signo); - - erl_drv_output_term(driver_mk_port(ctxt->port), reply, - sizeof(reply) / sizeof(reply[0])); - break; - } - } - - free_buf(&sig); - sig = erl_drv_ose_get_signal(event); - } -} - -/** - * Handler for 'port_control' - **/ -static ErlDrvSSizeT control(ErlDrvData driver_data, unsigned int cmd, - char *buf, ErlDrvSizeT len, - char **rbuf, ErlDrvSizeT rlen) -{ - driver_context_t *ctxt = (driver_context_t *)driver_data; - - switch(cmd) - { - case GET_SPID: - { - const PROCESS spid = ctxt->spid; - put_u32(spid, *rbuf); - return sizeof(PROCESS); - } - -#ifdef HAVE_OSE_SPI_H - case GET_NAME: - { - const PROCESS spid = get_u32(buf); - char *name = (char*)get_pid_info(spid,OSE_PI_NAME); - int n; - if (!name) { - *rbuf = NULL; - return 0; - } - - if (rlen < (n = strlen(name))) { - ErlDrvBinary *bin = driver_alloc_binary(n); - strncpy(bin->orig_bytes,name,n); - *rbuf = (char*)bin; - } else - strncpy(*rbuf,name,n); - free_buf((union SIGNAL**)&name); - - return n; - } -#endif - default: - { - /* Unknown command */ - return (ErlDrvSSizeT)ERL_DRV_ERROR_GENERAL; - break; - } - } -} - -static void stop_select(ErlDrvEvent event, void *reserved) -{ - erl_drv_ose_event_free(event); -} - -/** - * Setup the driver entry for the Erlang runtime - **/ -ErlDrvEntry ose_signal_driver_entry = { - .init = drv_init, - .start = drv_start, - .stop = drv_stop, - .outputv = outputv, - .ready_input = ready_input, - .driver_name = DRIVER_NAME, - .control = control, - .extended_marker = ERL_DRV_EXTENDED_MARKER, - .major_version = ERL_DRV_EXTENDED_MAJOR_VERSION, - .minor_version = ERL_DRV_EXTENDED_MINOR_VERSION, - .driver_flags = ERL_DRV_FLAG_USE_PORT_LOCKING, - .stop_select = stop_select -}; - diff --git a/erts/emulator/drivers/ose/ttsl_drv.c b/erts/emulator/drivers/ose/ttsl_drv.c deleted file mode 100644 index f759b47984..0000000000 --- a/erts/emulator/drivers/ose/ttsl_drv.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -/* - * Stub tty driver because group/user depend on this. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "erl_driver.h" - -static int ttysl_init(void); -static ErlDrvData ttysl_start(ErlDrvPort, char*); - -/* Define the driver table entry. */ -struct erl_drv_entry ttsl_driver_entry = { - ttysl_init, - ttysl_start, - NULL, - NULL, - NULL, - NULL, - "tty_sl", - NULL, - NULL, - NULL, - NULL, /* timeout */ - NULL, /* outputv */ - NULL, /* ready_async */ - NULL, /* flush */ - NULL, /* call */ - NULL, /* event */ - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, - 0, /* ERL_DRV_FLAGs */ - NULL, - NULL, /* process_exit */ - NULL -}; - - -static int ttysl_init(void) -{ - return 0; -} - -static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) -{ - return ERL_DRV_ERROR_GENERAL; -} diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 105b129065..f87196d724 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -1337,11 +1337,7 @@ print_select_op(erts_dsprintf_buf_t *dsbufp, { Port *pp = erts_drvport2port(ix); erts_dsprintf(dsbufp, -#ifdef __OSE__ - "driver_select(%p, %d,%s%s%s%s | %d, %d) " -#else "driver_select(%p, %d,%s%s%s%s, %d) " -#endif "by ", ix, (int) GET_FD(fd), @@ -1861,25 +1857,6 @@ stale_drv_select(Eterm id, ErtsDrvEventState *state, int mode) #ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS -#ifdef __OSE__ -static SafeHashValue drv_ev_state_hash(void *des) -{ - ErtsSysFdType fd = ((ErtsDrvEventState *) des)->fd; - /* We use hash on signo ^ id in order for steal to happen when the - same signo + fd is selected on by two different ports */ - SafeHashValue val = (SafeHashValue)(fd->signo ^ fd->id); - return val ^ (val >> 8); -} - -static int drv_ev_state_cmp(void *des1, void *des2) -{ - ErtsSysFdType fd1 = ((ErtsDrvEventState *) des1)->fd; - ErtsSysFdType fd2 = ((ErtsDrvEventState *) des2)->fd; - if (fd1->signo == fd2->signo && fd1->id == fd2->id) - return 0; - return 1; -} -#else /* !__OSE__ && !ERTS_SYS_CONTINOUS_FD_NUMBERS i.e. probably windows */ static SafeHashValue drv_ev_state_hash(void *des) { SafeHashValue val = (SafeHashValue) ((ErtsDrvEventState *) des)->fd; @@ -1891,7 +1868,6 @@ static int drv_ev_state_cmp(void *des1, void *des2) return ( ((ErtsDrvEventState *) des1)->fd == ((ErtsDrvEventState *) des2)->fd ? 0 : 1); } -#endif static void *drv_ev_state_alloc(void *des_tmpl) { diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index 19ce582154..6d8aef822e 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -93,7 +93,7 @@ # if defined(ERTS_USE_POLL) # undef ERTS_POLL_USE_POLL # define ERTS_POLL_USE_POLL 1 -# elif !defined(__WIN32__) && !defined(__OSE__) +# elif !defined(__WIN32__) # undef ERTS_POLL_USE_SELECT # define ERTS_POLL_USE_SELECT 1 # endif @@ -104,31 +104,13 @@ typedef Uint32 ErtsPollEvents; #undef ERTS_POLL_EV_E2N -#if defined(__WIN32__) || defined(__OSE__) /* --- win32 or ose -------- */ +#if defined(__WIN32__) /* --- win32 --------------------------------------- */ #define ERTS_POLL_EV_IN 1 #define ERTS_POLL_EV_OUT 2 #define ERTS_POLL_EV_ERR 4 #define ERTS_POLL_EV_NVAL 8 -#ifdef __OSE__ - -typedef struct ErtsPollOseMsgList_ { - struct ErtsPollOseMsgList_ *next; - union SIGNAL *data; -} ErtsPollOseMsgList; - -struct erts_sys_fd_type { - SIGSELECT signo; - ErlDrvOseEventId id; - ErtsPollOseMsgList *msgs; - ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig); - ethr_mutex mtx; - void *extra; -}; - -#endif - #elif ERTS_POLL_USE_EPOLL /* --- epoll ------------------------------- */ #include diff --git a/erts/emulator/sys/ose/beam.lmconf b/erts/emulator/sys/ose/beam.lmconf deleted file mode 100644 index 4ad46b01d9..0000000000 --- a/erts/emulator/sys/ose/beam.lmconf +++ /dev/null @@ -1,26 +0,0 @@ -OSE_LM_STACK_SIZES=256,512,1024,2048,4096,8192,16384,65536 -OSE_LM_SIGNAL_SIZES=31,63,127,255,1023,4095,16383,65535 -OSE_LM_POOL_SIZE=0x200000 -OSE_LM_MAIN_NAME=main -OSE_LM_MAIN_STACK_SIZE=0xF000 -OSE_LM_MAIN_PRIORITY=20 -## Has to be of a type that allows MAM -OSE_LM_PROGRAM_TYPE=APP_RAM -OSE_LM_DATA_INIT=YES -OSE_LM_BSS_INIT=YES -OSE_LM_EXEC_MODEL=SHARED -HEAP_MAX_SIZE=1000000000 -HEAP_SMALL_BUF_INIT_SIZE=20971520 -HEAP_LARGE_BUF_THRESHOLD=16000000 -HEAP_LOCK_TYPE=2 - -ERTS_DEFAULT_PRIO=24 -ERTS_SCHEDULER_PRIO=24 -ERTS_ASYNC_PRIO=22 -ERTS_AUX_PRIO=24 -ERTS_SYS_MSG_DISPATCHER_PRIO=21 - -# Setting the environment variable EFS_RESOLVE_TMO on the block to 0. -# This will eliminiate delays when trying to open files on not mounted -# volumes. -EFS_RESOLVE_TMO=0 diff --git a/erts/emulator/sys/ose/driver_int.h b/erts/emulator/sys/ose/driver_int.h deleted file mode 100644 index 4a5b7171d1..0000000000 --- a/erts/emulator/sys/ose/driver_int.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 1997-2009. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -/* - * System dependant driver declarations - */ - -#ifndef __DRIVER_INT_H__ -#define __DRIVER_INT_H__ - -#ifdef HAVE_SYS_UIO_H -#include -#include - -typedef struct iovec SysIOVec; - -#else - -typedef struct { - char* iov_base; - int iov_len; -} SysIOVec; - -#endif - -#endif diff --git a/erts/emulator/sys/ose/erl_main.c b/erts/emulator/sys/ose/erl_main.c deleted file mode 100644 index 877e85f43a..0000000000 --- a/erts/emulator/sys/ose/erl_main.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2000-2009. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include - -#include "sys.h" -#include "erl_vm.h" -#include "global.h" -#include "ose.h" - -int -main(int argc, char **argv) { - - (void)stdin;(void)stdout;(void)stderr; - - /* When starting using pm_create -c ARGV="-- -root ..", argv[0] is the first - part of ARGV and not the name of the executable. So we shuffle some - pointers here to make erl_start happy. */ - if (argv[0][0] == '-') { - int i; - char **tmp_argv = malloc(sizeof(char*)*(argc+1)); - for (i = 0; i < argc; i++) - tmp_argv[i+1] = argv[i]; - tmp_argv[0] = "beam"; - erl_start(argc+1,tmp_argv); - free(tmp_argv); - } else { - erl_start(argc,argv); - } - - stop(current_process()); - - return 0; -} diff --git a/erts/emulator/sys/ose/erl_ose_sys.h b/erts/emulator/sys/ose/erl_ose_sys.h deleted file mode 100644 index d0cd3180bf..0000000000 --- a/erts/emulator/sys/ose/erl_ose_sys.h +++ /dev/null @@ -1,356 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 1997-2011. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - * - * This file handles differences between different Unix systems. - * This should be the only place with conditional compilation - * depending on the type of OS. - */ - -#ifndef _ERL_OSE_SYS_H -#define _ERL_OSE_SYS_H - -#include "ose.h" -#undef NIL -#include "ramlog.h" -#include "erts.sig" - -#include "fcntl.h" -#include "math.h" -#include "stdio.h" -#include "stdlib.h" -#include "string.h" -#include "sys/param.h" -#include "sys/time.h" -#include "time.h" -#include "dirent.h" -#include "ethread.h" - -/* FIXME: configuration options */ -#define ERTS_SCHED_MIN_SPIN 1 -#define ERTS_SCHED_ONLY_POLL_SCHED_1 1 -#define ERTS_SCHED_FAIR 1 -#define NO_SYSCONF 1 -#define OPEN_MAX FOPEN_MAX - -#define MAP_ANON MAP_ANONYMOUS - -#ifndef HAVE_MMAP -# define HAVE_MMAP 0 -#endif - -#if HAVE_MMAP -# include "sys/mman.h" -#endif - -/* - * Min number of async threads - */ -#define ERTS_MIN_NO_OF_ASYNC_THREADS 1 - -/* - * Our own type of "FD's" - */ -#define ERTS_SYS_FD_TYPE struct erts_sys_fd_type* -#define NO_FSTAT_ON_SYS_FD_TYPE 1 /* They are signals, not files */ - -#include "sys/stat.h" - -/* FIXME mremap is not defined in OSE - POSIX issue */ -extern void *mremap (void *__addr, size_t __old_len, size_t __new_len, - int __flags, ...); - -/* FIXME: mremap constants */ -#define MREMAP_MAYMOVE 1 -#define MREMAP_FIXED 2 - -typedef void *GETENV_STATE; - -/* -** For the erl_timer_sup module. -*/ -#define HAVE_GETHRTIME - -typedef long long SysHrTime; -extern SysHrTime sys_gethrtime(void); - -void sys_init_hrtime(void); - -typedef time_t erts_time_t; - -typedef struct timeval SysTimeval; - -#define sys_gettimeofday(Arg) ((void) gettimeofday((Arg), NULL)) - -typedef struct { - clock_t tms_utime; - clock_t tms_stime; - clock_t tms_cutime; - clock_t tms_cstime; -} SysTimes; - -extern int erts_ticks_per_sec; - -#define SYS_CLK_TCK (erts_ticks_per_sec) - -extern clock_t sys_times(SysTimes *buffer); - -/* No use in having other resolutions than 1 Ms. */ -#define SYS_CLOCK_RESOLUTION 1 - -#define erts_isfinite finite - -#ifdef NO_FPE_SIGNALS - -#define erts_get_current_fp_exception() NULL -#ifdef ERTS_SMP -#define erts_thread_init_fp_exception() do{}while(0) -#endif -# define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0) -# define __ERTS_FP_ERROR(fpexnp, f, Action) if (!finite(f)) { Action; } else {} -# define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) __ERTS_FP_ERROR(fpexnp, f, Action) -# define __ERTS_SAVE_FP_EXCEPTION(fpexnp) -# define __ERTS_RESTORE_FP_EXCEPTION(fpexnp) - -#define erts_sys_block_fpe() 0 -#define erts_sys_unblock_fpe(x) do{}while(0) - -#else /* !NO_FPE_SIGNALS */ - -extern volatile unsigned long *erts_get_current_fp_exception(void); -#ifdef ERTS_SMP -extern void erts_thread_init_fp_exception(void); -#endif -# if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__) -# define erts_fwait(fpexnp,f) \ - __asm__ __volatile__("fwait" : "=m"(*(fpexnp)) : "m"(f)) -# elif (defined(__powerpc__) || defined(__ppc__)) && defined(__GNUC__) -# define erts_fwait(fpexnp,f) \ - __asm__ __volatile__("" : "=m"(*(fpexnp)) : "fm"(f)) -# elif defined(__sparc__) && defined(__linux__) && defined(__GNUC__) -# define erts_fwait(fpexnp,f) \ - __asm__ __volatile__("" : "=m"(*(fpexnp)) : "em"(f)) -# else -# define erts_fwait(fpexnp,f) \ - __asm__ __volatile__("" : "=m"(*(fpexnp)) : "g"(f)) -# endif -# if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__) - extern void erts_restore_fpu(void); -# else -# define erts_restore_fpu() /*empty*/ -# endif -# if (!defined(__GNUC__) || \ - (__GNUC__ < 2) || \ - (__GNUC__ == 2 && __GNUC_MINOR < 96)) && \ - !defined(__builtin_expect) -# define __builtin_expect(x, expected_value) (x) -# endif -static __inline__ int erts_check_fpe(volatile unsigned long *fp_exception, double f) -{ - erts_fwait(fp_exception, f); - if (__builtin_expect(*fp_exception == 0, 1)) - return 0; - *fp_exception = 0; - erts_restore_fpu(); - return 1; -} -# undef erts_fwait -# undef erts_restore_fpu -extern void erts_fp_check_init_error(volatile unsigned long *fp_exception); -static __inline__ void __ERTS_FP_CHECK_INIT(volatile unsigned long *fp_exception) -{ - if (__builtin_expect(*fp_exception == 0, 1)) - return; - erts_fp_check_init_error(fp_exception); -} -# define __ERTS_FP_ERROR(fpexnp, f, Action) do { if (erts_check_fpe((fpexnp),(f))) { Action; } } while (0) -# define __ERTS_SAVE_FP_EXCEPTION(fpexnp) unsigned long old_erl_fp_exception = *(fpexnp) -# define __ERTS_RESTORE_FP_EXCEPTION(fpexnp) \ - do { *(fpexnp) = old_erl_fp_exception; } while (0) - /* This is for library calls where we don't trust the external - code to always throw floating-point exceptions on errors. */ -static __inline__ int erts_check_fpe_thorough(volatile unsigned long *fp_exception, double f) -{ - return erts_check_fpe(fp_exception, f) || !finite(f); -} -# define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) \ - do { if (erts_check_fpe_thorough((fpexnp),(f))) { Action; } } while (0) - -int erts_sys_block_fpe(void); -void erts_sys_unblock_fpe(int); - -#endif /* !NO_FPE_SIGNALS */ - -#define ERTS_FP_CHECK_INIT(p) __ERTS_FP_CHECK_INIT(&(p)->fp_exception) -#define ERTS_FP_ERROR(p, f, A) __ERTS_FP_ERROR(&(p)->fp_exception, f, A) -#define ERTS_FP_ERROR_THOROUGH(p, f, A) __ERTS_FP_ERROR_THOROUGH(&(p)->fp_exception, f, A) - -/* FIXME: force HAVE_GETPAGESIZE and stub getpagesize */ -#ifndef HAVE_GETPAGESIZE -#define HAVE_GETPAGESIZE 1 -#endif - -extern int getpagesize(void); - -#ifndef HZ -#define HZ 60 -#endif - -/* OSE5 doesn't provide limits.h so a number of macros should be - * added manually */ - -#ifndef CHAR_BIT -#define CHAR_BIT 8 -#endif - -/* Minimum and maximum values a `signed int' can hold. */ -#ifndef INT_MAX -#define INT_MAX 2147483647 -#endif - -#ifndef INT_MIN -#define INT_MIN (-INT_MAX - 1) -#endif - -#ifndef UINT_MAX -# define UINT_MAX 4294967295U -#endif - -/* -static void erts_ose_sys_send(union SIGNAL **signal,PROCESS dst, - char* file,int line) { - SIGSELECT **ziggy = (SIGSELECT**)signal; - printf("%s:%d 0x%x Send signal 0x%x(0x%x) to 0x%x\r\n", - file,line,current_process(),ziggy[0][0],*ziggy,dst); - send(signal,dst); -} -#define send(signal,dst) erts_ose_sys_send(signal,dst,__FILE__,__LINE__) - -static void erts_ose_sys_send_w_sender(union SIGNAL **signal, - PROCESS sender,PROCESS dst, - char* file,int line) { - SIGSELECT **ziggy = (SIGSELECT**)signal; - printf("%s:%d 0x%x Send signal 0x%x(0x%x) to 0x%x as 0x%x\r\n", - file,line,current_process(),ziggy[0][0],*ziggy,dst,sender); - send_w_sender(signal,sender,dst); -} -#define send_w_sender(signal,sender,dst) \ - erts_ose_sys_send_w_sender(signal,sender,dst,__FILE__,__LINE__) - - -static union SIGNAL *erts_ose_sys_receive(SIGSELECT *sigsel, - char *file, - int line) { - SIGSELECT *sig; - int i; - - printf("%s:%d 0x%x receive({%d,",file,line,current_process(),sigsel[0]); - for (i = 1; i < sigsel[0]; i++) - printf("0x%x, ",sigsel[i]); - if (sigsel[0] != 0) - printf("0x%x",sigsel[i]); - printf("})\n"); - sig = (SIGSELECT*)receive(sigsel); - printf("%s:%d 0x%x got 0x%x from 0x%x\n",file,line,current_process(), - *sig,sender((union SIGNAL**)(&sig))); - return (union SIGNAL*)sig; -} -#define receive(SIGSEL) erts_ose_sys_receive(SIGSEL,__FILE__,__LINE__) - -static union SIGNAL *erts_ose_sys_receive_w_tmo(OSTIME tmo,SIGSELECT *sigsel, - char *file,int line) { - SIGSELECT *sig; - int i; - if (tmo == 0) { - sig = (SIGSELECT*)receive_w_tmo(tmo,sigsel); - if (sig != NULL) { - printf("%s:%d 0x%x receive_w_tmo(0,{%d,",file,line,current_process(), - sigsel[0]); - for (i = 1; i < sigsel[0]; i++) - printf("0x%x, ",sigsel[i]); - if (sigsel[0] != 0) - printf("0x%x",sigsel[i]); - printf("})\n"); - printf("%s:%d 0x%x got 0x%x from 0x%x\n",file,line,current_process(), - *sig,sender((union SIGNAL**)(&sig))); - } - } else { - printf("%s:%d 0x%x receive_w_tmo(%u,{%d,",file,line,current_process(),tmo, - sigsel[0]); - for (i = 1; i < sigsel[0]; i++) - printf("0x%x, ",sigsel[i]); - if (sigsel[0] != 0) - printf("0x%x",sigsel[i]); - printf("})\n"); - sig = (SIGSELECT*)receive_w_tmo(tmo,sigsel); - printf("%s:%d 0x%x got ",file,line,current_process()); - if (sig == NULL) - printf("TIMEOUT\n"); - else - printf("0x%x from 0x%x\n",*sig,sender((union SIGNAL**)(&sig))); - } - - return (union SIGNAL*)sig; -} - -#define receive_w_tmo(tmo,sigsel) erts_ose_sys_receive_w_tmo(tmo,sigsel, \ - __FILE__,__LINE__) - -static union SIGNAL *erts_ose_sys_receive_fsem(OSTIME tmo,SIGSELECT *sigsel, - OSFSEMVAL fsem, - char *file,int line) { - SIGSELECT *sig; - int i; - if (tmo == 0) { - sig = (SIGSELECT*)receive_fsem(tmo,sigsel,fsem); - if (sig != NULL && sig != OS_RCV_FSEM) { - printf("%s:%d 0x%x receive_fsem(0,{%d,",file,line,current_process(), - sigsel[0]); - for (i = 1; i < sigsel[0]; i++) - printf("0x%x, ",sigsel[i]); - if (sigsel[0] != 0) - printf("0x%x",sigsel[i]); - printf("},%d)\n",fsem); - printf("%s:%d 0x%x got 0x%x from 0x%x\n",file,line,current_process(), - *sig,sender((union SIGNAL**)(&sig))); - } - } else { - printf("%s:%d 0x%x receive_fsem(%u,{%d,",file,line,current_process(),tmo, - sigsel[0]); - for (i = 1; i < sigsel[0]; i++) - printf("0x%x, ",sigsel[i]); - if (sigsel[0] != 0) - printf("0x%x",sigsel[i]); - printf("},%d)\n",fsem); - sig = (SIGSELECT*)receive_fsem(tmo,sigsel,fsem); - printf("%s:%d 0x%x got ",file,line,current_process()); - if (sig == NULL) - printf("TIMEOUT\n"); - else if (sig == OS_RCV_FSEM) - printf("FSEM\n"); - else - printf("0x%x from 0x%x\n",*sig,sender((union SIGNAL**)(&sig))); - } - - return (union SIGNAL*)sig; -} - -#define receive_fsem(tmo,sigsel,fsem) \ - erts_ose_sys_receive_fsem(tmo,sigsel,fsem,__FILE__,__LINE__) -*/ -#endif /* _ERL_OSE_SYS_H */ diff --git a/erts/emulator/sys/ose/erl_ose_sys_ddll.c b/erts/emulator/sys/ose/erl_ose_sys_ddll.c deleted file mode 100644 index 5051f7fcc1..0000000000 --- a/erts/emulator/sys/ose/erl_ose_sys_ddll.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -/* - * Interface functions to the dynamic linker using dl* functions. - * (No support in OSE, we use static linkage instead) - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "sys.h" -#include "erl_vm.h" -#include "global.h" - - -void erl_sys_ddll_init(void) { -} - -/* - * Open a shared object - */ -int erts_sys_ddll_open(const char *full_name, void **handle, ErtsSysDdllError* err) -{ - return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY; -} - -int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err) -{ - return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY; -} - -/* - * Find a symbol in the shared object - */ -int erts_sys_ddll_sym2(void *handle, const char *func_name, void **function, - ErtsSysDdllError* err) -{ - return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY; -} - -/* XXX:PaN These two will be changed with new driver interface! */ - -/* - * Load the driver init function, might appear under different names depending on object arch... - */ - -int erts_sys_ddll_load_driver_init(void *handle, void **function) -{ - void *fn; - int res; - if ((res = erts_sys_ddll_sym2(handle, "driver_init", &fn, NULL)) != ERL_DE_NO_ERROR) { - res = erts_sys_ddll_sym2(handle, "_driver_init", &fn, NULL); - } - if (res == ERL_DE_NO_ERROR) { - *function = fn; - } - return res; -} - -int erts_sys_ddll_load_nif_init(void *handle, void **function, ErtsSysDdllError* err) -{ - void *fn; - int res; - if ((res = erts_sys_ddll_sym2(handle, "nif_init", &fn, err)) != ERL_DE_NO_ERROR) { - res = erts_sys_ddll_sym2(handle, "_nif_init", &fn, err); - } - if (res == ERL_DE_NO_ERROR) { - *function = fn; - } - return res; -} - -/* - * Call the driver_init function, whatever it's really called, simple on unix... -*/ -void *erts_sys_ddll_call_init(void *function) { - void *(*initfn)(void) = function; - return (*initfn)(); -} -void *erts_sys_ddll_call_nif_init(void *function) { - return erts_sys_ddll_call_init(function); -} - - - -/* - * Close a chared object - */ -int erts_sys_ddll_close2(void *handle, ErtsSysDdllError* err) -{ - return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY; -} - - -/* - * Return string that describes the (current) error - */ -char *erts_sys_ddll_error(int code) -{ - return "Unspecified error"; -} - -void erts_sys_ddll_free_error(ErtsSysDdllError* err) -{ - if (err->str != NULL) { - erts_free(ERTS_ALC_T_DDLL_TMP_BUF, err->str); - } -} diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c deleted file mode 100644 index 5cee582a00..0000000000 --- a/erts/emulator/sys/ose/erl_poll.c +++ /dev/null @@ -1,818 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2006-2012. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -/* - * Description: Poll interface suitable for ERTS on OSE with or without - * SMP support. - * - * The interface is currently implemented using: - * - receive + receive_fsem - * - * Author: Lukas Larsson - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "erl_thr_progress.h" -#include "erl_driver.h" -#include "erl_alloc.h" -#include "erl_poll.h" - -#define NOFILE 4096 - -/* - * Some debug macros - */ - -/* #define HARDDEBUG -#define HARDTRACE*/ -#ifdef HARDDEBUG -#ifdef HARDTRACE -#define HARDTRACEF(X, ...) { fprintf(stderr, X, __VA_ARGS__); fprintf(stderr,"\r\n"); } -#else -#define HARDTRACEF(...) -#endif - -#else -#define HARDTRACEF(X,...) -#define HARDDEBUGF(...) -#endif - -#if 0 -#define ERTS_POLL_DEBUG_PRINT -#endif - -#if defined(DEBUG) && 0 -#define HARD_DEBUG -#endif - -# define SEL_ALLOC erts_alloc -# define SEL_REALLOC realloc_wrap -# define SEL_FREE erts_free - -#ifdef ERTS_SMP - -#define ERTS_POLLSET_LOCK(PS) \ - erts_smp_mtx_lock(&(PS)->mtx) -#define ERTS_POLLSET_UNLOCK(PS) \ - erts_smp_mtx_unlock(&(PS)->mtx) - -#else - -#define ERTS_POLLSET_LOCK(PS) -#define ERTS_POLLSET_UNLOCK(PS) - -#endif - -/* - * --- Data types ------------------------------------------------------------ - */ - -union SIGNAL { - SIGSELECT sig_no; -}; - -typedef struct erts_sigsel_item_ ErtsSigSelItem; - -struct erts_sigsel_item_ { - ErtsSigSelItem *next; - ErtsSysFdType fd; - ErtsPollEvents events; -}; - -typedef struct erts_sigsel_info_ ErtsSigSelInfo; - -struct erts_sigsel_info_ { - ErtsSigSelInfo *next; - SIGSELECT signo; - ErlDrvOseEventId (*decode)(union SIGNAL* sig); - ErtsSigSelItem *fds; -}; - -struct ErtsPollSet_ { - SIGSELECT *sigs; - ErtsSigSelInfo *info; - Uint sig_count; - Uint item_count; - PROCESS interrupt; - erts_atomic32_t wakeup_state; - erts_atomic64_t timeout_time; -#ifdef ERTS_SMP - erts_smp_mtx_t mtx; -#endif -}; - -static int max_fds = -1; - -static ERTS_INLINE void -init_timeout_time(ErtsPollSet ps) -{ - erts_atomic64_init_nob(&ps->timeout_time, - (erts_aint64_t) ERTS_MONOTONIC_TIME_MAX); -} - -static ERTS_INLINE void -set_timeout_time(ErtsPollSet ps, ErtsMonotonicTime time) -{ - erts_atomic64_set_relb(&ps->timeout_time, - (erts_aint64_t) time); -} - -static ERTS_INLINE ErtsMonotonicTime -get_timeout_time(ErtsPollSet ps) -{ - return (ErtsMonotonicTime) erts_atomic64_read_acqb(&ps->timeout_time); -} - -#define ERTS_POLL_NOT_WOKEN ((erts_aint32_t) (1 << 0)) -#define ERTS_POLL_WOKEN_INTR ((erts_aint32_t) (1 << 1)) -#define ERTS_POLL_WOKEN_TIMEDOUT ((erts_aint32_t) (1 << 2)) -#define ERTS_POLL_WOKEN_IO_READY ((erts_aint32_t) (1 << 3)) -#define ERTS_POLL_SLEEPING ((erts_aint32_t) (1 << 4)) - -/* signal list prototypes */ -static ErtsSigSelInfo *get_sigsel_info(ErtsPollSet ps, SIGSELECT signo); -static ErtsSigSelItem *get_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd); -static ErtsSigSelInfo *add_sigsel_info(ErtsPollSet ps, ErtsSysFdType fd, - ErlDrvOseEventId (*decode)(union SIGNAL* sig)); -static ErtsSigSelItem *add_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd, - ErlDrvOseEventId (*decode)(union SIGNAL* sig)); -static int del_sigsel_info(ErtsPollSet ps, ErtsSigSelInfo *info); -static int del_sigsel_item(ErtsPollSet ps, ErtsSigSelItem *item); -static int update_sigsel(ErtsPollSet ps); - -static ErtsSigSelInfo * -get_sigsel_info(ErtsPollSet ps, SIGSELECT signo) { - ErtsSigSelInfo *curr = ps->info; - while (curr != NULL) { - if (curr->signo == signo) - return curr; - curr = curr->next; - } - return NULL; -} - -static ErtsSigSelItem * -get_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd) { - ErtsSigSelInfo *info = get_sigsel_info(ps,fd->signo); - ErtsSigSelItem *curr; - - if (info == NULL) - return NULL; - - curr = info->fds; - - while (curr != NULL) { - if (curr->fd->id == fd->id) { - ASSERT(curr->fd->signo == fd->signo); - return curr; - } - curr = curr->next; - } - return NULL; -} - -static ErtsSigSelInfo * -add_sigsel_info(ErtsPollSet ps, ErtsSysFdType fd, - ErlDrvOseEventId (*decode)(union SIGNAL* sig)) { - ErtsSigSelInfo *info = SEL_ALLOC(ERTS_ALC_T_POLLSET, - sizeof(ErtsSigSelInfo)); - info->next = ps->info; - info->fds = NULL; - info->signo = fd->signo; - info->decode = decode; - ps->info = info; - ps->sig_count++; - return info; -} - -static ErtsSigSelItem * -add_sigsel_item(ErtsPollSet ps, ErtsSysFdType fd, - ErlDrvOseEventId (*decode)(union SIGNAL* sig)) { - ErtsSigSelInfo *info = get_sigsel_info(ps,fd->signo); - ErtsSigSelItem *item = SEL_ALLOC(ERTS_ALC_T_POLLSET, - sizeof(ErtsSigSelItem)); - if (info == NULL) - info = add_sigsel_info(ps, fd, decode); - if (info->decode != decode) { - erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - erts_dsprintf(dsbufp, "erts_poll_control() inconsistency: multiple resolve_signal functions for same signal (%d)\n", - fd->signo); - erts_send_error_to_logger_nogl(dsbufp); - } - ASSERT(info->decode == decode); - item->next = info->fds; - item->fd = fd; - item->events = 0; - info->fds = item; - ps->item_count++; - return item; -} - -static int del_sigsel_info(ErtsPollSet ps, ErtsSigSelInfo *info) { - ErtsSigSelInfo *curr, *prev; - - if (ps->info == info) { - ps->info = ps->info->next; - } else { - curr = ps->info->next; - prev = ps->info; - - while (curr != info) { - if (curr == NULL) - return 1; - prev = curr; - curr = curr->next; - } - prev->next = curr->next; - } - - ps->sig_count--; - SEL_FREE(ERTS_ALC_T_POLLSET, info); - return 0; -} - -static int del_sigsel_item(ErtsPollSet ps, ErtsSigSelItem *item) { - ErtsSigSelInfo *info = get_sigsel_info(ps,item->fd->signo); - ErtsSigSelItem *curr, *prev; - - ps->item_count--; - ASSERT(ps->item_count >= 0); - - if (info->fds == item) { - info->fds = info->fds->next; - SEL_FREE(ERTS_ALC_T_POLLSET,item); - if (info->fds == NULL) - return del_sigsel_info(ps,info); - return 0; - } - - curr = info->fds->next; - prev = info->fds; - - while (curr != item) { - if (curr == NULL) { - /* We did not find an item to delete so we have to - * increment item count again. - */ - ps->item_count++; - return 1; - } - prev = curr; - curr = curr->next; - } - prev->next = curr->next; - SEL_FREE(ERTS_ALC_T_POLLSET,item); - return 0; -} - -#ifdef ERTS_SMP - -static void update_redir_tables(ErtsPollSet ps) { - struct OS_redir_entry *redir_table; - PROCESS sched_1 = ERTS_SCHEDULER_IX(0)->tid.id; - int i; - redir_table = SEL_ALLOC(ERTS_ALC_T_POLLSET, - sizeof(struct OS_redir_entry)*(ps->sig_count+1)); - - redir_table[0].sig = ps->sig_count+1; - redir_table[0].pid = 0; - - for (i = 1; i < ps->sig_count+1; i++) { - redir_table[i].sig = ps->sigs[i]; - redir_table[i].pid = sched_1; - } - - for (i = 1; i < erts_no_schedulers; i++) { - ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(i); - set_redirection(esdp->tid.id,redir_table); - } - - SEL_FREE(ERTS_ALC_T_POLLSET,redir_table); -} - -#endif - -static int update_sigsel(ErtsPollSet ps) { - ErtsSigSelInfo *info = ps->info; - - int i; - - if (ps->sigs != NULL) - SEL_FREE(ERTS_ALC_T_POLLSET,ps->sigs); - - if (ps->sig_count == 0) { - /* If there are no signals we place a non-valid signal to make sure that - * we do not trigger on a any unrelated signals which are sent to the - * process. - */ - ps->sigs = SEL_ALLOC(ERTS_ALC_T_POLLSET,sizeof(SIGSELECT)*(2)); - ps->sigs[0] = 1; - ps->sigs[1] = ERTS_SIGNAL_INVALID; - return 0; - } - - ps->sigs = SEL_ALLOC(ERTS_ALC_T_POLLSET,sizeof(SIGSELECT)*(ps->sig_count+1)); - ps->sigs[0] = ps->sig_count; - - for (i = 1; info != NULL; i++, info = info->next) - ps->sigs[i] = info->signo; - -#ifdef ERTS_SMP - update_redir_tables(ps); -#endif - - return 0; -} - -static ERTS_INLINE void -wake_poller(ErtsPollSet ps) -{ - erts_aint32_t wakeup_state; - - ERTS_THR_MEMORY_BARRIER; - wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state); - while (wakeup_state != ERTS_POLL_WOKEN_IO_READY - && wakeup_state != ERTS_POLL_WOKEN_INTR) { - erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, - ERTS_POLL_WOKEN_INTR, - wakeup_state); - if (act == wakeup_state) { - wakeup_state = act; - break; - } - wakeup_state = act; - } - if (wakeup_state == ERTS_POLL_SLEEPING) { - /* - * Since we don't know the internals of signal_fsem() we issue - * a memory barrier as a safety precaution ensuring that - * the store we just made to wakeup_state wont be reordered - * with loads in signal_fsem(). - */ - ERTS_THR_MEMORY_BARRIER; - signal_fsem(ps->interrupt); - } -} - -static ERTS_INLINE void -reset_interrupt(ErtsPollSet ps) -{ - /* We need to keep io-ready if set */ - erts_aint32_t wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state); - while (wakeup_state != ERTS_POLL_NOT_WOKEN && - wakeup_state != ERTS_POLL_SLEEPING) { - erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, - ERTS_POLL_NOT_WOKEN, - wakeup_state); - if (wakeup_state == act) - break; - wakeup_state = act; - } - ERTS_THR_MEMORY_BARRIER; -} - -static ERTS_INLINE void -set_interrupt(ErtsPollSet ps) -{ - wake_poller(ps); -} - -void erts_poll_interrupt(ErtsPollSet ps,int set) { - HARDTRACEF("erts_poll_interrupt called!\n"); - - if (!set) - reset_interrupt(ps); - else - set_interrupt(ps); - -} - -void erts_poll_interrupt_timed(ErtsPollSet ps, - int set, - ErtsTimeoutTime timeout_time) { - HARDTRACEF("erts_poll_interrupt_timed called!\n"); - - if (!set) - reset_interrupt(ps); - else if (get_timeout_time(ps) > timeout_time) - set_interrupt(ps); -} - -ErtsPollEvents erts_poll_control(ErtsPollSet ps, ErtsSysFdType fd, - ErtsPollEvents pe, int on, int* do_wake) { - ErtsSigSelItem *curr; - ErtsPollEvents new_events; - int old_sig_count; - - HARDTRACEF( - "%ux: In erts_poll_control, fd = %d, pe = %d, on = %d, *do_wake = %d, curr = 0x%xu", - ps, fd, pe, on, do_wake, curr); - - ERTS_POLLSET_LOCK(ps); - - if (on && (pe & ERTS_POLL_EV_IN) && (pe & ERTS_POLL_EV_OUT)) { - /* Check to make sure both in and out are not used at the same time */ - new_events = ERTS_POLL_EV_NVAL; - goto done; - } - - curr = get_sigsel_item(ps, fd); - old_sig_count = ps->sig_count; - - if (curr == NULL && on) { - curr = add_sigsel_item(ps, fd, fd->resolve_signal); - } else if (curr == NULL && !on) { - new_events = ERTS_POLL_EV_NVAL; - goto done; - } - - new_events = curr->events; - - if (pe == 0) { - *do_wake = 0; - goto done; - } - - if (on) { - new_events |= pe; - curr->events = new_events; - } else { - new_events &= ~pe; - curr->events = new_events; - if (new_events == 0 && del_sigsel_item(ps, curr)) { - new_events = ERTS_POLL_EV_NVAL; - goto done; - } - } - - if (ps->sig_count != old_sig_count) { - if (update_sigsel(ps)) - new_events = ERTS_POLL_EV_NVAL; - } -done: - ERTS_POLLSET_UNLOCK(ps); - HARDTRACEF("%ux: Out erts_poll_control", ps); - return new_events; -} - -int erts_poll_wait(ErtsPollSet ps, - ErtsPollResFd pr[], - int *len, - ErtsMonotonicTime timeout_time) -{ - int res = ETIMEDOUT, no_fds, currid = 0; - OSTIME timeout; - union SIGNAL *sig; - ErtsMonotonicTime current_time, diff_time, timeout; - // HARDTRACEF("%ux: In erts_poll_wait",ps); - if (ps->interrupt == (PROCESS)0) - ps->interrupt = current_process(); - - ASSERT(current_process() == ps->interrupt); - ASSERT(get_fsem(current_process()) == 0); - ASSERT(erts_atomic32_read_nob(&ps->wakeup_state) & - (ERTS_POLL_NOT_WOKEN | ERTS_POLL_WOKEN_INTR)); - /* Max no of spots avable in pr */ - no_fds = *len; - - *len = 0; - - /* erts_printf("Entering erts_poll_wait(), timeout_time=%bps\n", - timeout_time); */ - - if (timeout_time == ERTS_POLL_NO_TIMEOUT) { - no_timeout: - timeout = (OSTIME) 0; - save_timeout_time = ERTS_MONOTONIC_TIME_MIN; - } - else { - ErtsMonotonicTime current_time, diff_time; - current_time = erts_get_monotonic_time(NULL); - diff_time = timeout_time - current_time; - if (diff_time <= 0) - goto no_timeout; - diff_time = (ERTS_MONOTONIC_TO_MSEC(diff_time - 1) + 1); - if (diff_time > INT_MAX) - diff_time = INT_MAX; - timeout = (OSTIME) diff_time; - save_timeout_time = current_time; - save_timeout_time += ERTS_MSEC_TO_MONOTONIC(diff_time); - } - - set_timeout_time(ps, save_timeout_time); - - while (currid < no_fds) { - if (timeout > 0) { - erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, - ERTS_POLL_SLEEPING, - ERTS_POLL_NOT_WOKEN); - if (act == ERTS_POLL_NOT_WOKEN) { -#ifdef ERTS_SMP - erts_thr_progress_prepare_wait(NULL); -#endif - sig = receive_fsem(timeout, ps->sigs, 1); -#ifdef ERTS_SMP - erts_thr_progress_finalize_wait(NULL); -#endif - } else { - ASSERT(act == ERTS_POLL_WOKEN_INTR); - sig = OS_RCV_FSEM; - } - } else - sig = receive_w_tmo(0, ps->sigs); - - if (sig == NULL) { - if (timeout > 0) { - erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, - ERTS_POLL_WOKEN_TIMEDOUT, - ERTS_POLL_SLEEPING); - if (act == ERTS_POLL_WOKEN_INTR) - /* Restore fsem as it was signaled but we got a timeout */ - wait_fsem(1); - } else - erts_atomic32_cmpxchg_nob(&ps->wakeup_state, - ERTS_POLL_WOKEN_TIMEDOUT, - ERTS_POLL_NOT_WOKEN); - break; - } else if (sig == OS_RCV_FSEM) { - ASSERT(erts_atomic32_read_nob(&ps->wakeup_state) == ERTS_POLL_WOKEN_INTR); - break; - } - { - ErtsSigSelInfo *info = get_sigsel_info(ps, sig->sig_no); - struct erts_sys_fd_type fd = { sig->sig_no, info->decode(sig) }; - ErtsSigSelItem *item = get_sigsel_item(ps, &fd); - - ASSERT(sig); - if (currid == 0 && timeout > 0) { - erts_aint32_t act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, - ERTS_POLL_WOKEN_IO_READY, - ERTS_POLL_SLEEPING); - if (act == ERTS_POLL_WOKEN_INTR) { - /* Restore fsem as it was signaled but we got a msg */ - wait_fsem(1); - act = erts_atomic32_cmpxchg_nob(&ps->wakeup_state, - ERTS_POLL_WOKEN_IO_READY, - ERTS_POLL_WOKEN_INTR); - } - } else if (currid == 0) { - erts_atomic32_set_nob(&ps->wakeup_state, - ERTS_POLL_WOKEN_IO_READY); - } - - if (item == NULL) { - erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - erts_dsprintf( - dsbufp, - "erts_poll_wait() failed: found unkown signal id %d (signo %u) " - "(curr_proc 0x%x)\n", - fd.id, fd.signo, current_process()); - erts_send_error_to_logger_nogl(dsbufp); - timeout = 0; - /* Under normal circumstances the signal is deallocated by the - * driver that issued the select operation. But in this case - * there's no driver waiting for such signal so we have to - * deallocate it here */ - if (sig) - free_buf(&sig); - } else { - int i; - struct erts_sys_fd_type *fd = NULL; - ErtsPollOseMsgList *tl,*new; - - /* Check if this fd has already been triggered by a previous signal */ - for (i = 0; i < currid;i++) { - if (pr[i].fd == item->fd) { - fd = pr[i].fd; - pr[i].events |= item->events; - break; - } - } - - /* First time this fd is triggered */ - if (fd == NULL) { - pr[currid].fd = item->fd; - pr[currid].events = item->events; - fd = item->fd; - timeout = 0; - currid++; - } - - /* Insert new signal in approriate list */ - new = erts_alloc(ERTS_ALC_T_FD_SIG_LIST,sizeof(ErtsPollOseMsgList)); - new->next = NULL; - new->data = sig; - - ethr_mutex_lock(&fd->mtx); - tl = fd->msgs; - - if (tl == NULL) { - fd->msgs = new; - } else { - while (tl->next != NULL) - tl = tl->next; - tl->next = new; - } - ethr_mutex_unlock(&fd->mtx); - } - - } - } - - { - erts_aint32_t wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state); - - switch (wakeup_state) { - case ERTS_POLL_WOKEN_IO_READY: - res = 0; - break; - case ERTS_POLL_WOKEN_INTR: - res = EINTR; - break; - case ERTS_POLL_WOKEN_TIMEDOUT: - res = ETIMEDOUT; - break; - case ERTS_POLL_NOT_WOKEN: - /* This happens when we get an invalid signal only */ - res = EINVAL; - break; - default: - res = 0; - erl_exit(ERTS_ABORT_EXIT, - "%s:%d: Internal error: Invalid wakeup_state=%d\n", - __FILE__, __LINE__, (int) wakeup_state); - } - } - - erts_atomic32_set_nob(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN); - set_timeout_time(ps, ERTS_MONOTONIC_TIME_MAX); - - *len = currid; - - // HARDTRACEF("%ux: Out erts_poll_wait",ps); - return res; -} - -int erts_poll_max_fds(void) -{ - - HARDTRACEF("In/Out erts_poll_max_fds -> %d",max_fds); - return max_fds; -} - -void erts_poll_info(ErtsPollSet ps, - ErtsPollInfo *pip) -{ - Uint size = 0; - Uint num_events = 0; - - size += sizeof(struct ErtsPollSet_); - size += sizeof(ErtsSigSelInfo)*ps->sig_count; - size += sizeof(ErtsSigSelItem)*ps->item_count; - size += sizeof(SIGSELECT)*(ps->sig_count+1); - - pip->primary = "receive_fsem"; - - pip->fallback = NULL; - - pip->kernel_poll = NULL; - - pip->memory_size = size; - - pip->poll_set_size = num_events; - - pip->fallback_poll_set_size = 0; - - pip->lazy_updates = 0; - - pip->pending_updates = 0; - - pip->batch_updates = 0; - - pip->concurrent_updates = 0; - - - pip->max_fds = erts_poll_max_fds(); - HARDTRACEF("%ux: Out erts_poll_info",ps); - -} - -ErtsPollSet erts_poll_create_pollset(void) -{ - ErtsPollSet ps = SEL_ALLOC(ERTS_ALC_T_POLLSET, - sizeof(struct ErtsPollSet_)); - - ps->sigs = NULL; - ps->sig_count = 0; - ps->item_count = 0; - ps->info = NULL; - ps->interrupt = (PROCESS)0; - erts_atomic32_init_nob(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN); - init_timeout_time(ps); -#ifdef ERTS_SMP - erts_smp_mtx_init(&ps->mtx, "pollset"); -#endif - update_sigsel(ps); - HARDTRACEF("%ux: Out erts_poll_create_pollset",ps); - return ps; -} - -void erts_poll_destroy_pollset(ErtsPollSet ps) -{ - ErtsSigSelInfo *info; - for (info = ps->info; ps->info != NULL; info = ps->info, ps->info = ps->info->next) { - ErtsSigSelItem *item; - for (item = info->fds; info->fds != NULL; item = info->fds, info->fds = info->fds->next) - SEL_FREE(ERTS_ALC_T_POLLSET, item); - SEL_FREE(ERTS_ALC_T_POLLSET, info); - } - - SEL_FREE(ERTS_ALC_T_POLLSET,ps->sigs); - -#ifdef ERTS_SMP - erts_smp_mtx_destroy(&ps->mtx); -#endif - - SEL_FREE(ERTS_ALC_T_POLLSET,ps); -} - -void erts_poll_init(void) -{ - HARDTRACEF("In %s", __FUNCTION__); - max_fds = 256; - - HARDTRACEF("Out %s", __FUNCTION__); -} - - -/* OSE driver functions */ - -union SIGNAL *erl_drv_ose_get_signal(ErlDrvEvent drv_ev) { - struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; - ethr_mutex_lock(&ev->mtx); - if (ev->msgs == NULL) { - ethr_mutex_unlock(&ev->mtx); - return NULL; - } else { - ErtsPollOseMsgList *msg = ev->msgs; - union SIGNAL *sig = (union SIGNAL*)msg->data; - ASSERT(msg->data); - ev->msgs = msg->next; - ethr_mutex_unlock(&ev->mtx); - erts_free(ERTS_ALC_T_FD_SIG_LIST,msg); - restore(sig); - return sig; - } -} - -ErlDrvEvent -erl_drv_ose_event_alloc(SIGSELECT signo, ErlDrvOseEventId id, - ErlDrvOseEventId (*resolve_signal)(union SIGNAL *sig), void *extra) { - struct erts_sys_fd_type *ev = erts_alloc(ERTS_ALC_T_DRV_EV, - sizeof(struct erts_sys_fd_type)); - ev->signo = signo; - ev->extra = extra; - ev->id = id; - ev->msgs = NULL; - ev->resolve_signal = resolve_signal; - ethr_mutex_init(&ev->mtx); - return (ErlDrvEvent)ev; -} - -void erl_drv_ose_event_free(ErlDrvEvent drv_ev) { - struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; - ASSERT(ev->msgs == NULL); - ethr_mutex_destroy(&ev->mtx); - erts_free(ERTS_ALC_T_DRV_EV,ev); -} - -void erl_drv_ose_event_fetch(ErlDrvEvent drv_ev, SIGSELECT *signo, - ErlDrvOseEventId *id, void **extra) { - struct erts_sys_fd_type *ev = (struct erts_sys_fd_type *)drv_ev; - if (signo) - *signo = ev->signo; - if (extra) - *extra = ev->extra; - if (id) - *id = ev->id; -} diff --git a/erts/emulator/sys/ose/erts.sig b/erts/emulator/sys/ose/erts.sig deleted file mode 100644 index 78b883ee6c..0000000000 --- a/erts/emulator/sys/ose/erts.sig +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef ERTS_OSE_SIGNALS -#define ERTS_OSE_SIGNALS - -#ifndef ERTS_OSE_SIGNAL_BASE -#define ERTS_OSE_SIGNAL_BASE 0x01900280 -#endif - -#define ERTS_SIGNAL_INVALID ERTS_OSE_SIGNAL_BASE -#define ERTS_SIGNAL_FD_DRV_CONFIG ERTS_OSE_SIGNAL_BASE+1 -#define ERTS_SIGNAL_FD_DRV_ASYNC ERTS_OSE_SIGNAL_BASE+2 -#define ERTS_SIGNAL_OSE_DRV_ATTACH ERTS_OSE_SIGNAL_BASE+3 -#define ERTS_SIGNAL_OSE_DRV_HUNT ERTS_OSE_SIGNAL_BASE+4 - -#define ERTS_SIGNAL_RUN_ERL_SETUP ERTS_OSE_SIGNAL_BASE+100 -#define ERTS_SIGNAL_RUN_ERL_DAEMON ERTS_OSE_SIGNAL_BASE+101 - -#endif diff --git a/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf b/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf deleted file mode 100644 index a19d23facf..0000000000 --- a/erts/emulator/sys/ose/gcc_4.4.3_lm_ppc.lcf +++ /dev/null @@ -1,182 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2013-2014 by Enea Software AB, - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc") -OUTPUT_ARCH("powerpc") -ENTRY("crt0_lm") -MEMORY -{ - rom : ORIGIN = 0x01000000, LENGTH = 0x01000000 - ram : ORIGIN = 0x02000000, LENGTH = 0x01000000 -} -PHDRS -{ - ph_conf PT_LOAD ; - ph_rom PT_LOAD ; - ph_ram PT_LOAD ; -} -SECTIONS -{ - .text : - { - *(.text_first) - *(.text) - *(.text.*) - *(.stub) - *(oscode) - *(.init*) - *(.fini*) - *(.gnu.warning) - *(.gnu.linkonce.t.*) - *(.glue_7t) - *(.glue_7) - } > rom :ph_rom = 0 - .ose_sfk_biosentry : - { - *(.ose_sfk_biosentry) - } > rom :ph_rom - .ctors : - { - __CTOR_LIST__ = .; - *(.ctors) - *(SORT(.ctors.*)) - __CTOR_END__ = .; - } > rom :ph_rom - .dtors : - { - __DTOR_LIST__ = .; - *(.dtors) - *(SORT(.dtors.*)) - __DTOR_END__ = .; - } > rom :ph_rom - OSESYMS : - { - *(.osesyms) - } > rom :ph_rom - .rodata : - { - *(.rodata) - *(.rodata.*) - *(.gnu.linkonce.r.*) - } > rom :ph_rom - .eh_frame_hdr : - { - *(.eh_frame_hdr) - } > rom :ph_rom - .eh_frame : - { - __EH_FRAME_BEGIN__ = .; - *(.eh_frame) - LONG(0) - __EH_FRAME_END__ = .; - } > rom :ph_rom - .gcc_except_table : - { - *(.gcc_except_table .gcc_except_table.*) - } > rom :ph_rom - .sdata2 : - { - PROVIDE (_SDA2_BASE_ = .); - *(.sdata2) - *(.sdata2.*) - *(.gnu.linkonce.s2.*) - } > rom :ph_rom - .sbss2 : - { - *(.sbss2) - *(.sbss2.*) - *(.gnu.linkonce.sb2.*) - } > rom :ph_rom - LMCONF : - { - obj/?*?/ose_confd.o(.rodata) - *(LMCONF) - } > rom :ph_conf - .data : - { - LONG(0xDEADBABE) - *(.data) - *(.data.*) - *(.gnu.linkonce.d.*) - SORT(CONSTRUCTORS) - . = ALIGN(0x10); - } > ram :ph_ram = 0 - .sdata2 : - { - _SDA2_BASE_ = .; - *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) - }> ram :ph_ram - .sdata : - { - PROVIDE (_SDA_BASE_ = .); - *(.sdata) - *(.sdata.*) - *(.gnu.linkonce.s.*) - } > ram :ph_ram - .sbss : - { - *(.sbss) - *(.sbss.*) - *(.scommon) - *(.gnu.linkonce.sb.*) - } > ram :ph_ram - .bss (NOLOAD) : - { - *(.bss) - *(.bss.*) - *(COMMON) - *(.gnu.linkonce.b.*) - *(.osvars) - } > ram :ph_ram - .ignore (NOLOAD) : - { - *(.rel.dyn) - } > ram :ph_ram - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } -} -__OSESYMS_START = ADDR(OSESYMS); -__OSESYMS_END = ADDR(OSESYMS) + SIZEOF(OSESYMS); diff --git a/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf b/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf deleted file mode 100644 index 3440c2961b..0000000000 --- a/erts/emulator/sys/ose/gcc_4.6.3_lm_ppc.lcf +++ /dev/null @@ -1,242 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2013-2014 by Enea Software AB, - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc") -OUTPUT_ARCH("powerpc") - -ENTRY("crt0_lm") - -/* Note: - * You may have to increase the length of the "rom" memory region and the - * origin and length of the "ram" memory region below depending on the size - * of the code and data in your load module. - */ - -MEMORY -{ - conf : ORIGIN = 0x00100000, LENGTH = 0x00030000 - rom : ORIGIN = 0x01000000, LENGTH = 0x01000000 - ram : ORIGIN = 0x03000000, LENGTH = 0x01000000 -} - -PHDRS -{ - ph_conf PT_LOAD ; - ph_rom PT_LOAD ; - ph_ram PT_LOAD ; -} - -SECTIONS -{ -/*--------------------------------------------------------------------------- - * Load module configuration area - *-------------------------------------------------------------------------*/ - - /* Load module configuration section. */ - LMCONF : - { - obj/?*?/ose_confd.o(.rodata) - *(LMCONF) - } > conf :ph_conf - -/*--------------------------------------------------------------------------- - * Read-only area - *-------------------------------------------------------------------------*/ - - /* Code section. */ - .text : - { - *(.text) - *(.text.*) - *(.stub) - *(oscode) - *(.init*) - *(.fini*) - *(.gnu.warning) - *(.gnu.linkonce.t.*) - } > rom :ph_rom = 0 - - /* OSE symbols section. */ - OSESYMS : - { - *(.osesyms) - } > rom :ph_rom - - /* Read-only data section. */ - .rodata : - { - *(.rodata) - *(.rodata.*) - *(.gnu.linkonce.r.*) - } > rom :ph_rom - - /* C++ exception handling section. */ - .eh_frame : - { - __EH_FRAME_BEGIN__ = .; - *(.eh_frame) - LONG(0) - __EH_FRAME_END__ = .; - } > rom :ph_rom - - /* C++ exception handling section. */ - .gcc_except_table : - { - *(.gcc_except_table .gcc_except_table.*) - } > rom :ph_rom - - /* PowerPC EABI initialized read-only data section. */ - .sdata2 : - { - PROVIDE (_SDA2_BASE_ = .); - *(.sdata2) - *(.sdata2.*) - *(.gnu.linkonce.s2.*) - } > rom :ph_rom - - /* PowerPC EABI uninitialized read-only data section. */ - .sbss2 : - { - *(.sbss2) - *(.sbss2.*) - *(.gnu.linkonce.sb2.*) - } > rom :ph_rom - -/*--------------------------------------------------------------------------- - * Read-write area - *-------------------------------------------------------------------------*/ - - /*------------------------------------------------------------------- - * Initialized data (copied by PM) - *-----------------------------------------------------------------*/ - - /* Data section. */ - .data : - { - *(.data) - *(.data.*) - *(.gnu.linkonce.d.*) - SORT(CONSTRUCTORS) - } > ram :ph_ram - - /* C++ constructor section. */ - .ctors : - { - __CTOR_LIST__ = .; - *(.ctors) - *(SORT(.ctors.*)) - __CTOR_END__ = .; - } > ram :ph_ram - - /* C++ destructor section. */ - .dtors : - { - __DTOR_LIST__ = .; - *(.dtors) - *(SORT(.dtors.*)) - __DTOR_END__ = .; - } > ram :ph_ram - - - /* Small data section. */ - .sdata ALIGN(0x10) : - { - PROVIDE (_SDA_BASE_ = .); - *(.sdata) - *(.sdata.*) - *(.gnu.linkonce.s.*) - } > ram :ph_ram - - /*------------------------------------------------------------------- - * Uninitialized data (cleared by PM) - *-----------------------------------------------------------------*/ - - /* Small bss section. */ - .sbss : - { - *(.sbss) - *(.sbss.*) - *(.scommon) - *(.gnu.linkonce.sb.*) - } > ram :ph_ram - - /* Bss section. */ - .bss : - { - *(.bss) - *(.bss.*) - *(COMMON) - *(.gnu.linkonce.b.*) - } > ram :ph_ram - -/*--------------------------------------------------------------------------- - * Debug information - *-------------------------------------------------------------------------*/ - - /* - * Stabs debug sections. - */ - - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } - - /* - * DWARF debug sections. - */ - - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } -} diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c deleted file mode 100644 index bcd0ffa0b6..0000000000 --- a/erts/emulator/sys/ose/sys.c +++ /dev/null @@ -1,1847 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include "sys/time.h" -#include "time.h" -#include "sys/uio.h" -#include "termios.h" -#include "ctype.h" -#include "termios.h" - -#ifdef HAVE_FCNTL_H -#include "fcntl.h" -#endif - -#ifdef HAVE_SYS_IOCTL_H -#include "sys/ioctl.h" -#endif - -#define ERTS_WANT_BREAK_HANDLING -#define WANT_NONBLOCKING -#include "sys.h" -#include "erl_thr_progress.h" - -#ifdef USE_THREADS -#include "erl_threads.h" -#endif - -#include "erl_mseg.h" - -#include "unistd.h" -#include "efs.h" -#include "erl_printf.h" -#include "aio.h" -#include "pm.h" -#include "fcntl.h" - -/* Set the define to 1 to get some logging */ -#if 0 -#include "ramlog.h" -#define LOG(output) ramlog_printf output -#else -#define LOG(output) -#endif - -extern char **environ; -static erts_smp_rwmtx_t environ_rwmtx; -static PROCESS sig_proxy_pid = 0; - -#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O - * vector sock_sendv(). - */ -/* - * Don't need global.h, but bif_table.h (included by bif.h), - * won't compile otherwise - */ -#include "global.h" -#include "bif.h" - -#include "erl_sys_driver.h" -#include "erl_check_io.h" -#include "erl_cpu_topology.h" - -/* The priority for reader/writer processes */ -#define FD_PROC_PRI get_pri(current_process()) - -typedef struct ErtsSysReportExit_ ErtsSysReportExit; -struct ErtsSysReportExit_ { - ErtsSysReportExit *next; - Eterm port; - int pid; - int ifd; - int ofd; - ErlDrvEvent attach_event; - ErlDrvEvent input_event; - ErlDrvEvent output_event; -}; - -/* This data is shared by these drivers - initialized by spawn_init() */ -static struct driver_data { - ErlDrvPort port_num; - int ofd; - int ifd; - int packet_bytes; - ErtsSysReportExit *report_exit; - int pid; - int alive; - int status; - ErlDrvEvent input_event; - ErlDrvEvent output_event; - struct aiocb aiocb; - FmHandle handle; - char *install_handle; -} *driver_data; /* indexed by fd */ - -struct async { - SIGSELECT signo; - ErlDrvTermData port; - ErlDrvTermData proc; - PROCESS spid; - PROCESS target; - Uint32 ref; -}; - -static ErtsSysReportExit *report_exit_list; -static ERTS_INLINE void report_exit_status(ErtsSysReportExit *rep, int status); - -extern int driver_interrupt(int, int); -extern void do_break(void); - -extern void erl_sys_args(int*, char**); - -/* The following two defs should probably be moved somewhere else */ - -extern void erts_sys_init_float(void); - -extern void erl_crash_dump(char* file, int line, char* fmt, ...); - -#define DIR_SEPARATOR_CHAR '/' - -#if defined(DEBUG) -#define ERL_BUILD_TYPE_MARKER ".debug" -#else /* opt */ -#define ERL_BUILD_TYPE_MARKER -#endif - -#define CHILD_SETUP_PROG_NAME "child_setup" ERL_BUILD_TYPE_MARKER - -#ifdef DEBUG -static int debug_log = 0; -#endif - -#ifdef ERTS_SMP -static erts_smp_atomic32_t have_prepared_crash_dump; -#define ERTS_PREPARED_CRASH_DUMP \ - ((int) erts_smp_atomic32_xchg_nob(&have_prepared_crash_dump, 1)) -#else -static volatile int have_prepared_crash_dump; -#define ERTS_PREPARED_CRASH_DUMP \ - (have_prepared_crash_dump++) -#endif - -static erts_smp_atomic_t sys_misc_mem_sz; - -#if defined(ERTS_SMP) -erts_mtx_t chld_stat_mtx; -#endif - -#if defined(ERTS_SMP) /* ------------------------------------------------- */ -#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx) -#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx) - -#else /* ------------------------------------------------------------------- */ -#define CHLD_STAT_LOCK -#define CHLD_STAT_UNLOCK -static volatile int children_died; -#endif - -#define SET_AIO(REQ,FD,SIZE,BUFF) \ - memset(&(REQ),0,sizeof(REQ)); \ - (REQ).aio_fildes = FD; \ - (REQ).aio_offset = FM_POSITION_CURRENT; \ - (REQ).aio_nbytes = SIZE; \ - (REQ).aio_buf = BUFF; \ - (REQ).aio_sigevent.sigev_notify = SIGEV_NONE - -/* the first sizeof(struct aiocb *) bytes of the write buffer - * will contain the pointer to the aiocb struct, this needs - * to be freed between asynchronous writes. - * A write of 0 bytes is ignored. */ -#define WRITE_AIO(FD,SIZE,BUFF) do { \ - if (SIZE > 0) { \ - struct aiocb *write_req = driver_alloc(sizeof(struct aiocb)); \ - char *write_buff = driver_alloc((sizeof(char)*SIZE)+1+ \ - (sizeof(struct aiocb *))); \ - *(struct aiocb **)write_buff = (struct aiocb *)write_req; \ - write_buff += sizeof(struct aiocb *); \ - memcpy(write_buff,BUFF,SIZE+1); \ - SET_AIO(*write_req,FD,SIZE,write_buff); \ - if (aio_write(write_req)) \ - ramlog_printf("%s:%d: write failed with %d\n", \ - __FILE__,__LINE__,errno); \ - } \ -} while(0) - -/* free the write_buffer and write_req - * created in the WRITE_AIO() request macro */ -#define FREE_AIO(ptr) do { \ - struct aiocb *aiocb_ptr; \ - char *buffer_ptr; \ - aiocb_ptr = *(struct aiocb **)((ptr)-sizeof(struct aiocb *)); \ - buffer_ptr = (((char*)ptr)-sizeof(struct aiocb *)); \ - driver_free(aiocb_ptr); \ - driver_free(buffer_ptr); \ -} while(0) - -#define DISPATCH_AIO(sig) do { \ - if (aio_dispatch(sig)) \ - ramlog_printf("%s:%d: dispatch failed with %d\n", \ - __FILE__,__LINE__,errno); \ - } while(0) - -#define AIO_PIPE_SIZE 1024 - -/* debug print macros */ -#define DEBUG_RES 0 - -#ifdef DEBUG_RES -#define DEBUG_CHECK_RES(actual, expected) \ - do { \ - if (actual != expected ) { \ - ramlog_printf("Result check failed" \ - " got: 0x%08x expected:0x%08x\nat: %s:%d\n", \ - actual, expected, __FILE__, __LINE__); \ - abort(); /* This might perhaps be too harsh? */ \ - } \ - } while(0) -#else -#define DEBUG_CHECK_RES -#endif - -static struct fd_data { - char pbuf[4]; /* hold partial packet bytes */ - int psz; /* size of pbuf */ - char *buf; - char *cpos; - int sz; - int remain; /* for input on fd */ -} *fd_data; /* indexed by fd */ - -/********************* General functions ****************************/ - -/* This is used by both the drivers and general I/O, must be set early */ -static int max_files = -1; - -/* - * a few variables used by the break handler - */ -#ifdef ERTS_SMP -erts_smp_atomic32_t erts_break_requested; -#define ERTS_SET_BREAK_REQUESTED \ - erts_smp_atomic32_set_nob(&erts_break_requested, (erts_aint32_t) 1) -#define ERTS_UNSET_BREAK_REQUESTED \ - erts_smp_atomic32_set_nob(&erts_break_requested, (erts_aint32_t) 0) -#else -volatile int erts_break_requested = 0; -#define ERTS_SET_BREAK_REQUESTED (erts_break_requested = 1) -#define ERTS_UNSET_BREAK_REQUESTED (erts_break_requested = 0) -#endif -/* set early so the break handler has access to initial mode */ -static struct termios initial_tty_mode; -static int replace_intr = 0; -/* assume yes initially, ttsl_init will clear it */ -int using_oldshell = 1; -static PROCESS get_signal_proxy_pid(void); - -static void -init_check_io(void) -{ - erts_init_check_io(); - max_files = erts_check_io_max_files(); -} - -#ifdef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT -#define ERTS_CHK_IO_AS_INTR() erts_check_io_async_sig_interrupt() -#else -#define ERTS_CHK_IO_AS_INTR() erts_check_io_interrupt(1) -#endif -#define ERTS_CHK_IO_INTR erts_check_io_interrupt -#define ERTS_CHK_IO_INTR_TMD erts_check_io_interrupt_timed -#define ERTS_CHK_IO erts_check_io -#define ERTS_CHK_IO_SZ erts_check_io_size - - -void -erts_sys_schedule_interrupt(int set) -{ - ERTS_CHK_IO_INTR(set); -} - -#ifdef ERTS_SMP -void -erts_sys_schedule_interrupt_timed(int set, ErtsMonotonicTime timeout_time) -{ - ERTS_CHK_IO_INTR_TMD(set, timeout_time); -} -#endif - -Uint -erts_sys_misc_mem_sz(void) -{ - Uint res = ERTS_CHK_IO_SZ(); - res += erts_smp_atomic_read_mb(&sys_misc_mem_sz); - return res; -} - -/* - * reset the terminal to the original settings on exit - */ -void sys_tty_reset(int exit_code) -{ - if (using_oldshell && !replace_intr) { - SET_BLOCKING(0); - } - else if (isatty(0)) { - tcsetattr(0,TCSANOW,&initial_tty_mode); - } -} - -#ifdef USE_THREADS - -typedef struct { - int sched_bind_data; -} erts_thr_create_data_t; - -/* - * thr_create_prepare() is called in parent thread before thread creation. - * Returned value is passed as argument to thr_create_cleanup(). - */ -static void * -thr_create_prepare(void) -{ - erts_thr_create_data_t *tcdp; - - tcdp = erts_alloc(ERTS_ALC_T_TMP, sizeof(erts_thr_create_data_t)); - - tcdp->sched_bind_data = erts_sched_bind_atthrcreate_prepare(); - - return (void *) tcdp; -} - - -/* thr_create_cleanup() is called in parent thread after thread creation. */ -static void -thr_create_cleanup(void *vtcdp) -{ - erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp; - - erts_sched_bind_atthrcreate_parent(tcdp->sched_bind_data); - - erts_free(ERTS_ALC_T_TMP, tcdp); -} - -static void -thr_create_prepare_child(void *vtcdp) -{ - erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp; - -#ifdef ERTS_ENABLE_LOCK_COUNT - erts_lcnt_thread_setup(); -#endif - - erts_sched_bind_atthrcreate_child(tcdp->sched_bind_data); -} - -#endif /* #ifdef USE_THREADS */ - -/* The two functions below are stolen from win_con.c - They have to use malloc/free/realloc directly becasue - we want to do able to do erts_printf very early on. - */ -#define VPRINTF_BUF_INC_SIZE 128 -static erts_dsprintf_buf_t * -grow_vprintf_buf(erts_dsprintf_buf_t *dsbufp, size_t need) -{ - char *buf; - size_t size; - - ASSERT(dsbufp); - - if (!dsbufp->str) { - size = (((need + VPRINTF_BUF_INC_SIZE - 1) - / VPRINTF_BUF_INC_SIZE) - * VPRINTF_BUF_INC_SIZE); - buf = (char *) malloc(size * sizeof(char)); - } - else { - size_t free_size = dsbufp->size - dsbufp->str_len; - - if (need <= free_size) - return dsbufp; - - size = need - free_size + VPRINTF_BUF_INC_SIZE; - size = (((size + VPRINTF_BUF_INC_SIZE - 1) - / VPRINTF_BUF_INC_SIZE) - * VPRINTF_BUF_INC_SIZE); - size += dsbufp->size; - buf = (char *) realloc((void *) dsbufp->str, - size * sizeof(char)); - } - if (!buf) - return NULL; - if (buf != dsbufp->str) - dsbufp->str = buf; - dsbufp->size = size; - return dsbufp; -} - -static int erts_sys_ramlog_printf(char *format, va_list arg_list) -{ - int res,i; - erts_dsprintf_buf_t dsbuf = ERTS_DSPRINTF_BUF_INITER(grow_vprintf_buf); - res = erts_vdsprintf(&dsbuf, format, arg_list); - if (res >= 0) { - for (i = 0; i < dsbuf.str_len; i+= 50) - /* We print 50 characters at a time because otherwise - the ramlog looks broken */ - ramlog_printf("%.*s",dsbuf.str_len-50 < 0?dsbuf.str_len:50,dsbuf.str+i); - } - if (dsbuf.str) - free((void *) dsbuf.str); - return res; -} - -void -erts_sys_pre_init(void) -{ - erts_printf_add_cr_to_stdout = 1; - erts_printf_add_cr_to_stderr = 1; -#ifdef USE_THREADS - { - erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER; - - eid.thread_create_child_func = thr_create_prepare_child; - /* Before creation in parent */ - eid.thread_create_prepare_func = thr_create_prepare; - /* After creation in parent */ - eid.thread_create_parent_func = thr_create_cleanup, - - erts_thr_init(&eid); - - report_exit_list = NULL; - -#ifdef ERTS_ENABLE_LOCK_COUNT - erts_lcnt_init(); -#endif - -#if defined(ERTS_SMP) - erts_mtx_init(&chld_stat_mtx, "child_status"); -#endif - } -#ifdef ERTS_SMP - erts_smp_atomic32_init_nob(&erts_break_requested, 0); - erts_smp_atomic32_init_nob(&have_prepared_crash_dump, 0); -#else - erts_break_requested = 0; - have_prepared_crash_dump = 0; -#endif -#if !defined(ERTS_SMP) - children_died = 0; -#endif -#endif /* USE_THREADS */ - - erts_printf_stdout_func = erts_sys_ramlog_printf; - - erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0); -} - -void -erl_sys_init(void) -{ - -#ifdef USE_SETLINEBUF - setlinebuf(stdout); -#else - setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ); -#endif - - erts_sys_init_float(); - - /* we save this so the break handler can set and reset it properly */ - /* also so that we can reset on exit (break handler or not) */ - if (isatty(0)) { - tcgetattr(0,&initial_tty_mode); - } - tzset(); /* Required at least for NetBSD with localtime_r() */ -} - -static ERTS_INLINE int -prepare_crash_dump(int secs) -{ -#define NUFBUF (3) - int i, max; - char env[21]; /* enough to hold any 64-bit integer */ - size_t envsz; - /*DeclareTmpHeapNoproc(heap,NUFBUF);*/ - /*Eterm *hp = heap;*/ - /*Eterm list = NIL;*/ - int has_heart = 0; - - UseTmpHeapNoproc(NUFBUF); - - if (ERTS_PREPARED_CRASH_DUMP) - return 0; /* We have already been called */ - - - /* Positive secs means an alarm must be set - * 0 or negative means no alarm - * - * Set alarm before we try to write to a port - * we don't want to hang on a port write with - * no alarm. - * - */ - -#if 0 /*ose TBD!!!*/ - if (secs >= 0) { - alarm((unsigned int)secs); - } -#endif - - /* Make sure we unregister at epmd (unknown fd) and get at least - one free filedescriptor (for erl_crash.dump) */ - - max = max_files; - if (max < 1024) - max = 1024; - for (i = 3; i < max; i++) { - close(i); - } - - envsz = sizeof(env); - i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz); - if (i >= 0) { - int nice_val; - nice_val = i != 0 ? 0 : atoi(env); - if (nice_val > 39) { - nice_val = 39; - } - set_pri(nice_val); - } - - UnUseTmpHeapNoproc(NUFBUF); -#undef NUFBUF - return has_heart; -} - -int erts_sys_prepare_crash_dump(int secs) -{ - return prepare_crash_dump(secs); -} - -static ERTS_INLINE void -break_requested(void) -{ - /* - * just set a flag - checked for and handled by - * scheduler threads erts_check_io() (not signal handler). - */ -#ifdef DEBUG - fprintf(stderr,"break!\n"); -#endif - if (ERTS_BREAK_REQUESTED) - erl_exit(ERTS_INTR_EXIT, ""); - - ERTS_SET_BREAK_REQUESTED; - ERTS_CHK_IO_AS_INTR(); /* Make sure we don't sleep in poll */ -} - -/* Disable break */ -void erts_set_ignore_break(void) { - -} - -/* Don't use ctrl-c for break handler but let it be - used by the shell instead (see user_drv.erl) */ -void erts_replace_intr(void) { - struct termios mode; - - if (isatty(0)) { - tcgetattr(0, &mode); - - /* here's an example of how to replace ctrl-c with ctrl-u */ - /* mode.c_cc[VKILL] = 0; - mode.c_cc[VINTR] = CKILL; */ - - mode.c_cc[VINTR] = 0; /* disable ctrl-c */ - tcsetattr(0, TCSANOW, &mode); - replace_intr = 1; - } -} - -void init_break_handler(void) -{ - -} - -int sys_max_files(void) -{ - return(max_files); -} - - -/************************** OS info *******************************/ - -/* Used by erlang:info/1. */ -/* (This code was formerly in drv.XXX/XXX_os_drv.c) */ - -char os_type[] = "ose"; - -void -os_flavor(char* namebuf, /* Where to return the name. */ - unsigned size) /* Size of name buffer. */ -{ -#if 0 - struct utsname uts; /* Information about the system. */ - char* s; - - (void) uname(&uts); - for (s = uts.sysname; *s; s++) { - if (isupper((int) *s)) { - *s = tolower((int) *s); - } - } - strcpy(namebuf, uts.sysname); -#else - strncpy(namebuf, "release", size); -#endif -} - -void -os_version(pMajor, pMinor, pBuild) -int* pMajor; /* Pointer to major version. */ -int* pMinor; /* Pointer to minor version. */ -int* pBuild; /* Pointer to build number. */ -{ - *pMajor = 5; - *pMinor = 7; - *pBuild = 0; -} - -void init_getenv_state(GETENV_STATE *state) -{ - erts_smp_rwmtx_rlock(&environ_rwmtx); - *state = NULL; -} - -char **environ; /*ose - needs replacement*/ - -char *getenv_string(GETENV_STATE *state0) -{ - char **state = (char **) *state0; - char *cp; - - ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx)); - - if (state == NULL) - state = environ; - - cp = *state++; - *state0 = (GETENV_STATE) state; - - return cp; -} - -void fini_getenv_state(GETENV_STATE *state) -{ - *state = NULL; - erts_smp_rwmtx_runlock(&environ_rwmtx); -} - - -/************************** Port I/O *******************************/ - -/* I. Common stuff */ - -union SIGNAL { - SIGSELECT sig_no; - struct FmReadPtr fm_read_reply; - struct FmWritePtr fm_write_reply; - struct async async; -}; - -/* II. The spawn/fd drivers */ - -/* - * Decreasing the size of it below 16384 is not allowed. - */ -#define ERTS_SYS_READ_BUF_SZ (64*1024) - -/* Driver interfaces */ -static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*); -static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*); -static ErlDrvSSizeT fd_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT, - char **, ErlDrvSizeT); -static int spawn_init(void); -static void fd_stop(ErlDrvData); -static void erl_stop(ErlDrvData); -static void ready_input(ErlDrvData, ErlDrvEvent); -static void ready_output(ErlDrvData, ErlDrvEvent); -static void output(ErlDrvData, char*, ErlDrvSizeT); -static void stop_select(ErlDrvEvent, void*); - -static PROCESS -get_signal_proxy_pid(void) { - union SIGNAL *sig; - SIGSELECT any_sig[] = {1,ERTS_SIGNAL_OSE_DRV_ATTACH}; - - if (!sig_proxy_pid) { - sig = alloc(sizeof(union SIGNAL), ERTS_SIGNAL_OSE_DRV_ATTACH); - hunt("ose_signal_driver_proxy", 0, NULL, &sig); - sig = receive(any_sig); - sig_proxy_pid = sender(&sig); - free_buf(&sig); - } - ASSERT(sig_proxy_pid); - return sig_proxy_pid; -} - -static ErlDrvOseEventId -resolve_signal(union SIGNAL* sig) { - switch(sig->sig_no) { - - case FM_READ_PTR_REPLY: - return (ErlDrvOseEventId)sig->fm_read_reply.handle; - - case FM_WRITE_PTR_REPLY: - return (ErlDrvOseEventId)sig->fm_write_reply.handle; - - case ERTS_SIGNAL_OSE_DRV_ATTACH: - return (ErlDrvOseEventId)sig->async.target; - - default: - break; - } - return (ErlDrvOseEventId)-1; -} - -struct erl_drv_entry spawn_driver_entry = { - spawn_init, - spawn_start, - NULL, /* erl_stop, */ - output, - ready_input, - ready_output, - "spawn", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, - ERL_DRV_FLAG_USE_PORT_LOCKING, - NULL, NULL, - stop_select -}; -struct erl_drv_entry fd_driver_entry = { - NULL, - fd_start, - fd_stop, - output, - ready_input, - ready_output, - "fd", - NULL, - NULL, - fd_control, - NULL, - NULL, - NULL, /* ready_async */ - NULL, /* flush */ - NULL, /* call */ - NULL, /* event */ - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, - 0, /* ERL_DRV_FLAGs */ - NULL, /* handle2 */ - NULL, /* process_exit */ - stop_select -}; - -static void -set_spawn_fd(int local_fd, int remote_fd, PROCESS remote_pid) { - PROCESS vm_pid; - FmHandle handle; - char env_val[55]; - char env_name[10]; - EfsStatus efs_res; - - /* get pid of pipevm and handle of chosen fd */ - efs_res = efs_examine_fd(local_fd, FLIB_FD_VMPID, &vm_pid, 0); - DEBUG_CHECK_RES(efs_res, EFS_SUCCESS); - - /* setup the file descriptor to buffer per line */ - efs_res = efs_config_fd(local_fd, FLIB_FD_BUFMODE, FM_BUFF_LINE, - FLIB_FD_BUFSIZE, 80, 0); - DEBUG_CHECK_RES(efs_res, EFS_SUCCESS); - - /* duplicate handle and set spawn pid owner */ - efs_res = efs_dup_to(local_fd, remote_pid, &handle); - DEBUG_CHECK_RES(efs_res, EFS_SUCCESS); - - sprintf(env_name, "FD%d", remote_fd); - - /* Syntax of the environment variable: - * "FD#" ",,,," */ - sprintf(env_val, "0x%lx,0x%lx,%lu,%lu,0x%x", - vm_pid, handle, - FM_BUFF_LINE, 80, - O_APPEND); - - set_env(remote_pid, env_name, env_val); -} - -static ErlDrvData -set_driver_data(ErlDrvPort port_num, - int ifd, - int ofd, - int packet_bytes, - int read_write, - int exit_status, - PROCESS pid) -{ - Port *prt; - ErtsSysReportExit *report_exit; - - prt = erts_drvport2port(port_num); - if (prt != ERTS_INVALID_ERL_DRV_PORT) { - prt->os_pid = pid; - } - - /* READ */ - if (read_write & DO_READ) { - EfsStatus res = efs_examine_fd(ifd, FLIB_FD_HANDLE, - &driver_data[ifd].handle, 0); - if (res != EFS_SUCCESS) - ramlog_printf("%s:%d: efs_examine_fd(%d) failed with %d\n", - __FILE__,__LINE__,ifd,errno); - driver_data[ifd].ifd = ifd; - driver_data[ifd].packet_bytes = packet_bytes; - driver_data[ifd].port_num = port_num; - driver_data[ifd].pid = pid; - - /* async read struct */ - memset(&driver_data[ifd].aiocb, 0, sizeof(struct aiocb)); - driver_data[ifd].aiocb.aio_buf = driver_alloc(AIO_PIPE_SIZE); - driver_data[ifd].aiocb.aio_fildes = ifd; - driver_data[ifd].aiocb.aio_nbytes = (packet_bytes?packet_bytes:AIO_PIPE_SIZE); - driver_data[ifd].alive = 1; - driver_data[ifd].status = 0; - driver_data[ifd].input_event = - erl_drv_ose_event_alloc(FM_READ_PTR_REPLY, - driver_data[ifd].handle, resolve_signal, - &driver_data[ifd].ifd); - - /* READ & WRITE */ - if (read_write & DO_WRITE) { - driver_data[ifd].ofd = ofd; - efs_examine_fd(ofd, FLIB_FD_HANDLE, &driver_data[ofd].handle, 0); - - driver_data[ifd].output_event = - erl_drv_ose_event_alloc(FM_WRITE_PTR_REPLY, - driver_data[ofd].handle, resolve_signal, - &driver_data[ofd].ofd); - driver_data[ofd].pid = pid; - if (ifd != ofd) { - driver_data[ofd] = driver_data[ifd]; - driver_data[ofd].aiocb.aio_buf = NULL; - } - } - else { /* READ ONLY */ - driver_data[ifd].ofd = -1; - } - - /* enable input event */ - (void) driver_select(port_num, driver_data[ifd].input_event, - (ERL_DRV_READ | ERL_DRV_USE), 1); - - if (aio_read(&driver_data[ifd].aiocb)) - ramlog_printf("%s:%d: aio_read(%d) failed with %d\n", - __FILE__,__LINE__,ifd,errno); - } - else { /* WRITE ONLY */ - efs_examine_fd(ofd, FLIB_FD_HANDLE, &driver_data[ofd].handle, 0); - driver_data[ofd].packet_bytes = packet_bytes; - driver_data[ofd].port_num = port_num; - driver_data[ofd].ofd = ofd; - driver_data[ofd].pid = pid; - driver_data[ofd].alive = 1; - driver_data[ofd].status = 0; - driver_data[ofd].output_event = - erl_drv_ose_event_alloc(FM_WRITE_PTR_REPLY, driver_data[ofd].handle, - resolve_signal, &driver_data[ofd].ofd); - driver_data[ofd].input_event = driver_data[ofd].output_event; - } - - /* this is used for spawned load modules, and is needed - * to properly uninstall them */ - if (exit_status) { - struct PmProgramInfo *info; - int install_handle_size; - union SIGNAL *sig; - PmStatus pm_status; - report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT, - sizeof(ErtsSysReportExit)); - report_exit->next = report_exit_list; - report_exit->port = erts_drvport2id(port_num); - report_exit->pid = pid; - report_exit->ifd = (read_write & DO_READ) ? ifd : -1; - report_exit->ofd = (read_write & DO_WRITE) ? ofd : -1; - report_exit_list = report_exit; - report_exit->attach_event = - erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH, pid, - resolve_signal, &driver_data[ifd].ifd); - - /* setup ifd and ofd report exit */ - driver_data[ifd].report_exit = report_exit; - driver_data[ofd].report_exit = report_exit; - - pm_status = ose_pm_program_info(pid, &info); - DEBUG_CHECK_RES(pm_status, PM_SUCCESS); - - install_handle_size = strlen(info->install_handle)+1; - driver_data[ifd].install_handle = driver_alloc(install_handle_size); - strcpy(driver_data[ifd].install_handle, - info->install_handle); - - free_buf((union SIGNAL **)&info); - - sig = alloc(sizeof(struct async), ERTS_SIGNAL_OSE_DRV_ATTACH); - sig->async.target = pid; - send(&sig, get_signal_proxy_pid()); - - /* this event will trigger when we receive an attach signal - * from the recently dead load module */ - (void)driver_select(port_num,report_exit->attach_event, DO_READ, 1); - } - else { - report_exit = NULL; - } - - /* the return value is the pointer to the driver_data struct we created - * in this function, it will be used in the drivers input - * and output functions */ - return (ErlDrvData)((!(read_write & DO_READ) && read_write & DO_WRITE) - ? &driver_data[ofd] - : &driver_data[ifd]); -} - -static int spawn_init() -{ - int i; - - driver_data = (struct driver_data *) - erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data)); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, - max_files * sizeof(struct driver_data)); - - for (i = 0; i < max_files; i++) - driver_data[i].pid = -1; - - return 1; -} - -static void -init_fd_data(int fd, ErlDrvPort port_num) -{ - fd_data[fd].buf = NULL; - fd_data[fd].cpos = NULL; - fd_data[fd].remain = 0; - fd_data[fd].sz = 0; - fd_data[fd].psz = 0; -} - -/* FIXME write a decent text on pipes on ose */ -static ErlDrvData -spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) -{ - int ifd[2]; - int ofd[2]; - static uint32_t ticker = 1; - PmStatus pm_status; - OSDOMAIN domain = PM_NEW_DOMAIN; - PROCESS progpid, mainbid, mainpid; - char *handle = NULL; - struct PmProgramInfo *info; - char *args = NULL; - char *tmp_handle; - ErlDrvData res = (ErlDrvData)-1; - int handle_size; - char *ptr; - - - args = driver_alloc(strlen(name)+1); - strcpy(args, name); - /* We need to handle name in three parts - * - install handle (must be unique) - * - install binary (needed for ose_pm_install_load_module()) - * - full path (as argument to the spawned applications env.var - */ - - /* full path including arguments */ - args = driver_alloc(strlen(name)+1); - strcpy(args, name); - - /* handle path */ - tmp_handle = strrchr(name, '/'); - if (tmp_handle == NULL) { - tmp_handle = name; - } - else { - tmp_handle++; - } - - /* handle args */ - ptr = strchr(tmp_handle, ' '); - if (ptr != NULL) { - *ptr = '\0'; - handle_size = ptr - tmp_handle; - } - else { - handle_size = strlen(name)+1; - } - - /* make room for ticker */ - handle_size += (ticker<10)?3:((ticker<100)?4:5); - handle = driver_alloc(handle_size); - - do { - snprintf(handle, handle_size, "%s_%d", tmp_handle, ticker); - pm_status = ose_pm_install_load_module(0, "ELF", name, handle, - 0, 0, NULL); - ticker++; - } while (pm_status == PM_EINSTALL_HANDLE_ALREADY_INSTALLED); - - if (pm_status != PM_SUCCESS) { - errno = ENOSYS; /* FIXME add comment */ - return ERL_DRV_ERROR_ERRNO; - } - - /* Create Program */ - pm_status = ose_pm_create_program(&domain, handle, 0, 0, - NULL, &progpid, &mainbid); - DEBUG_CHECK_RES(pm_status, PM_SUCCESS); - - /* Get the mainpid from the newly created program */ - pm_status = ose_pm_program_info(progpid, &info); - DEBUG_CHECK_RES(pm_status, PM_SUCCESS); - - mainpid = info->main_process; - free_buf ((union SIGNAL **)&info); - - /* pipevm needs to be started - * pipe will return 0 if success, -1 if not, - * errno will be set */ - if (pipe(ifd) != 0 || pipe(ofd) != 0) { - DEBUG_CHECK_RES(0, -1); - ASSERT(0); - } - - /* setup driver data */ - res = set_driver_data(port_num, ofd[0], ifd[1], opts->packet_bytes, - opts->read_write, 1 /* opts->exit_status */, progpid); - - /* init the fd_data array for read/write */ - init_fd_data(ofd[0], port_num); - init_fd_data(ifd[1], port_num); - - /* setup additional configurations - * for the spawned applications environment */ - if (args != NULL) { - set_env(progpid, "ARGV", args); - } - set_env(mainbid, "EFS_RESOLVE_TMO", 0); - set_spawn_fd(ifd[0], 0, mainpid); - set_spawn_fd(ofd[1], 1, mainpid); - set_spawn_fd(ofd[1], 2, mainpid); - - /* start the spawned program */ - pm_status = ose_pm_start_program(mainbid); - DEBUG_CHECK_RES(pm_status, PM_SUCCESS); - - /* close unused fd's */ - close(ifd[0]); - close(ofd[1]); - - if (handle) { - driver_free(handle); - } - - return (ErlDrvData)res; -} - -#define FD_DEF_HEIGHT 24 -#define FD_DEF_WIDTH 80 -/* Control op */ -#define FD_CTRL_OP_GET_WINSIZE 100 - -static int fd_get_window_size(int fd, Uint32 *width, Uint32 *height) -{ -#ifdef TIOCGWINSZ - struct winsize ws; - if (ioctl(fd,TIOCGWINSZ,&ws) == 0) { - *width = (Uint32) ws.ws_col; - *height = (Uint32) ws.ws_row; - return 0; - } -#endif - return -1; -} - -static ErlDrvSSizeT fd_control(ErlDrvData drv_data, - unsigned int command, - char *buf, ErlDrvSizeT len, - char **rbuf, ErlDrvSizeT rlen) -{ - struct driver_data *data = (struct driver_data *)drv_data; - char resbuff[2*sizeof(Uint32)]; - switch (command) { - case FD_CTRL_OP_GET_WINSIZE: - { - Uint32 w,h; - if (fd_get_window_size(data->ifd,&w,&h)) - return 0; - memcpy(resbuff,&w,sizeof(Uint32)); - memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32)); - } - break; - default: - return 0; - } - if (rlen < 2*sizeof(Uint32)) { - *rbuf = driver_alloc(2*sizeof(Uint32)); - } - memcpy(*rbuf,resbuff,2*sizeof(Uint32)); - return 2*sizeof(Uint32); -} - -static ErlDrvData fd_start(ErlDrvPort port_num, char* name, - SysDriverOpts* opts) -{ - ErlDrvData res; - - CHLD_STAT_LOCK; - if (opts->read_write & DO_READ) { - init_fd_data(opts->ifd, port_num); - } - if (opts->read_write & DO_WRITE) { - init_fd_data(opts->ofd, port_num); - } - res = set_driver_data(port_num, opts->ifd, opts->ofd, - opts->packet_bytes, - opts->read_write, 0, -1); - CHLD_STAT_UNLOCK; - return res; -} - -static void clear_fd_data(int fd) -{ - if (fd_data[fd].sz > 0) { - erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf); - ASSERT(erts_smp_atomic_read_nob(&sys_misc_mem_sz) >= fd_data[fd].sz); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*fd_data[fd].sz); - } - fd_data[fd].buf = NULL; - fd_data[fd].sz = 0; - fd_data[fd].remain = 0; - fd_data[fd].cpos = NULL; - fd_data[fd].psz = 0; -} - -static void nbio_stop_fd(ErlDrvPort prt, ErlDrvEvent ev) -{ - int *fd; - driver_select(prt,ev,DO_READ|DO_WRITE,0); - erl_drv_ose_event_fetch(ev, NULL, NULL, (void **)&fd); - clear_fd_data(*fd); - SET_BLOCKING(*fd); -} - -static void fd_stop(ErlDrvData drv_data) /* Does not close the fds */ -{ - struct driver_data *data = (struct driver_data *)drv_data; - - if (data->ofd != -1) { - if (data->ifd != data->ofd) { /* read and write */ - nbio_stop_fd(data->port_num, data->input_event); - nbio_stop_fd(data->port_num, data->output_event); - } - else { /* write only */ - nbio_stop_fd(data->port_num, data->output_event); - } - } - else { /* read only */ - nbio_stop_fd(data->port_num, data->input_event); - } -} - - -static void erl_stop(ErlDrvData drv_data) -{ - struct driver_data *data = (struct driver_data *)drv_data; - - CHLD_STAT_LOCK; - data->pid = -1; - CHLD_STAT_UNLOCK; - - if (data->ofd != -1) { - if (data->ifd != data->ofd) { /* read and write */ - nbio_stop_fd(data->port_num, data->input_event); - nbio_stop_fd(data->port_num, data->output_event); - } - else { /* write only */ - nbio_stop_fd(data->port_num, data->output_event); - } - } - else { /* read only */ - nbio_stop_fd(data->port_num, data->input_event); - } - close(data->ifd); - close(data->ofd); -} - -/* The parameter e is a pointer to the driver_data structure - * related to the fd to be used as output */ -static void output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len) -{ - ErlDrvSizeT sz; - char lb[4]; - char* lbp; - struct driver_data *data = (struct driver_data *)drv_data; - - if (((data->packet_bytes == 2) && - (len > 0xffff)) || (data->packet_bytes == 1 && len > 0xff)) { - driver_failure_posix(data->port_num, EINVAL); - return; /* -1; */ - } - put_int32(len, lb); - lbp = lb + (4-(data->packet_bytes)); - - if ((sz = driver_sizeq(data->port_num)) > 0) { - if (data->packet_bytes != 0) { - driver_enq(data->port_num, lbp, data->packet_bytes); - } - driver_enq(data->port_num, buf, len); - - if (sz + len + data->packet_bytes >= (1 << 13)) - set_busy_port(data->port_num, 1); - } - else { - char *pbbuf; - if (data->packet_bytes != 0) { - pbbuf = malloc(len + data->packet_bytes); - int i; - for (i = 0; i < data->packet_bytes; i++) { - *pbbuf++ = *lbp++; - } - strncpy(pbbuf, buf, len); - pbbuf -= data->packet_bytes; - } - driver_select(data->port_num, data->output_event, - ERL_DRV_WRITE|ERL_DRV_USE, 1); - WRITE_AIO(data->ofd, - (data->packet_bytes ? len+data->packet_bytes : len), - (data->packet_bytes ? pbbuf : buf)); - if (data->packet_bytes != 0) free(pbbuf); - } - return; /* 0; */ -} - -/* This function is being run when we in recieve - * either a read of 0 bytes, or the attach signal from a dying - * spawned load module */ -static int port_inp_failure(ErlDrvPort port_num, ErlDrvEvent ready_fd, int res) - /* Result: 0 (eof) or -1 (error) */ -{ - int *fd; - SIGSELECT sig_no; - ASSERT(res <= 0); - - erl_drv_ose_event_fetch(ready_fd,&sig_no, NULL, (void **)&fd); - /* As we need to handle two signals, we do this in two steps */ - if (driver_data[*fd].alive) { - report_exit_status(driver_data[*fd].report_exit, 0); /* status? */ - } - else { - driver_select(port_num,ready_fd,DO_READ|DO_WRITE,0); - clear_fd_data(*fd); - driver_report_exit(driver_data[*fd].port_num, driver_data[*fd].status); - /* As we do not really know if the spawn has crashed or exited nicely - * we do not check the result status of the following call.. FIXME - * can we handle this in a better way? */ - ose_pm_uninstall_load_module(driver_data[*fd].install_handle); - driver_free(driver_data[*fd].install_handle); - driver_free((void *)driver_data[*fd].aiocb.aio_buf); - - close(*fd); - } - - return 0; -} - -/* The parameter e is a pointer to the driver_data structure - * related to the fd to be used as output. - * ready_fd is the event that triggered this call to ready_input */ -static void ready_input(ErlDrvData drv_data, ErlDrvEvent ready_fd) -{ - int res; - Uint h; - char *buf; - union SIGNAL *sig; - struct driver_data *data = (struct driver_data *)drv_data; - - sig = erl_drv_ose_get_signal(ready_fd); - ASSERT(sig); - - - while (sig) { - /* If we've recieved an attach signal, we need to handle - * it in port_inp_failure */ - if (sig->sig_no == ERTS_SIGNAL_OSE_DRV_ATTACH) { - port_inp_failure(data->port_num, ready_fd, 0); - } - else { - res = sig->fm_read_reply.actual; - if (res == 0) { - port_inp_failure(data->port_num, ready_fd, res); - break; - } - - if (data->packet_bytes == 0) { - if (res < 0) { - if ((errno != EINTR) && (errno != ERRNO_BLOCK)) { - port_inp_failure(data->port_num, ready_fd, res); - } - } - else if (res == 0) { - /* read of 0 bytes, eof, otherside of pipe is assumed dead */ - port_inp_failure(data->port_num, ready_fd, res); - break; - } - else { - buf = driver_alloc(res); - memcpy(buf, (void *)data->aiocb.aio_buf, res); - driver_select(data->port_num, data->output_event, - ERL_DRV_WRITE|ERL_DRV_USE, 1); - driver_output(data->port_num, (char*) buf, res); - driver_free(buf); - } - /* clear the previous read */ - memset(data->aiocb.aio_buf, 0, res); - - /* issue a new read */ - DISPATCH_AIO(sig); - aio_read(&data->aiocb); - } - else if (data->packet_bytes && fd_data[data->ifd].remain > 0) { - /* we've read a partial package, or a header */ - - if (res == fd_data[data->ifd].remain) { /* we are done! */ - char *buf = data->aiocb.aio_buf; - int i; - - /* do we have anything buffered? */ - if (fd_data[data->ifd].buf != NULL) { - memcpy(fd_data[data->ifd].buf + fd_data[data->ifd].sz, - buf, res); - buf = fd_data[data->ifd].buf; - } - - fd_data[data->ifd].sz += res; - driver_output(data->port_num, buf, (fd_data[data->ifd].sz>0?fd_data[data->ifd].sz:res)); - clear_fd_data(data->ifd); - - /* clear the previous read */ - memset(data->aiocb.aio_buf, 0, res); - - /* issue a new read */ - DISPATCH_AIO(sig); - data->aiocb.aio_nbytes = data->packet_bytes; - - if (data->aiocb.aio_buf == NULL) { - port_inp_failure(data->port_num, ready_fd, -1); - } - aio_read(&data->aiocb); - } - else if(res < fd_data[data->ifd].remain) { /* received part of a package */ - if (fd_data[data->ifd].sz == 0) { - - fd_data[data->ifd].sz += res; - memcpy(fd_data[data->ifd].buf, data->aiocb.aio_buf, res); - fd_data[data->ifd].remain -= res; - } - else { - memcpy(fd_data[data->ifd].buf + fd_data[data->ifd].sz, - data->aiocb.aio_buf, res); - fd_data[data->ifd].sz += res; - fd_data[data->ifd].remain -= res; - } - /* clear the previous read */ - memset(data->aiocb.aio_buf, 0, res); - - /* issue a new read */ - DISPATCH_AIO(sig); - data->aiocb.aio_nbytes = fd_data[data->ifd].remain; - - if (data->aiocb.aio_buf == NULL) { - port_inp_failure(data->port_num, ready_fd, -1); - } - aio_read(&data->aiocb); - } - } - else if (data->packet_bytes && fd_data[data->ifd].remain == 0) { /* we've recieved a header */ - - /* analyze the header FIXME */ - switch (data->packet_bytes) { - case 1: h = get_int8(data->aiocb.aio_buf); break; - case 2: h = get_int16(data->aiocb.aio_buf); break; - case 4: h = get_int32(data->aiocb.aio_buf); break; - } - - fd_data[data->ifd].buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h + data->packet_bytes); - fd_data[data->ifd].remain = ((h + data->packet_bytes) - res); - - /* clear the previous read */ - memset(data->aiocb.aio_buf, 0, data->packet_bytes); - - /* issue a new read */ - DISPATCH_AIO(sig); - data->aiocb.aio_nbytes = h; - - if (data->aiocb.aio_buf == NULL) { - port_inp_failure(data->port_num, ready_fd, -1); - } - aio_read(&data->aiocb); - } - } - sig = erl_drv_ose_get_signal(ready_fd); - } -} - - -/* The parameter e is a pointer to the driver_data structure - * related to the fd to be used as output. - * ready_fd is the event that triggered this call to ready_input */ -static void ready_output(ErlDrvData drv_data, ErlDrvEvent ready_fd) -{ - SysIOVec *iov; - int vlen; - int res; - union SIGNAL *sig; - struct driver_data *data = (struct driver_data *)drv_data; - - sig = erl_drv_ose_get_signal(ready_fd); - ASSERT(sig); - - while (sig != NULL) { - if (sig->fm_write_reply.actual <= 0) { - int status; - - status = efs_status_to_errno(sig->fm_write_reply.status); - driver_select(data->port_num, ready_fd, ERL_DRV_WRITE, 0); - DISPATCH_AIO(sig); - FREE_AIO(sig->fm_write_reply.buffer); - - driver_failure_posix(data->port_num, status); - } - else { /* written bytes > 0 */ - iov = driver_peekq(data->port_num, &vlen); - if (vlen > 0) { - DISPATCH_AIO(sig); - FREE_AIO(sig->fm_write_reply.buffer); - res = driver_deq(data->port_num, iov[0].iov_len); - if (res > 0) { - iov = driver_peekq(data->port_num, &vlen); - WRITE_AIO(data->ofd, iov[0].iov_len, iov[0].iov_base); - } - } - else if (vlen == 0) { - DISPATCH_AIO(sig); - FREE_AIO(sig->fm_write_reply.buffer); - } - - } - sig = erl_drv_ose_get_signal(ready_fd); - } -} - -static void stop_select(ErlDrvEvent ready_fd, void* _) -{ - int *fd; - erl_drv_ose_event_fetch(ready_fd, NULL, NULL, (void **)&fd); - erl_drv_ose_event_free(ready_fd); - close(*fd); -} - - -void erts_do_break_handling(void) -{ - struct termios temp_mode; - int saved = 0; - - /* - * Most functions that do_break() calls are intentionally not thread safe; - * therefore, make sure that all threads but this one are blocked before - * proceeding! - */ - erts_smp_thr_progress_block(); - - /* during break we revert to initial settings */ - /* this is done differently for oldshell */ - if (using_oldshell && !replace_intr) { - SET_BLOCKING(1); - } - else if (isatty(0)) { - tcgetattr(0,&temp_mode); - tcsetattr(0,TCSANOW,&initial_tty_mode); - saved = 1; - } - - /* call the break handling function, reset the flag */ - do_break(); - - fflush(stdout); - - /* after break we go back to saved settings */ - if (using_oldshell && !replace_intr) { - SET_NONBLOCKING(1); - } - else if (saved) { - tcsetattr(0,TCSANOW,&temp_mode); - } - - erts_smp_thr_progress_unblock(); -} - -static pid_t -getpid(void) -{ - return get_bid(current_process()); -} - -int getpagesize(void) -{ - return 1024; -} - - -/* Fills in the systems representation of the jam/beam process identifier. -** The Pid is put in STRING representation in the supplied buffer, -** no interpretatione of this should be done by the rest of the -** emulator. The buffer should be at least 21 bytes long. -*/ -void sys_get_pid(char *buffer, size_t buffer_size){ - pid_t p = getpid(); - /* Assume the pid is scalar and can rest in an unsigned long... */ - erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p); -} - -int -erts_sys_putenv_raw(char *key, char *value) { - return erts_sys_putenv(key, value); -} -int -erts_sys_putenv(char *key, char *value) -{ - int res; - - erts_smp_rwmtx_rwlock(&environ_rwmtx); - res = set_env(get_bid(current_process()), key, - value); - erts_smp_rwmtx_rwunlock(&environ_rwmtx); - return res; -} - - -int -erts_sys_unsetenv(char *key) -{ - int res; - - erts_smp_rwmtx_rwlock(&environ_rwmtx); - res = set_env(get_bid(current_process()),key,NULL); - erts_smp_rwmtx_rwunlock(&environ_rwmtx); - - return res; -} - -int -erts_sys_getenv__(char *key, char *value, size_t *size) -{ - int res; - char *orig_value = get_env(get_bid(current_process()), key); - if (!orig_value) - res = -1; - else { - size_t len = sys_strlen(orig_value); - if (len >= *size) { - *size = len + 1; - res = 1; - } - else { - *size = len; - sys_memcpy((void *) value, (void *) orig_value, len+1); - res = 0; - } - free_buf((union SIGNAL **)&orig_value); - } - return res; -} - -int -erts_sys_getenv_raw(char *key, char *value, size_t *size) { - return erts_sys_getenv(key, value, size); -} - -/* - * erts_sys_getenv - * returns: - * -1, if environment key is not set with a value - * 0, if environment key is set and value fits into buffer res - * 1, if environment key is set but does not fit into buffer res - * res is set with the needed buffer res value - */ - -int -erts_sys_getenv(char *key, char *value, size_t *size) -{ - int res; - erts_smp_rwmtx_rlock(&environ_rwmtx); - res = erts_sys_getenv__(key, value, size); - erts_smp_rwmtx_runlock(&environ_rwmtx); - return res; -} - -void -sys_init_io(void) -{ - fd_data = (struct fd_data *) - erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(struct fd_data)); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, - max_files * sizeof(struct fd_data)); -} - -extern const char pre_loaded_code[]; -extern Preload pre_loaded[]; - -void erts_sys_alloc_init(void) -{ -} - -void *erts_sys_alloc(ErtsAlcType_t t, void *x, Uint sz) -{ - void *res = malloc((size_t) sz); -#if HAVE_ERTS_MSEG - if (!res) { - erts_mseg_clear_cache(); - return malloc((size_t) sz); - } -#endif - return res; -} - -void *erts_sys_realloc(ErtsAlcType_t t, void *x, void *p, Uint sz) -{ - void *res = realloc(p, (size_t) sz); -#if HAVE_ERTS_MSEG - if (!res) { - erts_mseg_clear_cache(); - return realloc(p, (size_t) sz); - } -#endif - return res; -} - -void erts_sys_free(ErtsAlcType_t t, void *x, void *p) -{ - free(p); -} - -/* Return a pointer to a vector of names of preloaded modules */ - -Preload* -sys_preloaded(void) -{ - return pre_loaded; -} - -/* Return a pointer to preloaded code for module "module" */ -unsigned char* -sys_preload_begin(Preload* p) -{ - return p->code; -} - -/* Clean up if allocated */ -void sys_preload_end(Preload* p) -{ - /* Nothing */ -} - -/* Read a key from console (?) */ - -int sys_get_key(fd) -int fd; -{ - int c; - unsigned char rbuf[64]; - - fflush(stdout); /* Flush query ??? */ - - if ((c = read(fd,rbuf,64)) <= 0) { - return c; - } - - return rbuf[0]; -} - - -#ifdef DEBUG - -extern int erts_initialized; -void -erl_assert_error(const char* expr, const char* func, - const char* file, int line) -{ - fflush(stdout); - fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", - file, line, func, expr); - fflush(stderr); - ramlog_printf("%s:%d:%s() Assertion failed: %s\n", - file, line, func, expr); - - abort(); -} - -void -erl_debug(char* fmt, ...) -{ - char sbuf[1024]; /* Temporary buffer. */ - va_list va; - - if (debug_log) { - va_start(va, fmt); - vsprintf(sbuf, fmt, va); - va_end(va); - fprintf(stderr, "%s", sbuf); - } -} - -#endif /* DEBUG */ - -static ERTS_INLINE void -report_exit_status(ErtsSysReportExit *rep, int status) -{ - if (rep->ifd >= 0) { - driver_data[rep->ifd].alive = 0; - driver_data[rep->ifd].status = status; - } - if (rep->ofd >= 0) { - driver_data[rep->ofd].alive = 0; - driver_data[rep->ofd].status = status; - } - - erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep); -} - -#define ERTS_REPORT_EXIT_STATUS report_exit_status - -/* - * Called from schedule() when it runs out of runnable processes, - * or when Erlang code has performed INPUT_REDUCTIONS reduction - * steps. runnable == 0 iff there are no runnable Erlang processes. - */ -void -erl_sys_schedule(int runnable) -{ - ASSERT(get_fsem(current_process()) == 0); -#ifdef ERTS_SMP - ASSERT(erts_get_scheduler_data()->no == 1); - ERTS_CHK_IO(!runnable); -#else - ERTS_CHK_IO( 1 ); -#endif - ASSERT(get_fsem(current_process()) == 0); - ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); -} - - -#ifdef ERTS_SMP - -void -erts_sys_main_thread(void) -{ - erts_thread_disable_fpe(); - - /* Become signal receiver thread... */ -#ifdef ERTS_ENABLE_LOCK_CHECK - erts_lc_set_thread_name("signal_receiver"); -#endif - - while (1) { - static const SIGSELECT sigsel[] = {0}; - union SIGNAL *msg = receive(sigsel); - - fprintf(stderr,"Main thread got message %d from 0x%x!!\r\n", - msg->sig_no, sender(&msg)); - free_buf(&msg); - } -} - -#endif /* ERTS_SMP */ - -void -erl_sys_args(int* argc, char** argv) -{ - int i, j; - - erts_smp_rwmtx_init(&environ_rwmtx, "environ"); - - init_check_io(); - - /* Handled arguments have been marked with NULL. Slide arguments - not handled towards the beginning of argv. */ - for (i = 0, j = 0; i < *argc; i++) { - if (argv[i]) - argv[j++] = argv[i]; - } - *argc = j; - -} diff --git a/erts/emulator/sys/ose/sys_float.c b/erts/emulator/sys/ose/sys_float.c deleted file mode 100644 index 3d9abc6bd1..0000000000 --- a/erts/emulator/sys/ose/sys_float.c +++ /dev/null @@ -1,845 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "sys.h" -#include "global.h" -#include "erl_process.h" - - -#ifdef NO_FPE_SIGNALS - -void -erts_sys_init_float(void) -{ -# ifdef SIGFPE - sys_sigset(SIGFPE, SIG_IGN); /* Ignore so we can test for NaN and Inf */ -# endif -} - -#else /* !NO_FPE_SIGNALS */ - -#ifdef ERTS_SMP -static erts_tsd_key_t fpe_key; - -/* once-only initialisation early in the main thread (via erts_sys_init_float()) */ -static void erts_init_fp_exception(void) -{ - /* XXX: the wrappers prevent using a pthread destructor to - deallocate the key's value; so when/where do we do that? */ - erts_tsd_key_create(&fpe_key); -} - -void erts_thread_init_fp_exception(void) -{ - unsigned long *fpe = erts_alloc(ERTS_ALC_T_FP_EXCEPTION, sizeof(*fpe)); - *fpe = 0L; - erts_tsd_set(fpe_key, fpe); -} - -static ERTS_INLINE volatile unsigned long *erts_thread_get_fp_exception(void) -{ - return (volatile unsigned long*)erts_tsd_get(fpe_key); -} -#else /* !SMP */ -#define erts_init_fp_exception() /*empty*/ -static volatile unsigned long fp_exception; -#define erts_thread_get_fp_exception() (&fp_exception) -#endif /* SMP */ - -volatile unsigned long *erts_get_current_fp_exception(void) -{ - Process *c_p; - - c_p = erts_get_current_process(); - if (c_p) - return &c_p->fp_exception; - return erts_thread_get_fp_exception(); -} - -static void set_current_fp_exception(unsigned long pc) -{ - volatile unsigned long *fpexnp = erts_get_current_fp_exception(); - ASSERT(fpexnp != NULL); - *fpexnp = pc; -} - -void erts_fp_check_init_error(volatile unsigned long *fpexnp) -{ - char buf[64]; - snprintf(buf, sizeof buf, "ERTS_FP_CHECK_INIT at %p: detected unhandled FPE at %p\r\n", - __builtin_return_address(0), (void*)*fpexnp); - if (write(2, buf, strlen(buf)) <= 0) - erl_exit(ERTS_ABORT_EXIT, "%s", buf); - *fpexnp = 0; -#if defined(__i386__) || defined(__x86_64__) - erts_restore_fpu(); -#endif -} - -/* Is there no standard identifier for Darwin/MacOSX ? */ -#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) -#define __DARWIN__ 1 -#endif - -#if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__) - -static void unmask_x87(void) -{ - unsigned short cw; - - __asm__ __volatile__("fstcw %0" : "=m"(cw)); - cw &= ~(0x01|0x04|0x08); /* unmask IM, ZM, OM */ - __asm__ __volatile__("fldcw %0" : : "m"(cw)); -} - -/* mask x87 FPE, return true if the previous state was unmasked */ -static int mask_x87(void) -{ - unsigned short cw; - int unmasked; - - __asm__ __volatile__("fstcw %0" : "=m"(cw)); - unmasked = (cw & (0x01|0x04|0x08)) == 0; - /* or just set cw = 0x37f */ - cw |= (0x01|0x04|0x08); /* mask IM, ZM, OM */ - __asm__ __volatile__("fldcw %0" : : "m"(cw)); - return unmasked; -} - -static void unmask_sse2(void) -{ - unsigned int mxcsr; - - __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr)); - mxcsr &= ~(0x003F|0x0680); /* clear exn flags, unmask OM, ZM, IM (not PM, UM, DM) */ - __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr)); -} - -/* mask SSE2 FPE, return true if the previous state was unmasked */ -static int mask_sse2(void) -{ - unsigned int mxcsr; - int unmasked; - - __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr)); - unmasked = (mxcsr & 0x0680) == 0; - /* or just set mxcsr = 0x1f80 */ - mxcsr &= ~0x003F; /* clear exn flags */ - mxcsr |= 0x0680; /* mask OM, ZM, IM (not PM, UM, DM) */ - __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr)); - return unmasked; -} - -#if defined(__x86_64__) - -static inline int cpu_has_sse2(void) { return 1; } - -#else /* !__x86_64__ */ - -/* - * Check if an x86-32 processor has SSE2. - */ -static unsigned int xor_eflags(unsigned int mask) -{ - unsigned int eax, edx; - - eax = mask; /* eax = mask */ - __asm__("pushfl\n\t" - "popl %0\n\t" /* edx = original EFLAGS */ - "xorl %0, %1\n\t" /* eax = mask ^ EFLAGS */ - "pushl %1\n\t" - "popfl\n\t" /* new EFLAGS = mask ^ original EFLAGS */ - "pushfl\n\t" - "popl %1\n\t" /* eax = new EFLAGS */ - "xorl %0, %1\n\t" /* eax = new EFLAGS ^ old EFLAGS */ - "pushl %0\n\t" - "popfl" /* restore original EFLAGS */ - : "=d"(edx), "=a"(eax) - : "1"(eax)); - return eax; -} - -static __inline__ unsigned int cpuid_eax(unsigned int op) -{ - unsigned int eax, save_ebx; - - /* In PIC mode i386 reserves EBX. So we must save - and restore it ourselves to not upset gcc. */ - __asm__( - "movl %%ebx, %1\n\t" - "cpuid\n\t" - "movl %1, %%ebx" - : "=a"(eax), "=m"(save_ebx) - : "0"(op) - : "cx", "dx"); - return eax; -} - -static __inline__ unsigned int cpuid_edx(unsigned int op) -{ - unsigned int eax, edx, save_ebx; - - /* In PIC mode i386 reserves EBX. So we must save - and restore it ourselves to not upset gcc. */ - __asm__( - "movl %%ebx, %2\n\t" - "cpuid\n\t" - "movl %2, %%ebx" - : "=a"(eax), "=d"(edx), "=m"(save_ebx) - : "0"(op) - : "cx"); - return edx; -} - -/* The AC bit, bit #18, is a new bit introduced in the EFLAGS - * register on the Intel486 processor to generate alignment - * faults. This bit cannot be set on the Intel386 processor. - */ -static __inline__ int is_386(void) -{ - return ((xor_eflags(1<<18) >> 18) & 1) == 0; -} - -/* Newer x86 processors have a CPUID instruction, as indicated by - * the ID bit (#21) in EFLAGS being modifiable. - */ -static __inline__ int has_CPUID(void) -{ - return (xor_eflags(1<<21) >> 21) & 1; -} - -static int cpu_has_sse2(void) -{ - unsigned int maxlev, features; - static int has_sse2 = -1; - - if (has_sse2 >= 0) - return has_sse2; - has_sse2 = 0; - - if (is_386()) - return 0; - if (!has_CPUID()) - return 0; - maxlev = cpuid_eax(0); - /* Intel A-step Pentium had a preliminary version of CPUID. - It also didn't have SSE2. */ - if ((maxlev & 0xFFFFFF00) == 0x0500) - return 0; - /* If max level is zero then CPUID cannot report any features. */ - if (maxlev == 0) - return 0; - features = cpuid_edx(1); - has_sse2 = (features & (1 << 26)) != 0; - - return has_sse2; -} -#endif /* !__x86_64__ */ - -static void unmask_fpe(void) -{ - __asm__ __volatile__("fnclex"); - unmask_x87(); - if (cpu_has_sse2()) - unmask_sse2(); -} - -static void unmask_fpe_conditional(int unmasked) -{ - if (unmasked) - unmask_fpe(); -} - -/* mask x86 FPE, return true if the previous state was unmasked */ -static int mask_fpe(void) -{ - int unmasked; - - unmasked = mask_x87(); - if (cpu_has_sse2()) - unmasked |= mask_sse2(); - return unmasked; -} - -void erts_restore_fpu(void) -{ - __asm__ __volatile__("fninit"); - unmask_x87(); - if (cpu_has_sse2()) - unmask_sse2(); -} - -#elif defined(__sparc__) && defined(__linux__) - -#if defined(__arch64__) -#define LDX "ldx" -#define STX "stx" -#else -#define LDX "ld" -#define STX "st" -#endif - -static void unmask_fpe(void) -{ - unsigned long fsr; - - __asm__(STX " %%fsr, %0" : "=m"(fsr)); - fsr &= ~(0x1FUL << 23); /* clear FSR[TEM] field */ - fsr |= (0x1AUL << 23); /* enable NV, OF, DZ exceptions */ - __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr)); -} - -static void unmask_fpe_conditional(int unmasked) -{ - if (unmasked) - unmask_fpe(); -} - -/* mask SPARC FPE, return true if the previous state was unmasked */ -static int mask_fpe(void) -{ - unsigned long fsr; - int unmasked; - - __asm__(STX " %%fsr, %0" : "=m"(fsr)); - unmasked = ((fsr >> 23) & 0x1A) == 0x1A; - fsr &= ~(0x1FUL << 23); /* clear FSR[TEM] field */ - __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr)); - return unmasked; -} - -#elif (defined(__powerpc__) && defined(__linux__)) || (defined(__ppc__) && defined(__DARWIN__)) - -#if defined(__linux__) -#include - -static void set_fpexc_precise(void) -{ - if (prctl(PR_SET_FPEXC, PR_FP_EXC_PRECISE) < 0) { - perror("PR_SET_FPEXC"); - exit(1); - } -} - -#elif defined(__DARWIN__) - -#include -#include - -/* - * FE0 FE1 MSR bits - * 0 0 floating-point exceptions disabled - * 0 1 floating-point imprecise nonrecoverable - * 1 0 floating-point imprecise recoverable - * 1 1 floating-point precise mode - * - * Apparently: - * - Darwin 5.5 (MacOS X <= 10.1) starts with FE0 == FE1 == 0, - * and resets FE0 and FE1 to 0 after each SIGFPE. - * - Darwin 6.0 (MacOS X 10.2) starts with FE0 == FE1 == 1, - * and does not reset FE0 or FE1 after a SIGFPE. - */ -#define FE0_MASK (1<<11) -#define FE1_MASK (1<<8) - -/* a thread cannot get or set its own MSR bits */ -static void *fpu_fpe_enable(void *arg) -{ - thread_t t = *(thread_t*)arg; - struct ppc_thread_state state; - unsigned int state_size = PPC_THREAD_STATE_COUNT; - - if (thread_get_state(t, PPC_THREAD_STATE, (natural_t*)&state, &state_size) != KERN_SUCCESS) { - perror("thread_get_state"); - exit(1); - } - if ((state.srr1 & (FE1_MASK|FE0_MASK)) != (FE1_MASK|FE0_MASK)) { -#if 1 - /* This would also have to be performed in the SIGFPE handler - to work around the MSR reset older Darwin releases do. */ - state.srr1 |= (FE1_MASK|FE0_MASK); - thread_set_state(t, PPC_THREAD_STATE, (natural_t*)&state, state_size); -#else - fprintf(stderr, "srr1 == 0x%08x, your Darwin is too old\n", state.srr1); - exit(1); -#endif - } - return NULL; /* Ok, we appear to be on Darwin 6.0 or later */ -} - -static void set_fpexc_precise(void) -{ - thread_t self = mach_thread_self(); - pthread_t enabler; - - if (pthread_create(&enabler, NULL, fpu_fpe_enable, &self)) { - perror("pthread_create"); - } else if (pthread_join(enabler, NULL)) { - perror("pthread_join"); - } -} - -#endif - -static void set_fpscr(unsigned int fpscr) -{ - union { - double d; - unsigned int fpscr[2]; - } u; - - u.fpscr[0] = 0xFFF80000; - u.fpscr[1] = fpscr; - __asm__ __volatile__("mtfsf 255,%0" : : "f"(u.d)); -} - -static unsigned int get_fpscr(void) -{ - union { - double d; - unsigned int fpscr[2]; - } u; - - __asm__("mffs %0" : "=f"(u.d)); - return u.fpscr[1]; -} - -static void unmask_fpe(void) -{ - set_fpexc_precise(); - set_fpscr(0x80|0x40|0x10); /* VE, OE, ZE; not UE or XE */ -} - -static void unmask_fpe_conditional(int unmasked) -{ - if (unmasked) - unmask_fpe(); -} - -/* mask PowerPC FPE, return true if the previous state was unmasked */ -static int mask_fpe(void) -{ - int unmasked; - - unmasked = (get_fpscr() & (0x80|0x40|0x10)) == (0x80|0x40|0x10); - set_fpscr(0x00); - return unmasked; -} - -#else - -static void unmask_fpe(void) -{ - fpsetmask(FP_X_INV | FP_X_OFL | FP_X_DZ); -} - -static void unmask_fpe_conditional(int unmasked) -{ - if (unmasked) - unmask_fpe(); -} - -/* mask IEEE FPE, return true if previous state was unmasked */ -static int mask_fpe(void) -{ - const fp_except unmasked_mask = FP_X_INV | FP_X_OFL | FP_X_DZ; - fp_except old_mask; - - old_mask = fpsetmask(0); - return (old_mask & unmasked_mask) == unmasked_mask; -} - -#endif - -#if (defined(__linux__) && (defined(__i386__) || defined(__x86_64__) || defined(__sparc__) || defined(__powerpc__))) || (defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__) || defined(__ppc__))) || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__))) || ((defined(__NetBSD__) || defined(__OpenBSD__)) && defined(__x86_64__)) || (defined(__sun__) && defined(__x86_64__)) - -#if defined(__linux__) && defined(__i386__) -#if !defined(X86_FXSR_MAGIC) -#define X86_FXSR_MAGIC 0x0000 -#endif -#elif defined(__FreeBSD__) && defined(__x86_64__) -#include -#include -#elif defined(__FreeBSD__) && defined(__i386__) -#include -#include -#elif defined(__DARWIN__) -#include -#elif defined(__OpenBSD__) && defined(__x86_64__) -#include -#include -#endif -#if !(defined(__OpenBSD__) && defined(__x86_64__)) -#include -#endif -#include - -#if defined(__linux__) && defined(__x86_64__) -#define mc_pc(mc) ((mc)->gregs[REG_RIP]) -#elif defined(__linux__) && defined(__i386__) -#define mc_pc(mc) ((mc)->gregs[REG_EIP]) -#elif defined(__DARWIN__) && defined(__i386__) -#ifdef DARWIN_MODERN_MCONTEXT -#define mc_pc(mc) ((mc)->__ss.__eip) -#else -#define mc_pc(mc) ((mc)->ss.eip) -#endif -#elif defined(__DARWIN__) && defined(__x86_64__) -#ifdef DARWIN_MODERN_MCONTEXT -#define mc_pc(mc) ((mc)->__ss.__rip) -#else -#define mc_pc(mc) ((mc)->ss.rip) -#endif -#elif defined(__FreeBSD__) && defined(__x86_64__) -#define mc_pc(mc) ((mc)->mc_rip) -#elif defined(__FreeBSD__) && defined(__i386__) -#define mc_pc(mc) ((mc)->mc_eip) -#elif defined(__NetBSD__) && defined(__x86_64__) -#define mc_pc(mc) ((mc)->__gregs[_REG_RIP]) -#elif defined(__NetBSD__) && defined(__i386__) -#define mc_pc(mc) ((mc)->__gregs[_REG_EIP]) -#elif defined(__OpenBSD__) && defined(__x86_64__) -#define mc_pc(mc) ((mc)->sc_rip) -#elif defined(__sun__) && defined(__x86_64__) -#define mc_pc(mc) ((mc)->gregs[REG_RIP]) -#endif - -static void fpe_sig_action(int sig, siginfo_t *si, void *puc) -{ - ucontext_t *uc = puc; - unsigned long pc; - -#if defined(__linux__) -#if defined(__x86_64__) - mcontext_t *mc = &uc->uc_mcontext; - fpregset_t fpstate = mc->fpregs; - pc = mc_pc(mc); - /* A failed SSE2 instruction will restart. To avoid - looping we mask SSE2 exceptions now and unmask them - again later in erts_check_fpe()/erts_restore_fpu(). - On RISCs we update PC to skip the failed instruction, - but the ever increasing complexity of the x86 instruction - set encoding makes that a poor solution here. */ - fpstate->mxcsr = 0x1F80; - fpstate->swd &= ~0xFF; -#elif defined(__i386__) - mcontext_t *mc = &uc->uc_mcontext; - fpregset_t fpstate = mc->fpregs; - pc = mc_pc(mc); - if ((fpstate->status >> 16) == X86_FXSR_MAGIC) - ((struct _fpstate*)fpstate)->mxcsr = 0x1F80; - fpstate->sw &= ~0xFF; -#elif defined(__sparc__) && defined(__arch64__) - /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */ - struct sigcontext *sc = (struct sigcontext*)puc; - pc = sc->sigc_regs.tpc; - sc->sigc_regs.tpc = sc->sigc_regs.tnpc; - sc->sigc_regs.tnpc += 4; -#elif defined(__sparc__) - /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */ - struct sigcontext *sc = (struct sigcontext*)puc; - pc = sc->si_regs.pc; - sc->si_regs.pc = sc->si_regs.npc; - sc->si_regs.npc = (unsigned long)sc->si_regs.npc + 4; -#elif defined(__powerpc__) -#if defined(__powerpc64__) - mcontext_t *mc = &uc->uc_mcontext; - unsigned long *regs = &mc->gp_regs[0]; -#else - mcontext_t *mc = uc->uc_mcontext.uc_regs; - unsigned long *regs = &mc->gregs[0]; -#endif - pc = regs[PT_NIP]; - regs[PT_NIP] += 4; - regs[PT_FPSCR] = 0x80|0x40|0x10; /* VE, OE, ZE; not UE or XE */ -#endif -#elif defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__)) -#ifdef DARWIN_MODERN_MCONTEXT - mcontext_t mc = uc->uc_mcontext; - pc = mc_pc(mc); - mc->__fs.__fpu_mxcsr = 0x1F80; - *(unsigned short *)&mc->__fs.__fpu_fsw &= ~0xFF; -#else - mcontext_t mc = uc->uc_mcontext; - pc = mc_pc(mc); - mc->fs.fpu_mxcsr = 0x1F80; - *(unsigned short *)&mc->fs.fpu_fsw &= ~0xFF; -#endif /* DARWIN_MODERN_MCONTEXT */ -#elif defined(__DARWIN__) && defined(__ppc__) - mcontext_t mc = uc->uc_mcontext; - pc = mc->ss.srr0; - mc->ss.srr0 += 4; - mc->fs.fpscr = 0x80|0x40|0x10; -#elif defined(__FreeBSD__) && defined(__x86_64__) - mcontext_t *mc = &uc->uc_mcontext; - struct savefpu *savefpu = (struct savefpu*)&mc->mc_fpstate; - struct envxmm *envxmm = &savefpu->sv_env; - pc = mc_pc(mc); - envxmm->en_mxcsr = 0x1F80; - envxmm->en_sw &= ~0xFF; -#elif defined(__FreeBSD__) && defined(__i386__) - mcontext_t *mc = &uc->uc_mcontext; - union savefpu *savefpu = (union savefpu*)&mc->mc_fpstate; - pc = mc_pc(mc); - if (mc->mc_fpformat == _MC_FPFMT_XMM) { - struct envxmm *envxmm = &savefpu->sv_xmm.sv_env; - envxmm->en_mxcsr = 0x1F80; - envxmm->en_sw &= ~0xFF; - } else { - struct env87 *env87 = &savefpu->sv_87.sv_env; - env87->en_sw &= ~0xFF; - } -#elif defined(__NetBSD__) && defined(__x86_64__) - mcontext_t *mc = &uc->uc_mcontext; - struct fxsave64 *fxsave = (struct fxsave64 *)&mc->__fpregs; - pc = mc_pc(mc); - fxsave->fx_mxcsr = 0x1F80; - fxsave->fx_fsw &= ~0xFF; -#elif defined(__NetBSD__) && defined(__i386__) - mcontext_t *mc = &uc->uc_mcontext; - pc = mc_pc(mc); - if (uc->uc_flags & _UC_FXSAVE) { - struct envxmm *envxmm = (struct envxmm *)&mc->__fpregs; - envxmm->en_mxcsr = 0x1F80; - envxmm->en_sw &= ~0xFF; - } else { - struct env87 *env87 = (struct env87 *)&mc->__fpregs; - env87->en_sw &= ~0xFF; - } -#elif defined(__OpenBSD__) && defined(__x86_64__) - struct fxsave64 *fxsave = uc->sc_fpstate; - pc = mc_pc(uc); - fxsave->fx_mxcsr = 0x1F80; - fxsave->fx_fsw &= ~0xFF; -#elif defined(__sun__) && defined(__x86_64__) - mcontext_t *mc = &uc->uc_mcontext; - struct fpchip_state *fpstate = &mc->fpregs.fp_reg_set.fpchip_state; - pc = mc_pc(mc); - fpstate->mxcsr = 0x1F80; - fpstate->sw &= ~0xFF; -#endif -#if 0 - { - char buf[64]; - snprintf(buf, sizeof buf, "%s: FPE at %p\r\n", __FUNCTION__, (void*)pc); - write(2, buf, strlen(buf)); - } -#endif - set_current_fp_exception(pc); -} - -static void erts_thread_catch_fp_exceptions(void) -{ - struct sigaction act; - memset(&act, 0, sizeof act); - act.sa_sigaction = fpe_sig_action; - act.sa_flags = SA_SIGINFO; - sigaction(SIGFPE, &act, NULL); - unmask_fpe(); -} - -#else /* !((__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */ - -static void fpe_sig_handler(int sig) -{ - set_current_fp_exception(1); /* XXX: convert to sigaction so we can get the trap PC */ -} - -static void erts_thread_catch_fp_exceptions(void) -{ - sys_sigset(SIGFPE, fpe_sig_handler); - unmask_fpe(); -} - -#endif /* (__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */ - -/* once-only initialisation early in the main thread */ -void erts_sys_init_float(void) -{ - erts_init_fp_exception(); - erts_thread_catch_fp_exceptions(); - erts_printf_block_fpe = erts_sys_block_fpe; - erts_printf_unblock_fpe = erts_sys_unblock_fpe; -} - -#endif /* NO_FPE_SIGNALS */ - -void erts_thread_init_float(void) -{ -#ifdef ERTS_SMP - /* This allows Erlang schedulers to leave Erlang-process context - and still have working FP exceptions. XXX: is this needed? */ - erts_thread_init_fp_exception(); -#endif - -#ifndef NO_FPE_SIGNALS - /* NOTE: - * erts_thread_disable_fpe() is called in all threads at - * creation. We at least need to call unmask_fpe() - */ -#if defined(__DARWIN__) || defined(__FreeBSD__) - /* Darwin (7.9.0) does not appear to propagate FP exception settings - to a new thread from its parent. So if we want FP exceptions, we - must manually re-enable them in each new thread. - FreeBSD 6.1 appears to suffer from a similar issue. */ - erts_thread_catch_fp_exceptions(); -#else - unmask_fpe(); -#endif - -#endif -} - -void erts_thread_disable_fpe(void) -{ -#if !defined(NO_FPE_SIGNALS) - (void)mask_fpe(); -#endif -} - -#if !defined(NO_FPE_SIGNALS) -int erts_sys_block_fpe(void) -{ - return mask_fpe(); -} - -void erts_sys_unblock_fpe(int unmasked) -{ - unmask_fpe_conditional(unmasked); -} -#endif - -/* The following check is incorporated from the Vee machine */ - -#define ISDIGIT(d) ((d) >= '0' && (d) <= '9') - -/* - ** Convert a double to ascii format 0.dddde[+|-]ddd - ** return number of characters converted or -1 if error. - ** - ** These two functions should maybe use localeconv() to pick up - ** the current radix character, but since it is uncertain how - ** expensive such a system call is, and since no-one has heard - ** of other radix characters than '.' and ',' an ad-hoc - ** low execution time solution is used instead. - */ - -int -sys_double_to_chars_ext(double fp, char *buffer, size_t buffer_size, size_t decimals) -{ - char *s = buffer; - - if (erts_snprintf(buffer, buffer_size, "%.*e", decimals, fp) >= buffer_size) - return -1; - /* Search upto decimal point */ - if (*s == '+' || *s == '-') s++; - while (ISDIGIT(*s)) s++; - if (*s == ',') *s++ = '.'; /* Replace ',' with '.' */ - /* Scan to end of string */ - while (*s) s++; - return s-buffer; /* i.e strlen(buffer) */ -} - -/* Float conversion */ - -int -sys_chars_to_double(char* buf, double* fp) -{ -#ifndef NO_FPE_SIGNALS - volatile unsigned long *fpexnp = erts_get_current_fp_exception(); -#endif - char *s = buf, *t, *dp; - - /* Robert says that something like this is what he really wanted: - * (The [.,] radix test is NOT what Robert wanted - it was added later) - * - * 7 == sscanf(Tbuf, "%[+-]%[0-9][.,]%[0-9]%[eE]%[+-]%[0-9]%s", ....); - * if (*s2 == 0 || *s3 == 0 || *s4 == 0 || *s6 == 0 || *s7) - * break; - */ - - /* Scan string to check syntax. */ - if (*s == '+' || *s == '-') s++; - if (!ISDIGIT(*s)) /* Leading digits. */ - return -1; - while (ISDIGIT(*s)) s++; - if (*s != '.' && *s != ',') /* Decimal part. */ - return -1; - dp = s++; /* Remember decimal point pos just in case */ - if (!ISDIGIT(*s)) - return -1; - while (ISDIGIT(*s)) s++; - if (*s == 'e' || *s == 'E') { - /* There is an exponent. */ - s++; - if (*s == '+' || *s == '-') s++; - if (!ISDIGIT(*s)) - return -1; - while (ISDIGIT(*s)) s++; - } - if (*s) /* That should be it */ - return -1; - -#ifdef NO_FPE_SIGNALS - errno = 0; -#endif - __ERTS_FP_CHECK_INIT(fpexnp); - *fp = strtod(buf, &t); - __ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1); - if (t != s) { /* Whole string not scanned */ - /* Try again with other radix char */ - *dp = (*dp == '.') ? ',' : '.'; - errno = 0; - __ERTS_FP_CHECK_INIT(fpexnp); - *fp = strtod(buf, &t); - __ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1); - } - -#ifdef NO_FPE_SIGNALS - if (errno == ERANGE) { - if (*fp == HUGE_VAL || *fp == -HUGE_VAL) { - /* overflow, should give error */ - return -1; - } else if (t == s && *fp == 0.0) { - /* This should give 0.0 - OTP-7178 */ - errno = 0; - - } else if (*fp == 0.0) { - return -1; - } - } -#endif - return 0; -} - -int -matherr(struct exception *exc) -{ -#if !defined(NO_FPE_SIGNALS) - volatile unsigned long *fpexnp = erts_get_current_fp_exception(); - if (fpexnp != NULL) - *fpexnp = (unsigned long)__builtin_return_address(0); -#endif - return 1; -} diff --git a/erts/emulator/sys/ose/sys_time.c b/erts/emulator/sys/ose/sys_time.c deleted file mode 100644 index 5dac75956a..0000000000 --- a/erts/emulator/sys/ose/sys_time.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "sys.h" -#include "global.h" - -/******************* Routines for time measurement *********************/ - -int erts_ticks_per_sec = 0; /* Will be SYS_CLK_TCK in erl_unix_sys.h */ - -int sys_init_time(void) -{ - return SYS_CLOCK_RESOLUTION; -} - -clock_t sys_times(SysTimes *now) { - now->tms_utime = now->tms_stime = now->tms_cutime = now->tms_cstime = 0; - return 0; -} - -static OSTICK last_tick_count = 0; -static SysHrTime wrap = 0; -static OSTICK us_per_tick; - -void sys_init_hrtime() { - us_per_tick = system_tick(); -} - -SysHrTime sys_gethrtime() { - OSTICK ticks = get_ticks(); - if (ticks < (SysHrTime) last_tick_count) { - wrap += 1ULL << 32; - } - last_tick_count = ticks; - return ((((SysHrTime) ticks) + wrap) * 1000*us_per_tick); -} diff --git a/erts/emulator/test/emulator.spec.ose b/erts/emulator/test/emulator.spec.ose deleted file mode 100644 index 9f494609d9..0000000000 --- a/erts/emulator/test/emulator.spec.ose +++ /dev/null @@ -1,2 +0,0 @@ -{topcase, {dir, "../emulator_test"}}. -{skip, {obsolete_SUITE, "Not on ose"}}. diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in index b6e3ba7762..1266be44cb 100644 --- a/erts/epmd/src/Makefile.in +++ b/erts/epmd/src/Makefile.in @@ -19,10 +19,6 @@ # include $(ERL_TOP)/make/target.mk -ifeq ($(findstring ose,$(TARGET)),ose) -include $(ERL_TOP)/make/$(TARGET)/ose_lm.mk -endif - ifeq ($(TYPE),debug) PURIFY = TYPEMARKER = .debug @@ -32,21 +28,13 @@ else ifeq ($(TYPE),purify) PURIFY = purify TYPEMARKER = -ifeq ($(findstring ose,$(TARGET)),ose) - TYPE_FLAGS = -DPURIFY -else - TYPE_FLAGS = -O2 -DPURIFY -endif +TYPE_FLAGS = -O2 -DPURIFY else override TYPE = opt PURIFY = TYPEMARKER = -ifeq ($(findstring ose,$(TARGET)),ose) - TYPE_FLAGS = -else - TYPE_FLAGS = -O2 -endif +TYPE_FLAGS = -O2 endif endif @@ -68,13 +56,9 @@ else ifeq ($(findstring vxworks,$(TARGET)),vxworks) ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ else -ifeq ($(findstring ose,$(TARGET)),ose) -ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ -else ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ -lm endif endif -endif ERTS_LIB = $(ERL_TOP)/erts/lib_src/obj/$(TARGET)/$(TYPE)/MADE @@ -82,11 +66,7 @@ CC = @CC@ WFLAGS = @WFLAGS@ CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) $(WFLAGS) $(ERTS_INCL) LD = @LD@ -ifeq ($(findstring ose,$(TARGET)),ose) -LIBS = $(ERTS_INTERNAL_LIBS) @LIBS@ -else LIBS = @LIBS@ @SYSTEMD_DAEMON_LIBS@ $(ERTS_INTERNAL_LIBS) -endif LDFLAGS = @LDFLAGS@ @@ -135,25 +115,12 @@ clean: rm -f *.o rm -f *~ core -ifeq ($(findstring ose,$(TARGET)),ose) -$(OBJDIR)/ose_confd.o: $(OSE_CONFD) - $(V_CC) $(CFLAGS) -o $@ -c $< -$(OBJDIR)/crt0_lm.o: $(CRT0_LM) - $(V_CC) $(CFLAGS) -o $@ -c $< -OSE_LM_OBJS += $(OBJDIR)/ose_confd.o $(OBJDIR)/crt0_lm.o -endif - # # Objects & executables # -ifeq ($(findstring ose,$(TARGET)),ose) -$(BINDIR)/$(EPMD): $(EPMD_OBJS) $(ERTS_LIB) $(OSE_LM_OBJS) - $(call build-ose-load-module, $@, $(EPMD_OBJS) $(OSE_LM_OBJS), $(LIBS), $(EPMD_LMCONF)) -else $(BINDIR)/$(EPMD): $(EPMD_OBJS) $(ERTS_LIB) $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(EPMD_OBJS) $(LIBS) -endif $(OBJDIR)/%.o: %.c epmd.h epmd_int.h $(V_CC) $(CFLAGS) $(EPMD_FLAGS) -o $@ -c $< diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 132bda725c..7c373509be 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -397,7 +397,7 @@ static void run_daemon(EpmdVars *g) } #endif -#if defined(VXWORKS) || defined(__OSE__) +#if defined(VXWORKS) static void run_daemon(EpmdVars *g) { run(g); diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index 26100afc93..e222abb4b7 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -37,13 +37,6 @@ #define DONT_USE_MAIN #endif -#ifdef __OSE__ -# define NO_DAEMON -# define NO_SYSLOG -# define NO_SYSCONF -# define NO_FCNTL -#endif - /* ************************************************************************ */ /* Standard includes */ @@ -100,12 +93,7 @@ #endif /* ! WIN32 */ #include - -#if !defined(__OSE__) -# include -#endif - - +#include #include #ifdef HAVE_SYSLOG_H @@ -122,10 +110,6 @@ #include -#ifdef __OSE__ -# include "sys/select.h" -#endif - #ifdef HAVE_SYSTEMD_DAEMON # include #endif /* HAVE_SYSTEMD_DAEMON */ diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 8c8d7304f2..829eb4c74d 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -30,11 +30,6 @@ # define INADDR_NONE 0xffffffff #endif -#if defined(__OSE__) -# include "sys/ioctl.h" -# define sleep(x) delay(x*1000) -#endif - /* * * This server is a local name server for Erlang nodes. Erlang nodes can @@ -315,7 +310,7 @@ void run(EpmdVars *g) } #endif /* HAVE_SYSTEMD_DAEMON */ -#if !defined(__WIN32__) && !defined(__OSE__) +#if !defined(__WIN32__) /* We ignore the SIGPIPE signal that is raised when we call write twice on a socket closed by the other end. */ signal(SIGPIPE, SIG_IGN); diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index 8e55fa78c9..05d925f19f 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -21,10 +21,6 @@ include $(ERL_TOP)/make/output.mk include $(ERL_TOP)/make/target.mk -ifeq ($(findstring ose,$(TARGET)),ose) -include $(ERL_TOP)/make/$(TARGET)/ose_lm.mk -endif - ERTS_LIB_TYPEMARKER=.$(TYPE) USING_MINGW=@MIXED_CYGWIN_MINGW@ @@ -85,18 +81,13 @@ EMUOSDIR = $(ERL_TOP)/erts/emulator/@ERLANG_OSTYPE@ SYSDIR = $(ERL_TOP)/erts/emulator/sys/@ERLANG_OSTYPE@ DRVDIR = $(ERL_TOP)/erts/emulator/drivers/@ERLANG_OSTYPE@ UXETC = ../unix -OSEETC = ../ose WINETC = ../win32 ifeq ($(TARGET), win32) ETC = $(WINETC) else -ifeq ($(findstring ose,$(TARGET)),ose) -ETC = $(OSEETC) -else ETC = $(UXETC) endif -endif ifeq ($(TARGET), win32) ERLEXEC = erlexec.dll @@ -180,25 +171,6 @@ PORT_ENTRY_POINT=erl_port_entry ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT) else -ifeq ($(findstring ose,$(TARGET)),ose) -ENTRY_LDFLAGS= -ENTRY_OBJ= -ERLSRV_OBJECTS= -MC_OUTPUTS= -INET_GETHOST = -INSTALL_EMBEDDED_PROGS = $(BINDIR)/run_erl_lm -INSTALL_EMBEDDED_DATA = -INSTALL_TOP = Install -INSTALL_TOP_BIN = -INSTALL_MISC = -INSTALL_SRC = -ERLEXECDIR = . -INSTALL_LIBS = -INSTALL_OBJS = -INSTALL_INCLUDES = -TEXTFILES = Install erl.src -INSTALL_PROGS = $(INSTALL_EMBEDDED_PROGS) -else # UNIX (!win32 && !ose) ENTRY_LDFLAGS= ENTRY_OBJ= ERLSRV_OBJECTS= @@ -223,7 +195,6 @@ INSTALL_PROGS = \ $(BINDIR)/$(ERLEXEC) \ $(INSTALL_EMBEDDED_PROGS) endif -endif .PHONY: etc etc: $(ENTRY_OBJ) $(INSTALL_PROGS) $(INSTALL_LIBS) $(TEXTFILES) $(INSTALL_TOP_BIN) @@ -269,8 +240,8 @@ endif rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/to_erl.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/dyn_erl.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/safe_string.o - rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/run_erl_common.o - rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/to_erl_common.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/run_erl.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/to_erl.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/typer.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/ct_run.o rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/vxcall.o @@ -423,28 +394,24 @@ $(BINDIR)/inet_gethost@EXEEXT@: $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(ERTS_LIB $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(LIBS) $(ERTS_INTERNAL_LIBS) # run_erl -$(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(OBJDIR)/run_erl_common.o - $(V_LD) $(LDFLAGS) -o $@ $^ $(LIBS) -$(OBJDIR)/run_erl.o: $(ETC)/run_erl.c ../common/run_erl_common.h $(RC_GENERATED) - $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c $(ETC)/run_erl.c -$(OBJDIR)/run_erl_common.o: ../common/run_erl_common.c ../common/run_erl_common.h $(RC_GENERATED) - $(V_CC) $(CFLAGS) -o $@ -c $< +$(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o + $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(LIBS) +$(OBJDIR)/run_erl.o: $(ETC)/run_erl.c $(RC_GENERATED) + $(V_CC) $(CFLAGS) -o $@ -c $(ETC)/run_erl.c # to_erl -$(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o $(OBJDIR)/to_erl_common.o - $(V_LD) $(LDFLAGS) -o $@ $^ -$(OBJDIR)/to_erl.o: $(ETC)/to_erl.c ../common/safe_string.h $(RC_GENERATED) - $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c $(ETC)/to_erl.c -$(OBJDIR)/to_erl_common.o: ../common/to_erl_common.c ../common/to_erl_common.h $(RC_GENERATED) - $(V_CC) $(CFLAGS) -o $@ -c $< +$(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o + $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o +$(OBJDIR)/to_erl.o: $(ETC)/to_erl.c $(RC_GENERATED) + $(V_CC) $(CFLAGS) -o $@ -c $(ETC)/to_erl.c # dyn_erl $(BINDIR)/dyn_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o $(OBJDIR)/dyn_erl.o: $(UXETC)/dyn_erl.c $(RC_GENERATED) $(V_CC) $(CFLAGS) -o $@ -c $(UXETC)/dyn_erl.c -$(OBJDIR)/safe_string.o: ../common/safe_string.c $(RC_GENERATED) - $(V_CC) $(CFLAGS) -o $@ -c ../common/safe_string.c +$(OBJDIR)/safe_string.o: $(ETC)/safe_string.c $(RC_GENERATED) + $(V_CC) $(CFLAGS) -o $@ -c $(ETC)/safe_string.c ifneq ($(TARGET),win32) $(BINDIR)/$(ERLEXEC): $(OBJDIR)/$(ERLEXEC).o $(ERTS_LIB) @@ -499,30 +466,6 @@ erl.src: $(UXETC)/erl.src.src ../../vsn.mk $(TARGET)/Makefile -e 's;%VSN%;$(VSN);' \ $(UXETC)/erl.src.src > erl.src -#--------------------------------------------------------- -# OSE specific targets -#--------------------------------------------------------- -ifeq ($(findstring ose,$(TARGET)),ose) -$(OBJDIR)/ose_confd.o: $(OSE_CONFD) - $(V_CC) $(CFLAGS) -o $@ -c $< -$(OBJDIR)/crt0_lm.o: $(CRT0_LM) - $(V_CC) $(CFLAGS) -o $@ -c $< -OSE_LM_OBJS += $(OBJDIR)/ose_confd.o $(OBJDIR)/crt0_lm.o - -$(BINDIR)/run_erl_lm: $(OBJDIR)/run_erl_main.o $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(OBJDIR)/run_erl_common.o $(OBJDIR)/to_erl_common.o $(OSE_LM_OBJS) - $(call build-ose-load-module, $@, $^, $(LIBS), $(RUN_ERL_LMCONF)) - - -$(OBJDIR)/run_erl_main.o: $(OSEETC)/run_erl_main.c $(OSEETC)/run_erl.h ../common/to_erl_common.h $(RC_GENERATED) - $(V_CC) $(CFLAGS) -I ../common/ -o $@ -c $(OSEETC)/run_erl_main.c - -endif - -#--------------------------------------------------------- -# End of ose specific targets. -#--------------------------------------------------------- - - # ---------------------------------------------------- # Release Target # ---------------------------------------------------- @@ -537,11 +480,9 @@ endif $(INSTALL_DIR) "$(RELEASE_PATH)/erts-$(VSN)/bin" ifneq ($(TARGET), win32) ifneq ($(findstring vxworks,$(TARGET)), vxworks) -ifneq ($(findstring ose,$(TARGET)), ose) $(INSTALL_SCRIPT) erl.src "$(RELEASE_PATH)/erts-$(VSN)/bin" endif endif -endif ifneq ($(INSTALL_PROGS),) $(INSTALL_PROGRAM) $(INSTALL_PROGS) "$(RELEASE_PATH)/erts-$(VSN)/bin" endif diff --git a/erts/etc/common/run_erl_common.c b/erts/etc/common/run_erl_common.c deleted file mode 100644 index c03d5e3ae2..0000000000 --- a/erts/etc/common/run_erl_common.c +++ /dev/null @@ -1,696 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2014. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __ANDROID__ -# include -#endif - -#ifdef HAVE_SYSLOG_H -# include -#endif - -#ifdef HAVE_SYS_IOCTL_H -# include -#endif - -#ifdef __OSE__ -# include "ramlog.h" -#endif - -#include "run_erl_common.h" -#include "safe_string.h" - -#define DEFAULT_LOG_GENERATIONS 5 -#define LOG_MAX_GENERATIONS 1000 /* No more than 1000 log files */ -#define LOG_MIN_GENERATIONS 2 /* At least two to switch between */ -#define DEFAULT_LOG_MAXSIZE 100000 -#define LOG_MIN_MAXSIZE 1000 /* Smallast value for changing log file */ -#define LOG_STUBNAME "erlang.log." -#define LOG_PERM 0664 -#define DEFAULT_LOG_ACTIVITY_MINUTES 5 -#define DEFAULT_LOG_ALIVE_MINUTES 15 -#define DEFAULT_LOG_ALIVE_FORMAT "%a %b %e %T %Z %Y" -#define ALIVE_BUFFSIZ 1024 - -#define STATUSFILENAME "/run_erl.log" - -#define PIPE_STUBNAME "erlang.pipe" -#define PIPE_STUBLEN strlen(PIPE_STUBNAME) -#define PERM (S_IWUSR | S_IRUSR | S_IWOTH | S_IROTH | S_IWGRP | S_IRGRP) - -/* OSE has defined O_SYNC but it is not recognized by open */ -#if !defined(O_SYNC) || defined(__OSE__) -#undef O_SYNC -#define O_SYNC 0 -#define USE_FSYNC 1 -#endif - -/* Global variable definitions - * We need this complex way of handling global variables because of how - * OSE works here. We want to make it possible to run the shell command - * run_erl multiple times with different global variables without them - * effecting eachother. - */ - -#define STATUSFILE (RE_DATA->statusfile) -#define LOG_DIR (RE_DATA->log_dir) -#define STDSTATUS (RE_DATA->stdstatus) -#define LOG_GENERATIONS (RE_DATA->log_generations) -#define LOG_MAXSIZE (RE_DATA->log_maxsize) -#define LOG_ACTIVITY_MINUTES (RE_DATA->log_activity_minutes) -#define LOG_ALIVE_IN_GMT (RE_DATA->log_alive_in_gmt) -#define LOG_ALIVE_FORMAT (RE_DATA->log_alive_format) -#define RUN_DAEMON (RE_DATA->run_daemon) -#define LOG_ALIVE_MINUTES (RE_DATA->log_alive_minutes) -#define LOG_NUM (RE_DATA->log_num) -#define LFD (RE_DATA->lfd) -#define PROTOCOL_VER (RE_DATA->protocol_ver) - -struct run_erl_ { - /* constant config data */ - char statusfile[FILENAME_BUFSIZ]; - char log_dir[FILENAME_BUFSIZ]; - FILE *stdstatus; - int log_generations; - int log_maxsize; - int log_activity_minutes; - int log_alive_in_gmt; - char log_alive_format[ALIVE_BUFFSIZ+1]; - int run_daemon; - int log_alive_minutes; - /* Current log number and log fd */ - int log_num; - int lfd; - unsigned protocol_ver; -}; - -typedef struct run_erl_ run_erl; - -#ifdef __OSE__ -static OSPPDKEY run_erl_pp_key; -#define RE_DATA (*(run_erl**)ose_get_ppdata(run_erl_pp_key)) -#else -static run_erl re; -#define RE_DATA (&re) -#endif - -/* prototypes */ - -static int next_log(int log_num); -static int prev_log(int log_num); -static int find_next_log_num(void); -static int open_log(int log_num, int flags); - -/* - * getenv_int: - */ -static char *getenv_int(const char *name) { -#ifdef __OSE__ - return get_env(get_bid(current_process()),name); -#else - return getenv(name); -#endif -} - -/* - * next_log: - * Returns the index number that follows the given index number. - * (Wrapping after log_generations) - */ -static int next_log(int log_num) { - return log_num>=LOG_GENERATIONS?1:log_num+1; -} - -/* - * prev_log: - * Returns the index number that precedes the given index number. - * (Wrapping after log_generations) - */ -static int prev_log(int log_num) { - return log_num<=1?LOG_GENERATIONS:log_num-1; -} - -/* - * find_next_log_num() - * Searches through the log directory to check which logs that already - * exist. It finds the "hole" in the sequence, and returns the index - * number for the last log in the log sequence. If there is no hole, index - * 1 is returned. - */ -static int find_next_log_num(void) { - int i, next_gen, log_gen; - DIR *dirp; - struct dirent *direntp; - int log_exists[LOG_MAX_GENERATIONS+1]; - int stub_len = strlen(LOG_STUBNAME); - - /* Initialize exiting log table */ - - for(i=LOG_GENERATIONS; i>=0; i--) - log_exists[i] = 0; - dirp = opendir(LOG_DIR); - if(!dirp) { - ERRNO_ERR1(LOG_ERR,"Can't access log directory '%s'", LOG_DIR); - exit(1); - } - - /* Check the directory for existing logs */ - - while((direntp=readdir(dirp)) != NULL) { - if(strncmp(direntp->d_name,LOG_STUBNAME,stub_len)==0) { - int num = atoi(direntp->d_name+stub_len); - if(num < 1 || num > LOG_GENERATIONS) - continue; - log_exists[num] = 1; - } - } - closedir(dirp); - - /* Find out the next available log file number */ - - next_gen = 0; - for(i=LOG_GENERATIONS; i>=0; i--) { - if(log_exists[i]) - if(next_gen) - break; - else - ; - else - next_gen = i; - } - - /* Find out the current log file number */ - - if(next_gen) - log_gen = prev_log(next_gen); - else - log_gen = 1; - - return log_gen; -} /* find_next_log_num() */ - -static int open_log(int log_num, int flags) -{ - char buf[FILENAME_MAX]; - time_t now; - struct tm *tmptr; - char log_buffer[ALIVE_BUFFSIZ+1]; - - /* Remove the next log (to keep a "hole" in the log sequence) */ - sn_printf(buf, sizeof(buf), "%s/%s%d", - LOG_DIR, LOG_STUBNAME, next_log(log_num)); - unlink(buf); - - /* Create or continue on the current log file */ - sn_printf(buf, sizeof(buf), "%s/%s%d", LOG_DIR, LOG_STUBNAME, log_num); - - LFD = sf_open(buf, flags, LOG_PERM); - - if(LFD <0){ - ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf); - exit(1); - } - - /* Write a LOGGING STARTED and time stamp into the log file */ - time(&now); - if (LOG_ALIVE_IN_GMT) { - tmptr = gmtime(&now); - } else { - tmptr = localtime(&now); - } - if (!strftime(log_buffer, ALIVE_BUFFSIZ, LOG_ALIVE_FORMAT, - tmptr)) { - strn_cpy(log_buffer, sizeof(log_buffer), - "(could not format time in 256 positions " - "with current format string.)"); - } - log_buffer[ALIVE_BUFFSIZ] = '\0'; - - sn_printf(buf, sizeof(buf), "\n=====\n===== LOGGING STARTED %s\n=====\n", - log_buffer); - if (erts_run_erl_write_all(LFD, buf, strlen(buf)) < 0) - erts_run_erl_log_status("Error in writing to log.\n"); - -#if USE_FSYNC - fsync(LFD); -#endif - - return LFD; -} - -/* Instead of making sure basename exists, we do our own */ -char *simple_basename(char *path) -{ - char *ptr; - for (ptr = path; *ptr != '\0'; ++ptr) { - if (*ptr == '/') { - path = ptr + 1; - } - } - return path; -} - -ssize_t sf_read(int fd, void *buffer, size_t len) { - ssize_t n = 0; - - do { n = read(fd, buffer, len); } while (n < 0 && errno == EINTR); - - return n; -} - -ssize_t sf_write(int fd, const void *buffer, size_t len) { - ssize_t n = 0; - - do { n = write(fd, buffer, len); } while (n < 0 && errno == EINTR); - - return n; -} - -int sf_open(const char *path, int type, mode_t mode) { - int fd = 0; - - do { fd = open(path, type, mode); } while(fd < 0 && errno == EINTR); - - return fd; -} - -int sf_close(int fd) { - int res = 0; - - do { res = close(fd); } while(res < 0 && errno == EINTR); - - return res; -} - -/* Call write() until entire buffer has been written or error. - * Return len or -1. - */ -int erts_run_erl_write_all(int fd, const char* buf, int len) -{ - int left = len; - int written; - for (;;) { - do { - written = write(fd,buf,left); - } while (written < 0 && errno == EINTR); - if (written == left) { - return len; - } - if (written < 0) { - return -1; - } - left -= written; - buf += written; - } - return written; -} - -/* erts_run_erl_log_status() - * Prints the arguments to a status file - * Works like printf (see vfrpintf) - */ -void erts_run_erl_log_status(const char *format,...) -{ - va_list args; - time_t now; - - if (STDSTATUS == NULL) - STDSTATUS = fopen(STATUSFILE, "w"); - if (STDSTATUS == NULL) - return; - now = time(NULL); - fprintf(STDSTATUS, "run_erl [%d] %s", -#ifdef __OSE__ - (int)current_process(), -#else - (int)getpid(), -#endif - ctime(&now)); - va_start(args, format); - vfprintf(STDSTATUS, format, args); - va_end(args); - fflush(STDSTATUS); - return; -} - -/* Fetch the current log alive minutes */ -int erts_run_erl_log_alive_minutes() { - return LOG_ALIVE_MINUTES; -} - -/* error_logf() - * Prints the arguments to stderr or syslog - * Works like printf (see vfprintf) - */ -void erts_run_erl_log_error(int priority, int line, const char *format, ...) -{ - va_list args; - va_start(args, format); - -#ifdef HAVE_SYSLOG_H - if (RUN_DAEMON) { - vsyslog(priority,format,args); - } - else -#endif -#ifdef __OSE__ - if (RUN_DAEMON) { - char *buff = malloc(sizeof(char)*1024); - vsnprintf(buff,1024,format, args); - ramlog_printf(buff); - } - else -#endif - { - time_t now = time(NULL); - fprintf(stderr, "run_erl:%d [%d] %s", line, -#ifdef __OSE__ - (int)current_process(), -#else - (int)getpid(), -#endif - ctime(&now)); - vfprintf(stderr, format, args); - } - va_end(args); -} - -/* erts_run_erl_log_write() - * Writes a message to lfd. If the current log file is full, - * a new log file is opened. - */ -int erts_run_erl_log_write(char* buf, size_t len) -{ - int size; - ssize_t res; - /* Decide if new logfile needed, and open if so */ - - size = lseek(LFD,0,SEEK_END); - if(size+len > LOG_MAXSIZE) { - int res; - do { - res = close(LFD); - } while (res < 0 && errno == EINTR); - LOG_NUM = next_log(LOG_NUM); - LFD = open_log(LOG_NUM, O_RDWR|O_CREAT|O_TRUNC|O_SYNC); - } - - /* Write to log file */ - - if ((res = erts_run_erl_write_all(LFD, buf, len)) < 0) { - erts_run_erl_log_status("Error in writing to log.\n"); - } - -#if USE_FSYNC - fsync(LFD); -#endif - return res; -} - -int erts_run_erl_log_activity(int timeout,time_t now,time_t last_activity) { - char log_alive_buffer[ALIVE_BUFFSIZ+1]; - char buf[BUFSIZ]; - - if (timeout || now - last_activity > LOG_ACTIVITY_MINUTES*60) { - /* Either a time out: 15 minutes without action, */ - /* or something is coming in right now, but it's a long time */ - /* since last time, so let's write a time stamp this message */ - struct tm *tmptr; - if (LOG_ALIVE_IN_GMT) { - tmptr = gmtime(&now); - } else { - tmptr = localtime(&now); - } - if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, LOG_ALIVE_FORMAT, - tmptr)) { - strn_cpy(log_alive_buffer, sizeof(log_alive_buffer), - "(could not format time in 256 positions " - "with current format string.)"); - } - log_alive_buffer[ALIVE_BUFFSIZ] = '\0'; - - sn_printf(buf, sizeof(buf), "\n===== %s%s\n", - timeout?"ALIVE ":"", log_alive_buffer); - return erts_run_erl_log_write(buf, strlen(buf)); - } - return 0; -} - -int erts_run_erl_log_open() { - - LOG_NUM = find_next_log_num(); - LFD = open_log(LOG_NUM, O_RDWR|O_APPEND|O_CREAT|O_SYNC); - return 0; -} - -int erts_run_erl_log_init(int daemon, char* logdir) { - char *p; - -#ifdef __OSE__ - run_erl **re_pp; - if (!run_erl_pp_key) - ose_create_ppdata("run_erl_ppdata",&run_erl_pp_key); - re_pp = (run_erl **)ose_get_ppdata(run_erl_pp_key); - *re_pp = malloc(sizeof(run_erl)); -#endif - - STDSTATUS = NULL; - LOG_GENERATIONS = DEFAULT_LOG_GENERATIONS; - LOG_MAXSIZE = DEFAULT_LOG_MAXSIZE; - LOG_ACTIVITY_MINUTES = DEFAULT_LOG_ACTIVITY_MINUTES; - LOG_ALIVE_IN_GMT = 0; - RUN_DAEMON = 0; - LOG_ALIVE_MINUTES = DEFAULT_LOG_ALIVE_MINUTES; - LFD = 0; - PROTOCOL_VER = RUN_ERL_LO_VER; /* assume lowest to begin with */ - - /* Get values for LOG file handling from the environment */ - if ((p = getenv_int("RUN_ERL_LOG_ALIVE_MINUTES"))) { - LOG_ALIVE_MINUTES = atoi(p); - if (!LOG_ALIVE_MINUTES) { - ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ALIVE_MINUTES is 1 " - "(current value is %s)",p); - } - LOG_ACTIVITY_MINUTES = LOG_ALIVE_MINUTES / 3; - if (!LOG_ACTIVITY_MINUTES) { - ++LOG_ACTIVITY_MINUTES; - } - } - if ((p = getenv_int( - "RUN_ERL_LOG_ACTIVITY_MINUTES"))) { - LOG_ACTIVITY_MINUTES = atoi(p); - if (!LOG_ACTIVITY_MINUTES) { - ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ACTIVITY_MINUTES is 1 " - "(current value is %s)",p); - } - } - if ((p = getenv_int("RUN_ERL_LOG_ALIVE_FORMAT"))) { - if (strlen(p) > ALIVE_BUFFSIZ) { - ERROR1(LOG_ERR, "RUN_ERL_LOG_ALIVE_FORMAT can contain a maximum of " - "%d characters", ALIVE_BUFFSIZ); - } - strn_cpy(LOG_ALIVE_FORMAT, sizeof(LOG_ALIVE_FORMAT), p); - } else { - strn_cpy(LOG_ALIVE_FORMAT, sizeof(LOG_ALIVE_FORMAT), - DEFAULT_LOG_ALIVE_FORMAT); - } - if ((p = getenv_int("RUN_ERL_LOG_ALIVE_IN_UTC")) - && strcmp(p,"0")) { - ++LOG_ALIVE_IN_GMT; - } - if ((p = getenv_int("RUN_ERL_LOG_GENERATIONS"))) { - LOG_GENERATIONS = atoi(p); - if (LOG_GENERATIONS < LOG_MIN_GENERATIONS) - ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_GENERATIONS is %d", - LOG_MIN_GENERATIONS); - if (LOG_GENERATIONS > LOG_MAX_GENERATIONS) - ERROR1(LOG_ERR,"Maximum RUN_ERL_LOG_GENERATIONS is %d", - LOG_MAX_GENERATIONS); - } - - if ((p = getenv_int("RUN_ERL_LOG_MAXSIZE"))) { - LOG_MAXSIZE = atoi(p); - if (LOG_MAXSIZE < LOG_MIN_MAXSIZE) - ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_MAXSIZE is %d", LOG_MIN_MAXSIZE); - } - - RUN_DAEMON = daemon; - - strn_cpy(LOG_DIR, sizeof(LOG_DIR), logdir); - strn_cpy(STATUSFILE, sizeof(STATUSFILE), LOG_DIR); - strn_cat(STATUSFILE, sizeof(STATUSFILE), STATUSFILENAME); - - return 0; -} - -/* create_fifo() - * Creates a new fifo with the given name and permission. - */ -static int create_fifo(char *name, int perm) -{ - if ((mkfifo(name, perm) < 0) && (errno != EEXIST)) - return -1; - return 0; -} - -/* - * w- and r_pipename have to be pre-allocated of atleast FILENAME_MAX size - */ -int erts_run_erl_open_fifo(char *pipename,char *w_pipename,char *r_pipename) { - int calculated_pipename = 0; - int highest_pipe_num = 0; - int fd; - - /* - * Create FIFOs and open them - */ - - if(*pipename && pipename[strlen(pipename)-1] == '/') { - /* The user wishes us to find a unique pipe name in the specified */ - /* directory */ - DIR *dirp; - struct dirent *direntp; - - calculated_pipename = 1; - dirp = opendir(pipename); - if(!dirp) { - ERRNO_ERR1(LOG_ERR,"Can't access pipe directory '%s'.", pipename); - return 1; - } - - /* Check the directory for existing pipes */ - - while((direntp=readdir(dirp)) != NULL) { - if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) { - int num = atoi(direntp->d_name+PIPE_STUBLEN+1); - if(num > highest_pipe_num) - highest_pipe_num = num; - } - } - closedir(dirp); - strn_catf(pipename, BUFSIZ, "%s.%d", - PIPE_STUBNAME, highest_pipe_num+1); - } /* if */ - - for(;;) { - /* write FIFO - is read FIFO for `to_erl' program */ - strn_cpy(w_pipename, BUFSIZ, pipename); - strn_cat(w_pipename, BUFSIZ, ".r"); - if (create_fifo(w_pipename, PERM) < 0) { - ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for writing.", - w_pipename); - return 1; - } - - /* read FIFO - is write FIFO for `to_erl' program */ - strn_cpy(r_pipename, BUFSIZ, pipename); - strn_cat(r_pipename, BUFSIZ, ".w"); - - /* Check that nobody is running run_erl already */ - if ((fd = sf_open(r_pipename, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { - /* Open as client succeeded -- run_erl is already running! */ - sf_close(fd); - if (calculated_pipename) { - ++highest_pipe_num; - strn_catf(pipename, BUFSIZ, "%s.%d", - PIPE_STUBNAME, highest_pipe_num+1); - continue; - } - ERROR1(LOG_ERR, "Erlang already running on pipe %s.\n", pipename); - unlink(w_pipename); - return 1; - } - if (create_fifo(r_pipename, PERM) < 0) { - unlink(w_pipename); - ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for reading.", - r_pipename); - return 1; - } - break; - } - return 0; -} - -/* Extract any control sequences that are ment only for run_erl - * and should not be forwarded to the pty. - */ -int erts_run_erl_extract_ctrl_seq(char* buf, int len, int mfd) -{ - static const char prefix[] = "\033_"; - static const char suffix[] = "\033\\"; - char* bufend = buf + len; - char* start = buf; - char* command; - char* end; - - for (;;) { - start = find_str(start, bufend-start, prefix); - if (!start) break; - - command = start + strlen(prefix); - end = find_str(command, bufend-command, suffix); - if (end) { - unsigned col, row; - if (sscanf(command,"version=%u", &PROTOCOL_VER)==1) { - /*fprintf(stderr,"to_erl v%u\n", protocol_ver);*/ - } - else if (sscanf(command,"winsize=%u,%u", &col, &row)==2) { -#ifdef TIOCSWINSZ - struct winsize ws; - ws.ws_col = col; - ws.ws_row = row; - if (ioctl(mfd, TIOCSWINSZ, &ws) < 0) { - ERRNO_ERR0(LOG_ERR,"Failed to set window size"); - } -#endif - } - else { - ERROR2(LOG_ERR, "Ignoring unknown ctrl command '%.*s'\n", - (int)(end-command), command); - } - - /* Remove ctrl sequence from buf */ - end += strlen(suffix); - memmove(start, end, bufend-end); - bufend -= end - start; - } - else { - ERROR2(LOG_ERR, "Missing suffix in ctrl sequence '%.*s'\n", - (int)(bufend-start), start); - break; - } - } - return bufend - buf; -} diff --git a/erts/etc/common/run_erl_common.h b/erts/etc/common/run_erl_common.h deleted file mode 100644 index cecf7521f9..0000000000 --- a/erts/etc/common/run_erl_common.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2013. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -/* - * Functions that are common to both OSE and unix implementations of run_erl - */ -#ifndef ERL_RUN_ERL_LOG_H -#define ERL_RUN_ERL_LOG_H - -#include -#include -#include - -#include "run_erl_vsn.h" - -/* Log handling */ -int erts_run_erl_log_init(int run_daemon, char* logdir); -int erts_run_erl_log_open(void); -int erts_run_erl_log_close(void); -int erts_run_erl_log_write(char *buff, size_t len); -int erts_run_erl_log_activity(int timeout, time_t now, time_t last_activity); - -void erts_run_erl_log_status(const char *format,...); -void erts_run_erl_log_error(int priority, int line, const char *format,...); - -int erts_run_erl_open_fifo(char *pipename,char *w_pipename,char *r_pipename); -int erts_run_erl_log_alive_minutes(void); -int erts_run_erl_extract_ctrl_seq(char* buf, int len, int mfd); - -/* File operations */ -ssize_t sf_read(int fd, void *buffer, size_t len); -ssize_t sf_write(int fd, const void *buffer, size_t len); -int sf_open(const char *path, int type, mode_t mode); -int sf_close(int fd); -int erts_run_erl_write_all(int fd, const char* buf, int len); -char *simple_basename(char *path); - -#ifndef LOG_ERR -#ifdef __OSE__ -#define LOG_ERR 0 -#else -#define LOG_ERR NULL -#endif -#endif - -#define ERROR0(Prio,Format) erts_run_erl_log_error(Prio,__LINE__,Format"\n") -#define ERROR1(Prio,Format,A1) erts_run_erl_log_error(Prio,__LINE__,Format"\n",A1) -#define ERROR2(Prio,Format,A1,A2) erts_run_erl_log_error(Prio,__LINE__,Format"\n",A1,A2) - -#ifdef HAVE_STRERROR -# define ADD_ERRNO(Format) "errno=%d '%s'\n"Format"\n",errno,strerror(errno) -#else -# define ADD_ERRNO(Format) "errno=%d\n"Format"\n",errno -#endif -#define ERRNO_ERR0(Prio,Format) erts_run_erl_log_error(Prio,__LINE__,ADD_ERRNO(Format)) -#define ERRNO_ERR1(Prio,Format,A1) erts_run_erl_log_error(Prio,__LINE__,ADD_ERRNO(Format),A1) -#define ERRNO_ERR2(Prio,Format,A1,A2) erts_run_erl_log_error(Prio,__LINE__,ADD_ERRNO(Format),A1,A2) - -#define RUN_ERL_USAGE \ - "%s (pipe_name|pipe_dir/) log_dir \"command [parameters ...]\"" \ - "\n\nDESCRIPTION:\n" \ - "You may also set the environment variables RUN_ERL_LOG_GENERATIONS\n" \ - "and RUN_ERL_LOG_MAXSIZE to the number of log files to use and the\n" \ - "size of the log file when to switch to the next log file\n" - -#ifndef FILENAME_MAX -#define FILENAME_MAX 250 -#endif - -#define FILENAME_BUFSIZ FILENAME_MAX - -#ifdef O_NONBLOCK -# define DONT_BLOCK_PLEASE O_NONBLOCK -#else -# define DONT_BLOCK_PLEASE O_NDELAY -# ifndef EAGAIN -# define EAGAIN -3898734 -# endif -#endif - -#endif diff --git a/erts/etc/common/run_erl_vsn.h b/erts/etc/common/run_erl_vsn.h deleted file mode 100644 index 2c3e67e81c..0000000000 --- a/erts/etc/common/run_erl_vsn.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2008-2009. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -/* - * The protocol version number used between to_erl and run_erl. - */ -#define RUN_ERL_HI_VER 1 /* My preferred protocol version */ -#define RUN_ERL_LO_VER 0 /* The lowest version I accept to talk with */ - -/* Version history: - * 0: Older, without version handshake - * 1: R12B-3, version handshake + window size ctrl - */ diff --git a/erts/etc/common/safe_string.c b/erts/etc/common/safe_string.c deleted file mode 100644 index cdcdbf16f0..0000000000 --- a/erts/etc/common/safe_string.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2008-2009. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -/* - * Module: safe_string.c - * - * This is a bunch of generic string operation - * that are safe regarding buffer overflow. - * - * All string functions terminate the process with an error message - * on buffer overflow. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include "safe_string.h" -#include -#include -#include -#include - - -static void string_overflow_handler(const char* format, ...) -{ - va_list args; - va_start(args, format); - vfprintf(stderr,format,args); - va_end(args); - exit(1); -} - -int vsn_printf(char* dst, size_t size, const char* format, va_list args) -{ - int ret = vsnprintf(dst, size, format, args); - if (ret >= size || ret < 0) { - string_overflow_handler("Buffer truncated '%s'\n",dst); - } - return ret; -} - -int sn_printf(char* dst, size_t size, const char* format, ...) -{ - va_list args; - int ret; - va_start(args, format); - ret = vsn_printf(dst,size,format,args); - va_end(args); - return ret; -} - -int strn_cpy(char* dst, size_t size, const char* src) -{ - return sn_printf(dst,size,"%s",src); -} - -int strn_cat(char* dst, size_t size, const char* src) -{ - return strn_catf(dst,size,"%s",src); -} - -int strn_catf(char* dst, size_t size, const char* format, ...) -{ - int ret; - va_list args; -#ifdef _GNU_SOURCE - int len = strnlen(dst,size); -#else - int len = strlen(dst); -#endif - - if (len >= size) { - string_overflow_handler("Buffer already overflowed '%.*s'\n", - size, dst); - } - va_start(args, format); - ret = vsn_printf(dst+len, size-len, format, args); - va_end(args); - return len+ret; -} - -char* find_str(const char* haystack, int hsize, const char* needle) -{ - int i = 0; - int nsize = strlen(needle); - hsize -= nsize - 1; - for (i=0; i dest) { - for (i=0; i=0; i--) ((char*)dest)[i] = ((char*)src)[i]; - } - return dest; -} -#endif /* HAVE_MEMMOVE */ diff --git a/erts/etc/common/safe_string.h b/erts/etc/common/safe_string.h deleted file mode 100644 index f9d2b2023a..0000000000 --- a/erts/etc/common/safe_string.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2008-2009. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -/* - * Module: safe_string.h - * - * This is an interface to a bunch of generic string operation - * that are safe regarding buffer overflow. - * - * All string functions terminate the process with an error message - * on buffer overflow. - */ - -#include -#include - -/* Like vsnprintf() - */ -int vsn_printf(char* dst, size_t size, const char* format, va_list args); - -/* Like snprintf() - */ -int sn_printf(char* dst, size_t size, const char* format, ...); - -/* Like strncpy() - * Returns length of copied string. - */ -int strn_cpy(char* dst, size_t size, const char* src); - -/* Almost like strncat() - * size is sizeof entire dst buffer. - * Returns length of resulting string. - */ -int strn_cat(char* dst, size_t size, const char* src); - -/* Combination of strncat() and snprintf() - * size is sizeof entire dst buffer. - * Returns length of resulting string. - */ -int strn_catf(char* dst, size_t size, const char* format, ...); - -/* Simular to strstr() but search size bytes of haystack - * without regard to '\0' characters. - */ -char* find_str(const char* haystack, int size, const char* needle); - -#ifndef HAVE_MEMMOVE -void* memmove(void *dest, const void *src, size_t n); -#endif diff --git a/erts/etc/common/to_erl_common.c b/erts/etc/common/to_erl_common.c deleted file mode 100644 index 8aa94ccfa4..0000000000 --- a/erts/etc/common/to_erl_common.c +++ /dev/null @@ -1,717 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -/* - * Module: to_erl.c - * - * This module implements a process that opens two specified FIFOs, one - * for reading and one for writing; reads from its stdin, and writes what - * it has read to the write FIF0; reads from the read FIFO, and writes to - * its stdout. - * - ________ _________ - | |--<-- pipe.r (fifo1) --<--| | - | to_erl | | run_erl | (parent) - |________|-->-- pipe.w (fifo2) -->--|_________| - ^ master pty - | - | slave pty - ____V____ - | | - | "erl" | (child) - |_________| - */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __OSE__ -#include -#include "ose.h" -#include "efs.h" -#include "ose_spi/fm.sig" -#else /* __UNIX__ */ -#include -#include -#endif - -#ifdef HAVE_SYS_IOCTL_H -# include -#endif - -#include "to_erl_common.h" -#include "run_erl_vsn.h" -#include "safe_string.h" /* strn_cpy, strn_catf, sn_printf, etc. */ - -#if defined(O_NONBLOCK) -# define DONT_BLOCK_PLEASE O_NONBLOCK -#else -# define DONT_BLOCK_PLEASE O_NDELAY -# if !defined(EAGAIN) -# define EAGAIN -3898734 -# endif -#endif - -#ifdef HAVE_STRERROR -# define STRERROR(x) strerror(x) -#else -# define STRERROR(x) "" -#endif - -#define noDEBUG - -#ifdef __OSE__ -#define PIPE_DIR "/pipe/" -#else -#define PIPE_DIR "/tmp/" -#endif -#define PIPE_STUBNAME "erlang.pipe" -#define PIPE_STUBLEN strlen(PIPE_STUBNAME) - -#ifdef DEBUG -#define STATUS(s) { fprintf(stderr, (s)); fflush(stderr); } -#else -#define STATUS(s) -#endif - -#ifndef FILENAME_MAX -#define FILENAME_MAX 250 -#endif - -static int tty_eof = 0; -static int protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ - -static int write_all(int fd, const char* buf, int len); -static int version_handshake(char* buf, int len, int wfd); - - -#ifdef __OSE__ - -#define SET_AIO(REQ,FD,SIZE,BUFF) \ - /* Make sure to clean data structure of previous request */ \ - memset(&(REQ),0,sizeof(REQ)); \ - (REQ).aio_fildes = FD; \ - (REQ).aio_offset = FM_POSITION_CURRENT; \ - (REQ).aio_nbytes = SIZE; \ - (REQ).aio_buf = BUFF; \ - (REQ).aio_sigevent.sigev_notify = SIGEV_NONE - -#define READ_AIO(REQ,FD,SIZE,BUFF) \ - SET_AIO(REQ,FD,SIZE,BUFF); \ - if (aio_read(&(REQ)) != 0) \ - fprintf(stderr,"aio_read of child_read_req(%d) failed" \ - "with error %d\n",FD,errno) - -union SIGNAL { - SIGSELECT signo; - struct FmReadPtr fm_read_ptr; -}; - -#else /* __UNIX__ */ -static int recv_sig = 0; -static struct termios tty_smode, tty_rmode; -static int window_size_seq(char* buf, size_t bufsz); -#ifdef DEBUG -static void show_terminal_settings(struct termios *); -#endif - -static void handle_ctrlc(int sig) -{ - /* Reinstall the handler, and signal break flag */ - signal(SIGINT,handle_ctrlc); - recv_sig = SIGINT; -} - -static void handle_sigwinch(int sig) -{ - recv_sig = SIGWINCH; -} -#endif - -static void usage(char *pname) -{ - fprintf(stderr, "Usage: "); - fprintf(stderr,TO_ERL_USAGE,pname); -} - -int to_erl(int argc, char **argv) -{ - char FIFO1[FILENAME_MAX], FIFO2[FILENAME_MAX]; - int i, len, wfd, rfd; - char pipename[FILENAME_MAX]; - int pipeIx = 1; - int force_lock = 0; - int got_some = 0; - -#ifdef __OSE__ - struct aiocb stdin_read_req, pipe_read_req; - FmHandle stdin_fh, pipe_fh; - char *stdin_buf, *pipe_buf; - char *buf; - union SIGNAL *sig; -#else /* __UNIX__ */ - char buf[BUFSIZ]; - fd_set readfds; -#endif - - if (argc >= 2 && argv[1][0]=='-') { - switch (argv[1][1]) { - case 'h': - usage(argv[0]); - exit(1); - case 'F': - force_lock = 1; - break; - default: - fprintf(stderr,"Invalid option '%s'\n",argv[1]); - exit(1); - } - pipeIx = 2; - } - -#ifdef DEBUG - fprintf(stderr, "%s: pid is : %d\n", argv[0],(int) -#ifdef __OSE__ - current_process() -#else /* __UNIX__ */ - getpid() -#endif - ); -#endif - - strn_cpy(pipename, sizeof(pipename), - (argv[pipeIx] ? argv[pipeIx] : PIPE_DIR)); - - if(*pipename && pipename[strlen(pipename)-1] == '/') { - /* The user wishes us to find a pipe name in the specified */ - /* directory */ - int highest_pipe_num = 0; - DIR *dirp; - struct dirent *direntp; - - dirp = opendir(pipename); - if(!dirp) { - fprintf(stderr, "Can't access pipe directory %s: %s\n", pipename, strerror(errno)); - exit(1); - } - - /* Check the directory for existing pipes */ - - while((direntp=readdir(dirp)) != NULL) { - if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) { - int num = atoi(direntp->d_name+PIPE_STUBLEN+1); - if(num > highest_pipe_num) - highest_pipe_num = num; - } - } - closedir(dirp); - strn_catf(pipename, sizeof(pipename), (highest_pipe_num?"%s.%d":"%s"), - PIPE_STUBNAME, highest_pipe_num); - } /* if */ - - /* read FIFO */ - sn_printf(FIFO1,sizeof(FIFO1),"%s.r",pipename); - /* write FIFO */ - sn_printf(FIFO2,sizeof(FIFO2),"%s.w",pipename); - -#ifndef __OSE__ - /* Check that nobody is running to_erl on this pipe already */ - if ((wfd = open (FIFO1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { - /* Open as server succeeded -- to_erl is already running! */ - close(wfd); - fprintf(stderr, "Another to_erl process already attached to pipe " - "%s.\n", pipename); - if (force_lock) { - fprintf(stderr, "But we proceed anyway by force (-F).\n"); - } - else { - exit(1); - } - } -#endif - - if ((rfd = open (FIFO1, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) { -#ifdef DEBUG - fprintf(stderr, "Could not open FIFO %s for reading.\n", FIFO1); -#endif - fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno)); - exit(1); - } -#ifdef DEBUG - fprintf(stderr, "to_erl: %s opened for reading\n", FIFO1); -#endif - - if ((wfd = open (FIFO2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { -#ifdef DEBUG - fprintf(stderr, "Could not open FIFO %s for writing.\n", FIFO2); -#endif - fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno)); - close(rfd); - exit(1); - } -#ifdef DEBUG - fprintf(stderr, "to_erl: %s opened for writing\n", FIFO2); -#endif - -#ifndef __OSE__ - fprintf(stderr, "Attaching to %s (^D to exit)\n\n", pipename); -#else - fprintf(stderr, "Attaching to %s (^C to exit)\n\n", pipename); -#endif - -#ifndef __OSE__ - /* Set break handler to our handler */ - signal(SIGINT,handle_ctrlc); - - /* - * Save the current state of the terminal, and set raw mode. - */ - if (tcgetattr(0, &tty_rmode) , 0) { - fprintf(stderr, "Cannot get terminals current mode\n"); - exit(-1); - } - tty_smode = tty_rmode; - tty_eof = '\004'; /* Ctrl+D to exit */ -#ifdef DEBUG - show_terminal_settings(&tty_rmode); -#endif - tty_smode.c_iflag = - 1*BRKINT |/*Signal interrupt on break.*/ - 1*IGNPAR |/*Ignore characters with parity errors.*/ - 1*ISTRIP |/*Strip character.*/ - 0; - -#if 0 -0*IGNBRK |/*Ignore break condition.*/ -0*PARMRK |/*Mark parity errors.*/ -0*INPCK |/*Enable input parity check.*/ -0*INLCR |/*Map NL to CR on input.*/ -0*IGNCR |/*Ignore CR.*/ -0*ICRNL |/*Map CR to NL on input.*/ -0*IUCLC |/*Map upper-case to lower-case on input.*/ -0*IXON |/*Enable start/stop output control.*/ -0*IXANY |/*Enable any character to restart output.*/ -0*IXOFF |/*Enable start/stop input control.*/ -0*IMAXBEL|/*Echo BEL on input line too long.*/ -#endif - - tty_smode.c_oflag = - 1*OPOST |/*Post-process output.*/ - 1*ONLCR |/*Map NL to CR-NL on output.*/ -#ifdef XTABS - 1*XTABS |/*Expand tabs to spaces. (Linux)*/ -#endif -#ifdef OXTABS - 1*OXTABS |/*Expand tabs to spaces. (FreeBSD)*/ -#endif -#ifdef NL0 - 1*NL0 |/*Select newline delays*/ -#endif -#ifdef CR0 - 1*CR0 |/*Select carriage-return delays*/ -#endif -#ifdef TAB0 - 1*TAB0 |/*Select horizontal tab delays*/ -#endif -#ifdef BS0 - 1*BS0 |/*Select backspace delays*/ -#endif -#ifdef VT0 - 1*VT0 |/*Select vertical tab delays*/ -#endif -#ifdef FF0 - 1*FF0 |/*Select form feed delays*/ -#endif - 0; - -#if 0 -0*OLCUC |/*Map lower case to upper on output.*/ -0*OCRNL |/*Map CR to NL on output.*/ -0*ONOCR |/*No CR output at column 0.*/ -0*ONLRET |/*NL performs CR function.*/ -0*OFILL |/*Use fill characters for delay.*/ -0*OFDEL |/*Fill is DEL, else NULL.*/ -0*NL1 | -0*CR1 | -0*CR2 | -0*CR3 | -0*TAB1 | -0*TAB2 | -0*TAB3 |/*Expand tabs to spaces.*/ -0*BS1 | -0*VT1 | -0*FF1 | -#endif - - /* JALI: removed setting the tty_smode.c_cflag flags, since this is not */ - /* advisable if this is a *real* terminal, such as the console. In fact */ - /* this may hang the entire machine, deep, deep down (signalling break */ - /* or toggling the abort switch doesn't help) */ - - tty_smode.c_lflag = - 0; - -#if 0 -0*ISIG |/*Enable signals.*/ -0*ICANON |/*Canonical input (erase and kill processing).*/ -0*XCASE |/*Canonical upper/lower presentation.*/ -0*ECHO |/*Enable echo.*/ -0*ECHOE |/*Echo erase character as BS-SP-BS.*/ -0*ECHOK |/*Echo NL after kill character.*/ -0*ECHONL |/*Echo NL.*/ -0*NOFLSH |/*Disable flush after interrupt or quit.*/ -0*TOSTOP |/*Send SIGTTOU for background output.*/ -0*ECHOCTL|/*Echo control characters as ^char, delete as ^?.*/ -0*ECHOPRT|/*Echo erase character as character erased.*/ -0*ECHOKE |/*BS-SP-BS erase entire line on line kill.*/ -0*FLUSHO |/*Output is being flushed.*/ -0*PENDIN |/*Retype pending input at next read or input character.*/ -0*IEXTEN |/*Enable extended (implementation-defined) functions.*/ -#endif - - tty_smode.c_cc[VMIN] =0;/* Note that VMIN is the same as VEOF! */ - tty_smode.c_cc[VTIME] =0;/* Note that VTIME is the same as VEOL! */ - tty_smode.c_cc[VINTR] =3; - - tcsetattr(0, TCSADRAIN, &tty_smode); - -#ifdef DEBUG - show_terminal_settings(&tty_smode); -#endif - -#endif /* !__OSE__ */ - /* - * "Write a ^L to the FIFO which causes the other end to redisplay - * the input line." - * This does not seem to work as was intended in old comment above. - * However, this control character is now (R12B-3) used by run_erl - * to trigger the version handshaking between to_erl and run_erl - * at the start of every new to_erl-session. - */ - - if (write(wfd, "\014", 1) < 0) { - fprintf(stderr, "Error in writing ^L to FIFO.\n"); - } - -#ifdef __OSE__ - /* we have a tiny stack so we malloc the buffers */ - stdin_buf = malloc(sizeof(char) * BUFSIZ); - pipe_buf = malloc(sizeof(char) * BUFSIZ); - - efs_examine_fd(rfd,FLIB_FD_HANDLE,&pipe_fh); - efs_examine_fd(0,FLIB_FD_HANDLE,&stdin_fh); - READ_AIO(stdin_read_req,0,BUFSIZ,stdin_buf); - READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_buf); -#endif - - /* - * read and write - */ - while (1) { -#ifndef __OSE__ - FD_ZERO(&readfds); - FD_SET(0, &readfds); - FD_SET(rfd, &readfds); - if (select(rfd + 1, &readfds, NULL, NULL, NULL) < 0) { - if (recv_sig) { - FD_ZERO(&readfds); - } - else { - fprintf(stderr, "Error in select.\n"); - break; - } - } - len = 0; - - /* - * Read from terminal and write to FIFO - */ - if (recv_sig) { - switch (recv_sig) { - case SIGINT: - fprintf(stderr, "[Break]\n\r"); - buf[0] = '\003'; - len = 1; - break; - case SIGWINCH: - len = window_size_seq(buf,sizeof(buf)); - break; - default: - fprintf(stderr,"Unexpected signal: %u\n",recv_sig); - } - recv_sig = 0; - } - else -#else /* __OSE__ */ - SIGSELECT sigsel[] = {0}; - sig = receive(sigsel); - len = 0; -#endif -#ifndef __OSE__ - if (FD_ISSET(0,&readfds)) { - len = read(0, buf, sizeof(buf)); -#else /* __OSE__ */ - if (sig->signo == FM_READ_PTR_REPLY && - sig->fm_read_ptr.handle == stdin_fh) { - len = sig->fm_read_ptr.status == EFS_SUCCESS ? sig->fm_read_ptr.actual : -1; - buf = sig->fm_read_ptr.buffer; -#endif - if (len <= 0) { - close(rfd); - close(wfd); - if (len < 0) { - fprintf(stderr, "Error in reading from stdin.\n"); - } else { - fprintf(stderr, "[EOF]\n\r"); - } - break; - } - /* check if there is an eof character in input */ - for (i = 0; i < len-1 && buf[i] != tty_eof; i++); - if (buf[i] == tty_eof) { - fprintf(stderr, "[Quit]\n\r"); - break; - } - } - - if (len) { -#ifdef DEBUG - if(write(1, buf, len)); -#endif - if (write_all(wfd, buf, len) != len) { - fprintf(stderr, "Error in writing to FIFO.\n"); - close(rfd); - close(wfd); - break; - } - STATUS("\" OK\r\n"); -#ifdef __OSE__ - aio_dispatch(sig); - READ_AIO(stdin_read_req, 0, BUFSIZ, stdin_buf); -#endif - } - - /* - * Read from FIFO, write to terminal. - */ -#ifndef __OSE__ - if (FD_ISSET(rfd, &readfds)) { - STATUS("FIFO read: "); - len = read(rfd, buf, BUFSIZ); -#else /* __OSE__ */ - if (sig->signo == FM_READ_PTR_REPLY && - sig->fm_read_ptr.handle == pipe_fh) { - len = sig->fm_read_ptr.status == EFS_SUCCESS ? sig->fm_read_ptr.actual : -1; - buf = sig->fm_read_ptr.buffer; -#endif - if (len < 0 && errno == EAGAIN) { - /* - * No data this time, but the writing end of the FIFO is still open. - * Do nothing. - */ - ; - } else if (len <= 0) { - /* - * Either an error or end of file. In either case, break out - * of the loop. - */ - close(rfd); - close(wfd); - if (len < 0) { - fprintf(stderr, "Error in reading from FIFO.\n"); - } else - fprintf(stderr, "[End]\n\r"); - break; - } else { - if (!got_some) { - if ((len=version_handshake(buf,len,wfd)) < 0) { - close(rfd); - close(wfd); - break; - } -#ifndef __OSE__ - if (protocol_ver >= 1) { - /* Tell run_erl size of terminal window */ - signal(SIGWINCH, handle_sigwinch); - raise(SIGWINCH); - } -#endif - got_some = 1; - } - - /* - * We successfully read at least one character. Write what we got. - */ - STATUS("Terminal write: \""); - if (write_all(1, buf, len) != len) { - fprintf(stderr, "Error in writing to terminal.\n"); - close(rfd); - close(wfd); - break; - } - STATUS("\" OK\r\n"); -#ifdef __OSE__ - aio_dispatch(sig); - READ_AIO(pipe_read_req, rfd, BUFSIZ, pipe_buf); -#endif - } - } - } - -#ifndef __OSE__ - /* - * Reset terminal characterstics - * XXX - */ - tcsetattr(0, TCSADRAIN, &tty_rmode); -#endif - return 0; -} - -/* Call write() until entire buffer has been written or error. - * Return len or -1. - */ -static int write_all(int fd, const char* buf, int len) -{ - int left = len; - int written; - while (left) { - written = write(fd,buf,left); - if (written < 0) { - return -1; - } - left -= written; - buf += written; - } - return len; -} - -#ifndef __OSE__ -static int window_size_seq(char* buf, size_t bufsz) -{ -#ifdef TIOCGWINSZ - struct winsize ws; - static const char prefix[] = "\033_"; - static const char suffix[] = "\033\\"; - /* This Esc sequence is called "Application Program Command" - and seems suitable to use for our own customized stuff. */ - - if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) { - int len = sn_printf(buf, bufsz, "%swinsize=%u,%u%s", - prefix, ws.ws_col, ws.ws_row, suffix); - return len; - } -#endif /* TIOCGWINSZ */ - return 0; -} -#endif /* !__OSE__ */ - -/* to_erl run_erl - * | | - * |---------- '\014' -------->| (session start) - * | | - * |<---- "[run_erl v1-0]" ----| (version interval) - * | | - * |--- Esc_"version=1"Esc\ -->| (common version) - * | | - */ -static int version_handshake(char* buf, int len, int wfd) -{ - unsigned re_high=0, re_low; - char *end = find_str(buf,len,"]\n"); - - if (end && sscanf(buf,"[run_erl v%u-%u",&re_high,&re_low)==2) { - char wbuf[30]; - int wlen; - - if (re_low > RUN_ERL_HI_VER || re_high < RUN_ERL_LO_VER) { - fprintf(stderr,"Incompatible versions: to_erl=v%u-%u run_erl=v%u-%u\n", - RUN_ERL_HI_VER, RUN_ERL_LO_VER, re_high, re_low); - return -1; - } - /* Choose highest common version */ - protocol_ver = re_high < RUN_ERL_HI_VER ? re_high : RUN_ERL_HI_VER; - - wlen = sn_printf(wbuf, sizeof(wbuf), "\033_version=%u\033\\", - protocol_ver); - if (write_all(wfd, wbuf, wlen) < 0) { - fprintf(stderr,"Failed to send version handshake\n"); - return -1; - } - end += 2; - len -= (end-buf); - memmove(buf,end,len); - - } - else { /* we assume old run_erl without version handshake */ - protocol_ver = 0; - } - - if (re_high != RUN_ERL_HI_VER) { - fprintf(stderr,"run_erl has different version, " - "using common protocol level %u\n", protocol_ver); - } - - return len; -} - - -#if defined(DEBUG) && !defined(__OSE__) -#define S(x) ((x) > 0 ? 1 : 0) - -static void show_terminal_settings(struct termios *t) -{ - fprintf(stderr,"c_iflag:\n"); - fprintf(stderr,"Signal interrupt on break: BRKINT %d\n", S(t->c_iflag & BRKINT)); - fprintf(stderr,"Map CR to NL on input: ICRNL %d\n", S(t->c_iflag & ICRNL)); - fprintf(stderr,"Ignore break condition: IGNBRK %d\n", S(t->c_iflag & IGNBRK)); - fprintf(stderr,"Ignore CR: IGNCR %d\n", S(t->c_iflag & IGNCR)); - fprintf(stderr,"Ignore char with par. err's: IGNPAR %d\n", S(t->c_iflag & IGNPAR)); - fprintf(stderr,"Map NL to CR on input: INLCR %d\n", S(t->c_iflag & INLCR)); - fprintf(stderr,"Enable input parity check: INPCK %d\n", S(t->c_iflag & INPCK)); - fprintf(stderr,"Strip character ISTRIP %d\n", S(t->c_iflag & ISTRIP)); - fprintf(stderr,"Enable start/stop input ctrl IXOFF %d\n", S(t->c_iflag & IXOFF)); - fprintf(stderr,"ditto output ctrl IXON %d\n", S(t->c_iflag & IXON)); - fprintf(stderr,"Mark parity errors PARMRK %d\n", S(t->c_iflag & PARMRK)); - fprintf(stderr,"\n"); - fprintf(stderr,"c_oflag:\n"); - fprintf(stderr,"Perform output processing OPOST %d\n", S(t->c_oflag & OPOST)); - fprintf(stderr,"\n"); - fprintf(stderr,"c_cflag:\n"); - fprintf(stderr,"Ignore modem status lines CLOCAL %d\n", S(t->c_cflag & CLOCAL)); - fprintf(stderr,"\n"); - fprintf(stderr,"c_local:\n"); - fprintf(stderr,"Enable echo ECHO %d\n", S(t->c_lflag & ECHO)); - fprintf(stderr,"\n"); - fprintf(stderr,"c_cc:\n"); - fprintf(stderr,"c_cc[VEOF] %d\n", t->c_cc[VEOF]); -} -#endif /* DEBUG && !__OSE__ */ diff --git a/erts/etc/common/to_erl_common.h b/erts/etc/common/to_erl_common.h deleted file mode 100644 index a139da418f..0000000000 --- a/erts/etc/common/to_erl_common.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2013. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -#ifndef ERL_TO_ERL_H -#define ERL_TO_ERL_H - -#define TO_ERL_USAGE "to_erl [-h|-F] %s\n" \ - "\t-h\tThis help text.\n" \ - "\t-f\tForce connection even though pipe is locked by other to_erl process." - -int to_erl(int argc, char **argv); - -#endif diff --git a/erts/etc/ose/etc.lmconf b/erts/etc/ose/etc.lmconf deleted file mode 100644 index b402b325b1..0000000000 --- a/erts/etc/ose/etc.lmconf +++ /dev/null @@ -1,20 +0,0 @@ -OSE_LM_STACK_SIZES=256,512,1024,2048,4096,8192,16384,65536 -OSE_LM_SIGNAL_SIZES=31,63,127,255,1023,4095,16383,65535 -OSE_LM_POOL_SIZE=0x200000 -OSE_LM_MAIN_NAME=main -OSE_LM_MAIN_STACK_SIZE=0xF000 -OSE_LM_MAIN_PRIORITY=20 -## Has to be of a type that allows MAM -OSE_LM_PROGRAM_TYPE=APP_RAM -OSE_LM_DATA_INIT=YES -OSE_LM_BSS_INIT=YES -OSE_LM_EXEC_MODEL=SHARED -HEAP_MAX_SIZE=1000000000 -HEAP_SMALL_BUF_INIT_SIZE=64000000 -HEAP_LARGE_BUF_THRESHOLD=16000000 -HEAP_LOCK_TYPE=2 - -# Setting the environment variable EFS_RESOLVE_TMO on the block to 0. -# This will eliminiate delays when trying to open files on not mounted -# volumes. -EFS_RESOLVE_TMO=0 diff --git a/erts/etc/ose/run_erl.c b/erts/etc/ose/run_erl.c deleted file mode 100644 index 6775525297..0000000000 --- a/erts/etc/ose/run_erl.c +++ /dev/null @@ -1,664 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2013. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -/* - * Module: run_erl.c - * - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -/* System includes */ -#include -#include -#include -#include -#include -#include -#include -#include - -/* OSE includes */ -#include "ose.h" -#include "ose_spi/ose_spi.h" -#include "efs.h" -#include "pm.h" -#include "ose_spi/fm.sig" - -/* erts includes */ -#include "run_erl.h" -#include "run_erl_common.h" -#include "safe_string.h" /* sn_printf, strn_cpy, strn_cat, etc */ - -typedef struct RunErlSetup_ { - SIGSELECT signo; - int run_daemon; - char *logdir; - char *command; - char *pipename; - char *blockname; -} RunErlSetup; - -typedef struct ProgramState_ { - /* child process */ - int ifd, ofd; - OSDOMAIN domain; - PROCESS progpid, mainbid; - struct PmProgramInfo *info; - /* to_erl */ - char w_pipe[FILENAME_BUFSIZ], - r_pipe[FILENAME_BUFSIZ]; -} ProgramState; - -union SIGNAL { - SIGSELECT signo; - RunErlSetup setup; - struct FmReadPtr fm_read_ptr; - struct FmWritePtr fm_write_ptr; -}; - -static OSBOOLEAN hunt_in_block(char *block_name, - char *process_name, - PROCESS *pid); -static int create_child_process(char *command_string, char *blockname, - ProgramState *state); - - -static OSBOOLEAN hunt_in_block(char *block_name, - char *process_name, - PROCESS *pid) { - struct OS_pid_list *list; - PROCESS block_id = OSE_ILLEGAL_PROCESS; - int i; - char *name; - - *pid = OSE_ILLEGAL_PROCESS; - - list = get_bid_list(0); - - if (!list) - return 0; - - for (i = 0; i < list->count; i++) { - - if (list->list[i] == get_bid(current_process())) - continue; - - name = (char*)get_pid_info(list->list[i], OSE_PI_NAME); - if (name) { - if (strcmp(name,block_name) == 0) { - block_id = list->list[i]; - free_buf((union SIGNAL**)&name); - break; - } - free_buf((union SIGNAL**)&name); - } - } - - free_buf((union SIGNAL**)&list); - - if (block_id == OSE_ILLEGAL_PROCESS) - return 0; - - list = get_pid_list(block_id); - - if (!list) - return 0; - - for (i = 0; i < list->count; i++) { - name = (char*)get_pid_info(list->list[i], OSE_PI_NAME); - if (name) { - if (strcmp(name,process_name) == 0) { - *pid = list->list[i]; - free_buf((union SIGNAL**)&name); - break; - } - free_buf((union SIGNAL**)&name); - } - } - - free_buf((union SIGNAL**)&list); - - if (*pid == OSE_ILLEGAL_PROCESS) - return 0; - - return 1; - -} - - -static int create_child_process(char *command_string, char *blockname, - ProgramState *state) { - char *command = command_string; - char *argv; - int i = 0; - int ret_status; - PmStatus pm_status; - int tmp_io[2]; - int fd_arr[3]; - int ifd[2], ofd[2]; - char *handle; - struct PmLoadModuleInfoReply *mod_info; - - /* Parse out cmd and argv from the command string */ - while (1) { - if (command[i] == ' ' || command[i] == '\0') { - if (command[i] == '\0') - argv = NULL; - else { - command[i] = '\0'; - argv = command_string + i + 1; - } - break; - } - i++; - } - - if (blockname) - handle = blockname; - else - handle = simple_basename(command); - - if (ose_pm_load_module_info(handle,&mod_info) == PM_SUCCESS) { - /* Already installed */ - free_buf((union SIGNAL**)&mod_info); - } else if ((pm_status = ose_pm_install_load_module(0,"ELF",command,handle,0,0,NULL)) - != PM_SUCCESS) { - ERROR1(LOG_ERR,"ose_pm_install_load_module failed - pmstatus: 0x%08x\n", - pm_status); - return 0; - } - - state->domain = PM_NEW_DOMAIN; - - pm_status = ose_pm_create_program(&state->domain, handle, 0, 0 , NULL, - &state->progpid, &state->mainbid); - - if (pm_status != PM_SUCCESS) { - if (pm_status == PM_EINSTALL_HANDLE_IN_USE) - ERROR1(LOG_ERR,"ose_pm_create_program failed - " - "install handle \"%s\" is in use. You can specify another " - "install handle by using the -block option to run_erl.\n",handle); - else - ERROR1(LOG_ERR,"ose_pm_create_program failed - pmstatus: 0x%08x\n", - pm_status); - return 0; - } - - pm_status = ose_pm_program_info(state->progpid, &state->info); - /* FIXME don't forget to free this ((union SIGNAL **)&info) */ - if (pm_status != PM_SUCCESS) { - ERROR1(LOG_ERR,"ose_pm_program_info failed - pmstatus: 0x%08x\n", - pm_status); - return 0; - } - - /* We only clone stdin+stdout, what about stderr? */ - - /* create pipes */ - if (pipe(ifd) < 0) { - if (errno == ENOENT) - ERRNO_ERR0(LOG_ERR,"The /pipe file system is not available\n"); - else - ERRNO_ERR0(LOG_ERR,"pipe ifd failed\n"); - return 0; - } - - if (pipe(ofd) < 0) { - ERRNO_ERR0(LOG_ERR,"pipe ofd failed\n"); - return 0; - } - - /* FIXME Lock? */ - - /* backup our stdin stdout */ - if ((tmp_io[0] = dup(0)) < 0) { - ERRNO_ERR0(LOG_ERR,"dup 0 failed\n"); - return 0; - } - - if ((tmp_io[1] = dup(1)) < 0) { - ERRNO_ERR0(LOG_ERR,"dup 1 failed\n"); - return 0; - } - - /* set new pipe to fd 0,1 */ - if (dup2(ifd[1], 1) < 0) { - ERRNO_ERR0(LOG_ERR,"dup2 1 failed\n"); - return 0; - } - - if (dup2(ofd[0], 0) < 0) { - ERRNO_ERR0(LOG_ERR,"dup2 0 failed\n"); - return 0; - } - - /* clone array to newly created */ - fd_arr[0] = 2; /* Number of fd's */ - fd_arr[1] = 0; - fd_arr[2] = 1; - - if ((ret_status = efs_clone_array(state->info->main_process, fd_arr)) - != EFS_SUCCESS) { - ERROR1(LOG_ERR,"efs_close_array filed, errcode: %d\n", ret_status); - return 0; - } - - if (dup2(tmp_io[1], 1) < 0) { - ERRNO_ERR0(LOG_ERR,"restoring dup2 1 failed\n"); - return 0; - } - - if (dup2(tmp_io[0], 0) < 0) { - ERRNO_ERR0(LOG_ERR,"restoring dup2 1 failed\n"); - return 0; - } - - /* close loose-ends */ - sf_close(tmp_io[0]); - sf_close(tmp_io[1]); - sf_close(ifd[1]); - sf_close(ofd[0]); - state->ifd = ifd[0]; - state->ofd = ofd[1]; - - if (argv && set_env(state->progpid, "ARGV", argv)) { - ERRNO_ERR0(LOG_ERR,"something went wrong with set_env\n"); - } - - /* - * Start the program. - */ - pm_status = ose_pm_start_program(state->progpid); - if (pm_status != PM_SUCCESS) { - ERROR1(LOG_ERR,"ose_pm_install_load_module failed - pmstatus: 0x%08x\n", - pm_status); - return 0; - } - - return 1; -} - -#define SET_AIO(REQ,FD,SIZE,BUFF) \ - /* Make sure to clean data structure of previous request */ \ - memset(&(REQ),0,sizeof(REQ)); \ - (REQ).aio_fildes = FD; \ - (REQ).aio_offset = FM_POSITION_CURRENT; \ - (REQ).aio_nbytes = SIZE; \ - (REQ).aio_buf = BUFF; \ - (REQ).aio_sigevent.sigev_notify = SIGEV_NONE - -#define READ_AIO(REQ,FD,SIZE,BUFF) do { \ - SET_AIO(REQ,FD,SIZE,BUFF); \ - if (aio_read(&(REQ)) != 0) \ - ERRNO_ERR1(LOG_ERR,"aio_read of child_read_req(%d) failed\n",FD); \ - } while (0) - -#define WRITE_AIO(FD,SIZE,BUFF) do { \ - struct aiocb *write_req = malloc(sizeof(struct aiocb)); \ - char *write_buff = malloc(sizeof(char)*SIZE); \ - memcpy(write_buff,BUFF,SIZE); \ - SET_AIO(*write_req,FD,SIZE,write_buff); \ - if (aio_write(write_req) != 0) \ - ERRNO_ERR1(LOG_ERR,"aio_write of write_req(%d) failed\n",FD); \ - } while(0) - -int pass_on(ProgramState *state); -int pass_on(ProgramState *s) { - SIGSELECT sigsel[] = {0,FM_READ_PTR_REPLY}; - union SIGNAL *sig; - char child_read_buff[BUFSIZ], pipe_read_buff[BUFSIZ]; - struct aiocb child_read_req, pipe_read_req; - int rfd, wfd = 0; - FmHandle rfh, child_rfh; - int outstanding_writes = 0, got_some = 0, child_done = 0; - - if ((rfd = sf_open(s->r_pipe, O_RDONLY, 0)) < 0) { - ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.\n", s->r_pipe); - rfd = 0; - return 1; - } - - attach(NULL,s->progpid); - - /* Open the log file */ - erts_run_erl_log_open(); - - efs_examine_fd(rfd,FLIB_FD_HANDLE,&rfh); - efs_examine_fd(s->ifd,FLIB_FD_HANDLE,&child_rfh); - - READ_AIO(child_read_req,s->ifd,BUFSIZ,child_read_buff); - READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff); - - while (1) { - time_t now,last_activity; - - time(&last_activity); - sig = receive_w_tmo(erts_run_erl_log_alive_minutes()*60000,sigsel); - - time(&now); - - if (sig) { - erts_run_erl_log_activity(0,now,last_activity); - } else { - /* timeout */ - erts_run_erl_log_activity(1,now,last_activity); - continue; - } - - switch (sig->signo) { - case OS_ATTACH_SIG: { - if (rfd) { sf_close(rfd); rfd = 0; } - free_buf(&sig); - child_done = 1; - /* Make sure to to let all outstanding write request finish */ - if (outstanding_writes) - break; - if (wfd) sf_close(wfd); - return 0; - } - case FM_WRITE_PTR_REPLY: { - if (sig->fm_write_ptr.status == EFS_SUCCESS) { - if (sig->fm_write_ptr.actual < sig->fm_write_ptr.requested) { - WRITE_AIO(wfd, sig->fm_write_ptr.requested-sig->fm_write_ptr.actual, - sig->fm_write_ptr.buffer+sig->fm_write_ptr.actual); - } - } else { - /* Assume to_erl has terminated. */ - sf_close(wfd); - wfd = 0; - } - free((char*)sig->fm_write_ptr.buffer); - aio_dispatch(sig); - if ((--outstanding_writes == 0) && child_done) { - if (wfd) sf_close(wfd); - return 0; - } - break; - } - case FM_READ_PTR_REPLY: { - /* Child fd */ - if (sig->fm_read_ptr.handle == child_rfh) { - - /* Child terminated */ - if (sig->fm_read_ptr.status != EFS_SUCCESS || - sig->fm_read_ptr.actual == 0) { - - if (rfd) { sf_close(rfd); rfd = 0; } - - if (sig->fm_read_ptr.status != EFS_SUCCESS) { - ERROR0(LOG_ERR,"Erlang closed the connection."); - aio_dispatch(sig); - return 1; - } - - /* child closed connection gracefully */ - aio_dispatch(sig); - if (outstanding_writes) { - child_done = 1; - break; - } - - if (wfd) sf_close(wfd); - - return 0; - } else { - erts_run_erl_log_write(sig->fm_read_ptr.buffer, - sig->fm_read_ptr.actual); - if (wfd) { - WRITE_AIO(wfd, sig->fm_read_ptr.actual, sig->fm_read_ptr.buffer); - outstanding_writes++; - } - aio_dispatch(sig); - READ_AIO(child_read_req, s->ifd,BUFSIZ, child_read_buff); - } - /* pipe fd */ - } else if (sig->fm_read_ptr.handle == rfh) { - if (sig->fm_read_ptr.status != EFS_SUCCESS) { - if(rfd) sf_close(rfd); - if(wfd) sf_close(wfd); - aio_dispatch(sig); - ERRNO_ERR0(LOG_ERR,"Error in reading from FIFO."); - return 1; - } - if (sig->fm_read_ptr.actual == 0) { - /* to_erl closed its end of the pipe */ - aio_dispatch(sig); - sf_close(rfd); - rfd = sf_open(s->r_pipe,O_RDONLY|DONT_BLOCK_PLEASE, 0); - if (rfd < 0) { - ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", - s->r_pipe); - rfd = 0; - } else { - READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff); - } - got_some = 0; /* reset for next session */ - } else { - int len = sig->fm_read_ptr.actual; - char *buffer = sig->fm_read_ptr.buffer; - if (!wfd) { - /* Try to open the write pipe to to_erl. Now that we got some data - * from to_erl, to_erl should already be reading this pipe - open - * should succeed. But in case of error, we just ignore it. - */ - if ((wfd = sf_open(s->w_pipe, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { - erts_run_erl_log_status("Client expected on FIFO %s, " - "but can't open (len=%d)\n", - s->w_pipe, sig->fm_read_ptr.actual); - sf_close(rfd); - rfd = sf_open(s->r_pipe, O_RDONLY|DONT_BLOCK_PLEASE, 0); - if (rfd < 0) { - ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", - s->r_pipe); - return 1; - } - wfd = 0; - } else { -#ifdef DEBUG - erts_run_erl_log_status("run_erl: %s opened for writing\n", - s->w_pipe); -#endif - } - } - - if (!got_some && wfd && buffer[0] == '\014') { - char wbuf[30]; - int wlen = sn_printf(wbuf,sizeof(wbuf),"[run_erl v%u-%u]\n", - RUN_ERL_HI_VER, RUN_ERL_LO_VER); - /* For some reason this, the first write aio seems to - not get an FM_WRITE_PTR_REPLY, so we do not do: - outstanding_writes++; - */ - WRITE_AIO(wfd, wlen, wbuf); - } - got_some = 1; - - /* Write the message */ -#ifdef DEBUG - erts_run_erl_log_status("Pty master write; "); -#endif - len = erts_run_erl_extract_ctrl_seq(buffer,len, s->ofd); - - if (len > 0) { - int wlen = erts_run_erl_write_all(s->ofd, buffer, len); - if (wlen != len) { - aio_dispatch(sig); - ERRNO_ERR0(LOG_ERR,"Error in writing to terminal."); - if(rfd) sf_close(rfd); - if(wfd) sf_close(wfd); - return 1; - } - } -#ifdef DEBUG - erts_run_erl_log_status("OK\n"); -#endif - aio_dispatch(sig); - READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff); - } - } - break; - } - default: { - free_buf(&sig); - break; - } - } - } -} - -OS_PROCESS(run_erl_process) { - char *logdir, *command, *blockname; - SIGSELECT sigsel[] = {1,ERTS_SIGNAL_RUN_ERL_SETUP}; - union SIGNAL *sig = receive(sigsel); - ProgramState state; - char pipename[FILENAME_BUFSIZ]; - - state.info = NULL; - - logdir = strdup(sig->setup.logdir); - command = strdup(sig->setup.command); - strn_cpy(pipename,sizeof(pipename),sig->setup.pipename); - - if (sig->setup.blockname) - blockname = strdup(sig->setup.blockname); - else - blockname = NULL; - - erts_run_erl_log_init(sig->setup.run_daemon, logdir); - - free_buf(&sig); - - if (erts_run_erl_open_fifo(pipename,state.w_pipe,state.r_pipe)) - kill_proc(current_process()); - - if (create_child_process(command,blockname,&state)) - pass_on(&state); - - free(logdir); - free(command); - if (blockname) - free(blockname); - - if (state.info) - free_buf(((union SIGNAL**)&state.info)); - - sf_close(state.ifd); - sf_close(state.ofd); - - unlink(state.w_pipe); - unlink(state.r_pipe); - - kill_proc(current_process()); -} - -int run_erl(int argc,char **argv) { - char *pipename, *logdir, *command, *blockname = NULL; - int pipename_len, logdir_len, command_len, blockname_len = 0; - int i = 1, run_daemon = 0; - PROCESS pid; - SIGSELECT sigsel[] = {0}; - union SIGNAL *sig; - - if(argc < 4) { - fprintf(stderr,RUN_ERL_USAGE,"run_erl"); - return 1; - } - - while (1) { - if (argv[i][0] != '-') - break; - if (!strcmp(argv[i],"-daemon")) { - run_daemon = 1; - i++; - continue; - } - if (!strcmp(argv[i],"-block")) { - blockname = argv[i+1]; - blockname_len = strlen(argv[i+1]) + 1; - i+=2; - continue; - } - fprintf(stderr,RUN_ERL_USAGE,"run_erl"); - return 1; - } - - pipename = argv[i++]; - logdir = argv[i++]; - command = argv[i++]; - - /* + 1 to include NULL at end */ - logdir_len = strlen(logdir) + 1; - command_len = strlen(command) + 1; - pipename_len = strlen(pipename) + 1; - - if (run_daemon) { - /* We request that the run_erl_process should be started from the - main process so that it does not die when the shell command - returns */ - PROCESS main_pid; - hunt_in_block("run_erl","main",&main_pid); - sig = alloc(sizeof(*sig),ERTS_SIGNAL_RUN_ERL_DAEMON); - send(&sig,main_pid); - sig = receive(sigsel); - pid = sender(&sig); - free_buf(&sig); - } else { - pid = create_process(OS_BG_PROC,"run_erl_process", - run_erl_process, 0x800, - 0, 0, 0, NULL, 0, 0); - } - - sig = alloc(sizeof(RunErlSetup)+ - logdir_len+command_len+pipename_len+blockname_len, - ERTS_SIGNAL_RUN_ERL_SETUP); - sig->setup.run_daemon = run_daemon; - sig->setup.logdir = ((char*)sig)+sizeof(RunErlSetup); - sig->setup.command = ((char*)sig)+sizeof(RunErlSetup)+logdir_len; - sig->setup.pipename = ((char*)sig)+sizeof(RunErlSetup)+logdir_len+command_len; - if (blockname) - sig->setup.blockname = ((char*)sig)+sizeof(RunErlSetup)+ - logdir_len+command_len+pipename_len; - else - sig->setup.blockname = NULL; - - strcpy(sig->setup.logdir,logdir); - strcpy(sig->setup.command,command); - strcpy(sig->setup.pipename,pipename); - if (blockname) strcpy(sig->setup.blockname,blockname); - - send(&sig,pid); - - if (run_daemon) { - /* We are a daemon, error msgs will be sent to ramlog */ - start(pid); - return 1; - } - - /* We are not daemon, error msgs will be sent to stderr and we block here */ - efs_clone(pid); - start(pid); - - attach(NULL,pid); - sig = receive(sigsel); - - return 1; -} diff --git a/erts/etc/ose/run_erl.h b/erts/etc/ose/run_erl.h deleted file mode 100644 index bdc8b6c355..0000000000 --- a/erts/etc/ose/run_erl.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2013. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -#ifndef ERL_RUN_ERL_H -#define ERL_RUN_ERL_H - -#include "ose.h" - -#include "erts.sig" - -int run_erl(int argc, char **argv); -OS_PROCESS(run_erl_process); - -#endif diff --git a/erts/etc/ose/run_erl_main.c b/erts/etc/ose/run_erl_main.c deleted file mode 100644 index 8895c773a1..0000000000 --- a/erts/etc/ose/run_erl_main.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2013. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -/* - * Module: run_erl_main.c - * - * Container for load module that installs both run_erl and to_erl command. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -#include "ose.h" -#include "shell.h" - -#include "run_erl_common.h" -#include "run_erl.h" -#include "to_erl_common.h" - -union SIGNAL { - SIGSELECT signo; -}; - -int main(int argc, char **argv) -{ - - char run_erl_usage[320], - to_erl_usage[120]; - - (void)stdin;(void)stdout;(void)stderr; - - sprintf(run_erl_usage,RUN_ERL_USAGE,"run_erl [-daemon] [-block blockname]"); - sprintf(to_erl_usage,TO_ERL_USAGE,"pipename"); - - shell_add_cmd_attrs( - "run_erl",run_erl_usage, - "Redirect Erlang input and output streams", - run_erl,DEFAULT_PROC_TYPE,DEFAULT_PRIORITY,DEFAULT_STACK_SIZE); - - shell_add_cmd_attrs( - "to_erl",to_erl_usage, - "Attach to redirected Erlang input and output streams", - to_erl,DEFAULT_PROC_TYPE,DEFAULT_PRIORITY,DEFAULT_STACK_SIZE); - - while (1) { - static const SIGSELECT sigsel[] = {0}; - union SIGNAL *sig = receive(sigsel); - - if (sig->signo == ERTS_SIGNAL_RUN_ERL_DAEMON) { - PROCESS pid = create_process(OS_BG_PROC,"run_erl_daemon", - run_erl_process, 0x800, - 0, 0, 0, NULL, 0, 0); - send_w_s(&sig,pid,sender(&sig)); - } else { - printf("Got unexpected signal!"); - free_buf(&sig); - } - } - - return 1; -} diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index c8414030ca..44efb975ba 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2015. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,13 +41,11 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif - #ifdef HAVE_WORKING_POSIX_OPENPT #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif #endif - #include #include #include @@ -65,6 +63,11 @@ #include #include #include + +#ifdef __ANDROID__ +# include +#endif + #ifdef HAVE_SYSLOG_H # include #endif @@ -84,25 +87,81 @@ # include #endif -#include "run_erl_common.h" +#include "run_erl.h" #include "safe_string.h" /* sn_printf, strn_cpy, strn_cat, etc */ +#ifdef O_NONBLOCK +# define DONT_BLOCK_PLEASE O_NONBLOCK +#else +# define DONT_BLOCK_PLEASE O_NDELAY +# ifndef EAGAIN +# define EAGAIN -3898734 +# endif +#endif + +#define noDEBUG + +#define DEFAULT_LOG_GENERATIONS 5 +#define LOG_MAX_GENERATIONS 1000 /* No more than 1000 log files */ +#define LOG_MIN_GENERATIONS 2 /* At least two to switch between */ +#define DEFAULT_LOG_MAXSIZE 100000 +#define LOG_MIN_MAXSIZE 1000 /* Smallast value for changing log file */ +#define LOG_STUBNAME "erlang.log." +#define LOG_PERM 0664 +#define DEFAULT_LOG_ACTIVITY_MINUTES 5 +#define DEFAULT_LOG_ALIVE_MINUTES 15 +#define DEFAULT_LOG_ALIVE_FORMAT "%a %b %e %T %Z %Y" +#define ALIVE_BUFFSIZ 256 + +#define PERM 0600 +#define STATUSFILENAME "/run_erl.log" +#define PIPE_STUBNAME "erlang.pipe" +#define PIPE_STUBLEN strlen(PIPE_STUBNAME) + +#ifndef FILENAME_MAX +#define FILENAME_MAX 250 +#endif + +#ifndef O_SYNC +#define O_SYNC 0 +#define USE_FSYNC 1 +#endif + #define MAX(x,y) ((x) > (y) ? (x) : (y)) +#define FILENAME_BUFSIZ FILENAME_MAX + /* prototypes */ static void usage(char *); +static int create_fifo(char *name, int perm); static int open_pty_master(char **name, int *sfd); static int open_pty_slave(char *name); static void pass_on(pid_t); static void exec_shell(char **); +static void status(const char *format,...); +static void error_logf(int priority, int line, const char *format,...); static void catch_sigchild(int); +static int next_log(int log_num); +static int prev_log(int log_num); +static int find_next_log_num(void); +static int open_log(int log_num, int flags); +static void write_to_log(int* lfd, int* log_num, char* buf, int len); static void daemon_init(void); +static char *simple_basename(char *path); static void init_outbuf(void); static int outbuf_size(void); static void clear_outbuf(void); static char* outbuf_first(void); static void outbuf_delete(int bytes); static void outbuf_append(const char* bytes, int n); +static int write_all(int fd, const char* buf, int len); +static int extract_ctrl_seq(char* buf, int len); +static void set_window_size(unsigned col, unsigned row); + +static ssize_t sf_write(int fd, const void *buffer, size_t len); +static ssize_t sf_read(int fd, void *buffer, size_t len); +static int sf_open(const char *path, int flags, mode_t mode); +static int sf_close(int fd); #ifdef DEBUG static void show_terminal_settings(struct termios *t); @@ -110,11 +169,20 @@ static void show_terminal_settings(struct termios *t); /* static data */ static char fifo1[FILENAME_BUFSIZ], fifo2[FILENAME_BUFSIZ]; +static char statusfile[FILENAME_BUFSIZ]; +static char log_dir[FILENAME_BUFSIZ]; static char pipename[FILENAME_BUFSIZ]; static FILE *stdstatus = NULL; +static int log_generations = DEFAULT_LOG_GENERATIONS; +static int log_maxsize = DEFAULT_LOG_MAXSIZE; +static int log_alive_minutes = DEFAULT_LOG_ALIVE_MINUTES; +static int log_activity_minutes = DEFAULT_LOG_ACTIVITY_MINUTES; +static int log_alive_in_gmt = 0; +static char log_alive_format[ALIVE_BUFFSIZ+1]; static int run_daemon = 0; static char *program_name; static int mfd; /* master pty fd */ +static unsigned protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ /* * Output buffer. @@ -145,13 +213,29 @@ static char* outbuf_in; LOG_PID|LOG_CONS|LOG_NOWAIT,LOG_USER) #endif +#define ERROR0(Prio,Format) error_logf(Prio,__LINE__,Format"\n") +#define ERROR1(Prio,Format,A1) error_logf(Prio,__LINE__,Format"\n",A1) +#define ERROR2(Prio,Format,A1,A2) error_logf(Prio,__LINE__,Format"\n",A1,A2) + +#ifdef HAVE_STRERROR +# define ADD_ERRNO(Format) "errno=%d '%s'\n"Format"\n",errno,strerror(errno) +#else +# define ADD_ERRNO(Format) "errno=%d\n"Format"\n",errno +#endif +#define ERRNO_ERR0(Prio,Format) error_logf(Prio,__LINE__,ADD_ERRNO(Format)) +#define ERRNO_ERR1(Prio,Format,A1) error_logf(Prio,__LINE__,ADD_ERRNO(Format),A1) + + int main(int argc, char **argv) { int childpid; int sfd = -1; - char *ptyslave=NULL; + int fd; + char *p, *ptyslave=NULL; int i = 1; int off_argv; + int calculated_pipename = 0; + int highest_pipe_num = 0; program_name = argv[0]; @@ -169,16 +253,122 @@ int main(int argc, char **argv) off_argv = i; strn_cpy(pipename, sizeof(pipename), argv[i++]); - - erts_run_erl_log_init(run_daemon,argv[i]); + strn_cpy(log_dir, sizeof(log_dir), argv[i]); + strn_cpy(statusfile, sizeof(statusfile), log_dir); + strn_cat(statusfile, sizeof(statusfile), STATUSFILENAME); #ifdef DEBUG - erts_run_erl_log_status("%s: pid is : %d\n", argv[0], getpid()); + status("%s: pid is : %d\n", argv[0], getpid()); #endif - /* Open read and write fifo */ - if (erts_run_erl_open_fifo(pipename,fifo1,fifo2)) - exit(1); + /* Get values for LOG file handling from the environment */ + if ((p = getenv("RUN_ERL_LOG_ALIVE_MINUTES"))) { + log_alive_minutes = atoi(p); + if (!log_alive_minutes) { + ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ALIVE_MINUTES is 1 " + "(current value is %s)",p); + } + log_activity_minutes = log_alive_minutes / 3; + if (!log_activity_minutes) { + ++log_activity_minutes; + } + } + if ((p = getenv("RUN_ERL_LOG_ACTIVITY_MINUTES"))) { + log_activity_minutes = atoi(p); + if (!log_activity_minutes) { + ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ACTIVITY_MINUTES is 1 " + "(current value is %s)",p); + } + } + if ((p = getenv("RUN_ERL_LOG_ALIVE_FORMAT"))) { + if (strlen(p) > ALIVE_BUFFSIZ) { + ERROR1(LOG_ERR, "RUN_ERL_LOG_ALIVE_FORMAT can contain a maximum of " + "%d characters", ALIVE_BUFFSIZ); + } + strn_cpy(log_alive_format, sizeof(log_alive_format), p); + } else { + strn_cpy(log_alive_format, sizeof(log_alive_format), DEFAULT_LOG_ALIVE_FORMAT); + } + if ((p = getenv("RUN_ERL_LOG_ALIVE_IN_UTC")) && strcmp(p,"0")) { + ++log_alive_in_gmt; + } + if ((p = getenv("RUN_ERL_LOG_GENERATIONS"))) { + log_generations = atoi(p); + if (log_generations < LOG_MIN_GENERATIONS) + ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_GENERATIONS is %d", LOG_MIN_GENERATIONS); + if (log_generations > LOG_MAX_GENERATIONS) + ERROR1(LOG_ERR,"Maximum RUN_ERL_LOG_GENERATIONS is %d", LOG_MAX_GENERATIONS); + } + + if ((p = getenv("RUN_ERL_LOG_MAXSIZE"))) { + log_maxsize = atoi(p); + if (log_maxsize < LOG_MIN_MAXSIZE) + ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_MAXSIZE is %d", LOG_MIN_MAXSIZE); + } + + /* + * Create FIFOs and open them + */ + + if(*pipename && pipename[strlen(pipename)-1] == '/') { + /* The user wishes us to find a unique pipe name in the specified */ + /* directory */ + DIR *dirp; + struct dirent *direntp; + + calculated_pipename = 1; + dirp = opendir(pipename); + if(!dirp) { + ERRNO_ERR1(LOG_ERR,"Can't access pipe directory '%s'.", pipename); + exit(1); + } + + /* Check the directory for existing pipes */ + + while((direntp=readdir(dirp)) != NULL) { + if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) { + int num = atoi(direntp->d_name+PIPE_STUBLEN+1); + if(num > highest_pipe_num) + highest_pipe_num = num; + } + } + closedir(dirp); + strn_catf(pipename, sizeof(pipename), "%s.%d", + PIPE_STUBNAME, highest_pipe_num+1); + } /* if */ + + for(;;) { + /* write FIFO - is read FIFO for `to_erl' program */ + strn_cpy(fifo1, sizeof(fifo1), pipename); + strn_cat(fifo1, sizeof(fifo1), ".r"); + if (create_fifo(fifo1, PERM) < 0) { + ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for writing.", fifo1); + exit(1); + } + + /* read FIFO - is write FIFO for `to_erl' program */ + strn_cpy(fifo2, sizeof(fifo2), pipename); + strn_cat(fifo2, sizeof(fifo2), ".w"); + + /* Check that nobody is running run_erl already */ + if ((fd = sf_open(fifo2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { + /* Open as client succeeded -- run_erl is already running! */ + sf_close(fd); + if (calculated_pipename) { + ++highest_pipe_num; + strn_catf(pipename, sizeof(pipename), "%s.%d", + PIPE_STUBNAME, highest_pipe_num+1); + continue; + } + fprintf(stderr, "Erlang already running on pipe %s.\n", pipename); + exit(1); + } + if (create_fifo(fifo2, PERM) < 0) { + ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for reading.", fifo2); + exit(1); + } + break; + } /* * Open master pseudo-terminal @@ -250,7 +440,7 @@ int main(int argc, char **argv) sf_close(2); if (dup(sfd) != 0 || dup(sfd) != 1 || dup(sfd) != 2) { - erts_run_erl_log_status("Cannot dup\n"); + status("Cannot dup\n"); } sf_close(sfd); exec_shell(argv+off_argv); /* exec_shell expects argv[2] to be */ @@ -293,7 +483,9 @@ static void pass_on(pid_t childpid) struct timeval timeout; time_t last_activity; char buf[BUFSIZ]; - int rfd, wfd=0; + char log_alive_buffer[ALIVE_BUFFSIZ+1]; + int lognum; + int rfd, wfd=0, lfd=0; int maxfd; int ready; int got_some = 0; /* from to_erl */ @@ -308,12 +500,13 @@ static void pass_on(pid_t childpid) } #ifdef DEBUG - erts_run_erl_log_status("run_erl: %s opened for reading\n", fifo2); + status("run_erl: %s opened for reading\n", fifo2); #endif /* Open the log file */ - erts_run_erl_log_open(); + lognum = find_next_log_num(); + lfd = open_log(lognum, O_RDWR|O_APPEND|O_CREAT|O_SYNC); /* Enter the work loop */ @@ -332,8 +525,7 @@ static void pass_on(pid_t childpid) writefds_ptr = &writefds; } time(&last_activity); - /* don't assume old BSD bug */ - timeout.tv_sec = erts_run_erl_log_alive_minutes()*60; + timeout.tv_sec = log_alive_minutes*60; /* don't assume old BSD bug */ timeout.tv_usec = 0; ready = select(maxfd + 1, &readfds, writefds_ptr, NULL, &timeout); if (ready < 0) { @@ -363,7 +555,28 @@ static void pass_on(pid_t childpid) /* Check how long time we've been inactive */ time(&now); - erts_run_erl_log_activity(!ready,now,last_activity); + if(!ready || now - last_activity > log_activity_minutes*60) { + /* Either a time out: 15 minutes without action, */ + /* or something is coming in right now, but it's a long time */ + /* since last time, so let's write a time stamp this message */ + struct tm *tmptr; + if (log_alive_in_gmt) { + tmptr = gmtime(&now); + } else { + tmptr = localtime(&now); + } + if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, log_alive_format, + tmptr)) { + strn_cpy(log_alive_buffer, sizeof(log_alive_buffer), + "(could not format time in 256 positions " + "with current format string.)"); + } + log_alive_buffer[ALIVE_BUFFSIZ] = '\0'; + + sn_printf(buf, sizeof(buf), "\n===== %s%s\n", + ready?"":"ALIVE ", log_alive_buffer); + write_to_log(&lfd, &lognum, buf, strlen(buf)); + } } /* @@ -398,7 +611,7 @@ static void pass_on(pid_t childpid) */ if (FD_ISSET(mfd, &readfds)) { #ifdef DEBUG - erts_run_erl_log_status("Pty master read; "); + status("Pty master read; "); #endif if ((len = sf_read(mfd, buf, BUFSIZ)) <= 0) { sf_close(rfd); @@ -416,7 +629,7 @@ static void pass_on(pid_t childpid) exit(0); } - erts_run_erl_log_write(buf, len); + write_to_log(&lfd, &lognum, buf, len); /* * Save in the output queue. @@ -432,7 +645,7 @@ static void pass_on(pid_t childpid) */ if (FD_ISSET(rfd, &readfds)) { #ifdef DEBUG - erts_run_erl_log_status("FIFO read; "); + status("FIFO read; "); #endif if ((len = sf_read(rfd, buf, BUFSIZ)) < 0) { sf_close(rfd); @@ -461,7 +674,7 @@ static void pass_on(pid_t childpid) * should succeed. But in case of error, we just ignore it. */ if ((wfd = sf_open(fifo1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { - erts_run_erl_log_status("Client expected on FIFO %s, but can't open (len=%d)\n", + status("Client expected on FIFO %s, but can't open (len=%d)\n", fifo1, len); sf_close(rfd); rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0); @@ -473,7 +686,7 @@ static void pass_on(pid_t childpid) } else { #ifdef DEBUG - erts_run_erl_log_status("run_erl: %s opened for writing\n", fifo1); + status("run_erl: %s opened for writing\n", fifo1); #endif } } @@ -489,15 +702,14 @@ static void pass_on(pid_t childpid) /* Write the message */ #ifdef DEBUG - erts_run_erl_log_status("Pty master write; "); + status("Pty master write; "); #endif - len = erts_run_erl_extract_ctrl_seq(buf, len, mfd); + len = extract_ctrl_seq(buf, len); if(len==1 && buf[0] == '\003') { kill(childpid,SIGINT); - } - else if (len>0 && erts_run_erl_write_all(mfd, buf, len) != len) - { + } + else if (len>0 && write_all(mfd, buf, len) != len) { ERRNO_ERR0(LOG_ERR,"Error in writing to terminal."); sf_close(rfd); if(wfd) sf_close(wfd); @@ -506,7 +718,7 @@ static void pass_on(pid_t childpid) } } #ifdef DEBUG - erts_run_erl_log_status("OK\n"); + status("OK\n"); #endif } } @@ -516,6 +728,173 @@ static void catch_sigchild(int sig) { } +/* + * next_log: + * Returns the index number that follows the given index number. + * (Wrapping after log_generations) + */ +static int next_log(int log_num) { + return log_num>=log_generations?1:log_num+1; +} + +/* + * prev_log: + * Returns the index number that precedes the given index number. + * (Wrapping after log_generations) + */ +static int prev_log(int log_num) { + return log_num<=1?log_generations:log_num-1; +} + +/* + * find_next_log_num() + * Searches through the log directory to check which logs that already + * exist. It finds the "hole" in the sequence, and returns the index + * number for the last log in the log sequence. If there is no hole, index + * 1 is returned. + */ +static int find_next_log_num(void) { + int i, next_gen, log_gen; + DIR *dirp; + struct dirent *direntp; + int log_exists[LOG_MAX_GENERATIONS+1]; + int stub_len = strlen(LOG_STUBNAME); + + /* Initialize exiting log table */ + + for(i=log_generations; i>=0; i--) + log_exists[i] = 0; + dirp = opendir(log_dir); + if(!dirp) { + ERRNO_ERR1(LOG_ERR,"Can't access log directory '%s'", log_dir); + exit(1); + } + + /* Check the directory for existing logs */ + + while((direntp=readdir(dirp)) != NULL) { + if(strncmp(direntp->d_name,LOG_STUBNAME,stub_len)==0) { + int num = atoi(direntp->d_name+stub_len); + if(num < 1 || num > log_generations) + continue; + log_exists[num] = 1; + } + } + closedir(dirp); + + /* Find out the next available log file number */ + + next_gen = 0; + for(i=log_generations; i>=0; i--) { + if(log_exists[i]) + if(next_gen) + break; + else + ; + else + next_gen = i; + } + + /* Find out the current log file number */ + + if(next_gen) + log_gen = prev_log(next_gen); + else + log_gen = 1; + + return log_gen; +} /* find_next_log_num() */ + +/* open_log() + * Opens a log file (with given index) for writing. Writing may be + * at the end or a trucnating write, according to flags. + * A LOGGING STARTED and time stamp message is inserted into the log file + */ +static int open_log(int log_num, int flags) +{ + char buf[FILENAME_MAX]; + time_t now; + struct tm *tmptr; + char log_buffer[ALIVE_BUFFSIZ+1]; + int lfd; + + /* Remove the next log (to keep a "hole" in the log sequence) */ + sn_printf(buf, sizeof(buf), "%s/%s%d", + log_dir, LOG_STUBNAME, next_log(log_num)); + unlink(buf); + + /* Create or continue on the current log file */ + sn_printf(buf, sizeof(buf), "%s/%s%d", log_dir, LOG_STUBNAME, log_num); + if((lfd = sf_open(buf, flags, LOG_PERM))<0){ + ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf); + exit(1); + } + + /* Write a LOGGING STARTED and time stamp into the log file */ + time(&now); + if (log_alive_in_gmt) { + tmptr = gmtime(&now); + } else { + tmptr = localtime(&now); + } + if (!strftime(log_buffer, ALIVE_BUFFSIZ, log_alive_format, + tmptr)) { + strn_cpy(log_buffer, sizeof(log_buffer), + "(could not format time in 256 positions " + "with current format string.)"); + } + log_buffer[ALIVE_BUFFSIZ] = '\0'; + + sn_printf(buf, sizeof(buf), "\n=====\n===== LOGGING STARTED %s\n=====\n", + log_buffer); + if (write_all(lfd, buf, strlen(buf)) < 0) + status("Error in writing to log.\n"); + +#if USE_FSYNC + fsync(lfd); +#endif + + return lfd; +} + +/* write_to_log() + * Writes a message to a log file. If the current log file is full, + * a new log file is opened. + */ +static void write_to_log(int* lfd, int* log_num, char* buf, int len) +{ + int size; + + /* Decide if new logfile needed, and open if so */ + + size = lseek(*lfd,0,SEEK_END); + if(size+len > log_maxsize) { + sf_close(*lfd); + *log_num = next_log(*log_num); + *lfd = open_log(*log_num, O_RDWR|O_CREAT|O_TRUNC|O_SYNC); + } + + /* Write to log file */ + + if (write_all(*lfd, buf, len) < 0) { + status("Error in writing to log.\n"); + } + +#if USE_FSYNC + fsync(*lfd); +#endif +} + +/* create_fifo() + * Creates a new fifo with the given name and permission. + */ +static int create_fifo(char *name, int perm) +{ + if ((mkfifo(name, perm) < 0) && (errno != EEXIST)) + return -1; + return 0; +} + /* open_pty_master() * Find a master device, open and return fd and slave device name. @@ -712,9 +1091,9 @@ static void exec_shell(char **argv) else argv[0] = sh; argv[1] = "-c"; - erts_run_erl_log_status("Args before exec of shell:\n"); + status("Args before exec of shell:\n"); for (vp = argv, i = 0; *vp; vp++, i++) - erts_run_erl_log_status("argv[%d] = %s\n", i, *vp); + status("argv[%d] = %s\n", i, *vp); if (stdstatus) { fclose(stdstatus); } @@ -725,6 +1104,26 @@ static void exec_shell(char **argv) ERRNO_ERR0(LOG_ERR,"Could not execv"); } +/* status() + * Prints the arguments to a status file + * Works like printf (see vfrpintf) + */ +static void status(const char *format,...) +{ + va_list args; + time_t now; + + if (stdstatus == NULL) + stdstatus = fopen(statusfile, "w"); + if (stdstatus == NULL) + return; + now = time(NULL); + fprintf(stdstatus, "run_erl [%d] %s", (int)getpid(), ctime(&now)); + va_start(args, format); + vfprintf(stdstatus, format, args); + va_end(args); + fflush(stdstatus); +} static void daemon_init(void) /* As R Stevens wants it, to a certain extent anyway... */ @@ -764,10 +1163,47 @@ static void daemon_init(void) run_daemon = 1; } +/* error_logf() + * Prints the arguments to stderr or syslog + * Works like printf (see vfprintf) + */ +static void error_logf(int priority, int line, const char *format, ...) +{ + va_list args; + va_start(args, format); + +#ifdef HAVE_SYSLOG_H + if (run_daemon) { + vsyslog(priority,format,args); + } + else +#endif + { + time_t now = time(NULL); + fprintf(stderr, "run_erl:%d [%d] %s", line, (int)getpid(), ctime(&now)); + vfprintf(stderr, format, args); + } + va_end(args); +} + static void usage(char *pname) { - fprintf(stderr, "Usage: "); - fprintf(stderr, RUN_ERL_USAGE, pname); + fprintf(stderr, "Usage: %s (pipe_name|pipe_dir/) log_dir \"command [parameters ...]\"\n", pname); + fprintf(stderr, "\nYou may also set the environment variables RUN_ERL_LOG_GENERATIONS\n"); + fprintf(stderr, "and RUN_ERL_LOG_MAXSIZE to the number of log files to use and the\n"); + fprintf(stderr, "size of the log file when to switch to the next log file\n"); +} + +/* Instead of making sure basename exists, we do our own */ +static char *simple_basename(char *path) +{ + char *ptr; + for (ptr = path; *ptr != '\0'; ++ptr) { + if (*ptr == '/') { + path = ptr + 1; + } + } + return path; } static void init_outbuf(void) @@ -838,6 +1274,114 @@ static void outbuf_append(const char* buf, int n) outbuf_in += n; } +/* Call write() until entire buffer has been written or error. + * Return len or -1. + */ +static int write_all(int fd, const char* buf, int len) +{ + int left = len; + int written; + for (;;) { + written = sf_write(fd,buf,left); + if (written == left) { + return len; + } + if (written < 0) { + return -1; + } + left -= written; + buf += written; + } +} + +static ssize_t sf_read(int fd, void *buffer, size_t len) { + ssize_t n = 0; + + do { n = read(fd, buffer, len); } while (n < 0 && errno == EINTR); + + return n; +} + +static ssize_t sf_write(int fd, const void *buffer, size_t len) { + ssize_t n = 0; + + do { n = write(fd, buffer, len); } while (n < 0 && errno == EINTR); + + return n; +} + +static int sf_open(const char *path, int type, mode_t mode) { + int fd = 0; + + do { fd = open(path, type, mode); } while(fd < 0 && errno == EINTR); + + return fd; +} +static int sf_close(int fd) { + int res = 0; + + do { res = close(fd); } while(fd < 0 && errno == EINTR); + + return res; +} +/* Extract any control sequences that are ment only for run_erl + * and should not be forwarded to the pty. + */ +static int extract_ctrl_seq(char* buf, int len) +{ + static const char prefix[] = "\033_"; + static const char suffix[] = "\033\\"; + char* bufend = buf + len; + char* start = buf; + char* command; + char* end; + + for (;;) { + start = find_str(start, bufend-start, prefix); + if (!start) break; + + command = start + strlen(prefix); + end = find_str(command, bufend-command, suffix); + if (end) { + unsigned col, row; + if (sscanf(command,"version=%u", &protocol_ver)==1) { + /*fprintf(stderr,"to_erl v%u\n", protocol_ver);*/ + } + else if (sscanf(command,"winsize=%u,%u", &col, &row)==2) { + set_window_size(col,row); + } + else { + ERROR2(LOG_ERR, "Ignoring unknown ctrl command '%.*s'\n", + (int)(end-command), command); + } + + /* Remove ctrl sequence from buf */ + end += strlen(suffix); + memmove(start, end, bufend-end); + bufend -= end - start; + } + else { + ERROR2(LOG_ERR, "Missing suffix in ctrl sequence '%.*s'\n", + (int)(bufend-start), start); + break; + } + } + return bufend - buf; +} + +static void set_window_size(unsigned col, unsigned row) +{ +#ifdef TIOCSWINSZ + struct winsize ws; + ws.ws_col = col; + ws.ws_row = row; + if (ioctl(mfd, TIOCSWINSZ, &ws) < 0) { + ERRNO_ERR0(LOG_ERR,"Failed to set window size"); + } +#endif +} + + #ifdef DEBUG #define S(x) ((x) > 0 ? 1 : 0) diff --git a/erts/etc/unix/run_erl.h b/erts/etc/unix/run_erl.h new file mode 100644 index 0000000000..cc70a98e52 --- /dev/null +++ b/erts/etc/unix/run_erl.h @@ -0,0 +1,31 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2008-2009. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +/* + * The protocol version number used between to_erl and run_erl. + */ +#define RUN_ERL_HI_VER 1 /* My preferred protocol version */ +#define RUN_ERL_LO_VER 0 /* The lowest version I accept to talk with */ + +/* Version history: + * 0: Older, without version handshake + * 1: R12B-3, version handshake + window size ctrl + */ + diff --git a/erts/etc/unix/safe_string.c b/erts/etc/unix/safe_string.c new file mode 100644 index 0000000000..a5c11d41d8 --- /dev/null +++ b/erts/etc/unix/safe_string.c @@ -0,0 +1,124 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2008-2009. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ +/* + * Module: safe_string.c + * + * This is a bunch of generic string operation + * that are safe regarding buffer overflow. + * + * All string functions terminate the process with an error message + * on buffer overflow. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "safe_string.h" +#include +#include +#include +#include + + +static void string_overflow_handler(const char* format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr,format,args); + va_end(args); + exit(1); +} + +int vsn_printf(char* dst, size_t size, const char* format, va_list args) +{ + int ret = vsnprintf(dst, size, format, args); + if (ret >= size || ret < 0) { + string_overflow_handler("Buffer truncated '%s'\n",dst); + } + return ret; +} + +int sn_printf(char* dst, size_t size, const char* format, ...) +{ + va_list args; + int ret; + va_start(args, format); + ret = vsn_printf(dst,size,format,args); + va_end(args); + return ret; +} + +int strn_cpy(char* dst, size_t size, const char* src) +{ + return sn_printf(dst,size,"%s",src); +} + +int strn_cat(char* dst, size_t size, const char* src) +{ + return strn_catf(dst,size,"%s",src); +} + +int strn_catf(char* dst, size_t size, const char* format, ...) +{ + int ret; + va_list args; +#ifdef _GNU_SOURCE + int len = strnlen(dst,size); +#else + int len = strlen(dst); +#endif + + if (len >= size) { + string_overflow_handler("Buffer already overflowed '%.*s'\n", + size, dst); + } + va_start(args, format); + ret = vsn_printf(dst+len, size-len, format, args); + va_end(args); + return len+ret; +} + +char* find_str(const char* haystack, int hsize, const char* needle) +{ + int i = 0; + int nsize = strlen(needle); + hsize -= nsize - 1; + for (i=0; i dest) { + for (i=0; i=0; i--) ((char*)dest)[i] = ((char*)src)[i]; + } + return dest; +} +#endif /* HAVE_MEMMOVE */ + diff --git a/erts/etc/unix/safe_string.h b/erts/etc/unix/safe_string.h new file mode 100644 index 0000000000..5a471f10de --- /dev/null +++ b/erts/etc/unix/safe_string.h @@ -0,0 +1,66 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2008-2009. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ +/* + * Module: safe_string.h + * + * This is an interface to a bunch of generic string operation + * that are safe regarding buffer overflow. + * + * All string functions terminate the process with an error message + * on buffer overflow. + */ + +#include +#include + +/* Like vsnprintf() + */ +int vsn_printf(char* dst, size_t size, const char* format, va_list args); + +/* Like snprintf() + */ +int sn_printf(char* dst, size_t size, const char* format, ...); + +/* Like strncpy() + * Returns length of copied string. + */ +int strn_cpy(char* dst, size_t size, const char* src); + +/* Almost like strncat() + * size is sizeof entire dst buffer. + * Returns length of resulting string. + */ +int strn_cat(char* dst, size_t size, const char* src); + +/* Combination of strncat() and snprintf() + * size is sizeof entire dst buffer. + * Returns length of resulting string. + */ +int strn_catf(char* dst, size_t size, const char* format, ...); + +/* Simular to strstr() but search size bytes of haystack + * without regard to '\0' characters. + */ +char* find_str(const char* haystack, int size, const char* needle); + +#ifndef HAVE_MEMMOVE +void* memmove(void *dest, const void *src, size_t n); +#endif + diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c index 82d3218964..0bd469727c 100644 --- a/erts/etc/unix/to_erl.c +++ b/erts/etc/unix/to_erl.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2015. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,9 +17,592 @@ * * %CopyrightEnd% */ +/* + * Module: to_erl.c + * + * This module implements a process that opens two specified FIFOs, one + * for reading and one for writing; reads from its stdin, and writes what + * it has read to the write FIF0; reads from the read FIFO, and writes to + * its stdout. + * + ________ _________ + | |--<-- pipe.r (fifo1) --<--| | + | to_erl | | run_erl | (parent) + |________|-->-- pipe.w (fifo2) -->--|_________| + ^ master pty + | + | slave pty + ____V____ + | | + | "erl" | (child) + |_________| + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_IOCTL_H +# include +#endif + +#include "run_erl.h" +#include "safe_string.h" /* strn_cpy, strn_catf, sn_printf, etc. */ + +#if defined(O_NONBLOCK) +# define DONT_BLOCK_PLEASE O_NONBLOCK +#else +# define DONT_BLOCK_PLEASE O_NDELAY +# if !defined(EAGAIN) +# define EAGAIN -3898734 +# endif +#endif + +#ifdef HAVE_STRERROR +# define STRERROR(x) strerror(x) +#else +# define STRERROR(x) "" +#endif + +#define noDEBUG + +#define PIPE_DIR "/tmp/" +#define PIPE_STUBNAME "erlang.pipe" +#define PIPE_STUBLEN strlen(PIPE_STUBNAME) + +#ifdef DEBUG +#define STATUS(s) { fprintf(stderr, (s)); fflush(stderr); } +#else +#define STATUS(s) +#endif + +#ifndef FILENAME_MAX +#define FILENAME_MAX 250 +#endif + +static struct termios tty_smode, tty_rmode; +static int tty_eof = 0; +static int recv_sig = 0; +static int protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ + +static int write_all(int fd, const char* buf, int len); +static int window_size_seq(char* buf, size_t bufsz); +static int version_handshake(char* buf, int len, int wfd); +#ifdef DEBUG +static void show_terminal_settings(struct termios *); +#endif + +static void handle_ctrlc(int sig) +{ + /* Reinstall the handler, and signal break flag */ + signal(SIGINT,handle_ctrlc); + recv_sig = SIGINT; +} + +static void handle_sigwinch(int sig) +{ + recv_sig = SIGWINCH; +} + +static void usage(char *pname) +{ + fprintf(stderr, "Usage: %s [-h|-F] [pipe_name|pipe_dir/]\n", pname); + fprintf(stderr, "\t-h\tThis help text.\n"); + fprintf(stderr, "\t-F\tForce connection even though pipe is locked by other to_erl process.\n"); +} + +int main(int argc, char **argv) +{ + char FIFO1[FILENAME_MAX], FIFO2[FILENAME_MAX]; + int i, len, wfd, rfd; + fd_set readfds; + char buf[BUFSIZ]; + char pipename[FILENAME_MAX]; + int pipeIx = 1; + int force_lock = 0; + int got_some = 0; + + if (argc >= 2 && argv[1][0]=='-') { + switch (argv[1][1]) { + case 'h': + usage(argv[0]); + exit(1); + case 'F': + force_lock = 1; + break; + default: + fprintf(stderr,"Invalid option '%s'\n",argv[1]); + exit(1); + } + pipeIx = 2; + } + +#ifdef DEBUG + fprintf(stderr, "%s: pid is : %d\n", argv[0], (int)getpid()); +#endif + + strn_cpy(pipename, sizeof(pipename), + (argv[pipeIx] ? argv[pipeIx] : PIPE_DIR)); + + if(*pipename && pipename[strlen(pipename)-1] == '/') { + /* The user wishes us to find a pipe name in the specified */ + /* directory */ + int highest_pipe_num = 0; + DIR *dirp; + struct dirent *direntp; + + dirp = opendir(pipename); + if(!dirp) { + fprintf(stderr, "Can't access pipe directory %s: %s\n", pipename, strerror(errno)); + exit(1); + } + + /* Check the directory for existing pipes */ + + while((direntp=readdir(dirp)) != NULL) { + if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) { + int num = atoi(direntp->d_name+PIPE_STUBLEN+1); + if(num > highest_pipe_num) + highest_pipe_num = num; + } + } + closedir(dirp); + strn_catf(pipename, sizeof(pipename), (highest_pipe_num?"%s.%d":"%s"), + PIPE_STUBNAME, highest_pipe_num); + } /* if */ + + /* read FIFO */ + sn_printf(FIFO1,sizeof(FIFO1),"%s.r",pipename); + /* write FIFO */ + sn_printf(FIFO2,sizeof(FIFO2),"%s.w",pipename); + + /* Check that nobody is running to_erl on this pipe already */ + if ((wfd = open (FIFO1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { + /* Open as server succeeded -- to_erl is already running! */ + close(wfd); + fprintf(stderr, "Another to_erl process already attached to pipe " + "%s.\n", pipename); + if (force_lock) { + fprintf(stderr, "But we proceed anyway by force (-F).\n"); + } + else { + exit(1); + } + } + + if ((rfd = open (FIFO1, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) { +#ifdef DEBUG + fprintf(stderr, "Could not open FIFO %s for reading.\n", FIFO1); +#endif + fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno)); + exit(1); + } +#ifdef DEBUG + fprintf(stderr, "to_erl: %s opened for reading\n", FIFO1); +#endif + + if ((wfd = open (FIFO2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { +#ifdef DEBUG + fprintf(stderr, "Could not open FIFO %s for writing.\n", FIFO2); +#endif + fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno)); + close(rfd); + exit(1); + } +#ifdef DEBUG + fprintf(stderr, "to_erl: %s opened for writing\n", FIFO2); +#endif + + fprintf(stderr, "Attaching to %s (^D to exit)\n\n", pipename); + + /* Set break handler to our handler */ + signal(SIGINT,handle_ctrlc); + + /* + * Save the current state of the terminal, and set raw mode. + */ + if (tcgetattr(0, &tty_rmode) , 0) { + fprintf(stderr, "Cannot get terminals current mode\n"); + exit(-1); + } + tty_smode = tty_rmode; + tty_eof = '\004'; /* Ctrl+D to exit */ +#ifdef DEBUG + show_terminal_settings(&tty_rmode); +#endif + tty_smode.c_iflag = + 1*BRKINT |/*Signal interrupt on break.*/ + 1*IGNPAR |/*Ignore characters with parity errors.*/ + 1*ISTRIP |/*Strip character.*/ + 0; + +#if 0 +0*IGNBRK |/*Ignore break condition.*/ +0*PARMRK |/*Mark parity errors.*/ +0*INPCK |/*Enable input parity check.*/ +0*INLCR |/*Map NL to CR on input.*/ +0*IGNCR |/*Ignore CR.*/ +0*ICRNL |/*Map CR to NL on input.*/ +0*IUCLC |/*Map upper-case to lower-case on input.*/ +0*IXON |/*Enable start/stop output control.*/ +0*IXANY |/*Enable any character to restart output.*/ +0*IXOFF |/*Enable start/stop input control.*/ +0*IMAXBEL|/*Echo BEL on input line too long.*/ +#endif + + tty_smode.c_oflag = + 1*OPOST |/*Post-process output.*/ + 1*ONLCR |/*Map NL to CR-NL on output.*/ +#ifdef XTABS + 1*XTABS |/*Expand tabs to spaces. (Linux)*/ +#endif +#ifdef OXTABS + 1*OXTABS |/*Expand tabs to spaces. (FreeBSD)*/ +#endif +#ifdef NL0 + 1*NL0 |/*Select newline delays*/ +#endif +#ifdef CR0 + 1*CR0 |/*Select carriage-return delays*/ +#endif +#ifdef TAB0 + 1*TAB0 |/*Select horizontal tab delays*/ +#endif +#ifdef BS0 + 1*BS0 |/*Select backspace delays*/ +#endif +#ifdef VT0 + 1*VT0 |/*Select vertical tab delays*/ +#endif +#ifdef FF0 + 1*FF0 |/*Select form feed delays*/ +#endif + 0; + +#if 0 +0*OLCUC |/*Map lower case to upper on output.*/ +0*OCRNL |/*Map CR to NL on output.*/ +0*ONOCR |/*No CR output at column 0.*/ +0*ONLRET |/*NL performs CR function.*/ +0*OFILL |/*Use fill characters for delay.*/ +0*OFDEL |/*Fill is DEL, else NULL.*/ +0*NL1 | +0*CR1 | +0*CR2 | +0*CR3 | +0*TAB1 | +0*TAB2 | +0*TAB3 |/*Expand tabs to spaces.*/ +0*BS1 | +0*VT1 | +0*FF1 | +#endif + + /* JALI: removed setting the tty_smode.c_cflag flags, since this is not */ + /* advisable if this is a *real* terminal, such as the console. In fact */ + /* this may hang the entire machine, deep, deep down (signalling break */ + /* or toggling the abort switch doesn't help) */ + + tty_smode.c_lflag = + 0; + +#if 0 +0*ISIG |/*Enable signals.*/ +0*ICANON |/*Canonical input (erase and kill processing).*/ +0*XCASE |/*Canonical upper/lower presentation.*/ +0*ECHO |/*Enable echo.*/ +0*ECHOE |/*Echo erase character as BS-SP-BS.*/ +0*ECHOK |/*Echo NL after kill character.*/ +0*ECHONL |/*Echo NL.*/ +0*NOFLSH |/*Disable flush after interrupt or quit.*/ +0*TOSTOP |/*Send SIGTTOU for background output.*/ +0*ECHOCTL|/*Echo control characters as ^char, delete as ^?.*/ +0*ECHOPRT|/*Echo erase character as character erased.*/ +0*ECHOKE |/*BS-SP-BS erase entire line on line kill.*/ +0*FLUSHO |/*Output is being flushed.*/ +0*PENDIN |/*Retype pending input at next read or input character.*/ +0*IEXTEN |/*Enable extended (implementation-defined) functions.*/ +#endif + + tty_smode.c_cc[VMIN] =0;/* Note that VMIN is the same as VEOF! */ + tty_smode.c_cc[VTIME] =0;/* Note that VTIME is the same as VEOL! */ + tty_smode.c_cc[VINTR] =3; + + tcsetattr(0, TCSADRAIN, &tty_smode); + +#ifdef DEBUG + show_terminal_settings(&tty_smode); +#endif + /* + * "Write a ^L to the FIFO which causes the other end to redisplay + * the input line." + * This does not seem to work as was intended in old comment above. + * However, this control character is now (R12B-3) used by run_erl + * to trigger the version handshaking between to_erl and run_erl + * at the start of every new to_erl-session. + */ + + if (write(wfd, "\014", 1) < 0) { + fprintf(stderr, "Error in writing ^L to FIFO.\n"); + } + + /* + * read and write + */ + while (1) { + FD_ZERO(&readfds); + FD_SET(0, &readfds); + FD_SET(rfd, &readfds); + if (select(rfd + 1, &readfds, NULL, NULL, NULL) < 0) { + if (recv_sig) { + FD_ZERO(&readfds); + } + else { + fprintf(stderr, "Error in select.\n"); + break; + } + } + len = 0; + + /* + * Read from terminal and write to FIFO + */ + if (recv_sig) { + switch (recv_sig) { + case SIGINT: + fprintf(stderr, "[Break]\n\r"); + buf[0] = '\003'; + len = 1; + break; + case SIGWINCH: + len = window_size_seq(buf,sizeof(buf)); + break; + default: + fprintf(stderr,"Unexpected signal: %u\n",recv_sig); + } + recv_sig = 0; + } + else if (FD_ISSET(0, &readfds)) { + len = read(0, buf, sizeof(buf)); + if (len <= 0) { + close(rfd); + close(wfd); + if (len < 0) { + fprintf(stderr, "Error in reading from stdin.\n"); + } else { + fprintf(stderr, "[EOF]\n\r"); + } + break; + } + /* check if there is an eof character in input */ + for (i = 0; i < len && buf[i] != tty_eof; i++); + if (buf[i] == tty_eof) { + fprintf(stderr, "[Quit]\n\r"); + break; + } + } + + if (len) { +#ifdef DEBUG + if(write(1, buf, len)); +#endif + if (write_all(wfd, buf, len) != len) { + fprintf(stderr, "Error in writing to FIFO.\n"); + close(rfd); + close(wfd); + break; + } + STATUS("\" OK\r\n"); + } + + /* + * Read from FIFO, write to terminal. + */ + if (FD_ISSET(rfd, &readfds)) { + STATUS("FIFO read: "); + len = read(rfd, buf, BUFSIZ); + if (len < 0 && errno == EAGAIN) { + /* + * No data this time, but the writing end of the FIFO is still open. + * Do nothing. + */ + ; + } else if (len <= 0) { + /* + * Either an error or end of file. In either case, break out + * of the loop. + */ + close(rfd); + close(wfd); + if (len < 0) { + fprintf(stderr, "Error in reading from FIFO.\n"); + } else + fprintf(stderr, "[End]\n\r"); + break; + } else { + if (!got_some) { + if ((len=version_handshake(buf,len,wfd)) < 0) { + close(rfd); + close(wfd); + break; + } + if (protocol_ver >= 1) { + /* Tell run_erl size of terminal window */ + signal(SIGWINCH, handle_sigwinch); + raise(SIGWINCH); + } + got_some = 1; + } + + /* + * We successfully read at least one character. Write what we got. + */ + STATUS("Terminal write: \""); + if (write_all(1, buf, len) != len) { + fprintf(stderr, "Error in writing to terminal.\n"); + close(rfd); + close(wfd); + break; + } + STATUS("\" OK\r\n"); + } + } + } + + /* + * Reset terminal characterstics + * XXX + */ + tcsetattr(0, TCSADRAIN, &tty_rmode); + return 0; +} + +/* Call write() until entire buffer has been written or error. + * Return len or -1. + */ +static int write_all(int fd, const char* buf, int len) +{ + int left = len; + int written; + while (left) { + written = write(fd,buf,left); + if (written < 0) { + return -1; + } + left -= written; + buf += written; + } + return len; +} + +static int window_size_seq(char* buf, size_t bufsz) +{ +#ifdef TIOCGWINSZ + struct winsize ws; + static const char prefix[] = "\033_"; + static const char suffix[] = "\033\\"; + /* This Esc sequence is called "Application Program Command" + and seems suitable to use for our own customized stuff. */ + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) { + int len = sn_printf(buf, bufsz, "%swinsize=%u,%u%s", + prefix, ws.ws_col, ws.ws_row, suffix); + return len; + } +#endif /* TIOCGWINSZ */ + return 0; +} + +/* to_erl run_erl + * | | + * |---------- '\014' -------->| (session start) + * | | + * |<---- "[run_erl v1-0]" ----| (version interval) + * | | + * |--- Esc_"version=1"Esc\ -->| (common version) + * | | + */ +static int version_handshake(char* buf, int len, int wfd) +{ + unsigned re_high=0, re_low; + char *end = find_str(buf,len,"]\n"); + + if (end && sscanf(buf,"[run_erl v%u-%u",&re_high,&re_low)==2) { + char wbuf[30]; + int wlen; + + if (re_low > RUN_ERL_HI_VER || re_high < RUN_ERL_LO_VER) { + fprintf(stderr,"Incompatible versions: to_erl=v%u-%u run_erl=v%u-%u\n", + RUN_ERL_HI_VER, RUN_ERL_LO_VER, re_high, re_low); + return -1; + } + /* Choose highest common version */ + protocol_ver = re_high < RUN_ERL_HI_VER ? re_high : RUN_ERL_HI_VER; + + wlen = sn_printf(wbuf, sizeof(wbuf), "\033_version=%u\033\\", + protocol_ver); + if (write_all(wfd, wbuf, wlen) < 0) { + fprintf(stderr,"Failed to send version handshake\n"); + return -1; + } + end += 2; + len -= (end-buf); + memmove(buf,end,len); + + } + else { /* we assume old run_erl without version handshake */ + protocol_ver = 0; + } + + if (re_high != RUN_ERL_HI_VER) { + fprintf(stderr,"run_erl has different version, " + "using common protocol level %u\n", protocol_ver); + } + + return len; +} + -#include "to_erl_common.h" +#ifdef DEBUG +#define S(x) ((x) > 0 ? 1 : 0) -int main(int argc,char **argv) { - return to_erl(argc,argv); +static void show_terminal_settings(struct termios *t) +{ + fprintf(stderr,"c_iflag:\n"); + fprintf(stderr,"Signal interrupt on break: BRKINT %d\n", S(t->c_iflag & BRKINT)); + fprintf(stderr,"Map CR to NL on input: ICRNL %d\n", S(t->c_iflag & ICRNL)); + fprintf(stderr,"Ignore break condition: IGNBRK %d\n", S(t->c_iflag & IGNBRK)); + fprintf(stderr,"Ignore CR: IGNCR %d\n", S(t->c_iflag & IGNCR)); + fprintf(stderr,"Ignore char with par. err's: IGNPAR %d\n", S(t->c_iflag & IGNPAR)); + fprintf(stderr,"Map NL to CR on input: INLCR %d\n", S(t->c_iflag & INLCR)); + fprintf(stderr,"Enable input parity check: INPCK %d\n", S(t->c_iflag & INPCK)); + fprintf(stderr,"Strip character ISTRIP %d\n", S(t->c_iflag & ISTRIP)); + fprintf(stderr,"Enable start/stop input ctrl IXOFF %d\n", S(t->c_iflag & IXOFF)); + fprintf(stderr,"ditto output ctrl IXON %d\n", S(t->c_iflag & IXON)); + fprintf(stderr,"Mark parity errors PARMRK %d\n", S(t->c_iflag & PARMRK)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_oflag:\n"); + fprintf(stderr,"Perform output processing OPOST %d\n", S(t->c_oflag & OPOST)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_cflag:\n"); + fprintf(stderr,"Ignore modem status lines CLOCAL %d\n", S(t->c_cflag & CLOCAL)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_local:\n"); + fprintf(stderr,"Enable echo ECHO %d\n", S(t->c_lflag & ECHO)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_cc:\n"); + fprintf(stderr,"c_cc[VEOF] %d\n", t->c_cc[VEOF]); } +#endif diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h index f76c4262ca..b402a139f5 100644 --- a/erts/include/internal/ethr_mutex.h +++ b/erts/include/internal/ethr_mutex.h @@ -98,7 +98,7 @@ void LeaveCriticalSection(CRITICAL_SECTION *); #if 0 # define ETHR_MTX_Q_LOCK_SPINLOCK__ # define ETHR_MTX_QLOCK_TYPE__ ethr_spinlock_t -#elif defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS) +#elif defined(ETHR_PTHREADS) # define ETHR_MTX_Q_LOCK_PTHREAD_MUTEX__ # define ETHR_MTX_QLOCK_TYPE__ pthread_mutex_t #elif defined(ETHR_WIN32_THREADS) @@ -211,7 +211,7 @@ struct ethr_cond_ { #endif }; -#elif (defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS)) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) +#elif defined(ETHR_PTHREADS) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) typedef struct ethr_mutex_ ethr_mutex; struct ethr_mutex_ { @@ -355,7 +355,7 @@ void ethr_rwmutex_rwunlock(ethr_rwmutex *); #ifdef ETHR_MTX_HARD_DEBUG #define ETHR_MTX_HARD_ASSERT(A) \ - ((void) ((A) ? 1 : ethr_assert_failed(__FILE__, __LINE__, __func__,#A))) + ((void) ((A) ? 1 : ethr_assert_failed(__FILE__, __LINE__, __func__, #A))) #else #define ETHR_MTX_HARD_ASSERT(A) ((void) 1) #endif @@ -634,7 +634,7 @@ ETHR_INLINE_MTX_FUNC_NAME_(ethr_mutex_unlock)(ethr_mutex *mtx) #endif /* ETHR_TRY_INLINE_FUNCS */ -#elif (defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS)) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) +#elif defined(ETHR_PTHREADS) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) #if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_MUTEX_IMPL__) diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index f9c203e97c..899aa4ad3c 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -193,28 +193,6 @@ typedef DWORD ethr_tsd_key; #define ETHR_YIELD() (Sleep(0), 0) -#elif defined(ETHR_OSE_THREADS) - -#include "ose.h" -#undef NIL - -#if defined(ETHR_HAVE_PTHREAD_H) -#include -#endif - -typedef struct { - PROCESS id; - unsigned int tsd_key_index; - void *res; -} ethr_tid; - -typedef OSPPDKEY ethr_tsd_key; - -#undef ETHR_HAVE_ETHR_SIG_FUNCS - -/* Out own RW mutexes are probably faster, but use OSEs mutexes */ -#define ETHR_USE_OWN_RWMTX_IMPL__ - #else /* No supported thread lib found */ #ifdef ETHR_NO_SUPP_THR_LIB_NOT_FATAL @@ -382,19 +360,7 @@ extern ethr_runtime_t ethr_runtime__; #include "ethr_atomics.h" /* The atomics API */ -#if defined (ETHR_OSE_THREADS) -static ETHR_INLINE void -ose_yield(void) -{ - if (get_ptype(current_process()) == OS_PRI_PROC) { - set_pri(get_pri(current_process())); - } else { - delay(1); - } -} -#endif - -#if defined(__GNUC__) && !defined(ETHR_OSE_THREADS) +#if defined(__GNUC__) # ifndef ETHR_SPIN_BODY # if defined(__i386__) || defined(__x86_64__) # define ETHR_SPIN_BODY __asm__ __volatile__("rep;nop" : : : "memory") @@ -410,20 +376,9 @@ ose_yield(void) # ifndef ETHR_SPIN_BODY # define ETHR_SPIN_BODY do {YieldProcessor();ETHR_COMPILER_BARRIER;} while(0) # endif -#elif defined(ETHR_OSE_THREADS) -# ifndef ETHR_SPIN_BODY -# define ETHR_SPIN_BODY ose_yield() -# else -# error "OSE should use ose_yield()" -# endif #endif -#ifndef ETHR_OSE_THREADS #define ETHR_YIELD_AFTER_BUSY_LOOPS 50 -#else -#define ETHR_YIELD_AFTER_BUSY_LOOPS 0 -#endif - #ifndef ETHR_SPIN_BODY # define ETHR_SPIN_BODY ETHR_COMPILER_BARRIER @@ -446,18 +401,13 @@ ose_yield(void) # else # define ETHR_YIELD() (pthread_yield(), 0) # endif -# elif defined(ETHR_OSE_THREADS) -# define ETHR_YIELD() (ose_yield(), 0) # else # define ETHR_YIELD() (ethr_compiler_barrier(), 0) # endif #endif -#if defined(VALGRIND) || defined(ETHR_OSE_THREADS) -/* mutex as fallback for spinlock for VALGRIND and OSE. - OSE cannot use spinlocks as processes working on the - same execution unit have a tendency to deadlock. - */ +#if defined(VALGRIND) +/* mutex as fallback for spinlock for VALGRIND. */ # undef ETHR_HAVE_NATIVE_SPINLOCKS # undef ETHR_HAVE_NATIVE_RWSPINLOCKS #else @@ -504,16 +454,9 @@ typedef struct { int detached; /* boolean (default false) */ int suggested_stack_size; /* kilo words (default sys dependent) */ char *name; /* max 14 char long (default no-name) */ -#ifdef ETHR_OSE_THREADS - U32 coreNo; -#endif } ethr_thr_opts; -#if defined(ETHR_OSE_THREADS) -#define ETHR_THR_OPTS_DEFAULT_INITER {0, -1, NULL, 0} -#else #define ETHR_THR_OPTS_DEFAULT_INITER {0, -1, NULL} -#endif #if !defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) # define ETHR_NEED_SPINLOCK_PROTOTYPES__ @@ -627,8 +570,6 @@ typedef struct ethr_ts_event_ ethr_ts_event; /* Needed by ethr_mutex.h */ # include "win/ethr_event.h" #elif defined(ETHR_PTHREADS) # include "pthread/ethr_event.h" -#elif defined(ETHR_OSE_THREADS) -# include "ose/ethr_event.h" #endif int ethr_set_main_thr_status(int, int); @@ -718,37 +659,6 @@ ETHR_INLINE_FUNC_NAME_(ethr_leave_ts_event)(ethr_ts_event *tsep) #endif -#elif defined (ETHR_OSE_THREADS) - -#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHREAD_IMPL__) - -extern ethr_tsd_key ethr_ts_event_key__; - -static ETHR_INLINE ethr_ts_event * -ETHR_INLINE_FUNC_NAME_(ethr_get_ts_event)(void) -{ - ethr_ts_event *tsep = *(ethr_ts_event**)ose_get_ppdata(ethr_ts_event_key__); - if (!tsep) { - int res = ethr_get_tmp_ts_event__(&tsep); - if (res != 0) - ETHR_FATAL_ERROR__(res); - ETHR_ASSERT(tsep); - } - return tsep; -} - -static ETHR_INLINE void -ETHR_INLINE_FUNC_NAME_(ethr_leave_ts_event)(ethr_ts_event *tsep) -{ - if (tsep->iflgs & ETHR_TS_EV_TMP) { - int res = ethr_free_ts_event__(tsep); - if (res != 0) - ETHR_FATAL_ERROR__(res); - } -} - -#endif - #endif #include "ethr_mutex.h" /* Need atomic declarations and tse */ diff --git a/erts/include/internal/ose/ethr_event.h b/erts/include/internal/ose/ethr_event.h deleted file mode 100644 index c18f30aa4a..0000000000 --- a/erts/include/internal/ose/ethr_event.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2009-2011. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -/* - * Author: Rickard Green - */ - -//#define USE_PTHREAD_API - -#define ETHR_EVENT_OFF_WAITER__ -1L -#define ETHR_EVENT_OFF__ 1L -#define ETHR_EVENT_ON__ 0L - -#ifdef USE_PTHREAD_API - -typedef struct { - ethr_atomic32_t state; - pthread_mutex_t mtx; - pthread_cond_t cnd; -} ethr_event; - -#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__) - -static void ETHR_INLINE -ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) -{ - ethr_sint32_t val; - val = ethr_atomic32_xchg_mb(&e->state, ETHR_EVENT_ON__); - if (val == ETHR_EVENT_OFF_WAITER__) { - int res = pthread_mutex_lock(&e->mtx); - if (res != 0) - ETHR_FATAL_ERROR__(res); - res = pthread_cond_signal(&e->cnd); - if (res != 0) - ETHR_FATAL_ERROR__(res); - res = pthread_mutex_unlock(&e->mtx); - if (res != 0) - ETHR_FATAL_ERROR__(res); - } -} - -static void ETHR_INLINE -ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) -{ - ethr_atomic32_set(&e->state, ETHR_EVENT_OFF__); - ETHR_MEMORY_BARRIER; -} - -#endif - -#else - -typedef struct { - ethr_atomic32_t state; - PROCESS proc; -} ethr_event; - -#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__) - -static void ETHR_INLINE -ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) -{ - ethr_sint32_t val = ethr_atomic32_xchg_mb(&e->state, ETHR_EVENT_ON__); - if (val == ETHR_EVENT_OFF_WAITER__) { -#ifdef DEBUG - OSFSEMVAL fsem_val = get_fsem(e->proc); - - /* There is a race in this assert. - This is because the state is set before the wait call in wait__. - We hope that a delay of 10 ms is enough */ - if (fsem_val == 0) - delay(10); - ETHR_ASSERT(get_fsem(e->proc) == -1); -#endif - signal_fsem(e->proc); - } -} - -static void ETHR_INLINE -ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) -{ - ethr_atomic32_set(&e->state, ETHR_EVENT_OFF__); - ETHR_MEMORY_BARRIER; -} - -#endif - -#endif - -int ethr_event_init(ethr_event *e); -int ethr_event_destroy(ethr_event *e); -int ethr_event_wait(ethr_event *e); -int ethr_event_swait(ethr_event *e, int spincount); -#if !defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__) -void ethr_event_set(ethr_event *e); -void ethr_event_reset(ethr_event *e); -#endif diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c index 1262c50718..053217304b 100644 --- a/erts/lib_src/common/erl_misc_utils.c +++ b/erts/lib_src/common/erl_misc_utils.c @@ -160,8 +160,6 @@ erts_milli_sleep(long ms) if (ms > 0) { #ifdef __WIN32__ Sleep((DWORD) ms); -#elif defined(__OSE__) - delay(ms); #else struct timeval tv; tv.tv_sec = ms / 1000; @@ -320,10 +318,6 @@ erts_cpu_info_update(erts_cpu_info_t *cpuinfo) online = 0; #endif } -#elif defined(__OSE__) - online = ose_num_cpus(); - configured = ose_num_cpus(); - available = ose_num_cpus(); #endif if (online > configured) diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c index 0cbb1b2fb8..56fecf81b8 100644 --- a/erts/lib_src/common/ethr_aux.c +++ b/erts/lib_src/common/ethr_aux.c @@ -207,18 +207,7 @@ ethr_init_common__(ethr_init_data *id) ethr_min_stack_size__ = ETHR_B2KW(ethr_min_stack_size__); -#ifdef __OSE__ - /* For supervisor processes, OSE adds a number of bytes to the requested stack. With this - * addition, the resulting size must not exceed the largest available stack size. The number - * of bytes that will be added is configured in the monolith and can therefore not be - * specified here. We simply assume that it is less than 0x1000. The available stack sizes - * are configured in the .lmconf file and the largest one is usually 65536 bytes. - * Consequently, the requested stack size is limited to 0xF000. - */ - ethr_max_stack_size__ = 0xF000; -#else ethr_max_stack_size__ = 32*1024*1024; -#endif #if SIZEOF_VOID_P == 8 ethr_max_stack_size__ *= 2; #endif @@ -664,10 +653,6 @@ ETHR_IMPL_NORETURN__ ethr_fatal_error__(const char *file, int ethr_assert_failed(const char *file, int line, const char *func, char *a) { fprintf(stderr, "%s:%d: %s(): Assertion failed: %s\n", file, line, func, a); -#ifdef __OSE__ - ramlog_printf("%d: %s:%d: %s(): Assertion failed: %s\n", - current_process(),file, line, func, a); -#endif ethr_abort__(); return 0; } diff --git a/erts/lib_src/common/ethr_mutex.c b/erts/lib_src/common/ethr_mutex.c index 72aa34ec1c..a596e6c31c 100644 --- a/erts/lib_src/common/ethr_mutex.c +++ b/erts/lib_src/common/ethr_mutex.c @@ -1250,7 +1250,7 @@ ethr_cond_wait(ethr_cond *cnd, ethr_mutex *mtx) return 0; } -#elif (defined(ETHR_PTHREADS) || defined(ETHR_OSE_THREADS)) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) +#elif defined(ETHR_PTHREADS) && !defined(ETHR_DBG_WIN_MTX_WITH_PTHREADS) /* -- pthread mutex and condition variables -------------------------------- */ int diff --git a/erts/lib_src/ose/ethr_event.c b/erts/lib_src/ose/ethr_event.c deleted file mode 100644 index 24ea191c4b..0000000000 --- a/erts/lib_src/ose/ethr_event.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2009-2010. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -/* - * Author: Rickard Green - */ - -#define ETHR_INLINE_FUNC_NAME_(X) X ## __ -#define ETHR_EVENT_IMPL__ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "ethread.h" - -#ifdef USE_PTHREAD_API - -int -ethr_event_init(ethr_event *e) -{ - int res; - ethr_atomic32_init(&e->state, ETHR_EVENT_OFF__); - res = pthread_mutex_init(&e->mtx, NULL); - if (res != 0) - return res; - res = pthread_cond_init(&e->cnd, NULL); - if (res != 0) { - pthread_mutex_destroy(&e->mtx); - return res; - } - return 0; -} - -int -ethr_event_destroy(ethr_event *e) -{ - int res; - res = pthread_mutex_destroy(&e->mtx); - if (res != 0) - return res; - res = pthread_cond_destroy(&e->cnd); - if (res != 0) - return res; - return 0; -} - -static ETHR_INLINE int -wait__(ethr_event *e, int spincount) -{ - int sc = spincount; - ethr_sint32_t val; - int res, ulres; - int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; - - if (spincount < 0) - ETHR_FATAL_ERROR__(EINVAL); - - while (1) { - val = ethr_atomic32_read(&e->state); - if (val == ETHR_EVENT_ON__) - return 0; - if (sc == 0) - break; - sc--; - ETHR_SPIN_BODY; - if (--until_yield == 0) { - until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; - res = ETHR_YIELD(); - if (res != 0) - ETHR_FATAL_ERROR__(res); - } - } - - if (val != ETHR_EVENT_OFF_WAITER__) { - val = ethr_atomic32_cmpxchg(&e->state, - ETHR_EVENT_OFF_WAITER__, - ETHR_EVENT_OFF__); - if (val == ETHR_EVENT_ON__) - return 0; - ETHR_ASSERT(val == ETHR_EVENT_OFF__); - } - - ETHR_ASSERT(val == ETHR_EVENT_OFF_WAITER__ - || val == ETHR_EVENT_OFF__); - - res = pthread_mutex_lock(&e->mtx); - if (res != 0) - ETHR_FATAL_ERROR__(res); - - while (1) { - - val = ethr_atomic32_read(&e->state); - if (val == ETHR_EVENT_ON__) - break; - - res = pthread_cond_wait(&e->cnd, &e->mtx); - if (res == EINTR) - break; - if (res != 0) - ETHR_FATAL_ERROR__(res); - } - - ulres = pthread_mutex_unlock(&e->mtx); - if (ulres != 0) - ETHR_FATAL_ERROR__(ulres); - - return res; /* 0 || EINTR */ -} - -#else -/* --- OSE implementation of events ---------------------------- */ - -#ifdef DEBUG -union SIGNAL { - SIGSELECT signo; -}; -#endif - -int -ethr_event_init(ethr_event *e) -{ - ethr_atomic32_init(&e->state, ETHR_EVENT_OFF__); - e->proc = current_process(); - return 0; -} - -int -ethr_event_destroy(ethr_event *e) -{ - return 0; -} - -static ETHR_INLINE int -wait__(ethr_event *e, int spincount) -{ - int sc = spincount; - int res; - int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; - - if (spincount < 0) - ETHR_FATAL_ERROR__(EINVAL); - - ETHR_ASSERT(e->proc == current_process()); - ETHR_ASSERT(get_fsem(current_process()) == 0); - - while (1) { - ethr_sint32_t val; - while (1) { - val = ethr_atomic32_read(&e->state); - if (val == ETHR_EVENT_ON__) - return 0; - if (sc == 0) - break; - sc--; - ETHR_SPIN_BODY; - if (--until_yield == 0) { - until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; - res = ETHR_YIELD(); - if (res != 0) - ETHR_FATAL_ERROR__(res); - } - } - if (val != ETHR_EVENT_OFF_WAITER__) { - val = ethr_atomic32_cmpxchg(&e->state, - ETHR_EVENT_OFF_WAITER__, - ETHR_EVENT_OFF__); - if (val == ETHR_EVENT_ON__) - return 0; - ETHR_ASSERT(val == ETHR_EVENT_OFF__); - } - - wait_fsem(1); - - ETHR_ASSERT(get_fsem(current_process()) == 0); - } -} - -#endif - -void -ethr_event_reset(ethr_event *e) -{ - ethr_event_reset__(e); -} - -void -ethr_event_set(ethr_event *e) -{ - ethr_event_set__(e); -} - -int -ethr_event_wait(ethr_event *e) -{ - return wait__(e, 0); -} - -int -ethr_event_swait(ethr_event *e, int spincount) -{ - return wait__(e, spincount); -} diff --git a/erts/lib_src/ose/ethread.c b/erts/lib_src/ose/ethread.c deleted file mode 100644 index dc16acdd08..0000000000 --- a/erts/lib_src/ose/ethread.c +++ /dev/null @@ -1,833 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -/* - * Description: OSE implementation of the ethread library - * Author: Lukas Larsson - */ - - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "stdio.h" -#ifdef ETHR_TIME_WITH_SYS_TIME -# include "time.h" -# include "sys/time.h" -#else -# ifdef ETHR_HAVE_SYS_TIME_H -# include "sys/time.h" -# else -# include "time.h" -# endif -#endif -#include "sys/types.h" -#include "unistd.h" - -#include "limits.h" - -#define ETHR_INLINE_FUNC_NAME_(X) X ## __ -#define ETHREAD_IMPL__ - -#include "ethread.h" -#include "ethr_internal.h" - -#include "erl_printf.h" -#include "efs.h" -#include "ose.h" - -#include "ose_spi.h" - -#include "string.h" -#include "ctype.h" -#include "stdlib.h" - -#ifndef ETHR_HAVE_ETHREAD_DEFINES -#error Missing configure defines -#endif - -#define ETHR_INVALID_TID_ID -1 - -#define DEFAULT_PRIO_NAME "ERTS_ETHR_DEFAULT_PRIO" - -/* Set the define to 1 to get some logging */ -#if 0 -#include "ramlog.h" -#define LOG(output) ramlog_printf output -#else -#define LOG(output) -#endif - -static ethr_tid main_thr_tid; -static const char* own_tid_key = "ethread_own_tid"; -ethr_tsd_key ethr_ts_event_key__; - -#define ETHREADWRAPDATASIG 1 - -/* Init data sent to thr_wrapper() */ -typedef struct { - SIGSELECT sig_no; - ethr_ts_event *tse; - ethr_tid *tid; - ethr_sint32_t result; - void *(*thr_func)(void *); - void *arg; - void *prep_func_res; - const char *name; -} ethr_thr_wrap_data__; - -union SIGNAL { - SIGSELECT sig_no; - ethr_thr_wrap_data__ data; -}; - -#define ETHR_GET_OWN_TID__ ((ethr_tid *) get_envp(current_process(),\ - own_tid_key)) - -/* - * -------------------------------------------------------------------------- - * Static functions - * -------------------------------------------------------------------------- - */ - -/* Will retrive the instrinsic name by removing the 'prefix' and the - * suffix from 'name'. - * The 'prefix' is given as an inparameter. If NULL or an empty string no - * prefix will be removed. - * If 'strip_suffix' is 1 suffixes in the form of '_123' will be removed. - * Will return a pointer to a newly allocated buffer containing the intrinsic - * name in uppercase characters. - * The caller must remember to free this buffer when no lnger needed. - */ -static char * -ethr_intrinsic_name(const char *name, const char *prefix, int strip_suffix) -{ - const char *start = name; - const char *end = name + strlen(name); - char *intrinsic_name = NULL; - int i; - - if (name == NULL) { - LOG(("ERTS - ethr_intrinsic_namNo input name.\n")); - return NULL; - } - - /* take care of the prefix */ - if ((prefix != NULL) && (*prefix != '\0')) { - const char *found = strstr(name, prefix); - - if (found == name) { - /* found the prefix at the beginning */ - start += strlen(prefix); - } - } - - /* take care of the suffix */ - if (strip_suffix) { - const char *suffix_start = strrchr(start, '_'); - - if (suffix_start != NULL) { - const char *ch; - int only_numbers = 1; - - for (ch = suffix_start + 1; *ch != '\0'; ch++) { - if (strchr("0123456789", *ch) == NULL) { - only_numbers = 0; - break; - } - } - - if (only_numbers) { - end = suffix_start; - } - } - } - - intrinsic_name = malloc(end - start + 1); - for (i = 0; (start + i) < end; i++) { - intrinsic_name[i] = toupper(start[i]); - } - intrinsic_name[i] = '\0'; - - return intrinsic_name; -} - -static char * -ethr_get_amended_env(const char *name, const char *prefix, const char *suffix) -{ - unsigned len; - char *env_name = NULL; - char *env_value = NULL; - - if (name == NULL) { - return NULL; - } - - len = strlen(name); - - if (prefix != NULL) { - len += strlen(prefix); - } - - if (suffix != NULL) { - len += strlen(suffix); - } - - env_name = malloc(len + 1); - sprintf(env_name, "%s%s%s", (prefix != NULL) ? prefix : "", - name, - (suffix != NULL) ? suffix : ""); - env_value = get_env(get_bid(current_process()), env_name); - - if (env_value == NULL) { - LOG(("ERTS - ethr_get_amended_env(): %s environment variable not present\n", env_name)); - } else { - LOG(("ERTS - ethr_get_amended_env(): Found %s environment variable: %s.\n", env_name, env_value)); - } - free(env_name); - - return env_value; -} - -/* Reads the environment variable derived from 'name' and interprets it as as an - * OSE priority. If successfull it will update 'out_prio'. - * Returns: 0 if successfull - * -1 orherwise. - */ -static int -ethr_get_prio(const char *name, OSPRIORITY *out_prio) -{ - int rc = -1; - char *intrinsic_name = NULL; - char *prio_env = NULL; - long prio; - char *endptr = NULL; - - LOG(("ERTS - ethr_get_prio(): name: %s.\n", name)); - - intrinsic_name = ethr_intrinsic_name(name, NULL, 1); - LOG(("ERTS - ethr_get_prio(): Intrinsic name: %s.\n", intrinsic_name)); - - prio_env = ethr_get_amended_env(intrinsic_name, "ERTS_", "_PRIO"); - if (prio_env == NULL) { - goto fini; - } - - prio = efs_str_to_long(prio_env, (const char **)&endptr); - if (endptr != NULL) { - LOG(("ERTS - ethr_get_prio(): Environment varible for '%s' includes " - "non-numerical characters: '%s'.\n", intrinsic_name, prio_env)); - goto fini; - } - - if ((prio < 0) || (prio > 32)) { - LOG(("ERTS - ethr_get_prio(): prio for '%s' (%d) is out of bounds (0-32).\n", - intrinsic_name, prio)); - goto fini; - } - - /* Success */ - *out_prio = (OSPRIORITY)prio; - rc = 0; - -fini: - if (intrinsic_name != NULL) { - free(intrinsic_name); - } - if (prio_env != NULL) { - free_buf((union SIGNAL **) &prio_env); - } - - return rc; -} - -static PROCESS blockId(void) { - static PROCESS bid = (PROCESS)0; - - /* For now we only use the same block. */ - /* if (bid == 0) { - bid = create_block("Erlang-VM", 0, 0, 0, 0); - } - return bid; */ - return 0; -} - -static void thr_exit_cleanup(ethr_tid *tid, void *res) -{ - - ETHR_ASSERT(tid == ETHR_GET_OWN_TID__); - - tid->res = res; - - ethr_run_exit_handlers__(); - ethr_ts_event_destructor__((void *) ethr_get_tse__()); -} - -//static OS_PROCESS(thr_wrapper); -static OS_PROCESS(thr_wrapper) -{ - ethr_tid my_tid; - ethr_sint32_t result; - void *res; - void *(*thr_func)(void *); - void *arg; - ethr_ts_event *tsep = NULL; - -#ifdef DEBUG - { - PROCESS pid = current_process(); - - const char *execMode; - - PROCESS bid = get_bid(pid); - - /* In the call below, 16 is a secret number provided by frbr that makes - * the function return current domain. */ - OSADDRESS domain = get_pid_info(current_process(), 16); - -#ifdef HAVE_OSE_SPI_H - execMode = get_pid_info(pid, OSE_PI_SUPERVISOR) - ? "Supervisor" - : "User"; -#else - execMode = "unknown"; -#endif - - fprintf(stderr,"[0x%x] New process. Bid:0x%x, domain:%d, exec mode:%s\n", - current_process(), bid, domain, execMode); - } -#endif - - { - SIGSELECT sigsel[] = {1,ETHREADWRAPDATASIG}; - union SIGNAL *init_msg = receive(sigsel); - - thr_func = init_msg->data.thr_func; - arg = init_msg->data.arg; - - result = (ethr_sint32_t) ethr_make_ts_event__(&tsep); - - if (result == 0) { - tsep->iflgs |= ETHR_TS_EV_ETHREAD; - my_tid = *init_msg->data.tid; - set_envp(current_process(), own_tid_key, (OSADDRESS)&my_tid); - if (ethr_thr_child_func__) - ethr_thr_child_func__(init_msg->data.prep_func_res); - } - - init_msg->data.result = result; - - send(&init_msg,sender(&init_msg)); - } - - /* pthread mutex api says we have to do this */ - signal_fsem(current_process()); - ETHR_ASSERT(get_fsem(current_process()) == 0); - - res = result == 0 ? (*thr_func)(arg) : NULL; - - ethr_thr_exit(&res); -} - -/* internal exports */ - -int ethr_set_tse__(ethr_ts_event *tsep) -{ - return ethr_tsd_set(ethr_ts_event_key__,(void *) tsep); -} - -ethr_ts_event *ethr_get_tse__(void) -{ - return (ethr_ts_event *) ethr_tsd_get(ethr_ts_event_key__); -} - -#if defined(ETHR_PPC_RUNTIME_CONF__) - -static int -ppc_init__(void) -{ - int pid; - - - ethr_runtime__.conf.have_lwsync = 0; - - return 0; -} - -#endif - -#if defined(ETHR_X86_RUNTIME_CONF__) - -void -ethr_x86_cpuid__(int *eax, int *ebx, int *ecx, int *edx) -{ -#if ETHR_SIZEOF_PTR == 4 - int have_cpuid; - /* - * If it is possible to toggle eflags bit 21, - * we have the cpuid instruction. - */ - __asm__ ("pushf\n\t" - "popl %%eax\n\t" - "movl %%eax, %%ecx\n\t" - "xorl $0x200000, %%eax\n\t" - "pushl %%eax\n\t" - "popf\n\t" - "pushf\n\t" - "popl %%eax\n\t" - "movl $0x0, %0\n\t" - "xorl %%ecx, %%eax\n\t" - "jz no_cpuid\n\t" - "movl $0x1, %0\n\t" - "no_cpuid:\n\t" - : "=r"(have_cpuid) - : - : "%eax", "%ecx", "cc"); - if (!have_cpuid) { - *eax = *ebx = *ecx = *edx = 0; - return; - } -#endif -#if ETHR_SIZEOF_PTR == 4 && defined(__PIC__) && __PIC__ - /* - * When position independet code is used in 32-bit mode, the B register - * is used for storage of global offset table address, and we may not - * use it as input or output in an asm. We need to save and restore the - * B register explicitly (for some reason gcc doesn't provide this - * service to us). - */ - __asm__ ("pushl %%ebx\n\t" - "cpuid\n\t" - "movl %%ebx, %1\n\t" - "popl %%ebx\n\t" - : "=a"(*eax), "=r"(*ebx), "=c"(*ecx), "=d"(*edx) - : "0"(*eax) - : "cc"); -#else - __asm__ ("cpuid\n\t" - : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) - : "0"(*eax) - : "cc"); -#endif -} - -#endif /* ETHR_X86_RUNTIME_CONF__ */ - -/* - * -------------------------------------------------------------------------- - * Exported functions - * -------------------------------------------------------------------------- - */ - -int -ethr_init(ethr_init_data *id) -{ - int res; - - if (!ethr_not_inited__) - return EINVAL; - - -#if defined(ETHR_PPC_RUNTIME_CONF__) - res = ppc_init__(); - if (res != 0) - goto error; -#endif - - res = ethr_init_common__(id); - if (res != 0) - goto error; - - main_thr_tid.id = current_process(); - main_thr_tid.tsd_key_index = 0; - - set_envp(current_process(),own_tid_key,(OSADDRESS)&main_thr_tid); - signal_fsem(current_process()); - - - ETHR_ASSERT(&main_thr_tid == ETHR_GET_OWN_TID__); - - ethr_not_inited__ = 0; - - ethr_tsd_key_create(ðr_ts_event_key__,"ethread_tse"); - - return 0; - error: - ethr_not_inited__ = 1; - return res; - -} - -int -ethr_late_init(ethr_late_init_data *id) -{ - int res = ethr_late_init_common__(id); - if (res != 0) - return res; - ethr_not_completely_inited__ = 0; - return res; -} - -int -ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, - ethr_thr_opts *opts) -{ - int res; - int use_stack_size = (opts && opts->suggested_stack_size >= 0 - ? opts->suggested_stack_size - : 0x200 /* Use system default */); - OSPRIORITY use_prio; - char *use_name; - char default_thr_name[20]; - static int no_of_thr = 0; - cpuid_t use_core; - - union SIGNAL *init_msg; - SIGSELECT sigsel[] = {1,ETHREADWRAPDATASIG}; - void *prep_func_res; - - - if (opts != NULL) { - LOG(("ERTS - ethr_thr_create(): opts supplied: name: %s, coreNo: %u.\n", - opts->name, opts->coreNo)); - use_name = opts->name; - use_core = opts->coreNo; - if (0 != ethr_get_prio(use_name, &use_prio)) { - if (0 != ethr_get_prio("DEFAULT", &use_prio)) { - use_prio = get_pri(current_process()); - LOG(("ERTS - ethr_thr_create(): Using current process' prio: %d.\n", use_prio)); - } else { - LOG(("ERTS - ethr_thr_create(): Using default prio: %d.\n", use_prio)); - } - } else { - LOG(("ERTS - ethr_thr_create(): Using configured prio: %d.\n", use_prio)); - } - } else { - LOG(("ERTS - ethr_thr_create(): opts not supplied. Using defaults.\n")); - no_of_thr++; - sprintf(default_thr_name, "ethread_%d", no_of_thr); - use_name = default_thr_name; - use_core = ose_cpu_id(); - - if (0 != ethr_get_prio("DEFAULT", &use_prio)) { - use_prio = get_pri(current_process()); - LOG(("ERTS - ethr_thr_create(): Using current process' prio: %d.\n", use_prio)); - } - } - -#ifdef ETHR_MODIFIED_DEFAULT_STACK_SIZE - if (use_stack_size < 0) - use_stack_size = ETHR_MODIFIED_DEFAULT_STACK_SIZE; -#endif - -#if ETHR_XCHK - if (ethr_not_completely_inited__) { - ETHR_ASSERT(0); - return EACCES; - } - if (!tid || !func) { - ETHR_ASSERT(0); - return EINVAL; - } -#endif - - if (use_stack_size >= 0) { - size_t suggested_stack_size = (size_t) use_stack_size; - size_t stack_size; -#ifdef ETHR_DEBUG - suggested_stack_size /= 2; /* Make sure we got margin */ -#endif -#ifdef ETHR_STACK_GUARD_SIZE - /* The guard is at least on some platforms included in the stack size - passed when creating threads */ - suggested_stack_size += ETHR_B2KW(ETHR_STACK_GUARD_SIZE); -#endif - - if (suggested_stack_size < ethr_min_stack_size__) - stack_size = ETHR_KW2B(ethr_min_stack_size__); - else if (suggested_stack_size > ethr_max_stack_size__) - stack_size = ETHR_KW2B(ethr_max_stack_size__); - else - stack_size = ETHR_PAGE_ALIGN(ETHR_KW2B(suggested_stack_size)); - use_stack_size = stack_size; - } - - init_msg = alloc(sizeof(ethr_thr_wrap_data__), ETHREADWRAPDATASIG); - - /* Call prepare func if it exist */ - if (ethr_thr_prepare_func__) - init_msg->data.prep_func_res = ethr_thr_prepare_func__(); - else - init_msg->data.prep_func_res = NULL; - - LOG(("ERTS - ethr_thr_create(): Process [0x%x] is creating '%s', coreNo = %u, prio:%u\n", - current_process(), use_name, use_core, use_prio)); - - tid->id = create_process(OS_PRI_PROC, use_name, thr_wrapper, - use_stack_size, use_prio, 0, - get_bid(current_process()), NULL, 0, 0); - if (ose_bind_process(tid->id, use_core)) { - LOG(("ERTS - ethr_thr_create(): Bound pid 0x%x (%s) to core no %u.\n", - tid->id, use_name, use_core)); - } else { - LOG(("ERTS - ethr_thr_create(): Failed binding pid 0x%x (%s) to core no %u.\n", - tid->id, use_name, use_core)); - } - - /*FIXME!!! Normally this shouldn't be used in shared mode. Still there is - * a problem with stdin fd in fd_ processes which should be further - * investigated */ - efs_clone(tid->id); - - tid->tsd_key_index = 0; - tid->res = NULL; - - init_msg->data.tse = ethr_get_ts_event(); - init_msg->data.thr_func = func; - init_msg->data.arg = arg; - init_msg->data.tid = tid; - init_msg->data.name = opts->name; - - send(&init_msg, tid->id); - - start(tid->id); - init_msg = receive(sigsel); - - res = init_msg->data.result; - prep_func_res = init_msg->data.prep_func_res; - - free_buf(&init_msg); - /* Cleanup... */ - - if (ethr_thr_parent_func__) - ethr_thr_parent_func__(prep_func_res); - - LOG(("ERTS - ethr_thr_create(): Exiting.\n")); - return res; -} - -int -ethr_thr_join(ethr_tid tid, void **res) -{ - SIGSELECT sigsel[] = {1,OS_ATTACH_SIG}; -#if ETHR_XCHK - if (ethr_not_inited__) { - ETHR_ASSERT(0); - return EACCES; - } -#endif - - if (tid.id == ETHR_INVALID_TID_ID) - return EINVAL; - - attach(NULL,tid.id); - receive(sigsel); - - if (res) - *res = tid.res; - - return 0; -} - -int -ethr_thr_detach(ethr_tid tid) -{ -#if ETHR_XCHK - if (ethr_not_inited__) { - ETHR_ASSERT(0); - return EACCES; - } -#endif - return 0; -} - -void -ethr_thr_exit(void *res) -{ - ethr_tid *tid; -#if ETHR_XCHK - if (ethr_not_inited__) { - ETHR_ASSERT(0); - return; - } -#endif - tid = ETHR_GET_OWN_TID__; - if (!tid) { - ETHR_ASSERT(0); - kill_proc(current_process()); - } - thr_exit_cleanup(tid, res); - /* Harakiri possible? */ - kill_proc(current_process()); -} - -ethr_tid -ethr_self(void) -{ - ethr_tid *tid; -#if ETHR_XCHK - if (ethr_not_inited__) { - ethr_tid dummy_tid = {ETHR_INVALID_TID_ID, 0, NULL}; - ETHR_ASSERT(0); - return dummy_tid; - } -#endif - tid = ETHR_GET_OWN_TID__; - if (!tid) { - ethr_tid dummy_tid = {ETHR_INVALID_TID_ID, 0, NULL}; - return dummy_tid; - } - return *tid; -} - -int -ethr_equal_tids(ethr_tid tid1, ethr_tid tid2) -{ - return tid1.id == tid2.id && tid1.id != ETHR_INVALID_TID_ID; -} - - -/* - * Thread specific events - */ - -ethr_ts_event * -ethr_get_ts_event(void) -{ - return ethr_get_ts_event__(); -} - -void -ethr_leave_ts_event(ethr_ts_event *tsep) -{ - ethr_leave_ts_event__(tsep); -} - -/* - * Thread specific data - */ - -int -ethr_tsd_key_create(ethr_tsd_key *keyp, char *keyname) -{ - -#if ETHR_XCHK - if (ethr_not_inited__) { - ETHR_ASSERT(0); - return EACCES; - } - if (!keyp) { - ETHR_ASSERT(0); - return EINVAL; - } -#endif - - ose_create_ppdata(keyname,keyp); - - return 0; -} - -int -ethr_tsd_key_delete(ethr_tsd_key key) -{ -#if ETHR_XCHK - if (ethr_not_inited__) { - ETHR_ASSERT(0); - return EACCES; - } -#endif - /* Not possible to delete ppdata */ - - return 0; -} - -int -ethr_tsd_set(ethr_tsd_key key, void *value) -{ - void **ppdp; -#if ETHR_XCHK - if (ethr_not_inited__) { - ETHR_ASSERT(0); - return EACCES; - } -#endif - ppdp = (void **)ose_get_ppdata(key); - *ppdp = value; - return 0; -} - -void * -ethr_tsd_get(ethr_tsd_key key) -{ -#if ETHR_XCHK - if (ethr_not_inited__) { - ETHR_ASSERT(0); - return NULL; - } -#endif - return *(void**)ose_get_ppdata(key); -} - -/* - * Signal functions - */ - -#if ETHR_HAVE_ETHR_SIG_FUNCS - -int ethr_sigmask(int how, const sigset_t *set, sigset_t *oset) -{ -#if ETHR_XCHK - if (ethr_not_inited__) { - ETHR_ASSERT(0); - return EACCES; - } - if (!set && !oset) { - ETHR_ASSERT(0); - return EINVAL; - } -#endif - return pthread_sigmask(how, set, oset); -} - -int ethr_sigwait(const sigset_t *set, int *sig) -{ -#if ETHR_XCHK - if (ethr_not_inited__) { - ETHR_ASSERT(0); - return EACCES; - } - if (!set || !sig) { - ETHR_ASSERT(0); - return EINVAL; - } -#endif - if (sigwait(set, sig) < 0) - return errno; - return 0; -} - -#endif /* #if ETHR_HAVE_ETHR_SIG_FUNCS */ - -ETHR_IMPL_NORETURN__ -ethr_abort__(void) -{ - abort(); -} -- cgit v1.2.3 From d0143499cd9ff1c1cf029ff33c65c908229536c7 Mon Sep 17 00:00:00 2001 From: Luca Favatella Date: Thu, 2 Jul 2015 23:57:37 +0100 Subject: Teach Dialyzer arity of funs with literal arity Re-insert logic for `erlang:make_fun/3` in `erl_bif_types`. It had been removed in bd941f5 while type spec-ing `erlang.erl`. Type spec in `erlang.erl` cannot express arity of returned fun based on value of argument hence re-introducing logic in `erl_bif_types`. Re-definition of logic in `erl_bif_types` follows approach in 9d870a0. --- erts/preloaded/src/erlang.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 291356c7b1..b4c9de9e1e 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1974,6 +1974,7 @@ localtime_to_universaltime(_Localtime, _IsDst) -> %% CHECK! Why the strange very thorough specification of the error %% condition with disallowed arity in erl_bif_types? %% Not documented +%% Shadowed by erl_bif_types: erlang:make_fun/3 -spec erlang:make_fun(Module, Function, Arity) -> function() when Module :: atom(), Function :: atom(), -- cgit v1.2.3 From ed830e036f1bb5c2f1f67224897a31d2fa544c91 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 14 Jul 2015 13:05:39 +0200 Subject: erts: Don't abort when a system process is terminated --- erts/emulator/beam/erl_process.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index ee1dd36d48..db37243321 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -12126,7 +12126,8 @@ erts_do_exit_process(Process* p, Eterm reason) #endif if (p->static_flags & ERTS_STC_FLG_SYSTEM_PROC) - erl_exit(1, "System process %T terminated: %T\n", p->common.id, reason); + erl_exit(ERTS_DUMP_EXIT, "System process %T terminated: %T\n", + p->common.id, reason); #ifdef ERTS_SMP ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); -- cgit v1.2.3 From 7db3c1bff2850f395e85c58eeda3f0dae1a218f4 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 14 Jul 2015 16:39:33 +0200 Subject: erts: Update major version number --- erts/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index 1012f5eafd..9e4248a668 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 7.0 +VSN = 8.0 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 2e61f98dd41ce7328aebc27debd78845afdc0dba Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Fri, 31 Jul 2015 21:15:13 +0200 Subject: erts: fix binary_to_integer boundary case erlang:binary_to_integer/1 and /2 fail to detect invalid input consisting of a single + or - sign but nothing else. For an input like <<"+">> they return 0, while list_to_integer/1 correctly signals a badarg for "+". Fixed by checking if the input is empty after the initial +/- sign processing. Added a test case which fails without this fix but passes with it. Thanks to "niku" for reporting the issue. --- erts/emulator/beam/big.c | 3 +++ erts/emulator/test/num_bif_SUITE.erl | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 044bf6a34e..15bcd44fb9 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -2618,6 +2618,9 @@ Eterm erts_chars_to_integer(Process *BIF_P, char *bytes, size--; } + if (size == 0) + goto bytebuf_to_integer_1_error; + if (size < SMALL_DIGITS && base <= 10) { /* * * Take shortcut if we know that all chars are '0' < b < '9' and diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index f07f79b83d..90b6a36262 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -429,7 +429,7 @@ t_string_to_integer(Config) when is_list(Config) -> list_to_binary(Value))), {'EXIT', {badarg, _}} = (catch erlang:list_to_integer(Value)) - end,["1.0"," 1"," -1",""]), + end,["1.0"," 1"," -1","","+"]), % Custom base error cases lists:foreach(fun({Value,Base}) -> -- cgit v1.2.3 From 79b504ca9134121a63c049292d8636d3cd0d99b4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 10 Jul 2015 16:56:15 +0200 Subject: Teach smp VM how to deal with crash of a linked trace port Problem: The sys-msg-dispather crashes the VM when trying to send exit signals from the links of the terminating trace port. If try-lock of the linked process fails, a pending exit is scheduled and erts_scheduler_data() is then called to find "my" run queue. But sys-msg-dispatcher is not a scheduler and has no scheduler data, hence SEGV. Fix: If not a scheduler and we cannot get process locks, schedule process in its previous run-queue. --- erts/emulator/beam/erl_process.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b6ad8575cf..26ef96ea74 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11195,10 +11195,14 @@ save_pending_exiter(Process *p) { ErtsProcList *plp; ErtsRunQueue *rq; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); - rq = erts_get_runq_current(NULL); + if (!esdp) + rq = RUNQ_READ_RQ(&p->run_queue); + else + rq = esdp->run_queue; plp = proclist_create(p); @@ -11215,6 +11219,7 @@ save_pending_exiter(Process *p) else #endif wake_scheduler(rq); + } #endif @@ -11411,23 +11416,21 @@ send_exit_signal(Process *c_p, /* current process if and only if (need_locks && erts_smp_proc_trylock(rp, need_locks) == EBUSY) { /* ... but we havn't got all locks on it ... */ - save_pending_exiter(rp); + save_pending_exiter(rp); /* * The pending exit will be discovered when next * process is scheduled in */ - goto set_pending_exit; - } - else { - /* ...and we have all locks on it... */ - *rp_locks = ERTS_PROC_LOCKS_ALL; - set_proc_exiting(rp, - state, - (is_immed(rsn) - ? rsn - : copy_object(rsn, rp)), - NULL); + goto set_pending_exit; } + /* ...and we have all locks on it... */ + *rp_locks = ERTS_PROC_LOCKS_ALL; + set_proc_exiting(rp, + state, + (is_immed(rsn) + ? rsn + : copy_object(rsn, rp)), + NULL); } else { /* Process running... */ -- cgit v1.2.3 From 1497898b7d680de86d64bdec133003288adee820 Mon Sep 17 00:00:00 2001 From: Daniel Goertzen Date: Tue, 11 Aug 2015 12:02:50 -0500 Subject: fix unused parameter warning in enif_make_pid --- erts/emulator/beam/erl_nif_api_funcs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index f93152c921..2f2180e1aa 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -543,7 +543,7 @@ static ERL_NIF_INLINE ERL_NIF_TERM enif_make_list9(ErlNifEnv* env, #ifndef enif_make_pid -# define enif_make_pid(ENV, PID) ((const ERL_NIF_TERM)((PID)->pid)) +# define enif_make_pid(ENV, PID) ((void)(ENV),(const ERL_NIF_TERM)((PID)->pid)) #if SIZEOF_LONG == 8 # define enif_get_int64 enif_get_long -- cgit v1.2.3 From 02380778fd2a9d6af85865a89ef0747351cc0f88 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 13 Aug 2015 14:52:20 +0200 Subject: erts: Make sure to unlock status lock when setting process prio --- erts/emulator/beam/erl_process.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 7b3d12ce09..98f01bbce9 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -9177,6 +9177,10 @@ erts_set_process_priority(Process *p, Eterm value) a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); } while (a != e); + + if (slocked) + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + } switch (oprio) { -- cgit v1.2.3 From d2bb1ca56dcd0ab8e5b43c4368409317c3ddd4db Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 18 Aug 2015 16:18:42 +0200 Subject: erts: Fix binary memory leak in ttsl driver --- erts/emulator/drivers/unix/ttsl_drv.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index 0f773b69fb..53146e71f0 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -720,6 +720,7 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun } driver_enq_bin(ttysl_port,putcbuf,0,putcpos); + driver_free_binary(putcbuf); if (sz == 0) { for (;;) { @@ -1207,6 +1208,7 @@ static int outc(int c) putcbuf->orig_bytes[putcpos++] = c; if (putcpos == putclen) { driver_enq_bin(ttysl_port,putcbuf,0,putclen); + driver_free_binary(putcbuf); putcpos = 0; putclen = TTY_BUFFSIZE; putcbuf = driver_alloc_binary(BUFSIZ); -- cgit v1.2.3 From ba8740af39b7d4c612b2ee55e3bad6fbdcaf1418 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Mon, 10 Aug 2015 08:34:28 -0400 Subject: Handle ERRNO_BLOCK in fd_driver async functions Several users on erlang-questions have reported problems with recent releases where output to standard_error causes standard_error_sup to die from receiving an unexpected eagain error. In the fd_driver, change the fd_async() function to handle EINTR, and change fd_ready_async() to handle ERRNO_BLOCK. Add a new test to standard_error_SUITE to generate output to standard_error and ensure that standard_error_sup does not die. Thanks to Kota Uenishi for contributing the test case. --- erts/emulator/sys/unix/sys.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index b036b20b7b..8d7da3e47e 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -2527,7 +2527,7 @@ fd_async(void *async_data) SysIOVec *iov0; SysIOVec *iov; int iovlen; - int err; + int err = 0; /* much of this code is stolen from efile_drv:invoke_writev */ driver_pdl_lock(dd->blocking->pdl); iov0 = driver_peekq(dd->port_num, &iovlen); @@ -2542,8 +2542,11 @@ fd_async(void *async_data) memcpy(iov,iov0,iovlen*sizeof(SysIOVec)); driver_pdl_unlock(dd->blocking->pdl); - res = writev(dd->ofd, iov, iovlen); - err = errno; + do { + res = writev(dd->ofd, iov, iovlen); + } while (res < 0 && errno == EINTR); + if (res < 0) + err = errno; erts_free(ERTS_ALC_T_SYS_WRITE_BUF, iov); } @@ -2582,7 +2585,12 @@ void fd_ready_async(ErlDrvData drv_data, return /* 0; */; } } else if (dd->blocking->res < 0) { - driver_failure_posix(port_num, dd->blocking->err); + if (dd->blocking->err == ERRNO_BLOCK) { + set_busy_port(port_num, 1); + /* still data left to write in queue */ + driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL); + } else + driver_failure_posix(port_num, dd->blocking->err); return; /* -1; */ } return; /* 0; */ -- cgit v1.2.3 From 0dcd7fc911e4c0b6eca255e9bcfb0e58654326bf Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 18 Aug 2015 16:39:13 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 23 +++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index ab6291614c..bed1ac463d 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,29 @@

This document describes the changes made to the ERTS application.

+
Erts 7.0.3 + +
Fixed Bugs and Malfunctions + + +

+ Fixed a binary memory leak when printing to shell using + the tty driver (i.e. not -oldshell).

+

+ Own Id: OTP-12941

+
+ +

+ Fix a bug where the standard error port sometimes crashes + with eagain as the reason.

+

+ Own Id: OTP-12942

+
+
+
+ +
+
Erts 7.0.2
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 478f581f13..38b9a13e63 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 7.0.2 +VSN = 7.0.3 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From f237a833c2361183199cf4f2ce96672d03e290f3 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Thu, 20 Aug 2015 13:16:59 +0200 Subject: Take out unused code that results in a gcc warning These ifdef-ed lines were once upon a time needed, but the recent changes in the time mechanism of OTP render them unused and result in a warning on architectures which are not 64-bit. --- erts/emulator/hipe/hipe_native_bif.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index 98bda43f0e..688378b2fe 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -93,9 +93,6 @@ BIF_RETTYPE hipe_set_timeout(BIF_ALIST_1) { Process* p = BIF_P; Eterm timeout_value = BIF_ARG_1; -#if !defined(ARCH_64) - Uint time_val; -#endif /* XXX: This should be converted to follow BEAM conventions, * but that requires some compiler changes. * -- cgit v1.2.3 From 08f8b454596d51de367e5559b7104300c2a7b954 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 19 Aug 2015 16:15:32 +0200 Subject: Improve choice of clock sources at build time - Documented the configure switch --with-clock-resolution= - Changed default clock source for OS system time on Darwin to gettimeofday(). In order to use clock_get_time(CALENDER_CLOCK, ...) on Darwin, the user has to pass --with-clock-resolution=high when configuring the build. --- erts/aclocal.m4 | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 0714ce6030..7634cc0228 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -940,16 +940,16 @@ AC_DEFUN(ERL_WALL_CLOCK, erl_wall_clock_low_resolution=no erl_wall_clock_id= - case $erl_cv_clock_gettime_wall_$1-$erl_cv_mach_clock_get_time_wall-$ac_cv_func_gettimeofday-$host_os in - *-*-*-win32) + case $1-$erl_cv_clock_gettime_wall_$1-$erl_cv_mach_clock_get_time_wall-$ac_cv_func_gettimeofday-$host_os in + *-*-*-*-win32) erl_wall_clock_func=WindowsAPI erl_wall_clock_low_resolution=yes ;; - no-yes-*-*) + high_resolution-no-yes-*-*) erl_wall_clock_func=mach_clock_get_time erl_wall_clock_id=CALENDAR_CLOCK ;; - CLOCK_*-*-*-*) + *-CLOCK_*-*-*-*) erl_wall_clock_func=clock_gettime erl_wall_clock_id=$erl_cv_clock_gettime_wall_$1 for low_res_id in $low_resolution_clock_gettime_wall; do @@ -959,7 +959,7 @@ AC_DEFUN(ERL_WALL_CLOCK, fi done ;; - no-no-yes-*) + *-no-*-yes-*) erl_wall_clock_func=gettimeofday ;; *) @@ -2219,10 +2219,10 @@ AS_HELP_STRING([--with-clock-gettime-monotonic-id=CLOCKID], [specify clock id to use with clock_gettime() for monotonic time)])) AC_ARG_ENABLE(prefer-elapsed-monotonic-time-during-suspend, - AS_HELP_STRING([--enable-prefer-elapsed-monotonic-time-during-suspend], - [Prefer an OS monotonic time source with elapsed time during suspend]) - AS_HELP_STRING([--disable-prefer-elapsed-monotonic-time-during-suspend], - [Do not prefer an OS monotonic time source with elapsed time during suspend]), +AS_HELP_STRING([--enable-prefer-elapsed-monotonic-time-during-suspend], + [Prefer an OS monotonic time source with elapsed time during suspend]) +AS_HELP_STRING([--disable-prefer-elapsed-monotonic-time-during-suspend], + [Do not prefer an OS monotonic time source with elapsed time during suspend]), [ case "$enableval" in yes) prefer_elapsed_monotonic_time_during_suspend=yes ;; *) prefer_elapsed_monotonic_time_during_suspend=no ;; @@ -2296,6 +2296,9 @@ case "$erl_wall_clock_func-$erl_wall_clock_id-$with_clock_gettime_realtime_id" i esac case $erl_wall_clock_func in + none) + AC_MSG_ERROR([No wall clock source found]) + ;; mach_clock_get_time) AC_DEFINE(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME, [1], [Define if you want to implement erts_os_system_time() using mach clock_get_time()]) ;; -- cgit v1.2.3 From b5eba8b61ce3ea0058ba37df288738586c68d6ac Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 20 Aug 2015 20:18:57 +0200 Subject: erts: Change THE_NON_VALUE to not be hard coded in hipe compiler Instead ask running VM for the value of THE_NON_VALUE, which is different between opt and debug VM. Same hipe compiler can now compile for both opt and debug VM. --- erts/emulator/hipe/hipe_mkliterals.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index aa12df2932..b7009aec77 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -269,9 +269,6 @@ static const struct literal { /* freason codes */ { "FREASON_TRAP", TRAP }, - /* special Erlang constants */ - { "THE_NON_VALUE", (int)THE_NON_VALUE }, - /* funs */ #ifdef HIPE { "EFE_NATIVE_ADDRESS", offsetof(struct erl_fun_entry, native_address) }, @@ -526,6 +523,8 @@ static const struct rts_param rts_params[] = { { 49, "P_MSG_FIRST", 1, offsetof(struct process, msg.first) }, { 50, "P_MSG_SAVE", 1, offsetof(struct process, msg.save) }, { 51, "P_CALLEE_EXP", 1, offsetof(struct process, hipe.u.callee_exp) }, + + { 52, "THE_NON_VALUE", 1, (int)THE_NON_VALUE }, }; #define NR_PARAMS ARRAY_SIZE(rts_params) -- cgit v1.2.3 From f76ba62c7869bb7bbda27446d0a9f7214062db4f Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 25 Aug 2015 12:40:37 +0200 Subject: erts: bool is a reserved word, use boolean instead --- erts/emulator/beam/beam_debug.c | 6 +++--- erts/emulator/beam/bif.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index c774a70d4c..90985e4f53 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -79,7 +79,7 @@ erts_debug_breakpoint_2(BIF_ALIST_2) { Process* p = BIF_P; Eterm MFA = BIF_ARG_1; - Eterm bool = BIF_ARG_2; + Eterm boolean = BIF_ARG_2; Eterm* tp; Eterm mfa[3]; int i; @@ -87,7 +87,7 @@ erts_debug_breakpoint_2(BIF_ALIST_2) Eterm res; BpFunctions f; - if (bool != am_true && bool != am_false) + if (boolean != am_true && boolean != am_false) goto error; if (is_not_tuple(MFA)) { @@ -124,7 +124,7 @@ erts_debug_breakpoint_2(BIF_ALIST_2) erts_smp_thr_progress_block(); erts_bp_match_functions(&f, mfa, specified); - if (bool == am_true) { + if (boolean == am_true) { erts_set_debug_break(&f); erts_install_breakpoints(&f); erts_commit_staged_bp(); diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 4e3a1cef69..0bd46a2dae 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -616,7 +616,7 @@ erts_queue_monitor_message(Process *p, } static BIF_RETTYPE -local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int bool) +local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean) { BIF_RETTYPE ret; Process *rp; @@ -634,7 +634,7 @@ local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int bool) if (!rp) { erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK); p_locks &= ~ERTS_PROC_LOCK_LINK; - if (bool) + if (boolean) ret = am_false; else erts_queue_monitor_message(p, &p_locks, @@ -643,7 +643,7 @@ local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int bool) else { ASSERT(rp != p); - if (bool) + if (boolean) ret = am_true; erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, target, NIL); -- cgit v1.2.3 From 5a02ed2f3505f5ed3282c6b43963e19e63e49905 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 25 Aug 2015 15:47:29 +0200 Subject: Fix ethread events with timeout Lots of pthread platforms unnecessarily falled back on the pipe/select solution. This since we tried to use the same monotonic clock source for pthread_cond_timedwait() as used by OS monotonic time. This has been fixed on most platforms by using another clock source. Darwin can however not use pthread_cond_timedwait() with monotonic clock source and has to use the pipe/select solution. On darwin we now use select with _DARWIN_UNLIMITED_SELECT in order to be able to handle a large amount of file descriptors. --- erts/aclocal.m4 | 6 +- erts/emulator/beam/erl_process.c | 7 ++- erts/emulator/beam/erl_threads.h | 10 +++ erts/emulator/drivers/unix/unix_efile.c | 10 +-- erts/include/internal/pthread/ethr_event.h | 11 ++++ erts/include/internal/win/ethr_event.h | 1 + erts/lib_src/pthread/ethr_event.c | 97 +++++++++++++++++++++++++++--- erts/lib_src/win/ethr_event.c | 6 ++ 8 files changed, 130 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 0714ce6030..7550561737 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -750,8 +750,8 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, prefer_resolution_clock_gettime_monotonic="$2" ;; *) - check_msg="" - prefer_resolution_clock_gettime_monotonic= + check_msg="custom " + prefer_resolution_clock_gettime_monotonic="$2" ;; esac @@ -1472,7 +1472,7 @@ AC_ARG_WITH(with_sparc_memory_order, LM_CHECK_THR_LIB ERL_INTERNAL_LIBS -ERL_MONOTONIC_CLOCK(high_resolution, undefined, no) +ERL_MONOTONIC_CLOCK(try_find_pthread_compatible, CLOCK_HIGHRES CLOCK_MONOTONIC, no) case $erl_monotonic_clock_func in clock_gettime) diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index ee1dd36d48..58698173be 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -7938,11 +7938,16 @@ sched_thread_func(void *vesdp) ErtsThrPrgrCallbacks callbacks; ErtsSchedulerData *esdp = vesdp; Uint no = esdp->no; +#ifdef ERTS_SMP + erts_tse_t *tse; +#endif erts_sched_init_time_sup(esdp); #ifdef ERTS_SMP - ERTS_SCHED_SLEEP_INFO_IX(no - 1)->event = erts_tse_fetch(); + tse = erts_tse_fetch(); + erts_tse_prepare_timed(tse); + ERTS_SCHED_SLEEP_INFO_IX(no - 1)->event = tse; callbacks.arg = (void *) esdp->ssi; callbacks.wakeup = thr_prgr_wakeup; callbacks.prepare_wait = thr_prgr_prep_wait; diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index 5347979372..34f91e2ec8 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -649,6 +649,7 @@ ERTS_GLB_INLINE void erts_tsd_set(erts_tsd_key_t key, void *value); ERTS_GLB_INLINE void * erts_tsd_get(erts_tsd_key_t key); ERTS_GLB_INLINE erts_tse_t *erts_tse_fetch(void); ERTS_GLB_INLINE void erts_tse_return(erts_tse_t *ep); +ERTS_GLB_INLINE void erts_tse_prepare_timed(erts_tse_t *ep); ERTS_GLB_INLINE void erts_tse_set(erts_tse_t *ep); ERTS_GLB_INLINE void erts_tse_reset(erts_tse_t *ep); ERTS_GLB_INLINE int erts_tse_wait(erts_tse_t *ep); @@ -3461,6 +3462,15 @@ ERTS_GLB_INLINE void erts_tse_return(erts_tse_t *ep) #endif } +ERTS_GLB_INLINE void erts_tse_prepare_timed(erts_tse_t *ep) +{ +#ifdef USE_THREADS + int res = ethr_event_prepare_timed(&((ethr_ts_event *) ep)->event); + if (res != 0) + erts_thr_fatal_error(res, "prepare timed"); +#endif +} + ERTS_GLB_INLINE void erts_tse_set(erts_tse_t *ep) { #ifdef USE_THREADS diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index 06ba986044..46eccc6568 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -45,10 +45,10 @@ #endif #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) -#define DARWIN 1 +#define __DARWIN__ 1 #endif -#if defined(DARWIN) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE) +#if defined(__DARWIN__) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE) #include #endif @@ -476,11 +476,11 @@ efile_fsync(Efile_error *errInfo, /* Where to return error codes. */ #ifdef NO_FSYNC undefined fsync /* XXX: Really? */ #else -#if defined(DARWIN) && defined(F_FULLFSYNC) +#if defined(__DARWIN__) && defined(F_FULLFSYNC) return check_error(fcntl(fd, F_FULLFSYNC), errInfo); #else return check_error(fsync(fd), errInfo); -#endif /* DARWIN */ +#endif /* __DARWIN__ */ #endif /* NO_FSYNC */ } @@ -962,7 +962,7 @@ efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd, retval = len; } } while (len == SENDFILE_CHUNK_SIZE); -#elif defined(DARWIN) +#elif defined(__DARWIN__) int retval; off_t len; do { diff --git a/erts/include/internal/pthread/ethr_event.h b/erts/include/internal/pthread/ethr_event.h index 74cfa68e16..deb4b29686 100644 --- a/erts/include/internal/pthread/ethr_event.h +++ b/erts/include/internal/pthread/ethr_event.h @@ -83,12 +83,22 @@ ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) #elif defined(ETHR_PTHREADS) /* --- Posix mutex/cond pipe/select implementation of events ---------------- */ +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +# define __DARWIN__ 1 +#endif + +#ifdef __DARWIN__ +typedef struct ethr_event_fdsets___ ethr_event_fdsets__; +#endif typedef struct { ethr_atomic32_t state; pthread_mutex_t mtx; pthread_cond_t cnd; int fd[2]; +#ifdef __DARWIN__ + ethr_event_fdsets__ *fdsets; +#endif } ethr_event; #define ETHR_EVENT_OFF_WAITER_SELECT__ ((ethr_sint32_t) -2) @@ -148,6 +158,7 @@ ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) #endif int ethr_event_init(ethr_event *e); +int ethr_event_prepare_timed(ethr_event *e); int ethr_event_destroy(ethr_event *e); int ethr_event_wait(ethr_event *e); int ethr_event_swait(ethr_event *e, int spincount); diff --git a/erts/include/internal/win/ethr_event.h b/erts/include/internal/win/ethr_event.h index bf110e10f9..458565b9ea 100644 --- a/erts/include/internal/win/ethr_event.h +++ b/erts/include/internal/win/ethr_event.h @@ -56,6 +56,7 @@ ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) #endif int ethr_event_init(ethr_event *e); +int ethr_event_prepare_timed(ethr_event *e); int ethr_event_destroy(ethr_event *e); int ethr_event_wait(ethr_event *e); int ethr_event_swait(ethr_event *e, int spincount); diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c index ba664236f6..0629b4dfcd 100644 --- a/erts/lib_src/pthread/ethr_event.c +++ b/erts/lib_src/pthread/ethr_event.c @@ -29,6 +29,13 @@ #include "config.h" #endif +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +# define __DARWIN__ 1 +#endif +#ifdef __DARWIN__ +# define _DARWIN_UNLIMITED_SELECT +#endif + #include "ethread.h" #undef ETHR_INCLUDE_MONOTONIC_CLOCK__ #define ETHR_INCLUDE_MONOTONIC_CLOCK__ @@ -55,6 +62,12 @@ ethr_event_init(ethr_event *e) return 0; } +int +ethr_event_prepare_timed(ethr_event *e) +{ + return 0; +} + int ethr_event_destroy(ethr_event *e) { @@ -171,6 +184,17 @@ return_event_on: #include #include +#ifdef __DARWIN__ + +struct ethr_event_fdsets___ { + fd_set *rsetp; + fd_set *esetp; + size_t mem_size; + fd_mask mem[1]; +}; + +#endif + static void setup_nonblocking_pipe(ethr_event *e) { @@ -188,6 +212,35 @@ setup_nonblocking_pipe(ethr_event *e) flgs = fcntl(e->fd[1], F_GETFL, 0); fcntl(e->fd[1], F_SETFL, flgs | O_NONBLOCK); + +#ifndef __DARWIN__ + if (e->fd[0] >= FD_SETSIZE) + ETHR_FATAL_ERROR__(ENOTSUP); +#else + { + int nmasks; + ethr_event_fdsets__ *fdsets; + size_t mem_size; + + nmasks = (e->fd[0]+NFDBITS)/NFDBITS; + mem_size = 2*nmasks*sizeof(fd_mask); + if (mem_size < 2*sizeof(fd_set)) { + mem_size = 2*sizeof(fd_set); + nmasks = mem_size/(2*sizeof(fd_mask)); + } + + fdsets = malloc(sizeof(ethr_event_fdsets__) + + mem_size + - sizeof(fd_mask)); + if (!fdsets) + ETHR_FATAL_ERROR__(ENOMEM); + fdsets->rsetp = (fd_set *) (char *) &fdsets->mem[0]; + fdsets->esetp = (fd_set *) (char *) &fdsets->mem[nmasks]; + fdsets->mem_size = mem_size; + e->fdsets = fdsets; + } +#endif + ETHR_MEMBAR(ETHR_StoreStore); } @@ -252,6 +305,19 @@ ethr_event_init(ethr_event *e) e->fd[0] = e->fd[1] = ETHR_EVENT_INVALID_FD__; +#ifdef __DARWIN__ + e->fdsets = NULL; +#endif + + return 0; +} + +int +ethr_event_prepare_timed(ethr_event *e) +{ + if (e->fd[0] == ETHR_EVENT_INVALID_FD__) + setup_nonblocking_pipe(e); + return 0; } @@ -263,13 +329,14 @@ ethr_event_destroy(ethr_event *e) close(e->fd[0]); close(e->fd[1]); } +#ifdef __DARWIN__ + if (e->fdsets) + free(e->fdsets); +#endif res = pthread_mutex_destroy(&e->mtx); if (res != 0) return res; - res = pthread_cond_destroy(&e->cnd); - if (res != 0) - return res; - return 0; + return pthread_cond_destroy(&e->cnd); } static ETHR_INLINE int @@ -403,8 +470,10 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) int fd; int sres; ssize_t rres; - fd_set rset; - fd_set eset; +#ifndef __DARWIN__ + fd_set rset, eset; +#endif + fd_set *rsetp, *esetp; struct timeval select_timeout; #ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME @@ -457,12 +526,22 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) ETHR_ASSERT(act == val); } + +#ifdef __DARWIN__ + rsetp = e->fdsets->rsetp; + esetp = e->fdsets->esetp; + memset((void *) &e->fdsets->mem[0], 0, e->fdsets->mem_size); +#else FD_ZERO(&rset); - FD_SET(fd, &rset); FD_ZERO(&eset); - FD_SET(fd, &eset); + rsetp = &rset; + esetp = &eset; +#endif + + FD_SET(fd, rsetp); + FD_SET(fd, esetp); - sres = select(fd + 1, &rset, NULL, &eset, &select_timeout); + sres = select(fd + 1, rsetp, NULL, esetp, &select_timeout); if (sres == 0) res = ETIMEDOUT; else { diff --git a/erts/lib_src/win/ethr_event.c b/erts/lib_src/win/ethr_event.c index c88c8784a2..6951a216c5 100644 --- a/erts/lib_src/win/ethr_event.c +++ b/erts/lib_src/win/ethr_event.c @@ -46,6 +46,12 @@ ethr_event_init(ethr_event *e) return 0; } +int +ethr_event_prepare_timed(ethr_event *e) +{ + return 0; +} + int ethr_event_destroy(ethr_event *e) { -- cgit v1.2.3 From 1b8dcd4741399a9190d361bb517130163365680e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Fri, 28 Aug 2015 14:29:41 +0200 Subject: Add missing behavior/behaviours to absform docs --- erts/doc/src/absform.xml | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 547d5e583d..df2553ced3 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -70,6 +70,10 @@ Rep(D) = . If F is an attribute , then Rep(F) = . + If F is an attribute , then + Rep(F) = . + If F is an attribute , then + Rep(F) = . If F is an attribute , then Rep(F) = . If F is an attribute , then -- cgit v1.2.3 From c0694699e20ba9985d842c0efaeefbc457bc518c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 28 Aug 2015 18:49:42 +0200 Subject: erts: Beautify hipe wrapper macro --- erts/emulator/hipe/hipe_bif_list.m4 | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index b3bd5e4357..1d36ccdb5d 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -269,21 +269,22 @@ noproc_primop_interface_1(nbif_atomic_inc, hipe_atomic_inc) /* BIFs that disable GC while trapping are called via a wrapper * to reserve stack space for the "trap frame". */ -define(CFUN,`ifelse($1,term_to_binary_1,hipe_wrapper_term_to_binary_1, -ifelse($1,term_to_binary_2,hipe_wrapper_term_to_binary_2, -ifelse($1,binary_to_term_1,hipe_wrapper_binary_to_term_1, -ifelse($1,binary_to_term_2,hipe_wrapper_binary_to_term_2, -ifelse($1,binary_to_list_1,hipe_wrapper_binary_to_list_1, -ifelse($1,binary_to_list_3,hipe_wrapper_binary_to_list_3, -ifelse($1,bitstring_to_list_1,hipe_wrapper_bitstring_to_list_1, -ifelse($1,list_to_binary_1,hipe_wrapper_list_to_binary_1, -ifelse($1,iolist_to_binary_1,hipe_wrapper_iolist_to_binary_1, -ifelse($1,binary_list_to_bin_1,hipe_wrapper_binary_list_to_bin_1, -ifelse($1,list_to_bitstring_1,hipe_wrapper_list_to_bitstring_1, -ifelse($1,send_2,hipe_wrapper_send_2, -ifelse($1,send_3,hipe_wrapper_send_3, -ifelse($1,ebif_bang_2,hipe_wrapper_ebif_bang_2, -$1))))))))))))))') +define(CFUN,`ifelse( +$1, term_to_binary_1, hipe_wrapper_$1, +$1, term_to_binary_2, hipe_wrapper_$1, +$1, binary_to_term_1, hipe_wrapper_$1, +$1, binary_to_term_2, hipe_wrapper_$1, +$1, binary_to_list_1, hipe_wrapper_$1, +$1, binary_to_list_3, hipe_wrapper_$1, +$1, bitstring_to_list_1, hipe_wrapper_$1, +$1, list_to_binary_1, hipe_wrapper_$1, +$1, iolist_to_binary_1, hipe_wrapper_$1, +$1, binary_list_to_bin_1, hipe_wrapper_$1, +$1, list_to_bitstring_1, hipe_wrapper_$1, +$1, send_2, hipe_wrapper_$1, +$1, send_3, hipe_wrapper_$1, +$1, ebif_bang_2, hipe_wrapper_$1, +$1)') define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))') include(TARGET/`erl_bif_list.h') -- cgit v1.2.3 From 90e0bad70affc23228e3d11d1131aba615895dd5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 28 Aug 2015 18:08:04 +0200 Subject: erts: Fix hipe bug for maps:merge/2 Add forgotten HIPE_WRAPPER_BIF_DISABLE_GC which could lead to stack-heap overrun if unlucky with the yielding during maps:merge when called by native hipe code. --- erts/emulator/beam/erl_map.c | 5 +++++ erts/emulator/hipe/hipe_bif_list.m4 | 1 + 2 files changed, 6 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index a91e36e3c5..ff2a355309 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -32,7 +32,9 @@ #include "global.h" #include "erl_process.h" #include "error.h" +#define ERL_WANT_HIPE_BIF_WRAPPER__ #include "bif.h" +#undef ERL_WANT_HIPE_BIF_WRAPPER__ #include "erl_binary.h" #include "erl_map.h" @@ -952,8 +954,11 @@ BIF_RETTYPE maps_keys_1(BIF_ALIST_1) { BIF_P->fvalue = BIF_ARG_1; BIF_ERROR(BIF_P, BADMAP); } + /* maps:merge/2 */ +HIPE_WRAPPER_BIF_DISABLE_GC(maps_merge, 2) + BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { if (is_flatmap(BIF_ARG_1)) { if (is_flatmap(BIF_ARG_2)) { diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 1d36ccdb5d..6aa0c9a32e 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -284,6 +284,7 @@ $1, list_to_bitstring_1, hipe_wrapper_$1, $1, send_2, hipe_wrapper_$1, $1, send_3, hipe_wrapper_$1, $1, ebif_bang_2, hipe_wrapper_$1, +$1, maps_merge_2, hipe_wrapper_$1, $1)') define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))') -- cgit v1.2.3 From 6e3a0870ebd992ed410298e859066894134be9f6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 31 Aug 2015 16:56:02 +0200 Subject: erts,hipe,dialyzer: Fix hipe checkum of target runtime system Main problem: A faulty HIPE_LITERAL_CRC was not detected by the loader. Strangeness #1: Dialyzer should ask the hipe compiler about the target checksum, not an internal bif. Strangeness #2: The HIPE_SYSTEM_CRC checksum was based on the HIPE_LITERALS_CRC checksum. Solution: New HIPE_ERTS_CHECKSUM which is an bxor of the two (now independent) HIPE_LITERALS_CRC and HIPE_SYSTEM_CRC. HIPE_LITERALS_CRC represents values that are assumed to stay constant for different VM configurations of the same arch, and are therefor hard coded into the hipe compiler. HIPE_SYSTEM_CRC represents values that may differ between VM variants. By default the hipe compiler asks the running VM for this checksum, in order to create beam files for the same running VM. The hipe compiler can be configured (with "make XCOMP=yes ...") to create beam files for another VM variant, in which case HIPE_SYSTEM_CRC is also hard coded. ToDo: Treat all erts properties the same. Either ask the running VM or hard coded into hipe (if XCOMP=yes). This will simplify and reduce the risk of dangerous mismatches. One concern might be the added overhead from more frequent calls to hipe_bifs:get_rts_param. --- erts/emulator/hipe/hipe_bif0.c | 2 +- erts/emulator/hipe/hipe_mkliterals.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index efbe951ce5..cc68e1f74d 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1741,7 +1741,7 @@ BIF_RETTYPE hipe_bifs_check_crc_1(BIF_ALIST_1) if (!term_to_Uint(BIF_ARG_1, &crc)) BIF_ERROR(BIF_P, BADARG); - if (crc == HIPE_SYSTEM_CRC) + if (crc == HIPE_ERTS_CHECKSUM) BIF_RET(am_true); BIF_RET(am_false); } diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index b7009aec77..dfa5313739 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -542,6 +542,8 @@ static void compute_crc(void) crc_value = crc_update_int(crc_value, &literals[i].value); crc_value &= 0x07FFFFFF; literals_crc = crc_value; + + crc_value = crc_init(); for (i = 0; i < NR_PARAMS; ++i) if (rts_params[i].is_defined) crc_value = crc_update_int(crc_value, &rts_params[i].value); @@ -627,6 +629,7 @@ static int do_c(FILE *fp, const char* this_exe) print_params(fp, c_define_param); fprintf(fp, "#define HIPE_LITERALS_CRC %uU\n", literals_crc); fprintf(fp, "#define HIPE_SYSTEM_CRC %uU\n", system_crc); + fprintf(fp, "#define HIPE_ERTS_CHECKSUM (HIPE_LITERALS_CRC ^ HIPE_SYSTEM_CRC)\n"); fprintf(fp, "\n"); fprintf(fp, "#define RTS_PARAMS_CASES"); print_params(fp, c_case_param); @@ -644,12 +647,14 @@ static int do_e(FILE *fp, const char* this_exe) fprintf(fp, "\n"); print_params(fp, e_define_param); fprintf(fp, "\n"); + fprintf(fp, "-define(HIPE_LITERALS_CRC, %u).\n", literals_crc); if (is_xcomp) { fprintf(fp, "-define(HIPE_SYSTEM_CRC, %u).\n", system_crc); } else { fprintf(fp, "-define(HIPE_SYSTEM_CRC, hipe_bifs:system_crc()).\n"); } + fprintf(fp, "-define(HIPE_ERTS_CHECKSUM, (?HIPE_LITERALS_CRC bxor ?HIPE_SYSTEM_CRC)).\n"); return 0; } -- cgit v1.2.3 From a22b5ba19193e3f39129fadd20d375f6cc3f8529 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 1 Sep 2015 19:53:40 +0200 Subject: erts: Fix bug when tracing with 'process_dump' If the process stack contained a match state the print function would crash the vm as it was not recognized by tag_val_def(). Add new MATCHSTATE_DEF returned by tag_val_def(). All other callers either ignore it or has a default clause to handle invalid terms. --- erts/emulator/beam/erl_debug.c | 3 +++ erts/emulator/beam/erl_printf_term.c | 9 ++++----- erts/emulator/beam/erl_term.c | 1 + erts/emulator/beam/erl_term.h | 3 +++ erts/emulator/beam/utils.c | 9 ++++----- 5 files changed, 15 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index 50bdc79506..b6c131a43e 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -188,6 +188,9 @@ pdisplay1(int to, void *to_arg, Process* p, Eterm obj) case BINARY_DEF: erts_print(to, to_arg, "#Bin"); break; + case MATCHSTATE_DEF: + erts_print(to, to_arg, "#Matchstate"); + break; default: erts_print(to, to_arg, "unknown object %x", obj); } diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index d18760dc43..ba9a174fdf 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -441,11 +441,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, PRINT_DOUBLE(res, fn, arg, 'e', 6, 0, ff.fd); } break; - case BINARY_DEF: - if (header_is_bin_matchstate(*boxed_val(wobj))) { - PRINT_STRING(res, fn, arg, "#MatchState"); - } - else { + case BINARY_DEF: { ProcBin* pb = (ProcBin *) binary_val(wobj); if (pb->size == 1) PRINT_STRING(res, fn, arg, "<<1 byte>>"); @@ -519,6 +515,9 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, } } break; + case MATCHSTATE_DEF: + PRINT_STRING(res, fn, arg, "#MatchState"); + break; default: PRINT_STRING(res, fn, arg, "> _TAG_PRIMARY_SIZE): return BINARY_DEF; case (_TAG_HEADER_SUB_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): return MAP_DEF; + case (_TAG_HEADER_BIN_MATCHSTATE >> _TAG_PRIMARY_SIZE): return MATCHSTATE_DEF; } break; } diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 37014ccf94..1b6bb27395 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -1095,6 +1095,9 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint) #define FLOAT_DEF 0xe #define BIG_DEF 0xf #define SMALL_DEF 0x10 +#define MATCHSTATE_DEF 0x11 /* not a "real" term */ + +#define FIRST_VACANT_TAG_DEF 0x12 #if ET_DEBUG extern unsigned tag_val_def_debug(Wterm, const char*, unsigned); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index f810fca9a4..a8bbdb9354 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -788,11 +788,10 @@ Uint32 make_hash(Eterm term_arg) Eterm hash = 0; unsigned op; - /* Must not collide with the real tag_val_def's: */ -#define MAKE_HASH_TUPLE_OP 0x11 -#define MAKE_HASH_TERM_ARRAY_OP 0x12 -#define MAKE_HASH_CDR_PRE_OP 0x13 -#define MAKE_HASH_CDR_POST_OP 0x14 +#define MAKE_HASH_TUPLE_OP (FIRST_VACANT_TAG_DEF) +#define MAKE_HASH_TERM_ARRAY_OP (FIRST_VACANT_TAG_DEF+1) +#define MAKE_HASH_CDR_PRE_OP (FIRST_VACANT_TAG_DEF+2) +#define MAKE_HASH_CDR_POST_OP (FIRST_VACANT_TAG_DEF+3) /* ** Convenience macro for calculating a bytewise hash on an unsigned 32 bit -- cgit v1.2.3 From 0c52e3c18da16dbb896871865b71093b8c5617c4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 1 Sep 2015 19:09:39 +0200 Subject: erts: Add testcase for tracing whith 'process_dump' of a process with a matchstate on the stack. --- erts/emulator/test/match_spec_SUITE.erl | 60 +++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 21 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index fdce157abc..bfa09c625f 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -167,12 +167,11 @@ test_1(Config) when is_list(Config) -> [{['$1','$1'],[{is_atom, '$1'}],[kakalorum]}], [{call, {?MODULE, f2, [a, a]}}]), -% case tr0(fun() -> ?MODULE:f2(a, a) end, -% {?MODULE, f2, 2}, -% [{['$1','$1'],[{is_atom, '$1'}],[{message, {process_dump}}]}]) of -% [{trace, _, call, {?MODULE, f2, [a, a]}, Bin}] -> -% erlang:display(binary_to_list(Bin)) -% end, + %% Verify that 'process_dump' can handle a matchstate on the stack. + tr(fun() -> fbinmatch(<<0>>, 0) end, + {?MODULE, f1, 1}, + [{['_'],[],[{message, {process_dump}}]}], + [fun({trace, _, call, {?MODULE, f1, [0]}, _Bin}) -> true end]), % Error cases ?line errchk([{['$1','$1'],[{is_atom, '$1'}],[{banka, kanin}]}]), @@ -928,14 +927,14 @@ tr(Fun, MFA, TraceFlags, Pat, PatFlags, Expected0) -> erlang:trace(P, true, TraceFlags), erlang:trace_pattern(MFA, Pat, PatFlags), lists:map( - fun(X) -> - list_to_tuple([trace, P | tuple_to_list(X)]) + fun(X) when is_function(X,1) -> X; + (X) -> list_to_tuple([trace, P | tuple_to_list(X)]) end, Expected0) end). tr(RunFun, ControlFun) -> - P = spawn(?MODULE, runner, [self(), RunFun]), + P = spawn_link(?MODULE, runner, [self(), RunFun]), collect(P, ControlFun(P)). collect(P, TMs) -> @@ -954,18 +953,33 @@ collect([]) -> collect([TM | TMs]) -> ?t:format( "Expecting: ~p~n", [TM]), receive - M -> - case if element(1, M) == trace_ts -> - list_to_tuple(lists:reverse( - tl(lists:reverse(tuple_to_list(M))))); - true -> M - end of - TM -> - ?t:format("Got: ~p~n", [M]), - collect(TMs); - _ -> - ?t:format("Got unexpected: ~p~n", [M]), - flush({got_unexpected,M}) + M0 -> + M = case element(1, M0) of + trace_ts -> + list_to_tuple(lists:reverse( + tl(lists:reverse(tuple_to_list(M0))))); + _ -> M0 + end, + case is_function(TM,1) of + true -> + case (catch TM(M)) of + true -> + ?t:format("Got: ~p~n", [M]), + collect(TMs); + _ -> + ?t:format("Got unexpected: ~p~n", [M]), + flush({got_unexpected,M}) + end; + + false -> + case M of + TM -> + ?t:format("Got: ~p~n", [M]), + collect(TMs); + _ -> + ?t:format("Got unexpected: ~p~n", [M]), + flush({got_unexpected,M}) + end end end. @@ -1045,6 +1059,10 @@ fn(X, Y) -> fn(X, Y, Z) -> [X, Y, Z]. +fbinmatch(<>, Acc) -> + fbinmatch(Rest, [?MODULE:f1(Int) | Acc]); +fbinmatch(<<>>, Acc) -> Acc. + id(X) -> X. -- cgit v1.2.3 From efa86bac1cdac245213183574eca5f4662e80c7f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 7 Sep 2015 16:33:09 +0200 Subject: erts: Fix inet packet mode ssl_tls for passive mode Caused stack corruption leading to VM crash on windows. {packet,ssl_tls} is undocumented by the way. --- erts/emulator/drivers/common/inet_drv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 549de6503c..89b71aa66a 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -2749,7 +2749,7 @@ int ssl_tls_inetdrv(void* arg, unsigned type, unsigned major, unsigned minor, { tcp_descriptor* desc = (tcp_descriptor*) arg; int i = 0; - ErlDrvTermData spec[28]; + ErlDrvTermData spec[30]; ErlDrvTermData caller = ERL_DRV_NIL; ErlDrvBinary* bin; int ret; @@ -2790,11 +2790,11 @@ int ssl_tls_inetdrv(void* arg, unsigned type, unsigned major, unsigned minor, if (desc->inet.active == INET_PASSIVE) { i = LOAD_TUPLE(spec, i, 2); i = LOAD_TUPLE(spec, i, 4); - ASSERT(i <= 28); + ASSERT(i <= sizeof(spec)/sizeof(*spec)); ret = erl_drv_send_term(desc->inet.dport, caller, spec, i); } else { - ASSERT(i <= 28); + ASSERT(i <= sizeof(spec)/sizeof(*spec)); ret = erl_drv_output_term(desc->inet.dport, spec, i); } done: -- cgit v1.2.3 From fa30eeeb6f54bb0421a76a321f77b9163dca8de9 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 7 Sep 2015 17:49:29 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 17 +++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 5682b9254c..cc224bee49 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,23 @@

This document describes the changes made to the ERTS application.

+
Erts 6.4.1.3 + +
Fixed Bugs and Malfunctions + + +

+ When tracing with process_dump option, the VM + could abort if there was an ongoing binary match + somewhere in the call stack of the traced process./

+

+ Own Id: OTP-12968

+
+
+
+ +
+
Erts 6.4.1.2
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 35f40995d5..b8f4cf6946 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.4.1.2 +VSN = 6.4.1.3 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From e4dedc6cffb5025161d9a6295523b215e5a165c7 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 9 Sep 2015 17:41:41 +0200 Subject: Add configure switch --disable-saved-compile-time --- erts/configure.in | 9 +++++++++ erts/emulator/beam/break.c | 4 ++++ erts/emulator/utils/make_alloc_types | 2 +- erts/emulator/utils/make_version | 6 +++++- erts/emulator/utils/mkver.c | 6 +++++- 5 files changed, 24 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 22ca7ec17d..4fb725ff00 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -342,6 +342,15 @@ AS_HELP_STRING([--enable-systemd], [enable systemd support in epmd]), [], [enable_systemd=no]) +AC_ARG_ENABLE(saved-compile-time, +AS_HELP_STRING([--disable-saved-compile-time], [disable saved compile time]), +[ case "$enableval" in + no) save_compile_time=0 ;; + *) save_compile_time=1 ;; + esac ], save_compile_time=1) + +AC_DEFINE_UNQUOTED(ERTS_SAVED_COMPILE_TIME, $save_compile_time, [Save compile time?]) + dnl Magic test for clearcase. OTP_RELEASE= if test "${ERLANG_COMMERCIAL_BUILD}" != ""; then diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 64c8bc5e58..4ce9d24479 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -536,7 +536,9 @@ do_break(void) erts_printf("Erlang (%s) emulator version " ERLANG_VERSION "\n", EMULATOR); +#if ERTS_SAVED_COMPILE_TIME erts_printf("Compiled on " ERLANG_COMPILE_DATE "\n"); +#endif return; case 'd': distribution_info(ERTS_PRINT_STDOUT, NULL); @@ -774,7 +776,9 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) } erts_fdprintf(fd, "System version: "); erts_print_system_version(fd, NULL, NULL); +#if ERTS_SAVED_COMPILE_TIME erts_fdprintf(fd, "%s\n", "Compiled: " ERLANG_COMPILE_DATE); +#endif erts_fdprintf(fd, "Taints: "); erts_print_nif_taints(fd, NULL); diff --git a/erts/emulator/utils/make_alloc_types b/erts/emulator/utils/make_alloc_types index 88f537ea09..925b9d5810 100755 --- a/erts/emulator/utils/make_alloc_types +++ b/erts/emulator/utils/make_alloc_types @@ -246,7 +246,7 @@ print DST " print DST "#define ERTS_ALC_C_MIN ($c_no)\n\n"; -foreach my $c (keys(%c_tab)) { +foreach my $c (sort keys(%c_tab)) { push(@c_order, $c); set_number($c_tab{$c}, $c_no); print DST "#define ERTS_ALC_C_$c ($c_no)\n"; diff --git a/erts/emulator/utils/make_version b/erts/emulator/utils/make_version index 3461dc1637..37bdff181a 100755 --- a/erts/emulator/utils/make_version +++ b/erts/emulator/utils/make_version @@ -59,7 +59,11 @@ print FILE < Date: Tue, 8 Sep 2015 17:12:18 +0200 Subject: erts: Refactor config test for posix_memalign --- erts/configure.in | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 6950915a6a..25256bcac9 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2084,8 +2084,7 @@ fi case X$erl_xcomp_posix_memalign in Xno) ;; - Xyes) AC_DEFINE(HAVE_POSIX_MEMALIGN,[1], - [Define to 1 if you have the `posix_memalign' function.]) ;; + Xyes) have_posix_memalign=yes ;; *) AC_CHECK_FUNC( [posix_memalign], @@ -2100,15 +2099,19 @@ int main(void) { return error; return 0; } -],AC_DEFINE(HAVE_POSIX_MEMALIGN,[1], - [Define to 1 if you have the `posix_memalign' function.]) +],have_posix_memalign=yes ) else - AC_DEFINE(HAVE_POSIX_MEMALIGN,[1], - [Define to 1 if you have the `posix_memalign' function.]) + have_posix_memalign=yes fi]);; esac +if test $have_posix_memalign = yes; then + AC_DEFINE(HAVE_POSIX_MEMALIGN,[1], + [Define to 1 if you have the `posix_memalign' function.]) +fi + + dnl writev on OS X snow leopard is broken for files > 4GB case $host_os in darwin10.8.0) -- cgit v1.2.3 From 997881b9046d3468685c545dc917e8204e0a9ef6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 3 Sep 2015 17:57:30 +0200 Subject: erts: Remove unused erts_have_erts_mmap --- erts/emulator/sys/common/erl_mmap.c | 6 ------ erts/emulator/sys/common/erl_mmap.h | 3 --- 2 files changed, 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index e6d0e1e281..eeaf96dd93 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -67,7 +67,6 @@ (((UWord) (PTR)) - ((UWord) mmap_state.sua.bot) \ < ((UWord) mmap_state.sua.top) - ((UWord) mmap_state.sua.bot))) -int erts_have_erts_mmap; UWord erts_page_inv_mask; #if defined(DEBUG) || defined(ERTS_MMAP_DEBUG) @@ -2131,8 +2130,6 @@ erts_mmap_init(ErtsMMapInit *init) ERTS_MMAP_OP_RINGBUF_INIT(); - erts_have_erts_mmap = 0; - mmap_state.supercarrier = 0; mmap_state.reserve_physical = reserve_noop; mmap_state.unreserve_physical = unreserve_noop; @@ -2206,8 +2203,6 @@ erts_mmap_init(ErtsMMapInit *init) } #endif } - if (!mmap_state.no_os_mmap) - erts_have_erts_mmap |= ERTS_HAVE_ERTS_OS_MMAP; #endif mmap_state.no.free_seg_descs = 0; @@ -2291,7 +2286,6 @@ erts_mmap_init(ErtsMMapInit *init) init_free_seg_map(&mmap_state.sua.map, SZ_REVERSE_ADDR_ORDER); mmap_state.supercarrier = 1; - erts_have_erts_mmap |= ERTS_HAVE_ERTS_SUPERCARRIER_MMAP; mmap_state.desc.new_area_hint = end; diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 66619c5161..51f830d045 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -30,9 +30,6 @@ #define ERTS_MMAPFLG_SUPERCARRIER_ONLY (((Uint32) 1) << 1) #define ERTS_MMAPFLG_SUPERALIGNED (((Uint32) 1) << 2) -#define ERTS_HAVE_ERTS_OS_MMAP (1 << 0) -#define ERTS_HAVE_ERTS_SUPERCARRIER_MMAP (1 << 1) -extern int erts_have_erts_mmap; extern UWord erts_page_inv_mask; typedef struct { -- cgit v1.2.3 From 34e1b675f95d0837823b794f3440300806f7ef88 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 11 Sep 2015 16:40:56 +0200 Subject: erts: Cleanup main carrier creation and remove that magic "-40" from default mmbcs for ll_alloc --- erts/emulator/beam/erl_alloc.c | 4 ++-- erts/emulator/beam/erl_alloc_util.c | 33 ++++++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index d68f22d573..70d5357e27 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -272,9 +272,9 @@ set_default_ll_alloc_opts(struct au_init *ip) ip->init.util.name_prefix = "ll_"; ip->init.util.alloc_no = ERTS_ALC_A_LONG_LIVED; #ifndef SMALL_MEMORY - ip->init.util.mmbcs = 2*1024*1024 - 40; /* Main carrier size */ + ip->init.util.mmbcs = 2*1024*1024; /* Main carrier size */ #else - ip->init.util.mmbcs = 1*1024*1024 - 40; /* Main carrier size */ + ip->init.util.mmbcs = 1*1024*1024; /* Main carrier size */ #endif ip->init.util.ts = ERTS_ALC_MTA_LONG_LIVED; ip->init.util.asbcst = 0; diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index db4c30b9eb..444d7055c8 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -3534,7 +3534,21 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) return NULL; } - blk_sz = UMEMSZ2BLKSZ(allctr, umem_sz); + if (flags & CFLG_MAIN_CARRIER) { + ASSERT(flags & CFLG_MBC); + ASSERT(flags & CFLG_NO_CPOOL); + ASSERT(umem_sz == allctr->main_carrier_size); + ERTS_UNDEF(blk_sz, 0); + + if (allctr->main_carrier_size < allctr->min_mbc_size) + allctr->main_carrier_size = allctr->min_mbc_size; + crr_sz = bcrr_sz = allctr->main_carrier_size; + } + else { + ERTS_UNDEF(bcrr_sz, 0); + ERTS_UNDEF(crr_sz, 0); + blk_sz = UMEMSZ2BLKSZ(allctr, umem_sz); + } #ifdef ERTS_SMP allctr->cpool.disable_abandon = ERTS_ALC_CPOOL_MAX_DISABLE_ABANDON; @@ -3580,10 +3594,12 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) mseg_flags = ERTS_MSEG_FLG_NONE; } else { - crr_sz = (*allctr->get_next_mbc_size)(allctr); - if (crr_sz < MBC_HEADER_SIZE(allctr) + blk_sz) - crr_sz = MBC_HEADER_SIZE(allctr) + blk_sz; - mseg_flags = ERTS_MSEG_FLG_2POW; + if (!(flags & CFLG_MAIN_CARRIER)) { + crr_sz = (*allctr->get_next_mbc_size)(allctr); + if (crr_sz < MBC_HEADER_SIZE(allctr) + blk_sz) + crr_sz = MBC_HEADER_SIZE(allctr) + blk_sz; + } + mseg_flags = ERTS_MSEG_FLG_2POW; } crr = (Carrier_t *) alcu_mseg_alloc(allctr, &crr_sz, mseg_flags); @@ -3618,11 +3634,10 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) if (flags & CFLG_SBC) { bcrr_sz = blk_sz + SBC_HEADER_SIZE; } - else { + else if (!(flags & CFLG_MAIN_CARRIER)) { bcrr_sz = MBC_HEADER_SIZE(allctr) + blk_sz; - if (!(flags & CFLG_MAIN_CARRIER) - && bcrr_sz < allctr->smallest_mbc_size) - bcrr_sz = allctr->smallest_mbc_size; + if (bcrr_sz < allctr->smallest_mbc_size) + bcrr_sz = allctr->smallest_mbc_size; } crr_sz = (flags & CFLG_FORCE_SIZE -- cgit v1.2.3 From 1fd4809777931a4dc166fd9f1b552317a385cd62 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 7 Sep 2015 19:58:47 +0200 Subject: erts: Fix strangeness in treatment of MSEG_ALIGN_BITS And remove old case of using only page alignment (12 bits). --- erts/emulator/beam/erl_alloc_util.h | 2 +- erts/emulator/sys/common/erl_mseg.h | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index 792f8c63ac..f314eab4c2 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -220,7 +220,7 @@ erts_aint32_t erts_alcu_fix_alloc_shrink(Allctr_t *, erts_aint32_t); #if ERTS_HAVE_MSEG_SUPER_ALIGNED \ || (!HAVE_ERTS_MSEG && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC) -# ifndef MSEG_ALIGN_BITS +# ifdef MSEG_ALIGN_BITS # define ERTS_SUPER_ALIGN_BITS MSEG_ALIGN_BITS # else # define ERTS_SUPER_ALIGN_BITS 18 diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index 656484702d..677f8ea4ed 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -42,16 +42,6 @@ #if ERTS_HAVE_MSEG_SUPER_ALIGNED # define MSEG_ALIGN_BITS ERTS_MMAP_SUPERALIGNED_BITS -#else -/* If we don't use super aligned multiblock carriers - * we will mmap with page size alignment (and thus use corresponding - * align bits). - * - * Current implementation needs this to be a constant and - * only uses this for user dev testing so setting page size - * to 4096 (12 bits) is fine. - */ -# define MSEG_ALIGN_BITS (12) #endif #if HAVE_ERTS_MSEG -- cgit v1.2.3 From ada8342bedf8d4b84d4c3c10fcfc7919e532fd8c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 4 Sep 2015 18:45:06 +0200 Subject: erts: Add new allocator LITERAL --- erts/emulator/beam/beam_bif_load.c | 6 ++++++ erts/emulator/beam/beam_load.c | 24 +++++++++++------------ erts/emulator/beam/erl_alloc.c | 39 ++++++++++++++++++++++++++++++++++++++ erts/emulator/beam/erl_alloc.types | 3 +++ erts/emulator/beam/erl_bif_info.c | 2 +- erts/emulator/hipe/hipe_bif0.c | 2 +- 6 files changed, 62 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 11508a1b39..68689b8e7f 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -684,6 +684,9 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) erts_cleanup_funs_on_purge(code, end); beam_catches_delmod(modp->curr.catches, code, modp->curr.code_length, erts_active_code_ix()); + if (code[MI_LITERALS_START]) { + erts_free(ERTS_ALC_T_LITERAL, (void *) code[MI_LITERALS_START]); + } erts_free(ERTS_ALC_T_CODE, (void *) code); modp->curr.code = NULL; modp->curr.code_length = 0; @@ -1019,6 +1022,9 @@ BIF_RETTYPE purge_module_1(BIF_ALIST_1) beam_catches_delmod(modp->old.catches, code, modp->old.code_length, code_ix); decrement_refc(code); + if (code[MI_LITERALS_START]) { + erts_free(ERTS_ALC_T_LITERAL, (void *) code[MI_LITERALS_START]); + } erts_free(ERTS_ALC_T_CODE, (void *) code); modp->old.code = NULL; modp->old.code_length = 0; diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index a9d47eb7b0..f0b4be4c3d 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -654,6 +654,7 @@ erts_prepare_loading(Binary* magic, 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_LITERALS_START] = 0; stp->code[MI_MD5_PTR] = 0; /* @@ -921,6 +922,9 @@ loader_state_dtor(Binary* magic) stp->bin = 0; } if (stp->code != 0) { + if (stp->code[MI_LITERALS_START]) { + erts_free(ERTS_ALC_T_LITERAL, (void*) stp->code[MI_LITERALS_START]); + } erts_free(ERTS_ALC_T_CODE, stp->code); stp->code = 0; } @@ -4348,7 +4352,6 @@ static int freeze_code(LoaderState* stp) { BeamInstr* code = stp->code; - Uint *literal_end = NULL; int i; byte* str_table; unsigned strtab_size = stp->chunks[STR_CHUNK].size; @@ -4378,7 +4381,6 @@ freeze_code(LoaderState* stp) sizeof(Eterm) + (stp->current_li+1) * stp->loc_size; } size = (stp->ci * sizeof(BeamInstr)) + - (stp->total_literal_size * sizeof(Eterm)) + strtab_size + attr_size + compile_size + MD5_SIZE + line_size; /* @@ -4406,10 +4408,9 @@ freeze_code(LoaderState* stp) } CHKBLK(ERTS_ALC_T_CODE,code); - literal_end = (Uint *) (code+stp->ci); /* - * Place the literal heap directly after the code and fix up all - * instructions that refer to it. + * Place the literals in their own allocated heap (for fast range check) + * and fix up all instructions that refer to it. */ { Uint* ptr; @@ -4420,7 +4421,8 @@ freeze_code(LoaderState* stp) ERTS_INIT_OFF_HEAP(&code_off_heap); - low = (Uint *) (code+stp->ci); + low = erts_alloc(ERTS_ALC_T_LITERAL, + stp->total_literal_size*sizeof(Eterm)); high = low + stp->total_literal_size; code[MI_LITERALS_START] = (BeamInstr) low; code[MI_LITERALS_END] = (BeamInstr) high; @@ -4443,7 +4445,6 @@ freeze_code(LoaderState* stp) op_ptr[0] = lit->term; lp = lp->next; } - literal_end += stp->total_literal_size; } CHKBLK(ERTS_ALC_T_CODE,code); @@ -4452,9 +4453,9 @@ freeze_code(LoaderState* stp) */ if (stp->line_instr == 0) { code[MI_LINE_TABLE] = (BeamInstr) 0; - str_table = (byte *) literal_end; + str_table = (byte *) (code+stp->ci); } else { - Eterm* line_tab = (Eterm *) literal_end; + Eterm* line_tab = (Eterm *) (code+stp->ci); Eterm* p; int ftab_size = stp->num_functions; int num_instrs = stp->current_li; @@ -4486,7 +4487,7 @@ freeze_code(LoaderState* stp) *locp++ = (Uint16) stp->line_instr[i].loc; } *locp++ = LINE_INVALID_LOCATION; - str_table = (byte *) locp; + str_table = (byte *) locp; } else { Uint32* locp = (Uint32 *) p; ASSERT(stp->loc_size == 4); @@ -4494,9 +4495,8 @@ freeze_code(LoaderState* stp) *locp++ = stp->line_instr[i].loc; } *locp++ = LINE_INVALID_LOCATION; - str_table = (byte *) locp; + str_table = (byte *) locp; } - CHKBLK(ERTS_ALC_T_CODE,code); } diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 70d5357e27..d83a2930d6 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -130,6 +130,7 @@ static ErtsAllocatorState_t binary_alloc_state; static ErtsAllocatorState_t ets_alloc_state; static ErtsAllocatorState_t driver_alloc_state; static ErtsAllocatorState_t fix_alloc_state; +static ErtsAllocatorState_t literal_alloc_state; typedef struct { erts_smp_atomic32_t refc; @@ -211,6 +212,7 @@ typedef struct { struct au_init ets_alloc; struct au_init driver_alloc; struct au_init fix_alloc; + struct au_init literal_alloc; } erts_alc_hndl_args_init_t; #define ERTS_AU_INIT__ {0, 0, 1, GOODFIT, DEFAULT_ALLCTR_INIT, {1,1,1,1}} @@ -284,6 +286,32 @@ set_default_ll_alloc_opts(struct au_init *ip) ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL_LL_ALLOC; } +static void +set_default_literal_alloc_opts(struct au_init *ip) +{ + SET_DEFAULT_ALLOC_OPTS(ip); + ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); + ip->thr_spec = 0; + ip->atype = BESTFIT; + ip->init.bf.ao = 1; + ip->init.util.ramv = 0; + ip->init.util.mmsbc = 0; + ip->init.util.sbct = ~((UWord) 0); + ip->init.util.name_prefix = "literal_"; + ip->init.util.alloc_no = ERTS_ALC_A_LITERAL; +#ifndef SMALL_MEMORY + ip->init.util.mmbcs = 2*1024*1024; /* Main carrier size */ +#else + ip->init.util.mmbcs = 1*1024*1024; /* Main carrier size */ +#endif + ip->init.util.ts = ERTS_ALC_MTA_LITERAL; + ip->init.util.asbcst = 0; + ip->init.util.rsbcst = 0; + ip->init.util.rsbcmt = 0; + ip->init.util.rmbcmt = 0; + ip->init.util.acul = 0; +} + static void set_default_temp_alloc_opts(struct au_init *ip) { @@ -577,6 +605,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) set_default_driver_alloc_opts(&init.driver_alloc); set_default_fix_alloc_opts(&init.fix_alloc, fix_type_sizes); + set_default_literal_alloc_opts(&init.literal_alloc); if (argc && argv) handle_args(argc, argv, &init); @@ -604,6 +633,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) init.ets_alloc.thr_spec = 0; init.driver_alloc.thr_spec = 0; init.fix_alloc.thr_spec = 0; + init.literal_alloc.thr_spec = 0; #endif /* Make adjustments for carrier migration support */ @@ -616,6 +646,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) adjust_carrier_migration_support(&init.ets_alloc); adjust_carrier_migration_support(&init.driver_alloc); adjust_carrier_migration_support(&init.fix_alloc); + adjust_carrier_migration_support(&init.literal_alloc); if (init.erts_alloc_config) { /* Adjust flags that erts_alloc_config won't like */ @@ -630,6 +661,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) init.ets_alloc.thr_spec = 0; init.driver_alloc.thr_spec = 0; init.fix_alloc.thr_spec = 0; + init.literal_alloc.thr_spec = 0; /* No carrier migration */ init.temp_alloc.init.util.acul = 0; @@ -641,6 +673,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) init.ets_alloc.init.util.acul = 0; init.driver_alloc.init.util.acul = 0; init.fix_alloc.init.util.acul = 0; + init.literal_alloc.init.util.acul = 0; } #ifdef ERTS_SMP @@ -657,6 +690,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) adjust_tpref(&init.ets_alloc, erts_no_schedulers); adjust_tpref(&init.driver_alloc, erts_no_schedulers); adjust_tpref(&init.fix_alloc, erts_no_schedulers); + adjust_tpref(&init.literal_alloc, erts_no_schedulers); #else /* No thread specific if not smp */ @@ -675,6 +709,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) refuse_af_strategy(&init.ets_alloc); refuse_af_strategy(&init.driver_alloc); refuse_af_strategy(&init.fix_alloc); + refuse_af_strategy(&init.literal_alloc); #ifdef ERTS_SMP if (!init.temp_alloc.thr_spec) @@ -718,6 +753,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) set_au_allocator(ERTS_ALC_A_ETS, &init.ets_alloc, ncpu); set_au_allocator(ERTS_ALC_A_DRIVER, &init.driver_alloc, ncpu); set_au_allocator(ERTS_ALC_A_FIXED_SIZE, &init.fix_alloc, ncpu); + set_au_allocator(ERTS_ALC_A_LITERAL, &init.literal_alloc, ncpu); for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) { if (!erts_allctrs[i].alloc) @@ -770,6 +806,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) start_au_allocator(ERTS_ALC_A_FIXED_SIZE, &init.fix_alloc, &fix_alloc_state); + start_au_allocator(ERTS_ALC_A_LITERAL, + &init.literal_alloc, + &literal_alloc_state); erts_mtrace_install_wrapper_functions(); extra_block_size += erts_instr_init(init.instr.stat, init.instr.map); diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 4804fb407d..7d519c1be4 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -86,6 +86,7 @@ allocator LONG_LIVED true ll_alloc allocator EHEAP true eheap_alloc allocator ETS true ets_alloc allocator FIXED_SIZE true fix_alloc +allocator LITERAL true literal_alloc +else # Non smp build @@ -96,6 +97,7 @@ allocator LONG_LIVED false ll_alloc allocator EHEAP false eheap_alloc allocator ETS false ets_alloc allocator FIXED_SIZE false fix_alloc +allocator LITERAL false literal_alloc +endif @@ -343,6 +345,7 @@ type DDLL_PROCESS STANDARD SYSTEM ddll_processes type MONITOR_LH STANDARD PROCESSES monitor_lh type NLINK_LH STANDARD PROCESSES nlink_lh type CODE LONG_LIVED CODE code +type LITERAL LITERAL CODE literal type DB_HEIR_DATA STANDARD ETS db_heir_data type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 9a132ee007..79ccf8db64 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -4321,7 +4321,7 @@ static void os_info_init(void) os_flavor(buf, 1024); flav = erts_atom_put((byte *) buf, strlen(buf), ERTS_ATOM_ENC_LATIN1, 1); erts_free(ERTS_ALC_T_TMP, (void *) buf); - hp = erts_alloc(ERTS_ALC_T_LL_TEMP_TERM, (3+4)*sizeof(Eterm)); + hp = erts_alloc(ERTS_ALC_T_LITERAL, (3+4)*sizeof(Eterm)); os_type_tuple = TUPLE2(hp, type, flav); hp += 3; os_version(&major, &minor, &build); diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index cc68e1f74d..7193c3d301 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -488,7 +488,7 @@ static void *const_term_alloc(void *tmpl) alloc_size = size + (offsetof(struct const_term, mem)/sizeof(Eterm)); hipe_constants_size += alloc_size; - p = (struct const_term*)erts_alloc(ERTS_ALC_T_HIPE, alloc_size * sizeof(Eterm)); + p = (struct const_term*)erts_alloc(ERTS_ALC_T_LITERAL, alloc_size * sizeof(Eterm)); /* I have absolutely no idea if having a private 'off_heap' works or not. _Some_ off_heap object is required for -- cgit v1.2.3 From af2dae8fba475cd216bc3860c8b867e235eb8748 Mon Sep 17 00:00:00 2001 From: Constantin Rack Date: Tue, 15 Sep 2015 17:29:01 +0200 Subject: Typos in documentation example of list_to_bitstring/1 --- erts/doc/src/erlang.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 37f0aa289e..39febba1ec 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2143,10 +2143,10 @@ os_prompt% <<1,2,3>> > Bin2 = <<4,5>>. <<4,5>> -> Bin3 = <<6,7:4,>>. -<<6>> +> Bin3 = <<6,7:4>>. +<<6,7:4>> > list_to_bitstring([Bin1,1,[2,3,Bin2],4|Bin3]). -<<1,2,3,1,2,3,4,5,4,6,7:46>> +<<1,2,3,1,2,3,4,5,4,6,7:4>> -- cgit v1.2.3 From 11cf60809a26155c7904771974739f4a51911c93 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 15 Sep 2015 15:23:56 +0200 Subject: erts: Make sure to deal with EINTR write failures --- erts/emulator/drivers/unix/ttsl_drv.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index 53146e71f0..25cad37e25 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -733,13 +733,13 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun else written = 0; if (written < 0) { - if (errno == EAGAIN) { + if (errno == ERRNO_BLOCK || errno == EINTR) { driver_select(ttysl_port,(ErlDrvEvent)(long)ttysl_fd, ERL_DRV_USE|ERL_DRV_WRITE,1); break; } else { - /* we ignore all other errors */ - break; + driver_failure_posix(ttysl_port, errno); + return; } } else { if (driver_deq(ttysl_port, written) == 0) @@ -779,11 +779,12 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) { else written = 0; if (written < 0) { - if (errno == EAGAIN) { - break; - } else { - /* we ignore all other errors */ + if (errno == EINTR) { + continue; + } else if (errno != ERRNO_BLOCK){ + driver_failure_posix(ttysl_port, errno); } + break; } else { sz = driver_deq(ttysl_port, written); if (sz < TTY_BUFFSIZE && ttysl_send_ok) { -- cgit v1.2.3 From b37793a7b42114816ee8c0dc86217253c7e74781 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 11 Sep 2015 10:28:41 +0200 Subject: Exclude ose application from upgrade test --- erts/test/upgrade_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index 8a91cf5b7e..83cd2359d8 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -40,7 +40,7 @@ %% - typer requires hipe (in the .app file) %% - erl_interface, jinterface support no upgrade -define(appup_exclude, - [dialyzer,hipe,typer,erl_interface,jinterface]). + [dialyzer,hipe,typer,erl_interface,jinterface,ose]). init_per_suite(Config) -> %% Check that a real release is running, not e.g. cerl -- cgit v1.2.3 From 6738d356a279835222b951fd213ed4cf9897eb7e Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 21 Sep 2015 17:09:23 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 123 +++++++++++++++++++++++++++++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 124 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index bed1ac463d..e51cf93cf7 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,129 @@

This document describes the changes made to the ERTS application.

+
Erts 7.1 + +
Fixed Bugs and Malfunctions + + +

+ Fix bug in ETS that could cause stray objects marked for + deletion to occasionally be missed by the cleanup done by + safe_fixtable(_,false).

+

+ Own Id: OTP-12870

+
+ +

+ Fixed VM crash that could occur if a trace port was + linked to a process, and the trace port terminated + abnormally while handling a trace message. This bug has + always existed in the runtime system with SMP support.

+

+ Own Id: OTP-12901

+
+ +

+ Instead of aborting, the vm now creates a crash dump when + a system process is terminated.

+

+ Own Id: OTP-12934

+
+ +

+ Fixed a rare emulator dead lock that occurred when + erlang:process_flag(priority,...) was called by a process + that was also scheduled for an internal system activity.

+

+ Own Id: OTP-12943

+
+ +

+ The runtime system on various posix platforms (except for + Linux and Solaris) could crash when large amounts of + file-descriptors were in use.

+

+ Own Id: OTP-12954

+
+ +

+ A beam file compiled by hipe for an incompatible runtime + system was sometimes not rejected by the loader, which + could lead to vm crash. This fix will also allow the same + hipe compiler to be used by both normal and debug-built + vm.

+

+ Own Id: OTP-12962

+
+ +

+ Fix bug in maps:merge/2 when called by hipe + compiled code that could cause vm crash. Bug exists since + erts-7.0 (OTP 18.0).

+

+ Own Id: OTP-12965

+
+ +

+ When tracing with process_dump option, the VM + could abort if there was an ongoing binary match + somewhere in the call stack of the traced process.

+

+ Own Id: OTP-12968

+
+ +

+ Fixed possible output deadlock in tty driver when hitting + "CTRL-C" in a non-smp emulator shell on unix.

+

+ Own Id: OTP-12987 Aux Id: Seq12947

+
+ +

+ Fix binary_to_integer to throw badarg for "+" and + "-" similar to list_to_integer.

+

+ Own Id: OTP-12988

+
+ +

+ Suppress warning of unused argument when using macro + enif_make_pid.

+

+ Own Id: OTP-12989

+
+
+
+ + +
Improvements and New Features + + +

+ Changed default clock source used for OS system time on + MacOS X to gettimeofday() in order to improve + performance. The system can be configured during build to + use the previously used higher resolution clock source by + passing the switch --with-clock-resolution=high + when configuring the build.

+

+ Own Id: OTP-12945 Aux Id: OTP-12892

+
+ +

+ Added the configure option --disable-saved-compile-time + which disables saving of compile date and time in the + emulator binary.

+

+ Own Id: OTP-12971

+
+
+
+ +
+
Erts 7.0.3
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 38b9a13e63..140baeb846 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 7.0.3 +VSN = 7.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 6fa5c094e94e06f02b717f00e90ac777652ea6b8 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 18 Sep 2015 15:00:38 +0200 Subject: Adjust test case for missing applications --- erts/test/otp_SUITE.erl | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 69a0d19719..c92a7cf6f7 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -301,9 +301,11 @@ call_to_now_0(Config) when is_list(Config) -> test_server,tools,webtool], not_recommended_calls(Config, Apps, {erlang,now,0}). -not_recommended_calls(Config, Apps, MFA) -> +not_recommended_calls(Config, Apps0, MFA) -> Server = ?config(xref_server, Config), + Apps = [App || App <- Apps0, is_present_application(App, Server)], + Fs = [MFA], Q1 = io_lib:format("E || ~p : Fun", [Fs]), @@ -337,11 +339,28 @@ not_recommended_calls(Config, Apps, MFA) -> end, case CallsToMFA of [] -> - ok; + SkippedApps = ordsets:subtract(ordsets:from_list(Apps0), + ordsets:from_list(Apps)), + case SkippedApps of + [] -> + ok; + _ -> + AppStrings = [atom_to_list(A) || A <- SkippedApps], + Mess = io_lib:format("Application(s) not present: ~s\n", + [string:join(AppStrings, ", ")]), + {comment, Mess} + end; _ -> ?t:fail({length(CallsToMFA),calls_to_size_1}) end. +is_present_application(Name, Server) -> + Q = io_lib:format("~w : App", [Name]), + case xref:q(Server, lists:flatten(Q)) of + {ok,[Name]} -> true; + {error,_,_} -> false + end. + strong_components(Config) when is_list(Config) -> Server = ?config(xref_server, Config), ?line {ok,Cs} = xref:q(Server, "components AE"), -- cgit v1.2.3 From 3f75988488197f7cf3846f9b7705376779728f4c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 28 Sep 2015 15:42:22 +0200 Subject: erts: Remove assertion that ptab is empty Another process may already have been placed in this slot since the free och the process struct can be scheduled for later. --- erts/emulator/beam/erl_process_lock.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index 788348e613..a64c993e8f 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -854,9 +854,6 @@ ERTS_GLB_INLINE void erts_proc_dec_refc(Process *p) #endif if (!referred) { ASSERT(ERTS_PROC_IS_EXITING(p)); - ASSERT(ERTS_AINT_NULL - == erts_ptab_pix2intptr_ddrb(&erts_proc, - internal_pid_index(p->common.id))); erts_free_proc(p); } } @@ -872,9 +869,6 @@ ERTS_GLB_INLINE void erts_proc_add_refc(Process *p, Sint add_refc) #endif if (!referred) { ASSERT(ERTS_PROC_IS_EXITING(p)); - ASSERT(ERTS_AINT_NULL - == erts_ptab_pix2intptr_ddrb(&erts_proc, - internal_pid_index(p->common.id))); erts_free_proc(p); } } -- cgit v1.2.3 From a50c470e3d1af4660c09d993495dea56c50ad306 Mon Sep 17 00:00:00 2001 From: xsipewe Date: Fri, 11 Sep 2015 15:05:24 +0200 Subject: erts: Update erts time correction docs --- erts/doc/src/time_correction.xml | 764 ++++++++++++++++++++------------------- 1 file changed, 393 insertions(+), 371 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index aed38fbb92..aec9efa3d3 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -35,31 +35,35 @@
New Extended Time Functionality -

As of OTP 18 (ERTS version 7.0) the time functionality of - Erlang has been extended. This both includes a +

As of OTP 18 (ERTS version 7.0) the time functionality of + Erlang has been extended. This includes a new API - for time, as well as + for time and time warp - modes which alters the behavior of the system when + modes that alter the system behavior when system time changes.

+

The default time warp mode has the same behavior as before, and the - old API will still work, so you are not required to change + old API still works. Thus, you are not required to change anything unless you want to. However, you are strongly encouraged to use the new API instead of the old API based on erlang:now/0. - erlang:now/0 has been deprecated since it is and forever - will be a scalability bottleneck. By using the new API you will + erlang:now/0 is deprecated, as it is and + will be a scalability bottleneck.

+ +

By using the new API, you automatically get scalability and performance improvements. This - will also enable you to use the - multi time warp mode - which improves accuracy, and precision of time measurements.

+ also enables you to use the + multi-time warp mode + that improves accuracy and precision of time measurements.

+
- Some Terminology -

In order to make it easier to understand this document we first - define some terminology. This is a mixture of our own terminology + Terminology +

To make it easier to understand this section, some terms + are defined. This is a mix of our own terminology (Erlang/OS system time, Erlang/OS monotonic time, time warp) and globally accepted terminology.

@@ -67,7 +71,7 @@
Monotonically Increasing

In a monotonically increasing sequence of values, all values - that have a predecessor are either larger than, or equal to its + that have a predecessor are either larger than or equal to its predecessor.

@@ -82,19 +86,19 @@
UT1 -

Universal Time. Based on the rotation of the earth. Conceptually - mean solar time at 0° longitude.

+

Universal Time. UT1 is based on the rotation of the earth + and conceptually means solar time at 0° longitude.

UTC -

Coordinated Universal Time. UTC almost align with - UT1, however, UTC uses the - SI definition of a second which is not exactly of the same length +

Coordinated Universal Time. UTC almost aligns with + UT1. However, UTC uses the + SI definition of a second, which has not exactly the same length as the second used by UT1. This means that UTC slowly drifts from - UT1. In order to keep UTC relatively in sync with UT1, leap seconds - are inserted, and potentially also deleted. That is, an UTC day may + UT1. To keep UTC relatively in sync with UT1, leap seconds + are inserted, and potentially also deleted. That is, an UTC day can be 86400, 86401, or 86399 seconds long.

@@ -104,14 +108,15 @@

Time since Epoch. Epoch is defined to be 00:00:00 UTC, - January 1, 1970. + 1970-01-01. A day in POSIX time is defined to be exactly 86400 seconds long. Strangely enough - Epoch is defined to be a time in UTC, and UTC have another + Epoch is defined to be a time in UTC, and UTC has another definition of how long a day is. Quoting the Open Group - "POSIX time is therefore not necessarily UTC, despite its appearance". The effect of this is that when an UTC leap second is + "POSIX time is therefore not necessarily UTC, despite its appearance". + The effect of this is that when an UTC leap second is inserted, POSIX time either stops for a second, or repeats the - last second. If an UTC leap second would be deleted (has never + last second. If an UTC leap second would be deleted (which has not happened yet), POSIX time would make a one second leap forward.

@@ -125,11 +130,11 @@
Time Precision -

The shortest time interval that can be be distinguished +

The shortest time interval that can be distinguished repeatedly and reliably when reading time values. Precision is limited by the resolution, but - resolution and precision might differ significantly.

+ resolution and precision can differ significantly.

@@ -143,21 +148,23 @@ Time Warp

A time warp is a leap forwards or backwards in time. That is, the difference of time values taken before and after the - time warp will not correspond to the actual elapsed time.

+ time warp does not correspond to the actual elapsed time.

OS System Time

The operating systems view of - POSIX time. It can be - retrieved by calling + POSIX time. To + retrieve it, call os:system_time(). This may or may not be an accurate view of POSIX time. This time may typically be adjusted both backwards and forwards without limitation. That is, time warps - may be observed. You can get information about the Erlang runtime - system's source of OS system time by calling + may be observed.

+ +

To get information about the Erlang runtime + system's source of OS system time, call erlang:system_info(os_system_time_source).

@@ -165,15 +172,17 @@
OS Monotonic Time

A monotonically increasing time provided by the operating - system. This time does not leap and have a relatively steady + system. This time does not leap and has a relatively steady frequency although not completely correct. However, it is not - uncommon that the OS monotonic time stops if the system is - suspended. This time typically increase since some + uncommon that OS monotonic time stops if the system is + suspended. This time typically increases since some unspecified point in time that is not connected to - OS system time. Note - that this type of time is not necessarily provided by all - operating systems. You can get information about the Erlang - runtime system's source of OS monotonic time by calling + OS system time. + This type of time is not necessarily provided by all + operating systems.

+ +

To get information about the Erlang + runtime system's source of OS monotonic time, call erlang:system_info(os_monotonic_time_source).

@@ -181,14 +190,17 @@
Erlang System Time

The Erlang runtime systems view of - POSIX time. It can be - retrieved by calling - erlang:system_time(). - This time may or may not be an accurate view of POSIX time, and may + POSIX time. To + retrieve it, call + erlang:system_time().

+ +

This time may or may not be an accurate view of POSIX time, + and may or may not align with OS system time. The runtime system works towards aligning the two - system times. Depending on time - warp mode used, this may be achieved by letting the Erlang + system times. Depending on the + time warp mode used, + this can be achieved by letting Erlang system time perform a time warp.

@@ -197,35 +209,43 @@
Erlang Monotonic Time

A monotonically increasing time provided by the - Erlang runtime system. The Erlang monotonic time increase since - some unspecified point in time. It can be retrieved by calling + Erlang runtime system. Erlang monotonic time increases since + some unspecified point in time. To retrieve it, call erlang:monotonic_time(). - The - accuracy, and +

+ +

The accuracy and precision of Erlang - monotonic time heavily depends on the accuracy and precision of - OS monotonic time, - the accuracy and precision of - OS system time as well - as on the - time warp mode - used. On a system that is lacking OS monotonic time, the Erlang - monotonic time can only guarantee monotonicity and can more or less - not give any other guarantees. The frequency adjustments made to - the Erlang monotonic time depends on the time warp mode - used.

- -

Internally in the runtime system the Erlang monotonic + monotonic time heavily depends on the following:

+ + + Accuracy and precision of + OS monotonic time + + Accuracy and precision of + OS system time + + time warp mode used + + + +

On a system without OS monotonic time, Erlang monotonic + time guarantees monotonicity, but cannot give + other guarantees. The frequency adjustments made to + Erlang monotonic time depend on the time warp mode used.

+ +

Internally in the runtime system, Erlang monotonic time is the "time engine" that is used for more or less - everything that has anything to do with time. All timers + everything that has anything to do with time. All timers, regardless of it is a receive ... after timer, BIF timer, - or a timer in the timer module are triggered + or a timer in the timer module, are triggered relative Erlang monotonic time. Even Erlang system time is based on Erlang monotonic time. By adding current Erlang monotonic time with current time - offset you get current Erlang system time. Current time - offset can be retrieved by calling + offset, you get current Erlang system time.

+ +

To retrieve current time offset, call erlang:time_offset/0.

@@ -234,176 +254,169 @@
Introduction -

Time is vital to an Erlang program and, more importantly, correct time is vital to an Erlang program. As Erlang is a language with - soft real time properties and we have the possibility to express - time in our programs, the Virtual Machine and the language has to be - very careful about what is considered a correct point in time and in + soft real-time properties and we can express + time in our programs, the Virtual Machine and the language must be + careful about what is considered a correct time and in how time functions behave.

-

In the beginning, Erlang was constructed assuming that the wall +

When Erlang was designed, it was assumed that the wall clock time in the system showed a monotonic time moving forward at - exactly the same pace as the definition of time. That more or less - meant that an atomic clock (or better) was expected to be attached + exactly the same pace as the definition of time. This more or less meant + that an atomic clock (or better time source) was expected to be attached to your hardware and that the hardware was then expected to be - locked away from any human (or unearthly) tinkering for all - eternity. While this might be a compelling thought, it's simply - never the case.

- -

A "normal" modern computer can not keep time. Not on itself and - not unless you actually have a chip level atomic clock wired to - it. Time, as perceived by your computer, will normally need to be - corrected. Hence the NTP protocol that together with the ntpd - process will do it's best to keep your computers time in sync with - the "real" time in the universe. Between NTP corrections, usually a + locked away from any human tinkering forever. While this can be a + compelling thought, it is simply never the case.

+ +

A "normal" modern computer cannot keep time, not on itself and + not unless you have a chip-level atomic clock wired to it. Time, + as perceived by your computer, must normally be corrected. Hence + the Network Time Protocol (NTP) protocol, together with the ntpd + process, does its best to keep your computer time in sync with + the correct time. Between NTP corrections, usually a less potent time-keeper than an atomic clock is used.

-

But NTP is not fail safe. The NTP server can be unavailable, the - ntp.conf can be wrongly configured or your computer may from time to - time be disconnected from the internet. Furthermore you can have a - user (or even system administrator) on your system that thinks the - right way to handle daylight saving time is to adjust the clock one - hour two times a year (a tip, that is not the right way to do - it...). To further complicate things, this user fetched your - software from the internet and has never ever thought about what's - the correct time as perceived by a computer. The user simply does - not care about keeping the wall clock in sync with the rest of the - universe. The user expects your program to have omnipotent knowledge +

However, NTP is not fail-safe. The NTP server can be unavailable, + ntp.conf can be wrongly configured, or your computer may + sometimes be disconnected from Internet. Furthermore, you can have a + user (or even system administrator) who thinks the correct + way to handle Daylight Saving Time is to adjust the clock one + hour two times a year (which is the incorrect way to do it). + To complicate things further, this user fetched your + software from Internet and has not considered what + the correct time is as perceived by a computer. The user does + not care about keeping the wall clock in sync with the correct + time. The user expects your program to have unlimited knowledge about the time.

Most programmers also expect time to be reliable, at least until - they realize that the wall clock time on their workstation is of by - a minute. Then they simply set it to the correct time, maybe or - maybe not in a smooth way. Most probably not in a smooth way.

+ they realize that the wall clock time on their workstation is off by + a minute. Then they set it to the correct time, but most probably + not in a smooth way.

-

The amount of problems that arise when you expect the wall clock - time on the system to always be correct may be immense. Therefore Erlang +

The number of problems that arise when you always expect the wall clock + time on the system to be correct can be immense. Erlang therefore introduced the "corrected estimate of time", or the "time - correction" many years ago. The time correction relies on the fact + correction", many years ago. The time correction relies on the fact that most operating systems have some kind of monotonic clock, - either a real time extension or some built in "tick counter" that is - independent of the wall clock settings. This counter may have - microsecond resolution or much less, but generally it has a drift - that is not to be ignored.

- + either a real-time extension or some built-in "tick counter" that is + independent of the wall clock settings. This counter can have + microsecond resolution or much less, but it has a drift that cannot + be ignored.

-
+ Time Correction

If time correction is enabled, the Erlang runtime system - will make use of both + makes use of both OS system time and OS monotonic time, - in order to make adjustments of the frequency of the Erlang - monotonic clock. Time correction will ensure that + to adjust the frequency of the Erlang + monotonic clock. Time correction ensures that Erlang monotonic time - will not warp, and that the frequency is relatively accurate. - The type of adjustments made to the frequency depends on the - time warp mode used. This will be discussed in more details in - the time warp modes - section below.

- -

By default time correction will be enabled if support for - it on the specific platform exist. Support for it includes - both an OS monotonic time provided by the OS, and an - implementation in the Erlang runtime system utilizing the - OS monotonic time. You can check if your system has support - for OS monotonic time by calling - erlang:system_info(os_monotonic_time_source), - and you can check if time correction is enabled on your - system by calling + does not warp and that the frequency is relatively accurate. + The type of frequency adjustments depends on the time warp mode used. + Section Time Warp Modes + provides more details.

+ +

By default time correction is enabled if support for + it exists on the specific platform. Support for it includes + both OS monotonic time, provided by the OS, and an + implementation in the Erlang runtime system using + OS monotonic time. To check if your system has support + for OS monotonic time, call + erlang:system_info(os_monotonic_time_source). + To check if time correction is enabled on your system, call erlang:system_info(time_correction).

-

Time correction is enabled or disabled by passing the +

To enable or disable time correction, pass command-line argument +c [true|false] - command line argument to erl.

+ to erl.

If time correction is disabled, Erlang monotonic time - may warp forwards, it may stop and even freeze for extended - periods of time, and there are no guarantees that the frequency + may warp forwards or stop, or even freeze for extended + periods of time. There are then no guarantees that the frequency of the Erlang monotonic clock is accurate or stable.

You typically never want to disable time correction. - Previously there was a performance penalty associated with time - correction, but nowadays it is most often the other way around. - By disabling time correction you are likely to get bad scalability, + Previously a performance penalty was associated with time + correction, but nowadays it is usually the other way around. + If time correction is disabled, you probably get bad scalability, bad performance, and bad time measurements.

- -
+ Time Warp Safe Code -

Time warp safe code is code that is able to handle +

Time warp safe code can handle a time warp of - Erlang system time. -

+ Erlang system time.

erlang:now/0 - behaves very bad when Erlang system time warps. When Erlang - system time do a time warp backwards, the values returned - from erlang:now/0 will freeze (if you disregard the - micro second increments made due to the actual call) until - OS system time reach the point of the last value returned by - erlang:now/0. This freeze might continue for very - long periods of time. It might take years, decades, - and even longer than this until the freeze stops.

+ behaves bad when Erlang system time warps. When Erlang + system time does a time warp backwards, the values returned + from erlang:now/0 freeze (if you disregard the + microsecond increments made because of the actual call) until + OS system time reaches the point of the last value returned by + erlang:now/0. This freeze can continue for a long time. It + can take years, decades, and even longer until the freeze stops.

All uses of erlang:now/0 are not necessarily time warp unsafe. If you do not use it to get time, it - will be time warp safe. However all uses of + is time warp safe. However, all uses of erlang:now/0 are suboptimal from a performance and scalability perspective. So you really want to replace - the usage of it with other functionality. For examples - of how to replace the usage of erlang:now/0, - see the Dos and Donts - section.

+ the use of it with other functionality. For examples + of how to replace the use of erlang:now/0, see Section + How to Work with the New + API.

-
Time Warp Modes - +

Current Erlang system time is determined by adding current Erlang monotonic time with current time offset. The time offset is managed differently depending on which time - warp mode you use. The time warp mode is set by passing the + warp mode you use.

+ +

To set the time warp mode, pass command-line argument +C [no_time_warp|single_time_warp|multi_time_warp] - command line argument to erl.

+ to erl.

No Time Warp Mode

The time offset is determined at runtime system start - and will after this not change. This is the default behavior. - Not because it is the best mode (which it isn't). It is + and does not change later. This is the default behavior, but + not because it is the best mode (which it is not). It is default only because this is how the runtime system - always has behaved up until ERTS version 7.0, and you have to - ensure that your Erlang code that may execute during a time + behaved until ERTS 7.0. + Ensure that your Erlang code that may execute during a time warp is time warp - safe before you can enable other modes.

+ safe before enabling other modes.

-

Since the time offset is not allowed to change, time - correction needs to adjust the frequency of the Erlang - monotonic clock in order to smoothly align Erlang system - time with OS system time. A big downside of this approach +

As the time offset is not allowed to change, time + correction must adjust the frequency of the Erlang + monotonic clock to align Erlang system time with OS + system time smoothly. A significant downside of this approach is that we on purpose will use a faulty frequency on the Erlang monotonic clock if adjustments are needed. This - error may be as big as 1%. This error will show up in all + error can be as large as 1%. This error will show up in all time measurements in the runtime system.

-

If time correction is not enabled, the Erlang monotonic - time will freeze when the OS system time leap backwards. - The freeze of the monotonic time will continue until - OS system time catch up. The freeze may continue for - a very long time. When OS system time leaps forwards, - Erlang monotonic time will also leap forward.

+

If time correction is not enabled, Erlang monotonic + time freezes when OS system time leaps backwards. + The freeze of monotonic time continues until + OS system time catches up. The freeze can continue for + a long time. When OS system time leaps forwards, + Erlang monotonic time also leaps forward.

@@ -411,26 +424,27 @@ Single Time Warp Mode

This mode is more or less a backwards compatibility mode as of its introduction.

+

On an embedded system it is not uncommon that the system - has no power supply at all, not even a battery, when it is - shut off. The system clock on such a system will typically - be way off when the system boots. If the + has no power supply, not even a battery, when it is + shut off. The system clock on such a system is typically + way off when the system boots. If no time warp mode is used, and the Erlang runtime system is started before - the OS system time has been corrected, the Erlang system - time may be wrong for a very long time, even centuries or - more.

-

If you for some reason need to use Erlang code that - is not + OS system time has been corrected, Erlang system time + can be wrong for a long time, centuries or even longer.

+ +

If you need to use Erlang code that is not time warp safe, - and you need to start the Erlang runtime system before the OS + and you need to start the Erlang runtime system before OS system time has been corrected, you may want to use the single - time warp mode. Note that there are limitations to when you can + time warp mode.

+ +

There are limitations to when you can execute time warp unsafe code using this mode. If it is possible - to only utilize time warp safe code, it is much better - to use the multi time - warp mode instead. -

+ to use time warp safe code only, it is much better + to use the multi-time + warp mode instead.

Using the single time warp mode, the time offset is handled in two phases:

@@ -438,158 +452,150 @@ Preliminary Phase -

The preliminary phase starts when the runtime +

This phase starts when the runtime system starts. A preliminary time offset based on - current OS system time is determined. This offset will - from now on be fixed during the whole preliminary phase.

+ current OS system time is determined. This offset is from + now on to be fixed during the whole preliminary phase.

If time correction is enabled, adjustments to the - Erlang monotonic clock will be made to keep its - frequency as correct as possible, but no - adjustments will be made trying to align Erlang system - time and OS system time. That is, during the preliminary - Erlang system time and OS system time might diverge - from each other, and no attempt to prevent this will - be made.

+ Erlang monotonic clock are made to keep its + frequency as correct as possible. However, no + adjustments are made trying to align Erlang system + time and OS system time. That is, during the preliminary phase + Erlang system time and OS system time can diverge + from each other, and no attempt is made to prevent this.

If time correction is disabled, changes in OS system - time will effect the monotonic clock the same way as + time affects the monotonic clock the same way as when the no time warp mode is used.

Final Phase - -

The final phase begin when the user finalize the time +

This phase begins when the user finalizes the time offset by calling erlang:system_flag(time_offset, finalize). - The finalization can only be performed once. -

+ The finalization can only be performed once.

During finalization, the time offset is adjusted and - fixated so that current Erlang system time align with - current OS system time. Since the time offset may - change during the finalization, the Erlang system time - may do a time warp at this point. The time offset will - from now on be fixed until the runtime system terminates. + fixated so that current Erlang system time aligns with + current OS system time. As the time offset can + change during the finalization, Erlang system time + can do a time warp at this point. The time offset is + from now on fixed until the runtime system terminates. If time correction has been enabled, the time - correction will from now on also make adjustments - in order to align Erlang system time with OS system - time. When the system is in the final phase it behaves + correction from now on also makes adjustments + to align Erlang system time with OS system + time. When the system is in the final phase, it behaves exactly as in the no time warp mode.

-
-

In order for this to work properly there are two - requirements that the user needs to ensure are - satisfied:

+

In order for this to work properly, the user must ensure + that the following two requirements are satisfied:

Forward Time Warp

The time warp made when finalizing the time offset can only be done forwards without encountering problems. - This implies that the user has to ensure that the OS + This implies that the user must ensure that OS system time is set to a time earlier or equal to actual - POSIX time before starting the Erlang runtime system. If - you are not completely sure the OS system time is correct, + POSIX time before starting the Erlang runtime system.

+ +

If you are not sure that OS system time is correct, set it to a time that is guaranteed to be earlier than actual POSIX time before starting the Erlang runtime - system just to be safe.

+ system, just to be safe.

+ Finalize Correct OS System Time -

The OS system time needs to be correct when the - the user finalizes the time offset.

+

OS system time must be correct when + the user finalizes the time offset.

+

If these requirements are not fulfilled, the system - may behave very bad. -

- -

Assuming that the requirements above are fulfilled, - time correction is enabled, and that the OS system time - is adjusted using some time adjustment protocol like NTP - or similar, only small adjustments of the Erlang monotonic - time should be needed in order to keep system times - aligned after finilization. As long as the system is not - suspended, the largest adjustments needed should be for + may behave very bad.

+ +

Assuming that these requirements are fulfilled, + time correction is enabled, and that OS system time + is adjusted using a time adjustment protocol such as NTP, + only small adjustments of Erlang monotonic + time are needed to keep system times + aligned after finalization. As long as the system is not + suspended, the largest adjustments needed are for inserted (or deleted) leap seconds.

-

In order to be able to use this mode you have - to ensure that all Erlang code that will execute in - both phases are +

To use this mode, ensure that + all Erlang code that will execute in both phases are time warp safe.

-

Code that only execute in the final phase does not have +

Code executing only in the final phase does not have to be able to cope with the time warp.

-
- Multi Time Warp Mode - -

Multi time warp mode in combination with time - correction is the preferred configuration. This since, - on almost all platforms, the Erlang runtime system will have - better performance, will scale better, will behave better, - and since the accuracy, and precision of time measurements - will be better. Only Erlang runtime systems executing on - ancient platforms will benefit from another configuration.

+ Multi-Time Warp Mode +

Multi-time warp mode in combination with time + correction is the preferred configuration. This as + the Erlang runtime system have better performance, scale + better, and behave better on almost all platforms. In + addition, the accuracy and precision of time measurements + are better. Only Erlang runtime systems executing on + ancient platforms benefit from another configuration.

The time offset may change at any time without limitations. That is, Erlang system time may perform time warps both - forwards and backwards at any time. Since we align - the Erlang system time with the OS system time by changing + forwards and backwards at any time. As we align + Erlang system time with OS system time by changing the time offset, we can enable a time correction that tries to adjust the frequency of the Erlang monotonic clock to be as - correct as possible. This will make time measurements using - the Erlang monotonic time more accurate and precise.

+ correct as possible. This makes time measurements using + Erlang monotonic time more accurate and precise.

If time correction is disabled, Erlang monotonic time - will leap forward if OS system time leaps forward. If the - OS system time leaps backwards, Erlang monotonic time will - stop briefly but it does not freeze for extended periods - of time. This since the time offset is changed in order to + leaps forward if OS system time leaps forward. If + OS system time leaps backwards, Erlang monotonic time + stops briefly, but it does not freeze for extended periods + of time. This as the time offset is changed to align Erlang system time with OS system time.

-

In order to be able to use this mode you have - to ensure that all Erlang code that will execute on the - runtime system is +

To use this mode, ensure that all + Erlang code that will execute on the runtime system is time warp safe.

-
- The New Time API - + New Time API +

The old time API is based on erlang:now/0. - The major issue with erlang:now/0 is that it was - intended to be used for so many unrelated things. This - tied these unrelated operations together and unnecessarily - caused performance, scalability as well as accuracy, and - precision issues for operations that do not need to have - such issues. The new API spreads different functionality - over multiple functions in order to improve on this.

- -

In order to be backwards compatible erlang:now/0 will - remain as is, but you are strongly discouraged from using - it. A lot of uses of erlang:now/0 will also - prevent you from using the new - multi time warp - mode which is an important part of this + erlang:now/0 was intended to be used for many unrelated + things. This tied these unrelated operations together and + caused issues with performance, scalability, accuracy, and + precision for operations that did not need to have + such issues. To improve this, the new API spreads different + functionality over multiple functions.

+ +

To be backwards compatible, erlang:now/0 + remains as is, but you are strongly discouraged from using + it. Much use of erlang:now/0 + prevents you from using the new + multi-time warp + mode, which is an important part of this new time functionality improvement.

Some of the new BIFs on some systems, perhaps surprisingly, - return negative integer values on a newly started run time - system. This is not a bug, but a memory usage optimization.

+ return negative integer values on a newly started runtime + system. This is not a bug, but a memory use optimization.

+ +

The new API consists of the following new BIFs:

-

The new API consists of a number of new BIFs:

erlang:convert_time_unit/3

erlang:monotonic_time/0

@@ -604,7 +610,9 @@

os:system_time/0

os:system_time/1

-

and a number of extensions of existing BIFs:

+ +

The new API also consists of extensions of the following existing BIFs:

+

erlang:monitor(time_offset, clock_service)

erlang:system_flag(time_offset, finalize)

@@ -619,102 +627,99 @@
- The New Erlang Monotonic Time -

The Erlang monotonic time as such is new as of ERTS - version 7.0. It has been introduced in order to be able - to detach time measurements such as elapsed time from - calender time. It is very common that one is interested - in measuring elapsed time or specifying a time relative - to another point in time without having any need to know - what the involved times are in UTC or any other - globally defined time scale. By introducing a time scale - that has a local definition of where it starts, it is - possible to manage time that do not concern calender - time on that time scale. Erlang monotonic time use + New Erlang Monotonic Time +

Erlang monotonic time as such is new as of ERTS 7.0. + It is introduced to detach time measurements, such as elapsed + time from calendar time. Many programmers want to measure elapsed + time or specify a time relative to another point in time without + knowing the involved times in UTC or any other globally defined + time scale. By introducing a time scale + with a local definition of where it starts, time that do + not concern calendar time on that time scale can be managed. + Erlang monotonic time uses such a time scale with a locally defined start.

-

The introduction of Erlang monotonic time gives us - the possibility to adjust the two Erlang times (Erlang +

The introduction of Erlang monotonic time allows + us to adjust the two Erlang times (Erlang monotonic time and Erlang system time) separately. By - doing this, accuracy of elapsed time does not have to + doing this, the accuracy of elapsed time does not have to suffer just because the system time happened to be wrong at some point in time. Separate adjustments of the two times are only performed in the time warp modes, and only fully separated in the - multi - time warp mode. All other modes than the - multi time warp mode are there for backwards - compatibility reasons, and when using these the - accuracy of Erlang monotonic time suffer since + multi-time + warp mode. All other modes than the + multi-time warp mode are for backwards + compatibility reasons. When using these modes, the + accuracy of Erlang monotonic time suffer, as the adjustments of Erlang monotonic time in these - modes are more or less tied to the Erlang system - time.

+ modes are more or less tied to Erlang system time.

The adjustment of system time could have been made smother than using a time warp approach, but we think - that would be a bad choice. Since we are able to - express and measure time that aren't connected to - calender time by the use of Erlang monotonic time, it + that would be a bad choice. As we can + express and measure time that is not connected to + calendar time by the use of Erlang monotonic time, it is better to expose the change in Erlang system time - immediately. This since it makes it possible for the - Erlang applications executing on the system to react - on the change in system time as soon as possible. This - is also more or less exactly how most OSes handle this + immediately. This as the Erlang applications + executing on the system can react on the change in + system time as soon as possible. This is also more or + less exactly how most operating systems handle this (OS monotonic time and OS system time). By adjusting - system time smoothly we would just hide the fact that + system time smoothly, we would just hide the fact that system time changed and make it harder for the Erlang applications to react to the change in a sensible way.

-

In order to be able to react to a change in Erlang - system time you have to be able to detect that it +

To be able to react to a change in Erlang + system time, you must be able to detect that it happened. The change in Erlang system time occurs when current time offset is changed. We have therefore - introduced the possibility to monitor the time offset - using - erlang:monitor(time_offset, clock_service). A process monitoring the time - offset will be sent a message on the following format + introduced the possibility to monitor the time offset using + erlang:monitor(time_offset, clock_service). + A process monitoring the time + offset is sent a message on the following format when the time offset is changed:

+ {'CHANGE', MonitorReference, time_offset, clock_service, NewTimeOffset}
Unique Values -

Besides reporting time erlang:now/0 also - produce unique and strictly monotonically increasing - values. In order to detach this functionality from - time measurements we have introduced +

Besides reporting time, erlang:now/0 also + produces unique and strictly monotonically increasing + values. To detach this functionality from + time measurements, we have introduced erlang:unique_integer().

- Dos and Don'ts + How to Work with the New API

Previously erlang:now/0 was the only option for doing - quite a lot of things. We will look at a few different things - erlang:now/0 could be used for, and how you want to do - this using the new API:

+ many things. This section deals with some things that + erlang:now/0 can be used for, and how you are to + these using the new API.

Retrieve Erlang System Time

- use erlang:now/0 in order to retrieve current Erlang - system time. + Use erlang:now/0 to retrieve current Erlang system time.

- use + Use erlang:system_time/1 - in order to retrieve current Erlang system time on the + to retrieve current Erlang system time on the time unit of your choice.

If you want the same format as returned by erlang:now/0, use erlang:timestamp/0. -

+

@@ -723,26 +728,27 @@ Measure Elapsed Time

- take timestamps with erlang:now/0 and calculate + Take timestamps with erlang:now/0 and calculate the difference in time with timer:now_diff/2.

- take timestamps with + Take timestamps with erlang:monotonic_time/0 and calculate the time difference using ordinary subtraction. The result will be in native time unit. If you want to convert the - result to another time unit you can do this using + result to another time unit, you can use erlang:convert_time_unit/3.

-

Another easier way of doing this is to use + +

An easier way to do this is to use erlang:monotonic_time/1 - with desired time unit. However, you may lose accuracy, - and precision this way. + with the desired time unit. However, you can then lose accuracy + and precision.

@@ -752,16 +758,16 @@ Determine Order of Events

- determine the order of events by saving a timestamp - with erlang:now/0 when the event happens. + Determine the order of events by saving a timestamp + with erlang:now/0 when the event occurs.

- determine the order of events by saving the integer + Determine the order of events by saving the integer returned by erlang:unique_integer([monotonic]) - when the event happens. These integers will be strictly + when the event occurs. These integers will be strictly monotonically ordered on current runtime system instance corresponding to creation time.

@@ -770,40 +776,43 @@
- Determine Order of Events With Time of the Event + Determine Order of Events with Time of the Event

- determine the order of events by saving a timestamp - with erlang:now/0 when the event happens. + Determine the order of events by saving a timestamp + with erlang:now/0 when the event occurs.

- determine the order of events by saving a tuple - containing + Determine the order of events by saving a tuple containing monotonic time and a strictly - monotonically increasing integer like this:

+ monotonically increasing integer as follows:

+ Time = erlang:monotonic_time(), UMI = erlang:unique_integer([monotonic]), EventTag = {Time, UMI} +

These tuples will be strictly monotonically ordered - on the current runtime system instance according to - creation time. Note that it is important that the + on current runtime system instance according to + creation time. It is important that the monotonic time is in the first element (the most significant element when comparing 2-tuples). Using the monotonic time in the tuples, you can calculate time between events.

-

If you are interested in the Erlang system time at the - time when the event occurred you can also save the time + +

If you are interested in Erlang system time at the + time when the event occurred, you can also save the time offset before or after saving the events using erlang:time_offset/0. Erlang monotonic time added with the time offset corresponds to Erlang system time.

+

If you are executing in a mode where time offset - may change and you want to be able to get the actual - Erlang system time when the event occurred you can + can change, and you want to get the actual + Erlang system time when the event occurred, you can save the time offset as a third element in the tuple (the least significant element when comparing 3-tuples).

@@ -814,16 +823,15 @@ EventTag = {Time, UMI}
Create a Unique Name

- use the values returned from erlang:now/0 - in order to create a name unique on the current - runtime system instance. + Use the values returned from erlang:now/0 + to create a name unique on the current runtime system instance.

- use the value returned from + Use the value returned from erlang:unique_integer/0 - in order to create a name unique on the current runtime system + to create a name unique on the current runtime system instance. If you only want positive integers, you can use erlang:unique_integer([positive]).

@@ -832,48 +840,62 @@ EventTag = {Time, UMI}
- Seed Random Number Generation With a Unique Value + Seed Random Number Generation with a Unique Value

- seed random number generation using erlang:now(). + Seed random number generation using erlang:now().

- seed random number generation using a combination of + Seed random number generation using a combination of erlang:monotonic_time(), erlang:time_offset(), - erlang:unique_integer(), and other functionality. + erlang:unique_integer(), + and other functionality.

-

To sum this section up: Don't use erlang:now/0!

+

To sum up this section: Do not use erlang:now/0.

-
- Supporting Both New and Old OTP Releases -

Your code may be required to be able to run on a variety + + Support of Both New and Old OTP Releases +

It can be required that your code must run on a variety of OTP installations of different OTP releases. If so, you - can not just use the new API out of the box, since it will + cannot use the new API out of the box, as it will not be available on old pre OTP 18 releases. The solution - is not to avoid using the new API, since your - code then won't be able to benefit from the scalability - and accuracy improvements made. Instead you want to use the + is not to avoid using the new API, as your + code then would not benefit from the scalability + and accuracy improvements made. Instead, use the new API when available, and fall back on erlang:now/0 - when it is not available. Fortunately almost all of the new - API can easily be implemented using existing primitives - (except for - erlang:system_info(start_time), - erlang:system_info(end_time), - erlang:system_info(os_monotonic_time_source), and - erlang:system_info(os_system_time_source)). - By wrapping the API with functions that fall back on - erlang:now/0 when the new API is not available, - and using these wrappers instead of using the API directly - the problem is solved. These wrappers can for example + when the new API is unavailable.

+ +

Fortunately most of the new API can easily be + implemented using existing primitives, except for:

+ + + + erlang:system_info(start_time) + + + erlang:system_info(end_time) + + + erlang:system_info(os_monotonic_time_source) + + + erlang:system_info(os_system_time_source)) + + + +

By wrapping the API with functions that fall back on + erlang:now/0 when the new API is unavailable, + and using these wrappers instead of using the API directly, + the problem is solved. These wrappers can, for example, be implemented as in $ERL_TOP/erts/example/time_compat.erl.

-- cgit v1.2.3 From d3413b5ea34b592b92dc0d17a23c35c731368ad1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 23 Sep 2015 14:48:47 +0200 Subject: erts: Review time correction docs --- erts/doc/src/time_correction.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index aec9efa3d3..4de3739a36 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -584,7 +584,7 @@

To be backwards compatible, erlang:now/0 remains as is, but you are strongly discouraged from using - it. Much use of erlang:now/0 + it. Many use cases of erlang:now/0 prevents you from using the new multi-time warp mode, which is an important part of this @@ -630,14 +630,14 @@ New Erlang Monotonic Time

Erlang monotonic time as such is new as of ERTS 7.0. It is introduced to detach time measurements, such as elapsed - time from calendar time. Many programmers want to measure elapsed - time or specify a time relative to another point in time without - knowing the involved times in UTC or any other globally defined - time scale. By introducing a time scale - with a local definition of where it starts, time that do - not concern calendar time on that time scale can be managed. - Erlang monotonic time uses - such a time scale with a locally defined start.

+ time from calendar time. In many use cases there is a need to + measure elapsed time or specify a time relative to another point + in time without the need to know the involved times in UTC or + any other globally defined time scale. By introducing a time + scale with a local definition of where it starts, time that do + not concern calendar time can be managed on that time + scale. Erlang monotonic time uses such a time scale with a + locally defined start.

The introduction of Erlang monotonic time allows us to adjust the two Erlang times (Erlang -- cgit v1.2.3 From e17e236cd1661bc8f5bb1ebef0d80e93eb8f5b36 Mon Sep 17 00:00:00 2001 From: xsipewe Date: Tue, 1 Sep 2015 12:15:27 +0200 Subject: erts: Update module erlang docs Rebased 6ac77046b05cd3cb7b117 on OTP-18.1 Conflicts: erts/doc/src/erlang.xml --- erts/doc/src/erlang.xml | 6896 +++++++++++++++++++++++++---------------------- 1 file changed, 3702 insertions(+), 3194 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 37f0aa289e..8b20a5c12f 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -30,33 +30,34 @@ erlang.xml erlang - The Erlang BIFs + The Erlang BIFs. -

By convention, most built-in functions (BIFs) are seen as being - in the module erlang. A number of the BIFs are viewed more +

By convention, most Built-In Functions (BIFs) are seen as being + in this module. Some of the BIFs are viewed more or less as part of the Erlang programming language and are - auto-imported. Thus, it is not necessary to specify - the module name and both the calls atom_to_list(Erlang) and - erlang:atom_to_list(Erlang) are identical.

-

In the text, auto-imported BIFs are listed without module prefix. + auto-imported. Thus, it is not necessary to specify the + module name. For example, the calls atom_to_list(Erlang) + and erlang:atom_to_list(Erlang) are identical.

+

Auto-imported BIFs are listed without module prefix. BIFs listed with module prefix are not auto-imported.

-

BIFs may fail for a variety of reasons. All BIFs fail with +

BIFs can fail for various reasons. All BIFs fail with reason badarg if they are called with arguments of an - incorrect type. The other reasons that may make BIFs fail are - described in connection with the description of each individual - BIF.

-

Some BIFs may be used in guard tests, these are marked with + incorrect type. The other reasons are described in the + description of each individual BIF.

+

Some BIFs can be used in guard tests and are marked with "Allowed in guard tests".

- ext_binary() + ext_binary() +

A binary data object, structured according to the Erlang external term format.

+

See erlang:timestamp/0.

@@ -139,12 +140,15 @@ - - - Arithmetical absolute value - -

Returns an integer or float which is the arithmetical - absolute value of Float or Int.

+ Arithmetical absolute value. + + Float = float() + Int = integer() + + +

Returns an integer or float that is the arithmetical + absolute value of Float or + Int, for example:

 > abs(-3.33).
 3.33
@@ -153,206 +157,217 @@
         

Allowed in guard tests.

+ - Compute adler32 checksum + Computes adler32 checksum. -

Computes and returns the adler32 checksum for Data.

+

Computes and returns the adler32 checksum for + Data.

+ - Compute adler32 checksum + Computes adler32 checksum. -

Continue computing the adler32 checksum by combining - the previous checksum, OldAdler, with the checksum of - Data.

-

The following code:

- - X = erlang:adler32(Data1), - Y = erlang:adler32(X,Data2). - -

- would assign the same value to Y as this would:

- - Y = erlang:adler32([Data1,Data2]). - +

Continues computing the adler32 checksum by combining + the previous checksum, OldAdler, with + the checksum of Data.

+

The following code:

+ + X = erlang:adler32(Data1), + Y = erlang:adler32(X,Data2). +

assigns the same value to Y as this:

+ + Y = erlang:adler32([Data1,Data2]).
+ - Combine two adler32 checksums - -

Combines two previously computed adler32 checksums. - This computation requires the size of the data object for - the second checksum to be known.

-

The following code:

- - Y = erlang:adler32(Data1), - Z = erlang:adler32(Y,Data2). - -

- would assign the same value to Z as this would:

- - X = erlang:adler32(Data1), - Y = erlang:adler32(Data2), - Z = erlang:adler32_combine(X,Y,iolist_size(Data2)). - + Combines two adler32 checksums. + +

Combines two previously computed adler32 checksums. + This computation requires the size of the data object for + the second checksum to be known.

+

The following code:

+ + Y = erlang:adler32(Data1), + Z = erlang:adler32(Y,Data2). +

assigns the same value to Z as this:

+ + X = erlang:adler32(Data1), + Y = erlang:adler32(Data2), + Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).
+ - Append an extra element to a tuple - -

Returns a new tuple which has one element more than - Tuple1, and contains the elements in Tuple1 - followed by Term as the last element. Semantically - equivalent to - list_to_tuple(tuple_to_list(Tuple1) ++ [Term]), but much - faster.

+ Appends an extra element to a tuple. + +

Returns a new tuple that has one element more than + Tuple1, and contains the elements in + Tuple1 + followed by Term as the last element. + Semantically equivalent to + list_to_tuple(tuple_to_list(Tuple1) ++ + [Term]), but much faster.

+

Example:

 > erlang:append_element({one, two}, three).
 {one,two,three}
+ - Apply a function to an argument list + Applies a function to an argument list. -

Call a fun, passing the elements in Args as - arguments.

-

Note: If the number of elements in the arguments are known at - compile-time, the call is better written as +

Calls a fun, passing the elements in Args + as arguments.

+

If the number of elements in the arguments are known at + compile time, the call is better written as Fun(Arg1, Arg2, ... ArgN).

Earlier, Fun could also be given as {Module, Function}, equivalent to - apply(Module, Function, Args). This usage is - deprecated and will stop working in a future release of - Erlang/OTP.

+ apply(Module, Function, Args). This use is + deprecated and will stop working in a future release.

+ - Apply a function to an argument list + Applies a function to an argument list.

Returns the result of applying Function in - Module to Args. The applied function must + Module to Args. + The applied function must be exported from Module. The arity of the function is the length of Args.

+

Example:

 > apply(lists, reverse, [[a, b, c]]).
 [c,b,a]
-

apply can be used to evaluate BIFs by using +

apply evaluates BIFs by using the module name erlang.

 > apply(erlang, atom_to_list, ['Erlang']).
 "Erlang"
-

Note: If the number of arguments are known at compile-time, +

If the number of arguments are known at compile time, the call is better written as Module:Function(Arg1, Arg2, ..., ArgN).

Failure: error_handler:undefined_function/3 is called if the applied function is not exported. The error handler can be redefined (see process_flag/2). - If the error_handler is undefined, or if the user has + If error_handler is undefined, or if the user has redefined the default error_handler so the replacement module is undefined, an error with the reason undef is generated.

+ - Return the binary representation of an atom - -

Returns a binary which corresponds to the text - representation of Atom. If Encoding - is latin1, there will be one byte for each character - in the text representation. If Encoding is - utf8 or - unicode, the characters will be encoded using UTF-8 - (meaning that characters from 16#80 up to 0xFF will be - encoded in two bytes).

- -

Currently, atom_to_binary(Atom, latin1) 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 atom_to_binary(Atom, latin1) will fail if the - text representation for the Atom contains a Unicode - character greater than 16#FF.

- + Returns the binary representation of an atom. + +

Returns a binary corresponding to the text + representation of Atom. + If Encoding + is latin1, there is one byte for each character + in the text representation. If Encoding is + utf8 or + unicode, the characters are encoded using UTF-8 + (that is, characters from 16#80 through 0xFF are + encoded in two bytes).

+

atom_to_binary(Atom, latin1) never + fails because the text representation of an atom can only + contain characters from 0 through 16#FF. In a future release, + the text representation + of atoms can be allowed to contain any Unicode character and + atom_to_binary(Atom, latin1) will then fail if the + text representation for Atom contains a Unicode + character greater than 16#FF.

+

Example:

 > atom_to_binary('Erlang', latin1).
 <<"Erlang">>
+ - Text representation of an atom + Text representation of an atom. -

Returns a string which corresponds to the text - representation of Atom.

+

Returns a string corresponding to the text + representation of Atom, for example:

 > atom_to_list('Erlang').
 "Erlang"
+ - Extracts a part of a binary + Extracts a part of a binary. -

Extracts the part of the binary described by PosLen.

- -

Negative length can be used to extract bytes at the end of a binary:

- +

Extracts the part of the binary described by + PosLen.

+

Negative length can be used to extract bytes at the end + of a binary, for example:

1> Bin = <<1,2,3,4,5,6,7,8,9,10>>. 2> binary_part(Bin,{byte_size(Bin), -5}). -<<6,7,8,9,10>> - - -

If PosLen in any way references outside the binary, a badarg exception is raised.

- -

Start is zero-based, i.e.:

+<<6,7,8,9,10>> +

Failure: badarg if PosLen in any way + references outside the binary.

+

Start is zero-based, that is:

1> Bin = <<1,2,3>> 2> binary_part(Bin,{0,2}). -<<1,2>> - - -

See the STDLIB module binary for details about the PosLen semantics.

- +<<1,2>> +

For details about the PosLen semantics, see the + binary + manual page in STDLIB.

Allowed in guard tests.

+ - Extracts a part of a binary + Extracts a part of a binary. -

The same as binary_part(Subject, {Start, Length}).

- +

The same as binary_part(Subject, + {Start, Length}).

Allowed in guard tests.

+ - Convert from text representation to an atom + Converts from text representation to an atom.

Returns the atom whose text representation is - Binary. If Encoding is latin1, no - translation of bytes in the binary is done. If Encoding - is utf8 or unicode, the binary must contain - valid UTF-8 sequences; furthermore, only Unicode characters up - to 0xFF are allowed.

- -

binary_to_atom(Binary, utf8) will fail if - the binary contains Unicode characters greater than 16#FF. - In a future release, such Unicode characters might be allowed - and binary_to_atom(Binary, utf8) - will not fail in that case. For more information on Unicode support in atoms - see note on UTF-8 encoded atoms - in the chapter about the external term format in the ERTS User's Guide.

- + Binary. + If Encoding is latin1, no + translation of bytes in the binary is done. + If Encoding + is utf8 or unicode, the binary must contain + valid UTF-8 sequences. Only Unicode characters up + to 0xFF are allowed.

+

binary_to_atom(Binary, utf8) fails if + the binary contains Unicode characters greater than 16#FF. + In a future release, such Unicode characters can be allowed + and binary_to_atom(Binary, utf8) does then not fail. + For more information on Unicode support in atoms, see the + note on UTF-8 + encoded atoms + in Section "External Term Format" in the User's Guide.

+

Examples:

 > binary_to_atom(<<"Erlang">>, latin1).
 'Erlang'
@@ -362,20 +377,24 @@
         called as binary_to_atom(<<208,128>>,utf8)
+ - Convert from text representation to an atom + Converts from text representation to an atom. -

Works like binary_to_atom/2, - but the atom must already exist.

-

Failure: badarg if the atom does not already exist.

+

As + binary_to_atom/2, + but the atom must exist.

+

Failure: badarg if the atom does not exist.

+ - Convert from text representation to a float + Converts from text representation to a float. -

Returns the float whose text representation is Binary.

+

Returns the float whose text representation is + Binary, for example:

 > binary_to_float(<<"2.2017764e+0">>).
 2.2017764
@@ -383,12 +402,13 @@ representation of a float.

+ - Convert from text representation to an integer + Converts from text representation to an integer.

Returns an integer whose text representation is - Binary.

+ Binary, for example:

 > binary_to_integer(<<"123">>).
 123
@@ -396,12 +416,13 @@ representation of an integer.

+ - Convert from text representation to an integer + Converts from text representation to an integer.

Returns an integer whose text representation in base - Base is Binary.

+ Base is Binary, for example:

 > binary_to_integer(<<"3FF">>, 16).
 1023
@@ -409,93 +430,101 @@ representation of an integer.

+ - Convert a binary to a list + Converts a binary to a list. -

Returns a list of integers which correspond to the bytes of +

Returns a list of integers corresponding to the bytes of Binary.

+ - Convert part of a binary to a list + Converts part of a binary to a list. 1..byte_size(Binary)

As binary_to_list/1, but returns a list of integers corresponding to the bytes from position Start to - position Stop in Binary. Positions in the + position Stop in Binary. + The positions in the binary are numbered starting from 1.

- -

This function's indexing style of using one-based indices for - binaries is deprecated. New code should use the functions in - the STDLIB module binary instead. They consequently - use the same (zero-based) style of indexing.

+

The indexing style of using one-based indices for + binaries is deprecated for this function. New code is to + use the functions in module binary in STDLIB + instead. They therefore + use the same (zero-based) style of indexing.

+ - Convert a bitstring to a list + Converts a bitstring to a list. -

Returns a list of integers which correspond to the bytes of - Bitstring. 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).

+

Returns a list of integers corresponding to the bytes of + Bitstring. If the number of bits in the binary + is not divisible by 8, the last element of the list is a bitstring + containing the remaining 1-7 bits.

+ - Decode an Erlang external term format binary + Decodes an Erlang external term format binary. -

Returns an Erlang term which is the result of decoding - the binary object Binary, which must be encoded +

Returns an Erlang term that is the result of decoding + binary object Binary, which must be encoded according to the Erlang external term format.

- -

When decoding binaries from untrusted sources, consider using - binary_to_term/2 to prevent denial of service attacks.

-
-

See also - term_to_binary/1 - and - binary_to_term/2.

+

When decoding binaries from untrusted sources, + consider using binary_to_term/2 to prevent Denial + of Service attacks.

+

See also + term_to_binary/1 + and + binary_to_term/2.

+ - Decode an Erlang external term format binary + Decodes an Erlang external term format binary.

As binary_to_term/1, but takes options that affect decoding of the binary.

safe -

Use this option when receiving binaries from an untrusted +

Use this option when receiving binaries from an untrusted source.

-

When enabled, it prevents decoding data that may be used to - attack the Erlang system. In the event of receiving unsafe - data, decoding fails with a badarg error.

-

Currently, this prevents creation of new atoms directly, - creation of new atoms indirectly (as they are embedded in - certain structures like pids, refs, funs, etc.), and creation of - new external function references. None of those resources are - currently garbage collected, so unchecked creation of them can - exhaust available memory.

+

When enabled, it prevents decoding data that can be used to + attack the Erlang system. In the event of receiving unsafe + data, decoding fails with a badarg error.

+

This prevents creation of new atoms directly, + creation of new atoms indirectly (as they are embedded in + certain structures, such as process identifiers, + refs, and funs), and + creation of new external function references. + None of those resources are garbage collected, so unchecked + creation of them can exhaust available memory.

-

Failure: badarg if safe is specified and unsafe data - is decoded.

+

Failure: badarg if safe is specified and unsafe + data is decoded.

See also term_to_binary/1, binary_to_term/1, - and - list_to_existing_atom/1.

+ and + list_to_existing_atom/1.

+ - Return the size of a bitstring + Returns the size of a bitstring. -

Returns an integer which is the size in bits of Bitstring.

+

Returns an integer that is the size in bits of + Bitstring, for example:

 > bit_size(<<433:16,3:3>>).
 19
@@ -504,30 +533,34 @@
         

Allowed in guard tests.

+ - Increment the reduction counter + Increments the reduction counter.

This implementation-dependent function increments the reduction counter for the calling process. In the Beam emulator, the reduction counter is normally incremented by - one for each function and BIF call, and a context switch is - forced when the counter reaches the maximum number of reductions - for a process (2000 reductions in R12B).

+ one for each function and BIF call. A context switch is + forced when the counter reaches the maximum number of + reductions for a process (2000 reductions in OTP R12B).

-

This BIF might be removed in a future version of the Beam +

This BIF can be removed in a future version of the Beam machine without prior warning. It is unlikely to be implemented in other Erlang implementations.

+ - Return the size of a bitstring (or binary) + Returns the size of a bitstring (or binary). -

Returns an integer which is the number of bytes needed to contain - Bitstring. (That is, if the number of bits in Bitstring is not - divisible by 8, the resulting number of bytes will be rounded up.)

+

Returns an integer that is the number of bytes needed to + contain Bitstring. That is, if the number of bits + in Bitstring is not divisible by 8, the resulting + number of bytes is rounded up.

+

Examples:

 > byte_size(<<433:16,3:3>>).
 3
@@ -536,12 +569,13 @@
         

Allowed in guard tests.

+ - Cancel a timer + Cancels a timer.

- Cancels a timer that has been created by either + Cancels a timer that has been created by erlang:start_timer(), or erlang:send_after(). TimerRef identifies the timer, and @@ -606,7 +640,7 @@ the timeout message has been sent, but it does not tell you whether or not it has arrived at its destination yet. When the Result is an integer, it represents the - time in milli-seconds left until the timer will expire. + time in milli-seconds left until the timer would have expired.

@@ -632,7 +666,7 @@ - Cancel a timer + Cancels a timer.

Cancels a timer. The same as calling erlang:cancel_timer(TimerRef, @@ -641,100 +675,99 @@ - Check if a module has old code + Checks if a module has old code. -

Returns true if the Module has old code, - and false otherwise.

+

Returns true if Module has old code, + otherwise false.

See also code(3).

+ - Check if a process is executing old code for a module + Checks if a process executes old code for a module.

The same as - erlang:check_process_code(Pid, - Module, []).

+ erlang:check_process_code(Pid, Module, []).

+ - Check if a process is executing old code for a module + Checks if a process executes old code for a module. -

Check if the node local process identified by Pid - is executing old code for Module.

-

Currently available Options:

+

Checks if the node local process identified by Pid + executes old code for Module.

+

The available Options are as follows:

{allow_gc, boolean()} - Determines if garbage collection is allowed when performing - the operation. If {allow_gc, false} is passed, and - a garbage collection is needed in order to determine the - result of the operation, the operation will be aborted - (see information on CheckResult below). - The default is to allow garbage collection, i.e., - {allow_gc, true}. +

Determines if garbage collection is allowed when performing + the operation. If {allow_gc, false} is passed, and + a garbage collection is needed to determine the + result of the operation, the operation is aborted (see + information on CheckResult in the following). + The default is to allow garbage collection, that is, + {allow_gc, true}.

{async, RequestId} - The check_process_code/3 function will return - the value async immediately after the request - has been sent. When the request has been processed, the - process that called this function will be passed a - message on the form:
- {check_process_code, RequestId, CheckResult}. +

The function check_process_code/3 returns + the value async immediately after the request + has been sent. When the request has been processed, the + process that called this function is passed a + message on the form + {check_process_code, RequestId, CheckResult}.

-

If Pid equals self(), and - no async option has been passed, the operation will - be performed at once. In all other cases a request for - the operation will be sent to the process identified by - Pid, and will be handled when - appropriate. If no async option has been passed, - the caller will block until CheckResult - is available and can be returned.

-

CheckResult informs about the result of - the request:

+

If Pid equals self(), and + no async option has been passed, the operation + is performed at once. Otherwise a request for + the operation is sent to the process identified by + Pid, and is handled when + appropriate. If no async option has been passed, + the caller blocks until CheckResult + is available and can be returned.

+

CheckResult informs about the result of + the request as follows:

true - The process identified by Pid is - executing old code for Module. - That is, the current call of the process executes old - code for this module, or the process has references - to old code for this module, or the process contains - funs that references old code for this module. +

The process identified by Pid + executes old code for Module. + That is, the current call of the process executes old + code for this module, or the process has references + to old code for this module, or the process contains + funs that references old code for this module.

false - The process identified by Pid is - not executing old code for Module. +

The process identified by Pid does + not execute old code for Module.

aborted - The operation was aborted since the process needed to - be garbage collected in order to determine the result - of the operation, and the operation was requested - by passing the {allow_gc, false} option. +

The operation was aborted, as the process needed to + be garbage collected to determine the operation result, + and the operation was requested + by passing option {allow_gc, false}.

See also code(3).

Failures:

badarg - - If Pid is not a node local process identifier. + If Pid is not a node local process identifier. badarg - - If Module is not an atom. + If Module is not an atom. badarg - - If OptionList is not a valid list of options. + If OptionList is an invalid list of options.
+ Convert time unit of a time value @@ -753,99 +786,101 @@ - Compute crc32 (IEEE 802.3) checksum + Computes crc32 (IEEE 802.3) checksum. -

Computes and returns the crc32 (IEEE 802.3 style) checksum for Data.

+

Computes and returns the crc32 (IEEE 802.3 style) checksum + for Data.

+ - Compute crc32 (IEEE 802.3) checksum + Computes crc32 (IEEE 802.3) checksum. -

Continue computing the crc32 checksum by combining - the previous checksum, OldCrc, with the checksum of - Data.

-

The following code:

- - X = erlang:crc32(Data1), - Y = erlang:crc32(X,Data2). - -

- would assign the same value to Y as this would:

- - Y = erlang:crc32([Data1,Data2]). - +

Continues computing the crc32 checksum by combining + the previous checksum, OldCrc, with the checksum of + Data.

+

The following code:

+ + X = erlang:crc32(Data1), + Y = erlang:crc32(X,Data2). +

assigns the same value to Y as this:

+ + Y = erlang:crc32([Data1,Data2]).
+ - Combine two crc32 (IEEE 802.3) checksums - -

Combines two previously computed crc32 checksums. - This computation requires the size of the data object for - the second checksum to be known.

-

The following code:

- - Y = erlang:crc32(Data1), - Z = erlang:crc32(Y,Data2). - -

- would assign the same value to Z as this would:

+ Combines two crc32 (IEEE 802.3) checksums. + +

Combines two previously computed crc32 checksums. + This computation requires the size of the data object for + the second checksum to be known.

+

The following code:

+ + Y = erlang:crc32(Data1), + Z = erlang:crc32(Y,Data2). +

assigns the same value to Z as this:

- X = erlang:crc32(Data1), - Y = erlang:crc32(Data2), - Z = erlang:crc32_combine(X,Y,iolist_size(Data2)). - + X = erlang:crc32(Data1), + Y = erlang:crc32(Data2), + Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).
+ - Current date + Current date.

Returns the current date as {Year, Month, Day}.

-

The time zone and daylight saving time correction depend on +

The time zone and Daylight Saving Time correction depend on the underlying OS.

+

Example:

 > date().
 {1995,2,19}
+ - Extracts a protocol packet from a binary + Extracts a protocol packet from a binary. -

Decodes the binary Bin according to the packet - protocol specified by Type. Very similar to the packet - handling done by sockets with the option {packet,Type}.

-

If an entire packet is contained in Bin it is + protocol specified by Type. Similar to the packet + handling done by sockets with option {packet,Type}.

+

If an entire packet is contained in Bin, it is returned together with the remainder of the binary as {ok,Packet,Rest}.

If Bin does not contain the entire packet, - {more,Length} is returned. Length is either the - expected total size of the packet or undefined - if the expected packet size is not known. decode_packet + {more,Length} is returned. + Length is either the + expected total size of the packet, or undefined + if the expected packet size is unknown. decode_packet can then be called again with more data added.

-

If the packet does not conform to the protocol format +

If the packet does not conform to the protocol format, {error,Reason} is returned.

-

The following values of Type are valid:

+

The following Types are valid:

raw | 0 -

No packet handling is done. Entire binary is +

No packet handling is done. The entire binary is returned unless it is empty.

1 | 2 | 4

Packets consist of a header specifying the number of bytes in the packet, followed by that number of bytes. - The length of header can be one, two, or four bytes; + The length of the header can be one, two, or four bytes; the order of the bytes is big-endian. The header - will be stripped off when the packet is returned.

+ is stripped off when the packet is returned.

line -

A packet is a line terminated with newline. The +

A packet is a line-terminated with newline. The newline character is included in the returned packet - unless the line was truncated according to the option + unless the line was truncated according to option line_length.

asn1 | cdr | sunrm | fcgi | tpkt @@ -864,41 +899,46 @@

The Hypertext Transfer Protocol. The packets are returned with the format according to - HttpPacket described above. A packet is either a - request, a response, a header or an end of header - mark. Invalid lines are returned as HttpError.

-

Recognized request methods and header fields are returned as atoms. - Others are returned as strings. Strings of unrecognized header fields - are formatted with only capital letters first and after hyphen characters - (like "Sec-Websocket-Key").

-

The protocol type http should only be used for - the first line when a HttpRequest or a - HttpResponse is expected. The following calls - should use httph to get HttpHeader's until - http_eoh is returned that marks the end of the + HttpPacket described earlier. + A packet is either a + request, a response, a header, or an end of header + mark. Invalid lines are returned as + HttpError.

+

Recognized request methods and header fields are returned + as atoms. Others are returned as strings. Strings of + unrecognized header fields are formatted with only + capital letters first and after hyphen characters, for + example, "Sec-Websocket-Key".

+

The protocol type http is only to be used for + the first line when an HttpRequest or an + HttpResponse is expected. + The following calls are to use httph to get + HttpHeaders until + http_eoh is returned, which marks the end of the headers and the beginning of any following message body.

-

The variants http_bin and httph_bin will return +

The variants http_bin and httph_bin return strings (HttpString) as binaries instead of lists.

The following options are available:

{packet_size, integer() >= 0} -

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.

+

Sets the maximum allowed size of the packet body. + If the packet header indicates that the length of the + packet is longer than the maximum allowed length, the + packet is considered invalid. Default is 0, which means + no size limit.

{line_length, integer() >= 0} -

For packet type line, truncate lines longer - than the indicated length.

-

Option line_length also applies to http* - packet types as an alias for option packet_size in the - case when packet_size itself is not set. This usage is - only intended for backward compatibility.

+

For packet type line, lines longer than + the indicated length are truncated.

+

Option line_length also applies to http* + packet types as an alias for option packet_size + if packet_size itself is not set. This use is + only intended for backward compatibility.

+

Examples:

 > erlang:decode_packet(1,<<3,"abcd">>,[]).
 {ok,<<"abc">>,<<"d">>}
@@ -909,13 +949,11 @@
 
     
       
-      Delete element at index in a tuple
+      Deletes element at index in a tuple.
       1..tuple_size(Tuple1)
       
-		  

- Returns a new tuple with element at Index removed from - tuple Tuple1. -

+

Returns a new tuple with element at Index + removed from tuple Tuple1, for example:

 > erlang:delete_element(2, {one, two, three}).
 {one,three}
@@ -924,78 +962,82 @@ - Make the current code for a module old + Makes the current code for a module old. -

Makes the current code for Module become old code, and - deletes all references for this module from the export table. +

Makes the current code for Module become old code, + and deletes all references for this module from the export table. Returns undefined if the module does not exist, otherwise true.

This BIF is intended for the code server (see - code(3)) and should not be - used elsewhere.

+ code(3)) and is not + to be used elsewhere.

-

Failure: badarg if there is already an old version of +

Failure: badarg if there already is an old version of Module.

+ - Stop monitoring + Stops monitoring. -

If MonitorRef is a reference which the calling process - obtained by calling +

If MonitorRef is a reference that the + calling process obtained by calling monitor/2, this monitoring is turned off. If the monitoring is already turned off, nothing happens.

-

Once demonitor(MonitorRef) has returned it is - guaranteed that no {'DOWN', MonitorRef, _, _, _} message - due to the monitor will be placed in the caller's message queue - in the future. A {'DOWN', MonitorRef, _, _, _} message - might have been placed in the caller's message queue prior to - the call, though. Therefore, in most cases, it is advisable +

Once demonitor(MonitorRef) has returned, it is + guaranteed that no {'DOWN', + MonitorRef, _, _, _} message, + because of the monitor, will be placed in the caller message queue + in the future. A {'DOWN', + MonitorRef, _, _, _} message + can have been placed in the caller message queue before + the call, though. It is therefore usually advisable to remove such a 'DOWN' message from the message queue - after monitoring has been stopped. - demonitor(MonitorRef, [flush]) can be used instead of + after monitoring has been stopped. + demonitor(MonitorRef, [flush]) + can be used instead of demonitor(MonitorRef) if this cleanup is wanted.

-

Prior to OTP release R11B (erts version 5.5) demonitor/1 - behaved completely asynchronous, i.e., the monitor was active - until the "demonitor signal" reached the monitored entity. This - had one undesirable effect, though. You could never know when - you were guaranteed not to receive a DOWN message - due to the monitor.

-

Current behavior can be viewed as two combined operations: - asynchronously send a "demonitor signal" to the monitored entity - and ignore any future results of the monitor.

+

Before OTP R11B (ERTS 5.5), demonitor/1 + behaved asynchronous, that is, the monitor was active + until the "demonitor signal" reached the monitored entity. + This had an undesirable effect, as you could never know when + you were guaranteed not to receive a DOWN + message because of the monitor.

+

The current behavior can be viewed as two combined operations: + asynchronously send a "demonitor signal" to the monitored + entity and ignore any future results of the monitor.

Failure: It is an error if MonitorRef refers to a monitoring started by another process. Not all such cases are - cheap to check; if checking is cheap, the call fails with - badarg (for example if MonitorRef is a remote - reference).

+ cheap to check. If checking is cheap, the call fails with + badarg for example, if MonitorRef is a + remote reference.

+ - Stop monitoring + Stops monitoring.

The returned value is true unless info is part - of OptionList. -

+ of OptionList.

demonitor(MonitorRef, []) is equivalent to demonitor(MonitorRef).

-

Currently the following Options are valid:

+

The available Options are as follows:

flush -

Remove (one) {_, MonitorRef, _, _, _} message, - if there is one, from the caller's message queue after +

Removes (one) {_, + MonitorRef, _, _, _} message, + if there is one, from the caller message queue after monitoring has been stopped.

Calling demonitor(MonitorRef, [flush]) is equivalent to the following, but more efficient:

- demonitor(MonitorRef), receive {_, MonitorRef, _, _, _} -> @@ -1006,78 +1048,90 @@
info -

The returned value is one of the following:

- - true -

The monitor was found and removed. In this case - no 'DOWN' message due to this monitor have - been nor will be placed in the message queue - of the caller. -

-
- false -

The monitor was not found and could not be removed. - This probably because someone already has placed a - 'DOWN' message corresponding to this monitor - in the caller's message queue. -

-
-
-

If the info option is combined with the flush - option, false will be returned if a flush was needed; - otherwise, true. -

+

The returned value is one of the following:

+ + true + The monitor was found and removed. In this case, + no 'DOWN' message corresponding to this + monitor has been delivered and will not be delivered. + + false + The monitor was not found and could not be removed. + This probably because someone already has placed a + 'DOWN' message corresponding to this monitor + in the caller message queue. + + +

If option info is combined with option flush, + false is returned if a flush was needed, + otherwise true.

-

More options may be added in the future.

+

More options can be added in a future release.

-

Failure: badarg if OptionList is not a list, or - if Option is not a valid option, or the same failure as for - demonitor/1

+

Failures:

+ + badarg + If OptionList is not a list. + + badarg + If Option is an invalid option. + + badarg + The same failure as for + demonitor/1. + +
+ - Force the disconnection of a node + Forces the disconnection of a node. -

Forces the disconnection of a node. This will appear to - the node Node as if the local node has crashed. This - BIF is mainly used in the Erlang network authentication - protocols. Returns true if disconnection succeeds, +

Forces the disconnection of a node. This appears to + the node Node as if the local node has crashed. + This BIF is mainly used in the Erlang network authentication + protocols.

+

Returns true if disconnection succeeds, otherwise false. If the local node is not alive, - the function returns ignored.

+ ignored is returned.

+ - Print a term on standard output + Prints a term on standard output. -

Prints a text representation of Term on the standard - output. On OSE the term is printed to the ramlog.

+

Prints a text representation of Term on the + standard output. On OSE, the term is printed to the ramlog.

This BIF is intended for debugging only.

+ 1..tuple_size(Tuple) - Get Nth element of a tuple + Returns the Nth element of a tuple.

Returns the Nth element (numbering from 1) of - Tuple.

+ Tuple, for example:

 > element(2, {a, b, c}).
 b

Allowed in guard tests.

+ - Return and delete the process dictionary + Returns and deletes the process dictionary. -

Returns the process dictionary and deletes it.

+

Returns the process dictionary and deletes it, for + example:

 > put(key1, {1, 2, 3}),
 put(key2, [a, b, c]),
@@ -1085,13 +1139,16 @@ b
[{key1,{1,2,3}},{key2,[a,b,c]}]
+ - Return and delete a value from the process dictionary + Returns and deletes a value from the process dictionary. -

Returns the value Val associated with Key and - deletes it from the process dictionary. Returns - undefined if no value is associated with Key.

+

Returns the value Val associated with + Key and deletes it from the process dictionary. + Returns undefined if no value is associated with + Key.

+

Example:

 > put(key1, {merry, lambs, are, playing}),
 X = erase(key1),
@@ -1099,16 +1156,19 @@ b
{{merry,lambs,are,playing},undefined}
+ - Stop execution with a given reason + Stops execution with a given reason.

Stops the execution of the calling process with the reason - Reason, where Reason is any term. The actual - exit reason will be {Reason, Where}, where Where + Reason, where Reason + is any term. The exit reason is + {Reason, Where}, where Where 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.

+

Example:

 > catch error(foobar).
 {'EXIT',{foobar,[{erl_eval,do_apply,5},
@@ -1118,29 +1178,34 @@ b
{shell,eval_loop,3}]}}
+ - Stop execution with a given reason + Stops execution with a given reason.

Stops the execution of the calling process with the reason - Reason, where Reason is any term. The actual - exit reason will be {Reason, Where}, where Where + Reason, where Reason + is any term. The exit reason is + {Reason, Where}, where Where is a list of the functions most recently called (the current - function first). Args 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 Where term. Since evaluating this function causes + function first). Args is expected to be the + list of arguments for the current function; in Beam it is used + to provide the arguments for the current function in + the term Where. Since evaluating this function causes the process to terminate, it has no return value.

+ - Stop execution with a given reason + Stops execution with a given reason. -

Stops the execution of the calling process with the exit - reason Reason, where Reason is any term. Since +

Stops the execution of the calling process with exit reason + Reason, where Reason + is any term. Since evaluating this function causes the process to terminate, it has no return value.

+

Example:

 > exit(foobar).
 ** exception exit: foobar
@@ -1148,110 +1213,117 @@ b
{'EXIT',foobar}
+ - Send an exit signal to a process or a port + Sends an exit signal to a process or a port.

Sends an exit signal with exit reason Reason to the process or port identified by Pid.

-

The following behavior apply if Reason is any term - except normal or kill:

-

If Pid is not trapping exits, Pid itself will - exit with exit reason Reason. If Pid is trapping - exits, the exit signal is transformed into a message - {'EXIT', From, Reason} and delivered to the message - queue of Pid. From is the pid of the process - which sent the exit signal. See also - process_flag/2.

-

If Reason is the atom normal, Pid will - not exit. If it is trapping exits, the exit signal is - transformed into a message {'EXIT', From, normal} - and delivered to its message queue.

-

If Reason is the atom kill, that is if - exit(Pid, kill) is called, an untrappable exit signal - is sent to Pid which will unconditionally exit with - exit reason killed.

+

The following behavior applies if Reason + is any term, except normal or kill:

+ + If Pid is not trapping exits, + Pid + itself exits with exit reason Reason. + + If Pid is trapping exits, the exit + signal is transformed into a message + {'EXIT', From, Reason} + and delivered to the message queue of Pid. + + From is the process identifier of the process + that sent the exit signal. See also + process_flag/2. + + +

If Reason is the atom normal, + Pid + does not exit. If it is trapping exits, the exit signal is + transformed into a message {'EXIT', From, normal} + and delivered to its message queue.

+

If Reason is the atom kill, + that is, if exit(Pid, kill) is called, + an untrappable exit signal is sent to Pid, + which unconditionally exits with exit reason killed. +

+ - Calculate the maximum size for a term encoded in the Erlang - external term format + Calculates the maximum size for a term encoded in the Erlang external term format.

Calculates, without doing the encoding, the maximum byte size for a term encoded in the Erlang external term format. The following condition applies always:

-

 > Size1 = byte_size(term_to_binary(Term)),
 > Size2 = erlang:external_size(Term),
 > true = Size1 =< Size2.
-true
-          
-

-

This is equivalent to a call to: erlang:external_size(Term, []) -

+true +

This is equivalent to a call to:

+erlang:external_size(Term, [])
+ - Calculate the maximum size for a term encoded in the Erlang - external term format + Calculates the maximum size for a term encoded in the Erlang external term format.

Calculates, without doing the encoding, the maximum byte size for a term encoded in the Erlang external term format. The following condition applies always:

-

 > Size1 = byte_size(term_to_binary(Term, Options)),
 > Size2 = erlang:external_size(Term, Options),
 > true = Size1 =< Size2.
-true
-          
-

-

The option {minor_version, Version} specifies how floats - are encoded. See - term_to_binary/2 for - a more detailed description. -

+true +

Option {minor_version, Version} specifies how + floats are encoded. For a detailed description, see + term_to_binary/2.

+ - Convert a number to a float + Converts a number to a float. -

Returns a float by converting Number to a float.

+

Returns a float by converting Number to a float, + for example:

 > float(55).
 55.0

Allowed in guard tests.

-

Note that if used on the top-level in a guard, it will - test whether the argument is a floating point number; for - clarity, use +

If used on the top level in a guard, it tests whether the + argument is a floating point number; for clarity, use is_float/1 instead.

When float/1 is used in an expression in a guard, such as 'float(A) == 4.0', it converts a number as - described above.

+ described earlier.

+ - Text representation of a float + Text representation of a float. -

The same as float_to_binary(Float,[{scientific,20}]).

+

The same as + float_to_binary(Float,[{scientific,20}]).

+ - Text representation of a float formatted using given options + Text representation of a float formatted using given options. -

Returns a binary which corresponds to the text +

Returns a binary corresponding to the text representation of Float using fixed decimal - point formatting. The Options behave in the same - way as float_to_list/2. -

+ point formatting. Options behaves in the same + way as float_to_list/2.

+

Examples:

 > float_to_binary(7.12, [{decimals, 4}]).
 <<"7.1200">>
@@ -1259,31 +1331,42 @@ true
 <<"7.12">>
+ - Text representation of a float + Text representation of a float. -

The same as float_to_list(Float,[{scientific,20}]).

+

The same as + float_to_list(Float,[{scientific,20}]).

+ - Text representation of a float formatted using given options - -

Returns a string which corresponds to the text - representation of Float using fixed decimal point formatting. - When decimals option is specified - the returned value will contain at most Decimals number of - digits past the decimal point. If the number doesn't fit in the - internal static buffer of 256 bytes, the function throws badarg. - When compact option is provided - the trailing zeros at the end of the list are truncated (this option is - only meaningful together with the decimals option). When - scientific option is provided, the float will be formatted using - scientific notation with Decimals digits of precision. If - Options is [] the function behaves like - float_to_list/1. -

+ Text representation of a float formatted using given options. + +

Returns a string corresponding to the text representation + of Float using fixed decimal point formatting. The + options are as follows:

+ + If option decimals is specified, the returned value + contains at most Decimals number of digits past the + decimal point. If the number does not fit in the internal + static buffer of 256 bytes, the function throws badarg. + + If option compact is provided, the trailing zeros + at the end of the list are truncated. This option is only + meaningful together with option decimals. + + If option scientific is provided, the float is + formatted using scientific notation with Decimals + digits of precision. + + If Options is [], the function behaves as + float_to_list/1. + + +

Examples:

 > float_to_list(7.12, [{decimals, 4}]).
 "7.1200"
@@ -1291,36 +1374,40 @@ true
 "7.12"
+ - Information about a fun + Information about a fun. -

Returns a list containing information about the fun - Fun. Each element of the list is a tuple. The order of - the tuples is not defined, and more tuples may be added in a +

Returns a list with information about the fun + Fun. Each list element is a tuple. The order + of the tuples is undefined, and more tuples can be added in a future release.

This BIF is mainly intended for debugging, but it can - occasionally be useful in library functions that might need - to verify, for instance, the arity of a fun.

+ sometimes be useful in library functions that need + to verify, for example, the arity of a fun.

-

There are two types of funs with slightly different - semantics:

-

A fun created by fun M:F/A is called an - external fun. Calling it will always call the - function F with arity A in the latest code for - module M. Note that module M does not even need - to be loaded when the fun fun M:F/A is created.

-

All other funs are called local. When a local fun - is called, the same version of the code that created the fun - will be called (even if newer version of the module has been - loaded).

-

The following elements will always be present in the list +

Two types of funs have slightly different semantics:

+ + A fun created by fun M:F/A is called an + external fun. Calling it will always call the + function F with arity A in the latest code for + module M. Notice that module M does not even + need to be loaded when the fun fun M:F/A is created. + + All other funs are called local. When a local fun + is called, the same version of the code that created the fun + is called (even if a newer version of the module has been + loaded). + + +

The following elements are always present in the list for both local and external funs:

{type, Type} -

Type is either local or external.

+

Type is local or external.

{module, Module} @@ -1335,148 +1422,154 @@ true

Name (an atom) is a function name.

If Fun is a local fun, Name is the name of the local function that implements the fun. - (This name was generated by the compiler, and is generally + (This name was generated by the compiler, and is only of informational use. As it is a local function, it - is not possible to call it directly.) + cannot be called directly.) If no code is currently loaded for the fun, [] - will be returned instead of an atom.

+ is returned instead of an atom.

If Fun is an external fun, Name is the name of the exported function that the fun refers to.

{arity, Arity}

Arity is the number of arguments that the fun - should be called with.

+ is to be called with.

{env, Env}

Env (a list) is the environment or free variables - for the fun. (For external funs, the returned list is - always empty.)

+ for the fun. For external funs, the returned list is + always empty.

-

The following elements will only be present in the list if +

The following elements are only present in the list if Fun is local:

{pid, Pid} -

Pid is the pid of the process that originally - created the fun.

+

Pid is the process identifier of the process + that originally created the fun.

{index, Index} -

Index (an integer) is an index into the module's +

Index (an integer) is an index into the module fun table.

{new_index, Index} -

Index (an integer) is an index into the module's +

Index (an integer) is an index into the module fun table.

{new_uniq, Uniq} -

Uniq (a binary) is a unique value for this fun. - It is calculated from the compiled code for the entire module.

+

Uniq (a binary) is a unique value for this fun. It + is calculated from the compiled code for the entire module.

{uniq, Uniq}

Uniq (an integer) is a unique value for this fun. - Starting in the R15 release, this integer is calculated from - the compiled code for the entire module. Before R15, this - integer was based on only the body of the fun. -

+ As from OTP R15, this integer is calculated from the + compiled code for the entire module. Before OTP R15, this + integer was based on only the body of the fun.

+ + Information about a fun. - Information about a fun

Returns information about Fun as specified by - Item, in the form {Item,Info}.

+ Item, in the form + {Item,Info}.

For any fun, Item can be any of the atoms - module, name, arity, env, or type.

-

For a local fun, Item can also be any of the atoms - index, new_index, new_uniq, + module, name, arity, env, or + type.

+

For a local fun, Item can also be any of the + atoms index, new_index, new_uniq, uniq, and pid. For an external fun, the value of any of these items is always the atom undefined.

See erlang:fun_info/1.

+ - Text representation of a fun + Text representation of a fun. -

Returns a string which corresponds to the text +

Returns a string corresponding to the text representation of Fun.

+ - Check if a function is exported and loaded + Checks if a function is exported and loaded.

Returns true if the module Module is loaded and contains an exported function Function/Arity, or if there is a BIF (a built-in function implemented in C) - with the given name; otherwise returns false.

+ with the given name, otherwise returns false.

This function used to return false for built-in functions before the 18.0 release.

+ - Force an immediate garbage collection of the calling process + Forces an immediate garbage collection of the calling process. -

Forces an immediate garbage collection of the currently - executing process. The function should not be used, unless - it has been noticed -- or there are good reasons to suspect -- +

Forces an immediate garbage collection of the + executing process. The function is not to be used unless + it has been noticed (or there are good reasons to suspect) that the spontaneous garbage collection will occur too late - or not at all. Improper use may seriously degrade system - performance.

+ or not at all.

+ +

Improper use can seriously degrade system performance.

+
+ - Garbage collect a process + Garbage collects a process.

The same as garbage_collect(Pid, []).

+ - Garbage collect a process + Garbage collects a process. -

Garbage collect the node local process identified by - Pid.

-

Currently available Options:

+

Garbage collects the node local process identified by + Pid.

+

The available Options are as follows:

{async, RequestId} - - The garbage_collect/2 function will return + The function garbage_collect/2 returns the value async immediately after the request has been sent. When the request has been processed, the - process that called this function will be passed a - message on the form:
- {garbage_collect, RequestId, GCResult}. -
+ process that called this function is passed a message on + the form {garbage_collect, + RequestId, GCResult}. +

If Pid equals self(), and no async option has been passed, the garbage - collection will be performed at once, i.e. the same as - calling + collection is performed at once, that is, the same as calling garbage_collect/0. - In all other cases a request for garbage collection will - be sent to the process identified by Pid, + Otherwise a request for garbage collection + is sent to the process identified by Pid, and will be handled when appropriate. If no async - option has been passed, the caller will block until - GCResult is available and can be - returned.

+ option has been passed, the caller blocks until + GCResult is available and can be returned.

GCResult informs about the result of - the garbage collection request:

+ the garbage collection request as follows:

true @@ -1485,14 +1578,13 @@ true false - No garbage collection was performed. This since the + No garbage collection was performed, as the process identified by Pid terminated before the request could be satisfied. -

Note that the same caveats as for - garbage_collect/0 - apply.

+

Notice that the same caveats apply as for + garbage_collect/0.

Failures:

badarg @@ -1501,17 +1593,18 @@ true badarg - If OptionList is not a valid list of options. + If OptionList is an invalid list of options.
+ - Return the process dictionary + Returns the process dictionary.

Returns the process dictionary as a list of - {Key, Val} tuples.

+ {Key, Val} tuples, for example:

 > put(key1, merry),
 put(key2, lambs),
@@ -1520,13 +1613,15 @@ true
 [{key1,merry},{key2,lambs},{key3,{are,playing}}]
+ - Return a value from the process dictionary + Returns a value from the process dictionary.

Returns the value Val associated with Key in the process dictionary, or undefined if Key does not exist.

+

Example:

 > put(key1, merry),
 put(key2, lambs),
@@ -1535,14 +1630,16 @@ true
 {are,playing}
+ - Get the magic cookie of the local node + Gets the magic cookie of the local node. -

Returns the magic cookie of the local node, if the node is - alive; otherwise the atom nocookie.

+

Returns the magic cookie of the local node if the node is + alive, otherwise the atom nocookie.

+ Return a list of all keys from the process dictionary @@ -1558,10 +1655,10 @@ true - Return a list of keys from the process dictionary + Returns a list of keys from the process dictionary. -

Returns a list of keys which are associated with the value - Val in the process dictionary.

+

Returns a list of keys that are associated with the value + Val in the process dictionary, for example:

 > put(mary, {1, 2}),
 put(had, {1, 2}),
@@ -1573,40 +1670,40 @@ true
 [mary,had,a,little,lamb]
+ - Get the call stack back-trace of the last exception + Gets the call stack back-trace of the last exception. -

Get the call stack back-trace (stacktrace) of the last - exception in the calling process as a list of +

Gets the call stack back-trace (stacktrace) of the + last exception in the calling process as a list of {Module,Function,Arity,Location} tuples. - The Arity field in the first tuple may be the argument - list of that function call instead of an arity integer, + Field Arity in the first tuple can be the + argument list of that function call instead of an arity integer, depending on the exception.

If there has not been any exceptions in a process, the stacktrace is []. After a code change for the process, - the stacktrace may also be reset to [].

+ the stacktrace can also be reset to [].

The stacktrace is the same data as the catch operator returns, for example:

{'EXIT',{badarg,Stacktrace}} = catch abs(x)

-

Location 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 - items may occur:

+

Location is a (possibly empty) list + of two-tuples that + can indicate the location in the source code of the function. + The first element is an atom describing the type of + information in the second element. The following + items can occur:

file - -

The second element of the tuple is a string (list of + The second element of the tuple is a string (list of characters) representing the filename of the source file - of the function.

+ of the function.
line - -

The second element of the tuple is the line number + The second element of the tuple is the line number (an integer greater than zero) in the source file - where the exception occurred or the function was called.

+ where the exception occurred or the function was called.

See also @@ -1614,49 +1711,56 @@ true erlang:error/2.

+ - Get the group leader for the calling process + Gets the group leader for the calling process. -

Returns the pid of the group leader for the process which - evaluates the function.

+

Returns the process identifier of the group leader for the + process evaluating the function.

Every process is a member of some process group and all - groups have a group leader. All IO from the group + groups have a group leader. All I/O from the group is channeled to the group leader. When a new process is spawned, it gets the same group leader as the spawning - process. Initially, at system start-up, init is both + process. Initially, at system startup, init is both its own group leader and the group leader of all processes.

+ - Set the group leader for a process + Sets the group leader for a process. -

Sets the group leader of Pid to GroupLeader. - Typically, this is used when a processes started from a - certain shell should have another group leader than +

Sets the group leader of Pid + to GroupLeader. + Typically, this is used when a process started from a + certain shell is to have another group leader than init.

See also group_leader/0.

+ - Halt the Erlang runtime system and indicate normal exit to the calling environment + Halts the Erlang runtime system and indicates normal exit to the calling environment.

The same as halt(0, []).

+

Example:

 > halt().
 os_prompt% 
+ - Halt the Erlang runtime system + Halts the Erlang runtime system.

The same as halt(Status, []).

+

Example:

 > halt(17).
 os_prompt% echo $?
@@ -1664,178 +1768,188 @@ os_prompt% echo $?
 os_prompt% 
+ - Halt the Erlang runtime system + Halts the Erlang runtime system.

Status must be a non-negative integer, a string, or the atom abort. Halts the Erlang runtime system. Has no return value. - Depending on Status: -

+ Depending on Status, the following occurs:

integer() - The runtime system exits with the integer value Status - as status code to the calling environment (operating system). + The runtime system exits with integer value + Status + as status code to the calling environment (OS). string() - An erlang crash dump is produced with Status as slogan, - and then the runtime system exits with status code 1. + An Erlang crash dump is produced with Status + as slogan. Then the runtime system exits with status code 1. abort The runtime system aborts producing a core dump, if that is - enabled in the operating system. + enabled in the OS. -

Note that on many platforms, only the status codes 0-255 are - supported by the operating system. -

-

For integer Status the Erlang runtime system closes all ports - and allows async threads to finish their operations before exiting. - To exit without such flushing use - Option as {flush,false}. -

-

For statuses string() and abort the flush - option is ignored and flushing is not done. -

+

On many platforms, the OS supports only status + codes 0-255.

+

For integer Status, the Erlang runtime system + closes all ports and allows async threads to finish their + operations before exiting. To exit without such flushing, use + Option as {flush,false}.

+

For statuses string() and abort, option + flush is ignored and flushing is not done.

+ - Hash function (deprecated) + Hash function (deprecated).

Returns a hash value for Term within the range - 1..Range. The allowed range is 1..2^27-1.

+ 1..Range. The range is 1..2^27-1.

-

This BIF is deprecated as the hash value may differ on - different architectures. Also the hash values for integer - terms larger than 2^27 as well as large binaries are very +

This BIF is deprecated, as the hash value can differ on + different architectures. The hash values for integer + terms higher than 2^27 and large binaries are poor. The BIF is retained for backward compatibility - reasons (it may have been used to hash records into a file), - but all new code should use one of the BIFs + reasons (it can have been used to hash records into a file), + but all new code is to use one of the BIFs erlang:phash/2 or erlang:phash2/1,2 instead.

+ - Head of a list + Head of a list. -

Returns the head of List, that is, the first element.

+

Returns the head of List, that is, + the first element, for example:

 > hd([1,2,3,4,5]).
 1

Allowed in guard tests.

-

Failure: badarg if List is the empty list [].

+

Failure: badarg if List is the empty + list [].

+ - Hibernate a process until a message is sent to it + Hibernates a process until a message is sent to it.

Puts the calling process into a wait state where its memory - allocation has been reduced as much as possible, which is + allocation has been reduced as much as possible. This is useful if the process does not expect to receive any messages - in the near future.

-

The process will be awaken when a message is sent to it, and - control will resume in Module:Function with - the arguments given by Args with the call stack - emptied, meaning that the process will terminate when that - function returns. Thus erlang:hibernate/3 will never - return to its caller.

+ soon.

+

The process is awaken when a message is sent to it, and control + resumes in Module:Function with + the arguments given by Args with the call + stack emptied, meaning that the process terminates when that + function returns. Thus erlang:hibernate/3 never + returns to its caller.

If the process has any message in its message queue, - the process will be awaken immediately in the same way as - described above.

+ the process is awakened immediately in the same way as + described earlier.

In more technical terms, what erlang:hibernate/3 does - is the following. It discards the call stack for the process. - Then it garbage collects the process. After the garbage - collection, all live data is in one continuous heap. The heap + is the following. It discards the call stack for the process, + and then garbage collects the process. After this, + all live data is in one continuous heap. The heap is then shrunken to the exact same size as the live data - which it holds (even if that size is less than the minimum + that it holds (even if that size is less than the minimum heap size for the process).

If the size of the live data in the process is less than the minimum heap size, the first garbage collection occurring - after the process has been awaken will ensure that the heap + after the process is awakened ensures that the heap size is changed to a size not smaller than the minimum heap size.

-

Note that emptying the call stack means that any surrounding - catch is removed and has to be re-inserted after +

Notice that emptying the call stack means that any surrounding + catch is removed and must be reinserted after hibernation. One effect of this is that processes started using proc_lib (also indirectly, such as - gen_server processes), should use + gen_server processes), are to use proc_lib:hibernate/3 - instead to ensure that the exception handler continues to work + instead, to ensure that the exception handler continues to work when the process wakes up.

- Insert an element at index in a tuple + Inserts an element at index in a tuple. 1..tuple_size(Tuple1) + 1 -

- Returns a new tuple with element Term insert at position - Index in tuple Tuple1. - All elements from position Index and upwards are subsequently - pushed one step higher in the new tuple Tuple2. -

+

Returns a new tuple with element Term + inserted at position + Index in tuple Tuple1. + All elements from position Index and upwards are + pushed one step higher in the new tuple Tuple2.

+

Example:

 > erlang:insert_element(2, {one, two, three}, new).
 {one,new,two,three}
+ - Text representation of an integer + Text representation of an integer. -

Returns a binary which corresponds to the text - representation of Integer.

+

Returns a binary corresponding to the text + representation of Integer, for example:

 > integer_to_binary(77).
 <<"77">>
+ - Text representation of an integer + Text representation of an integer. -

Returns a binary which corresponds to the text - representation of Integer in base Base.

+

Returns a binary corresponding to the text + representation of Integer in base + Base, for example:

 > integer_to_binary(1023, 16).
 <<"3FF">>
+ - Text representation of an integer + Text representation of an integer. -

Returns a string which corresponds to the text - representation of Integer.

+

Returns a string corresponding to the text + representation of Integer, for example:

 > integer_to_list(77).
 "77"
+ - Text representation of an integer + Text representation of an integer. -

Returns a string which corresponds to the text - representation of Integer in base Base.

+

Returns a string corresponding to the text + representation of Integer in base + Base, for example:

 > integer_to_list(1023, 16).
 "3FF"
+ - Convert an iolist to a binary + Converts an iolist to a binary. -

Returns a binary which is made from the integers and - binaries in IoListOrBinary.

+

Returns a binary that is made from the integers and + binaries in IoListOrBinary, for example:

 > Bin1 = <<1,2,3>>.
 <<1,2,3>>
@@ -1847,278 +1961,311 @@ os_prompt% 
<<1,2,3,1,2,3,4,5,4,6>>
+ - Size of an iolist + Size of an iolist. -

Returns an integer which is the size in bytes - of the binary that would be the result of - iolist_to_binary(Item).

+

Returns an integer that is the size in bytes + of the binary that would be the result of + iolist_to_binary(Item), for example:

 > iolist_size([1,2|<<3,4>>]).
 4
+ - Check whether the local node is alive + Checks whether the local node is alive. -

Returns true if the local node is alive; that is, if - the node can be part of a distributed system. Otherwise, it - returns false.

+

Returns true if the local node is alive (that is, if + the node can be part of a distributed system), otherwise + false.

+ - Check whether a term is an atom + Checks whether a term is an atom. -

Returns true if Term is an atom; - otherwise returns false.

+

Returns true if Term is an atom, + otherwise false.

Allowed in guard tests.

+ - Check whether a term is a binary + Checks whether a term is a binary. -

Returns true if Term is a binary; - otherwise returns false.

- +

Returns true if Term is a binary, + otherwise false.

A binary always contains a complete number of bytes.

-

Allowed in guard tests.

+ - Check whether a term is a bitstring + Checks whether a term is a bitstring. -

Returns true if Term is a bitstring (including a binary); - otherwise returns false.

- +

Returns true if Term is a + bitstring (including a binary), otherwise false.

Allowed in guard tests.

+ - Check whether a term is a boolean + Checks whether a term is a boolean. -

Returns true if Term is - either the atom true or the atom false - (i.e. a boolean); otherwise returns false.

+

Returns true if Term is the + atom true or the atom false (that is, a boolean). + Otherwise returns false.

Allowed in guard tests.

+ - Check if a function is a BIF implemented in C + Checks if a function is a BIF implemented in C. -

Returns true if Module:Function/Arity is - a BIF implemented in C; otherwise returns false. - This BIF is useful for builders of cross reference tools.

+

This BIF is useful for builders of cross-reference tools.

+

Returns true if + Module:Function/Arity + ia a BIF implemented in C, otherwise false.

+ - Check whether a term is a float + Checks whether a term is a float.

Returns true if Term is a floating point - number; otherwise returns false.

+ number, otherwise false.

Allowed in guard tests.

+ - Check whether a term is a fun + Checks whether a term is a fun. -

Returns true if Term is a fun; otherwise - returns false.

+

Returns true if Term is a fun, otherwise + false.

Allowed in guard tests.

+ - Check whether a term is a fun with a given arity + Checks whether a term is a fun with a given arity.

Returns true if Term is a fun that can be - applied with Arity number of arguments; otherwise - returns false.

+ applied with Arity number of arguments, otherwise + false.

Allowed in guard tests.

+ - Check whether a term is an integer + Checks whether a term is an integer. -

Returns true if Term is an integer; - otherwise returns false.

+

Returns true if Term is an integer, + otherwise false.

Allowed in guard tests.

+ - Check whether a term is a list + Checks whether a term is a list.

Returns true if Term is a list with - zero or more elements; otherwise returns false.

+ zero or more elements, otherwise false.

Allowed in guard tests.

+ - Check whether a term is a map + Checks whether a term is a map. -

Returns true if Term is a map; - otherwise returns false.

+

Returns true if Term is a map, + otherwise false.

Allowed in guard tests.

+ - Check whether a term is a number + Checks whether a term is a number. -

Returns true if Term is either an integer or a - floating point number; otherwise returns false.

+

Returns true if Term is an integer or a + floating point number. Otherwise returns false.

Allowed in guard tests.

+ - Check whether a term is a pid + Checks whether a term is a process identifier. -

Returns true if Term is a pid (process - identifier); otherwise returns false.

+

Returns true if Term is a process + identifier, otherwise false.

Allowed in guard tests.

+ - Check whether a term is a port + Checks whether a term is a port. -

Returns true if Term is a port identifier; - otherwise returns false.

+

Returns true if Term is a port identifier, + otherwise false.

Allowed in guard tests.

+ - Check whether a process is alive + Checks whether a process is alive. -

- Pid must refer to a process at the local node. - Returns true if the process exists and is alive, that - is, is not exiting and has not exited. Otherwise, returns +

Pid must refer to a process at the local node.

+

Returns true if the process exists and is alive, that + is, is not exiting and has not exited. Otherwise returns false.

+ - Check whether a term appears to be a record + Checks whether a term appears to be a record. -

Returns true if Term is a tuple and its first - element is RecordTag. Otherwise, returns false.

+

Returns true if Term is a tuple and its + first element is RecordTag. + Otherwise returns false.

Normally the compiler treats calls to is_record/2 - specially. It emits code to verify that Term is a - tuple, that its first element is RecordTag, and that - the size is correct. However, if the RecordTag is - not a literal atom, the is_record/2 BIF will be - called instead and the size of the tuple will not be - verified.

+ specially. It emits code to verify that Term + is a tuple, that its first element is + RecordTag, and that the + size is correct. However, if RecordTag is + not a literal atom, the BIF is_record/2 is called + instead and the size of the tuple is not verified.

-

Allowed in guard tests, if RecordTag is a literal - atom.

+

Allowed in guard tests, if RecordTag is + a literal atom.

+ - Check whether a term appears to be a record - -

RecordTag must be an atom. Returns true if - Term is a tuple, its first element is RecordTag, - and its size is Size. Otherwise, returns false.

-

Allowed in guard tests, provided that RecordTag is + Checks whether a term appears to be a record. + +

RecordTag must be an atom.

+

Returns true if + Term is a tuple, + its first element is RecordTag, + and its size is Size. + Otherwise returns false.

+

Allowed in guard tests if RecordTag is a literal atom and Size is a literal integer.

-

This BIF is documented for completeness. In most cases - is_record/2 should be used.

+

This BIF is documented for completeness. Usually + is_record/2 is to be used.

+ - Check whether a term is a reference + Checks whether a term is a reference. -

Returns true if Term is a reference; - otherwise returns false.

+

Returns true if Term is a reference, + otherwise false.

Allowed in guard tests.

+ - Check whether a term is a tuple + Checks whether a term is a tuple. -

Returns true if Term is a tuple; - otherwise returns false.

+

Returns true if Term is a tuple, + otherwise false.

Allowed in guard tests.

+ - Length of a list + Length of a list. -

Returns the length of List.

+

Returns the length of List, for example:

 > length([1,2,3,4,5,6,7,8,9]).
 9

Allowed in guard tests.

+ - Create a link to another process (or port) + Creates a link to another process (or port).

Creates a link between the calling process and another - process (or port) PidOrPort, if there is not such a link + process (or port) PidOrPort, if there is + not such a link already. If a process attempts to create a link to itself, nothing is done. Returns true.

-

If PidOrPort does not exist, the behavior of the BIF depends - on if the calling process is trapping exits or not (see +

If PidOrPort does not exist, the behavior + of the BIF + depends on if the calling process is trapping exits or not (see process_flag/2):

If the calling process is not trapping exits, and - checking PidOrPort is cheap -- that is, if PidOrPort is - local -- link/1 fails with reason noproc. + checking PidOrPort is cheap + (that is, if PidOrPort + is local), link/1 fails with reason noproc. Otherwise, if the calling process is trapping exits, - and/or PidOrPort is remote, link/1 returns - true, but an exit signal with reason noproc + and/or PidOrPort is remote, link/1 + returns true, but an exit signal with reason noproc is sent to the calling process.
+ - Convert from text representation to an atom - -

Returns the atom whose text representation is String.

-

String may only contain ISO-latin-1 - characters (i.e. numbers below 256) as the current - implementation does not allow unicode characters >= 256 in - atoms. For more information on Unicode support in atoms - see note on UTF-8 encoded atoms - in the chapter about the external term format in the ERTS User's Guide.

+ Converts from text representation to an atom. + +

Returns the atom whose text representation is + String.

+

String can only contain ISO-latin-1 + characters (that is, + numbers less than 256) as the implementation does not + allow unicode characters equal to or above 256 in atoms. + For more information on Unicode support in atoms, see + note on UTF-8 + encoded atoms + in Section "External Term Format" in the User's Guide.

+

Example:

 > list_to_atom("Erlang").
 'Erlang'
+ - Convert a list to a binary + Converts a list to a binary. -

Returns a binary which is made from the integers and - binaries in IoList.

+

Returns a binary that is made from the integers and + binaries in IoList, for example:

 > Bin1 = <<1,2,3>>.
 <<1,2,3>>
@@ -2130,14 +2277,16 @@ os_prompt% 
<<1,2,3,1,2,3,4,5,4,6>>
+ + Converts a list to a bitstring. - Convert a list to a bitstring -

Returns a bitstring which is made from the integers and - bitstrings in BitstringList. (The last tail in BitstringList - is allowed to be a bitstring.)

+

Returns a bitstring that is made from the integers and + bitstrings in BitstringList. (The last tail in + BitstringList is allowed to be a bitstring.)

+

Example:

 > Bin1 = <<1,2,3>>.
 <<1,2,3>>
@@ -2149,21 +2298,25 @@ os_prompt% 
<<1,2,3,1,2,3,4,5,4,6,7:46>>
+ - Convert from text representation to an atom + Converts from text representation to an atom. -

Returns the atom whose text representation is String, +

Returns the atom whose text representation is + String, but only if there already exists such atom.

Failure: badarg if there does not already exist an atom whose text representation is String.

+ - Convert from text representation to a float + Converts from text representation to a float. -

Returns the float whose text representation is String.

+

Returns the float whose text representation is + String, for example:

 > list_to_float("2.2017764e+0").
 2.2017764
@@ -2171,12 +2324,13 @@ os_prompt% representation of a float.

+ - Convert from text representation to an integer + Converts from text representation to an integer.

Returns an integer whose text representation is - String.

+ String, for example:

 > list_to_integer("123").
 123
@@ -2184,12 +2338,14 @@ os_prompt% representation of an integer.

+ - Convert from text representation to an integer + Converts from text representation to an integer.

Returns an integer whose text representation in base - Base is String.

+ Base is String, + for example:

 > list_to_integer("3FF", 16).
 1023
@@ -2197,47 +2353,52 @@ os_prompt% representation of an integer.

+ - Convert from text representation to a pid + Converts from text representation to a pid. -

Returns a pid whose text representation is String.

- -

This BIF is intended for debugging and for use in - the Erlang operating system. It should not be used in - application programs.

-
+

Returns a process identifier whose text representation is a + String, for example:

 > list_to_pid("<0.4.1>").
 <0.4.1>

Failure: badarg if String contains a bad - representation of a pid.

+ representation of a process identifier.

+ +

This BIF is intended for debugging and for use in the + Erlang OS. It is not to be used in application programs.

+
+ - Convert a list to a tuple + Converts a list to a tuple. -

Returns a tuple which corresponds to List. List - can contain any Erlang terms.

+

Returns a tuple corresponding to List, + for example

 > list_to_tuple([share, ['Ericsson_B', 163]]).
 {share, ['Ericsson_B', 163]}
+

List can contain any Erlang terms.

+ - Load object code for a module + Loads object code for a module. -

If Binary contains the object code for the module - Module, this BIF loads that object code. Also, if - the code for the module Module already exists, all +

If Binary contains the object code for module + Module, this BIF loads that object code. If + the code for module Module 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 - {module, Module}, or {error, Reason} if loading - fails. Reason is one of the following:

+ as old code, as there can still be processes executing + that code.

+

Returns either {module, Module}, or + {error, Reason} if loading fails. + Reason is any of the following:

badfile @@ -2247,118 +2408,122 @@ os_prompt% not_purged -

Binary contains a module which cannot be loaded - because old code for this module already exists.

+

Binary contains a module that cannot be + loaded because old code for this module already exists.

This BIF is intended for the code server (see - code(3)) and should not be - used elsewhere.

+ code(3)) + and is not to be used elsewhere.

+ - Load NIF library + Loads NIF library. -

In releases older than OTP R14B, NIFs were an - experimental feature. Versions of OTP older than R14B might +

Before OTP R14B, NIFs were an + experimental feature. Versions before OTP R14B can have different and possibly incompatible NIF semantics and - interfaces. For example, in R13B03 the return value on - failure was - {error,Reason,Text}.

+ interfaces. For example, in OTP R13B03 the return value on + failure was {error,Reason,Text}.

Loads and links a dynamic library containing native - implemented functions (NIFs) for a module. Path 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 erl_nif - on how to implement a NIF library.

-

LoadInfo can be any term. It will be passed on to + implemented functions (NIFs) for a module. Path + is a file path to the sharable object/dynamic library file minus + the OS-dependent file extension (.so for Unix and + .dll for Windows. For information on how to + implement a NIF library, see + erl_nif.

+

LoadInfo can be any term. It is passed on to the library as part of the initialization. A good practice is to include a module version number to support future code upgrade scenarios.

The call to load_nif/2 must be made directly from the Erlang code of the module that the - NIF library belongs to.

-

It returns either ok, or {error,{Reason,Text}} - if loading fails. Reason is one of the atoms below, - while Text is a human readable string that may give - some more information about the failure.

+ NIF library belongs to. It returns either ok, or + {error,{Reason,Text}} if loading fails. + Reason is one of the following atoms + while Text is a human readable string that + can give more information about the failure:

load_failed - -

The OS failed to load the NIF library.

+ The OS failed to load the NIF library. bad_lib - -

The library did not fulfil the requirements as a NIF - library of the calling module.

+ The library did not fulfill the requirements as a NIF + library of the calling module. load | reload | upgrade - -

The corresponding library callback was not successful.

+ The corresponding library callback was unsuccessful. old_code - -

The call to load_nif/2 was made from the old - code of a module that has been upgraded. This is not - allowed.

+ The call to load_nif/2 was made from the old + code of a module that has been upgraded; this is not + allowed.
+ - List of all loaded modules + Lists all loaded modules. -

Returns a list of all loaded Erlang modules (current and/or +

Returns a list of all loaded Erlang modules (current and old code), including preloaded modules.

See also code(3).

+ - Current local date and time + Current local date and time. -

Returns the current local date and time - {{Year, Month, Day}, {Hour, Minute, Second}}.

-

The time zone and daylight saving time correction depend - on the underlying OS.

+

Returns the current local date and time, + {{Year, Month, Day}, {Hour, Minute, Second}}, + for example:

 > erlang:localtime().
 {{1996,11,6},{14,45,17}}
+

The time zone and Daylight Saving Time correction depend + on the underlying OS.

+ - Convert from local to Universal Time Coordinated (UTC) date and time + Converts from local to Universal Time Coordinated (UTC) date and time.

Converts local date and time to Universal Time Coordinated - (UTC), if this is supported by the underlying OS. Otherwise, - no conversion is done and Localtime is returned.

+ (UTC), if supported by the underlying OS. Otherwise + no conversion is done and Localtime + is returned.

+

Example:

 > erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}).
 {{1996,11,6},{13,45,17}}
-

Failure: badarg if Localtime does not denote - a valid date and time.

+

Failure: badarg if Localtime denotes an + invalid date and time.

+ - Convert from local to Universal Time Coordinated (UTC) date and time + Converts from local to Universal Time Coordinated (UTC) date and time.

Converts local date and time to Universal Time Coordinated - (UTC) just like erlang:localtime_to_universaltime/1, - but the caller decides if daylight saving time is active or - not.

-

If IsDst == true the Localtime is during - daylight saving time, if IsDst == false it is not, - and if IsDst == undefined the underlying OS may + (UTC) as erlang:localtime_to_universaltime/1, + but the caller decides if Daylight Saving Time is active.

+

If IsDst == true, Localtime is + during Daylight Saving Time, if IsDst == false it is + not. If IsDst == undefined, the underlying OS can guess, which is the same as calling erlang:localtime_to_universaltime(Localtime).

+

Examples:

 > erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}, true).
 {{1996,11,6},{12,45,17}}
@@ -2366,13 +2531,14 @@ os_prompt% 
{{1996,11,6},{13,45,17}} > erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}, undefined). {{1996,11,6},{13,45,17}} -

Failure: badarg if Localtime does not denote - a valid date and time.

+

Failure: badarg if Localtime denotes an + invalid date and time.

+ - Return a unique reference + Returns a unique reference.

Return a unique reference. The reference is unique among @@ -2383,200 +2549,209 @@ os_prompt% created on an older node with the same node name.

+ - Create a new tuple of a given arity + Creates a new tuple of a given arity. -

Returns a new tuple of the given Arity, where all - elements are InitialValue.

+

Creates a new tuple of the given Arity, where all + elements are InitialValue, for example:

 > erlang:make_tuple(4, []).
 {[],[],[],[]}
+ - Create a new tuple with given arity and contents - -

erlang:make_tuple first creates a tuple of size Arity - where each element has the value DefaultValue. It then fills - in values from InitList. Each list element in InitList - 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 - last occurrence will be used.

+ Creates a new tuple with given arity and contents. + +

Creates a tuple of size Arity, where each element + has value DefaultValue, and then fills in + values from InitList. + Each list element in InitList + must be a two-tuple, where the first element is a position in the + newly created tuple and the second element is any term. If a + position occurs more than once in the list, the term corresponding + to the last occurrence is used.

+

Example:

 > erlang:make_tuple(5, [], [{2,ignored},{5,zz},{2,aa}]).
 {{[],aa,[],[],zz}
+ - Return the size of a map + Returns the size of a map. -

Returns an integer which is the number of key-value pairs in Map.

+

Returns an integer, which is the number of key-value pairs + in Map, for example:

 > map_size(#{a=>1, b=>2, c=>3}).
 3

Allowed in guard tests.

+ - Return the largest of two term + Returns the largest of two terms. -

Return the largest of Term1 and Term2; - if the terms compare equal, Term1 will be returned.

+

Returns the largest of Term1 and + Term2. + If the terms are equal, Term1 is returned.

+ - Compute an MD5 message digest + Computes an MD5 message digest. -

Computes an MD5 message digest from Data, where - the length of the digest is 128 bits (16 bytes). Data +

Computes an MD5 message digest from Data, where + the length of the digest is 128 bits (16 bytes). + Data is a binary or a list of small integers and binaries.

-

See The MD5 Message Digest Algorithm (RFC 1321) for more - information about MD5.

-

The MD5 Message Digest Algorithm is not considered - safe for code-signing or software integrity purposes.

+

For more information about MD5, see RFC 1321 - The + MD5 Message-Digest Algorithm.

+

The MD5 Message-Digest Algorithm is not considered + safe for code-signing or software-integrity purposes.

+ - Finish the update of an MD5 context and return the computed MD5 message digest + Finishes the update of an MD5 context and returns the computed MD5 message digest.

Finishes the update of an MD5 Context and returns the computed MD5 message digest.

+ - Create an MD5 context + Creates an MD5 context.

Creates an MD5 context, to be used in subsequent calls to md5_update/2.

+ - Update an MD5 context with data, and return a new context + Updates an MD5 context with data and returns a new context. -

Updates an MD5 Context with Data, and returns - a NewContext.

+

Updates an MD5 Context with + Data and returns a + NewContext.

+ + Information about dynamically allocated memory. - Information about dynamically allocated memory - -

Returns a list containing information about memory - dynamically allocated by the Erlang emulator. Each element of - the list is a tuple {Type, Size}. The first element - Typeis an atom describing memory type. The second - element Sizeis memory size in bytes. A description of - each memory type follows:

+ +

Returns a list with information about memory + dynamically allocated by the Erlang emulator. Each list + element is a tuple {Type, Size}. The first element + Type is an atom describing memory type. The second + element Size is the memory size in bytes.

+

The memory types are as follows:

total -

The total amount of memory currently allocated, which is - the same as the sum of memory size for processes +

The total amount of memory currently allocated. This is + the same as the sum of the memory size for processes and system.

processes -

The total amount of memory currently allocated by +

The total amount of memory currently allocated for the Erlang processes.

processes_used

The total amount of memory currently used by the Erlang - processes.

-

This memory is part of the memory presented as + processes. This is part of the memory presented as processes memory.

system -

The total amount of memory currently allocated by +

The total amount of memory currently allocated for the emulator that is not directly related to any Erlang - process.

-

Memory presented as processes is not included in - this memory.

+ process. Memory presented as processes is not + included in this memory.

atom -

The total amount of memory currently allocated for atoms.

-

This memory is part of the memory presented as +

The total amount of memory currently allocated for atoms. + This memory is part of the memory presented as system memory.

atom_used -

The total amount of memory currently used for atoms.

-

This memory is part of the memory presented as +

The total amount of memory currently used for atoms. + This memory is part of the memory presented as atom memory.

binary

The total amount of memory currently allocated for - binaries.

-

This memory is part of the memory presented as - system memory.

+ binaries. This memory is part of the memory presented + as system memory.

code

The total amount of memory currently allocated for - Erlang code.

-

This memory is part of the memory presented as - system memory.

+ Erlang code. This memory is part of the memory presented + as system memory.

ets

The total amount of memory currently allocated for ets - tables.

-

This memory is part of the memory presented as + tables. This memory is part of the memory presented as system memory.

low -

Only on 64-bit halfword emulator.

-

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.

-

May be removed in future releases of halfword emulator.

+

Only on 64-bit halfword emulator. + The total amount of memory allocated in low memory areas + that are restricted to less than 4 GB, although + the system can have more memory.

+

Can be removed in a future release of the halfword + emulator.

maximum

The maximum total amount of memory allocated since - the emulator was started.

-

This tuple is only present when the emulator is run with - instrumentation.

+ the emulator was started. This tuple is only present + when the emulator is run with instrumentation.

For information on how to run the emulator with - instrumentation see + instrumentation, see instrument(3) and/or erl(1).

The system value is not complete. Some allocated - memory that should be part of the system value are - not.

+ memory that is to be part of this value is not.

When the emulator is run with instrumentation, the system value is more accurate, but memory - directly allocated by malloc (and friends) are still + directly allocated for malloc (and friends) is still not part of the system value. Direct calls to - malloc are only done from OS specific runtime - libraries and perhaps from user implemented Erlang drivers + malloc are only done from OS-specific runtime + libraries and perhaps from user-implemented Erlang drivers that do not use the memory allocation functions in the driver interface.

-

Since the total value is the sum of processes - and system the error in system will propagate +

As the total value is the sum of processes + and system, the error in system propagates to the total value.

The different amounts of memory that are summed are - not gathered atomically which also introduce + not gathered atomically, which introduces an error in the result.

-

The different values has the following relation to each +

The different values have the following relation to each other. Values beginning with an uppercase letter is not part of the result.

@@ -2584,69 +2759,62 @@ os_prompt% processes = processes_used + ProcessesNotUsed system = atom + binary + code + ets + OtherSystem atom = atom_used + AtomNotUsed - RealTotal = processes + RealSystem RealSystem = system + MissedSystem -

More tuples in the returned list may be added in the future.

+

More tuples in the returned list can be added in a + future release.

The total value is supposed to be the total amount of memory dynamically allocated by the emulator. Shared libraries, the code of the emulator itself, and - the emulator stack(s) are not supposed to be included. That + the emulator stacks are not supposed to be included. That is, the total value is not supposed to be - equal to the total size of all pages mapped to the emulator. - Furthermore, due to fragmentation and pre-reservation of - memory areas, the size of the memory segments which contain - the dynamically allocated memory blocks can be substantially + equal to the total size of all pages mapped to the emulator.

+

Furthermore, because of fragmentation and prereservation of + memory areas, the size of the memory segments containing + the dynamically allocated memory blocks can be much larger than the total size of the dynamically allocated memory blocks.

-

- Since erts version 5.6.4 erlang:memory/0 requires that +

As from ERTS 5.6.4, erlang:memory/0 requires that all erts_alloc(3) - allocators are enabled (default behaviour). -

+ allocators are enabled (default behavior).

-

Failure:

- - notsup - - If an erts_alloc(3) - allocator has been disabled. - - +

Failure: notsup if an + erts_alloc(3) + allocator has been disabled.

+ + Information about dynamically allocated memory. - Information about dynamically allocated memory

Returns the memory size in bytes allocated for memory of type Type. The argument can also be given as a list of memory_type() atoms, in which case a corresponding list of {memory_type(), Size :: integer >= 0} tuples is returned.

-

- Since erts version 5.6.4 erlang:memory/1 requires that +

As from ERTS version 5.6.4, + erlang:memory/1 requires that all erts_alloc(3) - allocators are enabled (default behaviour). -

+ allocators are enabled (default behavior).

Failures:

badarg - If Type is not one of the memory types listed in the - documentation of + If Type is not one of the memory types + listed in the decription of erlang:memory/0. badarg - If maximum is passed as Type and the emulator - is not run in instrumented mode. + If maximum is passed as Type and + the emulator is not run in instrumented mode. notsup @@ -2658,35 +2826,39 @@ os_prompt% erlang:memory/0.

+ - Return the smallest of two term + Returns the smallest of two terms. -

Return the smallest of Term1 and Term2; - if the terms compare equal, Term1 will be returned.

+

Returns the smallest of Term1 and + Term2. + If the terms are equal, Term1 is returned.

+ - Check if a module is loaded + Checks if a module is loaded. -

Returns true if the module Module is loaded, - otherwise returns false. It does not attempt to load +

Returns true if the module Module + is loaded, otherwise false. It does not attempt to load the module.

This BIF is intended for the code server (see - code(3)) and should not be + code(3)) and is not to be used elsewhere.

+ - Start monitoring + Starts monitoring.

Send a monitor request of type Type to the entity identified by Item. The caller of @@ -2695,14 +2867,14 @@ os_prompt% {Tag, MonitorRef, Type, Object, Info}

The monitor request is an asynchronous signal. That is, it takes time before the signal reach its destination.

-

Currently valid Types:

+

Valid Types:

process

Monitor the existence of the process identified by - Item. Currently valid + Item. Valid Items in combination with the - process Type:

+ process Type can be any of the following:

pid() @@ -2721,10 +2893,10 @@ os_prompt% will become monitored.

-

When a process is monitored by registered name, the - process that has the registered name at the time when the +

When a registered name is used, the + process that has the registered name when the monitor request reach its destination will be monitored. - The monitor will not be effected, if the registered name is + The monitor is not effected if the registered name is unregistered, or unregistered and later registered on another process.

The monitor is triggered either when the monitored process @@ -2732,22 +2904,22 @@ os_prompt% lost. In the case the connection to it is lost, we do not know if it still exist or not. After this type of monitor has been triggered, the monitor is automatically removed.

-

When the monitor is triggered a 'DOWN' message will - be sent to the monitoring process. A 'DOWN' message has +

When the monitor is triggered a 'DOWN' message is + sent to the monitoring process. A 'DOWN' message has the following pattern:

{'DOWN', MonitorRef, Type, Object, Info} -

where MonitorRef and Type are the same as - described above, and:

+

Here MonitorRef and Type are the same as + described earlier, and:

Object

equals:

Item - If Item was specified by a - pid. + If Item is specified by a + process identifier. {RegisteredName, Node} - If Item was specified as + If Item is specified as RegisteredName, or {RegisteredName, Node} where Node corresponds to the node that the monitored process resides on. @@ -2760,26 +2932,26 @@ os_prompt% connection to the node where the monitored process resides).

-

The monitoring is turned off either when the 'DOWN' - message is sent, or when +

The monitoring is turned off when the 'DOWN' + message is sent or when demonitor/1 is called.

If an attempt is made to monitor a process on an older node - (where remote process monitoring is not implemented or one + (where remote process monitoring is not implemented or where remote process monitoring by registered name is not implemented), the call fails with badarg.

-

The format of the 'DOWN' message changed in the 5.2 - version of the emulator (OTP release R9B) for monitor - by registered name. The Object element of +

The format of the 'DOWN' message changed in ERTS + version 5.2 (OTP R9B) for monitoring + by registered name. Element Object of the 'DOWN' message could in earlier versions - sometimes be the pid of the monitored process and sometimes - be the registered name. Now the Object element is + sometimes be the process identifier of the monitored process and sometimes + be the registered name. Now element Object is always a tuple consisting of the registered name and - the node name. Processes on new nodes (emulator version 5.2 - or greater) will always get 'DOWN' messages on + the node name. Processes on new nodes (ERTS version 5.2 + or higher) always get 'DOWN' messages on the new format even if they are monitoring processes on old - nodes. Processes on old nodes will always get 'DOWN' + nodes. Processes on old nodes always get 'DOWN' messages on the old format.

@@ -2836,7 +3008,7 @@ os_prompt%

When the 'CHANGE' message has been received you are guaranteed not to retrieve the old time offset when calling erlang:time_offset(). - Note that you may observe the change of the time offset + Note that you can observe the change of the time offset when calling erlang:time_offset() before you get the 'CHANGE' message.

@@ -2844,64 +3016,68 @@ os_prompt%

Making several calls to monitor/2 for the same Item and/or Type is not - an error; it results in many, completely independent, - monitorings.

+ an error; it results in as many independent monitorings.

The monitor functionality is expected to be extended. That is, other Types and Items - are expected to be supported in the future.

+ are expected to be supported in a future release.

-

If/when monitor/2 is extended, other - possible values for Tag, Object, and +

If or when monitor/2 is extended, other + possible values for Tag, Object and Info in the monitor message will be introduced.

+ - Monitor the status of a node + Monitors the status of a node. -

Monitors the status of the node Node. If Flag - is true, monitoring is turned on; if Flag is - false, monitoring is turned off.

+

Monitors the status of the node Node. + If Flag + is true, monitoring is turned on. If Flag + is false, monitoring is turned off.

Making several calls to monitor_node(Node, true) for - the same Node is not an error; it results in as many, - completely independent, monitorings.

+ the same Node is not an error; it results + in as many independent monitorings.

If Node fails or does not exist, the message {nodedown, Node} is delivered to the process. If a process has made two calls to monitor_node(Node, true) - and Node terminates, two nodedown messages are - delivered to the process. If there is no connection to - Node, there will be an attempt to create one. If this - fails, a nodedown message is delivered.

+ and Node terminates, two nodedown messages + are delivered to the process. If there is no connection to + Node, an attempt is made to create one. + If this fails, a nodedown message is delivered.

Nodes connected through hidden connections can be monitored - as any other node.

+ as any other nodes.

Failure: badarg if the local node is not alive.

+ - Monitor the status of a node + Monitors the status of a node. -

Behaves as monitor_node/2 except that it allows an +

Behaves as + monitor_node/2 + except that it allows an extra option to be given, namely allow_passive_connect. - The option allows the BIF to wait the normal net connection - timeout for the monitored node to connect itself, + This option allows the BIF to wait the normal network connection + time-out for the monitored node to connect itself, even if it cannot be actively connected from this node - (i.e. it is blocked). The state where this might be useful can - only be achieved by using the kernel option - dist_auto_connect once. If that kernel option is not - used, the allow_passive_connect option has no - effect.

+ (that is, it is blocked). The state where this can be useful + can only be achieved by using the Kernel option + dist_auto_connect once. If that option is not + used, option allow_passive_connect has no effect.

-

The allow_passive_connect option is used +

Option allow_passive_connect is used internally and is seldom needed in applications where the - network topology and the kernel options in effect is known in - advance.

+ network topology and the Kernel options in effect + are known in advance.

Failure: badarg if the local node is not alive or the option list is malformed.

+ Current Erlang monotonic time @@ -2949,61 +3125,68 @@ os_prompt% - Stop execution with a given reason + Stops execution with a given reason.

Works exactly like - erlang:error/1, - but Dialyzer thinks that this BIF will return an arbitrary term. - When used in a stub function for a NIF to generate an - exception when the NIF library is not loaded, Dialyzer - will not generate false warnings.

+ erlang:error/1, but + Dialyzer thinks that this BIF will return an arbitrary + term. When used in a stub function for a NIF to generate an + exception when the NIF library is not loaded, Dialyzer + does not generate false warnings.

+ - Stop execution with a given reason + Stops execution with a given reason.

Works exactly like - erlang:error/2, - but Dialyzer thinks that this BIF will return an arbitrary term. - When used in a stub function for a NIF to generate an - exception when the NIF library is not loaded, Dialyzer - will not generate false warnings.

+ erlang:error/2, but + Dialyzer thinks that this BIF will return an arbitrary + term. When used in a stub function for a NIF to generate an + exception when the NIF library is not loaded, Dialyzer + does not generate false warnings.

+ - Name of the local node + Name of the local node.

Returns the name of the local node. If the node is not alive, nonode@nohost is returned instead.

Allowed in guard tests.

+ - At which node is a pid, port or reference located + At which node a pid, port, or reference is located. -

Returns the node where Arg is located. Arg can - be a pid, a reference, or a port. If the local node is not +

Returns the node where Arg is located. + Arg can + be a process identifier, a reference, or a port. + If the local node is not alive, nonode@nohost is returned.

Allowed in guard tests.

+ - All visible nodes in the system + All visible nodes in the system. -

Returns a list of all visible nodes in the system, excluding +

Returns a list of all visible nodes in the system, except the local node. Same as nodes(visible).

+ - All nodes of a certain type in the system + All nodes of a certain type in the system. -

Returns a list of nodes according to argument given. - The result returned when the argument is a list, is the list +

Returns a list of nodes according to the argument given. + The returned result when the argument is a list, is the list of nodes satisfying the disjunction(s) of the list elements.

NodeType can be any of the following:

@@ -3025,22 +3208,24 @@ os_prompt% known -

Nodes which are known to this node, i.e., connected, - previously connected, etc.

+

Nodes that are known to this node, for example, connected + and previously connected.

Some equalities: [node()] = nodes(this), nodes(connected) = nodes([visible, hidden]), and nodes() = nodes(visible).

If the local node is not alive, - nodes(this) == nodes(known) == [nonode@nohost], for - any other Arg the empty list [] is returned.

+ nodes(this) == nodes(known) == [nonode@nohost]. For + any other Arg the empty list + [] is returned.

+ + Elapsed time since 00:00 GMT. - Elapsed time since 00:00 GMT

This function is deprecated! Do not use it! See the users guide chapter @@ -3050,107 +3235,101 @@ os_prompt% section for information on what to use instead of erlang:now/0.

Returns the tuple {MegaSecs, Secs, MicroSecs} which is - the elapsed time since 00:00 GMT, January 1, 1970 (zero hour) + the elapsed time since 00:00 GMT, January 1, 1970 (zero hour), on the assumption that the underlying OS supports this. - Otherwise, some other point in time is chosen. It is also - guaranteed that subsequent calls to this BIF returns + Otherwise some other point in time is chosen. It is also + guaranteed that subsequent calls to this BIF return continuously increasing values. Hence, the return value from - now() can be used to generate unique time-stamps, - and if it is called in a tight loop on a fast machine + now() can be used to generate unique time-stamps. + If it is called in a tight loop on a fast machine, the time of the node can become skewed.

-

It can only be used to check the local time of day if - the time-zone info of the underlying operating system is +

Can only be used to check the local time of day if + the time-zone information of the underlying OS is properly configured.

+ - Open a port + Opens a port.

Returns a port identifier as the result of opening a new Erlang port. A port can be seen as an external Erlang - process. -

+ process.

The name of the executable as well as the arguments - given in cd, env, args and arg0 is subject to - Unicode file name translation if the system is running - in Unicode file name mode. To avoid - translation or force i.e. UTF-8, supply the executable + given in cd, env, args, and arg0 are + subject to Unicode filename translation if the system is running + in Unicode filename mode. To avoid + translation or force, that is, UTF-8, supply the executable and/or arguments as a binary in the correct - encoding. See the file module, the - - file:native_name_encoding/0 function and the - stdlib users guide - for details.

- -

The characters in the name (if given as a list) - can only be > 255 if the Erlang VM is started in - Unicode file name translation mode, otherwise the name + encoding. For details, see the module + file, the function + file:native_name_encoding/0, and the + STDLIB + User's Guide.

+

The characters in the name (if given as a list) can + only be higher than 255 if the Erlang Virtual Machine is started + in Unicode filename translation mode. Otherwise the name of the executable is limited to the ISO-latin-1 character set.

- -

PortName is one of the following:

+

PortName can be any of the following:

{spawn, Command} -

Starts an external program. Command is the name - of the external program which will be run. Command +

Starts an external program. Command + is the name of the external program to be run. + Command runs outside the Erlang work space unless an Erlang - driver with the name Command is found. If found, - that driver will be started. A driver runs in the Erlang + driver with the name Command is found. + If found, that driver is started. A driver runs in the Erlang workspace, which means that it is linked with the Erlang runtime system.

When starting external programs on Solaris, the system call vfork is used in preference to fork for performance reasons, although it has a history of - being less robust. If there are problems with using - vfork, setting the environment variable - ERL_NO_VFORK to any value will cause fork + being less robust. If there are problems using + vfork, setting environment variable + ERL_NO_VFORK to any value causes fork to be used instead.

- -

For external programs, the PATH is searched +

For external programs, PATH is searched (or an equivalent method is used to find programs, - depending on operating system). This is done by invoking - the shell on certain platforms. The first space - separated token of the command will be considered as the + depending on OS). This is done by invoking + the shell on certain platforms. The first space-separated + token of the command is considered as the name of the executable (or driver). This (among other things) makes this option unsuitable for running - programs having spaces in file or directory names. Use - {spawn_executable, Command} instead if spaces in executable - file names is desired.

+ programs having spaces in filenames or directory names. + If spaces in executable filenames are desired, use + {spawn_executable, Command} instead.

{spawn_driver, Command}

Works like {spawn, Command}, but demands the - first (space separated) token of the command to be the name of a + first (space-separated) token of the command to be the name of a loaded driver. If no driver with that name is loaded, a badarg error is raised.

{spawn_executable, FileName} -

Works like {spawn, FileName}, but only runs - external executables. The FileName in its whole - is used as the name of the executable, including any - spaces. If arguments are to be passed, the - args and arg0 PortSettings can be used.

- -

The shell is not usually invoked to start the - program, it's executed directly. Neither is the - PATH (or equivalent) searched. To find a program - in the PATH to execute, use os:find_executable/1.

+ external executables. FileName in its whole + is used as the name of the executable, including any spaces. + If arguments are to be passed, the PortSettings + args and arg0 can be used.

+

The shell is usually not invoked to start the + program, it is executed directly. PATH (or + equivalent) is not searched. To find a program + in PATH to execute, use + os:find_executable/1.

Only if a shell script or .bat file is - executed, the appropriate command interpreter will - implicitly be invoked, but there will still be no - command argument expansion or implicit PATH search.

- -

If the FileName 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 enoent is raised - when one tries to run a program that is not found and + executed, the appropriate command interpreter is + invoked implicitly, but there is still no + command argument expansion or implicit PATH search.

+

If FileName cannot be run, an error + exception is raised, with the POSIX error code as the reason. + The error reason can differ between OSs. + Typically the error enoent is raised when an + attempt is made to run a program that is not found and eacces is raised when the given file is not executable.

@@ -3160,19 +3339,18 @@ os_prompt% file descriptors used by Erlang. The file descriptor In can be used for standard input, and the file descriptor Out for standard output. It is only - used for various servers in the Erlang operating system - (shell and user). Hence, its use is very - limited.

+ used for various servers in the Erlang OS (shell + and user). Hence, its use is limited.

PortSettings is a list of settings for the port. - Valid settings are:

+ The valid settings are as follows:

{packet, N}

Messages are preceded by their length, sent in N - bytes, with the most significant byte first. Valid values - for N are 1, 2, or 4.

+ bytes, with the most significant byte first. The valid values + for N are 1, 2, and 4.

stream @@ -3183,116 +3361,108 @@ os_prompt% {line, L}

Messages are delivered on a per line basis. Each line - (delimited by the OS-dependent newline sequence) is - delivered in one single message. The message data format - is {Flag, Line}, where Flag is either - eol or noeol and Line is the actual - data delivered (without the newline sequence).

+ (delimited by the OS-dependent new line sequence) is + delivered in a single message. The message data format + is {Flag, Line}, where Flag is + eol or noeol, and Line is the + data delivered (without the new line sequence).

L specifies the maximum line length in bytes. - Lines longer than this will be delivered in more than one - message, with the Flag set to noeol for all + Lines longer than this are delivered in more than one + message, with Flag set to noeol for all but the last message. If end of file is encountered - anywhere else than immediately following a newline - sequence, the last line will also be delivered with - the Flag set to noeol. In all other cases, + anywhere else than immediately following a new line + sequence, the last line is also delivered with + Flag set to noeol. Otherwise lines are delivered with Flag set to eol.

-

The {packet, N} and {line, L} settings are - mutually exclusive.

+

The {packet, N} and {line, + L} settings are mutually exclusive.

{cd, Dir} -

This is only valid for {spawn, Command} and - {spawn_executable, FileName}. +

Only valid for {spawn, Command} and + {spawn_executable, FileName}. The external program starts using Dir as its - working directory. Dir must be a string. -

+ working directory. Dir must be a string.

{env, Env} -

This is only valid for {spawn, Command} and +

Only valid for {spawn, Command} and {spawn_executable, FileName}. The environment of the started process is extended using the environment specifications in Env.

-

Env should be a list of tuples {Name, Val}, - where Name is the name of an environment variable, - and Val is the value it is to have in the spawned - port process. Both Name and Val must be - strings. The one exception is Val being the atom +

Env is to be a list of tuples + {Name, Val}, + where Name is the name of an + environment variable, and Val is the + value it is to have in the spawned + port process. Both Name and + Val must be strings. The one + exception is Val being the atom false (in analogy with os:getenv/1), which - removes the environment variable. -

+ removes the environment variable.

{args, [ string() | binary() ]} - -

This option is only valid for {spawn_executable, FileName} +

Only valid for {spawn_executable, FileName} and specifies arguments to the executable. Each argument is given as a separate string and (on Unix) eventually ends up as one element each in the argument vector. On - other platforms, similar behavior is mimicked.

- -

The arguments are not expanded by the shell prior to - being supplied to the executable, most notably this - means that file wildcard expansion will not happen. Use - filelib:wildcard/1 - to expand wildcards for the arguments. Note that even if + other platforms, a similar behavior is mimicked.

+

The arguments are not expanded by the shell before + being supplied to the executable. Most notably this + means that file wildcard expansion does not happen. + To expand wildcards for the arguments, use + filelib:wildcard/1. + Notice that even if the program is a Unix shell script, meaning that the - shell will ultimately be invoked, wildcard expansion - will not happen and the script will be provided with the - untouched arguments. On Windows®, wildcard expansion - is always up to the program itself, why this isn't an - issue.

- -

Note also that the actual executable name (a.k.a. argv[0]) - should not be given in this list. The proper executable name will - automatically be used as argv[0] where applicable.

- -

If one, for any reason, wants to explicitly set the - program name in the argument vector, the arg0 - option can be used.

- + shell ultimately is invoked, wildcard expansion + does not happen, and the script is provided with the + untouched arguments. On Windows, wildcard expansion + is always up to the program itself, therefore this is + not an issue issue.

+

The executable name (also known as argv[0]) + is not to be given in this list. The proper executable name + is automatically used as argv[0], where applicable.

+

If you explicitly want to set the + program name in the argument vector, option arg0 + can be used.

{arg0, string() | binary()} - -

This option is only valid for {spawn_executable, FileName} +

Only valid for {spawn_executable, FileName} 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 - responds to this is highly system dependent and no specific + running an executable. This can in some circumstances, + on some OSs, be desirable. How the program + responds to this is highly system-dependent and no specific effect is guaranteed.

-
- exit_status -

This is only valid for {spawn, Command} where - Command refers to an external program, and for - {spawn_executable, FileName}.

+

Only valid for {spawn, Command}, where + Command refers to an external program, and + for {spawn_executable, FileName}.

When the external process connected to the port exits, a message of the form {Port,{exit_status,Status}} is sent to the connected process, where Status is the exit status of the external process. If the program - aborts, on Unix the same convention is used as the shells - do (i.e., 128+signal).

-

If the eof option has been given as well, - the eof message and the exit_status message - appear in an unspecified order.

-

If the port program closes its stdout without exiting, - the exit_status option will not work.

+ aborts on Unix, the same convention is used as the shells + do (that is, 128+signal).

+

If option eof is also given, the messages eof + and exit_status appear in an unspecified order.

+

If the port program closes its stdout without exiting, + option exit_status does not work.

use_stdio -

This is only valid for {spawn, Command} and +

Only valid for {spawn, Command} and {spawn_executable, FileName}. It allows the standard input and output (file descriptors 0 - and 1) of the spawned (UNIX) process for communication + and 1) of the spawned (Unix) process for communication with Erlang.

nouse_stdio -

The opposite of use_stdio. Uses file descriptors +

The opposite of use_stdio. It uses file descriptors 3 and 4 for communication with Erlang.

stderr_to_stdout @@ -3304,14 +3474,15 @@ os_prompt%
overlapped_io -

Affects ports to external programs on Windows® only. - The standard input and standard output handles of the port program - will, if this option is supplied, be opened with the flag - FILE_FLAG_OVERLAPPED, so that the port program can (and has to) do +

Affects ports to external programs on Windows only. The + standard input and standard output handles of the port program + are, if this option is supplied, opened with flag + FILE_FLAG_OVERLAPPED, so that the port program can + (and must) do overlapped I/O on its standard handles. This is not normally the case for simple port programs, but an option of value for the - experienced Windows programmer. On all other platforms, this - option is silently discarded.

+ experienced Windows programmer. On all other platforms, this + option is silently discarded.

in @@ -3323,213 +3494,213 @@ os_prompt% binary -

All IO from the port are binary data objects as opposed +

All I/O from the port is binary data objects as opposed to lists of bytes.

eof -

The port will not be closed at the end of the file and - produce an exit signal. Instead, it will remain open and - a {Port, eof} message will be sent to the process +

The port is not closed at the end of the file and does not + produce an exit signal. Instead, it remains open and + a {Port, eof} message is sent to the process holding the port.

hide -

When running on Windows, suppress creation of a new +

When running on Windows, suppresses creation of a new console window when spawning the port program. (This option has no effect on other platforms.)

- {parallelism, Boolean} + {parallelism, Boolean} -

Set scheduler hint for port parallelism. If set to true, - the VM will schedule port tasks when doing so will improve - parallelism in the system. If set to false, the VM will - try to perform port tasks immediately, improving latency at the - expense of parallelism. The default can be set on system startup - by passing the - +spp command line argument - to erl(1). -

+ +

Sets scheduler hint for port parallelism. If set to + true, the Virtual Machine schedules port tasks; + when doing so, it improves parallelism in the system. If set + to false, the Virtual Machine tries to + perform port tasks immediately, improving latency at the + expense of parallelism. The default can be set at system startup + by passing command-line argument + +spp to erl(1).

-

The default is stream for all types of port and +

Default is stream for all port types and use_stdio for spawned ports.

Failure: If the port cannot be opened, the exit reason is - badarg, system_limit, or the Posix error code which - most closely describes the error, or einval if no Posix code - is appropriate:

+ badarg, system_limit, or the POSIX error code that + most closely describes the error, or einval if no POSIX + code is appropriate:

badarg - -

Bad input arguments to open_port.

+ Bad input arguments to open_port. system_limit - -

All available ports in the Erlang emulator are in use.

+ All available ports in the Erlang emulator are in use. enomem - -

There was not enough memory to create the port.

+ Not enough memory to create the port. eagain - -

There are no more available operating system processes.

+ No more available OS processes. enametoolong - -

The external command given was too long.

+ Too long external command. emfile - -

There are no more available file descriptors (for the operating system process - that the Erlang emulator runs in).

+ No more available file descriptors (for the + OS process that the Erlang emulator runs in). enfile - -

The file table is full (for the entire operating system).

+ Full file table (for the entire OS). eacces - -

The Command given in {spawn_executable, Command} does not point out an executable file.

+ Command given in {spawn_executable, Command} + does not point out an executable file. enoent - -

The FileName given in {spawn_executable, FileName} does not point out an existing file.

+ FileName given in + {spawn_executable, FileName} + does not point out an existing file.

During use of a port opened using {spawn, Name}, - {spawn_driver, Name} or {spawn_executable, Name}, + {spawn_driver, Name}, or {spawn_executable, Name}, errors arising when sending messages to it are reported to the owning process using signals of the form - {'EXIT', Port, PosixCode}. See file(3) for - possible values of PosixCode.

+ {'EXIT', Port, PosixCode}. For the possible values of + PosixCode, see the + file(3) + manual page in Kernel.

The maximum number of ports that can be open at the same - time can be configured by passing the - +Q - command line flag to - erl(1).

+ time can be configured by passing command-line flag + +Q to + erl(1).

+ Range = 1..2^32, Hash = 1..Range - Portable hash function + Portable hash function. -

Portable hash function that will give the same hash for +

Portable hash function that gives 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 Term within the range 1..Range.

-

This BIF could be used instead of the old deprecated - erlang:hash/2 BIF, as it calculates better hashes for - all data-types, but consider using phash2/1,2 instead.

+ ERTS version (the BIF was introduced in ERTS 4.9.1.1). + Range is 1..2^32. The function returns a hash value for + Term within the range + 1..Range.

+

This BIF can be used instead of the old deprecated BIF + erlang:hash/2, as it calculates better hashes for + all data types, but consider using phash2/1,2 instead.

+ 1..2^32 0..Range-1 - Portable hash function + Portable hash function. -

Portable hash function that will give the same hash for +

Portable hash function that gives 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 - Term within the range 0..Range-1. When called - without the Range argument, a value in the range - 0..2^27-1 is returned.

-

This BIF should always be used for hashing terms. It + ERTS version (the BIF was introduced in ERTS 5.2). + Range is 1..2^32. The function returns a hash value for + Term within the range + 0..Range-1. When without argument + Range, a value in the range + 0..2^27-1 is returned.

+

This BIF is always to be used for hashing terms. It distributes small integers better than phash/2, and it is faster for bignums and binaries.

-

Note that the range 0..Range-1 is different from - the range of phash/2 (1..Range).

+

Notice that the range 0..Range-1 is + different from the range of phash/2, which is + 1..Range.

+ - Text representation of a pid + Text representation of a pid. -

Returns a string which corresponds to the text +

Returns a string corresponding to the text representation of Pid.

-

This BIF is intended for debugging and for use in - the Erlang operating system. It should not be used in - application programs.

+

This BIF is intended for debugging and for use in the + Erlang OS. It is not to be used in application programs.

+ - Close an open port + Closes an open port.

Closes an open port. Roughly the same as - Port ! {self(), close} except for the error behaviour - (see below), being synchronous, and that the port does - not reply with {Port, closed}. Any process may + Port ! {self(), close} except for the error behavior + (see the following), being synchronous, and that the port does + not reply with {Port, closed}. Any process can close a port with port_close/1, not only the port owner (the connected process). If the calling process is linked to - port identified by Port, an exit signal due - to that link will be received by the process prior to the return - from port_close/1.

+ a port identified by Port, an exit signal is sent + because that link will be received before the return from + port_close/1

For comparison: Port ! {self(), close} fails with - badarg if Port cannot be sent to (i.e., - Port refers neither to a port nor to a process). If - Port is a closed port nothing happens. If Port + badarg if Port cannot be sent to (that is, + Port refers not to a port and not to a process). If + Port is a closed port, nothing happens. + If Port is an open port and the calling process is the port owner, the port replies with {Port, closed} when all buffers - have been flushed and the port really closes, but if - the calling process is not the port owner the port owner fails with badsig.

- -

Note that any process can close a port using - Port ! {PortOwner, close} just as if it itself was + have been flushed and the port really closes. If + the calling process is not the port owner, the + port owner fails with badsig.

+

Notice that any process can close a port using + Port ! {PortOwner, close} as if it itself was the port owner, but the reply always goes to the port owner.

-

As of OTP-R16 Port ! {PortOwner, close} is truly - asynchronous. Note that this operation has always been +

As from OTP R16, Port ! {PortOwner, close} is truly + asynchronous. Notice that this operation has always been documented as an asynchronous operation, while the underlying implementation has been synchronous. port_close/1 is - however still fully synchronous. This due to its error + however still fully synchronous. This because of its error behavior.

-

Failure:

- - badarg - - If Port is not an identifier of an open - port, or the registered name of an open port. If the calling - process was linked to the previously open port identified by - Port, an exit signal due to this link - was received by the process prior to this exception. - - +

Failure: badarg if Port is not an identifier + of an open port, or the registered name of an open port. + If the calling process was linked to the port, the exit + identified by Port, the exit signal is sent because + this link was delivered to the calling process before this + exception occurs.

+ - Send data to a port + Sends data to a port.

Sends data to a port. Same as - Port ! {PortOwner, {command, Data}} except for the error - behaviour and being synchronous (see below). Any process may - send data to a port with port_command/2, not only the + Port ! {PortOwner, {command, Data}} except + for the error + behavior and being synchronous (see the following). Any process + can send data to a port with port_command/2, not only the port owner (the connected process).

For comparison: Port ! {PortOwner, {command, Data}} - fails with badarg if Port cannot be sent to - (i.e., Port refers neither to a port nor to a process). - If Port is a closed port the data message disappears + fails with badarg if Port + cannot be sent to (that + is, Port refers not to a port and not to a process). + If Port is a closed port, the data message disappears without a sound. If Port is open and the calling process is not the port owner, the port owner fails with badsig. The port owner fails with badsig - also if Data is not a valid IO list.

-

Note that any process can send to a port using - Port ! {PortOwner, {command, Data}} just as if it - itself was the port owner.

-

If the port is busy, the calling process will be suspended - until the port is not busy anymore.

-

As of OTP-R16 Port ! {PortOwner, {command, Data}} is - truly asynchronous. Note that this operation has always been + also if Data is an invalid I/O list.

+

Notice that any process can send to a port using + Port ! {PortOwner, {command, Data}} + as if it itself was the port owner.

+

If the port is busy, the calling process is suspended + until the port is not busy any more.

+

As from OTP-R16, Port ! {PortOwner, {command, Data}} + is truly asynchronous. Notice that this operation has always been documented as an asynchronous operation, while the underlying implementation has been synchronous. port_command/2 is - however still fully synchronous. This due to its error + however still fully synchronous. This because of its error behavior.

Failures:

@@ -3537,46 +3708,46 @@ os_prompt% If Port is not an identifier of an open port, or the registered name of an open port. If the calling - process was linked to the previously open port identified by - Port, an exit signal due to this link - was received by the process prior to this exception. + process was linked to the port, the exit signal is sent + because this link was delivered to the calling process + before this exception occurs. badarg - If Data is not a valid io list. + If Data is an invalid I/O list.
+ - Send data to a port + Sends data to a port.

Sends data to a port. port_command(Port, Data, []) equals port_command(Port, Data).

-

If the port command is aborted false is returned; - otherwise, true is returned.

-

If the port is busy, the calling process will be suspended - until the port is not busy anymore.

-

Currently the following Options are valid:

+

If the port command is aborted, false is returned, + otherwise true.

+

If the port is busy, the calling process is suspended + until the port is not busy any more.

+

The following Options are valid:

force - The calling process will not be suspended if the port is - busy; instead, the port command is forced through. The - call will fail with a notsup exception if the + The calling process is not suspended if the port is + busy, instead the port command is forced through. The + call fails with a notsup exception if the driver of the port does not support this. For more - information see the - - driver flag. + information, see driver flag + . nosuspend - The calling process will not be suspended if the port is - busy; instead, the port command is aborted and + The calling process is not suspended if the port is + busy, instead the port command is aborted and false is returned. -

More options may be added in the future.

+

More options can be added in a future release.

Failures:

@@ -3584,84 +3755,89 @@ os_prompt% If Port is not an identifier of an open port, or the registered name of an open port. If the calling - process was linked to the previously open port identified by - Port, an exit signal due to this link - was received by the process prior to this exception. + process was linked to the port, the exit signal is sent + because this link was delivered to the calling process + before this exception occurs. badarg - If Data is not a valid io list. + If Data is an invalid I/O list. badarg - If OptionList is not a valid option list. + If OptionList is an invalid option list. notsup - If the force option has been passed, but the + If option force has been passed, but the driver of the port does not allow forcing through a busy port.
+ - Set the owner of a port + Sets the owner of a port.

Sets the port owner (the connected port) to Pid. - Roughly the same as Port ! {Owner, {connect, Pid}} + Roughly the same as + Port ! {Owner, {connect, Pid}} except for the following:

-

The error behavior differs, see below.

+

The error behavior differs, see the following.

The port does not reply with {Port,connected}.

-

port_connect/1 is synchronous, see below.

+

port_connect/1 is synchronous, see the following.

The new port owner gets linked to the port.

-

The old port owner stays linked to the port and have to call - unlink(Port) if this is not desired. Any process may +

The old port owner stays linked to the port and must call + unlink(Port) if this is not desired. Any process can set the port owner to be any process with port_connect/2.

-

For comparison: Port ! {self(), {connect, Pid}} fails - with badarg if Port cannot be sent to (i.e., - Port refers neither to a port nor to a process). If - Port is a closed port nothing happens. If Port +

For comparison: + Port ! {self(), {connect, Pid}} fails + with badarg if Port cannot be sent to (that is, + Port refers not to a port and not to a process). If + Port is a closed port, nothing happens. + If Port is an open port and the calling process is the port owner, the port replies with {Port, connected} to the old - port owner. Note that the old port owner is still linked to - the port, and that the new is not. If Port is an open + port owner. Notice that the old port owner is still linked to + the port, while the new is not. If Port is an open port and the calling process is not the port owner, the port owner fails with badsig. The port owner fails with badsig also if Pid is not an - existing local pid.

-

Note that any process can set the port owner using - Port ! {PortOwner, {connect, Pid}} just as if it - itself was the port owner, but the reply always goes to + existing local process identifier.

+

Notice that any process can set the port owner using + Port ! {PortOwner, {connect, Pid}} + as if it itself was the port owner, but the reply always goes to the port owner.

-

As of OTP-R16 Port ! {PortOwner, {connect, Pid}} is - truly asynchronous. Note that this operation has always been +

As from OTP-R16, + Port ! {PortOwner, {connect, Pid}} is + truly asynchronous. Notice that this operation has always been documented as an asynchronous operation, while the underlying implementation has been synchronous. port_connect/2 is - however still fully synchronous. This due to its error + however still fully synchronous. This because of its error behavior.

Failures:

badarg - If Port is not an identifier of an open - port, or the registered name of an open port. If the calling - process was linked to the previously open port identified by - Port, an exit signal due to this link - was received by the process prior to this exception. + If Port is not an identifier of an open port, or + the registered name of an open port. If the calling + process was linked to the port, the exit signal is sent + because this link was delivered to the calling process + before this exception occurs. badarg If process identified by Pid is not an existing @@ -3669,53 +3845,74 @@ os_prompt%
+ - Perform a synchronous control operation on a port + Performs a synchronous control operation on a port.

Performs a synchronous control operation on a port. - The meaning of Operation and Data depends on - the port, i.e., on the port driver. Not all port drivers + The meaning of Operation and + Data depends on + the port, that is, on the port driver. Not all port drivers support this control feature.

-

Returns: a list of integers in the range 0 through 255, or a +

Returns a list of integers in the range 0..255, or a binary, depending on the port driver. The meaning of the returned data also depends on the port driver.

-

Failure: badarg if Port is not an open port or - the registered name of an open port, if Operation - 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 - Operation or Data).

+

Failures:

+ + badarg + + If Port is not an open port or the registered + name of an open port. + + badarg + + If Operation cannot fit in a 32-bit integer. + + badarg + + If the port driver does not support synchronous control + operations. + + badarg + + If the port driver so decides for any reason (probably + something wrong with Operation or + Data). + +
+ - Synchronous call to a port with term data + Performs a synchronous call to a port with term data.

Performs a synchronous call to a port. The meaning of - Operation and Data depends on the port, i.e., + Operation and Data + depends on the port, that is, on the port driver. Not all port drivers support this feature.

-

Port is a port identifier, referring to a driver.

+

Port is a port identifier, + referring to a driver.

Operation is an integer, which is passed on to the driver.

-

Data is any Erlang term. This data is converted to - binary term format and sent to the port.

-

Returns: a term from the driver. The meaning of the returned +

Data is any Erlang term. This data is converted + to binary term format and sent to the port.

+

Returns a term from the driver. The meaning of the returned data also depends on the port driver.

Failures:

badarg - If Port is not an identifier of an open - port, or the registered name of an open port. If the calling - process was linked to the previously open port identified by - Port, an exit signal due to this link - was received by the process prior to this exception. + If Port is not an identifier of an open port, + or the registered name of an open port. If the calling + process was linked to the port, the exit signal is sent + because this link was delivered to the calling process + before this exception occurs. badarg - If Operation does not fit in a - 32-bit integer. + If Operation does not fit in a 32-bit integer. badarg @@ -3725,171 +3922,192 @@ os_prompt% badarg If the port driver so decides for any reason (probably - something wrong with Operation, or - Data). + something wrong with Operation + or Data).
+ - Information about a port + Information about a port.

Returns a list containing tuples with information about - the Port, or undefined if the port is not open. - The order of the tuples is not defined, nor are all the - tuples mandatory. + Port, or undefined if the port is not open. + The order of the tuples is undefined, and all the + tuples are not mandatory. If undefined is returned and the calling process was linked to a previously open port identified by - Port, an exit signal due to this link - was received by the process prior to the return from + Port, an exit signal is sent because this link + was received by the process before the return from port_info/1.

-

Currently the result will containt information about the - following Items: registered_name (if the port has - a registered name), id, connected, links, - name, input, and output. For more information - about the different Items, see +

The result contains information about the following + Items:

+ + registered_name (if the port has a registered + name) + id + connected + links + name + input + output + +

For more information about the different Items, see port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the connected process of a port + Information about the connected process of a port.

Pid is the process identifier of the process connected to the port.

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent because this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the internal index of a port + Information about the internal index of a port.

Index is the internal index of the port. This - index may be used to separate ports.

+ index can be used to separate ports.

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent because this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the input of a port + Information about the input of a port.

Bytes is the total number of bytes read from the port.

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent because this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the links of a port + Information about the links of a port.

Pids is a list of the process identifiers of the processes that the port is linked to.

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent because this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the locking of a port + Information about the locking of a port. -

Locking is currently either false - (emulator without SMP support), port_level (port specific - locking), or driver_level (driver specific locking). Note - that these results are highly implementation specific and might - change in the future.

+

Locking is one of the following:

+ + false (emulator without SMP support) + port_level (port-specific locking) + driver_level (driver-specific locking) + +

Notice that these results are highly implementation-specific + and can change in a future release.

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent because this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the memory size of a port + Information about the memory size of a port. -

Bytes is the total amount of memory, - in bytes, allocated for this port by the runtime system. Note - that the port itself might have allocated memory which is not +

Bytes is the total number of + bytes allocated for this port by the runtime system. The + port itself can have allocated memory that is not included in Bytes.

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent because this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the monitors of a port + Information about the monitors of a port.

Monitors represent processes that this port - is monitoring.

+ monitors.

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent because this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the name of a port + Information about the name of a port.

Name is the command name set by open_port/2.

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent because this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the OS pid of a port + Information about the OS pid of a port.

OsPid is the process identifier (or equivalent) of an OS process created with @@ -3899,430 +4117,466 @@ os_prompt%

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent because this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the output of a port + Information about the output of a port.

Bytes is the total number of bytes written - to the port from Erlang processes using either + to the port from Erlang processes using port_command/2, port_command/3, - or Port ! {Owner, {command, Data}. -

+ or Port ! {Owner, {command, Data}.

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent before this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the parallelism hint of a port + Information about the parallelism hint of a port.

Boolean corresponds to the port parallelism - hint being used by this port. For more information see - the parallelism - option of open_port/2.

+ hint being used by this port. For more information, see option + parallelism + of open_port/2.

+ - Information about the queue size of a port + Information about the queue size of a port. -

Bytes is the total amount of data, - in bytes, queued by the port using the ERTS driver queue +

Bytes is the total number + of bytes queued by the port using the ERTS driver queue implementation.

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent because this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Information about the registered name of a port + Information about the registered name of a port.

RegisteredName is the registered name of the port. If the port has no registered name, [] is returned.

If the port identified by Port is not open, undefined is returned. If undefined is returned and the calling process was linked to a previously open port identified - by Port, an exit signal due to this link - was received by the process prior to the return from + by Port, an exit signal is sent because this link + was received by the process before the return from port_info/2.

Failure: badarg if Port is not a local port identifier, or an atom.

+ - Text representation of a port identifier + Text representation of a port identifier. -

Returns a string which corresponds to the text +

Returns a string corresponding to the text representation of the port identifier Port.

-

This BIF is intended for debugging and for use in - the Erlang operating system. It should not be used in - application programs.

+

This BIF is intended for debugging and for use in the + Erlang OS. It is not to be used in application programs.

+ - All open ports + Lists all open ports.

Returns a list of port identifiers corresponding to all the - ports currently existing on the local node.

- -

Note that a port that is exiting, exists but is not open.

+ ports existing on the local node.

+

Notice that an exiting port exists, but is not open.

+ - List of all pre-loaded modules + Lists all pre-loaded modules. -

Returns a list of Erlang modules which are pre-loaded in +

Returns a list of Erlang modules that are preloaded in the system. As all loading of code is done through the file system, the file system must have been loaded previously. - Hence, at least the module init must be pre-loaded.

+ Hence, at least the module init must be preloaded.

+ - Write information about a local process on standard error + Writes information about a local process on standard error.

Writes information about the local process Pid on - standard error. The currently allowed value for the atom + standard error. The only allowed value for the atom Type is backtrace, 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.

+ - Set process flag trap_exit for the calling process + Sets process flag trap_exit for the calling process.

When trap_exit is set to true, exit signals - arriving to a process are converted to {'EXIT', From, Reason} messages, which can be received as ordinary + arriving to a process are converted to {'EXIT', From, Reason} + messages, which can be received as ordinary messages. If trap_exit is set to false, the process exits if it receives an exit signal other than normal and the exit signal is propagated to its - linked processes. Application processes should normally - not trap exits.

+ linked processes. Application processes are normally + not to trap exits.

Returns the old value of the flag.

See also exit/2.

+ - Set process flag error_handler for the calling process + Sets process flag error_handler for the calling process. -

This is used by a process to redefine the error handler +

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 + processes. Inexperienced users are not to use this flag, + as code auto-loading depends on the correct operation of the error handling module.

Returns the old value of the flag.

+ - Set process flag min_heap_size for the calling process + Sets process flag min_heap_size for the calling process. -

This changes the minimum heap size for the calling - process.

+

Changes the minimum heap size for the calling process.

Returns the old value of the flag.

+ - Set process flag min_bin_vheap_size for the calling process + Sets process flag min_bin_vheap_size for the calling process. -

This changes the minimum binary virtual heap size for the calling +

Changes the minimum binary virtual heap size for the calling process.

-

Returns the old value of the flag.

+

Returns the old value of the flag.

+
+ + Sets process flag priority for the calling process. - Set process flag priority for the calling process

- This sets the process priority. Level is an atom. - There are currently four priority levels: low, - normal, high, and max. The default - priority level is normal. NOTE: The - max priority level is reserved for internal use in - the Erlang runtime system, and should not be used - by others. -

-

Internally in each priority level processes are scheduled - in a round robin fashion. -

+ Sets the process priority. Level is an atom. + There are four priority levels: low, + normal, high, and max. Default + is normal.

+ +

Priority level max is reserved for internal use in + the Erlang runtime system, and is not to be used + by others.

+
+

Internally in each priority level, processes are scheduled + in a round robin fashion.

Execution of processes on priority normal and - priority low will be interleaved. Processes on - priority low will be selected for execution less - frequently than processes on priority normal. -

-

When there are runnable processes on priority high - no processes on priority low, or normal will - be selected for execution. Note, however, that this does - not mean that no processes on priority low, - or normal will be able to run when there are - processes on priority high running. On the runtime - system with SMP support there might be more processes running - in parallel than processes on priority high, i.e., - a low, and a high priority process might - execute at the same time. -

-

When there are runnable processes on priority max + low are interleaved. Processes on priority + low are selected for execution less + frequently than processes on priority normal.

+

When there are runnable processes on priority high, + no processes on priority low or normal are + selected for execution. Notice however, that this does + not mean that no processes on priority low + or normal can run when there are processes + running on priority high. On the runtime + system with SMP support, more processes can be running + in parallel than processes on priority high, that is, + a low and a high priority process can + execute at the same time.

+

When there are runnable processes on priority max, no processes on priority low, normal, or - high will be selected for execution. As with the - high priority, processes on lower priorities might - execute in parallel with processes on priority max. -

-

Scheduling is preemptive. Regardless of priority, a process - is preempted when it has consumed more than a certain amount + high are selected for execution. As with priority + high, processes on lower priorities can + execute in parallel with processes on priority max.

+

Scheduling is pre-emptive. Regardless of priority, a process + is pre-empted when it has consumed more than a certain number of reductions since the last time it was selected for - execution. -

-

NOTE: You should not depend on the scheduling + execution.

+ +

Do 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. -

-

There is currently no 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. -

+ the runtime system with SMP support, is likely to be + changed in a future release to use available + processor cores better.

+
+

There is no automatic mechanism for + avoiding priority inversion, such as priority inheritance + or priority ceilings. When using priorities, + take this into account and handle such scenarios by + yourself.

Making calls from a high priority process into code - that you don't have control over may cause the high - priority process to wait for a processes with lower - priority, i.e., effectively decreasing the priority of the + that you have no control over can cause the high + priority process to wait for a process with lower + priority. That is, effectively decreasing the priority of the high 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 - high priority process triggers code loading, since - the code server runs on priority normal. -

+ is not the case with one version of the code that you have no + control over, it can be the case in a future + version of it. This can, for example, occur if a + high priority process triggers code loading, as + the code server runs on priority normal.

Other priorities than normal are normally not needed. - When other priorities are used, they need to be used - with care, especially the high priority must - be used with care. A process on high priority should - only perform work for short periods of time. Busy looping for - long periods of time in a high priority process will - most likely cause problems, since there are important servers - in OTP running on priority normal. -

+ When other priorities are used, use them with care, + especially priority high. A + process on priority high is only + to perform work for short periods. Busy looping for + long periods in a high priority process does + most likely cause problems, as important OTP servers + run on priority normal.

Returns the old value of the flag.

+ - Set process flag save_calls for the calling process + Sets process flag save_calls for the calling process.

N must be an integer in the interval 0..10000. - If N > 0, call saving is made active for the - process, which means that information about the N - most recent global function calls, BIF calls, sends and + If N is greater than 0, call saving is made + active for the + process. This means that information about the N + most recent global function calls, BIF calls, sends, and receives made by the process are saved in a list, which can be retrieved with process_info(Pid, last_calls). 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 {Module, Function, Arity} for - function calls, and the mere atoms send, - 'receive' and timeout for sends and receives - ('receive' when a message is received and - timeout when a receive times out). If N = 0, + is saved, as follows:

+ + A tuple {Module, Function, Arity} for + function calls + The atoms send, 'receive', and + timeout for sends and receives ('receive' + when a message is received and timeout when a + receive times out) + +

If N = 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.

Returns the old value of the flag.

+ - Set process flag sensitive for the calling process + Sets process flag sensitive for the calling process. -

Set or clear the sensitive flag for the current process. +

Sets or clears flag sensitive for the current process. When a process has been marked as sensitive by calling - process_flag(sensitive, true), features in the run-time - system that can be used for examining the data and/or inner working + process_flag(sensitive, true), features in the runtime + system that can be used for examining the data or inner working of the process are silently disabled.

Features that are disabled include (but are not limited to) the following:

-

Tracing: Trace flags can still be set for the process, but no - trace messages of any kind will be generated. - (If the sensitive flag is turned off, trace messages will - again be generated if there are any trace flags set.)

-

Sequential tracing: The sequential trace token will be propagated - as usual, but no sequential trace messages will be generated.

-

process_info/1,2 cannot be used to read out the message - queue or the process dictionary (both will be returned as empty lists).

+ + Tracing: Trace flags can still be set for the process, + but no trace messages of any kind are generated. (If flag + sensitive is turned off, trace messages are again + generated if any trace flags are set.) + Sequential tracing: The sequential trace token is + propagated as usual, but no sequential trace messages are + generated. + +

process_info/1,2 cannot be used to read out the + message queue or the process dictionary (both are returned + as empty lists).

Stack back-traces cannot be displayed for the process.

In crash dumps, the stack, messages, and the process dictionary - will be omitted.

+ are omitted.

If {save_calls,N} 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.)

+ function calls are saved to the call saving list. + (The call saving list is not cleared. Furthermore, send, receive, + and timeout events are still added to the list.)

Returns the old value of the flag.

+ - Set process flags for a process + Sets process flags for a process. -

Sets certain flags for the process Pid, in the same - manner as +

Sets certain flags for the process Pid, + in the same manner as process_flag/2. - Returns the old value of the flag. The allowed values for + Returns the old value of the flag. The valid values for Flag are only a subset of those allowed in - process_flag/2, namely: save_calls.

-

Failure: badarg if Pid is not a local process.

+ process_flag/2, namely save_calls.

+

Failure: badarg if Pid + is not a local process.

+ + Information about a process. - Information about a process

Returns a list containing InfoTuples with miscellaneous information about the process identified by - Pid, or undefined if the process is not alive. -

-

- The order of the InfoTuples is not defined, nor - are all the InfoTuples mandatory. The InfoTuples - part of the result may be changed without prior notice. - Currently InfoTuples with the following items - are part of the result: - current_function, initial_call, status, - message_queue_len, messages, links, - dictionary, trap_exit, error_handler, - priority, group_leader, total_heap_size, - heap_size, stack_size, reductions, and - garbage_collection. - If the process identified by Pid has a registered name - also an InfoTuple with the item registered_name - will appear. -

-

See process_info/2 - for information about specific InfoTuples.

+ Pid, or undefined if the process is not alive.

+

The order of the InfoTuples is undefined and + all InfoTuples are not mandatory. + The InfoTuples + part of the result can be changed without prior notice.

+

The InfoTuples with the following items + are part of the result:

+ + current_function + initial_call + status + message_queue_len + messages + links + dictionary + trap_exit + error_handler + priority + group_leader + total_heap_size + heap_size + stack_size + reductions + garbage_collection + +

If the process identified by Pid has a + registered name, + also an InfoTuple with item registered_name + appears.

+

For information about specific InfoTuples, see + process_info/2.

-

This BIF is intended for debugging only, use - process_info/2 - for all other purposes. -

+

This BIF is intended for debugging only. For + all other purposes, use + process_info/2.

-

Failure: badarg if Pid is not a local process.

+

Failure: badarg if Pid is not a + local process.

+ + Information about a process. - Information about a process -

Returns information about the process identified by Pid - as specified by the Item or the ItemList, or undefined if the - process is not alive. -

-

If the process is alive and a single Item is given, - the returned value is the corresponding - InfoTuple unless Item =:= registered_name - and the process has no registered name. In this case - [] is returned. This strange behavior is due to - historical reasons, and is kept for backward compatibility. -

-

If an ItemList is given, the result is an - InfoTupleList. The InfoTuples in the - InfoTupleList will appear with the corresponding - Items in the same order as the Items appeared - in the ItemList. Valid Items may appear multiple - times in the ItemList. -

-

If registered_name is part of an ItemList +

Returns information about the process identified by + Pid, as specified by + Item or ItemList, + or undefined if the process is not alive.

+

If the process is alive and a single Item + is given, the returned value is the corresponding + InfoTuple, unless Item =:= registered_name + and the process has no registered name. In this case, + [] is returned. This strange behavior is because of + historical reasons, and is kept for backward compatibility.

+

If ItemList is given, the result is + InfoTupleList. + The InfoTuples in + InfoTupleList appear with the corresponding + Items in the same order as the + Items appeared + in ItemList. Valid Items can + appear multiple times in ItemList.

+

If registered_name is part of ItemList and the process has no name registered a - {registered_name, []} InfoTuple will - appear in the resulting InfoTupleList. This - behavior is different than when a single - Item =:= registered_name is given, and than when - process_info/1 is used. -

-

Currently the following InfoTuples with corresponding + {registered_name, []}, InfoTuple + will appear in the resulting + InfoTupleList. This + behavior is different when a single + Item =:= registered_name is given, and when + process_info/1 is used.

+
+

The following InfoTuples with corresponding Items are valid:

{backtrace, Bin} -

The binary Bin contains the same information as - the output from +

Binary Bin contains the same information + as the output from erlang:process_display(Pid, backtrace). Use binary_to_list/1 to obtain the string of characters from the binary.

{binary, BinInfo} -

BinInfo is a list containing miscellaneous information - about binaries currently being referred to by this process. - This InfoTuple may be changed or removed without prior - notice.

+

BinInfo is a list containing miscellaneous + information about binaries currently being referred to by this + process. This InfoTuple can be changed or + removed without prior notice.

{catchlevel, CatchLevel}

CatchLevel is the number of currently active - catches in this process. This InfoTuple may be + catches in this process. This InfoTuple can be changed or removed without prior notice.

- {current_function, {Module, Function, Arity}} + {current_function, {Module, + Function, Arity}} -

Module, Function, Arity is +

Module, Function, + Arity is the current function call of the process.

- {current_location, {Module, Function, Arity, Location}} + {current_location, {Module, + Function, Arity, + Location}} -

Module, Function, Arity is +

Module, Function, + Arity is the current function call of the process. - Location is a list of two-tuples that describes the - location in the source code. -

+ Location is a list of two-tuples describing the + location in the source code.

{current_stacktrace, Stack} -

Return the current call stack back-trace (stacktrace) +

Returns the current call stack back-trace (stacktrace) of the process. The stack has the same format as returned by - erlang:get_stacktrace/0. -

+ erlang:get_stacktrace/0.

{dictionary, Dictionary} -

Dictionary is the dictionary of the process.

+

Dictionary is the process dictionary.

{error_handler, Module} @@ -4331,34 +4585,36 @@ os_prompt% {garbage_collection, GCInfo} -

GCInfo is a list which contains miscellaneous +

GCInfo is a list containing miscellaneous information about garbage collection for this process. - The content of GCInfo may be changed without + The content of GCInfo can be changed without prior notice.

{group_leader, GroupLeader} -

GroupLeader is group leader for the IO of +

GroupLeader is group leader for the I/O of the process.

{heap_size, Size} -

Size 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. -

+

Size is the size in words of the youngest heap + generation of the process. This generation includes + the process stack. This information is highly + implementation-dependent, and can change if the + implementation changes.

- {initial_call, {Module, Function, Arity}} + {initial_call, {Module, Function, + Arity}} -

Module, Function, Arity is +

Module, Function, + Arity is the initial function call with which the process was spawned.

{links, PidsAndPorts} -

PidsAndPorts is a list of pids and - port identifiers, with processes or ports to which the process +

PidsAndPorts is a list of process identifiers + and port identifiers, with processes or ports to which the process has a link.

{last_calls, false|Calls} @@ -4372,14 +4628,14 @@ os_prompt% {memory, Size}

Size is the size in bytes of the process. This - includes call stack, heap and internal structures.

+ includes call stack, heap, and internal structures.

{message_queue_len, MessageQueueLen}

MessageQueueLen is the number of messages currently in the message queue of the process. This is the length of the list MessageQueue returned as - the info item messages (see below).

+ the information item messages (see the following).

{messages, MessageQueue} @@ -4388,31 +4644,35 @@ os_prompt% {min_heap_size, MinHeapSize} -

MinHeapSize is the minimum heap size for the process.

+

MinHeapSize is the minimum heap size + for the process.

{min_bin_vheap_size, MinBinVHeapSize} -

MinBinVHeapSize is the minimum binary virtual heap size for the process.

+

MinBinVHeapSize is the minimum binary virtual + heap size for the process.

{monitored_by, Pids} -

A list of pids that are monitoring the process (with +

A list of process identifiers monitoring the process (with monitor/2).

{monitors, Monitors}

A list of monitors (started by monitor/2) that are active for the process. For a local process - monitor or a remote process monitor by pid, the list item - is {process, Pid}, and for a remote process + monitor or a remote process monitor by a process + identifier, the list item is {process, Pid}. + For a remote process monitor by name, the list item is {process, {RegName, Node}}.

- {priority, Level} + {priority, Level}

Level is the current priority level for - the process. For more information on priorities see - process_flag(priority, Level).

+ the process. For more information on priorities, see + process_flag(priority, + Level).

{reductions, Number} @@ -4427,166 +4687,203 @@ os_prompt% {sequential_trace_token, [] | SequentialTraceToken} -

SequentialTraceToken the sequential trace token for - the process. This InfoTuple may be changed or removed - without prior notice.

+

SequentialTraceToken is the sequential trace + token for the process. This InfoTuple can be + changed or removed without prior notice.

{stack_size, Size} -

Size is the stack size of the process in words.

+

Size is the stack size, in words, + of the process.

{status, Status} -

Status is the status of the process. Status - is exiting, garbage_collecting, - waiting (for a message), running, - runnable (ready to run, but another process is - running), or suspended (suspended on a "busy" port - or by the erlang:suspend_process/[1,2] BIF).

+

Status is the status of the process and is one + of the following:

+ + exiting + garbage_collecting + waiting (for a message) + running + runnable (ready to run, but another process is + running) + suspended (suspended on a "busy" port + or by the BIF erlang:suspend_process/[1,2]) +
{suspending, SuspendeeList} -

SuspendeeList is a list of {Suspendee, - ActiveSuspendCount, OutstandingSuspendCount} tuples. - Suspendee is the pid of a process that have been or is to - be suspended by the process identified by Pid via the - erlang:suspend_process/2 - BIF, or the - erlang:suspend_process/1 - BIF. ActiveSuspendCount is the number of times the - Suspendee has been suspended by Pid. +

SuspendeeList is a list of + {Suspendee, ActiveSuspendCount, + OutstandingSuspendCount} tuples. + Suspendee is the process identifier of a + process that has been, or is to be, + suspended by the process identified by Pid + through one of the following BIFs:

+ + + erlang:suspend_process/2 + + + erlang:suspend_process/1 + + +

ActiveSuspendCount is the number of + times Suspendee has been suspended by + Pid. OutstandingSuspendCount is the number of not yet - completed suspend requests sent by Pid. That is, - if ActiveSuspendCount =/= 0, Suspendee is - currently in the suspended state, and if - OutstandingSuspendCount =/= 0 the asynchronous - option of erlang:suspend_process/2 has been used and - the suspendee has not yet been suspended by Pid. - Note that the ActiveSuspendCount and - OutstandingSuspendCount are not the total suspend count - on Suspendee, only the parts contributed by Pid. -

+ completed suspend requests sent by Pid, that is:

+ + If ActiveSuspendCount =/= 0, + Suspendee is + currently in the suspended state. + + If OutstandingSuspendCount =/= 0, option + asynchronous of erlang:suspend_process/2 + has been used and the suspendee has not yet been + suspended by Pid. + + +

Notice that ActiveSuspendCount and + OutstandingSuspendCount are not the + total suspend count on Suspendee, + only the parts contributed by Pid.

{total_heap_size, Size} -

Size is the total size in words of all heap - fragments of the process. This currently include the stack - of the process. -

+

Size is the total size, in words, of all heap + fragments of the process. This includes the process stack.

{trace, InternalTraceFlags} -

InternalTraceFlags is an integer representing - internal trace flag for this process. This InfoTuple - may be changed or removed without prior notice.

+

InternalTraceFlags is an integer + representing the internal trace flag for this process. + This InfoTuple + can be changed or removed without prior notice.

{trap_exit, Boolean} -

Boolean is true if the process is trapping - exits, otherwise it is false.

+

Boolean is true if the process + is trapping exits, otherwise false.

-

Note however, that not all implementations support every one - of the above Items.

-

Failure: badarg if Pid is not a local process, - or if Item is not a valid Item.

+

Notice that not all implementations support all + these Items.

+

Failures:

+ + badarg + If Pid is not a local process. + badarg + If Item is an invalid + Item. +
+ - All processes + All processes.

Returns a list of process identifiers corresponding to - all the processes currently existing on the local node. -

-

Note that a process that is exiting, exists but is not alive, i.e., - is_process_alive/1 will return false for a process - that is exiting, but its process identifier will be part - of the result returned from processes/0. -

+ all the processes currently existing on the local node.

+

Notice that an exiting process exists, but is not alive. + That is, is_process_alive/1 returns false + for an exiting process, but its process identifier is part + of the result returned from processes/0.

+

Example:

 > processes().
 [<0.0.0>,<0.2.0>,<0.4.0>,<0.5.0>,<0.7.0>,<0.8.0>]
+ - Remove old code for a module + Removes old code for a module. -

Removes old code for Module. Before this BIF is used, - erlang:check_process_code/2 should be called to check - that no processes are executing old code in the module.

+

Removes old code for Module. + Before this BIF is used, + erlang:check_process_code/2 is to be called to check + that no processes execute old code in the module.

This BIF is intended for the code server (see - code(3)) and should not be - used elsewhere.

+ code(3)) + and is not to be used elsewhere.

Failure: badarg if there is no old code for Module.

+ - Add a new value to the process dictionary - -

Adds a new Key to the process dictionary, associated - with the value Val, and returns undefined. If - Key already exists, the old value is deleted and - replaced by Val and the function returns the old value.

- -

The values stored when put is evaluated within - the scope of a catch will not be retracted if a - throw is evaluated, or if an error occurs.

-
+ Adds a new value to the process dictionary. + +

Adds a new Key to the process dictionary, + associated with the value Val, and returns + undefined. If Key exists, the old + value is deleted and replaced by Val, and + the function returns the old value.

+

Example:

 > X = put(name, walrus), Y = put(name, carpenter),
 Z = get(name),
 {X, Y, Z}.
 {undefined,walrus,carpenter}
+ +

The values stored when put is evaluated within + the scope of a catch are not retracted if a + throw is evaluated, or if an error occurs.

+
+ + Stops execution with an exception of given class, reason, and call stack backtrace. - Stop execution with an exception of given class, reason and call stack backtrace

Stops the execution of the calling process with an - exception of given class, reason and call stack backtrace + exception of given class, reason, and call stack backtrace (stacktrace).

This BIF is intended for debugging and for use in - the Erlang operating system. In general, it should - be avoided in applications, unless you know - very well what you are doing.

+ the Erlang OS. Avoid to use it in applications, + unless you really know what you are doing.

-

Class is one of error, exit or - throw, so if it were not for the stacktrace - erlang:raise(Class, Reason, Stacktrace) is - equivalent to erlang:Class(Reason). - Reason is any term and Stacktrace is a list as - returned from get_stacktrace(), that is a list of - 4-tuples {Module, Function, Arity | Args, - Location} where Module and Function - are atoms and the third element is an integer arity or an - argument list. The stacktrace may also contain {Fun, - Args, Location} tuples where - Fun is a local fun and Args is an argument list.

-

The Location element at the end is optional. +

Class is error, exit, or + throw. So, if it were not for the stacktrace, + erlang:raise(Class, Reason, + Stacktrace) is + equivalent to erlang:Class(Reason).

+

Reason is any term. + Stacktrace is a list as + returned from get_stacktrace(), that is, a list of + four-tuples {Module, Function, Arity | Args, + Location}, where Module and Function + are atoms, and the third element is an integer arity or an + argument list. The stacktrace can also contain {Fun, + Args, Location} tuples, where Fun is a local + fun and Args is an argument list.

+

Element Location at the end is optional. Omitting it is equivalent to specifying an empty list.

The stacktrace is used as the exception stacktrace for the - calling process; it will be truncated to the current + calling process; it is truncated to the current maximum stacktrace depth.

-

Because evaluating this function causes the process to - terminate, it has no return value - unless the arguments are - invalid, in which case the function returns the error reason, that is badarg. If you want to be - really sure not to return you can call - error(erlang:raise(Class, Reason, Stacktrace)) +

Since evaluating this function causes the process to + terminate, it has no return value unless the arguments are + invalid, in which case the function returns the error + reason badarg. If you want to be + sure not to return, you can call + error(erlang:raise(Class, Reason, + Stacktrace)) and hope to distinguish exceptions later.

+ - Read the state of a timer + Reads the state of a timer.

Read the state of a timer that has been created by either @@ -4626,7 +4923,7 @@ os_prompt% the timeout message has been sent, but it does not tell you whether or not it has arrived at its destination yet. When the Result is an integer, it represents the - time in milli-seconds left until the timer will expire. + time in milli-seconds left until the timer expires.

@@ -4651,70 +4948,86 @@ os_prompt% - Read the state of a timer + Reads the state of a timer.

Read the state of a timer. The same as calling erlang:read_timer(TimerRef, []).

+ - Text representation of a reference + Text representation of a reference. -

Returns a string which corresponds to the text +

Returns a string corresponding to the text representation of Ref.

-

This BIF is intended for debugging and for use in - the Erlang operating system. It should not be used in - application programs.

+

This BIF is intended for debugging and for use in the + Erlang OS. It is not to be used in application programs.

+ - Register a name for a pid (or port) + Registers a name for a pid (or port). -

Associates the name RegName with a pid or a port - identifier. RegName, which must be an atom, can be used - instead of the pid / port identifier in the send operator +

Associates the name RegName with a process + identifier (pid) or a port identifier. + RegName, which must be an atom, can be used + instead of the pid or port identifier in send operator (RegName ! Message).

+

Example:

 > register(db, Pid).
 true
-

Failure: badarg if PidOrPort is not an existing, - local process or port, if RegName is already in use, - if the process or port is already registered (already has a - name), or if RegName is the atom undefined.

+

Failures:

+ + badarg + If PidOrPort is not an existing local + process or port. + badarg + If RegName is already in use. + badarg + If the process or port is already registered + (already has a name). + badarg + If RegName is the atom + undefined. +
+ - All registered names + All registered names. -

Returns a list of names which have been registered using - register/2.

+

Returns a list of names that have been registered using + register/2, for + example:

 > registered().
 [code_server, file_server, init, user, my_db]
+ - Resume a suspended process + Resumes a suspended process.

Decreases the suspend count on the process identified by - Suspendee. Suspendee should previously have been - suspended via - erlang:suspend_process/2, + Suspendee. Suspendee + is previously to have been suspended through + erlang:suspend_process/2 or erlang:suspend_process/1 - by the process calling erlang:resume_process(Suspendee). When - the suspend count on Suspendee reach zero, Suspendee - will be resumed, i.e., the state of the Suspendee is changed - from suspended into the state Suspendee was in before it was - suspended. -

+ by the process calling + erlang:resume_process(Suspendee). When the + suspend count on Suspendee reaches zero, + Suspendee is resumed, that is, its state + is changed from suspended into the state it had before it was + suspended.

This BIF is intended for debugging only.

@@ -4722,7 +5035,7 @@ true badarg - If Suspendee isn't a process identifier. + If Suspendee is not a process identifier. badarg @@ -4732,58 +5045,65 @@ true badarg - If the process identified by Suspendee is not alive. + If the process identified by Suspendee + is not alive.
+ - Return an integer by rounding a number + Returns an integer by rounding a number. -

Returns an integer by rounding Number.

+

Returns an integer by rounding Number, + for example:

-> round(5.5).
+round(5.5).
 6

Allowed in guard tests.

+ - Pid of the calling process + Returns pid of the calling process. -

Returns the pid (process identifier) of the calling process.

+

Returns the process identifier of the calling process, for + example:

 > self().
 <0.26.0>

Allowed in guard tests.

+ - Send a message + Sends a message. -

Sends a message and returns Msg. This is the same as - Dest ! Msg.

-

Dest may be a remote or local pid, a (local) port, a - locally registered name, or a tuple {RegName, Node} +

Sends a message and returns Msg. This + is the same as Dest ! Msg.

+

Dest can be a remote or local process identifier, + a (local) port, a locally registered name, or a tuple + {RegName, Node} for a registered name at another node.

+ + Sends a message conditionally. - Send a message conditionally - -

Sends a message and returns ok, or does not send - the message but returns something else (see below). Otherwise - the same as - erlang:send/2. See - also - erlang:send_nosuspend/2,3. - for more detailed explanation and warnings.

-

The possible options are:

+ +

Either sends a message and returns ok, or does not send + the message but returns something else (see the following). + Otherwise the same as + erlang:send/2. + For more detailed explanation and warnings, see + erlang:send_nosuspend/2,3.

+

The options are as follows:

nosuspend @@ -4793,16 +5113,17 @@ true noconnect

If the destination node would have to be auto-connected - before doing the send, noconnect is returned + to do the send, noconnect is returned instead.

-

As with erlang:send_nosuspend/2,3: Use with extreme - care!

+

As with erlang:send_nosuspend/2,3: use with extreme + care.

+ Start a timer @@ -4819,288 +5140,334 @@ true - Start a timer + Starts a timer.

Starts a timer. The same as calling erlang:send_after(Time, Dest, Msg, []).

+ - Try to send a message without ever blocking + Tries to send a message without ever blocking.

The same as - erlang:send(Dest, Msg, [nosuspend]), but returns true if + erlang:send(Dest, + Msg, [nosuspend]), + but returns true if the message was sent and false if the message was not sent because the sender would have had to be suspended.

-

This function is intended for send operations towards an +

This function is intended for send operations to an unreliable remote node without ever blocking the sending (Erlang) process. If the connection to the remote node (usually not a real Erlang node, but a node written in C or - Java) is overloaded, this function will not send the message but return false instead.

-

The same happens, if Dest refers to a local port that - is busy. For all other destinations (allowed for the ordinary - send operator '!') this function sends the message and + Java) is overloaded, this function does not send the message + and returns false.

+

The same occurs if Dest refers to a local port + that is busy. For all other destinations (allowed for the ordinary + send operator '!'), this function sends the message and returns true.

-

This function is only to be used in very rare circumstances +

This function is only to be used in rare circumstances where a process communicates with Erlang nodes that can - disappear without any trace causing the TCP buffers and - the drivers queue to be over-full before the node will actually - be shut down (due to tick timeouts) by net_kernel. The - normal reaction to take when this happens is some kind of + disappear without any trace, causing the TCP buffers and + the drivers queue to be over-full before the node is + shut down (because of tick time-outs) by net_kernel. + The normal reaction to take when this occurs is some kind of premature shutdown of the other node.

-

Note that ignoring the return value from this function would - result in unreliable message passing, which is +

Notice that ignoring the return value from this function would + result in an unreliable message passing, which is contradictory to the Erlang programming model. The message is not sent if this function returns false.

-

Note also that in many systems, transient states of +

In many systems, transient states of overloaded queues are normal. The fact that this function - returns false does not in any way mean that the other + returns false does not mean that the other node is guaranteed to be non-responsive, it could be a - temporary overload. Also a return value of true does - only mean that the message could be sent on the (TCP) channel - without blocking, the message is not guaranteed to have - arrived at the remote node. Also in the case of a disconnected + temporary overload. Also, a return value of true does + only mean that the message can be sent on the (TCP) channel + without blocking, the message is not guaranteed to + arrive at the remote node. For a disconnected non-responsive node, the return value is true (mimics - the behaviour of the ! operator). The expected - behaviour as well as the actions to take when the function - returns false are application and hardware specific.

+ the behavior of operator !). The expected + behavior and the actions to take when the function + returns false are application- and hardware-specific.

-

Use with extreme care!

+

Use with extreme care.

+ - Try to send a message without ever blocking + Tries to send a message without ever blocking.

The same as - erlang:send(Dest, Msg, [nosuspend | Options]), - but with boolean return value.

+ erlang:send(Dest, + Msg, [nosuspend | Options]), + but with a Boolean return value.

This function behaves like erlang:send_nosuspend/2), - but takes a third parameter, a list of options. The only - currently implemented option is noconnect. The option - noconnect makes the function return false if + but takes a third parameter, a list of options. + The only option is noconnect, which + makes the function return false if the remote node is not currently reachable by the local - node. The normal behaviour is to try to connect to the node, - which may stall the process for a shorter period. The use of - the noconnect option makes it possible to be - absolutely sure not to get even the slightest delay when + node. The normal behavior is to try to connect to the node, + which can stall the process during a short period. The use of + option noconnect makes it possible to be + sure not to get the slightest delay when sending to a remote process. This is especially useful when - communicating with nodes who expect to always be - the connecting part (i.e. nodes written in C or Java).

+ communicating with nodes that expect to always be + the connecting part (that is, nodes written in C or Java).

Whenever the function returns false (either when a suspend would occur or when noconnect was specified and the node was not already connected), the message is guaranteed not to have been sent.

-

Use with extreme care!

+

Use with extreme care.

+ - Set the magic cookie of a node + Sets the magic cookie of a node.

Sets the magic cookie of Node to the atom - Cookie. If Node is the local node, the function + Cookie. If Node is the + local node, the function also sets the cookie of all other unknown nodes to - Cookie (see - Distributed Erlang in the Erlang Reference Manual).

+ Cookie (see Section + Distributed Erlang + in the Erlang Reference Manual in System Documentation).

Failure: function_clause if the local node is not alive.

+ - 1..tuple_size(Tuple1) - Set Nth element of a tuple + 1..tuple_size(Tuple1 + Sets the Nth element of a tuple. -

Returns a tuple which is a copy of the argument Tuple1 - with the element given by the integer argument Index +

Returns a tuple that is a copy of argument + Tuple1 + with the element given by integer argument + Index (the first element is the element with index 1) replaced by - the argument Value.

+ argument Value, for example:

 > setelement(2, {10, green, bottles}, red).
 {10,red,bottles}
+ - Size of a tuple or binary + Size of a tuple or binary. -

Returns an integer which is the size of the argument - Item, which must be either a tuple or a binary.

+

Returns an integer that is the size of argument + Item, which must be a tuple or a binary, + for example:

 > size({morni, mulle, bwange}).
 3

Allowed in guard tests.

+ - Create a new process with a fun as entry point + Creates a new process with a fun as entry point. -

Returns the pid of a new process started by the application - of Fun to the empty list []. Otherwise works - like spawn/3.

+

Returns the process identifier of a new process started by the + application of Fun to the empty list + []. Otherwise + works like spawn/3.

+ - Create a new process with a fun as entry point on a given node + Creates a new process with a fun as entry point on a given node. -

Returns the pid of a new process started by the application - of Fun to the empty list [] on Node. If - Node does not exist, a useless pid is returned. - Otherwise works like +

Returns the process identifier of a new process started + by the application of Fun to the + empty list [] on Node. If + Node does not exist, a useless pid is + returned. Otherwise works like spawn/3.

+ - Create a new process with a function as entry point - -

Returns the pid of a new process started by the application - of Module:Function to Args. The new process - created will be placed in the system scheduler queue and be - run some time later.

-

error_handler:undefined_function(Module, Function, Args) is evaluated by the new process if - Module:Function/Arity does not exist (where - Arity is the length of Args). The error handler + Creates a new process with a function as entry point. + +

Returns the process identifier of a new process started by + the application of Module:Function + to Args. + The new created process is placed in the system scheduler + queue and will be run some time later.

+

error_handler:undefined_function(Module, + Function, Args) + is evaluated by the new process if + Module:Function/Arity + does not exist (where Arity is the length of + Args). The error handler can be redefined (see process_flag/2). If error_handler is undefined, or the user has - redefined the default error_handler its replacement is - undefined, a failure with the reason undef will occur.

+ redefined the default error_handler, its replacement is + undefined, and a failure with reason undef occurs.

+

Example:

 > spawn(speed, regulator, [high_speed, thin_cut]).
 <0.13.1>
+ - Create a new process with a function as entry point on a given node + Creates a new process with a function as entry point on a given node. -

Returns the pid of a new process started by the application - of Module:Function to Args on Node. If - Node does not exists, a useless pid is returned. +

Returns the process identifier (pid) of a new process started + by the application + of Module:Function + to Args on Node. If + Node does not exist, a useless pid is returned. Otherwise works like spawn/3.

+ - Create and link to a new process with a fun as entry point + Creates and links to a new process with a fun as entry point. -

Returns the pid of a new process started by the application - of Fun to the empty list []. A link is created between +

Returns the process identifier of a new process started by + the application of Fun to the empty list + []. A link is created between the calling process and the new process, atomically. Otherwise works like spawn/3.

+ - Create and link to a new process with a fun as entry point on a specified node + Creates and links to a new process with a fun as entry point on a specified node. -

Returns the pid of a new process started by the application - of Fun to the empty list [] on Node. A link is +

Returns the process identifier (pid) of a new process started + by the application of Fun to the empty + list [] on Node. A link is created between the calling process and the new process, - atomically. If Node does not exist, a useless pid is - returned (and due to the link, an exit signal with exit - reason noconnection will be received). Otherwise works + atomically. If Node does not exist, + a useless pid is + returned (and, because of the link, an exit signal with exit + reason noconnection is received). Otherwise works like spawn/3.

+ - Create and link to a new process with a function as entry point + Creates and links to a new process with a function as entry point. -

Returns the pid of a new process started by the application - of Module:Function to Args. A link is created +

Returns the process identifier of a new process started by + the applicatio of Module:Function + to Args. A link is created between the calling process and the new process, atomically. Otherwise works like spawn/3.

+ - Create and link to a new process with a function as entry point on a given node + Creates and links to a new process with a function as entry point on a given node. -

Returns the pid of a new process started by the application - of Module:Function to Args on Node. A +

Returns the process identifier (pid) of a new process + started by the application + of Module:Function + to Args on Node. A link is created between the calling process and the new - process, atomically. If Node does not exist, a useless - pid is returned (and due to the link, an exit signal with exit - reason noconnection will be received). Otherwise works + process, atomically. If Node does + not exist, a useless pid + is returned (and, because of the link, an exit signal with exit + reason noconnection is received). Otherwise works like spawn/3.

+ - Create and monitor a new process with a fun as entry point + Creates and monitors a new process with a fun as entry point. -

Returns the pid of a new process started by the application - of Fun to the empty list [] and reference for a monitor - created to the new process. +

Returns the process identifier of a new process, started by + the application of Fun to the empty list + [], + and a reference for a monitor created to the new process. Otherwise works like spawn/3.

+ - Create and monitor a new process with a function as entry point + Creates and monitors a new process with a function as entry point.

A new process is started by the application - of Module:Function to Args, and the process is - monitored at the same time. Returns the pid and a reference - for the monitor. - Otherwise works like + of Module:Function + to Args. The process is + monitored at the same time. Returns the process identifier + and a reference for the monitor. Otherwise works like spawn/3.

+ - - Create a new process with a fun as entry point + Creates a new process with a fun as entry point. + -

Returns the pid of a new process started by the application - of Fun to the empty list []. Otherwise - works like +

Returns the process identifier (pid) of a new process + started by the application of Fun + to the empty list []. Otherwise works like spawn_opt/4.

-

If the option monitor is given, the newly created - process will be monitored and both the pid and reference for - the monitor will be returned.

+

If option monitor is given, the newly created + process is monitored, and both the pid and reference for + the monitor is returned.

+ - - Create a new process with a fun as entry point on a given node + Creates a new process with a fun as entry point on a given node. + -

Returns the pid of a new process started by the application - of Fun to the empty list [] on Node. If - Node does not exist, a useless pid is returned. - Otherwise works like +

Returns the process identifier (pid) of a new process started + by the application of Fun to the + empty list [] on Node. If + Node does not exist, a useless pid is + returned. Otherwise works like spawn_opt/4.

+ - - Create a new process with a function as entry point + Creates a new process with a function as entry point. + -

Works exactly like +

Works as spawn/3, except that an extra option list is given when creating the process.

-

If the option monitor is given, the newly created - process will be monitored and both the pid and reference for - the monitor will be returned.

+

If option monitor is given, the newly created + process is monitored, and both the pid and reference for + the monitor is returned.

+

The options are as follows:

link @@ -5109,112 +5476,123 @@ true monitor -

Monitor the new process (just like +

Monitors the new process (like monitor/2 does).

- {priority, Level} + {priority, Level

Sets the priority of the new process. Equivalent to executing - process_flag(priority, Level) in the start function of the new process, - except that the priority will be set before the process is - selected for execution for the first time. For more information - on priorities see - process_flag(priority, Level).

+ process_flag(priority, + Level) + in the start function of the new process, + except that the priority is set before the process is + selected for execution for the first time. For more + information on priorities, see + process_flag(priority, + Level).

{fullsweep_after, Number} -

This option is only useful for performance tuning. - In general, you should not use this option unless you - know that there is problem with execution times and/or - memory consumption, and you should measure to make sure - that the option improved matters. -

+

Useful only for performance tuning. Do not use this + option unless you + know that there is problem with execution times or + memory consumption, and ensure + that the option improves matters.

The Erlang runtime system uses a generational garbage collection scheme, using an "old heap" for data that has survived at least one garbage collection. When there is no more room on the old heap, a fullsweep garbage - collection will be done.

-

The fullsweep_after option makes it possible to + collection is done.

+

Option fullsweep_after makes it possible to specify the maximum number of generational collections - before forcing a fullsweep even if there is still room on - the old heap. Setting the number to zero effectively - disables the general collection algorithm, meaning that + before forcing a fullsweep, even if there is room on + the old heap. Setting the number to zero + disables the general collection algorithm, that is, all live data is copied at every garbage collection.

-

Here are a few cases when it could be useful to change - fullsweep_after. Firstly, if binaries that are no - longer used should be thrown away as soon as possible. - (Set Number to zero.) Secondly, a process that - mostly have short-lived data will be fullsweeped seldom - or never, meaning that the old heap will contain mostly - garbage. To ensure a fullsweep once in a while, set - Number to a suitable value such as 10 or 20. - Thirdly, in embedded systems with limited amount of RAM - and no virtual memory, one might want to preserve memory - by setting Number to zero. (The value may be set - globally, see - erlang:system_flag/2.)

+

A few cases when it can be useful to change + fullsweep_after:

+ + If binaries that are no longer used are to be + thrown away as soon as possible. (Set + Number to zero.) + + A process that mostly have short-lived data is + fullsweeped seldom or never, that is, the old heap + contains mostly garbage. To ensure a fullsweep + occasionally, set Number to a + suitable value, such as 10 or 20. + + In embedded systems with a limited amount of RAM + and no virtual memory, you might want to preserve memory + by setting Number to zero. + (The value can be set globally, see + erlang:system_flag/2.) + +
{min_heap_size, Size} -

This option is only useful for performance tuning. - In general, you should not use this option unless you - know that there is problem with execution times and/or - memory consumption, and you should measure to make sure - that the option improved matters. -

-

Gives a minimum heap size in words. Setting this value - higher than the system default might speed up some +

Useful only for performance tuning. Do not use this + option unless you know that there is problem with + execution times or memory consumption, and + ensure that the option improves matters.

+

Gives a minimum heap size, in words. Setting this value + higher than the system default can speed up some processes because less garbage collection is done. - Setting too high value, however, might waste memory and - slow down the system due to worse data locality. - Therefore, it is recommended to use this option only for + However, setting a too high value can waste memory and + slow down the system because of worse data locality. + Therefore, use this option only for fine-tuning an application and to measure the execution time with various Size values.

{min_bin_vheap_size, VSize} -

This option is only useful for performance tuning. - In general, you should not use this option unless you - know that there is problem with execution times and/or - memory consumption, and you should measure to make sure - that the option improved matters. -

-

Gives a minimum binary virtual heap size in words. Setting this value - higher than the system default might speed up some +

Useful only for performance tuning. Do not use this + option unless you know that there is problem with + execution times or memory consumption, and + ensure that the option improves matters.

+

Gives a minimum binary virtual heap size, in words. + Setting this value + higher than the system default can speed up some processes because less garbage collection is done. - Setting too high value, however, might waste memory. - Therefore, it is recommended to use this option only for + However, setting a too high value can waste memory. + Therefore, use this option only for fine-tuning an application and to measure the execution time with various VSize values.

-
+ - - Create a new process with a function as entry point on a given node + Creates a new process with a function as entry point on a given node. + -

Returns the pid of a new process started by the application - of Module:Function to Args on Node. If +

Returns the process identifier (pid) of a new process started + by the application + of Module:Function to + Args on Node. If Node does not exist, a useless pid is returned. Otherwise works like spawn_opt/4.

-

The monitor option is currently not supported by +

Option monitor is not supported by spawn_opt/5.

+ 0..byte_size(Bin) - Split a binary into two + Splits a binary into two. -

Returns a tuple containing the binaries which are the result - of splitting Bin into two parts at position Pos. +

Returns a tuple containing the binaries that are the result + of splitting Bin into two parts at + position Pos. This is not a destructive operation. After the operation, - there will be three binaries altogether.

+ there are three binaries altogether.

+

Example:

 > B = list_to_binary("0123456789").
 <<"0123456789">>
@@ -5228,9 +5606,10 @@ true
7
+ - Start a timer + Starts a timer.

Starts a timer. When the timer expires, the message @@ -5268,7 +5647,7 @@ true is not allowed to be negative.

- If Dest is a pid(), it has to + If Dest is a pid(), it must be a pid() of a process created on the current runtime system instance. This process may or may not have terminated. If Dest is an @@ -5278,11 +5657,11 @@ true is given if the name does not refer to a process.

- If Dest is a pid(), the timer will - be automatically canceled if the process referred to by the + If Dest is a pid(), the timer is + automatically canceled if the process referred to by the pid() 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 + feature was introduced in ERTS version 5.4.11. Notice that + timers are not automatically canceled when Dest is an atom().

See also @@ -5290,13 +5669,14 @@ true erlang:cancel_timer/2, and erlang:read_timer/2.

-

Failure: badarg if the arguments does not satisfy - the requirements specified above.

+

Failure: badarg if the arguments do not satisfy + the requirements specified here.

+ - Start a timer + Starts a timer.

Starts a timer. The same as calling erlang:start_timer(Time, @@ -5305,126 +5685,137 @@ true - Information about context switches + Information about context switches. -

ContextSwitches is the total number of context - switches since the system started.

+

Returns the total number of context switches since the + system started.

+ - Information about exact reductions + Information about exact reductions. -

statistics(exact_reductions) is - a more expensive operation than - statistics(reductions) - especially on an Erlang machine with SMP support.

-
+

Returns the number of exact reductions.

+

statistics(exact_reductions) is + a more expensive operation than + statistics(reductions), + especially on an Erlang machine with SMP support.

+
+ - Information about garbage collection + Information about garbage collection. -

This information may not be valid for all implementations.

+

Returns information about garbage collection, for example:

 > statistics(garbage_collection).
-{85,23961,0}
-
+{85,23961,0} +

This information can be invalid for some implementations.

+ - Information about io + Information about I/O. -

Input is the total number of bytes received - through ports, and Output is the total number of - bytes output to ports.

+

Returns Input, + which is the total number of bytes + received through ports, and Output, + which is the total number of bytes output to ports.

+ - Information about reductions + Information about reductions. - -

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 - statistics(exact_reductions).

-
+

Returns information about reductions, for example:

 > statistics(reductions).
-{2046,11}
-
+{2046,11} +

As from ERTS 5.5 (OTP R11B), + this value does not include reductions performed in current + time slices of currently scheduled processes. If an + exact value is wanted, use + statistics(exact_reductions).

+
+ - Information about the run-queue + Information about the run-queue. -

Returns the total length of the run queues, that is, the number - of processes that are ready to run on all available run queues.

+

Returns the total length of run-queues, that is, the number + of processes that are ready to run on all available run-queues.

+ - Information about run-time + Information about runtime. -

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. The time is returned in milliseconds.

+

Returns information about runtime, in milliseconds.

+

The runtime is the sum of the runtime for all threads + in the Erlang runtime system and can therefore be greater + than the wall clock time.

+

Example:

 > statistics(runtime).
-{1690,1620}
-
+{1690,1620}
+ - Information about each schedulers work time - - -

- Returns a list of tuples with {SchedulerId, - ActiveTime, TotalTime}, where - SchedulerId is an integer id of the scheduler, ActiveTime is - the duration the scheduler has been busy, TotalTime is the total time duration since - scheduler_wall_time - activation. The time unit is not defined and may be subject to change - between releases, operating systems and system restarts. - scheduler_wall_time should only be used to calculate relative - values for scheduler-utilization. ActiveTime can never exceed TotalTime. -

- -

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. -

- -

- Returns undefined if the system flag - scheduler_wall_time - is turned off. -

- -

The list of scheduler information is unsorted and may appear in different order - between calls. -

-

Using scheduler_wall_time to calculate scheduler utilization.

+ Information about each schedulers work time. + + +

Returns a list of tuples with + {SchedulerId, ActiveTime, + TotalTime}, where + SchedulerId is an integer ID of the scheduler, + ActiveTime is + the duration the scheduler has been busy, and + TotalTime is the total time duration since + scheduler_wall_time + activation. The time unit is undefined and can be subject + to change between releases, OSs, and system restarts. + scheduler_wall_time is only to be used to + calculate relative values for scheduler-use. + ActiveTime can never exceed + TotalTime.

+

The definition of a busy scheduler is when it is not idle + and is not scheduling (selecting) a process or port, + that is:

+ + Executing process code + Executing linked-in-driver or NIF code + Executing built-in-functions, or any other runtime + handling + Garbage collecting + Handling any other memory management + +

Notice that a scheduler can also be busy even if the + OS has scheduled out the scheduler thread.

+

Returns undefined if system flag + scheduler_wall_time + is turned off.

+

The list of scheduler information is unsorted and can + appear in different order between calls.

+

Using scheduler_wall_time to calculate scheduler-use:

 > erlang:system_flag(scheduler_wall_time, true).
 false
 > Ts0 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.
-ok
-
-

Some time later we will take another snapshot and calculate scheduler-utilization per scheduler.

+ok +

Some time later the user takes another snapshot and calculates + scheduler-use per scheduler, for example:

 > Ts1 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.
 ok
@@ -5437,86 +5828,90 @@ ok
  {5,0.9717956667018103},
  {6,0.9739235846420741},
  {7,0.973237033077876},
- {8,0.9741297293248656}]
-
-

Using the same snapshots to calculate a total scheduler-utilization.

+ {8,0.9741297293248656}] +

Using the same snapshots to calculate a total scheduler-use:

 > {A, T} = lists:foldl(fun({{_, A0, T0}, {_, A1, T1}}, {Ai,Ti}) ->
 	{Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0,Ts1)), A/T.
-0.9769136803764825
-
+0.9769136803764825 -

scheduler_wall_time is by default disabled. Use erlang:system_flag(scheduler_wall_time, true) to enable it.

+

scheduler_wall_time is by default disabled. To + enable it, use + erlang:system_flag(scheduler_wall_time, true).

+ - Information about wall-clock + Information about wall clock. -

wall_clock can be used in the same manner as +

Returns information about wall clock. wall_clock can + be used in the same manner as runtime, except that real time is measured as opposed to runtime or CPU time.

+ - Suspend a process + Suspends a process.

Increases the suspend count on the process identified by - Suspendee 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. -

- + Suspendee and puts it in the suspended + state if it is not + already in that state. A suspended process will not be + scheduled for execution until the process has been resumed.

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 Suspendee - is decreased when + process does not leave the suspended state until its suspend + count reaches zero. The suspend count of + Suspendee is decreased when erlang:resume_process(Suspendee) is called by the same process that called - erlang:suspend_process(Suspendee). All increased suspend - counts on other processes acquired by a process will automatically be + erlang:suspend_process(Suspendee). + All increased suspend + counts on other processes acquired by a process are automatically decreased when the process terminates.

- -

Currently the following options (Opts) are available:

+

The options (Opts) are as follows:

asynchronous A suspend request is sent to the process identified by - Suspendee. Suspendee will eventually suspend - unless it is resumed before it was able to suspend. The caller - of erlang:suspend_process/2 will return immediately, - regardless of whether the Suspendee has suspended yet - or not. Note that the point in time when the Suspendee - will actually suspend cannot be deduced from other events - in the system. The only guarantee given is that the - Suspendee will eventually suspend (unless it - is resumed). If the asynchronous option has not - been passed, the caller of erlang:suspend_process/2 will - be blocked until the Suspendee has actually suspended. + Suspendee. Suspendee + eventually suspends + unless it is resumed before it could suspend. The caller + of erlang:suspend_process/2 returns immediately, + regardless of whether Suspendee has + suspended yet or not. The point in time when + Suspendee suspends cannot be deduced + from other events in the system. It is only guaranteed that + Suspendee eventually suspends + (unless it + is resumed). If option asynchronous has not + been passed, the caller of erlang:suspend_process/2 is + blocked until Suspendee has suspended. unless_suspending - The process identified by Suspendee will be suspended - unless the calling process already is suspending the - Suspendee. If unless_suspending is combined - with the asynchronous option, a suspend request will be - sent unless the calling process already is suspending the - Suspendee or if a suspend request already has been sent - and is in transit. If the calling process already is suspending - the Suspendee, or if combined with the asynchronous - option and a send request already is in transit, - false is returned and the suspend count on Suspendee - will remain unchanged. + The process identified by Suspendee is + suspended unless the calling process already is suspending + Suspendee. + If unless_suspending is combined + with option asynchronous, a suspend request is + sent unless the calling process already is suspending + Suspendee or if a suspend request + already has been sent and is in transit. If the calling + process already is suspending Suspendee, + or if combined with option asynchronous + and a send request already is in transit, + false is returned and the suspend count on + Suspendee remains unchanged. -

If the suspend count on the process identified by - Suspendee was increased, true is returned; otherwise, - false is returned.

- + Suspendee is increased, true + is returned, otherwise false.

This BIF is intended for debugging only.

@@ -5524,310 +5919,324 @@ ok badarg - If Suspendee isn't a process identifier. + If Suspendee is not a process identifier. badarg - If the process identified by Suspendee is same the process as - the process calling erlang:suspend_process/2. + If the process identified by Suspendee + is the same process + as the process calling erlang:suspend_process/2. badarg - If the process identified by Suspendee is not alive. + If the process identified by Suspendee + is not alive. badarg - If the process identified by Suspendee resides on another node. + If the process identified by Suspendee + resides on another node. badarg - If OptList isn't a proper list of valid Opts. + If OptList is not a proper list of valid + Opts. system_limit - If the process identified by Suspendee 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 - than that. + If the process identified by Suspendee + has been suspended + more times by the calling process than can be represented by the + currently used internal data structures. The system limit is + higher than 2,000,000,000 suspends and will never be lower.
+ - Suspend a process - -

Suspends the process identified by Suspendee. The - same as calling - erlang:suspend_process(Suspendee, []). For more information see the documentation of erlang:suspend_process/2. -

+ Suspends a process. + +

Suspends the process identified by + Suspendee. The same as calling + erlang:suspend_process(Suspendee, + []). + For more information, see + erlang:suspend_process/2.

This BIF is intended for debugging only.

+ - Set system flag backtrace_depth + Sets system flag backtrace_depth.

Sets the maximum depth of call stack back-traces in the exit reason element of 'EXIT' tuples.

Returns the old value of the flag.

+ + Sets system flag cpu_topology. - Set system flag cpu_topology

- This argument is deprecated and - scheduled for removal in erts-5.10/OTP-R16. Instead of using - this argument you are advised to use the erl command - line argument +sct. - When this argument has been removed a final CPU topology to use - will be determined at emulator boot time.

+ This argument is deprecated and scheduled for + removal in ERTS 5.10/OTP R16. Instead of using this + argument, use command-line argument + +sct in + erl(1).

+

When this argument is removed, a final CPU topology + to use will be determined at emulator boot time.

-

Sets the user defined CpuTopology. The user defined - CPU topology will override any automatically detected - CPU topology. By passing undefined as CpuTopology - the system will revert back to the CPU topology automatically +

Sets the user-defined CpuTopology. + The user-defined + CPU topology overrides any automatically detected + CPU topology. By passing undefined as + CpuTopology, + the system reverts to the CPU topology automatically detected. The returned value equals the value returned from erlang:system_info(cpu_topology) before the - change was made. -

+ change was made.

Returns the old value of the flag.

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. -

-

The user defined CPU topology can also be set by passing - the +sct command - line argument to erl. -

-

For information on the CpuTopology type - and more, see the documentation of - erlang:system_info(cpu_topology), - and the erl +sct - and +sbt - command line flags. -

+ topology is changed, the schedulers are sent a request + to rebind according to the new CPU topology.

+

The user-defined CPU topology can also be set by passing + command-line argument + +sct to + erl(1).

+

For information on type CpuTopology + and more, see + erlang:system_info(cpu_topology) + as well as the command-line flags + +sct and + +sbt in + erl(1).

+ - Set system flag dirty CPU schedulers online + Sets system_flag_dirty_cpu_schedulers_online.

- Sets the amount of dirty CPU schedulers online. Valid range is - where N is the - lesser of the return values of erlang:system_info(dirty_cpu_schedulers) and - erlang:system_info(schedulers_online). -

+ Sets the number of dirty CPU schedulers online. Range is + , where N + is the smallest of the return values of + erlang:system_info(dirty_cpu_schedulers) and + erlang:system_info(schedulers_online).

Returns the old value of the flag.

-

Note that the number of dirty CPU schedulers online may change if the number of - schedulers online changes. For example, if there are 12 schedulers and all are - online, and 6 dirty CPU schedulers, all online as well, and system_flag/2 - is used to set the number of schedulers online to 6, then the number of dirty - CPU schedulers online is automatically decreased by half as well, down to 3. - Similarly, the number of dirty CPU schedulers online increases proportionally - to increases in the number of schedulers online.

-

Note that the dirty schedulers functionality is experimental, and - that you have to enable support for dirty schedulers when building OTP in order - to try out the functionality.

-

For more information see +

The number of dirty CPU schedulers online can change if the + number of schedulers online changes. For example, if 12 + schedulers and 6 dirty CPU schedulers are online, and + system_flag/2 is used to set the number of + schedulers online to 6, then the number of dirty CPU + schedulers online is automatically decreased by half as well, + down to 3. Similarly, the number of dirty CPU schedulers + online increases proportionally to increases in the number of + schedulers online.

+

The dirty schedulers functionality is experimental. + Enable support for dirty schedulers when building OTP to + try out the functionality.

+
+

For more information, see erlang:system_info(dirty_cpu_schedulers) and - erlang:system_info(dirty_cpu_schedulers_online). -

+ erlang:system_info(dirty_cpu_schedulers_online).

+ - Set system flag fullsweep_after + Sets system flag fullsweep_after. -

Number is a non-negative integer which indicates +

Sets system flag fullsweep_after. + Number is a non-negative integer indicating how many times generational garbage collections can be done without forcing a fullsweep collection. The value - applies to new processes; processes already running are + applies to new processes, while processes already running are not affected.

Returns the old value of the flag.

In low-memory systems (especially without virtual - memory), setting the value to 0 can help to conserve + memory), setting the value to 0 can help to conserve memory.

-

An alternative way to set this value is through the - (operating system) environment variable - ERL_FULLSWEEP_AFTER.

+

This value can also be set through (OS) + environment variable ERL_FULLSWEEP_AFTER.

+ - Set system flag min_heap_size - -

Sets the default minimum heap size for processes. The - size is given in words. The new min_heap_size only - effects processes spawned after the change of - min_heap_size has been made. - The min_heap_size can be set for individual - processes by use of + Sets system flag min_heap_size. + +

Sets the default minimum heap size for processes. The size + is given in words. The new min_heap_size effects + only processes spawned after the change of + min_heap_size has been made. min_heap_size + can be set for individual processes by using spawn_opt/N or - process_flag/2.

+ process_flag/2.

Returns the old value of the flag.

+ - Set system flag min_bin_vheap_size + Sets system flag min_bin_vheap_size. -

Sets the default minimum binary virtual heap size for processes. The - size is given in words. The new min_bin_vhheap_size only - effects processes spawned after the change of +

Sets the default minimum binary virtual heap size for + processes. The size is given in words. + The new min_bin_vhheap_size effects only + processes spawned after the change of min_bin_vhheap_size has been made. - The min_bin_vheap_size can be set for individual - processes by use of + min_bin_vheap_size can be set for individual + processes by using spawn_opt/N or - process_flag/2.

+ process_flag/2.

Returns the old value of the flag.

+ - Set system flag multi_scheduling + Sets system flag multi_scheduling.

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.

-

If BlockState =:= block, multi-scheduling will - be blocked. If BlockState =:= unblock 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 + blocked. When multi-scheduling is blocked, only + one scheduler thread schedules Erlang processes.

+

If BlockState =:= block, multi-scheduling is + blocked. If BlockState =:= unblock and no one + else blocks multi-scheduling, and this process has + blocked only once, multi-scheduling is unblocked.

+

One process can block multi-scheduling multiple times. + If a process has blocked multiple times, it must unblock exactly as many times as it has blocked before it has released its multi-scheduling block. If a process that - has blocked multi-scheduling exits, it will release its + has blocked multi-scheduling exits, it releases its blocking of multi-scheduling.

The return values are disabled, blocked, or enabled. The returned value describes the state just after the call to erlang:system_flag(multi_scheduling, BlockState) - has been made. The return values are described in the - documentation of erlang:system_info(multi_scheduling).

-

NOTE: 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 very inefficient way to solve the - problem.

-

See also erlang:system_info(multi_scheduling), + has been made. For information about the return values, see + erlang:system_info(multi_scheduling).

+

Blocking of multi-scheduling is normally not needed. + If you feel that you need to block multi-scheduling, + consider it a few more times again. Blocking multi-scheduling + is only to be used as a last resort, as it is most likely + a very inefficient way to solve the problem.

+
+

See also + erlang:system_info(multi_scheduling), erlang:system_info(multi_scheduling_blockers), and erlang:system_info(schedulers).

+ + Sets system flag scheduler_bind_type. - Set system flag scheduler_bind_type

- This argument is deprecated and - scheduled for removal in erts-5.10/OTP-R16. Instead of using - this argument you are advised to use the erl command - line argument +sbt. - When this argument has been removed a final scheduler bind type - to use will be determined at emulator boot time.

+ This argument is deprecated and scheduled for + removal in ERTS 5.10/OTP R16. Instead of using this + argument, use command-line argument + +sbt in erl(1). + When this argument is removed, a final scheduler bind + type to use will be determined at emulator boot time.

Controls if and how schedulers are bound to logical processors.

-

When erlang:system_flag(scheduler_bind_type, How) is - called, an asynchronous signal is sent to all schedulers - online which causes them to try to bind or unbind as requested. - NOTE: 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 - error_logger. If you want to verify that the - schedulers actually have bound as requested, call - erlang:system_info(scheduler_bindings). -

-

Schedulers can currently only be bound on newer Linux, +

When erlang:system_flag(scheduler_bind_type, How + is called, an asynchronous signal is sent to all schedulers + online, causing them to try to bind or unbind as requested.

+

If a scheduler fails to bind, this is often silently + ignored, as it is not always possible to verify valid + logical processor identifiers. If an error is reported, + it is reported to error_logger. To verify that the + schedulers have bound as requested, call + erlang:system_info(scheduler_bindings).

+
+

Schedulers can be bound on newer Linux, Solaris, FreeBSD, and Windows systems, but more systems will be - supported in the future. -

+ supported in future releases.

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. + the CPU topology must be known. If the runtime system fails + to detect the CPU topology automatically, it can be defined. For more information on how to define the CPU topology, see - the erl +sct command - line flag. -

-

The runtime system will by default not bind schedulers - to logical processors. -

-

NOTE: 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.

-

Schedulers can be bound in different ways. The How - argument determines how schedulers are bound. How can - currently be one of:

+ command-line flag +sct + in erl(1).

+

The runtime system does by default not bind schedulers + to logical processors.

+

If the Erlang runtime system is the only OS + process binding threads to logical processors, this + improves the performance of the runtime system. However, + if other OS processes (for example, another Erlang + runtime system) also bind threads to logical processors, + there can be a performance penalty instead. Sometimes this + performance penalty can be severe. If so, it is recommended + to not bind the schedulers.

+
+

Schedulers can be bound in different ways. Argument + How determines how schedulers are + bound and can be any of the following:

unbound -

Same as the erl command line argument - +sbt u. +

Same as command-line argument + +sbt u in erl(1).

no_spread -

Same as the erl command line argument - +sbt ns. +

Same as command-line argument + +sbt ns in erl(1).

thread_spread -

Same as the erl command line argument - +sbt ts. +

Same as command-line argument + +sbt ts in erl(1).

processor_spread -

Same as the erl command line argument - +sbt ps. +

Same as command-line argument + +sbt ps in erl(1).

spread -

Same as the erl command line argument - +sbt s. +

Same as command-line argument + +sbt s in erl(1).

no_node_thread_spread -

Same as the erl command line argument - +sbt nnts. +

Same as command-line argument + +sbt nnts in erl(1).

no_node_processor_spread -

Same as the erl command line argument - +sbt nnps. +

Same as command-line argument + +sbt nnps in erl(1).

thread_no_node_processor_spread -

Same as the erl command line argument - +sbt tnnps. +

Same as command-line argument + +sbt tnnps in erl(1).

default_bind -

Same as the erl command line argument - +sbt db. +

Same as command-line argument + +sbt db in erl(1).

-

The value returned equals How before the - scheduler_bind_type flag was changed.

-

Failure:

+

The returned value equals How before flag + scheduler_bind_type was changed.

+

Failures:

notsup @@ -5835,80 +6244,82 @@ ok badarg -

If How isn't one of the documented alternatives.

+

If How is not one of the documented + alternatives.

badarg -

If no CPU topology information is available.

+

If CPU topology information is unavailable.

The scheduler bind type can also be set by passing - the +sbt command - line argument to erl. -

+ command-line argument + +sbt to erl(1).

For more information, see erlang:system_info(scheduler_bind_type), erlang:system_info(scheduler_bindings), - the erl +sbt - and +sct command line - flags. -

+ as well as command-line flags + +sbt + and +sct + in erl(1).

+ - Set system flag scheduler_wall_time + Sets system flag scheduler_wall_time.

- Turns on/off scheduler wall time measurements.

-

For more information see, - erlang:statistics(scheduler_wall_time). -

+ Turns on or off scheduler wall time measurements.

+

For more information, see + erlang:statistics(scheduler_wall_time).

+ - Set system flag schedulers_online + Sets system flag schedulers_online.

- Sets the amount of schedulers online. Valid range is - . -

+ Sets the number of schedulers online. Range is + .

Returns the old value of the flag.

-

Note that if the emulator was built with support for dirty schedulers, - changing the number of schedulers online can also change the number of dirty - CPU schedulers online. For example, if there are 12 schedulers and all are - online, and 6 dirty CPU schedulers, all online as well, and system_flag/2 - is used to set the number of schedulers online to 6, then the number of dirty - CPU schedulers online is automatically decreased by half as well, down to 3. - Similarly, the number of dirty CPU schedulers online increases proportionally - to increases in the number of schedulers online.

-

For more information see, - erlang:system_info(schedulers), +

The emulator was built with support for + dirty schedulers. + Changing the number of schedulers online can also change the + number of dirty CPU schedulers online. For example, if 12 + schedulers and 6 dirty CPU schedulers are online, and + system_flag/2 is used to set the number of schedulers + online to 6, then the number of dirty CPU schedulers online + is automatically decreased by half as well, down to 3. + Similarly, the number of dirty CPU schedulers online increases + proportionally to increases in the number of schedulers online.

+

For more information, see + erlang:system_info(schedulers) and - erlang:system_info(schedulers_online). -

+ erlang:system_info(schedulers_online).

+ - Set system flag trace_control_word + Sets system flag trace_control_word. -

Sets the value of the node's trace control word to - TCW. TCW should be an unsigned integer. For - more information see documentation of the +

Sets the value of the node trace control word to + TCW, which is to be an unsigned integer. + For more information, see the function set_tcw - function in the match specification documentation in the - ERTS User's Guide.

+ in Section "Match Specifications in Erlang" in the + User's Guide.

Returns the old value of the flag.

- + Finalize the Time Offset -

Finalizes the time offset +

+ Finalizes the time offset when the single time warp mode is being used. If another time warp mode than the "single time warp mode" is used, the time offset state will be left @@ -5932,71 +6343,74 @@ ok + + Information about the system allocators. - Information about the allocators of the system -

- Returns various information about the - allocators of the + +

Returns various information about the allocators of the current system (emulator) as specified by Item:

+ - allocated_areas + allocated_areas

Returns a list of tuples with information about miscellaneous allocated memory areas.

-

Each tuple contains an atom describing type of memory as - first element and amount of allocated memory in bytes as - second element. In those cases when there is information - present about allocated and used memory, a third element - is present. This third element contains the amount of +

Each tuple contains an atom describing the type of + memory as first element and the amount of allocated + memory in bytes as second element. When information + about allocated and used memory is present, also a + third element is present, containing the amount of used memory in bytes.

erlang:system_info(allocated_areas) is intended - for debugging, and the content is highly implementation - dependent. The content of the results will therefore - change when needed without prior notice.

-

Note: The sum of these values is not + for debugging, and the content is highly + implementation-dependent. The content of the results + therefore changes when needed without prior notice.

+

Notice that the sum of these values is not the total amount of memory allocated by the emulator. Some values are part of other values, and some memory - areas are not part of the result. If you are interested - in the total amount of memory allocated by the emulator - see erlang:memory/0,1.

+ areas are not part of the result. For information about + the total amount of memory allocated by the emulator, see + erlang:memory/0,1.

- allocator + allocator -

Returns {Allocator, Version, Features, Settings}.

-

Explanation:

+ +

Returns {Allocator, Version, + Features, Settings, where:

-

Allocator corresponds to the malloc() - implementation used. If Allocator equals +

Allocator corresponds to the + malloc() implementation used. If + Allocator equals undefined, the malloc() implementation - used could not be identified. Currently - glibc can be identified.

+ used cannot be identified. glibc can be + identified.

-

Version is a list of integers (but not a - string) representing the version of +

Version is a list of integers + (but not a string) representing the version of the malloc() implementation used.

-

Features is a list of atoms representing - allocation features used.

+

Features is a list of atoms + representing the allocation features used.

-

Settings is a list of subsystems, their - configurable parameters, and used values. Settings - may differ between different combinations of +

Settings is a list of subsystems, + their configurable parameters, and used values. Settings + can differ between different combinations of platforms, allocators, and allocation features. Memory sizes are given in bytes.

@@ -6004,59 +6418,63 @@ ok

See also "System Flags Effecting erts_alloc" in erts_alloc(3).

- alloc_util_allocators + alloc_util_allocators -

Returns a list of the names of all allocators - using the ERTS internal alloc_util framework - as atoms. For more information see the - "the - alloc_util framework" section in the - erts_alloc(3) documentation. -

+ +

Returns a list of the names of all allocators using + the ERTS internal alloc_util framework + as atoms. For more information, see Section + "The + alloc_util framework" in erts_alloc(3).

- {allocator, Alloc} + {allocator, Alloc} +

Returns information about the specified allocator. - As of erts version 5.6.1 the return value is a list - of {instance, InstanceNo, InstanceInfo} tuples + As from ERTS 5.6.1, the return value is a list + of {instance, InstanceNo, InstanceInfo} tuples, where InstanceInfo contains information about - a specific instance of the allocator. As of erts version - 5.10.4 the returned list when calling + a specific instance of the allocator. As from + ERTS 5.10.4, the returned list when calling erlang:system_info({allocator, mseg_alloc}) also - include an {erts_mmap, _} tuple as one element - in the list. - If Alloc is not a recognized allocator, - undefined is returned. If Alloc is disabled, + includes an {erts_mmap, _} tuple as one element + in the list. If Alloc is not a + recognized allocator, undefined is returned. + If Alloc is disabled, false is returned.

-

Note: The information returned is highly - implementation dependent and may be changed, or removed +

Notice that the information returned is highly + implementation-dependent and can be changed or removed at any time without prior notice. It was initially intended as a tool when developing new allocators, but - since it might be of interest for others it has been + as it can be of interest for others it has been briefly documented.

The recognized allocators are listed in erts_alloc(3). After reading the erts_alloc(3) documentation, the returned information - should more or less speak for itself. But it can be worth + more or less speaks for itself, but it can be worth explaining some things. Call counts are presented by two - values. The first value is giga calls, and the second - value is calls. mbcs, and sbcs are - abbreviations for, respectively, multi-block carriers, and - single-block carriers. Sizes are presented in bytes. When - it is not a size that is presented, it is the amount of - something. Sizes and amounts are often presented by three - values, the first is current value, the second is maximum - value since the last call to - erlang:system_info({allocator, Alloc}), and - the third is maximum value since the emulator was started. - If only one value is present, it is the current value. + values, the first value is giga calls, and the second + value is calls. mbcs and sbcs denote + multi-block carriers, and single-block carriers, + respectively. Sizes are presented in bytes. When a + size is not presented, it is the amount of something. + Sizes and amounts are often presented by three values:

+ + The first is the current value. + The second is the maximum value since the last call + to erlang:system_info({allocator, Alloc}). + The third is the maximum value since the emulator + was started. + +

If only one value is present, it is the current value. fix_alloc memory block types are presented by two - values. The first value is memory pool size and - the second value used memory size.

+ values. The first value is the memory pool size and + the second value is the used memory size.

- {allocator_sizes, Alloc} + {allocator_sizes, Alloc} +

Returns various size information for the specified allocator. The information returned is a subset of the information returned by @@ -6066,103 +6484,103 @@ ok + + Information about the CPU topology of the system. - All LevelEntrys of a list must contain the same LevelTag, except on the top level where both node and - processor LevelTags may co-exist. + processor LevelTags can coexist. - {LevelTag, SubLevel} == {LevelTag, [], SubLevel} + {LevelTag, + SubLevel} == {LevelTag, [], + SubLevel} - More LevelTags may be introduced in the future. + More LevelTags can be introduced in a + future release. - The info_list() may be extended in the future. + The info_list() can be extended in a future release. - Information about the CPU topology of the system -

Returns various information about the - CPU topology - of the current system - (emulator) as specified by Item:

+ + +

Returns various information about the CPU topology of + the current system (emulator) as specified by + Item:

cpu_topology -

Returns the CpuTopology which currently is used by the - emulator. The CPU topology is used when binding schedulers +

Returns the CpuTopology currently used by + the emulator. The CPU topology is used when binding schedulers to logical processors. The CPU topology used is the - user - defined CPU topology if such exists; otherwise, the - automatically - detected CPU topology if such exists. If no CPU topology + user-defined CPU topology, + if such exists, otherwise the + automatically detected CPU topology, + if such exists. If no CPU topology exists, undefined is returned.

-

node refers to NUMA (non-uniform memory access) - nodes, and thread refers to hardware threads - (e.g. Intels hyper-threads).

-

A level in the CpuTopology term can be omitted if - only one entry exists and the InfoList is empty. -

+

node refers to Non-Uniform Memory Access (NUMA) + nodes. thread refers to hardware threads + (for example, Intel hyper-threads).

+

A level in term CpuTopology can be + omitted if only one entry exists and + InfoList is empty.

thread can only be a sub level to core. - core can be a sub level to either processor - or node. processor can either be on the + core can be a sub level to processor + or node. processor can be on the top level or a sub level to node. node - can either be on the top level or a sub level to + can be on the top level or a sub level to processor. That is, NUMA nodes can be processor internal or processor external. A CPU topology can consist of a mix of processor internal and external - NUMA nodes, as long as each logical CPU belongs to one - and only one NUMA node. Cache hierarchy is not part of - the CpuTopology 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 - CpuTopology type to change. -

-
- {cpu_topology, defined} - -

Returns the user defined CpuTopology. For more - information see the documentation of - the erl +sct command - line flag, and the documentation of the - cpu_topology - argument. -

-
- {cpu_topology, detected} - -

Returns the automatically detected CpuTopology. 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. -

-

For more information see the documentation of the - cpu_topology - argument. -

+ NUMA nodes, as long as each logical CPU belongs to + one NUMA node. Cache hierarchy is not part of + the CpuTopology type, but will be in a + future release. Other things can also make it into the CPU + topology in a future release. In other words, expect the + CpuTopology type to change.

+
+ {cpu_topology, defined} + + +

Returns the user-defined CpuTopology. + For more information, see command-line flag + +sct in + erl(1) and argument + cpu_topology.

+
+ {cpu_topology, detected} + + +

Returns the automatically detected + CpuTopology. The + emulator detects the CPU topology on some newer + Linux, Solaris, FreeBSD, and Windows systems. + On Windows system with more than 32 logical processors, + the CPU topology is not detected.

+

For more information, see argument + cpu_topology.

{cpu_topology, used} -

Returns the CpuTopology which is used by the - emulator. For more information see the - documentation of the - cpu_topology - argument. -

+

Returns CpuTopology used by the emulator. + For more information, see argument + cpu_topology.

+ @@ -6224,7 +6642,7 @@ ok - Information about the system + Information about the system.

Returns various information about the current system (emulator) as specified by Item:

@@ -6241,8 +6659,7 @@ ok Other possible return values are debug, purify, quantify, purecov, gcov, valgrind, gprof, and lcnt. Possible return values - may be added and/or removed at any time without prior notice. -

+ can be added or removed at any time without prior notice.

c_compiler_used @@ -6250,26 +6667,25 @@ ok compiling the runtime system. The first element is an atom describing the name of the compiler, or undefined if unknown. The second element is a term describing the - version of the compiler, or undefined if unknown. -

+ version of the compiler, or undefined if unknown.

check_io

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 + about the emulators internal I/O checking. Notice that + the content of the returned list can vary between + platforms and over time. It is only guaranteed that a list is returned.

compat_rel

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 + Erlang/OTP release that the current emulator has been set to be backward compatible with. The compatibility - mode can be configured at startup by using the command - line flag +R, see - erl(1).

+ mode can be configured at startup by using command-line flag + +R in + erl(1).

cpu_topology @@ -6282,19 +6698,19 @@ ok creation of a node is stored in process identifiers, port identifiers, and references. This makes it (to some extent) possible to distinguish between identifiers from - different incarnations of a node. Currently valid - creations are integers in the range 1..3, but this may - (probably will) change in the future. If the node is not - alive, 0 is returned.

+ different incarnations of a node. The valid + creations are integers in the range 1..3, but this will + probably change in a future release. If the node is not + alive, 0 is returned.

debug_compiled

Returns true if the emulator has been debug - compiled; otherwise, false. -

+ compiled, otherwise false.

- delayed_node_table_gc + delayed_node_table_gc +

Returns the amount of time in seconds that garbage collection of an entry in a node table will be delayed. This limit can be set on startup by passing the @@ -6302,124 +6718,130 @@ ok flag to erl. For more information see the documentation of the command line flag.

- dirty_cpu_schedulers + dirty_cpu_schedulers +

Returns the number of dirty CPU scheduler threads used by the emulator. Dirty CPU schedulers execute CPU-bound - native functions such as NIFs, linked-in driver code, and BIFs - that cannot be managed cleanly by the emulator's normal schedulers. -

-

The number of dirty CPU scheduler threads is determined at emulator - boot time and cannot be changed after that. The number of dirty CPU - scheduler threads online can however be changed at any time. The number of - dirty CPU schedulers can be set on startup by passing - the +SDcpu or - +SDPcpu command line flags, - see erl(1). -

-

Note that the dirty schedulers functionality is experimental, and - that you have to enable support for dirty schedulers when building OTP in - order to try out the functionality.

-

See also erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline), + native functions, such as NIFs, linked-in driver code, + and BIFs that cannot be managed cleanly by the normal + emulator schedulers.

+

The number of dirty CPU scheduler threads is determined + at emulator boot time and cannot be changed after that. + However, the number of dirty CPU scheduler threads online + can be changed at any time. The number of dirty CPU + schedulers can be set at startup by passing + command-line flag + +SDcpu or + +SDPcpu in + erl(1).

+

Notice that the dirty schedulers functionality is + experimental. Enable support for dirty schedulers when + building OTP to try out the functionality.

+

See also + erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline), erlang:system_info(dirty_cpu_schedulers_online), erlang:system_info(dirty_io_schedulers), erlang:system_info(schedulers), erlang:system_info(schedulers_online), and erlang:system_flag(schedulers_online, SchedulersOnline).

- dirty_cpu_schedulers_online - -

Returns the number of dirty CPU schedulers online. The return value - satisfies the following relationship: - , where N is - the lesser of the return values of erlang:system_info(dirty_cpu_schedulers) and - erlang:system_info(schedulers_online). -

-

The number of dirty CPU schedulers online can be set on startup by passing - the +SDcpu command line flag, see - erl(1). -

-

Note that the dirty schedulers functionality is experimental, and - that you have to enable support for dirty schedulers when building OTP in - order to try out the functionality.

+ dirty_cpu_schedulers_online + + +

Returns the number of dirty CPU schedulers online. + The return value satisfies + , + where N is the smallest of the return values of + erlang:system_info(dirty_cpu_schedulers) and + erlang:system_info(schedulers_online).

+

The number of dirty CPU schedulers online can be set at + startup by passing command-line flag + +SDcpu in + erl(1).

+

Notice that the dirty schedulers functionality is + experimental. Enable support for dirty schedulers when + building OTP to try out the functionality.

For more information, see erlang:system_info(dirty_cpu_schedulers), erlang:system_info(dirty_io_schedulers), erlang:system_info(schedulers_online), and - erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline). -

-
- dirty_io_schedulers - -

Returns the number of dirty I/O schedulers as an integer. Dirty I/O schedulers - execute I/O-bound native functions such as NIFs and linked-in driver code that - cannot be managed cleanly by the emulator's normal schedulers. -

-

This value can be set on startup by passing - the +SDio command line flag, see - erl(1). -

-

Note that the dirty schedulers functionality is experimental, and - that you have to enable support for dirty schedulers when building OTP in - order to try out the functionality.

+ erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline).

+
+ dirty_io_schedulers + + +

Returns the number of dirty I/O schedulers as an integer. + Dirty I/O schedulers execute I/O-bound native functions, + such as NIFs and linked-in driver code, which cannot be + managed cleanly by the normal emulator schedulers.

+

This value can be set at startup by passing command-line + argument +SDio + in erl(1).

+

Notice that the dirty schedulers functionality is + experimental. Enable support for dirty schedulers when + building OTP to try out the functionality.

For more information, see erlang:system_info(dirty_cpu_schedulers), erlang:system_info(dirty_cpu_schedulers_online), and - erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline). -

+ erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline).

dist

Returns a binary containing a string of distribution information formatted as in Erlang crash dumps. For more - information see the "How to interpret the Erlang crash dumps" - chapter in the ERTS User's Guide.

+ information, see Section + "How to interpret the Erlang crash dumps" + in the User's Guide.

- dist_buf_busy_limit + dist_buf_busy_limit +

Returns the value of the distribution buffer busy limit - in bytes. This limit can be set on startup by passing the - +zdbbl command line - flag to erl.

+ in bytes. This limit can be set at startup by passing + command-line flag + +zdbbl + to erl.

dist_ctrl

Returns a list of tuples - {Node, ControllingEntity}, one entry for each - connected remote node. The Node is the name of the - node and the ControllingEntity is the port or pid - responsible for the communication to that node. More - specifically, the ControllingEntity for nodes - connected via TCP/IP (the normal case) is the socket - actually used in communication with the specific node.

+ {Node, ControllingEntity}, + one entry for each connected remote node. + Node is the node name + and ControllingEntity is the port or process + identifier responsible for the communication to that node. + More specifically, ControllingEntity for + nodes connected through TCP/IP (the normal case) is the socket + used in communication with the specific node.

driver_version -

Returns a string containing the erlang driver version - used by the runtime system. It will be on the form +

Returns a string containing the Erlang driver version + used by the runtime system. It has the form "<major ver>.<minor ver>".

dynamic_trace

Returns an atom describing the dynamic trace framework - compiled into the virtual machine. It can currently be either - dtrace, systemtap or none. For a - commercial or standard build, this is always none, - the other return values indicate a custom configuration - (e.g. ./configure --with-dynamic-trace=dtrace). See - the dyntrace - manual page and the + compiled into the virtual machine. It can be + dtrace, systemtap, or none. For a + commercial or standard build, it is always none. + The other return values indicate a custom configuration + (for example, ./configure --with-dynamic-trace=dtrace). + For more information about dynamic tracing, see the + dyntrace + manual page and the README.dtrace/README.systemtap files in the - Erlang source code top directory for more information - about dynamic tracing.

+ Erlang source code top directory.

dynamic_trace_probes -

Returns a boolean() indicating if dynamic trace probes - (either dtrace or systemtap) are built into the - emulator. This can only be true if the virtual - machine was built for dynamic tracing - (i.e. system_info(dynamic_trace) returns +

Returns a boolean() indicating if dynamic trace + probes (dtrace or systemtap) are built into + the emulator. This can only be true if the Virtual + Machine was built for dynamic tracing (that is, + system_info(dynamic_trace) returns dtrace or systemtap).

end_time @@ -6433,8 +6855,8 @@ ok elib_malloc

This option will be removed in a future release. - The return value will always be false since - the elib_malloc allocator has been removed.

+ The return value will always be false, as the + elib_malloc allocator has been removed.

eager_check_io @@ -6448,27 +6870,28 @@ ok ets_limit -

Returns the maximum number of ETS tables allowed. This limit - can be increased on startup by passing the +e command line flag to - erl or by setting the environment variable - ERL_MAX_ETS_TABLES before starting the Erlang runtime - system.

+

Returns the maximum number of ETS tables allowed. This + limit can be increased at startup by passing + command-line flag + +e to + erl(1) or by setting environment variable + ERL_MAX_ETS_TABLES before starting the Erlang + runtime system.

fullsweep_after -

Returns {fullsweep_after, integer() >= 0} which is the - fullsweep_after garbage collection setting used - by default. For more information see - garbage_collection described below.

+

Returns {fullsweep_after, integer() >= 0}, which is + the fullsweep_after garbage collection setting used + by default. For more information, see + garbage_collection described in the following.

garbage_collection

Returns a list describing the default garbage collection settings. A process spawned on the local node by a - spawn or spawn_link will use these + spawn or spawn_link uses these garbage collection settings. The default settings can be - changed by use of + changed by using system_flag/2. spawn_opt/4 can spawn a process that does not use the default @@ -6482,8 +6905,8 @@ ok heap_type -

Returns the heap type used by the current emulator. - Currently only the following heap type exists:

+

Returns the heap type used by the current emulator. One + heap type exists:

private @@ -6498,51 +6921,51 @@ ok

Returns a binary containing a string of miscellaneous system information formatted as in Erlang crash dumps. - For more information see the - "How to interpret the Erlang crash dumps" chapter in the ERTS - User's Guide.

+ For more information, see Section + "How to interpret the Erlang crash dumps" + in the User's Guide.

kernel_poll

Returns true if the emulator uses some kind of - kernel-poll implementation; otherwise, false.

+ kernel-poll implementation, otherwise false.

loaded

Returns a binary containing a string of loaded module information formatted as in Erlang crash dumps. For more - information see the "How to interpret the Erlang crash dumps" chapter - in the ERTS User's Guide.

+ information, see Section + "How to interpret the Erlang crash dumps" + in the User's Guide.

- logical_processors + logical_processors +

Returns the detected number of logical processors configured - on the system. The return value is either an integer, or - the atom unknown if the emulator wasn't able to - detect logical processors configured. -

+ in the system. The return value is either an integer, or + the atom unknown if the emulator cannot + detect the configured logical processors.

- logical_processors_available + logical_processors_available -

Returns the detected number of logical processors available to - the Erlang runtime system. The return value is either an - integer, or the atom unknown if the emulator wasn't - able to detect logical processors available. The number - of logical processors available is less than or equal to - the number of logical - processors online. -

+ +

Returns the detected number of logical processors available + to the Erlang runtime system. The return value is either an + integer, or the atom unknown if the emulator + cannot detect the available logical processors. The number + of available logical processors is less than or equal to + the number of + logical processors online.

- logical_processors_online + logical_processors_online +

Returns the detected number of logical processors online on the system. The return value is either an integer, - or the atom unknown if the emulator wasn't able to + or the atom unknown if the emulator cannot detect logical processors online. The number of logical processors online is less than or equal to the number of - logical processors - configured. -

+ logical processors configured.

machine @@ -6550,27 +6973,30 @@ ok min_heap_size -

Returns {min_heap_size, MinHeapSize} where MinHeapSize is the current system wide - minimum heap size for spawned processes.

+

Returns {min_heap_size, MinHeapSize}, + where MinHeapSize is the current + system-wide minimum heap size for spawned processes.

min_bin_vheap_size -

Returns {min_bin_vheap_size, MinBinVHeapSize} where MinBinVHeapSize is the current system wide +

Returns {min_bin_vheap_size, + MinBinVHeapSize}, where + MinBinVHeapSize is the current system-wide minimum binary virtual heap size for spawned processes.

modified_timing_level -

Returns the modified timing level (an integer) if - modified timing has been enabled; otherwise, - undefined. See the +T command line flag - in the documentation of the - erl(1) - command for more information on modified timing.

+

Returns the modified timing-level (an integer) if + modified timing is enabled, otherwise, undefined. + For more information about modified timing, see + command-line flag + +T + in erl(1)

- multi_scheduling + multi_scheduling -

Returns disabled, blocked, or enabled. - A description of the return values:

+ +

Returns disabled, blocked, or enabled:

disabled @@ -6581,32 +7007,38 @@ ok blocked

The emulator has more than one scheduler thread, - but all scheduler threads but one have been blocked, - i.e., only one scheduler thread will schedule - Erlang processes and execute Erlang code.

+ but all scheduler threads except one are blocked, + that is, only one scheduler thread schedules + Erlang processes and executes Erlang code.

enabled

The emulator has more than one scheduler thread, - and no scheduler threads have been blocked, i.e., - all available scheduler threads will schedule + and no scheduler threads are blocked, that is, + all available scheduler threads schedule Erlang processes and execute Erlang code.

-

See also erlang:system_flag(multi_scheduling, BlockState), - erlang:system_info(multi_scheduling_blockers), and +

See also + erlang:system_flag(multi_scheduling, BlockState), + erlang:system_info(multi_scheduling_blockers), + and erlang:system_info(schedulers).

- multi_scheduling_blockers + multi_scheduling_blockers -

Returns a list of PIDs when multi-scheduling - is blocked; otherwise, the empty list. The PIDs - in the list is PIDs of the processes currently - blocking multi-scheduling. A PID will only be - present once in the list, even if the corresponding + +

Returns a list of Pids when + multi-scheduling is blocked, otherwise the empty list is + returned. The Pids in the list are + Pids of the processes currently + blocking multi-scheduling. A Pid is + present only once in the list, even if the corresponding process has blocked multiple times.

-

See also erlang:system_flag(multi_scheduling, BlockState), - erlang:system_info(multi_scheduling), and +

See also + erlang:system_flag(multi_scheduling, BlockState), + erlang:system_info(multi_scheduling), + and erlang:system_info(schedulers).

nif_version @@ -6614,19 +7046,19 @@ ok

Returns a string containing the erlang NIF version used by the runtime system. It will be on the form "<major ver>.<minor ver>".

- otp_release + otp_release +

Returns a string containing the OTP release number of the - OTP release that the currently executing ERTS application is + OTP release that the currently executing ERTS application is part of.

-

As of OTP release 17, the OTP release number corresponds to - the major OTP version number. There is no - erlang:system_info() argument giving the exact OTP - version. This since the exact OTP version in the general case - is hard to determine. For more information see - the - documentation of versions in the system principles - guide.

+

As from OTP 17, the OTP release number corresponds to + the major OTP version number. No + erlang:system_info() argument gives the exact OTP + version. This is because the exact OTP version in the general case + is difficult to determine. For more information, see the description + of versions in + System principles in System Documentation.

os_monotonic_time_source @@ -6745,130 +7177,137 @@ ok time unit.

- port_parallelism -

Returns the default port parallelism scheduling hint used. - For more information see the - +spp command line argument - of erl(1).

+ port_parallelism
+ + +

Returns the default port parallelism scheduling hint used. + For more information, see command-line argument + +spp in erl(1).

port_count -

Returns the number of ports currently existing at - the local node as an integer. The same value as - length(erlang:ports()) returns, but more efficient.

+

Returns the number of ports currently existing at the + local node. The value is given as an integer. This is + the same value as returned by + length(erlang:ports()), but more efficient.

- port_limit + port_limit +

Returns the maximum number of simultaneously existing - ports at the local node as an integer. This limit - can be configured at startup by using the - +Q - command line flag of - erl(1).

+ ports at the local node as an integer. This limit can be + configured at startup by using command-line flag + +Q in erl(1).

process_count -

Returns the number of processes currently existing at - the local node as an integer. The same value as - length(processes()) returns, but more efficient.

+

Returns the number of processes currently existing at the + local node. The value is given as an integer. This is + the same value as returned by + length(processes()), but more efficient.

- process_limit + process_limit +

Returns the maximum number of simultaneously existing - processes at the local node as an integer. This limit - can be configured at startup by using the - +P - command line flag of - erl(1).

+ processes at the local node. The value is given as an + integer. This limit can be configured at startup by using + command-line flag +P + in erl(1).

procs

Returns a binary containing a string of process and port information formatted as in Erlang crash dumps. For more - information see the "How to interpret the Erlang crash dumps" chapter - in the ERTS User's Guide.

+ information, see Section + "How to interpret the Erlang crash dumps" + in the User's Guide.

- scheduler_bind_type + scheduler_bind_type -

Returns information on how user has requested + +

Returns information about how the user has requested schedulers to be bound or not bound.

-

NOTE: Even though user has requested - schedulers to be bound, they might have silently failed - to bind. In order to inspect actual scheduler bindings call - erlang:system_info(scheduler_bindings). -

-

For more information, see - the erl +sbt - command line argument, and - erlang:system_info(scheduler_bindings). -

-
- scheduler_bindings - -

Returns information on currently used scheduler +

Notice that even though a user has requested + schedulers to be bound, they can silently have failed + to bind. To inspect the scheduler bindings, call + erlang:system_info(scheduler_bindings).

+

For more information, see command-line argument + +sbt + in erl(1) and + erlang:system_info(scheduler_bindings).

+
+ scheduler_bindings + + +

Returns information about the currently used scheduler bindings.

A tuple of a size equal to - erlang:system_info(schedulers) is returned. The elements of the tuple are integers + erlang:system_info(schedulers) + is returned. The tuple elements are integers or the atom unbound. Logical processor identifiers are represented as integers. The Nth element of the tuple equals the current binding for the scheduler with the scheduler identifier equal to - N. E.g., if the schedulers have been bound, + N. For example, if the schedulers are bound, element(erlang:system_info(scheduler_id), - erlang:system_info(scheduler_bindings)) will return + erlang:system_info(scheduler_bindings)) returns the identifier of the logical processor that the calling - process is executing on. -

-

Note that only schedulers online can be bound to logical + process is executing on.

+

Notice that only schedulers online can be bound to logical processors.

-

For more information, see - the erl +sbt - command line argument, +

For more information, see command-line argument + +sbt + in erl(1) and erlang:system_info(schedulers_online).

- scheduler_id + scheduler_id -

Returns the scheduler id (SchedulerId) of the + +

Returns the scheduler ID (SchedulerId) of the scheduler thread that the calling process is executing - on. SchedulerId is a positive integer; where - . See also + on. SchedulerId is a positive integer, + where + . + See also erlang:system_info(schedulers).

- schedulers + schedulers +

Returns the number of scheduler threads used by the emulator. Scheduler threads online schedules Erlang processes and Erlang ports, and execute Erlang code - and Erlang linked in driver code.

+ and Erlang linked-in driver code.

The number of scheduler threads is determined at - emulator boot time and cannot be changed after - that. The amount of schedulers online can - however be changed at any time.

-

See also erlang:system_flag(schedulers_online, SchedulersOnline), + emulator boot time and cannot be changed later. + However, the number of schedulers online can + be changed at any time.

+

See also + erlang:system_flag(schedulers_online, SchedulersOnline), erlang:system_info(schedulers_online), erlang:system_info(scheduler_id), erlang:system_flag(multi_scheduling, BlockState), - erlang:system_info(multi_scheduling), and - and erlang:system_info(multi_scheduling_blockers).

+ erlang:system_info(multi_scheduling), + and + erlang:system_info(multi_scheduling_blockers).

- schedulers_online + schedulers_online -

Returns the amount of schedulers online. The scheduler - identifiers of schedulers online satisfy the following - relationship: - . -

+ +

Returns the number of schedulers online. The scheduler + identifiers of schedulers online satisfy the relationship + .

For more information, see - erlang:system_info(schedulers), + erlang:system_info(schedulers) and - erlang:system_flag(schedulers_online, SchedulersOnline). -

- + erlang:system_flag(schedulers_online, SchedulersOnline).

+
smp_support

Returns true if the emulator has been compiled - with smp support; otherwise, false.

+ with SMP support, otherwise false is returned.

start_time

The Erlang monotonic @@ -6880,7 +7319,7 @@ ok system_version

Returns a string containing version number and - some important properties such as the number of schedulers.

+ some important properties, such as the number of schedulers.

system_architecture @@ -6890,23 +7329,28 @@ ok threads

Returns true if the emulator has been compiled - with thread support; otherwise, false is - returned.

+ with thread support, otherwise false is returned.

- thread_pool_size + thread_pool_size +

Returns the number of async threads in the async thread pool used for asynchronous driver calls - (driver_async()) - as an integer.

+ (driver_async()). + The value is given as an integer.

- time_correction -

Returns a boolean value indicating whether + + time_correction + + +

Returns a boolean value indicating whether time correction is enabled or not.

- time_offset -

Returns the state of the time offset:

+ time_offset + + +

Returns the state of the time offset:

preliminary

The time offset is preliminary, and will be changed @@ -6949,8 +7393,9 @@ ok time warp mode is being used.

- tolerant_timeofday + tolerant_timeofday +

Returns whether a pre erts-7.0 backwards compatible compensation for sudden changes of system time is enabled or disabled. Such compensation is enabled when the @@ -6961,89 +7406,91 @@ ok trace_control_word -

Returns the value of the node's trace control word. - For more information see documentation of the function - get_tcw in "Match Specifications in Erlang", - ERTS User's Guide.

+

Returns the value of the node trace control word. For + more information, see function get_tcw in Section + Match Specifications in Erlang in the User's Guide.

- update_cpu_info + update_cpu_info -

The runtime system rereads the CPU information available and - updates its internally stored information about the - detected CPU - topology and the amount of logical processors + +

The runtime system rereads the CPU information available + and updates its internally stored information about the + detected + CPU topology and the number of logical processors configured, online, and - available. - If the CPU information has changed since the last time it was read, - the atom changed is returned; otherwise, the atom - unchanged is returned. If the CPU information has changed + available.

+

If the CPU information has changed since the last time + it was read, the atom changed is returned, otherwise + the atom unchanged. If the CPU information has changed, you probably want to - adjust the amount - of schedulers online. You typically want to have as - many schedulers online as - logical processors - available. -

+ adjust the + number of schedulers online. You typically want + to have as many schedulers online as + logical + processors available.

- version + version +

Returns a string containing the version number of the emulator.

wordsize -

Same as {wordsize, internal}.

+

Same as {wordsize, internal}.

{wordsize, internal}

Returns the size of Erlang term words in bytes as an - integer, i.e. on a 32-bit architecture 4 is returned, - and on a pure 64-bit architecture 8 is returned. On a + integer, that is, 4 is returned on a 32-bit architecture, + and 8 is returned on a pure 64-bit architecture. On a halfword 64-bit emulator, 4 is returned, as the Erlang - terms are stored using a virtual wordsize of half the - system's wordsize.

+ terms are stored using a virtual word size of half the + system word size.

{wordsize, external} -

Returns the true wordsize of the emulator, i.e. the size - of a pointer, in bytes as an integer. On a pure 32-bit - architecture 4 is returned, on both a halfword and pure +

Returns the true word size of the emulator, that is, + the size of a pointer. The value is given in bytes + as an integer. On a pure 32-bit architecture, 4 is + returned. On both a half word and on a pure 64-bit architecture, 8 is returned.

-

The scheduler argument has changed name to - scheduler_id. This in order to avoid mixup with - the schedulers argument. The scheduler - argument was introduced in ERTS version 5.5 and renamed - in ERTS version 5.5.1.

+

Argument scheduler has changed name to + scheduler_id to avoid mix up with argument + schedulers. Argument scheduler was + introduced in ERTS 5.5 and renamed in + ERTS 5.5.1.

+ Current system performance monitoring settings. - Current system performance monitoring settings

Returns the current system monitoring settings set by erlang:system_monitor/2 - as {MonitorPid, Options}, or undefined if there - are no settings. The order of the options may be different + as {MonitorPid, Options}, + or undefined if there + are no settings. The order of the options can be different from the one that was set.

+ Sets or clears system performance monitoring options. - Set or clear system performance monitoring options -

When called with the argument undefined, all +

When called with argument undefined, all system performance monitoring settings are cleared.

-

Calling the function with {MonitorPid, Options} as - argument, is the same as calling +

Calling the function with {MonitorPid, + Options} as argument is the same as calling erlang:system_monitor(MonitorPid, Options).

Returns the previous system monitor settings just like erlang:system_monitor/0.

@@ -7052,102 +7499,101 @@ ok + Sets system performance monitoring options. - Set system performance monitoring options -

Sets system performance monitoring options. MonitorPid - is a local pid that will receive system monitor messages, and - the second argument is a list of monitoring options:

+

Sets the system performance monitoring options. + MonitorPid is a local process identifier (pid) + receiving system monitor messages. The + second argument is a list of monitoring options:

{long_gc, Time}

If a garbage collection in the system takes at least - Time wallclock milliseconds, a message + Time wall clock milliseconds, a message {monitor, GcPid, long_gc, Info} is sent to - MonitorPid. GcPid is the pid that was - garbage collected and Info is a list of two-element - tuples describing the result of the garbage collection. - One of the tuples is {timeout, GcTime} where - GcTime is the actual time for the garbage + MonitorPid. GcPid is the pid that + was garbage collected. Info is a list of two-element + tuples describing the result of the garbage collection.

+

One of the tuples is {timeout, GcTime}, where + GcTime is the time for the garbage collection in milliseconds. The other tuples are - tagged with heap_size, heap_block_size, - stack_size, mbuf_size, old_heap_size, - and old_heap_block_size. These tuples are - explained in the documentation of the - gc_start - trace message (see - erlang:trace/3). - New tuples may be added, and the order of the tuples in - the Info list may be changed at any time without prior - notice. -

+ tagged with heap_size, heap_block_size + stack_size, mbuf_size, old_heap_size, + and old_heap_block_size. These tuples are + explained in the description of trace message + gc_start (see + erlang:trace/3). + New tuples can be added, and the order of the tuples in + the Info list can be changed at any time without + prior notice.

{long_schedule, Time} -

If a process or port in the system runs uninterrupted +

If a process or port in the system runs uninterrupted for at least Time wall clock milliseconds, a message {monitor, PidOrPort, long_schedule, Info} is sent to MonitorPid. PidOrPort is the - process or port that was running and Info is a - list of two-element tuples describing the event. In case - of a pid(), the tuples {timeout, Millis}, - {in, Location} and {out, Location} will be + process or port that was running. Info is a + list of two-element tuples describing the event.

+

If a pid(), the tuples {timeout, Millis}, + {in, Location}, and {out, Location} are present, where Location is either an MFA ({Module, Function, Arity}) describing the function where the process was scheduled in/out, or the - atom undefined. In case of a port(), the + atom undefined.

+

If a port(), the tuples {timeout, Millis} and {port_op,Op} - will be present. Op will be one of proc_sig, + are present. Op is one of proc_sig, timeout, input, output, - event or dist_cmd, depending on which - driver callback was executing. proc_sig is an - internal operation and should never appear, while the + event, or dist_cmd, depending on which + driver callback was executing.

+

proc_sig is an + internal operation and is never to appear, while the others represent the corresponding driver callbacks timeout, ready_input, ready_output, - event and finally outputv (when the port - is used by distribution). The Millis value in - the timeout tuple will tell you the actual - uninterrupted execution time of the process or port, - which will always be >= the Time value - supplied when starting the trace. New tuples may be - added to the Info list in the future, and the - order of the tuples in the list may be changed at any - time without prior notice. -

-

This can be used to detect problems with NIF's or - drivers that take too long to execute. Generally, 1 ms - is considered a good maximum time for a driver callback - or a NIF. However, a time sharing system should usually - consider everything below 100 ms as "possible" and - fairly "normal". Schedule times above that might however - indicate swapping or a NIF/driver that is - misbehaving. Misbehaving NIF's and drivers could cause - bad resource utilization and bad overall performance of - the system.

+ event, and outputv (when the port + is used by distribution). Value Millis in + the timeout tuple informs about the + uninterrupted execution time of the process or port, which + always is equal to or higher than the Time value + supplied when starting the trace. New tuples can be + added to the Info list in a future release. The + order of the tuples in the list can be changed at any + time without prior notice.

+

This can be used to detect problems with NIFs or + drivers that take too long to execute. 1 ms is + considered a good maximum time for a driver callback + or a NIF. However, a time-sharing system is usually to + consider everything below 100 ms as "possible" and + fairly "normal". However, longer schedule times can + indicate swapping or a misbehaving NIF/driver. + Misbehaving NIFs and drivers can cause bad resource + use and bad overall system performance.

{large_heap, Size}

If a garbage collection in the system results in the allocated size of a heap being at least Size words, a message {monitor, GcPid, large_heap, Info} - is sent to MonitorPid. GcPid and Info - are the same as for long_gc above, except that - the tuple tagged with timeout is not present. - Note: As of erts version 5.6 the monitor message - is sent if the sum of the sizes of all memory blocks allocated - for all heap generations is equal to or larger than Size. - Previously the monitor message was sent if the memory block - allocated for the youngest generation was equal to or larger - than Size. -

+ is sent to MonitorPid. + GcPid and Info + are the same as for long_gc earlier, except that + the tuple tagged with timeout is not present.

+

As of ERTS 5.6, the monitor message is sent + if the sum of the sizes of all memory blocks allocated + for all heap generations is equal to or higher than Size. + Previously the monitor message was sent if the memory block + allocated for the youngest generation was equal to or higher + than Size.

busy_port

If a process in the system gets suspended because it sends to a busy port, a message {monitor, SusPid, busy_port, Port} is sent to - MonitorPid. SusPid is the pid that got - suspended when sending to Port.

+ MonitorPid. SusPid is the pid + that got suspended when sending to Port.

busy_dist_port @@ -7155,8 +7601,8 @@ ok sends to a process on a remote node whose inter-node communication was handled by a busy port, a message {monitor, SusPid, busy_dist_port, Port} is sent to - MonitorPid. SusPid is the pid that got - suspended when sending through the inter-node + MonitorPid. SusPid is the pid + that got suspended when sending through the inter-node communication port Port.

@@ -7165,74 +7611,77 @@ ok

If a monitoring process gets so large that it itself starts to cause system monitor messages when garbage - collecting, the messages will enlarge the process's + collecting, the messages enlarge the process message queue and probably make the problem worse.

Keep the monitoring process neat and do not set the system monitor limits too tight.

-

Failure: badarg if MonitorPid does not exist or is not a local process.

+

Failures:

+ + badarg + If MonitorPid does not exist. + badarg + If MonitorPid is not a local process. +
+ Current system profiling settings. - Current system profiling settings

Returns the current system profiling settings set by erlang:system_profile/2 - as {ProfilerPid, Options}, or undefined if there - are no settings. The order of the options may be different + as {ProfilerPid, Options}, + or undefined if there + are no settings. The order of the options can be different from the one that was set.

+ Current system profiling settings. - Current system profiling settings

Sets system profiler options. ProfilerPid - is a local pid or port that will receive profiling messages. The - receiver is excluded from all profiling. + is a local process identifier (pid) or port receiving profiling + messages. The receiver is excluded from all profiling. The second argument is a list of profiling options:

exclusive -

- If a synchronous call to a port from a process is done, the +

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 - inactive and subsequently active when the port - callback returns. -

+ inactive, and later active when the port + callback returns.

runnable_procs -

If a process is put into or removed from the run queue a message, - {profile, Pid, State, Mfa, Ts}, is sent to - ProfilerPid. Running processes that is reinserted into the - run queue after having been preemptively scheduled out will not trigger this - message. -

+

If a process is put into or removed from the run queue, a + message, {profile, Pid, State, Mfa, Ts}, is sent to + ProfilerPid. Running processes that + are reinserted + into the run queue after having been pre-emptively + scheduled out do not trigger this message.

runnable_ports -

If a port is put into or removed from the run queue a message, - {profile, Port, State, 0, Ts}, is sent to - ProfilerPid. -

+

If a port is put into or removed from the run queue, a + message, {profile, Port, State, 0, Ts}, is sent to + ProfilerPid.

scheduler -

If a scheduler is put to sleep or awoken a message, - {profile, scheduler, Id, State, NoScheds, Ts}, is sent - to ProfilerPid. -

+

If a scheduler is put to sleep or awoken, a message, + {profile, scheduler, Id, State, NoScheds, Ts}, is + sent to ProfilerPid.

-

erlang:system_profile is considered experimental and - its behaviour may change in the future.

+

erlang:system_profile is considered experimental + and its behavior can change in a future release.

@@ -7276,11 +7725,12 @@ ok
- Encode a term to an Erlang external term format binary + Encodes a term to an Erlang external term format binary. -

Returns a binary data object which is the result of encoding - Term according to the Erlang external term format.

-

This can be used for a variety of purposes, for example +

Returns a binary data object that is the result of encoding + Term according to the Erlang external + term format.

+

This can be used for various purposes, for example, writing a term to a file in an efficient way, or sending an Erlang term to some type of communications channel not supported by distributed Erlang.

@@ -7288,67 +7738,81 @@ ok binary_to_term/1.

+ - Encode a term to en Erlang external term format binary - -

Returns a binary data object which is the result of encoding - Term according to the Erlang external term format.

-

If the option compressed is provided, the external - term format will be compressed. The compressed format is - automatically recognized by binary_to_term/1 in R7B and later.

-

It is also possible to specify a compression level by giving - the option {compressed, Level}, where Level is an - integer from 0 through 9. 0 means that no compression - will be done (it is the same as not giving any compressed option); - 1 will take the least time but may not compress as well as - the higher levels; 9 will take the most time and may produce - a smaller result. Note the "mays" in the preceding sentence; depending - on the input term, level 9 compression may or may not produce a smaller - result than level 1 compression.

-

Currently, compressed gives the same result as - {compressed, 6}.

-

The option {minor_version, Version} can be use to control - some details of the encoding. This option was - introduced in R11B-4. Currently, the allowed values for Version - are 0 and 1.

-

{minor_version, 1} is since 17.0 the default, it forces any floats in - the term to be encoded - in a more space-efficient and exact way (namely in the 64-bit IEEE format, - rather than converted to a textual representation). binary_to_term/1 - in R11B-4 and later is able decode this representation.

-

{minor_version, 0} 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 + Encodes a term to en Erlang external term format binary. + +

Returns a binary data object that is the result of encoding + Term according to the Erlang external + term format.

+

If option compressed is provided, the external term + format is compressed. The compressed format is automatically + recognized by binary_to_term/1 as from Erlang R7B.

+

A compression level can be specified by giving option + {compressed, Level}. + Level is an integer + with range 0..9, where:

+ + 0 - No compression is done (it is the same as + giving no compressed option). + 1 - Takes least time but cannot compress, + as well as the higher levels. + 9 - Takes most time and can produce a smaller + result. Notice "can" in the preceding sentence; depending + on the input term, level 9 compression either does or does + not produce a smaller result than level 1 compression. + +

compressed and {compressed, 6} give the same + result.

+

Option {minor_version, Version} + can be used to control + some encoding details. This option was introduced in OTP R11B-4. + The valid values for Version are + 0 and 1.

+

As from OTP 17.0, {minor_version, 1} is the default. It + forces any floats in the term to be encoded in a more + space-efficient and exact way (namely in the 64-bit IEEE format, + rather than converted to a textual representation).

+

As from OTP R11B-4, binary_to_term/1 can decode this + representation.

+

{minor_version, 0} means that floats are encoded + using a textual representation. This option is useful to + ensure that releases before OTP R11B-4 can decode resulting binary.

See also binary_to_term/1.

+ - Throw an exception + Throws an exception.

A non-local return from a function. If evaluated within a - catch, catch will return the value Any.

+ catch, catch returns value Any.

+

Example:

 > catch throw({hello, there}).
 {hello,there}

Failure: nocatch if not evaluated within a catch.

+ - Current time + Current time.

Returns the current time as {Hour, Minute, Second}.

-

The time zone and daylight saving time correction depend on +

The time zone and Daylight Saving Time correction depend on the underlying OS.

+

Example:

 > time().
 {9,42,44}
+ Current time offset @@ -7433,147 +7897,150 @@ timestamp() -> - Tail of a list + Tail of a list. -

Returns the tail of List, that is, the list minus - the first element.

+

Returns the tail of List, that is, + the list minus the first element, for example:

 > tl([geesties, guilies, beasties]).
 [guilies, beasties]

Allowed in guard tests.

-

Failure: badarg if List is the empty list [].

+

Failure: badarg if List + is the empty list [].

+ + Sets trace flags for a process or processes. - Set trace flags for a process or processes

Turns on (if How == true) or off (if - How == false) the trace flags in FlagList for - the process or processes represented by PidSpec.

-

PidSpec is either a pid for a local process, or one of - the following atoms:

+ How == false) the trace flags in + FlagList for + the process or processes represented by + PidSpec.

+

PidSpec is either a process identifier + (pid) for a local process, or one of the following atoms:

existing -

All processes currently existing.

+

All currently existing processes.

new -

All processes that will be created in the future.

+

All processes that are created in the future.

all

All currently existing processes and all processes that - will be created in the future.

+ are created in the future.

-

FlagList can contain any number of the following - flags (the "message tags" refers to the list of messages - following below):

+

FlagList can contain any number of the + following flags (the "message tags" refers to the list of the + following messages):

all -

Set all trace flags except {tracer, Tracer} and - cpu_timestamp that are in their nature different +

Sets all trace flags except {tracer, Tracer} and + cpu_timestamp, which are in their nature different than the others.

send -

Trace sending of messages.

-

Message tags: send, +

Traces sending of messages.

+

Message tags: send and send_to_non_existing_process.

'receive' -

Trace receiving of messages.

+

Traces receiving of messages.

Message tags: 'receive'.

procs -

Trace process related events.

+

Traces process-related events.

Message tags: spawn, exit, register, unregister, link, - unlink, getting_linked, + unlink, getting_linked, and getting_unlinked.

call -

Trace certain function calls. Specify which function +

Traces certain function calls. Specify which function calls to trace by calling erlang:trace_pattern/3.

-

Message tags: call, return_from.

+

Message tags: call and return_from.

silent -

Used in conjunction with the call trace flag. - The call, return_from and return_to - trace messages are inhibited if this flag is set, - but if there are match specs they are executed as normal.

+

Used with the call trace flag. + The call, return_from, and return_to + trace messages are inhibited if this flag is set, but they + are executed as normal if there are match specifications.

Silent mode is inhibited by executing erlang:trace(_, false, [silent|_]), - or by a match spec executing the {silent, false} - function.

+ or by a match specification executing the function + {silent, false}.

The silent trace flag facilitates setting up a trace on many or even all processes in the system. - Then the interesting trace can be activated and - deactivated using the {silent,Bool} - match spec function, giving a high degree - of control of which functions with which - arguments that triggers the trace.

-

Message tags: call, return_from, + The trace is activated and deactivated using the match + specification function {silent,Bool}, giving + a high degree of control of which functions with which + arguments that trigger the trace.

+

Message tags: call, return_from, and return_to. Or rather, the absence of.

return_to -

Used in conjunction with the call trace flag. - Trace the actual return from a traced function back to +

Used with the call trace flag. + Traces the return from a traced function back to its caller. Only works for functions traced with - the local option to + option local to erlang:trace_pattern/3.

The semantics is that a trace message is sent when a - call traced function actually returns, that is, when a - chain of tail recursive calls is ended. There will be - only one trace message sent per chain of tail recursive - calls, why the properties of tail recursiveness for + call traced function returns, that is, when a + chain of tail recursive calls ends. Only one trace + message is sent per chain of tail recursive calls, + so the properties of tail recursiveness for function calls are kept while tracing with this flag. Using call and return_to trace together makes it possible to know exactly in which function a process executes at any time.

To get trace messages containing return values from - functions, use the {return_trace} match_spec - action instead.

+ functions, use the {return_trace} match + specification action instead.

Message tags: return_to.

running -

Trace scheduling of processes.

-

Message tags: in, and out.

+

Traces scheduling of processes.

+

Message tags: in and out.

exiting -

Trace scheduling of an exiting processes.

+

Traces scheduling of exiting processes.

Message tags: in_exiting, out_exiting, and out_exited.

garbage_collection -

Trace garbage collections of processes.

-

Message tags: gc_start, gc_end.

+

Traces garbage collections of processes.

+

Message tags: gc_start and gc_end.

timestamp -

Include a time stamp in all trace messages. The time - stamp (Ts) is of the same form as returned by +

Includes a time-stamp in all trace messages. The + time-stamp (Ts) has the same form as returned by erlang:now().

cpu_timestamp

A global trace flag for the Erlang node that makes all - trace timestamps be in CPU time, not wallclock. It is - only allowed with PidSpec==all. If the host - machine operating system does not support high resolution + trace time-stamps to be in CPU time, not wall clock time. + Only allowed with PidSpec==all. If the host + machine OS does not support high-resolution CPU time measurements, trace/3 exits with badarg. Note that most operating systems do not synchronize this value across cores, so be prepared @@ -7581,38 +8048,39 @@ timestamp() -> arity -

Used in conjunction with the call trace flag. - {M, F, Arity} will be specified instead of +

Used with the call trace flag. + {M, F, Arity} is specified instead of {M, F, Args} in call trace messages.

set_on_spawn

Makes any process created by a traced process inherit - its trace flags, including the set_on_spawn flag.

+ its trace flags, including flag set_on_spawn.

set_on_first_spawn

Makes the first process created by a traced process - inherit its trace flags, excluding - the set_on_first_spawn flag.

+ inherit its trace flags, excluding flag + set_on_first_spawn.

set_on_link

Makes any process linked by a traced process inherit its - trace flags, including the set_on_link flag.

+ trace flags, including flag set_on_link.

set_on_first_link

Makes the first process linked to by a traced process - inherit its trace flags, excluding - the set_on_first_link flag.

+ inherit its trace flags, excluding flag + set_on_first_link.

{tracer, Tracer} -

Specify where to send the trace messages. Tracer - must be the pid of a local process or the port identifier +

Specifies where to send the trace messages. Tracer + must be the process identifier of a local process + or the port identifier of a local port. If this flag is not given, trace - messages will be sent to the process that called + messages are sent to the process that called erlang:trace/3.

@@ -7620,27 +8088,28 @@ timestamp() -> set_on_link is the same as having set_on_first_link alone. Likewise for set_on_spawn and set_on_first_spawn.

-

If the timestamp flag is not given, the tracing - process will receive the trace messages described below. - Pid is the pid of the traced process in which - the traced event has occurred. The third element of the tuple +

If flag timestamp is not given, the tracing + process receives the trace messages described in the + following. Pid is the process identifier of the + traced process in which + the traced event has occurred. The third tuple element is the message tag.

-

If the timestamp flag is given, the first element of - the tuple will be trace_ts instead and the timestamp +

If flag timestamp is given, the first tuple + element is trace_ts instead, and the time-stamp is added last in the tuple.

{trace, Pid, 'receive', Msg} -

When Pid receives the message Msg.

+

When Pid receives message Msg.

{trace, Pid, send, Msg, To} -

When Pid sends the message Msg to - the process To.

+

When Pid sends message Msg to + process To.

{trace, Pid, send_to_non_existing_process, Msg, To} -

When Pid sends the message Msg to +

When Pid sends message Msg to the non-existing process To.

{trace, Pid, call, {M, F, Args}} @@ -7648,7 +8117,7 @@ timestamp() ->

When Pid calls a traced function. The return values of calls are never supplied, only the call and its arguments.

-

Note that the trace flag arity can be used to +

Trace flag arity can be used to change the contents of this message, so that Arity is specified instead of Args.

@@ -7656,35 +8125,34 @@ timestamp() ->

When Pid returns to the specified function. This trace message is sent if both - the call and the return_to flags are set, + the flags call and return_to are set, and the function is set to be traced on local function calls. The message is only sent when returning - from a chain of tail recursive function calls where at + from a chain of tail recursive function calls, where at least one call generated a call trace message - (that is, the functions match specification matched and + (that is, the functions match specification matched, and {message, false} was not an action).

{trace, Pid, return_from, {M, F, Arity}, ReturnValue}

When Pid returns from the specified - function. This trace message is sent if the call - flag is set, and the function has a match specification + function. This trace message is sent if flag call + is set, and the function has a match specification with a return_trace or exception_trace action.

{trace, Pid, exception_from, {M, F, Arity}, {Class, Value}}

When Pid exits from the specified - function due to an exception. This trace message is sent - if the call flag is set, and the function has + function because of an exception. This trace message is + sent if flag call is set, and the function has a match specification with an exception_trace action.

{trace, Pid, spawn, Pid2, {M, F, Args}}

When Pid spawns a new process Pid2 with the specified function call as entry point.

-

Note that Args is supposed to be the argument - list, but may be any term in the case of an erroneous - spawn.

+

Args is supposed to be the argument list, + but can be any term if the spawn is erroneous.

{trace, Pid, exit, Reason} @@ -7714,148 +8182,159 @@ timestamp() -> {trace, Pid, unregister, RegName}

When Pid gets the name RegName unregistered. - Note that this is done automatically when a registered + This is done automatically when a registered process exits.

{trace, Pid, in, {M, F, Arity} | 0} -

When Pid is scheduled to run. The process will - run in function {M, F, Arity}. On some rare - occasions the current function cannot be determined, then - the last element Arity is 0.

+

When Pid is scheduled to run. The process + runs in function {M, F, Arity}. On some rare + occasions, the current function cannot be determined, + then the last element Arity is 0.

{trace, Pid, out, {M, F, Arity} | 0}

When Pid is scheduled out. The process was - running in function {M, F, Arity}. On some rare occasions + running in function {M, F, Arity}. On some rare occasions, the current function cannot be determined, then the last - element Arity is 0.

+ element Arity is 0.

- {trace, Pid, gc_start, Info} + {trace, Pid, gc_start, Info} +

Sent when garbage collection is about to be started. Info is a list of two-element tuples, where the first element is a key, and the second is the value. - You should not depend on the tuples have any defined - order. Currently, the following keys are defined:

+ Do not depend on any order of the tuples. + The following keys are defined:

heap_size The size of the used part of the heap. heap_block_size The size of the memory block used for storing - the heap and the stack. + the heap and the stack.
old_heap_size The size of the used part of the old heap. old_heap_block_size The size of the memory block used for storing - the old heap. + the old heap.
stack_size - The actual size of the stack. + The size of the stack. recent_size The size of the data that survived the previous garbage - collection. + collection.
mbuf_size The combined size of message buffers associated with - the process. - + the process. bin_vheap_size - The total size of unique off-heap binaries referenced from the process heap. + The total size of unique off-heap binaries referenced + from the process heap. bin_vheap_block_size - The total size of binaries, in words, allowed in the virtual - heap in the process before doing a garbage collection. + The total size of binaries allowed in the virtual + heap in the process before doing a garbage collection. bin_old_vheap_size - The total size of unique off-heap binaries referenced from the process old heap. + The total size of unique off-heap binaries referenced + from the process old heap. bin_vheap_block_size - The total size of binaries, in words, allowed in the virtual - old heap in the process before doing a garbage collection. - - + The total size of binaries allowed in the virtual + old heap in the process before doing a garbage collection.

All sizes are in words.

{trace, Pid, gc_end, Info}

Sent when garbage collection is finished. Info - contains the same kind of list as in the gc_start - message, but the sizes reflect the new sizes after + contains the same kind of list as in message gc_start, + but the sizes reflect the new sizes after garbage collection.

-

If the tracing process dies, the flags will be silently +

If the tracing process dies, the flags are silently removed.

-

Only one process can trace a particular process. For this - reason, attempts to trace an already traced process will fail.

+

Only one process can trace a particular process. Therefore, + attempts to trace an already traced process fail.

Returns: A number indicating the number of processes that - matched PidSpec. If PidSpec is a pid, - the return value will be 1. If PidSpec is - all or existing the return value will be + matched PidSpec. + If PidSpec is a process + identifier, the return value is 1. + If PidSpec + is all or existing, the return value is the number of processes running, excluding tracer processes. - If PidSpec is new, the return value will be + If PidSpec is new, the return value is 0.

-

Failure: If specified arguments are not supported. For - example cpu_timestamp is not supported on all - platforms.

+

Failure: badarg if the specified arguments are + not supported. For example, cpu_timestamp is not + supported on all platforms.

+ - Notification when trace has been delivered + Notification when trace has been delivered.

The delivery of trace messages is dislocated on the time-line - compared to other events in the system. If you know that the - Tracee has passed some specific point in its execution, + compared to other events in the system. If you know that + Tracee 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 erlang:trace_delivered(Tracee). A + corresponding to events up to this point have reached the + tracer, use erlang:trace_delivered(Tracee). A {trace_delivered, Tracee, Ref} message is sent to the caller of erlang:trace_delivered(Tracee) when it - is guaranteed that all trace messages have been delivered to - the tracer up to the point that the Tracee had reached + is guaranteed that all trace messages are delivered to + the tracer up to the point that Tracee reached at the time of the call to erlang:trace_delivered(Tracee).

-

Note that the trace_delivered message does not - imply that trace messages have been delivered; instead, it implies - that all trace messages that should be delivered have - been delivered. It is not an error if Tracee isn't, and - hasn't been traced by someone, but if this is the case, - no trace messages will have been delivered when the +

Notice that message trace_delivered does not + imply that trace messages have been delivered. + Instead it implies that all trace messages that + are to be delivered have been delivered. + It is not an error if Tracee is not, and + has not been traced by someone, but if this is the case, + no trace messages have been delivered when the trace_delivered message arrives.

-

Note that Tracee has to refer to a process currently, +

Notice that that Tracee must refer + to a process currently, or previously existing on the same node as the caller of erlang:trace_delivered(Tracee) resides on. - The special Tracee atom all denotes all processes + The special Tracee atom all + denotes all processes that currently are traced in the node.

-

An example: Process A is Tracee, port B is +

Example: Process A is Tracee, + port B is tracer, and process C is the port owner of B. C wants to close B when A exits. C - can ensure that the trace isn't truncated by calling - erlang:trace_delivered(A) when A exits and wait - for the {trace_delivered, A, Ref} message before closing - B.

-

Failure: badarg if Tracee does not refer to a + can ensure that the trace is not truncated by calling + erlang:trace_delivered(A) when A exits and waits + for message {trace_delivered, A, Ref} + before closing B.

+

Failure: badarg if Tracee + does not refer to a process (dead or alive) on the same node as the caller of erlang:trace_delivered(Tracee) resides on.

+ + Traces information about a process or function. - Trace information about a process or function

Returns trace information about a process or function.

-

To get information about a process, PidOrFunc should - be a pid or the atom new. The atom new means - that the default trace state for processes to be created will - be returned. Item must have one of the following - values:

+

To get information about a process, + PidOrFunc is to + be a process identifier (pid) or the atom new. + The atom new means that the default trace state for + processes to be created is returned.

+

The following Items are valid:

flags -

Return a list of atoms indicating what kind of traces is - enabled for the process. The list will be empty if no +

Returns a list of atoms indicating what kind of traces is + enabled for the process. The list is empty if no traces are enabled, and one or more of the followings atoms if traces are enabled: send, 'receive', set_on_spawn, call, @@ -7866,129 +8345,132 @@ timestamp() -> tracer -

Return the identifier for process or port tracing this +

Returns the identifier for process or port tracing this process. If this process is not being traced, the return - value will be [].

+ value is [].

-

To get information about a function, PidOrFunc should - be a three-element tuple: {Module, Function, Arity} or +

To get information about a function, PidOrFunc is to + be the three-element tuple {Module, Function, Arity} or the atom on_load. No wildcards are allowed. Returns - undefined if the function does not exist or - false if the function is not traced at all. Item - must have one of the following values:

+ undefined if the function does not exist, or + false if the function is not traced.

+

The following Items are valid::

traced -

Return global if this function is traced on +

Returns global if this function is traced on global function calls, local if this function is - traced on local function calls (i.e local and global - function calls), and false if neither local nor - global function calls are traced.

+ traced on local function calls (that is, local and global + function calls), and false if local or + global function calls are not traced.

match_spec -

Return the match specification for this function, if it +

Returns the match specification for this function, if it has one. If the function is locally or globally traced but has no match specification defined, the returned value is [].

meta -

Return the meta trace tracer process or port for this - function, if it has one. If the function is not meta - traced the returned value is false, and if - the function is meta traced but has once detected that - the tracer proc is invalid, the returned value is [].

+

Returns the meta-trace tracer process or port for this + function, if it has one. If the function is not + meta-traced, the returned value is false. If + the function is meta-traced but has once detected that + the tracer process is invalid, the returned value is [].

meta_match_spec -

Return the meta trace match specification for this - function, if it has one. If the function is meta traced +

Returns the meta-trace match specification for this + function, if it has one. If the function is meta-traced but has no match specification defined, the returned value is [].

call_count -

Return the call count value for this function or +

Returns the call count value for this function or true for the pseudo function on_load if call - count tracing is active. Return false otherwise. + count tracing is active. Otherwise false is returned. See also erlang:trace_pattern/3.

call_time -

Return the call time values for this function or +

Returns the call time values for this function or true for the pseudo function on_load if call - time tracing is active. Returns false otherwise. + time tracing is active. Otherwise false is returned. The call time values returned, [{Pid, Count, S, Us}], - is a list of each process that has executed the function and its specific counters. - See also + is a list of each process that executed the function + and its specific counters. See also erlang:trace_pattern/3.

all -

Return a list containing the {Item, Value} tuples - for all other items, or return false if no tracing +

Returns a list containing the + {Item, Value} tuples + for all other items, or returns false if no tracing is active for this function.

-

The actual return value will be {Item, Value}, where - Value is the requested information as described above. +

The return value is {Item, Value}, where + Value is the requested information as described earlier. If a pid for a dead process was given, or the name of a - non-existing function, Value will be undefined.

-

If PidOrFunc is the on_load, the information + non-existing function, Value is undefined.

+

If PidOrFunc is on_load, the information returned refers to the default value for code that will be loaded.

+ + Sets trace patterns for global call tracing. - Set trace patterns for global call tracing

The same as erlang:trace_pattern(MFA, MatchSpec, []), retained for backward compatibility.

+ + Sets trace patterns for tracing of function calls. - Set trace patterns for tracing of function calls -

This BIF is used to enable or disable call tracing for - exported functions. It must be combined with +

Enables or disables call tracing for + exported functions. Must be combined with erlang:trace/3 to set the call trace flag for one or more processes.

-

Conceptually, call tracing works like this: Inside - the Erlang virtual machine there is a set of processes to be - traced and a set of functions to be traced. Tracing will be +

Conceptually, call tracing works as follows. Inside + the Erlang Virtual Machine, a set of processes and + a set of functions are to be traced. Tracing is enabled on the intersection of the set. That is, if a process included in the traced process set calls a function included - in the traced function set, the trace action will be taken. - Otherwise, nothing will happen.

-

Use - erlang:trace/3 to - add or remove one or more processes to the set of traced - processes. Use erlang:trace_pattern/2 to add or remove - exported functions to the set of traced functions.

-

The erlang:trace_pattern/3 BIF can also add match + in the traced function set, the trace action is taken. + Otherwise, nothing happens.

+

To add or remove one or more processes to the set of traced + processes, use + erlang:trace/3.

+

To add or remove exported functions to the set of traced + functions, use erlang:trace_pattern/2.

+

The BIF erlang:trace_pattern/3 can also add match specifications to an exported function. A match specification - comprises a pattern that the arguments to the function must - match, a guard expression which must evaluate to true + comprises a pattern that the function arguments must + match, a guard expression that must evaluate to true, 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.

-

The MFA argument should be a tuple like - {Module, Function, Arity} or the atom on_load - (described below). It can be the module, function, and arity - for an exported function (or a BIF in any module). - The '_' atom can be used to mean any of that kind. + fails, the action is not executed.

+

Argument MFA is to be a tuple, such as + {Module, Function, Arity}, or the atom on_load + (described in the following). It can be the module, function, + and arity for an exported function (or a BIF in any module). + The atom '_' can be used to mean any of that kinds. Wildcards can be used in any of the following ways:

{Module,Function,'_'} @@ -8006,197 +8488,213 @@ timestamp() ->

Other combinations, such as {Module,'_',Arity}, are - not allowed. Local functions will match wildcards only if - the local option is in the FlagList.

-

If the MFA argument is the atom on_load, - the match specification and flag list will be used on all + not allowed. Local functions match wildcards only if + option local is in FlagList.

+

If argument MFA is the atom on_load, + the match specification and flag list are used on all modules that are newly loaded.

-

The MatchSpec argument can take any of the following - forms:

+

Argument MatchSpec can take the + following forms:

false -

Disable tracing for the matching function(s). Any match - specification will be removed.

+

Disables tracing for the matching function or functions. + Any match specification is removed.

true -

Enable tracing for the matching function(s).

+

Enables tracing for the matching function or functions.

MatchSpecList

A list of match specifications. An empty list is - equivalent to true. See the ERTS User's Guide - for a description of match specifications.

+ equivalent to true. For a description of match + specifications, see the User's Guide.

restart -

For the FlagList option call_count and call_time: - restart the existing counters. The behaviour is undefined +

For the FlagList options call_count + and call_time: restarts + the existing counters. The behavior is undefined for other FlagList options.

pause -

For the FlagList option call_count and call_time: pause - the existing counters. The behaviour is undefined for - other FlagList options.

+

For the FlagList options + call_count and call_time: pauses + the existing counters. The behavior is undefined for + other FlagList options.

-

The FlagList parameter is a list of options. - The following options are allowed:

+

Parameter FlagList is a list of options. + The following are the valid options:

global -

Turn on or off call tracing for global function calls +

Turns on or off call tracing for global function calls (that is, calls specifying the module explicitly). Only - exported functions will match and only global calls will + exported functions match and only global calls generate trace messages. This is the default.

local -

Turn on or off call tracing for all types of function - calls. Trace messages will be sent whenever any of +

Turns on or off call tracing for all types of function + calls. Trace messages are sent whenever any of the specified functions are called, regardless of how they - are called. If the return_to flag is set for - the process, a return_to message will also be sent + are called. If flag return_to is set for + the process, a return_to message is also sent when this function returns to its caller.

meta | {meta, Pid} -

Turn on or off meta tracing for all types of function - calls. Trace messages will be sent to the tracer process +

Turns on or off meta-tracing for all types of function + calls. Trace messages are sent to the tracer process or port Pid whenever any of the specified functions are called, regardless of how they are called. - If no Pid is specified, self() is used as a - default tracer process.

-

Meta tracing traces all processes and does not care + If no Pid is specified, + self() is used as a default tracer process.

+

Meta-tracing traces all processes and does not care about the process trace flags set by trace/3, the trace flags are instead fixed to [call, timestamp].

-

The match spec function {return_trace} works with - meta trace and send its trace message to the same tracer - process.

+

The match specification function {return_trace} + works with meta-trace and sends its trace message to the + same tracer process.

call_count

Starts (MatchSpec == true) or stops - (MatchSpec == false) call count tracing for all - types of function calls. For every function a counter is + (MatchSpec == false) + 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.

If call count tracing is started while already running, - the count is restarted from zero. Running counters can be - paused with MatchSpec == pause. Paused and running - counters can be restarted from zero with + the count is restarted from zero. To pause running + counters, use MatchSpec == pause. + Paused and running counters can be restarted from zero with MatchSpec == restart.

-

The counter value can be read with +

To read the counter value, use erlang:trace_info/2.

call_time

Starts (MatchSpec == true) or stops - (MatchSpec == false) 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. + (MatchSpec == false) 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 microseconds. The counters are stored for each call traced process.

If call time tracing is started while already running, - the count and time is restarted from zero. Running counters can be - paused with MatchSpec == pause. Paused and running - counters can be restarted from zero with + the count and time is restarted from zero. To pause + running counters, use MatchSpec == pause. + Paused and running counters can be restarted from zero with MatchSpec == restart.

-

The counter value can be read with +

To read the counter value, use erlang:trace_info/2.

-
-

The global and local options are mutually - exclusive and global is the default (if no options are - specified). The call_count and meta options - perform a kind of local tracing, and can also not be combined - with global. A function can be either globally or +

The options global and local are mutually + exclusive, and global is the default (if no options are + specified). The options call_count and meta + perform a kind of local tracing, and cannot be combined + with global. A function can be globally or locally traced. If global tracing is specified for a - specified set of functions; local, meta, call time and call count - tracing for the matching set of local functions will be - disabled, and vice versa.

+ set of functions, then local, meta, call time, and call count + tracing for the matching set of local functions is + disabled, and conversely.

When disabling trace, the option must match the type of trace - that is set on the function, so that local tracing must be - disabled with the local option and global tracing with - the global option (or no option at all), and so forth.

-

There is no way to directly change part of a match - specification list. If a function has a match specification, - you can replace it with a completely new one. If you need to - change an existing match specification, use the + set on the function. That is, local tracing must be + disabled with option local and global tracing with + option global (or no option), and so forth.

+

Part of a match specification list cannot be changed directly. + If a function has a match specification, it can be replaced + with a new one. To change an existing match specification, + use the BIF erlang:trace_info/2 - BIF to retrieve the existing match specification.

-

Returns the number of exported functions that matched - the MFA argument. This will be zero if none matched at - all.

+ to retrieve the existing match specification.

+

Returns the number of exported functions matching + argument MFA. This is zero if none matched.

+ - Return an integer by the truncating a number + Returns an integer by truncating a number -

Returns an integer by the truncating Number.

+

Returns an integer by truncating Number, + for example:

 > trunc(5.5).
 5

Allowed in guard tests.

+ - Return the size of a tuple + Returns the size of a tuple. -

Returns an integer which is the number of elements in Tuple.

+

Returns an integer that is the number of elements in + Tuple, for example:

 > tuple_size({morni, mulle, bwange}).
 3

Allowed in guard tests.

+ - Convert a tuple to a list + Converts a tuple to a list. -

Returns a list which corresponds to Tuple. - Tuple may contain any Erlang terms.

+

Returns a list corresponding to Tuple. + Tuple can contain any Erlang terms.

+

Example:

 > tuple_to_list({share, {'Ericsson_B', 163}}).
 [share,{'Ericsson_B',163}]
+ - Current date and time according to Universal Time Coordinated (UTC) + Current date and time according to Universal Time Coordinated (UTC).

Returns the current date and time according to Universal - Time Coordinated (UTC), also called GMT, in the form + Time Coordinated (UTC) in the form {{Year, Month, Day}, {Hour, Minute, Second}} if - supported by the underlying operating system. If not, - erlang:universaltime() is equivalent to + supported by the underlying OS. + Otherwise erlang:universaltime() is equivalent to erlang:localtime().

+

Example:

 > erlang:universaltime().
 {{1996,11,6},{14,18,43}}
+ - Convert from Universal Time Coordinated (UTC) to local date and time + Converts from Universal Time Coordinated (UTC) to local date and time.

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 + local date and time in the form + {{Year, Month, Day}, {Hour, Minute, Second}} if + supported by the underlying OS. + Otherwise no conversion is done, and Universaltime is returned.

+

Example:

 > erlang:universaltime_to_localtime({{1996,11,6},{14,18,43}}).
 {{1996,11,7},{15,18,43}}
-

Failure: badarg if Universaltime does not denote - a valid date and time.

+

Failure: badarg if Universaltime denotes + an invalid date and time.

+ Get a unique integer value @@ -8293,25 +8791,30 @@ timestamp() -> - Remove a link, if there is one, to another process or port + Removes a link, if there is one, to another process or port.

Removes the link, if there is one, between the calling - process and the process or port referred to by Id.

+ process and the process or port referred to by + Id.

Returns true and does not fail, even if there is no - link to Id, or if Id does not exist.

-

Once unlink(Id) has returned it is guaranteed that + link to Id, or if Id + does not exist.

+

Once unlink(Id) has returned, + it is guaranteed that the link between the caller and the entity referred to by - Id has no effect on the caller in the future (unless - the link is setup again). If caller is trapping exits, an - {'EXIT', Id, _} message due to the link might have - been placed in the caller's message queue prior to the call, - though. Note, the {'EXIT', Id, _} message can be the - result of the link, but can also be the result of Id - calling exit/2. Therefore, it may be - appropriate to cleanup the message queue when trapping exits - after the call to unlink(Id), as follow:

+ Id has no effect on the caller + in the future (unless + the link is setup again). If the caller is trapping exits, an + {'EXIT', Id, _} + message is received, as the link can have + been placed in the caller's message queue before the call.

+

Notice that the {'EXIT', Id, _} + message can be the + result of the link, but can also be the result of Id + calling exit/2. Therefore, it can be + appropriate to clean up the message queue when trapping exits + after the call to unlink(Id), as follows:

- unlink(Id), receive {'EXIT', Id, _} -> @@ -8320,23 +8823,25 @@ timestamp() -> true end -

Prior to OTP release R11B (erts version 5.5) unlink/1 - behaved completely asynchronous, i.e., the link was active +

Before OTP R11B (ERTS 5.5) unlink/1 + behaved asynchronous, that is, the link was active until the "unlink signal" reached the linked entity. This - had one undesirable effect, though. You could never know when + had an undesirable effect, as you could never know when you were guaranteed not to be effected by the link.

-

Current behavior can be viewed as two combined operations: +

The current behavior can be viewed as two combined operations: asynchronously send an "unlink signal" to the linked entity and ignore any future results of the link.

+ - Remove the registered name for a process (or port) + Removes the registered name for a process (or port). -

Removes the registered name RegName, associated with a - pid or a port identifier.

+

Removes the registered name RegName + associated with a + process identifier or a port identifier, for example:

 > unregister(db).
 true
@@ -8345,31 +8850,34 @@ true name.

+ - Get the pid (or port) with a given registered name + Gets the pid (or port) with a given registered name. -

Returns the pid or port identifier with the registered name - RegName. Returns undefined if the name is not - registered.

+

Returns the process identifier or port identifier with + the registered name RegName. Returns undefined + if the name is not registered.

+

Example:

 > whereis(db).
 <0.43.0>
+ - Let other processes get a chance to execute + Lets other processes get a chance to execute. -

Voluntarily let other processes (if any) get a chance to +

Voluntarily lets other processes (if any) get a chance to execute. Using erlang:yield() is similar to receive after 1 -> ok end, except that yield() is faster.

There is seldom or never any need to use this BIF, - especially in the SMP-emulator as other processes will have a - chance to run in another scheduler thread anyway. - Using this BIF without a thorough grasp of how the scheduler - works may cause performance degradation.

+ especially in the SMP emulator, as other processes have a + chance to run in another scheduler thread anyway. + Using this BIF without a thorough grasp of how the scheduler + works can cause performance degradation.

-- cgit v1.2.3 From 07c1d0d975fbecf295a3c9f04f7b09e7a8b6ff99 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 23 Sep 2015 20:09:49 +0200 Subject: erts: Review module erlang docs --- erts/doc/src/erlang.xml | 455 +++++++++++++++++++++--------------------- erts/preloaded/src/erlang.erl | 2 +- 2 files changed, 223 insertions(+), 234 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 8b20a5c12f..a51774b9f0 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -251,10 +251,7 @@

Example:

 > apply(lists, reverse, [[a, b, c]]).
-[c,b,a]
-

apply evaluates BIFs by using - the module name erlang.

-
+[c,b,a]
 > apply(erlang, atom_to_list, ['Erlang']).
 "Erlang"

If the number of arguments are known at compile time, @@ -282,16 +279,16 @@ in the text representation. If Encoding is utf8 or unicode, the characters are encoded using UTF-8 - (that is, characters from 16#80 through 0xFF are + (that is, characters from 128 through 255 are encoded in two bytes).

atom_to_binary(Atom, latin1) never fails because the text representation of an atom can only - contain characters from 0 through 16#FF. In a future release, + contain characters from 0 through 255. In a future release, the text representation of atoms can be allowed to contain any Unicode character and atom_to_binary(Atom, latin1) will then fail if the text representation for Atom contains a Unicode - character greater than 16#FF.

+ character greater than 255.

Example:

 > atom_to_binary('Erlang', latin1).
@@ -358,9 +355,9 @@
         If Encoding
         is utf8 or unicode, the binary must contain
         valid UTF-8 sequences. Only Unicode characters up
-        to 0xFF are allowed.

+ to 255 are allowed.

binary_to_atom(Binary, utf8) fails if - the binary contains Unicode characters greater than 16#FF. + the binary contains Unicode characters greater than 255. In a future release, such Unicode characters can be allowed and binary_to_atom(Binary, utf8) does then not fail. For more information on Unicode support in atoms, see the @@ -450,11 +447,11 @@ position Stop in Binary. The positions in the binary are numbered starting from 1.

-

The indexing style of using one-based indices for - binaries is deprecated for this function. New code is to - use the functions in module binary in STDLIB - instead. They therefore - use the same (zero-based) style of indexing.

+

The one-based indexing for binaries used by + this function is deprecated. New code is to use + binary:bin_to_list/3 + in STDLIB instead. All functions in module + binary consistently use zero-based indexing.

@@ -698,7 +695,7 @@

Checks if the node local process identified by Pid executes old code for Module.

-

The available Options are as follows:

+

The available Options are as follows:

{allow_gc, boolean()} @@ -770,7 +767,7 @@ - Convert time unit of a time value + Converts time unit of a time value.

Converts the Time value of time unit FromUnit to the corresponding @@ -878,7 +875,7 @@ line -

A packet is a line-terminated with newline. The +

A packet is a line terminated with newline. The newline character is included in the returned packet unless the line was truncated according to option line_length.

@@ -1809,7 +1806,7 @@ os_prompt%
Hash function (deprecated).

Returns a hash value for Term within the range - 1..Range. The range is 1..2^27-1.

+ 1..Range. The maximum range is 1..2^27-1.

This BIF is deprecated, as the hash value can differ on different architectures. The hash values for integer @@ -2034,7 +2031,7 @@ os_prompt%

This BIF is useful for builders of cross-reference tools.

Returns true if Module:Function/Arity - ia a BIF implemented in C, otherwise false.

+ is a BIF implemented in C, otherwise false.

@@ -2366,8 +2363,8 @@ os_prompt%

Failure: badarg if String contains a bad representation of a process identifier.

-

This BIF is intended for debugging and for use in the - Erlang OS. It is not to be used in application programs.

+

This BIF is intended for debugging and is not to be used + in application programs.

@@ -3161,9 +3158,9 @@ os_prompt% - At which node a pid, port, or reference is located. + At which node a pid, port, or reference originates. -

Returns the node where Arg is located. +

Returns the node where Arg originates. Arg can be a process identifier, a reference, or a port. If the local node is not @@ -3208,17 +3205,19 @@ os_prompt% known -

Nodes that are known to this node, for example, connected - and previously connected.

+

Nodes that are known to this node. That is, connected + nodes and nodes referred to by process identifiers, port + identifiers and references located on this node. + The set of known nodes is garbage collected. Notice that + this garbage collection can be delayed. For more + information, see + delayed_node_table_gc. +

Some equalities: [node()] = nodes(this), nodes(connected) = nodes([visible, hidden]), and nodes() = nodes(visible).

-

If the local node is not alive, - nodes(this) == nodes(known) == [nonode@nohost]. For - any other Arg the empty list - [] is returned.

@@ -3260,7 +3259,7 @@ os_prompt% given in cd, env, args, and arg0 are subject to Unicode filename translation if the system is running in Unicode filename mode. To avoid - translation or force, that is, UTF-8, supply the executable + translation or to force, for example UTF-8, supply the executable and/or arguments as a binary in the correct encoding. For details, see the module file, the function @@ -3585,9 +3584,10 @@ os_prompt%

Portable hash function that gives 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 is 1..2^32. The function returns a hash value for + The function returns a hash value for Term within the range - 1..Range.

+ 1..Range. The maximum value for + Range is 2^32.

This BIF can be used instead of the old deprecated BIF erlang:hash/2, as it calculates better hashes for all data types, but consider using phash2/1,2 instead.

@@ -3604,9 +3604,10 @@ os_prompt%

Portable hash function that gives the same hash for the same Erlang term regardless of machine architecture and ERTS version (the BIF was introduced in ERTS 5.2). - Range is 1..2^32. The function returns a hash value for - Term within the range - 0..Range-1. When without argument + The function returns a hash value for + Term within the range + 0..Range-1. The maximum value for + Range is 2^32. When without argument Range, a value in the range 0..2^27-1 is returned.

This BIF is always to be used for hashing terms. It @@ -3625,8 +3626,8 @@ os_prompt%

Returns a string corresponding to the text representation of Pid.

-

This BIF is intended for debugging and for use in the - Erlang OS. It is not to be used in application programs.

+

This BIF is intended for debugging and is not to be used + in application programs.

@@ -3641,19 +3642,18 @@ os_prompt% not reply with {Port, closed}. Any process can close a port with port_close/1, not only the port owner (the connected process). If the calling process is linked to - a port identified by Port, an exit signal is sent - because that link will be received before the return from - port_close/1

-

For comparison: Port ! {self(), close} fails with - badarg if Port cannot be sent to (that is, - Port refers not to a port and not to a process). If - Port is a closed port, nothing happens. - If Port + the port identified by Port, the exit + signal from the port is guaranteed to be delivered before + port_close/1 returns.

+

For comparison: Port ! {self(), close} + only fails with badarg if Port does + not refer to a port or a process. If Port + is a closed port, nothing happens. If Port is an open port and the calling process is the port owner, - the port replies with {Port, closed} when all buffers - have been flushed and the port really closes. If - the calling process is not the port owner, the - port owner fails with badsig.

+ the port replies with {Port, closed} when all buffers + have been flushed and the port really closes. If the calling + process is not the port owner, the port owner fails + with badsig.

Notice that any process can close a port using Port ! {PortOwner, close} as if it itself was the port owner, but the reply always goes to the port owner.

@@ -3665,10 +3665,10 @@ os_prompt% behavior.

Failure: badarg if Port is not an identifier of an open port, or the registered name of an open port. - If the calling process was linked to the port, the exit - identified by Port, the exit signal is sent because - this link was delivered to the calling process before this - exception occurs.

+ If the calling process was previously linked to the closed + port, identified by Port, the exit + signal from the port is guaranteed to be delivered before + this badarg exception occurs.

@@ -3683,10 +3683,10 @@ os_prompt% can send data to a port with port_command/2, not only the port owner (the connected process).

For comparison: Port ! {PortOwner, {command, Data}} - fails with badarg if Port - cannot be sent to (that - is, Port refers not to a port and not to a process). - If Port is a closed port, the data message disappears + only fails with badarg if Port + does not refer to a port or a process. If + Port is a closed port, the data message + disappears without a sound. If Port is open and the calling process is not the port owner, the port owner fails with badsig. The port owner fails with badsig @@ -3707,10 +3707,11 @@ os_prompt% badarg If Port is not an identifier of an open - port, or the registered name of an open port. If the calling - process was linked to the port, the exit signal is sent - because this link was delivered to the calling process - before this exception occurs. + port, or the registered name of an open port. If the + calling process was previously linked to the closed port, + identified by Port, the exit signal + from the port is guaranteed to be delivered before this + badarg exception occurs. badarg @@ -3754,10 +3755,11 @@ os_prompt% badarg If Port is not an identifier of an open - port, or the registered name of an open port. If the calling - process was linked to the port, the exit signal is sent - because this link was delivered to the calling process - before this exception occurs. + port, or the registered name of an open port. If the + calling process was previously linked to the closed port, + identified by Port, the exit signal + from the port is guaranteed to be delivered before this + badarg exception occurs. badarg @@ -3805,9 +3807,9 @@ os_prompt% set the port owner to be any process with port_connect/2.

For comparison: - Port ! {self(), {connect, Pid}} fails - with badarg if Port cannot be sent to (that is, - Port refers not to a port and not to a process). If + Port ! {self(), {connect, Pid}} + only fails with badarg if Port + does not refer to a port or a process. If Port is a closed port, nothing happens. If Port is an open port and the calling process is the port owner, @@ -3835,9 +3837,10 @@ os_prompt% If Port is not an identifier of an open port, or the registered name of an open port. If the calling - process was linked to the port, the exit signal is sent - because this link was delivered to the calling process - before this exception occurs. + process was previously linked to the closed port, + identified by Port, the exit signal + from the port is guaranteed to be delivered before this + badarg exception occurs. badarg If process identified by Pid is not an existing @@ -3906,9 +3909,10 @@ os_prompt% If Port is not an identifier of an open port, or the registered name of an open port. If the calling - process was linked to the port, the exit signal is sent - because this link was delivered to the calling process - before this exception occurs. + process was previously linked to the closed port, + identified by Port, the exit signal + from the port is guaranteed to be delivered before this + badarg exception occurs. badarg @@ -3937,11 +3941,10 @@ os_prompt% Port, or undefined if the port is not open. The order of the tuples is undefined, and all the tuples are not mandatory. - If undefined is returned and the calling process - was linked to a previously open port identified by - Port, an exit signal is sent because this link - was received by the process before the return from - port_info/1.

+ If the port is closed and the calling process + was previously linked to the port, the exit signal from the + port is guaranteed to be delivered before port_info/1 + returns undefined.

The result contains information about the following Items:

@@ -3968,11 +3971,10 @@ os_prompt%

Pid is the process identifier of the process connected to the port.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent because this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -3985,11 +3987,10 @@ os_prompt%

Index is the internal index of the port. This index can be used to separate ports.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent because this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -4002,11 +4003,10 @@ os_prompt%

Bytes is the total number of bytes read from the port.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent because this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -4019,11 +4019,10 @@ os_prompt%

Pids is a list of the process identifiers of the processes that the port is linked to.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent because this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -4042,11 +4041,10 @@ os_prompt%

Notice that these results are highly implementation-specific and can change in a future release.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent because this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -4061,11 +4059,10 @@ os_prompt% port itself can have allocated memory that is not included in Bytes.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent because this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -4078,11 +4075,10 @@ os_prompt%

Monitors represent processes that this port monitors.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent because this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -4095,11 +4091,10 @@ os_prompt%

Name is the command name set by open_port/2.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent because this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -4115,11 +4110,10 @@ os_prompt% Command}, Options). If the port is not the result of spawning an OS process, the value is undefined.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent because this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -4135,11 +4129,10 @@ os_prompt% port_command/3, or Port ! {Owner, {command, Data}.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent before this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -4164,11 +4157,10 @@ os_prompt% of bytes queued by the port using the ERTS driver queue implementation.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent because this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -4181,11 +4173,10 @@ os_prompt%

RegisteredName is the registered name of the port. If the port has no registered name, [] is returned.

If the port identified by Port is not open, - undefined is returned. If undefined is returned and - the calling process was linked to a previously open port identified - by Port, an exit signal is sent because this link - was received by the process before the return from - port_info/2.

+ undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

Failure: badarg if Port is not a local port identifier, or an atom.

@@ -4198,15 +4189,15 @@ os_prompt%

Returns a string corresponding to the text representation of the port identifier Port.

-

This BIF is intended for debugging and for use in the - Erlang OS. It is not to be used in application programs.

+

This BIF is intended for debugging. It is not to be used + in application programs.

- Lists all open ports. + Lists all existing ports.

Returns a list of port identifiers corresponding to all the ports existing on the local node.

@@ -4502,8 +4493,8 @@ os_prompt%

Returns information about the process identified by Pid, as specified by - Item or ItemList, - or undefined if the process is not alive.

+ Item or ItemList. + Returns undefined if the process is not alive.

If the process is alive and a single Item is given, the returned value is the corresponding InfoTuple, unless Item =:= registered_name @@ -4774,8 +4765,7 @@ os_prompt% badarg If Pid is not a local process. badarg - If Item is an invalid - Item. + If Item is an invalid item. @@ -4847,8 +4837,7 @@ os_prompt% exception of given class, reason, and call stack backtrace (stacktrace).

-

This BIF is intended for debugging and for use in - the Erlang OS. Avoid to use it in applications, +

This BIF is intended for debugging. Avoid to use it in applications, unless you really know what you are doing.

Class is error, exit, or @@ -4963,8 +4952,8 @@ os_prompt%

Returns a string corresponding to the text representation of Ref.

-

This BIF is intended for debugging and for use in the - Erlang OS. It is not to be used in application programs.

+

This BIF is intended for debugging and is not to be used + in application programs.

@@ -5208,7 +5197,7 @@ true Msg, [nosuspend | Options]), but with a Boolean return value.

This function behaves like - erlang:send_nosuspend/2), + erlang:send_nosuspend/2, but takes a third parameter, a list of options. The only option is noconnect, which makes the function return false if @@ -5267,13 +5256,23 @@ true Size of a tuple or binary. -

Returns an integer that is the size of argument - Item, which must be a tuple or a binary, - for example:

+

Returns the number of elements in a tuple or the number of + bytes in a binary or bitstring, for example:

 > size({morni, mulle, bwange}).
-3
+3 +> size(<<11, 22, 33>>). +3 + +

For bitstrings the number of whole bytes is returned. That is, if the number of bits + in the bitstring is not divisible by 8, the resulting + number of bytes is rounded down.

Allowed in guard tests.

+

See also + tuple_size/1, + byte_size/1 + and + bit_size/1.

@@ -5307,9 +5306,7 @@ true

Returns the process identifier of a new process started by the application of Module:Function - to Args. - The new created process is placed in the system scheduler - queue and will be run some time later.

+ to Args.

error_handler:undefined_function(Module, Function, Args) is evaluated by the new process if @@ -5319,8 +5316,8 @@ true can be redefined (see process_flag/2). If error_handler is undefined, or the user has - redefined the default error_handler, its replacement is - undefined, and a failure with reason undef occurs.

+ redefined the default error_handler and its replacement is + undefined, a failure with reason undef occurs.

Example:

 > spawn(speed, regulator, [high_speed, thin_cut]).
@@ -5364,10 +5361,9 @@ true
list [] on Node. A link is created between the calling process and the new process, atomically. If Node does not exist, - a useless pid is - returned (and, because of the link, an exit signal with exit - reason noconnection is received). Otherwise works - like spawn/3.

+ a useless pid is returned and an exit signal with + reason noconnection is sent to the calling + process. Otherwise works like spawn/3.

@@ -5376,7 +5372,7 @@ true Creates and links to a new process with a function as entry point.

Returns the process identifier of a new process started by - the applicatio of Module:Function + the application of Module:Function to Args. A link is created between the calling process and the new process, atomically. Otherwise works like @@ -5394,10 +5390,9 @@ true to Args on Node. A link is created between the calling process and the new process, atomically. If Node does - not exist, a useless pid - is returned (and, because of the link, an exit signal with exit - reason noconnection is received). Otherwise works - like spawn/3.

+ not exist, a useless pid is returned and an exit signal with + reason noconnection is sent to the calling + process. Otherwise works like spawn/3.

@@ -5761,7 +5756,7 @@ true Information about runtime.

Returns information about runtime, in milliseconds.

-

The runtime is the sum of the runtime for all threads +

This is the sum of the runtime for all threads in the Erlang runtime system and can therefore be greater than the wall clock time.

Example:

@@ -5787,7 +5782,7 @@ true activation. The time unit is undefined and can be subject to change between releases, OSs, and system restarts. scheduler_wall_time is only to be used to - calculate relative values for scheduler-use. + calculate relative values for scheduler-utilization. ActiveTime can never exceed TotalTime.

The definition of a busy scheduler is when it is not idle @@ -5808,14 +5803,14 @@ true is turned off.

The list of scheduler information is unsorted and can appear in different order between calls.

-

Using scheduler_wall_time to calculate scheduler-use:

+

Using scheduler_wall_time to calculate scheduler-utilization:

 > erlang:system_flag(scheduler_wall_time, true).
 false
 > Ts0 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.
 ok

Some time later the user takes another snapshot and calculates - scheduler-use per scheduler, for example:

+ scheduler-utilization per scheduler, for example:

 > Ts1 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.
 ok
@@ -5829,7 +5824,7 @@ ok
  {6,0.9739235846420741},
  {7,0.973237033077876},
  {8,0.9741297293248656}]
-

Using the same snapshots to calculate a total scheduler-use:

+

Using the same snapshots to calculate a total scheduler-utilization:

 > {A, T} = lists:foldl(fun({{_, A0, T0}, {_, A1, T1}}, {Ai,Ti}) ->
 	{Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0,Ts1)), A/T.
@@ -5961,9 +5956,7 @@ ok
         

Suspends the process identified by Suspendee. The same as calling erlang:suspend_process(Suspendee, - []). - For more information, see - erlang:suspend_process/2.

+ []).

This BIF is intended for debugging only.

@@ -6163,7 +6156,7 @@ ok

Controls if and how schedulers are bound to logical processors.

-

When erlang:system_flag(scheduler_bind_type, How +

When erlang:system_flag(scheduler_bind_type, How) is called, an asynchronous signal is sent to all schedulers online, causing them to try to bind or unbind as requested.

If a scheduler fails to bind, this is often silently @@ -6283,9 +6276,9 @@ ok Sets the number of schedulers online. Range is .

Returns the old value of the flag.

-

The emulator was built with support for - dirty schedulers. - Changing the number of schedulers online can also change the +

If the emulator was built with support for + dirty schedulers, + changing the number of schedulers online can also change the number of dirty CPU schedulers online. For example, if 12 schedulers and 6 dirty CPU schedulers are online, and system_flag/2 is used to set the number of schedulers @@ -6563,7 +6556,7 @@ ok

Returns the automatically detected - CpuTopology. The + CpuTopologyy. The emulator detects the CPU topology on some newer Linux, Solaris, FreeBSD, and Windows systems. On Windows system with more than 32 logical processors, @@ -7030,10 +7023,10 @@ ok

Returns a list of Pids when multi-scheduling is blocked, otherwise the empty list is - returned. The Pids in the list are - Pids of the processes currently - blocking multi-scheduling. A Pid is - present only once in the list, even if the corresponding + returned. The Pids in the list + represent all the processes currently + blocking multi-scheduling. A Pid occurs + only once in the list, even if the corresponding process has blocked multiple times.

See also erlang:system_flag(multi_scheduling, BlockState), @@ -7177,7 +7170,7 @@ ok time unit.

- port_parallelism + port_parallelism

Returns the default port parallelism scheduling hint used. @@ -7569,7 +7562,7 @@ ok fairly "normal". However, longer schedule times can indicate swapping or a misbehaving NIF/driver. Misbehaving NIFs and drivers can cause bad resource - use and bad overall system performance.

+ utilization and bad overall system performance.

{large_heap, Size} @@ -7756,15 +7749,15 @@ ok 0 - No compression is done (it is the same as giving no compressed option). - 1 - Takes least time but cannot compress, + 1 - Takes least time but may not compress as well as the higher levels. - 9 - Takes most time and can produce a smaller - result. Notice "can" in the preceding sentence; depending + 6 - Default level when option compressed + is provided. + 9 - Takes most time and tries to produce a smaller + result. Notice "tries" in the preceding sentence; depending on the input term, level 9 compression either does or does not produce a smaller result than level 1 compression. -

compressed and {compressed, 6} give the same - result.

Option {minor_version, Version} can be used to control some encoding details. This option was introduced in OTP R11B-4. @@ -7938,8 +7931,8 @@ timestamp() ->

FlagList can contain any number of the - following flags (the "message tags" refers to the list of the - following messages):

+ following flags (the "message tags" refers to the list of + trace messages):

all @@ -7985,7 +7978,7 @@ timestamp() -> {silent, false}.

The silent trace flag facilitates setting up a trace on many or even all processes in the system. - The trace is activated and deactivated using the match + The trace can then be activated and deactivated using the match specification function {silent,Bool}, giving a high degree of control of which functions with which arguments that trigger the trace.

@@ -8088,15 +8081,14 @@ timestamp() -> set_on_link is the same as having set_on_first_link alone. Likewise for set_on_spawn and set_on_first_spawn.

-

If flag timestamp is not given, the tracing - process receives the trace messages described in the - following. Pid is the process identifier of the - traced process in which - the traced event has occurred. The third tuple element - is the message tag.

+

The tracing process receives the trace messages described + in the following list. Pid is the process identifier of the + traced process in which the traced event has occurred. The + third tuple element is the message tag.

If flag timestamp is given, the first tuple element is trace_ts instead, and the time-stamp - is added last in the tuple.

+ is added last in the message tuple.

+ {trace, Pid, 'receive', Msg} @@ -8301,13 +8293,12 @@ timestamp() -> denotes all processes that currently are traced in the node.

Example: Process A is Tracee, - port B is - tracer, and process C is the port owner of B. - C wants to close B when A exits. C - can ensure that the trace is not truncated by calling - erlang:trace_delivered(A) when A exits and waits - for message {trace_delivered, A, Ref} - before closing B.

+ port B is tracer, and process C is the port + owner of B. C wants to close B when + A exits. To ensure that the trace is not truncated, + C can call erlang:trace_delivered(A), when + A exits, and wait for message {trace_delivered, A, + Ref} before closing B.

Failure: badarg if Tracee does not refer to a process (dead or alive) on the same node as the caller of @@ -8317,7 +8308,7 @@ timestamp() -> - Traces information about a process or function. + Trace information about a process or function. @@ -8444,23 +8435,21 @@ timestamp() ->

Enables or disables call tracing for - exported functions. Must be combined with + one or more functions. Must be combined with erlang:trace/3 to set the call trace flag for one or more processes.

Conceptually, call tracing works as follows. Inside the Erlang Virtual Machine, a set of processes and - a set of functions are to be traced. Tracing is - enabled on the intersection of the set. That is, if a process - included in the traced process set calls a function included - in the traced function set, the trace action is taken. + a set of functions are to be traced. If a traced process + calls a traced function, the trace action is taken. Otherwise, nothing happens.

To add or remove one or more processes to the set of traced processes, use erlang:trace/3.

-

To add or remove exported functions to the set of traced - functions, use erlang:trace_pattern/2.

+

To add or remove functions to the set of traced + functions, use erlang:trace_pattern/3.

The BIF erlang:trace_pattern/3 can also add match - specifications to an exported function. A match specification + specifications to a function. A match specification comprises a pattern that the function arguments must match, a guard expression that must evaluate to true, and an action to be performed. The default action is to send a @@ -8469,22 +8458,22 @@ timestamp() ->

Argument MFA is to be a tuple, such as {Module, Function, Arity}, or the atom on_load (described in the following). It can be the module, function, - and arity for an exported function (or a BIF in any module). - The atom '_' can be used to mean any of that kinds. - Wildcards can be used in any of the following ways:

+ and arity for a function (or a BIF in any module). + The atom '_' can be used as a wildcard in any of the + following ways:

{Module,Function,'_'} -

All exported functions of any arity named Function +

All functions of any arity named Function in module Module.

{Module,'_','_'} -

All exported functions in module Module.

+

All functions in module Module.

{'_','_','_'} -

All exported functions in all loaded modules.

+

All functions in all loaded modules.

Other combinations, such as {Module,'_',Arity}, are @@ -8498,12 +8487,12 @@ timestamp() -> false -

Disables tracing for the matching function or functions. +

Disables tracing for the matching functions. Any match specification is removed.

true -

Enables tracing for the matching function or functions.

+

Enables tracing for the matching functions.

MatchSpecList @@ -8534,7 +8523,7 @@ timestamp() ->

Turns on or off call tracing for global function calls (that is, calls specifying the module explicitly). Only exported functions match and only global calls - generate trace messages. This is the default.

+ generate trace messages. This is the default.

local @@ -8615,7 +8604,7 @@ timestamp() -> use the BIF erlang:trace_info/2 to retrieve the existing match specification.

-

Returns the number of exported functions matching +

Returns the number of functions matching argument MFA. This is zero if none matched.

@@ -8791,7 +8780,7 @@ timestamp() -> - Removes a link, if there is one, to another process or port. + Removes a link to another process or port.

Removes the link, if there is one, between the calling process and the process or port referred to by @@ -8805,9 +8794,9 @@ timestamp() -> Id has no effect on the caller in the future (unless the link is setup again). If the caller is trapping exits, an - {'EXIT', Id, _} - message is received, as the link can have - been placed in the caller's message queue before the call.

+ {'EXIT', Id, _} message from the link + can have been placed in the caller's message queue before + the call.

Notice that the {'EXIT', Id, _} message can be the result of the link, but can also be the result of Id diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 291356c7b1..4e55f711b2 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2423,7 +2423,7 @@ tuple_to_list(_Tuple) -> MinBinVHeapSize :: pos_integer()}; (modified_timing_level) -> integer() | undefined; (multi_scheduling) -> disabled | blocked | enabled; - (multi_scheduling_blockers) -> [PID :: pid()]; + (multi_scheduling_blockers) -> [Pid :: pid()]; (nif_version) -> string(); (otp_release) -> string(); (os_monotonic_time_source) -> [{atom(),term()}]; -- cgit v1.2.3 From db5f5eeaa8ffab49758beb95552c1cf14b49a55d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 1 Oct 2015 18:57:21 +0200 Subject: erts: Review newer additions to erlang.xml Trying to adopt same style as done by xsipewe in e17e236cd1661bc for later additions. --- erts/doc/src/erlang.xml | 274 ++++++++++++++++++++++++------------------------ 1 file changed, 136 insertions(+), 138 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index a51774b9f0..221869799d 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -66,7 +66,7 @@ -

Currently supported time unit representations:

+

Supported time unit representations:

PartsPerSecond :: integer() >= 1

Time unit expressed in parts per second. That is, @@ -93,11 +93,11 @@ used by the Erlang runtime system.

The native time unit is determined at - runtime system start, and will remain the same until + runtime system start, and remains the same until the runtime system terminates. If a runtime system is stopped and then started again (even on the same machine), the native time unit of the new - runtime system instance may differ from the + runtime system instance can differ from the native time unit of the old runtime system instance.

@@ -106,8 +106,7 @@ seconds, native). The result equals the number of whole native time units per second. In case the number of native time units per second does - not add up to a whole number, the result will be - rounded downwards.

+ not add up to a whole number, the result is rounded downwards.

The value of the native time unit gives @@ -121,7 +120,7 @@ but it gives absolutely no information at all about the accuracy of time values. The resolution of the native time - unit and the resolution of time values may differ + unit and the resolution of time values can differ significantly.

@@ -578,7 +577,7 @@ TimerRef identifies the timer, and was returned by the BIF that created the timer.

-

Currently available Options:

+

Available Options:

{async, Async} @@ -587,7 +586,7 @@ defaults to false which will cause the cancellation to be performed synchronously. When Async is set to true, the cancel - operation will be performed asynchronously. That is, + operation is performed asynchronously. That is, erlang:cancel_timer() will send an asynchronous request for cancellation to the timer service that manages the timer, and then return ok. @@ -598,17 +597,17 @@

Request information about the Result of the cancellation. Info defaults to true - which means that the Result will - be given. When Info is set to false, no + which means the Result is + given. When Info is set to false, no information about the result of the cancellation - will be given. When the operation is performed

+ is given. When the operation is performed

synchronously

- If Info is true, the Result will + If Info is true, the Result is returned by erlang:cancel_timer(); otherwise, - ok will be returned. + ok is returned.

asynchronously @@ -616,10 +615,10 @@

If Info is true, a message on the form {cancel_timer, TimerRef, - Result} will be sent to the + Result} is sent to the caller of erlang:cancel_timer() when the cancellation operation has been performed; otherwise, - no message will be sent. + no message is sent.

@@ -628,30 +627,30 @@

More Options may be added in the future.

+

If Result is an integer, it represents + the time in milli-seconds left until the canceled timer would + have expired.

- When the Result equals false, a + If Result is false, a timer corresponding to TimerRef could not be found. This can be either because the timer had expired, already had been canceled, or because TimerRef - never has corresponded to a timer. If the timer has expired, - the timeout message has been sent, but it does not tell you - whether or not it has arrived at its destination yet. When the - Result is an integer, it represents the - time in milli-seconds left until the timer would have expired. + never corresponded to a timer. Even if the timer had expired, + it does not tell you whether or not the timeout message has + arrived at its destination yet.

The timer service that manages the timer may be co-located with another scheduler than the scheduler that the calling process is executing on. If this is the case, communication - with the timer service will take much longer time than if it + with the timer service takes much longer time than if it is located locally. If the calling process is in critical path, and can do other things while waiting for the result of this operation, or is not interested in the result of - the operation, you want to use the {async, true} - option. If using the {async, false} option, the calling - process will be blocked until the operation has been - performed. + the operation, you want to use option {async, true}. + If using option {async, false}, the calling + process blocks until the operation has been performed.

See also @@ -2537,7 +2536,7 @@ os_prompt%

Returns a unique reference. -

Return a unique +

Returns a unique reference. The reference is unique among connected nodes.

Known issue: When a node is restarted multiple @@ -2863,7 +2862,7 @@ os_prompt% following format if the monitored state is changed:

{Tag, MonitorRef, Type, Object, Info}

The monitor request is an asynchronous signal. That is, it - takes time before the signal reach its destination.

+ takes time before the signal reaches its destination.

Valid Types:

process @@ -2976,8 +2975,8 @@ os_prompt% single time warp mode is used. When a change from preliminary to final time offset is made, the monitor will be triggered once - regardless of whether the time offset value was changed due to - the finalization or not.

+ regardless of whether the time offset value was actually changed + or not.

If the runtime system is in multi @@ -3077,7 +3076,7 @@ os_prompt% - Current Erlang monotonic time + Current Erlang monotonic time.

Returns the current Erlang @@ -3090,7 +3089,7 @@ os_prompt% monotonically increasing time, but not a strictly monotonically increasing time. That is, consecutive calls to - erlang:monotonic_time/0 may produce the same result.

+ erlang:monotonic_time/0 can produce the same result.

Different runtime system instances will use different unspecified points in time as base for their Erlang monotonic clocks. @@ -3098,9 +3097,9 @@ os_prompt% different runtime system instances. Different runtime system instances may also place this unspecified point in time different relative runtime system start. It may be placed in the future (time at start - will be a negative value), the past (time at start will be a - positive value), or the runtime system start (time at start will - be zero). The monotonic time as of runtime system start can be + is a negative value), the past (time at start is a + positive value), or the runtime system start (time at start is + zero). The monotonic time at runtime system start can be retrieved by calling erlang:system_info(start_time).

@@ -4881,7 +4880,7 @@ os_prompt% TimerRef identifies the timer, and was returned by the BIF that created the timer.

-

Currently available Options:

+

Available Options:

{async, Async} @@ -4889,12 +4888,12 @@ os_prompt% Asynchronous request for state information. Async defaults to false which will cause the operation to be performed synchronously. In this case, the Result - will be returned by erlang:read_timer(). When - Async is set to true, erlang:read_timer() - will send an asynchronous request for the state information - to the timer service that manages the timer, and then return + is returned by erlang:read_timer(). When + Async is true, erlang:read_timer() + sends an asynchronous request for the state information + to the timer service that manages the timer, and then returns ok. A message on the format {read_timer, - TimerRef, Result} will be + TimerRef, Result} is sent to the caller of erlang:read_timer() when the operation has been processed.

@@ -4904,26 +4903,27 @@ os_prompt% More Options may be added in the future.

- When the Result equals false, a + If Result is an integer, it represents the + time in milli-seconds left until the timer expires.

+

+ If Result is false, a timer corresponding to TimerRef could not - be found. This can be either because the timer had expired, - had been canceled, or because TimerRef - never has corresponded to a timer. If the timer has expired, - the timeout message has been sent, but it does not tell you - whether or not it has arrived at its destination yet. When the - Result is an integer, it represents the - time in milli-seconds left until the timer expires. + be found. This can be because the timer had expired, + it had been canceled, or because TimerRef + never has corresponded to a timer. Even if the timer has expired, + it does not tell you whether or not the timeout message has + arrived at its destination yet.

The timer service that manages the timer may be co-located with another scheduler than the scheduler that the calling process is executing on. If this is the case, communication - with the timer service will take much longer time than if it + with the timer service takes much longer time than if it is located locally. If the calling process is in critical path, and can do other things while waiting for the result - of this operation you want to use the {async, true} - option. If using the {async, false} option, the calling + of this operation, you want to use option {async, true}. + If using option {async, false}, the calling process will be blocked until the operation has been performed.

@@ -5119,10 +5119,9 @@ true

Starts a timer. When the timer expires, the message - Msg will be sent to the process + Msg is sent to the process identified by Dest. Appart from - the format of the message sent to - Dest when the timer expires + the format of the timeout message, erlang:send_after/4 works exactly as erlang:start_timer/4.

@@ -5609,24 +5608,27 @@ true

Starts a timer. When the timer expires, the message {timeout, TimerRef, Msg} - will be sent to the process identified by + is sent to the process identified by Dest.

-

Currently available Options:

+

Available Options:

- {abs, Abs} + {abs, false}

- Absolute Time value. Abs - defaults to false which means that the - Time value will be interpreted - as a time in milli-seconds relative current + This is the default. It means the + Time value is interpreted + as a time in milli-seconds relative current Erlang - monotonic time. When Abs is set to - true, the Time value will - be interpreted as an absolute Erlang monotonic time of - milli-seconds - time unit. + monotonic time. +

+
+ {abs, true} + +

+ Absolute Time value. The + Time value is interpreted as an + absolute Erlang monotonic time in milli-seconds.

@@ -5634,7 +5636,7 @@ true More Options may be added in the future.

- The absolute point in time that the timer is set to expire on + The absolute point in time, the timer is set to expire on, has to be in the interval [erlang:system_info(start_time), erlang:system_info(end_time)]. @@ -5646,7 +5648,7 @@ true be a pid() of a process created on the current runtime system instance. This process may or may not have terminated. If Dest is an - atom(), it will be interpreted as the name of a + atom(), it is interpreted as the name of a locally registered process. The process referred to by the name is looked up at the time of timer expiration. No error is given if the name does not refer to a process. @@ -5654,7 +5656,7 @@ true

If Dest is a pid(), the timer is automatically canceled if the process referred to by the - pid() is not alive, or when the process exits. This + pid() is not alive, or if the process exits. This feature was introduced in ERTS version 5.4.11. Notice that timers are not automatically canceled when Dest is an atom(). @@ -5990,7 +5992,7 @@ ok +sct in erl(1).

When this argument is removed, a final CPU topology - to use will be determined at emulator boot time.

+ to use is determined at emulator boot time.

Sets the user-defined CpuTopology. The user-defined @@ -6152,7 +6154,7 @@ ok argument, use command-line argument +sbt in erl(1). When this argument is removed, a final scheduler bind - type to use will be determined at emulator boot time.

+ type to use is determined at emulator boot time.

Controls if and how schedulers are bound to logical processors.

@@ -6313,26 +6315,24 @@ ok

Finalizes the time offset - when the single - time warp mode is being used. If another time warp mode than - the "single time warp mode" is used, the time offset state will be left - unchanged.

-

Returns the old state identifier. That is, if:

+ when single + time warp mode is used. If another time warp mode + is used, the time offset state is left unchanged.

+

Returns the old state identifier. That is:

-

preliminary is returned, finalization was +

If preliminary is returned, finalization was performed and the time offset is now final.

-

final is returned, the time offset was - already in the final state. This either due to another +

If final is returned, the time offset was + already in the final state. This either because another erlang:system_flag(time_offset, finalize) call, or - due to the - no - time warp mode being used.

+ because no + time warp mode is used.

-

volatile is returned, the time offset - cannot be finalized due to the +

If volatile is returned, the time offset + cannot be finalized because multi - time warp mode being used.

+ time warp mode is used.

@@ -6704,11 +6704,11 @@ ok delayed_node_table_gc -

Returns the amount of time in seconds that garbage collection - of an entry in a node table will be delayed. This limit can be set - on startup by passing the - +zdntgc command line - flag to erl. For more information see the documentation of the +

Returns the amount of time in seconds garbage collection + of an entry in a node table is delayed. This limit can be set + on startup by passing the command line flag + +zdntgc + to erl. For more information see the documentation of the command line flag.

dirty_cpu_schedulers @@ -6854,9 +6854,9 @@ ok eager_check_io

- Returns the value of the erl - +secio command line - flag which is either true or false. See the + Returns the value of the erl command line flag + +secio + which is either true or false. See the documentation of the command line flag for information about the different values.

@@ -7036,8 +7036,9 @@ ok
nif_version -

Returns a string containing the erlang NIF version - used by the runtime system. It will be on the form "<major ver>.<minor ver>".

+

Returns a string containing the version of the Erlang NIF interface + used by the runtime system. It is on the form + "<major ver>.<minor ver>".

otp_release @@ -7058,11 +7059,11 @@ ok

Returns a list containing information about the source of OS monotonic time that is used by the runtime system.

-

In case [] is returned, no OS monotonic time is +

If [] is returned, no OS monotonic time is available. The list contains two-tuples with Keys as first element, and Values as second element. The - order if these tuples is undefined. Currently the following - tuples may be part of the list, but more tuples may be + order of these tuples is undefined. The following + tuples can be part of the list, but more tuples can be introduced in the future:

{function, Function} @@ -7081,18 +7082,17 @@ ok resolution of current OS monotonic time source as parts per second. If no resolution information can be retreived - from the OS, OsMonotonicTimeResolution will be + from the OS, OsMonotonicTimeResolution is set to the resolution of the time unit of Functions return value. That is, the actual - resolution may be lower than + resolution can be lower than OsMonotonicTimeResolution. Also note that the resolution does not say anything about the accuracy, - and that the + and whether the precision - might not align with the resolution. You do, - however, know that the precision won't be - better than + do align with the resolution. You do, + however, know that the precision is not better than OsMonotonicTimeResolution.

{extended, Extended} @@ -7124,8 +7124,8 @@ ok system time that is used by the runtime system.

The list contains two-tuples with Keys as first element, and Values as second element. The - order if these tuples is undefined. Currently the following - tuples may be part of the list, but more tuples may be + order if these tuples is undefined. The following + tuples can be part of the list, but more tuples can be introduced in the future:

{function, Function} @@ -7143,18 +7143,17 @@ ok resolution of current OS system time source as parts per second. If no resolution information can be retreived - from the OS, OsSystemTimeResolution will be + from the OS, OsSystemTimeResolution is set to the resolution of the time unit of Functions return value. That is, the actual resolution may be lower than OsSystemTimeResolution. Also note that the resolution does not say anything about the accuracy, - and that the + and whether the precision - might not align with the resolution. You do, - however, know that the precision won't be - better than + do align with the resolution. You do, + however, know that the precision is not better than OsSystemTimeResolution.

{parallel, Parallel} @@ -7353,19 +7352,18 @@ ok time warp mode.

final -

The time offset is final. This - either due to the use of the +

The time offset is final. This either because no - time warp mode, or due to the time offset having - been finalized when using the + time warp mode is used, or because the time + offset have been finalized when single - time warp mode.

+ time warp mode is used.

volatile -

The time offset is volatile. That is, it may - change at any time. This due to the +

The time offset is volatile. That is, it can + change at any time. This is because multi - time warp mode being used.

+ time warp mode is used.

time_warp_mode @@ -7375,15 +7373,15 @@ ok no_time_warp

The no - time warp mode is being used.

+ time warp mode is used.

single_time_warp

The single - time warp mode is being used.

+ time warp mode is used.

multi_time_warp

The multi - time warp mode is being used.

+ time warp mode is used.

tolerant_timeofday @@ -7862,8 +7860,8 @@ ok

Returns current Erlang system time on the format {MegaSecs, Secs, MicroSecs}. This format is - the same that os:timestamp/0 - and the now deprecated erlang:now/0 + the same as os:timestamp/0 + and the deprecated erlang:now/0 uses. The reason for the existence of erlang:timestamp() is purely to simplify usage for existing code that assumes this timestamp format. Current Erlang system time can more efficiently be retrieved in @@ -7877,7 +7875,7 @@ timestamp() -> Secs = ErlangSystemTime div 1000000 - MegaSecs*1000000, MicroSecs = ErlangSystemTime rem 1000000, {MegaSecs, Secs, MicroSecs}. -

It however use a native implementation which does +

The BIF uses a native implementation which does not build garbage on the heap and with slightly better performance.

@@ -8035,7 +8033,7 @@ timestamp() -> Only allowed with PidSpec==all. If the host machine OS does not support high-resolution CPU time measurements, trace/3 exits with - badarg. Note that most operating systems do + badarg. Notice that most OS do not synchronize this value across cores, so be prepared that time might seem to go backwards when using this option.

@@ -8708,17 +8706,17 @@ timestamp() -> Each integer value can of course be constructed by other means.

-

By default, i.e. when [] is passed as +

By default, when [] is passed as ModifierList, both negative and - positive integers will be returned. This is order - to be able to utilize the range of integers that do - not need to be heap allocated as much as possible. + positive integers can be returned. This in order + to utilize the range of integers that do + not need heap memory allocation as much as possible. By default the returned integers are also only - guaranteed to be unique, i.e., any integer returned - may be either smaller, or larger than previously + guaranteed to be unique, that is, any returned integer + can be smaller or larger than previously returned integers.

-

Currently valid Modifiers:

+

Valid Modifiers:

positive @@ -8736,7 +8734,7 @@ timestamp() -> returned will always be larger than previously returned integers on the current runtime system instance.

-

These values can be used when ordering events +

These values can be used to determine order between events on the runtime system instance. That is, if both X = erlang:unique_integer([monotonic]) and Y = erlang:unique_integer([monotonic]) are @@ -8746,15 +8744,15 @@ timestamp() -> before Y.

Strictly monotonically increasing values are inherently quite expensive to generate and scales - poorly. This since the values needs to be - synchronized. That is, do not pass the monotonic + poorly. This is because the values need to be + synchronized between cpu cores. That is, do not pass the monotonic modifier unless you really need strictly monotonically increasing values.

-

All currently valid Modifiers +

All valid Modifiers can be combined. Repeated (valid) Modifiers in the ModifierList are ignored.

-- cgit v1.2.3 From 5e229f3d7591b0df7d6d475065e1dc2d5273a148 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 16 Sep 2015 15:28:46 +0200 Subject: erts: Fix resurrection of carriers from dc_list Problem #1: Seems the dc_list check did end up as dead code by mistake. Solution: goto check_dc_list Problem #2: crr->cpool.max_size was set to zero for all carriers in dc_list, which meant no carriers were ever resurrected by cpool_fetch. Solution: Do not use callback 'largest_fblk_in_mbc' to set max_size as it will always return 0 (due to problem #3). Problem #3: Resurrected carriers were broken as their one free block was not linked. Solution: Link free block when fetching carrier from dc_list. --- erts/emulator/beam/erl_alloc_util.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 236ee35d18..3861196cfc 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -3149,7 +3149,7 @@ cpool_fetch(Allctr_t *allctr, UWord size) cpool_entrance = sentinel; cpdp = cpool_aint2cpd(cpool_read(&cpool_entrance->prev)); if (cpdp == sentinel) - return NULL; + goto check_dc_list; } has_passed_sentinel = 0; @@ -3160,18 +3160,18 @@ cpool_fetch(Allctr_t *allctr, UWord size) if (cpool_entrance == sentinel) { cpdp = cpool_aint2cpd(cpool_read(&cpdp->prev)); if (cpdp == sentinel) - return NULL; + break; } i = 0; /* Last one to inspect */ } else if (cpdp == sentinel) { if (has_passed_sentinel) { /* We been here before. cpool_entrance must have been removed */ - return NULL; + break; } cpdp = cpool_aint2cpd(cpool_read(&cpdp->prev)); if (cpdp == sentinel) - return NULL; + break; has_passed_sentinel = 1; } crr = (Carrier_t *)(((char *)cpdp) - offsetof(Carrier_t, cpool)); @@ -3195,10 +3195,12 @@ cpool_fetch(Allctr_t *allctr, UWord size) return NULL; } +check_dc_list: /* Last; check our own pending dealloc carrier list... */ crr = allctr->cpool.dc_list.last; while (crr) { if (erts_atomic_read_nob(&crr->cpool.max_size) >= size) { + Block_t* blk; unlink_carrier(&allctr->cpool.dc_list, crr); #ifdef ERTS_ALC_CPOOL_DEBUG ERTS_ALC_CPOOL_ASSERT(erts_smp_atomic_xchg_nob(&crr->allctr, @@ -3207,6 +3209,9 @@ cpool_fetch(Allctr_t *allctr, UWord size) #else erts_smp_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr)); #endif + blk = MBC_TO_FIRST_BLK(allctr, crr); + ASSERT(FBLK_TO_MBC(blk) == crr); + allctr->link_free_block(allctr, blk); return crr; } crr = crr->prev; @@ -3279,7 +3284,6 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr) orig_allctr = crr->cpool.orig_allctr; if (allctr != orig_allctr) { - Block_t *blk = MBC_TO_FIRST_BLK(allctr, crr); int cinit = orig_allctr->dd.ix - allctr->dd.ix; /* @@ -3296,6 +3300,7 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr) * since the block is an mbc block that is free and last * in the carrier. */ + blk = MBC_TO_FIRST_BLK(allctr, crr); ERTS_ALC_CPOOL_ASSERT(IS_FREE_LAST_MBC_BLK(blk)); ERTS_ALC_CPOOL_ASSERT(IS_MBC_FIRST_ABLK(allctr, blk)); @@ -3319,7 +3324,9 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr) return; } - max_size = (erts_aint_t) allctr->largest_fblk_in_mbc(allctr, crr); + blk = MBC_TO_FIRST_BLK(allctr, crr); + ASSERT(IS_FREE_LAST_MBC_BLK(blk)); + max_size = (erts_aint_t) MBC_FBLK_SZ(blk); erts_atomic_set_nob(&crr->cpool.max_size, max_size); crr->next = NULL; -- cgit v1.2.3 From 933bb51d40bcd665602fe4ece951b3111c301e74 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 22 Sep 2015 14:01:54 +0200 Subject: erts: Fix confusion of callbacks destroying_mbc() vs remove_mbc() Problem #1 Goodfit was crippled by the fact that destroying_mbc() was called _before_ the carriers was unlinked from mbc_list. Problem #2 destroying_mbc() was called for carriers that later could be resurrected from dc_list without a matching call to creating_mbc(). This was mostly a practical problem for the new test case alloc_SUITE:migration that use the callbacks to create/destroy a mutex. Solution: destroying_mbc() is now only called just before a carrier is destroyed (deallocated or put in mseg cache). remove_mbc() is called both (like before) when inserted into cpool but now also when last block is freed and mbc is scheduled for destruction but may later be resurrected from dc_list. --- erts/emulator/beam/erl_alloc_util.c | 27 ++++++++++++++++++++------- erts/emulator/beam/erl_alloc_util.h | 2 +- erts/emulator/beam/erl_ao_firstfit_alloc.c | 22 +++++++++++++++++----- 3 files changed, 38 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 3861196cfc..173cb091b6 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -1437,6 +1437,16 @@ erts_alcu_fix_alloc_shrink(Allctr_t *allctr, erts_aint32_t flgs) static void dealloc_carrier(Allctr_t *allctr, Carrier_t *crr, int superaligned); +static ERTS_INLINE void +dealloc_mbc(Allctr_t *allctr, Carrier_t *crr) +{ + ASSERT(IS_MB_CARRIER(crr)); + if (allctr->destroying_mbc) + allctr->destroying_mbc(allctr, crr); + + dealloc_carrier(allctr, crr, 1); +} + #ifdef ERTS_SMP static ERTS_INLINE Allctr_t* @@ -3242,7 +3252,7 @@ check_pending_dealloc_carrier(Allctr_t *allctr, dcrr = crr; crr = crr->next; - dealloc_carrier(allctr, dcrr, 1); + dealloc_mbc(allctr, dcrr); i++; } while (crr && i < ERTS_ALC_MAX_DEALLOC_CARRIER); @@ -3273,11 +3283,14 @@ static void schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr) { Allctr_t *orig_allctr; + Block_t *blk; int check_pending_dealloc; erts_aint_t max_size; + ASSERT(IS_MB_CARRIER(crr)); + if (!ERTS_ALC_IS_CPOOL_ENABLED(allctr)) { - dealloc_carrier(allctr, crr, 1); + dealloc_mbc(allctr, crr); return; } @@ -3320,7 +3333,7 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr) if (crr->cpool.thr_prgr == ERTS_THR_PRGR_INVALID || erts_thr_progress_has_reached(crr->cpool.thr_prgr)) { - dealloc_carrier(allctr, crr, 1); + dealloc_mbc(allctr, crr); return; } @@ -3901,9 +3914,6 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk, Carrier_t **busy_pcrr_pp) } #endif - if (allctr->destroying_mbc) - (*allctr->destroying_mbc)(allctr, crr); - #ifdef ERTS_SMP if (busy_pcrr_pp && *busy_pcrr_pp) { ERTS_ALC_CPOOL_ASSERT(*busy_pcrr_pp == crr); @@ -3927,12 +3937,15 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk, Carrier_t **busy_pcrr_pp) else #endif STAT_SYS_ALLOC_MBC_FREE(allctr, crr_sz); + + if (allctr->remove_mbc) + allctr->remove_mbc(allctr, crr); } #ifdef ERTS_SMP schedule_dealloc_carrier(allctr, crr); #else - dealloc_carrier(allctr, crr, 1); + dealloc_mbc(allctr, crr); #endif } } diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index df1f0aa65a..f4a2ae7ff3 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -277,7 +277,7 @@ typedef struct ErtsDoubleLink_t_ { typedef struct { erts_atomic_t next; erts_atomic_t prev; - Allctr_t *orig_allctr; + Allctr_t *orig_allctr; /* read-only while carrier is alive */ ErtsThrPrgrVal thr_prgr; erts_atomic_t max_size; UWord abandon_limit; diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c index 7c2a5c3323..19420af8ab 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.c +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c @@ -209,7 +209,9 @@ static Block_t* aoff_get_free_block(Allctr_t *, Uint, Block_t *, Uint); static void aoff_link_free_block(Allctr_t *, Block_t*); static void aoff_unlink_free_block(Allctr_t *allctr, Block_t *del); static void aoff_creating_mbc(Allctr_t*, Carrier_t*); +#ifdef DEBUG static void aoff_destroying_mbc(Allctr_t*, Carrier_t*); +#endif static void aoff_add_mbc(Allctr_t*, Carrier_t*); static void aoff_remove_mbc(Allctr_t*, Carrier_t*); static UWord aoff_largest_fblk_in_mbc(Allctr_t*, Carrier_t*); @@ -271,7 +273,11 @@ erts_aoffalc_start(AOFFAllctr_t *alc, allctr->get_next_mbc_size = NULL; allctr->creating_mbc = aoff_creating_mbc; +#ifdef DEBUG allctr->destroying_mbc = aoff_destroying_mbc; +#else + allctr->destroying_mbc = NULL; +#endif allctr->add_mbc = aoff_add_mbc; allctr->remove_mbc = aoff_remove_mbc; allctr->largest_fblk_in_mbc = aoff_largest_fblk_in_mbc; @@ -885,17 +891,18 @@ static void aoff_creating_mbc(Allctr_t *allctr, Carrier_t *carrier) HARD_CHECK_TREE(NULL, 0, *root, 0); } +#define IS_CRR_IN_TREE(CRR,ROOT) \ + ((CRR)->rbt_node.parent || (ROOT) == &(CRR)->rbt_node) + +#ifdef DEBUG static void aoff_destroying_mbc(Allctr_t *allctr, Carrier_t *carrier) { AOFFAllctr_t *alc = (AOFFAllctr_t *) allctr; AOFF_Carrier_t *crr = (AOFF_Carrier_t*) carrier; - AOFF_RBTree_t *root = alc->mbc_root; - if (crr->rbt_node.parent || &crr->rbt_node == root) { - aoff_remove_mbc(allctr, carrier); - } - /*else already removed */ + ASSERT(!IS_CRR_IN_TREE(crr, alc->mbc_root)); } +#endif static void aoff_add_mbc(Allctr_t *allctr, Carrier_t *carrier) { @@ -903,6 +910,7 @@ static void aoff_add_mbc(Allctr_t *allctr, Carrier_t *carrier) AOFF_Carrier_t *crr = (AOFF_Carrier_t*) carrier; AOFF_RBTree_t **root = &alc->mbc_root; + ASSERT(!IS_CRR_IN_TREE(crr, *root)); HARD_CHECK_TREE(NULL, 0, *root, 0); /* Link carrier in address order tree @@ -919,6 +927,10 @@ static void aoff_remove_mbc(Allctr_t *allctr, Carrier_t *carrier) AOFF_RBTree_t **root = &alc->mbc_root; ASSERT(allctr == ERTS_ALC_CARRIER_TO_ALLCTR(carrier)); + + if (!IS_CRR_IN_TREE(crr,*root)) + return; + HARD_CHECK_TREE(NULL, 0, *root, 0); rbt_delete(root, &crr->rbt_node); -- cgit v1.2.3 From 731890f3b4ac62eed1221aa7d9fd2bfa6bf51d8c Mon Sep 17 00:00:00 2001 From: Magnus Ottenklinger Date: Fri, 2 Oct 2015 11:29:05 +0200 Subject: Fix erroneous splitting of emulator path `ct_run.c`, `erlc.c`, `escript.c` and `typer.c` do not preserve space characters in the emulator path. Thus, if a path containing space is passed via environment variables, such as `ESCRIPT_EMULATOR`, or if `get_default_emulator(progname)` returns a path with space, the execution of the programs fail. This patch fixes all occurrences found with `grep push_words -R $ERL_TOP`. --- erts/etc/common/ct_run.c | 25 +++---------------------- erts/etc/common/dialyzer.c | 24 +----------------------- erts/etc/common/erlc.c | 22 +--------------------- erts/etc/common/escript.c | 25 ++----------------------- erts/etc/common/typer.c | 26 ++++---------------------- 5 files changed, 11 insertions(+), 111 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/ct_run.c b/erts/etc/common/ct_run.c index 548514ee6c..11cec26264 100644 --- a/erts/etc/common/ct_run.c +++ b/erts/etc/common/ct_run.c @@ -83,7 +83,6 @@ static int eargc; /* Number of arguments in eargv. */ static void error(char* format, ...); static char* emalloc(size_t size); static char* strsave(char* string); -static void push_words(char* src); static int run_erlang(char* name, char** argv); static char* get_default_emulator(char* progname); #ifdef __WIN32__ @@ -152,6 +151,8 @@ int main(int argc, char** argv) argv0 = argv; emulator = get_default_emulator(argv[0]); + if (strlen(emulator) >= MAXPATHLEN) + error("Emulator path length is too large"); /* * Allocate the argv vector to be used for arguments to Erlang. @@ -163,7 +164,7 @@ int main(int argc, char** argv) eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; - push_words(emulator); + PUSH(strsave(emulator)); eargc_base = eargc; eargv = eargv + eargv_size/2; eargc = 0; @@ -294,26 +295,6 @@ int main(int argc, char** argv) return run_erlang(eargv[0], eargv); } -static void -push_words(char* src) -{ - char sbuf[MAXPATHLEN]; - char* dst; - - dst = sbuf; - while ((*dst++ = *src++) != '\0') { - if (isspace((int)*src)) { - *dst = '\0'; - PUSH(strsave(sbuf)); - dst = sbuf; - do { - src++; - } while (isspace((int)*src)); - } - } - if (sbuf[0]) - PUSH(strsave(sbuf)); -} #ifdef __WIN32__ wchar_t *make_commandline(char **argv) { diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c index c45626606c..cac1464bf6 100644 --- a/erts/etc/common/dialyzer.c +++ b/erts/etc/common/dialyzer.c @@ -65,7 +65,6 @@ static int eargc; /* Number of arguments in eargv. */ static void error(char* format, ...); static char* emalloc(size_t size); static char* strsave(char* string); -static void push_words(char* src); static int run_erlang(char* name, char** argv); static char* get_default_emulator(char* progname); #ifdef __WIN32__ @@ -189,7 +188,7 @@ int main(int argc, char** argv) eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; - push_words(emulator); + PUSH(strsave(emulator)); eargc_base = eargc; eargv = eargv + eargv_size/2; eargc = 0; @@ -269,27 +268,6 @@ int main(int argc, char** argv) return run_erlang(eargv[0], eargv); } -static void -push_words(char* src) -{ - char sbuf[MAXPATHLEN]; - char* dst; - - dst = sbuf; - while ((*dst++ = *src++) != '\0') { - if (isspace((int)*src)) { - *dst = '\0'; - PUSH(strsave(sbuf)); - dst = sbuf; - do { - src++; - } while (isspace((int)*src)); - } - } - if (sbuf[0]) - PUSH(strsave(sbuf)); -} - #ifdef __WIN32__ wchar_t *make_commandline(char **argv) { diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c index f9d909e01c..049afc526a 100644 --- a/erts/etc/common/erlc.c +++ b/erts/etc/common/erlc.c @@ -200,7 +200,7 @@ int main(int argc, char** argv) eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; - push_words(emulator); + PUSH(strsave(emulator)); eargc_base = eargc; eargv = eargv + eargv_size/2; eargc = 0; @@ -330,26 +330,6 @@ process_opt(int* pArgc, char*** pArgv, int offset) return argv[1]; } -static void -push_words(char* src) -{ - char sbuf[MAXPATHLEN]; - char* dst; - - dst = sbuf; - while ((*dst++ = *src++) != '\0') { - if (isspace((int)*src)) { - *dst = '\0'; - PUSH(strsave(sbuf)); - dst = sbuf; - do { - src++; - } while (isspace((int)*src)); - } - } - if (sbuf[0]) - PUSH(strsave(sbuf)); -} #ifdef __WIN32__ wchar_t *make_commandline(char **argv) { diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c index 7fd02ed436..a5c6d0d40b 100644 --- a/erts/etc/common/escript.c +++ b/erts/etc/common/escript.c @@ -74,7 +74,6 @@ static void error(char* format, ...); static char* emalloc(size_t size); static void efree(void *p); static char* strsave(char* string); -static void push_words(char* src); static int run_erlang(char* name, char** argv); static char* get_default_emulator(char* progname); #ifdef __WIN32__ @@ -432,7 +431,7 @@ main(int argc, char** argv) emulator = get_default_emulator(argv[0]); } - if (strlen(emulator) >= PMAX) + if (strlen(emulator) >= MAXPATHLEN) error("Value of environment variable ESCRIPT_EMULATOR is too large"); /* @@ -445,7 +444,7 @@ main(int argc, char** argv) eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; - push_words(emulator); + PUSH(strsave(emulator)); eargc_base = eargc; eargv = eargv + eargv_size/2; eargc = 0; @@ -554,26 +553,6 @@ main(int argc, char** argv) return run_erlang(eargv[0], eargv); } -static void -push_words(char* src) -{ - char sbuf[PMAX]; - char* dst; - - dst = sbuf; - while ((*dst++ = *src++) != '\0') { - if (isspace((int)*src)) { - *dst = '\0'; - PUSH(strsave(sbuf)); - dst = sbuf; - do { - src++; - } while (isspace((int)*src)); - } - } - if (sbuf[0]) - PUSH(strsave(sbuf)); -} #ifdef __WIN32__ wchar_t *make_commandline(char **argv) { diff --git a/erts/etc/common/typer.c b/erts/etc/common/typer.c index 0aa0996808..7ff8aa76e2 100644 --- a/erts/etc/common/typer.c +++ b/erts/etc/common/typer.c @@ -65,7 +65,6 @@ static int eargc; /* Number of arguments in eargv. */ static void error(char* format, ...); static char* emalloc(size_t size); static char* strsave(char* string); -static void push_words(char* src); static int run_erlang(char* name, char** argv); static char* get_default_emulator(char* progname); #ifdef __WIN32__ @@ -129,6 +128,9 @@ main(int argc, char** argv) emulator = get_default_emulator(argv[0]); + if (strlen(emulator) >= MAXPATHLEN) + error("Emulator path length is too large"); + /* * Allocate the argv vector to be used for arguments to Erlang. * Arrange for starting to pushing information in the middle of @@ -139,7 +141,7 @@ main(int argc, char** argv) eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; - push_words(emulator); + PUSH(strsave(emulator)); eargc_base = eargc; eargv = eargv + eargv_size/2; eargc = 0; @@ -192,26 +194,6 @@ main(int argc, char** argv) return run_erlang(eargv[0], eargv); } -static void -push_words(char* src) -{ - char sbuf[MAXPATHLEN]; - char* dst; - - dst = sbuf; - while ((*dst++ = *src++) != '\0') { - if (isspace((int)*src)) { - *dst = '\0'; - PUSH(strsave(sbuf)); - dst = sbuf; - do { - src++; - } while (isspace((int)*src)); - } - } - if (sbuf[0]) - PUSH(strsave(sbuf)); -} #ifdef __WIN32__ wchar_t *make_commandline(char **argv) { -- cgit v1.2.3 From 4759886bf9fc2e8ab8821a36fa8061a18e69a08d Mon Sep 17 00:00:00 2001 From: Luis Rascao Date: Sat, 26 Sep 2015 12:06:29 +0100 Subject: Fix build fail when enabling distribution debug messages --- erts/emulator/beam/dist.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 23897a49ae..0bbcc5f966 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -45,6 +45,8 @@ #include "erl_thr_progress.h" #include "dtrace-wrapper.h" +#define DIST_CTL_DEFAULT_SIZE 64 + /* Turn this on to get printouts of all distribution messages * which go on the line */ @@ -66,9 +68,13 @@ static void bw(byte *buf, ErlDrvSizeT sz) static void dist_msg_dbg(ErtsDistExternal *edep, char *what, byte *buf, int sz) { + ErtsHeapFactory factory; + DeclareTmpHeapNoproc(ctl_default,DIST_CTL_DEFAULT_SIZE); + Eterm* ctl = ctl_default; byte *extp = edep->extp; Eterm msg; - Sint size = erts_decode_dist_ext_size(edep); + Sint ctl_len; + Sint size = ctl_len = erts_decode_dist_ext_size(edep); if (size < 0) { erts_fprintf(stderr, "DIST MSG DEBUG: erts_decode_dist_ext_size(%s) failed:\n", @@ -76,10 +82,9 @@ dist_msg_dbg(ErtsDistExternal *edep, char *what, byte *buf, int sz) bw(buf, sz); } else { - Eterm *hp; ErlHeapFragment *mbuf = new_message_buffer(size); - hp = mbuf->mem; - msg = erts_decode_dist_ext(&hp, &mbuf->off_heap, edep); + erts_factory_static_init(&factory, ctl, ctl_len, &mbuf->off_heap); + msg = erts_decode_dist_ext(&factory, edep); if (is_value(msg)) erts_fprintf(stderr, " %s: %T\n", what, msg); else { @@ -1136,7 +1141,6 @@ int erts_net_message(Port *prt, byte *buf, ErlDrvSizeT len) { -#define DIST_CTL_DEFAULT_SIZE 64 ErtsDistExternal ede; byte *t; Sint ctl_len; @@ -1790,8 +1794,8 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx) #ifdef ERTS_DIST_MSG_DBG erts_fprintf(stderr, ">>%s CTL: %T\n", ctx->pass_through_size ? "P" : " ", ctx->ctl); - if (is_value(msg)) - erts_fprintf(stderr, " MSG: %T\n", msg); + if (is_value(ctx->msg)) + erts_fprintf(stderr, " MSG: %T\n", ctx->msg); #endif ctx->data_size = ctx->pass_through_size; -- cgit v1.2.3 From ef9119ca3662c4e60e2d49c7edc0622cc73c21a0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 5 Oct 2015 15:37:20 +0200 Subject: erts: Spell-check erlang.xml --- erts/doc/src/erlang.xml | 63 ++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 32 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 221869799d..e4f3a06cc5 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1693,7 +1693,7 @@ true file The second element of the tuple is a string (list of - characters) representing the filename of the source file + characters) representing the file name of the source file of the function. line @@ -1718,7 +1718,7 @@ true groups have a group leader. All I/O from the group is channeled to the group leader. When a new process is spawned, it gets the same group leader as the spawning - process. Initially, at system startup, init is both + process. Initially, at system start-up, init is both its own group leader and the group leader of all processes.

@@ -2429,7 +2429,7 @@ os_prompt%

Loads and links a dynamic library containing native implemented functions (NIFs) for a module. Path - is a file path to the sharable object/dynamic library file minus + is a file path to the shareable object/dynamic library file minus the OS-dependent file extension (.so for Unix and .dll for Windows. For information on how to implement a NIF library, see @@ -2804,7 +2804,7 @@ os_prompt% badarg If Type is not one of the memory types - listed in the decription of + listed in the description of erlang:memory/0. badarg @@ -3012,7 +3012,7 @@ os_prompt%

Making several calls to monitor/2 for the same Item and/or Type is not - an error; it results in as many independent monitorings.

+ an error; it results in as many independent monitoring instances.

The monitor functionality is expected to be extended. That is, other Types and Items are expected to be supported in a future release.

@@ -3034,7 +3034,7 @@ os_prompt% is false, monitoring is turned off.

Making several calls to monitor_node(Node, true) for the same Node is not an error; it results - in as many independent monitorings.

+ in as many independent monitoring instances.

If Node fails or does not exist, the message {nodedown, Node} is delivered to the process. If a process has made two calls to monitor_node(Node, true) @@ -3256,8 +3256,8 @@ os_prompt% process.

The name of the executable as well as the arguments given in cd, env, args, and arg0 are - subject to Unicode filename translation if the system is running - in Unicode filename mode. To avoid + subject to Unicode file name translation if the system is running + in Unicode file name mode. To avoid translation or to force, for example UTF-8, supply the executable and/or arguments as a binary in the correct encoding. For details, see the module @@ -3267,7 +3267,7 @@ os_prompt% User's Guide.

The characters in the name (if given as a list) can only be higher than 255 if the Erlang Virtual Machine is started - in Unicode filename translation mode. Otherwise the name + in Unicode file name translation mode. Otherwise the name of the executable is limited to the ISO-latin-1 character set.

PortName can be any of the following:

@@ -3280,7 +3280,7 @@ os_prompt% runs outside the Erlang work space unless an Erlang driver with the name Command is found. If found, that driver is started. A driver runs in the Erlang - workspace, which means that it is linked with the Erlang + work space, which means that it is linked with the Erlang runtime system.

When starting external programs on Solaris, the system call vfork is used in preference to fork @@ -3296,8 +3296,8 @@ os_prompt% token of the command is considered as the name of the executable (or driver). This (among other things) makes this option unsuitable for running - programs having spaces in filenames or directory names. - If spaces in executable filenames are desired, use + programs having spaces in file names or directory names. + If spaces in executable file names are desired, use {spawn_executable, Command} instead.

{spawn_driver, Command} @@ -3408,14 +3408,14 @@ os_prompt% other platforms, a similar behavior is mimicked.

The arguments are not expanded by the shell before being supplied to the executable. Most notably this - means that file wildcard expansion does not happen. - To expand wildcards for the arguments, use + means that file wild card expansion does not happen. + To expand wild cards for the arguments, use filelib:wildcard/1. Notice that even if the program is a Unix shell script, meaning that the - shell ultimately is invoked, wildcard expansion + shell ultimately is invoked, wild card expansion does not happen, and the script is provided with the - untouched arguments. On Windows, wildcard expansion + untouched arguments. On Windows, wild card expansion is always up to the program itself, therefore this is not an issue issue.

The executable name (also known as argv[0]) @@ -4313,8 +4313,8 @@ os_prompt% high are selected for execution. As with priority high, processes on lower priorities can execute in parallel with processes on priority max.

-

Scheduling is pre-emptive. Regardless of priority, a process - is pre-empted when it has consumed more than a certain number +

Scheduling is preemptive. Regardless of priority, a process + is preempted when it has consumed more than a certain number of reductions since the last time it was selected for execution.

@@ -5120,7 +5120,7 @@ true

Starts a timer. When the timer expires, the message Msg is sent to the process - identified by Dest. Appart from + identified by Dest. Apart from the format of the timeout message, erlang:send_after/4 works exactly as erlang:start_timer/4.

@@ -7067,21 +7067,21 @@ ok introduced in the future:

{function, Function} -

Function is the name of the funcion +

Function is the name of the function used. This tuple always exist if OS monotonic time is available to the runtime system.

{clock_id, ClockId}

This tuple only exist if Function can be used with different clocks. ClockId - corresponds to the clock identifer used when calling + corresponds to the clock identifier used when calling Function.

{resolution, OsMonotonicTimeResolution}

Highest possible resolution of current OS monotonic time source as parts per - second. If no resolution information can be retreived + second. If no resolution information can be retrieved from the OS, OsMonotonicTimeResolution is set to the resolution of the time unit of Functions return value. That is, the actual @@ -7135,14 +7135,14 @@ ok {clock_id, ClockId}

This tuple only exist if Function can be used with different clocks. ClockId - corresponds to the clock identifer used when calling + corresponds to the clock identifier used when calling Function.

{resolution, OsSystemTimeResolution}

Highest possible resolution of current OS system time source as parts per - second. If no resolution information can be retreived + second. If no resolution information can be retrieved from the OS, OsSystemTimeResolution is set to the resolution of the time unit of Functions return value. That is, the actual @@ -7654,9 +7654,8 @@ ok

If a process is put into or removed from the run queue, a message, {profile, Pid, State, Mfa, Ts}, is sent to ProfilerPid. Running processes that - are reinserted - into the run queue after having been pre-emptively - scheduled out do not trigger this message.

+ are reinserted into the run queue after having been + preempted do not trigger this message.

runnable_ports @@ -8341,7 +8340,7 @@ timestamp() ->

To get information about a function, PidOrFunc is to be the three-element tuple {Module, Function, Arity} or - the atom on_load. No wildcards are allowed. Returns + the atom on_load. No wild cards are allowed. Returns undefined if the function does not exist, or false if the function is not traced.

The following Items are valid::

@@ -8457,7 +8456,7 @@ timestamp() -> {Module, Function, Arity}, or the atom on_load (described in the following). It can be the module, function, and arity for a function (or a BIF in any module). - The atom '_' can be used as a wildcard in any of the + The atom '_' can be used as a wild card in any of the following ways:

{Module,Function,'_'} @@ -8475,7 +8474,7 @@ timestamp() ->

Other combinations, such as {Module,'_',Arity}, are - not allowed. Local functions match wildcards only if + not allowed. Local functions match wild cards only if option local is in FlagList.

If argument MFA is the atom on_load, the match specification and flag list are used on all @@ -8722,7 +8721,7 @@ timestamp() -> positive

Return only positive integers.

Note that by passing the positive modifier - you will get heap allocated integers (big-nums) + you will get heap allocated integers (bignums) quicker.

@@ -8758,7 +8757,7 @@ timestamp() -> are ignored.

Note that the set of integers returned by - unique_integer/1 using diffrent sets of + unique_integer/1 using different sets of Modifiers will overlap. For example, by calling unique_integer([monotonic]), and unique_integer([positive, monotonic]) -- cgit v1.2.3 From 6e93fb788aebb9050da2166749b41ff54197e049 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Thu, 7 May 2015 14:43:02 +0200 Subject: Take out automatic insertion of 'undefined' from typed record fields Background ----------- In record fields with a type declaration but without an initializer, the Erlang parser inserted automatically the singleton type 'undefined' to the list of declared types, if that value was not present there. I.e. the record declaration: -record(rec, {f1 :: float(), f2 = 42 :: integer(), f3 :: some_mod:some_typ()}). was translated by the parser to: -record(rec, {f1 :: float() | 'undefined', f2 = 42 :: integer(), f3 :: some_mod:some_typ() | 'undefined'}). The rationale for this was that creation of a "dummy" #rec{} record should not result in a warning from dialyzer that e.g. the implicit initialization of the #rec.f1 field violates its type declaration. Problems --------- This seemingly innocent action has some unforeseen consequences. For starters, there is no way for programmers to declare that e.g. only floats make sense for the f1 field of #rec{} records when there is no `obvious' default initializer for this field. (This also affects tools like PropEr that use these declarations produced by the Erlang parser to generate random instances of records for testing purposes.) It also means that dialyzer does not warn if e.g. an is_atom/1 test or something more exotic like an atom_to_list/1 call is performed on the value of the f1 field. Similarly, there is no way to extend dialyzer to warn if it finds record constructions where f1 is not initialized to some float. Last but not least, it is semantically problematic when the type of the field is an opaque type: creating a union of an opaque and a structured type is very problematic for analysis because it fundamentally breaks the opacity of the term at that point. Change ------- To solve these problems the parser will not automatically insert the 'undefined' value anymore; instead the user has the option to choose the places where this value makes sense (for the field) and where it does not and insert the | 'undefined' there manually. Consequences of this change ---------------------------- This change means that dialyzer will issue a warning for all places where records with uninitialized fields are created and those fields have a declared type that is incompatible with 'undefined' (e.g. float()). This warning can be suppressed easily by adding | 'undefined' to the type of this field. This also adds documentation that the user really intends to create records where this field is uninitialized. --- erts/doc/src/absform.xml | 6 ------ 1 file changed, 6 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index df2553ced3..49fe784d06 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -246,12 +246,6 @@ Rep(V) = . If V is , then Rep(V) = . - If V is , where is - an atom and is a type and it does not contain - syntactically, then Rep(V) = - . - Note that if is an annotated type, it will be wrapped in - parentheses. If V is , where is an atom and is a type, then Rep(V) = . -- cgit v1.2.3 From 93f5844185a459cbf9efc3843919c463a2eef0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 8 Oct 2015 13:04:40 +0200 Subject: Teach erlang:is_builtin/3 that erlang:apply/3 is built-in erlang:is_builtin(erlang, apply, 3) returns 'false'. That seems to be an oversight in the implementation of erlang:is_builtin/3 rather than a conscious design decision. Part of apply/3 is implemented in C (as a special instruction), and part of it in Erlang (only used if apply/3 is used recursively). That makes apply/3 special compared to all other BIFs. From the viewpoint of the user, apply/3 is a built-in function, since it cannot possibly be implemented in pure Erlang. Noticed-by: Stavros Aronis --- erts/emulator/beam/beam_emu.c | 9 +++++++++ erts/emulator/test/bif_SUITE.erl | 21 +++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index a42ce1c9f1..faf496a030 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6752,6 +6752,15 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity) Export e; Export* ep; + if (Mod == am_erlang && Name == am_apply && arity == 3) { + /* + * Special case. apply/3 is built-in (implemented in C), + * but implemented in a different way than all other + * BIFs. + */ + return 1; + } + e.code[0] = Mod; e.code[1] = Name; e.code[2] = arity; diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index d6a771e7b9..ebc4db53c4 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -32,7 +32,8 @@ specs/1,improper_bif_stubs/1,auto_imports/1, t_list_to_existing_atom/1,os_env/1,otp_7526/1, binary_to_atom/1,binary_to_existing_atom/1, - atom_to_binary/1,min_max/1, erlang_halt/1]). + atom_to_binary/1,min_max/1, erlang_halt/1, + is_builtin/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -42,7 +43,7 @@ all() -> t_list_to_existing_atom, os_env, otp_7526, display, atom_to_binary, binary_to_atom, binary_to_existing_atom, - min_max, erlang_halt]. + min_max, erlang_halt, is_builtin]. groups() -> []. @@ -716,6 +717,22 @@ wait_until_stable_size(File,PrevSz) -> wait_until_stable_size(File,NewSz) end. +is_builtin(_Config) -> + Exp0 = [{M,F,A} || {M,_} <- code:all_loaded(), + {F,A} <- M:module_info(exports)], + Exp = ordsets:from_list(Exp0), + + %% erlang:apply/3 is considered to be built-in, but is not + %% implemented as other BIFs. + + Builtins0 = [{erlang,apply,3}|erlang:system_info(snifs)], + Builtins = ordsets:from_list(Builtins0), + NotBuiltin = ordsets:subtract(Exp, Builtins), + _ = [true = erlang:is_builtin(M, F, A) || {M,F,A} <- Builtins], + _ = [false = erlang:is_builtin(M, F, A) || {M,F,A} <- NotBuiltin], + + ok. + %% Helpers -- cgit v1.2.3 From 76777477d8722b785afa8da0166b4577fb7e047c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 9 Oct 2015 17:18:31 +0200 Subject: erts: Remove vheap mature from process control block Binary vheap mature is not necessary for binary gc. --- erts/emulator/beam/erl_gc.c | 2 -- erts/emulator/beam/erl_process.c | 2 -- erts/emulator/beam/erl_process.h | 2 -- 3 files changed, 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 734f120e09..29579f74e6 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -848,7 +848,6 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) if (OLD_HEAP(p) && ((mature <= OLD_HEND(p) - OLD_HTOP(p)) && - ((BIN_VHEAP_MATURE(p) < ( BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p)))) && ((BIN_OLD_VHEAP_SZ(p) > BIN_OLD_VHEAP(p))) ) ) { ErlMessage *msgp; Uint size_after; @@ -2373,7 +2372,6 @@ sweep_off_heap(Process *p, int fullsweep) } BIN_VHEAP_SZ(p) = next_vheap_size(p, bin_vheap, BIN_VHEAP_SZ(p)); MSO(p).overhead = bin_vheap; - BIN_VHEAP_MATURE(p) = bin_vheap; /* * If we got any shrink candidates, check them out. diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 879b523c38..15a6d5d651 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -10834,7 +10834,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->bin_vheap_sz = p->min_vheap_size; p->bin_old_vheap_sz = p->min_vheap_size; p->bin_old_vheap = 0; - p->bin_vheap_mature = 0; p->sys_task_qs = NULL; @@ -11054,7 +11053,6 @@ void erts_init_empty_process(Process *p) p->bin_old_vheap_sz = BIN_VH_MIN_SIZE; p->bin_old_vheap = 0; p->sys_task_qs = NULL; - p->bin_vheap_mature = 0; ERTS_PTMR_INIT(p); p->next = NULL; p->off_heap.first = NULL; diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 65422b8c15..e7c5614b9c 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -898,7 +898,6 @@ struct ErtsPendingSuspend_ { # define MIN_VHEAP_SIZE(p) (p)->min_vheap_size # define BIN_VHEAP_SZ(p) (p)->bin_vheap_sz -# define BIN_VHEAP_MATURE(p) (p)->bin_vheap_mature # define BIN_OLD_VHEAP_SZ(p) (p)->bin_old_vheap_sz # define BIN_OLD_VHEAP(p) (p)->bin_old_vheap @@ -1017,7 +1016,6 @@ struct process { ErtsPSD *psd; /* Rarely used process specific data */ Uint64 bin_vheap_sz; /* Virtual heap block size for binaries */ - Uint64 bin_vheap_mature; /* Virtual heap block size for binaries */ Uint64 bin_old_vheap_sz; /* Virtual old heap block size for binaries */ Uint64 bin_old_vheap; /* Virtual old heap size for binaries */ -- cgit v1.2.3 From 4b33813231f6a0a6f2c7aa3d369ede7373229522 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 12 Oct 2015 16:07:50 +0200 Subject: erts: Don't run processes tests on lcnt with little memory --- erts/emulator/test/process_SUITE.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 4c311e1f06..97aa5e573e 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -1478,7 +1478,15 @@ processes_this_tab(doc) -> processes_this_tab(suite) -> []; processes_this_tab(Config) when is_list(Config) -> - sys_mem_cond_run(1024, fun () -> chk_processes_bif_test_res(processes_bif_test()) end). + Mem = case {erlang:system_info(build_type), + erlang:system_info(allocator)} of + {lcnt, {_, _Vsn, [sys_alloc], _Opts}} -> + %% When running +Mea min + lcnt we may need more memory + 1024 * 4; + _ -> + 1024 + end, + sys_mem_cond_run(Mem, fun () -> chk_processes_bif_test_res(processes_bif_test()) end). chk_processes_bif_test_res(ok) -> ok; chk_processes_bif_test_res({comment, _} = Comment) -> Comment; -- cgit v1.2.3 From d8ff1e37c38c37304fb44ba6618da141e4d08c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 12 Oct 2015 12:21:15 +0200 Subject: Remove the deprecated webtool application --- erts/test/otp_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index c92a7cf6f7..87d620b180 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -290,7 +290,7 @@ call_to_deprecated(Config) when is_list(Config) -> call_to_size_1(Config) when is_list(Config) -> %% Applications that do not call erlang:size/1: Apps = [asn1,compiler,debugger,kernel,observer,parsetools, - runtime_tools,stdlib,tools,webtool], + runtime_tools,stdlib,tools], not_recommended_calls(Config, Apps, {erlang,size,1}). call_to_now_0(Config) when is_list(Config) -> @@ -298,7 +298,7 @@ call_to_now_0(Config) when is_list(Config) -> Apps = [asn1,common_test,compiler,debugger,dialyzer, gs,kernel,mnesia,observer,parsetools,reltool, runtime_tools,sasl,stdlib,syntax_tools, - test_server,tools,webtool], + test_server,tools], not_recommended_calls(Config, Apps, {erlang,now,0}). not_recommended_calls(Config, Apps0, MFA) -> -- cgit v1.2.3 From a36b6aeeeb3e40a7ba1737b596ede3e321540211 Mon Sep 17 00:00:00 2001 From: Constantin Rack Date: Thu, 15 Oct 2015 20:19:33 +0200 Subject: Fix minor typo "timout" -> "timeout" --- erts/preloaded/src/prim_inet.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 4d04e1dacb..f9dd6c9618 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -232,7 +232,7 @@ bindx(S, AddFlag, Addrs) -> %% if timeout is given: %% timeout < 0 -> infinity %% 0 -> immediate connect (mostly works for loopback) -%% > 0 -> wait for timout ms if not connected then +%% > 0 -> wait for timeout ms if not connected then %% return {error, timeout} %% %% ASYNC_CONNECT(insock(), IP, Port, Timeout) -> {ok, S, Ref} | {error, Reason} @@ -273,7 +273,7 @@ async_connect(S, IP, Port, Time) -> %% if timeout is given: %% timeout < 0 -> infinity %% 0 -> immediate accept (poll) -%% > 0 -> wait for timout ms for accept if no accept then +%% > 0 -> wait for timeout ms for accept if no accept then %% return {error, timeout} %% %% ASYNC_ACCEPT(insock(), Timeout) -- cgit v1.2.3 From 1cb0967db5ab24bcf4b492b273401e63c1fdae1a Mon Sep 17 00:00:00 2001 From: Ian Denhardt Date: Thu, 15 Oct 2015 18:17:02 -0400 Subject: Fix erroneous use of __uint32_t The presence of this symbol is libc-specific. In particular, it is absent from musl. The correct solution is to use uint32_t. --- erts/emulator/sys/common/erl_poll.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index 19ce582154..bd3a46ef0f 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -140,7 +140,7 @@ struct erts_sys_fd_type { #endif #define ERTS_POLL_EV_E2N(EV) \ - ((__uint32_t) (EV)) + ((uint32_t) (EV)) #define ERTS_POLL_EV_N2E(EV) \ ((ErtsPollEvents) (EV)) -- cgit v1.2.3 From 9b83311f160158a347d9bf4ef19e3847af60fc9f Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Fri, 16 Oct 2015 13:30:02 +0200 Subject: add path to vcredist.exe for VS-2013 --- erts/etc/win32/nsis/find_redist.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/win32/nsis/find_redist.sh b/erts/etc/win32/nsis/find_redist.sh index c0895c9dd5..03e92b21c7 100755 --- a/erts/etc/win32/nsis/find_redist.sh +++ b/erts/etc/win32/nsis/find_redist.sh @@ -164,7 +164,7 @@ fi #echo $BPATH_LIST for BP in $BPATH_LIST; do - for verdir in "sdk v2.0" "sdk v3.5" "v6.0A" "v7.0" "v7.0A" "v7.1"; do + for verdir in "sdk v2.0" "sdk v3.5" "v6.0A" "v7.0" "v7.0A" "v7.1" "VC redist 1033"; do BPATH=$BP fail=false allow_fail=false -- cgit v1.2.3 From 4a9f688b804688ff95e256d3412ca932b9972d8a Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Mon, 28 Sep 2015 12:34:53 +0200 Subject: erts: Detect and build on MSYS2 for windows Allow building win32 on MSYS2. Avoid msys2 path conversion which does not work. And print the real windows command when something fails. --- erts/etc/win32/msys_tools/vc/cc.sh | 10 ++++++++-- erts/etc/win32/msys_tools/vc/emu_cc.sh | 1 + erts/etc/win32/msys_tools/vc/mc.sh | 7 ++++++- erts/etc/win32/msys_tools/vc/rc.sh | 7 ++++++- erts/etc/win32/nsis/Makefile | 10 ++++++++-- 5 files changed, 29 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/etc/win32/msys_tools/vc/cc.sh b/erts/etc/win32/msys_tools/vc/cc.sh index ad05e5375b..ac89aac34e 100644 --- a/erts/etc/win32/msys_tools/vc/cc.sh +++ b/erts/etc/win32/msys_tools/vc/cc.sh @@ -242,7 +242,7 @@ for x in $SOURCES; do if [ $PREPROCESSING = true ]; then output_flag="-E" else - output_flag="-c -Fo`cmd //C echo ${output_filename}`" + output_flag="-FS -c -Fo`cmd //C echo ${output_filename}`" fi params="$COMMON_CFLAGS $MD $DEBUG_FLAGS $OPTIMIZE_FLAGS \ $CMD ${output_flag} $MPATH" @@ -250,6 +250,8 @@ for x in $SOURCES; do echo cc.sh "$SAVE" >>$CC_SH_DEBUG_LOG echo cl.exe $params >>$CC_SH_DEBUG_LOG fi + # MSYS2 (currently) converts the paths wrong, avoid it + export MSYS2_ARG_CONV_EXCL=-FoC eval cl.exe $params >$MSG_FILE 2>$ERR_FILE RES=$? if test $PREPROCESSING = false; then @@ -274,6 +276,7 @@ for x in $SOURCES; do fi rm -f $ERR_FILE $MSG_FILE if [ $RES != 0 ]; then + echo Failed: cl.exe $params rm -rf $TMPOBJDIR exit $RES fi @@ -312,7 +315,10 @@ if [ $LINKING = true ]; then stdlib="-lLIBMTD";; esac # And finally call the next script to do the linking... - params="$out_spec $LINKCMD $stdlib" + params="$out_spec $LINKCMD $stdlib" + if [ "X$CC_SH_DEBUG_LOG" != "X" ]; then + echo ld.sh $ACCUM_OBJECTS $params + fi eval ld.sh $ACCUM_OBJECTS $params RES=$? fi diff --git a/erts/etc/win32/msys_tools/vc/emu_cc.sh b/erts/etc/win32/msys_tools/vc/emu_cc.sh index 01f75b2468..10d59214ea 100644 --- a/erts/etc/win32/msys_tools/vc/emu_cc.sh +++ b/erts/etc/win32/msys_tools/vc/emu_cc.sh @@ -29,6 +29,7 @@ WTOOLDIR0=`win2msys_path.sh "$TOOLDIR"` WTOOLDIR=`cmd //C echo $WTOOLDIR0` # Do primitive 'make' newer_exe=`find $TOOLDIR -newer $COFFIX.c -name coffix.exe -print` +export MSYS2_ARG_CONV_EXCL="-FeC" if [ -z $newer_exe ]; then echo recompiling $COFFIX.exe cl.exe -Fe${WTOOLDIR}/coffix.exe ${WTOOLDIR}/coffix.c diff --git a/erts/etc/win32/msys_tools/vc/mc.sh b/erts/etc/win32/msys_tools/vc/mc.sh index e9ea9ff9a9..14b5ebaa8f 100644 --- a/erts/etc/win32/msys_tools/vc/mc.sh +++ b/erts/etc/win32/msys_tools/vc/mc.sh @@ -80,9 +80,14 @@ if [ -n "$OUTPUT_DIRNAME" ]; then exit $RES fi fi +# MSYS2 (currently) converts the paths wrong, avoid it +export MSYS2_ARG_CONV_EXCL= eval $MCC "$CMD" >/tmp/mc.exe.${p}.1 2>/tmp/mc.exe.${p}.2 RES=$? -tail +2 /tmp/mc.exe.${p}.2 >&2 +if [ $RES != 0 ]; then + echo Failed: $MCC "$CMD" +fi +tail -n +2 /tmp/mc.exe.${p}.2 >&2 cat /tmp/mc.exe.${p}.1 rm -f /tmp/mc.exe.${p}.2 /tmp/mc.exe.${p}.1 exit $RES diff --git a/erts/etc/win32/msys_tools/vc/rc.sh b/erts/etc/win32/msys_tools/vc/rc.sh index 1b3b1c85bd..1f8ade17cb 100644 --- a/erts/etc/win32/msys_tools/vc/rc.sh +++ b/erts/etc/win32/msys_tools/vc/rc.sh @@ -79,9 +79,14 @@ if [ "X$RC_SH_DEBUG_LOG" != "X" ]; then echo rc.sh "$SAVE" >>$RC_SH_DEBUG_LOG echo rc.exe $CMD >>$RC_SH_DEBUG_LOG fi +# MSYS2 (currently) converts the paths wrong, avoid it +export MSYS2_ARG_CONV_EXCL=-Fo eval $RCC "$CMD" >/tmp/rc.exe.${p}.1 2>/tmp/rc.exe.${p}.2 RES=$? -tail +2 /tmp/rc.exe.${p}.2 >&2 +if [ $RES != 0 ]; then + echo Failed: $RCC "$CMD" +fi +tail -n +2 /tmp/rc.exe.${p}.2 >&2 cat /tmp/rc.exe.${p}.1 rm -f /tmp/rc.exe.${p}.2 /tmp/rc.exe.${p}.1 exit $RES diff --git a/erts/etc/win32/nsis/Makefile b/erts/etc/win32/nsis/Makefile index 49d835170a..64f44ff86d 100644 --- a/erts/etc/win32/nsis/Makefile +++ b/erts/etc/win32/nsis/Makefile @@ -42,7 +42,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk TARGET_DIR = $(RELEASE_PATH) -ifeq ($(MSYSTEM),MINGW32) +ifdef MSYSTEM + ifeq ($(MSYSTEM),$(filter $(MSYSTEM),MSYS MINGW32 MINGW64)) + USEMSYS := true + endif +endif + +ifeq ($(USEMSYS),true) MAKENSISFLAGS = //V2 WTESTROOT=$(shell (msys2win_path.sh "$(RELEASE_PATH)")) @@ -63,7 +69,7 @@ else endif REDIST_FILE=$(shell (sh ./find_redist.sh || echo "")) -ifeq ($(MSYSTEM),MINGW32) +ifeq ($(USEMSYS),true) NICEREDISTFILE=$(shell (msys2win_path.sh -m "$(REDIST_FILE)" 2>/dev/null || echo "")) else NICEREDISTFILE=$(shell (cygpath -d -m "$(REDIST_FILE)" 2>/dev/null || echo "")) -- cgit v1.2.3 From 285b4fa13914dc4748c1020213a9c65cad3d2738 Mon Sep 17 00:00:00 2001 From: Serge Aleynikov Date: Tue, 2 Jun 2015 08:44:23 -0400 Subject: erts: Add {line_delimiter, byte()} option to inet:setopts/2 A new {line_delimiter, byte()} option allows line-oriented TCP-based protocols to use a custom line delimiting character. It is to be used in conjunction with {packet, line}. This option also works with erlang:decode_packet/3 when its first argument is 'line'. --- erts/doc/src/erlang.xml | 4 ++++ erts/emulator/beam/atom.names | 1 + erts/emulator/beam/erl_bif_port.c | 10 ++++++++-- erts/emulator/beam/packet_parser.c | 5 +++-- erts/emulator/beam/packet_parser.h | 3 ++- erts/emulator/drivers/common/inet_drv.c | 11 ++++++++++- erts/preloaded/ebin/prim_inet.beam | Bin 72748 -> 36456 bytes erts/preloaded/src/prim_inet.erl | 3 +++ 8 files changed, 31 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 0492924164..1963b27915 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -933,6 +933,10 @@ if packet_size itself is not set. This use is only intended for backward compatibility.

+ {line_delimiter, 0 ≤ char() ≤ 255} +

For packet type line, sets delimiting character. + Default $\n.

+

Examples:

diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index f9a2f3e33e..190e7817dc 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -321,6 +321,7 @@ atom ldflags
 atom Le='=<'
 atom lf
 atom line
+atom line_delimiter
 atom line_length
 atom linked_in_driver
 atom links
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 3ff54c7a60..e47d7bcbbb 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -1329,7 +1329,8 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3)
     ErlSubBin* rest;
     Eterm res;
     Eterm options;
-    int code;
+    int   code;
+    char  delimiter = '\n';
 
     if (!is_binary(BIF_ARG_2) || 
         (!is_list(BIF_ARG_3) && !is_nil(BIF_ARG_3))) {
@@ -1370,6 +1371,11 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3)
                 case am_line_length:
                     trunc_len = val;
                     goto next_option;
+                case am_line_delimiter:
+                    if (type == TCP_PB_LINE_LF && val >= 0 && val <= 255) {
+                        delimiter = (char)val;
+                        goto next_option;
+                    }
                 }
             }
         }
@@ -1390,7 +1396,7 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3)
         pca.aligned_ptr = bin_ptr;
     }
     packet_sz = packet_get_length(type, (char*)pca.aligned_ptr, pca.bin_sz,
-                                  max_plen, trunc_len, &http_state);
+                                  max_plen, trunc_len, delimiter, &http_state);
     if (!(packet_sz > 0 && packet_sz <= pca.bin_sz)) {
         if (packet_sz < 0) {
 	    goto error;
diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c
index 2dd421a9e9..a737a86f14 100644
--- a/erts/emulator/beam/packet_parser.c
+++ b/erts/emulator/beam/packet_parser.c
@@ -256,6 +256,7 @@ int packet_get_length(enum PacketParseType htype,
                       const char* ptr, unsigned n, /* Bytes read so far */
                       unsigned max_plen,     /* Max packet length, 0=no limit */
                       unsigned trunc_len,    /* Truncate (lines) if longer, 0=no limit */
+                      char     delimiter,    /* Line delimiting character */
                       int*     statep)       /* Protocol specific state */
 {
     unsigned hlen, plen;
@@ -299,9 +300,9 @@ int packet_get_length(enum PacketParseType htype,
         goto remain;
 
     case TCP_PB_LINE_LF: {
-        /* TCP_PB_LINE_LF:  [Data ... \n]  */
+        /* TCP_PB_LINE_LF:  [Data ... Delimiter]  */
         const char* ptr2;
-        if ((ptr2 = memchr(ptr, '\n', n)) == NULL) {
+        if ((ptr2 = memchr(ptr, delimiter, n)) == NULL) {
             if (n > max_plen && max_plen != 0) { /* packet full */
                 DEBUGF((" => packet full (no NL)=%d\r\n", n));
                 goto error;
diff --git a/erts/emulator/beam/packet_parser.h b/erts/emulator/beam/packet_parser.h
index ff158ff8b8..717d905fad 100644
--- a/erts/emulator/beam/packet_parser.h
+++ b/erts/emulator/beam/packet_parser.h
@@ -105,7 +105,8 @@ int packet_get_length(enum PacketParseType htype,
 		      const char* ptr, unsigned n,  /* Bytes read so far */
 		      unsigned max_plen,      /* Packet max length, 0=no limit */
 		      unsigned trunc_len,     /* Truncate (lines) if longer, 0=no limit */
-		      int* statep);           /* Internal protocol state */
+		      char     delimiter,     /* Line delimiting character */
+		      int*     statep);       /* Internal protocol state */
 
 ERTS_GLB_INLINE
 void packet_get_body(enum PacketParseType htype,
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 89b71aa66a..a829599fe5 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -885,6 +885,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
 #define INET_LOPT_MSGQ_LOWTRMRK     37  /* set local msgq low watermark */
 #define INET_LOPT_NETNS             38  /* Network namespace pathname */
 #define INET_LOPT_TCP_SHOW_ECONNRESET 39  /* tell user about incoming RST */
+#define INET_LOPT_LINE_DELIM        40  /* Line delimiting char */
 /* SCTP options: a separate range, from 100: */
 #define SCTP_OPT_RTOINFO		100
 #define SCTP_OPT_ASSOCINFO		101
@@ -1154,6 +1155,7 @@ typedef struct {
 #else
     Uint32        send_oct[2];  /* number of octets sent, 64 bits */
 #endif
+    char          delimiter;    /* Line delimiting character (def: '\n')  */
     unsigned long send_cnt;     /* number of packets sent */
     unsigned long send_max;     /* maximum packet send */
     double send_avg;            /* average packet size sent */
@@ -6276,6 +6278,12 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
 	    }
 	    continue;
 
+	case INET_LOPT_LINE_DELIM:
+	    DEBUGF(("inet_set_opts(%ld): s=%d, LINE_DELIM=%d\r\n",
+		    (long)desc->port, desc->s, ival));
+	    desc->delimiter = (char)ival;
+	    continue;
+
 	case INET_OPT_REUSEADDR: 
 #ifdef __WIN32__
 	    continue;  /* Bjorn says */
@@ -8371,6 +8379,7 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)
     desc->deliver = INET_DELIVER_TERM; /* standard term format */
     desc->active  = INET_PASSIVE;      /* start passive */
     desc->active_count = 0;
+    desc->delimiter    = '\n';         /* line delimiting char */
     desc->oph = NULL;
     desc->opt = NULL;
 
@@ -9882,7 +9891,7 @@ static int tcp_remain(tcp_descriptor* desc, int* len)
 
     tlen = packet_get_length(desc->inet.htype, ptr, n, 
                              desc->inet.psize, desc->i_bufsz,
-                             &desc->http_state);
+                             desc->inet.delimiter, &desc->http_state);
 
     DEBUGF(("tcp_remain(%ld): s=%d, n=%d, nfill=%d nsz=%d, tlen %d\r\n",
 	    (long)desc->inet.port, desc->inet.s, n, nfill, nsz, tlen));
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index 5a188be3ba..6cf8d30cb2 100644
Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 4d04e1dacb..d5c8fd4268 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -1147,6 +1147,7 @@ enc_opt(packet_size)     -> ?INET_LOPT_PACKET_SIZE;
 enc_opt(read_packets)    -> ?INET_LOPT_READ_PACKETS;
 enc_opt(netns)           -> ?INET_LOPT_NETNS;
 enc_opt(show_econnreset) -> ?INET_LOPT_TCP_SHOW_ECONNRESET;
+enc_opt(line_delimiter)  -> ?INET_LOPT_LINE_DELIM;
 enc_opt(raw)             -> ?INET_OPT_RAW;
 % Names of SCTP opts:
 enc_opt(sctp_rtoinfo)	 	   -> ?SCTP_OPT_RTOINFO;
@@ -1205,6 +1206,7 @@ dec_opt(?INET_LOPT_PACKET_SIZE)      -> packet_size;
 dec_opt(?INET_LOPT_READ_PACKETS)     -> read_packets;
 dec_opt(?INET_LOPT_NETNS)           -> netns;
 dec_opt(?INET_LOPT_TCP_SHOW_ECONNRESET) -> show_econnreset;
+dec_opt(?INET_LOPT_LINE_DELIM)      -> line_delimiter;
 dec_opt(?INET_OPT_RAW)              -> raw;
 dec_opt(I) when is_integer(I)     -> undefined.
 
@@ -1287,6 +1289,7 @@ type_opt_1(packet) ->
 	   {httph_bin,?TCP_PB_HTTPH_BIN},
 	   {ssl, ?TCP_PB_SSL_TLS}, % obsolete
 	   {ssl_tls, ?TCP_PB_SSL_TLS}]};
+type_opt_1(line_delimiter)  -> int;
 type_opt_1(mode) ->
     {enum,[{list, ?INET_MODE_LIST},
 	   {binary, ?INET_MODE_BINARY}]};
-- 
cgit v1.2.3


From 8377afdf5f56d018a1b439bbccc918ce123a834b Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin 
Date: Fri, 16 Oct 2015 11:43:20 +0200
Subject: erts: Clarify documentation

---
 erts/doc/src/erlang.xml | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

(limited to 'erts')

diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 1963b27915..70d000f763 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -874,10 +874,10 @@
           
           line
           
-            

A packet is a line terminated with newline. The - newline character is included in the returned packet - unless the line was truncated according to option - line_length.

+

A packet is a line terminated by a delimiter byte, + default is the latin1 newline character. The delimiter + byte is included in the returned packet unless the line + was truncated according to option line_length.

asn1 | cdr | sunrm | fcgi | tpkt @@ -933,9 +933,9 @@ if packet_size itself is not set. This use is only intended for backward compatibility.

- {line_delimiter, 0 ≤ char() ≤ 255} -

For packet type line, sets delimiting character. - Default $\n.

+ {line_delimiter, 0 =< byte() =< 255} +

For packet type line, sets the delimiting byte. + Default is the latin1 character $\n.

Examples:

-- cgit v1.2.3 From afac3c7136c48d8630bd400c5454e146915e634f Mon Sep 17 00:00:00 2001 From: Serge Aleynikov Date: Mon, 26 Oct 2015 14:46:54 +0100 Subject: erts: Add {line_delimiter, byte()} option to inet:setopts/2 A new {line_delimiter, byte()} option allows line-oriented TCP-based protocols to use a custom line delimiting character. It is to be used in conjunction with {packet, line}. This option also works with erlang:decode_packet/3 when its first argument is 'line'. --- erts/doc/src/erlang.xml | 4 ++++ erts/emulator/beam/atom.names | 1 + erts/emulator/beam/erl_bif_port.c | 10 ++++++++-- erts/emulator/beam/packet_parser.c | 5 +++-- erts/emulator/beam/packet_parser.h | 3 ++- erts/emulator/drivers/common/inet_drv.c | 11 ++++++++++- erts/preloaded/ebin/prim_inet.beam | Bin 72748 -> 72712 bytes erts/preloaded/src/prim_inet.erl | 3 +++ 8 files changed, 31 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 0492924164..1963b27915 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -933,6 +933,10 @@ if packet_size itself is not set. This use is only intended for backward compatibility.

+ {line_delimiter, 0 ≤ char() ≤ 255} +

For packet type line, sets delimiting character. + Default $\n.

+

Examples:

diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index f9a2f3e33e..190e7817dc 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -321,6 +321,7 @@ atom ldflags
 atom Le='=<'
 atom lf
 atom line
+atom line_delimiter
 atom line_length
 atom linked_in_driver
 atom links
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 3ff54c7a60..e47d7bcbbb 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -1329,7 +1329,8 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3)
     ErlSubBin* rest;
     Eterm res;
     Eterm options;
-    int code;
+    int   code;
+    char  delimiter = '\n';
 
     if (!is_binary(BIF_ARG_2) || 
         (!is_list(BIF_ARG_3) && !is_nil(BIF_ARG_3))) {
@@ -1370,6 +1371,11 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3)
                 case am_line_length:
                     trunc_len = val;
                     goto next_option;
+                case am_line_delimiter:
+                    if (type == TCP_PB_LINE_LF && val >= 0 && val <= 255) {
+                        delimiter = (char)val;
+                        goto next_option;
+                    }
                 }
             }
         }
@@ -1390,7 +1396,7 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3)
         pca.aligned_ptr = bin_ptr;
     }
     packet_sz = packet_get_length(type, (char*)pca.aligned_ptr, pca.bin_sz,
-                                  max_plen, trunc_len, &http_state);
+                                  max_plen, trunc_len, delimiter, &http_state);
     if (!(packet_sz > 0 && packet_sz <= pca.bin_sz)) {
         if (packet_sz < 0) {
 	    goto error;
diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c
index 2dd421a9e9..a737a86f14 100644
--- a/erts/emulator/beam/packet_parser.c
+++ b/erts/emulator/beam/packet_parser.c
@@ -256,6 +256,7 @@ int packet_get_length(enum PacketParseType htype,
                       const char* ptr, unsigned n, /* Bytes read so far */
                       unsigned max_plen,     /* Max packet length, 0=no limit */
                       unsigned trunc_len,    /* Truncate (lines) if longer, 0=no limit */
+                      char     delimiter,    /* Line delimiting character */
                       int*     statep)       /* Protocol specific state */
 {
     unsigned hlen, plen;
@@ -299,9 +300,9 @@ int packet_get_length(enum PacketParseType htype,
         goto remain;
 
     case TCP_PB_LINE_LF: {
-        /* TCP_PB_LINE_LF:  [Data ... \n]  */
+        /* TCP_PB_LINE_LF:  [Data ... Delimiter]  */
         const char* ptr2;
-        if ((ptr2 = memchr(ptr, '\n', n)) == NULL) {
+        if ((ptr2 = memchr(ptr, delimiter, n)) == NULL) {
             if (n > max_plen && max_plen != 0) { /* packet full */
                 DEBUGF((" => packet full (no NL)=%d\r\n", n));
                 goto error;
diff --git a/erts/emulator/beam/packet_parser.h b/erts/emulator/beam/packet_parser.h
index ff158ff8b8..717d905fad 100644
--- a/erts/emulator/beam/packet_parser.h
+++ b/erts/emulator/beam/packet_parser.h
@@ -105,7 +105,8 @@ int packet_get_length(enum PacketParseType htype,
 		      const char* ptr, unsigned n,  /* Bytes read so far */
 		      unsigned max_plen,      /* Packet max length, 0=no limit */
 		      unsigned trunc_len,     /* Truncate (lines) if longer, 0=no limit */
-		      int* statep);           /* Internal protocol state */
+		      char     delimiter,     /* Line delimiting character */
+		      int*     statep);       /* Internal protocol state */
 
 ERTS_GLB_INLINE
 void packet_get_body(enum PacketParseType htype,
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 89b71aa66a..a829599fe5 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -885,6 +885,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
 #define INET_LOPT_MSGQ_LOWTRMRK     37  /* set local msgq low watermark */
 #define INET_LOPT_NETNS             38  /* Network namespace pathname */
 #define INET_LOPT_TCP_SHOW_ECONNRESET 39  /* tell user about incoming RST */
+#define INET_LOPT_LINE_DELIM        40  /* Line delimiting char */
 /* SCTP options: a separate range, from 100: */
 #define SCTP_OPT_RTOINFO		100
 #define SCTP_OPT_ASSOCINFO		101
@@ -1154,6 +1155,7 @@ typedef struct {
 #else
     Uint32        send_oct[2];  /* number of octets sent, 64 bits */
 #endif
+    char          delimiter;    /* Line delimiting character (def: '\n')  */
     unsigned long send_cnt;     /* number of packets sent */
     unsigned long send_max;     /* maximum packet send */
     double send_avg;            /* average packet size sent */
@@ -6276,6 +6278,12 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
 	    }
 	    continue;
 
+	case INET_LOPT_LINE_DELIM:
+	    DEBUGF(("inet_set_opts(%ld): s=%d, LINE_DELIM=%d\r\n",
+		    (long)desc->port, desc->s, ival));
+	    desc->delimiter = (char)ival;
+	    continue;
+
 	case INET_OPT_REUSEADDR: 
 #ifdef __WIN32__
 	    continue;  /* Bjorn says */
@@ -8371,6 +8379,7 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)
     desc->deliver = INET_DELIVER_TERM; /* standard term format */
     desc->active  = INET_PASSIVE;      /* start passive */
     desc->active_count = 0;
+    desc->delimiter    = '\n';         /* line delimiting char */
     desc->oph = NULL;
     desc->opt = NULL;
 
@@ -9882,7 +9891,7 @@ static int tcp_remain(tcp_descriptor* desc, int* len)
 
     tlen = packet_get_length(desc->inet.htype, ptr, n, 
                              desc->inet.psize, desc->i_bufsz,
-                             &desc->http_state);
+                             desc->inet.delimiter, &desc->http_state);
 
     DEBUGF(("tcp_remain(%ld): s=%d, n=%d, nfill=%d nsz=%d, tlen %d\r\n",
 	    (long)desc->inet.port, desc->inet.s, n, nfill, nsz, tlen));
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index 5a188be3ba..8b87d1ae26 100644
Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 4d04e1dacb..d5c8fd4268 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -1147,6 +1147,7 @@ enc_opt(packet_size)     -> ?INET_LOPT_PACKET_SIZE;
 enc_opt(read_packets)    -> ?INET_LOPT_READ_PACKETS;
 enc_opt(netns)           -> ?INET_LOPT_NETNS;
 enc_opt(show_econnreset) -> ?INET_LOPT_TCP_SHOW_ECONNRESET;
+enc_opt(line_delimiter)  -> ?INET_LOPT_LINE_DELIM;
 enc_opt(raw)             -> ?INET_OPT_RAW;
 % Names of SCTP opts:
 enc_opt(sctp_rtoinfo)	 	   -> ?SCTP_OPT_RTOINFO;
@@ -1205,6 +1206,7 @@ dec_opt(?INET_LOPT_PACKET_SIZE)      -> packet_size;
 dec_opt(?INET_LOPT_READ_PACKETS)     -> read_packets;
 dec_opt(?INET_LOPT_NETNS)           -> netns;
 dec_opt(?INET_LOPT_TCP_SHOW_ECONNRESET) -> show_econnreset;
+dec_opt(?INET_LOPT_LINE_DELIM)      -> line_delimiter;
 dec_opt(?INET_OPT_RAW)              -> raw;
 dec_opt(I) when is_integer(I)     -> undefined.
 
@@ -1287,6 +1289,7 @@ type_opt_1(packet) ->
 	   {httph_bin,?TCP_PB_HTTPH_BIN},
 	   {ssl, ?TCP_PB_SSL_TLS}, % obsolete
 	   {ssl_tls, ?TCP_PB_SSL_TLS}]};
+type_opt_1(line_delimiter)  -> int;
 type_opt_1(mode) ->
     {enum,[{list, ?INET_MODE_LIST},
 	   {binary, ?INET_MODE_BINARY}]};
-- 
cgit v1.2.3


From 18270f5c208149323964ff9057b1b740cb72ea85 Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin 
Date: Fri, 16 Oct 2015 11:43:20 +0200
Subject: erts: Clarify documentation

---
 erts/doc/src/erlang.xml | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

(limited to 'erts')

diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 1963b27915..70d000f763 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -874,10 +874,10 @@
           
           line
           
-            

A packet is a line terminated with newline. The - newline character is included in the returned packet - unless the line was truncated according to option - line_length.

+

A packet is a line terminated by a delimiter byte, + default is the latin1 newline character. The delimiter + byte is included in the returned packet unless the line + was truncated according to option line_length.

asn1 | cdr | sunrm | fcgi | tpkt @@ -933,9 +933,9 @@ if packet_size itself is not set. This use is only intended for backward compatibility.

- {line_delimiter, 0 ≤ char() ≤ 255} -

For packet type line, sets delimiting character. - Default $\n.

+ {line_delimiter, 0 =< byte() =< 255} +

For packet type line, sets the delimiting byte. + Default is the latin1 character $\n.

Examples:

-- cgit v1.2.3 From fb1c22169c96b51f331c0e7885cdbd0806585da2 Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Mon, 26 Oct 2015 15:55:49 +0000 Subject: Make erl -make return non-zero exit code on failure This makes it behave like similar Unix tools. --- erts/etc/common/erlexec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index b68e109b43..60aefc9fa7 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -715,7 +715,7 @@ int main(int argc, char **argv) * on itself here. We'll avoid doing that. */ if (strcmp(argv[i], "-make") == 0) { - add_args("-noshell", "-noinput", "-s", "make", "all", NULL); + add_args("-noshell", "-noinput", "-s", "make", "all_or_nothing", NULL); add_Eargs("-B"); haltAfterwards = 1; i = argc; /* Skip rest of command line */ -- cgit v1.2.3 From 15af6b28beded3b253860b4ea5a7debe2c662674 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 2 Nov 2015 17:29:54 +0100 Subject: erts: Remove faulty ASSERT in erts_proc_*_refc There is no guarantee that the ptab-slot in not reused when we finally deallocates the process struct. --- erts/emulator/beam/erl_process_lock.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index 788348e613..a64c993e8f 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -854,9 +854,6 @@ ERTS_GLB_INLINE void erts_proc_dec_refc(Process *p) #endif if (!referred) { ASSERT(ERTS_PROC_IS_EXITING(p)); - ASSERT(ERTS_AINT_NULL - == erts_ptab_pix2intptr_ddrb(&erts_proc, - internal_pid_index(p->common.id))); erts_free_proc(p); } } @@ -872,9 +869,6 @@ ERTS_GLB_INLINE void erts_proc_add_refc(Process *p, Sint add_refc) #endif if (!referred) { ASSERT(ERTS_PROC_IS_EXITING(p)); - ASSERT(ERTS_AINT_NULL - == erts_ptab_pix2intptr_ddrb(&erts_proc, - internal_pid_index(p->common.id))); erts_free_proc(p); } } -- cgit v1.2.3 From 1eeb980a3591b51f12363543b2ac9b0260ebd870 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 30 Oct 2015 19:09:14 +0100 Subject: erts: Remove ERTS_PSD_DIST_ENTRY Not needed as it is always set to erts_this_dist_entry (on net_kernel). --- erts/emulator/beam/dist.c | 9 ++------- erts/emulator/beam/erl_node_tables.c | 6 ------ erts/emulator/beam/erl_process.c | 11 +---------- erts/emulator/beam/erl_process.h | 17 ++++------------- 4 files changed, 7 insertions(+), 36 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 23897a49ae..5a0f8388f8 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -2650,13 +2650,8 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2) if (!net_kernel) goto error; - /* By setting dist_entry==erts_this_dist_entry and DISTRIBUTION on - net_kernel do_net_exist will be called when net_kernel - is terminated !! */ - (void) ERTS_PROC_SET_DIST_ENTRY(net_kernel, - ERTS_PROC_LOCK_MAIN, - erts_this_dist_entry); - erts_refc_inc(&erts_this_dist_entry->refc, 2); + /* By setting F_DISTRIBUTION on net_kernel, + * do_net_exist will be called when net_kernel is terminated !! */ net_kernel->flags |= F_DISTRIBUTION; if (net_kernel != BIF_P) diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 2fb790b953..865de1726d 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -1472,12 +1472,6 @@ setup_reference_table(void) insert_links(ERTS_P_LINKS(proc), proc->common.id); if (ERTS_P_MONITORS(proc)) insert_monitors(ERTS_P_MONITORS(proc), proc->common.id); - /* Insert controller */ - { - DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(proc); - if (dep) - insert_dist_entry(dep, CTRL_REF, proc->common.id, 0); - } } } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 3b1b593d1c..d583118e7b 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -625,11 +625,6 @@ erts_pre_init_process(void) erts_psd_required_locks[ERTS_PSD_SCHED_ID].set_locks = ERTS_PSD_SCHED_ID_SET_LOCKS; - erts_psd_required_locks[ERTS_PSD_DIST_ENTRY].get_locks - = ERTS_PSD_DIST_ENTRY_GET_LOCKS; - erts_psd_required_locks[ERTS_PSD_DIST_ENTRY].set_locks - = ERTS_PSD_DIST_ENTRY_SET_LOCKS; - erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].get_locks = ERTS_PSD_CALL_TIME_BP_GET_LOCKS; erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].set_locks @@ -12373,9 +12368,7 @@ erts_continue_exit_process(Process *p) erts_proc_dec_refc(p); } - dep = ((p->flags & F_DISTRIBUTION) - ? ERTS_PROC_SET_DIST_ENTRY(p, ERTS_PROC_LOCKS_ALL, NULL) - : NULL); + dep = (p->flags & F_DISTRIBUTION) ? erts_this_dist_entry : NULL; scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, ERTS_PROC_LOCKS_ALL, NULL); pbt = ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCKS_ALL, NULL); nif_export = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, ERTS_PROC_LOCKS_ALL, NULL); @@ -12387,8 +12380,6 @@ erts_continue_exit_process(Process *p) if (dep) { erts_do_net_exits(dep, reason); - if(dep) - erts_deref_dist_entry(dep); } /* diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 20ffe7ea7c..10c6fa4a67 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -801,12 +801,11 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi) #define ERTS_PSD_ERROR_HANDLER 0 #define ERTS_PSD_SAVED_CALLS_BUF 1 #define ERTS_PSD_SCHED_ID 2 -#define ERTS_PSD_DIST_ENTRY 3 -#define ERTS_PSD_CALL_TIME_BP 4 -#define ERTS_PSD_DELAYED_GC_TASK_QS 5 -#define ERTS_PSD_NIF_TRAP_EXPORT 6 +#define ERTS_PSD_CALL_TIME_BP 3 +#define ERTS_PSD_DELAYED_GC_TASK_QS 4 +#define ERTS_PSD_NIF_TRAP_EXPORT 5 -#define ERTS_PSD_SIZE 7 +#define ERTS_PSD_SIZE 6 typedef struct { void *data[ERTS_PSD_SIZE]; @@ -824,9 +823,6 @@ typedef struct { #define ERTS_PSD_SCHED_ID_GET_LOCKS ERTS_PROC_LOCK_STATUS #define ERTS_PSD_SCHED_ID_SET_LOCKS ERTS_PROC_LOCK_STATUS -#define ERTS_PSD_DIST_ENTRY_GET_LOCKS ERTS_PROC_LOCK_MAIN -#define ERTS_PSD_DIST_ENTRY_SET_LOCKS ERTS_PROC_LOCK_MAIN - #define ERTS_PSD_CALL_TIME_BP_GET_LOCKS ERTS_PROC_LOCK_MAIN #define ERTS_PSD_CALL_TIME_BP_SET_LOCKS ERTS_PROC_LOCK_MAIN @@ -1887,11 +1883,6 @@ erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *data) #define ERTS_PROC_SCHED_ID(P, L, ID) \ ((UWord) erts_psd_set((P), (L), ERTS_PSD_SCHED_ID, (void *) (ID))) -#define ERTS_PROC_GET_DIST_ENTRY(P) \ - ((DistEntry *) erts_psd_get((P), ERTS_PSD_DIST_ENTRY)) -#define ERTS_PROC_SET_DIST_ENTRY(P, L, D) \ - ((DistEntry *) erts_psd_set((P), (L), ERTS_PSD_DIST_ENTRY, (void *) (D))) - #define ERTS_PROC_GET_SAVED_CALLS_BUF(P) \ ((struct saved_calls *) erts_psd_get((P), ERTS_PSD_SAVED_CALLS_BUF)) #define ERTS_PROC_SET_SAVED_CALLS_BUF(P, L, SCB) \ -- cgit v1.2.3 From 575b67139d8b1c6dee64b5ee239e3e7b5402959b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20G=C3=B6m=C3=B6ri?= Date: Thu, 8 Oct 2015 14:52:37 +0200 Subject: Fix typo in trace gc_start doc Conflicts: erts/doc/src/erlang.xml --- erts/doc/src/erlang.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 70d000f763..0d0672de3e 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -8228,7 +8228,7 @@ timestamp() -> bin_old_vheap_size The total size of unique off-heap binaries referenced from the process old heap. - bin_vheap_block_size + bin_old_vheap_block_size The total size of binaries allowed in the virtual old heap in the process before doing a garbage collection. -- cgit v1.2.3 From 5ae78de4e4f6e220e29ad2f54b95a0098da4b365 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 12 Oct 2015 12:12:27 +0200 Subject: [erts] Correct documentation Fix mistakes found by 'xmllint'. --- erts/doc/src/driver_entry.xml | 5 ++- erts/doc/src/erl.xml | 83 +++++++++++++++++++------------------- erts/doc/src/erl_dist_protocol.xml | 4 +- erts/doc/src/erl_driver.xml | 16 ++++---- erts/doc/src/erl_ext_dist.xml | 4 +- erts/doc/src/erl_nif.xml | 7 ++-- erts/doc/src/erlang.xml | 37 +++++++++-------- erts/doc/src/erts_alloc.xml | 74 ++++++++++++++++----------------- erts/doc/src/escript.xml | 6 +-- erts/doc/src/notes.xml | 49 ++++++++++++---------- erts/doc/src/time_correction.xml | 4 +- 11 files changed, 148 insertions(+), 141 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml index 30772c68fe..bad20d6343 100644 --- a/erts/doc/src/driver_entry.xml +++ b/erts/doc/src/driver_entry.xml @@ -4,7 +4,7 @@
- 20012013 + 20012015 Ericsson AB. All Rights Reserved. @@ -126,7 +126,7 @@
DATA TYPES - ErlDrvEntry + ErlDrvEntry

@@ -235,6 +235,7 @@ typedef struct erl_drv_entry { void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event) + void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event)

This is called when a driver event (given in the diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index b0322b7d43..63ab2532ab 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -4,7 +4,7 @@

- 19962013 + 19962015 Ericsson AB. All Rights Reserved. @@ -138,7 +138,7 @@ see app(4) and application(3).

- +

Command line arguments are read from the file . The arguments read from the file replace the @@ -203,7 +203,7 @@ app(4) and application(3).

- +

If this flag is present, will not maintain a fully connected network of distributed Erlang nodes, and then @@ -288,7 +288,7 @@

Makes write some debug information while interpreting the boot script.

- (emulator flag) + (emulator flag)

Selects an instrumented Erlang runtime system (virtual machine) to run, instead of the ordinary one. When running an @@ -436,7 +436,7 @@ flag and those running with the flag, as node names must be unique in distributed Erlang systems.

- +

-smp enable and -smp starts the Erlang runtime system with SMP support enabled. This may fail if no runtime @@ -462,7 +462,7 @@

invokes the code for the Erlang emulator (virtual machine), which supports the following flags:

- +

Suggested stack size, in kilowords, for threads in the async-thread pool. Valid range is 16-8192 kilowords. The @@ -477,7 +477,7 @@ suggestion, and it might even be ignored on some platforms.

- +

Sets the number of threads in async thread pool, valid range is 0-1024. If thread support is available, the default is 10.

@@ -496,7 +496,7 @@ , not (). Note also that is used instead of on Windows.

- +

Enable or disable time correction:

@@ -512,7 +512,7 @@ This is interpreted as +c false.

- +

Set time warp mode: @@ -540,7 +540,7 @@ produce a crash dump. On Unix systems, sending an emulator process a SIGUSR1 signal will also force a crash dump.

- +

Set max number of ETS tables.

@@ -625,7 +625,7 @@ information about the file names and line numbers.

- +

Memory allocator specific flags, see erts_alloc(3) for @@ -664,10 +664,10 @@ debugging. - +

Sets the range of characters that the system will consider printable in heuristic detection of strings. This typically affects the shell, debugger and io:format functions (when ~tp is used in the format string).

-

Currently two values for the Range are supported: +

Currently two values for the Range are supported:

latin1 The default. Only characters in the ISO-latin-1 range can be considered printable, which means @@ -682,11 +682,10 @@ example your font does not cover all Unicode characters. -

Se also io:printable_range/0.

- +

Sets the maximum number of simultaneously existing processes for this system if a Number is passed as value. Valid range for @@ -706,7 +705,7 @@ circumstances be extremely expensive. The legacy algoritm is deprecated, and the legacy option is scheduled for removal in OTP-R18.

- +

Sets the maximum number of simultaneously existing ports for this system if a Number is passed as value. Valid range for Number @@ -737,7 +736,7 @@ circumstances be extremely expensive. The legacy algoritm is deprecated, and the legacy option is scheduled for removal in OTP-R18.

- +

Sets the compatibility mode.

The distribution mechanism is not backwards compatible by @@ -757,7 +756,7 @@

Force ets memory block to be moved on realloc.

- +

Limits the amount of reader groups used by read/write locks optimized for read operations in the Erlang runtime system. By @@ -775,7 +774,7 @@ schedulers to logical processors, since the reader groups are distributed better between schedulers.

- +

Sets the number of scheduler threads to create and scheduler threads to set online when SMP support has been enabled. The maximum for @@ -800,7 +799,7 @@ SMP support enabled (see the -smp flag).

- +

Similar to +S but uses percentages to set the number of scheduler threads to create, based on logical processors configured, @@ -821,7 +820,7 @@ SMP support enabled (see the -smp flag).

- +

Sets the number of dirty CPU scheduler threads to create and dirty CPU scheduler threads to set online when threading support has been @@ -845,7 +844,7 @@ enabled (it's disabled by default).

- +

Similar to +SDcpu but uses percentages to set the number of dirty CPU scheduler threads to create and number of dirty CPU scheduler threads @@ -868,7 +867,7 @@ enabled (it's disabled by default).

- +

Sets the number of dirty I/O scheduler threads to create when threading support has been enabled. The valid range is 0-1024. By default, the number @@ -886,7 +885,7 @@

Scheduling specific flags.

- +sbt BindType + +sbt BindType

Set scheduler bind type.

Schedulers can also be bound using the @@ -1010,7 +1009,7 @@ erlang:system_info(scheduler_bindings).

- +sbwt none|very_short|short|medium|long|very_long + +sbwt none|very_short|short|medium|long|very_long

Set scheduler busy wait threshold. Default is medium. The threshold determines how long schedulers should busy @@ -1020,7 +1019,7 @@ without prior notice.

- +scl true|false + +scl true|false

Enable or disable scheduler compaction of load. By default scheduler compaction of load is enabled. When enabled, load @@ -1037,7 +1036,7 @@ between schedulers.

- +sct CpuTopology + +sct CpuTopology = integer(); when 0 =< =< 65535]]> @@ -1159,7 +1158,7 @@

For more information, see erlang:system_info(cpu_topology).

- +secio true|false + +secio true|false

Enable or disable eager check I/O scheduling. The default is currently true. The default was changed from false @@ -1176,7 +1175,7 @@

erlang:system_info(eager_check_io) returns the value of this parameter used when starting the VM.

- +sfwi Interval + +sfwi Interval

Set scheduler forced wakeup interval. All run queues will be scanned each Interval milliseconds. While there are @@ -1190,7 +1189,7 @@ the +sfwi flag will be removed.

- +stbt BindType + +stbt BindType

Try to set scheduler bind type. The same as the +sbt flag with the exception of @@ -1198,7 +1197,7 @@ documentation of the +sbt flag.

- +sub true|false + +sub true|false

Enable or disable scheduler @@ -1221,7 +1220,7 @@ utilization.

- +swct very_eager|eager|medium|lazy|very_lazy + +swct very_eager|eager|medium|lazy|very_lazy

Set scheduler wake cleanup threshold. Default is medium. @@ -1235,7 +1234,7 @@

NOTE: This flag may be removed or changed at any time without prior notice.

- +sws default|legacy + +sws default|legacy

Set scheduler wakeup strategy. Default strategy changed in erts-5.10/OTP-R16A. This strategy was previously known as proposal in OTP-R15. The legacy strategy was used as default from R13 up to and including R15. @@ -1243,7 +1242,7 @@

NOTE: This flag may be removed or changed at any time without prior notice.

- +swt very_low|low|medium|high|very_high + +swt very_low|low|medium|high|very_high

Set scheduler wakeup threshold. Default is medium. The threshold determines when to wake up sleeping schedulers @@ -1257,7 +1256,7 @@ without prior notice.

- +spp Bool + +spp Bool

Set default scheduler hint for port parallelism. If set to true, the VM will schedule port tasks when doing so will @@ -1273,7 +1272,7 @@ option to open_port/2

.
- +

Suggested stack size, in kilowords, for scheduler threads. Valid range is 4-8192 kilowords. The default stack size @@ -1281,11 +1280,11 @@ - +

Set the maximum number of atoms the VM can handle. Default is 1048576.

- +

Enables modified timing and sets the modified timing level. Currently valid range is 0-9. The timing of the runtime system @@ -1339,7 +1338,7 @@

Miscellaneous flags.

- +zdbbl size + +zdbbl size

Set the distribution buffer busy limit (dist_buf_busy_limit) @@ -1352,7 +1351,7 @@ give lower latency and higher throughput at the expense of higher memory usage.

- +zdntgc time + +zdntgc time

Set the delayed node table garbage collection time (delayed_node_table_gc) @@ -1426,7 +1425,7 @@ - +

The content of this environment variable will be added to the beginning of the command line for .

@@ -1436,7 +1435,7 @@ the section, i.e. the end of the command line following after an flag.

- and + and

The content of these environment variables will be added to the end of the command line for .

diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml index e1a58856f3..b435d5c9b4 100644 --- a/erts/doc/src/erl_dist_protocol.xml +++ b/erts/doc/src/erl_dist_protocol.xml @@ -5,7 +5,7 @@
2007 - 2013 + 2015 Ericsson AB, All Rights Reserved @@ -549,10 +549,10 @@ If Result > 0, the packet only consists of [119, Result]. -->
-
Distribution Handshake

+ This section describes the distribution handshake protocol introduced in the OTP-R6 release of Erlang/OTP. This description was previously located in diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 1f7fe0f961..42b6a3bfef 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -4,7 +4,7 @@

- 20012014 + 20012015 Ericsson AB. All Rights Reserved. @@ -223,7 +223,7 @@ asynchronous function calls, using a thread pool provided by Erlang. There is also a select call, that can be used for asynchronous drivers. - Multi-threading + Multi-threading

A POSIX thread like API for multi-threading is provided. The Erlang driver thread API only provide a subset of the functionality @@ -297,7 +297,7 @@

A driver can add and later remove drivers.

Monitoring processes

A driver can monitor a process that does not own a port.

- Version management + Version management

Version management is enabled for drivers that have set the extended_marker @@ -384,12 +384,12 @@

Rewrite driver callback - control + control to use return type ErlDrvSSizeT instead of int.

Rewrite driver callback - call + call to use return type ErlDrvSSizeT instead of int.

@@ -407,19 +407,19 @@

Driver callback - output + output now gets ErlDrvSizeT as 3rd argument instead of previously int.

Driver callback - control + control now gets ErlDrvSizeT as 4th and 6th arguments instead of previously int.

Driver callback - call + call now gets ErlDrvSizeT as 4th and 6th arguments instead of previously int.

diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml index caf1e812c4..2ac974f497 100644 --- a/erts/doc/src/erl_ext_dist.xml +++ b/erts/doc/src/erl_ext_dist.xml @@ -5,7 +5,7 @@
2007 - 2014 + 2015 Ericsson AB, All Rights Reserved @@ -150,10 +150,10 @@
-
Distribution header

+ As of erts version 5.7.2 the old atom cache protocol was dropped and a new one was introduced. This atom cache protocol introduced the distribution header. Nodes with erts versions diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 23c3d5fcee..dae14b8d08 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -4,7 +4,7 @@

- 20012013 + 20012015 Ericsson AB. All Rights Reserved. @@ -833,9 +833,10 @@ typedef enum { Determine if a term is an empty list

Return true if term is an empty list.

- intenif_is_exception(ErlNifEnv* env, ERL_NIF_TERM term) + intenif_is_exception(ErlNifEnv* env, ERL_NIF_TERM term) Determine if a term is an exception -

Return true if term is an exception.

+ +

Return true if term is an exception.

intenif_is_map(ErlNifEnv* env, ERL_NIF_TERM term) Determine if a term is a map diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 70d000f763..df7af3ce6b 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4,7 +4,7 @@
- 19962013 + 19962015 Ericsson AB. All Rights Reserved. @@ -63,10 +63,10 @@

See erlang:timestamp/0.

- -

Supported time unit representations:

+

+ Supported time unit representations:

PartsPerSecond :: integer() >= 1

Time unit expressed in parts per second. That is, @@ -439,7 +439,7 @@ Converts part of a binary to a list. - 1..byte_size(Binary) + 1..byte_size(Binary)

As binary_to_list/1, but returns a list of integers corresponding to the bytes from position Start to @@ -997,7 +997,7 @@ the call, though. It is therefore usually advisable to remove such a 'DOWN' message from the message queue after monitoring has been stopped. - demonitor(MonitorRef, [flush]) + demonitor(MonitorRef, [flush]) can be used instead of demonitor(MonitorRef) if this cleanup is wanted.

@@ -1026,7 +1026,7 @@

The returned value is true unless info is part of OptionList.

demonitor(MonitorRef, []) is equivalent to - demonitor(MonitorRef).

+ demonitor(MonitorRef).

The available Options are as follows:

flush @@ -1114,8 +1114,8 @@ - 1..tuple_size(Tuple) Returns the Nth element of a tuple. + 1..tuple_size(Tuple)

Returns the Nth element (numbering from 1) of Tuple, for example:

@@ -2855,10 +2855,10 @@ os_prompt%
+ Starts monitoring. - Starts monitoring.

Send a monitor request of type Type to the entity identified by Item. The caller of @@ -3581,8 +3581,8 @@ os_prompt%

- Range = 1..2^32, Hash = 1..Range Portable hash function. + Range = 1..2^32, Hash = 1..Range

Portable hash function that gives the same hash for the same Erlang term regardless of machine architecture and @@ -3600,9 +3600,9 @@ os_prompt% + Portable hash function. 1..2^32 0..Range-1 - Portable hash function.

Portable hash function that gives the same hash for the same Erlang term regardless of machine architecture and @@ -5240,8 +5240,8 @@ true - 1..tuple_size(Tuple1 Sets the Nth element of a tuple. + 1..tuple_size(Tuple1

Returns a tuple that is a copy of argument Tuple1 @@ -5582,8 +5582,8 @@ true - 0..byte_size(Bin) Splits a binary into two. + 0..byte_size(Bin)

Returns a tuple containing the binaries that are the result of splitting Bin into two parts at @@ -6475,7 +6475,7 @@ ok

Returns various size information for the specified allocator. The information returned is a subset of the information returned by - erlang:system_info({allocator, Alloc}). + erlang:system_info({allocator, Alloc}).

@@ -6855,7 +6855,7 @@ ok The return value will always be false, as the elib_malloc allocator has been removed.

- eager_check_io + eager_check_io

Returns the value of the erl command line flag @@ -7058,7 +7058,7 @@ ok of versions in System principles in System Documentation.

- os_monotonic_time_source + os_monotonic_time_source

Returns a list containing information about the source of OS @@ -7121,7 +7121,7 @@ ok time unit.

- os_system_time_source + os_system_time_source

Returns a list containing information about the source of OS @@ -7298,7 +7298,6 @@ ok erlang:system_info(schedulers) and erlang:system_flag(schedulers_online, SchedulersOnline).

-
smp_support @@ -7486,7 +7485,7 @@ ok system performance monitoring settings are cleared.

Calling the function with {MonitorPid, Options} as argument is the same as calling - erlang:system_monitor(MonitorPid, Options).

+ erlang:system_monitor(MonitorPid, Options).

Returns the previous system monitor settings just like erlang:system_monitor/0.

@@ -7857,8 +7856,8 @@ ok
- Current Erlang System time +

Returns current Erlang system time diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 376cae4a95..15b78ffa10 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -4,7 +4,7 @@

- 20022014 + 20022015 Ericsson AB. All Rights Reserved. @@ -260,19 +260,19 @@

The following flags are available for configuration of mseg_alloc:

- ]]> + ]]> Absolute max cache bad fit (in kilobytes). A segment in the memory segment cache is not reused if its size exceeds the requested size with more than the value of this parameter. Default value is 4096. - ]]> + ]]> Relative max cache bad fit (in percent). A segment in the memory segment cache is not reused if its size exceeds the requested size with more than relative max cache bad fit percent of the requested size. Default value is 20. - + Set super carrier only flag. This flag defaults to true. When a super carrier is used and this @@ -292,7 +292,7 @@ disabled on halfword heap systems. This flag will be ignored on halfword heap systems. - ]]> + ]]> Set super carrier reserved free segment descriptors. This parameter defaults to 65536. @@ -305,7 +305,7 @@ erts_mmap tuple part of the result from calling erlang:system_info({allocator, mseg_alloc}). - + Set super carrier reserve physical memory flag. This flag defaults to true. When this flag is @@ -328,7 +328,7 @@ disabled on halfword heap systems. This flag will be ignored on halfword heap systems. - ]]> + ]]> Set super carrier size (in MB). The super carrier size defaults to zero; i.e, the super carrier is by default disabled. The super @@ -343,7 +343,7 @@ disabled on halfword heap systems. This flag will be ignored on halfword heap systems. - ]]> + ]]> Max cached segments. The maximum number of memory segments stored in the memory segment cache. Valid range is @@ -352,15 +352,15 @@

The following flags are available for configuration of sys_alloc:

- +MYe true + +MYe true Enable sys_alloc. Note: sys_alloc cannot be disabled. - +MYm libc + +MYm libc malloc library to use. Currently only libc is available. libc enables the standard libc malloc implementation. By default libc is used. - ]]> + ]]> Trim threshold size (in kilobytes). This is the maximum amount of free memory at the top of the heap (allocated by @@ -372,7 +372,7 @@ trim threshold is 128. Note: This flag will only have any effect when the emulator has been linked with the GNU C library, and uses its malloc implementation. - ]]> + ]]> Top pad size (in kilobytes). This is the amount of extra memory that will be allocated by malloc when @@ -390,7 +390,7 @@ subsystem identifier, only the specific allocator identified will be effected:

- acul |de]]> + acul |de]]> Abandon carrier utilization limit. A valid ]]> is an integer in the range @@ -422,7 +422,7 @@ allocators based on the alloc_util framework with the exception of temp_alloc (which would be pointless). - as bf|aobf|aoff|aoffcbf|aoffcaobf|gf|af]]> + as bf|aobf|aoff|aoffcbf|aoffcaobf|gf|af]]> Allocation strategy. Valid strategies are bf (best fit), aobf (address order best fit), aoff (address order first fit), @@ -430,7 +430,7 @@ aoffcaobf (address order first fit carrier address order best fit), gf (good fit), and af (a fit). See the description of allocation strategies in "the alloc_util framework" section. - asbcst ]]> + asbcst ]]> Absolute singleblock carrier shrink threshold (in kilobytes). When a block located in an @@ -438,23 +438,23 @@ will be left unchanged if the amount of unused memory is less than this threshold; otherwise, the carrier will be shrunk. See also rsbcst. - e true|false]]> + e true|false]]> Enable allocator ]]>. - lmbcs ]]> + lmbcs ]]> Largest (mseg_alloc) multiblock carrier size (in kilobytes). See the description on how sizes for mseg_alloc multiblock carriers are decided in "the alloc_util framework" section. On 32-bit Unix style OS this limit can not be set higher than 128 megabyte. - mbcgs ]]> + mbcgs ]]> (mseg_alloc) multiblock carrier growth stages. See the description on how sizes for mseg_alloc multiblock carriers are decided in "the alloc_util framework" section. - mbsd ]]> + mbsd ]]> Max block search depth. This flag has effect only if the good fit strategy has been selected for allocator @@ -464,40 +464,40 @@ search depth sets a limit on the maximum number of blocks to inspect in a free list during a search for suitable block satisfying the request. - mmbcs ]]> + mmbcs ]]> Main multiblock carrier size. Sets the size of the main multiblock carrier for allocator ]]>. The main multiblock carrier is allocated via and is never deallocated. - mmmbc ]]> + mmmbc ]]> Max mseg_alloc multiblock carriers. Maximum number of multiblock carriers allocated via mseg_alloc by allocator ]]>. When this limit has been reached, new multiblock carriers will be allocated via sys_alloc. - mmsbc ]]> + mmsbc ]]> Max mseg_alloc singleblock carriers. Maximum number of singleblock carriers allocated via mseg_alloc by allocator ]]>. When this limit has been reached, new singleblock carriers will be allocated via sys_alloc. - ramv ]]> + ramv ]]> Realloc always moves. When enabled, reallocate operations will more or less be translated into an allocate, copy, free sequence. This often reduce memory fragmentation, but costs performance. - rmbcmt ]]> + rmbcmt ]]> Relative multiblock carrier move threshold (in percent). When a block located in a multiblock carrier is shrunk, the block will be moved if the ratio of the size of the returned memory compared to the previous size is more than this threshold; otherwise, the block will be shrunk at current location. - rsbcmt ]]> + rsbcmt ]]> Relative singleblock carrier move threshold (in percent). When a block located in a singleblock carrier is shrunk to @@ -506,7 +506,7 @@ the block will be left unchanged in the singleblock carrier if the ratio of unused memory is less than this threshold; otherwise, it will be moved into a multiblock carrier. - rsbcst ]]> + rsbcst ]]> Relative singleblock carrier shrink threshold (in percent). When a block located in an mseg_alloc @@ -514,20 +514,20 @@ unchanged if the ratio of unused memory is less than this threshold; otherwise, the carrier will be shrunk. See also asbcst. - sbct ]]> + sbct ]]> Singleblock carrier threshold. Blocks larger than this threshold will be placed in singleblock carriers. Blocks smaller than this threshold will be placed in multiblock carriers. On 32-bit Unix style OS this threshold can not be set higher than 8 megabytes. - smbcs ]]> + smbcs ]]> Smallest (mseg_alloc) multiblock carrier size (in kilobytes). See the description on how sizes for mseg_alloc multiblock carriers are decided in "the alloc_util framework" section. - t true|false]]> + t true|false]]>

Multiple, thread specific instances of the allocator. This option will only have any effect on the runtime system @@ -544,20 +544,20 @@ alloc_util, i.e. all allocators based on alloc_util will be effected:

- ]]> + ]]> sys_alloc carrier size. Carriers allocated via sys_alloc will be allocated in sizes which are multiples of the sys_alloc carrier size. This is not true for main multiblock carriers and carriers allocated during a memory shortage, though. - ]]> + ]]> Max mseg_alloc carriers. Maximum number of carriers placed in separate memory segments. When this limit has been reached, new carriers will be placed in memory retrieved from sys_alloc. - ]]> + ]]> Allow sys_alloc carriers. By default true. If set to false, sys_alloc carriers will never be @@ -565,19 +565,19 @@

Instrumentation flags:

- +Mim true|false + +Mim true|false A map over current allocations is kept by the emulator. The allocation map can be retrieved via the instrument module. +Mim true implies +Mis true. +Mim true is the same as -instr. - +Mis true|false + +Mis true|false Status over allocated memory is kept by the emulator. The allocation status can be retrieved via the instrument module. - +Mit X + +Mit X Reserved for future use. Do not use this flag. @@ -587,7 +587,7 @@

Other flags:

- +Mea min|max|r9c|r10b|r11b|config + +Mea min|max|r9c|r10b|r11b|config min @@ -617,7 +617,7 @@
- +Mlpm all|no + +Mlpm all|no Lock physical memory. The default value is no, i.e., no physical memory will be locked. If set to all, all memory mappings made by the runtime system, will be locked into diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml index 46110333f9..f12f76890c 100644 --- a/erts/doc/src/escript.xml +++ b/erts/doc/src/escript.xml @@ -4,7 +4,7 @@
- 20072014 + 20072015 Ericsson AB. All Rights Reserved. @@ -96,8 +96,8 @@ $ escript factorial 5

The encoding specified by the above mentioned comment applies to the script itself. The encoding of the - I/O-server, however, has to be set explicitly like this: -io:setopts([{encoding, unicode}])

+ I/O-server, however, has to be set explicitly like this:

+io:setopts([{encoding, unicode}])

The default encoding of the I/O-server for standard_io is latin1 since the script runs in a non-interactive terminal diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 3f6d5b1d89..f27e73b9d3 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -4,7 +4,7 @@

- 20042013 + 20042015 Ericsson AB. All Rights Reserved. @@ -708,19 +708,20 @@

- Use persistent hashmaps for large Maps

Maps will use a + Use persistent hashmaps for large Maps

+

Maps will use a persistent hashmap implementation when the number of pairs in a Map becomes sufficiently large. The change will occur when a Map reaches 33 pairs in size but this - limit might change in the future.

-

The most significant impact for the user by this + limit might change in the future.

+

The most significant impact for the user by this change is speed, and to a lesser degree memory consumption and introspection of Maps. Memory consumption size is probalistic but lesser than gb_trees or dict for instance. Any other impacts will be transparent for the user except for the following changes.

-

Semantics of Maps have changed in two incompatible +

Semantics of Maps have changed in two incompatible ways compared to the experimental implementation in OTP 17:

Hashing of maps is done different by erlang:phash2/1,2, erlang:phash/1 and @@ -1368,7 +1369,7 @@

Improved support for atomic memory operations provided by the libatomic_ops + href="https://github.com/ivmai/libatomic_ops/">libatomic_ops library. Most importantly support for use of native double word atomics when implemented by libatomic_ops (for example, implemented for ARM).

@@ -2335,22 +2336,28 @@

EEP43: New data type - Maps

- With Maps you may for instance: M0 = - #{ a => 1, b => 2}, % create - associations M1 = M0#{ a := 10 }, % - update values M2 = M1#{ "hi" => - "hello"}, % add new associations #{ - "hi" := V1, a := V2, b := V3} = M2. % match keys with - values

+ With Maps you may for instance:

+ + M0 = #{ a => 1, b => 2}, % create + associations + M1 = M0#{ a := 10 }, % update values + M2 = M1#{ "hi" => + "hello"}, % add new associations + #{ "hi" := V1, a := V2, b := V3} = M2. + % match keys with values +

For information on how to use Maps please see Map Expressions in the Reference Manual.

The current implementation is without the following - features: No variable keys - No single value access No map - comprehensions

+ features:

+ + No variable keys + No single value access + No map comprehensions +

Note that Maps is experimental during OTP 17.0.

@@ -4510,8 +4517,7 @@

Fix erl_prim_loader errors in handling of primary archive. The following errors have been corrected:

-

- If primary archive was named "xxx", then 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. erl_prim_loader did not correctly create @@ -4526,7 +4532,8 @@ 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.

+ This has been removed.
+

Thanks to Tuncer Ayaz and Shunichi Shinohara for reporting and co-authoring corrections.

@@ -6969,12 +6976,12 @@ Own Id: OTP-8726 Aux Id: seq11617

-

Fix libm linking with --as-needed flag +

Fix libm linking with --as-needed flag

When building with "--as-needed" linker flags on Linux the build will fail. This has now been fixed.

- (Thanks to Christian Faulhammer)

+ (Thanks to Christian Faulhammer)

Own Id: OTP-8728

diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index 4de3739a36..236fe679cb 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -4,7 +4,7 @@
- 19992014 + 19992015 Ericsson AB. All Rights Reserved. @@ -897,6 +897,6 @@ EventTag = {Time, UMI} and using these wrappers instead of using the API directly, the problem is solved. These wrappers can, for example, be implemented as in - $ERL_TOP/erts/example/time_compat.erl.

+ $ERL_TOP/erts/example/time_compat.erl.

-- cgit v1.2.3 From b93e9b611056828ac2c82f225960aa29348ebe97 Mon Sep 17 00:00:00 2001 From: Andrew Bennett Date: Wed, 10 Jun 2015 13:48:30 -0600 Subject: stdlib: Add BIF binary:split/2 and binary:split/3 --- erts/emulator/beam/atom.names | 2 + erts/emulator/beam/bif.tab | 7 + erts/emulator/beam/erl_bif_binary.c | 697 ++++++++++++++++++++++++++++++++++++ 3 files changed, 706 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index f9a2f3e33e..3d357886ee 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -121,6 +121,7 @@ atom binary_longest_prefix_trap atom binary_longest_suffix_trap atom binary_match_trap atom binary_matches_trap +atom binary_split_trap atom binary_to_list_continue atom binary_to_term_trap atom block @@ -584,6 +585,7 @@ atom trace trace_ts traced atom trace_control_word atom tracer atom trap_exit +atom trim atom try_clause atom true atom tuple diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 4f0656d174..65f8d6f1f5 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -643,3 +643,10 @@ bif erts_debug:map_info/1 # bif erlang:hash/2 + +# +# New in 19.0 +# + +bif binary:split/2 +bif binary:split/3 diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 134aa2d396..68e5fe23c7 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -67,12 +67,16 @@ static Export binary_bin_to_list_trap_export; static BIF_RETTYPE binary_bin_to_list_trap(BIF_ALIST_3); static Export binary_copy_trap_export; static BIF_RETTYPE binary_copy_trap(BIF_ALIST_2); +static Export binary_split_trap_export; +static BIF_RETTYPE binary_split_trap(BIF_ALIST_3); static Uint max_loop_limit; static BIF_RETTYPE binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3); static BIF_RETTYPE binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3); +static BIF_RETTYPE +binary_split(Process *p, Eterm arg1, Eterm arg2, Eterm arg3); void erts_init_bif_binary(void) { @@ -100,6 +104,10 @@ void erts_init_bif_binary(void) am_erlang, am_binary_copy_trap, 2, &binary_copy_trap); + erts_init_trap_export(&binary_split_trap_export, + am_erlang, am_binary_split_trap, 3, + &binary_split_trap); + max_loop_limit = 0; return; } @@ -2534,6 +2542,695 @@ BIF_RETTYPE binary_copy_2(BIF_ALIST_2) return do_binary_copy(BIF_P,BIF_ARG_1,BIF_ARG_2); } +#define BINARY_SPLIT_GLOBAL 0x01 +#define BINARY_SPLIT_TRIM 0x02 + +static int do_binary_split(Process *p, Eterm subject, Uint hsstart, + Uint hsend, Uint hsflags, Eterm type, Binary *bin, + Eterm state_term, Eterm *res_term) +{ + byte *bytes; + Uint bitoffs, bitsize; + byte *temp_alloc = NULL; + + ERTS_GET_BINARY_BYTES(subject, bytes, bitoffs, bitsize); + if (bitsize != 0) { + goto badarg; + } + if (bitoffs != 0) { + bytes = erts_get_aligned_binary_bytes(subject, &temp_alloc); + } + if (state_term != NIL) { + Eterm *ptr = big_val(state_term); + type = ptr[1]; + hsflags = (Uint)(ptr[2]); + } + + if (hsflags & BINARY_SPLIT_GLOBAL) { + if (type == am_bm) { + BMData *bm; + Sint pos; + Eterm ret; + Eterm *hp; + BMFindAllState state; + Uint reds = get_reds(p, BM_LOOP_FACTOR); + Uint save_reds = reds; + + bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin); +#ifdef HARDDEBUG + dump_bm_data(bm); +#endif + if (state_term == NIL) { + bm_init_find_all(&state, hsstart, hsend); + } else { + Eterm *ptr = big_val(state_term); + bm_restore_find_all(&state, (char *)(ptr+3)); + } + + pos = bm_find_all_non_overlapping(&state, bm, bytes, &reds); + if (pos == BM_NOT_FOUND) { + hp = HAlloc(p, 2); + ret = NIL; + ret = CONS(hp, subject, ret); + } else if (pos == BM_RESTART) { + int x = + (SIZEOF_BM_SERIALIZED_FIND_ALL_STATE(state) / sizeof(Eterm)) + + !!(SIZEOF_BM_SERIALIZED_FIND_ALL_STATE(state) % sizeof(Eterm)); +#ifdef HARDDEBUG + erts_printf("Trap bm!\n"); +#endif + hp = HAlloc(p, x+3); + hp[0] = make_pos_bignum_header(x+2); + hp[1] = type; + hp[2] = (Eterm)(hsflags); + bm_serialize_find_all(&state, (char *)(hp+3)); + *res_term = make_big(hp); + erts_free_aligned_binary_bytes(temp_alloc); + bm_clean_find_all(&state); + return DO_BIN_MATCH_RESTART; + } else { + size_t orig_size; + Eterm orig; + Uint offset; + Uint bit_offset; + Uint bit_size; + ErlSubBin *sb; + FindallData *fad = state.out; + int i, j, k; + orig_size = binary_size(subject); + j = state.m - 1; + k = (int)(orig_size); + if ((hsflags & BINARY_SPLIT_TRIM) && (orig_size - fad[j].pos - fad[j].len) == 0) { + for (i = (j - 1); i >= 0; --i) { + if ((fad[i+1].pos - fad[i].pos - fad[i].len) != 0) { + break; + } + } + if (i == -1) { + if (fad[0].pos == 0) { + ret = NIL; + } else { + hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[0].pos; + sb->offs = offset; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = bit_size; + sb->is_writable = 0; + fad[0].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + } + erts_free_aligned_binary_bytes(temp_alloc); + bm_clean_find_all(&state); + BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); + *res_term = ret; + return DO_BIN_MATCH_OK; + } + j = i; + k = fad[j+1].pos; + } + hp = HAlloc(p, (j + 2) * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[0].pos; + sb->offs = offset; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + fad[0].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; + + for (i = 1; i <= j; ++i) { + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; + sb->offs = offset + fad[i-1].pos + fad[i-1].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + fad[i].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; + } + ret = NIL; + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = k - fad[j].pos - fad[j].len; + sb->offs = offset + fad[j].pos + fad[j].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = bit_size; + sb->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + for (i = j; i >= 0; --i) { + ret = CONS(hp, fad[i].epos, ret); + hp += 2; + } + } + erts_free_aligned_binary_bytes(temp_alloc); + bm_clean_find_all(&state); + BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); + *res_term = ret; + return DO_BIN_MATCH_OK; + } else if (type == am_ac) { + ACTrie *act; + int acr; + ACFindAllState state; + Eterm ret; + Eterm *hp; + Uint reds = get_reds(p, AC_LOOP_FACTOR); + Uint save_reds = reds; + + act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin); +#ifdef HARDDEBUG + dump_ac_trie(act); +#endif + if (state_term == NIL) { + ac_init_find_all(&state, act, hsstart, hsend); + } else { + Eterm *ptr = big_val(state_term); + ac_restore_find_all(&state, (char *)(ptr+3)); + } + acr = ac_find_all_non_overlapping(&state, bytes, &reds); + if (acr == AC_NOT_FOUND) { + hp = HAlloc(p, 2); + ret = NIL; + ret = CONS(hp, subject, ret); + } else if (acr == AC_RESTART) { + int x = (SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(state) / sizeof(Eterm)) + + !!(SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(state) % sizeof(Eterm)); +#ifdef HARDDEBUG + erts_printf("Trap ac!\n"); +#endif + hp = HAlloc(p, x+3); + hp[0] = make_pos_bignum_header(x+2); + hp[1] = type; + hp[2] = (Eterm)(hsflags); + ac_serialize_find_all(&state, (char *)(hp+3)); + *res_term = make_big(hp); + erts_free_aligned_binary_bytes(temp_alloc); + ac_clean_find_all(&state); + return DO_BIN_MATCH_RESTART; + } else { + size_t orig_size; + Eterm orig; + Uint offset; + Uint bit_offset; + Uint bit_size; + ErlSubBin *sb; + FindallData *fad = state.out; + int i, j, k; + orig_size = binary_size(subject); + j = state.m - 1; + k = (int)(orig_size); + if ((hsflags & BINARY_SPLIT_TRIM) && (orig_size - fad[j].pos - fad[j].len) == 0) { + for (i = (j - 1); i >= 0; --i) { + if ((fad[i+1].pos - fad[i].pos - fad[i].len) != 0) { + break; + } + } + if (i == -1) { + if (fad[0].pos == 0) { + ret = NIL; + } else { + hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[0].pos; + sb->offs = offset; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = bit_size; + sb->is_writable = 0; + fad[0].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + } + erts_free_aligned_binary_bytes(temp_alloc); + ac_clean_find_all(&state); + BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); + *res_term = ret; + return DO_BIN_MATCH_OK; + } + j = i; + k = fad[j+1].pos; + } + hp = HAlloc(p, (j + 2) * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[0].pos; + sb->offs = offset; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + fad[0].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; + + for (i = 1; i <= j; ++i) { + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; + sb->offs = offset + fad[i-1].pos + fad[i-1].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + fad[i].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; + } + ret = NIL; + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = k - fad[j].pos - fad[j].len; + sb->offs = offset + fad[j].pos + fad[j].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = bit_size; + sb->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + for (i = j; i >= 0; --i) { + ret = CONS(hp, fad[i].epos, ret); + hp += 2; + } + } + erts_free_aligned_binary_bytes(temp_alloc); + ac_clean_find_all(&state); + BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); + *res_term = ret; + return DO_BIN_MATCH_OK; + } + } else { + if (type == am_bm) { + BMData *bm; + Sint pos; + Eterm ret; + Eterm *hp; + BMFindFirstState state; + Uint reds = get_reds(p, BM_LOOP_FACTOR); + Uint save_reds = reds; + + bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin); +#ifdef HARDDEBUG + dump_bm_data(bm); +#endif + if (state_term == NIL) { + bm_init_find_first_match(&state, hsstart, hsend); + } else { + Eterm *ptr = big_val(state_term); + memcpy((void *)(&state), (const void *)(ptr+3), sizeof(BMFindFirstState)); + } + +#ifdef HARDDEBUG + erts_printf("(bm) state->pos = %ld, state->len = %lu\n",state.pos, + state.len); +#endif + pos = bm_find_first_match(&state, bm, bytes, &reds); + if (pos == BM_NOT_FOUND) { + hp = HAlloc(p, 2); + ret = NIL; + ret = CONS(hp, subject, ret); + } else if (pos == BM_RESTART) { + int x = + (sizeof(state) / sizeof(Eterm)) + + !!(sizeof(state) % sizeof(Eterm)); +#ifdef HARDDEBUG + erts_printf("Trap bm!\n"); +#endif + hp = HAlloc(p, x+3); + hp[0] = make_pos_bignum_header(x+2); + hp[1] = type; + hp[2] = (Eterm)(hsflags); + memcpy((void *)(hp+3), (const void *)(&state), sizeof(state)); + *res_term = make_big(hp); + erts_free_aligned_binary_bytes(temp_alloc); + return DO_BIN_MATCH_RESTART; + } else { + size_t orig_size; + Eterm orig; + Uint offset; + Uint bit_offset; + Uint bit_size; + ErlSubBin *sb1; + ErlSubBin *sb2; + + orig_size = binary_size(subject); + + if ((hsflags & BINARY_SPLIT_TRIM) && (orig_size - pos - bm->len) == 0) { + if (pos == 0) { + ret = NIL; + } else { + hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = (ErlSubBin *) hp; + sb1->thing_word = HEADER_SUB_BIN; + sb1->size = pos; + sb1->offs = offset; + sb1->orig = orig; + sb1->bitoffs = bit_offset; + sb1->bitsize = bit_size; + sb1->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb1), ret); + hp += 2; + } + } else { + hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = (ErlSubBin *) hp; + sb1->thing_word = HEADER_SUB_BIN; + sb1->size = pos; + sb1->offs = offset; + sb1->orig = orig; + sb1->bitoffs = bit_offset; + sb1->bitsize = 0; + sb1->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + sb2 = (ErlSubBin *) hp; + sb2->thing_word = HEADER_SUB_BIN; + sb2->size = orig_size - pos - bm->len; + sb2->offs = offset + pos + bm->len; + sb2->orig = orig; + sb2->bitoffs = bit_offset; + sb2->bitsize = bit_size; + sb2->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb2), ret); + hp += 2; + ret = CONS(hp, make_binary(sb1), ret); + hp += 2; + } + } + erts_free_aligned_binary_bytes(temp_alloc); + BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); + *res_term = ret; + return DO_BIN_MATCH_OK; + } else if (type == am_ac) { + ACTrie *act; + Uint pos, rlen; + int acr; + ACFindFirstState state; + Eterm ret; + Eterm *hp; + Uint reds = get_reds(p, AC_LOOP_FACTOR); + Uint save_reds = reds; + + act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin); +#ifdef HARDDEBUG + dump_ac_trie(act); +#endif + if (state_term == NIL) { + ac_init_find_first_match(&state, act, hsstart, hsend); + } else { + Eterm *ptr = big_val(state_term); + memcpy((void *)(&state), (const void *)(ptr+3), sizeof(ACFindFirstState)); + } + acr = ac_find_first_match(&state, bytes, &pos, &rlen, &reds); + if (acr == AC_NOT_FOUND) { + hp = HAlloc(p, 2); + ret = NIL; + ret = CONS(hp, subject, ret); + } else if (acr == AC_RESTART) { + int x = + (sizeof(state) / sizeof(Eterm)) + + !!(sizeof(state) % sizeof(Eterm)); +#ifdef HARDDEBUG + erts_printf("Trap ac!\n"); +#endif + hp = HAlloc(p, x+3); + hp[0] = make_pos_bignum_header(x+2); + hp[1] = type; + hp[2] = (Eterm)(hsflags); + memcpy((void *)(hp+3), (const void *)(&state), sizeof(state)); + *res_term = make_big(hp); + erts_free_aligned_binary_bytes(temp_alloc); + return DO_BIN_MATCH_RESTART; + } else { + size_t orig_size; + Eterm orig; + Uint offset; + Uint bit_offset; + Uint bit_size; + ErlSubBin *sb1; + ErlSubBin *sb2; + + orig_size = binary_size(subject); + + if ((hsflags & BINARY_SPLIT_TRIM) && (orig_size - pos - rlen) == 0) { + if (pos == 0) { + ret = NIL; + } else { + hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = (ErlSubBin *) hp; + sb1->thing_word = HEADER_SUB_BIN; + sb1->size = pos; + sb1->offs = offset; + sb1->orig = orig; + sb1->bitoffs = bit_offset; + sb1->bitsize = bit_size; + sb1->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb1), ret); + hp += 2; + } + } else { + hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = (ErlSubBin *) hp; + sb1->thing_word = HEADER_SUB_BIN; + sb1->size = pos; + sb1->offs = offset; + sb1->orig = orig; + sb1->bitoffs = bit_offset; + sb1->bitsize = 0; + sb1->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + sb2 = (ErlSubBin *) hp; + sb2->thing_word = HEADER_SUB_BIN; + sb2->size = orig_size - pos - rlen; + sb2->offs = offset + pos + rlen; + sb2->orig = orig; + sb2->bitoffs = bit_offset; + sb2->bitsize = bit_size; + sb2->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb2), ret); + hp += 2; + ret = CONS(hp, make_binary(sb1), ret); + hp += 2; + } + } + erts_free_aligned_binary_bytes(temp_alloc); + BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); + *res_term = ret; + return DO_BIN_MATCH_OK; + } + } + badarg: + return DO_BIN_MATCH_BADARG; +} + +static int parse_split_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp, Uint *optp) +{ + Eterm *tp; + Uint pos; + Sint len; + *optp = 0; + *posp = 0; + *endp = binary_size(bin); + if (l == ((Eterm) 0) || l == NIL) { + return 0; + } else if (is_list(l)) { + while(is_list(l)) { + Eterm t = CAR(list_val(l)); + Uint orig_size; + if (is_atom(t)) { + if (t == am_global) { + *optp |= BINARY_SPLIT_GLOBAL; + l = CDR(list_val(l)); + continue; + } + if (t == am_trim) { + *optp |= BINARY_SPLIT_TRIM; + l = CDR(list_val(l)); + continue; + } + } + if (!is_tuple(t)) { + goto badarg; + } + tp = tuple_val(t); + if (arityval(*tp) != 2) { + goto badarg; + } + if (tp[1] != am_scope || is_not_tuple(tp[2])) { + goto badarg; + } + tp = tuple_val(tp[2]); + if (arityval(*tp) != 2) { + goto badarg; + } + if (!term_to_Uint(tp[1], &pos)) { + goto badarg; + } + if (!term_to_Sint(tp[2], &len)) { + goto badarg; + } + if (len < 0) { + Uint lentmp = -(Uint)len; + /* overflow */ + if ((Sint)lentmp < 0) { + goto badarg; + } + len = lentmp; + pos -= len; + } + /* overflow */ + if ((pos + len) < pos || (len > 0 && (pos + len) == pos)) { + goto badarg; + } + *endp = len + pos; + *posp = pos; + if ((orig_size = binary_size(bin)) < pos || + orig_size < (*endp)) { + goto badarg; + } + l = CDR(list_val(l)); + } + return 0; + } else { + badarg: + return 1; + } +} + +static BIF_RETTYPE binary_split_trap(BIF_ALIST_3) +{ + int runres; + Eterm result; + Binary *bin = ((ProcBin *) binary_val(BIF_ARG_3))->val; + runres = do_binary_split(BIF_P,BIF_ARG_1,0,0,0,NIL,bin,BIF_ARG_2,&result); + if (runres == DO_BIN_MATCH_OK) { + BIF_RET(result); + } else { + BUMP_ALL_REDS(BIF_P); + BIF_TRAP3(&binary_split_trap_export, BIF_P, BIF_ARG_1, result, + BIF_ARG_3); + } +} + +BIF_RETTYPE binary_split_3(BIF_ALIST_3) +{ + return binary_split(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); +} + +static BIF_RETTYPE +binary_split(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) +{ + Uint hsflags; + Uint hsstart; + Uint hsend; + Eterm *tp; + Eterm type; + Binary *bin; + Eterm bin_term = NIL; + int runres; + Eterm result; + + if (is_not_binary(arg1)) { + goto badarg; + } + if (parse_split_opts_list(arg3, arg1, &hsstart, &hsend, &hsflags)) { + goto badarg; + } + if (hsend == 0) { + tp = HAlloc(p, 2); + result = NIL; + result = CONS(tp, arg1, result); + BIF_RET(result); + } + if (is_tuple(arg2)) { + tp = tuple_val(arg2); + if (arityval(*tp) != 2 || is_not_atom(tp[1])) { + goto badarg; + } + if (((tp[1] != am_bm) && (tp[1] != am_ac)) || + !ERTS_TERM_IS_MAGIC_BINARY(tp[2])) { + goto badarg; + } + type = tp[1]; + bin = ((ProcBin *) binary_val(tp[2]))->val; + if (type == am_bm && + ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_bm) { + goto badarg; + } + if (type == am_ac && + ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_ac) { + goto badarg; + } + bin_term = tp[2]; + } else if (do_binary_match_compile(arg2, &type, &bin)) { + goto badarg; + } + runres = do_binary_split(p, arg1, hsstart, hsend, hsflags, type, bin, NIL, &result); + if (runres == DO_BIN_MATCH_RESTART && bin_term == NIL) { + Eterm *hp = HAlloc(p, PROC_BIN_SIZE); + bin_term = erts_mk_magic_binary_term(&hp, &MSO(p), bin); + } else if (bin_term == NIL) { + erts_bin_free(bin); + } + switch(runres) { + case DO_BIN_MATCH_OK: + BIF_RET(result); + case DO_BIN_MATCH_RESTART: + BIF_TRAP3(&binary_split_trap_export, p, arg1, result, bin_term); + default: + goto badarg; + } + badarg: + BIF_ERROR(p,BADARG); +} + + +BIF_RETTYPE binary_split_2(BIF_ALIST_2) +{ + return binary_split(BIF_P,BIF_ARG_1,BIF_ARG_2,((Eterm) 0)); +} + + BIF_RETTYPE binary_referenced_byte_size_1(BIF_ALIST_1) { ErlSubBin *sb; -- cgit v1.2.3 From d1283d1f826bdbd584bc87572ab46001164939e1 Mon Sep 17 00:00:00 2001 From: Andrew Bennett Date: Thu, 25 Jun 2015 11:26:48 -0600 Subject: stdlib: Add BIF option 'trim_all' to binary:split/3 --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/erl_bif_binary.c | 496 +++++++++++++++++++++++++----------- 2 files changed, 349 insertions(+), 148 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 3d357886ee..5f27aaa14b 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -586,6 +586,7 @@ atom trace_control_word atom tracer atom trap_exit atom trim +atom trim_all atom try_clause atom true atom tuple diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 68e5fe23c7..1709f7671d 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -2544,6 +2544,7 @@ BIF_RETTYPE binary_copy_2(BIF_ALIST_2) #define BINARY_SPLIT_GLOBAL 0x01 #define BINARY_SPLIT_TRIM 0x02 +#define BINARY_SPLIT_TRIM_ALL 0x04 static int do_binary_split(Process *p, Eterm subject, Uint hsstart, Uint hsend, Uint hsflags, Eterm type, Binary *bin, @@ -2616,88 +2617,177 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, Uint bit_size; ErlSubBin *sb; FindallData *fad = state.out; - int i, j, k; + Sint i, j; + Sint drop = 0; + Sint head = 0; + Sint tail; + Uint list_size; + Uint tail_pos; + + tail = state.m - 1; + list_size = state.m + 1; orig_size = binary_size(subject); - j = state.m - 1; - k = (int)(orig_size); - if ((hsflags & BINARY_SPLIT_TRIM) && (orig_size - fad[j].pos - fad[j].len) == 0) { - for (i = (j - 1); i >= 0; --i) { - if ((fad[i+1].pos - fad[i].pos - fad[i].len) != 0) { - break; + tail_pos = (Uint)(orig_size); + + if (hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) { + if ((orig_size - fad[tail].pos - fad[tail].len) == 0) { + list_size--; + for (i = (tail - 1); i >= 0; --i) { + if ((fad[i+1].pos - fad[i].pos - fad[i].len) != 0) { + break; + } + list_size--; } + if (i == -1) { + if (fad[head].pos == 0) { + ret = NIL; + } else { + hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[head].pos; + sb->offs = offset; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = bit_size; + sb->is_writable = 0; + fad[head].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + } + erts_free_aligned_binary_bytes(temp_alloc); + bm_clean_find_all(&state); + BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); + *res_term = ret; + return DO_BIN_MATCH_OK; + } + tail = i; + tail_pos = fad[tail+1].pos; } - if (i == -1) { - if (fad[0].pos == 0) { - ret = NIL; - } else { - hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + } + if (hsflags & BINARY_SPLIT_TRIM_ALL) { + if (fad[head].pos == 0) { + drop++; + list_size--; + for (i = drop, j = tail; i <= j; ++i) { + if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { + break; + } + drop++; + list_size--; + } + head = drop - 1; + } + for (i = (head+1), j = tail; i <= j; ++i) { + if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) == 0) { + list_size--; + } + } + + hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + if (drop == 0) { + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[head].pos; + sb->offs = offset; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + fad[head].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; + } + + for (i = (head+1), j = tail; i <= j; ++i) { + if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { sb = (ErlSubBin *)(hp); sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[0].pos; - sb->offs = offset; + sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; + sb->offs = offset + fad[i-1].pos + fad[i-1].len; sb->orig = orig; sb->bitoffs = bit_offset; - sb->bitsize = bit_size; + sb->bitsize = 0; sb->is_writable = 0; - fad[0].epos = make_binary(sb); + fad[i].epos = make_binary(sb); hp += ERL_SUB_BIN_SIZE; + } + } - ret = NIL; - ret = CONS(hp, make_binary(sb), ret); + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = tail_pos - fad[tail].pos - fad[tail].len; + sb->offs = offset + fad[tail].pos + fad[tail].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = bit_size; + sb->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + for (i = tail, j = head; i > j; --i) { + if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { + ret = CONS(hp, fad[i].epos, ret); hp += 2; } - erts_free_aligned_binary_bytes(temp_alloc); - bm_clean_find_all(&state); - BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); - *res_term = ret; - return DO_BIN_MATCH_OK; } - j = i; - k = fad[j+1].pos; - } - hp = HAlloc(p, (j + 2) * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[0].pos; - sb->offs = offset; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - fad[0].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - - for (i = 1; i <= j; ++i) { + if (drop == 0) { + ret = CONS(hp, fad[head].epos, ret); + hp += 2; + } + } else { + hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb = (ErlSubBin *)(hp); sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; - sb->offs = offset + fad[i-1].pos + fad[i-1].len; + sb->size = fad[head].pos; + sb->offs = offset; sb->orig = orig; sb->bitoffs = bit_offset; sb->bitsize = 0; sb->is_writable = 0; - fad[i].epos = make_binary(sb); + fad[head].epos = make_binary(sb); hp += ERL_SUB_BIN_SIZE; - } - ret = NIL; - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = k - fad[j].pos - fad[j].len; - sb->offs = offset + fad[j].pos + fad[j].len; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = bit_size; - sb->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - ret = CONS(hp, make_binary(sb), ret); - hp += 2; - for (i = j; i >= 0; --i) { - ret = CONS(hp, fad[i].epos, ret); + + for (i = (head+1), j = tail; i <= j; ++i) { + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; + sb->offs = offset + fad[i-1].pos + fad[i-1].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + fad[i].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; + } + + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = tail_pos - fad[tail].pos - fad[tail].len; + sb->offs = offset + fad[tail].pos + fad[tail].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = bit_size; + sb->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb), ret); hp += 2; + for (i = tail, j = head; i >= j; --i) { + ret = CONS(hp, fad[i].epos, ret); + hp += 2; + } } } erts_free_aligned_binary_bytes(temp_alloc); @@ -2752,88 +2842,177 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, Uint bit_size; ErlSubBin *sb; FindallData *fad = state.out; - int i, j, k; + Sint i, j; + Sint drop = 0; + Sint head = 0; + Sint tail; + Uint list_size; + Uint tail_pos; + + tail = state.m - 1; + list_size = state.m + 1; orig_size = binary_size(subject); - j = state.m - 1; - k = (int)(orig_size); - if ((hsflags & BINARY_SPLIT_TRIM) && (orig_size - fad[j].pos - fad[j].len) == 0) { - for (i = (j - 1); i >= 0; --i) { - if ((fad[i+1].pos - fad[i].pos - fad[i].len) != 0) { - break; + tail_pos = (Uint)(orig_size); + + if (hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) { + if ((orig_size - fad[tail].pos - fad[tail].len) == 0) { + list_size--; + for (i = (tail - 1); i >= 0; --i) { + if ((fad[i+1].pos - fad[i].pos - fad[i].len) != 0) { + break; + } + list_size--; } + if (i == -1) { + if (fad[head].pos == 0) { + ret = NIL; + } else { + hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[head].pos; + sb->offs = offset; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = bit_size; + sb->is_writable = 0; + fad[head].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + } + erts_free_aligned_binary_bytes(temp_alloc); + ac_clean_find_all(&state); + BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); + *res_term = ret; + return DO_BIN_MATCH_OK; + } + tail = i; + tail_pos = fad[tail+1].pos; + } + } + if (hsflags & BINARY_SPLIT_TRIM_ALL) { + if (fad[head].pos == 0) { + drop++; + list_size--; + for (i = drop, j = tail; i <= j; ++i) { + if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { + break; + } + drop++; + list_size--; + } + head = drop - 1; + } + for (i = (head+1), j = tail; i <= j; ++i) { + if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) == 0) { + list_size--; + } + } + + hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + + if (drop == 0) { + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[head].pos; + sb->offs = offset; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + fad[head].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; } - if (i == -1) { - if (fad[0].pos == 0) { - ret = NIL; - } else { - hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + for (i = (head+1), j = tail; i <= j; ++i) { + if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { sb = (ErlSubBin *)(hp); sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[0].pos; - sb->offs = offset; + sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; + sb->offs = offset + fad[i-1].pos + fad[i-1].len; sb->orig = orig; sb->bitoffs = bit_offset; - sb->bitsize = bit_size; + sb->bitsize = 0; sb->is_writable = 0; - fad[0].epos = make_binary(sb); + fad[i].epos = make_binary(sb); hp += ERL_SUB_BIN_SIZE; + } + } - ret = NIL; - ret = CONS(hp, make_binary(sb), ret); + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = tail_pos - fad[tail].pos - fad[tail].len; + sb->offs = offset + fad[tail].pos + fad[tail].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = bit_size; + sb->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + for (i = tail, j = head; i > j; --i) { + if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { + ret = CONS(hp, fad[i].epos, ret); hp += 2; } - erts_free_aligned_binary_bytes(temp_alloc); - ac_clean_find_all(&state); - BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); - *res_term = ret; - return DO_BIN_MATCH_OK; } - j = i; - k = fad[j+1].pos; - } - hp = HAlloc(p, (j + 2) * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[0].pos; - sb->offs = offset; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - fad[0].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - - for (i = 1; i <= j; ++i) { + if (drop == 0) { + ret = CONS(hp, fad[head].epos, ret); + hp += 2; + } + } else { + hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb = (ErlSubBin *)(hp); sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; - sb->offs = offset + fad[i-1].pos + fad[i-1].len; + sb->size = fad[head].pos; + sb->offs = offset; sb->orig = orig; sb->bitoffs = bit_offset; sb->bitsize = 0; sb->is_writable = 0; - fad[i].epos = make_binary(sb); + fad[head].epos = make_binary(sb); hp += ERL_SUB_BIN_SIZE; - } - ret = NIL; - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = k - fad[j].pos - fad[j].len; - sb->offs = offset + fad[j].pos + fad[j].len; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = bit_size; - sb->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - ret = CONS(hp, make_binary(sb), ret); - hp += 2; - for (i = j; i >= 0; --i) { - ret = CONS(hp, fad[i].epos, ret); + + for (i = (head+1), j = tail; i <= j; ++i) { + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; + sb->offs = offset + fad[i-1].pos + fad[i-1].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + fad[i].epos = make_binary(sb); + hp += ERL_SUB_BIN_SIZE; + } + + sb = (ErlSubBin *)(hp); + sb->thing_word = HEADER_SUB_BIN; + sb->size = tail_pos - fad[tail].pos - fad[tail].len; + sb->offs = offset + fad[tail].pos + fad[tail].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = bit_size; + sb->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = NIL; + ret = CONS(hp, make_binary(sb), ret); hp += 2; + for (i = tail, j = head; i >= j; --i) { + ret = CONS(hp, fad[i].epos, ret); + hp += 2; + } } } erts_free_aligned_binary_bytes(temp_alloc); @@ -2898,7 +3077,7 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, orig_size = binary_size(subject); - if ((hsflags & BINARY_SPLIT_TRIM) && (orig_size - pos - bm->len) == 0) { + if ((hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) && (orig_size - pos - bm->len) == 0) { if (pos == 0) { ret = NIL; } else { @@ -2919,17 +3098,23 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, hp += 2; } } else { - hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - sb1 = (ErlSubBin *) hp; - sb1->thing_word = HEADER_SUB_BIN; - sb1->size = pos; - sb1->offs = offset; - sb1->orig = orig; - sb1->bitoffs = bit_offset; - sb1->bitsize = 0; - sb1->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; + if ((hsflags & BINARY_SPLIT_TRIM_ALL) && (pos == 0)) { + hp = HAlloc(p, 1 * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = NULL; + } else { + hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = (ErlSubBin *) hp; + sb1->thing_word = HEADER_SUB_BIN; + sb1->size = pos; + sb1->offs = offset; + sb1->orig = orig; + sb1->bitoffs = bit_offset; + sb1->bitsize = 0; + sb1->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + } sb2 = (ErlSubBin *) hp; sb2->thing_word = HEADER_SUB_BIN; @@ -2944,8 +3129,10 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, ret = NIL; ret = CONS(hp, make_binary(sb2), ret); hp += 2; - ret = CONS(hp, make_binary(sb1), ret); - hp += 2; + if (sb1 != NULL) { + ret = CONS(hp, make_binary(sb1), ret); + hp += 2; + } } } erts_free_aligned_binary_bytes(temp_alloc); @@ -3003,7 +3190,7 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, orig_size = binary_size(subject); - if ((hsflags & BINARY_SPLIT_TRIM) && (orig_size - pos - rlen) == 0) { + if ((hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) && (orig_size - pos - rlen) == 0) { if (pos == 0) { ret = NIL; } else { @@ -3024,17 +3211,23 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, hp += 2; } } else { - hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - sb1 = (ErlSubBin *) hp; - sb1->thing_word = HEADER_SUB_BIN; - sb1->size = pos; - sb1->offs = offset; - sb1->orig = orig; - sb1->bitoffs = bit_offset; - sb1->bitsize = 0; - sb1->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; + if ((hsflags & BINARY_SPLIT_TRIM_ALL) && (pos == 0)) { + hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = NULL; + } else { + hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = (ErlSubBin *) hp; + sb1->thing_word = HEADER_SUB_BIN; + sb1->size = pos; + sb1->offs = offset; + sb1->orig = orig; + sb1->bitoffs = bit_offset; + sb1->bitsize = 0; + sb1->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + } sb2 = (ErlSubBin *) hp; sb2->thing_word = HEADER_SUB_BIN; @@ -3049,8 +3242,10 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, ret = NIL; ret = CONS(hp, make_binary(sb2), ret); hp += 2; - ret = CONS(hp, make_binary(sb1), ret); - hp += 2; + if (sb1 != NULL) { + ret = CONS(hp, make_binary(sb1), ret); + hp += 2; + } } } erts_free_aligned_binary_bytes(temp_alloc); @@ -3088,6 +3283,11 @@ static int parse_split_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp, Uin l = CDR(list_val(l)); continue; } + if (t == am_trim_all) { + *optp |= BINARY_SPLIT_TRIM_ALL; + l = CDR(list_val(l)); + continue; + } } if (!is_tuple(t)) { goto badarg; -- cgit v1.2.3 From 20855f1819f91eeeb1fa746186477d3824024500 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 26 Aug 2015 15:20:45 +0200 Subject: erts: Replace 0 with THE_NON_VALUE --- erts/emulator/beam/erl_bif_binary.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 1709f7671d..860c9a9779 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -1305,7 +1305,7 @@ static int parse_match_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp) Eterm *tp; Uint pos; Sint len; - if (l == ((Eterm) 0) || l == NIL) { + if (l == THE_NON_VALUE || l == NIL) { /* Invalid term or NIL, we're called from binary_match(es)_2 or have no options*/ *posp = 0; @@ -1535,13 +1535,13 @@ binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) BIF_RETTYPE binary_match_2(BIF_ALIST_2) { - return binary_match(BIF_P,BIF_ARG_1,BIF_ARG_2,((Eterm) 0)); + return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE); } BIF_RETTYPE binary_matches_2(BIF_ALIST_2) { - return binary_matches(BIF_P,BIF_ARG_1,BIF_ARG_2,((Eterm) 0)); + return binary_matches(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE); } @@ -3266,7 +3266,7 @@ static int parse_split_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp, Uin *optp = 0; *posp = 0; *endp = binary_size(bin); - if (l == ((Eterm) 0) || l == NIL) { + if (l == THE_NON_VALUE || l == NIL) { return 0; } else if (is_list(l)) { while(is_list(l)) { @@ -3427,7 +3427,7 @@ binary_split(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) BIF_RETTYPE binary_split_2(BIF_ALIST_2) { - return binary_split(BIF_P,BIF_ARG_1,BIF_ARG_2,((Eterm) 0)); + return binary_split(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE); } -- cgit v1.2.3 From 5b600aac42fdc4d08fabba682d7803351c9bfbdb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 26 Aug 2015 15:43:00 +0200 Subject: erts: Refactor backend of binary:split to reduce code volume. --- erts/emulator/beam/erl_bif_binary.c | 659 +++++++++--------------------------- 1 file changed, 151 insertions(+), 508 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 860c9a9779..6adc61df19 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -2542,6 +2542,11 @@ BIF_RETTYPE binary_copy_2(BIF_ALIST_2) return do_binary_copy(BIF_P,BIF_ARG_1,BIF_ARG_2); } +static Eterm do_split_single_result(Process*, Eterm subject, + Sint pos, Sint len, Uint hsflags); +static Eterm do_split_global_result(Process*, FindallData *fad, Uint fad_sz, + Eterm subject, Uint hsflags); + #define BINARY_SPLIT_GLOBAL 0x01 #define BINARY_SPLIT_TRIM 0x02 #define BINARY_SPLIT_TRIM_ALL 0x04 @@ -2571,7 +2576,6 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, if (type == am_bm) { BMData *bm; Sint pos; - Eterm ret; Eterm *hp; BMFindAllState state; Uint reds = get_reds(p, BM_LOOP_FACTOR); @@ -2591,8 +2595,7 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, pos = bm_find_all_non_overlapping(&state, bm, bytes, &reds); if (pos == BM_NOT_FOUND) { hp = HAlloc(p, 2); - ret = NIL; - ret = CONS(hp, subject, ret); + *res_term = CONS(hp, subject, NIL); } else if (pos == BM_RESTART) { int x = (SIZEOF_BM_SERIALIZED_FIND_ALL_STATE(state) / sizeof(Eterm)) + @@ -2610,196 +2613,16 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, bm_clean_find_all(&state); return DO_BIN_MATCH_RESTART; } else { - size_t orig_size; - Eterm orig; - Uint offset; - Uint bit_offset; - Uint bit_size; - ErlSubBin *sb; - FindallData *fad = state.out; - Sint i, j; - Sint drop = 0; - Sint head = 0; - Sint tail; - Uint list_size; - Uint tail_pos; - - tail = state.m - 1; - list_size = state.m + 1; - orig_size = binary_size(subject); - tail_pos = (Uint)(orig_size); - - if (hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) { - if ((orig_size - fad[tail].pos - fad[tail].len) == 0) { - list_size--; - for (i = (tail - 1); i >= 0; --i) { - if ((fad[i+1].pos - fad[i].pos - fad[i].len) != 0) { - break; - } - list_size--; - } - if (i == -1) { - if (fad[head].pos == 0) { - ret = NIL; - } else { - hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[head].pos; - sb->offs = offset; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = bit_size; - sb->is_writable = 0; - fad[head].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - - ret = NIL; - ret = CONS(hp, make_binary(sb), ret); - hp += 2; - } - erts_free_aligned_binary_bytes(temp_alloc); - bm_clean_find_all(&state); - BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); - *res_term = ret; - return DO_BIN_MATCH_OK; - } - tail = i; - tail_pos = fad[tail+1].pos; - } - } - if (hsflags & BINARY_SPLIT_TRIM_ALL) { - if (fad[head].pos == 0) { - drop++; - list_size--; - for (i = drop, j = tail; i <= j; ++i) { - if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { - break; - } - drop++; - list_size--; - } - head = drop - 1; - } - for (i = (head+1), j = tail; i <= j; ++i) { - if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) == 0) { - list_size--; - } - } - - hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - - if (drop == 0) { - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[head].pos; - sb->offs = offset; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - fad[head].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - } - - for (i = (head+1), j = tail; i <= j; ++i) { - if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; - sb->offs = offset + fad[i-1].pos + fad[i-1].len; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - fad[i].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - } - } - - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = tail_pos - fad[tail].pos - fad[tail].len; - sb->offs = offset + fad[tail].pos + fad[tail].len; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = bit_size; - sb->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - - ret = NIL; - ret = CONS(hp, make_binary(sb), ret); - hp += 2; - for (i = tail, j = head; i > j; --i) { - if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { - ret = CONS(hp, fad[i].epos, ret); - hp += 2; - } - } - if (drop == 0) { - ret = CONS(hp, fad[head].epos, ret); - hp += 2; - } - } else { - hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[head].pos; - sb->offs = offset; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - fad[head].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - - for (i = (head+1), j = tail; i <= j; ++i) { - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; - sb->offs = offset + fad[i-1].pos + fad[i-1].len; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - fad[i].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - } - - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = tail_pos - fad[tail].pos - fad[tail].len; - sb->offs = offset + fad[tail].pos + fad[tail].len; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = bit_size; - sb->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - - ret = NIL; - ret = CONS(hp, make_binary(sb), ret); - hp += 2; - for (i = tail, j = head; i >= j; --i) { - ret = CONS(hp, fad[i].epos, ret); - hp += 2; - } - } + *res_term = do_split_global_result(p, state.out, state.m, subject, hsflags); } erts_free_aligned_binary_bytes(temp_alloc); bm_clean_find_all(&state); BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); - *res_term = ret; return DO_BIN_MATCH_OK; } else if (type == am_ac) { ACTrie *act; int acr; ACFindAllState state; - Eterm ret; Eterm *hp; Uint reds = get_reds(p, AC_LOOP_FACTOR); Uint save_reds = reds; @@ -2817,8 +2640,7 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, acr = ac_find_all_non_overlapping(&state, bytes, &reds); if (acr == AC_NOT_FOUND) { hp = HAlloc(p, 2); - ret = NIL; - ret = CONS(hp, subject, ret); + *res_term = CONS(hp, subject, NIL); } else if (acr == AC_RESTART) { int x = (SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(state) / sizeof(Eterm)) + !!(SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(state) % sizeof(Eterm)); @@ -2835,197 +2657,17 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, ac_clean_find_all(&state); return DO_BIN_MATCH_RESTART; } else { - size_t orig_size; - Eterm orig; - Uint offset; - Uint bit_offset; - Uint bit_size; - ErlSubBin *sb; - FindallData *fad = state.out; - Sint i, j; - Sint drop = 0; - Sint head = 0; - Sint tail; - Uint list_size; - Uint tail_pos; - - tail = state.m - 1; - list_size = state.m + 1; - orig_size = binary_size(subject); - tail_pos = (Uint)(orig_size); - - if (hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) { - if ((orig_size - fad[tail].pos - fad[tail].len) == 0) { - list_size--; - for (i = (tail - 1); i >= 0; --i) { - if ((fad[i+1].pos - fad[i].pos - fad[i].len) != 0) { - break; - } - list_size--; - } - if (i == -1) { - if (fad[head].pos == 0) { - ret = NIL; - } else { - hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[head].pos; - sb->offs = offset; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = bit_size; - sb->is_writable = 0; - fad[head].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - - ret = NIL; - ret = CONS(hp, make_binary(sb), ret); - hp += 2; - } - erts_free_aligned_binary_bytes(temp_alloc); - ac_clean_find_all(&state); - BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); - *res_term = ret; - return DO_BIN_MATCH_OK; - } - tail = i; - tail_pos = fad[tail+1].pos; - } - } - if (hsflags & BINARY_SPLIT_TRIM_ALL) { - if (fad[head].pos == 0) { - drop++; - list_size--; - for (i = drop, j = tail; i <= j; ++i) { - if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { - break; - } - drop++; - list_size--; - } - head = drop - 1; - } - for (i = (head+1), j = tail; i <= j; ++i) { - if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) == 0) { - list_size--; - } - } - - hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - - if (drop == 0) { - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[head].pos; - sb->offs = offset; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - fad[head].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - } - - for (i = (head+1), j = tail; i <= j; ++i) { - if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; - sb->offs = offset + fad[i-1].pos + fad[i-1].len; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - fad[i].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - } - } - - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = tail_pos - fad[tail].pos - fad[tail].len; - sb->offs = offset + fad[tail].pos + fad[tail].len; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = bit_size; - sb->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - - ret = NIL; - ret = CONS(hp, make_binary(sb), ret); - hp += 2; - for (i = tail, j = head; i > j; --i) { - if ((fad[i].pos - fad[i-1].pos - fad[i-1].len) != 0) { - ret = CONS(hp, fad[i].epos, ret); - hp += 2; - } - } - if (drop == 0) { - ret = CONS(hp, fad[head].epos, ret); - hp += 2; - } - } else { - hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[head].pos; - sb->offs = offset; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - fad[head].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - - for (i = (head+1), j = tail; i <= j; ++i) { - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = fad[i].pos - fad[i-1].pos - fad[i-1].len; - sb->offs = offset + fad[i-1].pos + fad[i-1].len; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - fad[i].epos = make_binary(sb); - hp += ERL_SUB_BIN_SIZE; - } - - sb = (ErlSubBin *)(hp); - sb->thing_word = HEADER_SUB_BIN; - sb->size = tail_pos - fad[tail].pos - fad[tail].len; - sb->offs = offset + fad[tail].pos + fad[tail].len; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = bit_size; - sb->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - - ret = NIL; - ret = CONS(hp, make_binary(sb), ret); - hp += 2; - for (i = tail, j = head; i >= j; --i) { - ret = CONS(hp, fad[i].epos, ret); - hp += 2; - } - } + *res_term = do_split_global_result(p, state.out, state.m, subject, hsflags); } erts_free_aligned_binary_bytes(temp_alloc); ac_clean_find_all(&state); BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); - *res_term = ret; return DO_BIN_MATCH_OK; } } else { if (type == am_bm) { BMData *bm; Sint pos; - Eterm ret; Eterm *hp; BMFindFirstState state; Uint reds = get_reds(p, BM_LOOP_FACTOR); @@ -3049,8 +2691,7 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, pos = bm_find_first_match(&state, bm, bytes, &reds); if (pos == BM_NOT_FOUND) { hp = HAlloc(p, 2); - ret = NIL; - ret = CONS(hp, subject, ret); + *res_term = CONS(hp, subject, NIL); } else if (pos == BM_RESTART) { int x = (sizeof(state) / sizeof(Eterm)) + @@ -3067,84 +2708,16 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, erts_free_aligned_binary_bytes(temp_alloc); return DO_BIN_MATCH_RESTART; } else { - size_t orig_size; - Eterm orig; - Uint offset; - Uint bit_offset; - Uint bit_size; - ErlSubBin *sb1; - ErlSubBin *sb2; - - orig_size = binary_size(subject); - - if ((hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) && (orig_size - pos - bm->len) == 0) { - if (pos == 0) { - ret = NIL; - } else { - hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - sb1 = (ErlSubBin *) hp; - sb1->thing_word = HEADER_SUB_BIN; - sb1->size = pos; - sb1->offs = offset; - sb1->orig = orig; - sb1->bitoffs = bit_offset; - sb1->bitsize = bit_size; - sb1->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - - ret = NIL; - ret = CONS(hp, make_binary(sb1), ret); - hp += 2; - } - } else { - if ((hsflags & BINARY_SPLIT_TRIM_ALL) && (pos == 0)) { - hp = HAlloc(p, 1 * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - sb1 = NULL; - } else { - hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - sb1 = (ErlSubBin *) hp; - sb1->thing_word = HEADER_SUB_BIN; - sb1->size = pos; - sb1->offs = offset; - sb1->orig = orig; - sb1->bitoffs = bit_offset; - sb1->bitsize = 0; - sb1->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - } - - sb2 = (ErlSubBin *) hp; - sb2->thing_word = HEADER_SUB_BIN; - sb2->size = orig_size - pos - bm->len; - sb2->offs = offset + pos + bm->len; - sb2->orig = orig; - sb2->bitoffs = bit_offset; - sb2->bitsize = bit_size; - sb2->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - - ret = NIL; - ret = CONS(hp, make_binary(sb2), ret); - hp += 2; - if (sb1 != NULL) { - ret = CONS(hp, make_binary(sb1), ret); - hp += 2; - } - } + *res_term = do_split_single_result(p, subject, pos, bm->len, hsflags); } erts_free_aligned_binary_bytes(temp_alloc); BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); - *res_term = ret; return DO_BIN_MATCH_OK; } else if (type == am_ac) { ACTrie *act; Uint pos, rlen; int acr; ACFindFirstState state; - Eterm ret; Eterm *hp; Uint reds = get_reds(p, AC_LOOP_FACTOR); Uint save_reds = reds; @@ -3162,8 +2735,7 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, acr = ac_find_first_match(&state, bytes, &pos, &rlen, &reds); if (acr == AC_NOT_FOUND) { hp = HAlloc(p, 2); - ret = NIL; - ret = CONS(hp, subject, ret); + *res_term = CONS(hp, subject, NIL); } else if (acr == AC_RESTART) { int x = (sizeof(state) / sizeof(Eterm)) + @@ -3180,77 +2752,10 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, erts_free_aligned_binary_bytes(temp_alloc); return DO_BIN_MATCH_RESTART; } else { - size_t orig_size; - Eterm orig; - Uint offset; - Uint bit_offset; - Uint bit_size; - ErlSubBin *sb1; - ErlSubBin *sb2; - - orig_size = binary_size(subject); - - if ((hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) && (orig_size - pos - rlen) == 0) { - if (pos == 0) { - ret = NIL; - } else { - hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - sb1 = (ErlSubBin *) hp; - sb1->thing_word = HEADER_SUB_BIN; - sb1->size = pos; - sb1->offs = offset; - sb1->orig = orig; - sb1->bitoffs = bit_offset; - sb1->bitsize = bit_size; - sb1->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - - ret = NIL; - ret = CONS(hp, make_binary(sb1), ret); - hp += 2; - } - } else { - if ((hsflags & BINARY_SPLIT_TRIM_ALL) && (pos == 0)) { - hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - sb1 = NULL; - } else { - hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - sb1 = (ErlSubBin *) hp; - sb1->thing_word = HEADER_SUB_BIN; - sb1->size = pos; - sb1->offs = offset; - sb1->orig = orig; - sb1->bitoffs = bit_offset; - sb1->bitsize = 0; - sb1->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - } - - sb2 = (ErlSubBin *) hp; - sb2->thing_word = HEADER_SUB_BIN; - sb2->size = orig_size - pos - rlen; - sb2->offs = offset + pos + rlen; - sb2->orig = orig; - sb2->bitoffs = bit_offset; - sb2->bitsize = bit_size; - sb2->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - - ret = NIL; - ret = CONS(hp, make_binary(sb2), ret); - hp += 2; - if (sb1 != NULL) { - ret = CONS(hp, make_binary(sb1), ret); - hp += 2; - } - } + *res_term = do_split_single_result(p, subject, pos, rlen, hsflags); } erts_free_aligned_binary_bytes(temp_alloc); BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); - *res_term = ret; return DO_BIN_MATCH_OK; } } @@ -3258,6 +2763,144 @@ static int do_binary_split(Process *p, Eterm subject, Uint hsstart, return DO_BIN_MATCH_BADARG; } +static Eterm do_split_single_result(Process* p, Eterm subject, + Sint pos, Sint len, Uint hsflags) +{ + size_t orig_size; + Eterm orig; + Uint offset; + Uint bit_offset; + Uint bit_size; + ErlSubBin *sb1; + ErlSubBin *sb2; + Eterm* hp; + Eterm ret; + + orig_size = binary_size(subject); + + if ((hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) && (orig_size - pos - len) == 0) { + if (pos != 0) { + ret = NIL; + } else { + hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = (ErlSubBin *) hp; + sb1->thing_word = HEADER_SUB_BIN; + sb1->size = pos; + sb1->offs = offset; + sb1->orig = orig; + sb1->bitoffs = bit_offset; + sb1->bitsize = bit_size; + sb1->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = CONS(hp, make_binary(sb1), NIL); + hp += 2; + } + } else { + if ((hsflags & BINARY_SPLIT_TRIM_ALL) && (pos == 0)) { + hp = HAlloc(p, 1 * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = NULL; + } else { + hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = (ErlSubBin *) hp; + sb1->thing_word = HEADER_SUB_BIN; + sb1->size = pos; + sb1->offs = offset; + sb1->orig = orig; + sb1->bitoffs = bit_offset; + sb1->bitsize = 0; + sb1->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + } + + sb2 = (ErlSubBin *) hp; + sb2->thing_word = HEADER_SUB_BIN; + sb2->size = orig_size - pos - len; + sb2->offs = offset + pos + len; + sb2->orig = orig; + sb2->bitoffs = bit_offset; + sb2->bitsize = bit_size; + sb2->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = CONS(hp, make_binary(sb2), NIL); + hp += 2; + if (sb1 != NULL) { + ret = CONS(hp, make_binary(sb1), ret); + hp += 2; + } + } + return ret; +} + +static Eterm do_split_global_result(Process* p, FindallData *fad, Uint fad_sz, + Uint subject, Uint hsflags) +{ + size_t orig_size; + Eterm orig; + Uint offset; + Uint bit_offset; + Uint bit_size; + ErlSubBin *sb; + Sint i; + Sint tail; + Uint list_size; + Uint end_pos; + Uint do_trim = hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL); + Eterm* hp; + Eterm* hendp; + Eterm ret; + + tail = fad_sz - 1; + list_size = fad_sz + 1; + orig_size = binary_size(subject); + end_pos = (Uint)(orig_size); + + hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2)); + hendp = hp + list_size * (ERL_SUB_BIN_SIZE + 2); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + ASSERT(bit_size == 0); + + ret = NIL; + + for (i = tail; i >= 0; --i) { + sb = (ErlSubBin *)(hp); + sb->size = end_pos - (fad[i].pos + fad[i].len); + if (!(sb->size == 0 && do_trim)) { + sb->thing_word = HEADER_SUB_BIN; + sb->offs = offset + fad[i].pos + fad[i].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + do_trim &= ~BINARY_SPLIT_TRIM; + } + end_pos = fad[i].pos; + } + + sb = (ErlSubBin *)(hp); + sb->size = fad[0].pos; + if (!(sb->size == 0 && do_trim)) { + sb->thing_word = HEADER_SUB_BIN; + sb->offs = offset; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + } + HRelease(p, hendp, hp); + return ret; +} + static int parse_split_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp, Uint *optp) { Eterm *tp; -- cgit v1.2.3 From 55777538791419a6c3d86c1c440c7eb7fbdd5e51 Mon Sep 17 00:00:00 2001 From: Andrew Bennett Date: Thu, 10 Sep 2015 13:40:21 -0600 Subject: erts: Refactor BIF for binary:match,matches,split with an common do_binary_find() used by match, matches and split. --- erts/emulator/beam/atom.names | 4 +- erts/emulator/beam/erl_bif_binary.c | 1659 ++++++++++++++--------------------- 2 files changed, 678 insertions(+), 985 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 5f27aaa14b..7a50b24818 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -117,11 +117,9 @@ atom bif_timer_server atom binary atom binary_bin_to_list_trap atom binary_copy_trap +atom binary_find_trap atom binary_longest_prefix_trap atom binary_longest_suffix_trap -atom binary_match_trap -atom binary_matches_trap -atom binary_split_trap atom binary_to_list_continue atom binary_to_term_trap atom block diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 6adc61df19..9f72b8c0ac 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -55,10 +55,8 @@ /* Init and local variables */ -static Export binary_match_trap_export; -static BIF_RETTYPE binary_match_trap(BIF_ALIST_3); -static Export binary_matches_trap_export; -static BIF_RETTYPE binary_matches_trap(BIF_ALIST_3); +static Export binary_find_trap_export; +static BIF_RETTYPE binary_find_trap(BIF_ALIST_3); static Export binary_longest_prefix_trap_export; static BIF_RETTYPE binary_longest_prefix_trap(BIF_ALIST_3); static Export binary_longest_suffix_trap_export; @@ -67,26 +65,18 @@ static Export binary_bin_to_list_trap_export; static BIF_RETTYPE binary_bin_to_list_trap(BIF_ALIST_3); static Export binary_copy_trap_export; static BIF_RETTYPE binary_copy_trap(BIF_ALIST_2); -static Export binary_split_trap_export; -static BIF_RETTYPE binary_split_trap(BIF_ALIST_3); static Uint max_loop_limit; static BIF_RETTYPE -binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3); -static BIF_RETTYPE -binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3); +binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3, Uint flags); static BIF_RETTYPE binary_split(Process *p, Eterm arg1, Eterm arg2, Eterm arg3); void erts_init_bif_binary(void) { - erts_init_trap_export(&binary_match_trap_export, - am_erlang, am_binary_match_trap, 3, - &binary_match_trap); - - erts_init_trap_export(&binary_matches_trap_export, - am_erlang, am_binary_matches_trap, 3, - &binary_matches_trap); + erts_init_trap_export(&binary_find_trap_export, + am_erlang, am_binary_find_trap, 3, + &binary_find_trap); erts_init_trap_export(&binary_longest_prefix_trap_export, am_erlang, am_binary_longest_prefix_trap, 3, @@ -104,10 +94,6 @@ void erts_init_bif_binary(void) am_erlang, am_binary_copy_trap, 2, &binary_copy_trap); - erts_init_trap_export(&binary_split_trap_export, - am_erlang, am_binary_split_trap, 3, - &binary_split_trap); - max_loop_limit = 0; return; } @@ -322,8 +308,8 @@ static BMData *create_bmdata(MyAllocator *my, byte *x, Uint len, /* * Aho Corasick - Build a Trie and fill in the failure functions * when all strings are added. - * The algorithm is nicely described by Dieter Bühler of University of - * Tübingen: + * The algorithm is nicely described by Dieter Bühler of University of + * Tübingen: * http://www-sr.informatik.uni-tuebingen.de/~buehler/AC/AC.html */ @@ -573,9 +559,6 @@ static void ac_clean_find_all(ACFindAllState *state) #endif } -#define SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(S) \ - (sizeof(ACFindAllState)+(sizeof(FindallData)*(S).m)) - /* * Differs to the find_first function in that it stores all matches and the values * arte returned only in the state. @@ -853,9 +836,6 @@ static void bm_clean_find_all(BMFindAllState *state) #endif } -#define SIZEOF_BM_SERIALIZED_FIND_ALL_STATE(S) \ - (sizeof(BMFindAllState)+(sizeof(FindallData)*(S).m)) - /* * Differs to the find_first function in that it stores all matches and the * values are returned only in the state. @@ -1038,267 +1018,36 @@ BIF_RETTYPE binary_compile_pattern_1(BIF_ALIST_1) #define DO_BIN_MATCH_BADARG -1 #define DO_BIN_MATCH_RESTART -2 -static int do_binary_match(Process *p, Eterm subject, Uint hsstart, Uint hsend, - Eterm type, Binary *bin, Eterm state_term, - Eterm *res_term) -{ - byte *bytes; - Uint bitoffs, bitsize; - byte *temp_alloc = NULL; - - ERTS_GET_BINARY_BYTES(subject, bytes, bitoffs, bitsize); - if (bitsize != 0) { - goto badarg; - } - if (bitoffs != 0) { - bytes = erts_get_aligned_binary_bytes(subject, &temp_alloc); - } - if (state_term != NIL) { - Eterm *ptr = big_val(state_term); - type = ptr[1]; - } - - if (type == am_bm) { - BMData *bm; - Sint pos; - Eterm ret; - Eterm *hp; - BMFindFirstState state; - Uint reds = get_reds(p, BM_LOOP_FACTOR); - Uint save_reds = reds; - - bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin); -#ifdef HARDDEBUG - dump_bm_data(bm); -#endif - if (state_term == NIL) { - bm_init_find_first_match(&state, hsstart, hsend); - } else { - Eterm *ptr = big_val(state_term); - memcpy(&state,ptr+2,sizeof(state)); - } -#ifdef HARDDEBUG - erts_printf("(bm) state->pos = %ld, state->len = %lu\n",state.pos, - state.len); -#endif - pos = bm_find_first_match(&state, bm, bytes, &reds); - if (pos == BM_NOT_FOUND) { - ret = am_nomatch; - } else if (pos == BM_RESTART) { - int x = (sizeof(BMFindFirstState) / sizeof(Eterm)) + - !!(sizeof(BMFindFirstState) % sizeof(Eterm)); -#ifdef HARDDEBUG - erts_printf("Trap bm!\n"); -#endif - hp = HAlloc(p,x+2); - hp[0] = make_pos_bignum_header(x+1); - hp[1] = type; - memcpy(hp+2,&state,sizeof(state)); - *res_term = make_big(hp); - erts_free_aligned_binary_bytes(temp_alloc); - return DO_BIN_MATCH_RESTART; - } else { - Eterm erlen = erts_make_integer((Uint) bm->len, p); - ret = erts_make_integer(pos,p); - hp = HAlloc(p,3); - ret = TUPLE2(hp, ret, erlen); - } - erts_free_aligned_binary_bytes(temp_alloc); - BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); - *res_term = ret; - return DO_BIN_MATCH_OK; - } else if (type == am_ac) { - ACTrie *act; - Uint pos, rlen; - int acr; - ACFindFirstState state; - Eterm ret; - Eterm *hp; - Uint reds = get_reds(p, AC_LOOP_FACTOR); - Uint save_reds = reds; - - act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin); -#ifdef HARDDEBUG - dump_ac_trie(act); -#endif - if (state_term == NIL) { - ac_init_find_first_match(&state, act, hsstart, hsend); - } else { - Eterm *ptr = big_val(state_term); - memcpy(&state,ptr+2,sizeof(state)); - } - acr = ac_find_first_match(&state, bytes, &pos, &rlen, &reds); - if (acr == AC_NOT_FOUND) { - ret = am_nomatch; - } else if (acr == AC_RESTART) { - int x = (sizeof(state) / sizeof(Eterm)) + - !!(sizeof(ACFindFirstState) % sizeof(Eterm)); -#ifdef HARDDEBUG - erts_printf("Trap ac!\n"); -#endif - hp = HAlloc(p,x+2); - hp[0] = make_pos_bignum_header(x+1); - hp[1] = type; - memcpy(hp+2,&state,sizeof(state)); - *res_term = make_big(hp); - erts_free_aligned_binary_bytes(temp_alloc); - return DO_BIN_MATCH_RESTART; - } else { - Eterm epos = erts_make_integer(pos,p); - Eterm erlen = erts_make_integer(rlen,p); - hp = HAlloc(p,3); - ret = TUPLE2(hp, epos, erlen); - } - erts_free_aligned_binary_bytes(temp_alloc); - BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); - *res_term = ret; - return DO_BIN_MATCH_OK; - } - badarg: - return DO_BIN_MATCH_BADARG; -} - -static int do_binary_matches(Process *p, Eterm subject, Uint hsstart, - Uint hsend, Eterm type, Binary *bin, - Eterm state_term, Eterm *res_term) -{ - byte *bytes; - Uint bitoffs, bitsize; - byte *temp_alloc = NULL; - - ERTS_GET_BINARY_BYTES(subject, bytes, bitoffs, bitsize); - if (bitsize != 0) { - goto badarg; - } - if (bitoffs != 0) { - bytes = erts_get_aligned_binary_bytes(subject, &temp_alloc); - } - if (state_term != NIL) { - Eterm *ptr = big_val(state_term); - type = ptr[1]; - } - - if (type == am_bm) { - BMData *bm; - Sint pos; - Eterm ret,tpl; - Eterm *hp; - BMFindAllState state; - Uint reds = get_reds(p, BM_LOOP_FACTOR); - Uint save_reds = reds; - - bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin); -#ifdef HARDDEBUG - dump_bm_data(bm); -#endif - if (state_term == NIL) { - bm_init_find_all(&state, hsstart, hsend); - } else { - Eterm *ptr = big_val(state_term); - bm_restore_find_all(&state,(char *) (ptr+2)); - } - - pos = bm_find_all_non_overlapping(&state, bm, bytes, &reds); - if (pos == BM_NOT_FOUND) { - ret = NIL; - } else if (pos == BM_RESTART) { - int x = - (SIZEOF_BM_SERIALIZED_FIND_ALL_STATE(state) / sizeof(Eterm)) + - !!(SIZEOF_BM_SERIALIZED_FIND_ALL_STATE(state) % sizeof(Eterm)); -#ifdef HARDDEBUG - erts_printf("Trap bm!\n"); -#endif - hp = HAlloc(p,x+2); - hp[0] = make_pos_bignum_header(x+1); - hp[1] = type; - bm_serialize_find_all(&state, (char *) (hp+2)); - *res_term = make_big(hp); - erts_free_aligned_binary_bytes(temp_alloc); - bm_clean_find_all(&state); - return DO_BIN_MATCH_RESTART; - } else { - FindallData *fad = state.out; - int i; - for (i = 0; i < state.m; ++i) { - fad[i].epos = erts_make_integer(fad[i].pos,p); - fad[i].elen = erts_make_integer(fad[i].len,p); - } - hp = HAlloc(p,state.m * (3 + 2)); - ret = NIL; - for (i = state.m - 1; i >= 0; --i) { - tpl = TUPLE2(hp, fad[i].epos, fad[i].elen); - hp +=3; - ret = CONS(hp,tpl,ret); - hp += 2; - } - } - erts_free_aligned_binary_bytes(temp_alloc); - bm_clean_find_all(&state); - BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); - *res_term = ret; - return DO_BIN_MATCH_OK; - } else if (type == am_ac) { - ACTrie *act; - int acr; - ACFindAllState state; - Eterm ret,tpl; - Eterm *hp; - Uint reds = get_reds(p, AC_LOOP_FACTOR); - Uint save_reds = reds; +#define BINARY_FIND_ALL 0x01 +#define BINARY_SPLIT_TRIM 0x02 +#define BINARY_SPLIT_TRIM_ALL 0x04 - act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin); -#ifdef HARDDEBUG - dump_ac_trie(act); -#endif - if (state_term == NIL) { - ac_init_find_all(&state, act, hsstart, hsend); - } else { - Eterm *ptr = big_val(state_term); - ac_restore_find_all(&state,(char *) (ptr+2)); - } - acr = ac_find_all_non_overlapping(&state, bytes, &reds); - if (acr == AC_NOT_FOUND) { - ret = NIL; - } else if (acr == AC_RESTART) { - int x = - (SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(state) / sizeof(Eterm)) + - !!(SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(state) % sizeof(Eterm)); -#ifdef HARDDEBUG - erts_printf("Trap ac!\n"); -#endif - hp = HAlloc(p,x+2); - hp[0] = make_pos_bignum_header(x+1); - hp[1] = type; - ac_serialize_find_all(&state, (char *) (hp+2)); - *res_term = make_big(hp); - erts_free_aligned_binary_bytes(temp_alloc); - ac_clean_find_all(&state); - return DO_BIN_MATCH_RESTART; - } else { - FindallData *fad = state.out; - int i; - for (i = 0; i < state.m; ++i) { - fad[i].epos = erts_make_integer(fad[i].pos,p); - fad[i].elen = erts_make_integer(fad[i].len,p); - } - hp = HAlloc(p,state.m * (3 + 2)); - ret = NIL; - for (i = state.m - 1; i >= 0; --i) { - tpl = TUPLE2(hp, fad[i].epos, fad[i].elen); - hp +=3; - ret = CONS(hp,tpl,ret); - hp += 2; - } - } - erts_free_aligned_binary_bytes(temp_alloc); - ac_clean_find_all(&state); - BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); - *res_term = ret; - return DO_BIN_MATCH_OK; - } - badarg: - return DO_BIN_MATCH_BADARG; -} +typedef struct BinaryFindState { + Eterm type; + Uint flags; + Uint hsstart; + Uint hsend; + Eterm (*not_found_result) (Process *, Eterm, struct BinaryFindState *); + Eterm (*single_result) (Process *, Eterm, struct BinaryFindState *, Sint, Sint); + Eterm (*global_result) (Process *, Eterm, struct BinaryFindState *, FindallData *, Uint); +} BinaryFindState; + +#define SIZEOF_BINARY_FIND_STATE(S) \ + (sizeof(BinaryFindState)+sizeof(S)) + +#define SIZEOF_BINARY_FIND_ALL_STATE(S) \ + (sizeof(BinaryFindState)+sizeof(S)+(sizeof(FindallData)*(S).m)) + +static Eterm do_match_not_found_result(Process *p, Eterm subject, BinaryFindState *bfs); +static Eterm do_match_single_result(Process *p, Eterm subject, BinaryFindState *bfs, + Sint pos, Sint len); +static Eterm do_match_global_result(Process *p, Eterm subject, BinaryFindState *bfs, + FindallData *fad, Uint fad_sz); +static Eterm do_split_not_found_result(Process *p, Eterm subject, BinaryFindState *bfs); +static Eterm do_split_single_result(Process *p, Eterm subject, BinaryFindState *bfs, + Sint pos, Sint len); +static Eterm do_split_global_result(Process *p, Eterm subject, BinaryFindState *bfs, + FindallData *fad, Uint fad_sz); static int parse_match_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp) { @@ -1363,116 +1112,298 @@ static int parse_match_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp) } } -static BIF_RETTYPE binary_match_trap(BIF_ALIST_3) -{ - int runres; - Eterm result; - Binary *bin = ((ProcBin *) binary_val(BIF_ARG_3))->val; - runres = do_binary_match(BIF_P,BIF_ARG_1,0,0,NIL,bin,BIF_ARG_2,&result); - if (runres == DO_BIN_MATCH_OK) { - BIF_RET(result); - } else { - BUMP_ALL_REDS(BIF_P); - BIF_TRAP3(&binary_match_trap_export, BIF_P, BIF_ARG_1, result, - BIF_ARG_3); - } -} - -static BIF_RETTYPE binary_matches_trap(BIF_ALIST_3) +static int parse_split_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp, Uint *optp) { - int runres; - Eterm result; - Binary *bin = ((ProcBin *) binary_val(BIF_ARG_3))->val; - runres = do_binary_matches(BIF_P,BIF_ARG_1,0,0,NIL,bin,BIF_ARG_2,&result); - if (runres == DO_BIN_MATCH_OK) { - BIF_RET(result); + Eterm *tp; + Uint pos; + Sint len; + *optp = 0; + *posp = 0; + *endp = binary_size(bin); + if (l == THE_NON_VALUE || l == NIL) { + return 0; + } else if (is_list(l)) { + while(is_list(l)) { + Eterm t = CAR(list_val(l)); + Uint orig_size; + if (is_atom(t)) { + if (t == am_global) { + *optp |= BINARY_FIND_ALL; + l = CDR(list_val(l)); + continue; + } + if (t == am_trim) { + *optp |= BINARY_SPLIT_TRIM; + l = CDR(list_val(l)); + continue; + } + if (t == am_trim_all) { + *optp |= BINARY_SPLIT_TRIM_ALL; + l = CDR(list_val(l)); + continue; + } + } + if (!is_tuple(t)) { + goto badarg; + } + tp = tuple_val(t); + if (arityval(*tp) != 2) { + goto badarg; + } + if (tp[1] != am_scope || is_not_tuple(tp[2])) { + goto badarg; + } + tp = tuple_val(tp[2]); + if (arityval(*tp) != 2) { + goto badarg; + } + if (!term_to_Uint(tp[1], &pos)) { + goto badarg; + } + if (!term_to_Sint(tp[2], &len)) { + goto badarg; + } + if (len < 0) { + Uint lentmp = -(Uint)len; + /* overflow */ + if ((Sint)lentmp < 0) { + goto badarg; + } + len = lentmp; + pos -= len; + } + /* overflow */ + if ((pos + len) < pos || (len > 0 && (pos + len) == pos)) { + goto badarg; + } + *endp = len + pos; + *posp = pos; + if ((orig_size = binary_size(bin)) < pos || + orig_size < (*endp)) { + goto badarg; + } + l = CDR(list_val(l)); + } + return 0; } else { - BUMP_ALL_REDS(BIF_P); - BIF_TRAP3(&binary_matches_trap_export, BIF_P, BIF_ARG_1, result, - BIF_ARG_3); + badarg: + return 1; } } -BIF_RETTYPE binary_match_3(BIF_ALIST_3) -{ - return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); -} - -static BIF_RETTYPE -binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) +static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binary *bin, + Eterm state_term, Eterm *res_term) { - Uint hsstart; - Uint hsend; - Eterm *tp; - Eterm type; - Binary *bin; - Eterm bin_term = NIL; - int runres; - Eterm result; + byte *bytes; + Uint bitoffs, bitsize; + byte *temp_alloc = NULL; + char *state_ptr = NULL; - if (is_not_binary(arg1)) { - goto badarg; - } - if (parse_match_opts_list(arg3,arg1,&hsstart,&hsend)) { - goto badarg; - } - if (hsend == 0) { - BIF_RET(am_nomatch); - } - if (is_tuple(arg2)) { - tp = tuple_val(arg2); - if (arityval(*tp) != 2 || is_not_atom(tp[1])) { - goto badarg; - } - if (((tp[1] != am_bm) && (tp[1] != am_ac)) || - !ERTS_TERM_IS_MAGIC_BINARY(tp[2])) { - goto badarg; - } - type = tp[1]; - bin = ((ProcBin *) binary_val(tp[2]))->val; - if (type == am_bm && - ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_bm) { - goto badarg; - } - if (type == am_ac && - ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_ac) { - goto badarg; - } - bin_term = tp[2]; - } else if (do_binary_match_compile(arg2,&type,&bin)) { + ERTS_GET_BINARY_BYTES(subject, bytes, bitoffs, bitsize); + if (bitsize != 0) { goto badarg; } - runres = do_binary_match(p,arg1,hsstart,hsend,type,bin,NIL,&result); - if (runres == DO_BIN_MATCH_RESTART && bin_term == NIL) { - Eterm *hp = HAlloc(p, PROC_BIN_SIZE); - bin_term = erts_mk_magic_binary_term(&hp, &MSO(p), bin); - } else if (bin_term == NIL) { - erts_bin_free(bin); + if (bitoffs != 0) { + bytes = erts_get_aligned_binary_bytes(subject, &temp_alloc); } - switch (runres) { - case DO_BIN_MATCH_OK: - BIF_RET(result); - case DO_BIN_MATCH_RESTART: - BUMP_ALL_REDS(p); - BIF_TRAP3(&binary_match_trap_export, p, arg1, result, bin_term); - default: - goto badarg; + if (state_term != NIL) { + state_ptr = (char *)(big_val(state_term)); + state_ptr += sizeof(Eterm); + bfs = (BinaryFindState *)(state_ptr); + state_ptr += sizeof(BinaryFindState); } - badarg: - BIF_ERROR(p,BADARG); -} -BIF_RETTYPE binary_matches_3(BIF_ALIST_3) -{ - return binary_matches(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); + if (bfs->flags & BINARY_FIND_ALL) { + if (bfs->type == am_bm) { + BMData *bm; + Sint pos; + Eterm *hp; + BMFindAllState state; + Uint reds = get_reds(p, BM_LOOP_FACTOR); + Uint save_reds = reds; + + bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin); +#ifdef HARDDEBUG + dump_bm_data(bm); +#endif + if (state_term == NIL) { + bm_init_find_all(&state, bfs->hsstart, bfs->hsend); + } else { + bm_restore_find_all(&state, state_ptr); + } + + pos = bm_find_all_non_overlapping(&state, bm, bytes, &reds); + if (pos == BM_NOT_FOUND) { + *res_term = bfs->not_found_result(p, subject, bfs); + } else if (pos == BM_RESTART) { + int x = + (SIZEOF_BINARY_FIND_ALL_STATE(state) / sizeof(Eterm)) + + !!(SIZEOF_BINARY_FIND_ALL_STATE(state) % sizeof(Eterm)); +#ifdef HARDDEBUG + erts_printf("Trap bm!\n"); +#endif + hp = HAlloc(p, x+1); + hp[0] = make_pos_bignum_header(x); + state_ptr = (char *)(hp); + memcpy((void *)(state_ptr+sizeof(Eterm)), bfs, sizeof(BinaryFindState)); + bm_serialize_find_all(&state, state_ptr+sizeof(Eterm)+sizeof(BinaryFindState)); + *res_term = make_big(hp); + erts_free_aligned_binary_bytes(temp_alloc); + bm_clean_find_all(&state); + return DO_BIN_MATCH_RESTART; + } else { + *res_term = bfs->global_result(p, subject, bfs, state.out, state.m); + } + erts_free_aligned_binary_bytes(temp_alloc); + bm_clean_find_all(&state); + BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); + return DO_BIN_MATCH_OK; + } else if (bfs->type == am_ac) { + ACTrie *act; + int acr; + ACFindAllState state; + Eterm *hp; + Uint reds = get_reds(p, AC_LOOP_FACTOR); + Uint save_reds = reds; + + act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin); +#ifdef HARDDEBUG + dump_ac_trie(act); +#endif + if (state_term == NIL) { + ac_init_find_all(&state, act, bfs->hsstart, bfs->hsend); + } else { + ac_restore_find_all(&state, state_ptr); + } + acr = ac_find_all_non_overlapping(&state, bytes, &reds); + if (acr == AC_NOT_FOUND) { + *res_term = bfs->not_found_result(p, subject, bfs); + } else if (acr == AC_RESTART) { + int x = + (SIZEOF_BINARY_FIND_ALL_STATE(state) / sizeof(Eterm)) + + !!(SIZEOF_BINARY_FIND_ALL_STATE(state) % sizeof(Eterm)); +#ifdef HARDDEBUG + erts_printf("Trap ac!\n"); +#endif + hp = HAlloc(p, x+1); + hp[0] = make_pos_bignum_header(x); + state_ptr = (char *)(hp); + memcpy((void *)(state_ptr+sizeof(Eterm)), bfs, sizeof(BinaryFindState)); + ac_serialize_find_all(&state, state_ptr+sizeof(Eterm)+sizeof(BinaryFindState)); + *res_term = make_big(hp); + erts_free_aligned_binary_bytes(temp_alloc); + ac_clean_find_all(&state); + return DO_BIN_MATCH_RESTART; + } else { + *res_term = bfs->global_result(p, subject, bfs, state.out, state.m); + } + erts_free_aligned_binary_bytes(temp_alloc); + ac_clean_find_all(&state); + BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); + return DO_BIN_MATCH_OK; + } + } else { + if (bfs->type == am_bm) { + BMData *bm; + Sint pos; + Eterm *hp; + BMFindFirstState state; + Uint reds = get_reds(p, BM_LOOP_FACTOR); + Uint save_reds = reds; + + bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin); +#ifdef HARDDEBUG + dump_bm_data(bm); +#endif + if (state_term == NIL) { + bm_init_find_first_match(&state, bfs->hsstart, bfs->hsend); + } else { + memcpy((void *)(&state), (const void *)(state_ptr), sizeof(BMFindFirstState)); + } + +#ifdef HARDDEBUG + erts_printf("(bm) state->pos = %ld, state->len = %lu\n",state.pos, + state.len); +#endif + pos = bm_find_first_match(&state, bm, bytes, &reds); + if (pos == BM_NOT_FOUND) { + *res_term = bfs->not_found_result(p, subject, bfs); + } else if (pos == BM_RESTART) { + int x = + (SIZEOF_BINARY_FIND_STATE(state) / sizeof(Eterm)) + + !!(SIZEOF_BINARY_FIND_STATE(state) % sizeof(Eterm)); +#ifdef HARDDEBUG + erts_printf("Trap bm!\n"); +#endif + hp = HAlloc(p, x+1); + hp[0] = make_pos_bignum_header(x); + state_ptr = (char *)(hp); + memcpy((void *)(state_ptr+sizeof(Eterm)), bfs, sizeof(BinaryFindState)); + memcpy((void *)(state_ptr+sizeof(Eterm)+sizeof(BinaryFindState)), + (const void *)(&state), sizeof(BMFindFirstState)); + *res_term = make_big(hp); + erts_free_aligned_binary_bytes(temp_alloc); + return DO_BIN_MATCH_RESTART; + } else { + *res_term = bfs->single_result(p, subject, bfs, pos, bm->len); + } + erts_free_aligned_binary_bytes(temp_alloc); + BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); + return DO_BIN_MATCH_OK; + } else if (bfs->type == am_ac) { + ACTrie *act; + Uint pos, rlen; + int acr; + ACFindFirstState state; + Eterm *hp; + Uint reds = get_reds(p, AC_LOOP_FACTOR); + Uint save_reds = reds; + + act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin); +#ifdef HARDDEBUG + dump_ac_trie(act); +#endif + if (state_term == NIL) { + ac_init_find_first_match(&state, act, bfs->hsstart, bfs->hsend); + } else { + memcpy((void *)(&state), (const void *)(state_ptr), sizeof(ACFindFirstState)); + } + acr = ac_find_first_match(&state, bytes, &pos, &rlen, &reds); + if (acr == AC_NOT_FOUND) { + *res_term = bfs->not_found_result(p, subject, bfs); + } else if (acr == AC_RESTART) { + int x = + (SIZEOF_BINARY_FIND_STATE(state) / sizeof(Eterm)) + + !!(SIZEOF_BINARY_FIND_STATE(state) % sizeof(Eterm)); +#ifdef HARDDEBUG + erts_printf("Trap ac!\n"); +#endif + hp = HAlloc(p, x+1); + hp[0] = make_pos_bignum_header(x); + state_ptr = (char *)(hp); + memcpy((void *)(state_ptr+sizeof(Eterm)), bfs, sizeof(BinaryFindState)); + memcpy((void *)(state_ptr+sizeof(Eterm)+sizeof(BinaryFindState)), + (const void *)(&state), sizeof(ACFindFirstState)); + *res_term = make_big(hp); + erts_free_aligned_binary_bytes(temp_alloc); + return DO_BIN_MATCH_RESTART; + } else { + *res_term = bfs->single_result(p, subject, bfs, pos, rlen); + } + erts_free_aligned_binary_bytes(temp_alloc); + BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); + return DO_BIN_MATCH_OK; + } + } + badarg: + return DO_BIN_MATCH_BADARG; } static BIF_RETTYPE -binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) +binary_match(Process *p, Eterm arg1, Eterm arg2, Eterm arg3, Uint flags) { - Uint hsstart, hsend; + BinaryFindState bfs; Eterm *tp; - Eterm type; Binary *bin; Eterm bin_term = NIL; int runres; @@ -1481,11 +1412,12 @@ binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) if (is_not_binary(arg1)) { goto badarg; } - if (parse_match_opts_list(arg3,arg1,&hsstart,&hsend)) { + bfs.flags = flags; + if (parse_match_opts_list(arg3, arg1, &(bfs.hsstart), &(bfs.hsend))) { goto badarg; } - if (hsend == 0) { - BIF_RET(NIL); + if (bfs.hsend == 0) { + BIF_RET(do_match_not_found_result(p, arg1, &bfs)); } if (is_tuple(arg2)) { tp = tuple_val(arg2); @@ -1496,22 +1428,24 @@ binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) !ERTS_TERM_IS_MAGIC_BINARY(tp[2])) { goto badarg; } - type = tp[1]; + bfs.type = tp[1]; bin = ((ProcBin *) binary_val(tp[2]))->val; - if (type == am_bm && + if (bfs.type == am_bm && ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_bm) { goto badarg; } - if (type == am_ac && + if (bfs.type == am_ac && ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_ac) { goto badarg; } bin_term = tp[2]; - } else if (do_binary_match_compile(arg2,&type,&bin)) { + } else if (do_binary_match_compile(arg2, &(bfs.type), &bin)) { goto badarg; } - runres = do_binary_matches(p,arg1,hsstart,hsend,type,bin, - NIL,&result); + bfs.not_found_result = &do_match_not_found_result; + bfs.single_result = &do_match_single_result; + bfs.global_result = &do_match_global_result; + runres = do_binary_find(p, arg1, &bfs, bin, NIL, &result); if (runres == DO_BIN_MATCH_RESTART && bin_term == NIL) { Eterm *hp = HAlloc(p, PROC_BIN_SIZE); bin_term = erts_mk_magic_binary_term(&hp, &MSO(p), bin); @@ -1523,8 +1457,7 @@ binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) BIF_RET(result); case DO_BIN_MATCH_RESTART: BUMP_ALL_REDS(p); - BIF_TRAP3(&binary_matches_trap_export, p, arg1, result, - bin_term); + BIF_TRAP3(&binary_find_trap_export, p, arg1, result, bin_term); default: goto badarg; } @@ -1532,98 +1465,392 @@ binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) BIF_ERROR(p,BADARG); } - BIF_RETTYPE binary_match_2(BIF_ALIST_2) { - return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE); + return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE, 0); } +BIF_RETTYPE binary_match_3(BIF_ALIST_3) +{ + return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, 0); +} BIF_RETTYPE binary_matches_2(BIF_ALIST_2) { - return binary_matches(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE); + return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE, BINARY_FIND_ALL); } +BIF_RETTYPE binary_matches_3(BIF_ALIST_3) +{ + return binary_match(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, BINARY_FIND_ALL); +} -BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen) +static BIF_RETTYPE +binary_split(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) { - Uint pos; - Sint len; - size_t orig_size; - Eterm orig; - Uint offset; - Uint bit_offset; - Uint bit_size; - Eterm* hp; - ErlSubBin* sb; + BinaryFindState bfs; + Eterm *tp; + Binary *bin; + Eterm bin_term = NIL; + int runres; + Eterm result; - if (is_not_binary(binary)) { + if (is_not_binary(arg1)) { goto badarg; } - if (!term_to_Uint(epos, &pos)) { + if (parse_split_opts_list(arg3, arg1, &(bfs.hsstart), &(bfs.hsend), &(bfs.flags))) { goto badarg; } - if (!term_to_Sint(elen, &len)) { - goto badarg; + if (bfs.hsend == 0) { + result = do_split_not_found_result(p, arg1, &bfs); + BIF_RET(result); } - if (len < 0) { - Uint lentmp = -(Uint)len; - /* overflow */ - if ((Sint)lentmp < 0) { + if (is_tuple(arg2)) { + tp = tuple_val(arg2); + if (arityval(*tp) != 2 || is_not_atom(tp[1])) { goto badarg; } - len = lentmp; - if (len > pos) { + if (((tp[1] != am_bm) && (tp[1] != am_ac)) || + !ERTS_TERM_IS_MAGIC_BINARY(tp[2])) { goto badarg; } - pos -= len; - } - /* overflow */ - if ((pos + len) < pos || (len > 0 && (pos + len) == pos)){ + bfs.type = tp[1]; + bin = ((ProcBin *) binary_val(tp[2]))->val; + if (bfs.type == am_bm && + ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_bm) { + goto badarg; + } + if (bfs.type == am_ac && + ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_ac) { + goto badarg; + } + bin_term = tp[2]; + } else if (do_binary_match_compile(arg2, &(bfs.type), &bin)) { goto badarg; } - if ((orig_size = binary_size(binary)) < pos || - orig_size < (pos + len)) { + bfs.not_found_result = &do_split_not_found_result; + bfs.single_result = &do_split_single_result; + bfs.global_result = &do_split_global_result; + runres = do_binary_find(p, arg1, &bfs, bin, NIL, &result); + if (runres == DO_BIN_MATCH_RESTART && bin_term == NIL) { + Eterm *hp = HAlloc(p, PROC_BIN_SIZE); + bin_term = erts_mk_magic_binary_term(&hp, &MSO(p), bin); + } else if (bin_term == NIL) { + erts_bin_free(bin); + } + switch(runres) { + case DO_BIN_MATCH_OK: + BIF_RET(result); + case DO_BIN_MATCH_RESTART: + BIF_TRAP3(&binary_find_trap_export, p, arg1, result, bin_term); + default: goto badarg; } + badarg: + BIF_ERROR(p, BADARG); +} +BIF_RETTYPE binary_split_2(BIF_ALIST_2) +{ + return binary_split(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE); +} +BIF_RETTYPE binary_split_3(BIF_ALIST_3) +{ + return binary_split(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); +} - hp = HAlloc(p, ERL_SUB_BIN_SIZE); +static Eterm do_match_not_found_result(Process *p, Eterm subject, BinaryFindState *bfs) +{ + if (bfs->flags & BINARY_FIND_ALL) { + return NIL; + } else { + return am_nomatch; + } +} - ERTS_GET_REAL_BIN(binary, orig, offset, bit_offset, bit_size); - sb = (ErlSubBin *) hp; - sb->thing_word = HEADER_SUB_BIN; - sb->size = len; - sb->offs = offset + pos; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; +static Eterm do_match_single_result(Process *p, Eterm subject, BinaryFindState *bfs, + Sint pos, Sint len) +{ + Eterm erlen; + Eterm *hp; + Eterm ret; - BIF_RET(make_binary(sb)); + erlen = erts_make_integer((Uint)(len), p); + ret = erts_make_integer(pos, p); + hp = HAlloc(p, 3); + ret = TUPLE2(hp, ret, erlen); - badarg: - BIF_ERROR(p, BADARG); + return ret; } -#define ERTS_NEED_GC(p, need) ((HEAP_LIMIT((p)) - HEAP_TOP((p))) <= (need)) - -BIF_RETTYPE erts_gc_binary_part(Process *p, Eterm *reg, Eterm live, int range_is_tuple) +static Eterm do_match_global_result(Process *p, Eterm subject, BinaryFindState *bfs, + FindallData *fad, Uint fad_sz) { - Uint pos; - Sint len; - size_t orig_size; - Eterm orig; - Uint offset; - Uint bit_offset; - Uint bit_size; - Eterm* hp; - ErlSubBin* sb; - Eterm binary; - Eterm *tp; - Eterm epos, elen; - int extra_args; + Sint i; + Eterm tpl; + Eterm *hp; + Eterm ret; + + for (i = 0; i < fad_sz; ++i) { + fad[i].epos = erts_make_integer(fad[i].pos, p); + fad[i].elen = erts_make_integer(fad[i].len, p); + } + hp = HAlloc(p, fad_sz * (3 + 2)); + ret = NIL; + for (i = fad_sz - 1; i >= 0; --i) { + tpl = TUPLE2(hp, fad[i].epos, fad[i].elen); + hp += 3; + ret = CONS(hp, tpl, ret); + hp += 2; + } + + return ret; +} + +static Eterm do_split_not_found_result(Process *p, Eterm subject, BinaryFindState *bfs) +{ + Eterm *hp; + Eterm ret; + + hp = HAlloc(p, 2); + ret = CONS(hp, subject, NIL); + + return ret; +} + +static Eterm do_split_single_result(Process *p, Eterm subject, BinaryFindState *bfs, + Sint pos, Sint len) +{ + size_t orig_size; + Eterm orig; + Uint offset; + Uint bit_offset; + Uint bit_size; + ErlSubBin *sb1; + ErlSubBin *sb2; + Eterm *hp; + Eterm ret; + + orig_size = binary_size(subject); + + if ((bfs->flags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) && + (orig_size - pos - len) == 0) { + if (pos == 0) { + ret = NIL; + } else { + hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = (ErlSubBin *) hp; + sb1->thing_word = HEADER_SUB_BIN; + sb1->size = pos; + sb1->offs = offset; + sb1->orig = orig; + sb1->bitoffs = bit_offset; + sb1->bitsize = bit_size; + sb1->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = CONS(hp, make_binary(sb1), NIL); + hp += 2; + } + } else { + if ((bfs->flags & BINARY_SPLIT_TRIM_ALL) && (pos == 0)) { + hp = HAlloc(p, 1 * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = NULL; + } else { + hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + sb1 = (ErlSubBin *) hp; + sb1->thing_word = HEADER_SUB_BIN; + sb1->size = pos; + sb1->offs = offset; + sb1->orig = orig; + sb1->bitoffs = bit_offset; + sb1->bitsize = 0; + sb1->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + } + + sb2 = (ErlSubBin *) hp; + sb2->thing_word = HEADER_SUB_BIN; + sb2->size = orig_size - pos - len; + sb2->offs = offset + pos + len; + sb2->orig = orig; + sb2->bitoffs = bit_offset; + sb2->bitsize = bit_size; + sb2->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + ret = CONS(hp, make_binary(sb2), NIL); + hp += 2; + if (sb1 != NULL) { + ret = CONS(hp, make_binary(sb1), ret); + hp += 2; + } + } + return ret; +} + +static Eterm do_split_global_result(Process *p, Eterm subject, BinaryFindState *bfs, + FindallData *fad, Uint fad_sz) +{ + size_t orig_size; + Eterm orig; + Uint offset; + Uint bit_offset; + Uint bit_size; + ErlSubBin *sb; + Sint i; + Sint tail; + Uint list_size; + Uint end_pos; + Uint do_trim = bfs->flags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL); + Eterm *hp; + Eterm *hendp; + Eterm ret; + + tail = fad_sz - 1; + list_size = fad_sz + 1; + orig_size = binary_size(subject); + end_pos = (Uint)(orig_size); + + hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2)); + hendp = hp + list_size * (ERL_SUB_BIN_SIZE + 2); + ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); + ASSERT(bit_size == 0); + + ret = NIL; + + for (i = tail; i >= 0; --i) { + sb = (ErlSubBin *)(hp); + sb->size = end_pos - (fad[i].pos + fad[i].len); + if (!(sb->size == 0 && do_trim)) { + sb->thing_word = HEADER_SUB_BIN; + sb->offs = offset + fad[i].pos + fad[i].len; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + do_trim &= ~BINARY_SPLIT_TRIM; + } + end_pos = fad[i].pos; + } + + sb = (ErlSubBin *)(hp); + sb->size = fad[0].pos; + if (!(sb->size == 0 && do_trim)) { + sb->thing_word = HEADER_SUB_BIN; + sb->offs = offset; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + ret = CONS(hp, make_binary(sb), ret); + hp += 2; + } + HRelease(p, hendp, hp); + return ret; +} + +static BIF_RETTYPE binary_find_trap(BIF_ALIST_3) +{ + int runres; + Eterm result; + Binary *bin = ((ProcBin *) binary_val(BIF_ARG_3))->val; + runres = do_binary_find(BIF_P, BIF_ARG_1, THE_NON_VALUE, bin, BIF_ARG_2, &result); + if (runres == DO_BIN_MATCH_OK) { + BIF_RET(result); + } else { + BUMP_ALL_REDS(BIF_P); + BIF_TRAP3(&binary_find_trap_export, BIF_P, BIF_ARG_1, result, BIF_ARG_3); + } +} + +BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen) +{ + Uint pos; + Sint len; + size_t orig_size; + Eterm orig; + Uint offset; + Uint bit_offset; + Uint bit_size; + Eterm* hp; + ErlSubBin* sb; + + if (is_not_binary(binary)) { + goto badarg; + } + if (!term_to_Uint(epos, &pos)) { + goto badarg; + } + if (!term_to_Sint(elen, &len)) { + goto badarg; + } + if (len < 0) { + Uint lentmp = -(Uint)len; + /* overflow */ + if ((Sint)lentmp < 0) { + goto badarg; + } + len = lentmp; + if (len > pos) { + goto badarg; + } + pos -= len; + } + /* overflow */ + if ((pos + len) < pos || (len > 0 && (pos + len) == pos)){ + goto badarg; + } + if ((orig_size = binary_size(binary)) < pos || + orig_size < (pos + len)) { + goto badarg; + } + + + + hp = HAlloc(p, ERL_SUB_BIN_SIZE); + + ERTS_GET_REAL_BIN(binary, orig, offset, bit_offset, bit_size); + sb = (ErlSubBin *) hp; + sb->thing_word = HEADER_SUB_BIN; + sb->size = len; + sb->offs = offset + pos; + sb->orig = orig; + sb->bitoffs = bit_offset; + sb->bitsize = 0; + sb->is_writable = 0; + + BIF_RET(make_binary(sb)); + + badarg: + BIF_ERROR(p, BADARG); +} + +#define ERTS_NEED_GC(p, need) ((HEAP_LIMIT((p)) - HEAP_TOP((p))) <= (need)) + +BIF_RETTYPE erts_gc_binary_part(Process *p, Eterm *reg, Eterm live, int range_is_tuple) +{ + Uint pos; + Sint len; + size_t orig_size; + Eterm orig; + Uint offset; + Uint bit_offset; + Uint bit_size; + Eterm* hp; + ErlSubBin* sb; + Eterm binary; + Eterm *tp; + Eterm epos, elen; + int extra_args; if (range_is_tuple) { @@ -2542,538 +2769,6 @@ BIF_RETTYPE binary_copy_2(BIF_ALIST_2) return do_binary_copy(BIF_P,BIF_ARG_1,BIF_ARG_2); } -static Eterm do_split_single_result(Process*, Eterm subject, - Sint pos, Sint len, Uint hsflags); -static Eterm do_split_global_result(Process*, FindallData *fad, Uint fad_sz, - Eterm subject, Uint hsflags); - -#define BINARY_SPLIT_GLOBAL 0x01 -#define BINARY_SPLIT_TRIM 0x02 -#define BINARY_SPLIT_TRIM_ALL 0x04 - -static int do_binary_split(Process *p, Eterm subject, Uint hsstart, - Uint hsend, Uint hsflags, Eterm type, Binary *bin, - Eterm state_term, Eterm *res_term) -{ - byte *bytes; - Uint bitoffs, bitsize; - byte *temp_alloc = NULL; - - ERTS_GET_BINARY_BYTES(subject, bytes, bitoffs, bitsize); - if (bitsize != 0) { - goto badarg; - } - if (bitoffs != 0) { - bytes = erts_get_aligned_binary_bytes(subject, &temp_alloc); - } - if (state_term != NIL) { - Eterm *ptr = big_val(state_term); - type = ptr[1]; - hsflags = (Uint)(ptr[2]); - } - - if (hsflags & BINARY_SPLIT_GLOBAL) { - if (type == am_bm) { - BMData *bm; - Sint pos; - Eterm *hp; - BMFindAllState state; - Uint reds = get_reds(p, BM_LOOP_FACTOR); - Uint save_reds = reds; - - bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin); -#ifdef HARDDEBUG - dump_bm_data(bm); -#endif - if (state_term == NIL) { - bm_init_find_all(&state, hsstart, hsend); - } else { - Eterm *ptr = big_val(state_term); - bm_restore_find_all(&state, (char *)(ptr+3)); - } - - pos = bm_find_all_non_overlapping(&state, bm, bytes, &reds); - if (pos == BM_NOT_FOUND) { - hp = HAlloc(p, 2); - *res_term = CONS(hp, subject, NIL); - } else if (pos == BM_RESTART) { - int x = - (SIZEOF_BM_SERIALIZED_FIND_ALL_STATE(state) / sizeof(Eterm)) + - !!(SIZEOF_BM_SERIALIZED_FIND_ALL_STATE(state) % sizeof(Eterm)); -#ifdef HARDDEBUG - erts_printf("Trap bm!\n"); -#endif - hp = HAlloc(p, x+3); - hp[0] = make_pos_bignum_header(x+2); - hp[1] = type; - hp[2] = (Eterm)(hsflags); - bm_serialize_find_all(&state, (char *)(hp+3)); - *res_term = make_big(hp); - erts_free_aligned_binary_bytes(temp_alloc); - bm_clean_find_all(&state); - return DO_BIN_MATCH_RESTART; - } else { - *res_term = do_split_global_result(p, state.out, state.m, subject, hsflags); - } - erts_free_aligned_binary_bytes(temp_alloc); - bm_clean_find_all(&state); - BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); - return DO_BIN_MATCH_OK; - } else if (type == am_ac) { - ACTrie *act; - int acr; - ACFindAllState state; - Eterm *hp; - Uint reds = get_reds(p, AC_LOOP_FACTOR); - Uint save_reds = reds; - - act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin); -#ifdef HARDDEBUG - dump_ac_trie(act); -#endif - if (state_term == NIL) { - ac_init_find_all(&state, act, hsstart, hsend); - } else { - Eterm *ptr = big_val(state_term); - ac_restore_find_all(&state, (char *)(ptr+3)); - } - acr = ac_find_all_non_overlapping(&state, bytes, &reds); - if (acr == AC_NOT_FOUND) { - hp = HAlloc(p, 2); - *res_term = CONS(hp, subject, NIL); - } else if (acr == AC_RESTART) { - int x = (SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(state) / sizeof(Eterm)) + - !!(SIZEOF_AC_SERIALIZED_FIND_ALL_STATE(state) % sizeof(Eterm)); -#ifdef HARDDEBUG - erts_printf("Trap ac!\n"); -#endif - hp = HAlloc(p, x+3); - hp[0] = make_pos_bignum_header(x+2); - hp[1] = type; - hp[2] = (Eterm)(hsflags); - ac_serialize_find_all(&state, (char *)(hp+3)); - *res_term = make_big(hp); - erts_free_aligned_binary_bytes(temp_alloc); - ac_clean_find_all(&state); - return DO_BIN_MATCH_RESTART; - } else { - *res_term = do_split_global_result(p, state.out, state.m, subject, hsflags); - } - erts_free_aligned_binary_bytes(temp_alloc); - ac_clean_find_all(&state); - BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); - return DO_BIN_MATCH_OK; - } - } else { - if (type == am_bm) { - BMData *bm; - Sint pos; - Eterm *hp; - BMFindFirstState state; - Uint reds = get_reds(p, BM_LOOP_FACTOR); - Uint save_reds = reds; - - bm = (BMData *) ERTS_MAGIC_BIN_DATA(bin); -#ifdef HARDDEBUG - dump_bm_data(bm); -#endif - if (state_term == NIL) { - bm_init_find_first_match(&state, hsstart, hsend); - } else { - Eterm *ptr = big_val(state_term); - memcpy((void *)(&state), (const void *)(ptr+3), sizeof(BMFindFirstState)); - } - -#ifdef HARDDEBUG - erts_printf("(bm) state->pos = %ld, state->len = %lu\n",state.pos, - state.len); -#endif - pos = bm_find_first_match(&state, bm, bytes, &reds); - if (pos == BM_NOT_FOUND) { - hp = HAlloc(p, 2); - *res_term = CONS(hp, subject, NIL); - } else if (pos == BM_RESTART) { - int x = - (sizeof(state) / sizeof(Eterm)) + - !!(sizeof(state) % sizeof(Eterm)); -#ifdef HARDDEBUG - erts_printf("Trap bm!\n"); -#endif - hp = HAlloc(p, x+3); - hp[0] = make_pos_bignum_header(x+2); - hp[1] = type; - hp[2] = (Eterm)(hsflags); - memcpy((void *)(hp+3), (const void *)(&state), sizeof(state)); - *res_term = make_big(hp); - erts_free_aligned_binary_bytes(temp_alloc); - return DO_BIN_MATCH_RESTART; - } else { - *res_term = do_split_single_result(p, subject, pos, bm->len, hsflags); - } - erts_free_aligned_binary_bytes(temp_alloc); - BUMP_REDS(p, (save_reds - reds) / BM_LOOP_FACTOR); - return DO_BIN_MATCH_OK; - } else if (type == am_ac) { - ACTrie *act; - Uint pos, rlen; - int acr; - ACFindFirstState state; - Eterm *hp; - Uint reds = get_reds(p, AC_LOOP_FACTOR); - Uint save_reds = reds; - - act = (ACTrie *) ERTS_MAGIC_BIN_DATA(bin); -#ifdef HARDDEBUG - dump_ac_trie(act); -#endif - if (state_term == NIL) { - ac_init_find_first_match(&state, act, hsstart, hsend); - } else { - Eterm *ptr = big_val(state_term); - memcpy((void *)(&state), (const void *)(ptr+3), sizeof(ACFindFirstState)); - } - acr = ac_find_first_match(&state, bytes, &pos, &rlen, &reds); - if (acr == AC_NOT_FOUND) { - hp = HAlloc(p, 2); - *res_term = CONS(hp, subject, NIL); - } else if (acr == AC_RESTART) { - int x = - (sizeof(state) / sizeof(Eterm)) + - !!(sizeof(state) % sizeof(Eterm)); -#ifdef HARDDEBUG - erts_printf("Trap ac!\n"); -#endif - hp = HAlloc(p, x+3); - hp[0] = make_pos_bignum_header(x+2); - hp[1] = type; - hp[2] = (Eterm)(hsflags); - memcpy((void *)(hp+3), (const void *)(&state), sizeof(state)); - *res_term = make_big(hp); - erts_free_aligned_binary_bytes(temp_alloc); - return DO_BIN_MATCH_RESTART; - } else { - *res_term = do_split_single_result(p, subject, pos, rlen, hsflags); - } - erts_free_aligned_binary_bytes(temp_alloc); - BUMP_REDS(p, (save_reds - reds) / AC_LOOP_FACTOR); - return DO_BIN_MATCH_OK; - } - } - badarg: - return DO_BIN_MATCH_BADARG; -} - -static Eterm do_split_single_result(Process* p, Eterm subject, - Sint pos, Sint len, Uint hsflags) -{ - size_t orig_size; - Eterm orig; - Uint offset; - Uint bit_offset; - Uint bit_size; - ErlSubBin *sb1; - ErlSubBin *sb2; - Eterm* hp; - Eterm ret; - - orig_size = binary_size(subject); - - if ((hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL)) && (orig_size - pos - len) == 0) { - if (pos != 0) { - ret = NIL; - } else { - hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - sb1 = (ErlSubBin *) hp; - sb1->thing_word = HEADER_SUB_BIN; - sb1->size = pos; - sb1->offs = offset; - sb1->orig = orig; - sb1->bitoffs = bit_offset; - sb1->bitsize = bit_size; - sb1->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - - ret = CONS(hp, make_binary(sb1), NIL); - hp += 2; - } - } else { - if ((hsflags & BINARY_SPLIT_TRIM_ALL) && (pos == 0)) { - hp = HAlloc(p, 1 * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - sb1 = NULL; - } else { - hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2)); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - sb1 = (ErlSubBin *) hp; - sb1->thing_word = HEADER_SUB_BIN; - sb1->size = pos; - sb1->offs = offset; - sb1->orig = orig; - sb1->bitoffs = bit_offset; - sb1->bitsize = 0; - sb1->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - } - - sb2 = (ErlSubBin *) hp; - sb2->thing_word = HEADER_SUB_BIN; - sb2->size = orig_size - pos - len; - sb2->offs = offset + pos + len; - sb2->orig = orig; - sb2->bitoffs = bit_offset; - sb2->bitsize = bit_size; - sb2->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - - ret = CONS(hp, make_binary(sb2), NIL); - hp += 2; - if (sb1 != NULL) { - ret = CONS(hp, make_binary(sb1), ret); - hp += 2; - } - } - return ret; -} - -static Eterm do_split_global_result(Process* p, FindallData *fad, Uint fad_sz, - Uint subject, Uint hsflags) -{ - size_t orig_size; - Eterm orig; - Uint offset; - Uint bit_offset; - Uint bit_size; - ErlSubBin *sb; - Sint i; - Sint tail; - Uint list_size; - Uint end_pos; - Uint do_trim = hsflags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL); - Eterm* hp; - Eterm* hendp; - Eterm ret; - - tail = fad_sz - 1; - list_size = fad_sz + 1; - orig_size = binary_size(subject); - end_pos = (Uint)(orig_size); - - hp = HAlloc(p, list_size * (ERL_SUB_BIN_SIZE + 2)); - hendp = hp + list_size * (ERL_SUB_BIN_SIZE + 2); - ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size); - ASSERT(bit_size == 0); - - ret = NIL; - - for (i = tail; i >= 0; --i) { - sb = (ErlSubBin *)(hp); - sb->size = end_pos - (fad[i].pos + fad[i].len); - if (!(sb->size == 0 && do_trim)) { - sb->thing_word = HEADER_SUB_BIN; - sb->offs = offset + fad[i].pos + fad[i].len; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - ret = CONS(hp, make_binary(sb), ret); - hp += 2; - do_trim &= ~BINARY_SPLIT_TRIM; - } - end_pos = fad[i].pos; - } - - sb = (ErlSubBin *)(hp); - sb->size = fad[0].pos; - if (!(sb->size == 0 && do_trim)) { - sb->thing_word = HEADER_SUB_BIN; - sb->offs = offset; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; - ret = CONS(hp, make_binary(sb), ret); - hp += 2; - } - HRelease(p, hendp, hp); - return ret; -} - -static int parse_split_opts_list(Eterm l, Eterm bin, Uint *posp, Uint *endp, Uint *optp) -{ - Eterm *tp; - Uint pos; - Sint len; - *optp = 0; - *posp = 0; - *endp = binary_size(bin); - if (l == THE_NON_VALUE || l == NIL) { - return 0; - } else if (is_list(l)) { - while(is_list(l)) { - Eterm t = CAR(list_val(l)); - Uint orig_size; - if (is_atom(t)) { - if (t == am_global) { - *optp |= BINARY_SPLIT_GLOBAL; - l = CDR(list_val(l)); - continue; - } - if (t == am_trim) { - *optp |= BINARY_SPLIT_TRIM; - l = CDR(list_val(l)); - continue; - } - if (t == am_trim_all) { - *optp |= BINARY_SPLIT_TRIM_ALL; - l = CDR(list_val(l)); - continue; - } - } - if (!is_tuple(t)) { - goto badarg; - } - tp = tuple_val(t); - if (arityval(*tp) != 2) { - goto badarg; - } - if (tp[1] != am_scope || is_not_tuple(tp[2])) { - goto badarg; - } - tp = tuple_val(tp[2]); - if (arityval(*tp) != 2) { - goto badarg; - } - if (!term_to_Uint(tp[1], &pos)) { - goto badarg; - } - if (!term_to_Sint(tp[2], &len)) { - goto badarg; - } - if (len < 0) { - Uint lentmp = -(Uint)len; - /* overflow */ - if ((Sint)lentmp < 0) { - goto badarg; - } - len = lentmp; - pos -= len; - } - /* overflow */ - if ((pos + len) < pos || (len > 0 && (pos + len) == pos)) { - goto badarg; - } - *endp = len + pos; - *posp = pos; - if ((orig_size = binary_size(bin)) < pos || - orig_size < (*endp)) { - goto badarg; - } - l = CDR(list_val(l)); - } - return 0; - } else { - badarg: - return 1; - } -} - -static BIF_RETTYPE binary_split_trap(BIF_ALIST_3) -{ - int runres; - Eterm result; - Binary *bin = ((ProcBin *) binary_val(BIF_ARG_3))->val; - runres = do_binary_split(BIF_P,BIF_ARG_1,0,0,0,NIL,bin,BIF_ARG_2,&result); - if (runres == DO_BIN_MATCH_OK) { - BIF_RET(result); - } else { - BUMP_ALL_REDS(BIF_P); - BIF_TRAP3(&binary_split_trap_export, BIF_P, BIF_ARG_1, result, - BIF_ARG_3); - } -} - -BIF_RETTYPE binary_split_3(BIF_ALIST_3) -{ - return binary_split(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); -} - -static BIF_RETTYPE -binary_split(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) -{ - Uint hsflags; - Uint hsstart; - Uint hsend; - Eterm *tp; - Eterm type; - Binary *bin; - Eterm bin_term = NIL; - int runres; - Eterm result; - - if (is_not_binary(arg1)) { - goto badarg; - } - if (parse_split_opts_list(arg3, arg1, &hsstart, &hsend, &hsflags)) { - goto badarg; - } - if (hsend == 0) { - tp = HAlloc(p, 2); - result = NIL; - result = CONS(tp, arg1, result); - BIF_RET(result); - } - if (is_tuple(arg2)) { - tp = tuple_val(arg2); - if (arityval(*tp) != 2 || is_not_atom(tp[1])) { - goto badarg; - } - if (((tp[1] != am_bm) && (tp[1] != am_ac)) || - !ERTS_TERM_IS_MAGIC_BINARY(tp[2])) { - goto badarg; - } - type = tp[1]; - bin = ((ProcBin *) binary_val(tp[2]))->val; - if (type == am_bm && - ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_bm) { - goto badarg; - } - if (type == am_ac && - ERTS_MAGIC_BIN_DESTRUCTOR(bin) != cleanup_my_data_ac) { - goto badarg; - } - bin_term = tp[2]; - } else if (do_binary_match_compile(arg2, &type, &bin)) { - goto badarg; - } - runres = do_binary_split(p, arg1, hsstart, hsend, hsflags, type, bin, NIL, &result); - if (runres == DO_BIN_MATCH_RESTART && bin_term == NIL) { - Eterm *hp = HAlloc(p, PROC_BIN_SIZE); - bin_term = erts_mk_magic_binary_term(&hp, &MSO(p), bin); - } else if (bin_term == NIL) { - erts_bin_free(bin); - } - switch(runres) { - case DO_BIN_MATCH_OK: - BIF_RET(result); - case DO_BIN_MATCH_RESTART: - BIF_TRAP3(&binary_split_trap_export, p, arg1, result, bin_term); - default: - goto badarg; - } - badarg: - BIF_ERROR(p,BADARG); -} - - -BIF_RETTYPE binary_split_2(BIF_ALIST_2) -{ - return binary_split(BIF_P, BIF_ARG_1, BIF_ARG_2, THE_NON_VALUE); -} - - BIF_RETTYPE binary_referenced_byte_size_1(BIF_ALIST_1) { ErlSubBin *sb; -- cgit v1.2.3 From 8067992954f9b882ffd7332ee669a254c000f1b2 Mon Sep 17 00:00:00 2001 From: Andrew Bennett Date: Fri, 18 Sep 2015 09:38:46 -0600 Subject: erts: Minor refactor for binary find BIF backend * Use NULL instead of THE_NON_VALUE for non-Eterm variable. * Add BinaryFindState_bignum struct to avoid unnecessary type casting. --- erts/emulator/beam/erl_bif_binary.c | 109 ++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 54 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 9f72b8c0ac..b1ebf0327e 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -531,21 +531,23 @@ static void ac_init_find_all(ACFindAllState *state, ACTrie *act, Sint startpos, state->out = NULL; } -static void ac_restore_find_all(ACFindAllState *state, char *buff) +static void ac_restore_find_all(ACFindAllState *state, + const ACFindAllState *src) { - memcpy(state,buff,sizeof(ACFindAllState)); + memcpy(state, src, sizeof(ACFindAllState)); if (state->allocated > 0) { state->out = erts_alloc(ERTS_ALC_T_TMP, sizeof(FindallData) * (state->allocated)); - memcpy(state->out,buff+sizeof(ACFindAllState),sizeof(FindallData)*state->m); + memcpy(state->out, src+1, sizeof(FindallData)*state->m); } else { state->out = NULL; } } -static void ac_serialize_find_all(ACFindAllState *state, char *buff) +static void ac_serialize_find_all(const ACFindAllState *state, + ACFindAllState *dst) { - memcpy(buff,state,sizeof(ACFindAllState)); - memcpy(buff+sizeof(ACFindAllState),state->out,sizeof(FindallData)*state->m); + memcpy(dst, state, sizeof(ACFindAllState)); + memcpy(dst+1, state->out, sizeof(FindallData)*state->m); } static void ac_clean_find_all(ACFindAllState *state) @@ -805,24 +807,24 @@ static void bm_init_find_all(BMFindAllState *state, Sint startpos, Uint len) state->out = NULL; } -static void bm_restore_find_all(BMFindAllState *state, char *buff) +static void bm_restore_find_all(BMFindAllState *state, + const BMFindAllState *src) { - memcpy(state,buff,sizeof(BMFindAllState)); + memcpy(state, src, sizeof(BMFindAllState)); if (state->allocated > 0) { state->out = erts_alloc(ERTS_ALC_T_TMP, sizeof(FindallData) * (state->allocated)); - memcpy(state->out,buff+sizeof(BMFindAllState), - sizeof(FindallData)*state->m); + memcpy(state->out, src+1, sizeof(FindallData)*state->m); } else { state->out = NULL; } } -static void bm_serialize_find_all(BMFindAllState *state, char *buff) +static void bm_serialize_find_all(const BMFindAllState *state, + BMFindAllState *dst) { - memcpy(buff,state,sizeof(BMFindAllState)); - memcpy(buff+sizeof(BMFindAllState),state->out, - sizeof(FindallData)*state->m); + memcpy(dst, state, sizeof(BMFindAllState)); + memcpy(dst+1, state->out, sizeof(FindallData)*state->m); } static void bm_clean_find_all(BMFindAllState *state) @@ -1032,6 +1034,17 @@ typedef struct BinaryFindState { Eterm (*global_result) (Process *, Eterm, struct BinaryFindState *, FindallData *, Uint); } BinaryFindState; +typedef struct BinaryFindState_bignum { + Eterm bignum_hdr; + BinaryFindState bfs; + union { + BMFindFirstState bmffs; + BMFindAllState bmfas; + ACFindFirstState acffs; + ACFindAllState acfas; + } data; +} BinaryFindState_bignum; + #define SIZEOF_BINARY_FIND_STATE(S) \ (sizeof(BinaryFindState)+sizeof(S)) @@ -1197,7 +1210,7 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar byte *bytes; Uint bitoffs, bitsize; byte *temp_alloc = NULL; - char *state_ptr = NULL; + BinaryFindState_bignum *state_ptr = NULL; ERTS_GET_BINARY_BYTES(subject, bytes, bitoffs, bitsize); if (bitsize != 0) { @@ -1207,17 +1220,14 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar bytes = erts_get_aligned_binary_bytes(subject, &temp_alloc); } if (state_term != NIL) { - state_ptr = (char *)(big_val(state_term)); - state_ptr += sizeof(Eterm); - bfs = (BinaryFindState *)(state_ptr); - state_ptr += sizeof(BinaryFindState); + state_ptr = (BinaryFindState_bignum *)(big_val(state_term)); + bfs = &(state_ptr->bfs); } if (bfs->flags & BINARY_FIND_ALL) { if (bfs->type == am_bm) { BMData *bm; Sint pos; - Eterm *hp; BMFindAllState state; Uint reds = get_reds(p, BM_LOOP_FACTOR); Uint save_reds = reds; @@ -1229,7 +1239,7 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar if (state_term == NIL) { bm_init_find_all(&state, bfs->hsstart, bfs->hsend); } else { - bm_restore_find_all(&state, state_ptr); + bm_restore_find_all(&state, &(state_ptr->data.bmfas)); } pos = bm_find_all_non_overlapping(&state, bm, bytes, &reds); @@ -1242,12 +1252,11 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar #ifdef HARDDEBUG erts_printf("Trap bm!\n"); #endif - hp = HAlloc(p, x+1); - hp[0] = make_pos_bignum_header(x); - state_ptr = (char *)(hp); - memcpy((void *)(state_ptr+sizeof(Eterm)), bfs, sizeof(BinaryFindState)); - bm_serialize_find_all(&state, state_ptr+sizeof(Eterm)+sizeof(BinaryFindState)); - *res_term = make_big(hp); + state_ptr = (BinaryFindState_bignum*) HAlloc(p, x+1); + state_ptr->bignum_hdr = make_pos_bignum_header(x); + memcpy(&state_ptr->bfs, bfs, sizeof(BinaryFindState)); + bm_serialize_find_all(&state, &state_ptr->data.bmfas); + *res_term = make_big(&state_ptr->bignum_hdr); erts_free_aligned_binary_bytes(temp_alloc); bm_clean_find_all(&state); return DO_BIN_MATCH_RESTART; @@ -1262,7 +1271,6 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar ACTrie *act; int acr; ACFindAllState state; - Eterm *hp; Uint reds = get_reds(p, AC_LOOP_FACTOR); Uint save_reds = reds; @@ -1273,7 +1281,7 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar if (state_term == NIL) { ac_init_find_all(&state, act, bfs->hsstart, bfs->hsend); } else { - ac_restore_find_all(&state, state_ptr); + ac_restore_find_all(&state, &(state_ptr->data.acfas)); } acr = ac_find_all_non_overlapping(&state, bytes, &reds); if (acr == AC_NOT_FOUND) { @@ -1285,12 +1293,11 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar #ifdef HARDDEBUG erts_printf("Trap ac!\n"); #endif - hp = HAlloc(p, x+1); - hp[0] = make_pos_bignum_header(x); - state_ptr = (char *)(hp); - memcpy((void *)(state_ptr+sizeof(Eterm)), bfs, sizeof(BinaryFindState)); - ac_serialize_find_all(&state, state_ptr+sizeof(Eterm)+sizeof(BinaryFindState)); - *res_term = make_big(hp); + state_ptr = (BinaryFindState_bignum*) HAlloc(p, x+1); + state_ptr->bignum_hdr = make_pos_bignum_header(x); + memcpy(&state_ptr->bfs, bfs, sizeof(BinaryFindState)); + ac_serialize_find_all(&state, &state_ptr->data.acfas); + *res_term = make_big(&state_ptr->bignum_hdr); erts_free_aligned_binary_bytes(temp_alloc); ac_clean_find_all(&state); return DO_BIN_MATCH_RESTART; @@ -1306,7 +1313,6 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar if (bfs->type == am_bm) { BMData *bm; Sint pos; - Eterm *hp; BMFindFirstState state; Uint reds = get_reds(p, BM_LOOP_FACTOR); Uint save_reds = reds; @@ -1318,7 +1324,7 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar if (state_term == NIL) { bm_init_find_first_match(&state, bfs->hsstart, bfs->hsend); } else { - memcpy((void *)(&state), (const void *)(state_ptr), sizeof(BMFindFirstState)); + memcpy(&state, &state_ptr->data.bmffs, sizeof(BMFindFirstState)); } #ifdef HARDDEBUG @@ -1335,13 +1341,11 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar #ifdef HARDDEBUG erts_printf("Trap bm!\n"); #endif - hp = HAlloc(p, x+1); - hp[0] = make_pos_bignum_header(x); - state_ptr = (char *)(hp); - memcpy((void *)(state_ptr+sizeof(Eterm)), bfs, sizeof(BinaryFindState)); - memcpy((void *)(state_ptr+sizeof(Eterm)+sizeof(BinaryFindState)), - (const void *)(&state), sizeof(BMFindFirstState)); - *res_term = make_big(hp); + state_ptr = (BinaryFindState_bignum*) HAlloc(p, x+1); + state_ptr->bignum_hdr = make_pos_bignum_header(x); + memcpy(&state_ptr->bfs, bfs, sizeof(BinaryFindState)); + memcpy(&state_ptr->data.acffs, &state, sizeof(BMFindFirstState)); + *res_term = make_big(&state_ptr->bignum_hdr); erts_free_aligned_binary_bytes(temp_alloc); return DO_BIN_MATCH_RESTART; } else { @@ -1355,7 +1359,6 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar Uint pos, rlen; int acr; ACFindFirstState state; - Eterm *hp; Uint reds = get_reds(p, AC_LOOP_FACTOR); Uint save_reds = reds; @@ -1366,7 +1369,7 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar if (state_term == NIL) { ac_init_find_first_match(&state, act, bfs->hsstart, bfs->hsend); } else { - memcpy((void *)(&state), (const void *)(state_ptr), sizeof(ACFindFirstState)); + memcpy(&state, &state_ptr->data.acffs, sizeof(ACFindFirstState)); } acr = ac_find_first_match(&state, bytes, &pos, &rlen, &reds); if (acr == AC_NOT_FOUND) { @@ -1378,13 +1381,11 @@ static int do_binary_find(Process *p, Eterm subject, BinaryFindState *bfs, Binar #ifdef HARDDEBUG erts_printf("Trap ac!\n"); #endif - hp = HAlloc(p, x+1); - hp[0] = make_pos_bignum_header(x); - state_ptr = (char *)(hp); - memcpy((void *)(state_ptr+sizeof(Eterm)), bfs, sizeof(BinaryFindState)); - memcpy((void *)(state_ptr+sizeof(Eterm)+sizeof(BinaryFindState)), - (const void *)(&state), sizeof(ACFindFirstState)); - *res_term = make_big(hp); + state_ptr = (BinaryFindState_bignum*) HAlloc(p, x+1); + state_ptr->bignum_hdr = make_pos_bignum_header(x); + memcpy(&state_ptr->bfs, bfs, sizeof(BinaryFindState)); + memcpy(&state_ptr->data.acffs, &state, sizeof(ACFindFirstState)); + *res_term = make_big(&state_ptr->bignum_hdr); erts_free_aligned_binary_bytes(temp_alloc); return DO_BIN_MATCH_RESTART; } else { @@ -1763,7 +1764,7 @@ static BIF_RETTYPE binary_find_trap(BIF_ALIST_3) int runres; Eterm result; Binary *bin = ((ProcBin *) binary_val(BIF_ARG_3))->val; - runres = do_binary_find(BIF_P, BIF_ARG_1, THE_NON_VALUE, bin, BIF_ARG_2, &result); + runres = do_binary_find(BIF_P, BIF_ARG_1, NULL, bin, BIF_ARG_2, &result); if (runres == DO_BIN_MATCH_OK) { BIF_RET(result); } else { -- cgit v1.2.3 From cb62ace6eac881e2b42880c6f645afbc56295ee0 Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Wed, 4 Nov 2015 16:04:54 +0100 Subject: add missing time.h --- erts/epmd/src/epmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 132bda725c..63ec18d939 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -29,7 +29,7 @@ #ifdef HAVE_STDLIB_H # include #endif - +#include /* forward declarations */ static void usage(EpmdVars *); -- cgit v1.2.3 From dc6d89bc1c08e15f8d4cd32f2e0886713cdc2dc2 Mon Sep 17 00:00:00 2001 From: Derek Brown Date: Wed, 4 Nov 2015 16:05:33 +0100 Subject: Fix typos and grammar --- erts/doc/src/erl.xml | 6 +++--- erts/doc/src/erlang.xml | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 63ab2532ab..ec4a0dee05 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -371,7 +371,7 @@ path, similar to . See code(3). As an alternative to -pa, if several directories are - to be prepended to the code and the directories have a + to be prepended to the code path and the directories have a common parent directory, that parent directory could be specified in the ERL_LIBS environment variable. See code(3).

@@ -1184,7 +1184,7 @@ disables this feature, which also is the default.

This feature has been introduced as a temporary workaround - for lengthy executing native code, and native code that do not + for long-executing native code, and native code that does not bump reductions properly in OTP. When these bugs have be fixed the +sfwi flag will be removed.

@@ -1210,7 +1210,7 @@ balance scheduler utilization between schedulers. That is, strive for equal scheduler utilization on all schedulers.
   +sub true is only supported on - systems where the runtime system detects and use a monotonically + systems where the runtime system detects and uses a monotonically increasing high resolution clock. On other systems, the runtime system will fail to start.
   +sub true implies diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index df7af3ce6b..94f9cd3148 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1001,15 +1001,15 @@ can be used instead of demonitor(MonitorRef) if this cleanup is wanted.

-

Before OTP R11B (ERTS 5.5), demonitor/1 - behaved asynchronous, that is, the monitor was active - until the "demonitor signal" reached the monitored entity. - This had an undesirable effect, as you could never know when - you were guaranteed not to receive a DOWN - message because of the monitor.

-

The current behavior can be viewed as two combined operations: - asynchronously send a "demonitor signal" to the monitored - entity and ignore any future results of the monitor.

+

Prior to OTP release R11B (ERTS version 5.5) demonitor/1 + behaved completely asynchronously, i.e., the monitor was active + until the "demonitor signal" reached the monitored entity. This + had one undesirable effect. You could never know when + you were guaranteed not to receive a DOWN message + due to the monitor.

+

Current behavior can be viewed as two combined operations: + asynchronously send a "demonitor signal" to the monitored entity + and ignore any future results of the monitor.

Failure: It is an error if MonitorRef refers to a monitoring started by another process. Not all such cases are @@ -7877,9 +7877,9 @@ timestamp() -> Secs = ErlangSystemTime div 1000000 - MegaSecs*1000000, MicroSecs = ErlangSystemTime rem 1000000, {MegaSecs, Secs, MicroSecs}. -

The BIF uses a native implementation which does - not build garbage on the heap and with slightly better - performance.

+

It, however, uses a native implementation which does + not build garbage on the heap and with slightly better + performance.

This time is not a monotonically increasing time in the general case. For more information, see the documentation of @@ -8812,8 +8812,8 @@ timestamp() -> true end -

Before OTP R11B (ERTS 5.5) unlink/1 - behaved asynchronous, that is, the link was active +

Prior to OTP release R11B (ERTS version 5.5) unlink/1 + behaved completely asynchronously, i.e., the link was active until the "unlink signal" reached the linked entity. This had an undesirable effect, as you could never know when you were guaranteed not to be effected by the link.

-- cgit v1.2.3 From f1f6ba4da602eb96727b6e9b5ac3dfdac17a1bd8 Mon Sep 17 00:00:00 2001 From: Luis Rascao Date: Sat, 12 Sep 2015 15:48:30 +0100 Subject: Fix crash on init restart On load handler process not being launched on a restart, NIF's such as asn1rt_nif require it to be present for correct loading. --- erts/preloaded/ebin/init.beam | Bin 48768 -> 48812 bytes erts/preloaded/src/init.erl | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 851513b2e9..73dfb3d351 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index c4e37b76f1..0ad5824ad1 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -167,7 +167,6 @@ stop(Status) -> init ! {stop,{stop,Status}}, ok. boot(BootArgs) -> register(init, self()), process_flag(trap_exit, true), - start_on_load_handler_process(), {Start0,Flags,Args} = parse_boot_args(BootArgs), Start = map(fun prepare_run_args/1, Start0), Flags0 = flags_to_atoms_again(Flags), @@ -225,6 +224,7 @@ code_path_choice() -> end. boot(Start,Flags,Args) -> + start_on_load_handler_process(), BootPid = do_boot(Flags,Start), State = #state{flags = Flags, args = Args, -- cgit v1.2.3 From 949de78331b9c4ecb9c628bb651cecb113790e2e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 10 Nov 2015 15:43:15 +0100 Subject: erts: Fix bug in setnode/2 that could lead strange things to happen when renaming a node with a name that already exist in node and dist tables. Problem: erts_set_this_node() is a bit brutal and does not handle the case when an old remote node name is reused as the new local node name. Solution: Treat erts_this_node and erts_this_dist_entry as all the others with correct reference counting. --- erts/emulator/beam/bif.c | 1 + erts/emulator/beam/dist.c | 14 +- erts/emulator/beam/erl_node_tables.c | 144 +++++++-------------- erts/emulator/test/distribution_SUITE_data/run.erl | 47 +++++-- 4 files changed, 95 insertions(+), 111 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 4e3a1cef69..6c61b0de6b 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4231,6 +4231,7 @@ BIF_RETTYPE list_to_pid_1(BIF_ALIST_1) goto bad; enp = erts_find_or_insert_node(dep->sysname, dep->creation); + ASSERT(enp != erts_this_node); etp = (ExternalThing *) HAlloc(BIF_P, EXTERNAL_THING_HEAD_SIZE + 1); etp->header = make_external_pid_header(1); diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 5a0f8388f8..56a8633f85 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -2572,7 +2572,9 @@ int distribution_info(int to, void *arg) /* Called by break handler */ } for (dep = erts_not_connected_dist_entries; dep; dep = dep->next) { - info_dist_entry(to, arg, dep, 0, 0); + if (dep != erts_this_dist_entry) { + info_dist_entry(to, arg, dep, 0, 0); + } } return(0); @@ -3012,11 +3014,11 @@ BIF_RETTYPE nodes_1(BIF_ALIST_1) erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx); - ASSERT(erts_no_of_not_connected_dist_entries >= 0); + ASSERT(erts_no_of_not_connected_dist_entries > 0); ASSERT(erts_no_of_hidden_dist_entries >= 0); ASSERT(erts_no_of_visible_dist_entries >= 0); if(not_connected) - length += erts_no_of_not_connected_dist_entries; + length += (erts_no_of_not_connected_dist_entries - 1); if(hidden) length += erts_no_of_hidden_dist_entries; if(visible) @@ -3038,8 +3040,10 @@ BIF_RETTYPE nodes_1(BIF_ALIST_1) #endif if(not_connected) for(dep = erts_not_connected_dist_entries; dep; dep = dep->next) { - result = CONS(hp, dep->sysname, result); - hp += 2; + if (dep != erts_this_dist_entry) { + result = CONS(hp, dep->sysname, result); + hp += 2; + } } if(hidden) for(dep = erts_hidden_dist_entries; dep; dep = dep->next) { diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 865de1726d..a7d0511bf9 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -37,18 +37,18 @@ erts_smp_rwmtx_t erts_node_table_rwmtx; DistEntry *erts_hidden_dist_entries; DistEntry *erts_visible_dist_entries; -DistEntry *erts_not_connected_dist_entries; +DistEntry *erts_not_connected_dist_entries; /* including erts_this_dist_entry */ Sint erts_no_of_hidden_dist_entries; Sint erts_no_of_visible_dist_entries; -Sint erts_no_of_not_connected_dist_entries; +Sint erts_no_of_not_connected_dist_entries; /* including erts_this_dist_entry */ DistEntry *erts_this_dist_entry; ErlNode *erts_this_node; char erts_this_node_sysname_BUFFER[256], *erts_this_node_sysname = "uninitialized yet"; -static Uint node_entries; -static Uint dist_entries; +static Uint node_entries = 0; +static Uint dist_entries = 0; static int references_atoms_need_init = 1; @@ -91,9 +91,6 @@ dist_table_alloc(void *dep_tmpl) erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ; - if(((DistEntry *) dep_tmpl) == erts_this_dist_entry) - return dep_tmpl; - sysname = ((DistEntry *) dep_tmpl)->sysname; chnl_nr = make_small((Uint) atom_val(sysname)); dep = (DistEntry *) erts_alloc(ERTS_ALC_T_DIST_ENTRY, sizeof(DistEntry)); @@ -132,7 +129,9 @@ dist_table_alloc(void *dep_tmpl) /* Link in */ - /* All new dist entries are "not connected" */ + /* All new dist entries are "not connected". + * erts_this_dist_entry is also always included among "not connected" + */ dep->next = erts_not_connected_dist_entries; if(erts_not_connected_dist_entries) { ASSERT(erts_not_connected_dist_entries->prev == NULL); @@ -149,9 +148,6 @@ dist_table_free(void *vdep) { DistEntry *dep = (DistEntry *) vdep; - if(dep == erts_this_dist_entry) - return; - ASSERT(is_nil(dep->cid)); ASSERT(dep->nlinks == NULL); ASSERT(dep->node_links == NULL); @@ -186,7 +182,7 @@ dist_table_free(void *vdep) #endif erts_free(ERTS_ALC_T_DIST_ENTRY, (void *) dep); - ASSERT(dist_entries > 1); + ASSERT(dist_entries > 0); dist_entries--; } @@ -306,7 +302,7 @@ static void try_delete_dist_entry(void *vdep) * thread incremented refc twice. Once for the new reference * and once for this thread. * - * If refc reach -1, noone has used the entry since we + * If refc reach -1, no one has used the entry since we * set up the timer. Delete the entry. * * If refc reach 0, the entry is currently not in use @@ -369,8 +365,7 @@ erts_dist_table_size(void) ASSERT(dist_entries == (erts_no_of_visible_dist_entries + erts_no_of_hidden_dist_entries - + erts_no_of_not_connected_dist_entries - + 1 /* erts_this_dist_entry */)); + + erts_no_of_not_connected_dist_entries)); #endif res = (hash_table_sz(&erts_dist_table) @@ -543,9 +538,6 @@ node_table_alloc(void *venp_tmpl) { ErlNode *enp; - if(((ErlNode *) venp_tmpl) == erts_this_node) - return venp_tmpl; - enp = (ErlNode *) erts_alloc(ERTS_ALC_T_NODE_ENTRY, sizeof(ErlNode)); node_entries++; @@ -563,8 +555,7 @@ node_table_free(void *venp) { ErlNode *enp = (ErlNode *) venp; - if(enp == erts_this_node) - return; + ERTS_SMP_LC_ASSERT(enp != erts_this_node || erts_thr_progress_is_blocking()); erts_deref_dist_entry(enp->dist_entry); #ifdef DEBUG @@ -572,7 +563,7 @@ node_table_free(void *venp) #endif erts_free(ERTS_ALC_T_NODE_ENTRY, venp); - ASSERT(node_entries > 1); + ASSERT(node_entries > 0); node_entries--; } @@ -650,7 +641,7 @@ static void try_delete_node(void *venp) * thread incremented refc twice. Once for the new reference * and once for this thread. * - * If refc reach -1, noone has used the entry since we + * If refc reach -1, no one has used the entry since we * set up the timer. Delete the entry. * * If refc reach 0, the entry is currently not in use @@ -747,25 +738,20 @@ void erts_print_node_info(int to, void erts_set_this_node(Eterm sysname, Uint creation) { - erts_smp_rwmtx_rwlock(&erts_node_table_rwmtx); - erts_smp_rwmtx_rwlock(&erts_dist_table_rwmtx); + ERTS_SMP_LC_ASSERT(erts_thr_progress_is_blocking()); + ASSERT(erts_refc_read(&erts_this_dist_entry->refc, 2)); - (void) hash_erase(&erts_dist_table, (void *) erts_this_dist_entry); - erts_this_dist_entry->sysname = sysname; - erts_this_dist_entry->creation = creation; - (void) hash_put(&erts_dist_table, (void *) erts_this_dist_entry); + if (erts_refc_dectest(&erts_this_node->refc, 0) == 0) + try_delete_node(erts_this_node); - (void) hash_erase(&erts_node_table, (void *) erts_this_node); - erts_this_node->sysname = sysname; - erts_this_node->creation = creation; - erts_this_node_sysname = erts_this_node_sysname_BUFFER; - erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname_BUFFER), - "%T", sysname); - (void) hash_put(&erts_node_table, (void *) erts_this_node); + if (erts_refc_dectest(&erts_this_dist_entry->refc, 0) == 0) + try_delete_dist_entry(erts_this_dist_entry); - erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx); - erts_smp_rwmtx_rwunlock(&erts_node_table_rwmtx); + erts_this_node = NULL; /* to make sure refc is bumped for this node */ + erts_this_node = erts_find_or_insert_node(sysname, creation); + erts_this_dist_entry = erts_this_node->dist_entry; + erts_refc_inc(&erts_this_dist_entry->refc, 2); } Uint @@ -782,6 +768,7 @@ void erts_init_node_tables(int dd_sec) { erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; HashFunctions f; + ErlNode node_tmpl; if (dd_sec == ERTS_NODE_TAB_DELAY_GC_INFINITY) node_tab_delete_delay = (ErtsMonotonicTime) -1; @@ -793,16 +780,21 @@ void erts_init_node_tables(int dd_sec) rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ; rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED; + erts_smp_rwmtx_init_opt(&erts_node_table_rwmtx, &rwmtx_opt, "node_table"); + erts_smp_rwmtx_init_opt(&erts_dist_table_rwmtx, &rwmtx_opt, "dist_table"); + f.hash = (H_FUN) dist_table_hash; f.cmp = (HCMP_FUN) dist_table_cmp; f.alloc = (HALLOC_FUN) dist_table_alloc; f.free = (HFREE_FUN) dist_table_free; - - erts_this_dist_entry = erts_alloc(ERTS_ALC_T_DIST_ENTRY, sizeof(DistEntry)); - dist_entries = 1; - hash_init(ERTS_ALC_T_DIST_TABLE, &erts_dist_table, "dist_table", 11, f); + f.hash = (H_FUN) node_table_hash; + f.cmp = (HCMP_FUN) node_table_cmp; + f.alloc = (HALLOC_FUN) node_table_alloc; + f.free = (HFREE_FUN) node_table_free; + hash_init(ERTS_ALC_T_NODE_TABLE, &erts_node_table, "node_table", 11, f); + erts_hidden_dist_entries = NULL; erts_visible_dist_entries = NULL; erts_not_connected_dist_entries = NULL; @@ -810,69 +802,23 @@ void erts_init_node_tables(int dd_sec) erts_no_of_visible_dist_entries = 0; erts_no_of_not_connected_dist_entries = 0; - erts_this_dist_entry->next = NULL; - erts_this_dist_entry->prev = NULL; - erts_refc_init(&erts_this_dist_entry->refc, 1); /* erts_this_node */ - - erts_smp_rwmtx_init_opt_x(&erts_this_dist_entry->rwmtx, - &rwmtx_opt, - "dist_entry", - make_small(ERST_INTERNAL_CHANNEL_NO)); - erts_this_dist_entry->sysname = am_Noname; - erts_this_dist_entry->cid = NIL; - erts_this_dist_entry->connection_id = 0; - erts_this_dist_entry->status = 0; - erts_this_dist_entry->flags = 0; - erts_this_dist_entry->version = 0; - - erts_smp_mtx_init_x(&erts_this_dist_entry->lnk_mtx, - "dist_entry_links", - make_small(ERST_INTERNAL_CHANNEL_NO)); - erts_this_dist_entry->node_links = NULL; - erts_this_dist_entry->nlinks = NULL; - erts_this_dist_entry->monitors = NULL; - - erts_smp_mtx_init_x(&erts_this_dist_entry->qlock, - "dist_entry_out_queue", - make_small(ERST_INTERNAL_CHANNEL_NO)); - erts_this_dist_entry->qflgs = 0; - erts_this_dist_entry->qsize = 0; - erts_this_dist_entry->out_queue.first = NULL; - erts_this_dist_entry->out_queue.last = NULL; - erts_this_dist_entry->suspended = NULL; - - erts_this_dist_entry->finalized_out_queue.first = NULL; - erts_this_dist_entry->finalized_out_queue.last = NULL; - erts_smp_atomic_init_nob(&erts_this_dist_entry->dist_cmd_scheduled, 0); - erts_port_task_handle_init(&erts_this_dist_entry->dist_cmd); - erts_this_dist_entry->send = NULL; - erts_this_dist_entry->cache = NULL; - - (void) hash_put(&erts_dist_table, (void *) erts_this_dist_entry); + node_tmpl.sysname = am_Noname; + node_tmpl.creation = 0; + erts_this_node = hash_put(&erts_node_table, &node_tmpl); + /* +1 for erts_this_node */ + erts_refc_init(&erts_this_node->refc, 1); - f.hash = (H_FUN) node_table_hash; - f.cmp = (HCMP_FUN) node_table_cmp; - f.alloc = (HALLOC_FUN) node_table_alloc; - f.free = (HFREE_FUN) node_table_free; - - hash_init(ERTS_ALC_T_NODE_TABLE, &erts_node_table, "node_table", 11, f); + ASSERT(erts_this_node->dist_entry != NULL); + erts_this_dist_entry = erts_this_node->dist_entry; + /* +1 for erts_this_dist_entry */ + /* +1 for erts_this_node->dist_entry */ + erts_refc_init(&erts_this_dist_entry->refc, 2); - erts_this_node = erts_alloc(ERTS_ALC_T_NODE_ENTRY, sizeof(ErlNode)); - node_entries = 1; - erts_refc_init(&erts_this_node->refc, 1); /* The system itself */ - erts_this_node->sysname = am_Noname; - erts_this_node->creation = 0; - erts_this_node->dist_entry = erts_this_dist_entry; erts_this_node_sysname = erts_this_node_sysname_BUFFER; erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname_BUFFER), "%T", erts_this_node->sysname); - (void) hash_put(&erts_node_table, (void *) erts_this_node); - - erts_smp_rwmtx_init_opt(&erts_node_table_rwmtx, &rwmtx_opt, "node_table"); - erts_smp_rwmtx_init_opt(&erts_dist_table_rwmtx, &rwmtx_opt, "dist_table"); - references_atoms_need_init = 1; } @@ -1410,6 +1356,10 @@ setup_reference_table(void) SYSTEM_REF, TUPLE2(&heap[0], AM_system, am_undefined)); + insert_dist_entry(erts_this_dist_entry, + SYSTEM_REF, + TUPLE2(&heap[0], AM_system, am_undefined), + erts_this_node->creation); UnUseTmpHeapNoproc(3); max = erts_ptab_max(&erts_proc); diff --git a/erts/emulator/test/distribution_SUITE_data/run.erl b/erts/emulator/test/distribution_SUITE_data/run.erl index f5169e160c..d5ed139369 100644 --- a/erts/emulator/test/distribution_SUITE_data/run.erl +++ b/erts/emulator/test/distribution_SUITE_data/run.erl @@ -30,16 +30,19 @@ from(H, [_ | T]) -> from(H, T); from(H, []) -> []. start() -> - net_kernel:start([fideridum,shortnames]), - {ok, Node} = slave:start(host(), heppel), - P = spawn(Node, a, b, []), - B1 = term_to_binary(P), - N1 = node(P), - ok = net_kernel:stop(), - N2 = node(P), - io:format("~w~n", [N1 == N2]), + Result = do_it(), + + %% Do GCs and node_and_dist_references + %% in an attempt to crash the VM (without OTP-13076 fix) + lists:foreach(fun(P) -> erlang:garbage_collect(P) end, + processes()), + erts_debug:set_internal_state(available_internal_state, true), + erts_debug:get_internal_state(node_and_dist_references), + + io:format("~w~n", [Result]), + if - N1 == N2 -> + Result -> init:stop(); true -> %% Make sure that the io:format/2 output is really written @@ -47,3 +50,29 @@ start() -> erlang:yield(), init:stop() end. + + +do_it() -> + {ok, _} = net_kernel:start([fideridum,shortnames]), + {ok, Node} = slave:start(host(), heppel), + P = spawn(Node, net_kernel, stop, []), + B1 = term_to_binary(P), + N1 = node(P), + ok = net_kernel:stop(), + N2 = node(P), + + %% OTP-13076 + %% Restart distribution with same node name as previous remote node + %% Repeat to wrap around creation + Result = lists:foldl(fun(_, Acc) -> + timer:sleep(2), % give net_kernel:stop() time to take effect :-( + {ok, _} = net_kernel:start([heppel,shortnames]), + N3 = node(P), + ok = net_kernel:stop(), + N4 = node(P), + Acc and (N3 =:= N1) and (N4 =:= N1) + end, + (N2 =:= N1), + lists:seq(1,3)), + + Result. -- cgit v1.2.3 From b72ad981e96fcc14c245ef2bd10c5c98c6a239f6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 5 Dec 2014 15:13:07 +0100 Subject: erts: Add TEST allocator --- erts/emulator/beam/erl_alloc.c | 53 +++++++++++++++++++++- erts/emulator/beam/erl_alloc.types | 5 +- .../test/alloc_SUITE_data/allocator_test.h | 2 + erts/etc/common/erlexec.c | 1 + 4 files changed, 58 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 55c164bf11..4223b357f1 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -134,6 +134,7 @@ static ErtsAllocatorState_t binary_alloc_state; static ErtsAllocatorState_t ets_alloc_state; static ErtsAllocatorState_t driver_alloc_state; static ErtsAllocatorState_t fix_alloc_state; +static ErtsAllocatorState_t test_alloc_state; typedef struct { erts_smp_atomic32_t refc; @@ -233,6 +234,7 @@ typedef struct { struct au_init std_low_alloc; struct au_init ll_low_alloc; #endif + struct au_init test_alloc; } erts_alc_hndl_args_init_t; #define ERTS_AU_INIT__ {0, 0, 1, GOODFIT, DEFAULT_ALLCTR_INIT, {1,1,1,1}} @@ -432,6 +434,33 @@ set_default_fix_alloc_opts(struct au_init *ip, ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL; } +static void +set_default_test_alloc_opts(struct au_init *ip) +{ + SET_DEFAULT_ALLOC_OPTS(ip); + ip->enable = 0; /* Disabled by default */ + ip->thr_spec = -1 * erts_no_schedulers; + ip->atype = AOFIRSTFIT; + ip->init.aoff.flavor = AOFF_BF; + ip->init.util.name_prefix = "test_"; + ip->init.util.alloc_no = ERTS_ALC_A_TEST; + ip->init.util.mmbcs = 0; /* Main carrier size */ + ip->init.util.ts = ERTS_ALC_MTA_TEST; + ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL; + + /* Use a constant minimal MBC size */ +#if ERTS_SA_MB_CARRIERS + ip->init.util.smbcs = ERTS_SACRR_UNIT_SZ; + ip->init.util.lmbcs = ERTS_SACRR_UNIT_SZ; + ip->init.util.sbct = ERTS_SACRR_UNIT_SZ; +#else + ip->init.util.smbcs = 1 << 12; + ip->init.util.lmbcs = 1 << 12; + ip->init.util.sbct = 1 << 12; +#endif +} + + #ifdef ERTS_SMP static void @@ -613,6 +642,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) set_default_driver_alloc_opts(&init.driver_alloc); set_default_fix_alloc_opts(&init.fix_alloc, fix_type_sizes); + set_default_test_alloc_opts(&init.test_alloc); if (argc && argv) handle_args(argc, argv, &init); @@ -772,6 +802,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) set_au_allocator(ERTS_ALC_A_ETS, &init.ets_alloc, ncpu); set_au_allocator(ERTS_ALC_A_DRIVER, &init.driver_alloc, ncpu); set_au_allocator(ERTS_ALC_A_FIXED_SIZE, &init.fix_alloc, ncpu); + set_au_allocator(ERTS_ALC_A_TEST, &init.test_alloc, ncpu); for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) { if (!erts_allctrs[i].alloc) @@ -833,6 +864,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) &init.fix_alloc, &fix_alloc_state); + start_au_allocator(ERTS_ALC_A_TEST, + &init.test_alloc, + &test_alloc_state); + erts_mtrace_install_wrapper_functions(); extra_block_size += erts_instr_init(init.instr.stat, init.instr.map); @@ -1418,6 +1453,7 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) &init->fix_alloc, &init->sl_alloc, &init->temp_alloc + /* test_alloc not affected by +Mea??? or +Mu??? */ }; int aui_sz = (int) sizeof(aui)/sizeof(aui[0]); char *arg; @@ -1508,6 +1544,9 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) case 'T': handle_au_arg(&init->temp_alloc, &argv[i][3], argv, &i, 0); break; + case 'Z': + handle_au_arg(&init->test_alloc, &argv[i][3], argv, &i, 0); + break; case 'Y': { /* sys_alloc */ if (has_prefix("tt", param+2)) { /* set trim threshold */ @@ -2222,11 +2261,12 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) return am_badarg; } - /* All alloc_util allocators *have* to be enabled */ + /* All alloc_util allocators *have* to be enabled, except test_alloc */ for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++) { switch (ai) { case ERTS_ALC_A_SYSTEM: + case ERTS_ALC_A_TEST: break; default: if (!erts_allctrs_info[ai].enabled @@ -2267,6 +2307,8 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) * contain any allocated memory. */ continue; + case ERTS_ALC_A_TEST: + continue; case ERTS_ALC_A_EHEAP: save = &size.processes; break; @@ -2675,14 +2717,17 @@ erts_alloc_util_allocators(void *proc) /* * Currently all allocators except sys_alloc are * alloc_util allocators. + * Also hide test_alloc which is disabled by default + * and only intended for our own testing. */ - sz = ((ERTS_ALC_A_MAX + 1 - ERTS_ALC_A_MIN) - 1)*2; + sz = ((ERTS_ALC_A_MAX + 1 - ERTS_ALC_A_MIN) - 2)*2; ASSERT(sz > 0); hp = HAlloc((Process *) proc, sz); res = NIL; for (i = ERTS_ALC_A_MAX; i >= ERTS_ALC_A_MIN; i--) { switch (i) { case ERTS_ALC_A_SYSTEM: + case ERTS_ALC_A_TEST: break; default: { char *alc_str = (char *) ERTS_ALC_A2AD(i); @@ -3566,6 +3611,10 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3) #else case 0xf13: return (UWord) 0; #endif + case 0xf14: return (UWord) erts_alloc(ERTS_ALC_T_TEST, (Uint)a1); + + case 0xf15: erts_free(ERTS_ALC_T_TEST, (void*)a1); return 0; + default: break; } diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index b1d511ab78..1ecebdeb07 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -112,7 +112,7 @@ allocator STANDARD_LOW false std_low_alloc allocator BINARY true binary_alloc allocator DRIVER true driver_alloc - +allocator TEST true test_alloc # --- Class declarations ----------------------------------------------------- # @@ -456,4 +456,7 @@ type CON_VPRINTF_BUF TEMPORARY SYSTEM con_vprintf_buf +endif +# This type should only be used for test +type TEST TEST SYSTEM testing + # ---------------------------------------------------------------------------- diff --git a/erts/emulator/test/alloc_SUITE_data/allocator_test.h b/erts/emulator/test/alloc_SUITE_data/allocator_test.h index 1d6b2f4907..a7f12c6ca8 100644 --- a/erts/emulator/test/alloc_SUITE_data/allocator_test.h +++ b/erts/emulator/test/alloc_SUITE_data/allocator_test.h @@ -142,5 +142,7 @@ typedef void* erts_cond; #define THR_JOIN(T) ((void) ALC_TEST1(0xf11, (T))) #define THR_EXIT(R) ((void) ALC_TEST1(0xf12, (R))) #define IS_SMP_ENABLED ((int) ALC_TEST0(0xf13)) +#define ALLOC_TEST(S) ((void*) ALC_TEST1(0xf14, (S))) +#define FREE_TEST(P) ((void) ALC_TEST1(0xf15, (P))) #endif diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index cde0b25a2a..df8ad6e098 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -73,6 +73,7 @@ static const char plusM_au_allocs[]= { 'R', /* driver_alloc */ 'S', /* sl_alloc */ 'T', /* temp_alloc */ + 'Z', /* test_alloc */ '\0' }; -- cgit v1.2.3 From 38ddf72b48377bd6b2fb8c4b6981360ae7d44d79 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 8 Dec 2014 15:38:04 +0100 Subject: erts: Add alloc_SUITE:migration --- erts/emulator/beam/erl_alloc.c | 27 +++ erts/emulator/beam/erl_alloc_util.c | 10 + erts/emulator/test/alloc_SUITE.erl | 78 +++++-- erts/emulator/test/alloc_SUITE_data/Makefile.src | 3 +- .../test/alloc_SUITE_data/allocator_test.h | 2 + erts/emulator/test/alloc_SUITE_data/migration.c | 229 +++++++++++++++++++++ .../test/alloc_SUITE_data/testcase_driver.c | 14 ++ .../test/alloc_SUITE_data/testcase_driver.h | 1 + 8 files changed, 350 insertions(+), 14 deletions(-) create mode 100644 erts/emulator/test/alloc_SUITE_data/migration.c (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 4223b357f1..0aa45acd82 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3615,6 +3615,33 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3) case 0xf15: erts_free(ERTS_ALC_T_TEST, (void*)a1); return 0; + case 0xf16: { + Uint extra_hdr_sz = UNIT_CEILING((Uint)a1); + ErtsAllocatorThrSpec_t* ts = &erts_allctr_thr_spec[ERTS_ALC_A_TEST]; + Uint offset = ts->allctr[0]->mbc_header_size; + void* orig_creating_mbc = ts->allctr[0]->creating_mbc; + void* orig_destroying_mbc = ts->allctr[0]->destroying_mbc; + void* new_creating_mbc = *(void**)a2; /* inout arg */ + void* new_destroying_mbc = *(void**)a3; /* inout arg */ + int i; + + for (i=0; i < ts->size; i++) { + Allctr_t* ap = ts->allctr[i]; + if (ap->mbc_header_size != offset + || ap->creating_mbc != orig_creating_mbc + || ap->destroying_mbc != orig_destroying_mbc + || ap->mbc_list.first != NULL) + return -1; + } + for (i=0; i < ts->size; i++) { + ts->allctr[i]->mbc_header_size += extra_hdr_sz; + ts->allctr[i]->creating_mbc = new_creating_mbc; + ts->allctr[i]->destroying_mbc = new_destroying_mbc; + } + *(void**)a2 = orig_creating_mbc; + *(void**)a3 = orig_destroying_mbc; + return offset; + } default: break; } diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 173cb091b6..8229a15824 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -6074,6 +6074,16 @@ erts_alcu_test(UWord op, UWord a1, UWord a2) case 0x023: return (UWord) 0; case 0x024: return (UWord) 0; #endif + case 0x025: /* UMEM2BLK_TEST*/ +#ifdef DEBUG +# ifdef HARD_DEBUG + return (UWord)UMEM2BLK(a1-3*sizeof(UWord)); +# else + return (UWord)UMEM2BLK(a1-2*sizeof(UWord)); +# endif +#else + return (UWord)UMEM2BLK(a1); +#endif default: ASSERT(0); return ~((UWord) 0); } diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 7c7ddde5d4..9b0d4737b1 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -31,7 +31,8 @@ rbtree/1, mseg_clear_cache/1, erts_mmap/1, - cpool/1]). + cpool/1, + migration/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -43,7 +44,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [basic, coalesce, threads, realloc_copy, bucket_index, - bucket_mask, rbtree, mseg_clear_cache, erts_mmap, cpool]. + bucket_mask, rbtree, mseg_clear_cache, erts_mmap, cpool, migration]. groups() -> []. @@ -112,6 +113,8 @@ cpool(suite) -> []; cpool(doc) -> []; cpool(Cfg) -> ?line drv_case(Cfg). +migration(Cfg) -> drv_case(Cfg, concurrent, "+MZe true"). + erts_mmap(Config) when is_list(Config) -> case {?t:os_type(), is_halfword_vm()} of {{unix, _}, false} -> @@ -176,18 +179,17 @@ erts_mmap_do(Config, SCO, SCRPM, SCRFSD) -> %% %% drv_case(Config) -> - drv_case(Config, ""). + drv_case(Config, one_shot, ""). -drv_case(Config, Command) when is_list(Config), - is_list(Command) -> +drv_case(Config, Mode, NodeOpts) when is_list(Config) -> case ?t:os_type() of {Family, _} when Family == unix; Family == win32 -> - ?line {ok, Node} = start_node(Config), + ?line {ok, Node} = start_node(Config, NodeOpts), ?line Self = self(), ?line Ref = make_ref(), ?line spawn_link(Node, fun () -> - Res = run_drv_case(Config, Command), + Res = run_drv_case(Config, Mode), Self ! {Ref, Res} end), ?line Result = receive {Ref, Rslt} -> Rslt end, @@ -199,7 +201,7 @@ drv_case(Config, Command) when is_list(Config), | io_lib:format("~p",[SkipOs])])} end. -run_drv_case(Config, Command) -> +run_drv_case(Config, Mode) -> ?line DataDir = ?config(data_dir,Config), ?line CaseName = ?config(testcase,Config), case erl_ddll:load_driver(DataDir, CaseName) of @@ -208,6 +210,19 @@ run_drv_case(Config, Command) -> io:format("~s\n", [erl_ddll:format_error(Error)]), ?line ?t:fail() end, + + case Mode of + one_shot -> + Result = one_shot(CaseName, ""); + + concurrent -> + Result = concurrent(CaseName) + end, + + ?line ok = erl_ddll:unload_driver(CaseName), + ?line Result. + +one_shot(CaseName, Command) -> ?line Port = open_port({spawn, atom_to_list(CaseName)}, []), ?line true = is_port(Port), ?line Port ! {self(), {command, Command}}, @@ -217,8 +232,45 @@ run_drv_case(Config, Command) -> {Port, closed} -> ok end, - ?line ok = erl_ddll:unload_driver(CaseName), - ?line Result. + Result. + + +many_shot(CaseName, Command) -> + ?line Port = open_port({spawn, atom_to_list(CaseName)}, []), + ?line true = is_port(Port), + Result = repeat_while(fun() -> + ?line Port ! {self(), {command, Command}}, + receive_drv_result(Port, CaseName) =:= continue + end), + ?line Port ! {self(), close}, + ?line receive + {Port, closed} -> + ok + end, + Result. + +concurrent(CaseName) -> + one_shot(CaseName, "init"), + PRs = lists:map(fun(I) -> spawn_opt(fun() -> + many_shot(CaseName, "") + end, + [monitor, {scheduler,I}]) + end, + lists:seq(1, erlang:system_info(schedulers))), + lists:foreach(fun({Pid,Ref}) -> + receive {'DOWN', Ref, process, Pid, Reason} -> + Reason + end + end, + PRs), + ok. + +repeat_while(Fun) -> + io:format("~p calls fun\n", [self()]), + case Fun() of + true -> repeat_while(Fun); + false -> ok + end. receive_drv_result(Port, CaseName) -> ?line receive @@ -236,11 +288,11 @@ receive_drv_result(Port, CaseName) -> {succeeded, Port, CaseName, ""} -> ?line succeeded; {succeeded, Port, CaseName, Comment} -> - ?line {comment, Comment} + ?line {comment, Comment}; + continue -> + continue end. -start_node(Config) -> - start_node(Config, []). start_node(Config, Opts) when is_list(Config), is_list(Opts) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) diff --git a/erts/emulator/test/alloc_SUITE_data/Makefile.src b/erts/emulator/test/alloc_SUITE_data/Makefile.src index a441fe946b..e31de54e1b 100644 --- a/erts/emulator/test/alloc_SUITE_data/Makefile.src +++ b/erts/emulator/test/alloc_SUITE_data/Makefile.src @@ -25,7 +25,8 @@ TEST_DRVS = basic@dll@ \ bucket_mask@dll@ \ rbtree@dll@ \ mseg_clear_cache@dll@ \ - cpool@dll@ + cpool@dll@ \ + migration@dll@ CC = @CC@ LD = @LD@ diff --git a/erts/emulator/test/alloc_SUITE_data/allocator_test.h b/erts/emulator/test/alloc_SUITE_data/allocator_test.h index a7f12c6ca8..bfd0bb3094 100644 --- a/erts/emulator/test/alloc_SUITE_data/allocator_test.h +++ b/erts/emulator/test/alloc_SUITE_data/allocator_test.h @@ -85,6 +85,7 @@ typedef void* erts_cond; #define CPOOL_DELETE(A,B) ((Carrier_t *) ALC_TEST2(0x022, (A), (B))) #define CPOOL_IS_EMPTY(A) ((int) ALC_TEST1(0x023, (A))) #define CPOOL_IS_IN_POOL(A,B) ((int) ALC_TEST2(0x024, (A), (B))) +#define UMEM2BLK_TEST(P) ((Block_t*) ALC_TEST1(0x025, (P))) /* From erl_goodfit_alloc.c */ #define BKT_IX(A, S) ((Ulong) ALC_TEST2(0x100, (A), (S))) @@ -144,5 +145,6 @@ typedef void* erts_cond; #define IS_SMP_ENABLED ((int) ALC_TEST0(0xf13)) #define ALLOC_TEST(S) ((void*) ALC_TEST1(0xf14, (S))) #define FREE_TEST(P) ((void) ALC_TEST1(0xf15, (P))) +#define SET_TEST_MBC_USER_HEADER(SZ,CMBC,DMBC) ((int)ALC_TEST3(0xf16, (SZ), (CMBC), (DMBC))) #endif diff --git a/erts/emulator/test/alloc_SUITE_data/migration.c b/erts/emulator/test/alloc_SUITE_data/migration.c new file mode 100644 index 0000000000..dd58a0d3dd --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/migration.c @@ -0,0 +1,229 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2014. All Rights Reserved. + * + * The 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% + */ + +/* + * Test the carrier migration logic + */ + +#ifndef __WIN32__ +#include +#include +#include +#endif +#include +#include +#include +#include "testcase_driver.h" +#include "allocator_test.h" + +#define FATAL_ASSERT(A) \ + ((void) ((A) \ + ? 1 \ + : (fatal_assert_failed(#A, \ + (char *) __FILE__, \ + __LINE__), \ + 0))) + +static void +fatal_assert_failed(char* expr, char* file, int line) +{ + fflush(stdout); + fprintf(stderr, "%s:%d: Assertion failed: %s\n", + file, line, expr); + fflush(stderr); + abort(); +} + + +char * +testcase_name(void) +{ + return "migration"; +} + +void +testcase_cleanup(TestCaseState_t *tcs) +{ +} + +#define MAX_BLOCK_PER_THR 100 +#define MAX_ROUNDS 10 + +typedef struct MyBlock_ { + struct MyBlock_* next; + struct MyBlock_** prevp; +} MyBlock; + +typedef struct { + MyBlock* blockv[MAX_BLOCK_PER_THR]; + enum { GROWING, SHRINKING, CLEANUP, DONE } phase; + int ix; + int round; +} MigrationState; + +typedef struct { + ErlDrvMutex* mtx; + int nblocks; + MyBlock* first; +} MyCrrInfo; + + +static int crr_info_offset = -1; +static void (*orig_create_mbc_fn)(Allctr_t *allctr, Carrier_t *carrier); +static void (*orig_destroying_mbc_fn)(Allctr_t *allctr, Carrier_t *carrier); + +static void my_creating_mbc(Allctr_t *allctr, Carrier_t *carrier) +{ + MyCrrInfo* mci = (MyCrrInfo*) ((char*)carrier + crr_info_offset); + if (orig_create_mbc_fn) + orig_create_mbc_fn(allctr, carrier); + + mci->mtx = erl_drv_mutex_create("alloc_SUITE.migration"); + mci->nblocks = 0; + mci->first = NULL; +} + +static void my_destroying_mbc(Allctr_t *allctr, Carrier_t *carrier) +{ + MyCrrInfo* mci = (MyCrrInfo*) ((char*)carrier + crr_info_offset); + + FATAL_ASSERT(mci->nblocks == 0); + FATAL_ASSERT(mci->first == NULL); + erl_drv_mutex_destroy(mci->mtx); + + if (orig_destroying_mbc_fn) + orig_destroying_mbc_fn(allctr, carrier); +} + + +static void setup(TestCaseState_t* tcs) +{ + void* creating_mbc_arg = (void*)my_creating_mbc; + void* destroying_mbc_arg = (void*)my_destroying_mbc; + crr_info_offset = SET_TEST_MBC_USER_HEADER(sizeof(MyCrrInfo), + &creating_mbc_arg, + &destroying_mbc_arg); + ASSERT(tcs, crr_info_offset >= 0); + orig_create_mbc_fn = creating_mbc_arg; + orig_destroying_mbc_fn = destroying_mbc_arg; +} + +static void add_block(MyBlock* p) +{ + MyCrrInfo* mci = (MyCrrInfo*)((char*)BLK_TO_MBC(UMEM2BLK_TEST(p)) + crr_info_offset); + + erl_drv_mutex_lock(mci->mtx); + mci->nblocks++; + p->next = mci->first; + p->prevp = &mci->first; + mci->first = p; + if (p->next) + p->next->prevp = &p->next; + erl_drv_mutex_unlock(mci->mtx); +} + +static void remove_block(MyBlock* p) +{ + MyCrrInfo* mci = (MyCrrInfo*)((char*)BLK_TO_MBC(UMEM2BLK_TEST(p)) + crr_info_offset); + + erl_drv_mutex_lock(mci->mtx); + mci->nblocks--; + if (p->next) + p->next->prevp = p->prevp; + *p->prevp = p->next; + erl_drv_mutex_unlock(mci->mtx); +} + +void +testcase_run(TestCaseState_t *tcs) +{ + MigrationState* state = (MigrationState*) tcs->extra; + + if (tcs->command_len == 4 + && memcmp(tcs->command, "init", tcs->command_len) == 0) { + setup(tcs); + return; + } + + if (!tcs->extra) { + if (!IS_SMP_ENABLED) + testcase_skipped(tcs, "No SMP support"); + + tcs->extra = driver_alloc(sizeof(MigrationState)); + state = (MigrationState*) tcs->extra; + memset(state->blockv, 0, sizeof(state->blockv)); + state->phase = GROWING; + state->ix = 0; + state->round = 0; + } + + switch (state->phase) { + case GROWING: { + MyBlock* p; + FATAL_ASSERT(!state->blockv[state->ix]); + p = ALLOC_TEST((1 << 18) / 5); + FATAL_ASSERT(p); + add_block(p); + state->blockv[state->ix] = p; + do { + if (++state->ix >= MAX_BLOCK_PER_THR) { + state->phase = SHRINKING; + state->ix = 0; + break; + } + } while (state->blockv[state->ix] != NULL); + break; + } + case SHRINKING: + FATAL_ASSERT(state->blockv[state->ix]); + remove_block(state->blockv[state->ix]); + FREE_TEST(state->blockv[state->ix]); + state->blockv[state->ix] = NULL; + + state->ix += 1 + ((state->ix % 3) == 0); + if (state->ix >= MAX_BLOCK_PER_THR) { + if (++state->round >= MAX_ROUNDS) { + state->phase = CLEANUP; + } else { + state->phase = GROWING; + } + state->ix = 0; + } + break; + + case CLEANUP: + if (state->blockv[state->ix]) { + remove_block(state->blockv[state->ix]); + FREE_TEST(state->blockv[state->ix]); + } + if (++state->ix >= MAX_BLOCK_PER_THR) + state->phase = DONE; + break; + + default: + FATAL_ASSERT(!"Invalid phase"); + } + + if (state->phase == DONE) { + driver_free(tcs->extra); + tcs->extra = NULL; + } + else + testcase_continue(tcs); +} diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c index bc674c56b7..d04eb1bcb0 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c @@ -39,6 +39,7 @@ #define TESTCASE_FAILED 0 #define TESTCASE_SKIPPED 1 #define TESTCASE_SUCCEEDED 2 +#define TESTCASE_CONTINUE 3 typedef struct { TestCaseState_t visible; @@ -129,6 +130,12 @@ testcase_drv_run(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) } switch (itcs->result) { + case TESTCASE_CONTINUE: + msg[0] = ERL_DRV_ATOM; + msg[1] = driver_mk_atom("continue"); + erl_drv_output_term(itcs->port_id, msg, 2); + return; + case TESTCASE_SUCCEEDED: result_atom = driver_mk_atom("succeeded"); break; @@ -239,6 +246,13 @@ void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...) longjmp(itcs->done_jmp_buf, 1); } +void testcase_continue(TestCaseState_t *tcs) +{ + InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs; + itcs->result = TESTCASE_CONTINUE; + longjmp(itcs->done_jmp_buf, 1); +} + void testcase_failed(TestCaseState_t *tcs, char *frmt, ...) { InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs; diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h index 5d17eaec64..5d439735b7 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h @@ -37,6 +37,7 @@ typedef struct { void testcase_printf(TestCaseState_t *tcs, char *frmt, ...); void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...); void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...); +void testcase_continue(TestCaseState_t *tcs); void testcase_failed(TestCaseState_t *tcs, char *frmt, ...); int testcase_assertion_failed(TestCaseState_t *tcs, char *file, int line, char *assertion); -- cgit v1.2.3 From 01cc99b35c00be86d832693776ee8ed880b59882 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 8 Dec 2014 20:36:28 +0100 Subject: erts: Make key argument constant for erl_drv_{get|put}env This should be a harmless and compatible API change. --- erts/doc/src/erl_driver.xml | 4 ++-- erts/emulator/beam/erl_driver.h | 4 ++-- erts/emulator/beam/io.c | 8 ++++---- erts/emulator/sys/win32/erl_win_dyn_driver.h | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 1f7fe0f961..d829aeda10 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -2811,7 +2811,7 @@ ERL_DRV_MAP int sz - interl_drv_putenv(char *key, char *value) + interl_drv_putenv(const char *key, char *value) Set the value of an environment variable @@ -2840,7 +2840,7 @@ ERL_DRV_MAP int sz - interl_drv_getenv(char *key, char *value, size_t *value_size) + interl_drv_getenv(const char *key, char *value, size_t *value_size) Get the value of an environment variable diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 6b406d069c..dbb4d719c1 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -696,8 +696,8 @@ EXTERN int driver_dl_close(void *); EXTERN char *driver_dl_error(void); /* environment */ -EXTERN int erl_drv_putenv(char *key, char *value); -EXTERN int erl_drv_getenv(char *key, char *value, size_t *value_size); +EXTERN int erl_drv_putenv(const char *key, char *value); +EXTERN int erl_drv_getenv(const char *key, char *value, size_t *value_size); #ifdef __OSE__ typedef ErlDrvUInt ErlDrvOseEventId; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 900616c981..c64c8802b9 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -7596,15 +7596,15 @@ int null_func(void) } int -erl_drv_putenv(char *key, char *value) +erl_drv_putenv(const char *key, char *value) { - return erts_sys_putenv_raw(key, value); + return erts_sys_putenv_raw((char*)key, value); } int -erl_drv_getenv(char *key, char *value, size_t *value_size) +erl_drv_getenv(const char *key, char *value, size_t *value_size) { - return erts_sys_getenv_raw(key, value, value_size); + return erts_sys_getenv_raw((char*)key, value, value_size); } /* get heart_port diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h index 5e62320be4..baac7c903e 100644 --- a/erts/emulator/sys/win32/erl_win_dyn_driver.h +++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h @@ -145,8 +145,8 @@ WDD_TYPEDEF(ErlDrvTid, erl_drv_thread_self, (void)); WDD_TYPEDEF(int, erl_drv_equal_tids, (ErlDrvTid tid1, ErlDrvTid tid2)); WDD_TYPEDEF(void, erl_drv_thread_exit, (void *resp)); WDD_TYPEDEF(int, erl_drv_thread_join, (ErlDrvTid, void **respp)); -WDD_TYPEDEF(int, erl_drv_putenv, (char *key, char *value)); -WDD_TYPEDEF(int, erl_drv_getenv, (char *key, char *value, size_t *value_size)); +WDD_TYPEDEF(int, erl_drv_putenv, (const char *key, char *value)); +WDD_TYPEDEF(int, erl_drv_getenv, (const char *key, char *value, size_t *value_size)); typedef struct { WDD_FTYPE(null_func) *null_func; -- cgit v1.2.3 From 4d41edb71e4e7dd07e906d916d11c8508b88f041 Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Wed, 11 Nov 2015 17:16:30 +0000 Subject: Remove obsolete comment in heart.c The "if" referred to in the comment was removed in commit 70c9312c4b. --- erts/etc/common/heart.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index 01ef840b5d..9571b83ffd 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -718,14 +718,12 @@ do_terminate(int erlin_fd, int reason) { print_error("Would reboot. Terminating."); else { kill_old_erlang(); - /* suppress gcc warning with 'if' */ ret = system(command); print_error("Executed \"%s\" -> %d. Terminating.",command, ret); } free_env_val(command); } else { kill_old_erlang(); - /* suppress gcc warning with 'if' */ ret = system((char*)&cmd[0]); print_error("Executed \"%s\" -> %d. Terminating.",cmd, ret); } -- cgit v1.2.3 From b179d2bfb775b12e51ceefca9a8bc63dc2260f54 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Thu, 12 Nov 2015 10:28:32 +0100 Subject: win32: Fix mixed cygwin and msys configure tests And test for mixed msys in emulator --- erts/aclocal.m4 | 14 +++++++------- erts/include/internal/ethread.h | 3 ++- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 390d6cfc4d..3d52538933 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -142,18 +142,18 @@ MIXED_MSYS=no AC_MSG_CHECKING(for mixed cygwin or msys and native VC++ environment) if test "X$host" = "Xwin32" -a "x$GCC" != "xyes"; then - if test -x /usr/bin/cygpath; then - CFLAGS="-O2" - MIXED_CYGWIN=yes - AC_MSG_RESULT([Cygwin and VC]) - MIXED_CYGWIN_VC=yes - CPPFLAGS="$CPPFLAGS -DERTS_MIXED_CYGWIN_VC" - elif test -x /usr/bin/msysinfo; then + if test -x /usr/bin/msys-?.0.dll; then CFLAGS="-O2" MIXED_MSYS=yes AC_MSG_RESULT([MSYS and VC]) MIXED_MSYS_VC=yes CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MSYS_VC" + elif test -x /usr/bin/cygpath; then + CFLAGS="-O2" + MIXED_CYGWIN=yes + AC_MSG_RESULT([Cygwin and VC]) + MIXED_CYGWIN_VC=yes + CPPFLAGS="$CPPFLAGS -DERTS_MIXED_CYGWIN_VC" else AC_MSG_RESULT([undeterminable]) AC_MSG_ERROR(Seems to be mixed windows but not with cygwin, cannot handle this!) diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index f9c203e97c..8964f95652 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -54,7 +54,8 @@ #endif #if defined(ETHR_DEBUG) || !defined(ETHR_INLINE) || ETHR_XCHK \ - || (defined(__GNUC__) && defined(ERTS_MIXED_CYGWIN_VC)) + || (defined(__GNUC__) && defined(ERTS_MIXED_CYGWIN_VC)) \ + || (defined(__GNUC__) && defined(ERTS_MIXED_MSYS_VC)) # undef ETHR_INLINE # define ETHR_INLINE # undef ETHR_FORCE_INLINE -- cgit v1.2.3 From 63466c5522ed58b6e73e35dc29c7c7584f073768 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 2 Sep 2015 18:35:29 +0200 Subject: erts: Refactor erl_mmap to allow several mapper instances --- erts/emulator/beam/erl_alloc.c | 5 +- erts/emulator/beam/erl_bif_info.c | 2 +- erts/emulator/sys/common/erl_mmap.c | 550 ++++++++++++++++++------------------ erts/emulator/sys/common/erl_mmap.h | 19 +- erts/emulator/sys/common/erl_mseg.c | 11 +- 5 files changed, 299 insertions(+), 288 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index d83a2930d6..bb3998769c 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -2668,7 +2668,7 @@ erts_allocator_info(int to, void *arg) erts_mseg_info(i, &to, arg, 0, NULL, NULL); } erts_print(to, arg, "=allocator:mseg_alloc.erts_mmap\n"); - erts_mmap_info(&to, arg, NULL, NULL, &emis); + erts_mmap_info(&erts_dflt_mmapper, &to, arg, NULL, NULL, &emis); } #endif @@ -3020,7 +3020,8 @@ reply_alloc_info(void *vair) ai_list = erts_bld_cons(hpp, szp, ainfo, ai_list); - ainfo = (air->only_sz ? NIL : erts_mmap_info(NULL, NULL, hpp, szp, &emis)); + ainfo = (air->only_sz ? NIL : + erts_mmap_info(&erts_dflt_mmapper, NULL, NULL, hpp, szp, &emis)); ainfo = erts_bld_tuple3(hpp, szp, alloc_atom, erts_bld_atom(hpp,szp,"erts_mmap"), diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 79ccf8db64..a73ad826db 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3441,7 +3441,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) BIF_RET(res); } else if (ERTS_IS_ATOM_STR("mmap", BIF_ARG_1)) { - BIF_RET(erts_mmap_debug_info(BIF_P)); + BIF_RET(erts_mmap_debug_info(&erts_dflt_mmapper, BIF_P)); } else if (ERTS_IS_ATOM_STR("unique_monotonic_integer_state", BIF_ARG_1)) { BIF_RET(erts_debug_get_unique_monotonic_integer_state(BIF_P)); diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index eeaf96dd93..e8ff2bf752 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -51,21 +51,21 @@ #endif /* - * `mmap_state.sa.bot` and `mmap_state.sua.top` are read only after + * `mm->sa.bot` and `mm->sua.top` are read only after * initialization, but the other pointers are not; i.e., only * ERTS_MMAP_IN_SUPERCARRIER() is allowed without the mutex held. */ #define ERTS_MMAP_IN_SUPERCARRIER(PTR) \ - (((UWord) (PTR)) - ((UWord) mmap_state.sa.bot) \ - < ((UWord) mmap_state.sua.top) - ((UWord) mmap_state.sa.bot)) + (((UWord) (PTR)) - ((UWord) mm->sa.bot) \ + < ((UWord) mm->sua.top) - ((UWord) mm->sa.bot)) #define ERTS_MMAP_IN_SUPERALIGNED_AREA(PTR) \ - (ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&mmap_state.mtx)), \ - (((UWord) (PTR)) - ((UWord) mmap_state.sa.bot) \ - < ((UWord) mmap_state.sa.top) - ((UWord) mmap_state.sa.bot))) + (ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&mm->mtx)), \ + (((UWord) (PTR)) - ((UWord) mm->sa.bot) \ + < ((UWord) mm->sa.top) - ((UWord) mm->sa.bot))) #define ERTS_MMAP_IN_SUPERUNALIGNED_AREA(PTR) \ - (ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&mmap_state.mtx)), \ - (((UWord) (PTR)) - ((UWord) mmap_state.sua.bot) \ - < ((UWord) mmap_state.sua.top) - ((UWord) mmap_state.sua.bot))) + (ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&mm->mtx)), \ + (((UWord) (PTR)) - ((UWord) mm->sua.bot) \ + < ((UWord) mm->sua.top) - ((UWord) mm->sua.bot))) UWord erts_page_inv_mask; @@ -196,10 +196,10 @@ static ErtsMMapOp mmap_ops[ERTS_MMAP_OP_RINGBUF_SZ]; #define ERTS_MMAP_OP_LCK(RES, IN_SZ, OUT_SZ) \ do { \ - erts_smp_mtx_lock(&mmap_state.mtx); \ + erts_smp_mtx_lock(&mm->mtx); \ ERTS_MMAP_OP_START((IN_SZ)); \ ERTS_MMAP_OP_END((RES), (OUT_SZ)); \ - erts_smp_mtx_unlock(&mmap_state.mtx); \ + erts_smp_mtx_unlock(&mm->mtx); \ } while (0) #define ERTS_MUNMAP_OP(PTR, SZ) \ @@ -218,9 +218,9 @@ static ErtsMMapOp mmap_ops[ERTS_MMAP_OP_RINGBUF_SZ]; #define ERTS_MUNMAP_OP_LCK(PTR, SZ) \ do { \ - erts_smp_mtx_lock(&mmap_state.mtx); \ + erts_smp_mtx_lock(&mm->mtx); \ ERTS_MUNMAP_OP((PTR), (SZ)); \ - erts_smp_mtx_unlock(&mmap_state.mtx); \ + erts_smp_mtx_unlock(&mm->mtx); \ } while (0) #define ERTS_MREMAP_OP_START(OLD_PTR, OLD_SZ, IN_SZ) \ @@ -246,10 +246,10 @@ static ErtsMMapOp mmap_ops[ERTS_MMAP_OP_RINGBUF_SZ]; #define ERTS_MREMAP_OP_LCK(RES, OLD_PTR, OLD_SZ, IN_SZ, OUT_SZ) \ do { \ - erts_smp_mtx_lock(&mmap_state.mtx); \ + erts_smp_mtx_lock(&mm->mtx); \ ERTS_MREMAP_OP_START((OLD_PTR), (OLD_SZ), (IN_SZ)); \ ERTS_MREMAP_OP_END((RES), (OUT_SZ)); \ - erts_smp_mtx_unlock(&mmap_state.mtx); \ + erts_smp_mtx_unlock(&mm->mtx); \ } while (0) #define ERTS_MMAP_OP_ABORT() \ @@ -293,7 +293,7 @@ typedef struct { Uint nseg; }ErtsFreeSegMap; -static struct { +struct ErtsMemMapper_ { int (*reserve_physical)(char *, UWord); void (*unreserve_physical)(char *, UWord); int supercarrier; @@ -345,54 +345,56 @@ static struct { UWord used; } os; } size; -} mmap_state; +}; + +ErtsMemMapper erts_dflt_mmapper; #define ERTS_MMAP_SIZE_SC_SA_INC(SZ) \ do { \ - mmap_state.size.supercarrier.used.total += (SZ); \ - mmap_state.size.supercarrier.used.sa += (SZ); \ - ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total \ - <= mmap_state.size.supercarrier.total); \ - ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sa \ - <= mmap_state.size.supercarrier.used.total); \ + mm->size.supercarrier.used.total += (SZ); \ + mm->size.supercarrier.used.sa += (SZ); \ + ERTS_MMAP_ASSERT(mm->size.supercarrier.used.total \ + <= mm->size.supercarrier.total); \ + ERTS_MMAP_ASSERT(mm->size.supercarrier.used.sa \ + <= mm->size.supercarrier.used.total); \ } while (0) #define ERTS_MMAP_SIZE_SC_SA_DEC(SZ) \ do { \ - ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total >= (SZ)); \ - mmap_state.size.supercarrier.used.total -= (SZ); \ - ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sa >= (SZ)); \ - mmap_state.size.supercarrier.used.sa -= (SZ); \ + ERTS_MMAP_ASSERT(mm->size.supercarrier.used.total >= (SZ)); \ + mm->size.supercarrier.used.total -= (SZ); \ + ERTS_MMAP_ASSERT(mm->size.supercarrier.used.sa >= (SZ)); \ + mm->size.supercarrier.used.sa -= (SZ); \ } while (0) #define ERTS_MMAP_SIZE_SC_SUA_INC(SZ) \ do { \ - mmap_state.size.supercarrier.used.total += (SZ); \ - mmap_state.size.supercarrier.used.sua += (SZ); \ - ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total \ - <= mmap_state.size.supercarrier.total); \ - ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sua \ - <= mmap_state.size.supercarrier.used.total); \ + mm->size.supercarrier.used.total += (SZ); \ + mm->size.supercarrier.used.sua += (SZ); \ + ERTS_MMAP_ASSERT(mm->size.supercarrier.used.total \ + <= mm->size.supercarrier.total); \ + ERTS_MMAP_ASSERT(mm->size.supercarrier.used.sua \ + <= mm->size.supercarrier.used.total); \ } while (0) #define ERTS_MMAP_SIZE_SC_SUA_DEC(SZ) \ do { \ - ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.total >= (SZ)); \ - mmap_state.size.supercarrier.used.total -= (SZ); \ - ERTS_MMAP_ASSERT(mmap_state.size.supercarrier.used.sua >= (SZ)); \ - mmap_state.size.supercarrier.used.sua -= (SZ); \ + ERTS_MMAP_ASSERT(mm->size.supercarrier.used.total >= (SZ)); \ + mm->size.supercarrier.used.total -= (SZ); \ + ERTS_MMAP_ASSERT(mm->size.supercarrier.used.sua >= (SZ)); \ + mm->size.supercarrier.used.sua -= (SZ); \ } while (0) #define ERTS_MMAP_SIZE_OS_INC(SZ) \ do { \ - ERTS_MMAP_ASSERT(mmap_state.size.os.used + (SZ) >= (SZ)); \ - mmap_state.size.os.used += (SZ); \ + ERTS_MMAP_ASSERT(mm->size.os.used + (SZ) >= (SZ)); \ + mm->size.os.used += (SZ); \ } while (0) #define ERTS_MMAP_SIZE_OS_DEC(SZ) \ do { \ - ERTS_MMAP_ASSERT(mmap_state.size.os.used >= (SZ)); \ - mmap_state.size.os.used -= (SZ); \ + ERTS_MMAP_ASSERT(mm->size.os.used >= (SZ)); \ + mm->size.os.used -= (SZ); \ } while (0) static void -add_free_desc_area(char *start, char *end) +add_free_desc_area(ErtsMemMapper* mm, char *start, char *end) { ERTS_MMAP_ASSERT(end == (void *) 0 || end > start); if (sizeof(ErtsFreeSegDesc) <= ((UWord) end) - ((UWord) start)) { @@ -402,7 +404,7 @@ add_free_desc_area(char *start, char *end) no = 1; prev_desc = (ErtsFreeSegDesc *) start; - prev_desc->start = mmap_state.desc.free_list; + prev_desc->start = mm->desc.free_list; desc = (ErtsFreeSegDesc *) (start + sizeof(ErtsFreeSegDesc)); desc_end = start + 2*sizeof(ErtsFreeSegDesc); @@ -413,59 +415,59 @@ add_free_desc_area(char *start, char *end) desc_end += sizeof(ErtsFreeSegDesc); no++; } - mmap_state.desc.free_list = (char *) prev_desc; - mmap_state.no.free_seg_descs += no; + mm->desc.free_list = (char *) prev_desc; + mm->no.free_seg_descs += no; } } static ErtsFreeSegDesc * -add_unused_free_desc_area(void) +add_unused_free_desc_area(ErtsMemMapper* mm) { char *ptr; - if (!mmap_state.desc.unused_start) + if (!mm->desc.unused_start) return NULL; - ERTS_MMAP_ASSERT(mmap_state.desc.unused_end); + ERTS_MMAP_ASSERT(mm->desc.unused_end); ERTS_MMAP_ASSERT(ERTS_PAGEALIGNED_SIZE - <= mmap_state.desc.unused_end - mmap_state.desc.unused_start); + <= mm->desc.unused_end - mm->desc.unused_start); - ptr = mmap_state.desc.unused_start + ERTS_PAGEALIGNED_SIZE; - add_free_desc_area(mmap_state.desc.unused_start, ptr); + ptr = mm->desc.unused_start + ERTS_PAGEALIGNED_SIZE; + add_free_desc_area(mm, mm->desc.unused_start, ptr); - if ((mmap_state.desc.unused_end - ptr) >= ERTS_PAGEALIGNED_SIZE) - mmap_state.desc.unused_start = ptr; + if ((mm->desc.unused_end - ptr) >= ERTS_PAGEALIGNED_SIZE) + mm->desc.unused_start = ptr; else - mmap_state.desc.unused_end = mmap_state.desc.unused_start = NULL; + mm->desc.unused_end = mm->desc.unused_start = NULL; - ERTS_MMAP_ASSERT(mmap_state.desc.free_list); - return (ErtsFreeSegDesc *) mmap_state.desc.free_list; + ERTS_MMAP_ASSERT(mm->desc.free_list); + return (ErtsFreeSegDesc *) mm->desc.free_list; } static ERTS_INLINE ErtsFreeSegDesc * -alloc_desc(void) +alloc_desc(ErtsMemMapper* mm) { ErtsFreeSegDesc *res; - res = (ErtsFreeSegDesc *) mmap_state.desc.free_list; + res = (ErtsFreeSegDesc *) mm->desc.free_list; if (!res) { - res = add_unused_free_desc_area(); + res = add_unused_free_desc_area(mm); if (!res) return NULL; } - mmap_state.desc.free_list = res->start; - ASSERT(mmap_state.no.free_segs.curr < mmap_state.no.free_seg_descs); - mmap_state.no.free_segs.curr++; - if (mmap_state.no.free_segs.max < mmap_state.no.free_segs.curr) - mmap_state.no.free_segs.max = mmap_state.no.free_segs.curr; + mm->desc.free_list = res->start; + ASSERT(mm->no.free_segs.curr < mm->no.free_seg_descs); + mm->no.free_segs.curr++; + if (mm->no.free_segs.max < mm->no.free_segs.curr) + mm->no.free_segs.max = mm->no.free_segs.curr; return res; } static ERTS_INLINE void -free_desc(ErtsFreeSegDesc *desc) +free_desc(ErtsMemMapper* mm, ErtsFreeSegDesc *desc) { - desc->start = mmap_state.desc.free_list; - mmap_state.desc.free_list = (char *) desc; - ERTS_MMAP_ASSERT(mmap_state.no.free_segs.curr > 0); - mmap_state.no.free_segs.curr--; + desc->start = mm->desc.free_list; + mm->desc.free_list = (char *) desc; + ERTS_MMAP_ASSERT(mm->no.free_segs.curr > 0); + mm->no.free_segs.curr--; } static ERTS_INLINE ErtsFreeSegDesc* anode_to_desc(RBTNode* anode) @@ -1232,7 +1234,7 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map) # define ERTS_MMAP_FD (-1) # else # define ERTS_MMAP_FLAGS (MAP_PRIVATE) -# define ERTS_MMAP_FD mmap_state.mmap_fd +# define ERTS_MMAP_FD mm->mmap_fd # endif #endif @@ -1377,11 +1379,12 @@ static void unreserve_noop(char *ptr, UWord size) } static UWord -alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end) +alloc_desc_insert_free_seg(ErtsMemMapper* mm, + ErtsFreeSegMap *map, char* start, char* end) { char *ptr; ErtsFreeSegMap *da_map; - ErtsFreeSegDesc *desc = alloc_desc(); + ErtsFreeSegDesc *desc = alloc_desc(mm); if (desc) { insert_free_seg(map, desc, start, end); return 0; @@ -1394,13 +1397,13 @@ alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end) */ #if ERTS_HAVE_OS_MMAP - if (!mmap_state.no_os_mmap) { - ptr = os_mmap(mmap_state.desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0); + if (!mm->no_os_mmap) { + ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0); if (ptr) { - mmap_state.desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE; + mm->desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE; ERTS_MMAP_SIZE_OS_INC(ERTS_PAGEALIGNED_SIZE); - add_free_desc_area(ptr, ptr+ERTS_PAGEALIGNED_SIZE); - desc = alloc_desc(); + add_free_desc_area(mm, ptr, ptr+ERTS_PAGEALIGNED_SIZE); + desc = alloc_desc(mm); ERTS_MMAP_ASSERT(desc); insert_free_seg(map, desc, start, end); return 0; @@ -1411,20 +1414,20 @@ alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end) /* * ...then try to find a good place in the supercarrier... */ - da_map = &mmap_state.sua.map; + da_map = &mm->sua.map; desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE); if (desc) { - if (mmap_state.reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE)) + if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE)) ERTS_MMAP_SIZE_SC_SUA_INC(ERTS_PAGEALIGNED_SIZE); else desc = NULL; } else { - da_map = &mmap_state.sa.map; + da_map = &mm->sa.map; desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE); if (desc) { - if (mmap_state.reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE)) + if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE)) ERTS_MMAP_SIZE_SC_SA_INC(ERTS_PAGEALIGNED_SIZE); else desc = NULL; @@ -1432,15 +1435,15 @@ alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end) } if (desc) { char *da_end = desc->start + ERTS_PAGEALIGNED_SIZE; - add_free_desc_area(desc->start, da_end); + add_free_desc_area(mm, desc->start, da_end); if (da_end != desc->end) resize_free_seg(da_map, desc, da_end, desc->end); else { delete_free_seg(da_map, desc); - free_desc(desc); + free_desc(mm, desc); } - desc = alloc_desc(); + desc = alloc_desc(mm); ERTS_MMAP_ASSERT(desc); insert_free_seg(map, desc, start, end); return 0; @@ -1453,10 +1456,10 @@ alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end) ptr = start + ERTS_PAGEALIGNED_SIZE; ERTS_MMAP_ASSERT(ptr <= end); - add_free_desc_area(start, ptr); + add_free_desc_area(mm, start, ptr); if (ptr != end) { - desc = alloc_desc(); + desc = alloc_desc(mm); ERTS_MMAP_ASSERT(desc); insert_free_seg(map, desc, ptr, end); } @@ -1465,46 +1468,46 @@ alloc_desc_insert_free_seg(ErtsFreeSegMap *map, char* start, char* end) } void * -erts_mmap(Uint32 flags, UWord *sizep) +erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) { char *seg; UWord asize = ERTS_PAGEALIGNED_CEILING(*sizep); /* Map in premapped supercarrier */ - if (mmap_state.supercarrier && !(ERTS_MMAPFLG_OS_ONLY & flags)) { + if (mm->supercarrier && !(ERTS_MMAPFLG_OS_ONLY & flags)) { char *end; ErtsFreeSegDesc *desc; Uint32 superaligned = (ERTS_MMAPFLG_SUPERALIGNED & flags); - erts_smp_mtx_lock(&mmap_state.mtx); + erts_smp_mtx_lock(&mm->mtx); ERTS_MMAP_OP_START(*sizep); if (!superaligned) { - desc = lookup_free_seg(&mmap_state.sua.map, asize); + desc = lookup_free_seg(&mm->sua.map, asize); if (desc) { seg = desc->start; end = seg+asize; - if (!mmap_state.reserve_physical(seg, asize)) + if (!mm->reserve_physical(seg, asize)) goto supercarrier_reserve_failure; if (desc->end == end) { - delete_free_seg(&mmap_state.sua.map, desc); - free_desc(desc); + delete_free_seg(&mm->sua.map, desc); + free_desc(mm, desc); } else { ERTS_MMAP_ASSERT(end < desc->end); - resize_free_seg(&mmap_state.sua.map, desc, end, desc->end); + resize_free_seg(&mm->sua.map, desc, end, desc->end); } ERTS_MMAP_SIZE_SC_SUA_INC(asize); goto supercarrier_success; } - if (asize <= mmap_state.sua.bot - mmap_state.sa.top) { - if (!mmap_state.reserve_physical(mmap_state.sua.bot - asize, + if (asize <= mm->sua.bot - mm->sa.top) { + if (!mm->reserve_physical(mm->sua.bot - asize, asize)) goto supercarrier_reserve_failure; - mmap_state.sua.bot -= asize; - seg = mmap_state.sua.bot; + mm->sua.bot -= asize; + seg = mm->sua.bot; ERTS_MMAP_SIZE_SC_SUA_INC(asize); goto supercarrier_success; } @@ -1512,84 +1515,84 @@ erts_mmap(Uint32 flags, UWord *sizep) asize = ERTS_SUPERALIGNED_CEILING(asize); - desc = lookup_free_seg(&mmap_state.sa.map, asize); + desc = lookup_free_seg(&mm->sa.map, asize); if (desc) { char *start = seg = desc->start; seg = (char *) ERTS_SUPERALIGNED_CEILING(seg); end = seg+asize; - if (!mmap_state.reserve_physical(start, (UWord) (end - start))) + if (!mm->reserve_physical(start, (UWord) (end - start))) goto supercarrier_reserve_failure; ERTS_MMAP_SIZE_SC_SA_INC(asize); if (desc->end == end) { if (start != seg) - resize_free_seg(&mmap_state.sa.map, desc, start, seg); + resize_free_seg(&mm->sa.map, desc, start, seg); else { - delete_free_seg(&mmap_state.sa.map, desc); - free_desc(desc); + delete_free_seg(&mm->sa.map, desc); + free_desc(mm, desc); } } else { ERTS_MMAP_ASSERT(end < desc->end); - resize_free_seg(&mmap_state.sa.map, desc, end, desc->end); + resize_free_seg(&mm->sa.map, desc, end, desc->end); if (start != seg) { UWord ad_sz; - ad_sz = alloc_desc_insert_free_seg(&mmap_state.sua.map, + ad_sz = alloc_desc_insert_free_seg(mm, &mm->sua.map, start, seg); start += ad_sz; if (start != seg) - mmap_state.unreserve_physical(start, (UWord) (seg - start)); + mm->unreserve_physical(start, (UWord) (seg - start)); } } goto supercarrier_success; } if (superaligned) { - char *start = mmap_state.sa.top; + char *start = mm->sa.top; seg = (char *) ERTS_SUPERALIGNED_CEILING(start); - if (asize + (seg - start) <= mmap_state.sua.bot - start) { + if (asize + (seg - start) <= mm->sua.bot - start) { end = seg + asize; - if (!mmap_state.reserve_physical(start, (UWord) (end - start))) + if (!mm->reserve_physical(start, (UWord) (end - start))) goto supercarrier_reserve_failure; - mmap_state.sa.top = end; + mm->sa.top = end; ERTS_MMAP_SIZE_SC_SA_INC(asize); if (start != seg) { UWord ad_sz; - ad_sz = alloc_desc_insert_free_seg(&mmap_state.sua.map, + ad_sz = alloc_desc_insert_free_seg(mm, &mm->sua.map, start, seg); start += ad_sz; if (start != seg) - mmap_state.unreserve_physical(start, (UWord) (seg - start)); + mm->unreserve_physical(start, (UWord) (seg - start)); } goto supercarrier_success; } - desc = lookup_free_seg(&mmap_state.sua.map, asize + ERTS_SUPERALIGNED_SIZE); + desc = lookup_free_seg(&mm->sua.map, asize + ERTS_SUPERALIGNED_SIZE); if (desc) { char *org_start = desc->start; char *org_end = desc->end; seg = (char *) ERTS_SUPERALIGNED_CEILING(org_start); end = seg + asize; - if (!mmap_state.reserve_physical(seg, (UWord) (org_end - seg))) + if (!mm->reserve_physical(seg, (UWord) (org_end - seg))) goto supercarrier_reserve_failure; ERTS_MMAP_SIZE_SC_SUA_INC(asize); if (org_start != seg) { ERTS_MMAP_ASSERT(org_start < seg); - resize_free_seg(&mmap_state.sua.map, desc, org_start, seg); + resize_free_seg(&mm->sua.map, desc, org_start, seg); desc = NULL; } if (end != org_end) { UWord ad_sz = 0; ERTS_MMAP_ASSERT(end < org_end); if (desc) - resize_free_seg(&mmap_state.sua.map, desc, end, org_end); + resize_free_seg(&mm->sua.map, desc, end, org_end); else - ad_sz = alloc_desc_insert_free_seg(&mmap_state.sua.map, + ad_sz = alloc_desc_insert_free_seg(mm, &mm->sua.map, end, org_end); end += ad_sz; if (end != org_end) - mmap_state.unreserve_physical(end, + mm->unreserve_physical(end, (UWord) (org_end - end)); } goto supercarrier_success; @@ -1597,12 +1600,12 @@ erts_mmap(Uint32 flags, UWord *sizep) } ERTS_MMAP_OP_ABORT(); - erts_smp_mtx_unlock(&mmap_state.mtx); + erts_smp_mtx_unlock(&mm->mtx); } #if ERTS_HAVE_OS_MMAP /* Map using OS primitives */ - if (!(ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) && !mmap_state.no_os_mmap) { + if (!(ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) && !mm->no_os_mmap) { if (!(ERTS_MMAPFLG_SUPERALIGNED & flags)) { seg = os_mmap(NULL, asize, 0); if (!seg) @@ -1660,25 +1663,25 @@ supercarrier_success: #endif ERTS_MMAP_OP_END(seg, asize); - erts_smp_mtx_unlock(&mmap_state.mtx); + erts_smp_mtx_unlock(&mm->mtx); *sizep = asize; return (void *) seg; supercarrier_reserve_failure: - erts_smp_mtx_unlock(&mmap_state.mtx); + erts_smp_mtx_unlock(&mm->mtx); *sizep = 0; return NULL; } void -erts_munmap(Uint32 flags, void *ptr, UWord size) +erts_munmap(ErtsMemMapper* mm, Uint32 flags, void *ptr, UWord size) { ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(ptr)); ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(size)); if (!ERTS_MMAP_IN_SUPERCARRIER(ptr)) { - ERTS_MMAP_ASSERT(!mmap_state.no_os_mmap); + ERTS_MMAP_ASSERT(!mm->no_os_mmap); #if ERTS_HAVE_OS_MMAP ERTS_MUNMAP_OP_LCK(ptr, size); ERTS_MMAP_SIZE_OS_DEC(size); @@ -1691,45 +1694,45 @@ erts_munmap(Uint32 flags, void *ptr, UWord size) ErtsFreeSegDesc *prev, *next, *desc; UWord ad_sz = 0; - ERTS_MMAP_ASSERT(mmap_state.supercarrier); + ERTS_MMAP_ASSERT(mm->supercarrier); start = (char *) ptr; end = start + size; - erts_smp_mtx_lock(&mmap_state.mtx); + erts_smp_mtx_lock(&mm->mtx); ERTS_MUNMAP_OP(ptr, size); if (ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)) { - map = &mmap_state.sa.map; + map = &mm->sa.map; adjacent_free_seg(map, start, end, &prev, &next); ERTS_MMAP_SIZE_SC_SA_DEC(size); - if (end == mmap_state.sa.top) { + if (end == mm->sa.top) { ERTS_MMAP_ASSERT(!next); if (prev) { start = prev->start; delete_free_seg(map, prev); - free_desc(prev); + free_desc(mm, prev); } - mmap_state.sa.top = start; + mm->sa.top = start; goto supercarrier_success; } } else { - map = &mmap_state.sua.map; + map = &mm->sua.map; adjacent_free_seg(map, start, end, &prev, &next); ERTS_MMAP_SIZE_SC_SUA_DEC(size); - if (start == mmap_state.sua.bot) { + if (start == mm->sua.bot) { ERTS_MMAP_ASSERT(!prev); if (next) { end = next->end; delete_free_seg(map, next); - free_desc(next); + free_desc(mm, next); } - mmap_state.sua.bot = end; + mm->sua.bot = end; goto supercarrier_success; } } @@ -1741,7 +1744,7 @@ erts_munmap(Uint32 flags, void *ptr, UWord size) end = next->end; if (prev) { delete_free_seg(map, next); - free_desc(next); + free_desc(mm, next); goto save_prev; } desc = next; @@ -1755,7 +1758,7 @@ erts_munmap(Uint32 flags, void *ptr, UWord size) if (desc) resize_free_seg(map, desc, start, end); else - ad_sz = alloc_desc_insert_free_seg(map, start, end); + ad_sz = alloc_desc_insert_free_seg(mm, map, start, end); supercarrier_success: { UWord unres_sz; @@ -1763,30 +1766,32 @@ erts_munmap(Uint32 flags, void *ptr, UWord size) ERTS_MMAP_ASSERT(size >= ad_sz); unres_sz = size - ad_sz; if (unres_sz) - mmap_state.unreserve_physical(((char *) ptr) + ad_sz, unres_sz); + mm->unreserve_physical(((char *) ptr) + ad_sz, unres_sz); - erts_smp_mtx_unlock(&mmap_state.mtx); + erts_smp_mtx_unlock(&mm->mtx); } } } static void * -remap_move(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) +remap_move(ErtsMemMapper* mm, + Uint32 flags, void *ptr, UWord old_size, UWord *sizep) { UWord size = *sizep; - void *new_ptr = erts_mmap(flags, &size); + void *new_ptr = erts_mmap(mm, flags, &size); if (!new_ptr) return NULL; *sizep = size; if (old_size < size) size = old_size; sys_memcpy(new_ptr, ptr, (size_t) size); - erts_munmap(flags, ptr, old_size); + erts_munmap(mm, flags, ptr, old_size); return new_ptr; } void * -erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) +erts_mremap(ErtsMemMapper* mm, + Uint32 flags, void *ptr, UWord old_size, UWord *sizep) { void *new_ptr; Uint32 superaligned; @@ -1798,11 +1803,11 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) if (!ERTS_MMAP_IN_SUPERCARRIER(ptr)) { - ERTS_MMAP_ASSERT(!mmap_state.no_os_mmap); + ERTS_MMAP_ASSERT(!mm->no_os_mmap); - if (!(ERTS_MMAPFLG_OS_ONLY & flags) && mmap_state.supercarrier) { - new_ptr = remap_move(ERTS_MMAPFLG_SUPERCARRIER_ONLY|flags, ptr, - old_size, sizep); + if (!(ERTS_MMAPFLG_OS_ONLY & flags) && mm->supercarrier) { + new_ptr = remap_move(mm, ERTS_MMAPFLG_SUPERCARRIER_ONLY|flags, + ptr, old_size, sizep); if (new_ptr) return new_ptr; } @@ -1849,7 +1854,7 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) #endif #if ERTS_HAVE_OS_MREMAP if (superaligned) - return remap_move(flags, new_ptr, old_size, sizep); + return remap_move(mm, flags, new_ptr, old_size, sizep); else { new_ptr = os_mremap(ptr, old_size, asize, 0); if (!new_ptr) @@ -1871,10 +1876,10 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) ErtsFreeSegDesc *prev, *next; UWord ad_sz = 0; - ERTS_MMAP_ASSERT(mmap_state.supercarrier); + ERTS_MMAP_ASSERT(mm->supercarrier); if (ERTS_MMAPFLG_OS_ONLY & flags) - return remap_move(flags, ptr, old_size, sizep); + return remap_move(mm, flags, ptr, old_size, sizep); superaligned = (ERTS_MMAPFLG_SUPERALIGNED & flags); @@ -1882,19 +1887,19 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) ? ERTS_SUPERALIGNED_CEILING(*sizep) : ERTS_PAGEALIGNED_CEILING(*sizep)); - erts_smp_mtx_lock(&mmap_state.mtx); + erts_smp_mtx_lock(&mm->mtx); if (ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr) - ? (!superaligned && lookup_free_seg(&mmap_state.sua.map, asize)) - : (superaligned && lookup_free_seg(&mmap_state.sa.map, asize))) { - erts_smp_mtx_unlock(&mmap_state.mtx); + ? (!superaligned && lookup_free_seg(&mm->sua.map, asize)) + : (superaligned && lookup_free_seg(&mm->sa.map, asize))) { + erts_smp_mtx_unlock(&mm->mtx); /* * Segment currently in wrong area (due to a previous memory * shortage), move it to the right area. * (remap_move() will succeed) */ - return remap_move(ERTS_MMAPFLG_SUPERCARRIER_ONLY|flags, ptr, - old_size, sizep); + return remap_move(mm, ERTS_MMAPFLG_SUPERCARRIER_ONLY|flags, + ptr, old_size, sizep); } ERTS_MREMAP_OP_START(ptr, old_size, *sizep); @@ -1916,18 +1921,18 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) UWord unres_sz; new_ptr = ptr; if (!ERTS_MMAP_IN_SUPERALIGNED_AREA(ptr)) { - map = &mmap_state.sua.map; + map = &mm->sua.map; ERTS_MMAP_SIZE_SC_SUA_DEC(old_size - asize); } else { - if (end == mmap_state.sa.top) { - mmap_state.sa.top = new_end; - mmap_state.unreserve_physical(((char *) ptr) + asize, + if (end == mm->sa.top) { + mm->sa.top = new_end; + mm->unreserve_physical(((char *) ptr) + asize, old_size - asize); goto supercarrier_resize_success; } ERTS_MMAP_SIZE_SC_SA_DEC(old_size - asize); - map = &mmap_state.sa.map; + map = &mm->sa.map; } adjacent_free_seg(map, start, end, &prev, &next); @@ -1935,11 +1940,11 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) if (next) resize_free_seg(map, next, new_end, next->end); else - ad_sz = alloc_desc_insert_free_seg(map, new_end, end); + ad_sz = alloc_desc_insert_free_seg(mm, map, new_end, end); ERTS_MMAP_ASSERT(old_size - asize >= ad_sz); unres_sz = old_size - asize - ad_sz; if (unres_sz) - mmap_state.unreserve_physical(((char *) ptr) + asize + ad_sz, + mm->unreserve_physical(((char *) ptr) + asize + ad_sz, unres_sz); goto supercarrier_resize_success; } @@ -1949,17 +1954,17 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(old_size)); ERTS_MMAP_ASSERT(ERTS_IS_PAGEALIGNED(asize)); - adjacent_free_seg(&mmap_state.sua.map, start, end, &prev, &next); + adjacent_free_seg(&mm->sua.map, start, end, &prev, &next); if (next && new_end <= next->end) { - if (!mmap_state.reserve_physical(((char *) ptr) + old_size, + if (!mm->reserve_physical(((char *) ptr) + old_size, asize - old_size)) goto supercarrier_reserve_failure; if (new_end < next->end) - resize_free_seg(&mmap_state.sua.map, next, new_end, next->end); + resize_free_seg(&mm->sua.map, next, new_end, next->end); else { - delete_free_seg(&mmap_state.sua.map, next); - free_desc(next); + delete_free_seg(&mm->sua.map, next); + free_desc(mm, next); } new_ptr = ptr; ERTS_MMAP_SIZE_SC_SUA_INC(asize - old_size); @@ -1968,28 +1973,28 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) } else { /* Superaligned area */ - if (end == mmap_state.sa.top) { - if (new_end <= mmap_state.sua.bot) { - if (!mmap_state.reserve_physical(((char *) ptr) + old_size, + if (end == mm->sa.top) { + if (new_end <= mm->sua.bot) { + if (!mm->reserve_physical(((char *) ptr) + old_size, asize - old_size)) goto supercarrier_reserve_failure; - mmap_state.sa.top = new_end; + mm->sa.top = new_end; new_ptr = ptr; ERTS_MMAP_SIZE_SC_SA_INC(asize - old_size); goto supercarrier_resize_success; } } else { - adjacent_free_seg(&mmap_state.sa.map, start, end, &prev, &next); + adjacent_free_seg(&mm->sa.map, start, end, &prev, &next); if (next && new_end <= next->end) { - if (!mmap_state.reserve_physical(((char *) ptr) + old_size, + if (!mm->reserve_physical(((char *) ptr) + old_size, asize - old_size)) goto supercarrier_reserve_failure; if (new_end < next->end) - resize_free_seg(&mmap_state.sa.map, next, new_end, next->end); + resize_free_seg(&mm->sa.map, next, new_end, next->end); else { - delete_free_seg(&mmap_state.sa.map, next); - free_desc(next); + delete_free_seg(&mm->sa.map, next); + free_desc(mm, next); } new_ptr = ptr; ERTS_MMAP_SIZE_SC_SA_INC(asize - old_size); @@ -1999,12 +2004,12 @@ erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep) } ERTS_MMAP_OP_ABORT(); - erts_smp_mtx_unlock(&mmap_state.mtx); + erts_smp_mtx_unlock(&mm->mtx); /* Failed to resize... */ } - return remap_move(flags, ptr, old_size, sizep); + return remap_move(mm, flags, ptr, old_size, sizep); supercarrier_resize_success: @@ -2021,25 +2026,24 @@ supercarrier_resize_success: #endif ERTS_MREMAP_OP_END(new_ptr, asize); - erts_smp_mtx_unlock(&mmap_state.mtx); + erts_smp_mtx_unlock(&mm->mtx); *sizep = asize; return new_ptr; supercarrier_reserve_failure: ERTS_MREMAP_OP_END(NULL, old_size); - erts_smp_mtx_unlock(&mmap_state.mtx); + erts_smp_mtx_unlock(&mm->mtx); *sizep = old_size; return NULL; } -int erts_mmap_in_supercarrier(void *ptr) +int erts_mmap_in_supercarrier(ErtsMemMapper* mm, void *ptr) { return ERTS_MMAP_IN_SUPERCARRIER(ptr); } - static struct { Eterm total; Eterm total_sa; @@ -2102,7 +2106,7 @@ static void hard_dbg_mseg_init(void); #endif void -erts_mmap_init(ErtsMMapInit *init) +erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init) { int virtual_map = 0; char *start = NULL, *end = NULL; @@ -2130,17 +2134,17 @@ erts_mmap_init(ErtsMMapInit *init) ERTS_MMAP_OP_RINGBUF_INIT(); - mmap_state.supercarrier = 0; - mmap_state.reserve_physical = reserve_noop; - mmap_state.unreserve_physical = unreserve_noop; + mm->supercarrier = 0; + mm->reserve_physical = reserve_noop; + mm->unreserve_physical = unreserve_noop; #if HAVE_MMAP && !defined(MAP_ANON) - mmap_state.mmap_fd = open("/dev/zero", O_RDWR); - if (mmap_state.mmap_fd < 0) + mm->mmap_fd = open("/dev/zero", O_RDWR); + if (mm->mmap_fd < 0) erl_exit(-1, "erts_mmap: Failed to open /dev/zero\n"); #endif - erts_smp_mtx_init(&mmap_state.mtx, "erts_mmap"); + erts_smp_mtx_init(&mm->mtx, "erts_mmap"); erts_mtx_init(&am.init_mutex, "mmap_init_atoms"); #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION @@ -2157,8 +2161,8 @@ erts_mmap_init(ErtsMMapInit *init) sz = start - ptr; if (sz) os_munmap(end, sz); - mmap_state.reserve_physical = os_reserve_physical; - mmap_state.unreserve_physical = os_unreserve_physical; + mm->reserve_physical = os_reserve_physical; + mm->unreserve_physical = os_unreserve_physical; virtual_map = 1; } else @@ -2176,8 +2180,8 @@ erts_mmap_init(ErtsMMapInit *init) #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION if (!init->scrpm) { start = os_mmap_virtual(NULL, sz); - mmap_state.reserve_physical = os_reserve_physical; - mmap_state.unreserve_physical = os_unreserve_physical; + mm->reserve_physical = os_reserve_physical; + mm->unreserve_physical = os_unreserve_physical; virtual_map = 1; } else @@ -2205,30 +2209,30 @@ erts_mmap_init(ErtsMMapInit *init) } #endif - mmap_state.no.free_seg_descs = 0; - mmap_state.no.free_segs.curr = 0; - mmap_state.no.free_segs.max = 0; + mm->no.free_seg_descs = 0; + mm->no.free_segs.curr = 0; + mm->no.free_segs.max = 0; - mmap_state.size.supercarrier.total = 0; - mmap_state.size.supercarrier.used.total = 0; - mmap_state.size.supercarrier.used.sa = 0; - mmap_state.size.supercarrier.used.sua = 0; - mmap_state.size.os.used = 0; + mm->size.supercarrier.total = 0; + mm->size.supercarrier.used.total = 0; + mm->size.supercarrier.used.sa = 0; + mm->size.supercarrier.used.sua = 0; + mm->size.os.used = 0; - mmap_state.desc.new_area_hint = NULL; + mm->desc.new_area_hint = NULL; if (!start) { - mmap_state.sa.bot = NULL; - mmap_state.sua.top = NULL; - mmap_state.sa.bot = NULL; - mmap_state.sua.top = NULL; - mmap_state.no_os_mmap = 0; - mmap_state.supercarrier = 0; + mm->sa.bot = NULL; + mm->sua.top = NULL; + mm->sa.bot = NULL; + mm->sua.top = NULL; + mm->no_os_mmap = 0; + mm->supercarrier = 0; } else { size_t desc_size; - mmap_state.no_os_mmap = init->sco; + mm->no_os_mmap = init->sco; desc_size = init->scrfsd; if (desc_size < 100) @@ -2239,60 +2243,60 @@ erts_mmap_init(ErtsMMapInit *init) + ERTS_PAGEALIGNED_SIZE) > end - start) erl_exit(-1, "erts_mmap: No space for segments in super carrier\n"); - mmap_state.sa.bot = start; - mmap_state.sa.bot += desc_size; - mmap_state.sa.bot = (char *) ERTS_SUPERALIGNED_CEILING(mmap_state.sa.bot); - mmap_state.sa.top = mmap_state.sa.bot; - mmap_state.sua.top = end; - mmap_state.sua.bot = mmap_state.sua.top; + mm->sa.bot = start; + mm->sa.bot += desc_size; + mm->sa.bot = (char *) ERTS_SUPERALIGNED_CEILING(mm->sa.bot); + mm->sa.top = mm->sa.bot; + mm->sua.top = end; + mm->sua.bot = mm->sua.top; - mmap_state.size.supercarrier.used.total += (UWord) (mmap_state.sa.bot - start); + mm->size.supercarrier.used.total += (UWord) (mm->sa.bot - start); - mmap_state.desc.free_list = NULL; - mmap_state.desc.reserved = 0; + mm->desc.free_list = NULL; + mm->desc.reserved = 0; if (end == (void *) 0) { /* * Very unlikely, but we need a guarantee - * that `mmap_state.sua.top` always will + * that `mm->sua.top` always will * compare as larger than all segment pointers * into the super carrier... */ - mmap_state.sua.top -= ERTS_PAGEALIGNED_SIZE; - mmap_state.size.supercarrier.used.total += ERTS_PAGEALIGNED_SIZE; + mm->sua.top -= ERTS_PAGEALIGNED_SIZE; + mm->size.supercarrier.used.total += ERTS_PAGEALIGNED_SIZE; #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION - if (!virtual_map || os_reserve_physical(mmap_state.sua.top, ERTS_PAGEALIGNED_SIZE)) + if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE)) #endif - add_free_desc_area(mmap_state.sua.top, end); - mmap_state.desc.reserved += (end - mmap_state.sua.top) / sizeof(ErtsFreeSegDesc); + add_free_desc_area(mm, mm->sua.top, end); + mm->desc.reserved += (end - mm->sua.top) / sizeof(ErtsFreeSegDesc); } - mmap_state.size.supercarrier.total = (UWord) (mmap_state.sua.top - start); + mm->size.supercarrier.total = (UWord) (mm->sua.top - start); /* * Area before (and after) super carrier * will be used for free segment descritors. */ #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION - if (virtual_map && !os_reserve_physical(start, mmap_state.sa.bot - start)) + if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start)) erl_exit(-1, "erts_mmap: Failed to reserve physical memory for descriptors\n"); #endif - mmap_state.desc.unused_start = start; - mmap_state.desc.unused_end = mmap_state.sa.bot; - mmap_state.desc.reserved += ((mmap_state.desc.unused_end - start) + mm->desc.unused_start = start; + mm->desc.unused_end = mm->sa.bot; + mm->desc.reserved += ((mm->desc.unused_end - start) / sizeof(ErtsFreeSegDesc)); - init_free_seg_map(&mmap_state.sa.map, SA_SZ_ADDR_ORDER); - init_free_seg_map(&mmap_state.sua.map, SZ_REVERSE_ADDR_ORDER); + init_free_seg_map(&mm->sa.map, SA_SZ_ADDR_ORDER); + init_free_seg_map(&mm->sua.map, SZ_REVERSE_ADDR_ORDER); - mmap_state.supercarrier = 1; + mm->supercarrier = 1; - mmap_state.desc.new_area_hint = end; + mm->desc.new_area_hint = end; } #if !ERTS_HAVE_OS_MMAP - mmap_state.no_os_mmap = 1; + mm->no_os_mmap = 1; #endif #ifdef HARD_DEBUG_MSEG @@ -2307,7 +2311,8 @@ add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2) *lp = erts_bld_cons(hpp, szp, erts_bld_tuple(hpp, szp, 2, el1, el2), *lp); } -Eterm erts_mmap_info(int *print_to_p, +Eterm erts_mmap_info(ErtsMemMapper* mm, + int *print_to_p, void *print_to_arg, Eterm** hpp, Uint* szp, struct erts_mmap_info_struct* emis) @@ -2322,29 +2327,29 @@ Eterm erts_mmap_info(int *print_to_p, Eterm res = THE_NON_VALUE; if (!hpp) { - erts_smp_mtx_lock(&mmap_state.mtx); - emis->sizes[0] = mmap_state.size.supercarrier.total; - emis->sizes[1] = mmap_state.sa.top - mmap_state.sa.bot; - emis->sizes[2] = mmap_state.sua.top - mmap_state.sua.bot; - emis->sizes[3] = mmap_state.size.supercarrier.used.total; - emis->sizes[4] = mmap_state.size.supercarrier.used.sa; - emis->sizes[5] = mmap_state.size.supercarrier.used.sua; + erts_smp_mtx_lock(&mm->mtx); + emis->sizes[0] = mm->size.supercarrier.total; + emis->sizes[1] = mm->sa.top - mm->sa.bot; + emis->sizes[2] = mm->sua.top - mm->sua.bot; + emis->sizes[3] = mm->size.supercarrier.used.total; + emis->sizes[4] = mm->size.supercarrier.used.sa; + emis->sizes[5] = mm->size.supercarrier.used.sua; - emis->segs[0] = mmap_state.no.free_segs.curr; - emis->segs[1] = mmap_state.no.free_segs.max; - emis->segs[2] = mmap_state.no.free_seg_descs; - emis->segs[3] = mmap_state.desc.reserved; - emis->segs[4] = mmap_state.sa.map.nseg; - emis->segs[5] = mmap_state.sua.map.nseg; + emis->segs[0] = mm->no.free_segs.curr; + emis->segs[1] = mm->no.free_segs.max; + emis->segs[2] = mm->no.free_seg_descs; + emis->segs[3] = mm->desc.reserved; + emis->segs[4] = mm->sa.map.nseg; + emis->segs[5] = mm->sua.map.nseg; - emis->os_used = mmap_state.size.os.used; - erts_smp_mtx_unlock(&mmap_state.mtx); + emis->os_used = mm->size.os.used; + erts_smp_mtx_unlock(&mm->mtx); } if (print_to_p) { int to = *print_to_p; void *arg = print_to_arg; - if (mmap_state.supercarrier) { + if (mm->supercarrier) { const char* prefix = "supercarrier "; erts_print(to, arg, "%stotal size: %bpu\n", prefix, emis->sizes[0]); erts_print(to, arg, "%stotal sa size: %bpu\n", prefix, emis->sizes[1]); @@ -2359,7 +2364,7 @@ Eterm erts_mmap_info(int *print_to_p, erts_print(to, arg, "%ssa free segs: %bpu\n", prefix, emis->segs[4]); erts_print(to, arg, "%ssua free segs: %bpu\n", prefix, emis->segs[5]); } - if (!mmap_state.no_os_mmap) { + if (!mm->no_os_mmap) { erts_print(to, arg, "os mmap size used: %bpu\n", emis->os_used); } } @@ -2371,7 +2376,7 @@ Eterm erts_mmap_info(int *print_to_p, } lix = 0; - if (mmap_state.supercarrier) { + if (mm->supercarrier) { group[0] = erts_bld_atom_uword_2tup_list(hpp, szp, sizeof(size_tags)/sizeof(Eterm), size_tags, emis->sizes); @@ -2383,7 +2388,7 @@ Eterm erts_mmap_info(int *print_to_p, lix++; } - if (!mmap_state.no_os_mmap) { + if (!mm->no_os_mmap) { group[0] = erts_bld_atom_uword_2tup_list(hpp, szp, 1, &am.used, &emis->os_used); list[lix] = erts_bld_2tup_list(hpp, szp, 1, group_tags, group); @@ -2395,25 +2400,26 @@ Eterm erts_mmap_info(int *print_to_p, return res; } -Eterm erts_mmap_info_options(char *prefix, +Eterm erts_mmap_info_options(ErtsMemMapper* mm, + char *prefix, int *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp) { - const UWord scs = mmap_state.sua.top - mmap_state.sa.bot; - const Eterm sco = mmap_state.no_os_mmap ? am_true : am_false; - const Eterm scrpm = (mmap_state.reserve_physical == reserve_noop) ? am_true : am_false; + const UWord scs = mm->sua.top - mm->sa.bot; + const Eterm sco = mm->no_os_mmap ? am_true : am_false; + const Eterm scrpm = (mm->reserve_physical == reserve_noop) ? am_true : am_false; Eterm res = THE_NON_VALUE; if (print_to_p) { int to = *print_to_p; void *arg = print_to_arg; erts_print(to, arg, "%sscs: %bpu\n", prefix, scs); - if (mmap_state.supercarrier) { + if (mm->supercarrier) { erts_print(to, arg, "%ssco: %T\n", prefix, sco); erts_print(to, arg, "%sscrpm: %T\n", prefix, scrpm); - erts_print(to, arg, "%sscrfsd: %beu\n", prefix, mmap_state.desc.reserved); + erts_print(to, arg, "%sscrfsd: %beu\n", prefix, mm->desc.reserved); } } @@ -2423,9 +2429,9 @@ Eterm erts_mmap_info_options(char *prefix, } res = NIL; - if (mmap_state.supercarrier) { + if (mm->supercarrier) { add_2tup(hpp, szp, &res, am.scrfsd, - erts_bld_uint(hpp,szp, mmap_state.desc.reserved)); + erts_bld_uint(hpp,szp, mm->desc.reserved)); add_2tup(hpp, szp, &res, am.scrpm, scrpm); add_2tup(hpp, szp, &res, am.sco, sco); } @@ -2435,9 +2441,9 @@ Eterm erts_mmap_info_options(char *prefix, } -Eterm erts_mmap_debug_info(Process* p) +Eterm erts_mmap_debug_info(ErtsMemMapper* mm, Process* p) { - if (mmap_state.supercarrier) { + if (mm->supercarrier) { ERTS_DECL_AM(sabot); ERTS_DECL_AM(satop); ERTS_DECL_AM(suabot); @@ -2448,14 +2454,14 @@ Eterm erts_mmap_debug_info(Process* p) Eterm *hp, *hp_end; Uint may_need; - erts_smp_mtx_lock(&mmap_state.mtx); - values[0] = (UWord)mmap_state.sa.bot; - values[1] = (UWord)mmap_state.sa.top; - values[2] = (UWord)mmap_state.sua.bot; - values[3] = (UWord)mmap_state.sua.top; - sa_list = build_free_seg_list(p, &mmap_state.sa.map); - sua_list = build_free_seg_list(p, &mmap_state.sua.map); - erts_smp_mtx_unlock(&mmap_state.mtx); + erts_smp_mtx_lock(&mm->mtx); + values[0] = (UWord)mm->sa.bot; + values[1] = (UWord)mm->sa.top; + values[2] = (UWord)mm->sua.bot; + values[3] = (UWord)mm->sua.top; + sa_list = build_free_seg_list(p, &mm->sa.map); + sua_list = build_free_seg_list(p, &mm->sua.map); + erts_smp_mtx_unlock(&mm->mtx); may_need = 4*(2+3+2) + 2*(2+3); hp = HAlloc(p, may_need); diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 51f830d045..8707c23527 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -50,23 +50,26 @@ typedef struct { #define ERTS_MMAP_INIT_DEFAULT_INITER \ {{NULL, NULL}, {NULL, NULL}, 0, 1, (1 << 16), 1} -void *erts_mmap(Uint32 flags, UWord *sizep); -void erts_munmap(Uint32 flags, void *ptr, UWord size); -void *erts_mremap(Uint32 flags, void *ptr, UWord old_size, UWord *sizep); -int erts_mmap_in_supercarrier(void *ptr); -void erts_mmap_init(ErtsMMapInit*); +typedef struct ErtsMemMapper_ ErtsMemMapper; +extern ErtsMemMapper erts_dflt_mmapper; +void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep); +void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size); +void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep); +int erts_mmap_in_supercarrier(ErtsMemMapper*, void *ptr); +void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*); struct erts_mmap_info_struct { UWord sizes[6]; UWord segs[6]; UWord os_used; }; -Eterm erts_mmap_info(int *print_to_p, void *print_to_arg, +Eterm erts_mmap_info(ErtsMemMapper*, int *print_to_p, void *print_to_arg, Eterm** hpp, Uint* szp, struct erts_mmap_info_struct*); -Eterm erts_mmap_info_options(char *prefix, int *print_to_p, void *print_to_arg, +Eterm erts_mmap_info_options(ErtsMemMapper*, + char *prefix, int *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp); struct process; -Eterm erts_mmap_debug_info(struct process*); +Eterm erts_mmap_debug_info(ErtsMemMapper*, struct process*); #define ERTS_SUPERALIGNED_SIZE \ (1 << ERTS_MMAP_SUPERALIGNED_BITS) diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 7eb8a4a460..aadcc755c1 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -291,7 +291,7 @@ mseg_create(ErtsMsegAllctr_t *ma, Uint flags, UWord *sizep) if (MSEG_FLG_IS_2POW(flags)) mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; - seg = erts_mmap(mmap_flags, sizep); + seg = erts_mmap(&erts_dflt_mmapper, mmap_flags, sizep); #ifdef ERTS_PRINT_ERTS_MMAP erts_fprintf(stderr, "%p = erts_mmap(%s, {%bpu, %bpu});\n", seg, @@ -311,7 +311,7 @@ mseg_destroy(ErtsMsegAllctr_t *ma, Uint flags, void *seg_p, UWord size) { if (MSEG_FLG_IS_2POW(flags)) mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; - erts_munmap(mmap_flags, seg_p, size); + erts_munmap(&erts_dflt_mmapper, mmap_flags, seg_p, size); #ifdef ERTS_PRINT_ERTS_MMAP erts_fprintf(stderr, "erts_munmap(%s, %p, %bpu);\n", (mmap_flags & ERTS_MMAPFLG_SUPERALIGNED) ? "sa" : "sua", @@ -332,7 +332,7 @@ mseg_recreate(ErtsMsegAllctr_t *ma, Uint flags, void *old_seg, UWord old_size, U if (MSEG_FLG_IS_2POW(flags)) mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; - new_seg = erts_mremap(mmap_flags, old_seg, old_size, sizep); + new_seg = erts_mremap(&erts_dflt_mmapper, mmap_flags, old_seg, old_size, sizep); #ifdef ERTS_PRINT_ERTS_MMAP erts_fprintf(stderr, "%p = erts_mremap(%s, %p, %bpu, {%bpu, %bpu});\n", @@ -997,7 +997,8 @@ info_options(ErtsMsegAllctr_t *ma, { Eterm res; - res = erts_mmap_info_options(prefix, print_to_p, print_to_arg, hpp, szp); + res = erts_mmap_info_options(&erts_dflt_mmapper, + prefix, print_to_p, print_to_arg, hpp, szp); if (print_to_p) { int to = *print_to_p; @@ -1401,7 +1402,7 @@ erts_mseg_init(ErtsMsegInit_t *init) erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms"); - erts_mmap_init(&init->mmap); + erts_mmap_init(&erts_dflt_mmapper, &init->mmap); if (!IS_2POW(GET_PAGE_SIZE)) erl_exit(ERTS_ABORT_EXIT, "erts_mseg: Unexpected page_size %beu\n", GET_PAGE_SIZE); -- cgit v1.2.3 From b7f760cee119e1824de0cfc2b34ae6fe971bf505 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 11 Sep 2015 16:03:00 +0200 Subject: erts: Add support for fast erts_is_literal() --- erts/emulator/beam/beam_load.c | 1 + erts/emulator/beam/erl_alloc.c | 40 +++-- erts/emulator/beam/erl_alloc.h | 24 +++ erts/emulator/beam/erl_alloc_util.c | 286 ++++++++++++++++++++++++++++++++---- erts/emulator/beam/erl_alloc_util.h | 64 +++++++- erts/emulator/beam/sys.h | 3 + erts/emulator/sys/common/erl_mmap.c | 19 ++- erts/emulator/sys/common/erl_mmap.h | 10 +- erts/emulator/sys/common/erl_mseg.c | 2 +- erts/emulator/sys/common/erl_mseg.h | 6 +- 10 files changed, 407 insertions(+), 48 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index f0b4be4c3d..a846e96204 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4431,6 +4431,7 @@ freeze_code(LoaderState* stp) if (stp->literals[i].heap_frags) { move_multi_frags(&ptr, &code_off_heap, stp->literals[i].heap_frags, &stp->literals[i].term, 1); + ASSERT(erts_is_literal(ptr_val(stp->literals[i].term))); } else ASSERT(is_immed(stp->literals[i].term)); } diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index bb3998769c..3e300f88ea 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -310,6 +310,25 @@ set_default_literal_alloc_opts(struct au_init *ip) ip->init.util.rsbcmt = 0; ip->init.util.rmbcmt = 0; ip->init.util.acul = 0; + +#if defined(ARCH_32) +# if HAVE_ERTS_MSEG + ip->init.util.mseg_alloc = &erts_alcu_literal_32_mseg_alloc; + ip->init.util.mseg_realloc = &erts_alcu_literal_32_mseg_realloc; + ip->init.util.mseg_dealloc = &erts_alcu_literal_32_mseg_dealloc; +# endif + ip->init.util.sys_alloc = &erts_alcu_literal_32_sys_alloc; + ip->init.util.sys_realloc = &erts_alcu_literal_32_sys_realloc; + ip->init.util.sys_dealloc = &erts_alcu_literal_32_sys_dealloc; +#elif defined(ARCH_64) +# ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION + ip->init.util.mseg_alloc = &erts_alcu_literal_64_mseg_alloc; + ip->init.util.mseg_realloc = &erts_alcu_literal_64_mseg_realloc; + ip->init.util.mseg_dealloc = &erts_alcu_literal_64_mseg_dealloc; +# endif +#else +# error Unknown architecture +#endif } static void @@ -720,6 +739,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) #if HAVE_ERTS_MSEG init.mseg.nos = erts_no_schedulers; erts_mseg_init(&init.mseg); +# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) + erts_mmap_init(&erts_literal_mmapper, &init.mseg.literal_mmap); +# endif #endif erts_alcu_init(&init.alloc_util); @@ -991,7 +1013,7 @@ start_au_allocator(ErtsAlcType_t alctr_n, } for (i = 0; i < size; i++) { - void *as; + Allctr_t *as; atype = init->atype; if (!init->thr_spec) @@ -1028,22 +1050,22 @@ start_au_allocator(ErtsAlcType_t alctr_n, switch (atype) { case GOODFIT: - as = (void *) erts_gfalc_start((GFAllctr_t *) as0, + as = erts_gfalc_start((GFAllctr_t *) as0, &init->init.gf, &init->init.util); break; case BESTFIT: - as = (void *) erts_bfalc_start((BFAllctr_t *) as0, + as = erts_bfalc_start((BFAllctr_t *) as0, &init->init.bf, &init->init.util); break; case AFIT: - as = (void *) erts_afalc_start((AFAllctr_t *) as0, + as = erts_afalc_start((AFAllctr_t *) as0, &init->init.af, &init->init.util); break; case AOFIRSTFIT: - as = (void *) erts_aoffalc_start((AOFFAllctr_t *) as0, + as = erts_aoffalc_start((AOFFAllctr_t *) as0, &init->init.aoff, &init->init.util); break; @@ -1445,25 +1467,25 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) } else if (has_prefix("scs", argv[i]+3)) { #if HAVE_ERTS_MSEG - init->mseg.mmap.scs = + init->mseg.dflt_mmap.scs = #endif get_mb_value(argv[i]+6, argv, &i); } else if (has_prefix("sco", argv[i]+3)) { #if HAVE_ERTS_MSEG - init->mseg.mmap.sco = + init->mseg.dflt_mmap.sco = #endif get_bool_value(argv[i]+6, argv, &i); } else if (has_prefix("scrpm", argv[i]+3)) { #if HAVE_ERTS_MSEG - init->mseg.mmap.scrpm = + init->mseg.dflt_mmap.scrpm = #endif get_bool_value(argv[i]+8, argv, &i); } else if (has_prefix("scrfsd", argv[i]+3)) { #if HAVE_ERTS_MSEG - init->mseg.mmap.scrfsd = + init->mseg.dflt_mmap.scrfsd = #endif get_amount_value(argv[i]+9, argv, &i); } diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index f540bae20d..a0d8561e84 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -30,6 +30,7 @@ #ifdef USE_THREADS #include "erl_threads.h" #endif +#include "erl_mmap.h" #ifdef DEBUG # undef ERTS_ALC_WANT_INLINE @@ -204,6 +205,7 @@ void erts_free(ErtsAlcType_t type, void *ptr); void *erts_alloc_fnf(ErtsAlcType_t type, Uint size); void *erts_realloc_fnf(ErtsAlcType_t type, void *ptr, Uint size); int erts_is_allctr_wrapper_prelocked(void); +int erts_is_literal(void* ptr); #endif /* #if !ERTS_ALC_DO_INLINE */ @@ -281,6 +283,28 @@ int erts_is_allctr_wrapper_prelocked(void) && !!erts_tsd_get(erts_allctr_prelock_tsd_key); /* by me */ } +ERTS_ALC_INLINE +int erts_is_literal(void* ptr) +{ +#if defined(ARCH_32) + Uint ix = (UWord)ptr >> ERTS_MMAP_SUPERALIGNED_BITS; + + return erts_literal_vspace_map[ix / ERTS_VSPACE_WORD_BITS] + & ((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS)); + +#elif defined(ARCH_64) +# if defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) + extern char* erts_literals_start; + extern UWord erts_literals_size; + return ErtsInArea(ptr, erts_literals_start, erts_literals_size); +# else +# error Do the tag thing +# endif +#else +# error No ARCH_xx +#endif +} + #endif /* #if ERTS_ALC_DO_INLINE || defined(ERTS_ALC_INTERNAL__) */ #define ERTS_ALC_GET_THR_IX() ((int) erts_get_scheduler_id()) diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 444d7055c8..f34916f1ab 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -751,12 +751,77 @@ internal_free(void *ptr) #endif +#ifdef ARCH_32 + +/* + * Bit vector for the entire 32-bit virtual address space + * with one bit for each super aligned memory segment. + */ + +#define VSPACE_MAP_BITS (1 << (32 - ERTS_MMAP_SUPERALIGNED_BITS)) +#define VSPACE_MAP_SZ (VSPACE_MAP_BITS / ERTS_VSPACE_WORD_BITS) + +static ERTS_INLINE void set_bit(UWord* map, Uint ix) +{ + ASSERT(ix / ERTS_VSPACE_WORD_BITS < VSPACE_MAP_SZ); + map[ix / ERTS_VSPACE_WORD_BITS] + |= ((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS)); +} + +static ERTS_INLINE void clr_bit(UWord* map, Uint ix) +{ + ASSERT(ix / ERTS_VSPACE_WORD_BITS < VSPACE_MAP_SZ); + map[ix / ERTS_VSPACE_WORD_BITS] + &= ~((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS)); +} + +static ERTS_INLINE int is_bit_set(UWord* map, Uint ix) +{ + ASSERT(ix / ERTS_VSPACE_WORD_BITS < VSPACE_MAP_SZ); + return map[ix / ERTS_VSPACE_WORD_BITS] + & ((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS)); +} + +UWord erts_literal_vspace_map[VSPACE_MAP_SZ]; + +static void set_literal_range(void* start, Uint size) +{ + Uint ix = (UWord)start >> ERTS_MMAP_SUPERALIGNED_BITS; + Uint n = size >> ERTS_MMAP_SUPERALIGNED_BITS; + + ASSERT(!((UWord)start & ERTS_INV_SUPERALIGNED_MASK)); + ASSERT(!((UWord)size & ERTS_INV_SUPERALIGNED_MASK)); + ASSERT(n); + while (n--) { + ASSERT(!is_bit_set(erts_literal_vspace_map, ix)); + set_bit(erts_literal_vspace_map, ix); + ix++; + } +} + +static void clear_literal_range(void* start, Uint size) +{ + Uint ix = (UWord)start >> ERTS_MMAP_SUPERALIGNED_BITS; + Uint n = size >> ERTS_MMAP_SUPERALIGNED_BITS; + + ASSERT(!((UWord)start & ERTS_INV_SUPERALIGNED_MASK)); + ASSERT(!((UWord)size & ERTS_INV_SUPERALIGNED_MASK)); + ASSERT(n); + while (n--) { + ASSERT(is_bit_set(erts_literal_vspace_map, ix)); + clr_bit(erts_literal_vspace_map, ix); + ix++; + } +} + +#endif /* ARCH_32 */ + /* mseg ... */ #if HAVE_ERTS_MSEG -static ERTS_INLINE void * -alcu_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) +void* +erts_alcu_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) { void *res; UWord size = (UWord) *size_p; @@ -766,8 +831,9 @@ alcu_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) return res; } -static ERTS_INLINE void * -alcu_mseg_realloc(Allctr_t *allctr, void *seg, Uint old_size, Uint *new_size_p) +void* +erts_alcu_mseg_realloc(Allctr_t *allctr, void *seg, + Uint old_size, Uint *new_size_p) { void *res; UWord new_size = (UWord) *new_size_p; @@ -778,17 +844,103 @@ alcu_mseg_realloc(Allctr_t *allctr, void *seg, Uint old_size, Uint *new_size_p) return res; } -static ERTS_INLINE void -alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags) +void +erts_alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags) { erts_mseg_dealloc_opt(allctr->alloc_no, seg, (UWord) size, flags, &allctr->mseg_opt); INC_CC(allctr->calls.mseg_dealloc); } -#endif -static ERTS_INLINE void * -alcu_sys_alloc(Allctr_t *allctr, Uint size, int superalign) +#if defined(ARCH_32) + +void* +erts_alcu_literal_32_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) +{ + void* res; + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL + && !allctr->t && allctr->thread_safe); + + res = erts_alcu_mseg_alloc(allctr, size_p, flags); + if (res) + set_literal_range(res, *size_p); + return res; +} + +void* +erts_alcu_literal_32_mseg_realloc(Allctr_t *allctr, void *seg, + Uint old_size, Uint *new_size_p) +{ + void* res; + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL + && !allctr->t && allctr->thread_safe); + + if (seg && old_size) + clear_literal_range(seg, old_size); + res = erts_alcu_mseg_realloc(allctr, seg, old_size, new_size_p); + if (res) + set_literal_range(res, *new_size_p); + return res; +} + +void +erts_alcu_literal_32_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, + Uint flags) +{ + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL + && !allctr->t && allctr->thread_safe); + + erts_alcu_mseg_dealloc(allctr, seg, size, flags); + + clear_literal_range(seg, size); +} + +#elif defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) + +void* +erts_alcu_literal_64_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) +{ + void* res; + UWord size = (UWord) *size_p; + Uint32 mmap_flags = ERTS_MMAPFLG_SUPERCARRIER_ONLY; + if (flags & ERTS_MSEG_FLG_2POW) + mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; + + res = erts_mmap(&erts_literal_mmapper, mmap_flags, &size); + *size_p = (Uint)size; + INC_CC(allctr->calls.mseg_alloc); + return res; +} + +void* +erts_alcu_literal_64_mseg_realloc(Allctr_t *allctr, void *seg, + Uint old_size, Uint *new_size_p) +{ + void *res; + UWord new_size = (UWord) *new_size_p; + res = erts_mremap(&erts_literal_mmapper, ERTS_MSEG_FLG_NONE, seg, old_size, &new_size); + *new_size_p = (Uint) new_size; + INC_CC(allctr->calls.mseg_realloc); + return res; +} + +void +erts_alcu_literal_64_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, + Uint flags) +{ + Uint32 mmap_flags = ERTS_MMAPFLG_SUPERCARRIER_ONLY; + if (flags & ERTS_MSEG_FLG_2POW) + mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; + + erts_munmap(&erts_literal_mmapper, mmap_flags, seg, (UWord)size); + INC_CC(allctr->calls.mseg_dealloc); +} +#endif /* ARCH_64 && ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION */ + +#endif /* HAVE_ERTS_MSEG */ + +void* +erts_alcu_sys_alloc(Allctr_t *allctr, Uint size, int superalign) { void *res; #if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC @@ -803,8 +955,8 @@ alcu_sys_alloc(Allctr_t *allctr, Uint size, int superalign) return res; } -static ERTS_INLINE void * -alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign) +void* +erts_alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign) { void *res; @@ -824,8 +976,8 @@ alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int supe return res; } -static ERTS_INLINE void -alcu_sys_free(Allctr_t *allctr, void *ptr, int superalign) +void +erts_alcu_sys_dealloc(Allctr_t *allctr, void *ptr, Uint size, int superalign) { #if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC if (superalign) @@ -838,6 +990,49 @@ alcu_sys_free(Allctr_t *allctr, void *ptr, int superalign) erts_mtrace_crr_free(allctr->alloc_no, ERTS_ALC_A_SYSTEM, ptr); } +#ifdef ARCH_32 + +void* +erts_alcu_literal_32_sys_alloc(Allctr_t *allctr, Uint size, int superalign) +{ + void* res; + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL + && !allctr->t && allctr->thread_safe); + + res = erts_alcu_sys_alloc(allctr, size, 1); + if (res) + set_literal_range(res, size); + return res; +} + +void* +erts_alcu_literal_32_sys_realloc(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign) +{ + void* res; + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL + && !allctr->t && allctr->thread_safe); + + if (ptr && old_size) + clear_literal_range(ptr, old_size); + res = erts_alcu_sys_realloc(allctr, ptr, size, old_size, 1); + if (res) + set_literal_range(res, size); + return res; +} + +void +erts_alcu_literal_32_sys_dealloc(Allctr_t *allctr, void *ptr, Uint size, int superalign) +{ + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL + && !allctr->t && allctr->thread_safe); + + erts_alcu_sys_dealloc(allctr, ptr, size, 1); + + clear_literal_range(ptr, size); +} + +#endif /* ARCH_32 */ + static Uint get_next_mbc_size(Allctr_t *allctr) { @@ -3519,6 +3714,8 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) return NULL; #endif } + flags |= allctr->crr_set_flgs; + flags &= ~allctr->crr_clr_flgs; ASSERT((flags & CFLG_SBC && !(flags & CFLG_MBC)) || (flags & CFLG_MBC && !(flags & CFLG_SBC))); @@ -3602,7 +3799,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) mseg_flags = ERTS_MSEG_FLG_2POW; } - crr = (Carrier_t *) alcu_mseg_alloc(allctr, &crr_sz, mseg_flags); + crr = (Carrier_t *) allctr->mseg_alloc(allctr, &crr_sz, mseg_flags); if (!crr) { have_tried_mseg = 1; if (!(have_tried_sys_alloc || flags & CFLG_FORCE_MSEG)) @@ -3644,12 +3841,12 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) ? UNIT_CEILING(bcrr_sz) : SYS_ALLOC_CARRIER_CEILING(bcrr_sz)); - crr = (Carrier_t *) alcu_sys_alloc(allctr, crr_sz, flags & CFLG_MBC); + crr = (Carrier_t *) allctr->sys_alloc(allctr, crr_sz, flags & CFLG_MBC); if (!crr) { if (crr_sz > UNIT_CEILING(bcrr_sz)) { crr_sz = UNIT_CEILING(bcrr_sz); - crr = (Carrier_t *) alcu_sys_alloc(allctr, crr_sz, flags & CFLG_MBC); + crr = (Carrier_t *) allctr->sys_alloc(allctr, crr_sz, flags & CFLG_MBC); } if (!crr) { #if HAVE_ERTS_MSEG @@ -3748,7 +3945,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) new_crr_sz = new_blk_sz + SBC_HEADER_SIZE; new_crr_sz = ERTS_SACRR_UNIT_CEILING(new_crr_sz); - new_crr = (Carrier_t *) alcu_mseg_realloc(allctr, + new_crr = (Carrier_t *) allctr->mseg_realloc(allctr, old_crr, old_crr_sz, &new_crr_sz); @@ -3773,7 +3970,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) (void *) BLK2UMEM(old_blk), MIN(new_blk_sz, old_blk_sz) - ABLK_HDR_SZ); unlink_carrier(&allctr->sbc_list, old_crr); - alcu_mseg_dealloc(allctr, old_crr, old_crr_sz, ERTS_MSEG_FLG_NONE); + allctr->mseg_dealloc(allctr, old_crr, old_crr_sz, ERTS_MSEG_FLG_NONE); } else { /* Old carrier unchanged; restore stat */ @@ -3790,7 +3987,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) ? UNIT_CEILING(new_bcrr_sz) : SYS_ALLOC_CARRIER_CEILING(new_bcrr_sz)); - new_crr = (Carrier_t *) alcu_sys_realloc(allctr, + new_crr = (Carrier_t *) allctr->sys_realloc(allctr, (void *) old_crr, new_crr_sz, old_crr_sz, @@ -3811,7 +4008,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) else if (new_crr_sz > UNIT_CEILING(new_bcrr_sz)) { new_crr_sz = new_blk_sz + SBC_HEADER_SIZE; new_crr_sz = UNIT_CEILING(new_crr_sz); - new_crr = (Carrier_t *) alcu_sys_realloc(allctr, + new_crr = (Carrier_t *) allctr->sys_realloc(allctr, (void *) old_crr, new_crr_sz, old_crr_sz, @@ -3834,7 +4031,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) (void *) BLK2UMEM(old_blk), MIN(new_blk_sz, old_blk_sz) - ABLK_HDR_SZ); unlink_carrier(&allctr->sbc_list, old_crr); - alcu_sys_free(allctr, old_crr, 0); + allctr->sys_dealloc(allctr, old_crr, CARRIER_SZ(old_crr), 0); } else { /* Old carrier unchanged; restore... */ @@ -3850,13 +4047,13 @@ dealloc_carrier(Allctr_t *allctr, Carrier_t *crr, int superaligned) { #if HAVE_ERTS_MSEG if (IS_MSEG_CARRIER(crr)) - alcu_mseg_dealloc(allctr, crr, CARRIER_SZ(crr), + allctr->mseg_dealloc(allctr, crr, CARRIER_SZ(crr), (superaligned ? ERTS_MSEG_FLG_2POW : ERTS_MSEG_FLG_NONE)); else #endif - alcu_sys_free(allctr, crr, superaligned); + allctr->sys_dealloc(allctr, crr, CARRIER_SZ(crr), superaligned); } static void @@ -5854,17 +6051,52 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) + ABLK_HDR_SZ) - ABLK_HDR_SZ); + if (init->sys_alloc) { + ASSERT(init->sys_realloc && init->sys_dealloc); + allctr->sys_alloc = init->sys_alloc; + allctr->sys_realloc = init->sys_realloc; + allctr->sys_dealloc = init->sys_dealloc; + } + else { + ASSERT(!init->sys_realloc && !init->sys_dealloc); + allctr->sys_alloc = &erts_alcu_sys_alloc; + allctr->sys_realloc = &erts_alcu_sys_realloc; + allctr->sys_dealloc = &erts_alcu_sys_dealloc; + } +#if HAVE_ERTS_MSEG + if (init->mseg_alloc) { + ASSERT(init->mseg_realloc && init->mseg_dealloc); + allctr->mseg_alloc = init->mseg_alloc; + allctr->mseg_realloc = init->mseg_realloc; + allctr->mseg_dealloc = init->mseg_dealloc; + } + else { + ASSERT(!init->mseg_realloc && !init->mseg_dealloc); + allctr->mseg_alloc = &erts_alcu_mseg_alloc; + allctr->mseg_realloc = &erts_alcu_mseg_realloc; + allctr->mseg_dealloc = &erts_alcu_mseg_dealloc; + } + /* If a custom carrier alloc function is specified, make sure it's used */ + if (init->mseg_alloc && !init->sys_alloc) { + allctr->crr_set_flgs = CFLG_FORCE_MSEG; + allctr->crr_clr_flgs = CFLG_FORCE_SYS_ALLOC; + } + else if (!init->mseg_alloc && init->sys_alloc) { + allctr->crr_set_flgs = CFLG_FORCE_SYS_ALLOC; + allctr->crr_clr_flgs = CFLG_FORCE_MSEG; + } +#endif + if (allctr->main_carrier_size) { Block_t *blk; blk = create_carrier(allctr, allctr->main_carrier_size, - CFLG_MBC + (ERTS_SUPER_ALIGNED_MSEG_ONLY + ? CFLG_FORCE_MSEG : CFLG_FORCE_SYS_ALLOC) + | CFLG_MBC | CFLG_FORCE_SIZE | CFLG_NO_CPOOL -#if !ERTS_SUPER_ALIGNED_MSEG_ONLY - | CFLG_FORCE_SYS_ALLOC -#endif | CFLG_MAIN_CARRIER); if (!blk) { #ifdef USE_THREADS diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index f314eab4c2..16ad673d26 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -24,6 +24,12 @@ #define ERTS_ALCU_VSN_STR "3.0" #include "erl_alloc_types.h" +#ifdef USE_THREADS +#define ERL_THREADS_EMU_INTERNAL__ +#include "erl_threads.h" +#endif + +#include "erl_mseg.h" #define ERTS_AU_PREF_ALLOC_BITS 11 #define ERTS_AU_MAX_PREF_ALLOC_INSTANCES (1 << ERTS_AU_PREF_ALLOC_BITS) @@ -60,6 +66,15 @@ typedef struct { void *fix; size_t *fix_type_size; + +#if HAVE_ERTS_MSEG + void* (*mseg_alloc)(Allctr_t*, Uint *size_p, Uint flags); + void* (*mseg_realloc)(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p); + void (*mseg_dealloc)(Allctr_t*, void *seg, Uint size, Uint flags); +#endif + void* (*sys_alloc)(Allctr_t *allctr, Uint size, int superalign); + void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign); + void (*sys_dealloc)(Allctr_t *allctr, void *ptr, Uint size, int superalign); } AllctrInit_t; typedef struct { @@ -173,20 +188,44 @@ void erts_alcu_check_delayed_dealloc(Allctr_t *, int, int *, ErtsThrPrgrVal * #endif erts_aint32_t erts_alcu_fix_alloc_shrink(Allctr_t *, erts_aint32_t); +#ifdef ARCH_32 +extern UWord erts_literal_vspace_map[]; +# define ERTS_VSPACE_WORD_BITS (sizeof(UWord)*8) +#endif + +void* erts_alcu_mseg_alloc(Allctr_t*, Uint *size_p, Uint flags); +void* erts_alcu_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p); +void erts_alcu_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags); + +#if HAVE_ERTS_MSEG +# if defined(ARCH_32) +void* erts_alcu_literal_32_mseg_alloc(Allctr_t*, Uint *size_p, Uint flags); +void* erts_alcu_literal_32_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p); +void erts_alcu_literal_32_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags); + +# elif defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) +void* erts_alcu_literal_64_mseg_alloc(Allctr_t*, Uint *size_p, Uint flags); +void* erts_alcu_literal_64_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p); +void erts_alcu_literal_64_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags); +# endif +#endif /* HAVE_ERTS_MSEG */ + +void* erts_alcu_sys_alloc(Allctr_t*, Uint size, int superalign); +void* erts_alcu_sys_realloc(Allctr_t*, void *ptr, Uint size, Uint old_size, int superalign); +void erts_alcu_sys_dealloc(Allctr_t*, void *ptr, Uint size, int superalign); +#ifdef ARCH_32 +void* erts_alcu_literal_32_sys_alloc(Allctr_t*, Uint size, int superalign); +void* erts_alcu_literal_32_sys_realloc(Allctr_t*, void *ptr, Uint size, Uint old_size, int superalign); +void erts_alcu_literal_32_sys_dealloc(Allctr_t*, void *ptr, Uint size, int superalign); #endif +#endif /* !ERL_ALLOC_UTIL__ */ + #if defined(GET_ERL_ALLOC_UTIL_IMPL) && !defined(ERL_ALLOC_UTIL_IMPL__) #define ERL_ALLOC_UTIL_IMPL__ #define ERTS_ALCU_FLG_FAIL_REALLOC_MOVE (((Uint32) 1) << 0) -#ifdef USE_THREADS -#define ERL_THREADS_EMU_INTERNAL__ -#include "erl_threads.h" -#endif - -#include "erl_mseg.h" - #undef ERTS_ALLOC_UTIL_HARD_DEBUG #ifdef DEBUG # if 0 @@ -498,6 +537,8 @@ struct Allctr_t_ { Uint min_mbc_size; Uint min_mbc_first_free_size; Uint min_block_size; + UWord crr_set_flgs; + UWord crr_clr_flgs; /* Carriers */ CarrierList_t mbc_list; @@ -543,6 +584,15 @@ struct Allctr_t_ { void (*remove_mbc) (Allctr_t *, Carrier_t *); UWord (*largest_fblk_in_mbc) (Allctr_t *, Carrier_t *); +#if HAVE_ERTS_MSEG + void* (*mseg_alloc)(Allctr_t*, Uint *size_p, Uint flags); + void* (*mseg_realloc)(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p); + void (*mseg_dealloc)(Allctr_t*, void *seg, Uint size, Uint flags); +#endif + void* (*sys_alloc)(Allctr_t *allctr, Uint size, int superalign); + void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign); + void (*sys_dealloc)(Allctr_t *allctr, void *ptr, Uint size, int superalign); + void (*init_atoms) (void); #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 34011147d9..46577bd07f 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -1045,6 +1045,9 @@ extern int erts_use_kernel_poll; #define put_int8(i, s) do {((unsigned char*)(s))[0] = (i) & 0xff;} while (0) +#define ErtsInArea(PTR,START,NBYTES) \ + ((UWord)((char*)(PTR) - (char*)(START)) < (NBYTES)) + /* * Use DEBUGF as you would use printf, but use double parentheses: * diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index e8ff2bf752..03ca080c14 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -349,6 +349,12 @@ struct ErtsMemMapper_ { ErtsMemMapper erts_dflt_mmapper; +#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) +ErtsMemMapper erts_literal_mmapper; +char* erts_literals_start; +UWord erts_literals_size; +#endif + #define ERTS_MMAP_SIZE_SC_SA_INC(SZ) \ do { \ mm->size.supercarrier.used.total += (SZ); \ @@ -2108,6 +2114,7 @@ static void hard_dbg_mseg_init(void); void erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init) { + static int is_first_call = 1; int virtual_map = 0; char *start = NULL, *end = NULL; UWord pagesize; @@ -2145,7 +2152,9 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init) #endif erts_smp_mtx_init(&mm->mtx, "erts_mmap"); - erts_mtx_init(&am.init_mutex, "mmap_init_atoms"); + if (is_first_call) { + erts_mtx_init(&am.init_mutex, "mmap_init_atoms"); + } #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION if (init->virtual_range.start) { @@ -2302,6 +2311,14 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init) #ifdef HARD_DEBUG_MSEG hard_dbg_mseg_init(); #endif + +#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) + if (mm == &erts_literal_mmapper) { + erts_literals_start = erts_literal_mmapper.sa.bot; + erts_literals_size = erts_literal_mmapper.sua.top - erts_literals_start; + } +#endif + is_first_call = 0; } diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 8707c23527..61d912fd28 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -50,8 +50,11 @@ typedef struct { #define ERTS_MMAP_INIT_DEFAULT_INITER \ {{NULL, NULL}, {NULL, NULL}, 0, 1, (1 << 16), 1} +#define ERTS_MMAP_INIT_LITERAL_INITER \ + {{NULL, NULL}, {NULL, NULL}, 1024*1024*1024, 1, (1 << 16), 0} + typedef struct ErtsMemMapper_ ErtsMemMapper; -extern ErtsMemMapper erts_dflt_mmapper; + void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep); void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size); void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep); @@ -121,6 +124,11 @@ Eterm erts_mmap_debug_info(ErtsMemMapper*, struct process*); # define ERTS_HAVE_OS_MMAP 1 #endif +extern ErtsMemMapper erts_dflt_mmapper; +#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) +extern ErtsMemMapper erts_literal_mmapper; +#endif + /*#define HARD_DEBUG_MSEG*/ #ifdef HARD_DEBUG_MSEG # define HARD_DBG_INSERT_MSEG hard_dbg_insert_mseg diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index aadcc755c1..20695899eb 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -1402,7 +1402,7 @@ erts_mseg_init(ErtsMsegInit_t *init) erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms"); - erts_mmap_init(&erts_dflt_mmapper, &init->mmap); + erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap); if (!IS_2POW(GET_PAGE_SIZE)) erl_exit(ERTS_ABORT_EXIT, "erts_mseg: Unexpected page_size %beu\n", GET_PAGE_SIZE); diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index 677f8ea4ed..2acd8f8505 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -59,7 +59,8 @@ typedef struct { Uint rmcbf; Uint mcs; Uint nos; - ErtsMMapInit mmap; + ErtsMMapInit dflt_mmap; + ErtsMMapInit literal_mmap; } ErtsMsegInit_t; #define ERTS_MSEG_INIT_DEFAULT_INITIALIZER \ @@ -68,7 +69,8 @@ typedef struct { 20, /* rmcbf: Relative max cache bad fit */ \ 10, /* mcs: Max cache size */ \ 1000, /* cci: Cache check interval */ \ - ERTS_MMAP_INIT_DEFAULT_INITER \ + ERTS_MMAP_INIT_DEFAULT_INITER, \ + ERTS_MMAP_INIT_LITERAL_INITER \ } typedef struct { -- cgit v1.2.3 From 49d2f809cf8435b17d54f0fd2f37a8aa939ea457 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 10 Sep 2015 12:01:17 +0200 Subject: fix check_process_code for separate literal area --- erts/emulator/beam/beam_bif_load.c | 28 ++++++++++++---------------- erts/emulator/beam/erl_gc.c | 4 ++-- 2 files changed, 14 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 68689b8e7f..adc3bd37d9 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -728,8 +728,8 @@ static Eterm check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) { BeamInstr* start; - char* mod_start; - Uint mod_size; + char* literals; + Uint lit_bsize; BeamInstr* end; Eterm* sp; struct erl_off_heap_header* oh; @@ -742,8 +742,6 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) */ start = modp->old.code; end = (BeamInstr *)((char *)start + modp->old.code_length); - mod_start = (char *) start; - mod_size = modp->old.code_length; /* * Check if current instruction or continuation pointer points into module. @@ -834,22 +832,24 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) * See if there are constants inside the module referenced by the process. */ done_gc = 0; + literals = (char*) modp->old.code[MI_LITERALS_START]; + lit_bsize = (char*) modp->old.code[MI_LITERALS_END] - literals; for (;;) { ErlMessage* mp; - if (any_heap_ref_ptrs(&rp->fvalue, &rp->fvalue+1, mod_start, mod_size)) { + if (any_heap_ref_ptrs(&rp->fvalue, &rp->fvalue+1, literals, lit_bsize)) { rp->freason = EXC_NULL; rp->fvalue = NIL; rp->ftrace = NIL; } - if (any_heap_ref_ptrs(rp->stop, rp->hend, mod_start, mod_size)) { + if (any_heap_ref_ptrs(rp->stop, rp->hend, literals, lit_bsize)) { goto need_gc; } - if (any_heap_refs(rp->heap, rp->htop, mod_start, mod_size)) { + if (any_heap_refs(rp->heap, rp->htop, literals, lit_bsize)) { goto need_gc; } - if (any_heap_refs(rp->old_heap, rp->old_htop, mod_start, mod_size)) { + if (any_heap_refs(rp->old_heap, rp->old_htop, literals, lit_bsize)) { goto need_gc; } @@ -857,13 +857,13 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) Eterm* start = rp->dictionary->data; Eterm* end = start + rp->dictionary->used; - if (any_heap_ref_ptrs(start, end, mod_start, mod_size)) { + if (any_heap_ref_ptrs(start, end, literals, lit_bsize)) { goto need_gc; } } for (mp = rp->msg.first; mp != NULL; mp = mp->next) { - if (any_heap_ref_ptrs(mp->m, mp->m+2, mod_start, mod_size)) { + if (any_heap_ref_ptrs(mp->m, mp->m+2, literals, lit_bsize)) { goto need_gc; } } @@ -873,8 +873,6 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) if (done_gc) { return am_true; } else { - Eterm* literals; - Uint lit_size; struct erl_off_heap_header* oh; if (!allow_gc) @@ -890,12 +888,10 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) done_gc = 1; FLAGS(rp) |= F_NEED_FULLSWEEP; *redsp += erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); - literals = (Eterm *) modp->old.code[MI_LITERALS_START]; - lit_size = (Eterm *) modp->old.code[MI_LITERALS_END] - literals; oh = (struct erl_off_heap_header *) modp->old.code[MI_LITERALS_OFF_HEAP]; - *redsp += lit_size / 10; /* Need, better value... */ - erts_garbage_collect_literals(rp, literals, lit_size, oh); + *redsp += lit_bsize / 64; /* Need, better value... */ + erts_garbage_collect_literals(rp, (Eterm*)literals, lit_bsize, oh); } } return am_false; diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 734f120e09..89fabde67c 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -645,10 +645,10 @@ erts_garbage_collect_hibernate(Process* p) void erts_garbage_collect_literals(Process* p, Eterm* literals, - Uint lit_size, + Uint byte_lit_size, struct erl_off_heap_header* oh) { - Uint byte_lit_size = sizeof(Eterm)*lit_size; + Uint lit_size = byte_lit_size / sizeof(Eterm); Uint old_heap_size; Eterm* temp_lit; Sint offs; -- cgit v1.2.3 From b21b604137c5cb5f5039a40994e429871e5b707b Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 26 Aug 2015 19:47:10 +0200 Subject: Introduce literal tag --- erts/configure.in | 2 +- erts/emulator/beam/beam_emu.c | 3 +++ erts/emulator/beam/beam_load.c | 11 +++++----- erts/emulator/beam/copy.c | 29 +++++++++++++++++++------- erts/emulator/beam/erl_alloc.h | 20 ++++++++++++------ erts/emulator/beam/erl_bif_info.c | 17 +++++++++++++++ erts/emulator/beam/erl_gc.c | 42 ++++++++++++++++++++++++++++++++++--- erts/emulator/beam/erl_gc.h | 8 +++---- erts/emulator/beam/erl_message.c | 8 +++---- erts/emulator/beam/erl_term.c | 31 +++++++++++++++++++++++++++ erts/emulator/beam/erl_term.h | 44 ++++++++++++++++++++++++++++++++++++--- erts/emulator/beam/global.h | 25 ++++++++++++++++++++-- erts/emulator/beam/sys.h | 7 +++---- erts/emulator/hipe/hipe_bif0.c | 2 ++ 14 files changed, 209 insertions(+), 40 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 25256bcac9..a93d1af0bf 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3702,7 +3702,7 @@ dnl crypto # #-------------------------------------------------------------------- -DED_SYS_INCLUDE="-I${ERL_TOP}/erts/emulator/beam -I${ERL_TOP}/erts/include -I${ERL_TOP}/erts/include/$host -I${ERL_TOP}/erts/include/internal -I${ERL_TOP}/erts/include/internal/$host -I${ERL_TOP}/erts/emulator/sys/$ERLANG_OSTYPE" +DED_SYS_INCLUDE="-I${ERL_TOP}/erts/emulator/beam -I${ERL_TOP}/erts/include -I${ERL_TOP}/erts/include/$host -I${ERL_TOP}/erts/include/internal -I${ERL_TOP}/erts/include/internal/$host -I${ERL_TOP}/erts/emulator/sys/$ERLANG_OSTYPE -I${ERL_TOP}/erts/emulator/sys/common" if test "X$ETHR_DEFS" = "X"; then DED_THR_DEFS="-D_THREAD_SAFE -D_REENTRANT" diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 6e4c8ecee3..4d19f52a52 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2963,6 +2963,9 @@ do { \ } } Op1 = small_to_big(ires, tmp_big); +#ifdef TAG_LITERAL_PTR + Op1 |= TAG_LITERAL_PTR; +#endif big_shift: if (i > 0) { /* Left shift. */ diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 1c598601c6..7a1a563be2 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4439,12 +4439,13 @@ freeze_code(LoaderState* stp) code_hdr->literals_start = ptr; code_hdr->literals_end = ptr + stp->total_literal_size; for (i = 0; i < stp->num_literals; i++) { - if (stp->literals[i].heap_frags) { - move_multi_frags(&ptr, &code_off_heap, stp->literals[i].heap_frags, - &stp->literals[i].term, 1); - ASSERT(erts_is_literal(ptr_val(stp->literals[i].term))); + if (is_not_immed(stp->literals[i].term)) { + erts_move_multi_frags(&ptr, &code_off_heap, + stp->literals[i].heap_frags, + &stp->literals[i].term, 1, 1); + ASSERT(erts_is_literal(stp->literals[i].term, + ptr_val(stp->literals[i].term))); } - else ASSERT(is_immed(stp->literals[i].term)); } code_hdr->literals_off_heap = code_off_heap.first; lp = stp->literal_patches; diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index ec769c3b49..b185758b1d 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -35,7 +35,7 @@ #include "erl_bits.h" #include "dtrace-wrapper.h" -static void move_one_frag(Eterm** hpp, ErlHeapFragment*, ErlOffHeap*); +static void move_one_frag(Eterm** hpp, ErlHeapFragment*, ErlOffHeap*, int); /* * Copy object "obj" to process p. @@ -621,17 +621,24 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) * move markers. * Typically used to copy a multi-fragmented message (from NIF). */ -void move_multi_frags(Eterm** hpp, ErlOffHeap* off_heap, ErlHeapFragment* first, - Eterm* refs, unsigned nrefs) +void erts_move_multi_frags(Eterm** hpp, ErlOffHeap* off_heap, ErlHeapFragment* first, + Eterm* refs, unsigned nrefs, int literals) { ErlHeapFragment* bp; Eterm* hp_start = *hpp; Eterm* hp_end; Eterm* hp; unsigned i; + Eterm literal_tag; + +#ifdef TAG_LITERAL_PTR + literal_tag = (Eterm) literals ? TAG_LITERAL_PTR : 0; +#else + literal_tag = (Eterm) 0; +#endif for (bp=first; bp!=NULL; bp=bp->next) { - move_one_frag(hpp, bp, off_heap); + move_one_frag(hpp, bp, off_heap, literals); } hp_end = *hpp; for (hp=hp_start; hpmem; Eterm* end = ptr + frag->used_size; @@ -704,4 +718,3 @@ move_one_frag(Eterm** hpp, ErlHeapFragment* frag, ErlOffHeap* off_heap) OH_OVERHEAD(off_heap, frag->off_heap.overhead); frag->off_heap.first = NULL; } - diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index a0d8561e84..9da9c823f7 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -178,6 +178,12 @@ void sys_free(void *) __deprecated; /* erts_free() */ void *sys_alloc(Uint ) __deprecated; /* erts_alloc_fnf() */ void *sys_realloc(void *, Uint) __deprecated; /* erts_realloc_fnf() */ +#undef ERTS_HAVE_IS_IN_LITERAL_RANGE +#if defined(ARCH_32) || defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) +# define ERTS_HAVE_IS_IN_LITERAL_RANGE +#endif + + /* * erts_alloc[_fnf](), erts_realloc[_fnf](), erts_free() works as * malloc(), realloc(), and free() with the following exceptions: @@ -205,7 +211,9 @@ void erts_free(ErtsAlcType_t type, void *ptr); void *erts_alloc_fnf(ErtsAlcType_t type, Uint size); void *erts_realloc_fnf(ErtsAlcType_t type, void *ptr, Uint size); int erts_is_allctr_wrapper_prelocked(void); -int erts_is_literal(void* ptr); +#ifdef ERTS_HAVE_IS_IN_LITERAL_RANGE +int erts_is_in_literal_range(void* ptr); +#endif #endif /* #if !ERTS_ALC_DO_INLINE */ @@ -283,8 +291,10 @@ int erts_is_allctr_wrapper_prelocked(void) && !!erts_tsd_get(erts_allctr_prelock_tsd_key); /* by me */ } +#ifdef ERTS_HAVE_IS_IN_LITERAL_RANGE + ERTS_ALC_INLINE -int erts_is_literal(void* ptr) +int erts_is_in_literal_range(void* ptr) { #if defined(ARCH_32) Uint ix = (UWord)ptr >> ERTS_MMAP_SUPERALIGNED_BITS; @@ -293,18 +303,16 @@ int erts_is_literal(void* ptr) & ((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS)); #elif defined(ARCH_64) -# if defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) extern char* erts_literals_start; extern UWord erts_literals_size; return ErtsInArea(ptr, erts_literals_start, erts_literals_size); -# else -# error Do the tag thing -# endif #else # error No ARCH_xx #endif } +#endif /* ERTS_HAVE_IS_IN_LITERAL_RANGE */ + #endif /* #if ERTS_ALC_DO_INLINE || defined(ERTS_ALC_INTERNAL__) */ #define ERTS_ALC_GET_THR_IX() ((int) erts_get_scheduler_id()) diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index a73ad826db..a684c81445 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2766,6 +2766,20 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) else if (ERTS_IS_ATOM_STR("eager_check_io",BIF_ARG_1)) { BIF_RET(erts_eager_check_io ? am_true : am_false); } + else if (ERTS_IS_ATOM_STR("literal_test",BIF_ARG_1)) { +#ifdef ERTS_HAVE_IS_IN_LITERAL_RANGE +#ifdef ARCH_64 + DECL_AM(range); + BIF_RET(AM_range); +#else /* ARCH_32 */ + DECL_AM(range_bitmask); + BIF_RET(AM_range_bitmask); +#endif /* ARCH_32 */ +#else /* ! ERTS_HAVE_IS_IN_LITERAL_RANGE */ + DECL_AM(tag); + BIF_RET(AM_tag); +#endif + } BIF_ERROR(BIF_P, BADARG); } @@ -4323,12 +4337,15 @@ static void os_info_init(void) erts_free(ERTS_ALC_T_TMP, (void *) buf); hp = erts_alloc(ERTS_ALC_T_LITERAL, (3+4)*sizeof(Eterm)); os_type_tuple = TUPLE2(hp, type, flav); + erts_set_literal_tag(&os_type_tuple, hp, 3); + hp += 3; os_version(&major, &minor, &build); os_version_tuple = TUPLE3(hp, make_small(major), make_small(minor), make_small(build)); + erts_set_literal_tag(&os_version_tuple, hp, 4); } void diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 89fabde67c..e316ab95ab 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1004,6 +1004,8 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) Uint mature_size = (char *) HIGH_WATER(p) - heap; Eterm* old_htop = OLD_HTOP(p); Eterm* n_heap; + char* oh = (char *) OLD_HEAP(p); + Uint oh_size = (char *) OLD_HTOP(p) - oh; n_htop = n_heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*new_sz); @@ -1035,8 +1037,12 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) } else if (in_area(ptr, heap, mature_size)) { MOVE_BOXED(ptr,val,old_htop,g_ptr++); } else if (in_area(ptr, heap, heap_size)) { + ASSERT(!erts_is_literal(gval, ptr) + && !in_area(ptr, oh, oh_size)); MOVE_BOXED(ptr,val,n_htop,g_ptr++); } else { + ASSERT(erts_is_literal(gval, ptr) + || in_area(ptr, oh, oh_size)); g_ptr++; } break; @@ -1050,8 +1056,12 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) } else if (in_area(ptr, heap, mature_size)) { MOVE_CONS(ptr,val,old_htop,g_ptr++); } else if (in_area(ptr, heap, heap_size)) { + ASSERT(!erts_is_literal(gval, ptr) + && !in_area(ptr, oh, oh_size)); MOVE_CONS(ptr,val,n_htop,g_ptr++); } else { + ASSERT(erts_is_literal(gval, ptr) + || in_area(ptr, oh, oh_size)); g_ptr++; } break; @@ -1094,8 +1104,12 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) } else if (in_area(ptr, heap, mature_size)) { MOVE_BOXED(ptr,val,old_htop,n_hp++); } else if (in_area(ptr, heap, heap_size)) { + ASSERT(!erts_is_literal(gval, ptr) + && !in_area(ptr, oh, oh_size)); MOVE_BOXED(ptr,val,n_htop,n_hp++); } else { + ASSERT(erts_is_literal(gval, ptr) + || in_area(ptr, oh, oh_size)); n_hp++; } break; @@ -1108,8 +1122,12 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) } else if (in_area(ptr, heap, mature_size)) { MOVE_CONS(ptr,val,old_htop,n_hp++); } else if (in_area(ptr, heap, heap_size)) { + ASSERT(!erts_is_literal(gval, ptr) + && !in_area(ptr, oh, oh_size)); MOVE_CONS(ptr,val,n_htop,n_hp++); } else { + ASSERT(erts_is_literal(gval, ptr) + || in_area(ptr, oh, oh_size)); n_hp++; } break; @@ -1131,9 +1149,15 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) MOVE_BOXED(ptr,val,old_htop,origptr); mb->base = binary_bytes(mb->orig); } else if (in_area(ptr, heap, heap_size)) { + ASSERT(!erts_is_literal(*origptr, origptr) + && !in_area(ptr, oh, oh_size)); MOVE_BOXED(ptr,val,n_htop,origptr); mb->base = binary_bytes(mb->orig); } + else { + ASSERT(erts_is_literal(*origptr, origptr) + || in_area(ptr, oh, oh_size)); + } } n_hp += (thing_arityval(gval)+1); } @@ -1275,8 +1299,10 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) ASSERT(is_boxed(val)); *g_ptr++ = val; } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) { + ASSERT(!erts_is_literal(gval, ptr)); MOVE_BOXED(ptr,val,n_htop,g_ptr++); } else { + ASSERT(erts_is_literal(gval, ptr)); g_ptr++; } continue; @@ -1288,8 +1314,10 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) if (IS_MOVED_CONS(val)) { *g_ptr++ = ptr[1]; } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) { + ASSERT(!erts_is_literal(gval, ptr)); MOVE_CONS(ptr,val,n_htop,g_ptr++); } else { + ASSERT(erts_is_literal(gval, ptr)); g_ptr++; } continue; @@ -1330,8 +1358,10 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) ASSERT(is_boxed(val)); *n_hp++ = val; } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) { + ASSERT(!erts_is_literal(gval, ptr)); MOVE_BOXED(ptr,val,n_htop,n_hp++); } else { + ASSERT(erts_is_literal(gval, ptr)); n_hp++; } break; @@ -1342,8 +1372,10 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) if (IS_MOVED_CONS(val)) { *n_hp++ = ptr[1]; } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) { + ASSERT(!erts_is_literal(gval, ptr)); MOVE_CONS(ptr,val,n_htop,n_hp++); } else { + ASSERT(erts_is_literal(gval, ptr)); n_hp++; } break; @@ -1363,12 +1395,16 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) *origptr = val; mb->base = binary_bytes(*origptr); } else if (in_area(ptr, src, src_size) || - in_area(ptr, oh, oh_size)) { +- in_area(ptr, oh, oh_size)) { + ASSERT(!erts_is_literal(*origptr, origptr)); MOVE_BOXED(ptr,val,n_htop,origptr); mb->base = binary_bytes(*origptr); ptr = boxed_val(*origptr); val = *ptr; } + else { + ASSERT(erts_is_literal(*origptr, origptr)); + } } n_hp += (thing_arityval(gval)+1); } @@ -1985,7 +2021,7 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) } ASSERT((is_nil(p->seq_trace_token) || - is_tuple(follow_moved(p->seq_trace_token)) || + is_tuple(follow_moved(p->seq_trace_token, (Eterm) 0)) || is_atom(p->seq_trace_token))); if (is_not_immed(p->seq_trace_token)) { roots[n].v = &p->seq_trace_token; @@ -2003,7 +2039,7 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) is_internal_pid(ERTS_TRACER_PROC(p)) || is_internal_port(ERTS_TRACER_PROC(p))); - ASSERT(is_pid(follow_moved(p->group_leader))); + ASSERT(is_pid(follow_moved(p->group_leader, (Eterm) 0))); if (is_not_immed(p->group_leader)) { roots[n].v = &p->group_leader; roots[n].sz = 1; diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index ecd1bf4d22..fdbf948f9d 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -76,10 +76,10 @@ do { \ int within(Eterm *ptr, Process *p); #endif -ERTS_GLB_INLINE Eterm follow_moved(Eterm term); +ERTS_GLB_INLINE Eterm follow_moved(Eterm term, Eterm xptr_tag); #if ERTS_GLB_INLINE_INCL_FUNC_DEF -ERTS_GLB_INLINE Eterm follow_moved(Eterm term) +ERTS_GLB_INLINE Eterm follow_moved(Eterm term, Eterm xptr_tag) { Eterm* ptr; switch (primary_tag(term)) { @@ -87,11 +87,11 @@ ERTS_GLB_INLINE Eterm follow_moved(Eterm term) break; case TAG_PRIMARY_BOXED: ptr = boxed_val(term); - if (IS_MOVED_BOXED(*ptr)) term = *ptr; + if (IS_MOVED_BOXED(*ptr)) term = (*ptr) | xptr_tag; break; case TAG_PRIMARY_LIST: ptr = list_val(term); - if (IS_MOVED_CONS(ptr[0])) term = ptr[1]; + if (IS_MOVED_CONS(ptr[0])) term = (ptr[1]) | xptr_tag; break; default: ASSERT(!"strange tag in follow_moved"); diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index ef52823287..e23c79d301 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -642,13 +642,13 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg) #endif if (bp->next != NULL) { - move_multi_frags(hpp, off_heap, bp, msg->m, + erts_move_multi_frags(hpp, off_heap, bp, msg->m, #ifdef USE_VM_PROBES - 3 + 3, #else - 2 + 2, #endif - ); + 0); goto copy_done; } diff --git a/erts/emulator/beam/erl_term.c b/erts/emulator/beam/erl_term.c index 3a5fbcc284..8efbb8c554 100644 --- a/erts/emulator/beam/erl_term.c +++ b/erts/emulator/beam/erl_term.c @@ -28,6 +28,37 @@ #include #include +void +erts_set_literal_tag(Eterm *term, Eterm *hp_start, Eterm hsz) +{ +#ifdef TAG_LITERAL_PTR + Eterm *hp_end, *hp; + + hp_end = hp_start + hsz; + hp = hp_start; + + while (hp < hp_end) { + switch (primary_tag(*hp)) { + case TAG_PRIMARY_BOXED: + case TAG_PRIMARY_LIST: + *hp |= TAG_LITERAL_PTR; + break; + case TAG_PRIMARY_HEADER: + if (header_is_thing(*hp)) { + hp += thing_arityval(*hp); + } + break; + default: + break; + } + + hp++; + } + if (is_boxed(*term) || is_list(*term)) + *term |= TAG_LITERAL_PTR; +#endif +} + __decl_noreturn static void __noreturn et_abort(const char *expr, const char *file, unsigned line) { diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 089eecf024..a80716fb0f 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -21,6 +21,8 @@ #ifndef __ERL_TERM_H #define __ERL_TERM_H +#include "erl_mmap.h" + typedef UWord Wterm; /* Full word terms */ struct erl_node_; /* Declared in erl_node_tables.h */ @@ -48,6 +50,24 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #define _ET_APPLY(F,X) _unchecked_##F(X) #endif +#if defined(ARCH_64) +# define TAG_PTR_MASK__ 0x7 +# if !defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) +# ifdef HIPE +# error Hipe on 64-bit needs a real mmap as it does not support the literal tag +# endif +# define TAG_LITERAL_PTR 0x4 +# else +# undef TAG_LITERAL_PTR +# endif +#elif defined(ARCH_32) +# define TAG_PTR_MASK__ 0x3 +# undef TAG_LITERAL_PTR +#else +# error Not supported arch +#endif + + #define _TAG_PRIMARY_SIZE 2 #define _TAG_PRIMARY_MASK 0x3 #define TAG_PRIMARY_HEADER 0x0 @@ -165,10 +185,11 @@ struct erl_node_; /* Declared in erl_node_tables.h */ /* boxed object access methods */ -#define _is_taggable_pointer(x) (((Uint)(x) & 0x3) == 0) +#define _is_taggable_pointer(x) (((Uint)(x) & TAG_PTR_MASK__) == 0) + #define _boxed_precond(x) (is_boxed(x)) -#define _is_aligned(x) (((Uint)(x) & 0x3) == 0) +#define _is_aligned(x) (((Uint)(x) & TAG_PTR_MASK__) == 0) #define _unchecked_make_boxed(x) ((Uint)(x) + TAG_PRIMARY_BOXED) _ET_DECLARE_CHECKED(Eterm,make_boxed,const Eterm*) #define make_boxed(x) _ET_APPLY(make_boxed,(x)) @@ -180,7 +201,11 @@ _ET_DECLARE_CHECKED(int,is_boxed,Eterm) #else #define is_boxed(x) (((x) & _TAG_PRIMARY_MASK) == TAG_PRIMARY_BOXED) #endif +#ifdef TAG_LITERAL_PTR +#define _unchecked_boxed_val(x) _unchecked_ptr_val(x) +#else #define _unchecked_boxed_val(x) ((Eterm*) ((x) - TAG_PRIMARY_BOXED)) +#endif _ET_DECLARE_CHECKED(Eterm*,boxed_val,Wterm) #define boxed_val(x) _ET_APPLY(boxed_val,(x)) @@ -198,7 +223,11 @@ _ET_DECLARE_CHECKED(int,is_not_list,Eterm) #define is_not_list(x) (!is_list((x))) #endif #define _list_precond(x) (is_list(x)) +#ifdef TAG_LITERAL_PTR +#define _unchecked_list_val(x) _unchecked_ptr_val(x) +#else #define _unchecked_list_val(x) ((Eterm*) ((x) - TAG_PRIMARY_LIST)) +#endif _ET_DECLARE_CHECKED(Eterm*,list_val,Wterm) #define list_val(x) _ET_APPLY(list_val,(x)) @@ -209,13 +238,20 @@ _ET_DECLARE_CHECKED(Eterm*,list_val,Wterm) #define CDR(x) ((x)[1]) /* generic tagged pointer (boxed or list) access methods */ -#define _unchecked_ptr_val(x) ((Eterm*) ((x) & ~((Uint) 0x3))) +#define _unchecked_ptr_val(x) ((Eterm*) ((x) & ~((Uint) TAG_PTR_MASK__))) #define ptr_val(x) _unchecked_ptr_val((x)) /*XXX*/ #define _unchecked_offset_ptr(x,offs) ((x)+((offs)*sizeof(Eterm))) #define offset_ptr(x,offs) _unchecked_offset_ptr(x,offs) /*XXX*/ #define _unchecked_byte_offset_ptr(x,byte_offs) ((x)+(offs)) #define byte_offset_ptr(x,offs) _unchecked_byte_offset_ptr(x,offs) /*XXX*/ +#ifdef TAG_LITERAL_PTR +#define _unchecked_is_not_literal_ptr(x) (!((x) & TAG_LITERAL_PTR)) +#define is_not_literal_ptr(x) _unchecked_is_not_literal_ptr((x)) /*XXX*/ +#define is_literal_ptr(x) (!is_not_literal_ptr((x))) /*XXX*/ +#endif + + /* fixnum ("small") access methods */ #if defined(ARCH_64) #define SMALL_BITS (64-4) @@ -1113,5 +1149,7 @@ extern unsigned tag_val_def(Wterm); #define is_same(A,B) ((A)==(B)) +void erts_set_literal_tag(Eterm *term, Eterm *hp_start, Eterm hsz); + #endif /* __ERL_TERM_H */ diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index b4d02dd1dd..052994b972 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -961,8 +961,8 @@ Uint size_object(Eterm); Eterm copy_struct(Eterm, Uint, Eterm**, ErlOffHeap*); Eterm copy_shallow(Eterm*, Uint, Eterm**, ErlOffHeap*); -void move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first, - Eterm* refs, unsigned nrefs); +void erts_move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first, + Eterm* refs, unsigned nrefs, int literals); /* Utilities */ extern void erts_delete_nodes_monitors(Process *, ErtsProcLocks); @@ -1274,6 +1274,27 @@ int erts_print_system_version(int to, void *arg, Process *c_p); int erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* reg); +ERTS_GLB_INLINE int erts_is_literal(Eterm tptr, Eterm *ptr); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE int erts_is_literal(Eterm tptr, Eterm *ptr) +{ + ASSERT(is_boxed(tptr) || is_list(tptr)); + ASSERT(ptr == ptr_val(tptr)); + +#if defined(ERTS_HAVE_IS_IN_LITERAL_RANGE) + return erts_is_in_literal_range(ptr); +#elif defined(TAG_LITERAL_PTR) + return is_literal_ptr(tptr); +#else +# error Not able to detect literals... +#endif + +} + +#endif + /* ** Call_trace uses this API for the parameter matching functions */ diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 46577bd07f..d63e81cb5e 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -72,6 +72,9 @@ #define ERTS_I64_LITERAL(X) X##LL +#define ErtsInArea(ptr,start,nbytes) \ + ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes)) + #if defined (__WIN32__) # include "erl_win_sys.h" #else @@ -1044,10 +1047,6 @@ extern int erts_use_kernel_poll; #define put_int8(i, s) do {((unsigned char*)(s))[0] = (i) & 0xff;} while (0) - -#define ErtsInArea(PTR,START,NBYTES) \ - ((UWord)((char*)(PTR) - (char*)(START)) < (NBYTES)) - /* * Use DEBUGF as you would use printf, but use double parentheses: * diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index c6ea8a5132..00936b6b8a 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -497,6 +497,8 @@ static void *const_term_alloc(void *tmpl) hp = &p->mem[0]; p->val = copy_struct(obj, size, &hp, &const_term_table_off_heap); + erts_set_literal_tag(&p->val, &p->mem[0], size); + return &p->bucket; } -- cgit v1.2.3 From b9caedf093d0ccf268562656e28cdda6a02631cb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 2 Oct 2015 19:05:57 +0200 Subject: erts: Refactor header of loaded beam code to use a real C struct instead of array. --- erts/emulator/beam/beam_bif_load.c | 75 ++++---- erts/emulator/beam/beam_bp.c | 30 +-- erts/emulator/beam/beam_debug.c | 8 +- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/beam_load.c | 384 +++++++++++++++++++------------------ erts/emulator/beam/beam_load.h | 112 +++++------ erts/emulator/beam/beam_ranges.c | 17 +- erts/emulator/beam/break.c | 30 +-- erts/emulator/beam/erl_nif.c | 16 +- erts/emulator/beam/module.c | 4 +- erts/emulator/beam/module.h | 2 +- erts/emulator/hipe/hipe_bif0.c | 8 +- 12 files changed, 349 insertions(+), 339 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index adc3bd37d9..2c275c4649 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -40,7 +40,7 @@ static void set_default_trace_pattern(Eterm module); static Eterm check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp); static void delete_code(Module* modp); -static void decrement_refc(BeamInstr* code); +static void decrement_refc(BeamCodeHeader*); 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); @@ -58,8 +58,8 @@ BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1) return am_undefined; } erts_rlock_old_code(code_ix); - res = (erts_is_module_native(modp->curr.code) || - erts_is_module_native(modp->old.code)) ? + res = (erts_is_module_native(modp->curr.code_hdr) || + erts_is_module_native(modp->old.code_hdr)) ? am_true : am_false; erts_runlock_old_code(code_ix); return res; @@ -81,7 +81,7 @@ BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3) modp = erts_get_module(BIF_ARG_1, erts_active_code_ix()); if (modp && modp->curr.num_breakpoints > 0) { - ASSERT(modp->curr.code != NULL); + ASSERT(modp->curr.code_hdr != NULL); erts_clear_module_break(modp); ASSERT(modp->curr.num_breakpoints == 0); } @@ -281,7 +281,7 @@ finish_loading_1(BIF_ALIST_1) exceptions = 0; for (i = 0; i < n; i++) { p[i].exception = 0; - if (p[i].modp->curr.code && p[i].modp->old.code) { + if (p[i].modp->curr.code_hdr && p[i].modp->old.code_hdr) { p[i].exception = 1; exceptions++; } @@ -417,7 +417,7 @@ check_old_code_1(BIF_ALIST_1) modp = erts_get_module(BIF_ARG_1, code_ix); if (modp != NULL) { erts_rlock_old_code(code_ix); - if (modp->old.code != NULL) { + if (modp->old.code_hdr) { res = am_true; } erts_runlock_old_code(code_ix); @@ -441,7 +441,7 @@ erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp) if (!modp) return am_false; erts_rlock_old_code(code_ix); - res = modp->old.code ? check_process_code(c_p, modp, allow_gc, redsp) : am_false; + res = modp->old.code_hdr ? check_process_code(c_p, modp, allow_gc, redsp) : am_false; erts_runlock_old_code(code_ix); return res; @@ -525,7 +525,7 @@ BIF_RETTYPE delete_module_1(BIF_ALIST_1) if (!modp) { res = am_undefined; } - else if (modp->old.code != 0) { + else if (modp->old.code_hdr) { erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); erts_dsprintf(dsbufp, "Module %T must be purged before loading\n", BIF_ARG_1); @@ -563,8 +563,8 @@ BIF_RETTYPE module_loaded_1(BIF_ALIST_1) } code_ix = erts_active_code_ix(); if ((modp = erts_get_module(BIF_ARG_1, code_ix)) != NULL) { - if (modp->curr.code != NULL - && modp->curr.code[MI_ON_LOAD_FUNCTION_PTR] == 0) { + if (modp->curr.code_hdr + && modp->curr.code_hdr->on_load_function_ptr == NULL) { res = am_true; } } @@ -611,8 +611,8 @@ BIF_RETTYPE call_on_load_function_1(BIF_ALIST_1) { Module* modp = erts_get_module(BIF_ARG_1, erts_active_code_ix()); - if (modp && modp->curr.code) { - BIF_TRAP_CODE_PTR_0(BIF_P, modp->curr.code[MI_ON_LOAD_FUNCTION_PTR]); + if (modp && modp->curr.code_hdr) { + BIF_TRAP_CODE_PTR_0(BIF_P, modp->curr.code_hdr->on_load_function_ptr); } else { BIF_ERROR(BIF_P, BADARG); @@ -623,7 +623,6 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) { ErtsCodeIndex code_ix; Module* modp; - Eterm on_load; if (!erts_try_seize_code_write_permission(BIF_P)) { ERTS_BIF_YIELD2(bif_export[BIF_finish_after_on_load_2], @@ -638,14 +637,14 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) code_ix = erts_active_code_ix(); modp = erts_get_module(BIF_ARG_1, code_ix); - if (!modp || modp->curr.code == 0) { + if (!modp || !modp->curr.code_hdr) { error: erts_smp_thr_progress_unblock(); erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_release_code_write_permission(); BIF_ERROR(BIF_P, BADARG); } - if ((on_load = modp->curr.code[MI_ON_LOAD_FUNCTION_PTR]) == 0) { + if (modp->curr.code_hdr->on_load_function_ptr == NULL) { goto error; } if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) { @@ -667,7 +666,7 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) ep->code[4] = 0; } } - modp->curr.code[MI_ON_LOAD_FUNCTION_PTR] = 0; + modp->curr.code_hdr->on_load_function_ptr = NULL; set_default_trace_pattern(BIF_ARG_1); } else if (BIF_ARG_2 == am_false) { BeamInstr* code; @@ -679,16 +678,16 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) * the current code; the old code is not touched. */ erts_total_code_size -= modp->curr.code_length; - code = modp->curr.code; - end = (BeamInstr *)((char *)code + modp->curr.code_length); + code = (BeamInstr*) modp->curr.code_hdr; + end = (BeamInstr *) ((char *)code + modp->curr.code_length); erts_cleanup_funs_on_purge(code, end); beam_catches_delmod(modp->curr.catches, code, modp->curr.code_length, erts_active_code_ix()); - if (code[MI_LITERALS_START]) { - erts_free(ERTS_ALC_T_LITERAL, (void *) code[MI_LITERALS_START]); + if (modp->curr.code_hdr->literals_start) { + erts_free(ERTS_ALC_T_LITERAL, modp->curr.code_hdr->literals_start); } - erts_free(ERTS_ALC_T_CODE, (void *) code); - modp->curr.code = NULL; + erts_free(ERTS_ALC_T_CODE, modp->curr.code_hdr); + modp->curr.code_hdr = NULL; modp->curr.code_length = 0; modp->curr.catches = BEAM_CATCHES_NIL; erts_remove_from_ranges(code); @@ -740,7 +739,7 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) /* * Pick up limits for the module. */ - start = modp->old.code; + start = (BeamInstr*) modp->old.code_hdr; end = (BeamInstr *)((char *)start + modp->old.code_length); /* @@ -832,8 +831,8 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) * See if there are constants inside the module referenced by the process. */ done_gc = 0; - literals = (char*) modp->old.code[MI_LITERALS_START]; - lit_bsize = (char*) modp->old.code[MI_LITERALS_END] - literals; + literals = (char*) modp->old.code_hdr->literals_start; + lit_bsize = (char*) modp->old.code_hdr->literals_end - literals; for (;;) { ErlMessage* mp; @@ -888,8 +887,7 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) done_gc = 1; FLAGS(rp) |= F_NEED_FULLSWEEP; *redsp += erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); - oh = (struct erl_off_heap_header *) - modp->old.code[MI_LITERALS_OFF_HEAP]; + oh = modp->old.code_hdr->literals_off_heap; *redsp += lit_bsize / 64; /* Need, better value... */ erts_garbage_collect_literals(rp, (Eterm*)literals, lit_bsize, oh); } @@ -989,7 +987,7 @@ BIF_RETTYPE purge_module_1(BIF_ALIST_1) /* * Any code to purge? */ - if (modp->old.code == 0) { + if (!modp->old.code_hdr) { ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG); } else { @@ -1012,17 +1010,17 @@ BIF_RETTYPE purge_module_1(BIF_ALIST_1) */ ASSERT(erts_total_code_size >= modp->old.code_length); erts_total_code_size -= modp->old.code_length; - code = modp->old.code; + code = (BeamInstr*) modp->old.code_hdr; 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); - if (code[MI_LITERALS_START]) { - erts_free(ERTS_ALC_T_LITERAL, (void *) code[MI_LITERALS_START]); + decrement_refc(modp->old.code_hdr); + if (modp->old.code_hdr->literals_start) { + erts_free(ERTS_ALC_T_LITERAL, modp->old.code_hdr->literals_start); } erts_free(ERTS_ALC_T_CODE, (void *) code); - modp->old.code = NULL; + modp->old.code_hdr = NULL; modp->old.code_length = 0; modp->old.catches = BEAM_CATCHES_NIL; erts_remove_from_ranges(code); @@ -1039,10 +1037,9 @@ BIF_RETTYPE purge_module_1(BIF_ALIST_1) } static void -decrement_refc(BeamInstr* code) +decrement_refc(BeamCodeHeader* code_hdr) { - struct erl_off_heap_header* oh = - (struct erl_off_heap_header *) code[MI_LITERALS_OFF_HEAP]; + struct erl_off_heap_header* oh = code_hdr->literals_off_heap; while (oh) { Binary* bptr; @@ -1091,7 +1088,7 @@ delete_code(Module* modp) ASSERT(modp->curr.num_breakpoints == 0); ASSERT(modp->curr.num_traced_exports == 0); modp->old = modp->curr; - modp->curr.code = NULL; + modp->curr.code_hdr = NULL; modp->curr.code_length = 0; modp->curr.catches = BEAM_CATCHES_NIL; modp->curr.nif = NULL; @@ -1108,9 +1105,9 @@ beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks, Eterm module) * if not, delete old code; error if old code already exists. */ - if (modp->curr.code != NULL && modp->old.code != NULL) { + if (modp->curr.code_hdr && modp->old.code_hdr) { return am_not_purged; - } else if (modp->old.code == NULL) { /* Make the current version old. */ + } else if (!modp->old.code_hdr) { /* Make the current version old. */ delete_code(modp); } return NIL; diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 016d0aaa32..2a8663d7ee 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -152,8 +152,8 @@ erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified) num_modules = 0; for (current = 0; current < max_modules; current++) { modp = module_code(current, code_ix); - if (modp->curr.code) { - max_funcs += modp->curr.code[MI_NUM_FUNCTIONS]; + if (modp->curr.code_hdr) { + max_funcs += modp->curr.code_hdr->num_functions; module[num_modules++] = modp; } } @@ -161,9 +161,9 @@ erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified) f->matching = (BpFunction *) Alloc(max_funcs*sizeof(BpFunction)); i = 0; for (current = 0; current < num_modules; current++) { - BeamInstr** code_base = (BeamInstr **) module[current]->curr.code; + BeamCodeHeader* code_hdr = module[current]->curr.code_hdr; BeamInstr* code; - Uint num_functions = (Uint)(UWord) code_base[MI_NUM_FUNCTIONS]; + Uint num_functions = (Uint)(UWord) code_hdr->num_functions; Uint fi; if (specified > 0) { @@ -177,7 +177,7 @@ erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified) BeamInstr* pc; int wi; - code = code_base[MI_FUNCTIONS+fi]; + code = code_hdr->functions[fi]; ASSERT(code[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); pc = code+5; if (erts_is_native_break(pc)) { @@ -547,21 +547,21 @@ erts_clear_all_breaks(BpFunctions* f) int erts_clear_module_break(Module *modp) { - BeamInstr** code_base; + BeamCodeHeader* code_hdr; Uint n; Uint i; ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); ASSERT(modp); - code_base = (BeamInstr **) modp->curr.code; - if (code_base == NULL) { + code_hdr = modp->curr.code_hdr; + if (!code_hdr) { return 0; } - n = (Uint)(UWord) code_base[MI_NUM_FUNCTIONS]; + n = (Uint)(UWord) code_hdr->num_functions; for (i = 0; i < n; ++i) { BeamInstr* pc; - pc = code_base[MI_FUNCTIONS+i] + 5; + pc = code_hdr->functions[i] + 5; if (erts_is_native_break(pc)) { continue; } @@ -573,7 +573,7 @@ erts_clear_module_break(Module *modp) { for (i = 0; i < n; ++i) { BeamInstr* pc; - pc = code_base[MI_FUNCTIONS+i] + 5; + pc = code_hdr->functions[i] + 5; if (erts_is_native_break(pc)) { continue; } @@ -1204,17 +1204,17 @@ int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) { BeamInstr * erts_find_local_func(Eterm mfa[3]) { Module *modp; - BeamInstr** code_base; + BeamCodeHeader* code_hdr; BeamInstr* code_ptr; Uint i,n; if ((modp = erts_get_module(mfa[0], erts_active_code_ix())) == NULL) return NULL; - if ((code_base = (BeamInstr **) modp->curr.code) == NULL) + if ((code_hdr = modp->curr.code_hdr) == NULL) return NULL; - n = (BeamInstr) code_base[MI_NUM_FUNCTIONS]; + n = (BeamInstr) code_hdr->num_functions; for (i = 0; i < n; ++i) { - code_ptr = code_base[MI_FUNCTIONS+i]; + code_ptr = code_hdr->functions[i]; ASSERT(((BeamInstr) BeamOp(op_i_func_info_IaaI)) == code_ptr[0]); ASSERT(mfa[0] == ((Eterm) code_ptr[2]) || is_nil((Eterm) code_ptr[2])); diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 3a1d328bbd..e961989fca 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -208,7 +208,7 @@ erts_debug_disassemble_1(BIF_ALIST_1) Eterm bin; Eterm mfa; BeamInstr* funcinfo = NULL; /* Initialized to eliminate warning. */ - BeamInstr* code_base; + BeamCodeHeader* code_hdr; BeamInstr* code_ptr = NULL; /* Initialized to eliminate warning. */ BeamInstr instr; BeamInstr uaddr; @@ -258,12 +258,12 @@ erts_debug_disassemble_1(BIF_ALIST_1) */ code_ptr = ((BeamInstr *) ep->addressv[code_ix]) - 5; funcinfo = code_ptr+2; - } else if (modp == NULL || (code_base = modp->curr.code) == NULL) { + } else if (modp == NULL || (code_hdr = modp->curr.code_hdr) == NULL) { BIF_RET(am_undef); } else { - n = code_base[MI_NUM_FUNCTIONS]; + n = code_hdr->num_functions; for (i = 0; i < n; i++) { - code_ptr = (BeamInstr *) code_base[MI_FUNCTIONS+i]; + code_ptr = code_hdr->functions[i]; if (code_ptr[3] == name && code_ptr[4] == arity) { funcinfo = code_ptr+2; break; diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index a42ce1c9f1..6e4c8ecee3 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6080,7 +6080,7 @@ call_fun(Process* p, /* Current process. */ */ module = fe->module; if ((modp = erts_get_module(module, code_ix)) != NULL - && modp->curr.code != NULL) { + && modp->curr.code_hdr != NULL) { /* * There is a module loaded, but obviously the fun is not * defined in it. We must not call the error_handler diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index a846e96204..c56e6732a0 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -80,7 +80,7 @@ ErlDrvBinary* erts_gzinflate_buffer(char*, int); typedef struct { Uint value; /* Value of label (NULL if not known yet). */ - Uint patches; /* Index (into code buffer) to first location + Sint patches; /* Index (into code buffer) to first location * which must be patched with the value of this label. */ #ifdef ERTS_SMP @@ -284,9 +284,10 @@ typedef struct LoaderState { int specific_op; /* Specific opcode (-1 if not found). */ int num_functions; /* Number of functions in module. */ int num_labels; /* Number of labels. */ - int code_buffer_size; /* Size of code buffer in words. */ - BeamInstr* code; /* Loaded code. */ - int ci; /* Current index into loaded code. */ + BeamCodeHeader* hdr; /* Loaded code header */ + BeamInstr* codev; /* Loaded code buffer */ + int codev_size; /* Size of code buffer in words. */ + int ci; /* Current index into loaded code buffer. */ Label* labels; StringPatch* string_patches; /* Linked list of position into string table to patch. */ BeamInstr catches; /* Linked list of catch_yf instructions. */ @@ -480,7 +481,7 @@ static void free_literal_fragment(ErlHeapFragment*); static void loader_state_dtor(Binary* magic); static Eterm insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, Eterm group_leader, Eterm module, - BeamInstr* code, Uint size); + BeamCodeHeader* code, Uint size); static int init_iff_file(LoaderState* stp, byte* code, Uint size); static int scan_iff_file(LoaderState* stp, Uint* chunk_types, Uint num_types, Uint num_mandatory); @@ -526,15 +527,15 @@ static void new_string_patch(LoaderState* stp, int pos); static Uint new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size); static int genopargcompare(GenOpArg* a, GenOpArg* b); static Eterm get_module_info(Process* p, ErtsCodeIndex code_ix, - BeamInstr* code, Eterm module, Eterm what); + BeamCodeHeader*, Eterm module, Eterm what); static Eterm exported_from_module(Process* p, ErtsCodeIndex code_ix, Eterm mod); -static Eterm functions_in_module(Process* p, BeamInstr* code); -static Eterm attributes_for_module(Process* p, BeamInstr* code); -static Eterm compilation_info_for_module(Process* p, BeamInstr* code); -static Eterm md5_of_module(Process* p, BeamInstr* code); -static Eterm has_native(BeamInstr* code); -static Eterm native_addresses(Process* p, BeamInstr* code); +static Eterm functions_in_module(Process* p, BeamCodeHeader*); +static Eterm attributes_for_module(Process* p, BeamCodeHeader*); +static Eterm compilation_info_for_module(Process* p, BeamCodeHeader*); +static Eterm md5_of_module(Process* p, BeamCodeHeader*); +static Eterm has_native(BeamCodeHeader*); +static Eterm native_addresses(Process* p, BeamCodeHeader*); int patch_funentries(Eterm Patchlist); int patch(Eterm Addresses, Uint fe); static int safe_mul(UWord a, UWord b, UWord* resp); @@ -601,6 +602,7 @@ extern void check_allocated_block(Uint type, void *blk); #define CHKBLK(TYPE,BLK) /* nothing */ #endif + Eterm erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader, Eterm* modp, byte* code, Uint unloaded_size) @@ -641,21 +643,27 @@ erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader, /* * Initialize code area. */ - stp->code_buffer_size = 2048 + stp->num_functions; - stp->code = (BeamInstr *) erts_alloc(ERTS_ALC_T_CODE, - sizeof(BeamInstr) * stp->code_buffer_size); - - stp->code[MI_NUM_FUNCTIONS] = stp->num_functions; - stp->ci = MI_FUNCTIONS + stp->num_functions + 1; - - stp->code[MI_ATTR_PTR] = 0; - stp->code[MI_ATTR_SIZE] = 0; - stp->code[MI_ATTR_SIZE_ON_HEAP] = 0; - stp->code[MI_COMPILE_PTR] = 0; - stp->code[MI_COMPILE_SIZE] = 0; - stp->code[MI_COMPILE_SIZE_ON_HEAP] = 0; - stp->code[MI_LITERALS_START] = 0; - stp->code[MI_MD5_PTR] = 0; + stp->codev_size = 2048 + stp->num_functions; + stp->hdr = (BeamCodeHeader*) erts_alloc(ERTS_ALC_T_CODE, + (offsetof(BeamCodeHeader,functions) + + sizeof(BeamInstr) * stp->codev_size)); + + stp->hdr->num_functions = stp->num_functions; + + /* Let the codev array start at functions[0] in order to index + * both function pointers and the loaded code itself that follows. + */ + stp->codev = (BeamInstr*) &stp->hdr->functions; + stp->ci = stp->num_functions + 1; + + stp->hdr->attr_ptr = NULL; + stp->hdr->attr_size = 0; + stp->hdr->attr_size_on_heap = 0; + stp->hdr->compile_ptr = NULL; + stp->hdr->compile_size = 0; + stp->hdr->compile_size_on_heap = 0; + stp->hdr->literals_start = NULL; + stp->hdr->md5_ptr = NULL; /* * Read the atom table. @@ -777,7 +785,7 @@ erts_finish_loading(Binary* magic, Process* c_p, CHKBLK(ERTS_ALC_T_CODE,stp->code); retval = insert_new_code(c_p, c_p_locks, stp->group_leader, stp->module, - stp->code, stp->loaded_size); + stp->hdr, stp->loaded_size); if (retval != NIL) { goto load_error; } @@ -800,7 +808,8 @@ erts_finish_loading(Binary* magic, Process* c_p, debug_dump_code(stp->code,stp->ci); #endif #endif - stp->code = NULL; /* Prevent code from being freed. */ + stp->hdr = NULL; /* Prevent code from being freed. */ + stp->codev = NULL; *modp = stp->module; /* @@ -832,7 +841,8 @@ erts_alloc_loader_state(void) stp->specific_op = -1; stp->genop = NULL; stp->atom = NULL; - stp->code = NULL; + stp->hdr = NULL; + stp->codev = NULL; stp->labels = NULL; stp->import = NULL; stp->export = NULL; @@ -871,7 +881,7 @@ erts_module_for_prepared_code(Binary* magic) return NIL; } stp = ERTS_MAGIC_BIN_DATA(magic); - if (stp->code != 0) { + if (stp->hdr != 0) { return stp->module; } else { return NIL; @@ -921,12 +931,13 @@ loader_state_dtor(Binary* magic) driver_free_binary(stp->bin); stp->bin = 0; } - if (stp->code != 0) { - if (stp->code[MI_LITERALS_START]) { - erts_free(ERTS_ALC_T_LITERAL, (void*) stp->code[MI_LITERALS_START]); + if (stp->hdr != 0) { + if (stp->hdr->literals_start) { + erts_free(ERTS_ALC_T_LITERAL, stp->hdr->literals_start); } - erts_free(ERTS_ALC_T_CODE, stp->code); - stp->code = 0; + erts_free(ERTS_ALC_T_CODE, stp->hdr); + stp->hdr = 0; + stp->codev = 0; } if (stp->labels != 0) { erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->labels); @@ -999,7 +1010,7 @@ loader_state_dtor(Binary* magic) static Eterm insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, - Eterm group_leader, Eterm module, BeamInstr* code, + Eterm group_leader, Eterm module, BeamCodeHeader* code_hdr, Uint size) { Module* modp; @@ -1020,7 +1031,7 @@ insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, erts_total_code_size += size; modp = erts_put_module(module); - modp->curr.code = code; + modp->curr.code_hdr = code_hdr; modp->curr.code_length = size; modp->curr.catches = BEAM_CATCHES_NIL; /* Will be filled in later. */ @@ -1028,7 +1039,7 @@ insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, * Update ranges (used for finding a function from a PC value). */ - erts_update_ranges(code, size); + erts_update_ranges((BeamInstr*)modp->curr.code_hdr, size); return NIL; } @@ -1377,7 +1388,7 @@ read_export_table(LoaderState* stp) if (value == 0) { LoadError2(stp, "export table entry %d: label %d not resolved", i, n); } - stp->export[i].address = address = stp->code + value; + stp->export[i].address = address = stp->codev + value; /* * Find out if there is a BIF with the same name. @@ -1396,7 +1407,7 @@ read_export_table(LoaderState* stp) * any other functions that walk through all local functions. */ - if (stp->labels[n].patches) { + if (stp->labels[n].patches >= 0) { LoadError3(stp, "there are local calls to the stub for " "the BIF %T:%T/%d", stp->module, func, arity); @@ -1739,7 +1750,7 @@ read_code_header(LoaderState* stp) stp->num_labels * sizeof(Label)); for (i = 0; i < stp->num_labels; i++) { stp->labels[i].value = 0; - stp->labels[i].patches = 0; + stp->labels[i].patches = -1; #ifdef ERTS_SMP stp->labels[i].looprec_targeted = 0; #endif @@ -1758,13 +1769,14 @@ read_code_header(LoaderState* stp) } else {} #define CodeNeed(w) do { \ - ASSERT(ci <= code_buffer_size); \ - if (code_buffer_size < ci+(w)) { \ - code_buffer_size = 2*ci+(w); \ - stp->code = code = \ - (BeamInstr *) erts_realloc(ERTS_ALC_T_CODE, \ - (void *) code, \ - code_buffer_size * sizeof(BeamInstr)); \ + ASSERT(ci <= codev_size); \ + if (codev_size < ci+(w)) { \ + codev_size = 2*ci+(w); \ + stp->hdr = (BeamCodeHeader*) erts_realloc(ERTS_ALC_T_CODE, \ + (void *) stp->hdr, \ + (offsetof(BeamCodeHeader,functions) \ + + codev_size * sizeof(BeamInstr))); \ + code = stp->codev = (BeamInstr*) &stp->hdr->functions; \ } \ } while (0) @@ -1780,7 +1792,7 @@ load_code(LoaderState* stp) int arg; /* Number of current argument. */ int num_specific; /* Number of specific ops for current. */ BeamInstr* code; - int code_buffer_size; + int codev_size; int specific; Uint last_label = 0; /* Number of last label. */ Uint function_number = 0; @@ -1797,15 +1809,15 @@ load_code(LoaderState* stp) FUNC_INFO_SZ = 5 }; - code = stp->code; - code_buffer_size = stp->code_buffer_size; + code = stp->codev; + codev_size = stp->codev_size; ci = stp->ci; for (;;) { int new_op; GenOp* tmp_op; - ASSERT(ci <= code_buffer_size); + ASSERT(ci <= codev_size); get_next_instr: GetByte(stp, new_op); @@ -2420,8 +2432,7 @@ load_code(LoaderState* stp) switch (stp->specific_op) { case op_i_func_info_IaaI: { - Uint offset; - + Sint offset; if (function_number >= stp->num_functions) { LoadError1(stp, "too many functions in module (header said %d)", stp->num_functions); @@ -2463,15 +2474,15 @@ load_code(LoaderState* stp) stp->arity = code[ci-1]; ASSERT(stp->labels[last_label].value == ci - FUNC_INFO_SZ); - offset = MI_FUNCTIONS + function_number; - code[offset] = stp->labels[last_label].patches; + stp->hdr->functions[function_number] = (BeamInstr*) stp->labels[last_label].patches; + offset = function_number; stp->labels[last_label].patches = offset; function_number++; if (stp->arity > MAX_ARG) { LoadError1(stp, "too many arguments: %d", stp->arity); } #ifdef DEBUG - ASSERT(stp->labels[0].patches == 0); /* Should not be referenced. */ + ASSERT(stp->labels[0].patches < 0); /* Should not be referenced. */ for (i = 1; i < stp->num_labels; i++) { ASSERT(stp->labels[i].patches < ci); } @@ -2541,7 +2552,7 @@ load_code(LoaderState* stp) * End of code found. */ case op_int_code_end: - stp->code_buffer_size = code_buffer_size; + stp->codev_size = codev_size; stp->ci = ci; stp->function = THE_NON_VALUE; stp->genop = NULL; @@ -4351,7 +4362,8 @@ gen_has_map_fields(LoaderState* stp, GenOpArg Fail, GenOpArg Src, static int freeze_code(LoaderState* stp) { - BeamInstr* code = stp->code; + BeamCodeHeader* code_hdr = stp->hdr; + BeamInstr* codev = (BeamInstr*) &stp->hdr->functions; int i; byte* str_table; unsigned strtab_size = stp->chunks[STR_CHUNK].size; @@ -4380,53 +4392,50 @@ freeze_code(LoaderState* stp) (stp->current_li+1) + stp->num_fnames) * sizeof(Eterm) + (stp->current_li+1) * stp->loc_size; } - size = (stp->ci * sizeof(BeamInstr)) + + size = offsetof(BeamCodeHeader,functions) + (stp->ci * sizeof(BeamInstr)) + strtab_size + attr_size + compile_size + MD5_SIZE + line_size; /* * Move the code to its final location. */ - code = (BeamInstr *) erts_realloc(ERTS_ALC_T_CODE, (void *) code, size); - CHKBLK(ERTS_ALC_T_CODE,code); + code_hdr = (BeamCodeHeader*) erts_realloc(ERTS_ALC_T_CODE, (void *) code_hdr, size); + codev = (BeamInstr*) &code_hdr->functions; + CHKBLK(ERTS_ALC_T_CODE,code_hdr); /* * Place a pointer to the op_int_code_end instruction in the * function table in the beginning of the file. */ - code[MI_FUNCTIONS+stp->num_functions] = (BeamInstr) (code + stp->ci - 1); - CHKBLK(ERTS_ALC_T_CODE,code); + code_hdr->functions[stp->num_functions] = (codev + stp->ci - 1); + CHKBLK(ERTS_ALC_T_CODE,code_hdr); /* * Store the pointer to the on_load function. */ if (stp->on_load) { - code[MI_ON_LOAD_FUNCTION_PTR] = (BeamInstr) (code + stp->on_load); + code_hdr->on_load_function_ptr = codev + stp->on_load; } else { - code[MI_ON_LOAD_FUNCTION_PTR] = 0; + code_hdr->on_load_function_ptr = NULL; } - CHKBLK(ERTS_ALC_T_CODE,code); + CHKBLK(ERTS_ALC_T_CODE,code_hdr); /* * Place the literals in their own allocated heap (for fast range check) * and fix up all instructions that refer to it. */ { - Uint* ptr; - Uint* low; - Uint* high; + Eterm* ptr; LiteralPatch* lp; ErlOffHeap code_off_heap; ERTS_INIT_OFF_HEAP(&code_off_heap); - low = erts_alloc(ERTS_ALC_T_LITERAL, - stp->total_literal_size*sizeof(Eterm)); - high = low + stp->total_literal_size; - code[MI_LITERALS_START] = (BeamInstr) low; - code[MI_LITERALS_END] = (BeamInstr) high; - ptr = low; + ptr = (Eterm*)erts_alloc(ERTS_ALC_T_LITERAL, + stp->total_literal_size*sizeof(Eterm)); + code_hdr->literals_start = ptr; + code_hdr->literals_end = ptr + stp->total_literal_size; for (i = 0; i < stp->num_literals; i++) { if (stp->literals[i].heap_frags) { move_multi_frags(&ptr, &code_off_heap, stp->literals[i].heap_frags, @@ -4435,13 +4444,13 @@ freeze_code(LoaderState* stp) } else ASSERT(is_immed(stp->literals[i].term)); } - code[MI_LITERALS_OFF_HEAP] = (BeamInstr) code_off_heap.first; + code_hdr->literals_off_heap = code_off_heap.first; lp = stp->literal_patches; while (lp != 0) { BeamInstr* op_ptr; Literal* lit; - op_ptr = code + lp->pos; + op_ptr = codev + lp->pos; lit = &stp->literals[op_ptr[0]]; op_ptr[0] = lit->term; lp = lp->next; @@ -4453,16 +4462,16 @@ freeze_code(LoaderState* stp) * If there is line information, place it here. */ if (stp->line_instr == 0) { - code[MI_LINE_TABLE] = (BeamInstr) 0; - str_table = (byte *) (code+stp->ci); + code_hdr->line_table = NULL; + str_table = (byte *) (codev + stp->ci); } else { - Eterm* line_tab = (Eterm *) (code+stp->ci); + Eterm* line_tab = (Eterm *) (codev+stp->ci); Eterm* p; int ftab_size = stp->num_functions; int num_instrs = stp->current_li; Eterm* first_line_item; - code[MI_LINE_TABLE] = (BeamInstr) line_tab; + code_hdr->line_table = line_tab; p = line_tab + MI_LINE_FUNC_TAB; first_line_item = (p + ftab_size + 1); @@ -4472,9 +4481,9 @@ freeze_code(LoaderState* stp) *p++ = (Eterm) (BeamInstr) (first_line_item + num_instrs); ASSERT(p == first_line_item); for (i = 0; i < num_instrs; i++) { - *p++ = (Eterm) (BeamInstr) (code + stp->line_instr[i].pos); + *p++ = (Eterm) (BeamInstr) (codev + stp->line_instr[i].pos); } - *p++ = (Eterm) (BeamInstr) (code + stp->ci - 1); + *p++ = (Eterm) (BeamInstr) (codev + stp->ci - 1); line_tab[MI_LINE_FNAME_PTR] = (Eterm) (BeamInstr) p; memcpy(p, stp->fname, stp->num_fnames*sizeof(Eterm)); @@ -4509,13 +4518,13 @@ freeze_code(LoaderState* stp) if (attr_size) { byte* attr = str_table + strtab_size; sys_memcpy(attr, stp->chunks[ATTR_CHUNK].start, stp->chunks[ATTR_CHUNK].size); - code[MI_ATTR_PTR] = (BeamInstr) attr; - code[MI_ATTR_SIZE] = (BeamInstr) stp->chunks[ATTR_CHUNK].size; + code_hdr->attr_ptr = attr; + code_hdr->attr_size = (BeamInstr) stp->chunks[ATTR_CHUNK].size; decoded_size = erts_decode_ext_size(attr, attr_size); if (decoded_size < 0) { LoadError0(stp, "bad external term representation of module attributes"); } - code[MI_ATTR_SIZE_ON_HEAP] = decoded_size; + code_hdr->attr_size_on_heap = decoded_size; } CHKBLK(ERTS_ALC_T_CODE,code); if (compile_size) { @@ -4525,9 +4534,9 @@ freeze_code(LoaderState* stp) stp->chunks[COMPILE_CHUNK].size); CHKBLK(ERTS_ALC_T_CODE,code); - code[MI_COMPILE_PTR] = (BeamInstr) compile_info; + code_hdr->compile_ptr = compile_info; CHKBLK(ERTS_ALC_T_CODE,code); - code[MI_COMPILE_SIZE] = (BeamInstr) stp->chunks[COMPILE_CHUNK].size; + code_hdr->compile_size = (BeamInstr) stp->chunks[COMPILE_CHUNK].size; CHKBLK(ERTS_ALC_T_CODE,code); decoded_size = erts_decode_ext_size(compile_info, compile_size); CHKBLK(ERTS_ALC_T_CODE,code); @@ -4535,7 +4544,7 @@ freeze_code(LoaderState* stp) LoadError0(stp, "bad external term representation of compilation information"); } CHKBLK(ERTS_ALC_T_CODE,code); - code[MI_COMPILE_SIZE_ON_HEAP] = decoded_size; + code_hdr->compile_size_on_heap = decoded_size; } CHKBLK(ERTS_ALC_T_CODE,code); { @@ -4543,7 +4552,7 @@ freeze_code(LoaderState* stp) CHKBLK(ERTS_ALC_T_CODE,code); sys_memcpy(md5_sum, stp->mod_md5, MD5_SIZE); CHKBLK(ERTS_ALC_T_CODE,code); - code[MI_MD5_PTR] = (BeamInstr) md5_sum; + code_hdr->md5_ptr = md5_sum; CHKBLK(ERTS_ALC_T_CODE,code); } CHKBLK(ERTS_ALC_T_CODE,code); @@ -4552,7 +4561,7 @@ freeze_code(LoaderState* stp) * Make sure that we have not overflowed the allocated code space. */ ASSERT(str_table + strtab_size + attr_size + compile_size + MD5_SIZE == - ((byte *) code) + size); + ((byte *) code_hdr) + size); /* * Patch all instructions that refer to the string table. @@ -4564,46 +4573,47 @@ freeze_code(LoaderState* stp) BeamInstr* op_ptr; byte* strp; - op_ptr = code + sp->pos; + op_ptr = codev + sp->pos; strp = str_table + op_ptr[0]; op_ptr[0] = (BeamInstr) strp; sp = sp->next; } } - CHKBLK(ERTS_ALC_T_CODE,code); + CHKBLK(ERTS_ALC_T_CODE,code_hdr); /* * Resolve all labels. */ for (i = 0; i < stp->num_labels; i++) { - Uint this_patch; - Uint next_patch; + Sint this_patch; + Sint next_patch; Uint value = stp->labels[i].value; - if (value == 0 && stp->labels[i].patches != 0) { + if (value == 0 && stp->labels[i].patches >= 0) { LoadError1(stp, "label %d not resolved", i); } ASSERT(value < stp->ci); this_patch = stp->labels[i].patches; - while (this_patch != 0) { + while (this_patch >= 0) { ASSERT(this_patch < stp->ci); - next_patch = code[this_patch]; + next_patch = codev[this_patch]; ASSERT(next_patch < stp->ci); - code[this_patch] = (BeamInstr) (code + value); + codev[this_patch] = (BeamInstr) (codev + value); this_patch = next_patch; } } - CHKBLK(ERTS_ALC_T_CODE,code); + CHKBLK(ERTS_ALC_T_CODE,code_hdr); /* * Save the updated code pointer and code size. */ - stp->code = code; + stp->hdr = code_hdr; + stp->codev = codev; stp->loaded_size = size; - CHKBLK(ERTS_ALC_T_CODE,code); + CHKBLK(ERTS_ALC_T_CODE,code_hdr); return 1; load_error: @@ -4611,7 +4621,8 @@ freeze_code(LoaderState* stp) * Make sure that the caller frees the newly reallocated block, and * not the old one (in case it has moved). */ - stp->code = code; + stp->hdr = code_hdr; + stp->codev = codev; return 0; } @@ -4622,7 +4633,7 @@ final_touch(LoaderState* stp) int on_load = stp->on_load; unsigned catches; Uint index; - BeamInstr* code = stp->code; + BeamInstr* codev = stp->codev; Module* modp; /* @@ -4632,10 +4643,10 @@ final_touch(LoaderState* stp) index = stp->catches; catches = BEAM_CATCHES_NIL; while (index != 0) { - BeamInstr next = code[index]; - code[index] = BeamOpCode(op_catch_yf); - catches = beam_catches_cons((BeamInstr *)code[index+2], catches); - code[index+2] = make_catch(catches); + BeamInstr next = codev[index]; + codev[index] = BeamOpCode(op_catch_yf); + catches = beam_catches_cons((BeamInstr *)codev[index+2], catches); + codev[index+2] = make_catch(catches); index = next; } modp = erts_put_module(stp->module); @@ -4686,8 +4697,8 @@ final_touch(LoaderState* stp) current = stp->import[i].patches; while (current != 0) { ASSERT(current < stp->ci); - next = stp->code[current]; - stp->code[current] = import; + next = stp->codev[current]; + stp->codev[current] = import; current = next; } } @@ -4700,7 +4711,7 @@ final_touch(LoaderState* stp) for (i = 0; i < stp->num_lambdas; i++) { unsigned entry_label = stp->lambdas[i].label; ErlFunEntry* fe = stp->lambdas[i].fe; - BeamInstr* code_ptr = (BeamInstr *) (stp->code + stp->labels[entry_label].value); + BeamInstr* code_ptr = stp->codev + stp->labels[entry_label].value; if (fe->address[0] != 0) { /* @@ -5324,7 +5335,7 @@ new_label(LoaderState* stp) (void *) stp->labels, stp->num_labels * sizeof(Label)); stp->labels[num].value = 0; - stp->labels[num].patches = 0; + stp->labels[num].patches = -1; return num; } @@ -5384,7 +5395,7 @@ erts_module_info_0(Process* p, Eterm module) { Module* modp; ErtsCodeIndex code_ix = erts_active_code_ix(); - BeamInstr* code; + BeamCodeHeader* code_hdr; Eterm *hp; Eterm list = NIL; Eterm tup; @@ -5398,13 +5409,13 @@ erts_module_info_0(Process* p, Eterm module) return THE_NON_VALUE; } - code = modp->curr.code; - if (code == NULL) { + code_hdr = modp->curr.code_hdr; + if (code_hdr == NULL) { return THE_NON_VALUE; } #define BUILD_INFO(What) \ - tup = get_module_info(p, code_ix, code, module, What); \ + tup = get_module_info(p, code_ix, code_hdr, module, What); \ hp = HAlloc(p, 5); \ tup = TUPLE2(hp, What, tup); \ hp += 3; \ @@ -5427,7 +5438,7 @@ erts_module_info_1(Process* p, Eterm module, Eterm what) { Module* modp; ErtsCodeIndex code_ix = erts_active_code_ix(); - BeamInstr* code; + BeamCodeHeader* code_hdr; if (is_not_atom(module)) { return THE_NON_VALUE; @@ -5438,34 +5449,34 @@ erts_module_info_1(Process* p, Eterm module, Eterm what) return THE_NON_VALUE; } - code = modp->curr.code; - if (code == NULL) { + code_hdr = modp->curr.code_hdr; + if (code_hdr == NULL) { return THE_NON_VALUE; } - return get_module_info(p, code_ix, code, module, what); + return get_module_info(p, code_ix, code_hdr, module, what); } static Eterm -get_module_info(Process* p, ErtsCodeIndex code_ix, BeamInstr* code, +get_module_info(Process* p, ErtsCodeIndex code_ix, BeamCodeHeader* code_hdr, Eterm module, Eterm what) { if (what == am_module) { return module; } else if (what == am_md5) { - return md5_of_module(p, code); + return md5_of_module(p, code_hdr); } else if (what == am_exports) { return exported_from_module(p, code_ix, module); } else if (what == am_functions) { - return functions_in_module(p, code); + return functions_in_module(p, code_hdr); } else if (what == am_attributes) { - return attributes_for_module(p, code); + return attributes_for_module(p, code_hdr); } else if (what == am_compile) { - return compilation_info_for_module(p, code); + return compilation_info_for_module(p, code_hdr); } else if (what == am_native_addresses) { - return native_addresses(p, code); + return native_addresses(p, code_hdr); } else if (what == am_native) { - return has_native(code); + return has_native(code_hdr); } return THE_NON_VALUE; } @@ -5477,7 +5488,7 @@ get_module_info(Process* p, ErtsCodeIndex code_ix, BeamInstr* code, Eterm functions_in_module(Process* p, /* Process whose heap to use. */ - BeamInstr* code) + BeamCodeHeader* code_hdr) { int i; Uint num_functions; @@ -5486,12 +5497,12 @@ functions_in_module(Process* p, /* Process whose heap to use. */ Eterm* hp_end; Eterm result = NIL; - num_functions = code[MI_NUM_FUNCTIONS]; + num_functions = code_hdr->num_functions; need = 5*num_functions; hp = HAlloc(p, need); hp_end = hp + need; for (i = num_functions-1; i >= 0 ; i--) { - BeamInstr* func_info = (BeamInstr *) code[MI_FUNCTIONS+i]; + BeamInstr* func_info = code_hdr->functions[i]; Eterm name = (Eterm) func_info[3]; int arity = (int) func_info[4]; Eterm tuple; @@ -5517,11 +5528,11 @@ functions_in_module(Process* p, /* Process whose heap to use. */ */ static Eterm -has_native(BeamInstr *code) +has_native(BeamCodeHeader *code_hdr) { Eterm result = am_false; #ifdef HIPE - if (erts_is_module_native(code)) { + if (erts_is_module_native(code_hdr)) { result = am_true; } #endif @@ -5529,15 +5540,15 @@ has_native(BeamInstr *code) } int -erts_is_module_native(BeamInstr* code) +erts_is_module_native(BeamCodeHeader* code_hdr) { Uint i, num_functions; /* Check NativeAdress of first real function in module */ - if (code != NULL) { - num_functions = code[MI_NUM_FUNCTIONS]; + if (code_hdr != NULL) { + num_functions = code_hdr->num_functions; for (i=0; ifunctions[i]; Eterm name = (Eterm) func_info[3]; if (is_atom(name)) { return func_info[1] != 0; @@ -5554,7 +5565,7 @@ erts_is_module_native(BeamInstr* code) */ static Eterm -native_addresses(Process* p, BeamInstr* code) +native_addresses(Process* p, BeamCodeHeader* code_hdr) { int i; Eterm* hp; @@ -5563,12 +5574,12 @@ native_addresses(Process* p, BeamInstr* code) Eterm* hp_end; Eterm result = NIL; - num_functions = code[MI_NUM_FUNCTIONS]; + num_functions = code_hdr->num_functions; need = (6+BIG_UINT_HEAP_SIZE)*num_functions; hp = HAlloc(p, need); hp_end = hp + need; for (i = num_functions-1; i >= 0 ; i--) { - BeamInstr* func_info = (BeamInstr *) code[MI_FUNCTIONS+i]; + BeamInstr* func_info = code_hdr->functions[i]; Eterm name = (Eterm) func_info[3]; int arity = (int) func_info[4]; Eterm tuple; @@ -5634,15 +5645,15 @@ exported_from_module(Process* p, /* Process whose heap to use. */ Eterm attributes_for_module(Process* p, /* Process whose heap to use. */ - BeamInstr* code) + BeamCodeHeader* code_hdr) { byte* ext; Eterm result = NIL; - ext = (byte *) code[MI_ATTR_PTR]; + ext = code_hdr->attr_ptr; if (ext != NULL) { ErtsHeapFactory factory; - erts_factory_proc_prealloc_init(&factory, p, code[MI_ATTR_SIZE_ON_HEAP]); + erts_factory_proc_prealloc_init(&factory, p, code_hdr->attr_size_on_heap); result = erts_decode_ext(&factory, &ext); if (is_value(result)) { erts_factory_close(&factory); @@ -5657,15 +5668,15 @@ attributes_for_module(Process* p, /* Process whose heap to use. */ Eterm compilation_info_for_module(Process* p, /* Process whose heap to use. */ - BeamInstr* code) + BeamCodeHeader* code_hdr) { byte* ext; Eterm result = NIL; - ext = (byte *) code[MI_COMPILE_PTR]; + ext = code_hdr->compile_ptr; if (ext != NULL) { ErtsHeapFactory factory; - erts_factory_proc_prealloc_init(&factory, p, code[MI_COMPILE_SIZE_ON_HEAP]); + erts_factory_proc_prealloc_init(&factory, p, code_hdr->compile_size_on_heap); result = erts_decode_ext(&factory, &ext); if (is_value(result)) { erts_factory_close(&factory); @@ -5680,9 +5691,9 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */ Eterm md5_of_module(Process* p, /* Process whose heap to use. */ - BeamInstr* code) + BeamCodeHeader* code_hdr) { - return new_binary(p, (byte *) code[MI_MD5_PTR], MD5_SIZE); + return new_binary(p, code_hdr->md5_ptr, MD5_SIZE); } /* @@ -5893,7 +5904,7 @@ static byte* stub_copy_info(LoaderState* stp, int chunk, /* Chunk: ATTR_CHUNK or COMPILE_CHUNK */ byte* info, /* Where to store info. */ - BeamInstr* ptr_word, /* Where to store pointer into info. */ + byte** ptr_word, /* Where to store pointer into info. */ BeamInstr* size_word, /* Where to store size into info. */ BeamInstr* size_on_heap_word) /* Where to store size on heap. */ { @@ -5901,7 +5912,7 @@ stub_copy_info(LoaderState* stp, Uint size = stp->chunks[chunk].size; if (size != 0) { memcpy(info, stp->chunks[chunk].start, size); - *ptr_word = (BeamInstr) info; + *ptr_word = info; decoded_size = erts_decode_ext_size(info, size); if (decoded_size < 0) { return 0; @@ -6156,11 +6167,10 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) BeamInstr Patchlist; Eterm MD5Bin; Eterm* tp; - BeamInstr* code = NULL; - BeamInstr* ptrs; + BeamCodeHeader* code_hdr; + BeamInstr* code_base; BeamInstr* fp; byte* info; - Uint ci; int n; int code_size; int rval; @@ -6238,39 +6248,39 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) * Allocate memory for the stub module. */ - code_size = ((WORDS_PER_FUNCTION+1)*n + MI_FUNCTIONS + 2) * sizeof(BeamInstr); - code_size += stp->chunks[ATTR_CHUNK].size; - code_size += stp->chunks[COMPILE_CHUNK].size; - code_size += MD5_SIZE; - code = erts_alloc_fnf(ERTS_ALC_T_CODE, code_size); - if (!code) { + code_size = (offsetof(BeamCodeHeader,functions) + + ((n+1) * sizeof(BeamInstr*)) + + (WORDS_PER_FUNCTION*n + 1) * sizeof(BeamInstr) + + stp->chunks[ATTR_CHUNK].size + + stp->chunks[COMPILE_CHUNK].size + + MD5_SIZE); + code_hdr = erts_alloc_fnf(ERTS_ALC_T_CODE, code_size); + if (!code_hdr) { goto error; } /* - * Initialize code area. + * Initialize code header. */ - code[MI_NUM_FUNCTIONS] = n; - code[MI_ATTR_PTR] = 0; - code[MI_ATTR_SIZE] = 0; - code[MI_ATTR_SIZE_ON_HEAP] = 0; - code[MI_COMPILE_PTR] = 0; - code[MI_COMPILE_SIZE] = 0; - code[MI_COMPILE_SIZE_ON_HEAP] = 0; - code[MI_LITERALS_START] = 0; - code[MI_LITERALS_END] = 0; - code[MI_LITERALS_OFF_HEAP] = 0; - code[MI_ON_LOAD_FUNCTION_PTR] = 0; - code[MI_MD5_PTR] = 0; - ci = MI_FUNCTIONS + n + 1; + code_hdr->num_functions = n; + code_hdr->attr_ptr = NULL; + code_hdr->attr_size = 0; + code_hdr->attr_size_on_heap = 0; + code_hdr->compile_ptr = NULL; + code_hdr->compile_size = 0; + code_hdr->compile_size_on_heap = 0; + code_hdr->literals_start = NULL; + code_hdr->literals_end = NULL; + code_hdr->literals_off_heap = 0; + code_hdr->on_load_function_ptr = NULL; + code_hdr->md5_ptr = NULL; /* * Make stubs for all functions. */ - ptrs = code + MI_FUNCTIONS; - fp = code + ci; + fp = code_base = (BeamInstr*) &code_hdr->functions[n+1]; for (i = 0; i < n; i++) { Eterm* listp; Eterm tuple; @@ -6313,7 +6323,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) * Set the pointer and make the stub. Put a return instruction * as the body until we know what kind of trap we should put there. */ - ptrs[i] = (BeamInstr) fp; + code_hdr->functions[i] = fp; #ifdef HIPE op = (Eterm) BeamOpCode(op_hipe_trap_call); /* Might be changed later. */ #else @@ -6326,7 +6336,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) * Insert the last pointer and the int_code_end instruction. */ - ptrs[i] = (BeamInstr) fp; + code_hdr->functions[i] = fp; *fp++ = (BeamInstr) BeamOp(op_int_code_end); /* @@ -6335,16 +6345,16 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) info = (byte *) fp; info = stub_copy_info(stp, ATTR_CHUNK, info, - code+MI_ATTR_PTR, - code+MI_ATTR_SIZE, - code+MI_ATTR_SIZE_ON_HEAP); + &code_hdr->attr_ptr, + &code_hdr->attr_size, + &code_hdr->attr_size_on_heap); if (info == NULL) { goto error; } info = stub_copy_info(stp, COMPILE_CHUNK, info, - code+MI_COMPILE_PTR, - code+MI_COMPILE_SIZE, - code+MI_COMPILE_SIZE_ON_HEAP); + &code_hdr->compile_ptr, + &code_hdr->compile_size, + &code_hdr->compile_size_on_heap); if (info == NULL) { goto error; } @@ -6353,7 +6363,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) byte *md5 = NULL; if ((md5 = erts_get_aligned_binary_bytes(MD5Bin, &tmp)) != NULL) { sys_memcpy(info, md5, MD5_SIZE); - code[MI_MD5_PTR] = (BeamInstr) info; + code_hdr->md5_ptr = info; } erts_free_aligned_binary_bytes(tmp); } @@ -6362,7 +6372,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) * Insert the module in the module table. */ - rval = insert_new_code(p, 0, p->group_leader, Mod, code, code_size); + rval = insert_new_code(p, 0, p->group_leader, Mod, code_hdr, code_size); if (rval != NIL) { goto error; } @@ -6371,7 +6381,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) * Export all stub functions and insert the correct type of HiPE trap. */ - fp = code + ci; + fp = code_base; for (i = 0; i < n; i++) { stub_final_touch(stp, fp); fp += WORDS_PER_FUNCTION; diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index eedb5ee4cd..c86ac65521 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -24,7 +24,6 @@ #include "beam_opcodes.h" #include "erl_process.h" -int erts_is_module_native(BeamInstr* code); Eterm beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks, Eterm module); @@ -61,63 +60,66 @@ extern BeamInstr* em_call_nif; /* Total code size in bytes */ extern Uint erts_total_code_size; -/* - * Index into start of code chunks which contains additional information - * about the loaded module. - * - * First number of functions. - */ - -#define MI_NUM_FUNCTIONS 0 - -/* - * The attributes retrieved by Mod:module_info(attributes). - */ - -#define MI_ATTR_PTR 1 -#define MI_ATTR_SIZE 2 -#define MI_ATTR_SIZE_ON_HEAP 3 - -/* - * The compilation information retrieved by Mod:module_info(compile). - */ - -#define MI_COMPILE_PTR 4 -#define MI_COMPILE_SIZE 5 -#define MI_COMPILE_SIZE_ON_HEAP 6 - -/* - * Literal area (constant pool). - */ -#define MI_LITERALS_START 7 -#define MI_LITERALS_END 8 -#define MI_LITERALS_OFF_HEAP 9 - -/* - * Pointer to the on_load function (or NULL if none). - */ -#define MI_ON_LOAD_FUNCTION_PTR 10 - -/* - * Pointer to the line table (or NULL if none). - */ -#define MI_LINE_TABLE 11 - -/* - * Pointer to the module MD5 sum (16 bytes) - */ -#define MI_MD5_PTR 12 /* - * Start of function pointer table. This table contains pointers to - * all functions in the module plus an additional pointer just beyond - * the end of the last function. - * - * The actual loaded code (for the first function) start just beyond - * this table. + * Header of code chunks which contains additional information + * about the loaded module. */ - -#define MI_FUNCTIONS 13 +typedef struct beam_code_header { + /* + * Number of functions. + */ + UWord num_functions; + + /* + * The attributes retrieved by Mod:module_info(attributes). + */ + byte* attr_ptr; + UWord attr_size; + UWord attr_size_on_heap; + + /* + * The compilation information retrieved by Mod:module_info(compile). + */ + byte* compile_ptr; + UWord compile_size; + UWord compile_size_on_heap; + + /* + * Literal area (constant pool). + */ + Eterm* literals_start; + Eterm* literals_end; + struct erl_off_heap_header* literals_off_heap; + + /* + * Pointer to the on_load function (or NULL if none). + */ + BeamInstr* on_load_function_ptr; + + /* + * Pointer to the line table (or NULL if none). + */ + Eterm* line_table; + + /* + * Pointer to the module MD5 sum (16 bytes) + */ + byte* md5_ptr; + + /* + * Start of function pointer table. This table contains pointers to + * all functions in the module plus an additional pointer just beyond + * the end of the last function. + * + * The actual loaded code (for the first function) start just beyond + * this table. + */ + BeamInstr* functions[1]; + +}BeamCodeHeader; + +int erts_is_module_native(BeamCodeHeader* code); /* * Layout of the line table. diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c index 19079ba150..e6cf43446e 100644 --- a/erts/emulator/beam/beam_ranges.c +++ b/erts/emulator/beam/beam_ranges.c @@ -38,7 +38,7 @@ typedef struct { static Range* find_range(BeamInstr* pc); static void lookup_loc(FunctionInfo* fi, BeamInstr* pc, - BeamInstr* modp, int idx); + BeamCodeHeader*, int idx); /* * The following variables keep a sorted list of address ranges for @@ -241,6 +241,7 @@ erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info) BeamInstr** high; BeamInstr** mid; Range* rp; + BeamCodeHeader* hdr; fi->current = NULL; fi->needed = 5; @@ -249,9 +250,10 @@ erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info) if (rp == 0) { return; } + hdr = (BeamCodeHeader*) rp->start; - low = (BeamInstr **) (rp->start + MI_FUNCTIONS); - high = low + rp->start[MI_NUM_FUNCTIONS]; + low = hdr->functions; + high = low + hdr->num_functions; while (low < high) { mid = low + (high-low) / 2; if (pc < mid[0]) { @@ -259,10 +261,9 @@ erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info) } else if (pc < mid[1]) { fi->current = mid[0]+2; if (full_info) { - BeamInstr** fp = (BeamInstr **) (rp->start + - MI_FUNCTIONS); + BeamInstr** fp = hdr->functions; int idx = mid - fp; - lookup_loc(fi, pc, rp->start, idx); + lookup_loc(fi, pc, hdr, idx); } return; } else { @@ -295,9 +296,9 @@ find_range(BeamInstr* pc) } static void -lookup_loc(FunctionInfo* fi, BeamInstr* orig_pc, BeamInstr* modp, int idx) +lookup_loc(FunctionInfo* fi, BeamInstr* orig_pc, BeamCodeHeader* code_hdr, int idx) { - Eterm* line = (Eterm *) modp[MI_LINE_TABLE]; + Eterm* line = code_hdr->line_table; Eterm* low; Eterm* high; Eterm* mid; diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 64c8bc5e58..c7e7411935 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -381,7 +381,7 @@ loaded(int to, void *to_arg) int i; int old = 0; int cur = 0; - BeamInstr* code; + BeamCodeHeader* code; Module* modp; ErtsCodeIndex code_ix; @@ -439,30 +439,30 @@ loaded(int to, void *to_arg) erts_print(to, to_arg, "\n"); erts_print(to, to_arg, "Current size: %d\n", modp->curr.code_length); - code = modp->curr.code; - if (code != NULL && code[MI_ATTR_PTR]) { + code = modp->curr.code_hdr; + if (code != NULL && code->attr_ptr) { erts_print(to, to_arg, "Current attributes: "); - dump_attributes(to, to_arg, (byte *) code[MI_ATTR_PTR], - code[MI_ATTR_SIZE]); + dump_attributes(to, to_arg, code->attr_ptr, + code->attr_size); } - if (code != NULL && code[MI_COMPILE_PTR]) { + if (code != NULL && code->compile_ptr) { erts_print(to, to_arg, "Current compilation info: "); - dump_attributes(to, to_arg, (byte *) code[MI_COMPILE_PTR], - code[MI_COMPILE_SIZE]); + dump_attributes(to, to_arg, code->compile_ptr, + code->compile_size); } if (modp->old.code_length != 0) { erts_print(to, to_arg, "Old size: %d\n", modp->old.code_length); - code = modp->old.code; - if (code[MI_ATTR_PTR]) { + code = modp->old.code_hdr; + if (code->attr_ptr) { erts_print(to, to_arg, "Old attributes: "); - dump_attributes(to, to_arg, (byte *) code[MI_ATTR_PTR], - code[MI_ATTR_SIZE]); + dump_attributes(to, to_arg, code->attr_ptr, + code->attr_size); } - if (code[MI_COMPILE_PTR]) { + if (code->compile_ptr) { erts_print(to, to_arg, "Old compilation info: "); - dump_attributes(to, to_arg, (byte *) code[MI_COMPILE_PTR], - code[MI_COMPILE_SIZE]); + dump_attributes(to, to_arg, code->compile_ptr, + code->compile_size); } } } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index add4a66f90..01414f326d 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -2230,17 +2230,17 @@ int enif_map_iterator_get_pair(ErlNifEnv *env, ***************************************************************************/ -static BeamInstr** get_func_pp(BeamInstr* mod_code, Eterm f_atom, unsigned arity) +static BeamInstr** get_func_pp(BeamCodeHeader* mod_code, Eterm f_atom, unsigned arity) { - int n = (int) mod_code[MI_NUM_FUNCTIONS]; + int n = (int) mod_code->num_functions; int j; for (j = 0; j < n; ++j) { - BeamInstr* code_ptr = (BeamInstr*) mod_code[MI_FUNCTIONS+j]; + BeamInstr* code_ptr = (BeamInstr*) mod_code->functions[j]; ASSERT(code_ptr[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); if (f_atom == ((Eterm) code_ptr[3]) && arity == ((unsigned) code_ptr[4])) { - return (BeamInstr**) &mod_code[MI_FUNCTIONS+j]; + return (BeamInstr**) &mod_code->functions[j]; } } return NULL; @@ -2423,8 +2423,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) if (init_func != NULL) handle = init_func; - if (!in_area(caller, mod->curr.code, mod->curr.code_length)) { - ASSERT(in_area(caller, mod->old.code, mod->old.code_length)); + if (!in_area(caller, mod->curr.code_hdr, mod->curr.code_length)) { + ASSERT(in_area(caller, mod->old.code_hdr, mod->old.code_length)); ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old " "module '%T' not allowed", mod_atom); @@ -2478,7 +2478,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) for (i=0; i < entry->num_of_funcs && ret==am_ok; i++) { BeamInstr** code_pp; if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1) - || (code_pp = get_func_pp(mod->curr.code, f_atom, f->arity))==NULL) { + || (code_pp = get_func_pp(mod->curr.code_hdr, f_atom, f->arity))==NULL) { ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u", mod_atom, f->name, f->arity); } @@ -2621,7 +2621,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) { BeamInstr* code_ptr; erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1); - code_ptr = *get_func_pp(mod->curr.code, f_atom, f->arity); + code_ptr = *get_func_pp(mod->curr.code_hdr, f_atom, f->arity); if (code_ptr[1] == 0) { code_ptr[5+0] = (BeamInstr) BeamOp(op_call_nif); diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c index 86dd3b5aac..f6794c012f 100644 --- a/erts/emulator/beam/module.c +++ b/erts/emulator/beam/module.c @@ -74,8 +74,8 @@ static Module* module_alloc(Module* tmpl) erts_smp_atomic_add_nob(&tot_module_bytes, sizeof(Module)); obj->module = tmpl->module; - obj->curr.code = 0; - obj->old.code = 0; + obj->curr.code_hdr = 0; + obj->old.code_hdr = 0; obj->curr.code_length = 0; obj->old.code_length = 0; obj->slot.index = -1; diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h index c8a6351b04..e66d628ca9 100644 --- a/erts/emulator/beam/module.h +++ b/erts/emulator/beam/module.h @@ -26,7 +26,7 @@ #endif struct erl_module_instance { - BeamInstr* code; + BeamCodeHeader* code_hdr; int code_length; /* Length of loaded code in bytes. */ unsigned catches; struct erl_module_nif* nif; diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 7193c3d301..c6ea8a5132 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -574,15 +574,15 @@ static void print_mfa(Eterm mod, Eterm fun, unsigned int ari) static Uint *hipe_find_emu_address(Eterm mod, Eterm name, unsigned int arity) { Module *modp; - Uint *code_base; + BeamCodeHeader* code_hdr; int i, n; modp = erts_get_module(mod, erts_active_code_ix()); - if (modp == NULL || (code_base = modp->curr.code) == NULL) + if (modp == NULL || (code_hdr = modp->curr.code_hdr) == NULL) return NULL; - n = code_base[MI_NUM_FUNCTIONS]; + n = code_hdr->num_functions; for (i = 0; i < n; ++i) { - Uint *code_ptr = (Uint*)code_base[MI_FUNCTIONS+i]; + Uint *code_ptr = (Uint*)code_hdr->functions[i]; ASSERT(code_ptr[0] == BeamOpCode(op_i_func_info_IaaI)); if (code_ptr[3] == name && code_ptr[4] == arity) return code_ptr+5; -- cgit v1.2.3 From ec4a7e7150c47523a553cac806c86ec08ab2dae7 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 16 Sep 2015 15:02:19 +0200 Subject: Refactor GC --- erts/emulator/beam/erl_gc.c | 620 ++++++++++++++++++++++---------------------- erts/emulator/beam/sys.h | 37 ++- 2 files changed, 334 insertions(+), 323 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index e316ab95ab..2d7b7cafa4 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -42,11 +42,76 @@ #include "dtrace-wrapper.h" #include "erl_bif_unique.h" +#define ERTS_CONTINUOUS_NEW_HEAP + #define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1 #define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20 #define ERTS_INACT_WR_PB_LEAVE_LIMIT 10 #define ERTS_INACT_WR_PB_LEAVE_PERCENTAGE 10 +#if defined(DEBUG) || 0 +#define ERTS_GC_DEBUG +#else +#undef ERTS_GC_DEBUG +#endif +#ifdef ERTS_GC_DEBUG +# define ERTS_GC_ASSERT ASSERT +#else +# define ERTS_GC_ASSERT(B) ((void) 1) +#endif + +#ifdef ERTS_CONTINUOUS_NEW_HEAP +#define ERTS_IS_NEW_HEAP_PTR__(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ + ErtsInArea((Ptr), (NhPtr), (NhSz)) +#define ERTS_IS_LITERAL_PTR__(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ + (!ErtsInArea((Ptr), (NhPtr), (NhSz)) \ + && !ErtsInArea((Ptr), (OhPtr), (OhSz))) + +#ifdef ERTS_GC_DEBUG +#define ERTS_IS_NEW_HEAP_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ + (ERTS_IS_NEW_HEAP_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) \ + ? (ERTS_GC_ASSERT(!erts_is_literal((TPtr), (Ptr)) \ + && !ErtsInArea((Ptr), (OhPtr), (OhSz))), 1) \ + : (ERTS_GC_ASSERT(erts_is_literal((TPtr), (Ptr)) \ + || ErtsInArea((Ptr), (OhPtr), (OhSz))), 0)) +#define ERTS_IS_LITERAL_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ + (ERTS_IS_LITERAL_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) \ + ? (ERTS_GC_ASSERT(erts_is_literal((TPtr), (Ptr))), 1) \ + : (ERTS_GC_ASSERT(!erts_is_literal((TPtr), (Ptr))), 0)) +#endif + +#else + +#define ERTS_IS_NEW_HEAP_PTR__(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ + (!erts_is_literal((TPtr), (Ptr)) && !ErtsInArea((Ptr), (OhPtr), (OhSz))) +#define ERTS_IS_LITERAL_PTR__(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ + (erts_is_literal((TPtr), (Ptr))) + +#ifdef ERTS_GC_DEBUG +#define ERTS_IS_NEW_HEAP_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ + (ERTS_IS_NEW_HEAP_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) \ + ? (ERTS_GC_ASSERT(ErtsInArea((Ptr), (NhPtr), (NhSz))), 1) \ + : (ERTS_GC_ASSERT(!ErtsInArea((Ptr), (NhPtr), (NhSz))), 0)) +#define ERTS_IS_LITERAL_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ + (ERTS_IS_LITERAL_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) \ + ? (ERTS_GC_ASSERT(!ErtsInArea((Ptr), (NhPtr), (NhSz)) \ + && !ErtsInArea((Ptr), (OhPtr), (OhSz))), 1) \ + : (ERTS_GC_ASSERT(ErtsInArea((Ptr), (NhPtr), (NhSz)) \ + || ErtsInArea((Ptr), (OhPtr), (OhSz))), 0)) +#endif + +#endif + +#ifndef ERTS_IS_NEW_HEAP_PTR +#define ERTS_IS_NEW_HEAP_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ + ERTS_IS_NEW_HEAP_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) +#endif + +#ifndef ERTS_IS_LITERAL_PTR +#define ERTS_IS_LITERAL_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ + ERTS_IS_LITERAL_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) +#endif + /* * Returns number of elements in an array. */ @@ -100,13 +165,27 @@ static Uint setup_rootset(Process*, Eterm*, int, Rootset*); static void cleanup_rootset(Rootset *rootset); static Uint combined_message_size(Process* p); static void remove_message_buffers(Process* p); +static Eterm *full_sweep_heaps(Process *p, + int hibernate, + Eterm *n_heap, Eterm* n_htop, + char *h, Uint h_size, + char *oh, Uint oh_size, + Eterm *objv, int nobj); static int major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl); static int minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl); static void do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj); -static Eterm* sweep_rootset(Rootset *rootset, Eterm* htop, char* src, Uint src_size); -static Eterm* sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size); -static Eterm* sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, - char* src, Uint src_size); +static Eterm *sweep_new_heap(Eterm *n_hp, Eterm *n_htop, + char* new_heap, Uint new_heap_size, + char* old_heap, Uint old_heap_size); +static Eterm *sweep_heaps(Eterm *n_hp, Eterm *n_htop, + char* new_heap, Uint new_heap_size, + char* old_heap, Uint old_heap_size); +static Eterm* sweep_literal_area(Eterm* n_hp, Eterm* n_htop, + char* new_heap, Uint new_heap_size, + char* old_heap, Uint old_heap_size, + char* src, Uint src_size); +static Eterm* sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, + char* src, Uint src_size); static Eterm* collect_heap_frags(Process* p, Eterm* heap, Eterm* htop, Eterm* objv, int nobj); static void adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj); @@ -514,9 +593,6 @@ erts_garbage_collect_hibernate(Process* p) Uint heap_size; Eterm* heap; Eterm* htop; - Rootset rootset; - char* src; - Uint src_size; Uint actual_size; char* area; Uint area_size; @@ -544,41 +620,16 @@ erts_garbage_collect_hibernate(Process* p) sizeof(Eterm)*heap_size); htop = heap; - (void) setup_rootset(p, p->arg_reg, p->arity, &rootset); -#if HIPE - hipe_empty_nstack(p); -#endif - - src = (char *) p->heap; - src_size = (char *) p->htop - src; - htop = sweep_rootset(&rootset, htop, src, src_size); - htop = sweep_one_area(heap, htop, src, src_size); - - if (p->old_heap) { - src = (char *) p->old_heap; - src_size = (char *) p->old_htop - src; - htop = sweep_rootset(&rootset, htop, src, src_size); - htop = sweep_one_area(heap, htop, src, src_size); - } - - cleanup_rootset(&rootset); - - if (MSO(p).first) { - sweep_off_heap(p, 1); - } - - /* - * Update all pointers. - */ - ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, - (void*)HEAP_START(p), - HEAP_SIZE(p) * sizeof(Eterm)); - if (p->old_heap) { - ERTS_HEAP_FREE(ERTS_ALC_T_OLD_HEAP, - (void*)p->old_heap, - (p->old_hend - p->old_heap) * sizeof(Eterm)); - p->old_heap = p->old_htop = p->old_hend = 0; - } + htop = full_sweep_heaps(p, + 1, + heap, + htop, + (char *) p->heap, + (char *) p->htop - (char *) p->heap, + (char *) p->old_heap, + (char *) p->old_htop - (char *) p->old_heap, + p->arg_reg, + p->arity); p->heap = heap; p->high_water = htop; @@ -724,7 +775,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals, if (IS_MOVED_BOXED(val)) { ASSERT(is_boxed(val)); *g_ptr++ = val; - } else if (in_area(ptr, area, area_size)) { + } else if (ErtsInArea(ptr, area, area_size)) { MOVE_BOXED(ptr,val,old_htop,g_ptr++); } else { g_ptr++; @@ -735,7 +786,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals, val = *ptr; if (IS_MOVED_CONS(val)) { /* Moved */ *g_ptr++ = ptr[1]; - } else if (in_area(ptr, area, area_size)) { + } else if (ErtsInArea(ptr, area, area_size)) { MOVE_CONS(ptr,val,old_htop,g_ptr++); } else { g_ptr++; @@ -755,8 +806,11 @@ erts_garbage_collect_literals(Process* p, Eterm* literals, * Now we'll have to go through all heaps updating all other references. */ - old_htop = sweep_one_heap(p->heap, p->htop, old_htop, area, area_size); - old_htop = sweep_one_area(p->old_heap, old_htop, area, area_size); + old_htop = sweep_literals_to_old_heap(p->heap, p->htop, old_htop, area, area_size); + old_htop = sweep_literal_area(p->old_heap, old_htop, + (char *) p->heap, sizeof(Eterm)*p->heap_sz, + (char *) p->old_heap, sizeof(Eterm)*old_heap_size, + area, area_size); ASSERT(p->old_htop <= old_htop && old_htop <= p->old_hend); p->old_htop = old_htop; @@ -1034,15 +1088,13 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) if (IS_MOVED_BOXED(val)) { ASSERT(is_boxed(val)); *g_ptr++ = val; - } else if (in_area(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, heap, mature_size)) { MOVE_BOXED(ptr,val,old_htop,g_ptr++); - } else if (in_area(ptr, heap, heap_size)) { - ASSERT(!erts_is_literal(gval, ptr) - && !in_area(ptr, oh, oh_size)); + } else if (ERTS_IS_NEW_HEAP_PTR(gval, ptr, + heap, heap_size, + oh, oh_size)) { MOVE_BOXED(ptr,val,n_htop,g_ptr++); } else { - ASSERT(erts_is_literal(gval, ptr) - || in_area(ptr, oh, oh_size)); g_ptr++; } break; @@ -1053,15 +1105,13 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) val = *ptr; if (IS_MOVED_CONS(val)) { /* Moved */ *g_ptr++ = ptr[1]; - } else if (in_area(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, heap, mature_size)) { MOVE_CONS(ptr,val,old_htop,g_ptr++); - } else if (in_area(ptr, heap, heap_size)) { - ASSERT(!erts_is_literal(gval, ptr) - && !in_area(ptr, oh, oh_size)); + } else if (ERTS_IS_NEW_HEAP_PTR(gval, ptr, + heap, heap_size, + oh, oh_size)) { MOVE_CONS(ptr,val,n_htop,g_ptr++); } else { - ASSERT(erts_is_literal(gval, ptr) - || in_area(ptr, oh, oh_size)); g_ptr++; } break; @@ -1084,7 +1134,8 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) */ if (mature_size == 0) { - n_htop = sweep_one_area(n_heap, n_htop, heap, heap_size); + n_htop = sweep_new_heap(n_heap, n_htop, heap, heap_size, + oh, oh_size); } else { Eterm* n_hp = n_heap; Eterm* ptr; @@ -1101,15 +1152,13 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) if (IS_MOVED_BOXED(val)) { ASSERT(is_boxed(val)); *n_hp++ = val; - } else if (in_area(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, heap, mature_size)) { MOVE_BOXED(ptr,val,old_htop,n_hp++); - } else if (in_area(ptr, heap, heap_size)) { - ASSERT(!erts_is_literal(gval, ptr) - && !in_area(ptr, oh, oh_size)); + } else if (ERTS_IS_NEW_HEAP_PTR(gval, ptr, + heap, heap_size, + oh, oh_size)) { MOVE_BOXED(ptr,val,n_htop,n_hp++); } else { - ASSERT(erts_is_literal(gval, ptr) - || in_area(ptr, oh, oh_size)); n_hp++; } break; @@ -1119,15 +1168,13 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) val = *ptr; if (IS_MOVED_CONS(val)) { *n_hp++ = ptr[1]; - } else if (in_area(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, heap, mature_size)) { MOVE_CONS(ptr,val,old_htop,n_hp++); - } else if (in_area(ptr, heap, heap_size)) { - ASSERT(!erts_is_literal(gval, ptr) - && !in_area(ptr, oh, oh_size)); + } else if (ERTS_IS_NEW_HEAP_PTR(gval, ptr, + heap, heap_size, + oh, oh_size)) { MOVE_CONS(ptr,val,n_htop,n_hp++); } else { - ASSERT(erts_is_literal(gval, ptr) - || in_area(ptr, oh, oh_size)); n_hp++; } break; @@ -1145,19 +1192,15 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) if (IS_MOVED_BOXED(val)) { *origptr = val; mb->base = binary_bytes(val); - } else if (in_area(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, heap, mature_size)) { MOVE_BOXED(ptr,val,old_htop,origptr); mb->base = binary_bytes(mb->orig); - } else if (in_area(ptr, heap, heap_size)) { - ASSERT(!erts_is_literal(*origptr, origptr) - && !in_area(ptr, oh, oh_size)); + } else if (ERTS_IS_NEW_HEAP_PTR(*origptr, ptr, + heap, heap_size, + oh, oh_size)) { MOVE_BOXED(ptr,val,n_htop,origptr); mb->base = binary_bytes(mb->orig); } - else { - ASSERT(erts_is_literal(*origptr, origptr) - || in_area(ptr, oh, oh_size)); - } } n_hp += (thing_arityval(gval)+1); } @@ -1176,7 +1219,9 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) */ if (OLD_HTOP(p) < old_htop) { - old_htop = sweep_one_area(OLD_HTOP(p), old_htop, heap, heap_size); + old_htop = sweep_new_heap(OLD_HTOP(p), old_htop, + heap, heap_size, + oh, oh_size); } OLD_HTOP(p) = old_htop; HIGH_WATER(p) = n_htop; @@ -1229,8 +1274,6 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) static int major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) { - Rootset rootset; - Roots* roots; const Uint size_before = ((HEAP_TOP(p) - HEAP_START(p)) + (OLD_HTOP(p) - OLD_HEAP(p)) + MBUF_SIZE(p)); @@ -1240,8 +1283,7 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) Uint src_size = (char *) HEAP_TOP(p) - src; char* oh = (char *) OLD_HEAP(p); Uint oh_size = (char *) OLD_HTOP(p) - oh; - Uint n; - Uint new_sz; + Uint new_sz, stk_sz; /* * Do a fullsweep GC. First figure out the size of the heap @@ -1272,13 +1314,93 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) n_htop = collect_heap_frags(p, n_heap, n_htop, objv, nobj); } + n_htop = full_sweep_heaps(p, 0, n_heap, n_htop, src, src_size, + oh, oh_size, objv, nobj); + + /* Move the stack to the end of the heap */ + stk_sz = HEAP_END(p) - p->stop; + sys_memcpy(n_heap + new_sz - stk_sz, p->stop, stk_sz * sizeof(Eterm)); + p->stop = n_heap + new_sz - stk_sz; + +#ifdef USE_VM_PROBES + if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) { + DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, pidbuf); + DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz); + } +#endif + + ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, + (void *) HEAP_START(p), + (HEAP_END(p) - HEAP_START(p)) * sizeof(Eterm)); + HEAP_START(p) = n_heap; + HEAP_TOP(p) = n_htop; + HEAP_SIZE(p) = new_sz; + HEAP_END(p) = n_heap + new_sz; + GEN_GCS(p) = 0; + + HIGH_WATER(p) = HEAP_TOP(p); + + ErtsGcQuickSanityCheck(p); + + *recl += size_before - (HEAP_TOP(p) - HEAP_START(p)); + + { + ErlMessage *msgp; + + /* + * Copy newly received message onto the end of the new heap. + */ + for (msgp = p->msg.first; msgp; msgp = msgp->next) { + if (msgp->data.attached) { + ErtsHeapFactory factory; + erts_factory_proc_prealloc_init(&factory, p, + erts_msg_attached_data_size(msgp)); + erts_move_msg_attached_data_to_heap(&factory, msgp); + erts_factory_close(&factory); + ErtsGcQuickSanityCheck(p); + } + } + } + + adjust_after_fullsweep(p, need, objv, nobj); + +#ifdef HARDDEBUG + disallow_heap_frag_ref_in_heap(p); +#endif + remove_message_buffers(p); + + ErtsGcQuickSanityCheck(p); + return 1; /* We are done. */ +} + +static Eterm * +full_sweep_heaps(Process *p, + int hibernate, + Eterm *n_heap, Eterm* n_htop, + char *h, Uint h_size, + char *oh, Uint oh_size, + Eterm *objv, int nobj) +{ + Rootset rootset; + Roots *roots; + Uint n; + /* * Copy all top-level terms directly referenced by the rootset to * the new new_heap. */ n = setup_rootset(p, objv, nobj, &rootset); - n_htop = fullsweep_nstack(p, n_htop); + +#ifdef HIPE + if (hibernate) + hipe_empty_nstack(p); + else + n_htop = fullsweep_nstack(p, n_htop); +#endif + roots = rootset.roots; while (n--) { Eterm* g_ptr = roots->v; @@ -1298,11 +1420,11 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) if (IS_MOVED_BOXED(val)) { ASSERT(is_boxed(val)); *g_ptr++ = val; - } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) { - ASSERT(!erts_is_literal(gval, ptr)); + } else if (!ERTS_IS_LITERAL_PTR(gval, ptr, + h, h_size, + oh, oh_size)) { MOVE_BOXED(ptr,val,n_htop,g_ptr++); } else { - ASSERT(erts_is_literal(gval, ptr)); g_ptr++; } continue; @@ -1313,11 +1435,11 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) val = *ptr; if (IS_MOVED_CONS(val)) { *g_ptr++ = ptr[1]; - } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) { - ASSERT(!erts_is_literal(gval, ptr)); + } else if (!ERTS_IS_LITERAL_PTR(gval, ptr, + h, h_size, + oh, oh_size)) { MOVE_CONS(ptr,val,n_htop,g_ptr++); } else { - ASSERT(erts_is_literal(gval, ptr)); g_ptr++; } continue; @@ -1340,82 +1462,7 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) * until all is copied. */ - if (oh_size == 0) { - n_htop = sweep_one_area(n_heap, n_htop, src, src_size); - } else { - Eterm* n_hp = n_heap; - - while (n_hp != n_htop) { - Eterm* ptr; - Eterm val; - Eterm gval = *n_hp; - - switch (primary_tag(gval)) { - case TAG_PRIMARY_BOXED: { - ptr = boxed_val(gval); - val = *ptr; - if (IS_MOVED_BOXED(val)) { - ASSERT(is_boxed(val)); - *n_hp++ = val; - } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) { - ASSERT(!erts_is_literal(gval, ptr)); - MOVE_BOXED(ptr,val,n_htop,n_hp++); - } else { - ASSERT(erts_is_literal(gval, ptr)); - n_hp++; - } - break; - } - case TAG_PRIMARY_LIST: { - ptr = list_val(gval); - val = *ptr; - if (IS_MOVED_CONS(val)) { - *n_hp++ = ptr[1]; - } else if (in_area(ptr, src, src_size) || in_area(ptr, oh, oh_size)) { - ASSERT(!erts_is_literal(gval, ptr)); - MOVE_CONS(ptr,val,n_htop,n_hp++); - } else { - ASSERT(erts_is_literal(gval, ptr)); - n_hp++; - } - break; - } - case TAG_PRIMARY_HEADER: { - if (!header_is_thing(gval)) - n_hp++; - else { - if (header_is_bin_matchstate(gval)) { - ErlBinMatchState *ms = (ErlBinMatchState*) n_hp; - ErlBinMatchBuffer *mb = &(ms->mb); - Eterm* origptr; - origptr = &(mb->orig); - ptr = boxed_val(*origptr); - val = *ptr; - if (IS_MOVED_BOXED(val)) { - *origptr = val; - mb->base = binary_bytes(*origptr); - } else if (in_area(ptr, src, src_size) || -- in_area(ptr, oh, oh_size)) { - ASSERT(!erts_is_literal(*origptr, origptr)); - MOVE_BOXED(ptr,val,n_htop,origptr); - mb->base = binary_bytes(*origptr); - ptr = boxed_val(*origptr); - val = *ptr; - } - else { - ASSERT(erts_is_literal(*origptr, origptr)); - } - } - n_hp += (thing_arityval(gval)+1); - } - break; - } - default: - n_hp++; - break; - } - } - } + n_htop = sweep_heaps(n_heap, n_htop, h, h_size, oh, oh_size); if (MSO(p).first) { sweep_off_heap(p, 1); @@ -1428,62 +1475,7 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) OLD_HEAP(p) = OLD_HTOP(p) = OLD_HEND(p) = NULL; } - /* Move the stack to the end of the heap */ - n = HEAP_END(p) - p->stop; - sys_memcpy(n_heap + new_sz - n, p->stop, n * sizeof(Eterm)); - p->stop = n_heap + new_sz - n; - -#ifdef USE_VM_PROBES - if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) { - DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); - - dtrace_proc_str(p, pidbuf); - DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz); - } -#endif - - ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, - (void *) HEAP_START(p), - (HEAP_END(p) - HEAP_START(p)) * sizeof(Eterm)); - HEAP_START(p) = n_heap; - HEAP_TOP(p) = n_htop; - HEAP_SIZE(p) = new_sz; - HEAP_END(p) = n_heap + new_sz; - GEN_GCS(p) = 0; - - HIGH_WATER(p) = HEAP_TOP(p); - - ErtsGcQuickSanityCheck(p); - - *recl += size_before - (HEAP_TOP(p) - HEAP_START(p)); - - { - ErlMessage *msgp; - - /* - * Copy newly received message onto the end of the new heap. - */ - for (msgp = p->msg.first; msgp; msgp = msgp->next) { - if (msgp->data.attached) { - ErtsHeapFactory factory; - erts_factory_proc_prealloc_init(&factory, p, - erts_msg_attached_data_size(msgp)); - erts_move_msg_attached_data_to_heap(&factory, msgp); - erts_factory_close(&factory); - ErtsGcQuickSanityCheck(p); - } - } - } - - adjust_after_fullsweep(p, need, objv, nobj); - -#ifdef HARDDEBUG - disallow_heap_frag_ref_in_heap(p); -#endif - remove_message_buffers(p); - - ErtsGcQuickSanityCheck(p); - return 1; /* We are done. */ + return n_htop; } static void @@ -1585,7 +1577,7 @@ disallow_heap_frag_ref(Process* p, Eterm* n_htop, Eterm* objv, int nobj) objv++; } else { for (qb = mbuf; qb != NULL; qb = qb->next) { - if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { + if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { abort(); } } @@ -1601,7 +1593,7 @@ disallow_heap_frag_ref(Process* p, Eterm* n_htop, Eterm* objv, int nobj) objv++; } else { for (qb = mbuf; qb != NULL; qb = qb->next) { - if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { + if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { abort(); } } @@ -1644,9 +1636,9 @@ disallow_heap_frag_ref_in_heap(Process* p) switch (primary_tag(val)) { case TAG_PRIMARY_BOXED: ptr = _unchecked_boxed_val(val); - if (!in_area(ptr, heap, heap_size)) { + if (!ErtsInArea(ptr, heap, heap_size)) { for (qb = MBUF(p); qb != NULL; qb = qb->next) { - if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { + if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { abort(); } } @@ -1654,9 +1646,9 @@ disallow_heap_frag_ref_in_heap(Process* p) break; case TAG_PRIMARY_LIST: ptr = _unchecked_list_val(val); - if (!in_area(ptr, heap, heap_size)) { + if (!ErtsInArea(ptr, heap, heap_size)) { for (qb = MBUF(p); qb != NULL; qb = qb->next) { - if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { + if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { abort(); } } @@ -1699,12 +1691,12 @@ disallow_heap_frag_ref_in_old_heap(Process* p) switch (primary_tag(val)) { case TAG_PRIMARY_BOXED: ptr = (Eterm *) val; - if (!in_area(ptr, old_heap, old_heap_size)) { - if (in_area(ptr, new_heap, new_heap_size)) { + if (!ErtsInArea(ptr, old_heap, old_heap_size)) { + if (ErtsInArea(ptr, new_heap, new_heap_size)) { abort(); } for (qb = MBUF(p); qb != NULL; qb = qb->next) { - if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { + if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { abort(); } } @@ -1712,12 +1704,12 @@ disallow_heap_frag_ref_in_old_heap(Process* p) break; case TAG_PRIMARY_LIST: ptr = (Eterm *) val; - if (!in_area(ptr, old_heap, old_heap_size)) { - if (in_area(ptr, new_heap, new_heap_size)) { + if (!ErtsInArea(ptr, old_heap, old_heap_size)) { + if (ErtsInArea(ptr, new_heap, new_heap_size)) { abort(); } for (qb = MBUF(p); qb != NULL; qb = qb->next) { - if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { + if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { abort(); } } @@ -1726,7 +1718,7 @@ disallow_heap_frag_ref_in_old_heap(Process* p) case TAG_PRIMARY_HEADER: if (header_is_thing(val)) { hp += _unchecked_thing_arityval(val); - if (!in_area(hp, old_heap, old_heap_size+1)) { + if (!ErtsInArea(hp, old_heap, old_heap_size+1)) { abort(); } } @@ -1736,66 +1728,35 @@ disallow_heap_frag_ref_in_old_heap(Process* p) } #endif -static Eterm* -sweep_rootset(Rootset* rootset, Eterm* htop, char* src, Uint src_size) +typedef enum { + ErtsSweepNewHeap, + ErtsSweepHeaps, + ErtsSweepLiteralArea +} ErtsSweepType; + +static ERTS_FORCE_INLINE Eterm * +sweep(Eterm *n_hp, Eterm *n_htop, + ErtsSweepType type, + char *h, Uint hsz, + char *oh, Uint ohsz, + char *src, Uint src_size) { - Roots* roots = rootset->roots; - Uint n = rootset->num_roots; Eterm* ptr; - Eterm gval; Eterm val; + Eterm gval; - while (n--) { - Eterm* g_ptr = roots->v; - Uint g_sz = roots->sz; - - roots++; - while (g_sz--) { - gval = *g_ptr; - - switch (primary_tag(gval)) { - case TAG_PRIMARY_BOXED: { - ptr = boxed_val(gval); - val = *ptr; - if (IS_MOVED_BOXED(val)) { - ASSERT(is_boxed(val)); - *g_ptr++ = val; - } else if (in_area(ptr, src, src_size)) { - MOVE_BOXED(ptr,val,htop,g_ptr++); - } else { - g_ptr++; - } - break; - } - case TAG_PRIMARY_LIST: { - ptr = list_val(gval); - val = *ptr; - if (IS_MOVED_CONS(val)) { - *g_ptr++ = ptr[1]; - } else if (in_area(ptr, src, src_size)) { - MOVE_CONS(ptr,val,htop,g_ptr++); - } else { - g_ptr++; - } - break; - } - - default: - g_ptr++; - break; - } - } - } - return htop; -} - +#undef ERTS_IS_IN_SWEEP_AREA -static Eterm* -sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size) -{ - Eterm* ptr; - Eterm val; - Eterm gval; +#define ERTS_IS_IN_SWEEP_AREA(TPtr, Ptr) \ + (type == ErtsSweepHeaps \ + ? !ERTS_IS_LITERAL_PTR((TPtr), (Ptr), h, hsz, oh, ohsz) \ + : (type == ErtsSweepNewHeap \ + ? ERTS_IS_NEW_HEAP_PTR((TPtr), (Ptr), h, hsz, oh, ohsz) \ + : (ErtsInArea((Ptr), src, src_size) \ + ? (ERTS_GC_ASSERT(erts_is_literal((TPtr), (Ptr)) \ + && !ErtsInArea((Ptr), h, hsz) \ + && !ErtsInArea((Ptr), oh, ohsz)), 1) \ + : 0))) while (n_hp != n_htop) { ASSERT(n_hp < n_htop); @@ -1807,7 +1768,7 @@ sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size) if (IS_MOVED_BOXED(val)) { ASSERT(is_boxed(val)); *n_hp++ = val; - } else if (in_area(ptr, src, src_size)) { + } else if (ERTS_IS_IN_SWEEP_AREA(gval, ptr)) { MOVE_BOXED(ptr,val,n_htop,n_hp++); } else { n_hp++; @@ -1819,7 +1780,7 @@ sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size) val = *ptr; if (IS_MOVED_CONS(val)) { *n_hp++ = ptr[1]; - } else if (in_area(ptr, src, src_size)) { + } else if (ERTS_IS_IN_SWEEP_AREA(gval, ptr)) { MOVE_CONS(ptr,val,n_htop,n_hp++); } else { n_hp++; @@ -1840,7 +1801,7 @@ sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size) if (IS_MOVED_BOXED(val)) { *origptr = val; mb->base = binary_bytes(*origptr); - } else if (in_area(ptr, src, src_size)) { + } else if (ERTS_IS_IN_SWEEP_AREA(*origptr, ptr)) { MOVE_BOXED(ptr,val,n_htop,origptr); mb->base = binary_bytes(*origptr); } @@ -1855,10 +1816,49 @@ sweep_one_area(Eterm* n_hp, Eterm* n_htop, char* src, Uint src_size) } } return n_htop; +#undef ERTS_IS_IN_SWEEP_AREA +} + +static Eterm * +sweep_new_heap(Eterm *n_hp, Eterm *n_htop, + char* new_heap, Uint new_heap_size, + char* old_heap, Uint old_heap_size) +{ + return sweep(n_hp, n_htop, + ErtsSweepNewHeap, + new_heap, new_heap_size, + old_heap, old_heap_size, + NULL, 0); +} + +static Eterm * +sweep_heaps(Eterm *n_hp, Eterm *n_htop, + char* new_heap, Uint new_heap_size, + char* old_heap, Uint old_heap_size) +{ + return sweep(n_hp, n_htop, + ErtsSweepHeaps, + new_heap, new_heap_size, + old_heap, old_heap_size, + NULL, 0); +} + +static Eterm * +sweep_literal_area(Eterm *n_hp, Eterm *n_htop, + char* new_heap, Uint new_heap_size, + char* old_heap, Uint old_heap_size, + char* src, Uint src_size) +{ + return sweep(n_hp, n_htop, + ErtsSweepLiteralArea, + new_heap, new_heap_size, + old_heap, old_heap_size, + src, src_size); } static Eterm* -sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, char* src, Uint src_size) +sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, + char* src, Uint src_size) { while (heap_ptr < heap_end) { Eterm* ptr; @@ -1872,7 +1872,7 @@ sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, char* src, Uint sr if (IS_MOVED_BOXED(val)) { ASSERT(is_boxed(val)); *heap_ptr++ = val; - } else if (in_area(ptr, src, src_size)) { + } else if (ErtsInArea(ptr, src, src_size)) { MOVE_BOXED(ptr,val,htop,heap_ptr++); } else { heap_ptr++; @@ -1884,7 +1884,7 @@ sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, char* src, Uint sr val = *ptr; if (IS_MOVED_CONS(val)) { *heap_ptr++ = ptr[1]; - } else if (in_area(ptr, src, src_size)) { + } else if (ErtsInArea(ptr, src, src_size)) { MOVE_CONS(ptr,val,htop,heap_ptr++); } else { heap_ptr++; @@ -1905,7 +1905,7 @@ sweep_one_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, char* src, Uint sr if (IS_MOVED_BOXED(val)) { *origptr = val; mb->base = binary_bytes(*origptr); - } else if (in_area(ptr, src, src_size)) { + } else if (ErtsInArea(ptr, src, src_size)) { MOVE_BOXED(ptr,val,htop,origptr); mb->base = binary_bytes(*origptr); } @@ -2340,11 +2340,11 @@ sweep_off_heap(Process *p, int fullsweep) */ while (ptr) { if (IS_MOVED_BOXED(ptr->thing_word)) { - ASSERT(!in_area(ptr, oheap, oheap_sz)); + ASSERT(!ErtsInArea(ptr, oheap, oheap_sz)); *prev = ptr = (struct erl_off_heap_header*) boxed_val(ptr->thing_word); ASSERT(!IS_MOVED_BOXED(ptr->thing_word)); if (ptr->thing_word == HEADER_PROC_BIN) { - int to_new_heap = !in_area(ptr, oheap, oheap_sz); + int to_new_heap = !ErtsInArea(ptr, oheap, oheap_sz); ASSERT(to_new_heap == !seen_mature || (!to_new_heap && (seen_mature=1))); if (to_new_heap) { bin_vheap += ptr->size / sizeof(Eterm); @@ -2358,7 +2358,7 @@ sweep_off_heap(Process *p, int fullsweep) ptr = ptr->next; } } - else if (!in_area(ptr, oheap, oheap_sz)) { + else if (!ErtsInArea(ptr, oheap, oheap_sz)) { /* garbage */ switch (thing_subtag(ptr->thing_word)) { case REFC_BINARY_SUBTAG: @@ -2390,7 +2390,7 @@ sweep_off_heap(Process *p, int fullsweep) * generational collection - keep objects in list. */ while (ptr) { - ASSERT(in_area(ptr, oheap, oheap_sz)); + ASSERT(ErtsInArea(ptr, oheap, oheap_sz)); ASSERT(!IS_MOVED_BOXED(ptr->thing_word)); if (ptr->thing_word == HEADER_PROC_BIN) { BIN_OLD_VHEAP(p) += ptr->size / sizeof(Eterm); /* for binary gc (words)*/ @@ -2480,7 +2480,7 @@ offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size) switch (primary_tag(val)) { case TAG_PRIMARY_LIST: case TAG_PRIMARY_BOXED: - if (in_area(ptr_val(val), area, area_size)) { + if (ErtsInArea(ptr_val(val), area, area_size)) { *hp = offset_ptr(val, offs); } hp++; @@ -2502,7 +2502,7 @@ offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size) { struct erl_off_heap_header* oh = (struct erl_off_heap_header*) hp; - if (in_area(oh->next, area, area_size)) { + if (ErtsInArea(oh->next, area, area_size)) { Eterm** uptr = (Eterm **) (void *) &oh->next; *uptr += offs; /* Patch the mso chain */ } @@ -2512,7 +2512,7 @@ offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size) { ErlBinMatchState *ms = (ErlBinMatchState*) hp; ErlBinMatchBuffer *mb = &(ms->mb); - if (in_area(ptr_val(mb->orig), area, area_size)) { + if (ErtsInArea(ptr_val(mb->orig), area, area_size)) { mb->orig = offset_ptr(mb->orig, offs); mb->base = binary_bytes(mb->orig); } @@ -2542,7 +2542,7 @@ offset_heap_ptr(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size) switch (primary_tag(val)) { case TAG_PRIMARY_LIST: case TAG_PRIMARY_BOXED: - if (in_area(ptr_val(val), area, area_size)) { + if (ErtsInArea(ptr_val(val), area, area_size)) { *hp = offset_ptr(val, offs); } hp++; @@ -2557,7 +2557,7 @@ offset_heap_ptr(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size) static void offset_off_heap(Process* p, Sint offs, char* area, Uint area_size) { - if (MSO(p).first && in_area((Eterm *)MSO(p).first, area, area_size)) { + if (MSO(p).first && ErtsInArea((Eterm *)MSO(p).first, area, area_size)) { Eterm** uptr = (Eterm**) (void *) &MSO(p).first; *uptr += offs; } @@ -2577,19 +2577,19 @@ offset_mqueue(Process *p, Sint offs, char* area, Uint area_size) switch (primary_tag(mesg)) { case TAG_PRIMARY_LIST: case TAG_PRIMARY_BOXED: - if (in_area(ptr_val(mesg), area, area_size)) { + if (ErtsInArea(ptr_val(mesg), area, area_size)) { ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs); } break; } } mesg = ERL_MESSAGE_TOKEN(mp); - if (is_boxed(mesg) && in_area(ptr_val(mesg), area, area_size)) { + if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) { ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs); } #ifdef USE_VM_PROBES mesg = ERL_MESSAGE_DT_UTAG(mp); - if (is_boxed(mesg) && in_area(ptr_val(mesg), area, area_size)) { + if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) { ERL_MESSAGE_DT_UTAG(mp) = offset_ptr(mesg, offs); } #endif diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index d63e81cb5e..5db7ee6d7c 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -21,6 +21,19 @@ #ifndef __SYS_H__ #define __SYS_H__ +#if !defined(__GNUC__) +# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) 0 +#elif !defined(__GNUC_MINOR__) +# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ + ((__GNUC__ << 24) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) +#elif !defined(__GNUC_PATCHLEVEL__) +# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ + (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12)) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) +#else +# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ + (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12) | __GNUC_PATCHLEVEL__) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) +#endif + #ifdef ERTS_INLINE # ifndef ERTS_CAN_INLINE # define ERTS_CAN_INLINE 1 @@ -38,6 +51,17 @@ # endif #endif +#ifndef ERTS_FORCE_INLINE +# if ERTS_AT_LEAST_GCC_VSN__(3,1,1) +# define ERTS_FORCE_INLINE __inline__ __attribute__((__always_inline__)) +# elif defined(__WIN32__) +# define ERTS_FORCE_INLINE __forceinline +# endif +# ifndef ERTS_FORCE_INLINE +# define ERTS_FORCE_INLINE ERTS_INLINE +# endif +#endif + #if defined(DEBUG) || defined(ERTS_ENABLE_LOCK_CHECK) # undef ERTS_CAN_INLINE # define ERTS_CAN_INLINE 0 @@ -112,19 +136,6 @@ typedef int ErtsSysFdType; typedef ERTS_SYS_FD_TYPE ErtsSysFdType; #endif -#if !defined(__GNUC__) -# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) 0 -#elif !defined(__GNUC_MINOR__) -# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ - ((__GNUC__ << 24) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) -#elif !defined(__GNUC_PATCHLEVEL__) -# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ - (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12)) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) -#else -# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \ - (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12) | __GNUC_PATCHLEVEL__) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) -#endif - #if ERTS_AT_LEAST_GCC_VSN__(2, 96, 0) # define ERTS_LIKELY(BOOL) __builtin_expect((BOOL), !0) # define ERTS_UNLIKELY(BOOL) __builtin_expect((BOOL), 0) -- cgit v1.2.3 From 7858ca939f8bf2db918396616fee13364d150a1e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 5 Oct 2015 16:13:37 +0200 Subject: erts: Refactor line table in loaded beam code to use real C struct with correct types --- erts/emulator/beam/beam_load.c | 52 ++++++++++++++++++++-------------------- erts/emulator/beam/beam_load.h | 18 +++++++++----- erts/emulator/beam/beam_ranges.c | 37 +++++++++++++--------------- 3 files changed, 54 insertions(+), 53 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index c56e6732a0..1c598601c6 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4388,9 +4388,11 @@ freeze_code(LoaderState* stp) if (stp->line_instr == 0) { line_size = 0; } else { - line_size = (MI_LINE_FUNC_TAB + (stp->num_functions + 1) + - (stp->current_li+1) + stp->num_fnames) * - sizeof(Eterm) + (stp->current_li+1) * stp->loc_size; + line_size = (offsetof(BeamCodeLineTab,func_tab) + + (stp->num_functions + 1) * sizeof(BeamInstr**) /* func_tab */ + + (stp->current_li + 1) * sizeof(BeamInstr*) /* line items */ + + stp->num_fnames * sizeof(Eterm) /* fname table */ + + (stp->current_li + 1) * stp->loc_size); /* loc_tab */ } size = offsetof(BeamCodeHeader,functions) + (stp->ci * sizeof(BeamInstr)) + strtab_size + attr_size + compile_size + MD5_SIZE + line_size; @@ -4465,42 +4467,40 @@ freeze_code(LoaderState* stp) code_hdr->line_table = NULL; str_table = (byte *) (codev + stp->ci); } else { - Eterm* line_tab = (Eterm *) (codev+stp->ci); - Eterm* p; - int ftab_size = stp->num_functions; - int num_instrs = stp->current_li; - Eterm* first_line_item; + BeamCodeLineTab* const line_tab = (BeamCodeLineTab *) (codev+stp->ci); + const int ftab_size = stp->num_functions; + const int num_instrs = stp->current_li; + const BeamInstr** const line_items = + (const BeamInstr**) &line_tab->func_tab[ftab_size + 1]; code_hdr->line_table = line_tab; - p = line_tab + MI_LINE_FUNC_TAB; - first_line_item = (p + ftab_size + 1); for (i = 0; i < ftab_size; i++) { - *p++ = (Eterm) (BeamInstr) (first_line_item + stp->func_line[i]); + line_tab->func_tab[i] = line_items + stp->func_line[i]; } - *p++ = (Eterm) (BeamInstr) (first_line_item + num_instrs); - ASSERT(p == first_line_item); + line_tab->func_tab[i] = line_items + num_instrs; + for (i = 0; i < num_instrs; i++) { - *p++ = (Eterm) (BeamInstr) (codev + stp->line_instr[i].pos); + line_items[i] = codev + stp->line_instr[i].pos; } - *p++ = (Eterm) (BeamInstr) (codev + stp->ci - 1); + line_items[i] = codev + stp->ci - 1; - line_tab[MI_LINE_FNAME_PTR] = (Eterm) (BeamInstr) p; - memcpy(p, stp->fname, stp->num_fnames*sizeof(Eterm)); - p += stp->num_fnames; + line_tab->fname_ptr = (Eterm*) &line_items[i + 1]; + memcpy(line_tab->fname_ptr, stp->fname, stp->num_fnames*sizeof(Eterm)); - line_tab[MI_LINE_LOC_TAB] = (Eterm) (BeamInstr) p; - line_tab[MI_LINE_LOC_SIZE] = stp->loc_size; + line_tab->loc_size = stp->loc_size; if (stp->loc_size == 2) { - Uint16* locp = (Uint16 *) p; - for (i = 0; i < num_instrs; i++) { + Uint16* locp = (Uint16 *) &line_tab->fname_ptr[stp->num_fnames]; + line_tab->loc_tab.p2 = locp; + for (i = 0; i < num_instrs; i++) { *locp++ = (Uint16) stp->line_instr[i].loc; - } - *locp++ = LINE_INVALID_LOCATION; + } + *locp++ = LINE_INVALID_LOCATION; str_table = (byte *) locp; } else { - Uint32* locp = (Uint32 *) p; - ASSERT(stp->loc_size == 4); + Uint32* locp = (Uint32 *) &line_tab->fname_ptr[stp->num_fnames]; + ASSERT(stp->loc_size == 4); + line_tab->loc_tab.p4 = locp; for (i = 0; i < num_instrs; i++) { *locp++ = stp->line_instr[i].loc; } diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index c86ac65521..22ab71c868 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -61,6 +61,8 @@ extern BeamInstr* em_call_nif; /* Total code size in bytes */ extern Uint erts_total_code_size; +typedef struct BeamCodeLineTab_ BeamCodeLineTab; + /* * Header of code chunks which contains additional information * about the loaded module. @@ -100,7 +102,7 @@ typedef struct beam_code_header { /* * Pointer to the line table (or NULL if none). */ - Eterm* line_table; + BeamCodeLineTab* line_table; /* * Pointer to the module MD5 sum (16 bytes) @@ -124,11 +126,15 @@ int erts_is_module_native(BeamCodeHeader* code); /* * Layout of the line table. */ - -#define MI_LINE_FNAME_PTR 0 -#define MI_LINE_LOC_TAB 1 -#define MI_LINE_LOC_SIZE 2 -#define MI_LINE_FUNC_TAB 3 +struct BeamCodeLineTab_ { + Eterm* fname_ptr; + int loc_size; + union { + Uint16* p2; + Uint32* p4; + }loc_tab; + const BeamInstr** func_tab[1]; +}; #define LINE_INVALID_LOCATION (0) diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c index e6cf43446e..5a2b66727a 100644 --- a/erts/emulator/beam/beam_ranges.c +++ b/erts/emulator/beam/beam_ranges.c @@ -37,7 +37,7 @@ typedef struct { #define RANGE_END(R) ((BeamInstr*)erts_smp_atomic_read_nob(&(R)->end)) static Range* find_range(BeamInstr* pc); -static void lookup_loc(FunctionInfo* fi, BeamInstr* pc, +static void lookup_loc(FunctionInfo* fi, const BeamInstr* pc, BeamCodeHeader*, int idx); /* @@ -296,39 +296,34 @@ find_range(BeamInstr* pc) } static void -lookup_loc(FunctionInfo* fi, BeamInstr* orig_pc, BeamCodeHeader* code_hdr, int idx) +lookup_loc(FunctionInfo* fi, const BeamInstr* pc, + BeamCodeHeader* code_hdr, int idx) { - Eterm* line = code_hdr->line_table; - Eterm* low; - Eterm* high; - Eterm* mid; - Eterm pc; + BeamCodeLineTab* lt = code_hdr->line_table; + const BeamInstr** low; + const BeamInstr** high; + const BeamInstr** mid; - if (line == 0) { + if (lt == NULL) { return; } - pc = (Eterm) (BeamInstr) orig_pc; - fi->fname_ptr = (Eterm *) (BeamInstr) line[MI_LINE_FNAME_PTR]; - low = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx]; - high = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx+1]; + fi->fname_ptr = lt->fname_ptr; + low = lt->func_tab[idx]; + high = lt->func_tab[idx+1]; while (high > low) { mid = low + (high-low) / 2; if (pc < mid[0]) { high = mid; } else if (pc < mid[1]) { int file; - int index = mid - (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB]; + int index = mid - lt->func_tab[0]; - if (line[MI_LINE_LOC_SIZE] == 2) { - Uint16* loc_table = - (Uint16 *) (BeamInstr) line[MI_LINE_LOC_TAB]; - fi->loc = loc_table[index]; + if (lt->loc_size == 2) { + fi->loc = lt->loc_tab.p2[index]; } else { - Uint32* loc_table = - (Uint32 *) (BeamInstr) line[MI_LINE_LOC_TAB]; - ASSERT(line[MI_LINE_LOC_SIZE] == 4); - fi->loc = loc_table[index]; + ASSERT(lt->loc_size == 4); + fi->loc = lt->loc_tab.p4[index]; } if (fi->loc == LINE_INVALID_LOCATION) { return; -- cgit v1.2.3 From 3ac08f9b668613a4292436979eacc61863c2ab94 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 16 Sep 2015 15:02:50 +0200 Subject: Fragmented young heap generation and off_heap_message_queue option * The youngest generation of the heap can now consist of multiple blocks. Heap fragments and message fragments are added to the youngest generation when needed without triggering a GC. After a GC the youngest generation is contained in one single block. * The off_heap_message_queue process flag has been added. When enabled all message data in the queue is kept off heap. When a message is selected from the queue, the message fragment (or heap fragment) containing the actual message is attached to the youngest generation. Messages stored off heap is not part of GC. --- erts/doc/src/erl.xml | 15 + erts/doc/src/erlang.xml | 77 +- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/beam_bif_load.c | 216 +++-- erts/emulator/beam/beam_emu.c | 142 ++-- erts/emulator/beam/beam_load.c | 6 +- erts/emulator/beam/bif.c | 39 +- erts/emulator/beam/bif.h | 48 +- erts/emulator/beam/break.c | 4 +- erts/emulator/beam/copy.c | 4 +- erts/emulator/beam/dist.c | 34 +- erts/emulator/beam/erl_alloc.c | 21 +- erts/emulator/beam/erl_alloc.h | 4 +- erts/emulator/beam/erl_alloc.types | 2 + erts/emulator/beam/erl_bif_ddll.c | 17 +- erts/emulator/beam/erl_bif_info.c | 270 +++--- erts/emulator/beam/erl_debug.c | 6 +- erts/emulator/beam/erl_gc.c | 688 ++++++++------- erts/emulator/beam/erl_gc.h | 27 +- erts/emulator/beam/erl_hl_timer.c | 85 +- erts/emulator/beam/erl_init.c | 26 +- erts/emulator/beam/erl_message.c | 1509 ++++++++++++++++++++------------- erts/emulator/beam/erl_message.h | 277 ++++-- erts/emulator/beam/erl_nif.c | 7 +- erts/emulator/beam/erl_node_tables.c | 68 +- erts/emulator/beam/erl_process.c | 346 ++++---- erts/emulator/beam/erl_process.h | 106 ++- erts/emulator/beam/erl_process_dump.c | 13 +- erts/emulator/beam/erl_process_lock.h | 6 - erts/emulator/beam/erl_time_sup.c | 7 +- erts/emulator/beam/erl_trace.c | 68 +- erts/emulator/beam/global.h | 123 +-- erts/emulator/beam/io.c | 113 +-- erts/emulator/beam/sys.h | 2 + erts/emulator/beam/utils.c | 17 +- erts/emulator/hipe/hipe_gc.c | 36 +- erts/emulator/hipe/hipe_native_bif.c | 34 +- erts/etc/common/erlexec.c | 22 +- erts/preloaded/ebin/erlang.beam | Bin 101840 -> 101544 bytes erts/preloaded/src/erlang.erl | 44 +- 40 files changed, 2549 insertions(+), 1981 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index b0322b7d43..8c1be4dff5 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1335,6 +1335,21 @@ error_logger(3) for further information.

+ + +

Default process flag settings.

+ + +xohmq true|false +

+ Sets the default value for the process flag + off_heap_message_queue. If +xohmq is not + passed, false will be the default. For more information, + see the documentation of + process_flag(off_heap_message_queue, + OHMQ). +

+
+

Miscellaneous flags.

diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index e77532463e..9426d30390 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4058,8 +4058,46 @@ os_prompt% process.

Returns the old value of the flag.

+ + Set process flag off_heap_message_queue for the calling process + +

This flag determines how messages in the message queue + are stored. When the flag is:

+ + true +

+ All messages in the message queue will be stored + outside of the process heap. This implies that no + messages in the message queue will be part of a garbage + collection of the process. +

+ false +

+ Messages may be placed either on the heap or outside + of the heap. +

+
+

+ If the process potentially may get a hugh amount of messages, + you are recommended to set the flag to true. This since + a garbage collection with lots of messages placed on the heap + may become extremly expensive. Performance of the actual + message passing is however generally better when setting the + flag to false. +

+

+ When changing this flag from false to true, + all messages in the message queue are moved off heap. This + work has been initiated but not completed when this function + call returns. +

+

Returns the old value of the flag.

+
+
+ + Set process flag priority for the calling process @@ -4138,7 +4176,7 @@ os_prompt% - + Set process flag save_calls for the calling process

N must be an integer in the interval 0..10000. @@ -4162,7 +4200,7 @@ os_prompt% - + Set process flag sensitive for the calling process

Set or clear the sensitive flag for the current process. @@ -4408,6 +4446,14 @@ os_prompt% monitor by name, the list item is {process, {RegName, Node}}.

+ {off_heap_message_queue, OHMQ} + +

Returns the current state of the off_heap_message_queue + process flag. OHMQ is either true, or + false. For more information, see the documentation of + process_flag(off_heap_message_queue, + OHMQ).

+
{priority, Level}

Level is the current priority level for @@ -5067,6 +5113,7 @@ true + Create a new process with a fun as entry point

Returns the pid of a new process started by the application @@ -5081,6 +5128,7 @@ true + Create a new process with a fun as entry point on a given node

Returns the pid of a new process started by the application @@ -5093,6 +5141,7 @@ true + Create a new process with a function as entry point

Works exactly like @@ -5188,6 +5237,18 @@ true fine-tuning an application and to measure the execution time with various VSize values.

+ {off_heap_message_queue, OHMQ} + +

Sets the state of the off_heap_message_queue process + flag. OHMQ should be either true, or + false. The default off_heap_message_queue process + flag is determined by the + +xohmq erl + command line argument. For more information, see the + documentation of + process_flag(off_heap_message_queue, + OHMQ).

+
@@ -5195,6 +5256,7 @@ true + Create a new process with a function as entry point on a given node

Returns the pid of a new process started by the application @@ -6224,6 +6286,7 @@ ok + Information about the system

Returns various information about the current system @@ -6614,6 +6677,16 @@ ok

Returns a string containing the erlang NIF version used by the runtime system. It will be on the form "<major ver>.<minor ver>".

+ off_heap_message_queue + +

Returns the default value of the off_heap_message_queue + process flag which is either true or false. This + default is set by the erl command line argument + +xohmq. For more information on the + off_heap_message_queue process flag, see documentation of + process_flag(off_heap_message_queue, + OHMQ).

+
otp_release

Returns a string containing the OTP release number of the diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 6328b3d18f..f142cf1142 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -423,6 +423,7 @@ atom notify atom notsup atom nouse_stdio atom objects +atom off_heap_message_queue atom offset atom ok atom old_heap_block_size diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 2c275c4649..22b4e26c77 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -723,29 +723,50 @@ set_default_trace_pattern(Eterm module) } } +static ERTS_INLINE int +check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size) +{ + struct erl_off_heap_header* oh; + for (oh = off_heap->first; oh; oh = oh->next) { + if (thing_subtag(oh->thing_word) == FUN_SUBTAG) { + ErlFunThing* funp = (ErlFunThing*) oh; + if (ErtsInArea(funp->fe->address, area, area_size)) + return !0; + } + } + return 0; +} + + static Eterm check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) { BeamInstr* start; char* literals; Uint lit_bsize; - BeamInstr* end; + char* mod_start; + Uint mod_size; Eterm* sp; - struct erl_off_heap_header* oh; int done_gc = 0; + int need_gc = 0; + ErtsMessage *msgp; + ErlHeapFragment *hfrag; -#define INSIDE(a) (start <= (a) && (a) < end) +#define ERTS_ORDINARY_GC__ (1 << 0) +#define ERTS_LITERAL_GC__ (1 << 1) /* * Pick up limits for the module. */ start = (BeamInstr*) modp->old.code_hdr; - end = (BeamInstr *)((char *)start + modp->old.code_length); + mod_start = (char *) start; + mod_size = modp->old.code_length; /* * Check if current instruction or continuation pointer points into module. */ - if (INSIDE(rp->i) || INSIDE(rp->cp)) { + if (ErtsInArea(rp->i, mod_start, mod_size) + || ErtsInArea(rp->cp, mod_start, mod_size)) { return am_true; } @@ -753,7 +774,7 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) * Check all continuation pointers stored on the stack. */ for (sp = rp->stop; sp < STACK_START(rp); sp++) { - if (is_CP(*sp) && INSIDE(cp_val(*sp))) { + if (is_CP(*sp) && ErtsInArea(cp_val(*sp), mod_start, mod_size)) { return am_true; } } @@ -767,15 +788,15 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) struct StackTrace *s; ASSERT(is_list(rp->ftrace)); s = (struct StackTrace *) big_val(CDR(list_val(rp->ftrace))); - if ((s->pc && INSIDE(s->pc)) || - (s->current && INSIDE(s->current))) { + if ((s->pc && ErtsInArea(s->pc, mod_start, mod_size)) || + (s->current && ErtsInArea(s->current, mod_start, mod_size))) { rp->freason = EXC_NULL; rp->fvalue = NIL; rp->ftrace = NIL; } else { int i; for (i = 0; i < s->depth; i++) { - if (INSIDE(s->trace[i])) { + if (ErtsInArea(s->trace[i], mod_start, mod_size)) { rp->freason = EXC_NULL; rp->fvalue = NIL; rp->ftrace = NIL; @@ -796,108 +817,141 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) } /* - * See if there are funs that refer to the old version of the module. + * Message queue can contains funs, but (at least currently) no + * constants. If we got references to this module from the message + * queue, a GC cannot remove these... */ - rescan: - for (oh = MSO(rp).first; oh; oh = oh->next) { - if (thing_subtag(oh->thing_word) == FUN_SUBTAG) { - ErlFunThing* funp = (ErlFunThing*) oh; + erts_smp_proc_lock(rp, ERTS_PROC_LOCK_MSGQ); + ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp); + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ); - if (INSIDE((BeamInstr *) funp->fe->address)) { - if (done_gc) { - return am_true; - } else { - if (!allow_gc) - return am_aborted; - /* - * Try to get rid of this fun by garbage collecting. - * Clear both fvalue and ftrace to make sure they - * don't hold any funs. - */ - rp->freason = EXC_NULL; - rp->fvalue = NIL; - rp->ftrace = NIL; - done_gc = 1; - FLAGS(rp) |= F_NEED_FULLSWEEP; - *redsp += erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); - goto rescan; - } - } + for (msgp = rp->msg.first; msgp; msgp = msgp->next) { + if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) + hfrag = &msgp->hfrag; + else if (is_value(ERL_MESSAGE_TERM(msgp)) && msgp->data.heap_frag) + hfrag = msgp->data.heap_frag; + else + continue; + for (; hfrag; hfrag = hfrag->next) { + if (check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size)) + return am_true; + /* Should not contain any constants... */ + ASSERT(!any_heap_ref_ptrs(&hfrag->mem[0], + &hfrag->mem[hfrag->used_size], + mod_start, + mod_size)); } } - /* - * See if there are constants inside the module referenced by the process. - */ - done_gc = 0; literals = (char*) modp->old.code_hdr->literals_start; lit_bsize = (char*) modp->old.code_hdr->literals_end - literals; - for (;;) { - ErlMessage* mp; + while (1) { + + /* Check heap, stack etc... */ + if (check_mod_funs(rp, &rp->off_heap, mod_start, mod_size)) + goto try_gc; if (any_heap_ref_ptrs(&rp->fvalue, &rp->fvalue+1, literals, lit_bsize)) { rp->freason = EXC_NULL; rp->fvalue = NIL; rp->ftrace = NIL; } - if (any_heap_ref_ptrs(rp->stop, rp->hend, literals, lit_bsize)) { - goto need_gc; - } - if (any_heap_refs(rp->heap, rp->htop, literals, lit_bsize)) { - goto need_gc; - } + if (any_heap_ref_ptrs(rp->stop, rp->hend, literals, lit_bsize)) + goto try_literal_gc; + if (any_heap_refs(rp->heap, rp->htop, literals, lit_bsize)) + goto try_literal_gc; + if (any_heap_refs(rp->old_heap, rp->old_htop, literals, lit_bsize)) + goto try_literal_gc; + + /* Check dictionary */ + if (rp->dictionary) { + Eterm* start = rp->dictionary->data; + Eterm* end = start + rp->dictionary->used; - if (any_heap_refs(rp->old_heap, rp->old_htop, literals, lit_bsize)) { - goto need_gc; + if (any_heap_ref_ptrs(start, end, literals, lit_bsize)) + goto try_literal_gc; } - if (rp->dictionary != NULL) { - Eterm* start = rp->dictionary->data; - Eterm* end = start + rp->dictionary->used; + /* Check heap fragments */ + for (hfrag = rp->mbuf; hfrag; hfrag = hfrag->next) { + Eterm *hp, *hp_end; + /* Off heap lists should already have been moved into process */ + ASSERT(!check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size)); - if (any_heap_ref_ptrs(start, end, literals, lit_bsize)) { - goto need_gc; - } + hp = &hfrag->mem[0]; + hp_end = &hfrag->mem[hfrag->used_size]; + if (any_heap_ref_ptrs(hp, hp_end, mod_start, lit_bsize)) + goto try_literal_gc; } - for (mp = rp->msg.first; mp != NULL; mp = mp->next) { - if (any_heap_ref_ptrs(mp->m, mp->m+2, literals, lit_bsize)) { - goto need_gc; +#ifdef DEBUG + /* + * Message buffer fragments should not have any references + * to constants, and off heap lists should already have + * been moved into process off heap structure. + */ + for (msgp = rp->msg_frag; msgp; msgp = msgp->next) { + if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) + hfrag = &msgp->hfrag; + else + hfrag = msgp->data.heap_frag; + for (; hfrag; hfrag = hfrag->next) { + Eterm *hp, *hp_end; + ASSERT(!check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size)); + + hp = &hfrag->mem[0]; + hp_end = &hfrag->mem[hfrag->used_size]; + ASSERT(!any_heap_ref_ptrs(hp, hp_end, mod_start, lit_bsize)); } } - break; - need_gc: - if (done_gc) { +#endif + + return am_false; + + try_literal_gc: + need_gc |= ERTS_LITERAL_GC__; + + try_gc: + need_gc |= ERTS_ORDINARY_GC__; + + if ((done_gc & need_gc) == need_gc) return am_true; - } else { - struct erl_off_heap_header* oh; - if (!allow_gc) - return am_aborted; + if (!allow_gc) + return am_aborted; - /* - * Try to get rid of constants by by garbage collecting. - * Clear both fvalue and ftrace. - */ - rp->freason = EXC_NULL; - rp->fvalue = NIL; - rp->ftrace = NIL; - done_gc = 1; + need_gc &= ~done_gc; + + /* + * Try to get rid of constants by by garbage collecting. + * Clear both fvalue and ftrace. + */ + + rp->freason = EXC_NULL; + rp->fvalue = NIL; + rp->ftrace = NIL; + + if (need_gc & ERTS_ORDINARY_GC__) { FLAGS(rp) |= F_NEED_FULLSWEEP; *redsp += erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); + done_gc |= ERTS_ORDINARY_GC__; + } + if (need_gc & ERTS_LITERAL_GC__) { + struct erl_off_heap_header* oh; oh = modp->old.code_hdr->literals_off_heap; *redsp += lit_bsize / 64; /* Need, better value... */ erts_garbage_collect_literals(rp, (Eterm*)literals, lit_bsize, oh); + done_gc |= ERTS_LITERAL_GC__; } + need_gc = 0; } - return am_false; -#undef INSIDE -} -#define in_area(ptr,start,nbytes) \ - ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes)) +#undef ERTS_ORDINARY_GC__ +#undef ERTS_LITERAL_GC__ + +} static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) @@ -910,7 +964,7 @@ any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) switch (primary_tag(val)) { case TAG_PRIMARY_BOXED: case TAG_PRIMARY_LIST: - if (in_area(val, mod_start, mod_size)) { + if (ErtsInArea(val, mod_start, mod_size)) { return 1; } break; @@ -930,7 +984,7 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) switch (primary_tag(val)) { case TAG_PRIMARY_BOXED: case TAG_PRIMARY_LIST: - if (in_area(val, mod_start, mod_size)) { + if (ErtsInArea(val, mod_start, mod_size)) { return 1; } break; @@ -940,7 +994,7 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) if (header_is_bin_matchstate(val)) { ErlBinMatchState *ms = (ErlBinMatchState*) p; ErlBinMatchBuffer *mb = &(ms->mb); - if (in_area(mb->orig, mod_start, mod_size)) { + if (ErtsInArea(mb->orig, mod_start, mod_size)) { return 1; } } @@ -953,8 +1007,6 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) return 0; } -#undef in_area - BIF_RETTYPE purge_module_1(BIF_ALIST_1) { ErtsCodeIndex code_ix; diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 4d19f52a52..1dd56ff989 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1654,10 +1654,6 @@ void process_main(void) ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); - if (c_p->mbuf || MSO(c_p).overhead >= BIN_VHEAP_SZ(c_p)) { - result = erts_gc_after_bif_call(c_p, result, reg, 2); - E = c_p->stop; - } HTOP = HEAP_TOP(c_p); FCALLS = c_p->fcalls; if (is_value(result)) { @@ -1745,8 +1741,7 @@ void process_main(void) SWAPIN; } /* only x(2) is included in the rootset here */ - if (E - HTOP < 3 || c_p->mbuf) { /* Force GC in case add_stacktrace() - * created heap fragments */ + if (E - HTOP < 3) { SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); FCALLS -= erts_garbage_collect(c_p, 3, reg+2, 1); @@ -1833,10 +1828,17 @@ void process_main(void) OpCase(i_loop_rec_f): { BeamInstr *next; - ErlMessage* msgp; + ErtsMessage* msgp; - loop_rec__: + /* + * We need to disable GC while matching messages + * in the queue. This since messages with data outside + * the heap will be corrupted by a GC. + */ + ASSERT(!(c_p->flags & F_DISABLE_GC)); + c_p->flags |= F_DISABLE_GC; + loop_rec__: PROCESS_MAIN_CHK_LOCKS(c_p); msgp = PEEK_MESSAGE(c_p); @@ -1848,6 +1850,7 @@ void process_main(void) if (ERTS_PROC_PENDING_EXIT(c_p)) { erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); SWAPOUT; + c_p->flags &= ~F_DISABLE_GC; goto do_schedule; /* Will be rescheduled for exit */ } ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); @@ -1857,30 +1860,27 @@ void process_main(void) else #endif { + c_p->flags &= ~F_DISABLE_GC; SET_I((BeamInstr *) Arg(0)); Goto(*I); /* Jump to a wait or wait_timeout instruction */ } } - ErtsMoveMsgAttachmentIntoProc(msgp, c_p, E, HTOP, FCALLS, - { - SWAPOUT; - PROCESS_MAIN_CHK_LOCKS(c_p); - }, - { - ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - PROCESS_MAIN_CHK_LOCKS(c_p); - SWAPIN; - }); if (is_non_value(ERL_MESSAGE_TERM(msgp))) { - /* - * A corrupt distribution message that we weren't able to decode; - * remove it... - */ - ASSERT(!msgp->data.attached); - /* TODO: Add DTrace probe for this bad message situation? */ - UNLINK_MESSAGE(c_p, msgp); - free_message(msgp); - goto loop_rec__; + SWAPOUT; /* erts_decode_dist_message() may write to heap... */ + if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) { + /* + * A corrupt distribution message that we weren't able to decode; + * remove it... + */ + /* No swapin should be needed */ + ASSERT(HTOP == c_p->htop && E == c_p->stop); + /* TODO: Add DTrace probe for this bad message situation? */ + UNLINK_MESSAGE(c_p, msgp); + msgp->next = NULL; + erts_cleanup_messages(msgp); + goto loop_rec__; + } + SWAPIN; } PreFetch(1, next); r(0) = ERL_MESSAGE_TERM(msgp); @@ -1892,8 +1892,7 @@ void process_main(void) */ OpCase(remove_message): { BeamInstr *next; - ErlMessage* msgp; - + ErtsMessage* msgp; PROCESS_MAIN_CHK_LOCKS(c_p); PreFetch(0, next); @@ -1988,11 +1987,21 @@ void process_main(void) UNLINK_MESSAGE(c_p, msgp); JOIN_MESSAGE(c_p); CANCEL_TIMER(c_p); - free_message(msgp); + + erts_save_message_in_proc(c_p, msgp); + c_p->flags &= ~F_DISABLE_GC; + + if (ERTS_IS_GC_DESIRED_INTERNAL(c_p, HTOP, E)) { + /* + * We want to GC soon but we leave a few + * reductions giving the message some time + * to turn into garbage. + */ + ERTS_VBUMP_LEAVE_REDS_INTERNAL(c_p, 5, FCALLS); + } ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); - NextPF(0, next); } @@ -2001,9 +2010,22 @@ void process_main(void) * message didn't match), then jump to the loop_rec instruction. */ OpCase(loop_rec_end_f): { + + ASSERT(c_p->flags & F_DISABLE_GC); + SET_I((BeamInstr *) Arg(0)); SAVE_MESSAGE(c_p); - goto loop_rec__; + if (FCALLS > 0 || FCALLS > neg_o_reds) { + FCALLS--; + goto loop_rec__; + } + + c_p->flags &= ~F_DISABLE_GC; + c_p->i = I; + SWAPOUT; + c_p->arity = 0; + c_p->current = NULL; + goto do_schedule; } /* * Prepare to wait for a message or a timeout, whichever occurs first. @@ -2733,6 +2755,7 @@ do { \ Eterm (*bf)(Process*, Eterm*, BeamInstr*) = GET_BIF_ADDRESS(Arg(0)); Eterm result; BeamInstr *next; + ErlHeapFragment *live_hf_end; PRE_BIF_SWAPOUT(c_p); c_p->fcalls = FCALLS - 1; @@ -2742,17 +2765,18 @@ do { \ PreFetch(1, next); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); + live_hf_end = c_p->mbuf; result = (*bf)(c_p, reg, I); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_HOLE_CHECK(c_p); ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); - PROCESS_MAIN_CHK_LOCKS(c_p); - if (c_p->mbuf || MSO(c_p).overhead >= BIN_VHEAP_SZ(c_p)) { + if (ERTS_IS_GC_DESIRED(c_p)) { Uint arity = ((Export *)Arg(0))->code[2]; - result = erts_gc_after_bif_call(c_p, result, reg, arity); + result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, result, reg, arity); E = c_p->stop; } + PROCESS_MAIN_CHK_LOCKS(c_p); HTOP = HEAP_TOP(c_p); FCALLS = c_p->fcalls; if (is_value(result)) { @@ -3414,9 +3438,6 @@ do { \ goto do_schedule; } else { ASSERT(!is_value(r(0))); - if (c_p->mbuf) { - erts_garbage_collect(c_p, 0, reg+1, 3); - } SWAPIN; Goto(*I); } @@ -3440,6 +3461,7 @@ do { \ * I[3]: Function pointer to dirty NIF */ BifFunction vbf; + ErlHeapFragment *live_hf_end; DTRACE_NIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); c_p->current = I-3; /* current and vbf set to please handle_error */ @@ -3455,6 +3477,7 @@ do { \ NifF* fp = vbf = (NifF*) I[1]; struct enif_environment_t env; erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2]); + live_hf_end = c_p->mbuf; nif_bif_result = (*fp)(&env, bif_nif_arity, reg); if (env.exception_thrown) nif_bif_result = THE_NON_VALUE; @@ -3497,6 +3520,7 @@ do { \ { Eterm (*bf)(Process*, Eterm*, BeamInstr*) = vbf; ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + live_hf_end = c_p->mbuf; nif_bif_result = (*bf)(c_p, reg, I); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result)); @@ -3509,9 +3533,17 @@ do { \ apply_bif_or_nif_epilogue: ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); ERTS_HOLE_CHECK(c_p); - if (c_p->mbuf) { - nif_bif_result = erts_gc_after_bif_call(c_p, nif_bif_result, - reg, bif_nif_arity); + /* + * We want to test with ERTS_IS_GC_DESIRED(c_p) in order + * to trigger gc due to binaries based on same conditions + * regardless of how the bif is called. This change will + * however be introduced in a separate commit in order to + * easier identify why the characteristics changed. + */ + if (c_p->stop - c_p->htop < c_p->mbuf_sz) { + nif_bif_result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, + nif_bif_result, + reg, bif_nif_arity); } SWAPIN; /* There might have been a garbage collection. */ FCALLS = c_p->fcalls; @@ -6340,13 +6372,6 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) erts_factory_proc_init(&factory, p); res = erts_hashmap_from_array(&factory, thp, n/2, 0); erts_factory_close(&factory); - if (p->mbuf) { - Uint live = Arg(2); - reg[live] = res; - erts_garbage_collect(p, 0, reg, live+1); - res = reg[live]; - E = p->stop; - } return res; } @@ -6412,13 +6437,6 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) hx = hashmap_make_hash(new_key); res = erts_hashmap_insert(p, hx, new_key, val, res, 0); - if (p->mbuf) { - Uint live = Arg(3); - reg[live] = res; - erts_garbage_collect(p, 0, reg, live+1); - res = reg[live]; - E = p->stop; - } new_p += 2; } @@ -6578,12 +6596,6 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) /* The expensive case, need to build a hashmap */ if (n > MAP_SMALL_MAP_LIMIT) { res = erts_hashmap_from_ks_and_vs(p,flatmap_get_keys(mp),flatmap_get_values(mp),n); - if (p->mbuf) { - Uint live = Arg(3); - reg[live] = res; - erts_garbage_collect(p, 0, reg, live+1); - res = reg[live]; - } } return res; } @@ -6639,14 +6651,6 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) return res; } - if (p->mbuf) { - Uint live = Arg(3); - reg[live] = res; - erts_garbage_collect(p, 0, reg, live+1); - res = reg[live]; - E = p->stop; - } - new_p += 2; } return res; diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 7a1a563be2..5db971b6af 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -902,7 +902,7 @@ static ErlHeapFragment* new_literal_fragment(Uint size) ErlHeapFragment* bp; bp = (ErlHeapFragment*) ERTS_HEAP_ALLOC(ERTS_ALC_T_PREPARED_CODE, ERTS_HEAP_FRAG_SIZE(size)); - ERTS_INIT_HEAP_FRAG(bp, size); + ERTS_INIT_HEAP_FRAG(bp, size, size); return bp; } @@ -1528,8 +1528,8 @@ read_literal_table(LoaderState* stp) } if (heap_size > 0) { - erts_factory_message_init(&factory, NULL, NULL, - new_literal_fragment(heap_size)); + erts_factory_heap_frag_init(&factory, + new_literal_fragment(heap_size)); factory.alloc_type = ERTS_ALC_T_PREPARED_CODE; val = erts_decode_ext(&factory, &p); diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 5ec1840c7b..e4283ac945 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -72,7 +72,7 @@ BIF_RETTYPE spawn_3(BIF_ALIST_3) ErlSpawnOpts so; Eterm pid; - so.flags = 0; + so.flags = erts_default_spo_flags; pid = erl_create_process(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &so); if (is_non_value(pid)) { BIF_ERROR(BIF_P, so.error_code); @@ -589,7 +589,7 @@ erts_queue_monitor_message(Process *p, Eterm reason_copy, ref_copy, item_copy; Uint reason_size, ref_size, item_size, heap_size; ErlOffHeap *ohp; - ErlHeapFragment *bp; + ErtsMessage *msgp; reason_size = IS_CONST(reason) ? 0 : size_object(reason); item_size = IS_CONST(item) ? 0 : size_object(item); @@ -597,11 +597,8 @@ erts_queue_monitor_message(Process *p, heap_size = 6+reason_size+ref_size+item_size; - hp = erts_alloc_message_heap(heap_size, - &bp, - &ohp, - p, - p_locksp); + msgp = erts_alloc_message_heap(p, p_locksp, heap_size, + &hp, &ohp); reason_copy = (IS_CONST(reason) ? reason @@ -612,7 +609,7 @@ erts_queue_monitor_message(Process *p, ref_copy = copy_struct(ref, ref_size, &hp, ohp); tup = TUPLE5(hp, am_DOWN, ref_copy, type, item_copy, reason_copy); - erts_queue_message(p, p_locksp, bp, tup, NIL); + erts_queue_message(p, p_locksp, msgp, tup, NIL); } static BIF_RETTYPE @@ -841,7 +838,7 @@ BIF_RETTYPE spawn_link_3(BIF_ALIST_3) ErlSpawnOpts so; Eterm pid; - so.flags = SPO_LINK; + so.flags = erts_default_spo_flags|SPO_LINK; pid = erl_create_process(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &so); if (is_non_value(pid)) { BIF_ERROR(BIF_P, so.error_code); @@ -878,7 +875,7 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1) /* * Store default values for options. */ - so.flags = SPO_USE_ARGS; + so.flags = erts_default_spo_flags|SPO_USE_ARGS; so.min_heap_size = H_MIN_SIZE; so.min_vheap_size = BIN_VH_MIN_SIZE; so.priority = PRIORITY_NORMAL; @@ -913,6 +910,13 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1) so.priority = PRIORITY_LOW; else goto error; + } else if (arg == am_off_heap_message_queue) { + if (val == am_true) + so.flags |= SPO_OFF_HEAP_MSGQ; + else if (val == am_false) + so.flags &= ~SPO_OFF_HEAP_MSGQ; + else + goto error; } else if (arg == am_min_heap_size && is_small(val)) { Sint min_heap_size = signed_val(val); if (min_heap_size < 0) { @@ -1691,6 +1695,17 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2) } BIF_RET(old_value); } + else if (BIF_ARG_1 == am_off_heap_message_queue) { + int enable; + if (BIF_ARG_2 == am_true) + enable = 1; + else if (BIF_ARG_2 == am_false) + enable = 0; + else + goto error; + old_value = erts_change_off_heap_message_queue_state(BIF_P, enable); + BIF_RET(old_value); + } else if (BIF_ARG_1 == am_sensitive) { Uint is_sensitive; if (BIF_ARG_2 == am_true) { @@ -1931,7 +1946,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) } else if (is_atom(to)) { Eterm id = erts_whereis_name_to_id(p, to); - rp = erts_proc_lookup(id); + rp = erts_proc_lookup_raw(id); if (rp) { if (IS_TRACED(p)) trace_send(p, to, msg); @@ -4479,7 +4494,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) } } else if (BIF_ARG_1 == make_small(1)) { int i, max; - ErlMessage* mp; + ErtsMessage* mp; erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_smp_thr_progress_block(); diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index c6ed60376a..a62eddf36b 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -54,22 +54,24 @@ extern Export *erts_convert_time_unit_trap; (p)->fcalls = -CONTEXT_REDS; \ } while(0) - -#define ERTS_VBUMP_ALL_REDS(p) \ +#define ERTS_VBUMP_ALL_REDS_INTERNAL(p, fcalls) \ do { \ if (!ERTS_PROC_GET_SAVED_CALLS_BUF((p))) { \ - if ((p)->fcalls > 0) \ - ERTS_PROC_GET_SCHDATA((p))->virtual_reds += (p)->fcalls; \ - (p)->fcalls = 0; \ + if ((fcalls) > 0) \ + ERTS_PROC_GET_SCHDATA((p))->virtual_reds += (fcalls); \ + (fcalls) = 0; \ } \ else { \ - if ((p)->fcalls > -CONTEXT_REDS) \ + if ((fcalls) > -CONTEXT_REDS) \ ERTS_PROC_GET_SCHDATA((p))->virtual_reds \ - += ((p)->fcalls - (-CONTEXT_REDS)); \ - (p)->fcalls = -CONTEXT_REDS; \ + += ((fcalls) - (-CONTEXT_REDS)); \ + (fcalls) = -CONTEXT_REDS; \ } \ } while(0) +#define ERTS_VBUMP_ALL_REDS(p) \ + ERTS_VBUMP_ALL_REDS_INTERNAL((p), (p)->fcalls) + #define BUMP_REDS(p, gc) do { \ ASSERT(p); \ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p));\ @@ -110,10 +112,34 @@ do { \ } \ } while(0) -#define ERTS_BIF_REDS_LEFT(p) \ +#define ERTS_VBUMP_LEAVE_REDS_INTERNAL(P, Reds, FCalls) \ + do { \ + if (ERTS_PROC_GET_SAVED_CALLS_BUF((P))) { \ + int nreds__ = ((int)(Reds)) - CONTEXT_REDS; \ + if ((FCalls) > nreds__) { \ + ERTS_PROC_GET_SCHDATA((P))->virtual_reds \ + += (FCalls) - nreds__; \ + (FCalls) = nreds__; \ + } \ + } \ + else { \ + if ((FCalls) > (Reds)) { \ + ERTS_PROC_GET_SCHDATA((P))->virtual_reds \ + += (FCalls) - (Reds); \ + (FCalls) = (Reds); \ + } \ + } \ + } while (0) + +#define ERTS_VBUMP_LEAVE_REDS(P, Reds) \ + ERTS_VBUMP_LEAVE_REDS_INTERNAL(P, Reds, (P)->fcalls) + +#define ERTS_REDS_LEFT(p, FCalls) \ (ERTS_PROC_GET_SAVED_CALLS_BUF((p)) \ - ? ((p)->fcalls > -CONTEXT_REDS ? ((p)->fcalls - (-CONTEXT_REDS)) : 0)\ - : ((p)->fcalls > 0 ? (p)->fcalls : 0)) + ? ((FCalls) > -CONTEXT_REDS ? ((FCalls) - (-CONTEXT_REDS)) : 0) \ + : ((FCalls) > 0 ? (FCalls) : 0)) + +#define ERTS_BIF_REDS_LEFT(p) ERTS_REDS_LEFT(p, p->fcalls) #define BIF_RET2(x, gc) do { \ BUMP_REDS(BIF_P, (gc)); \ diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index c7e7411935..aa5ec123a7 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -252,7 +252,7 @@ print_process_info(int to, void *to_arg, Process *p) /* display the message queue only if there is anything in it */ if (!ERTS_IS_CRASH_DUMPING && p->msg.first != NULL && !garbing) { - ErlMessage* mp; + ErtsMessage* mp; erts_print(to, to_arg, "Message queue: ["); for (mp = p->msg.first; mp; mp = mp->next) erts_print(to, to_arg, mp->next ? "%T," : "%T", ERL_MESSAGE_TERM(mp)); @@ -323,7 +323,7 @@ print_process_info(int to, void *to_arg, Process *p) erts_print(to, to_arg, "Heap unused: %bpu\n", (p->hend - p->htop)); erts_print(to, to_arg, "OldHeap unused: %bpu\n", (OLD_HEAP(p) == NULL) ? 0 : (OLD_HEND(p) - OLD_HTOP(p)) ); - erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p)); + erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p, !0)); if (garbing) { print_garb_info(to, to_arg, p); diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index b185758b1d..f27c526413 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -279,7 +279,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) break; case TAG_PRIMARY_LIST: objp = list_val(obj); - if (in_area(objp,hstart,hsize)) { + if (ErtsInArea(objp,hstart,hsize)) { hp++; break; } @@ -318,7 +318,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } case TAG_PRIMARY_BOXED: - if (in_area(boxed_val(obj),hstart,hsize)) { + if (ErtsInArea(boxed_val(obj),hstart,hsize)) { hp++; break; } diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index efd5109269..bfddcadca3 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -373,10 +373,11 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp) ASSERT(lnk->type == LINK_NODE); if (is_internal_pid(lnk->pid)) { ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK; - rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks); - if (!rp) { + ErlOffHeap *ohp; + rp = erts_proc_lookup(lnk->pid); + if (!rp) goto done; - } + erts_smp_proc_lock(rp, rp_locks); rlnk = erts_remove_link(&ERTS_P_LINKS(rp), name); if (rlnk != NULL) { ASSERT(is_atom(rlnk->pid) && (rlnk->type == LINK_NODE)); @@ -384,12 +385,14 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp) } n = ERTS_LINK_REFC(lnk); for (i = 0; i < n; ++i) { - ErlHeapFragment* bp; - ErlOffHeap *ohp; Eterm tup; - Eterm *hp = erts_alloc_message_heap(3,&bp,&ohp,rp,&rp_locks); + Eterm *hp; + ErtsMessage *msgp; + + msgp = erts_alloc_message_heap(rp, &rp_locks, + 3, &hp, &ohp); tup = TUPLE2(hp, am_nodedown, name); - erts_queue_message(rp, &rp_locks, bp, tup, NIL); + erts_queue_message(rp, &rp_locks, msgp, tup, NIL); } erts_smp_proc_unlock(rp, rp_locks); } @@ -1458,7 +1461,7 @@ int erts_net_message(Port *prt, ErlOffHeap *ohp; ASSERT(xsize); heap_frag = erts_dist_ext_trailer(ede_copy); - ERTS_INIT_HEAP_FRAG(heap_frag, token_size); + ERTS_INIT_HEAP_FRAG(heap_frag, token_size, token_size); hp = heap_frag->mem; ohp = &heap_frag->off_heap; token = tuple[5]; @@ -1507,7 +1510,7 @@ int erts_net_message(Port *prt, ErlOffHeap *ohp; ASSERT(xsize); heap_frag = erts_dist_ext_trailer(ede_copy); - ERTS_INIT_HEAP_FRAG(heap_frag, token_size); + ERTS_INIT_HEAP_FRAG(heap_frag, token_size, token_size); hp = heap_frag->mem; ohp = &heap_frag->off_heap; token = tuple[4]; @@ -3267,11 +3270,16 @@ send_nodes_mon_msg(Process *rp, Uint sz) { Eterm msg; - ErlHeapFragment* bp; + Eterm *hp; + ErtsMessage *mp; ErlOffHeap *ohp; - Eterm *hp = erts_alloc_message_heap(sz, &bp, &ohp, rp, rp_locksp); #ifdef DEBUG - Eterm *hend = hp + sz; + Eterm *hend; +#endif + + mp = erts_alloc_message_heap(rp, rp_locksp, sz, &hp, &ohp); +#ifdef DEBUG + hend = hp + sz; #endif if (!nmp->opts) { @@ -3317,7 +3325,7 @@ send_nodes_mon_msg(Process *rp, } ASSERT(hend == hp); - erts_queue_message(rp, rp_locksp, bp, msg, NIL); + erts_queue_message(rp, rp_locksp, mp, msg, NIL); } static void diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 3e300f88ea..019aa0f16c 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -582,7 +582,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_DRV_SEL_D_STATE)] = sizeof(ErtsDrvSelectDataState); fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MSG_REF)] - = sizeof(ErlMessage); + = sizeof(ErtsMessageRef); #ifdef ERTS_SMP fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_THR_Q_EL_SL)] = sizeof(ErtsThrQElement_t); @@ -2916,12 +2916,12 @@ reply_alloc_info(void *vair) int global_instances = air->req_sched == sched_id; ErtsProcLocks rp_locks; Process *rp = air->proc; - Eterm ref_copy = NIL, ai_list, msg; - Eterm *hp = NULL, *hp_end = NULL, *hp_start = NULL; + Eterm ref_copy = NIL, ai_list, msg = NIL; + Eterm *hp = NULL, *hp_start = NULL, *hp_end = NULL; Eterm **hpp; Uint sz, *szp; ErlOffHeap *ohp = NULL; - ErlHeapFragment *bp = NULL; + ErtsMessage *mp = NULL; struct erts_mmap_info_struct emis; int i; Eterm (*info_func)(Allctr_t *, @@ -3123,20 +3123,17 @@ reply_alloc_info(void *vair) if (hpp) break; - hp = erts_alloc_message_heap(sz, &bp, &ohp, rp, &rp_locks); + mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp); hp_start = hp; hp_end = hp + sz; szp = NULL; hpp = &hp; } - if (bp) - bp = erts_resize_message_buffer(bp, hp - hp_start, &msg, 1); - else { - ASSERT(hp); - HRelease(rp, hp_end, hp); - } - erts_queue_message(rp, &rp_locks, bp, msg, NIL); + if (hp != hp_end) + erts_shrink_message_heap(&mp, rp, hp_start, hp, hp_end, &msg, 1); + + erts_queue_message(rp, &rp_locks, mp, msg, NIL); if (air->req_sched == sched_id) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index 9da9c823f7..14e80960f5 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -44,9 +44,11 @@ #if ERTS_CAN_INLINE && ERTS_ALC_WANT_INLINE # define ERTS_ALC_DO_INLINE 1 # define ERTS_ALC_INLINE static ERTS_INLINE +# define ERTS_ALC_FORCE_INLINE static ERTS_FORCE_INLINE #else # define ERTS_ALC_DO_INLINE 0 # define ERTS_ALC_INLINE +# define ERTS_ALC_FORCE_INLINE #endif #define ERTS_ALC_NO_FIXED_SIZES \ @@ -293,7 +295,7 @@ int erts_is_allctr_wrapper_prelocked(void) #ifdef ERTS_HAVE_IS_IN_LITERAL_RANGE -ERTS_ALC_INLINE +ERTS_ALC_FORCE_INLINE int erts_is_in_literal_range(void* ptr) { #if defined(ARCH_32) diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 7d519c1be4..75b4913012 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -152,6 +152,8 @@ type OLD_HEAP EHEAP PROCESSES old_heap type HEAP_FRAG EHEAP PROCESSES heap_frag type TMP_HEAP TEMPORARY PROCESSES tmp_heap type MSG_REF FIXED_SIZE PROCESSES msg_ref +type MSG EHEAP PROCESSES message +type MSGQ_CHNG SHORT_LIVED PROCESSES messages_queue_change type MSG_ROOTS TEMPORARY PROCESSES msg_roots type ROOTSET TEMPORARY PROCESSES root_set type LOADER_TMP TEMPORARY CODE loader_tmp diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index 28bec6325c..2b1d875bfe 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -1707,18 +1707,19 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type, Eterm mess; Eterm r; Eterm *hp; - ErlHeapFragment *bp; - ErlOffHeap *ohp; + ErtsMessage *mp; ErtsProcLocks rp_locks = 0; + ErlOffHeap *ohp; ERTS_SMP_CHK_NO_PROC_LOCKS; assert_drv_list_rwlocked(); if (errcode != 0) { int need = load_error_need(errcode); Eterm e; - hp = erts_alloc_message_heap(6 /* tuple */ + 3 /* Error tuple */ + - REF_THING_SIZE + need, &bp, &ohp, - proc, &rp_locks); + mp = erts_alloc_message_heap(proc, &rp_locks, + (6 /* tuple */ + 3 /* Error tuple */ + + REF_THING_SIZE + need), + &hp, &ohp); r = copy_ref(ref,hp); hp += REF_THING_SIZE; e = build_load_error_hp(hp, errcode); @@ -1727,12 +1728,14 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type, hp += 3; mess = TUPLE5(hp,type,r,am_driver,driver_name,mess); } else { - hp = erts_alloc_message_heap(6 /* tuple */ + REF_THING_SIZE, &bp, &ohp, proc, &rp_locks); + mp = erts_alloc_message_heap(proc, &rp_locks, + 6 /* tuple */ + REF_THING_SIZE, + &hp, &ohp); r = copy_ref(ref,hp); hp += REF_THING_SIZE; mess = TUPLE5(hp,type,r,am_driver,driver_name,tag); } - erts_queue_message(proc, &rp_locks, bp, mess, am_undefined); + erts_queue_message(proc, &rp_locks, mp, mess, am_undefined); erts_smp_proc_unlock(proc, rp_locks); ERTS_SMP_CHK_NO_PROC_LOCKS; } diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index a684c81445..1eb106a551 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -589,6 +589,7 @@ static Eterm pi_args[] = { am_min_bin_vheap_size, am_current_location, am_current_stacktrace, + am_off_heap_message_queue }; #define ERTS_PI_ARGS ((int) (sizeof(pi_args)/sizeof(Eterm))) @@ -636,6 +637,7 @@ pi_arg2ix(Eterm arg) case am_min_bin_vheap_size: return 28; case am_current_location: return 29; case am_current_stacktrace: return 30; + case am_off_heap_message_queue: return 31; default: return -1; } } @@ -718,9 +720,10 @@ pi_pid2proc(Process *c_p, Eterm pid, ErtsProcLocks info_locks) -BIF_RETTYPE +static BIF_RETTYPE process_info_aux(Process *BIF_P, Process *rp, + ErtsProcLocks rp_locks, Eterm rpid, Eterm item, int always_wrap); @@ -811,10 +814,31 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap, *fail_type = ERTS_PI_FAIL_TYPE_AWAIT_EXIT; goto done; } - else if (!(locks & ERTS_PROC_LOCK_STATUS)) { - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); + else { + ErtsProcLocks unlock_locks = 0; + + if (c_p == rp) + locks |= ERTS_PROC_LOCK_MAIN; + + if (!(locks & ERTS_PROC_LOCK_STATUS)) + unlock_locks |= ERTS_PROC_LOCK_STATUS; + + if (locks & ERTS_PROC_LOCK_MSGQ) { + /* + * Move in queue into private queue and + * release msgq lock, enabling others to + * send messages to the process while it + * is being inspected... + */ + ASSERT(locks & ERTS_PROC_LOCK_MAIN); + ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp); + locks &= ~ERTS_PROC_LOCK_MSGQ; + unlock_locks |= ERTS_PROC_LOCK_MSGQ; + } + + if (unlock_locks) + erts_smp_proc_unlock(rp, unlock_locks); } - /* * We always handle 'messages' first if it should be part @@ -826,7 +850,7 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap, if (want_messages) { ix = pi_arg2ix(am_messages); ASSERT(part_res[ix] == THE_NON_VALUE); - part_res[ix] = process_info_aux(c_p, rp, pid, am_messages, always_wrap); + part_res[ix] = process_info_aux(c_p, rp, locks, pid, am_messages, always_wrap); ASSERT(part_res[ix] != THE_NON_VALUE); } @@ -834,7 +858,7 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap, ix = res_elem_ix[res_elem_ix_ix]; if (part_res[ix] == THE_NON_VALUE) { arg = pi_ix2arg(ix); - part_res[ix] = process_info_aux(c_p, rp, pid, arg, always_wrap); + part_res[ix] = process_info_aux(c_p, rp, locks, pid, arg, always_wrap); ASSERT(part_res[ix] != THE_NON_VALUE); } } @@ -965,9 +989,31 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2) ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined); } else { + ErtsProcLocks unlock_locks = 0; + + if (BIF_P == rp) + info_locks |= ERTS_PROC_LOCK_MAIN; + if (!(info_locks & ERTS_PROC_LOCK_STATUS)) - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); - res = process_info_aux(BIF_P, rp, pid, BIF_ARG_2, 0); + unlock_locks |= ERTS_PROC_LOCK_STATUS; + + if (info_locks & ERTS_PROC_LOCK_MSGQ) { + /* + * Move in queue into private queue and + * release msgq lock, enabling others to + * send messages to the process while it + * is being inspected... + */ + ASSERT(info_locks & ERTS_PROC_LOCK_MAIN); + ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp); + info_locks &= ~ERTS_PROC_LOCK_MSGQ; + unlock_locks |= ERTS_PROC_LOCK_MSGQ; + } + + if (unlock_locks) + erts_smp_proc_unlock(rp, unlock_locks); + + res = process_info_aux(BIF_P, rp, info_locks, pid, BIF_ARG_2, 0); } ASSERT(is_value(res)); @@ -985,6 +1031,7 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2) Eterm process_info_aux(Process *BIF_P, Process *rp, + ErtsProcLocks rp_locks, Eterm rpid, Eterm item, int always_wrap) @@ -1056,171 +1103,55 @@ process_info_aux(Process *BIF_P, break; case am_messages: { - ErlMessage* mp; - int n; - ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp); - n = rp->msg.len; - - if (n == 0 || ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) { + if (rp->msg.len == 0 || ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) { hp = HAlloc(BIF_P, 3); } else { - int remove_bad_messages = 0; - struct { - Uint copy_struct_size; - ErlMessage* msgp; - } *mq = erts_alloc(ERTS_ALC_T_TMP, n*sizeof(*mq)); - Sint i = 0; - Uint heap_need = 3; + ErtsMessageInfo *mip; + Sint i; + Uint heap_need; +#ifdef DEBUG Eterm *hp_end; +#endif - for (mp = rp->msg.first; mp; mp = mp->next) { - heap_need += 2; - mq[i].msgp = mp; - if (rp != BIF_P) { - Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp); - if (is_value(msg)) { - mq[i].copy_struct_size = (is_immed(msg)? 0 : - size_object(msg)); - } - else if (mq[i].msgp->data.attached) { - mq[i].copy_struct_size - = erts_msg_attached_data_size(mq[i].msgp); - } - else { - /* Bad distribution message; ignore */ - remove_bad_messages = 1; - mq[i].copy_struct_size = 0; - } - heap_need += mq[i].copy_struct_size; - } - else { - mq[i].copy_struct_size = mp->data.attached ? - erts_msg_attached_data_size(mp) : 0; - } - i++; - } + mip = erts_alloc(ERTS_ALC_T_TMP, + rp->msg.len*sizeof(ErtsMessageInfo)); - if (rp != BIF_P) { - hp = HAlloc(BIF_P, heap_need); - hp_end = hp + heap_need; - ASSERT(i == n); - for (i--; i >= 0; i--) { - Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp); - if (is_value(msg)) { - if (mq[i].copy_struct_size) - msg = copy_struct(msg, - mq[i].copy_struct_size, - &hp, - &MSO(BIF_P)); - } - else if (mq[i].msgp->data.attached) { - ErlHeapFragment *hfp; - /* - * Decode it into a message buffer and attach it - * to the message instead of the attached external - * term. - * - * Note that we may not pass a process pointer - * to erts_msg_distext2heap(), since it would then - * try to alter locks on that process. - */ - msg = erts_msg_distext2heap( - NULL, NULL, &hfp, &ERL_MESSAGE_TOKEN(mq[i].msgp), - mq[i].msgp->data.dist_ext); - - ERL_MESSAGE_TERM(mq[i].msgp) = msg; - mq[i].msgp->data.heap_frag = hfp; - - if (is_non_value(msg)) { - ASSERT(!mq[i].msgp->data.heap_frag); - /* Bad distribution message; ignore */ - remove_bad_messages = 1; - continue; - } - else { - /* Make our copy of the message */ - ASSERT(size_object(msg) == erts_used_frag_sz(hfp)); - msg = copy_struct(msg, - erts_used_frag_sz(hfp), - &hp, - &MSO(BIF_P)); - } - } - else { - /* Bad distribution message; ignore */ - remove_bad_messages = 1; - continue; - } - res = CONS(hp, msg, res); - hp += 2; - } - HRelease(BIF_P, hp_end, hp+3); - } - else { - for (i--; i >= 0; i--) { - ErtsHeapFactory factory; - Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp); - - erts_factory_proc_prealloc_init(&factory, BIF_P, - mq[i].copy_struct_size+2); - if (mq[i].msgp->data.attached) { - /* Decode it on the heap */ - erts_move_msg_attached_data_to_heap(&factory, - mq[i].msgp); - msg = ERL_MESSAGE_TERM(mq[i].msgp); - ASSERT(!mq[i].msgp->data.attached); - } - if (is_value(msg)) { - hp = erts_produce_heap(&factory, 2, 0); - res = CONS(hp, msg, res); - } - else { - /* Bad distribution message; ignore */ - remove_bad_messages = 1; - continue; - } - erts_factory_close(&factory); - } - hp = HAlloc(BIF_P, 3); - } - erts_free(ERTS_ALC_T_TMP, mq); - if (remove_bad_messages) { - ErlMessage **mpp; - /* - * We need to remove bad distribution messages from - * the queue, so that the value returned for - * 'message_queue_len' is consistent with the value - * returned for 'messages'. - */ - mpp = &rp->msg.first; - mp = rp->msg.first; - while (mp) { - if (is_value(ERL_MESSAGE_TERM(mp))) { - mpp = &mp->next; - mp = mp->next; - } - else { - ErlMessage* bad_mp = mp; - ASSERT(!mp->data.attached); - if (rp->msg.save == &mp->next) - rp->msg.save = mpp; - if (rp->msg.last == &mp->next) - rp->msg.last = mpp; - *mpp = mp->next; - mp = mp->next; - rp->msg.len--; - free_message(bad_mp); - } - } + /* + * Note that message queue may shrink when calling + * erts_prep_msgq_for_inspection() since it removes + * corrupt distribution messages. + */ + heap_need = erts_prep_msgq_for_inspection(BIF_P, rp, rp_locks, mip); + heap_need += 3; /* top 2-tuple */ + heap_need += rp->msg.len*2; /* Cons cells */ + + hp = HAlloc(BIF_P, heap_need); /* heap_need is exact */ +#ifdef DEBUG + hp_end = hp + heap_need; +#endif + + /* Build list of messages... */ + for (i = rp->msg.len - 1, res = NIL; i >= 0; i--) { + Eterm msg = ERL_MESSAGE_TERM(mip[i].msgp); + Uint sz = mip[i].size; + + if (sz != 0) + msg = copy_struct(msg, sz, &hp, &BIF_P->off_heap); + + res = CONS(hp, msg, res); + hp += 2; } + + ASSERT(hp_end == hp + 3); + + erts_free(ERTS_ALC_T_TMP, mip); } break; } case am_message_queue_len: hp = HAlloc(BIF_P, 3); - ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp); res = make_small(rp->msg.len); break; @@ -1408,7 +1339,7 @@ process_info_aux(Process *BIF_P, } case am_total_heap_size: { - ErlMessage *mp; + ErtsMessage *mp; Uint total_heap_size; Uint hsz = 3; @@ -1418,8 +1349,6 @@ process_info_aux(Process *BIF_P, total_heap_size += rp->mbuf_sz; - ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp); - for (mp = rp->msg.first; mp; mp = mp->next) if (mp->data.attached) total_heap_size += erts_msg_attached_data_size(mp); @@ -1441,7 +1370,7 @@ process_info_aux(Process *BIF_P, case am_memory: { /* Memory consumed in bytes */ Uint hsz = 3; - Uint size = erts_process_memory(rp); + Uint size = erts_process_memory(rp, 0); (void) erts_bld_uint(NULL, &hsz, size); hp = HAlloc(BIF_P, hsz); res = erts_bld_uint(&hp, NULL, size); @@ -1567,6 +1496,11 @@ process_info_aux(Process *BIF_P, break; } + case am_off_heap_message_queue: + res = BIF_P->flags & F_OFF_HEAP_MSGQ ? am_true : am_false; + hp = HAlloc(BIF_P, 3); + break; + default: return THE_NON_VALUE; /* will produce badarg */ @@ -2728,6 +2662,10 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(am_true); } #endif + else if (BIF_ARG_1 == am_off_heap_message_queue) { + BIF_RET(erts_default_spo_flags & SPO_OFF_HEAP_MSGQ + ? am_true : am_false); + } else if (ERTS_IS_ATOM_STR("compile_info",BIF_ARG_1)) { Uint sz; Eterm res = NIL, tup, text; @@ -3865,9 +3803,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) BIF_RET(am_false); } else { - FLAGS(rp) |= F_FORCE_GC; - if (BIF_P != rp) - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); + ERTS_FORCE_GC(BIF_P); BIF_RET(am_true); } } diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index 434b6c6d7a..6652ef9bb3 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -309,6 +309,8 @@ void erts_check_for_holes(Process* p) p->last_htop = HEAP_TOP(p); for (hf = MBUF(p); hf != 0; hf = hf->next) { + if (hf == p->heap_hfrag) + continue; if (hf == p->last_mbuf) { break; } @@ -399,7 +401,7 @@ void verify_process(Process *p) erl_exit(1,"Wild pointer found in " name " of %T!\n",p->common.id); } - ErlMessage* mp = p->msg.first; + ErtsMessage* mp = p->msg.first; VERBOSE(DEBUG_MEMORY,("Verify process: %T...\n",p->common.id)); @@ -528,7 +530,7 @@ static void print_process_memory(Process *p) PTR_SIZE, "PCB", dashes, dashes, dashes, dashes); if (p->msg.first != NULL) { - ErlMessage* mp; + ErtsMessage* mp; erts_printf(" Message Queue:\n"); mp = p->msg.first; while (mp != NULL) { diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 2d7b7cafa4..6a52e1a890 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -42,8 +42,6 @@ #include "dtrace-wrapper.h" #include "erl_bif_unique.h" -#define ERTS_CONTINUOUS_NEW_HEAP - #define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1 #define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20 #define ERTS_INACT_WR_PB_LEAVE_LIMIT 10 @@ -60,58 +58,6 @@ # define ERTS_GC_ASSERT(B) ((void) 1) #endif -#ifdef ERTS_CONTINUOUS_NEW_HEAP -#define ERTS_IS_NEW_HEAP_PTR__(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ - ErtsInArea((Ptr), (NhPtr), (NhSz)) -#define ERTS_IS_LITERAL_PTR__(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ - (!ErtsInArea((Ptr), (NhPtr), (NhSz)) \ - && !ErtsInArea((Ptr), (OhPtr), (OhSz))) - -#ifdef ERTS_GC_DEBUG -#define ERTS_IS_NEW_HEAP_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ - (ERTS_IS_NEW_HEAP_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) \ - ? (ERTS_GC_ASSERT(!erts_is_literal((TPtr), (Ptr)) \ - && !ErtsInArea((Ptr), (OhPtr), (OhSz))), 1) \ - : (ERTS_GC_ASSERT(erts_is_literal((TPtr), (Ptr)) \ - || ErtsInArea((Ptr), (OhPtr), (OhSz))), 0)) -#define ERTS_IS_LITERAL_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ - (ERTS_IS_LITERAL_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) \ - ? (ERTS_GC_ASSERT(erts_is_literal((TPtr), (Ptr))), 1) \ - : (ERTS_GC_ASSERT(!erts_is_literal((TPtr), (Ptr))), 0)) -#endif - -#else - -#define ERTS_IS_NEW_HEAP_PTR__(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ - (!erts_is_literal((TPtr), (Ptr)) && !ErtsInArea((Ptr), (OhPtr), (OhSz))) -#define ERTS_IS_LITERAL_PTR__(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ - (erts_is_literal((TPtr), (Ptr))) - -#ifdef ERTS_GC_DEBUG -#define ERTS_IS_NEW_HEAP_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ - (ERTS_IS_NEW_HEAP_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) \ - ? (ERTS_GC_ASSERT(ErtsInArea((Ptr), (NhPtr), (NhSz))), 1) \ - : (ERTS_GC_ASSERT(!ErtsInArea((Ptr), (NhPtr), (NhSz))), 0)) -#define ERTS_IS_LITERAL_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ - (ERTS_IS_LITERAL_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) \ - ? (ERTS_GC_ASSERT(!ErtsInArea((Ptr), (NhPtr), (NhSz)) \ - && !ErtsInArea((Ptr), (OhPtr), (OhSz))), 1) \ - : (ERTS_GC_ASSERT(ErtsInArea((Ptr), (NhPtr), (NhSz)) \ - || ErtsInArea((Ptr), (OhPtr), (OhSz))), 0)) -#endif - -#endif - -#ifndef ERTS_IS_NEW_HEAP_PTR -#define ERTS_IS_NEW_HEAP_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ - ERTS_IS_NEW_HEAP_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) -#endif - -#ifndef ERTS_IS_LITERAL_PTR -#define ERTS_IS_LITERAL_PTR(TPtr, Ptr, NhPtr, NhSz, OhPtr, OhSz) \ - ERTS_IS_LITERAL_PTR__((TPtr), (Ptr), (NhPtr), (NhSz), (OhPtr), (OhSz)) -#endif - /* * Returns number of elements in an array. */ @@ -132,10 +78,10 @@ #define ErtsGcQuickSanityCheck(P) \ do { \ ASSERT((P)->heap < (P)->hend); \ - ASSERT((P)->heap_sz == (P)->hend - (P)->heap); \ + ASSERT((p)->abandoned_heap || (P)->heap_sz == (P)->hend - (P)->heap); \ ASSERT((P)->heap <= (P)->htop && (P)->htop <= (P)->hend); \ ASSERT((P)->heap <= (P)->stop && (P)->stop <= (P)->hend); \ - ASSERT((P)->heap <= (P)->high_water && (P)->high_water <= (P)->hend);\ + ASSERT((p)->abandoned_heap || ((P)->heap <= (P)->high_water && (P)->high_water <= (P)->hend)); \ OverRunCheck((P)); \ } while (0) #else @@ -163,31 +109,32 @@ typedef struct { static Uint setup_rootset(Process*, Eterm*, int, Rootset*); static void cleanup_rootset(Rootset *rootset); -static Uint combined_message_size(Process* p); static void remove_message_buffers(Process* p); static Eterm *full_sweep_heaps(Process *p, int hibernate, Eterm *n_heap, Eterm* n_htop, - char *h, Uint h_size, char *oh, Uint oh_size, Eterm *objv, int nobj); -static int major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl); -static int minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl); -static void do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj); +static int garbage_collect(Process* p, ErlHeapFragment *live_hf_end, + int need, Eterm* objv, int nobj); +static int major_collection(Process* p, ErlHeapFragment *live_hf_end, + int need, Eterm* objv, int nobj, Uint *recl); +static int minor_collection(Process* p, ErlHeapFragment *live_hf_end, + int need, Eterm* objv, int nobj, Uint *recl); +static void do_minor(Process *p, ErlHeapFragment *live_hf_end, + char *mature, Uint mature_size, + Uint new_sz, Eterm* objv, int nobj); static Eterm *sweep_new_heap(Eterm *n_hp, Eterm *n_htop, - char* new_heap, Uint new_heap_size, char* old_heap, Uint old_heap_size); static Eterm *sweep_heaps(Eterm *n_hp, Eterm *n_htop, - char* new_heap, Uint new_heap_size, char* old_heap, Uint old_heap_size); static Eterm* sweep_literal_area(Eterm* n_hp, Eterm* n_htop, - char* new_heap, Uint new_heap_size, - char* old_heap, Uint old_heap_size, - char* src, Uint src_size); + char* old_heap, Uint old_heap_size, + char* src, Uint src_size); static Eterm* sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop, char* src, Uint src_size); -static Eterm* collect_heap_frags(Process* p, Eterm* heap, - Eterm* htop, Eterm* objv, int nobj); +static Eterm* collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end, + Eterm* heap, Eterm* htop, Eterm* objv, int nobj); static void adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj); static void shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj); static void grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj); @@ -204,7 +151,6 @@ static void init_gc_info(ErtsGCInfo *gcip); #ifdef HARDDEBUG static void disallow_heap_frag_ref_in_heap(Process* p); static void disallow_heap_frag_ref_in_old_heap(Process* p); -static void disallow_heap_frag_ref(Process* p, Eterm* n_htop, Eterm* objv, int nobj); #endif #if defined(ARCH_64) @@ -411,10 +357,19 @@ erts_offset_off_heap(ErlOffHeap *ohp, Sint offs, Eterm* low, Eterm* high) #undef ptr_within Eterm -erts_gc_after_bif_call(Process* p, Eterm result, Eterm* regs, Uint arity) +erts_gc_after_bif_call_lhf(Process* p, ErlHeapFragment *live_hf_end, + Eterm result, Eterm* regs, Uint arity) { int cost; + if (p->flags & F_HIBERNATE_SCHED) { + /* + * We just hibernated. We do *not* want to mess + * up the hibernation by an ordinary GC... + */ + return result; + } + if (is_non_value(result)) { if (p->freason == TRAP) { #if HIPE @@ -422,21 +377,28 @@ erts_gc_after_bif_call(Process* p, Eterm result, Eterm* regs, Uint arity) regs = ERTS_PROC_GET_SCHDATA(p)->x_reg_array; } #endif - cost = erts_garbage_collect(p, 0, regs, p->arity); + cost = garbage_collect(p, live_hf_end, 0, regs, p->arity); } else { - cost = erts_garbage_collect(p, 0, regs, arity); + cost = garbage_collect(p, live_hf_end, 0, regs, arity); } } else { Eterm val[1]; val[0] = result; - cost = erts_garbage_collect(p, 0, val, 1); + cost = garbage_collect(p, live_hf_end, 0, val, 1); result = val[0]; } BUMP_REDS(p, cost); return result; } +Eterm +erts_gc_after_bif_call(Process* p, Eterm result, Eterm* regs, Uint arity) +{ + return erts_gc_after_bif_call_lhf(p, ERTS_INVALID_HFRAG_PTR, + result, regs, arity); +} + static ERTS_INLINE void reset_active_writer(Process *p) { struct erl_off_heap_header* ptr; @@ -450,6 +412,117 @@ static ERTS_INLINE void reset_active_writer(Process *p) } } +#define ERTS_DELAY_GC_EXTRA_FREE 40 + +static int +delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need) +{ + ErlHeapFragment *hfrag; + Eterm *orig_heap, *orig_hend, *orig_htop, *orig_stop; + Eterm *stop, *hend; + Uint hsz, ssz; + + ERTS_HOLE_CHECK(p); + + if (p->live_hf_end == ERTS_INVALID_HFRAG_PTR) + p->live_hf_end = live_hf_end; + + if (need == 0) + return 1; + + /* + * Satisfy need in a heap fragment... + */ + ASSERT(need > 0); + + orig_heap = p->heap; + orig_hend = p->hend; + orig_htop = p->htop; + orig_stop = p->stop; + + ssz = orig_hend - orig_stop; + hsz = ssz + need + ERTS_DELAY_GC_EXTRA_FREE; + + hfrag = new_message_buffer(hsz); + hfrag->next = p->mbuf; + p->mbuf = hfrag; + p->mbuf_sz += hsz; + p->heap = p->htop = &hfrag->mem[0]; + p->hend = hend = &hfrag->mem[hsz]; + p->stop = stop = hend - ssz; + sys_memcpy((void *) stop, (void *) orig_stop, ssz * sizeof(Eterm)); + + if (p->abandoned_heap) { + /* Active heap already in a fragment; adjust it... */ + ErlHeapFragment *hfrag = ((ErlHeapFragment *) + (((char *) orig_heap) + - offsetof(ErlHeapFragment, mem))); + Uint unused = orig_hend - orig_htop; + ASSERT(hfrag->used_size == hfrag->alloc_size); + ASSERT(hfrag->used_size >= unused); + hfrag->used_size -= unused; + p->mbuf_sz -= unused; + } + else { + /* Do not leave a hole in the abandoned heap... */ + if (orig_htop < orig_hend) { + *orig_htop = make_pos_bignum_header(orig_hend-orig_htop-1); + if (orig_htop + 1 < orig_hend) { + orig_hend[-1] = (Uint) (orig_htop - orig_heap); + p->flags |= F_ABANDONED_HEAP_USE; + } + } + p->abandoned_heap = orig_heap; + } + +#ifdef CHECK_FOR_HOLES + p->last_htop = p->htop; + p->heap_hfrag = hfrag; +#endif + + /* Make sure that we do a proper GC as soon as possible... */ + p->flags |= F_FORCE_GC; + return CONTEXT_REDS; +} + +static ERTS_FORCE_INLINE Uint +young_gen_usage(Process *p) +{ + Uint hsz; + Eterm *aheap; + + hsz = p->mbuf_sz; + aheap = p->abandoned_heap; + if (!aheap) + hsz += p->htop - p->heap; + else { + /* used in orig heap */ + if (p->flags & F_ABANDONED_HEAP_USE) + hsz += aheap[p->heap_sz-1]; + else + hsz += p->heap_sz; + /* Remove unused part in latest fragment */ + hsz -= p->hend - p->htop; + } + return hsz; +} + +#define ERTS_GET_ORIG_HEAP(Proc, Heap, HTop) \ + do { \ + Eterm *aheap__ = (Proc)->abandoned_heap; \ + if (!aheap__) { \ + (Heap) = (Proc)->heap; \ + (HTop) = (Proc)->htop; \ + } \ + else { \ + (Heap) = aheap__; \ + if ((Proc)->flags & F_ABANDONED_HEAP_USE) \ + (HTop) = aheap__ + aheap__[(Proc)->heap_sz-1]; \ + else \ + (HTop) = aheap__ + (Proc)->heap_sz; \ + } \ + } while (0) + /* * Garbage collect a process. * @@ -458,8 +531,9 @@ static ERTS_INLINE void reset_active_writer(Process *p) * objv: Array of terms to add to rootset; that is to preserve. * nobj: Number of objects in objv. */ -int -erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) +static int +garbage_collect(Process* p, ErlHeapFragment *live_hf_end, + int need, Eterm* objv, int nobj) { Uint reclaimed_now = 0; int done = 0; @@ -469,10 +543,11 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); #endif - if (p->flags & F_DISABLE_GC) { - ASSERT(need == 0); - return 1; - } + if (p->flags & F_DISABLE_GC) + return delay_garbage_collection(p, live_hf_end, need); + + if (p->live_hf_end != ERTS_INVALID_HFRAG_PTR) + live_hf_end = p->live_hf_end; esdp = erts_get_scheduler_data(); @@ -480,7 +555,7 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) trace_gc(p, am_gc_start); } - (void) erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC); + erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC); if (erts_system_monitor_long_gc != 0) start_time = erts_get_monotonic_time(esdp); @@ -505,11 +580,11 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) while (!done) { if ((FLAGS(p) & F_NEED_FULLSWEEP) != 0) { DTRACE2(gc_major_start, pidbuf, need); - done = major_collection(p, need, objv, nobj, &reclaimed_now); + done = major_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); DTRACE2(gc_major_end, pidbuf, reclaimed_now); } else { DTRACE2(gc_minor_start, pidbuf, need); - done = minor_collection(p, need, objv, nobj, &reclaimed_now); + done = minor_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); DTRACE2(gc_minor_end, pidbuf, reclaimed_now); } } @@ -551,6 +626,7 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) esdp->gc_info.reclaimed += reclaimed_now; FLAGS(p) &= ~F_FORCE_GC; + p->live_hf_end = ERTS_INVALID_HFRAG_PTR; #ifdef CHECK_FOR_HOLES /* @@ -583,6 +659,12 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) } } +int +erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) +{ + return garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj); +} + /* * Place all living data on a the new heap; deallocate any old heap. * Meant to be used by hibernate/3. @@ -606,16 +688,16 @@ erts_garbage_collect_hibernate(Process* p) */ erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC); ErtsGcQuickSanityCheck(p); - ASSERT(p->mbuf_sz == 0); - ASSERT(p->mbuf == 0); + ASSERT(p->mbuf == NULL); ASSERT(p->stop == p->hend); /* Stack must be empty. */ + ASSERT(!p->abandoned_heap); /* * Do it. */ - heap_size = p->heap_sz + (p->old_htop - p->old_heap); + heap_size = p->heap_sz + (p->old_htop - p->old_heap) + p->mbuf_sz; heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_TMP_HEAP, sizeof(Eterm)*heap_size); htop = heap; @@ -624,8 +706,6 @@ erts_garbage_collect_hibernate(Process* p) 1, heap, htop, - (char *) p->heap, - (char *) p->htop - (char *) p->heap, (char *) p->old_heap, (char *) p->old_htop - (char *) p->old_heap, p->arg_reg, @@ -644,6 +724,7 @@ erts_garbage_collect_hibernate(Process* p) } FLAGS(p) &= ~F_FORCE_GC; + p->live_hf_end = ERTS_INVALID_HFRAG_PTR; /* * Move the heap to its final destination. @@ -663,6 +744,8 @@ erts_garbage_collect_hibernate(Process* p) sys_memcpy((void *) heap, (void *) p->heap, actual_size*sizeof(Eterm)); ERTS_HEAP_FREE(ERTS_ALC_T_TMP_HEAP, p->heap, p->heap_sz*sizeof(Eterm)); + remove_message_buffers(p); + p->stop = p->hend = heap + heap_size; offs = heap - p->heap; @@ -808,7 +891,6 @@ erts_garbage_collect_literals(Process* p, Eterm* literals, old_htop = sweep_literals_to_old_heap(p->heap, p->htop, old_htop, area, area_size); old_htop = sweep_literal_area(p->old_heap, old_htop, - (char *) p->heap, sizeof(Eterm)*p->heap_sz, (char *) p->old_heap, sizeof(Eterm)*old_heap_size, area, area_size); ASSERT(p->old_htop <= old_htop && old_htop <= p->old_hend); @@ -869,15 +951,18 @@ erts_garbage_collect_literals(Process* p, Eterm* literals, } static int -minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) +minor_collection(Process* p, ErlHeapFragment *live_hf_end, + int need, Eterm* objv, int nobj, Uint *recl) { - Uint mature = HIGH_WATER(p) - HEAP_START(p); + Eterm *mature = p->abandoned_heap ? p->abandoned_heap : p->heap; + Uint mature_size = p->high_water - mature; + Uint size_before = young_gen_usage(p); /* * Allocate an old heap if we don't have one and if we'll need one. */ - if (OLD_HEAP(p) == NULL && mature != 0) { + if (OLD_HEAP(p) == NULL && mature_size != 0) { Eterm* n_old; /* Note: We choose a larger heap size than strictly needed, @@ -885,7 +970,7 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) * This improved Estone by more than 1200 estones on my computer * (Ultra Sparc 10). */ - Uint new_sz = erts_next_heap_size(HEAP_TOP(p) - HEAP_START(p), 1); + Uint new_sz = erts_next_heap_size(size_before, 1); /* Create new, empty old_heap */ n_old = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_OLD_HEAP, @@ -901,41 +986,25 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) */ if (OLD_HEAP(p) && - ((mature <= OLD_HEND(p) - OLD_HTOP(p)) && - ((BIN_VHEAP_MATURE(p) < ( BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p)))) && - ((BIN_OLD_VHEAP_SZ(p) > BIN_OLD_VHEAP(p))) ) ) { - ErlMessage *msgp; - Uint size_after; - Uint need_after; - const Uint stack_size = STACK_SZ_ON_HEAP(p); - const Uint size_before = MBUF_SIZE(p) + (HEAP_TOP(p) - HEAP_START(p)); - Uint new_sz = HEAP_SIZE(p) + MBUF_SIZE(p) + combined_message_size(p); + ((mature_size <= OLD_HEND(p) - OLD_HTOP(p)) && + ((BIN_VHEAP_MATURE(p) < ( BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p)))) && + ((BIN_OLD_VHEAP_SZ(p) > BIN_OLD_VHEAP(p))) ) ) { + Uint stack_size, size_after, need_after, new_sz; + + stack_size = p->hend - p->stop; + new_sz = stack_size + size_before; new_sz = next_heap_size(p, new_sz, 0); - do_minor(p, new_sz, objv, nobj); + do_minor(p, live_hf_end, (char *) mature, mature_size*sizeof(Eterm), + new_sz, objv, nobj); size_after = HEAP_TOP(p) - HEAP_START(p); *recl += (size_before - size_after); - /* - * Copy newly received message onto the end of the new heap. - */ - ErtsGcQuickSanityCheck(p); - for (msgp = p->msg.first; msgp; msgp = msgp->next) { - if (msgp->data.attached) { - ErtsHeapFactory factory; - erts_factory_proc_prealloc_init(&factory, p, - erts_msg_attached_data_size(msgp)); - erts_move_msg_attached_data_to_heap(&factory, msgp); - erts_factory_close(&factory); - ErtsGcQuickSanityCheck(p); - } - } ErtsGcQuickSanityCheck(p); GEN_GCS(p)++; need_after = ((HEAP_TOP(p) - HEAP_START(p)) - + erts_used_frag_sz(MBUF(p)) + need + stack_size); @@ -984,10 +1053,13 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) ASSERT(MBUF(p) == NULL); return 1; } + + grow_new_heap(p, next_heap_size(p, need_after, 0), objv, nobj); + return 1; } /* - * Still not enough room after minor collection. Must force a major collection. + * Not enough room for a minor collection. Must force a major collection. */ FLAGS(p) |= F_NEED_FULLSWEEP; return 0; @@ -1044,7 +1116,9 @@ static ERTS_INLINE void offset_nstack(Process* p, Sint offs, #endif /* HIPE */ static void -do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) +do_minor(Process *p, ErlHeapFragment *live_hf_end, + char *mature, Uint mature_size, + Uint new_sz, Eterm* objv, int nobj) { Rootset rootset; /* Rootset for GC (stack, dictionary, etc). */ Roots* roots; @@ -1053,9 +1127,6 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) Eterm* ptr; Eterm val; Eterm gval; - char* heap = (char *) HEAP_START(p); - Uint heap_size = (char *) HEAP_TOP(p) - heap; - Uint mature_size = (char *) HIGH_WATER(p) - heap; Eterm* old_htop = OLD_HTOP(p); Eterm* n_heap; char* oh = (char *) OLD_HEAP(p); @@ -1064,8 +1135,13 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) n_htop = n_heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*new_sz); - if (MBUF(p) != NULL) { - n_htop = collect_heap_frags(p, n_heap, n_htop, objv, nobj); + if (live_hf_end != ERTS_INVALID_HFRAG_PTR) { + /* + * Move heap frags that we know are completely live + * directly into the new young heap generation. + */ + n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop, + objv, nobj); } n = setup_rootset(p, objv, nobj, &rootset); @@ -1088,11 +1164,9 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) if (IS_MOVED_BOXED(val)) { ASSERT(is_boxed(val)); *g_ptr++ = val; - } else if (ErtsInArea(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, mature, mature_size)) { MOVE_BOXED(ptr,val,old_htop,g_ptr++); - } else if (ERTS_IS_NEW_HEAP_PTR(gval, ptr, - heap, heap_size, - oh, oh_size)) { + } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) { MOVE_BOXED(ptr,val,n_htop,g_ptr++); } else { g_ptr++; @@ -1105,11 +1179,9 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) val = *ptr; if (IS_MOVED_CONS(val)) { /* Moved */ *g_ptr++ = ptr[1]; - } else if (ErtsInArea(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, mature, mature_size)) { MOVE_CONS(ptr,val,old_htop,g_ptr++); - } else if (ERTS_IS_NEW_HEAP_PTR(gval, ptr, - heap, heap_size, - oh, oh_size)) { + } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) { MOVE_CONS(ptr,val,n_htop,g_ptr++); } else { g_ptr++; @@ -1134,8 +1206,7 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) */ if (mature_size == 0) { - n_htop = sweep_new_heap(n_heap, n_htop, heap, heap_size, - oh, oh_size); + n_htop = sweep_new_heap(n_heap, n_htop, oh, oh_size); } else { Eterm* n_hp = n_heap; Eterm* ptr; @@ -1152,11 +1223,9 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) if (IS_MOVED_BOXED(val)) { ASSERT(is_boxed(val)); *n_hp++ = val; - } else if (ErtsInArea(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, mature, mature_size)) { MOVE_BOXED(ptr,val,old_htop,n_hp++); - } else if (ERTS_IS_NEW_HEAP_PTR(gval, ptr, - heap, heap_size, - oh, oh_size)) { + } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) { MOVE_BOXED(ptr,val,n_htop,n_hp++); } else { n_hp++; @@ -1168,11 +1237,9 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) val = *ptr; if (IS_MOVED_CONS(val)) { *n_hp++ = ptr[1]; - } else if (ErtsInArea(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, mature, mature_size)) { MOVE_CONS(ptr,val,old_htop,n_hp++); - } else if (ERTS_IS_NEW_HEAP_PTR(gval, ptr, - heap, heap_size, - oh, oh_size)) { + } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) { MOVE_CONS(ptr,val,n_htop,n_hp++); } else { n_hp++; @@ -1192,12 +1259,10 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) if (IS_MOVED_BOXED(val)) { *origptr = val; mb->base = binary_bytes(val); - } else if (ErtsInArea(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, mature, mature_size)) { MOVE_BOXED(ptr,val,old_htop,origptr); mb->base = binary_bytes(mb->orig); - } else if (ERTS_IS_NEW_HEAP_PTR(*origptr, ptr, - heap, heap_size, - oh, oh_size)) { + } else if (ErtsInYoungGen(*origptr, ptr, oh, oh_size)) { MOVE_BOXED(ptr,val,n_htop,origptr); mb->base = binary_bytes(mb->orig); } @@ -1218,11 +1283,8 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) * may point to the old (soon to be deleted) new_heap. */ - if (OLD_HTOP(p) < old_htop) { - old_htop = sweep_new_heap(OLD_HTOP(p), old_htop, - heap, heap_size, - oh, oh_size); - } + if (OLD_HTOP(p) < old_htop) + old_htop = sweep_new_heap(OLD_HTOP(p), old_htop, oh, oh_size); OLD_HTOP(p) = old_htop; HIGH_WATER(p) = n_htop; @@ -1254,8 +1316,12 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) #endif ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, - (void*)HEAP_START(p), + (p->abandoned_heap + ? p->abandoned_heap + : HEAP_START(p)), HEAP_SIZE(p) * sizeof(Eterm)); + p->abandoned_heap = NULL; + p->flags &= ~F_ABANDONED_HEAP_USE; HEAP_START(p) = n_heap; HEAP_TOP(p) = n_htop; HEAP_SIZE(p) = new_sz; @@ -1272,15 +1338,12 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) */ static int -major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) +major_collection(Process* p, ErlHeapFragment *live_hf_end, + int need, Eterm* objv, int nobj, Uint *recl) { - const Uint size_before = ((HEAP_TOP(p) - HEAP_START(p)) - + (OLD_HTOP(p) - OLD_HEAP(p)) - + MBUF_SIZE(p)); + Uint size_before, stack_size; Eterm* n_heap; Eterm* n_htop; - char* src = (char *) HEAP_START(p); - Uint src_size = (char *) HEAP_TOP(p) - src; char* oh = (char *) OLD_HEAP(p); Uint oh_size = (char *) OLD_HTOP(p) - oh; Uint new_sz, stk_sz; @@ -1290,9 +1353,11 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) * to receive all live data. */ - new_sz = (HEAP_SIZE(p) + MBUF_SIZE(p) - + combined_message_size(p) - + (OLD_HTOP(p) - OLD_HEAP(p))); + size_before = young_gen_usage(p); + size_before += p->old_htop - p->old_heap; + stack_size = p->hend - p->stop; + + new_sz = stack_size + size_before; new_sz = next_heap_size(p, new_sz, 0); /* @@ -1306,16 +1371,16 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) n_htop = n_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*new_sz); - /* - * Get rid of heap fragments. - */ - - if (MBUF(p) != NULL) { - n_htop = collect_heap_frags(p, n_heap, n_htop, objv, nobj); + if (live_hf_end != ERTS_INVALID_HFRAG_PTR) { + /* + * Move heap frags that we know are completely live + * directly into the heap. + */ + n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop, + objv, nobj); } - n_htop = full_sweep_heaps(p, 0, n_heap, n_htop, src, src_size, - oh, oh_size, objv, nobj); + n_htop = full_sweep_heaps(p, 0, n_heap, n_htop, oh, oh_size, objv, nobj); /* Move the stack to the end of the heap */ stk_sz = HEAP_END(p) - p->stop; @@ -1332,8 +1397,12 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) #endif ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, - (void *) HEAP_START(p), + (p->abandoned_heap + ? p->abandoned_heap + : HEAP_START(p)), (HEAP_END(p) - HEAP_START(p)) * sizeof(Eterm)); + p->abandoned_heap = NULL; + p->flags &= ~F_ABANDONED_HEAP_USE; HEAP_START(p) = n_heap; HEAP_TOP(p) = n_htop; HEAP_SIZE(p) = new_sz; @@ -1346,24 +1415,6 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) *recl += size_before - (HEAP_TOP(p) - HEAP_START(p)); - { - ErlMessage *msgp; - - /* - * Copy newly received message onto the end of the new heap. - */ - for (msgp = p->msg.first; msgp; msgp = msgp->next) { - if (msgp->data.attached) { - ErtsHeapFactory factory; - erts_factory_proc_prealloc_init(&factory, p, - erts_msg_attached_data_size(msgp)); - erts_move_msg_attached_data_to_heap(&factory, msgp); - erts_factory_close(&factory); - ErtsGcQuickSanityCheck(p); - } - } - } - adjust_after_fullsweep(p, need, objv, nobj); #ifdef HARDDEBUG @@ -1379,7 +1430,6 @@ static Eterm * full_sweep_heaps(Process *p, int hibernate, Eterm *n_heap, Eterm* n_htop, - char *h, Uint h_size, char *oh, Uint oh_size, Eterm *objv, int nobj) { @@ -1420,9 +1470,7 @@ full_sweep_heaps(Process *p, if (IS_MOVED_BOXED(val)) { ASSERT(is_boxed(val)); *g_ptr++ = val; - } else if (!ERTS_IS_LITERAL_PTR(gval, ptr, - h, h_size, - oh, oh_size)) { + } else if (!erts_is_literal(gval, ptr)) { MOVE_BOXED(ptr,val,n_htop,g_ptr++); } else { g_ptr++; @@ -1435,9 +1483,7 @@ full_sweep_heaps(Process *p, val = *ptr; if (IS_MOVED_CONS(val)) { *g_ptr++ = ptr[1]; - } else if (!ERTS_IS_LITERAL_PTR(gval, ptr, - h, h_size, - oh, oh_size)) { + } else if (!erts_is_literal(gval, ptr)) { MOVE_CONS(ptr,val,n_htop,g_ptr++); } else { g_ptr++; @@ -1462,7 +1508,7 @@ full_sweep_heaps(Process *p, * until all is copied. */ - n_htop = sweep_heaps(n_heap, n_htop, h, h_size, oh, oh_size); + n_htop = sweep_heaps(n_heap, n_htop, oh, oh_size); if (MSO(p).first) { sweep_off_heap(p, 1); @@ -1513,23 +1559,6 @@ adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj) } } -/* - * Return the size of all message buffers that are NOT linked in the - * mbuf list. - */ -static Uint -combined_message_size(Process* p) -{ - Uint sz; - ErlMessage *msgp; - - for (sz = 0, msgp = p->msg.first; msgp; msgp = msgp->next) { - if (msgp->data.attached) - sz += erts_msg_attached_data_size(msgp); - } - return sz; -} - /* * Remove all message buffers. */ @@ -1540,6 +1569,10 @@ remove_message_buffers(Process* p) free_message_buffer(MBUF(p)); MBUF(p) = NULL; } + if (p->msg_frag) { + erts_cleanup_messages(p->msg_frag); + p->msg_frag = NULL; + } MBUF_SIZE(p) = 0; } #ifdef HARDDEBUG @@ -1551,64 +1584,6 @@ remove_message_buffers(Process* p) * For performance reasons, we use _unchecked_list_val(), _unchecked_boxed_val(), * and so on to avoid a function call. */ - -static void -disallow_heap_frag_ref(Process* p, Eterm* n_htop, Eterm* objv, int nobj) -{ - ErlHeapFragment* mbuf; - ErlHeapFragment* qb; - Eterm gval; - Eterm* ptr; - Eterm val; - - ASSERT(p->htop != NULL); - mbuf = MBUF(p); - - while (nobj--) { - gval = *objv; - - switch (primary_tag(gval)) { - - case TAG_PRIMARY_BOXED: { - ptr = _unchecked_boxed_val(gval); - val = *ptr; - if (IS_MOVED_BOXED(val)) { - ASSERT(is_boxed(val)); - objv++; - } else { - for (qb = mbuf; qb != NULL; qb = qb->next) { - if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { - abort(); - } - } - objv++; - } - break; - } - - case TAG_PRIMARY_LIST: { - ptr = _unchecked_list_val(gval); - val = *ptr; - if (IS_MOVED_CONS(val)) { - objv++; - } else { - for (qb = mbuf; qb != NULL; qb = qb->next) { - if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) { - abort(); - } - } - objv++; - } - break; - } - - default: { - objv++; - break; - } - } - } -} static void disallow_heap_frag_ref_in_heap(Process* p) @@ -1737,7 +1712,6 @@ typedef enum { static ERTS_FORCE_INLINE Eterm * sweep(Eterm *n_hp, Eterm *n_htop, ErtsSweepType type, - char *h, Uint hsz, char *oh, Uint ohsz, char *src, Uint src_size) { @@ -1749,14 +1723,10 @@ sweep(Eterm *n_hp, Eterm *n_htop, #define ERTS_IS_IN_SWEEP_AREA(TPtr, Ptr) \ (type == ErtsSweepHeaps \ - ? !ERTS_IS_LITERAL_PTR((TPtr), (Ptr), h, hsz, oh, ohsz) \ + ? !erts_is_literal((TPtr), (Ptr)) \ : (type == ErtsSweepNewHeap \ - ? ERTS_IS_NEW_HEAP_PTR((TPtr), (Ptr), h, hsz, oh, ohsz) \ - : (ErtsInArea((Ptr), src, src_size) \ - ? (ERTS_GC_ASSERT(erts_is_literal((TPtr), (Ptr)) \ - && !ErtsInArea((Ptr), h, hsz) \ - && !ErtsInArea((Ptr), oh, ohsz)), 1) \ - : 0))) + ? ErtsInYoungGen((TPtr), (Ptr), oh, ohsz) \ + : ErtsInArea((Ptr), src, src_size))) while (n_hp != n_htop) { ASSERT(n_hp < n_htop); @@ -1820,38 +1790,30 @@ sweep(Eterm *n_hp, Eterm *n_htop, } static Eterm * -sweep_new_heap(Eterm *n_hp, Eterm *n_htop, - char* new_heap, Uint new_heap_size, - char* old_heap, Uint old_heap_size) +sweep_new_heap(Eterm *n_hp, Eterm *n_htop, char* old_heap, Uint old_heap_size) { return sweep(n_hp, n_htop, ErtsSweepNewHeap, - new_heap, new_heap_size, old_heap, old_heap_size, NULL, 0); } static Eterm * -sweep_heaps(Eterm *n_hp, Eterm *n_htop, - char* new_heap, Uint new_heap_size, - char* old_heap, Uint old_heap_size) +sweep_heaps(Eterm *n_hp, Eterm *n_htop, char* old_heap, Uint old_heap_size) { return sweep(n_hp, n_htop, ErtsSweepHeaps, - new_heap, new_heap_size, old_heap, old_heap_size, NULL, 0); } static Eterm * sweep_literal_area(Eterm *n_hp, Eterm *n_htop, - char* new_heap, Uint new_heap_size, char* old_heap, Uint old_heap_size, char* src, Uint src_size) { return sweep(n_hp, n_htop, ErtsSweepLiteralArea, - new_heap, new_heap_size, old_heap, old_heap_size, src, src_size); } @@ -1956,32 +1918,21 @@ move_one_area(Eterm* n_htop, char* src, Uint src_size) */ static Eterm* -collect_heap_frags(Process* p, Eterm* n_hstart, Eterm* n_htop, - Eterm* objv, int nobj) +collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end, + Eterm* n_hstart, Eterm* n_htop, + Eterm* objv, int nobj) { ErlHeapFragment* qb; char* frag_begin; Uint frag_size; - /* - * We don't allow references to a heap fragments from the stack, heap, - * or process dictionary. - */ -#ifdef HARDDEBUG - disallow_heap_frag_ref(p, n_htop, p->stop, STACK_START(p) - p->stop); - if (p->dictionary != NULL) { - disallow_heap_frag_ref(p, n_htop, p->dictionary->data, p->dictionary->used); - } - disallow_heap_frag_ref_in_heap(p); -#endif - /* * Move the heap fragments to the new heap. Note that no GC is done on * the heap fragments. Any garbage will thus be moved as well and survive * until next GC. */ qb = MBUF(p); - while (qb != NULL) { + while (qb != live_hf_end) { ASSERT(!qb->off_heap.first); /* process fragments use the MSO(p) list */ frag_size = qb->used_size * sizeof(Eterm); if (frag_size != 0) { @@ -1996,9 +1947,7 @@ collect_heap_frags(Process* p, Eterm* n_hstart, Eterm* n_htop, static Uint setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) { - Uint avail; Roots* roots; - ErlMessage* mp; Uint n; n = 0; @@ -2076,31 +2025,48 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) ASSERT(n <= rootset->size); - mp = p->msg.first; - avail = rootset->size - n; - while (mp != NULL) { - if (avail == 0) { - Uint new_size = 2*rootset->size; - if (roots == rootset->def) { - roots = erts_alloc(ERTS_ALC_T_ROOTSET, - new_size*sizeof(Roots)); - sys_memcpy(roots, rootset->def, sizeof(rootset->def)); - } else { - roots = erts_realloc(ERTS_ALC_T_ROOTSET, - (void *) roots, - new_size*sizeof(Roots)); - } + switch (p->flags & (F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG)) { + case F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG: + (void) erts_move_messages_off_heap(p); + case F_OFF_HEAP_MSGQ: + break; + case F_OFF_HEAP_MSGQ_CHNG: + case 0: { + /* + * Off heap message queue disabled, i.e. we may + * have references from the message queue to the + * heap... + */ + ErtsMessage *mp; + + /* Ensure large enough rootset... */ + if (n + p->msg.len > rootset->size) { + Uint new_size = n + p->msg.len; + ERTS_GC_ASSERT(roots == rootset->def); + roots = erts_alloc(ERTS_ALC_T_ROOTSET, + new_size*sizeof(Roots)); + sys_memcpy(roots, rootset->def, n*sizeof(Roots)); rootset->size = new_size; - avail = new_size - n; } - if (mp->data.attached == NULL) { - roots[n].v = mp->m; - roots[n].sz = 2; - n++; - avail--; + + for (mp = p->msg.first; mp; mp = mp->next) { + + if (!mp->data.attached) { + /* + * Message may refer data on heap; + * add it to rootset... + */ + roots[n].v = mp->m; + roots[n].sz = ERL_MESSAGE_REF_ARRAY_SZ; + n++; + } } - mp = mp->next; + break; + } } + + ASSERT(rootset->size >= n); + rootset->roots = roots; rootset->num_roots = n; return n; @@ -2569,7 +2535,7 @@ offset_off_heap(Process* p, Sint offs, char* area, Uint area_size) static void offset_mqueue(Process *p, Sint offs, char* area, Uint area_size) { - ErlMessage* mp = p->msg.first; + ErtsMessage* mp = p->msg.first; while (mp != NULL) { Eterm mesg = ERL_MESSAGE_TERM(mp); @@ -2656,7 +2622,7 @@ reply_gc_info(void *vgcirp) Eterm **hpp; Uint sz, *szp; ErlOffHeap *ohp = NULL; - ErlHeapFragment *bp = NULL; + ErtsMessage *mp = NULL; ASSERT(esdp); @@ -2682,12 +2648,13 @@ reply_gc_info(void *vgcirp) if (hpp) break; - hp = erts_alloc_message_heap(sz, &bp, &ohp, rp, &rp_locks); + mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp); + szp = NULL; hpp = &hp; } - erts_queue_message(rp, &rp_locks, bp, msg, NIL); + erts_queue_message(rp, &rp_locks, mp, msg, NIL); if (gcirp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -2739,36 +2706,49 @@ erts_gc_info_request(Process *c_p) static int within2(Eterm *ptr, Process *p, Eterm *real_htop) { - ErlHeapFragment* bp = MBUF(p); - ErlMessage* mp = p->msg.first; - Eterm *htop = real_htop ? real_htop : HEAP_TOP(p); + ErlHeapFragment* bp; + ErtsMessage* mp; + Eterm *htop, *heap; + + if (p->abandoned_heap) + ERTS_GET_ORIG_HEAP(p, heap, htop); + else { + heap = p->heap; + htop = real_htop ? real_htop : HEAP_TOP(p); + } if (OLD_HEAP(p) && (OLD_HEAP(p) <= ptr && ptr < OLD_HEND(p))) { return 1; } - if (HEAP_START(p) <= ptr && ptr < htop) { + if (heap <= ptr && ptr < htop) { return 1; } - while (bp != NULL) { - if (bp->mem <= ptr && ptr < bp->mem + bp->used_size) { - return 1; - } - bp = bp->next; - } + + mp = p->msg_frag; + bp = p->mbuf; + + if (bp) + goto search_heap_frags; + while (mp) { - if (mp->data.attached) { - ErlHeapFragment *hfp; - if (is_value(ERL_MESSAGE_TERM(mp))) - hfp = mp->data.heap_frag; - else if (is_not_nil(ERL_MESSAGE_TOKEN(mp))) - hfp = erts_dist_ext_trailer(mp->data.dist_ext); - else - hfp = NULL; - if (hfp && hfp->mem <= ptr && ptr < hfp->mem + hfp->used_size) + + if (mp->data.attached == ERTS_MSG_COMBINED_HFRAG) + bp = &mp->hfrag; + else + bp = mp->data.heap_frag; + + mp = mp->next; + + search_heap_frags: + + while (bp) { + if (bp->mem <= ptr && ptr < bp->mem + bp->used_size) { return 1; + } + bp = bp->next; } - mp = mp->next; } + return 0; } @@ -2790,11 +2770,11 @@ do { \ __FILE__, __LINE__, #EXP); \ } while (0) + #ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_LIST # define ERTS_OFFHEAP_VISITED_BIT ((Eterm) 1 << 31) #endif - void erts_check_off_heap2(Process *p, Eterm *htop) { @@ -2823,7 +2803,7 @@ erts_check_off_heap2(Process *p, Eterm *htop) } ERTS_CHK_OFFHEAP_ASSERT(refc >= 1); #ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_LIST - ERTS_CHK_OFFHEAP_ASSERT(!(u.hdr->thing_word & ERTS_EXTERNAL_VISITED_BIT)); + ERTS_CHK_OFFHEAP_ASSERT(!(u.hdr->thing_word & ERTS_OFFHEAP_VISITED_BIT)); u.hdr->thing_word |= ERTS_OFFHEAP_VISITED_BIT; #endif if (old) { @@ -2836,7 +2816,7 @@ erts_check_off_heap2(Process *p, Eterm *htop) } } -#ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_EXTERNAL_LIST +#ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_LIST for (u.hdr = MSO(p).first; u.hdr; u.hdr = u.hdr->next) u.hdr->thing_word &= ~ERTS_OFFHEAP_VISITED_BIT; #endif diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index fdbf948f9d..a496c5f008 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -69,13 +69,14 @@ do { \ while (nelts--) *HTOP++ = *PTR++; \ } while(0) -#define in_area(ptr,start,nbytes) \ - ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes)) - #if defined(DEBUG) || defined(ERTS_OFFHEAP_DEBUG) int within(Eterm *ptr, Process *p); #endif +#define ErtsInYoungGen(TPtr, Ptr, OldHeap, OldHeapSz) \ + (!erts_is_literal((TPtr), (Ptr)) \ + & !ErtsInArea((Ptr), (OldHeap), (OldHeapSz))) + ERTS_GLB_INLINE Eterm follow_moved(Eterm term, Eterm xptr_tag); #if ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -98,6 +99,7 @@ ERTS_GLB_INLINE Eterm follow_moved(Eterm term, Eterm xptr_tag) } return term; } + #endif #endif /* ERL_GC_C__ || HIPE_GC_C__ */ @@ -106,6 +108,23 @@ ERTS_GLB_INLINE Eterm follow_moved(Eterm term, Eterm xptr_tag) * Global exported */ +#define ERTS_IS_GC_DESIRED_INTERNAL(Proc, HTop, STop) \ + ((((STop) - (HTop) < (Proc)->mbuf_sz)) \ + | ((Proc)->off_heap.overhead > (Proc)->bin_vheap_sz) \ + | !!((Proc)->flags & F_FORCE_GC)) + +#define ERTS_IS_GC_DESIRED(Proc) \ + ERTS_IS_GC_DESIRED_INTERNAL((Proc), (Proc)->htop, (Proc)->stop) + +#define ERTS_FORCE_GC_INTERNAL(Proc, FCalls) \ + do { \ + (Proc)->flags |= F_FORCE_GC; \ + ERTS_VBUMP_ALL_REDS_INTERNAL((Proc), (FCalls)); \ + } while (0) + +#define ERTS_FORCE_GC(Proc) \ + ERTS_FORCE_GC_INTERNAL((Proc), (Proc)->fcalls) + extern Uint erts_test_long_gc_sleep; typedef struct { @@ -117,6 +136,8 @@ void erts_gc_info(ErtsGCInfo *gcip); void erts_init_gc(void); int erts_garbage_collect(struct process*, int, Eterm*, int); void erts_garbage_collect_hibernate(struct process* p); +Eterm erts_gc_after_bif_call_lhf(struct process* p, ErlHeapFragment *live_hf_end, + Eterm result, Eterm* regs, Uint arity); Eterm erts_gc_after_bif_call(struct process* p, Eterm result, Eterm* regs, Uint arity); void erts_garbage_collect_literals(struct process* p, Eterm* literals, Uint lit_size, diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 51a0d68247..6853278828 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -1245,7 +1245,9 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs) * the middle of tree destruction). */ if (!ERTS_PROC_IS_EXITING(proc)) { - erts_queue_message(proc, &proc_locks, tmr->btm.bp, + ErtsMessage *mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = tmr->btm.bp; + erts_queue_message(proc, &proc_locks, mp, tmr->btm.message, NIL); erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_SEND); queued_message = 1; @@ -1926,36 +1928,31 @@ access_sched_local_btm(Process *c_p, Eterm pid, if (proc) { Uint hsz; - ErlOffHeap *ohp; - ErlHeapFragment* bp; + ErtsMessage *mp; Eterm *hp, msg, ref, result; + ErlOffHeap *ohp; + Uint32 *refn; #ifdef ERTS_HLT_DEBUG Eterm *hp_end; #endif - hsz = 3; /* 2-tuple */ - if (!async) - hsz += REF_THING_SIZE; + hsz = REF_THING_SIZE; + if (async) { + refn = trefn; /* timer ref */ + hsz += 4; /* 3-tuple */ + } else { - if (is_non_value(tref) || proc != c_p) - hsz += REF_THING_SIZE; - hsz += 1; /* upgrade to 3-tuple */ + refn = rrefn; /* request ref */ + hsz += 3; /* 2-tuple */ } + + ERTS_HLT_ASSERT(refn); + if (time_left > (Sint64) MAX_SMALL) hsz += ERTS_SINT64_HEAP_SIZE(time_left); - if (proc == c_p) { - bp = NULL; - ohp = NULL; - hp = HAlloc(c_p, hsz); - } - else { - hp = erts_alloc_message_heap(hsz, - &bp, - &ohp, - proc, - &proc_locks); - } + mp = erts_alloc_message_heap(proc, &proc_locks, + hsz, &hp, &ohp); #ifdef ERTS_HLT_DEBUG hp_end = hp + hsz; @@ -1968,35 +1965,22 @@ access_sched_local_btm(Process *c_p, Eterm pid, else result = erts_sint64_to_big(time_left, &hp); - if (!async) { - write_ref_thing(hp, - rrefn[0], - rrefn[1], - rrefn[2]); - ref = make_internal_ref(hp); - hp += REF_THING_SIZE; - msg = TUPLE2(hp, ref, result); + write_ref_thing(hp, + refn[0], + refn[1], + refn[2]); + ref = make_internal_ref(hp); + hp += REF_THING_SIZE; - ERTS_HLT_ASSERT(hp + 3 == hp_end); - } - else { - Eterm tag = cancel ? am_cancel_timer : am_read_timer; - if (is_value(tref) && proc == c_p) - ref = tref; - else { - write_ref_thing(hp, - trefn[0], - trefn[1], - trefn[2]); - ref = make_internal_ref(hp); - hp += REF_THING_SIZE; - } - msg = TUPLE3(hp, tag, ref, result); + msg = (async + ? TUPLE3(hp, (cancel + ? am_cancel_timer + : am_read_timer), ref, result) + : TUPLE2(hp, ref, result)); - ERTS_HLT_ASSERT(hp + 4 == hp_end); + ERTS_HLT_ASSERT(hp + (async ? 4 : 3) == hp_end); - } - erts_queue_message(proc, &proc_locks, bp, msg, NIL); + erts_queue_message(proc, &proc_locks, mp, msg, NIL); if (c_p) proc_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -2093,16 +2077,19 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp, } } else { + ErtsMessage *mp; Eterm tag, res, msg; Uint hsz; Eterm *hp; ErtsProcLocks proc_locks = ERTS_PROC_LOCK_MAIN; + ErlOffHeap *ohp; hsz = 4; if (time_left > (Sint64) MAX_SMALL) hsz += ERTS_SINT64_HEAP_SIZE(time_left); - hp = HAlloc(c_p, hsz); + mp = erts_alloc_message_heap(c_p, &proc_locks, + hsz, &hp, &ohp); if (cancel) tag = am_cancel_timer; else @@ -2117,7 +2104,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp, msg = TUPLE3(hp, tag, tref, res); - erts_queue_message(c_p, &proc_locks, NULL, msg, NIL); + erts_queue_message(c_p, &proc_locks, mp, msg, NIL); proc_locks &= ~ERTS_PROC_LOCK_MAIN; if (proc_locks) diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 5c209a4af2..f396a0a156 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -431,7 +431,7 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** hp += 2; args = CONS(hp, env, args); - so.flags = SPO_SYSTEM_PROC; + so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC; res = erl_create_process(&parent, start_mod, am_start, args, &so); erts_smp_proc_unlock(&parent, ERTS_PROC_LOCK_MAIN); erts_cleanup_empty_process(&parent); @@ -630,6 +630,7 @@ void erts_usage(void) erts_fprintf(stderr, "-W set error logger warnings mapping,\n"); erts_fprintf(stderr, " see error_logger documentation for details\n"); + erts_fprintf(stderr, "-xohmq bool set default off_heap_message_queue flag for processes\n"); erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n"); erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024); erts_fprintf(stderr, "-zdntgc time set delayed node table gc in seconds\n"); @@ -2015,6 +2016,26 @@ erl_start(int argc, char **argv) } break; + case 'x': { + char *sub_param = argv[i]+2; + if (has_prefix("ohmq", sub_param)) { + arg = get_arg(sub_param+4, argv[i+1], &i); + if (sys_strcmp(arg, "true") == 0) + erts_default_spo_flags |= SPO_OFF_HEAP_MSGQ; + else if (sys_strcmp(arg, "false") == 0) + erts_default_spo_flags &= ~SPO_OFF_HEAP_MSGQ; + else { + erts_fprintf(stderr, + "Invalid off_heap_message_queue flag: %s\n", arg); + erts_usage(); + } + } else { + erts_fprintf(stderr, "bad -x option %s\n", argv[i]); + erts_usage(); + } + break; + } + case 'z': { char *sub_param = argv[i]+2; @@ -2068,7 +2089,8 @@ erl_start(int argc, char **argv) "Invalid ets busy wait threshold: %s\n", arg); erts_usage(); } - } else { + } + else { erts_fprintf(stderr, "bad -z option %s\n", argv[i]); erts_usage(); } diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index e23c79d301..79739501a8 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -33,8 +33,8 @@ #include "erl_binary.h" #include "dtrace-wrapper.h" -ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message, - ErlMessage, +ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message_ref, + ErtsMessageRef, ERL_MESSAGE_BUF_SZ, ERTS_ALC_T_MSG_REF) @@ -44,27 +44,20 @@ ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message, #undef HARD_DEBUG #endif - - - -#ifdef DEBUG -static ERTS_INLINE int in_heapfrag(const Eterm* ptr, const ErlHeapFragment *bp) +void +init_message(void) { - return ((unsigned)(ptr - bp->mem) < bp->used_size); + init_message_ref_alloc(); } -#endif - -void -init_message(void) +void *erts_alloc_message_ref(void) { - init_message_alloc(); + return (void *) message_ref_alloc(); } -void -free_message(ErlMessage* mp) +void erts_free_message_ref(void *mp) { - message_free(mp); + message_ref_free((ErtsMessageRef *) mp); } /* Allocate message buffer (size in words) */ @@ -74,7 +67,7 @@ new_message_buffer(Uint size) ErlHeapFragment* bp; bp = (ErlHeapFragment*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP_FRAG, ERTS_HEAP_FRAG_SIZE(size)); - ERTS_INIT_HEAP_FRAG(bp, size); + ERTS_INIT_HEAP_FRAG(bp, size, size); return bp; } @@ -203,83 +196,87 @@ free_message_buffer(ErlHeapFragment* bp) }while (bp != NULL); } -static ERTS_INLINE void -link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp) +void +erts_cleanup_messages(ErtsMessage *msgp) { - if (bp) { - /* Link the message buffer */ - bp->next = MBUF(proc); - MBUF(proc) = bp; - MBUF_SIZE(proc) += bp->used_size; - FLAGS(proc) |= F_FORCE_GC; - - /* Move any off_heap's into the process */ - if (bp->off_heap.first != NULL) { - struct erl_off_heap_header** next_p = &bp->off_heap.first; - while (*next_p != NULL) { - next_p = &((*next_p)->next); + ErtsMessage *mp = msgp; + while (mp) { + ErtsMessage *fmp; + ErlHeapFragment *bp; + if (is_non_value(ERL_MESSAGE_TERM(mp))) { + if (is_not_immed(ERL_MESSAGE_TOKEN(mp))) { + bp = (ErlHeapFragment *) mp->data.dist_ext->ext_endp; + erts_cleanup_offheap(&bp->off_heap); } - *next_p = MSO(proc).first; - MSO(proc).first = bp->off_heap.first; - bp->off_heap.first = NULL; - OH_OVERHEAD(&(MSO(proc)), bp->off_heap.overhead); + if (mp->data.dist_ext) + erts_free_dist_ext_copy(mp->data.dist_ext); } + else { + if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) + bp = mp->data.heap_frag; + else { + bp = mp->hfrag.next; + erts_cleanup_offheap(&mp->hfrag.off_heap); + } + if (bp) + free_message_buffer(bp); + } + fmp = mp; + mp = mp->next; + erts_free_message(fmp); } } -Eterm -erts_msg_distext2heap(Process *pp, - ErtsProcLocks *plcksp, - ErlHeapFragment **bpp, - Eterm *tokenp, - ErtsDistExternal *dist_extp) +ErtsMessage * +erts_realloc_shrink_message(ErtsMessage *mp, Uint sz, Eterm *brefs, Uint brefs_size) { - Eterm msg; - Uint tok_sz = 0; - Eterm *hp = NULL; - ErtsHeapFactory factory; - Sint sz; - - *bpp = NULL; - sz = erts_decode_dist_ext_size(dist_extp); - if (sz < 0) - goto decode_error; - if (is_not_nil(*tokenp)) { - ErlHeapFragment *heap_frag = erts_dist_ext_trailer(dist_extp); - tok_sz = heap_frag->used_size; - sz += tok_sz; - } - if (pp) { - ErlOffHeap *ohp; - hp = erts_alloc_message_heap(sz, bpp, &ohp, pp, plcksp); - } - else { - *bpp = new_message_buffer(sz); - hp = (*bpp)->mem; - } - erts_factory_message_init(&factory, pp, hp, *bpp); - msg = erts_decode_dist_ext(&factory, dist_extp); - if (is_non_value(msg)) - goto decode_error; - if (is_not_nil(*tokenp)) { - ErlHeapFragment *heap_frag = erts_dist_ext_trailer(dist_extp); - hp = erts_produce_heap(&factory, tok_sz, 0); - *tokenp = copy_struct(*tokenp, tok_sz, &hp, factory.off_heap); - erts_cleanup_offheap(&heap_frag->off_heap); + ErtsMessage *nmp = erts_realloc(ERTS_ALC_T_MSG, mp, + sizeof(ErtsMessage) + (sz - 1)*sizeof(Eterm)); + if (nmp != mp) { + Eterm *sp = &mp->hfrag.mem[0]; + Eterm *ep = sp + sz; + Sint offs = &nmp->hfrag.mem[0] - sp; + erts_offset_off_heap(&nmp->hfrag.off_heap, offs, sp, ep); + erts_offset_heap(&nmp->hfrag.mem[0], sz, offs, sp, ep); + if (brefs && brefs_size) + erts_offset_heap_ptr(brefs, brefs_size, offs, sp, ep); } - erts_free_dist_ext_copy(dist_extp); - erts_factory_close(&factory); - return msg; - decode_error: - if (is_not_nil(*tokenp)) { - ErlHeapFragment *heap_frag = erts_dist_ext_trailer(dist_extp); - erts_cleanup_offheap(&heap_frag->off_heap); + nmp->hfrag.used_size = sz; + nmp->hfrag.alloc_size = sz; + + return nmp; +} + +void +erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *first_bp) +{ + if (first_bp) { + ErlHeapFragment *bp = first_bp; + + while (1) { + /* Move any off_heap's into the process */ + if (bp->off_heap.first != NULL) { + struct erl_off_heap_header** next_p = &bp->off_heap.first; + while (*next_p != NULL) { + next_p = &((*next_p)->next); + } + *next_p = MSO(proc).first; + MSO(proc).first = bp->off_heap.first; + bp->off_heap.first = NULL; + OH_OVERHEAD(&(MSO(proc)), bp->off_heap.overhead); + } + MBUF_SIZE(proc) += bp->used_size; + if (!bp->next) + break; + bp = bp->next; + } + + /* Link the message buffer */ + bp->next = MBUF(proc); + MBUF(proc) = first_bp; } - erts_free_dist_ext_copy(dist_extp); - *bpp = NULL; - return THE_NON_VALUE; - } +} void erts_queue_dist_message(Process *rcvr, @@ -287,7 +284,7 @@ erts_queue_dist_message(Process *rcvr, ErtsDistExternal *dist_ext, Eterm token) { - ErlMessage* mp; + ErtsMessage* mp; #ifdef USE_VM_PROBES Sint tok_label = 0; Sint tok_lastcnt = 0; @@ -299,7 +296,17 @@ erts_queue_dist_message(Process *rcvr, ERTS_SMP_LC_ASSERT(*rcvr_locks == erts_proc_lc_my_proc_locks(rcvr)); - mp = message_alloc(); + mp = erts_alloc_message(0, NULL); + mp->data.dist_ext = dist_ext; + + ERL_MESSAGE_TERM(mp) = THE_NON_VALUE; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; + if (token == am_have_dt_utag) + ERL_MESSAGE_TOKEN(mp) = NIL; + else +#endif + ERL_MESSAGE_TOKEN(mp) = token; #ifdef ERTS_SMP if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) { @@ -318,58 +325,40 @@ erts_queue_dist_message(Process *rcvr, if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ); /* Drop message if receiver is exiting or has a pending exit ... */ - if (is_not_nil(token)) { - ErlHeapFragment *heap_frag; - heap_frag = erts_dist_ext_trailer(mp->data.dist_ext); - erts_cleanup_offheap(&heap_frag->off_heap); - } - erts_free_dist_ext_copy(dist_ext); - message_free(mp); + erts_cleanup_messages(mp); } else #endif if (IS_TRACED_FL(rcvr, F_TRACE_RECEIVE)) { /* 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)) + if (!erts_decode_dist_message(rcvr, *rcvr_locks, mp, 0)) + erts_free_message(mp); + else { + Eterm msg = ERL_MESSAGE_TERM(mp); + token = ERL_MESSAGE_TOKEN(mp); #ifdef USE_VM_PROBES - if (DTRACE_ENABLED(message_queued)) { - DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); - - dtrace_proc_str(rcvr, receiver_name); - if (token != NIL && token != am_have_dt_utag) { - tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); - tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); - tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); - } - DTRACE6(message_queued, - receiver_name, size_object(msg), rcvr->msg.len, - tok_label, tok_lastcnt, tok_serial); - } + if (DTRACE_ENABLED(message_queued)) { + DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(rcvr, receiver_name); + if (token != NIL && token != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); + } + DTRACE6(message_queued, + receiver_name, size_object(msg), rcvr->msg.len, + tok_label, tok_lastcnt, tok_serial); + } #endif - erts_queue_message(rcvr, rcvr_locks, mbuf, msg, token); + erts_queue_message(rcvr, rcvr_locks, mp, msg, token); + } } else { /* Enqueue message on external format */ - ERL_MESSAGE_TERM(mp) = THE_NON_VALUE; -#ifdef USE_VM_PROBES - ERL_MESSAGE_DT_UTAG(mp) = NIL; - if (token == am_have_dt_utag) { - ERL_MESSAGE_TOKEN(mp) = NIL; - } else { -#endif - ERL_MESSAGE_TOKEN(mp) = token; -#ifdef USE_VM_PROBES - } -#endif - mp->next = NULL; - #ifdef USE_VM_PROBES if (DTRACE_ENABLED(message_queued)) { DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); @@ -388,7 +377,7 @@ erts_queue_dist_message(Process *rcvr, tok_label, tok_lastcnt, tok_serial); } #endif - mp->data.dist_ext = dist_ext; + LINK_MESSAGE(rcvr, mp); if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) @@ -408,9 +397,9 @@ erts_queue_dist_message(Process *rcvr, static Sint queue_message(Process *c_p, Process* receiver, - ErtsProcLocks *receiver_locks, erts_aint32_t *receiver_state, - ErlHeapFragment* bp, + ErtsProcLocks *receiver_locks, + ErtsMessage* mp, Eterm message, Eterm seq_trace_token #ifdef USE_VM_PROBES @@ -419,31 +408,24 @@ queue_message(Process *c_p, ) { Sint res; - ErlMessage* mp; int locked_msgq = 0; - erts_aint_t state; - -#ifndef ERTS_SMP - ASSERT(bp != NULL || receiver->mbuf == NULL); -#endif + erts_aint32_t state; ERTS_SMP_LC_ASSERT(*receiver_locks == erts_proc_lc_my_proc_locks(receiver)); - mp = message_alloc(); - - if (receiver_state) - state = *receiver_state; - else - state = erts_smp_atomic32_read_acqb(&receiver->state); - #ifdef ERTS_SMP - 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_state) + state = *receiver_state; + else + state = erts_smp_atomic32_read_nob(&receiver->state); + if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) + goto exiting; + if (*receiver_locks & ERTS_PROC_LOCK_STATUS) { erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_STATUS); need_locks |= ERTS_PROC_LOCK_STATUS; @@ -451,13 +433,12 @@ queue_message(Process *c_p, erts_smp_proc_lock(receiver, need_locks); } locked_msgq = 1; - state = erts_smp_atomic32_read_nob(&receiver->state); - if (receiver_state) - *receiver_state = state; } #endif + state = erts_smp_atomic32_read_nob(&receiver->state); + if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) { #ifdef ERTS_SMP exiting: @@ -465,9 +446,7 @@ queue_message(Process *c_p, /* Drop message if receiver is exiting or has a pending exit... */ if (locked_msgq) erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ); - if (bp) - free_message_buffer(bp); - message_free(mp); + erts_cleanup_messages(mp); return 0; } @@ -476,13 +455,9 @@ queue_message(Process *c_p, #ifdef USE_VM_PROBES ERL_MESSAGE_DT_UTAG(mp) = dt_utag; #endif - mp->next = NULL; - mp->data.heap_frag = bp; -#ifndef ERTS_SMP res = receiver->msg.len; -#else - res = receiver->msg_inq.len; +#ifdef ERTS_SMP if (*receiver_locks & ERTS_PROC_LOCK_MAIN) { /* * We move 'in queue' to 'private queue' and place @@ -492,7 +467,7 @@ queue_message(Process *c_p, * we don't need to include the 'in queue' in * the root set when garbage collecting. */ - res += receiver->msg.len; + res += receiver->msg_inq.len; ERTS_SMP_MSGQ_MV_INQ2PRIVQ(receiver); LINK_MESSAGE_PRIVQ(receiver, mp); } @@ -544,19 +519,19 @@ queue_message(Process *c_p, void #ifdef USE_VM_PROBES erts_queue_message_probe(Process* receiver, ErtsProcLocks *receiver_locks, - ErlHeapFragment* bp, + ErtsMessage* mp, Eterm message, Eterm seq_trace_token, Eterm dt_utag) #else erts_queue_message(Process* receiver, ErtsProcLocks *receiver_locks, - ErlHeapFragment* bp, + ErtsMessage* mp, Eterm message, Eterm seq_trace_token) #endif { queue_message(NULL, receiver, - receiver_locks, NULL, - bp, + receiver_locks, + mp, message, seq_trace_token #ifdef USE_VM_PROBES @@ -565,246 +540,8 @@ erts_queue_message(Process* receiver, ErtsProcLocks *receiver_locks, ); } -void -erts_link_mbuf_to_proc(struct process *proc, ErlHeapFragment *bp) -{ - Eterm* htop = HEAP_TOP(proc); - - link_mbuf_to_proc(proc, bp); - if (htop < HEAP_LIMIT(proc)) { - *htop = make_pos_bignum_header(HEAP_LIMIT(proc)-htop-1); - HEAP_TOP(proc) = HEAP_LIMIT(proc); - } -} - -/* - * Moves content of message buffer attached to a message into a heap. - * The message buffer is deallocated. - */ -void -erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg) -{ - struct erl_off_heap_header* oh; - Eterm term, token, *fhp, *hp; - Sint offs; - Uint sz; - ErlHeapFragment *bp; -#ifdef USE_VM_PROBES - Eterm utag; -#endif - -#ifdef HARD_DEBUG - struct erl_off_heap_header* dbg_oh_start = off_heap->first; - Eterm dbg_term, dbg_token; - ErlHeapFragment *dbg_bp; - Uint *dbg_hp, *dbg_thp_start; - Uint dbg_term_sz, dbg_token_sz; -#ifdef USE_VM_PROBES - Eterm dbg_utag; - Uint dbg_utag_sz; -#endif -#endif - - bp = msg->data.heap_frag; - term = ERL_MESSAGE_TERM(msg); - token = ERL_MESSAGE_TOKEN(msg); -#ifdef USE_VM_PROBES - utag = ERL_MESSAGE_DT_UTAG(msg); -#endif - if (!bp) { -#ifdef USE_VM_PROBES - ASSERT(is_immed(term) && is_immed(token) && is_immed(utag)); -#else - ASSERT(is_immed(term) && is_immed(token)); -#endif - return; - } - -#ifdef HARD_DEBUG - dbg_term_sz = size_object(term); - dbg_token_sz = size_object(token); - dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz); -#ifdef USE_VM_PROBES - dbg_utag_sz = size_object(utag); - dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz + dbg_utag_sz ); -#endif - /*ASSERT(dbg_term_sz + dbg_token_sz == erts_msg_used_frag_sz(msg)); - Copied size may be smaller due to removed SubBins's or garbage. - Copied size may be larger due to duplicated shared terms. - */ - dbg_hp = dbg_bp->mem; - dbg_term = copy_struct(term, dbg_term_sz, &dbg_hp, &dbg_bp->off_heap); - dbg_token = copy_struct(token, dbg_token_sz, &dbg_hp, &dbg_bp->off_heap); -#ifdef USE_VM_PROBES - dbg_utag = copy_struct(utag, dbg_utag_sz, &dbg_hp, &dbg_bp->off_heap); -#endif - dbg_thp_start = *hpp; -#endif - - if (bp->next != NULL) { - erts_move_multi_frags(hpp, off_heap, bp, msg->m, -#ifdef USE_VM_PROBES - 3, -#else - 2, -#endif - 0); - goto copy_done; - } - - OH_OVERHEAD(off_heap, bp->off_heap.overhead); - sz = bp->used_size; - - ASSERT(is_immed(term) || in_heapfrag(ptr_val(term),bp)); - ASSERT(is_immed(token) || in_heapfrag(ptr_val(token),bp)); - - fhp = bp->mem; - hp = *hpp; - offs = hp - fhp; - - oh = NULL; - while (sz--) { - Uint cpy_sz; - Eterm val = *fhp++; - - switch (primary_tag(val)) { - case TAG_PRIMARY_IMMED1: - *hp++ = val; - break; - case TAG_PRIMARY_LIST: - case TAG_PRIMARY_BOXED: - ASSERT(in_heapfrag(ptr_val(val), bp)); - *hp++ = offset_ptr(val, offs); - break; - case TAG_PRIMARY_HEADER: - *hp++ = val; - switch (val & _HEADER_SUBTAG_MASK) { - case ARITYVAL_SUBTAG: - break; - case REFC_BINARY_SUBTAG: - case FUN_SUBTAG: - case EXTERNAL_PID_SUBTAG: - case EXTERNAL_PORT_SUBTAG: - case EXTERNAL_REF_SUBTAG: - oh = (struct erl_off_heap_header*) (hp-1); - cpy_sz = thing_arityval(val); - goto cpy_words; - default: - cpy_sz = header_arity(val); - - cpy_words: - ASSERT(sz >= cpy_sz); - sz -= cpy_sz; - while (cpy_sz >= 8) { - cpy_sz -= 8; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - } - switch (cpy_sz) { - case 7: *hp++ = *fhp++; - case 6: *hp++ = *fhp++; - case 5: *hp++ = *fhp++; - case 4: *hp++ = *fhp++; - case 3: *hp++ = *fhp++; - case 2: *hp++ = *fhp++; - case 1: *hp++ = *fhp++; - default: break; - } - if (oh) { - /* Add to offheap list */ - oh->next = off_heap->first; - off_heap->first = oh; - ASSERT(*hpp <= (Eterm*)oh); - ASSERT(hp > (Eterm*)oh); - oh = NULL; - } - break; - } - break; - } - } - - ASSERT(bp->used_size == hp - *hpp); - *hpp = hp; - - if (is_not_immed(token)) { - ASSERT(in_heapfrag(ptr_val(token), bp)); - ERL_MESSAGE_TOKEN(msg) = offset_ptr(token, offs); -#ifdef HARD_DEBUG - ASSERT(dbg_thp_start <= ptr_val(ERL_MESSAGE_TOKEN(msg))); - ASSERT(hp > ptr_val(ERL_MESSAGE_TOKEN(msg))); -#endif - } - - if (is_not_immed(term)) { - ASSERT(in_heapfrag(ptr_val(term),bp)); - ERL_MESSAGE_TERM(msg) = offset_ptr(term, offs); -#ifdef HARD_DEBUG - ASSERT(dbg_thp_start <= ptr_val(ERL_MESSAGE_TERM(msg))); - ASSERT(hp > ptr_val(ERL_MESSAGE_TERM(msg))); -#endif - } -#ifdef USE_VM_PROBES - if (is_not_immed(utag)) { - ASSERT(in_heapfrag(ptr_val(utag), bp)); - ERL_MESSAGE_DT_UTAG(msg) = offset_ptr(utag, offs); -#ifdef HARD_DEBUG - ASSERT(dbg_thp_start <= ptr_val(ERL_MESSAGE_DT_UTAG(msg))); - ASSERT(hp > ptr_val(ERL_MESSAGE_DT_UTAG(msg))); -#endif - } -#endif - -copy_done: - -#ifdef HARD_DEBUG - { - int i, j; - ErlHeapFragment* frag; - { - struct erl_off_heap_header* dbg_oh = off_heap->first; - i = j = 0; - while (dbg_oh != dbg_oh_start) { - dbg_oh = dbg_oh->next; - i++; - } - for (frag=bp; frag; frag=frag->next) { - dbg_oh = frag->off_heap.first; - while (dbg_oh) { - dbg_oh = dbg_oh->next; - j++; - } - } - ASSERT(i == j); - } - } -#endif - - - bp->off_heap.first = NULL; - free_message_buffer(bp); - msg->data.heap_frag = NULL; - -#ifdef HARD_DEBUG - ASSERT(eq(ERL_MESSAGE_TERM(msg), dbg_term)); - ASSERT(eq(ERL_MESSAGE_TOKEN(msg), dbg_token)); -#ifdef USE_VM_PROBES - ASSERT(eq(ERL_MESSAGE_DT_UTAG(msg), dbg_utag)); -#endif - free_message_buffer(dbg_bp); -#endif - -} - - Uint -erts_msg_attached_data_size_aux(ErlMessage *msg) +erts_msg_attached_data_size_aux(ErtsMessage *msg) { Sint sz; ASSERT(is_non_value(ERL_MESSAGE_TERM(msg))); @@ -833,29 +570,72 @@ erts_msg_attached_data_size_aux(ErlMessage *msg) return sz; } -void -erts_move_msg_attached_data_to_heap(ErtsHeapFactory* factory, - ErlMessage *msg) +ErtsMessage * +erts_try_alloc_message_on_heap(Process *pp, + erts_aint32_t *psp, + ErtsProcLocks *plp, + Uint sz, + Eterm **hpp, + ErlOffHeap **ohpp, + int *on_heap_p) { - if (is_value(ERL_MESSAGE_TERM(msg))) - erts_move_msg_mbuf_to_heap(&factory->hp, factory->off_heap, msg); - else if (msg->data.dist_ext) { - ASSERT(msg->data.dist_ext->heap_size >= 0); - if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) { - ErlHeapFragment *heap_frag; - heap_frag = erts_dist_ext_trailer(msg->data.dist_ext); - ERL_MESSAGE_TOKEN(msg) = copy_struct(ERL_MESSAGE_TOKEN(msg), - heap_frag->used_size, - &factory->hp, - factory->off_heap); - erts_cleanup_offheap(&heap_frag->off_heap); +#ifdef ERTS_SMP + int locked_main = 0; +#endif + ErtsMessage *mp; + + ASSERT(!(*psp & ERTS_PSFLG_OFF_HEAP_MSGQ)); + + if ( +#if defined(ERTS_SMP) + *plp & ERTS_PROC_LOCK_MAIN +#else + 1 +#endif + ) { +#ifdef ERTS_SMP + try_on_heap: +#endif + if ((*psp & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) + || (pp->flags & F_DISABLE_GC) + || HEAP_LIMIT(pp) - HEAP_TOP(pp) <= sz) { + /* + * The heap is either potentially in an inconsistent + * state, or not large enough. + */ +#ifdef ERTS_SMP + if (locked_main) { + *plp &= ~ERTS_PROC_LOCK_MAIN; + erts_smp_proc_unlock(pp, ERTS_PROC_LOCK_MAIN); + } +#endif + goto in_message_fragment; } - ERL_MESSAGE_TERM(msg) = erts_decode_dist_ext(factory, - msg->data.dist_ext); - erts_free_dist_ext_copy(msg->data.dist_ext); - msg->data.dist_ext = NULL; + + *hpp = HEAP_TOP(pp); + HEAP_TOP(pp) = *hpp + sz; + *ohpp = &MSO(pp); + mp = erts_alloc_message(0, NULL); + mp->data.attached = NULL; + *on_heap_p = !0; + } +#ifdef ERTS_SMP + else if (erts_smp_proc_trylock(pp, ERTS_PROC_LOCK_MAIN) == 0) { + locked_main = 1; + *psp = erts_smp_atomic32_read_nob(&pp->state); + *plp |= ERTS_PROC_LOCK_MAIN; + goto try_on_heap; + } +#endif + else { + in_message_fragment: + + mp = erts_alloc_message(sz, hpp); + *ohpp = sz == 0 ? NULL : &mp->hfrag.off_heap; + *on_heap_p = 0; } - /* else: bad external detected when calculating size */ + + return mp; } /* @@ -870,7 +650,8 @@ erts_send_message(Process* sender, unsigned flags) { Uint msize; - ErlHeapFragment* bp = NULL; + ErtsMessage* mp; + ErlOffHeap *ohp; Eterm token = NIL; Sint res = 0; #ifdef USE_VM_PROBES @@ -879,27 +660,31 @@ erts_send_message(Process* sender, Sint tok_label = 0; Sint tok_lastcnt = 0; Sint tok_serial = 0; + Eterm utag = NIL; #endif + erts_aint32_t receiver_state; BM_STOP_TIMER(system); BM_MESSAGE(message,sender,receiver); BM_START_TIMER(send); #ifdef USE_VM_PROBES *sender_name = *receiver_name = '\0'; - if (DTRACE_ENABLED(message_send)) { + if (DTRACE_ENABLED(message_send)) { erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), "%T", sender->common.id); erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), "%T", receiver->common.id); } #endif + + receiver_state = erts_smp_atomic32_read_nob(&receiver->state); + if (SEQ_TRACE_TOKEN(sender) != NIL && !(flags & ERTS_SND_FLG_NO_SEQ_TRACE)) { Eterm* hp; Eterm stoken = SEQ_TRACE_TOKEN(sender); Uint seq_trace_size = 0; #ifdef USE_VM_PROBES Uint dt_utag_size = 0; - Eterm utag = NIL; #endif BM_SWAP_TIMER(send,size); @@ -923,23 +708,32 @@ erts_send_message(Process* sender, } #endif - bp = new_message_buffer(msize + seq_trace_size + mp = erts_alloc_message_heap_state(receiver, + &receiver_state, + receiver_locks, + (msize #ifdef USE_VM_PROBES - + dt_utag_size + + dt_utag_size #endif - ); - hp = bp->mem; + + seq_trace_size), + &hp, + &ohp); BM_SWAP_TIMER(send,copy); - token = copy_struct(stoken, - seq_trace_size, - &hp, - &bp->off_heap); + if (is_immed(stoken)) + token = stoken; + else + token = copy_struct(stoken, seq_trace_size, &hp, ohp); + + if (is_not_immed(message)) + message = copy_struct(message, msize, &hp, ohp); - message = copy_struct(message, msize, &hp, &bp->off_heap); #ifdef USE_VM_PROBES if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) { - utag = copy_struct(DT_UTAG(sender), dt_utag_size, &hp, &bp->off_heap); + if (is_immed(DT_UTAG(sender))) + utag = DT_UTAG(sender); + else + utag = copy_struct(DT_UTAG(sender), dt_utag_size, ohp); #ifdef DTRACE_TAG_HARDDEBUG erts_fprintf(stderr, "Dtrace -> (%T) Spreading tag (%T) with " @@ -961,101 +755,49 @@ erts_send_message(Process* sender, msize, tok_label, tok_lastcnt, tok_serial); } #endif - res = queue_message(NULL, - receiver, - receiver_locks, - NULL, - bp, - message, - token -#ifdef USE_VM_PROBES - , utag -#endif - ); - BM_SWAP_TIMER(send,system); - } else if (sender == receiver) { - /* Drop message if receiver has a pending exit ... */ -#ifdef ERTS_SMP - ErtsProcLocks need_locks = (~(*receiver_locks) - & (ERTS_PROC_LOCK_MSGQ - | ERTS_PROC_LOCK_STATUS)); - if (need_locks) { - *receiver_locks |= need_locks; - if (erts_smp_proc_trylock(receiver, need_locks) == EBUSY) { - if (need_locks == ERTS_PROC_LOCK_MSGQ) { - erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_STATUS); - need_locks = ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS; - } - erts_smp_proc_lock(receiver, need_locks); - } - } - if (!ERTS_PROC_PENDING_EXIT(receiver)) -#endif - { - ErlMessage* mp = message_alloc(); - - DTRACE6(message_send, sender_name, receiver_name, - size_object(message), tok_label, tok_lastcnt, tok_serial); - mp->data.attached = NULL; - ERL_MESSAGE_TERM(mp) = message; - ERL_MESSAGE_TOKEN(mp) = NIL; -#ifdef USE_VM_PROBES - ERL_MESSAGE_DT_UTAG(mp) = NIL; -#endif - mp->next = NULL; - /* - * We move 'in queue' to 'private queue' and place - * message at the end of 'private queue' in order - * to ensure that the 'in queue' doesn't contain - * references into the heap. By ensuring this, - * we don't need to include the 'in queue' in - * the root set when garbage collecting. - */ - - ERTS_SMP_MSGQ_MV_INQ2PRIVQ(receiver); - LINK_MESSAGE_PRIVQ(receiver, mp); - - res = receiver->msg.len; - - if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) { - trace_receive(receiver, message); - } - } - BM_SWAP_TIMER(send,system); } else { - ErlOffHeap *ohp; Eterm *hp; - erts_aint32_t state; - BM_SWAP_TIMER(send,size); - msize = size_object(message); - BM_SWAP_TIMER(size,send); - hp = erts_alloc_message_heap_state(msize, - &bp, - &ohp, - receiver, - receiver_locks, - &state); - BM_SWAP_TIMER(send,copy); - message = copy_struct(message, msize, &hp, ohp); - BM_MESSAGE_COPIED(msz); - BM_SWAP_TIMER(copy,send); + if (receiver == sender && !(receiver_state & ERTS_PSFLG_OFF_HEAP_MSGQ)) { + mp = erts_alloc_message(0, NULL); + msize = 0; + } + else { + BM_SWAP_TIMER(send,size); + msize = size_object(message); + BM_SWAP_TIMER(size,send); + + mp = erts_alloc_message_heap_state(receiver, + &receiver_state, + receiver_locks, + msize, + &hp, + &ohp); + BM_SWAP_TIMER(send,copy); + if (is_not_immed(message)) + 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); - res = queue_message(sender, - receiver, - receiver_locks, - &state, - bp, - message, - token + } + + res = queue_message(sender, + receiver, + &receiver_state, + receiver_locks, + mp, + message, + token #ifdef USE_VM_PROBES - , NIL + , utag #endif - ); - BM_SWAP_TIMER(send,system); - } - return res; + ); + + BM_SWAP_TIMER(send,system); + + return res; } /* @@ -1075,7 +817,8 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, Uint sz_from; Eterm* hp; Eterm temptoken; - ErlHeapFragment* bp = NULL; + ErtsMessage* mp; + ErlOffHeap *ohp; if (token != NIL #ifdef USE_VM_PROBES @@ -1087,36 +830,483 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, sz_reason = size_object(reason); sz_token = size_object(token); sz_from = size_object(from); - bp = new_message_buffer(sz_reason + sz_from + sz_token + 4); - hp = bp->mem; - mess = copy_struct(reason, sz_reason, &hp, &bp->off_heap); - from_copy = copy_struct(from, sz_from, &hp, &bp->off_heap); + mp = erts_alloc_message_heap(to, to_locksp, + sz_reason + sz_from + sz_token + 4, + &hp, &ohp); + mess = copy_struct(reason, sz_reason, &hp, ohp); + from_copy = copy_struct(from, sz_from, &hp, ohp); save = TUPLE3(hp, am_EXIT, from_copy, mess); hp += 4; /* the trace token must in this case be updated by the caller */ seq_trace_output(token, save, SEQ_TRACE_SEND, to->common.id, NULL); - temptoken = copy_struct(token, sz_token, &hp, &bp->off_heap); - erts_queue_message(to, to_locksp, bp, save, temptoken); + temptoken = copy_struct(token, sz_token, &hp, ohp); + erts_queue_message(to, to_locksp, mp, save, temptoken); } else { - ErlOffHeap *ohp; sz_reason = size_object(reason); sz_from = IS_CONST(from) ? 0 : size_object(from); - hp = erts_alloc_message_heap(sz_reason+sz_from+4, - &bp, - &ohp, - to, - to_locksp); + mp = erts_alloc_message_heap(to, to_locksp, + sz_reason+sz_from+4, &hp, &ohp); mess = copy_struct(reason, sz_reason, &hp, ohp); from_copy = (IS_CONST(from) ? from : copy_struct(from, sz_from, &hp, ohp)); save = TUPLE3(hp, am_EXIT, from_copy, mess); - erts_queue_message(to, to_locksp, bp, save, NIL); + erts_queue_message(to, to_locksp, mp, save, NIL); } } +void erts_save_message_in_proc(Process *p, ErtsMessage *msgp) +{ + ErlHeapFragment *hfp; + + if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) + hfp = &msgp->hfrag; + else if (msgp->data.attached) { + hfp = msgp->data.heap_frag; + } + else { + erts_free_message(msgp); + return; /* Nothing to save */ + } + + while (1) { + struct erl_off_heap_header *ohhp = hfp->off_heap.first; + if (ohhp) { + for ( ; ohhp->next; ohhp = ohhp->next) + ; + ohhp->next = p->off_heap.first; + p->off_heap.first = hfp->off_heap.first; + hfp->off_heap.first = NULL; + } + p->off_heap.overhead += hfp->off_heap.overhead; + hfp->off_heap.overhead = 0; + p->mbuf_sz += hfp->used_size; + + if (!hfp->next) + break; + hfp = hfp->next; + } + + msgp->next = p->msg_frag; + p->msg_frag = msgp; +} + +Sint +erts_move_messages_off_heap(Process *c_p) +{ + int reds = 1; + /* + * Move all messages off heap. This *only* occurs when the + * process had off heap message disabled and just enabled + * it... + */ + ErtsMessage *mp; + + reds += c_p->msg.len / 10; + + ASSERT(erts_smp_atomic32_read_nob(&c_p->state) + & ERTS_PSFLG_OFF_HEAP_MSGQ); + ASSERT(c_p->flags & F_OFF_HEAP_MSGQ_CHNG); + + for (mp = c_p->msg.first; mp; mp = mp->next) { + Uint msg_sz, token_sz; +#ifdef USE_VM_PROBES + Uint utag_sz; +#endif + Eterm *hp; + ErlHeapFragment *hfrag; + + if (mp->data.attached) + continue; + + if (is_immed(ERL_MESSAGE_TERM(mp)) +#ifdef USE_VM_PROBES + && is_immed(ERL_MESSAGE_DT_UTAG(mp)) +#endif + && is_not_immed(ERL_MESSAGE_TOKEN(mp))) + continue; + + /* + * The message refers into the heap. Copy the message + * from the heap into a heap fragment and attach + * it to the message... + */ + msg_sz = size_object(ERL_MESSAGE_TERM(mp)); +#ifdef USE_VM_PROBES + utag_sz = size_object(ERL_MESSAGE_DT_UTAG(mp)); +#endif + token_sz = size_object(ERL_MESSAGE_TOKEN(mp)); + + hfrag = new_message_buffer(msg_sz +#ifdef USE_VM_PROBES + + utag_sz +#endif + + token_sz); + hp = hfrag->mem; + if (is_not_immed(ERL_MESSAGE_TERM(mp))) + ERL_MESSAGE_TERM(mp) = copy_struct(ERL_MESSAGE_TERM(mp), + msg_sz, &hp, + &hfrag->off_heap); + if (is_not_immed(ERL_MESSAGE_TOKEN(mp))) + ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp), + token_sz, &hp, + &hfrag->off_heap); +#ifdef USE_VM_PROBES + if (is_not_immed(ERL_MESSAGE_DT_UTAG(mp))) + ERL_MESSAGE_DT_UTAG(mp) = copy_struct(ERL_MESSAGE_DT_UTAG(mp), + utag_sz, &hp, + &hfrag->off_heap); +#endif + mp->data.heap_frag = hfrag; + reds += 1; + } + + return reds; +} + +Sint +erts_complete_off_heap_message_queue_change(Process *c_p) +{ + int reds = 1; + + ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p)); + ASSERT(c_p->flags & F_OFF_HEAP_MSGQ_CHNG); + ASSERT(erts_smp_atomic32_read_nob(&c_p->state) & ERTS_PSFLG_OFF_HEAP_MSGQ); + + /* + * This job was first initiated when the process changed + * "off heap message queue" state from false to true. Since + * then ERTS_PSFLG_OFF_HEAP_MSGQ has been set. However, the + * state change might have been changed again (multiple times) + * since then. Check users last requested state (the flag + * F_OFF_HEAP_MSGQ), and make the state consistent with that. + */ + + if (!(c_p->flags & F_OFF_HEAP_MSGQ)) + erts_smp_atomic32_read_band_nob(&c_p->state, + ~ERTS_PSFLG_OFF_HEAP_MSGQ); + else { + reds += 2; + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ); + ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ); + reds += erts_move_messages_off_heap(c_p); + } + c_p->flags &= ~F_OFF_HEAP_MSGQ_CHNG; + return reds; +} + +typedef struct { + Eterm pid; + ErtsThrPrgrLaterOp lop; +} ErtsChangeOffHeapMessageQueue; + +static void +change_off_heap_msgq(void *vcohmq) +{ + ErtsChangeOffHeapMessageQueue *cohmq; + /* + * Now we've waited thread progress which ensures that all + * messages to the process are enqueued off heap. Schedule + * completion of this change as a system task on the process + * itself. This in order to avoid lock contention on its + * main lock. We will be called in + * erts_complete_off_heap_message_queue_change() (above) when + * the system task has been selected for execution. + */ + cohmq = (ErtsChangeOffHeapMessageQueue *) vcohmq; + erts_schedule_complete_off_heap_message_queue_change(cohmq->pid); + erts_free(ERTS_ALC_T_MSGQ_CHNG, vcohmq); +} + +Eterm +erts_change_off_heap_message_queue_state(Process *c_p, int enable) +{ + +#ifdef DEBUG + if (c_p->flags & F_OFF_HEAP_MSGQ) { + ASSERT(erts_smp_atomic32_read_nob(&c_p->state) + & ERTS_PSFLG_OFF_HEAP_MSGQ); + } + else { + if (c_p->flags & F_OFF_HEAP_MSGQ_CHNG) { + ASSERT(erts_smp_atomic32_read_nob(&c_p->state) + & ERTS_PSFLG_OFF_HEAP_MSGQ); + } + else { + ASSERT(!(erts_smp_atomic32_read_nob(&c_p->state) + & ERTS_PSFLG_OFF_HEAP_MSGQ)); + } + } +#endif + + if (c_p->flags & F_OFF_HEAP_MSGQ) { + /* Off heap message queue is enabled */ + + if (!enable) { + c_p->flags &= ~F_OFF_HEAP_MSGQ; + /* + * We are not allowed to clear ERTS_PSFLG_OFF_HEAP_MSGQ + * if a change is ongoing. It will be adjusted when the + * change completes... + */ + if (!(c_p->flags & F_OFF_HEAP_MSGQ_CHNG)) { + /* Safe to clear ERTS_PSFLG_OFF_HEAP_MSGQ... */ + erts_smp_atomic32_read_band_nob(&c_p->state, + ~ERTS_PSFLG_OFF_HEAP_MSGQ); + } + } + + return am_true; /* Old state */ + } + + /* Off heap message queue is disabled */ + + if (enable) { + c_p->flags |= F_OFF_HEAP_MSGQ; + /* + * We do not have to schedule a change if + * we have an ongoing change... + */ + if (!(c_p->flags & F_OFF_HEAP_MSGQ_CHNG)) { + ErtsChangeOffHeapMessageQueue *cohmq; + /* + * Need to set ERTS_PSFLG_OFF_HEAP_MSGQ and wait + * thread progress before completing the change in + * order to ensure that all senders observe that + * messages should be passed off heap. When the + * change has completed, GC does not need to inspect + * the message queue at all. + */ + erts_smp_atomic32_read_bor_nob(&c_p->state, + ERTS_PSFLG_OFF_HEAP_MSGQ); + c_p->flags |= F_OFF_HEAP_MSGQ_CHNG; + cohmq = erts_alloc(ERTS_ALC_T_MSGQ_CHNG, + sizeof(ErtsChangeOffHeapMessageQueue)); + cohmq->pid = c_p->common.id; + erts_schedule_thr_prgr_later_op(change_off_heap_msgq, + (void *) cohmq, + &cohmq->lop); + } + } + + return am_false; /* Old state */ +} + +int +erts_decode_dist_message(Process *proc, ErtsProcLocks proc_locks, + ErtsMessage *msgp, int force_off_heap) +{ + ErtsHeapFactory factory; + Eterm msg; + ErlHeapFragment *bp; + Sint need; + int decode_in_heap_frag; + + decode_in_heap_frag = (force_off_heap + || !(proc_locks & ERTS_PROC_LOCK_MAIN) + || (proc->flags & F_OFF_HEAP_MSGQ)); + + if (msgp->data.dist_ext->heap_size >= 0) + need = msgp->data.dist_ext->heap_size; + else { + need = erts_decode_dist_ext_size(msgp->data.dist_ext); + if (need < 0) { + /* bad msg; remove it... */ + if (is_not_immed(ERL_MESSAGE_TOKEN(msgp))) { + bp = erts_dist_ext_trailer(msgp->data.dist_ext); + erts_cleanup_offheap(&bp->off_heap); + } + erts_free_dist_ext_copy(msgp->data.dist_ext); + msgp->data.dist_ext = NULL; + return 0; + } + + msgp->data.dist_ext->heap_size = need; + } + + if (is_not_immed(ERL_MESSAGE_TOKEN(msgp))) { + bp = erts_dist_ext_trailer(msgp->data.dist_ext); + need += bp->used_size; + } + + if (decode_in_heap_frag) + erts_factory_heap_frag_init(&factory, new_message_buffer(need)); + else + erts_factory_proc_prealloc_init(&factory, proc, need); + + ASSERT(msgp->data.dist_ext->heap_size >= 0); + if (is_not_immed(ERL_MESSAGE_TOKEN(msgp))) { + ErlHeapFragment *heap_frag; + heap_frag = erts_dist_ext_trailer(msgp->data.dist_ext); + ERL_MESSAGE_TOKEN(msgp) = copy_struct(ERL_MESSAGE_TOKEN(msgp), + heap_frag->used_size, + &factory.hp, + factory.off_heap); + erts_cleanup_offheap(&heap_frag->off_heap); + } + + msg = erts_decode_dist_ext(&factory, msgp->data.dist_ext); + ERL_MESSAGE_TERM(msgp) = msg; + erts_free_dist_ext_copy(msgp->data.dist_ext); + msgp->data.attached = NULL; + + if (is_non_value(msg)) { + erts_factory_undo(&factory); + return 0; + } + + erts_factory_trim_and_close(&factory, msgp->m, + ERL_MESSAGE_REF_ARRAY_SZ); + + ASSERT(!msgp->data.heap_frag); + + if (decode_in_heap_frag) + msgp->data.heap_frag = factory.heap_frags; + + return 1; +} + +/* + * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0 will move off heap messages + * into the heap of the inspected process if off_heap_message_queue + * is false when process_info(_, messages) is called. That is, the + * following GC will have more data in the rootset compared to the + * scenario when process_info(_, messages) had not been called. + * + * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS != 0 will keep off heap messages + * off heap when process_info(_, messages) is called regardless of + * the off_heap_message_queue setting of the process. That is, it + * will change the following execution of the process as little as + * possible. + */ +#define ERTS_INSPECT_MSGQ_KEEP_OH_MSGS 1 + +Uint +erts_prep_msgq_for_inspection(Process *c_p, Process *rp, + ErtsProcLocks rp_locks, ErtsMessageInfo *mip) +{ + Uint tot_heap_size; + ErtsMessage* mp; + Sint i; + int self_on_heap; + + /* + * Prepare the message queue for inspection + * by process_info(). + * + * + * - Decode all messages on external format + * - Remove all corrupt dist messages from queue + * - Save pointer to, and heap size need of each + * message in the mip array. + * - Return total heap size need for all messages + * that needs to be copied. + * + * If ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0: + * - In case off heap messages is disabled and + * we are inspecting our own queue, move all + * off heap data into the heap. + */ + + self_on_heap = c_p == rp && !(c_p->flags & F_OFF_HEAP_MSGQ); + + tot_heap_size = 0; + i = 0; + mp = rp->msg.first; + while (mp) { + Eterm msg = ERL_MESSAGE_TERM(mp); + + mip[i].size = 0; + + if (is_non_value(msg)) { + /* Dist message on external format; decode it... */ + if (mp->data.attached) + erts_decode_dist_message(rp, rp_locks, mp, + ERTS_INSPECT_MSGQ_KEEP_OH_MSGS); + + msg = ERL_MESSAGE_TERM(mp); + + if (is_non_value(msg)) { + ErtsMessage **mpp; + ErtsMessage *bad_mp = mp; + /* + * Bad distribution message; remove + * it from the queue... + */ + ASSERT(!mp->data.attached); + + mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next; + + if (rp->msg.save == &bad_mp->next) + rp->msg.save = mpp; + if (rp->msg.last == &bad_mp->next) + rp->msg.last = mpp; + mp = mp->next; + *mpp = mp; + rp->msg.len--; + bad_mp->next = NULL; + erts_cleanup_messages(bad_mp); + continue; + } + } + + ASSERT(is_value(msg)); + +#if ERTS_INSPECT_MSGQ_KEEP_OH_MSGS + if (is_not_immed(msg) && (!self_on_heap || mp->data.attached)) { + Uint sz = size_object(msg); + mip[i].size = sz; + tot_heap_size += sz; + } +#else + if (self_on_heap) { + if (mp->data.attached) { + ErtsMessage *tmp = NULL; + if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) { + erts_link_mbuf_to_proc(rp, mp->data.heap_frag); + mp->data.attached = NULL; + } + else { + /* + * Need to replace the message reference since + * we will get references to the message data + * from the heap... + */ + ErtsMessage **mpp; + tmp = erts_alloc_message(0, NULL); + sys_memcpy((void *) tmp->m, (void *) mp->m, + sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ); + mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next; + tmp->next = mp->next; + if (rp->msg.save == &mp->next) + rp->msg.save = &tmp->next; + if (rp->msg.last == &mp->next) + rp->msg.last = &tmp->next; + *mpp = tmp; + erts_save_message_in_proc(rp, mp); + mp = tmp; + } + } + } + else if (is_not_immed(msg)) { + Uint sz = size_object(msg); + mip[i].size = sz; + tot_heap_size += sz; + } + +#endif + + mip[i].msgp = mp; + i++; + mp = mp->next; + } + + return tot_heap_size; +} + void erts_factory_proc_init(ErtsHeapFactory* factory, Process* p) { @@ -1127,47 +1317,138 @@ void erts_factory_proc_prealloc_init(ErtsHeapFactory* factory, Process* p, Sint size) { + ErlHeapFragment *bp = p->mbuf; factory->mode = FACTORY_HALLOC; factory->p = p; factory->hp_start = HAlloc(p, size); factory->hp = factory->hp_start; factory->hp_end = factory->hp_start + size; factory->off_heap = &p->off_heap; + factory->message = NULL; factory->off_heap_saved.first = p->off_heap.first; factory->off_heap_saved.overhead = p->off_heap.overhead; - factory->heap_frags_saved = p->mbuf; + factory->heap_frags_saved = bp; + factory->heap_frags_saved_used = bp ? bp->used_size : 0; factory->heap_frags = NULL; /* not used */ factory->alloc_type = 0; /* not used */ } -void erts_factory_message_init(ErtsHeapFactory* factory, - Process* rp, - Eterm* hp, - ErlHeapFragment* bp) +void erts_factory_heap_frag_init(ErtsHeapFactory* factory, + ErlHeapFragment* bp) +{ + factory->mode = FACTORY_HEAP_FRAGS; + factory->p = NULL; + factory->hp_start = bp->mem; + factory->hp = bp->mem; + factory->hp_end = bp->mem + bp->alloc_size; + factory->off_heap = &bp->off_heap; + factory->message = NULL; + factory->heap_frags = bp; + factory->heap_frags_saved = NULL; + factory->heap_frags_saved_used = 0; + factory->alloc_type = ERTS_ALC_T_HEAP_FRAG; + ASSERT(!bp->next); + factory->off_heap_saved.first = factory->off_heap->first; + factory->off_heap_saved.overhead = factory->off_heap->overhead; + + ASSERT(factory->hp >= factory->hp_start && factory->hp <= factory->hp_end); +} + + +ErtsMessage * +erts_factory_message_create(ErtsHeapFactory* factory, + Process *proc, + ErtsProcLocks *proc_locksp, + Uint sz) +{ + Eterm *hp; + ErlOffHeap *ohp; + ErtsMessage *msgp; + int on_heap; + erts_aint32_t state; + + state = erts_smp_atomic32_read_nob(&proc->state); + + if (state & ERTS_PSFLG_OFF_HEAP_MSGQ) { + msgp = erts_alloc_message(sz, &hp); + ohp = sz == 0 ? NULL : &msgp->hfrag.off_heap; + on_heap = 0; + } + else { + msgp = erts_try_alloc_message_on_heap(proc, &state, + proc_locksp, + sz, &hp, &ohp, + &on_heap); + } + + if (on_heap) { + ASSERT(*proc_locksp & ERTS_PROC_LOCK_MAIN); + ASSERT(ohp == &proc->off_heap); + factory->mode = FACTORY_HALLOC; + factory->p = proc; + factory->heap_frags_saved = proc->mbuf; + factory->heap_frags_saved_used = proc->mbuf ? proc->mbuf->used_size : 0; + } + else { + factory->mode = FACTORY_MESSAGE; + factory->p = NULL; + factory->heap_frags_saved = NULL; + factory->heap_frags_saved_used = 0; + + if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) { + ASSERT(!msgp->hfrag.next); + factory->heap_frags = NULL; + } + else { + ASSERT(!msgp->data.heap_frag + || !msgp->data.heap_frag->next); + factory->heap_frags = msgp->data.heap_frag; + } + } + factory->hp_start = hp; + factory->hp = hp; + factory->hp_end = hp + sz; + factory->message = msgp; + factory->off_heap = ohp; + factory->alloc_type = ERTS_ALC_T_HEAP_FRAG; + if (ohp) { + factory->off_heap_saved.first = ohp->first; + factory->off_heap_saved.overhead = ohp->overhead; + } + else { + factory->off_heap_saved.first = NULL; + factory->off_heap_saved.overhead = 0; + } + + ASSERT(factory->hp >= factory->hp_start && factory->hp <= factory->hp_end); + + return msgp; +} + +void erts_factory_selfcontained_message_init(ErtsHeapFactory* factory, + ErtsMessage *msgp, + Eterm *hp) { - if (bp) { - factory->mode = FACTORY_HEAP_FRAGS; - factory->p = NULL; - factory->hp_start = bp->mem; - factory->hp = hp ? hp : bp->mem; - factory->hp_end = bp->mem + bp->alloc_size; - factory->off_heap = &bp->off_heap; - factory->heap_frags = bp; - factory->heap_frags_saved = bp; - factory->alloc_type = ERTS_ALC_T_HEAP_FRAG; - ASSERT(!bp->next); + ErlHeapFragment* bp; + if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) { + bp = &msgp->hfrag; + factory->heap_frags = NULL; } else { - factory->mode = FACTORY_HALLOC; - factory->p = rp; - factory->hp_start = hp; - factory->hp = hp; - factory->hp_end = HEAP_TOP(rp); - factory->off_heap = &rp->off_heap; - factory->heap_frags_saved = rp->mbuf; - factory->heap_frags = NULL; /* not used */ - factory->alloc_type = 0; /* not used */ + bp = msgp->data.heap_frag; + factory->heap_frags = bp; } + factory->mode = FACTORY_MESSAGE; + factory->p = NULL; + factory->hp_start = bp->mem; + factory->hp = hp; + factory->hp_end = bp->mem + bp->alloc_size; + factory->message = msgp; + factory->off_heap = &bp->off_heap; + factory->heap_frags_saved = NULL; + factory->heap_frags_saved_used = 0; + factory->alloc_type = ERTS_ALC_T_HEAP_FRAG; + ASSERT(!bp->next); factory->off_heap_saved.first = factory->off_heap->first; factory->off_heap_saved.overhead = factory->off_heap->overhead; @@ -1230,8 +1511,16 @@ static void reserve_heap(ErtsHeapFactory* factory, Uint need, Uint xtra) factory->hp_end = factory->hp + need; return; - case FACTORY_HEAP_FRAGS: - bp = factory->heap_frags; + case FACTORY_MESSAGE: + if (!factory->heap_frags) { + ASSERT(factory->message->data.attached == ERTS_MSG_COMBINED_HFRAG); + bp = &factory->message->hfrag; + } + else { + /* Fall through */ + case FACTORY_HEAP_FRAGS: + bp = factory->heap_frags; + } if (bp) { ASSERT(factory->hp > bp->mem); @@ -1269,8 +1558,23 @@ void erts_factory_close(ErtsHeapFactory* factory) HRelease(factory->p, factory->hp_end, factory->hp); break; - case FACTORY_HEAP_FRAGS: - bp = factory->heap_frags; + case FACTORY_MESSAGE: + if (!factory->heap_frags) { + if (factory->message->data.attached == ERTS_MSG_COMBINED_HFRAG) + bp = &factory->message->hfrag; + else + bp = NULL; + } + else { + if (factory->message->data.attached == ERTS_MSG_COMBINED_HFRAG) + factory->message->hfrag.next = factory->heap_frags; + else + factory->message->data.heap_frag = factory->heap_frags; + + /* Fall through */ + case FACTORY_HEAP_FRAGS: + bp = factory->heap_frags; + } if (bp) { ASSERT(factory->hp >= bp->mem); @@ -1291,17 +1595,47 @@ void erts_factory_close(ErtsHeapFactory* factory) void erts_factory_trim_and_close(ErtsHeapFactory* factory, Eterm *brefs, Uint brefs_size) { - if (factory->mode == FACTORY_HEAP_FRAGS) { - ErlHeapFragment* bp = factory->heap_frags; + ErlHeapFragment *bp; + + switch (factory->mode) { + case FACTORY_MESSAGE: { + ErtsMessage *mp = factory->message; + if (mp->data.attached == ERTS_MSG_COMBINED_HFRAG) { + if (!mp->hfrag.next) { + Uint sz = factory->hp - factory->hp_start; + mp = erts_shrink_message(mp, sz, brefs, brefs_size); + factory->message = mp; + factory->mode = FACTORY_CLOSED; + return; + } + /*else we don't trim multi fragmented messages for now (off_heap...) */ + break; + } + /* Fall through... */ + } + case FACTORY_HEAP_FRAGS: + bp = factory->heap_frags; + if (!bp) + break; if (bp->next == NULL) { Uint used_sz = factory->hp - bp->mem; ASSERT(used_sz <= bp->alloc_size); - factory->heap_frags = erts_resize_message_buffer(bp, used_sz, - brefs, brefs_size); + if (used_sz > 0) + bp = erts_resize_message_buffer(bp, used_sz, + brefs, brefs_size); + else { + free_message_buffer(bp); + bp = NULL; + } + factory->heap_frags = bp; + if (factory->mode == FACTORY_MESSAGE) + factory->message->data.heap_frag = bp; factory->mode = FACTORY_CLOSED; return; } - /*else we don't trim multi fragmented messages for now */ + /*else we don't trim multi fragmented messages for now (off_heap...) */ + default: + break; } erts_factory_close(factory); } @@ -1349,38 +1683,35 @@ void erts_factory_undo(ErtsHeapFactory* factory) /* Rollback heap top */ - if (factory->heap_frags_saved == NULL) { /* No heap frags when we started */ - ASSERT(factory->hp_start >= HEAP_START(factory->p)); - ASSERT(factory->hp_start <= HEAP_LIMIT(factory->p)); - HEAP_TOP(factory->p) = factory->hp_start; - } - else { + if (HEAP_START(factory->p) <= factory->hp_start + && factory->hp_start <= HEAP_LIMIT(factory->p)) { + HEAP_TOP(factory->p) = factory->hp_start; + } + + /* Fix last heap frag */ + if (factory->heap_frags_saved) { ASSERT(factory->heap_frags_saved == factory->p->mbuf); - if (factory->hp_start == factory->heap_frags_saved->mem) { + if (factory->hp_start != factory->heap_frags_saved->mem) + factory->heap_frags_saved->used_size = factory->heap_frags_saved_used; + else { factory->p->mbuf = factory->p->mbuf->next; ERTS_HEAP_FREE(ERTS_ALC_T_HEAP_FRAG, factory->heap_frags_saved, ERTS_HEAP_FRAG_SIZE(factory->heap_frags_saved->alloc_size)); } - else if (factory->hp_start != factory->hp_end) { - unsigned remains = factory->hp_start - factory->heap_frags_saved->mem; - ASSERT(remains > 0 && remains < factory->heap_frags_saved->used_size); - factory->heap_frags_saved->used_size = remains; - } } } break; + case FACTORY_MESSAGE: + if (factory->message->data.attached == ERTS_MSG_COMBINED_HFRAG) + factory->message->hfrag.next = factory->heap_frags; + else + factory->message->data.heap_frag = factory->heap_frags; + erts_cleanup_messages(factory->message); + break; case FACTORY_HEAP_FRAGS: - bp = factory->heap_frags; - do { - ErlHeapFragment* next_bp = bp->next; - - erts_cleanup_offheap(&bp->off_heap); - ERTS_HEAP_FREE(factory->alloc_type, (void *) bp, - ERTS_HEAP_FRAG_SIZE(bp->size)); - bp = next_bp; - }while (bp != NULL); + free_message_buffer(factory->heap_frags); break; case FACTORY_CLOSED: break; diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index f37b430d27..740ae46a0f 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -24,6 +24,8 @@ struct proc_bin; struct external_thing_; +typedef struct erl_mesg ErtsMessage; + /* * This struct represents data that must be updated by structure copy, * but is stored outside of any heap. @@ -54,6 +56,7 @@ typedef struct { enum { FACTORY_CLOSED = 0, FACTORY_HALLOC, + FACTORY_MESSAGE, FACTORY_HEAP_FRAGS, FACTORY_STATIC } mode; @@ -61,8 +64,10 @@ typedef struct { Eterm* hp_start; Eterm* hp; Eterm* hp_end; + ErtsMessage *message; struct erl_heap_fragment* heap_frags; struct erl_heap_fragment* heap_frags_saved; + Uint heap_frags_saved_used; ErlOffHeap* off_heap; ErlOffHeap off_heap_saved; Uint32 alloc_type; @@ -70,7 +75,10 @@ typedef struct { void erts_factory_proc_init(ErtsHeapFactory*, Process*); void erts_factory_proc_prealloc_init(ErtsHeapFactory*, Process*, Sint size); -void erts_factory_message_init(ErtsHeapFactory*, Process*, Eterm* hp, struct erl_heap_fragment*); +void erts_factory_heap_frag_init(ErtsHeapFactory*, struct erl_heap_fragment*); +ErtsMessage *erts_factory_message_create(ErtsHeapFactory *, Process *, + ErtsProcLocks *, Uint sz); +void erts_factory_selfcontained_message_init(ErtsHeapFactory*, ErtsMessage *, Eterm *); void erts_factory_static_init(ErtsHeapFactory*, Eterm* hp, Uint size, ErlOffHeap*); void erts_factory_dummy_init(ErtsHeapFactory*); @@ -91,6 +99,8 @@ void erts_factory_undo(ErtsHeapFactory*); #include "external.h" #include "erl_process.h" +#define ERTS_INVALID_HFRAG_PTR ((ErlHeapFragment *) ~((UWord) 7)) + /* * This struct represents a heap fragment, which is used when there * isn't sufficient room in the process heap and we can't do a GC. @@ -105,33 +115,46 @@ struct erl_heap_fragment { Eterm mem[1]; /* Data */ }; -typedef struct erl_mesg { - struct erl_mesg* next; /* Next message */ - union { - ErtsDistExternal *dist_ext; - ErlHeapFragment *heap_frag; - void *attached; - } data; -#ifdef USE_VM_PROBES - Eterm m[3]; /* m[0] = message, m[1] = seq trace token, m[3] = dynamic trace user tag */ -#else - Eterm m[2]; /* m[0] = message, m[1] = seq trace token */ -#endif -} ErlMessage; - +/* m[0] = message, m[1] = seq trace token */ +#define ERL_MESSAGE_REF_ARRAY_SZ 2 #define ERL_MESSAGE_TERM(mp) ((mp)->m[0]) #define ERL_MESSAGE_TOKEN(mp) ((mp)->m[1]) + #ifdef USE_VM_PROBES +/* m[2] = dynamic trace user tag */ +#undef ERL_MESSAGE_REF_ARRAY_SZ +#define ERL_MESSAGE_REF_ARRAY_SZ 3 #define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[2]) +#else #endif +#define ERL_MESSAGE_REF_FIELDS__ \ + ErtsMessage *next; /* Next message */ \ + union { \ + ErtsDistExternal *dist_ext; \ + ErlHeapFragment *heap_frag; \ + void *attached; \ + } data; \ + Eterm m[ERL_MESSAGE_REF_ARRAY_SZ] + + +typedef struct erl_msg_ref__ { + ERL_MESSAGE_REF_FIELDS__; +} ErtsMessageRef; + +struct erl_mesg { + ERL_MESSAGE_REF_FIELDS__; + + ErlHeapFragment hfrag; +}; + /* Size of default message buffer (erl_message.c) */ #define ERL_MESSAGE_BUF_SZ 500 typedef struct { - ErlMessage* first; - ErlMessage** last; /* point to the last next pointer */ - ErlMessage** save; + ErtsMessage* first; + ErtsMessage** last; /* point to the last next pointer */ + ErtsMessage** save; Sint len; /* queue length */ /* @@ -139,14 +162,14 @@ typedef struct { * recv_set/1 instructions. */ BeamInstr* mark; /* address to rec_loop/2 instruction */ - ErlMessage** saved_last; /* saved last pointer */ + ErtsMessage** saved_last; /* saved last pointer */ } ErlMessageQueue; #ifdef ERTS_SMP typedef struct { - ErlMessage* first; - ErlMessage** last; /* point to the last next pointer */ + ErtsMessage* first; + ErtsMessage** last; /* point to the last next pointer */ Sint len; /* queue length */ } ErlMessageInQueue; @@ -197,7 +220,7 @@ do { \ /* Unlink current message */ #define UNLINK_MESSAGE(p,msgp) do { \ - ErlMessage* __mp = (msgp)->next; \ + ErtsMessage* __mp = (msgp)->next; \ *(p)->msg.save = __mp; \ (p)->msg.len--; \ if (__mp == NULL) \ @@ -213,76 +236,33 @@ do { \ #define SAVE_MESSAGE(p) \ (p)->msg.save = &(*(p)->msg.save)->next -/* - * ErtsMoveMsgAttachmentIntoProc() moves data attached to a message - * onto the heap of a process. The attached data is the content of - * the the message either on the internal format or on the external - * format, and also possibly a seq trace token on the internal format. - * If the message content is on the external format, the decode might - * fail. If the decoding fails, ERL_MESSAGE_TERM(M) will contain - * THE_NON_VALUE. That is, ERL_MESSAGE_TERM(M) *has* to be checked - * afterwards and taken care of appropriately. - * - * ErtsMoveMsgAttachmentIntoProc() will shallow copy to heap if - * possible; otherwise, move to heap via garbage collection. - * - * ErtsMoveMsgAttachmentIntoProc() is used when receiveing messages - * in process_main() and in hipe_check_get_msg(). - */ - -#define ErtsMoveMsgAttachmentIntoProc(M, P, ST, HT, FC, SWPO, SWPI) \ -do { \ - if ((M)->data.attached) { \ - Uint need__ = erts_msg_attached_data_size((M)); \ - { SWPO ; } \ - if ((ST) - (HT) >= need__) { \ - ErtsHeapFactory factory__; \ - erts_factory_proc_prealloc_init(&factory__, (P), need__); \ - erts_move_msg_attached_data_to_heap(&factory__, (M)); \ - erts_factory_close(&factory__); \ - if ((P)->mbuf != NULL) { \ - /* Heap was exhausted by messages. This is a rare case */ \ - /* that can currently (OTP 18) only happen if hamts are */ \ - /* far exceeding the estimated heap size. Do GC. */ \ - (FC) -= erts_garbage_collect((P), 0, NULL, 0); \ - } \ - } \ - else { \ - (FC) -= erts_garbage_collect((P), 0, NULL, 0); \ - } \ - { SWPI ; } \ - ASSERT(!(M)->data.attached); \ - } \ -} while (0) - #define ERTS_SND_FLG_NO_SEQ_TRACE (((unsigned) 1) << 0) #define ERTS_HEAP_FRAG_SIZE(DATA_WORDS) \ (sizeof(ErlHeapFragment) - sizeof(Eterm) + (DATA_WORDS)*sizeof(Eterm)) -#define ERTS_INIT_HEAP_FRAG(HEAP_FRAG_P, DATA_WORDS) \ -do { \ - (HEAP_FRAG_P)->next = NULL; \ - (HEAP_FRAG_P)->alloc_size = (DATA_WORDS); \ - (HEAP_FRAG_P)->used_size = (DATA_WORDS); \ - (HEAP_FRAG_P)->off_heap.first = NULL; \ - (HEAP_FRAG_P)->off_heap.overhead = 0; \ -} while (0) +#define ERTS_INIT_HEAP_FRAG(HEAP_FRAG_P, USED_WORDS, DATA_WORDS) \ + do { \ + (HEAP_FRAG_P)->next = NULL; \ + (HEAP_FRAG_P)->alloc_size = (DATA_WORDS); \ + (HEAP_FRAG_P)->used_size = (USED_WORDS); \ + (HEAP_FRAG_P)->off_heap.first = NULL; \ + (HEAP_FRAG_P)->off_heap.overhead = 0; \ + } while (0) void init_message(void); -void free_message(ErlMessage *); ErlHeapFragment* new_message_buffer(Uint); ErlHeapFragment* erts_resize_message_buffer(ErlHeapFragment *, Uint, Eterm *, Uint); void free_message_buffer(ErlHeapFragment *); void erts_queue_dist_message(Process*, ErtsProcLocks*, ErtsDistExternal *, Eterm); #ifdef USE_VM_PROBES -void erts_queue_message_probe(Process*, ErtsProcLocks*, ErlHeapFragment*, +void erts_queue_message_probe(Process*, ErtsProcLocks*, ErtsMessage*, Eterm message, Eterm seq_trace_token, Eterm dt_utag); #define erts_queue_message(RP,RL,BP,Msg,SEQ) \ erts_queue_message_probe((RP),(RL),(BP),(Msg),(SEQ),NIL) #else -void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*, +void erts_queue_message(Process*, ErtsProcLocks*, ErtsMessage*, Eterm message, Eterm seq_trace_token); #define erts_queue_message_probe(RP,RL,BP,Msg,SEQ,TAG) \ erts_queue_message((RP),(RL),(BP),(Msg),(SEQ)) @@ -291,20 +271,141 @@ void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm); Sint erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned); void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp); -void erts_move_msg_mbuf_to_heap(Eterm**, ErlOffHeap*, ErlMessage *); - -Uint erts_msg_attached_data_size_aux(ErlMessage *msg); -void erts_move_msg_attached_data_to_heap(ErtsHeapFactory*, ErlMessage *); -Eterm erts_msg_distext2heap(Process *, ErtsProcLocks *, ErlHeapFragment **, - Eterm *, ErtsDistExternal *); +Uint erts_msg_attached_data_size_aux(ErtsMessage *msg); void erts_cleanup_offheap(ErlOffHeap *offheap); +void erts_save_message_in_proc(Process *p, ErtsMessage *msg); +Sint erts_move_messages_off_heap(Process *c_p); +Sint erts_complete_off_heap_message_queue_change(Process *c_p); +Eterm erts_change_off_heap_message_queue_state(Process *c_p, int enable); + +int erts_decode_dist_message(Process *, ErtsProcLocks, ErtsMessage *, int); + +void erts_cleanup_messages(ErtsMessage *mp); + +typedef struct { + Uint size; + ErtsMessage *msgp; +} ErtsMessageInfo; + +Uint erts_prep_msgq_for_inspection(Process *c_p, + Process *rp, + ErtsProcLocks rp_locks, + ErtsMessageInfo *mip); +void *erts_alloc_message_ref(void); +void erts_free_message_ref(void *); +#define ERTS_SMALL_FIX_MSG_SZ 10 +#define ERTS_MEDIUM_FIX_MSG_SZ 20 +#define ERTS_LARGE_FIX_MSG_SZ 30 + +void *erts_alloc_small_message(void); +void erts_free_small_message(void *mp); + +typedef struct { + ErtsMessage m; + Eterm data[ERTS_SMALL_FIX_MSG_SZ-1]; +} ErtsSmallFixSzMessage; + +typedef struct { + ErtsMessage m; + Eterm data[ERTS_MEDIUM_FIX_MSG_SZ-1]; +} ErtsMediumFixSzMessage; + +typedef struct { + ErtsMessage m; + Eterm data[ERTS_LARGE_FIX_MSG_SZ-1]; +} ErtsLargeFixSzMessage; + +ErtsMessage *erts_try_alloc_message_on_heap(Process *pp, + erts_aint32_t *psp, + ErtsProcLocks *plp, + Uint sz, + Eterm **hpp, + ErlOffHeap **ohpp, + int *on_heap_p); +ErtsMessage *erts_realloc_shrink_message(ErtsMessage *mp, Uint sz, + Eterm *brefs, Uint brefs_size); + +ERTS_GLB_FORCE_INLINE ErtsMessage *erts_alloc_message(Uint sz, Eterm **hpp); +ERTS_GLB_FORCE_INLINE ErtsMessage *erts_shrink_message(ErtsMessage *mp, Uint sz, + Eterm *brefs, Uint brefs_size); +ERTS_GLB_FORCE_INLINE void erts_free_message(ErtsMessage *mp); ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment*); -ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErlMessage *msg); +ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg); + +#define ERTS_MSG_COMBINED_HFRAG ((void *) 0x1) #if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_FORCE_INLINE ErtsMessage *erts_alloc_message(Uint sz, Eterm **hpp) +{ + ErtsMessage *mp; + + if (sz == 0) { + mp = erts_alloc_message_ref(); + mp->next = NULL; + ERL_MESSAGE_TERM(mp) = NIL; + mp->data.attached = NULL; + if (hpp) + *hpp = NULL; + return mp; + } + + mp = erts_alloc(ERTS_ALC_T_MSG, + sizeof(ErtsMessage) + (sz - 1)*sizeof(Eterm)); + + mp->next = NULL; + ERL_MESSAGE_TERM(mp) = NIL; + mp->data.attached = ERTS_MSG_COMBINED_HFRAG; + ERTS_INIT_HEAP_FRAG(&mp->hfrag, sz, sz); + + if (hpp) + *hpp = &mp->hfrag.mem[0]; + + return mp; +} + +ERTS_GLB_FORCE_INLINE ErtsMessage * +erts_shrink_message(ErtsMessage *mp, Uint sz, Eterm *brefs, Uint brefs_size) +{ + if (sz == 0) { + ErtsMessage *nmp; + if (!mp->data.attached) + return mp; + ASSERT(mp->data.attached == ERTS_MSG_COMBINED_HFRAG); + nmp = erts_alloc_message_ref(); +#ifdef DEBUG + if (brefs && brefs_size) { + int i; + for (i = 0; i < brefs_size; i++) + ASSERT(is_non_value(brefs[i]) || is_immed(brefs[i])); + } +#endif + erts_free(ERTS_ALC_T_MSG, mp); + return nmp; + } + + ASSERT(mp->data.attached == ERTS_MSG_COMBINED_HFRAG); + ASSERT(mp->hfrag.used_size >= sz); + + if (sz >= (mp->hfrag.alloc_size - mp->hfrag.alloc_size / 16)) { + mp->hfrag.used_size = sz; + return mp; + } + + return erts_realloc_shrink_message(mp, sz, brefs, brefs_size); +} + +ERTS_GLB_FORCE_INLINE void erts_free_message(ErtsMessage *mp) +{ + if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) + erts_free_message_ref(mp); + else + erts_free(ERTS_ALC_T_MSG, mp); +} + ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment* bp) { Uint sz = 0; @@ -314,11 +415,17 @@ ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment* bp) return sz; } -ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErlMessage *msg) +ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg) { ASSERT(msg->data.attached); - if (is_value(ERL_MESSAGE_TERM(msg))) - return erts_used_frag_sz(msg->data.heap_frag); + if (is_value(ERL_MESSAGE_TERM(msg))) { + ErlHeapFragment *bp; + if (msg->data.attached == ERTS_MSG_COMBINED_HFRAG) + bp = &msg->hfrag; + else + bp = msg->data.heap_frag; + return erts_used_frag_sz(bp); + } else if (msg->data.dist_ext->heap_size < 0) return erts_msg_attached_data_size_aux(msg); else { diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 01414f326d..a37cda93ef 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -314,6 +314,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ErtsProcLocks rp_locks = 0; Process* rp; Process* c_p; + ErtsMessage *mp; ErlHeapFragment* frags; Eterm receiver = to_pid->pid; int flush_me = 0; @@ -347,7 +348,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ASSERT(frags == MBUF(&menv->phony_proc)); if (frags != NULL) { /* Move all offheap's from phony proc to the first fragment. - Quick and dirty, but erts_move_msg_mbuf_to_heap doesn't care. */ + Quick and dirty... */ ASSERT(!is_offheap(&frags->off_heap)); frags->off_heap = MSO(&menv->phony_proc); clear_offheap(&MSO(&menv->phony_proc)); @@ -359,7 +360,9 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, if (flush_me) { flush_env(env); /* Needed for ERTS_HOLE_CHECK */ } - erts_queue_message(rp, &rp_locks, frags, msg, am_undefined); + mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = frags; + erts_queue_message(rp, &rp_locks, mp, msg, am_undefined); if (c_p == rp) rp_locks &= ~ERTS_PROC_LOCK_MAIN; if (rp_locks) diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 62a44f7129..a4da288e79 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -1401,56 +1401,50 @@ setup_reference_table(void) for (i = 0; i < max; i++) { Process *proc = erts_pix2proc(i); if (proc) { - ErlMessage *msg; + int mli; + ErtsMessage *msg_list[] = { + proc->msg.first, +#ifdef ERTS_SMP + proc->msg_inq.first, +#endif + proc->msg_frag}; /* Insert Heap */ insert_offheap(&(proc->off_heap), HEAP_REF, proc->common.id); - /* Insert message buffers */ + /* Insert heap fragments buffers */ for(hfp = proc->mbuf; hfp; hfp = hfp->next) insert_offheap(&(hfp->off_heap), HEAP_REF, proc->common.id); - /* Insert msg msg buffers */ - for (msg = proc->msg.first; msg; msg = msg->next) { - ErlHeapFragment *heap_frag = NULL; - if (msg->data.attached) { - if (is_value(ERL_MESSAGE_TERM(msg))) - heap_frag = msg->data.heap_frag; - else { - if (msg->data.dist_ext->dep) - insert_dist_entry(msg->data.dist_ext->dep, - HEAP_REF, proc->common.id, 0); - if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) - heap_frag = erts_dist_ext_trailer(msg->data.dist_ext); + + /* Insert msg buffers */ + for (mli = 0; mli < sizeof(msg_list)/sizeof(msg_list[0]); mli++) { + ErtsMessage *msg; + for (msg = msg_list[mli]; msg; msg = msg->next) { + ErlHeapFragment *heap_frag = NULL; + if (msg->data.attached) { + if (msg->data.attached == ERTS_MSG_COMBINED_HFRAG) + heap_frag = &msg->hfrag; + else if (is_value(ERL_MESSAGE_TERM(msg))) + heap_frag = msg->data.heap_frag; + else { + if (msg->data.dist_ext->dep) + insert_dist_entry(msg->data.dist_ext->dep, + HEAP_REF, proc->common.id, 0); + if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) + heap_frag = erts_dist_ext_trailer(msg->data.dist_ext); + } } - } - if (heap_frag) - insert_offheap(&(heap_frag->off_heap), - HEAP_REF, - proc->common.id); - } -#ifdef ERTS_SMP - for (msg = proc->msg_inq.first; msg; msg = msg->next) { - ErlHeapFragment *heap_frag = NULL; - if (msg->data.attached) { - if (is_value(ERL_MESSAGE_TERM(msg))) - heap_frag = msg->data.heap_frag; - else { - if (msg->data.dist_ext->dep) - insert_dist_entry(msg->data.dist_ext->dep, - HEAP_REF, proc->common.id, 0); - if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) - heap_frag = erts_dist_ext_trailer(msg->data.dist_ext); + while (heap_frag) { + insert_offheap(&(heap_frag->off_heap), + HEAP_REF, + proc->common.id); + heap_frag = heap_frag->next; } } - if (heap_frag) - insert_offheap(&(heap_frag->off_heap), - HEAP_REF, - proc->common.id); } -#endif /* Insert links */ if (ERTS_P_LINKS(proc)) insert_links(ERTS_P_LINKS(proc), proc->common.id); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index bad9da90ea..e490ffea5a 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -148,6 +148,7 @@ extern BeamInstr beam_apply[]; extern BeamInstr beam_exit[]; extern BeamInstr beam_continue_exit[]; +int erts_default_spo_flags = 0; int erts_eager_check_io = 1; int erts_sched_compact_load; int erts_sched_balance_util = 0; @@ -351,7 +352,8 @@ struct erts_system_profile_flags_t erts_system_profile_flags; typedef enum { ERTS_PSTT_GC, /* Garbage Collect */ - ERTS_PSTT_CPC /* Check Process Code */ + ERTS_PSTT_CPC, /* Check Process Code */ + ERTS_PSTT_COHMQ /* Change off heap message queue */ } ErtsProcSysTaskType; #define ERTS_MAX_PROC_SYS_TASK_ARGS 2 @@ -982,7 +984,7 @@ reply_sched_wall_time(void *vswtrp) Eterm **hpp; Uint sz, *szp; ErlOffHeap *ohp = NULL; - ErlHeapFragment *bp = NULL; + ErtsMessage *mp = NULL; ASSERT(esdp); #ifdef ERTS_DIRTY_SCHEDULERS @@ -1038,12 +1040,12 @@ reply_sched_wall_time(void *vswtrp) if (hpp) break; - hp = erts_alloc_message_heap(sz, &bp, &ohp, rp, &rp_locks); + mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp); szp = NULL; hpp = &hp; } - erts_queue_message(rp, &rp_locks, bp, msg, NIL); + erts_queue_message(rp, &rp_locks, mp, msg, NIL); if (swtrp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -6294,22 +6296,99 @@ erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks) schedule_process(p, state, locks); } -static void -schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) +static int +schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) { - /* - * Expects status lock to be locked when called, and - * returns with status lock unlocked... - */ - erts_aint32_t a = state, n, enq_prio = -1; + int res; + int locked; + ErtsProcSysTaskQs *stqs, *free_stqs; + erts_aint32_t state, a, n, enq_prio; int enqueue; /* < 0 -> use proxy */ - unsigned int prof_runnable_procs = erts_system_profile_flags.runnable_procs; + unsigned int prof_runnable_procs; + + res = 1; /* prepare for success */ + st->next = st->prev = st; /* Prep for empty prio queue */ + state = erts_smp_atomic32_read_nob(&p->state); + prof_runnable_procs = erts_system_profile_flags.runnable_procs; + locked = 0; + free_stqs = NULL; + if (state & ERTS_PSFLG_ACTIVE_SYS) + stqs = NULL; + else { + alloc_qs: + stqs = proc_sys_task_queues_alloc(); + stqs->qmask = 1 << prio; + stqs->ncount = 0; + stqs->q[PRIORITY_MAX] = NULL; + stqs->q[PRIORITY_HIGH] = NULL; + stqs->q[PRIORITY_NORMAL] = NULL; + stqs->q[PRIORITY_LOW] = NULL; + stqs->q[prio] = st; + } + + if (!locked) { + locked = 1; + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + + state = erts_smp_atomic32_read_nob(&p->state); + if (state & ERTS_PSFLG_EXITING) { + free_stqs = stqs; + res = 0; + goto cleanup; + } + } + + if (!p->sys_task_qs) { + if (stqs) + p->sys_task_qs = stqs; + else + goto alloc_qs; + } + else { + free_stqs = stqs; + stqs = p->sys_task_qs; + if (!stqs->q[prio]) { + stqs->q[prio] = st; + stqs->qmask |= 1 << prio; + } + else { + st->next = stqs->q[prio]; + st->prev = stqs->q[prio]->prev; + st->next->prev = st; + st->prev->next = st; + ASSERT(stqs->qmask & (1 << prio)); + } + } + + if (ERTS_PSFLGS_GET_ACT_PRIO(state) > prio) { + erts_aint32_t n, a, e; + /* Need to elevate actual prio */ + + a = state; + do { + if (ERTS_PSFLGS_GET_ACT_PRIO(a) <= prio) { + n = a; + break; + } + n = e = a; + n &= ~ERTS_PSFLGS_ACT_PRIO_MASK; + n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET); + a = erts_smp_atomic32_cmpxchg_nob(&p->state, n, e); + } while (a != e); + state = n; + } + + + a = state; + enq_prio = -1; /* Status lock prevents out of order "runnable proc" trace msgs */ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); - if (!prof_runnable_procs) + if (!prof_runnable_procs) { erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + locked = 0; + } ASSERT(!(state & ERTS_PSFLG_PROXY)); @@ -6317,8 +6396,10 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) erts_aint32_t e; n = e = a; - if (a & ERTS_PSFLG_FREE) + if (a & ERTS_PSFLG_FREE) { + res = 0; goto cleanup; /* We don't want to schedule free processes... */ + } enqueue = ERTS_ENQUEUE_NOT; n |= ERTS_PSFLG_ACTIVE_SYS; @@ -6342,29 +6423,24 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) } erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - prof_runnable_procs = 0; + locked = 0; } - if (enqueue != ERTS_ENQUEUE_NOT) { - Process *sched_p; - if (enqueue > 0) - sched_p = p; - else { - sched_p = make_proxy_proc(proxy, p, enq_prio); - proxy = NULL; - } - add2runq(sched_p, n, enq_prio); - } + if (enqueue != ERTS_ENQUEUE_NOT) + add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), + n, enq_prio); cleanup: - if (prof_runnable_procs) + if (locked) erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - if (proxy) - free_proxy_proc(proxy); + if (free_stqs) + proc_sys_task_queues_free(free_stqs); ERTS_SMP_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); + + return res; } static ERTS_INLINE int @@ -9696,13 +9772,13 @@ Process *schedule(Process *p, int calls) } } - if (!(state & ERTS_PSFLG_EXITING) - && ((FLAGS(p) & F_FORCE_GC) - || (MSO(p).overhead > BIN_VHEAP_SZ(p)))) { - reds -= erts_garbage_collect(p, 0, p->arg_reg, p->arity); - if (reds <= 0) { - p->fcalls = reds; - goto sched_out_proc; + if (ERTS_IS_GC_DESIRED(p)) { + if (!(state & ERTS_PSFLG_EXITING) && !(p->flags & F_DISABLE_GC)) { + reds -= erts_garbage_collect(p, 0, p->arg_reg, p->arity); + if (reds <= 0) { + p->fcalls = reds; + goto sched_out_proc; + } } } @@ -9742,7 +9818,7 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result) if (rp) { ErtsProcLocks rp_locks; ErlOffHeap *ohp; - ErlHeapFragment* bp; + ErtsMessage *mp; Eterm *hp, msg, req_id, result; Uint st_result_sz, hsz; #ifdef DEBUG @@ -9754,11 +9830,7 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result) st_result_sz = is_immed(st_result) ? 0 : size_object(st_result); hsz = st->req_id_sz + st_result_sz + 4 /* 3-tuple */; - hp = erts_alloc_message_heap(hsz, - &bp, - &ohp, - rp, - &rp_locks); + mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp); #ifdef DEBUG hp_start = hp; @@ -9783,7 +9855,7 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result) ASSERT(hp_start + hsz == hp); #endif - erts_queue_message(rp, &rp_locks, bp, msg, NIL); + erts_queue_message(rp, &rp_locks, mp, msg, NIL); if (c_p == rp) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -10005,6 +10077,10 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) st = NULL; } break; + case ERTS_PSTT_COHMQ: + reds += erts_complete_off_heap_message_queue_change(c_p); + st_res = am_true; + break; default: ERTS_INTERNAL_ERROR("Invalid process sys task type"); st_res = am_false; @@ -10047,6 +10123,9 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds) case ERTS_PSTT_CPC: st_res = am_false; break; + case ERTS_PSTT_COHMQ: + st_res = am_false; + break; default: ERTS_INTERNAL_ERROR("Invalid process sys task type"); st_res = am_false; @@ -10065,10 +10144,8 @@ BIF_RETTYPE erts_internal_request_system_task_3(BIF_ALIST_3) { Process *rp = erts_proc_lookup(BIF_ARG_1); - ErtsProcSysTaskQs *stqs, *free_stqs = NULL; ErtsProcSysTask *st = NULL; - erts_aint32_t prio, rp_state; - int rp_locked; + erts_aint32_t prio; Eterm noproc_res, req_type; if (!rp && !is_internal_pid(BIF_ARG_1)) { @@ -10125,7 +10202,6 @@ erts_internal_request_system_task_3(BIF_ALIST_3) } st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK, ERTS_PROC_SYS_TASK_SIZE(tot_sz)); - st->next = st->prev = st; /* Prep for empty prio queue */ ERTS_INIT_OFF_HEAP(&st->off_heap); hp = &st->heap[0]; @@ -10169,95 +10245,11 @@ erts_internal_request_system_task_3(BIF_ALIST_3) goto badarg; } - rp_state = erts_smp_atomic32_read_nob(&rp->state); - - rp_locked = 0; - - free_stqs = NULL; - if (rp_state & ERTS_PSFLG_ACTIVE_SYS) - stqs = NULL; - else { - alloc_qs: - stqs = proc_sys_task_queues_alloc(); - stqs->qmask = 1 << prio; - stqs->ncount = 0; - stqs->q[PRIORITY_MAX] = NULL; - stqs->q[PRIORITY_HIGH] = NULL; - stqs->q[PRIORITY_NORMAL] = NULL; - stqs->q[PRIORITY_LOW] = NULL; - stqs->q[prio] = st; - } - - if (!rp_locked) { - rp_locked = 1; - erts_smp_proc_lock(rp, ERTS_PROC_LOCK_STATUS); - - rp_state = erts_smp_atomic32_read_nob(&rp->state); - if (rp_state & ERTS_PSFLG_EXITING) { - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); - rp = NULL; - free_stqs = stqs; - goto noproc; - } + if (!schedule_process_sys_task(rp, prio, st)) { + noproc: + notify_sys_task_executed(BIF_P, st, noproc_res); } - if (!rp->sys_task_qs) { - if (stqs) - rp->sys_task_qs = stqs; - else - goto alloc_qs; - } - else { - if (stqs) - free_stqs = stqs; - stqs = rp->sys_task_qs; - if (!stqs->q[prio]) { - stqs->q[prio] = st; - stqs->qmask |= 1 << prio; - } - else { - st->next = stqs->q[prio]; - st->prev = stqs->q[prio]->prev; - st->next->prev = st; - st->prev->next = st; - ASSERT(stqs->qmask & (1 << prio)); - } - } - - if (ERTS_PSFLGS_GET_ACT_PRIO(rp_state) > prio) { - erts_aint32_t n, a, e; - /* Need to elevate actual prio */ - - a = rp_state; - do { - if (ERTS_PSFLGS_GET_ACT_PRIO(a) <= prio) { - n = a; - break; - } - n = e = a; - n &= ~ERTS_PSFLGS_ACT_PRIO_MASK; - n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET); - a = erts_smp_atomic32_cmpxchg_nob(&rp->state, n, e); - } while (a != e); - rp_state = n; - } - - /* - * schedule_process_sys_task() unlocks status - * lock on process. - */ - schedule_process_sys_task(rp, rp_state, NULL); - - if (free_stqs) - proc_sys_task_queues_free(free_stqs); - - BIF_RET(am_ok); - -noproc: - - notify_sys_task_executed(BIF_P, st, noproc_res); - if (free_stqs) - proc_sys_task_queues_free(free_stqs); BIF_RET(am_ok); badarg: @@ -10266,11 +10258,35 @@ badarg: erts_cleanup_offheap(&st->off_heap); erts_free(ERTS_ALC_T_PROC_SYS_TSK, st); } - if (free_stqs) - proc_sys_task_queues_free(free_stqs); BIF_ERROR(BIF_P, BADARG); } +void +erts_schedule_complete_off_heap_message_queue_change(Eterm pid) +{ + Process *rp = erts_proc_lookup(pid); + if (rp) { + ErtsProcSysTask *st; + erts_aint32_t state; + int i; + + st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK, + ERTS_PROC_SYS_TASK_SIZE(0)); + st->type = ERTS_PSTT_COHMQ; + st->requester = NIL; + st->reply_tag = NIL; + st->req_id = NIL; + st->req_id_sz = 0; + for (i = 0; i < ERTS_MAX_PROC_SYS_TASK_ARGS; i++) + st->arg[i] = NIL; + ERTS_INIT_OFF_HEAP(&st->off_heap); + state = erts_smp_atomic32_read_nob(&rp->state); + + if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state), st)) + erts_free(ERTS_ALC_T_PROC_SYS_TSK, st); + } +} + static void save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio) { @@ -10716,6 +10732,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). Eterm args, /* Arguments for function (must be well-formed list). */ ErlSpawnOpts* so) /* Options for spawn. */ { + Uint flags = erts_default_process_flags; ErtsRunQueue *rq = NULL; Process *p; Sint arity; /* Number of arguments. */ @@ -10753,6 +10770,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). state |= (((prio & ERTS_PSFLGS_PRIO_MASK) << ERTS_PSFLGS_ACT_PRIO_OFFSET) | ((prio & ERTS_PSFLGS_PRIO_MASK) << ERTS_PSFLGS_USR_PRIO_OFFSET)); + if (so->flags & SPO_OFF_HEAP_MSGQ) { + state |= ERTS_PSFLG_OFF_HEAP_MSGQ; + flags |= F_OFF_HEAP_MSGQ; + } + if (!rq) rq = erts_get_runq_proc(parent); @@ -10775,7 +10797,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). BM_SWAP_TIMER(size,system); heap_need = arg_size; - p->flags = erts_default_process_flags; + p->flags = flags; p->static_flags = 0; if (so->flags & SPO_SYSTEM_PROC) @@ -10824,6 +10846,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->stop = p->hend = p->heap + sz; p->htop = p->heap; p->heap_sz = sz; + p->abandoned_heap = NULL; + p->live_hf_end = ERTS_INVALID_HFRAG_PTR; p->catches = 0; p->bin_vheap_sz = p->min_vheap_size; @@ -10894,6 +10918,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->accessor_bif_timers = NULL; #endif p->mbuf = NULL; + p->msg_frag = NULL; p->mbuf_sz = 0; p->psd = NULL; p->dictionary = NULL; @@ -11029,6 +11054,8 @@ void erts_init_empty_process(Process *p) p->stop = NULL; p->hend = NULL; p->heap = NULL; + p->abandoned_heap = NULL; + p->live_hf_end = ERTS_INVALID_HFRAG_PTR; p->gen_gcs = 0; p->max_gen_gcs = 0; p->min_heap_size = 0; @@ -11061,6 +11088,7 @@ void erts_init_empty_process(Process *p) p->old_htop = NULL; p->old_heap = NULL; p->mbuf = NULL; + p->msg_frag = NULL; p->mbuf_sz = 0; p->psd = NULL; ERTS_P_MONITORS(p) = NULL; @@ -11149,6 +11177,8 @@ erts_debug_verify_clean_empty_process(Process* p) ASSERT(p->htop == NULL); ASSERT(p->stop == NULL); ASSERT(p->hend == NULL); + ASSERT(p->abandoned_heap == NULL); + ASSERT(p->live_hf_end == ERTS_INVALID_HFRAG_PTR); ASSERT(p->heap == NULL); ASSERT(p->common.id == ERTS_INVALID_PID); ASSERT(ERTS_TRACER_PROC(p) == NIL); @@ -11226,8 +11256,6 @@ erts_cleanup_empty_process(Process* p) static void delete_process(Process* p) { - ErlMessage* mp; - VERBOSE(DEBUG_PROCESSES, ("Removing process: %T\n",p->common.id)); /* Cleanup psd */ @@ -11283,24 +11311,8 @@ delete_process(Process* p) erts_erase_dicts(p); /* free all pending messages */ - mp = p->msg.first; - while(mp != NULL) { - ErlMessage* next_mp = mp->next; - if (mp->data.attached) { - if (is_value(mp->m[0])) - free_message_buffer(mp->data.heap_frag); - else { - if (is_not_nil(mp->m[1])) { - ErlHeapFragment *heap_frag; - heap_frag = (ErlHeapFragment *) mp->data.dist_ext->ext_endp; - erts_cleanup_offheap(&heap_frag->off_heap); - } - erts_free_dist_ext_copy(mp->data.dist_ext); - } - } - free_message(mp); - mp = next_mp; - } + erts_cleanup_messages(p->msg.first); + p->msg.first = NULL; ASSERT(!p->nodes_monitors); ASSERT(!p->suspend_monitors); @@ -11488,6 +11500,9 @@ static ERTS_INLINE void send_exit_message(Process *to, ErtsProcLocks *to_locksp, Eterm exit_term, Uint term_size, Eterm token) { + ErtsMessage *mp; + ErlOffHeap *ohp; + if (token == NIL #ifdef USE_VM_PROBES || token == am_have_dt_utag @@ -11495,14 +11510,12 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, ) { Eterm* hp; Eterm mess; - ErlHeapFragment* bp; - ErlOffHeap *ohp; - hp = erts_alloc_message_heap(term_size, &bp, &ohp, to, to_locksp); + mp = erts_alloc_message_heap(to, to_locksp, + term_size, &hp, &ohp); mess = copy_struct(exit_term, term_size, &hp, ohp); - erts_queue_message(to, to_locksp, bp, mess, NIL); + erts_queue_message(to, to_locksp, mp, mess, NIL); } else { - ErlHeapFragment* bp; Eterm* hp; Eterm mess; Eterm temp_token; @@ -11510,13 +11523,14 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, ASSERT(is_tuple(token)); sz_token = size_object(token); - bp = new_message_buffer(term_size+sz_token); - hp = bp->mem; - mess = copy_struct(exit_term, term_size, &hp, &bp->off_heap); + + mp = erts_alloc_message_heap(to, to_locksp, + term_size+sz_token, &hp, &ohp); + mess = copy_struct(exit_term, term_size, &hp, ohp); /* the trace token must in this case be updated by the caller */ seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, NULL); - temp_token = copy_struct(token, sz_token, &hp, &bp->off_heap); - erts_queue_message(to, to_locksp, bp, mess, temp_token); + temp_token = copy_struct(token, sz_token, &hp, ohp); + erts_queue_message(to, to_locksp, mp, mess, temp_token); } } diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 65422b8c15..c6376c0166 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -916,6 +916,7 @@ struct process { Eterm* stop; /* Stack top */ Eterm* heap; /* Heap start */ Eterm* hend; /* Heap end */ + Eterm* abandoned_heap; Uint heap_sz; /* Size of heap in words */ Uint min_heap_size; /* Minimum size of heap (in words). */ Uint min_vheap_size; /* Minimum size of virtual heap (in words). */ @@ -1012,8 +1013,10 @@ struct process { Uint16 gen_gcs; /* Number of (minor) generational GCs. */ Uint16 max_gen_gcs; /* Max minor gen GCs before fullsweep. */ ErlOffHeap off_heap; /* Off-heap data updated by copy_struct(). */ - ErlHeapFragment* mbuf; /* Pointer to message buffer list */ - Uint mbuf_sz; /* Size of all message buffers */ + ErlHeapFragment* mbuf; /* Pointer to heap fragment list */ + ErlHeapFragment* live_hf_end; + ErtsMessage *msg_frag; /* Pointer to message fragment list */ + Uint mbuf_sz; /* Total size of heap fragments and message fragments */ ErtsPSD *psd; /* Rarely used process specific data */ Uint64 bin_vheap_sz; /* Virtual heap block size for binaries */ @@ -1041,6 +1044,7 @@ struct process { #ifdef CHECK_FOR_HOLES Eterm* last_htop; /* No need to scan the heap below this point. */ ErlHeapFragment* last_mbuf; /* No need to scan beyond this mbuf. */ + ErlHeapFragment* heap_hfrag; /* Heap abandoned, htop now lives in this frag */ #endif #ifdef DEBUG @@ -1064,6 +1068,7 @@ extern const Process erts_invalid_process; do { \ (p)->last_htop = 0; \ (p)->last_mbuf = 0; \ + (p)->heap_hfrag = NULL; \ } while (0) # define ERTS_HOLE_CHECK(p) erts_check_for_holes((p)) @@ -1141,14 +1146,15 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15) #define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16) #define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17) +#define ERTS_PSFLG_OFF_HEAP_MSGQ ERTS_PSFLG_BIT(18) #ifdef ERTS_DIRTY_SCHEDULERS -#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(18) -#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(19) -#define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q ERTS_PSFLG_BIT(20) -#define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q ERTS_PSFLG_BIT(21) -#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 22) +#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(19) +#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(20) +#define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q ERTS_PSFLG_BIT(21) +#define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q ERTS_PSFLG_BIT(22) +#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 23) #else -#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 18) +#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 19) #endif #define ERTS_PSFLGS_IN_PRQ_MASK (ERTS_PSFLG_IN_PRQ_MAX \ @@ -1196,12 +1202,15 @@ void erts_check_for_holes(Process* p); #define SPO_USE_ARGS 2 #define SPO_MONITOR 4 #define SPO_SYSTEM_PROC 8 +#define SPO_OFF_HEAP_MSGQ 16 + +extern int erts_default_spo_flags; /* * The following struct contains options for a process to be spawned. */ typedef struct { - Uint flags; + int flags; int error_code; /* Error code returned from create_process(). */ Eterm mref; /* Monitor ref returned (if SPO_MONITOR was given). */ @@ -1283,6 +1292,9 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; #define F_P2PNR_RESCHED (1 << 9) /* Process has been rescheduled via erts_pid2proc_not_running() */ #define F_FORCE_GC (1 << 10) /* Force gc at process in-scheduling */ #define F_DISABLE_GC (1 << 11) /* Disable GC */ +#define F_OFF_HEAP_MSGQ (1 << 12) /* Off heap msg queue */ +#define F_OFF_HEAP_MSGQ_CHNG (1 << 13) /* Off heap msg queue changing */ +#define F_ABANDONED_HEAP_USE (1 << 14) /* Have usage of abandoned heap */ /* process trace_flags */ #define F_SENSITIVE (1 << 0) @@ -1616,6 +1628,7 @@ void erts_schedule_thr_prgr_later_cleanup_op(void (*)(void *), void *, ErtsThrPrgrLaterOp *, UWord); +void erts_schedule_complete_off_heap_message_queue_change(Eterm pid); #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) int erts_dbg_check_halloc_lock(Process *p); @@ -1743,7 +1756,7 @@ Uint erts_debug_nbalance(void); int erts_debug_wait_completed(Process *c_p, int flags); -Uint erts_process_memory(Process *c_p); +Uint erts_process_memory(Process *c_p, int incl_msg_inq); #ifdef ERTS_SMP # define ERTS_GET_SCHEDULER_DATA_FROM_PROC(PROC) ((PROC)->scheduler_data) @@ -2058,6 +2071,22 @@ ERTS_GLB_INLINE void erts_smp_xrunq_unlock(ErtsRunQueue *rq, ErtsRunQueue *xrq); ERTS_GLB_INLINE void erts_smp_runqs_lock(ErtsRunQueue *rq1, ErtsRunQueue *rq2); ERTS_GLB_INLINE void erts_smp_runqs_unlock(ErtsRunQueue *rq1, ErtsRunQueue *rq2); +ERTS_GLB_INLINE ErtsMessage *erts_alloc_message_heap_state(Process *pp, + erts_aint32_t *psp, + ErtsProcLocks *plp, + Uint sz, + Eterm **hpp, + ErlOffHeap **ohpp); +ERTS_GLB_INLINE ErtsMessage *erts_alloc_message_heap(Process *pp, + ErtsProcLocks *plp, + Uint sz, + Eterm **hpp, + ErlOffHeap **ohpp); + +ERTS_GLB_INLINE void erts_shrink_message_heap(ErtsMessage **msgpp, Process *pp, + Eterm *start_hp, Eterm *used_hp, Eterm *end_hp, + Eterm *brefs, Uint brefs_size); + #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE @@ -2206,6 +2235,63 @@ erts_smp_runqs_unlock(ErtsRunQueue *rq1, ErtsRunQueue *rq2) #endif } +ERTS_GLB_INLINE ErtsMessage * +erts_alloc_message_heap_state(Process *pp, + erts_aint32_t *psp, + ErtsProcLocks *plp, + Uint sz, + Eterm **hpp, + ErlOffHeap **ohpp) +{ + int on_heap; + + if ((*psp) & ERTS_PSFLG_OFF_HEAP_MSGQ) { + ErtsMessage *mp = erts_alloc_message(sz, hpp); + *ohpp = sz == 0 ? NULL : &mp->hfrag.off_heap; + return mp; + } + + return erts_try_alloc_message_on_heap(pp, psp, plp, sz, hpp, ohpp, &on_heap); +} + +ERTS_GLB_INLINE ErtsMessage * +erts_alloc_message_heap(Process *pp, + ErtsProcLocks *plp, + Uint sz, + Eterm **hpp, + ErlOffHeap **ohpp) +{ + erts_aint32_t state = erts_smp_atomic32_read_nob(&pp->state); + return erts_alloc_message_heap_state(pp, &state, plp, sz, hpp, ohpp); +} + +ERTS_GLB_INLINE void +erts_shrink_message_heap(ErtsMessage **msgpp, Process *pp, + Eterm *start_hp, Eterm *used_hp, Eterm *end_hp, + Eterm *brefs, Uint brefs_size) +{ + ASSERT(start_hp <= used_hp && used_hp <= end_hp); + if ((*msgpp)->data.attached == ERTS_MSG_COMBINED_HFRAG) + *msgpp = erts_shrink_message(*msgpp, used_hp - start_hp, + brefs, brefs_size); + else if (!(*msgpp)->data.attached) { + ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN + & erts_proc_lc_my_proc_locks(pp)); + HRelease(pp, end_hp, used_hp); + } + else { + ErlHeapFragment *hfrag = (*msgpp)->data.heap_frag; + if (start_hp != used_hp) + hfrag = erts_resize_message_buffer(hfrag, used_hp - start_hp, + brefs, brefs_size); + else { + free_message_buffer(hfrag); + hfrag = NULL; + } + (*msgpp)->data.heap_frag = hfrag; + } +} + #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ ERTS_GLB_INLINE ErtsAtomCacheMap *erts_get_atom_cache_map(Process *c_p); diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index 3b8ae11e94..71396561a3 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -78,13 +78,14 @@ erts_deep_process_dump(int to, void *to_arg) dump_binaries(to, to_arg, all_binaries); } -Uint erts_process_memory(Process *p) { - ErlMessage *mp; +Uint erts_process_memory(Process *p, int incl_msg_inq) { + ErtsMessage *mp; Uint size = 0; struct saved_calls *scb; size += sizeof(Process); - ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p); + if (incl_msg_inq) + ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p); erts_doforall_links(ERTS_P_LINKS(p), &erts_one_link_size, &size); erts_doforall_monitors(ERTS_P_MONITORS(p), &erts_one_mon_size, &size); @@ -92,7 +93,7 @@ Uint erts_process_memory(Process *p) { if (p->old_hend && p->old_heap) size += (p->old_hend - p->old_heap) * sizeof(Eterm); - size += p->msg.len * sizeof(ErlMessage); + size += p->msg.len * sizeof(ErtsMessage); for (mp = p->msg.first; mp; mp = mp->next) if (mp->data.attached) @@ -119,7 +120,7 @@ static void dump_process_info(int to, void *to_arg, Process *p) { Eterm* sp; - ErlMessage* mp; + ErtsMessage* mp; int yreg = -1; ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p); @@ -657,6 +658,8 @@ erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg) { erts_print(to, to_arg, "PROXY"); break; case ERTS_PSFLG_DELAYED_SYS: erts_print(to, to_arg, "DELAYED_SYS"); break; + case ERTS_PSFLG_OFF_HEAP_MSGQ: + erts_print(to, to_arg, "OFF_HEAP_MSGQ"); break; #ifdef ERTS_DIRTY_SCHEDULERS case ERTS_PSFLG_DIRTY_CPU_PROC: erts_print(to, to_arg, "DIRTY_CPU_PROC"); break; diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index 788348e613..a64c993e8f 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -854,9 +854,6 @@ ERTS_GLB_INLINE void erts_proc_dec_refc(Process *p) #endif if (!referred) { ASSERT(ERTS_PROC_IS_EXITING(p)); - ASSERT(ERTS_AINT_NULL - == erts_ptab_pix2intptr_ddrb(&erts_proc, - internal_pid_index(p->common.id))); erts_free_proc(p); } } @@ -872,9 +869,6 @@ ERTS_GLB_INLINE void erts_proc_add_refc(Process *p, Sint add_refc) #endif if (!referred) { ASSERT(ERTS_PROC_IS_EXITING(p)); - ASSERT(ERTS_AINT_NULL - == erts_ptab_pix2intptr_ddrb(&erts_proc, - internal_pid_index(p->common.id))); erts_free_proc(p); } } diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 7327e0b48c..7ec64506e8 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -1919,15 +1919,16 @@ send_time_offset_changed_notifications(void *new_offsetp) ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK; erts_smp_proc_lock(rp, ERTS_PROC_LOCK_LINK); if (erts_lookup_monitor(ERTS_P_MONITORS(rp), ref)) { - ErlHeapFragment *bp; + ErtsMessage *mp; ErlOffHeap *ohp; Eterm message; - hp = erts_alloc_message_heap(hsz, &bp, &ohp, rp, &rp_locks); + mp = erts_alloc_message_heap(rp, &rp_locks, + hsz, &hp, &ohp); *patch_refp = ref; ASSERT(hsz == size_object(message_template)); message = copy_struct(message_template, hsz, &hp, ohp); - erts_queue_message(rp, &rp_locks, bp, message, NIL); + erts_queue_message(rp, &rp_locks, mp, message, NIL); } erts_smp_proc_unlock(rp, rp_locks); } diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index e9dd96efc4..d02f1f7213 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -114,15 +114,10 @@ void erts_init_trace(void) { static Eterm system_seq_tracer; -#ifdef ERTS_SMP #define ERTS_ALLOC_SYSMSG_HEAP(SZ, BPP, OHPP, UNUSED) \ (*(BPP) = new_message_buffer((SZ)), \ *(OHPP) = &(*(BPP))->off_heap, \ (*(BPP))->mem) -#else -#define ERTS_ALLOC_SYSMSG_HEAP(SZ, BPP, OHPP, RPP) \ - erts_alloc_message_heap((SZ), (BPP), (OHPP), (RPP), 0) -#endif #ifdef ERTS_SMP #define ERTS_ENQ_TRACE_MSG(FPID, TPID, MSG, BP) \ @@ -131,8 +126,12 @@ do { \ enqueue_sys_msg_unlocked(SYS_MSG_TYPE_TRACE, (FPID), (TPID), (MSG), (BP)); \ } while(0) #else -#define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \ - erts_queue_message((TPROC), NULL, (BP), (MSG), NIL) +#define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \ + do { \ + ErtsMessage *mp__ = erts_alloc_message(0, NULL); \ + mp__->data.heap_frag = (BP); \ + erts_queue_message((TPROC), NULL, mp__, (MSG), NIL); \ + } while (0) #endif /* @@ -591,11 +590,9 @@ send_to_port(Process *c_p, Eterm message, static void profile_send(Eterm from, Eterm message) { Uint sz = 0; - ErlHeapFragment *bp = NULL; Uint *hp = NULL; Eterm msg = NIL; Process *profile_p = NULL; - ErlOffHeap *off_heap = NULL; Eterm profiler = erts_get_system_profile(); @@ -621,6 +618,7 @@ profile_send(Eterm from, Eterm message) { } } else { + ErtsMessage *mp; ASSERT(is_internal_pid(profiler)); profile_p = erts_proc_lookup(profiler); @@ -629,10 +627,13 @@ profile_send(Eterm from, Eterm message) { return; sz = size_object(message); - hp = erts_alloc_message_heap(sz, &bp, &off_heap, profile_p, 0); - msg = copy_struct(message, sz, &hp, &bp->off_heap); - - erts_queue_message(profile_p, NULL, bp, msg, NIL); + mp = erts_alloc_message(sz, &hp); + if (sz == 0) + msg = message; + else + msg = copy_struct(message, sz, &hp, &mp->hfrag.off_heap); + + erts_queue_message(profile_p, NULL, mp, msg, NIL); } } @@ -1233,7 +1234,11 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, erts_smp_mtx_unlock(&smq_mtx); #else /* trace_token must be NIL here */ - erts_queue_message(tracer, NULL, bp, mess, NIL); + { + ErtsMessage *mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = bp; + erts_queue_message(tracer, NULL, mp, mess, NIL); + } #endif } } @@ -2308,7 +2313,11 @@ monitor_long_schedule_proc(Process *p, BeamInstr *in_fp, BeamInstr *out_fp, Uint #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL); + { + ErtsMessage *mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = bp; + erts_queue_message(monitor_p, NULL, mp, msg, NIL); + } #endif } void @@ -2369,7 +2378,11 @@ monitor_long_schedule_port(Port *pp, ErtsPortTaskType type, Uint time) #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, pp->common.id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL); + { + ErtsMessage *mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = bp; + erts_queue_message(monitor_p, NULL, mp, msg, NIL); + } #endif } @@ -2440,7 +2453,11 @@ monitor_long_gc(Process *p, Uint time) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL); + { + ErtsMessage *mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = bp; + erts_queue_message(monitor_p, NULL, mp, msg, NIL); + } #endif } @@ -2511,7 +2528,11 @@ monitor_large_heap(Process *p) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL); + { + ErtsMessage *mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = bp; + erts_queue_message(monitor_p, NULL, mp, msg, NIL); + } #endif } @@ -2539,7 +2560,11 @@ monitor_generic(Process *p, Eterm type, Eterm spec) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL); + { + ErtsMessage *mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = bp; + erts_queue_message(monitor_p, NULL, mp, msg, NIL); + } #endif } @@ -3331,8 +3356,11 @@ sys_msg_dispatcher_func(void *unused) goto failure; } else { + ErtsMessage *mp; queue_proc_msg: - erts_queue_message(proc,&proc_locks,smqp->bp,smqp->msg,NIL); + mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = smqp->bp; + erts_queue_message(proc,&proc_locks,mp,smqp->msg,NIL); #ifdef DEBUG_PRINTOUTS erts_fprintf(stderr, "delivered\n"); #endif diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 052994b972..594c0ccf94 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1274,11 +1274,11 @@ int erts_print_system_version(int to, void *arg, Process *c_p); int erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* reg); -ERTS_GLB_INLINE int erts_is_literal(Eterm tptr, Eterm *ptr); +ERTS_GLB_FORCE_INLINE int erts_is_literal(Eterm tptr, Eterm *ptr); #if ERTS_GLB_INLINE_INCL_FUNC_DEF -ERTS_GLB_INLINE int erts_is_literal(Eterm tptr, Eterm *ptr) +ERTS_GLB_FORCE_INLINE int erts_is_literal(Eterm tptr, Eterm *ptr) { ASSERT(is_boxed(tptr) || is_list(tptr)); ASSERT(ptr == ptr_val(tptr)); @@ -1347,124 +1347,6 @@ extern erts_driver_t fd_driver; int erts_beam_jump_table(void); -/* Should maybe be placed in erl_message.h, but then we get an include mess. */ -ERTS_GLB_INLINE Eterm * -erts_alloc_message_heap_state(Uint size, - ErlHeapFragment **bpp, - ErlOffHeap **ohpp, - Process *receiver, - ErtsProcLocks *receiver_locks, - erts_aint32_t *statep); - -ERTS_GLB_INLINE Eterm * -erts_alloc_message_heap(Uint size, - ErlHeapFragment **bpp, - ErlOffHeap **ohpp, - Process *receiver, - ErtsProcLocks *receiver_locks); - -#if ERTS_GLB_INLINE_INCL_FUNC_DEF - -/* - * NOTE: erts_alloc_message_heap() releases msg q and status - * lock on receiver without ensuring that other locks are - * held. User is responsible to ensure that the receiver - * pointer cannot become invalid until after message has - * been passed. This is normal done either by increasing - * reference count on process (preferred) or by holding - * main or link lock over the whole message passing - * operation. - */ - -ERTS_GLB_INLINE Eterm * -erts_alloc_message_heap_state(Uint size, - ErlHeapFragment **bpp, - ErlOffHeap **ohpp, - Process *receiver, - ErtsProcLocks *receiver_locks, - erts_aint32_t *statep) -{ - Eterm *hp; - erts_aint32_t state; -#ifdef ERTS_SMP - int locked_main = 0; - state = erts_smp_atomic32_read_acqb(&receiver->state); - if (statep) - *statep = state; - if (state & (ERTS_PSFLG_EXITING - | ERTS_PSFLG_PENDING_EXIT)) - goto allocate_in_mbuf; -#endif - - if (size > (Uint) INT_MAX) - erl_exit(ERTS_ABORT_EXIT, "HUGE size (%beu)\n", size); - - if ( -#if defined(ERTS_SMP) - *receiver_locks & ERTS_PROC_LOCK_MAIN -#else - 1 -#endif - ) { -#ifdef ERTS_SMP - try_allocate_on_heap: -#endif - state = erts_smp_atomic32_read_nob(&receiver->state); - if (statep) - *statep = state; - if ((state & (ERTS_PSFLG_EXITING - | ERTS_PSFLG_PENDING_EXIT)) - || (receiver->flags & F_DISABLE_GC) - || HEAP_LIMIT(receiver) - HEAP_TOP(receiver) <= size) { - /* - * The heap is either potentially in an inconsistent - * state, or not large enough. - */ -#ifdef ERTS_SMP - if (locked_main) { - *receiver_locks &= ~ERTS_PROC_LOCK_MAIN; - erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MAIN); - } -#endif - goto allocate_in_mbuf; - } - hp = HEAP_TOP(receiver); - HEAP_TOP(receiver) = hp + size; - *bpp = NULL; - *ohpp = &MSO(receiver); - } -#ifdef ERTS_SMP - else if (erts_smp_proc_trylock(receiver, ERTS_PROC_LOCK_MAIN) == 0) { - locked_main = 1; - *receiver_locks |= ERTS_PROC_LOCK_MAIN; - goto try_allocate_on_heap; - } -#endif - else { - ErlHeapFragment *bp; - allocate_in_mbuf: - bp = new_message_buffer(size); - hp = bp->mem; - *bpp = bp; - *ohpp = &bp->off_heap; - } - - return hp; -} - -ERTS_GLB_INLINE Eterm * -erts_alloc_message_heap(Uint size, - ErlHeapFragment **bpp, - ErlOffHeap **ohpp, - Process *receiver, - ErtsProcLocks *receiver_locks) -{ - return erts_alloc_message_heap_state(size, bpp, ohpp, receiver, - receiver_locks, NULL); -} - -#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ - #define DeclareTmpHeap(VariableName,Size,Process) \ Eterm VariableName[Size] #define DeclareTypedTmpHeap(Type,VariableName,Process) \ @@ -1522,6 +1404,7 @@ dtrace_fun_decode(Process *process, erts_snprintf(mfa_buf, DTRACE_TERM_BUF_SIZE, "%T:%T/%d", module, function, arity); } + #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ #endif /* !__GLOBAL_H__ */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index fdd26fcc4b..1b0c617632 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1410,7 +1410,7 @@ queue_port_sched_op_reply(Process *rp, erts_factory_trim_and_close(factory, &msg, 1); - erts_queue_message(rp, rp_locksp, factory->heap_frags, msg, NIL); + erts_queue_message(rp, rp_locksp, factory->message, msg, NIL); } static void @@ -1418,12 +1418,9 @@ port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg) { Process *rp = erts_proc_lookup_raw(to); if (rp) { - ErlOffHeap *ohp; - ErlHeapFragment* bp; ErtsHeapFactory factory; Eterm msg_copy; Uint hsz, msg_sz; - Eterm *hp; ErtsProcLocks rp_locks = 0; hsz = ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE; @@ -1434,18 +1431,13 @@ port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg) hsz += msg_sz; } - hp = erts_alloc_message_heap(hsz, - &bp, - &ohp, - rp, - &rp_locks); - erts_factory_message_init(&factory, rp, hp, bp); - if (is_immed(msg)) - msg_copy = msg; - else { - msg_copy = copy_struct(msg, msg_sz, &hp, ohp); - factory.hp = hp; - } + (void) erts_factory_message_create(&factory, rp, + &rp_locks, hsz); + msg_copy = (is_immed(msg) + ? msg + : copy_struct(msg, msg_sz, + &factory.hp, + factory.off_heap)); queue_port_sched_op_reply(rp, &rp_locks, @@ -3050,16 +3042,17 @@ deliver_result(Eterm sender, Eterm pid, Eterm res) if (rp) { Eterm tuple; - ErlHeapFragment *bp; + ErtsMessage *mp; ErlOffHeap *ohp; Eterm* hp; Uint sz_res; sz_res = size_object(res); - hp = erts_alloc_message_heap(sz_res + 3, &bp, &ohp, rp, &rp_locks); + mp = erts_alloc_message_heap(rp, &rp_locks, + sz_res + 3, &hp, &ohp); res = copy_struct(res, sz_res, &hp, ohp); tuple = TUPLE2(hp, sender, res); - erts_queue_message(rp, &rp_locks, bp, tuple, NIL); + erts_queue_message(rp, &rp_locks, mp, tuple, NIL); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); @@ -3087,7 +3080,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to, Eterm tuple; Process* rp; Eterm* hp; - ErlHeapFragment *bp; + ErtsMessage *mp; ErlOffHeap *ohp; ErtsProcLocks rp_locks = 0; int scheduler = erts_get_scheduler_id() != 0; @@ -3113,7 +3106,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to, if (!rp) return; - hp = erts_alloc_message_heap(need, &bp, &ohp, rp, &rp_locks); + mp = erts_alloc_message_heap(rp, &rp_locks, need, &hp, &ohp); listp = NIL; if ((state & ERTS_PORT_SFLG_BINARY_IO) == 0) { @@ -3155,7 +3148,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to, tuple = TUPLE2(hp, prt->common.id, tuple); hp += 3; - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); + erts_queue_message(rp, &rp_locks, mp, tuple, am_undefined); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) @@ -3229,7 +3222,7 @@ deliver_vec_message(Port* prt, /* Port */ Eterm tuple; Process* rp; Eterm* hp; - ErlHeapFragment *bp; + ErtsMessage *mp; ErlOffHeap *ohp; ErtsProcLocks rp_locks = 0; int scheduler = erts_get_scheduler_id() != 0; @@ -3261,7 +3254,7 @@ deliver_vec_message(Port* prt, /* Port */ need += (hlen+csize)*2; } - hp = erts_alloc_message_heap(need, &bp, &ohp, rp, &rp_locks); + mp = erts_alloc_message_heap(rp, &rp_locks, need, &hp, &ohp); listp = NIL; iov += vsize; @@ -3322,7 +3315,7 @@ deliver_vec_message(Port* prt, /* Port */ tuple = TUPLE2(hp, prt->common.id, tuple); hp += 3; - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); + erts_queue_message(rp, &rp_locks, mp, tuple, am_undefined); erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) erts_proc_dec_refc(rp); @@ -3813,7 +3806,6 @@ write_port_control_result(int control_flags, ErlDrvSizeT resp_size, char *pre_alloc_buf, Eterm **hpp, - ErlHeapFragment *bp, ErlOffHeap *ohp) { Eterm res; @@ -3887,9 +3879,6 @@ port_sig_control(Port *prt, if (res == ERTS_PORT_OP_DONE) { Eterm msg; - Eterm *hp; - ErlHeapFragment *bp; - ErlOffHeap *ohp; ErtsHeapFactory factory; Process *rp; ErtsProcLocks rp_locks = 0; @@ -3909,22 +3898,15 @@ port_sig_control(Port *prt, hsz = rsz + ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE; - hp = erts_alloc_message_heap(hsz, - &bp, - &ohp, - rp, - &rp_locks); - erts_factory_message_init(&factory, rp, hp, bp); + (void) erts_factory_message_create(&factory, rp, + &rp_locks, hsz); msg = write_port_control_result(control_flags, resp_bufp, resp_size, &resp_buf[0], - &hp, - bp, - ohp); - factory.hp = hp; - + &factory.hp, + factory.off_heap); queue_port_sched_op_reply(rp, &rp_locks, &factory, @@ -4065,7 +4047,6 @@ erts_port_control(Process* c_p, resp_size, &resp_buf[0], &hp, - NULL, &c_p->off_heap); BUMP_REDS(c_p, ERTS_PORT_REDS_CONTROL); return ERTS_PORT_OP_DONE; @@ -4224,21 +4205,14 @@ port_sig_call(Port *prt, hsz = erts_decode_ext_size((byte *) resp_bufp, resp_size); if (hsz >= 0) { - ErlHeapFragment* bp; - ErlOffHeap* ohp; ErtsHeapFactory factory; byte *endp; hsz += 3; /* ok tuple */ hsz += ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE; - hp = erts_alloc_message_heap(hsz, - &bp, - &ohp, - rp, - &rp_locks); + (void) erts_factory_message_create(&factory, rp, &rp_locks, hsz); endp = (byte *) resp_bufp; - erts_factory_message_init(&factory, rp, hp, bp); msg = erts_decode_ext(&factory, &endp); if (is_value(msg)) { hp = erts_produce_heap(&factory, @@ -4499,7 +4473,9 @@ port_sig_info(Port *prt, sigdp->u.info.item); if (is_value(value)) { ErtsHeapFactory factory; - erts_factory_message_init(&factory, NULL, hp, bp); + ErtsMessage *mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = bp; + erts_factory_selfcontained_message_init(&factory, mp, hp); queue_port_sched_op_reply(rp, &rp_locks, &factory, @@ -4587,8 +4563,8 @@ reply_io_bytes(void *vreq) rp = erts_proc_lookup(req->pid); if (rp) { - ErlOffHeap *ohp = NULL; - ErlHeapFragment *bp = NULL; + ErlOffHeap *ohp; + ErtsMessage *mp; ErtsProcLocks rp_locks; Eterm ref, msg, ein, eout, *hp; Uint64 in, out; @@ -4610,7 +4586,7 @@ reply_io_bytes(void *vreq) erts_bld_uint64(NULL, &hsz, in); erts_bld_uint64(NULL, &hsz, out); - hp = erts_alloc_message_heap(hsz, &bp, &ohp, rp, &rp_locks); + mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp); ref = make_internal_ref(hp); write_ref_thing(hp, req->refn[0], req->refn[1], req->refn[2]); @@ -4620,7 +4596,7 @@ reply_io_bytes(void *vreq) eout = erts_bld_uint64(&hp, NULL, out); msg = TUPLE4(hp, ref, make_small(sched_id), ein, eout); - erts_queue_message(rp, &rp_locks, bp, msg, NIL); + erts_queue_message(rp, &rp_locks, mp, msg, NIL); if (req->sched_id == sched_id) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -5065,11 +5041,11 @@ ErlDrvTermData driver_mk_term_nil(void) void driver_report_exit(ErlDrvPort ix, int status) { Eterm* hp; + ErlOffHeap *ohp; Eterm tuple; Process *rp; Eterm pid; - ErlHeapFragment *bp = NULL; - ErlOffHeap *ohp; + ErtsMessage *mp; ErtsProcLocks rp_locks = 0; int scheduler = erts_get_scheduler_id() != 0; Port* prt = erts_drvport2port(ix); @@ -5089,13 +5065,13 @@ void driver_report_exit(ErlDrvPort ix, int status) if (!rp) return; - hp = erts_alloc_message_heap(3+3, &bp, &ohp, rp, &rp_locks); + mp = erts_alloc_message_heap(rp, &rp_locks, 3+3, &hp, &ohp); tuple = TUPLE2(hp, am_exit_status, make_small(status)); hp += 3; tuple = TUPLE2(hp, prt->common.id, tuple); - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); + erts_queue_message(rp, &rp_locks, mp, tuple, am_undefined); erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) @@ -5205,7 +5181,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) ErtsProcLocks rp_locks = 0; struct b2t_states__ b2t; int scheduler; - int is_heap_need_limited = 1; ErtsSchedulerData *esdp = erts_get_scheduler_data(); ERTS_UNDEF(mess,NIL); @@ -5374,9 +5349,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) need += hsz; ptr += 2; depth++; - if (size > MAP_SMALL_MAP_LIMIT*3) { /* may contain big map */ - is_heap_need_limited = 0; - } break; } case ERL_DRV_MAP: { /* int */ @@ -5384,7 +5356,6 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) if ((int) ptr[0] < 0) ERTS_DDT_FAIL; if (ptr[0] > MAP_SMALL_MAP_LIMIT) { need += HASHMAP_ESTIMATED_HEAP_SIZE(ptr[0]); - is_heap_need_limited = 0; } else { need += MAP_HEADER_FLATMAP_SZ + 1 + 2*ptr[0]; } @@ -5423,17 +5394,7 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) goto done; } - /* Try copy directly to destination heap if we know there are no big maps */ - if (is_heap_need_limited) { - ErlOffHeap *ohp; - ErlHeapFragment* bp; - Eterm* hp = erts_alloc_message_heap(need, &bp, &ohp, rp, &rp_locks); - erts_factory_message_init(&factory, rp, hp, bp); - } - else { - erts_factory_message_init(&factory, NULL, NULL, - new_message_buffer(need)); - } + (void) erts_factory_message_create(&factory, rp, &rp_locks, need); /* * Interpret the instructions and build the term. @@ -5702,9 +5663,9 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) if (res > 0) { mess = ESTACK_POP(stack); /* get resulting value */ - erts_factory_close(&factory); + erts_factory_trim_and_close(&factory, &mess, 1); /* send message */ - erts_queue_message(rp, &rp_locks, factory.heap_frags, mess, am_undefined); + erts_queue_message(rp, &rp_locks, factory.message, mess, am_undefined); } else { if (b2t.ix > b2t.used) diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 5db7ee6d7c..90e16ca14f 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -70,8 +70,10 @@ #endif #if ERTS_CAN_INLINE +#define ERTS_GLB_FORCE_INLINE static ERTS_FORCE_INLINE #define ERTS_GLB_INLINE static ERTS_INLINE #else +#define ERTS_GLB_FORCE_INLINE #define ERTS_GLB_INLINE #endif diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index a741e2e2e6..e03113b8cc 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -110,7 +110,6 @@ Eterm* erts_heap_alloc(Process* p, Uint need, Uint xtra) { ErlHeapFragment* bp; - Eterm* htop; Uint n; #if defined(DEBUG) || defined(CHECK_FOR_HOLES) Uint i; @@ -156,16 +155,6 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra) n--; #endif - /* - * When we have created a heap fragment, we are no longer allowed - * to store anything more on the heap. - */ - htop = HEAP_TOP(p); - if (htop < HEAP_LIMIT(p)) { - *htop = make_pos_bignum_header(HEAP_LIMIT(p)-htop-1); - HEAP_TOP(p) = HEAP_LIMIT(p); - } - bp->next = MBUF(p); MBUF(p) = bp; bp->alloc_size = n; @@ -2285,7 +2274,11 @@ static void do_send_logger_message(Eterm *hp, ErlOffHeap *ohp, ErlHeapFragment * erts_queue_error_logger_message(from, message, bp); } #else - erts_queue_message(p, NULL /* only used for smp build */, bp, message, NIL); + { + ErtsMessage *mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = bp; + erts_queue_message(p, NULL /* only used for smp build */, mp, message, NIL); + } #endif } diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c index 2c747771ac..2e19bf88bf 100644 --- a/erts/emulator/hipe/hipe_gc.c +++ b/erts/emulator/hipe/hipe_gc.c @@ -46,10 +46,6 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop) /* arch-specific nstack walk state */ struct nstack_walk_state walk_state; - /* fullsweep-specific state */ - char *src, *oh; - Uint src_size, oh_size; - if (!p->hipe.nstack) { ASSERT(!p->hipe.nsp && !p->hipe.nstend); return n_htop; @@ -66,11 +62,6 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop) sdesc = nstack_walk_init_sdesc(p, &walk_state); - src = (char*)HEAP_START(p); - src_size = (char*)HEAP_TOP(p) - src; - oh = (char*)OLD_HEAP(p); - oh_size = (char*)OLD_HTOP(p) - oh; - for (;;) { if (nstack_walk_nsp_reached_end(nsp, nsp_end)) { if (nsp == nsp_end) { @@ -97,8 +88,7 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop) if (IS_MOVED_BOXED(val)) { ASSERT(is_boxed(val)); *nsp_i = val; - } else if (in_area(ptr, src, src_size) || - in_area(ptr, oh, oh_size)) { + } else if (!erts_is_literal(gval, ptr)) { MOVE_BOXED(ptr, val, n_htop, nsp_i); } } else if (is_list(gval)) { @@ -106,8 +96,7 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop) Eterm val = *ptr; if (IS_MOVED_CONS(val)) { *nsp_i = ptr[1]; - } else if (in_area(ptr, src, src_size) || - in_area(ptr, oh, oh_size)) { + } else if (!erts_is_literal(gval, ptr)) { ASSERT(within(ptr, p)); MOVE_CONS(ptr, val, n_htop, nsp_i); } @@ -139,11 +128,13 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) unsigned int mask; /* arch-specific nstack walk state */ struct nstack_walk_state walk_state; + char *oh; + Uint oh_size; /* gensweep-specific state */ Eterm *old_htop, *n_htop; - char *heap; - Uint heap_size, mature_size; + char *mature; + Uint mature_size; if (!p->hipe.nstack) { ASSERT(!p->hipe.nsp && !p->hipe.nstend); @@ -168,9 +159,10 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) old_htop = *ptr_old_htop; n_htop = *ptr_n_htop; - heap = (char*)HEAP_START(p); - heap_size = (char*)HEAP_TOP(p) - heap; - mature_size = (char*)HIGH_WATER(p) - heap; + mature = (char *) (p->abandoned_heap ? p->abandoned_heap : p->heap); + mature_size = (char*)HIGH_WATER(p) - mature; + oh = (char*)OLD_HEAP(p); + oh_size = (char*)OLD_HTOP(p) - oh; for (;;) { if (nstack_walk_nsp_reached_end(nsp, nsp_end)) { @@ -209,9 +201,9 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) if (IS_MOVED_BOXED(val)) { ASSERT(is_boxed(val)); *nsp_i = val; - } else if (in_area(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, mature, mature_size)) { MOVE_BOXED(ptr, val, old_htop, nsp_i); - } else if (in_area(ptr, heap, heap_size)) { + } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) { ASSERT(within(ptr, p)); MOVE_BOXED(ptr, val, n_htop, nsp_i); } @@ -220,9 +212,9 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) Eterm val = *ptr; if (IS_MOVED_CONS(val)) { *nsp_i = ptr[1]; - } else if (in_area(ptr, heap, mature_size)) { + } else if (ErtsInArea(ptr, mature, mature_size)) { MOVE_CONS(ptr, val, old_htop, nsp_i); - } else if (in_area(ptr, heap, heap_size)) { + } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) { ASSERT(within(ptr, p)); MOVE_CONS(ptr, val, n_htop, nsp_i); } diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index 98bda43f0e..ceae3497c5 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -160,13 +160,22 @@ BIF_RETTYPE hipe_set_timeout(BIF_ALIST_1) */ void hipe_select_msg(Process *p) { - ErlMessage *msgp; + ErtsMessage *msgp; msgp = PEEK_MESSAGE(p); UNLINK_MESSAGE(p, msgp); /* decrements global 'erts_proc_tot_mem' variable */ JOIN_MESSAGE(p); CANCEL_TIMER(p); /* calls erts_cancel_proc_timer() */ - free_message(msgp); + erts_save_message_in_proc(p, msgp); + p->flags &= ~F_DISABLE_GC; + if (ERTS_IS_GC_DESIRED(p)) { + /* + * We want to GC soon but we leave a few + * reductions giving the message some time + * to turn into garbage. + */ + ERTS_VBUMP_LEAVE_REDS(p, 5); + } } void hipe_fclearerror_error(Process *p) @@ -511,8 +520,9 @@ int hipe_bs_validate_unicode_retract(ErlBinMatchBuffer* mb, Eterm arg) */ Eterm hipe_check_get_msg(Process *c_p) { - Eterm ret; - ErlMessage *msgp; + ErtsMessage *msgp; + + c_p->flags |= F_DISABLE_GC; next_message: @@ -534,25 +544,29 @@ Eterm hipe_check_get_msg(Process *c_p) /* XXX: BEAM doesn't need this */ c_p->hipe_smp.have_receive_locks = 1; #endif + c_p->flags &= ~F_DISABLE_GC; return THE_NON_VALUE; #ifdef ERTS_SMP } #endif } - ErtsMoveMsgAttachmentIntoProc(msgp, c_p, c_p->stop, HEAP_TOP(c_p), - c_p->fcalls, (void) 0, (void) 0); - ret = ERL_MESSAGE_TERM(msgp); - if (is_non_value(ret)) { + + if (is_non_value(ERL_MESSAGE_TERM(msgp)) + && !erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) { /* * A corrupt distribution message that we weren't able to decode; * remove it... */ ASSERT(!msgp->data.attached); UNLINK_MESSAGE(c_p, msgp); - free_message(msgp); + msgp->next = NULL; + erts_cleanup_messages(msgp); goto next_message; } - return ret; + + ASSERT(is_value(ERL_MESSAGE_TERM(msgp))); + + return ERL_MESSAGE_TERM(msgp); } /* diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index cde0b25a2a..461957be10 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -155,6 +155,12 @@ static char *plusr_val_switches[] = { NULL }; +/* +x arguments with values */ +static char *plusx_val_switches[] = { + "ohmq", + NULL +}; + /* +z arguments with values */ static char *plusz_val_switches[] = { "dbbl", @@ -975,6 +981,20 @@ int main(int argc, char **argv) add_Eargs(argv[i+1]); i++; break; + case 'x': + if (!is_one_of_strings(&argv[i][2], plusx_val_switches)) { + goto the_default; + } else { + if (i+1 >= argc + || argv[i+1][0] == '-' + || argv[i+1][0] == '+') + usage(argv[i]); + argv[i][0] = '-'; + add_Eargs(argv[i]); + add_Eargs(argv[i+1]); + i++; + } + break; case 'z': if (!is_one_of_strings(&argv[i][2], plusz_val_switches)) { goto the_default; @@ -1175,7 +1195,7 @@ usage_aux(void) "[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] " "[+SP PERCENTAGE_SCHEDULERS:PERCENTAGE_SCHEDULERS_ONLINE] " "[+T LEVEL] [+V] [+v] " - "[+W] [+z MISC_OPTION] [args ...]\n"); + "[+W] [+x DEFAULT_PROC_FLAGS] [+z MISC_OPTION] [args ...]\n"); exit(1); } diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 863a5e61ef..641fac2d26 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 291356c7b1..e46d64eb0a 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2044,6 +2044,9 @@ open_port(_PortName,_PortSettings) -> (min_bin_vheap_size, MinBinVHeapSize) -> OldMinBinVHeapSize when MinBinVHeapSize :: non_neg_integer(), OldMinBinVHeapSize :: non_neg_integer(); + (off_heap_message_queue, OHMQ) -> OldOHMQ when + OHMQ :: boolean(), + OldOHMQ :: boolean(); (priority, Level) -> OldLevel when Level :: priority_level(), OldLevel :: priority_level(); @@ -2082,6 +2085,7 @@ process_flag(_Flag, _Value) -> min_bin_vheap_size | monitored_by | monitors | + off_heap_message_queue | priority | reductions | registered_name | @@ -2123,6 +2127,7 @@ process_flag(_Flag, _Value) -> {monitors, Monitors :: [{process, Pid :: pid() | {RegName :: atom(), Node :: node()}}]} | + {off_heap_message_queue, OHMQ :: boolean()} | {priority, Level :: priority_level()} | {reductions, Number :: non_neg_integer()} | {registered_name, Atom :: atom()} | @@ -2425,6 +2430,7 @@ tuple_to_list(_Tuple) -> (multi_scheduling) -> disabled | blocked | enabled; (multi_scheduling_blockers) -> [PID :: pid()]; (nif_version) -> string(); + (off_heap_message_queue) -> boolean(); (otp_release) -> string(); (os_monotonic_time_source) -> [{atom(),term()}]; (os_system_time_source) -> [{atom(),term()}]; @@ -2552,14 +2558,19 @@ spawn_monitor(M, F, A) when erlang:is_atom(M), spawn_monitor(M, F, A) -> erlang:error(badarg, [M,F,A]). + +-type spawn_opt_option() :: + link + | monitor + | {priority, Level :: priority_level()} + | {fullsweep_after, Number :: non_neg_integer()} + | {min_heap_size, Size :: non_neg_integer()} + | {min_bin_vheap_size, VSize :: non_neg_integer()} + | {off_heap_message_queue, OHMQ :: boolean()}. + -spec spawn_opt(Fun, Options) -> pid() | {pid(), reference()} when Fun :: function(), - Options :: [Option], - Option :: link | monitor - | {priority, Level :: priority_level()} - | {fullsweep_after, Number :: non_neg_integer()} - | {min_heap_size, Size :: non_neg_integer()} - | {min_bin_vheap_size, VSize :: non_neg_integer()}. + Options :: [spawn_opt_option()]. spawn_opt(F, O) when erlang:is_function(F) -> spawn_opt(erlang, apply, [F, []], O); spawn_opt({M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) -> @@ -2572,12 +2583,7 @@ spawn_opt(F, O) -> -spec spawn_opt(Node, Fun, Options) -> pid() | {pid(), reference()} when Node :: node(), Fun :: function(), - Options :: [Option], - Option :: link | monitor - | {priority, Level :: priority_level()} - | {fullsweep_after, Number :: non_neg_integer()} - | {min_heap_size, Size :: non_neg_integer()} - | {min_bin_vheap_size, VSize :: non_neg_integer()}. + Options :: [spawn_opt_option()]. spawn_opt(N, F, O) when N =:= erlang:node() -> spawn_opt(F, O); spawn_opt(N, F, O) when erlang:is_function(F) -> @@ -2664,12 +2670,7 @@ spawn_link(N,M,F,A) -> Module :: module(), Function :: atom(), Args :: [term()], - Options :: [Option], - Option :: link | monitor - | {priority, Level :: priority_level()} - | {fullsweep_after, Number :: non_neg_integer()} - | {min_heap_size, Size :: non_neg_integer()} - | {min_bin_vheap_size, VSize :: non_neg_integer()}. + Options :: [spawn_opt_option()]. spawn_opt(M, F, A, Opts) -> case catch erlang:spawn_opt({M,F,A,Opts}) of {'EXIT',{Reason,_}} -> @@ -2684,12 +2685,7 @@ spawn_opt(M, F, A, Opts) -> Module :: module(), Function :: atom(), Args :: [term()], - Options :: [Option], - Option :: link | monitor - | {priority, Level :: priority_level()} - | {fullsweep_after, Number :: non_neg_integer()} - | {min_heap_size, Size :: non_neg_integer()} - | {min_bin_vheap_size, VSize :: non_neg_integer()}. + Options :: [spawn_opt_option()]. spawn_opt(N, M, F, A, O) when N =:= erlang:node(), erlang:is_atom(M), erlang:is_atom(F), erlang:is_list(A), erlang:is_list(O) -> -- cgit v1.2.3 From b56f5a163555181dceb79cbfd0d69d3cb5015e9c Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 Nov 2015 12:06:30 +0100 Subject: Use the same conditions when triggering GC after BIF --- erts/emulator/beam/beam_emu.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 1dd56ff989..9521997987 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3533,14 +3533,7 @@ do { \ apply_bif_or_nif_epilogue: ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); ERTS_HOLE_CHECK(c_p); - /* - * We want to test with ERTS_IS_GC_DESIRED(c_p) in order - * to trigger gc due to binaries based on same conditions - * regardless of how the bif is called. This change will - * however be introduced in a separate commit in order to - * easier identify why the characteristics changed. - */ - if (c_p->stop - c_p->htop < c_p->mbuf_sz) { + if (ERTS_IS_GC_DESIRED(c_p)) { nif_bif_result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, nif_bif_result, reg, bif_nif_arity); -- cgit v1.2.3 From 9c6f45b901ee701553afe34c0b33b7d931d73fd9 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 11 Nov 2015 11:39:32 +0100 Subject: Bump reductions on GC --- erts/emulator/beam/beam_bif_load.c | 2 +- erts/emulator/beam/beam_emu.c | 108 ++++++++++++++++------------- erts/emulator/beam/bif.c | 6 +- erts/emulator/beam/erl_gc.c | 126 ++++++++++++++++++++++------------ erts/emulator/beam/erl_gc.h | 3 +- erts/emulator/beam/erl_process.c | 10 +-- erts/emulator/beam/erl_process_dict.c | 6 +- erts/emulator/hipe/hipe_mode_switch.c | 2 +- erts/emulator/hipe/hipe_mode_switch.h | 2 +- erts/emulator/hipe/hipe_native_bif.c | 2 +- 10 files changed, 159 insertions(+), 108 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 22b4e26c77..c3ebf71a01 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -935,7 +935,7 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) if (need_gc & ERTS_ORDINARY_GC__) { FLAGS(rp) |= F_NEED_FULLSWEEP; - *redsp += erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); + *redsp += erts_garbage_collect_nobump(rp, 0, rp->arg_reg, rp->arity); done_gc |= ERTS_ORDINARY_GC__; } if (need_gc & ERTS_LITERAL_GC__) { diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 9521997987..f74f182863 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -237,6 +237,14 @@ void** beam_ops; HEAP_TOP(c_p) = HTOP; \ c_p->stop = E +#define HEAVY_SWAPIN \ + SWAPIN; \ + FCALLS = c_p->fcalls + +#define HEAVY_SWAPOUT \ + SWAPOUT; \ + c_p->fcalls = FCALLS + /* * Use LIGHT_SWAPOUT when the called function * will call HeapOnlyAlloc() (and never HAlloc()). @@ -297,7 +305,7 @@ void** beam_ops; if (E - HTOP < (needed + (HeapNeed))) { \ SWAPOUT; \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - FCALLS -= erts_garbage_collect(c_p, needed + (HeapNeed), reg, (M)); \ + FCALLS -= erts_garbage_collect_nobump(c_p, needed + (HeapNeed), reg, (M)); \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ SWAPIN; \ @@ -349,7 +357,7 @@ void** beam_ops; if ((E - HTOP < need) || (MSO(c_p).overhead + (VNh) >= BIN_VHEAP_SZ(c_p))) {\ SWAPOUT; \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)); \ + FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live)); \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ SWAPIN; \ @@ -370,7 +378,7 @@ void** beam_ops; if (E - HTOP < need) { \ SWAPOUT; \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)); \ + FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live));\ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ SWAPIN; \ @@ -391,7 +399,7 @@ void** beam_ops; SWAPOUT; \ reg[Live] = Extra; \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)+1); \ + FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live)+1); \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ Extra = reg[Live]; \ @@ -415,9 +423,9 @@ void** beam_ops; #define MakeFun(FunP, NumFree) \ do { \ - SWAPOUT; \ + HEAVY_SWAPOUT; \ r(0) = new_fun(c_p, reg, (ErlFunEntry *) FunP, NumFree); \ - SWAPIN; \ + HEAVY_SWAPIN; \ } while (0) #define PutTuple(Dst, Arity) \ @@ -1402,11 +1410,11 @@ void process_main(void) } live = Arg(2); - SWAPOUT; + HEAVY_SWAPOUT; reg[live] = increment_reg_val; reg[live+1] = make_small(increment_val); result = erts_gc_mixed_plus(c_p, reg, live); - SWAPIN; + HEAVY_SWAPIN; ERTS_HOLE_CHECK(c_p); if (is_value(result)) { StoreBifResult(3, result); @@ -1420,11 +1428,11 @@ void process_main(void) Eterm result; \ Uint live = Arg(1); \ \ - SWAPOUT; \ + HEAVY_SWAPOUT; \ reg[live] = Op1; \ reg[live+1] = Op2; \ result = erts_gc_##name(c_p, reg, live); \ - SWAPIN; \ + HEAVY_SWAPIN; \ ERTS_HOLE_CHECK(c_p); \ if (is_value(result)) { \ StoreBifResult(4, result); \ @@ -1744,7 +1752,7 @@ void process_main(void) if (E - HTOP < 3) { SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); - FCALLS -= erts_garbage_collect(c_p, 3, reg+2, 1); + FCALLS -= erts_garbage_collect_nobump(c_p, 3, reg+2, 1); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); SWAPIN; @@ -2382,9 +2390,9 @@ void process_main(void) OpCase(new_map_dII): { Eterm res; - SWAPOUT; + HEAVY_SWAPOUT; res = new_map(c_p, reg, I-1); - SWAPIN; + HEAVY_SWAPIN; StoreResult(res, Arg(0)); Next(3+Arg(2)); } @@ -2472,9 +2480,9 @@ do { \ Eterm map; GetArg1(1, map); - SWAPOUT; + HEAVY_SWAPOUT; res = update_map_assoc(c_p, reg, map, I); - SWAPIN; + HEAVY_SWAPIN; if (is_value(res)) { StoreResult(res, Arg(2)); Next(5+Arg(4)); @@ -2494,9 +2502,9 @@ do { \ Eterm map; GetArg1(1, map); - SWAPOUT; + HEAVY_SWAPOUT; res = update_map_exact(c_p, reg, map, I); - SWAPIN; + HEAVY_SWAPIN; if (is_value(res)) { StoreResult(res, Arg(2)); Next(5+Arg(4)); @@ -3072,10 +3080,10 @@ do { \ bnot_val = make_small(~signed_val(bnot_val)); } else { Uint live = Arg(2); - SWAPOUT; + HEAVY_SWAPOUT; reg[live] = bnot_val; bnot_val = erts_gc_bnot(c_p, reg, live); - SWAPIN; + HEAVY_SWAPIN; ERTS_HOLE_CHECK(c_p); if (is_nil(bnot_val)) { goto lb_Cl_error; @@ -3090,9 +3098,9 @@ do { \ OpCase(i_apply): { BeamInstr *next; - SWAPOUT; + HEAVY_SWAPOUT; next = apply(c_p, r(0), x(1), x(2), reg); - SWAPIN; + HEAVY_SWAPIN; if (next != NULL) { SET_CP(c_p, I+1); SET_I(next); @@ -3104,9 +3112,9 @@ do { \ OpCase(i_apply_last_P): { BeamInstr *next; - SWAPOUT; + HEAVY_SWAPOUT; next = apply(c_p, r(0), x(1), x(2), reg); - SWAPIN; + HEAVY_SWAPIN; if (next != NULL) { SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(0)); @@ -3119,9 +3127,9 @@ do { \ OpCase(i_apply_only): { BeamInstr *next; - SWAPOUT; + HEAVY_SWAPOUT; next = apply(c_p, r(0), x(1), x(2), reg); - SWAPIN; + HEAVY_SWAPIN; if (next != NULL) { SET_I(next); Dispatch(); @@ -3133,9 +3141,9 @@ do { \ OpCase(apply_I): { BeamInstr *next; - SWAPOUT; + HEAVY_SWAPOUT; next = fixed_apply(c_p, reg, Arg(0)); - SWAPIN; + HEAVY_SWAPIN; if (next != NULL) { SET_CP(c_p, I+2); SET_I(next); @@ -3148,9 +3156,9 @@ do { \ OpCase(apply_last_IP): { BeamInstr *next; - SWAPOUT; + HEAVY_SWAPOUT; next = fixed_apply(c_p, reg, Arg(0)); - SWAPIN; + HEAVY_SWAPIN; if (next != NULL) { SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(1)); @@ -3164,9 +3172,9 @@ do { \ OpCase(i_apply_fun): { BeamInstr *next; - SWAPOUT; + HEAVY_SWAPOUT; next = apply_fun(c_p, r(0), x(1), reg); - SWAPIN; + HEAVY_SWAPIN; if (next != NULL) { SET_CP(c_p, I+1); SET_I(next); @@ -3178,9 +3186,9 @@ do { \ OpCase(i_apply_fun_last_P): { BeamInstr *next; - SWAPOUT; + HEAVY_SWAPOUT; next = apply_fun(c_p, r(0), x(1), reg); - SWAPIN; + HEAVY_SWAPIN; if (next != NULL) { SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(0)); @@ -3193,9 +3201,9 @@ do { \ OpCase(i_apply_fun_only): { BeamInstr *next; - SWAPOUT; + HEAVY_SWAPOUT; next = apply_fun(c_p, r(0), x(1), reg); - SWAPIN; + HEAVY_SWAPIN; if (next != NULL) { SET_I(next); Dispatchfun(); @@ -3206,9 +3214,9 @@ do { \ OpCase(i_call_fun_I): { BeamInstr *next; - SWAPOUT; + HEAVY_SWAPOUT; next = call_fun(c_p, Arg(0), reg, THE_NON_VALUE); - SWAPIN; + HEAVY_SWAPIN; if (next != NULL) { SET_CP(c_p, I+2); SET_I(next); @@ -3220,9 +3228,9 @@ do { \ OpCase(i_call_fun_last_IP): { BeamInstr *next; - SWAPOUT; + HEAVY_SWAPOUT; next = call_fun(c_p, Arg(0), reg, THE_NON_VALUE); - SWAPIN; + HEAVY_SWAPIN; if (next != NULL) { SET_CP(c_p, (BeamInstr *) E[0]); E = ADD_BYTE_OFFSET(E, Arg(1)); @@ -3421,9 +3429,9 @@ do { \ * code[3]: &&call_error_handler * code[4]: Not used */ - SWAPOUT; + HEAVY_SWAPOUT; I = call_error_handler(c_p, I-3, reg, am_undefined_function); - SWAPIN; + HEAVY_SWAPIN; if (I) { Goto(*I); } @@ -3977,10 +3985,10 @@ do { \ Eterm Size; GetArg1(4, Size); - SWAPOUT; + HEAVY_SWAPOUT; reg[live] = x(SCRATCH_X_REG); res = erts_bs_append(c_p, reg, live, Size, Arg(1), Arg(3)); - SWAPIN; + HEAVY_SWAPIN; if (is_non_value(res)) { /* c_p->freason is already set (may be either BADARG or SYSTEM_LIMIT). */ goto lb_Cl_error; @@ -4005,9 +4013,9 @@ do { \ } OpCase(bs_init_writable): { - SWAPOUT; + HEAVY_SWAPOUT; r(0) = erts_bs_init_writable(c_p, r(0)); - SWAPIN; + HEAVY_SWAPIN; Next(0); } @@ -4835,7 +4843,7 @@ do { \ BeamInstr *next; next = call_fun(c_p, c_p->arity - 1, reg, THE_NON_VALUE); - SWAPIN; + HEAVY_SWAPIN; if (next != NULL) { SET_I(next); Dispatchfun(); @@ -4884,20 +4892,22 @@ do { \ } OpCase(i_hibernate): { - SWAPOUT; + HEAVY_SWAPOUT; if (erts_hibernate(c_p, r(0), x(1), x(2), reg)) { + FCALLS = c_p->fcalls; c_p->flags &= ~F_HIBERNATE_SCHED; goto do_schedule; } else { + HEAVY_SWAPIN; I = handle_error(c_p, I, reg, hibernate_3); goto post_error_handling; } } OpCase(i_debug_breakpoint): { - SWAPOUT; + HEAVY_SWAPOUT; I = call_error_handler(c_p, I-3, reg, am_breakpoint); - SWAPIN; + HEAVY_SWAPIN; if (I) { Goto(*I); } diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index e4283ac945..57dd045193 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3839,11 +3839,9 @@ BIF_RETTYPE now_0(BIF_ALIST_0) BIF_RETTYPE garbage_collect_0(BIF_ALIST_0) { - int reds; - FLAGS(BIF_P) |= F_NEED_FULLSWEEP; - reds = erts_garbage_collect(BIF_P, 0, NULL, 0); - BIF_RET2(am_true, reds); + erts_garbage_collect(BIF_P, 0, NULL, 0); + BIF_RET(am_true); } /**********************************************************************/ diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 6a52e1a890..7163b4839d 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -135,7 +135,7 @@ static Eterm* sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm char* src, Uint src_size); static Eterm* collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end, Eterm* heap, Eterm* htop, Eterm* objv, int nobj); -static void adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj); +static int adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj); static void shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj); static void grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj); static void sweep_off_heap(Process *p, int fullsweep); @@ -172,6 +172,20 @@ typedef struct { erts_smp_atomic32_t refc; } ErtsGCInfoReq; +static ERTS_INLINE int +gc_cost(Uint gc_moved_live_words, Uint resize_moved_words) +{ + Sint reds; + + reds = gc_moved_live_words/10; + reds += resize_moved_words/100; + if (reds < 1) + return 1; + if (reds > INT_MAX) + return INT_MAX; + return (int) reds; +} + ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(gcireq, ErtsGCInfoReq, 5, @@ -413,6 +427,7 @@ static ERTS_INLINE void reset_active_writer(Process *p) } #define ERTS_DELAY_GC_EXTRA_FREE 40 +#define ERTS_ABANDON_HEAP_COST 10 static int delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need) @@ -421,6 +436,7 @@ delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need) Eterm *orig_heap, *orig_hend, *orig_htop, *orig_stop; Eterm *stop, *hend; Uint hsz, ssz; + int reds_left; ERTS_HOLE_CHECK(p); @@ -482,7 +498,12 @@ delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need) /* Make sure that we do a proper GC as soon as possible... */ p->flags |= F_FORCE_GC; - return CONTEXT_REDS; + reds_left = ERTS_BIF_REDS_LEFT(p); + if (reds_left > ERTS_ABANDON_HEAP_COST) { + int vreds = reds_left - ERTS_ABANDON_HEAP_COST; + ERTS_VBUMP_REDS(p, vreds); + } + return ERTS_ABANDON_HEAP_COST; } static ERTS_FORCE_INLINE Uint @@ -536,7 +557,7 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, int need, Eterm* objv, int nobj) { Uint reclaimed_now = 0; - int done = 0; + int reds; ErtsMonotonicTime start_time = 0; /* Shut up faulty warning... */ ErtsSchedulerData *esdp; #ifdef USE_VM_PROBES @@ -562,9 +583,7 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, ERTS_CHK_OFFHEAP(p); ErtsGcQuickSanityCheck(p); - if (GEN_GCS(p) >= MAX_GEN_GCS(p)) { - FLAGS(p) |= F_NEED_FULLSWEEP; - } + #ifdef USE_VM_PROBES *pidbuf = '\0'; if (DTRACE_ENABLED(gc_major_start) @@ -577,17 +596,21 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, /* * Test which type of GC to do. */ - while (!done) { - if ((FLAGS(p) & F_NEED_FULLSWEEP) != 0) { - DTRACE2(gc_major_start, pidbuf, need); - done = major_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); - DTRACE2(gc_major_end, pidbuf, reclaimed_now); - } else { - DTRACE2(gc_minor_start, pidbuf, need); - done = minor_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); - DTRACE2(gc_minor_end, pidbuf, reclaimed_now); - } + + if (GEN_GCS(p) < MAX_GEN_GCS(p) && !(FLAGS(p) & F_NEED_FULLSWEEP)) { + DTRACE2(gc_minor_start, pidbuf, need); + reds = minor_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); + DTRACE2(gc_minor_end, pidbuf, reclaimed_now); + if (reds < 0) + goto do_major_collection; + } + else { + do_major_collection: + DTRACE2(gc_major_start, pidbuf, need); + reds = major_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); + DTRACE2(gc_major_end, pidbuf, reclaimed_now); } + reset_active_writer(p); /* @@ -648,23 +671,22 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, p->last_old_htop = p->old_htop; #endif - /* FIXME: This function should really return an Sint, i.e., a possibly - 64 bit wide signed integer, but that requires updating all the code - that calls it. For now, we just return INT_MAX if the result is too - large for an int. */ - { - Sint result = (HEAP_TOP(p) - HEAP_START(p)) / 10; - if (result >= INT_MAX) return INT_MAX; - else return (int) result; - } + return reds; } int -erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) +erts_garbage_collect_nobump(Process* p, int need, Eterm* objv, int nobj) { return garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj); } +void +erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) +{ + int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj); + BUMP_REDS(p, reds); +} + /* * Place all living data on a the new heap; deallocate any old heap. * Meant to be used by hibernate/3. @@ -679,6 +701,7 @@ erts_garbage_collect_hibernate(Process* p) char* area; Uint area_size; Sint offs; + int reds; if (p->flags & F_DISABLE_GC) ERTS_INTERNAL_ERROR("GC disabled"); @@ -774,6 +797,9 @@ erts_garbage_collect_hibernate(Process* p) ErtsGcQuickSanityCheck(p); erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC); + + reds = gc_cost(actual_size, actual_size); + BUMP_REDS(p, reds); } @@ -989,16 +1015,21 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end, ((mature_size <= OLD_HEND(p) - OLD_HTOP(p)) && ((BIN_VHEAP_MATURE(p) < ( BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p)))) && ((BIN_OLD_VHEAP_SZ(p) > BIN_OLD_VHEAP(p))) ) ) { - Uint stack_size, size_after, need_after, new_sz; + Eterm *prev_old_htop; + Uint stack_size, size_after, adjust_size, need_after, new_sz, new_mature; stack_size = p->hend - p->stop; new_sz = stack_size + size_before; new_sz = next_heap_size(p, new_sz, 0); + prev_old_htop = p->old_htop; do_minor(p, live_hf_end, (char *) mature, mature_size*sizeof(Eterm), new_sz, objv, nobj); - size_after = HEAP_TOP(p) - HEAP_START(p); + new_mature = p->old_htop - prev_old_htop; + + size_after = new_mature; + size_after += HEAP_TOP(p) - HEAP_START(p); *recl += (size_before - size_after); ErtsGcQuickSanityCheck(p); @@ -1017,6 +1048,8 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end, * the heap size is substantial, we don't want to shrink. */ + adjust_size = 0; + if ((HEAP_SIZE(p) > 3000) && (4 * need_after < HEAP_SIZE(p)) && ((HEAP_SIZE(p) > 8000) || (HEAP_SIZE(p) > (OLD_HEND(p) - OLD_HEAP(p))))) { @@ -1038,31 +1071,33 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end, : next_heap_size(p, wanted, 0); if (wanted < HEAP_SIZE(p)) { shrink_new_heap(p, wanted, objv, nobj); + adjust_size = p->htop - p->heap; } - ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0)); - ASSERT(MBUF(p) == NULL); - return 1; /* We are done. */ + goto done; } if (HEAP_SIZE(p) >= need_after) { /* * The heap size turned out to be just right. We are done. */ - ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0)); - ASSERT(MBUF(p) == NULL); - return 1; + goto done; } grow_new_heap(p, next_heap_size(p, need_after, 0), objv, nobj); - return 1; + adjust_size = p->htop - p->heap; + + done: + ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0)); + ASSERT(MBUF(p) == NULL); + + return gc_cost(size_after, adjust_size); } /* * Not enough room for a minor collection. Must force a major collection. */ - FLAGS(p) |= F_NEED_FULLSWEEP; - return 0; + return -1; } /* @@ -1341,12 +1376,13 @@ static int major_collection(Process* p, ErlHeapFragment *live_hf_end, int need, Eterm* objv, int nobj, Uint *recl) { - Uint size_before, stack_size; + Uint size_before, size_after, stack_size; Eterm* n_heap; Eterm* n_htop; char* oh = (char *) OLD_HEAP(p); Uint oh_size = (char *) OLD_HTOP(p) - oh; Uint new_sz, stk_sz; + int adjusted; /* * Do a fullsweep GC. First figure out the size of the heap @@ -1413,9 +1449,10 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end, ErtsGcQuickSanityCheck(p); - *recl += size_before - (HEAP_TOP(p) - HEAP_START(p)); + size_after = HEAP_TOP(p) - HEAP_START(p); + *recl += size_before - size_after; - adjust_after_fullsweep(p, need, objv, nobj); + adjusted = adjust_after_fullsweep(p, need, objv, nobj); #ifdef HARDDEBUG disallow_heap_frag_ref_in_heap(p); @@ -1423,7 +1460,8 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end, remove_message_buffers(p); ErtsGcQuickSanityCheck(p); - return 1; /* We are done. */ + + return gc_cost(size_after, adjusted ? size_after : 0); } static Eterm * @@ -1524,9 +1562,10 @@ full_sweep_heaps(Process *p, return n_htop; } -static void +static int adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj) { + int adjusted = 0; Uint wanted, sz, need_after; Uint stack_size = STACK_SZ_ON_HEAP(p); @@ -1539,6 +1578,7 @@ adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj) /* Too small - grow to match requested need */ sz = next_heap_size(p, need_after, 0); grow_new_heap(p, sz, objv, nobj); + adjusted = 1; } else if (3 * HEAP_SIZE(p) < 4 * need_after){ /* Need more than 75% of current, postpone to next GC.*/ FLAGS(p) |= F_HEAP_GROW; @@ -1555,8 +1595,10 @@ adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj) if (sz < HEAP_SIZE(p)) { shrink_new_heap(p, sz, objv, nobj); + adjusted = 1; } } + return adjusted; } /* diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index a496c5f008..d603866cbf 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -134,7 +134,8 @@ typedef struct { void erts_gc_info(ErtsGCInfo *gcip); void erts_init_gc(void); -int erts_garbage_collect(struct process*, int, Eterm*, int); +int erts_garbage_collect_nobump(struct process*, int, Eterm*, int); +void erts_garbage_collect(struct process*, int, Eterm*, int); void erts_garbage_collect_hibernate(struct process* p); Eterm erts_gc_after_bif_call_lhf(struct process* p, ErlHeapFragment *live_hf_end, Eterm result, Eterm* regs, Uint arity); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index e490ffea5a..5e7ed0d151 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -9774,7 +9774,7 @@ Process *schedule(Process *p, int calls) if (ERTS_IS_GC_DESIRED(p)) { if (!(state & ERTS_PSFLG_EXITING) && !(p->flags & F_DISABLE_GC)) { - reds -= erts_garbage_collect(p, 0, p->arg_reg, p->arity); + reds -= erts_garbage_collect_nobump(p, 0, p->arg_reg, p->arity); if (reds <= 0) { p->fcalls = reds; goto sched_out_proc; @@ -10057,10 +10057,10 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) else { if (!garbage_collected) { FLAGS(c_p) |= F_NEED_FULLSWEEP; - reds += erts_garbage_collect(c_p, - 0, - c_p->arg_reg, - c_p->arity); + reds += erts_garbage_collect_nobump(c_p, + 0, + c_p->arg_reg, + c_p->arity); garbage_collected = 1; } st_res = am_true; diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 8606371bdf..f82cad745a 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -583,7 +583,7 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) root[0] = id; root[1] = value; root[2] = old; - BUMP_REDS(p, erts_garbage_collect(p, needed, root, 3)); + erts_garbage_collect(p, needed, root, 3); id = root[0]; value = root[1]; old = root[2]; @@ -715,7 +715,7 @@ static void shrink(Process *p, Eterm* ret) needed = 2*erts_list_length(hi); } if (HeapWordsLeft(p) < needed) { - BUMP_REDS(p, erts_garbage_collect(p, needed, ret, 1)); + erts_garbage_collect(p, needed, ret, 1); hi = pd->data[(pd->splitPosition + pd->homeSize)]; lo = pd->data[pd->splitPosition]; } @@ -811,7 +811,7 @@ static void grow(Process *p) } } if (HeapWordsLeft(p) < needed) { - BUMP_REDS(p, erts_garbage_collect(p, needed, 0, 0)); + erts_garbage_collect(p, needed, 0, 0); } #ifdef DEBUG hp_limit = p->htop + needed; diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 968452a641..976180cc30 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -196,7 +196,7 @@ hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity) ASSERT(!(p->flags & F_DISABLE_GC)); if ((p->stop - 2) < p->htop) { DPRINTF("calling gc to increase BEAM stack size"); - p->fcalls -= erts_garbage_collect(p, 2, reg, arity); + erts_garbage_collect(p, 2, reg, arity); ASSERT(!((p->stop - 2) < p->htop)); } p->stop -= 2; diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h index bc863a4f36..620cc6356b 100644 --- a/erts/emulator/hipe/hipe_mode_switch.h +++ b/erts/emulator/hipe/hipe_mode_switch.h @@ -95,7 +95,7 @@ ERTS_GLB_INLINE void hipe_reserve_beam_trap_frame(Process *p, Eterm reg[], unsig /* ensure that at least 2 words are available on the BEAM stack */ if ((p->stop - 2) < p->htop) { - p->fcalls -= erts_garbage_collect(p, 2, reg, arity); + erts_garbage_collect(p, 2, reg, arity); ASSERT(!((p->stop - 2) < p->htop)); } p->stop -= 2; diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index ceae3497c5..aa81008b12 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -80,7 +80,7 @@ Eterm hipe_show_nstack_1(BIF_ALIST_1) void hipe_gc(Process *p, Eterm need) { hipe_set_narity(p, 1); - p->fcalls -= erts_garbage_collect(p, unsigned_val(need), NULL, 0); + erts_garbage_collect(p, unsigned_val(need), NULL, 0); hipe_set_narity(p, 0); } -- cgit v1.2.3 From 3e490ee1b43140ffcd8cb3fc3ccfe4339c08584e Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 11 Nov 2015 10:20:18 +0100 Subject: Improve yield strategy for list_to_binary()/binary_to_list() Avoid yield in the BIF when input is small. This either yielding before beginning to work in the BIF, or by allowing some more reductions before yielding. --- erts/emulator/beam/binary.c | 81 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index e670fbf31c..d3e481c7f9 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -34,6 +34,9 @@ #include "erl_binary.h" #include "erl_bits.h" +#define L2B_B2L_MIN_EXEC_REDS (CONTEXT_REDS/4) +#define L2B_B2L_RESCHED_REDS (CONTEXT_REDS/40) + static Export binary_to_list_continue_export; static Export list_to_binary_continue_export; @@ -415,10 +418,10 @@ binary_to_list_chunk(Process *c_p, } static ERTS_INLINE BIF_RETTYPE -binary_to_list(Process *c_p, Eterm *hp, Eterm tail, byte *bytes, Uint size, Uint bitoffs) +binary_to_list(Process *c_p, Eterm *hp, Eterm tail, byte *bytes, + Uint size, Uint bitoffs, int reds_left, int one_chunk) { - int reds_left = ERTS_BIF_REDS_LEFT(c_p); - if (size < reds_left*ERTS_B2L_BYTES_PER_REDUCTION) { + if (one_chunk) { Eterm res; BIF_RETTYPE ret; int bump_reds = (size - 1)/ERTS_B2L_BYTES_PER_REDUCTION + 1; @@ -472,11 +475,29 @@ BIF_RETTYPE binary_to_list_1(BIF_ALIST_1) Uint size; Uint bitsize; Uint bitoffs; + int reds_left; + int one_chunk; if (is_not_binary(BIF_ARG_1)) { goto error; } + size = binary_size(BIF_ARG_1); + reds_left = ERTS_BIF_REDS_LEFT(BIF_P); + one_chunk = size < reds_left*ERTS_B2L_BYTES_PER_REDUCTION; + if (!one_chunk) { + if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) { + if (reds_left <= L2B_B2L_RESCHED_REDS) { + /* Yield and do it with full context reds... */ + ERTS_BIF_YIELD1(bif_export[BIF_binary_to_list_1], + BIF_P, BIF_ARG_1); + } + /* Allow a bit more reductions... */ + one_chunk = 1; + reds_left = L2B_B2L_MIN_EXEC_REDS; + } + } + ERTS_GET_REAL_BIN(BIF_ARG_1, real_bin, offset, bitoffs, bitsize); if (bitsize != 0) { goto error; @@ -486,7 +507,8 @@ BIF_RETTYPE binary_to_list_1(BIF_ALIST_1) } else { Eterm* hp = HAlloc(BIF_P, 2 * size); byte* bytes = binary_bytes(real_bin)+offset; - return binary_to_list(BIF_P, hp, NIL, bytes, size, bitoffs); + return binary_to_list(BIF_P, hp, NIL, bytes, size, + bitoffs, reds_left, one_chunk); } error: @@ -505,6 +527,8 @@ BIF_RETTYPE binary_to_list_3(BIF_ALIST_3) Uint start; Uint stop; Eterm* hp; + int reds_left; + int one_chunk; if (is_not_binary(BIF_ARG_1)) { goto error; @@ -513,6 +537,21 @@ BIF_RETTYPE binary_to_list_3(BIF_ALIST_3) goto error; } size = binary_size(BIF_ARG_1); + reds_left = ERTS_BIF_REDS_LEFT(BIF_P); + one_chunk = size < reds_left*ERTS_B2L_BYTES_PER_REDUCTION; + if (!one_chunk) { + if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) { + if (reds_left <= L2B_B2L_RESCHED_REDS) { + /* Yield and do it with full context reds... */ + ERTS_BIF_YIELD3(bif_export[BIF_binary_to_list_3], + BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); + } + /* Allow a bit more reductions... */ + one_chunk = 1; + reds_left = L2B_B2L_MIN_EXEC_REDS; + } + } + ERTS_GET_BINARY_BYTES(BIF_ARG_1, bytes, bitoffs, bitsize); if (start < 1 || start > size || stop < 1 || stop > size || stop < start ) { @@ -520,7 +559,8 @@ BIF_RETTYPE binary_to_list_3(BIF_ALIST_3) } i = stop-start+1; hp = HAlloc(BIF_P, 2*i); - return binary_to_list(BIF_P, hp, NIL, bytes+start-1, i, bitoffs); + return binary_to_list(BIF_P, hp, NIL, bytes+start-1, i, + bitoffs, reds_left, one_chunk); error: BIF_ERROR(BIF_P, BADARG); } @@ -537,11 +577,27 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1) byte* bytes; Eterm previous = NIL; Eterm* hp; + int reds_left; + int one_chunk; if (is_not_binary(BIF_ARG_1)) { BIF_ERROR(BIF_P, BADARG); } size = binary_size(BIF_ARG_1); + reds_left = ERTS_BIF_REDS_LEFT(BIF_P); + one_chunk = size < reds_left*ERTS_B2L_BYTES_PER_REDUCTION; + if (!one_chunk) { + if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) { + if (reds_left <= L2B_B2L_RESCHED_REDS) { + /* Yield and do it with full context reds... */ + ERTS_BIF_YIELD1(bif_export[BIF_bitstring_to_list_1], + BIF_P, BIF_ARG_1); + } + /* Allow a bit more reductions... */ + one_chunk = 1; + reds_left = L2B_B2L_MIN_EXEC_REDS; + } + } ERTS_GET_REAL_BIN(BIF_ARG_1, real_bin, offset, bitoffs, bitsize); bytes = binary_bytes(real_bin)+offset; if (bitsize == 0) { @@ -566,7 +622,8 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1) hp += 2; } - return binary_to_list(BIF_P, hp, previous, bytes, size, bitoffs); + return binary_to_list(BIF_P, hp, previous, bytes, size, + bitoffs, reds_left, one_chunk); } @@ -795,8 +852,19 @@ static BIF_RETTYPE list_to_binary_continue(BIF_ALIST_1) BIF_RETTYPE erts_list_to_binary_bif(Process *c_p, Eterm arg, Export *bif) { + int orig_reds_left = ERTS_BIF_REDS_LEFT(c_p); BIF_RETTYPE ret; + if (orig_reds_left < L2B_B2L_MIN_EXEC_REDS) { + if (orig_reds_left <= L2B_B2L_RESCHED_REDS) { + /* Yield and do it with full context reds... */ + ERTS_BIF_PREP_YIELD1(ret, bif, c_p, arg); + return ret; + } + /* Allow a bit more reductions... */ + orig_reds_left = L2B_B2L_MIN_EXEC_REDS; + } + if (is_nil(arg)) ERTS_BIF_PREP_RET(ret, new_binary(c_p, (byte *) "", 0)); else if (is_not_list(arg)) @@ -818,7 +886,6 @@ BIF_RETTYPE erts_list_to_binary_bif(Process *c_p, Eterm arg, Export *bif) bif, erts_iolist_size_yielding, erts_iolist_to_buf_yielding); - int orig_reds_left = ERTS_BIF_REDS_LEFT(c_p); /* * First try to do it all at once without having to use -- cgit v1.2.3 From 16293b64dee1357b36926fc62ab59bca5cfdef0b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 12 Nov 2015 15:31:29 +0100 Subject: erts: Fix nif_SUITE for win64 where type long is only 32-bit and can not hold a pointer unless your lucky. --- erts/emulator/test/nif_SUITE_data/nif_mod.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c index 9c78c0e04d..f7e729e2b6 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.c +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c @@ -240,7 +240,7 @@ static ERL_NIF_TERM lib_version(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg static ERL_NIF_TERM get_priv_data_ptr(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ADD_CALL("get_priv_data_ptr"); - return enif_make_ulong(env, (unsigned long)priv_data(env)); + return enif_make_uint64(env, (ErlNifUInt64)priv_data(env)); } static ERL_NIF_TERM make_new_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -- cgit v1.2.3 From fd1e6b2b2623395512ea0450c3b4e656cb354f42 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 13 Nov 2015 19:13:03 +0100 Subject: stdlib: Fix bug in binary:split for empty binary Bug introduced om master branch at b93e9b611056828a and reported in ERL-43. --- erts/emulator/beam/erl_bif_binary.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index b9640e211d..aec72bd61a 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -1614,6 +1614,10 @@ static Eterm do_split_not_found_result(Process *p, Eterm subject, BinaryFindStat Eterm *hp; Eterm ret; + if (bfs->flags & (BINARY_SPLIT_TRIM | BINARY_SPLIT_TRIM_ALL) + && binary_size(subject) == 0) { + return NIL; + } hp = HAlloc(p, 2); ret = CONS(hp, subject, NIL); -- cgit v1.2.3 From 3d42f1aac27268652c17972051f303f35b035e70 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sat, 14 Nov 2015 11:45:01 +0100 Subject: hipe_x86_signal: add support for musl libc - change #if tests at default Solaris case to explicitly check for __sun__ - add new default case, instantiate it for musl --- erts/emulator/hipe/hipe_x86_signal.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index bb8a3f041f..69d4ea10c2 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -198,7 +198,7 @@ static void do_init(void) #define INIT() do { if (!init_done()) do_init(); } while (0) #endif /* __DARWIN__ */ -#if !defined(__GLIBC__) && !defined(__DARWIN__) && !defined(__NetBSD__) +#if defined(__sun__) /* * Assume Solaris/x86 2.8. * There is a number of sigaction() procedures in libc: @@ -232,7 +232,34 @@ static void do_init(void) } #define _NSIG NSIG #define INIT() do { if (!init_done()) do_init(); } while (0) -#endif /* not glibc or darwin */ +#endif /* __sun__ */ + +#if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__sun__)) +/* + * Unknown libc -- assume musl. Note: musl deliberately does not provide a musl-specific + * feature test macro, so we cannot check for it. + * + * sigaction is a weak alias for __sigaction, which is a wrapper for __libc_sigaction. + * There are libc-internal calls to __libc_sigaction which install handlers, so we must + * override __libc_sigaction rather than __sigaction. + */ +#include +static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); +#define init_done() (__next_sigaction != 0) +#define __SIGACTION __libc_sigaction +static void do_init(void) +{ + __next_sigaction = dlsym(RTLD_NEXT, "__libc_sigaction"); + if (__next_sigaction != 0) + return; + perror("dlsym"); + abort(); +} +#ifndef _NSIG +#define _NSIG NSIG +#endif +#define INIT() do { if (!init_done()) do_init(); } while (0) +#endif /* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __sun__) */ #if !defined(__NetBSD__) /* -- cgit v1.2.3 From c8a7d2d7d1762378e49d3890a8dccde0110bed6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 16 Nov 2015 10:25:21 +0100 Subject: erl_prim_loader doc: Remove description of custom loaders Custom loaders have not been supported for several releases. Remove the documentation for custom loaders. --- erts/doc/src/erl_prim_loader.xml | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml index d05f0d9aea..db4f132609 100644 --- a/erts/doc/src/erl_prim_loader.xml +++ b/erts/doc/src/erl_prim_loader.xml @@ -36,17 +36,11 @@ the system. The start script is also fetched with this low level loader.

erl_prim_loader knows about the environment and how to - fetch modules. The loader could, for example, fetch files using - the file system (with absolute file names as input), or a - database (where the binary format of a module is stored).

+ fetch modules.

The -loader Loader command line flag can be used to choose the method used by the erl_prim_loader. Two Loader methods are supported by the Erlang runtime system: - efile and inet. If another loader is required, then - it has to be implemented by the user. The Loader provided - by the user must fulfill the protocol defined below, and it is - started with the erl_prim_loader by evaluating - open_port({spawn,Loader},[binary]).

+ efile and inet.

The support for loading of code from archive files is experimental. The sole purpose of releasing it before it is ready @@ -83,9 +77,6 @@ started on each of hosts given in Hosts in order to answer the requests. See erl_boot_server(3).

-

If -loader is something else, the given port program - is started. The port program is supposed to follow - the protocol specified below.

@@ -174,22 +165,6 @@ -
- Protocol -

The following protocol must be followed if a user provided - loader port program is used. The Loader port program is - started with the command - open_port({spawn,Loader},[binary]). The protocol is as - follows:

-
-Function          Send               Receive
--------------------------------------------------------------
-get_file          [102 | FileName]   [121 | BinaryFile] (on success)
-                                     [122]              (failure)
-
-stop              eof                terminate
-
-
Command Line Flags

The erl_prim_loader module interprets the following @@ -199,10 +174,8 @@ stop eof terminate

Specifies the name of the loader used by erl_prim_loader. Loader can be efile - (use the local file system), or inet (load using - the boot_server on another Erlang node). If - Loader is user defined, the defined Loader port - program is started.

+ (use the local file system) or inet (load using + the boot_server on another Erlang node).

If the -loader flag is omitted, it defaults to efile.

-- cgit v1.2.3 From dad527f55b51d60e75a0d19aa0f4f42c1065777f Mon Sep 17 00:00:00 2001 From: "Nikolaos S. Papaspyrou" Date: Fri, 8 Jun 2012 22:21:02 +0300 Subject: An implementation of lightweight unbounded queues --- erts/emulator/beam/global.h | 84 ++++++++++++++++++++++++++++++++++++++++++++- erts/emulator/beam/utils.c | 25 ++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 594c0ccf94..20f100b427 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -380,7 +380,7 @@ extern int bif_reductions; /* reductions + fcalls (when doing call_bif) */ extern int stackdump_on_exit; /* - * Here is an implementation of a lightweiht stack. + * Here is an implementation of a lightweight stack. * * Use it like this: * @@ -845,6 +845,88 @@ do {\ } while(0) +/* + * An implementation of lightweight unbounded queues, + * using a circular dynamic array. + * It does not include support for change_allocator. + * + * Use it like this: + * + * DECLARE_EQUEUE(Queue) (At the start of a block) + * ... + * EQUEUE_PUT(Queue, Term) + * ... + * if (EQUEUE_ISEMPTY(Queue)) { + * Queue is empty + * } else { + * Term = EQUEUE_GET(Stack); + * Process popped Term here + * } + * ... + * DESTROY_EQUEUE(Queue) + */ + +typedef struct { + Eterm* start; + Eterm* front; + Eterm* back; + int possibly_empty; + Eterm* end; + ErtsAlcType_t alloc_type; +}ErtsEQueue; + +#define DEF_EQUEUE_SIZE (16) + +void erl_grow_equeue(ErtsEQueue*, Eterm* def_queue); +#define EQUE_CONCAT(a,b) a##b +#define EQUE_DEF_QUEUE(q) EQUE_CONCAT(q,_default_equeue) + +#define DECLARE_EQUEUE(q) \ + UWord EQUE_DEF_QUEUE(q)[DEF_EQUEUE_SIZE]; \ + ErtsEQueue q = { \ + EQUE_DEF_QUEUE(q), /* start */ \ + EQUE_DEF_QUEUE(q), /* front */ \ + EQUE_DEF_QUEUE(q), /* back */ \ + 1, /* possibly_empty */ \ + EQUE_DEF_QUEUE(q) + DEF_EQUEUE_SIZE, /* end */ \ + ERTS_ALC_T_ESTACK /* alloc_type */ \ + } + +#define DESTROY_EQUEUE(q) \ +do { \ + if (q.start != EQUE_DEF_QUEUE(q)) { \ + erts_free(q.alloc_type, q.start); \ + } \ +} while(0) + +#define EQUEUE_PUT_UNCHECKED(q, x) \ +do { \ + q.possibly_empty = 0; \ + *(q.back) = (x); \ + if (++(q.back) == q.end) { \ + q.back = q.start; \ + } \ +} while(0) + +#define EQUEUE_PUT(q, x) \ +do { \ + if (q.back == q.front && !q.possibly_empty) { \ + erl_grow_equeue(&q, EQUE_DEF_QUEUE(q)); \ + } \ + EQUEUE_PUT_UNCHECKED(q, x); \ +} while(0) + +#define EQUEUE_ISEMPTY(q) (q.back == q.front && q.possibly_empty) + +#define EQUEUE_GET(q) ({ \ + q.possibly_empty = 1; \ + UWord x = *(q.front); \ + if (++(q.front) == q.end) { \ + q.front = q.start; \ + } \ + x; \ +}) + /* binary.c */ void erts_emasculate_writable_binary(ProcBin* pb); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index c3735683bb..184477c36b 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -258,6 +258,31 @@ erl_grow_pstack(ErtsPStack* s, void* default_pstack, unsigned need_bytes) s->psp = s->pstart + sp_offs; } +/* + * Helper function for the EQUEUE macros defined in global.h. + */ + +void +erl_grow_equeue(ErtsEQueue* q, Eterm* default_equeue) +{ + Uint old_size = (q->end - q->start); + Uint new_size = old_size * 2; + Uint first_part = (q->end - q->front); + Uint second_part = (q->back - q->start); + Eterm* new_ptr = erts_alloc(q->alloc_type, new_size*sizeof(Eterm)); + ASSERT(q->back == q->front); // of course the queue is full now! + if (first_part > 0) + sys_memcpy(new_ptr, q->front, first_part*sizeof(Eterm)); + if (second_part > 0) + sys_memcpy(new_ptr+first_part, q->start, second_part*sizeof(Eterm)); + if (q->start != default_equeue) + erts_free(q->alloc_type, q->start); + q->start = new_ptr; + q->end = q->start + new_size; + q->front = q->start; + q->back = q->start + old_size; +} + /* CTYPE macros */ #define LATIN1 -- cgit v1.2.3 From 9d0a5bf2c1cc564fd38268cbb5313cd8813ea138 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 16 Nov 2015 14:57:12 +0100 Subject: erts: Add garbage_collection_info to process_info/2 This info request returns greater details about the current gc state. This info is not included in the default process_info/1 as it would clutter the default printout with too much information. --- erts/doc/src/erlang.xml | 11 +++++++ erts/emulator/beam/atom.names | 1 + erts/emulator/beam/erl_bif_info.c | 30 ++++++++++++++++++- erts/emulator/beam/erl_gc.c | 51 ++++++++++++++++++++++++++++++++ erts/emulator/beam/erl_gc.h | 5 ++++ erts/emulator/beam/erl_trace.c | 57 ++++-------------------------------- erts/emulator/test/process_SUITE.erl | 47 ++++++++++++++++++++++++++++- erts/preloaded/src/erlang.erl | 2 ++ 8 files changed, 151 insertions(+), 53 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 2e82bb62a9..e4d3533c75 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4621,6 +4621,17 @@ os_prompt% The content of GCInfo can be changed without prior notice.

+ {garbage_collection_info, GCInfo} + +

GCInfo is a list containing miscellaneous + detailed information about garbage collection for this process. + The content of GCInfo can be changed without + prior notice. + See gc_start in + erlang:trace/3 for details about + what each item means. +

+
{group_leader, GroupLeader}

GroupLeader is group leader for the I/O of diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index ea04495574..4ad989cac1 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -256,6 +256,7 @@ atom functions atom function_clause atom garbage_collecting atom garbage_collection +atom garbage_collection_info atom gc_end atom gc_start atom Ge='>=' diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 1eb106a551..855ef8742a 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -589,7 +589,8 @@ static Eterm pi_args[] = { am_min_bin_vheap_size, am_current_location, am_current_stacktrace, - am_off_heap_message_queue + am_off_heap_message_queue, + am_garbage_collection_info }; #define ERTS_PI_ARGS ((int) (sizeof(pi_args)/sizeof(Eterm))) @@ -638,6 +639,7 @@ pi_arg2ix(Eterm arg) case am_current_location: return 29; case am_current_stacktrace: return 30; case am_off_heap_message_queue: return 31; + case am_garbage_collection_info: return 32; default: return -1; } } @@ -1395,6 +1397,32 @@ process_info_aux(Process *BIF_P, break; } + case am_garbage_collection_info: { + Uint sz = 0, actual_sz = 0; + + if (rp == BIF_P) { + sz += ERTS_PROCESS_GC_INFO_MAX_SIZE; + } else { + erts_process_gc_info(rp, &sz, NULL); + sz += 3; + } + + hp = HAlloc(BIF_P, sz); + res = erts_process_gc_info(rp, &actual_sz, &hp); + + /* We may have some extra space, fill with 0 tuples */ + if (actual_sz <= sz - 3) { + for (; actual_sz < sz - 3; hp++, actual_sz++) + hp[0] = make_arityval(0); + } else { + for (; actual_sz < sz; hp++, actual_sz++) + hp[0] = make_arityval(0); + hp = HAlloc(BIF_P, 3); + } + + break; + } + case am_group_leader: { int sz = NC_HEAP_SIZE(rp->group_leader); hp = HAlloc(BIF_P, 3 + sz); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 6cb37752bc..3399b1a9f2 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2741,6 +2741,57 @@ erts_gc_info_request(Process *c_p) return ref; } +Eterm +erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp) +{ + ERTS_DECL_AM(bin_vheap_size); + ERTS_DECL_AM(bin_vheap_block_size); + ERTS_DECL_AM(bin_old_vheap_size); + ERTS_DECL_AM(bin_old_vheap_block_size); + Eterm tags[] = { + /* If you increase the number of elements here, make sure to update + any call sites as they may have stack allocations that depend + on the number of elements here. */ + am_old_heap_block_size, + am_heap_block_size, + am_mbuf_size, + am_recent_size, + am_stack_size, + am_old_heap_size, + am_heap_size, + AM_bin_vheap_size, + AM_bin_vheap_block_size, + AM_bin_old_vheap_size, + AM_bin_old_vheap_block_size + }; + UWord values[] = { + OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) : 0, + HEAP_SIZE(p), + MBUF_SIZE(p), + HIGH_WATER(p) - HEAP_START(p), + STACK_START(p) - p->stop, + OLD_HEAP(p) ? OLD_HTOP(p) - OLD_HEAP(p) : 0, + HEAP_TOP(p) - HEAP_START(p), + MSO(p).overhead, + BIN_VHEAP_SZ(p), + BIN_OLD_VHEAP(p), + BIN_OLD_VHEAP_SZ(p) + }; + + Eterm res = THE_NON_VALUE; + + ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(*tags)); + ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == ERTS_PROCESS_GC_INFO_MAX_TERMS); + + res = erts_bld_atom_uword_2tup_list(hpp, + sizep, + sizeof(values)/sizeof(*values), + tags, + values); + + return res; +} + #if defined(DEBUG) || defined(ERTS_OFFHEAP_DEBUG) static int diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index d603866cbf..2cedd9361f 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -132,6 +132,11 @@ typedef struct { Uint64 garbage_cols; } ErtsGCInfo; +#define ERTS_PROCESS_GC_INFO_MAX_TERMS (11) /* number of elements in process_gc_info*/ +#define ERTS_PROCESS_GC_INFO_MAX_SIZE \ + (ERTS_PROCESS_GC_INFO_MAX_TERMS * (2/*cons*/ + 3/*2-tuple*/ + BIG_UINT_HEAP_SIZE)) +Eterm erts_process_gc_info(struct process*, Uint *, Eterm **); + void erts_gc_info(ErtsGCInfo *gcip); void erts_init_gc(void); int erts_garbage_collect_nobump(struct process*, int, Eterm*, int); diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index d02f1f7213..e47b1e4edc 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -2144,11 +2144,6 @@ void save_calls(Process *p, Export *e) void trace_gc(Process *p, Eterm what) { - ERTS_DECL_AM(bin_vheap_size); - ERTS_DECL_AM(bin_vheap_block_size); - ERTS_DECL_AM(bin_old_vheap_size); - ERTS_DECL_AM(bin_old_vheap_block_size); - ErlHeapFragment *bp = NULL; ErlOffHeap *off_heap; ERTS_TRACER_REF_TYPE tracer_ref = ERTS_NULL_TRACER_REF; /* Initialized @@ -2159,55 +2154,22 @@ trace_gc(Process *p, Eterm what) Eterm msg = NIL; Uint size; - Eterm tags[] = { - am_old_heap_block_size, - am_heap_block_size, - am_mbuf_size, - am_recent_size, - am_stack_size, - am_old_heap_size, - am_heap_size, - AM_bin_vheap_size, - AM_bin_vheap_block_size, - AM_bin_old_vheap_size, - AM_bin_old_vheap_block_size - }; - - UWord values[] = { - OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) : 0, - HEAP_SIZE(p), - MBUF_SIZE(p), - HIGH_WATER(p) - HEAP_START(p), - STACK_START(p) - p->stop, - OLD_HEAP(p) ? OLD_HTOP(p) - OLD_HEAP(p) : 0, - HEAP_TOP(p) - HEAP_START(p), - MSO(p).overhead, - BIN_VHEAP_SZ(p), - BIN_OLD_VHEAP(p), - BIN_OLD_VHEAP_SZ(p) - }; #define LOCAL_HEAP_SIZE \ - (sizeof(values)/sizeof(*values)) * \ - (2/*cons*/ + 3/*2-tuple*/ + BIG_UINT_HEAP_SIZE) + \ + (ERTS_PROCESS_GC_INFO_MAX_SIZE) + \ 5/*4-tuple */ + TS_HEAP_WORDS DeclareTmpHeap(local_heap,LOCAL_HEAP_SIZE,p); #ifdef DEBUG Eterm* limit; #endif - ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(Eterm)); - UseTmpHeap(LOCAL_HEAP_SIZE,p); if (is_internal_port(ERTS_TRACER_PROC(p))) { hp = local_heap; #ifdef DEBUG size = 0; - (void) erts_bld_atom_uword_2tup_list(NULL, - &size, - sizeof(values)/sizeof(*values), - tags, - values); + (void) erts_process_gc_info(p, &size, NULL); + size += 5/*4-tuple*/ + TS_SIZE(p); #endif } else { @@ -2218,11 +2180,8 @@ trace_gc(Process *p, Eterm what) ERTS_TRACE_FLAGS(p)); size = 0; - (void) erts_bld_atom_uword_2tup_list(NULL, - &size, - sizeof(values)/sizeof(*values), - tags, - values); + (void) erts_process_gc_info(p, &size, NULL); + size += 5/*4-tuple*/ + TS_SIZE(p); hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); @@ -2233,11 +2192,7 @@ trace_gc(Process *p, Eterm what) ASSERT(size <= LOCAL_HEAP_SIZE); #endif - msg = erts_bld_atom_uword_2tup_list(&hp, - NULL, - sizeof(values)/sizeof(*values), - tags, - values); + msg = erts_process_gc_info(p, NULL, &hp); msg = TUPLE4(hp, am_trace, p->common.id, what, msg); hp += 5; diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 97aa5e573e..bfd1e78bf0 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -41,6 +41,7 @@ process_info_2_list/1, process_info_lock_reschedule/1, process_info_lock_reschedule2/1, process_info_lock_reschedule3/1, + process_info_garbage_collection/1, bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1, process_status_exiting/1, otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1, @@ -75,7 +76,9 @@ all() -> process_info_other_dist_msg, process_info_2_list, process_info_lock_reschedule, process_info_lock_reschedule2, - process_info_lock_reschedule3, process_status_exiting, + process_info_lock_reschedule3, + process_info_garbage_collection, + process_status_exiting, bump_reductions, low_prio, yield, yield2, otp_4725, bad_register, garbage_collect, process_info_messages, process_flag_badarg, process_flag_heap_size, @@ -932,6 +935,48 @@ start_spawner() -> stop_spawner() -> ok. +%% Tests erlang:process_info(Pid, garbage_collection_info) +process_info_garbage_collection(_Config) -> + Parent = self(), + Pid = spawn_link( + fun() -> + receive go -> ok end, + (fun F(0) -> + Parent ! deep, + receive ok -> ok end, + []; + F(N) -> + timer:sleep(1), + [lists:seq(1,100) | F(N-1)] + end)(1000), + Parent ! shallow, + receive done -> ok end + end), + {garbage_collection_info, Before} = + erlang:process_info(Pid, garbage_collection_info), + Pid ! go, receive deep -> ok end, + {_, Deep} = erlang:process_info(Pid, garbage_collection_info), + Pid ! ok, receive shallow -> ok end, + {_, After} = erlang:process_info(Pid, garbage_collection_info), + Pid ! done, + + %% Do some general checks to see if everything seems to be roughly correct + ct:log("Before: ~p",[Before]), + ct:log("Deep: ~p",[Deep]), + ct:log("After: ~p",[After]), + + %% Check stack_size + true = proplists:get_value(stack_size, Before) < proplists:get_value(stack_size, Deep), + true = proplists:get_value(stack_size, After) < proplists:get_value(stack_size, Deep), + + %% Check used heap size + true = proplists:get_value(heap_size, Before) + proplists:get_value(old_heap_size, Before) + < proplists:get_value(heap_size, Deep) + proplists:get_value(old_heap_size, Deep), + true = proplists:get_value(heap_size, Before) + proplists:get_value(old_heap_size, Before) + < proplists:get_value(heap_size, After) + proplists:get_value(old_heap_size, After), + + ok. + %% Tests erlang:bump_reductions/1. bump_reductions(Config) when is_list(Config) -> erlang:garbage_collect(), diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 6a9ec9c915..cb607a7730 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2074,6 +2074,7 @@ process_flag(_Flag, _Value) -> dictionary | error_handler | garbage_collection | + garbage_collection_info | group_leader | heap_size | initial_call | @@ -2114,6 +2115,7 @@ process_flag(_Flag, _Value) -> {dictionary, Dictionary :: [{Key :: term(), Value :: term()}]} | {error_handler, Module :: module()} | {garbage_collection, GCInfo :: [{atom(),non_neg_integer()}]} | + {garbage_collection_info, GCInfo :: [{atom(),non_neg_integer()}]} | {group_leader, GroupLeader :: pid()} | {heap_size, Size :: non_neg_integer()} | {initial_call, mfa()} | -- cgit v1.2.3 From d5711cb70be9ad2e5c78839e8a368900ba248a4a Mon Sep 17 00:00:00 2001 From: "Nikolaos S. Papaspyrou" Date: Fri, 8 Jun 2012 23:13:50 +0300 Subject: Add all the main machinery Add functions size_shared, copy_shared_calculate and copy_shared_perform. Add the infrastructure for making these communicate with each other. Add debug information to other places in the VM, to watch interaction with the sharing-preserving copy. CAUTION: If you define the SHCOPY_DEBUG macro (after SHCOPY is actually used in the VM) and make the whole OTP, there will be a lot of debugging messages during make (it will also be enabled in erlc). You have been warned... --- erts/emulator/beam/copy.c | 1131 +++++++++++++++++++++++++++++++++++++++++++ erts/emulator/beam/erl_gc.c | 10 + erts/emulator/beam/global.h | 56 ++- 3 files changed, 1196 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index f27c526413..b31e043f08 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -75,8 +75,17 @@ Uint size_object(Eterm obj) Uint sum = 0; Eterm* ptr; int arity; +#ifdef SHCOPY_DEBUG + Eterm mypid; +#endif DECLARE_ESTACK(s); + +#ifdef SHCOPY_DEBUG + mypid = erts_get_current_pid(); + VERBOSE_DEBUG("[pid=%T] size_object %p\n", mypid, obj); +#endif + for (;;) { switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: @@ -211,6 +220,7 @@ Uint size_object(Eterm obj) pop_next: if (ESTACK_ISEMPTY(s)) { DESTROY_ESTACK(s); + VERBOSE_DEBUG("[pid=%T] size was: %u\n", mypid, sum); return sum; } obj = ESTACK_POP(s); @@ -221,6 +231,350 @@ Uint size_object(Eterm obj) } } +/* + * Machinery for sharing preserving information + * Using a WSTACK but not very transparently; consider refactoring + */ + +#define DECLARE_BITSTORE(s) \ + DECLARE_WSTACK(s); \ + int WSTK_CONCAT(s,_bitoffs) = 0; \ + int WSTK_CONCAT(s,_offset) = 0; \ + UWord WSTK_CONCAT(s,_buffer) = 0 + +#define DESTROY_BITSTORE(s) DESTROY_WSTACK(s) +#define BITSTORE_PUT(s,i) \ +do { \ + WSTK_CONCAT(s,_buffer) |= i << WSTK_CONCAT(s,_bitoffs); \ + WSTK_CONCAT(s,_bitoffs) += 2; \ + if (WSTK_CONCAT(s,_bitoffs) >= 8*sizeof(UWord)) { \ + WSTACK_PUSH(s, WSTK_CONCAT(s,_buffer)); \ + WSTK_CONCAT(s,_bitoffs) = 0; \ + WSTK_CONCAT(s,_buffer) = 0; \ + } \ +} while(0) +#define BITSTORE_CLOSE(s) \ +do { \ + if (WSTK_CONCAT(s,_bitoffs) > 0) { \ + WSTACK_PUSH(s, WSTK_CONCAT(s,_buffer)); \ + WSTK_CONCAT(s,_bitoffs) = 0; \ + } \ +} while(0) + +#define BITSTORE_GET(s) ({ \ + UWord result; \ + if (WSTK_CONCAT(s,_bitoffs) <= 0) { \ + WSTK_CONCAT(s,_buffer) = s.wstart[WSTK_CONCAT(s,_offset)]; \ + WSTK_CONCAT(s,_offset)++; \ + WSTK_CONCAT(s,_bitoffs) = 8*sizeof(UWord); \ + } \ + WSTK_CONCAT(s,_bitoffs) -= 2; \ + result = WSTK_CONCAT(s,_buffer) & 3; \ + WSTK_CONCAT(s,_buffer) >>= 2; \ + result; \ +}) + +#define BOXED_VISITED_MASK ((Eterm) 3) +#define BOXED_VISITED ((Eterm) 1) +#define BOXED_SHARED_UNPROCESSED ((Eterm) 2) +#define BOXED_SHARED_PROCESSED ((Eterm) 3) + + +/* + * Is an object in the local heap of a process? + */ + +#define INHEAP_SIMPLE(p, ptr) ( \ + (OLD_HEAP(p) && OLD_HEAP(p) <= ptr && ptr < OLD_HEND(p)) || \ + (HEAP_START(p) <= ptr && ptr < HEAP_END(p)) \ + ) +#define INHEAP(p, ptr) ( \ + INHEAP_SIMPLE(p, ptr) || \ + (force_local ? (force_local = 0, 1) : 0) \ + ) +#define COUNT_OFF_HEAP 0 + + +/* + * Return the real size of an object and find sharing information + * This currently returns the same as erts_debug:size/1. + * It is argued whether the size of subterms in constant pools + * should be counted or not. + */ +#if HALFWORD_HEAP +Uint size_shared_rel(Eterm obj, Eterm* base) +#else +Uint size_shared(Eterm obj) +#endif +{ + Eterm saved_obj = obj; + Uint sum = 0; + Eterm* ptr; + Process* myself; + + DECLARE_EQUEUE(s); + DECLARE_BITSTORE(b); + + myself = erts_get_current_process(); + if (myself == NULL) + return size_object(obj); + + for (;;) { + VERBOSE_DEBUG("[size] visiting: %x ", obj); + switch (primary_tag(obj)) { + case TAG_PRIMARY_LIST: { + Eterm head, tail; + VERBOSE_DEBUG("L"); + ptr = list_val_rel(obj, base); + /* we're not counting anything that's outside our heap */ + if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { + goto pop_next; + } + head = CAR(ptr); + tail = CDR(ptr); + /* if it's visited, don't count it */ + if (primary_tag(tail) == TAG_PRIMARY_HEADER || + primary_tag(head) == TAG_PRIMARY_HEADER) { + VERBOSE_DEBUG("!"); + goto pop_next; + } + /* else make it visited now */ + switch (primary_tag(tail)) { + case TAG_PRIMARY_LIST: + VERBOSE_DEBUG("/L"); + ptr[1] = (tail - TAG_PRIMARY_LIST) | TAG_PRIMARY_HEADER; + break; + case TAG_PRIMARY_IMMED1: + VERBOSE_DEBUG("/I"); + CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER; + CDR(ptr) = (tail - TAG_PRIMARY_IMMED1) | primary_tag(head); + break; + case TAG_PRIMARY_BOXED: + VERBOSE_DEBUG("/B saved %d", primary_tag(head)); + BITSTORE_PUT(b, primary_tag(head)); + CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER; + CDR(ptr) = (tail - TAG_PRIMARY_BOXED) | TAG_PRIMARY_HEADER; + break; + } + /* and count it */ + sum += 2; + if (!IS_CONST(head)) { + EQUEUE_PUT(s, head); + } + obj = tail; + break; + } + case TAG_PRIMARY_BOXED: { + Eterm hdr; + VERBOSE_DEBUG("B"); + ptr = boxed_val_rel(obj, base); + /* we're not counting anything that's outside our heap */ + if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { + goto pop_next; + } + hdr = *ptr; + /* if it's visited, don't count it */ + if (primary_tag(hdr) != TAG_PRIMARY_HEADER) { + VERBOSE_DEBUG("!"); + goto pop_next; + } + /* else make it visited now */ + *ptr = (hdr - primary_tag(hdr)) + BOXED_VISITED; + /* and count it */ + ASSERT(is_header(hdr)); + switch (hdr & _TAG_HEADER_MASK) { + case ARITYVAL_SUBTAG: { + int arity = header_arity(hdr); + VERBOSE_DEBUG("/T"); + sum += arity + 1; + if (arity == 0) { /* Empty tuple -- unusual. */ + VERBOSE_DEBUG("e"); + goto pop_next; + } + while (arity-- > 0) { + obj = *++ptr; + if (!IS_CONST(obj)) { + EQUEUE_PUT(s, obj); + } + } + goto pop_next; + } + case FUN_SUBTAG: { + ErlFunThing* funp = (ErlFunThing *) ptr; + unsigned eterms = 1 /* creator */ + funp->num_free; + unsigned sz = thing_arityval(hdr); + VERBOSE_DEBUG("/F"); + sum += 1 /* header */ + sz + eterms; + ptr += 1 /* header */ + sz; + while (eterms-- > 0) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT(s, obj); + } + } + goto pop_next; + } + case SUB_BINARY_SUBTAG: { + ErlSubBin* sb = (ErlSubBin *) ptr; + Uint extra_bytes; + Eterm hdr; + ASSERT((sb->thing_word & ~BOXED_VISITED_MASK) == HEADER_SUB_BIN); + if (sb->bitsize + sb->bitoffs > 8) { + sum += ERL_SUB_BIN_SIZE; + extra_bytes = 2; + } else if (sb->bitsize + sb->bitoffs > 0) { + sum += ERL_SUB_BIN_SIZE; + extra_bytes = 1; + } else { + extra_bytes = 0; + } + ptr = binary_val_rel(sb->orig, base); + hdr = (*ptr) & ~BOXED_VISITED_MASK; + if (thing_subtag(hdr) == REFC_BINARY_SUBTAG) { + sum += PROC_BIN_SIZE; + } else { + ASSERT(thing_subtag(hdr) == HEAP_BINARY_SUBTAG); + sum += heap_bin_size(binary_size_rel(obj, base) + extra_bytes); + } + goto pop_next; + } + case BIN_MATCHSTATE_SUBTAG: + erl_exit(ERTS_ABORT_EXIT, + "size_shared: matchstate term not allowed"); + default: + VERBOSE_DEBUG("/D"); + sum += thing_arityval(hdr) + 1; + goto pop_next; + } + break; + } + case TAG_PRIMARY_IMMED1: + VERBOSE_DEBUG("I"); + pop_next: + if (EQUEUE_ISEMPTY(s)) { + goto cleanup; + } + obj = EQUEUE_GET(s); + break; + default: + erl_exit(ERTS_ABORT_EXIT, "size_shared: bad tag for %#x\n", obj); + } + VERBOSE_DEBUG("\n"); + } + +cleanup: + VERBOSE_DEBUG("\n"); + obj = saved_obj; + BITSTORE_CLOSE(b); + for (;;) { + VERBOSE_DEBUG("[size] revisiting: %x ", obj); + switch (primary_tag(obj)) { + case TAG_PRIMARY_LIST: { + Eterm head, tail; + VERBOSE_DEBUG("L"); + ptr = list_val_rel(obj, base); + if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { + goto cleanup_next; + } + head = CAR(ptr); + tail = CDR(ptr); + /* if not already clean, clean it up */ + if (primary_tag(tail) == TAG_PRIMARY_HEADER) { + if (primary_tag(head) == TAG_PRIMARY_HEADER) { + Eterm saved = BITSTORE_GET(b); + VERBOSE_DEBUG("/B restoring %d", saved); + CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | saved; + CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) | TAG_PRIMARY_BOXED; + } else { + VERBOSE_DEBUG("/L"); + CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) | TAG_PRIMARY_LIST; + } + } else if (primary_tag(head) == TAG_PRIMARY_HEADER) { + VERBOSE_DEBUG("/I"); + CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | primary_tag(tail); + CDR(ptr) = tail = (tail - primary_tag(tail)) | TAG_PRIMARY_IMMED1; + } else { + VERBOSE_DEBUG("!"); + goto cleanup_next; + } + /* and its children too */ + if (!IS_CONST(head)) { + EQUEUE_PUT_UNCHECKED(s, head); + } + obj = tail; + break; + } + case TAG_PRIMARY_BOXED: { + Eterm hdr; + VERBOSE_DEBUG("B"); + ptr = boxed_val_rel(obj, base); + if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { + goto cleanup_next; + } + hdr = *ptr; + /* if not already clean, clean it up */ + if (primary_tag(hdr) == TAG_PRIMARY_HEADER) { + goto cleanup_next; + } + else { + ASSERT(primary_tag(hdr) == BOXED_VISITED); + *ptr = hdr = (hdr - BOXED_VISITED) + TAG_PRIMARY_HEADER; + } + /* and its children too */ + switch (hdr & _TAG_HEADER_MASK) { + case ARITYVAL_SUBTAG: { + int arity = header_arity(hdr); + if (arity == 0) { /* Empty tuple -- unusual. */ + goto cleanup_next; + } + while (arity-- > 0) { + obj = *++ptr; + if (!IS_CONST(obj)) { + EQUEUE_PUT_UNCHECKED(s, obj); + } + } + goto cleanup_next; + } + case FUN_SUBTAG: { + ErlFunThing* funp = (ErlFunThing *) ptr; + unsigned eterms = 1 /* creator */ + funp->num_free; + unsigned sz = thing_arityval(hdr); + ptr += 1 /* header */ + sz; + while (eterms-- > 0) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT_UNCHECKED(s, obj); + } + } + goto cleanup_next; + } + default: + goto cleanup_next; + } + break; + } + case TAG_PRIMARY_IMMED1: + cleanup_next: + if (EQUEUE_ISEMPTY(s)) { + goto all_clean; + } + obj = EQUEUE_GET(s); + break; + default: + erl_exit(ERTS_ABORT_EXIT, "size_shared: bad tag for %#x\n", obj); + } + VERBOSE_DEBUG("\n"); + } + + all_clean: + VERBOSE_DEBUG("\n"); + /* Return the result */ + DESTROY_EQUEUE(s); + DESTROY_BITSTORE(b); + return sum; +} + + /* * Copy a structure to a heap. */ @@ -244,10 +598,18 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) Eterm org_obj = obj; Uint org_sz = sz; #endif +#ifdef SHCOPY_DEBUG + Eterm mypid; +#endif if (IS_CONST(obj)) return obj; +#ifdef SHCOPY_DEBUG + mypid = erts_get_current_pid(); + VERBOSE_DEBUG("[pid=%T] copy_struct %p\n", mypid, obj); +#endif + DTRACE1(copy_struct, (int32_t)sz); hp = htop = *hpp; @@ -529,9 +891,778 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } #endif *hpp = (Eterm *) (hstart+hsize); + VERBOSE_DEBUG("[pid=%T] result is at %p\n", mypid, res); return res; } + +/* + * Machinery for the table used by the sharing preserving copier + * Using an ESTACK but not very transparently; consider refactoring + */ + +#define DECLARE_SHTABLE(s) \ + DECLARE_ESTACK(s); \ + Uint ESTK_CONCAT(s,_offset) = 0 +#define DESTROY_SHTABLE(s) DESTROY_ESTACK(s) +#define SHTABLE_INCR 4 +#define SHTABLE_NEXT(s) ESTK_CONCAT(s,_offset) +#define SHTABLE_PUSH(s,x,y,b) \ +do { \ + if (s.sp > s.end - SHTABLE_INCR) { \ + erl_grow_estack(&s, ESTK_DEF_STACK(s)); \ + } \ + *s.sp++ = (x); \ + *s.sp++ = (y); \ + *s.sp++ = (Eterm) NULL; \ + *s.sp++ = (Eterm) (b); /* bad in HALF_WORD */ \ + ESTK_CONCAT(s,_offset) += SHTABLE_INCR; \ +} while(0) +#define SHTABLE_X(s,e) (s.start[e]) +#define SHTABLE_Y(s,e) (s.start[(e)+1]) +#define SHTABLE_FWD(s,e) ((Eterm *) (s.start[(e)+2])) +#define SHTABLE_FWD_UPD(s,e,p) (s.start[(e)+2] = (Eterm) (p)) +#define SHTABLE_REV(s,e) ((Eterm *) (s.start[(e)+3])) + +#define LIST_SHARED_UNPROCESSED ((Eterm) 0) +#define LIST_SHARED_PROCESSED ((Eterm) 1) + +#define HEAP_ELEM_TO_BE_FILLED _unchecked_make_list(NULL) + + +/* + * Specialized macros for using/reusing the persistent state + */ + +#define DECLARE_EQUEUE_INIT_INFO(q, info) \ + UWord* EQUE_DEF_QUEUE(q) = info->queue_default; \ + ErtsEQueue q = { \ + EQUE_DEF_QUEUE(q), /* start */ \ + EQUE_DEF_QUEUE(q), /* front */ \ + EQUE_DEF_QUEUE(q), /* back */ \ + 1, /* possibly_empty */ \ + EQUE_DEF_QUEUE(q) + DEF_EQUEUE_SIZE, /* end */ \ + ERTS_ALC_T_ESTACK /* alloc_type */ \ + } + +#define DECLARE_EQUEUE_FROM_INFO(q, info) \ + /* no EQUE_DEF_QUEUE(q), read-only */ \ + ErtsEQueue q = { \ + info->queue_start, /* start */ \ + info->queue_start, /* front */ \ + info->queue_start, /* back */ \ + 1, /* possibly_empty */ \ + info->queue_end, /* end */ \ + info->queue_alloc_type /* alloc_type */ \ + } + +#define DECLARE_BITSTORE_INIT_INFO(s, info) \ + UWord* WSTK_DEF_STACK(s) = info->bitstore_default; \ + ErtsWStack s = { \ + WSTK_DEF_STACK(s), /* wstart */ \ + WSTK_DEF_STACK(s), /* wsp */ \ + WSTK_DEF_STACK(s) + DEF_WSTACK_SIZE, /* wend */ \ + ERTS_ALC_T_ESTACK /* alloc_type */ \ + }; \ + int WSTK_CONCAT(s,_bitoffs) = 0; \ + /* no WSTK_CONCAT(s,_offset), write-only */ \ + UWord WSTK_CONCAT(s,_buffer) = 0 + +#define DECLARE_BITSTORE_FROM_INFO(s, info) \ + /* no WSTK_DEF_STACK(s), read-only */ \ + ErtsWStack s = { \ + info->bitstore_start, /* wstart */ \ + NULL, /* wsp, read-only */ \ + NULL, /* wend, read-only */ \ + info->bitstore_alloc_type /* alloc_type */ \ + }; \ + int WSTK_CONCAT(s,_bitoffs) = 0; \ + int WSTK_CONCAT(s,_offset) = 0; \ + UWord WSTK_CONCAT(s,_buffer) = 0 + +#define DECLARE_SHTABLE_INIT_INFO(s, info) \ + Eterm* ESTK_DEF_STACK(s) = info->shtable_default; \ + ErtsEStack s = { \ + ESTK_DEF_STACK(s), /* start */ \ + ESTK_DEF_STACK(s), /* sp */ \ + ESTK_DEF_STACK(s) + DEF_ESTACK_SIZE, /* end */ \ + ERTS_ALC_T_ESTACK /* alloc_type */ \ + }; \ + Uint ESTK_CONCAT(s,_offset) = 0 + +#define DECLARE_SHTABLE_FROM_INFO(s, info) \ + /* no ESTK_DEF_STACK(s), read-only */ \ + ErtsEStack s = { \ + info->shtable_start, /* start */ \ + NULL, /* sp, read-only */ \ + NULL, /* end, read-only */ \ + info->shtable_alloc_type /* alloc_type */ \ + }; \ + /* no ESTK_CONCAT(s,_offset), read-only */ + +/* + * Copy object "obj" preserving sharing. + * First half: count size and calculate sharing. + * NOTE: We do not support HALF_WORD (yet?). + */ +Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) +{ + Uint sum; + Uint e; + unsigned sz; + Eterm* ptr; + Process* myself; + int force_local = flags & ERTS_SHCOPY_FLG_TMP_BUF; + + DECLARE_EQUEUE_INIT_INFO(s, info); + DECLARE_BITSTORE_INIT_INFO(b, info); + DECLARE_SHTABLE_INIT_INFO(t, info); + + /* step #0: + ------------------------------------------------------- + get rid of the easy cases first: + - copying constants + - if not a proper process, do flat copy + */ + + if (IS_CONST(obj)) + return 0; + + myself = erts_get_current_process(); + if (myself == NULL || (flags & ERTS_SHCOPY_FLG_NONE)) + return size_object(obj); + + VERBOSE_DEBUG("[pid=%T] copy_shared_calculate %p\n", myself->common.id, obj); + VERBOSE_DEBUG("[pid=%T] message is %T\n", myself->common.id, obj); + + /* step #1: + ------------------------------------------------------- + traverse the term and calculate the size; + when traversing, transform as you do in size_shared + but when you find shared objects: + + a. add entry in the table, indexed by i + b. mark them: + b1. boxed terms, set header to (i | 11) + store (old header, NONV, NULL, backptr) in the entry + b2. cons cells, set CDR to NONV, set CAR to i + store (old CAR, old CDR, NULL, backptr) in the entry + */ + + sum = 0; + + for (;;) { + VERBOSE_DEBUG("[copy] visiting: %x ", obj); + switch (primary_tag(obj)) { + case TAG_PRIMARY_LIST: { + Eterm head, tail; + VERBOSE_DEBUG("L"); + ptr = list_val_rel(obj, base); + /* off heap list pointers are copied verbatim */ + if (!INHEAP(myself, ptr)) { + VERBOSE_DEBUG("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj); + if (myself->mbuf != NULL) + VERBOSE_DEBUG("[pid=%T] BUT !!! there are message buffers!\n", myself->common.id); + VERBOSE_DEBUG("#"); + goto pop_next; + } + head = CAR(ptr); + tail = CDR(ptr); + /* if it's visited, don't count it; + if not already shared, make it shared and store it in the table */ + if (primary_tag(tail) == TAG_PRIMARY_HEADER || + primary_tag(head) == TAG_PRIMARY_HEADER) { + VERBOSE_DEBUG("!"); + if (tail != THE_NON_VALUE) { + e = SHTABLE_NEXT(t); + VERBOSE_DEBUG("[pid=%T] tabling L %p\n", myself->common.id, ptr); + SHTABLE_PUSH(t, head, tail, ptr); + CAR(ptr) = (e << _TAG_PRIMARY_SIZE) | LIST_SHARED_UNPROCESSED; + CDR(ptr) = THE_NON_VALUE; + } + goto pop_next; + } + /* else make it visited now */ + switch (primary_tag(tail)) { + case TAG_PRIMARY_LIST: + VERBOSE_DEBUG("/L"); + VERBOSE_DEBUG("[pid=%T] mangling L/L %p\n", myself->common.id, ptr); + CDR(ptr) = (tail - TAG_PRIMARY_LIST) | TAG_PRIMARY_HEADER; + break; + case TAG_PRIMARY_IMMED1: + VERBOSE_DEBUG("/I"); + VERBOSE_DEBUG("[pid=%T] mangling L/I %p\n", myself->common.id, ptr); + CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER; + CDR(ptr) = (tail - TAG_PRIMARY_IMMED1) | primary_tag(head); + break; + case TAG_PRIMARY_BOXED: + VERBOSE_DEBUG("/B saved %d", primary_tag(head)); + BITSTORE_PUT(b, primary_tag(head)); + VERBOSE_DEBUG("[pid=%T] mangling L/B %p\n", myself->common.id, ptr); + CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER; + CDR(ptr) = (tail - TAG_PRIMARY_BOXED) | TAG_PRIMARY_HEADER; + break; + } + /* and count it */ + sum += 2; + if (!IS_CONST(head)) { + EQUEUE_PUT(s, head); + } + obj = tail; + break; + } + case TAG_PRIMARY_BOXED: { + Eterm hdr; + VERBOSE_DEBUG("B"); + ptr = boxed_val_rel(obj, base); + /* off heap pointers to boxes are copied verbatim */ + if (!INHEAP(myself, ptr)) { + VERBOSE_DEBUG("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj); + VERBOSE_DEBUG("#"); + goto pop_next; + } + hdr = *ptr; + /* if it's visited, don't count it; + if not already shared, make it shared and store it in the table */ + if (primary_tag(hdr) != TAG_PRIMARY_HEADER) { + VERBOSE_DEBUG("!"); + if (primary_tag(hdr) == BOXED_VISITED) { + e = SHTABLE_NEXT(t); + VERBOSE_DEBUG("[pid=%T] tabling B %p\n", myself->common.id, ptr); + SHTABLE_PUSH(t, hdr, THE_NON_VALUE, ptr); + *ptr = (e << _TAG_PRIMARY_SIZE) | BOXED_SHARED_UNPROCESSED; + } + goto pop_next; + } + /* else make it visited now */ + VERBOSE_DEBUG("[pid=%T] mangling B %p\n", myself->common.id, ptr); + *ptr = (hdr - primary_tag(hdr)) + BOXED_VISITED; + /* and count it */ + ASSERT(is_header(hdr)); + switch (hdr & _TAG_HEADER_MASK) { + case ARITYVAL_SUBTAG: { + int arity = header_arity(hdr); + VERBOSE_DEBUG("/T"); + sum += arity + 1; + if (arity == 0) { /* Empty tuple -- unusual. */ + VERBOSE_DEBUG("e"); + goto pop_next; + } + while (arity-- > 0) { + obj = *++ptr; + if (!IS_CONST(obj)) { + EQUEUE_PUT(s, obj); + } + } + goto pop_next; + } + case FUN_SUBTAG: { + ErlFunThing* funp = (ErlFunThing *) ptr; + unsigned eterms = 1 /* creator */ + funp->num_free; + sz = thing_arityval(hdr); + VERBOSE_DEBUG("/F"); + sum += 1 /* header */ + sz + eterms; + ptr += 1 /* header */ + sz; + while (eterms-- > 0) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT(s, obj); + } + } + goto pop_next; + } + case SUB_BINARY_SUBTAG: { + ErlSubBin* sb = (ErlSubBin *) ptr; + Eterm real_bin = sb->orig; + Uint bit_offset = sb->bitoffs; + Uint bit_size = sb->bitsize; + size_t size = sb->size; + Uint extra_bytes; + Eterm hdr; + if (bit_size + bit_offset > 8) { + sum += ERL_SUB_BIN_SIZE; + extra_bytes = 2; + } else if (bit_size + bit_offset > 0) { + sum += ERL_SUB_BIN_SIZE; + extra_bytes = 1; + } else { + extra_bytes = 0; + } + ASSERT(is_boxed(rterm2wterm(real_bin, base)) && + (((*boxed_val(rterm2wterm(real_bin, base))) & + (_TAG_HEADER_MASK - _BINARY_XXX_MASK - BOXED_VISITED_MASK)) + == _TAG_HEADER_REFC_BIN)); + hdr = *_unchecked_binary_val(rterm2wterm(real_bin, base)) & ~BOXED_VISITED_MASK; + if (thing_subtag(hdr) == HEAP_BINARY_SUBTAG) { + sum += heap_bin_size(size+extra_bytes); + } else { + ASSERT(thing_subtag(hdr) == REFC_BINARY_SUBTAG); + sum += PROC_BIN_SIZE; + } + goto pop_next; + } + case BIN_MATCHSTATE_SUBTAG: + erl_exit(ERTS_ABORT_EXIT, + "size_shared: matchstate term not allowed"); + default: + VERBOSE_DEBUG("/D"); + sum += thing_arityval(hdr) + 1; + goto pop_next; + } + break; + } + case TAG_PRIMARY_IMMED1: + VERBOSE_DEBUG("I"); + pop_next: + if (EQUEUE_ISEMPTY(s)) { + VERBOSE_DEBUG("\n"); + // add sentinel to the table + SHTABLE_PUSH(t, THE_NON_VALUE, THE_NON_VALUE, NULL); + // store persistent info + BITSTORE_CLOSE(b); + info->queue_start = s.start; + info->queue_end = s.end; + info->queue_alloc_type = s.alloc_type; + info->bitstore_start = b.wstart; + info->bitstore_alloc_type = b.alloc_type; + info->shtable_start = t.start; + info->shtable_alloc_type = t.alloc_type; + // single point of return: the size of the object + VERBOSE_DEBUG("[pid=%T] size was: %u\n", myself->common.id, sum); + return sum; + } + obj = EQUEUE_GET(s); + break; + default: + erl_exit(ERTS_ABORT_EXIT, "[pid=%T] size_shared: bad tag for %#x\n", obj); + } + VERBOSE_DEBUG("\n"); + } +} + + +/* + * Copy object "obj" preserving sharing. + * Second half: copy and restore the object. + * NOTE: We do not support HALF_WORD (yet?). + */ +Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, ErlOffHeap* off_heap, unsigned flags) +{ + Uint e; + unsigned sz; + Eterm* ptr; + Eterm* hp; + Eterm* hscan; + Eterm result; + Eterm* resp; + unsigned remaining; + Process* myself; + int force_local = flags & ERTS_SHCOPY_FLG_TMP_BUF; +#if defined(DEBUG) || defined(SHCOPY_DEBUG) + Eterm saved_obj = obj; +#endif + + DECLARE_EQUEUE_FROM_INFO(s, info); + DECLARE_BITSTORE_FROM_INFO(b, info); + DECLARE_SHTABLE_FROM_INFO(t, info); + + /* step #0: + ------------------------------------------------------- + get rid of the easy cases first: + - copying constants + - if not a proper process, do flat copy + */ + + if (IS_CONST(obj)) + return obj; + + myself = erts_get_current_process(); + if (myself == NULL || (flags & ERTS_SHCOPY_FLG_NONE)) + return copy_struct(obj, size, hpp, off_heap); + + VERBOSE_DEBUG("[pid=%T] copy_shared_perform %p\n", myself->common.id, obj); + + /* step #2: was performed before this function was called + ------------------------------------------------------- + allocate new space + */ + + hscan = hp = *hpp; + + /* step #3: + ------------------------------------------------------- + traverse the term a second time and when traversing: + a. if the object is marked as shared + a1. if the entry contains a forwarding ptr, use that + a2. otherwise, copy it to the new space and store the + forwarding ptr to the entry + b. otherwise, reverse-transform as you do in size_shared + and copy to the new space + */ + + resp = &result; + remaining = 0; + for (;;) { + VERBOSE_DEBUG("[copy] revisiting: %x ", obj); + switch (primary_tag(obj)) { + case TAG_PRIMARY_LIST: { + Eterm head, tail; + VERBOSE_DEBUG("L"); + ptr = list_val_rel(obj, base); + /* off heap list pointers are copied verbatim */ + if (!INHEAP(myself, ptr)) { + VERBOSE_DEBUG("#"); + *resp = obj; + goto cleanup_next; + } + head = CAR(ptr); + tail = CDR(ptr); + /* if it is shared */ + if (tail == THE_NON_VALUE) { + e = head >> _TAG_PRIMARY_SIZE; + /* if it has been processed, just use the forwarding pointer */ + if (primary_tag(head) == LIST_SHARED_PROCESSED) { + VERBOSE_DEBUG("!"); + *resp = make_list(SHTABLE_FWD(t, e)); + goto cleanup_next; + } + /* else, let's process it now, + copy it and keep the forwarding pointer */ + else { + VERBOSE_DEBUG("$"); + CAR(ptr) = (head - primary_tag(head)) + LIST_SHARED_PROCESSED; + head = SHTABLE_X(t, e); + tail = SHTABLE_Y(t, e); + ptr = &(SHTABLE_X(t, e)); + VERBOSE_DEBUG("[pid=%T] tabled L %p is %p\n", myself->common.id, ptr, SHTABLE_REV(t, e)); + SHTABLE_FWD_UPD(t, e, hp); + } + } + /* if not already clean, clean it up and copy it */ + if (primary_tag(tail) == TAG_PRIMARY_HEADER) { + if (primary_tag(head) == TAG_PRIMARY_HEADER) { + Eterm saved = BITSTORE_GET(b); + VERBOSE_DEBUG("/B restoring %d", saved); + VERBOSE_DEBUG("[pid=%T] unmangling L/B %p\n", myself->common.id, ptr); + CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) + saved; + CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_BOXED; + } else { + VERBOSE_DEBUG("/L"); + VERBOSE_DEBUG("[pid=%T] unmangling L/L %p\n", myself->common.id, ptr); + CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_LIST; + } + } else if (primary_tag(head) == TAG_PRIMARY_HEADER) { + VERBOSE_DEBUG("/I"); + VERBOSE_DEBUG("[pid=%T] unmangling L/I %p\n", myself->common.id, ptr); + CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | primary_tag(tail); + CDR(ptr) = tail = (tail - primary_tag(tail)) | TAG_PRIMARY_IMMED1; + } else { + ASSERT(0 && "cannot come here"); + goto cleanup_next; + } + /* and its children too */ + if (IS_CONST(head)) { + CAR(hp) = head; + } else { + EQUEUE_PUT_UNCHECKED(s, head); + CAR(hp) = HEAP_ELEM_TO_BE_FILLED; + } + *resp = make_list(hp); + resp = &(CDR(hp)); + hp += 2; + obj = tail; + break; + } + case TAG_PRIMARY_BOXED: { + Eterm hdr; + VERBOSE_DEBUG("B"); + ptr = boxed_val_rel(obj, base); + /* off heap pointers to boxes are copied verbatim */ + if (!INHEAP(myself, ptr)) { + VERBOSE_DEBUG("#"); + *resp = obj; + goto cleanup_next; + } + hdr = *ptr; + /* clean it up, unless it's already clean or shared and processed */ + switch (primary_tag(hdr)) { + case TAG_PRIMARY_HEADER: + ASSERT(0 && "cannot come here"); + /* if it is shared and has been processed, + just use the forwarding pointer */ + case BOXED_SHARED_PROCESSED: + VERBOSE_DEBUG("!"); + e = hdr >> _TAG_PRIMARY_SIZE; + *resp = make_boxed(SHTABLE_FWD(t, e)); + goto cleanup_next; + /* if it is shared but has not been processed yet, let's process + it now: copy it and keep the forwarding pointer */ + case BOXED_SHARED_UNPROCESSED: + e = hdr >> _TAG_PRIMARY_SIZE; + VERBOSE_DEBUG("$"); + *ptr = (hdr - primary_tag(hdr)) + BOXED_SHARED_PROCESSED; + hdr = SHTABLE_X(t, e); + ASSERT(primary_tag(hdr) == BOXED_VISITED); + VERBOSE_DEBUG("[pid=%T] tabled B %p is %p\n", myself->common.id, ptr, SHTABLE_REV(t, e)); + VERBOSE_DEBUG("[pid=%T] unmangling B %p\n", myself->common.id, ptr); + SHTABLE_X(t, e) = hdr = (hdr - BOXED_VISITED) + TAG_PRIMARY_HEADER; + SHTABLE_FWD_UPD(t, e, hp); + break; + case BOXED_VISITED: + VERBOSE_DEBUG("[pid=%T] unmangling B %p\n", myself->common.id, ptr); + *ptr = hdr = (hdr - BOXED_VISITED) + TAG_PRIMARY_HEADER; + break; + } + /* and its children too */ + switch (hdr & _TAG_HEADER_MASK) { + case ARITYVAL_SUBTAG: { + int arity = header_arity(hdr); + *resp = make_boxed(hp); + *hp++ = hdr; + while (arity-- > 0) { + obj = *++ptr; + if (IS_CONST(obj)) { + *hp++ = obj; + } else { + EQUEUE_PUT_UNCHECKED(s, obj); + *hp++ = HEAP_ELEM_TO_BE_FILLED; + } + } + goto cleanup_next; + } + case FUN_SUBTAG: { + ErlFunThing* funp = (ErlFunThing *) ptr; + unsigned eterms = 1 /* creator */ + funp->num_free; + sz = thing_arityval(hdr); + funp = (ErlFunThing *) hp; + *resp = make_fun(hp); + *hp++ = hdr; + ptr++; + while (sz-- > 0) { + *hp++ = *ptr++; + } + while (eterms-- > 0) { + obj = *ptr++; + if (IS_CONST(obj)) { + *hp++ = obj; + } else { + EQUEUE_PUT_UNCHECKED(s, obj); + *hp++ = HEAP_ELEM_TO_BE_FILLED; + } + } + funp->next = off_heap->first; + off_heap->first = (struct erl_off_heap_header*) funp; + erts_refc_inc(&funp->fe->refc, 2); + goto cleanup_next; + } + case REFC_BINARY_SUBTAG: { + ProcBin* pb = (ProcBin *) ptr; + sz = thing_arityval(hdr); + if (pb->flags) { + erts_emasculate_writable_binary(pb); + } + pb = (ProcBin *) hp; + *resp = make_binary(hp); + *hp++ = hdr; + ptr++; + while (sz-- > 0) { + *hp++ = *ptr++; + } + erts_refc_inc(&pb->val->refc, 2); + pb->next = off_heap->first; + pb->flags = 0; + off_heap->first = (struct erl_off_heap_header*) pb; + OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm)); + goto cleanup_next; + } + case SUB_BINARY_SUBTAG: { + ErlSubBin* sb = (ErlSubBin *) ptr; + Eterm real_bin = sb->orig; + Uint bit_offset = sb->bitoffs; + Uint bit_size = sb->bitsize; + Uint offset = sb->offs; + size_t size = sb->size; + Uint extra_bytes; + Uint real_size; + if ((bit_size + bit_offset) > 8) { + extra_bytes = 2; + } else if ((bit_size + bit_offset) > 0) { + extra_bytes = 1; + } else { + extra_bytes = 0; + } + real_size = size+extra_bytes; + ASSERT(is_boxed(rterm2wterm(real_bin, base)) && + (((*boxed_val(rterm2wterm(real_bin, base))) & + (_TAG_HEADER_MASK - _BINARY_XXX_MASK - BOXED_VISITED_MASK)) + == _TAG_HEADER_REFC_BIN)); + ptr = _unchecked_binary_val(rterm2wterm(real_bin, base)); + *resp = make_binary(hp); + if (extra_bytes != 0) { + ErlSubBin* res = (ErlSubBin *) hp; + hp += ERL_SUB_BIN_SIZE; + res->thing_word = HEADER_SUB_BIN; + res->size = size; + res->bitsize = bit_size; + res->bitoffs = bit_offset; + res->offs = 0; + res->is_writable = 0; + res->orig = make_binary(hp); + } + if (thing_subtag(*ptr & ~BOXED_VISITED_MASK) == HEAP_BINARY_SUBTAG) { + ErlHeapBin* from = (ErlHeapBin *) ptr; + ErlHeapBin* to = (ErlHeapBin *) hp; + hp += heap_bin_size(real_size); + to->thing_word = header_heap_bin(real_size); + to->size = real_size; + sys_memcpy(to->data, ((byte *)from->data)+offset, real_size); + } else { + ProcBin* from = (ProcBin *) ptr; + ProcBin* to = (ProcBin *) hp; + ASSERT(thing_subtag(*ptr & ~BOXED_VISITED_MASK) == REFC_BINARY_SUBTAG); + if (from->flags) { + erts_emasculate_writable_binary(from); + } + hp += PROC_BIN_SIZE; + to->thing_word = HEADER_PROC_BIN; + to->size = real_size; + to->val = from->val; + erts_refc_inc(&to->val->refc, 2); + to->bytes = from->bytes + offset; + to->next = off_heap->first; + to->flags = 0; + off_heap->first = (struct erl_off_heap_header*) to; + OH_OVERHEAD(off_heap, to->size / sizeof(Eterm)); + } + goto cleanup_next; + } + case EXTERNAL_PID_SUBTAG: + case EXTERNAL_PORT_SUBTAG: + case EXTERNAL_REF_SUBTAG: { + ExternalThing *etp = (ExternalThing *) hp; + sz = thing_arityval(hdr); + *resp = make_external(hp); + *hp++ = hdr; + ptr++; + while (sz-- > 0) { + *hp++ = *ptr++; + } + etp->next = off_heap->first; + off_heap->first = (struct erl_off_heap_header*) etp; + erts_refc_inc(&etp->node->refc, 2); + goto cleanup_next; + } + default: + sz = thing_arityval(hdr); + *resp = make_boxed(hp); + *hp++ = hdr; + ptr++; + while (sz-- > 0) { + *hp++ = *ptr++; + } + goto cleanup_next; + } + break; + } + case TAG_PRIMARY_IMMED1: + *resp = obj; + cleanup_next: + if (EQUEUE_ISEMPTY(s)) { + goto all_clean; + } + obj = EQUEUE_GET(s); + for (;;) { + ASSERT(hscan < hp); + if (remaining == 0) { + if (*hscan == HEAP_ELEM_TO_BE_FILLED) { + resp = hscan; + hscan += 2; + break; /* scanning loop */ + } else if (primary_tag(*hscan) == TAG_PRIMARY_HEADER) { + switch (*hscan & _TAG_HEADER_MASK) { + case ARITYVAL_SUBTAG: + remaining = header_arity(*hscan); + hscan++; + break; + case FUN_SUBTAG: { + ErlFunThing* funp = (ErlFunThing *) hscan; + hscan += 1 + thing_arityval(*hscan); + remaining = 1 + funp->num_free; + break; + } + case SUB_BINARY_SUBTAG: + ASSERT(((ErlSubBin *) hscan)->bitoffs + + ((ErlSubBin *) hscan)->bitsize > 0); + hscan += ERL_SUB_BIN_SIZE; + break; + default: + hscan += 1 + thing_arityval(*hscan); + break; + } + } else { + hscan++; + } + } else if (*hscan == HEAP_ELEM_TO_BE_FILLED) { + resp = hscan++; + remaining--; + break; /* scanning loop */ + } else { + hscan++; + remaining--; + } + } + ASSERT(resp < hp); + break; + default: + erl_exit(ERTS_ABORT_EXIT, "size_shared: bad tag for %#x\n", obj); + } + VERBOSE_DEBUG("\n"); + } + + /* step #4: + ------------------------------------------------------- + traverse the table and reverse-transform all stored entries + */ + +all_clean: + VERBOSE_DEBUG("\n"); + for (e = 0; ; e += SHTABLE_INCR) { + ptr = SHTABLE_REV(t, e); + if (ptr == NULL) + break; + VERBOSE_DEBUG("[copy] restoring shared: %x\n", ptr); + /* entry was a list */ + if (SHTABLE_Y(t, e) != THE_NON_VALUE) { + VERBOSE_DEBUG("[pid=%T] untabling L %p\n", myself->common.id, ptr); + CAR(ptr) = SHTABLE_X(t, e); + CDR(ptr) = SHTABLE_Y(t, e); + } + /* entry was boxed */ + else { + VERBOSE_DEBUG("[pid=%T] untabling B %p\n", myself->common.id, ptr); + *ptr = SHTABLE_X(t, e); + ASSERT(primary_tag(*ptr) == TAG_PRIMARY_HEADER); + } + } + +#ifdef DEBUG + if (eq(saved_obj, result) == 0) { + erts_fprintf(stderr, "original = %T\n", saved_obj); + erts_fprintf(stderr, "copy = %T\n", result); + erl_exit(ERTS_ABORT_EXIT, "copy (shared) not equal to source\n"); + } +#endif + + VERBOSE_DEBUG("[pid=%T] original was %T\n", myself->common.id, saved_obj); + VERBOSE_DEBUG("[pid=%T] copy is %T\n", myself->common.id, result); + VERBOSE_DEBUG("[pid=%T] result is at %p\n", myself->common.id, result); + + ASSERT(hp == *hpp + size); + *hpp = hp; + return result; +} + + /* * Copy a term that is guaranteed to be contained in a single * heap block. The heap block is copied word by word, and any diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 6cb37752bc..ec96a7563a 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -42,6 +42,8 @@ #include "dtrace-wrapper.h" #include "erl_bif_unique.h" +#undef SHCOPY_DEBUG + #define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1 #define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20 #define ERTS_INACT_WR_PB_LEAVE_LIMIT 10 @@ -1166,6 +1168,10 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end, char* oh = (char *) OLD_HEAP(p); Uint oh_size = (char *) OLD_HTOP(p) - oh; +#ifdef SHCOPY_DEBUG + VERBOSE_DEBUG("[pid=%T] MINOR GC: %p %p %p %p\n", p->common.id, HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p)); +#endif + n_htop = n_heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*new_sz); @@ -1383,6 +1389,10 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end, Uint new_sz, stk_sz; int adjusted; +#ifdef SHCOPY_DEBUG + VERBOSE_DEBUG("[pid=%T] MAJOR GC: %p %p %p %p\n", p->common.id, HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p)); +#endif + /* * Do a fullsweep GC. First figure out the size of the heap * to receive all live data. diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 20f100b427..d99f6548f9 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -919,8 +919,9 @@ do { \ #define EQUEUE_ISEMPTY(q) (q.back == q.front && q.possibly_empty) #define EQUEUE_GET(q) ({ \ + UWord x; \ q.possibly_empty = 1; \ - UWord x = *(q.front); \ + x = *(q.front); \ if (++(q.front) == q.end) { \ q.front = q.start; \ } \ @@ -1035,11 +1036,64 @@ __decl_noreturn void __noreturn erl_exit(int n, char*, ...); __decl_noreturn void __noreturn erl_exit_flush_async(int n, char*, ...); void erl_error(char*, va_list); +/* This controls whether sharing-preserving copy is used by Erlang */ + +#define SHCOPY_SEND +#define SHCOPY_SPAWN + +#if defined(SHCOPY_SEND) \ + || defined(SHCOPY_SPAWN) +#define SHCOPY +/* Use this with care, it is *very* verbose! */ +#undef SHCOPY_DEBUG +#endif + +#define VERBOSE_DEBUG(...) do { \ + erts_fprintf(stderr, __VA_ARGS__); \ + } while(0) + +#define ERTS_SHCOPY_FLG_MASK (((unsigned) 3) << 0) +#define ERTS_SHCOPY_FLG_NONE (((unsigned) 1) << 0) +#define ERTS_SHCOPY_FLG_TMP_BUF (((unsigned) 1) << 1) + +/* The persistent state while the sharing-preserving copier works */ + +typedef struct shcopy_info { + Eterm queue_default[DEF_EQUEUE_SIZE]; + Eterm* queue_start; + Eterm* queue_end; + ErtsAlcType_t queue_alloc_type; + UWord bitstore_default[DEF_WSTACK_SIZE]; + UWord* bitstore_start; + ErtsAlcType_t bitstore_alloc_type; + Eterm shtable_default[DEF_ESTACK_SIZE]; + Eterm* shtable_start; + ErtsAlcType_t shtable_alloc_type; +} shcopy_info; + +#define DESTROY_INFO(info) \ +do { \ + if (info.queue_start != info.queue_default) { \ + erts_free(info.queue_alloc_type, info.queue_start); \ + } \ + if (info.bitstore_start != info.bitstore_default) { \ + erts_free(info.bitstore_alloc_type, info.bitstore_start); \ + } \ + if (info.shtable_start != info.shtable_default) { \ + erts_free(info.shtable_alloc_type, info.shtable_start); \ + } \ +} while(0) + /* copy.c */ Eterm copy_object_x(Eterm, Process*, Uint); #define copy_object(Term, Proc) copy_object_x(Term,Proc,0) Uint size_object(Eterm); +Uint copy_shared_calculate(Eterm, shcopy_info*, unsigned); +Eterm copy_shared_perform(Eterm, Uint, shcopy_info*, Eterm**, ErlOffHeap*, unsigned); + +Uint size_shared(Eterm); + Eterm copy_struct(Eterm, Uint, Eterm**, ErlOffHeap*); Eterm copy_shallow(Eterm*, Uint, Eterm**, ErlOffHeap*); -- cgit v1.2.3 From cfe998988c942bed0fbf026c00a2531fdf5aba7c Mon Sep 17 00:00:00 2001 From: "Nikolaos S. Papaspyrou" Date: Sat, 9 Jun 2012 00:19:28 +0300 Subject: Add the BIF size_shared/1 and debug cleanup --- erts/emulator/beam/beam_debug.c | 14 ++++++++++++++ erts/emulator/beam/bif.tab | 12 +++++++----- erts/emulator/beam/erl_gc.c | 2 -- erts/emulator/beam/global.h | 4 ++++ 4 files changed, 25 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index e989310789..40e3f4db4e 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -73,6 +73,20 @@ erts_debug_flat_size_1(BIF_ALIST_1) } } +BIF_RETTYPE +erts_debug_size_shared_1(BIF_ALIST_1) +{ + Process* p = BIF_P; + Eterm term = BIF_ARG_1; + Uint size = size_shared(term); + + if (IS_USMALL(0, size)) { + BIF_RET(make_small(size)); + } else { + Eterm* hp = HAlloc(p, BIG_UINT_HEAP_SIZE); + BIF_RET(uint_to_big(size, hp)); + } +} BIF_RETTYPE erts_debug_breakpoint_2(BIF_ALIST_2) diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 65f8d6f1f5..f45f886395 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -639,14 +639,16 @@ bif ets:update_counter/4 bif erts_debug:map_info/1 # -# Obsolete +# New in 19.0 # -bif erlang:hash/2 +bif binary:split/2 +bif binary:split/3 +bif erts_debug:size_shared/1 +bif 'erl.system.debug':size_shared/1 ebif_erts_debug_size_shared_1 # -# New in 19.0 +# Obsolete # -bif binary:split/2 -bif binary:split/3 +bif erlang:hash/2 diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index ec96a7563a..1cf6509012 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -42,8 +42,6 @@ #include "dtrace-wrapper.h" #include "erl_bif_unique.h" -#undef SHCOPY_DEBUG - #define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1 #define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20 #define ERTS_INACT_WR_PB_LEAVE_LIMIT 10 diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index d99f6548f9..c2056dafaa 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1048,9 +1048,13 @@ void erl_error(char*, va_list); #undef SHCOPY_DEBUG #endif +#ifdef SHCOPY_DEBUG #define VERBOSE_DEBUG(...) do { \ erts_fprintf(stderr, __VA_ARGS__); \ } while(0) +#else +#define VERBOSE_DEBUG(...) +#endif #define ERTS_SHCOPY_FLG_MASK (((unsigned) 3) << 0) #define ERTS_SHCOPY_FLG_NONE (((unsigned) 1) << 0) -- cgit v1.2.3 From f3ae60838a5e8c83cfdd3000e25562dfcbbd438d Mon Sep 17 00:00:00 2001 From: "Nikolaos S. Papaspyrou" Date: Sat, 9 Jun 2012 01:12:50 +0300 Subject: Enable shcopy for sending messages --- erts/emulator/beam/erl_db.c | 4 +- erts/emulator/beam/erl_message.c | 114 +++++++++++++++++++++++++-------------- erts/emulator/beam/erl_message.h | 5 ++ erts/emulator/beam/global.h | 7 +++ 4 files changed, 87 insertions(+), 43 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 9ec14ab5ae..6119a9225f 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -1832,7 +1832,7 @@ BIF_RETTYPE ets_give_away_3(BIF_ALIST_3) tb->common.id, from_pid, BIF_ARG_3), - 0); + ERTS_SND_FLG_SHCOPY_TMP_BUF); erts_smp_proc_unlock(to_proc, to_locks); UnUseTmpHeap(5,BIF_P); BIF_RET(am_true); @@ -3211,7 +3211,7 @@ retry: tb->common.id, p->common.id, heir_data), - 0); + ERTS_SND_FLG_SHCOPY_TMP_BUF); erts_smp_proc_unlock(to_proc, to_locks); return !0; } diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 79739501a8..b1fca1df0c 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -68,6 +68,9 @@ new_message_buffer(Uint size) bp = (ErlHeapFragment*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP_FRAG, ERTS_HEAP_FRAG_SIZE(size)); ERTS_INIT_HEAP_FRAG(bp, size, size); +#ifdef SHCOPY_DEBUG + VERBOSE_DEBUG("[pid=%T] new message buffer %p\n", erts_get_current_pid(), bp->mem); +#endif return bp; } @@ -248,36 +251,6 @@ erts_realloc_shrink_message(ErtsMessage *mp, Uint sz, Eterm *brefs, Uint brefs_s return nmp; } -void -erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *first_bp) -{ - if (first_bp) { - ErlHeapFragment *bp = first_bp; - - while (1) { - /* Move any off_heap's into the process */ - if (bp->off_heap.first != NULL) { - struct erl_off_heap_header** next_p = &bp->off_heap.first; - while (*next_p != NULL) { - next_p = &((*next_p)->next); - } - *next_p = MSO(proc).first; - MSO(proc).first = bp->off_heap.first; - bp->off_heap.first = NULL; - OH_OVERHEAD(&(MSO(proc)), bp->off_heap.overhead); - } - MBUF_SIZE(proc) += bp->used_size; - if (!bp->next) - break; - bp = bp->next; - } - - /* Link the message buffer */ - bp->next = MBUF(proc); - MBUF(proc) = first_bp; - } -} - void erts_queue_dist_message(Process *rcvr, ErtsProcLocks *rcvr_locks, @@ -540,6 +513,36 @@ erts_queue_message(Process* receiver, ErtsProcLocks *receiver_locks, ); } +void +erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *first_bp) +{ + if (first_bp) { + ErlHeapFragment *bp = first_bp; + + while (1) { + /* Move any off_heap's into the process */ + if (bp->off_heap.first != NULL) { + struct erl_off_heap_header** next_p = &bp->off_heap.first; + while (*next_p != NULL) { + next_p = &((*next_p)->next); + } + *next_p = MSO(proc).first; + MSO(proc).first = bp->off_heap.first; + bp->off_heap.first = NULL; + OH_OVERHEAD(&(MSO(proc)), bp->off_heap.overhead); + } + MBUF_SIZE(proc) += bp->used_size; + if (!bp->next) + break; + bp = bp->next; + } + + /* Link the message buffer */ + bp->next = MBUF(proc); + MBUF(proc) = first_bp; + } +} + Uint erts_msg_attached_data_size_aux(ErtsMessage *msg) { @@ -663,11 +666,15 @@ erts_send_message(Process* sender, Eterm utag = NIL; #endif erts_aint32_t receiver_state; +#ifdef SHCOPY_SEND + unsigned shflags = (flags & ERTS_SND_FLG_SHCOPY_MASK) >> ERTS_SND_FLG_SHCOPY_SHIFT; + erts_shcopy_t info; +#endif BM_STOP_TIMER(system); BM_MESSAGE(message,sender,receiver); BM_START_TIMER(send); - #ifdef USE_VM_PROBES +#ifdef USE_VM_PROBES *sender_name = *receiver_name = '\0'; if (DTRACE_ENABLED(message_send)) { erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), @@ -686,15 +693,23 @@ erts_send_message(Process* sender, #ifdef USE_VM_PROBES Uint dt_utag_size = 0; #endif - +#ifdef SHCOPY_SEND + unsigned shflags = (flags & ERTS_SND_FLG_SHCOPY_MASK) >> ERTS_SND_FLG_SHCOPY_SHIFT; + erts_shcopy_t info; + INITIALIZE_SHCOPY(info); +#endif BM_SWAP_TIMER(send,size); - msize = size_object(message); +#ifdef SHCOPY_SEND + INITIALIZE_SHCOPY(info); + msize = copy_shared_calculate(message, &info, shflags); +#else + msize = size_object(message); +#endif BM_SWAP_TIMER(size,send); #ifdef USE_VM_PROBES if (stoken != am_have_dt_utag) { #endif - seq_trace_update_send(sender); seq_trace_output(stoken, message, SEQ_TRACE_SEND, receiver->common.id, sender); @@ -720,14 +735,20 @@ erts_send_message(Process* sender, &ohp); BM_SWAP_TIMER(send,copy); + +#ifdef SHCOPY_SEND + if (is_not_immed(message)) + message = copy_shared_perform(message, msize, &info, &hp, ohp, shflags); + DESTROY_SHCOPY(info); +#else + if (is_not_immed(message)) + message = copy_struct(message, msize, &hp, ohp); +#endif if (is_immed(stoken)) token = stoken; else token = copy_struct(stoken, seq_trace_size, &hp, ohp); - if (is_not_immed(message)) - message = copy_struct(message, msize, &hp, ohp); - #ifdef USE_VM_PROBES if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) { if (is_immed(DT_UTAG(sender))) @@ -764,7 +785,12 @@ erts_send_message(Process* sender, } else { BM_SWAP_TIMER(send,size); - msize = size_object(message); +#ifdef SHCOPY_SEND + INITIALIZE_SHCOPY(info); + msize = copy_shared_calculate(message, &info, shflags); +#else + msize = size_object(message); +#endif BM_SWAP_TIMER(size,send); mp = erts_alloc_message_heap_state(receiver, @@ -774,8 +800,14 @@ erts_send_message(Process* sender, &hp, &ohp); BM_SWAP_TIMER(send,copy); - if (is_not_immed(message)) - message = copy_struct(message, msize, &hp, ohp); +#ifdef SHCOPY_SEND + if (is_not_immed(message)) + message = copy_shared_perform(message, msize, &info, &hp, ohp, shflags); + DESTROY_SHCOPY(info); +#else + if (is_not_immed(message)) + message = copy_struct(message, msize, &hp, ohp); +#endif BM_MESSAGE_COPIED(msz); BM_SWAP_TIMER(copy,send); } @@ -800,6 +832,7 @@ erts_send_message(Process* sender, return res; } + /* * This function delivers an EXIT message to a process * which is trapping EXITs. @@ -1725,4 +1758,3 @@ void erts_factory_undo(ErtsHeapFactory* factory) factory->heap_frags = NULL; #endif } - diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 740ae46a0f..0de36c3199 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -238,6 +238,11 @@ do { \ #define ERTS_SND_FLG_NO_SEQ_TRACE (((unsigned) 1) << 0) +#define ERTS_SND_FLG_SHCOPY_SHIFT 1 +#define ERTS_SND_FLG_SHCOPY_MASK (ERTS_SHCOPY_FLG_MASK << ERTS_SND_FLG_SHCOPY_SHIFT) +#define ERTS_SND_FLG_SHCOPY_NONE (ERTS_SHCOPY_FLG_NONE << ERTS_SND_FLG_SHCOPY_SHIFT) +#define ERTS_SND_FLG_SHCOPY_TMP_BUF (ERTS_SHCOPY_FLG_TMP_BUF << ERTS_SND_FLG_SHCOPY_SHIFT) + #define ERTS_HEAP_FRAG_SIZE(DATA_WORDS) \ (sizeof(ErlHeapFragment) - sizeof(Eterm) + (DATA_WORDS)*sizeof(Eterm)) diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index c2056dafaa..a5e6ff6f1c 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1075,6 +1075,13 @@ typedef struct shcopy_info { ErtsAlcType_t shtable_alloc_type; } shcopy_info; +#define INITIALIZE_INFO(info) \ +do { \ + info.queue_start = info.queue_default; \ + info.bitstore_start = info.bitstore_default; \ + info.shtable_start = info.shtable_default; \ +} while(0) + #define DESTROY_INFO(info) \ do { \ if (info.queue_start != info.queue_default) { \ -- cgit v1.2.3 From 7141fa8e4534ab7e4dcc3cad6c46872efa0e6e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 Nov 2015 14:44:52 +0100 Subject: Remove DTrace Harddebug clutter --- erts/emulator/beam/beam_emu.c | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 208a16dfd0..af97cba61c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1914,20 +1914,7 @@ void process_main(void) if (DT_UTAG(c_p) != NIL) { if (DT_UTAG_FLAGS(c_p) & DT_UTAG_PERMANENT) { SEQ_TRACE_TOKEN(c_p) = am_have_dt_utag; -#ifdef DTRACE_TAG_HARDDEBUG - if (DT_UTAG_FLAGS(c_p) & DT_UTAG_SPREADING) - erts_fprintf(stderr, - "Dtrace -> (%T) stop spreading " - "tag %T with message %T\r\n", - c_p->common.id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp)); -#endif } else { -#ifdef DTRACE_TAG_HARDDEBUG - erts_fprintf(stderr, - "Dtrace -> (%T) kill tag %T with " - "message %T\r\n", - c_p->common.id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp)); -#endif DT_UTAG(c_p) = NIL; SEQ_TRACE_TOKEN(c_p) = NIL; } @@ -1947,12 +1934,6 @@ void process_main(void) DT_UTAG(c_p) = ERL_MESSAGE_DT_UTAG(msgp); } DT_UTAG_FLAGS(c_p) |= DT_UTAG_SPREADING; -#ifdef DTRACE_TAG_HARDDEBUG - erts_fprintf(stderr, - "Dtrace -> (%T) receive tag (%T) " - "with message %T\r\n", - c_p->common.id, DT_UTAG(c_p), ERL_MESSAGE_TERM(msgp)); -#endif } else { #endif ASSERT(is_tuple(SEQ_TRACE_TOKEN(c_p))); -- cgit v1.2.3 From 8d02ad60c88aa060ff83ff3179bc8c0ab66868ee Mon Sep 17 00:00:00 2001 From: "Nikolaos S. Papaspyrou" Date: Fri, 14 Mar 2014 11:32:46 +0200 Subject: Add machinery to enable SHCOPY dynamically This commit is just for debugging purposes, will probably be reverted. It comes with a the erts_debug:copy_shared/1 BIF. If SHCOPY_DISABLE is defined, SHCOPY starts disabled and is dynamically enabled the first time that the BIF is called. --- erts/emulator/beam/beam_debug.c | 26 ++++++++++++++++++++++++++ erts/emulator/beam/copy.c | 12 ++++++++++++ erts/emulator/beam/global.h | 2 ++ 3 files changed, 40 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 40e3f4db4e..36f3cfabbc 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -88,6 +88,32 @@ erts_debug_size_shared_1(BIF_ALIST_1) } } +BIF_RETTYPE +erts_debug_copy_shared_1(BIF_ALIST_1) +{ + Process* p = BIF_P; + Eterm term = BIF_ARG_1; + Uint size; + Eterm* hp; + Eterm copy; + shcopy_info info; +#ifdef SHCOPY_DISABLE + extern int disable_copy_shared; +#endif + INITIALIZE_INFO(info); + + size = copy_shared_calculate(term, &info, 0); + if (size > 0) { + hp = HAlloc(p, size); + } + copy = copy_shared_perform(term, size, &info, &hp, &p->off_heap, 0); + DESTROY_INFO(info); +#ifdef SHCOPY_DISABLE + disable_copy_shared = 0; +#endif + BIF_RET(copy); +} + BIF_RETTYPE erts_debug_breakpoint_2(BIF_ALIST_2) { diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index b31e043f08..2566707717 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -901,6 +901,10 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) * Using an ESTACK but not very transparently; consider refactoring */ +#ifdef SHCOPY_DISABLE +int disable_copy_shared = ERTS_SHCOPY_FLG_NONE; +#endif + #define DECLARE_SHTABLE(s) \ DECLARE_ESTACK(s); \ Uint ESTK_CONCAT(s,_offset) = 0 @@ -1028,6 +1032,10 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) if (IS_CONST(obj)) return 0; +#ifdef SHCOPY_DISABLE + flags |= disable_copy_shared; +#endif + myself = erts_get_current_process(); if (myself == NULL || (flags & ERTS_SHCOPY_FLG_NONE)) return size_object(obj); @@ -1276,6 +1284,10 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E if (IS_CONST(obj)) return obj; +#ifdef SHCOPY_DISABLE + flags |= disable_copy_shared; +#endif + myself = erts_get_current_process(); if (myself == NULL || (flags & ERTS_SHCOPY_FLG_NONE)) return copy_struct(obj, size, hpp, off_heap); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index a5e6ff6f1c..3e1f3664bc 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1044,6 +1044,8 @@ void erl_error(char*, va_list); #if defined(SHCOPY_SEND) \ || defined(SHCOPY_SPAWN) #define SHCOPY +/* Use this if you want sharing-preserving copy to be initially disabled */ +#undef SHCOPY_DISABLE /* Use this with care, it is *very* verbose! */ #undef SHCOPY_DEBUG #endif -- cgit v1.2.3 From 244e9d5855d1b1f160d667b5cf369defee72829d Mon Sep 17 00:00:00 2001 From: "Nikolaos S. Papaspyrou" Date: Sat, 9 Jun 2012 01:37:29 +0300 Subject: Enable shcopy for spawning processes --- erts/emulator/beam/erl_process.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 9acce8acb6..f2da5289d3 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -10747,6 +10747,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). Eterm res = THE_NON_VALUE; erts_aint32_t state = 0; erts_aint32_t prio = (erts_aint32_t) PRIORITY_NORMAL; +#ifdef SHCOPY_SPAWN + unsigned shflags = 0; /* could be taken from so->flags, if necessary */ + shcopy_info info; + INITIALIZE_INFO(info); +#endif #ifdef ERTS_SMP erts_smp_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR); @@ -10798,7 +10803,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). BM_COUNT(processes_spawned); BM_SWAP_TIMER(system,size); +#ifdef SHCOPY_SPAWN + arg_size = copy_shared_calculate(args, &info, shflags); +#else arg_size = size_object(args); +#endif BM_SWAP_TIMER(size,system); heap_need = arg_size; @@ -10876,7 +10885,12 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). BM_MESSAGE(args,p,parent); BM_START_TIMER(system); BM_SWAP_TIMER(system,copy); +#ifdef SHCOPY_SPAWN + p->arg_reg[2] = copy_shared_perform(args, arg_size, &info, &p->htop, &p->off_heap, shflags); + DESTROY_INFO(info); +#else p->arg_reg[2] = copy_struct(args, arg_size, &p->htop, &p->off_heap); +#endif BM_MESSAGE_COPIED(arg_size); BM_SWAP_TIMER(copy,system); p->arity = 3; @@ -11260,6 +11274,8 @@ static void delete_process(Process* p) { VERBOSE(DEBUG_PROCESSES, ("Removing process: %T\n",p->common.id)); + VERBOSE_DEBUG("[pid=%T] delete process: %p %p %p %p\n", p->common.id, + HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p)); /* Cleanup psd */ -- cgit v1.2.3 From 277e8e77384ed6628009243e63d62f0555d10c69 Mon Sep 17 00:00:00 2001 From: "Nikolaos S. Papaspyrou" Date: Mon, 11 Jun 2012 15:17:01 +0300 Subject: Add -debug +vc flag for debuging SHCOPY This is very verbose, you have been warned. It should work with the copy-spy.py script, which may be a bit outdated. --- erts/emulator/beam/copy.c | 131 ++++++++++----------------------------- erts/emulator/beam/erl_debug.h | 1 + erts/emulator/beam/erl_gc.c | 10 ++- erts/emulator/beam/erl_init.c | 2 + erts/emulator/beam/erl_message.c | 4 +- erts/emulator/beam/erl_process.c | 4 +- erts/emulator/beam/global.h | 10 --- 7 files changed, 43 insertions(+), 119 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 2566707717..f74c6b1c89 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -75,16 +75,13 @@ Uint size_object(Eterm obj) Uint sum = 0; Eterm* ptr; int arity; -#ifdef SHCOPY_DEBUG - Eterm mypid; +#ifdef DEBUG + Eterm mypid = erts_get_current_pid(); #endif DECLARE_ESTACK(s); -#ifdef SHCOPY_DEBUG - mypid = erts_get_current_pid(); - VERBOSE_DEBUG("[pid=%T] size_object %p\n", mypid, obj); -#endif + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size_object %p\n", mypid, obj)); for (;;) { switch (primary_tag(obj)) { @@ -220,7 +217,7 @@ Uint size_object(Eterm obj) pop_next: if (ESTACK_ISEMPTY(s)) { DESTROY_ESTACK(s); - VERBOSE_DEBUG("[pid=%T] size was: %u\n", mypid, sum); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size was: %u\n", mypid, sum)); return sum; } obj = ESTACK_POP(s); @@ -320,11 +317,9 @@ Uint size_shared(Eterm obj) return size_object(obj); for (;;) { - VERBOSE_DEBUG("[size] visiting: %x ", obj); switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: { Eterm head, tail; - VERBOSE_DEBUG("L"); ptr = list_val_rel(obj, base); /* we're not counting anything that's outside our heap */ if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { @@ -335,22 +330,18 @@ Uint size_shared(Eterm obj) /* if it's visited, don't count it */ if (primary_tag(tail) == TAG_PRIMARY_HEADER || primary_tag(head) == TAG_PRIMARY_HEADER) { - VERBOSE_DEBUG("!"); goto pop_next; } /* else make it visited now */ switch (primary_tag(tail)) { case TAG_PRIMARY_LIST: - VERBOSE_DEBUG("/L"); ptr[1] = (tail - TAG_PRIMARY_LIST) | TAG_PRIMARY_HEADER; break; case TAG_PRIMARY_IMMED1: - VERBOSE_DEBUG("/I"); CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER; CDR(ptr) = (tail - TAG_PRIMARY_IMMED1) | primary_tag(head); break; case TAG_PRIMARY_BOXED: - VERBOSE_DEBUG("/B saved %d", primary_tag(head)); BITSTORE_PUT(b, primary_tag(head)); CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER; CDR(ptr) = (tail - TAG_PRIMARY_BOXED) | TAG_PRIMARY_HEADER; @@ -366,7 +357,6 @@ Uint size_shared(Eterm obj) } case TAG_PRIMARY_BOXED: { Eterm hdr; - VERBOSE_DEBUG("B"); ptr = boxed_val_rel(obj, base); /* we're not counting anything that's outside our heap */ if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { @@ -375,7 +365,6 @@ Uint size_shared(Eterm obj) hdr = *ptr; /* if it's visited, don't count it */ if (primary_tag(hdr) != TAG_PRIMARY_HEADER) { - VERBOSE_DEBUG("!"); goto pop_next; } /* else make it visited now */ @@ -385,10 +374,8 @@ Uint size_shared(Eterm obj) switch (hdr & _TAG_HEADER_MASK) { case ARITYVAL_SUBTAG: { int arity = header_arity(hdr); - VERBOSE_DEBUG("/T"); sum += arity + 1; if (arity == 0) { /* Empty tuple -- unusual. */ - VERBOSE_DEBUG("e"); goto pop_next; } while (arity-- > 0) { @@ -403,7 +390,6 @@ Uint size_shared(Eterm obj) ErlFunThing* funp = (ErlFunThing *) ptr; unsigned eterms = 1 /* creator */ + funp->num_free; unsigned sz = thing_arityval(hdr); - VERBOSE_DEBUG("/F"); sum += 1 /* header */ + sz + eterms; ptr += 1 /* header */ + sz; while (eterms-- > 0) { @@ -442,14 +428,12 @@ Uint size_shared(Eterm obj) erl_exit(ERTS_ABORT_EXIT, "size_shared: matchstate term not allowed"); default: - VERBOSE_DEBUG("/D"); sum += thing_arityval(hdr) + 1; goto pop_next; } break; } case TAG_PRIMARY_IMMED1: - VERBOSE_DEBUG("I"); pop_next: if (EQUEUE_ISEMPTY(s)) { goto cleanup; @@ -459,19 +443,15 @@ Uint size_shared(Eterm obj) default: erl_exit(ERTS_ABORT_EXIT, "size_shared: bad tag for %#x\n", obj); } - VERBOSE_DEBUG("\n"); } cleanup: - VERBOSE_DEBUG("\n"); obj = saved_obj; BITSTORE_CLOSE(b); for (;;) { - VERBOSE_DEBUG("[size] revisiting: %x ", obj); switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: { Eterm head, tail; - VERBOSE_DEBUG("L"); ptr = list_val_rel(obj, base); if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { goto cleanup_next; @@ -482,19 +462,15 @@ cleanup: if (primary_tag(tail) == TAG_PRIMARY_HEADER) { if (primary_tag(head) == TAG_PRIMARY_HEADER) { Eterm saved = BITSTORE_GET(b); - VERBOSE_DEBUG("/B restoring %d", saved); CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | saved; CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) | TAG_PRIMARY_BOXED; } else { - VERBOSE_DEBUG("/L"); CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) | TAG_PRIMARY_LIST; } } else if (primary_tag(head) == TAG_PRIMARY_HEADER) { - VERBOSE_DEBUG("/I"); CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | primary_tag(tail); CDR(ptr) = tail = (tail - primary_tag(tail)) | TAG_PRIMARY_IMMED1; } else { - VERBOSE_DEBUG("!"); goto cleanup_next; } /* and its children too */ @@ -506,7 +482,6 @@ cleanup: } case TAG_PRIMARY_BOXED: { Eterm hdr; - VERBOSE_DEBUG("B"); ptr = boxed_val_rel(obj, base); if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { goto cleanup_next; @@ -563,11 +538,9 @@ cleanup: default: erl_exit(ERTS_ABORT_EXIT, "size_shared: bad tag for %#x\n", obj); } - VERBOSE_DEBUG("\n"); } all_clean: - VERBOSE_DEBUG("\n"); /* Return the result */ DESTROY_EQUEUE(s); DESTROY_BITSTORE(b); @@ -597,18 +570,13 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) #ifdef DEBUG Eterm org_obj = obj; Uint org_sz = sz; -#endif -#ifdef SHCOPY_DEBUG - Eterm mypid; + Eterm mypid = erts_get_current_pid(); #endif if (IS_CONST(obj)) return obj; -#ifdef SHCOPY_DEBUG - mypid = erts_get_current_pid(); - VERBOSE_DEBUG("[pid=%T] copy_struct %p\n", mypid, obj); -#endif + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_struct %p\n", mypid, obj)); DTRACE1(copy_struct, (int32_t)sz); @@ -891,7 +859,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } #endif *hpp = (Eterm *) (hstart+hsize); - VERBOSE_DEBUG("[pid=%T] result is at %p\n", mypid, res); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] result is at %p\n", mypid, res)); return res; } @@ -1040,8 +1008,8 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) if (myself == NULL || (flags & ERTS_SHCOPY_FLG_NONE)) return size_object(obj); - VERBOSE_DEBUG("[pid=%T] copy_shared_calculate %p\n", myself->common.id, obj); - VERBOSE_DEBUG("[pid=%T] message is %T\n", myself->common.id, obj); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_shared_calculate %p\n", myself->common.id, obj)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] message is %T\n", myself->common.id, obj)); /* step #1: ------------------------------------------------------- @@ -1060,18 +1028,13 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) sum = 0; for (;;) { - VERBOSE_DEBUG("[copy] visiting: %x ", obj); switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: { Eterm head, tail; - VERBOSE_DEBUG("L"); ptr = list_val_rel(obj, base); /* off heap list pointers are copied verbatim */ if (!INHEAP(myself, ptr)) { - VERBOSE_DEBUG("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj); - if (myself->mbuf != NULL) - VERBOSE_DEBUG("[pid=%T] BUT !!! there are message buffers!\n", myself->common.id); - VERBOSE_DEBUG("#"); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj)); goto pop_next; } head = CAR(ptr); @@ -1080,10 +1043,9 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) if not already shared, make it shared and store it in the table */ if (primary_tag(tail) == TAG_PRIMARY_HEADER || primary_tag(head) == TAG_PRIMARY_HEADER) { - VERBOSE_DEBUG("!"); if (tail != THE_NON_VALUE) { e = SHTABLE_NEXT(t); - VERBOSE_DEBUG("[pid=%T] tabling L %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabling L %p\n", myself->common.id, ptr)); SHTABLE_PUSH(t, head, tail, ptr); CAR(ptr) = (e << _TAG_PRIMARY_SIZE) | LIST_SHARED_UNPROCESSED; CDR(ptr) = THE_NON_VALUE; @@ -1093,20 +1055,17 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) /* else make it visited now */ switch (primary_tag(tail)) { case TAG_PRIMARY_LIST: - VERBOSE_DEBUG("/L"); - VERBOSE_DEBUG("[pid=%T] mangling L/L %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/L %p\n", myself->common.id, ptr)); CDR(ptr) = (tail - TAG_PRIMARY_LIST) | TAG_PRIMARY_HEADER; break; case TAG_PRIMARY_IMMED1: - VERBOSE_DEBUG("/I"); - VERBOSE_DEBUG("[pid=%T] mangling L/I %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/I %p\n", myself->common.id, ptr)); CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER; CDR(ptr) = (tail - TAG_PRIMARY_IMMED1) | primary_tag(head); break; case TAG_PRIMARY_BOXED: - VERBOSE_DEBUG("/B saved %d", primary_tag(head)); BITSTORE_PUT(b, primary_tag(head)); - VERBOSE_DEBUG("[pid=%T] mangling L/B %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/B %p\n", myself->common.id, ptr)); CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER; CDR(ptr) = (tail - TAG_PRIMARY_BOXED) | TAG_PRIMARY_HEADER; break; @@ -1121,39 +1080,34 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) } case TAG_PRIMARY_BOXED: { Eterm hdr; - VERBOSE_DEBUG("B"); ptr = boxed_val_rel(obj, base); /* off heap pointers to boxes are copied verbatim */ if (!INHEAP(myself, ptr)) { - VERBOSE_DEBUG("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj); - VERBOSE_DEBUG("#"); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj)); goto pop_next; } hdr = *ptr; /* if it's visited, don't count it; if not already shared, make it shared and store it in the table */ if (primary_tag(hdr) != TAG_PRIMARY_HEADER) { - VERBOSE_DEBUG("!"); if (primary_tag(hdr) == BOXED_VISITED) { e = SHTABLE_NEXT(t); - VERBOSE_DEBUG("[pid=%T] tabling B %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabling B %p\n", myself->common.id, ptr)); SHTABLE_PUSH(t, hdr, THE_NON_VALUE, ptr); *ptr = (e << _TAG_PRIMARY_SIZE) | BOXED_SHARED_UNPROCESSED; } goto pop_next; } /* else make it visited now */ - VERBOSE_DEBUG("[pid=%T] mangling B %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling B %p\n", myself->common.id, ptr)); *ptr = (hdr - primary_tag(hdr)) + BOXED_VISITED; /* and count it */ ASSERT(is_header(hdr)); switch (hdr & _TAG_HEADER_MASK) { case ARITYVAL_SUBTAG: { int arity = header_arity(hdr); - VERBOSE_DEBUG("/T"); sum += arity + 1; if (arity == 0) { /* Empty tuple -- unusual. */ - VERBOSE_DEBUG("e"); goto pop_next; } while (arity-- > 0) { @@ -1168,7 +1122,6 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) ErlFunThing* funp = (ErlFunThing *) ptr; unsigned eterms = 1 /* creator */ + funp->num_free; sz = thing_arityval(hdr); - VERBOSE_DEBUG("/F"); sum += 1 /* header */ + sz + eterms; ptr += 1 /* header */ + sz; while (eterms-- > 0) { @@ -1213,17 +1166,14 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) erl_exit(ERTS_ABORT_EXIT, "size_shared: matchstate term not allowed"); default: - VERBOSE_DEBUG("/D"); sum += thing_arityval(hdr) + 1; goto pop_next; } break; } case TAG_PRIMARY_IMMED1: - VERBOSE_DEBUG("I"); pop_next: if (EQUEUE_ISEMPTY(s)) { - VERBOSE_DEBUG("\n"); // add sentinel to the table SHTABLE_PUSH(t, THE_NON_VALUE, THE_NON_VALUE, NULL); // store persistent info @@ -1236,7 +1186,7 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) info->shtable_start = t.start; info->shtable_alloc_type = t.alloc_type; // single point of return: the size of the object - VERBOSE_DEBUG("[pid=%T] size was: %u\n", myself->common.id, sum); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size was: %u\n", myself->common.id, sum)); return sum; } obj = EQUEUE_GET(s); @@ -1244,7 +1194,6 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) default: erl_exit(ERTS_ABORT_EXIT, "[pid=%T] size_shared: bad tag for %#x\n", obj); } - VERBOSE_DEBUG("\n"); } } @@ -1266,7 +1215,7 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E unsigned remaining; Process* myself; int force_local = flags & ERTS_SHCOPY_FLG_TMP_BUF; -#if defined(DEBUG) || defined(SHCOPY_DEBUG) +#ifdef DEBUG Eterm saved_obj = obj; #endif @@ -1292,7 +1241,7 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E if (myself == NULL || (flags & ERTS_SHCOPY_FLG_NONE)) return copy_struct(obj, size, hpp, off_heap); - VERBOSE_DEBUG("[pid=%T] copy_shared_perform %p\n", myself->common.id, obj); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_shared_perform %p\n", myself->common.id, obj)); /* step #2: was performed before this function was called ------------------------------------------------------- @@ -1315,15 +1264,12 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E resp = &result; remaining = 0; for (;;) { - VERBOSE_DEBUG("[copy] revisiting: %x ", obj); switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: { Eterm head, tail; - VERBOSE_DEBUG("L"); ptr = list_val_rel(obj, base); /* off heap list pointers are copied verbatim */ if (!INHEAP(myself, ptr)) { - VERBOSE_DEBUG("#"); *resp = obj; goto cleanup_next; } @@ -1334,19 +1280,17 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E e = head >> _TAG_PRIMARY_SIZE; /* if it has been processed, just use the forwarding pointer */ if (primary_tag(head) == LIST_SHARED_PROCESSED) { - VERBOSE_DEBUG("!"); *resp = make_list(SHTABLE_FWD(t, e)); goto cleanup_next; } /* else, let's process it now, copy it and keep the forwarding pointer */ else { - VERBOSE_DEBUG("$"); CAR(ptr) = (head - primary_tag(head)) + LIST_SHARED_PROCESSED; head = SHTABLE_X(t, e); tail = SHTABLE_Y(t, e); ptr = &(SHTABLE_X(t, e)); - VERBOSE_DEBUG("[pid=%T] tabled L %p is %p\n", myself->common.id, ptr, SHTABLE_REV(t, e)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabled L %p is %p\n", myself->common.id, ptr, SHTABLE_REV(t, e))); SHTABLE_FWD_UPD(t, e, hp); } } @@ -1354,18 +1298,15 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E if (primary_tag(tail) == TAG_PRIMARY_HEADER) { if (primary_tag(head) == TAG_PRIMARY_HEADER) { Eterm saved = BITSTORE_GET(b); - VERBOSE_DEBUG("/B restoring %d", saved); - VERBOSE_DEBUG("[pid=%T] unmangling L/B %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/B %p\n", myself->common.id, ptr)); CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) + saved; CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_BOXED; } else { - VERBOSE_DEBUG("/L"); - VERBOSE_DEBUG("[pid=%T] unmangling L/L %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/L %p\n", myself->common.id, ptr)); CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_LIST; } } else if (primary_tag(head) == TAG_PRIMARY_HEADER) { - VERBOSE_DEBUG("/I"); - VERBOSE_DEBUG("[pid=%T] unmangling L/I %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/I %p\n", myself->common.id, ptr)); CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | primary_tag(tail); CDR(ptr) = tail = (tail - primary_tag(tail)) | TAG_PRIMARY_IMMED1; } else { @@ -1387,11 +1328,9 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E } case TAG_PRIMARY_BOXED: { Eterm hdr; - VERBOSE_DEBUG("B"); ptr = boxed_val_rel(obj, base); /* off heap pointers to boxes are copied verbatim */ if (!INHEAP(myself, ptr)) { - VERBOSE_DEBUG("#"); *resp = obj; goto cleanup_next; } @@ -1403,7 +1342,6 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E /* if it is shared and has been processed, just use the forwarding pointer */ case BOXED_SHARED_PROCESSED: - VERBOSE_DEBUG("!"); e = hdr >> _TAG_PRIMARY_SIZE; *resp = make_boxed(SHTABLE_FWD(t, e)); goto cleanup_next; @@ -1411,17 +1349,16 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E it now: copy it and keep the forwarding pointer */ case BOXED_SHARED_UNPROCESSED: e = hdr >> _TAG_PRIMARY_SIZE; - VERBOSE_DEBUG("$"); *ptr = (hdr - primary_tag(hdr)) + BOXED_SHARED_PROCESSED; hdr = SHTABLE_X(t, e); ASSERT(primary_tag(hdr) == BOXED_VISITED); - VERBOSE_DEBUG("[pid=%T] tabled B %p is %p\n", myself->common.id, ptr, SHTABLE_REV(t, e)); - VERBOSE_DEBUG("[pid=%T] unmangling B %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabled B %p is %p\n", myself->common.id, ptr, SHTABLE_REV(t, e))); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling B %p\n", myself->common.id, ptr)); SHTABLE_X(t, e) = hdr = (hdr - BOXED_VISITED) + TAG_PRIMARY_HEADER; SHTABLE_FWD_UPD(t, e, hp); break; case BOXED_VISITED: - VERBOSE_DEBUG("[pid=%T] unmangling B %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling B %p\n", myself->common.id, ptr)); *ptr = hdr = (hdr - BOXED_VISITED) + TAG_PRIMARY_HEADER; break; } @@ -1628,7 +1565,6 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E default: erl_exit(ERTS_ABORT_EXIT, "size_shared: bad tag for %#x\n", obj); } - VERBOSE_DEBUG("\n"); } /* step #4: @@ -1637,21 +1573,20 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E */ all_clean: - VERBOSE_DEBUG("\n"); for (e = 0; ; e += SHTABLE_INCR) { ptr = SHTABLE_REV(t, e); if (ptr == NULL) break; - VERBOSE_DEBUG("[copy] restoring shared: %x\n", ptr); + VERBOSE(DEBUG_SHCOPY, ("[copy] restoring shared: %x\n", ptr)); /* entry was a list */ if (SHTABLE_Y(t, e) != THE_NON_VALUE) { - VERBOSE_DEBUG("[pid=%T] untabling L %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] untabling L %p\n", myself->common.id, ptr)); CAR(ptr) = SHTABLE_X(t, e); CDR(ptr) = SHTABLE_Y(t, e); } /* entry was boxed */ else { - VERBOSE_DEBUG("[pid=%T] untabling B %p\n", myself->common.id, ptr); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] untabling B %p\n", myself->common.id, ptr)); *ptr = SHTABLE_X(t, e); ASSERT(primary_tag(*ptr) == TAG_PRIMARY_HEADER); } @@ -1665,9 +1600,9 @@ all_clean: } #endif - VERBOSE_DEBUG("[pid=%T] original was %T\n", myself->common.id, saved_obj); - VERBOSE_DEBUG("[pid=%T] copy is %T\n", myself->common.id, result); - VERBOSE_DEBUG("[pid=%T] result is at %p\n", myself->common.id, result); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] original was %T\n", myself->common.id, saved_obj)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy is %T\n", myself->common.id, result)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] result is at %p\n", myself->common.id, result)); ASSERT(hp == *hpp + size); *hpp = hp; diff --git a/erts/emulator/beam/erl_debug.h b/erts/emulator/beam/erl_debug.h index f4259e7dae..029320691d 100644 --- a/erts/emulator/beam/erl_debug.h +++ b/erts/emulator/beam/erl_debug.h @@ -48,6 +48,7 @@ #define DEBUG_THREADS 0x0010 /* Thread-related stuff */ #define DEBUG_PROCESSES 0x0020 /* Process creation and removal */ #define DEBUG_MEMORY 0x0040 /* Display results of memory checks */ +#define DEBUG_SHCOPY 0x0080 /* Sharing-preserving copying of terms */ extern Uint32 verbose; diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 1cf6509012..c50756d56b 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1166,9 +1166,8 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end, char* oh = (char *) OLD_HEAP(p); Uint oh_size = (char *) OLD_HTOP(p) - oh; -#ifdef SHCOPY_DEBUG - VERBOSE_DEBUG("[pid=%T] MINOR GC: %p %p %p %p\n", p->common.id, HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p)); -#endif + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] MINOR GC: %p %p %p %p\n", p->common.id, + HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p))); n_htop = n_heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*new_sz); @@ -1387,9 +1386,8 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end, Uint new_sz, stk_sz; int adjusted; -#ifdef SHCOPY_DEBUG - VERBOSE_DEBUG("[pid=%T] MAJOR GC: %p %p %p %p\n", p->common.id, HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p)); -#endif + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] MAJOR GC: %p %p %p %p\n", p->common.id, + HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p))); /* * Do a fullsweep GC. First figure out the size of the heap diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index f396a0a156..296cfdabc3 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -1401,6 +1401,7 @@ erl_start(int argc, char **argv) case 't': verbose |= DEBUG_THREADS; break; case 'p': verbose |= DEBUG_PROCESSES; break; case 'm': verbose |= DEBUG_MESSAGES; break; + case 'c': verbose |= DEBUG_SHCOPY; break; default : erts_fprintf(stderr,"Unknown verbose option: %c\n",*ch); } } @@ -1413,6 +1414,7 @@ erl_start(int argc, char **argv) if (verbose & DEBUG_THREADS) erts_printf("THREADS "); if (verbose & DEBUG_PROCESSES) erts_printf("PROCESSES "); if (verbose & DEBUG_MESSAGES) erts_printf("MESSAGES "); + if (verbose & DEBUG_SHCOPY) erts_printf("SHCOPY "); erts_printf("\n"); #else erts_fprintf(stderr, "warning: -v (only in debug compiled code)\n"); diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index b1fca1df0c..11890a756d 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -68,9 +68,7 @@ new_message_buffer(Uint size) bp = (ErlHeapFragment*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP_FRAG, ERTS_HEAP_FRAG_SIZE(size)); ERTS_INIT_HEAP_FRAG(bp, size, size); -#ifdef SHCOPY_DEBUG - VERBOSE_DEBUG("[pid=%T] new message buffer %p\n", erts_get_current_pid(), bp->mem); -#endif + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] new message buffer %p\n", erts_get_current_pid(), bp->mem)); return bp; } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index f2da5289d3..96d17306a5 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11274,8 +11274,8 @@ static void delete_process(Process* p) { VERBOSE(DEBUG_PROCESSES, ("Removing process: %T\n",p->common.id)); - VERBOSE_DEBUG("[pid=%T] delete process: %p %p %p %p\n", p->common.id, - HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] delete process: %p %p %p %p\n", p->common.id, + HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p))); /* Cleanup psd */ diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 3e1f3664bc..303b9ee51b 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1046,16 +1046,6 @@ void erl_error(char*, va_list); #define SHCOPY /* Use this if you want sharing-preserving copy to be initially disabled */ #undef SHCOPY_DISABLE -/* Use this with care, it is *very* verbose! */ -#undef SHCOPY_DEBUG -#endif - -#ifdef SHCOPY_DEBUG -#define VERBOSE_DEBUG(...) do { \ - erts_fprintf(stderr, __VA_ARGS__); \ - } while(0) -#else -#define VERBOSE_DEBUG(...) #endif #define ERTS_SHCOPY_FLG_MASK (((unsigned) 3) << 0) -- cgit v1.2.3 From eebdde01b149ea45966c7412bc2a062136457b54 Mon Sep 17 00:00:00 2001 From: Yiannis Tsiouris Date: Tue, 22 Jan 2013 18:16:33 +0200 Subject: Add --enable-sharing-preserving configure flag --- erts/configure.in | 12 ++++++++++++ erts/emulator/beam/erl_bif_info.c | 3 +++ erts/emulator/beam/global.h | 5 +---- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 2419925c33..9ad1588b6c 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -788,6 +788,18 @@ esac AC_SUBST(LIBCARBON) +dnl Check if we should/can build a sharing-preserving emulator + +AC_MSG_CHECKING(if we are building a sharing-preserving emulator) +if test "$enable_sharing_preserving" = "yes"; then + AC_DEFINE(SHCOPY, [1], + [Define if building a sharing-preserving emulator]) + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + + dnl some tests below will call this if we haven't already - and autoconf dnl can't handle those tests being done conditionally at runtime AC_PROG_CPP diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 1eb106a551..82c2aa4b9e 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -129,6 +129,9 @@ static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE #endif #ifdef USE_SYSTEMTAP " [systemtap]" +#endif +#ifdef SHCOPY + " [sharing-preserving]" #endif "\n"); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 303b9ee51b..3c59df5f41 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1038,12 +1038,9 @@ void erl_error(char*, va_list); /* This controls whether sharing-preserving copy is used by Erlang */ +#ifdef SHCOPY #define SHCOPY_SEND #define SHCOPY_SPAWN - -#if defined(SHCOPY_SEND) \ - || defined(SHCOPY_SPAWN) -#define SHCOPY /* Use this if you want sharing-preserving copy to be initially disabled */ #undef SHCOPY_DISABLE #endif -- cgit v1.2.3 From bcdf32795a3f5a6aad95567cffba78fcecb9f40f Mon Sep 17 00:00:00 2001 From: "Nikolaos S. Papaspyrou" Date: Thu, 24 Apr 2014 12:59:39 +0300 Subject: Add support for maps in preserved copy --- erts/emulator/beam/copy.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index f74c6b1c89..cb1f38429a 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -424,6 +424,19 @@ Uint size_shared(Eterm obj) } goto pop_next; } + case MAP_SUBTAG: { + map_t *mp = (map_t *) ptr; + Uint n = map_get_size(mp) + 1; + sum += n + 2; + ptr += 2; /* hdr + size words */ + while (n-- > 0) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT(s, obj); + } + } + goto pop_next; + } case BIN_MATCHSTATE_SUBTAG: erl_exit(ERTS_ABORT_EXIT, "size_shared: matchstate term not allowed"); @@ -523,6 +536,18 @@ cleanup: } goto cleanup_next; } + case MAP_SUBTAG: { + map_t *mp = (map_t *) ptr; + Uint n = map_get_size(mp) + 1; + ptr += 2; /* hdr + size words */ + while (n-- > 0) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT_UNCHECKED(s, obj); + } + } + goto cleanup_next; + } default: goto cleanup_next; } @@ -1162,6 +1187,19 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) } goto pop_next; } + case MAP_SUBTAG: { + map_t *mp = (map_t *) ptr; + Uint n = map_get_size(mp) + 1; + sum += n + 2; + ptr += 2; /* hdr + size words */ + while (n-- > 0) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT(s, obj); + } + } + goto pop_next; + } case BIN_MATCHSTATE_SUBTAG: erl_exit(ERTS_ABORT_EXIT, "size_shared: matchstate term not allowed"); @@ -1404,6 +1442,23 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E erts_refc_inc(&funp->fe->refc, 2); goto cleanup_next; } + case MAP_SUBTAG: { + map_t *mp = (map_t *) ptr; + Uint n = map_get_size(mp) + 1; + *resp = make_map(hp); + *hp++ = hdr; + *hp++ = *++ptr; + while (n-- > 0) { + obj = *++ptr; + if (IS_CONST(obj)) { + *hp++ = obj; + } else { + EQUEUE_PUT_UNCHECKED(s, obj); + *hp++ = HEAP_ELEM_TO_BE_FILLED; + } + } + goto cleanup_next; + } case REFC_BINARY_SUBTAG: { ProcBin* pb = (ProcBin *) ptr; sz = thing_arityval(hdr); @@ -1539,6 +1594,12 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E remaining = 1 + funp->num_free; break; } + case MAP_SUBTAG: { + map_t *mp = (map_t *) hscan; + remaining = map_get_size(mp) + 1; + hscan += 2; + break; + } case SUB_BINARY_SUBTAG: ASSERT(((ErlSubBin *) hscan)->bitoffs + ((ErlSubBin *) hscan)->bitsize > 0); -- cgit v1.2.3 From 99de9eb01d8884ac293fdda20ab65c045d215d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Sep 2015 17:47:36 +0200 Subject: Fix internal stacks The internal stacks has changed between releases. --- erts/emulator/beam/copy.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index cb1f38429a..added85b46 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -907,7 +907,7 @@ int disable_copy_shared = ERTS_SHCOPY_FLG_NONE; #define SHTABLE_PUSH(s,x,y,b) \ do { \ if (s.sp > s.end - SHTABLE_INCR) { \ - erl_grow_estack(&s, ESTK_DEF_STACK(s)); \ + erl_grow_estack(&(s), SHTABLE_INCR); \ } \ *s.sp++ = (x); \ *s.sp++ = (y); \ @@ -959,6 +959,7 @@ do { \ WSTK_DEF_STACK(s), /* wstart */ \ WSTK_DEF_STACK(s), /* wsp */ \ WSTK_DEF_STACK(s) + DEF_WSTACK_SIZE, /* wend */ \ + WSTK_DEF_STACK(s), /* wdflt */ \ ERTS_ALC_T_ESTACK /* alloc_type */ \ }; \ int WSTK_CONCAT(s,_bitoffs) = 0; \ @@ -969,8 +970,9 @@ do { \ /* no WSTK_DEF_STACK(s), read-only */ \ ErtsWStack s = { \ info->bitstore_start, /* wstart */ \ - NULL, /* wsp, read-only */ \ + NULL, /* wsp, read-only */ \ NULL, /* wend, read-only */ \ + NULL, /* wdef, read-only */ \ info->bitstore_alloc_type /* alloc_type */ \ }; \ int WSTK_CONCAT(s,_bitoffs) = 0; \ @@ -983,6 +985,7 @@ do { \ ESTK_DEF_STACK(s), /* start */ \ ESTK_DEF_STACK(s), /* sp */ \ ESTK_DEF_STACK(s) + DEF_ESTACK_SIZE, /* end */ \ + ESTK_DEF_STACK(s), /* default */ \ ERTS_ALC_T_ESTACK /* alloc_type */ \ }; \ Uint ESTK_CONCAT(s,_offset) = 0 @@ -991,8 +994,9 @@ do { \ /* no ESTK_DEF_STACK(s), read-only */ \ ErtsEStack s = { \ info->shtable_start, /* start */ \ - NULL, /* sp, read-only */ \ + NULL, /* sp, read-only */ \ NULL, /* end, read-only */ \ + NULL, /* def, read-only */ \ info->shtable_alloc_type /* alloc_type */ \ }; \ /* no ESTK_CONCAT(s,_offset), read-only */ -- cgit v1.2.3 From 2ab76f7f1fe6ec4c94541ba5a2f0cd9dc1b9c79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Sep 2015 16:49:28 +0200 Subject: Fix Halfword removal Halfword is no longer present in the runtime system. --- erts/emulator/beam/copy.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index added85b46..42b4bee29d 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -298,11 +298,7 @@ do { \ * It is argued whether the size of subterms in constant pools * should be counted or not. */ -#if HALFWORD_HEAP -Uint size_shared_rel(Eterm obj, Eterm* base) -#else Uint size_shared(Eterm obj) -#endif { Eterm saved_obj = obj; Uint sum = 0; @@ -320,7 +316,7 @@ Uint size_shared(Eterm obj) switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: { Eterm head, tail; - ptr = list_val_rel(obj, base); + ptr = list_val(obj); /* we're not counting anything that's outside our heap */ if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { goto pop_next; @@ -357,7 +353,7 @@ Uint size_shared(Eterm obj) } case TAG_PRIMARY_BOXED: { Eterm hdr; - ptr = boxed_val_rel(obj, base); + ptr = boxed_val(obj); /* we're not counting anything that's outside our heap */ if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { goto pop_next; @@ -414,13 +410,13 @@ Uint size_shared(Eterm obj) } else { extra_bytes = 0; } - ptr = binary_val_rel(sb->orig, base); + ptr = binary_val(sb->orig); hdr = (*ptr) & ~BOXED_VISITED_MASK; if (thing_subtag(hdr) == REFC_BINARY_SUBTAG) { sum += PROC_BIN_SIZE; } else { ASSERT(thing_subtag(hdr) == HEAP_BINARY_SUBTAG); - sum += heap_bin_size(binary_size_rel(obj, base) + extra_bytes); + sum += heap_bin_size(binary_size(obj) + extra_bytes); } goto pop_next; } @@ -465,7 +461,7 @@ cleanup: switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: { Eterm head, tail; - ptr = list_val_rel(obj, base); + ptr = list_val(obj); if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { goto cleanup_next; } @@ -495,7 +491,7 @@ cleanup: } case TAG_PRIMARY_BOXED: { Eterm hdr; - ptr = boxed_val_rel(obj, base); + ptr = boxed_val(obj); if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { goto cleanup_next; } @@ -912,7 +908,7 @@ do { \ *s.sp++ = (x); \ *s.sp++ = (y); \ *s.sp++ = (Eterm) NULL; \ - *s.sp++ = (Eterm) (b); /* bad in HALF_WORD */ \ + *s.sp++ = (Eterm) (b); \ ESTK_CONCAT(s,_offset) += SHTABLE_INCR; \ } while(0) #define SHTABLE_X(s,e) (s.start[e]) @@ -1004,7 +1000,6 @@ do { \ /* * Copy object "obj" preserving sharing. * First half: count size and calculate sharing. - * NOTE: We do not support HALF_WORD (yet?). */ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) { @@ -1060,7 +1055,7 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: { Eterm head, tail; - ptr = list_val_rel(obj, base); + ptr = list_val(obj); /* off heap list pointers are copied verbatim */ if (!INHEAP(myself, ptr)) { VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj)); @@ -1109,7 +1104,7 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) } case TAG_PRIMARY_BOXED: { Eterm hdr; - ptr = boxed_val_rel(obj, base); + ptr = boxed_val(obj); /* off heap pointers to boxes are copied verbatim */ if (!INHEAP(myself, ptr)) { VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj)); @@ -1178,11 +1173,11 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) } else { extra_bytes = 0; } - ASSERT(is_boxed(rterm2wterm(real_bin, base)) && - (((*boxed_val(rterm2wterm(real_bin, base))) & + ASSERT(is_boxed(real_bin) && + (((*boxed_val(real_bin)) & (_TAG_HEADER_MASK - _BINARY_XXX_MASK - BOXED_VISITED_MASK)) == _TAG_HEADER_REFC_BIN)); - hdr = *_unchecked_binary_val(rterm2wterm(real_bin, base)) & ~BOXED_VISITED_MASK; + hdr = *_unchecked_binary_val(real_bin) & ~BOXED_VISITED_MASK; if (thing_subtag(hdr) == HEAP_BINARY_SUBTAG) { sum += heap_bin_size(size+extra_bytes); } else { @@ -1243,7 +1238,6 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) /* * Copy object "obj" preserving sharing. * Second half: copy and restore the object. - * NOTE: We do not support HALF_WORD (yet?). */ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, ErlOffHeap* off_heap, unsigned flags) { @@ -1309,7 +1303,7 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: { Eterm head, tail; - ptr = list_val_rel(obj, base); + ptr = list_val(obj); /* off heap list pointers are copied verbatim */ if (!INHEAP(myself, ptr)) { *resp = obj; @@ -1370,7 +1364,7 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E } case TAG_PRIMARY_BOXED: { Eterm hdr; - ptr = boxed_val_rel(obj, base); + ptr = boxed_val(obj); /* off heap pointers to boxes are copied verbatim */ if (!INHEAP(myself, ptr)) { *resp = obj; @@ -1500,11 +1494,11 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E extra_bytes = 0; } real_size = size+extra_bytes; - ASSERT(is_boxed(rterm2wterm(real_bin, base)) && - (((*boxed_val(rterm2wterm(real_bin, base))) & + ASSERT(is_boxed(real_bin) && + (((*boxed_val(real_bin)) & (_TAG_HEADER_MASK - _BINARY_XXX_MASK - BOXED_VISITED_MASK)) == _TAG_HEADER_REFC_BIN)); - ptr = _unchecked_binary_val(rterm2wterm(real_bin, base)); + ptr = _unchecked_binary_val(real_bin); *resp = make_binary(hp); if (extra_bytes != 0) { ErlSubBin* res = (ErlSubBin *) hp; -- cgit v1.2.3 From 95b25f7a3a33c43912079af713bf1f386a764071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Sep 2015 17:36:22 +0200 Subject: Fix Map preserved sharing copy implementation The Map implementation has changed since initial preserved copy implementation. --- erts/emulator/beam/copy.c | 135 +++++++++++++++++++++++++++++----------------- 1 file changed, 85 insertions(+), 50 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 42b4bee29d..c7806ed8ff 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -420,19 +420,28 @@ Uint size_shared(Eterm obj) } goto pop_next; } - case MAP_SUBTAG: { - map_t *mp = (map_t *) ptr; - Uint n = map_get_size(mp) + 1; - sum += n + 2; - ptr += 2; /* hdr + size words */ - while (n-- > 0) { - obj = *ptr++; - if (!IS_CONST(obj)) { - EQUEUE_PUT(s, obj); + case MAP_SUBTAG: + switch (MAP_HEADER_TYPE(hdr)) { + case MAP_HEADER_TAG_FLATMAP_HEAD : { + flatmap_t *mp = (flatmap_t*)flatmap_val(obj); + Uint n = flatmap_get_size(mp) + 1; + ptr = (Eterm *)mp; + sum += n + 2; + ptr += 2; /* hdr + size words */ + while (n--) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT(s, obj); + } + } + goto pop_next; } + case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : + case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : + case MAP_HEADER_TAG_HAMT_NODE_BITMAP : + default: + erl_exit(ERTS_ABORT_EXIT, "size_shared: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); } - goto pop_next; - } case BIN_MATCHSTATE_SUBTAG: erl_exit(ERTS_ABORT_EXIT, "size_shared: matchstate term not allowed"); @@ -533,10 +542,10 @@ cleanup: goto cleanup_next; } case MAP_SUBTAG: { - map_t *mp = (map_t *) ptr; - Uint n = map_get_size(mp) + 1; + flatmap_t *mp = (flatmap_t *) ptr; + Uint n = flatmap_get_size(mp) + 1; ptr += 2; /* hdr + size words */ - while (n-- > 0) { + while (n--) { obj = *ptr++; if (!IS_CONST(obj)) { EQUEUE_PUT_UNCHECKED(s, obj); @@ -1186,20 +1195,28 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) } goto pop_next; } - case MAP_SUBTAG: { - map_t *mp = (map_t *) ptr; - Uint n = map_get_size(mp) + 1; - sum += n + 2; - ptr += 2; /* hdr + size words */ - while (n-- > 0) { - obj = *ptr++; - if (!IS_CONST(obj)) { - EQUEUE_PUT(s, obj); + case MAP_SUBTAG: + switch (MAP_HEADER_TYPE(hdr)) { + case MAP_HEADER_TAG_FLATMAP_HEAD : { + flatmap_t *mp = (flatmap_t *) ptr; + Uint n = flatmap_get_size(mp) + 1; + sum += n + 2; + ptr += 2; /* hdr + size words */ + while (n--) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT(s, obj); + } + } + goto pop_next; } + case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : + case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : + case MAP_HEADER_TAG_HAMT_NODE_BITMAP : + default: + erl_exit(ERTS_ABORT_EXIT, "copy_shared_calculate: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); } - goto pop_next; - } - case BIN_MATCHSTATE_SUBTAG: + case BIN_MATCHSTATE_SUBTAG: erl_exit(ERTS_ABORT_EXIT, "size_shared: matchstate term not allowed"); default: @@ -1234,7 +1251,6 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) } } - /* * Copy object "obj" preserving sharing. * Second half: copy and restore the object. @@ -1440,23 +1456,31 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E erts_refc_inc(&funp->fe->refc, 2); goto cleanup_next; } - case MAP_SUBTAG: { - map_t *mp = (map_t *) ptr; - Uint n = map_get_size(mp) + 1; - *resp = make_map(hp); - *hp++ = hdr; - *hp++ = *++ptr; - while (n-- > 0) { - obj = *++ptr; - if (IS_CONST(obj)) { - *hp++ = obj; - } else { - EQUEUE_PUT_UNCHECKED(s, obj); - *hp++ = HEAP_ELEM_TO_BE_FILLED; - } - } - goto cleanup_next; - } + case MAP_SUBTAG: + switch (MAP_HEADER_TYPE(hdr)) { + case MAP_HEADER_TAG_FLATMAP_HEAD : { + flatmap_t *mp = (flatmap_t *) ptr; + Uint n = flatmap_get_size(mp) + 1; + *resp = make_flatmap(hp); + *hp++ = hdr; + *hp++ = *++ptr; + while (n--) { + obj = *++ptr; + if (IS_CONST(obj)) { + *hp++ = obj; + } else { + EQUEUE_PUT_UNCHECKED(s, obj); + *hp++ = HEAP_ELEM_TO_BE_FILLED; + } + } + goto cleanup_next; + } + case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : + case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : + case MAP_HEADER_TAG_HAMT_NODE_BITMAP : + default: + erl_exit(ERTS_ABORT_EXIT, "copy_shared_perform: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); + } case REFC_BINARY_SUBTAG: { ProcBin* pb = (ProcBin *) ptr; sz = thing_arityval(hdr); @@ -1592,12 +1616,23 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E remaining = 1 + funp->num_free; break; } - case MAP_SUBTAG: { - map_t *mp = (map_t *) hscan; - remaining = map_get_size(mp) + 1; - hscan += 2; - break; - } + case MAP_SUBTAG: + switch (MAP_HEADER_TYPE(*hscan)) { + case MAP_HEADER_TAG_FLATMAP_HEAD : { + flatmap_t *mp = (flatmap_t *) hscan; + remaining = flatmap_get_size(mp) + 1; + hscan += 2; + break; + } + case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : + case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : + case MAP_HEADER_TAG_HAMT_NODE_BITMAP : + default: + erl_exit(ERTS_ABORT_EXIT, + "copy_shared_perform: bad hashmap type %d\n", + MAP_HEADER_TYPE(*hscan)); + } + break; case SUB_BINARY_SUBTAG: ASSERT(((ErlSubBin *) hscan)->bitoffs + ((ErlSubBin *) hscan)->bitsize > 0); -- cgit v1.2.3 From 2e10fe29f61bbe3246902e8eaf1636dd6457979f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 16 Sep 2015 16:21:43 +0200 Subject: Add support for HAMT maps in preserved copy --- erts/emulator/beam/copy.c | 115 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 27 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index c7806ed8ff..aa17713d07 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -438,7 +438,18 @@ Uint size_shared(Eterm obj) } case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : - case MAP_HEADER_TAG_HAMT_NODE_BITMAP : + case MAP_HEADER_TAG_HAMT_NODE_BITMAP : { + Uint n = hashmap_bitcount(MAP_HEADER_VAL(hdr)); + sum += 1 + n + header_arity(hdr); + ptr += 1 + header_arity(hdr); + while (n--) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT(s, obj); + } + } + goto pop_next; + } default: erl_exit(ERTS_ABORT_EXIT, "size_shared: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); } @@ -541,18 +552,37 @@ cleanup: } goto cleanup_next; } - case MAP_SUBTAG: { - flatmap_t *mp = (flatmap_t *) ptr; - Uint n = flatmap_get_size(mp) + 1; - ptr += 2; /* hdr + size words */ - while (n--) { - obj = *ptr++; - if (!IS_CONST(obj)) { - EQUEUE_PUT_UNCHECKED(s, obj); + case MAP_SUBTAG: + switch (MAP_HEADER_TYPE(hdr)) { + case MAP_HEADER_TAG_FLATMAP_HEAD : { + flatmap_t *mp = (flatmap_t *) ptr; + Uint n = flatmap_get_size(mp) + 1; + ptr += 2; /* hdr + size words */ + while (n--) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT_UNCHECKED(s, obj); + } + } + goto cleanup_next; + } + case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : + case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : + case MAP_HEADER_TAG_HAMT_NODE_BITMAP : { + Uint n = hashmap_bitcount(MAP_HEADER_VAL(hdr)); + sum += 1 + n + header_arity(hdr); + ptr += 1 + header_arity(hdr); + while (n--) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT_UNCHECKED(s, obj); + } + } + goto cleanup_next; } + default: + erl_exit(ERTS_ABORT_EXIT, "size_shared: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); } - goto cleanup_next; - } default: goto cleanup_next; } @@ -1212,7 +1242,22 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) } case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : - case MAP_HEADER_TAG_HAMT_NODE_BITMAP : + case MAP_HEADER_TAG_HAMT_NODE_BITMAP : { + Uint n = hashmap_bitcount(MAP_HEADER_VAL(hdr)); + sum += 1 + n + header_arity(hdr); + ptr += 1 + header_arity(hdr); + + if (n == 0) { + goto pop_next; + } + while(n--) { + obj = *ptr++; + if (!IS_CONST(obj)) { + EQUEUE_PUT(s, obj); + } + } + goto pop_next; + } default: erl_exit(ERTS_ABORT_EXIT, "copy_shared_calculate: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); } @@ -1228,20 +1273,20 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) case TAG_PRIMARY_IMMED1: pop_next: if (EQUEUE_ISEMPTY(s)) { - // add sentinel to the table - SHTABLE_PUSH(t, THE_NON_VALUE, THE_NON_VALUE, NULL); - // store persistent info - BITSTORE_CLOSE(b); - info->queue_start = s.start; - info->queue_end = s.end; + /* add sentinel to the table */ + SHTABLE_PUSH(t, THE_NON_VALUE, THE_NON_VALUE, NULL); + /* store persistent info */ + BITSTORE_CLOSE(b); + info->queue_start = s.start; + info->queue_end = s.end; info->queue_alloc_type = s.alloc_type; - info->bitstore_start = b.wstart; + info->bitstore_start = b.wstart; info->bitstore_alloc_type = b.alloc_type; - info->shtable_start = t.start; + info->shtable_start = t.start; info->shtable_alloc_type = t.alloc_type; - // single point of return: the size of the object - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size was: %u\n", myself->common.id, sum)); - return sum; + /* single point of return: the size of the object */ + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size was: %u\n", myself->common.id, sum)); + return sum; } obj = EQUEUE_GET(s); break; @@ -1457,13 +1502,13 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E goto cleanup_next; } case MAP_SUBTAG: + *resp = make_flatmap(hp); + *hp++ = hdr; switch (MAP_HEADER_TYPE(hdr)) { case MAP_HEADER_TAG_FLATMAP_HEAD : { flatmap_t *mp = (flatmap_t *) ptr; Uint n = flatmap_get_size(mp) + 1; - *resp = make_flatmap(hp); - *hp++ = hdr; - *hp++ = *++ptr; + *hp++ = *++ptr; /* keys */ while (n--) { obj = *++ptr; if (IS_CONST(obj)) { @@ -1477,7 +1522,20 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E } case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : - case MAP_HEADER_TAG_HAMT_NODE_BITMAP : + *hp++ = *++ptr; /* total map size */ + case MAP_HEADER_TAG_HAMT_NODE_BITMAP : { + Uint n = hashmap_bitcount(MAP_HEADER_VAL(hdr)); + while (n--) { + obj = *++ptr; + if (IS_CONST(obj)) { + *hp++ = obj; + } else { + EQUEUE_PUT_UNCHECKED(s, obj); + *hp++ = HEAP_ELEM_TO_BE_FILLED; + } + } + goto cleanup_next; + } default: erl_exit(ERTS_ABORT_EXIT, "copy_shared_perform: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); } @@ -1627,6 +1685,9 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : case MAP_HEADER_TAG_HAMT_NODE_BITMAP : + remaining = hashmap_bitcount(MAP_HEADER_VAL(*hscan)); + hscan += MAP_HEADER_ARITY(*hscan) + 1; + break; default: erl_exit(ERTS_ABORT_EXIT, "copy_shared_perform: bad hashmap type %d\n", -- cgit v1.2.3 From 748c73f1687b2375d4c607487f40036ba990c4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 17 Sep 2015 16:17:10 +0200 Subject: Refactor copy sharing --- erts/emulator/beam/beam_debug.c | 6 +++--- erts/emulator/beam/copy.c | 10 ++++----- erts/emulator/beam/erl_db.c | 4 ++-- erts/emulator/beam/erl_message.h | 6 +++--- erts/emulator/beam/erl_process.c | 6 +++--- erts/emulator/beam/global.h | 46 ++++++++++++++++++++-------------------- 6 files changed, 39 insertions(+), 39 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 36f3cfabbc..b007d000bf 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -96,18 +96,18 @@ erts_debug_copy_shared_1(BIF_ALIST_1) Uint size; Eterm* hp; Eterm copy; - shcopy_info info; + erts_shcopy_t info; #ifdef SHCOPY_DISABLE extern int disable_copy_shared; #endif - INITIALIZE_INFO(info); + INITIALIZE_SHCOPY(info); size = copy_shared_calculate(term, &info, 0); if (size > 0) { hp = HAlloc(p, size); } copy = copy_shared_perform(term, size, &info, &hp, &p->off_heap, 0); - DESTROY_INFO(info); + DESTROY_SHCOPY(info); #ifdef SHCOPY_DISABLE disable_copy_shared = 0; #endif diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index aa17713d07..f2dd73d862 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -1040,14 +1040,14 @@ do { \ * Copy object "obj" preserving sharing. * First half: count size and calculate sharing. */ -Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) +Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) { Uint sum; Uint e; unsigned sz; Eterm* ptr; Process* myself; - int force_local = flags & ERTS_SHCOPY_FLG_TMP_BUF; + int force_local = flags & ERTS_SHCOPY_FLG_TMPBUF; DECLARE_EQUEUE_INIT_INFO(s, info); DECLARE_BITSTORE_INIT_INFO(b, info); @@ -1300,8 +1300,8 @@ Uint copy_shared_calculate(Eterm obj, shcopy_info *info, unsigned flags) * Copy object "obj" preserving sharing. * Second half: copy and restore the object. */ -Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, ErlOffHeap* off_heap, unsigned flags) -{ +Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, + Eterm** hpp, ErlOffHeap* off_heap, Uint32 flags) { Uint e; unsigned sz; Eterm* ptr; @@ -1311,7 +1311,7 @@ Uint copy_shared_perform(Eterm obj, Uint size, shcopy_info *info, Eterm** hpp, E Eterm* resp; unsigned remaining; Process* myself; - int force_local = flags & ERTS_SHCOPY_FLG_TMP_BUF; + int force_local = flags & ERTS_SHCOPY_FLG_TMPBUF; #ifdef DEBUG Eterm saved_obj = obj; #endif diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 6119a9225f..59a0c0b808 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -1832,7 +1832,7 @@ BIF_RETTYPE ets_give_away_3(BIF_ALIST_3) tb->common.id, from_pid, BIF_ARG_3), - ERTS_SND_FLG_SHCOPY_TMP_BUF); + ERTS_SND_FLG_SHCOPY_TMPBUF); erts_smp_proc_unlock(to_proc, to_locks); UnUseTmpHeap(5,BIF_P); BIF_RET(am_true); @@ -3211,7 +3211,7 @@ retry: tb->common.id, p->common.id, heir_data), - ERTS_SND_FLG_SHCOPY_TMP_BUF); + ERTS_SND_FLG_SHCOPY_TMPBUF); erts_smp_proc_unlock(to_proc, to_locks); return !0; } diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 0de36c3199..52a648fe0b 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -239,9 +239,9 @@ do { \ #define ERTS_SND_FLG_NO_SEQ_TRACE (((unsigned) 1) << 0) #define ERTS_SND_FLG_SHCOPY_SHIFT 1 -#define ERTS_SND_FLG_SHCOPY_MASK (ERTS_SHCOPY_FLG_MASK << ERTS_SND_FLG_SHCOPY_SHIFT) -#define ERTS_SND_FLG_SHCOPY_NONE (ERTS_SHCOPY_FLG_NONE << ERTS_SND_FLG_SHCOPY_SHIFT) -#define ERTS_SND_FLG_SHCOPY_TMP_BUF (ERTS_SHCOPY_FLG_TMP_BUF << ERTS_SND_FLG_SHCOPY_SHIFT) +#define ERTS_SND_FLG_SHCOPY_MASK (ERTS_SHCOPY_FLG_MASK << ERTS_SND_FLG_SHCOPY_SHIFT) +#define ERTS_SND_FLG_SHCOPY_NONE (ERTS_SHCOPY_FLG_NONE << ERTS_SND_FLG_SHCOPY_SHIFT) +#define ERTS_SND_FLG_SHCOPY_TMPBUF (ERTS_SHCOPY_FLG_TMPBUF << ERTS_SND_FLG_SHCOPY_SHIFT) #define ERTS_HEAP_FRAG_SIZE(DATA_WORDS) \ (sizeof(ErlHeapFragment) - sizeof(Eterm) + (DATA_WORDS)*sizeof(Eterm)) diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 96d17306a5..a691a3c773 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -10749,8 +10749,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). erts_aint32_t prio = (erts_aint32_t) PRIORITY_NORMAL; #ifdef SHCOPY_SPAWN unsigned shflags = 0; /* could be taken from so->flags, if necessary */ - shcopy_info info; - INITIALIZE_INFO(info); + erts_shcopy_t info; + INITIALIZE_SHCOPY(info); #endif #ifdef ERTS_SMP @@ -10887,7 +10887,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). BM_SWAP_TIMER(system,copy); #ifdef SHCOPY_SPAWN p->arg_reg[2] = copy_shared_perform(args, arg_size, &info, &p->htop, &p->off_heap, shflags); - DESTROY_INFO(info); + DESTROY_SHCOPY(info); #else p->arg_reg[2] = copy_struct(args, arg_size, &p->htop, &p->off_heap); #endif diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 3c59df5f41..bdee20969d 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1045,13 +1045,13 @@ void erl_error(char*, va_list); #undef SHCOPY_DISABLE #endif -#define ERTS_SHCOPY_FLG_MASK (((unsigned) 3) << 0) -#define ERTS_SHCOPY_FLG_NONE (((unsigned) 1) << 0) -#define ERTS_SHCOPY_FLG_TMP_BUF (((unsigned) 1) << 1) +#define ERTS_SHCOPY_FLG_MASK (((Uint32) 3) << 0) +#define ERTS_SHCOPY_FLG_NONE (((Uint32) 1) << 0) +#define ERTS_SHCOPY_FLG_TMPBUF (((Uint32) 1) << 1) /* forces INHEAP to true */ /* The persistent state while the sharing-preserving copier works */ -typedef struct shcopy_info { +typedef struct { Eterm queue_default[DEF_EQUEUE_SIZE]; Eterm* queue_start; Eterm* queue_end; @@ -1062,26 +1062,26 @@ typedef struct shcopy_info { Eterm shtable_default[DEF_ESTACK_SIZE]; Eterm* shtable_start; ErtsAlcType_t shtable_alloc_type; -} shcopy_info; +} erts_shcopy_t; -#define INITIALIZE_INFO(info) \ -do { \ - info.queue_start = info.queue_default; \ - info.bitstore_start = info.bitstore_default; \ - info.shtable_start = info.shtable_default; \ +#define INITIALIZE_SHCOPY(info) \ +do { \ + info.queue_start = info.queue_default; \ + info.bitstore_start = info.bitstore_default; \ + info.shtable_start = info.shtable_default; \ } while(0) -#define DESTROY_INFO(info) \ -do { \ - if (info.queue_start != info.queue_default) { \ - erts_free(info.queue_alloc_type, info.queue_start); \ - } \ - if (info.bitstore_start != info.bitstore_default) { \ - erts_free(info.bitstore_alloc_type, info.bitstore_start); \ - } \ - if (info.shtable_start != info.shtable_default) { \ - erts_free(info.shtable_alloc_type, info.shtable_start); \ - } \ +#define DESTROY_SHCOPY(info) \ +do { \ + if (info.queue_start != info.queue_default) { \ + erts_free(info.queue_alloc_type, info.queue_start); \ + } \ + if (info.bitstore_start != info.bitstore_default) { \ + erts_free(info.bitstore_alloc_type, info.bitstore_start); \ + } \ + if (info.shtable_start != info.shtable_default) { \ + erts_free(info.shtable_alloc_type, info.shtable_start); \ + } \ } while(0) /* copy.c */ @@ -1089,8 +1089,8 @@ Eterm copy_object_x(Eterm, Process*, Uint); #define copy_object(Term, Proc) copy_object_x(Term,Proc,0) Uint size_object(Eterm); -Uint copy_shared_calculate(Eterm, shcopy_info*, unsigned); -Eterm copy_shared_perform(Eterm, Uint, shcopy_info*, Eterm**, ErlOffHeap*, unsigned); +Uint copy_shared_calculate(Eterm, erts_shcopy_t*, Uint32); +Eterm copy_shared_perform(Eterm, Uint, erts_shcopy_t*, Eterm**, ErlOffHeap*, Uint32); Uint size_shared(Eterm); -- cgit v1.2.3 From 5d2d888f368ba8ca0098d8bd9936ca9d67df7d3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 17 Sep 2015 19:30:08 +0200 Subject: Copy literals in copy sharing --- erts/emulator/beam/copy.c | 49 +++++++++++++++++++++++++++++---------------- erts/emulator/beam/global.h | 6 +++++- 2 files changed, 37 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index f2dd73d862..4f4d20cb83 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -611,7 +611,7 @@ cleanup: /* * Copy a structure to a heap. */ -Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) +Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint *bsz) { char* hstart; Uint hsize; @@ -626,6 +626,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) Eterm* argp; Eterm* const_tuple; Eterm hdr; + Eterm *hend; int i; #ifdef DEBUG Eterm org_obj = obj; @@ -641,7 +642,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) DTRACE1(copy_struct, (int32_t)sz); hp = htop = *hpp; - hbot = htop + sz; + hbot = hend = htop + sz; hstart = (char *)htop; hsize = (char*) hbot - hstart; const_tuple = 0; @@ -906,19 +907,24 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } } + if (bsz) { + *hpp = htop; + *bsz = hend - hbot; + } else { #ifdef DEBUG - if (htop != hbot) - erl_exit(ERTS_ABORT_EXIT, - "Internal error in copy_struct() when copying %T:" - " htop=%p != hbot=%p (sz=%beu)\n", - org_obj, htop, hbot, org_sz); + if (htop != hbot) + erl_exit(ERTS_ABORT_EXIT, + "Internal error in copy_struct() when copying %T:" + " htop=%p != hbot=%p (sz=%beu)\n", + org_obj, htop, hbot, org_sz); #else - if (htop > hbot) { - erl_exit(ERTS_ABORT_EXIT, - "Internal error in copy_struct(): htop, hbot overrun\n"); - } + if (htop > hbot) { + erl_exit(ERTS_ABORT_EXIT, + "Internal error in copy_struct(): htop, hbot overrun\n"); + } #endif - *hpp = (Eterm *) (hstart+hsize); + *hpp = (Eterm *) (hstart+hsize); + } VERBOSE(DEBUG_SHCOPY, ("[pid=%T] result is at %p\n", mypid, res)); return res; } @@ -1098,6 +1104,7 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) /* off heap list pointers are copied verbatim */ if (!INHEAP(myself, ptr)) { VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj)); + info->literal_size += size_object(obj); goto pop_next; } head = CAR(ptr); @@ -1147,6 +1154,7 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) /* off heap pointers to boxes are copied verbatim */ if (!INHEAP(myself, ptr)) { VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj)); + info->literal_size += size_object(obj); goto pop_next; } hdr = *ptr; @@ -1286,7 +1294,7 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) info->shtable_alloc_type = t.alloc_type; /* single point of return: the size of the object */ VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size was: %u\n", myself->common.id, sum)); - return sum; + return sum + info->literal_size; } obj = EQUEUE_GET(s); break; @@ -1309,6 +1317,7 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, Eterm* hscan; Eterm result; Eterm* resp; + Eterm *hbot, *hend; unsigned remaining; Process* myself; int force_local = flags & ERTS_SHCOPY_FLG_TMPBUF; @@ -1346,6 +1355,7 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, */ hscan = hp = *hpp; + hbot = hend = hp + size; /* step #3: ------------------------------------------------------- @@ -1367,7 +1377,9 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, ptr = list_val(obj); /* off heap list pointers are copied verbatim */ if (!INHEAP(myself, ptr)) { - *resp = obj; + Uint bsz; + *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz); + hbot -= bsz; goto cleanup_next; } head = CAR(ptr); @@ -1428,7 +1440,9 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, ptr = boxed_val(obj); /* off heap pointers to boxes are copied verbatim */ if (!INHEAP(myself, ptr)) { - *resp = obj; + Uint bsz; + *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz); + hbot -= bsz; goto cleanup_next; } hdr = *ptr; @@ -1759,8 +1773,9 @@ all_clean: VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy is %T\n", myself->common.id, result)); VERBOSE(DEBUG_SHCOPY, ("[pid=%T] result is at %p\n", myself->common.id, result)); - ASSERT(hp == *hpp + size); - *hpp = hp; + ASSERT(hbot == hp); + ASSERT(size == ((hp - *hpp) + (hend - hbot))); + *hpp = hend; return result; } diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index bdee20969d..f106b941ef 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1062,6 +1062,7 @@ typedef struct { Eterm shtable_default[DEF_ESTACK_SIZE]; Eterm* shtable_start; ErtsAlcType_t shtable_alloc_type; + Uint literal_size; } erts_shcopy_t; #define INITIALIZE_SHCOPY(info) \ @@ -1069,6 +1070,7 @@ do { \ info.queue_start = info.queue_default; \ info.bitstore_start = info.bitstore_default; \ info.shtable_start = info.shtable_default; \ + info.literal_size = 0; \ } while(0) #define DESTROY_SHCOPY(info) \ @@ -1094,7 +1096,9 @@ Eterm copy_shared_perform(Eterm, Uint, erts_shcopy_t*, Eterm**, ErlOffHeap*, Uin Uint size_shared(Eterm); -Eterm copy_struct(Eterm, Uint, Eterm**, ErlOffHeap*); +Eterm copy_struct_x(Eterm, Uint, Eterm**, ErlOffHeap*, Uint* bsz); +#define copy_struct(Obj,Sz,HPP,OH) \ + copy_struct_x(Obj,Sz,HPP,OH,NULL) Eterm copy_shallow(Eterm*, Uint, Eterm**, ErlOffHeap*); void erts_move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first, -- cgit v1.2.3 From 8f8aa9c5c4e26e563c935e06f8346175fa15d876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 18 Sep 2015 19:20:19 +0200 Subject: Add BIF for setting internal copy literal range --- erts/emulator/beam/beam_bif_load.c | 104 ++++++++++++++++++++++++++++++++++--- erts/emulator/beam/bif.tab | 1 + 2 files changed, 99 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index c3ebf71a01..e61d9ab0a6 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -154,7 +154,7 @@ static struct /* Protected by code_write_permission */ { Process* stager; ErtsThrPrgrLaterOp lop; -}commiter_state; +} committer_state; #endif static Eterm @@ -367,9 +367,9 @@ staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking, * schedulers to read active code_ix in a safe way while executing * without any memory barriers at all. */ - ASSERT(commiter_state.stager == NULL); - commiter_state.stager = c_p; - erts_schedule_thr_prgr_later_op(smp_code_ix_commiter, NULL, &commiter_state.lop); + ASSERT(committer_state.stager == NULL); + committer_state.stager = c_p; + erts_schedule_thr_prgr_later_op(smp_code_ix_commiter, NULL, &committer_state.lop); erts_proc_inc_refc(c_p); erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL); /* @@ -385,11 +385,11 @@ staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking, #ifdef ERTS_SMP static void smp_code_ix_commiter(void* null) { - Process* p = commiter_state.stager; + Process* p = committer_state.stager; erts_commit_staging_code_ix(); #ifdef DEBUG - commiter_state.stager = NULL; + committer_state.stager = NULL; #endif erts_release_code_write_permission(); erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); @@ -1007,6 +1007,98 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) return 0; } +#undef in_area + +#ifdef ERTS_SMP +static void copy_literals_commit(void*); +#endif + +typedef struct { + Eterm *ptr; + Uint sz; +} copy_literals_t; + +copy_literals_t erts_clrange = {NULL, 0}; + +/* copy literals + * + * copy_literals.ptr = LitPtr + * copy_literals.sz = LitSz + * ------ THR PROG COMMIT ----- + * + * - check process code + * - check process code + * ... + * copy_literals.ptr = NULL + * copy_literals.sz = 0 + * ------ THR PROG COMMIT ----- + * ... + */ + + +BIF_RETTYPE copy_literals_2(BIF_ALIST_2) +{ + Module* modp; + ErtsCodeIndex code_ix; + Eterm res = am_true; + + if (is_not_atom(BIF_ARG_1) || (am_true != BIF_ARG_2 && am_false != BIF_ARG_2)) { + BIF_ERROR(BIF_P, BADARG); + } + + if (!erts_try_seize_code_write_permission(BIF_P)) { + ERTS_BIF_YIELD2(bif_export[BIF_copy_literals_2], BIF_P, BIF_ARG_1, BIF_ARG_2); + } + + code_ix = erts_active_code_ix(); + + if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL || !modp->old.code_hdr) { + res = am_false; + goto done; + } + + if (BIF_ARG_2 == am_true) { + if (erts_clrange.ptr != NULL) { + res = am_aborted; + goto done; + } + erts_clrange.ptr = (Eterm*) modp->old.code_hdr->literals_start; + erts_clrange.sz = (Eterm*) modp->old.code_hdr->literals_end - erts_clrange.ptr; + } else if (BIF_ARG_2 == am_false) { + erts_clrange.ptr = NULL; + erts_clrange.sz = 0; + } + +#ifdef ERTS_SMP + ASSERT(committer_state.stager == NULL); + committer_state.stager = BIF_P; + erts_schedule_thr_prgr_later_op(copy_literals_commit, NULL, &committer_state.lop); + erts_proc_inc_refc(BIF_P); + erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL); + ERTS_BIF_YIELD_RETURN(BIF_P, am_true); +#endif +done: + erts_release_code_write_permission(); + BIF_RET(res); +} + +#ifdef ERTS_SMP +static void copy_literals_commit(void* null) { + Process* p = committer_state.stager; +#ifdef DEBUG + committer_state.stager = NULL; +#endif + 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_proc_dec_refc(p); +} +#endif /* ERTS_SMP */ + + BIF_RETTYPE purge_module_1(BIF_ALIST_1) { ErtsCodeIndex code_ix; diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index f45f886395..63a0d0b1f0 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -642,6 +642,7 @@ bif erts_debug:map_info/1 # New in 19.0 # +bif erlang:copy_literals/2 bif binary:split/2 bif binary:split/3 bif erts_debug:size_shared/1 -- cgit v1.2.3 From 1391715d8bbba315e1509e60e6245159a009bd9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 25 Sep 2015 17:03:02 +0200 Subject: Use copy literal range check in message passing and purging --- erts/emulator/beam/beam_bif_load.c | 5 -- erts/emulator/beam/copy.c | 136 ++++++++++++++++++------------------- erts/emulator/beam/global.h | 10 +++ 3 files changed, 75 insertions(+), 76 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index e61d9ab0a6..6b6c066211 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -1013,11 +1013,6 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) static void copy_literals_commit(void*); #endif -typedef struct { - Eterm *ptr; - Uint sz; -} copy_literals_t; - copy_literals_t erts_clrange = {NULL, 0}; /* copy literals diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 4f4d20cb83..5bca3877b5 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -276,49 +276,35 @@ do { \ #define BOXED_SHARED_UNPROCESSED ((Eterm) 2) #define BOXED_SHARED_PROCESSED ((Eterm) 3) +#define COUNT_OFF_HEAP (0) -/* - * Is an object in the local heap of a process? - */ - -#define INHEAP_SIMPLE(p, ptr) ( \ - (OLD_HEAP(p) && OLD_HEAP(p) <= ptr && ptr < OLD_HEND(p)) || \ - (HEAP_START(p) <= ptr && ptr < HEAP_END(p)) \ - ) -#define INHEAP(p, ptr) ( \ - INHEAP_SIMPLE(p, ptr) || \ - (force_local ? (force_local = 0, 1) : 0) \ - ) -#define COUNT_OFF_HEAP 0 - - +#define IN_LITERAL_PURGE_AREA(info, ptr) \ + ((info)->range_ptr && ( \ + (info)->range_ptr <= (ptr) && \ + (ptr) < ((info)->range_ptr + (info)->range_sz))) /* * Return the real size of an object and find sharing information * This currently returns the same as erts_debug:size/1. * It is argued whether the size of subterms in constant pools * should be counted or not. */ + Uint size_shared(Eterm obj) { Eterm saved_obj = obj; Uint sum = 0; Eterm* ptr; - Process* myself; DECLARE_EQUEUE(s); DECLARE_BITSTORE(b); - myself = erts_get_current_process(); - if (myself == NULL) - return size_object(obj); - for (;;) { switch (primary_tag(obj)) { case TAG_PRIMARY_LIST: { Eterm head, tail; ptr = list_val(obj); /* we're not counting anything that's outside our heap */ - if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { + if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) { goto pop_next; } head = CAR(ptr); @@ -355,7 +341,7 @@ Uint size_shared(Eterm obj) Eterm hdr; ptr = boxed_val(obj); /* we're not counting anything that's outside our heap */ - if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { + if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) { goto pop_next; } hdr = *ptr; @@ -482,7 +468,7 @@ cleanup: case TAG_PRIMARY_LIST: { Eterm head, tail; ptr = list_val(obj); - if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { + if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) { goto cleanup_next; } head = CAR(ptr); @@ -512,7 +498,7 @@ cleanup: case TAG_PRIMARY_BOXED: { Eterm hdr; ptr = boxed_val(obj); - if (!COUNT_OFF_HEAP && !INHEAP_SIMPLE(myself, ptr)) { + if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) { goto cleanup_next; } hdr = *ptr; @@ -1052,8 +1038,9 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) Uint e; unsigned sz; Eterm* ptr; - Process* myself; - int force_local = flags & ERTS_SHCOPY_FLG_TMPBUF; +#ifdef DEBUG + Eterm mypid = erts_get_current_pid(); +#endif DECLARE_EQUEUE_INIT_INFO(s, info); DECLARE_BITSTORE_INIT_INFO(b, info); @@ -1073,12 +1060,11 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) flags |= disable_copy_shared; #endif - myself = erts_get_current_process(); - if (myself == NULL || (flags & ERTS_SHCOPY_FLG_NONE)) + if (flags & ERTS_SHCOPY_FLG_NONE) return size_object(obj); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_shared_calculate %p\n", myself->common.id, obj)); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] message is %T\n", myself->common.id, obj)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_shared_calculate %p\n", mypid, obj)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] message is %T\n", mypid, obj)); /* step #1: ------------------------------------------------------- @@ -1102,9 +1088,10 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) Eterm head, tail; ptr = list_val(obj); /* off heap list pointers are copied verbatim */ - if (!INHEAP(myself, ptr)) { - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj)); - info->literal_size += size_object(obj); + if (erts_is_literal(obj,ptr)) { + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", mypid, ptr, obj)); + if (IN_LITERAL_PURGE_AREA(info,ptr)) + info->literal_size += size_object(obj); goto pop_next; } head = CAR(ptr); @@ -1115,7 +1102,7 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) primary_tag(head) == TAG_PRIMARY_HEADER) { if (tail != THE_NON_VALUE) { e = SHTABLE_NEXT(t); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabling L %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabling L %p\n", mypid, ptr)); SHTABLE_PUSH(t, head, tail, ptr); CAR(ptr) = (e << _TAG_PRIMARY_SIZE) | LIST_SHARED_UNPROCESSED; CDR(ptr) = THE_NON_VALUE; @@ -1125,17 +1112,17 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) /* else make it visited now */ switch (primary_tag(tail)) { case TAG_PRIMARY_LIST: - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/L %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/L %p\n", mypid, ptr)); CDR(ptr) = (tail - TAG_PRIMARY_LIST) | TAG_PRIMARY_HEADER; break; case TAG_PRIMARY_IMMED1: - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/I %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/I %p\n", mypid, ptr)); CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER; CDR(ptr) = (tail - TAG_PRIMARY_IMMED1) | primary_tag(head); break; case TAG_PRIMARY_BOXED: BITSTORE_PUT(b, primary_tag(head)); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/B %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling L/B %p\n", mypid, ptr)); CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER; CDR(ptr) = (tail - TAG_PRIMARY_BOXED) | TAG_PRIMARY_HEADER; break; @@ -1152,9 +1139,10 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) Eterm hdr; ptr = boxed_val(obj); /* off heap pointers to boxes are copied verbatim */ - if (!INHEAP(myself, ptr)) { - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", myself->common.id, ptr, obj)); - info->literal_size += size_object(obj); + if (erts_is_literal(obj,ptr)) { + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] bypassed copying %p is %T\n", mypid, ptr, obj)); + if (IN_LITERAL_PURGE_AREA(info,ptr)) + info->literal_size += size_object(obj); goto pop_next; } hdr = *ptr; @@ -1163,14 +1151,14 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) if (primary_tag(hdr) != TAG_PRIMARY_HEADER) { if (primary_tag(hdr) == BOXED_VISITED) { e = SHTABLE_NEXT(t); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabling B %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabling B %p\n", mypid, ptr)); SHTABLE_PUSH(t, hdr, THE_NON_VALUE, ptr); *ptr = (e << _TAG_PRIMARY_SIZE) | BOXED_SHARED_UNPROCESSED; } goto pop_next; } /* else make it visited now */ - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling B %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] mangling B %p\n", mypid, ptr)); *ptr = (hdr - primary_tag(hdr)) + BOXED_VISITED; /* and count it */ ASSERT(is_header(hdr)); @@ -1293,7 +1281,7 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) info->shtable_start = t.start; info->shtable_alloc_type = t.alloc_type; /* single point of return: the size of the object */ - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size was: %u\n", myself->common.id, sum)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] size was: %u\n", mypid, sum)); return sum + info->literal_size; } obj = EQUEUE_GET(s); @@ -1319,9 +1307,8 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, Eterm* resp; Eterm *hbot, *hend; unsigned remaining; - Process* myself; - int force_local = flags & ERTS_SHCOPY_FLG_TMPBUF; #ifdef DEBUG + Eterm mypid = erts_get_current_pid(); Eterm saved_obj = obj; #endif @@ -1343,11 +1330,10 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, flags |= disable_copy_shared; #endif - myself = erts_get_current_process(); - if (myself == NULL || (flags & ERTS_SHCOPY_FLG_NONE)) + if (flags & ERTS_SHCOPY_FLG_NONE) return copy_struct(obj, size, hpp, off_heap); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_shared_perform %p\n", myself->common.id, obj)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_shared_perform %p\n", mypid, obj)); /* step #2: was performed before this function was called ------------------------------------------------------- @@ -1376,10 +1362,14 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, Eterm head, tail; ptr = list_val(obj); /* off heap list pointers are copied verbatim */ - if (!INHEAP(myself, ptr)) { - Uint bsz; - *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz); - hbot -= bsz; + if (erts_is_literal(obj,ptr)) { + if (!IN_LITERAL_PURGE_AREA(info,ptr)) { + *resp = obj; + } else { + Uint bsz = 0; + *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz); + hbot -= bsz; + } goto cleanup_next; } head = CAR(ptr); @@ -1399,23 +1389,23 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, head = SHTABLE_X(t, e); tail = SHTABLE_Y(t, e); ptr = &(SHTABLE_X(t, e)); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabled L %p is %p\n", myself->common.id, ptr, SHTABLE_REV(t, e))); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabled L %p is %p\n", mypid, ptr, SHTABLE_REV(t, e))); SHTABLE_FWD_UPD(t, e, hp); } } /* if not already clean, clean it up and copy it */ if (primary_tag(tail) == TAG_PRIMARY_HEADER) { if (primary_tag(head) == TAG_PRIMARY_HEADER) { - Eterm saved = BITSTORE_GET(b); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/B %p\n", myself->common.id, ptr)); - CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) + saved; - CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_BOXED; + Eterm saved = BITSTORE_GET(b); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/B %p\n", mypid, ptr)); + CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) + saved; + CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_BOXED; } else { - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/L %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/L %p\n", mypid, ptr)); CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_LIST; } } else if (primary_tag(head) == TAG_PRIMARY_HEADER) { - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/I %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/I %p\n", mypid, ptr)); CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | primary_tag(tail); CDR(ptr) = tail = (tail - primary_tag(tail)) | TAG_PRIMARY_IMMED1; } else { @@ -1439,10 +1429,14 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, Eterm hdr; ptr = boxed_val(obj); /* off heap pointers to boxes are copied verbatim */ - if (!INHEAP(myself, ptr)) { - Uint bsz; - *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz); - hbot -= bsz; + if (erts_is_literal(obj,ptr)) { + if (!IN_LITERAL_PURGE_AREA(info,ptr)) { + *resp = obj; + } else { + Uint bsz = 0; + *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz); + hbot -= bsz; + } goto cleanup_next; } hdr = *ptr; @@ -1463,13 +1457,13 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, *ptr = (hdr - primary_tag(hdr)) + BOXED_SHARED_PROCESSED; hdr = SHTABLE_X(t, e); ASSERT(primary_tag(hdr) == BOXED_VISITED); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabled B %p is %p\n", myself->common.id, ptr, SHTABLE_REV(t, e))); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling B %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] tabled B %p is %p\n", mypid, ptr, SHTABLE_REV(t, e))); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling B %p\n", mypid, ptr)); SHTABLE_X(t, e) = hdr = (hdr - BOXED_VISITED) + TAG_PRIMARY_HEADER; SHTABLE_FWD_UPD(t, e, hp); break; case BOXED_VISITED: - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling B %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling B %p\n", mypid, ptr)); *ptr = hdr = (hdr - BOXED_VISITED) + TAG_PRIMARY_HEADER; break; } @@ -1749,13 +1743,13 @@ all_clean: VERBOSE(DEBUG_SHCOPY, ("[copy] restoring shared: %x\n", ptr)); /* entry was a list */ if (SHTABLE_Y(t, e) != THE_NON_VALUE) { - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] untabling L %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] untabling L %p\n", mypid, ptr)); CAR(ptr) = SHTABLE_X(t, e); CDR(ptr) = SHTABLE_Y(t, e); } /* entry was boxed */ else { - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] untabling B %p\n", myself->common.id, ptr)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] untabling B %p\n", mypid, ptr)); *ptr = SHTABLE_X(t, e); ASSERT(primary_tag(*ptr) == TAG_PRIMARY_HEADER); } @@ -1769,9 +1763,9 @@ all_clean: } #endif - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] original was %T\n", myself->common.id, saved_obj)); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy is %T\n", myself->common.id, result)); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] result is at %p\n", myself->common.id, result)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] original was %T\n", mypid, saved_obj)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy is %T\n", mypid, result)); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] result is at %p\n", mypid, result)); ASSERT(hbot == hp); ASSERT(size == ((hp - *hpp) + (hend - hbot))); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index f106b941ef..71d072fa7e 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -982,6 +982,12 @@ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2); /* beam_bif_load.c */ Eterm erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp); +typedef struct { + Eterm *ptr; + Uint sz; +} copy_literals_t; + +extern copy_literals_t erts_clrange; /* beam_load.c */ typedef struct { @@ -1063,6 +1069,8 @@ typedef struct { Eterm* shtable_start; ErtsAlcType_t shtable_alloc_type; Uint literal_size; + Eterm *range_ptr; + Uint range_sz; } erts_shcopy_t; #define INITIALIZE_SHCOPY(info) \ @@ -1071,6 +1079,8 @@ do { \ info.bitstore_start = info.bitstore_default; \ info.shtable_start = info.shtable_default; \ info.literal_size = 0; \ + info.range_ptr = erts_clrange.ptr; \ + info.range_sz = erts_clrange.sz; \ } while(0) #define DESTROY_SHCOPY(info) \ -- cgit v1.2.3 From 0d5ee7a4ad7bc41b7cca878990926fb5ba57f6a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 1 Oct 2015 12:19:50 +0200 Subject: Do not use GCC extensions in copy --- erts/emulator/beam/copy.c | 37 ++++++++++++++++++++----------------- erts/emulator/beam/global.h | 25 +++++++++++++++---------- 2 files changed, 35 insertions(+), 27 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 5bca3877b5..83ca527334 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -258,18 +258,19 @@ do { \ } \ } while(0) -#define BITSTORE_GET(s) ({ \ - UWord result; \ - if (WSTK_CONCAT(s,_bitoffs) <= 0) { \ - WSTK_CONCAT(s,_buffer) = s.wstart[WSTK_CONCAT(s,_offset)]; \ - WSTK_CONCAT(s,_offset)++; \ - WSTK_CONCAT(s,_bitoffs) = 8*sizeof(UWord); \ - } \ - WSTK_CONCAT(s,_bitoffs) -= 2; \ - result = WSTK_CONCAT(s,_buffer) & 3; \ - WSTK_CONCAT(s,_buffer) >>= 2; \ - result; \ -}) +#define BITSTORE_FETCH(s,dst) \ +do { \ + UWord result; \ + if (WSTK_CONCAT(s,_bitoffs) <= 0) { \ + WSTK_CONCAT(s,_buffer) = s.wstart[WSTK_CONCAT(s,_offset)]; \ + WSTK_CONCAT(s,_offset)++; \ + WSTK_CONCAT(s,_bitoffs) = 8*sizeof(UWord); \ + } \ + WSTK_CONCAT(s,_bitoffs) -= 2; \ + result = WSTK_CONCAT(s,_buffer) & 3; \ + WSTK_CONCAT(s,_buffer) >>= 2; \ + (dst) = result; \ +} while(0) #define BOXED_VISITED_MASK ((Eterm) 3) #define BOXED_VISITED ((Eterm) 1) @@ -476,7 +477,8 @@ cleanup: /* if not already clean, clean it up */ if (primary_tag(tail) == TAG_PRIMARY_HEADER) { if (primary_tag(head) == TAG_PRIMARY_HEADER) { - Eterm saved = BITSTORE_GET(b); + Eterm saved; + BITSTORE_FETCH(b, saved); CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | saved; CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) | TAG_PRIMARY_BOXED; } else { @@ -1396,10 +1398,11 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, /* if not already clean, clean it up and copy it */ if (primary_tag(tail) == TAG_PRIMARY_HEADER) { if (primary_tag(head) == TAG_PRIMARY_HEADER) { - Eterm saved = BITSTORE_GET(b); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/B %p\n", mypid, ptr)); - CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) + saved; - CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_BOXED; + Eterm saved; + BITSTORE_FETCH(b, saved); + VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/B %p\n", mypid, ptr)); + CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) + saved; + CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_BOXED; } else { VERBOSE(DEBUG_SHCOPY, ("[pid=%T] unmangling L/L %p\n", mypid, ptr)); CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) + TAG_PRIMARY_LIST; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 71d072fa7e..e9f7901a51 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -873,7 +873,7 @@ typedef struct { int possibly_empty; Eterm* end; ErtsAlcType_t alloc_type; -}ErtsEQueue; +} ErtsEQueue; #define DEF_EQUEUE_SIZE (16) @@ -918,15 +918,20 @@ do { \ #define EQUEUE_ISEMPTY(q) (q.back == q.front && q.possibly_empty) -#define EQUEUE_GET(q) ({ \ - UWord x; \ - q.possibly_empty = 1; \ - x = *(q.front); \ - if (++(q.front) == q.end) { \ - q.front = q.start; \ - } \ - x; \ -}) +ERTS_GLB_INLINE Eterm erts_equeue_get(ErtsEQueue *q); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF +ERTS_GLB_INLINE Eterm erts_equeue_get(ErtsEQueue *q) { + Eterm x; + q->possibly_empty = 1; + x = *(q->front); + if (++(q->front) == q->end) { + q->front = q->start; + } + return x; +} +#endif +#define EQUEUE_GET(q) erts_equeue_get(&(q)); /* binary.c */ -- cgit v1.2.3 From 7f215c6db8caf0760161437e9d1c3c0a06cf5841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 25 Sep 2015 18:20:10 +0200 Subject: Add copy_literals testcase Repeatedly reload a literals module while sending the references literals around in a process ring. This will smoke test the non-copying literals message sending does not corrupt code unloading. --- erts/emulator/test/code_SUITE.erl | 82 ++++++++++++++++++++++++- erts/emulator/test/code_SUITE_data/literals.erl | 7 +++ 2 files changed, 87 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 9f318a38be..1acc4538fb 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -26,7 +26,8 @@ t_check_process_code_ets/1, external_fun/1,get_chunk/1,module_md5/1,make_stub/1, make_stub_many_funs/1,constant_pools/1,constant_refc_binaries/1, - false_dependency/1,coverage/1,fun_confusion/1]). + false_dependency/1,coverage/1,fun_confusion/1, + t_copy_literals/1]). -define(line_trace, 1). -include_lib("test_server/include/test_server.hrl"). @@ -38,7 +39,7 @@ all() -> t_check_process_code_ets, t_check_old_code, external_fun, get_chunk, module_md5, make_stub, make_stub_many_funs, constant_pools, constant_refc_binaries, false_dependency, - coverage, fun_confusion]. + coverage, fun_confusion, t_copy_literals]. groups() -> []. @@ -753,6 +754,80 @@ compile_load(Mod, Src, Ver) -> {module,Mod} = code:load_binary(Mod, "fun_confusion.beam", Code1), ok. + +t_copy_literals(Config) when is_list(Config) -> + %% Compile the the literals module. + Data = ?config(data_dir, Config), + File = filename:join(Data, "literals"), + {ok,literals,Code} = compile:file(File, [report,binary]), + {module,literals} = erlang:load_module(literals, Code), + + N = 30, + Me = self(), + %% reload literals code every 567 ms + Rel = spawn_link(fun() -> reloader(literals,Code,567) end), + %% add new literal msgs to the loop every 789 ms + Sat = spawn_link(fun() -> saturate(Me,789) end), + %% run for 10s + _ = spawn_link(fun() -> receive after 10000 -> Me ! done end end), + ok = chase_msg(N, Me), + %% cleanup + Rel ! done, + Sat ! done, + ok = flush(), + ok. + + +chase_msg(0, Pid) -> + chase_loop(Pid); +chase_msg(N, Master) -> + Pid = spawn_link(fun() -> chase_msg(N - 1,Master) end), + chase_loop(Pid). + +chase_loop(Pid) -> + receive + done -> + Pid ! done, + ok; + {_From,Msg} -> + Pid ! {self(), Msg}, + ok = traverse(Msg), + chase_loop(Pid) + end. + +saturate(Pid,Time) -> + Es = [msg1,msg2,msg3,msg4,msg5], + Msg = [literals:E()||E <- Es], + Pid ! {self(), Msg}, + receive + done -> ok + after Time -> + saturate(Pid,Time) + end. + +traverse([]) -> ok; +traverse([H|T]) -> + ok = traverse(H), + traverse(T); +traverse(T) when is_tuple(T) -> ok; +traverse(B) when is_binary(B) -> ok; +traverse(I) when is_integer(I) -> ok; +traverse(#{ 1 := V1, b := V2 }) -> + ok = traverse(V1), + ok = traverse(V2), + ok. + + +reloader(Mod,Code,Time) -> + receive + done -> ok + after Time -> + code:purge(Mod), + {module,Mod} = erlang:load_module(Mod, Code), + reloader(Mod,Code,Time) + end. + + %% Utilities. make_sub_binary(Bin) when is_binary(Bin) -> @@ -775,4 +850,7 @@ bit_sized_binary(Bin0) -> BitSize = 8*size(Bin) + 1, Bin. +flush() -> + receive _ -> flush() after 0 -> ok end. + id(I) -> I. diff --git a/erts/emulator/test/code_SUITE_data/literals.erl b/erts/emulator/test/code_SUITE_data/literals.erl index 9802d9d3f9..a36bfe09dd 100644 --- a/erts/emulator/test/code_SUITE_data/literals.erl +++ b/erts/emulator/test/code_SUITE_data/literals.erl @@ -20,6 +20,7 @@ -module(literals). -export([a/0,b/0,huge_bignum/0,binary/0,unused_binaries/0,bits/0]). +-export([msg1/0,msg2/0,msg3/0,msg4/0,msg5/0]). a() -> {a,42.0,[7,38877938333399637266518333334747]}. @@ -101,3 +102,9 @@ unused_binaries() -> bits() -> {bits,<<42:13,?MB_1>>}. + +msg1() -> "halloj". +msg2() -> {"hello","world"}. +msg3() -> <<"halloj">>. +msg4() -> #{ 1=> "hello", b => "world"}. +msg5() -> {1,2,3,4,5,6}. -- cgit v1.2.3 From 85491375da14ab087ec99532ee3a896ff6fe0371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 29 Sep 2015 15:33:18 +0200 Subject: Add erlang:copy_literals/2 spec --- erts/preloaded/src/erlang.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 6a9ec9c915..0d5176019f 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -91,7 +91,7 @@ -export([bit_size/1, bitsize/1, bitstring_to_list/1]). -export([bump_reductions/1, byte_size/1, call_on_load_function/1]). -export([cancel_timer/1, cancel_timer/2, check_old_code/1, check_process_code/2, - check_process_code/3, crc32/1]). + check_process_code/3, copy_literals/2, crc32/1]). -export([crc32/2, crc32_combine/3, date/0, decode_packet/3]). -export([delete_element/2]). -export([delete_module/1, demonitor/1, demonitor/2, display/1]). @@ -520,6 +520,13 @@ get_cpc_opts([{allow_gc, AllowGC} | Options], Async, _OldAllowGC) -> get_cpc_opts([], Async, AllowGC) -> {Async, AllowGC}. +%% copy_literals/2 +-spec erlang:copy_literals(Module,Bool) -> 'true' | 'false' | 'aborted' when + Module :: module(), + Bool :: boolean(). +copy_literals(_Mod, _Bool) -> + erlang:nif_error(undefined). + %% crc32/1 -spec erlang:crc32(Data) -> non_neg_integer() when Data :: iodata(). -- cgit v1.2.3 From da8220501710184f3ee80337bac57d215560ea64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 29 Sep 2015 15:35:54 +0200 Subject: Update preloaded module erlang.beam --- erts/preloaded/ebin/erlang.beam | Bin 101544 -> 101816 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 641fac2d26..4f35928db2 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ -- cgit v1.2.3 From 15c26af0a35fb482408885ea90b3e669d64d71f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 Nov 2015 16:26:35 +0100 Subject: Fix erts_debug:copy_shared/1 prototype --- erts/emulator/beam/bif.tab | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 63a0d0b1f0..c49a3ff313 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -646,7 +646,7 @@ bif erlang:copy_literals/2 bif binary:split/2 bif binary:split/3 bif erts_debug:size_shared/1 -bif 'erl.system.debug':size_shared/1 ebif_erts_debug_size_shared_1 +bif erts_debug:copy_shared/1 # # Obsolete -- cgit v1.2.3 From 3f29cf1f72f50d20ce1864f76e1a298602429ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 18 Nov 2015 16:59:08 +0100 Subject: Fix rebase of SHCOPY seq_tokens --- erts/emulator/beam/erl_message.c | 55 ++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 27 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 11890a756d..66b337402d 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -691,22 +691,15 @@ erts_send_message(Process* sender, #ifdef USE_VM_PROBES Uint dt_utag_size = 0; #endif -#ifdef SHCOPY_SEND - unsigned shflags = (flags & ERTS_SND_FLG_SHCOPY_MASK) >> ERTS_SND_FLG_SHCOPY_SHIFT; - erts_shcopy_t info; - INITIALIZE_SHCOPY(info); -#endif - BM_SWAP_TIMER(send,size); -#ifdef SHCOPY_SEND - INITIALIZE_SHCOPY(info); - msize = copy_shared_calculate(message, &info, shflags); -#else - msize = size_object(message); -#endif - BM_SWAP_TIMER(size,send); + BM_SWAP_TIMER(send,size); + /* SHCOPY corrupts the heap between + * copy_shared_calculate, and + * copy_shared_perform. (it inserts move_markers like the gc). + * Make sure we don't use the heap between those instances. + */ #ifdef USE_VM_PROBES - if (stoken != am_have_dt_utag) { + if (stoken != am_have_dt_utag) { #endif seq_trace_update_send(sender); seq_trace_output(stoken, message, SEQ_TRACE_SEND, @@ -714,23 +707,31 @@ erts_send_message(Process* sender, seq_trace_size = 6; /* TUPLE5 */ #ifdef USE_VM_PROBES } - if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) { - dt_utag_size = size_object(DT_UTAG(sender)); - } else if (stoken == am_have_dt_utag ) { - stoken = NIL; - } + if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) { + dt_utag_size = size_object(DT_UTAG(sender)); + } else if (stoken == am_have_dt_utag ) { + stoken = NIL; + } +#endif + +#ifdef SHCOPY_SEND + INITIALIZE_SHCOPY(info); + msize = copy_shared_calculate(message, &info, shflags); +#else + msize = size_object(message); #endif + BM_SWAP_TIMER(size,send); - mp = erts_alloc_message_heap_state(receiver, - &receiver_state, - receiver_locks, - (msize + mp = erts_alloc_message_heap_state(receiver, + &receiver_state, + receiver_locks, + (msize #ifdef USE_VM_PROBES - + dt_utag_size + + dt_utag_size #endif - + seq_trace_size), - &hp, - &ohp); + + seq_trace_size), + &hp, + &ohp); BM_SWAP_TIMER(send,copy); -- cgit v1.2.3 From 5d764f988ab09326d24e39a172083b09ab364c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 18 Nov 2015 17:58:14 +0100 Subject: Refactor sharing preserved copy flags The TMPBUF option is no longer needed due to is_literal test and NONE was only used for initial debugging. So we remove the entire option. --- erts/emulator/beam/beam_debug.c | 10 ++-------- erts/emulator/beam/copy.c | 22 ++-------------------- erts/emulator/beam/erl_db.c | 4 ++-- erts/emulator/beam/erl_message.c | 9 ++++----- erts/emulator/beam/erl_message.h | 5 ----- erts/emulator/beam/erl_process.c | 5 ++--- erts/emulator/beam/global.h | 10 ++-------- 7 files changed, 14 insertions(+), 51 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index b007d000bf..e37bd4d78c 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -97,20 +97,14 @@ erts_debug_copy_shared_1(BIF_ALIST_1) Eterm* hp; Eterm copy; erts_shcopy_t info; -#ifdef SHCOPY_DISABLE - extern int disable_copy_shared; -#endif INITIALIZE_SHCOPY(info); - size = copy_shared_calculate(term, &info, 0); + size = copy_shared_calculate(term, &info); if (size > 0) { hp = HAlloc(p, size); } - copy = copy_shared_perform(term, size, &info, &hp, &p->off_heap, 0); + copy = copy_shared_perform(term, size, &info, &hp, &p->off_heap); DESTROY_SHCOPY(info); -#ifdef SHCOPY_DISABLE - disable_copy_shared = 0; -#endif BIF_RET(copy); } diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 83ca527334..67a96f6442 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -923,10 +923,6 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint * Using an ESTACK but not very transparently; consider refactoring */ -#ifdef SHCOPY_DISABLE -int disable_copy_shared = ERTS_SHCOPY_FLG_NONE; -#endif - #define DECLARE_SHTABLE(s) \ DECLARE_ESTACK(s); \ Uint ESTK_CONCAT(s,_offset) = 0 @@ -1034,7 +1030,7 @@ do { \ * Copy object "obj" preserving sharing. * First half: count size and calculate sharing. */ -Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) +Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info) { Uint sum; Uint e; @@ -1058,13 +1054,6 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) if (IS_CONST(obj)) return 0; -#ifdef SHCOPY_DISABLE - flags |= disable_copy_shared; -#endif - - if (flags & ERTS_SHCOPY_FLG_NONE) - return size_object(obj); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_shared_calculate %p\n", mypid, obj)); VERBOSE(DEBUG_SHCOPY, ("[pid=%T] message is %T\n", mypid, obj)); @@ -1299,7 +1288,7 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info, Uint32 flags) * Second half: copy and restore the object. */ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, - Eterm** hpp, ErlOffHeap* off_heap, Uint32 flags) { + Eterm** hpp, ErlOffHeap* off_heap) { Uint e; unsigned sz; Eterm* ptr; @@ -1328,13 +1317,6 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info, if (IS_CONST(obj)) return obj; -#ifdef SHCOPY_DISABLE - flags |= disable_copy_shared; -#endif - - if (flags & ERTS_SHCOPY_FLG_NONE) - return copy_struct(obj, size, hpp, off_heap); - VERBOSE(DEBUG_SHCOPY, ("[pid=%T] copy_shared_perform %p\n", mypid, obj)); /* step #2: was performed before this function was called diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 59a0c0b808..3030c1c91a 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -1832,7 +1832,7 @@ BIF_RETTYPE ets_give_away_3(BIF_ALIST_3) tb->common.id, from_pid, BIF_ARG_3), - ERTS_SND_FLG_SHCOPY_TMPBUF); + 0); erts_smp_proc_unlock(to_proc, to_locks); UnUseTmpHeap(5,BIF_P); BIF_RET(am_true); @@ -3211,7 +3211,7 @@ retry: tb->common.id, p->common.id, heir_data), - ERTS_SND_FLG_SHCOPY_TMPBUF); + 0); erts_smp_proc_unlock(to_proc, to_locks); return !0; } diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 66b337402d..a964f1968b 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -665,7 +665,6 @@ erts_send_message(Process* sender, #endif erts_aint32_t receiver_state; #ifdef SHCOPY_SEND - unsigned shflags = (flags & ERTS_SND_FLG_SHCOPY_MASK) >> ERTS_SND_FLG_SHCOPY_SHIFT; erts_shcopy_t info; #endif BM_STOP_TIMER(system); @@ -716,7 +715,7 @@ erts_send_message(Process* sender, #ifdef SHCOPY_SEND INITIALIZE_SHCOPY(info); - msize = copy_shared_calculate(message, &info, shflags); + msize = copy_shared_calculate(message, &info); #else msize = size_object(message); #endif @@ -737,7 +736,7 @@ erts_send_message(Process* sender, #ifdef SHCOPY_SEND if (is_not_immed(message)) - message = copy_shared_perform(message, msize, &info, &hp, ohp, shflags); + message = copy_shared_perform(message, msize, &info, &hp, ohp); DESTROY_SHCOPY(info); #else if (is_not_immed(message)) @@ -786,7 +785,7 @@ erts_send_message(Process* sender, BM_SWAP_TIMER(send,size); #ifdef SHCOPY_SEND INITIALIZE_SHCOPY(info); - msize = copy_shared_calculate(message, &info, shflags); + msize = copy_shared_calculate(message, &info); #else msize = size_object(message); #endif @@ -801,7 +800,7 @@ erts_send_message(Process* sender, BM_SWAP_TIMER(send,copy); #ifdef SHCOPY_SEND if (is_not_immed(message)) - message = copy_shared_perform(message, msize, &info, &hp, ohp, shflags); + message = copy_shared_perform(message, msize, &info, &hp, ohp); DESTROY_SHCOPY(info); #else if (is_not_immed(message)) diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 52a648fe0b..740ae46a0f 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -238,11 +238,6 @@ do { \ #define ERTS_SND_FLG_NO_SEQ_TRACE (((unsigned) 1) << 0) -#define ERTS_SND_FLG_SHCOPY_SHIFT 1 -#define ERTS_SND_FLG_SHCOPY_MASK (ERTS_SHCOPY_FLG_MASK << ERTS_SND_FLG_SHCOPY_SHIFT) -#define ERTS_SND_FLG_SHCOPY_NONE (ERTS_SHCOPY_FLG_NONE << ERTS_SND_FLG_SHCOPY_SHIFT) -#define ERTS_SND_FLG_SHCOPY_TMPBUF (ERTS_SHCOPY_FLG_TMPBUF << ERTS_SND_FLG_SHCOPY_SHIFT) - #define ERTS_HEAP_FRAG_SIZE(DATA_WORDS) \ (sizeof(ErlHeapFragment) - sizeof(Eterm) + (DATA_WORDS)*sizeof(Eterm)) diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a691a3c773..23616f36bf 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -10748,7 +10748,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). erts_aint32_t state = 0; erts_aint32_t prio = (erts_aint32_t) PRIORITY_NORMAL; #ifdef SHCOPY_SPAWN - unsigned shflags = 0; /* could be taken from so->flags, if necessary */ erts_shcopy_t info; INITIALIZE_SHCOPY(info); #endif @@ -10804,7 +10803,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). BM_SWAP_TIMER(system,size); #ifdef SHCOPY_SPAWN - arg_size = copy_shared_calculate(args, &info, shflags); + arg_size = copy_shared_calculate(args, &info); #else arg_size = size_object(args); #endif @@ -10886,7 +10885,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). BM_START_TIMER(system); BM_SWAP_TIMER(system,copy); #ifdef SHCOPY_SPAWN - p->arg_reg[2] = copy_shared_perform(args, arg_size, &info, &p->htop, &p->off_heap, shflags); + p->arg_reg[2] = copy_shared_perform(args, arg_size, &info, &p->htop, &p->off_heap); DESTROY_SHCOPY(info); #else p->arg_reg[2] = copy_struct(args, arg_size, &p->htop, &p->off_heap); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index e9f7901a51..98c275a20c 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1052,14 +1052,8 @@ void erl_error(char*, va_list); #ifdef SHCOPY #define SHCOPY_SEND #define SHCOPY_SPAWN -/* Use this if you want sharing-preserving copy to be initially disabled */ -#undef SHCOPY_DISABLE #endif -#define ERTS_SHCOPY_FLG_MASK (((Uint32) 3) << 0) -#define ERTS_SHCOPY_FLG_NONE (((Uint32) 1) << 0) -#define ERTS_SHCOPY_FLG_TMPBUF (((Uint32) 1) << 1) /* forces INHEAP to true */ - /* The persistent state while the sharing-preserving copier works */ typedef struct { @@ -1106,8 +1100,8 @@ Eterm copy_object_x(Eterm, Process*, Uint); #define copy_object(Term, Proc) copy_object_x(Term,Proc,0) Uint size_object(Eterm); -Uint copy_shared_calculate(Eterm, erts_shcopy_t*, Uint32); -Eterm copy_shared_perform(Eterm, Uint, erts_shcopy_t*, Eterm**, ErlOffHeap*, Uint32); +Uint copy_shared_calculate(Eterm, erts_shcopy_t*); +Eterm copy_shared_perform(Eterm, Uint, erts_shcopy_t*, Eterm**, ErlOffHeap*); Uint size_shared(Eterm); -- cgit v1.2.3 From 8b93e77a6d3df65e45a3ca3e2ec9dd4c52464f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 18 Nov 2015 18:47:55 +0100 Subject: Use sharing preserving copy in enif_make_copy --- erts/emulator/beam/erl_nif.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index a37cda93ef..2ff509e6d0 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -379,9 +379,19 @@ ERL_NIF_TERM enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term) { Uint sz; Eterm* hp; +#ifdef SHCOPY + erts_shcopy_t info; + INITIALIZE_SHCOPY(info); + sz = copy_shared_calculate(src_term, &info); + hp = alloc_heap(dst_env, sz); + src_term = copy_shared_perform(src_term, sz, &info, &hp, &MSO(dst_env->proc)); + DESTROY_SHCOPY(info); + return src_term; +#else sz = size_object(src_term); hp = alloc_heap(dst_env, sz); return copy_struct(src_term, sz, &hp, &MSO(dst_env->proc)); +#endif } -- cgit v1.2.3 From 02f038355d16e9b6474837727878f58e4ca669c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 18 Nov 2015 20:12:38 +0100 Subject: Use sharing preserving copy error messages and exceptions --- erts/emulator/beam/erl_message.c | 28 +++++++++++++++++++++++++--- erts/emulator/beam/erl_process.c | 33 +++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index a964f1968b..f64385d92b 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -850,6 +850,9 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, Eterm temptoken; ErtsMessage* mp; ErlOffHeap *ohp; +#ifdef SHCOPY_SEND + erts_shcopy_t info; +#endif if (token != NIL #ifdef USE_VM_PROBES @@ -858,13 +861,23 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, ) { ASSERT(is_tuple(token)); - sz_reason = size_object(reason); sz_token = size_object(token); sz_from = size_object(from); +#ifdef SHCOPY_SEND + INITIALIZE_SHCOPY(info); + sz_reason = copy_shared_calculate(reason, &info); +#else + sz_reason = size_object(reason); +#endif mp = erts_alloc_message_heap(to, to_locksp, sz_reason + sz_from + sz_token + 4, &hp, &ohp); +#ifdef SHCOPY_SEND + mess = copy_shared_perform(reason, sz_reason, &info, &hp, ohp); + DESTROY_SHCOPY(info); +#else mess = copy_struct(reason, sz_reason, &hp, ohp); +#endif from_copy = copy_struct(from, sz_from, &hp, ohp); save = TUPLE3(hp, am_EXIT, from_copy, mess); hp += 4; @@ -873,13 +886,22 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, temptoken = copy_struct(token, sz_token, &hp, ohp); erts_queue_message(to, to_locksp, mp, save, temptoken); } else { - sz_reason = size_object(reason); sz_from = IS_CONST(from) ? 0 : size_object(from); - +#ifdef SHCOPY_SEND + INITIALIZE_SHCOPY(info); + sz_reason = copy_shared_calculate(reason, &info); +#else + sz_reason = size_object(reason); +#endif mp = erts_alloc_message_heap(to, to_locksp, sz_reason+sz_from+4, &hp, &ohp); +#ifdef SHCOPY_SEND + mess = copy_shared_perform(reason, sz_reason, &info, &hp, ohp); + DESTROY_SHCOPY(info); +#else mess = copy_struct(reason, sz_reason, &hp, ohp); +#endif from_copy = (IS_CONST(from) ? from : copy_struct(from, sz_from, &hp, ohp)); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 23616f36bf..f347d57a9d 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11520,31 +11520,44 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, { ErtsMessage *mp; ErlOffHeap *ohp; + Eterm* hp; + Eterm mess; +#ifdef SHCOPY_SEND + erts_shcopy_t info; +#endif if (token == NIL #ifdef USE_VM_PROBES || token == am_have_dt_utag #endif ) { - Eterm* hp; - Eterm mess; - - mp = erts_alloc_message_heap(to, to_locksp, - term_size, &hp, &ohp); +#ifdef SHCOPY_SEND + INITIALIZE_SHCOPY(info); + term_size = copy_shared_calculate(exit_term, &info); + mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp); + mess = copy_shared_perform(exit_term, term_size, &info, &hp, ohp); + DESTROY_SHCOPY(info); +#else + mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp); mess = copy_struct(exit_term, term_size, &hp, ohp); +#endif erts_queue_message(to, to_locksp, mp, mess, NIL); } else { - Eterm* hp; - Eterm mess; Eterm temp_token; Uint sz_token; ASSERT(is_tuple(token)); sz_token = size_object(token); - - mp = erts_alloc_message_heap(to, to_locksp, - term_size+sz_token, &hp, &ohp); +#ifdef SHCOPY_SEND + INITIALIZE_SHCOPY(info); + term_size = copy_shared_calculate(exit_term, &info); + mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp); + mess = copy_shared_perform(exit_term, term_size, &info, &hp, ohp); + DESTROY_SHCOPY(info); +#else + mp = erts_alloc_message_heap(to, to_locksp, term_size+sz_token, &hp, &ohp); mess = copy_struct(exit_term, term_size, &hp, ohp); +#endif /* the trace token must in this case be updated by the caller */ seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, NULL); temp_token = copy_struct(token, sz_token, &hp, ohp); -- cgit v1.2.3 From 4580ee5b133f680cf808ea12ebc0c930e97643b2 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 19 Nov 2015 12:24:48 +0100 Subject: Fix error propagate from setopts --- erts/emulator/drivers/common/inet_drv.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 3c6922eb8e..6fff863fa6 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2013. All Rights Reserved. + * Copyright Ericsson AB 1997-2015. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -6051,9 +6051,9 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) int arg_sz; enum PacketParseType old_htype = desc->htype; int old_active = desc->active; - int propagate = 0; /* Set to 1 if failure to set this option - should be propagated to erlang (not all - errors can be propagated for BC reasons) */ + int propagate; /* Set to 1 if failure to set this option + should be propagated to erlang (not all + errors can be propagated for BC reasons) */ int res; #ifdef HAVE_SCTP /* SCTP sockets are treated completely separately: */ @@ -6070,6 +6070,7 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) arg_ptr = (char*) &ival; arg_sz = sizeof(ival); proto = SOL_SOCKET; + propagate = 0; switch(opt) { case INET_LOPT_HEADER: -- cgit v1.2.3 From 6ff15f23c68db356bbad6ab5f939c191b58d453d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 19 Nov 2015 19:36:08 +0100 Subject: Refactor have seq_trace token test --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/bif.c | 6 +----- erts/emulator/beam/dist.c | 24 ++++++------------------ erts/emulator/beam/erl_bif_trace.c | 26 +++++--------------------- erts/emulator/beam/erl_db_util.c | 12 ++---------- erts/emulator/beam/erl_message.c | 19 ++++++------------- erts/emulator/beam/erl_message.h | 7 +++++++ erts/emulator/beam/erl_process.c | 12 ++---------- erts/emulator/beam/erl_trace.c | 6 +----- 9 files changed, 31 insertions(+), 83 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index af97cba61c..1a4133bceb 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1963,7 +1963,7 @@ void process_main(void) dtrace_proc_str(c_p, receiver_name); token2 = SEQ_TRACE_TOKEN(c_p); - if (token2 != NIL && token2 != am_have_dt_utag) { + if (have_seqtrace(token2)) { tok_label = signed_val(SEQ_TRACE_T_LABEL(token2)); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token2)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token2)); diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index f0340540cb..14ab113b32 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2038,11 +2038,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) save_calls(p, &exp_send); - if (SEQ_TRACE_TOKEN(p) != NIL -#ifdef USE_VM_PROBES - && SEQ_TRACE_TOKEN(p) != am_have_dt_utag -#endif - ) { + if (have_seqtrace(SEQ_TRACE_TOKEN(p))) { seq_trace_update_send(p); seq_trace_output(SEQ_TRACE_TOKEN(p), msg, SEQ_TRACE_SEND, portid, p); diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 68745fc448..f480915256 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -881,11 +881,7 @@ erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx) DTRACE_CHARBUF(receiver_name, 64); #endif - if (SEQ_TRACE_TOKEN(sender) != NIL -#ifdef USE_VM_PROBES - && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag -#endif - ) { + if (have_seqtrace(SEQ_TRACE_TOKEN(sender))) { seq_trace_update_send(sender); token = SEQ_TRACE_TOKEN(sender); seq_trace_output(token, message, SEQ_TRACE_SEND, remote, sender); @@ -900,7 +896,7 @@ erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx) erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), "%T", remote); msize = size_object(message); - if (token != NIL && token != am_have_dt_utag) { + if (have_seqtrace(token)) { tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); @@ -942,11 +938,7 @@ erts_dsig_send_reg_msg(Eterm remote_name, Eterm message, DTRACE_CHARBUF(receiver_name, 128); #endif - if (SEQ_TRACE_TOKEN(sender) != NIL -#ifdef USE_VM_PROBES - && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag -#endif - ) { + if (have_seqtrace(SEQ_TRACE_TOKEN(sender))) { seq_trace_update_send(sender); token = SEQ_TRACE_TOKEN(sender); seq_trace_output(token, message, SEQ_TRACE_SEND, remote_name, sender); @@ -961,7 +953,7 @@ erts_dsig_send_reg_msg(Eterm remote_name, Eterm message, erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), "{%T,%s}", remote_name, node_name); msize = size_object(message); - if (token != NIL && token != am_have_dt_utag) { + if (have_seqtrace(token)) { tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); @@ -1006,11 +998,7 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote, #endif UseTmpHeapNoproc(6); - if (token != NIL -#ifdef USE_VM_PROBES - && token != am_have_dt_utag -#endif - ) { + if (have_seqtrace(token)) { seq_trace_update_send(dsdp->proc); seq_trace_output_exit(token, reason, SEQ_TRACE_SEND, remote, local); ctl = TUPLE5(&ctl_heap[0], @@ -1029,7 +1017,7 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote, "{%T,%s}", remote, node_name); erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)), "%T", reason); - if (token != NIL && token != am_have_dt_utag) { + if (have_seqtrace(token)) { tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 03f51132b1..4d67e39e7e 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -1879,11 +1879,7 @@ new_seq_trace_token(Process* p) { Eterm* hp; - if (SEQ_TRACE_TOKEN(p) == NIL -#ifdef USE_VM_PROBES - || SEQ_TRACE_TOKEN(p) == am_have_dt_utag -#endif - ) { + if (have_no_seqtrace(SEQ_TRACE_TOKEN(p))) { hp = HAlloc(p, 6); SEQ_TRACE_TOKEN(p) = TUPLE5(hp, make_small(0), /* Flags */ make_small(0), /* Label */ @@ -1903,12 +1899,8 @@ BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item) BIF_ERROR(p, BADARG); } - if (SEQ_TRACE_TOKEN(p) == NIL -#ifdef USE_VM_PROBES - || SEQ_TRACE_TOKEN(p) == am_have_dt_utag -#endif - ) { - if ((item == am_send) || (item == am_receive) || + if (have_no_seqtrace(SEQ_TRACE_TOKEN(p))) { + if ((item == am_send) || (item == am_receive) || (item == am_print) || (item == am_timestamp)) { hp = HAlloc(p,3); res = TUPLE2(hp, item, am_false); @@ -1964,11 +1956,7 @@ BIF_RETTYPE seq_trace_info_1(BIF_ALIST_1) */ BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1) { - if (SEQ_TRACE_TOKEN(BIF_P) == NIL -#ifdef USE_VM_PROBES - || SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag -#endif - ) { + if (have_no_seqtrace(SEQ_TRACE_TOKEN(BIF_P))) { BIF_RET(am_false); } seq_trace_update_send(BIF_P); @@ -1987,11 +1975,7 @@ BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1) */ BIF_RETTYPE seq_trace_print_2(BIF_ALIST_2) { - if (SEQ_TRACE_TOKEN(BIF_P) == NIL -#ifdef USE_VM_PROBES - || SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag -#endif - ) { + if (have_no_seqtrace(SEQ_TRACE_TOKEN(BIF_P))) { BIF_RET(am_false); } if (!(is_atom(BIF_ARG_1) || is_small(BIF_ARG_1))) { diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 0bf9558ac9..399be6058c 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2308,11 +2308,7 @@ restart: *esp++ = am_true; break; case matchIsSeqTrace: - if (SEQ_TRACE_TOKEN(c_p) != NIL -#ifdef USE_VM_PROBES - && SEQ_TRACE_TOKEN(c_p) != am_have_dt_utag -#endif - ) + if (have_seqtrace(SEQ_TRACE_TOKEN(c_p))) *esp++ = am_true; else *esp++ = am_false; @@ -2336,11 +2332,7 @@ restart: --esp; break; case matchGetSeqToken: - if (SEQ_TRACE_TOKEN(c_p) == NIL -#ifdef USE_VM_PROBES - || SEQ_TRACE_TOKEN(c_p) == am_have_dt_utag -#endif - ) + if (have_no_seqtrace(SEQ_TRACE_TOKEN(c_p))) *esp++ = NIL; else { Eterm sender = SEQ_TRACE_TOKEN_SENDER(c_p); diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index f64385d92b..1811651a58 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -314,7 +314,7 @@ erts_queue_dist_message(Process *rcvr, DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); dtrace_proc_str(rcvr, receiver_name); - if (token != NIL && token != am_have_dt_utag) { + if (have_seqtrace(token)) { tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); @@ -335,7 +335,7 @@ erts_queue_dist_message(Process *rcvr, DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); dtrace_proc_str(rcvr, receiver_name); - if (token != NIL && token != am_have_dt_utag) { + if (have_seqtrace(token)) { tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); @@ -697,15 +697,13 @@ erts_send_message(Process* sender, * copy_shared_perform. (it inserts move_markers like the gc). * Make sure we don't use the heap between those instances. */ -#ifdef USE_VM_PROBES - if (stoken != am_have_dt_utag) { -#endif + if (have_seqtrace(stoken)) { seq_trace_update_send(sender); seq_trace_output(stoken, message, SEQ_TRACE_SEND, receiver->common.id, sender); seq_trace_size = 6; /* TUPLE5 */ -#ifdef USE_VM_PROBES } +#ifdef USE_VM_PROBES if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) { dt_utag_size = size_object(DT_UTAG(sender)); } else if (stoken == am_have_dt_utag ) { @@ -765,7 +763,7 @@ erts_send_message(Process* sender, #ifdef USE_VM_PROBES if (DTRACE_ENABLED(message_send)) { - if (stoken != NIL && stoken != am_have_dt_utag) { + if (have_seqtrace(stoken)) { tok_label = signed_val(SEQ_TRACE_T_LABEL(stoken)); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(stoken)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(stoken)); @@ -854,12 +852,7 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, erts_shcopy_t info; #endif - if (token != NIL -#ifdef USE_VM_PROBES - && token != am_have_dt_utag -#endif - ) { - + if (have_seqtrace(token)) { ASSERT(is_tuple(token)); sz_token = size_object(token); sz_from = size_object(from); diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 740ae46a0f..76387bc34c 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -128,6 +128,13 @@ struct erl_heap_fragment { #else #endif +#ifdef USE_VM_PROBES +#define have_no_seqtrace(T) ((T) == NIL || (T) == am_have_dt_utag) +#else +#define have_no_seqtrace(T) ((T) == NIL) +#endif +#define have_seqtrace(T) (!have_no_seqtrace(T)) + #define ERL_MESSAGE_REF_FIELDS__ \ ErtsMessage *next; /* Next message */ \ union { \ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index f347d57a9d..5907dd4567 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11526,11 +11526,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, erts_shcopy_t info; #endif - if (token == NIL -#ifdef USE_VM_PROBES - || token == am_have_dt_utag -#endif - ) { + if (!have_seqtrace(token)) { #ifdef SHCOPY_SEND INITIALIZE_SHCOPY(info); term_size = copy_shared_calculate(exit_term, &info); @@ -11670,11 +11666,7 @@ send_exit_signal(Process *c_p, /* current process if and only if ((state & ERTS_PSFLG_TRAP_EXIT) && (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) { - if (is_not_nil(token) -#ifdef USE_VM_PROBES - && token != am_have_dt_utag -#endif - && token_update) + if (have_seqtrace(token)) seq_trace_update_send(token_update); if (is_value(exit_tuple)) send_exit_message(rp, rp_locks, exit_tuple, exit_tuple_sz, token); diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index d02f1f7213..b774ba97af 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1046,11 +1046,7 @@ seq_trace_update_send(Process *p) { Eterm seq_tracer = erts_get_system_seq_tracer(); ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p)))); - if ( (p->common.id == seq_tracer) || (SEQ_TRACE_TOKEN(p) == NIL) -#ifdef USE_VM_PROBES - || (SEQ_TRACE_TOKEN(p) == am_have_dt_utag) -#endif - ) { + if ((p->common.id == seq_tracer) || have_no_seqtrace(SEQ_TRACE_TOKEN(p))) { return 0; } SEQ_TRACE_TOKEN_SENDER(p) = p->common.id; -- cgit v1.2.3 From 7dd3fcb287c6d307ce28800d79d1e4c86a533952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 20 Nov 2015 16:07:41 +0100 Subject: erts: Fix maps decode in erlang:binary_to_term/1 Decoding a term with a large (HAMT) map in an small (FLAT) map could cause a critical error if the external format was not produced by beam. --- erts/emulator/beam/external.c | 76 +++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 39 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index c6d7e3fcc5..a85aa15403 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1179,7 +1179,7 @@ typedef struct { ErtsHeapFactory factory; int remaining_n; char* remaining_bytes; - Eterm* maps_list; + ErtsWStack flat_maps; ErtsPStack hamt_array; } B2TDecodeContext; @@ -1519,7 +1519,7 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar ctx->u.dc.res = (Eterm) (UWord) NULL; ctx->u.dc.next = &ctx->u.dc.res; erts_factory_proc_prealloc_init(&ctx->u.dc.factory, p, ctx->heap_size); - ctx->u.dc.maps_list = NULL; + ctx->u.dc.flat_maps.wstart = NULL; ctx->u.dc.hamt_array.pstart = NULL; ctx->state = B2TDecode; /*fall through*/ @@ -2938,7 +2938,7 @@ dec_term(ErtsDistExternal *edep, int n; ErtsAtomEncoding char_enc; register Eterm* hp; /* Please don't take the address of hp */ - Eterm *maps_list; /* for preprocessing of small maps */ + DECLARE_WSTACK(flat_maps); /* for preprocessing of small maps */ Eterm* next; SWord reds; #ifdef DEBUG @@ -2950,7 +2950,6 @@ dec_term(ErtsDistExternal *edep, next = ctx->u.dc.next; ep = ctx->u.dc.ep; factory = &ctx->u.dc.factory; - maps_list = ctx->u.dc.maps_list; if (ctx->state != B2TDecode) { int n_limit = reds; @@ -3026,15 +3025,18 @@ dec_term(ErtsDistExternal *edep, } } PSTACK_CHANGE_ALLOCATOR(hamt_array, ERTS_ALC_T_SAVED_ESTACK); + WSTACK_CHANGE_ALLOCATOR(flat_maps, ERTS_ALC_T_SAVED_ESTACK); if (ctx->u.dc.hamt_array.pstart) { PSTACK_RESTORE(hamt_array, &ctx->u.dc.hamt_array); } + if (ctx->u.dc.flat_maps.wstart) { + WSTACK_RESTORE(flat_maps, &ctx->u.dc.flat_maps); + } } else { reds = ERTS_SWORD_MAX; next = objp; *next = (Eterm) (UWord) NULL; - maps_list = NULL; } hp = factory->hp; @@ -3595,14 +3597,8 @@ dec_term_atom_common: * vptr, last word for values */ - /* - * Use thing_word to link through decoded maps. - * The list of maps is for later validation. - */ - - mp->thing_word = (Eterm) COMPRESS_POINTER(maps_list); - maps_list = (Eterm *) mp; - + WSTACK_PUSH(flat_maps, (UWord)mp); + mp->thing_word = MAP_HEADER_FLATMAP; mp->size = size; mp->keys = keys; *objp = make_flatmap(mp); @@ -3851,7 +3847,9 @@ dec_term_atom_common: ctx->u.dc.ep = ep; ctx->u.dc.next = next; ctx->u.dc.factory.hp = hp; - ctx->u.dc.maps_list = maps_list; + if (!WSTACK_ISEMPTY(flat_maps)) { + WSTACK_SAVE(flat_maps, &ctx->u.dc.flat_maps); + } if (!PSTACK_IS_EMPTY(hamt_array)) { PSTACK_SAVE(hamt_array, &ctx->u.dc.hamt_array); } @@ -3865,18 +3863,6 @@ dec_term_atom_common: } } - /* Iterate through all the maps and check for validity and sort keys - * - done here for when we know it is complete. - */ - - while (maps_list) { - next = (Eterm *)(EXPAND_POINTER(*maps_list)); - *maps_list = MAP_HEADER_FLATMAP; - if (!erts_validate_and_sort_flatmap((flatmap_t*)maps_list)) - goto error; - maps_list = next; - } - ASSERT(hp <= factory->hp_end || (factory->mode == FACTORY_CLOSED && is_immed(*dbg_resultp))); factory->hp = hp; @@ -3885,20 +3871,31 @@ dec_term_atom_common: */ if (!PSTACK_IS_EMPTY(hamt_array)) { - do { - struct dec_term_hamt* hamt = PSTACK_TOP(hamt_array); - - *hamt->objp = erts_hashmap_from_array(factory, - hamt->leaf_array, - hamt->size, - 1); - if (is_non_value(*hamt->objp)) - goto error_hamt; - - (void) PSTACK_POP(hamt_array); - } while (!PSTACK_IS_EMPTY(hamt_array)); - PSTACK_DESTROY(hamt_array); + do { + struct dec_term_hamt* hamt = PSTACK_TOP(hamt_array); + + *hamt->objp = erts_hashmap_from_array(factory, + hamt->leaf_array, + hamt->size, + 1); + if (is_non_value(*hamt->objp)) + goto error_hamt; + + (void) PSTACK_POP(hamt_array); + } while (!PSTACK_IS_EMPTY(hamt_array)); + PSTACK_DESTROY(hamt_array); + } + + /* Iterate through all the (flat)maps and check for validity and sort keys + * - done here for when we know it is complete. + */ + + while(!WSTACK_ISEMPTY(flat_maps)) { + next = (Eterm *)WSTACK_POP(flat_maps); + if (!erts_validate_and_sort_flatmap((flatmap_t*)next)) + goto error; } + WSTACK_DESTROY(flat_maps); ASSERT((Eterm*)EXPAND_POINTER(*dbg_resultp) != NULL); @@ -3924,6 +3921,7 @@ error_hamt: ctx->state = B2TDecodeFail; ctx->reds = reds; } + WSTACK_DESTROY(flat_maps); return NULL; } -- cgit v1.2.3 From 32934bef5bb7de41252ff9aab64f62ddabeeac33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 20 Nov 2015 17:32:49 +0100 Subject: erts: More testcases for map binary_to_term/1 --- erts/emulator/test/map_SUITE.erl | 110 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 886ae7d516..62a94e5281 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -2181,7 +2181,9 @@ t_map_encode_decode(Config) when is_list(Config) -> {<<>>, sc9}, {3.14158, sc10}, {[3.14158], sc11}, {more_atoms, sc12}, {{more_tuples}, sc13}, {self(), sc14}, - {{},{}},{[],[]} + {{},{}},{[],[]}, + {map_s, #{a=>a, 2=>b, 3=>c}}, + {map_l, maps:from_list([{I,I}||I <- lists:seq(1,74)])} ], ok = map_encode_decode_and_match(Pairs,[],#{}), @@ -2245,9 +2247,30 @@ t_map_encode_decode(Config) when is_list(Config) -> %% bad size (too small) .. should fail just truncate it .. weird. %% possibly change external format so truncated will be #{a:=1} - #{ a:=b } = - erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>), - + #{ a:=b } = erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>), + + %% specific fannerl (opensource app) binary_to_term error in 18.1 + + #{bias := {1,1,0}, + bit_fail := 0, + connections := #{{2,9} := _, + {8,14} := _, + {2,12} := _, + {5,7} := _, + {11,16} := _, + {11,15} := _}, + layers := {5,7,3}, + network_type := fann_nettype_layer, + num_input := 5, + num_layers := 3, + num_output := 3, + rprop_delta_max := _, + rprop_delta_min := _, + total_connections := 66, + total_neurons := 17, + train_error_function := fann_errorfunc_tanh, + train_stop_function := fann_stopfunc_mse, + training_algorithm := fann_train_rprop} = erlang:binary_to_term(fannerl()), ok. map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> @@ -2966,3 +2989,82 @@ do_badmap_17(Config) -> %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. + + +%% map external_format (fannerl). +fannerl() -> + <<131,116,0,0,0,28,100,0,13,108,101,97,114,110,105,110,103,95,114, + 97,116,101,70,63,230,102,102,96,0,0,0,100,0,17,108,101,97,114,110,105,110, + 103,95,109,111,109,101,110,116,117,109,70,0,0,0,0,0,0,0,0,100,0, + 18,116,114,97,105,110,105,110,103,95,97,108,103,111,114,105,116,104,109,100,0, + 16,102,97,110,110,95,116,114,97,105,110,95,114,112,114,111,112, + 100,0,17,109,101,97,110,95,115,113,117,97,114,101,95,101,114,114,111,114,70, + 0,0,0,0,0,0,0,0,100,0,8,98,105,116,95,102,97,105,108,97,0,100,0,20, + 116,114,97,105,110,95,101,114,114,111,114,95,102,117,110,99,116,105,111, + 110,100,0,19,102,97,110,110,95,101,114,114,111,114,102,117,110,99, + 95,116,97,110,104,100,0,9,110,117,109,95,105,110,112,117,116,97,5,100,0,10,110, + 117,109,95,111,117,116,112,117,116,97,3,100,0,13,116,111,116,97,108, + 95,110,101,117,114,111,110,115,97,17,100,0,17,116,111,116,97,108,95,99,111,110, + 110,101,99,116,105,111,110,115,97,66,100,0,12,110,101,116,119,111,114,107, + 95,116,121,112,101,100,0,18,102,97,110,110,95,110,101,116,116,121,112,101, + 95,108,97,121,101,114,100,0,15,99,111,110,110,101,99,116,105,111,110,95, + 114,97,116,101,70,63,240,0,0,0,0,0,0,100,0,10,110,117,109,95,108,97,121,101, + 114,115,97,3,100,0,19,116,114,97,105,110,95,115,116,111,112,95,102,117,110, + 99,116,105,111,110,100,0,17,102,97,110,110,95,115,116,111,112,102,117,110, + 99,95,109,115,101,100,0,15,113,117,105,99,107,112,114,111,112,95,100,101,99, + 97,121,70,191,26,54,226,224,0,0,0,100,0,12,113,117,105,99,107,112,114, + 111,112,95,109,117,70,63,252,0,0,0,0,0,0,100,0,21,114,112,114,111,112,95,105, + 110,99,114,101,97,115,101,95,102,97,99,116,111,114,70,63,243,51,51, + 64,0,0,0,100,0,21,114,112,114,111,112,95,100,101,99,114,101,97,115,101, + 95,102,97,99,116,111,114,70,63,224,0,0,0,0,0,0,100,0,15,114,112,114,111,112, + 95,100,101,108,116,97,95,109,105,110,70,0,0,0,0,0,0,0,0,100,0,15,114,112,114, + 111,112,95,100,101,108,116,97,95,109,97,120,70,64,73,0,0,0,0,0,0,100,0, + 16,114,112,114,111,112,95,100,101,108,116,97,95,122,101,114,111,70,63,185,153, + 153,160,0,0,0,100,0,26,115,97,114,112,114,111,112,95,119,101,105,103, + 104,116,95,100,101,99,97,121,95,115,104,105,102,116,70,192,26,147,116,192,0,0,0, + 100,0,35,115,97,114,112,114,111,112,95,115,116,101,112,95,101,114, + 114,111,114,95,116,104,114,101,115,104,111,108,100,95,102,97,99,116,111,114,70, + 63,185,153,153,160,0,0,0,100,0,24,115,97,114,112,114,111,112,95,115, + 116,101,112,95,101,114,114,111,114,95,115,104,105,102,116,70,63,246,40,245, + 192,0,0,0,100,0,19,115,97,114,112,114,111,112,95,116,101,109,112,101,114, + 97,116,117,114,101,70,63,142,184,81,224,0,0,0,100,0,6,108,97,121,101,114,115, + 104,3,97,5,97,7,97,3,100,0,4,98,105,97,115,104,3,97,1,97,1,97,0,100,0,11, + 99,111,110,110,101,99,116,105,111,110,115,116,0,0,0,66,104,2,97,0,97,6,70, + 191,179,51,44,64,0,0,0,104,2,97,1,97,6,70,63,178,130,90,32,0,0,0,104,2,97,2, + 97,6,70,63,82,90,88,0,0,0,0,104,2,97,3,97,6,70,63,162,91,63,192,0,0,0,104,2, + 97,4,97,6,70,191,151,70,169,0,0,0,0,104,2,97,5,97,6,70,191,117,52,222,0,0,0, + 0,104,2,97,0,97,7,70,63,152,240,139,0,0,0,0,104,2,97,1,97,7,70,191,166,31, + 187,160,0,0,0,104,2,97,2,97,7,70,191,150,70,63,0,0,0,0,104,2,97,3,97,7,70, + 63,152,181,126,128,0,0,0,104,2,97,4,97,7,70,63,151,187,162,128,0,0,0,104,2, + 97,5,97,7,70,191,143,161,101,0,0,0,0,104,2,97,0,97,8,70,191,153,102,36,128,0, + 0,0,104,2,97,1,97,8,70,63,160,139,250,64,0,0,0,104,2,97,2,97,8,70,63,164,62, + 196,64,0,0,0,104,2,97,3,97,8,70,191,178,78,209,192,0,0,0,104,2,97,4,97,8,70, + 191,185,19,76,224,0,0,0,104,2,97,5,97,8,70,63,183,142,196,96,0,0,0,104,2,97,0, + 97,9,70,63,150,104,248,0,0,0,0,104,2,97,1,97,9,70,191,164,4,100,224,0,0,0, + 104,2,97,2,97,9,70,191,169,42,42,224,0,0,0,104,2,97,3,97,9,70,63,145,54,78,128,0, + 0,0,104,2,97,4,97,9,70,63,126,243,134,0,0,0,0,104,2,97,5,97,9,70,63,177, + 203,25,96,0,0,0,104,2,97,0,97,10,70,63,172,104,47,64,0,0,0,104,2,97,1,97,10, + 70,63,161,242,193,64,0,0,0,104,2,97,2,97,10,70,63,175,208,241,192,0,0,0,104,2, + 97,3,97,10,70,191,129,202,161,0,0,0,0,104,2,97,4,97,10,70,63,178,151,55,32,0,0,0, + 104,2,97,5,97,10,70,63,137,155,94,0,0,0,0,104,2,97,0,97,11,70,191,179, + 106,160,0,0,0,0,104,2,97,1,97,11,70,63,184,253,164,96,0,0,0,104,2,97,2,97,11, + 70,191,143,30,157,0,0,0,0,104,2,97,3,97,11,70,63,153,225,140,128,0,0,0,104, + 2,97,4,97,11,70,63,161,35,85,192,0,0,0,104,2,97,5,97,11,70,63,175,200,55,192, + 0,0,0,104,2,97,0,97,12,70,191,180,116,132,96,0,0,0,104,2,97,1,97,12,70,191, + 165,151,152,0,0,0,0,104,2,97,2,97,12,70,191,180,197,91,160,0,0,0,104,2,97,3,97,12, + 70,191,91,30,160,0,0,0,0,104,2,97,4,97,12,70,63,180,251,45,32,0,0,0, + 104,2,97,5,97,12,70,63,165,134,77,64,0,0,0,104,2,97,6,97,14,70,63,181,56,242,96, + 0,0,0,104,2,97,7,97,14,70,191,165,239,234,224,0,0,0,104,2,97,8,97,14, + 70,191,154,65,216,128,0,0,0,104,2,97,9,97,14,70,63,150,250,236,0,0,0,0,104,2,97, + 10,97,14,70,191,141,105,108,0,0,0,0,104,2,97,11,97,14,70,191,152,40, + 165,0,0,0,0,104,2,97,12,97,14,70,63,141,159,46,0,0,0,0,104,2,97,13,97,14,70, + 191,183,172,137,32,0,0,0,104,2,97,6,97,15,70,63,163,26,123,192,0,0,0,104, + 2,97,7,97,15,70,63,176,184,106,32,0,0,0,104,2,97,8,97,15,70,63,152,234,144, + 0,0,0,0,104,2,97,9,97,15,70,191,172,58,70,160,0,0,0,104,2,97,10,97,15,70, + 63,161,211,211,192,0,0,0,104,2,97,11,97,15,70,191,148,171,120,128,0,0,0,104, + 2,97,12,97,15,70,63,180,117,214,224,0,0,0,104,2,97,13,97,15,70,191,104, + 230,216,0,0,0,0,104,2,97,6,97,16,70,63,178,53,103,96,0,0,0,104,2,97,7,97,16, + 70,63,170,230,232,64,0,0,0,104,2,97,8,97,16,70,191,183,45,100,192,0,0,0, + 104,2,97,9,97,16,70,63,184,100,97,32,0,0,0,104,2,97,10,97,16,70,63,169,174, + 254,64,0,0,0,104,2,97,11,97,16,70,191,119,121,234,0,0,0,0,104,2,97,12,97, + 16,70,63,149,12,170,128,0,0,0,104,2,97,13,97,16,70,191,144,193,191,0,0,0,0>>. -- cgit v1.2.3 From 5cf556df2e178ce78d0c4197a73654d593f58f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 24 Nov 2015 16:05:04 +0100 Subject: Fix seq_trace refactoring bug --- erts/emulator/beam/erl_process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 5907dd4567..a8b7211793 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11666,7 +11666,7 @@ send_exit_signal(Process *c_p, /* current process if and only if ((state & ERTS_PSFLG_TRAP_EXIT) && (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) { - if (have_seqtrace(token)) + if (have_seqtrace(token) && token_update) seq_trace_update_send(token_update); if (is_value(exit_tuple)) send_exit_message(rp, rp_locks, exit_tuple, exit_tuple_sz, token); -- cgit v1.2.3 From bc08768e5bad4baa2eaec4c6798ead9ded0a8889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 24 Nov 2015 18:35:11 +0100 Subject: Fix seq_trace token copy size --- erts/emulator/beam/erl_process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a8b7211793..a814462668 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11547,7 +11547,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, #ifdef SHCOPY_SEND INITIALIZE_SHCOPY(info); term_size = copy_shared_calculate(exit_term, &info); - mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp); + mp = erts_alloc_message_heap(to, to_locksp, term_size+sz_token, &hp, &ohp); mess = copy_shared_perform(exit_term, term_size, &info, &hp, ohp); DESTROY_SHCOPY(info); #else -- cgit v1.2.3 From ef45d2c9f874354b17c2aca96de7b3306a9eb943 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 8 Dec 2014 20:37:59 +0100 Subject: erts: Add enif_getenv to read OS environment variables in a safe and portable way. --- erts/doc/src/erl_nif.xml | 4 ++++ erts/emulator/beam/erl_nif.c | 1 + erts/emulator/beam/erl_nif.h | 3 ++- erts/emulator/beam/erl_nif_api_funcs.h | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 23c3d5fcee..3b77b1ffa0 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -791,6 +791,10 @@ typedef enum { and return true, or return false if term is not an unsigned integer or is outside the bounds of type unsigned long.

+ intenif_getenv(const char* key, char* value, size_t *value_size) + Get the value of an environment variable +

Same as erl_drv_getenv.

+
intenif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason) Check if an exception has been raised

Return true if a pending exception is associated diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index add4a66f90..d7a2076d85 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1173,6 +1173,7 @@ ErlNifTid enif_thread_self(void) { return erl_drv_thread_self(); } int enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2) { return erl_drv_equal_tids(tid1,tid2); } void enif_thread_exit(void *resp) { erl_drv_thread_exit(resp); } int enif_thread_join(ErlNifTid tid, void **respp) { return erl_drv_thread_join(tid,respp); } +int enif_getenv(const char *key, char *value, size_t *value_size) { return erl_drv_getenv(key, value, value_size); } int enif_fprintf(void* filep, const char* format, ...) { diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 7d880126f8..4cbfd8360b 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -48,9 +48,10 @@ ** add ErlNifEntry options ** add ErlNifFunc flags ** 2.8: 18.0 add enif_has_pending_exception +** 2.9: 18.2 enif_getenv */ #define ERL_NIF_MAJOR_VERSION 2 -#define ERL_NIF_MINOR_VERSION 8 +#define ERL_NIF_MINOR_VERSION 9 /* * The emulator will refuse to load a nif-lib with a major version diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 2f2180e1aa..08b9afc6af 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -159,6 +159,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[])); ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env, ERL_NIF_TERM* reason)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_raise_exception, (ErlNifEnv *env, ERL_NIF_TERM reason)); +ERL_NIF_API_FUNC_DECL(int,enif_getenv,(const char* key, char* value, size_t* value_size)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! @@ -310,6 +311,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_schedule_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_nif) # define enif_has_pending_exception ERL_NIF_API_FUNC_MACRO(enif_has_pending_exception) # define enif_raise_exception ERL_NIF_API_FUNC_MACRO(enif_raise_exception) +# define enif_getenv ERL_NIF_API_FUNC_MACRO(enif_getenv) /* ** ADD NEW ENTRIES HERE (before this comment) -- cgit v1.2.3 From 41e0c6e584d392ed0d5fbbc51a84418c4f7abcf5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 9 Dec 2014 11:53:16 +0100 Subject: erts: Refactor alloc_SUITE to use NIFs instead of drivers --- erts/emulator/beam/erl_nif.h | 1 + erts/emulator/sys/win32/erl_win32_sys_ddll.c | 3 +- erts/emulator/test/alloc_SUITE.erl | 109 +++++++------ .../test/alloc_SUITE_data/allocator_test.h | 15 +- erts/emulator/test/alloc_SUITE_data/basic.c | 3 + erts/emulator/test/alloc_SUITE_data/basic.erl | 10 ++ erts/emulator/test/alloc_SUITE_data/bucket_index.c | 2 + .../test/alloc_SUITE_data/bucket_index.erl | 10 ++ erts/emulator/test/alloc_SUITE_data/bucket_mask.c | 2 + .../emulator/test/alloc_SUITE_data/bucket_mask.erl | 10 ++ erts/emulator/test/alloc_SUITE_data/coalesce.c | 3 + erts/emulator/test/alloc_SUITE_data/coalesce.erl | 10 ++ erts/emulator/test/alloc_SUITE_data/cpool.c | 9 +- erts/emulator/test/alloc_SUITE_data/cpool.erl | 10 ++ erts/emulator/test/alloc_SUITE_data/migration.c | 44 +++--- erts/emulator/test/alloc_SUITE_data/migration.erl | 10 ++ .../test/alloc_SUITE_data/mseg_clear_cache.c | 3 + .../test/alloc_SUITE_data/mseg_clear_cache.erl | 10 ++ erts/emulator/test/alloc_SUITE_data/rbtree.c | 3 + erts/emulator/test/alloc_SUITE_data/rbtree.erl | 10 ++ erts/emulator/test/alloc_SUITE_data/realloc_copy.c | 2 + .../test/alloc_SUITE_data/realloc_copy.erl | 10 ++ .../test/alloc_SUITE_data/testcase_driver.c | 171 ++++++++------------- .../test/alloc_SUITE_data/testcase_driver.h | 11 +- erts/emulator/test/alloc_SUITE_data/threads.c | 6 +- erts/emulator/test/alloc_SUITE_data/threads.erl | 10 ++ 26 files changed, 290 insertions(+), 197 deletions(-) create mode 100644 erts/emulator/test/alloc_SUITE_data/basic.erl create mode 100644 erts/emulator/test/alloc_SUITE_data/bucket_index.erl create mode 100644 erts/emulator/test/alloc_SUITE_data/bucket_mask.erl create mode 100644 erts/emulator/test/alloc_SUITE_data/coalesce.erl create mode 100644 erts/emulator/test/alloc_SUITE_data/cpool.erl create mode 100644 erts/emulator/test/alloc_SUITE_data/migration.erl create mode 100644 erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.erl create mode 100644 erts/emulator/test/alloc_SUITE_data/rbtree.erl create mode 100644 erts/emulator/test/alloc_SUITE_data/realloc_copy.erl create mode 100644 erts/emulator/test/alloc_SUITE_data/threads.erl (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 4cbfd8360b..5e39343e9b 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -232,6 +232,7 @@ typedef enum { # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct { # include "erl_nif_api_funcs.h" + void* erts_alc_test; } TWinDynNifCallbacks; extern TWinDynNifCallbacks WinDynNifCallbacks; # undef ERL_NIF_API_FUNC_DECL diff --git a/erts/emulator/sys/win32/erl_win32_sys_ddll.c b/erts/emulator/sys/win32/erl_win32_sys_ddll.c index 9a5557e93d..7c24a77e31 100644 --- a/erts/emulator/sys/win32/erl_win32_sys_ddll.c +++ b/erts/emulator/sys/win32/erl_win32_sys_ddll.c @@ -52,7 +52,8 @@ void erl_sys_ddll_init(void) { #define ERL_NIF_API_FUNC_DECL(RET,NAME,ARGS) nif_callbacks.NAME = NAME #include "erl_nif_api_funcs.h" #undef ERL_NIF_API_FUNC_DECL - + nif_callbacks.erts_alc_test = erts_alc_test; + return; } diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 9b0d4737b1..0dd68b778b 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -204,55 +204,43 @@ drv_case(Config, Mode, NodeOpts) when is_list(Config) -> run_drv_case(Config, Mode) -> ?line DataDir = ?config(data_dir,Config), ?line CaseName = ?config(testcase,Config), - case erl_ddll:load_driver(DataDir, CaseName) of - ok -> ok; - {error, Error} -> - io:format("~s\n", [erl_ddll:format_error(Error)]), - ?line ?t:fail() - end, + File = filename:join(DataDir, CaseName), + {ok,CaseName,Bin} = compile:file(File, [binary,return_errors]), + ?line {module,CaseName} = erlang:load_module(CaseName,Bin), + ok = CaseName:init(File), case Mode of one_shot -> - Result = one_shot(CaseName, ""); + Result = one_shot(CaseName); concurrent -> Result = concurrent(CaseName) end, - ?line ok = erl_ddll:unload_driver(CaseName), + ?line true = erlang:delete_module(CaseName), ?line Result. -one_shot(CaseName, Command) -> - ?line Port = open_port({spawn, atom_to_list(CaseName)}, []), - ?line true = is_port(Port), - ?line Port ! {self(), {command, Command}}, - ?line Result = receive_drv_result(Port, CaseName), - ?line Port ! {self(), close}, - ?line receive - {Port, closed} -> - ok - end, - Result. +one_shot(CaseName) -> + State = CaseName:start(1), + Result0 = CaseName:run(State), + false = (Result0 =:= continue), + Result1 = handle_result(State, Result0), + CaseName:stop(State), + Result1. -many_shot(CaseName, Command) -> - ?line Port = open_port({spawn, atom_to_list(CaseName)}, []), - ?line true = is_port(Port), - Result = repeat_while(fun() -> - ?line Port ! {self(), {command, Command}}, - receive_drv_result(Port, CaseName) =:= continue - end), - ?line Port ! {self(), close}, - ?line receive - {Port, closed} -> - ok - end, - Result. +many_shot(CaseName, I) -> + State = CaseName:start(I), + Result1 = repeat_while(fun() -> + Result0 = CaseName:run(State), + handle_result(State, Result0) + end), + CaseName:stop(State), + Result1. concurrent(CaseName) -> - one_shot(CaseName, "init"), PRs = lists:map(fun(I) -> spawn_opt(fun() -> - many_shot(CaseName, "") + many_shot(CaseName, I) end, [monitor, {scheduler,I}]) end, @@ -266,32 +254,39 @@ concurrent(CaseName) -> ok. repeat_while(Fun) -> - io:format("~p calls fun\n", [self()]), + %%io:format("~p calls fun\n", [self()]), case Fun() of - true -> repeat_while(Fun); - false -> ok + continue -> repeat_while(Fun); + R -> R end. -receive_drv_result(Port, CaseName) -> - ?line receive - {print, Port, CaseName, Str} -> - ?line ?t:format("~s", [Str]), - ?line receive_drv_result(Port, CaseName); - {'EXIT', Port, Error} -> - ?line ?t:fail(Error); - {'EXIT', error, Error} -> - ?line ?t:fail(Error); - {failed, Port, CaseName, Comment} -> - ?line ?t:fail(Comment); - {skipped, Port, CaseName, Comment} -> - ?line {skipped, Comment}; - {succeeded, Port, CaseName, ""} -> - ?line succeeded; - {succeeded, Port, CaseName, Comment} -> - ?line {comment, Comment}; - continue -> - continue - end. +flush_log() -> + receive + {print, Str} -> + ?t:format("~s", [Str]), + flush_log() + after 0 -> + ok + end. + +handle_result(_State, Result0) -> + flush_log(), + case Result0 of + {'EXIT', Error} -> + ?line ?t:fail(Error); + {'EXIT', error, Error} -> + ?line ?t:fail(Error); + {failed, Comment} -> + ?line ?t:fail(Comment); + {skipped, Comment} -> + ?line {skipped, Comment}; + {succeeded, ""} -> + ?line succeeded; + {succeeded, Comment} -> + ?line {comment, Comment}; + continue -> + continue + end. start_node(Config, Opts) when is_list(Config), is_list(Opts) -> Pa = filename:dirname(code:which(?MODULE)), diff --git a/erts/emulator/test/alloc_SUITE_data/allocator_test.h b/erts/emulator/test/alloc_SUITE_data/allocator_test.h index bfd0bb3094..dd0227e725 100644 --- a/erts/emulator/test/alloc_SUITE_data/allocator_test.h +++ b/erts/emulator/test/alloc_SUITE_data/allocator_test.h @@ -20,9 +20,20 @@ #ifndef ALLOCATOR_TEST_H__ #define ALLOCATOR_TEST_H__ -typedef ErlDrvUInt Ulong; +#if SIZEOF_VOID_P == SIZEOF_INT +typedef unsigned int Ulong; +#elif SIZEOF_VOID_P == SIZEOF_LONG +typedef unsigned long Ulong; +#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG +typedef unsigned long long Ulong; +#else +# error No pointer sized integer type found ??? +#endif -#ifndef __WIN32__ +#ifdef __WIN32__ +typedef Ulong erts_alc_test_Fn(Ulong, Ulong, Ulong, Ulong); +# define erts_alc_test ((erts_alc_test_Fn*)WinDynNifCallbacks.erts_alc_test) +#else Ulong erts_alc_test(Ulong, Ulong, Ulong, Ulong); #endif diff --git a/erts/emulator/test/alloc_SUITE_data/basic.c b/erts/emulator/test/alloc_SUITE_data/basic.c index 323a24a11f..debb3d7ebe 100644 --- a/erts/emulator/test/alloc_SUITE_data/basic.c +++ b/erts/emulator/test/alloc_SUITE_data/basic.c @@ -60,3 +60,6 @@ testcase_cleanup(TestCaseState_t *tcs) if (tcs->extra) STOP_ALC((Allctr_t *) tcs->extra); } + +ERL_NIF_INIT(basic, testcase_nif_funcs, testcase_nif_init, + NULL, NULL, NULL); diff --git a/erts/emulator/test/alloc_SUITE_data/basic.erl b/erts/emulator/test/alloc_SUITE_data/basic.erl new file mode 100644 index 0000000000..a018fd5582 --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/basic.erl @@ -0,0 +1,10 @@ +-module(basic). + +-export([init/1, start/1, run/1, stop/1]). + +init(File) -> + ok = erlang:load_nif(File, 0). + +start(_) -> erlang:nif_error(not_loaded). +run(_) -> erlang:nif_error(not_loaded). +stop(_) -> erlang:nif_error(not_loaded). diff --git a/erts/emulator/test/alloc_SUITE_data/bucket_index.c b/erts/emulator/test/alloc_SUITE_data/bucket_index.c index c13f229049..45cb53fbf7 100644 --- a/erts/emulator/test/alloc_SUITE_data/bucket_index.c +++ b/erts/emulator/test/alloc_SUITE_data/bucket_index.c @@ -113,3 +113,5 @@ test_it(TestCaseState_t *tcs, unsigned sbct) sbct ? sbct_buf : "default"); } +ERL_NIF_INIT(bucket_index, testcase_nif_funcs, testcase_nif_init, + NULL, NULL, NULL); diff --git a/erts/emulator/test/alloc_SUITE_data/bucket_index.erl b/erts/emulator/test/alloc_SUITE_data/bucket_index.erl new file mode 100644 index 0000000000..c54f54e2f5 --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/bucket_index.erl @@ -0,0 +1,10 @@ +-module(bucket_index). + +-export([init/1, start/1, run/1, stop/1]). + +init(File) -> + ok = erlang:load_nif(File, 0). + +start(_) -> erlang:nif_error(not_loaded). +run(_) -> erlang:nif_error(not_loaded). +stop(_) -> erlang:nif_error(not_loaded). diff --git a/erts/emulator/test/alloc_SUITE_data/bucket_mask.c b/erts/emulator/test/alloc_SUITE_data/bucket_mask.c index 8d6166771e..d474c80343 100644 --- a/erts/emulator/test/alloc_SUITE_data/bucket_mask.c +++ b/erts/emulator/test/alloc_SUITE_data/bucket_mask.c @@ -183,3 +183,5 @@ testcase_run(TestCaseState_t *tcs) tcs->extra = NULL; } +ERL_NIF_INIT(bucket_mask, testcase_nif_funcs, testcase_nif_init, + NULL, NULL, NULL); diff --git a/erts/emulator/test/alloc_SUITE_data/bucket_mask.erl b/erts/emulator/test/alloc_SUITE_data/bucket_mask.erl new file mode 100644 index 0000000000..589a50e1fa --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/bucket_mask.erl @@ -0,0 +1,10 @@ +-module(bucket_mask). + +-export([init/1, start/1, run/1, stop/1]). + +init(File) -> + ok = erlang:load_nif(File, 0). + +start(_) -> erlang:nif_error(not_loaded). +run(_) -> erlang:nif_error(not_loaded). +stop(_) -> erlang:nif_error(not_loaded). diff --git a/erts/emulator/test/alloc_SUITE_data/coalesce.c b/erts/emulator/test/alloc_SUITE_data/coalesce.c index 0a5e0c5b0e..7791409a34 100644 --- a/erts/emulator/test/alloc_SUITE_data/coalesce.c +++ b/erts/emulator/test/alloc_SUITE_data/coalesce.c @@ -317,3 +317,6 @@ testcase_cleanup(TestCaseState_t *tcs) if (tcs->extra) STOP_ALC((Allctr_t *) tcs->extra); } + +ERL_NIF_INIT(coalesce, testcase_nif_funcs, testcase_nif_init, + NULL, NULL, NULL); diff --git a/erts/emulator/test/alloc_SUITE_data/coalesce.erl b/erts/emulator/test/alloc_SUITE_data/coalesce.erl new file mode 100644 index 0000000000..453c726c4e --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/coalesce.erl @@ -0,0 +1,10 @@ +-module(coalesce). + +-export([init/1, start/1, run/1, stop/1]). + +init(File) -> + ok = erlang:load_nif(File, 0). + +start(_) -> erlang:nif_error(not_loaded). +run(_) -> erlang:nif_error(not_loaded). +stop(_) -> erlang:nif_error(not_loaded). diff --git a/erts/emulator/test/alloc_SUITE_data/cpool.c b/erts/emulator/test/alloc_SUITE_data/cpool.c index 75c2bc13ae..73026cc758 100644 --- a/erts/emulator/test/alloc_SUITE_data/cpool.c +++ b/erts/emulator/test/alloc_SUITE_data/cpool.c @@ -86,13 +86,13 @@ thread_func(void *arg) for (i = 0; i < (TEST_NO_CARRIERS_PER_THREAD+TEST_CARRIERS_OFFSET); i++) { int d; if (i < TEST_NO_CARRIERS_PER_THREAD) { - CPOOL_INSERT(alloc, crr[i]); + (void) CPOOL_INSERT(alloc, crr[i]); if ((i & 0x7) == 0) FATAL_ASSERT(CPOOL_IS_IN_POOL(alloc, crr[i])); } d = i-TEST_CARRIERS_OFFSET; if (d >= 0) { - CPOOL_DELETE(alloc, crr[d]); + (void) CPOOL_DELETE(alloc, crr[d]); if ((d & 0x7) == 0) FATAL_ASSERT(!CPOOL_IS_IN_POOL(alloc, crr[d])); } @@ -129,7 +129,7 @@ testcase_run(TestCaseState_t *tcs) for (c = 0; c < TEST_NO_CARRIERS_PER_THREAD; c++) { Carrier_t *crr = (Carrier_t *) p; p += zcrr_sz; - ZERO_CRR_INIT(alloc, crr); + (void) ZERO_CRR_INIT(alloc, crr); threads[t].crr[c] = crr; } } @@ -156,3 +156,6 @@ testcase_run(TestCaseState_t *tcs) ASSERT(tcs, no_threads == TEST_NO_THREADS); } + +ERL_NIF_INIT(cpool, testcase_nif_funcs, testcase_nif_init, + NULL, NULL, NULL); diff --git a/erts/emulator/test/alloc_SUITE_data/cpool.erl b/erts/emulator/test/alloc_SUITE_data/cpool.erl new file mode 100644 index 0000000000..89053471fa --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/cpool.erl @@ -0,0 +1,10 @@ +-module(cpool). + +-export([init/1, start/1, run/1, stop/1]). + +init(File) -> + ok = erlang:load_nif(File, 0). + +start(_) -> erlang:nif_error(not_loaded). +run(_) -> erlang:nif_error(not_loaded). +stop(_) -> erlang:nif_error(not_loaded). diff --git a/erts/emulator/test/alloc_SUITE_data/migration.c b/erts/emulator/test/alloc_SUITE_data/migration.c index dd58a0d3dd..9f6535c834 100644 --- a/erts/emulator/test/alloc_SUITE_data/migration.c +++ b/erts/emulator/test/alloc_SUITE_data/migration.c @@ -60,6 +60,8 @@ testcase_name(void) void testcase_cleanup(TestCaseState_t *tcs) { + enif_free(tcs->extra); + tcs->extra = NULL; } #define MAX_BLOCK_PER_THR 100 @@ -78,7 +80,7 @@ typedef struct { } MigrationState; typedef struct { - ErlDrvMutex* mtx; + ErlNifMutex* mtx; int nblocks; MyBlock* first; } MyCrrInfo; @@ -94,7 +96,7 @@ static void my_creating_mbc(Allctr_t *allctr, Carrier_t *carrier) if (orig_create_mbc_fn) orig_create_mbc_fn(allctr, carrier); - mci->mtx = erl_drv_mutex_create("alloc_SUITE.migration"); + mci->mtx = enif_mutex_create("alloc_SUITE.migration"); mci->nblocks = 0; mci->first = NULL; } @@ -105,49 +107,54 @@ static void my_destroying_mbc(Allctr_t *allctr, Carrier_t *carrier) FATAL_ASSERT(mci->nblocks == 0); FATAL_ASSERT(mci->first == NULL); - erl_drv_mutex_destroy(mci->mtx); + enif_mutex_destroy(mci->mtx); if (orig_destroying_mbc_fn) orig_destroying_mbc_fn(allctr, carrier); } - -static void setup(TestCaseState_t* tcs) +static int migration_init(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { void* creating_mbc_arg = (void*)my_creating_mbc; void* destroying_mbc_arg = (void*)my_destroying_mbc; + + if (testcase_nif_init(env, priv_data, load_info)) + return -1; + crr_info_offset = SET_TEST_MBC_USER_HEADER(sizeof(MyCrrInfo), &creating_mbc_arg, &destroying_mbc_arg); - ASSERT(tcs, crr_info_offset >= 0); + FATAL_ASSERT(crr_info_offset >= 0); orig_create_mbc_fn = creating_mbc_arg; orig_destroying_mbc_fn = destroying_mbc_arg; + + return 0; } static void add_block(MyBlock* p) { MyCrrInfo* mci = (MyCrrInfo*)((char*)BLK_TO_MBC(UMEM2BLK_TEST(p)) + crr_info_offset); - erl_drv_mutex_lock(mci->mtx); + enif_mutex_lock(mci->mtx); mci->nblocks++; p->next = mci->first; p->prevp = &mci->first; mci->first = p; if (p->next) p->next->prevp = &p->next; - erl_drv_mutex_unlock(mci->mtx); + enif_mutex_unlock(mci->mtx); } static void remove_block(MyBlock* p) { MyCrrInfo* mci = (MyCrrInfo*)((char*)BLK_TO_MBC(UMEM2BLK_TEST(p)) + crr_info_offset); - erl_drv_mutex_lock(mci->mtx); + enif_mutex_lock(mci->mtx); mci->nblocks--; if (p->next) p->next->prevp = p->prevp; *p->prevp = p->next; - erl_drv_mutex_unlock(mci->mtx); + enif_mutex_unlock(mci->mtx); } void @@ -155,17 +162,11 @@ testcase_run(TestCaseState_t *tcs) { MigrationState* state = (MigrationState*) tcs->extra; - if (tcs->command_len == 4 - && memcmp(tcs->command, "init", tcs->command_len) == 0) { - setup(tcs); - return; - } - if (!tcs->extra) { if (!IS_SMP_ENABLED) testcase_skipped(tcs, "No SMP support"); - tcs->extra = driver_alloc(sizeof(MigrationState)); + tcs->extra = enif_alloc(sizeof(MigrationState)); state = (MigrationState*) tcs->extra; memset(state->blockv, 0, sizeof(state->blockv)); state->phase = GROWING; @@ -220,10 +221,9 @@ testcase_run(TestCaseState_t *tcs) FATAL_ASSERT(!"Invalid phase"); } - if (state->phase == DONE) { - driver_free(tcs->extra); - tcs->extra = NULL; - } - else + if (state->phase != DONE) testcase_continue(tcs); } + +ERL_NIF_INIT(migration, testcase_nif_funcs, migration_init, + NULL, NULL, NULL); diff --git a/erts/emulator/test/alloc_SUITE_data/migration.erl b/erts/emulator/test/alloc_SUITE_data/migration.erl new file mode 100644 index 0000000000..440a99becd --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/migration.erl @@ -0,0 +1,10 @@ +-module(migration). + +-export([init/1, start/1, run/1, stop/1]). + +init(File) -> + ok = erlang:load_nif(File, 0). + +start(_) -> erlang:nif_error(not_loaded). +run(_) -> erlang:nif_error(not_loaded). +stop(_) -> erlang:nif_error(not_loaded). diff --git a/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c b/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c index 9c03f3a331..e5df3d647f 100644 --- a/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c +++ b/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c @@ -101,3 +101,6 @@ testcase_cleanup(TestCaseState_t *tcs) tcs->extra = NULL; } } + +ERL_NIF_INIT(mseg_clear_cache, testcase_nif_funcs, testcase_nif_init, + NULL, NULL, NULL); diff --git a/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.erl b/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.erl new file mode 100644 index 0000000000..befd6c2e8e --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.erl @@ -0,0 +1,10 @@ +-module(mseg_clear_cache). + +-export([init/1, start/1, run/1, stop/1]). + +init(File) -> + ok = erlang:load_nif(File, 0). + +start(_) -> erlang:nif_error(not_loaded). +run(_) -> erlang:nif_error(not_loaded). +stop(_) -> erlang:nif_error(not_loaded). diff --git a/erts/emulator/test/alloc_SUITE_data/rbtree.c b/erts/emulator/test/alloc_SUITE_data/rbtree.c index 8d4d5535a8..eb7b36984e 100644 --- a/erts/emulator/test/alloc_SUITE_data/rbtree.c +++ b/erts/emulator/test/alloc_SUITE_data/rbtree.c @@ -577,3 +577,6 @@ testcase_run(TestCaseState_t *tcs) testcase_printf(tcs, "aoffcaobf test succeeded!\n"); } + +ERL_NIF_INIT(rbtree, testcase_nif_funcs, testcase_nif_init, + NULL, NULL, NULL); diff --git a/erts/emulator/test/alloc_SUITE_data/rbtree.erl b/erts/emulator/test/alloc_SUITE_data/rbtree.erl new file mode 100644 index 0000000000..f5b7120ff2 --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/rbtree.erl @@ -0,0 +1,10 @@ +-module(rbtree). + +-export([init/1, start/1, run/1, stop/1]). + +init(File) -> + ok = erlang:load_nif(File, 0). + +start(_) -> erlang:nif_error(not_loaded). +run(_) -> erlang:nif_error(not_loaded). +stop(_) -> erlang:nif_error(not_loaded). diff --git a/erts/emulator/test/alloc_SUITE_data/realloc_copy.c b/erts/emulator/test/alloc_SUITE_data/realloc_copy.c index e405f06225..c4147eb00d 100644 --- a/erts/emulator/test/alloc_SUITE_data/realloc_copy.c +++ b/erts/emulator/test/alloc_SUITE_data/realloc_copy.c @@ -278,3 +278,5 @@ testcase_cleanup(TestCaseState_t *tcs) STOP_ALC((Allctr_t *) tcs->extra); } +ERL_NIF_INIT(realloc_copy, testcase_nif_funcs, testcase_nif_init, + NULL, NULL, NULL); diff --git a/erts/emulator/test/alloc_SUITE_data/realloc_copy.erl b/erts/emulator/test/alloc_SUITE_data/realloc_copy.erl new file mode 100644 index 0000000000..cc6617bf64 --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/realloc_copy.erl @@ -0,0 +1,10 @@ +-module(realloc_copy). + +-export([init/1, start/1, run/1, stop/1]). + +init(File) -> + ok = erlang:load_nif(File, 0). + +start(_) -> erlang:nif_error(not_loaded). +run(_) -> erlang:nif_error(not_loaded). +stop(_) -> erlang:nif_error(not_loaded). diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c index d04eb1bcb0..83ee1b67ca 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c @@ -43,128 +43,95 @@ typedef struct { TestCaseState_t visible; - ErlDrvPort port; - ErlDrvTermData port_id; + ErlNifEnv* curr_env; int result; jmp_buf done_jmp_buf; char *comment; char comment_buf[COMMENT_BUF_SZ]; } InternalTestCaseState_t; -ErlDrvData testcase_drv_start(ErlDrvPort port, char *command); -void testcase_drv_stop(ErlDrvData drv_data); -void testcase_drv_run(ErlDrvData drv_data, char *buf, ErlDrvSizeT len); - -static ErlDrvEntry testcase_drv_entry = { - NULL, - testcase_drv_start, - testcase_drv_stop, - testcase_drv_run, - NULL, - NULL, - "testcase_drv", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, - 0, - NULL, - NULL, - NULL +ERL_NIF_TERM testcase_nif_start(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM testcase_nif_stop(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM testcase_nif_run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +ErlNifFunc testcase_nif_funcs[] = +{ + {"start", 1, testcase_nif_start}, + {"run", 1, testcase_nif_run}, + {"stop", 1, testcase_nif_stop} }; +static ErlNifResourceType* testcase_rt; +static ERL_NIF_TERM print_atom; -DRIVER_INIT(testcase_drv) +int testcase_nif_init(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { - testcase_drv_entry.driver_name = testcase_name(); - return &testcase_drv_entry; + testcase_rt = enif_open_resource_type(env, NULL, "testcase_rt", NULL, + ERL_NIF_RT_CREATE, NULL); + + print_atom = enif_make_atom(env, "print"); + return 0; } -ErlDrvData -testcase_drv_start(ErlDrvPort port, char *command) +ERL_NIF_TERM +testcase_nif_start(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + ERL_NIF_TERM ret; InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) - driver_alloc(sizeof(InternalTestCaseState_t)); - if (!itcs) { - return ERL_DRV_ERROR_GENERAL; + enif_alloc_resource(testcase_rt, sizeof(InternalTestCaseState_t)); + + if (!itcs || !enif_get_int(env, argv[0], &itcs->visible.thr_nr)) { + enif_make_badarg(env); } itcs->visible.testcase_name = testcase_name(); + itcs->visible.extra = NULL; - itcs->port = port; - itcs->port_id = driver_mk_port(port); itcs->result = TESTCASE_FAILED; itcs->comment = ""; - return (ErlDrvData) itcs; + ret = enif_make_resource(env, itcs); + enif_release_resource(itcs); + return ret; } -void -testcase_drv_stop(ErlDrvData drv_data) +ERL_NIF_TERM +testcase_nif_stop(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - testcase_cleanup((TestCaseState_t *) drv_data); - driver_free((void *) drv_data); + InternalTestCaseState_t *itcs; + if (!enif_get_resource(env, argv[0], testcase_rt, (void**)&itcs)) + return enif_make_badarg(env); + testcase_cleanup(&itcs->visible); + return enif_make_atom(env,"ok"); } -void -testcase_drv_run(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) +ERL_NIF_TERM +testcase_nif_run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) drv_data; - ErlDrvTermData result_atom; - ErlDrvTermData msg[12]; + InternalTestCaseState_t *itcs; + const char* result_atom; + + if (!enif_get_resource(env, argv[0], testcase_rt, (void**)&itcs)) + return enif_make_badarg(env); - itcs->visible.command = buf; - itcs->visible.command_len = len; + itcs->curr_env = env; if (setjmp(itcs->done_jmp_buf) == 0) { - testcase_run((TestCaseState_t *) itcs); + testcase_run(&itcs->visible); itcs->result = TESTCASE_SUCCEEDED; } switch (itcs->result) { case TESTCASE_CONTINUE: - msg[0] = ERL_DRV_ATOM; - msg[1] = driver_mk_atom("continue"); - erl_drv_output_term(itcs->port_id, msg, 2); - return; - - case TESTCASE_SUCCEEDED: - result_atom = driver_mk_atom("succeeded"); - break; - case TESTCASE_SKIPPED: - result_atom = driver_mk_atom("skipped"); - break; - case TESTCASE_FAILED: - default: - result_atom = driver_mk_atom("failed"); - break; - } - - msg[0] = ERL_DRV_ATOM; - msg[1] = (ErlDrvTermData) result_atom; - - msg[2] = ERL_DRV_PORT; - msg[3] = itcs->port_id; + return enif_make_atom(env, "continue"); - msg[4] = ERL_DRV_ATOM; - msg[5] = driver_mk_atom(itcs->visible.testcase_name); - - msg[6] = ERL_DRV_STRING; - msg[7] = (ErlDrvTermData) itcs->comment; - msg[8] = (ErlDrvTermData) strlen(itcs->comment); - - msg[9] = ERL_DRV_TUPLE; - msg[10] = (ErlDrvTermData) 4; + case TESTCASE_SUCCEEDED: result_atom = "succeeded"; break; + case TESTCASE_SKIPPED: result_atom = "skipped"; break; + case TESTCASE_FAILED: result_atom = "failed"; break; + } - erl_drv_output_term(itcs->port_id, msg, 11); + return enif_make_tuple2(env, enif_make_atom(env, result_atom), + enif_make_string(env, itcs->comment, ERL_NIF_LATIN1)); } int @@ -179,8 +146,10 @@ testcase_assertion_failed(TestCaseState_t *tcs, void testcase_printf(TestCaseState_t *tcs, char *frmt, ...) { - InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs; - ErlDrvTermData msg[12]; + InternalTestCaseState_t* itcs = (InternalTestCaseState_t*)tcs; + ErlNifPid pid; + ErlNifEnv* msg_env = enif_alloc_env(); + ERL_NIF_TERM msg; va_list va; va_start(va, frmt); #if HAVE_VSNPRINTF @@ -190,23 +159,13 @@ testcase_printf(TestCaseState_t *tcs, char *frmt, ...) #endif va_end(va); - msg[0] = ERL_DRV_ATOM; - msg[1] = (ErlDrvTermData) driver_mk_atom("print"); - - msg[2] = ERL_DRV_PORT; - msg[3] = itcs->port_id; - - msg[4] = ERL_DRV_ATOM; - msg[5] = driver_mk_atom(itcs->visible.testcase_name); - - msg[6] = ERL_DRV_STRING; - msg[7] = (ErlDrvTermData) itcs->comment_buf; - msg[8] = (ErlDrvTermData) strlen(itcs->comment_buf); + msg = enif_make_tuple2(msg_env, print_atom, + enif_make_string(msg_env, itcs->comment_buf, ERL_NIF_LATIN1)); - msg[9] = ERL_DRV_TUPLE; - msg[10] = (ErlDrvTermData) 4; + enif_send(itcs->curr_env, enif_self(itcs->curr_env, &pid), + msg_env, msg); - erl_drv_output_term(itcs->port_id, msg, 11); + enif_free_env(msg_env); } @@ -270,7 +229,7 @@ void testcase_failed(TestCaseState_t *tcs, char *frmt, ...) itcs->result = TESTCASE_FAILED; itcs->comment = itcs->comment_buf; - if (erl_drv_getenv("ERL_ABORT_ON_FAILURE", buf, &bufsz) == 0 + if (enif_getenv("ERL_ABORT_ON_FAILURE", buf, &bufsz) == 0 && strcmp("true", buf) == 0) { fprintf(stderr, "Testcase \"%s\" failed: %s\n", itcs->visible.testcase_name, itcs->comment); @@ -282,15 +241,15 @@ void testcase_failed(TestCaseState_t *tcs, char *frmt, ...) void *testcase_alloc(size_t size) { - return driver_alloc(size); + return enif_alloc(size); } void *testcase_realloc(void *ptr, size_t size) { - return driver_realloc(ptr, size); + return enif_realloc(ptr, size); } void testcase_free(void *ptr) { - driver_free(ptr); + enif_free(ptr); } diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h index 5d439735b7..698ae66fac 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h @@ -20,13 +20,12 @@ #ifndef TESTCASE_DRIVER_H__ #define TESTCASE_DRIVER_H__ -#include "erl_driver.h" +#include "erl_nif.h" #include typedef struct { char *testcase_name; - char *command; - int command_len; + int thr_nr; void *extra; } TestCaseState_t; @@ -34,6 +33,7 @@ typedef struct { ((void) ((B) ? 1 : testcase_assertion_failed((TCS), __FILE__, __LINE__, #B))) +int testcase_nif_init(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); void testcase_printf(TestCaseState_t *tcs, char *frmt, ...); void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...); void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...); @@ -46,8 +46,11 @@ void *testcase_realloc(void *ptr, size_t size); void testcase_free(void *ptr); +/* Implemented by testcase: */ char *testcase_name(void); void testcase_run(TestCaseState_t *tcs); void testcase_cleanup(TestCaseState_t *tcs); -#endif +extern ErlNifFunc testcase_nif_funcs[3]; + +#endif /* TESTCASE_DRIVER_H__ */ diff --git a/erts/emulator/test/alloc_SUITE_data/threads.c b/erts/emulator/test/alloc_SUITE_data/threads.c index edad24ee6b..a8a6a23695 100644 --- a/erts/emulator/test/alloc_SUITE_data/threads.c +++ b/erts/emulator/test/alloc_SUITE_data/threads.c @@ -86,7 +86,7 @@ static void fail(int t_no, char *frmt, ...) tc_failed = 1; - if (erl_drv_getenv("ERL_ABORT_ON_FAILURE", buf, &bufsz) == 0 + if (enif_getenv("ERL_ABORT_ON_FAILURE", buf, &bufsz) == 0 && strcmp("true", buf) == 0) { fprintf(stderr, "Testcase \"%s\" failed: %s\n", testcase_name(), err_buf); @@ -187,7 +187,6 @@ testcase_run(TestCaseState_t *tcs) for(i = 1; i <= NO_OF_THREADS; i++) { char *alc; - int res; threads[i].arg.no_ops_per_bl = NO_OF_OPS_PER_BL; @@ -446,3 +445,6 @@ thread_func(void *arg) exit_thread(td->t_no, 1); return NULL; } + +ERL_NIF_INIT(threads, testcase_nif_funcs, testcase_nif_init, + NULL, NULL, NULL); diff --git a/erts/emulator/test/alloc_SUITE_data/threads.erl b/erts/emulator/test/alloc_SUITE_data/threads.erl new file mode 100644 index 0000000000..a7b4965f5e --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/threads.erl @@ -0,0 +1,10 @@ +-module(threads). + +-export([init/1, start/1, run/1, stop/1]). + +init(File) -> + ok = erlang:load_nif(File, 0). + +start(_) -> erlang:nif_error(not_loaded). +run(_) -> erlang:nif_error(not_loaded). +stop(_) -> erlang:nif_error(not_loaded). -- cgit v1.2.3 From 3198ed17179f35b86f7f8ab5c0b9c165d44c1ba6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 10 Dec 2014 20:03:02 +0100 Subject: erts: Workaround for strange crash on win64 in alloc_SUITE test code For some reason setjmp() crash when having jmp_buf heap allocated but works when stack allocated. --- erts/emulator/test/alloc_SUITE_data/testcase_driver.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c index 83ee1b67ca..5b35f581ea 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c @@ -45,7 +45,7 @@ typedef struct { TestCaseState_t visible; ErlNifEnv* curr_env; int result; - jmp_buf done_jmp_buf; + jmp_buf* done_jmp_buf; char *comment; char comment_buf[COMMENT_BUF_SZ]; } InternalTestCaseState_t; @@ -110,13 +110,20 @@ testcase_nif_run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { InternalTestCaseState_t *itcs; const char* result_atom; + jmp_buf the_jmp_buf; if (!enif_get_resource(env, argv[0], testcase_rt, (void**)&itcs)) return enif_make_badarg(env); itcs->curr_env = env; - if (setjmp(itcs->done_jmp_buf) == 0) { + /* For some unknown reason, first call to setjmp crashes on win64 + * when jmp_buf is allocated as part of the resource. But it works when + * allocated on stack. It used to work when this was a driver. + */ + itcs->done_jmp_buf = &the_jmp_buf; + + if (setjmp(the_jmp_buf) == 0) { testcase_run(&itcs->visible); itcs->result = TESTCASE_SUCCEEDED; } @@ -184,7 +191,7 @@ void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...) itcs->result = TESTCASE_SUCCEEDED; itcs->comment = itcs->comment_buf; - longjmp(itcs->done_jmp_buf, 1); + longjmp(*itcs->done_jmp_buf, 1); } void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...) @@ -202,14 +209,14 @@ void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...) itcs->result = TESTCASE_SKIPPED; itcs->comment = itcs->comment_buf; - longjmp(itcs->done_jmp_buf, 1); + longjmp(*itcs->done_jmp_buf, 1); } void testcase_continue(TestCaseState_t *tcs) { InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs; itcs->result = TESTCASE_CONTINUE; - longjmp(itcs->done_jmp_buf, 1); + longjmp(*itcs->done_jmp_buf, 1); } void testcase_failed(TestCaseState_t *tcs, char *frmt, ...) @@ -236,7 +243,7 @@ void testcase_failed(TestCaseState_t *tcs, char *frmt, ...) abort(); } - longjmp(itcs->done_jmp_buf, 1); + longjmp(*itcs->done_jmp_buf, 1); } void *testcase_alloc(size_t size) -- cgit v1.2.3 From 8421d318090ca59406ef6e04a43af16e1a467d9b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 12 Oct 2015 17:00:07 +0200 Subject: erts: Fix snprintf in alloc_SUITE for windows --- .../test/alloc_SUITE_data/testcase_driver.c | 47 ++++++++++------------ 1 file changed, 21 insertions(+), 26 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c index 5b35f581ea..1503fea4cb 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c @@ -25,14 +25,25 @@ #include #ifdef __WIN32__ -#undef HAVE_VSNPRINTF -#define HAVE_VSNPRINTF 1 -#define vsnprintf _vsnprintf +static void my_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) +{ + _vsnprintf(outBuf, size, format, ap); + outBuf[size-1] = 0; /* be sure string is terminated */ +} +#elif defined(HAVE_VSNPRINTF) +# define my_vsnprintf(B,S,F,A) (void)vsnprintf(B,S,F,A) +#else +# warning Using unsafe 'vsprintf' without buffer overflow protection +# define my_vsnprintf(B,S,F,A) (void)vsprintf(B,F,A) #endif -#ifndef HAVE_VSNPRINTF -#define HAVE_VSNPRINTF 0 -#endif +static void my_snprintf(char *outBuf, size_t size, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + my_vsnprintf(outBuf, size, format, ap); + va_end(ap); +} #define COMMENT_BUF_SZ 4096 @@ -159,11 +170,7 @@ testcase_printf(TestCaseState_t *tcs, char *frmt, ...) ERL_NIF_TERM msg; va_list va; va_start(va, frmt); -#if HAVE_VSNPRINTF - vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va); -#else - vsprintf(itcs->comment_buf, frmt, va); -#endif + my_vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va); va_end(va); msg = enif_make_tuple2(msg_env, print_atom, @@ -181,11 +188,7 @@ void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...) InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs; va_list va; va_start(va, frmt); -#if HAVE_VSNPRINTF - vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va); -#else - vsprintf(itcs->comment_buf, frmt, va); -#endif + my_vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va); va_end(va); itcs->result = TESTCASE_SUCCEEDED; @@ -199,11 +202,7 @@ void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...) InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs; va_list va; va_start(va, frmt); -#if HAVE_VSNPRINTF - vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va); -#else - vsprintf(itcs->comment_buf, frmt, va); -#endif + my_vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va); va_end(va); itcs->result = TESTCASE_SKIPPED; @@ -226,11 +225,7 @@ void testcase_failed(TestCaseState_t *tcs, char *frmt, ...) size_t bufsz = sizeof(buf); va_list va; va_start(va, frmt); -#if HAVE_VSNPRINTF - vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va); -#else - vsprintf(itcs->comment_buf, frmt, va); -#endif + my_vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va); va_end(va); itcs->result = TESTCASE_FAILED; -- cgit v1.2.3 From d1464746f9e2c24e7bc645a4fdb543d63a8e3e87 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 12 Oct 2015 17:06:27 +0200 Subject: erts: Pass free mem and build type to alloc_SUITE tests --- erts/emulator/test/alloc_SUITE.erl | 59 ++++++++++++++++++---- erts/emulator/test/alloc_SUITE_data/bucket_mask.c | 2 +- .../test/alloc_SUITE_data/testcase_driver.c | 30 ++++++++--- .../test/alloc_SUITE_data/testcase_driver.h | 3 ++ 4 files changed, 75 insertions(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 0dd68b778b..866008e3e0 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -202,13 +202,14 @@ drv_case(Config, Mode, NodeOpts) when is_list(Config) -> end. run_drv_case(Config, Mode) -> - ?line DataDir = ?config(data_dir,Config), - ?line CaseName = ?config(testcase,Config), + DataDir = ?config(data_dir,Config), + CaseName = ?config(testcase,Config), File = filename:join(DataDir, CaseName), {ok,CaseName,Bin} = compile:file(File, [binary,return_errors]), - ?line {module,CaseName} = erlang:load_module(CaseName,Bin), + {module,CaseName} = erlang:load_module(CaseName,Bin), ok = CaseName:init(File), + SlaveState = slave_init(CaseName), case Mode of one_shot -> Result = one_shot(CaseName); @@ -217,11 +218,26 @@ run_drv_case(Config, Mode) -> Result = concurrent(CaseName) end, - ?line true = erlang:delete_module(CaseName), - ?line Result. + true = erlang:delete_module(CaseName), + slave_end(SlaveState), + Result. + +slave_init(migration) -> + A0 = case application:start(sasl) of + ok -> [sasl]; + _ -> [] + end, + case application:start(os_mon) of + ok -> [os_mon|A0]; + _ -> A0 + end; +slave_init(_) -> []. + +slave_end(Apps) -> + lists:foreach(fun (A) -> application:stop(A) end, Apps). one_shot(CaseName) -> - State = CaseName:start(1), + State = CaseName:start({1, 0, erlang:system_info(build_type)}), Result0 = CaseName:run(State), false = (Result0 =:= continue), Result1 = handle_result(State, Result0), @@ -229,8 +245,8 @@ one_shot(CaseName) -> Result1. -many_shot(CaseName, I) -> - State = CaseName:start(I), +many_shot(CaseName, I, Mem) -> + State = CaseName:start({I, Mem, erlang:system_info(build_type)}), Result1 = repeat_while(fun() -> Result0 = CaseName:run(State), handle_result(State, Result0) @@ -239,12 +255,15 @@ many_shot(CaseName, I) -> Result1. concurrent(CaseName) -> + NSched = erlang:system_info(schedulers), + Mem = (free_memory() * 3) div 4, PRs = lists:map(fun(I) -> spawn_opt(fun() -> - many_shot(CaseName, I) + many_shot(CaseName, I, + Mem div NSched) end, [monitor, {scheduler,I}]) end, - lists:seq(1, erlang:system_info(schedulers))), + lists:seq(1, NSched)), lists:foreach(fun({Pid,Ref}) -> receive {'DOWN', Ref, process, Pid, Reason} -> Reason @@ -308,3 +327,23 @@ is_halfword_vm() -> {4, 8} -> true; {WS, WS} -> false end. + +free_memory() -> + %% Free memory in MB. + try + SMD = memsup:get_system_memory_data(), + {value, {free_memory, Free}} = lists:keysearch(free_memory, 1, SMD), + TotFree = (Free + + case lists:keysearch(cached_memory, 1, SMD) of + {value, {cached_memory, Cached}} -> Cached; + false -> 0 + end + + case lists:keysearch(buffered_memory, 1, SMD) of + {value, {buffered_memory, Buffed}} -> Buffed; + false -> 0 + end), + TotFree div (1024*1024) + catch + error : undef -> + ?t:fail({"os_mon not built"}) + end. diff --git a/erts/emulator/test/alloc_SUITE_data/bucket_mask.c b/erts/emulator/test/alloc_SUITE_data/bucket_mask.c index d474c80343..c94c265f4e 100644 --- a/erts/emulator/test/alloc_SUITE_data/bucket_mask.c +++ b/erts/emulator/test/alloc_SUITE_data/bucket_mask.c @@ -52,7 +52,7 @@ testcase_run(TestCaseState_t *tcs) typedef struct linked_block { struct linked_block* next; }Linked; - Linked* link; + Linked* link = NULL; Linked* fence_list; Linked* pad_list; void* tmp; diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c index 1503fea4cb..7dcca544e5 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef __WIN32__ static void my_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) @@ -54,7 +55,6 @@ static void my_snprintf(char *outBuf, size_t size, const char *format, ...) typedef struct { TestCaseState_t visible; - ErlNifEnv* curr_env; int result; jmp_buf* done_jmp_buf; char *comment; @@ -86,17 +86,26 @@ int testcase_nif_init(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) ERL_NIF_TERM testcase_nif_start(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ +{ /* (ThrNr, FreeMeg, BuildType) */ ERL_NIF_TERM ret; InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) enif_alloc_resource(testcase_rt, sizeof(InternalTestCaseState_t)); - - if (!itcs || !enif_get_int(env, argv[0], &itcs->visible.thr_nr)) { + int free_megabyte; + const int max_megabyte = INT_MAX / (1024*1024); + const ERL_NIF_TERM* tpl; + int tpl_arity; + + if (!itcs + || !enif_get_tuple(env, argv[0], &tpl_arity, &tpl) + || tpl_arity != 3 + || !enif_get_int(env, tpl[0], &itcs->visible.thr_nr) + || !enif_get_int(env, tpl[1], &free_megabyte)) { enif_make_badarg(env); } - + itcs->visible.free_mem = (free_megabyte < max_megabyte ? + free_megabyte : max_megabyte) * (1024*1024); itcs->visible.testcase_name = testcase_name(); - + itcs->visible.build_type = tpl[2]; itcs->visible.extra = NULL; itcs->result = TESTCASE_FAILED; itcs->comment = ""; @@ -126,7 +135,7 @@ testcase_nif_run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if (!enif_get_resource(env, argv[0], testcase_rt, (void**)&itcs)) return enif_make_badarg(env); - itcs->curr_env = env; + itcs->visible.curr_env = env; /* For some unknown reason, first call to setjmp crashes on win64 * when jmp_buf is allocated as part of the resource. But it works when @@ -146,6 +155,11 @@ testcase_nif_run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) case TESTCASE_SUCCEEDED: result_atom = "succeeded"; break; case TESTCASE_SKIPPED: result_atom = "skipped"; break; case TESTCASE_FAILED: result_atom = "failed"; break; + default: + result_atom = "failed"; + my_snprintf(itcs->comment_buf, sizeof(itcs->comment_buf), + "Unexpected test result code %d.", itcs->result); + itcs->comment = itcs->comment_buf; } return enif_make_tuple2(env, enif_make_atom(env, result_atom), @@ -176,7 +190,7 @@ testcase_printf(TestCaseState_t *tcs, char *frmt, ...) msg = enif_make_tuple2(msg_env, print_atom, enif_make_string(msg_env, itcs->comment_buf, ERL_NIF_LATIN1)); - enif_send(itcs->curr_env, enif_self(itcs->curr_env, &pid), + enif_send(itcs->visible.curr_env, enif_self(itcs->visible.curr_env, &pid), msg_env, msg); enif_free_env(msg_env); diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h index 698ae66fac..f0ca91bd06 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h @@ -24,8 +24,11 @@ #include typedef struct { + ErlNifEnv* curr_env; char *testcase_name; int thr_nr; + int free_mem; /* in bytes */ + ERL_NIF_TERM build_type; /* opt, debug, valgrind, ... */ void *extra; } TestCaseState_t; -- cgit v1.2.3 From 5c9557a814ac993a72d7b045ee374745d6dcab79 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 2 Oct 2015 18:14:28 +0200 Subject: erts: Improve alloc_SUITE:migration test In the quest to improve code coverage in cpool_fetch --- erts/emulator/beam/erl_alloc.c | 4 + erts/emulator/test/alloc_SUITE.erl | 82 ++++++++- .../test/alloc_SUITE_data/allocator_test.h | 1 + erts/emulator/test/alloc_SUITE_data/migration.c | 190 ++++++++++++++++----- 4 files changed, 231 insertions(+), 46 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 0aa45acd82..c3f4fe5a63 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3642,6 +3642,10 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3) *(void**)a3 = orig_destroying_mbc; return offset; } + case 0xf17: { + ErtsAllocatorThrSpec_t* ts = &erts_allctr_thr_spec[ERTS_ALC_A_TEST]; + return ts->allctr[0]->largest_mbc_size; + } default: break; } diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 866008e3e0..aa6a1fbcdc 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -65,7 +65,7 @@ end_per_group(_GroupName, Config) -> init_per_testcase(Case, Config) when is_list(Config) -> Dog = ?t:timetrap(?t:seconds(?DEFAULT_TIMETRAP_SECS)), - [{watchdog, Dog},{testcase, Case}|Config]. + [{watchdog, Dog}, {testcase, Case}, {debug,false} | Config]. end_per_testcase(_Case, Config) when is_list(Config) -> Dog = ?config(watchdog, Config), @@ -113,7 +113,13 @@ cpool(suite) -> []; cpool(doc) -> []; cpool(Cfg) -> ?line drv_case(Cfg). -migration(Cfg) -> drv_case(Cfg, concurrent, "+MZe true"). +migration(Cfg) -> + case erlang:system_info(smp_support) of + true -> + drv_case(Cfg, concurrent, "+MZe true"); + false -> + {skipped, "No smp"} + end. erts_mmap(Config) when is_list(Config) -> case {?t:os_type(), is_halfword_vm()} of @@ -207,6 +213,7 @@ run_drv_case(Config, Mode) -> File = filename:join(DataDir, CaseName), {ok,CaseName,Bin} = compile:file(File, [binary,return_errors]), {module,CaseName} = erlang:load_module(CaseName,Bin), + print_stats(CaseName), ok = CaseName:init(File), SlaveState = slave_init(CaseName), @@ -218,6 +225,9 @@ run_drv_case(Config, Mode) -> Result = concurrent(CaseName) end, + wait_for_memory_deallocations(), + print_stats(CaseName), + true = erlang:delete_module(CaseName), slave_end(SlaveState), Result. @@ -236,6 +246,41 @@ slave_init(_) -> []. slave_end(Apps) -> lists:foreach(fun (A) -> application:stop(A) end, Apps). +wait_for_memory_deallocations() -> + try + erts_debug:set_internal_state(wait, deallocations) + catch + error:undef -> + erts_debug:set_internal_state(available_internal_state, true), + wait_for_memory_deallocations() + end. + +print_stats(migration) -> + {Btot,Ctot} = lists:foldl(fun({instance,Inr,Istats}, {Bacc,Cacc}) -> + {mbcs,MBCS} = lists:keyfind(mbcs, 1, Istats), + Btup = lists:keyfind(blocks, 1, MBCS), + Ctup = lists:keyfind(carriers, 1, MBCS), + io:format("{instance,~p,~p,~p}\n", [Inr, Btup, Ctup]), + {tuple_add(Bacc,Btup),tuple_add(Cacc,Ctup)}; + (_, Acc) -> Acc + end, + {{blocks,0,0,0},{carriers,0,0,0}}, + erlang:system_info({allocator,test_alloc})), + + io:format("Number of blocks : ~p\n", [Btot]), + io:format("Number of carriers: ~p\n", [Ctot]); + +print_stats(_) -> ok. + +tuple_add(T1, T2) -> + list_to_tuple(lists:zipwith(fun(E1,E2) when is_number(E1), is_number(E2) -> + E1 + E2; + (A,A) -> + A + end, + tuple_to_list(T1), tuple_to_list(T2))). + + one_shot(CaseName) -> State = CaseName:start({1, 0, erlang:system_info(build_type)}), Result0 = CaseName:run(State), @@ -250,8 +295,10 @@ many_shot(CaseName, I, Mem) -> Result1 = repeat_while(fun() -> Result0 = CaseName:run(State), handle_result(State, Result0) - end), + end, + 10*1000, I), CaseName:stop(State), + flush_log(), Result1. concurrent(CaseName) -> @@ -272,11 +319,23 @@ concurrent(CaseName) -> PRs), ok. -repeat_while(Fun) -> - %%io:format("~p calls fun\n", [self()]), - case Fun() of - continue -> repeat_while(Fun); - R -> R +repeat_while(Fun, Timeout, I) -> + TRef = erlang:start_timer(Timeout, self(), timeout), + R = repeat_while_loop(Fun, TRef, I), + erlang:cancel_timer(TRef, [{async,true},{info,false}]), + R. + +repeat_while_loop(Fun, TRef, I) -> + receive + {timeout, TRef, timeout} -> + io:format("~p: Timeout, enough is enough.",[I]), + succeeded + after 0 -> + %%io:format("~p calls fun\n", [self()]), + case Fun() of + continue -> repeat_while_loop(Fun, TRef, I); + R -> R + end end. flush_log() -> @@ -308,6 +367,12 @@ handle_result(_State, Result0) -> end. start_node(Config, Opts) when is_list(Config), is_list(Opts) -> + case ?config(debug,Config) of + true -> {ok, node()}; + _ -> start_node_1(Config, Opts) + end. + +start_node_1(Config, Opts) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" @@ -318,6 +383,7 @@ start_node(Config, Opts) when is_list(Config), is_list(Opts) -> ++ integer_to_list(erlang:unique_integer([positive]))), ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). +stop_node(Node) when Node =:= node() -> ok; stop_node(Node) -> ?t:stop_node(Node). diff --git a/erts/emulator/test/alloc_SUITE_data/allocator_test.h b/erts/emulator/test/alloc_SUITE_data/allocator_test.h index dd0227e725..97ee58cdad 100644 --- a/erts/emulator/test/alloc_SUITE_data/allocator_test.h +++ b/erts/emulator/test/alloc_SUITE_data/allocator_test.h @@ -157,5 +157,6 @@ typedef void* erts_cond; #define ALLOC_TEST(S) ((void*) ALC_TEST1(0xf14, (S))) #define FREE_TEST(P) ((void) ALC_TEST1(0xf15, (P))) #define SET_TEST_MBC_USER_HEADER(SZ,CMBC,DMBC) ((int)ALC_TEST3(0xf16, (SZ), (CMBC), (DMBC))) +#define GET_TEST_MBC_SIZE() ((int) ALC_TEST0(0xf17)) #endif diff --git a/erts/emulator/test/alloc_SUITE_data/migration.c b/erts/emulator/test/alloc_SUITE_data/migration.c index 9f6535c834..b006360043 100644 --- a/erts/emulator/test/alloc_SUITE_data/migration.c +++ b/erts/emulator/test/alloc_SUITE_data/migration.c @@ -27,6 +27,7 @@ #include #endif #include +#include #include #include #include "testcase_driver.h" @@ -57,15 +58,52 @@ testcase_name(void) return "migration"; } -void -testcase_cleanup(TestCaseState_t *tcs) +/* Turns out random_r() is a nonstandard glibc extension. +#define HAVE_RANDOM_R +*/ +#ifdef HAVE_RANDOM_R + +typedef struct { struct random_data rnd; char rndbuf[32]; } MyRandState; + +static void myrand_init(MyRandState* mrs, unsigned int seed) { - enif_free(tcs->extra); - tcs->extra = NULL; + int res; + memset(&mrs->rnd, 0, sizeof(mrs->rnd)); + res = initstate_r(seed, mrs->rndbuf, sizeof(mrs->rndbuf), &mrs->rnd); + FATAL_ASSERT(res == 0); +} + +static int myrand(MyRandState* mrs) +{ + int32_t x; + int res = random_r(&mrs->rnd, &x); + FATAL_ASSERT(res == 0); + return (int)x; +} + +#else /* !HAVE_RANDOM_R */ + +typedef unsigned int MyRandState; + +static void myrand_init(MyRandState* mrs, unsigned int seed) +{ + *mrs = seed; +} + +static int myrand(MyRandState* mrs) +{ + /* Taken from rand(3) man page. + * Modified to return a full 31-bit value by using low half of *mrs as well. + */ + *mrs = (*mrs) * 1103515245 + 12345; + return (int) (((*mrs >> 16) | (*mrs << 16)) & ~(1 << 31)); } -#define MAX_BLOCK_PER_THR 100 -#define MAX_ROUNDS 10 +#endif /* !HAVE_RANDOM_R */ + +#define MAX_BLOCK_PER_THR 200 +#define BLOCKS_PER_MBC 10 +#define MAX_ROUNDS 10000 typedef struct MyBlock_ { struct MyBlock_* next; @@ -74,15 +112,23 @@ typedef struct MyBlock_ { typedef struct { MyBlock* blockv[MAX_BLOCK_PER_THR]; + MyRandState rand_state; enum { GROWING, SHRINKING, CLEANUP, DONE } phase; - int ix; + int nblocks; + int goal_nblocks; int round; + int nr_of_migrations; + int nr_of_carriers; + int max_blocks_in_mbc; + int block_size; + int max_nblocks; } MigrationState; typedef struct { ErlNifMutex* mtx; int nblocks; MyBlock* first; + MigrationState* employer; } MyCrrInfo; @@ -99,6 +145,7 @@ static void my_creating_mbc(Allctr_t *allctr, Carrier_t *carrier) mci->mtx = enif_mutex_create("alloc_SUITE.migration"); mci->nblocks = 0; mci->first = NULL; + mci->employer = NULL; } static void my_destroying_mbc(Allctr_t *allctr, Carrier_t *carrier) @@ -131,17 +178,28 @@ static int migration_init(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_in return 0; } -static void add_block(MyBlock* p) +static void add_block(MyBlock* p, MigrationState* state) { MyCrrInfo* mci = (MyCrrInfo*)((char*)BLK_TO_MBC(UMEM2BLK_TEST(p)) + crr_info_offset); enif_mutex_lock(mci->mtx); - mci->nblocks++; + if (++mci->nblocks > state->max_blocks_in_mbc) + state->max_blocks_in_mbc = mci->nblocks; p->next = mci->first; p->prevp = &mci->first; mci->first = p; if (p->next) p->next->prevp = &p->next; + if (mci->employer != state) { + if (!mci->employer) { + FATAL_ASSERT(mci->nblocks == 1); + state->nr_of_carriers++; + } + else { + state->nr_of_migrations++; + } + mci->employer = state; + } enif_mutex_unlock(mci->mtx); } @@ -157,6 +215,38 @@ static void remove_block(MyBlock* p) enif_mutex_unlock(mci->mtx); } +static int rand_int(MigrationState* state, int low, int high) +{ + int x; + FATAL_ASSERT(high >= low); + x = myrand(&state->rand_state); + return low + (x % (high+1-low)); +} + + +static void do_cleanup(TestCaseState_t *tcs, MigrationState* state) +{ + if (state->nblocks == 0) { + state->phase = DONE; + testcase_printf(tcs, "%d: Done %d rounds", tcs->thr_nr, state->round); + testcase_printf(tcs, "%d: Cleanup all blocks", tcs->thr_nr); + testcase_printf(tcs, "%d: Empty carriers detected = %d", tcs->thr_nr, + state->nr_of_carriers); + testcase_printf(tcs, "%d: Migrations detected = %d", tcs->thr_nr, + state->nr_of_migrations); + testcase_printf(tcs, "%d: Max blocks in carrier = %d", tcs->thr_nr, + state->max_blocks_in_mbc); + } + else { + state->nblocks--; + if (state->blockv[state->nblocks]) { + remove_block(state->blockv[state->nblocks]); + FREE_TEST(state->blockv[state->nblocks]); + } + } +} + + void testcase_run(TestCaseState_t *tcs) { @@ -169,61 +259,85 @@ testcase_run(TestCaseState_t *tcs) tcs->extra = enif_alloc(sizeof(MigrationState)); state = (MigrationState*) tcs->extra; memset(state->blockv, 0, sizeof(state->blockv)); + myrand_init(&state->rand_state, tcs->thr_nr); state->phase = GROWING; - state->ix = 0; + state->nblocks = 0; state->round = 0; + state->nr_of_migrations = 0; + state->nr_of_carriers = 0; + state->max_blocks_in_mbc = 0; + state->block_size = GET_TEST_MBC_SIZE() / (BLOCKS_PER_MBC+1); + if (MAX_BLOCK_PER_THR * state->block_size < tcs->free_mem) { + state->max_nblocks = MAX_BLOCK_PER_THR; + } else { + state->max_nblocks = tcs->free_mem / state->block_size; + } + state->goal_nblocks = rand_int(state, 1, state->max_nblocks); } switch (state->phase) { case GROWING: { MyBlock* p; - FATAL_ASSERT(!state->blockv[state->ix]); - p = ALLOC_TEST((1 << 18) / 5); + FATAL_ASSERT(!state->blockv[state->nblocks]); + p = ALLOC_TEST(rand_int(state, state->block_size/2, state->block_size)); FATAL_ASSERT(p); - add_block(p); - state->blockv[state->ix] = p; - do { - if (++state->ix >= MAX_BLOCK_PER_THR) { - state->phase = SHRINKING; - state->ix = 0; - break; - } - } while (state->blockv[state->ix] != NULL); + add_block(p, state); + state->blockv[state->nblocks] = p; + if (++state->nblocks >= state->goal_nblocks) { + /*testcase_printf(tcs, "%d: Grown to %d blocks", tcs->thr_nr, state->nblocks);*/ + state->phase = SHRINKING; + state->goal_nblocks = rand_int(state, 0, state->goal_nblocks-1); + } + else + FATAL_ASSERT(!state->blockv[state->nblocks]); break; } - case SHRINKING: - FATAL_ASSERT(state->blockv[state->ix]); - remove_block(state->blockv[state->ix]); - FREE_TEST(state->blockv[state->ix]); - state->blockv[state->ix] = NULL; - - state->ix += 1 + ((state->ix % 3) == 0); - if (state->ix >= MAX_BLOCK_PER_THR) { + case SHRINKING: { + int ix = rand_int(state, 0, state->nblocks-1); + FATAL_ASSERT(state->blockv[ix]); + remove_block(state->blockv[ix]); + FREE_TEST(state->blockv[ix]); + state->blockv[ix] = state->blockv[--state->nblocks]; + state->blockv[state->nblocks] = NULL; + + if (state->nblocks <= state->goal_nblocks) { + /*testcase_printf(tcs, "%d: Shrunk to %d blocks", tcs->thr_nr, state->nblocks);*/ if (++state->round >= MAX_ROUNDS) { state->phase = CLEANUP; } else { state->phase = GROWING; + state->goal_nblocks = rand_int(state, state->goal_nblocks+1, state->max_nblocks); } - state->ix = 0; } break; - + } case CLEANUP: - if (state->blockv[state->ix]) { - remove_block(state->blockv[state->ix]); - FREE_TEST(state->blockv[state->ix]); - } - if (++state->ix >= MAX_BLOCK_PER_THR) - state->phase = DONE; + do_cleanup(tcs, state); break; default: FATAL_ASSERT(!"Invalid phase"); } - if (state->phase != DONE) + if (state->phase == DONE) { + } + else { testcase_continue(tcs); + } } +void +testcase_cleanup(TestCaseState_t *tcs) +{ + MigrationState* state = (MigrationState*) tcs->extra; + + while (state->phase != DONE) + do_cleanup(tcs, state); + + enif_free(tcs->extra); + tcs->extra = NULL; +} + + ERL_NIF_INIT(migration, testcase_nif_funcs, migration_init, NULL, NULL, NULL); -- cgit v1.2.3 From bb1869148129c8ad30167e74aa6b4d7f16798116 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 22 Sep 2015 15:29:30 +0200 Subject: erts: Remove double free in efile_drv That double free is probably very seldom invoked as the port is already gone leading to free_data being called instead of file_async_ready. --- erts/emulator/drivers/common/efile_drv.c | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 8aff6c1865..3b6abec25e 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -2581,7 +2581,6 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) case FILE_CLOSE_ON_PORT_EXIT: /* See file_stop. However this is never invoked after the port is killed. */ free_data(data); - EF_FREE(desc); desc = NULL; /* This is it for this port, so just send dtrace and return, avoid doing anything to the freed data */ DTRACE6(efile_drv_return, sched_i1, sched_i2, sched_utag, -- cgit v1.2.3 From 64d1a7397c53d6cca32c49af9f833cdf6d26fc6e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 4 Nov 2015 17:55:49 +0100 Subject: erts: Reduce alloc_SUITE:rbtree runtime for valgrind --- erts/emulator/test/alloc_SUITE_data/rbtree.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE_data/rbtree.c b/erts/emulator/test/alloc_SUITE_data/rbtree.c index eb7b36984e..38bbbdf90c 100644 --- a/erts/emulator/test/alloc_SUITE_data/rbtree.c +++ b/erts/emulator/test/alloc_SUITE_data/rbtree.c @@ -20,7 +20,7 @@ #include "testcase_driver.h" #include "allocator_test.h" -#define NO_BLOCKS 100000 +int NO_BLOCKS; #define RIGHT_VISITED (1 << 0) #define LEFT_VISITED (1 << 1) @@ -265,9 +265,10 @@ check_tree(TestCaseState_t *tcs, Allctr_t *alc, Ulong size) ASSERT(tcs, curr_blacks == 0); ASSERT(tcs, i == -1); + /* testcase_printf(tcs, "Red-Black Tree OK! Max depth = %d; " "Black depth = %d\n", max_i+1, blacks < 0 ? 0 : blacks); - + */ return res; } @@ -468,6 +469,12 @@ testcase_run(TestCaseState_t *tcs) Allctr_t *a; rbtree_test_data *td; + NO_BLOCKS = 100*1000; + if (enif_is_identical(tcs->build_type, + enif_make_atom(tcs->curr_env,"valgrind"))) { + NO_BLOCKS /= 10; + } + /* Best fit... */ testcase_printf(tcs, "Setup...\n"); -- cgit v1.2.3 From cb62c989e59f0ec8556f9f1d4e9a45b8d9ee32f6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 27 Nov 2015 17:06:39 +0100 Subject: erts: Fix rare case of faulty heap fragment deallocation after major GC. Can only be caused by distributed messages containing large maps. Bad map hashing will increase the risk. --- erts/emulator/beam/erl_gc.c | 36 +++++++++++---- erts/emulator/test/map_SUITE.erl | 96 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index d2604f1595..2f21111a2e 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1237,6 +1237,7 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) Uint oh_size = (char *) OLD_HTOP(p) - oh; Uint n; Uint new_sz; + int done; /* * Do a fullsweep GC. First figure out the size of the heap @@ -1440,6 +1441,8 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) *recl += size_before - (HEAP_TOP(p) - HEAP_START(p)); + remove_message_buffers(p); + { ErlMessage *msgp; @@ -1458,15 +1461,21 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) } } - adjust_after_fullsweep(p, need, objv, nobj); - -#ifdef HARDDEBUG - disallow_heap_frag_ref_in_heap(p); -#endif - remove_message_buffers(p); + if (MBUF(p)) { + /* This is a very rare case when distributed messages copied above + * contained maps so big they did not fit on the heap causing the + * factory to create heap frags. + * Solution: Trigger a minor gc (without tenuring) + */ + HIGH_WATER(p) = HEAP_START(p); + done = 0; + } else { + adjust_after_fullsweep(p, need, objv, nobj); + done = 1; + } ErtsGcQuickSanityCheck(p); - return 1; /* We are done. */ + return done; } static void @@ -1955,7 +1964,18 @@ collect_heap_frags(Process* p, Eterm* n_hstart, Eterm* n_htop, if (p->dictionary != NULL) { disallow_heap_frag_ref(p, n_htop, p->dictionary->data, p->dictionary->used); } - disallow_heap_frag_ref_in_heap(p); + /* OTP-18: Actually we do allow references from heap to heap fragments now. + This can happen when doing "binary_to_term" with a "fat" map contained + in another term. A "fat" map is a hashmap with higher heap demand than + first estimated by "binary_to_term" causing the factory to allocate + additional heap (fragments) for the hashmap tree nodes. + Run map_SUITE:t_gc_rare_map_overflow to provoke this. + + Inverted references like this does not matter however. The copy done + below by move_one_area() with move markers in the fragments and the + sweeping done later by the GC should make everything ok in the end. + */ + /***disallow_heap_frag_ref_in_heap(p);***/ #endif /* diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 886ae7d516..1b9758c23e 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -58,6 +58,7 @@ %% erlang t_erlang_hash/1, t_map_encode_decode/1, + t_gc_rare_map_overflow/1, %% non specific BIF related t_bif_build_and_check/1, @@ -121,6 +122,7 @@ all() -> [ %% erlang t_erlang_hash, t_map_encode_decode, + t_gc_rare_map_overflow, t_map_size, t_is_map, %% non specific BIF related @@ -2966,3 +2968,97 @@ do_badmap_17(Config) -> %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. + + +%% OTP-13146 +%% Provoke major GC with a lot of "fat" maps on external format in msg queue +%% causing heap fragments to be allocated. +t_gc_rare_map_overflow(Config) -> + Pa = filename:dirname(code:which(?MODULE)), + {ok, Node} = test_server:start_node(gc_rare_map_overflow, slave, [{args, "-pa \""++Pa++"\""}]), + Echo = spawn_link(Node, fun Loop() -> receive {From,Msg} -> From ! Msg + end, + Loop() + end), + FatMap = fatmap(34), + false = (flatmap =:= erts_internal:map_type(FatMap)), + + t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> erlang:garbage_collect() end), + + % Repeat test for minor gc: + minor_collect(), % need this to make the next gc really be a minor + t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> true = minor_collect() end), + + unlink(Echo), + test_server:stop_node(Node). + +t_gc_rare_map_overflow_do(Echo, FatMap, GcFun) -> + Master = self(), + true = receive M -> false after 0 -> true end, % assert empty msg queue + Echo ! {Master, token}, + repeat(1000, fun(_) -> Echo ! {Master, FatMap} end, void), + + timer:sleep(100), % Wait for maps to arrive in our msg queue + token = receive Tok -> Tok end, % and provoke move from outer to inner msg queue + + %% Do GC that will "overflow" and create heap frags due to all the fat maps + GcFun(), + + %% Now check that all maps in msg queueu are intact + %% Will crash emulator in OTP-18.1 + repeat(1000, fun(_) -> FatMap = receive FM -> FM end end, void), + ok. + +minor_collect() -> + minor_collect(minor_gcs()). + +minor_collect(Before) -> + After = minor_gcs(), + case After of + _ when After > Before -> true; + _ when After =:= Before -> minor_collect(Before); + 0 -> false + end. + +minor_gcs() -> + {garbage_collection, Info} = process_info(self(), garbage_collection), + {minor_gcs, GCS} = lists:keyfind(minor_gcs, 1, Info), + GCS. + +%% Generate a map with N (or N+1) keys that has an abnormal heap demand. +%% Done by finding keys that collide in the first 32-bit hash. +fatmap(N) -> + erts_debug:set_internal_state(available_internal_state, true), + Table = ets:new(void, [bag, private]), + + Seed0 = rand:seed_s(exsplus, {4711, 3141592, 2718281}), + Seed1 = fatmap_populate(Table, Seed0, (1 bsl 16)), + Keys = fatmap_generate(Table, Seed1, N, []), + ets:delete(Table), + maps:from_list([{K,K} || K <- Keys]). + +fatmap_populate(_, Seed, 0) -> Seed; +fatmap_populate(Table, Seed, N) -> + {I, NextSeed} = rand:uniform_s(1 bsl 48, Seed), + Hash = internal_hash(I), + ets:insert(Table, [{Hash, I}]), + fatmap_populate(Table, NextSeed, N-1). + + +fatmap_generate(_, _, N, Acc) when N =< 0 -> + Acc; +fatmap_generate(Table, Seed, N0, Acc0) -> + {I, NextSeed} = rand:uniform_s(1 bsl 48, Seed), + Hash = internal_hash(I), + case ets:member(Table, Hash) of + true -> + NewKeys = [I | ets:lookup_element(Table, Hash, 2)], + Acc1 = lists:usort(Acc0 ++ NewKeys), + N1 = N0 - (length(Acc1) - length(Acc0)), + fatmap_generate(Table, NextSeed, N1, Acc1); + false -> + fatmap_generate(Table, NextSeed, N0, Acc0) + end. + +internal_hash(Term) -> + erts_debug:get_internal_state({internal_hash, Term}). -- cgit v1.2.3 From fd21382290333e6cc25728c1b6dd7c211ddfc297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20L=C3=A5ng?= Date: Fri, 23 Oct 2015 15:31:28 +0200 Subject: hipe: test unit size match in bs_append This feature was previously missing and expressions such as <<<<1:1>>/binary>> would succeed construction when compiled with HiPE. A primop is_divisible is introduced to handle the case when the unit size is not a power of two. --- erts/emulator/hipe/hipe_bif0.tab | 2 +- erts/emulator/hipe/hipe_bif_list.m4 | 1 + erts/emulator/hipe/hipe_native_bif.c | 12 ++++++++++++ erts/emulator/hipe/hipe_native_bif.h | 2 ++ erts/emulator/hipe/hipe_primops.h | 2 ++ 5 files changed, 18 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab index e3328c7d2c..5ce254314a 100644 --- a/erts/emulator/hipe/hipe_bif0.tab +++ b/erts/emulator/hipe/hipe_bif0.tab @@ -142,4 +142,4 @@ atom bs_validate_unicode atom bs_validate_unicode_retract atom emulate_fpe atom emasculate_binary - +atom is_divisible diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 6aa0c9a32e..7240280345 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -193,6 +193,7 @@ standard_bif_interface_2(nbif_rethrow, hipe_rethrow) standard_bif_interface_3(nbif_find_na_or_make_stub, hipe_find_na_or_make_stub) standard_bif_interface_2(nbif_nonclosure_address, hipe_nonclosure_address) nocons_nofail_primop_interface_0(nbif_fclearerror_error, hipe_fclearerror_error) +standard_bif_interface_2(nbif_is_divisible, hipe_is_divisible) /* * Mbox primops with implicit P parameter. diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index 688378b2fe..119b0b0895 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -504,6 +504,18 @@ int hipe_bs_validate_unicode_retract(ErlBinMatchBuffer* mb, Eterm arg) return 1; } +BIF_RETTYPE hipe_is_divisible(BIF_ALIST_2) +{ + /* Arguments are Eterm-sized unsigned integers */ + Uint dividend = BIF_ARG_1; + Uint divisor = BIF_ARG_2; + if (dividend % divisor) { + BIF_ERROR(BIF_P, BADARG); + } else { + return NIL; + } +} + /* This is like the loop_rec_fr BEAM instruction */ Eterm hipe_check_get_msg(Process *c_p) diff --git a/erts/emulator/hipe/hipe_native_bif.h b/erts/emulator/hipe/hipe_native_bif.h index 0e1a75f7eb..55a0d3bb1b 100644 --- a/erts/emulator/hipe/hipe_native_bif.h +++ b/erts/emulator/hipe/hipe_native_bif.h @@ -68,6 +68,7 @@ AEXTERN(Eterm,nbif_bs_put_utf16le,(Process*,Eterm,byte*,unsigned int)); AEXTERN(Eterm,nbif_bs_get_utf16,(void)); AEXTERN(Eterm,nbif_bs_validate_unicode,(Process*,Eterm)); AEXTERN(Eterm,nbif_bs_validate_unicode_retract,(void)); +AEXTERN(void,nbif_is_divisible,(Process*,Uint,Uint)); AEXTERN(void,nbif_select_msg,(Process*)); AEXTERN(Eterm,nbif_cmp_2,(void)); @@ -93,6 +94,7 @@ BIF_RETTYPE hipe_bs_put_utf16le(BIF_ALIST_3); BIF_RETTYPE hipe_bs_validate_unicode(BIF_ALIST_1); struct erl_bin_match_buffer; int hipe_bs_validate_unicode_retract(struct erl_bin_match_buffer*, Eterm); +BIF_RETTYPE hipe_is_divisible(BIF_ALIST_2); #ifdef NO_FPE_SIGNALS AEXTERN(void,nbif_emulate_fpe,(Process*)); diff --git a/erts/emulator/hipe/hipe_primops.h b/erts/emulator/hipe/hipe_primops.h index adf7b1f382..0bec677574 100644 --- a/erts/emulator/hipe/hipe_primops.h +++ b/erts/emulator/hipe/hipe_primops.h @@ -68,6 +68,8 @@ PRIMOP_LIST(am_bs_get_utf16, &nbif_bs_get_utf16) PRIMOP_LIST(am_bs_validate_unicode, &nbif_bs_validate_unicode) PRIMOP_LIST(am_bs_validate_unicode_retract, &nbif_bs_validate_unicode_retract) +PRIMOP_LIST(am_is_divisible, &nbif_is_divisible) + PRIMOP_LIST(am_cmp_2, &nbif_cmp_2) PRIMOP_LIST(am_op_exact_eqeq_2, &nbif_eq_2) -- cgit v1.2.3 From fa4cc49b0e64b655a167a5daceb2f16252102fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20L=C3=A5ng?= Date: Fri, 20 Nov 2015 15:25:30 +0100 Subject: Add missing corner-case to bs_construct_SUITE huge_binary/1 did not consider that the Shift variable is not a constant, and misses the case of a literal size. --- erts/emulator/test/bs_construct_SUITE.erl | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index cadb30e1a4..1afd01e27b 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -551,10 +551,24 @@ huge_binary(Config) when is_list(Config) -> ?line 16777216 = size(<<0:(id(1 bsl 26)),(-1):(id(1 bsl 26))>>), ?line garbage_collect(), {Shift,Return} = case free_mem() of - undefined -> {32,ok}; - Mb when Mb > 600 -> {32,ok}; - Mb when Mb > 300 -> {31,"Limit huge binaries to 256 Mb"}; - _ -> {30,"Limit huge binary to 128 Mb"} + undefined -> + %% This test has to be inlined inside the case to + %% use a literal Shift + ?line garbage_collect(), + ?line id(<<0:((1 bsl 32)-1)>>), + {32,ok}; + Mb when Mb > 600 -> + ?line garbage_collect(), + ?line id(<<0:((1 bsl 32)-1)>>), + {32,ok}; + Mb when Mb > 300 -> + ?line garbage_collect(), + ?line id(<<0:((1 bsl 31)-1)>>), + {31,"Limit huge binaries to 256 Mb"}; + _ -> + ?line garbage_collect(), + ?line id(<<0:((1 bsl 30)-1)>>), + {30,"Limit huge binary to 128 Mb"} end, ?line garbage_collect(), ?line id(<<0:((1 bsl Shift)-1)>>), -- cgit v1.2.3 From 99e6213c0f0cebaa01f8310b6950a814cf4b21ee Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 13 Nov 2015 18:04:09 +0100 Subject: erts: Remove dead code erts_hash_merge --- erts/emulator/beam/hash.c | 50 ----------------------------------------------- erts/emulator/beam/hash.h | 2 -- 2 files changed, 52 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c index e0fde337f2..636aafc108 100644 --- a/erts/emulator/beam/hash.c +++ b/erts/emulator/beam/hash.c @@ -280,56 +280,6 @@ void* hash_put(Hash* h, void* tmpl) return (void*) b; } -static void -hash_insert_entry(Hash* h, HashBucket* entry) -{ - HashValue hval = entry->hvalue; - int ix = hval % h->size; - HashBucket* b = h->bucket[ix]; - - while (b != (HashBucket*) 0) { - if ((b->hvalue == hval) && (h->fun.cmp((void*)entry, (void*)b) == 0)) { - abort(); /* Should not happen */ - } - b = b->next; - } - - if (h->bucket[ix] == NULL) - h->used++; - - entry->next = h->bucket[ix]; - h->bucket[ix] = entry; - - if (h->used > h->size80percent) /* rehash at 80% */ - rehash(h, 1); -} - - -/* - * Move all entries in src into dst; empty src. - * Entries in src must not exist in dst. - */ -void -erts_hash_merge(Hash* src, Hash* dst) -{ - int limit = src->size; - HashBucket** bucket = src->bucket; - int i; - - src->used = 0; - for (i = 0; i < limit; i++) { - HashBucket* b = bucket[i]; - HashBucket* next; - - bucket[i] = NULL; - while (b) { - next = b->next; - hash_insert_entry(dst, b); - b = next; - } - } -} - /* ** Erase hash entry return template if erased ** return 0 if not erased diff --git a/erts/emulator/beam/hash.h b/erts/emulator/beam/hash.h index 87fdb360e3..c9e75d7acf 100644 --- a/erts/emulator/beam/hash.h +++ b/erts/emulator/beam/hash.h @@ -93,6 +93,4 @@ void* hash_erase(Hash*, void*); void* hash_remove(Hash*, void*); void hash_foreach(Hash*, void (*func)(void *, void *), void *); -void erts_hash_merge(Hash* src, Hash* dst); - #endif -- cgit v1.2.3 From 4b4c3d525a06309b7e23c7c3ccf7a358bd0f33f3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 13 Nov 2015 18:12:32 +0100 Subject: erts: Redesign grow/shrink thresholds of hash.c 1. Use load factor as indicator, not used buckets. Used buckets is a bad indicator as it makes the situation even worse with a bad hash function. Set grow_threshold to load factor of 160% as it roughly corresponds to the old 80% used bucket limit. 2. Never shrink table below initial size. --- erts/emulator/beam/hash.c | 60 ++++++++++++++++++++++++----------------------- erts/emulator/beam/hash.h | 9 +++---- 2 files changed, 36 insertions(+), 33 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c index 636aafc108..9d247db039 100644 --- a/erts/emulator/beam/hash.c +++ b/erts/emulator/beam/hash.c @@ -66,6 +66,7 @@ void hash_get_info(HashInfo *hi, Hash *h) int i; int max_depth = 0; int objects = 0; + int used = 0; for (i = 0; i < size; i++) { int depth = 0; @@ -76,14 +77,18 @@ void hash_get_info(HashInfo *hi, Hash *h) depth++; b = b->next; } - if (depth > max_depth) - max_depth = depth; + if (depth) { + used++; + if (depth > max_depth) + max_depth = depth; + } } + ASSERT(objects == h->nobjs); hi->name = h->name; hi->size = h->size; - hi->used = h->used; - hi->objs = objects; + hi->used = used; + hi->objs = h->nobjs; hi->depth = max_depth; } @@ -119,6 +124,15 @@ hash_table_sz(Hash *h) } +static ERTS_INLINE void set_thresholds(Hash* h) +{ + h->grow_threshold = (8*h->size)/5; /* grow at 160% load */ + if (h->size_ix > h->min_size_ix) + h->shrink_threshold = h->size / 5; /* shrink at 20% load */ + else + h->shrink_threshold = -1; /* never shrink below inital size */ +} + /* ** init a pre allocated or static hash structure ** and allocate buckets. @@ -145,10 +159,10 @@ Hash* hash_init(ErtsAlcType_t type, Hash* h, char* name, int size, HashFunctions h->name = name; h->fun = fun; h->size = size; - h->size20percent = h->size/5; - h->size80percent = (4*h->size)/5; - h->ix = ix; - h->used = 0; + h->size_ix = ix; + h->min_size_ix = ix; + h->nobjs = 0; + set_thresholds(h); return h; } @@ -199,32 +213,26 @@ static void rehash(Hash* h, int grow) int i; if (grow) { - if ((h_size_table[h->ix+1]) == -1) + if ((h_size_table[h->size_ix+1]) == -1) return; - h->ix++; + h->size_ix++; } else { - if (h->ix == 0) + if (h->size_ix == 0) return; - h->ix--; + h->size_ix--; } - h->size = h_size_table[h->ix]; - h->size20percent = h->size/5; - h->size80percent = (4*h->size)/5; + h->size = h_size_table[h->size_ix]; sz = h->size*sizeof(HashBucket*); new_bucket = (HashBucket **) erts_alloc(h->type, sz); sys_memzero(new_bucket, sz); - h->used = 0; - for (i = 0; i < old_size; i++) { HashBucket* b = h->bucket[i]; while (b != (HashBucket*) 0) { HashBucket* b_next = b->next; int ix = b->hvalue % h->size; - if (new_bucket[ix] == NULL) - h->used++; b->next = new_bucket[ix]; new_bucket[ix] = b; b = b_next; @@ -232,6 +240,7 @@ static void rehash(Hash* h, int grow) } erts_free(h->type, (void *) h->bucket); h->bucket = new_bucket; + set_thresholds(h); } /* @@ -268,14 +277,11 @@ void* hash_put(Hash* h, void* tmpl) } b = (HashBucket*) h->fun.alloc(tmpl); - if (h->bucket[ix] == NULL) - h->used++; - b->hvalue = hval; b->next = h->bucket[ix]; h->bucket[ix] = b; - if (h->used > h->size80percent) /* rehash at 80% */ + if (++h->nobjs > h->grow_threshold) rehash(h, 1); return (void*) b; } @@ -298,9 +304,7 @@ void* hash_erase(Hash* h, void* tmpl) else h->bucket[ix] = b->next; h->fun.free((void*)b); - if (h->bucket[ix] == NULL) - h->used--; - if (h->used < h->size20percent) /* rehash at 20% */ + if (--h->nobjs < h->shrink_threshold) rehash(h, 0); return tmpl; } @@ -331,9 +335,7 @@ hash_remove(Hash *h, void *tmpl) prev->next = b->next; else h->bucket[ix] = b->next; - if (h->bucket[ix] == NULL) - h->used--; - if (h->used < h->size20percent) /* rehash at 20% */ + if (--h->nobjs < h->shrink_threshold) rehash(h, 0); return (void *) b; } diff --git a/erts/emulator/beam/hash.h b/erts/emulator/beam/hash.h index c9e75d7acf..dc7e9c10c5 100644 --- a/erts/emulator/beam/hash.h +++ b/erts/emulator/beam/hash.h @@ -72,10 +72,11 @@ typedef struct hash ErtsAlcType_t type; char* name; /* Table name (static string, for debugging) */ int size; /* Number of slots */ - int size20percent; /* 20 percent of number of slots */ - int size80percent; /* 80 percent of number of slots */ - int ix; /* Size index in size table */ - int used; /* Number of slots used */ + int shrink_threshold; + int grow_threshold; + int size_ix; /* Size index in size table */ + int min_size_ix; /* Never shrink table smaller than this */ + int nobjs; /* Number of objects in table */ HashBucket** bucket; /* Vector of bucket pointers (objects) */ } Hash; -- cgit v1.2.3 From 2afa7580910050b9b087a188215f27553cc0aba3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 30 Nov 2015 12:08:15 +0100 Subject: erts: Remove unused include files from hash.c Note that hash.c is quite "clean" from Erlang stuff and is used by erl_child_setup as well. --- erts/emulator/beam/hash.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c index 9d247db039..75d091d11c 100644 --- a/erts/emulator/beam/hash.c +++ b/erts/emulator/beam/hash.c @@ -27,8 +27,6 @@ #endif #include "sys.h" -#include "erl_vm.h" -#include "global.h" #include "hash.h" /* -- cgit v1.2.3 From 6fe04e5b66ce020651b2c150d26b9af8488b7dcd Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 30 Nov 2015 18:50:31 +0100 Subject: Remove ERTS_PRINT_INVALID from erts_print() ERTS_PRINT_INVALID prevented file descriptor 0 to be used which could cause an empty crash dump. --- erts/emulator/beam/sys.h | 1 - erts/emulator/beam/utils.c | 3 --- 2 files changed, 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index bb871b05ba..ec94e3a596 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -634,7 +634,6 @@ Uint erts_sys_misc_mem_sz(void); /* Io constants to erts_print and erts_putc */ #define ERTS_PRINT_STDERR (2) #define ERTS_PRINT_STDOUT (1) -#define ERTS_PRINT_INVALID (0) /* Don't want to use 0 since CBUF was 0 */ #define ERTS_PRINT_FILE (-1) #define ERTS_PRINT_SBUF (-2) #define ERTS_PRINT_SNBUF (-3) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index e9d7c91ac9..5286391746 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -399,9 +399,6 @@ erts_print(int to, void *arg, char *format, ...) case ERTS_PRINT_DSBUF: res = erts_vdsprintf((erts_dsprintf_buf_t *) arg, format, arg_list); break; - case ERTS_PRINT_INVALID: - res = -EINVAL; - break; default: res = erts_vfdprintf((int) to, format, arg_list); break; -- cgit v1.2.3 From 33299ece737c635910e358d7e09dd8af6bce1a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20L=C3=A5ng?= Date: Wed, 2 Dec 2015 14:30:56 +0100 Subject: beam: Fix overflow bug in i_bs_add_jId The test whether the result would fit in a smallnum could overflow into a negative number that would fit a smallnum. A test that reproduces the issue was added to bs_construct_SUITE. --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/test/bs_construct_SUITE.erl | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 38def5d89f..73292885ce 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4069,7 +4069,7 @@ do { \ tmp_arg1 += Arg1; store_bs_add_result: - if (MY_IS_SSMALL((Sint) tmp_arg1)) { + if (tmp_arg1 <= MAX_SMALL) { tmp_arg1 = make_small(tmp_arg1); } else { /* diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 1afd01e27b..f2bd6c233a 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -29,7 +29,7 @@ mem_leak/1, coerce_to_float/1, bjorn/1, huge_float_field/1, huge_binary/1, system_limit/1, badarg/1, copy_writable_binary/1, kostis/1, dynamic/1, bs_add/1, - otp_7422/1, zero_width/1, bad_append/1]). + otp_7422/1, zero_width/1, bad_append/1, bs_add_overflow/1]). -include_lib("test_server/include/test_server.hrl"). @@ -40,7 +40,7 @@ all() -> in_guard, mem_leak, coerce_to_float, bjorn, huge_float_field, huge_binary, system_limit, badarg, copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width, - bad_append]. + bad_append, bs_add_overflow]. groups() -> []. @@ -925,5 +925,19 @@ append_unit_8(Bin) -> append_unit_16(Bin) -> <>. +%% Produce a large result of bs_add that would fit a smallnum if it was viewed +%% as signed. +bs_add_overflow(Config) -> + case erlang:system_info(wordsize) of + 8 -> + {skip, "64-bit architecture"}; + 4 -> + Large = <<0:((1 bsl 30)-1)>>, + {'EXIT',{system_limit,_}} = + (catch <>), + ok + end. id(I) -> I. -- cgit v1.2.3 From 34380bad4985bc827866129597e0bea940e076f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20L=C3=A5ng?= Date: Tue, 1 Dec 2015 15:24:25 +0000 Subject: hipe: Fix signed compares of unsigned sizes Also, some of the branches were testing sizes in bits against a constant ?MAX_BINSIZE, which was in bytes. The signed comparisons masked this mistake. These branches have been removed since all sizes in bits that fit in a machine word are valid binary sizes. Finally, a test that reproduces the issue was added to bs_construct, along with a test for one of the cases (bs_init<0>(...)) when the test against ?MAX_BINSIZE must be changed to unsigned rather than removed. --- erts/emulator/test/bs_construct_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index f2bd6c233a..7ed99f5b4e 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -925,8 +925,8 @@ append_unit_8(Bin) -> append_unit_16(Bin) -> <>. -%% Produce a large result of bs_add that would fit a smallnum if it was viewed -%% as signed. +%% Produce a large result of bs_add that, if cast to signed int, would overflow +%% into a negative number that fits a smallnum. bs_add_overflow(Config) -> case erlang:system_info(wordsize) of 8 -> -- cgit v1.2.3 From ad50eefb67a69d755d46126bf5e436bf85644c8b Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 3 Dec 2015 11:11:17 +0100 Subject: Prepare release --- erts/doc/src/notes.xml | 19 +++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index cc224bee49..a64d699cec 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,25 @@

This document describes the changes made to the ERTS application.

+
Erts 6.4.1.4 + +
Fixed Bugs and Malfunctions + + +

+ The 'raw' socket option could not be used multiple times + in one call to any e.g gen_tcp function because only one + of the occurrences were used. This bug has been fixed, + and also a small bug concerning propagating error codes + from within inet:setopts/2.

+

+ Own Id: OTP-11482 Aux Id: seq12872

+
+
+
+ +
+
Erts 6.4.1.3
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index b8f4cf6946..4f5002d401 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.4.1.3 +VSN = 6.4.1.4 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 6b912d7fdd5fca46dee840b0bfa6a92915c0a093 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 3 Dec 2015 14:39:31 +0100 Subject: erts: Fix bug in heap_factory_undo for FACTORY_HEAP_FRAGS mode Make sure a heap fragment is not deallocated before all off_heap terms have been cleared. The fix assumes/asserts that the off_heap-lists of all additional heap fragments are empty. I think this bug has been harmless as hashmap nodes, which is only ones (?) that can cause a factory to produce more heap, are not linked in off_heap-list. --- erts/emulator/beam/erl_message.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index ef52823287..2a703fb102 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1372,13 +1372,16 @@ void erts_factory_undo(ErtsHeapFactory* factory) break; case FACTORY_HEAP_FRAGS: + erts_cleanup_offheap(factory->off_heap); + factory->off_heap->first = NULL; + bp = factory->heap_frags; do { ErlHeapFragment* next_bp = bp->next; - erts_cleanup_offheap(&bp->off_heap); + ASSERT(bp->off_heap.first == NULL); ERTS_HEAP_FREE(factory->alloc_type, (void *) bp, - ERTS_HEAP_FRAG_SIZE(bp->size)); + ERTS_HEAP_FRAG_SIZE(bp->alloc_size)); bp = next_bp; }while (bp != NULL); break; -- cgit v1.2.3 From 59aae51046e79b1c8a3cf2473fd4618e9d617ac6 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 30 Nov 2015 18:50:31 +0100 Subject: Remove ERTS_PRINT_INVALID from erts_print() ERTS_PRINT_INVALID prevented file descriptor 0 to be used which could cause an empty crash dump. --- erts/emulator/beam/sys.h | 1 - erts/emulator/beam/utils.c | 3 --- 2 files changed, 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index c29d4b3777..f8ab0df082 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -577,7 +577,6 @@ Uint erts_sys_misc_mem_sz(void); /* Io constants to erts_print and erts_putc */ #define ERTS_PRINT_STDERR (2) #define ERTS_PRINT_STDOUT (1) -#define ERTS_PRINT_INVALID (0) /* Don't want to use 0 since CBUF was 0 */ #define ERTS_PRINT_FILE (-1) #define ERTS_PRINT_SBUF (-2) #define ERTS_PRINT_SNBUF (-3) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index a8bbdb9354..ebc549fb31 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -343,9 +343,6 @@ erts_print(int to, void *arg, char *format, ...) case ERTS_PRINT_DSBUF: res = erts_vdsprintf((erts_dsprintf_buf_t *) arg, format, arg_list); break; - case ERTS_PRINT_INVALID: - res = -EINVAL; - break; default: res = erts_vfdprintf((int) to, format, arg_list); break; -- cgit v1.2.3 From 6fd3aba6bc714b779c5c91ed998a7661190ec882 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 4 Dec 2015 15:13:14 +0100 Subject: Prepare release --- erts/doc/src/notes.xml | 16 ++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index a64d699cec..9ff2e5de08 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,22 @@

This document describes the changes made to the ERTS application.

+
Erts 6.4.1.5 + +
Fixed Bugs and Malfunctions + + +

+ Fixed a bug that could cause a crash dump to become + almost empty.

+

+ Own Id: OTP-13150

+
+
+
+ +
+
Erts 6.4.1.4
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 4f5002d401..98946f4797 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.4.1.4 +VSN = 6.4.1.5 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 23885a8ab609688641098a759110e295052323f8 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 1 Dec 2015 10:58:43 +0100 Subject: erts: Correct the types section in The Abstract Format document The Types section is more consistent with Kostis' text in The Reference Manual. --- erts/doc/src/absform.xml | 375 ++++++++++++++++++++++------------------------- 1 file changed, 178 insertions(+), 197 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index df2553ced3..8584898a9d 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -61,7 +61,7 @@

- Module declarations and forms + Module Declarations and Forms

A module declaration consists of a sequence of forms that are either function declarations or attributes.

@@ -78,204 +78,87 @@ Rep(F) = . If F is an attribute , then Rep(F) = . + If F is an attribute , then + Rep(F) = . If F is an attribute , then Rep(F) = . If F is an attribute , then Rep(F) = . - If F is a record declaration , then - Rep(F) = - . For Rep(V), see below. - If F is a type attribute (i.e. or - ) - where each - is a variable, then Rep(F) = - . - For Rep(T), see below. - If F is a type spec (i.e. or - ) - , - where each is a fun type clause with an - argument sequence of the same length , then - Rep(F) = - . - For Rep(Tc_i), see below. - If F is a type spec (i.e. or - ) - , - where each is a fun type clause with an - argument sequence of the same length , then - Rep(F) = - . - For Rep(Tc_i), see below. + If F is a record declaration + -record(Name,{V_1, ..., V_k}), then Rep(F) = + {attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}. + For Rep(V), see below. + If F is a type declaration + -Type Name(V_1, ..., V_k) :: T, where + Type is either the atom type or the atom opaque, + each V_i is a variable, and T is a type, then Rep(F) = + {attribute,LINE,Type,{Name,Rep(T),[Rep(V_1), ..., Rep(V_k)]}}. + + If F is a function specification + -Spec Name Ft_1; ...; Ft_k, + where Spec is either the atom spec or the atom + callback, and each Ft_i is a possibly constrained + function type with an argument sequence of the same length + Arity, then Rep(F) = + {attribute,Line,Spec,{{Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}. + + If F is a function specification + -spec Mod:Name Ft_1; ...; Ft_k, + where each Ft_i is a possibly constrained + function type with an argument sequence of the same length + Arity, then Rep(F) = + {attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}. + If F is a wild attribute , then Rep(F) = .

- If F is a function declaration , - where each is a function clause with a - pattern sequence of the same length , then - Rep(F) = . + If F is a function declaration + Name Fc_1 ; ... ; Name Fc_k, + where each Fc_i is a function clause with a + pattern sequence of the same length Arity, then + Rep(F) = {function,LINE,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}. +
- Type clauses - - If T is a fun type clause - Ret]]>, where each - and are types, then - Rep(T) = - . - - If T is a bounded fun type clause , - where is an unbounded fun type clause and - is a type guard sequence, then Rep(T) = - . - -
- -
- Type guards - - If G is a constraint , where - is an atom and each is a - type, then Rep(G) = - . - - If G is a type definition , - where is a variable and - is a type, then Rep(G) = - . - -
- -
- Types - - If T is a type definition , - where is a variable and - is a type, then Rep(T) = - . - If T is a type union , - where each is a type, then Rep(T) = - . - If T is a type range , - where and are types, then - Rep(T) = . - If T is a binary operation , - where is an arithmetic or bitwise binary operator - and and are types, then - Rep(T) = . - If T is , where is an - arithmetic or bitwise unary operator and is a - type, then Rep(T) = . - If T is a fun type , then Rep(T) = - . - If T is a variable , then Rep(T) = - , where is an atom - with a printname consisting of the same characters as - . - If T is an atomic literal L and L is not a string literal, then - Rep(T) = Rep(L). - If T is a tuple or map type (i.e. - or ), then Rep(T) = - . - If T is a type , where each - is a type, then Rep(T) = - . - If T is a remote type , where - each is a type and and - , then Rep(T) = - . - - If T is the nil type , then Rep(T) = - . - If T is a list type , where - is a type, then Rep(T) = - . - If T is a non-empty list type , where - is a type, then Rep(T) = - . - If T is a map type , where each - is a map pair type, then Rep(T) = - . - If T is a map pair type V]]>, where - and are types, - then Rep(T) = - . - If T is a tuple type , where - each is a type, then Rep(T) = - . - If T is a record type , where - is an atom, then Rep(T) = - . - If T is a record type , - where is an atom, then Rep(T) = - . - - If T is a record field type , - where is an atom, then Rep(T) = - . - If T is a record field type >]]>, then Rep(T) = - . - - If T is a binary type >]]>, where - is a type, then Rep(T) = - . - If T is a binary type >]]>, - where is a type, then Rep(T) = - . - If T is a binary type >]]>, - where and is a type, then - Rep(T) = - . - - If T is a fun type Ret)]]>, then - Rep(T) = . - - If T is a fun type , where - is an unbounded fun type clause, - then Rep(T) = . - -
- -
- Record fields + Record Fields

Each field in a record declaration may have an optional - explicit default initializer expression

+ explicit default initializer expression, as well as an + optional type.

If V is , then Rep(V) = . - If V is , then + If V is , + where E is an expression, then Rep(V) = . - If V is , where is - an atom and is a type and it does not contain - syntactically, then Rep(V) = - . - Note that if is an annotated type, it will be wrapped in - parentheses. - If V is , where is - an atom and is a type, then Rep(V) = - . - - If V is , where - is an atom, is an expression and - is a type, then Rep(V) = - . - + If V is A :: T, where T is a + type and it does not contain + undefined syntactically, then Rep(V) = + {typed_record_field,{record_field,LINE,Rep(A)},Rep(undefined | T)}. + + If V is A :: T, where T is a type, then Rep(V) = + {typed_record_field,{record_field,LINE,Rep(A)},Rep(T)}. + + If V is A = E :: T, where + E is an expression and T is a type, then Rep(V) = + {typed_record_field,{record_field,LINE,Rep(A),Rep(E)},Rep(T)}. +
- Representation of parse errors and end of file + Representation of Parse Errors and End-of-file

In addition to the representations of forms, the list that represents - a module declaration (as returned by functions in and - ) may contain tuples and , denoting - syntactically incorrect forms and warnings, and , denoting an end - of stream encountered before a complete form had been parsed.

+ a module declaration (as returned by functions in erl_parse and + epp) may contain tuples {error,E} and + {warning,W}, denoting syntactically incorrect forms and + warnings, and {eof,LINE}, denoting an end-of-stream + encountered before a complete form had been parsed.

- Atomic literals + Atomic Literals

There are five kinds of atomic literals, which are represented in the same way in patterns, expressions and guards:

@@ -330,12 +213,12 @@ time), then Rep(P) = . If P is a record pattern , then Rep(P) = - . + . If P is , then Rep(P) = . If P is , then Rep(P) = , - i.e., patterns cannot be distinguished from their bodies. + that is, patterns cannot be distinguished from their bodies.

Note that every pattern has the same source form as some expression, and is represented the same way as the corresponding expression.

@@ -372,10 +255,10 @@ Rep(E) = . If E is , then Rep(E) = - . + . If E is , then Rep(E) = - . + . If E is , then Rep(E) = . If E is , then @@ -466,20 +349,13 @@ is a function clause then Rep(E) = . - If E is , - where each is a generator or a filter, then - Rep(E) = . - For Rep(W), see below. - If E is , a Mnesia record access - inside a query, then - Rep(E) = . If E is , then - Rep(E) = , - i.e., parenthesized expressions cannot be distinguished from their bodies. + Rep(E) = Rep(E_0), that is, parenthesized + expressions cannot be distinguished from their bodies.
- Generators and filters + Generators and Filters

When W is a generator or a filter (in the body of a list or binary comprehension), then:

If W is a generator , where is a pattern and @@ -494,20 +370,20 @@
- Binary element type specifiers + Binary Element Type Specifiers

A type specifier list TSL for a binary element is a sequence of type specifiers . Rep(TSL) = .

When TS is a type specifier for a binary element, then:

- If TS is an atom , Rep(TS) = . + If TS is an atom , then Rep(TS) = . If TS is a couple where is an atom and - is an integer, Rep(TS) = . + is an integer, then Rep(TS) = {A,Value}.
- Map assoc and exact fields + Map Assoc and Exact Fields

When W is an assoc or exact field (in the body of a map), then:

If W is an assoc field V]]>, where @@ -595,7 +471,7 @@ Rep(Gt) = . If Gt is , then Rep(E) = - . + . If Gt is , then Rep(Gt) = . If Gt is , then @@ -609,15 +485,120 @@ the atom and is an atom or an operator, then Rep(Gt) = . If Gt is , then - Rep(Gt) = , - i.e., parenthesized guard tests cannot be distinguished from their bodies. + Rep(Gt) = , that is, parenthesized + guard tests cannot be distinguished from their bodies.

Note that every guard test has the same source form as some expression, and is represented the same way as the corresponding expression.

- The abstract format after preprocessing + Types + + If T is an annotated type Anno :: Type, + where Anno is a variable and + Type is a type, then Rep(T) = + {ann_type,LINE,[Rep(Anno),Rep(Type)]}. + If T is an atom or integer literal L, then Rep(T) = Rep(L). + + If T is L Op R, + where Op is a binary operator and L and R + are types (this is an occurrence of an expression that can be + evaluated to an integer at compile time), then + Rep(T) = {op,LINE,Op,Rep(L),Rep(R)}. + If T is Op A, where Op is a + unary operator and A is a type (this is an occurrence of + an expression that can be evaluated to an integer at compile time), + then Rep(T) = {op,LINE,Op,Rep(A)}. + If T is a bitstring type <<_:M,_:_*N>>, + where M and N are singleton integer types, then Rep(T) = + {type,LINE,binary,[Rep(M),Rep(N)]}. + If T is the empty list type [], then Rep(T) = + {type,Line,nil,[]}. + If T is a fun type fun(), then Rep(T) = + {type,LINE,'fun',[]}. + If T is a fun type fun((...) -> B), + where B is a type, then + Rep(T) = {type,LINE,'fun',[{type,LINE,any},Rep(B)]}. + + If T is a fun type fun(Ft), where + Ft is a function type, + then Rep(T) = Rep(Ft). + If T is an integer range type L .. H, + where L and H are singleton integer types, then + Rep(T) = {type,LINE,range,[Rep(L),Rep(H)]}. + If T is a map type map(), then Rep(T) = + {type,LINE,map,any}. + If T is a map type #{P_1, ..., P_k}, where each + P_i is a map pair type, then Rep(T) = + {type,LINE,map,[Rep(P_1), ..., Rep(P_k)]}. + If T is a map pair type K => V, where + K and V are types, then Rep(T) = + {type,LINE,map_field_assoc,[Rep(K),Rep(V)]}. + If T is a predefined (or built-in) type N(A_1, ..., A_k), + where each A_i is a type, then Rep(T) = + {type,LINE,N,[Rep(A_1), ..., Rep(A_k)]}. + If T is a record type #Name{F_1, ..., F_k}, + where each F_i is a record field type, then Rep(T) = + {type,LINE,record,[Rep(Name),Rep(F_1), ..., Rep(F_k)]}. + + If T is a record field type Name :: Type, + where Type is a type, then Rep(T) = + {type,LINE,field_type,[Rep(Name),Rep(Type)]}. + If T is a remote type M:N(A_1, ..., A_k), where + each A_i is a type, then Rep(T) = + {remote_type,LINE,[Rep(M),Rep(N),[Rep(A_1), ..., Rep(A_k)]]}. + + If T is a tuple type tuple(), then Rep(T) = + {type,LINE,tuple,any}. + If T is a tuple type {A_1, ..., A_k}, where + each A_i is a type, then Rep(T) = + {type,LINE,tuple,[Rep(A_1), ..., Rep(A_k)]}. + If T is a type union T_1 | ... | T_k, + where each T_i is a type, then Rep(T) = + {type,LINE,union,[Rep(T_1), ..., Rep(T_k)]}. + If T is a type variable V, then Rep(T) = + {var,LINE,A}, where A is an atom with a printname + consisting of the same characters as V. A type variable + is any variable except underscore (_). + If T is a user-defined type N(A_1, ..., A_k), + where each A_i is a type, then Rep(T) = + {user_type,LINE,N,[Rep(A_1), ..., Rep(A_k)]}. + If T is ( T_0 ), then Rep(T) = Rep(T_0), + that is, parenthesized types cannot be distinguished from their + bodies. + + +
+ Function Types + + If Ft is a constrained function type Ft_1 when Fc, + where Ft_1 is a function type and + Fc is a function constraint, then Rep(T) = + {type,LINE,bounded_fun,[Rep(Ft_1),Rep(Fc)]}. + If Ft is a function type (A_1, ..., A_n) -> B, + where each A_i and B are types, then + Rep(Ft) = {type,LINE,'fun',[{type,LINE,product,[Rep(A_1), + ..., Rep(A_n)]},Rep(B)]}. + +
+ +
+ Function Constraints +

A function constraint Fc is a nonempty sequence of constraints + C_1, ..., C_k, and + Rep(Fc) = [Rep(C_1), ..., Rep(C_k)].

+ + If C is a constraint is_subtype(V, T) or V :: T, + where V is a type variable and T is a type, then + Rep(C) = {type,LINE,constraint,[Rep(F),[Rep(V),Rep(T)]]}. + + +
+
+ +
+ The Abstract Format After Preprocessing

The compilation option can be given to the compiler to have the abstract code stored in the chunk in the BEAM file -- cgit v1.2.3 From af1e0396fda62efb6c0817134b419b166b110d09 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 1 Dec 2015 12:45:03 +0100 Subject: erts: Remove CDATA from The Abstract Format document --- erts/doc/src/absform.xml | 579 +++++++++++++++++++++++------------------------ 1 file changed, 288 insertions(+), 291 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 8584898a9d..ca06794a53 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -11,7 +11,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software @@ -19,7 +19,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + The Abstract Format @@ -35,24 +35,24 @@

This document describes the standard representation of parse trees for Erlang programs as Erlang terms. This representation is known as the abstract format. - Functions dealing with such parse trees are + Functions dealing with such parse trees are compile:forms/[1,2] and functions in the modules - , - , - , - , - , + epp, + erl_eval, + erl_lint, + erl_pp, + erl_parse, and - . + io. They are also used as input and output for parse transforms (see the module - ).

-

We use the function to denote the mapping from an Erlang source - construct to its abstract format representation , and write - . + compile).

+

We use the function Rep to denote the mapping from an Erlang source + construct C to its abstract format representation R, and write + R = Rep(C).

-

The word below represents an integer, and denotes the +

The word LINE below represents an integer, and denotes the number of the line in the source file where the construction occurred. - Several instances of in the same construction may denote + Several instances of LINE in the same construction may denote different lines.

Since operators are not terms in their own right, when operators are mentioned below, the representation of an operator should be taken to @@ -66,24 +66,24 @@ function declarations or attributes.

If D is a module declaration consisting of the forms - , ..., , then - Rep(D) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . + F_1, ..., F_k, then + Rep(D) = [Rep(F_1), ..., Rep(F_k)]. + If F is an attribute -module(Mod), then + Rep(F) = {attribute,LINE,module,Mod}. + If F is an attribute -behavior(Behavior), then + Rep(F) = {attribute,LINE,behavior,Behavior}. + If F is an attribute -behaviour(Behaviour), then + Rep(F) = {attribute,LINE,behaviour,Behaviour}. + If F is an attribute -export([Fun_1/A_1, ..., Fun_k/A_k]), then + Rep(F) = {attribute,LINE,export,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}. + If F is an attribute -import(Mod,[Fun_1/A_1, ..., Fun_k/A_k]), then + Rep(F) = {attribute,LINE,import,{Mod,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}}. + If F is an attribute -export_type([Type_1/A_1, ..., Type_k/A_k]), then + Rep(F) = {attribute,LINE,export_type,[{Type_1,A_1}, ..., {Type_k,A_k}]}. + If F is an attribute -compile(Options), then + Rep(F) = {attribute,LINE,compile,Options}. + If F is an attribute -file(File,Line), then + Rep(F) = {attribute,LINE,file,{File,Line}}. If F is a record declaration -record(Name,{V_1, ..., V_k}), then Rep(F) = {attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}. @@ -109,8 +109,8 @@ Arity, then Rep(F) = {attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}. - If F is a wild attribute , then - Rep(F) = . + If F is a wild attribute -A(T), then + Rep(F) = {attribute,LINE,A,T}.

If F is a function declaration Name Fc_1 ; ... ; Name Fc_k, @@ -126,11 +126,11 @@ explicit default initializer expression, as well as an optional type.

- If V is , then - Rep(V) = . - If V is , + If V is A, then + Rep(V) = {record_field,LINE,Rep(A)}. + If V is A = E, where E is an expression, then - Rep(V) = . + Rep(V) = {record_field,LINE,Rep(A),Rep(E)}. If V is A :: T, where T is a type and it does not contain undefined syntactically, then Rep(V) = @@ -163,14 +163,14 @@ same way in patterns, expressions and guards:

If L is an integer or character literal, then - Rep(L) = . + Rep(L) = {integer,LINE,L}.
If L is a float literal, then - Rep(L) = . + Rep(L) = {float,LINE,L}.
If L is a string literal consisting of the characters - , ..., , then - Rep(L) = . + C_1, ..., C_k, then + Rep(L) = {string,LINE,[C_1, ..., C_k]}.
If L is an atom literal, then - Rep(L) = . + Rep(L) = {atom,LINE,L}.

Note that negative integer and float literals do not occur as such; they are parsed as an application of the unary negation operator.

@@ -178,46 +178,46 @@
Patterns -

If is a sequence of patterns , then - Rep(Ps) = . Such sequences occur as the +

If Ps is a sequence of patterns P_1, ..., P_k, then + Rep(Ps) = [Rep(P_1), ..., Rep(P_k)]. Such sequences occur as the list of arguments to a function or fun.

Individual patterns are represented as follows:

If P is an atomic literal L, then Rep(P) = Rep(L). - If P is a compound pattern , then - Rep(P) = . - If P is a variable pattern , then - Rep(P) = , + If P is a compound pattern P_1 = P_2, then + Rep(P) = {match,LINE,Rep(P_1),Rep(P_2)}. + If P is a variable pattern V, then + Rep(P) = {var,LINE,A}, where A is an atom with a printname consisting of the same characters as - . - If P is a universal pattern , then - Rep(P) = . - If P is a tuple pattern , then - Rep(P) = . - If P is a nil pattern , then - Rep(P) = . - If P is a cons pattern , then - Rep(P) = . - If E is a binary pattern >]]>, then - Rep(E) = . + V. + If P is a universal pattern _, then + Rep(P) = {var,LINE,'_'}. + If P is a tuple pattern {P_1, ..., P_k}, then + Rep(P) = {tuple,LINE,[Rep(P_1), ..., Rep(P_k)]}. + If P is a nil pattern [], then + Rep(P) = {nil,LINE}. + If P is a cons pattern [P_h | P_t], then + Rep(P) = {cons,LINE,Rep(P_h),Rep(P_t)}. + If E is a binary pattern <<P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>>, then + Rep(E) = {bin,LINE,[{bin_element,LINE,Rep(P_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(P_k),Rep(Size_k),Rep(TSL_k)}]}. For Rep(TSL), see below. - An omitted is represented by . An omitted - (type specifier list) is represented by . - If P is , where is a binary operator (this - is either an occurrence of applied to a literal string or character + An omitted Size is represented by default. An omitted TSL + (type specifier list) is represented by default. + If P is P_1 Op P_2, where Op is a binary operator (this + is either an occurrence of ++ applied to a literal string or character list, or an occurrence of an expression that can be evaluated to a number at compile time), - then Rep(P) = . - If P is , where is a unary operator (this is an + then Rep(P) = {op,LINE,Op,Rep(P_1),Rep(P_2)}. + If P is Op P_0, where Op is a unary operator (this is an occurrence of an expression that can be evaluated to a number at compile - time), then Rep(P) = . - If P is a record pattern , + time), then Rep(P) = {op,LINE,Op,Rep(P_0)}. + If P is a record pattern #Name{Field_1=P_1, ..., Field_k=P_k}, then Rep(P) = - . - If P is , then - Rep(P) = . - If P is , then - Rep(P) = , + {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(P_k)}]}. + If P is #Name.Field, then + Rep(P) = {record_index,LINE,Name,Rep(Field)}. + If P is ( P_0 ), then + Rep(P) = Rep(P_0), that is, patterns cannot be distinguished from their bodies.

Note that every pattern has the same source form as some expression, and is @@ -226,159 +226,153 @@

Expressions -

A body B is a sequence of expressions , and - Rep(B) = .

+

A body B is a sequence of expressions E_1, ..., E_k, and + Rep(B) = [Rep(E_1), ..., Rep(E_k)].

An expression E is one of the following alternatives:

- If P is an atomic literal , then - Rep(P) = Rep(L). - If E is , then - Rep(E) = . - If E is a variable , then - Rep(E) = , - where is an atom with a printname consisting of the same - characters as . - If E is a tuple skeleton , then - Rep(E) = . - If E is , then - Rep(E) = . - If E is a cons skeleton , then - Rep(E) = . - If E is a binary constructor >]]>, then - Rep(E) = . + If P is an atomic literal L, then Rep(P) = Rep(L). + If E is P = E_0, then + Rep(E) = {match,LINE,Rep(P),Rep(E_0)}. + If E is a variable V, then Rep(E) = {var,LINE,A}, + where A is an atom with a printname consisting of the same + characters as V. + If E is a tuple skeleton {E_1, ..., E_k}, then + Rep(E) = {tuple,LINE,[Rep(E_1), ..., Rep(E_k)]}. + If E is [], then + Rep(E) = {nil,LINE}. + If E is a cons skeleton [E_h | E_t], then + Rep(E) = {cons,LINE,Rep(E_h),Rep(E_t)}. + If E is a binary constructor <<V_1:Size_1/TSL_1, ..., V_k:Size_k/TSL_k>>, then Rep(E) = + {bin,LINE,[{bin_element,LINE,Rep(V_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(V_k),Rep(Size_k),Rep(TSL_k)}]}. For Rep(TSL), see below. - An omitted is represented by . An omitted - (type specifier list) is represented by . - If E is , where is a binary operator, - then Rep(E) = . - If E is , where is a unary operator, then - Rep(E) = . - If E is , then - Rep(E) = - . - If E is , then - Rep(E) = - . - If E is , then - Rep(E) = . - If E is , then - Rep(E) = . - If E is where each - is a map assoc or exact field, then Rep(E) = - . For Rep(W), see - below. - If E is where - is a map assoc or exact field, then Rep(E) = - . For - Rep(W), see below. - If E is , then - Rep(E) = . - If E is , then - Rep(E) = . - If E is , then + An omitted Size is represented by default. An omitted TSL + (type specifier list) is represented by default. + If E is E_1 Op E_2, where Op is a binary operator, + then Rep(E) = {op,LINE,Op,Rep(E_1),Rep(E_2)}. + If E is Op E_0, where Op is a unary operator, then + Rep(E) = {op,LINE,Op,Rep(E_0)}. + If E is #Name{Field_1=E_1, ..., Field_k=E_k}, + then Rep(E) = + {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}. + If E is E_0#Name{Field_1=E_1, ..., Field_k=E_k}, then Rep(E) = - . - If E is a list comprehension , - where each is a generator or a filter, then - Rep(E) = . For Rep(W), see + {record,LINE,Rep(E_0),Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}. + If E is #Name.Field, then + Rep(E) = {record_index,LINE,Name,Rep(Field)}. + If E is E_0#Name.Field, then + Rep(E) = {record_field,LINE,Rep(E_0),Name,Rep(Field)}. + If E is #{W_1, ..., W_k} where each + W_i is a map assoc or exact field, then Rep(E) = + {map,LINE,[Rep(W_1), ..., Rep(W_k)]}. For Rep(W), see below. - If E is a binary comprehension >]]>, - where each is a generator or a filter, then - Rep(E) = . For Rep(W), see + If E is E_0#{W_1, ..., W_k} where + W_i is a map assoc or exact field, then Rep(E) = + {map,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}. + For Rep(W), see below. + If E is catch E_0, then + Rep(E) = {'catch',LINE,Rep(E_0)}. + If E is E_0(E_1, ..., E_k), then + Rep(E) = {call,LINE,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}. + If E is E_m:E_0(E_1, ..., E_k), then Rep(E) = + {call,LINE,{remote,LINE,Rep(E_m),Rep(E_0)},[Rep(E_1), ..., Rep(E_k)]}. + + If E is a list comprehension [E_0 || W_1, ..., W_k], + where each W_i is a generator or a filter, then Rep(E) = + {lc,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}. For Rep(W), see below. - If E is , where is a body, then - Rep(E) = . - If E is , - where each is an if clause then - Rep(E) = - . - If E is , - where is an expression and each is a - case clause then - Rep(E) = - . - If E is , - where is a body and each is a catch clause then + If E is a binary comprehension + <<E_0 || W_1, ..., W_k>>, + where each W_i is a generator or a filter, then + Rep(E) = {bc,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}. + For Rep(W), see below. + If E is begin B end, where B is a body, then + Rep(E) = {block,LINE,Rep(B)}. + If E is if Ic_1 ; ... ; Ic_k end, + where each Ic_i is an if clause then Rep(E) = + {'if',LINE,[Rep(Ic_1), ..., Rep(Ic_k)]}. + If E is case E_0 of Cc_1 ; ... ; Cc_k end, + where E_0 is an expression and each Cc_i is a + case clause then Rep(E) = + {'case',LINE,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}. + If E is try B catch Tc_1 ; ... ; Tc_k end, + where B is a body and each Tc_i is a catch clause then Rep(E) = - . - If E is , - where is a body, - each is a case clause and - each is a catch clause then + {'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],[]}. + If E is try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end, + where B is a body, + each Cc_i is a case clause and + each Tc_j is a catch clause then Rep(E) = + {'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],[]}. + If E is try B after A end, + where B and A are bodies then Rep(E) = + {'try',LINE,Rep(B),[],[],Rep(A)}. + If E is try B of Cc_1 ; ... ; Cc_k after A end, + where B and A are a bodies and + each Cc_i is a case clause then Rep(E) = + {'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[],Rep(A)}. + If E is try B catch Tc_1 ; ... ; Tc_k after A end, + where B and A are bodies and + each Tc_i is a catch clause then Rep(E) = + {'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],Rep(A)}. + If E is try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A end, + where B and A are a bodies, + each Cc_i is a case clause and + each Tc_j is a catch clause then Rep(E) = - . - If E is , - where and are bodies then - Rep(E) = - . - If E is , - where and are a bodies and - each is a case clause then - Rep(E) = - . - If E is , - where and are bodies and - each is a catch clause then - Rep(E) = - . - If E is , - where and are a bodies, - each is a case clause and - each is a catch clause then - Rep(E) = - . - If E is , - where each is a case clause then - Rep(E) = - . - If E is B_t end]]>, - where each is a case clause, - is an expression and is a body, then - Rep(E) = - . - If E is , then - Rep(E) = . - If E is , then - Rep(E) = . - (Before the R15 release: Rep(E) = .) - If E is - where each is a function clause then Rep(E) = - . - If E is - where is a variable and each - is a function clause then Rep(E) = - . + {'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],Rep(A)}. + If E is receive Cc_1 ; ... ; Cc_k end, + where each Cc_i is a case clause then Rep(E) = + {'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)]}. + If E is receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end, + where each Cc_i is a case clause, + E_0 is an expression and B_t is a body, then Rep(E) = + {'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)],Rep(E_0),Rep(B_t)}. + If E is fun Name / Arity, then + Rep(E) = {'fun',LINE,{function,Name,Arity}}. + If E is fun Module:Name/Arity, then Rep(E) = + {'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}. + (Before the R15 release: Rep(E) = + {'fun',LINE,{function,Module,Name,Arity}}.) + If E is fun Fc_1 ; ... ; Fc_k end + where each Fc_i is a function clause then Rep(E) = + {'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}. + If E is fun Name Fc_1 ; ... ; Name Fc_k end + where Name is a variable and each + Fc_i is a function clause then Rep(E) = + {named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}. - If E is , then + If E is ( E_0 ), then Rep(E) = Rep(E_0), that is, parenthesized expressions cannot be distinguished from their bodies.
Generators and Filters -

When W is a generator or a filter (in the body of a list or binary comprehension), then:

+

When W is a generator or a filter (in the body of a list or + binary comprehension), then:

- If W is a generator , where is a pattern and - is an expression, then - Rep(W) = . - If W is a generator , where is a pattern and - is an expression, then - Rep(W) = . - If W is a filter , which is an expression, then - Rep(W) = . + If W is a generator P <- E, where P is + a pattern and E is an expression, then + Rep(W) = {generate,LINE,Rep(P),Rep(E)}. + If W is a generator P <= E, where P is + a pattern and E is an expression, then + Rep(W) = {b_generate,LINE,Rep(P),Rep(E)}. + If W is a filter E, which is an expression, then + Rep(W) = Rep(E).
Binary Element Type Specifiers

A type specifier list TSL for a binary element is a sequence of type - specifiers . - Rep(TSL) = .

+ specifiers TS_1 - ... - TS_k. + Rep(TSL) = [Rep(TS_1), ..., Rep(TS_k)].

When TS is a type specifier for a binary element, then:

- If TS is an atom , then Rep(TS) = . - If TS is a couple where is an atom and - is an integer, then Rep(TS) = {A,Value}. + If TS is an atom A, then Rep(TS) = A. + If TS is a couple A:Value where A is an atom + and Value is an integer, then Rep(TS) = + {A,Value}.
@@ -386,13 +380,13 @@ Map Assoc and Exact Fields

When W is an assoc or exact field (in the body of a map), then:

- If W is an assoc field V]]>, where - and are both expressions, - then Rep(W) = . + If W is an assoc field K => V, where + K and V are both expressions, + then Rep(W) = {map_field_assoc,LINE,Rep(K),Rep(V)}. - If W is an exact field , where - and are both expressions, - then Rep(W) = . + If W is an exact field K := V, where + K and V are both expressions, + then Rep(W) = {map_field_exact,LINE,Rep(K),Rep(V)}.
@@ -400,92 +394,95 @@
Clauses -

There are function clauses, if clauses, case clauses +

There are function clauses, if clauses, case clauses and catch clauses.

-

A clause is one of the following alternatives:

+

A clause C is one of the following alternatives:

- If C is a function clause B]]> - where is a pattern sequence and is a body, then - Rep(C) = . - If C is a function clause B]]> - where is a pattern sequence, - is a guard sequence and is a body, then - Rep(C) = . - If C is an if clause B]]> - where is a guard sequence and is a body, then - Rep(C) = . - If C is a case clause B]]> - where is a pattern and is a body, then - Rep(C) = . - If C is a case clause B]]> - where is a pattern, - is a guard sequence and is a body, then - Rep(C) = . - If C is a catch clause B]]> - where is a pattern and is a body, then - Rep(C) = . - If C is a catch clause B]]> - where is an atomic literal or a variable pattern, - is a pattern and is a body, then - Rep(C) = . - If C is a catch clause B]]> - where is a pattern, is a guard sequence - and is a body, then - Rep(C) = . - If C is a catch clause B]]> - where is an atomic literal or a variable pattern, - is a pattern, is a guard sequence - and is a body, then - Rep(C) = . + If C is a function clause ( Ps ) -> B + where Ps is a pattern sequence and B is a body, then + Rep(C) = {clause,LINE,Rep(Ps),[],Rep(B)}. + If C is a function clause ( Ps ) when Gs -> B + where Ps is a pattern sequence, + Gs is a guard sequence and B is a body, then + Rep(C) = {clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}. + If C is an if clause Gs -> B + where Gs is a guard sequence and B is a body, then + Rep(C) = {clause,LINE,[],Rep(Gs),Rep(B)}. + If C is a case clause P -> B + where P is a pattern and B is a body, then + Rep(C) = {clause,LINE,[Rep(P)],[],Rep(B)}. + If C is a case clause P when Gs -> B + where P is a pattern, + Gs is a guard sequence and B is a body, then + Rep(C) = {clause,LINE,[Rep(P)],Rep(Gs),Rep(B)}. + If C is a catch clause P -> B + where P is a pattern and B is a body, then + Rep(C) = {clause,LINE,[Rep({throw,P,_})],[],Rep(B)}. + If C is a catch clause X : P -> B + where X is an atomic literal or a variable pattern, + P is a pattern and B is a body, then + Rep(C) = {clause,LINE,[Rep({X,P,_})],[],Rep(B)}. + If C is a catch clause P when Gs -> B + where P is a pattern, Gs is a guard sequence + and B is a body, then + Rep(C) = {clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}. + If C is a catch clause X : P when Gs -> B + where X is an atomic literal or a variable pattern, + P is a pattern, Gs is a guard sequence + and B is a body, then + Rep(C) = {clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}.
Guards -

A guard sequence Gs is a sequence of guards , and - Rep(Gs) = . If the guard sequence is - empty, Rep(Gs) = .

-

A guard G is a nonempty sequence of guard tests , and - Rep(G) = .

-

A guard test is one of the following alternatives:

+

A guard sequence Gs is a sequence of guards G_1; ...; G_k, and + Rep(Gs) = [Rep(G_1), ..., Rep(G_k)]. If the guard sequence is + empty, Rep(Gs) = [].

+

A guard G is a nonempty sequence of guard tests + Gt_1, ..., Gt_k, and Rep(G) = + [Rep(Gt_1), ..., Rep(Gt_k)].

+

A guard test Gt is one of the following alternatives:

If Gt is an atomic literal L, then Rep(Gt) = Rep(L). - If Gt is a variable pattern , then - Rep(Gt) = , - where A is an atom with a printname consisting of the same characters as - . - If Gt is a tuple skeleton , then - Rep(Gt) = . - If Gt is , then - Rep(Gt) = . - If Gt is a cons skeleton , then - Rep(Gt) = . - If Gt is a binary constructor >]]>, then - Rep(Gt) = . + If Gt is a variable pattern V, then + Rep(Gt) = {var,LINE,A}, where A is an atom with + a printname consisting of the same characters as V. + If Gt is a tuple skeleton {Gt_1, ..., Gt_k}, then + Rep(Gt) = {tuple,LINE,[Rep(Gt_1), ..., Rep(Gt_k)]}. + If Gt is [], then Rep(Gt) = {nil,LINE}. + If Gt is a cons skeleton [Gt_h | Gt_t], then + Rep(Gt) = {cons,LINE,Rep(Gt_h),Rep(Gt_t)}. + If Gt is a binary constructor + <<Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>>, then + Rep(Gt) = {bin,LINE,[{bin_element,LINE,Rep(Gt_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(Gt_k),Rep(Size_k),Rep(TSL_k)}]}. For Rep(TSL), see above. - An omitted is represented by . An omitted - (type specifier list) is represented by . - If Gt is , where - is a binary operator, then Rep(Gt) = . - If Gt is , where is a unary operator, then - Rep(Gt) = . - If Gt is , then + An omitted Size is represented by default. + An omitted TSL (type specifier list) is represented + by default. + If Gt is Gt_1 Op Gt_2, where Op + is a binary operator, then Rep(Gt) = + {op,LINE,Op,Rep(Gt_1),Rep(Gt_2)}. + If Gt is Op Gt_0, where Op is a unary operator, then + Rep(Gt) = {op,LINE,Op,Rep(Gt_0)}. + If Gt is #Name{Field_1=Gt_1, ..., Field_k=Gt_k}, then Rep(E) = - . - If Gt is , then - Rep(Gt) = . - If Gt is , then - Rep(Gt) = . - If Gt is , where is an atom, then - Rep(Gt) = . - If Gt is , where is - the atom and is an atom or an operator, then - Rep(Gt) = . - If Gt is , where is - the atom and is an atom or an operator, then - Rep(Gt) = . - If Gt is , then - Rep(Gt) = , that is, parenthesized + {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(Gt_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(Gt_k)}]}. + If Gt is #Name.Field, then + Rep(Gt) = {record_index,LINE,Name,Rep(Field)}. + If Gt is Gt_0#Name.Field, then + Rep(Gt) = {record_field,LINE,Rep(Gt_0),Name,Rep(Field)}. + If Gt is A(Gt_1, ..., Gt_k), where A is an atom, then + Rep(Gt) = {call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}. + If Gt is A_m:A(Gt_1, ..., Gt_k), where A_m is + the atom erlang and A is an atom or an operator, then + Rep(Gt) = {call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ..., Rep(Gt_k)]}. + If Gt is {A_m,A}(Gt_1, ..., Gt_k), where A_m is + the atom erlang and A is an atom or an operator, then + Rep(Gt) = {call,LINE,Rep({A_m,A}),[Rep(Gt_1), ..., Rep(Gt_k)]}. + + If Gt is ( Gt_0 ), then + Rep(Gt) = Rep(Gt_0), that is, parenthesized guard tests cannot be distinguished from their bodies.

Note that every guard test has the same source form as some expression, @@ -599,18 +596,18 @@

The Abstract Format After Preprocessing -

The compilation option can be given to the - compiler to have the abstract code stored in - the chunk in the BEAM file +

The compilation option debug_info can be given to the + compiler to have the abstract code stored in + the abstract_code chunk in the BEAM file (for debugging purposes).

-

In OTP R9C and later, the chunk will +

In OTP R9C and later, the abstract_code chunk will contain

-

-

where is the abstract code as described +

{raw_abstract_v1,AbstractCode}

+

where AbstractCode is the abstract code as described in this document.

In releases of OTP prior to R9C, the abstract code after some more processing was stored in the BEAM file. The first element of the - tuple would be either (R7B) or + tuple would be either abstract_v1 (R7B) or abstract_v2 (R8B).

-- cgit v1.2.3 From f67a7375e19734c3f7d6947b0dcf608d0fe1c8fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Dec 2015 18:02:54 +0100 Subject: erts: Use internal hash for process dictionaries --- erts/emulator/beam/erl_process_dict.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 8606371bdf..81469e8716 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -53,11 +53,11 @@ /* Hash utility macros */ #define HASH_RANGE(PDict) ((PDict)->homeSize + (PDict)->splitPosition) -#define MAKE_HASH(Term) \ -((is_small(Term)) ? unsigned_val(Term) : \ - ((is_atom(Term)) ? \ - (atom_tab(atom_val(term))->slot.bucket.hvalue) : \ - make_hash2(Term))) +#define MAKE_HASH(Term) \ + ((is_small(Term)) ? unsigned_val(Term) : \ + ((is_atom(Term)) ? \ + (atom_tab(atom_val(Term))->slot.bucket.hvalue) : \ + make_internal_hash(Term))) #define PD_SZ2BYTES(Sz) (sizeof(ProcDict) + ((Sz) - 1)*sizeof(Eterm)) -- cgit v1.2.3 From c97f3332aeddf039ee2207196229b9ff07047c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Dec 2015 13:17:28 +0100 Subject: erts: Add i_get_hash instruction Calculate hashvalue in load-time for constant process dictionary gets. --- erts/emulator/beam/beam_emu.c | 10 +++++++ erts/emulator/beam/beam_load.c | 47 +++++++++++++++++++++++++++++ erts/emulator/beam/erl_process_dict.c | 56 +++++++++++++++++++++++------------ erts/emulator/beam/erl_process_dict.h | 1 + erts/emulator/beam/ops.tab | 3 +- 5 files changed, 97 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 38def5d89f..d39cd9d8ea 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3735,6 +3735,16 @@ do { \ StoreBifResult(1, result); } + OpCase(i_get_hash_cId): + { + Eterm arg; + Eterm result; + + GetArg1(0, arg); + result = erts_pd_hash_get_with_hx(c_p, Arg(1), arg); + StoreBifResult(2, result); + } + { Eterm case_end_val; diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index b70e5b9a2d..636672217b 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4258,6 +4258,53 @@ gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src, return op; } +static int +hash_internal_genop_arg(LoaderState* stp, GenOpArg Key, Uint32* hx) +{ + switch (Key.type) { + case TAG_a: + *hx = atom_tab(atom_val(Key.val))->slot.bucket.hvalue; + return 1; + case TAG_i: + *hx = Key.val; + return 1; + case TAG_n: + *hx = make_internal_hash(NIL); + return 1; + case TAG_q: + *hx = make_internal_hash(stp->literals[Key.val].term); + return 1; + default: + return 0; + } +} + + +static GenOp* +gen_get(LoaderState* stp, GenOpArg Src, GenOpArg Dst) +{ + GenOp* op; + Uint32 hx = 0; + + NEW_GENOP(stp, op); + op->next = NULL; + if (hash_internal_genop_arg(stp, Src, &hx)) { + op->arity = 3; + op->op = genop_i_get_hash_3; + op->a[0] = Src; + op->a[1].type = TAG_u; + op->a[1].val = (BeamInstr) hx; + op->a[2] = Dst; + } else { + op->arity = 2; + op->op = genop_i_get_2; + op->a[0] = Src; + op->a[1] = Dst; + } + return op; +} + + static GenOp* gen_get_map_elements(LoaderState* stp, GenOpArg Fail, GenOpArg Src, GenOpArg Size, GenOpArg* Rest) diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 81469e8716..e497267b63 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -61,6 +61,9 @@ #define PD_SZ2BYTES(Sz) (sizeof(ProcDict) + ((Sz) - 1)*sizeof(Eterm)) +#define pd_hash_value(Pdict, Key) \ + pd_hash_value_to_ix(Pdict, MAKE_HASH((Key))) + /* Memory allocation macros */ #define PD_ALLOC(Sz) \ erts_alloc(ERTS_ALC_T_PROC_DICT, (Sz)) @@ -82,6 +85,7 @@ */ static void pd_hash_erase(Process *p, Eterm id, Eterm *ret); static void pd_hash_erase_all(Process *p); +static Eterm pd_hash_get_with_hval(Process *p, Eterm bucket, Eterm id); static Eterm pd_hash_get_keys(Process *p, Eterm value); static Eterm pd_hash_get_all_keys(Process *p, ProcDict *pd); static Eterm pd_hash_get_all(Process *p, ProcDict *pd); @@ -93,7 +97,7 @@ static void grow(Process *p); static void array_shrink(ProcDict **ppd, unsigned int need); static Eterm array_put(ProcDict **ppdict, unsigned int ndx, Eterm term); -static unsigned int pd_hash_value(ProcDict *pdict, Eterm term); +static unsigned int pd_hash_value_to_ix(ProcDict *pdict, Uint32 hx); static unsigned int next_array_size(unsigned int need); /* @@ -390,40 +394,55 @@ static void pd_hash_erase_all(Process *p) } } +Eterm erts_pd_hash_get_with_hx(Process *p, Uint32 hx, Eterm id) +{ + unsigned int hval; + ProcDict *pd = p->dictionary; + + if (pd == NULL) + return am_undefined; + hval = pd_hash_value_to_ix(pd, hx); + return pd_hash_get_with_hval(p, ARRAY_GET(pd, hval), id); +} + Eterm erts_pd_hash_get(Process *p, Eterm id) { unsigned int hval; - Eterm tmp; ProcDict *pd = p->dictionary; if (pd == NULL) return am_undefined; hval = pd_hash_value(pd, id); - tmp = ARRAY_GET(pd, hval); - if (is_boxed(tmp)) { /* Tuple */ - ASSERT(is_tuple(tmp)); - if (EQ(tuple_val(tmp)[1], id)) { - return tuple_val(tmp)[2]; + return pd_hash_get_with_hval(p, ARRAY_GET(pd, hval), id); +} + +Eterm pd_hash_get_with_hval(Process *p, Eterm bucket, Eterm id) +{ + if (is_boxed(bucket)) { /* Tuple */ + ASSERT(is_tuple(bucket)); + if (EQ(tuple_val(bucket)[1], id)) { + return tuple_val(bucket)[2]; } - } else if (is_list(tmp)) { - for (; tmp != NIL && !EQ(tuple_val(TCAR(tmp))[1], id); tmp = TCDR(tmp)) { + } else if (is_list(bucket)) { + for (; bucket != NIL && !EQ(tuple_val(TCAR(bucket))[1], id); bucket = TCDR(bucket)) { ; } - if (tmp != NIL) { - return tuple_val(TCAR(tmp))[2]; + if (bucket != NIL) { + return tuple_val(TCAR(bucket))[2]; } - } else if (is_not_nil(tmp)) { + } else if (is_not_nil(bucket)) { #ifdef DEBUG erts_fprintf(stderr, "Process dictionary for process %T is broken, trying to " "display term found in line %d:\n" - "%T\n", p->common.id, __LINE__, tmp); + "%T\n", p->common.id, __LINE__, bucket); #endif erl_exit(1, "Damaged process dictionary found during get/1."); } return am_undefined; } + #define PD_GET_TKEY(Dst,Src) \ do { \ ASSERT(is_tuple((Src))); \ @@ -932,17 +951,16 @@ static Eterm array_put(ProcDict **ppdict, unsigned int ndx, Eterm term) ** Basic utilities */ -static unsigned int pd_hash_value(ProcDict *pdict, Eterm term) +static unsigned int pd_hash_value_to_ix(ProcDict *pdict, Uint32 hx) { - Uint hash, high; - - hash = MAKE_HASH(term); - high = hash % (pdict->homeSize*2); + Uint high; + high = hx % (pdict->homeSize*2); if (high >= HASH_RANGE(pdict)) - return hash % pdict->homeSize; + return hx % pdict->homeSize; return high; } + static unsigned int next_array_size(unsigned int need) { static unsigned int tab[] = diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h index cc53800eb5..9aa21b7c38 100644 --- a/erts/emulator/beam/erl_process_dict.h +++ b/erts/emulator/beam/erl_process_dict.h @@ -39,5 +39,6 @@ void erts_deep_dictionary_dump(int to, void *to_arg, Eterm erts_dictionary_copy(struct process *p, ProcDict *pd); Eterm erts_pd_hash_get(struct process *p, Eterm id); +Eterm erts_pd_hash_get_with_hx(Process *p, Uint32 hx, Eterm id); #endif diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 1d32e72247..86ae189c27 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1118,7 +1118,7 @@ call_bif e bif0 u$bif:erlang:self/0 Dst=d => self Dst bif0 u$bif:erlang:node/0 Dst=d => node Dst -bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => i_get Src Dst +bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => gen_get(Src, Dst) bif2 Jump=j u$bif:erlang:element/2 S1=s S2=rxy Dst=d => gen_element(Jump, S1, S2, Dst) @@ -1130,6 +1130,7 @@ bif1_body Bif Literal=q Dst => move Literal x | bif1_body Bif x Dst bif2 p Bif S1 S2 Dst => i_fetch S1 S2 | i_bif2_body Bif Dst bif2 Fail Bif S1 S2 Dst => i_fetch S1 S2 | i_bif2 Fail Bif Dst +i_get_hash c I d i_get s d %macro: self Self -- cgit v1.2.3 From 75ef9b7cae7533fb9be7953ac72f743b055d2a7a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 3 Dec 2015 15:32:38 +0100 Subject: erts: Add test for remote exit signal with fat map --- erts/emulator/test/map_SUITE.erl | 61 ++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 6890c42b7a..a256cf4195 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -2999,21 +2999,38 @@ id(I) -> I. t_gc_rare_map_overflow(Config) -> Pa = filename:dirname(code:which(?MODULE)), {ok, Node} = test_server:start_node(gc_rare_map_overflow, slave, [{args, "-pa \""++Pa++"\""}]), - Echo = spawn_link(Node, fun Loop() -> receive {From,Msg} -> From ! Msg - end, - Loop() - end), - FatMap = fatmap(34), - false = (flatmap =:= erts_internal:map_type(FatMap)), - - t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> erlang:garbage_collect() end), - - % Repeat test for minor gc: - minor_collect(), % need this to make the next gc really be a minor - t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> true = minor_collect() end), - - unlink(Echo), - test_server:stop_node(Node). + erts_debug:set_internal_state(available_internal_state, true), + try + Echo = spawn_link(Node, fun Loop() -> receive {From,Msg} -> From ! Msg + end, + Loop() + end), + FatMap = fatmap(34), + false = (flatmap =:= erts_internal:map_type(FatMap)), + + t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> erlang:garbage_collect() end), + + %% Repeat test for minor gc: + t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> minor_collect() end), + + unlink(Echo), + + %% Test fatmap in exit signal + Exiter = spawn_link(Node, fun Loop() -> receive {From,Msg} -> + "not_a_map" = Msg % badmatch! + end, + Loop() + end), + process_flag(trap_exit, true), + Exiter ! {self(), FatMap}, + {'EXIT', Exiter, {{badmatch,FatMap}, _}} = receive M -> M end, + ok + + after + process_flag(trap_exit, false), + erts_debug:set_internal_state(available_internal_state, false), + test_server:stop_node(Node) + end. t_gc_rare_map_overflow_do(Echo, FatMap, GcFun) -> Master = self(), @@ -3033,15 +3050,11 @@ t_gc_rare_map_overflow_do(Echo, FatMap, GcFun) -> ok. minor_collect() -> - minor_collect(minor_gcs()). - -minor_collect(Before) -> + Before = minor_gcs(), + erts_debug:set_internal_state(force_gc, self()), + erlang:yield(), After = minor_gcs(), - case After of - _ when After > Before -> true; - _ when After =:= Before -> minor_collect(Before); - 0 -> false - end. + io:format("minor_gcs: ~p -> ~p\n", [Before, After]). minor_gcs() -> {garbage_collection, Info} = process_info(self(), garbage_collection), @@ -3051,7 +3064,7 @@ minor_gcs() -> %% Generate a map with N (or N+1) keys that has an abnormal heap demand. %% Done by finding keys that collide in the first 32-bit hash. fatmap(N) -> - erts_debug:set_internal_state(available_internal_state, true), + %%erts_debug:set_internal_state(available_internal_state, true), Table = ets:new(void, [bag, private]), Seed0 = rand:seed_s(exsplus, {4711, 3141592, 2718281}), -- cgit v1.2.3 From ce8279d6a48d41f9de577825844f499bb3084b96 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 3 Dec 2015 15:34:14 +0100 Subject: erts: Fix bug for remote control message containing fat maps that could cause the static factory to overflow Fix: Introduce a new factory mode FACTORY_TMP --- erts/emulator/beam/dist.c | 10 +++------- erts/emulator/beam/erl_message.c | 29 +++++++++++++++++++++++++++-- erts/emulator/beam/erl_message.h | 4 +++- 3 files changed, 33 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 4846133aa6..170690ca89 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -1153,7 +1153,6 @@ int erts_net_message(Port *prt, Process* rp; DeclareTmpHeapNoproc(ctl_default,DIST_CTL_DEFAULT_SIZE); Eterm* ctl = ctl_default; - ErlOffHeap off_heap; ErtsHeapFactory factory; Eterm* hp; Sint type; @@ -1168,9 +1167,6 @@ int erts_net_message(Port *prt, #endif UseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE); - /* Thanks to Luke Gorrie */ - off_heap.first = NULL; - off_heap.overhead = 0; ERTS_SMP_CHK_NO_PROC_LOCKS; @@ -1231,7 +1227,7 @@ int erts_net_message(Port *prt, } hp = ctl; - erts_factory_static_init(&factory, ctl, ctl_len, &off_heap); + erts_factory_tmp_init(&factory, ctl, ctl_len, ERTS_ALC_T_DCTRL_BUF); arg = erts_decode_dist_ext(&factory, &ede); if (is_non_value(arg)) { #ifdef ERTS_DIST_MSG_DBG @@ -1719,7 +1715,7 @@ int erts_net_message(Port *prt, goto invalid_message; } - erts_cleanup_offheap(&off_heap); + erts_factory_close(&factory); if (ctl != ctl_default) { erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl); } @@ -1734,7 +1730,7 @@ int erts_net_message(Port *prt, } data_error: PURIFY_MSG("data error"); - erts_cleanup_offheap(&off_heap); + erts_factory_close(&factory); if (ctl != ctl_default) { erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl); } diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 2a703fb102..fa6b2fc613 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1174,6 +1174,9 @@ void erts_factory_message_init(ErtsHeapFactory* factory, ASSERT(factory->hp >= factory->hp_start && factory->hp <= factory->hp_end); } +/* One static sized heap that must suffice. + No extra heap fragments will be allocated. +*/ void erts_factory_static_init(ErtsHeapFactory* factory, Eterm* hp, Uint size, @@ -1188,6 +1191,23 @@ void erts_factory_static_init(ErtsHeapFactory* factory, factory->off_heap_saved.overhead = factory->off_heap->overhead; } +/* A temporary heap with default buffer allocated/freed by client. + * factory_close is same as factory_undo + */ +void erts_factory_tmp_init(ErtsHeapFactory* factory, Eterm* hp, Uint size, + Uint32 atype) +{ + factory->mode = FACTORY_TMP; + factory->hp_start = hp; + factory->hp = hp; + factory->hp_end = hp + size; + factory->heap_frags = NULL; + factory->off_heap_saved.first = NULL; + factory->off_heap_saved.overhead = 0; + factory->off_heap = &factory->off_heap_saved; + factory->alloc_type = atype; +} + /* When we know the term is an immediate and need no heap. */ void erts_factory_dummy_init(ErtsHeapFactory* factory) @@ -1231,6 +1251,7 @@ static void reserve_heap(ErtsHeapFactory* factory, Uint need, Uint xtra) return; case FACTORY_HEAP_FRAGS: + case FACTORY_TMP: bp = factory->heap_frags; if (bp) { @@ -1280,6 +1301,9 @@ void erts_factory_close(ErtsHeapFactory* factory) bp->used_size = factory->hp - bp->mem; } break; + case FACTORY_TMP: + erts_factory_undo(factory); + break; case FACTORY_STATIC: break; case FACTORY_CLOSED: break; default: @@ -1371,19 +1395,20 @@ void erts_factory_undo(ErtsHeapFactory* factory) } break; + case FACTORY_TMP: case FACTORY_HEAP_FRAGS: erts_cleanup_offheap(factory->off_heap); factory->off_heap->first = NULL; bp = factory->heap_frags; - do { + while (bp != NULL) { ErlHeapFragment* next_bp = bp->next; ASSERT(bp->off_heap.first == NULL); ERTS_HEAP_FREE(factory->alloc_type, (void *) bp, ERTS_HEAP_FRAG_SIZE(bp->alloc_size)); bp = next_bp; - }while (bp != NULL); + } break; case FACTORY_CLOSED: break; diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index fbdf3fb0e2..92ba3e571c 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -58,7 +58,8 @@ typedef struct { FACTORY_CLOSED = 0, FACTORY_HALLOC, FACTORY_HEAP_FRAGS, - FACTORY_STATIC + FACTORY_STATIC, + FACTORY_TMP } mode; Process* p; Eterm* hp_start; @@ -75,6 +76,7 @@ void erts_factory_proc_init(ErtsHeapFactory*, Process*); void erts_factory_proc_prealloc_init(ErtsHeapFactory*, Process*, Sint size); void erts_factory_message_init(ErtsHeapFactory*, Process*, Eterm* hp, struct erl_heap_fragment*); void erts_factory_static_init(ErtsHeapFactory*, Eterm* hp, Uint size, ErlOffHeap*); +void erts_factory_tmp_init(ErtsHeapFactory*, Eterm* hp, Uint size, Uint32 atype); void erts_factory_dummy_init(ErtsHeapFactory*); Eterm* erts_produce_heap(ErtsHeapFactory*, Uint need, Uint xtra); -- cgit v1.2.3 From a2b28094081f1b185a31b33e3c1bcb377d6761bb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 3 Dec 2015 18:50:20 +0100 Subject: erts: Tweak hashmap heap size estimation 1. Change order between mul and div to not lose too much in integer divisions. 2. Fix estimation in DEBUG to really be an *under* estimation. --- erts/emulator/beam/erl_map.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index c391de3f11..4d9d74bc37 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -195,14 +195,17 @@ typedef struct hashmap_head_s { [one cons cell + one list term in parent node] per key [one header + one boxed term in parent node] per inner node [one header + one size word] for root node + Observed average number of nodes per key is about 0.35. */ -#define HASHMAP_HEAP_SIZE(KEYS,NODES) ((KEYS)*3 + (NODES)*2) +#define HASHMAP_WORDS_PER_KEY 3 +#define HASHMAP_WORDS_PER_NODE 2 #ifdef DEBUG -# define HASHMAP_ESTIMATED_NODE_COUNT(KEYS) (KEYS) +# define HASHMAP_ESTIMATED_TOT_NODE_SIZE(KEYS) \ + (HASHMAP_WORDS_PER_NODE * (KEYS) * 3/10) /* slightly under estimated */ #else -# define HASHMAP_ESTIMATED_NODE_COUNT(KEYS) (2*(KEYS)/5) +# define HASHMAP_ESTIMATED_TOT_NODE_SIZE(KEYS) \ + (HASHMAP_WORDS_PER_NODE * (KEYS) * 4/10) /* slightly over estimated */ #endif #define HASHMAP_ESTIMATED_HEAP_SIZE(KEYS) \ - HASHMAP_HEAP_SIZE(KEYS,HASHMAP_ESTIMATED_NODE_COUNT(KEYS)) - + ((KEYS)*HASHMAP_WORDS_PER_KEY + HASHMAP_ESTIMATED_TOT_NODE_SIZE(KEYS)) #endif -- cgit v1.2.3 From 2ae91c3ade0538500ff4dbda29ad539e595f64df Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 15 Oct 2015 20:10:46 +0200 Subject: erts: Change erts_internal:map_type/1 into term_type/1 to support other terms, not just maps --- erts/emulator/beam/atom.h | 1 + erts/emulator/beam/bif.tab | 2 +- erts/emulator/beam/erl_map.c | 25 +++++++++++++++++-------- erts/emulator/test/map_SUITE.erl | 4 ++-- erts/preloaded/ebin/erts_internal.beam | Bin 5964 -> 5988 bytes erts/preloaded/src/erts_internal.erl | 12 ++++++------ 6 files changed, 27 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.h b/erts/emulator/beam/atom.h index ead56c83d8..2c002ca92f 100644 --- a/erts/emulator/beam/atom.h +++ b/erts/emulator/beam/atom.h @@ -129,6 +129,7 @@ typedef enum { (erts_is_atom_utf8_bytes((byte *) LSTR, sizeof(LSTR) - 1, (TERM))) #define ERTS_DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) #define ERTS_INIT_AM(S) AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) +#define ERTS_MAKE_AM(Str) am_atom_put(Str, sizeof(Str) - 1) int atom_table_size(void); /* number of elements */ int atom_table_sz(void); /* table size in bytes, excluding stored objects */ diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index c49a3ff313..07d4702b92 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -167,7 +167,7 @@ bif erts_internal:request_system_task/3 bif erts_internal:check_process_code/2 bif erts_internal:map_to_tuple_keys/1 -bif erts_internal:map_type/1 +bif erts_internal:term_type/1 bif erts_internal:map_hashmap_children/1 bif erts_internal:time_unit/0 diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 29b3024644..ac10b9a3e3 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -2698,29 +2698,38 @@ BIF_RETTYPE erts_internal_map_to_tuple_keys_1(BIF_ALIST_1) { } /* - * erts_internal:map_type/1 + * erts_internal:term_type/1 * * Used in erts_debug:size/1 */ -BIF_RETTYPE erts_internal_map_type_1(BIF_ALIST_1) { - DECL_AM(hashmap); - DECL_AM(hashmap_node); - DECL_AM(flatmap); +BIF_RETTYPE erts_internal_term_type_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Eterm hdr = *(boxed_val(BIF_ARG_1)); ASSERT(is_header(hdr)); switch (hdr & _HEADER_MAP_SUBTAG_MASK) { case HAMT_SUBTAG_HEAD_FLATMAP: - BIF_RET(AM_flatmap); + BIF_RET(ERTS_MAKE_AM("flatmap")); case HAMT_SUBTAG_HEAD_ARRAY: case HAMT_SUBTAG_HEAD_BITMAP: - BIF_RET(AM_hashmap); + BIF_RET(ERTS_MAKE_AM("hashmap")); case HAMT_SUBTAG_NODE_BITMAP: - BIF_RET(AM_hashmap_node); + BIF_RET(ERTS_MAKE_AM("hashmap_node")); default: erl_exit(1, "bad header"); } + } else if (is_immed(BIF_ARG_1)) { + if (is_small(BIF_ARG_1)) { + BIF_RET(ERTS_MAKE_AM("small")); + } else if (is_ifloat(BIF_ARG_1)) { + BIF_RET(ERTS_MAKE_AM("ifloat")); + } + } else if (is_boxed(BIF_ARG_1)) { + if (is_big(BIF_ARG_1)) { + BIF_RET(ERTS_MAKE_AM("big")); + } else if (is_hfloat(BIF_ARG_1)) { + BIF_RET(ERTS_MAKE_AM("hfloat")); + } } BIF_P->fvalue = BIF_ARG_1; BIF_ERROR(BIF_P, BADMAP); diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 6890c42b7a..bde0d408f3 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -2598,7 +2598,7 @@ hashmap_balance(KeyFun) -> F = fun(I, {M0,Max0}) -> Key = KeyFun(I), M1 = M0#{Key => Key}, - Max1 = case erts_internal:map_type(M1) of + Max1 = case erts_internal:term_type(M1) of hashmap -> Nodes = hashmap_nodes(M1), Avg = maps:size(M1) * 0.4, @@ -3004,7 +3004,7 @@ t_gc_rare_map_overflow(Config) -> Loop() end), FatMap = fatmap(34), - false = (flatmap =:= erts_internal:map_type(FatMap)), + false = (flatmap =:= erts_internal:term_type(FatMap)), t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> erlang:garbage_collect() end), diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index dc8c711e1a..d63f79c327 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 7ed4efea4b..023af1579f 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -31,7 +31,7 @@ -export([await_port_send_result/3]). -export([cmp_term/2]). --export([map_to_tuple_keys/1, map_type/1, map_hashmap_children/1]). +-export([map_to_tuple_keys/1, term_type/1, map_hashmap_children/1]). -export([port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). @@ -215,12 +215,12 @@ cmp_term(_A,_B) -> map_to_tuple_keys(_M) -> erlang:nif_error(undefined). -%% return the internal map type --spec map_type(M) -> Type when - M :: map(), - Type :: 'flatmap' | 'hashmap' | 'hashmap_node'. +%% return the internal term type +-spec term_type(M) -> Type when + M :: term(), + Type :: 'flatmap' | 'hashmap' | 'hashmap_node' | 'small' | 'big' | 'ifloat' | 'hfloat'. -map_type(_M) -> +term_type(_M) -> erlang:nif_error(undefined). %% return the internal hashmap sub-nodes from -- cgit v1.2.3 From f6eacb9981b40604a031c9d61967b0c3a3588bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 13 Nov 2015 17:41:36 +0100 Subject: erts: Let term_type/1 encompass all types --- erts/emulator/beam/erl_map.c | 101 +++++++++++++++++++++++++---------- erts/preloaded/src/erts_internal.erl | 16 ++++-- 2 files changed, 85 insertions(+), 32 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index ac10b9a3e3..d0ffb11e79 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -2704,35 +2704,82 @@ BIF_RETTYPE erts_internal_map_to_tuple_keys_1(BIF_ALIST_1) { */ BIF_RETTYPE erts_internal_term_type_1(BIF_ALIST_1) { - if (is_map(BIF_ARG_1)) { - Eterm hdr = *(boxed_val(BIF_ARG_1)); - ASSERT(is_header(hdr)); - switch (hdr & _HEADER_MAP_SUBTAG_MASK) { - case HAMT_SUBTAG_HEAD_FLATMAP: - BIF_RET(ERTS_MAKE_AM("flatmap")); - case HAMT_SUBTAG_HEAD_ARRAY: - case HAMT_SUBTAG_HEAD_BITMAP: - BIF_RET(ERTS_MAKE_AM("hashmap")); - case HAMT_SUBTAG_NODE_BITMAP: - BIF_RET(ERTS_MAKE_AM("hashmap_node")); - default: - erl_exit(1, "bad header"); - } - } else if (is_immed(BIF_ARG_1)) { - if (is_small(BIF_ARG_1)) { - BIF_RET(ERTS_MAKE_AM("small")); - } else if (is_ifloat(BIF_ARG_1)) { - BIF_RET(ERTS_MAKE_AM("ifloat")); - } - } else if (is_boxed(BIF_ARG_1)) { - if (is_big(BIF_ARG_1)) { - BIF_RET(ERTS_MAKE_AM("big")); - } else if (is_hfloat(BIF_ARG_1)) { - BIF_RET(ERTS_MAKE_AM("hfloat")); + Eterm obj = BIF_ARG_1; + switch (primary_tag(obj)) { + case TAG_PRIMARY_LIST: + BIF_RET(ERTS_MAKE_AM("list")); + case TAG_PRIMARY_BOXED: { + Eterm hdr = *boxed_val(obj); + ASSERT(is_header(hdr)); + switch (hdr & _TAG_HEADER_MASK) { + case ARITYVAL_SUBTAG: + BIF_RET(ERTS_MAKE_AM("tuple")); + case EXPORT_SUBTAG: + BIF_RET(ERTS_MAKE_AM("export")); + case FUN_SUBTAG: + BIF_RET(ERTS_MAKE_AM("fun")); + case MAP_SUBTAG: + switch (MAP_HEADER_TYPE(hdr)) { + case MAP_HEADER_TAG_FLATMAP_HEAD : + BIF_RET(ERTS_MAKE_AM("flatmap")); + case MAP_HEADER_TAG_HAMT_HEAD_BITMAP : + case MAP_HEADER_TAG_HAMT_HEAD_ARRAY : + BIF_RET(ERTS_MAKE_AM("hashmap")); + case MAP_HEADER_TAG_HAMT_NODE_BITMAP : + BIF_RET(ERTS_MAKE_AM("hashmap_node")); + default: + erl_exit(ERTS_ABORT_EXIT, "term_type: bad map header type %d\n", MAP_HEADER_TYPE(hdr)); + } + case REFC_BINARY_SUBTAG: + BIF_RET(ERTS_MAKE_AM("refc_binary")); + case HEAP_BINARY_SUBTAG: + BIF_RET(ERTS_MAKE_AM("heap_binary")); + case SUB_BINARY_SUBTAG: + BIF_RET(ERTS_MAKE_AM("sub_binary")); + case BIN_MATCHSTATE_SUBTAG: + BIF_RET(ERTS_MAKE_AM("matchstate")); + case POS_BIG_SUBTAG: + case NEG_BIG_SUBTAG: + BIF_RET(ERTS_MAKE_AM("bignum")); + case REF_SUBTAG: + BIF_RET(ERTS_MAKE_AM("reference")); + case EXTERNAL_REF_SUBTAG: + BIF_RET(ERTS_MAKE_AM("external_reference")); + case EXTERNAL_PID_SUBTAG: + BIF_RET(ERTS_MAKE_AM("external_pid")); + case EXTERNAL_PORT_SUBTAG: + BIF_RET(ERTS_MAKE_AM("external_port")); + case FLOAT_SUBTAG: + BIF_RET(ERTS_MAKE_AM("hfloat")); + default: + erl_exit(ERTS_ABORT_EXIT, "term_type: Invalid tag (0x%X)\n", hdr); + } } + case TAG_PRIMARY_IMMED1: + switch (obj & _TAG_IMMED1_MASK) { + case _TAG_IMMED1_SMALL: + BIF_RET(ERTS_MAKE_AM("fixnum")); + case _TAG_IMMED1_PID: + BIF_RET(ERTS_MAKE_AM("pid")); + case _TAG_IMMED1_PORT: + BIF_RET(ERTS_MAKE_AM("port")); + case _TAG_IMMED1_IMMED2: + switch (obj & _TAG_IMMED2_MASK) { + case _TAG_IMMED2_ATOM: + BIF_RET(ERTS_MAKE_AM("atom")); + case _TAG_IMMED2_CATCH: + BIF_RET(ERTS_MAKE_AM("catch")); + case _TAG_IMMED2_NIL: + BIF_RET(ERTS_MAKE_AM("nil")); + default: + erl_exit(ERTS_ABORT_EXIT, "term_type: Invalid tag (0x%X)\n", obj); + } + default: + erl_exit(ERTS_ABORT_EXIT, "term_type: Invalid tag (0x%X)\n", obj); + } + default: + erl_exit(ERTS_ABORT_EXIT, "term_type: Invalid tag (0x%X)\n", obj); } - BIF_P->fvalue = BIF_ARG_1; - BIF_ERROR(BIF_P, BADMAP); } /* diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 023af1579f..ce0a6a1d9e 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -216,11 +216,17 @@ map_to_tuple_keys(_M) -> erlang:nif_error(undefined). %% return the internal term type --spec term_type(M) -> Type when - M :: term(), - Type :: 'flatmap' | 'hashmap' | 'hashmap_node' | 'small' | 'big' | 'ifloat' | 'hfloat'. - -term_type(_M) -> +-spec term_type(T) -> Type when + T :: term(), + Type :: 'flatmap' | 'hashmap' | 'hashmap_node' + | 'fixnum' | 'bignum' | 'hfloat' + | 'list' | 'tuple' | 'export' | 'fun' + | 'refc_binary' | 'heap_binary' | 'sub_binary' + | 'reference' | 'external_reference' + | 'pid' | 'external_pid' | 'port' | 'external_port' + | 'atom' | 'catch' | 'nil'. + +term_type(_T) -> erlang:nif_error(undefined). %% return the internal hashmap sub-nodes from -- cgit v1.2.3 From 4f3212c9a84e6feaea1bbffdc1972e4760cc7815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 13 Nov 2015 19:14:10 +0100 Subject: Test erts_internal:term_type/1 --- erts/emulator/test/erts_debug_SUITE.erl | 45 +++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl index 35677f9953..bbba829501 100644 --- a/erts/emulator/test/erts_debug_SUITE.erl +++ b/erts/emulator/test/erts_debug_SUITE.erl @@ -24,13 +24,13 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, - test_size/1,flat_size_big/1,df/1, + test_size/1,flat_size_big/1,df/1,term_type/1, instructions/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [test_size, flat_size_big, df, instructions]. + [test_size, flat_size_big, df, instructions, term_type]. groups() -> []. @@ -138,6 +138,47 @@ flat_size_big_1(Term, Size0, Limit) when Size0 < Limit -> end; flat_size_big_1(_, _, _) -> ok. + +term_type(Config) when is_list(Config) -> + Ts = [{fixnum, 1}, + {fixnum, -1}, + {bignum, 1 bsl 300}, + {bignum, -(1 bsl 300)}, + {hfloat, 0.0}, + {hfloat, 0.0/-1}, + {hfloat, 1.0/(1 bsl 302)}, + {hfloat, 1.0*(1 bsl 302)}, + {hfloat, -1.0/(1 bsl 302)}, + {hfloat, -1.0*(1 bsl 302)}, + {hfloat, 3.1416}, + {hfloat, 1.0e18}, + {hfloat, -3.1416}, + {hfloat, -1.0e18}, + + {heap_binary, <<1,2,3>>}, + {refc_binary, <<0:(8*80)>>}, + {sub_binary, <<5:7>>}, + + {flatmap, #{ a => 1}}, + {hashmap, maps:from_list([{I,I}||I <- lists:seq(1,76)])}, + + {list, [1,2,3]}, + {nil, []}, + {tuple, {1,2,3}}, + {tuple, {}}, + + {export, fun lists:sort/1}, + {'fun', fun() -> ok end}, + {pid, self()}, + {atom, atom}], + lists:foreach(fun({E,Val}) -> + R = erts_internal:term_type(Val), + io:format("expecting term type ~w, got ~w (~p)~n", [E,R,Val]), + E = R + end, Ts), + ok. + + df(Config) when is_list(Config) -> P0 = pps(), PrivDir = ?config(priv_dir, Config), -- cgit v1.2.3 From 04a8110358706fe73a9dbedb81584d9f7fd27377 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 7 Dec 2015 20:02:19 +0100 Subject: erts: Add new test cases to float_SUITE --- erts/emulator/test/float_SUITE.erl | 124 +++++++++++++++++++++++++++ erts/emulator/test/float_SUITE_data/fp_drv.c | 1 + 2 files changed, 125 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index c1a76b8af4..8826026f88 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -26,9 +26,11 @@ 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, + t_mul_add_ops/1, bad_float_unpack/1, write/1, cmp_zero/1, cmp_integer/1, cmp_bignum/1]). -export([otp_7178/1]). -export([hidden_inf/1]). +-export([arith/1]). init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> @@ -45,6 +47,7 @@ all() -> [fpe, fp_drv, fp_drv_thread, otp_7178, denormalized, match, bad_float_unpack, write, {group, comparison} ,hidden_inf + ,arith, t_mul_add_ops ]. groups() -> @@ -328,3 +331,124 @@ hidden_inf_1(A, B, Zero, Huge) -> {'EXIT',{badarith,_}} = (catch (B * (Huge + Huge))), {'EXIT',{badarith,_}} = (catch (B / (-Huge - Huge))), {'EXIT',{badarith,_}} = (catch (B * (-Huge - Huge))). + +%% Improve code coverage in our different arithmetic functions +%% and make sure they yield consistent results. +arith(_Config) -> + _TAG_IMMED1_SIZE = 4, + + <> = <<0:1, 16#7fe:11, -1:52>>, + <> = <<0:1, 0:11, 1:52>>, + <> = <<1:1, 0:11, 0:52>>, + + WORD_BITS = erlang:system_info(wordsize) * 8, + SMALL_BITS = (WORD_BITS - _TAG_IMMED1_SIZE), + SMALL_MAX = (1 bsl (SMALL_BITS-1)) - 1, + SMALL_MIN = -(1 bsl (SMALL_BITS-1)), + BIG1_MAX = (1 bsl WORD_BITS) - 1, + BIG2_MAX = (1 bsl (WORD_BITS*2)) - 1, + + fixnum = erts_internal:term_type(SMALL_MAX), + fixnum = erts_internal:term_type(SMALL_MIN), + bignum = erts_internal:term_type(SMALL_MAX + 1), + bignum = erts_internal:term_type(SMALL_MIN - 1), + + L = [0, 0.0, FloatNegZero, 1, 1.0, 17, 17.0, 0.17, + FLOAT_MIN, FLOAT_MAX, + SMALL_MAX, SMALL_MAX+1, + SMALL_MIN, SMALL_MIN-1, + BIG1_MAX, BIG1_MAX+1, + BIG2_MAX, BIG2_MAX+1, + trunc(FLOAT_MAX), trunc(FLOAT_MAX)+1, trunc(FLOAT_MAX)*2, + + immed_badarg, + "list badarg", + {"boxed badarg"} + ], + + foreach_pair(fun(A,B) -> do_bin_ops(A,B) end, L). + +foreach_pair(F, L) -> + lists:foreach( + fun(A) -> lists:foreach(fun(B) -> F(A,B) end, L) end, + L). + +do_bin_ops(A, B) -> + Fun = fun(Op) -> + Op(A,B), + is_number(A) andalso Op(-A,B), + is_number(B) andalso Op(A,-B), + is_number(A) andalso is_number(B) andalso Op(-A,-B) + end, + lists:foreach(Fun, + [fun op_add/2, fun op_sub/2, fun op_mul/2, fun op_div/2]). + +op_add(A, B) -> + Info = [A,B], + R = unify(catch A + B, Info), + R = unify(my_apply(erlang,'+',[A,B]), Info), + case R of + _ when A + B =:= element(1,R) -> ok; + {{'EXIT',badarith}, Info} -> ok + end. + +op_sub(A, B) -> + Info = [A,B], + R = unify(catch A - B, Info), + R = unify(my_apply(erlang,'-',[A,B]), Info), + case R of + _ when A - B =:= element(1,R) -> ok; + {{'EXIT',badarith}, Info} -> ok + end. + +op_mul(A, B) -> + Info = [A,B], + R = unify(catch A * B, Info), + R = unify(my_apply(erlang,'*',[A,B]), Info), + case R of + _ when A * B =:= element(1,R) -> ok; + {{'EXIT',badarith}, Info} -> ok + end. + +op_div(A, B) -> + Info = [A,B], + R = unify(catch A / B, Info), + R = unify(my_apply(erlang,'/',[A,B]), Info), + case R of + _ when A / B =:= element(1,R) -> ok; + {{'EXIT',badarith}, Info} -> ok + end. + +my_apply(M, F, A) -> + catch apply(id(M), id(F), A). + +% Unify exceptions be removing stack traces. +% and add argument info to make it easer to debug failed matches. +unify({'EXIT',{Reason,_Stack}}, Info) -> + {{'EXIT', Reason}, Info}; +unify(Other, Info) -> + {Other, Info}. + + +-define(epsilon, 1.0e-20). +check_epsilon(R,Val) -> + if erlang:abs(R-Val) < ?epsilon -> ok; + true -> ?t:fail({R,Val}) + end. + +t_mul_add_ops(Config) when is_list(Config) -> + check_epsilon(op_mul_add(1, 2.0, 1.0, 0.0), 1.0), + check_epsilon(op_mul_add(2, 2.0, 1.0, 0.0), 3.0), + check_epsilon(op_mul_add(3, 2.0, 1.0, 0.0), 7.0), + check_epsilon(op_mul_add(4, 2.0, 1.0, 0.0), 15.0), + check_epsilon(op_mul_add(5, 2.0, 1.0, 0.0), 31.0), + check_epsilon(op_mul_add(6, 2.0, 1.0, 0.0), 63.0), + check_epsilon(op_mul_add(6, 2.0, 1.3, 0.0), 81.9), + check_epsilon(op_mul_add(6, 2.03, 1.3, 0.0), 87.06260151458997), + ok. + + +op_mul_add(0, _, _, R) -> R; +op_mul_add(N, A, B, R) when is_float(A), is_float(B), is_float(R) -> + op_mul_add(N - 1, A, B, R * A + B). + diff --git a/erts/emulator/test/float_SUITE_data/fp_drv.c b/erts/emulator/test/float_SUITE_data/fp_drv.c index 5919dd8e2f..a91d622040 100644 --- a/erts/emulator/test/float_SUITE_data/fp_drv.c +++ b/erts/emulator/test/float_SUITE_data/fp_drv.c @@ -18,6 +18,7 @@ */ #if defined(DEBUG) || 0 +# include # define PRINTF(X) printf X #else # define PRINTF(X) -- cgit v1.2.3 From bc40e21224e871c9bcbb5c2f27854c308bc4fe01 Mon Sep 17 00:00:00 2001 From: Luca Favatella Date: Sun, 6 Dec 2015 21:37:42 +0000 Subject: Fix compilation with `--enable-vm-probes` ... broken by 3ac08f9b. Compilation error: ``` beam/erl_message.c: In function 'erts_send_message': beam/erl_message.c:753:56: error: macro "copy_struct" requires 4 arguments, but only 3 given utag = copy_struct(DT_UTAG(sender), dt_utag_size, ohp); ^ beam/erl_message.c:753:10: error: 'copy_struct' undeclared (first use in this function) utag = copy_struct(DT_UTAG(sender), dt_utag_size, ohp); ^ beam/erl_message.c:753:10: note: each undeclared identifier is reported only once for each function it appears in ``` --- erts/emulator/beam/erl_message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 1811651a58..d593108f8e 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -750,7 +750,7 @@ erts_send_message(Process* sender, if (is_immed(DT_UTAG(sender))) utag = DT_UTAG(sender); else - utag = copy_struct(DT_UTAG(sender), dt_utag_size, ohp); + utag = copy_struct(DT_UTAG(sender), dt_utag_size, &hp, ohp); #ifdef DTRACE_TAG_HARDDEBUG erts_fprintf(stderr, "Dtrace -> (%T) Spreading tag (%T) with " -- cgit v1.2.3 From ac529b0326496e52f3289464f9410001bc3bde6d Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 16 Nov 2015 11:21:49 +0100 Subject: Fix memory leaks --- erts/emulator/beam/erl_gc.c | 8 +++++++- erts/emulator/beam/erl_process.c | 17 +++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 6cb37752bc..3bb1f601aa 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -734,6 +734,12 @@ erts_garbage_collect_hibernate(Process* p) p->arg_reg, p->arity); + ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, + (p->abandoned_heap + ? p->abandoned_heap + : p->heap), + p->heap_sz * sizeof(Eterm)); + p->heap = heap; p->high_water = htop; p->htop = htop; @@ -1435,7 +1441,7 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end, (p->abandoned_heap ? p->abandoned_heap : HEAP_START(p)), - (HEAP_END(p) - HEAP_START(p)) * sizeof(Eterm)); + p->heap_sz * sizeof(Eterm)); p->abandoned_heap = NULL; p->flags &= ~F_ABANDONED_HEAP_USE; HEAP_START(p) = n_heap; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 9acce8acb6..ebb4d323e6 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11259,6 +11259,7 @@ erts_cleanup_empty_process(Process* p) static void delete_process(Process* p) { + Eterm *heap; VERBOSE(DEBUG_PROCESSES, ("Removing process: %T\n",p->common.id)); /* Cleanup psd */ @@ -11283,16 +11284,17 @@ delete_process(Process* p) * Release heaps. Clobber contents in DEBUG build. */ - -#ifdef DEBUG - sys_memset(p->heap, DEBUG_BAD_BYTE, p->heap_sz*sizeof(Eterm)); -#endif - #ifdef HIPE hipe_delete_process(&p->hipe); #endif - ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, (void*) p->heap, p->heap_sz*sizeof(Eterm)); + heap = p->abandoned_heap ? p->abandoned_heap : p->heap; + +#ifdef DEBUG + sys_memset(heap, DEBUG_BAD_BYTE, p->heap_sz*sizeof(Eterm)); +#endif + + ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, (void*) heap, p->heap_sz*sizeof(Eterm)); if (p->old_heap != NULL) { #ifdef DEBUG @@ -11311,6 +11313,9 @@ delete_process(Process* p) free_message_buffer(p->mbuf); } + if (p->msg_frag) + erts_cleanup_messages(p->msg_frag); + erts_erase_dicts(p); /* free all pending messages */ -- cgit v1.2.3 From ccbb5ba4d0198f3ba21b83ae1aca3fe52a7f2c80 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 17 Nov 2015 15:21:56 +0100 Subject: Remove unused variable --- erts/emulator/beam/erl_process.c | 4 +--- erts/emulator/beam/erl_process.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index ebb4d323e6..ebe9361b8d 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -335,7 +335,6 @@ static ErtsAlignedSchedulerSleepInfo *aligned_dirty_io_sched_sleep_info; static Uint last_reductions; static Uint last_exact_reductions; -Uint erts_default_process_flags; Eterm erts_system_monitor; Eterm erts_system_monitor_long_gc; Uint erts_system_monitor_long_schedule; @@ -682,7 +681,6 @@ erts_init_process(int ncpu, int proc_tab_size, int legacy_proc_tab) last_reductions = 0; last_exact_reductions = 0; - erts_default_process_flags = 0; } void @@ -10737,7 +10735,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). Eterm args, /* Arguments for function (must be well-formed list). */ ErlSpawnOpts* so) /* Options for spawn. */ { - Uint flags = erts_default_process_flags; + Uint flags = 0; ErtsRunQueue *rq = NULL; Process *p; Sint arity; /* Number of arguments. */ diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 884027f482..b80aa12872 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1248,7 +1248,6 @@ Eterm* erts_heap_alloc(Process* p, Uint need, Uint xtra); Eterm* erts_set_hole_marker(Eterm* ptr, Uint sz); #endif -extern Uint erts_default_process_flags; extern erts_smp_rwmtx_t erts_cpu_bind_rwmtx; /* If any of the erts_system_monitor_* variables are set (enabled), ** erts_system_monitor must be != NIL, to allow testing on just -- cgit v1.2.3 From 6ba10e6d64477a4c470baa079eaaf2b20a746747 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 17 Nov 2015 15:24:55 +0100 Subject: Off heap message queue test suite --- erts/emulator/test/Makefile | 1 + .../emulator/test/off_heap_message_queue_SUITE.erl | 216 +++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 erts/emulator/test/off_heap_message_queue_SUITE.erl (limited to 'erts') diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 77614d455c..6519fd8982 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -79,6 +79,7 @@ MODULES= \ node_container_SUITE \ nofrag_SUITE \ num_bif_SUITE \ + off_heap_message_queue_SUITE \ op_SUITE \ port_SUITE \ port_bif_SUITE \ diff --git a/erts/emulator/test/off_heap_message_queue_SUITE.erl b/erts/emulator/test/off_heap_message_queue_SUITE.erl new file mode 100644 index 0000000000..a667704942 --- /dev/null +++ b/erts/emulator/test/off_heap_message_queue_SUITE.erl @@ -0,0 +1,216 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(off_heap_message_queue_SUITE). + +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2,end_per_testcase/2]). +-export([basic/1, process_info_messages/1]). + +-export([basic_test/1]). + +-include_lib("test_server/include/test_server.hrl"). + +init_per_testcase(Case, Config) -> + ?line Dog=test_server:timetrap(test_server:minutes(2)), + [{watchdog, Dog}, {testcase, Case}|Config]. + +end_per_testcase(_, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [basic, process_info_messages]. + +groups() -> + []. + +init_per_suite(Config) -> +%% erts_debug:set_internal_state(available_internal_state, true), + Config. + +end_per_suite(_Config) -> +%% erts_debug:set_internal_state(available_internal_state, false), + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%% +%% +%% Test cases +%% +%% + +basic(Config) when is_list(Config) -> + + basic_test(erlang:system_info(off_heap_message_queue)), + + {ok, Node1} = start_node(Config, "+xohmq true"), + ok = rpc:call(Node1, ?MODULE, basic_test, [true]), + stop_node(Node1), + + {ok, Node2} = start_node(Config, "+xohmq false"), + ok = rpc:call(Node2, ?MODULE, basic_test, [false]), + stop_node(Node2), + ok. + +basic_test(Default) -> + + Default = erlang:system_info(off_heap_message_queue), + true = (Default == true) orelse (Default == false), + + {off_heap_message_queue, Default} = process_info(self(), off_heap_message_queue), + Default = process_flag(off_heap_message_queue, true), + {off_heap_message_queue, true} = process_info(self(), off_heap_message_queue), + true = process_flag(off_heap_message_queue, false), + {off_heap_message_queue, false} = process_info(self(), off_heap_message_queue), + false = process_flag(off_heap_message_queue, Default), + {'EXIT', _} = (catch process_flag(off_heap_message_queue, blupp)), + + P1 = spawn_opt(fun () -> receive after infinity -> ok end end, + [link]), + {off_heap_message_queue, Default} = process_info(P1, off_heap_message_queue), + unlink(P1), + exit(P1, bye), + + P2 = spawn_opt(fun () -> receive after infinity -> ok end end, + [link, {off_heap_message_queue, false}]), + {off_heap_message_queue, false} = process_info(P2, off_heap_message_queue), + unlink(P2), + exit(P2, bye), + + P3 = spawn_opt(fun () -> receive after infinity -> ok end end, + [link, {off_heap_message_queue, true}]), + {off_heap_message_queue, true} = process_info(P3, off_heap_message_queue), + unlink(P3), + exit(P3, bye), + + {'EXIT', _} = (catch spawn_opt(fun () -> receive after infinity -> ok end end, + [link, {off_heap_message_queue, blapp}])), + + ok. + +process_info_messages(Config) when is_list(Config) -> + Tester = self(), + P1 = spawn_opt(fun () -> + receive after 500 -> ok end, + false = process_flag(off_heap_message_queue, true), + Tester ! first, + receive after 500 -> ok end, + true = process_flag(off_heap_message_queue, false), + Tester ! second, + receive after 500 -> ok end, + false = process_flag(off_heap_message_queue, true), + Tester ! third, + receive after 500 -> ok end, + true = process_flag(off_heap_message_queue, false), + Tester ! fourth, + + receive after infinity -> ok end + end, + [link, {off_heap_message_queue, false}]), + + P1 ! "A", + receive first -> ok end, + P1 ! "B", + receive second -> ok end, + P1 ! "C", + receive third -> ok end, + P1 ! "D", + receive fourth -> ok end, + P1 ! "E", + + {messages, ["A", "B", "C", "D", "E"]} = process_info(P1, messages), + + P2 = spawn_opt(fun () -> + receive after 500 -> ok end, + false = process_flag(off_heap_message_queue, true), + Tester ! first, + receive after 500 -> ok end, + true = process_flag(off_heap_message_queue, false), + Tester ! second, + receive after 500 -> ok end, + false = process_flag(off_heap_message_queue, true), + Tester ! third, + receive after 500 -> ok end, + true = process_flag(off_heap_message_queue, false), + Tester ! fourth, + receive after 500 -> ok end, + + Tester ! process_info(self(), messages), + + receive M1 -> M1 = "A" end, + receive M2 -> M2 = "B" end, + receive M3 -> M3 = "C" end, + receive M4 -> M4 = "D" end, + receive M5 -> M5 = "E" end, + + Tester ! self() + end, + [link, {off_heap_message_queue, false}]), + + P2 ! "A", + receive first -> ok end, + P2 ! "B", + receive second -> ok end, + P2 ! "C", + receive third -> ok end, + P2 ! "D", + receive fourth -> ok end, + P2 ! "E", + + receive + Msg -> + {messages, ["A", "B", "C", "D", "E"]} = Msg + end, + + receive P2 -> ok end, + + ok. + +%% +%% +%% helpers +%% +%% + +start_node(Config) -> + start_node(Config, []). +start_node(Config, Opts) when is_list(Config), is_list(Opts) -> + Pa = filename:dirname(code:which(?MODULE)), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(?config(testcase, Config)) + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive]))), + ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). + +stop_node(Node) -> + ?t:stop_node(Node). -- cgit v1.2.3 From cc3cc025f43a00281d2f836e12c2e1627cd50145 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 17 Nov 2015 15:25:22 +0100 Subject: Fix process_info(_, off_heap_message_queue) --- erts/emulator/beam/erl_bif_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 1eb106a551..3fe8aed50e 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1497,7 +1497,7 @@ process_info_aux(Process *BIF_P, } case am_off_heap_message_queue: - res = BIF_P->flags & F_OFF_HEAP_MSGQ ? am_true : am_false; + res = rp->flags & F_OFF_HEAP_MSGQ ? am_true : am_false; hp = HAlloc(BIF_P, 3); break; -- cgit v1.2.3 From a5dd6499d53ed596e2f8aa17ee35ff87cd32fe60 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 19 Nov 2015 17:07:06 +0100 Subject: Distinguish between GC disabled by BIFs and other disabled GC Processes remember heap fragments that are known to be fully live due to creation in a just called BIF that yields in the live_hf_end field. This field must not be used if we have not disabled GC in a BIF. F_DELAY_GC has been introduced in order to distinguish between to two different scenarios. - F_DISABLE_GC should *only* be used by BIFs. This when the BIF needs to yield while preventig a GC. - F_DELAY_GC should only be used when GC is temporarily disabled while the process is scheduled. A process must not be scheduled out while F_DELAY_GC is set. --- erts/emulator/beam/beam_emu.c | 14 +++++++------- erts/emulator/beam/erl_gc.c | 15 ++++++++++++--- erts/emulator/beam/erl_process.c | 2 ++ erts/emulator/beam/erl_process.h | 14 +++++++++++++- erts/emulator/hipe/hipe_native_bif.c | 6 +++--- 5 files changed, 37 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 208a16dfd0..95a22e6c54 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1843,8 +1843,8 @@ void process_main(void) * in the queue. This since messages with data outside * the heap will be corrupted by a GC. */ - ASSERT(!(c_p->flags & F_DISABLE_GC)); - c_p->flags |= F_DISABLE_GC; + ASSERT(!(c_p->flags & F_DELAY_GC)); + c_p->flags |= F_DELAY_GC; loop_rec__: PROCESS_MAIN_CHK_LOCKS(c_p); @@ -1858,7 +1858,7 @@ void process_main(void) if (ERTS_PROC_PENDING_EXIT(c_p)) { erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); SWAPOUT; - c_p->flags &= ~F_DISABLE_GC; + c_p->flags &= ~F_DELAY_GC; goto do_schedule; /* Will be rescheduled for exit */ } ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); @@ -1868,7 +1868,7 @@ void process_main(void) else #endif { - c_p->flags &= ~F_DISABLE_GC; + c_p->flags &= ~F_DELAY_GC; SET_I((BeamInstr *) Arg(0)); Goto(*I); /* Jump to a wait or wait_timeout instruction */ } @@ -1997,7 +1997,7 @@ void process_main(void) CANCEL_TIMER(c_p); erts_save_message_in_proc(c_p, msgp); - c_p->flags &= ~F_DISABLE_GC; + c_p->flags &= ~F_DELAY_GC; if (ERTS_IS_GC_DESIRED_INTERNAL(c_p, HTOP, E)) { /* @@ -2019,7 +2019,7 @@ void process_main(void) */ OpCase(loop_rec_end_f): { - ASSERT(c_p->flags & F_DISABLE_GC); + ASSERT(c_p->flags & F_DELAY_GC); SET_I((BeamInstr *) Arg(0)); SAVE_MESSAGE(c_p); @@ -2028,7 +2028,7 @@ void process_main(void) goto loop_rec__; } - c_p->flags &= ~F_DISABLE_GC; + c_p->flags &= ~F_DELAY_GC; c_p->i = I; SWAPOUT; c_p->arity = 0; diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 3bb1f601aa..3784367195 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -440,8 +440,15 @@ delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need) ERTS_HOLE_CHECK(p); - if (p->live_hf_end == ERTS_INVALID_HFRAG_PTR) + if ((p->flags & F_DISABLE_GC) + && p->live_hf_end == ERTS_INVALID_HFRAG_PTR) { + /* + * A BIF yielded with disabled GC. Remember + * heap fragments created by the BIF until we + * do next GC. + */ p->live_hf_end = live_hf_end; + } if (need == 0) return 1; @@ -564,10 +571,12 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); #endif - if (p->flags & F_DISABLE_GC) + if (p->flags & (F_DISABLE_GC|F_DELAY_GC)) return delay_garbage_collection(p, live_hf_end, need); - if (p->live_hf_end != ERTS_INVALID_HFRAG_PTR) + if (p->abandoned_heap) + live_hf_end = ERTS_INVALID_HFRAG_PTR; + else if (p->live_hf_end != ERTS_INVALID_HFRAG_PTR) live_hf_end = p->live_hf_end; esdp = erts_get_scheduler_data(); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index ebe9361b8d..d17355207b 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -9262,6 +9262,8 @@ Process *schedule(Process *p, int calls) } else { sched_out_proc: + ASSERT(!(p->flags & F_DELAY_GC)); + #ifdef ERTS_SMP ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); esdp = p->scheduler_data; diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index b80aa12872..41b367435d 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1288,10 +1288,22 @@ 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_DISABLE_GC (1 << 11) /* Disable GC */ +#define F_DISABLE_GC (1 << 11) /* Disable GC (see below) */ #define F_OFF_HEAP_MSGQ (1 << 12) /* Off heap msg queue */ #define F_OFF_HEAP_MSGQ_CHNG (1 << 13) /* Off heap msg queue changing */ #define F_ABANDONED_HEAP_USE (1 << 14) /* Have usage of abandoned heap */ +#define F_DELAY_GC (1 << 15) /* Similar to disable GC (see below) */ + +/* + * F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent + * GC of the process, but it is important to use the right + * one: + * - F_DISABLE_GC should *only* be used by BIFs. This when + * the BIF needs to yield while preventig a GC. + * - F_DELAY_GC should only be used when GC is temporarily + * disabled while the process is scheduled. A process must + * not be scheduled out while F_DELAY_GC is set. + */ /* process trace_flags */ #define F_SENSITIVE (1 << 0) diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index ad8fb685e5..1bfee94e9e 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -164,7 +164,7 @@ void hipe_select_msg(Process *p) JOIN_MESSAGE(p); CANCEL_TIMER(p); /* calls erts_cancel_proc_timer() */ erts_save_message_in_proc(p, msgp); - p->flags &= ~F_DISABLE_GC; + p->flags &= ~F_DELAY_GC; if (ERTS_IS_GC_DESIRED(p)) { /* * We want to GC soon but we leave a few @@ -519,7 +519,7 @@ Eterm hipe_check_get_msg(Process *c_p) { ErtsMessage *msgp; - c_p->flags |= F_DISABLE_GC; + c_p->flags |= F_DELAY_GC; next_message: @@ -541,7 +541,7 @@ Eterm hipe_check_get_msg(Process *c_p) /* XXX: BEAM doesn't need this */ c_p->hipe_smp.have_receive_locks = 1; #endif - c_p->flags &= ~F_DISABLE_GC; + c_p->flags &= ~F_DELAY_GC; return THE_NON_VALUE; #ifdef ERTS_SMP } -- cgit v1.2.3 From 1cd97bc82d042bc713473932af7d6061065f6527 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 20 Nov 2015 16:57:32 +0100 Subject: Always use literal_alloc --- erts/emulator/beam/erl_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 019aa0f16c..5574a3b713 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -290,7 +290,7 @@ static void set_default_literal_alloc_opts(struct au_init *ip) { SET_DEFAULT_ALLOC_OPTS(ip); - ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); + ip->enable = 1; ip->thr_spec = 0; ip->atype = BESTFIT; ip->init.bf.ao = 1; -- cgit v1.2.3 From 19c4689eea86f26c5af9b8f712c227ce4f62310b Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 24 Nov 2015 15:57:55 +0100 Subject: Replace off_heap_message_queue option with message_queue_data option The message_queue_data option can have the values - off_heap - on_heap - mixed --- erts/doc/src/erl.xml | 10 +- erts/doc/src/erlang.xml | 94 +++++--- erts/emulator/beam/atom.names | 5 +- erts/emulator/beam/bif.c | 30 +-- erts/emulator/beam/erl_bif_info.c | 37 +++- erts/emulator/beam/erl_gc.c | 195 ++++++++++++++++- erts/emulator/beam/erl_init.c | 21 +- erts/emulator/beam/erl_message.c | 160 ++++++++++---- erts/emulator/beam/erl_message.h | 2 +- erts/emulator/beam/erl_process.c | 4 + erts/emulator/beam/erl_process.h | 21 +- erts/emulator/test/Makefile | 2 +- erts/emulator/test/message_queue_data_SUITE.erl | 239 +++++++++++++++++++++ .../emulator/test/off_heap_message_queue_SUITE.erl | 216 ------------------- erts/etc/common/erlexec.c | 2 +- erts/preloaded/src/erlang.erl | 17 +- 16 files changed, 704 insertions(+), 351 deletions(-) create mode 100644 erts/emulator/test/message_queue_data_SUITE.erl delete mode 100644 erts/emulator/test/off_heap_message_queue_SUITE.erl (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index c4eb0e16ec..b6fa4c254c 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1338,14 +1338,14 @@

Default process flag settings.

- +xohmq true|false + +xmqd off_heap|on_heap|mixed

Sets the default value for the process flag - off_heap_message_queue. If +xohmq is not - passed, false will be the default. For more information, + message_queue_data. If +xmqd is not + passed, mixed will be the default. For more information, see the documentation of - process_flag(off_heap_message_queue, - OHMQ). + process_flag(message_queue_data, + MQD).

diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 2e82bb62a9..6ed03f3dfc 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -58,6 +58,12 @@ + + +

See erlang:process_flag(message_queue_data, MQD).

+
+
+

See erlang:timestamp/0.

@@ -4280,39 +4286,52 @@ os_prompt%

Returns the old value of the flag.

- + - Set process flag off_heap_message_queue for the calling process + Set process flag message_queue_data for the calling process +

This flag determines how messages in the message queue are stored. When the flag is:

- true + off_heap

All messages in the message queue will be stored outside of the process heap. This implies that no messages in the message queue will be part of a garbage collection of the process.

- false + on_heap +

+ All messages in the message queue will eventually be + placed on heap. They may however temporarily be stored + off heap. This is how messages always have been stored + up until ERTS version 8.0. +

+ mixed

Messages may be placed either on the heap or outside of the heap.

+

+ The default message_queue_data process flag is determined + by the +xmqd + erl command line argument. +

If the process potentially may get a hugh amount of messages, - you are recommended to set the flag to true. This since - a garbage collection with lots of messages placed on the heap - may become extremly expensive. Performance of the actual - message passing is however generally better when setting the - flag to false. + you are recommended to set the flag to off_heap. This + since a garbage collection with lots of messages placed on + the heap may become extremly expensive and the process may + consume large amounts of memory. Performance of the + actual message passing is however generally better when not + using the off_heap flag.

- When changing this flag from false to true, - all messages in the message queue are moved off heap. This - work has been initiated but not completed when this function + When changing this flag messages will be moved. This work + has been initiated but not completed when this function call returns.

Returns the old value of the flag.

@@ -4478,6 +4497,7 @@ os_prompt% +

Returns a list containing InfoTuples with miscellaneous information about the process identified by @@ -4530,6 +4550,7 @@ os_prompt% +

Returns information about the process identified by Pid, as specified by @@ -4698,13 +4719,14 @@ os_prompt% monitor by name, the list item is {process, {RegName, Node}}.

- {off_heap_message_queue, OHMQ} + {message_queue_data, MQD} -

Returns the current state of the off_heap_message_queue - process flag. OHMQ is either true, or - false. For more information, see the documentation of - process_flag(off_heap_message_queue, - OHMQ).

+

Returns the current state of the message_queue_data + process flag. MQD is either off_heap, + on_heap, or mixed. For more information, see the + documentation of + process_flag(message_queue_data, + MQD).

{priority, Level} @@ -5474,6 +5496,7 @@ true Creates a new process with a fun as entry point. +

Returns the process identifier (pid) of a new process @@ -5490,6 +5513,7 @@ true Creates a new process with a fun as entry point on a given node. +

Returns the process identifier (pid) of a new process started @@ -5505,6 +5529,7 @@ true Creates a new process with a function as entry point. +

Works as @@ -5607,17 +5632,17 @@ true fine-tuning an application and to measure the execution time with various VSize values.

- {off_heap_message_queue, OHMQ} + {message_queue_data, MQD} -

Sets the state of the off_heap_message_queue process - flag. OHMQ should be either true, or - false. The default off_heap_message_queue process - flag is determined by the - +xohmq erl +

Sets the state of the message_queue_data process + flag. MQD should be either off_heap, + on_heap, or mixed. The default + message_queue_data process flag is determined by the + +xmqd erl command line argument. For more information, see the documentation of - process_flag(off_heap_message_queue, - OHMQ).

+ process_flag(message_queue_data, + MQD).

@@ -5627,6 +5652,7 @@ true Creates a new process with a function as entry point on a given node. +

Returns the process identifier (pid) of a new process started @@ -7106,15 +7132,15 @@ ok used by the runtime system. It is on the form "<major ver>.<minor ver>".

- off_heap_message_queue + message_queue_data -

Returns the default value of the off_heap_message_queue - process flag which is either true or false. This - default is set by the erl command line argument - +xohmq. For more information on the - off_heap_message_queue process flag, see documentation of - process_flag(off_heap_message_queue, - OHMQ).

+

Returns the default value of the message_queue_data + process flag which is either off_heap, on_heap, or mixed. + This default is set by the erl command line argument + +xmqd. For more information on the + message_queue_data process flag, see documentation of + process_flag(message_queue_data, + MQD).

otp_release diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index ea04495574..7424e47ec3 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -350,6 +350,7 @@ atom memory_internal atom memory_types atom message atom message_binary +atom message_queue_data atom message_queue_len atom messages atom merge_trap @@ -361,6 +362,7 @@ atom min_heap_size atom min_bin_vheap_size atom minor_version atom Minus='-' +atom mixed atom module atom module_info atom monitored_by @@ -423,11 +425,12 @@ atom notify atom notsup atom nouse_stdio atom objects -atom off_heap_message_queue +atom off_heap atom offset atom ok atom old_heap_block_size atom old_heap_size +atom on_heap atom on_load atom open atom open_error diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index f0340540cb..410a6cecac 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -910,13 +910,22 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1) so.priority = PRIORITY_LOW; else goto error; - } else if (arg == am_off_heap_message_queue) { - if (val == am_true) - so.flags |= SPO_OFF_HEAP_MSGQ; - else if (val == am_false) + } else if (arg == am_message_queue_data) { + switch (val) { + case am_mixed: + so.flags &= ~(SPO_OFF_HEAP_MSGQ|SPO_ON_HEAP_MSGQ); + break; + case am_on_heap: so.flags &= ~SPO_OFF_HEAP_MSGQ; - else + so.flags |= SPO_ON_HEAP_MSGQ; + break; + case am_off_heap: + so.flags &= ~SPO_ON_HEAP_MSGQ; + so.flags |= SPO_OFF_HEAP_MSGQ; + break; + default: goto error; + } } else if (arg == am_min_heap_size && is_small(val)) { Sint min_heap_size = signed_val(val); if (min_heap_size < 0) { @@ -1695,15 +1704,10 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2) } BIF_RET(old_value); } - else if (BIF_ARG_1 == am_off_heap_message_queue) { - int enable; - if (BIF_ARG_2 == am_true) - enable = 1; - else if (BIF_ARG_2 == am_false) - enable = 0; - else + else if (BIF_ARG_1 == am_message_queue_data) { + old_value = erts_change_message_queue_management(BIF_P, BIF_ARG_2); + if (is_non_value(old_value)) goto error; - old_value = erts_change_off_heap_message_queue_state(BIF_P, enable); BIF_RET(old_value); } else if (BIF_ARG_1 == am_sensitive) { diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 3fe8aed50e..bb75b8abb6 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -589,7 +589,7 @@ static Eterm pi_args[] = { am_min_bin_vheap_size, am_current_location, am_current_stacktrace, - am_off_heap_message_queue + am_message_queue_data }; #define ERTS_PI_ARGS ((int) (sizeof(pi_args)/sizeof(Eterm))) @@ -637,7 +637,7 @@ pi_arg2ix(Eterm arg) case am_min_bin_vheap_size: return 28; case am_current_location: return 29; case am_current_stacktrace: return 30; - case am_off_heap_message_queue: return 31; + case am_message_queue_data: return 31; default: return -1; } } @@ -1496,8 +1496,22 @@ process_info_aux(Process *BIF_P, break; } - case am_off_heap_message_queue: - res = rp->flags & F_OFF_HEAP_MSGQ ? am_true : am_false; + case am_message_queue_data: + switch (rp->flags & (F_OFF_HEAP_MSGQ|F_ON_HEAP_MSGQ)) { + case F_OFF_HEAP_MSGQ: + res = am_off_heap; + break; + case F_ON_HEAP_MSGQ: + res = am_on_heap; + break; + case 0: + res = am_mixed; + break; + default: + res = am_error; + ERTS_INTERNAL_ERROR("Inconsistent message queue management state"); + break; + } hp = HAlloc(BIF_P, 3); break; @@ -2662,9 +2676,18 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(am_true); } #endif - else if (BIF_ARG_1 == am_off_heap_message_queue) { - BIF_RET(erts_default_spo_flags & SPO_OFF_HEAP_MSGQ - ? am_true : am_false); + else if (BIF_ARG_1 == am_message_queue_data) { + switch (erts_default_spo_flags & (SPO_ON_HEAP_MSGQ|SPO_OFF_HEAP_MSGQ)) { + case SPO_OFF_HEAP_MSGQ: + BIF_RET(am_off_heap); + case SPO_ON_HEAP_MSGQ: + BIF_RET(am_on_heap); + case 0: + BIF_RET(am_mixed); + default: + ERTS_INTERNAL_ERROR("Inconsistent message queue management state"); + BIF_RET(am_error); + } } else if (ERTS_IS_ATOM_STR("compile_info",BIF_ARG_1)) { Uint sz; diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 3784367195..b63567e563 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -145,6 +145,7 @@ static void offset_rootset(Process *p, Sint offs, char* area, Uint area_size, Eterm* objv, int nobj); static void offset_off_heap(Process* p, Sint offs, char* area, Uint area_size); static void offset_mqueue(Process *p, Sint offs, char* area, Uint area_size); +static void move_msgq_to_heap(Process *p); static void init_gc_info(ErtsGCInfo *gcip); @@ -520,6 +521,14 @@ young_gen_usage(Process *p) Eterm *aheap; hsz = p->mbuf_sz; + + if (p->flags & F_ON_HEAP_MSGQ) { + ErtsMessage *mp; + for (mp = p->msg.first; mp; mp = mp->next) + if (mp->data.attached) + hsz += erts_msg_attached_data_size(mp); + } + aheap = p->abandoned_heap; if (!aheap) hsz += p->htop - p->heap; @@ -1040,10 +1049,13 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end, do_minor(p, live_hf_end, (char *) mature, mature_size*sizeof(Eterm), new_sz, objv, nobj); + if (p->flags & F_ON_HEAP_MSGQ) + move_msgq_to_heap(p); + new_mature = p->old_htop - prev_old_htop; size_after = new_mature; - size_after += HEAP_TOP(p) - HEAP_START(p); + size_after += HEAP_TOP(p) - HEAP_START(p) + p->mbuf_sz; *recl += (size_before - size_after); ErtsGcQuickSanityCheck(p); @@ -1461,9 +1473,14 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end, HIGH_WATER(p) = HEAP_TOP(p); + remove_message_buffers(p); + + if (p->flags & F_ON_HEAP_MSGQ) + move_msgq_to_heap(p); + ErtsGcQuickSanityCheck(p); - size_after = HEAP_TOP(p) - HEAP_START(p); + size_after = HEAP_TOP(p) - HEAP_START(p) + p->mbuf_sz; *recl += size_before - size_after; adjusted = adjust_after_fullsweep(p, need, objv, nobj); @@ -1471,8 +1488,6 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end, #ifdef HARDDEBUG disallow_heap_frag_ref_in_heap(p); #endif - remove_message_buffers(p); - ErtsGcQuickSanityCheck(p); return gc_cost(size_after, adjusted ? size_after : 0); @@ -2000,6 +2015,173 @@ collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end, return n_htop; } +static ERTS_INLINE void +copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap, + ErlHeapFragment *bp, Eterm *refs, int nrefs) +{ + Uint sz; + int i; + Sint offs; + struct erl_off_heap_header* oh; + Eterm *fhp, *hp; + + OH_OVERHEAD(off_heap, bp->off_heap.overhead); + sz = bp->used_size; + + fhp = bp->mem; + hp = *hpp; + offs = hp - fhp; + + oh = NULL; + while (sz--) { + Uint cpy_sz; + Eterm val = *fhp++; + + switch (primary_tag(val)) { + case TAG_PRIMARY_IMMED1: + *hp++ = val; + break; + case TAG_PRIMARY_LIST: + case TAG_PRIMARY_BOXED: + *hp++ = offset_ptr(val, offs); + break; + case TAG_PRIMARY_HEADER: + *hp++ = val; + switch (val & _HEADER_SUBTAG_MASK) { + case ARITYVAL_SUBTAG: + break; + case REFC_BINARY_SUBTAG: + case FUN_SUBTAG: + case EXTERNAL_PID_SUBTAG: + case EXTERNAL_PORT_SUBTAG: + case EXTERNAL_REF_SUBTAG: + oh = (struct erl_off_heap_header*) (hp-1); + cpy_sz = thing_arityval(val); + goto cpy_words; + default: + cpy_sz = header_arity(val); + + cpy_words: + ASSERT(sz >= cpy_sz); + sz -= cpy_sz; + while (cpy_sz >= 8) { + cpy_sz -= 8; + *hp++ = *fhp++; + *hp++ = *fhp++; + *hp++ = *fhp++; + *hp++ = *fhp++; + *hp++ = *fhp++; + *hp++ = *fhp++; + *hp++ = *fhp++; + *hp++ = *fhp++; + } + switch (cpy_sz) { + case 7: *hp++ = *fhp++; + case 6: *hp++ = *fhp++; + case 5: *hp++ = *fhp++; + case 4: *hp++ = *fhp++; + case 3: *hp++ = *fhp++; + case 2: *hp++ = *fhp++; + case 1: *hp++ = *fhp++; + default: break; + } + if (oh) { + /* Add to offheap list */ + oh->next = off_heap->first; + off_heap->first = oh; + ASSERT(*hpp <= (Eterm*)oh); + ASSERT(hp > (Eterm*)oh); + oh = NULL; + } + break; + } + break; + } + } + + ASSERT(bp->used_size == hp - *hpp); + *hpp = hp; + + for (i = 0; i < nrefs; i++) { + if (is_not_immed(refs[i])) + refs[i] = offset_ptr(refs[i], offs); + } + bp->off_heap.first = NULL; +} + +static void +move_msgq_to_heap(Process *p) +{ + ErtsMessage **mpp = &p->msg.first; + + while (*mpp) { + ErtsMessage *mp = *mpp; + + if (mp->data.attached) { + ErlHeapFragment *bp; + ErtsHeapFactory factory; + + erts_factory_proc_prealloc_init(&factory, p, + erts_msg_attached_data_size(mp)); + + if (is_non_value(ERL_MESSAGE_TERM(mp))) { + if (mp->data.dist_ext) { + ASSERT(mp->data.dist_ext->heap_size >= 0); + if (is_not_nil(ERL_MESSAGE_TOKEN(mp))) { + bp = erts_dist_ext_trailer(mp->data.dist_ext); + ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp), + bp->used_size, + &factory.hp, + factory.off_heap); + erts_cleanup_offheap(&bp->off_heap); + } + ERL_MESSAGE_TERM(mp) = erts_decode_dist_ext(&factory, + mp->data.dist_ext); + erts_free_dist_ext_copy(mp->data.dist_ext); + mp->data.dist_ext = NULL; + } + } + else { + + if (mp->data.attached == ERTS_MSG_COMBINED_HFRAG) + bp = &mp->hfrag; + else + bp = mp->data.heap_frag; + + if (bp->next) + erts_move_multi_frags(&factory.hp, factory.off_heap, bp, + mp->m, ERL_MESSAGE_REF_ARRAY_SZ, 0); + else + copy_one_frag(&factory.hp, factory.off_heap, bp, + mp->m, ERL_MESSAGE_REF_ARRAY_SZ); + + if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) { + mp->data.heap_frag = NULL; + free_message_buffer(bp); + } + else { + ErtsMessage *tmp = erts_alloc_message(0, NULL); + sys_memcpy((void *) tmp->m, (void *) mp->m, + sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ); + tmp->next = mp->next; + if (p->msg.save == &mp->next) + p->msg.save = &tmp->next; + if (p->msg.last == &mp->next) + p->msg.last = &tmp->next; + *mpp = tmp; + mp->next = NULL; + erts_cleanup_messages(mp); + mp = tmp; + } + } + + erts_factory_close(&factory); + } + + mpp = &(*mpp)->next; + } +} + static Uint setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) { @@ -2089,9 +2271,8 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) case F_OFF_HEAP_MSGQ_CHNG: case 0: { /* - * Off heap message queue disabled, i.e. we may - * have references from the message queue to the - * heap... + * We do not have off heap message queue enabled, i.e. we + * need to add message queue to rootset... */ ErtsMessage *mp; diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index f396a0a156..628915eb23 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -630,7 +630,8 @@ void erts_usage(void) erts_fprintf(stderr, "-W set error logger warnings mapping,\n"); erts_fprintf(stderr, " see error_logger documentation for details\n"); - erts_fprintf(stderr, "-xohmq bool set default off_heap_message_queue flag for processes\n"); + erts_fprintf(stderr, "-xmqd val set default message queue data flag for processes,\n"); + erts_fprintf(stderr, " valid values are: off_heap | on_heap | mixed\n"); erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n"); erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024); erts_fprintf(stderr, "-zdntgc time set delayed node table gc in seconds\n"); @@ -2018,15 +2019,21 @@ erl_start(int argc, char **argv) case 'x': { char *sub_param = argv[i]+2; - if (has_prefix("ohmq", sub_param)) { - arg = get_arg(sub_param+4, argv[i+1], &i); - if (sys_strcmp(arg, "true") == 0) - erts_default_spo_flags |= SPO_OFF_HEAP_MSGQ; - else if (sys_strcmp(arg, "false") == 0) + if (has_prefix("mqd", sub_param)) { + arg = get_arg(sub_param+3, argv[i+1], &i); + if (sys_strcmp(arg, "mixed") == 0) + erts_default_spo_flags &= ~(SPO_ON_HEAP_MSGQ|SPO_OFF_HEAP_MSGQ); + else if (sys_strcmp(arg, "on_heap") == 0) { erts_default_spo_flags &= ~SPO_OFF_HEAP_MSGQ; + erts_default_spo_flags |= SPO_ON_HEAP_MSGQ; + } + else if (sys_strcmp(arg, "off_heap") == 0) { + erts_default_spo_flags &= ~SPO_ON_HEAP_MSGQ; + erts_default_spo_flags |= SPO_OFF_HEAP_MSGQ; + } else { erts_fprintf(stderr, - "Invalid off_heap_message_queue flag: %s\n", arg); + "Invalid message_queue_data flag: %s\n", arg); erts_usage(); } } else { diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 79739501a8..797212450c 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -629,9 +629,24 @@ erts_try_alloc_message_on_heap(Process *pp, #endif else { in_message_fragment: - - mp = erts_alloc_message(sz, hpp); - *ohpp = sz == 0 ? NULL : &mp->hfrag.off_heap; + if (!((*psp) & ERTS_PSFLG_ON_HEAP_MSGQ)) { + mp = erts_alloc_message(sz, hpp); + *ohpp = sz == 0 ? NULL : &mp->hfrag.off_heap; + } + else { + mp = erts_alloc_message(0, NULL); + if (!sz) { + *hpp = NULL; + *ohpp = NULL; + } + else { + ErlHeapFragment *bp; + bp = new_message_buffer(sz); + *hpp = &bp->mem[0]; + mp->data.heap_frag = bp; + *ohpp = &bp->off_heap; + } + } *on_heap_p = 0; } @@ -976,12 +991,12 @@ erts_complete_off_heap_message_queue_change(Process *c_p) ASSERT(erts_smp_atomic32_read_nob(&c_p->state) & ERTS_PSFLG_OFF_HEAP_MSGQ); /* - * This job was first initiated when the process changed - * "off heap message queue" state from false to true. Since - * then ERTS_PSFLG_OFF_HEAP_MSGQ has been set. However, the - * state change might have been changed again (multiple times) - * since then. Check users last requested state (the flag - * F_OFF_HEAP_MSGQ), and make the state consistent with that. + * This job was first initiated when the process changed to off heap + * message queue management. Since then ERTS_PSFLG_OFF_HEAP_MSGQ + * has been set. However, the management state might have been changed + * again (multiple times) since then. Check users last requested state + * (the flags F_OFF_HEAP_MSGQ, and F_ON_HEAP_MSGQ), and make the state + * consistent with that. */ if (!(c_p->flags & F_OFF_HEAP_MSGQ)) @@ -1022,8 +1037,9 @@ change_off_heap_msgq(void *vcohmq) } Eterm -erts_change_off_heap_message_queue_state(Process *c_p, int enable) +erts_change_message_queue_management(Process *c_p, Eterm new_state) { + Eterm res; #ifdef DEBUG if (c_p->flags & F_OFF_HEAP_MSGQ) { @@ -1042,57 +1058,117 @@ erts_change_off_heap_message_queue_state(Process *c_p, int enable) } #endif - if (c_p->flags & F_OFF_HEAP_MSGQ) { - /* Off heap message queue is enabled */ + switch (c_p->flags & (F_OFF_HEAP_MSGQ|F_ON_HEAP_MSGQ)) { - if (!enable) { + case F_OFF_HEAP_MSGQ: + res = am_off_heap; + + switch (new_state) { + case am_off_heap: + break; + case am_on_heap: + c_p->flags |= F_ON_HEAP_MSGQ; + erts_smp_atomic32_read_bor_nob(&c_p->state, + ERTS_PSFLG_ON_HEAP_MSGQ); + /* fall through */ + case am_mixed: c_p->flags &= ~F_OFF_HEAP_MSGQ; /* * We are not allowed to clear ERTS_PSFLG_OFF_HEAP_MSGQ - * if a change is ongoing. It will be adjusted when the - * change completes... + * if a off heap change is ongoing. It will be adjusted + * when the change completes... */ if (!(c_p->flags & F_OFF_HEAP_MSGQ_CHNG)) { /* Safe to clear ERTS_PSFLG_OFF_HEAP_MSGQ... */ erts_smp_atomic32_read_band_nob(&c_p->state, ~ERTS_PSFLG_OFF_HEAP_MSGQ); } + break; + default: + res = THE_NON_VALUE; /* badarg */ + break; } + break; + + case F_ON_HEAP_MSGQ: + res = am_on_heap; - return am_true; /* Old state */ + switch (new_state) { + case am_on_heap: + break; + case am_mixed: + c_p->flags &= ~F_ON_HEAP_MSGQ; + erts_smp_atomic32_read_band_nob(&c_p->state, + ~ERTS_PSFLG_ON_HEAP_MSGQ); + break; + case am_off_heap: + c_p->flags &= ~F_ON_HEAP_MSGQ; + erts_smp_atomic32_read_band_nob(&c_p->state, + ~ERTS_PSFLG_ON_HEAP_MSGQ); + goto change_to_off_heap; + default: + res = THE_NON_VALUE; /* badarg */ + break; + } + break; + + case 0: + res = am_mixed; + + switch (new_state) { + case am_mixed: + break; + case am_on_heap: + c_p->flags |= F_ON_HEAP_MSGQ; + erts_smp_atomic32_read_bor_nob(&c_p->state, + ERTS_PSFLG_ON_HEAP_MSGQ); + break; + case am_off_heap: + goto change_to_off_heap; + default: + res = THE_NON_VALUE; /* badarg */ + break; + } + break; + + default: + res = am_error; + ERTS_INTERNAL_ERROR("Inconsistent message queue management state"); + break; } - /* Off heap message queue is disabled */ + return res; + +change_to_off_heap: - if (enable) { - c_p->flags |= F_OFF_HEAP_MSGQ; + c_p->flags |= F_OFF_HEAP_MSGQ; + + /* + * We do not have to schedule a change if + * we have an ongoing off heap change... + */ + if (!(c_p->flags & F_OFF_HEAP_MSGQ_CHNG)) { + ErtsChangeOffHeapMessageQueue *cohmq; /* - * We do not have to schedule a change if - * we have an ongoing change... + * Need to set ERTS_PSFLG_OFF_HEAP_MSGQ and wait + * thread progress before completing the change in + * order to ensure that all senders observe that + * messages should be passed off heap. When the + * change has completed, GC does not need to inspect + * the message queue at all. */ - if (!(c_p->flags & F_OFF_HEAP_MSGQ_CHNG)) { - ErtsChangeOffHeapMessageQueue *cohmq; - /* - * Need to set ERTS_PSFLG_OFF_HEAP_MSGQ and wait - * thread progress before completing the change in - * order to ensure that all senders observe that - * messages should be passed off heap. When the - * change has completed, GC does not need to inspect - * the message queue at all. - */ - erts_smp_atomic32_read_bor_nob(&c_p->state, - ERTS_PSFLG_OFF_HEAP_MSGQ); - c_p->flags |= F_OFF_HEAP_MSGQ_CHNG; - cohmq = erts_alloc(ERTS_ALC_T_MSGQ_CHNG, - sizeof(ErtsChangeOffHeapMessageQueue)); - cohmq->pid = c_p->common.id; - erts_schedule_thr_prgr_later_op(change_off_heap_msgq, - (void *) cohmq, - &cohmq->lop); - } + erts_smp_atomic32_read_bor_nob(&c_p->state, + ERTS_PSFLG_OFF_HEAP_MSGQ); + c_p->flags |= F_OFF_HEAP_MSGQ_CHNG; + cohmq = erts_alloc(ERTS_ALC_T_MSGQ_CHNG, + sizeof(ErtsChangeOffHeapMessageQueue)); + cohmq->pid = c_p->common.id; + erts_schedule_thr_prgr_later_op(change_off_heap_msgq, + (void *) cohmq, + &cohmq->lop); } - return am_false; /* Old state */ + return res; } int diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 740ae46a0f..e241926638 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -277,7 +277,7 @@ void erts_cleanup_offheap(ErlOffHeap *offheap); void erts_save_message_in_proc(Process *p, ErtsMessage *msg); Sint erts_move_messages_off_heap(Process *c_p); Sint erts_complete_off_heap_message_queue_change(Process *c_p); -Eterm erts_change_off_heap_message_queue_state(Process *c_p, int enable); +Eterm erts_change_message_queue_management(Process *c_p, Eterm new_state); int erts_decode_dist_message(Process *, ErtsProcLocks, ErtsMessage *, int); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index d17355207b..7a31aa3e33 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -10779,6 +10779,10 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). state |= ERTS_PSFLG_OFF_HEAP_MSGQ; flags |= F_OFF_HEAP_MSGQ; } + else if (so->flags & SPO_ON_HEAP_MSGQ) { + state |= ERTS_PSFLG_ON_HEAP_MSGQ; + flags |= F_ON_HEAP_MSGQ; + } if (!rq) rq = erts_get_runq_proc(parent); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 41b367435d..f0798d8c2d 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1145,14 +1145,15 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16) #define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17) #define ERTS_PSFLG_OFF_HEAP_MSGQ ERTS_PSFLG_BIT(18) +#define ERTS_PSFLG_ON_HEAP_MSGQ ERTS_PSFLG_BIT(19) #ifdef ERTS_DIRTY_SCHEDULERS -#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(19) -#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(20) -#define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q ERTS_PSFLG_BIT(21) -#define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q ERTS_PSFLG_BIT(22) -#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 23) +#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(20) +#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(21) +#define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q ERTS_PSFLG_BIT(22) +#define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q ERTS_PSFLG_BIT(23) +#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 24) #else -#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 19) +#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 20) #endif #define ERTS_PSFLGS_IN_PRQ_MASK (ERTS_PSFLG_IN_PRQ_MAX \ @@ -1201,6 +1202,7 @@ void erts_check_for_holes(Process* p); #define SPO_MONITOR 4 #define SPO_SYSTEM_PROC 8 #define SPO_OFF_HEAP_MSGQ 16 +#define SPO_ON_HEAP_MSGQ 32 extern int erts_default_spo_flags; @@ -1290,9 +1292,10 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; #define F_FORCE_GC (1 << 10) /* Force gc at process in-scheduling */ #define F_DISABLE_GC (1 << 11) /* Disable GC (see below) */ #define F_OFF_HEAP_MSGQ (1 << 12) /* Off heap msg queue */ -#define F_OFF_HEAP_MSGQ_CHNG (1 << 13) /* Off heap msg queue changing */ -#define F_ABANDONED_HEAP_USE (1 << 14) /* Have usage of abandoned heap */ -#define F_DELAY_GC (1 << 15) /* Similar to disable GC (see below) */ +#define F_ON_HEAP_MSGQ (1 << 13) /* Off heap msg queue */ +#define F_OFF_HEAP_MSGQ_CHNG (1 << 14) /* Off heap msg queue changing */ +#define F_ABANDONED_HEAP_USE (1 << 15) /* Have usage of abandoned heap */ +#define F_DELAY_GC (1 << 16) /* Similar to disable GC (see below) */ /* * F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 6519fd8982..8cc47937b7 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -79,7 +79,7 @@ MODULES= \ node_container_SUITE \ nofrag_SUITE \ num_bif_SUITE \ - off_heap_message_queue_SUITE \ + message_queue_data_SUITE \ op_SUITE \ port_SUITE \ port_bif_SUITE \ diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl new file mode 100644 index 0000000000..11481409aa --- /dev/null +++ b/erts/emulator/test/message_queue_data_SUITE.erl @@ -0,0 +1,239 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(message_queue_data_SUITE). + +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2,end_per_testcase/2]). +-export([basic/1, process_info_messages/1]). + +-export([basic_test/1]). + +-include_lib("test_server/include/test_server.hrl"). + +init_per_testcase(Case, Config) -> + ?line Dog=test_server:timetrap(test_server:minutes(2)), + [{watchdog, Dog}, {testcase, Case}|Config]. + +end_per_testcase(_, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [basic, process_info_messages]. + +groups() -> + []. + +init_per_suite(Config) -> +%% erts_debug:set_internal_state(available_internal_state, true), + Config. + +end_per_suite(_Config) -> +%% erts_debug:set_internal_state(available_internal_state, false), + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%% +%% +%% Test cases +%% +%% + +basic(Config) when is_list(Config) -> + + basic_test(erlang:system_info(message_queue_data)), + + {ok, Node1} = start_node(Config, "+xmqd off_heap"), + ok = rpc:call(Node1, ?MODULE, basic_test, [off_heap]), + stop_node(Node1), + + {ok, Node2} = start_node(Config, "+xmqd on_heap"), + ok = rpc:call(Node2, ?MODULE, basic_test, [on_heap]), + stop_node(Node2), + + {ok, Node3} = start_node(Config, "+xmqd mixed"), + ok = rpc:call(Node3, ?MODULE, basic_test, [mixed]), + stop_node(Node3), + + ok. + +is_valid_mqd_value(off_heap) -> + true; +is_valid_mqd_value(on_heap) -> + true; +is_valid_mqd_value(mixed) -> + true; +is_valid_mqd_value(_) -> + false. + + +basic_test(Default) -> + + Default = erlang:system_info(message_queue_data), + true = is_valid_mqd_value(Default), + + {message_queue_data, Default} = process_info(self(), message_queue_data), + Default = process_flag(message_queue_data, off_heap), + {message_queue_data, off_heap} = process_info(self(), message_queue_data), + off_heap = process_flag(message_queue_data, on_heap), + {message_queue_data, on_heap} = process_info(self(), message_queue_data), + on_heap = process_flag(message_queue_data, mixed), + {message_queue_data, mixed} = process_info(self(), message_queue_data), + mixed = process_flag(message_queue_data, Default), + {'EXIT', _} = (catch process_flag(message_queue_data, blupp)), + + P1 = spawn_opt(fun () -> receive after infinity -> ok end end, + [link]), + {message_queue_data, Default} = process_info(P1, message_queue_data), + unlink(P1), + exit(P1, bye), + + P2 = spawn_opt(fun () -> receive after infinity -> ok end end, + [link, {message_queue_data, off_heap}]), + {message_queue_data, off_heap} = process_info(P2, message_queue_data), + unlink(P2), + exit(P2, bye), + + P3 = spawn_opt(fun () -> receive after infinity -> ok end end, + [link, {message_queue_data, on_heap}]), + {message_queue_data, on_heap} = process_info(P3, message_queue_data), + unlink(P3), + exit(P3, bye), + + P4 = spawn_opt(fun () -> receive after infinity -> ok end end, + [link, {message_queue_data, mixed}]), + {message_queue_data, mixed} = process_info(P4, message_queue_data), + unlink(P4), + exit(P4, bye), + + {'EXIT', _} = (catch spawn_opt(fun () -> receive after infinity -> ok end end, + [link, {message_queue_data, blapp}])), + + ok. + +process_info_messages(Config) when is_list(Config) -> + Tester = self(), + P1 = spawn_opt(fun () -> + receive after 500 -> ok end, + mixed = process_flag(message_queue_data, off_heap), + Tester ! first, + receive after 500 -> ok end, + off_heap = process_flag(message_queue_data, on_heap), + Tester ! second, + receive after 500 -> ok end, + on_heap = process_flag(message_queue_data, mixed), + Tester ! third, + receive after 500 -> ok end, + mixed = process_flag(message_queue_data, off_heap), + Tester ! fourth, + + receive after infinity -> ok end + end, + [link, {message_queue_data, mixed}]), + + P1 ! "A", + receive first -> ok end, + P1 ! "B", + receive second -> ok end, + P1 ! "C", + receive third -> ok end, + P1 ! "D", + receive fourth -> ok end, + P1 ! "E", + + {messages, ["A", "B", "C", "D", "E"]} = process_info(P1, messages), + + P2 = spawn_opt(fun () -> + receive after 500 -> ok end, + mixed = process_flag(message_queue_data, off_heap), + Tester ! first, + receive after 500 -> ok end, + off_heap = process_flag(message_queue_data, on_heap), + Tester ! second, + receive after 500 -> ok end, + on_heap = process_flag(message_queue_data, mixed), + Tester ! third, + receive after 500 -> ok end, + mixed = process_flag(message_queue_data, off_heap), + Tester ! fourth, + receive after 500 -> ok end, + + Tester ! process_info(self(), messages), + + receive M1 -> M1 = "A" end, + receive M2 -> M2 = "B" end, + receive M3 -> M3 = "C" end, + receive M4 -> M4 = "D" end, + receive M5 -> M5 = "E" end, + + Tester ! self() + end, + [link, {message_queue_data, mixed}]), + + P2 ! "A", + receive first -> ok end, + P2 ! "B", + receive second -> ok end, + P2 ! "C", + receive third -> ok end, + P2 ! "D", + receive fourth -> ok end, + P2 ! "E", + + receive + Msg -> + {messages, ["A", "B", "C", "D", "E"]} = Msg + end, + + receive P2 -> ok end, + + ok. + +%% +%% +%% helpers +%% +%% + +start_node(Config) -> + start_node(Config, []). +start_node(Config, Opts) when is_list(Config), is_list(Opts) -> + Pa = filename:dirname(code:which(?MODULE)), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(?config(testcase, Config)) + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive]))), + ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). + +stop_node(Node) -> + ?t:stop_node(Node). diff --git a/erts/emulator/test/off_heap_message_queue_SUITE.erl b/erts/emulator/test/off_heap_message_queue_SUITE.erl deleted file mode 100644 index a667704942..0000000000 --- a/erts/emulator/test/off_heap_message_queue_SUITE.erl +++ /dev/null @@ -1,216 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2014. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - --module(off_heap_message_queue_SUITE). - --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2]). --export([basic/1, process_info_messages/1]). - --export([basic_test/1]). - --include_lib("test_server/include/test_server.hrl"). - -init_per_testcase(Case, Config) -> - ?line Dog=test_server:timetrap(test_server:minutes(2)), - [{watchdog, Dog}, {testcase, Case}|Config]. - -end_per_testcase(_, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. - -suite() -> [{ct_hooks,[ts_install_cth]}]. - -all() -> - [basic, process_info_messages]. - -groups() -> - []. - -init_per_suite(Config) -> -%% erts_debug:set_internal_state(available_internal_state, true), - Config. - -end_per_suite(_Config) -> -%% erts_debug:set_internal_state(available_internal_state, false), - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - -%% -%% -%% Test cases -%% -%% - -basic(Config) when is_list(Config) -> - - basic_test(erlang:system_info(off_heap_message_queue)), - - {ok, Node1} = start_node(Config, "+xohmq true"), - ok = rpc:call(Node1, ?MODULE, basic_test, [true]), - stop_node(Node1), - - {ok, Node2} = start_node(Config, "+xohmq false"), - ok = rpc:call(Node2, ?MODULE, basic_test, [false]), - stop_node(Node2), - ok. - -basic_test(Default) -> - - Default = erlang:system_info(off_heap_message_queue), - true = (Default == true) orelse (Default == false), - - {off_heap_message_queue, Default} = process_info(self(), off_heap_message_queue), - Default = process_flag(off_heap_message_queue, true), - {off_heap_message_queue, true} = process_info(self(), off_heap_message_queue), - true = process_flag(off_heap_message_queue, false), - {off_heap_message_queue, false} = process_info(self(), off_heap_message_queue), - false = process_flag(off_heap_message_queue, Default), - {'EXIT', _} = (catch process_flag(off_heap_message_queue, blupp)), - - P1 = spawn_opt(fun () -> receive after infinity -> ok end end, - [link]), - {off_heap_message_queue, Default} = process_info(P1, off_heap_message_queue), - unlink(P1), - exit(P1, bye), - - P2 = spawn_opt(fun () -> receive after infinity -> ok end end, - [link, {off_heap_message_queue, false}]), - {off_heap_message_queue, false} = process_info(P2, off_heap_message_queue), - unlink(P2), - exit(P2, bye), - - P3 = spawn_opt(fun () -> receive after infinity -> ok end end, - [link, {off_heap_message_queue, true}]), - {off_heap_message_queue, true} = process_info(P3, off_heap_message_queue), - unlink(P3), - exit(P3, bye), - - {'EXIT', _} = (catch spawn_opt(fun () -> receive after infinity -> ok end end, - [link, {off_heap_message_queue, blapp}])), - - ok. - -process_info_messages(Config) when is_list(Config) -> - Tester = self(), - P1 = spawn_opt(fun () -> - receive after 500 -> ok end, - false = process_flag(off_heap_message_queue, true), - Tester ! first, - receive after 500 -> ok end, - true = process_flag(off_heap_message_queue, false), - Tester ! second, - receive after 500 -> ok end, - false = process_flag(off_heap_message_queue, true), - Tester ! third, - receive after 500 -> ok end, - true = process_flag(off_heap_message_queue, false), - Tester ! fourth, - - receive after infinity -> ok end - end, - [link, {off_heap_message_queue, false}]), - - P1 ! "A", - receive first -> ok end, - P1 ! "B", - receive second -> ok end, - P1 ! "C", - receive third -> ok end, - P1 ! "D", - receive fourth -> ok end, - P1 ! "E", - - {messages, ["A", "B", "C", "D", "E"]} = process_info(P1, messages), - - P2 = spawn_opt(fun () -> - receive after 500 -> ok end, - false = process_flag(off_heap_message_queue, true), - Tester ! first, - receive after 500 -> ok end, - true = process_flag(off_heap_message_queue, false), - Tester ! second, - receive after 500 -> ok end, - false = process_flag(off_heap_message_queue, true), - Tester ! third, - receive after 500 -> ok end, - true = process_flag(off_heap_message_queue, false), - Tester ! fourth, - receive after 500 -> ok end, - - Tester ! process_info(self(), messages), - - receive M1 -> M1 = "A" end, - receive M2 -> M2 = "B" end, - receive M3 -> M3 = "C" end, - receive M4 -> M4 = "D" end, - receive M5 -> M5 = "E" end, - - Tester ! self() - end, - [link, {off_heap_message_queue, false}]), - - P2 ! "A", - receive first -> ok end, - P2 ! "B", - receive second -> ok end, - P2 ! "C", - receive third -> ok end, - P2 ! "D", - receive fourth -> ok end, - P2 ! "E", - - receive - Msg -> - {messages, ["A", "B", "C", "D", "E"]} = Msg - end, - - receive P2 -> ok end, - - ok. - -%% -%% -%% helpers -%% -%% - -start_node(Config) -> - start_node(Config, []). -start_node(Config, Opts) when is_list(Config), is_list(Opts) -> - Pa = filename:dirname(code:which(?MODULE)), - Name = list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ atom_to_list(?config(testcase, Config)) - ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) - ++ "-" - ++ integer_to_list(erlang:unique_integer([positive]))), - ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). - -stop_node(Node) -> - ?t:stop_node(Node). diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 461957be10..f1cabe5d0b 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -157,7 +157,7 @@ static char *plusr_val_switches[] = { /* +x arguments with values */ static char *plusx_val_switches[] = { - "ohmq", + "mqd", NULL }; diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 6a9ec9c915..7a76c95c53 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2033,6 +2033,9 @@ open_port(_PortName,_PortSettings) -> -type priority_level() :: low | normal | high | max. +-type message_queue_data() :: + off_heap | on_heap | mixed. + -spec process_flag(trap_exit, Boolean) -> OldBoolean when Boolean :: boolean(), OldBoolean :: boolean(); @@ -2045,9 +2048,9 @@ open_port(_PortName,_PortSettings) -> (min_bin_vheap_size, MinBinVHeapSize) -> OldMinBinVHeapSize when MinBinVHeapSize :: non_neg_integer(), OldMinBinVHeapSize :: non_neg_integer(); - (off_heap_message_queue, OHMQ) -> OldOHMQ when - OHMQ :: boolean(), - OldOHMQ :: boolean(); + (message_queue_data, MQD) -> OldMQD when + MQD :: message_queue_data(), + OldMQD :: message_queue_data(); (priority, Level) -> OldLevel when Level :: priority_level(), OldLevel :: priority_level(); @@ -2086,7 +2089,7 @@ process_flag(_Flag, _Value) -> min_bin_vheap_size | monitored_by | monitors | - off_heap_message_queue | + message_queue_data | priority | reductions | registered_name | @@ -2128,7 +2131,7 @@ process_flag(_Flag, _Value) -> {monitors, Monitors :: [{process, Pid :: pid() | {RegName :: atom(), Node :: node()}}]} | - {off_heap_message_queue, OHMQ :: boolean()} | + {message_queue_data, MQD :: message_queue_data()} | {priority, Level :: priority_level()} | {reductions, Number :: non_neg_integer()} | {registered_name, Atom :: atom()} | @@ -2431,7 +2434,7 @@ tuple_to_list(_Tuple) -> (multi_scheduling) -> disabled | blocked | enabled; (multi_scheduling_blockers) -> [Pid :: pid()]; (nif_version) -> string(); - (off_heap_message_queue) -> boolean(); + (message_queue_data) -> message_queue_data(); (otp_release) -> string(); (os_monotonic_time_source) -> [{atom(),term()}]; (os_system_time_source) -> [{atom(),term()}]; @@ -2567,7 +2570,7 @@ spawn_monitor(M, F, A) -> | {fullsweep_after, Number :: non_neg_integer()} | {min_heap_size, Size :: non_neg_integer()} | {min_bin_vheap_size, VSize :: non_neg_integer()} - | {off_heap_message_queue, OHMQ :: boolean()}. + | {message_queue_data, MQD :: message_queue_data()}. -spec spawn_opt(Fun, Options) -> pid() | {pid(), reference()} when Fun :: function(), -- cgit v1.2.3 From cfecb31a2f6795dafb8858e39a3844bd301b84db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 8 Dec 2015 15:16:17 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 56328 -> 56060 bytes erts/preloaded/ebin/erlang.beam | Bin 101816 -> 101752 bytes erts/preloaded/ebin/erts_internal.beam | Bin 5988 -> 6076 bytes erts/preloaded/ebin/init.beam | Bin 48812 -> 48600 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1468 -> 1460 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1340 -> 1332 bytes erts/preloaded/ebin/prim_file.beam | Bin 44904 -> 44788 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72628 -> 72624 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23416 -> 23296 bytes erts/preloaded/ebin/zlib.beam | Bin 14176 -> 14168 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index df12c6f8e0..e94a1ba796 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 4f35928db2..632defdb46 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index d63f79c327..fd0a502d2c 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 73dfb3d351..60c08819eb 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 33c112f4de..04814c091b 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index ebca6e7eea..7779c8374d 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index e8817d183e..254b0e5b90 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 357bcd3d9a..b7cfe26462 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 969239be98..6b5c6195c8 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 281f668f8c..43d7b436be 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From ce22e0f430a98cc096b056066d427edbd2449a13 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 9 Dec 2015 08:53:31 +0100 Subject: erts: Correct the types section in The Abstract Format document Fixed a mistake in commit 23885a. --- erts/doc/src/absform.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index ca06794a53..186c9a1143 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -588,7 +588,7 @@ If C is a constraint is_subtype(V, T) or V :: T, where V is a type variable and T is a type, then - Rep(C) = {type,LINE,constraint,[Rep(F),[Rep(V),Rep(T)]]}. + Rep(C) = {type,LINE,constraint,[{atom,LINE,is_subtype},[Rep(V),Rep(T)]]}.
-- cgit v1.2.3 From 9805ea44b04b4d7db65fc6d6369addb223040032 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 9 Dec 2015 10:20:38 +0100 Subject: Fix check_process_code() --- erts/emulator/beam/beam_bif_load.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index c3ebf71a01..8e6123eacd 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -837,10 +837,10 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) if (check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size)) return am_true; /* Should not contain any constants... */ - ASSERT(!any_heap_ref_ptrs(&hfrag->mem[0], - &hfrag->mem[hfrag->used_size], - mod_start, - mod_size)); + ASSERT(!any_heap_refs(&hfrag->mem[0], + &hfrag->mem[hfrag->used_size], + mod_start, + mod_size)); } } @@ -881,7 +881,7 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) hp = &hfrag->mem[0]; hp_end = &hfrag->mem[hfrag->used_size]; - if (any_heap_ref_ptrs(hp, hp_end, mod_start, lit_bsize)) + if (any_heap_refs(hp, hp_end, mod_start, lit_bsize)) goto try_literal_gc; } @@ -902,7 +902,7 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) hp = &hfrag->mem[0]; hp_end = &hfrag->mem[hfrag->used_size]; - ASSERT(!any_heap_ref_ptrs(hp, hp_end, mod_start, lit_bsize)); + ASSERT(!any_heap_refs(hp, hp_end, mod_start, lit_bsize)); } } -- cgit v1.2.3 From 343f461a2810ce3440b1d7c55cda996038071d87 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 8 Dec 2015 18:45:32 +0100 Subject: erts: Optimize hashing in process dictionary by limiting table sizes to powers of 2. This will change the default size from 10 to 8. --- erts/emulator/beam/erl_init.c | 4 ++-- erts/emulator/beam/erl_process_dict.c | 21 +++++++++++++++++++-- erts/emulator/beam/erl_process_dict.h | 1 + 3 files changed, 22 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index d9c3b0dcf4..0a34d91a01 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -197,7 +197,7 @@ Uint32 verbose; /* See erl_debug.h for information about verbose */ int erts_atom_table_size = ATOM_LIMIT; /* Maximum number of atoms */ -int erts_pd_initial_size = 10; +int erts_pd_initial_size = 8; /* must be power of 2 */ int erts_modified_timing_level; @@ -1479,7 +1479,7 @@ erl_start(int argc, char **argv) VERBOSE(DEBUG_SYSTEM, ("using minimum heap size %d\n", H_MIN_SIZE)); } else if (has_prefix("pds", sub_param)) { arg = get_arg(sub_param+3, argv[i+1], &i); - if ((erts_pd_initial_size = atoi(arg)) <= 0) { + if (!erts_pd_set_initial_size(atoi(arg))) { erts_fprintf(stderr, "bad initial process dictionary size %s\n", arg); erts_usage(); } diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index e497267b63..c56a722542 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -80,6 +80,8 @@ #define ARRAY_GET(PDict, Index) (((PDict)->size > (Index)) ? \ (PDict)->data[Index] : NIL) +#define IS_POW2(X) ((X) && !((X) & ((X)-1))) + /* * Forward decalarations */ @@ -138,6 +140,16 @@ static void pd_check(ProcDict *pd); ** External interface */ +int +erts_pd_set_initial_size(int size) +{ + if (size <= 0) + return 0; + + erts_pd_initial_size = 1 << erts_fit_in_bits_uint(size-1); + return 1; +} + /* * Called from break handler */ @@ -399,6 +411,7 @@ Eterm erts_pd_hash_get_with_hx(Process *p, Uint32 hx, Eterm id) unsigned int hval; ProcDict *pd = p->dictionary; + ASSERT(hx == MAKE_HASH(id)); if (pd == NULL) return am_undefined; hval = pd_hash_value_to_ix(pd, hx); @@ -570,6 +583,7 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) /* Create it */ array_put(&(p->dictionary), INITIAL_SIZE - 1, NIL); p->dictionary->homeSize = INITIAL_SIZE; + ASSERT(IS_POW2(p->dictionary->homeSize)); } hval = pd_hash_value(p->dictionary, id); old = ARRAY_GET(p->dictionary, hval); @@ -954,9 +968,12 @@ static Eterm array_put(ProcDict **ppdict, unsigned int ndx, Eterm term) static unsigned int pd_hash_value_to_ix(ProcDict *pdict, Uint32 hx) { Uint high; - high = hx % (pdict->homeSize*2); + + ASSERT(IS_POW2(pdict->homeSize)); + + high = hx & (pdict->homeSize*2 - 1); if (high >= HASH_RANGE(pdict)) - return hx % pdict->homeSize; + return hx & (pdict->homeSize - 1); return high; } diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h index 9aa21b7c38..ab50d45c63 100644 --- a/erts/emulator/beam/erl_process_dict.h +++ b/erts/emulator/beam/erl_process_dict.h @@ -31,6 +31,7 @@ typedef struct proc_dict { Eterm data[1]; /* The beginning of an array of erlang terms */ } ProcDict; +int erts_pd_set_initial_size(int size); Uint erts_dicts_mem_size(struct process *p); void erts_erase_dicts(struct process *p); void erts_dictionary_dump(int to, void *to_arg, ProcDict *pd); -- cgit v1.2.3 From eb2e118ee2be5c008fc4417a443c351807adf123 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 8 Dec 2015 20:28:38 +0100 Subject: erts: Optimize away function "array_put" in proc dict Replace heave array_put() with a dumb array index assignment ARRAY_PUT and instead introduce ensure_array_size() to be called when we know the array might need to grow. This change also ensures the entire HASH_RANGE is always allocated. No need for ARRAY_GET to check index any more. --- erts/emulator/beam/erl_process_dict.c | 104 ++++++++++++++++------------------ 1 file changed, 48 insertions(+), 56 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index c56a722542..0934183b6a 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -77,8 +77,10 @@ #define TCDR(Term) CDR(list_val(Term)) /* Array access macro */ -#define ARRAY_GET(PDict, Index) (((PDict)->size > (Index)) ? \ - (PDict)->data[Index] : NIL) +#define ARRAY_GET(PDict, Index) (ASSERT((Index) < (PDict)->size), \ + (PDict)->data[Index]) +#define ARRAY_PUT(PDict, Index, Val) (ASSERT((Index) < (PDict)->size), \ + (PDict)->data[Index] = (Val)) #define IS_POW2(X) ((X) && !((X) & ((X)-1))) @@ -97,7 +99,7 @@ static void shrink(Process *p, Eterm* ret); static void grow(Process *p); static void array_shrink(ProcDict **ppd, unsigned int need); -static Eterm array_put(ProcDict **ppdict, unsigned int ndx, Eterm term); +static void ensure_array_size(ProcDict**, unsigned int size); static unsigned int pd_hash_value_to_ix(ProcDict *pdict, Uint32 hx); static unsigned int next_array_size(unsigned int need); @@ -361,7 +363,7 @@ static void pd_hash_erase(Process *p, Eterm id, Eterm *ret) if (is_boxed(old)) { /* Tuple */ ASSERT(is_tuple(old)); if (EQ(tuple_val(old)[1], id)) { - array_put(&(p->dictionary), hval, NIL); + ARRAY_PUT(p->dictionary, hval, NIL); --(p->dictionary->numElements); *ret = tuple_val(old)[2]; } @@ -381,7 +383,7 @@ static void pd_hash_erase(Process *p, Eterm id, Eterm *ret) old = ARRAY_GET(p->dictionary, hval); ASSERT(is_list(old)); if (is_nil(TCDR(old))) { - array_put(&p->dictionary, hval, TCAR(old)); + ARRAY_PUT(p->dictionary, hval, TCAR(old)); } } else if (is_not_nil(old)) { #ifdef DEBUG @@ -581,9 +583,8 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) if (p->dictionary == NULL) { /* Create it */ - array_put(&(p->dictionary), INITIAL_SIZE - 1, NIL); + ensure_array_size(&p->dictionary, INITIAL_SIZE); p->dictionary->homeSize = INITIAL_SIZE; - ASSERT(IS_POW2(p->dictionary->homeSize)); } hval = pd_hash_value(p->dictionary, id); old = ARRAY_GET(p->dictionary, hval); @@ -635,19 +636,19 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) * Update the dictionary. */ if (is_nil(old)) { - array_put(&(p->dictionary), hval, tpl); + ARRAY_PUT(p->dictionary, hval, tpl); ++(p->dictionary->numElements); } else if (is_boxed(old)) { ASSERT(is_tuple(old)); if (EQ(tuple_val(old)[1],id)) { - array_put(&(p->dictionary), hval, tpl); + ARRAY_PUT(p->dictionary, hval, tpl); return tuple_val(old)[2]; } else { hp = HeapOnlyAlloc(p, 4); tmp = CONS(hp, old, NIL); hp += 2; ++(p->dictionary->numElements); - array_put(&(p->dictionary), hval, CONS(hp, tpl, tmp)); + ARRAY_PUT(p->dictionary, hval, CONS(hp, tpl, tmp)); hp += 2; ASSERT(hp <= hp_limit); } @@ -657,7 +658,7 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) * New key. Simply prepend the tuple to the beginning of the list. */ hp = HeapOnlyAlloc(p, 2); - array_put(&(p->dictionary), hval, CONS(hp, tpl, old)); + ARRAY_PUT(p->dictionary, hval, CONS(hp, tpl, old)); hp += 2; ASSERT(hp <= hp_limit); ++(p->dictionary->numElements); @@ -692,7 +693,7 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) nlist = CONS(hp, tpl, nlist); hp += 2; ASSERT(hp <= hp_limit); - array_put(&(p->dictionary), hval, nlist); + ARRAY_PUT(p->dictionary, hval, nlist); return tuple_val(TCAR(tmp))[2]; } } else { @@ -741,7 +742,7 @@ static void shrink(Process *p, Eterm* ret) lo = ARRAY_GET(pd, pd->splitPosition); if (hi != NIL) { if (lo == NIL) { - array_put(&(p->dictionary), pd->splitPosition, hi); + ARRAY_PUT(p->dictionary, pd->splitPosition, hi); } else { int needed = 4; if (is_list(hi) && is_list(lo)) { @@ -760,13 +761,13 @@ static void shrink(Process *p, Eterm* ret) hp = HeapOnlyAlloc(p, 4); tmp = CONS(hp, hi, NIL); hp += 2; - array_put(&(p->dictionary), pd->splitPosition, + ARRAY_PUT(p->dictionary, pd->splitPosition, CONS(hp,lo,tmp)); hp += 2; ASSERT(hp <= hp_limit); } else { /* hi is a list */ hp = HeapOnlyAlloc(p, 2); - array_put(&(p->dictionary), pd->splitPosition, + ARRAY_PUT(p->dictionary, pd->splitPosition, CONS(hp, lo, hi)); hp += 2; ASSERT(hp <= hp_limit); @@ -774,7 +775,7 @@ static void shrink(Process *p, Eterm* ret) } else { /* lo is a list */ if (is_tuple(hi)) { hp = HeapOnlyAlloc(p, 2); - array_put(&(p->dictionary), pd->splitPosition, + ARRAY_PUT(p->dictionary, pd->splitPosition, CONS(hp, hi, lo)); hp += 2; ASSERT(hp <= hp_limit); @@ -786,12 +787,12 @@ static void shrink(Process *p, Eterm* ret) hp += 2; } ASSERT(hp <= hp_limit); - array_put(&(p->dictionary), pd->splitPosition, lo); + ARRAY_PUT(p->dictionary, pd->splitPosition, lo); } } } } - array_put(&(p->dictionary), (pd->splitPosition + pd->homeSize), NIL); + ARRAY_PUT(p->dictionary, (pd->splitPosition + pd->homeSize), NIL); } if (HASH_RANGE(p->dictionary) <= (p->dictionary->size / 4)) { array_shrink(&(p->dictionary), (HASH_RANGE(p->dictionary) * 3) / 2); @@ -808,7 +809,7 @@ static void grow(Process *p) unsigned int pos; unsigned int homeSize; int needed = 0; - ProcDict *pd; + ProcDict *pd = p->dictionary; #ifdef DEBUG Eterm *hp_limit; #endif @@ -817,16 +818,18 @@ static void grow(Process *p) if (steps == 0) steps = 1; /* Dont grow over MAX_HASH */ - if ((MAX_HASH - steps) <= HASH_RANGE(p->dictionary)) { + if ((MAX_HASH - steps) <= HASH_RANGE(pd)) { return; } + ensure_array_size(&p->dictionary, HASH_RANGE(pd) + steps); + pd = p->dictionary; + /* * Calculate total number of heap words needed, and garbage collect * if necessary. */ - pd = p->dictionary; pos = pd->splitPosition; homeSize = pd->homeSize; for (i = 0; i < steps; ++i) { @@ -855,7 +858,6 @@ static void grow(Process *p) */ for (i = 0; i < steps; ++i) { - ProcDict *pd = p->dictionary; if (pd->splitPosition == pd->homeSize) { pd->homeSize *= 2; pd->splitPosition = 0; @@ -865,9 +867,8 @@ static void grow(Process *p) l = ARRAY_GET(pd, pos); if (is_tuple(l)) { if (pd_hash_value(pd, tuple_val(l)[1]) != pos) { - array_put(&(p->dictionary), pos + - p->dictionary->homeSize, l); - array_put(&(p->dictionary), pos, NIL); + ARRAY_PUT(pd, pos + pd->homeSize, l); + ARRAY_PUT(pd, pos, NIL); } } else { l2 = NIL; @@ -889,10 +890,8 @@ static void grow(Process *p) if (l2 != NIL && TCDR(l2) == NIL) l2 = TCAR(l2); ASSERT(hp <= hp_limit); - /* After array_put pd is no longer valid */ - array_put(&(p->dictionary), pos, l1); - array_put(&(p->dictionary), pos + - p->dictionary->homeSize, l2); + ARRAY_PUT(pd, pos, l1); + ARRAY_PUT(pd, pos + pd->homeSize, l2); } } @@ -912,7 +911,7 @@ static void array_shrink(ProcDict **ppd, unsigned int need) HDEBUGF(("array_shrink: size = %d, used = %d, need = %d", (*ppd)->size, (*ppd)->used, need)); - if (siz > (*ppd)->size) + if (siz >= (*ppd)->size) return; /* Only shrink */ *ppd = PD_REALLOC(((void *) *ppd), @@ -925,40 +924,33 @@ static void array_shrink(ProcDict **ppd, unsigned int need) } -static Eterm array_put(ProcDict **ppdict, unsigned int ndx, Eterm term) +static void ensure_array_size(ProcDict **ppdict, unsigned int size) { + ProcDict *pd = *ppdict; unsigned int i; - Eterm ret; - if (*ppdict == NULL) { - Uint siz = next_array_size(ndx+1); - ProcDict *p; - p = PD_ALLOC(PD_SZ2BYTES(siz)); + if (pd == NULL) { + Uint siz = next_array_size(size); + + pd = PD_ALLOC(PD_SZ2BYTES(siz)); for (i = 0; i < siz; ++i) - p->data[i] = NIL; - p->size = siz; - p->homeSize = p->splitPosition = p->numElements = p->used = 0; - *ppdict = p; - } else if (ndx >= (*ppdict)->size) { - Uint osize = (*ppdict)->size; - Uint nsize = next_array_size(ndx+1); - *ppdict = PD_REALLOC(((void *) *ppdict), + pd->data[i] = NIL; + pd->size = siz; + pd->homeSize = pd->splitPosition = pd->numElements = pd->used = 0; + *ppdict = pd; + } else if (size > pd->size) { + Uint osize = pd->size; + Uint nsize = next_array_size(size); + pd = PD_REALLOC(((void *) pd), PD_SZ2BYTES(osize), PD_SZ2BYTES(nsize)); for (i = osize; i < nsize; ++i) - (*ppdict)->data[i] = NIL; - (*ppdict)->size = nsize; + pd->data[i] = NIL; + pd->size = nsize; + *ppdict = pd; } - ret = (*ppdict)->data[ndx]; - (*ppdict)->data[ndx] = term; - if ((ndx + 1) > (*ppdict)->used) - (*ppdict)->used = ndx + 1; -#ifdef HARDDEBUG - HDEBUGF(("array_put: (*ppdict)->size = %d, (*ppdict)->used = %d, ndx = %d", - (*ppdict)->size, (*ppdict)->used, ndx)); - erts_fprintf(stderr, "%T", term); -#endif /* HARDDEBUG */ - return ret; + if (size > pd->used) + pd->used = size; } /* -- cgit v1.2.3 From bb5f71a7573158056dd9c80228c95833f970ec0b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 9 Dec 2015 19:28:15 +0100 Subject: erts: Add proc dict macros ERTS_PD_START/SIZE --- erts/emulator/beam/beam_bif_load.c | 4 ++-- erts/emulator/beam/erl_debug.c | 6 +++--- erts/emulator/beam/erl_gc.c | 10 +++++----- erts/emulator/beam/erl_process_dict.h | 3 +++ 4 files changed, 13 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 0e192b1ebd..c804a09f87 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -851,8 +851,8 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) } if (rp->dictionary != NULL) { - Eterm* start = rp->dictionary->data; - Eterm* end = start + rp->dictionary->used; + Eterm* start = ERTS_PD_START(rp->dictionary); + Eterm* end = start + ERTS_PD_SIZE(rp->dictionary); if (any_heap_ref_ptrs(start, end, mod_start, mod_size)) { goto need_gc; diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index 2dcfb79f00..a2af3adf70 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -416,7 +416,7 @@ void verify_process(Process *p) erts_check_heap(p); if (p->dictionary) - VERIFY_AREA("dictionary",p->dictionary->data, p->dictionary->used); + VERIFY_AREA("dictionary", ERTS_PD_START(p->dictionary), ERTS_PD_SIZE(p->dictionary)); VERIFY_ETERM("seq trace token",p->seq_trace_token); VERIFY_ETERM("group leader",p->group_leader); VERIFY_ETERM("fvalue",p->fvalue); @@ -542,8 +542,8 @@ static void print_process_memory(Process *p) } if (p->dictionary != NULL) { - int n = p->dictionary->used; - Eterm *ptr = p->dictionary->data; + int n = ERTS_PD_SIZE(p->dictionary); + Eterm *ptr = ERTS_PD_START(p->dictionary); erts_printf(" Dictionary: "); while (n--) erts_printf("0x%0*lx ",PTR_SIZE,(unsigned long)ptr++); erts_printf("\n"); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index d2604f1595..b743b7e8f6 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1953,7 +1953,7 @@ collect_heap_frags(Process* p, Eterm* n_hstart, Eterm* n_htop, #ifdef HARDDEBUG disallow_heap_frag_ref(p, n_htop, p->stop, STACK_START(p) - p->stop); if (p->dictionary != NULL) { - disallow_heap_frag_ref(p, n_htop, p->dictionary->data, p->dictionary->used); + disallow_heap_frag_ref(p, n_htop, ERTS_PD_START(p->dictionary), ERTS_PD_SIZE(p->dictionary)); } disallow_heap_frag_ref_in_heap(p); #endif @@ -1993,8 +1993,8 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) ++n; if (p->dictionary != NULL) { - roots[n].v = p->dictionary->data; - roots[n].sz = p->dictionary->used; + roots[n].v = ERTS_PD_START(p->dictionary); + roots[n].sz = ERTS_PD_SIZE(p->dictionary); ++n; } if (nobj > 0) { @@ -2589,8 +2589,8 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size, Eterm* objv, int nobj) { if (p->dictionary) { - offset_heap(p->dictionary->data, - p->dictionary->used, + offset_heap(ERTS_PD_START(p->dictionary), + ERTS_PD_SIZE(p->dictionary), offs, area, area_size); } diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h index ab50d45c63..3ad070d914 100644 --- a/erts/emulator/beam/erl_process_dict.h +++ b/erts/emulator/beam/erl_process_dict.h @@ -31,6 +31,9 @@ typedef struct proc_dict { Eterm data[1]; /* The beginning of an array of erlang terms */ } ProcDict; +#define ERTS_PD_START(PD) ((PD)->data) +#define ERTS_PD_SIZE(PD) ((PD)->used) + int erts_pd_set_initial_size(int size); Uint erts_dicts_mem_size(struct process *p); void erts_erase_dicts(struct process *p); -- cgit v1.2.3 From 4179ca9f10cdc78e882ce4496cf0a1261a0129af Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 9 Dec 2015 19:34:45 +0100 Subject: erts: Remove ProcDict.used (homeSize + splitPosition) will do just fine --- erts/emulator/beam/erl_process_dict.c | 20 +++++++++----------- erts/emulator/beam/erl_process_dict.h | 3 +-- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 0934183b6a..34c1f0c993 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -164,9 +164,9 @@ erts_dictionary_dump(int to, void *to_arg, ProcDict *pd) /*PD_CHECK(pd);*/ if (pd == NULL) return; - erts_print(to, to_arg, "(size = %d, used = %d, homeSize = %d, " + erts_print(to, to_arg, "(size = %d, homeSize = %d, " "splitPosition = %d, numElements = %d)\n", - pd->size, pd->used, pd->homeSize, + pd->size, pd->homeSize, pd->splitPosition, (unsigned int) pd->numElements); for (i = 0; i < HASH_RANGE(pd); ++i) { erts_print(to, to_arg, "%d: %T\n", i, ARRAY_GET(pd, i)); @@ -908,8 +908,8 @@ static void array_shrink(ProcDict **ppd, unsigned int need) { unsigned int siz = next_array_size(need); - HDEBUGF(("array_shrink: size = %d, used = %d, need = %d", - (*ppd)->size, (*ppd)->used, need)); + HDEBUGF(("array_shrink: size = %d, need = %d", + (*ppd)->size, need)); if (siz >= (*ppd)->size) return; /* Only shrink */ @@ -919,8 +919,6 @@ static void array_shrink(ProcDict **ppd, unsigned int need) PD_SZ2BYTES(siz)); (*ppd)->size = siz; - if ((*ppd)->size < (*ppd)->used) - (*ppd)->used = (*ppd)->size; } @@ -936,7 +934,7 @@ static void ensure_array_size(ProcDict **ppdict, unsigned int size) for (i = 0; i < siz; ++i) pd->data[i] = NIL; pd->size = siz; - pd->homeSize = pd->splitPosition = pd->numElements = pd->used = 0; + pd->homeSize = pd->splitPosition = pd->numElements = 0; *ppdict = pd; } else if (size > pd->size) { Uint osize = pd->size; @@ -949,8 +947,6 @@ static void ensure_array_size(ProcDict **ppdict, unsigned int size) pd->size = nsize; *ppdict = pd; } - if (size > pd->used) - pd->used = size; } /* @@ -1029,12 +1025,14 @@ static unsigned int next_array_size(unsigned int need) static void pd_check(ProcDict *pd) { unsigned int i; + unsigned int used; Uint num; if (pd == NULL) return; - ASSERT(pd->size >= pd->used); + used = HASH_RANGE(pd); + ASSERT(pd->size >= used); ASSERT(HASH_RANGE(pd) <= MAX_HASH); - for (i = 0, num = 0; i < pd->used; ++i) { + for (i = 0, num = 0; i < used; ++i) { Eterm t = pd->data[i]; if (is_nil(t)) { continue; diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h index 3ad070d914..eb26a1ffcc 100644 --- a/erts/emulator/beam/erl_process_dict.h +++ b/erts/emulator/beam/erl_process_dict.h @@ -24,7 +24,6 @@ typedef struct proc_dict { unsigned int size; - unsigned int used; unsigned int homeSize; unsigned int splitPosition; Uint numElements; @@ -32,7 +31,7 @@ typedef struct proc_dict { } ProcDict; #define ERTS_PD_START(PD) ((PD)->data) -#define ERTS_PD_SIZE(PD) ((PD)->used) +#define ERTS_PD_SIZE(PD) ((PD)->homeSize + (PD)->splitPosition) int erts_pd_set_initial_size(int size); Uint erts_dicts_mem_size(struct process *p); -- cgit v1.2.3 From 4804ea2aa50af490ab3998466269efa540abd90d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 10 Dec 2015 15:25:22 +0100 Subject: erts: Add sizeMask for faster proc dict indexing --- erts/emulator/beam/erl_process_dict.c | 26 +++++++++++++++----------- erts/emulator/beam/erl_process_dict.h | 1 + 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 34c1f0c993..97a2828b4e 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -585,6 +585,9 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) /* Create it */ ensure_array_size(&p->dictionary, INITIAL_SIZE); p->dictionary->homeSize = INITIAL_SIZE; + p->dictionary->sizeMask = p->dictionary->homeSize*2 - 1; + p->dictionary->splitPosition = 0; + p->dictionary->numElements = 0; } hval = pd_hash_value(p->dictionary, id); old = ARRAY_GET(p->dictionary, hval); @@ -718,6 +721,7 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) static void shrink(Process *p, Eterm* ret) { + ProcDict *pd = p->dictionary; unsigned int range = HASH_RANGE(p->dictionary); unsigned int steps = (range*3) / 10; Eterm hi, lo, tmp; @@ -732,8 +736,8 @@ static void shrink(Process *p, Eterm* ret) } for (i = 0; i < steps; ++i) { - ProcDict *pd = p->dictionary; if (pd->splitPosition == 0) { + pd->sizeMask = pd->homeSize - 1; pd->homeSize /= 2; pd->splitPosition = pd->homeSize; } @@ -742,7 +746,7 @@ static void shrink(Process *p, Eterm* ret) lo = ARRAY_GET(pd, pd->splitPosition); if (hi != NIL) { if (lo == NIL) { - ARRAY_PUT(p->dictionary, pd->splitPosition, hi); + ARRAY_PUT(pd, pd->splitPosition, hi); } else { int needed = 4; if (is_list(hi) && is_list(lo)) { @@ -761,13 +765,13 @@ static void shrink(Process *p, Eterm* ret) hp = HeapOnlyAlloc(p, 4); tmp = CONS(hp, hi, NIL); hp += 2; - ARRAY_PUT(p->dictionary, pd->splitPosition, + ARRAY_PUT(pd, pd->splitPosition, CONS(hp,lo,tmp)); hp += 2; ASSERT(hp <= hp_limit); } else { /* hi is a list */ hp = HeapOnlyAlloc(p, 2); - ARRAY_PUT(p->dictionary, pd->splitPosition, + ARRAY_PUT(pd, pd->splitPosition, CONS(hp, lo, hi)); hp += 2; ASSERT(hp <= hp_limit); @@ -775,7 +779,7 @@ static void shrink(Process *p, Eterm* ret) } else { /* lo is a list */ if (is_tuple(hi)) { hp = HeapOnlyAlloc(p, 2); - ARRAY_PUT(p->dictionary, pd->splitPosition, + ARRAY_PUT(pd, pd->splitPosition, CONS(hp, hi, lo)); hp += 2; ASSERT(hp <= hp_limit); @@ -787,12 +791,12 @@ static void shrink(Process *p, Eterm* ret) hp += 2; } ASSERT(hp <= hp_limit); - ARRAY_PUT(p->dictionary, pd->splitPosition, lo); + ARRAY_PUT(pd, pd->splitPosition, lo); } } } } - ARRAY_PUT(p->dictionary, (pd->splitPosition + pd->homeSize), NIL); + ARRAY_PUT(pd, (pd->splitPosition + pd->homeSize), NIL); } if (HASH_RANGE(p->dictionary) <= (p->dictionary->size / 4)) { array_shrink(&(p->dictionary), (HASH_RANGE(p->dictionary) * 3) / 2); @@ -860,7 +864,8 @@ static void grow(Process *p) for (i = 0; i < steps; ++i) { if (pd->splitPosition == pd->homeSize) { pd->homeSize *= 2; - pd->splitPosition = 0; + pd->sizeMask = pd->homeSize*2 - 1; + pd->splitPosition = 0; } pos = pd->splitPosition; ++pd->splitPosition; /* For the hashes */ @@ -934,7 +939,6 @@ static void ensure_array_size(ProcDict **ppdict, unsigned int size) for (i = 0; i < siz; ++i) pd->data[i] = NIL; pd->size = siz; - pd->homeSize = pd->splitPosition = pd->numElements = 0; *ppdict = pd; } else if (size > pd->size) { Uint osize = pd->size; @@ -959,9 +963,9 @@ static unsigned int pd_hash_value_to_ix(ProcDict *pdict, Uint32 hx) ASSERT(IS_POW2(pdict->homeSize)); - high = hx & (pdict->homeSize*2 - 1); + high = hx & pdict->sizeMask; if (high >= HASH_RANGE(pdict)) - return hx & (pdict->homeSize - 1); + return hx & (pdict->sizeMask >> 1); return high; } diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h index eb26a1ffcc..fd59c969cf 100644 --- a/erts/emulator/beam/erl_process_dict.h +++ b/erts/emulator/beam/erl_process_dict.h @@ -23,6 +23,7 @@ #include "sys.h" typedef struct proc_dict { + unsigned int sizeMask; unsigned int size; unsigned int homeSize; unsigned int splitPosition; -- cgit v1.2.3 From efa7a08d1b16aeb280c87f035a97d31ca6eebba6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 11 Dec 2015 14:17:57 +0100 Subject: erts: Fix faulty cleanup when receiving broken dist msg Bug introduced in ce8279d6a48d41f9. Thank you valgrind. --- erts/emulator/beam/dist.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 170690ca89..7be2b77a3b 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -1231,11 +1231,11 @@ int erts_net_message(Port *prt, arg = erts_decode_dist_ext(&factory, &ede); if (is_non_value(arg)) { #ifdef ERTS_DIST_MSG_DBG - erts_fprintf(stderr, "DIST MSG DEBUG: erts_dist_ext_size(CTL) failed:\n"); + erts_fprintf(stderr, "DIST MSG DEBUG: erts_decode_dist_ext(CTL) failed:\n"); bw(buf, orig_len); #endif PURIFY_MSG("data error"); - goto data_error; + goto decode_error; } ctl_len = t - buf; @@ -1728,12 +1728,13 @@ int erts_net_message(Port *prt, erts_dsprintf(dsbufp, "Invalid distribution message: %.200T", arg); erts_send_error_to_logger_nogl(dsbufp); } - data_error: +decode_error: PURIFY_MSG("data error"); erts_factory_close(&factory); if (ctl != ctl_default) { erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl); } +data_error: UnUseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE); erts_deliver_port_exit(prt, dep->cid, am_killed, 0); ERTS_SMP_CHK_NO_PROC_LOCKS; -- cgit v1.2.3 From 71ddd8c1aba0478fe5aa07bdc8f9e6a86515bb11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 9 Dec 2015 15:04:28 +0100 Subject: Emulator test suite: Replace use of 'random' with 'rand' --- erts/emulator/test/binary_SUITE.erl | 39 ++++++++++++------------ erts/emulator/test/bs_bincomp_SUITE.erl | 2 +- erts/emulator/test/decode_packet_SUITE.erl | 47 ++++++++++++++--------------- erts/emulator/test/driver_SUITE.erl | 15 +++------ erts/emulator/test/evil_SUITE.erl | 4 +-- erts/emulator/test/hash_SUITE.erl | 10 +++--- erts/emulator/test/map_SUITE.erl | 27 ++++++++--------- erts/emulator/test/nif_SUITE.erl | 19 +++++------- erts/emulator/test/op_SUITE.erl | 23 +++++++------- erts/emulator/test/port_SUITE.erl | 15 +++++---- erts/emulator/test/port_bif_SUITE.erl | 9 +----- erts/emulator/test/random_iolist.erl | 16 +++++----- erts/emulator/test/save_calls_SUITE.erl | 4 +-- erts/emulator/test/system_profile_SUITE.erl | 2 +- erts/emulator/test/time_SUITE.erl | 36 ++++++++++------------ erts/emulator/test/trace_SUITE.erl | 4 +-- 16 files changed, 121 insertions(+), 151 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 96ba2f64d4..f8f71efecc 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -521,30 +521,29 @@ external_size_1(Term, Size0, Limit) when Size0 < Limit -> external_size_1(_, _, _) -> ok. t_iolist_size(Config) when is_list(Config) -> - ?line Seed = {erlang:monotonic_time(), - erlang:time_offset(), - erlang:unique_integer([positive])}, - ?line io:format("Seed: ~p", [Seed]), - ?line random:seed(Seed), - ?line Base = <<0:(1 bsl 20)/unit:8>>, - ?line Powers = [1 bsl N || N <- lists:seq(2, 37)], - ?line Sizes0 = [[N - random:uniform(N div 2), - lists:seq(N-2, N+2), - N+N div 2, - N + random:uniform(N div 2)] || - N <- Powers], + _ = rand:uniform(), %Seed generator + io:format("Seed: ~p", [rand:export_seed()]), + + Base = <<0:(1 bsl 20)/unit:8>>, + Powers = [1 bsl N || N <- lists:seq(2, 37)], + Sizes0 = [[N - rand:uniform(N div 2), + lists:seq(N-2, N+2), + N+N div 2, + N + rand:uniform(N div 2)] || + N <- Powers], + %% Test sizes around 1^32 more thoroughly. FourGigs = 1 bsl 32, - ?line Sizes1 = [FourGigs+N || N <- lists:seq(-8, 40)] ++ Sizes0, - ?line Sizes2 = lists:flatten(Sizes1), - ?line Sizes = lists:usort(Sizes2), + Sizes1 = [FourGigs+N || N <- lists:seq(-8, 40)] ++ Sizes0, + Sizes2 = lists:flatten(Sizes1), + Sizes = lists:usort(Sizes2), io:format("~p sizes:", [length(Sizes)]), io:format("~p\n", [Sizes]), - ?line [Sz = iolist_size(build_iolist(Sz, Base)) || Sz <- Sizes], + _ = [Sz = iolist_size(build_iolist(Sz, Base)) || Sz <- Sizes], ok. build_iolist(N, Base) when N < 16 -> - case random:uniform(3) of + case rand:uniform(3) of 1 -> <> = Base, Bin; @@ -552,7 +551,7 @@ build_iolist(N, Base) when N < 16 -> lists:seq(1, N) end; build_iolist(N, Base) when N =< byte_size(Base) -> - case random:uniform(3) of + case rand:uniform(3) of 1 -> <> = Base, Bin; @@ -570,7 +569,7 @@ build_iolist(N, Base) when N =< byte_size(Base) -> end end; build_iolist(N0, Base) -> - Small = random:uniform(15), + Small = rand:uniform(15), Seq = lists:seq(1, Small), N = N0 - Small, case N rem 2 of @@ -1604,7 +1603,7 @@ bit_sized_binary(Bin0) -> unaligned_sub_bin(Bin, 0) -> Bin; unaligned_sub_bin(Bin0, Offs) -> - F = random:uniform(256), + F = rand:uniform(256), Roffs = 8-Offs, Bin1 = <>, Sz = size(Bin0), diff --git a/erts/emulator/test/bs_bincomp_SUITE.erl b/erts/emulator/test/bs_bincomp_SUITE.erl index dcd13c19df..8836fe40ae 100644 --- a/erts/emulator/test/bs_bincomp_SUITE.erl +++ b/erts/emulator/test/bs_bincomp_SUITE.erl @@ -131,7 +131,7 @@ tracing(Config) when is_list(Config) -> random_binary() -> Seq = [1,2,3,4,5,6,7,8,9,10], - << <<($a + random:uniform($z - $a)):8>> || _ <- Seq >>. + << <<($a + rand:uniform($z - $a)):8>> || _ <- Seq >>. random_binaries(N) when N > 0 -> random_binary(), diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl index 6a5ca20ac3..65ae94d0dc 100644 --- a/erts/emulator/test/decode_packet_SUITE.erl +++ b/erts/emulator/test/decode_packet_SUITE.erl @@ -53,11 +53,8 @@ end_per_group(_GroupName, Config) -> init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Seed = {S1,S2,S3} = {erlang:monotonic_time(), - erlang:time_offset(), - erlang:unique_integer()}, - random:seed(S1,S2,S3), - io:format("*** SEED: ~p ***\n", [Seed]), + rand:seed(exsplus), + io:format("*** SEED: ~p ***\n", [rand:export_seed()]), Dog=?t:timetrap(?t:minutes(1)), [{watchdog, Dog}|Config]. @@ -136,7 +133,7 @@ pack(Type,Body,Rest,BitOffs) -> {Packet,Unpacked} = pack(Type,Body), %% Make Bin a sub-bin with an arbitrary bitoffset within Orig - Prefix = random:uniform(1 bsl BitOffs) - 1, + Prefix = rand:uniform(1 bsl BitOffs) - 1, Orig = <>, <<_:BitOffs,Bin/bits>> = Orig, {Bin,Unpacked,Orig}. @@ -151,13 +148,13 @@ pack(4,Bin) -> Psz = byte_size(Bin), {<>, Bin}; pack(asn1,Bin) -> - Ident = case random:uniform(3) of + Ident = case rand:uniform(3) of 1 -> <<17>>; 2 -> <<16#1f,16#81,17>>; 3 -> <<16#1f,16#81,16#80,16#80,17>> end, Psz = byte_size(Bin), - Length = case random:uniform(4) of + Length = case rand:uniform(4) of 1 when Psz < 128 -> <>; R when R=<2 andalso Psz < 16#10000 -> @@ -177,42 +174,42 @@ pack(sunrm,Bin) -> {Res,Res}; pack(cdr,Bin) -> GIOP = <<"GIOP">>, - Major = random:uniform(256) - 1, - Minor = random:uniform(256) - 1, - MType = random:uniform(256) - 1, + Major = rand:uniform(256) - 1, + Minor = rand:uniform(256) - 1, + MType = rand:uniform(256) - 1, Psz = byte_size(Bin), - Res = case random:uniform(2) of + Res = case rand:uniform(2) of 1 -> <>; 2 -> <> end, {Res,Res}; pack(fcgi,Bin) -> Ver = 1, - Type = random:uniform(256) - 1, - Id = random:uniform(65536) - 1, - PaddSz = random:uniform(16) - 1, + Type = rand:uniform(256) - 1, + Id = rand:uniform(65536) - 1, + PaddSz = rand:uniform(16) - 1, Psz = byte_size(Bin), - Reserv = random:uniform(256) - 1, + Reserv = rand:uniform(256) - 1, Padd = case PaddSz of 0 -> <<>>; - _ -> list_to_binary([random:uniform(256)-1 + _ -> list_to_binary([rand:uniform(256)-1 || _<- lists:seq(1,PaddSz)]) end, Res = <>, {<>, Res}; pack(tpkt,Bin) -> Ver = 3, - Reserv = random:uniform(256) - 1, + Reserv = rand:uniform(256) - 1, Size = byte_size(Bin) + 4, Res = <>, {Res, Res}; pack(ssl_tls,Bin) -> - Content = case (random:uniform(256) - 1) of + Content = case (rand:uniform(256) - 1) of C when C<128 -> C; _ -> v2hello end, - Major = random:uniform(256) - 1, - Minor = random:uniform(256) - 1, + Major = rand:uniform(256) - 1, + Minor = rand:uniform(256) - 1, pack_ssl(Content,Major,Minor,Bin). pack_ssl(Content, Major, Minor, Body) -> @@ -371,10 +368,10 @@ http_do({Bin,[{_Line,PL,PB}|Tail]}, Type) -> ?line {ok, PB, Rest} = decode_pkt(http_with_bin(Type),Bin), %% Same tests again but as SubBin - PreLen = random:uniform(64), - Prefix = random:uniform(1 bsl PreLen) - 1, - SufLen = random:uniform(64), - Suffix = random:uniform(1 bsl SufLen) - 1, + PreLen = rand:uniform(64), + Prefix = rand:uniform(1 bsl PreLen) - 1, + SufLen = rand:uniform(64), + Suffix = rand:uniform(1 bsl SufLen) - 1, Orig = <>, BinLen = bit_size(Bin), <<_:PreLen, SubBin:BinLen/bits, _/bits>> = Orig, % Make SubBin diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index b72d6cbe52..4fd7b36e6a 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -224,7 +224,7 @@ outputv_errors_1(Term) -> port_close(Port). build_iolist(N, Base) when N < 16 -> - case random:uniform(3) of + case rand:uniform(3) of 1 -> <> = Base, Bin; @@ -232,7 +232,7 @@ build_iolist(N, Base) when N < 16 -> lists:seq(1, N) end; build_iolist(N, Base) when N =< byte_size(Base) -> - case random:uniform(3) of + case rand:uniform(3) of 1 -> <> = Base, Bin; @@ -250,7 +250,7 @@ build_iolist(N, Base) when N =< byte_size(Base) -> end end; build_iolist(N0, Base) -> - Small = random:uniform(15), + Small = rand:uniform(15), Seq = lists:seq(1, Small), N = N0 - Small, case N rem 2 of @@ -2502,14 +2502,7 @@ random_char() -> uniform(256) - 1. uniform(N) -> - case get(random_seed) of - undefined -> - {X, Y, Z} = time(), - random:seed(X, Y, Z); - _ -> - ok - end, - random:uniform(N). + rand:uniform(N). erl_millisecs() -> erl_millisecs(erlang:monotonic_time()). diff --git a/erts/emulator/test/evil_SUITE.erl b/erts/emulator/test/evil_SUITE.erl index 484d2a8bf5..d28e4d9596 100644 --- a/erts/emulator/test/evil_SUITE.erl +++ b/erts/emulator/test/evil_SUITE.erl @@ -382,10 +382,10 @@ my_appender_1(N, T0) -> my_appender_1(N-1, T). seed() -> - random:seed(3172, 9815, 20129). + rand:seed(exsplus, {3172,9815,20129}). rnd_term() -> - U0 = random:uniform(), + U0 = rand:uniform(), B = <>, {U0,U0 * 2.5 + 3.14,[U0*2.3,B]}. diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl index 2ea49467b8..1b2acf48e1 100644 --- a/erts/emulator/test/hash_SUITE.erl +++ b/erts/emulator/test/hash_SUITE.erl @@ -223,11 +223,10 @@ basic_test() -> range_test() -> - random:seed(), F = fun(From,From,_FF) -> ok; (From,To,FF) -> - R = random:uniform(16#FFFFFFFFFFFFFFFF), + R = rand:uniform(16#FFFFFFFFFFFFFFFF), X = erlang:phash(R, From), Y = erlang:phash(R, 16#100000000) - 1, Z = (Y rem From) + 1, @@ -265,14 +264,13 @@ spread_test(N) -> cmp_test(N) -> - % No need to save seed, the error indicates what number caused it. - random:seed(), do_cmp_hashes(N,8). + do_cmp_hashes(0,_) -> ok; do_cmp_hashes(N,Steps) -> - R0 = random:uniform(1 bsl Steps - 1) + random:uniform(16#FFFFFFFF), - R = case random:uniform(2) of + R0 = rand:uniform(1 bsl Steps - 1) + rand:uniform(16#FFFFFFFF), + R = case rand:uniform(2) of 1 -> R0; _ -> diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 6890c42b7a..b74be7c53a 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -1511,11 +1511,8 @@ t_map_equal(Config) when is_list(Config) -> t_map_compare(Config) when is_list(Config) -> - Seed = {erlang:monotonic_time(), - erlang:time_offset(), - erlang:unique_integer()}, - io:format("seed = ~p\n", [Seed]), - random:seed(Seed), + rand:seed(exsplus), + io:format("seed = ~p\n", [rand:export_seed()]), repeat(100, fun(_) -> float_int_compare() end, []), repeat(100, fun(_) -> recursive_compare() end, []), ok. @@ -1533,7 +1530,7 @@ float_int_compare() -> numeric_keys(N) -> lists:foldl(fun(_,Acc) -> - Int = random:uniform(N*4) - N*2, + Int = rand:uniform(N*4) - N*2, Float = float(Int), [Int, Float, Float * 0.99, Float * 1.01 | Acc] end, @@ -1564,7 +1561,7 @@ do_compare([Gen1, Gen2]) -> %% Change one key from int to float (or vice versa) and check compare ML1 = maps:to_list(M1), - {K1,V1} = lists:nth(random:uniform(length(ML1)), ML1), + {K1,V1} = lists:nth(rand:uniform(length(ML1)), ML1), case K1 of I when is_integer(I) -> case maps:find(float(I),M1) of @@ -1655,9 +1652,9 @@ cmp_others(T1, T2, _) -> map_gen(Pairs, Size) -> {_,L} = lists:foldl(fun(_, {Keys, Acc}) -> - KI = random:uniform(size(Keys)), + KI = rand:uniform(size(Keys)), K = element(KI,Keys), - KV = element(random:uniform(size(K)), K), + KV = element(rand:uniform(size(K)), K), {erlang:delete_element(KI,Keys), [KV | Acc]} end, {Pairs, []}, @@ -1697,15 +1694,15 @@ term_gen_recursive(Leafs, Flags, Depth) -> MaxDepth = 10, Rnd = case {Flags, Depth} of {_, MaxDepth} -> % Only leafs - random:uniform(size(Leafs)) + 3; + rand:uniform(size(Leafs)) + 3; {0, 0} -> % Only containers - random:uniform(3); + rand:uniform(3); {0,_} -> % Anything - random:uniform(size(Leafs)+3) + rand:uniform(size(Leafs)+3) end, case Rnd of 1 -> % Make map - Size = random:uniform(size(Leafs)), + Size = rand:uniform(size(Leafs)), lists:foldl(fun(_, {Acc1,Acc2}) -> {K1,K2} = term_gen_recursive(Leafs, Flags, Depth+1), @@ -1720,7 +1717,7 @@ term_gen_recursive(Leafs, Flags, Depth) -> {Cdr1,Cdr2} = term_gen_recursive(Leafs, Flags, Depth+1), {[Car1 | Cdr1], [Car2 | Cdr2]}; 3 -> % Make tuple - Size = random:uniform(size(Leafs)), + Size = rand:uniform(size(Leafs)), L = lists:map(fun(_) -> term_gen_recursive(Leafs, Flags, Depth+1) end, lists:seq(1,Size)), {L1, L2} = lists:unzip(L), @@ -1729,7 +1726,7 @@ term_gen_recursive(Leafs, Flags, Depth) -> N -> % Make leaf case element(N-3, Leafs) of I when is_integer(I) -> - case random:uniform(4) of + case rand:uniform(4) of 1 -> {I, float(I)}; 2 -> {float(I), I}; _ -> {I,I} diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index af2b955184..56b36d2626 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1192,11 +1192,8 @@ send3(Config) when is_list(Config) -> %% Let a number of processes send random message blobs between each other %% using enif_send. Kill and spawn new ones randomly to keep a ~constant %% number of workers running. - Seed = {erlang:monotonic_time(), - erlang:time_offset(), - erlang:unique_integer()}, - io:format("seed: ~p\n",[Seed]), - random:seed(Seed), + rand:seed(exsplus), + io:format("seed: ~p\n",[rand:export_seed()]), ets:new(nif_SUITE,[named_table,public]), ?line true = ets:insert(nif_SUITE,{send3,0,0,0,0}), timer:send_after(10000, timeout), % Run for 10 seconds @@ -1229,7 +1226,7 @@ send3_controller(SpawnCnt0, Mons0, Pids0, Tick) -> after Tick -> Max = 20, N = length(Pids0), - PidN = random:uniform(Max), + PidN = rand:uniform(Max), %%io:format("N=~p PidN=~p Pids0=~p\n", [N,PidN,Pids0]), case PidN > N of true -> @@ -1293,7 +1290,7 @@ send3_proc(Pids0, Counters={Rcv,SndOk,SndFail}, State0) -> end. send3_proc_send(Pids, {Rcv,SndOk,SndFail}, State0) -> - To = lists:nth(random:uniform(length(Pids)),Pids), + To = lists:nth(rand:uniform(length(Pids)),Pids), Blob = send3_make_blob(), State1 = send3_new_state(State0,Blob), case send3_send(To, Blob) of @@ -1305,12 +1302,12 @@ send3_proc_send(Pids, {Rcv,SndOk,SndFail}, State0) -> send3_make_blob() -> - case random:uniform(20)-1 of + case rand:uniform(20)-1 of 0 -> {term,[]}; N -> MsgEnv = alloc_msgenv(), repeat(N bsr 1, - fun(_) -> grow_blob(MsgEnv,other_term(),random:uniform(1 bsl 20)) + fun(_) -> grow_blob(MsgEnv,other_term(),rand:uniform(1 bsl 20)) end, void), case (N band 1) of 0 -> {term,copy_blob(MsgEnv)}; @@ -1320,7 +1317,7 @@ send3_make_blob() -> send3_send(Pid, Msg) -> %% 90% enif_send and 10% normal bang - case random:uniform(10) of + case rand:uniform(10) of 1 -> send3_send_bang(Pid,Msg); _ -> send3_send_nif(Pid,Msg) end. @@ -1341,7 +1338,7 @@ send3_send_bang(Pid, {msgenv,MsgEnv}) -> true. send3_new_state(State, Blob) -> - case random:uniform(5+2) of + case rand:uniform(5+2) of N when N =< 5-> setelement(N, State, Blob); _ -> State % Don't store blob end. diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl index 6eda78a57b..65a5a4c505 100644 --- a/erts/emulator/test/op_SUITE.erl +++ b/erts/emulator/test/op_SUITE.erl @@ -97,10 +97,11 @@ relop_simple(Config) when is_list(Config) -> lists:foreach(fun({A,B}) -> relop_simple_do(A,B) end, Combos), - repeat(fun() -> Size = random:uniform(100), - Rnd1 = make_rand_term(Size), - {Rnd2,0} = clone_and_mutate(Rnd1, random:uniform(Size)), - relop_simple_do(Rnd1,Rnd2) + repeat(fun() -> + Size = rand:uniform(100), + Rnd1 = make_rand_term(Size), + {Rnd2,0} = clone_and_mutate(Rnd1, rand:uniform(Size)), + relop_simple_do(Rnd1,Rnd2) end, 1000), ok. @@ -158,7 +159,7 @@ cmp_emu(A,B) -> make_rand_term(1) -> make_rand_term_single(); make_rand_term(Arity) -> - case random:uniform(3) of + case rand:uniform(3) of 1 -> make_rand_list(Arity); 2 -> @@ -169,17 +170,17 @@ make_rand_term(Arity) -> end. make_rand_term_single() -> - Range = 1 bsl random:uniform(200), - case random:uniform(12) of + Range = 1 bsl rand:uniform(200), + case rand:uniform(12) of 1 -> random; 2 -> uniform; - 3 -> random:uniform(Range) - (Range div 2); - 4 -> Range * (random:uniform() - 0.5); + 3 -> rand:uniform(Range) - (Range div 2); + 4 -> Range * (rand:uniform() - 0.5); 5 -> 0; 6 -> 0.0; 7 -> make_ref(); 8 -> self(); - 9 -> term_to_binary(random:uniform(Range)); + 9 -> term_to_binary(rand:uniform(Range)); 10 -> fun(X) -> X*Range end; 11 -> fun(X) -> X/Range end; 12 -> [] @@ -188,7 +189,7 @@ make_rand_term_single() -> make_rand_term_rand_size(1) -> {make_rand_term(1), 0}; make_rand_term_rand_size(MaxArity) -> - Arity = random:uniform(MaxArity-1), + Arity = rand:uniform(MaxArity-1), {make_rand_term(Arity), MaxArity-Arity}. make_rand_list(0) -> []; diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 3d0509a28c..b42e02a1e1 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -2183,15 +2183,14 @@ random_char(Chars) -> lists:nth(uniform(length(Chars)), Chars). uniform(N) -> - case get(random_seed) of - undefined -> - {X, Y, Z} = Seed = time(), - io:format("Random seed = ~p\n",[Seed]), - random:seed(X, Y, Z); + case rand:export_seed() of + undefined -> + rand:seed(exsplus), + io:format("Random seed = ~p\n", [rand:export_seed()]); _ -> ok end, - random:uniform(N). + rand:uniform(N). fun_spawn(Fun) -> fun_spawn(Fun, []). @@ -2331,7 +2330,7 @@ close_deaf_port(Config) when is_list(Config) -> close_deaf_port_1(200, _) -> ok; close_deaf_port_1(N, Cmd) -> - Timeout = integer_to_list(random:uniform(5*1000)), + Timeout = integer_to_list(rand:uniform(5*1000)), try open_port({spawn_executable,Cmd},[{args,[Timeout]}]) of Port -> erlang:port_command(Port,"Hello, can you hear me!?!?"), @@ -2372,7 +2371,7 @@ port_setget_data(Config) when is_list(Config) -> ok. port_setget_data_hammer(Port, HeapData, IsSet0, N) -> - Rand = random:uniform(3), + Rand = rand:uniform(3), IsSet1 = try case Rand of 1 -> true = erlang:port_set_data(Port, atom), true; 2 -> true = erlang:port_set_data(Port, HeapData), true; diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index b65a22a528..981899b167 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -485,14 +485,7 @@ random_char(Chars) -> lists:nth(uniform(length(Chars)), Chars). uniform(N) -> - case get(random_seed) of - undefined -> - {X, Y, Z} = time(), - random:seed(X, Y, Z); - _ -> - ok - end, - random:uniform(N). + rand:uniform(N). unaligned_sub_bin(Bin0) -> Bin1 = <<0:3,Bin0/binary,31:5>>, diff --git a/erts/emulator/test/random_iolist.erl b/erts/emulator/test/random_iolist.erl index 9a0f034e72..6da7da04de 100644 --- a/erts/emulator/test/random_iolist.erl +++ b/erts/emulator/test/random_iolist.erl @@ -36,7 +36,7 @@ run2(Iter,Fun1,Fun2) -> compare2(Iter,Fun1,Fun2). random_byte() -> - random:uniform(256) - 1. + rand:uniform(256) - 1. random_list(0,Acc) -> Acc; @@ -45,7 +45,7 @@ random_list(N,Acc) -> random_binary(N) -> B = list_to_binary(random_list(N,[])), - case {random:uniform(2),size(B)} of + case {rand:uniform(2),size(B)} of {2,M} when M > 1 -> S = M-1, <<_:3,C:S/binary,_:5>> = B, @@ -57,7 +57,7 @@ random_list(N) -> random_list(N,[]). front() -> - case random:uniform(10) of + case rand:uniform(10) of 10 -> false; _ -> @@ -65,7 +65,7 @@ front() -> end. any_type() -> - case random:uniform(10) of + case rand:uniform(10) of 1 -> list; 2 -> @@ -77,7 +77,7 @@ any_type() -> end. tail_type() -> - case random:uniform(5) of + case rand:uniform(5) of 1 -> list; 2 -> @@ -90,9 +90,9 @@ random_length(N) -> UpperLimit = 255, case N of M when M > UpperLimit -> - random:uniform(UpperLimit+1) - 1; + rand:uniform(UpperLimit+1) - 1; _ -> - random:uniform(N+1) - 1 + rand:uniform(N+1) - 1 end. random_iolist(0,Acc) -> @@ -139,7 +139,7 @@ random_iolist(N) -> standard_seed() -> - random:seed(1201,855653,380975). + rand:seed(exsplus, {1201,855653,380975}). do_comp(List,F1,F2) -> X = F1(List), diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl index 544d841f16..810bc07eed 100644 --- a/erts/emulator/test/save_calls_SUITE.erl +++ b/erts/emulator/test/save_calls_SUITE.erl @@ -189,7 +189,7 @@ is_local_function(_) -> % Number crunching for reds test. carmichaels_below(N) -> - random:seed(3172,9814,20125), + rand:seed(exsplus, {3172,9814,20125}), carmichaels_below(1,N). carmichaels_below(N,N2) when N >= N2 -> @@ -219,7 +219,7 @@ expmod(Base,Exp,Mod) -> (Base * expmod(Base,Exp - 1,Mod)) rem Mod. uniform(N) -> - random:uniform(N-1). + rand:uniform(N-1). fermat(N) -> R = uniform(N), diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl index e4b6511d1f..0a0784337f 100644 --- a/erts/emulator/test/system_profile_SUITE.erl +++ b/erts/emulator/test/system_profile_SUITE.erl @@ -448,7 +448,7 @@ run_load(N, Pids) -> run_load(N - 1, [Pid | Pids]). list_load() -> - ok = case math:sin(random:uniform(32451)) of + ok = case math:sin(rand:uniform(32451)) of A when is_float(A) -> ok; _ -> ok end, diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index 33076c7461..3bd28a6d20 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -69,7 +69,7 @@ init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> [{testcase, Func}|Config]. -end_per_testcase(_Func, Config) -> +end_per_testcase(_Func, _Config) -> ok. suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -742,25 +742,21 @@ chk_strc(Res0, Res1) -> ok. chk_random_values(FR, TR) -> -% case (FR rem TR == 0) orelse (TR rem FR == 0) of -% true -> - io:format("rand values ~p -> ~p~n", [FR, TR]), - random:seed(268438039, 268440479, 268439161), - Values = lists:map(fun (_) -> random:uniform(1 bsl 65) - (1 bsl 64) end, - lists:seq(1, 100000)), - CheckFun = fun (V) -> - CV = erlang:convert_time_unit(V, FR, TR), - case {(FR*CV) div TR =< V, - (FR*(CV+1)) div TR >= V} of - {true, true} -> - ok; - Failure -> - ?t:fail({Failure, CV, V, FR, TR}) - end - end, - lists:foreach(CheckFun, Values).%; -% false -> ok -% end. + io:format("rand values ~p -> ~p~n", [FR, TR]), + rand:seed(exsplus, {268438039,268440479,268439161}), + Values = lists:map(fun (_) -> rand:uniform(1 bsl 65) - (1 bsl 64) end, + lists:seq(1, 100000)), + CheckFun = fun (V) -> + CV = erlang:convert_time_unit(V, FR, TR), + case {(FR*CV) div TR =< V, + (FR*(CV+1)) div TR >= V} of + {true, true} -> + ok; + Failure -> + ?t:fail({Failure, CV, V, FR, TR}) + end + end, + lists:foreach(CheckFun, Values). chk_values_per_value(_FromRes, _ToRes, diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 6eae182e45..00b90e3c3d 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -933,7 +933,7 @@ suspend_exit(suite) -> []; suspend_exit(Config) when is_list(Config) -> ?line Dog = test_server:timetrap(test_server:minutes(2)), - ?line random:seed(4711,17,4711), + rand:seed(exsplus, {4711,17,4711}), ?line do_suspend_exit(5000), ?line test_server:timetrap_cancel(Dog), ?line ok. @@ -941,7 +941,7 @@ suspend_exit(Config) when is_list(Config) -> do_suspend_exit(0) -> ?line ok; do_suspend_exit(N) -> - ?line Work = random:uniform(50), + Work = rand:uniform(50), ?line Parent = self(), ?line {Suspendee, Mon2} = spawn_monitor(fun () -> -- cgit v1.2.3 From eda21ee8a6b87a7ec03822e255d82827db1aee12 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 11 Dec 2015 19:37:51 +0100 Subject: erts: Fix correct node name for DTRACE broken by 949de78331b9c4ecb9. --- erts/emulator/beam/erl_node_tables.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index a7d0511bf9..707de39556 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -752,6 +752,10 @@ erts_set_this_node(Eterm sysname, Uint creation) erts_this_dist_entry = erts_this_node->dist_entry; erts_refc_inc(&erts_this_dist_entry->refc, 2); + + erts_this_node_sysname = erts_this_node_sysname_BUFFER; + erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname_BUFFER), + "%T", sysname); } Uint -- cgit v1.2.3 From 83b9161cb4f96b5ee1fddb1c4f69b63b91c5b6e2 Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Mon, 14 Dec 2015 11:12:03 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 56328 -> 56336 bytes erts/preloaded/ebin/erlang.beam | Bin 101840 -> 101796 bytes erts/preloaded/ebin/erts_internal.beam | Bin 5964 -> 5960 bytes erts/preloaded/ebin/init.beam | Bin 48812 -> 48764 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1468 -> 1468 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1340 -> 1340 bytes erts/preloaded/ebin/prim_file.beam | Bin 44904 -> 44892 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72712 -> 72716 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23416 -> 23424 bytes erts/preloaded/ebin/zlib.beam | Bin 14176 -> 14176 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index df12c6f8e0..4a6fb6109f 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 863a5e61ef..cd2e7f18a2 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index dc8c711e1a..32d5d70122 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 73dfb3d351..9b0fc82bed 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 33c112f4de..c8166d5ed7 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index ebca6e7eea..ddcc3886a4 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index e8817d183e..97170551bf 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 8b87d1ae26..72065661c5 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 969239be98..2a0b33279d 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 281f668f8c..0d3d1d9343 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 128fc752a45d51beb71c1aea4d5433efd00ac02a Mon Sep 17 00:00:00 2001 From: Alexey Lebedeff Date: Mon, 14 Dec 2015 15:08:15 +0300 Subject: Don't wait for twice the delay_write timeout This happens only during processing ALIVE2 request. reply() already performs the same delay as in the deleted code. --- erts/epmd/src/epmd_srv.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 8c8d7304f2..5b58554590 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -700,9 +700,6 @@ static void do_request(g, fd, s, buf, bsize) put_int16(node->creation, wbuf+2); } - if (g->delay_write) /* Test of busy server */ - sleep(g->delay_write); - if (reply(g, fd, wbuf, 4) != 4) { dbg_tty_printf(g,1,"** failed to send ALIVE2_RESP for \"%s\"", -- cgit v1.2.3 From 5850300f41de8047b9a9da1e4f00aaee3dcd662c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 10 Dec 2015 12:40:40 +0100 Subject: system tests: Replace 'random' with 'rand' --- erts/test/ethread_SUITE.erl | 32 -------------------------------- erts/test/run_erl_SUITE.erl | 18 ++++++++---------- 2 files changed, 8 insertions(+), 42 deletions(-) (limited to 'erts') diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index 4a40dbb11e..388af66b23 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -123,38 +123,6 @@ try_lock_mutex(suite) -> try_lock_mutex(Config) -> run_case(Config, "try_lock_mutex", ""). -%% Remove dead code? - -% wd_dispatch(P) -> -% receive -% bye -> -% ?line true = port_command(P, "-1 "), -% ?line bye; -% L when is_list(L) -> -% ?line true = port_command(P, L), -% ?line wd_dispatch(P) -% end. -% -% watchdog(Port) -> -% ?line process_flag(priority, max), -% ?line receive after 500 -> ok end, -% -% ?line random:seed(), -% ?line true = port_command(Port, "0 "), -% ?line lists:foreach(fun (T) -> -% erlang:send_after(T, -% self(), -% integer_to_list(T) -% ++ " ") -% end, -% lists:usort(lists:map(fun (_) -> -% random:uniform(4500)+500 -% end, -% lists:duplicate(50,0)))), -% ?line erlang:send_after(5100, self(), bye), -% -% wd_dispatch(Port). - cond_wait(doc) -> ["Tests ethr_cond_wait with ethr_cond_signal and ethr_cond_broadcast."]; cond_wait(suite) -> diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl index 328477d870..6759d41a2b 100644 --- a/erts/test/run_erl_SUITE.erl +++ b/erts/test/run_erl_SUITE.erl @@ -141,12 +141,10 @@ heavier_1(Config) -> ?line ToErl = open_port({spawn,"to_erl "++Pipe}, []), io:format("ToErl = ~p\n", [ToErl]), - X = 1, - Y = 555, - Z = 42, - ?line random:seed(X, Y, Z), - SeedCmd = lists:flatten(io_lib:format("random:seed(~p, ~p, ~p). \r\n", - [X,Y,Z])), + Seed = {1,555,42}, + rand:seed(exsplus, Seed), + SeedCmd = lists:flatten(io_lib:format("rand:seed(exsplus, ~p). \r\n", + [Seed])), ?line io:format("~p\n", [SeedCmd]), ?line erlang:port_command(ToErl, SeedCmd), @@ -157,9 +155,9 @@ heavier_1(Config) -> "F = fun(F,0) -> ok; "++ "(F,N) -> " ++ "io:format(\"\\\"~s\\\"~n\","++ - "[[35|[random:uniform(25)+65 || " ++ + "[[35|[rand:uniform(25)+65 || " ++ "_ <- lists:seq(1, "++ - "random:uniform("++ + "rand:uniform("++ integer_to_list(MaxLen)++ "))]]]), "++ "F(F,N-1) "++ @@ -189,8 +187,8 @@ receive_all(Iter, ToErl, MaxLen) -> receive_all_1(0, _, _, _) -> ok; receive_all_1(Iter, Line, ToErl, MaxLen) -> - NumChars = random:uniform(MaxLen), - Pattern = [random:uniform(25)+65 || _ <- lists:seq(1, NumChars)], + NumChars = rand:uniform(MaxLen), + Pattern = [rand:uniform(25)+65 || _ <- lists:seq(1, NumChars)], receive_all_2(Iter, {NumChars,Pattern}, Line, ToErl, MaxLen). -- cgit v1.2.3 From 41d147339c65d2b6dfcf60c1e63482612774bede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 10 Dec 2015 15:45:55 +0100 Subject: Eliminate mentions of 'random' in documentation --- erts/doc/src/init.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index fe26df61f7..2a33096d04 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -247,10 +247,7 @@ Expr during system initialization. If any of these steps fail (syntax error, parse error or exception during evaluation), Erlang stops with an error message. Here is an - example that seeds the random number generator:

-
-% erl -eval '{X,Y,Z} = now(), random:seed(X,Y,Z).'
-

This example uses Erlang as a hexadecimal calculator:

+ example that uses Erlang as a hexadecimal calculator:

 % erl -noshell -eval 'R = 16#1F+16#A0, io:format("~.16B~n", [R])' \\
 -s erlang halt
-- 
cgit v1.2.3


From 4586435cd4ca0d3ad3aedd60f462aed8da589460 Mon Sep 17 00:00:00 2001
From: Erlang/OTP 
Date: Tue, 15 Dec 2015 09:43:08 +0100
Subject: Update version numbers

---
 erts/vsn.mk | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'erts')

diff --git a/erts/vsn.mk b/erts/vsn.mk
index 140baeb846..be6cf2e376 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
 # %CopyrightEnd%
 # 
 
-VSN = 7.1
+VSN = 7.2
 
 # Port number 4365 in 4.2
 # Port number 4366 in 4.3
-- 
cgit v1.2.3


From f4a0ae1736216feac5ae053610644bba2e12ed34 Mon Sep 17 00:00:00 2001
From: Erlang/OTP 
Date: Tue, 15 Dec 2015 09:45:27 +0100
Subject: Update release notes

---
 erts/doc/src/notes.xml | 105 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

(limited to 'erts')

diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 5a65115cb2..5cb9bdb690 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -32,6 +32,111 @@
   

This document describes the changes made to the ERTS application.

+
Erts 7.2 + +
Fixed Bugs and Malfunctions + + +

+ Small documentation fixes

+

+ Own Id: OTP-13017

+
+ +

+ Fix memory corruption bug caused by disabling + distribution and then re-enable distribution with a node + name that has previously been used by a remote node.

+

+ Own Id: OTP-13076 Aux Id: seq12959

+
+ +

+ Renamed variables with name bool as Visual Studio 2015 + now treats this is a keyword.

+

+ Own Id: OTP-13079

+
+ +

erl_prim_loader has not supported custom + loaders for several releases. In the documentation for + erl_prim_loader, all references to custom loaders + have now been removed.

+

+ Own Id: OTP-13102

+
+ +

+ Fixed compilation of erts together with libc versions + that do not define __uint32_t.

+

+ Own Id: OTP-13105

+
+ +

+ erl -make now returns non-zero exit codes on failure

+

+ Own Id: OTP-13107

+
+ +

+ Fix crash on init:restart in embedded mode caused by + on_load handler process not being relaunched leading to + load failure for modules such as crypto and asn1rt_nif + that need it to be present for correct NIF loading.

+

+ Own Id: OTP-13115

+
+ +

+ Fix maps decode in erlang:binary_to_term/1

+

Decoding a term with a large (HAMT) map in an small + (FLAT) map could cause a critical error if the external + format was not produced by beam.

+

+ Own Id: OTP-13125

+
+ +

+ Fix very rare bug in GC when big maps with a lot of hash + collisions from a remote node are waiting in inner + message queue.

+

+ Own Id: OTP-13146

+
+ +

+ Fixed a bug that could cause a crash dump to become + almost empty.

+

+ Own Id: OTP-13150

+
+
+
+ + +
Improvements and New Features + + +

Updated the xmllint target to just check the xml + files with real documentation content.
Corrected + some errors and added some missing target in the DTD's. +

+

+ Own Id: OTP-13026

+
+ +

+ Add function enif_getenv to read OS environment variables + in a portable way from NIFs.

+

+ Own Id: OTP-13147

+
+
+
+ +
+
Erts 7.1
Fixed Bugs and Malfunctions -- cgit v1.2.3 From 4b98e710b9c45481c1cdc7a4ee68f7ce7fca908a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 13 Jul 2015 15:38:41 +0200 Subject: erts: Add support for asynchronous open_port OTP-13086 --- erts/doc/src/driver_entry.xml | 9 +++++- erts/doc/src/erl_driver.xml | 28 +++++++++++++++++ erts/emulator/beam/atom.names | 1 + erts/emulator/beam/bif.tab | 2 +- erts/emulator/beam/erl_bif_port.c | 54 +++++++++++++++++++++++++------- erts/emulator/beam/erl_driver.h | 4 +++ erts/emulator/beam/erl_port.h | 5 +++ erts/emulator/beam/io.c | 60 ++++++++++++++++++++++++++++++++++++ erts/preloaded/src/erlang.erl | 10 ++++-- erts/preloaded/src/erts_internal.erl | 9 +++++- 10 files changed, 166 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml index c802693977..ae7f264d0c 100644 --- a/erts/doc/src/driver_entry.xml +++ b/erts/doc/src/driver_entry.xml @@ -437,7 +437,14 @@ typedef struct erl_drv_entry { erl_drv_busy_msgq_limits() function. - + ERL_DRV_FLAG_USE_INIT_ACK + When this flag is given the linked-in driver has to manually + acknowledge that the port has been successfully started using + erl_drv_init_ack(). + This allows the implementor to make the erlang:open_port exit with + badarg after some initial asynchronous initialization has been done. + + void *handle2 diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index f7b4187b80..a2a1df37e5 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -2130,6 +2130,34 @@ ERL_DRV_MAP int sz + + voiderl_drv_init_ack(ErlDrvPort port, ErlDrvData res) + Acknowledge the start of the port + + +

Arguments:

+ + port + The port handle of the port (driver instance) creating + doing the acknowledgment. + + res + The result of the port initialization. This can be the same values + as the return value of start, + i.e any of the error codes or the ErlDrvData that is to be used for this + port. + + +

+ When this function is called the initiating erlang:open_port call is + returned as if the start + function had just been called. It can only be used when the + ERL_DRV_FLAG_USE_INIT_ACK + flag has been set on the linked-in driver. +

+
+
+ interl_drv_thread_create(char *name, ErlDrvTid *tid, diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 967cf013f0..f20d99f114 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -209,6 +209,7 @@ atom dsend_continue_trap atom dunlink atom duplicate_bag atom dupnames +atom einval atom elib_malloc atom emulator atom enable_trace diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 65f8d6f1f5..884555dee2 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -115,7 +115,7 @@ bif erlang:time_offset/0 bif erlang:time_offset/1 bif erlang:timestamp/0 -bif erlang:open_port/2 +bif erts_internal:open_port/2 bif erlang:pid_to_list/1 bif erlang:ports/0 diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index e47d7bcbbb..839abd0424 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -41,6 +41,7 @@ #include "external.h" #include "packet_parser.h" #include "erl_bits.h" +#include "erl_bif_unique.h" #include "dtrace-wrapper.h" static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump); @@ -50,10 +51,10 @@ static void free_args(char **); char *erts_default_arg0 = "default"; -BIF_RETTYPE open_port_2(BIF_ALIST_2) +BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2) { Port *port; - Eterm port_id; + Eterm res; char *str; int err_type, err_num; @@ -61,27 +62,58 @@ BIF_RETTYPE open_port_2(BIF_ALIST_2) if (!port) { if (err_type == -3) { ASSERT(err_num == BADARG || err_num == SYSTEM_LIMIT); - BIF_ERROR(BIF_P, err_num); + if (err_num == BADARG) + res = am_badarg; + else if (err_num == SYSTEM_LIMIT) + res = am_system_limit; + else + /* this is only here to silence gcc, it should not happen */ + BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); } else if (err_type == -2) { str = erl_errno_id(err_num); + res = erts_atom_put((byte *) str, strlen(str), ERTS_ATOM_ENC_LATIN1, 1); } else { - str = "einval"; + res = am_einval; } - BIF_P->fvalue = erts_atom_put((byte *) str, strlen(str), ERTS_ATOM_ENC_LATIN1, 1); - BIF_ERROR(BIF_P, EXC_ERROR); - } + BIF_RET(res); + } + + if (port->drv_ptr->flags & ERL_DRV_FLAG_USE_INIT_ACK) { + /* Copied from erl_port_task.c */ + port->async_open_port = erts_alloc(ERTS_ALC_T_PRTSD, + sizeof(*port->async_open_port)); + erts_make_ref_in_array(port->async_open_port->ref); + port->async_open_port->to = BIF_P->common.id; + + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK); + if (ERTS_PROC_PENDING_EXIT(BIF_P)) { + /* need to exit caller instead */ + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK); + KILL_CATCHES(BIF_P); + BIF_P->freason = EXC_EXIT; + erts_port_release(port); + BIF_RET(am_badarg); + } + + ERTS_SMP_MSGQ_MV_INQ2PRIVQ(BIF_P); + BIF_P->msg.save = BIF_P->msg.last; - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE); + + res = erts_proc_store_ref(BIF_P, port->async_open_port->ref); + } else { + res = port->common.id; + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); + } - port_id = port->common.id; erts_add_link(&ERTS_P_LINKS(port), LINK_PID, BIF_P->common.id); - erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, port_id); + erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, port->common.id); erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); erts_port_release(port); - BIF_RET(port_id); + BIF_RET(res); } static ERTS_INLINE Port * diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index e71b87803b..5f2115ef7f 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -163,6 +163,7 @@ typedef struct { #define ERL_DRV_FLAG_USE_PORT_LOCKING (1 << 0) #define ERL_DRV_FLAG_SOFT_BUSY (1 << 1) #define ERL_DRV_FLAG_NO_BUSY_MSGQ (1 << 2) +#define ERL_DRV_FLAG_USE_INIT_ACK (1 << 3) /* * Integer types @@ -690,6 +691,9 @@ EXTERN char *driver_dl_error(void); EXTERN int erl_drv_putenv(char *key, char *value); EXTERN int erl_drv_getenv(char *key, char *value, size_t *value_size); +/* spawn start init ack */ +EXTERN void erl_drv_init_ack(ErlDrvPort ix, ErlDrvData res); + #endif /* !ERL_DRIVER_TYPES_ONLY */ #ifdef WIN32_DYNAMIC_ERL_DRIVER diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index acd68ef0ad..bc4b412594 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -187,6 +187,11 @@ struct _erl_drv_port { ErtsPrtSD *psd; /* Port specific data */ int reds; /* Only used while executing driver callbacks */ + + struct { + Eterm to; + Uint32 ref[ERTS_MAX_REF_NUMBERS]; + } *async_open_port; /* Reference used with async open port */ }; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index fdd26fcc4b..409df846e9 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -84,6 +84,7 @@ static void deliver_result(Eterm sender, Eterm pid, Eterm res); static int init_driver(erts_driver_t *, ErlDrvEntry *, DE_Handle *); static void terminate_port(Port *p); static void pdl_init(void); +static int driver_failure_term(ErlDrvPort ix, Eterm term, int eof); #ifdef ERTS_SMP static void driver_monitor_lock_pdl(Port *p); static void driver_monitor_unlock_pdl(Port *p); @@ -383,6 +384,7 @@ static Port *create_port(char *name, ERTS_PTMR_INIT(prt); erts_port_task_handle_init(&prt->timeout_task); prt->psd = NULL; + prt->async_open_port = NULL; prt->drv_data = (SWord) 0; prt->os_pid = -1; @@ -2732,6 +2734,61 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp) port_sig_link); } +static void +init_ack_send_reply(Port *port, Eterm resp) +{ + + if (!is_internal_port(resp)) { + Process *rp = erts_proc_lookup_raw(port->async_open_port->to); + erts_smp_proc_lock(rp, ERTS_PROC_LOCK_LINK); + erts_remove_link(&ERTS_P_LINKS(port), port->async_open_port->to); + erts_remove_link(&ERTS_P_LINKS(rp), port->common.id); + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + } + port_sched_op_reply(port->async_open_port->to, + port->async_open_port->ref, + resp); + + erts_free(ERTS_ALC_T_PRTSD, port->async_open_port); + port->async_open_port = NULL; +} + +void +erl_drv_init_ack(ErlDrvPort ix, ErlDrvData res) { + Port *port = erts_drvport2port(ix); + SWord err_type = (SWord)res; + Eterm resp; + + if (port == ERTS_INVALID_ERL_DRV_PORT && port->async_open_port) + return; + + if (port->async_open_port) { + switch(err_type) { + case -3: + resp = am_badarg; + break; + case -2: { + char *str = erl_errno_id(errno); + resp = erts_atom_put((byte *) str, strlen(str), + ERTS_ATOM_ENC_LATIN1, 1); + break; + } + case -1: + resp = am_einval; + break; + default: + resp = port->common.id; + break; + } + + init_ack_send_reply(port, resp); + + if (err_type == -1 || err_type == -2 || err_type == -3) + driver_failure_term(ix, am_normal, 0); + port->drv_data = err_type; + } +} + void erts_init_io(int port_tab_size, int port_tab_size_ignore_files, int legacy_port_tab) @@ -6972,6 +7029,9 @@ driver_failure_term(ErlDrvPort ix, Eterm term, int eof) if (prt == ERTS_INVALID_ERL_DRV_PORT) return -1; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); + + if (prt->async_open_port) + init_ack_send_reply(prt, prt->common.id); if (eof) flush_linebuf_messages(prt, state); if (state & ERTS_PORT_SFLG_CLOSING) { diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 7280b43502..4c22c596eb 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2027,8 +2027,14 @@ nodes(_Arg) -> | eof | {parallelism, Boolean :: boolean()} | hide. -open_port(_PortName,_PortSettings) -> - erlang:nif_error(undefined). +open_port(PortName, PortSettings) -> + case case erts_internal:open_port(PortName, PortSettings) of + Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end; + Res -> Res + end of + Port when erlang:is_port(Port) -> Port; + Error -> erlang:error(Error, [PortName, PortSettings]) + end. -type priority_level() :: low | normal | high | max. diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 7ed4efea4b..81202ed3e2 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -32,7 +32,7 @@ -export([await_port_send_result/3]). -export([cmp_term/2]). -export([map_to_tuple_keys/1, map_type/1, map_hashmap_children/1]). --export([port_command/3, port_connect/2, port_close/1, +-export([open_port/2, port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). -export([request_system_task/3]). @@ -88,6 +88,13 @@ gather_io_bytes(Ref, No, InAcc, OutAcc) -> %% Statically linked port NIFs %% +-spec erts_internal:open_port(PortName, PortSettings) -> Result when + PortName :: tuple(), + PortSettings :: term(), + Result :: port() | reference() | atom(). +open_port(_PortName, _PortSettings) -> + erlang:nif_error(undefined). + -spec erts_internal:port_command(Port, Data, OptionList) -> Result when Port :: port() | atom(), Data :: iodata(), -- cgit v1.2.3 From 91c1876f70870f450f7e8fd3b02f2396067e3cb8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 14 Jul 2015 11:07:33 +0200 Subject: erts: Add erl_drv_set_pid OTP-13087 --- erts/doc/src/erl_driver.xml | 19 +++++++++++++++++++ erts/emulator/beam/erl_driver.h | 3 +++ erts/emulator/beam/io.c | 11 +++++++++++ 3 files changed, 33 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index a2a1df37e5..ef6d69cbeb 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -2158,6 +2158,25 @@ ERL_DRV_MAP int sz + + voiderl_drv_set_os_pid(ErlDrvPort port, ErlDrvSInt pid) + Set the os_pid for the port + + +

Arguments:

+ + port + The port handle of the port (driver instance) to set the pid on. + + pid + The pid to set. + +

+ Set the os_pid seen when doing erlang:port_info/2 on this port. +

+
+
+ interl_drv_thread_create(char *name, ErlDrvTid *tid, diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 5f2115ef7f..3d663655a2 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -694,6 +694,9 @@ EXTERN int erl_drv_getenv(char *key, char *value, size_t *value_size); /* spawn start init ack */ EXTERN void erl_drv_init_ack(ErlDrvPort ix, ErlDrvData res); +/* set the pid seen in port_info */ +EXTERN void erl_drv_set_os_pid(ErlDrvPort ix, ErlDrvSInt pid); + #endif /* !ERL_DRIVER_TYPES_ONLY */ #ifdef WIN32_DYNAMIC_ERL_DRIVER diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 409df846e9..2a3759212e 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -2789,6 +2789,17 @@ erl_drv_init_ack(ErlDrvPort ix, ErlDrvData res) { } } +void +erl_drv_set_os_pid(ErlDrvPort ix, ErlDrvSInt pid) { + Port *port = erts_drvport2port(ix); + + if (port == ERTS_INVALID_ERL_DRV_PORT) + return; + + port->os_pid = (SWord)pid; + +} + void erts_init_io(int port_tab_size, int port_tab_size_ignore_files, int legacy_port_tab) -- cgit v1.2.3 From 31b8dd2b4b5259d9ed2178017c3580c42bf62ec6 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 12 Aug 2015 10:21:09 +0200 Subject: erts: Bump driver minor version --- erts/emulator/beam/erl_driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 3d663655a2..ef7792ecab 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -125,7 +125,7 @@ typedef struct { #define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed) #define ERL_DRV_EXTENDED_MAJOR_VERSION 3 -#define ERL_DRV_EXTENDED_MINOR_VERSION 2 +#define ERL_DRV_EXTENDED_MINOR_VERSION 3 /* * The emulator will refuse to load a driver with a major version -- cgit v1.2.3 From 17ecb94437bac6726044d299dde48d02dd2b2e9c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 14 Jul 2015 15:20:16 +0200 Subject: erts: Refactor out erts functions from hash --- erts/emulator/beam/atom.c | 3 +++ erts/emulator/beam/erl_fun.c | 3 +++ erts/emulator/beam/erl_node_tables.c | 12 ++++++++---- erts/emulator/beam/erl_node_tables.h | 1 + erts/emulator/beam/export.c | 3 +++ erts/emulator/beam/hash.c | 30 +++++++++++++++--------------- erts/emulator/beam/hash.h | 20 +++++++++++++------- erts/emulator/beam/index.h | 4 ++++ erts/emulator/beam/module.c | 3 +++ erts/emulator/beam/register.c | 3 +++ erts/emulator/hipe/hipe_bif0.c | 12 ++++++++++++ 11 files changed, 68 insertions(+), 26 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c index fe91134ef4..fd2adac676 100644 --- a/erts/emulator/beam/atom.c +++ b/erts/emulator/beam/atom.c @@ -435,6 +435,9 @@ init_atom_table(void) f.cmp = (HCMP_FUN) atom_cmp; f.alloc = (HALLOC_FUN) atom_alloc; f.free = (HFREE_FUN) atom_free; + f.meta_alloc = (HMALLOC_FUN) erts_alloc; + f.meta_free = (HMFREE_FUN) erts_free; + f.meta_print = (HMPRINT_FUN) erts_print; atom_text_pos = NULL; atom_text_end = NULL; diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c index 4268e2d40a..cff476694c 100644 --- a/erts/emulator/beam/erl_fun.c +++ b/erts/emulator/beam/erl_fun.c @@ -66,6 +66,9 @@ erts_init_fun_table(void) f.cmp = (HCMP_FUN) fun_cmp; f.alloc = (HALLOC_FUN) fun_alloc; f.free = (HFREE_FUN) fun_free; + f.meta_alloc = (HMALLOC_FUN) erts_alloc; + f.meta_free = (HMFREE_FUN) erts_free; + f.meta_print = (HMPRINT_FUN) erts_print; hash_init(ERTS_ALC_T_FUN_TABLE, &erts_fun_table, "fun_table", 16, f); } diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 62a44f7129..e5bd0d58f0 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -793,10 +793,14 @@ void erts_init_node_tables(int dd_sec) rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ; rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED; - f.hash = (H_FUN) dist_table_hash; - f.cmp = (HCMP_FUN) dist_table_cmp; - f.alloc = (HALLOC_FUN) dist_table_alloc; - f.free = (HFREE_FUN) dist_table_free; + f.hash = (H_FUN) dist_table_hash; + f.cmp = (HCMP_FUN) dist_table_cmp; + f.alloc = (HALLOC_FUN) dist_table_alloc; + f.free = (HFREE_FUN) dist_table_free; + f.meta_alloc = (HMALLOC_FUN) erts_alloc; + f.meta_free = (HMFREE_FUN) erts_free; + f.meta_print = (HMPRINT_FUN) erts_print; + erts_this_dist_entry = erts_alloc(ERTS_ALC_T_DIST_ENTRY, sizeof(DistEntry)); dist_entries = 1; diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index 64278d2ea0..fb2f2a5407 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -41,6 +41,7 @@ #include "sys.h" #include "hash.h" +#include "erl_alloc.h" #include "erl_process.h" #include "erl_monitors.h" #include "erl_smp.h" diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c index 2420df36b5..581efe6eec 100644 --- a/erts/emulator/beam/export.c +++ b/erts/emulator/beam/export.c @@ -184,6 +184,9 @@ init_export_table(void) f.cmp = (HCMP_FUN) export_cmp; f.alloc = (HALLOC_FUN) export_alloc; f.free = (HFREE_FUN) export_free; + f.meta_alloc = (HMALLOC_FUN) erts_alloc; + f.meta_free = (HMFREE_FUN) erts_free; + f.meta_print = (HMPRINT_FUN) erts_print; for (i=0; ifun.meta_print(to, arg, "=hash_table:%s\n", hi.name); + h->fun.meta_print(to, arg, "size: %d\n", hi.size); + h->fun.meta_print(to, arg, "used: %d\n", hi.used); + h->fun.meta_print(to, arg, "objs: %d\n", hi.objs); + h->fun.meta_print(to, arg, "depth: %d\n", hi.depth); } @@ -123,22 +123,22 @@ hash_table_sz(Hash *h) ** init a pre allocated or static hash structure ** and allocate buckets. */ -Hash* hash_init(ErtsAlcType_t type, Hash* h, char* name, int size, HashFunctions fun) +Hash* hash_init(int type, Hash* h, char* name, int size, HashFunctions fun) { int sz; int ix = 0; - h->type = type; + h->meta_alloc_type = type; while (h_size_table[ix] != -1 && h_size_table[ix] < size) ix++; if (h_size_table[ix] == -1) - erl_exit(1, "panic: too large hash table size (%d)\n", size); + return NULL; size = h_size_table[ix]; sz = size*sizeof(HashBucket*); - h->bucket = (HashBucket**) erts_alloc(h->type, sz); + h->bucket = (HashBucket**) fun.meta_alloc(h->meta_alloc_type, sz); sys_memzero(h->bucket, sz); h->is_allocated = 0; @@ -155,11 +155,11 @@ Hash* hash_init(ErtsAlcType_t type, Hash* h, char* name, int size, HashFunctions /* ** Create a new hash table */ -Hash* hash_new(ErtsAlcType_t type, char* name, int size, HashFunctions fun) +Hash* hash_new(int type, char* name, int size, HashFunctions fun) { Hash* h; - h = erts_alloc(type, sizeof(Hash)); + h = fun.meta_alloc(type, sizeof(Hash)); h = hash_init(type, h, name, size, fun); h->is_allocated = 1; @@ -183,9 +183,9 @@ void hash_delete(Hash* h) b = b_next; } } - erts_free(h->type, h->bucket); + h->fun.meta_free(h->meta_alloc_type, h->bucket); if (h->is_allocated) - erts_free(h->type, (void*) h); + h->fun.meta_free(h->meta_alloc_type, (void*) h); } /* @@ -213,7 +213,7 @@ static void rehash(Hash* h, int grow) h->size80percent = (4*h->size)/5; sz = h->size*sizeof(HashBucket*); - new_bucket = (HashBucket **) erts_alloc(h->type, sz); + new_bucket = (HashBucket **) h->fun.meta_alloc(h->meta_alloc_type, sz); sys_memzero(new_bucket, sz); h->used = 0; @@ -230,7 +230,7 @@ static void rehash(Hash* h, int grow) b = b_next; } } - erts_free(h->type, (void *) h->bucket); + h->fun.meta_free(h->meta_alloc_type, (void *) h->bucket); h->bucket = new_bucket; } diff --git a/erts/emulator/beam/hash.h b/erts/emulator/beam/hash.h index 87fdb360e3..e98f6c32e1 100644 --- a/erts/emulator/beam/hash.h +++ b/erts/emulator/beam/hash.h @@ -29,14 +29,17 @@ #include "sys.h" #endif -#include "erl_alloc.h" - typedef unsigned long HashValue; +typedef struct hash Hash; typedef int (*HCMP_FUN)(void*, void*); typedef HashValue (*H_FUN)(void*); typedef void* (*HALLOC_FUN)(void*); typedef void (*HFREE_FUN)(void*); +/* Meta functions */ +typedef void* (*HMALLOC_FUN)(int,size_t); +typedef void (*HMFREE_FUN)(int,void*); +typedef int (*HMPRINT_FUN)(int,void*,char*, ...); /* ** This bucket must be placed in top of @@ -55,6 +58,9 @@ typedef struct hash_functions HCMP_FUN cmp; HALLOC_FUN alloc; HFREE_FUN free; + HMALLOC_FUN meta_alloc; + HMFREE_FUN meta_free; + HMPRINT_FUN meta_print; } HashFunctions; typedef struct { @@ -65,11 +71,11 @@ typedef struct { int depth; } HashInfo; -typedef struct hash +struct hash { HashFunctions fun; /* Function block */ int is_allocated; /* 0 iff hash structure is on stack or is static */ - ErtsAlcType_t type; + int meta_alloc_type; /* argument to pass to meta_alloc and meta_free */ char* name; /* Table name (static string, for debugging) */ int size; /* Number of slots */ int size20percent; /* 20 percent of number of slots */ @@ -77,10 +83,10 @@ typedef struct hash int ix; /* Size index in size table */ int used; /* Number of slots used */ HashBucket** bucket; /* Vector of bucket pointers (objects) */ -} Hash; +}; -Hash* hash_new(ErtsAlcType_t, char*, int, HashFunctions); -Hash* hash_init(ErtsAlcType_t, Hash*, char*, int, HashFunctions); +Hash* hash_new(int, char*, int, HashFunctions); +Hash* hash_init(int, Hash*, char*, int, HashFunctions); void hash_delete(Hash*); void hash_get_info(HashInfo*, Hash*); diff --git a/erts/emulator/beam/index.h b/erts/emulator/beam/index.h index 14fab41026..99b2bdfab0 100644 --- a/erts/emulator/beam/index.h +++ b/erts/emulator/beam/index.h @@ -30,6 +30,10 @@ #include "hash.h" #endif +#ifndef ERL_ALLOC_H__ +#include "erl_alloc.h" +#endif + typedef struct index_slot { HashBucket bucket; diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c index 86dd3b5aac..2db6f957c6 100644 --- a/erts/emulator/beam/module.c +++ b/erts/emulator/beam/module.c @@ -103,6 +103,9 @@ void init_module_table(void) f.cmp = (HCMP_FUN) module_cmp; f.alloc = (HALLOC_FUN) module_alloc; f.free = (HFREE_FUN) module_free; + f.meta_alloc = (HMALLOC_FUN) erts_alloc; + f.meta_free = (HMFREE_FUN) erts_free; + f.meta_print = (HMPRINT_FUN) erts_print; for (i = 0; i < ERTS_NUM_CODE_IX; i++) { erts_index_init(ERTS_ALC_T_MODULE_TABLE, &module_tables[i], "module_code", diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c index 7ade8bca0f..fdb6cbc813 100644 --- a/erts/emulator/beam/register.c +++ b/erts/emulator/beam/register.c @@ -151,6 +151,9 @@ void init_register_table(void) f.cmp = (HCMP_FUN) reg_cmp; f.alloc = (HALLOC_FUN) reg_alloc; f.free = (HFREE_FUN) reg_free; + f.meta_alloc = (HMALLOC_FUN) erts_alloc; + f.meta_free = (HMFREE_FUN) erts_free; + f.meta_print = (HMPRINT_FUN) erts_print; hash_init(ERTS_ALC_T_REG_TABLE, &process_reg, "process_reg", PREG_HASH_SIZE, f); diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index cc68e1f74d..70a57ba465 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -507,6 +507,9 @@ static void init_const_term_table(void) f.cmp = (HCMP_FUN) const_term_cmp; f.alloc = (HALLOC_FUN) const_term_alloc; f.free = (HFREE_FUN) NULL; + f.meta_alloc = (HMALLOC_FUN) erts_alloc; + f.meta_free = (HMFREE_FUN) erts_free; + f.meta_print = (HMPRINT_FUN) erts_print; hash_init(ERTS_ALC_T_HIPE, &const_term_table, "const_term_table", 97, f); } @@ -715,6 +718,9 @@ static void init_nbif_table(void) f.cmp = (HCMP_FUN) nbif_cmp; f.alloc = (HALLOC_FUN) nbif_alloc; f.free = NULL; + f.meta_alloc = (HMALLOC_FUN) erts_alloc; + f.meta_free = (HMFREE_FUN) erts_free; + f.meta_print = (HMPRINT_FUN) erts_print; hash_init(ERTS_ALC_T_NBIF_TABLE, &nbif_table, "nbif_table", 500, f); @@ -808,6 +814,9 @@ static void init_primop_table(void) f.cmp = (HCMP_FUN) primop_cmp; f.alloc = (HALLOC_FUN) primop_alloc; f.free = NULL; + f.meta_alloc = (HMALLOC_FUN) erts_alloc; + f.meta_free = (HMFREE_FUN) erts_free; + f.meta_print = (HMPRINT_FUN) erts_print; hash_init(ERTS_ALC_T_HIPE, &primop_table, "primop_table", 50, f); @@ -1826,6 +1835,9 @@ static void init_modinfo_table(void) f.cmp = (HCMP_FUN) modinfo_cmp; f.alloc = (HALLOC_FUN) modinfo_alloc; f.free = (HFREE_FUN) NULL; + f.meta_alloc = (HMALLOC_FUN) erts_alloc; + f.meta_free = (HMFREE_FUN) erts_free; + f.meta_print = (HMPRINT_FUN) erts_print; hash_init(ERTS_ALC_T_HIPE, &modinfo_table, "modinfo_table", 11, f); } -- cgit v1.2.3 From 37f057fca161373bf565ea1bf24fbe89d4946b48 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 14 Jul 2015 15:42:29 +0200 Subject: erts: Rename sys driver structs --- erts/emulator/sys/unix/sys.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 8d7da3e47e..5f7f07940d 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -143,7 +143,7 @@ typedef struct ErtsSysBlocking_ { /* This data is shared by these drivers - initialized by spawn_init() */ -static struct driver_data { +typedef struct driver_data { ErlDrvPort port_num; int ofd, packet_bytes; ErtsSysReportExit *report_exit; @@ -152,7 +152,9 @@ static struct driver_data { int status; int terminating; ErtsSysBlocking *blocking; -} *driver_data; /* indexed by fd */ +} ErtsSysDriverData; + +static ErtsSysDriverData *driver_data; /* indexed by fd */ static ErtsSysReportExit *report_exit_list; #if CHLDWTHR && !defined(ERTS_SMP) @@ -257,14 +259,16 @@ static volatile int children_died; #endif -static struct fd_data { +typedef struct ErtsSysFdData { char pbuf[4]; /* hold partial packet bytes */ int psz; /* size of pbuf */ char *buf; char *cpos; int sz; int remain; /* for input on fd */ -} *fd_data; /* indexed by fd */ +} ErtsSysFdData; + +static ErtsSysFdData *fd_data; /* indexed by fd */ /* static FUNCTION(int, write_fill, (int, char*, int)); unused? */ static void note_child_death(int, int); @@ -1233,7 +1237,7 @@ static RETSIGTYPE onchld(int signum) #endif } -static int set_blocking_data(struct driver_data *dd) { +static int set_blocking_data(ErtsSysDriverData *dd) { dd->blocking = erts_alloc(ERTS_ALC_T_SYS_BLOCKING, sizeof(ErtsSysBlocking)); @@ -1329,10 +1333,10 @@ static int spawn_init() #endif sys_signal(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */ - driver_data = (struct driver_data *) - erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data)); + driver_data = (ErtsSysDriverData *) + erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(ErtsSysDriverData)); erts_smp_atomic_add_nob(&sys_misc_mem_sz, - max_files * sizeof(struct driver_data)); + max_files * sizeof(ErtsSysDriverData)); for (i = 0; i < max_files; i++) driver_data[i].pid = -1; @@ -2523,7 +2527,7 @@ static void fd_async(void *async_data) { int res; - struct driver_data *dd = (struct driver_data*)async_data; + ErtsSysDriverData *dd = (ErtsSysDriverData*)async_data; SysIOVec *iov0; SysIOVec *iov; int iovlen; @@ -2556,7 +2560,7 @@ fd_async(void *async_data) void fd_ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data) { - struct driver_data *dd = (struct driver_data *)thread_data; + ErtsSysDriverData *dd = (ErtsSysDriverData *)thread_data; ErlDrvPort port_num = dd->port_num; ASSERT(dd->blocking); @@ -2738,10 +2742,10 @@ erts_sys_unsetenv(char *key) void sys_init_io(void) { - fd_data = (struct fd_data *) - erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(struct fd_data)); + fd_data = (ErtsSysFdData *) + erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(ErtsSysFdData)); erts_smp_atomic_add_nob(&sys_misc_mem_sz, - max_files * sizeof(struct fd_data)); + max_files * sizeof(ErtsSysFdData)); } #if (0) /* unused? */ -- cgit v1.2.3 From fdc2ce62a041d34cd99413e278e7dd8ff79832d1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 26 Aug 2015 11:18:29 +0200 Subject: erts: Change name of child_setup to erl_child_setup --- erts/emulator/Makefile.in | 2 +- erts/emulator/sys/unix/sys.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index c5080d5b5d..802de21ef0 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -398,7 +398,7 @@ EMULATOR_EXECUTABLE = beam$(TF_MARKER).dll else EMULATOR_EXECUTABLE = beam$(TF_MARKER) endif -CS_EXECUTABLE = child_setup$(TYPEMARKER) +CS_EXECUTABLE = erl_child_setup$(TYPEMARKER) # ---------------------------------------------------------------------- diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 5f7f07940d..1cb7f72a2b 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -195,10 +195,8 @@ extern void erl_crash_dump(char* file, int line, char* fmt, ...); #define ERL_BUILD_TYPE_MARKER #endif -#define CHILD_SETUP_PROG_NAME "child_setup" ERL_BUILD_TYPE_MARKER -#if !DISABLE_VFORK +#define CHILD_SETUP_PROG_NAME "erl_child_setup" ERL_BUILD_TYPE_MARKER static char *child_setup_prog; -#endif #ifdef DEBUG static int debug_log = 0; -- cgit v1.2.3 From 6089f3c961a981b6bacb6c1590386bb67905ff23 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 26 Aug 2015 14:43:20 +0200 Subject: erts: Move sys drivers to another file --- erts/emulator/Makefile.in | 1 + erts/emulator/sys/unix/erl_unix_sys.h | 16 + erts/emulator/sys/unix/sys.c | 1914 +------------------------------ erts/emulator/sys/unix/sys_drivers.c | 1983 +++++++++++++++++++++++++++++++++ 4 files changed, 2004 insertions(+), 1910 deletions(-) create mode 100644 erts/emulator/sys/unix/sys_drivers.c (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 802de21ef0..69f28b0c10 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -798,6 +798,7 @@ OS_OBJS = \ else OS_OBJS = \ $(OBJDIR)/sys.o \ + $(OBJDIR)/sys_drivers.o \ $(OBJDIR)/driver_tab.o \ $(OBJDIR)/unix_efile.o \ $(OBJDIR)/gzio.o \ diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 8d4e98bf3a..a11a44c259 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -470,4 +470,20 @@ extern jmp_buf erts_sys_sigsegv_jmp; } while(0) #endif +#ifdef USE_THREADS +# ifdef ENABLE_CHILD_WAITER_THREAD +# define CHLDWTHR ENABLE_CHILD_WAITER_THREAD +# else +# define CHLDWTHR 0 +# endif +# define FDBLOCK 1 +#else +# define CHLDWTHR 0 +# define FDBLOCK 0 +#endif + +#define ERTS_SYS_SUSPEND_SIGNAL SIGUSR2 + +int check_children(void); + #endif /* #ifndef _ERL_UNIX_SYS_H */ diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 1cb7f72a2b..503ef5c2f6 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -67,7 +67,7 @@ #include "erl_mseg.h" extern char **environ; -static erts_smp_rwmtx_t environ_rwmtx; +erts_smp_rwmtx_t environ_rwmtx; #define MAX_VSIZE 16 /* Max number of entries allowed in an I/O * vector sock_sendv(). @@ -76,91 +76,12 @@ static erts_smp_rwmtx_t environ_rwmtx; * Don't need global.h, but bif_table.h (included by bif.h), * won't compile otherwise */ -#include "global.h" +#include "global.h" #include "bif.h" -#include "erl_sys_driver.h" #include "erl_check_io.h" #include "erl_cpu_topology.h" -#ifndef DISABLE_VFORK -#define DISABLE_VFORK 0 -#endif - -#if defined IOV_MAX -#define MAXIOV IOV_MAX -#elif defined UIO_MAXIOV -#define MAXIOV UIO_MAXIOV -#else -#define MAXIOV 16 -#endif - -#ifdef USE_THREADS -# ifdef ENABLE_CHILD_WAITER_THREAD -# define CHLDWTHR ENABLE_CHILD_WAITER_THREAD -# else -# define CHLDWTHR 0 -# endif -# define FDBLOCK 1 -#else -# define CHLDWTHR 0 -# define FDBLOCK 0 -#endif -/* - * [OTP-3906] - * Solaris signal management gets confused when threads are used and a - * lot of child processes dies. The confusion results in that SIGCHLD - * signals aren't delivered to the emulator which in turn results in - * a lot of defunct processes in the system. - * - * The problem seems to appear when a signal is frequently - * blocked/unblocked at the same time as the signal is frequently - * propagated. The child waiter thread is a workaround for this problem. - * The SIGCHLD signal is always blocked (in all threads), and the child - * waiter thread fetches the signal by a call to sigwait(). See - * child_waiter(). - */ - -typedef struct ErtsSysReportExit_ ErtsSysReportExit; -struct ErtsSysReportExit_ { - ErtsSysReportExit *next; - Eterm port; - int pid; - int ifd; - int ofd; -#if CHLDWTHR && !defined(ERTS_SMP) - int status; -#endif -}; - -/* Used by the fd driver iff the fd could not be set to non-blocking */ -typedef struct ErtsSysBlocking_ { - ErlDrvPDL pdl; - int res; - int err; - unsigned int pkey; -} ErtsSysBlocking; - - -/* This data is shared by these drivers - initialized by spawn_init() */ -typedef struct driver_data { - ErlDrvPort port_num; - int ofd, packet_bytes; - ErtsSysReportExit *report_exit; - int pid; - int alive; - int status; - int terminating; - ErtsSysBlocking *blocking; -} ErtsSysDriverData; - -static ErtsSysDriverData *driver_data; /* indexed by fd */ - -static ErtsSysReportExit *report_exit_list; -#if CHLDWTHR && !defined(ERTS_SMP) -static ErtsSysReportExit *report_exit_transit_list; -#endif - extern int driver_interrupt(int, int); extern void do_break(void); @@ -172,32 +93,6 @@ extern void erts_sys_init_float(void); extern void erl_crash_dump(char* file, int line, char* fmt, ...); -#define DIR_SEPARATOR_CHAR '/' - -#if defined(__ANDROID__) -#define SHELL "/system/bin/sh" -#else -#define SHELL "/bin/sh" -#endif /* __ANDROID__ */ - - -#if defined(DEBUG) -#define ERL_BUILD_TYPE_MARKER ".debug" -#elif defined(PURIFY) -#define ERL_BUILD_TYPE_MARKER ".purify" -#elif defined(QUANTIFY) -#define ERL_BUILD_TYPE_MARKER ".quantify" -#elif defined(PURECOV) -#define ERL_BUILD_TYPE_MARKER ".purecov" -#elif defined(VALGRIND) -#define ERL_BUILD_TYPE_MARKER ".valgrind" -#else /* opt */ -#define ERL_BUILD_TYPE_MARKER -#endif - -#define CHILD_SETUP_PROG_NAME "erl_child_setup" ERL_BUILD_TYPE_MARKER -static char *child_setup_prog; - #ifdef DEBUG static int debug_log = 0; #endif @@ -220,61 +115,18 @@ static volatile int have_prepared_crash_dump; (have_prepared_crash_dump++) #endif -static erts_smp_atomic_t sys_misc_mem_sz; +erts_smp_atomic_t sys_misc_mem_sz; #if defined(ERTS_SMP) static void smp_sig_notify(char c); static int sig_notify_fds[2] = {-1, -1}; static int sig_suspend_fds[2] = {-1, -1}; -#define ERTS_SYS_SUSPEND_SIGNAL SIGUSR2 #endif jmp_buf erts_sys_sigsegv_jmp; -#if CHLDWTHR || defined(ERTS_SMP) -erts_mtx_t chld_stat_mtx; -#endif -#if CHLDWTHR -static erts_tid_t child_waiter_tid; -/* chld_stat_mtx is used to protect against concurrent accesses - of the driver_data fields pid, alive, and status. */ -erts_cnd_t chld_stat_cnd; -static long children_alive; -#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx) -#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx) -#define CHLD_STAT_WAIT erts_cnd_wait(&chld_stat_cnd, &chld_stat_mtx) -#define CHLD_STAT_SIGNAL erts_cnd_signal(&chld_stat_cnd) -#elif defined(ERTS_SMP) /* ------------------------------------------------- */ -#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx) -#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx) - -#else /* ------------------------------------------------------------------- */ -#define CHLD_STAT_LOCK -#define CHLD_STAT_UNLOCK -static volatile int children_died; -#endif - - -typedef struct ErtsSysFdData { - char pbuf[4]; /* hold partial packet bytes */ - int psz; /* size of pbuf */ - char *buf; - char *cpos; - int sz; - int remain; /* for input on fd */ -} ErtsSysFdData; - -static ErtsSysFdData *fd_data; /* indexed by fd */ - -/* static FUNCTION(int, write_fill, (int, char*, int)); unused? */ -static void note_child_death(int, int); - -#if CHLDWTHR -static void* child_waiter(void *); -#endif - static int crashdump_companion_cube_fd = -1; /********************* General functions ****************************/ @@ -559,8 +411,6 @@ erts_sys_pre_init(void) erts_thr_init(&eid); - report_exit_list = NULL; - #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_init(); #endif @@ -571,17 +421,6 @@ erts_sys_pre_init(void) #ifdef USE_THREADS -#if CHLDWTHR || defined(ERTS_SMP) - erts_mtx_init(&chld_stat_mtx, "child_status"); -#endif -#if CHLDWTHR -#ifndef ERTS_SMP - report_exit_transit_list = NULL; -#endif - erts_cnd_init(&chld_stat_cnd); - children_alive = 0; -#endif - #ifdef ERTS_SMP erts_smp_atomic32_init_nob(&erts_break_requested, 0); erts_smp_atomic32_init_nob(&erts_got_sigusr1, 0); @@ -591,9 +430,6 @@ erts_sys_pre_init(void) erts_got_sigusr1 = 0; have_prepared_crash_dump = 0; #endif -#if !CHLDWTHR && !defined(ERTS_SMP) - children_died = 0; -#endif #endif /* USE_THREADS */ @@ -630,39 +466,6 @@ erts_sys_pre_init(void) void erl_sys_init(void) { -#if !DISABLE_VFORK - { - int res; - char bindir[MAXPATHLEN]; - size_t bindirsz = sizeof(bindir); - Uint csp_path_sz; - - res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz); - if (res != 0) { - if (res < 0) - erl_exit(-1, - "Environment variable BINDIR is not set\n"); - if (res > 0) - erl_exit(-1, - "Value of environment variable BINDIR is too large\n"); - } - if (bindir[0] != DIR_SEPARATOR_CHAR) - erl_exit(-1, - "Environment variable BINDIR does not contain an" - " absolute path\n"); - csp_path_sz = (strlen(bindir) - + 1 /* DIR_SEPARATOR_CHAR */ - + sizeof(CHILD_SETUP_PROG_NAME) - + 1); - child_setup_prog = erts_alloc(ERTS_ALC_T_CS_PROG_PATH, csp_path_sz); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, csp_path_sz); - erts_snprintf(child_setup_prog, csp_path_sz, - "%s%c%s", - bindir, - DIR_SEPARATOR_CHAR, - CHILD_SETUP_PROG_NAME); - } -#endif #ifdef USE_SETLINEBUF setlinebuf(stdout); @@ -980,43 +783,6 @@ int sys_max_files(void) return(max_files); } -static void block_signals(void) -{ -#if !CHLDWTHR - sys_sigblock(SIGCHLD); -#endif -#ifndef ERTS_SMP - sys_sigblock(SIGINT); -#ifndef ETHR_UNUSABLE_SIGUSRX - sys_sigblock(SIGUSR1); -#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ -#endif /* #ifndef ERTS_SMP */ - -#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX) - sys_sigblock(ERTS_SYS_SUSPEND_SIGNAL); -#endif - -} - -static void unblock_signals(void) -{ - /* Update erl_child_setup.c if changed */ -#if !CHLDWTHR - sys_sigrelease(SIGCHLD); -#endif -#ifndef ERTS_SMP - sys_sigrelease(SIGINT); -#ifndef ETHR_UNUSABLE_SIGUSRX - sys_sigrelease(SIGUSR1); -#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ -#endif /* #ifndef ERTS_SMP */ - -#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX) - sys_sigrelease(ERTS_SYS_SUSPEND_SIGNAL); -#endif - -} - /************************** OS info *******************************/ /* Used by erlang:info/1. */ @@ -1104,1502 +870,6 @@ void fini_getenv_state(GETENV_STATE *state) erts_smp_rwmtx_runlock(&environ_rwmtx); } - -/************************** Port I/O *******************************/ - - - -/* I. Common stuff */ - -/* - * Decreasing the size of it below 16384 is not allowed. - */ - -/* II. The spawn/fd/vanilla drivers */ - -#define ERTS_SYS_READ_BUF_SZ (64*1024) - -/* Driver interfaces */ -static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*); -static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*); -#if FDBLOCK -static void fd_async(void *); -static void fd_ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data); -#endif -static ErlDrvSSizeT fd_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT, - char **, ErlDrvSizeT); -static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*); -static int spawn_init(void); -static void fd_stop(ErlDrvData); -static void fd_flush(ErlDrvData); -static void stop(ErlDrvData); -static void ready_input(ErlDrvData, ErlDrvEvent); -static void ready_output(ErlDrvData, ErlDrvEvent); -static void output(ErlDrvData, char*, ErlDrvSizeT); -static void outputv(ErlDrvData, ErlIOVec*); -static void stop_select(ErlDrvEvent, void*); - -struct erl_drv_entry spawn_driver_entry = { - spawn_init, - spawn_start, - stop, - output, - ready_input, - ready_output, - "spawn", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, - ERL_DRV_FLAG_USE_PORT_LOCKING, - NULL, NULL, - stop_select -}; -struct erl_drv_entry fd_driver_entry = { - NULL, - fd_start, - fd_stop, - output, - ready_input, - ready_output, - "fd", - NULL, - NULL, - fd_control, - NULL, - outputv, -#if FDBLOCK - fd_ready_async, /* ready_async */ -#else - NULL, -#endif - fd_flush, /* flush */ - NULL, /* call */ - NULL, /* event */ - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, - 0, /* ERL_DRV_FLAGs */ - NULL, /* handle2 */ - NULL, /* process_exit */ - stop_select -}; -struct erl_drv_entry vanilla_driver_entry = { - NULL, - vanilla_start, - stop, - output, - ready_input, - ready_output, - "vanilla", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, /* flush */ - NULL, /* call */ - NULL, /* event */ - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, - 0, /* ERL_DRV_FLAGs */ - NULL, /* handle2 */ - NULL, /* process_exit */ - stop_select -}; - -/* Handle SIGCHLD signals. */ -#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL)) -static RETSIGTYPE onchld(void) -#else -static RETSIGTYPE onchld(int signum) -#endif -{ -#if CHLDWTHR - ASSERT(0); /* We should *never* catch a SIGCHLD signal */ -#elif defined(ERTS_SMP) - smp_sig_notify('C'); -#else - children_died = 1; - ERTS_CHK_IO_AS_INTR(); /* Make sure we don't sleep in poll */ -#endif -} - -static int set_blocking_data(ErtsSysDriverData *dd) { - - dd->blocking = erts_alloc(ERTS_ALC_T_SYS_BLOCKING, sizeof(ErtsSysBlocking)); - - erts_smp_atomic_add_nob(&sys_misc_mem_sz, sizeof(ErtsSysBlocking)); - - dd->blocking->pdl = driver_pdl_create(dd->port_num); - dd->blocking->res = 0; - dd->blocking->err = 0; - dd->blocking->pkey = driver_async_port_key(dd->port_num); - - return 1; -} - -static int set_driver_data(ErlDrvPort port_num, - int ifd, - int ofd, - int packet_bytes, - int read_write, - int exit_status, - int pid, - int is_blocking) -{ - Port *prt; - ErtsSysReportExit *report_exit; - - if (!exit_status) - report_exit = NULL; - else { - report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT, - sizeof(ErtsSysReportExit)); - report_exit->next = report_exit_list; - report_exit->port = erts_drvport2id(port_num); - report_exit->pid = pid; - report_exit->ifd = read_write & DO_READ ? ifd : -1; - report_exit->ofd = read_write & DO_WRITE ? ofd : -1; -#if CHLDWTHR && !defined(ERTS_SMP) - report_exit->status = 0; -#endif - report_exit_list = report_exit; - } - - prt = erts_drvport2port(port_num); - if (prt != ERTS_INVALID_ERL_DRV_PORT) - prt->os_pid = pid; - - if (read_write & DO_READ) { - driver_data[ifd].packet_bytes = packet_bytes; - driver_data[ifd].port_num = port_num; - driver_data[ifd].report_exit = report_exit; - driver_data[ifd].pid = pid; - driver_data[ifd].alive = 1; - driver_data[ifd].status = 0; - driver_data[ifd].terminating = 0; - driver_data[ifd].blocking = NULL; - if (read_write & DO_WRITE) { - driver_data[ifd].ofd = ofd; - if (is_blocking && FDBLOCK) - if (!set_blocking_data(driver_data+ifd)) - return -1; - if (ifd != ofd) - driver_data[ofd] = driver_data[ifd]; /* structure copy */ - } else { /* DO_READ only */ - driver_data[ifd].ofd = -1; - } - (void) driver_select(port_num, ifd, (ERL_DRV_READ|ERL_DRV_USE), 1); - return(ifd); - } else { /* DO_WRITE only */ - driver_data[ofd].packet_bytes = packet_bytes; - driver_data[ofd].port_num = port_num; - driver_data[ofd].report_exit = report_exit; - driver_data[ofd].ofd = ofd; - driver_data[ofd].pid = pid; - driver_data[ofd].alive = 1; - driver_data[ofd].status = 0; - driver_data[ofd].terminating = 0; - driver_data[ofd].blocking = NULL; - if (is_blocking && FDBLOCK) - if (!set_blocking_data(driver_data+ofd)) - return -1; - return(ofd); - } -} - -static int spawn_init() -{ - int i; -#if CHLDWTHR - erts_thr_opts_t thr_opts = ERTS_THR_OPTS_DEFAULT_INITER; - - thr_opts.detached = 0; - thr_opts.suggested_stack_size = 0; /* Smallest possible */ - thr_opts.name = "child_waiter"; -#endif - - sys_signal(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */ - driver_data = (ErtsSysDriverData *) - erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(ErtsSysDriverData)); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, - max_files * sizeof(ErtsSysDriverData)); - - for (i = 0; i < max_files; i++) - driver_data[i].pid = -1; - -#if CHLDWTHR - sys_sigblock(SIGCHLD); -#endif - - sys_signal(SIGCHLD, onchld); /* Reap children */ - -#if CHLDWTHR - erts_thr_create(&child_waiter_tid, child_waiter, NULL, &thr_opts); -#endif - - return 1; -} - -static void close_pipes(int ifd[2], int ofd[2], int read_write) -{ - if (read_write & DO_READ) { - (void) close(ifd[0]); - (void) close(ifd[1]); - } - if (read_write & DO_WRITE) { - (void) close(ofd[0]); - (void) close(ofd[1]); - } -} - -static void init_fd_data(int fd, ErlDrvPort port_num) -{ - fd_data[fd].buf = NULL; - fd_data[fd].cpos = NULL; - fd_data[fd].remain = 0; - fd_data[fd].sz = 0; - fd_data[fd].psz = 0; -} - -static char **build_unix_environment(char *block) -{ - int i; - int j; - int len; - char *cp; - char **cpp; - char** old_env; - - ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx)); - - cp = block; - len = 0; - while (*cp != '\0') { - cp += strlen(cp) + 1; - len++; - } - old_env = environ; - while (*old_env++ != NULL) { - len++; - } - - cpp = (char **) erts_alloc_fnf(ERTS_ALC_T_ENVIRONMENT, - sizeof(char *) * (len+1)); - if (cpp == NULL) { - return NULL; - } - - cp = block; - len = 0; - while (*cp != '\0') { - cpp[len] = cp; - cp += strlen(cp) + 1; - len++; - } - - i = len; - for (old_env = environ; *old_env; old_env++) { - char* old = *old_env; - - for (j = 0; j < len; j++) { - char *s, *t; - - s = cpp[j]; - t = old; - while (*s == *t && *s != '=') { - s++, t++; - } - if (*s == '=' && *t == '=') { - break; - } - } - - if (j == len) { /* New version not found */ - cpp[len++] = old; - } - } - - for (j = 0; j < i; ) { - size_t last = strlen(cpp[j])-1; - if (cpp[j][last] == '=' && strchr(cpp[j], '=') == cpp[j]+last) { - cpp[j] = cpp[--len]; - if (len < i) { - i--; - } else { - j++; - } - } - else { - j++; - } - } - - cpp[len] = NULL; - return cpp; -} - -/* - [arndt] In most Unix systems, including Solaris 2.5, 'fork' allocates memory - in swap space for the child of a 'fork', whereas 'vfork' does not do this. - The natural call to use here is therefore 'vfork'. Due to a bug in - 'vfork' in Solaris 2.5 (apparently fixed in 2.6), using 'vfork' - can be dangerous in what seems to be these circumstances: - If the child code under a vfork sets the signal action to SIG_DFL - (or SIG_IGN) - for any signal which was previously set to a signal handler, the - state of the parent is clobbered, so that the later arrival of - such a signal yields a sigsegv in the parent. If the signal was - not set to a signal handler, but ignored, all seems to work. - If you change the forking code below, beware of this. - */ - -static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) -{ -#define CMD_LINE_PREFIX_STR "exec " -#define CMD_LINE_PREFIX_STR_SZ (sizeof(CMD_LINE_PREFIX_STR) - 1) - - int ifd[2], ofd[2], len, pid, i; - char **volatile new_environ; /* volatile since a vfork() then cannot - cause 'new_environ' to be clobbered - in the parent process. */ - int saved_errno; - long res; - char *cmd_line; -#ifndef QNX - int unbind; -#endif -#if !DISABLE_VFORK - int no_vfork; - size_t no_vfork_sz = sizeof(no_vfork); - - no_vfork = (erts_sys_getenv_raw("ERL_NO_VFORK", - (char *) &no_vfork, - &no_vfork_sz) >= 0); -#endif - - switch (opts->read_write) { - case DO_READ: - if (pipe(ifd) < 0) - return ERL_DRV_ERROR_ERRNO; - if (ifd[0] >= max_files) { - close_pipes(ifd, ofd, opts->read_write); - errno = EMFILE; - return ERL_DRV_ERROR_ERRNO; - } - ofd[1] = -1; /* keep purify happy */ - break; - case DO_WRITE: - if (pipe(ofd) < 0) return ERL_DRV_ERROR_ERRNO; - if (ofd[1] >= max_files) { - close_pipes(ifd, ofd, opts->read_write); - errno = EMFILE; - return ERL_DRV_ERROR_ERRNO; - } - ifd[0] = -1; /* keep purify happy */ - break; - case DO_READ|DO_WRITE: - if (pipe(ifd) < 0) return ERL_DRV_ERROR_ERRNO; - errno = EMFILE; /* default for next two conditions */ - if (ifd[0] >= max_files || pipe(ofd) < 0) { - close_pipes(ifd, ofd, DO_READ); - return ERL_DRV_ERROR_ERRNO; - } - if (ofd[1] >= max_files) { - close_pipes(ifd, ofd, opts->read_write); - errno = EMFILE; - return ERL_DRV_ERROR_ERRNO; - } - break; - default: - ASSERT(0); - return ERL_DRV_ERROR_GENERAL; - } - - if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { - /* started with spawn_executable, not with spawn */ - len = strlen(name); - cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP, len + 1); - if (!cmd_line) { - close_pipes(ifd, ofd, opts->read_write); - errno = ENOMEM; - return ERL_DRV_ERROR_ERRNO; - } - memcpy((void *) cmd_line,(void *) name, len); - cmd_line[len] = '\0'; - if (access(cmd_line,X_OK) != 0) { - int save_errno = errno; - erts_free(ERTS_ALC_T_TMP, cmd_line); - errno = save_errno; - return ERL_DRV_ERROR_ERRNO; - } - } else { - /* make the string suitable for giving to "sh" */ - len = strlen(name); - cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP, - CMD_LINE_PREFIX_STR_SZ + len + 1); - if (!cmd_line) { - close_pipes(ifd, ofd, opts->read_write); - errno = ENOMEM; - return ERL_DRV_ERROR_ERRNO; - } - memcpy((void *) cmd_line, - (void *) CMD_LINE_PREFIX_STR, - CMD_LINE_PREFIX_STR_SZ); - memcpy((void *) (cmd_line + CMD_LINE_PREFIX_STR_SZ), (void *) name, len); - cmd_line[CMD_LINE_PREFIX_STR_SZ + len] = '\0'; - } - - erts_smp_rwmtx_rlock(&environ_rwmtx); - - if (opts->envir == NULL) { - new_environ = environ; - } else if ((new_environ = build_unix_environment(opts->envir)) == NULL) { - erts_smp_rwmtx_runlock(&environ_rwmtx); - erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); - errno = ENOMEM; - return ERL_DRV_ERROR_ERRNO; - } - -#ifndef QNX - /* Block child from SIGINT and SIGUSR1. Must be before fork() - to be safe. */ - block_signals(); - - CHLD_STAT_LOCK; - - unbind = erts_sched_bind_atfork_prepare(); - -#if !DISABLE_VFORK - /* See fork/vfork discussion before this function. */ - if (no_vfork) { -#endif - - DEBUGF(("Using fork\n")); - pid = fork(); - - if (pid == 0) { - /* The child! Setup child... */ - - if (erts_sched_bind_atfork_child(unbind) != 0) - goto child_error; - - /* OBSERVE! - * Keep child setup after vfork() (implemented below and in - * erl_child_setup.c) up to date if changes are made here. - */ - - if (opts->use_stdio) { - if (opts->read_write & DO_READ) { - /* stdout for process */ - if (dup2(ifd[1], 1) < 0) - goto child_error; - if(opts->redir_stderr) - /* stderr for process */ - if (dup2(ifd[1], 2) < 0) - goto child_error; - } - if (opts->read_write & DO_WRITE) - /* stdin for process */ - if (dup2(ofd[0], 0) < 0) - goto child_error; - } - else { /* XXX will fail if ofd[0] == 4 (unlikely..) */ - if (opts->read_write & DO_READ) - if (dup2(ifd[1], 4) < 0) - goto child_error; - if (opts->read_write & DO_WRITE) - if (dup2(ofd[0], 3) < 0) - goto child_error; - } - -#if defined(HAVE_CLOSEFROM) - closefrom(opts->use_stdio ? 3 : 5); -#else - for (i = opts->use_stdio ? 3 : 5; i < max_files; i++) - (void) close(i); -#endif - - if (opts->wd && chdir(opts->wd) < 0) - goto child_error; - -#if defined(USE_SETPGRP_NOARGS) /* SysV */ - (void) setpgrp(); -#elif defined(USE_SETPGRP) /* BSD */ - (void) setpgrp(0, getpid()); -#else /* POSIX */ - (void) setsid(); -#endif - - unblock_signals(); - - if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { - if (opts->argv == NULL) { - execle(cmd_line,cmd_line,(char *) NULL, new_environ); - } else { - if (opts->argv[0] == erts_default_arg0) { - opts->argv[0] = cmd_line; - } - execve(cmd_line, opts->argv, new_environ); - if (opts->argv[0] == cmd_line) { - opts->argv[0] = erts_default_arg0; - } - } - } else { - execle(SHELL, "sh", "-c", cmd_line, (char *) NULL, new_environ); - } - child_error: - _exit(1); - } -#if !DISABLE_VFORK - } -#define ENOUGH_BYTES (44) - else { /* Use vfork() */ - char **cs_argv= erts_alloc(ERTS_ALC_T_TMP,(CS_ARGV_NO_OF_ARGS + 1)* - sizeof(char *)); - char fd_close_range[ENOUGH_BYTES]; /* 44 bytes are enough to */ - char dup2_op[CS_ARGV_NO_OF_DUP2_OPS][ENOUGH_BYTES]; /* hold any "%d:%d" string */ - /* on a 64-bit machine. */ - - /* Setup argv[] for the child setup program (implemented in - erl_child_setup.c) */ - i = 0; - if (opts->use_stdio) { - if (opts->read_write & DO_READ){ - /* stdout for process */ - erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 1); - if(opts->redir_stderr) - /* stderr for process */ - erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 2); - } - if (opts->read_write & DO_WRITE) - /* stdin for process */ - erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ofd[0], 0); - } else { /* XXX will fail if ofd[0] == 4 (unlikely..) */ - if (opts->read_write & DO_READ) - erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 4); - if (opts->read_write & DO_WRITE) - erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ofd[0], 3); - } - for (; i < CS_ARGV_NO_OF_DUP2_OPS; i++) - strcpy(&dup2_op[i][0], "-"); - erts_snprintf(fd_close_range, ENOUGH_BYTES, "%d:%d", opts->use_stdio ? 3 : 5, max_files-1); - - cs_argv[CS_ARGV_PROGNAME_IX] = child_setup_prog; - cs_argv[CS_ARGV_WD_IX] = opts->wd ? opts->wd : "."; - cs_argv[CS_ARGV_UNBIND_IX] = erts_sched_bind_atvfork_child(unbind); - cs_argv[CS_ARGV_FD_CR_IX] = fd_close_range; - for (i = 0; i < CS_ARGV_NO_OF_DUP2_OPS; i++) - cs_argv[CS_ARGV_DUP2_OP_IX(i)] = &dup2_op[i][0]; - - if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { - int num = 0; - int j = 0; - if (opts->argv != NULL) { - for(; opts->argv[num] != NULL; ++num) - ; - } - cs_argv = erts_realloc(ERTS_ALC_T_TMP,cs_argv, (CS_ARGV_NO_OF_ARGS + 1 + num + 1) * sizeof(char *)); - cs_argv[CS_ARGV_CMD_IX] = "-"; - cs_argv[CS_ARGV_NO_OF_ARGS] = cmd_line; - if (opts->argv != NULL) { - for (;opts->argv[j] != NULL; ++j) { - if (opts->argv[j] == erts_default_arg0) { - cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = cmd_line; - } else { - cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = opts->argv[j]; - } - } - } - cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = NULL; - } else { - cs_argv[CS_ARGV_CMD_IX] = cmd_line; /* Command */ - cs_argv[CS_ARGV_NO_OF_ARGS] = NULL; - } - DEBUGF(("Using vfork\n")); - pid = vfork(); - - if (pid == 0) { - /* The child! */ - - /* Observe! - * OTP-4389: The child setup program (implemented in - * erl_child_setup.c) will perform the necessary setup of the - * child before it execs to the user program. This because - * vfork() only allow an *immediate* execve() or _exit() in the - * child. - */ - execve(child_setup_prog, cs_argv, new_environ); - _exit(1); - } - erts_free(ERTS_ALC_T_TMP,cs_argv); - } -#undef ENOUGH_BYTES -#endif - - erts_sched_bind_atfork_parent(unbind); - - if (pid == -1) { - saved_errno = errno; - CHLD_STAT_UNLOCK; - erts_smp_rwmtx_runlock(&environ_rwmtx); - erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); - unblock_signals(); - close_pipes(ifd, ofd, opts->read_write); - errno = saved_errno; - return ERL_DRV_ERROR_ERRNO; - } -#else /* QNX */ - if (opts->use_stdio) { - if (opts->read_write & DO_READ) - qnx_spawn_options.iov[1] = ifd[1]; /* stdout for process */ - if (opts->read_write & DO_WRITE) - qnx_spawn_options.iov[0] = ofd[0]; /* stdin for process */ - } - else { - if (opts->read_write & DO_READ) - qnx_spawn_options.iov[4] = ifd[1]; - if (opts->read_write & DO_WRITE) - qnx_spawn_options.iov[3] = ofd[0]; - } - /* Close fds on exec */ - for (i = 3; i < max_files; i++) - fcntl(i, F_SETFD, 1); - - qnx_spawn_options.flags = _SPAWN_SETSID; - if ((pid = spawnl(P_NOWAIT, SHELL, SHELL, "-c", cmd_line, - (char *) 0)) < 0) { - erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); - reset_qnx_spawn(); - erts_smp_rwmtx_runlock(&environ_rwmtx); - close_pipes(ifd, ofd, opts->read_write); - return ERL_DRV_ERROR_GENERAL; - } - reset_qnx_spawn(); -#endif /* QNX */ - - erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); - - if (new_environ != environ) - erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); - - if (opts->read_write & DO_READ) - (void) close(ifd[1]); - if (opts->read_write & DO_WRITE) - (void) close(ofd[0]); - - if (opts->read_write & DO_READ) { - SET_NONBLOCKING(ifd[0]); - init_fd_data(ifd[0], port_num); - } - if (opts->read_write & DO_WRITE) { - SET_NONBLOCKING(ofd[1]); - init_fd_data(ofd[1], port_num); - } - - res = set_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes, - opts->read_write, opts->exit_status, pid, 0); - /* Don't unblock SIGCHLD until now, since the call above must - first complete putting away the info about our new subprocess. */ - unblock_signals(); - -#if CHLDWTHR - ASSERT(children_alive >= 0); - - if (!(children_alive++)) - CHLD_STAT_SIGNAL; /* Wake up child waiter thread if no children - was alive before we fork()ed ... */ -#endif - /* Don't unlock chld_stat_mtx until now of the same reason as above */ - CHLD_STAT_UNLOCK; - - erts_smp_rwmtx_runlock(&environ_rwmtx); - - return (ErlDrvData)res; -#undef CMD_LINE_PREFIX_STR -#undef CMD_LINE_PREFIX_STR_SZ -} - -#ifdef QNX -static reset_qnx_spawn() -{ - int i; - - /* Reset qnx_spawn_options */ - qnx_spawn_options.flags = 0; - qnx_spawn_options.iov[0] = 0xff; - qnx_spawn_options.iov[1] = 0xff; - qnx_spawn_options.iov[2] = 0xff; - qnx_spawn_options.iov[3] = 0xff; -} -#endif - -#define FD_DEF_HEIGHT 24 -#define FD_DEF_WIDTH 80 -/* Control op */ -#define FD_CTRL_OP_GET_WINSIZE 100 - -static int fd_get_window_size(int fd, Uint32 *width, Uint32 *height) -{ -#ifdef TIOCGWINSZ - struct winsize ws; - if (ioctl(fd,TIOCGWINSZ,&ws) == 0) { - *width = (Uint32) ws.ws_col; - *height = (Uint32) ws.ws_row; - return 0; - } -#endif - return -1; -} - -static ErlDrvSSizeT fd_control(ErlDrvData drv_data, - unsigned int command, - char *buf, ErlDrvSizeT len, - char **rbuf, ErlDrvSizeT rlen) -{ - int fd = (int)(long)drv_data; - char resbuff[2*sizeof(Uint32)]; - switch (command) { - case FD_CTRL_OP_GET_WINSIZE: - { - Uint32 w,h; - if (fd_get_window_size(fd,&w,&h)) - return 0; - memcpy(resbuff,&w,sizeof(Uint32)); - memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32)); - } - break; - default: - return 0; - } - if (rlen < 2*sizeof(Uint32)) { - *rbuf = driver_alloc(2*sizeof(Uint32)); - } - memcpy(*rbuf,resbuff,2*sizeof(Uint32)); - return 2*sizeof(Uint32); -} - -static ErlDrvData fd_start(ErlDrvPort port_num, char* name, - SysDriverOpts* opts) -{ - ErlDrvData res; - int non_blocking = 0; - - if (((opts->read_write & DO_READ) && opts->ifd >= max_files) || - ((opts->read_write & DO_WRITE) && opts->ofd >= max_files)) - return ERL_DRV_ERROR_GENERAL; - - /* - * Historical: - * - * "Note about nonblocking I/O. - * - * At least on Solaris, setting the write end of a TTY to nonblocking, - * will set the input end to nonblocking as well (and vice-versa). - * If erl is run in a pipeline like this: cat | erl - * the input end of the TTY will be the standard input of cat. - * And cat is not prepared to handle nonblocking I/O." - * - * Actually, the reason for this is not that the tty itself gets set - * in non-blocking mode, but that the "input end" (cat's stdin) and - * the "output end" (erlang's stdout) are typically the "same" file - * descriptor, dup()'ed from a single fd by one of this process' - * ancestors. - * - * The workaround for this problem used to be a rather bad kludge, - * interposing an extra process ("internal cat") between erlang's - * stdout and the original stdout, allowing erlang to set its stdout - * in non-blocking mode without affecting the stdin of the preceding - * process in the pipeline - and being a kludge, it caused all kinds - * of weird problems. - * - * So, this is the current logic: - * - * The only reason to set non-blocking mode on the output fd at all is - * if it's something that can cause a write() to block, of course, - * i.e. primarily if it points to a tty, socket, pipe, or fifo. - * - * If we don't set non-blocking mode when we "should" have, and output - * becomes blocked, the entire runtime system will be suspended - this - * is normally bad of course, and can happen fairly "easily" - e.g. user - * hits ^S on tty - but doesn't necessarily happen. - * - * If we do set non-blocking mode when we "shouldn't" have, the runtime - * system will end up seeing EOF on the input fd (due to the preceding - * process dying), which typically will cause the entire runtime system - * to terminate immediately (due to whatever erlang process is seeing - * the EOF taking it as a signal to halt the system). This is *very* bad. - * - * I.e. we should take a conservative approach, and only set non- - * blocking mode when we a) need to, and b) are reasonably certain - * that it won't be a problem. And as in the example above, the problem - * occurs when input fd and output fd point to different "things". - * - * However, determining that they are not just the same "type" of - * "thing", but actually the same instance of that type of thing, is - * unreasonably complex in many/most cases. - * - * Also, with pipes, sockets, and fifos it's far from obvious that the - * user *wants* non-blocking output: If you're running erlang inside - * some complex pipeline, you're probably not running a real-time system - * that must never stop, but rather *want* it to suspend if the output - * channel is "full". - * - * So, the bottom line: We will only set the output fd non-blocking if - * it points to a tty, and either a) the input fd also points to a tty, - * or b) we can make sure that setting the output fd non-blocking - * doesn't interfere with someone else's input, via a somewhat milder - * kludge than the above. - * - * Also keep in mind that while this code is almost exclusively run as - * a result of an erlang open_port({fd,0,1}, ...), that isn't the only - * case - it can be called with any old pre-existing file descriptors, - * the relations between which (if they're even two) we can only guess - * at - still, we try our best... - * - * Added note OTP 18: Some systems seem to use stdout/stderr to log data - * using unix pipes, so we cannot allow the system to block on a write. - * Therefore we use an async thread to write the data to fd's that could - * not be set to non-blocking. When no async threads are available we - * fall back on the old behaviour. - * - * Also the guarantee about what is delivered to the OS has changed. - * Pre 18 the fd driver did no flushing of data before terminating. - * Now it does. This is because we want to be able to guarantee that things - * such as escripts and friends really have outputted all data before - * terminating. This could potentially block the termination of the system - * for a very long time, but if the user wants to terminate fast she should - * use erlang:halt with flush=false. - */ - - if (opts->read_write & DO_READ) { - init_fd_data(opts->ifd, port_num); - } - if (opts->read_write & DO_WRITE) { - init_fd_data(opts->ofd, port_num); - - /* If we don't have a read end, all bets are off - no non-blocking. */ - if (opts->read_write & DO_READ) { - - if (isatty(opts->ofd)) { /* output fd is a tty:-) */ - - if (isatty(opts->ifd)) { /* input fd is also a tty */ - - /* To really do this "right", we should also check that - input and output fd point to the *same* tty - but - this seems like overkill; ttyname() isn't for free, - and this is a very common case - and it's hard to - imagine a scenario where setting non-blocking mode - here would cause problems - go ahead and do it. */ - - non_blocking = 1; - SET_NONBLOCKING(opts->ofd); - - } else { /* output fd is a tty, input fd isn't */ - - /* This is a "problem case", but also common (see the - example above) - i.e. it makes sense to try a bit - harder before giving up on non-blocking mode: Try to - re-open the tty that the output fd points to, and if - successful replace the original one with the "new" fd - obtained this way, and set *that* one in non-blocking - mode. (Yes, this is a kludge.) - - However, re-opening the tty may fail in a couple of - (unusual) cases: - - 1) The name of the tty (or an equivalent one, i.e. - same major/minor number) can't be found, because - it actually lives somewhere other than /dev (or - wherever ttyname() looks for it), and isn't - equivalent to any of those that do live in the - "standard" place - this should be *very* unusual. - - 2) Permissions on the tty don't allow us to open it - - it's perfectly possible to have an fd open to an - object whose permissions wouldn't allow us to open - it. This is not as unusual as it sounds, one case - is if the user has su'ed to someone else (not - root) - we have a read/write fd open to the tty - (because it has been inherited all the way down - here), but we have neither read nor write - permission for the tty. - - In these cases, we finally give up, and don't set the - output fd in non-blocking mode. */ - - char *tty; - int nfd; - - if ((tty = ttyname(opts->ofd)) != NULL && - (nfd = open(tty, O_WRONLY)) != -1) { - dup2(nfd, opts->ofd); - close(nfd); - non_blocking = 1; - SET_NONBLOCKING(opts->ofd); - } - } - } - } - } - CHLD_STAT_LOCK; - res = (ErlDrvData)(long)set_driver_data(port_num, opts->ifd, opts->ofd, - opts->packet_bytes, - opts->read_write, 0, -1, - !non_blocking); - CHLD_STAT_UNLOCK; - return res; -} - -static void clear_fd_data(int fd) -{ - if (fd_data[fd].sz > 0) { - erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf); - ASSERT(erts_smp_atomic_read_nob(&sys_misc_mem_sz) >= fd_data[fd].sz); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*fd_data[fd].sz); - } - fd_data[fd].buf = NULL; - fd_data[fd].sz = 0; - fd_data[fd].remain = 0; - fd_data[fd].cpos = NULL; - fd_data[fd].psz = 0; -} - -static void nbio_stop_fd(ErlDrvPort prt, int fd) -{ - driver_select(prt,fd,DO_READ|DO_WRITE,0); - clear_fd_data(fd); - SET_BLOCKING(fd); -} - -static void fd_stop(ErlDrvData ev) /* Does not close the fds */ -{ - int ofd; - int fd = (int)(long)ev; - ErlDrvPort prt = driver_data[fd].port_num; - -#if FDBLOCK - if (driver_data[fd].blocking) { - erts_free(ERTS_ALC_T_SYS_BLOCKING,driver_data[fd].blocking); - driver_data[fd].blocking = NULL; - erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*sizeof(ErtsSysBlocking)); - } -#endif - - nbio_stop_fd(prt, fd); - ofd = driver_data[fd].ofd; - if (ofd != fd && ofd != -1) - nbio_stop_fd(prt, ofd); -} - -static void fd_flush(ErlDrvData fd) -{ - if (!driver_data[(int)(long)fd].terminating) - driver_data[(int)(long)fd].terminating = 1; -} - -static ErlDrvData vanilla_start(ErlDrvPort port_num, char* name, - SysDriverOpts* opts) -{ - int flags, fd; - ErlDrvData res; - - flags = (opts->read_write == DO_READ ? O_RDONLY : - opts->read_write == DO_WRITE ? O_WRONLY|O_CREAT|O_TRUNC : - O_RDWR|O_CREAT); - if ((fd = open(name, flags, 0666)) < 0) - return ERL_DRV_ERROR_GENERAL; - if (fd >= max_files) { - close(fd); - return ERL_DRV_ERROR_GENERAL; - } - SET_NONBLOCKING(fd); - init_fd_data(fd, port_num); - - CHLD_STAT_LOCK; - res = (ErlDrvData)(long)set_driver_data(port_num, fd, fd, - opts->packet_bytes, - opts->read_write, 0, -1, 0); - CHLD_STAT_UNLOCK; - return res; -} - -/* Note that driver_data[fd].ifd == fd if the port was opened for reading, */ -/* otherwise (i.e. write only) driver_data[fd].ofd = fd. */ - -static void stop(ErlDrvData fd) -{ - ErlDrvPort prt; - int ofd; - - prt = driver_data[(int)(long)fd].port_num; - nbio_stop_fd(prt, (int)(long)fd); - - ofd = driver_data[(int)(long)fd].ofd; - if (ofd != (int)(long)fd && (int)(long)ofd != -1) - nbio_stop_fd(prt, ofd); - else - ofd = -1; - - CHLD_STAT_LOCK; - - /* Mark as unused. */ - driver_data[(int)(long)fd].pid = -1; - - CHLD_STAT_UNLOCK; - - /* SMP note: Close has to be last thing done (open file descriptors work - as locks on driver_data[] entries) */ - driver_select(prt, (int)(long)fd, ERL_DRV_USE, 0); /* close(fd); */ - if (ofd >= 0) { - driver_select(prt, (int)(long)ofd, ERL_DRV_USE, 0); /* close(ofd); */ - } -} - -/* used by fd_driver */ -static void outputv(ErlDrvData e, ErlIOVec* ev) -{ - int fd = (int)(long)e; - ErlDrvPort ix = driver_data[fd].port_num; - int pb = driver_data[fd].packet_bytes; - int ofd = driver_data[fd].ofd; - ssize_t n; - ErlDrvSizeT sz; - char lb[4]; - char* lbp; - ErlDrvSizeT len = ev->size; - - /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ - /* if (pb >= 0 && (len & (((ErlDrvSizeT)1 << (pb*8))) - 1) != len) {*/ - if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) { - driver_failure_posix(ix, EINVAL); - return; /* -1; */ - } - /* Handles 0 <= pb <= 4 only */ - put_int32((Uint32) len, lb); - lbp = lb + (4-pb); - - ev->iov[0].iov_base = lbp; - ev->iov[0].iov_len = pb; - ev->size += pb; - - if (driver_data[fd].blocking && FDBLOCK) - driver_pdl_lock(driver_data[fd].blocking->pdl); - - if ((sz = driver_sizeq(ix)) > 0) { - driver_enqv(ix, ev, 0); - - if (driver_data[fd].blocking && FDBLOCK) - driver_pdl_unlock(driver_data[fd].blocking->pdl); - - if (sz + ev->size >= (1 << 13)) - set_busy_port(ix, 1); - } - else if (!driver_data[fd].blocking || !FDBLOCK) { - /* We try to write directly if the fd in non-blocking */ - int vsize = ev->vsize > MAX_VSIZE ? MAX_VSIZE : ev->vsize; - - n = writev(ofd, (const void *) (ev->iov), vsize); - if (n == ev->size) - return; /* 0;*/ - if (n < 0) { - if ((errno != EINTR) && (errno != ERRNO_BLOCK)) { - driver_failure_posix(ix, errno); - return; /* -1;*/ - } - n = 0; - } - driver_enqv(ix, ev, n); /* n is the skip value */ - driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1); - } -#if FDBLOCK - else { - if (ev->size != 0) { - driver_enqv(ix, ev, 0); - driver_pdl_unlock(driver_data[fd].blocking->pdl); - driver_async(ix, &driver_data[fd].blocking->pkey, - fd_async, driver_data+fd, NULL); - } else { - driver_pdl_unlock(driver_data[fd].blocking->pdl); - } - } -#endif - /* return 0;*/ -} - -/* Used by spawn_driver and vanilla driver */ -static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) -{ - int fd = (int)(long)e; - ErlDrvPort ix = driver_data[fd].port_num; - int pb = driver_data[fd].packet_bytes; - int ofd = driver_data[fd].ofd; - ssize_t n; - ErlDrvSizeT sz; - char lb[4]; - char* lbp; - struct iovec iv[2]; - - /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ - if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) { - driver_failure_posix(ix, EINVAL); - return; /* -1; */ - } - put_int32(len, lb); - lbp = lb + (4-pb); - - if ((sz = driver_sizeq(ix)) > 0) { - driver_enq(ix, lbp, pb); - driver_enq(ix, buf, len); - if (sz + len + pb >= (1 << 13)) - set_busy_port(ix, 1); - } - else { - iv[0].iov_base = lbp; - iv[0].iov_len = pb; /* should work for pb=0 */ - iv[1].iov_base = buf; - iv[1].iov_len = len; - n = writev(ofd, iv, 2); - if (n == pb+len) - return; /* 0; */ - if (n < 0) { - if ((errno != EINTR) && (errno != ERRNO_BLOCK)) { - driver_failure_posix(ix, errno); - return; /* -1; */ - } - n = 0; - } - if (n < pb) { - driver_enq(ix, lbp+n, pb-n); - driver_enq(ix, buf, len); - } - else { - n -= pb; - driver_enq(ix, buf+n, len-n); - } - driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1); - } - return; /* 0; */ -} - -static int port_inp_failure(ErlDrvPort port_num, int ready_fd, int res) - /* Result: 0 (eof) or -1 (error) */ -{ - int err = errno; - - ASSERT(res <= 0); - (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0); - clear_fd_data(ready_fd); - - if (driver_data[ready_fd].blocking && FDBLOCK) { - driver_pdl_lock(driver_data[ready_fd].blocking->pdl); - if (driver_sizeq(driver_data[ready_fd].port_num) > 0) { - driver_pdl_unlock(driver_data[ready_fd].blocking->pdl); - /* We have stuff in the output queue, so we just - set the state to terminating and wait for fd_async_ready - to terminate the port */ - if (res == 0) - driver_data[ready_fd].terminating = 2; - else - driver_data[ready_fd].terminating = -err; - return 0; - } - driver_pdl_unlock(driver_data[ready_fd].blocking->pdl); - } - - if (res == 0) { - if (driver_data[ready_fd].report_exit) { - CHLD_STAT_LOCK; - - if (driver_data[ready_fd].alive) { - /* - * We have eof and want to report exit status, but the process - * hasn't exited yet. When it does report_exit_status() will - * driver_select() this fd which will make sure that we get - * back here with driver_data[ready_fd].alive == 0 and - * driver_data[ready_fd].status set. - */ - CHLD_STAT_UNLOCK; - return 0; - } - else { - int status = driver_data[ready_fd].status; - CHLD_STAT_UNLOCK; - - /* We need not be prepared for stopped/continued processes. */ - if (WIFSIGNALED(status)) - status = 128 + WTERMSIG(status); - else - status = WEXITSTATUS(status); - - driver_report_exit(driver_data[ready_fd].port_num, status); - } - } - driver_failure_eof(port_num); - } else { - driver_failure_posix(port_num, err); - } - return 0; -} - -/* fd is the drv_data that is returned from the */ -/* initial start routine */ -/* ready_fd is the descriptor that is ready to read */ - -static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) -{ - int fd = (int)(long)e; - ErlDrvPort port_num; - int packet_bytes; - int res; - Uint h; - - port_num = driver_data[fd].port_num; - packet_bytes = driver_data[fd].packet_bytes; - - - if (packet_bytes == 0) { - byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, - ERTS_SYS_READ_BUF_SZ); - res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ); - if (res < 0) { - if ((errno != EINTR) && (errno != ERRNO_BLOCK)) - port_inp_failure(port_num, ready_fd, res); - } - else if (res == 0) - port_inp_failure(port_num, ready_fd, res); - else - driver_output(port_num, (char*) read_buf, res); - erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf); - } - else if (fd_data[ready_fd].remain > 0) { /* We try to read the remainder */ - /* space is allocated in buf */ - res = read(ready_fd, fd_data[ready_fd].cpos, - fd_data[ready_fd].remain); - if (res < 0) { - if ((errno != EINTR) && (errno != ERRNO_BLOCK)) - port_inp_failure(port_num, ready_fd, res); - } - else if (res == 0) { - port_inp_failure(port_num, ready_fd, res); - } - else if (res == fd_data[ready_fd].remain) { /* we're done */ - driver_output(port_num, fd_data[ready_fd].buf, - fd_data[ready_fd].sz); - clear_fd_data(ready_fd); - } - else { /* if (res < fd_data[ready_fd].remain) */ - fd_data[ready_fd].cpos += res; - fd_data[ready_fd].remain -= res; - } - } - else if (fd_data[ready_fd].remain == 0) { /* clean fd */ - byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, - ERTS_SYS_READ_BUF_SZ); - /* We make one read attempt and see what happens */ - res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ); - if (res < 0) { - if ((errno != EINTR) && (errno != ERRNO_BLOCK)) - port_inp_failure(port_num, ready_fd, res); - } - else if (res == 0) { /* eof */ - port_inp_failure(port_num, ready_fd, res); - } - else if (res < packet_bytes - fd_data[ready_fd].psz) { - memcpy(fd_data[ready_fd].pbuf+fd_data[ready_fd].psz, - read_buf, res); - fd_data[ready_fd].psz += res; - } - else { /* if (res >= packet_bytes) */ - unsigned char* cpos = read_buf; - int bytes_left = res; - - while (1) { - int psz = fd_data[ready_fd].psz; - char* pbp = fd_data[ready_fd].pbuf + psz; - - while(bytes_left && (psz < packet_bytes)) { - *pbp++ = *cpos++; - bytes_left--; - psz++; - } - - if (psz < packet_bytes) { - fd_data[ready_fd].psz = psz; - break; - } - fd_data[ready_fd].psz = 0; - - switch (packet_bytes) { - case 1: h = get_int8(fd_data[ready_fd].pbuf); break; - case 2: h = get_int16(fd_data[ready_fd].pbuf); break; - case 4: h = get_int32(fd_data[ready_fd].pbuf); break; - default: ASSERT(0); return; /* -1; */ - } - - if (h <= (bytes_left)) { - driver_output(port_num, (char*) cpos, h); - cpos += h; - bytes_left -= h; - continue; - } - else { /* The last message we got was split */ - char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); - if (!buf) { - errno = ENOMEM; - port_inp_failure(port_num, ready_fd, -1); - } - else { - erts_smp_atomic_add_nob(&sys_misc_mem_sz, h); - sys_memcpy(buf, cpos, bytes_left); - fd_data[ready_fd].buf = buf; - fd_data[ready_fd].sz = h; - fd_data[ready_fd].remain = h - bytes_left; - fd_data[ready_fd].cpos = buf + bytes_left; - } - break; - } - } - } - erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf); - } -} - - -/* fd is the drv_data that is returned from the */ -/* initial start routine */ -/* ready_fd is the descriptor that is ready to read */ - -static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) -{ - int fd = (int)(long)e; - ErlDrvPort ix = driver_data[fd].port_num; - int n; - struct iovec* iv; - int vsize; - - - if ((iv = (struct iovec*) driver_peekq(ix, &vsize)) == NULL) { - driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); - if (driver_data[fd].terminating) - driver_failure_atom(driver_data[fd].port_num,"normal"); - return; /* 0; */ - } - vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize; - if ((n = writev(ready_fd, iv, vsize)) > 0) { - if (driver_deq(ix, n) == 0) - set_busy_port(ix, 0); - } - else if (n < 0) { - if (errno == ERRNO_BLOCK || errno == EINTR) - return; /* 0; */ - else { - int res = errno; - driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); - driver_failure_posix(ix, res); - return; /* -1; */ - } - } - return; /* 0; */ -} - -static void stop_select(ErlDrvEvent fd, void* _) -{ - close((int)fd); -} - -#if FDBLOCK - -static void -fd_async(void *async_data) -{ - int res; - ErtsSysDriverData *dd = (ErtsSysDriverData*)async_data; - SysIOVec *iov0; - SysIOVec *iov; - int iovlen; - int err = 0; - /* much of this code is stolen from efile_drv:invoke_writev */ - driver_pdl_lock(dd->blocking->pdl); - iov0 = driver_peekq(dd->port_num, &iovlen); - iovlen = iovlen < MAXIOV ? iovlen : MAXIOV; - iov = erts_alloc_fnf(ERTS_ALC_T_SYS_WRITE_BUF, - sizeof(SysIOVec)*iovlen); - if (!iov) { - res = -1; - err = ENOMEM; - driver_pdl_unlock(dd->blocking->pdl); - } else { - memcpy(iov,iov0,iovlen*sizeof(SysIOVec)); - driver_pdl_unlock(dd->blocking->pdl); - - do { - res = writev(dd->ofd, iov, iovlen); - } while (res < 0 && errno == EINTR); - if (res < 0) - err = errno; - - erts_free(ERTS_ALC_T_SYS_WRITE_BUF, iov); - } - dd->blocking->res = res; - dd->blocking->err = err; -} - -void fd_ready_async(ErlDrvData drv_data, - ErlDrvThreadData thread_data) { - ErtsSysDriverData *dd = (ErtsSysDriverData *)thread_data; - ErlDrvPort port_num = dd->port_num; - - ASSERT(dd->blocking); - ASSERT(dd == (driver_data + (int)(long)drv_data)); - - if (dd->blocking->res > 0) { - driver_pdl_lock(dd->blocking->pdl); - if (driver_deq(port_num, dd->blocking->res) == 0) { - driver_pdl_unlock(dd->blocking->pdl); - set_busy_port(port_num, 0); - if (dd->terminating) { - /* The port is has been ordered to terminate - from either fd_flush or port_inp_failure */ - if (dd->terminating == 1) - driver_failure_atom(port_num, "normal"); - else if (dd->terminating == 2) - driver_failure_eof(port_num); - else if (dd->terminating < 0) - driver_failure_posix(port_num, -dd->terminating); - return; /* -1; */ - } - } else { - driver_pdl_unlock(dd->blocking->pdl); - /* still data left to write in queue */ - driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL); - return /* 0; */; - } - } else if (dd->blocking->res < 0) { - if (dd->blocking->err == ERRNO_BLOCK) { - set_busy_port(port_num, 1); - /* still data left to write in queue */ - driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL); - } else - driver_failure_posix(port_num, dd->blocking->err); - return; /* -1; */ - } - return; /* 0; */ -} - -#endif - void erts_do_break_handling(void) { struct termios temp_mode; @@ -2740,10 +1010,7 @@ erts_sys_unsetenv(char *key) void sys_init_io(void) { - fd_data = (ErtsSysFdData *) - erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(ErtsSysFdData)); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, - max_files * sizeof(ErtsSysFdData)); + } #if (0) /* unused? */ @@ -2937,178 +1204,6 @@ erl_debug(char* fmt, ...) #endif /* DEBUG */ -static ERTS_INLINE void -report_exit_status(ErtsSysReportExit *rep, int status) -{ - Port *pp; -#ifdef ERTS_SMP - CHLD_STAT_UNLOCK; - pp = erts_thr_id2port_sflgs(rep->port, - ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); - CHLD_STAT_LOCK; -#else - pp = erts_id2port_sflgs(rep->port, - NULL, - 0, - ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); -#endif - if (pp) { - if (rep->ifd >= 0) { - driver_data[rep->ifd].alive = 0; - driver_data[rep->ifd].status = status; - (void) driver_select(ERTS_Port2ErlDrvPort(pp), - rep->ifd, - (ERL_DRV_READ|ERL_DRV_USE), - 1); - } - if (rep->ofd >= 0) { - driver_data[rep->ofd].alive = 0; - driver_data[rep->ofd].status = status; - (void) driver_select(ERTS_Port2ErlDrvPort(pp), - rep->ofd, - (ERL_DRV_WRITE|ERL_DRV_USE), - 1); - } -#ifdef ERTS_SMP - erts_thr_port_release(pp); -#else - erts_port_release(pp); -#endif - } - erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep); -} - -#if !CHLDWTHR /* ---------------------------------------------------------- */ - -#define ERTS_REPORT_EXIT_STATUS report_exit_status - -static int check_children(void) -{ - int res = 0; - int pid; - int status; - -#ifndef ERTS_SMP - if (children_died) -#endif - { - sys_sigblock(SIGCHLD); - CHLD_STAT_LOCK; - while ((pid = waitpid(-1, &status, WNOHANG)) > 0) - note_child_death(pid, status); -#ifndef ERTS_SMP - children_died = 0; -#endif - CHLD_STAT_UNLOCK; - sys_sigrelease(SIGCHLD); - res = 1; - } - return res; -} - -#ifdef ERTS_SMP - -void -erts_check_children(void) -{ - (void) check_children(); -} - -#endif - -#elif CHLDWTHR && defined(ERTS_SMP) /* ------------------------------------- */ - -#define ERTS_REPORT_EXIT_STATUS report_exit_status - -#define check_children() (0) - - -#else /* CHLDWTHR && !defined(ERTS_SMP) ------------------------------------ */ - -#define ERTS_REPORT_EXIT_STATUS initiate_report_exit_status - -static ERTS_INLINE void -initiate_report_exit_status(ErtsSysReportExit *rep, int status) -{ - rep->next = report_exit_transit_list; - rep->status = status; - report_exit_transit_list = rep; - erts_sys_schedule_interrupt(1); -} - -static int check_children(void) -{ - int res; - ErtsSysReportExit *rep; - CHLD_STAT_LOCK; - rep = report_exit_transit_list; - res = rep != NULL; - while (rep) { - ErtsSysReportExit *curr_rep = rep; - rep = rep->next; - report_exit_status(curr_rep, curr_rep->status); - } - report_exit_transit_list = NULL; - CHLD_STAT_UNLOCK; - return res; -} - -#endif /* ------------------------------------------------------------------ */ - -static void note_child_death(int pid, int status) -{ - ErtsSysReportExit **repp = &report_exit_list; - ErtsSysReportExit *rep = report_exit_list; - - while (rep) { - if (pid == rep->pid) { - *repp = rep->next; - ERTS_REPORT_EXIT_STATUS(rep, status); - break; - } - repp = &rep->next; - rep = rep->next; - } -} - -#if CHLDWTHR - -static void * -child_waiter(void *unused) -{ - int pid; - int status; - -#ifdef ERTS_ENABLE_LOCK_CHECK - erts_lc_set_thread_name("child waiter"); -#endif - - while(1) { -#ifdef DEBUG - int waitpid_errno; -#endif - pid = waitpid(-1, &status, 0); -#ifdef DEBUG - waitpid_errno = errno; -#endif - CHLD_STAT_LOCK; - if (pid < 0) { - ASSERT(waitpid_errno == ECHILD); - } - else { - children_alive--; - ASSERT(children_alive >= 0); - note_child_death(pid, status); - } - while (!children_alive) - CHLD_STAT_WAIT; /* Wait for children to wait on... :) */ - CHLD_STAT_UNLOCK; - } - - return NULL; -} - -#endif /* * Called from schedule() when it runs out of runnable processes, @@ -3127,7 +1222,6 @@ erl_sys_schedule(int runnable) (void) check_children(); } - #ifdef ERTS_SMP static erts_smp_tid_t sig_dispatcher_tid; diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c new file mode 100644 index 0000000000..e3f089fc0f --- /dev/null +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -0,0 +1,1983 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef ISC32 +#define _POSIX_SOURCE +#define _XOPEN_SOURCE +#endif + +#include /* ! */ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ISC32 +#include +#endif + +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#define NEED_CHILD_SETUP_DEFINES +#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */ +#include "sys.h" +#include "erl_thr_progress.h" + +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +#define __DARWIN__ 1 +#endif + +#ifdef USE_THREADS +#include "erl_threads.h" +#endif + +#include "erl_mseg.h" + +extern char **environ; +extern erts_smp_rwmtx_t environ_rwmtx; + +extern erts_smp_atomic_t sys_misc_mem_sz; + +#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O + * vector sock_sendv(). + */ +/* + * Don't need global.h, but erl_cpu_topology.h won't compile otherwise + */ +#include "global.h" + +#include "erl_sys_driver.h" +#include "erl_check_io.h" +#include "erl_cpu_topology.h" + +#ifndef DISABLE_VFORK +#define DISABLE_VFORK 0 +#endif + +#if defined IOV_MAX +#define MAXIOV IOV_MAX +#elif defined UIO_MAXIOV +#define MAXIOV UIO_MAXIOV +#else +#define MAXIOV 16 +#endif + +/* + * [OTP-3906] + * Solaris signal management gets confused when threads are used and a + * lot of child processes dies. The confusion results in that SIGCHLD + * signals aren't delivered to the emulator which in turn results in + * a lot of defunct processes in the system. + * + * The problem seems to appear when a signal is frequently + * blocked/unblocked at the same time as the signal is frequently + * propagated. The child waiter thread is a workaround for this problem. + * The SIGCHLD signal is always blocked (in all threads), and the child + * waiter thread fetches the signal by a call to sigwait(). See + * child_waiter(). + */ + +typedef struct ErtsSysReportExit_ ErtsSysReportExit; +struct ErtsSysReportExit_ { + ErtsSysReportExit *next; + Eterm port; + int pid; + int ifd; + int ofd; +#if CHLDWTHR && !defined(ERTS_SMP) + int status; +#endif +}; + +/* Used by the fd driver iff the fd could not be set to non-blocking */ +typedef struct ErtsSysBlocking_ { + ErlDrvPDL pdl; + int res; + int err; + unsigned int pkey; +} ErtsSysBlocking; + + +/* This data is shared by these drivers - initialized by spawn_init() */ +typedef struct driver_data { + ErlDrvPort port_num; + int ofd, packet_bytes; + ErtsSysReportExit *report_exit; + int pid; + int alive; + int status; + int terminating; + ErtsSysBlocking *blocking; +} ErtsSysDriverData; + +static ErtsSysDriverData *driver_data; /* indexed by fd */ + +static ErtsSysReportExit *report_exit_list; +#if CHLDWTHR && !defined(ERTS_SMP) +static ErtsSysReportExit *report_exit_transit_list; +#endif + +#define DIR_SEPARATOR_CHAR '/' + +#if defined(__ANDROID__) +#define SHELL "/system/bin/sh" +#else +#define SHELL "/bin/sh" +#endif /* __ANDROID__ */ + + +#if defined(DEBUG) +#define ERL_BUILD_TYPE_MARKER ".debug" +#elif defined(PURIFY) +#define ERL_BUILD_TYPE_MARKER ".purify" +#elif defined(QUANTIFY) +#define ERL_BUILD_TYPE_MARKER ".quantify" +#elif defined(PURECOV) +#define ERL_BUILD_TYPE_MARKER ".purecov" +#elif defined(VALGRIND) +#define ERL_BUILD_TYPE_MARKER ".valgrind" +#else /* opt */ +#define ERL_BUILD_TYPE_MARKER +#endif + +#define CHILD_SETUP_PROG_NAME "erl_child_setup" ERL_BUILD_TYPE_MARKER +static char *child_setup_prog; + +#if CHLDWTHR || defined(ERTS_SMP) +erts_mtx_t chld_stat_mtx; +#endif +#if CHLDWTHR +static erts_tid_t child_waiter_tid; +/* chld_stat_mtx is used to protect against concurrent accesses + of the driver_data fields pid, alive, and status. */ +erts_cnd_t chld_stat_cnd; +static long children_alive; +#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx) +#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx) +#define CHLD_STAT_WAIT erts_cnd_wait(&chld_stat_cnd, &chld_stat_mtx) +#define CHLD_STAT_SIGNAL erts_cnd_signal(&chld_stat_cnd) +#elif defined(ERTS_SMP) /* ------------------------------------------------- */ +#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx) +#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx) + +#else /* ------------------------------------------------------------------- */ +#define CHLD_STAT_LOCK +#define CHLD_STAT_UNLOCK +static volatile int children_died; +#endif + +typedef struct ErtsSysFdData { + char pbuf[4]; /* hold partial packet bytes */ + int psz; /* size of pbuf */ + char *buf; + char *cpos; + int sz; + int remain; /* for input on fd */ +} ErtsSysFdData; + +static ErtsSysFdData *fd_data; /* indexed by fd */ + +/* static FUNCTION(int, write_fill, (int, char*, int)); unused? */ +static void note_child_death(int, int); + +#if CHLDWTHR +static void* child_waiter(void *); +#endif + +static void block_signals(void) +{ +#if !CHLDWTHR + sys_sigblock(SIGCHLD); +#endif +#ifndef ERTS_SMP + sys_sigblock(SIGINT); +#ifndef ETHR_UNUSABLE_SIGUSRX + sys_sigblock(SIGUSR1); +#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ +#endif /* #ifndef ERTS_SMP */ + +#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX) + sys_sigblock(ERTS_SYS_SUSPEND_SIGNAL); +#endif + +} + +static void unblock_signals(void) +{ + /* Update erl_child_setup.c if changed */ +#if !CHLDWTHR + sys_sigrelease(SIGCHLD); +#endif +#ifndef ERTS_SMP + sys_sigrelease(SIGINT); +#ifndef ETHR_UNUSABLE_SIGUSRX + sys_sigrelease(SIGUSR1); +#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ +#endif /* #ifndef ERTS_SMP */ + +#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX) + sys_sigrelease(ERTS_SYS_SUSPEND_SIGNAL); +#endif + +} + +/************************** Port I/O *******************************/ + + + +/* I. Common stuff */ + +/* + * Decreasing the size of it below 16384 is not allowed. + */ + +/* II. The spawn/fd/vanilla drivers */ + +#define ERTS_SYS_READ_BUF_SZ (64*1024) + +/* Driver interfaces */ +static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*); +static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*); +#if FDBLOCK +static void fd_async(void *); +static void fd_ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data); +#endif +static ErlDrvSSizeT fd_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT, + char **, ErlDrvSizeT); +static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*); +static int spawn_init(void); +static void fd_stop(ErlDrvData); +static void fd_flush(ErlDrvData); +static void stop(ErlDrvData); +static void ready_input(ErlDrvData, ErlDrvEvent); +static void ready_output(ErlDrvData, ErlDrvEvent); +static void output(ErlDrvData, char*, ErlDrvSizeT); +static void outputv(ErlDrvData, ErlIOVec*); +static void stop_select(ErlDrvEvent, void*); + +struct erl_drv_entry spawn_driver_entry = { + spawn_init, + spawn_start, + stop, + output, + ready_input, + ready_output, + "spawn", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL, NULL, + stop_select +}; +struct erl_drv_entry fd_driver_entry = { + NULL, + fd_start, + fd_stop, + output, + ready_input, + ready_output, + "fd", + NULL, + NULL, + fd_control, + NULL, + outputv, +#if FDBLOCK + fd_ready_async, /* ready_async */ +#else + NULL, +#endif + fd_flush, /* flush */ + NULL, /* call */ + NULL, /* event */ + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, /* ERL_DRV_FLAGs */ + NULL, /* handle2 */ + NULL, /* process_exit */ + stop_select +}; +struct erl_drv_entry vanilla_driver_entry = { + NULL, + vanilla_start, + stop, + output, + ready_input, + ready_output, + "vanilla", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* flush */ + NULL, /* call */ + NULL, /* event */ + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, /* ERL_DRV_FLAGs */ + NULL, /* handle2 */ + NULL, /* process_exit */ + stop_select +}; + +/* Handle SIGCHLD signals. */ +#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL)) +static RETSIGTYPE onchld(void) +#else +static RETSIGTYPE onchld(int signum) +#endif +{ +#if CHLDWTHR + ASSERT(0); /* We should *never* catch a SIGCHLD signal */ +#elif defined(ERTS_SMP) + smp_sig_notify('C'); +#else + children_died = 1; + ERTS_CHK_IO_AS_INTR(); /* Make sure we don't sleep in poll */ +#endif +} + +static int set_blocking_data(ErtsSysDriverData *dd) { + + dd->blocking = erts_alloc(ERTS_ALC_T_SYS_BLOCKING, sizeof(ErtsSysBlocking)); + + erts_smp_atomic_add_nob(&sys_misc_mem_sz, sizeof(ErtsSysBlocking)); + + dd->blocking->pdl = driver_pdl_create(dd->port_num); + dd->blocking->res = 0; + dd->blocking->err = 0; + dd->blocking->pkey = driver_async_port_key(dd->port_num); + + return 1; +} + +static int set_driver_data(ErlDrvPort port_num, + int ifd, + int ofd, + int packet_bytes, + int read_write, + int exit_status, + int pid, + int is_blocking) +{ + Port *prt; + ErtsSysReportExit *report_exit; + + if (!exit_status) + report_exit = NULL; + else { + report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT, + sizeof(ErtsSysReportExit)); + report_exit->next = report_exit_list; + report_exit->port = erts_drvport2id(port_num); + report_exit->pid = pid; + report_exit->ifd = read_write & DO_READ ? ifd : -1; + report_exit->ofd = read_write & DO_WRITE ? ofd : -1; +#if CHLDWTHR && !defined(ERTS_SMP) + report_exit->status = 0; +#endif + report_exit_list = report_exit; + } + + prt = erts_drvport2port(port_num); + if (prt != ERTS_INVALID_ERL_DRV_PORT) + prt->os_pid = pid; + + if (read_write & DO_READ) { + driver_data[ifd].packet_bytes = packet_bytes; + driver_data[ifd].port_num = port_num; + driver_data[ifd].report_exit = report_exit; + driver_data[ifd].pid = pid; + driver_data[ifd].alive = 1; + driver_data[ifd].status = 0; + driver_data[ifd].terminating = 0; + driver_data[ifd].blocking = NULL; + if (read_write & DO_WRITE) { + driver_data[ifd].ofd = ofd; + if (is_blocking && FDBLOCK) + if (!set_blocking_data(driver_data+ifd)) + return -1; + if (ifd != ofd) + driver_data[ofd] = driver_data[ifd]; /* structure copy */ + } else { /* DO_READ only */ + driver_data[ifd].ofd = -1; + } + (void) driver_select(port_num, ifd, (ERL_DRV_READ|ERL_DRV_USE), 1); + return(ifd); + } else { /* DO_WRITE only */ + driver_data[ofd].packet_bytes = packet_bytes; + driver_data[ofd].port_num = port_num; + driver_data[ofd].report_exit = report_exit; + driver_data[ofd].ofd = ofd; + driver_data[ofd].pid = pid; + driver_data[ofd].alive = 1; + driver_data[ofd].status = 0; + driver_data[ofd].terminating = 0; + driver_data[ofd].blocking = NULL; + if (is_blocking && FDBLOCK) + if (!set_blocking_data(driver_data+ofd)) + return -1; + return(ofd); + } +} + +static int spawn_init() +{ + int i; +#if CHLDWTHR + erts_thr_opts_t thr_opts = ERTS_THR_OPTS_DEFAULT_INITER; +#endif + int res; + char bindir[MAXPATHLEN]; + size_t bindirsz = sizeof(bindir); + Uint csp_path_sz; + +#if CHLDWTHR + thr_opts.detached = 0; + thr_opts.suggested_stack_size = 0; /* Smallest possible */ + thr_opts.name = "child_waiter"; +#endif + +#if !DISABLE_VFORK + res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz); + if (res != 0) { + if (res < 0) + erl_exit(-1, + "Environment variable BINDIR is not set\n"); + if (res > 0) + erl_exit(-1, + "Value of environment variable BINDIR is too large\n"); + } + if (bindir[0] != DIR_SEPARATOR_CHAR) + erl_exit(-1, + "Environment variable BINDIR does not contain an" + " absolute path\n"); + csp_path_sz = (strlen(bindir) + + 1 /* DIR_SEPARATOR_CHAR */ + + sizeof(CHILD_SETUP_PROG_NAME) + + 1); + child_setup_prog = erts_alloc(ERTS_ALC_T_CS_PROG_PATH, csp_path_sz); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, csp_path_sz); + erts_snprintf(child_setup_prog, csp_path_sz, + "%s%c%s", + bindir, + DIR_SEPARATOR_CHAR, + CHILD_SETUP_PROG_NAME); +#endif + + report_exit_list = NULL; + +#ifdef USE_THREADS + +#if CHLDWTHR || defined(ERTS_SMP) + erts_mtx_init(&chld_stat_mtx, "child_status"); +#endif +#if CHLDWTHR +#ifndef ERTS_SMP + report_exit_transit_list = NULL; +#endif + erts_cnd_init(&chld_stat_cnd); + children_alive = 0; +#endif + +#if !CHLDWTHR && !defined(ERTS_SMP) + children_died = 0; +#endif + +#endif /* USE_THREADS */ + + sys_signal(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */ + driver_data = (ErtsSysDriverData *) + erts_alloc(ERTS_ALC_T_DRV_TAB, sys_max_files() * sizeof(ErtsSysDriverData)); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, + sys_max_files() * sizeof(ErtsSysDriverData)); + + for (i = 0; i < sys_max_files(); i++) + driver_data[i].pid = -1; + + fd_data = (ErtsSysFdData *) + erts_alloc(ERTS_ALC_T_FD_TAB, sys_max_files() * sizeof(ErtsSysFdData)); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, + sys_max_files() * sizeof(ErtsSysFdData)); + +#if CHLDWTHR + sys_sigblock(SIGCHLD); +#endif + + sys_signal(SIGCHLD, onchld); /* Reap children */ + +#if CHLDWTHR + erts_thr_create(&child_waiter_tid, child_waiter, NULL, &thr_opts); +#endif + + return 1; +} + +static void close_pipes(int ifd[2], int ofd[2], int read_write) +{ + if (read_write & DO_READ) { + (void) close(ifd[0]); + (void) close(ifd[1]); + } + if (read_write & DO_WRITE) { + (void) close(ofd[0]); + (void) close(ofd[1]); + } +} + +static void init_fd_data(int fd, ErlDrvPort port_num) +{ + fd_data[fd].buf = NULL; + fd_data[fd].cpos = NULL; + fd_data[fd].remain = 0; + fd_data[fd].sz = 0; + fd_data[fd].psz = 0; +} + +static char **build_unix_environment(char *block) +{ + int i; + int j; + int len; + char *cp; + char **cpp; + char** old_env; + + ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx)); + + cp = block; + len = 0; + while (*cp != '\0') { + cp += strlen(cp) + 1; + len++; + } + old_env = environ; + while (*old_env++ != NULL) { + len++; + } + + cpp = (char **) erts_alloc_fnf(ERTS_ALC_T_ENVIRONMENT, + sizeof(char *) * (len+1)); + if (cpp == NULL) { + return NULL; + } + + cp = block; + len = 0; + while (*cp != '\0') { + cpp[len] = cp; + cp += strlen(cp) + 1; + len++; + } + + i = len; + for (old_env = environ; *old_env; old_env++) { + char* old = *old_env; + + for (j = 0; j < len; j++) { + char *s, *t; + + s = cpp[j]; + t = old; + while (*s == *t && *s != '=') { + s++, t++; + } + if (*s == '=' && *t == '=') { + break; + } + } + + if (j == len) { /* New version not found */ + cpp[len++] = old; + } + } + + for (j = 0; j < i; ) { + size_t last = strlen(cpp[j])-1; + if (cpp[j][last] == '=' && strchr(cpp[j], '=') == cpp[j]+last) { + cpp[j] = cpp[--len]; + if (len < i) { + i--; + } else { + j++; + } + } + else { + j++; + } + } + + cpp[len] = NULL; + return cpp; +} + +/* + [arndt] In most Unix systems, including Solaris 2.5, 'fork' allocates memory + in swap space for the child of a 'fork', whereas 'vfork' does not do this. + The natural call to use here is therefore 'vfork'. Due to a bug in + 'vfork' in Solaris 2.5 (apparently fixed in 2.6), using 'vfork' + can be dangerous in what seems to be these circumstances: + If the child code under a vfork sets the signal action to SIG_DFL + (or SIG_IGN) + for any signal which was previously set to a signal handler, the + state of the parent is clobbered, so that the later arrival of + such a signal yields a sigsegv in the parent. If the signal was + not set to a signal handler, but ignored, all seems to work. + If you change the forking code below, beware of this. + */ + +static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) +{ +#define CMD_LINE_PREFIX_STR "exec " +#define CMD_LINE_PREFIX_STR_SZ (sizeof(CMD_LINE_PREFIX_STR) - 1) + + int ifd[2], ofd[2], len, pid, i; + char **volatile new_environ; /* volatile since a vfork() then cannot + cause 'new_environ' to be clobbered + in the parent process. */ + int saved_errno; + long res; + char *cmd_line; +#ifndef QNX + int unbind; +#endif +#if !DISABLE_VFORK + int no_vfork; + size_t no_vfork_sz = sizeof(no_vfork); + + no_vfork = (erts_sys_getenv_raw("ERL_NO_VFORK", + (char *) &no_vfork, + &no_vfork_sz) >= 0); +#endif + + switch (opts->read_write) { + case DO_READ: + if (pipe(ifd) < 0) + return ERL_DRV_ERROR_ERRNO; + if (ifd[0] >= sys_max_files()) { + close_pipes(ifd, ofd, opts->read_write); + errno = EMFILE; + return ERL_DRV_ERROR_ERRNO; + } + ofd[1] = -1; /* keep purify happy */ + break; + case DO_WRITE: + if (pipe(ofd) < 0) return ERL_DRV_ERROR_ERRNO; + if (ofd[1] >= sys_max_files()) { + close_pipes(ifd, ofd, opts->read_write); + errno = EMFILE; + return ERL_DRV_ERROR_ERRNO; + } + ifd[0] = -1; /* keep purify happy */ + break; + case DO_READ|DO_WRITE: + if (pipe(ifd) < 0) return ERL_DRV_ERROR_ERRNO; + errno = EMFILE; /* default for next two conditions */ + if (ifd[0] >= sys_max_files() || pipe(ofd) < 0) { + close_pipes(ifd, ofd, DO_READ); + return ERL_DRV_ERROR_ERRNO; + } + if (ofd[1] >= sys_max_files()) { + close_pipes(ifd, ofd, opts->read_write); + errno = EMFILE; + return ERL_DRV_ERROR_ERRNO; + } + break; + default: + ASSERT(0); + return ERL_DRV_ERROR_GENERAL; + } + + if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { + /* started with spawn_executable, not with spawn */ + len = strlen(name); + cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP, len + 1); + if (!cmd_line) { + close_pipes(ifd, ofd, opts->read_write); + errno = ENOMEM; + return ERL_DRV_ERROR_ERRNO; + } + memcpy((void *) cmd_line,(void *) name, len); + cmd_line[len] = '\0'; + if (access(cmd_line,X_OK) != 0) { + int save_errno = errno; + erts_free(ERTS_ALC_T_TMP, cmd_line); + errno = save_errno; + return ERL_DRV_ERROR_ERRNO; + } + } else { + /* make the string suitable for giving to "sh" */ + len = strlen(name); + cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP, + CMD_LINE_PREFIX_STR_SZ + len + 1); + if (!cmd_line) { + close_pipes(ifd, ofd, opts->read_write); + errno = ENOMEM; + return ERL_DRV_ERROR_ERRNO; + } + memcpy((void *) cmd_line, + (void *) CMD_LINE_PREFIX_STR, + CMD_LINE_PREFIX_STR_SZ); + memcpy((void *) (cmd_line + CMD_LINE_PREFIX_STR_SZ), (void *) name, len); + cmd_line[CMD_LINE_PREFIX_STR_SZ + len] = '\0'; + } + + erts_smp_rwmtx_rlock(&environ_rwmtx); + + if (opts->envir == NULL) { + new_environ = environ; + } else if ((new_environ = build_unix_environment(opts->envir)) == NULL) { + erts_smp_rwmtx_runlock(&environ_rwmtx); + erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); + errno = ENOMEM; + return ERL_DRV_ERROR_ERRNO; + } + +#ifndef QNX + /* Block child from SIGINT and SIGUSR1. Must be before fork() + to be safe. */ + block_signals(); + + CHLD_STAT_LOCK; + + unbind = erts_sched_bind_atfork_prepare(); + +#if !DISABLE_VFORK + /* See fork/vfork discussion before this function. */ + if (no_vfork) { +#endif + + DEBUGF(("Using fork\n")); + pid = fork(); + + if (pid == 0) { + /* The child! Setup child... */ + + if (erts_sched_bind_atfork_child(unbind) != 0) + goto child_error; + + /* OBSERVE! + * Keep child setup after vfork() (implemented below and in + * erl_child_setup.c) up to date if changes are made here. + */ + + if (opts->use_stdio) { + if (opts->read_write & DO_READ) { + /* stdout for process */ + if (dup2(ifd[1], 1) < 0) + goto child_error; + if(opts->redir_stderr) + /* stderr for process */ + if (dup2(ifd[1], 2) < 0) + goto child_error; + } + if (opts->read_write & DO_WRITE) + /* stdin for process */ + if (dup2(ofd[0], 0) < 0) + goto child_error; + } + else { /* XXX will fail if ofd[0] == 4 (unlikely..) */ + if (opts->read_write & DO_READ) + if (dup2(ifd[1], 4) < 0) + goto child_error; + if (opts->read_write & DO_WRITE) + if (dup2(ofd[0], 3) < 0) + goto child_error; + } + +#if defined(HAVE_CLOSEFROM) + closefrom(opts->use_stdio ? 3 : 5); +#else + for (i = opts->use_stdio ? 3 : 5; i < sys_max_files(); i++) + (void) close(i); +#endif + + if (opts->wd && chdir(opts->wd) < 0) + goto child_error; + +#if defined(USE_SETPGRP_NOARGS) /* SysV */ + (void) setpgrp(); +#elif defined(USE_SETPGRP) /* BSD */ + (void) setpgrp(0, getpid()); +#else /* POSIX */ + (void) setsid(); +#endif + + unblock_signals(); + + if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { + if (opts->argv == NULL) { + execle(cmd_line,cmd_line,(char *) NULL, new_environ); + } else { + if (opts->argv[0] == erts_default_arg0) { + opts->argv[0] = cmd_line; + } + execve(cmd_line, opts->argv, new_environ); + if (opts->argv[0] == cmd_line) { + opts->argv[0] = erts_default_arg0; + } + } + } else { + execle(SHELL, "sh", "-c", cmd_line, (char *) NULL, new_environ); + } + child_error: + _exit(1); + } +#if !DISABLE_VFORK + } +#define ENOUGH_BYTES (44) + else { /* Use vfork() */ + char **cs_argv= erts_alloc(ERTS_ALC_T_TMP,(CS_ARGV_NO_OF_ARGS + 1)* + sizeof(char *)); + char fd_close_range[ENOUGH_BYTES]; /* 44 bytes are enough to */ + char dup2_op[CS_ARGV_NO_OF_DUP2_OPS][ENOUGH_BYTES]; /* hold any "%d:%d" string */ + /* on a 64-bit machine. */ + + /* Setup argv[] for the child setup program (implemented in + erl_child_setup.c) */ + i = 0; + if (opts->use_stdio) { + if (opts->read_write & DO_READ){ + /* stdout for process */ + erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 1); + if(opts->redir_stderr) + /* stderr for process */ + erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 2); + } + if (opts->read_write & DO_WRITE) + /* stdin for process */ + erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ofd[0], 0); + } else { /* XXX will fail if ofd[0] == 4 (unlikely..) */ + if (opts->read_write & DO_READ) + erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 4); + if (opts->read_write & DO_WRITE) + erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ofd[0], 3); + } + for (; i < CS_ARGV_NO_OF_DUP2_OPS; i++) + strcpy(&dup2_op[i][0], "-"); + erts_snprintf(fd_close_range, ENOUGH_BYTES, "%d:%d", opts->use_stdio ? 3 : 5, sys_max_files()-1); + + cs_argv[CS_ARGV_PROGNAME_IX] = child_setup_prog; + cs_argv[CS_ARGV_WD_IX] = opts->wd ? opts->wd : "."; + cs_argv[CS_ARGV_UNBIND_IX] = erts_sched_bind_atvfork_child(unbind); + cs_argv[CS_ARGV_FD_CR_IX] = fd_close_range; + for (i = 0; i < CS_ARGV_NO_OF_DUP2_OPS; i++) + cs_argv[CS_ARGV_DUP2_OP_IX(i)] = &dup2_op[i][0]; + + if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { + int num = 0; + int j = 0; + if (opts->argv != NULL) { + for(; opts->argv[num] != NULL; ++num) + ; + } + cs_argv = erts_realloc(ERTS_ALC_T_TMP,cs_argv, (CS_ARGV_NO_OF_ARGS + 1 + num + 1) * sizeof(char *)); + cs_argv[CS_ARGV_CMD_IX] = "-"; + cs_argv[CS_ARGV_NO_OF_ARGS] = cmd_line; + if (opts->argv != NULL) { + for (;opts->argv[j] != NULL; ++j) { + if (opts->argv[j] == erts_default_arg0) { + cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = cmd_line; + } else { + cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = opts->argv[j]; + } + } + } + cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = NULL; + } else { + cs_argv[CS_ARGV_CMD_IX] = cmd_line; /* Command */ + cs_argv[CS_ARGV_NO_OF_ARGS] = NULL; + } + DEBUGF(("Using vfork\n")); + pid = vfork(); + + if (pid == 0) { + /* The child! */ + + /* Observe! + * OTP-4389: The child setup program (implemented in + * erl_child_setup.c) will perform the necessary setup of the + * child before it execs to the user program. This because + * vfork() only allow an *immediate* execve() or _exit() in the + * child. + */ + execve(child_setup_prog, cs_argv, new_environ); + _exit(1); + } + erts_free(ERTS_ALC_T_TMP,cs_argv); + } +#undef ENOUGH_BYTES +#endif + + erts_sched_bind_atfork_parent(unbind); + + if (pid == -1) { + saved_errno = errno; + CHLD_STAT_UNLOCK; + erts_smp_rwmtx_runlock(&environ_rwmtx); + erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); + unblock_signals(); + close_pipes(ifd, ofd, opts->read_write); + errno = saved_errno; + return ERL_DRV_ERROR_ERRNO; + } +#else /* QNX */ + if (opts->use_stdio) { + if (opts->read_write & DO_READ) + qnx_spawn_options.iov[1] = ifd[1]; /* stdout for process */ + if (opts->read_write & DO_WRITE) + qnx_spawn_options.iov[0] = ofd[0]; /* stdin for process */ + } + else { + if (opts->read_write & DO_READ) + qnx_spawn_options.iov[4] = ifd[1]; + if (opts->read_write & DO_WRITE) + qnx_spawn_options.iov[3] = ofd[0]; + } + /* Close fds on exec */ + for (i = 3; i < sys_max_files(); i++) + fcntl(i, F_SETFD, 1); + + qnx_spawn_options.flags = _SPAWN_SETSID; + if ((pid = spawnl(P_NOWAIT, SHELL, SHELL, "-c", cmd_line, + (char *) 0)) < 0) { + erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); + reset_qnx_spawn(); + erts_smp_rwmtx_runlock(&environ_rwmtx); + close_pipes(ifd, ofd, opts->read_write); + return ERL_DRV_ERROR_GENERAL; + } + reset_qnx_spawn(); +#endif /* QNX */ + + erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); + + if (new_environ != environ) + erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); + + if (opts->read_write & DO_READ) + (void) close(ifd[1]); + if (opts->read_write & DO_WRITE) + (void) close(ofd[0]); + + if (opts->read_write & DO_READ) { + SET_NONBLOCKING(ifd[0]); + init_fd_data(ifd[0], port_num); + } + if (opts->read_write & DO_WRITE) { + SET_NONBLOCKING(ofd[1]); + init_fd_data(ofd[1], port_num); + } + + res = set_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes, + opts->read_write, opts->exit_status, pid, 0); + /* Don't unblock SIGCHLD until now, since the call above must + first complete putting away the info about our new subprocess. */ + unblock_signals(); + +#if CHLDWTHR + ASSERT(children_alive >= 0); + + if (!(children_alive++)) + CHLD_STAT_SIGNAL; /* Wake up child waiter thread if no children + was alive before we fork()ed ... */ +#endif + /* Don't unlock chld_stat_mtx until now of the same reason as above */ + CHLD_STAT_UNLOCK; + + erts_smp_rwmtx_runlock(&environ_rwmtx); + + return (ErlDrvData)res; +#undef CMD_LINE_PREFIX_STR +#undef CMD_LINE_PREFIX_STR_SZ +} + +#ifdef QNX +static reset_qnx_spawn() +{ + int i; + + /* Reset qnx_spawn_options */ + qnx_spawn_options.flags = 0; + qnx_spawn_options.iov[0] = 0xff; + qnx_spawn_options.iov[1] = 0xff; + qnx_spawn_options.iov[2] = 0xff; + qnx_spawn_options.iov[3] = 0xff; +} +#endif + +#define FD_DEF_HEIGHT 24 +#define FD_DEF_WIDTH 80 +/* Control op */ +#define FD_CTRL_OP_GET_WINSIZE 100 + +static int fd_get_window_size(int fd, Uint32 *width, Uint32 *height) +{ +#ifdef TIOCGWINSZ + struct winsize ws; + if (ioctl(fd,TIOCGWINSZ,&ws) == 0) { + *width = (Uint32) ws.ws_col; + *height = (Uint32) ws.ws_row; + return 0; + } +#endif + return -1; +} + +static ErlDrvSSizeT fd_control(ErlDrvData drv_data, + unsigned int command, + char *buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen) +{ + int fd = (int)(long)drv_data; + char resbuff[2*sizeof(Uint32)]; + switch (command) { + case FD_CTRL_OP_GET_WINSIZE: + { + Uint32 w,h; + if (fd_get_window_size(fd,&w,&h)) + return 0; + memcpy(resbuff,&w,sizeof(Uint32)); + memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32)); + } + break; + default: + return 0; + } + if (rlen < 2*sizeof(Uint32)) { + *rbuf = driver_alloc(2*sizeof(Uint32)); + } + memcpy(*rbuf,resbuff,2*sizeof(Uint32)); + return 2*sizeof(Uint32); +} + +static ErlDrvData fd_start(ErlDrvPort port_num, char* name, + SysDriverOpts* opts) +{ + ErlDrvData res; + int non_blocking = 0; + + if (((opts->read_write & DO_READ) && opts->ifd >= sys_max_files()) || + ((opts->read_write & DO_WRITE) && opts->ofd >= sys_max_files())) + return ERL_DRV_ERROR_GENERAL; + + /* + * Historical: + * + * "Note about nonblocking I/O. + * + * At least on Solaris, setting the write end of a TTY to nonblocking, + * will set the input end to nonblocking as well (and vice-versa). + * If erl is run in a pipeline like this: cat | erl + * the input end of the TTY will be the standard input of cat. + * And cat is not prepared to handle nonblocking I/O." + * + * Actually, the reason for this is not that the tty itself gets set + * in non-blocking mode, but that the "input end" (cat's stdin) and + * the "output end" (erlang's stdout) are typically the "same" file + * descriptor, dup()'ed from a single fd by one of this process' + * ancestors. + * + * The workaround for this problem used to be a rather bad kludge, + * interposing an extra process ("internal cat") between erlang's + * stdout and the original stdout, allowing erlang to set its stdout + * in non-blocking mode without affecting the stdin of the preceding + * process in the pipeline - and being a kludge, it caused all kinds + * of weird problems. + * + * So, this is the current logic: + * + * The only reason to set non-blocking mode on the output fd at all is + * if it's something that can cause a write() to block, of course, + * i.e. primarily if it points to a tty, socket, pipe, or fifo. + * + * If we don't set non-blocking mode when we "should" have, and output + * becomes blocked, the entire runtime system will be suspended - this + * is normally bad of course, and can happen fairly "easily" - e.g. user + * hits ^S on tty - but doesn't necessarily happen. + * + * If we do set non-blocking mode when we "shouldn't" have, the runtime + * system will end up seeing EOF on the input fd (due to the preceding + * process dying), which typically will cause the entire runtime system + * to terminate immediately (due to whatever erlang process is seeing + * the EOF taking it as a signal to halt the system). This is *very* bad. + * + * I.e. we should take a conservative approach, and only set non- + * blocking mode when we a) need to, and b) are reasonably certain + * that it won't be a problem. And as in the example above, the problem + * occurs when input fd and output fd point to different "things". + * + * However, determining that they are not just the same "type" of + * "thing", but actually the same instance of that type of thing, is + * unreasonably complex in many/most cases. + * + * Also, with pipes, sockets, and fifos it's far from obvious that the + * user *wants* non-blocking output: If you're running erlang inside + * some complex pipeline, you're probably not running a real-time system + * that must never stop, but rather *want* it to suspend if the output + * channel is "full". + * + * So, the bottom line: We will only set the output fd non-blocking if + * it points to a tty, and either a) the input fd also points to a tty, + * or b) we can make sure that setting the output fd non-blocking + * doesn't interfere with someone else's input, via a somewhat milder + * kludge than the above. + * + * Also keep in mind that while this code is almost exclusively run as + * a result of an erlang open_port({fd,0,1}, ...), that isn't the only + * case - it can be called with any old pre-existing file descriptors, + * the relations between which (if they're even two) we can only guess + * at - still, we try our best... + * + * Added note OTP 18: Some systems seem to use stdout/stderr to log data + * using unix pipes, so we cannot allow the system to block on a write. + * Therefore we use an async thread to write the data to fd's that could + * not be set to non-blocking. When no async threads are available we + * fall back on the old behaviour. + * + * Also the guarantee about what is delivered to the OS has changed. + * Pre 18 the fd driver did no flushing of data before terminating. + * Now it does. This is because we want to be able to guarantee that things + * such as escripts and friends really have outputted all data before + * terminating. This could potentially block the termination of the system + * for a very long time, but if the user wants to terminate fast she should + * use erlang:halt with flush=false. + */ + + if (opts->read_write & DO_READ) { + init_fd_data(opts->ifd, port_num); + } + if (opts->read_write & DO_WRITE) { + init_fd_data(opts->ofd, port_num); + + /* If we don't have a read end, all bets are off - no non-blocking. */ + if (opts->read_write & DO_READ) { + + if (isatty(opts->ofd)) { /* output fd is a tty:-) */ + + if (isatty(opts->ifd)) { /* input fd is also a tty */ + + /* To really do this "right", we should also check that + input and output fd point to the *same* tty - but + this seems like overkill; ttyname() isn't for free, + and this is a very common case - and it's hard to + imagine a scenario where setting non-blocking mode + here would cause problems - go ahead and do it. */ + + non_blocking = 1; + SET_NONBLOCKING(opts->ofd); + + } else { /* output fd is a tty, input fd isn't */ + + /* This is a "problem case", but also common (see the + example above) - i.e. it makes sense to try a bit + harder before giving up on non-blocking mode: Try to + re-open the tty that the output fd points to, and if + successful replace the original one with the "new" fd + obtained this way, and set *that* one in non-blocking + mode. (Yes, this is a kludge.) + + However, re-opening the tty may fail in a couple of + (unusual) cases: + + 1) The name of the tty (or an equivalent one, i.e. + same major/minor number) can't be found, because + it actually lives somewhere other than /dev (or + wherever ttyname() looks for it), and isn't + equivalent to any of those that do live in the + "standard" place - this should be *very* unusual. + + 2) Permissions on the tty don't allow us to open it - + it's perfectly possible to have an fd open to an + object whose permissions wouldn't allow us to open + it. This is not as unusual as it sounds, one case + is if the user has su'ed to someone else (not + root) - we have a read/write fd open to the tty + (because it has been inherited all the way down + here), but we have neither read nor write + permission for the tty. + + In these cases, we finally give up, and don't set the + output fd in non-blocking mode. */ + + char *tty; + int nfd; + + if ((tty = ttyname(opts->ofd)) != NULL && + (nfd = open(tty, O_WRONLY)) != -1) { + dup2(nfd, opts->ofd); + close(nfd); + non_blocking = 1; + SET_NONBLOCKING(opts->ofd); + } + } + } + } + } + CHLD_STAT_LOCK; + res = (ErlDrvData)(long)set_driver_data(port_num, opts->ifd, opts->ofd, + opts->packet_bytes, + opts->read_write, 0, -1, + !non_blocking); + CHLD_STAT_UNLOCK; + return res; +} + +static void clear_fd_data(int fd) +{ + if (fd_data[fd].sz > 0) { + erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf); + ASSERT(erts_smp_atomic_read_nob(&sys_misc_mem_sz) >= fd_data[fd].sz); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*fd_data[fd].sz); + } + fd_data[fd].buf = NULL; + fd_data[fd].sz = 0; + fd_data[fd].remain = 0; + fd_data[fd].cpos = NULL; + fd_data[fd].psz = 0; +} + +static void nbio_stop_fd(ErlDrvPort prt, int fd) +{ + driver_select(prt,fd,DO_READ|DO_WRITE,0); + clear_fd_data(fd); + SET_BLOCKING(fd); +} + +static void fd_stop(ErlDrvData ev) /* Does not close the fds */ +{ + int ofd; + int fd = (int)(long)ev; + ErlDrvPort prt = driver_data[fd].port_num; + +#if FDBLOCK + if (driver_data[fd].blocking) { + erts_free(ERTS_ALC_T_SYS_BLOCKING,driver_data[fd].blocking); + driver_data[fd].blocking = NULL; + erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*sizeof(ErtsSysBlocking)); + } +#endif + + nbio_stop_fd(prt, fd); + ofd = driver_data[fd].ofd; + if (ofd != fd && ofd != -1) + nbio_stop_fd(prt, ofd); +} + +static void fd_flush(ErlDrvData fd) +{ + if (!driver_data[(int)(long)fd].terminating) + driver_data[(int)(long)fd].terminating = 1; +} + +static ErlDrvData vanilla_start(ErlDrvPort port_num, char* name, + SysDriverOpts* opts) +{ + int flags, fd; + ErlDrvData res; + + flags = (opts->read_write == DO_READ ? O_RDONLY : + opts->read_write == DO_WRITE ? O_WRONLY|O_CREAT|O_TRUNC : + O_RDWR|O_CREAT); + if ((fd = open(name, flags, 0666)) < 0) + return ERL_DRV_ERROR_GENERAL; + if (fd >= sys_max_files()) { + close(fd); + return ERL_DRV_ERROR_GENERAL; + } + SET_NONBLOCKING(fd); + init_fd_data(fd, port_num); + + CHLD_STAT_LOCK; + res = (ErlDrvData)(long)set_driver_data(port_num, fd, fd, + opts->packet_bytes, + opts->read_write, 0, -1, 0); + CHLD_STAT_UNLOCK; + return res; +} + +/* Note that driver_data[fd].ifd == fd if the port was opened for reading, */ +/* otherwise (i.e. write only) driver_data[fd].ofd = fd. */ + +static void stop(ErlDrvData fd) +{ + ErlDrvPort prt; + int ofd; + + prt = driver_data[(int)(long)fd].port_num; + nbio_stop_fd(prt, (int)(long)fd); + + ofd = driver_data[(int)(long)fd].ofd; + if (ofd != (int)(long)fd && (int)(long)ofd != -1) + nbio_stop_fd(prt, ofd); + else + ofd = -1; + + CHLD_STAT_LOCK; + + /* Mark as unused. */ + driver_data[(int)(long)fd].pid = -1; + + CHLD_STAT_UNLOCK; + + /* SMP note: Close has to be last thing done (open file descriptors work + as locks on driver_data[] entries) */ + driver_select(prt, (int)(long)fd, ERL_DRV_USE, 0); /* close(fd); */ + if (ofd >= 0) { + driver_select(prt, (int)(long)ofd, ERL_DRV_USE, 0); /* close(ofd); */ + } +} + +/* used by fd_driver */ +static void outputv(ErlDrvData e, ErlIOVec* ev) +{ + int fd = (int)(long)e; + ErlDrvPort ix = driver_data[fd].port_num; + int pb = driver_data[fd].packet_bytes; + int ofd = driver_data[fd].ofd; + ssize_t n; + ErlDrvSizeT sz; + char lb[4]; + char* lbp; + ErlDrvSizeT len = ev->size; + + /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ + /* if (pb >= 0 && (len & (((ErlDrvSizeT)1 << (pb*8))) - 1) != len) {*/ + if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) { + driver_failure_posix(ix, EINVAL); + return; /* -1; */ + } + /* Handles 0 <= pb <= 4 only */ + put_int32((Uint32) len, lb); + lbp = lb + (4-pb); + + ev->iov[0].iov_base = lbp; + ev->iov[0].iov_len = pb; + ev->size += pb; + + if (driver_data[fd].blocking && FDBLOCK) + driver_pdl_lock(driver_data[fd].blocking->pdl); + + if ((sz = driver_sizeq(ix)) > 0) { + driver_enqv(ix, ev, 0); + + if (driver_data[fd].blocking && FDBLOCK) + driver_pdl_unlock(driver_data[fd].blocking->pdl); + + if (sz + ev->size >= (1 << 13)) + set_busy_port(ix, 1); + } + else if (!driver_data[fd].blocking || !FDBLOCK) { + /* We try to write directly if the fd in non-blocking */ + int vsize = ev->vsize > MAX_VSIZE ? MAX_VSIZE : ev->vsize; + + n = writev(ofd, (const void *) (ev->iov), vsize); + if (n == ev->size) + return; /* 0;*/ + if (n < 0) { + if ((errno != EINTR) && (errno != ERRNO_BLOCK)) { + driver_failure_posix(ix, errno); + return; /* -1;*/ + } + n = 0; + } + driver_enqv(ix, ev, n); /* n is the skip value */ + driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1); + } +#if FDBLOCK + else { + if (ev->size != 0) { + driver_enqv(ix, ev, 0); + driver_pdl_unlock(driver_data[fd].blocking->pdl); + driver_async(ix, &driver_data[fd].blocking->pkey, + fd_async, driver_data+fd, NULL); + } else { + driver_pdl_unlock(driver_data[fd].blocking->pdl); + } + } +#endif + /* return 0;*/ +} + +/* Used by spawn_driver and vanilla driver */ +static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) +{ + int fd = (int)(long)e; + ErlDrvPort ix = driver_data[fd].port_num; + int pb = driver_data[fd].packet_bytes; + int ofd = driver_data[fd].ofd; + ssize_t n; + ErlDrvSizeT sz; + char lb[4]; + char* lbp; + struct iovec iv[2]; + + /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ + if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) { + driver_failure_posix(ix, EINVAL); + return; /* -1; */ + } + put_int32(len, lb); + lbp = lb + (4-pb); + + if ((sz = driver_sizeq(ix)) > 0) { + driver_enq(ix, lbp, pb); + driver_enq(ix, buf, len); + if (sz + len + pb >= (1 << 13)) + set_busy_port(ix, 1); + } + else { + iv[0].iov_base = lbp; + iv[0].iov_len = pb; /* should work for pb=0 */ + iv[1].iov_base = buf; + iv[1].iov_len = len; + n = writev(ofd, iv, 2); + if (n == pb+len) + return; /* 0; */ + if (n < 0) { + if ((errno != EINTR) && (errno != ERRNO_BLOCK)) { + driver_failure_posix(ix, errno); + return; /* -1; */ + } + n = 0; + } + if (n < pb) { + driver_enq(ix, lbp+n, pb-n); + driver_enq(ix, buf, len); + } + else { + n -= pb; + driver_enq(ix, buf+n, len-n); + } + driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1); + } + return; /* 0; */ +} + +static int port_inp_failure(ErlDrvPort port_num, int ready_fd, int res) + /* Result: 0 (eof) or -1 (error) */ +{ + int err = errno; + + ASSERT(res <= 0); + (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0); + clear_fd_data(ready_fd); + + if (driver_data[ready_fd].blocking && FDBLOCK) { + driver_pdl_lock(driver_data[ready_fd].blocking->pdl); + if (driver_sizeq(driver_data[ready_fd].port_num) > 0) { + driver_pdl_unlock(driver_data[ready_fd].blocking->pdl); + /* We have stuff in the output queue, so we just + set the state to terminating and wait for fd_async_ready + to terminate the port */ + if (res == 0) + driver_data[ready_fd].terminating = 2; + else + driver_data[ready_fd].terminating = -err; + return 0; + } + driver_pdl_unlock(driver_data[ready_fd].blocking->pdl); + } + + if (res == 0) { + if (driver_data[ready_fd].report_exit) { + CHLD_STAT_LOCK; + + if (driver_data[ready_fd].alive) { + /* + * We have eof and want to report exit status, but the process + * hasn't exited yet. When it does report_exit_status() will + * driver_select() this fd which will make sure that we get + * back here with driver_data[ready_fd].alive == 0 and + * driver_data[ready_fd].status set. + */ + CHLD_STAT_UNLOCK; + return 0; + } + else { + int status = driver_data[ready_fd].status; + CHLD_STAT_UNLOCK; + + /* We need not be prepared for stopped/continued processes. */ + if (WIFSIGNALED(status)) + status = 128 + WTERMSIG(status); + else + status = WEXITSTATUS(status); + + driver_report_exit(driver_data[ready_fd].port_num, status); + } + } + driver_failure_eof(port_num); + } else { + driver_failure_posix(port_num, err); + } + return 0; +} + +/* fd is the drv_data that is returned from the */ +/* initial start routine */ +/* ready_fd is the descriptor that is ready to read */ + +static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) +{ + int fd = (int)(long)e; + ErlDrvPort port_num; + int packet_bytes; + int res; + Uint h; + + port_num = driver_data[fd].port_num; + packet_bytes = driver_data[fd].packet_bytes; + + + if (packet_bytes == 0) { + byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, + ERTS_SYS_READ_BUF_SZ); + res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ); + if (res < 0) { + if ((errno != EINTR) && (errno != ERRNO_BLOCK)) + port_inp_failure(port_num, ready_fd, res); + } + else if (res == 0) + port_inp_failure(port_num, ready_fd, res); + else + driver_output(port_num, (char*) read_buf, res); + erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf); + } + else if (fd_data[ready_fd].remain > 0) { /* We try to read the remainder */ + /* space is allocated in buf */ + res = read(ready_fd, fd_data[ready_fd].cpos, + fd_data[ready_fd].remain); + if (res < 0) { + if ((errno != EINTR) && (errno != ERRNO_BLOCK)) + port_inp_failure(port_num, ready_fd, res); + } + else if (res == 0) { + port_inp_failure(port_num, ready_fd, res); + } + else if (res == fd_data[ready_fd].remain) { /* we're done */ + driver_output(port_num, fd_data[ready_fd].buf, + fd_data[ready_fd].sz); + clear_fd_data(ready_fd); + } + else { /* if (res < fd_data[ready_fd].remain) */ + fd_data[ready_fd].cpos += res; + fd_data[ready_fd].remain -= res; + } + } + else if (fd_data[ready_fd].remain == 0) { /* clean fd */ + byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, + ERTS_SYS_READ_BUF_SZ); + /* We make one read attempt and see what happens */ + res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ); + if (res < 0) { + if ((errno != EINTR) && (errno != ERRNO_BLOCK)) + port_inp_failure(port_num, ready_fd, res); + } + else if (res == 0) { /* eof */ + port_inp_failure(port_num, ready_fd, res); + } + else if (res < packet_bytes - fd_data[ready_fd].psz) { + memcpy(fd_data[ready_fd].pbuf+fd_data[ready_fd].psz, + read_buf, res); + fd_data[ready_fd].psz += res; + } + else { /* if (res >= packet_bytes) */ + unsigned char* cpos = read_buf; + int bytes_left = res; + + while (1) { + int psz = fd_data[ready_fd].psz; + char* pbp = fd_data[ready_fd].pbuf + psz; + + while(bytes_left && (psz < packet_bytes)) { + *pbp++ = *cpos++; + bytes_left--; + psz++; + } + + if (psz < packet_bytes) { + fd_data[ready_fd].psz = psz; + break; + } + fd_data[ready_fd].psz = 0; + + switch (packet_bytes) { + case 1: h = get_int8(fd_data[ready_fd].pbuf); break; + case 2: h = get_int16(fd_data[ready_fd].pbuf); break; + case 4: h = get_int32(fd_data[ready_fd].pbuf); break; + default: ASSERT(0); return; /* -1; */ + } + + if (h <= (bytes_left)) { + driver_output(port_num, (char*) cpos, h); + cpos += h; + bytes_left -= h; + continue; + } + else { /* The last message we got was split */ + char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); + if (!buf) { + errno = ENOMEM; + port_inp_failure(port_num, ready_fd, -1); + } + else { + erts_smp_atomic_add_nob(&sys_misc_mem_sz, h); + sys_memcpy(buf, cpos, bytes_left); + fd_data[ready_fd].buf = buf; + fd_data[ready_fd].sz = h; + fd_data[ready_fd].remain = h - bytes_left; + fd_data[ready_fd].cpos = buf + bytes_left; + } + break; + } + } + } + erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf); + } +} + + +/* fd is the drv_data that is returned from the */ +/* initial start routine */ +/* ready_fd is the descriptor that is ready to read */ + +static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) +{ + int fd = (int)(long)e; + ErlDrvPort ix = driver_data[fd].port_num; + int n; + struct iovec* iv; + int vsize; + + + if ((iv = (struct iovec*) driver_peekq(ix, &vsize)) == NULL) { + driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); + if (driver_data[fd].terminating) + driver_failure_atom(driver_data[fd].port_num,"normal"); + return; /* 0; */ + } + vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize; + if ((n = writev(ready_fd, iv, vsize)) > 0) { + if (driver_deq(ix, n) == 0) + set_busy_port(ix, 0); + } + else if (n < 0) { + if (errno == ERRNO_BLOCK || errno == EINTR) + return; /* 0; */ + else { + int res = errno; + driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); + driver_failure_posix(ix, res); + return; /* -1; */ + } + } + return; /* 0; */ +} + +static void stop_select(ErlDrvEvent fd, void* _) +{ + close((int)fd); +} + +#if FDBLOCK + +static void +fd_async(void *async_data) +{ + int res; + ErtsSysDriverData *dd = (ErtsSysDriverData*)async_data; + SysIOVec *iov0; + SysIOVec *iov; + int iovlen; + int err = 0; + /* much of this code is stolen from efile_drv:invoke_writev */ + driver_pdl_lock(dd->blocking->pdl); + iov0 = driver_peekq(dd->port_num, &iovlen); + iovlen = iovlen < MAXIOV ? iovlen : MAXIOV; + iov = erts_alloc_fnf(ERTS_ALC_T_SYS_WRITE_BUF, + sizeof(SysIOVec)*iovlen); + if (!iov) { + res = -1; + err = ENOMEM; + driver_pdl_unlock(dd->blocking->pdl); + } else { + memcpy(iov,iov0,iovlen*sizeof(SysIOVec)); + driver_pdl_unlock(dd->blocking->pdl); + + do { + res = writev(dd->ofd, iov, iovlen); + } while (res < 0 && errno == EINTR); + if (res < 0) + err = errno; + err = errno; + + erts_free(ERTS_ALC_T_SYS_WRITE_BUF, iov); + } + dd->blocking->res = res; + dd->blocking->err = err; +} + +void fd_ready_async(ErlDrvData drv_data, + ErlDrvThreadData thread_data) { + ErtsSysDriverData *dd = (ErtsSysDriverData *)thread_data; + ErlDrvPort port_num = dd->port_num; + + ASSERT(dd->blocking); + ASSERT(dd == (driver_data + (int)(long)drv_data)); + + if (dd->blocking->res > 0) { + driver_pdl_lock(dd->blocking->pdl); + if (driver_deq(port_num, dd->blocking->res) == 0) { + driver_pdl_unlock(dd->blocking->pdl); + set_busy_port(port_num, 0); + if (dd->terminating) { + /* The port is has been ordered to terminate + from either fd_flush or port_inp_failure */ + if (dd->terminating == 1) + driver_failure_atom(port_num, "normal"); + else if (dd->terminating == 2) + driver_failure_eof(port_num); + else if (dd->terminating < 0) + driver_failure_posix(port_num, -dd->terminating); + return; /* -1; */ + } + } else { + driver_pdl_unlock(dd->blocking->pdl); + /* still data left to write in queue */ + driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL); + return /* 0; */; + } + } else if (dd->blocking->res < 0) { + if (dd->blocking->err == ERRNO_BLOCK) { + set_busy_port(port_num, 1); + /* still data left to write in queue */ + driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL); + } else + driver_failure_posix(port_num, dd->blocking->err); + return; /* -1; */ + } + return; /* 0; */ +} + +#endif + +static ERTS_INLINE void +report_exit_status(ErtsSysReportExit *rep, int status) +{ + Port *pp; +#ifdef ERTS_SMP + CHLD_STAT_UNLOCK; + pp = erts_thr_id2port_sflgs(rep->port, + ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); + CHLD_STAT_LOCK; +#else + pp = erts_id2port_sflgs(rep->port, + NULL, + 0, + ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); +#endif + if (pp) { + if (rep->ifd >= 0) { + driver_data[rep->ifd].alive = 0; + driver_data[rep->ifd].status = status; + (void) driver_select(ERTS_Port2ErlDrvPort(pp), + rep->ifd, + (ERL_DRV_READ|ERL_DRV_USE), + 1); + } + if (rep->ofd >= 0) { + driver_data[rep->ofd].alive = 0; + driver_data[rep->ofd].status = status; + (void) driver_select(ERTS_Port2ErlDrvPort(pp), + rep->ofd, + (ERL_DRV_WRITE|ERL_DRV_USE), + 1); + } +#ifdef ERTS_SMP + erts_thr_port_release(pp); +#else + erts_port_release(pp); +#endif + } + erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep); +} + +#if !CHLDWTHR /* ---------------------------------------------------------- */ + +#define ERTS_REPORT_EXIT_STATUS report_exit_status + +int check_children(void) +{ + int res = 0; + int pid; + int status; + +#ifndef ERTS_SMP + if (children_died) +#endif + { + sys_sigblock(SIGCHLD); + CHLD_STAT_LOCK; + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + note_child_death(pid, status); +#ifndef ERTS_SMP + children_died = 0; +#endif + CHLD_STAT_UNLOCK; + sys_sigrelease(SIGCHLD); + res = 1; + } + return res; +} + +#ifdef ERTS_SMP + +void +erts_check_children(void) +{ + (void) check_children(); +} + +#endif + +#elif CHLDWTHR && defined(ERTS_SMP) /* ------------------------------------- */ + +#define ERTS_REPORT_EXIT_STATUS report_exit_status + +int check_children(void) +{ + return 0; +} + +#else /* CHLDWTHR && !defined(ERTS_SMP) ------------------------------------ */ + +#define ERTS_REPORT_EXIT_STATUS initiate_report_exit_status + +static ERTS_INLINE void +initiate_report_exit_status(ErtsSysReportExit *rep, int status) +{ + rep->next = report_exit_transit_list; + rep->status = status; + report_exit_transit_list = rep; + erts_sys_schedule_interrupt(1); +} + +int check_children(void) +{ + int res; + ErtsSysReportExit *rep; + CHLD_STAT_LOCK; + rep = report_exit_transit_list; + res = rep != NULL; + while (rep) { + ErtsSysReportExit *curr_rep = rep; + rep = rep->next; + report_exit_status(curr_rep, curr_rep->status); + } + report_exit_transit_list = NULL; + CHLD_STAT_UNLOCK; + return res; +} + +#endif /* ------------------------------------------------------------------ */ + +static void note_child_death(int pid, int status) +{ + ErtsSysReportExit **repp = &report_exit_list; + ErtsSysReportExit *rep = report_exit_list; + + while (rep) { + if (pid == rep->pid) { + *repp = rep->next; + ERTS_REPORT_EXIT_STATUS(rep, status); + break; + } + repp = &rep->next; + rep = rep->next; + } +} + +#if CHLDWTHR + +static void * +child_waiter(void *unused) +{ + int pid; + int status; + +#ifdef ERTS_ENABLE_LOCK_CHECK + erts_lc_set_thread_name("child waiter"); +#endif + + while(1) { +#ifdef DEBUG + int waitpid_errno; +#endif + pid = waitpid(-1, &status, 0); +#ifdef DEBUG + waitpid_errno = errno; +#endif + CHLD_STAT_LOCK; + if (pid < 0) { + ASSERT(waitpid_errno == ECHILD); + } + else { + children_alive--; + ASSERT(children_alive >= 0); + note_child_death(pid, status); + } + while (!children_alive) + CHLD_STAT_WAIT; /* Wait for children to wait on... :) */ + CHLD_STAT_UNLOCK; + } + + return NULL; +} + +#endif -- cgit v1.2.3 From 14c7fefd51be035a44bfe42127fb4b9df92d760b Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 6 Jul 2015 17:13:52 +0200 Subject: erts: Create forker process for spawn driver Instead of forking from the beam process, we create a separate process in which all forks are done. This has several advantages: 1) performance: * don't have to close all fd's in the world * fork only has to copy stuff from a small process * work is done in a completely seperate process * a 3x performance increase has been measured, can be made even greater (10x) if we cache the environment in child setup 2) stability * the exec is done in another process than beam, which means that if the file that we exec to is on an nfs that is not available right now we will not block a scheduler until the nfs returns. 3) simplicity * don't have to deal with SIGCHLD in the erts Unfortunately, this solution also implies some badness. 1) There will always be a seperate process running together with beam on unix. This could be confusing and undesirable. 2) We have to transfer the entire environment to child_setup for each command. OTP-13088 --- erts/configure.in | 49 +- erts/emulator/Makefile.in | 11 +- erts/emulator/beam/erl_init.c | 1 + erts/emulator/beam/erl_lock_check.c | 4 +- erts/emulator/beam/erl_port.h | 7 +- erts/emulator/beam/erl_process.c | 37 - erts/emulator/beam/erl_process.h | 6 - erts/emulator/beam/global.h | 1 + erts/emulator/beam/io.c | 67 +- erts/emulator/beam/sys.h | 10 +- erts/emulator/sys/unix/erl_child_setup.c | 427 +++++-- erts/emulator/sys/unix/erl_unix_sys.h | 49 +- erts/emulator/sys/unix/sys.c | 33 +- erts/emulator/sys/unix/sys_drivers.c | 1771 ++++++++++++++---------------- erts/emulator/sys/unix/sys_uds.c | 125 +++ erts/emulator/sys/unix/sys_uds.h | 47 + erts/emulator/sys/win32/sys.c | 12 +- erts/emulator/test/port_SUITE.erl | 30 +- 18 files changed, 1473 insertions(+), 1214 deletions(-) create mode 100644 erts/emulator/sys/unix/sys_uds.c create mode 100644 erts/emulator/sys/unix/sys_uds.h (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 96ad58f941..32d8bca98c 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -106,7 +106,6 @@ AC_CONFIG_HEADER($host/config.h:config.h.in include/internal/$host/ethread_heade dnl ---------------------------------------------------------------------- dnl Optional features. dnl ---------------------------------------------------------------------- -enable_child_waiter_thread=no ENABLE_ALLOC_TYPE_VARS= AC_SUBST(ENABLE_ALLOC_TYPE_VARS) @@ -1291,11 +1290,7 @@ else AC_MSG_RESULT(no) fi - disable_child_waiter_thread=no case $host_os in - solaris*) - enable_child_waiter_thread=yes - ;; linux*) AC_MSG_CHECKING([whether dlopen() needs to be called before first call to dlerror()]) if test "x$ETHR_THR_LIB_BASE_TYPE" != "xposix_nptl"; then @@ -1305,16 +1300,6 @@ else else AC_MSG_RESULT(no) fi - if test "x$ETHR_THR_LIB_BASE_TYPE" != "xposix_nptl"; then - # Child waiter thread cannot be enabled - disable_child_waiter_thread=yes - enable_child_waiter_thread=no - fi - ;; - win32) - # Child waiter thread cannot be enabled - disable_child_waiter_thread=yes - enable_child_waiter_thread=no ;; *) ;; @@ -1334,24 +1319,6 @@ else esac done EMU_THR_DEFS=$new_emu_thr_defs - - AC_MSG_CHECKING(whether the child waiter thread should be enabled) - if test $enable_child_waiter_thread = yes; then - AC_DEFINE(ENABLE_CHILD_WAITER_THREAD,[1], - [Define if you want to enable child waiter thread]) - AC_MSG_RESULT(yes) - else - case $ERTS_BUILD_SMP_EMU-$disable_child_waiter_thread in - yes-no) - AC_MSG_RESULT([yes on SMP build, but not on non-SMP build]);; - *-yes) - AC_DEFINE(DISABLE_CHILD_WAITER_THREAD,[1], - [Define if you want to disable child waiter thread]) - AC_MSG_RESULT(no);; - *) - AC_MSG_RESULT(no);; - esac - fi fi AC_SUBST(EMU_THR_LIB_NAME) @@ -1483,19 +1450,27 @@ dnl # if -lsocket doesn't work by itself. #-------------------------------------------------------------------- +tk_oldLibs=$LIBS erl_checkBoth=0 +SOCKET_LIBS="" AC_CHECK_FUNC(connect, erl_checkSocket=0, erl_checkSocket=1) if test "$erl_checkSocket" = 1; then - AC_CHECK_LIB(socket, main, LIBS="$LIBS -lsocket", erl_checkBoth=1) + AC_CHECK_LIB(socket, main, SOCKET_LIBS="-lsocket", erl_checkBoth=1) fi + if test "$erl_checkBoth" = 1; then - tk_oldLibs=$LIBS LIBS="$LIBS -lsocket -lnsl" - AC_CHECK_FUNC(accept, erl_checkNsl=0, [LIBS=$tk_oldLibs]) + AC_CHECK_FUNC(accept, SOCKET_LIBS="-lsocket -lnsl") fi -AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"])) + +LIBS="$tk_oldLibs $SOCKET_LIBS" +AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, main, [SOCKET_LIBS="$SOCKET_LIBS -lnsl"])) AC_CHECK_FUNC(gethostbyname_r,have_gethostbyname_r=yes) +LIBS="$tk_oldLibs $SOCKET_LIBS" + +AC_SUBST(SOCKET_LIBS) + dnl dnl These gethostbyname thingies use old style AC_DEFINE for BC with ancient dnl autoconf... diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 69f28b0c10..ab415b66b4 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -317,7 +317,7 @@ else CS_CFLAGS = $(CS_CFLAGS_) endif CS_LDFLAGS = $(LDFLAGS) -CS_LIBS = -L../lib/internal/$(TARGET) -lerts_internal$(TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ +CS_LIBS = -L../lib/internal/$(TARGET) -lerts_internal$(TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ @SOCKET_LIBS@ LIBS += @TERMCAP_LIB@ -L../lib/internal/$(TARGET) @ERTS_INTERNAL_X_LIBS@ @@ -690,11 +690,11 @@ $(OBJDIR)/%.o: drivers/$(ERLANG_OSTYPE)/%.c # ---------------------------------------------------------------------- # Specials # -CS_SRC = sys/$(ERLANG_OSTYPE)/erl_child_setup.c +CS_OBJ = $(OBJDIR)/erl_child_setup.o $(OBJDIR)/sys_uds.o -$(BINDIR)/$(CS_EXECUTABLE): $(TTF_DIR)/GENERATED $(PRELOAD_SRC) $(CS_SRC) $(ERTS_LIB) - $(ld_verbose)$(CS_PURIFY) $(CC) $(CS_LDFLAGS) -o $(BINDIR)/$(CS_EXECUTABLE) \ - $(CS_CFLAGS) $(COMMON_INCLUDES) $(CS_SRC) $(CS_LIBS) +$(BINDIR)/$(CS_EXECUTABLE): $(TTF_DIR)/GENERATED $(PRELOAD_SRC) $(CS_OBJ) $(ERTS_LIB) + $(ld_verbose)$(CS_PURIFY) $(LD) $(CS_LDFLAGS) -o $(BINDIR)/$(CS_EXECUTABLE) \ + $(CS_CFLAGS) $(COMMON_INCLUDES) $(CS_OBJ) $(CS_LIBS) $(OBJDIR)/%.kp.o: sys/common/%.c $(V_CC) -DERTS_KERNEL_POLL_VERSION $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@ @@ -799,6 +799,7 @@ else OS_OBJS = \ $(OBJDIR)/sys.o \ $(OBJDIR)/sys_drivers.o \ + $(OBJDIR)/sys_uds.o \ $(OBJDIR)/driver_tab.o \ $(OBJDIR)/unix_efile.o \ $(OBJDIR)/gzio.o \ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 5c209a4af2..69c0de81be 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -388,6 +388,7 @@ erl_init(int ncpu, erts_mseg_late_init(); /* Must be after timer (erts_init_time()) and thread initializations */ #endif + erl_sys_late_init(); #ifdef HIPE hipe_mode_switch_init(); /* Must be after init_load/beam_catches/init */ #endif diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 84bee976ff..34c0144cbc 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -113,9 +113,6 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "environ", NULL }, #endif { "efile_drv", "address" }, -#if defined(ENABLE_CHILD_WAITER_THREAD) || defined(ERTS_SMP) - { "child_status", NULL }, -#endif { "drv_ev_state_grow", NULL, }, { "drv_ev_state", "address" }, { "safe_hash", "address" }, @@ -187,6 +184,7 @@ static erts_lc_lock_order_t erts_lock_order[] = { #ifdef ERTS_SMP { "os_monotonic_time", NULL }, #endif + { "forker_hash_mtx", NULL }, { "erts_alloc_hard_debug", NULL }, { "hard_dbg_mseg", NULL }, { "erts_mmap", NULL } diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index bc4b412594..fa97707a87 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -692,7 +692,7 @@ erts_drvport2port_state(ErlDrvPort drvport, erts_aint32_t *statep) Port *prt = ERTS_ErlDrvPort2Port(drvport); erts_aint32_t state; ASSERT(prt); - ERTS_LC_ASSERT(erts_lc_is_emu_thr()); +// ERTS_LC_ASSERT(erts_lc_is_emu_thr()); if (prt == ERTS_INVALID_ERL_DRV_PORT) return ERTS_INVALID_ERL_DRV_PORT; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt) @@ -949,4 +949,9 @@ ErtsPortOpResult erts_port_control(Process *, Port *, unsigned int, Eterm, Eterm ErtsPortOpResult erts_port_call(Process *, Port *, unsigned int, Eterm, Eterm *); ErtsPortOpResult erts_port_info(Process *, Port *, Eterm, Eterm *); +/* + * Signals from ports to ports. Used by sys drivers. + */ +int erl_drv_port_control(Eterm, char, char*, ErlDrvSizeT); + #endif diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 15a6d5d651..03cb3dc254 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -497,9 +497,6 @@ dbg_chk_aux_work_val(erts_aint32_t value) #if HAVE_ERTS_MSEG valid |= ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK; #endif -#ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN - valid |= ERTS_SSI_AUX_WORK_CHECK_CHILDREN; -#endif #ifdef ERTS_SSI_AUX_WORK_REAP_PORTS valid |= ERTS_SSI_AUX_WORK_REAP_PORTS; #endif @@ -586,8 +583,6 @@ erts_pre_init_process(void) = "MISC_THR_PRGR"; erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MISC_IX] = "MISC"; - erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_CHECK_CHILDREN_IX] - = "CHECK_CHILDREN"; erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_SET_TMO_IX] = "SET_TMO"; erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX] @@ -2100,34 +2095,6 @@ erts_debug_wait_completed(Process *c_p, int flags) } -#ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN -void -erts_smp_notify_check_children_needed(void) -{ - int i; - for (i = 0; i < erts_no_schedulers; i++) - set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(i), - ERTS_SSI_AUX_WORK_CHECK_CHILDREN); -#ifdef ERTS_DIRTY_SCHEDULERS - for (i = 0; i < erts_no_dirty_cpu_schedulers; i++) - set_aux_work_flags_wakeup_nob(ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(i), - ERTS_SSI_AUX_WORK_CHECK_CHILDREN); - for (i = 0; i < erts_no_dirty_io_schedulers; i++) - set_aux_work_flags_wakeup_nob(ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(i), - ERTS_SSI_AUX_WORK_CHECK_CHILDREN); -#endif -} - -static ERTS_INLINE erts_aint32_t -handle_check_children(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) -{ - unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_CHECK_CHILDREN); - erts_check_children(); - return aux_work & ~ERTS_SSI_AUX_WORK_CHECK_CHILDREN; -} - -#endif - static void notify_reap_ports_relb(void) { @@ -2281,10 +2248,6 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting) HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_MISC, handle_misc_aux_work); -#ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN - HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_CHECK_CHILDREN, - handle_check_children); -#endif HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_SET_TMO, handle_setup_aux_work_timer); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index e7c5614b9c..ef58fef102 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -293,7 +293,6 @@ typedef enum { ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN_IX, ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX, ERTS_SSI_AUX_WORK_MISC_IX, - ERTS_SSI_AUX_WORK_CHECK_CHILDREN_IX, ERTS_SSI_AUX_WORK_SET_TMO_IX, ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX, ERTS_SSI_AUX_WORK_REAP_PORTS_IX, @@ -326,8 +325,6 @@ typedef enum { (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX) #define ERTS_SSI_AUX_WORK_MISC \ (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_IX) -#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN \ - (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_CHECK_CHILDREN_IX) #define ERTS_SSI_AUX_WORK_SET_TMO \ (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_SET_TMO_IX) #define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK \ @@ -1643,11 +1640,8 @@ Eterm erts_multi_scheduling_blockers(Process *); void erts_start_schedulers(void); void erts_alloc_notify_delayed_dealloc(int); void erts_alloc_ensure_handle_delayed_dealloc_call(int); -#ifdef ERTS_SMP void erts_notify_canceled_timer(ErtsSchedulerData *, int); #endif -void erts_smp_notify_check_children_needed(void); -#endif #if ERTS_USE_ASYNC_READY_Q void erts_notify_check_async_ready_queue(void *); #endif diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index b4d02dd1dd..4352d5758b 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1322,6 +1322,7 @@ extern void erts_match_prog_foreach_offheap(Binary *b, extern erts_driver_t vanilla_driver; extern erts_driver_t spawn_driver; +extern erts_driver_t forker_driver; extern erts_driver_t fd_driver; int erts_beam_jump_table(void); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 2a3759212e..6ec7a9ba1e 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -54,6 +54,7 @@ extern ErlDrvEntry fd_driver_entry; extern ErlDrvEntry vanilla_driver_entry; extern ErlDrvEntry spawn_driver_entry; +extern ErlDrvEntry forker_driver_entry; extern ErlDrvEntry *driver_tab[]; /* table of static drivers, only used during initialization */ erts_driver_t *driver_list; /* List of all drivers, static and dynamic. */ @@ -71,6 +72,7 @@ const Port erts_invalid_port = {{ERTS_INVALID_PORT}}; erts_driver_t vanilla_driver; erts_driver_t spawn_driver; +erts_driver_t forker_driver; erts_driver_t fd_driver; int erts_port_synchronous_ops = 0; @@ -306,12 +308,9 @@ static Port *create_port(char *name, size_t port_size, busy_port_queue_size, size; erts_aint32_t state = ERTS_PORT_SFLG_CONNECTED; erts_aint32_t x_pts_flgs = 0; -#ifdef DEBUG - /* Make sure the debug flags survives until port is freed */ - state |= ERTS_PORT_SFLG_PORT_DEBUG; -#endif #ifdef ERTS_SMP + ErtsRunQueue *runq; if (!driver_lock) { /* Align size for mutex following port struct */ port_size = size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port)); @@ -321,6 +320,12 @@ static Port *create_port(char *name, #endif port_size = size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port)); +#ifdef DEBUG + /* Make sure the debug flags survives until port is freed */ + state |= ERTS_PORT_SFLG_PORT_DEBUG; +#endif + + busy_port_queue_size = ((driver->flags & ERL_DRV_FLAG_NO_BUSY_MSGQ) ? 0 @@ -356,8 +361,12 @@ static Port *create_port(char *name, p += sizeof(erts_mtx_t); state |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK; } - erts_smp_atomic_set_nob(&prt->run_queue, - (erts_aint_t) erts_get_runq_current(NULL)); + if (erts_get_scheduler_data()) + runq = erts_get_runq_current(NULL); + else + runq = ERTS_RUNQ_IX(0); + erts_smp_atomic_set_nob(&prt->run_queue, (erts_aint_t) runq); + prt->xports = NULL; #else erts_atomic32_init_nob(&prt->refc, 1); @@ -1535,6 +1544,26 @@ erts_schedule_proc2port_signal(Process *c_p, return ERTS_PORT_OP_SCHEDULED; } +static int +erts_schedule_port2port_signal(Eterm port_num, ErtsProc2PortSigData *sigdp, + int task_flags, + ErtsProc2PortSigCallback callback) +{ + Port *prt = erts_port_lookup_raw(port_num); + + if (!prt) + return -1; + + sigdp->caller = ERTS_INVALID_PID; + + return erts_port_task_schedule(prt->common.id, + NULL, + ERTS_PORT_TASK_PROC_SIG, + sigdp, + callback, + task_flags); +} + static ERTS_INLINE void send_badsig(Port *prt) { ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; @@ -2862,6 +2891,7 @@ void erts_init_io(int port_tab_size, init_driver(&fd_driver, &fd_driver_entry, NULL); init_driver(&vanilla_driver, &vanilla_driver_entry, NULL); init_driver(&spawn_driver, &spawn_driver_entry, NULL); + init_driver(&forker_driver, &forker_driver_entry, NULL); erts_init_static_drivers(); for (dp = driver_tab; *dp != NULL; dp++) erts_add_driver_entry(*dp, NULL, 1); @@ -2923,6 +2953,7 @@ void erts_lcnt_enable_io_lock_count(int enable) { lcnt_enable_drv_lock_count(&vanilla_driver, enable); lcnt_enable_drv_lock_count(&spawn_driver, enable); + lcnt_enable_drv_lock_count(&forker_driver, enable); lcnt_enable_drv_lock_count(&fd_driver, enable); /* enable lock counting in all drivers */ for (dp = driver_list; dp; dp = dp->next) { @@ -3964,7 +3995,7 @@ port_sig_control(Port *prt, Uint hsz, rsz; int control_flags; - rp = erts_proc_lookup_raw(sigdp->caller); + rp = sigdp->caller == ERTS_INVALID_PID ? NULL : erts_proc_lookup_raw(sigdp->caller); if (!rp) goto done; @@ -4007,7 +4038,8 @@ port_sig_control(Port *prt, /* failure */ - port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg); + if (sigdp->caller != ERTS_INVALID_PID) + port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg); done: @@ -4017,6 +4049,23 @@ done: return ERTS_PORT_REDS_CONTROL; } +/* + * This is an asynchronous control call. I.e. it will not return anything + * to the caller. + */ +int +erl_drv_port_control(Eterm port_num, char cmd, char* buff, ErlDrvSizeT size) +{ + ErtsProc2PortSigData *sigdp = erts_port_task_alloc_p2p_sig_data(); + + sigdp->flags = ERTS_P2P_SIG_TYPE_CONTROL | ERTS_P2P_SIG_DATA_FLG_REPLY; + sigdp->u.control.binp = NULL; + sigdp->u.control.command = cmd; + sigdp->u.control.bufp = buff; + sigdp->u.control.size = size; + + return erts_schedule_port2port_signal(port_num, sigdp, 0, port_sig_control); +} ErtsPortOpResult erts_port_control(Process* c_p, @@ -4794,6 +4843,8 @@ print_port_info(Port *p, int to, void *arg) erts_print(to, arg, "Port is a file: %s\n",p->name); } else if (p->drv_ptr == &spawn_driver) { erts_print(to, arg, "Port controls external process: %s\n",p->name); + } else if (p->drv_ptr == &forker_driver) { + erts_print(to, arg, "Port controls forker process: %s\n",p->name); } else { erts_print(to, arg, "Port controls linked-in driver: %s\n",p->name); } diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 34011147d9..522d899c94 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -61,15 +61,6 @@ # define NO_FPE_SIGNALS #endif -#ifdef DISABLE_CHILD_WAITER_THREAD -#undef ENABLE_CHILD_WAITER_THREAD -#endif - -#if defined(ERTS_SMP) && !defined(DISABLE_CHILD_WAITER_THREAD) -#undef ENABLE_CHILD_WAITER_THREAD -#define ENABLE_CHILD_WAITER_THREAD 1 -#endif - #define ERTS_I64_LITERAL(X) X##LL #if defined (__WIN32__) @@ -731,6 +722,7 @@ void erts_sys_main_thread(void); extern int erts_sys_prepare_crash_dump(int secs); extern void erts_sys_pre_init(void); extern void erl_sys_init(void); +extern void erl_sys_late_init(void); extern void erl_sys_args(int *argc, char **argv); extern void erl_sys_schedule(int); void sys_tty_reset(int); diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index a3c5c20641..71c7948bf0 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2009. All Rights Reserved. + * Copyright Ericsson AB 2002-2015. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,109 +19,195 @@ */ /* - * After a vfork() (or fork()) the child exec()s to this program which - * sets up the child and exec()s to the user program (see spawn_start() - * in sys.c and ticket OTP-4389). + * This program is started at erts startup and all fork's that + * have to be done are done in here. This is done for a couple + * of reasons: + * - Allow usage of fork without a memory explosion. + * -- we do not want to use vfork, as it blocks the VM + * until the execv is done, and if the program that + * is to be executed is on an NFS that is unavailable, + * the execv can block for a very long time. + * -- we cannot do fork inside the VM as that would temporarily + * duplicate the memory usage of the VM per parallel exec. + * + * Some implementation notes: + * - A single Unix Domain Socket is setup in between the VM and + * this program. Over that UDS the file descriptors that should + * be used to talk to the child program are sent. + * The actual command to execute, together with options and the + * environment, is sent over the pipe represented by the + * file descriptors mentioned above. We don't send the + * command over the UDS as that would increase the likely hood + * that it's buffer would be full. + * + * - Since it is this program that execv's, it has to take care of + * all the SIGCHLD signals that the child programs generate. The + * signals are received and the pid+exit reason is sent as data + * on the UDS to the VM. The VM is then able to map the pid to the + * port of the child program that just exited and deliver the status + * code if requested. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#define NEED_CHILD_SETUP_DEFINES -#include "sys.h" -#include "erl_misc_utils.h" +#include +#include +#include -#ifdef SIG_SIGSET /* Old SysV */ -void sys_sigrelease(int sig) -{ - sigrelse(sig); -} -#else /* !SIG_SIGSET */ -#ifdef SIG_SIGNAL /* Old BSD */ -sys_sigrelease(int sig) +#include "erl_driver.h" +#include "sys_uds.h" + +#define SET_CLOEXEC(fd) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) + +#if defined(__ANDROID__) +#define SHELL "/system/bin/sh" +#else +#define SHELL "/bin/sh" +#endif /* __ANDROID__ */ + +//#define HARD_DEBUG +#ifdef HARD_DEBUG +#define DEBUG_PRINT(fmt, ...) fprintf(stderr, fmt "\r\n", ##__VA_ARGS__) +#else +#define DEBUG_PRINT(fmt, ...) +#endif + +#define ABORT(fmt, ...) do { \ + fprintf(stderr, "erl_child_setup: " fmt "\r\n", ##__VA_ARGS__); \ + abort(); \ + } while(0) + +#undef ASSERT +#ifdef DEBUG +#define ASSERT(cnd) do { if (!(cnd)) { ABORT("assertion %s failed", #cnd); } } while(0) +#else +#define ASSERT(cnd) +#endif + +void sys_sigblock(int sig) { - sigsetmask(sigblock(0) & ~sigmask(sig)); + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, sig); + sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL); } -#else /* !SIG_SIGNAL */ /* The True Way - POSIX!:-) */ + void sys_sigrelease(int sig) { sigset_t mask; - sigemptyset(&mask); sigaddset(&mask, sig); sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL); } -#endif /* !SIG_SIGNAL */ -#endif /* !SIG_SIGSET */ -#if defined(__ANDROID__) -static int system_properties_fd(void); -#endif /* __ANDROID__ */ +static int max_files = -1; +static int sigchld_pipe[2]; -#if defined(__ANDROID__) -#define SHELL "/system/bin/sh" -#else -#define SHELL "/bin/sh" -#endif /* __ANDROID__ */ +static int +start_new_child(int pipes[]) +{ + int size, res, i; + char *buff, *o_buff; + char *cmd, *wd, **new_environ, **args = NULL, cbuff[1]; -int -main(int argc, char *argv[]) -{ - int i, from, to; - int erts_spawn_executable = 0; - - /* OBSERVE! - * Keep child setup after fork() (implemented in sys.c) up to date - * if changes are made here. - */ - - if (argc != CS_ARGV_NO_OF_ARGS) { - if (argc < CS_ARGV_NO_OF_ARGS) { - return 1; - } else { - erts_spawn_executable = 1; - } + Sint cnt, flags; + + /* only child executes here */ + + if ((res = read(pipes[0], (char*)&size, sizeof(size))) < 0) { + ABORT("Failed to read size from %d (%d)", pipes[0], errno); } - if (strcmp("false", argv[CS_ARGV_UNBIND_IX]) != 0) - if (erts_unbind_from_cpu_str(argv[CS_ARGV_UNBIND_IX]) != 0) - return 1; - - for (i = 0; i < CS_ARGV_NO_OF_DUP2_OPS; i++) { - if (argv[CS_ARGV_DUP2_OP_IX(i)][0] == '-' - && argv[CS_ARGV_DUP2_OP_IX(i)][1] == '\0') - break; - if (sscanf(argv[CS_ARGV_DUP2_OP_IX(i)], "%d:%d", &from, &to) != 2) - return 1; - if (dup2(from, to) < 0) - return 1; + buff = malloc(size); + + DEBUG_PRINT("size = %d", size); + + if ((res = read(pipes[0], buff, size)) != size) { + ABORT("Failed to read %d bytes from %d (%d,%d)", + size, pipes[0], res, errno); } - if (sscanf(argv[CS_ARGV_FD_CR_IX], "%d:%d", &from, &to) != 2) - return 1; + o_buff = buff; -#if defined(HAVE_CLOSEFROM) - closefrom(from); -#elif defined(__ANDROID__) - if (from <= to) { - int spfd = system_properties_fd(); - for (i = from; i <= to; i++) { - if (i != spfd) { - (void) close(i); - } - } + flags = *(int*)buff; + buff += sizeof(int); + + DEBUG_PRINT("flags = %d", flags); + + cmd = buff; + buff += strlen(buff) + 1; + if (*buff == '\0') { + wd = NULL; + } else { + wd = buff; + buff += strlen(buff) + 1; + } + buff++; + + DEBUG_PRINT("wd = %s", wd); + + cnt = *(int*)buff; + buff += sizeof(int); + new_environ = malloc(sizeof(char*)*(cnt + 1)); + + for (i = 0; i < cnt; i++, buff++) { + new_environ[i] = buff; + while(*buff != '\0') buff++; + } + new_environ[cnt] = NULL; + + if (o_buff + size != buff) { + /* This is a spawn executable call */ + cnt = *(int*)buff; + buff += sizeof(int); + args = malloc(sizeof(char*)*(cnt + 1)); + for (i = 0; i < cnt; i++, buff++) { + args[i] = buff; + while(*buff != '\0') buff++; + } + args[cnt] = NULL; + } + + if (o_buff + size != buff) { + ABORT("Buff error: %p, %p:%p", o_buff, o_buff+size, buff); + } + + if (read(pipes[0], cbuff, 1) < 1) + goto child_error; + + DEBUG_PRINT("Do that forking business: '%s'\n",cmd); + + /* When the dup2'ing below is done, only + fd's 0, 1, 2 and maybe 3, 4 should survive the + exec. All other fds (i.e. the unix domain sockets + and stray pipe ends) should have CLOEXEC set on them + so they will be closed when the exec happens */ + if (flags & FORKER_FLAG_USE_STDIO) { + /* stdin for process */ + if (flags & FORKER_FLAG_DO_WRITE && + dup2(pipes[0], 0) < 0) + goto child_error; + /* stdout for process */ + if (flags & FORKER_FLAG_DO_READ && + dup2(pipes[1], 1) < 0) + goto child_error; } -#else /* !__ANDROID__ */ - for (i = from; i <= to; i++) { - (void) close(i); + else { /* XXX will fail if pipes[0] == 4 (unlikely..) */ + if (flags & FORKER_FLAG_DO_READ && dup2(pipes[1], 4) < 0) + goto child_error; + if (flags & FORKER_FLAG_DO_WRITE && dup2(pipes[0], 3) < 0) + goto child_error; } -#endif /* HAVE_CLOSEFROM */ - if (!(argv[CS_ARGV_WD_IX][0] == '.' && argv[CS_ARGV_WD_IX][1] == '\0') - && chdir(argv[CS_ARGV_WD_IX]) < 0) - return 1; + if (dup2(pipes[2], 2) < 0) + goto child_error; + + if (wd && chdir(wd) < 0) + goto child_error; #if defined(USE_SETPGRP_NOARGS) /* SysV */ (void) setpgrp(); @@ -131,34 +217,197 @@ main(int argc, char *argv[]) (void) setsid(); #endif + close(pipes[0]); + close(pipes[1]); + close(pipes[2]); + sys_sigrelease(SIGCHLD); - sys_sigrelease(SIGINT); - sys_sigrelease(SIGUSR1); - - if (erts_spawn_executable) { - if (argv[CS_ARGV_NO_OF_ARGS + 1] == NULL) { - execl(argv[CS_ARGV_NO_OF_ARGS],argv[CS_ARGV_NO_OF_ARGS], - (char *) NULL); - } else { - execv(argv[CS_ARGV_NO_OF_ARGS],&(argv[CS_ARGV_NO_OF_ARGS + 1])); - } + + if (args) { + /* spawn_executable */ + execve(cmd, args, new_environ); } else { - execl(SHELL, "sh", "-c", argv[CS_ARGV_CMD_IX], (char *) NULL); + execle(SHELL, "sh", "-c", cmd, (char *) NULL, new_environ); } - return 1; +child_error: + _exit(1); +} + + +/* + * [OTP-3906] + * Solaris signal management gets confused when threads are used and a + * lot of child processes dies. The confusion results in that SIGCHLD + * signals aren't delivered to the emulator which in turn results in + * a lot of defunct processes in the system. + * + * The problem seems to appear when a signal is frequently + * blocked/unblocked at the same time as the signal is frequently + * propagated. The child waiter thread is a workaround for this problem. + * The SIGCHLD signal is always blocked (in all threads), and the child + * waiter thread fetches the signal by a call to sigwait(). See + * child_waiter(). + * + * This should be a non-issue since the fork:ing was moved outside of + * the emulator into erl_child_setup. I'm leaving the comment here + * for posterity. */ + +static void handle_sigchld(int sig) { + int buff[2], res; + + sys_sigblock(SIGCHLD); + + while ((buff[0] = waitpid((pid_t)(-1), buff+1, WNOHANG)) > 0) { + if ((res = write(sigchld_pipe[1], buff, sizeof(buff))) <= 0) + ABORT("Failed to write to sigchld_pipe (%d): %d (%d)", sigchld_pipe[1], res, errno); + DEBUG_PRINT("Reap child %d (%d)", buff[0], buff[1]); + } + + sys_sigrelease(SIGCHLD); } #if defined(__ANDROID__) static int system_properties_fd(void) { - int fd; + static int fd = -2; char *env; + if (fd != -2) return fd; env = getenv("ANDROID_PROPERTY_WORKSPACE"); if (!env) { + fd = -1; return -1; } fd = atoi(env); return fd; } #endif /* __ANDROID__ */ + +int +main(int argc, char *argv[]) +{ + /* This fd should be open from beam */ + int uds_fd = 3, max_fd = 3; +#ifndef HAVE_CLOSEFROM + int i; +#endif + struct sigaction sa; + + if (argc < 1 || sscanf(argv[1],"%d",&max_files) != 1) { + ABORT("Invalid arguments to child_setup"); + } + +/* We close all fds except the uds from beam. + All other fds from now on will have the + CLOEXEC flags set on them. This means that we + only have to close a very limited number of fds + after we fork before the exec. */ +#if defined(HAVE_CLOSEFROM) + closefrom(4); +#else + for (i = 4; i < max_files; i++) +#if defined(__ANDROID__) + if (i != system_properties_fd()) +#endif + (void) close(i); +#endif + + if (pipe(sigchld_pipe) < 0) { + ABORT("Failed to setup sigchld pipe (%d)", errno); + } + + SET_CLOEXEC(sigchld_pipe[0]); + SET_CLOEXEC(sigchld_pipe[1]); + + max_fd = max_fd < sigchld_pipe[0] ? sigchld_pipe[0] : max_fd; + + sa.sa_handler = &handle_sigchld; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; + if (sigaction(SIGCHLD, &sa, 0) == -1) { + perror(0); + exit(1); + } + + SET_CLOEXEC(uds_fd); + + DEBUG_PRINT("Starting forker %d", max_files); + + while (1) { + fd_set read_fds; + int res; + FD_ZERO(&read_fds); + FD_SET(uds_fd, &read_fds); + FD_SET(sigchld_pipe[0], &read_fds); + DEBUG_PRINT("child_setup selecting on %d, %d (%d)", + uds_fd, sigchld_pipe[0], max_fd); + res = select(max_fd+1, &read_fds, NULL, NULL, NULL); + + if (res < 0) { + if (errno == EINTR) continue; + ABORT("Select failed: %d (%d)",res, errno); + } + + if (FD_ISSET(uds_fd, &read_fds)) { + int pipes[3], res, os_pid; + char buff[256]; + errno = 0; + if ((res = sys_uds_read(uds_fd, buff, 1, pipes, 3, MSG_DONTWAIT)) < 0) { + if (errno == EINTR) + continue; + DEBUG_PRINT("erl_child_setup failed to read from uds: %d, %d", res, errno); + _exit(0); + } + + if (res == 0) { + DEBUG_PRINT("uds was closed!"); + _exit(0); + } + /* Since we use unix domain sockets and send the entire data in + one go we *should* get the entire payload at once. */ + ASSERT(res == 1); + ASSERT(buff[0] == 'S'); + + sys_sigblock(SIGCHLD); + + errno = 0; + + os_pid = fork(); + if (os_pid == 0) + start_new_child(pipes); + + /* We write an ack here, but expect the reply on + the pipes[0] inside the fork */ + res = sprintf(buff,"GO:%010d:%010d", os_pid, errno); + if (write(pipes[1], buff, res + 1)) + ; /* remove gcc warning */ + + sys_sigrelease(SIGCHLD); + close(pipes[0]); + close(pipes[1]); + close(pipes[2]); + } + + if (FD_ISSET(sigchld_pipe[0], &read_fds)) { + int ibuff[2]; + char buff[256]; + res = read(sigchld_pipe[0], ibuff, sizeof(ibuff)); + if (res < 0) { + if (errno == EINTR) + continue; + ABORT("Failed to read from sigchld pipe: %d (%d)", res, errno); + } + res = snprintf(buff, 256, "SIGCHLD:%010d:%010d", ibuff[0], ibuff[1]); + DEBUG_PRINT("send %s to %d", buff, uds_fd); + if (write(uds_fd, buff, res + 1) < 0) { + if (errno == EINTR) + continue; + /* The uds was close, which most likely means that the VM + has exited. This will be detected when we try to read + from the uds_fd. */ + DEBUG_PRINT("Failed to write to uds: %d (%d)", uds_fd, errno); + } + } + } + return 1; +} diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index a11a44c259..c46f2c1fa2 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -30,9 +30,7 @@ #include #include #include -#ifndef QNX #include -#endif #if defined(__sun__) && defined(__SVR4) && !defined(__EXTENSIONS__) # define __EXTENSIONS__ @@ -92,11 +90,6 @@ #include #endif -#ifdef QNX -#include -#include -#endif - #include #ifndef HZ @@ -136,13 +129,6 @@ # define ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT #endif -#ifndef ENABLE_CHILD_WAITER_THREAD -# ifdef ERTS_SMP -# define ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN -void erts_check_children(void); -# endif -#endif - typedef void *GETENV_STATE; /* @@ -310,7 +296,6 @@ typedef void (*SIGFUNC)(int); extern SIGFUNC sys_signal(int, SIGFUNC); extern void sys_sigrelease(int); extern void sys_sigblock(int); -extern void sys_stop_cat(void); /* * Handling of floating point exceptions. @@ -425,18 +410,14 @@ void erts_sys_unblock_fpe(int); #define ERTS_FP_ERROR_THOROUGH(p, f, A) __ERTS_FP_ERROR_THOROUGH(&(p)->fp_exception, f, A) -#ifdef NEED_CHILD_SETUP_DEFINES -/* The child setup argv[] */ -#define CS_ARGV_PROGNAME_IX 0 /* Program name */ -#define CS_ARGV_UNBIND_IX 1 /* Unbind from cpu */ -#define CS_ARGV_WD_IX 2 /* Working directory */ -#define CS_ARGV_CMD_IX 3 /* Command */ -#define CS_ARGV_FD_CR_IX 4 /* Fd close range */ -#define CS_ARGV_DUP2_OP_IX(N) ((N) + 5) /* dup2 operations */ +#define FORKER_ARGV_NO_OF_ARGS 3 +#define FORKER_ARGV_PROGNAME_IX 0 /* Program name */ +#define FORKER_ARGV_MAX_FILES 1 /* max_files */ -#define CS_ARGV_NO_OF_DUP2_OPS 3 /* Number of dup2 ops */ -#define CS_ARGV_NO_OF_ARGS 8 /* Number of arguments */ -#endif /* #ifdef NEED_CHILD_SETUP_DEFINES */ +#define FORKER_FLAG_USE_STDIO (1 << 0) /* dup the pipe to stdin/stderr */ +#define FORKER_FLAG_EXIT_STATUS (1 << 1) /* send the exit status to parent */ +#define FORKER_FLAG_DO_READ (1 << 2) /* dup write fd */ +#define FORKER_FLAG_DO_WRITE (1 << 3) /* dup read fd */ /* Threads */ #ifdef USE_THREADS @@ -470,20 +451,4 @@ extern jmp_buf erts_sys_sigsegv_jmp; } while(0) #endif -#ifdef USE_THREADS -# ifdef ENABLE_CHILD_WAITER_THREAD -# define CHLDWTHR ENABLE_CHILD_WAITER_THREAD -# else -# define CHLDWTHR 0 -# endif -# define FDBLOCK 1 -#else -# define CHLDWTHR 0 -# define FDBLOCK 0 -#endif - -#define ERTS_SYS_SUSPEND_SIGNAL SIGUSR2 - -int check_children(void); - #endif /* #ifndef _ERL_UNIX_SYS_H */ diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 503ef5c2f6..2ad5f3b4d5 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -49,7 +49,6 @@ #include #endif -#define NEED_CHILD_SETUP_DEFINES #define ERTS_WANT_BREAK_HANDLING #define ERTS_WANT_GOT_SIGUSR1 #define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */ @@ -93,6 +92,7 @@ extern void erts_sys_init_float(void); extern void erl_crash_dump(char* file, int line, char* fmt, ...); + #ifdef DEBUG static int debug_log = 0; #endif @@ -122,6 +122,7 @@ static void smp_sig_notify(char c); static int sig_notify_fds[2] = {-1, -1}; static int sig_suspend_fds[2] = {-1, -1}; +#define ERTS_SYS_SUSPEND_SIGNAL SIGUSR2 #endif @@ -307,9 +308,10 @@ MALLOC_USE_HASH(1); #ifdef USE_THREADS #ifdef ERTS_THR_HAVE_SIG_FUNCS + /* * Child thread inherits parents signal mask at creation. In order to - * guarantee that the main thread will receive all SIGINT, SIGCHLD, and + * guarantee that the main thread will receive all SIGINT, and * SIGUSR1 signals sent to the process, we block these signals in the * parent thread when creating a new thread. */ @@ -405,7 +407,6 @@ erts_sys_pre_init(void) #ifdef ERTS_THR_HAVE_SIG_FUNCS sigemptyset(&thr_create_sigmask); sigaddset(&thr_create_sigmask, SIGINT); /* block interrupt */ - sigaddset(&thr_create_sigmask, SIGCHLD); /* block child signals */ sigaddset(&thr_create_sigmask, SIGUSR1); /* block user defined signal */ #endif @@ -1010,7 +1011,6 @@ erts_sys_unsetenv(char *key) void sys_init_io(void) { - } #if (0) /* unused? */ @@ -1204,7 +1204,6 @@ erl_debug(char* fmt, ...) #endif /* DEBUG */ - /* * Called from schedule() when it runs out of runnable processes, * or when Erlang code has performed INPUT_REDUCTIONS reduction @@ -1213,15 +1212,11 @@ erl_debug(char* fmt, ...) void erl_sys_schedule(int runnable) { -#ifdef ERTS_SMP ERTS_CHK_IO(!runnable); -#else - ERTS_CHK_IO(runnable ? 0 : !check_children()); -#endif ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); - (void) check_children(); } + #ifdef ERTS_SMP static erts_smp_tid_t sig_dispatcher_tid; @@ -1246,10 +1241,6 @@ smp_sig_notify(char c) static void * signal_dispatcher_thread_func(void *unused) { -#if !CHLDWTHR - int initialized = 0; - int notify_check_children = 0; -#endif #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_set_thread_name("signal_dispatcher"); #endif @@ -1287,19 +1278,7 @@ signal_dispatcher_thread_func(void *unused) */ switch (buf[i]) { case 0: /* Emulator initialized */ -#if !CHLDWTHR - initialized = 1; - if (!notify_check_children) -#endif - break; -#if !CHLDWTHR - case 'C': /* SIGCHLD */ - if (initialized) - erts_smp_notify_check_children_needed(); - else - notify_check_children = 1; - break; -#endif + break; case 'I': /* SIGINT */ break_requested(); break; diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index e3f089fc0f..8402197924 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -36,6 +36,7 @@ #include #include #include +#include #ifdef ISC32 #include @@ -49,26 +50,20 @@ #include #endif -#define NEED_CHILD_SETUP_DEFINES #define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */ #include "sys.h" -#include "erl_thr_progress.h" - -#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) -#define __DARWIN__ 1 -#endif #ifdef USE_THREADS #include "erl_threads.h" #endif -#include "erl_mseg.h" - extern char **environ; extern erts_smp_rwmtx_t environ_rwmtx; extern erts_smp_atomic_t sys_misc_mem_sz; +static Eterm forker_port; + #define MAX_VSIZE 16 /* Max number of entries allowed in an I/O * vector sock_sendv(). */ @@ -76,14 +71,10 @@ extern erts_smp_atomic_t sys_misc_mem_sz; * Don't need global.h, but erl_cpu_topology.h won't compile otherwise */ #include "global.h" - -#include "erl_sys_driver.h" -#include "erl_check_io.h" #include "erl_cpu_topology.h" -#ifndef DISABLE_VFORK -#define DISABLE_VFORK 0 -#endif +#include "erl_sys_driver.h" +#include "sys_uds.h" #if defined IOV_MAX #define MAXIOV IOV_MAX @@ -93,32 +84,11 @@ extern erts_smp_atomic_t sys_misc_mem_sz; #define MAXIOV 16 #endif -/* - * [OTP-3906] - * Solaris signal management gets confused when threads are used and a - * lot of child processes dies. The confusion results in that SIGCHLD - * signals aren't delivered to the emulator which in turn results in - * a lot of defunct processes in the system. - * - * The problem seems to appear when a signal is frequently - * blocked/unblocked at the same time as the signal is frequently - * propagated. The child waiter thread is a workaround for this problem. - * The SIGCHLD signal is always blocked (in all threads), and the child - * waiter thread fetches the signal by a call to sigwait(). See - * child_waiter(). - */ - -typedef struct ErtsSysReportExit_ ErtsSysReportExit; -struct ErtsSysReportExit_ { - ErtsSysReportExit *next; - Eterm port; - int pid; - int ifd; - int ofd; -#if CHLDWTHR && !defined(ERTS_SMP) - int status; +#ifdef USE_THREADS +# define FDBLOCK 1 +#else +# define FDBLOCK 0 #endif -}; /* Used by the fd driver iff the fd could not be set to non-blocking */ typedef struct ErtsSysBlocking_ { @@ -128,12 +98,21 @@ typedef struct ErtsSysBlocking_ { unsigned int pkey; } ErtsSysBlocking; +typedef struct fd_data { + int fd; + char pbuf[4]; /* hold partial packet bytes */ + int psz; /* size of pbuf */ + char *buf; + char *cpos; + int sz; + int remain; /* for input on fd */ +} ErtsSysFdData; -/* This data is shared by these drivers - initialized by spawn_init() */ typedef struct driver_data { ErlDrvPort port_num; - int ofd, packet_bytes; - ErtsSysReportExit *report_exit; + ErtsSysFdData *ofd; + ErtsSysFdData *ifd; + int packet_bytes; int pid; int alive; int status; @@ -141,12 +120,11 @@ typedef struct driver_data { ErtsSysBlocking *blocking; } ErtsSysDriverData; -static ErtsSysDriverData *driver_data; /* indexed by fd */ - -static ErtsSysReportExit *report_exit_list; -#if CHLDWTHR && !defined(ERTS_SMP) -static ErtsSysReportExit *report_exit_transit_list; -#endif +typedef struct exit_status { + HashBucket hb; + pid_t os_pid; + Eterm port_id; +} ErtsSysExitStatus; #define DIR_SEPARATOR_CHAR '/' @@ -156,7 +134,6 @@ static ErtsSysReportExit *report_exit_transit_list; #define SHELL "/bin/sh" #endif /* __ANDROID__ */ - #if defined(DEBUG) #define ERL_BUILD_TYPE_MARKER ".debug" #elif defined(PURIFY) @@ -171,103 +148,80 @@ static ErtsSysReportExit *report_exit_transit_list; #define ERL_BUILD_TYPE_MARKER #endif +#ifdef DEBUG +#define close(fd) do { int res = close(fd); ASSERT(res > -1); } while(0) +#endif + #define CHILD_SETUP_PROG_NAME "erl_child_setup" ERL_BUILD_TYPE_MARKER -static char *child_setup_prog; -#if CHLDWTHR || defined(ERTS_SMP) -erts_mtx_t chld_stat_mtx; -#endif -#if CHLDWTHR -static erts_tid_t child_waiter_tid; -/* chld_stat_mtx is used to protect against concurrent accesses - of the driver_data fields pid, alive, and status. */ -erts_cnd_t chld_stat_cnd; -static long children_alive; -#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx) -#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx) -#define CHLD_STAT_WAIT erts_cnd_wait(&chld_stat_cnd, &chld_stat_mtx) -#define CHLD_STAT_SIGNAL erts_cnd_signal(&chld_stat_cnd) -#elif defined(ERTS_SMP) /* ------------------------------------------------- */ -#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx) -#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx) - -#else /* ------------------------------------------------------------------- */ -#define CHLD_STAT_LOCK -#define CHLD_STAT_UNLOCK -static volatile int children_died; +// #define HARD_DEBUG +#ifdef HARD_DEBUG +#define driver_select(port_num, fd, flags, onoff) \ + do { \ + if (((flags) & ERL_DRV_READ) && onoff) \ + fprintf(stderr,"%010d %p: read select %d\r\n", __LINE__, port_num, (int)fd); \ + if (((flags) & ERL_DRV_WRITE) && onoff) \ + fprintf(stderr,"%010d %p: writ select %d\r\n", __LINE__, port_num, (int)fd); \ + if (((flags) & ERL_DRV_READ) && !onoff) \ + fprintf(stderr,"%010d %p: read unsele %d\r\n", __LINE__, port_num, (int)fd); \ + if (((flags) & ERL_DRV_WRITE) && !onoff) \ + fprintf(stderr,"%010d %p: writ unsele %d\r\n", __LINE__, port_num, (int)fd); \ + driver_select_nkp(port_num, fd, flags, onoff); \ + } while(0) #endif -typedef struct ErtsSysFdData { - char pbuf[4]; /* hold partial packet bytes */ - int psz; /* size of pbuf */ - char *buf; - char *cpos; - int sz; - int remain; /* for input on fd */ -} ErtsSysFdData; - -static ErtsSysFdData *fd_data; /* indexed by fd */ +/* + * Decreasing the size of it below 16384 is not allowed. + */ -/* static FUNCTION(int, write_fill, (int, char*, int)); unused? */ -static void note_child_death(int, int); +#define ERTS_SYS_READ_BUF_SZ (64*1024) -#if CHLDWTHR -static void* child_waiter(void *); -#endif +/* I. Initialization */ -static void block_signals(void) +void +erl_sys_late_init(void) { -#if !CHLDWTHR - sys_sigblock(SIGCHLD); -#endif -#ifndef ERTS_SMP - sys_sigblock(SIGINT); -#ifndef ETHR_UNUSABLE_SIGUSRX - sys_sigblock(SIGUSR1); -#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ -#endif /* #ifndef ERTS_SMP */ - -#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX) - sys_sigblock(ERTS_SYS_SUSPEND_SIGNAL); + SysDriverOpts opts; +#ifdef ERTS_SMP + Port *port; #endif -} + sys_signal(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */ + + opts.packet_bytes = 0; + opts.use_stdio = 1; + opts.redir_stderr = 0; + opts.read_write = 0; + opts.hide_window = 0; + opts.wd = NULL; + opts.envir = NULL; + opts.exit_status = 0; + opts.overlapped_io = 0; + opts.spawn_type = ERTS_SPAWN_ANY; + opts.argv = NULL; + opts.parallelism = erts_port_parallelism; -static void unblock_signals(void) -{ - /* Update erl_child_setup.c if changed */ -#if !CHLDWTHR - sys_sigrelease(SIGCHLD); +#ifdef ERTS_SMP + port = #endif -#ifndef ERTS_SMP - sys_sigrelease(SIGINT); -#ifndef ETHR_UNUSABLE_SIGUSRX - sys_sigrelease(SIGUSR1); -#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ -#endif /* #ifndef ERTS_SMP */ - -#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX) - sys_sigrelease(ERTS_SYS_SUSPEND_SIGNAL); + erts_open_driver(&forker_driver, make_internal_pid(0), "forker", &opts, NULL, NULL); +#ifdef ERTS_SMP + erts_mtx_unlock(port->lock); #endif - } -/************************** Port I/O *******************************/ +/* II. Prototypes */ +/* II.I Spawn prototypes */ +static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*); +static ErlDrvSSizeT spawn_control(ErlDrvData, unsigned int, char *, + ErlDrvSizeT, char **, ErlDrvSizeT); +/* II.II Vanilla prototypes */ +static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*); -/* I. Common stuff */ - -/* - * Decreasing the size of it below 16384 is not allowed. - */ - -/* II. The spawn/fd/vanilla drivers */ - -#define ERTS_SYS_READ_BUF_SZ (64*1024) -/* Driver interfaces */ -static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*); +/* II.III FD prototypes */ static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*); #if FDBLOCK static void fd_async(void *); @@ -275,10 +229,10 @@ static void fd_ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data); #endif static ErlDrvSSizeT fd_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT, char **, ErlDrvSizeT); -static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*); -static int spawn_init(void); static void fd_stop(ErlDrvData); static void fd_flush(ErlDrvData); + +/* II.IV Common prototypes */ static void stop(ErlDrvData); static void ready_input(ErlDrvData, ErlDrvEvent); static void ready_output(ErlDrvData, ErlDrvEvent); @@ -286,8 +240,22 @@ static void output(ErlDrvData, char*, ErlDrvSizeT); static void outputv(ErlDrvData, ErlIOVec*); static void stop_select(ErlDrvEvent, void*); +/* II.V Forker prototypes */ +static int forker_init(void); +static ErlDrvData forker_start(ErlDrvPort, char*, SysDriverOpts*); +static void forker_stop(ErlDrvData); +static void forker_ready_input(ErlDrvData, ErlDrvEvent); +static void forker_ready_output(ErlDrvData, ErlDrvEvent); +static ErlDrvSSizeT forker_control(ErlDrvData, unsigned int, char *, + ErlDrvSizeT, char **, ErlDrvSizeT); +static void forker_add_os_pid_mapping(ErtsSysDriverData *); +static void forker_remove_os_pid_mapping(ErtsSysDriverData *); + +/* III Driver entries */ + +/* III.I The spawn driver */ struct erl_drv_entry spawn_driver_entry = { - spawn_init, + forker_init, spawn_start, stop, output, @@ -296,7 +264,7 @@ struct erl_drv_entry spawn_driver_entry = { "spawn", NULL, NULL, - NULL, + spawn_control, NULL, NULL, NULL, @@ -306,17 +274,19 @@ struct erl_drv_entry spawn_driver_entry = { ERL_DRV_EXTENDED_MARKER, ERL_DRV_EXTENDED_MAJOR_VERSION, ERL_DRV_EXTENDED_MINOR_VERSION, - ERL_DRV_FLAG_USE_PORT_LOCKING, + ERL_DRV_FLAG_USE_PORT_LOCKING | ERL_DRV_FLAG_USE_INIT_ACK, NULL, NULL, stop_select }; + +/* III.II The fd driver */ struct erl_drv_entry fd_driver_entry = { NULL, fd_start, fd_stop, output, ready_input, - ready_output, + ready_output, "fd", NULL, NULL, @@ -339,6 +309,8 @@ struct erl_drv_entry fd_driver_entry = { NULL, /* process_exit */ stop_select }; + +/* III.III The vanilla driver */ struct erl_drv_entry vanilla_driver_entry = { NULL, vanilla_start, @@ -365,22 +337,33 @@ struct erl_drv_entry vanilla_driver_entry = { stop_select }; -/* Handle SIGCHLD signals. */ -#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL)) -static RETSIGTYPE onchld(void) -#else -static RETSIGTYPE onchld(int signum) -#endif -{ -#if CHLDWTHR - ASSERT(0); /* We should *never* catch a SIGCHLD signal */ -#elif defined(ERTS_SMP) - smp_sig_notify('C'); -#else - children_died = 1; - ERTS_CHK_IO_AS_INTR(); /* Make sure we don't sleep in poll */ -#endif -} +/* III.III The forker driver */ +struct erl_drv_entry forker_driver_entry = { + NULL, + forker_start, + forker_stop, + NULL, + forker_ready_input, + forker_ready_output, + "spawn_forker", + NULL, + NULL, + forker_control, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, + NULL, NULL, + stop_select +}; + +/* Untility functions */ static int set_blocking_data(ErtsSysDriverData *dd) { @@ -396,187 +379,94 @@ static int set_blocking_data(ErtsSysDriverData *dd) { return 1; } -static int set_driver_data(ErlDrvPort port_num, - int ifd, - int ofd, - int packet_bytes, - int read_write, - int exit_status, - int pid, - int is_blocking) +static void init_fd_data(ErtsSysFdData *fd_data, int fd) { - Port *prt; - ErtsSysReportExit *report_exit; - - if (!exit_status) - report_exit = NULL; - else { - report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT, - sizeof(ErtsSysReportExit)); - report_exit->next = report_exit_list; - report_exit->port = erts_drvport2id(port_num); - report_exit->pid = pid; - report_exit->ifd = read_write & DO_READ ? ifd : -1; - report_exit->ofd = read_write & DO_WRITE ? ofd : -1; -#if CHLDWTHR && !defined(ERTS_SMP) - report_exit->status = 0; -#endif - report_exit_list = report_exit; - } - - prt = erts_drvport2port(port_num); - if (prt != ERTS_INVALID_ERL_DRV_PORT) - prt->os_pid = pid; - - if (read_write & DO_READ) { - driver_data[ifd].packet_bytes = packet_bytes; - driver_data[ifd].port_num = port_num; - driver_data[ifd].report_exit = report_exit; - driver_data[ifd].pid = pid; - driver_data[ifd].alive = 1; - driver_data[ifd].status = 0; - driver_data[ifd].terminating = 0; - driver_data[ifd].blocking = NULL; - if (read_write & DO_WRITE) { - driver_data[ifd].ofd = ofd; - if (is_blocking && FDBLOCK) - if (!set_blocking_data(driver_data+ifd)) - return -1; - if (ifd != ofd) - driver_data[ofd] = driver_data[ifd]; /* structure copy */ - } else { /* DO_READ only */ - driver_data[ifd].ofd = -1; - } - (void) driver_select(port_num, ifd, (ERL_DRV_READ|ERL_DRV_USE), 1); - return(ifd); - } else { /* DO_WRITE only */ - driver_data[ofd].packet_bytes = packet_bytes; - driver_data[ofd].port_num = port_num; - driver_data[ofd].report_exit = report_exit; - driver_data[ofd].ofd = ofd; - driver_data[ofd].pid = pid; - driver_data[ofd].alive = 1; - driver_data[ofd].status = 0; - driver_data[ofd].terminating = 0; - driver_data[ofd].blocking = NULL; - if (is_blocking && FDBLOCK) - if (!set_blocking_data(driver_data+ofd)) - return -1; - return(ofd); - } + fd_data->fd = fd; + fd_data->buf = NULL; + fd_data->cpos = NULL; + fd_data->remain = 0; + fd_data->sz = 0; + fd_data->psz = 0; } -static int spawn_init() +static ErtsSysDriverData * +create_driver_data(ErlDrvPort port_num, + int ifd, + int ofd, + int packet_bytes, + int read_write, + int exit_status, + int pid, + int is_blocking) { - int i; -#if CHLDWTHR - erts_thr_opts_t thr_opts = ERTS_THR_OPTS_DEFAULT_INITER; -#endif - int res; - char bindir[MAXPATHLEN]; - size_t bindirsz = sizeof(bindir); - Uint csp_path_sz; - -#if CHLDWTHR - thr_opts.detached = 0; - thr_opts.suggested_stack_size = 0; /* Smallest possible */ - thr_opts.name = "child_waiter"; -#endif - -#if !DISABLE_VFORK - res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz); - if (res != 0) { - if (res < 0) - erl_exit(-1, - "Environment variable BINDIR is not set\n"); - if (res > 0) - erl_exit(-1, - "Value of environment variable BINDIR is too large\n"); - } - if (bindir[0] != DIR_SEPARATOR_CHAR) - erl_exit(-1, - "Environment variable BINDIR does not contain an" - " absolute path\n"); - csp_path_sz = (strlen(bindir) - + 1 /* DIR_SEPARATOR_CHAR */ - + sizeof(CHILD_SETUP_PROG_NAME) - + 1); - child_setup_prog = erts_alloc(ERTS_ALC_T_CS_PROG_PATH, csp_path_sz); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, csp_path_sz); - erts_snprintf(child_setup_prog, csp_path_sz, - "%s%c%s", - bindir, - DIR_SEPARATOR_CHAR, - CHILD_SETUP_PROG_NAME); -#endif - - report_exit_list = NULL; - -#ifdef USE_THREADS - -#if CHLDWTHR || defined(ERTS_SMP) - erts_mtx_init(&chld_stat_mtx, "child_status"); -#endif -#if CHLDWTHR -#ifndef ERTS_SMP - report_exit_transit_list = NULL; -#endif - erts_cnd_init(&chld_stat_cnd); - children_alive = 0; -#endif - -#if !CHLDWTHR && !defined(ERTS_SMP) - children_died = 0; -#endif - -#endif /* USE_THREADS */ - - sys_signal(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */ - driver_data = (ErtsSysDriverData *) - erts_alloc(ERTS_ALC_T_DRV_TAB, sys_max_files() * sizeof(ErtsSysDriverData)); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, - sys_max_files() * sizeof(ErtsSysDriverData)); + Port *prt; + ErtsSysDriverData *driver_data; + char *data; + int size = sizeof(ErtsSysDriverData); - for (i = 0; i < sys_max_files(); i++) - driver_data[i].pid = -1; + if (read_write & DO_READ) + size += sizeof(ErtsSysFdData); - fd_data = (ErtsSysFdData *) - erts_alloc(ERTS_ALC_T_FD_TAB, sys_max_files() * sizeof(ErtsSysFdData)); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, - sys_max_files() * sizeof(ErtsSysFdData)); + if ((read_write & DO_WRITE) && + ((ifd != ofd || ofd == -1) || !(read_write & DO_READ))) + size += sizeof(ErtsSysFdData); -#if CHLDWTHR - sys_sigblock(SIGCHLD); -#endif + data = erts_alloc(ERTS_ALC_T_DRV_TAB,size); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, size); - sys_signal(SIGCHLD, onchld); /* Reap children */ + driver_data = (ErtsSysDriverData*)data; + data += sizeof(*driver_data); -#if CHLDWTHR - erts_thr_create(&child_waiter_tid, child_waiter, NULL, &thr_opts); -#endif + prt = erts_drvport2port(port_num); + if (prt != ERTS_INVALID_ERL_DRV_PORT) + prt->os_pid = pid; - return 1; -} + driver_data->packet_bytes = packet_bytes; + driver_data->port_num = port_num; + driver_data->pid = pid; + driver_data->alive = exit_status ? 1 : 0; + driver_data->status = 0; + driver_data->terminating = 0; + driver_data->blocking = NULL; -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]); + driver_data->ifd = (ErtsSysFdData*)data; + data += sizeof(*driver_data->ifd); + init_fd_data(driver_data->ifd, ifd); + driver_select(port_num, ifd, (ERL_DRV_READ|ERL_DRV_USE), 1); + } else { + driver_data->ifd = NULL; } + if (read_write & DO_WRITE) { - (void) close(ofd[0]); - (void) close(ofd[1]); + if (ofd != -1 && ifd == ofd && read_write & DO_READ) { + /* This is for when ifd and ofd are the same fd */ + driver_data->ofd = driver_data->ifd; + } else { + driver_data->ofd = (ErtsSysFdData*)data; + data += sizeof(*driver_data->ofd); + init_fd_data(driver_data->ofd, ofd); + } + if (is_blocking && FDBLOCK) + if (!set_blocking_data(driver_data)) { + erts_free(ERTS_ALC_T_DRV_TAB, driver_data); + return NULL; + } + } else { + driver_data->ofd = NULL; } + + return driver_data; } -static void init_fd_data(int fd, ErlDrvPort port_num) +/* Spawn driver */ + +static void close_pipes(int ifd[2], int ofd[2]) { - fd_data[fd].buf = NULL; - fd_data[fd].cpos = NULL; - fd_data[fd].remain = 0; - fd_data[fd].sz = 0; - fd_data[fd].psz = 0; + close(ifd[0]); + close(ifd[1]); + close(ofd[0]); + close(ofd[1]); } static char **build_unix_environment(char *block) @@ -622,6 +512,10 @@ static char **build_unix_environment(char *block) for (j = 0; j < len; j++) { char *s, *t; + /* check if cpp[j] equals old + before the = sign, + i.e. + "TMPDIR=/tmp/" */ s = cpp[j]; t = old; while (*s == *t && *s != '=') { @@ -656,81 +550,43 @@ static char **build_unix_environment(char *block) return cpp; } -/* - [arndt] In most Unix systems, including Solaris 2.5, 'fork' allocates memory - in swap space for the child of a 'fork', whereas 'vfork' does not do this. - The natural call to use here is therefore 'vfork'. Due to a bug in - 'vfork' in Solaris 2.5 (apparently fixed in 2.6), using 'vfork' - can be dangerous in what seems to be these circumstances: - If the child code under a vfork sets the signal action to SIG_DFL - (or SIG_IGN) - for any signal which was previously set to a signal handler, the - state of the parent is clobbered, so that the later arrival of - such a signal yields a sigsegv in the parent. If the signal was - not set to a signal handler, but ignored, all seems to work. - If you change the forking code below, beware of this. - */ - -static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) +static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, + SysDriverOpts* opts) { #define CMD_LINE_PREFIX_STR "exec " #define CMD_LINE_PREFIX_STR_SZ (sizeof(CMD_LINE_PREFIX_STR) - 1) - int ifd[2], ofd[2], len, pid, i; - char **volatile new_environ; /* volatile since a vfork() then cannot - cause 'new_environ' to be clobbered - in the parent process. */ - int saved_errno; - long res; + int len; + char **new_environ; + ErtsSysDriverData *res; char *cmd_line; -#ifndef QNX - int unbind; -#endif -#if !DISABLE_VFORK - int no_vfork; - size_t no_vfork_sz = sizeof(no_vfork); + char wd_buff[MAXPATHLEN+1]; + char *wd; + int ifd[2], ofd[2], stderrfd; + + if (pipe(ifd) < 0) return ERL_DRV_ERROR_ERRNO; + errno = EMFILE; /* default for next three conditions */ + if (ifd[0] >= sys_max_files() || pipe(ofd) < 0) { + close(ifd[0]); + close(ifd[1]); + return ERL_DRV_ERROR_ERRNO; + } + if (ofd[1] >= sys_max_files()) { + close_pipes(ifd, ofd); + errno = EMFILE; + return ERL_DRV_ERROR_ERRNO; + } - no_vfork = (erts_sys_getenv_raw("ERL_NO_VFORK", - (char *) &no_vfork, - &no_vfork_sz) >= 0); -#endif + SET_NONBLOCKING(ifd[0]); + SET_NONBLOCKING(ofd[1]); - switch (opts->read_write) { - case DO_READ: - if (pipe(ifd) < 0) - return ERL_DRV_ERROR_ERRNO; - if (ifd[0] >= sys_max_files()) { - close_pipes(ifd, ofd, opts->read_write); - errno = EMFILE; - return ERL_DRV_ERROR_ERRNO; - } - ofd[1] = -1; /* keep purify happy */ - break; - case DO_WRITE: - if (pipe(ofd) < 0) return ERL_DRV_ERROR_ERRNO; - if (ofd[1] >= sys_max_files()) { - close_pipes(ifd, ofd, opts->read_write); - errno = EMFILE; - return ERL_DRV_ERROR_ERRNO; - } - ifd[0] = -1; /* keep purify happy */ - break; - case DO_READ|DO_WRITE: - if (pipe(ifd) < 0) return ERL_DRV_ERROR_ERRNO; - errno = EMFILE; /* default for next two conditions */ - if (ifd[0] >= sys_max_files() || pipe(ofd) < 0) { - close_pipes(ifd, ofd, DO_READ); - return ERL_DRV_ERROR_ERRNO; - } - if (ofd[1] >= sys_max_files()) { - close_pipes(ifd, ofd, opts->read_write); - errno = EMFILE; - return ERL_DRV_ERROR_ERRNO; - } - break; - default: - ASSERT(0); - return ERL_DRV_ERROR_GENERAL; + stderrfd = opts->redir_stderr ? ifd[1] : dup(2); + + if (stderrfd >= sys_max_files() || stderrfd < 0) { + close_pipes(ifd, ofd); + if (stderrfd > -1) + close(stderrfd); + return ERL_DRV_ERROR_ERRNO; } if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { @@ -738,15 +594,17 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op len = strlen(name); cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP, len + 1); if (!cmd_line) { - close_pipes(ifd, ofd, opts->read_write); + close_pipes(ifd, ofd); errno = ENOMEM; return ERL_DRV_ERROR_ERRNO; } memcpy((void *) cmd_line,(void *) name, len); cmd_line[len] = '\0'; + len = len + 1; if (access(cmd_line,X_OK) != 0) { int save_errno = errno; erts_free(ERTS_ALC_T_TMP, cmd_line); + close_pipes(ifd, ofd); errno = save_errno; return ERL_DRV_ERROR_ERRNO; } @@ -756,7 +614,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP, CMD_LINE_PREFIX_STR_SZ + len + 1); if (!cmd_line) { - close_pipes(ifd, ofd, opts->read_write); + close_pipes(ifd, ofd); errno = ENOMEM; return ERL_DRV_ERROR_ERRNO; } @@ -765,6 +623,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op CMD_LINE_PREFIX_STR_SZ); memcpy((void *) (cmd_line + CMD_LINE_PREFIX_STR_SZ), (void *) name, len); cmd_line[CMD_LINE_PREFIX_STR_SZ + len] = '\0'; + len = CMD_LINE_PREFIX_STR_SZ + len + 1; } erts_smp_rwmtx_rlock(&environ_rwmtx); @@ -773,283 +632,194 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op new_environ = environ; } else if ((new_environ = build_unix_environment(opts->envir)) == NULL) { erts_smp_rwmtx_runlock(&environ_rwmtx); + close_pipes(ifd, ofd); erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); errno = ENOMEM; return ERL_DRV_ERROR_ERRNO; - } + } -#ifndef QNX - /* Block child from SIGINT and SIGUSR1. Must be before fork() - to be safe. */ - block_signals(); + if (opts->wd == NULL) { + if ((wd = getcwd(wd_buff, MAXPATHLEN+1)) == NULL) { + /* on some OSs this call opens a fd in the + background which means that this can + return EMFILE */ + int err = errno; + close_pipes(ifd, ofd); + erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); + if (new_environ != environ) + erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); + erts_smp_rwmtx_runlock(&environ_rwmtx); + errno = err; + return ERL_DRV_ERROR_ERRNO; + } + } else { + wd = opts->wd; + } - CHLD_STAT_LOCK; + { + struct iovec *io_vector; + int buffsz = 0, iov_len = 5; + char nullbuff[] = "\0"; + int j, i = 0; + Sint32 env_len = 0, argv_len = 0, + flags = (opts->use_stdio ? FORKER_FLAG_USE_STDIO : 0) + | (opts->exit_status ? FORKER_FLAG_EXIT_STATUS : 0) + | (opts->read_write & DO_READ ? FORKER_FLAG_DO_READ : 0) + | (opts->read_write & DO_WRITE ? FORKER_FLAG_DO_WRITE : 0); + + /* count number of elements in environment */ + while(new_environ[env_len] != NULL) + env_len++; + iov_len += 1 + env_len; /* num envs including size int */ + + /* count number of element in argument list */ + if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { + if (opts->argv != NULL) { + while(opts->argv[argv_len] != NULL) + argv_len++; + } else { + argv_len++; + } + iov_len += 1 + argv_len; /* num argvs including size int */ + } - unbind = erts_sched_bind_atfork_prepare(); + io_vector = erts_alloc_fnf(ERTS_ALC_T_TMP, sizeof(struct iovec) * iov_len); -#if !DISABLE_VFORK - /* See fork/vfork discussion before this function. */ - if (no_vfork) { -#endif + if (!io_vector) { + close_pipes(ifd, ofd); + erts_smp_rwmtx_runlock(&environ_rwmtx); + erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); + if (new_environ != environ) + erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); + errno = ENOMEM; + return ERL_DRV_ERROR_ERRNO; + } - DEBUGF(("Using fork\n")); - pid = fork(); - - if (pid == 0) { - /* The child! Setup child... */ - - if (erts_sched_bind_atfork_child(unbind) != 0) - goto child_error; - - /* OBSERVE! - * Keep child setup after vfork() (implemented below and in - * erl_child_setup.c) up to date if changes are made here. - */ - - if (opts->use_stdio) { - if (opts->read_write & DO_READ) { - /* stdout for process */ - if (dup2(ifd[1], 1) < 0) - goto child_error; - if(opts->redir_stderr) - /* stderr for process */ - if (dup2(ifd[1], 2) < 0) - goto child_error; - } - if (opts->read_write & DO_WRITE) - /* stdin for process */ - if (dup2(ofd[0], 0) < 0) - goto child_error; - } - else { /* XXX will fail if ofd[0] == 4 (unlikely..) */ - if (opts->read_write & DO_READ) - if (dup2(ifd[1], 4) < 0) - goto child_error; - if (opts->read_write & DO_WRITE) - if (dup2(ofd[0], 3) < 0) - goto child_error; - } + io_vector[i].iov_base = (void*)&buffsz; + io_vector[i++].iov_len = sizeof(buffsz); -#if defined(HAVE_CLOSEFROM) - closefrom(opts->use_stdio ? 3 : 5); -#else - for (i = opts->use_stdio ? 3 : 5; i < sys_max_files(); i++) - (void) close(i); -#endif + io_vector[i].iov_base = (void*)&flags; + io_vector[i++].iov_len = sizeof(flags); + buffsz += sizeof(flags); - if (opts->wd && chdir(opts->wd) < 0) - goto child_error; + io_vector[i].iov_base = cmd_line; + io_vector[i++].iov_len = len; + buffsz += len; -#if defined(USE_SETPGRP_NOARGS) /* SysV */ - (void) setpgrp(); -#elif defined(USE_SETPGRP) /* BSD */ - (void) setpgrp(0, getpid()); -#else /* POSIX */ - (void) setsid(); -#endif - - unblock_signals(); - - if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { - if (opts->argv == NULL) { - execle(cmd_line,cmd_line,(char *) NULL, new_environ); - } else { - if (opts->argv[0] == erts_default_arg0) { - opts->argv[0] = cmd_line; - } - execve(cmd_line, opts->argv, new_environ); - if (opts->argv[0] == cmd_line) { - opts->argv[0] = erts_default_arg0; - } - } - } else { - execle(SHELL, "sh", "-c", cmd_line, (char *) NULL, new_environ); - } - child_error: - _exit(1); - } -#if !DISABLE_VFORK - } -#define ENOUGH_BYTES (44) - else { /* Use vfork() */ - char **cs_argv= erts_alloc(ERTS_ALC_T_TMP,(CS_ARGV_NO_OF_ARGS + 1)* - sizeof(char *)); - char fd_close_range[ENOUGH_BYTES]; /* 44 bytes are enough to */ - char dup2_op[CS_ARGV_NO_OF_DUP2_OPS][ENOUGH_BYTES]; /* hold any "%d:%d" string */ - /* on a 64-bit machine. */ - - /* Setup argv[] for the child setup program (implemented in - erl_child_setup.c) */ - i = 0; - if (opts->use_stdio) { - if (opts->read_write & DO_READ){ - /* stdout for process */ - erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 1); - if(opts->redir_stderr) - /* stderr for process */ - erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 2); - } - if (opts->read_write & DO_WRITE) - /* stdin for process */ - erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ofd[0], 0); - } else { /* XXX will fail if ofd[0] == 4 (unlikely..) */ - if (opts->read_write & DO_READ) - erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 4); - if (opts->read_write & DO_WRITE) - erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ofd[0], 3); - } - for (; i < CS_ARGV_NO_OF_DUP2_OPS; i++) - strcpy(&dup2_op[i][0], "-"); - erts_snprintf(fd_close_range, ENOUGH_BYTES, "%d:%d", opts->use_stdio ? 3 : 5, sys_max_files()-1); - - cs_argv[CS_ARGV_PROGNAME_IX] = child_setup_prog; - cs_argv[CS_ARGV_WD_IX] = opts->wd ? opts->wd : "."; - cs_argv[CS_ARGV_UNBIND_IX] = erts_sched_bind_atvfork_child(unbind); - cs_argv[CS_ARGV_FD_CR_IX] = fd_close_range; - for (i = 0; i < CS_ARGV_NO_OF_DUP2_OPS; i++) - cs_argv[CS_ARGV_DUP2_OP_IX(i)] = &dup2_op[i][0]; - - if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { - int num = 0; - int j = 0; - if (opts->argv != NULL) { - for(; opts->argv[num] != NULL; ++num) - ; - } - cs_argv = erts_realloc(ERTS_ALC_T_TMP,cs_argv, (CS_ARGV_NO_OF_ARGS + 1 + num + 1) * sizeof(char *)); - cs_argv[CS_ARGV_CMD_IX] = "-"; - cs_argv[CS_ARGV_NO_OF_ARGS] = cmd_line; - if (opts->argv != NULL) { - for (;opts->argv[j] != NULL; ++j) { - if (opts->argv[j] == erts_default_arg0) { - cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = cmd_line; - } else { - cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = opts->argv[j]; - } - } - } - cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = NULL; - } else { - cs_argv[CS_ARGV_CMD_IX] = cmd_line; /* Command */ - cs_argv[CS_ARGV_NO_OF_ARGS] = NULL; - } - DEBUGF(("Using vfork\n")); - pid = vfork(); - - if (pid == 0) { - /* The child! */ - - /* Observe! - * OTP-4389: The child setup program (implemented in - * erl_child_setup.c) will perform the necessary setup of the - * child before it execs to the user program. This because - * vfork() only allow an *immediate* execve() or _exit() in the - * child. - */ - execve(child_setup_prog, cs_argv, new_environ); - _exit(1); - } - erts_free(ERTS_ALC_T_TMP,cs_argv); - } -#undef ENOUGH_BYTES -#endif + io_vector[i].iov_base = wd; + io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1; + buffsz += io_vector[i++].iov_len; - erts_sched_bind_atfork_parent(unbind); + io_vector[i].iov_base = nullbuff; + io_vector[i++].iov_len = 1; + buffsz += io_vector[i-1].iov_len; - if (pid == -1) { - saved_errno = errno; - CHLD_STAT_UNLOCK; - erts_smp_rwmtx_runlock(&environ_rwmtx); - erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); - unblock_signals(); - close_pipes(ifd, ofd, opts->read_write); - errno = saved_errno; - return ERL_DRV_ERROR_ERRNO; - } -#else /* QNX */ - if (opts->use_stdio) { - if (opts->read_write & DO_READ) - qnx_spawn_options.iov[1] = ifd[1]; /* stdout for process */ - if (opts->read_write & DO_WRITE) - qnx_spawn_options.iov[0] = ofd[0]; /* stdin for process */ - } - else { - if (opts->read_write & DO_READ) - qnx_spawn_options.iov[4] = ifd[1]; - if (opts->read_write & DO_WRITE) - qnx_spawn_options.iov[3] = ofd[0]; - } - /* Close fds on exec */ - for (i = 3; i < sys_max_files(); i++) - fcntl(i, F_SETFD, 1); + io_vector[i].iov_base = (void*)&env_len; + io_vector[i++].iov_len = sizeof(env_len); + buffsz += io_vector[i-1].iov_len; - qnx_spawn_options.flags = _SPAWN_SETSID; - if ((pid = spawnl(P_NOWAIT, SHELL, SHELL, "-c", cmd_line, - (char *) 0)) < 0) { - erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); - reset_qnx_spawn(); - erts_smp_rwmtx_runlock(&environ_rwmtx); - close_pipes(ifd, ofd, opts->read_write); - return ERL_DRV_ERROR_GENERAL; + for (j = 0; new_environ[j] != NULL; j++) { + io_vector[i].iov_base = new_environ[j]; + io_vector[i++].iov_len = strlen(new_environ[j]) + 1; + buffsz += io_vector[i-1].iov_len; + } + + /* only append arguments if this was a spawn_executable */ + if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { + + io_vector[i].iov_base = (void*)&argv_len; + io_vector[i++].iov_len = sizeof(argv_len); + buffsz += io_vector[i-1].iov_len; + + if (opts->argv) { + /* If there are arguments we copy in the references to + them into the iov */ + for (j = 0; opts->argv[j]; j++) { + if (opts->argv[j] == erts_default_arg0) + io_vector[i].iov_base = cmd_line; + else + io_vector[i].iov_base = opts->argv[j]; + io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1; + buffsz += io_vector[i++].iov_len; + } + } else { + io_vector[i].iov_base = cmd_line; + io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1; + buffsz += io_vector[i++].iov_len; + } + } + + /* we send the request to do the fork */ + if (writev(ofd[1], io_vector, iov_len) < 0) { + int err = errno; + close_pipes(ifd, ofd); + erts_free(ERTS_ALC_T_TMP, io_vector); + if (new_environ != environ) + erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); + erts_smp_rwmtx_runlock(&environ_rwmtx); + erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); + errno = err; + return ERL_DRV_ERROR_ERRNO; + } + + erts_free(ERTS_ALC_T_TMP, io_vector); } - reset_qnx_spawn(); -#endif /* QNX */ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); if (new_environ != environ) erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); - if (opts->read_write & DO_READ) - (void) close(ifd[1]); - if (opts->read_write & DO_WRITE) - (void) close(ofd[0]); - - if (opts->read_write & DO_READ) { - SET_NONBLOCKING(ifd[0]); - init_fd_data(ifd[0], port_num); - } - if (opts->read_write & DO_WRITE) { - SET_NONBLOCKING(ofd[1]); - init_fd_data(ofd[1], port_num); - } + erts_smp_rwmtx_runlock(&environ_rwmtx); - res = set_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes, - opts->read_write, opts->exit_status, pid, 0); - /* Don't unblock SIGCHLD until now, since the call above must - first complete putting away the info about our new subprocess. */ - unblock_signals(); + res = create_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes, + DO_WRITE | DO_READ, opts->exit_status, + 0, 0); -#if CHLDWTHR - ASSERT(children_alive >= 0); + { + /* send ofd[0] + ifd[1] + stderrfd to forker port */ + int *fds = erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA, sizeof(int)*3); + memset(fds, 0, sizeof(int)*3); + fds[0] = ofd[0]; + fds[1] = ifd[1]; + fds[2] = stderrfd; + if (erl_drv_port_control(forker_port, 'S', (char*)fds, sizeof(int)*3)) { + /* The forker port has been killed, we close both fd's which will + make open_port throw an epipe error */ + close(ofd[0]); + close(ifd[1]); + } + } - if (!(children_alive++)) - CHLD_STAT_SIGNAL; /* Wake up child waiter thread if no children - was alive before we fork()ed ... */ -#endif - /* Don't unlock chld_stat_mtx until now of the same reason as above */ - CHLD_STAT_UNLOCK; + /* we set these fds to negative to mark if + they should be closed after the handshake */ + if (!(opts->read_write & DO_READ)) + res->ifd->fd *= -1; - erts_smp_rwmtx_runlock(&environ_rwmtx); + if (!(opts->read_write & DO_WRITE)) + res->ofd->fd *= -1; return (ErlDrvData)res; #undef CMD_LINE_PREFIX_STR #undef CMD_LINE_PREFIX_STR_SZ } -#ifdef QNX -static reset_qnx_spawn() +static ErlDrvSSizeT spawn_control(ErlDrvData e, unsigned int cmd, char *buf, + ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) { - int i; + ErtsSysDriverData *dd = (ErtsSysDriverData*)e; + + memcpy(&dd->status, buf, sizeof(dd->status)); + dd->alive = -1; + + if (dd->ifd) + driver_select(dd->port_num, abs(dd->ifd->fd), ERL_DRV_READ | ERL_DRV_USE, 1); + + if (dd->ofd) + driver_select(dd->port_num, abs(dd->ofd->fd), ERL_DRV_WRITE | ERL_DRV_USE, 1); - /* Reset qnx_spawn_options */ - qnx_spawn_options.flags = 0; - qnx_spawn_options.iov[0] = 0xff; - qnx_spawn_options.iov[1] = 0xff; - qnx_spawn_options.iov[2] = 0xff; - qnx_spawn_options.iov[3] = 0xff; + return 0; } -#endif #define FD_DEF_HEIGHT 24 #define FD_DEF_WIDTH 80 @@ -1099,7 +869,6 @@ static ErlDrvSSizeT fd_control(ErlDrvData drv_data, static ErlDrvData fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) { - ErlDrvData res; int non_blocking = 0; if (((opts->read_write & DO_READ) && opts->ifd >= sys_max_files()) || @@ -1189,11 +958,8 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name, * use erlang:halt with flush=false. */ - if (opts->read_write & DO_READ) { - init_fd_data(opts->ifd, port_num); - } + /* Try to figure out if we can use non-blocking writes */ if (opts->read_write & DO_WRITE) { - init_fd_data(opts->ofd, port_num); /* If we don't have a read end, all bets are off - no non-blocking. */ if (opts->read_write & DO_READ) { @@ -1259,60 +1025,66 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name, } } } - CHLD_STAT_LOCK; - res = (ErlDrvData)(long)set_driver_data(port_num, opts->ifd, opts->ofd, - opts->packet_bytes, - opts->read_write, 0, -1, - !non_blocking); - CHLD_STAT_UNLOCK; - return res; + return (ErlDrvData)create_driver_data(port_num, opts->ifd, opts->ofd, + opts->packet_bytes, + opts->read_write, 0, -1, + !non_blocking); } -static void clear_fd_data(int fd) +static void clear_fd_data(ErtsSysFdData *fdd) { - if (fd_data[fd].sz > 0) { - erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf); - ASSERT(erts_smp_atomic_read_nob(&sys_misc_mem_sz) >= fd_data[fd].sz); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*fd_data[fd].sz); + if (fdd->sz > 0) { + erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fdd->buf); + ASSERT(erts_smp_atomic_read_nob(&sys_misc_mem_sz) >= fdd->sz); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*fdd->sz); } - fd_data[fd].buf = NULL; - fd_data[fd].sz = 0; - fd_data[fd].remain = 0; - fd_data[fd].cpos = NULL; - fd_data[fd].psz = 0; + fdd->buf = NULL; + fdd->sz = 0; + fdd->remain = 0; + fdd->cpos = NULL; + fdd->psz = 0; } -static void nbio_stop_fd(ErlDrvPort prt, int fd) +static void nbio_stop_fd(ErlDrvPort prt, ErtsSysFdData *fdd) { - driver_select(prt,fd,DO_READ|DO_WRITE,0); - clear_fd_data(fd); - SET_BLOCKING(fd); + driver_select(prt, abs(fdd->fd), DO_READ|DO_WRITE, 0); + clear_fd_data(fdd); + SET_BLOCKING(abs(fdd->fd)); + } static void fd_stop(ErlDrvData ev) /* Does not close the fds */ { - int ofd; - int fd = (int)(long)ev; - ErlDrvPort prt = driver_data[fd].port_num; - + ErtsSysDriverData* dd = (ErtsSysDriverData*)ev; + ErlDrvPort prt = dd->port_num; + int sz = sizeof(ErtsSysDriverData); + #if FDBLOCK - if (driver_data[fd].blocking) { - erts_free(ERTS_ALC_T_SYS_BLOCKING,driver_data[fd].blocking); - driver_data[fd].blocking = NULL; - erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*sizeof(ErtsSysBlocking)); + if (dd->blocking) { + erts_free(ERTS_ALC_T_SYS_BLOCKING, dd->blocking); + dd->blocking = NULL; + sz += sizeof(ErtsSysBlocking); } #endif - nbio_stop_fd(prt, fd); - ofd = driver_data[fd].ofd; - if (ofd != fd && ofd != -1) - nbio_stop_fd(prt, ofd); + if (dd->ifd) { + sz += sizeof(ErtsSysFdData); + nbio_stop_fd(prt, dd->ifd); + } + if (dd->ofd && dd->ofd != dd->ifd) { + sz += sizeof(ErtsSysFdData); + nbio_stop_fd(prt, dd->ofd); + } + + erts_free(ERTS_ALC_T_DRV_TAB, dd); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, -sz); } -static void fd_flush(ErlDrvData fd) +static void fd_flush(ErlDrvData ev) { - if (!driver_data[(int)(long)fd].terminating) - driver_data[(int)(long)fd].terminating = 1; + ErtsSysDriverData* dd = (ErtsSysDriverData*)ev; + if (!dd->terminating) + dd->terminating = 1; } static ErlDrvData vanilla_start(ErlDrvPort port_num, char* name, @@ -1331,55 +1103,45 @@ static ErlDrvData vanilla_start(ErlDrvPort port_num, char* name, return ERL_DRV_ERROR_GENERAL; } SET_NONBLOCKING(fd); - init_fd_data(fd, port_num); - CHLD_STAT_LOCK; - res = (ErlDrvData)(long)set_driver_data(port_num, fd, fd, - opts->packet_bytes, - opts->read_write, 0, -1, 0); - CHLD_STAT_UNLOCK; + res = (ErlDrvData)(long)create_driver_data(port_num, fd, fd, + opts->packet_bytes, + opts->read_write, 0, -1, 0); return res; } /* Note that driver_data[fd].ifd == fd if the port was opened for reading, */ /* otherwise (i.e. write only) driver_data[fd].ofd = fd. */ -static void stop(ErlDrvData fd) +static void stop(ErlDrvData ev) { - ErlDrvPort prt; - int ofd; - - prt = driver_data[(int)(long)fd].port_num; - nbio_stop_fd(prt, (int)(long)fd); + ErtsSysDriverData* dd = (ErtsSysDriverData*)ev; + ErlDrvPort prt = dd->port_num; - ofd = driver_data[(int)(long)fd].ofd; - if (ofd != (int)(long)fd && (int)(long)ofd != -1) - nbio_stop_fd(prt, ofd); - else - ofd = -1; + if (dd->alive == 1) + /* Stop has been called before the exit status has been reported */ + forker_remove_os_pid_mapping(dd); - CHLD_STAT_LOCK; - - /* Mark as unused. */ - driver_data[(int)(long)fd].pid = -1; - - CHLD_STAT_UNLOCK; + if (dd->ifd) { + nbio_stop_fd(prt, dd->ifd); + driver_select(prt, abs(dd->ifd->fd), ERL_DRV_USE, 0); /* close(ifd); */ + } - /* SMP note: Close has to be last thing done (open file descriptors work - as locks on driver_data[] entries) */ - driver_select(prt, (int)(long)fd, ERL_DRV_USE, 0); /* close(fd); */ - if (ofd >= 0) { - driver_select(prt, (int)(long)ofd, ERL_DRV_USE, 0); /* close(ofd); */ + if (dd->ofd && dd->ofd != dd->ifd) { + nbio_stop_fd(prt, dd->ofd); + driver_select(prt, abs(dd->ofd->fd), ERL_DRV_USE, 0); /* close(ofd); */ } + + erts_free(ERTS_ALC_T_DRV_TAB, dd); } /* used by fd_driver */ static void outputv(ErlDrvData e, ErlIOVec* ev) { - int fd = (int)(long)e; - ErlDrvPort ix = driver_data[fd].port_num; - int pb = driver_data[fd].packet_bytes; - int ofd = driver_data[fd].ofd; + ErtsSysDriverData *dd = (ErtsSysDriverData*)e; + ErlDrvPort ix = dd->port_num; + int pb = dd->packet_bytes; + int ofd = dd->ofd ? dd->ofd->fd : -1; ssize_t n; ErlDrvSizeT sz; char lb[4]; @@ -1400,19 +1162,19 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) ev->iov[0].iov_len = pb; ev->size += pb; - if (driver_data[fd].blocking && FDBLOCK) - driver_pdl_lock(driver_data[fd].blocking->pdl); + if (dd->blocking && FDBLOCK) + driver_pdl_lock(dd->blocking->pdl); if ((sz = driver_sizeq(ix)) > 0) { driver_enqv(ix, ev, 0); - if (driver_data[fd].blocking && FDBLOCK) - driver_pdl_unlock(driver_data[fd].blocking->pdl); + if (dd->blocking && FDBLOCK) + driver_pdl_unlock(dd->blocking->pdl); if (sz + ev->size >= (1 << 13)) set_busy_port(ix, 1); } - else if (!driver_data[fd].blocking || !FDBLOCK) { + else if (!dd->blocking || !FDBLOCK) { /* We try to write directly if the fd in non-blocking */ int vsize = ev->vsize > MAX_VSIZE ? MAX_VSIZE : ev->vsize; @@ -1433,11 +1195,11 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) else { if (ev->size != 0) { driver_enqv(ix, ev, 0); - driver_pdl_unlock(driver_data[fd].blocking->pdl); - driver_async(ix, &driver_data[fd].blocking->pkey, - fd_async, driver_data+fd, NULL); + driver_pdl_unlock(dd->blocking->pdl); + driver_async(ix, &dd->blocking->pkey, + fd_async, dd, NULL); } else { - driver_pdl_unlock(driver_data[fd].blocking->pdl); + driver_pdl_unlock(dd->blocking->pdl); } } #endif @@ -1447,10 +1209,10 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) /* Used by spawn_driver and vanilla driver */ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) { - int fd = (int)(long)e; - ErlDrvPort ix = driver_data[fd].port_num; - int pb = driver_data[fd].packet_bytes; - int ofd = driver_data[fd].ofd; + ErtsSysDriverData *dd = (ErtsSysDriverData*)e; + ErlDrvPort ix = dd->port_num; + int pb = dd->packet_bytes; + int ofd = dd->ofd ? dd->ofd->fd : -1; ssize_t n; ErlDrvSizeT sz; char lb[4]; @@ -1458,7 +1220,9 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) struct iovec iv[2]; /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ - if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) { + if (((pb == 2) && (len > 0xffff)) + || (pb == 1 && len > 0xff) + || dd->pid == 0 /* Attempt at output before port is ready */) { driver_failure_posix(ix, EINVAL); return; /* -1; */ } @@ -1499,62 +1263,58 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) return; /* 0; */ } -static int port_inp_failure(ErlDrvPort port_num, int ready_fd, int res) +static int port_inp_failure(ErtsSysDriverData *dd, int res) /* Result: 0 (eof) or -1 (error) */ { int err = errno; ASSERT(res <= 0); - (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0); - clear_fd_data(ready_fd); + if (dd->ifd) { + driver_select(dd->port_num, dd->ifd->fd, ERL_DRV_READ|ERL_DRV_WRITE, 0); + clear_fd_data(dd->ifd); + } - if (driver_data[ready_fd].blocking && FDBLOCK) { - driver_pdl_lock(driver_data[ready_fd].blocking->pdl); - if (driver_sizeq(driver_data[ready_fd].port_num) > 0) { - driver_pdl_unlock(driver_data[ready_fd].blocking->pdl); + if (dd->blocking && FDBLOCK) { + driver_pdl_lock(dd->blocking->pdl); + if (driver_sizeq(dd->port_num) > 0) { + driver_pdl_unlock(dd->blocking->pdl); /* We have stuff in the output queue, so we just set the state to terminating and wait for fd_async_ready to terminate the port */ if (res == 0) - driver_data[ready_fd].terminating = 2; + dd->terminating = 2; else - driver_data[ready_fd].terminating = -err; + dd->terminating = -err; return 0; } - driver_pdl_unlock(driver_data[ready_fd].blocking->pdl); + driver_pdl_unlock(dd->blocking->pdl); } if (res == 0) { - if (driver_data[ready_fd].report_exit) { - CHLD_STAT_LOCK; - - if (driver_data[ready_fd].alive) { - /* - * We have eof and want to report exit status, but the process - * hasn't exited yet. When it does report_exit_status() will - * driver_select() this fd which will make sure that we get - * back here with driver_data[ready_fd].alive == 0 and - * driver_data[ready_fd].status set. - */ - CHLD_STAT_UNLOCK; - return 0; - } - else { - int status = driver_data[ready_fd].status; - CHLD_STAT_UNLOCK; - - /* We need not be prepared for stopped/continued processes. */ - if (WIFSIGNALED(status)) - status = 128 + WTERMSIG(status); - else - status = WEXITSTATUS(status); + if (dd->alive == 1) { + /* + * We have eof and want to report exit status, but the process + * hasn't exited yet. When it does ready_input will + * driver_select() this fd which will make sure that we get + * back here with dd->alive == -1 and dd->status set. + */ + return 0; + } + else if (dd->alive == -1) { + int status = dd->status; - driver_report_exit(driver_data[ready_fd].port_num, status); - } - } - driver_failure_eof(port_num); + /* We need not be prepared for stopped/continued processes. */ + if (WIFSIGNALED(status)) + status = 128 + WTERMSIG(status); + else + status = WEXITSTATUS(status); + driver_report_exit(dd->port_num, status); + } + driver_failure_eof(dd->port_num); + } else if (dd->ifd) { + erl_drv_init_ack(dd->port_num, ERL_DRV_ERROR_ERRNO); } else { - driver_failure_posix(port_num, err); + driver_failure_posix(dd->port_num, err); } return 0; } @@ -1565,15 +1325,70 @@ static int port_inp_failure(ErlDrvPort port_num, int ready_fd, int res) static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) { - int fd = (int)(long)e; + ErtsSysDriverData *dd = (ErtsSysDriverData*)e; ErlDrvPort port_num; int packet_bytes; int res; Uint h; - port_num = driver_data[fd].port_num; - packet_bytes = driver_data[fd].packet_bytes; + port_num = dd->port_num; + packet_bytes = dd->packet_bytes; + + ASSERT(abs(dd->ifd->fd) == ready_fd); + + if (dd->pid == 0) { + /* the pid is sent from erl_child_setup. spawn driver only. */ + char message_buffer[3 + 10 + 1 + 10 + 1]; + int reason, res; + + if((res = read(ready_fd, message_buffer, sizeof(message_buffer))) <= 0) { + /* hmm, child setup seems to have closed the pipe too early... + we close the port as there is not much else we can do */ + if (res < 0 && errno == ERRNO_BLOCK) + return; + driver_select(port_num, ready_fd, ERL_DRV_READ, 0); + if (res == 0) + errno = EPIPE; + port_inp_failure(dd, -1); + return; + } + + if(sscanf(message_buffer,"GO:%010d:%010d", &dd->pid, &reason) == 2) { + if (dd->pid == -1) { + /* Setup failed! The only reason why this should happen is if + the fork fails. */ + errno = reason; + port_inp_failure(dd, -1); + return; + } + + if (write(abs(dd->ofd->fd), "A", 1) < 0) + ; /* do nothing on failure here. If the ofd is broken, then + the ifd will probably also be broken and trigger + a port_inp_failure */ + + if (dd->ifd->fd < 0) { + driver_select(port_num, abs(dd->ifd->fd), ERL_DRV_READ|ERL_DRV_USE, 0); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, -sizeof(ErtsSysFdData)); + dd->ifd = NULL; + } + + if (dd->ofd->fd < 0 || driver_sizeq(port_num) > 0) + /* we select in order to close fd or write to queue, + child setup will close this fd if fd < 0 */ + driver_select(port_num, abs(dd->ofd->fd), ERL_DRV_WRITE|ERL_DRV_USE, 1); + + if (dd->alive == 1) { + forker_add_os_pid_mapping(dd); + } + erl_drv_set_os_pid(port_num, dd->pid); + erl_drv_init_ack(port_num, e); + return; + } + ASSERT(0); + return; + } if (packet_bytes == 0) { byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, @@ -1581,76 +1396,76 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ); if (res < 0) { if ((errno != EINTR) && (errno != ERRNO_BLOCK)) - port_inp_failure(port_num, ready_fd, res); + port_inp_failure(dd, res); } else if (res == 0) - port_inp_failure(port_num, ready_fd, res); - else + port_inp_failure(dd, res); + else driver_output(port_num, (char*) read_buf, res); erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf); } - else if (fd_data[ready_fd].remain > 0) { /* We try to read the remainder */ + else if (dd->ifd->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); + res = read(ready_fd, dd->ifd->cpos, + dd->ifd->remain); if (res < 0) { if ((errno != EINTR) && (errno != ERRNO_BLOCK)) - port_inp_failure(port_num, ready_fd, res); + port_inp_failure(dd, res); } else if (res == 0) { - port_inp_failure(port_num, ready_fd, res); + port_inp_failure(dd, 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 == dd->ifd->remain) { /* we're done */ + driver_output(port_num, dd->ifd->buf, + dd->ifd->sz); + clear_fd_data(dd->ifd); } - else { /* if (res < fd_data[ready_fd].remain) */ - fd_data[ready_fd].cpos += res; - fd_data[ready_fd].remain -= res; + else { /* if (res < dd->ifd->remain) */ + dd->ifd->cpos += res; + dd->ifd->remain -= res; } } - else if (fd_data[ready_fd].remain == 0) { /* clean fd */ + else if (dd->ifd->remain == 0) { /* clean fd */ byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF, ERTS_SYS_READ_BUF_SZ); /* We make one read attempt and see what happens */ res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ); - if (res < 0) { + if (res < 0) { if ((errno != EINTR) && (errno != ERRNO_BLOCK)) - port_inp_failure(port_num, ready_fd, res); + port_inp_failure(dd, res); } else if (res == 0) { /* eof */ - port_inp_failure(port_num, ready_fd, res); - } - else if (res < packet_bytes - fd_data[ready_fd].psz) { - memcpy(fd_data[ready_fd].pbuf+fd_data[ready_fd].psz, + port_inp_failure(dd, res); + } + else if (res < packet_bytes - dd->ifd->psz) { + memcpy(dd->ifd->pbuf+dd->ifd->psz, read_buf, res); - fd_data[ready_fd].psz += res; + dd->ifd->psz += res; } else { /* if (res >= packet_bytes) */ unsigned char* cpos = read_buf; int bytes_left = res; while (1) { - int psz = fd_data[ready_fd].psz; - char* pbp = fd_data[ready_fd].pbuf + psz; + int psz = dd->ifd->psz; + char* pbp = dd->ifd->pbuf + psz; while(bytes_left && (psz < packet_bytes)) { *pbp++ = *cpos++; bytes_left--; psz++; } - + if (psz < packet_bytes) { - fd_data[ready_fd].psz = psz; + dd->ifd->psz = psz; break; } - fd_data[ready_fd].psz = 0; + dd->ifd->psz = 0; switch (packet_bytes) { - case 1: h = get_int8(fd_data[ready_fd].pbuf); break; - case 2: h = get_int16(fd_data[ready_fd].pbuf); break; - case 4: h = get_int32(fd_data[ready_fd].pbuf); break; + case 1: h = get_int8(dd->ifd->pbuf); break; + case 2: h = get_int16(dd->ifd->pbuf); break; + case 4: h = get_int32(dd->ifd->pbuf); break; default: ASSERT(0); return; /* -1; */ } @@ -1664,15 +1479,15 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); if (!buf) { errno = ENOMEM; - port_inp_failure(port_num, ready_fd, -1); + port_inp_failure(dd, -1); } else { erts_smp_atomic_add_nob(&sys_misc_mem_sz, h); sys_memcpy(buf, cpos, bytes_left); - fd_data[ready_fd].buf = buf; - fd_data[ready_fd].sz = h; - fd_data[ready_fd].remain = h - bytes_left; - fd_data[ready_fd].cpos = buf + bytes_left; + dd->ifd->buf = buf; + dd->ifd->sz = h; + dd->ifd->remain = h - bytes_left; + dd->ifd->cpos = buf + bytes_left; } break; } @@ -1689,17 +1504,24 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) { - int fd = (int)(long)e; - ErlDrvPort ix = driver_data[fd].port_num; + ErtsSysDriverData *dd = (ErtsSysDriverData*)e; + ErlDrvPort ix = dd->port_num; int n; struct iovec* iv; int vsize; - if ((iv = (struct iovec*) driver_peekq(ix, &vsize)) == NULL) { driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); - if (driver_data[fd].terminating) - driver_failure_atom(driver_data[fd].port_num,"normal"); + if (dd->pid > 0 && dd->ofd->fd < 0) { + /* The port was opened with 'in' option, which means we + should close the output fd as soon as the command has + been sent. */ + driver_select(ix, ready_fd, ERL_DRV_WRITE|ERL_DRV_USE, 0); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, -sizeof(ErtsSysFdData)); + dd->ofd = NULL; + } + if (dd->terminating) + driver_failure_atom(dd->port_num,"normal"); return; /* 0; */ } vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize; @@ -1731,7 +1553,7 @@ static void fd_async(void *async_data) { int res; - ErtsSysDriverData *dd = (ErtsSysDriverData*)async_data; + ErtsSysDriverData *dd = (ErtsSysDriverData *)async_data; SysIOVec *iov0; SysIOVec *iov; int iovlen; @@ -1751,7 +1573,7 @@ fd_async(void *async_data) driver_pdl_unlock(dd->blocking->pdl); do { - res = writev(dd->ofd, iov, iovlen); + res = writev(dd->ofd->fd, iov, iovlen); } while (res < 0 && errno == EINTR); if (res < 0) err = errno; @@ -1769,7 +1591,6 @@ void fd_ready_async(ErlDrvData drv_data, ErlDrvPort port_num = dd->port_num; ASSERT(dd->blocking); - ASSERT(dd == (driver_data + (int)(long)drv_data)); if (dd->blocking->res > 0) { driver_pdl_lock(dd->blocking->pdl); @@ -1807,177 +1628,259 @@ void fd_ready_async(ErlDrvData drv_data, #endif -static ERTS_INLINE void -report_exit_status(ErtsSysReportExit *rep, int status) +/* Forker driver */ + +static int forker_fd; +static Hash *forker_hash; +static erts_smp_mtx_t forker_hash_mtx; + +static void ffree(void *e); + +static ErlDrvData forker_start(ErlDrvPort port_num, char* name, + SysDriverOpts* opts) { - Port *pp; -#ifdef ERTS_SMP - CHLD_STAT_UNLOCK; - pp = erts_thr_id2port_sflgs(rep->port, - ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); - CHLD_STAT_LOCK; -#else - pp = erts_id2port_sflgs(rep->port, - NULL, - 0, - ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); -#endif - if (pp) { - if (rep->ifd >= 0) { - driver_data[rep->ifd].alive = 0; - driver_data[rep->ifd].status = status; - (void) driver_select(ERTS_Port2ErlDrvPort(pp), - rep->ifd, - (ERL_DRV_READ|ERL_DRV_USE), - 1); - } - if (rep->ofd >= 0) { - driver_data[rep->ofd].alive = 0; - driver_data[rep->ofd].status = status; - (void) driver_select(ERTS_Port2ErlDrvPort(pp), - rep->ofd, - (ERL_DRV_WRITE|ERL_DRV_USE), - 1); - } -#ifdef ERTS_SMP - erts_thr_port_release(pp); -#else - erts_port_release(pp); -#endif + + int i; + int fds[2]; + int res, unbind; + char bindir[MAXPATHLEN]; + size_t bindirsz = sizeof(bindir); + Uint csp_path_sz; + char *child_setup_prog; + + forker_port = erts_drvport2id(port_num); + + res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz); + if (res != 0) { + if (res < 0) + erl_exit(-1, + "Environment variable BINDIR is not set\n"); + if (res > 0) + erl_exit(-1, + "Value of environment variable BINDIR is too large\n"); + } + if (bindir[0] != DIR_SEPARATOR_CHAR) + erl_exit(-1, + "Environment variable BINDIR does not contain an" + " absolute path\n"); + csp_path_sz = (strlen(bindir) + + 1 /* DIR_SEPARATOR_CHAR */ + + sizeof(CHILD_SETUP_PROG_NAME) + + 1); + child_setup_prog = erts_alloc(ERTS_ALC_T_CS_PROG_PATH, csp_path_sz); + erts_snprintf(child_setup_prog, csp_path_sz, + "%s%c%s", + bindir, + DIR_SEPARATOR_CHAR, + CHILD_SETUP_PROG_NAME); + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + erl_exit(ERTS_ABORT_EXIT, + "Could not open unix domain socket in spawn_init: %d\n", + errno); } - erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep); -} -#if !CHLDWTHR /* ---------------------------------------------------------- */ + forker_fd = fds[0]; -#define ERTS_REPORT_EXIT_STATUS report_exit_status + unbind = erts_sched_bind_atfork_prepare(); -int check_children(void) -{ - int res = 0; - int pid; - int status; + i = fork(); -#ifndef ERTS_SMP - if (children_died) -#endif - { - sys_sigblock(SIGCHLD); - CHLD_STAT_LOCK; - while ((pid = waitpid(-1, &status, WNOHANG)) > 0) - note_child_death(pid, status); -#ifndef ERTS_SMP - children_died = 0; + if (i == 0) { + /* The child */ + char *cs_argv[FORKER_ARGV_NO_OF_ARGS] = + {CHILD_SETUP_PROG_NAME, NULL, NULL}; + char buff[128]; + + erts_sched_bind_atfork_child(unbind); + + snprintf(buff, 128, "%d", sys_max_files()); + cs_argv[FORKER_ARGV_MAX_FILES] = buff; + + /* We preallocate fd 3 for the uds fd */ + if (fds[1] != 3) { + dup2(fds[1], 3); + } + +#if defined(USE_SETPGRP_NOARGS) /* SysV */ + (void) setpgrp(); +#elif defined(USE_SETPGRP) /* BSD */ + (void) setpgrp(0, getpid()); +#else /* POSIX */ + (void) setsid(); #endif - CHLD_STAT_UNLOCK; - sys_sigrelease(SIGCHLD); - res = 1; + + execv(child_setup_prog, cs_argv); + _exit(1); } - return res; -} -#ifdef ERTS_SMP + erts_sched_bind_atfork_parent(unbind); -void -erts_check_children(void) -{ - (void) check_children(); -} + erts_free(ERTS_ALC_T_CS_PROG_PATH, child_setup_prog); -#endif + close(fds[1]); -#elif CHLDWTHR && defined(ERTS_SMP) /* ------------------------------------- */ + SET_NONBLOCKING(forker_fd); -#define ERTS_REPORT_EXIT_STATUS report_exit_status + driver_select(port_num, forker_fd, ERL_DRV_READ|ERL_DRV_USE, 1); -int check_children(void) + return (ErlDrvData)port_num; +} + +static void forker_stop(ErlDrvData e) { - return 0; + /* we probably should do something here */ } -#else /* CHLDWTHR && !defined(ERTS_SMP) ------------------------------------ */ +static void forker_ready_input(ErlDrvData e, ErlDrvEvent fd) +{ + int res, *exit_status; + char buff[8+10+1+10+1] = {0}; + ErtsSysExitStatus est, *es; + + if ((res = read(fd, buff, sizeof(buff))) < 0) { + if (errno == ERRNO_BLOCK) + return; + erl_exit(ERTS_DUMP_EXIT, "Failed to read from erl_child_setup: %d\n", errno); + } + + if (res == 0) + erl_exit(ERTS_DUMP_EXIT, "erl_child_setup closed\n"); + + if (sscanf(buff, "SIGCHLD:%010d:%010d", &est.os_pid, &res) != 2) + erl_exit(ERTS_DUMP_EXIT, "Got corrupt data from erl_child_setup: %.s\n", + buff, sizeof(buff)); + + erts_smp_mtx_lock(&forker_hash_mtx); + es = hash_remove(forker_hash, &est); + erts_smp_mtx_unlock(&forker_hash_mtx); + + if (!es) + return; -#define ERTS_REPORT_EXIT_STATUS initiate_report_exit_status + exit_status = erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA, sizeof(int)); + exit_status[0] = res; + /* ideally this would be a port_command call, but as command is + already used by the spawn_driver, we use control instead. + Note that when using erl_drv_port_control it is an asynchronous + control. */ + erl_drv_port_control(es->port_id, 'S', (char*)exit_status, sizeof(int)); -static ERTS_INLINE void -initiate_report_exit_status(ErtsSysReportExit *rep, int status) + ffree(es); + +} + +static void forker_ready_output(ErlDrvData e, ErlDrvEvent fd) { - rep->next = report_exit_transit_list; - rep->status = status; - report_exit_transit_list = rep; - erts_sys_schedule_interrupt(1); + ErlDrvPort port_num = (ErlDrvPort)e; + + while(driver_sizeq(port_num) > 0) { + int vlen; + SysIOVec *iov = driver_peekq(port_num, &vlen); + int *fds = (int*)iov[0].iov_base; + ASSERT(iov[0].iov_len >= sizeof(int)*3); + if (sys_uds_write(forker_fd, "S", 1, fds, 3, 0) < 0) { + if (errno == ERRNO_BLOCK) + return; + erl_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno); + } + close(fds[0]); + close(fds[1]); + if (fds[1] != fds[2]) + close(fds[2]); + driver_deq(port_num, sizeof(int)*3); + } + + driver_select(port_num, forker_fd, ERL_DRV_WRITE, 0); } -int check_children(void) +static ErlDrvSSizeT forker_control(ErlDrvData e, unsigned int cmd, char *buf, + ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) { + int *fds = (int*)buf; + ErlDrvPort port_num = (ErlDrvPort)e; int res; - ErtsSysReportExit *rep; - CHLD_STAT_LOCK; - rep = report_exit_transit_list; - res = rep != NULL; - while (rep) { - ErtsSysReportExit *curr_rep = rep; - rep = rep->next; - report_exit_status(curr_rep, curr_rep->status); + + if (driver_sizeq(port_num) > 0) { + driver_enq(port_num, buf, len); + return 0; } - report_exit_transit_list = NULL; - CHLD_STAT_UNLOCK; - return res; + + if ((res = sys_uds_write(forker_fd, "S", 1, fds, 3, 0)) < 0) { + if (errno == ERRNO_BLOCK) { + driver_enq(port_num, buf, len); + driver_select(port_num, forker_fd, ERL_DRV_WRITE|ERL_DRV_USE, 1); + return 0; + } + erl_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno); + } + close(fds[0]); + close(fds[1]); + if (fds[1] != fds[2]) + close(fds[2]); + return 0; } -#endif /* ------------------------------------------------------------------ */ +static void forker_add_os_pid_mapping(ErtsSysDriverData *dd) +{ + Eterm port_id = erts_drvport2id(dd->port_num); + ErtsSysExitStatus es; + es.os_pid = dd->pid; + es.port_id = port_id; + erts_smp_mtx_lock(&forker_hash_mtx); + hash_put(forker_hash, &es); + erts_smp_mtx_unlock(&forker_hash_mtx); +} -static void note_child_death(int pid, int status) +static void forker_remove_os_pid_mapping(ErtsSysDriverData *dd) { - ErtsSysReportExit **repp = &report_exit_list; - ErtsSysReportExit *rep = report_exit_list; - - while (rep) { - if (pid == rep->pid) { - *repp = rep->next; - ERTS_REPORT_EXIT_STATUS(rep, status); - break; - } - repp = &rep->next; - rep = rep->next; - } + ErtsSysExitStatus est, *es; + est.os_pid = dd->pid; + erts_smp_mtx_lock(&forker_hash_mtx); + es = hash_remove(forker_hash, &est); + erts_smp_mtx_unlock(&forker_hash_mtx); + if (es) + ffree(es); } -#if CHLDWTHR +static int fcmp(void *a, void *b) +{ + ErtsSysExitStatus *sa = a; + ErtsSysExitStatus *sb = b; + return !(sa->os_pid == sb->os_pid); +} -static void * -child_waiter(void *unused) +static HashValue fhash(void *e) { - int pid; - int status; + ErtsSysExitStatus *se = e; + return make_hash2(make_small(se->os_pid)); +} -#ifdef ERTS_ENABLE_LOCK_CHECK - erts_lc_set_thread_name("child waiter"); -#endif +static void *falloc(void *e) +{ + ErtsSysExitStatus *se = e; + ErtsSysExitStatus *ne = erts_alloc(ERTS_ALC_T_DRV, sizeof(ErtsSysExitStatus)); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, sizeof(ErtsSysBlocking)); + ne->os_pid = se->os_pid; + ne->port_id = se->port_id; + return ne; +} - while(1) { -#ifdef DEBUG - int waitpid_errno; -#endif - pid = waitpid(-1, &status, 0); -#ifdef DEBUG - waitpid_errno = errno; -#endif - CHLD_STAT_LOCK; - if (pid < 0) { - ASSERT(waitpid_errno == ECHILD); - } - else { - children_alive--; - ASSERT(children_alive >= 0); - note_child_death(pid, status); - } - while (!children_alive) - CHLD_STAT_WAIT; /* Wait for children to wait on... :) */ - CHLD_STAT_UNLOCK; - } - - return NULL; +static void ffree(void *e) +{ + erts_free(ERTS_ALC_T_DRV, e); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, -sizeof(ErtsSysBlocking)); } -#endif +static int forker_init(void) +{ + HashFunctions forker_hash_functions; + forker_hash_functions.hash = fhash; + forker_hash_functions.cmp = fcmp; + forker_hash_functions.alloc = falloc; + forker_hash_functions.free = ffree; + forker_hash = hash_new(ERTS_ALC_T_DRV, "forker_hash", + 16, forker_hash_functions); + erts_smp_mtx_init(&forker_hash_mtx, "forker_hash_mtx"); + + return 1; +} diff --git a/erts/emulator/sys/unix/sys_uds.c b/erts/emulator/sys/unix/sys_uds.c new file mode 100644 index 0000000000..3b63d05cf6 --- /dev/null +++ b/erts/emulator/sys/unix/sys_uds.c @@ -0,0 +1,125 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2002-2009. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + + +#include "sys_uds.h" + +int +sys_uds_readv(int fd, struct iovec *iov, size_t iov_len, + int *fds, int fd_count, int flags) { + struct msghdr msg; + struct cmsghdr *cmsg = NULL; + char ancillary_buff[256] = {0}; + int res, i = 0; + + /* setup a place to fill in message contents */ + memset(&msg, 0, sizeof(struct msghdr)); + msg.msg_iov = iov; + msg.msg_iovlen = iov_len; + + /* provide space for the ancillary data */ + msg.msg_control = ancillary_buff; + msg.msg_controllen = sizeof(ancillary_buff); + + if((res = recvmsg(fd, &msg, flags)) < 0) { + return res; + } + + if((msg.msg_flags & MSG_CTRUNC) == MSG_CTRUNC) + { + /* We assume that we have given enough space for any header + that are sent to us. So the only remaining reason to get + this flag set is if the caller has run out of file descriptors. + */ + errno = EMFILE; + return -1; + } + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg) ) { + if ((cmsg->cmsg_level == SOL_SOCKET) && + (cmsg->cmsg_type == SCM_RIGHTS)) { + int *cmsg_data = (int *)CMSG_DATA(cmsg); + while ((char*)cmsg_data < (char*)cmsg + cmsg->cmsg_len) { + if (i < fd_count) { + fds[i++] = *cmsg_data++; + } else { + /* for some strange reason, we have received more FD's + than we wanted... close them if we are not running + debug. */ + if(i >= fd_count) abort(); + close(*cmsg_data++); + } + } + } + } + + return res; +} + +int +sys_uds_read(int fd, char *buff, size_t len, + int *fds, int fd_count, int flags) { + struct iovec iov; + iov.iov_base = buff; + iov.iov_len = len; + return sys_uds_readv(fd, &iov, 1, fds, fd_count, flags); +} + + +int +sys_uds_writev(int fd, struct iovec *iov, size_t iov_len, + int *fds, int fd_count, int flags) { + + struct msghdr msg; + struct cmsghdr *cmsg = NULL; + int res; + + /* initialize socket message */ + memset(&msg, 0, sizeof(struct msghdr)); + msg.msg_iov = iov; + msg.msg_iovlen = iov_len; + + /* initialize the ancillary data */ + msg.msg_control = calloc(1, CMSG_SPACE(sizeof(int) * fd_count)); + msg.msg_controllen = CMSG_SPACE(sizeof(int) * fd_count); + + /* copy the fd array into the ancillary data */ + cmsg = CMSG_FIRSTHDR(&msg); + if(!cmsg) abort(); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fd_count); + memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * fd_count); + + res = sendmsg(fd, &msg, flags); + + free(msg.msg_control); + + return res; +} + +int +sys_uds_write(int fd, char *buff, size_t len, + int *fds, int fd_count, int flags) { + struct iovec iov; + iov.iov_base = buff; + iov.iov_len = len; + return sys_uds_writev(fd, &iov, 1, fds, fd_count, flags); +} diff --git a/erts/emulator/sys/unix/sys_uds.h b/erts/emulator/sys/unix/sys_uds.h new file mode 100644 index 0000000000..7ff58b17dd --- /dev/null +++ b/erts/emulator/sys/unix/sys_uds.h @@ -0,0 +1,47 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2002-2009. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef _ERL_UNIX_UDS_H +#define _ERL_UNIX_UDS_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if defined(__sun__) && !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE 500 +#endif + +#include +#include +#include + +#include "sys.h" + +int sys_uds_readv(int fd, struct iovec *iov, size_t iov_len, + int *fds, int fd_count, int flags); +int sys_uds_read(int fd, char *buff, size_t len, + int *fds, int fd_count, int flags); +int sys_uds_writev(int fd, struct iovec *iov, size_t iov_len, + int *fds, int fd_count, int flags); +int sys_uds_write(int fd, char *buff, size_t len, + int *fds, int fd_count, int flags); + +#endif /* #ifndef _ERL_UNIX_UDS_H */ diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index fce76db28f..3793357848 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -1334,10 +1334,8 @@ spawn_start(ErlDrvPort port_num, char* utf8_name, SysDriverOpts* opts) retval = set_driver_data(dp, hFromChild, hToChild, opts->read_write, opts->exit_status); if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) { - Port *prt = erts_drvport2port(port_num); - /* We assume that this cannot generate a negative number */ - ASSERT(prt != ERTS_INVALID_ERL_DRV_PORT); - prt->os_pid = (SWord) pid; + /* We assume that this cannot generate a negative number */ + erl_drv_set_os_pid(port_num, pid); } } @@ -3272,6 +3270,12 @@ void erl_sys_init(void) SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); } +void +erl_sys_late_init(void) +{ + /* do nothing */ +} + void erts_sys_schedule_interrupt(int set) { diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 3d0509a28c..f2a7ddef2c 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -385,27 +385,33 @@ input_only(Config) when is_list(Config) -> output_only(Config) when is_list(Config) -> Dog = test_server:timetrap(test_server:seconds(100)), Dir = ?config(priv_dir, Config), + + %% First we test that the port program gets the data Filename = filename:join(Dir, "output_only_stream"), - output_and_verify(Config, Filename, "-h0", - random_packet(35777, "echo")), + Data = random_packet(35777, "echo"), + output_and_verify(Config, ["-h0 -o", Filename], Data), + Wait_time = 500, + test_server:sleep(Wait_time), + {ok, Written} = file:read_file(Filename), + Data = binary_to_list(Written), + + %% Then we test that any writes to stdout from + %% the port program is not sent to erlang + output_and_verify(Config, ["-h0"], Data), + test_server:timetrap_cancel(Dog), ok. -output_and_verify(Config, Filename, Options, Data) -> +output_and_verify(Config, Options, Data) -> PortTest = port_test(Config), - Command = lists:concat([PortTest, " ", - Options, " -o", Filename]), + Command = lists:concat([PortTest, " " | Options]), Port = open_port({spawn, Command}, [out]), Port ! {self(), {command, Data}}, Port ! {self(), close}, receive - {Port, closed} -> ok - end, - Wait_time = 500, - test_server:sleep(Wait_time), - {ok, Written} = file:read_file(Filename), - Data = binary_to_list(Written), - ok. + {Port, closed} -> ok; + Msg -> ct:fail({received_unexpected_message, Msg}) + end. %% Test that receiving several packages written in the same %% write operation works. -- cgit v1.2.3 From a8276a38ded70b2854ae0fc2941ba21cc06a9130 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 15 Jul 2015 11:02:36 +0200 Subject: erts: Add fd count test for spawn_driver --- erts/emulator/test/port_SUITE.erl | 35 +++++++++++++++++++++++++- erts/emulator/test/port_SUITE_data/port_test.c | 35 ++++++++++++++++++++------ 2 files changed, 61 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index f2a7ddef2c..b67fd1c409 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -82,6 +82,7 @@ mul_basic/1, mul_slow_writes/1, dying_port/1, port_program_with_path/1, open_input_file_port/1, open_output_file_port/1, + count_fds/1, iter_max_ports/1, eof/1, input_only/1, output_only/1, name1/1, t_binary/1, parallell/1, t_exit/1, @@ -112,7 +113,7 @@ all() -> {group, multiple_packets}, parallell, dying_port, port_program_with_path, open_input_file_port, open_output_file_port, name1, env, bad_env, cd, - exit_status, iter_max_ports, t_exit, {group, tps}, line, + exit_status, iter_max_ports, count_fds, t_exit, {group, tps}, line, stderr_to_stdout, otp_3906, otp_4389, win_massive, mix_up_ports, otp_5112, otp_5119, exit_status_multi_scheduling_block, ports, spawn_driver, @@ -616,6 +617,38 @@ open_output_file_port(Config) when is_list(Config) -> test_server:timetrap_cancel(Dog), ok. +%% Tests that all appropriate fd's have been closed in the port program +count_fds(suite) -> []; +count_fds(Config) when is_list(Config) -> + case os:type() of + {unix, _} -> + PrivDir = proplists:get_value(priv_dir, Config), + Filename = filename:join(PrivDir, "my_fd_counter"), + + RunTest = fun(PortOpts) -> + PortTest = port_test(Config), + Command = lists:concat([PortTest, " -n -f -o", Filename]), + Port = open_port({spawn, Command}, PortOpts), + Port ! {self(), close}, + receive + {Port, closed} -> ok + end, + test_server:sleep(500), + {ok, Written} = file:read_file(Filename), + Written + end, + <<4:32/native>> = RunTest([out, nouse_stdio]), + <<4:32/native>> = RunTest([in, nouse_stdio]), + <<5:32/native>> = RunTest([in, out, nouse_stdio]), + <<3:32/native>> = RunTest([out, use_stdio]), + <<3:32/native>> = RunTest([in, use_stdio]), + <<3:32/native>> = RunTest([in, out, use_stdio]), + <<3:32/native>> = RunTest([in, out, use_stdio, stderr_to_stdout]), + <<3:32/native>> = RunTest([out, use_stdio, stderr_to_stdout]); + _ -> + {skip, "Skipped on windows"} + end. + %% %% Open as many ports as possible. Do this several times and check %% that we get the same number of ports every time. diff --git a/erts/emulator/test/port_SUITE_data/port_test.c b/erts/emulator/test/port_SUITE_data/port_test.c index 7abefab2e3..cc3ebdf0f8 100644 --- a/erts/emulator/test/port_SUITE_data/port_test.c +++ b/erts/emulator/test/port_SUITE_data/port_test.c @@ -13,6 +13,7 @@ #ifndef __WIN32__ #include +#include #include @@ -48,6 +49,7 @@ typedef struct { * after reading the header for a packet * before reading the rest. */ + int fd_count; /* Count the number of open fds */ int break_mode; /* If set, this program will close standard * input, which should case broken pipe * error in the writer. @@ -107,7 +109,7 @@ MAIN(argc, argv) int argc; char *argv[]; { - int ret; + int ret, fd_count; if((port_data = (PORT_TEST_DATA *) malloc(sizeof(PORT_TEST_DATA))) == NULL) { fprintf(stderr, "Couldn't malloc for port_data"); exit(1); @@ -115,6 +117,7 @@ char *argv[]; port_data->header_size = 0; port_data->io_buf_size = 0; port_data->delay_mode = 0; + port_data->fd_count = 0; port_data->break_mode = 0; port_data->quit_mode = 0; port_data->slow_writes = 0; @@ -144,6 +147,9 @@ char *argv[]; case 'e': port_data->fd_to_erl = 2; break; + case 'f': + port_data->fd_count = 1; + break; case 'h': /* Header size for packets. */ switch (argv[1][2]) { case '0': port_data->header_size = 0; break; @@ -189,18 +195,31 @@ char *argv[]; /* XXX Add error printout here */ } + if (port_data->fd_count) { +#ifdef __WIN32__ + DWORD handles; + GetProcessHandleCount(GetCurrentProcess(), &handles); + fd_count = handles; +#else + int i; + for (i = 0, fd_count = 0; i < 1024; i++) + if (fcntl(i, F_GETFD) >= 0) { + fd_count++; + } +#endif + } + + if (port_data->output_file) + replace_stdout(port_data->output_file); + + if (port_data->fd_count) + reply(&fd_count, sizeof(fd_count)); + if (port_data->no_packet_loop){ free(port_data); exit(0); } - /* - * If an output file was given, let it replace standard output. - */ - - if (port_data->output_file) - replace_stdout(port_data->output_file); - ret = packet_loop(); if(port_data->io_buf_size > 0) free(port_data->io_buf); -- cgit v1.2.3 From d5f541904839b0307b1db3a28aace3ea6ba2fd37 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 11 Aug 2015 13:20:49 +0200 Subject: erts: Flatten too long io vectors in uds write --- erts/emulator/sys/unix/sys_uds.c | 30 ++++++++++++++++++++++++++---- erts/emulator/sys/unix/sys_uds.h | 10 ++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys_uds.c b/erts/emulator/sys/unix/sys_uds.c index 3b63d05cf6..daebcb307b 100644 --- a/erts/emulator/sys/unix/sys_uds.c +++ b/erts/emulator/sys/unix/sys_uds.c @@ -18,7 +18,6 @@ * %CopyrightEnd% */ - #include "sys_uds.h" int @@ -89,12 +88,32 @@ sys_uds_writev(int fd, struct iovec *iov, size_t iov_len, struct msghdr msg; struct cmsghdr *cmsg = NULL; - int res; + int res, i; /* initialize socket message */ memset(&msg, 0, sizeof(struct msghdr)); - msg.msg_iov = iov; - msg.msg_iovlen = iov_len; + + /* We flatten the iov if it is too long */ + if (iov_len > MAXIOV) { + int size = 0; + char *buff; + for (i = 0; i < iov_len; i++) + size += iov[i].iov_len; + buff = malloc(size); + + for (i = 0; i < iov_len; i++) { + memcpy(buff, iov[i].iov_base, iov[i].iov_len); + buff += iov[i].iov_len; + } + + iov[0].iov_base = buff - size; + iov[0].iov_len = size; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + } else { + msg.msg_iov = iov; + msg.msg_iovlen = iov_len; + } /* initialize the ancillary data */ msg.msg_control = calloc(1, CMSG_SPACE(sizeof(int) * fd_count)); @@ -110,6 +129,9 @@ sys_uds_writev(int fd, struct iovec *iov, size_t iov_len, res = sendmsg(fd, &msg, flags); + if (iov_len > MAXIOV) + free(iov[0].iov_base); + free(msg.msg_control); return res; diff --git a/erts/emulator/sys/unix/sys_uds.h b/erts/emulator/sys/unix/sys_uds.h index 7ff58b17dd..844a2804d8 100644 --- a/erts/emulator/sys/unix/sys_uds.h +++ b/erts/emulator/sys/unix/sys_uds.h @@ -29,10 +29,20 @@ #define _XOPEN_SOURCE 500 #endif +#include + #include #include #include +#if defined IOV_MAX +#define MAXIOV IOV_MAX +#elif defined UIO_MAXIOV +#define MAXIOV UIO_MAXIOV +#else +#define MAXIOV 16 +#endif + #include "sys.h" int sys_uds_readv(int fd, struct iovec *iov, size_t iov_len, -- cgit v1.2.3 From 898ca7f86dff3fe21c9bf2e5018c7fb93dca158e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 14 Aug 2015 11:51:21 +0200 Subject: erts: Fix dereferencing of unaligned integer for sparc --- erts/emulator/sys/unix/erl_child_setup.c | 12 ++++++------ erts/emulator/sys/unix/sys_drivers.c | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 71c7948bf0..8bec36be60 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -133,8 +133,8 @@ start_new_child(int pipes[]) o_buff = buff; - flags = *(int*)buff; - buff += sizeof(int); + flags = get_int32(buff); + buff += sizeof(Sint32); DEBUG_PRINT("flags = %d", flags); @@ -150,8 +150,8 @@ start_new_child(int pipes[]) DEBUG_PRINT("wd = %s", wd); - cnt = *(int*)buff; - buff += sizeof(int); + cnt = get_int32(buff); + buff += sizeof(Sint32); new_environ = malloc(sizeof(char*)*(cnt + 1)); for (i = 0; i < cnt; i++, buff++) { @@ -162,8 +162,8 @@ start_new_child(int pipes[]) if (o_buff + size != buff) { /* This is a spawn executable call */ - cnt = *(int*)buff; - buff += sizeof(int); + cnt = get_int32(buff); + buff += sizeof(Sint32); args = malloc(sizeof(char*)*(cnt + 1)); for (i = 0; i < cnt; i++, buff++) { args[i] = buff; diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 8402197924..97a9c3dfaa 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -699,6 +699,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, io_vector[i++].iov_len = sizeof(buffsz); io_vector[i].iov_base = (void*)&flags; + flags = htonl(flags); io_vector[i++].iov_len = sizeof(flags); buffsz += sizeof(flags); @@ -715,6 +716,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, buffsz += io_vector[i-1].iov_len; io_vector[i].iov_base = (void*)&env_len; + env_len = htonl(env_len); io_vector[i++].iov_len = sizeof(env_len); buffsz += io_vector[i-1].iov_len; @@ -728,6 +730,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) { io_vector[i].iov_base = (void*)&argv_len; + argv_len = htonl(argv_len); io_vector[i++].iov_len = sizeof(argv_len); buffsz += io_vector[i-1].iov_len; -- cgit v1.2.3 From 9612b1fff77dc50e797f23b587321c90ceffe953 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 14 Aug 2015 19:45:44 +0200 Subject: erts: Fix uds socket handling for os x OS X is very strict in what it requires of you and also gives strange error codes for some errors. In this commit we remap EMSGSIZE to EMFILE and also precisely tune the amount of data we send on the socket so that we can recv only that data. If we try to recv more, recvmsg fails with EPIPE if the remote end has been closed. --- erts/emulator/sys/unix/sys_uds.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys_uds.c b/erts/emulator/sys/unix/sys_uds.c index daebcb307b..015d0346a1 100644 --- a/erts/emulator/sys/unix/sys_uds.c +++ b/erts/emulator/sys/unix/sys_uds.c @@ -38,6 +38,14 @@ sys_uds_readv(int fd, struct iovec *iov, size_t iov_len, msg.msg_controllen = sizeof(ancillary_buff); if((res = recvmsg(fd, &msg, flags)) < 0) { +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) + /* When some OS X versions run out of fd's + they give EMSGSIZE instead of EMFILE. + We remap this as we want the correct + error to appear for the user */ + if (errno == EMSGSIZE) + errno = EMFILE; +#endif return res; } -- cgit v1.2.3 From 05823de18bd48b70b398a6b6fd756a0f71587283 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 27 Aug 2015 10:53:07 +0200 Subject: erts: Fix forker driver ifdefs for win32 --- erts/emulator/beam/io.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 6ec7a9ba1e..fc247a3260 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -54,7 +54,9 @@ extern ErlDrvEntry fd_driver_entry; extern ErlDrvEntry vanilla_driver_entry; extern ErlDrvEntry spawn_driver_entry; +#ifndef __WIN32__ extern ErlDrvEntry forker_driver_entry; +#endif extern ErlDrvEntry *driver_tab[]; /* table of static drivers, only used during initialization */ erts_driver_t *driver_list; /* List of all drivers, static and dynamic. */ @@ -72,7 +74,9 @@ const Port erts_invalid_port = {{ERTS_INVALID_PORT}}; erts_driver_t vanilla_driver; erts_driver_t spawn_driver; +#ifndef __WIN32__ erts_driver_t forker_driver; +#endif erts_driver_t fd_driver; int erts_port_synchronous_ops = 0; @@ -2891,7 +2895,9 @@ void erts_init_io(int port_tab_size, init_driver(&fd_driver, &fd_driver_entry, NULL); init_driver(&vanilla_driver, &vanilla_driver_entry, NULL); init_driver(&spawn_driver, &spawn_driver_entry, NULL); +#ifndef __WIN32__ init_driver(&forker_driver, &forker_driver_entry, NULL); +#endif erts_init_static_drivers(); for (dp = driver_tab; *dp != NULL; dp++) erts_add_driver_entry(*dp, NULL, 1); @@ -2953,7 +2959,9 @@ void erts_lcnt_enable_io_lock_count(int enable) { lcnt_enable_drv_lock_count(&vanilla_driver, enable); lcnt_enable_drv_lock_count(&spawn_driver, enable); +#ifndef __WIN32__ lcnt_enable_drv_lock_count(&forker_driver, enable); +#endif lcnt_enable_drv_lock_count(&fd_driver, enable); /* enable lock counting in all drivers */ for (dp = driver_list; dp; dp = dp->next) { @@ -4843,8 +4851,10 @@ print_port_info(Port *p, int to, void *arg) erts_print(to, arg, "Port is a file: %s\n",p->name); } else if (p->drv_ptr == &spawn_driver) { erts_print(to, arg, "Port controls external process: %s\n",p->name); +#ifndef __WIN32__ } else if (p->drv_ptr == &forker_driver) { erts_print(to, arg, "Port controls forker process: %s\n",p->name); +#endif } else { erts_print(to, arg, "Port controls linked-in driver: %s\n",p->name); } -- cgit v1.2.3 From 3599b995428032eb26e8b7cc6ac52bc5260ee454 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 27 Aug 2015 10:56:15 +0200 Subject: erts: Make child_setup work with large environments --- erts/emulator/sys/unix/erl_child_setup.c | 26 +++++++++---- erts/emulator/sys/unix/sys_drivers.c | 66 ++++++++++++++++++++------------ 2 files changed, 61 insertions(+), 31 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 8bec36be60..6e0a7c143f 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -109,7 +109,7 @@ static int sigchld_pipe[2]; static int start_new_child(int pipes[]) { - int size, res, i; + int size, res, i, pos = 0; char *buff, *o_buff; char *cmd, *wd, **new_environ, **args = NULL, cbuff[1]; @@ -126,10 +126,19 @@ start_new_child(int pipes[]) DEBUG_PRINT("size = %d", size); - if ((res = read(pipes[0], buff, size)) != size) { - ABORT("Failed to read %d bytes from %d (%d,%d)", - size, pipes[0], res, errno); - } + do { + if ((res = read(pipes[0], buff + pos, size - pos)) < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + ABORT("Failed to read %d bytes from %d (%d,%d)", + size, pipes[0], res, errno); + } + if (res == 0) { + errno = EPIPE; + goto child_error; + } + pos += res; + } while(size - pos != 0); o_buff = buff; @@ -154,6 +163,7 @@ start_new_child(int pipes[]) buff += sizeof(Sint32); new_environ = malloc(sizeof(char*)*(cnt + 1)); + DEBUG_PRINT("env_len = %ld", cnt); for (i = 0; i < cnt; i++, buff++) { new_environ[i] = buff; while(*buff != '\0') buff++; @@ -176,6 +186,7 @@ start_new_child(int pipes[]) ABORT("Buff error: %p, %p:%p", o_buff, o_buff+size, buff); } + DEBUG_PRINT("read ack"); if (read(pipes[0], cbuff, 1) < 1) goto child_error; @@ -230,7 +241,8 @@ start_new_child(int pipes[]) execle(SHELL, "sh", "-c", cmd, (char *) NULL, new_environ); } child_error: - _exit(1); + DEBUG_PRINT("exec error: %d\r\n",errno); + _exit(128 + errno); } @@ -392,7 +404,7 @@ main(int argc, char *argv[]) int ibuff[2]; char buff[256]; res = read(sigchld_pipe[0], ibuff, sizeof(ibuff)); - if (res < 0) { + if (res <= 0) { if (errno == EINTR) continue; ABORT("Failed to read from sigchld pipe: %d (%d)", res, errno); diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 97a9c3dfaa..6a1f2f6b2c 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -558,7 +558,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, int len; char **new_environ; - ErtsSysDriverData *res; + ErtsSysDriverData *dd; char *cmd_line; char wd_buff[MAXPATHLEN+1]; char *wd; @@ -658,10 +658,10 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, { struct iovec *io_vector; - int buffsz = 0, iov_len = 5; + int iov_len = 5; char nullbuff[] = "\0"; - int j, i = 0; - Sint32 env_len = 0, argv_len = 0, + int j, i = 0, res; + Sint32 buffsz = 0, env_len = 0, argv_len = 0, flags = (opts->use_stdio ? FORKER_FLAG_USE_STDIO : 0) | (opts->exit_status ? FORKER_FLAG_EXIT_STATUS : 0) | (opts->read_write & DO_READ ? FORKER_FLAG_DO_READ : 0) @@ -753,16 +753,29 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, } /* we send the request to do the fork */ - if (writev(ofd[1], io_vector, iov_len) < 0) { - int err = errno; - close_pipes(ifd, ofd); - erts_free(ERTS_ALC_T_TMP, io_vector); - if (new_environ != environ) - erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); - erts_smp_rwmtx_runlock(&environ_rwmtx); - erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); - errno = err; - return ERL_DRV_ERROR_ERRNO; + if ((res = writev(ofd[1], io_vector, iov_len > MAXIOV ? MAXIOV : iov_len)) < 0) { + if (errno == ERRNO_BLOCK) { + res = 0; + } else { + int err = errno; + close_pipes(ifd, ofd); + erts_free(ERTS_ALC_T_TMP, io_vector); + if (new_environ != environ) + erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ); + erts_smp_rwmtx_runlock(&environ_rwmtx); + erts_free(ERTS_ALC_T_TMP, (void *) cmd_line); + errno = err; + return ERL_DRV_ERROR_ERRNO; + } + } + + if (res < buffsz) { + /* we only wrote part of the command payload. Enqueue the rest. */ + for (i = 0; i < iov_len; i++) { + driver_enq(port_num, io_vector[i].iov_base, io_vector[i].iov_len); + } + driver_deq(port_num, res); + driver_select(port_num, ofd[1], ERL_DRV_WRITE|ERL_DRV_USE, 1); } erts_free(ERTS_ALC_T_TMP, io_vector); @@ -775,7 +788,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, erts_smp_rwmtx_runlock(&environ_rwmtx); - res = create_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes, + dd = create_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes, DO_WRITE | DO_READ, opts->exit_status, 0, 0); @@ -797,12 +810,12 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, /* we set these fds to negative to mark if they should be closed after the handshake */ if (!(opts->read_write & DO_READ)) - res->ifd->fd *= -1; + dd->ifd->fd *= -1; if (!(opts->read_write & DO_WRITE)) - res->ofd->fd *= -1; + dd->ofd->fd *= -1; - return (ErlDrvData)res; + return (ErlDrvData)dd; #undef CMD_LINE_PREFIX_STR #undef CMD_LINE_PREFIX_STR_SZ } @@ -1365,10 +1378,16 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) return; } - if (write(abs(dd->ofd->fd), "A", 1) < 0) - ; /* do nothing on failure here. If the ofd is broken, then - the ifd will probably also be broken and trigger - a port_inp_failure */ + if (driver_sizeq(port_num) > 0) { + driver_enq(port_num, "A", 1); + } else { + if (write(abs(dd->ofd->fd), "A", 1) < 0) + if (errno == ERRNO_BLOCK || errno == EINTR) + driver_enq(port_num, "A", 1); + /* do nothing on failure here. If the ofd is broken, then + the ifd will probably also be broken and trigger + a port_inp_failure */ + } if (dd->ifd->fd < 0) { driver_select(port_num, abs(dd->ifd->fd), ERL_DRV_READ|ERL_DRV_USE, 0); @@ -1381,9 +1400,8 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) child setup will close this fd if fd < 0 */ driver_select(port_num, abs(dd->ofd->fd), ERL_DRV_WRITE|ERL_DRV_USE, 1); - if (dd->alive == 1) { + if (dd->alive == 1) forker_add_os_pid_mapping(dd); - } erl_drv_set_os_pid(port_num, dd->pid); erl_drv_init_ack(port_num, e); -- cgit v1.2.3 From 123797a395b96b083d895c6ed7f41c56f4eafc78 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 14 Sep 2015 11:04:22 +0200 Subject: erts: Handle all EINTR and EAGAIN cases in child setup --- erts/emulator/sys/unix/erl_child_setup.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 6e0a7c143f..1f7ead0ec3 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -56,6 +56,8 @@ #include #include +#define WANT_NONBLOCKING + #include "erl_driver.h" #include "sys_uds.h" @@ -118,7 +120,11 @@ start_new_child(int pipes[]) /* only child executes here */ - if ((res = read(pipes[0], (char*)&size, sizeof(size))) < 0) { + do { + res = read(pipes[0], (char*)&size, sizeof(size)); + } while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK)); + + if (res <= 0) { ABORT("Failed to read size from %d (%d)", pipes[0], errno); } @@ -128,7 +134,7 @@ start_new_child(int pipes[]) do { if ((res = read(pipes[0], buff + pos, size - pos)) < 0) { - if (errno == EAGAIN || errno == EINTR) + if (errno == ERRNO_BLOCK || errno == EINTR) continue; ABORT("Failed to read %d bytes from %d (%d,%d)", size, pipes[0], res, errno); @@ -187,8 +193,13 @@ start_new_child(int pipes[]) } DEBUG_PRINT("read ack"); - if (read(pipes[0], cbuff, 1) < 1) + do { + res = read(pipes[0], cbuff, 1); + } while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK)); + if (res < 1) { + errno = EPIPE; goto child_error; + } DEBUG_PRINT("Do that forking business: '%s'\n",cmd); @@ -270,7 +281,10 @@ static void handle_sigchld(int sig) { sys_sigblock(SIGCHLD); while ((buff[0] = waitpid((pid_t)(-1), buff+1, WNOHANG)) > 0) { - if ((res = write(sigchld_pipe[1], buff, sizeof(buff))) <= 0) + do { + res = write(sigchld_pipe[1], buff, sizeof(buff)); + } while (res < 0 && errno == EINTR); + if (res <= 0) ABORT("Failed to write to sigchld_pipe (%d): %d (%d)", sigchld_pipe[1], res, errno); DEBUG_PRINT("Reap child %d (%d)", buff[0], buff[1]); } @@ -391,7 +405,7 @@ main(int argc, char *argv[]) /* We write an ack here, but expect the reply on the pipes[0] inside the fork */ res = sprintf(buff,"GO:%010d:%010d", os_pid, errno); - if (write(pipes[1], buff, res + 1)) + while (write(pipes[1], buff, res + 1) < 0 && errno == EINTR) ; /* remove gcc warning */ sys_sigrelease(SIGCHLD); -- cgit v1.2.3 From 0ad8c5f46bc0173c09fa5e7e91f917de82389068 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 11 Sep 2015 16:35:48 +0200 Subject: erts: Move os_pid to port hash to child setup Had to move the hashing because of a race that can otherwise happen where a new os_pid value was inserted into the hash before the previous value had been removed. Also replaced the protocol inbetween erts and child setup to be a binary protocol. This was done in order to deal with the varying size of Eterm. --- erts/emulator/Makefile.in | 2 +- erts/emulator/beam/erl_lock_check.c | 1 - erts/emulator/sys/unix/erl_child_setup.c | 127 +++++++++++++++++-- erts/emulator/sys/unix/erl_child_setup.h | 68 ++++++++++ erts/emulator/sys/unix/erl_unix_sys.h | 9 -- erts/emulator/sys/unix/sys_drivers.c | 211 ++++++++++--------------------- 6 files changed, 251 insertions(+), 167 deletions(-) create mode 100644 erts/emulator/sys/unix/erl_child_setup.h (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index ab415b66b4..8cf435905b 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -690,7 +690,7 @@ $(OBJDIR)/%.o: drivers/$(ERLANG_OSTYPE)/%.c # ---------------------------------------------------------------------- # Specials # -CS_OBJ = $(OBJDIR)/erl_child_setup.o $(OBJDIR)/sys_uds.o +CS_OBJ = $(OBJDIR)/erl_child_setup.o $(OBJDIR)/sys_uds.o $(OBJDIR)/hash.o $(BINDIR)/$(CS_EXECUTABLE): $(TTF_DIR)/GENERATED $(PRELOAD_SRC) $(CS_OBJ) $(ERTS_LIB) $(ld_verbose)$(CS_PURIFY) $(LD) $(CS_LDFLAGS) -o $(BINDIR)/$(CS_EXECUTABLE) \ diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 34c0144cbc..f7b4bd8041 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -184,7 +184,6 @@ static erts_lc_lock_order_t erts_lock_order[] = { #ifdef ERTS_SMP { "os_monotonic_time", NULL }, #endif - { "forker_hash_mtx", NULL }, { "erts_alloc_hard_debug", NULL }, { "hard_dbg_mseg", NULL }, { "erts_mmap", NULL } diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 1f7ead0ec3..f74cb0f356 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -60,6 +60,8 @@ #include "erl_driver.h" #include "sys_uds.h" +#include "hash.h" +#include "erl_child_setup.h" #define SET_CLOEXEC(fd) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) @@ -105,6 +107,10 @@ void sys_sigrelease(int sig) sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL); } +static void add_os_pid_to_port_id_mapping(Eterm, pid_t); +static Eterm get_port_id(pid_t); +static int forker_hash_init(void); + static int max_files = -1; static int sigchld_pipe[2]; @@ -114,7 +120,7 @@ start_new_child(int pipes[]) int size, res, i, pos = 0; char *buff, *o_buff; - char *cmd, *wd, **new_environ, **args = NULL, cbuff[1]; + char *cmd, *wd, **new_environ, **args = NULL; Sint cnt, flags; @@ -194,7 +200,12 @@ start_new_child(int pipes[]) DEBUG_PRINT("read ack"); do { - res = read(pipes[0], cbuff, 1); + ErtsSysForkerProto proto; + res = read(pipes[0], &proto, sizeof(proto)); + if (res > 0) { + ASSERT(proto.action == ErtsSysForkerProtoAction_Ack); + ASSERT(res == sizeof(proto)); + } } while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK)); if (res < 1) { errno = EPIPE; @@ -355,6 +366,8 @@ main(int argc, char *argv[]) exit(1); } + forker_hash_init(); + SET_CLOEXEC(uds_fd); DEBUG_PRINT("Starting forker %d", max_files); @@ -376,9 +389,10 @@ main(int argc, char *argv[]) if (FD_ISSET(uds_fd, &read_fds)) { int pipes[3], res, os_pid; - char buff[256]; + ErtsSysForkerProto proto; errno = 0; - if ((res = sys_uds_read(uds_fd, buff, 1, pipes, 3, MSG_DONTWAIT)) < 0) { + if ((res = sys_uds_read(uds_fd, (char*)&proto, sizeof(proto), + pipes, 3, MSG_DONTWAIT)) < 0) { if (errno == EINTR) continue; DEBUG_PRINT("erl_child_setup failed to read from uds: %d, %d", res, errno); @@ -391,8 +405,8 @@ main(int argc, char *argv[]) } /* Since we use unix domain sockets and send the entire data in one go we *should* get the entire payload at once. */ - ASSERT(res == 1); - ASSERT(buff[0] == 'S'); + ASSERT(res == sizeof(proto)); + ASSERT(proto.action == ErtsSysForkerProtoAction_Start); sys_sigblock(SIGCHLD); @@ -402,10 +416,14 @@ main(int argc, char *argv[]) if (os_pid == 0) start_new_child(pipes); + add_os_pid_to_port_id_mapping(proto.u.start.port_id, os_pid); + /* We write an ack here, but expect the reply on the pipes[0] inside the fork */ - res = sprintf(buff,"GO:%010d:%010d", os_pid, errno); - while (write(pipes[1], buff, res + 1) < 0 && errno == EINTR) + proto.action = ErtsSysForkerProtoAction_Go; + proto.u.go.os_pid = os_pid; + proto.u.go.error_number = errno; + while (write(pipes[1], &proto, sizeof(proto)) < 0 && errno == EINTR) ; /* remove gcc warning */ sys_sigrelease(SIGCHLD); @@ -416,16 +434,23 @@ main(int argc, char *argv[]) if (FD_ISSET(sigchld_pipe[0], &read_fds)) { int ibuff[2]; - char buff[256]; + ErtsSysForkerProto proto; res = read(sigchld_pipe[0], ibuff, sizeof(ibuff)); if (res <= 0) { if (errno == EINTR) continue; ABORT("Failed to read from sigchld pipe: %d (%d)", res, errno); } - res = snprintf(buff, 256, "SIGCHLD:%010d:%010d", ibuff[0], ibuff[1]); + + proto.u.sigchld.port_id = get_port_id((pid_t)(ibuff[0])); + + if (proto.u.sigchld.port_id == THE_NON_VALUE) + continue; /* exit status report not requested */ + + proto.action = ErtsSysForkerProtoAction_SigChld; + proto.u.sigchld.error_number = ibuff[1]; DEBUG_PRINT("send %s to %d", buff, uds_fd); - if (write(uds_fd, buff, res + 1) < 0) { + if (write(uds_fd, &proto, sizeof(proto)) < 0) { if (errno == EINTR) continue; /* The uds was close, which most likely means that the VM @@ -437,3 +462,83 @@ main(int argc, char *argv[]) } return 1; } + +typedef struct exit_status { + HashBucket hb; + pid_t os_pid; + Eterm port_id; +} ErtsSysExitStatus; + +static Hash *forker_hash; + +static void add_os_pid_to_port_id_mapping(Eterm port_id, pid_t os_pid) +{ + if (port_id != THE_NON_VALUE) { + /* exit status report requested */ + ErtsSysExitStatus es; + es.os_pid = os_pid; + es.port_id = port_id; + hash_put(forker_hash, &es); + } +} + +static Eterm get_port_id(pid_t os_pid) +{ + ErtsSysExitStatus est, *es; + Eterm port_id; + est.os_pid = os_pid; + es = hash_remove(forker_hash, &est); + if (!es) return THE_NON_VALUE; + port_id = es->port_id; + free(es); + return port_id; +} + +static int fcmp(void *a, void *b) +{ + ErtsSysExitStatus *sa = a; + ErtsSysExitStatus *sb = b; + return !(sa->os_pid == sb->os_pid); +} + +static HashValue fhash(void *e) +{ + ErtsSysExitStatus *se = e; + Uint32 val = se->os_pid; + val = (val+0x7ed55d16) + (val<<12); + val = (val^0xc761c23c) ^ (val>>19); + val = (val+0x165667b1) + (val<<5); + val = (val+0xd3a2646c) ^ (val<<9); + val = (val+0xfd7046c5) + (val<<3); + val = (val^0xb55a4f09) ^ (val>>16); + return val; +} + +static void *falloc(void *e) +{ + ErtsSysExitStatus *se = e; + ErtsSysExitStatus *ne = malloc(sizeof(ErtsSysExitStatus)); + ne->os_pid = se->os_pid; + ne->port_id = se->port_id; + return ne; +} + +static void *meta_alloc(int type, size_t size) { return malloc(size); } +static void meta_free(int type, void *p) { free(p); } + +static int forker_hash_init(void) +{ + HashFunctions forker_hash_functions; + forker_hash_functions.hash = fhash; + forker_hash_functions.cmp = fcmp; + forker_hash_functions.alloc = falloc; + forker_hash_functions.free = free; + forker_hash_functions.meta_alloc = meta_alloc; + forker_hash_functions.meta_free = meta_free; + forker_hash_functions.meta_print = NULL; + + forker_hash = hash_new(0, "forker_hash", + 16, forker_hash_functions); + + return 1; +} diff --git a/erts/emulator/sys/unix/erl_child_setup.h b/erts/emulator/sys/unix/erl_child_setup.h new file mode 100644 index 0000000000..c3258f7cbe --- /dev/null +++ b/erts/emulator/sys/unix/erl_child_setup.h @@ -0,0 +1,68 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2015-2015. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + * + * This file defines the interface inbetween erts and child_setup. + */ + +#ifndef _ERL_UNIX_FORKER_H +#define _ERL_UNIX_FORKER_H + +#include "sys.h" + +#define FORKER_ARGV_NO_OF_ARGS 3 +#define FORKER_ARGV_PROGNAME_IX 0 /* Program name */ +#define FORKER_ARGV_MAX_FILES 1 /* max_files */ + +#define FORKER_FLAG_USE_STDIO (1 << 0) /* dup the pipe to stdin/stderr */ +#define FORKER_FLAG_EXIT_STATUS (1 << 1) /* send the exit status to parent */ +#define FORKER_FLAG_DO_READ (1 << 2) /* dup write fd */ +#define FORKER_FLAG_DO_WRITE (1 << 3) /* dup read fd */ + +#if SIZEOF_VOID_P == SIZEOF_LONG +typedef unsigned long ErtsSysPortId; +#elif SIZEOF_VOID_P == SIZEOF_INT +typedef unsigned int ErtsSysPortId; +#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG +typedef unsigned long long ErtsSysPortId; +#endif + +typedef struct ErtsSysForkerProto_ { + enum { + ErtsSysForkerProtoAction_Start, + ErtsSysForkerProtoAction_Go, + ErtsSysForkerProtoAction_SigChld, + ErtsSysForkerProtoAction_Ack + } action; + union { + struct { + ErtsSysPortId port_id; + int fds[3]; + } start; + struct { + pid_t os_pid; + int error_number; + } go; + struct { + ErtsSysPortId port_id; + int error_number; + } sigchld; + } u; +} ErtsSysForkerProto; + +#endif /* #ifndef _ERL_UNIX_FORKER_H */ diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index c46f2c1fa2..0352ee1b3c 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -410,15 +410,6 @@ void erts_sys_unblock_fpe(int); #define ERTS_FP_ERROR_THOROUGH(p, f, A) __ERTS_FP_ERROR_THOROUGH(&(p)->fp_exception, f, A) -#define FORKER_ARGV_NO_OF_ARGS 3 -#define FORKER_ARGV_PROGNAME_IX 0 /* Program name */ -#define FORKER_ARGV_MAX_FILES 1 /* max_files */ - -#define FORKER_FLAG_USE_STDIO (1 << 0) /* dup the pipe to stdin/stderr */ -#define FORKER_FLAG_EXIT_STATUS (1 << 1) /* send the exit status to parent */ -#define FORKER_FLAG_DO_READ (1 << 2) /* dup write fd */ -#define FORKER_FLAG_DO_WRITE (1 << 3) /* dup read fd */ - /* Threads */ #ifdef USE_THREADS extern int init_async(int); diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 6a1f2f6b2c..82096e5779 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -76,6 +76,8 @@ static Eterm forker_port; #include "erl_sys_driver.h" #include "sys_uds.h" +#include "erl_child_setup.h" + #if defined IOV_MAX #define MAXIOV IOV_MAX #elif defined UIO_MAXIOV @@ -120,12 +122,6 @@ typedef struct driver_data { ErtsSysBlocking *blocking; } ErtsSysDriverData; -typedef struct exit_status { - HashBucket hb; - pid_t os_pid; - Eterm port_id; -} ErtsSysExitStatus; - #define DIR_SEPARATOR_CHAR '/' #if defined(__ANDROID__) @@ -241,21 +237,18 @@ static void outputv(ErlDrvData, ErlIOVec*); static void stop_select(ErlDrvEvent, void*); /* II.V Forker prototypes */ -static int forker_init(void); static ErlDrvData forker_start(ErlDrvPort, char*, SysDriverOpts*); static void forker_stop(ErlDrvData); static void forker_ready_input(ErlDrvData, ErlDrvEvent); static void forker_ready_output(ErlDrvData, ErlDrvEvent); static ErlDrvSSizeT forker_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT, char **, ErlDrvSizeT); -static void forker_add_os_pid_mapping(ErtsSysDriverData *); -static void forker_remove_os_pid_mapping(ErtsSysDriverData *); /* III Driver entries */ /* III.I The spawn driver */ struct erl_drv_entry spawn_driver_entry = { - forker_init, + NULL, spawn_start, stop, output, @@ -794,12 +787,16 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, { /* send ofd[0] + ifd[1] + stderrfd to forker port */ - int *fds = erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA, sizeof(int)*3); - memset(fds, 0, sizeof(int)*3); - fds[0] = ofd[0]; - fds[1] = ifd[1]; - fds[2] = stderrfd; - if (erl_drv_port_control(forker_port, 'S', (char*)fds, sizeof(int)*3)) { + ErtsSysForkerProto *proto = + erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA, + sizeof(ErtsSysForkerProto)); + memset(proto, 0, sizeof(ErtsSysForkerProto)); + proto->action = ErtsSysForkerProtoAction_Start; + proto->u.start.fds[0] = ofd[0]; + proto->u.start.fds[1] = ifd[1]; + proto->u.start.fds[2] = stderrfd; + proto->u.start.port_id = opts->exit_status ? erts_drvport2id(port_num) : THE_NON_VALUE; + if (erl_drv_port_control(forker_port, 'S', (char*)proto, sizeof(*proto))) { /* The forker port has been killed, we close both fd's which will make open_port throw an epipe error */ close(ofd[0]); @@ -824,8 +821,12 @@ static ErlDrvSSizeT spawn_control(ErlDrvData e, unsigned int cmd, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) { ErtsSysDriverData *dd = (ErtsSysDriverData*)e; + ErtsSysForkerProto *proto = (ErtsSysForkerProto *)buf; - memcpy(&dd->status, buf, sizeof(dd->status)); + ASSERT(len == sizeof(*proto)); + ASSERT(proto->action == ErtsSysForkerProtoAction_SigChld); + + dd->status = proto->u.sigchld.error_number; dd->alive = -1; if (dd->ifd) @@ -1134,10 +1135,6 @@ static void stop(ErlDrvData ev) ErtsSysDriverData* dd = (ErtsSysDriverData*)ev; ErlDrvPort prt = dd->port_num; - if (dd->alive == 1) - /* Stop has been called before the exit status has been reported */ - forker_remove_os_pid_mapping(dd); - if (dd->ifd) { nbio_stop_fd(prt, dd->ifd); driver_select(prt, abs(dd->ifd->fd), ERL_DRV_USE, 0); /* close(ifd); */ @@ -1354,10 +1351,10 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) if (dd->pid == 0) { /* the pid is sent from erl_child_setup. spawn driver only. */ - char message_buffer[3 + 10 + 1 + 10 + 1]; - int reason, res; + ErtsSysForkerProto proto; + int res; - if((res = read(ready_fd, message_buffer, sizeof(message_buffer))) <= 0) { + if((res = read(ready_fd, &proto, sizeof(proto))) <= 0) { /* hmm, child setup seems to have closed the pipe too early... we close the port as there is not much else we can do */ if (res < 0 && errno == ERRNO_BLOCK) @@ -1369,21 +1366,25 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) return; } - if(sscanf(message_buffer,"GO:%010d:%010d", &dd->pid, &reason) == 2) { - if (dd->pid == -1) { - /* Setup failed! The only reason why this should happen is if - the fork fails. */ - errno = reason; - port_inp_failure(dd, -1); - return; - } + ASSERT(proto.action == ErtsSysForkerProtoAction_Go); + dd->pid = proto.u.go.os_pid; + + if (dd->pid == -1) { + /* Setup failed! The only reason why this should happen is if + the fork fails. */ + errno = proto.u.go.error_number; + port_inp_failure(dd, -1); + return; + } + + proto.action = ErtsSysForkerProtoAction_Ack; - if (driver_sizeq(port_num) > 0) { - driver_enq(port_num, "A", 1); + if (driver_sizeq(port_num) > 0) { + driver_enq(port_num, (char*)&proto, sizeof(proto)); } else { - if (write(abs(dd->ofd->fd), "A", 1) < 0) + if (write(abs(dd->ofd->fd), &proto, sizeof(proto)) < 0) if (errno == ERRNO_BLOCK || errno == EINTR) - driver_enq(port_num, "A", 1); + driver_enq(port_num, (char*)&proto, sizeof(proto)); /* do nothing on failure here. If the ofd is broken, then the ifd will probably also be broken and trigger a port_inp_failure */ @@ -1400,15 +1401,9 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd) child setup will close this fd if fd < 0 */ driver_select(port_num, abs(dd->ofd->fd), ERL_DRV_WRITE|ERL_DRV_USE, 1); - if (dd->alive == 1) - forker_add_os_pid_mapping(dd); - erl_drv_set_os_pid(port_num, dd->pid); erl_drv_init_ack(port_num, e); return; - } - ASSERT(0); - return; } if (packet_bytes == 0) { @@ -1652,10 +1647,6 @@ void fd_ready_async(ErlDrvData drv_data, /* Forker driver */ static int forker_fd; -static Hash *forker_hash; -static erts_smp_mtx_t forker_hash_mtx; - -static void ffree(void *e); static ErlDrvData forker_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) @@ -1749,16 +1740,18 @@ static ErlDrvData forker_start(ErlDrvPort port_num, char* name, static void forker_stop(ErlDrvData e) { - /* we probably should do something here */ + /* we probably should do something here, + the port has been closed by the user. */ } static void forker_ready_input(ErlDrvData e, ErlDrvEvent fd) { - int res, *exit_status; - char buff[8+10+1+10+1] = {0}; - ErtsSysExitStatus est, *es; + int res; + ErtsSysForkerProto *proto; + + proto = erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA, sizeof(*proto)); - if ((res = read(fd, buff, sizeof(buff))) < 0) { + if ((res = read(fd, proto, sizeof(*proto))) < 0) { if (errno == ERRNO_BLOCK) return; erl_exit(ERTS_DUMP_EXIT, "Failed to read from erl_child_setup: %d\n", errno); @@ -1767,26 +1760,15 @@ static void forker_ready_input(ErlDrvData e, ErlDrvEvent fd) if (res == 0) erl_exit(ERTS_DUMP_EXIT, "erl_child_setup closed\n"); - if (sscanf(buff, "SIGCHLD:%010d:%010d", &est.os_pid, &res) != 2) - erl_exit(ERTS_DUMP_EXIT, "Got corrupt data from erl_child_setup: %.s\n", - buff, sizeof(buff)); - - erts_smp_mtx_lock(&forker_hash_mtx); - es = hash_remove(forker_hash, &est); - erts_smp_mtx_unlock(&forker_hash_mtx); + ASSERT(res == sizeof(*proto)); + ASSERT(proto->action == ErtsSysForkerProtoAction_SigChld); - if (!es) - return; - - exit_status = erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA, sizeof(int)); - exit_status[0] = res; /* ideally this would be a port_command call, but as command is already used by the spawn_driver, we use control instead. Note that when using erl_drv_port_control it is an asynchronous control. */ - erl_drv_port_control(es->port_id, 'S', (char*)exit_status, sizeof(int)); - - ffree(es); + erl_drv_port_control(proto->u.sigchld.port_id, 'S', + (char*)proto, sizeof(*proto)); } @@ -1797,18 +1779,19 @@ static void forker_ready_output(ErlDrvData e, ErlDrvEvent fd) while(driver_sizeq(port_num) > 0) { int vlen; SysIOVec *iov = driver_peekq(port_num, &vlen); - int *fds = (int*)iov[0].iov_base; - ASSERT(iov[0].iov_len >= sizeof(int)*3); - if (sys_uds_write(forker_fd, "S", 1, fds, 3, 0) < 0) { + ErtsSysForkerProto *proto = (ErtsSysForkerProto *)iov[0].iov_base; + ASSERT(iov[0].iov_len >= (sizeof(*proto))); + if (sys_uds_write(forker_fd, (char*)proto, sizeof(*proto), + proto->u.start.fds, 3, 0) < 0) { if (errno == ERRNO_BLOCK) return; erl_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno); } - close(fds[0]); - close(fds[1]); - if (fds[1] != fds[2]) - close(fds[2]); - driver_deq(port_num, sizeof(int)*3); + close(proto->u.start.fds[0]); + close(proto->u.start.fds[1]); + if (proto->u.start.fds[1] != proto->u.start.fds[2]) + close(proto->u.start.fds[2]); + driver_deq(port_num, sizeof(*proto)); } driver_select(port_num, forker_fd, ERL_DRV_WRITE, 0); @@ -1817,7 +1800,7 @@ static void forker_ready_output(ErlDrvData e, ErlDrvEvent fd) static ErlDrvSSizeT forker_control(ErlDrvData e, unsigned int cmd, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) { - int *fds = (int*)buf; + ErtsSysForkerProto *proto = (ErtsSysForkerProto *)buf; ErlDrvPort port_num = (ErlDrvPort)e; int res; @@ -1826,7 +1809,8 @@ static ErlDrvSSizeT forker_control(ErlDrvData e, unsigned int cmd, char *buf, return 0; } - if ((res = sys_uds_write(forker_fd, "S", 1, fds, 3, 0)) < 0) { + if ((res = sys_uds_write(forker_fd, (char*)proto, sizeof(*proto), + proto->u.start.fds, 3, 0)) < 0) { if (errno == ERRNO_BLOCK) { driver_enq(port_num, buf, len); driver_select(port_num, forker_fd, ERL_DRV_WRITE|ERL_DRV_USE, 1); @@ -1834,74 +1818,11 @@ static ErlDrvSSizeT forker_control(ErlDrvData e, unsigned int cmd, char *buf, } erl_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno); } - close(fds[0]); - close(fds[1]); - if (fds[1] != fds[2]) - close(fds[2]); - return 0; -} -static void forker_add_os_pid_mapping(ErtsSysDriverData *dd) -{ - Eterm port_id = erts_drvport2id(dd->port_num); - ErtsSysExitStatus es; - es.os_pid = dd->pid; - es.port_id = port_id; - erts_smp_mtx_lock(&forker_hash_mtx); - hash_put(forker_hash, &es); - erts_smp_mtx_unlock(&forker_hash_mtx); -} - -static void forker_remove_os_pid_mapping(ErtsSysDriverData *dd) -{ - ErtsSysExitStatus est, *es; - est.os_pid = dd->pid; - erts_smp_mtx_lock(&forker_hash_mtx); - es = hash_remove(forker_hash, &est); - erts_smp_mtx_unlock(&forker_hash_mtx); - if (es) - ffree(es); -} - -static int fcmp(void *a, void *b) -{ - ErtsSysExitStatus *sa = a; - ErtsSysExitStatus *sb = b; - return !(sa->os_pid == sb->os_pid); -} - -static HashValue fhash(void *e) -{ - ErtsSysExitStatus *se = e; - return make_hash2(make_small(se->os_pid)); -} - -static void *falloc(void *e) -{ - ErtsSysExitStatus *se = e; - ErtsSysExitStatus *ne = erts_alloc(ERTS_ALC_T_DRV, sizeof(ErtsSysExitStatus)); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, sizeof(ErtsSysBlocking)); - ne->os_pid = se->os_pid; - ne->port_id = se->port_id; - return ne; -} - -static void ffree(void *e) -{ - erts_free(ERTS_ALC_T_DRV, e); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, -sizeof(ErtsSysBlocking)); -} - -static int forker_init(void) -{ - HashFunctions forker_hash_functions; - forker_hash_functions.hash = fhash; - forker_hash_functions.cmp = fcmp; - forker_hash_functions.alloc = falloc; - forker_hash_functions.free = ffree; - forker_hash = hash_new(ERTS_ALC_T_DRV, "forker_hash", - 16, forker_hash_functions); - erts_smp_mtx_init(&forker_hash_mtx, "forker_hash_mtx"); - - return 1; + ASSERT(res == sizeof(*proto)); + close(proto->u.start.fds[0]); + close(proto->u.start.fds[1]); + if (proto->u.start.fds[1] != proto->u.start.fds[2]) + close(proto->u.start.fds[2]); + return 0; } -- cgit v1.2.3 From 846c86559dea147d069bd489e141484dc2194610 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 14 Sep 2015 11:02:18 +0200 Subject: erts: Add testcase for huge port environment --- erts/emulator/test/port_SUITE.erl | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index b67fd1c409..6cc86a26a3 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -86,7 +86,7 @@ iter_max_ports/1, eof/1, input_only/1, output_only/1, name1/1, t_binary/1, parallell/1, t_exit/1, - env/1, bad_env/1, cd/1, exit_status/1, + env/1, huge_env/1, bad_env/1, cd/1, exit_status/1, tps_16_bytes/1, tps_1K/1, line/1, stderr_to_stdout/1, otp_3906/1, otp_4389/1, win_massive/1, win_massive_client/1, mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1, @@ -112,7 +112,7 @@ all() -> bad_packet, bad_port_messages, {group, options}, {group, multiple_packets}, parallell, dying_port, port_program_with_path, open_input_file_port, - open_output_file_port, name1, env, bad_env, cd, + open_output_file_port, name1, env, huge_env, bad_env, cd, exit_status, iter_max_ports, count_fds, t_exit, {group, tps}, line, stderr_to_stdout, otp_3906, otp_4389, win_massive, mix_up_ports, otp_5112, otp_5119, @@ -962,6 +962,40 @@ try_bad_env(Env) -> error:badarg -> ok end. +%% Test that we can handle a very very large environment gracefully. +huge_env(Config) when is_list(Config) -> + Vars = case os:type() of + {win32,_} -> 500; + _ -> + %% We create a huge environment, + %% 20000 variables is about 25MB + %% which seems to be the limit on Linux. + 20000 + end, + Env = [{[$a + I div (25*25*25*25) rem 25, + $a + I div (25*25*25) rem 25, + $a + I div (25*25) rem 25, + $a+I div 25 rem 25, $a+I rem 25], + lists:duplicate(100,$a+I rem 25)} + || I <- lists:seq(1,Vars)], + try erlang:open_port({spawn,"ls"},[exit_status, {env, Env}]) of + P -> + receive + {P, {exit_status,N}} = M -> + %% We test that the exit status is an integer, this means + %% that the child program has started. If we get an atom + %% something went wrong in the driver which is not ok. + ct:log("Got ~p",[M]), + true = is_integer(N) + end + catch E:R -> + %% Have to catch the error here, as printing the stackdump + %% in the ct log is way to heavy for some test machines. + ct:fail("Open port failed ~p:~p",[E,R]) + end. + + + %% 'cd' option %% (Can perhaps be made smaller by calling the other utility functions %% in this module.) -- cgit v1.2.3 From e56d78b761680d83ab61045a62d4fc7bdb312b3c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 28 Sep 2015 11:02:40 +0200 Subject: erts: Fix memory leak at async open port When an async open port fails early, it would sometimes leak memory. --- erts/emulator/beam/io.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index fc247a3260..df8a213a1d 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -479,6 +479,11 @@ erts_port_free(Port *prt) erts_port_task_fini_sched(&prt->sched); + if (prt->async_open_port) { + erts_free(ERTS_ALC_T_PRTSD, prt->async_open_port); + prt->async_open_port = NULL; + } + #ifdef ERTS_SMP ASSERT(prt->lock); if (state & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK) -- cgit v1.2.3 From 42b6156235421fbda1bc65c725e4ea7d183e4c84 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 12 Oct 2015 14:59:37 +0200 Subject: erts: Fix large open_port arg segfault for win32 os_SUITE:large_output_command send a 10k large argument to open_port({spawn,""}) which was too small for the 2K buffer allocated for win32. The buffer is now dynamic and any size can be used. --- erts/emulator/sys/win32/sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 3793357848..76ce25916a 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -1526,8 +1526,8 @@ create_child_process * Parse out the program name from the command line (it can be quoted and * contain spaces). */ - newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, 2048*sizeof(wchar_t)); cmdlength = parse_command(origcmd); + newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (MAX_PATH+wcslen(origcmd)-cmdlength)*sizeof(wchar_t)); thecommand = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (cmdlength+1)*sizeof(wchar_t)); wcsncpy(thecommand, origcmd, cmdlength); thecommand[cmdlength] = L'\0'; -- cgit v1.2.3 From 598e7e6a456d882aaa586b5ddb17811ecd43f2e5 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 13 Oct 2015 16:55:50 +0200 Subject: erts: Add forker StartAck for port start flowcontrol An acknowledgement of the Start command has to be managed as we have to make sure that packages are not dropped and also that the close calls do not happen too early. --- erts/emulator/sys/unix/erl_child_setup.c | 4 ++ erts/emulator/sys/unix/erl_child_setup.h | 1 + erts/emulator/sys/unix/sys_drivers.c | 76 +++++++++++++++++++------------- 3 files changed, 51 insertions(+), 30 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index f74cb0f356..fdffc7f119 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -426,6 +426,10 @@ main(int argc, char *argv[]) while (write(pipes[1], &proto, sizeof(proto)) < 0 && errno == EINTR) ; /* remove gcc warning */ + proto.action = ErtsSysForkerProtoAction_StartAck; + while (write(uds_fd, &proto, sizeof(proto)) < 0 && errno == EINTR) + ; /* remove gcc warning */ + sys_sigrelease(SIGCHLD); close(pipes[0]); close(pipes[1]); diff --git a/erts/emulator/sys/unix/erl_child_setup.h b/erts/emulator/sys/unix/erl_child_setup.h index c3258f7cbe..93b39d46b2 100644 --- a/erts/emulator/sys/unix/erl_child_setup.h +++ b/erts/emulator/sys/unix/erl_child_setup.h @@ -45,6 +45,7 @@ typedef unsigned long long ErtsSysPortId; typedef struct ErtsSysForkerProto_ { enum { ErtsSysForkerProtoAction_Start, + ErtsSysForkerProtoAction_StartAck, ErtsSysForkerProtoAction_Go, ErtsSysForkerProtoAction_SigChld, ErtsSysForkerProtoAction_Ack diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 82096e5779..dc1c00ff96 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -1761,37 +1761,59 @@ static void forker_ready_input(ErlDrvData e, ErlDrvEvent fd) erl_exit(ERTS_DUMP_EXIT, "erl_child_setup closed\n"); ASSERT(res == sizeof(*proto)); - ASSERT(proto->action == ErtsSysForkerProtoAction_SigChld); - - /* ideally this would be a port_command call, but as command is - already used by the spawn_driver, we use control instead. - Note that when using erl_drv_port_control it is an asynchronous - control. */ - erl_drv_port_control(proto->u.sigchld.port_id, 'S', - (char*)proto, sizeof(*proto)); - -} -static void forker_ready_output(ErlDrvData e, ErlDrvEvent fd) -{ - ErlDrvPort port_num = (ErlDrvPort)e; - - while(driver_sizeq(port_num) > 0) { + if (proto->action == ErtsSysForkerProtoAction_StartAck) { + /* Ideally we would like to not have to ack each Start + command being sent over the uds, but it would seem + that some operating systems (only observed on FreeBSD) + throw away data on the uds when the socket becomes full, + so we have to. + + Also the freebsd manual explicitly states that + you should not close fds before they are known + to have reached the other side, so this Ack protects + against that as well. + */ + ErlDrvPort port_num = (ErlDrvPort)e; int vlen; SysIOVec *iov = driver_peekq(port_num, &vlen); ErtsSysForkerProto *proto = (ErtsSysForkerProto *)iov[0].iov_base; - ASSERT(iov[0].iov_len >= (sizeof(*proto))); - if (sys_uds_write(forker_fd, (char*)proto, sizeof(*proto), - proto->u.start.fds, 3, 0) < 0) { - if (errno == ERRNO_BLOCK) - return; - erl_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno); - } + close(proto->u.start.fds[0]); close(proto->u.start.fds[1]); if (proto->u.start.fds[1] != proto->u.start.fds[2]) close(proto->u.start.fds[2]); + driver_deq(port_num, sizeof(*proto)); + + if (driver_sizeq(port_num) > 0) + driver_select(port_num, forker_fd, ERL_DRV_WRITE|ERL_DRV_USE, 1); + } else { + ASSERT(proto->action == ErtsSysForkerProtoAction_SigChld); + + /* ideally this would be a port_command call, but as command is + already used by the spawn_driver, we use control instead. + Note that when using erl_drv_port_control it is an asynchronous + control. */ + erl_drv_port_control(proto->u.sigchld.port_id, 'S', + (char*)proto, sizeof(*proto)); + } + +} + +static void forker_ready_output(ErlDrvData e, ErlDrvEvent fd) +{ + ErlDrvPort port_num = (ErlDrvPort)e; + + int vlen; + SysIOVec *iov = driver_peekq(port_num, &vlen); + ErtsSysForkerProto *proto = (ErtsSysForkerProto *)iov[0].iov_base; + ASSERT(iov[0].iov_len >= (sizeof(*proto))); + if (sys_uds_write(forker_fd, (char*)proto, sizeof(*proto), + proto->u.start.fds, 3, 0) < 0) { + if (errno == ERRNO_BLOCK) + return; + erl_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup uds: %d", errno); } driver_select(port_num, forker_fd, ERL_DRV_WRITE, 0); @@ -1804,25 +1826,19 @@ static ErlDrvSSizeT forker_control(ErlDrvData e, unsigned int cmd, char *buf, ErlDrvPort port_num = (ErlDrvPort)e; int res; - if (driver_sizeq(port_num) > 0) { - driver_enq(port_num, buf, len); + driver_enq(port_num, buf, len); + if (driver_sizeq(port_num) > sizeof(*proto)) { return 0; } if ((res = sys_uds_write(forker_fd, (char*)proto, sizeof(*proto), proto->u.start.fds, 3, 0)) < 0) { if (errno == ERRNO_BLOCK) { - driver_enq(port_num, buf, len); driver_select(port_num, forker_fd, ERL_DRV_WRITE|ERL_DRV_USE, 1); return 0; } erl_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno); } - ASSERT(res == sizeof(*proto)); - close(proto->u.start.fds[0]); - close(proto->u.start.fds[1]); - if (proto->u.start.fds[1] != proto->u.start.fds[2]) - close(proto->u.start.fds[2]); return 0; } -- cgit v1.2.3 From b96e62d346eea60b2300dce22d8d892387de4681 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 14 Oct 2015 09:17:58 +0200 Subject: erts: It is not possible to exit the forker driver --- erts/emulator/beam/io.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index df8a213a1d..8d9199162c 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -2408,6 +2408,11 @@ erts_port_exit(Process *c_p, | ERTS_PORT_SIG_FLG_BROKEN_LINK | ERTS_PORT_SIG_FLG_FORCE_SCHED)) == 0); +#ifndef __WIN32__ + if (prt->drv_ptr == &forker_driver) + return ERTS_PORT_OP_DROPPED; +#endif + if (!(flags & ERTS_PORT_SIG_FLG_FORCE_SCHED)) { ErtsTryImmDrvCallState try_call_state = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(c_p, -- cgit v1.2.3 From 6fc7ccb41da5e9ec4357b40811ad740dd6a3b5b2 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 14 Oct 2015 09:56:03 +0200 Subject: erts: Only use forker StackAck on freebsd --- erts/emulator/sys/unix/erl_child_setup.c | 2 ++ erts/emulator/sys/unix/erl_child_setup.h | 8 ++++++ erts/emulator/sys/unix/sys_drivers.c | 48 ++++++++++++++++++++++---------- 3 files changed, 43 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index fdffc7f119..998a0d297f 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -426,9 +426,11 @@ main(int argc, char *argv[]) while (write(pipes[1], &proto, sizeof(proto)) < 0 && errno == EINTR) ; /* remove gcc warning */ +#ifdef FORKER_PROTO_START_ACK proto.action = ErtsSysForkerProtoAction_StartAck; while (write(uds_fd, &proto, sizeof(proto)) < 0 && errno == EINTR) ; /* remove gcc warning */ +#endif sys_sigrelease(SIGCHLD); close(pipes[0]); diff --git a/erts/emulator/sys/unix/erl_child_setup.h b/erts/emulator/sys/unix/erl_child_setup.h index 93b39d46b2..a28b136bfc 100644 --- a/erts/emulator/sys/unix/erl_child_setup.h +++ b/erts/emulator/sys/unix/erl_child_setup.h @@ -25,6 +25,14 @@ #include "sys.h" +#ifdef __FreeBSD__ +/* The freebsd sendmsg man page explicitly states that + you should not close fds before they are known + to have reached the other side, so this Ack protects + against that. */ +#define FORKER_PROTO_START_ACK 1 +#endif + #define FORKER_ARGV_NO_OF_ARGS 3 #define FORKER_ARGV_PROGNAME_IX 0 /* Program name */ #define FORKER_ARGV_MAX_FILES 1 /* max_files */ diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index dc1c00ff96..2a7cd91265 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -1762,17 +1762,13 @@ static void forker_ready_input(ErlDrvData e, ErlDrvEvent fd) ASSERT(res == sizeof(*proto)); +#ifdef FORKER_PROTO_START_ACK if (proto->action == ErtsSysForkerProtoAction_StartAck) { /* Ideally we would like to not have to ack each Start command being sent over the uds, but it would seem that some operating systems (only observed on FreeBSD) throw away data on the uds when the socket becomes full, so we have to. - - Also the freebsd manual explicitly states that - you should not close fds before they are known - to have reached the other side, so this Ack protects - against that as well. */ ErlDrvPort port_num = (ErlDrvPort)e; int vlen; @@ -1788,7 +1784,9 @@ static void forker_ready_input(ErlDrvData e, ErlDrvEvent fd) if (driver_sizeq(port_num) > 0) driver_select(port_num, forker_fd, ERL_DRV_WRITE|ERL_DRV_USE, 1); - } else { + } else +#endif + { ASSERT(proto->action == ErtsSysForkerProtoAction_SigChld); /* ideally this would be a port_command call, but as command is @@ -1805,16 +1803,27 @@ static void forker_ready_output(ErlDrvData e, ErlDrvEvent fd) { ErlDrvPort port_num = (ErlDrvPort)e; - int vlen; - SysIOVec *iov = driver_peekq(port_num, &vlen); - ErtsSysForkerProto *proto = (ErtsSysForkerProto *)iov[0].iov_base; - ASSERT(iov[0].iov_len >= (sizeof(*proto))); - if (sys_uds_write(forker_fd, (char*)proto, sizeof(*proto), - proto->u.start.fds, 3, 0) < 0) { - if (errno == ERRNO_BLOCK) - return; - erl_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup uds: %d", errno); +#ifndef FORKER_PROTO_START_ACK + while (driver_sizeq(port_num) > 0) { +#endif + int vlen; + SysIOVec *iov = driver_peekq(port_num, &vlen); + ErtsSysForkerProto *proto = (ErtsSysForkerProto *)iov[0].iov_base; + ASSERT(iov[0].iov_len >= (sizeof(*proto))); + if (sys_uds_write(forker_fd, (char*)proto, sizeof(*proto), + proto->u.start.fds, 3, 0) < 0) { + if (errno == ERRNO_BLOCK) + return; + erl_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno); + } +#ifndef FORKER_PROTO_START_ACK + close(proto->u.start.fds[0]); + close(proto->u.start.fds[1]); + if (proto->u.start.fds[1] != proto->u.start.fds[2]) + close(proto->u.start.fds[2]); + driver_deq(port_num, sizeof(*proto)); } +#endif driver_select(port_num, forker_fd, ERL_DRV_WRITE, 0); } @@ -1840,5 +1849,14 @@ static ErlDrvSSizeT forker_control(ErlDrvData e, unsigned int cmd, char *buf, erl_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno); } +#ifndef FORKER_PROTO_START_ACK + ASSERT(res == sizeof(*proto)); + close(proto->u.start.fds[0]); + close(proto->u.start.fds[1]); + if (proto->u.start.fds[1] != proto->u.start.fds[2]) + close(proto->u.start.fds[2]); + driver_deq(port_num, sizeof(*proto)); +#endif + return 0; } -- cgit v1.2.3 From 638476d255cf6f9e5cd880eb9318d5b08664e5f8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 15 Oct 2015 11:53:24 +0200 Subject: erts: Allow one dangling fd if there is a gethost port --- erts/emulator/test/driver_SUITE.erl | 26 +++++++++++++++++++++++-- erts/emulator/test/z_SUITE.erl | 38 ++++++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index b72d6cbe52..e17d0ff96c 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -2395,13 +2395,35 @@ z_test(Config) when is_list(Config) -> check_io_debug() -> get_stable_check_io_info(), - {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs} + {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs} = CheckIoDebug = erts_debug:get_internal_state(check_io_debug), + HasGetHost = has_gethost(), + ct:log("check_io_debug: ~p~n" + "HasGetHost: ~p",[CheckIoDebug, HasGetHost]), 0 = NoErrorFds, - NoUsedFds = NoDrvSelStructs, + if + NoUsedFds == NoDrvSelStructs -> + ok; + HasGetHost andalso (NoUsedFds == (NoDrvSelStructs - 1)) -> + %% If the inet_gethost port is alive, we may have + %% one extra used fd that is not selected on + ok + end, 0 = NoDrvEvStructs, ok. +has_gethost() -> + has_gethost(erlang:ports()). +has_gethost([P|T]) -> + case erlang:port_info(P, name) of + {name,"inet_gethost"++_} -> + true; + _ -> + has_gethost(T) + end; +has_gethost([]) -> + false. + %flush_msgs() -> % receive % M -> diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index f4d9030255..abc353fb01 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -249,6 +249,7 @@ pollset_size(Config) when is_list(Config) -> ?line io:format("Initial: ~p~nFinal: ~p~n", [InitChkIo, FinChkIo]), ?line InitPollsetSize = lists:keysearch(total_poll_set_size, 1, InitChkIo), ?line FinPollsetSize = lists:keysearch(total_poll_set_size, 1, FinChkIo), + HasGethost = case has_gethost() of true -> 1; _ -> 0 end, ?line case InitPollsetSize =:= FinPollsetSize of true -> case InitPollsetSize of @@ -269,7 +270,7 @@ pollset_size(Config) when is_list(Config) -> = InitPollsetSize, ?line {value, {total_poll_set_size, FinSize}} = FinPollsetSize, - ?line true = FinSize < InitSize, + ?line true = FinSize < (InitSize + HasGethost), ?line true = 2 =< FinSize, ?line {comment, "Start pollset size: " @@ -289,16 +290,39 @@ check_io_debug(Config) when is_list(Config) -> end. check_io_debug_test() -> - ?line erlang:display(get_check_io_info()), - ?line erts_debug:set_internal_state(available_internal_state, true), - ?line {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs} + erlang:display(get_check_io_info()), + erts_debug:set_internal_state(available_internal_state, true), + {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs} = CheckIoDebug = erts_debug:get_internal_state(check_io_debug), - ?line erts_debug:set_internal_state(available_internal_state, false), - ?line 0 = NoErrorFds, - ?line NoUsedFds = NoDrvSelStructs, + erts_debug:set_internal_state(available_internal_state, false), + HasGetHost = has_gethost(), + ct:log("check_io_debug: ~p~n" + "HasGetHost: ~p",[CheckIoDebug, HasGetHost]), + 0 = NoErrorFds, + if + NoUsedFds == NoDrvSelStructs -> + ok; + HasGetHost andalso (NoUsedFds == (NoDrvSelStructs - 1)) -> + %% If the inet_gethost port is alive, we may have + %% one extra used fd that is not selected on. + %% This happens when the initial setup of the + %% port returns an EAGAIN + ok + end, ?line 0 = NoDrvEvStructs, ?line ok. +has_gethost() -> + has_gethost(erlang:ports()). +has_gethost([P|T]) -> + case erlang:port_info(P, name) of + {name,"inet_gethost"++_} -> + true; + _ -> + has_gethost(T) + end; +has_gethost([]) -> + false. %% -- cgit v1.2.3 From 99415cedd98151ca74715b6cadf90f6dd8493025 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 19 Oct 2015 17:29:48 +0200 Subject: erts: iter_port sleep longer on freebsd --- erts/emulator/test/port_SUITE.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 6cc86a26a3..19065fe811 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -719,7 +719,16 @@ close_ports([]) -> ok. open_ports(Name, Settings) -> - test_server:sleep(5), + case os:type() of + {unix, freebsd} -> + %% FreeBsd has issues with sendmsg/recvmsg in fork + %% implementation and we therefor have to spawn + %% slower to make sure that we always hit the same + %% make roof. + test_server:sleep(10); + _ -> + test_server:sleep(5) + end, case catch open_port(Name, Settings) of P when is_port(P) -> [P| open_ports(Name, Settings)]; -- cgit v1.2.3 From 70c4711252e260e4ad4c43625226b21ecf775a75 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 20 Oct 2015 18:19:17 +0200 Subject: erts: Allow enomem failures in port_SUITE With the new forker implementation also enomem can be returned as an indicator that it is not possible to create more ports. --- erts/emulator/test/port_SUITE.erl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 19065fe811..6f29e3ad95 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1310,13 +1310,15 @@ otp_4389(Config) when is_list(Config) -> {P,{exit_status,_}} -> TCR ! {self(),ok}; {'EXIT',_,{R2,_}} when R2 == emfile; - R2 == eagain -> + R2 == eagain; + R2 == enomem -> TCR ! {self(),ok}; Err2 -> TCR ! {self(),{msg,Err2}} end; {'EXIT',{R1,_}} when R1 == emfile; - R1 == eagain -> + R1 == eagain; + R1 == enomem -> TCR ! {self(),ok}; Err1 -> TCR ! {self(), {open_port,Err1}} @@ -1922,10 +1924,12 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> {Prt, erlang:system_info(scheduler_id)}; {'EXIT', {Err, _}} when Err == eagain; - Err == emfile -> + Err == emfile; + Err == enomem -> noop; {'EXIT', Err} when Err == eagain; - Err == emfile -> + Err == emfile; + Err == enomem -> noop; Error -> ?t:fail(Error) -- cgit v1.2.3 From 10ffa0b2f8c66b3a318503b4dfdfe02d6577d746 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 30 Nov 2015 12:00:44 +0100 Subject: erts: Mend ASSERT makro for erl_child_setup when called by hash.c for example. We use ASSERT from sys.h but erl_child_setup implements its own erl_assert_error() as it doesn't link with sys.o. --- erts/emulator/sys/unix/erl_child_setup.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 998a0d297f..94d1fd64c7 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -83,11 +83,16 @@ abort(); \ } while(0) -#undef ASSERT #ifdef DEBUG -#define ASSERT(cnd) do { if (!(cnd)) { ABORT("assertion %s failed", #cnd); } } while(0) -#else -#define ASSERT(cnd) +void +erl_assert_error(const char* expr, const char* func, const char* file, int line) +{ + fflush(stdout); + fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", + file, line, func, expr); + fflush(stderr); + abort(); +} #endif void sys_sigblock(int sig) -- cgit v1.2.3 From 4b1b3bf6c62f8208b2eea506c9dac1504df6e916 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 8 Dec 2015 12:17:22 +0100 Subject: erts: Never abort in the forked child We always want the error to propagate up to the application when a child cannot be created. --- erts/emulator/sys/unix/erl_child_setup.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 94d1fd64c7..4e61530cf1 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -136,7 +136,7 @@ start_new_child(int pipes[]) } while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK)); if (res <= 0) { - ABORT("Failed to read size from %d (%d)", pipes[0], errno); + goto child_error; } buff = malloc(size); @@ -147,8 +147,7 @@ start_new_child(int pipes[]) if ((res = read(pipes[0], buff + pos, size - pos)) < 0) { if (errno == ERRNO_BLOCK || errno == EINTR) continue; - ABORT("Failed to read %d bytes from %d (%d,%d)", - size, pipes[0], res, errno); + goto child_error; } if (res == 0) { errno = EPIPE; @@ -200,7 +199,8 @@ start_new_child(int pipes[]) } if (o_buff + size != buff) { - ABORT("Buff error: %p, %p:%p", o_buff, o_buff+size, buff); + errno = EINVAL; + goto child_error; } DEBUG_PRINT("read ack"); -- cgit v1.2.3 From 1cf9bed55624fefabcb8c517f9e496a92883117a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 15 Dec 2015 10:22:25 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erlang.beam | Bin 101752 -> 34396 bytes erts/preloaded/ebin/erts_internal.beam | Bin 6076 -> 2032 bytes 2 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 632defdb46..6610800458 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index fd0a502d2c..fcc8c85e52 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ -- cgit v1.2.3 From 8e6d005dcd8f62745d28d1820c4b2a5025634f5f Mon Sep 17 00:00:00 2001 From: Alexey Lebedeff Date: Mon, 14 Dec 2015 15:15:52 +0300 Subject: Prevent down nodes going undetected in epmd In the following (rare) case down node will be always registered in epmd: - client connects to epmd and sends ALIVE2 request - epmd reads this request and starts to process it - during that time client socket closes in such way that subsequent write(2) in epmd will result in error - at this point we have node that was registered in database, but as the connection struct has no 'keep' flag set, the do_read() closes connection and removes it from select fdset - and so there is no way for this node to be cleaned up later. We've seen several epmd instances in such state on our production systems. And while I'm not sure what was the exact sequence of events that leads to failed write(2), issue could be easily reproduced using SO_LINGER option for socket. --- erts/epmd/src/epmd_srv.c | 1 + erts/epmd/test/epmd_SUITE.erl | 58 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 8c8d7304f2..c9d49e73d0 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -705,6 +705,7 @@ static void do_request(g, fd, s, buf, bsize) if (reply(g, fd, wbuf, 4) != 4) { + node_unreg(g, name); dbg_tty_printf(g,1,"** failed to send ALIVE2_RESP for \"%s\"", name); return; diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index e8bbfdbb18..4de65500e9 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -76,7 +76,9 @@ buffer_overrun_2/1, no_nonlocal_register/1, no_nonlocal_kill/1, - no_live_killing/1 + no_live_killing/1, + + socket_reset_before_alive2_reply_is_written/1 ]). @@ -123,7 +125,8 @@ all() -> returns_valid_populated_extra_with_nulls, names_stdout, {group, buffer_overrun}, no_nonlocal_register, - no_nonlocal_kill, no_live_killing]. + no_nonlocal_kill, no_live_killing, + socket_reset_before_alive2_reply_is_written]. groups() -> [{buffer_overrun, [], @@ -243,11 +246,7 @@ register_node(Name,Port) -> register_node_v2(Port,$M,0,5,5,Name,""). register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) -> - Utf8Name = unicode:characters_to_binary(Name), - Req = [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot, - put16(HVsn), put16(LVsn), - put16(size(Utf8Name)), binary_to_list(Utf8Name), - size16(Extra), Extra], + Req = alive2_req(Port, NodeType, Prot, HVsn, LVsn, Name, Extra), case send_req(Req) of {ok,Sock} -> case recv(Sock,4) of @@ -938,6 +937,42 @@ no_live_killing(Config) when is_list(Config) -> ?line close(Sock3), ok. +socket_reset_before_alive2_reply_is_written(doc) -> + ["Check for regression - don't make zombie from node which " + "sends TCP RST at wrong time"]; +socket_reset_before_alive2_reply_is_written(suite) -> + []; +socket_reset_before_alive2_reply_is_written(Config) when is_list(Config) -> + %% - delay_write for easier triggering of race condition + %% - relaxed_command_check for gracefull shutdown of epmd even if there + %% is stuck node. + ?line ok = epmdrun("-delay_write 1 -relaxed_command_check"), + + %% We can't use send_req/1 directly as we want to do inet:setopts/2 + %% on our socket. + ?line {ok, Sock} = connect(), + + %% Issuing close/1 on such socket will result in immediate RST packet. + ?line ok = inet:setopts(Sock, [{linger, {true, 0}}]), + + Req = alive2_req(4711, 77, 0, 5, 5, "test", []), + ?line ok = send(Sock, [size16(Req), Req]), + + timer:sleep(500), %% Wait for the first 1/2 of delay_write before closing + ?line ok = close(Sock), + + timer:sleep(500 + ?SHORT_PAUSE), %% Wait for the other 1/2 of delay_write + + %% Wait another delay_write interval, due to delay doubling in epmd. + %% Should be removed when this is issue is fixed there. + timer:sleep(1000), + + ?line {ok, SockForNames} = connect_active(), + + %% And there should be no stuck nodes + ?line {ok, []} = do_get_names(SockForNames), + ?line ok = close(SockForNames), + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Terminate all tests with killing epmd. @@ -1200,3 +1235,12 @@ flat_count([_|T], N) -> flat_count(T, N); flat_count([], N) -> N. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +alive2_req(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) -> + Utf8Name = unicode:characters_to_binary(Name), + [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot, + put16(HVsn), put16(LVsn), + put16(size(Utf8Name)), binary_to_list(Utf8Name), + size16(Extra), Extra]. -- cgit v1.2.3 From 5801defc866e34c6effd49b9dec995b5dac164f3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 10 Dec 2015 16:21:59 +0100 Subject: erts: Refactor proc dict with 'usedSlots' which is same as old homeSize + splitPosition. --- erts/emulator/beam/erl_process_dict.c | 50 ++++++++++++++++++++--------------- erts/emulator/beam/erl_process_dict.h | 4 +-- 2 files changed, 31 insertions(+), 23 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 97a2828b4e..b3d7627f08 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -51,7 +51,7 @@ #define INITIAL_SIZE (erts_pd_initial_size) /* Hash utility macros */ -#define HASH_RANGE(PDict) ((PDict)->homeSize + (PDict)->splitPosition) +#define HASH_RANGE(PDict) ((PDict)->usedSlots) #define MAKE_HASH(Term) \ ((is_small(Term)) ? unsigned_val(Term) : \ @@ -164,9 +164,9 @@ erts_dictionary_dump(int to, void *to_arg, ProcDict *pd) /*PD_CHECK(pd);*/ if (pd == NULL) return; - erts_print(to, to_arg, "(size = %d, homeSize = %d, " + erts_print(to, to_arg, "(size = %d, usedSlots = %d, " "splitPosition = %d, numElements = %d)\n", - pd->size, pd->homeSize, + pd->arraySize, pd->usedSlots, pd->splitPosition, (unsigned int) pd->numElements); for (i = 0; i < HASH_RANGE(pd); ++i) { erts_print(to, to_arg, "%d: %T\n", i, ARRAY_GET(pd, i)); @@ -584,8 +584,8 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) if (p->dictionary == NULL) { /* Create it */ ensure_array_size(&p->dictionary, INITIAL_SIZE); - p->dictionary->homeSize = INITIAL_SIZE; - p->dictionary->sizeMask = p->dictionary->homeSize*2 - 1; + p->dictionary->usedSlots = INITIAL_SIZE; + p->dictionary->sizeMask = INITIAL_SIZE*2 - 1; p->dictionary->splitPosition = 0; p->dictionary->numElements = 0; } @@ -737,12 +737,13 @@ static void shrink(Process *p, Eterm* ret) for (i = 0; i < steps; ++i) { if (pd->splitPosition == 0) { - pd->sizeMask = pd->homeSize - 1; - pd->homeSize /= 2; - pd->splitPosition = pd->homeSize; + ASSERT(IS_POW2(pd->usedSlots)); + pd->sizeMask = pd->usedSlots - 1; + pd->splitPosition = pd->usedSlots / 2; } --(pd->splitPosition); - hi = ARRAY_GET(pd, (pd->splitPosition + pd->homeSize)); + /* Must wait to decrement 'usedSlots' for GC rootset below */ + hi = ARRAY_GET(pd, pd->usedSlots - 1); lo = ARRAY_GET(pd, pd->splitPosition); if (hi != NIL) { if (lo == NIL) { @@ -754,7 +755,7 @@ static void shrink(Process *p, Eterm* ret) } if (HeapWordsLeft(p) < needed) { BUMP_REDS(p, erts_garbage_collect(p, needed, ret, 1)); - hi = pd->data[(pd->splitPosition + pd->homeSize)]; + hi = pd->data[pd->usedSlots - 1]; lo = pd->data[pd->splitPosition]; } #ifdef DEBUG @@ -796,7 +797,8 @@ static void shrink(Process *p, Eterm* ret) } } } - ARRAY_PUT(pd, (pd->splitPosition + pd->homeSize), NIL); + --pd->usedSlots; + ARRAY_PUT(pd, pd->usedSlots, NIL); } if (HASH_RANGE(p->dictionary) <= (p->dictionary->size / 4)) { array_shrink(&(p->dictionary), (HASH_RANGE(p->dictionary) * 3) / 2); @@ -806,7 +808,7 @@ static void shrink(Process *p, Eterm* ret) static void grow(Process *p) { unsigned int i,j; - unsigned int steps = p->dictionary->homeSize / 5; + unsigned int steps = (p->dictionary->usedSlots / 4) & 0xf; Eterm l1,l2; Eterm l; Eterm *hp; @@ -835,7 +837,7 @@ static void grow(Process *p) */ pos = pd->splitPosition; - homeSize = pd->homeSize; + homeSize = pd->usedSlots - pd->splitPosition; for (i = 0; i < steps; ++i) { if (pos == homeSize) { homeSize *= 2; @@ -860,19 +862,21 @@ static void grow(Process *p) /* * Now grow. */ - + homeSize = pd->usedSlots - pd->splitPosition; for (i = 0; i < steps; ++i) { - if (pd->splitPosition == pd->homeSize) { - pd->homeSize *= 2; - pd->sizeMask = pd->homeSize*2 - 1; + if (pd->splitPosition == homeSize) { + homeSize *= 2; + pd->sizeMask = homeSize*2 - 1; pd->splitPosition = 0; } pos = pd->splitPosition; ++pd->splitPosition; /* For the hashes */ + ++pd->usedSlots; + ASSERT(pos + homeSize == pd->usedSlots - 1); l = ARRAY_GET(pd, pos); if (is_tuple(l)) { if (pd_hash_value(pd, tuple_val(l)[1]) != pos) { - ARRAY_PUT(pd, pos + pd->homeSize, l); + ARRAY_PUT(pd, pos + homeSize, l); ARRAY_PUT(pd, pos, NIL); } } else { @@ -896,7 +900,7 @@ static void grow(Process *p) l2 = TCAR(l2); ASSERT(hp <= hp_limit); ARRAY_PUT(pd, pos, l1); - ARRAY_PUT(pd, pos + pd->homeSize, l2); + ARRAY_PUT(pd, pos + homeSize, l2); } } @@ -961,7 +965,9 @@ static unsigned int pd_hash_value_to_ix(ProcDict *pdict, Uint32 hx) { Uint high; - ASSERT(IS_POW2(pdict->homeSize)); + ASSERT(IS_POW2(pdict->sizeMask+1)); + ASSERT(HASH_RANGE(pdict) >= (pdict->sizeMask >> 1)); + ASSERT(HASH_RANGE(pdict) <= (pdict->sizeMask + 1)); high = hx & pdict->sizeMask; if (high >= HASH_RANGE(pdict)) @@ -1043,12 +1049,14 @@ static void pd_check(ProcDict *pd) } else if (is_tuple(t)) { ++num; ASSERT(arityval(*tuple_val(t)) == 2); + ASSERT(pd_hash_value(pd, tuple_val(t)[1]) == i); continue; } else if (is_list(t)) { while (t != NIL) { ++num; ASSERT(is_tuple(TCAR(t))); ASSERT(arityval(*(tuple_val(TCAR(t)))) == 2); + ASSERT(pd_hash_value(pd, tuple_val(TCAR(t))[1]) == i); t = TCDR(t); } continue; @@ -1059,7 +1067,7 @@ static void pd_check(ProcDict *pd) } } ASSERT(num == pd->numElements); - ASSERT(pd->splitPosition <= pd->homeSize); + ASSERT(pd->usedSlots >= pd->splitPosition*2); } #endif /* DEBUG */ diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h index fd59c969cf..f0d57da1e6 100644 --- a/erts/emulator/beam/erl_process_dict.h +++ b/erts/emulator/beam/erl_process_dict.h @@ -25,14 +25,14 @@ typedef struct proc_dict { unsigned int sizeMask; unsigned int size; - unsigned int homeSize; + unsigned int usedSlots; unsigned int splitPosition; Uint numElements; Eterm data[1]; /* The beginning of an array of erlang terms */ } ProcDict; #define ERTS_PD_START(PD) ((PD)->data) -#define ERTS_PD_SIZE(PD) ((PD)->homeSize + (PD)->splitPosition) +#define ERTS_PD_SIZE(PD) ((PD)->usedSlots) int erts_pd_set_initial_size(int size); Uint erts_dicts_mem_size(struct process *p); -- cgit v1.2.3 From 0f32d250876e7bb226fb96e07fb31734ba7d16f2 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 10 Dec 2015 17:47:37 +0100 Subject: erts: Rename proc dict size to arraySize for naming style consistency. --- erts/emulator/beam/erl_process_dict.c | 26 +++++++++++++------------- erts/emulator/beam/erl_process_dict.h | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index b3d7627f08..e384f4c3f6 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -77,9 +77,9 @@ #define TCDR(Term) CDR(list_val(Term)) /* Array access macro */ -#define ARRAY_GET(PDict, Index) (ASSERT((Index) < (PDict)->size), \ +#define ARRAY_GET(PDict, Index) (ASSERT((Index) < (PDict)->arraySize), \ (PDict)->data[Index]) -#define ARRAY_PUT(PDict, Index, Val) (ASSERT((Index) < (PDict)->size), \ +#define ARRAY_PUT(PDict, Index, Val) (ASSERT((Index) < (PDict)->arraySize), \ (PDict)->data[Index] = (Val)) #define IS_POW2(X) ((X) && !((X) & ((X)-1))) @@ -221,7 +221,7 @@ erts_dicts_mem_size(Process *p) { Uint size = 0; if (p->dictionary) - size += PD_SZ2BYTES(p->dictionary->size); + size += PD_SZ2BYTES(p->dictionary->arraySize); return size; } @@ -800,7 +800,7 @@ static void shrink(Process *p, Eterm* ret) --pd->usedSlots; ARRAY_PUT(pd, pd->usedSlots, NIL); } - if (HASH_RANGE(p->dictionary) <= (p->dictionary->size / 4)) { + if (HASH_RANGE(p->dictionary) <= (p->dictionary->arraySize / 4)) { array_shrink(&(p->dictionary), (HASH_RANGE(p->dictionary) * 3) / 2); } } @@ -918,16 +918,16 @@ static void array_shrink(ProcDict **ppd, unsigned int need) unsigned int siz = next_array_size(need); HDEBUGF(("array_shrink: size = %d, need = %d", - (*ppd)->size, need)); + (*ppd)->arraySize, need)); - if (siz >= (*ppd)->size) + if (siz >= (*ppd)->arraySize) return; /* Only shrink */ *ppd = PD_REALLOC(((void *) *ppd), - PD_SZ2BYTES((*ppd)->size), + PD_SZ2BYTES((*ppd)->arraySize), PD_SZ2BYTES(siz)); - (*ppd)->size = siz; + (*ppd)->arraySize = siz; } @@ -942,17 +942,17 @@ static void ensure_array_size(ProcDict **ppdict, unsigned int size) pd = PD_ALLOC(PD_SZ2BYTES(siz)); for (i = 0; i < siz; ++i) pd->data[i] = NIL; - pd->size = siz; + pd->arraySize = siz; *ppdict = pd; - } else if (size > pd->size) { - Uint osize = pd->size; + } else if (size > pd->arraySize) { + Uint osize = pd->arraySize; Uint nsize = next_array_size(size); pd = PD_REALLOC(((void *) pd), PD_SZ2BYTES(osize), PD_SZ2BYTES(nsize)); for (i = osize; i < nsize; ++i) pd->data[i] = NIL; - pd->size = nsize; + pd->arraySize = nsize; *ppdict = pd; } } @@ -1040,7 +1040,7 @@ static void pd_check(ProcDict *pd) if (pd == NULL) return; used = HASH_RANGE(pd); - ASSERT(pd->size >= used); + ASSERT(pd->arraySize >= used); ASSERT(HASH_RANGE(pd) <= MAX_HASH); for (i = 0, num = 0; i < used; ++i) { Eterm t = pd->data[i]; diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h index f0d57da1e6..dac214c8a1 100644 --- a/erts/emulator/beam/erl_process_dict.h +++ b/erts/emulator/beam/erl_process_dict.h @@ -24,8 +24,8 @@ typedef struct proc_dict { unsigned int sizeMask; - unsigned int size; unsigned int usedSlots; + unsigned int arraySize; unsigned int splitPosition; Uint numElements; Eterm data[1]; /* The beginning of an array of erlang terms */ -- cgit v1.2.3 From 46a1a3b8c8819a117d7f48a864d8e0f5e08ac548 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 14 Dec 2015 20:02:54 +0100 Subject: erts: Add 'fill_heap' to erts_debug:state_internal_state to make it easy to provoke GC inside/after a BIF or instruction. --- erts/emulator/beam/erl_bif_info.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b44382cde8..caa1cb7608 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -4085,6 +4085,17 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) BIF_RET(am_ok); } } + else if (ERTS_IS_ATOM_STR("fill_heap", BIF_ARG_1)) { + UWord left = HeapWordsLeft(BIF_P); + if (left > 1) { + Eterm* hp = HAlloc(BIF_P, left); + *hp = make_pos_bignum_header(left - 1); + } + if (BIF_ARG_2 == am_true) { + FLAGS(BIF_P) |= F_NEED_FULLSWEEP; + } + BIF_RET(am_ok); + } } BIF_ERROR(BIF_P, BADARG); -- cgit v1.2.3 From 1a101efd5654d3d4a3e96523fbbf4eb871e61d1b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 15 Dec 2015 18:08:41 +0100 Subject: erts: Fix bug in check_process_code for literals --- erts/emulator/beam/beam_bif_load.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index c925a8c812..956454d9b3 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -826,6 +826,9 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp); erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ); + literals = (char*) modp->old.code_hdr->literals_start; + lit_bsize = (char*) modp->old.code_hdr->literals_end - literals; + for (msgp = rp->msg.first; msgp; msgp = msgp->next) { if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) hfrag = &msgp->hfrag; @@ -839,14 +842,11 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) /* Should not contain any constants... */ ASSERT(!any_heap_refs(&hfrag->mem[0], &hfrag->mem[hfrag->used_size], - mod_start, - mod_size)); + literals, + lit_bsize)); } } - literals = (char*) modp->old.code_hdr->literals_start; - lit_bsize = (char*) modp->old.code_hdr->literals_end - literals; - while (1) { /* Check heap, stack etc... */ @@ -881,7 +881,7 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) hp = &hfrag->mem[0]; hp_end = &hfrag->mem[hfrag->used_size]; - if (any_heap_refs(hp, hp_end, mod_start, lit_bsize)) + if (any_heap_refs(hp, hp_end, literals, lit_bsize)) goto try_literal_gc; } @@ -902,7 +902,7 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) hp = &hfrag->mem[0]; hp_end = &hfrag->mem[hfrag->used_size]; - ASSERT(!any_heap_refs(hp, hp_end, mod_start, lit_bsize)); + ASSERT(!any_heap_refs(hp, hp_end, literals, lit_bsize)); } } -- cgit v1.2.3 From 6b60ed6d30dccaeab1207178d5e786aaa14b3201 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 16 Dec 2015 11:14:40 +0100 Subject: Fix offset_mqueue --- erts/emulator/beam/erl_gc.c | 48 ++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index b63567e563..e182782323 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2773,33 +2773,37 @@ offset_mqueue(Process *p, Sint offs, char* area, Uint area_size) { ErtsMessage* mp = p->msg.first; - while (mp != NULL) { - Eterm mesg = ERL_MESSAGE_TERM(mp); - if (is_value(mesg)) { - switch (primary_tag(mesg)) { - case TAG_PRIMARY_LIST: - case TAG_PRIMARY_BOXED: - if (ErtsInArea(ptr_val(mesg), area, area_size)) { - ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs); + if ((p->flags & (F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG)) != F_OFF_HEAP_MSGQ) { + + while (mp != NULL) { + Eterm mesg = ERL_MESSAGE_TERM(mp); + if (is_value(mesg)) { + switch (primary_tag(mesg)) { + case TAG_PRIMARY_LIST: + case TAG_PRIMARY_BOXED: + if (ErtsInArea(ptr_val(mesg), area, area_size)) { + ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs); + } + break; } - break; } - } - mesg = ERL_MESSAGE_TOKEN(mp); - if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) { - ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs); - } + mesg = ERL_MESSAGE_TOKEN(mp); + if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) { + ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs); + } #ifdef USE_VM_PROBES - mesg = ERL_MESSAGE_DT_UTAG(mp); - if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) { - ERL_MESSAGE_DT_UTAG(mp) = offset_ptr(mesg, offs); - } + mesg = ERL_MESSAGE_DT_UTAG(mp); + if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) { + ERL_MESSAGE_DT_UTAG(mp) = offset_ptr(mesg, offs); + } #endif - ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) || - is_tuple(ERL_MESSAGE_TOKEN(mp)) || - is_atom(ERL_MESSAGE_TOKEN(mp)))); - mp = mp->next; + ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) || + is_tuple(ERL_MESSAGE_TOKEN(mp)) || + is_atom(ERL_MESSAGE_TOKEN(mp)))); + mp = mp->next; + } + } } -- cgit v1.2.3 From 568cd49812ff0b59b0b8f1ebfc2da588f78d55a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 Dec 2015 12:45:05 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 56060 -> 56052 bytes erts/preloaded/ebin/erlang.beam | Bin 34396 -> 102012 bytes erts/preloaded/ebin/erts_internal.beam | Bin 2032 -> 6260 bytes erts/preloaded/ebin/init.beam | Bin 48600 -> 48592 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1460 -> 1452 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1332 -> 1324 bytes erts/preloaded/ebin/prim_file.beam | Bin 44788 -> 44780 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72624 -> 72612 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23296 -> 23284 bytes erts/preloaded/ebin/zlib.beam | Bin 14168 -> 14160 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index e94a1ba796..8ccbd1e36b 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 6610800458..87a48525bb 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index fcc8c85e52..191dd332e2 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 60c08819eb..a78d9abc20 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 04814c091b..9cc91be343 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 7779c8374d..e84c8ffd2d 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 254b0e5b90..e1b2a1c8eb 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index b7cfe26462..8853ae8bda 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 6b5c6195c8..563e604a1d 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 43d7b436be..304783ae37 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 27b921fef54b7410efdf756d6ad20ce2877fbc6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 8 Oct 2015 14:11:07 +0200 Subject: Clean up parsing and lookup of flags The handling of flags has been incrementally messed up over time, leading to convoluted code. Rewrite the parsing and lookup of flags. By using the appropriate data structures, the code will become simpler. --- erts/preloaded/src/init.erl | 98 ++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 64 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 0ad5824ad1..b166aba81d 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -169,8 +169,7 @@ boot(BootArgs) -> process_flag(trap_exit, true), {Start0,Flags,Args} = parse_boot_args(BootArgs), Start = map(fun prepare_run_args/1, Start0), - Flags0 = flags_to_atoms_again(Flags), - boot(Start,Flags0,Args). + boot(Start, Flags, Args). prepare_run_args({eval, [Expr]}) -> {eval,Expr}; @@ -202,16 +201,6 @@ map(_F, []) -> map(F, [X|Rest]) -> [F(X) | map(F, Rest)]. -flags_to_atoms_again([]) -> - []; -flags_to_atoms_again([{F0,L0}|Rest]) -> - L = L0, - F = b2a(F0), - [{F,L}|flags_to_atoms_again(Rest)]; -flags_to_atoms_again([{F0}|Rest]) -> - F = b2a(F0), - [{F}|flags_to_atoms_again(Rest)]. - -spec code_path_choice() -> 'relaxed' | 'strict'. code_path_choice() -> case get_argument(code_path_choice) of @@ -451,9 +440,9 @@ do_handle_msg(Msg,State) -> %%% ------------------------------------------------- make_permanent(Boot,Config,Flags0,State) -> - case set_flag('-boot',Boot,Flags0) of + case set_flag(boot, Boot, Flags0) of {ok,Flags1} -> - case set_flag('-config',Config,Flags1) of + case set_flag(config, Config, Flags1) of {ok,Flags} -> {ok,State#state{flags = Flags}}; Error -> @@ -716,10 +705,10 @@ add_to_kernel(Init,Pid) -> end. prim_load_flags(Flags) -> - PortPgm = get_flag('-loader',Flags,<<"efile">>), - Hosts = get_flag_list('-hosts', Flags, []), - Id = get_flag('-id',Flags,none), - Path = get_flag_list('-path',Flags,false), + PortPgm = get_flag(loader, Flags, <<"efile">>), + Hosts = get_flag_list(hosts, Flags, []), + Id = get_flag(id, Flags, none), + Path = get_flag_list(path, Flags, false), {PortPgm, Hosts, Id, Path}. %%% ------------------------------------------------- @@ -735,17 +724,17 @@ do_boot(Flags,Start) -> do_boot(Init,Flags,Start) -> process_flag(trap_exit,true), {Pgm0,Nodes,Id,Path} = prim_load_flags(Flags), - Root = b2s(get_flag('-root',Flags)), + Root = b2s(get_flag(root, Flags)), PathFls = path_flags(Flags), Pgm = b2s(Pgm0), _Pid = start_prim_loader(Init,b2a(Id),Pgm,bs2as(Nodes), bs2ss(Path),PathFls), BootFile = bootfile(Flags,Root), BootList = get_boot(BootFile,Root), - LoadMode = b2a(get_flag('-mode',Flags,false)), - Deb = b2a(get_flag('-init_debug',Flags,false)), + LoadMode = b2a(get_flag(mode, Flags, false)), + Deb = b2a(get_flag(init_debug, Flags, false)), catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb}, - BootVars = get_flag_args('-boot_var',Flags), + BootVars = get_flag_args(boot_var, Flags), ParallelLoad = (Pgm =:= "efile") and (erlang:system_info(thread_pool_size) > 0), @@ -760,11 +749,11 @@ do_boot(Init,Flags,Start) -> start_em(Start). bootfile(Flags,Root) -> - b2s(get_flag('-boot',Flags,concat([Root,"/bin/start"]))). + b2s(get_flag(boot, Flags, concat([Root,"/bin/start"]))). path_flags(Flags) -> - Pa = append(reverse(get_flag_args('-pa',Flags))), - Pz = append(get_flag_args('-pz',Flags)), + Pa = append(reverse(get_flag_args(pa, Flags))), + Pz = append(get_flag_args(pz, Flags)), {bs2ss(Pa),bs2ss(Pz)}. get_boot(BootFile0,Root) -> @@ -1102,7 +1091,7 @@ load_mod_code(Mod, BinCode, FullName) -> %% -------------------------------------------------------- shutdown_timer(Flags) -> - case get_flag('-shutdown_time',Flags,infinity) of + case get_flag(shutdown_time, Flags, infinity) of infinity -> self(); Time -> @@ -1152,14 +1141,10 @@ parse_boot_args([B|Bs], Ss, Fs, As) -> eval_arg -> {Expr,Rest} = get_args(Bs, []), parse_boot_args(Rest, [{eval, Expr}|Ss], Fs, As); - flag -> + {flag,A} -> {F,Rest} = get_args(Bs, []), - Fl = case F of - [] -> [B]; - FF -> [B,FF] - end, - parse_boot_args(Rest, Ss, - [list_to_tuple(Fl)|Fs], As); + Fl = {A,F}, + parse_boot_args(Rest, Ss, [Fl|Fs], As); arg -> parse_boot_args(Bs, Ss, Fs, [B|As]); end_args -> @@ -1173,12 +1158,8 @@ check(<<"-s">>) -> start_arg; check(<<"-run">>) -> start_arg2; check(<<"-eval">>) -> eval_arg; check(<<"--">>) -> end_args; -check(X) when is_binary(X) -> - case binary_to_list(X) of - [$-|_Rest] -> flag; - _Chars -> arg %Even empty atoms - end; -check(_X) -> arg. %This should never occur +check(<<"-",Flag/binary>>) -> {flag,b2a(Flag)}; +check(_) -> arg. get_args([B|Bs], As) -> case check(B) of @@ -1187,7 +1168,7 @@ get_args([B|Bs], As) -> start_arg2 -> {reverse(As), [B|Bs]}; eval_arg -> {reverse(As), [B|Bs]}; end_args -> {reverse(As), Bs}; - flag -> {reverse(As), [B|Bs]}; + {flag,_} -> {reverse(As), [B|Bs]}; arg -> get_args(Bs, [B|As]) end; @@ -1209,12 +1190,12 @@ get_flag(F,Flags,Default) -> get_flag(F,Flags) -> case search(F,Flags) of + {value,{F,[]}} -> + true; {value,{F,[V]}} -> V; {value,{F,V}} -> V; - {value,{F}} -> % Flag given! - true; _ -> exit(list_to_atom(concat(["no ",F," flag"]))) end. @@ -1246,21 +1227,15 @@ get_flag_list(F,Flags) -> %% get_flag_args(F,Flags) -> get_flag_args(F,Flags,[]). -get_flag_args(F,[{F,V}|Flags],Acc) when is_list(V) -> - get_flag_args(F,Flags,[V|Acc]); get_flag_args(F,[{F,V}|Flags],Acc) -> - get_flag_args(F,Flags,[[V]|Acc]); + get_flag_args(F,Flags,[V|Acc]); get_flag_args(F,[_|Flags],Acc) -> get_flag_args(F,Flags,Acc); get_flag_args(_,[],Acc) -> reverse(Acc). get_arguments([{F,V}|Flags]) -> - [$-|Fl] = atom_to_list(F), - [{list_to_atom(Fl),to_strings(V)}|get_arguments(Flags)]; -get_arguments([{F}|Flags]) -> - [$-|Fl] = atom_to_list(F), - [{list_to_atom(Fl),[]}|get_arguments(Flags)]; + [{F,to_strings(V)}|get_arguments(Flags)]; get_arguments([]) -> []. @@ -1268,26 +1243,21 @@ to_strings([H|T]) when is_atom(H) -> [atom_to_list(H)|to_strings(T)]; to_strings([H|T]) when is_binary(H) -> [b2s(H)|to_strings(T)]; to_strings([]) -> []. -get_argument(Arg,Flags) -> - Args = get_arguments(Flags), - case get_argument1(Arg,Args) of - [] -> - error; - Value -> - {ok,Value} +get_argument(Arg, Flags) -> + case get_argument1(Arg, Flags) of + [] -> error; + Value -> {ok,Value} end. -get_argument1(Arg,[{Arg,V}|Args]) -> - [V|get_argument1(Arg,Args)]; -get_argument1(Arg,[_|Args]) -> - get_argument1(Arg,Args); -get_argument1(_,[]) -> +get_argument1(Arg, [{Arg,V}|Args]) -> + [to_strings(V)|get_argument1(Arg, Args)]; +get_argument1(Arg, [_|Args]) -> + get_argument1(Arg, Args); +get_argument1(_, []) -> []. set_argument([{Flag,_}|Flags],Flag,Value) -> [{Flag,[Value]}|Flags]; -set_argument([{Flag}|Flags],Flag,Value) -> - [{Flag,[Value]}|Flags]; set_argument([Item|Flags],Flag,Value) -> [Item|set_argument(Flags,Flag,Value)]; set_argument([],Flag,Value) -> -- cgit v1.2.3 From e1dc0aa4100f881f4350162bd523c53d38f08b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 12 Oct 2015 15:14:14 +0200 Subject: Simplify get_flag() and get_flag_list() The get_flag/3 function which returns a default value if the flag doesn't exist, is implemented in terms of get_flag/2 which throws an exception if the flag doesn't exist. get_flag/3 is frequently used for the flags that don't exist, which means that means that an exception will be generated and catched for no good reason. Reimplement get_flag/3 so that it doesn't have to call get_flag/2 and catch an exception. Eliminate the get_flag/2 function by writing a special purpose function for the only time it's used, that is for retrieving the value for the -root flag. As a side-effect, we will get a nicer error message if there is something wrong with the -root flag. Similarly, simplify get_flag_list/3 and remove get_flag_list/2. We can also eliminate search/3 and use the lists:keyfind/3 BIF instead. --- erts/preloaded/src/init.erl | 53 ++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 34 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index b166aba81d..a85b41fddb 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -724,7 +724,7 @@ do_boot(Flags,Start) -> do_boot(Init,Flags,Start) -> process_flag(trap_exit,true), {Pgm0,Nodes,Id,Path} = prim_load_flags(Flags), - Root = b2s(get_flag(root, Flags)), + Root = get_root(Flags), PathFls = path_flags(Flags), Pgm = b2s(Pgm0), _Pid = start_prim_loader(Init,b2a(Id),Pgm,bs2as(Nodes), @@ -748,6 +748,14 @@ do_boot(Init,Flags,Start) -> start_em(Start). +get_root(Flags) -> + case get_argument(root, Flags) of + {ok,[[Root]]} -> + Root; + _ -> + exit(no_or_multiple_root_variables) + end. + bootfile(Flags,Root) -> b2s(get_flag(boot, Flags, concat([Root,"/bin/start"]))). @@ -1180,44 +1188,28 @@ get_args([], As) -> {reverse(As),[]}. %% atom() if a single arg was given. %% list(atom()) if several args were given. %% -get_flag(F,Flags,Default) -> - case catch get_flag(F,Flags) of - {'EXIT',_} -> - Default; - Value -> - Value - end. - -get_flag(F,Flags) -> - case search(F,Flags) of - {value,{F,[]}} -> +get_flag(F, Flags, Default) -> + case lists:keyfind(F, 1, Flags) of + {F,[]} -> true; - {value,{F,[V]}} -> + {F,[V]} -> V; - {value,{F,V}} -> + {F,V} -> V; _ -> - exit(list_to_atom(concat(["no ",F," flag"]))) + Default end. %% %% Internal get_flag function, with default value. %% Return: list(atom()) %% -get_flag_list(F,Flags,Default) -> - case catch get_flag_list(F,Flags) of - {'EXIT',_} -> - Default; - Value -> - Value - end. - -get_flag_list(F,Flags) -> - case search(F,Flags) of - {value,{F,V}} -> +get_flag_list(F, Flags, Default) -> + case lists:keyfind(F, 1, Flags) of + {F,[_|_]=V} -> V; _ -> - exit(list_to_atom(concat(["no ",F," flag"]))) + Default end. %% @@ -1290,13 +1282,6 @@ reverse([A, B]) -> reverse([A, B | L]) -> lists:reverse(L, [B, A]). % BIF -search(Key, [H|_T]) when is_tuple(H), element(1, H) =:= Key -> - {value, H}; -search(Key, [_|T]) -> - search(Key, T); -search(_Key, []) -> - false. - -spec objfile_extension() -> nonempty_string(). objfile_extension() -> ".beam". -- cgit v1.2.3 From 93f7c2dcdd36a31dca6bfd06e0784ed715e51f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 12 Oct 2015 15:56:35 +0200 Subject: Remove useless 'catch' in start_it/1 The last clause in start_it/1 calls a function in some module. It goes to great length to catch any exception and pass them on unchanged, and if there was a normal return, it will just return the return value. It can been seen that the entire 'catch' construction with the reference trick is totally unnecessary. --- erts/preloaded/src/init.erl | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index a85b41fddb..c1c0e781e7 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -1046,18 +1046,10 @@ start_it({eval,Bin}) -> {value, _Value, _Bs} = erl_eval:exprs(Expr, erl_eval:new_bindings()), ok; start_it([_|_]=MFA) -> - Ref = make_ref(), - case catch {Ref,case MFA of - [M] -> M:start(); - [M,F] -> M:F(); - [M,F|Args] -> M:F(Args) % Args is a list - end} of - {Ref,R} -> - R; - {'EXIT',Reason} -> - exit(Reason); - Other -> - throw(Other) + case MFA of + [M] -> M:start(); + [M,F] -> M:F(); + [M,F|Args] -> M:F(Args) % Args is a list end. %% -- cgit v1.2.3 From 4551a14515a57b9aabaa95b729ac546c91ff71f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 13 Oct 2015 10:10:25 +0200 Subject: Clean up handling of boot_vars Expansion of $ROOT in paths are handled specially compared to boot variables. There is no reason $ROOT can't be handled as a boot variable. We can simplify the expansion of boot variables if we spend a little extra effort upfront collecting all boot variables into a map. Make the error checking for -boot_var arguments stricter. Only allow -boot_var followed by exactly two arguments to help users catch errors earlier. --- erts/preloaded/src/init.erl | 53 +++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index c1c0e781e7..730aac9902 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -734,12 +734,12 @@ do_boot(Init,Flags,Start) -> LoadMode = b2a(get_flag(mode, Flags, false)), Deb = b2a(get_flag(init_debug, Flags, false)), catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb}, - BootVars = get_flag_args(boot_var, Flags), + BootVars = get_boot_vars(Root, Flags), ParallelLoad = (Pgm =:= "efile") and (erlang:system_info(thread_pool_size) > 0), PathChoice = code_path_choice(), - eval_script(BootList,Init,PathFls,{Root,BootVars},Path, + eval_script(BootList,Init,PathFls,BootVars,Path, {true,LoadMode,ParallelLoad},Deb,PathChoice), %% To help identifying Purify windows that pop up, @@ -756,6 +756,20 @@ get_root(Flags) -> exit(no_or_multiple_root_variables) end. +get_boot_vars(Root, Flags) -> + BootVars = get_boot_vars_1(#{}, Flags), + RootKey = <<"ROOT">>, + BootVars#{RootKey=>Root}. + +get_boot_vars_1(Vars, [{boot_var,[Key,Value]}|T]) -> + get_boot_vars_1(Vars#{Key=>Value}, T); +get_boot_vars_1(_, [{boot_var,_}|_]) -> + exit(invalid_boot_var_argument); +get_boot_vars_1(Vars, [_|T]) -> + get_boot_vars_1(Vars, T); +get_boot_vars_1(Vars, []) -> + Vars. + bootfile(Flags,Root) -> b2s(get_flag(boot, Flags, concat([Root,"/bin/start"]))). @@ -905,34 +919,25 @@ fix_path([Path|Ps], Vars) -> fix_path(_, _) -> []. -add_var("$ROOT/" ++ Path, {Root,_}) -> - concat([Root, "/", Path]); -add_var([$$|Path0], {_,VarList}) -> - {Var,Path} = extract_var(Path0,[]), - Value = b2s(get_var_value(list_to_binary(Var),VarList)), - concat([Value, "/", Path]); -add_var(Path, _) -> +add_var("$"++Path0, Vars) -> + {Var,Path} = extract_var(Path0, []), + Key = list_to_binary(Var), + case Vars of + #{Key:=Value0} -> + Value = b2s(Value0), + Value ++ "/" ++ Path; + _ -> + Error0 = "cannot expand $" ++ Var ++ " in bootfile", + Error = list_to_atom(Error0), + exit(Error) + end; +add_var(Path, _) -> Path. extract_var([$/|Path],Var) -> {reverse(Var),Path}; extract_var([H|T],Var) -> extract_var(T,[H|Var]); extract_var([],Var) -> {reverse(Var),[]}. -%% get_var_value(Var, [Vars]) where Vars == [atom()] -get_var_value(Var,[Vars|VarList]) -> - case get_var_val(Var,Vars) of - {ok, Value} -> - Value; - _ -> - get_var_value(Var,VarList) - end; -get_var_value(Var,[]) -> - exit(list_to_atom(concat(["cannot expand \$", Var, " in bootfile"]))). - -get_var_val(Var,[Var,Value|_]) -> {ok, Value}; -get_var_val(Var,[_,_|Vars]) -> get_var_val(Var,Vars); -get_var_val(_,_) -> false. - patch_path(Dirs, strict) -> Dirs; patch_path(Dirs, relaxed) -> -- cgit v1.2.3 From 657a1be6b54e9baaf8d2c7a28ac261b4086148dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 13 Oct 2015 15:53:39 +0200 Subject: Reduce the ludicrous number of arguments for eval_script() The compact wall of arguments makes it hard to see what is actually happening in eval_script(). Collect the arguments into a record. --- erts/preloaded/src/init.erl | 100 +++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 39 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 730aac9902..cc30999ba5 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -75,6 +75,20 @@ subscribed = []}). -type state() :: #state{}. +%% Data for eval_script/2. +-record(es, + {init, + debug, + path, + pa, + pz, + path_choice, + prim_load, + load_mode, + par_load, + vars + }). + -define(ON_LOAD_HANDLER, init__boot__on_load_handler). debug(false, _) -> ok; @@ -725,7 +739,7 @@ do_boot(Init,Flags,Start) -> process_flag(trap_exit,true), {Pgm0,Nodes,Id,Path} = prim_load_flags(Flags), Root = get_root(Flags), - PathFls = path_flags(Flags), + {Pa,Pz} = PathFls = path_flags(Flags), Pgm = b2s(Pgm0), _Pid = start_prim_loader(Init,b2a(Id),Pgm,bs2as(Nodes), bs2ss(Path),PathFls), @@ -735,12 +749,15 @@ do_boot(Init,Flags,Start) -> Deb = b2a(get_flag(init_debug, Flags, false)), catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb}, BootVars = get_boot_vars(Root, Flags), - ParallelLoad = - (Pgm =:= "efile") and (erlang:system_info(thread_pool_size) > 0), + ParLoad = Pgm =:= "efile" andalso + erlang:system_info(thread_pool_size) > 0, PathChoice = code_path_choice(), - eval_script(BootList,Init,PathFls,BootVars,Path, - {true,LoadMode,ParallelLoad},Deb,PathChoice), + Es = #es{init=Init,debug=Deb,path=Path,pa=Pa,pz=Pz, + path_choice=PathChoice, + prim_load=true,load_mode=LoadMode,par_load=ParLoad, + vars=BootVars}, + eval_script(BootList, Es), %% To help identifying Purify windows that pop up, %% print the node name into the Purify log. @@ -818,48 +835,53 @@ get_boot(BootFile) -> %% boot process hangs (we want to ensure syncronicity). %% -eval_script([{progress,Info}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) -> - debug(Deb,{progress,Info}), +eval_script([{progress,Info}=Progress|T], #es{debug=Deb}=Es) -> + debug(Deb, Progress), init ! {self(),progress,Info}, - eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice); -eval_script([{preLoaded,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) -> - eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice); -eval_script([{path,Path}|CfgL],Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice) -> + eval_script(T, Es); +eval_script([{preLoaded,_}|T], #es{}=Es) -> + eval_script(T, Es); +eval_script([{path,Path}|T], #es{path=false,pa=Pa,pz=Pz, + path_choice=PathChoice, + vars=Vars}=Es) -> RealPath0 = make_path(Pa, Pz, Path, Vars), RealPath = patch_path(RealPath0, PathChoice), erl_prim_loader:set_path(RealPath), - eval_script(CfgL,Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice); -eval_script([{path,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) -> + eval_script(T, Es); +eval_script([{path,_}|T], #es{}=Es) -> %% Ignore, use the command line -path flag. - eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice); -eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,embedded,Par},Deb,PathChoice) -> - eval_script(CfgL,Init,PathFs,Vars,P,{true,embedded,Par},Deb,PathChoice); -eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,E,Par},Deb,PathChoice) -> - eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice); -eval_script([{primLoad,Mods}|CfgL],Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice) + eval_script(T, Es); +eval_script([{kernel_load_completed}|T], #es{load_mode=Mode}=Es0) -> + Es = case Mode of + embedded -> Es0; + _ -> Es0#es{prim_load=false} + end, + eval_script(T, Es); +eval_script([{primLoad,Mods}|T], #es{init=Init,prim_load=PrimLoad, + par_load=Par}=Es) when is_list(Mods) -> - if - Par =:= true -> - par_load_modules(Mods,Init); - true -> - load_modules(Mods) + case {PrimLoad,Par} of + {true,true} -> + par_load_modules(Mods, Init); + {true,false} -> + load_modules(Mods); + {false,_} -> + %% Do not load now, code_server does that dynamically! + ok end, - eval_script(CfgL,Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice); -eval_script([{primLoad,_Mods}|CfgL],Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice) -> - %% Do not load now, code_server does that dynamically! - eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice); -eval_script([{kernelProcess,Server,{Mod,Fun,Args}}|CfgL],Init, - PathFs,Vars,P,Ph,Deb,PathChoice) -> - debug(Deb,{start,Server}), - start_in_kernel(Server,Mod,Fun,Args,Init), - eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice); -eval_script([{apply,{Mod,Fun,Args}}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) -> - debug(Deb,{apply,{Mod,Fun,Args}}), - apply(Mod,Fun,Args), - eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice); -eval_script([],_,_,_,_,_,_,_) -> + eval_script(T, Es); +eval_script([{kernelProcess,Server,{Mod,Fun,Args}}|T], + #es{init=Init,debug=Deb}=Es) -> + debug(Deb, {start,Server}), + start_in_kernel(Server, Mod, Fun, Args, Init), + eval_script(T, Es); +eval_script([{apply,{Mod,Fun,Args}}=Apply|T], #es{debug=Deb}=Es) -> + debug(Deb, Apply), + apply(Mod, Fun, Args), + eval_script(T, Es); +eval_script([], #es{}) -> ok; -eval_script(What,_,_,_,_,_,_,_) -> +eval_script(What, #es{}) -> exit({'unexpected command in bootfile',What}). load_modules([Mod|Mods]) -> -- cgit v1.2.3 From ca72f6a938201d71baf25eeba649d7ec33628c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 18 Nov 2015 16:11:06 +0100 Subject: prim_file: Suppress a dialyzer warning Kostis Sagonas pointed out that there is a dialyzer warning for constructing an improper list in the following clause: translate_response(?FILE_RESP_N2DATA = X, [<<_:64, _:64, _:64>> | <<>>] = Data) -> {error, {bad_response_from_port, [X | Data]}}; I don't want to change the code to somehow eliminate the warning. An improper list has already been constructed in the efile driver itself, and that would be difficult to fix. Therefore, tell dialyzer to ignore warnings for improper lists in translate_response/2. --- erts/preloaded/src/prim_file.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index c87b2645ec..2eb1b1d408 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -1276,6 +1276,7 @@ lseek_position(_) -> %% Translates the response from the driver into %% {ok, Result} or {error, Reason}. +-dialyzer({no_improper_lists, translate_response/2}). translate_response(?FILE_RESP_OK, []) -> ok; translate_response(?FILE_RESP_ERROR, List) when is_list(List) -> -- cgit v1.2.3 From afcec4ab26ef5d9d202810ed6fd8661b3b3ddfcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 15 Dec 2015 09:13:34 +0100 Subject: erl_prim_loader: Break loop/3 into two functions for readability The deep indentation makes loop/3 difficult to read and maintain. Break out the request handling code into a separate function. --- erts/preloaded/src/erl_prim_loader.erl | 102 +++++++++++++++------------------ 1 file changed, 46 insertions(+), 56 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 9f6cba33bd..d19de63b65 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -310,69 +310,59 @@ check_file_result(_, _, Other) -> %%% The main loop. %%% -------------------------------------------------------- -loop(State, Parent, Paths) -> +loop(St0, Parent, Paths) -> receive + {Pid,{set_path,NewPaths}} when is_pid(Pid) -> + Pid ! {self(),ok}, + loop(St0, Parent, to_strs(NewPaths)); {Pid,Req} when is_pid(Pid) -> - %% erlang:display(Req), - {Resp,State2,Paths2} = - case Req of - {set_path,NewPaths} -> - {ok,State,to_strs(NewPaths)}; - {get_path,_} -> - {{ok,Paths},State,Paths}; - {get_file,File} -> - {Res,State1} = handle_get_file(State, Paths, File), - {Res,State1,Paths}; - {get_files,{ModFiles,Fun}} -> - {Res,State1} = handle_get_files(State, ModFiles, Paths, Fun), - {Res,State1,Paths}; - {list_dir,Dir} -> - {Res,State1} = handle_list_dir(State, Dir), - {Res,State1,Paths}; - {read_file_info,File} -> - {Res,State1} = handle_read_file_info(State, File), - {Res,State1,Paths}; - {read_link_info,File} -> - {Res,State1} = handle_read_link_info(State, File), - {Res,State1,Paths}; - {get_cwd,[]} -> - {Res,State1} = handle_get_cwd(State, []), - {Res,State1,Paths}; - {get_cwd,[_]=Args} -> - {Res,State1} = handle_get_cwd(State, Args), - {Res,State1,Paths}; - {set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} -> - {Res,State1} = - handle_set_primary_archive(State, File, - ArchiveBin, FileInfo, - ParserFun), - {Res,State1,Paths}; - release_archives -> - {Res,State1} = handle_release_archives(State), - {Res,State1,Paths}; - _Other -> - {ignore,State,Paths} - end, - if Resp =:= ignore -> ok; - true -> Pid ! {self(),Resp}, ok - end, - if - is_record(State2, state) -> - loop(State2, Parent, Paths2); - true -> - exit({bad_state, Req, State2}) + case handle_request(Req, Paths, St0) of + ignore -> + ok; + {Resp,#state{}=St1} -> + Pid ! {self(),Resp}, + loop(St1, Parent, Paths); + {_,State2,_} -> + exit({bad_state,Req,State2}) end; {'EXIT',Parent,W} -> - _State1 = handle_stop(State), + _ = handle_stop(St0), exit(W); {'EXIT',P,W} -> - State1 = handle_exit(State, P, W), - loop(State1, Parent, Paths); + St1 = handle_exit(St0, P, W), + loop(St1, Parent, Paths); _Message -> - loop(State, Parent, Paths) - after State#state.timeout -> - State1 = handle_timeout(State, Parent), - loop(State1, Parent, Paths) + loop(St0, Parent, Paths) + after St0#state.timeout -> + St1 = handle_timeout(St0, Parent), + loop(St1, Parent, Paths) + end. + +handle_request(Req, Paths, St0) -> + case Req of + {get_path,_} -> + {{ok,Paths},St0}; + {get_file,File} -> + handle_get_file(St0, Paths, File); + {get_files,{ModFiles,Fun}} -> + handle_get_files(St0, ModFiles, Paths, Fun); + {list_dir,Dir} -> + handle_list_dir(St0, Dir); + {read_file_info,File} -> + handle_read_file_info(St0, File); + {read_link_info,File} -> + handle_read_link_info(St0, File); + {get_cwd,[]} -> + handle_get_cwd(St0, []); + {get_cwd,[_]=Args} -> + handle_get_cwd(St0, Args); + {set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} -> + handle_set_primary_archive(St0, File, ArchiveBin, + FileInfo, ParserFun); + release_archives -> + handle_release_archives(St0); + _ -> + ignore end. handle_get_files(State = #state{multi_get = true}, ModFiles, Paths, Fun) -> -- cgit v1.2.3 From 471d2408de06f3c93507769ce0eb0a9f42c3d119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 19 Nov 2015 10:17:28 +0100 Subject: erl_prim_loader: Remove code for handling OSE --- erts/preloaded/src/erl_prim_loader.erl | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index d19de63b65..07c439a990 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -1405,8 +1405,6 @@ absname_vr([Drive, $\: | NameRest], _) -> %% Assumes normalized name pathtype(Name) when is_list(Name) -> case erlang:system_info(os_type) of - {ose, _} -> - unix_pathtype(Name); {unix, _} -> unix_pathtype(Name); {win32, _} -> -- cgit v1.2.3 From af9bfce55f0df03edaab638dcd3612c8478dfcc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 Dec 2015 14:50:56 +0100 Subject: Remove erl_prim_loader:get_files/2 erl_prim_loader:get_files/2 was an optimization introduced before the SMP emulator (that is, before R11). The idea was to use the async threads in the efile driver to read multiple BEAM files from the disk in parallel. In a modern computer with the SMP emulator, loading a BEAM module seems to be more time-consuming than reading it from disk. To optimize loading we would need to load several modules in parallel. We could modify get_files/2 so that it would support parallel loading, but it is cleaner to first remove get_files/2 and then (in a future commit), introduce new functions to support parallel loading. --- erts/preloaded/src/erl_prim_loader.erl | 76 +--------------------------------- erts/preloaded/src/init.erl | 51 +++-------------------- 2 files changed, 6 insertions(+), 121 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 07c439a990..a8d1e4df76 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -42,7 +42,7 @@ -include("inet_boot.hrl"). %% Public --export([start/3, set_path/1, get_path/0, get_file/1, get_files/2, +-export([start/3, set_path/1, get_path/0, get_file/1, list_dir/1, read_file_info/1, read_link_info/1, get_cwd/0, get_cwd/1]). %% Used by erl_boot_server @@ -69,7 +69,6 @@ timeout :: timeout(), % idle timeout %% Number of timeouts before archives are released n_timeouts :: non_neg_integer(), - multi_get = false :: boolean(), prim_state :: prim_state()}). % state for efile code loader -define(IDLE_TIMEOUT, 60000). %% tear inet connection after 1 minutes @@ -162,16 +161,11 @@ start_it("efile", Id, Pid, _Hosts) -> _ -> init_ack(Pid) end, - MultiGet = case erlang:system_info(thread_pool_size) of - 0 -> false; - _ -> true - end, PS = prim_init(), State = #state {loader = efile, id = Id, data = Port, timeout = infinity, - multi_get = MultiGet, prim_state = PS}, loop(State, Pid, []). @@ -198,20 +192,6 @@ get_file(File) when is_atom(File) -> get_file(File) -> check_file_result(get_file, File, request({get_file,File})). --spec get_files([{atom(), string()}], - fun((atom(),binary(),string()) -> 'ok' | {'error', atom()})) -> - 'ok' | {'error', atom()}. -get_files(ModFiles, Fun) -> - case request({get_files,{ModFiles,Fun}}) of - E = {error,_M} -> - E; - {error,Reason,M} -> - check_file_result(get_files, M, {error,Reason}), - {error,M}; - ok -> - ok - end. - -spec list_dir(Dir) -> {'ok', Filenames} | 'error' when Dir :: string(), Filenames :: [Filename :: string()]. @@ -344,8 +324,6 @@ handle_request(Req, Paths, St0) -> {{ok,Paths},St0}; {get_file,File} -> handle_get_file(St0, Paths, File); - {get_files,{ModFiles,Fun}} -> - handle_get_files(St0, ModFiles, Paths, Fun); {list_dir,Dir} -> handle_list_dir(St0, Dir); {read_file_info,File} -> @@ -365,11 +343,6 @@ handle_request(Req, Paths, St0) -> ignore end. -handle_get_files(State = #state{multi_get = true}, ModFiles, Paths, Fun) -> - ?SAFE2(efile_multi_get_file_from_port(State, ModFiles, Paths, Fun), State); -handle_get_files(State, _ModFiles, _Paths, _Fun) -> % no multi get - {{error,no_multi_get},State}. - handle_get_file(State = #state{loader = efile}, Paths, File) -> ?SAFE2(efile_get_file_from_port(State, File, Paths), State); handle_get_file(State = #state{loader = inet}, Paths, File) -> @@ -420,53 +393,6 @@ handle_timeout(State = #state{loader = inet}, Parent) -> %%% Functions which handle efile as prim_loader (default). %%% -------------------------------------------------------- -%%% Reading many files in parallel is an optimization. -%%% See also comment in init.erl. - -%% -> {ok,State} | {{error,Module},State} | {{error,Reason,Module},State} -efile_multi_get_file_from_port(State, ModFiles, Paths, Fun) -> - Ref = make_ref(), - %% More than 200 processes is no gain. - Max = erlang:min(200, erlang:system_info(thread_pool_size)), - efile_multi_get_file_from_port2(ModFiles, 0, Max, State, Paths, Fun, Ref, ok). - -efile_multi_get_file_from_port2([MF | MFs], Out, Max, State, Paths, Fun, Ref, Ret) when Out < Max -> - Self = self(), - _Pid = spawn(fun() -> efile_par_get_file(Ref, State, MF, Paths, Self, Fun) end), - efile_multi_get_file_from_port2(MFs, Out+1, Max, State, Paths, Fun, Ref, Ret); -efile_multi_get_file_from_port2(MFs, Out, Max, _State, Paths, Fun, Ref, Ret) when Out > 0 -> - receive - {Ref, ok, State1} -> - efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Ret); - {Ref, {error,_Mod} = Error, State1} -> - efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Error); - {Ref, MF, {error,emfile,State1}} -> - %% Max can take negative values. Out cannot. - efile_multi_get_file_from_port2([MF | MFs], Out-1, Max-1, State1, Paths, Fun, Ref, Ret); - {Ref, {M,_F}, {error,Error,State1}} -> - efile_multi_get_file_from_port2(MFs, Out-1, 0, State1, Paths, Fun, Ref, {error,Error,M}) - end; -efile_multi_get_file_from_port2(_MFs, 0, _Max, State, _Paths, _Fun, _Ref, Ret) -> - {Ret,State}. - -efile_par_get_file(Ref, State, {Mod,File} = MF, Paths, Pid, Fun) -> - %% One port for each file read in "parallel": - case prim_file:start() of - {ok, Port} -> - Port0 = State#state.data, - State1 = State#state{data = Port}, - R = case efile_get_file_from_port(State1, File, Paths) of - {{error,Reason},State2} -> - {Ref,MF,{error,Reason,State2}}; - {{ok,BinFile,Full},State2} -> - %% Fun(...) -> ok | {error,Mod} - {Ref,Fun(Mod, BinFile, Full),State2#state{data=Port0}} - end, - prim_file:close(Port), - Pid ! R; - {error, Error} -> - Pid ! {Ref,MF,{error,Error,State}} - end. %% -> {{ok,BinFile,File},State} | {{error,Reason},State} efile_get_file_from_port(State, File, Paths) -> diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index cc30999ba5..b5c1d46e60 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -85,7 +85,6 @@ path_choice, prim_load, load_mode, - par_load, vars }). @@ -749,13 +748,11 @@ do_boot(Init,Flags,Start) -> Deb = b2a(get_flag(init_debug, Flags, false)), catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb}, BootVars = get_boot_vars(Root, Flags), - ParLoad = Pgm =:= "efile" andalso - erlang:system_info(thread_pool_size) > 0, PathChoice = code_path_choice(), Es = #es{init=Init,debug=Deb,path=Path,pa=Pa,pz=Pz, path_choice=PathChoice, - prim_load=true,load_mode=LoadMode,par_load=ParLoad, + prim_load=true,load_mode=LoadMode, vars=BootVars}, eval_script(BootList, Es), @@ -857,15 +854,12 @@ eval_script([{kernel_load_completed}|T], #es{load_mode=Mode}=Es0) -> _ -> Es0#es{prim_load=false} end, eval_script(T, Es); -eval_script([{primLoad,Mods}|T], #es{init=Init,prim_load=PrimLoad, - par_load=Par}=Es) +eval_script([{primLoad,Mods}|T], #es{prim_load=PrimLoad}=Es) when is_list(Mods) -> - case {PrimLoad,Par} of - {true,true} -> - par_load_modules(Mods, Init); - {true,false} -> + case PrimLoad of + true -> load_modules(Mods); - {false,_} -> + false -> %% Do not load now, code_server does that dynamically! ok end, @@ -892,41 +886,6 @@ load_modules([Mod|Mods]) -> load_modules([]) -> ok. -%%% An optimization: erl_prim_loader gets the chance of loading many -%%% files in parallel, using threads. This will reduce the seek times, -%%% and loaded code can be processed while other threads are waiting -%%% for the disk. The optimization is not tried unless the loader is -%%% "efile" and there is a non-empty pool of threads. -%%% -%%% Many threads are needed to get a good result, so it would be -%%% beneficial to load several applications in parallel. However, -%%% measurements show that the file system handles one directory at a -%%% time, regardless if parallel threads are created for files on -%%% several directories (a guess: writing the meta information when -%%% the file was last read ('mtime'), forces the file system to sync -%%% between directories). - -par_load_modules(Mods,Init) -> - Ext = objfile_extension(), - ModFiles = [{Mod,concat([Mod,Ext])} || Mod <- Mods, - not erlang:module_loaded(Mod)], - Self = self(), - Fun = fun(Mod, BinCode, FullName) -> - case catch load_mod_code(Mod, BinCode, FullName) of - {ok, _} -> - Init ! {Self,loaded,{Mod,FullName}}, - ok; - _EXIT -> - {error, Mod} - end - end, - case erl_prim_loader:get_files(ModFiles, Fun) of - ok -> - ok; - {error,Mod} -> - exit({'cannot load',Mod,get_files}) - end. - make_path(Pa, Pz, Path, Vars) -> append([Pa,append([fix_path(Path,Vars),Pz])]). -- cgit v1.2.3 From 566a7f4324376428f3f0f6a77bc57679f04ada78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 Dec 2015 15:15:03 +0100 Subject: Clean up start of erl_prim_loader The 'init' module fetches command line parameters and passes them to erl_prim_loader:start/3. The code can be simplified if 'init' calls a new erl_prim_loader:start/0 function that itself fetches the necessary command line parameters. Also remove the documentation for the start() function, since it there is no way that it can be usefully called by a user application. While we are at it, also get rid of '-id' command line parameter, which is fetched and stored but never actually used. --- erts/doc/src/erl_prim_loader.xml | 40 +++------------------ erts/preloaded/src/erl_prim_loader.erl | 65 ++++++++++++++++------------------ erts/preloaded/src/init.erl | 51 ++++++++++++-------------- 3 files changed, 56 insertions(+), 100 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml index db4f132609..6fdec8c89e 100644 --- a/erts/doc/src/erl_prim_loader.xml +++ b/erts/doc/src/erl_prim_loader.xml @@ -50,35 +50,8 @@ -loader_debug are also experimental

- - - - - - - - Start the Erlang low level loader - -

Starts the Erlang low level loader. This function is called - by the init process (and module). The init - process reads the command line flags -id Id, - -loader Loader, and -hosts Hosts. These are - the arguments supplied to the start/3 function.

-

If -loader is not given, the default loader is - efile which tells the system to read from the file - system.

-

If -loader is inet, the -id Id, - -hosts Hosts, and -setcookie Cookie flags must - also be supplied. Hosts identifies hosts which this - node can contact in order to load modules. One Erlang - runtime system with a erl_boot_server process must be - started on each of hosts given in Hosts in order to - answer the requests. See erl_boot_server(3).

-
-
Get a file @@ -189,17 +162,12 @@

Specifies which other Erlang nodes the inet loader can use. This flag is mandatory if the -loader inet flag is present. On each host, there must be on Erlang node - with the erl_boot_server which handles the load - requests. Hosts is a list of IP addresses (hostnames + with the erl_boot_server(3) + which handles the load requests. + Hosts is a list of IP addresses (hostnames are not acceptable).

- -id Id - -

Specifies the identity of the Erlang runtime system. If - the system runs as a distributed node, Id must be - identical to the name supplied with the -sname or - -name distribution flags.

-
-setcookie Cookie

Specifies the cookie of the Erlang runtime system. This flag diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index a8d1e4df76..041b54ee0d 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -42,7 +42,7 @@ -include("inet_boot.hrl"). %% Public --export([start/3, set_path/1, get_path/0, get_file/1, +-export([start/0, set_path/1, get_path/0, get_file/1, list_dir/1, read_file_info/1, read_link_info/1, get_cwd/0, get_cwd/1]). %% Used by erl_boot_server @@ -64,9 +64,8 @@ -record(state, {loader :: 'efile' | 'inet', hosts = [] :: [host()], % hosts list (to boot from) - id, % not used any more? data :: 'noport' | port(), % data port etc - timeout :: timeout(), % idle timeout + timeout :: timeout(), % idle timeout %% Number of timeouts before archives are released n_timeouts :: non_neg_integer(), prim_state :: prim_state()}). % state for efile code loader @@ -102,26 +101,13 @@ debug(#prim_state{debug = Deb}, Term) -> %%% Interface Functions. %%% -------------------------------------------------------- --spec start(Id, Loader, Hosts) -> +-spec start() -> {'ok', Pid} | {'error', What} when - Id :: term(), - Loader :: atom() | string(), - Hosts :: Host | [Host], - Host :: host(), Pid :: pid(), What :: term(). -start(Id, Pgm, Hosts) when is_atom(Hosts) -> - start(Id, Pgm, [Hosts]); -start(Id, Pgm0, Hosts) -> - Pgm = if - is_atom(Pgm0) -> - atom_to_list(Pgm0); - true -> - Pgm0 - end, +start() -> Self = self(), - Pid = spawn_link(fun() -> start_it(Pgm, Id, Self, Hosts) end), - register(erl_prim_loader, Pid), + Pid = spawn_link(fun() -> start_it(Self) end), receive {Pid,ok} -> {ok,Pid}; @@ -129,26 +115,40 @@ start(Id, Pgm0, Hosts) -> {error,Reason} end. -%% Hosts must be a list of form ['1.2.3.4' ...] -start_it("inet", Id, Pid, Hosts) -> +start_it(Parent) -> process_flag(trap_exit, true), - ?dbg(inet, {Id,Pid,Hosts}), + register(erl_prim_loader, self()), + Loader = case init:get_argument(loader) of + {ok,[[Loader0]]} -> + Loader0; + error -> + "efile" + end, + case Loader of + "efile" -> start_efile(Parent); + "inet" -> start_inet(Parent) + end. + +%% Hosts must be a list of form ['1.2.3.4' ...] +start_inet(Parent) -> + Hosts = case init:get_argument(hosts) of + {ok,[Hosts0]} -> Hosts0; + _ -> [] + end, AL = ipv4_list(Hosts), ?dbg(addresses, AL), {ok,Tcp} = find_master(AL), - init_ack(Pid), + init_ack(Parent), PS = prim_init(), State = #state {loader = inet, hosts = AL, - id = Id, data = Tcp, timeout = ?IDLE_TIMEOUT, n_timeouts = ?N_TIMEOUTS, prim_state = PS}, - loop(State, Pid, []); + loop(State, Parent, []). -start_it("efile", Id, Pid, _Hosts) -> - process_flag(trap_exit, true), +start_efile(Parent) -> {ok, Port} = prim_file:start(), %% Check that we started in a valid directory. case prim_file:get_cwd(Port) of @@ -159,15 +159,14 @@ start_it("efile", Id, Pid, _Hosts) -> erlang:display(Report), exit({error, invalid_current_directory}); _ -> - init_ack(Pid) + init_ack(Parent) end, PS = prim_init(), State = #state {loader = efile, - id = Id, data = Port, timeout = infinity, prim_state = PS}, - loop(State, Pid, []). + loop(State, Parent, []). init_ack(Pid) -> Pid ! {self(),ok}, @@ -1262,11 +1261,7 @@ string_split2(_, _Ext, _RevBase, _RevTop, SaveFile, SaveExt, SaveTop) -> %% Parse list of ipv4 addresses ipv4_list([H | T]) -> - IPV = if is_atom(H) -> ipv4_address(atom_to_list(H)); - is_list(H) -> ipv4_address(H); - true -> {error,einal} - end, - case IPV of + case ipv4_address(H) of {ok,IP} -> [IP | ipv4_list(T)]; _ -> ipv4_list(T) end; diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index b5c1d46e60..197bc5fde8 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -23,7 +23,6 @@ %% a local file or distributed from another erlang node. %% %% Flags: -%% -id Identity : identity of the system. %% -boot File : Absolute file name of the boot script. %% -boot_var Var Value %% : $Var in the boot script is expanded to @@ -693,17 +692,15 @@ sleep(T) -> receive after T -> ok end. %%% The loader shall run for ever! %%% ------------------------------------------------- -start_prim_loader(Init,Id,Pgm,Nodes,Path,{Pa,Pz}) -> - case erl_prim_loader:start(Id,Pgm,Nodes) of - {ok,Pid} when Path =:= false -> - InitPath = append(Pa,["."|Pz]), - erl_prim_loader:set_path(InitPath), - add_to_kernel(Init,Pid), - Pid; +start_prim_loader(Init, Path0, {Pa,Pz}) -> + Path = case Path0 of + false -> Pa ++ ["."|Pz]; + _ -> Path0 + end, + case erl_prim_loader:start() of {ok,Pid} -> erl_prim_loader:set_path(Path), - add_to_kernel(Init,Pid), - Pid; + add_to_kernel(Init, Pid); {error,Reason} -> erlang:display({"cannot start loader",Reason}), exit(Reason) @@ -717,13 +714,6 @@ add_to_kernel(Init,Pid) -> ok end. -prim_load_flags(Flags) -> - PortPgm = get_flag(loader, Flags, <<"efile">>), - Hosts = get_flag_list(hosts, Flags, []), - Id = get_flag(id, Flags, none), - Path = get_flag_list(path, Flags, false), - {PortPgm, Hosts, Id, Path}. - %%% ------------------------------------------------- %%% The boot process fetches a boot script and loads %%% all modules specified and starts spec. processes. @@ -736,12 +726,10 @@ do_boot(Flags,Start) -> do_boot(Init,Flags,Start) -> process_flag(trap_exit,true), - {Pgm0,Nodes,Id,Path} = prim_load_flags(Flags), Root = get_root(Flags), + Path = get_flag_list(path, Flags, false), {Pa,Pz} = PathFls = path_flags(Flags), - Pgm = b2s(Pgm0), - _Pid = start_prim_loader(Init,b2a(Id),Pgm,bs2as(Nodes), - bs2ss(Path),PathFls), + start_prim_loader(Init, bs2ss(Path), PathFls), BootFile = bootfile(Flags,Root), BootList = get_boot(BootFile,Root), LoadMode = b2a(get_flag(mode, Flags, false)), @@ -854,11 +842,18 @@ eval_script([{kernel_load_completed}|T], #es{load_mode=Mode}=Es0) -> _ -> Es0#es{prim_load=false} end, eval_script(T, Es); -eval_script([{primLoad,Mods}|T], #es{prim_load=PrimLoad}=Es) +eval_script([{primLoad,[Mod]}|T], #es{prim_load=true}=Es) -> + %% Common special case (loading of error_handler). Nothing + %% to gain by parallel loading. + File = atom_to_list(Mod) ++ objfile_extension(), + {ok,Full} = load_mod(Mod, File), + init ! {self(),loaded,{Mod,Full}}, % Tell init about loaded module + eval_script(T, Es); +eval_script([{primLoad,Mods}|T], #es{init=Init,prim_load=PrimLoad}=Es) when is_list(Mods) -> case PrimLoad of true -> - load_modules(Mods); + load_modules(Mods, Init); false -> %% Do not load now, code_server does that dynamically! ok @@ -878,12 +873,12 @@ eval_script([], #es{}) -> eval_script(What, #es{}) -> exit({'unexpected command in bootfile',What}). -load_modules([Mod|Mods]) -> +load_modules([Mod|Mods], Init) -> File = concat([Mod,objfile_extension()]), {ok,Full} = load_mod(Mod,File), - init ! {self(),loaded,{Mod,Full}}, %% Tell init about loaded module - load_modules(Mods); -load_modules([]) -> + Init ! {self(),loaded,{Mod,Full}}, %Tell init about loaded module + load_modules(Mods, Init); +load_modules([], _) -> ok. make_path(Pa, Pz, Path, Vars) -> @@ -1244,8 +1239,6 @@ concat([S|T]) -> concat([]) -> []. -append(L, Z) -> L ++ Z. - append([E]) -> E; append([H|T]) -> H ++ append(T); -- cgit v1.2.3 From e2e49ee0b0292da4a48d90ed762d7df0b3a64f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 26 Nov 2015 11:23:41 +0100 Subject: init: Eliminate the concat/1 function There is no need to use the concat/1 function since all arguments that are passed to it have known types. --- erts/preloaded/src/init.erl | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 197bc5fde8..383c4a1ec6 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -338,7 +338,7 @@ boot_loop(BootPid, State) -> end. ensure_loaded(Module, Loaded) -> - File = concat([Module,objfile_extension()]), + File = atom_to_list(Module) ++ objfile_extension(), case catch load_mod(Module,File) of {ok, FullName} -> {{module, Module}, [{Module, FullName}|Loaded]}; @@ -773,7 +773,7 @@ get_boot_vars_1(Vars, []) -> Vars. bootfile(Flags,Root) -> - b2s(get_flag(boot, Flags, concat([Root,"/bin/start"]))). + b2s(get_flag(boot, Flags, Root++"/bin/start")). path_flags(Flags) -> Pa = append(reverse(get_flag_args(pa, Flags))), @@ -781,12 +781,12 @@ path_flags(Flags) -> {bs2ss(Pa),bs2ss(Pz)}. get_boot(BootFile0,Root) -> - BootFile = concat([BootFile0,".boot"]), + BootFile = BootFile0 ++ ".boot", case get_boot(BootFile) of {ok, CmdList} -> CmdList; not_found -> %% Check for default. - BootF = concat([Root,"/bin/",BootFile]), + BootF = Root ++ "/bin/" ++ BootFile, case get_boot(BootF) of {ok, CmdList} -> CmdList; @@ -874,7 +874,7 @@ eval_script(What, #es{}) -> exit({'unexpected command in bootfile',What}). load_modules([Mod|Mods], Init) -> - File = concat([Mod,objfile_extension()]), + File = atom_to_list(Mod) ++ objfile_extension(), {ok,Full} = load_mod(Mod,File), Init ! {self(),loaded,{Mod,Full}}, %Tell init about loaded module load_modules(Mods, Init); @@ -1228,17 +1228,6 @@ set_argument([Item|Flags],Flag,Value) -> set_argument([],Flag,Value) -> [{Flag,[Value]}]. -concat([A|T]) when is_atom(A) -> - atom_to_list(A) ++ concat(T); -concat([C|T]) when is_integer(C), 0 =< C, C =< 255 -> - [C|concat(T)]; -concat([Bin|T]) when is_binary(Bin) -> - binary_to_list(Bin) ++ concat(T); -concat([S|T]) -> - S ++ concat(T); -concat([]) -> - []. - append([E]) -> E; append([H|T]) -> H ++ append(T); -- cgit v1.2.3 From 07c69ccb45b5d39493cdc830ee78fe3ec0f3d973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 27 Nov 2015 15:53:23 +0100 Subject: erl_prim_loader: Clean up splitting of filenames --- erts/preloaded/src/erl_prim_loader.erl | 55 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 28 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 041b54ee0d..dbb658c904 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -1205,24 +1205,15 @@ path_join([Path|Paths],Acc) -> name_split(ArchiveFile, File0) -> File = absname(File0), do_name_split(ArchiveFile, File). - + do_name_split(undefined, File) -> %% Ignore primary archive - case string_split(File, init:archive_extension(), []) of + RevExt = reverse(init:archive_extension()), + case archive_split(File, RevExt, []) of no_split -> - %% Plain file {file, File}; - {split, _RevArchiveBase, RevArchiveFile, []} -> - %% Top dir in archive - ArchiveFile = reverse(RevArchiveFile), - {archive, ArchiveFile, []}; - {split, _RevArchiveBase, RevArchiveFile, [$/ | FileInArchive]} -> - %% File in archive - ArchiveFile = reverse(RevArchiveFile), - {archive, ArchiveFile, FileInArchive}; - {split, _RevArchiveBase, _RevArchiveFile, _FileInArchive} -> - %% False match. Assume plain file - {file, File} + Archive -> + Archive end; do_name_split(ArchiveFile, File) -> %% Look first in primary archive @@ -1244,20 +1235,28 @@ string_match([$/ | File], [], RevTop) -> string_match(_File, _Archive, _RevTop) -> no_match. -string_split([Char | File], [Char | Ext] = FullExt, RevTop) -> - RevTop2 = [Char | RevTop], - string_split2(File, Ext, RevTop, RevTop2, File, FullExt, RevTop2); -string_split([Char | File], Ext, RevTop) -> - string_split(File, Ext, [Char | RevTop]); -string_split([], _Ext, _RevTop) -> - no_split. - -string_split2([Char | File], [Char | Ext], RevBase, RevTop, SaveFile, SaveExt, SaveTop) -> - string_split2(File, Ext, RevBase, [Char | RevTop], SaveFile, SaveExt, SaveTop); -string_split2(File, [], RevBase, RevTop, _SaveFile, _SaveExt, _SaveTop) -> - {split, RevBase, RevTop, File}; -string_split2(_, _Ext, _RevBase, _RevTop, SaveFile, SaveExt, SaveTop) -> - string_split(SaveFile, SaveExt, SaveTop). +archive_split("/"++File, RevExt, Acc) -> + case is_prefix(RevExt, Acc) of + false -> + archive_split(File, RevExt, [$/|Acc]); + true -> + ArchiveFile = reverse(Acc), + {archive, ArchiveFile, File} + end; +archive_split([H|T], RevExt, Acc) -> + archive_split(T, RevExt, [H|Acc]); +archive_split([], RevExt, Acc) -> + case is_prefix(RevExt, Acc) of + false -> + no_split; + true -> + ArchiveFile = reverse(Acc), + {archive, ArchiveFile, []} + end. + +is_prefix([H|T1], [H|T2]) -> is_prefix(T1, T2); +is_prefix([_|_], _) -> false; +is_prefix([], _ ) -> true. %% Parse list of ipv4 addresses ipv4_list([H | T]) -> -- cgit v1.2.3 From ea2481f1fdec3ce9f510201130eca51ab553fa71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 27 Nov 2015 16:06:41 +0100 Subject: erl_prim_loader: Avoid making absolute paths We don't need absolute paths unless we are dealing with archives. Since it is not free to turn a relative path absolute (we will need a call to prim_file to fetch the current directory), it's better to delay the call to absname/1 until we are sure it's needed. --- erts/preloaded/src/erl_prim_loader.erl | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index dbb658c904..91ef2bd6d0 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -1202,11 +1202,7 @@ path_join([Path],Acc) -> path_join([Path|Paths],Acc) -> path_join(Paths,"/" ++ reverse(Path) ++ Acc). -name_split(ArchiveFile, File0) -> - File = absname(File0), - do_name_split(ArchiveFile, File). - -do_name_split(undefined, File) -> +name_split(undefined, File) -> %% Ignore primary archive RevExt = reverse(init:archive_extension()), case archive_split(File, RevExt, []) of @@ -1215,12 +1211,13 @@ do_name_split(undefined, File) -> Archive -> Archive end; -do_name_split(ArchiveFile, File) -> +name_split(ArchiveFile, File0) -> %% Look first in primary archive + File = absname(File0), case string_match(real_path(File), ArchiveFile, []) of no_match -> %% Archive or plain file - do_name_split(undefined, File); + name_split(undefined, File); {match, _RevPrimArchiveFile, FileInArchive} -> %% Primary archive {archive, ArchiveFile, FileInArchive} @@ -1240,7 +1237,7 @@ archive_split("/"++File, RevExt, Acc) -> false -> archive_split(File, RevExt, [$/|Acc]); true -> - ArchiveFile = reverse(Acc), + ArchiveFile = absname(reverse(Acc)), {archive, ArchiveFile, File} end; archive_split([H|T], RevExt, Acc) -> @@ -1250,7 +1247,7 @@ archive_split([], RevExt, Acc) -> false -> no_split; true -> - ArchiveFile = reverse(Acc), + ArchiveFile = absname(reverse(Acc)), {archive, ArchiveFile, []} end. -- cgit v1.2.3 From 8a2c833d1ff02957d2fbd15640e876a5247a1b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 14 Dec 2015 13:53:05 +0100 Subject: erl_prim_loader: Clean up string_match() Part of the return value for string_match/3 is not used by its only caller. Eliminate the unused part of the return value and the accumulator argument for string_match(). --- erts/preloaded/src/erl_prim_loader.erl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 91ef2bd6d0..5f88029585 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -1214,22 +1214,22 @@ name_split(undefined, File) -> name_split(ArchiveFile, File0) -> %% Look first in primary archive File = absname(File0), - case string_match(real_path(File), ArchiveFile, []) of + case string_match(real_path(File), ArchiveFile) of no_match -> %% Archive or plain file name_split(undefined, File); - {match, _RevPrimArchiveFile, FileInArchive} -> + {match, FileInArchive} -> %% Primary archive {archive, ArchiveFile, FileInArchive} end. -string_match([Char | File], [Char | Archive], RevTop) -> - string_match(File, Archive, [Char | RevTop]); -string_match([] = File, [], RevTop) -> - {match, RevTop, File}; -string_match([$/ | File], [], RevTop) -> - {match, RevTop, File}; -string_match(_File, _Archive, _RevTop) -> +string_match([Char | File], [Char | Archive]) -> + string_match(File, Archive); +string_match([] = File, []) -> + {match, File}; +string_match([$/ | File], []) -> + {match, File}; +string_match(_File, _Archive) -> no_match. archive_split("/"++File, RevExt, Acc) -> -- cgit v1.2.3 From b74e7f4c1404a334be10c5d4a8b1ef5415405425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 Dec 2015 14:38:55 +0100 Subject: erl_prim_loader doc: Remove mention of user supplied loader Custom loaders are no longer supported. Most of the documentation for them were removed in c8a7d2d7. --- erts/doc/src/erl_prim_loader.xml | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml index 6fdec8c89e..8f66e07ae1 100644 --- a/erts/doc/src/erl_prim_loader.xml +++ b/erts/doc/src/erl_prim_loader.xml @@ -60,8 +60,6 @@ Filename is either an absolute file name or just the name of the file, for example "lists.beam". If an internal path is set to the loader, this path is used to find the file. - If a user supplied loader is used, the path can be stripped - off if it is obsolete, and the loader does not use a path. FullName is the complete name of the fetched file. Bin is the contents of the file as a binary.

-- cgit v1.2.3 From ef8a03c6dc3965ed56a746df524036b6b205feb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 Dec 2015 15:52:56 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 56052 -> 51424 bytes erts/preloaded/ebin/erlang.beam | Bin 102012 -> 102012 bytes erts/preloaded/ebin/erts_internal.beam | Bin 6260 -> 6260 bytes erts/preloaded/ebin/init.beam | Bin 48592 -> 44700 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1452 -> 1452 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1324 -> 1324 bytes erts/preloaded/ebin/prim_file.beam | Bin 44780 -> 44892 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72612 -> 72612 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23284 -> 23284 bytes erts/preloaded/ebin/zlib.beam | Bin 14160 -> 14160 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 8ccbd1e36b..6ee26b7575 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 87a48525bb..77f25653a3 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 191dd332e2..4e1cb7f8a0 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index a78d9abc20..a44b022931 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 9cc91be343..328520844d 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index e84c8ffd2d..8d6c1927fd 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index e1b2a1c8eb..1221c513db 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 8853ae8bda..fa617f1f51 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 563e604a1d..83e1e49974 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 304783ae37..8f654e3abf 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From e616e04d55b28bff5f0660eb8e3a32fffe398a13 Mon Sep 17 00:00:00 2001 From: Kenji Rikitake Date: Thu, 17 Dec 2015 20:52:54 +0900 Subject: hipe_x86_signal.c: add FreeBSD sigaction code * erts/emulator/hipe/hipe_x86_signal.c: add FreeBSD sigaction code, based on the Darwin (OS X) code --- erts/emulator/hipe/hipe_x86_signal.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index 69d4ea10c2..b7dae88417 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -234,7 +234,29 @@ static void do_init(void) #define INIT() do { if (!init_done()) do_init(); } while (0) #endif /* __sun__ */ -#if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__sun__)) +#if defined(__FreeBSD__) +/* + * This is a copy of Darwin code for FreeBSD. + * CAVEAT: detailed semantics are not verified yet. + */ +#include +static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); +#define init_done() (__next_sigaction != 0) +extern int _sigaction(int, const struct sigaction*, struct sigaction*); +#define __SIGACTION _sigaction +static void do_init(void) +{ + __next_sigaction = dlsym(RTLD_NEXT, "sigaction"); + if (__next_sigaction != 0) + return; + perror("dlsym_freebsd"); + abort(); +} +#define _NSIG NSIG +#define INIT() do { if (!init_done()) do_init(); } while (0) +#endif /* __FreeBSD__ */ + +#if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sun__)) /* * Unknown libc -- assume musl. Note: musl deliberately does not provide a musl-specific * feature test macro, so we cannot check for it. @@ -259,7 +281,7 @@ static void do_init(void) #define _NSIG NSIG #endif #define INIT() do { if (!init_done()) do_init(); } while (0) -#endif /* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __sun__) */ +#endif /* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __FreeBSD__ || __sun__) */ #if !defined(__NetBSD__) /* @@ -299,7 +321,7 @@ int __SIGACTION(int signum, const struct sigaction *act, struct sigaction *oldac /* * This catches the application's own sigaction() calls. */ -#if !defined(__DARWIN__) && !defined(__NetBSD__) +#if !defined(__DARWIN__) && !defined(__NetBSD__) && !defined(__FreeBSD__) int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { return my_sigaction(signum, act, oldact); -- cgit v1.2.3 From f77436f337d83a9751dc84d53791500c7f99c92e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 17 Dec 2015 14:11:12 +0100 Subject: Revert "Fix erroneous splitting of emulator path" This reverts commit 731890f3b4ac62eed1221aa7d9fd2bfa6bf51d8c. --- erts/etc/common/ct_run.c | 25 ++++++++++++++++++++++--- erts/etc/common/dialyzer.c | 24 +++++++++++++++++++++++- erts/etc/common/erlc.c | 22 +++++++++++++++++++++- erts/etc/common/escript.c | 25 +++++++++++++++++++++++-- erts/etc/common/typer.c | 26 ++++++++++++++++++++++---- 5 files changed, 111 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/ct_run.c b/erts/etc/common/ct_run.c index 11cec26264..548514ee6c 100644 --- a/erts/etc/common/ct_run.c +++ b/erts/etc/common/ct_run.c @@ -83,6 +83,7 @@ static int eargc; /* Number of arguments in eargv. */ static void error(char* format, ...); static char* emalloc(size_t size); static char* strsave(char* string); +static void push_words(char* src); static int run_erlang(char* name, char** argv); static char* get_default_emulator(char* progname); #ifdef __WIN32__ @@ -151,8 +152,6 @@ int main(int argc, char** argv) argv0 = argv; emulator = get_default_emulator(argv[0]); - if (strlen(emulator) >= MAXPATHLEN) - error("Emulator path length is too large"); /* * Allocate the argv vector to be used for arguments to Erlang. @@ -164,7 +163,7 @@ int main(int argc, char** argv) eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; - PUSH(strsave(emulator)); + push_words(emulator); eargc_base = eargc; eargv = eargv + eargv_size/2; eargc = 0; @@ -295,6 +294,26 @@ int main(int argc, char** argv) return run_erlang(eargv[0], eargv); } +static void +push_words(char* src) +{ + char sbuf[MAXPATHLEN]; + char* dst; + + dst = sbuf; + while ((*dst++ = *src++) != '\0') { + if (isspace((int)*src)) { + *dst = '\0'; + PUSH(strsave(sbuf)); + dst = sbuf; + do { + src++; + } while (isspace((int)*src)); + } + } + if (sbuf[0]) + PUSH(strsave(sbuf)); +} #ifdef __WIN32__ wchar_t *make_commandline(char **argv) { diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c index cac1464bf6..c45626606c 100644 --- a/erts/etc/common/dialyzer.c +++ b/erts/etc/common/dialyzer.c @@ -65,6 +65,7 @@ static int eargc; /* Number of arguments in eargv. */ static void error(char* format, ...); static char* emalloc(size_t size); static char* strsave(char* string); +static void push_words(char* src); static int run_erlang(char* name, char** argv); static char* get_default_emulator(char* progname); #ifdef __WIN32__ @@ -188,7 +189,7 @@ int main(int argc, char** argv) eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; - PUSH(strsave(emulator)); + push_words(emulator); eargc_base = eargc; eargv = eargv + eargv_size/2; eargc = 0; @@ -268,6 +269,27 @@ int main(int argc, char** argv) return run_erlang(eargv[0], eargv); } +static void +push_words(char* src) +{ + char sbuf[MAXPATHLEN]; + char* dst; + + dst = sbuf; + while ((*dst++ = *src++) != '\0') { + if (isspace((int)*src)) { + *dst = '\0'; + PUSH(strsave(sbuf)); + dst = sbuf; + do { + src++; + } while (isspace((int)*src)); + } + } + if (sbuf[0]) + PUSH(strsave(sbuf)); +} + #ifdef __WIN32__ wchar_t *make_commandline(char **argv) { diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c index 049afc526a..f9d909e01c 100644 --- a/erts/etc/common/erlc.c +++ b/erts/etc/common/erlc.c @@ -200,7 +200,7 @@ int main(int argc, char** argv) eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; - PUSH(strsave(emulator)); + push_words(emulator); eargc_base = eargc; eargv = eargv + eargv_size/2; eargc = 0; @@ -330,6 +330,26 @@ process_opt(int* pArgc, char*** pArgv, int offset) return argv[1]; } +static void +push_words(char* src) +{ + char sbuf[MAXPATHLEN]; + char* dst; + + dst = sbuf; + while ((*dst++ = *src++) != '\0') { + if (isspace((int)*src)) { + *dst = '\0'; + PUSH(strsave(sbuf)); + dst = sbuf; + do { + src++; + } while (isspace((int)*src)); + } + } + if (sbuf[0]) + PUSH(strsave(sbuf)); +} #ifdef __WIN32__ wchar_t *make_commandline(char **argv) { diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c index a5c6d0d40b..7fd02ed436 100644 --- a/erts/etc/common/escript.c +++ b/erts/etc/common/escript.c @@ -74,6 +74,7 @@ static void error(char* format, ...); static char* emalloc(size_t size); static void efree(void *p); static char* strsave(char* string); +static void push_words(char* src); static int run_erlang(char* name, char** argv); static char* get_default_emulator(char* progname); #ifdef __WIN32__ @@ -431,7 +432,7 @@ main(int argc, char** argv) emulator = get_default_emulator(argv[0]); } - if (strlen(emulator) >= MAXPATHLEN) + if (strlen(emulator) >= PMAX) error("Value of environment variable ESCRIPT_EMULATOR is too large"); /* @@ -444,7 +445,7 @@ main(int argc, char** argv) eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; - PUSH(strsave(emulator)); + push_words(emulator); eargc_base = eargc; eargv = eargv + eargv_size/2; eargc = 0; @@ -553,6 +554,26 @@ main(int argc, char** argv) return run_erlang(eargv[0], eargv); } +static void +push_words(char* src) +{ + char sbuf[PMAX]; + char* dst; + + dst = sbuf; + while ((*dst++ = *src++) != '\0') { + if (isspace((int)*src)) { + *dst = '\0'; + PUSH(strsave(sbuf)); + dst = sbuf; + do { + src++; + } while (isspace((int)*src)); + } + } + if (sbuf[0]) + PUSH(strsave(sbuf)); +} #ifdef __WIN32__ wchar_t *make_commandline(char **argv) { diff --git a/erts/etc/common/typer.c b/erts/etc/common/typer.c index 7ff8aa76e2..0aa0996808 100644 --- a/erts/etc/common/typer.c +++ b/erts/etc/common/typer.c @@ -65,6 +65,7 @@ static int eargc; /* Number of arguments in eargv. */ static void error(char* format, ...); static char* emalloc(size_t size); static char* strsave(char* string); +static void push_words(char* src); static int run_erlang(char* name, char** argv); static char* get_default_emulator(char* progname); #ifdef __WIN32__ @@ -128,9 +129,6 @@ main(int argc, char** argv) emulator = get_default_emulator(argv[0]); - if (strlen(emulator) >= MAXPATHLEN) - error("Emulator path length is too large"); - /* * Allocate the argv vector to be used for arguments to Erlang. * Arrange for starting to pushing information in the middle of @@ -141,7 +139,7 @@ main(int argc, char** argv) eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; - PUSH(strsave(emulator)); + push_words(emulator); eargc_base = eargc; eargv = eargv + eargv_size/2; eargc = 0; @@ -194,6 +192,26 @@ main(int argc, char** argv) return run_erlang(eargv[0], eargv); } +static void +push_words(char* src) +{ + char sbuf[MAXPATHLEN]; + char* dst; + + dst = sbuf; + while ((*dst++ = *src++) != '\0') { + if (isspace((int)*src)) { + *dst = '\0'; + PUSH(strsave(sbuf)); + dst = sbuf; + do { + src++; + } while (isspace((int)*src)); + } + } + if (sbuf[0]) + PUSH(strsave(sbuf)); +} #ifdef __WIN32__ wchar_t *make_commandline(char **argv) { -- cgit v1.2.3 From d38569f7deb8a5ae582e13802ae605b03d5f9498 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 17 Dec 2015 14:52:58 +0100 Subject: Update version numbers --- erts/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/vsn.mk b/erts/vsn.mk index be6cf2e376..94b5d0b169 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 7.2 +VSN = 7.2.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From a96f36890da959bcb134ac4f6d24d5132e7bb5be Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 17 Dec 2015 14:53:04 +0100 Subject: Update release notes --- erts/doc/src/notes.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 5cb9bdb690..a726cc7b97 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -32,6 +32,27 @@

This document describes the changes made to the ERTS application.

+
Erts 7.2.1 + +
Fixed Bugs and Malfunctions + + +

+ Revert "Fix erroneous splitting of emulator path"

+

+ Own Id: OTP-13202

+
+ +

+ Fix HiPE enabled emulator for FreeBSD.

+

+ Own Id: OTP-13204 Aux Id: pr926

+
+
+
+ +
+
Erts 7.2
Fixed Bugs and Malfunctions -- cgit v1.2.3 From fe72df791e7857fa72f6ac2b7ba476212a0c2edd Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 20 Dec 2015 17:02:15 +0100 Subject: efile_drv: logic error in compressed file write Compiling OTP 18.2.1 with gcc-5.3 shows the following warning: drivers/common/efile_drv.c:1538:23: warning: logical not is only applied to the left hand side of comparison [-Wlogical-not-parentheses] The code in question is: if (! (status = erts_gzwrite((ErtsGzFile)d->fd, iov[i].iov_base, iov[i].iov_len)) == iov[i].iov_len) { d->errInfo.posix_errno = d->errInfo.os_errno = errno; /* XXX Correct? */ break; } If we hoist the assignment out of the if for clarity, it becomes: status = erts_gzwrite(..., iov[i].iov_len); if (! status == iov[i].iov_len) { ...; break; } iov_len is > 0 here, and status will equal iov_len if erts_gzwrite succeeded, but will be less than iov_len if an error occurred. "! status" is 0 or 1, which can only equal iov_len if iov_len is 1 and erts_gzwrite detected an error and returned 0. The effect of this mistake is that any error when iov_len >= 2 will skip the conditional code and break statement. In particular, partial writes (0 < status && status < iov_len) will not be flagged as errors. All releases since OTP R8B-0 are affected. The variable "status" is really a boolean, which is to be set to zero on error. The fix is to set status to 1 if erts_gzwrite() returned iov_len and 0 otherwise, and to change the condition to "if (! status) ...". I'm also hoisting the assignment out of the condition since it obscures the code while providing not benefit (the condition in a while or for loop would be a different matter). --- erts/emulator/drivers/common/efile_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 3b6abec25e..a5a5dfb7f8 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -1532,10 +1532,10 @@ static void invoke_writev(void *data) { * with errno. */ errno = EINVAL; - if (! (status = - erts_gzwrite((ErtsGzFile)d->fd, - iov[i].iov_base, - iov[i].iov_len)) == iov[i].iov_len) { + status = erts_gzwrite((ErtsGzFile)d->fd, + iov[i].iov_base, + iov[i].iov_len) == iov[i].iov_len; + if (! status) { d->errInfo.posix_errno = d->errInfo.os_errno = errno; /* XXX Correct? */ break; -- cgit v1.2.3 From 47a7d8b9d701b81355a02f4bad8d16a327bc1588 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Mon, 21 Dec 2015 23:07:10 -0500 Subject: Do not allow aux work on dirty schedulers The nature of aux work is such that dirty schedulers should not attempt to perform it. Modify the code to ensure that dirty schedulers avoid aux work. Also fix an incorrect assumption about the size of a Uint in the ErtsDirtySchedId type. --- erts/emulator/beam/erl_process.c | 75 +++++++++++++++++++--------------------- erts/emulator/beam/erl_process.h | 2 +- 2 files changed, 37 insertions(+), 40 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index d583118e7b..eae05f1651 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2239,6 +2239,7 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting) erts_aint32_t aux_work = orig_aux_work; erts_aint32_t ignore = 0; + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); #ifdef ERTS_SMP haw_thr_prgr_current_reset(awdp); #endif @@ -2972,14 +2973,13 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ErtsMonotonicTime current_time; aux_work = erts_atomic32_read_acqb(&ssi->aux_work); - if (aux_work) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { + if (aux_work && !ERTS_SCHEDULER_IS_DIRTY(esdp)) { + 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 && !ERTS_SCHEDULER_IS_DIRTY(esdp) - && erts_thr_progress_update(esdp)) + if (aux_work && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); } @@ -3131,19 +3131,16 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) #endif aux_work = erts_atomic32_read_acqb(&ssi->aux_work); - if (aux_work) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { - if (!working) - sched_wall_time_change(esdp, working = 1); + if (aux_work && !ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (!working) + sched_wall_time_change(esdp, working = 1); #ifdef ERTS_SMP - if (!thr_prgr_active) - erts_thr_progress_active(esdp, thr_prgr_active = 1); + if (!thr_prgr_active) + erts_thr_progress_active(esdp, thr_prgr_active = 1); #endif - } aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); #ifdef ERTS_SMP - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && aux_work && - erts_thr_progress_update(esdp)) + if (aux_work && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); #endif } @@ -6798,18 +6795,19 @@ suspend_scheduler(ErtsSchedulerData *esdp) & ERTS_RUNQ_FLGS_QMASK); aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work|qmask) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); - sched_wall_time_change(esdp, 1); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + if (aux_work) + aux_work = handle_aux_work(&esdp->aux_work_data, + aux_work, + 1); + + if (aux_work && erts_thr_progress_update(esdp)) + erts_thr_progress_leader_update(esdp); } - if (aux_work) - aux_work = handle_aux_work(&esdp->aux_work_data, - aux_work, - 1); - - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && - (aux_work && erts_thr_progress_update(esdp))) - erts_thr_progress_leader_update(esdp); if (qmask) { #ifdef ERTS_DIRTY_SCHEDULERS if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { @@ -7026,17 +7024,18 @@ suspend_scheduler(ErtsSchedulerData *esdp) & ERTS_RUNQ_FLGS_QMASK); aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work|qmask) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); - sched_wall_time_change(esdp, 1); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + if (aux_work) + aux_work = handle_aux_work(&esdp->aux_work_data, + aux_work, + 1); + if (aux_work && erts_thr_progress_update(esdp)) + erts_thr_progress_leader_update(esdp); } - if (aux_work) - aux_work = handle_aux_work(&esdp->aux_work_data, - aux_work, - 1); - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && 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); @@ -9407,10 +9406,9 @@ Process *schedule(Process *p, int calls) suspend_scheduler(esdp); #endif - { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { erts_aint32_t aux_work; - int leader_update = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 - : erts_thr_progress_update(esdp); + int leader_update = erts_thr_progress_update(esdp); aux_work = erts_atomic32_read_acqb(&esdp->ssi->aux_work); if (aux_work | leader_update | ERTS_SCHED_FAIR) { erts_smp_runq_unlock(rq); @@ -9423,8 +9421,7 @@ Process *schedule(Process *p, int calls) erts_smp_runq_lock(rq); } - ERTS_SMP_LC_ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp) - || !erts_thr_progress_is_blocking()); + ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); } ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 10c6fa4a67..f5f50b77ba 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -607,7 +607,7 @@ typedef enum { typedef union { struct { ErtsDirtySchedulerType type: 1; - unsigned num: 31; + Uint num: sizeof(Uint)*8 - 1; } s; Uint no; } ErtsDirtySchedId; -- cgit v1.2.3 From 77f0f265f860130102acc1db31d99f8dd9e690c5 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 22 Dec 2015 18:32:10 +0100 Subject: Use monotonic time for call_time trace --- erts/emulator/beam/beam_bp.c | 99 ++++++++++++++------------------------------ erts/emulator/beam/beam_bp.h | 7 +--- 2 files changed, 32 insertions(+), 74 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 016d0aaa32..9860968687 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -75,6 +75,16 @@ extern BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */ erts_smp_atomic32_t erts_active_bp_index; erts_smp_atomic32_t erts_staging_bp_index; +/* + * Inlined helpers + */ + +static ERTS_INLINE ErtsMonotonicTime +get_mtime(Process *c_p) +{ + return erts_get_monotonic_time(ERTS_PROC_GET_SCHDATA(c_p)); +} + /* ************************************************************************* ** Local prototypes */ @@ -97,9 +107,6 @@ 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); @@ -110,13 +117,8 @@ static void uninstall_breakpoint(BeamInstr* pc); /* bp_hash */ #define BP_TIME_ADD(pi0, pi1) \ do { \ - Uint r; \ (pi0)->count += (pi1)->count; \ - (pi0)->s_time += (pi1)->s_time; \ - (pi0)->us_time += (pi1)->us_time; \ - r = (pi0)->us_time / 1000000; \ - (pi0)->s_time += r; \ - (pi0)->us_time = (pi0)->us_time % 1000000; \ + (pi0)->time += (pi1)->time; \ } while(0) static void bp_hash_init(bp_time_hash_t *hash, Uint n); @@ -948,7 +950,7 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg, void erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt) { - Uint ms,s,us; + ErtsMonotonicTime time; process_breakpoint_time_t *pbt = NULL; bp_data_time_item_t sitem, *item = NULL; bp_time_hash_t *h = NULL; @@ -961,7 +963,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt) * from the process psd */ pbt = ERTS_PROC_GET_CALL_TIME(c_p); - get_sys_now(&ms, &s, &us); + time = get_mtime(c_p); /* get pbt * timestamp = t0 @@ -976,7 +978,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt) } else { ASSERT(pbt->pc); /* add time to previous code */ - bp_time_diff(&sitem, pbt, ms, s, us); + sitem.time = time - pbt->time; sitem.pid = c_p->common.id; sitem.count = 0; @@ -1002,8 +1004,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt) /* Add count to this code */ sitem.pid = c_p->common.id; sitem.count = 1; - sitem.s_time = 0; - sitem.us_time = 0; + sitem.time = 0; /* this breakpoint */ ASSERT(bdt); @@ -1020,15 +1021,13 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt) } pbt->pc = I; - pbt->ms = ms; - pbt->s = s; - pbt->us = us; + pbt->time = time; } void erts_trace_time_return(Process *p, BeamInstr *pc) { - Uint ms,s,us; + ErtsMonotonicTime time; process_breakpoint_time_t *pbt = NULL; bp_data_time_item_t sitem, *item = NULL; bp_time_hash_t *h = NULL; @@ -1041,7 +1040,7 @@ erts_trace_time_return(Process *p, BeamInstr *pc) * from the process psd */ pbt = ERTS_PROC_GET_CALL_TIME(p); - get_sys_now(&ms,&s,&us); + time = get_mtime(p); /* get pbt * lookup bdt from code @@ -1057,7 +1056,7 @@ erts_trace_time_return(Process *p, BeamInstr *pc) */ ASSERT(pbt->pc); - bp_time_diff(&sitem, pbt, ms, s, us); + sitem.time = time - pbt->time; sitem.pid = p->common.id; sitem.count = 0; @@ -1080,9 +1079,7 @@ erts_trace_time_return(Process *p, BeamInstr *pc) } pbt->pc = pc; - pbt->ms = ms; - pbt->s = s; - pbt->us = us; + pbt->time = time; } } @@ -1183,10 +1180,14 @@ int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) { for(ix = 0; ix < hash.n; ix++) { item = &(hash.item[ix]); if (item->pid != NIL) { + ErtsMonotonicTime sec, usec; + usec = ERTS_MONOTONIC_TO_USEC(item->time); + sec = usec / 1000000; + usec = usec - sec*1000000; t = TUPLE4(hp, item->pid, make_small(item->count), - make_small(item->s_time), - make_small(item->us_time)); + make_small((Uint) sec), + make_small((Uint) usec)); hp += 5; *retval = CONS(hp, t, *retval); hp += 2; } @@ -1266,8 +1267,7 @@ static void bp_hash_rehash(bp_time_hash_t *hash, Uint n) { } item[hval].pid = hash->item[ix].pid; item[hval].count = hash->item[ix].count; - item[hval].s_time = hash->item[ix].s_time; - item[hval].us_time = hash->item[ix].us_time; + item[hval].time = hash->item[ix].time; } } @@ -1315,8 +1315,7 @@ static ERTS_INLINE bp_data_time_item_t * bp_hash_put(bp_time_hash_t *hash, bp_da item = &(hash->item[hval]); item->pid = sitem->pid; - item->s_time = sitem->s_time; - item->us_time = sitem->us_time; + item->time = sitem->time; item->count = sitem->count; hash->used++; @@ -1330,41 +1329,7 @@ static void bp_hash_delete(bp_time_hash_t *hash) { hash->item = NULL; } -static void bp_time_diff(bp_data_time_item_t *item, /* out */ - process_breakpoint_time_t *pbt, /* in */ - Uint ms, Uint s, Uint us) { - int ds,dus; -#ifdef DEBUG - int dms; - - - dms = ms - pbt->ms; -#endif - ds = s - pbt->s; - dus = us - pbt->us; - - /* get_sys_now may return zero difftime, - * this is ok. - */ - -#ifdef DEBUG - ASSERT(dms >= 0 || ds >= 0 || dus >= 0); -#endif - - if (dus < 0) { - dus += 1000000; - ds -= 1; - } - if (ds < 0) { - ds += 1000000; - } - - item->s_time = ds; - item->us_time = dus; -} - void erts_schedule_time_break(Process *p, Uint schedule) { - Uint ms, s, us; process_breakpoint_time_t *pbt = NULL; bp_data_time_item_t sitem, *item = NULL; bp_time_hash_t *h = NULL; @@ -1387,8 +1352,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) { pbdt = get_time_break(pbt->pc); if (pbdt) { - get_sys_now(&ms,&s,&us); - bp_time_diff(&sitem, pbt, ms, s, us); + sitem.time = get_mtime(p) - pbt->time; sitem.pid = p->common.id; sitem.count = 0; @@ -1410,10 +1374,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) { * timestamp it and remove the previous * timestamp in the psd. */ - get_sys_now(&ms,&s,&us); - pbt->ms = ms; - pbt->s = s; - pbt->us = us; + pbt->time = get_mtime(p); break; default : ASSERT(0); diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index 97d0539ac7..2b89d6fc71 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -29,8 +29,7 @@ typedef struct { Eterm pid; Sint count; - Uint s_time; - Uint us_time; + ErtsMonotonicTime time; } bp_data_time_item_t; typedef struct { @@ -46,9 +45,7 @@ typedef struct bp_data_time { /* Call time */ } BpDataTime; typedef struct { - Uint ms; - Uint s; - Uint us; + ErtsMonotonicTime time; BeamInstr *pc; } process_breakpoint_time_t; /* used within psd */ -- cgit v1.2.3 From 172d3bf7b28b28f3ac6ecd2348f1d8cd8db7ff7a Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 29 Dec 2015 15:26:00 +0100 Subject: Fix asynchronous BIF timer cancellation message reply --- erts/emulator/beam/erl_hl_timer.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 6853278828..734057a12c 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -2018,7 +2018,7 @@ bif_timer_access_request(void *vreq) static int try_access_sched_remote_btm(ErtsSchedulerData *esdp, Process *c_p, Uint32 sid, - Eterm tref, Uint32 *trefn, + Uint32 *trefn, int async, int cancel, int info, Eterm *resp) { @@ -2078,13 +2078,13 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp, } else { ErtsMessage *mp; - Eterm tag, res, msg; + Eterm tag, res, msg, tref; Uint hsz; Eterm *hp; ErtsProcLocks proc_locks = ERTS_PROC_LOCK_MAIN; ErlOffHeap *ohp; - hsz = 4; + hsz = 4 + REF_THING_SIZE; if (time_left > (Sint64) MAX_SMALL) hsz += ERTS_SINT64_HEAP_SIZE(time_left); @@ -2095,6 +2095,13 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp, else tag = am_read_timer; + write_ref_thing(hp, + trefn[0], + trefn[1], + trefn[2]); + tref = make_internal_ref(hp); + hp += REF_THING_SIZE; + if (time_left < 0) res = am_false; else if (time_left <= (Sint64) MAX_SMALL) @@ -2145,8 +2152,8 @@ access_bif_timer(Process *c_p, Eterm tref, int cancel, int async, int info) info); ERTS_BIF_PREP_RET(ret, res); } - else if (try_access_sched_remote_btm(esdp, c_p, sid, - tref, trefn, + else if (try_access_sched_remote_btm(esdp, c_p, + sid, trefn, async, cancel, info, &res)) { ERTS_BIF_PREP_RET(ret, res); -- cgit v1.2.3 From a23ef6e1e6e7cbce65912953ca789ac3f758952f Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 28 Dec 2015 11:36:28 +0100 Subject: Skip time_SUITE:timestamp on timewarp test --- erts/emulator/test/time_SUITE.erl | 46 ++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index 33076c7461..787870588d 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -320,7 +320,41 @@ timestamp(suite) -> timestamp(doc) -> ["Test that os:timestamp works."]; timestamp(Config) when is_list(Config) -> - repeating_timestamp_check(100000). + try + repeating_timestamp_check(100000) + catch + throw : {fail, Failure} -> + %% + %% Our time warping test machines currently warps + %% time every 6:th second. If we get a warp during + %% 10 seconds, assume this is a time warping test + %% and ignore the failure. + %% + case had_time_warp(10) of + true -> + {skip, "Seems to be time warp test run..."}; + false -> + test_server:fail(Failure) + end + end. + +os_system_time_offset() -> + erlang:convert_time_unit(os:system_time() - erlang:monotonic_time(), + native, micro_seconds). + +had_time_warp(Secs) -> + had_time_warp(os_system_time_offset(), Secs). + +had_time_warp(OrigOffs, 0) -> + false; +had_time_warp(OrigOffs, N) -> + receive after 1000 -> ok end, + case OrigOffs - os_system_time_offset() of + Diff when Diff > 500000; Diff < -500000 -> + true; + _Diff -> + had_time_warp(OrigOffs, N-1) + end. repeating_timestamp_check(0) -> ok; @@ -346,15 +380,15 @@ repeating_timestamp_check(N) -> NSecs = NA*1000000+NB+round(NC/1000000), case Secs - NSecs of TooLarge when TooLarge > 3600 -> - test_server:fail( - lists:flatten( + throw({fail, + lists:flatten( io_lib:format("os:timestamp/0 is ~w s more than erlang:now/0", - [TooLarge]))); + [TooLarge]))}); TooSmall when TooSmall < -3600 -> - test_server:fail( + throw({fail, lists:flatten( io_lib:format("os:timestamp/0 is ~w s less than erlang:now/0", - [-TooSmall]))); + [-TooSmall]))}); _ -> ok end, -- cgit v1.2.3 From 1a5aaef553493a873f2bf839119670febd4570e7 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 28 Dec 2015 15:24:56 +0100 Subject: Fix stack alignment problem in ethread test on arm --- erts/test/ethread_SUITE_data/ethread_tests.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/test/ethread_SUITE_data/ethread_tests.c b/erts/test/ethread_SUITE_data/ethread_tests.c index 12f7f3db7a..b51771c736 100644 --- a/erts/test/ethread_SUITE_data/ethread_tests.c +++ b/erts/test/ethread_SUITE_data/ethread_tests.c @@ -1457,6 +1457,9 @@ do { \ ASSERT(ethr_ ## A ## _read ## B(&A) == 0x33333333); \ } while (0) +ethr_atomic32_t atomic32; +ethr_atomic_t atomic; +ethr_dw_atomic_t dw_atomic; static void atomic_basic_test(void) @@ -1465,8 +1468,6 @@ atomic_basic_test(void) * Verify that each op does what it is expected * to do for at least one input. */ - ethr_atomic32_t atomic32; - ethr_atomic_t atomic; print_line("AT_AINT32_MAX=%d",AT_AINT32_MAX); print_line("AT_AINT32_MIN=%d",AT_AINT32_MIN); @@ -1629,7 +1630,6 @@ atomic_basic_test(void) /* Double word */ { - ethr_dw_atomic_t dw_atomic; ethr_dw_sint_t dw0, dw1; dw0.sint[0] = 4711; dw0.sint[1] = 4712; -- cgit v1.2.3 From 9293a250e02e63d295a841786f03c3e3a3f30e97 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 29 Dec 2015 15:45:20 +0100 Subject: Fix HL timer hard debug implementation --- erts/emulator/beam/erl_hl_timer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 51a0d68247..fb6d249145 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -1055,6 +1055,8 @@ create_hl_timer(ErtsSchedulerData *esdp, erts_aint32_t refc; Uint32 roflgs; + ERTS_HLT_HDBG_CHK_SRV(srv); + check_canceled_queue(esdp, srv); ERTS_HLT_ASSERT((esdp->no & ~ERTS_TMR_ROFLG_SID_MASK) == 0); @@ -1179,8 +1181,6 @@ create_hl_timer(ErtsSchedulerData *esdp, erts_smp_atomic32_init_nob(&tmr->head.refc, refc); erts_smp_atomic32_init_nob(&tmr->state, ERTS_TMR_STATE_ACTIVE); - ERTS_HLT_HDBG_CHK_SRV(srv); - if (!srv->next_timeout || tmr->timeout < srv->next_timeout->timeout) { if (srv->next_timeout) @@ -3099,7 +3099,8 @@ tt_hdbg_func(ErtsHLTimer *tmr, void *vhdbg) & ~ERTS_HLT_PFLGS_MASK); ERTS_HLT_ASSERT(tmr == prnt); } - ERTS_HLT_ASSERT(btm_rbt_lookup(hdbg->srv->btm_tree, tmr->btm.refn) == tmr); + if (tmr->head.roflgs & ERTS_TMR_ROFLG_BIF_TMR) + ERTS_HLT_ASSERT(btm_rbt_lookup(hdbg->srv->btm_tree, tmr->btm.refn) == tmr); if (tmr->time.tree.same_time) { ErtsHdbgHLT st_hdbg; st_hdbg.srv = hdbg->srv; -- cgit v1.2.3 From 37f58ad6bff2bf2bac4f3f20c2684e8cee66af03 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 15 Dec 2015 09:40:30 +0100 Subject: Light weight statistics of run queue lengths - statistics(total_run_queue_lengths) - statistics(run_queue_lengths) - statistics(total_active_tasks) - statistics(active_tasks) Conflicts: erts/emulator/beam/erl_process.c --- erts/doc/src/erlang.xml | 111 ++++++++++++++++++++++++++++---- erts/emulator/beam/atom.names | 4 ++ erts/emulator/beam/erl_bif_info.c | 37 ++++++++++- erts/emulator/beam/erl_process.c | 65 +++++++++++++------ erts/emulator/beam/erl_process.h | 48 +++++++++----- erts/emulator/test/statistics_SUITE.erl | 61 +++++++++++++++++- erts/preloaded/ebin/erlang.beam | Bin 101796 -> 102000 bytes erts/preloaded/src/erlang.erl | 10 ++- 8 files changed, 281 insertions(+), 55 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index c37ed3bea5..64eebec936 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5684,8 +5684,31 @@ true
Dest, Msg, []).

+ + Information about active processes and ports. + +

+ Returns a list where each element represents the amount + of active processes and ports on each run queue and its + associated scheduler. That is, the number of processes and + ports that are ready to run, or are currently running. The + element location in the list corresponds to the scheduler + and its run queue. The first element corresponds to scheduler + number 1 and so on. The information is not gathered + atomically. That is, the result is not necessarily a + consistent snapshot of the state, but instead quite + efficiently gathered. See also, + statistics(total_active_tasks), + statistics(run_queue_lengths), and + statistics(total_run_queue_lengths). +

+
+
+ + + Information about context switches.

Returns the total number of context switches since the @@ -5694,7 +5717,7 @@ true - + Information about exact reductions. @@ -5708,7 +5731,7 @@ true - + Information about garbage collection.

Returns information about garbage collection, for example:

@@ -5720,7 +5743,7 @@ true
- + Information about I/O.

Returns Input, @@ -5731,7 +5754,7 @@ true - + Information about reductions. @@ -5749,16 +5772,43 @@ true - - Information about the run-queue. - -

Returns the total length of run-queues, that is, the number - of processes that are ready to run on all available run-queues.

+ + Information about the run-queues. + +

+ Returns the total length of the run-queues. That is, the number + of processes and ports that are ready to run on all available + run-queues. The information is gathered atomically. That + is, the result is a consistent snapshot of the state, but + this operation is much more expensive compared to + statistics(total_run_queue_lengths). + This especially when a large amount of schedulers is used. +

- + + Information about the run-queue lengths. + +

+ Returns a list where each element represents the amount + of processes and ports ready to run for each run queue. The + element location in the list corresponds to the run queue + of a scheduler. The first element corresponds to the run + queue of scheduler number 1 and so on. The information is + not gathered atomically. That is, the result is + not necessarily a consistent snapshot of the state, but + instead quite efficiently gathered. See also, + statistics(total_run_queue_lengths), + statistics(active_tasks), and + statistics(total_active_tasks). +

+
+
+ + + Information about runtime.

Returns information about runtime, in milliseconds.

@@ -5773,7 +5823,7 @@ true
- + Information about each schedulers work time. @@ -5844,7 +5894,44 @@ ok - + + Information about active processes and ports. + +

+ Returns the total amount of active processes and ports in + the system. That is, the number of processes and ports that + are ready to run, or are currently running. The information + is not gathered atomically. That is, the result + is not necessarily a consistent snapshot of the state, but + instead quite efficiently gathered. See also, + statistics(active_tasks), + statistics(run_queue_lengths), and + statistics(total_run_queue_lengths). +

+
+
+ + + + Information about the run-queue lengths. + +

+ Returns the total length of the run-queues. That is, the number + of processes and ports that are ready to run on all available + run-queues. The information is not gathered atomically. + That is, the result is not necessarily a consistent snapshot of + the state, but much more efficiently gathered compared to + statistics(run_queue). + See also, + statistics(run_queue_lengths), + statistics(total_active_tasks), and + statistics(active_tasks). +

+
+
+ + + Information about wall clock.

Returns information about wall clock. wall_clock can diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 190e7817dc..fb3368eae2 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -71,6 +71,7 @@ atom absoluteURI atom ac atom accessor atom active +atom active_tasks atom all atom all_but_first atom all_names @@ -512,6 +513,7 @@ atom return_from atom return_to atom return_trace atom run_queue +atom run_queue_lengths atom runnable atom runnable_ports atom runnable_procs @@ -579,7 +581,9 @@ atom timeout_value atom Times='*' atom timestamp atom total +atom total_active_tasks atom total_heap_size +atom total_run_queue_lengths atom tpkt atom trace trace_ts traced atom trace_control_word diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b44382cde8..414ff6711a 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3234,6 +3234,39 @@ BIF_RETTYPE statistics_1(BIF_ALIST_1) if (is_non_value(res)) BIF_RET(am_undefined); BIF_TRAP1(gather_sched_wall_time_res_trap, BIF_P, res); + } else if (BIF_ARG_1 == am_total_active_tasks + || BIF_ARG_1 == am_total_run_queue_lengths) { + Uint no = erts_run_queues_len(NULL, 0, BIF_ARG_1 == am_total_active_tasks); + if (IS_USMALL(0, no)) + res = make_small(no); + else { + Eterm *hp = HAlloc(BIF_P, BIG_UINT_HEAP_SIZE); + res = uint_to_big(no, hp); + } + BIF_RET(res); + } else if (BIF_ARG_1 == am_active_tasks + || BIF_ARG_1 == am_run_queue_lengths) { + Eterm res, *hp, **hpp; + Uint sz, *szp; + int no_qs = erts_no_run_queues; + Uint *qszs = erts_alloc(ERTS_ALC_T_TMP,sizeof(Uint)*no_qs*2); + (void) erts_run_queues_len(qszs, 0, BIF_ARG_1 == am_active_tasks); + sz = 0; + szp = &sz; + hpp = NULL; + while (1) { + int i; + for (i = 0; i < no_qs; i++) + qszs[no_qs+i] = erts_bld_uint(hpp, szp, qszs[i]); + res = erts_bld_list(hpp, szp, no_qs, &qszs[no_qs]); + if (hpp) { + erts_free(ERTS_ALC_T_TMP, qszs); + BIF_RET(res); + } + hp = HAlloc(BIF_P, sz); + szp = NULL; + hpp = &hp; + } } else if (BIF_ARG_1 == am_context_switches) { Eterm cs = erts_make_integer(erts_get_total_context_switches(), BIF_P); hp = HAlloc(BIF_P, 3); @@ -3282,7 +3315,7 @@ BIF_RETTYPE statistics_1(BIF_ALIST_1) res = TUPLE2(hp, b1, b2); BIF_RET(res); } else if (BIF_ARG_1 == am_run_queue) { - res = erts_run_queues_len(NULL); + res = erts_run_queues_len(NULL, 1, 0); BIF_RET(make_small(res)); } else if (BIF_ARG_1 == am_wall_clock) { UWord w1, w2; @@ -3302,7 +3335,7 @@ BIF_RETTYPE statistics_1(BIF_ALIST_1) Uint sz, *szp; int no_qs = erts_no_run_queues; Uint *qszs = erts_alloc(ERTS_ALC_T_TMP,sizeof(Uint)*no_qs*2); - (void) erts_run_queues_len(qszs); + (void) erts_run_queues_len(qszs, 0, 0); sz = 0; szp = &sz; hpp = NULL; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index d583118e7b..b47aae0f74 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -3149,7 +3149,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) } #ifndef ERTS_SMP - if (rq->len != 0 || rq->misc.start) + if (erts_smp_atomic32_read_dirty(&rq->len) != 0 || rq->misc.start) goto sys_woken; #else flgs = erts_smp_atomic32_read_acqb(&ssi->flags); @@ -3248,7 +3248,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) } #ifndef ERTS_SMP - if (rq->len == 0 && !rq->misc.start) + if (erts_smp_atomic32_read_dirty(&rq->len) == 0 && !rq->misc.start) goto sys_aux_work; sys_woken: #else @@ -4965,7 +4965,7 @@ erts_fprintf(stderr, "--------------------------------\n"); rq->out_of_work_count = 0; (void) ERTS_RUNQ_FLGS_READ_BSET(rq, ERTS_RUNQ_FLGS_MIGRATION_INFO, flags); - rq->max_len = rq->len; + rq->max_len = erts_smp_atomic32_read_dirty(&rq->len); for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) { ErtsRunQueueInfo *rqi; rqi = (pix == ERTS_PORT_PRIO_LEVEL @@ -5123,7 +5123,7 @@ wakeup_other_check(ErtsRunQueue *rq, Uint32 flags) { int wo_reds = rq->wakeup_other_reds; if (wo_reds) { - int left_len = rq->len - 1; + int left_len = erts_smp_atomic32_read_dirty(&rq->len) - 1; if (left_len < 1) { int wo_reduce = wo_reds << wakeup_other.dec_shift; wo_reduce &= wakeup_other.dec_mask; @@ -5196,7 +5196,7 @@ wakeup_other_check_legacy(ErtsRunQueue *rq, Uint32 flags) { int wo_reds = rq->wakeup_other_reds; if (wo_reds) { - erts_aint32_t len = rq->len; + erts_aint32_t len = erts_smp_atomic32_read_dirty(&rq->len); if (len < 2) { rq->wakeup_other -= ERTS_WAKEUP_OTHER_DEC_LEGACY*wo_reds; if (rq->wakeup_other < 0) @@ -5292,7 +5292,7 @@ runq_supervisor(void *unused) ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); if (ERTS_RUNQ_FLGS_GET(rq) & ERTS_RUNQ_FLG_NONEMPTY) { erts_smp_runq_lock(rq); - if (rq->len != 0) + if (erts_smp_atomic32_read_dirty(&rq->len) != 0) wake_scheduler_on_empty_runq(rq); /* forced wakeup... */ erts_smp_runq_unlock(rq); } @@ -5642,7 +5642,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online } rq->out_of_work_count = 0; rq->max_len = 0; - rq->len = 0; + erts_smp_atomic32_set_nob(&rq->len, 0); rq->wakeup_other = 0; rq->wakeup_other_reds = 0; rq->halt_in_progress = 0; @@ -7939,6 +7939,9 @@ sched_thread_func(void *vesdp) erts_sched_init_time_sup(esdp); + (void) ERTS_RUNQ_FLGS_SET_NOB(esdp->run_queue, + ERTS_RUNQ_FLG_EXEC); + #ifdef ERTS_SMP tse = erts_tse_fetch(); erts_tse_prepare_timed(tse); @@ -8947,24 +8950,39 @@ resume_process_1(BIF_ALIST_1) } Uint -erts_run_queues_len(Uint *qlen) +erts_run_queues_len(Uint *qlen, int atomic_queues_read, int incl_active_sched) { int i = 0; Uint len = 0; - ERTS_ATOMIC_FOREACH_RUNQ(rq, - { - 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 (atomic_queues_read) + ERTS_ATOMIC_FOREACH_RUNQ(rq, + { + Sint rq_len = (Sint) erts_smp_atomic32_read_dirty(&rq->len); + ASSERT(rq_len >= 0); + if (incl_active_sched + && (ERTS_RUNQ_FLGS_GET_NOB(rq) & ERTS_RUNQ_FLG_EXEC)) { + rq_len++; + } + if (qlen) + qlen[i++] = rq_len; + len += (Uint) rq_len; + } + ); + else { + for (i = 0; i < erts_no_run_queues; i++) { + ErtsRunQueue *rq = ERTS_RUNQ_IX(i); + Sint rq_len = (Sint) erts_smp_atomic32_read_nob(&rq->len); + ASSERT(rq_len >= 0); + if (incl_active_sched + && (ERTS_RUNQ_FLGS_GET_NOB(rq) & ERTS_RUNQ_FLG_EXEC)) { + rq_len++; + } + if (qlen) + qlen[i] = rq_len; + len += (Uint) rq_len; + } - if (pqlen < 0) - pqlen = 0; - if (qlen) - qlen[i++] = pqlen; - len += pqlen; } - ); return len; } @@ -9391,8 +9409,10 @@ Process *schedule(Process *p, int calls) if (flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND|ERTS_RUNQ_FLG_SUSPENDED)) { if (flags & ERTS_RUNQ_FLG_SUSPENDED) { + (void) ERTS_RUNQ_FLGS_UNSET_NOB(rq, ERTS_RUNQ_FLG_EXEC); suspend_scheduler(esdp); - flags = ERTS_RUNQ_FLGS_GET_NOB(rq); + flags = ERTS_RUNQ_FLGS_SET_NOB(rq, ERTS_RUNQ_FLG_EXEC); + flags |= ERTS_RUNQ_FLG_EXEC; } if (flags & ERTS_RUNQ_FLG_CHK_CPU_BIND) { flags = ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND); @@ -9483,7 +9503,10 @@ Process *schedule(Process *p, int calls) } #endif + (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_EXEC); scheduler_wait(&fcalls, esdp, rq); + flags = ERTS_RUNQ_FLGS_SET_NOB(rq, ERTS_RUNQ_FLG_EXEC); + flags |= ERTS_RUNQ_FLG_EXEC; #ifdef ERTS_SMP non_empty_runq(rq); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 10c6fa4a67..fd7d4183f4 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -170,8 +170,10 @@ extern int erts_sched_thread_suggested_stack_size; (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 5)) #define ERTS_RUNQ_FLG_PROTECTED \ (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 6)) +#define ERTS_RUNQ_FLG_EXEC \ + (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 7)) -#define ERTS_RUNQ_FLG_MAX (ERTS_RUNQ_FLG_BASE2 + 7) +#define ERTS_RUNQ_FLG_MAX (ERTS_RUNQ_FLG_BASE2 + 8) #define ERTS_RUNQ_FLGS_MIGRATION_QMASKS \ (ERTS_RUNQ_FLGS_EMIGRATE_QMASK \ @@ -215,6 +217,9 @@ extern int erts_sched_thread_suggested_stack_size; #define ERTS_RUNQ_FLGS_SET(RQ, FLGS) \ ((Uint32) erts_smp_atomic32_read_bor_relb(&(RQ)->flags, \ (erts_aint32_t) (FLGS))) +#define ERTS_RUNQ_FLGS_SET_NOB(RQ, FLGS) \ + ((Uint32) erts_smp_atomic32_read_bor_nob(&(RQ)->flags, \ + (erts_aint32_t) (FLGS))) #define ERTS_RUNQ_FLGS_BSET(RQ, MSK, FLGS) \ ((Uint32) erts_smp_atomic32_read_bset_relb(&(RQ)->flags, \ (erts_aint32_t) (MSK), \ @@ -222,6 +227,9 @@ extern int erts_sched_thread_suggested_stack_size; #define ERTS_RUNQ_FLGS_UNSET(RQ, FLGS) \ ((Uint32) erts_smp_atomic32_read_band_relb(&(RQ)->flags, \ (erts_aint32_t) ~(FLGS))) +#define ERTS_RUNQ_FLGS_UNSET_NOB(RQ, FLGS) \ + ((Uint32) erts_smp_atomic32_read_band_nob(&(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) \ @@ -467,7 +475,7 @@ struct ErtsRunQueue_ { int full_reds_history[ERTS_FULL_REDS_HISTORY_SIZE]; int out_of_work_count; erts_aint32_t max_len; - erts_aint32_t len; + erts_smp_atomic32_t len; int wakeup_other; int wakeup_other_reds; int halt_in_progress; @@ -728,7 +736,19 @@ erts_smp_inc_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio) ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); - len = erts_smp_atomic32_read_nob(&rqi->len); + len = erts_smp_atomic32_read_dirty(&rq->len); + +#ifdef ERTS_SMP + if (len == 0) + erts_non_empty_runq(rq); +#endif + len++; + if (rq->max_len < len) + rq->max_len = len; + ASSERT(len > 0); + erts_smp_atomic32_set_nob(&rq->len, len); + + len = erts_smp_atomic32_read_dirty(&rqi->len); ASSERT(len >= 0); if (len == 0) { ASSERT((erts_smp_atomic32_read_nob(&rq->flags) @@ -741,15 +761,6 @@ erts_smp_inc_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio) rqi->max_len = len; erts_smp_atomic32_set_relb(&rqi->len, len); - -#ifdef ERTS_SMP - if (rq->len == 0) - erts_non_empty_runq(rq); -#endif - rq->len++; - if (rq->max_len < rq->len) - rq->max_len = len; - ASSERT(rq->len > 0); } ERTS_GLB_INLINE void @@ -759,7 +770,12 @@ erts_smp_dec_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio) ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); - len = erts_smp_atomic32_read_nob(&rqi->len); + len = erts_smp_atomic32_read_dirty(&rq->len); + len--; + ASSERT(len >= 0); + erts_smp_atomic32_set_nob(&rq->len, len); + + len = erts_smp_atomic32_read_dirty(&rqi->len); len--; ASSERT(len >= 0); if (len == 0) { @@ -770,8 +786,6 @@ erts_smp_dec_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio) } erts_smp_atomic32_set_relb(&rqi->len, len); - rq->len--; - ASSERT(rq->len >= 0); } ERTS_GLB_INLINE void @@ -781,7 +795,7 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi) ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); - len = erts_smp_atomic32_read_nob(&rqi->len); + len = erts_smp_atomic32_read_dirty(&rqi->len); ASSERT(rqi->max_len >= len); rqi->max_len = len; } @@ -1678,7 +1692,7 @@ void erts_sched_notify_check_cpu_bind(void); Uint erts_active_schedulers(void); void erts_init_process(int, int, int); Eterm erts_process_status(Process *, ErtsProcLocks, Process *, Eterm); -Uint erts_run_queues_len(Uint *); +Uint erts_run_queues_len(Uint *, int, int); void erts_add_to_runq(Process *); Eterm erts_bound_schedulers_term(Process *c_p); Eterm erts_get_cpu_topology_term(Process *c_p, Eterm which); diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index 56ecf4195a..53c9ba8715 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -32,7 +32,7 @@ run_queue_one/1, scheduler_wall_time/1, reductions/1, reductions_big/1, garbage_collection/1, io/1, - badarg/1]). + badarg/1, run_queues_lengths_active_tasks/1]). %% Internal exports. @@ -54,7 +54,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [{group, wall_clock}, {group, runtime}, reductions, reductions_big, {group, run_queue}, scheduler_wall_time, - garbage_collection, io, badarg]. + garbage_collection, io, badarg, + run_queues_lengths_active_tasks]. groups() -> [{wall_clock, [], @@ -409,3 +410,59 @@ badarg(Config) when is_list(Config) -> ?line case catch statistics(bad_atom) of {'EXIT', {badarg, _}} -> ok end. + +tok_loop() -> + tok_loop(). + +run_queues_lengths_active_tasks(Config) -> + TokLoops = lists:map(fun (_) -> + spawn_opt(fun () -> + tok_loop() + end, + [link, {priority, low}]) + end, + lists:seq(1,10)), + + TRQLs0 = statistics(total_run_queue_lengths), + TATs0 = statistics(total_active_tasks), + true = is_integer(TRQLs0), + true = is_integer(TATs0), + true = TRQLs0 >= 0, + true = TATs0 >= 11, + + NoScheds = erlang:system_info(schedulers), + RQLs0 = statistics(run_queue_lengths), + ATs0 = statistics(active_tasks), + NoScheds = length(RQLs0), + NoScheds = length(ATs0), + true = lists:sum(RQLs0) >= 0, + true = lists:sum(ATs0) >= 11, + + SO = erlang:system_flag(schedulers_online, 1), + + TRQLs1 = statistics(total_run_queue_lengths), + TATs1 = statistics(total_active_tasks), + true = TRQLs1 >= 10, + true = TATs1 >= 11, + NoScheds = erlang:system_info(schedulers), + + RQLs1 = statistics(run_queue_lengths), + ATs1 = statistics(active_tasks), + NoScheds = length(RQLs1), + NoScheds = length(ATs1), + TRQLs2 = lists:sum(RQLs1), + TATs2 = lists:sum(ATs1), + true = TRQLs2 >= 10, + true = TATs2 >= 11, + [TRQLs2|_] = RQLs1, + [TATs2|_] = ATs1, + + erlang:system_flag(schedulers_online, SO), + + lists:foreach(fun (P) -> + unlink(P), + exit(P, bang) + end, + TokLoops), + + ok. diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index cd2e7f18a2..58516c0ff3 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 7280b43502..5fc6d14938 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2205,7 +2205,9 @@ setelement(_Index, _Tuple1, _Value) -> spawn_opt(_Tuple) -> erlang:nif_error(undefined). --spec statistics(context_switches) -> {ContextSwitches,0} when +-spec statistics(active_tasks) -> [ActiveTasks] when + ActiveTasks :: non_neg_integer(); + (context_switches) -> {ContextSwitches,0} when ContextSwitches :: non_neg_integer(); (exact_reductions) -> {Total_Exact_Reductions, Exact_Reductions_Since_Last_Call} when @@ -2222,6 +2224,8 @@ spawn_opt(_Tuple) -> Total_Reductions :: non_neg_integer(), Reductions_Since_Last_Call :: non_neg_integer(); (run_queue) -> non_neg_integer(); + (run_queue_lengths) -> [RunQueueLenght] when + RunQueueLenght :: 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(); @@ -2229,6 +2233,10 @@ spawn_opt(_Tuple) -> SchedulerId :: pos_integer(), ActiveTime :: non_neg_integer(), TotalTime :: non_neg_integer(); + (total_active_tasks) -> ActiveTasks when + ActiveTasks :: non_neg_integer(); + (total_run_queue_lengths) -> TotalRunQueueLenghts when + TotalRunQueueLenghts :: non_neg_integer(); (wall_clock) -> {Total_Wallclock_Time, Wallclock_Time_Since_Last_Call} when Total_Wallclock_Time :: non_neg_integer(), -- cgit v1.2.3 From 03b00985c181f90a927b9af5316d27a534398c24 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 7 Jan 2016 17:14:04 +0100 Subject: erts: Add config test for MAP_NORESERVE for hipe on 64-bit to get a nicer error on FreeBSD and others that does not support MAP_NORESERVE for mmap. Q: How to support this? A: Implement the "literal tag" in hipe or another way to reserve virtual address space in erl_mmap.c --- erts/configure.in | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 20075b08c9..2f19c0f760 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2765,6 +2765,21 @@ LM_SYS_IPV6 LM_SYS_MULTICAST ERL_TIME_CORRECTION AC_CHECK_PROG(M4, m4, m4) + + +dnl HiPE cannot run on 64-bit without MAP_FIXED and MAP_NORESERVE +if test X${enable_hipe} != Xno && test X$ac_cv_sizeof_void_p != X4; then + AC_CHECK_DECLS([MAP_FIXED, MAP_NORESERVE], [], [], [#include ]) + if test X$ac_cv_have_decl_MAP_FIXED != Xyes || test X$ac_cv_have_decl_MAP_NORESERVE != Xyes; then + if test X${enable_hipe} = Xyes; then + AC_MSG_ERROR([HiPE on 64-bit needs MAP_FIXED and MAP_NORESERVE flags for mmap()]) + else + enable_hipe=no + AC_MSG_WARN([Disable HiPE due to lack of MAP_FIXED and MAP_NORESERVE flags for mmap()]) + fi + fi +fi + dnl check to auto-enable hipe here... if test "$cross_compiling" != "yes" && test X${enable_hipe} != Xno; then if test -z "$M4"; then -- cgit v1.2.3 From b18d251931a43099cc18527bcf6898fff1b144f5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 8 Jan 2016 14:56:28 +0100 Subject: erts: Refactor ERL_NIF_INIT macro --- erts/emulator/beam/erl_nif.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 1a21048ec9..6f1b7a7571 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -243,20 +243,20 @@ extern TWinDynNifCallbacks WinDynNifCallbacks; #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_INIT_GLOB TWinDynNifCallbacks WinDynNifCallbacks; -# ifdef STATIC_ERLANG_NIF -# define ERL_NIF_INIT_DECL(MODNAME) __declspec(dllexport) ErlNifEntry* MODNAME ## _nif_init(TWinDynNifCallbacks* callbacks) -# else -# define ERL_NIF_INIT_DECL(MODNAME) __declspec(dllexport) ErlNifEntry* nif_init(TWinDynNifCallbacks* callbacks) -# endif +# define ERL_NIF_INIT_ARGS TWinDynNifCallbacks* callbacks # define ERL_NIF_INIT_BODY memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks)) +# define ERL_NIF_INIT_EXPORT __declspec(dllexport) #else # define ERL_NIF_INIT_GLOB +# define ERL_NIF_INIT_ARGS void # define ERL_NIF_INIT_BODY -# ifdef STATIC_ERLANG_NIF -# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _nif_init(void) -# else -# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void) -# endif +# define ERL_NIF_INIT_EXPORT +#endif + +#ifdef STATIC_ERLANG_NIF +# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _nif_init(ERL_NIF_INIT_ARGS) +#else +# define ERL_NIF_INIT_DECL(MODNAME) ERL_NIF_INIT_EXPORT ErlNifEntry* nif_init(ERL_NIF_INIT_ARGS) #endif #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -- cgit v1.2.3 From 52fc89120c2f2237ec5191e72d844a6dca150040 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 8 Jan 2016 16:41:21 +0100 Subject: erts: Cleanup erl_driver.h for windows The comment is misleading and no need to "export" static windows drivers. DRIVER_INIT for dynamic windows drivers is defined in erl_win_dyn_driver.h --- erts/emulator/beam/erl_driver.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index bda4d5d1c6..268bda8b58 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -382,18 +382,11 @@ typedef struct erl_drv_entry { # define ERLANG_DRIVER_NAME(NAME) driver_init #endif -/* For windows dynamic drivers */ #ifndef ERL_DRIVER_TYPES_ONLY -#if defined(__WIN32__) -# define DRIVER_INIT(DRIVER_NAME) \ - __declspec(dllexport) ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void); \ - __declspec(dllexport) ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void) -#else # define DRIVER_INIT(DRIVER_NAME) \ ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void); \ ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void) -#endif #define ERL_DRV_BUSY_MSGQ_DISABLED (~((ErlDrvSizeT) 0)) #define ERL_DRV_BUSY_MSGQ_READ_ONLY ((ErlDrvSizeT) 0) -- cgit v1.2.3 From 1117dd3651be3cf763abf6fedcd4e06a6444fa04 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 8 Jan 2016 15:22:52 +0100 Subject: erts: Allow -fvisibility=hidden for NIFs and drivers as is strongly recommended by gcc man page. We use __attribute__ ((visibility("default"))) to make sure the init functions are properly exported. --- erts/emulator/beam/erl_driver.h | 14 +++++++++++--- erts/emulator/beam/erl_nif.h | 8 +++++++- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 268bda8b58..0636171ad1 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -378,15 +378,23 @@ typedef struct erl_drv_entry { #ifdef STATIC_ERLANG_DRIVER # define ERLANG_DRIVER_NAME(NAME) NAME ## _driver_init +# define ERL_DRIVER_EXPORT #else # define ERLANG_DRIVER_NAME(NAME) driver_init +# if defined(__GNUC__) && __GNUC__ >= 4 +# define ERL_DRIVER_EXPORT __attribute__ ((visibility("default"))) +# elif defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550) +# define ERL_DRIVER_EXPORT __global +# else +# define ERL_DRIVER_EXPORT +# endif #endif #ifndef ERL_DRIVER_TYPES_ONLY -# define DRIVER_INIT(DRIVER_NAME) \ - ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void); \ - ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void) +#define DRIVER_INIT(DRIVER_NAME) \ + ERL_DRIVER_EXPORT ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void); \ + ERL_DRIVER_EXPORT ErlDrvEntry* ERLANG_DRIVER_NAME(DRIVER_NAME)(void) #define ERL_DRV_BUSY_MSGQ_DISABLED (~((ErlDrvSizeT) 0)) #define ERL_DRV_BUSY_MSGQ_READ_ONLY ((ErlDrvSizeT) 0) diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 6f1b7a7571..40c2ad6f08 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -250,7 +250,13 @@ extern TWinDynNifCallbacks WinDynNifCallbacks; # define ERL_NIF_INIT_GLOB # define ERL_NIF_INIT_ARGS void # define ERL_NIF_INIT_BODY -# define ERL_NIF_INIT_EXPORT +# if defined(__GNUC__) && __GNUC__ >= 4 +# define ERL_NIF_INIT_EXPORT __attribute__ ((visibility("default"))) +# elif defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550) +# define ERL_NIF_INIT_EXPORT __global +# else +# define ERL_NIF_INIT_EXPORT +# endif #endif #ifdef STATIC_ERLANG_NIF -- cgit v1.2.3 From ae116fb714d54ca40bcd897195de521b0ca39f8c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 11 Jan 2016 17:20:24 +0100 Subject: erts: Workaround memset bug in test case memset seen to fail with values larger than 255 on (armata) 32-bit ARM Debian with EGLIBC 2.13-38+rpi2+deb7u8 and gcc 4.6.3-14+rpi1. --- erts/emulator/test/alloc_SUITE_data/threads.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE_data/threads.c b/erts/emulator/test/alloc_SUITE_data/threads.c index edad24ee6b..f0711ee6e7 100644 --- a/erts/emulator/test/alloc_SUITE_data/threads.c +++ b/erts/emulator/test/alloc_SUITE_data/threads.c @@ -397,7 +397,7 @@ alloc_op(int t_no, Allctr_t *a, block *bp, int id, int clean_up) bp->p = (unsigned char *) ALLOC(a, bp->s); if(!bp->p) fail(t_no, "ALLOC(%lu) failed [id=%d])\n", bp->s, id); - memset((void *) bp->p, id, (size_t) bp->s); + memset((void *) bp->p, (unsigned char)id, (size_t) bp->s); } else { unsigned char *p = (unsigned char *) REALLOC(a, bp->p, bp->as[bp->i]); @@ -407,7 +407,7 @@ alloc_op(int t_no, Allctr_t *a, block *bp, int id, int clean_up) if(bp->s < bp->as[bp->i]) { CHECK_BLOCK_DATA(t_no, p, bp->s, id); - memset((void *) p, id, (size_t) bp->as[bp->i]); + memset((void *) p, (unsigned char)id, (size_t) bp->as[bp->i]); } else CHECK_BLOCK_DATA(t_no, p, bp->as[bp->i], id); -- cgit v1.2.3 From e293ad1b08b2f937555a102e6f3b4336574773c8 Mon Sep 17 00:00:00 2001 From: Serge Aleynikov Date: Wed, 30 Dec 2015 13:29:34 -0500 Subject: Assign externally open fd to gen_tcp (UDS support) When a AF_LOCAL file descriptor is created externally (e.g. Unix Domain Socket) and passed to `gen_tcp:listen(0, [{fd, FD}])`, the implementation incorrectly assigned the address family to be equal to `inet`, which in the inet_drv driver translated to AF_INET instead of AF_LOCAL (or AF_UNIX), and an `einval` error code was returned. This patch fixes this problem such that the file descriptors of the `local` address family are supported in the inet:fdopen/5, gen_tcp:connect/3, gen_tcp:listen/2, gen_udp:open/2 calls --- erts/configure.in | 2 +- erts/emulator/drivers/common/inet_drv.c | 161 +++++++++++++++++++++++++++++--- erts/preloaded/src/prim_inet.erl | 17 +++- 3 files changed, 162 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 4fb725ff00..68ead3285b 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2238,7 +2238,7 @@ fi dnl Need by run_erl. AC_CHECK_FUNCS([openpty]) -AC_CHECK_HEADERS(net/if_dl.h ifaddrs.h netpacket/packet.h) +AC_CHECK_HEADERS(net/if_dl.h ifaddrs.h netpacket/packet.h sys/un.h) AC_CHECK_FUNCS([getifaddrs]) dnl Checks for variables in6addr_any and in6addr_loopback, diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 89011d89ad..bd4543f831 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -58,6 +58,9 @@ #ifdef HAVE_NETPACKET_PACKET_H #include #endif +#ifdef HAVE_SYS_UN_H +#include +#endif /* All platforms fail on malloc errors. */ #define FATAL_MALLOC @@ -747,6 +750,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_AF_INET6 2 #define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */ #define INET_AF_LOOPBACK 4 /* INADDR_LOOPBACK or IN6ADDR_LOOPBACK_INIT */ +#define INET_AF_LOCAL 5 /* open and INET_REQ_GETTYPE enumeration */ #define INET_TYPE_STREAM 1 @@ -1032,19 +1036,29 @@ typedef union { #ifdef HAVE_IN6 struct sockaddr_in6 sai6; #endif +#ifdef HAVE_SYS_UN_H + struct sockaddr_un sal; +#endif } inet_address; /* for AF_INET & AF_INET6 */ #define inet_address_port(x) ((x)->sai.sin_port) +#ifdef HAVE_SYS_UN_H +#define localaddrlen(family, data) \ + ((family == AF_LOCAL) ? *(unsigned char*)(data) : 0) +#else + 0 +#endif + #if defined(HAVE_IN6) && defined(AF_INET6) -#define addrlen(family) \ +#define addrlen(family, data) \ ((family == AF_INET) ? sizeof(struct in_addr) : \ - ((family == AF_INET6) ? sizeof(struct in6_addr) : 0)) + ((family == AF_INET6) ? sizeof(struct in6_addr) : localaddrlen(family, data))) #else -#define addrlen(family) \ - ((family == AF_INET) ? sizeof(struct in_addr) : 0) +#define addrlen(family, data) \ + ((family == AF_INET) ? sizeof(struct in_addr) : localaddrlen(family, data)) #endif typedef struct _multi_timer_data { @@ -1727,6 +1741,12 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) spec[i++] = ERL_DRV_TUPLE; spec[i++] = 8; } +#endif +#ifdef HAVE_SYS_UN_H + else if (family == AF_LOCAL) { + int len = *(unsigned char*)buf++; + i = LOAD_STRING(spec, i, buf, len); + } #endif else { spec[i++] = ERL_DRV_TUPLE; @@ -3573,10 +3593,11 @@ static int tcp_error_message(tcp_descriptor* desc, int err) #ifdef HAVE_UDP /* ** active mode message: -** {udp, S, IP, Port, [H1,...Hsz | Data]} or -** {sctp, S, IP, Port, {[AncilData], Event_or_Data}} +** {udp, S, IP, Port, [H1,...Hsz | Data]} or +** {sctp, S, IP, Port, {[AncilData], Event_or_Data}} ** where ** [H1,...,HSz] are msg headers (without IP/Port, UDP only), +** [AddrLen, H2,...,HSz] are msg headers for UDP AF_LOCAL only ** Data : List() | Binary() */ static int packet_binary_message @@ -3586,6 +3607,7 @@ static int packet_binary_message ErlDrvTermData spec [PACKET_ERL_DRV_TERM_DATA_LEN]; int i = 0; int alen; + char* data = bin->orig_bytes+offs; DEBUGF(("packet_binary_message(%ld): len = %d\r\n", (long)desc->port, len)); @@ -3596,10 +3618,15 @@ static int packet_binary_message # endif i = LOAD_PORT(spec, i, desc->dport); /* S */ - alen = addrlen(desc->sfamily); - i = load_ip_address(spec, i, desc->sfamily, bin->orig_bytes+offs+3); - i = load_ip_port(spec, i, bin->orig_bytes+offs+1); /* IP, Port */ + alen = addrlen(desc->sfamily, data+3); + i = load_ip_address(spec, i, desc->sfamily, data+3); + i = load_ip_port(spec, i, data+1); /* IP, Port */ +# ifdef HAVE_SYS_UN_H + /* AF_LOCAL addresses have a prefix byte containing address length */ + if (desc->sfamily == AF_LOCAL) + alen++; +# endif offs += (alen + 3); len -= (alen + 3); @@ -4151,6 +4178,16 @@ static char* inet_set_address(int family, inet_address* dst, *len = sizeof(struct sockaddr_in6); return src + 2+16; } +#endif +#ifdef HAVE_SYS_UN_H + else if ((family == AF_LOCAL) && (*len >= 3+sizeof(struct sockaddr_un))) { + int n = *((unsigned char*)src+2); + dst->sal.sun_family = family; + sys_memcpy(dst->sal.sun_path, src+3, n); + dst->sal.sun_path[n-1] = '\0'; + *len = n; + return src + 3 + n; + } #endif return NULL; } @@ -4160,7 +4197,7 @@ static char* inet_set_address(int family, inet_address* dst, ** or from argument if source data specifies constant address. ** ** src = [TAG,P1,P0] when TAG = INET_AF_ANY | INET_AF_LOOPBACK -** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6 +** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6 | INET_AF_LOCAL */ static char *inet_set_faddress(int family, inet_address* dst, char *src, ErlDrvSizeT* len) { @@ -4177,6 +4214,21 @@ static char *inet_set_faddress(int family, inet_address* dst, case INET_AF_INET6: family = AF_INET6; break; +# endif +# ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: { + int n; + if (*len || *len < 3) return NULL; + family = AF_LOCAL; + /* Next two bytes are the length of the local path (< 256) */ + src++; + n = *(unsigned char*)src++; + if (n+3 > *len) return NULL; + dst->sal.sun_family = family; + sys_memcpy(dst->sal.sun_path, src, n); + *len = n; + break; + } # endif case INET_AF_ANY: case INET_AF_LOOPBACK: { @@ -4241,7 +4293,6 @@ static char *inet_set_faddress(int family, inet_address* dst, return inet_set_address(family, dst, src, len); } - /* Get a inaddr structure ** src = inaddr structure ** *len is the lenght of structure @@ -4273,10 +4324,55 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) *len = 3 + sizeof(struct in6_addr); return 0; } +#endif +#ifdef HAVE_SYS_UN_H + else if ((family == AF_LOCAL) && *len > 0) { + int n = *len - 4; + dst[0] = INET_AF_LOCAL; + put_int16(0, dst+1); + if (n == 0 || n >= sizeof(src->sal.sun_path)) { + *(dst+3) = 0; + *len = 3+1; + } else { + *(dst+3) = n; + sys_memcpy(dst+4, src->sal.sun_path, n); + *len = 3+1+n; + } + return 0; + } #endif return -1; } +static int inet_family_get_address(inet_descriptor* desc, char* dst, inet_address* src, unsigned int* len) +{ +#ifdef HAVE_SYS_UN_H + if (desc->sfamily == AF_LOCAL) { + int n = *len - 4; + dst[0] = INET_AF_LOCAL; + put_int16(0, dst+1); + if (n <= 0 || n >= sizeof(src->sal.sun_path)) { + if (desc->name_ptr) { + char* p = desc->name_ptr->sal.sun_path; + n = strlen(p); + *(dst+3) = n; + sys_memcpy(dst+4, p, n); + *len = 3+1+n; + } else { + *(dst+3) = 0; + *len = 3+1; + } + } else { + *(dst+3) = n; + sys_memcpy(dst+4, src->sal.sun_path, n); + *len = 3+1+n; + } + return 0; + } +#endif + return inet_get_address(dst, src, len); +} + /* Same as the above, but take family from the address structure, ** and advance the address pointer to the next address ** according to the size of the current, @@ -4306,6 +4402,19 @@ static int inet_address_to_erlang(char *dst, inet_address **src) { } (*src) = (inet_address *) (&(*src)->sai6 + 1); return 1 + 2 + 16; +#endif +#ifdef HAVE_SYS_UN_H + case AF_LOCAL: { + int n = strlen((*src)->sal.sun_path); + if (dst) { + dst[0] = INET_AF_LOCAL; + put_int16(0, dst+1); + *(dst+3) = n; + sys_memcpy(dst+1+2+1, (*src)->sal.sun_path, n); + } + (*src) = (inet_address *) (&(*src)->sal + 1); + return 1+2+1+n; + } #endif default: return -1; @@ -4402,7 +4511,8 @@ static void desc_close_read(inet_descriptor* desc) static int erl_inet_close(inet_descriptor* desc) { free_subscribers(&desc->empty_out_q_subs); - if ((desc->prebound == 0) && (desc->state & INET_F_OPEN)) { + if ((desc->prebound == 0 || desc->sfamily == AF_LOCAL) && + (desc->state & INET_F_OPEN)) { desc_close(desc); desc->state = INET_STATE_CLOSED; } else if (desc->prebound && (desc->s != INVALID_SOCKET)) { @@ -4582,6 +4692,13 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, return ctl_error(sock_errno(), rbuf, rsize); if (name.sa.sa_family != domain) return ctl_error(EINVAL, rbuf, rsize); +#ifdef HAVE_SYS_UN_H + if (domain == AF_LOCAL) { + sys_memcpy(&desc->name_addr, &name, sizeof(desc->name_addr)); + if (desc->name_ptr == NULL) + desc->name_ptr = &desc->name_addr; + } +#endif } #ifdef __OSE__ /* for fdopen duplicating the sd will allow to uniquely identify @@ -8570,6 +8687,11 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, else if (desc->sfamily == AF_INET6) { put_int32(INET_AF_INET6, &tbuf[0]); } +#endif +#ifdef HAVE_SYS_UN_H + else if (desc->sfamily == AF_LOCAL) { + put_int32(INET_AF_LOCAL, &tbuf[0]); + } #endif else return ctl_error(EINVAL, rbuf, rsize); @@ -9255,6 +9377,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, case INET_AF_INET6: return ctl_xerror("eafnosupport", rbuf, rsize); break; +#endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: + domain = AF_LOCAL; + break; #endif default: return ctl_error(EINVAL, rbuf, rsize); @@ -9281,6 +9408,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, case INET_AF_INET6: return ctl_xerror("eafnosupport", rbuf, rsize); break; +#endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: + domain = AF_LOCAL; + break; #endif default: return ctl_error(EINVAL, rbuf, rsize); @@ -11343,6 +11475,9 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, case INET_AF_INET6: return ctl_xerror("eafnosupport", rbuf, rsize); break; +#endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: af = AF_LOCAL; break; #endif default: return ctl_error(EINVAL, rbuf, rsize); @@ -11955,7 +12090,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) inet_input_count(desc, n); udesc->i_ptr += n; - inet_get_address(abuf, &other, &len); + inet_family_get_address(desc, abuf, &other, &len); /* Copy formatted address to the buffer allocated; "len" is the actual length which must be <= than the original reserved. This means that the addr + data in the buffer are contiguous, diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index bd74831bb7..4659448221 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -70,6 +70,8 @@ open(Protocol, Family, Type) -> open(Protocol, Family, Type, Opts) -> open(Protocol, Family, Type, Opts, ?INET_REQ_OPEN, []). +%% FDOPEN(tcp|udp|sctp, inet|inet6|local, stream|dgram|seqpacket, integer()) + fdopen(Protocol, Family, Type, Fd) when is_integer(Fd) -> fdopen(Protocol, Family, Type, Fd, true). @@ -104,8 +106,9 @@ open(Protocol, Family, Type, Opts, Req, Data) -> error:system_limit -> {error, system_limit} end. -enc_family(inet) -> ?INET_AF_INET; -enc_family(inet6) -> ?INET_AF_INET6. +enc_family(inet) -> ?INET_AF_INET; +enc_family(inet6) -> ?INET_AF_INET6; +enc_family(local) -> ?INET_AF_LOCAL. enc_type(stream) -> ?INET_TYPE_STREAM; enc_type(dgram) -> ?INET_TYPE_DGRAM; @@ -1619,6 +1622,8 @@ enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 4 -> [?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)]; enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 8 -> [?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)]; +enc_value_2(addr, {File,0}) when is_list(File) -> + [?INET_AF_LOCAL,0,0,length(File)|File]; enc_value_2(ether, [_,_,_,_,_,_]=Xs) -> Xs; enc_value_2(sockaddr, any) -> [?INET_AF_ANY]; @@ -1628,6 +1633,8 @@ enc_value_2(sockaddr, IP) when tuple_size(IP) =:= 4 -> [?INET_AF_INET|ip4_to_bytes(IP)]; enc_value_2(sockaddr, IP) when tuple_size(IP) =:= 8 -> [?INET_AF_INET6|ip6_to_bytes(IP)]; +enc_value_2(sockaddr, File) when is_list(File) -> + [?INET_AF_LOCAL,0,0,length(File)|File]; enc_value_2(linkaddr, Linkaddr) -> [?int16(length(Linkaddr)),Linkaddr]; enc_value_2(sctp_assoc_id, Val) -> ?int32(Val); @@ -2265,8 +2272,10 @@ get_addrs([F,P1,P0|Addr]) -> {IP,Addrs} = get_ip(F, Addr), [{IP,?u16(P1, P0)}|get_addrs(Addrs)]. -get_ip(?INET_AF_INET, Addr) -> get_ip4(Addr); -get_ip(?INET_AF_INET6, Addr) -> get_ip6(Addr). +get_ip(?INET_AF_INET, Addr) -> get_ip4(Addr); +get_ip(?INET_AF_INET6, Addr) -> get_ip6(Addr); +get_ip(?INET_AF_LOCAL, [0]) -> {[], []}; +get_ip(?INET_AF_LOCAL, [N | Addr]) -> lists:split(N, Addr). get_ip4([A,B,C,D | T]) -> {{A,B,C,D},T}. -- cgit v1.2.3 From ebe42ec76748546f464076d8cf5d1238a56baf91 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 15 Dec 2015 16:37:10 +0100 Subject: erts: Introduce erts_code_purger as a system process with preloaded code. --- erts/emulator/Makefile.in | 3 +++ erts/emulator/beam/erl_init.c | 30 ++++++++++++++++++++++++++-- erts/preloaded/ebin/erts_code_purger.beam | Bin 0 -> 1132 bytes erts/preloaded/src/Makefile | 1 + erts/preloaded/src/erts.app.src | 1 + erts/preloaded/src/erts_code_purger.erl | 32 ++++++++++++++++++++++++++++++ 6 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 erts/preloaded/ebin/erts_code_purger.beam create mode 100644 erts/preloaded/src/erts_code_purger.erl (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 8cf435905b..f4b806fae9 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -591,6 +591,7 @@ ifeq ($(TARGET),win32) PRELOAD_OBJ = $(OBJDIR)/beams.$(RES_EXT) PRELOAD_SRC = $(TARGET)/beams.rc $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ + $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \ $(ERL_TOP)/erts/preloaded/ebin/init.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_inet.beam \ @@ -600,11 +601,13 @@ $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ $(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \ $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \ $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam + $(gen_verbose)LANG=C $(PERL) utils/make_preload $(MAKE_PRELOAD_EXTRA) -rc $^ > $@ else PRELOAD_OBJ = $(OBJDIR)/preload.o PRELOAD_SRC = $(TARGET)/preload.c $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ + $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \ $(ERL_TOP)/erts/preloaded/ebin/init.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_inet.beam \ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 58ef09662c..42aca726bf 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -439,6 +439,29 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** return res; } +static Eterm +erl_system_process_otp(Eterm parent_pid, char* modname) +{ + Eterm start_mod; + Process* parent; + ErlSpawnOpts so; + Eterm res; + + start_mod = erts_atom_put((byte *) modname, sys_strlen(modname), ERTS_ATOM_ENC_LATIN1, 1); + if (erts_find_function(start_mod, am_start, 0, + erts_active_code_ix()) == NULL) { + erl_exit(5, "No function %s:start/0\n", modname); + } + + parent = erts_pid2proc(NULL, 0, parent_pid, ERTS_PROC_LOCK_MAIN); + + so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC; + res = erl_create_process(parent, start_mod, am_start, NIL, &so); + erts_smp_proc_unlock(parent, ERTS_PROC_LOCK_MAIN); + return res; +} + + Eterm erts_preloaded(Process* p) { @@ -1234,6 +1257,7 @@ erl_start(int argc, char **argv) ErtsTimeWarpMode time_warp_mode; int node_tab_delete_delay = ERTS_NODE_TAB_DELAY_GC_DEFAULT; ErtsDbSpinCount db_spin_count = ERTS_DB_SPNCNT_NORMAL; + Eterm otp_ring0_pid; set_default_time_adj(&time_correction, &time_warp_mode); @@ -2183,8 +2207,10 @@ erl_start(int argc, char **argv) erts_initialized = 1; - (void) erl_first_process_otp("otp_ring0", NULL, 0, - boot_argc, boot_argv); + otp_ring0_pid = erl_first_process_otp("otp_ring0", NULL, 0, + boot_argc, boot_argv); + + (void) erl_system_process_otp(otp_ring0_pid, "erts_code_purger"); #ifdef ERTS_SMP erts_start_schedulers(); diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam new file mode 100644 index 0000000000..176497b052 Binary files /dev/null and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 52034a0881..31383dda83 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -41,6 +41,7 @@ PRE_LOADED_ERL_MODULES = \ zlib \ prim_zip \ otp_ring0 \ + erts_code_purger \ erlang \ erts_internal diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index 8442aaf7e8..e53b6e5bab 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -27,6 +27,7 @@ erts_internal, init, otp_ring0, + erts_code_purger, prim_eval, prim_file, prim_inet, diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl new file mode 100644 index 0000000000..1ef4d096b7 --- /dev/null +++ b/erts/preloaded/src/erts_code_purger.erl @@ -0,0 +1,32 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(erts_code_purger). + +%% Purpose : Implement system process erts_code_purger +%% to handle code module purging. + +-export([start/0]). + +-spec start() -> term(). +start() -> + receive M -> + erlang:display({"erts_code_purger got msg", M}) + end, + start(). -- cgit v1.2.3 From c612edf4ada1f00b2bdb8404103e0d8307dc8f4c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 11 Jan 2016 19:17:04 +0100 Subject: erts: Refactor code:purge/1 and code:soft_purge/1 by moving code from code_server to erts_code_purger. This is more or less a copy-paste from code_server.erl to erts_code_purger.erl. All the inner mechanics of code:purge/1 and code:soft_purge/1 are unchanged. --- erts/preloaded/ebin/erts_code_purger.beam | Bin 1132 -> 9020 bytes erts/preloaded/src/erts_code_purger.erl | 283 +++++++++++++++++++++++++++++- 2 files changed, 280 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index 176497b052..adfe298a44 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl index 1ef4d096b7..6bb6e4fcdc 100644 --- a/erts/preloaded/src/erts_code_purger.erl +++ b/erts/preloaded/src/erts_code_purger.erl @@ -22,11 +22,288 @@ %% Purpose : Implement system process erts_code_purger %% to handle code module purging. --export([start/0]). +-export([start/0, purge/1, soft_purge/1]). -spec start() -> term(). start() -> - receive M -> + register(erts_code_purger, self()), + process_flag(trap_exit, true), + loop(). + +loop() -> + receive + {purge,Mod,From,Ref} when is_atom(Mod), is_pid(From) -> + Res = do_purge(Mod), + From ! {reply, purge, Res, Ref}; + + {soft_purge,Mod,From,Ref} when is_atom(Mod), is_pid(From) -> + Res = do_soft_purge(Mod), + From ! {reply, soft_purge, Res, Ref}; + + M -> erlang:display({"erts_code_purger got msg", M}) end, - start(). + loop(). + + +%% purge(Module) +%% Kill all processes running code from *old* Module, and then purge the +%% module. Return {WasOld, DidKill}: +%% {false, false} there was no old module to purge +%% {true, false} module purged, no process killed +%% {true, true} module purged, at least one process killed + +purge(Mod) when is_atom(Mod) -> + Ref = make_ref(), + erts_code_purger ! {purge, Mod, self(), Ref}, + receive + {reply, purge, Result, Ref} -> + Result + end. + + +do_purge(Mod) -> + case erlang:check_old_code(Mod) of + false -> + {false, false}; + true -> + true = erlang:copy_literals(Mod, true), + DidKill = check_proc_code(erlang:processes(), Mod, true), + true = erlang:copy_literals(Mod, false), + try + erlang:purge_module(Mod) + catch + _:_ -> ignore + end, + {true, DidKill} + end. + +%% soft_purge(Module) +%% Purge old code only if no procs remain that run old code. +%% Return true in that case, false if procs remain (in this +%% case old code is not purged) + +soft_purge(Mod) -> + Ref = make_ref(), + erts_code_purger ! {soft_purge, Mod, self(), Ref}, + receive + {reply, soft_purge, Result, Ref} -> + Result + end. + + +do_soft_purge(Mod) -> + case erlang:check_old_code(Mod) of + false -> + true; + true -> + true = erlang:copy_literals(Mod, true), + case check_proc_code(erlang:processes(), Mod, false) of + false -> + true = erlang:copy_literals(Mod, false), + false; + true -> + true = erlang:copy_literals(Mod, false), + try + erlang:purge_module(Mod) + catch + _:_ -> ignore + end, + true + end + end. + +%% +%% check_proc_code(Pids, Mod, Hard) - Send asynchronous +%% requests to all processes to perform a check_process_code +%% operation. Each process will check their own state and +%% reply with the result. If 'Hard' equals +%% - true, processes that refer 'Mod' will be killed. If +%% any processes were killed true is returned; otherwise, +%% false. +%% - false, and any processes refer 'Mod', false will +%% returned; otherwise, true. +%% +%% Requests will be sent to all processes identified by +%% Pids at once, but without allowing GC to be performed. +%% Check process code operations that are aborted due to +%% GC need, will be restarted allowing GC. However, only +%% ?MAX_CPC_GC_PROCS outstanding operation allowing GC at +%% a time will be allowed. This in order not to blow up +%% memory wise. +%% +%% We also only allow ?MAX_CPC_NO_OUTSTANDING_KILLS +%% outstanding kills. This both in order to avoid flooding +%% our message queue with 'DOWN' messages and limiting the +%% amount of memory used to keep references to all +%% outstanding kills. +%% + +%% We maybe should allow more than two outstanding +%% GC requests, but for now we play it safe... +-define(MAX_CPC_GC_PROCS, 2). +-define(MAX_CPC_NO_OUTSTANDING_KILLS, 10). + +-record(cpc_static, {hard, module, tag}). + +-record(cpc_kill, {outstanding = [], + no_outstanding = 0, + waiting = [], + killed = false}). + +check_proc_code(Pids, Mod, Hard) -> + Tag = erlang:make_ref(), + CpcS = #cpc_static{hard = Hard, + module = Mod, + tag = Tag}, + check_proc_code(CpcS, cpc_init(CpcS, Pids, 0), 0, [], #cpc_kill{}, true). + +check_proc_code(#cpc_static{hard = true}, 0, 0, [], + #cpc_kill{outstanding = [], waiting = [], killed = Killed}, + true) -> + %% No outstanding requests. We did a hard check, so result is whether or + %% not we killed any processes... + Killed; +check_proc_code(#cpc_static{hard = false}, 0, 0, [], _KillState, Success) -> + %% No outstanding requests and we did a soft check... + Success; +check_proc_code(#cpc_static{hard = false, tag = Tag} = CpcS, NoReq0, NoGcReq0, + [], _KillState, false) -> + %% Failed soft check; just cleanup the remaining replies corresponding + %% to the requests we've sent... + {NoReq1, NoGcReq1} = receive + {check_process_code, {Tag, _P, GC}, _Res} -> + case GC of + false -> {NoReq0-1, NoGcReq0}; + true -> {NoReq0, NoGcReq0-1} + end + end, + check_proc_code(CpcS, NoReq1, NoGcReq1, [], _KillState, false); +check_proc_code(#cpc_static{tag = Tag} = CpcS, NoReq0, NoGcReq0, NeedGC0, + KillState0, Success) -> + + %% Check if we should request a GC operation + {NoGcReq1, NeedGC1} = case NoGcReq0 < ?MAX_CPC_GC_PROCS of + GcOpAllowed when GcOpAllowed == false; + NeedGC0 == [] -> + {NoGcReq0, NeedGC0}; + _ -> + {NoGcReq0+1, cpc_request_gc(CpcS,NeedGC0)} + end, + + %% Wait for a cpc reply or 'DOWN' message + {NoReq1, NoGcReq2, Pid, Result, KillState1} = cpc_recv(Tag, + NoReq0, + NoGcReq1, + KillState0), + + %% Check the result of the reply + case Result of + aborted -> + %% Operation aborted due to the need to GC in order to + %% determine if the process is referring the module. + %% Schedule the operation for restart allowing GC... + check_proc_code(CpcS, NoReq1, NoGcReq2, [Pid|NeedGC1], KillState1, + Success); + false -> + %% Process not referring the module; done with this process... + check_proc_code(CpcS, NoReq1, NoGcReq2, NeedGC1, KillState1, + Success); + true -> + %% Process referring the module... + case CpcS#cpc_static.hard of + false -> + %% ... and soft check. The whole operation failed so + %% no point continuing; clean up and fail... + check_proc_code(CpcS, NoReq1, NoGcReq2, [], KillState1, + false); + true -> + %% ... and hard check; schedule kill of it... + check_proc_code(CpcS, NoReq1, NoGcReq2, NeedGC1, + cpc_sched_kill(Pid, KillState1), Success) + end; + 'DOWN' -> + %% Handled 'DOWN' message + check_proc_code(CpcS, NoReq1, NoGcReq2, NeedGC1, + KillState1, Success) + end. + +cpc_recv(Tag, NoReq, NoGcReq, #cpc_kill{outstanding = []} = KillState) -> + receive + {check_process_code, {Tag, Pid, GC}, Res} -> + cpc_handle_cpc(NoReq, NoGcReq, GC, Pid, Res, KillState) + end; +cpc_recv(Tag, NoReq, NoGcReq, + #cpc_kill{outstanding = [R0, R1, R2, R3, R4 | _]} = KillState) -> + receive + {'DOWN', R, process, _, _} when R == R0; + R == R1; + R == R2; + R == R3; + R == R4 -> + cpc_handle_down(NoReq, NoGcReq, R, KillState); + {check_process_code, {Tag, Pid, GC}, Res} -> + cpc_handle_cpc(NoReq, NoGcReq, GC, Pid, Res, KillState) + end; +cpc_recv(Tag, NoReq, NoGcReq, #cpc_kill{outstanding = [R|_]} = KillState) -> + receive + {'DOWN', R, process, _, _} -> + cpc_handle_down(NoReq, NoGcReq, R, KillState); + {check_process_code, {Tag, Pid, GC}, Res} -> + cpc_handle_cpc(NoReq, NoGcReq, GC, Pid, Res, KillState) + end. + +cpc_handle_down(NoReq, NoGcReq, R, #cpc_kill{outstanding = Rs, + no_outstanding = N} = KillState) -> + {NoReq, NoGcReq, undefined, 'DOWN', + cpc_sched_kill_waiting(KillState#cpc_kill{outstanding = cpc_list_rm(R, Rs), + no_outstanding = N-1})}. + +cpc_list_rm(R, [R|Rs]) -> + Rs; +cpc_list_rm(R0, [R1|Rs]) -> + [R1|cpc_list_rm(R0, Rs)]. + +cpc_handle_cpc(NoReq, NoGcReq, false, Pid, Res, KillState) -> + {NoReq-1, NoGcReq, Pid, Res, KillState}; +cpc_handle_cpc(NoReq, NoGcReq, true, Pid, Res, KillState) -> + {NoReq, NoGcReq-1, Pid, Res, KillState}. + +cpc_sched_kill_waiting(#cpc_kill{waiting = []} = KillState) -> + KillState; +cpc_sched_kill_waiting(#cpc_kill{outstanding = Rs, + no_outstanding = N, + waiting = [P|Ps]} = KillState) -> + R = erlang:monitor(process, P), + exit(P, kill), + KillState#cpc_kill{outstanding = [R|Rs], + no_outstanding = N+1, + waiting = Ps, + killed = true}. + +cpc_sched_kill(Pid, #cpc_kill{no_outstanding = N, waiting = Pids} = KillState) + when N >= ?MAX_CPC_NO_OUTSTANDING_KILLS -> + KillState#cpc_kill{waiting = [Pid|Pids]}; +cpc_sched_kill(Pid, + #cpc_kill{outstanding = Rs, no_outstanding = N} = KillState) -> + R = erlang:monitor(process, Pid), + exit(Pid, kill), + KillState#cpc_kill{outstanding = [R|Rs], + no_outstanding = N+1, + killed = true}. + +cpc_request(#cpc_static{tag = Tag, module = Mod}, Pid, AllowGc) -> + erlang:check_process_code(Pid, Mod, [{async, {Tag, Pid, AllowGc}}, + {allow_gc, AllowGc}]). + +cpc_request_gc(CpcS, [Pid|Pids]) -> + cpc_request(CpcS, Pid, true), + Pids. + +cpc_init(_CpcS, [], NoReqs) -> + NoReqs; +cpc_init(CpcS, [Pid|Pids], NoReqs) -> + cpc_request(CpcS, Pid, false), + cpc_init(CpcS, Pids, NoReqs+1). + +% end of check_proc_code() implementation. -- cgit v1.2.3 From fa44f865c3fc6253cf4691cf94839c303a3ee40f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 15 Dec 2015 20:32:39 +0100 Subject: erts: Make erlang:purge_module/1 safe Problem: erlang:purge_module/1 is not safe in the sense that very bad things may happen if the code to be purged is still referred to by live processes. Introduce erts_internal:purge_module which is the same as the old erlang:purge_module BIF (except it returns false if no such old module). Implement erlang:purge_module in Erlang and let it invoke erts_code_purger for safe purging where all clogging processes first are killed. --- erts/emulator/beam/beam_bif_load.c | 14 ++++++++++---- erts/emulator/beam/bif.tab | 2 +- erts/preloaded/ebin/erlang.beam | Bin 102216 -> 102556 bytes erts/preloaded/ebin/erts_code_purger.beam | Bin 9020 -> 8992 bytes erts/preloaded/ebin/erts_internal.beam | Bin 6260 -> 6432 bytes erts/preloaded/ebin/init.beam | Bin 44700 -> 44728 bytes erts/preloaded/src/erlang.erl | 12 ++++++++++-- erts/preloaded/src/erts_code_purger.erl | 14 +++----------- erts/preloaded/src/erts_internal.erl | 6 ++++++ erts/preloaded/src/init.erl | 4 ++-- 10 files changed, 32 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 6bb70cc5a7..014ee35fd0 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -1094,7 +1094,12 @@ static void copy_literals_commit(void* null) { #endif /* ERTS_SMP */ -BIF_RETTYPE purge_module_1(BIF_ALIST_1) +/* Do the actualy module purging and return: + * true for success + * false if no such old module + * BADARG if not an atom + */ +BIF_RETTYPE erts_internal_purge_module_1(BIF_ALIST_1) { ErtsCodeIndex code_ix; BeamInstr* code; @@ -1108,7 +1113,8 @@ BIF_RETTYPE purge_module_1(BIF_ALIST_1) } if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD1(bif_export[BIF_purge_module_1], BIF_P, BIF_ARG_1); + ERTS_BIF_YIELD1(bif_export[BIF_erts_internal_purge_module_1], + BIF_P, BIF_ARG_1); } code_ix = erts_active_code_ix(); @@ -1118,7 +1124,7 @@ BIF_RETTYPE purge_module_1(BIF_ALIST_1) */ if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) { - ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG); + ERTS_BIF_PREP_RET(ret, am_false); } else { erts_rwlock_old_code(code_ix); @@ -1127,7 +1133,7 @@ BIF_RETTYPE purge_module_1(BIF_ALIST_1) * Any code to purge? */ if (!modp->old.code_hdr) { - ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG); + ERTS_BIF_PREP_RET(ret, am_false); } else { /* diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 0aee8681c6..3b95ec508c 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -125,7 +125,6 @@ bif erlang:process_flag/3 bif erlang:process_info/1 bif erlang:process_info/2 bif erlang:processes/0 -bif erlang:purge_module/1 bif erlang:put/2 bif erlang:register/2 bif erlang:registered/0 @@ -643,6 +642,7 @@ bif erts_debug:map_info/1 # bif erlang:copy_literals/2 +bif erts_internal:purge_module/1 bif binary:split/2 bif binary:split/3 bif erts_debug:size_shared/1 diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 68578c3a49..4353e115ca 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index adfe298a44..553b34b105 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 4e1cb7f8a0..150e76505d 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index a44b022931..b6d1df7bbc 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index d9dc9a1976..ab54d716cc 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1471,8 +1471,16 @@ processes() -> %% purge_module/1 -spec purge_module(Module) -> true when Module :: atom(). -purge_module(_Module) -> - erlang:nif_error(undefined). +purge_module(Module) when erlang:is_atom(Module) -> + case erts_code_purger:purge(Module) of + {false, _} -> + erlang:error(badarg, [Module]); + {true, _} -> + true + end; +purge_module(Arg) -> + erlang:error(badarg, [Arg]). + %% put/2 -spec put(Key, Val) -> term() when diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl index 6bb6e4fcdc..880c86aad1 100644 --- a/erts/preloaded/src/erts_code_purger.erl +++ b/erts/preloaded/src/erts_code_purger.erl @@ -70,12 +70,8 @@ do_purge(Mod) -> true = erlang:copy_literals(Mod, true), DidKill = check_proc_code(erlang:processes(), Mod, true), true = erlang:copy_literals(Mod, false), - try - erlang:purge_module(Mod) - catch - _:_ -> ignore - end, - {true, DidKill} + WasPurged = erts_internal:purge_module(Mod), + {WasPurged, DidKill} end. %% soft_purge(Module) @@ -104,11 +100,7 @@ do_soft_purge(Mod) -> false; true -> true = erlang:copy_literals(Mod, false), - try - erlang:purge_module(Mod) - catch - _:_ -> ignore - end, + erts_internal:purge_module(Mod), true end end. diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 426749264f..6e649e8395 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -38,6 +38,7 @@ -export([request_system_task/3]). -export([check_process_code/2]). +-export([purge_module/1]). -export([flush_monitor_messages/3]). @@ -204,6 +205,11 @@ request_system_task(_Pid, _Prio, _Request) -> check_process_code(_Module, _OptionList) -> erlang:nif_error(undefined). +-spec purge_module(Module) -> boolean() when + Module :: module(). +purge_module(_Module) -> + erlang:nif_error(undefined). + %% term compare where integer() < float() = true -spec cmp_term(A,B) -> Result when diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 383c4a1ec6..ed65c57c0d 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -636,9 +636,9 @@ unload(_) -> do_unload(sub([heart|erlang:pre_loaded()],erlang:loaded())). do_unload([M|Mods]) -> - catch erlang:purge_module(M), + catch erts_internal:purge_module(M), catch erlang:delete_module(M), - catch erlang:purge_module(M), + catch erts_internal:purge_module(M), do_unload(Mods); do_unload([]) -> purge_all_hipe_refs(), -- cgit v1.2.3 From dc54c2a27c41930a18e0c7f2b97eda6cd4a0b1c1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 16 Dec 2015 19:11:48 +0100 Subject: erts: Move copy_literals/2 from erlang to erts_internal as it's not a public interface. --- erts/emulator/beam/beam_bif_load.c | 5 +++-- erts/emulator/beam/bif.tab | 2 +- erts/preloaded/ebin/erlang.beam | Bin 102556 -> 102332 bytes erts/preloaded/ebin/erts_code_purger.beam | Bin 8992 -> 8996 bytes erts/preloaded/ebin/erts_internal.beam | Bin 6432 -> 6632 bytes erts/preloaded/src/erlang.erl | 9 +-------- erts/preloaded/src/erts_code_purger.erl | 10 +++++----- erts/preloaded/src/erts_internal.erl | 7 +++++++ 8 files changed, 17 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 014ee35fd0..39a5bff04c 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -1031,7 +1031,7 @@ copy_literals_t erts_clrange = {NULL, 0}; */ -BIF_RETTYPE copy_literals_2(BIF_ALIST_2) +BIF_RETTYPE erts_internal_copy_literals_2(BIF_ALIST_2) { Module* modp; ErtsCodeIndex code_ix; @@ -1042,7 +1042,8 @@ BIF_RETTYPE copy_literals_2(BIF_ALIST_2) } if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD2(bif_export[BIF_copy_literals_2], BIF_P, BIF_ARG_1, BIF_ARG_2); + ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_copy_literals_2], + BIF_P, BIF_ARG_1, BIF_ARG_2); } code_ix = erts_active_code_ix(); diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 3b95ec508c..1b8ae8cef5 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -641,7 +641,7 @@ bif erts_debug:map_info/1 # New in 19.0 # -bif erlang:copy_literals/2 +bif erts_internal:copy_literals/2 bif erts_internal:purge_module/1 bif binary:split/2 bif binary:split/3 diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 4353e115ca..d58d9c909e 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index 553b34b105..74001fc799 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 150e76505d..7577522151 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index ab54d716cc..0a41951900 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -91,7 +91,7 @@ -export([bit_size/1, bitsize/1, bitstring_to_list/1]). -export([bump_reductions/1, byte_size/1, call_on_load_function/1]). -export([cancel_timer/1, cancel_timer/2, check_old_code/1, check_process_code/2, - check_process_code/3, copy_literals/2, crc32/1]). + check_process_code/3, crc32/1]). -export([crc32/2, crc32_combine/3, date/0, decode_packet/3]). -export([delete_element/2]). -export([delete_module/1, demonitor/1, demonitor/2, display/1]). @@ -520,13 +520,6 @@ get_cpc_opts([{allow_gc, AllowGC} | Options], Async, _OldAllowGC) -> get_cpc_opts([], Async, AllowGC) -> {Async, AllowGC}. -%% copy_literals/2 --spec erlang:copy_literals(Module,Bool) -> 'true' | 'false' | 'aborted' when - Module :: module(), - Bool :: boolean(). -copy_literals(_Mod, _Bool) -> - erlang:nif_error(undefined). - %% crc32/1 -spec erlang:crc32(Data) -> non_neg_integer() when Data :: iodata(). diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl index 880c86aad1..c7fe3ce22f 100644 --- a/erts/preloaded/src/erts_code_purger.erl +++ b/erts/preloaded/src/erts_code_purger.erl @@ -67,9 +67,9 @@ do_purge(Mod) -> false -> {false, false}; true -> - true = erlang:copy_literals(Mod, true), + true = erts_internal:copy_literals(Mod, true), DidKill = check_proc_code(erlang:processes(), Mod, true), - true = erlang:copy_literals(Mod, false), + true = erts_internal:copy_literals(Mod, false), WasPurged = erts_internal:purge_module(Mod), {WasPurged, DidKill} end. @@ -93,13 +93,13 @@ do_soft_purge(Mod) -> false -> true; true -> - true = erlang:copy_literals(Mod, true), + true = erts_internal:copy_literals(Mod, true), case check_proc_code(erlang:processes(), Mod, false) of false -> - true = erlang:copy_literals(Mod, false), + true = erts_internal:copy_literals(Mod, false), false; true -> - true = erlang:copy_literals(Mod, false), + true = erts_internal:copy_literals(Mod, false), erts_internal:purge_module(Mod), true end diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 6e649e8395..30e9ba304c 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -38,6 +38,7 @@ -export([request_system_task/3]). -export([check_process_code/2]). +-export([copy_literals/2]). -export([purge_module/1]). -export([flush_monitor_messages/3]). @@ -205,6 +206,12 @@ request_system_task(_Pid, _Prio, _Request) -> check_process_code(_Module, _OptionList) -> erlang:nif_error(undefined). +-spec copy_literals(Module,Bool) -> 'true' | 'false' | 'aborted' when + Module :: module(), + Bool :: boolean(). +copy_literals(_Mod, _Bool) -> + erlang:nif_error(undefined). + -spec purge_module(Module) -> boolean() when Module :: module(). purge_module(_Module) -> -- cgit v1.2.3 From 79efde2d8503e5055ef9e8afa5d8d63208710b1f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 16 Dec 2015 20:03:32 +0100 Subject: erts: Make copy_literals more fail safe * Same process must do enable-disable. * System process will force it and never get 'aborted' --- erts/emulator/beam/beam_bif_load.c | 28 +++++++++++++++++----------- erts/emulator/beam/global.h | 1 + erts/preloaded/ebin/erts_code_purger.beam | Bin 8996 -> 8832 bytes erts/preloaded/src/erts_code_purger.erl | 12 +++++------- 4 files changed, 23 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 39a5bff04c..892b2d16c2 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -1013,7 +1013,7 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) static void copy_literals_commit(void*); #endif -copy_literals_t erts_clrange = {NULL, 0}; +copy_literals_t erts_clrange = {NULL, 0, THE_NON_VALUE}; /* copy literals * @@ -1033,7 +1033,6 @@ copy_literals_t erts_clrange = {NULL, 0}; BIF_RETTYPE erts_internal_copy_literals_2(BIF_ALIST_2) { - Module* modp; ErtsCodeIndex code_ix; Eterm res = am_true; @@ -1048,21 +1047,28 @@ BIF_RETTYPE erts_internal_copy_literals_2(BIF_ALIST_2) code_ix = erts_active_code_ix(); - if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL || !modp->old.code_hdr) { - res = am_false; - goto done; - } - if (BIF_ARG_2 == am_true) { - if (erts_clrange.ptr != NULL) { + Module* modp = erts_get_module(BIF_ARG_1, code_ix); + if (!modp || !modp->old.code_hdr) { + res = am_false; + goto done; + } + if (erts_clrange.ptr != NULL + && !(BIF_P->static_flags & ERTS_STC_FLG_SYSTEM_PROC)) { res = am_aborted; goto done; - } - erts_clrange.ptr = (Eterm*) modp->old.code_hdr->literals_start; - erts_clrange.sz = (Eterm*) modp->old.code_hdr->literals_end - erts_clrange.ptr; + } + erts_clrange.ptr = modp->old.code_hdr->literals_start; + erts_clrange.sz = modp->old.code_hdr->literals_end - erts_clrange.ptr; + erts_clrange.pid = BIF_P->common.id; } else if (BIF_ARG_2 == am_false) { + if (erts_clrange.pid != BIF_P->common.id) { + res = am_false; + goto done; + } erts_clrange.ptr = NULL; erts_clrange.sz = 0; + erts_clrange.pid = THE_NON_VALUE; } #ifdef ERTS_SMP diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 0bf5988244..bbf684d49d 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -990,6 +990,7 @@ Eterm erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *red typedef struct { Eterm *ptr; Uint sz; + Eterm pid; } copy_literals_t; extern copy_literals_t erts_clrange; diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index 74001fc799..c5aec6bb25 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl index c7fe3ce22f..791ef72f13 100644 --- a/erts/preloaded/src/erts_code_purger.erl +++ b/erts/preloaded/src/erts_code_purger.erl @@ -63,11 +63,10 @@ purge(Mod) when is_atom(Mod) -> do_purge(Mod) -> - case erlang:check_old_code(Mod) of + case erts_internal:copy_literals(Mod, true) of false -> {false, false}; true -> - true = erts_internal:copy_literals(Mod, true), DidKill = check_proc_code(erlang:processes(), Mod, true), true = erts_internal:copy_literals(Mod, false), WasPurged = erts_internal:purge_module(Mod), @@ -89,17 +88,16 @@ soft_purge(Mod) -> do_soft_purge(Mod) -> - case erlang:check_old_code(Mod) of + case erts_internal:copy_literals(Mod, true) of false -> true; true -> - true = erts_internal:copy_literals(Mod, true), - case check_proc_code(erlang:processes(), Mod, false) of + DoPurge = check_proc_code(erlang:processes(), Mod, false), + true = erts_internal:copy_literals(Mod, false), + case DoPurge of false -> - true = erts_internal:copy_literals(Mod, false), false; true -> - true = erts_internal:copy_literals(Mod, false), erts_internal:purge_module(Mod), true end -- cgit v1.2.3 From a4920dc4045f394f6f4ab1cc89d54d55722a66d6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 12 Jan 2016 17:02:06 +0100 Subject: erts: Refactor check_process_code/3 Move impl from erlang to erts_internal. Cut and paste. --- erts/preloaded/ebin/erlang.beam | Bin 102332 -> 101268 bytes erts/preloaded/ebin/erts_internal.beam | Bin 6632 -> 8168 bytes erts/preloaded/src/erlang.erl | 44 ++------------------------ erts/preloaded/src/erts_internal.erl | 55 ++++++++++++++++++++++++++++++++- 4 files changed, 56 insertions(+), 43 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index d58d9c909e..b6e38e4b5b 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 7577522151..4c66171965 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 0a41951900..40d5aedd24 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -460,7 +460,7 @@ check_old_code(_Module) -> CheckResult :: boolean(). check_process_code(Pid, Module) -> try - erlang:check_process_code(Pid, Module, [{allow_gc, true}]) + erts_internal:check_process_code(Pid, Module, [{allow_gc, true}]) catch error:Error -> erlang:error(Error, [Pid, Module]) end. @@ -475,51 +475,11 @@ check_process_code(Pid, Module) -> CheckResult :: boolean() | aborted. check_process_code(Pid, Module, OptionList) -> try - {Async, AllowGC} = get_cpc_opts(OptionList, sync, true), - case Async of - {async, ReqId} -> - {priority, Prio} = erlang:process_info(erlang:self(), - priority), - erts_internal:request_system_task(Pid, - Prio, - {check_process_code, - ReqId, - Module, - AllowGC}), - async; - sync -> - case Pid == erlang:self() of - true -> - erts_internal:check_process_code(Module, - [{allow_gc, AllowGC}]); - false -> - {priority, Prio} = erlang:process_info(erlang:self(), - priority), - ReqId = erlang:make_ref(), - erts_internal:request_system_task(Pid, - Prio, - {check_process_code, - ReqId, - Module, - AllowGC}), - receive - {check_process_code, ReqId, CheckResult} -> - CheckResult - end - end - end + erts_internal:check_process_code(Pid, Module, OptionList) catch error:Error -> erlang:error(Error, [Pid, Module, OptionList]) end. -% gets async and allow_gc opts and verify valid option list -get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, AllowGC) -> - get_cpc_opts(Options, AsyncTuple, AllowGC); -get_cpc_opts([{allow_gc, AllowGC} | Options], Async, _OldAllowGC) -> - get_cpc_opts(Options, Async, AllowGC); -get_cpc_opts([], Async, AllowGC) -> - {Async, AllowGC}. - %% crc32/1 -spec erlang:crc32(Data) -> non_neg_integer() when Data :: iodata(). diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 30e9ba304c..e32d65ff59 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -37,7 +37,7 @@ -export([request_system_task/3]). --export([check_process_code/2]). +-export([check_process_code/3]). -export([copy_literals/2]). -export([purge_module/1]). @@ -49,6 +49,9 @@ -export([is_system_process/1]). +%% Auto import name clash +-export([check_process_code/2]). + %% %% Await result of send to port %% @@ -206,6 +209,56 @@ request_system_task(_Pid, _Prio, _Request) -> check_process_code(_Module, _OptionList) -> erlang:nif_error(undefined). +-spec check_process_code(Pid, Module, OptionList) -> CheckResult | async when + Pid :: pid(), + Module :: module(), + RequestId :: term(), + Option :: {async, RequestId} | {allow_gc, boolean()}, + OptionList :: [Option], + CheckResult :: boolean() | aborted. +check_process_code(Pid, Module, OptionList) -> + {Async, AllowGC} = get_cpc_opts(OptionList, sync, true), + case Async of + {async, ReqId} -> + {priority, Prio} = erlang:process_info(erlang:self(), + priority), + erts_internal:request_system_task(Pid, + Prio, + {check_process_code, + ReqId, + Module, + AllowGC}), + async; + sync -> + case Pid == erlang:self() of + true -> + erts_internal:check_process_code(Module, + [{allow_gc, AllowGC}]); + false -> + {priority, Prio} = erlang:process_info(erlang:self(), + priority), + ReqId = erlang:make_ref(), + erts_internal:request_system_task(Pid, + Prio, + {check_process_code, + ReqId, + Module, + AllowGC}), + receive + {check_process_code, ReqId, CheckResult} -> + CheckResult + end + end + end. + +% gets async and allow_gc opts and verify valid option list +get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, AllowGC) -> + get_cpc_opts(Options, AsyncTuple, AllowGC); +get_cpc_opts([{allow_gc, AllowGC} | Options], Async, _OldAllowGC) -> + get_cpc_opts(Options, Async, AllowGC); +get_cpc_opts([], Async, AllowGC) -> + {Async, AllowGC}. + -spec copy_literals(Module,Bool) -> 'true' | 'false' | 'aborted' when Module :: module(), Bool :: boolean(). -- cgit v1.2.3 From 4c763443365591e170308a1c5f11a4586734ca4e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 12 Jan 2016 16:57:59 +0100 Subject: erts: Optimize erlang:check_process_code by ignoring literals. erts_internal:check_process_code will be called again anyway (with option {copy_literals, true}) before the module is actually purged. No need to check literals twice. --- erts/emulator/beam/beam_bif_load.c | 61 ++++++++++-------------------- erts/emulator/beam/erl_process.c | 4 +- erts/emulator/beam/global.h | 5 ++- erts/preloaded/ebin/erts_code_purger.beam | Bin 8832 -> 8884 bytes erts/preloaded/ebin/erts_internal.beam | Bin 8168 -> 8536 bytes erts/preloaded/src/erts_code_purger.erl | 5 ++- erts/preloaded/src/erts_internal.erl | 42 +++++++++++--------- 7 files changed, 54 insertions(+), 63 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 892b2d16c2..a000935388 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -38,7 +38,7 @@ #include "erl_thr_progress.h" static void set_default_trace_pattern(Eterm module); -static Eterm check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp); +static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp); static void delete_code(Module* modp); static void decrement_refc(BeamCodeHeader*); static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size); @@ -426,7 +426,7 @@ check_old_code_1(BIF_ALIST_1) } Eterm -erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp) +erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp) { Module* modp; Eterm res; @@ -441,7 +441,8 @@ erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp) if (!modp) return am_false; erts_rlock_old_code(code_ix); - res = modp->old.code_hdr ? check_process_code(c_p, modp, allow_gc, redsp) : am_false; + res = (!modp->old.code_hdr ? am_false : + check_process_code(c_p, modp, flags, redsp)); erts_runlock_old_code(code_ix); return res; @@ -450,49 +451,21 @@ erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp) BIF_RETTYPE erts_internal_check_process_code_2(BIF_ALIST_2) { int reds = 0; + Uint flags; Eterm res; - Eterm olist = BIF_ARG_2; - int allow_gc = 1; if (is_not_atom(BIF_ARG_1)) goto badarg; - while (is_list(olist)) { - Eterm *lp = list_val(olist); - Eterm opt = CAR(lp); - if (is_tuple(opt)) { - Eterm* tp = tuple_val(opt); - switch (arityval(tp[0])) { - case 2: - switch (tp[1]) { - case am_allow_gc: - switch (tp[2]) { - case am_false: - allow_gc = 0; - break; - case am_true: - allow_gc = 1; - break; - default: - goto badarg; - } - break; - default: - goto badarg; - } - break; - default: - goto badarg; - } - } - else - goto badarg; - olist = CDR(lp); + if (is_not_small(BIF_ARG_2)) + goto badarg; + + flags = unsigned_val(BIF_ARG_2); + if (flags & ~ERTS_CPC_ALL) { + goto badarg; } - if (is_not_nil(olist)) - goto badarg; - res = erts_check_process_code(BIF_P, BIF_ARG_1, allow_gc, &reds); + res = erts_check_process_code(BIF_P, BIF_ARG_1, flags, &reds); ASSERT(is_value(res)); @@ -739,7 +712,7 @@ check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size) static Eterm -check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) +check_process_code(Process* rp, Module* modp, Uint flags, int *redsp) { BeamInstr* start; char* literals; @@ -852,6 +825,12 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) /* Check heap, stack etc... */ if (check_mod_funs(rp, &rp->off_heap, mod_start, mod_size)) goto try_gc; + if (!(flags & ERTS_CPC_COPY_LITERALS)) { + /* Process ok. May contain old literals but we will be called + * again before module is purged. + */ + return am_false; + } if (any_heap_ref_ptrs(&rp->fvalue, &rp->fvalue+1, literals, lit_bsize)) { rp->freason = EXC_NULL; rp->fvalue = NIL; @@ -919,7 +898,7 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp) if ((done_gc & need_gc) == need_gc) return am_true; - if (!allow_gc) + if (!(flags & ERTS_CPC_ALLOW_GC)) return am_aborted; need_gc &= ~done_gc; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 9495436ba6..d36d866b2f 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -10055,7 +10055,7 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) case ERTS_PSTT_CPC: st_res = erts_check_process_code(c_p, st->arg[0], - st->arg[1] == am_true, + unsigned_val(st->arg[1]), &reds); if (is_non_value(st_res)) { /* Needed gc, but gc was disabled */ @@ -10219,7 +10219,7 @@ erts_internal_request_system_task_3(BIF_ALIST_3) case am_check_process_code: if (is_not_atom(st->arg[0])) goto badarg; - if (st->arg[1] != am_true && st->arg[1] != am_false) + if (is_not_small(st->arg[1]) || (unsigned_val(st->arg[1]) & ~ERTS_CPC_ALL)) goto badarg; noproc_res = am_false; st->type = ERTS_PSTT_CPC; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index bbf684d49d..3f5925765d 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -985,7 +985,10 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg); Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2); /* beam_bif_load.c */ -Eterm erts_check_process_code(Process *c_p, Eterm module, int allow_gc, int *redsp); +#define ERTS_CPC_ALLOW_GC (1 << 0) +#define ERTS_CPC_COPY_LITERALS (1 << 1) +#define ERTS_CPC_ALL (ERTS_CPC_ALLOW_GC | ERTS_CPC_COPY_LITERALS) +Eterm erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp); typedef struct { Eterm *ptr; diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index c5aec6bb25..ca6c8570ed 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 4c66171965..d3d990519d 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl index 791ef72f13..e492ba1812 100644 --- a/erts/preloaded/src/erts_code_purger.erl +++ b/erts/preloaded/src/erts_code_purger.erl @@ -283,8 +283,9 @@ cpc_sched_kill(Pid, killed = true}. cpc_request(#cpc_static{tag = Tag, module = Mod}, Pid, AllowGc) -> - erlang:check_process_code(Pid, Mod, [{async, {Tag, Pid, AllowGc}}, - {allow_gc, AllowGc}]). + erts_internal:check_process_code(Pid, Mod, [{async, {Tag, Pid, AllowGc}}, + {allow_gc, AllowGc}, + {copy_literals, true}]). cpc_request_gc(CpcS, [Pid|Pids]) -> cpc_request(CpcS, Pid, true), diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index e32d65ff59..84dedab930 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -202,22 +202,24 @@ port_info(_Result, _Item) -> request_system_task(_Pid, _Prio, _Request) -> erlang:nif_error(undefined). --spec check_process_code(Module, OptionList) -> boolean() when +-define(ERTS_CPC_ALLOW_GC, (1 bsl 0)). +-define(ERTS_CPC_COPY_LITERALS, (1 bsl 1)). + +-spec check_process_code(Module, Flags) -> boolean() when Module :: module(), - Option :: {allow_gc, boolean()}, - OptionList :: [Option]. -check_process_code(_Module, _OptionList) -> + Flags :: non_neg_integer(). +check_process_code(_Module, _Flags) -> erlang:nif_error(undefined). -spec check_process_code(Pid, Module, OptionList) -> CheckResult | async when Pid :: pid(), Module :: module(), RequestId :: term(), - Option :: {async, RequestId} | {allow_gc, boolean()}, + Option :: {async, RequestId} | {allow_gc, boolean()} | {copy_literals, boolean()}, OptionList :: [Option], CheckResult :: boolean() | aborted. check_process_code(Pid, Module, OptionList) -> - {Async, AllowGC} = get_cpc_opts(OptionList, sync, true), + {Async, Flags} = get_cpc_opts(OptionList, sync, ?ERTS_CPC_ALLOW_GC), case Async of {async, ReqId} -> {priority, Prio} = erlang:process_info(erlang:self(), @@ -227,13 +229,12 @@ check_process_code(Pid, Module, OptionList) -> {check_process_code, ReqId, Module, - AllowGC}), + Flags}), async; sync -> case Pid == erlang:self() of true -> - erts_internal:check_process_code(Module, - [{allow_gc, AllowGC}]); + erts_internal:check_process_code(Module, Flags); false -> {priority, Prio} = erlang:process_info(erlang:self(), priority), @@ -243,7 +244,7 @@ check_process_code(Pid, Module, OptionList) -> {check_process_code, ReqId, Module, - AllowGC}), + Flags}), receive {check_process_code, ReqId, CheckResult} -> CheckResult @@ -251,13 +252,20 @@ check_process_code(Pid, Module, OptionList) -> end end. -% gets async and allow_gc opts and verify valid option list -get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, AllowGC) -> - get_cpc_opts(Options, AsyncTuple, AllowGC); -get_cpc_opts([{allow_gc, AllowGC} | Options], Async, _OldAllowGC) -> - get_cpc_opts(Options, Async, AllowGC); -get_cpc_opts([], Async, AllowGC) -> - {Async, AllowGC}. +% gets async and flag opts and verify valid option list +get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, Flags) -> + get_cpc_opts(Options, AsyncTuple, Flags); +get_cpc_opts([{allow_gc, AllowGC} | Options], Async, Flags) -> + get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_ALLOW_GC, AllowGC)); +get_cpc_opts([{copy_literals, CopyLit} | Options], Async, Flags) -> + get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_COPY_LITERALS, CopyLit)); +get_cpc_opts([], Async, Flags) -> + {Async, Flags}. + +cpc_flags(OldFlags, Bit, true) -> + OldFlags bor Bit; +cpc_flags(OldFlags, Bit, false) -> + OldFlags band (bnot Bit). -spec copy_literals(Module,Bool) -> 'true' | 'false' | 'aborted' when Module :: module(), -- cgit v1.2.3 From f5c9dd660f3f098ba1e2a4110be594013a6dff11 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 14 Jan 2016 12:07:39 +0100 Subject: Add documentation of '-path' flag to 'erl' This flag replaces the path specified in the boot script. It has always existed, but was earlier only documented in SASL (script). --- erts/doc/src/erl.xml | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index ec4a0dee05..e8621fecc3 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -382,6 +382,11 @@ similar to . See code(3).

+ + +

Replaces the path specified in the boot script. See + script(4).

+

Starts Erlang with a remote shell connected to .

-- cgit v1.2.3 From 43a7421c36d3feddb238b92426f938f11ef91174 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 14 Jan 2016 16:10:38 +0100 Subject: erts: Correct faulty doc for erlang:trace/3 The entire MFA tuple is replaced with 0, not just Arity. --- erts/doc/src/erlang.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index c37ed3bea5..20184bd7f3 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -8182,14 +8182,14 @@ timestamp() ->

When Pid is scheduled to run. The process runs in function {M, F, Arity}. On some rare occasions, the current function cannot be determined, - then the last element Arity is 0.

+ then the last element is 0.

{trace, Pid, out, {M, F, Arity} | 0}

When Pid is scheduled out. The process was running in function {M, F, Arity}. On some rare occasions, the current function cannot be determined, then the last - element Arity is 0.

+ element is 0.

{trace, Pid, gc_start, Info} -- cgit v1.2.3 From 52738d8bee488c262cf514ce4df49e87dfa47bc3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 18 Jan 2016 18:47:30 +0100 Subject: erts: Fix race between receive timeout and exit signal Must re-read 'state' after seizing proc locks as other thread may have set EXITING. --- erts/emulator/beam/erl_process.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 7b3d12ce09..d767e1bb5a 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -9674,6 +9674,8 @@ Process *schedule(Process *p, int calls) erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS); + state = erts_smp_atomic32_read_nob(&p->state); + if (erts_sched_stat.enabled) { int prio; UWord old = ERTS_PROC_SCHED_ID(p, -- cgit v1.2.3 From 693db9ef9c5a67b36215f21c32f91a986fc5630d Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Mon, 18 Jan 2016 14:04:24 -0500 Subject: Fix dirty scheduler check in handle_aux_work --- erts/emulator/beam/erl_process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 96d9a2f8b4..dd8bc9a698 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2239,7 +2239,7 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting) erts_aint32_t aux_work = orig_aux_work; erts_aint32_t ignore = 0; - ASSERT(!ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); #ifdef ERTS_SMP haw_thr_prgr_current_reset(awdp); #endif -- cgit v1.2.3 From f6c266765cfd48416000e49f0043827d42e0e83f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 19 Jan 2016 16:22:44 +0100 Subject: erts: Ignore unexpected messages to erts_code_purger --- erts/preloaded/ebin/erts_code_purger.beam | Bin 8884 -> 8768 bytes erts/preloaded/src/erts_code_purger.erl | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index ca6c8570ed..227d96d4c8 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl index e492ba1812..a64860bec8 100644 --- a/erts/preloaded/src/erts_code_purger.erl +++ b/erts/preloaded/src/erts_code_purger.erl @@ -40,8 +40,7 @@ loop() -> Res = do_soft_purge(Mod), From ! {reply, soft_purge, Res, Ref}; - M -> - erlang:display({"erts_code_purger got msg", M}) + _Other -> ignore end, loop(). -- cgit v1.2.3 From 34e02fed50bbaa2af7b1828968b6ec02a54e98c8 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 20 Jan 2016 09:54:00 +0100 Subject: erts: Improve the documentation of the abstract format --- erts/doc/src/absform.xml | 240 ++++++++++++++++++++++++++--------------------- 1 file changed, 131 insertions(+), 109 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 1c0c3e1319..3f47b3061b 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -4,7 +4,7 @@
- 20012015 + 20012016 Ericsson AB. All Rights Reserved. @@ -80,12 +80,15 @@ Rep(F) = {attribute,LINE,import,{Mod,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}}. If F is an attribute -export_type([Type_1/A_1, ..., Type_k/A_k]), then Rep(F) = {attribute,LINE,export_type,[{Type_1,A_1}, ..., {Type_k,A_k}]}. + If F is an attribute -optional_callbacks([Fun_1/A_1, ..., Fun_k/A_k]), then + Rep(F) = {attribute,LINE,optional_callbacks,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}. If F is an attribute -compile(Options), then Rep(F) = {attribute,LINE,compile,Options}. If F is an attribute -file(File,Line), then Rep(F) = {attribute,LINE,file,{File,Line}}. If F is a record declaration - -record(Name,{V_1, ..., V_k}), then Rep(F) = + -record(Name,{V_1, ..., V_k}), + where each V_i is a record field, then Rep(F) = {attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}. For Rep(V), see below. If F is a type declaration @@ -173,12 +176,12 @@
Patterns -

If Ps is a sequence of patterns P_1, ..., P_k, then +

If Ps is a sequence of patterns P_1, ..., P_k, then Rep(Ps) = [Rep(P_1), ..., Rep(P_k)]. Such sequences occur as the list of arguments to a function or fun.

Individual patterns are represented as follows:

- If P is an atomic literal L, then Rep(P) = Rep(L). + If P is an atomic literal L, then Rep(P) = Rep(L). If P is a compound pattern P_1 = P_2, then Rep(P) = {match,LINE,Rep(P_1),Rep(P_2)}. If P is a variable pattern V, then @@ -211,6 +214,10 @@ {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(P_k)}]}. If P is #Name.Field, then Rep(P) = {record_index,LINE,Name,Rep(Field)}. + If P is a map pattern #{A_1, ..., A_k}, where each + A_i is an association P_i_1 := P_i_2, then Rep(P) = + {map,LINE,[Rep(A_1), ..., Rep(A_k)]}. For Rep(A), see + below. If P is ( P_0 ), then Rep(P) = Rep(P_0), that is, patterns cannot be distinguished from their bodies. @@ -221,11 +228,11 @@
Expressions -

A body B is a sequence of expressions E_1, ..., E_k, and - Rep(B) = [Rep(E_1), ..., Rep(E_k)].

+

A body B is a nonempty sequence of expressions E_1, ..., E_k, + and Rep(B) = [Rep(E_1), ..., Rep(E_k)].

An expression E is one of the following alternatives:

- If P is an atomic literal L, then Rep(P) = Rep(L). + If E is an atomic literal L, then Rep(E) = Rep(L). If E is P = E_0, then Rep(E) = {match,LINE,Rep(P),Rep(E_0)}. If E is a variable V, then Rep(E) = {var,LINE,A}, @@ -256,14 +263,16 @@ Rep(E) = {record_index,LINE,Name,Rep(Field)}. If E is E_0#Name.Field, then Rep(E) = {record_field,LINE,Rep(E_0),Name,Rep(Field)}. - If E is #{W_1, ..., W_k} where each - W_i is a map assoc or exact field, then Rep(E) = - {map,LINE,[Rep(W_1), ..., Rep(W_k)]}. For Rep(W), see + If E is a map creation #{A_1, ..., A_k}, + where each A_i is an association E_i_1 => E_i_2 + or E_i_1 := E_i_2, then Rep(E) = + {map,LINE,[Rep(A_1), ..., Rep(A_k)]}. For Rep(A), see below. - If E is E_0#{W_1, ..., W_k} where - W_i is a map assoc or exact field, then Rep(E) = - {map,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}. - For Rep(W), see below. + If E is a map update E_0#{A_1, ..., A_k}, + where each A_i is an association E_i_1 => E_i_2 + or E_i_1 := E_i_2, then Rep(E) = + {map,LINE,Rep(E_0),[Rep(A_1), ..., Rep(A_k)]}. + For Rep(A), see below. If E is catch E_0, then Rep(E) = {'catch',LINE,Rep(E_0)}. If E is E_0(E_1, ..., E_k), then @@ -271,15 +280,15 @@ If E is E_m:E_0(E_1, ..., E_k), then Rep(E) = {call,LINE,{remote,LINE,Rep(E_m),Rep(E_0)},[Rep(E_1), ..., Rep(E_k)]}. - If E is a list comprehension [E_0 || W_1, ..., W_k], - where each W_i is a generator or a filter, then Rep(E) = - {lc,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}. For Rep(W), see + If E is a list comprehension [E_0 || Q_1, ..., Q_k], + where each Q_i is a qualifier, then Rep(E) = + {lc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}. For Rep(Q), see below. If E is a binary comprehension - <<E_0 || W_1, ..., W_k>>, - where each W_i is a generator or a filter, then - Rep(E) = {bc,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}. - For Rep(W), see below. + <<E_0 || Q_1, ..., Q_k>>, + where each Q_i is a qualifier, then + Rep(E) = {bc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}. + For Rep(Q), see below. If E is begin B end, where B is a body, then Rep(E) = {block,LINE,Rep(B)}. If E is if Ic_1 ; ... ; Ic_k end, @@ -311,7 +320,7 @@ {'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],Rep(A)}. If E is try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A end, where B and A are a bodies, - each Cc_i is a case clause and + each Cc_i is a case clause, and each Tc_j is a catch clause then Rep(E) = {'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],Rep(A)}. @@ -328,10 +337,10 @@ {'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}. (Before the R15 release: Rep(E) = {'fun',LINE,{function,Module,Name,Arity}}.) - If E is fun Fc_1 ; ... ; Fc_k end + If E is fun Fc_1 ; ... ; Fc_k end, where each Fc_i is a function clause then Rep(E) = {'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}. - If E is fun Name Fc_1 ; ... ; Name Fc_k end + If E is fun Name Fc_1 ; ... ; Name Fc_k end, where Name is a variable and each Fc_i is a function clause then Rep(E) = {named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}. @@ -342,46 +351,43 @@
- Generators and Filters -

When W is a generator or a filter (in the body of a list or - binary comprehension), then:

+ Qualifiers +

A qualifier Q is one of the following alternatives:

- If W is a generator P <- E, where P is + If Q is a generator P <- E, where P is a pattern and E is an expression, then - Rep(W) = {generate,LINE,Rep(P),Rep(E)}. - If W is a generator P <= E, where P is + Rep(Q) = {generate,LINE,Rep(P),Rep(E)}. + If Q is a generator P <= E, where P is a pattern and E is an expression, then - Rep(W) = {b_generate,LINE,Rep(P),Rep(E)}. - If W is a filter E, which is an expression, then - Rep(W) = Rep(E). + Rep(Q) = {b_generate,LINE,Rep(P),Rep(E)}. + If Q is a filter E, where E is an expression, then + Rep(Q) = Rep(E).
Binary Element Type Specifiers

A type specifier list TSL for a binary element is a sequence of type - specifiers TS_1 - ... - TS_k. + specifiers TS_1 - ... - TS_k, and Rep(TSL) = [Rep(TS_1), ..., Rep(TS_k)].

-

When TS is a type specifier for a binary element, then:

- If TS is an atom A, then Rep(TS) = A. - If TS is a couple A:Value where A is an atom - and Value is an integer, then Rep(TS) = - {A,Value}. + If TS is a type specifier A, where A is an atom, + then Rep(TS) = A. + If TS is a type specifier A:Value, + where A is an atom and Value is an integer, + then Rep(TS) = {A,Value}.
- Map Assoc and Exact Fields -

When W is an assoc or exact field (in the body of a map), then:

+ Associations +

An association A is one of the following alternatives:

- If W is an assoc field K => V, where - K and V are both expressions, - then Rep(W) = {map_field_assoc,LINE,Rep(K),Rep(V)}. + If A is an association K => V, + then Rep(A) = {map_field_assoc,LINE,Rep(K),Rep(V)}. - If W is an exact field K := V, where - K and V are both expressions, - then Rep(W) = {map_field_exact,LINE,Rep(K),Rep(V)}. + If A is an association K := V, + then Rep(A) = {map_field_exact,LINE,Rep(K),Rep(V)}.
@@ -393,37 +399,37 @@ and catch clauses.

A clause C is one of the following alternatives:

- If C is a function clause ( Ps ) -> B + If C is a function clause ( Ps ) -> B, where Ps is a pattern sequence and B is a body, then Rep(C) = {clause,LINE,Rep(Ps),[],Rep(B)}. - If C is a function clause ( Ps ) when Gs -> B + If C is a function clause ( Ps ) when Gs -> B, where Ps is a pattern sequence, Gs is a guard sequence and B is a body, then Rep(C) = {clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}. - If C is an if clause Gs -> B + If C is an if clause Gs -> B, where Gs is a guard sequence and B is a body, then Rep(C) = {clause,LINE,[],Rep(Gs),Rep(B)}. - If C is a case clause P -> B + If C is a case clause P -> B, where P is a pattern and B is a body, then Rep(C) = {clause,LINE,[Rep(P)],[],Rep(B)}. - If C is a case clause P when Gs -> B + If C is a case clause P when Gs -> B, where P is a pattern, Gs is a guard sequence and B is a body, then Rep(C) = {clause,LINE,[Rep(P)],Rep(Gs),Rep(B)}. - If C is a catch clause P -> B + If C is a catch clause P -> B, where P is a pattern and B is a body, then Rep(C) = {clause,LINE,[Rep({throw,P,_})],[],Rep(B)}. - If C is a catch clause X : P -> B + If C is a catch clause X : P -> B, where X is an atomic literal or a variable pattern, - P is a pattern and B is a body, then + P is a pattern, and B is a body, then Rep(C) = {clause,LINE,[Rep({X,P,_})],[],Rep(B)}. - If C is a catch clause P when Gs -> B - where P is a pattern, Gs is a guard sequence + If C is a catch clause P when Gs -> B, + where P is a pattern, Gs is a guard sequence, and B is a body, then Rep(C) = {clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}. - If C is a catch clause X : P when Gs -> B + If C is a catch clause X : P when Gs -> B, where X is an atomic literal or a variable pattern, - P is a pattern, Gs is a guard sequence + P is a pattern, Gs is a guard sequence, and B is a body, then Rep(C) = {clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}. @@ -439,7 +445,7 @@ [Rep(Gt_1), ..., Rep(Gt_k)].

A guard test Gt is one of the following alternatives:

- If Gt is an atomic literal L, then Rep(Gt) = Rep(L). + If Gt is an atomic literal L, then Rep(Gt) = Rep(L). If Gt is a variable pattern V, then Rep(Gt) = {var,LINE,A}, where A is an atom with a printname consisting of the same characters as V. @@ -467,15 +473,21 @@ Rep(Gt) = {record_index,LINE,Name,Rep(Field)}. If Gt is Gt_0#Name.Field, then Rep(Gt) = {record_field,LINE,Rep(Gt_0),Name,Rep(Field)}. + If Gt is a map creation #{A_1, ..., A_k}, + where each A_i is an association Gt_i_1 => Gt_i_2 + or Gt_i_1 := Gt_i_2, then Rep(Gt) = + {map,LINE,[Rep(A_1), ..., Rep(A_k)]}. For Rep(A), see + above. + If Gt is a map update Gt_0#{A_1, ..., A_k}, where each + A_i is an association Gt_i_1 => Gt_i_2 + or Gt_i_1 := Gt_i_2, then Rep(Gt) = + {map,LINE,Rep(Gt_0),[Rep(A_1), ..., Rep(A_k)]}. + For Rep(A), see above. If Gt is A(Gt_1, ..., Gt_k), where A is an atom, then Rep(Gt) = {call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}. If Gt is A_m:A(Gt_1, ..., Gt_k), where A_m is the atom erlang and A is an atom or an operator, then Rep(Gt) = {call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ..., Rep(Gt_k)]}. - If Gt is {A_m,A}(Gt_1, ..., Gt_k), where A_m is - the atom erlang and A is an atom or an operator, then - Rep(Gt) = {call,LINE,Rep({A_m,A}),[Rep(Gt_1), ..., Rep(Gt_k)]}. - If Gt is ( Gt_0 ), then Rep(Gt) = Rep(Gt_0), that is, parenthesized guard tests cannot be distinguished from their bodies. @@ -487,21 +499,20 @@
Types - If T is an annotated type Anno :: Type, - where Anno is a variable and - Type is a type, then Rep(T) = - {ann_type,LINE,[Rep(Anno),Rep(Type)]}. + If T is an annotated type A :: T_0, + where A is a variable, then Rep(T) = + {ann_type,LINE,[Rep(A),Rep(T_0)]}. If T is an atom or integer literal L, then Rep(T) = Rep(L). - If T is L Op R, - where Op is a binary operator and L and R - are types (this is an occurrence of an expression that can be - evaluated to an integer at compile time), then - Rep(T) = {op,LINE,Op,Rep(L),Rep(R)}. - If T is Op A, where Op is a - unary operator and A is a type (this is an occurrence of + If T is an operator type T_1 Op T_2, + where Op is a binary operator (this is an occurrence of + an expression that can be evaluated to an integer at compile + time), then + Rep(T) = {op,LINE,Op,Rep(T_1),Rep(T_2)}. + If T is an operator type Op T_0, where Op is a + unary operator (this is an occurrence of an expression that can be evaluated to an integer at compile time), - then Rep(T) = {op,LINE,Op,Rep(A)}. + then Rep(T) = {op,LINE,Op,Rep(T_0)}. If T is a bitstring type <<_:M,_:_*N>>, where M and N are singleton integer types, then Rep(T) = {type,LINE,binary,[Rep(M),Rep(N)]}. @@ -509,53 +520,44 @@ {type,Line,nil,[]}. If T is a fun type fun(), then Rep(T) = {type,LINE,'fun',[]}. - If T is a fun type fun((...) -> B), - where B is a type, then - Rep(T) = {type,LINE,'fun',[{type,LINE,any},Rep(B)]}. + If T is a fun type fun((...) -> T_0), then + Rep(T) = {type,LINE,'fun',[{type,LINE,any},Rep(T_0)]}. If T is a fun type fun(Ft), where Ft is a function type, - then Rep(T) = Rep(Ft). + then Rep(T) = Rep(Ft). For Rep(Ft), see below. If T is an integer range type L .. H, where L and H are singleton integer types, then Rep(T) = {type,LINE,range,[Rep(L),Rep(H)]}. If T is a map type map(), then Rep(T) = {type,LINE,map,any}. - If T is a map type #{P_1, ..., P_k}, where each - P_i is a map pair type, then Rep(T) = - {type,LINE,map,[Rep(P_1), ..., Rep(P_k)]}. - If T is a map pair type K => V, where - K and V are types, then Rep(T) = - {type,LINE,map_field_assoc,[Rep(K),Rep(V)]}. - If T is a predefined (or built-in) type N(A_1, ..., A_k), - where each A_i is a type, then Rep(T) = - {type,LINE,N,[Rep(A_1), ..., Rep(A_k)]}. + If T is a map type #{A_1, ..., A_k}, where each + A_i is an association type, then Rep(T) = + {type,LINE,map,[Rep(A_1), ..., Rep(A_k)]}. + For Rep(A), see below. + If T is a predefined (or built-in) type N(T_1, ..., T_k), + then Rep(T) = + {type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}. If T is a record type #Name{F_1, ..., F_k}, where each F_i is a record field type, then Rep(T) = {type,LINE,record,[Rep(Name),Rep(F_1), ..., Rep(F_k)]}. - - If T is a record field type Name :: Type, - where Type is a type, then Rep(T) = - {type,LINE,field_type,[Rep(Name),Rep(Type)]}. - If T is a remote type M:N(A_1, ..., A_k), where - each A_i is a type, then Rep(T) = - {remote_type,LINE,[Rep(M),Rep(N),[Rep(A_1), ..., Rep(A_k)]]}. + For Rep(F), see below. + If T is a remote type M:N(T_1, ..., T_k), then Rep(T) = + {remote_type,LINE,[Rep(M),Rep(N),[Rep(T_1), ..., Rep(T_k)]]}. If T is a tuple type tuple(), then Rep(T) = {type,LINE,tuple,any}. - If T is a tuple type {A_1, ..., A_k}, where - each A_i is a type, then Rep(T) = - {type,LINE,tuple,[Rep(A_1), ..., Rep(A_k)]}. - If T is a type union T_1 | ... | T_k, - where each T_i is a type, then Rep(T) = + If T is a tuple type {T_1, ..., T_k}, then Rep(T) = + {type,LINE,tuple,[Rep(T_1), ..., Rep(T_k)]}. + If T is a type union T_1 | ... | T_k, then Rep(T) = {type,LINE,union,[Rep(T_1), ..., Rep(T_k)]}. If T is a type variable V, then Rep(T) = {var,LINE,A}, where A is an atom with a printname consisting of the same characters as V. A type variable is any variable except underscore (_). - If T is a user-defined type N(A_1, ..., A_k), - where each A_i is a type, then Rep(T) = - {user_type,LINE,N,[Rep(A_1), ..., Rep(A_k)]}. + If T is a user-defined type N(T_1, ..., T_k), + then Rep(T) = + {user_type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}. If T is ( T_0 ), then Rep(T) = Rep(T_0), that is, parenthesized types cannot be distinguished from their bodies. @@ -563,15 +565,17 @@
Function Types +

A function type Ft is one of the following alternatives:

If Ft is a constrained function type Ft_1 when Fc, where Ft_1 is a function type and Fc is a function constraint, then Rep(T) = - {type,LINE,bounded_fun,[Rep(Ft_1),Rep(Fc)]}. - If Ft is a function type (A_1, ..., A_n) -> B, - where each A_i and B are types, then - Rep(Ft) = {type,LINE,'fun',[{type,LINE,product,[Rep(A_1), - ..., Rep(A_n)]},Rep(B)]}. + {type,LINE,bounded_fun,[Rep(Ft_1),Rep(Fc)]}. + For Rep(Fc), see below. + If Ft is a function type (T_1, ..., T_n) -> T_0, + where each T_i is a type, then + Rep(Ft) = {type,LINE,'fun',[{type,LINE,product,[Rep(T_1), + ..., Rep(T_n)]},Rep(T_0)]}.
@@ -587,6 +591,24 @@
+ +
+ Association Types + + If A is an association type K => V, where + K and V are types, then Rep(A) = + {type,LINE,map_field_assoc,[Rep(K),Rep(V)]}. + +
+ +
+ Record Field Types + + If F is a record field type Name :: Type, + where Type is a type, then Rep(F) = + {type,LINE,field_type,[Rep(Name),Rep(Type)]}. + +
-- cgit v1.2.3 From 6e2d941bf278191c11f6d1cebdfab5e51419d734 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 20 Jan 2016 09:55:21 +0100 Subject: erts: Improve readability of The Abstract Format More verbose, but hopefully more readable than before. --- erts/doc/src/absform.xml | 420 +++++++++++++++++++++++++---------------------- 1 file changed, 228 insertions(+), 192 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 3f47b3061b..ccdecf44ec 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -68,34 +68,29 @@ If D is a module declaration consisting of the forms F_1, ..., F_k, then Rep(D) = [Rep(F_1), ..., Rep(F_k)]. - If F is an attribute -module(Mod), then - Rep(F) = {attribute,LINE,module,Mod}. If F is an attribute -behavior(Behavior), then Rep(F) = {attribute,LINE,behavior,Behavior}. If F is an attribute -behaviour(Behaviour), then Rep(F) = {attribute,LINE,behaviour,Behaviour}. + If F is an attribute -compile(Options), then + Rep(F) = {attribute,LINE,compile,Options}. If F is an attribute -export([Fun_1/A_1, ..., Fun_k/A_k]), then Rep(F) = {attribute,LINE,export,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}. - If F is an attribute -import(Mod,[Fun_1/A_1, ..., Fun_k/A_k]), then - Rep(F) = {attribute,LINE,import,{Mod,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}}. If F is an attribute -export_type([Type_1/A_1, ..., Type_k/A_k]), then Rep(F) = {attribute,LINE,export_type,[{Type_1,A_1}, ..., {Type_k,A_k}]}. + If F is an attribute -import(Mod,[Fun_1/A_1, ..., Fun_k/A_k]), then + Rep(F) = {attribute,LINE,import,{Mod,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}}. + If F is an attribute -module(Mod), then + Rep(F) = {attribute,LINE,module,Mod}. If F is an attribute -optional_callbacks([Fun_1/A_1, ..., Fun_k/A_k]), then Rep(F) = {attribute,LINE,optional_callbacks,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}. - If F is an attribute -compile(Options), then - Rep(F) = {attribute,LINE,compile,Options}. If F is an attribute -file(File,Line), then Rep(F) = {attribute,LINE,file,{File,Line}}. - If F is a record declaration - -record(Name,{V_1, ..., V_k}), - where each V_i is a record field, then Rep(F) = - {attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}. - For Rep(V), see below. - If F is a type declaration - -Type Name(V_1, ..., V_k) :: T, where - Type is either the atom type or the atom opaque, - each V_i is a variable, and T is a type, then Rep(F) = - {attribute,LINE,Type,{Name,Rep(T),[Rep(V_1), ..., Rep(V_k)]}}. + If F is a function declaration + Name Fc_1 ; ... ; Name Fc_k, + where each Fc_i is a function clause with a + pattern sequence of the same length Arity, then + Rep(F) = {function,LINE,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}. If F is a function specification -Spec Name Ft_1; ...; Ft_k, @@ -112,15 +107,20 @@ Arity, then Rep(F) = {attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}. + If F is a record declaration + -record(Name,{V_1, ..., V_k}), + where each V_i is a record field, then Rep(F) = + {attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}. + For Rep(V), see below. + If F is a type declaration + -Type Name(V_1, ..., V_k) :: T, where + Type is either the atom type or the atom opaque, + each V_i is a variable, and T is a type, then Rep(F) = + {attribute,LINE,Type,{Name,Rep(T),[Rep(V_1), ..., Rep(V_k)]}}. + If F is a wild attribute -A(T), then Rep(F) = {attribute,LINE,A,T}.

- If F is a function declaration - Name Fc_1 ; ... ; Name Fc_k, - where each Fc_i is a function clause with a - pattern sequence of the same length Arity, then - Rep(F) = {function,LINE,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}. -
@@ -160,15 +160,15 @@

There are five kinds of atomic literals, which are represented in the same way in patterns, expressions and guards:

- If L is an integer or character literal, then - Rep(L) = {integer,LINE,L}. + If L is an atom literal, then + Rep(L) = {atom,LINE,L}. If L is a float literal, then Rep(L) = {float,LINE,L}. + If L is an integer or character literal, then + Rep(L) = {integer,LINE,L}. If L is a string literal consisting of the characters C_1, ..., C_k, then Rep(L) = {string,LINE,[C_1, ..., C_k]}. - If L is an atom literal, then - Rep(L) = {atom,LINE,L}.

Note that negative integer and float literals do not occur as such; they are parsed as an application of the unary negation operator.

@@ -182,45 +182,53 @@

Individual patterns are represented as follows:

If P is an atomic literal L, then Rep(P) = Rep(L). + If P is a binary pattern + <<P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>>, where each + Size_i is an expression that can be evaluated to an integer + and each TSL_i is a type specificer list, then + Rep(P) = {bin,LINE,[{bin_element,LINE,Rep(P_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(P_k),Rep(Size_k),Rep(TSL_k)}]}. + For Rep(TSL), see below. + An omitted Size_i is represented by default. + An omitted TSL_i is represented by default. If P is a compound pattern P_1 = P_2, then Rep(P) = {match,LINE,Rep(P_1),Rep(P_2)}. - If P is a variable pattern V, then - Rep(P) = {var,LINE,A}, - where A is an atom with a printname consisting of the same characters as - V. - If P is a universal pattern _, then - Rep(P) = {var,LINE,'_'}. - If P is a tuple pattern {P_1, ..., P_k}, then - Rep(P) = {tuple,LINE,[Rep(P_1), ..., Rep(P_k)]}. - If P is a nil pattern [], then - Rep(P) = {nil,LINE}. If P is a cons pattern [P_h | P_t], then Rep(P) = {cons,LINE,Rep(P_h),Rep(P_t)}. - If E is a binary pattern <<P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>>, then - Rep(E) = {bin,LINE,[{bin_element,LINE,Rep(P_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(P_k),Rep(Size_k),Rep(TSL_k)}]}. - For Rep(TSL), see below. - An omitted Size is represented by default. An omitted TSL - (type specifier list) is represented by default. - If P is P_1 Op P_2, where Op is a binary operator (this - is either an occurrence of ++ applied to a literal string or character - list, or an occurrence of an expression that can be evaluated to a number - at compile time), - then Rep(P) = {op,LINE,Op,Rep(P_1),Rep(P_2)}. - If P is Op P_0, where Op is a unary operator (this is an - occurrence of an expression that can be evaluated to a number at compile - time), then Rep(P) = {op,LINE,Op,Rep(P_0)}. - If P is a record pattern #Name{Field_1=P_1, ..., Field_k=P_k}, - then Rep(P) = - {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(P_k)}]}. - If P is #Name.Field, then - Rep(P) = {record_index,LINE,Name,Rep(Field)}. If P is a map pattern #{A_1, ..., A_k}, where each A_i is an association P_i_1 := P_i_2, then Rep(P) = {map,LINE,[Rep(A_1), ..., Rep(A_k)]}. For Rep(A), see below. - If P is ( P_0 ), then + If P is a nil pattern [], then + Rep(P) = {nil,LINE}. + If P is an operator pattern P_1 Op P_2, + where Op is a binary operator (this is either an occurrence + of ++ applied to a literal string or character + list, or an occurrence of an expression that can be evaluated to a number + at compile time), + then Rep(P) = {op,LINE,Op,Rep(P_1),Rep(P_2)}. + If P is an operator pattern Op P_0, + where Op is a unary operator (this is an occurrence of + an expression that can be evaluated to a number at compile + time), then Rep(P) = {op,LINE,Op,Rep(P_0)}. + If P is a parenthesized pattern ( P_0 ), then Rep(P) = Rep(P_0), - that is, patterns cannot be distinguished from their bodies. + that is, parenthesized patterns cannot be distinguished from their + bodies. + If P is a record field index pattern #Name.Field, + where Field is an atom, then + Rep(P) = {record_index,LINE,Name,Rep(Field)}. + If P is a record pattern + #Name{Field_1=P_1, ..., Field_k=P_k}, + where each Field_i is an atom or _, then Rep(P) = + {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(P_k)}]}. + If P is a tuple pattern {P_1, ..., P_k}, then + Rep(P) = {tuple,LINE,[Rep(P_1), ..., Rep(P_k)]}. + If P is a universal pattern _, then + Rep(P) = {var,LINE,'_'}. + If P is a variable pattern V, then + Rep(P) = {var,LINE,A}, + where A is an atom with a printname consisting of the same characters as + V.

Note that every pattern has the same source form as some expression, and is represented the same way as the corresponding expression.

@@ -233,36 +241,58 @@

An expression E is one of the following alternatives:

If E is an atomic literal L, then Rep(E) = Rep(L). - If E is P = E_0, then - Rep(E) = {match,LINE,Rep(P),Rep(E_0)}. - If E is a variable V, then Rep(E) = {var,LINE,A}, - where A is an atom with a printname consisting of the same - characters as V. - If E is a tuple skeleton {E_1, ..., E_k}, then - Rep(E) = {tuple,LINE,[Rep(E_1), ..., Rep(E_k)]}. - If E is [], then - Rep(E) = {nil,LINE}. + If E is a binary comprehension + <<E_0 || Q_1, ..., Q_k>>, + where each Q_i is a qualifier, then + Rep(E) = {bc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}. + For Rep(Q), see below. + If E is a binary constructor <<E_1:Size_1/TSL_1, ..., E_k:Size_k/TSL_k>>, + where each Size_i is an expression and each + TSL_i is a type specificer list, then Rep(E) = + {bin,LINE,[{bin_element,LINE,Rep(E_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(E_k),Rep(Size_k),Rep(TSL_k)}]}. + For Rep(TSL), see below. + An omitted Size_i is represented by default. + An omitted TSL_i is represented by default. + If E is a block expression begin B end, + where B is a body, then + Rep(E) = {block,LINE,Rep(B)}. + If E is a case expression case E_0 of Cc_1 ; ... ; Cc_k end, + where E_0 is an expression and each Cc_i is a + case clause then Rep(E) = + {'case',LINE,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}. + If E is a catch expression catch E_0, then + Rep(E) = {'catch',LINE,Rep(E_0)}. If E is a cons skeleton [E_h | E_t], then Rep(E) = {cons,LINE,Rep(E_h),Rep(E_t)}. - If E is a binary constructor <<V_1:Size_1/TSL_1, ..., V_k:Size_k/TSL_k>>, then Rep(E) = - {bin,LINE,[{bin_element,LINE,Rep(V_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(V_k),Rep(Size_k),Rep(TSL_k)}]}. - For Rep(TSL), see below. - An omitted Size is represented by default. An omitted TSL - (type specifier list) is represented by default. - If E is E_1 Op E_2, where Op is a binary operator, - then Rep(E) = {op,LINE,Op,Rep(E_1),Rep(E_2)}. - If E is Op E_0, where Op is a unary operator, then - Rep(E) = {op,LINE,Op,Rep(E_0)}. - If E is #Name{Field_1=E_1, ..., Field_k=E_k}, + If E is a fun expression fun Name/Arity, then + Rep(E) = {'fun',LINE,{function,Name,Arity}}. + If E is a fun expression + fun Module:Name/Arity, then Rep(E) = + {'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}. + (Before the R15 release: Rep(E) = + {'fun',LINE,{function,Module,Name,Arity}}.) + If E is a fun expression fun Fc_1 ; ... ; Fc_k end, + where each Fc_i is a function clause then Rep(E) = + {'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}. + If E is a fun expression + fun Name Fc_1 ; ... ; Name Fc_k end, + where Name is a variable and each + Fc_i is a function clause then Rep(E) = + {named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}. + + If E is a function call E_0(E_1, ..., E_k), then + Rep(E) = {call,LINE,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}. + If E is a function call E_m:E_0(E_1, ..., E_k), then Rep(E) = - {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}. - If E is E_0#Name{Field_1=E_1, ..., Field_k=E_k}, then - Rep(E) = - {record,LINE,Rep(E_0),Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}. - If E is #Name.Field, then - Rep(E) = {record_index,LINE,Name,Rep(Field)}. - If E is E_0#Name.Field, then - Rep(E) = {record_field,LINE,Rep(E_0),Name,Rep(Field)}. + {call,LINE,{remote,LINE,Rep(E_m),Rep(E_0)},[Rep(E_1), ..., Rep(E_k)]}. + + If E is an if expression if Ic_1 ; ... ; Ic_k end, + where each Ic_i is an if clause then Rep(E) = + {'if',LINE,[Rep(Ic_1), ..., Rep(Ic_k)]}. + If E is a list comprehension [E_0 || Q_1, ..., Q_k], + where each Q_i is a qualifier, then Rep(E) = + {lc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}. For Rep(Q), see + below. If E is a map creation #{A_1, ..., A_k}, where each A_i is an association E_i_1 => E_i_2 or E_i_1 := E_i_2, then Rep(E) = @@ -273,95 +303,92 @@ or E_i_1 := E_i_2, then Rep(E) = {map,LINE,Rep(E_0),[Rep(A_1), ..., Rep(A_k)]}. For Rep(A), see below. - If E is catch E_0, then - Rep(E) = {'catch',LINE,Rep(E_0)}. - If E is E_0(E_1, ..., E_k), then - Rep(E) = {call,LINE,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}. - If E is E_m:E_0(E_1, ..., E_k), then Rep(E) = - {call,LINE,{remote,LINE,Rep(E_m),Rep(E_0)},[Rep(E_1), ..., Rep(E_k)]}. - - If E is a list comprehension [E_0 || Q_1, ..., Q_k], - where each Q_i is a qualifier, then Rep(E) = - {lc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}. For Rep(Q), see - below. - If E is a binary comprehension - <<E_0 || Q_1, ..., Q_k>>, - where each Q_i is a qualifier, then - Rep(E) = {bc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}. - For Rep(Q), see below. - If E is begin B end, where B is a body, then - Rep(E) = {block,LINE,Rep(B)}. - If E is if Ic_1 ; ... ; Ic_k end, - where each Ic_i is an if clause then Rep(E) = - {'if',LINE,[Rep(Ic_1), ..., Rep(Ic_k)]}. - If E is case E_0 of Cc_1 ; ... ; Cc_k end, - where E_0 is an expression and each Cc_i is a - case clause then Rep(E) = - {'case',LINE,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}. - If E is try B catch Tc_1 ; ... ; Tc_k end, + If E is a match operator expression P = E_0, + where P is a pattern, then + Rep(E) = {match,LINE,Rep(P),Rep(E_0)}. + If E is nil, [], then + Rep(E) = {nil,LINE}. + If E is an operator expression E_1 Op E_2, + where Op is a binary operator other than the match + operator =, then + Rep(E) = {op,LINE,Op,Rep(E_1),Rep(E_2)}. + If E is an operator expression Op E_0, + where Op is a unary operator, then + Rep(E) = {op,LINE,Op,Rep(E_0)}. + If E is a parenthesized expression ( E_0 ), then + Rep(E) = Rep(E_0), that is, parenthesized + expressions cannot be distinguished from their bodies. + If E is a receive expression receive Cc_1 ; ... ; Cc_k end, + where each Cc_i is a case clause then Rep(E) = + {'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)]}. + If E is a receive expression + receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end, + where each Cc_i is a case clause, + E_0 is an expression and B_t is a body, then Rep(E) = + {'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)],Rep(E_0),Rep(B_t)}. + If E is a record creation + #Name{Field_1=E_1, ..., Field_k=E_k}, + where each Field_i is an atom or _, then Rep(E) = + {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}. + If E is a record field access E_0#Name.Field, + where Field is an atom, then + Rep(E) = {record_field,LINE,Rep(E_0),Name,Rep(Field)}. + If E is a record field index #Name.Field, + where Field is an atom, then + Rep(E) = {record_index,LINE,Name,Rep(Field)}. + If E is a record update + E_0#Name{Field_1=E_1, ..., Field_k=E_k}, + where each Field_i is an atom, then Rep(E) = + {record,LINE,Rep(E_0),Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}. + If E is a tuple skeleton {E_1, ..., E_k}, then + Rep(E) = {tuple,LINE,[Rep(E_1), ..., Rep(E_k)]}. + If E is a try expression try B catch Tc_1 ; ... ; Tc_k end, where B is a body and each Tc_i is a catch clause then Rep(E) = {'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],[]}. - If E is try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end, + If E is a try expression + try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end, where B is a body, each Cc_i is a case clause and each Tc_j is a catch clause then Rep(E) = {'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],[]}. - If E is try B after A end, + If E is a try expression try B after A end, where B and A are bodies then Rep(E) = {'try',LINE,Rep(B),[],[],Rep(A)}. - If E is try B of Cc_1 ; ... ; Cc_k after A end, + If E is a try expression + try B of Cc_1 ; ... ; Cc_k after A end, where B and A are a bodies and each Cc_i is a case clause then Rep(E) = {'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[],Rep(A)}. - If E is try B catch Tc_1 ; ... ; Tc_k after A end, + If E is a try expression + try B catch Tc_1 ; ... ; Tc_k after A end, where B and A are bodies and each Tc_i is a catch clause then Rep(E) = {'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],Rep(A)}. - If E is try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A end, + If E is a try expression + try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A end, where B and A are a bodies, each Cc_i is a case clause, and each Tc_j is a catch clause then Rep(E) = {'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],Rep(A)}. - If E is receive Cc_1 ; ... ; Cc_k end, - where each Cc_i is a case clause then Rep(E) = - {'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)]}. - If E is receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end, - where each Cc_i is a case clause, - E_0 is an expression and B_t is a body, then Rep(E) = - {'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)],Rep(E_0),Rep(B_t)}. - If E is fun Name / Arity, then - Rep(E) = {'fun',LINE,{function,Name,Arity}}. - If E is fun Module:Name/Arity, then Rep(E) = - {'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}. - (Before the R15 release: Rep(E) = - {'fun',LINE,{function,Module,Name,Arity}}.) - If E is fun Fc_1 ; ... ; Fc_k end, - where each Fc_i is a function clause then Rep(E) = - {'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}. - If E is fun Name Fc_1 ; ... ; Name Fc_k end, - where Name is a variable and each - Fc_i is a function clause then Rep(E) = - {named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}. - - If E is ( E_0 ), then - Rep(E) = Rep(E_0), that is, parenthesized - expressions cannot be distinguished from their bodies. + If E is a variable V, then Rep(E) = {var,LINE,A}, + where A is an atom with a printname consisting of the same + characters as V.
Qualifiers

A qualifier Q is one of the following alternatives:

+ If Q is a filter E, where E is an expression, then + Rep(Q) = Rep(E). If Q is a generator P <- E, where P is a pattern and E is an expression, then Rep(Q) = {generate,LINE,Rep(P),Rep(E)}. If Q is a generator P <= E, where P is a pattern and E is an expression, then Rep(Q) = {b_generate,LINE,Rep(P),Rep(E)}. - If Q is a filter E, where E is an expression, then - Rep(Q) = Rep(E).
@@ -399,16 +426,6 @@ and catch clauses.

A clause C is one of the following alternatives:

- If C is a function clause ( Ps ) -> B, - where Ps is a pattern sequence and B is a body, then - Rep(C) = {clause,LINE,Rep(Ps),[],Rep(B)}. - If C is a function clause ( Ps ) when Gs -> B, - where Ps is a pattern sequence, - Gs is a guard sequence and B is a body, then - Rep(C) = {clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}. - If C is an if clause Gs -> B, - where Gs is a guard sequence and B is a body, then - Rep(C) = {clause,LINE,[],Rep(Gs),Rep(B)}. If C is a case clause P -> B, where P is a pattern and B is a body, then Rep(C) = {clause,LINE,[Rep(P)],[],Rep(B)}. @@ -432,6 +449,16 @@ P is a pattern, Gs is a guard sequence, and B is a body, then Rep(C) = {clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}. + If C is a function clause ( Ps ) -> B, + where Ps is a pattern sequence and B is a body, then + Rep(C) = {clause,LINE,Rep(Ps),[],Rep(B)}. + If C is a function clause ( Ps ) when Gs -> B, + where Ps is a pattern sequence, + Gs is a guard sequence and B is a body, then + Rep(C) = {clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}. + If C is an if clause Gs -> B, + where Gs is a guard sequence and B is a body, then + Rep(C) = {clause,LINE,[],Rep(Gs),Rep(B)}.
@@ -446,33 +473,23 @@

A guard test Gt is one of the following alternatives:

If Gt is an atomic literal L, then Rep(Gt) = Rep(L). - If Gt is a variable pattern V, then - Rep(Gt) = {var,LINE,A}, where A is an atom with - a printname consisting of the same characters as V. - If Gt is a tuple skeleton {Gt_1, ..., Gt_k}, then - Rep(Gt) = {tuple,LINE,[Rep(Gt_1), ..., Rep(Gt_k)]}. - If Gt is [], then Rep(Gt) = {nil,LINE}. - If Gt is a cons skeleton [Gt_h | Gt_t], then - Rep(Gt) = {cons,LINE,Rep(Gt_h),Rep(Gt_t)}. If Gt is a binary constructor - <<Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>>, then + <<Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>>, + where each Size_i is a guard test and each + TSL_i is a type specificer list, then Rep(Gt) = {bin,LINE,[{bin_element,LINE,Rep(Gt_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(Gt_k),Rep(Size_k),Rep(TSL_k)}]}. For Rep(TSL), see above. - An omitted Size is represented by default. - An omitted TSL (type specifier list) is represented - by default. - If Gt is Gt_1 Op Gt_2, where Op - is a binary operator, then Rep(Gt) = - {op,LINE,Op,Rep(Gt_1),Rep(Gt_2)}. - If Gt is Op Gt_0, where Op is a unary operator, then - Rep(Gt) = {op,LINE,Op,Rep(Gt_0)}. - If Gt is #Name{Field_1=Gt_1, ..., Field_k=Gt_k}, then - Rep(E) = - {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(Gt_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(Gt_k)}]}. - If Gt is #Name.Field, then - Rep(Gt) = {record_index,LINE,Name,Rep(Field)}. - If Gt is Gt_0#Name.Field, then - Rep(Gt) = {record_field,LINE,Rep(Gt_0),Name,Rep(Field)}. + An omitted Size_i is represented by default. + An omitted TSL_i is represented by default.
+ If Gt is a cons skeleton [Gt_h | Gt_t], then + Rep(Gt) = {cons,LINE,Rep(Gt_h),Rep(Gt_t)}. + If Gt is a function call A(Gt_1, ..., Gt_k), + where A is an atom, then Rep(Gt) = + {call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}. + If Gt is a function call A_m:A(Gt_1, ..., Gt_k), + where A_m is the atom erlang and A is + an atom or an operator, then Rep(Gt) = + {call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ..., Rep(Gt_k)]}. If Gt is a map creation #{A_1, ..., A_k}, where each A_i is an association Gt_i_1 => Gt_i_2 or Gt_i_1 := Gt_i_2, then Rep(Gt) = @@ -483,14 +500,33 @@ or Gt_i_1 := Gt_i_2, then Rep(Gt) = {map,LINE,Rep(Gt_0),[Rep(A_1), ..., Rep(A_k)]}. For Rep(A), see above. - If Gt is A(Gt_1, ..., Gt_k), where A is an atom, then - Rep(Gt) = {call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}. - If Gt is A_m:A(Gt_1, ..., Gt_k), where A_m is - the atom erlang and A is an atom or an operator, then - Rep(Gt) = {call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ..., Rep(Gt_k)]}. - If Gt is ( Gt_0 ), then + If Gt is nil, [], + then Rep(Gt) = {nil,LINE}. + If Gt is an operator guard test Gt_1 Op Gt_2, + where Op is a binary operator other than the match + operator =, then + Rep(Gt) = {op,LINE,Op,Rep(Gt_1),Rep(Gt_2)}. + If Gt is an operator guard test Op Gt_0, + where Op is a unary operator, then + Rep(Gt) = {op,LINE,Op,Rep(Gt_0)}. + If Gt is a parenthesized guard test ( Gt_0 ), then Rep(Gt) = Rep(Gt_0), that is, parenthesized guard tests cannot be distinguished from their bodies. + If Gt is a record creation + #Name{Field_1=Gt_1, ..., Field_k=Gt_k}, + where each Field_i is an atom or _, then Rep(Gt) = + {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(Gt_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(Gt_k)}]}. + If Gt is a record field access Gt_0#Name.Field, + where Field is an atom, then + Rep(Gt) = {record_field,LINE,Rep(Gt_0),Name,Rep(Field)}. + If Gt is a record field index #Name.Field, + where Field is an atom, then + Rep(Gt) = {record_index,LINE,Name,Rep(Field)}. + If Gt is a tuple skeleton {Gt_1, ..., Gt_k}, then + Rep(Gt) = {tuple,LINE,[Rep(Gt_1), ..., Rep(Gt_k)]}. + If Gt is a variable pattern V, then + Rep(Gt) = {var,LINE,A}, where A is an atom with + a printname consisting of the same characters as V.

Note that every guard test has the same source form as some expression, and is represented the same way as the corresponding expression.

@@ -504,15 +540,6 @@ {ann_type,LINE,[Rep(A),Rep(T_0)]}.
If T is an atom or integer literal L, then Rep(T) = Rep(L). - If T is an operator type T_1 Op T_2, - where Op is a binary operator (this is an occurrence of - an expression that can be evaluated to an integer at compile - time), then - Rep(T) = {op,LINE,Op,Rep(T_1),Rep(T_2)}. - If T is an operator type Op T_0, where Op is a - unary operator (this is an occurrence of - an expression that can be evaluated to an integer at compile time), - then Rep(T) = {op,LINE,Op,Rep(T_0)}. If T is a bitstring type <<_:M,_:_*N>>, where M and N are singleton integer types, then Rep(T) = {type,LINE,binary,[Rep(M),Rep(N)]}. @@ -535,6 +562,18 @@ A_i is an association type, then Rep(T) = {type,LINE,map,[Rep(A_1), ..., Rep(A_k)]}. For Rep(A), see below. + If T is an operator type T_1 Op T_2, + where Op is a binary operator (this is an occurrence of + an expression that can be evaluated to an integer at compile + time), then + Rep(T) = {op,LINE,Op,Rep(T_1),Rep(T_2)}. + If T is an operator type Op T_0, where Op is a + unary operator (this is an occurrence of + an expression that can be evaluated to an integer at compile time), + then Rep(T) = {op,LINE,Op,Rep(T_0)}. + If T is ( T_0 ), then Rep(T) = Rep(T_0), + that is, parenthesized types cannot be distinguished from their + bodies. If T is a predefined (or built-in) type N(T_1, ..., T_k), then Rep(T) = {type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}. @@ -558,9 +597,6 @@ If T is a user-defined type N(T_1, ..., T_k), then Rep(T) = {user_type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}. - If T is ( T_0 ), then Rep(T) = Rep(T_0), - that is, parenthesized types cannot be distinguished from their - bodies.
-- cgit v1.2.3 From 858c6f7fa44f7b2dc363b359198d6522dd60e914 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 5 Jan 2016 16:55:04 +0100 Subject: Introduce time warp safe trace timestamp formats New timestamp options for trace, sequential trace, and system profile: - monotonic_timestamp - strict_monotonic_timestamp --- erts/doc/src/erlang.xml | 64 ++- erts/emulator/beam/atom.names | 2 + erts/emulator/beam/erl_bif_trace.c | 41 +- erts/emulator/beam/erl_process.c | 1 + erts/emulator/beam/erl_process.h | 87 ++-- erts/emulator/beam/erl_trace.c | 700 +++++++++++++++++----------- erts/emulator/beam/erl_trace.h | 32 +- erts/emulator/test/system_profile_SUITE.erl | 155 ++++-- erts/emulator/test/trace_bif_SUITE.erl | 249 ++++++---- erts/preloaded/ebin/erlang.beam | Bin 102000 -> 102116 bytes erts/preloaded/src/erlang.erl | 13 +- 11 files changed, 905 insertions(+), 439 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 64eebec936..79d3f66ea8 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -7739,6 +7739,13 @@ ok inactive, and later active when the port callback returns.

+ monotonic_timestamp + +

Timestamps in profile messages will use + Erlang + monotonic time. The time-stamp (Ts) has the same + format and value as produced by erlang:monotonic_time().

+
runnable_procs

If a process is put into or removed from the run queue, a @@ -7759,6 +7766,25 @@ ok {profile, scheduler, Id, State, NoScheds, Ts}, is sent to ProfilerPid.

+ strict_monotonic_timestamp + +

Timestamps in profile messages will consisting of + Erlang + monotonic time and a monotonically increasing + integer. The time-stamp (Ts) has the same format and value + as produced by {erlang:monotonic_time(), + erlang:unique_integer([monotonic])}.

+
+ timestamp + +

Timestamps in profile messages will include a + time-stamp (Ts) that has the same form as returned by + erlang:now(). This is also the default if no + timestamp flag is given. If cpu_timestamp has + been enabled via erlang:trace/3, this will also + effect the timestamp produced in profiling messages + when timestamp flag is enabled.

+

erlang:system_profile is considered experimental and its behavior can change in a future release.

@@ -8118,7 +8144,10 @@ timestamp() -> cpu_timestamp

A global trace flag for the Erlang node that makes all - trace time-stamps to be in CPU time, not wall clock time. + trace time-stamps using the timestamp flag to be + in CPU time, not wall clock time. That is, cpu_timestamp + will not be used if monotonic_timestamp, or + strict_monotonic_timestamp is enabled. Only allowed with PidSpec==all. If the host machine OS does not support high-resolution CPU time measurements, trace/3 exits with @@ -8126,6 +8155,26 @@ timestamp() -> not synchronize this value across cores, so be prepared that time might seem to go backwards when using this option.

+ monotonic_timestamp + +

Includes an + Erlang + monotonic time time-stamp in all trace messages. The + time-stamp (Ts) has the same format and value as produced by + erlang:monotonic_time(). This flag overrides + the cpu_timestamp flag.

+
+ strict_monotonic_timestamp + +

Includes an timestamp consisting of + Erlang + monotonic time and a monotonically increasing + integer in all trace messages. The time-stamp (Ts) has the + same format and value as produced by + {erlang:monotonic_time(), + erlang:unique_integer([monotonic])}. This flag overrides + the cpu_timestamp flag.

+
arity

Used with the call trace flag. @@ -8172,9 +8221,16 @@ timestamp() -> in the following list. Pid is the process identifier of the traced process in which the traced event has occurred. The third tuple element is the message tag.

-

If flag timestamp is given, the first tuple - element is trace_ts instead, and the time-stamp - is added last in the message tuple.

+

If flag timestamp, strict_monotonic_timestamp, or + monotonic_timestamp is given, the first tuple + element is trace_ts instead, and the time-stamp + is added as an extra element last in the message tuple. If + multiple timestamp flags are passed, timestamp has + precedence over strict_monotonic_timestamp which + in turn has precedence over monotonic_timestamp. All + timestamp flags are remembered, so if two are passed + and the one with highest precedence later is disabled + the other one will become active.

{trace, Pid, 'receive', Msg} diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index fb3368eae2..07f6492948 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -370,6 +370,7 @@ atom monitor atom monitor_nodes atom monitors atom monotonic +atom monotonic_timestamp atom more atom multi_scheduling atom multiline @@ -559,6 +560,7 @@ atom static atom stderr_to_stdout atom stop atom stream +atom strict_monotonic_timestamp atom sunrm atom suspend atom suspended diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 03f51132b1..08807d72c9 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -431,6 +431,9 @@ Uint erts_trace_flag2bit(Eterm flag) { switch (flag) { + case am_timestamp: return F_NOW_TS; + case am_strict_monotonic_timestamp: return F_STRICT_MON_TS; + case am_monotonic_timestamp: return F_MON_TS; case am_all: return TRACEE_FLAGS; case am_send: return F_TRACE_SEND; case am_receive: return F_TRACE_RECEIVE; @@ -439,7 +442,6 @@ erts_trace_flag2bit(Eterm flag) case am_set_on_first_spawn: return F_TRACE_SOS1; case am_set_on_link: return F_TRACE_SOL; case am_set_on_first_link: return F_TRACE_SOL1; - case am_timestamp: return F_TIMESTAMP; case am_running: return F_TRACE_SCHED; case am_exiting: return F_TRACE_SCHED_EXIT; case am_garbage_collection: return F_TRACE_GC; @@ -592,7 +594,7 @@ Eterm trace_3(BIF_ALIST_3) ERTS_TRACE_FLAGS(tracee_port) |= mask; else ERTS_TRACE_FLAGS(tracee_port) &= ~mask; - + if (!ERTS_TRACE_FLAGS(tracee_port)) ERTS_TRACER_PROC(tracee_port) = NIL; else if (tracer != NIL) @@ -978,7 +980,7 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) } if (key == am_flags) { - int num_flags = 19; /* MAXIMUM number of flags. */ + int num_flags = 21; /* MAXIMUM number of flags. */ Uint needed = 3+2*num_flags; Eterm flag_list = NIL; Eterm* limit; @@ -996,6 +998,9 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) #endif hp = HAlloc(p, needed); limit = hp+needed; + FLAG(F_NOW_TS, am_timestamp); + FLAG(F_STRICT_MON_TS, am_strict_monotonic_timestamp); + FLAG(F_MON_TS, am_monotonic_timestamp); FLAG(F_TRACE_SEND, am_send); FLAG(F_TRACE_RECEIVE, am_receive); FLAG(F_TRACE_SOS, am_set_on_spawn); @@ -1007,7 +1012,6 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) FLAG(F_TRACE_SCHED, am_running); FLAG(F_TRACE_SCHED_EXIT, am_exiting); FLAG(F_TRACE_GC, am_garbage_collection); - FLAG(F_TIMESTAMP, am_timestamp); FLAG(F_TRACE_ARITY_ONLY, am_arity); FLAG(F_TRACE_RETURN_TO, am_return_to); FLAG(F_TRACE_SILENT, am_silent); @@ -1798,7 +1802,11 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, } else if (arg1 == am_print) { current_flag = SEQ_TRACE_PRINT; } else if (arg1 == am_timestamp) { - current_flag = SEQ_TRACE_TIMESTAMP; + current_flag = SEQ_TRACE_NOW_TS; + } else if (arg1 == am_strict_monotonic_timestamp) { + current_flag = SEQ_TRACE_STRICT_MON_TS; + } else if (arg1 == am_monotonic_timestamp) { + current_flag = SEQ_TRACE_MON_TS; } else current_flag = 0; @@ -1909,7 +1917,9 @@ BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item) #endif ) { if ((item == am_send) || (item == am_receive) || - (item == am_print) || (item == am_timestamp)) { + (item == am_print) || (item == am_timestamp) + || (item == am_monotonic_timestamp) + || (item == am_strict_monotonic_timestamp)) { hp = HAlloc(p,3); res = TUPLE2(hp, item, am_false); BIF_RET(res); @@ -1927,7 +1937,11 @@ BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item) } else if (item == am_print) { current_flag = SEQ_TRACE_PRINT; } else if (item == am_timestamp) { - current_flag = SEQ_TRACE_TIMESTAMP; + current_flag = SEQ_TRACE_NOW_TS; + } else if (item == am_strict_monotonic_timestamp) { + current_flag = SEQ_TRACE_STRICT_MON_TS; + } else if (item == am_monotonic_timestamp) { + current_flag = SEQ_TRACE_MON_TS; } else { current_flag = 0; } @@ -2237,6 +2251,7 @@ static Eterm system_profile_get(Process *p) { if (erts_system_profile_flags.exclusive) { res = CONS(hp, am_exclusive, res); hp += 2; } + return TUPLE2(hp, system_profile, res); } } @@ -2255,6 +2270,7 @@ BIF_RETTYPE system_profile_2(BIF_ALIST_2) int system_blocked = 0; Process *profiler_p = NULL; Port *profiler_port = NULL; + int ts; if (profiler == am_undefined || list == NIL) { prev = system_profile_get(p); @@ -2286,7 +2302,8 @@ BIF_RETTYPE system_profile_2(BIF_ALIST_2) goto error; } - for (scheduler = 0, runnable_ports = 0, runnable_procs = 0, exclusive = 0; + for (ts = ERTS_TRACE_FLG_NOW_TIMESTAMP, scheduler = 0, + runnable_ports = 0, runnable_procs = 0, exclusive = 0; is_list(list); list = CDR(list_val(list))) { @@ -2299,6 +2316,12 @@ BIF_RETTYPE system_profile_2(BIF_ALIST_2) exclusive = !0; } else if (t == am_scheduler) { scheduler = !0; + } else if (t == am_timestamp) { + ts = ERTS_TRACE_FLG_NOW_TIMESTAMP; + } else if (t == am_strict_monotonic_timestamp) { + ts = ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP; + } else if (t == am_monotonic_timestamp) { + ts = ERTS_TRACE_FLG_MONOTONIC_TIMESTAMP; } else goto error; } if (is_not_nil(list)) goto error; @@ -2311,7 +2334,7 @@ BIF_RETTYPE system_profile_2(BIF_ALIST_2) erts_system_profile_flags.runnable_ports = !!runnable_ports; erts_system_profile_flags.runnable_procs = !!runnable_procs; erts_system_profile_flags.exclusive = !!exclusive; - + erts_system_profile_ts_type = ts; erts_smp_thr_progress_unblock(); erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b47aae0f74..c1d2e8db8e 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -353,6 +353,7 @@ struct erts_system_monitor_flags_t erts_system_monitor_flags; /* system performance monitor */ Eterm erts_system_profile; struct erts_system_profile_flags_t erts_system_profile_flags; +int erts_system_profile_ts_type = ERTS_TRACE_FLG_NOW_TIMESTAMP; #if ERTS_MAX_PROCESSES > 0x7fffffff #error "Need to store process_count in another type" diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index fd7d4183f4..6512710f48 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -60,6 +60,9 @@ typedef struct process Process; #include "erl_mseg.h" #include "erl_async.h" #include "erl_gc.h" +#define ERTS_ONLY_INCLUDE_TRACE_FLAGS +#include "erl_trace.h" +#undef ERTS_ONLY_INCLUDE_TRACE_FLAGS #ifdef HIPE #include "hipe_process.h" @@ -1292,6 +1295,7 @@ struct erts_system_profile_flags_t { unsigned int exclusive : 1; }; extern struct erts_system_profile_flags_t erts_system_profile_flags; +extern int erts_system_profile_ts_type; /* process flags */ #define F_HIBERNATE_SCHED (1 << 0) /* Schedule out after hibernate op */ @@ -1307,61 +1311,90 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; #define F_FORCE_GC (1 << 10) /* Force gc at process in-scheduling */ #define F_DISABLE_GC (1 << 11) /* Disable GC */ +#define ERTS_TRACE_FLAGS_TS_TYPE_SHIFT 0 + +#define F_TRACE_FLAG(N) (1 << (ERTS_TRACE_TS_TYPE_BITS + (N))) + /* process trace_flags */ -#define F_SENSITIVE (1 << 0) -#define F_TRACE_SEND (1 << 1) -#define F_TRACE_RECEIVE (1 << 2) -#define F_TRACE_SOS (1 << 3) /* Set on spawn */ -#define F_TRACE_SOS1 (1 << 4) /* Set on first spawn */ -#define F_TRACE_SOL (1 << 5) /* Set on link */ -#define F_TRACE_SOL1 (1 << 6) /* Set on first link */ -#define F_TRACE_CALLS (1 << 7) -#define F_TIMESTAMP (1 << 8) -#define F_TRACE_PROCS (1 << 9) -#define F_TRACE_FIRST_CHILD (1 << 10) -#define F_TRACE_SCHED (1 << 11) -#define F_TRACE_GC (1 << 12) -#define F_TRACE_ARITY_ONLY (1 << 13) -#define F_TRACE_RETURN_TO (1 << 14) /* Return_to trace when breakpoint tracing */ -#define F_TRACE_SILENT (1 << 15) /* No call trace msg suppress */ -#define F_TRACER (1 << 16) /* May be (has been) tracer */ -#define F_EXCEPTION_TRACE (1 << 17) /* May have exception trace on stack */ + +#define F_NOW_TS (ERTS_TRACE_FLG_NOW_TIMESTAMP \ + << ERTS_TRACE_FLAGS_TS_TYPE_SHIFT) +#define F_STRICT_MON_TS (ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP \ + << ERTS_TRACE_FLAGS_TS_TYPE_SHIFT) +#define F_MON_TS (ERTS_TRACE_FLG_MONOTONIC_TIMESTAMP \ + << ERTS_TRACE_FLAGS_TS_TYPE_SHIFT) +#define F_SENSITIVE F_TRACE_FLAG(0) +#define F_TRACE_SEND F_TRACE_FLAG(1) +#define F_TRACE_RECEIVE F_TRACE_FLAG(2) +#define F_TRACE_SOS F_TRACE_FLAG(3) /* Set on spawn */ +#define F_TRACE_SOS1 F_TRACE_FLAG(4) /* Set on first spawn */ +#define F_TRACE_SOL F_TRACE_FLAG(5) /* Set on link */ +#define F_TRACE_SOL1 F_TRACE_FLAG(6) /* Set on first link */ +#define F_TRACE_CALLS F_TRACE_FLAG(7) +#define F_TRACE_PROCS F_TRACE_FLAG(8) +#define F_TRACE_FIRST_CHILD F_TRACE_FLAG(9) +#define F_TRACE_SCHED F_TRACE_FLAG(10) +#define F_TRACE_GC F_TRACE_FLAG(11) +#define F_TRACE_ARITY_ONLY F_TRACE_FLAG(12) +#define F_TRACE_RETURN_TO F_TRACE_FLAG(13) /* Return_to trace when breakpoint tracing */ +#define F_TRACE_SILENT F_TRACE_FLAG(14) /* No call trace msg suppress */ +#define F_TRACER F_TRACE_FLAG(15) /* May be (has been) tracer */ +#define F_EXCEPTION_TRACE F_TRACE_FLAG(16) /* May have exception trace on stack */ /* port trace flags, currently the same as process trace flags */ -#define F_TRACE_SCHED_PORTS (1 << 18) /* Trace of port scheduling */ -#define F_TRACE_SCHED_PROCS (1 << 19) /* With virtual scheduling */ -#define F_TRACE_PORTS (1 << 20) /* Ports equivalent to F_TRACE_PROCS */ -#define F_TRACE_SCHED_NO (1 << 21) /* Trace with scheduler id */ -#define F_TRACE_SCHED_EXIT (1 << 22) +#define F_TRACE_SCHED_PORTS F_TRACE_FLAG(17) /* Trace of port scheduling */ +#define F_TRACE_SCHED_PROCS F_TRACE_FLAG(18) /* With virtual scheduling */ +#define F_TRACE_PORTS F_TRACE_FLAG(19) /* Ports equivalent to F_TRACE_PROCS */ +#define F_TRACE_SCHED_NO F_TRACE_FLAG(20) /* Trace with scheduler id */ +#define F_TRACE_SCHED_EXIT F_TRACE_FLAG(21) -#define F_NUM_FLAGS 23 +#define F_NUM_FLAGS (ERTS_TRACE_TS_TYPE_BITS + 22) #ifdef DEBUG # define F_INITIAL_TRACE_FLAGS (5 << F_NUM_FLAGS) #else # define F_INITIAL_TRACE_FLAGS 0 #endif +/* F_TIMESTAMP_MASK is a bit-field of all all timestamp types */ +#define F_TIMESTAMP_MASK \ + (ERTS_TRACE_TS_TYPE_MASK << ERTS_TRACE_FLAGS_TS_TYPE_SHIFT) + #define TRACEE_FLAGS ( F_TRACE_PROCS | F_TRACE_CALLS \ | F_TRACE_SOS | F_TRACE_SOS1| F_TRACE_RECEIVE \ | F_TRACE_SOL | F_TRACE_SOL1 | F_TRACE_SEND \ - | F_TRACE_SCHED | F_TIMESTAMP | F_TRACE_GC \ + | F_TRACE_SCHED | F_TIMESTAMP_MASK | F_TRACE_GC \ | F_TRACE_ARITY_ONLY | F_TRACE_RETURN_TO \ | F_TRACE_SILENT | F_TRACE_SCHED_PROCS | F_TRACE_PORTS \ | F_TRACE_SCHED_PORTS | F_TRACE_SCHED_NO \ | F_TRACE_SCHED_EXIT) #define ERTS_TRACEE_MODIFIER_FLAGS \ - (F_TRACE_SILENT | F_TIMESTAMP | F_TRACE_SCHED_NO) + (F_TRACE_SILENT | F_TIMESTAMP_MASK | F_TRACE_SCHED_NO) #define ERTS_PORT_TRACEE_FLAGS \ (ERTS_TRACEE_MODIFIER_FLAGS | F_TRACE_PORTS | F_TRACE_SCHED_PORTS) #define ERTS_PROC_TRACEE_FLAGS \ ((TRACEE_FLAGS & ~ERTS_PORT_TRACEE_FLAGS) | ERTS_TRACEE_MODIFIER_FLAGS) +#define SEQ_TRACE_FLAG(N) (1 << (ERTS_TRACE_TS_TYPE_BITS + (N))) + /* Sequential trace flags */ + +/* SEQ_TRACE_TIMESTAMP_MASK is a bit-field */ +#define SEQ_TRACE_TIMESTAMP_MASK \ + (ERTS_TRACE_TS_TYPE_MASK << ERTS_SEQ_TRACE_FLAGS_TS_TYPE_SHIFT) + #define SEQ_TRACE_SEND (1 << 0) #define SEQ_TRACE_RECEIVE (1 << 1) #define SEQ_TRACE_PRINT (1 << 2) -#define SEQ_TRACE_TIMESTAMP (1 << 3) + +#define ERTS_SEQ_TRACE_FLAGS_TS_TYPE_SHIFT 3 + +#define SEQ_TRACE_NOW_TS (ERTS_TRACE_FLG_NOW_TIMESTAMP \ + << ERTS_SEQ_TRACE_FLAGS_TS_TYPE_SHIFT) +#define SEQ_TRACE_STRICT_MON_TS (ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP \ + << ERTS_SEQ_TRACE_FLAGS_TS_TYPE_SHIFT) +#define SEQ_TRACE_MON_TS (ERTS_TRACE_FLG_MONOTONIC_TIMESTAMP \ + << ERTS_SEQ_TRACE_FLAGS_TS_TYPE_SHIFT) #ifdef USE_VM_PROBES #define DT_UTAG_PERMANENT (1 << 0) diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index e1b03a057f..2243639099 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -38,6 +38,7 @@ #include "erl_binary.h" #include "erl_bits.h" #include "erl_thr_progress.h" +#include "erl_bif_unique.h" #if 0 #define DEBUG_PRINTOUTS @@ -77,6 +78,263 @@ enum ErtsSysMsgType { SYS_MSG_TYPE_SYSPROF }; +#define ERTS_TRACE_TS_NOW_MAX_SIZE \ + 4 +#define ERTS_TRACE_TS_MONOTONIC_MAX_SIZE \ + ERTS_MAX_SINT64_HEAP_SIZE +#define ERTS_TRACE_TS_STRICT_MONOTONIC_MAX_SIZE \ + (3 + ERTS_MAX_SINT64_HEAP_SIZE \ + + ERTS_MAX_UINT64_HEAP_SIZE) + +#define ERTS_TRACE_PATCH_TS_MAX_SIZE \ + (1 + ((ERTS_TRACE_TS_NOW_MAX_SIZE \ + > ERTS_TRACE_TS_MONOTONIC_MAX_SIZE) \ + ? ((ERTS_TRACE_TS_NOW_MAX_SIZE \ + > ERTS_TRACE_TS_STRICT_MONOTONIC_MAX_SIZE) \ + ? ERTS_TRACE_TS_NOW_MAX_SIZE \ + : ERTS_TRACE_TS_STRICT_MONOTONIC_MAX_SIZE) \ + : ((ERTS_TRACE_TS_MONOTONIC_MAX_SIZE \ + > ERTS_TRACE_TS_STRICT_MONOTONIC_MAX_SIZE) \ + ? ERTS_TRACE_TS_MONOTONIC_MAX_SIZE \ + : ERTS_TRACE_TS_STRICT_MONOTONIC_MAX_SIZE))) + +#define TFLGS_TS_TYPE(p) ERTS_TFLGS2TSTYPE(ERTS_TRACE_FLAGS((p))) + +/* + * FUTURE CHANGES: + * + * The timestamp functionality has intentionally been + * split in two parts for future use even though it + * is not used like this today. take_timestamp() takes + * the timestamp and calculate heap need for it (which + * is not constant). write_timestamp() writes the + * timestamp to the allocated heap. That is, one typically + * want to take the timestamp before allocating the heap + * and then write it to the heap. + * + * The trace output functionality now use patch_ts_size(), + * write_ts(), and patch_ts(). write_ts() both takes the + * timestamp and writes it. Since we don't know the + * heap need when allocating the heap area we need to + * over allocate (maximum size from patch_ts_size()) and + * then potentially (often) shrink the heap area after the + * timestamp has been written. The only reason it is + * currently done this way is because we do not want to + * make major changes of the trace behavior in a patch. + * This is planned to be changed in next major release. + */ + +typedef struct { + int ts_type_flag; + union { + struct { + Uint ms; + Uint s; + Uint us; + } now; + struct { + ErtsMonotonicTime time; + Sint64 raw_unique; + } monotonic; + } u; +} ErtsTraceTimeStamp; + +static ERTS_INLINE Uint +take_timestamp(ErtsTraceTimeStamp *tsp, int ts_type) +{ + int ts_type_flag = ts_type & -ts_type; /* least significant flag */ + + ASSERT(ts_type_flag == ERTS_TRACE_FLG_NOW_TIMESTAMP + || ts_type_flag == ERTS_TRACE_FLG_MONOTONIC_TIMESTAMP + || ts_type_flag == ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP + || ts_type_flag == 0); + + tsp->ts_type_flag = ts_type_flag; + switch (ts_type_flag) { + case 0: + return (Uint) 0; + case ERTS_TRACE_FLG_NOW_TIMESTAMP: +#ifdef HAVE_ERTS_NOW_CPU + if (erts_cpu_timestamp) + erts_get_now_cpu(&tsp->u.now.ms, &tsp->u.now.s, &tsp->u.now.us); + else +#endif + get_now(&tsp->u.now.ms, &tsp->u.now.s, &tsp->u.now.us); + return (Uint) 4; + case ERTS_TRACE_FLG_MONOTONIC_TIMESTAMP: + case ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP: { + Uint hsz = 0; + ErtsMonotonicTime mtime = erts_get_monotonic_time(NULL); + mtime += ERTS_MONOTONIC_OFFSET_NATIVE; + hsz = (IS_SSMALL(mtime) ? + (Uint) 0 + : ERTS_SINT64_HEAP_SIZE((Sint64) mtime)); + tsp->u.monotonic.time = mtime; + if (ts_type_flag == ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP) { + Sint64 raw_unique; + hsz += 3; /* 2-tuple */ + raw_unique = erts_raw_get_unique_monotonic_integer(); + tsp->u.monotonic.raw_unique = raw_unique; + hsz += erts_raw_unique_monotonic_integer_heap_size(raw_unique); + } + return hsz; + } + default: + ERTS_INTERNAL_ERROR("invalid timestamp type"); + return 0; + } +} + +static ERTS_INLINE Eterm +write_timestamp(ErtsTraceTimeStamp *tsp, Eterm **hpp) +{ + int ts_type_flag = tsp->ts_type_flag; + Eterm res; + + switch (ts_type_flag) { + case 0: + return NIL; + case ERTS_TRACE_FLG_NOW_TIMESTAMP: + res = TUPLE3(*hpp, + make_small(tsp->u.now.ms), + make_small(tsp->u.now.s), + make_small(tsp->u.now.us)); + *hpp += 4; + return res; + case ERTS_TRACE_FLG_MONOTONIC_TIMESTAMP: + case ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP: { + Sint64 mtime, raw; + Eterm unique, emtime; + + mtime = (Sint64) tsp->u.monotonic.time; + emtime = (IS_SSMALL(mtime) + ? make_small((Sint64) mtime) + : erts_sint64_to_big((Sint64) mtime, hpp)); + + if (ts_type_flag == ERTS_TRACE_FLG_MONOTONIC_TIMESTAMP) + return emtime; + + raw = tsp->u.monotonic.raw_unique; + unique = erts_raw_make_unique_monotonic_integer_value(hpp, + raw); + res = TUPLE2(*hpp, emtime, unique); + *hpp += 3; + return res; + } + default: + ERTS_INTERNAL_ERROR("invalid timestamp type"); + return THE_NON_VALUE; + } +} + +#define PATCH_TS_SIZE(p) patch_ts_size(TFLGS_TS_TYPE(p)) + +static ERTS_INLINE Uint +patch_ts_size(int ts_type) +{ + int ts_type_flag = ts_type & -ts_type; /* least significant flag */ + switch (ts_type_flag) { + case 0: + return 0; + case ERTS_TRACE_FLG_NOW_TIMESTAMP: + return 1 + ERTS_TRACE_TS_NOW_MAX_SIZE; + case ERTS_TRACE_FLG_MONOTONIC_TIMESTAMP: + return 1 + ERTS_TRACE_TS_MONOTONIC_MAX_SIZE; + case ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP: + return 1 + ERTS_TRACE_TS_STRICT_MONOTONIC_MAX_SIZE; + default: + ERTS_INTERNAL_ERROR("invalid timestamp type"); + return 0; + } +} + +/* + * Write a timestamp. The timestamp MUST be the last + * thing built on the heap. This since write_ts() might + * adjust the size of the used area. + */ +static Eterm +write_ts(int ts_type, Eterm *hp, ErlHeapFragment *bp, Process *tracer) +{ + ErtsTraceTimeStamp ts; + Sint shrink; + Eterm res, *ts_hp = hp; + Uint hsz; + + ASSERT(ts_type); + + hsz = take_timestamp(&ts, ts_type); + + res = write_timestamp(&ts, &ts_hp); + + ASSERT(ts_hp == hp + hsz); + + switch (ts.ts_type_flag) { + case ERTS_TRACE_FLG_MONOTONIC_TIMESTAMP: + shrink = ERTS_TRACE_TS_MONOTONIC_MAX_SIZE; + break; + case ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP: + shrink = ERTS_TRACE_TS_STRICT_MONOTONIC_MAX_SIZE; + break; + default: + return res; + } + + shrink -= hsz; + + ASSERT(shrink >= 0); + + if (shrink) { + if (bp) + bp->used_size -= shrink; +#ifndef ERTS_SMP + else if (tracer) { + Eterm *endp = ts_hp + shrink; + HRelease(tracer, endp, ts_hp); + } +#endif + } + + return res; +} + +/* + * Patch a timestamp into a tuple. The tuple MUST be the last thing + * built on the heap before the call, and the timestamp MUST be + * the last thing after the call. This since patch_ts() might adjust + * the size of the used area. + */ + +#define PATCH_TS__(Type, Tuple, Hp, Bp, Tracer) \ + do { \ + int ts_type__ = (Type); \ + if (ts_type__) \ + patch_ts(ts_type__, (Tuple), (Hp), (Bp), (Tracer)); \ + } while (0) + +#ifdef ERTS_SMP +#define PATCH_TS(Type, Tuple, Hp, Bp, Tracer) \ + PATCH_TS__((Type), (Tuple), (Hp), (Bp), NULL) +#else +#define PATCH_TS(Type, Tuple, Hp, Bp, Tracer) \ + PATCH_TS__((Type), (Tuple), (Hp), (Bp), (Tracer)) +#endif + +static ERTS_INLINE void +patch_ts(int ts_type, Eterm tuple, Eterm* hp, ErlHeapFragment *bp, Process *tracer) +{ + Eterm *tptr = tuple_val(tuple); + int arity = arityval(*tptr); + + ASSERT(ts_type); + ASSERT((tptr+arity+1) == hp); + + tptr[0] = make_arityval(arity+1); + tptr[1] = am_trace_ts; + + *hp = write_ts(ts_type, hp+1, bp, tracer); +} + #ifdef ERTS_SMP static void enqueue_sys_msg_unlocked(enum ErtsSysMsgType type, Eterm from, @@ -365,23 +623,6 @@ erts_get_system_profile(void) { return profile; } - -#ifdef HAVE_ERTS_NOW_CPU -# define GET_NOW(m, s, u) \ -do { \ - if (erts_cpu_timestamp) \ - erts_get_now_cpu(m, s, u); \ - else \ - get_now(m, s, u); \ -} while (0) -#else -# define GET_NOW(m, s, u) do {get_now(m, s, u);} while (0) -#endif - - - -static Eterm* patch_ts(Eterm tuple4, Eterm* hp); - #ifdef ERTS_SMP static void do_send_to_port(Eterm to, @@ -436,11 +677,11 @@ WRITE_SYS_MSG_TO_PORT(Eterm unused_to, /* Send {trace_ts, Pid, out, 0, Timestamp} * followed by {trace_ts, Pid, in, 0, NewTimestamp} * - * 'NewTimestamp' is fetched from GET_NOW() through patch_ts(). + * 'NewTimestamp' through patch_ts(). */ static void -do_send_schedfix_to_port(Port *trace_port, Eterm pid, Eterm timestamp) { -#define LOCAL_HEAP_SIZE (4+5+5) +do_send_schedfix_to_port(Port *trace_port, Eterm pid, Eterm timestamp, int ts_type) { +#define LOCAL_HEAP_SIZE (5+5+ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); Eterm message; Eterm *hp; @@ -462,9 +703,11 @@ do_send_schedfix_to_port(Port *trace_port, Eterm pid, Eterm timestamp) { SYS_MSG_TYPE_UNDEFINED, message); - message = TUPLE4(hp, am_trace_ts, pid, am_in, mfarity); - hp += 5; - hp = patch_ts(message, hp); + + message = TUPLE5(hp, am_trace_ts, pid, am_in, mfarity, + NIL /* Will be overwritten by timestamp */); + hp += 6; + hp[-1] = write_ts(ts_type, hp, NULL, NULL); do_send_to_port(trace_port->common.id, trace_port, @@ -481,7 +724,7 @@ do_send_schedfix_to_port(Port *trace_port, Eterm pid, Eterm timestamp) { * It is assumed that 'message' is not an 'out' message. * * 'c_p' is the currently executing process, "tracee" is the traced process - * which 'message' concerns => if (*tracee_flags & F_TIMESTAMP), + * which 'message' concerns => if (*tracee_flags & F_TIMESTAMP_MASK), * 'message' must contain a timestamp. */ static void @@ -489,8 +732,9 @@ send_to_port(Process *c_p, Eterm message, Eterm *tracer_pid, Uint *tracee_flags) { Port* trace_port; #ifndef ERTS_SMP -#define LOCAL_HEAP_SIZE (4) - Eterm ts, *hp; + int ts_type; +#define LOCAL_HEAP_SIZE ERTS_TRACE_PATCH_TS_MAX_SIZE + Eterm ts; DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); #endif @@ -519,7 +763,7 @@ send_to_port(Process *c_p, Eterm message, */ if ( c_p == NULL || - (! IS_TRACED_FL(c_p, F_TRACE_SCHED | F_TIMESTAMP))) { + (! IS_TRACED_FL(c_p, F_TRACE_SCHED | F_TIMESTAMP_MASK))) { #endif do_send_to_port(*tracer_pid, trace_port, @@ -538,22 +782,12 @@ send_to_port(Process *c_p, Eterm message, */ UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - if (*tracee_flags & F_TIMESTAMP) { - ASSERT(is_tuple(message)); - hp = tuple_val(message); - ts = hp[arityval(hp[0])]; - } else { - /* A fake schedule might be needed, - * but this message does not contain a timestamp. - * Create a dummy trace message with timestamp to be - * passed to do_send_schedfix_to_port(). - */ - Uint ms,s,us; - GET_NOW(&ms, &s, &us); - hp = local_heap; - ts = TUPLE3(hp, make_small(ms), make_small(s), make_small(us)); - hp += 4; - } + /* A fake schedule might be needed. + * Create a dummy trace message with timestamp to be + * passed to do_send_schedfix_to_port(). + */ + ts_type = TFLGS_TS_TYPE(c_p); + ts = write_ts(ts_type, local_heap, NULL, NULL); trace_port->control_flags &= ~PORT_CONTROL_FLAG_HEAVY; do_send_to_port(*tracer_pid, @@ -572,7 +806,7 @@ send_to_port(Process *c_p, Eterm message, * just after writning the real trace message, and now gets scheduled * in again. */ - do_send_schedfix_to_port(trace_port, c_p->common.id, ts); + do_send_schedfix_to_port(trace_port, c_p->common.id, ts, ts_type); } erts_port_release(trace_port); @@ -641,20 +875,19 @@ profile_send(Eterm from, Eterm message) { /* A fake schedule out/in message pair will be sent, * if the driver so requests. - * If (timestamp == NIL), one is fetched from GET_NOW(). * * 'c_p' is the currently executing process, may be NULL. */ static void seq_trace_send_to_port(Process *c_p, Eterm seq_tracer, - Eterm message, - Eterm timestamp) + Eterm message) { Port* trace_port; #ifndef ERTS_SMP - Eterm ts, *hp; -#define LOCAL_HEAP_SIZE (4) + int ts_type; + Eterm ts; +#define LOCAL_HEAP_SIZE ERTS_TRACE_PATCH_TS_MAX_SIZE DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); #endif @@ -679,7 +912,7 @@ seq_trace_send_to_port(Process *c_p, } if (c_p == NULL - || (! IS_TRACED_FL(c_p, F_TRACE_SCHED | F_TIMESTAMP))) { + || (! IS_TRACED_FL(c_p, F_TRACE_SCHED | F_TIMESTAMP_MASK))) { #endif do_send_to_port(seq_tracer, trace_port, @@ -696,20 +929,12 @@ seq_trace_send_to_port(Process *c_p, * with 'running' and 'timestamp'. */ - if (timestamp != NIL) { - ts = timestamp; - } else { - /* A fake schedule might be needed, - * but this message does not contain a timestamp. - * Create a dummy trace message with timestamp to be - * passed to do_send_schedfix_to_port(). - */ - Uint ms,s,us; - GET_NOW(&ms, &s, &us); - hp = local_heap; - ts = TUPLE3(hp, make_small(ms), make_small(s), make_small(us)); - hp += 4; - } + /* A fake schedule might be needed. + * Create a dummy trace message with timestamp to be + * passed to do_send_schedfix_to_port(). + */ + ts_type = TFLGS_TS_TYPE(c_p); + ts = write_ts(ts_type, local_heap, NULL, NULL); trace_port->control_flags &= ~PORT_CONTROL_FLAG_HEAVY; do_send_to_port(seq_tracer, @@ -728,7 +953,7 @@ seq_trace_send_to_port(Process *c_p, * just after writing the real trace message, and now gets scheduled * in again. */ - do_send_schedfix_to_port(trace_port, c_p->common.id, ts); + do_send_schedfix_to_port(trace_port, c_p->common.id, ts, ts_type); } erts_port_release(trace_port); @@ -738,32 +963,6 @@ seq_trace_send_to_port(Process *c_p, #endif } -#define TS_HEAP_WORDS 5 -#define TS_SIZE(p) ((ERTS_TRACE_FLAGS((p)) & F_TIMESTAMP) \ - ? TS_HEAP_WORDS \ - : 0) - -/* - * Patch a timestamp into a tuple. The tuple must be the last thing - * built on the heap. - * - * Returns the new hp pointer. -*/ -static Eterm* -patch_ts(Eterm tuple, Eterm* hp) -{ - Uint ms, s, us; - Eterm* ptr = tuple_val(tuple); - int arity = arityval(*ptr); - - ASSERT((ptr+arity+1) == hp); - ptr[0] = make_arityval(arity+1); - ptr[1] = am_trace_ts; - GET_NOW(&ms, &s, &us); - *hp = TUPLE3(hp+1, make_small(ms), make_small(s), make_small(us)); - return hp+5; -} - static ERTS_INLINE void send_to_tracer(Process *tracee, ERTS_TRACER_REF_TYPE tracer_ref, @@ -776,13 +975,13 @@ send_to_tracer(Process *tracee, erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(tracee) & F_TIMESTAMP) - *hpp = patch_ts(msg, *hpp); - - if (is_internal_pid(ERTS_TRACER_PROC(tracee))) + if (is_internal_pid(ERTS_TRACER_PROC(tracee))) { + PATCH_TS(TFLGS_TS_TYPE(tracee), msg, *hpp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(tracee->common.id, tracer_ref, msg, bp); + } else { ASSERT(is_internal_port(ERTS_TRACER_PROC(tracee))); + PATCH_TS(TFLGS_TS_TYPE(tracee), msg, *hpp, NULL, NULL); send_to_port(no_fake_sched ? NULL : tracee, msg, &ERTS_TRACER_PROC(tracee), @@ -796,7 +995,7 @@ send_to_tracer(Process *tracee, static void trace_sched_aux(Process *p, Eterm what, int never_fake_sched) { -#define LOCAL_HEAP_SIZE (5+4+1+TS_HEAP_WORDS) +#define LOCAL_HEAP_SIZE (5+4+1+ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeap(local_heap,LOCAL_HEAP_SIZE,p); Eterm tmp, mess, *hp; ErlHeapFragment *bp = NULL; @@ -852,7 +1051,7 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched) size += 4; if (sched_no) size += 1; - size += TS_SIZE(p); + size += PATCH_TS_SIZE(p); hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); } @@ -926,7 +1125,7 @@ trace_send(Process *p, Eterm to, Eterm msg) } if (is_internal_port(ERTS_TRACER_PROC(p))) { -#define LOCAL_HEAP_SIZE (11) +#define LOCAL_HEAP_SIZE (6 + ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); @@ -934,9 +1133,7 @@ trace_send(Process *p, Eterm to, Eterm msg) mess = TUPLE5(hp, am_trace, p->common.id, operation, msg, to); hp += 6; erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL); send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); #undef LOCAL_HEAP_SIZE @@ -955,7 +1152,7 @@ trace_send(Process *p, Eterm to, Eterm msg) sz_msg = size_object(msg); sz_to = size_object(to); - need = sz_msg + sz_to + 6 + TS_SIZE(p); + need = sz_msg + sz_to + 6 + PATCH_TS_SIZE(p); hp = ERTS_ALLOC_SYSMSG_HEAP(need, &bp, &off_heap, tracer_ref); @@ -972,10 +1169,7 @@ trace_send(Process *p, Eterm to, Eterm msg) erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) { - patch_ts(mess, hp); - } - + PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp); erts_smp_mtx_unlock(&smq_mtx); } @@ -992,7 +1186,7 @@ trace_receive(Process *rp, Eterm msg) Eterm* hp; if (is_internal_port(ERTS_TRACER_PROC(rp))) { -#define LOCAL_HEAP_SIZE (10) +#define LOCAL_HEAP_SIZE (5+ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); @@ -1000,9 +1194,7 @@ trace_receive(Process *rp, Eterm msg) mess = TUPLE4(hp, am_trace, rp->common.id, am_receive, msg); hp += 5; erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(rp) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(TFLGS_TS_TYPE(rp), mess, hp, NULL, NULL); send_to_port(rp, mess, &ERTS_TRACER_PROC(rp), &ERTS_TRACE_FLAGS(rp)); UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); #undef LOCAL_HEAP_SIZE @@ -1021,7 +1213,7 @@ trace_receive(Process *rp, Eterm msg) sz_msg = size_object(msg); - hsz = sz_msg + 5 + TS_SIZE(rp); + hsz = sz_msg + 5 + PATCH_TS_SIZE(rp); hp = ERTS_ALLOC_SYSMSG_HEAP(hsz, &bp, &off_heap, tracer_ref); @@ -1031,10 +1223,7 @@ trace_receive(Process *rp, Eterm msg) erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(rp) & F_TIMESTAMP) { - patch_ts(mess, hp); - } - + PATCH_TS(TFLGS_TS_TYPE(rp), mess, hp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(rp->common.id, tracer_ref, mess, bp); erts_smp_mtx_unlock(&smq_mtx); } @@ -1084,6 +1273,7 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, Eterm type_atom; int sz_exit; Eterm seq_tracer; + int ts_type; seq_tracer = erts_get_system_seq_tracer(); @@ -1111,8 +1301,10 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, return; /* no need to send anything */ } + ts_type = ERTS_SEQTFLGS2TSTYPE(unsigned_val(SEQ_TRACE_T_FLAGS(token))); + if (is_internal_port(seq_tracer)) { -#define LOCAL_HEAP_SIZE (64) +#define LOCAL_HEAP_SIZE (60 + ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); @@ -1128,17 +1320,17 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, mess = TUPLE5(hp, type_atom, lastcnt_serial, SEQ_TRACE_T_SENDER(token), receiver, msg); hp += 6; + erts_smp_mtx_lock(&smq_mtx); - if ((unsigned_val(SEQ_TRACE_T_FLAGS(token)) & SEQ_TRACE_TIMESTAMP) == 0) { + if (!ts_type) { mess = TUPLE3(hp, am_seq_trace, label, mess); - seq_trace_send_to_port(NULL, seq_tracer, mess, NIL); + seq_trace_send_to_port(NULL, seq_tracer, mess); } else { - Uint ms,s,us,ts; - GET_NOW(&ms, &s, &us); - ts = TUPLE3(hp, make_small(ms),make_small(s), make_small(us)); - hp += 4; - mess = TUPLE4(hp, am_seq_trace, label, mess, ts); - seq_trace_send_to_port(process, seq_tracer, mess, ts); + mess = TUPLE4(hp, am_seq_trace, label, mess, + NIL /* Will be overwritten by timestamp */); + hp += 5; + hp[-1] = write_ts(ts_type, hp, NULL, NULL); + seq_trace_send_to_port(process, seq_tracer, mess); } UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); #undef LOCAL_HEAP_SIZE @@ -1173,8 +1365,7 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, sz_lastcnt_serial = 3; /* TUPLE2 */ sz_msg = size_object(msg); - sz_ts = ((unsigned_val(SEQ_TRACE_T_FLAGS(token)) & SEQ_TRACE_TIMESTAMP) ? - 5 : 0); + sz_ts = patch_ts_size(ts_type); if (exitfrom != NIL) { sz_exit = 4; /* create {'EXIT',exitfrom,msg} */ sz_exitfrom = size_object(exitfrom); @@ -1218,14 +1409,20 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, erts_smp_mtx_lock(&smq_mtx); - if (sz_ts) {/* timestamp should be included */ - Uint ms,s,us,ts; - GET_NOW(&ms, &s, &us); - ts = TUPLE3(hp, make_small(ms),make_small(s), make_small(us)); - hp += 4; - mess = TUPLE4(hp, am_seq_trace, label, mess, ts); - } else { + if (!ts_type) mess = TUPLE3(hp, am_seq_trace, label, mess); + else { + mess = TUPLE4(hp, am_seq_trace, label, mess, + NIL /* Will be overwritten by timestamp */); + hp += 5; + /* Write timestamp in element 6 of the 'msg' tuple */ + hp[-1] = write_ts(ts_type, hp, bp, +#ifndef ERTS_SMP + tracer +#else + NULL +#endif + ); } #ifdef ERTS_SMP @@ -1244,7 +1441,7 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, void erts_trace_return_to(Process *p, BeamInstr *pc) { -#define LOCAL_HEAP_SIZE (4+5+5) +#define LOCAL_HEAP_SIZE (4+5+ERTS_TRACE_PATCH_TS_MAX_SIZE) Eterm* hp; Eterm mfa; Eterm mess; @@ -1269,9 +1466,7 @@ erts_trace_return_to(Process *p, BeamInstr *pc) erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL); if (is_internal_port(ERTS_TRACER_PROC(p))) { send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); @@ -1318,6 +1513,7 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid) Eterm mod, name; int arity; Uint meta_flags, *tracee_flags; + int ts_type; #ifdef ERTS_SMP Eterm tracee; #endif @@ -1353,7 +1549,7 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid) * meta trace => * use fixed flag set instead of process flags */ - meta_flags = F_TRACE_CALLS | F_TIMESTAMP; + meta_flags = F_TRACE_CALLS | F_NOW_TS; tracee_flags = &meta_flags; #ifdef ERTS_SMP tracee = NIL; @@ -1367,8 +1563,10 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid) name = fi[1]; arity = fi[2]; + ts_type = ERTS_TFLGS2TSTYPE(*tracee_flags); + if (is_internal_port(*tracer_pid)) { -#define LOCAL_HEAP_SIZE (4+6+5) +#define LOCAL_HEAP_SIZE (4+6+ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); hp = local_heap; @@ -1377,9 +1575,7 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid) mess = TUPLE5(hp, am_trace, p->common.id, am_return_from, mfa, retval); hp += 6; erts_smp_mtx_lock(&smq_mtx); - if (*tracee_flags & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(ts_type, mess, hp, NULL, NULL); send_to_port(p, mess, tracer_pid, tracee_flags); UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); #undef LOCAL_HEAP_SIZE @@ -1390,24 +1586,15 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid) ERTS_TRACER_REF_TYPE tracer_ref; unsigned size; unsigned retval_size; -#ifdef DEBUG - Eterm* limit; -#endif ASSERT(is_internal_pid(*tracer_pid)); ERTS_GET_TRACER_REF(tracer_ref, *tracer_pid, *tracee_flags); - + retval_size = size_object(retval); - size = 6 + 4 + retval_size; - if (*tracee_flags & F_TIMESTAMP) { - size += 1+4; - } + size = 6 + 4 + retval_size + patch_ts_size(ts_type); hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); -#ifdef DEBUG - limit = hp + size; -#endif /* * Build the trace tuple and put it into receive queue of the tracer process. @@ -1421,11 +1608,7 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid) erts_smp_mtx_lock(&smq_mtx); - if (*tracee_flags & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } - - ASSERT(hp == limit); + PATCH_TS(ts_type, mess, hp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(tracee, tracer_ref, mess, bp); erts_smp_mtx_unlock(&smq_mtx); @@ -1448,6 +1631,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value, Eterm cv; Eterm mess; Uint meta_flags, *tracee_flags; + int ts_type; #ifdef ERTS_SMP Eterm tracee; #endif @@ -1486,15 +1670,17 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value, * meta trace => * use fixed flag set instead of process flags */ - meta_flags = F_TRACE_CALLS | F_TIMESTAMP; + meta_flags = F_TRACE_CALLS | F_NOW_TS; tracee_flags = &meta_flags; #ifdef ERTS_SMP tracee = NIL; #endif } + ts_type = ERTS_TFLGS2TSTYPE(*tracee_flags); + if (is_internal_port(*tracer_pid)) { -#define LOCAL_HEAP_SIZE (4+3+6+5) +#define LOCAL_HEAP_SIZE (4+3+6+ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); @@ -1507,10 +1693,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value, hp += 6; ASSERT((hp - local_heap) <= LOCAL_HEAP_SIZE); erts_smp_mtx_lock(&smq_mtx); - if (*tracee_flags & F_TIMESTAMP) { - hp = patch_ts(mess, hp); /* hp += 5 */ - ASSERT((hp - local_heap) == LOCAL_HEAP_SIZE); - } + PATCH_TS(ts_type, mess, hp, NULL, NULL); send_to_port(p, mess, tracer_pid, tracee_flags); UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); #undef LOCAL_HEAP_SIZE @@ -1521,24 +1704,15 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value, ERTS_TRACER_REF_TYPE tracer_ref; unsigned size; unsigned value_size; -#ifdef DEBUG - Eterm* limit; -#endif ASSERT(is_internal_pid(*tracer_pid)); ERTS_GET_TRACER_REF(tracer_ref, *tracer_pid, *tracee_flags); value_size = size_object(value); - size = 6 + 4 + 3 + value_size; - if (*tracee_flags & F_TIMESTAMP) { - size += 1+4; - } + size = 6 + 4 + 3 + value_size + patch_ts_size(ts_type); hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); -#ifdef DEBUG - limit = hp + size; -#endif /* * Build the trace tuple and put it into receive queue of the tracer process. @@ -1555,11 +1729,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value, erts_smp_mtx_lock(&smq_mtx); - if (*tracee_flags & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } - - ASSERT(hp == limit); + PATCH_TS(ts_type, mess, hp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(tracee, tracer_ref, mess, bp); erts_smp_mtx_unlock(&smq_mtx); @@ -1592,6 +1762,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, Eterm pam_result = am_true; Eterm mess; Uint meta_flags, *tracee_flags; + int ts_type; #ifdef ERTS_SMP Eterm tracee; #endif @@ -1633,7 +1804,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, /* No trace messages for sensitive processes. */ return 0; } - meta_flags = F_TRACE_CALLS | F_TIMESTAMP; + meta_flags = F_TRACE_CALLS | F_NOW_TS; tracee_flags = &meta_flags; #ifdef ERTS_SMP tracee = NIL; @@ -1676,12 +1847,14 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, } args = transformed_args; + ts_type = ERTS_TFLGS2TSTYPE(*tracee_flags); + if (is_internal_port(*tracer_pid)) { #if HEAP_ON_C_STACK - Eterm local_heap[64+MAX_ARG]; + Eterm local_heap[60+ERTS_TRACE_PATCH_TS_MAX_SIZE+MAX_ARG]; #else Eterm *local_heap = erts_alloc(ERTS_ALC_T_TEMP_TERM, - sizeof(Eterm)*(64+MAX_ARG)); + sizeof(Eterm)*(60+ERTS_TRACE_PATCH_TS_MAX_SIZE+MAX_ARG)); #endif hp = local_heap; @@ -1796,9 +1969,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, *hp++ = pam_result; } erts_smp_mtx_lock(&smq_mtx); - if (*tracee_flags & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(ts_type, mess, hp, NULL, NULL); send_to_port(p, mess, tracer_pid, tracee_flags); erts_smp_mtx_unlock(&smq_mtx); erts_match_set_release_result(p); @@ -1820,9 +1991,6 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, unsigned sizes[MAX_ARG]; unsigned pam_result_size = 0; int invalid_tracer; -#ifdef DEBUG - Eterm* limit; -#endif ASSERT(is_internal_pid(*tracer_pid)); @@ -1915,10 +2083,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, size += sizes[i]; } } - if (*tracee_flags & F_TIMESTAMP) { - size += 1 + 4; - /* One element in trace tuple + timestamp tuple. */ - } + size += patch_ts_size(ts_type); if (pam_result != am_true) { pam_result_size = size_object(pam_result); size += 1 + pam_result_size; @@ -1926,9 +2091,6 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, } hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); -#ifdef DEBUG - limit = hp + size; -#endif /* * Build the the {M,F,A} tuple in the message buffer. @@ -1971,11 +2133,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, erts_smp_mtx_lock(&smq_mtx); - if (*tracee_flags & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } - - ASSERT(hp == limit); + PATCH_TS(ts_type, mess, hp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(tracee, tracer_ref, mess, bp); erts_smp_mtx_unlock(&smq_mtx); UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); @@ -2010,9 +2168,7 @@ trace_proc(Process *c_p, Process *t_p, Eterm what, Eterm data) mess = TUPLE4(hp, am_trace, t_p->common.id, what, data); hp += 5; erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(t_p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, NULL, NULL); send_to_port( #ifndef ERTS_SMP /* No fake schedule out and in again after an exit */ @@ -2042,7 +2198,7 @@ trace_proc(Process *c_p, Process *t_p, Eterm what, Eterm data) sz_data = size_object(data); - need = sz_data + 5 + TS_SIZE(t_p); + need = sz_data + 5 + PATCH_TS_SIZE(t_p); hp = ERTS_ALLOC_SYSMSG_HEAP(need, &bp, &off_heap, tracer_ref); @@ -2052,9 +2208,7 @@ trace_proc(Process *c_p, Process *t_p, Eterm what, Eterm data) erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(t_p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(t_p->common.id, tracer_ref, mess, bp); erts_smp_mtx_unlock(&smq_mtx); @@ -2088,9 +2242,7 @@ trace_proc_spawn(Process *p, Eterm pid, mess = TUPLE5(hp, am_trace, p->common.id, am_spawn, pid, mfa); hp += 6; erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL); send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); #undef LOCAL_HEAP_SIZE @@ -2111,7 +2263,7 @@ trace_proc_spawn(Process *p, Eterm pid, sz_args = size_object(args); sz_pid = size_object(pid); - need = sz_args + 4 + 6 + TS_SIZE(p); + need = sz_args + 4 + 6 + PATCH_TS_SIZE(p); hp = ERTS_ALLOC_SYSMSG_HEAP(need, &bp, &off_heap, tracer_ref); @@ -2124,9 +2276,7 @@ trace_proc_spawn(Process *p, Eterm pid, erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp); erts_smp_mtx_unlock(&smq_mtx); @@ -2208,11 +2358,8 @@ trace_gc(Process *p, Eterm what) #define LOCAL_HEAP_SIZE \ (sizeof(values)/sizeof(*values)) * \ (2/*cons*/ + 3/*2-tuple*/ + BIG_UINT_HEAP_SIZE) + \ - 5/*4-tuple */ + TS_HEAP_WORDS + 5/*4-tuple */ + ERTS_TRACE_PATCH_TS_MAX_SIZE DeclareTmpHeap(local_heap,LOCAL_HEAP_SIZE,p); -#ifdef DEBUG - Eterm* limit; -#endif ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(Eterm)); @@ -2227,7 +2374,7 @@ trace_gc(Process *p, Eterm what) sizeof(values)/sizeof(*values), tags, values); - size += 5/*4-tuple*/ + TS_SIZE(p); + size += 5/*4-tuple*/ + PATCH_TS_SIZE(p); #endif } else { ASSERT(is_internal_pid(ERTS_TRACER_PROC(p))); @@ -2242,15 +2389,12 @@ trace_gc(Process *p, Eterm what) sizeof(values)/sizeof(*values), tags, values); - size += 5/*4-tuple*/ + TS_SIZE(p); + size += 5/*4-tuple*/ + PATCH_TS_SIZE(p); hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); } -#ifdef DEBUG - limit = hp + size; ASSERT(size <= LOCAL_HEAP_SIZE); -#endif msg = erts_bld_atom_uword_2tup_list(&hp, NULL, @@ -2263,14 +2407,14 @@ trace_gc(Process *p, Eterm what) erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) { - hp = patch_ts(msg, hp); - } - ASSERT(hp == limit); - if (is_internal_port(ERTS_TRACER_PROC(p))) + if (is_internal_port(ERTS_TRACER_PROC(p))) { + PATCH_TS(TFLGS_TS_TYPE(p), msg, hp, NULL, NULL); send_to_port(p, msg, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); - else + } + else { + PATCH_TS(TFLGS_TS_TYPE(p), msg, hp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, msg, bp); + } erts_smp_mtx_unlock(&smq_mtx); UnUseTmpHeap(LOCAL_HEAP_SIZE,p); #undef LOCAL_HEAP_SIZE @@ -2574,19 +2718,18 @@ monitor_generic(Process *p, Eterm type, Eterm spec) { void profile_scheduler(Eterm scheduler_id, Eterm state) { - Eterm *hp, msg, timestamp; - Uint Ms, s, us; + Eterm *hp, msg; + ErlHeapFragment *bp = NULL; #ifndef ERTS_SMP -#define LOCAL_HEAP_SIZE (4 + 7) +#define LOCAL_HEAP_SIZE (7 + ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); hp = local_heap; #else - ErlHeapFragment *bp; Uint hsz; - hsz = 4 + 7; + hsz = 7 + patch_ts_size(erts_system_profile_ts_type)-1; bp = new_message_buffer(hsz); hp = bp->mem; @@ -2606,10 +2749,13 @@ profile_scheduler(Eterm scheduler_id, Eterm state) { break; } - GET_NOW(&Ms, &s, &us); - timestamp = TUPLE3(hp, make_small(Ms), make_small(s), make_small(us)); hp += 4; - msg = TUPLE6(hp, am_profile, am_scheduler, scheduler_id, state, - make_small(active_sched), timestamp); hp += 7; + msg = TUPLE6(hp, am_profile, am_scheduler, scheduler_id, + state, make_small(active_sched), + NIL /* Will be overwritten by timestamp */); + hp += 7; + + /* Write timestamp in element 6 of the 'msg' tuple */ + hp[-1] = write_ts(erts_system_profile_ts_type, hp, bp, NULL); #ifndef ERTS_SMP profile_send(NIL, msg); @@ -2680,7 +2826,7 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) { Eterm* hp; if (is_internal_port(ERTS_TRACER_PROC(p))) { -#define LOCAL_HEAP_SIZE (5+6) +#define LOCAL_HEAP_SIZE (6+ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); @@ -2689,9 +2835,7 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) { mess = TUPLE5(hp, am_trace, calling_pid, am_open, p->common.id, drv_name); hp += 6; erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL); /* No fake schedule */ send_to_port(NULL, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); @@ -2705,7 +2849,7 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) { ASSERT(is_internal_pid(ERTS_TRACER_PROC(p))); - sz_data = 6 + TS_SIZE(p); + sz_data = 6 + PATCH_TS_SIZE(p); ERTS_GET_TRACER_REF(tracer_ref, ERTS_TRACER_PROC(p), @@ -2718,9 +2862,7 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) { erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp); erts_smp_mtx_unlock(&smq_mtx); @@ -2744,7 +2886,7 @@ trace_port(Port *t_p, Eterm what, Eterm data) { || erts_thr_progress_is_blocking()); if (is_internal_port(ERTS_TRACER_PROC(t_p))) { -#define LOCAL_HEAP_SIZE (5+5) +#define LOCAL_HEAP_SIZE (5+ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); @@ -2752,9 +2894,7 @@ trace_port(Port *t_p, Eterm what, Eterm data) { mess = TUPLE4(hp, am_trace, t_p->common.id, what, data); hp += 5; erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(t_p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, NULL, NULL); /* No fake schedule */ send_to_port(NULL,mess,&ERTS_TRACER_PROC(t_p),&ERTS_TRACE_FLAGS(t_p)); UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); @@ -2768,7 +2908,7 @@ trace_port(Port *t_p, Eterm what, Eterm data) { ASSERT(is_internal_pid(ERTS_TRACER_PROC(t_p))); - sz_data = 5 + TS_SIZE(t_p); + sz_data = 5 + PATCH_TS_SIZE(t_p); ERTS_GET_TRACER_REF(tracer_ref, ERTS_TRACER_PROC(t_p), @@ -2781,9 +2921,7 @@ trace_port(Port *t_p, Eterm what, Eterm data) { erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(t_p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(t_p->common.id, tracer_ref, mess, bp); erts_smp_mtx_unlock(&smq_mtx); @@ -2811,7 +2949,7 @@ trace_sched_ports_where(Port *p, Eterm what, Eterm where) { Eterm sched_id = am_undefined; if (is_internal_port(ERTS_TRACER_PROC(p))) { -#define LOCAL_HEAP_SIZE (5+6) +#define LOCAL_HEAP_SIZE (6+ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); @@ -2834,9 +2972,8 @@ trace_sched_ports_where(Port *p, Eterm what, Eterm where) { hp += ws; erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } + + PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL); /* No fake scheduling */ send_to_port(NULL, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); @@ -2856,7 +2993,7 @@ trace_sched_ports_where(Port *p, Eterm what, Eterm where) { ERTS_TRACER_PROC(p), ERTS_TRACE_FLAGS(p)); - hp = ERTS_ALLOC_SYSMSG_HEAP(ws+TS_SIZE(p), &bp, &off_heap, tracer_ref); + hp = ERTS_ALLOC_SYSMSG_HEAP(ws+PATCH_TS_SIZE(p), &bp, &off_heap, tracer_ref); if (IS_TRACED_FL(p, F_TRACE_SCHED_NO)) { #ifdef ERTS_SMP @@ -2874,10 +3011,7 @@ trace_sched_ports_where(Port *p, Eterm what, Eterm where) { erts_smp_mtx_lock(&smq_mtx); - if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) { - hp = patch_ts(mess, hp); - } - + PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref); ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp); erts_smp_mtx_unlock(&smq_mtx); } @@ -2887,13 +3021,12 @@ trace_sched_ports_where(Port *p, Eterm what, Eterm where) { void profile_runnable_port(Port *p, Eterm status) { - Uint Ms, s, us; - Eterm *hp, msg, timestamp; - + Eterm *hp, msg; + ErlHeapFragment *bp = NULL; Eterm count = make_small(0); #ifndef ERTS_SMP -#define LOCAL_HEAP_SIZE (4 + 6) +#define LOCAL_HEAP_SIZE (6 + ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); @@ -2901,10 +3034,9 @@ profile_runnable_port(Port *p, Eterm status) { hp = local_heap; #else - ErlHeapFragment *bp; Uint hsz; - hsz = 4 + 6; + hsz = 6 + patch_ts_size(erts_system_profile_ts_type)-1; bp = new_message_buffer(hsz); hp = bp->mem; @@ -2912,9 +3044,12 @@ profile_runnable_port(Port *p, Eterm status) { erts_smp_mtx_lock(&smq_mtx); - GET_NOW(&Ms, &s, &us); - timestamp = TUPLE3(hp, make_small(Ms), make_small(s), make_small(us)); hp += 4; - msg = TUPLE5(hp, am_profile, p->common.id, status, count, timestamp); hp += 6; + msg = TUPLE5(hp, am_profile, p->common.id, status, count, + NIL /* Will be overwritten by timestamp */); + hp += 6; + + /* Write timestamp in element 5 of the 'msg' tuple */ + hp[-1] = write_ts(erts_system_profile_ts_type, hp, bp, NULL); #ifndef ERTS_SMP profile_send(p->common.id, msg); @@ -2929,20 +3064,19 @@ profile_runnable_port(Port *p, Eterm status) { /* Process profiling */ void profile_runnable_proc(Process *p, Eterm status){ - Uint Ms, s, us; - Eterm *hp, msg, timestamp; + Eterm *hp, msg; Eterm where = am_undefined; + ErlHeapFragment *bp = NULL; #ifndef ERTS_SMP -#define LOCAL_HEAP_SIZE (4 + 6 + 4) +#define LOCAL_HEAP_SIZE (4 + 6 + ERTS_TRACE_PATCH_TS_MAX_SIZE) DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); hp = local_heap; #else - ErlHeapFragment *bp; - Uint hsz = 4 + 6 + 4; + Uint hsz = 4 + 6 + patch_ts_size(erts_system_profile_ts_type)-1; #endif if (!p->current) { @@ -2951,7 +3085,7 @@ profile_runnable_proc(Process *p, Eterm status){ #ifdef ERTS_SMP if (!p->current) { - hsz = 4 + 6; + hsz -= 4; } bp = new_message_buffer(hsz); @@ -2966,9 +3100,13 @@ profile_runnable_proc(Process *p, Eterm status){ erts_smp_mtx_lock(&smq_mtx); - GET_NOW(&Ms, &s, &us); - timestamp = TUPLE3(hp, make_small(Ms), make_small(s), make_small(us)); hp += 4; - msg = TUPLE5(hp, am_profile, p->common.id, status, where, timestamp); hp += 6; + msg = TUPLE5(hp, am_profile, p->common.id, status, where, + NIL /* Will be overwritten by timestamp */); + hp += 6; + + /* Write timestamp in element 5 of the 'msg' tuple */ + hp[-1] = write_ts(erts_system_profile_ts_type, hp, bp, NULL); + #ifndef ERTS_SMP profile_send(p->common.id, msg); UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index 7405490f76..a0058264d7 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -18,8 +18,38 @@ * %CopyrightEnd% */ +#ifndef ERL_TRACE_H__FLAGS__ +#define ERL_TRACE_H__FLAGS__ +/* + * NOTE! The bits used for these flags matter. The flag with + * the least significant bit will take precedence! + * + * The "now timestamp" has highest precedence due to + * compatibility reasons. + */ +#define ERTS_TRACE_FLG_NOW_TIMESTAMP (1 << 0) +#define ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP (1 << 1) +#define ERTS_TRACE_FLG_MONOTONIC_TIMESTAMP (1 << 2) + +/* + * The bits used effects trace flags (of processes and ports) + * as well as sequential trace flags. If changed make sure + * these arn't messed up... + */ +#define ERTS_TRACE_TS_TYPE_BITS 3 +#define ERTS_TRACE_TS_TYPE_MASK \ + ((1 << ERTS_TRACE_TS_TYPE_BITS) - 1) + +#define ERTS_TFLGS2TSTYPE(TFLGS) \ + ((int) (((TFLGS) >> ERTS_TRACE_FLAGS_TS_TYPE_SHIFT) \ + & ERTS_TRACE_TS_TYPE_MASK)) +#define ERTS_SEQTFLGS2TSTYPE(SEQTFLGS) \ + ((int) (((SEQTFLGS) >> ERTS_SEQ_TRACE_FLAGS_TS_TYPE_SHIFT) \ + & ERTS_TRACE_TS_TYPE_MASK)) + +#endif /* ERL_TRACE_H__FLAGS__ */ -#ifndef ERL_TRACE_H__ +#if !defined(ERL_TRACE_H__) && !defined(ERTS_ONLY_INCLUDE_TRACE_FLAGS) #define ERL_TRACE_H__ struct binary; diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl index e4b6511d1f..2ecb4a28a7 100644 --- a/erts/emulator/test/system_profile_SUITE.erl +++ b/erts/emulator/test/system_profile_SUITE.erl @@ -113,13 +113,27 @@ runnable_procs(suite) -> runnable_procs(doc) -> ["Tests system_profiling with runnable_procs."]; runnable_procs(Config) when is_list(Config) -> + lists:foreach(fun (TsType) -> + Arg = case TsType of + no_timestamp -> + {timestamp, []}; + _ -> + {TsType, [TsType]} + end, + do_runnable_procs(Arg), + receive after 1000 -> ok end + end, + [no_timestamp, timestamp, monotonic_timestamp, + strict_monotonic_timestamp]). + +do_runnable_procs({TsType, TsTypeFlag}) -> Pid = start_profiler_process(), % start a ring of processes % FIXME: Set #laps and #nodes in config file Nodes = 10, Laps = 10, Master = ring(Nodes), - undefined = erlang:system_profile(Pid, [runnable_procs]), + undefined = erlang:system_profile(Pid, [runnable_procs]++TsTypeFlag), % loop a message ok = ring_message(Master, message, Laps), Events = get_profiler_events(), @@ -127,9 +141,9 @@ runnable_procs(Config) when is_list(Config) -> erlang:system_profile(undefined, []), put(master, Master), put(laps, Laps), - true = has_runnable_event(Events), + true = has_runnable_event(TsType, Events), Pids = sort_events_by_pid(Events), - ok = check_events(Pids), + ok = check_events(TsType, Pids), erase(), exit(Pid,kill), ok. @@ -139,8 +153,22 @@ runnable_ports(suite) -> runnable_ports(doc) -> ["Tests system_profiling with runnable_port."]; runnable_ports(Config) when is_list(Config) -> + lists:foreach(fun (TsType) -> + Arg = case TsType of + no_timestamp -> + {timestamp, []}; + _ -> + {TsType, [TsType]} + end, + do_runnable_ports(Arg, Config), + receive after 1000 -> ok end + end, + [no_timestamp, timestamp, monotonic_timestamp, + strict_monotonic_timestamp]). + +do_runnable_ports({TsType, TsTypeFlag}, Config) -> Pid = start_profiler_process(), - undefined = erlang:system_profile(Pid, [runnable_ports]), + undefined = erlang:system_profile(Pid, [runnable_ports]++TsTypeFlag), EchoPid = echo(Config), % FIXME: Set config to number_of_echos Laps = 10, @@ -149,9 +177,9 @@ runnable_ports(Config) when is_list(Config) -> Events = get_profiler_events(), kill_em_all = kill_echo(EchoPid), erlang:system_profile(undefined, []), - true = has_runnable_event(Events), + true = has_runnable_event(TsType, Events), Pids = sort_events_by_pid(Events), - ok = check_events(Pids), + ok = check_events(TsType, Pids), erase(), exit(Pid,kill), ok. @@ -166,8 +194,19 @@ scheduler(Config) when is_list(Config) -> {_, 1} -> {skipped, "No need for scheduler test when only one scheduler online."}; _ -> Nodes = 10, - ok = check_block_system(Nodes), - ok = check_multi_scheduling_block(Nodes) + lists:foreach(fun (TsType) -> + Arg = case TsType of + no_timestamp -> + {timestamp, []}; + _ -> + {TsType, [TsType]} + end, + ok = check_block_system(Arg, Nodes), + ok = check_multi_scheduling_block(Arg, Nodes), + receive after 1000 -> ok end + end, + [no_timestamp, timestamp, monotonic_timestamp, + strict_monotonic_timestamp]) end. % the profiler pid should not be profiled @@ -195,9 +234,9 @@ dont_profile_profiler(Config) when is_list(Config) -> %%% Check scheduler profiling -check_multi_scheduling_block(Nodes) -> +check_multi_scheduling_block({TsType, TsTypeFlag}, Nodes) -> Pid = start_profiler_process(), - undefined = erlang:system_profile(Pid, [scheduler]), + undefined = erlang:system_profile(Pid, [scheduler]++TsTypeFlag), {ok, Supervisor} = start_load(Nodes), wait(600), erlang:system_flag(multi_scheduling, block), @@ -205,23 +244,23 @@ check_multi_scheduling_block(Nodes) -> erlang:system_flag(multi_scheduling, unblock), {Pid, [scheduler]} = erlang:system_profile(undefined, []), Events = get_profiler_events(), - true = has_scheduler_event(Events), + true = has_scheduler_event(TsType, Events), stop_load(Supervisor), exit(Pid,kill), erase(), ok. -check_block_system(Nodes) -> +check_block_system({TsType, TsTypeFlag}, Nodes) -> Dummy = spawn(?MODULE, profiler_process, [[]]), Pid = start_profiler_process(), - undefined = erlang:system_profile(Pid, [scheduler]), + undefined = erlang:system_profile(Pid, [scheduler]++TsTypeFlag), {ok, Supervisor} = start_load(Nodes), wait(300), undefined = erlang:system_monitor(Dummy, [busy_port]), {Dummy, [busy_port]} = erlang:system_monitor(undefined, []), {Pid, [scheduler]} = erlang:system_profile(undefined, []), Events = get_profiler_events(), - true = has_scheduler_event(Events), + true = has_scheduler_event(TsType, Events), stop_load(Supervisor), exit(Pid,kill), exit(Dummy,kill), @@ -230,40 +269,49 @@ check_block_system(Nodes) -> %%% Check events -check_events([]) -> ok; -check_events([Pid | Pids]) -> +check_events(_TsType, []) -> ok; +check_events(TsType, [Pid | Pids]) -> Master = get(master), Laps = get(laps), CheckPids = get(pids), {Events, N} = get_pid_events(Pid), ok = check_event_flow(Events), - ok = check_event_ts(Events), + ok = check_event_ts(TsType, Events), IsMember = lists:member(Pid, CheckPids), case Pid of Master -> io:format("Expected ~p and got ~p profile events from ~p: ok~n", [Laps*2+2, N, Pid]), N = Laps*2 + 2, - check_events(Pids); + check_events(TsType, Pids); Pid when IsMember == true -> io:format("Expected ~p and got ~p profile events from ~p: ok~n", [Laps*2, N, Pid]), N = Laps*2, - check_events(Pids); + check_events(TsType, Pids); Pid -> - check_events(Pids) + check_events(TsType, Pids) end. %% timestamp consistency check for descending timestamps -check_event_ts(Events) -> - check_event_ts(Events, undefined). -check_event_ts([], _) -> ok; -check_event_ts([Event | Events], undefined) -> - check_event_ts(Events, Event); -check_event_ts([{Pid, _, _, TS1}=Event | Events], {Pid,_,_,TS0}) -> - Time = timer:now_diff(TS1, TS0), +check_event_ts(TsType, Events) -> + check_event_ts(TsType, Events, undefined). +check_event_ts(_TsType, [], _) -> ok; +check_event_ts(TsType, [Event | Events], undefined) -> + check_event_ts(TsType, Events, Event); +check_event_ts(TsType, [{Pid, _, _, TS1}=Event | Events], {Pid,_,_,TS0}) -> + Time = case TsType of + timestamp -> + timer:now_diff(TS1, TS0); + monotonic_timestamp -> + TS1 - TS0; + strict_monotonic_timestamp -> + {MT1, _} = TS1, + {MT0, _} = TS0, + MT1 - MT0 + end, if Time < 0.0 -> timestamp_error; - true -> check_event_ts(Events, Event) + true -> check_event_ts(TsType, Events, Event) end. %% consistency check for active vs. inactive activity (runnable) @@ -428,6 +476,44 @@ port_echo_loop(Port) -> %% Helpers %%% +check_ts(no_timestamp, Ts) -> + try + no_timestamp = Ts + catch + _ : _ -> + ?t:fail({unexpected_timestamp, Ts}) + end, + ok; +check_ts(timestamp, Ts) -> + try + {Ms,S,Us} = Ts, + true = is_integer(Ms), + true = is_integer(S), + true = is_integer(Us) + catch + _ : _ -> + ?t:fail({unexpected_timestamp, Ts}) + end, + ok; +check_ts(monotonic_timestamp, Ts) -> + try + true = is_integer(Ts) + catch + _ : _ -> + ?t:fail({unexpected_timestamp, Ts}) + end, + ok; +check_ts(strict_monotonic_timestamp, Ts) -> + try + {MT, UMI} = Ts, + true = is_integer(MT), + true = is_integer(UMI) + catch + _ : _ -> + ?t:fail({unexpected_timestamp, Ts}) + end, + ok. + start_load(N) -> Pid = spawn_link(?MODULE, run_load, [N, []]), {ok, Pid}. @@ -454,21 +540,24 @@ list_load() -> end, list_load(). - -has_scheduler_event(Events) -> +has_scheduler_event(TsType, Events) -> lists:any( fun (Pred) -> case Pred of - {profile, scheduler, _ID, _Activity, _NR, _TS} -> true; + {profile, scheduler, _ID, _Activity, _NR, TS} -> + check_ts(TsType, TS), + true; _ -> false end end, Events). -has_runnable_event(Events) -> +has_runnable_event(TsType, Events) -> lists:any( fun (Pred) -> case Pred of - {profile, _Pid, _Activity, _MFA, _TS} -> true; + {profile, _Pid, _Activity, _MFA, TS} -> + check_ts(TsType, TS), + true; _ -> false end end, Events). diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index a12c41a3aa..760666d077 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -67,7 +67,8 @@ trace_on_and_off(Config) when is_list(Config) -> ?line Pid = spawn(?MODULE, bif_process, []), ?line Self = self(), ?line 1 = erlang:trace(Pid, true, [call,timestamp]), - ?line {flags,[timestamp,call]} = erlang:trace_info(Pid,flags), + ?line {flags, Flags} = erlang:trace_info(Pid,flags), + ?line [call,timestamp] = lists:sort(Flags), ?line {tracer, Self} = erlang:trace_info(Pid,tracer), ?line 1 = erlang:trace(Pid, false, [timestamp]), ?line {flags,[call]} = erlang:trace_info(Pid,flags), @@ -111,93 +112,145 @@ do_trace_bif(Flags) -> trace_bif_timestamp(doc) -> "Test tracing BIFs with timestamps."; trace_bif_timestamp(Config) when is_list(Config) -> - do_trace_bif_timestamp([]). - + do_trace_bif_timestamp([], timestamp, [timestamp]), + do_trace_bif_timestamp([], timestamp, + [timestamp, + monotonic_timestamp, + strict_monotonic_timestamp]), + do_trace_bif_timestamp([], strict_monotonic_timestamp, + [strict_monotonic_timestamp]), + do_trace_bif_timestamp([], strict_monotonic_timestamp, + [monotonic_timestamp, strict_monotonic_timestamp]), + do_trace_bif_timestamp([], monotonic_timestamp, [monotonic_timestamp]). + trace_bif_timestamp_local(doc) -> "Test tracing BIFs with timestamps and local flag."; trace_bif_timestamp_local(Config) when is_list(Config) -> - do_trace_bif_timestamp([local]). - -do_trace_bif_timestamp(Flags) -> - ?line Pid=spawn(?MODULE, bif_process, []), - ?line 1 = erlang:trace(Pid, true, [call,timestamp]), - ?line erlang:trace_pattern({erlang,'_','_'}, [], Flags), - - ?line Pid ! {do_bif, time, []}, - ?line receive_trace_msg_ts({trace_ts,Pid,call,{erlang,time,[]}}), - - ?line Pid ! {do_bif, statistics, [runtime]}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,statistics, [runtime]}}), - - ?line Pid ! {do_time_bif}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,time, []}}), - - ?line Pid ! {do_statistics_bif}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,statistics, [runtime]}}), + do_trace_bif_timestamp([local], timestamp, [timestamp]), + do_trace_bif_timestamp([local], timestamp, + [timestamp, + monotonic_timestamp, + strict_monotonic_timestamp]), + do_trace_bif_timestamp([local], strict_monotonic_timestamp, + [strict_monotonic_timestamp]), + do_trace_bif_timestamp([local], strict_monotonic_timestamp, + [monotonic_timestamp, strict_monotonic_timestamp]), + do_trace_bif_timestamp([local], monotonic_timestamp, [monotonic_timestamp]). + +do_trace_bif_timestamp(Flags, TsType, TsFlags) -> + io:format("Testing with TsType=~p TsFlags=~p~n", [TsType, TsFlags]), + Pid=spawn(?MODULE, bif_process, []), + 1 = erlang:trace(Pid, true, [call]++TsFlags), + erlang:trace_pattern({erlang,'_','_'}, [], Flags), + + Ts0 = make_ts(TsType), + Pid ! {do_bif, time, []}, + Ts1 = receive_trace_msg_ts({trace_ts,Pid,call,{erlang,time,[]}}, + Ts0,TsType), + + Pid ! {do_bif, statistics, [runtime]}, + Ts2 = receive_trace_msg_ts({trace_ts,Pid,call, + {erlang,statistics, [runtime]}}, + Ts1, TsType), + + Pid ! {do_time_bif}, + Ts3 = receive_trace_msg_ts({trace_ts,Pid,call, + {erlang,time, []}}, + Ts2, TsType), + + Pid ! {do_statistics_bif}, + Ts4 = receive_trace_msg_ts({trace_ts,Pid,call, + {erlang,statistics, [runtime]}}, + Ts3, TsType), + + check_ts(TsType, Ts4, make_ts(TsType)), %% We should be able to turn off the timestamp. - ?line 1 = erlang:trace(Pid, false, [timestamp]), + 1 = erlang:trace(Pid, false, TsFlags), - ?line Pid ! {do_statistics_bif}, - ?line receive_trace_msg({trace,Pid,call, - {erlang,statistics, [runtime]}}), + Pid ! {do_statistics_bif}, + receive_trace_msg({trace,Pid,call, + {erlang,statistics, [runtime]}}), - ?line Pid ! {do_bif, statistics, [runtime]}, - ?line receive_trace_msg({trace,Pid,call, - {erlang,statistics, [runtime]}}), + Pid ! {do_bif, statistics, [runtime]}, + receive_trace_msg({trace,Pid,call, + {erlang,statistics, [runtime]}}), - ?line 1 = erlang:trace(Pid, false, [call]), - ?line erlang:trace_pattern({erlang,'_','_'}, false, Flags), + 1 = erlang:trace(Pid, false, [call]), + erlang:trace_pattern({erlang,'_','_'}, false, Flags), - ?line exit(Pid, die), + exit(Pid, die), ok. trace_bif_return(doc) -> "Test tracing BIF's with return/return_to trace."; trace_bif_return(Config) when is_list(Config) -> - ?line Pid=spawn(?MODULE, bif_process, []), - ?line 1 = erlang:trace(Pid, true, [call,timestamp,return_to]), - ?line erlang:trace_pattern({erlang,'_','_'}, [{'_',[],[{return_trace}]}], - [local]), - - - ?line Pid ! {do_bif, time, []}, - ?line receive_trace_msg_ts({trace_ts,Pid,call,{erlang,time,[]}}), - ?line receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, - {erlang,time,0}}), - ?line receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, - {?MODULE, bif_process,0}}), - - - ?line Pid ! {do_bif, statistics, [runtime]}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,statistics, [runtime]}}), - ?line receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, - {erlang,statistics,1}}), - ?line receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, - {?MODULE, bif_process,0}}), - - - ?line Pid ! {do_time_bif}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,time, []}}), - ?line receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, - {erlang,time,0}}), - ?line receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, - {?MODULE, bif_process,0}}), - - - - ?line Pid ! {do_statistics_bif}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,statistics, [runtime]}}), - ?line receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, - {erlang,statistics,1}}), - ?line receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, - {?MODULE, bif_process,0}}), + do_trace_bif_return(timestamp, [timestamp]), + do_trace_bif_return(timestamp, + [timestamp, + monotonic_timestamp, + strict_monotonic_timestamp]), + do_trace_bif_return(strict_monotonic_timestamp, + [strict_monotonic_timestamp]), + do_trace_bif_return(strict_monotonic_timestamp, + [monotonic_timestamp, strict_monotonic_timestamp]), + do_trace_bif_return(monotonic_timestamp, [monotonic_timestamp]). + +do_trace_bif_return(TsType, TsFlags) -> + io:format("Testing with TsType=~p TsFlags=~p~n", [TsType, TsFlags]), + Pid=spawn(?MODULE, bif_process, []), + 1 = erlang:trace(Pid, true, [call,return_to]++TsFlags), + erlang:trace_pattern({erlang,'_','_'}, [{'_',[],[{return_trace}]}], + [local]), + + Ts0 = make_ts(TsType), + Pid ! {do_bif, time, []}, + Ts1 = receive_trace_msg_ts({trace_ts,Pid,call,{erlang,time,[]}}, + Ts0, TsType), + Ts2 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, + {erlang,time,0}}, + Ts1, TsType), + Ts3 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, + {?MODULE, bif_process,0}}, + Ts2, TsType), + + + Pid ! {do_bif, statistics, [runtime]}, + Ts4 = receive_trace_msg_ts({trace_ts,Pid,call, + {erlang,statistics, [runtime]}}, + Ts3, TsType), + Ts5 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, + {erlang,statistics,1}}, + Ts4, TsType), + Ts6 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, + {?MODULE, bif_process,0}}, + Ts5, TsType), + + + Pid ! {do_time_bif}, + Ts7 = receive_trace_msg_ts({trace_ts,Pid,call, + {erlang,time, []}}, + Ts6, TsType), + Ts8 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, + {erlang,time,0}}, + Ts7, TsType), + Ts9 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, + {?MODULE, bif_process,0}}, + Ts8, TsType), + + + + Pid ! {do_statistics_bif}, + Ts10 = receive_trace_msg_ts({trace_ts,Pid,call, + {erlang,statistics, [runtime]}}, + Ts9, TsType), + Ts11 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, + {erlang,statistics,1}}, + Ts10, TsType), + Ts12 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, + {?MODULE, bif_process,0}}, + Ts11, TsType), + check_ts(TsType, Ts12, make_ts(TsType)), ok. @@ -213,10 +266,11 @@ receive_trace_msg(Mess) -> ?t:fail() end. -receive_trace_msg_ts({trace_ts, Pid, call, {erlang,F,A}}) -> +receive_trace_msg_ts({trace_ts, Pid, call, {erlang,F,A}}, PrevTs, TsType) -> receive - {trace_ts, Pid, call, {erlang, F, A}, _Ts} -> - ok; + {trace_ts, Pid, call, {erlang, F, A}, Ts} -> + check_ts(TsType, PrevTs, Ts), + Ts; Other -> io:format("Expected: {trace, ~p, call, {~p, ~p, ~p}, TimeStamp}},~n" "Got: ~p~n", @@ -227,10 +281,11 @@ receive_trace_msg_ts({trace_ts, Pid, call, {erlang,F,A}}) -> ?t:fail() end. -receive_trace_msg_ts_return_from({trace_ts, Pid, return_from, {erlang,F,A}}) -> +receive_trace_msg_ts_return_from({trace_ts, Pid, return_from, {erlang,F,A}}, PrevTs, TsType) -> receive - {trace_ts, Pid, return_from, {erlang, F, A}, _Value, _Ts} -> - ok; + {trace_ts, Pid, return_from, {erlang, F, A}, _Value, Ts} -> + check_ts(TsType, PrevTs, Ts), + Ts; Other -> io:format("Expected: {trace_ts, ~p, return_from, {~p, ~p, ~p}, Value, TimeStamp}},~n" "Got: ~p~n", @@ -241,10 +296,11 @@ receive_trace_msg_ts_return_from({trace_ts, Pid, return_from, {erlang,F,A}}) -> ?t:fail() end. -receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}) -> +receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}, PrevTs, TsType) -> receive - {trace_ts, Pid, return_to, {M, F, A}, _Ts} -> - ok; + {trace_ts, Pid, return_to, {M, F, A}, Ts} -> + check_ts(TsType, PrevTs, Ts), + Ts; Other -> io:format("Expected: {trace_ts, ~p, return_to, {~p, ~p, ~p}, TimeStamp}},~n" "Got: ~p~n", @@ -255,6 +311,33 @@ receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}) -> ?t:fail() end. +make_ts(timestamp) -> + erlang:now(); +make_ts(monotonic_timestamp) -> + erlang:monotonic_time(); +make_ts(strict_monotonic_timestamp) -> + MT = erlang:monotonic_time(), + UMI = erlang:unique_integer([monotonic]), + {MT, UMI}. + +check_ts(timestamp, PrevTs, Ts) -> + {Ms, S, Us} = Ts, + true = is_integer(Ms), + true = is_integer(S), + true = is_integer(Us), + true = PrevTs < Ts, + Ts; +check_ts(monotonic_timestamp, PrevTs, Ts) -> + true = is_integer(Ts), + true = PrevTs =< Ts, + Ts; +check_ts(strict_monotonic_timestamp, PrevTs, Ts) -> + {MT, UMI} = Ts, + true = is_integer(MT), + true = is_integer(UMI), + true = PrevTs < Ts, + Ts. + bif_process() -> receive {do_bif, Name, Args} -> diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 58516c0ff3..36862802ca 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 5fc6d14938..063b9a1f26 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -185,6 +185,8 @@ 'receive' | 'print' | 'timestamp' | + 'monotonic_timestamp' | + 'strict_monotonic_timestamp' | 'label' | 'serial'. @@ -198,7 +200,10 @@ 'exclusive' | 'runnable_ports' | 'runnable_procs' | - 'scheduler'. + 'scheduler' | + 'timestamp' | + 'monotonic_timestamp' | + 'strict_monotonic_timestamp'. -type system_monitor_option() :: 'busy_port' | @@ -230,6 +235,8 @@ garbage_collection | timestamp | cpu_timestamp | + monotonic_timestamp | + strict_monotonic_timestamp | arity | set_on_spawn | set_on_first_spawn | @@ -258,6 +265,8 @@ running | garbage_collection | timestamp | + monotonic_timestamp | + strict_monotonic_timestamp | arity. -type trace_info_return() :: @@ -2178,6 +2187,8 @@ send(_Dest,_Msg,_Options) -> ('receive') -> {'receive', boolean()}; (print) -> {print, boolean()}; (timestamp) -> {timestamp, boolean()}; + (monotonic_timestamp) -> {timestamp, boolean()}; + (strict_monotonic_timestamp) -> {strict_monotonic_timestamp, boolean()}; (label) -> [] | {label, non_neg_integer()}; (serial) -> [] | {serial, {non_neg_integer(), non_neg_integer()}}. seq_trace_info(_What) -> -- cgit v1.2.3 From dc8e62f33ad0ca7a772ec74d67b6d3e157f639b4 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 13 Jan 2016 18:33:42 +0100 Subject: Introduce time warp safe replacement for safe_fixed option The new time warp safe option is safe_fixed_monotonic_time which gives erlang:monotonic_time(). The safe_fixed option was also slightly changed. It now gives erlang:timestamp() instead of erlang:now(). This has however not been documented, so it is considered a compatible change. The above effects both ets, and dets. This commit also include the bugfix OTP-13239 for dets:info(Tab, safe_fixed). The timestamp in the result returned by dets:info(Tab, safe_fixed) was unintentionally broken as a result of the time API rewrites in OTP 18.0. --- erts/emulator/beam/erl_db.c | 46 ++++++++++++++++++++++++++++---------- erts/emulator/beam/erl_db_util.h | 5 ++++- erts/emulator/beam/erl_time_sup.c | 47 ++++++++++++++++++++++++++++++--------- erts/emulator/beam/sys.h | 4 ++++ 4 files changed, 78 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 878ee32b47..645f9e3c28 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -1376,7 +1376,6 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2) status |= DB_ORDERED_SET; status &= ~(DB_SET | DB_BAG | DB_DUPLICATE_BAG); } - /*TT*/ else if (is_tuple(val)) { Eterm *tp = tuple_val(val); if (arityval(tp[0]) == 2) { @@ -3466,10 +3465,10 @@ static void fix_table_locked(Process* p, DbTable* tb) #endif erts_refc_inc(&tb->common.ref,1); fix = tb->common.fixations; - if (fix == NULL) { - get_now(&(tb->common.megasec), - &(tb->common.sec), - &(tb->common.microsec)); + if (fix == NULL) { + tb->common.time.monotonic + = erts_get_monotonic_time(ERTS_PROC_GET_SCHDATA(p)); + tb->common.time.offset = erts_get_time_offset(); } else { for (; fix != NULL; fix = fix->next) { @@ -3731,6 +3730,7 @@ static int free_table_cont(Process *p, static Eterm table_info(Process* p, DbTable* tb, Eterm What) { Eterm ret = THE_NON_VALUE; + int use_monotonic; if (What == am_size) { ret = make_small(erts_smp_atomic_read_nob(&tb->common.nitems)); @@ -3788,7 +3788,10 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What) ret = am_true; else ret = am_false; - } else if (What == am_atom_put("safe_fixed",10)) { + } else if ((use_monotonic + = ERTS_IS_ATOM_STR("safe_fixed_monotonic_time", + What)) + || ERTS_IS_ATOM_STR("safe_fixed", What)) { #ifdef ERTS_SMP erts_smp_mtx_lock(&tb->common.fixlock); #endif @@ -3797,7 +3800,19 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What) Eterm *hp; Eterm tpl, lst; DbFixation *fix; - need = 7; + Sint64 mtime; + + need = 3; + if (use_monotonic) { + mtime = (Sint64) tb->common.time.monotonic; + mtime += ERTS_MONOTONIC_OFFSET_NATIVE; + if (!IS_SSMALL(mtime)) + need += ERTS_SINT64_HEAP_SIZE(mtime); + } + else { + mtime = 0; + need += 4; + } for (fix = tb->common.fixations; fix != NULL; fix = fix->next) { need += 5; } @@ -3809,11 +3824,18 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What) lst = CONS(hp,tpl,lst); hp += 2; } - tpl = TUPLE3(hp, - make_small(tb->common.megasec), - make_small(tb->common.sec), - make_small(tb->common.microsec)); - hp += 4; + if (use_monotonic) + tpl = (IS_SSMALL(mtime) + ? make_small(mtime) + : erts_sint64_to_big(mtime, &hp)); + else { + Uint ms, s, us; + erts_make_timestamp_value(&ms, &s, &us, + tb->common.time.monotonic, + tb->common.time.offset); + tpl = TUPLE3(hp, make_small(ms), make_small(s), make_small(us)); + hp += 4; + } ret = TUPLE2(hp, tpl, lst); } else { ret = am_false; diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 1ccdc0305b..0903a40460 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -229,7 +229,10 @@ typedef struct db_table_common { DbTableMethod* meth; /* table methods */ erts_smp_atomic_t nitems; /* Total number of items in table */ erts_smp_atomic_t memory_size;/* Total memory size. NOTE: in bytes! */ - Uint megasec,sec,microsec; /* Last fixation time */ + struct { /* Last fixation time */ + ErtsMonotonicTime monotonic; + ErtsMonotonicTime offset; + } time; DbFixation* fixations; /* List of processes who have done safe_fixtable, "local" fixations not included. */ /* All 32-bit fields */ diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 7327e0b48c..2c87b8e81c 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -1747,6 +1747,39 @@ erts_get_monotonic_time(ErtsSchedulerData *esdp) return mtime; } +ErtsMonotonicTime +erts_get_time_offset(void) +{ + return get_time_offset(); +} + +static ERTS_INLINE void +make_timestamp_value(Uint* megasec, Uint* sec, Uint* microsec, + ErtsMonotonicTime mtime, ErtsMonotonicTime offset) +{ + ErtsMonotonicTime stime, as; + Uint ms; + + stime = ERTS_MONOTONIC_TO_USEC(mtime + offset); + + as = stime / ERTS_MONOTONIC_TIME_MEGA; + *megasec = ms = (Uint) (stime / ERTS_MONOTONIC_TIME_TERA); + *sec = (Uint) (as - (((ErtsMonotonicTime) ms) + * ERTS_MONOTONIC_TIME_MEGA)); + *microsec = (Uint) (stime - as*ERTS_MONOTONIC_TIME_MEGA); + + ASSERT(((ErtsMonotonicTime) ms)*ERTS_MONOTONIC_TIME_TERA + + ((ErtsMonotonicTime) *sec)*ERTS_MONOTONIC_TIME_MEGA + + *microsec == stime); +} + +void +erts_make_timestamp_value(Uint* megasec, Uint* sec, Uint* microsec, + ErtsMonotonicTime mtime, ErtsMonotonicTime offset) +{ + make_timestamp_value(megasec, sec, microsec, mtime, offset); +} + void get_sys_now(Uint* megasec, Uint* sec, Uint* microsec) { @@ -2220,22 +2253,14 @@ BIF_RETTYPE time_offset_1(BIF_ALIST_1) BIF_RETTYPE timestamp_0(BIF_ALIST_0) { Eterm *hp, res; - ErtsMonotonicTime stime, mtime, all_sec, offset; + ErtsMonotonicTime mtime, offset; Uint mega_sec, sec, micro_sec; mtime = time_sup.r.o.get_time(); offset = get_time_offset(); update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime); - stime = ERTS_MONOTONIC_TO_USEC(mtime + offset); - all_sec = stime / ERTS_MONOTONIC_TIME_MEGA; - mega_sec = (Uint) (stime / ERTS_MONOTONIC_TIME_TERA); - sec = (Uint) (all_sec - (((ErtsMonotonicTime) mega_sec) - * ERTS_MONOTONIC_TIME_MEGA)); - micro_sec = (Uint) (stime - all_sec*ERTS_MONOTONIC_TIME_MEGA); - - ASSERT(((ErtsMonotonicTime) mega_sec)*ERTS_MONOTONIC_TIME_TERA - + ((ErtsMonotonicTime) sec)*ERTS_MONOTONIC_TIME_MEGA - + micro_sec == stime); + + make_timestamp_value(&mega_sec, &sec, µ_sec, mtime, offset); /* * Mega seconds is the only value that potentially diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index ec94e3a596..03e30573de 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -819,6 +819,10 @@ int local_to_univ(Sint *year, Sint *month, Sint *day, void get_now(Uint*, Uint*, Uint*); struct ErtsSchedulerData_; ErtsMonotonicTime erts_get_monotonic_time(struct ErtsSchedulerData_ *); +ErtsMonotonicTime erts_get_time_offset(void); +void +erts_make_timestamp_value(Uint* megasec, Uint* sec, Uint* microsec, + ErtsMonotonicTime mtime, ErtsMonotonicTime offset); void get_sys_now(Uint*, Uint*, Uint*); void set_break_quit(void (*)(void), void (*)(void)); -- cgit v1.2.3 From 7189317ef89dfedaa35a804dfedd4fc27bf28d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 13 Jan 2016 12:59:15 +0100 Subject: erl_prim_loader: Remove unused 'cache' field The #prim_state.cache' field is unused. The actual cache is kept in the process dictionary. --- erts/preloaded/src/erl_prim_loader.erl | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 5f88029585..3ef48ad61e 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -57,7 +57,6 @@ -type host() :: atom(). -record(prim_state, {debug :: boolean(), - cache, primary_archive}). -type prim_state() :: #prim_state{}. -- cgit v1.2.3 From ca1964e765595dfe9089759adb5ef5eded4f2bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 13 Jan 2016 13:35:53 +0100 Subject: erl_prim_loader: Correct purging of the archive cache prim_do_release_archives/3 can't make up its mind whether the primary archive should be released or not. The key in the process dictionary is kept, while #prim_state.primary_archive is cleared. It seems that intent was the primary archive should be preserved, because the function was intended to be called by a timeout routine every sixth minute (it is not because of a bug in setting up the timeout). Therefore, rewrite the code to preserve the primary archive and simplify it while at it. Also, rename prim_release_archives/1 to prim_purge_cache/0 to make it clearer what it is doing. --- erts/preloaded/src/erl_prim_loader.erl | 47 +++++++++++----------------------- 1 file changed, 15 insertions(+), 32 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 3ef48ad61e..ac461bc6c7 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -435,10 +435,6 @@ efile_set_primary_archive(#state{prim_state = PS} = State, File, FileInfo, ParserFun), {Res,State#state{prim_state = PS2}}. -efile_release_archives(#state{prim_state = PS} = State) -> - {Res, PS2} = prim_release_archives(PS), - {Res,State#state{prim_state = PS2}}. - efile_list_dir(#state{prim_state = PS} = State, Dir) -> {Res, PS2} = prim_list_dir(PS, Dir), {Res, State#state{prim_state = PS2}}. @@ -463,8 +459,8 @@ efile_exit_port(State, _Port, _Reason) -> efile_timeout_handler(#state{n_timeouts = N} = State, _Parent) -> if N =< 0 -> - {_Res, State2} = efile_release_archives(State), - State2#state{n_timeouts = ?N_TIMEOUTS}; + prim_purge_cache(), + State#state{n_timeouts = ?N_TIMEOUTS}; true -> State#state{n_timeouts = N - 1} end. @@ -733,32 +729,19 @@ prim_init() -> end, cache_new(#prim_state{debug = Deb}). -prim_release_archives(PS) -> - debug(PS, release_archives), - {Res, PS2} = prim_do_release_archives(PS, get(), []), - debug(PS2, {return, Res}), - {Res, PS2}. - -prim_do_release_archives(PS, [{ArchiveFile, DictVal} | KeyVals], Acc) -> - Res = - case DictVal of - {primary, _PrimZip, _FI, _ParserFun} -> - ok; % Keep primary archive - {Cache, _FI} -> - debug(PS, {release, cache, ArchiveFile}), - erase(ArchiveFile), - clear_cache(ArchiveFile, Cache) - end, - case Res of - ok -> - prim_do_release_archives(PS, KeyVals, Acc); - {error, Reason} -> - prim_do_release_archives(PS, KeyVals, [{ArchiveFile, Reason} | Acc]) - end; -prim_do_release_archives(PS, [], []) -> - {ok, PS#prim_state{primary_archive = undefined}}; -prim_do_release_archives(PS, [], Errors) -> - {{error, Errors}, PS#prim_state{primary_archive = undefined}}. +prim_purge_cache() -> + do_prim_purge_cache(get()). + +do_prim_purge_cache([{Key,Val}|T]) -> + case Val of + {Cache,_FI} -> + catch clear_cache(Key, Cache); + _ -> + ok + end, + do_prim_purge_cache(T); +do_prim_purge_cache([]) -> + ok. prim_set_primary_archive(PS, undefined, undefined, undefined, _ParserFun) -> debug(PS, {set_primary_archive, clean}), -- cgit v1.2.3 From b662e8abbdf63390af5173752ea87d7c952f5185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 13 Jan 2016 13:41:53 +0100 Subject: erl_prim_loader: Correct timeout handling for efile The timeout routine for efile was never called. While at it, eliminate the n_timeouts field and simplify the logic. --- erts/preloaded/src/erl_prim_loader.erl | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index ac461bc6c7..e1c014fee6 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -65,12 +65,10 @@ hosts = [] :: [host()], % hosts list (to boot from) data :: 'noport' | port(), % data port etc timeout :: timeout(), % idle timeout - %% Number of timeouts before archives are released - n_timeouts :: non_neg_integer(), prim_state :: prim_state()}). % state for efile code loader --define(IDLE_TIMEOUT, 60000). %% tear inet connection after 1 minutes --define(N_TIMEOUTS, 6). %% release efile archive after 6 minutes +-define(EFILE_IDLE_TIMEOUT, (6*60*1000)). %purge archives +-define(INET_IDLE_TIMEOUT, (60*1000)). %tear down connection timeout %% Defines for inet as prim_loader -define(INET_FAMILY, inet). @@ -142,8 +140,7 @@ start_inet(Parent) -> State = #state {loader = inet, hosts = AL, data = Tcp, - timeout = ?IDLE_TIMEOUT, - n_timeouts = ?N_TIMEOUTS, + timeout = ?INET_IDLE_TIMEOUT, prim_state = PS}, loop(State, Parent, []). @@ -163,7 +160,7 @@ start_efile(Parent) -> PS = prim_init(), State = #state {loader = efile, data = Port, - timeout = infinity, + timeout = ?EFILE_IDLE_TIMEOUT, prim_state = PS}, loop(State, Parent, []). @@ -456,14 +453,9 @@ efile_exit_port(State, Port, Reason) when State#state.data =:= Port -> efile_exit_port(State, _Port, _Reason) -> State. -efile_timeout_handler(#state{n_timeouts = N} = State, _Parent) -> - if - N =< 0 -> - prim_purge_cache(), - State#state{n_timeouts = ?N_TIMEOUTS}; - true -> - State#state{n_timeouts = N - 1} - end. +efile_timeout_handler(State, _Parent) -> + prim_purge_cache(), + State. %%% -------------------------------------------------------- %%% Functions which handle inet prim_loader @@ -604,7 +596,7 @@ inet_get_file_from_port1(_File, [], State) -> inet_send_and_rcv(Msg, Tag, State) when State#state.data =:= noport -> {ok,Tcp} = find_master(State#state.hosts), %% reconnect inet_send_and_rcv(Msg, Tag, State#state{data = Tcp, - timeout = ?IDLE_TIMEOUT}); + timeout = ?INET_IDLE_TIMEOUT}); inet_send_and_rcv(Msg, Tag, #state{data = Tcp, timeout = Timeout} = State) -> prim_inet:send(Tcp, term_to_binary(Msg)), receive -- cgit v1.2.3 From 56eded8ac675bf0e6f955c291be845f668d2b795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 13 Jan 2016 15:11:43 +0100 Subject: erl_prim_loader: Rename release_archives/0 Rename release_archives/0 to purge_archive_cache/0 to make it clearer what it does and what it doesn't do. Also add a comment about its intended purpose. Note that release_archives/0 is not documented and is part of the experimental archive feature. Furthermore, the only uses I could find were in the test suite. I did not find any uses in the external applications relx and rebar3 applications that are known to use archives. Therefore, I think that the increased clarity is worth the small risk of breaking code. --- erts/preloaded/src/erl_prim_loader.erl | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index e1c014fee6..824ed3435d 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -50,7 +50,10 @@ prim_read_file_info/3, prim_get_cwd/2]). %% Used by escript and code --export([set_primary_archive/4, release_archives/0]). +-export([set_primary_archive/4]). + +%% Used by test suites +-export([purge_archive_cache/0]). -include_lib("kernel/include/file.hrl"). @@ -225,10 +228,13 @@ set_primary_archive(File, ArchiveBin, FileInfo, ParserFun) when is_list(File), is_binary(ArchiveBin), is_record(FileInfo, file_info) -> request({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}). --spec release_archives() -> 'ok' | {'error', _}. +%% NOTE: Does not close the primary archive. Only closes all +%% open zip files kept in the cache. Should be called before an archive +%% file is to be removed (for example in the test suites). -release_archives() -> - request(release_archives). +-spec purge_archive_cache() -> 'ok' | {'error', _}. +purge_archive_cache() -> + request(purge_archive_cache). request(Req) -> Loader = whereis(erl_prim_loader), @@ -332,8 +338,8 @@ handle_request(Req, Paths, St0) -> {set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} -> handle_set_primary_archive(St0, File, ArchiveBin, FileInfo, ParserFun); - release_archives -> - handle_release_archives(St0); + purge_archive_cache -> + handle_purge_archive_cache(St0); _ -> ignore end. @@ -346,8 +352,9 @@ handle_get_file(State = #state{loader = inet}, Paths, File) -> handle_set_primary_archive(State= #state{loader = efile}, File, ArchiveBin, FileInfo, ParserFun) -> ?SAFE2(efile_set_primary_archive(State, File, ArchiveBin, FileInfo, ParserFun), State). -handle_release_archives(State= #state{loader = efile}) -> - ?SAFE2(efile_release_archives(State), State). +handle_purge_archive_cache(#state{loader = efile}=State) -> + prim_purge_cache(), + {ok,State}. handle_list_dir(State = #state{loader = efile}, Dir) -> ?SAFE2(efile_list_dir(State, Dir), State); -- cgit v1.2.3 From 53dcd92be965d35ef53a27efda0597b2961921ba Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 20 Jan 2016 19:42:31 +0100 Subject: erts: Update docs for erlang:purge_module/1 --- erts/doc/src/erlang.xml | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 8ddbd95de5..72d08a03fe 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4870,6 +4870,12 @@ os_prompt% code(3)) and is not to be used elsewhere.

+ +

As from ERTS 8.0 (OTP 19), any lingering processes + that still execute the old code will be killed by this function. + In earlier versions, such incorrect use could cause much + more fatal failures, like emulator crash.

+

Failure: badarg if there is no old code for Module.

-- cgit v1.2.3 From 40053b5f68655d76c0138e9be4306228ec14cd1e Mon Sep 17 00:00:00 2001 From: Michael Santos Date: Wed, 20 Jan 2016 15:16:06 -0500 Subject: Fix typo in Windows build scripts --- erts/etc/win32/cygwin_tools/vc/cc.sh | 2 +- erts/etc/win32/msys_tools/vc/cc.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/etc/win32/cygwin_tools/vc/cc.sh b/erts/etc/win32/cygwin_tools/vc/cc.sh index 48a579d5f0..651b6e098d 100755 --- a/erts/etc/win32/cygwin_tools/vc/cc.sh +++ b/erts/etc/win32/cygwin_tools/vc/cc.sh @@ -267,7 +267,7 @@ for x in $SOURCES; do echo echo after_sed=`date '+%s'` - echo Made dependencises for $x':' `expr $after_sed '-' $start_time` 's' >&2 + echo Made dependencies for $x':' `expr $after_sed '-' $start_time` 's' >&2 fi else cat $MSG_FILE diff --git a/erts/etc/win32/msys_tools/vc/cc.sh b/erts/etc/win32/msys_tools/vc/cc.sh index ac89aac34e..72005862ed 100644 --- a/erts/etc/win32/msys_tools/vc/cc.sh +++ b/erts/etc/win32/msys_tools/vc/cc.sh @@ -268,7 +268,7 @@ for x in $SOURCES; do echo echo after_sed=`date '+%s'` - echo Made dependencises for $x':' `expr $after_sed '-' $start_time` 's' >&2 + echo Made dependencies for $x':' `expr $after_sed '-' $start_time` 's' >&2 fi else cat $MSG_FILE -- cgit v1.2.3 From 613a8d74d1641ee67d3d9e7333c9711ff8a1f446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 20 Jan 2016 12:36:49 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 51424 -> 49964 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 6ee26b7575..f6057b45d4 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ -- cgit v1.2.3 From da89d3fa551d80c0b16cfd6d46580274cd644ace Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 19 Jan 2016 17:06:30 +0100 Subject: Fix inet driver multi timers using new time API --- erts/emulator/drivers/common/inet_drv.c | 139 +++----------------------------- 1 file changed, 13 insertions(+), 126 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 89011d89ad..43cb15a25f 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1048,7 +1048,7 @@ typedef union { #endif typedef struct _multi_timer_data { - ErlDrvNowData when; + ErlDrvTime when; ErlDrvTermData caller; void (*timeout_function)(ErlDrvData drv_data, ErlDrvTermData caller); struct _multi_timer_data *next; @@ -12144,115 +12144,18 @@ make_noninheritable_handle(SOCKET s) * Multi-timers */ -static void absolute_timeout(unsigned millis, ErlDrvNowData *out) -{ - unsigned rest; - unsigned long millipart; - unsigned long secpart; - unsigned long megasecpart; - unsigned tmo_secs = (millis / 1000U); - unsigned tmo_millis = (millis % 1000); - driver_get_now(out); - rest = (out->microsecs) % 1000; - millipart = ((out->microsecs) / 1000UL); - if (rest >= 500) { - ++millipart; - } - secpart = out->secs; - megasecpart = out->megasecs; - millipart += tmo_millis; - secpart += (millipart / 1000000UL); - millipart %= 1000000UL; - secpart += tmo_secs; - megasecpart += (secpart / 1000000UL); - secpart %= 1000000UL; - out->megasecs = megasecpart; - out->secs = secpart; - out->microsecs = (millipart * 1000UL); -} - -static unsigned relative_timeout(ErlDrvNowData *in) -{ - ErlDrvNowData now; - unsigned rest; - unsigned long millipart, in_millis, in_secs, in_megasecs; - - driver_get_now(&now); - - in_secs = in->secs; - in_megasecs = in->megasecs; - - rest = (now.microsecs) % 1000; - millipart = ((now.microsecs) / 1000UL); - if (rest >= 500) { - ++millipart; - } - in_millis = ((in->microsecs) / 1000UL); - if ( in_millis < millipart ) { - if (in_secs > 0) { - --in_secs; - } else { - in_secs = (1000000UL - 1UL); - if (in_megasecs <= now.megasecs) { - return 0; - } else { - --in_megasecs; - } - } - in_millis += 1000UL; - } - in_millis -= millipart; - - if (in_secs < now.secs) { - if (in_megasecs <= now.megasecs) { - return 0; - } else { - --in_megasecs; - } - in_secs += 1000000; - } - in_secs -= now.secs; - if (in_megasecs < now.megasecs) { - return 0; - } else { - in_megasecs -= now.megasecs; - } - return (unsigned) ((in_megasecs * 1000000000UL) + - (in_secs * 1000UL) + - in_millis); -} - -#ifdef DEBUG -static int nowcmp(ErlDrvNowData *d1, ErlDrvNowData *d2) -{ - /* Assume it's not safe to do signed conversion on megasecs... */ - if (d1->megasecs < d2->megasecs) { - return -1; - } else if (d1->megasecs > d2->megasecs) { - return 1; - } else if (d1->secs != d2->secs) { - return ((int) d1->secs) - ((int) d2->secs); - } - return ((int) d1->microsecs) - ((int) d2->microsecs); -} -#endif - static void fire_multi_timers(MultiTimerData **first, ErlDrvPort port, ErlDrvData data) { - unsigned next_timeout; + ErlDrvTime next_timeout; if (!*first) { ASSERT(0); return; } #ifdef DEBUG { - ErlDrvNowData chk; - driver_get_now(&chk); - chk.microsecs /= 10000UL; - chk.microsecs *= 10000UL; - chk.microsecs += 10000; - ASSERT(nowcmp(&chk,&((*first)->when)) >= 0); + ErlDrvTime chk = erl_drv_monotonic_time(ERL_DRV_MSEC); + ASSERT(chk >= (*first)->when); } #endif do { @@ -12264,9 +12167,9 @@ static void fire_multi_timers(MultiTimerData **first, ErlDrvPort port, return; } (*first)->prev = NULL; - next_timeout = relative_timeout(&((*first)->when)); - } while (next_timeout == 0); - driver_set_timer(port,next_timeout); + next_timeout = (*first)->when - erl_drv_monotonic_time(ERL_DRV_MSEC); + } while (next_timeout <= 0); + driver_set_timer(port, (unsigned long) next_timeout); } static void clean_multi_timers(MultiTimerData **first, ErlDrvPort port) @@ -12289,8 +12192,10 @@ static void remove_multi_timer(MultiTimerData **first, ErlDrvPort port, MultiTim driver_cancel_timer(port); *first = p->next; if (*first) { - unsigned ntmo = relative_timeout(&((*first)->when)); - driver_set_timer(port,ntmo); + ErlDrvTime ntmo = (*first)->when - erl_drv_monotonic_time(ERL_DRV_MSEC); + if (ntmo < 0) + ntmo = 0; + driver_set_timer(port, (unsigned long) ntmo); } } if (p->next != NULL) { @@ -12304,26 +12209,14 @@ static MultiTimerData *add_multi_timer(MultiTimerData **first, ErlDrvPort port, void (*timeout_fun)(ErlDrvData drv_data, ErlDrvTermData caller)) { -#define eq_mega(a, b) ((a)->when.megasecs == (b)->when.megasecs) -#define eq_sec(a, b) ((a)->when.secs == (b)->when.secs) MultiTimerData *mtd, *p, *s; mtd = ALLOC(sizeof(MultiTimerData)); - absolute_timeout(timeout, &(mtd->when)); + mtd->when = erl_drv_monotonic_time(ERL_DRV_MSEC) + ((ErlDrvTime) timeout) + 1; mtd->timeout_function = timeout_fun; mtd->caller = caller; mtd->next = mtd->prev = NULL; for(p = *first,s = NULL; p != NULL; s = p, p = p->next) { - if (p->when.megasecs >= mtd->when.megasecs) { - break; - } - } - for (; p!= NULL && eq_mega(p, mtd); s = p, p = p->next) { - if (p->when.secs >= mtd->when.secs) { - break; - } - } - for (; p!= NULL && eq_mega(p, mtd) && eq_sec(p, mtd); s = p, p = p->next) { - if (p->when.microsecs >= mtd->when.microsecs) { + if (p->when >= mtd->when) { break; } } @@ -12353,12 +12246,6 @@ static MultiTimerData *add_multi_timer(MultiTimerData **first, ErlDrvPort port, } return mtd; } -#undef eq_mega -#undef eq_sec - - - - /*----------------------------------------------------------------------------- -- cgit v1.2.3 From 3f33428db9aea0d767295322c4e882a5c6bbf7db Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 19 Jan 2016 17:05:55 +0100 Subject: Introduce time management in native APIs --- erts/doc/src/erl_driver.xml | 112 +++++++++++++++++++ erts/doc/src/erl_nif.xml | 112 +++++++++++++++++++ erts/emulator/beam/erl_driver.h | 92 +++++----------- erts/emulator/beam/erl_drv_nif.h | 84 ++++++++++++++ erts/emulator/beam/erl_nif.c | 20 ++++ erts/emulator/beam/erl_nif.h | 62 +++-------- erts/emulator/beam/erl_nif_api_funcs.h | 6 + erts/emulator/beam/erl_time.h | 4 + erts/emulator/beam/erl_time_sup.c | 145 ++++++++++++++++++++++++ erts/emulator/beam/io.c | 23 ++++ erts/emulator/sys/win32/erl_win_dyn_driver.h | 14 +++ erts/emulator/test/nif_SUITE.erl | 153 +++++++++++++++++++++++++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 94 +++++++++++++++- 13 files changed, 809 insertions(+), 112 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index e717fc0c4e..e338e95938 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -347,6 +347,16 @@ the driver does not handle sizes that overflow an int all will work as before.

+ Time Measurement +

Support for time measurement in drivers: + + ErlDrvTime + ErlDrvTimeUnit + erl_drv_monotonic_time() + erl_drv_time_offset() + erl_drv_convert_time_unit() +

+
@@ -860,6 +870,24 @@ typedef struct ErlIOVec { erl_drv_tsd_get().

+ ErlDrvTime + +

A signed 64-bit integer type for representation of time.

+
+ ErlDrvTimeUnit + +

An enumeration of time units supported by the driver API:

+ + ERL_DRV_SEC +

Seconds

+ ERL_DRV_MSEC +

Milliseconds

+ ERL_DRV_USEC +

Microseconds

+ ERL_DRV_NSEC +

Nanoseconds

+
+
@@ -1023,6 +1051,10 @@ typedef struct ErlIOVec { Read a system timestamp +

This function is deprecated! Do not use it! + Use the documented + time measurement functionality + instead.

This function reads a timestamp into the memory pointed to by the parameter now. See the description of ErlDrvNowData for specification of its fields.

@@ -2997,6 +3029,86 @@ ERL_DRV_MAP int sz
+ + ErlDrvTimeerl_drv_monotonic_time(ErlDrvTimeUnit time_unit) + Get Erlang Monotonic Time + + +

Arguments:

+ + time_unit + Time unit of returned value. + +

+ Returns + Erlang + monotonic time. Note that it is not uncommon with + negative values. +

+

Returns ERL_DRV_TIME_ERROR if called with an invalid + time unit argument, or if called from a thread that is not a + scheduler thread.

+

See also:

+ + ErlDrvTime + ErlDrvTimeUnit + +
+
+ + + ErlDrvTimeerl_drv_time_offset(ErlDrvTimeUnit time_unit) + Get current Time Offset + + +

Arguments:

+ + time_unit + Time unit of returned value. + +

Returns the current time offset between + Erlang monotonic time + and + Erlang system time + converted into the time_unit passed as argument.

+

Returns ERL_DRV_TIME_ERROR if called with an invalid + time unit argument, or if called from a thread that is not a + scheduler thread.

+

See also:

+ + ErlDrvTime + ErlDrvTimeUnit + +
+
+ + + ErlDrvTimeerl_drv_convert_time_unit(ErlDrvTime val, ErlDrvTimeUnit from, ErlDrvTimeUnit to) + Convert time unit of a time value + + +

Arguments:

+ + val + Value to convert time unit for. + from + Time unit of val. + to + Time unit of returned value. + +

Converts the val value of time unit from to + the corresponding value of time unit to. The result is + rounded using the floor function.

+

Returns ERL_DRV_TIME_ERROR if called with an invalid + time unit argument.

+

See also:

+ + ErlDrvTime + ErlDrvTimeUnit + +
+
+
SEE ALSO diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 2d8706169f..420c9fea38 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -317,6 +317,17 @@ ok libraries might however fail if deprecated features are used.

+ Time Measurement +

Support for time measurement in NIF libraries: + + ErlNifTime + ErlNifTimeUnit + enif_monotonic_time() + enif_time_offset() + enif_convert_time_unit() +

+
+ Long-running NIFs

Native functions @@ -560,6 +571,25 @@ typedef enum {

A native signed 64-bit integer type.

ErlNifUInt64

A native unsigned 64-bit integer type.

+ + ErlNifTime + +

A signed 64-bit integer type for representation of time.

+
+ ErlNifTimeUnit + +

An enumeration of time units supported by the NIF API:

+ + ERL_NIF_SEC +

Seconds

+ ERL_NIF_MSEC +

Milliseconds

+ ERL_NIF_USEC +

Microseconds

+ ERL_NIF_NSEC +

Nanoseconds

+
+
@@ -1486,6 +1516,88 @@ enif_map_iterator_destroy(env, &iter);

Same as erl_drv_tsd_set.

+ + + + ErlNifTimeenif_monotonic_time(ErlNifTimeUnit time_unit) + Get Erlang Monotonic Time + + +

Arguments:

+ + time_unit + Time unit of returned value. + +

+ Returns + Erlang + monotonic time. Note that it is not uncommon with + negative values. +

+

Returns ERL_NIF_TIME_ERROR if called with an invalid + time unit argument, or if called from a thread that is not a + scheduler thread.

+

See also:

+ + ErlNifTime + ErlNifTimeUnit + +
+
+ + + ErlNifTimeenif_time_offset(ErlNifTimeUnit time_unit) + Get current Time Offset + + +

Arguments:

+ + time_unit + Time unit of returned value. + +

Returns the current time offset between + Erlang monotonic time + and + Erlang system time + converted into the time_unit passed as argument.

+

Returns ERL_NIF_TIME_ERROR if called with an invalid + time unit argument, or if called from a thread that is not a + scheduler thread.

+

See also:

+ + ErlNifTime + ErlNifTimeUnit + +
+
+ + + ErlNifTimeenif_convert_time_unit(ErlNifTime val, ErlNifTimeUnit from, ErlNifTimeUnit to) + Convert time unit of a time value + + +

Arguments:

+ + val + Value to convert time unit for. + from + Time unit of val. + to + Time unit of returned value. + +

Converts the val value of time unit from to + the corresponding value of time unit to. The result is + rounded using the floor function.

+

Returns ERL_NIF_TIME_ERROR if called with an invalid + time unit argument.

+

See also:

+ + ErlNifTime + ErlNifTimeUnit + +
+
+
SEE ALSO diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index dbb4d719c1..7ab58e336b 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -37,47 +37,6 @@ # endif #endif -#ifdef SIZEOF_CHAR -# define SIZEOF_CHAR_SAVED__ SIZEOF_CHAR -# undef SIZEOF_CHAR -#endif -#ifdef SIZEOF_SHORT -# define SIZEOF_SHORT_SAVED__ SIZEOF_SHORT -# undef SIZEOF_SHORT -#endif -#ifdef SIZEOF_INT -# define SIZEOF_INT_SAVED__ SIZEOF_INT -# undef SIZEOF_INT -#endif -#ifdef SIZEOF_LONG -# define SIZEOF_LONG_SAVED__ SIZEOF_LONG -# undef SIZEOF_LONG -#endif -#ifdef SIZEOF_LONG_LONG -# define SIZEOF_LONG_LONG_SAVED__ SIZEOF_LONG_LONG -# undef SIZEOF_LONG_LONG -#endif -#ifdef HALFWORD_HEAP_EMULATOR -# define HALFWORD_HEAP_EMULATOR_SAVED__ HALFWORD_HEAP_EMULATOR -# undef HALFWORD_HEAP_EMULATOR -#endif -#include "erl_int_sizes_config.h" -#if defined(SIZEOF_CHAR_SAVED__) && SIZEOF_CHAR_SAVED__ != SIZEOF_CHAR -# error SIZEOF_CHAR mismatch -#endif -#if defined(SIZEOF_SHORT_SAVED__) && SIZEOF_SHORT_SAVED__ != SIZEOF_SHORT -# error SIZEOF_SHORT mismatch -#endif -#if defined(SIZEOF_INT_SAVED__) && SIZEOF_INT_SAVED__ != SIZEOF_INT -# error SIZEOF_INT mismatch -#endif -#if defined(SIZEOF_LONG_SAVED__) && SIZEOF_LONG_SAVED__ != SIZEOF_LONG -# error SIZEOF_LONG mismatch -#endif -#if defined(SIZEOF_LONG_LONG_SAVED__) && SIZEOF_LONG_LONG_SAVED__ != SIZEOF_LONG_LONG -# error SIZEOF_LONG_LONG mismatch -#endif - /* This is OK to override by the NIF/driver implementor */ #if defined(HALFWORD_HEAP_EMULATOR_SAVED__) && !defined(HALFWORD_HEAP_EMULATOR) #define HALFWORD_HEAP_EMULATOR HALFWORD_HEAP_EMULATOR_SAVED__ @@ -134,7 +93,7 @@ typedef struct { #define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed) #define ERL_DRV_EXTENDED_MAJOR_VERSION 3 -#define ERL_DRV_EXTENDED_MINOR_VERSION 2 +#define ERL_DRV_EXTENDED_MINOR_VERSION 3 /* * The emulator will refuse to load a driver with a major version @@ -176,28 +135,12 @@ typedef struct { /* * Integer types */ -#if defined(__WIN32__) && (SIZEOF_VOID_P == 8) -typedef unsigned __int64 ErlDrvTermData; -typedef unsigned __int64 ErlDrvUInt; -typedef signed __int64 ErlDrvSInt; -#else -typedef unsigned long ErlDrvTermData; -typedef unsigned long ErlDrvUInt; -typedef signed long ErlDrvSInt; -#endif -#if defined(__WIN32__) -typedef unsigned __int64 ErlDrvUInt64; -typedef __int64 ErlDrvSInt64; -#elif SIZEOF_LONG == 8 -typedef unsigned long ErlDrvUInt64; -typedef long ErlDrvSInt64; -#elif SIZEOF_LONG_LONG == 8 -typedef unsigned long long ErlDrvUInt64; -typedef long long ErlDrvSInt64; -#else -#error No 64-bit integer type -#endif +typedef ErlNapiUInt64 ErlDrvUInt64; +typedef ErlNapiSInt64 ErlDrvSInt64; +typedef ErlNapiUInt ErlDrvUInt; +typedef ErlNapiSInt ErlDrvSInt; +typedef ErlNapiUInt ErlDrvTermData; #if defined(__WIN32__) || defined(_WIN32) typedef ErlDrvUInt ErlDrvSizeT; @@ -250,6 +193,17 @@ typedef struct { unsigned long microsecs; } ErlDrvNowData; +typedef ErlDrvSInt64 ErlDrvTime; + +#define ERL_DRV_TIME_ERROR ((ErlDrvSInt64) ERTS_NAPI_TIME_ERROR__) + +typedef enum { + ERL_DRV_SEC = ERTS_NAPI_SEC__, + ERL_DRV_MSEC = ERTS_NAPI_MSEC__, + ERL_DRV_USEC = ERTS_NAPI_USEC__, + ERL_DRV_NSEC = ERTS_NAPI_NSEC__ +} ErlDrvTimeUnit; + /* * Error codes that can be return from driver. */ @@ -685,8 +639,16 @@ EXTERN long driver_async(ErlDrvPort ix, EXTERN int driver_lock_driver(ErlDrvPort ix); /* Get the current 'now' timestamp (analogue to erlang:now()) */ -EXTERN int driver_get_now(ErlDrvNowData *now); - +EXTERN int driver_get_now(ErlDrvNowData *now) ERL_DRV_DEPRECATED_FUNC; + +/* Erlang Monotonic Time */ +EXTERN ErlDrvTime erl_drv_monotonic_time(ErlDrvTimeUnit time_unit); +/* Time offset between Erlang Monotonic Time and Erlang System Time */ +EXTERN ErlDrvTime erl_drv_time_offset(ErlDrvTimeUnit time_unit); +/* Time unit conversion */ +EXTERN ErlDrvTime erl_drv_convert_time_unit(ErlDrvTime val, + ErlDrvTimeUnit from, + ErlDrvTimeUnit to); /* These were removed from the ANSI version, now they're back. */ diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h index e2385f63f4..f6b946ae82 100644 --- a/erts/emulator/beam/erl_drv_nif.h +++ b/erts/emulator/beam/erl_drv_nif.h @@ -50,6 +50,90 @@ typedef enum { } ErlDrvDirtyJobFlags; #endif +#ifdef SIZEOF_CHAR +# define SIZEOF_CHAR_SAVED__ SIZEOF_CHAR +# undef SIZEOF_CHAR +#endif +#ifdef SIZEOF_SHORT +# define SIZEOF_SHORT_SAVED__ SIZEOF_SHORT +# undef SIZEOF_SHORT +#endif +#ifdef SIZEOF_INT +# define SIZEOF_INT_SAVED__ SIZEOF_INT +# undef SIZEOF_INT +#endif +#ifdef SIZEOF_LONG +# define SIZEOF_LONG_SAVED__ SIZEOF_LONG +# undef SIZEOF_LONG +#endif +#ifdef SIZEOF_LONG_LONG +# define SIZEOF_LONG_LONG_SAVED__ SIZEOF_LONG_LONG +# undef SIZEOF_LONG_LONG +#endif +#ifdef HALFWORD_HEAP_EMULATOR +# define HALFWORD_HEAP_EMULATOR_SAVED__ HALFWORD_HEAP_EMULATOR +# undef HALFWORD_HEAP_EMULATOR +#endif +#include "erl_int_sizes_config.h" +#if defined(SIZEOF_CHAR_SAVED__) && SIZEOF_CHAR_SAVED__ != SIZEOF_CHAR +# error SIZEOF_CHAR mismatch +#endif +#if defined(SIZEOF_SHORT_SAVED__) && SIZEOF_SHORT_SAVED__ != SIZEOF_SHORT +# error SIZEOF_SHORT mismatch +#endif +#if defined(SIZEOF_INT_SAVED__) && SIZEOF_INT_SAVED__ != SIZEOF_INT +# error SIZEOF_INT mismatch +#endif +#if defined(SIZEOF_LONG_SAVED__) && SIZEOF_LONG_SAVED__ != SIZEOF_LONG +# error SIZEOF_LONG mismatch +#endif +#if defined(SIZEOF_LONG_LONG_SAVED__) && SIZEOF_LONG_LONG_SAVED__ != SIZEOF_LONG_LONG +# error SIZEOF_LONG_LONG mismatch +#endif + +#if !defined(__GNUC__) && (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) +typedef unsigned __int64 ErlNapiUInt64; +typedef signed __int64 ErlNapiSInt64; +#define ERL_NAPI_SINT64_MAX__ 9223372036854775807i64 +#define ERL_NAPI_SINT64_MIN__ (-ERL_NAPI_SINT64_MAX__ - 1i64) +#elif SIZEOF_LONG == 8 +typedef unsigned long ErlNapiUInt64; +typedef signed long ErlNapiSInt64; +#define ERL_NAPI_SINT64_MAX__ 9223372036854775807L +#define ERL_NAPI_SINT64_MIN__ (-ERL_NAPI_SINT64_MAX__ - 1L) +#elif SIZEOF_LONG_LONG == 8 +typedef unsigned long long ErlNapiUInt64; +typedef signed long long ErlNapiSInt64; +#define ERL_NAPI_SINT64_MAX__ 9223372036854775807LL +#define ERL_NAPI_SINT64_MIN__ (-ERL_NAPI_SINT64_MAX__ - 1LL) +#else +# error No 64-bit integer type +#endif + +#if SIZEOF_VOID_P == 8 +typedef ErlNapiUInt64 ErlNapiUInt; +typedef ErlNapiSInt64 ErlNapiSInt; +#elif SIZEOF_VOID_P == 4 +# if SIZEOF_LONG == SIZEOF_VOID_P +typedef unsigned long ErlNapiUInt; +typedef signed long ErlNapiSInt; +# elif SIZEOF_INT == SIZEOF_VOID_P +typedef unsigned int ErlNapiUInt; +typedef signed int ErlNapiSInt; +# else +# error No 32-bit integer type +# endif +#else +# error Not support arch +#endif + +#define ERTS_NAPI_TIME_ERROR__ ERL_NAPI_SINT64_MIN__ + +#define ERTS_NAPI_SEC__ 0 +#define ERTS_NAPI_MSEC__ 1 +#define ERTS_NAPI_USEC__ 2 +#define ERTS_NAPI_NSEC__ 3 + #endif /* __ERL_DRV_NIF_H__ */ diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index d7a2076d85..3141b05e2b 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1175,6 +1175,26 @@ void enif_thread_exit(void *resp) { erl_drv_thread_exit(resp); } int enif_thread_join(ErlNifTid tid, void **respp) { return erl_drv_thread_join(tid,respp); } int enif_getenv(const char *key, char *value, size_t *value_size) { return erl_drv_getenv(key, value, value_size); } +ErlNifTime enif_monotonic_time(ErlNifTimeUnit time_unit) +{ + return (ErlNifTime) erts_napi_monotonic_time((int) time_unit); +} + +ErlNifTime enif_time_offset(ErlNifTimeUnit time_unit) +{ + return (ErlNifTime) erts_napi_time_offset((int) time_unit); +} + +ErlNifTime +enif_convert_time_unit(ErlNifTime val, + ErlNifTimeUnit from, + ErlNifTimeUnit to) +{ + return (ErlNifTime) erts_napi_convert_time_unit((ErtsMonotonicTime) val, + (int) from, + (int) to); +} + int enif_fprintf(void* filep, const char* format, ...) { int ret; diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 5e39343e9b..75070ad901 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -49,9 +49,10 @@ ** add ErlNifFunc flags ** 2.8: 18.0 add enif_has_pending_exception ** 2.9: 18.2 enif_getenv +** 2.10: Time API */ #define ERL_NIF_MAJOR_VERSION 2 -#define ERL_NIF_MINOR_VERSION 9 +#define ERL_NIF_MINOR_VERSION 10 /* * The emulator will refuse to load a nif-lib with a major version @@ -67,63 +68,36 @@ #include -#ifdef SIZEOF_CHAR -# define SIZEOF_CHAR_SAVED__ SIZEOF_CHAR -# undef SIZEOF_CHAR -#endif -#ifdef SIZEOF_SHORT -# define SIZEOF_SHORT_SAVED__ SIZEOF_SHORT -# undef SIZEOF_SHORT -#endif -#ifdef SIZEOF_INT -# define SIZEOF_INT_SAVED__ SIZEOF_INT -# undef SIZEOF_INT -#endif -#ifdef SIZEOF_LONG -# define SIZEOF_LONG_SAVED__ SIZEOF_LONG -# undef SIZEOF_LONG -#endif -#ifdef SIZEOF_LONG_LONG -# define SIZEOF_LONG_LONG_SAVED__ SIZEOF_LONG_LONG -# undef SIZEOF_LONG_LONG -#endif -#ifdef HALFWORD_HEAP_EMULATOR -# define HALFWORD_HEAP_EMULATOR_SAVED__ HALFWORD_HEAP_EMULATOR -# undef HALFWORD_HEAP_EMULATOR -#endif -#include "erl_int_sizes_config.h" - #ifdef __cplusplus extern "C" { #endif -#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) -typedef unsigned __int64 ErlNifUInt64; -typedef __int64 ErlNifSInt64; -#elif SIZEOF_LONG == 8 -typedef unsigned long ErlNifUInt64; -typedef long ErlNifSInt64; -#elif SIZEOF_LONG_LONG == 8 -typedef unsigned long long ErlNifUInt64; -typedef long long ErlNifSInt64; -#else -#error No 64-bit integer type -#endif +typedef ErlNapiUInt64 ErlNifUInt64; +typedef ErlNapiSInt64 ErlNifSInt64; +typedef ErlNapiUInt ErlNifUInt; +typedef ErlNapiSInt ErlNifSInt; #ifdef HALFWORD_HEAP_EMULATOR # define ERL_NIF_VM_VARIANT "beam.halfword" typedef unsigned int ERL_NIF_TERM; #else # define ERL_NIF_VM_VARIANT "beam.vanilla" -# if SIZEOF_LONG == SIZEOF_VOID_P -typedef unsigned long ERL_NIF_TERM; -# elif SIZEOF_LONG_LONG == SIZEOF_VOID_P -typedef unsigned long long ERL_NIF_TERM; -# endif +typedef ErlNifUInt ERL_NIF_TERM; #endif typedef ERL_NIF_TERM ERL_NIF_UINT; +typedef ErlNifSInt64 ErlNifTime; + +#define ERL_NIF_TIME_ERROR ((ErlNifSInt64) ERTS_NAPI_TIME_ERROR__) + +typedef enum { + ERL_NIF_SEC = ERTS_NAPI_SEC__, + ERL_NIF_MSEC = ERTS_NAPI_MSEC__, + ERL_NIF_USEC = ERTS_NAPI_USEC__, + ERL_NIF_NSEC = ERTS_NAPI_NSEC__ +} ErlNifTimeUnit; + struct enif_environment_t; typedef struct enif_environment_t ErlNifEnv; diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 08b9afc6af..1448a508a2 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -160,6 +160,9 @@ ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env, ERL_NIF_TERM* reason)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_raise_exception, (ErlNifEnv *env, ERL_NIF_TERM reason)); ERL_NIF_API_FUNC_DECL(int,enif_getenv,(const char* key, char* value, size_t* value_size)); +ERL_NIF_API_FUNC_DECL(ErlNifTime, enif_monotonic_time, (ErlNifTimeUnit)); +ERL_NIF_API_FUNC_DECL(ErlNifTime, enif_time_offset, (ErlNifTimeUnit)); +ERL_NIF_API_FUNC_DECL(ErlNifTime, enif_convert_time_unit, (ErlNifTime, ErlNifTimeUnit, ErlNifTimeUnit)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! @@ -312,6 +315,9 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_has_pending_exception ERL_NIF_API_FUNC_MACRO(enif_has_pending_exception) # define enif_raise_exception ERL_NIF_API_FUNC_MACRO(enif_raise_exception) # define enif_getenv ERL_NIF_API_FUNC_MACRO(enif_getenv) +# define enif_monotonic_time ERL_NIF_API_FUNC_MACRO(enif_monotonic_time) +# define enif_time_offset ERL_NIF_API_FUNC_MACRO(enif_time_offset) +# define enif_convert_time_unit ERL_NIF_API_FUNC_MACRO(enif_convert_time_unit) /* ** ADD NEW ENTRIES HERE (before this comment) diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 43e543e035..93a0d556bf 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -133,6 +133,10 @@ typedef struct { extern ErtsTimeSupData erts_time_sup__; +ErtsMonotonicTime erts_napi_monotonic_time(int time_unit); +ErtsMonotonicTime erts_napi_time_offset(int time_unit); +ErtsMonotonicTime erts_napi_convert_time_unit(ErtsMonotonicTime val, int from, int to); + ERTS_GLB_INLINE Uint64 erts_time_unit_conversion(Uint64 value, Uint32 from_time_unit, diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 2c87b8e81c..5f12c7809a 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -33,6 +33,8 @@ #include "global.h" #define ERTS_WANT_TIMER_WHEEL_API #include "erl_time.h" +#include "erl_driver.h" +#include "erl_nif.h" static erts_smp_mtx_t erts_timeofday_mtx; static erts_smp_mtx_t erts_get_time_mtx; @@ -57,6 +59,7 @@ static int time_sup_initialized = 0; #define ERTS_MONOTONIC_TIME_TERA \ (ERTS_MONOTONIC_TIME_GIGA*ERTS_MONOTONIC_TIME_KILO) +static void init_time_napi(void); static void schedule_send_time_offset_changed_notifications(ErtsMonotonicTime new_offset); @@ -948,6 +951,8 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) ErtsMonotonicTime abs_native_offset, native_offset; #endif + init_time_napi(); + erts_hl_timer_init(); ASSERT(ERTS_MONOTONIC_TIME_MIN < ERTS_MONOTONIC_TIME_MAX); @@ -2197,6 +2202,146 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto return ret; } + +/* + * Time Native API (drivers and NIFs) + */ + +#define ERTS_NAPI_TIME_ERROR ((ErtsMonotonicTime) ERTS_NAPI_TIME_ERROR__) + +static void +init_time_napi(void) +{ + /* Verify that time native api constants are as expected... */ + + ASSERT(sizeof(ErtsMonotonicTime) == sizeof(ErlDrvTime)); + ASSERT(ERL_DRV_TIME_ERROR == (ErlDrvTime) ERTS_NAPI_TIME_ERROR); + ASSERT(ERL_DRV_TIME_ERROR < (ErlDrvTime) 0); + ASSERT(ERTS_NAPI_SEC__ == (int) ERL_DRV_SEC); + ASSERT(ERTS_NAPI_MSEC__ == (int) ERL_DRV_MSEC); + ASSERT(ERTS_NAPI_USEC__ == (int) ERL_DRV_USEC); + ASSERT(ERTS_NAPI_NSEC__ == (int) ERL_DRV_NSEC); + + ASSERT(sizeof(ErtsMonotonicTime) == sizeof(ErlNifTime)); + ASSERT(ERL_NIF_TIME_ERROR == (ErlNifTime) ERTS_NAPI_TIME_ERROR); + ASSERT(ERL_NIF_TIME_ERROR < (ErlNifTime) 0); + ASSERT(ERTS_NAPI_SEC__ == (int) ERL_NIF_SEC); + ASSERT(ERTS_NAPI_MSEC__ == (int) ERL_NIF_MSEC); + ASSERT(ERTS_NAPI_USEC__ == (int) ERL_NIF_USEC); + ASSERT(ERTS_NAPI_NSEC__ == (int) ERL_NIF_NSEC); +} + +ErtsMonotonicTime +erts_napi_monotonic_time(int time_unit) +{ + ErtsSchedulerData *esdp; + ErtsMonotonicTime mtime; + + /* At least for now only allow schedulers to do this... */ + esdp = erts_get_scheduler_data(); + if (!esdp) + return ERTS_NAPI_TIME_ERROR; + + mtime = time_sup.r.o.get_time(); + update_last_mtime(esdp, mtime); + + switch (time_unit) { + case ERTS_NAPI_SEC__: + mtime = ERTS_MONOTONIC_TO_SEC(mtime); + mtime += ERTS_MONOTONIC_OFFSET_SEC; + break; + case ERTS_NAPI_MSEC__: + mtime = ERTS_MONOTONIC_TO_MSEC(mtime); + mtime += ERTS_MONOTONIC_OFFSET_MSEC; + break; + case ERTS_NAPI_USEC__: + mtime = ERTS_MONOTONIC_TO_USEC(mtime); + mtime += ERTS_MONOTONIC_OFFSET_USEC; + break; + case ERTS_NAPI_NSEC__: + mtime = ERTS_MONOTONIC_TO_NSEC(mtime); + mtime += ERTS_MONOTONIC_OFFSET_NSEC; + break; + default: + return ERTS_NAPI_TIME_ERROR; + } + + return mtime; +} + +ErtsMonotonicTime +erts_napi_time_offset(int time_unit) +{ + ErtsSchedulerData *esdp; + ErtsSystemTime offs; + + /* At least for now only allow schedulers to do this... */ + esdp = erts_get_scheduler_data(); + if (!esdp) + return ERTS_NAPI_TIME_ERROR; + + offs = get_time_offset(); + switch (time_unit) { + case ERTS_NAPI_SEC__: + offs = ERTS_MONOTONIC_TO_SEC(offs); + offs -= ERTS_MONOTONIC_OFFSET_SEC; + break; + case ERTS_NAPI_MSEC__: + offs = ERTS_MONOTONIC_TO_MSEC(offs); + offs -= ERTS_MONOTONIC_OFFSET_MSEC; + break; + case ERTS_NAPI_USEC__: + offs = ERTS_MONOTONIC_TO_USEC(offs); + offs -= ERTS_MONOTONIC_OFFSET_USEC; + break; + case ERTS_NAPI_NSEC__: + offs = ERTS_MONOTONIC_TO_NSEC(offs); + offs -= ERTS_MONOTONIC_OFFSET_NSEC; + break; + default: + return ERTS_NAPI_TIME_ERROR; + } + return offs; +} + +ErtsMonotonicTime +erts_napi_convert_time_unit(ErtsMonotonicTime val, int from, int to) +{ + ErtsMonotonicTime ffreq, tfreq, denom; + /* + * Convertion between time units using floor function. + * + * Note that this needs to work also for negative + * values. Ordinary integer division on a negative + * value will give ceiling... + */ + + switch ((int) from) { + case ERTS_NAPI_SEC__: ffreq = 1; break; + case ERTS_NAPI_MSEC__: ffreq = 1000; break; + case ERTS_NAPI_USEC__: ffreq = 1000*1000; break; + case ERTS_NAPI_NSEC__: ffreq = 1000*1000*1000; break; + default: return ERTS_NAPI_TIME_ERROR; + } + + switch ((int) to) { + case ERTS_NAPI_SEC__: tfreq = 1; break; + case ERTS_NAPI_MSEC__: tfreq = 1000; break; + case ERTS_NAPI_USEC__: tfreq = 1000*1000; break; + case ERTS_NAPI_NSEC__: tfreq = 1000*1000*1000; break; + default: return ERTS_NAPI_TIME_ERROR; + } + + if (tfreq >= ffreq) + return val * (tfreq / ffreq); + + denom = ffreq / tfreq; + if (val >= 0) + return val / denom; + + return (val - (denom - 1)) / denom; +} + /* Built in functions */ BIF_RETTYPE monotonic_time_0(BIF_ALIST_0) diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index c64c8802b9..2bd31ee97e 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -50,6 +50,7 @@ #include "erl_map.h" #include "erl_bif_unique.h" #include "erl_hl_timer.h" +#include "erl_time.h" extern ErlDrvEntry fd_driver_entry; #ifndef __OSE__ @@ -6762,6 +6763,28 @@ driver_get_now(ErlDrvNowData *now_data) return 0; } +ErlDrvTime +erl_drv_monotonic_time(ErlDrvTimeUnit time_unit) +{ + return (ErlDrvTime) erts_napi_monotonic_time((int) time_unit); +} + +ErlDrvTime +erl_drv_time_offset(ErlDrvTimeUnit time_unit) +{ + return (ErlDrvTime) erts_napi_time_offset((int) time_unit); +} + +ErlDrvTime +erl_drv_convert_time_unit(ErlDrvTime val, + ErlDrvTimeUnit from, + ErlDrvTimeUnit to) +{ + return (ErlDrvTime) erts_napi_convert_time_unit((ErtsMonotonicTime) val, + (int) from, + (int) to); +} + static void ref_to_driver_monitor(Eterm ref, ErlDrvMonitor *mon) { RefThing *refp; diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h index baac7c903e..9c699fdba0 100644 --- a/erts/emulator/sys/win32/erl_win_dyn_driver.h +++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h @@ -103,6 +103,11 @@ WDD_TYPEDEF(ErlDrvSInt, driver_pdl_inc_refc, (ErlDrvPDL)); WDD_TYPEDEF(ErlDrvSInt, driver_pdl_dec_refc, (ErlDrvPDL)); WDD_TYPEDEF(void, driver_system_info, (ErlDrvSysInfo *, size_t)); WDD_TYPEDEF(int, driver_get_now, (ErlDrvNowData *)); +WDD_TYPEDEF(ErlDrvTime, erl_drv_monotonic_time, (ErlDrvTimeUnit)); +WDD_TYPEDEF(ErlDrvTime, erl_drv_time_offset, (ErlDrvTimeUnit)); +WDD_TYPEDEF(ErlDrvTime, erl_drv_convert_time_unit, (ErlDrvTime, + ErlDrvTimeUnit, + ErlDrvTimeUnit)); WDD_TYPEDEF(int, driver_monitor_process, (ErlDrvPort port, ErlDrvTermData process, ErlDrvMonitor *monitor)); @@ -217,6 +222,9 @@ typedef struct { WDD_FTYPE(driver_pdl_dec_refc) *driver_pdl_dec_refc; WDD_FTYPE(driver_system_info) *driver_system_info; WDD_FTYPE(driver_get_now) *driver_get_now; + WDD_FTYPE(erl_drv_monotonic_time) *erl_drv_monotonic_time; + WDD_FTYPE(erl_drv_time_offset) *erl_drv_time_offset; + WDD_FTYPE(erl_drv_convert_time_unit) *erl_drv_convert_time_unit; WDD_FTYPE(driver_monitor_process) *driver_monitor_process; WDD_FTYPE(driver_demonitor_process) *driver_demonitor_process; WDD_FTYPE(driver_get_monitored_process) *driver_get_monitored_process; @@ -328,6 +336,9 @@ extern TWinDynDriverCallbacks WinDynDriverCallbacks; #define driver_pdl_dec_refc (WinDynDriverCallbacks.driver_pdl_dec_refc) #define driver_system_info (WinDynDriverCallbacks.driver_system_info) #define driver_get_now (WinDynDriverCallbacks.driver_get_now) +#define erl_drv_monotonic_time (WinDynDriverCallbacks.erl_drv_monotonic_time) +#define erl_drv_time_offset (WinDynDriverCallbacks.erl_drv_time_offset) +#define erl_drv_convert_time_unit (WinDynDriverCallbacks.erl_drv_convert_time_unit) #define driver_monitor_process \ (WinDynDriverCallbacks.driver_monitor_process) #define driver_demonitor_process \ @@ -463,6 +474,9 @@ do { \ ((W).driver_pdl_dec_refc) = driver_pdl_dec_refc; \ ((W).driver_system_info) = driver_system_info; \ ((W).driver_get_now) = driver_get_now; \ +((W).erl_drv_monotonic_time) = erl_drv_monotonic_time; \ +((W).erl_drv_time_offset) = erl_drv_time_offset; \ +((W).erl_drv_convert_time_unit) = erl_drv_convert_time_unit; \ ((W).driver_monitor_process) = driver_monitor_process; \ ((W).driver_demonitor_process) = driver_demonitor_process; \ ((W).driver_get_monitored_process) = driver_get_monitored_process; \ diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index af2b955184..3d478654b1 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -42,7 +42,8 @@ otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1, dirty_nif_exception/1, call_dirty_nif_exception/1, nif_schedule/1, nif_exception/1, call_nif_exception/1, - nif_nan_and_inf/1, nif_atom_too_long/1 + nif_nan_and_inf/1, nif_atom_too_long/1, + nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1 ]). -export([many_args_100/100]). @@ -72,7 +73,8 @@ all() -> otp_9828, otp_9668, consume_timeslice, nif_schedule, dirty_nif, dirty_nif_send, dirty_nif_exception, - nif_exception, nif_nan_and_inf, nif_atom_too_long + nif_exception, nif_nan_and_inf, nif_atom_too_long, + nif_monotonic_time, nif_time_offset, nif_convert_time_unit ]. groups() -> @@ -1783,6 +1785,148 @@ nif_raise_exceptions(NifFunc) -> end end, ok, ExcTerms). +-define(ERL_NIF_TIME_ERROR, -9223372036854775808). +-define(TIME_UNITS, [seconds, milli_seconds, micro_seconds, nano_seconds]). + +nif_monotonic_time(Config) -> + ?ERL_NIF_TIME_ERROR = monotonic_time(invalid_time_unit), + mtime_loop(1000000). + +mtime_loop(0) -> + ok; +mtime_loop(N) -> + chk_mtime(?TIME_UNITS), + mtime_loop(N-1). + +chk_mtime([]) -> + ok; +chk_mtime([TU|TUs]) -> + A = erlang:monotonic_time(TU), + B = monotonic_time(TU), + C = erlang:monotonic_time(TU), + try + true = A =< B, + true = B =< C + catch + _ : _ -> + ?t:fail({monotonic_time_missmatch, TU, A, B, C}) + end, + chk_mtime(TUs). + +nif_time_offset(Config) -> + ?ERL_NIF_TIME_ERROR = time_offset(invalid_time_unit), + toffs_loop(1000000). + +toffs_loop(0) -> + ok; +toffs_loop(N) -> + chk_toffs(?TIME_UNITS), + toffs_loop(N-1). + +chk_toffs([]) -> + ok; +chk_toffs([TU|TUs]) -> + TO = erlang:time_offset(TU), + NifTO = time_offset(TU), + case TO =:= NifTO of + true -> + ok; + false -> + case erlang:system_info(time_warp_mode) of + no_time_warp -> + ?t:fail({time_offset_mismatch, TU, TO, NifTO}); + _ -> + %% Most frequent time offset change + %% is currently only every 15:th + %% second so this should currently + %% work... + NTO = erlang:time_offset(TU), + case NifTO =:= NTO of + true -> + ok; + false -> + ?t:fail({time_offset_mismatch, TU, TO, NifTO, NTO}) + end + end + end, + chk_toffs(TUs). + +nif_convert_time_unit(Config) -> + ?ERL_NIF_TIME_ERROR = convert_time_unit(0, seconds, invalid_time_unit), + ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, seconds), + ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, invalid_time_unit), + lists:foreach(fun (Offset) -> + lists:foreach(fun (Diff) -> + chk_ctu(Diff+(Offset*1000*1000*1000)) + end, + [999999999999, + 99999999999, + 9999999999, + 999999999, + 99999999, + 9999999, + 999999, + 99999, + 999, + 99, + 9, + 1, + 11, + 101, + 1001, + 10001, + 100001, + 1000001, + 10000001, + 100000001, + 1000000001, + 100000000001, + 1000000000001, + 5, + 50, + 500, + 5000, + 50000, + 500000, + 5000000, + 50000000, + 500000000, + 5000000000, + 50000000000, + 500000000000]) + end, + [-4711, -1000, -475, -5, -4, -3, -2, -1, 0, + 1, 2, 3, 4, 5, 475, 1000, 4711]), + ctu_loop(1000000). + +ctu_loop(0) -> + ok; +ctu_loop(N) -> + chk_ctu(erlang:monotonic_time(nano_seconds)), + ctu_loop(N-1). + +chk_ctu(Time) -> + chk_ctu(Time, ?TIME_UNITS). + +chk_ctu(_Time, []) -> + ok; +chk_ctu(Time, [FromTU|FromTUs]) -> + chk_ctu(Time, FromTU, ?TIME_UNITS), + chk_ctu(Time, FromTUs). + +chk_ctu(_Time, _FromTU, []) -> + ok; +chk_ctu(Time, FromTU, [ToTU|ToTUs]) -> + T = erlang:convert_time_unit(Time, nano_seconds, FromTU), + TE = erlang:convert_time_unit(T, FromTU, ToTU), + TN = convert_time_unit(T, FromTU, ToTU), + case TE =:= TN of + false -> + ?t:fail({conversion_mismatch, FromTU, T, ToTU, TE, TN}); + true -> + chk_ctu(Time, FromTU, ToTUs) + end. + %% The NIFs: lib_version() -> undefined. call_history() -> ?nif_stub. @@ -1852,6 +1996,11 @@ make_map_remove_nif(_,_) -> ?nif_stub. maps_from_list_nif(_) -> ?nif_stub. sorted_list_from_maps_nif(_) -> ?nif_stub. +%% Time +monotonic_time(_) -> ?nif_stub. +time_offset(_) -> ?nif_stub. +convert_time_unit(_,_,_) -> ?nif_stub. + nif_stub_error(Line) -> exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 98e1efe18f..8ebce4fef4 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -34,6 +34,10 @@ static ERL_NIF_TERM atom_self; static ERL_NIF_TERM atom_ok; static ERL_NIF_TERM atom_join; static ERL_NIF_TERM atom_binary_resource_type; +static ERL_NIF_TERM atom_seconds; +static ERL_NIF_TERM atom_milli_seconds; +static ERL_NIF_TERM atom_micro_seconds; +static ERL_NIF_TERM atom_nano_seconds; typedef struct @@ -138,6 +142,10 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_ok = enif_make_atom(env,"ok"); atom_join = enif_make_atom(env,"join"); atom_binary_resource_type = enif_make_atom(env,"binary_resource_type"); + atom_seconds = enif_make_atom(env,"seconds"); + atom_milli_seconds = enif_make_atom(env,"milli_seconds"); + atom_micro_seconds = enif_make_atom(env,"micro_seconds"); + atom_nano_seconds = enif_make_atom(env,"nano_seconds"); *priv_data = data; return 0; @@ -1885,6 +1893,87 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER return enif_make_tuple2(env, list_f, list_b); } + +static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifTimeUnit time_unit; + + if (argc != 1) + return atom_false; + + if (enif_compare(argv[0], atom_seconds) == 0) + time_unit = ERL_NIF_SEC; + else if (enif_compare(argv[0], atom_milli_seconds) == 0) + time_unit = ERL_NIF_MSEC; + else if (enif_compare(argv[0], atom_micro_seconds) == 0) + time_unit = ERL_NIF_USEC; + else if (enif_compare(argv[0], atom_nano_seconds) == 0) + time_unit = ERL_NIF_NSEC; + else + time_unit = 4711; /* invalid time unit */ + + return enif_make_int64(env, enif_monotonic_time(time_unit)); +} + +static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifTimeUnit time_unit; + + if (argc != 1) + return atom_false; + + if (enif_compare(argv[0], atom_seconds) == 0) + time_unit = ERL_NIF_SEC; + else if (enif_compare(argv[0], atom_milli_seconds) == 0) + time_unit = ERL_NIF_MSEC; + else if (enif_compare(argv[0], atom_micro_seconds) == 0) + time_unit = ERL_NIF_USEC; + else if (enif_compare(argv[0], atom_nano_seconds) == 0) + time_unit = ERL_NIF_NSEC; + else + time_unit = 4711; /* invalid time unit */ + return enif_make_int64(env, enif_time_offset(time_unit)); +} + +static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifSInt64 i64; + ErlNifTime val; + ErlNifTimeUnit from, to; + + if (argc != 3) + return atom_false; + + if (!enif_get_int64(env, argv[0], &i64)) + return enif_make_badarg(env); + + val = (ErlNifTime) i64; + + if (enif_compare(argv[1], atom_seconds) == 0) + from = ERL_NIF_SEC; + else if (enif_compare(argv[1], atom_milli_seconds) == 0) + from = ERL_NIF_MSEC; + else if (enif_compare(argv[1], atom_micro_seconds) == 0) + from = ERL_NIF_USEC; + else if (enif_compare(argv[1], atom_nano_seconds) == 0) + from = ERL_NIF_NSEC; + else + from = 4711; /* invalid time unit */ + + if (enif_compare(argv[2], atom_seconds) == 0) + to = ERL_NIF_SEC; + else if (enif_compare(argv[2], atom_milli_seconds) == 0) + to = ERL_NIF_MSEC; + else if (enif_compare(argv[2], atom_micro_seconds) == 0) + to = ERL_NIF_USEC; + else if (enif_compare(argv[2], atom_nano_seconds) == 0) + to = ERL_NIF_NSEC; + else + to = 4711; /* invalid time unit */ + + return enif_make_int64(env, enif_convert_time_unit(val, from, to)); +} + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -1954,7 +2043,10 @@ static ErlNifFunc nif_funcs[] = {"make_map_update_nif", 3, make_map_update_nif}, {"make_map_remove_nif", 2, make_map_remove_nif}, {"maps_from_list_nif", 1, maps_from_list_nif}, - {"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif} + {"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif}, + {"monotonic_time", 1, monotonic_time}, + {"time_offset", 1, time_offset}, + {"convert_time_unit", 3, convert_time_unit} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) -- cgit v1.2.3 From 66a80a7ab735a22249ffbbb7c88eccebba906194 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 21 Jan 2016 19:53:59 +0100 Subject: erts: Add checks for thread safe allocation Assert thread unsafe allocator is only created on non-smp and only called by the main thread. Removed test of unsafe allocator in custom thread. --- erts/emulator/beam/erl_alloc.c | 12 ++++++++++-- erts/emulator/beam/erl_alloc_util.c | 5 +++++ erts/emulator/beam/erl_init.c | 11 ++++++----- erts/emulator/beam/global.h | 3 +++ erts/emulator/test/alloc_SUITE_data/threads.c | 17 +---------------- 5 files changed, 25 insertions(+), 23 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 5544712e8d..e7523ac989 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -968,6 +968,10 @@ set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init, int ncpu) else #endif { +#ifdef ERTS_SMP + erl_exit(ERTS_ABORT_EXIT, "%salloc is not thread safe\n", + init->init.util.name_prefix); +#else af->alloc = erts_alcu_alloc; if (init->init.util.fix_type_size) af->realloc = erts_realloc_fixed_size; @@ -976,6 +980,7 @@ set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init, int ncpu) else af->realloc = erts_alcu_realloc; af->free = erts_alcu_free; +#endif } af->extra = NULL; ai->alloc_util = 1; @@ -3402,8 +3407,11 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3) init.enable = 1; init.atype = GOODFIT; init.init.util.name_prefix = (char *) a1; - init.init.util.ts = a2 ? 1 : 0; - +#ifdef ERTS_SMP + init.init.util.ts = 1; +#else + init.init.util.ts = a2 ? 1 : 0; +#endif if ((char **) a3) { char **argv = (char **) a3; int i = 0; diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index eedfd1e13d..3230b6ef34 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -5337,6 +5337,11 @@ do_erts_alcu_alloc(ErtsAlcType_t type, void *extra, Uint size) void *erts_alcu_alloc(ErtsAlcType_t type, void *extra, Uint size) { void *res; +#ifdef ERTS_SMP + ASSERT(!"This is not thread safe"); +#elif defined(USE_THREADS) + ASSERT(erts_equal_tids(erts_main_thread, erts_thr_self())); +#endif res = do_erts_alcu_alloc(type, extra, size); DEBUG_CHECK_ALIGNMENT(res); return res; diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 42aca726bf..39957eb58b 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -152,7 +152,7 @@ volatile int erts_writing_erl_crash_dump = 0; int erts_initialized = 0; #if defined(USE_THREADS) && !defined(ERTS_SMP) -static erts_tid_t main_thread; +erts_tid_t erts_main_thread; #endif int erts_use_sender_punish; @@ -744,6 +744,10 @@ early_init(int *argc, char **argv) /* char envbuf[21]; /* enough for any 64-bit integer */ size_t envbufsz; +#if defined(USE_THREADS) && !defined(ERTS_SMP) + erts_main_thread = erts_thr_self(); +#endif + erts_save_emu_args(*argc, argv); erts_sched_compact_load = 1; @@ -797,9 +801,6 @@ early_init(int *argc, char **argv) /* (erts_aint32_t) ((Uint16) -1)); erts_pre_init_process(); -#if defined(USE_THREADS) && !defined(ERTS_SMP) - main_thread = erts_thr_self(); -#endif /* * We need to know the number of schedulers to use before we @@ -2285,7 +2286,7 @@ system_cleanup(int flush_async) if (!flush_async || !erts_initialized #if defined(USE_THREADS) && !defined(ERTS_SMP) - || !erts_equal_tids(main_thread, erts_thr_self()) + || !erts_equal_tids(erts_main_thread, erts_thr_self()) #endif ) return; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 3f5925765d..ad9fdfc878 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1168,6 +1168,9 @@ extern ErtsModifiedTimings erts_modified_timings[]; extern int erts_no_line_info; extern Eterm erts_error_logger_warnings; extern int erts_initialized; +#if defined(USE_THREADS) && !defined(ERTS_SMP) +extern erts_tid_t erts_main_thread; +#endif extern int erts_compat_rel; extern int erts_use_sender_punish; void erts_short_init(void); diff --git a/erts/emulator/test/alloc_SUITE_data/threads.c b/erts/emulator/test/alloc_SUITE_data/threads.c index 2f5f841e3d..44d982b6c7 100644 --- a/erts/emulator/test/alloc_SUITE_data/threads.c +++ b/erts/emulator/test/alloc_SUITE_data/threads.c @@ -96,16 +96,11 @@ static void fail(int t_no, char *frmt, ...) exit_thread(t_no, 0); } -static Allctr_t *alloc_not_ts = NULL; static Allctr_t *alloc_ts_1 = NULL; static Allctr_t *alloc_ts_2 = NULL; static void stop_allocators(void) { - if (alloc_not_ts) { - STOP_ALC(alloc_not_ts); - alloc_not_ts = NULL; - } if (alloc_ts_1) { STOP_ALC(alloc_ts_1); alloc_ts_1 = NULL; @@ -155,7 +150,6 @@ testcase_run(TestCaseState_t *tcs) if (!IS_THREADS_ENABLED) testcase_skipped(tcs, "Threads not enabled"); - alloc_not_ts = NULL; alloc_ts_1 = NULL; alloc_ts_2 = NULL; @@ -163,9 +157,6 @@ testcase_run(TestCaseState_t *tcs) sprintf(sbct_buf, "%d", SBC_THRESHOLD/1024); - memcpy((void *) argv, argv_org, sizeof(argv_org)); - alloc_not_ts = START_ALC("threads_not_ts", 0, argv); - ASSERT(tcs, alloc_not_ts); memcpy((void *) argv, argv_org, sizeof(argv_org)); alloc_ts_1 = START_ALC("threads_ts_1", 1, argv); ASSERT(tcs, alloc_ts_1); @@ -173,7 +164,6 @@ testcase_run(TestCaseState_t *tcs) alloc_ts_2 = START_ALC("threads_ts_2", 1, argv); ASSERT(tcs, alloc_ts_2); - ASSERT(tcs, !IS_ALLOC_THREAD_SAFE(alloc_not_ts)); ASSERT(tcs, IS_ALLOC_THREAD_SAFE(alloc_ts_1)); ASSERT(tcs, IS_ALLOC_THREAD_SAFE(alloc_ts_2)); @@ -190,12 +180,7 @@ testcase_run(TestCaseState_t *tcs) threads[i].arg.no_ops_per_bl = NO_OF_OPS_PER_BL; - if (i == 1) { - alc = "threads_not_ts"; - threads[i].arg.no_ops_per_bl *= 2; - threads[i].arg.a = alloc_not_ts; - } - else if (i % 2 == 0) { + if (i % 2 == 0) { alc = "threads_ts_1"; threads[i].arg.a = alloc_ts_1; } -- cgit v1.2.3 From e9d6797e15e687828e5ef0d33fb790181d657779 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 21 Jan 2016 19:54:46 +0100 Subject: erts: Fix faulty assert for non-smp --- erts/emulator/beam/erl_message.c | 2 +- erts/emulator/beam/sys.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index b3e74e3e6a..88efb2c59f 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1504,7 +1504,7 @@ erts_factory_message_create(ErtsHeapFactory* factory, } if (on_heap) { - ASSERT(*proc_locksp & ERTS_PROC_LOCK_MAIN); + ERTS_SMP_ASSERT(*proc_locksp & ERTS_PROC_LOCK_MAIN); ASSERT(ohp == &proc->off_heap); factory->mode = FACTORY_HALLOC; factory->p = proc; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 53f8313daa..37fcfd1c52 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -196,6 +196,12 @@ __decl_noreturn void __noreturn erl_assert_error(const char* expr, const char *f # define ASSERT(e) ((void) 1) #endif +#ifdef ERTS_SMP +# define ERTS_SMP_ASSERT(e) ASSERT(e) +#else +# define ERTS_SMP_ASSERT(e) ((void)1) +#endif + /* ERTS_UNDEF can be used to silence false warnings about * "variable may be used uninitialized" while keeping the variable * marked as undefined by valgrind. -- cgit v1.2.3 From 89098ea8beb9e60faa59c3f2ea9ffc918ef87ae8 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 22 Jan 2016 10:24:13 +0100 Subject: Fix testcase --- erts/emulator/test/statistics_SUITE.erl | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index 53c9ba8715..a6305d453c 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -440,6 +440,10 @@ run_queues_lengths_active_tasks(Config) -> SO = erlang:system_flag(schedulers_online, 1), + %% Give newly suspended schedulers some time to + %% migrate away work from their run queues... + receive after 1000 -> ok end, + TRQLs1 = statistics(total_run_queue_lengths), TATs1 = statistics(total_active_tasks), true = TRQLs1 >= 10, -- cgit v1.2.3 From 0236a875929729eca1933cbb854267f584734b26 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Thu, 21 Jan 2016 17:26:56 +0100 Subject: Moved do_list_to_integer from bif.c to big.c --- erts/emulator/beam/bif.c | 157 ----------------------------------------------- erts/emulator/beam/big.c | 153 +++++++++++++++++++++++++++++++++++++++++++++ erts/emulator/beam/big.h | 8 +++ 3 files changed, 161 insertions(+), 157 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index bb9165cd79..d519216fbd 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2907,163 +2907,6 @@ BIF_RETTYPE integer_to_list_1(BIF_ALIST_1) /* convert a list of ascii ascii integer value to an integer */ -#define LTI_BAD_STRUCTURE 0 -#define LTI_NO_INTEGER 1 -#define LTI_SOME_INTEGER 2 -#define LTI_ALL_INTEGER 3 - -static int do_list_to_integer(Process *p, Eterm orig_list, - Eterm *integer, Eterm *rest) -{ - Sint i = 0; - Uint ui = 0; - int skip = 0; - int neg = 0; - Sint n = 0; - int m; - int lg2; - Eterm res; - Eterm* hp; - Eterm *hp_end; - Eterm lst = orig_list; - Eterm tail = lst; - int error_res = LTI_BAD_STRUCTURE; - - if (is_nil(lst)) { - error_res = LTI_NO_INTEGER; - error: - *rest = tail; - *integer = make_small(0); - return error_res; - } - if (is_not_list(lst)) - goto error; - - /* if first char is a '-' then it is a negative integer */ - if (CAR(list_val(lst)) == make_small('-')) { - neg = 1; - skip = 1; - lst = CDR(list_val(lst)); - if (is_not_list(lst)) { - tail = lst; - error_res = LTI_NO_INTEGER; - goto error; - } - } else if (CAR(list_val(lst)) == make_small('+')) { - /* ignore plus */ - skip = 1; - lst = CDR(list_val(lst)); - if (is_not_list(lst)) { - tail = lst; - error_res = LTI_NO_INTEGER; - goto error; - } - } - - /* Calculate size and do type check */ - - while(1) { - if (is_not_small(CAR(list_val(lst)))) { - break; - } - if (unsigned_val(CAR(list_val(lst))) < '0' || - unsigned_val(CAR(list_val(lst))) > '9') { - break; - } - ui = ui * 10; - ui = ui + unsigned_val(CAR(list_val(lst))) - '0'; - n++; - lst = CDR(list_val(lst)); - if (is_nil(lst)) { - break; - } - if (is_not_list(lst)) { - break; - } - } - - tail = lst; - if (!n) { - error_res = LTI_NO_INTEGER; - goto error; - } - - - /* If n <= 8 then we know it's a small int - ** since 2^27 = 134217728. If n > 8 then we must - ** construct a bignum and let that routine do the checking - */ - - if (n <= SMALL_DIGITS) { /* It must be small */ - if (neg) i = -(Sint)ui; - else i = (Sint)ui; - res = make_small(i); - } else { - /* Convert from log10 to log2 by multiplying with 1/log10(2)=3.3219 - which we round up to (3 + 1/3) */ - lg2 = (n+1)*3 + (n+1)/3 + 1; - m = (lg2+D_EXP-1)/D_EXP; /* number of digits */ - m = BIG_NEED_SIZE(m); /* number of words + thing */ - - hp = HAlloc(p, m); - hp_end = hp + m; - - lst = orig_list; - if (skip) - lst = CDR(list_val(lst)); - - /* load first digits (at least one digit) */ - if ((i = (n % D_DECIMAL_EXP)) == 0) - i = D_DECIMAL_EXP; - n -= i; - m = 0; - while(i--) { - m = 10*m + (unsigned_val(CAR(list_val(lst))) - '0'); - lst = CDR(list_val(lst)); - } - res = small_to_big(m, hp); /* load first digits */ - - while(n) { - i = D_DECIMAL_EXP; - n -= D_DECIMAL_EXP; - m = 0; - while(i--) { - m = 10*m + (unsigned_val(CAR(list_val(lst))) - '0'); - lst = CDR(list_val(lst)); - } - if (is_small(res)) - res = small_to_big(signed_val(res), hp); - res = big_times_small(res, D_DECIMAL_BASE, hp); - if (is_small(res)) - res = small_to_big(signed_val(res), hp); - res = big_plus_small(res, m, hp); - } - - if (neg) { - if (is_small(res)) - res = make_small(-signed_val(res)); - else { - Uint *big = big_val(res); /* point to thing */ - *big = bignum_header_neg(*big); - } - } - - if (is_not_small(res)) { - res = big_plus_small(res, 0, hp); /* includes conversion to small */ - - if (is_not_small(res)) { - hp += (big_arity(res)+1); - } - } - HRelease(p,hp_end,hp); - } - *integer = res; - *rest = tail; - if (tail != NIL) { - return LTI_SOME_INTEGER; - } - return LTI_ALL_INTEGER; -} BIF_RETTYPE string_to_integer_1(BIF_ALIST_1) { Eterm res; diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 02d37e24df..29e677d2e5 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -2732,3 +2732,156 @@ bytebuf_to_integer_1_done: return res; } + +int do_list_to_integer(Process *p, Eterm orig_list, + Eterm *integer, Eterm *rest) +{ + Sint i = 0; + Uint ui = 0; + int skip = 0; + int neg = 0; + Sint n = 0; + int m; + int lg2; + Eterm res; + Eterm* hp; + Eterm *hp_end; + Eterm lst = orig_list; + Eterm tail = lst; + int error_res = LTI_BAD_STRUCTURE; + + if (is_nil(lst)) { + error_res = LTI_NO_INTEGER; + error: + *rest = tail; + *integer = make_small(0); + return error_res; + } + if (is_not_list(lst)) + goto error; + + /* if first char is a '-' then it is a negative integer */ + if (CAR(list_val(lst)) == make_small('-')) { + neg = 1; + skip = 1; + lst = CDR(list_val(lst)); + if (is_not_list(lst)) { + tail = lst; + error_res = LTI_NO_INTEGER; + goto error; + } + } else if (CAR(list_val(lst)) == make_small('+')) { + /* ignore plus */ + skip = 1; + lst = CDR(list_val(lst)); + if (is_not_list(lst)) { + tail = lst; + error_res = LTI_NO_INTEGER; + goto error; + } + } + + /* Calculate size and do type check */ + + while(1) { + if (is_not_small(CAR(list_val(lst)))) { + break; + } + if (unsigned_val(CAR(list_val(lst))) < '0' || + unsigned_val(CAR(list_val(lst))) > '9') { + break; + } + ui = ui * 10; + ui = ui + unsigned_val(CAR(list_val(lst))) - '0'; + n++; + lst = CDR(list_val(lst)); + if (is_nil(lst)) { + break; + } + if (is_not_list(lst)) { + break; + } + } + + tail = lst; + if (!n) { + error_res = LTI_NO_INTEGER; + goto error; + } + + + /* If n <= 8 then we know it's a small int + ** since 2^27 = 134217728. If n > 8 then we must + ** construct a bignum and let that routine do the checking + */ + + if (n <= SMALL_DIGITS) { /* It must be small */ + if (neg) i = -(Sint)ui; + else i = (Sint)ui; + res = make_small(i); + } else { + /* Convert from log10 to log2 by multiplying with 1/log10(2)=3.3219 + which we round up to (3 + 1/3) */ + lg2 = (n+1)*3 + (n+1)/3 + 1; + m = (lg2+D_EXP-1)/D_EXP; /* number of digits */ + m = BIG_NEED_SIZE(m); /* number of words + thing */ + + hp = HAlloc(p, m); + hp_end = hp + m; + + lst = orig_list; + if (skip) + lst = CDR(list_val(lst)); + + /* load first digits (at least one digit) */ + if ((i = (n % D_DECIMAL_EXP)) == 0) + i = D_DECIMAL_EXP; + n -= i; + m = 0; + while(i--) { + m = 10*m + (unsigned_val(CAR(list_val(lst))) - '0'); + lst = CDR(list_val(lst)); + } + res = small_to_big(m, hp); /* load first digits */ + + while(n) { + i = D_DECIMAL_EXP; + n -= D_DECIMAL_EXP; + m = 0; + while(i--) { + m = 10*m + (unsigned_val(CAR(list_val(lst))) - '0'); + lst = CDR(list_val(lst)); + } + if (is_small(res)) + res = small_to_big(signed_val(res), hp); + res = big_times_small(res, D_DECIMAL_BASE, hp); + if (is_small(res)) + res = small_to_big(signed_val(res), hp); + res = big_plus_small(res, m, hp); + } + + if (neg) { + if (is_small(res)) + res = make_small(-signed_val(res)); + else { + Uint *big = big_val(res); /* point to thing */ + *big = bignum_header_neg(*big); + } + } + + if (is_not_small(res)) { + res = big_plus_small(res, 0, hp); /* includes conversion to small */ + + if (is_not_small(res)) { + hp += (big_arity(res)+1); + } + } + HRelease(p,hp_end,hp); + } + *integer = res; + *rest = tail; + if (tail != NIL) { + return LTI_SOME_INTEGER; + } + return LTI_ALL_INTEGER; +} diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 94f9bce10e..85807d6eea 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -173,4 +173,12 @@ Eterm erts_sint64_to_big(Sint64, Eterm **); Eterm erts_chars_to_integer(Process *, char*, Uint, const int); +#define LTI_BAD_STRUCTURE 0 +#define LTI_NO_INTEGER 1 +#define LTI_SOME_INTEGER 2 +#define LTI_ALL_INTEGER 3 + +int do_list_to_integer(Process *p, Eterm orig_list, + Eterm *integer, Eterm *rest); + #endif -- cgit v1.2.3 From 70e92b321feb10b5976c9a9f571a273155ebcf79 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 25 Jan 2016 15:45:13 +0100 Subject: erts: Fix harmless dialyzer warnings --- erts/preloaded/ebin/erts_code_purger.beam | Bin 8768 -> 8776 bytes erts/preloaded/ebin/erts_internal.beam | Bin 8536 -> 8536 bytes erts/preloaded/src/erts_code_purger.erl | 2 +- erts/preloaded/src/erts_internal.erl | 4 ++-- 4 files changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index 227d96d4c8..541bc7e7cc 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index d3d990519d..c19d59ab3f 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl index a64860bec8..d1e64342e0 100644 --- a/erts/preloaded/src/erts_code_purger.erl +++ b/erts/preloaded/src/erts_code_purger.erl @@ -31,7 +31,7 @@ start() -> loop(). loop() -> - receive + _ = receive {purge,Mod,From,Ref} when is_atom(Mod), is_pid(From) -> Res = do_purge(Mod), From ! {reply, purge, Res, Ref}; diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 84dedab930..2b15abe0fb 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -49,7 +49,7 @@ -export([is_system_process/1]). -%% Auto import name clash +%% Auto-import name clash -export([check_process_code/2]). %% @@ -196,7 +196,7 @@ port_info(_Result, _Item) -> -spec request_system_task(Pid, Prio, Request) -> 'ok' when Prio :: 'max' | 'high' | 'normal' | 'low', Request :: {'garbage_collect', term()} - | {'check_process_code', term(), module(), boolean()}, + | {'check_process_code', term(), module(), non_neg_integer()}, Pid :: pid(). request_system_task(_Pid, _Prio, _Request) -> -- cgit v1.2.3 From fa89f452f528321114593e0c79efba87fc4478d7 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 26 Jan 2016 10:24:04 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 49964 -> 49956 bytes erts/preloaded/ebin/erlang.beam | Bin 101268 -> 101288 bytes erts/preloaded/ebin/erts_code_purger.beam | Bin 8768 -> 8744 bytes erts/preloaded/ebin/erts_internal.beam | Bin 8536 -> 8512 bytes erts/preloaded/ebin/init.beam | Bin 44728 -> 44700 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1452 -> 1448 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1324 -> 1320 bytes erts/preloaded/ebin/prim_file.beam | Bin 44892 -> 44884 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72612 -> 72608 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23284 -> 23280 bytes erts/preloaded/ebin/zlib.beam | Bin 14160 -> 14156 bytes 11 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index f6057b45d4..f007880683 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index b6e38e4b5b..b99b28f3a1 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index 227d96d4c8..37e6b181d6 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index d3d990519d..2380930767 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index b6d1df7bbc..67159713f5 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 328520844d..fd80258d0e 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 8d6c1927fd..0e51a1bfc7 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 1221c513db..663f5d46e6 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index fa617f1f51..d8a8061bae 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 83e1e49974..1d69c2e583 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 8f654e3abf..2ffd839f8a 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From b721bebe663ca3abc747591b28726a367bdb9758 Mon Sep 17 00:00:00 2001 From: JP Date: Mon, 21 Sep 2015 22:52:46 +0000 Subject: erts: Fix sendfile:ing of large files on FreeBSD If the file was larger than the OS send buffer the call would fail before this patch. --- erts/emulator/drivers/unix/unix_efile.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index 06ba986044..61148dd58d 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -39,6 +39,11 @@ #ifdef HAVE_SYS_UIO_H #include #include +#if defined(HAVE_SENDFILE) && (defined(__FreeBSD__) || defined(__DragonFly__)) +/* Need to define __BSD_VISIBLE in order to expose prototype of sendfile */ +#define __BSD_VISIBLE 1 +#include +#endif #endif #if defined(HAVE_SENDFILE) && (defined(__linux__) || (defined(__sun) && defined(__SVR4))) #include -- cgit v1.2.3 From cd6903be0740db0c0061533cfb46729b43016316 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 27 Jan 2016 15:13:00 +0100 Subject: erts: When erts_alloc fails, the emulator no longer aborts --- erts/emulator/beam/erl_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index c3f4fe5a63..2a97069ac2 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1923,7 +1923,7 @@ erts_alc_fatal_error(int error, int func, ErtsAlcType_t n, ...) va_start(argp, n); size = va_arg(argp, Uint); va_end(argp); - erl_exit(1, + erl_exit(-1, "%s: Cannot %s %lu bytes of memory (of type \"%s\").\n", allctr_str, op, size, t_str); break; -- cgit v1.2.3 From 042677624b1d7b3f4c99be4e1483180e7fe8b2c0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 28 Jan 2016 15:09:35 +0100 Subject: erts: Fix bug concerning line information for hipe modules Line table was left uninitialized for hipe (stub) modules causing process_info(OtherPid, current_location) to crash. --- erts/emulator/beam/beam_load.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index b70e5b9a2d..a6dce2d1d2 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -6249,6 +6249,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) code[MI_LITERALS_END] = 0; code[MI_LITERALS_OFF_HEAP] = 0; code[MI_ON_LOAD_FUNCTION_PTR] = 0; + code[MI_LINE_TABLE] = 0; code[MI_MD5_PTR] = 0; ci = MI_FUNCTIONS + n + 1; -- cgit v1.2.3 From 9194e3beb7170c7a3db7391f005e6ab0aabd180d Mon Sep 17 00:00:00 2001 From: Michael Santos Date: Thu, 28 Jan 2016 11:11:45 -0500 Subject: erts/common: check for OOM on Windows Fix the command line tools to abort on allocation failures when run on Windows. Modify the malloc() wrapper to consistently return void* across all the tools. --- erts/etc/common/ct_run.c | 24 ++++++++++++++++++------ erts/etc/common/dialyzer.c | 24 ++++++++++++++++++------ erts/etc/common/erlc.c | 24 ++++++++++++++++++------ erts/etc/common/escript.c | 24 ++++++++++++++++++------ erts/etc/common/inet_gethost.c | 2 +- erts/etc/common/typer.c | 24 ++++++++++++++++++------ 6 files changed, 91 insertions(+), 31 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/ct_run.c b/erts/etc/common/ct_run.c index 548514ee6c..03ab22d1a1 100644 --- a/erts/etc/common/ct_run.c +++ b/erts/etc/common/ct_run.c @@ -81,13 +81,14 @@ static int eargc; /* Number of arguments in eargv. */ */ static void error(char* format, ...); -static char* emalloc(size_t size); +static void* emalloc(size_t size); static char* strsave(char* string); static void push_words(char* src); static int run_erlang(char* name, char** argv); static char* get_default_emulator(char* progname); #ifdef __WIN32__ static char* possibly_quote(char* arg); +static void* erealloc(void *p, size_t size); #endif /* @@ -141,10 +142,10 @@ int main(int argc, char** argv) int i; int len; /* Convert argv to utf8 */ - argv = malloc((argc+1) * sizeof(char*)); + argv = emalloc((argc+1) * sizeof(char*)); for (i=0; idata_present = CreateEvent(NULL, TRUE, FALSE,NULL); if (tmp->data_present == NULL) { free(tmp); diff --git a/erts/etc/common/typer.c b/erts/etc/common/typer.c index 0aa0996808..7f08050cc6 100644 --- a/erts/etc/common/typer.c +++ b/erts/etc/common/typer.c @@ -63,13 +63,14 @@ static int eargc; /* Number of arguments in eargv. */ */ static void error(char* format, ...); -static char* emalloc(size_t size); +static void* emalloc(size_t size); static char* strsave(char* string); static void push_words(char* src); static int run_erlang(char* name, char** argv); static char* get_default_emulator(char* progname); #ifdef __WIN32__ static char* possibly_quote(char* arg); +static void* erealloc(void *p, size_t size); #endif /* @@ -118,10 +119,10 @@ main(int argc, char** argv) int i; int len; /* Convert argv to utf8 */ - argv = malloc((argc+1) * sizeof(char*)); + argv = emalloc((argc+1) * sizeof(char*)); for (i=0; i Date: Fri, 29 Jan 2016 11:56:18 +0100 Subject: Remove faulty asserts --- erts/emulator/beam/erl_gc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 6839c24c25..06acece471 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -729,9 +729,7 @@ erts_garbage_collect_hibernate(Process* p) */ erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC); ErtsGcQuickSanityCheck(p); - ASSERT(p->mbuf == NULL); ASSERT(p->stop == p->hend); /* Stack must be empty. */ - ASSERT(!p->abandoned_heap); /* * Do it. -- cgit v1.2.3 From 18955a72ddf9190f9850cb6b143fd39ff8ab6168 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 31 Jan 2016 15:11:15 +0100 Subject: hipe_x86_signal: cleanups - remove glibc < 2.3 code - move glibc-2.2 comment to glibc >= 2.3 block - remove obsolete "only supports" comment - remove support for broken sigaltstack() in pre-2.4 Linux kernels --- erts/emulator/hipe/hipe_x86_signal.c | 80 +++--------------------------------- 1 file changed, 6 insertions(+), 74 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index b7dae88417..6887bdacbc 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -38,9 +38,6 @@ * * Our solution is to override the C library's signal handler setup * procedure with our own which enforces the SA_ONSTACK flag. - * - * XXX: This code only supports Linux with glibc-2.1 or above, - * and Solaris 8. */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -55,27 +52,6 @@ #include "hipe_signal.h" #if __GLIBC__ == 2 && (__GLIBC_MINOR__ >= 3) -/* See comment below for glibc 2.2. */ -#ifndef __USE_GNU -#define __USE_GNU /* to un-hide RTLD_NEXT */ -#endif -#include -static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); -#define init_done() (__next_sigaction != 0) -extern int __sigaction(int, const struct sigaction*, struct sigaction*); -#define __SIGACTION __sigaction -static void do_init(void) -{ - __next_sigaction = dlsym(RTLD_NEXT, "__sigaction"); - if (__next_sigaction != 0) - return; - perror("dlsym"); - abort(); -} -#define INIT() do { if (!init_done()) do_init(); } while (0) -#endif /* glibc 2.3 */ - -#if __GLIBC__ == 2 && (__GLIBC_MINOR__ == 2 /*|| __GLIBC_MINOR__ == 3*/) /* * __libc_sigaction() is the core routine. * Without libpthread, sigaction() and __sigaction() are both aliases @@ -100,16 +76,14 @@ static void do_init(void) * old BSD or SysV interfaces. * glibc's internal calls to __sigaction() appear to be mostly safe. * hipe_signal_init() fixes some unsafe ones, e.g. the SIGPROF handler. - * - * Tested with glibc-2.1.92 on RedHat 7.0, glibc-2.2.2 on RedHat 7.1, - * glibc-2.2.4 on RedHat 7.2, and glibc-2.2.5 on RedHat 7.3. */ -#if 0 -/* works with 2.2.5 and 2.2.4, but not 2.2.2 or 2.1.92 */ +#ifndef __USE_GNU #define __USE_GNU /* to un-hide RTLD_NEXT */ +#endif #include static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); #define init_done() (__next_sigaction != 0) +extern int __sigaction(int, const struct sigaction*, struct sigaction*); #define __SIGACTION __sigaction static void do_init(void) { @@ -120,45 +94,7 @@ static void do_init(void) abort(); } #define INIT() do { if (!init_done()) do_init(); } while (0) -#else -/* semi-works with all 2.2 versions so far */ -extern int __sigaction(int, const struct sigaction*, struct sigaction*); -#define __next_sigaction __sigaction /* pthreads-aware version */ -#undef __SIGACTION /* we can't override __sigaction() */ -#define INIT() do{}while(0) -#endif -#endif /* glibc 2.2 */ - -#if __GLIBC__ == 2 && __GLIBC_MINOR__ == 1 -/* - * __sigaction() is the core routine. - * Without libpthread, sigaction() is an alias for __sigaction(). - * libpthread redefines sigaction() as a non-trivial wrapper around - * __sigaction(). - * glibc has internal calls to both sigaction() and __sigaction(). - * - * Overriding __sigaction() would be ideal, but doing so breaks - * libpthread (threads hang). Instead we override sigaction() and - * use dlsym RTLD_NEXT to find glibc's version of sigaction(). - * glibc's internal calls to __sigaction() appear to be mostly safe. - * hipe_signal_init() fixes some unsafe ones, e.g. the SIGPROF handler. - * - * Tested with glibc-2.1.3 on RedHat 6.2. - */ -#include -static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); -#define init_done() (__next_sigaction != 0) -#undef __SIGACTION -static void do_init(void) -{ - __next_sigaction = dlsym(RTLD_NEXT, "sigaction"); - if (__next_sigaction != 0) - return; - perror("dlsym"); - abort(); -} -#define INIT() do { if (!init_done()) do_init(); } while (0) -#endif /* glibc 2.1 */ +#endif /* glibc >= 2.3 */ /* Is there no standard identifier for Darwin/MacOSX ? */ #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) @@ -339,12 +275,8 @@ static void hipe_sigaltstack(void *ss_sp) ss.ss_flags = SS_ONSTACK; ss.ss_size = SIGSTKSZ; if (sigaltstack(&ss, NULL) < 0) { - /* might be a broken pre-2.4 Linux kernel, try harder */ - ss.ss_flags = 0; - if (sigaltstack(&ss, NULL) < 0) { - perror("sigaltstack"); - abort(); - } + perror("sigaltstack"); + abort(); } } -- cgit v1.2.3 From 050480b2326ea3583231a0de02fb087bf51db82a Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 31 Jan 2016 15:13:21 +0100 Subject: hipe_x86_signal: cleanups - add block for NetBSD, define dummy INIT() macro - eliminate ifndef NetBSD around INIT() invocation --- erts/emulator/hipe/hipe_x86_signal.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index 6887bdacbc..741f570c50 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -192,6 +192,15 @@ static void do_init(void) #define INIT() do { if (!init_done()) do_init(); } while (0) #endif /* __FreeBSD__ */ +#if defined(__NetBSD__) +/* + * Note: This is only stub code to allow the build to succeed. + * Whether this actually provides the needed overrides for safe + * signal delivery or not is unknown. + */ +#define INIT() do { } while (0) +#endif /* __NetBSD__ */ + #if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sun__)) /* * Unknown libc -- assume musl. Note: musl deliberately does not provide a musl-specific @@ -243,6 +252,7 @@ static int my_sigaction(int signum, const struct sigaction *act, struct sigactio return __next_sigaction(signum, act, oldact); } #endif + /* * This overrides the C library's core sigaction() procedure, catching * all its internal calls. @@ -313,9 +323,7 @@ void hipe_signal_init(void) struct sigaction sa; int i; -#ifndef __NetBSD__ INIT(); -#endif hipe_sigaltstack_init(); -- cgit v1.2.3 From 97ea79864fc9d55fa3679597d4c2dbe8e518e06a Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 31 Jan 2016 15:14:09 +0100 Subject: hipe_x86_signal: cleanups - rename __SIGACTION to LIBC_SIGACTION, to avoid defining _-prefixed symbols --- erts/emulator/hipe/hipe_x86_signal.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index 741f570c50..6bb442490e 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -84,7 +84,7 @@ static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); #define init_done() (__next_sigaction != 0) extern int __sigaction(int, const struct sigaction*, struct sigaction*); -#define __SIGACTION __sigaction +#define LIBC_SIGACTION __sigaction static void do_init(void) { __next_sigaction = dlsym(RTLD_NEXT, "__sigaction"); @@ -121,7 +121,7 @@ static void do_init(void) static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); #define init_done() (__next_sigaction != 0) extern int _sigaction(int, const struct sigaction*, struct sigaction*); -#define __SIGACTION _sigaction +#define LIBC_SIGACTION _sigaction static void do_init(void) { __next_sigaction = dlsym(RTLD_NEXT, "sigaction"); @@ -157,7 +157,7 @@ static void do_init(void) #include static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); #define init_done() (__next_sigaction != 0) -#define __SIGACTION _sigaction +#define LIBC_SIGACTION _sigaction static void do_init(void) { __next_sigaction = dlsym(RTLD_NEXT, "_sigaction"); @@ -179,7 +179,7 @@ static void do_init(void) static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); #define init_done() (__next_sigaction != 0) extern int _sigaction(int, const struct sigaction*, struct sigaction*); -#define __SIGACTION _sigaction +#define LIBC_SIGACTION _sigaction static void do_init(void) { __next_sigaction = dlsym(RTLD_NEXT, "sigaction"); @@ -213,7 +213,7 @@ static void do_init(void) #include static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); #define init_done() (__next_sigaction != 0) -#define __SIGACTION __libc_sigaction +#define LIBC_SIGACTION __libc_sigaction static void do_init(void) { __next_sigaction = dlsym(RTLD_NEXT, "__libc_sigaction"); @@ -257,8 +257,8 @@ static int my_sigaction(int signum, const struct sigaction *act, struct sigactio * This overrides the C library's core sigaction() procedure, catching * all its internal calls. */ -#ifdef __SIGACTION -int __SIGACTION(int signum, const struct sigaction *act, struct sigaction *oldact) +#if defined(LIBC_SIGACTION) +int LIBC_SIGACTION(int signum, const struct sigaction *act, struct sigaction *oldact) { return my_sigaction(signum, act, oldact); } -- cgit v1.2.3 From 202c3dac37587e468b4c968a392c0b97668dda7e Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 31 Jan 2016 15:15:00 +0100 Subject: hipe_x86_signal: cleanups - rename __next_sigaction to next_sigaction, to avoid defining _-prefixed symbols - factor out common code for declaring and initializing next_sigaction, system-specific code now only needs to #define NEXT_SIGACTION --- erts/emulator/hipe/hipe_x86_signal.c | 89 +++++++++++------------------------- 1 file changed, 27 insertions(+), 62 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index 6bb442490e..2e7e64eae3 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -80,20 +80,9 @@ #ifndef __USE_GNU #define __USE_GNU /* to un-hide RTLD_NEXT */ #endif -#include -static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); -#define init_done() (__next_sigaction != 0) +#define NEXT_SIGACTION "__sigaction" extern int __sigaction(int, const struct sigaction*, struct sigaction*); #define LIBC_SIGACTION __sigaction -static void do_init(void) -{ - __next_sigaction = dlsym(RTLD_NEXT, "__sigaction"); - if (__next_sigaction != 0) - return; - perror("dlsym"); - abort(); -} -#define INIT() do { if (!init_done()) do_init(); } while (0) #endif /* glibc >= 2.3 */ /* Is there no standard identifier for Darwin/MacOSX ? */ @@ -117,21 +106,10 @@ static void do_init(void) * The other _sigaction, _sigaction_no_bind I don't understand the purpose * of and don't modify. */ -#include -static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); -#define init_done() (__next_sigaction != 0) +#define NEXT_SIGACTION "sigaction" extern int _sigaction(int, const struct sigaction*, struct sigaction*); #define LIBC_SIGACTION _sigaction -static void do_init(void) -{ - __next_sigaction = dlsym(RTLD_NEXT, "sigaction"); - if (__next_sigaction != 0) - return; - perror("dlsym_darwin"); - abort(); -} #define _NSIG NSIG -#define INIT() do { if (!init_done()) do_init(); } while (0) #endif /* __DARWIN__ */ #if defined(__sun__) @@ -154,20 +132,9 @@ static void do_init(void) * our init routine has had a chance to find _sigaction()'s address. * This forces us to initialise at the first call. */ -#include -static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); -#define init_done() (__next_sigaction != 0) +#define NEXT_SIGACTION "_sigaction" #define LIBC_SIGACTION _sigaction -static void do_init(void) -{ - __next_sigaction = dlsym(RTLD_NEXT, "_sigaction"); - if (__next_sigaction != 0) - return; - perror("dlsym"); - abort(); -} #define _NSIG NSIG -#define INIT() do { if (!init_done()) do_init(); } while (0) #endif /* __sun__ */ #if defined(__FreeBSD__) @@ -175,21 +142,10 @@ static void do_init(void) * This is a copy of Darwin code for FreeBSD. * CAVEAT: detailed semantics are not verified yet. */ -#include -static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); -#define init_done() (__next_sigaction != 0) +#define NEXT_SIGACTION "sigaction" extern int _sigaction(int, const struct sigaction*, struct sigaction*); #define LIBC_SIGACTION _sigaction -static void do_init(void) -{ - __next_sigaction = dlsym(RTLD_NEXT, "sigaction"); - if (__next_sigaction != 0) - return; - perror("dlsym_freebsd"); - abort(); -} #define _NSIG NSIG -#define INIT() do { if (!init_done()) do_init(); } while (0) #endif /* __FreeBSD__ */ #if defined(__NetBSD__) @@ -198,7 +154,7 @@ static void do_init(void) * Whether this actually provides the needed overrides for safe * signal delivery or not is unknown. */ -#define INIT() do { } while (0) +#undef NEXT_SIGACTION #endif /* __NetBSD__ */ #if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sun__)) @@ -210,30 +166,39 @@ static void do_init(void) * There are libc-internal calls to __libc_sigaction which install handlers, so we must * override __libc_sigaction rather than __sigaction. */ -#include -static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); -#define init_done() (__next_sigaction != 0) +#define NEXT_SIGACTION "__libc_sigaction" #define LIBC_SIGACTION __libc_sigaction +#ifndef _NSIG +#define _NSIG NSIG +#endif +#endif /* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __FreeBSD__ || __sun__) */ + +#if defined(NEXT_SIGACTION) +/* + * Initialize a function pointer to the libc core sigaction routine, + * to be used by our wrappers. + */ +#include +static int (*next_sigaction)(int, const struct sigaction*, struct sigaction*); static void do_init(void) { - __next_sigaction = dlsym(RTLD_NEXT, "__libc_sigaction"); - if (__next_sigaction != 0) + next_sigaction = dlsym(RTLD_NEXT, NEXT_SIGACTION); + if (next_sigaction != 0) return; perror("dlsym"); abort(); } -#ifndef _NSIG -#define _NSIG NSIG -#endif -#define INIT() do { if (!init_done()) do_init(); } while (0) -#endif /* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __FreeBSD__ || __sun__) */ +#define INIT() do { if (!next_sigaction) do_init(); } while (0) +#else /* !defined(NEXT_SIGACTION) */ +#define INIT() do { } while (0) +#endif /* !defined(NEXT_SIGACTION) */ -#if !defined(__NetBSD__) +#if defined(NEXT_SIGACTION) /* * This is our wrapper for sigaction(). sigaction() can be called before * hipe_signal_init() has been executed, especially when threads support * has been linked with the executable. Therefore, we must initialise - * __next_sigaction() dynamically, the first time it's needed. + * next_sigaction() dynamically, the first time it's needed. */ static int my_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { @@ -249,7 +214,7 @@ static int my_sigaction(int signum, const struct sigaction *act, struct sigactio newact.sa_flags |= SA_ONSTACK; act = &newact; } - return __next_sigaction(signum, act, oldact); + return next_sigaction(signum, act, oldact); } #endif -- cgit v1.2.3 From 2d86df7cf1c5645fa80ef091b1f9a39ce44f764d Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 31 Jan 2016 15:15:40 +0100 Subject: hipe_x86_signal: cleanups - move extern declarations of LIBC_SIGACTION() from system-specific blocks to the common LIBC_SIGACTION block --- erts/emulator/hipe/hipe_x86_signal.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index 2e7e64eae3..b44899401b 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -81,7 +81,6 @@ #define __USE_GNU /* to un-hide RTLD_NEXT */ #endif #define NEXT_SIGACTION "__sigaction" -extern int __sigaction(int, const struct sigaction*, struct sigaction*); #define LIBC_SIGACTION __sigaction #endif /* glibc >= 2.3 */ @@ -107,7 +106,6 @@ extern int __sigaction(int, const struct sigaction*, struct sigaction*); * of and don't modify. */ #define NEXT_SIGACTION "sigaction" -extern int _sigaction(int, const struct sigaction*, struct sigaction*); #define LIBC_SIGACTION _sigaction #define _NSIG NSIG #endif /* __DARWIN__ */ @@ -143,7 +141,6 @@ extern int _sigaction(int, const struct sigaction*, struct sigaction*); * CAVEAT: detailed semantics are not verified yet. */ #define NEXT_SIGACTION "sigaction" -extern int _sigaction(int, const struct sigaction*, struct sigaction*); #define LIBC_SIGACTION _sigaction #define _NSIG NSIG #endif /* __FreeBSD__ */ @@ -218,11 +215,12 @@ static int my_sigaction(int signum, const struct sigaction *act, struct sigactio } #endif +#if defined(LIBC_SIGACTION) /* * This overrides the C library's core sigaction() procedure, catching * all its internal calls. */ -#if defined(LIBC_SIGACTION) +extern int LIBC_SIGACTION(int, const struct sigaction*, struct sigaction*); int LIBC_SIGACTION(int signum, const struct sigaction *act, struct sigaction *oldact) { return my_sigaction(signum, act, oldact); -- cgit v1.2.3 From 6823fb17e3cd9429f9e562d3e2e096bc1bb3ebe7 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 31 Jan 2016 15:16:31 +0100 Subject: hipe_x86_signal: cleanups - replace system-specific #ifndef around sigaction() override with a test for OVERRIDE_SIGACTION, #define or #undef that as appropriate in each system-specific block --- erts/emulator/hipe/hipe_x86_signal.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index b44899401b..10a40ce901 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -82,6 +82,7 @@ #endif #define NEXT_SIGACTION "__sigaction" #define LIBC_SIGACTION __sigaction +#define OVERRIDE_SIGACTION #endif /* glibc >= 2.3 */ /* Is there no standard identifier for Darwin/MacOSX ? */ @@ -107,6 +108,7 @@ */ #define NEXT_SIGACTION "sigaction" #define LIBC_SIGACTION _sigaction +#undef OVERRIDE_SIGACTION #define _NSIG NSIG #endif /* __DARWIN__ */ @@ -132,6 +134,7 @@ */ #define NEXT_SIGACTION "_sigaction" #define LIBC_SIGACTION _sigaction +#define OVERRIDE_SIGACTION #define _NSIG NSIG #endif /* __sun__ */ @@ -142,6 +145,7 @@ */ #define NEXT_SIGACTION "sigaction" #define LIBC_SIGACTION _sigaction +#undef OVERRIDE_SIGACTION #define _NSIG NSIG #endif /* __FreeBSD__ */ @@ -152,6 +156,7 @@ * signal delivery or not is unknown. */ #undef NEXT_SIGACTION +#undef OVERRIDE_SIGACTION #endif /* __NetBSD__ */ #if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sun__)) @@ -165,6 +170,7 @@ */ #define NEXT_SIGACTION "__libc_sigaction" #define LIBC_SIGACTION __libc_sigaction +#define OVERRIDE_SIGACTION #ifndef _NSIG #define _NSIG NSIG #endif @@ -227,10 +233,10 @@ int LIBC_SIGACTION(int signum, const struct sigaction *act, struct sigaction *ol } #endif +#if defined(OVERRIDE_SIGACTION) /* * This catches the application's own sigaction() calls. */ -#if !defined(__DARWIN__) && !defined(__NetBSD__) && !defined(__FreeBSD__) int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { return my_sigaction(signum, act, oldact); -- cgit v1.2.3 From d76ee58c07f32dfc0652844ec2b513af2105ffa1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 22 Oct 2014 17:15:31 +0200 Subject: erts: Add ERTS_WRITE_UNLIKELY ERTS_WRITE_UNLIKELY can be used to place global variables in a specific section where only data that is very rarely modified sits. This is used to improve cache locality. --- erts/emulator/beam/erl_alloc.c | 4 ++-- erts/emulator/beam/erl_process.c | 24 ++++++++++++------------ erts/emulator/beam/sys.h | 11 +++++++++++ 3 files changed, 25 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 5544712e8d..51b522c9e0 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -103,9 +103,9 @@ static Uint install_debug_functions(void); static int lock_all_physical_memory = 0; -ErtsAllocatorFunctions_t erts_allctrs[ERTS_ALC_A_MAX+1]; +ErtsAllocatorFunctions_t ERTS_WRITE_UNLIKELY(erts_allctrs[ERTS_ALC_A_MAX+1]); ErtsAllocatorInfo_t erts_allctrs_info[ERTS_ALC_A_MAX+1]; -ErtsAllocatorThrSpec_t erts_allctr_thr_spec[ERTS_ALC_A_MAX+1]; +ErtsAllocatorThrSpec_t ERTS_WRITE_UNLIKELY(erts_allctr_thr_spec[ERTS_ALC_A_MAX+1]); #define ERTS_MIN(A, B) ((A) < (B) ? (A) : (B)) #define ERTS_MAX(A, B) ((A) > (B) ? (A) : (B)) diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 7d194f9840..3902632ef8 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -148,14 +148,14 @@ extern BeamInstr beam_apply[]; extern BeamInstr beam_exit[]; extern BeamInstr beam_continue_exit[]; -int erts_default_spo_flags = 0; -int erts_eager_check_io = 1; -int erts_sched_compact_load; -int erts_sched_balance_util = 0; -Uint erts_no_schedulers; +int ERTS_WRITE_UNLIKELY(erts_default_spo_flags) = 0; +int ERTS_WRITE_UNLIKELY(erts_eager_check_io) = 1; +int ERTS_WRITE_UNLIKELY(erts_sched_compact_load); +int ERTS_WRITE_UNLIKELY(erts_sched_balance_util) = 0; +Uint ERTS_WRITE_UNLIKELY(erts_no_schedulers); #ifdef ERTS_DIRTY_SCHEDULERS -Uint erts_no_dirty_cpu_schedulers; -Uint erts_no_dirty_io_schedulers; +Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_cpu_schedulers); +Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_io_schedulers); #endif static char *erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_NO_FLAGS] = {0}; @@ -295,7 +295,7 @@ do { \ erts_sched_stat_t erts_sched_stat; #ifdef USE_THREADS -static erts_tsd_key_t sched_data_key; +static erts_tsd_key_t ERTS_WRITE_UNLIKELY(sched_data_key); #endif static erts_smp_atomic32_t function_calls; @@ -335,10 +335,10 @@ static ErtsAlignedSchedulerSleepInfo *aligned_dirty_io_sched_sleep_info; static Uint last_reductions; static Uint last_exact_reductions; -Eterm erts_system_monitor; -Eterm erts_system_monitor_long_gc; -Uint erts_system_monitor_long_schedule; -Eterm erts_system_monitor_large_heap; +Eterm ERTS_WRITE_UNLIKELY(erts_system_monitor); +Eterm ERTS_WRITE_UNLIKELY(erts_system_monitor_long_gc); +Uint ERTS_WRITE_UNLIKELY(erts_system_monitor_long_schedule); +Eterm ERTS_WRITE_UNLIKELY(erts_system_monitor_large_heap); struct erts_system_monitor_flags_t erts_system_monitor_flags; /* system performance monitor */ diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 279e4db0af..e77b4b2dc8 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -136,6 +136,17 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType; # define ERTS_LIKELY(BOOL) (BOOL) # define ERTS_UNLIKELY(BOOL) (BOOL) #endif + +#if ERTS_AT_LEAST_GCC_VSN__(2, 96, 0) +#ifndef __llvm__ +# define ERTS_WRITE_UNLIKELY(X) X __attribute__ ((section ("ERTS_LOW_WRITE") )) +#else +# define ERTS_WRITE_UNLIKELY(X) X __attribute__ ((section ("__DATA,ERTS_LOW_WRITE") )) +#endif +#else +# define ERTS_WRITE_UNLIKELY(X) X +#endif + #ifdef __GNUC__ # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) # define ERTS_DECLARE_DUMMY(X) X __attribute__ ((unused)) -- cgit v1.2.3 From eea5f896780e07f7ca76685061d01e7be5a7abaa Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 11 Sep 2014 18:26:26 +0200 Subject: erts, kernel: Add os:perf_counter function The perf_counter is a very very cheap and high resolution timer that can be used to timestamp system events. It does not have monoticity guarantees, but should on most OS's expose a monotonous time. A special instruction has been created for this counter to further speed up fetching it. OTP-12908 --- erts/doc/src/erlang.xml | 11 +++++ erts/emulator/beam/beam_emu.c | 32 +++++++++++++++ erts/emulator/beam/bif.tab | 2 + erts/emulator/beam/erl_time.h | 2 + erts/emulator/beam/erl_time_sup.c | 76 ++++++++++++++++++++++++++++++++++- erts/emulator/beam/ops.tab | 9 +++++ erts/emulator/sys/unix/erl_unix_sys.h | 30 +++++++++++++- erts/emulator/sys/unix/sys_time.c | 2 + erts/emulator/sys/win32/erl_win_sys.h | 2 + erts/preloaded/src/erlang.erl | 5 ++- erts/preloaded/src/erts_internal.erl | 7 +++- 11 files changed, 173 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index d0d35ea25f..6915b35fcb 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -131,6 +131,17 @@ + perf_counter +

Symbolic representation of the performance counter + time unit used by the Erlang runtime system.

+ +

The perf_counter time unit behaves much in the same way + as the native time unit. That is it might differ inbetween + run-time restarts. You get values of this type by calling + os:perf_counter() +

+
+

The time_unit/0 type may be extended. Use diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 4d7b00b032..a0979c7b0e 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4895,6 +4895,38 @@ do { \ } } + /* This is optimised as an instruction because + it has to be very very fast */ + OpCase(i_perf_counter): { + BeamInstr* next; + ErtsSysHrTime ts; + PreFetch(0, next); + + sys_perf_counter(&ts); + + if (IS_SSMALL(ts)) { + r(0) = make_small((Sint)ts); + } else { + TestHeap(ERTS_SINT64_HEAP_SIZE(ts),0); + r(0) = make_big(HTOP); +#if defined(ARCH_32) || HALFWORD_HEAP + if (ts >= (((Uint64) 1) << 32)) { + *HTOP = make_pos_bignum_header(2); + BIG_DIGIT(HTOP, 0) = (Uint) (ts & ((Uint) 0xffffffff)); + BIG_DIGIT(HTOP, 1) = (Uint) ((ts >> 32) & ((Uint) 0xffffffff)); + HTOP += 3; + } + else +#endif + { + *HTOP = make_pos_bignum_header(1); + BIG_DIGIT(HTOP, 0) = (Uint) ts; + HTOP += 2; + } + } + NextPF(0, next); + } + OpCase(i_debug_breakpoint): { HEAVY_SWAPOUT; I = call_error_handler(c_p, I-3, reg, am_breakpoint); diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 1b8ae8cef5..4efc055aaf 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -170,6 +170,7 @@ bif erts_internal:term_type/1 bif erts_internal:map_hashmap_children/1 bif erts_internal:time_unit/0 +bif erts_internal:perf_counter_unit/0 bif erts_internal:is_system_process/1 @@ -369,6 +370,7 @@ bif os:getpid/0 bif os:timestamp/0 bif os:system_time/0 bif os:system_time/1 +bif os:perf_counter/0 # # Bifs in the erl_ddll module (the module actually does not exist) diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 93a0d556bf..446adcf4af 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -142,6 +142,8 @@ erts_time_unit_conversion(Uint64 value, Uint32 from_time_unit, Uint32 to_time_unit); +ErtsSysHrTime erts_perf_counter_unit(void); + #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE Uint64 diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 668dbb5daa..6509c6a805 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -1749,7 +1749,7 @@ erts_get_monotonic_time(ErtsSchedulerData *esdp) { ErtsMonotonicTime mtime = time_sup.r.o.get_time(); update_last_mtime(esdp, mtime); - return mtime; + return mtime; } ErtsMonotonicTime @@ -2435,9 +2435,81 @@ BIF_RETTYPE os_system_time_0(BIF_ALIST_0) BIF_RET(make_time_val(BIF_P, stime)); } -BIF_RETTYPE os_system_time_1(BIF_ALIST_0) +BIF_RETTYPE os_system_time_1(BIF_ALIST_1) { ErtsSystemTime stime = erts_os_system_time(); BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, stime, 0)); } +BIF_RETTYPE +os_perf_counter_0(BIF_ALIST_0) +{ + ErtsSysHrTime pcounter; + sys_perf_counter(&pcounter); + BIF_RET(make_time_val(BIF_P, pcounter)); +} + +BIF_RETTYPE erts_internal_perf_counter_unit_0(BIF_ALIST_0) +{ + BIF_RET(make_time_val(BIF_P, SYS_PERF_COUNTER_UNIT)); +} + +/* What resolution to spin to in micro seconds */ +#define RESOLUTION 100 +/* How many iterations to spin */ +#define ITERATIONS 1 +/* How many significant figures to round to */ +#define SIGFIGS 3 + +static ErtsSysHrTime perf_counter_unit = 0; + +static ErtsSysHrTime erts_calculate_perf_counter_unit(void); +static ErtsSysHrTime erts_calculate_perf_counter_unit() { + int i; + ErtsSysHrTime pre, post; + double value = 0; + double round_factor; +#if defined(HAVE_GETHRTIME) && defined(GETHRTIME_WITH_CLOCK_GETTIME) + struct timespec basetime,comparetime; +#define __GETTIME(arg) clock_gettime(CLOCK_MONOTONIC,arg) +#define __GETUSEC(arg) (arg.tv_nsec / 1000) +#else + SysTimeval basetime,comparetime; +#define __GETTIME(arg) sys_gettimeofday(arg) +#define __GETUSEC(arg) arg.tv_usec +#endif + + for (i = 0; i < ITERATIONS; i++) { + /* Make sure usec just flipped over at current resolution */ + __GETTIME(&basetime); + do { + __GETTIME(&comparetime); + } while ((__GETUSEC(basetime) / RESOLUTION) == (__GETUSEC(comparetime) / RESOLUTION)); + + sys_perf_counter(&pre); + + __GETTIME(&basetime); + do { + __GETTIME(&comparetime); + } while ((__GETUSEC(basetime) / RESOLUTION) == (__GETUSEC(comparetime) / RESOLUTION)); + + sys_perf_counter(&post); + + value += post - pre; + } + /* After this value is ticks per us */ + value /= (RESOLUTION*ITERATIONS); + + /* We round to 3 significant figures */ + round_factor = pow(10.0, SIGFIGS - ceil(log10(value))); + value = ((ErtsSysHrTime)(value * round_factor + 0.5)) / round_factor; + + /* convert to ticks per second */ + return 1000000 * value; +} + +ErtsSysHrTime erts_perf_counter_unit() { + if (perf_counter_unit == 0) + perf_counter_unit = erts_calculate_perf_counter_unit(); + return perf_counter_unit; +} diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 081c4108a0..9e53b4bfcc 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -988,6 +988,13 @@ move Discarded x==0 | move Something x==0 => move Something x=0 %endif +call_ext u==0 u$func:os:perf_counter/0 => \ + i_perf_counter +call_ext_last u==0 u$func:os:perf_counter/0 D => \ + i_perf_counter | deallocate_return D +call_ext_only u==0 u$func:os:perf_counter/0 => \ + i_perf_counter | return + # # The general case for BIFs that have no special instructions. # A BIF used in the tail must be followed by a return instruction. @@ -1027,6 +1034,8 @@ i_apply_fun_only i_hibernate +i_perf_counter + call_bif e # diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 0352ee1b3c..9f137048de 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -101,6 +101,10 @@ #endif #include +#ifdef HAVE_MACH_ABSOLUTE_TIME +#include +#endif + #ifdef HAVE_POSIX_MEMALIGN # define ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC 1 #endif @@ -266,7 +270,31 @@ erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) #endif /* ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT */ /* - * + * Functions for getting the performance counter + */ + +#if defined(__x86_64__) + /* available on all x86_64. Best if used when we have constant_tsc and + nonstop_tsc cpu features. It may have been a good idea to put the + cpuid instruction before the rdtsc, but I decided against it + because it is not really needed for msacc, and it slows it down by + quite a bit. As a result though, this timestamp becomes much less + accurate as it might be re-ordered to be executed way before this + function is called. + */ +#define sys_perf_counter(ts) do { \ + __asm__ __volatile__ ("rdtsc\n\t" \ + "shl $32, %%rdx\n\t" \ + "or %%rdx, %0" : "=a" (*ts) : : "rdx"); \ + } while(0) +#define SYS_PERF_COUNTER_UNIT erts_perf_counter_unit() +#else +#define sys_perf_counter(ts) *(ts) = erts_get_perf_counter() +#define SYS_PERF_COUNTER_UNIT 1000000000LL +#endif + +/* + * Functions for measuring CPU time */ #if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME_CPU_TIME)) diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index 2e1914f564..6fc4fc7dc4 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -912,6 +912,8 @@ erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) #ifdef HAVE_GETHRVTIME_PROCFS_IOCTL +/* The code below only has effect on solaris < 10, + needed in order for gehhrvtime to work properly */ int sys_start_hrvtime(void) { long msacct = PR_MSACCT; diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 7e8dd8a4ca..1ab9eadefd 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -238,6 +238,8 @@ erts_sys_hrtime(void) extern void sys_gettimeofday(SysTimeval *tv); extern clock_t sys_times(SysTimes *buffer); +#define sys_perf_counter(ts) *(ts) = erts_sys_hrtime() +#define SYS_PERF_COUNTER_UNIT ERTS_I64_LITERAL(1000000000) extern char *win_build_environment(char *); diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index bca366681d..6f56a81eec 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -71,7 +71,8 @@ | 'milli_seconds' | 'micro_seconds' | 'nano_seconds' - | 'native'. + | 'native' + | 'perf_counter'. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Native code BIF stubs and their types @@ -1347,6 +1348,7 @@ convert_time_unit(Time, FromUnit, ToUnit) -> try FU = case FromUnit of native -> erts_internal:time_unit(); + perf_counter -> erts_internal:perf_counter_unit(); nano_seconds -> 1000*1000*1000; micro_seconds -> 1000*1000; milli_seconds -> 1000; @@ -1355,6 +1357,7 @@ convert_time_unit(Time, FromUnit, ToUnit) -> end, TU = case ToUnit of native -> erts_internal:time_unit(); + perf_counter -> erts_internal:perf_counter_unit(); nano_seconds -> 1000*1000*1000; micro_seconds -> 1000*1000; milli_seconds -> 1000; diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 84dedab930..c3a14d272d 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -45,7 +45,7 @@ -export([await_result/1, gather_io_bytes/2]). --export([time_unit/0]). +-export([time_unit/0, perf_counter_unit/0]). -export([is_system_process/1]). @@ -345,6 +345,11 @@ flush_monitor_messages(Ref, Multi, Res) when is_reference(Ref) -> time_unit() -> erlang:nif_error(undefined). +-spec erts_internal:perf_counter_unit() -> pos_integer(). + +perf_counter_unit() -> + erlang:nif_error(undefined). + -spec erts_internal:is_system_process(Pid) -> boolean() when Pid :: pid(). -- cgit v1.2.3 From 664ed2a6fd2b324bb6b56db3d3eca853cfda8f61 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 12 Sep 2014 16:38:00 +0200 Subject: erts: Add microstate accounting Microstate accounting is a way to track which state the different threads within ERTS are in. The main usage area is to pin point performance bottlenecks by checking which states the threads are in and then from there figuring out why and where to optimize. Since checking whether microstate accounting is on or off is relatively expensive if done in a short loop only a few of the states are enabled by default and more states can be enabled through configure. I've done some benchmarking and the overhead with it turned off is not noticible and with it on it is a fraction of a percent. If you enable the extra states, depending on the benchmark, the ovehead when turned off is about 1% and when turned on somewhere inbetween 5-15%. OTP-12345 --- erts/configure.in | 15 + erts/doc/src/erlang.xml | 177 +++++++++++- erts/emulator/Makefile.in | 3 +- erts/emulator/beam/atom.names | 4 + erts/emulator/beam/beam_emu.c | 43 ++- erts/emulator/beam/bif.c | 48 +++- erts/emulator/beam/erl_alloc.h | 18 +- erts/emulator/beam/erl_alloc.types | 1 + erts/emulator/beam/erl_async.c | 34 ++- erts/emulator/beam/erl_bif_info.c | 12 + erts/emulator/beam/erl_gc.c | 7 + erts/emulator/beam/erl_init.c | 3 + erts/emulator/beam/erl_lock_check.c | 2 + erts/emulator/beam/erl_msacc.c | 486 ++++++++++++++++++++++++++++++++ erts/emulator/beam/erl_msacc.h | 409 +++++++++++++++++++++++++++ erts/emulator/beam/erl_port_task.c | 3 + erts/emulator/beam/erl_process.c | 48 +++- erts/emulator/beam/erl_threads.h | 34 ++- erts/emulator/beam/global.h | 2 + erts/emulator/beam/io.c | 32 +++ erts/emulator/beam/time.c | 20 +- erts/emulator/sys/common/erl_poll.c | 36 ++- erts/emulator/sys/win32/erl_poll.c | 4 + erts/emulator/test/statistics_SUITE.erl | 95 ++++++- erts/preloaded/src/erlang.erl | 13 + erts/preloaded/src/erts_internal.erl | 28 ++ 26 files changed, 1529 insertions(+), 48 deletions(-) create mode 100644 erts/emulator/beam/erl_msacc.c create mode 100644 erts/emulator/beam/erl_msacc.h (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 2f19c0f760..d84651f4f9 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -342,6 +342,21 @@ AS_HELP_STRING([--disable-saved-compile-time], [disable saved compile time]), AC_DEFINE_UNQUOTED(ERTS_SAVED_COMPILE_TIME, $save_compile_time, [Save compile time?]) +AC_ARG_WITH(microstate-accounting, +AS_HELP_STRING([--with-microstate-accounting={yes|extra}], + [enable microstate account, possibly with extra detailed states]) +AS_HELP_STRING([--without-microstate-accounting], + [don't enable microstate accounting]), +[],[with_microstate_accounting=yes]) + +case "$with_microstate_accounting" in + yes) AC_DEFINE(ERTS_ENABLE_MSACC,[1], + [Define as 1 if you want to enable microstate accounting, 2 if you want extra states]) ;; + extra) AC_DEFINE(ERTS_ENABLE_MSACC,[2], + [Define as 1 if you want to enable microstate accounting, 2 if you want extra states]) ;; + *) ;; +esac + dnl Magic test for clearcase. OTP_RELEASE= if test "${ERLANG_COMMERCIAL_BUILD}" != ""; then diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 6915b35fcb..ddca492040 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5870,6 +5870,146 @@ true + Information about microstate accounting. + + +

+ Microstate accounting can be used to measure how much time the Erlang + runtime system spends doing various tasks. It is designed to be as + lightweight as possible, but there will be some overhead when this + is enabled. Microstate accounting is meant to be a profiling tool + to help figure out performance bottlenecks. + To start/stop/reset microstate_accounting you use + the system_flag + + microstate_accounting. +

+

+ erlang:statistics(microstate_accounting) returns a list of maps + representing some of the OS threads within ERTS. Each map contains + type and id fields that can be used to identify what + thread it is, and also a counters field that contains data about how + much time has been spent in the various states.

+
+> erlang:statistics(microstate_accounting).
+[#{counters => #{aux => 1899182914,
+                 check_io => 2605863602,
+                 emulator => 45731880463,
+                 gc => 1512206910,
+                 other => 5421338456,
+                 port => 221631,
+                 sleep => 5150294100},
+   id => 1,
+   type => scheduler}|...]
+        
+

The time unit is the same as returned by + + os:perf_counter/0. + So to convert it to milliseconds you could do something like this:

+
+lists:map(
+  fun(#{ counters := Cnt } = M) ->
+          MsCnt = maps:map(fun(_K, PerfCount) ->
+                                   erlang:convert_time_unit(PerfCount, perf_counter, 1000)
+                           end, Cnt),
+         M#{ counters := MsCnt }
+  end, erlang:statistics(microstate_accounting)).
+        
+

+ It is important to note that these values are not guaranteed to be + the exact time spent in each state. This is because of various + optimisation done in order to keep the overhead as small as possible. +

+ +

Currently the following MSAcc_Thread_Type are available:

+ + scheduler + The main execution threads that do most of the work. + asyncAsync threads are used by various + linked-in drivers (mainly the file drivers) do offload non-cpu + intensive work. + auxTakes care of any work that is not + specifically assigned to a scheduler. + +

Currently the following MSAcc_Thread_States are available. + All states are exclusive, meaning that a thread cannot be in two states + at once. So if you add the numbers of all counters in a thread + you will get the total run-time for that thread.

+ + aux + Time spent handling auxiliary jobs. + check_io + Time spent checking for new I/O events. + emulator + Time spent executing erlang processes. + gc + Time spent doing garbage collection. When extra states are + enabled this is the time spent doing non-fullsweep garbage + collections. + other + Time spent doing unaccounted things. + port + Time spent executing ports. + sleep + Time spent sleeping. + +

It is possible to add more fine grained MSAcc_Thread_States + through configure. + (e.g. ./configure --with-microstate-accounting=extra). + Enabling these states will cause a performance degradation when + microstate accounting is turned off and increase the overhead when + it is turned on.

+ + alloc + Time spent managing memory. Without extra states this time is + spread out over all other states. + bif + Time spent in bifs. Without extra states this time is part of + the emulator state. + busy_wait + Time spent busy waiting. This is also the state where a + scheduler no longer reports that it is active when using + + erlang:statistics(scheduler_wall_time). + So if you add all other states but this and sleep and then divide that + by all time in the thread you should get something very similar to the + scheduler_wall_time fraction. Without extra states this time is part + of the other state. + ets + Time spent executing ETS bifs. Without extra states this time is + part of the emulator state. + gc_full + Time spent doing fullsweep garbage collection. Without extra + states this time is part of the gc state. + nif + Time spent in nifs. Without extra states this time is part of + the emulator state. + send + Time spent sending messages (processes only). Without extra + states this time is part of the emulator state. + timers + Time spent managing timers. Without extra states this time is + part of the other state. + +

There is a utility module called + msacc in + runtime_tools that can be used to more easily analyse these + statistics.

+ +

+ Returns undefined if the system flag + + microstate_accounting + is turned off. +

+

The list of thread information is unsorted and may appear in + different order between calls.

+

The threads and states are subject to change without any + prior notice.

+ + + + Information about reductions. @@ -5887,7 +6027,7 @@ true - + Information about the run-queues.

@@ -5903,7 +6043,7 @@ true - + Information about the run-queue lengths.

@@ -5923,7 +6063,7 @@ true - + Information about runtime.

Returns information about runtime, in milliseconds.

@@ -5938,7 +6078,7 @@ true
- + Information about each schedulers work time. @@ -6009,7 +6149,7 @@ ok - + Information about active processes and ports.

@@ -6027,7 +6167,7 @@ ok - + Information about the run-queue lengths.

@@ -6046,7 +6186,7 @@ ok - + Information about wall clock.

Returns information about wall clock. wall_clock can @@ -6280,6 +6420,17 @@ ok + Set system flag microstate_accounting +

+ Turns on/off microstate accounting measurements. By passing reset it is possible to reset + all counters to 0.

+

For more information see, + erlang:statistics(microstate_accounting). +

+
+
+ + Sets system flag min_heap_size.

Sets the default minimum heap size for processes. The size @@ -6294,7 +6445,7 @@ ok - + Sets system flag min_bin_vheap_size.

Sets the default minimum binary virtual heap size for @@ -6311,7 +6462,7 @@ ok - + Sets system flag multi_scheduling.

@@ -6349,7 +6500,7 @@ ok - + Sets system flag scheduler_bind_type. @@ -6467,7 +6618,7 @@ ok - + Sets system flag scheduler_wall_time.

Turns on or off scheduler wall time measurements.

@@ -6477,7 +6628,7 @@ ok
- + Sets system flag schedulers_online.

@@ -6502,7 +6653,7 @@ ok - + Sets system flag trace_control_word.

Sets the value of the node trace control word to diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index f4b806fae9..12148ad9c7 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -776,7 +776,8 @@ RUN_OBJS = \ $(OBJDIR)/erl_zlib.o $(OBJDIR)/erl_nif.o \ $(OBJDIR)/erl_bif_binary.o $(OBJDIR)/erl_ao_firstfit_alloc.o \ $(OBJDIR)/erl_thr_queue.o $(OBJDIR)/erl_sched_spec_pre_alloc.o \ - $(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o + $(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o \ + $(OBJDIR)/erl_msacc.o ifeq ($(TARGET),win32) DRV_OBJS = \ diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 1629a92aa9..6fb08ee896 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -102,6 +102,7 @@ atom asynchronous atom atom atom atom_used atom attributes +atom await_microstate_accounting_modifications atom await_port_send_result atom await_proc_exit atom await_result @@ -172,6 +173,7 @@ atom const atom context_switches atom control atom copy +atom counters atom cpu atom cpu_timestamp atom cr @@ -269,6 +271,7 @@ atom get_tcw atom getenv atom gather_gc_info_result atom gather_io_bytes +atom gather_microstate_accounting_result atom gather_sched_wall_time_result atom getting_linked atom getting_unlinked @@ -360,6 +363,7 @@ atom merge_trap atom meta atom meta_match_spec atom micro_seconds +atom microstate_accounting atom milli_seconds atom min_heap_size atom min_bin_vheap_size diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index a0979c7b0e..14b81a0a6e 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -113,6 +113,9 @@ do { \ #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #endif +#define GET_BIF_MODULE(p) ((Eterm) (((Export *) p)->code[0])) +#define GET_BIF_FUNCTION(p) ((Eterm) (((Export *) p)->code[1])) +#define GET_BIF_ARITY(p) ((Eterm) (((Export *) p)->code[2])) #define GET_BIF_ADDRESS(p) ((BifFunction) (((Export *) p)->code[4])) #define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm)))) @@ -1249,6 +1252,8 @@ void process_main(void) Uint64 start_time = 0; /* Monitor long schedule */ BeamInstr* start_time_i = NULL; + ERTS_MSACC_DECLARE_CACHE_X(); /* a cached value of the tsd pointer for msacc */ + ERL_BITS_DECLARE_STATEP; /* Has to be last declaration */ @@ -1302,6 +1307,8 @@ void process_main(void) ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); + ERTS_MSACC_UPDATE_CACHE_X(); + if (erts_system_monitor_long_schedule != 0) { start_time = erts_timestamp_millis(); start_time_i = c_p->i; @@ -2741,11 +2748,21 @@ do { \ */ OpCase(call_bif_e): { - Eterm (*bf)(Process*, Eterm*, BeamInstr*) = GET_BIF_ADDRESS(Arg(0)); + Eterm (*bf)(Process*, Eterm*, BeamInstr*); Eterm result; BeamInstr *next; ErlHeapFragment *live_hf_end; + if (ERTS_MSACC_IS_ENABLED_CACHED_X()) { + if (GET_BIF_MODULE(Arg(0)) == am_ets) { + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ETS); + } else { + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF); + } + } + + bf = GET_BIF_ADDRESS(Arg(0)); + PRE_BIF_SWAPOUT(c_p); c_p->fcalls = FCALLS - 1; if (FCALLS <= 0) { @@ -2768,6 +2785,12 @@ do { \ PROCESS_MAIN_CHK_LOCKS(c_p); HTOP = HEAP_TOP(c_p); FCALLS = c_p->fcalls; + /* We have to update the cache if we are enabled in order + to make sure no book keeping is done after we disabled + msacc. We don't always do this as it is quite expensive. */ + if (ERTS_MSACC_IS_ENABLED_CACHED_X()) + ERTS_MSACC_UPDATE_CACHE_X(); + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR); if (is_value(result)) { r(0) = result; CHECK_TERM(r(0)); @@ -3452,6 +3475,8 @@ do { \ BifFunction vbf; ErlHeapFragment *live_hf_end; + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF); + DTRACE_NIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); c_p->current = I-3; /* current and vbf set to please handle_error */ SWAPOUT; @@ -3476,6 +3501,8 @@ do { \ PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR); + DTRACE_NIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); goto apply_bif_or_nif_epilogue; @@ -3490,6 +3517,13 @@ do { \ * code[3]: &&apply_bif * code[4]: Function pointer to BIF function */ + if (ERTS_MSACC_IS_ENABLED_CACHED_X()) { + if ((Eterm)I[-3] == am_ets) { + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ETS); + } else { + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF); + } + } c_p->current = I-3; /* In case we apply process_info/1,2 or load_nif/1 */ c_p->i = I; /* In case we apply check_process_code/2. */ @@ -3516,7 +3550,12 @@ do { \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); } - + /* We have to update the cache if we are enabled in order + to make sure no book keeping is done after we disabled + msacc. We don't always do this as it is quite expensive. */ + if (ERTS_MSACC_IS_ENABLED_CACHED_X()) + ERTS_MSACC_UPDATE_CACHE_X(); + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR); DTRACE_BIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); apply_bif_or_nif_epilogue: diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index bb9165cd79..7e8eb44680 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -44,6 +44,7 @@ #include "erl_ptab.h" #include "erl_bits.h" #include "erl_bif_unique.h" +#include "erl_msacc.h" Export *erts_await_result; static Export* flush_monitor_messages_trap = NULL; @@ -54,6 +55,9 @@ Export* erts_format_cpu_topology_trap = NULL; static Export dsend_continue_trap_export; Export *erts_convert_time_unit_trap = NULL; +static Export *await_msacc_mod_trap = NULL; +static erts_smp_atomic32_t msacc; + static Export *await_sched_wall_time_mod_trap; static erts_smp_atomic32_t sched_wall_time; @@ -2143,7 +2147,11 @@ BIF_RETTYPE send_3(BIF_ALIST_3) int connect = !0; Eterm l = opts; Sint result; + DeclareTypedTmpHeap(ErtsSendContext, ctx, BIF_P); + + ERTS_MSACC_PUSH_STATE_M_X(); + UseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), BIF_P); ctx->suspend = !0; @@ -2172,7 +2180,10 @@ BIF_RETTYPE send_3(BIF_ALIST_3) ref = NIL; #endif + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_SEND); result = do_send(p, to, msg, &ref, ctx); + ERTS_MSACC_POP_STATE_M_X(); + if (result > 0) { ERTS_VBUMP_REDS(p, result); if (ERTS_IS_PROC_OUT_OF_REDS(p)) @@ -2288,8 +2299,8 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg) Eterm ref; Sint result; DeclareTypedTmpHeap(ErtsSendContext, ctx, p); + ERTS_MSACC_PUSH_AND_SET_STATE_M_X(ERTS_MSACC_STATE_SEND); UseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), p); - #ifdef DEBUG ref = NIL; #endif @@ -2300,7 +2311,9 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg) ctx->dss.phase = ERTS_DSIG_SEND_PHASE_INIT; result = do_send(p, to, msg, &ref, ctx); - + + ERTS_MSACC_POP_STATE_M_X(); + if (result > 0) { ERTS_VBUMP_REDS(p, result); if (ERTS_IS_PROC_OUT_OF_REDS(p)) @@ -4586,6 +4599,31 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) default: ERTS_INTERNAL_ERROR("Unknown state"); } +#ifdef ERTS_ENABLE_MSACC + } else if (BIF_ARG_1 == am_microstate_accounting) { + Eterm threads; + if (BIF_ARG_2 == am_true || BIF_ARG_2 == am_false) { + erts_aint32_t new = BIF_ARG_2 == am_true ? ERTS_MSACC_ENABLE : ERTS_MSACC_DISABLE; + erts_aint32_t old = erts_smp_atomic32_xchg_nob(&msacc, new); + Eterm ref = erts_msacc_request(BIF_P, new, &threads); + if (is_non_value(ref)) + BIF_RET(old ? am_true : am_false); + BIF_TRAP3(await_msacc_mod_trap, + BIF_P, + ref, + old ? am_true : am_false, + threads); + } else if (BIF_ARG_2 == am_reset) { + Eterm ref = erts_msacc_request(BIF_P, ERTS_MSACC_RESET, &threads); + erts_aint32_t old = erts_smp_atomic32_read_nob(&msacc); + ASSERT(is_value(ref)); + BIF_TRAP3(await_msacc_mod_trap, + BIF_P, + ref, + old ? am_true : am_false, + threads); + } +#endif } else if (ERTS_IS_ATOM_STR("scheduling_statistics", BIF_ARG_1)) { int what; if (ERTS_IS_ATOM_STR("disable", BIF_ARG_2)) @@ -4915,8 +4953,12 @@ void erts_init_bif(void) await_port_send_result_trap = erts_export_put(am_erts_internal, am_await_port_send_result, 3); await_sched_wall_time_mod_trap - = erts_export_put(am_erlang, am_await_sched_wall_time_modifications, 2); + = erts_export_put(am_erlang, am_await_sched_wall_time_modifications, 2); + await_msacc_mod_trap + = erts_export_put(am_erts_internal, am_await_microstate_accounting_modifications, 3); + erts_smp_atomic32_init_nob(&sched_wall_time, 0); + erts_smp_atomic32_init_nob(&msacc, ERTS_MSACC_IS_ENABLED()); } #ifdef HARDDEBUG diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index 14e80960f5..71e4713624 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -233,12 +233,14 @@ ERTS_ALC_INLINE void *erts_alloc(ErtsAlcType_t type, Uint size) { void *res; + ERTS_MSACC_PUSH_AND_SET_STATE_X(ERTS_MSACC_STATE_ALLOC); res = (*erts_allctrs[ERTS_ALC_T2A(type)].alloc)( ERTS_ALC_T2N(type), erts_allctrs[ERTS_ALC_T2A(type)].extra, size); if (!res) erts_alloc_n_enomem(ERTS_ALC_T2N(type), size); + ERTS_MSACC_POP_STATE_X(); return res; } @@ -246,6 +248,7 @@ ERTS_ALC_INLINE void *erts_realloc(ErtsAlcType_t type, void *ptr, Uint size) { void *res; + ERTS_MSACC_PUSH_AND_SET_STATE_X(ERTS_MSACC_STATE_ALLOC); res = (*erts_allctrs[ERTS_ALC_T2A(type)].realloc)( ERTS_ALC_T2N(type), erts_allctrs[ERTS_ALC_T2A(type)].extra, @@ -253,37 +256,48 @@ void *erts_realloc(ErtsAlcType_t type, void *ptr, Uint size) size); if (!res) erts_realloc_n_enomem(ERTS_ALC_T2N(type), ptr, size); + ERTS_MSACC_POP_STATE_X(); return res; } ERTS_ALC_INLINE void erts_free(ErtsAlcType_t type, void *ptr) { + ERTS_MSACC_PUSH_AND_SET_STATE_X(ERTS_MSACC_STATE_ALLOC); (*erts_allctrs[ERTS_ALC_T2A(type)].free)( ERTS_ALC_T2N(type), erts_allctrs[ERTS_ALC_T2A(type)].extra, ptr); + ERTS_MSACC_POP_STATE_X(); } ERTS_ALC_INLINE void *erts_alloc_fnf(ErtsAlcType_t type, Uint size) { - return (*erts_allctrs[ERTS_ALC_T2A(type)].alloc)( + void *res; + ERTS_MSACC_PUSH_AND_SET_STATE_X(ERTS_MSACC_STATE_ALLOC); + res = (*erts_allctrs[ERTS_ALC_T2A(type)].alloc)( ERTS_ALC_T2N(type), erts_allctrs[ERTS_ALC_T2A(type)].extra, size); + ERTS_MSACC_POP_STATE_X(); + return res; } ERTS_ALC_INLINE void *erts_realloc_fnf(ErtsAlcType_t type, void *ptr, Uint size) { - return (*erts_allctrs[ERTS_ALC_T2A(type)].realloc)( + void *res; + ERTS_MSACC_PUSH_AND_SET_STATE_X(ERTS_MSACC_STATE_ALLOC); + res = (*erts_allctrs[ERTS_ALC_T2A(type)].realloc)( ERTS_ALC_T2N(type), erts_allctrs[ERTS_ALC_T2A(type)].extra, ptr, size); + ERTS_MSACC_POP_STATE_X(); + return res; } ERTS_ALC_INLINE diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index e0bc71c88a..6b7eff1428 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -361,6 +361,7 @@ type AINFO_REQ SHORT_LIVED SYSTEM alloc_info_request type SCHED_WTIME_REQ SHORT_LIVED SYSTEM sched_wall_time_request type GC_INFO_REQ SHORT_LIVED SYSTEM gc_info_request type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap +type MSACC LONG_LIVED SYSTEM microstate_accounting # # Types used by system specific code diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index be0bc0cfec..cdeeb5281b 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -400,13 +400,19 @@ static ERTS_INLINE void call_async_ready(ErtsAsync *a) ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); #endif if (!p) { - if (a->async_free) + if (a->async_free) { + ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_PORT); a->async_free(a->async_data); + ERTS_MSACC_POP_STATE(); + } } else { if (async_ready(p, a->async_data)) { - if (a->async_free) + if (a->async_free) { + ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_PORT); a->async_free(a->async_data); + ERTS_MSACC_POP_STATE(); + } } #if ERTS_USE_ASYNC_READY_Q erts_port_release(p); @@ -460,6 +466,8 @@ static erts_tse_t *async_thread_init(ErtsAsyncQ *aq) { ErtsThrQInit_t qinit = ERTS_THR_Q_INIT_DEFAULT; erts_tse_t *tse = erts_tse_fetch(); + ERTS_DECLARE_DUMMY(Uint no); + #ifdef ERTS_SMP ErtsThrPrgrCallbacks callbacks; @@ -483,10 +491,12 @@ static erts_tse_t *async_thread_init(ErtsAsyncQ *aq) /* Inform main thread that we are done initializing... */ erts_mtx_lock(&async->init.data.mtx); - async->init.data.no_initialized++; + no = async->init.data.no_initialized++; erts_cnd_signal(&async->init.data.cnd); erts_mtx_unlock(&async->init.data.mtx); + erts_msacc_init_thread("async", no, 0); + return tse; } @@ -494,6 +504,7 @@ static void *async_main(void* arg) { ErtsAsyncQ *aq = (ErtsAsyncQ *) arg; erts_tse_t *tse = async_thread_init(aq); + ERTS_MSACC_DECLARE_CACHE(); while (1) { ErtsThrQPrepEnQ_t *prep_enq; @@ -501,11 +512,14 @@ static void *async_main(void* arg) if (is_nil(a->port)) break; /* Time to die */ + ERTS_MSACC_UPDATE_CACHE(); + #if ERTS_ASYNC_PRINT_JOB erts_fprintf(stderr, "<- %ld\n", a->async_id); #endif - + ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_PORT); a->async_invoke(a->async_data); + ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_OTHER); async_reply(a, prep_enq); } @@ -628,10 +642,13 @@ long driver_async(ErlDrvPort ix, unsigned int* key, unsigned int qix; #if ERTS_USE_ASYNC_READY_Q Uint sched_id; + ERTS_MSACC_PUSH_STATE(); sched_id = erts_get_scheduler_id(); if (!sched_id) sched_id = 1; +#else + ERTS_MSACC_PUSH_STATE(); #endif prt = erts_drvport2port(ix); @@ -684,12 +701,17 @@ long driver_async(ErlDrvPort ix, unsigned int* key, return id; } #endif - + + ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_PORT); (*a->async_invoke)(a->async_data); + ERTS_MSACC_POP_STATE(); if (async_ready(prt, a->async_data)) { - if (a->async_free != NULL) + if (a->async_free != NULL) { + ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_PORT); (*a->async_free)(a->async_data); + ERTS_MSACC_POP_STATE(); + } } erts_free(ERTS_ALC_T_ASYNC, (void *) a); diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 06e7dc8661..017339e1f6 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -64,6 +64,7 @@ static Export* alloc_sizes_trap = NULL; static Export* gather_io_bytes_trap = NULL; static Export *gather_sched_wall_time_res_trap; +static Export *gather_msacc_res_trap; static Export *gather_gc_info_res_trap; #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) @@ -3260,6 +3261,14 @@ BIF_RETTYPE statistics_1(BIF_ALIST_1) szp = NULL; hpp = &hp; } +#ifdef ERTS_ENABLE_MSACC + } else if (BIF_ARG_1 == am_microstate_accounting) { + Eterm threads; + res = erts_msacc_request(BIF_P, ERTS_MSACC_GATHER, &threads); + if (is_non_value(res)) + BIF_RET(am_undefined); + BIF_TRAP2(gather_msacc_res_trap, BIF_P, res, threads); +#endif } else if (BIF_ARG_1 == am_context_switches) { Eterm cs = erts_make_integer(erts_get_total_context_switches(), BIF_P); hp = HAlloc(BIF_P, 3); @@ -4396,6 +4405,9 @@ erts_bif_info_init(void) = erts_export_put(am_erlang, am_gather_gc_info_result, 1); gather_io_bytes_trap = erts_export_put(am_erts_internal, am_gather_io_bytes, 2); + gather_msacc_res_trap + = erts_export_put(am_erts_internal, am_gather_microstate_accounting_result, 2); + process_info_init(); os_info_init(); } diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 06acece471..21b03ae8bd 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -576,6 +576,7 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, int reds; ErtsMonotonicTime start_time = 0; /* Shut up faulty warning... */ ErtsSchedulerData *esdp; + ERTS_MSACC_PUSH_STATE_M(); #ifdef USE_VM_PROBES DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); #endif @@ -588,6 +589,8 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, else if (p->live_hf_end != ERTS_INVALID_HFRAG_PTR) live_hf_end = p->live_hf_end; + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_GC); + esdp = erts_get_scheduler_data(); if (IS_TRACED_FL(p, F_TRACE_GC)) { @@ -624,9 +627,11 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, } else { do_major_collection: + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_GC_FULL); DTRACE2(gc_major_start, pidbuf, need); reds = major_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); DTRACE2(gc_major_end, pidbuf, reclaimed_now); + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_GC); } reset_active_writer(p); @@ -669,6 +674,8 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, FLAGS(p) &= ~F_FORCE_GC; p->live_hf_end = ERTS_INVALID_HFRAG_PTR; + ERTS_MSACC_POP_STATE_M(); + #ifdef CHECK_FOR_HOLES /* * We intentionally do not rescan the areas copied by the GC. diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 42aca726bf..520b504fcb 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -394,6 +394,7 @@ erl_init(int ncpu, #endif packet_parser_init(); erl_nif_init(); + erts_msacc_init(); } static Eterm @@ -1193,6 +1194,7 @@ early_init(int *argc, char **argv) /* erts_thr_late_init(&elid); } #endif + erts_msacc_early_init(); #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_late_init(); @@ -2220,6 +2222,7 @@ erl_start(int argc, char **argv) #else { ErtsSchedulerData *esdp = erts_get_scheduler_data(); + erts_msacc_init_thread("scheduler", 1, 1); erts_thr_set_main_status(1, 1); #if ERTS_USE_ASYNC_READY_Q esdp->aux_work_data.async_ready.queue diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index f7b4bd8041..598bf84c0b 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -137,6 +137,8 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "mmap_init_atoms", NULL }, { "drv_tsd", NULL }, { "async_enq_mtx", NULL }, + { "msacc_list_mutex", NULL }, + { "msacc_unmanaged_mutex", NULL }, #ifdef ERTS_SMP { "atom_tab", NULL }, { "misc_op_list_pre_alloc_lock", "address" }, diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c new file mode 100644 index 0000000000..bf1c06dea7 --- /dev/null +++ b/erts/emulator/beam/erl_msacc.c @@ -0,0 +1,486 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2014-2015. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +/* + * Description: Microstate accounting. + * + * We keep track of the different states that the + * Erlang VM threads are in, in order to provide + * performance/debugging statistics. There is a + * small overhead in enabling this, but in the big + * scheme of things it should be negligible. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define ERTS_MSACC_STATE_STRINGS 1 + +#include "sys.h" +#include "global.h" +#include "erl_threads.h" +#include "erl_bif_unique.h" +#include "erl_map.h" +#include "erl_msacc.h" + +#if ERTS_ENABLE_MSACC + +static Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, Eterm **hpp, Uint *szp); +static void erts_msacc_reset(ErtsMsAcc *msacc); +static ErtsMsAcc* get_msacc(void); + +#ifdef USE_THREADS +erts_tsd_key_t ERTS_WRITE_UNLIKELY(erts_msacc_key); +#else +ErtsMsAcc *ERTS_WRITE_UNLIKELY(erts_msacc) = NULL; +#endif +int ERTS_WRITE_UNLIKELY(erts_msacc_enabled); + +static Eterm *erts_msacc_state_atoms = NULL; +static erts_rwmtx_t msacc_mutex; +static ErtsMsAcc *msacc_managed = NULL; +#ifdef USE_THREADS +static ErtsMsAcc *msacc_unmanaged = NULL; +static Uint msacc_unmanaged_count = 0; +#endif + +/* we have to split initiation as atoms are not inited in early init */ +void erts_msacc_early_init(void) { +#ifndef ERTS_MSACC_ALWAYS_ON + erts_msacc_enabled = 0; +#endif + erts_rwmtx_init(&msacc_mutex,"msacc_list_mutex"); +#ifdef USE_THREADS + erts_tsd_key_create(&erts_msacc_key,"erts_msacc_key"); +#else + erts_msacc = NULL; +#endif +} + +void erts_msacc_init(void) { + int i; + erts_msacc_state_atoms = erts_alloc(ERTS_ALC_T_MSACC, + sizeof(Eterm)*ERTS_MSACC_STATE_COUNT); + for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) { + erts_msacc_state_atoms[i] = am_atom_put(erts_msacc_states[i], + strlen(erts_msacc_states[i])); + } +} + +void erts_msacc_init_thread(char *type, int id, int managed) { + ErtsMsAcc *msacc; + + msacc = erts_alloc(ERTS_ALC_T_MSACC, sizeof(ErtsMsAcc)); + + msacc->type = strdup(type); + msacc->id = make_small(id); + msacc->unmanaged = !managed; + msacc->tid = erts_thr_self(); + msacc->perf_counter = 0; + +#ifdef USE_THREADS + erts_rwmtx_rwlock(&msacc_mutex); + if (!managed) { + erts_mtx_init(&msacc->mtx,"msacc_unmanaged_mutex"); + msacc->next = msacc_unmanaged; + msacc_unmanaged = msacc; + msacc_unmanaged_count++; + ERTS_MSACC_TSD_SET(msacc); + } else { + msacc->next = msacc_managed; + msacc_managed = msacc; + } + erts_rwmtx_rwunlock(&msacc_mutex); +#else + msacc_managed = msacc; +#endif + + erts_msacc_reset(msacc); + +#ifdef ERTS_MSACC_ALWAYS_ON + ERTS_MSACC_TSD_SET(msacc); + sys_perf_counter(&msacc->perf_counter); + msacc->state = ERTS_MSACC_STATE_OTHER; +#endif +} + +/* + * Creates a structure looking like this + * #{ type => scheduler, id => 1, counters => #{ State1 => Counter1 ... StateN => CounterN}} + */ +static +Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, Eterm **hpp, Uint *szp) { + int i; + Eterm *hp; + Eterm key, state_key, state_map; + Eterm res = THE_NON_VALUE; + flatmap_t *map; + + if (szp) { + *szp += MAP_HEADER_FLATMAP_SZ + 1 + 2*(3); + *szp += MAP_HEADER_FLATMAP_SZ + 1 + 2*(ERTS_MSACC_STATE_COUNT); + for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) { + (void)erts_bld_sint64(NULL,szp,(Sint64)msacc->perf_counters[i]); +#ifdef ERTS_MSACC_STATE_COUNTERS + (void)erts_bld_uint64(NULL,szp,msacc->state_counters[i]); + *szp += 3; /* tuple to put state+perf counter in */ +#endif + } + } + + if (hpp) { + Eterm counters[ERTS_MSACC_STATE_COUNT]; + hp = *hpp; + for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) { + Eterm counter = erts_bld_sint64(&hp,NULL,(Sint64)msacc->perf_counters[i]); +#ifdef ERTS_MSACC_STATE_COUNTERS + Eterm counter__ = erts_bld_uint64(&hp,NULL,msacc->state_counters[i]); + counters[i] = TUPLE2(hp,counter,counter__); + hp += 3; +#else + counters[i] = counter; +#endif + } + + key = TUPLE3(hp,am_counters,am_id,am_type); + hp += 4; + + state_key = make_tuple(hp); + hp[0] = make_arityval(ERTS_MSACC_STATE_COUNT); + + for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) + hp[1+i] = erts_msacc_state_atoms[i]; + hp += 1 + ERTS_MSACC_STATE_COUNT; + + map = (flatmap_t*)hp; + hp += MAP_HEADER_FLATMAP_SZ; + map->thing_word = MAP_HEADER_FLATMAP; + map->size = ERTS_MSACC_STATE_COUNT; + map->keys = state_key; + for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) + hp[i] = counters[i]; + hp += ERTS_MSACC_STATE_COUNT; + state_map = make_flatmap(map); + + map = (flatmap_t*)hp; + hp += MAP_HEADER_FLATMAP_SZ; + map->thing_word = MAP_HEADER_FLATMAP; + map->size = 3; + map->keys = key; + hp[0] = state_map; + hp[1] = msacc->id; + hp[2] = am_atom_put(msacc->type,strlen(msacc->type)); + hp += 3; + + *hpp = hp; + res = make_flatmap(map); + } + + return res; +} + +typedef struct { + int action; + Process *proc; + Eterm ref; + Eterm ref_heap[REF_THING_SIZE]; + Uint req_sched; + erts_smp_atomic32_t refc; +} ErtsMSAccReq; + +static ErtsMsAcc* get_msacc(void) { + ErtsMsAcc *msacc; + erts_rwmtx_rlock(&msacc_mutex); + msacc = msacc_managed; + while (!erts_equal_tids(msacc->tid,erts_thr_self())) { + msacc = msacc->next; + ASSERT(msacc != NULL); + } + erts_rwmtx_runlock(&msacc_mutex); + return msacc; +} + +static void send_reply(ErtsMsAcc *msacc, ErtsMSAccReq *msaccrp) { + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + Process *rp = msaccrp->proc; + ErtsMessage *msgp = NULL; + Eterm **hpp, *hp; + Eterm ref_copy = NIL, msg; + Uint sz, *szp; + ErlOffHeap *ohp = NULL; + ErtsProcLocks rp_locks = (esdp && msaccrp->req_sched == esdp->no + ? ERTS_PROC_LOCK_MAIN : 0); + + sz = 0; + hpp = NULL; + szp = &sz; + + if (msacc->unmanaged) erts_mtx_lock(&msacc->mtx); + + while (1) { + if (hpp) + ref_copy = STORE_NC(hpp, ohp, msaccrp->ref); + else + *szp += REF_THING_SIZE; + + if (msaccrp->action != ERTS_MSACC_GATHER) + msg = ref_copy; + else { + msg = erts_msacc_gather_stats(msacc, hpp, szp); + msg = erts_bld_tuple(hpp, szp, 2, ref_copy, msg); + } + if (hpp) + break; + + msgp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp); + hpp = &hp; + szp = NULL; + } + + if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx); + + erts_queue_message(rp, &rp_locks, msgp, msg, NIL); + + if (esdp && msaccrp->req_sched == esdp->no) + rp_locks &= ~ERTS_PROC_LOCK_MAIN; + + if (rp_locks) + erts_smp_proc_unlock(rp, rp_locks); + +} + +static void +reply_msacc(void *vmsaccrp) +{ + ErtsMsAcc *msacc = ERTS_MSACC_TSD_GET(); + ErtsMSAccReq *msaccrp = (ErtsMSAccReq *) vmsaccrp; + + ASSERT(!msacc || !msacc->unmanaged); + + if (msaccrp->action == ERTS_MSACC_ENABLE && !msacc) { + msacc = get_msacc(); + + sys_perf_counter(&msacc->perf_counter); + + msacc->state = ERTS_MSACC_STATE_OTHER; + + ERTS_MSACC_TSD_SET(msacc); + + } else if (msaccrp->action == ERTS_MSACC_DISABLE && msacc) { + ERTS_MSACC_TSD_SET(NULL); + } else if (msaccrp->action == ERTS_MSACC_RESET) { + msacc = msacc ? msacc : get_msacc(); + erts_msacc_reset(msacc); + } else if (msaccrp->action == ERTS_MSACC_GATHER && !msacc) { + msacc = get_msacc(); + } + + ASSERT(!msacc || !msacc->unmanaged); + + send_reply(msacc, msaccrp); + + erts_proc_dec_refc(msaccrp->proc); + + if (erts_smp_atomic32_dec_read_nob(&msaccrp->refc) == 0) + erts_free(ERTS_ALC_T_MSACC, vmsaccrp); +} + +static void erts_msacc_reset(ErtsMsAcc *msacc) { + int i; + if (msacc->unmanaged) erts_mtx_lock(&msacc->mtx); + + for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) { + msacc->perf_counters[i] = 0; +#ifdef ERTS_MSACC_STATE_COUNTERS + msacc->state_counters[i] = 0; +#endif + } + + if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx); +} + +#endif /* ERTS_ENABLE_MSACC */ + + +/* + * This function is responsible for enabling, disabling, resetting and + * gathering data related to microstate accounting. + * + * Managed threads and unmanaged threads are handled differently. + * - managed threads get a misc_aux job telling them to switch on msacc + * - unmanaged have some fields protected by a mutex that has to be taken + * before any values can be updated + * + * For performance reasons there is also a global value erts_msacc_enabled + * that controls the state of all threads. Statistics gathering is only on + * if erts_msacc_enabled && msacc is true. + */ +Eterm +erts_msacc_request(Process *c_p, int action, Eterm *threads) +{ +#ifdef ERTS_ENABLE_MSACC + ErtsMsAcc *msacc = ERTS_MSACC_TSD_GET(); + ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p); + Eterm ref; + ErtsMSAccReq *msaccrp; + Eterm *hp; + + +#ifdef ERTS_MSACC_ALWAYS_ON + if (action == ERTS_MSACC_ENABLE || action == ERTS_MSACC_DISABLE) + return THE_NON_VALUE; +#else + /* take care of double enable, and double disable here */ + if (msacc && action == ERTS_MSACC_ENABLE) { + return THE_NON_VALUE; + } else if (!msacc && action == ERTS_MSACC_DISABLE) { + return THE_NON_VALUE; + } +#endif + + ref = erts_make_ref(c_p); + + msaccrp = erts_alloc(ERTS_ALC_T_MSACC, sizeof(ErtsMSAccReq)); + hp = &msaccrp->ref_heap[0]; + + msaccrp->action = action; + msaccrp->proc = c_p; + msaccrp->ref = STORE_NC(&hp, NULL, ref); + msaccrp->req_sched = esdp->no; + +#ifdef ERTS_SMP + *threads = erts_no_schedulers; + *threads += 1; /* aux thread */ +#else + *threads = 1; +#endif + + erts_smp_atomic32_init_nob(&msaccrp->refc,(erts_aint32_t)*threads); + + erts_proc_add_refc(c_p, *threads); + + if (erts_no_schedulers > 1) + erts_schedule_multi_misc_aux_work(1, + erts_no_schedulers, + reply_msacc, + (void *) msaccrp); +#ifdef ERTS_SMP + /* aux thread */ + erts_schedule_misc_aux_work(0, reply_msacc, (void *) msaccrp); +#endif + +#ifdef USE_THREADS + /* Manage unmanaged threads */ + switch (action) { + case ERTS_MSACC_GATHER: { + Uint unmanaged_count; + ErtsMsAcc *msacc, **unmanaged; + int i = 0; + + /* we copy a list of pointers here so that we do not have to have + the msacc_mutex when sending messages */ + erts_rwmtx_rlock(&msacc_mutex); + unmanaged_count = msacc_unmanaged_count; + unmanaged = erts_alloc(ERTS_ALC_T_MSACC, + sizeof(ErtsMsAcc*)*unmanaged_count); + + for (i = 0, msacc = msacc_unmanaged; + i < unmanaged_count; + i++, msacc = msacc->next) { + unmanaged[i] = msacc; + } + erts_rwmtx_runlock(&msacc_mutex); + + for (i = 0; i < unmanaged_count; i++) { + erts_mtx_lock(&unmanaged[i]->mtx); + if (unmanaged[i]->perf_counter) { + ErtsSysHrTime perf_counter; + /* if enabled update stats */ + sys_perf_counter(&perf_counter); + unmanaged[i]->perf_counters[unmanaged[i]->state] += + perf_counter - unmanaged[i]->perf_counter; + unmanaged[i]->perf_counter = perf_counter; + } + erts_mtx_unlock(&unmanaged[i]->mtx); + send_reply(unmanaged[i],msaccrp); + } + erts_free(ERTS_ALC_T_MSACC,unmanaged); + /* We have just sent unmanaged_count messages, so bump no of threads */ + *threads += unmanaged_count; + break; + } + case ERTS_MSACC_RESET: { + ErtsMsAcc *msacc; + erts_rwmtx_rlock(&msacc_mutex); + for (msacc = msacc_unmanaged; msacc != NULL; msacc = msacc->next) + erts_msacc_reset(msacc); + erts_rwmtx_runlock(&msacc_mutex); + break; + } + case ERTS_MSACC_ENABLE: { + erts_rwmtx_rlock(&msacc_mutex); + for (msacc = msacc_unmanaged; msacc != NULL; msacc = msacc->next) { + erts_mtx_lock(&msacc->mtx); + sys_perf_counter(&msacc->perf_counter); + /* we assume the unmanaged thread is sleeping */ + msacc->state = ERTS_MSACC_STATE_SLEEP; + erts_mtx_unlock(&msacc->mtx); + } + erts_rwmtx_runlock(&msacc_mutex); + break; + } + case ERTS_MSACC_DISABLE: { + ErtsSysHrTime perf_counter; + erts_rwmtx_rlock(&msacc_mutex); + /* make sure to update stats with latest results */ + for (msacc = msacc_unmanaged; msacc != NULL; msacc = msacc->next) { + erts_mtx_lock(&msacc->mtx); + sys_perf_counter(&perf_counter); + msacc->perf_counters[msacc->state] += perf_counter - msacc->perf_counter; + msacc->perf_counter = 0; + erts_mtx_unlock(&msacc->mtx); + } + erts_rwmtx_runlock(&msacc_mutex); + break; + } + default: { ASSERT(0); } + } + +#endif + + *threads = make_small(*threads); + + reply_msacc((void *) msaccrp); + +#ifndef ERTS_MSACC_ALWAYS_ON + /* enable/disable the global value */ + if (action == ERTS_MSACC_ENABLE) { + erts_msacc_enabled = 1; + } else if (action == ERTS_MSACC_DISABLE) { + erts_msacc_enabled = 0; + } +#endif + + return ref; +#else + return THE_NON_VALUE; +#endif +} diff --git a/erts/emulator/beam/erl_msacc.h b/erts/emulator/beam/erl_msacc.h new file mode 100644 index 0000000000..de72fe9597 --- /dev/null +++ b/erts/emulator/beam/erl_msacc.h @@ -0,0 +1,409 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2014-2015. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef ERL_MSACC_H__ +#define ERL_MSACC_H__ + +/* Can be enabled/disabled via configure */ +#if ERTS_ENABLE_MSACC == 2 +#define ERTS_MSACC_EXTENDED_STATES 1 +#endif + +/* Uncomment this to also count the number of + transitions to a state. This will add a count + to the counter map. */ +/* #define ERTS_MSACC_STATE_COUNTERS 1 */ + +/* Uncomment this to make msacc to always be on, + this reduces overhead a little bit when profiling */ +/* #define ERTS_MSACC_ALWAYS_ON 1 */ + +#define ERTS_MSACC_DISABLE 0 +#define ERTS_MSACC_ENABLE 1 +#define ERTS_MSACC_RESET 2 +#define ERTS_MSACC_GATHER 3 + +/* + * When adding a new state, you have to: + * * Add it here + * * Increment ERTS_MSACC_STATE_COUNT + * * Add string value to erts_msacc_states + * * Have to be in alphabetical order! + * * Only add states to the non-extended section after + * careful benchmarking to make sure the overhead + * when disabled is minimal. + */ + +#ifndef ERTS_MSACC_EXTENDED_STATES +#define ERTS_MSACC_STATE_AUX 0 +#define ERTS_MSACC_STATE_CHECK_IO 1 +#define ERTS_MSACC_STATE_EMULATOR 2 +#define ERTS_MSACC_STATE_GC 3 +#define ERTS_MSACC_STATE_OTHER 4 +#define ERTS_MSACC_STATE_PORT 5 +#define ERTS_MSACC_STATE_SLEEP 6 + +#define ERTS_MSACC_STATE_COUNT 7 + +#if ERTS_MSACC_STATE_STRINGS && ERTS_ENABLE_MSACC +static char *erts_msacc_states[] = { + "aux", + "check_io", + "emulator", + "gc", + "other", + "port", + "sleep" +}; +#endif + +#else + +#define ERTS_MSACC_STATE_ALLOC 0 +#define ERTS_MSACC_STATE_AUX 1 +#define ERTS_MSACC_STATE_BIF 2 +#define ERTS_MSACC_STATE_BUSY_WAIT 3 +#define ERTS_MSACC_STATE_CHECK_IO 4 +#define ERTS_MSACC_STATE_EMULATOR 5 +#define ERTS_MSACC_STATE_ETS 6 +#define ERTS_MSACC_STATE_GC 7 +#define ERTS_MSACC_STATE_GC_FULL 8 +#define ERTS_MSACC_STATE_NIF 9 +#define ERTS_MSACC_STATE_OTHER 10 +#define ERTS_MSACC_STATE_PORT 11 +#define ERTS_MSACC_STATE_SEND 12 +#define ERTS_MSACC_STATE_SLEEP 13 +#define ERTS_MSACC_STATE_TIMERS 14 + +#define ERTS_MSACC_STATE_COUNT 15 + +#if ERTS_MSACC_STATE_STRINGS +static char *erts_msacc_states[] = { + "alloc", + "aux", + "bif", + "busy_wait", + "check_io", + "emulator", + "ets", + "gc", + "gc_full", + "nif", + "other", + "port", + "send", + "sleep", + "timers" +}; +#endif + +#endif + +typedef struct erl_msacc_t_ ErtsMsAcc; + +struct erl_msacc_t_ { + + /* the the values below are protected by mtx iff unmanaged = 1 */ + ErtsSysHrTime perf_counter; + ErtsSysHrTime perf_counters[ERTS_MSACC_STATE_COUNT]; +#ifdef ERTS_MSACC_STATE_COUNTERS + Uint64 state_counters[ERTS_MSACC_STATE_COUNT]; +#endif + Uint state; + + /* protected by msacc_mutex in erl_msacc.c, and should be constant */ + int unmanaged; + erts_mtx_t mtx; + ErtsMsAcc *next; + erts_tid_t tid; + Eterm id; + char *type; +}; + +#if ERTS_ENABLE_MSACC + +#define ERTS_MSACC_INLINE ERTS_GLB_INLINE + +#ifdef USE_THREADS +extern erts_tsd_key_t erts_msacc_key; +#else +extern ErtsMsAcc *erts_msacc; +#endif + +#ifdef ERTS_MSACC_ALWAYS_ON +#define erts_msacc_enabled 1 +#else +extern int erts_msacc_enabled; +#endif + +#ifdef USE_THREADS +#define ERTS_MSACC_TSD_GET() erts_tsd_get(erts_msacc_key) +#define ERTS_MSACC_TSD_SET(tsd) erts_tsd_set(erts_msacc_key,tsd) +#else +#define ERTS_MSACC_TSD_GET() erts_msacc +#define ERTS_MSACC_TSD_SET(tsd) erts_msacc = tsd +#endif + +void erts_msacc_early_init(void); +void erts_msacc_init(void); +void erts_msacc_init_thread(char *type, int id, int liberty); + +/* The defines below are used to instrument the vm code + * with different state changes. There are two variants + * of each define. One that has a cached ErtsMsAcc * + * that it can use, and one that does not. + * The cached values are necessary to have in order to get + * low enough overhead when running without msacc enabled. + * + * The two most common patterns to use the function with are: + * + * ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_NEWSTATE); + * ... call other function in new state ... + * ERTS_MSACC_POP_STATE(); + * + * Note that the erts_msacc_push* function declare new variables, so + * to conform with C89 we have to call it in the beginning of a function. + * We might not want to change state it the beginning though, so we use this: + * + * ERTS_MSACC_PUSH_STATE(); + * ... some other code ... + * ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_NEWSTATE); + * ... call other function in new state ... + * ERTS_MSACC_POP_STATE(); + * + * Notice that we used the cached version of set_state as push_state already + * read the erts_msacc_enabled to the cache. + * + * Most macros also have other variants with the suffix _m which means that + * they are known to only be called in managed threads, or with the _x suffix + * which means that it should only be used in an emulator compiled with + * extended states. + * + * Here is a listing of the entire api: + * + * void ERTS_MSACC_DECLARE_CACHE() + * void ERTS_MSACC_UPDATE_CACHE() + * void ERTS_MSACC_IS_ENABLED() + * void ERTS_MSACC_IS_ENABLED_CACHED() + * + * void ERTS_MSACC_PUSH_STATE() + * void ERTS_MSACC_SET_STATE(int state) + * void ERTS_MSACC_PUSH_AND_SET_STATE(int state) + * + * void ERTS_MSACC_PUSH_STATE_CACHED() + * void ERTS_MSACC_SET_STATE_CACHED(int state) + * void ERTS_MSACC_PUSH_AND_SET_STATE_CACHED(int state) + * void ERTS_MSACC_POP_STATE() + * + * void ERTS_MSACC_PUSH_STATE_M() + * void ERTS_MSACC_PUSH_STATE_CACHED_M() + * void ERTS_MSACC_SET_STATE_CACHED_M(int state) + * void ERTS_MSACC_SET_STATE_M(int state) + * void ERTS_MSACC_POP_STATE_M() + * void ERTS_MSACC_PUSH_AND_SET_STATE_M(int state) + * + * Most functions are also available with an _x suffix that are only enabled + * when using the extra states. If they are not, just add them to the end + * of this file. + */ + +/* cache handling functions */ +#define ERTS_MSACC_IS_ENABLED() ERTS_UNLIKELY(erts_msacc_enabled) +#define ERTS_MSACC_DECLARE_CACHE() \ + ErtsMsAcc *ERTS_MSACC_UPDATE_CACHE(); \ + ERTS_DECLARE_DUMMY(Uint __erts_msacc_state) = ERTS_MSACC_STATE_OTHER +#define ERTS_MSACC_IS_ENABLED_CACHED() ERTS_UNLIKELY(__erts_msacc_cache != NULL) +#define ERTS_MSACC_UPDATE_CACHE() \ + __erts_msacc_cache = erts_msacc_enabled ? ERTS_MSACC_TSD_GET() : NULL + + +/* The defines below implicitly declare and load a new cache */ +#define ERTS_MSACC_PUSH_STATE() \ + ERTS_MSACC_DECLARE_CACHE(); \ + ERTS_MSACC_PUSH_STATE_CACHED() +#define ERTS_MSACC_SET_STATE(state) \ + ERTS_MSACC_DECLARE_CACHE(); \ + ERTS_MSACC_SET_STATE_CACHED(state) +#define ERTS_MSACC_PUSH_AND_SET_STATE(state) \ + ERTS_MSACC_PUSH_STATE(); ERTS_MSACC_SET_STATE_CACHED(state) + +/* The defines below need an already declared cache to work */ +#define ERTS_MSACC_PUSH_STATE_CACHED() \ + __erts_msacc_state = ERTS_MSACC_IS_ENABLED_CACHED() ? \ + erts_msacc_get_state_um__(__erts_msacc_cache) : ERTS_MSACC_STATE_OTHER +#define ERTS_MSACC_SET_STATE_CACHED(state) \ + if (ERTS_MSACC_IS_ENABLED_CACHED()) \ + erts_msacc_set_state_um__(__erts_msacc_cache, state, 1) +#define ERTS_MSACC_PUSH_AND_SET_STATE_CACHED(state) \ + ERTS_MSACC_PUSH_STATE_CACHED(); ERTS_MSACC_SET_STATE_CACHED(state) +#define ERTS_MSACC_POP_STATE() \ + if (ERTS_MSACC_IS_ENABLED_CACHED()) \ + erts_msacc_set_state_um__(__erts_msacc_cache, __erts_msacc_state, 0) + +/* Only use these defines when we know that we have in a managed thread */ +#define ERTS_MSACC_PUSH_STATE_M() \ + ERTS_MSACC_DECLARE_CACHE(); \ + ERTS_MSACC_PUSH_STATE_CACHED_M() +#define ERTS_MSACC_PUSH_STATE_CACHED_M() \ + __erts_msacc_state = ERTS_MSACC_IS_ENABLED_CACHED() ? \ + erts_msacc_get_state_m__(__erts_msacc_cache) : ERTS_MSACC_STATE_OTHER +#define ERTS_MSACC_SET_STATE_M(state) \ + ERTS_MSACC_DECLARE_CACHE(); \ + ERTS_MSACC_SET_STATE_CACHED_M(state) +#define ERTS_MSACC_SET_STATE_CACHED_M(state) \ + if (ERTS_MSACC_IS_ENABLED_CACHED()) \ + erts_msacc_set_state_m__(__erts_msacc_cache, state, 1) +#define ERTS_MSACC_POP_STATE_M() \ + if (ERTS_MSACC_IS_ENABLED_CACHED()) \ + erts_msacc_set_state_m__(__erts_msacc_cache, __erts_msacc_state, 0) +#define ERTS_MSACC_PUSH_AND_SET_STATE_M(state) \ + ERTS_MSACC_PUSH_STATE_M(); ERTS_MSACC_SET_STATE_CACHED_M(state) + +ERTS_MSACC_INLINE +void erts_msacc_set_state_um__(ErtsMsAcc *msacc,Uint state,int increment); +ERTS_MSACC_INLINE +void erts_msacc_set_state_m__(ErtsMsAcc *msacc,Uint state,int increment); + +ERTS_MSACC_INLINE +Uint erts_msacc_get_state_um__(ErtsMsAcc *msacc); +ERTS_MSACC_INLINE +Uint erts_msacc_get_state_m__(ErtsMsAcc *msacc); + + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_MSACC_INLINE +Uint erts_msacc_get_state_um__(ErtsMsAcc *msacc) { + Uint state; + if (msacc->unmanaged) + erts_mtx_lock(&msacc->mtx); + state = msacc->state; + if (msacc->unmanaged) + erts_mtx_unlock(&msacc->mtx); + return state; +} + +ERTS_MSACC_INLINE +Uint erts_msacc_get_state_m__(ErtsMsAcc *msacc) { + return msacc->state; +} + +ERTS_MSACC_INLINE +void erts_msacc_set_state_um__(ErtsMsAcc *msacc, Uint new_state, int increment) { + if (ERTS_UNLIKELY(msacc->unmanaged)) { + erts_mtx_lock(&msacc->mtx); + msacc->state = new_state; + if (ERTS_LIKELY(!msacc->perf_counter)) { + erts_mtx_unlock(&msacc->mtx); + return; + } + } + + erts_msacc_set_state_m__(msacc,new_state,increment); + + if (ERTS_UNLIKELY(msacc->unmanaged)) + erts_mtx_unlock(&msacc->mtx); +} + +ERTS_MSACC_INLINE +void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) { + ErtsSysHrTime prev_perf_counter; + Sint64 diff; + + if (new_state == msacc->state) + return; + + prev_perf_counter = msacc->perf_counter; + sys_perf_counter(&msacc->perf_counter); + diff = msacc->perf_counter - prev_perf_counter; + ASSERT(diff >= 0); + msacc->perf_counters[msacc->state] += diff; +#ifdef ERTS_MSACC_STATE_COUNTERS + msacc->state_counters[new_state] += increment; +#endif + msacc->state = new_state; +} + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +#else + +#define ERTS_MSACC_IS_ENABLED() 0 +#define erts_msacc_early_init() +#define erts_msacc_init() +#define erts_msacc_init_thread(type, id, liberty) +#define ERTS_MSACC_PUSH_STATE() +#define ERTS_MSACC_PUSH_STATE_CACHED() +#define ERTS_MSACC_POP_STATE() +#define ERTS_MSACC_SET_STATE(state) +#define ERTS_MSACC_SET_STATE_CACHED(state) +#define ERTS_MSACC_PUSH_AND_SET_STATE(state) +#define ERTS_MSACC_PUSH_AND_SET_STATE_CACHED(state) +#define ERTS_MSACC_UPDATE_CACHE() +#define ERTS_MSACC_IS_ENABLED_CACHED() +#define ERTS_MSACC_DECLARE_CACHE() +#define ERTS_MSACC_PUSH_STATE_M() +#define ERTS_MSACC_PUSH_STATE_CACHED_M() +#define ERTS_MSACC_SET_STATE_CACHED_M(state) +#define ERTS_MSACC_POP_STATE_M() +#define ERTS_MSACC_PUSH_AND_SET_STATE_M(state) + + +#endif /* ERTS_ENABLE_MSACC */ + +#ifndef ERTS_MSACC_EXTENDED_STATES + +#define ERTS_MSACC_PUSH_STATE_X() +#define ERTS_MSACC_POP_STATE_X() +#define ERTS_MSACC_SET_STATE_X(state) +#define ERTS_MSACC_SET_STATE_M_X(state) +#define ERTS_MSACC_SET_STATE_CACHED_X(state) +#define ERTS_MSACC_PUSH_AND_SET_STATE_X(state) +#define ERTS_MSACC_PUSH_AND_SET_STATE_CACHED_X(state) +#define ERTS_MSACC_UPDATE_CACHE_X() +#define ERTS_MSACC_IS_ENABLED_CACHED_X() 0 +#define ERTS_MSACC_DECLARE_CACHE_X() +#define ERTS_MSACC_PUSH_STATE_M_X() +#define ERTS_MSACC_PUSH_STATE_CACHED_M_X() +#define ERTS_MSACC_SET_STATE_CACHED_M_X(state) +#define ERTS_MSACC_POP_STATE_M_X() +#define ERTS_MSACC_PUSH_AND_SET_STATE_M_X(state) + +#else + +#define ERTS_MSACC_PUSH_STATE_X() ERTS_MSACC_PUSH_STATE() +#define ERTS_MSACC_POP_STATE_X() ERTS_MSACC_POP_STATE() +#define ERTS_MSACC_SET_STATE_X(state) ERTS_MSACC_SET_STATE(state) +#define ERTS_MSACC_SET_STATE_M_X(state) ERTS_MSACC_SET_STATE_M(state) +#define ERTS_MSACC_SET_STATE_CACHED_X(state) ERTS_MSACC_SET_STATE_CACHED(state) +#define ERTS_MSACC_PUSH_AND_SET_STATE_X(state) ERTS_MSACC_PUSH_AND_SET_STATE(state) +#define ERTS_MSACC_PUSH_AND_SET_STATE_CACHED_X(state) ERTS_MSACC_PUSH_AND_SET_STATE_CACHED(state) +#define ERTS_MSACC_UPDATE_CACHE_X() ERTS_MSACC_UPDATE_CACHE() +#define ERTS_MSACC_IS_ENABLED_CACHED_X() ERTS_MSACC_IS_ENABLED_CACHED() +#define ERTS_MSACC_DECLARE_CACHE_X() ERTS_MSACC_DECLARE_CACHE() +#define ERTS_MSACC_PUSH_STATE_M_X() ERTS_MSACC_PUSH_STATE_M() +#define ERTS_MSACC_PUSH_STATE_CACHED_M_X() ERTS_MSACC_PUSH_STATE_CACHED_M() +#define ERTS_MSACC_SET_STATE_CACHED_M_X(state) ERTS_MSACC_SET_STATE_CACHED_M(state) +#define ERTS_MSACC_POP_STATE_M_X() ERTS_MSACC_POP_STATE_M() +#define ERTS_MSACC_PUSH_AND_SET_STATE_M_X(state) ERTS_MSACC_PUSH_AND_SET_STATE_M(state) + +#endif /* !ERTS_MSACC_EXTENDED_STATES */ + +#endif /* ERL_MSACC_H__ */ diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 2c09834d19..197b328fe2 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -1648,6 +1648,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) int active; Uint64 start_time = 0; ErtsSchedulerData *esdp = runq->scheduler; + ERTS_MSACC_PUSH_STATE_M(); ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq)); @@ -1690,6 +1691,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) state = erts_atomic32_read_nob(&pp->state); pp->reds = ERTS_PORT_REDS_EXECUTE; + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); goto begin_handle_tasks; while (1) { @@ -1822,6 +1824,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) } erts_unblock_fpe(fpe_was_unmasked); + ERTS_MSACC_POP_STATE_M(); if (io_tasks_executed) { diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 3902632ef8..8e1ebe795c 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -941,6 +941,12 @@ sched_wall_time_change(ErtsSchedulerData *esdp, int working) } } } + if (!working) { + ERTS_MSACC_SET_STATE_M_X(ERTS_MSACC_STATE_BUSY_WAIT); + } else { + ERTS_MSACC_SET_STATE_M_X(ERTS_MSACC_STATE_OTHER); + } + } typedef struct { @@ -1696,15 +1702,17 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waitin int need_thr_progress = 0; ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID; int more_work = 0; - + ERTS_MSACC_PUSH_STATE_M_X(); #ifdef ERTS_DIRTY_SCHEDULERS ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); #endif unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD); + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ALLOC); erts_alloc_scheduler_handle_delayed_dealloc((void *) awdp->esdp, &need_thr_progress, &wakeup, &more_work); + ERTS_MSACC_POP_STATE_M_X(); if (more_work) { if (set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD) & ERTS_SSI_AUX_WORK_DD_THR_PRGR) { @@ -2176,12 +2184,15 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting) ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); \ if (!(aux_work & ~ignore)) { \ ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); \ + ERTS_MSACC_UPDATE_CACHE(); \ + ERTS_MSACC_POP_STATE_M(); \ return aux_work; \ } \ } erts_aint32_t aux_work = orig_aux_work; erts_aint32_t ignore = 0; + ERTS_MSACC_PUSH_AND_SET_STATE_M(ERTS_MSACC_STATE_AUX); ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); #ifdef ERTS_SMP @@ -2272,6 +2283,8 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting) haw_thr_prgr_current_check_progress(awdp); #endif + ERTS_MSACC_UPDATE_CACHE(); + ERTS_MSACC_POP_STATE_M(); return aux_work; #undef HANDLE_AUX_WORK @@ -2779,6 +2792,8 @@ aux_thread(void *unused) ssi->event = erts_tse_fetch(); + erts_msacc_init_thread("aux", 1, 1); + callbacks.arg = (void *) ssi; callbacks.wakeup = thr_prgr_wakeup; callbacks.prepare_wait = thr_prgr_prep_wait; @@ -2789,6 +2804,7 @@ aux_thread(void *unused) init_aux_work_data(awdp, NULL, NULL); awdp->ssi = ssi; + sched_prep_spin_wait(ssi); while (1) { @@ -2842,6 +2858,9 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) #ifdef ERTS_SMP int thr_prgr_active = 1; erts_aint32_t flgs; +#endif + ERTS_MSACC_PUSH_STATE_M(); +#ifdef ERTS_SMP ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); @@ -2898,6 +2917,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) sched_wall_time_change(esdp, 1); } aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); + ERTS_MSACC_UPDATE_CACHE(); if (aux_work && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); } @@ -2959,7 +2979,9 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) - 1) + 1; } else timeout = -1; + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP); res = erts_tse_twait(ssi->event, timeout); + ERTS_MSACC_POP_STATE_M(); current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : erts_get_monotonic_time(esdp); } while (res == EINTR); @@ -3028,9 +3050,13 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) if (working) sched_wall_time_change(esdp, working = 0); + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO); + ASSERT(!erts_port_task_have_outstanding_io_tasks()); erl_sys_schedule(1); /* Might give us something to do */ + ERTS_MSACC_POP_STATE_M(); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { current_time = erts_get_monotonic_time(esdp); if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) @@ -3051,6 +3077,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erts_thr_progress_active(esdp, thr_prgr_active = 1); #endif aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); + ERTS_MSACC_UPDATE_CACHE(); #ifdef ERTS_SMP if (aux_work && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); @@ -3148,8 +3175,12 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ASSERT(!erts_port_task_have_outstanding_io_tasks()); + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO); + erl_sys_schedule(0); + ERTS_MSACC_POP_STATE_M(); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { ErtsMonotonicTime current_time = erts_get_monotonic_time(esdp); if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) @@ -7926,6 +7957,8 @@ sched_thread_func(void *vesdp) callbacks.wait = thr_prgr_wait; callbacks.finalize_wait = thr_prgr_fin_wait; + erts_msacc_init_thread("scheduler", no, 1); + erts_thr_progress_register_managed_thread(esdp, &callbacks, 0); erts_alloc_register_scheduler(vesdp); #endif @@ -9204,6 +9237,8 @@ Process *schedule(Process *p, int calls) Uint32 flags; erts_aint32_t state = 0; /* Supress warning... */ + ERTS_MSACC_DECLARE_CACHE(); + #ifdef USE_VM_PROBES if (p != NULL && DTRACE_ENABLED(process_unscheduled)) { DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE); @@ -9308,6 +9343,8 @@ Process *schedule(Process *p, int calls) erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS); + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER); + if (state & ERTS_PSFLG_FREE) { #ifdef ERTS_SMP ASSERT(esdp->free_process == p); @@ -9468,7 +9505,7 @@ Process *schedule(Process *p, int calls) scheduler_wait(&fcalls, esdp, rq); flags = ERTS_RUNQ_FLGS_SET_NOB(rq, ERTS_RUNQ_FLG_EXEC); flags |= ERTS_RUNQ_FLG_EXEC; - + ERTS_MSACC_UPDATE_CACHE(); #ifdef ERTS_SMP non_empty_runq(rq); #endif @@ -9483,6 +9520,8 @@ Process *schedule(Process *p, int calls) * Schedule system-level activities. */ + ERTS_MSACC_PUSH_STATE_CACHED_M(); + erts_smp_atomic32_set_relb(&function_calls, 0); fcalls = 0; @@ -9490,7 +9529,9 @@ Process *schedule(Process *p, int calls) erts_sys_schedule_interrupt(0); #endif erts_smp_runq_unlock(rq); + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO); erl_sys_schedule(1); + ERTS_MSACC_POP_STATE_M(); current_time = erts_get_monotonic_time(esdp); if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) @@ -9568,9 +9609,12 @@ Process *schedule(Process *p, int calls) case 0: /* No process at all */ default: ASSERT(qmask == 0); + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER); goto check_activities_to_run; } + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_EMULATOR); + BM_START_TIMER(system); /* diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index 34f91e2ec8..e689547d1d 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -2056,6 +2056,8 @@ erts_atomic64_read_dirty(erts_atomic64_t *var) #endif /* !USE_THREADS */ +#include "erl_msacc.h" + #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE void @@ -2414,6 +2416,7 @@ erts_cnd_wait(erts_cnd_t *cnd, erts_mtx_t *mtx) { #ifdef USE_THREADS int res; + ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_SLEEP); #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_unlock(&mtx->lc); #endif @@ -2432,6 +2435,7 @@ erts_cnd_wait(erts_cnd_t *cnd, erts_mtx_t *mtx) #endif if (res != 0 && res != EINTR) erts_thr_fatal_error(res, "wait on condition variable"); + ERTS_MSACC_POP_STATE(); #endif } @@ -3488,7 +3492,11 @@ ERTS_GLB_INLINE void erts_tse_reset(erts_tse_t *ep) ERTS_GLB_INLINE int erts_tse_wait(erts_tse_t *ep) { #ifdef USE_THREADS - return ethr_event_wait(&((ethr_ts_event *) ep)->event); + int res; + ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_SLEEP); + res = ethr_event_wait(&((ethr_ts_event *) ep)->event); + ERTS_MSACC_POP_STATE(); + return res; #else return ENOTSUP; #endif @@ -3497,7 +3505,11 @@ ERTS_GLB_INLINE int erts_tse_wait(erts_tse_t *ep) ERTS_GLB_INLINE int erts_tse_swait(erts_tse_t *ep, int spincount) { #ifdef USE_THREADS - return ethr_event_swait(&((ethr_ts_event *) ep)->event, spincount); + int res; + ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_SLEEP); + res = ethr_event_swait(&((ethr_ts_event *) ep)->event, spincount); + ERTS_MSACC_POP_STATE(); + return res; #else return ENOTSUP; #endif @@ -3506,8 +3518,12 @@ ERTS_GLB_INLINE int erts_tse_swait(erts_tse_t *ep, int spincount) ERTS_GLB_INLINE int erts_tse_twait(erts_tse_t *ep, Sint64 tmo) { #ifdef USE_THREADS - return ethr_event_twait(&((ethr_ts_event *) ep)->event, - (ethr_sint64_t) tmo); + int res; + ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_SLEEP); + res = ethr_event_twait(&((ethr_ts_event *) ep)->event, + (ethr_sint64_t) tmo); + ERTS_MSACC_POP_STATE(); + return res; #else return ENOTSUP; #endif @@ -3516,9 +3532,13 @@ ERTS_GLB_INLINE int erts_tse_twait(erts_tse_t *ep, Sint64 tmo) ERTS_GLB_INLINE int erts_tse_stwait(erts_tse_t *ep, int spincount, Sint64 tmo) { #ifdef USE_THREADS - return ethr_event_stwait(&((ethr_ts_event *) ep)->event, - spincount, - (ethr_sint64_t) tmo); + int res; + ERTS_MSACC_PUSH_AND_SET_STATE(ERTS_MSACC_STATE_SLEEP); + res = ethr_event_stwait(&((ethr_ts_event *) ep)->event, + spincount, + (ethr_sint64_t) tmo); + ERTS_MSACC_POP_STATE(); + return res; #else return ENOTSUP; #endif diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 3f5925765d..e8a7573e86 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1448,6 +1448,8 @@ ERTS_GLB_FORCE_INLINE int erts_is_literal(Eterm tptr, Eterm *ptr) #endif +Eterm erts_msacc_request(Process *c_p, int action, Eterm *threads); + /* ** Call_trace uses this API for the parameter matching functions */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 07578fd24e..41b0fcdd1b 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -706,6 +706,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ error_number = error_type = 0; if (driver->start) { + ERTS_MSACC_PUSH_STATE_M(); if (IS_TRACED_FL(port, F_TRACE_SCHED_PORTS)) { trace_sched_ports_where(port, am_in, am_start); } @@ -716,6 +717,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ DTRACE3(driver_start, process_str, driver->name, port_str); } #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); fpe_was_unmasked = erts_block_fpe(); drv_data = (*driver->start)(ERTS_Port2ErlDrvPort(port), name, opts); if (((SWord) drv_data) == -1) @@ -735,6 +737,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ } erts_unblock_fpe(fpe_was_unmasked); + ERTS_MSACC_POP_STATE_M(); port->caller = NIL; if (IS_TRACED_FL(port, F_TRACE_SCHED_PORTS)) { trace_sched_ports_where(port, am_out, am_start); @@ -1710,6 +1713,7 @@ call_driver_outputv(int bang_op, else { ErtsSchedulerData *esdp = erts_get_scheduler_data(); ErlDrvSizeT size = evp->size; + ERTS_MSACC_PUSH_AND_SET_STATE_M(ERTS_MSACC_STATE_PORT); ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt) || ERTS_IS_CRASH_DUMPING); @@ -1730,6 +1734,8 @@ call_driver_outputv(int bang_op, esdp->io.out += (Uint64) size; else erts_atomic64_add_nob(&bytes_out, (erts_aint64_t) size); + + ERTS_MSACC_POP_STATE_M(); } } @@ -1810,6 +1816,7 @@ call_driver_output(int bang_op, send_badsig(prt); else { ErtsSchedulerData *esdp = erts_get_scheduler_data(); + ERTS_MSACC_PUSH_AND_SET_STATE_M(ERTS_MSACC_STATE_PORT); ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt) || ERTS_IS_CRASH_DUMPING); @@ -1829,6 +1836,8 @@ call_driver_output(int bang_op, esdp->io.out += (Uint64) size; else erts_atomic64_add_nob(&bytes_out, (erts_aint64_t) size); + + ERTS_MSACC_POP_STATE_M(); } } @@ -3473,6 +3482,7 @@ static void flush_port(Port *p) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); if (p->drv_ptr->flush != NULL) { + ERTS_MSACC_PUSH_STATE_M(); #ifdef USE_VM_PROBES if (DTRACE_ENABLED(driver_flush)) { DTRACE_FORMAT_COMMON_PID_AND_PORT(ERTS_PORT_GET_CONNECTED(p), p) @@ -3482,9 +3492,11 @@ static void flush_port(Port *p) if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) { trace_sched_ports_where(p, am_in, am_flush); } + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); fpe_was_unmasked = erts_block_fpe(); (*p->drv_ptr->flush)((ErlDrvData)p->drv_data); erts_unblock_fpe(fpe_was_unmasked); + ERTS_MSACC_POP_STATE_M(); if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) { trace_sched_ports_where(p, am_out, am_flush); } @@ -3532,6 +3544,7 @@ terminate_port(Port *prt) drv = prt->drv_ptr; if ((drv != NULL) && (drv->stop != NULL)) { int fpe_was_unmasked = erts_block_fpe(); + ERTS_MSACC_PUSH_AND_SET_STATE_M(ERTS_MSACC_STATE_PORT); #ifdef USE_VM_PROBES if (DTRACE_ENABLED(driver_stop)) { DTRACE_FORMAT_COMMON_PID_AND_PORT(connected_id, prt) @@ -3540,6 +3553,7 @@ terminate_port(Port *prt) #endif (*drv->stop)((ErlDrvData)prt->drv_data); erts_unblock_fpe(fpe_was_unmasked); + ERTS_MSACC_POP_STATE_M(); #ifdef ERTS_SMP if (prt->xports) erts_port_handle_xports(prt); @@ -3850,6 +3864,7 @@ call_driver_control(Eterm caller, ErlDrvSizeT *from_size) { ErlDrvSSizeT cres; + ERTS_MSACC_PUSH_STATE_M(); if (!prt->drv_ptr->control) return ERTS_PORT_OP_BADARG; @@ -3863,6 +3878,8 @@ call_driver_control(Eterm caller, command, size); } #endif + + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); prt->caller = caller; cres = prt->drv_ptr->control((ErlDrvData) prt->drv_data, @@ -3873,6 +3890,8 @@ call_driver_control(Eterm caller, *from_size); prt->caller = NIL; + ERTS_MSACC_POP_STATE_M(); + if (cres < 0) return ERTS_PORT_OP_BADARG; @@ -4260,6 +4279,7 @@ call_driver_call(Eterm caller, unsigned *ret_flagsp) { ErlDrvSSizeT cres; + ERTS_MSACC_PUSH_STATE_M(); if (!prt->drv_ptr->call) return ERTS_PORT_OP_BADARG; @@ -4275,6 +4295,8 @@ call_driver_call(Eterm caller, } #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); + prt->caller = caller; cres = prt->drv_ptr->call((ErlDrvData) prt->drv_data, command, @@ -4285,6 +4307,8 @@ call_driver_call(Eterm caller, ret_flagsp); prt->caller = NIL; + ERTS_MSACC_POP_STATE_M(); + if (cres <= 0 || ((byte) (*resp_bufp)[0]) != VERSION_MAGIC) return ERTS_PORT_OP_BADARG; @@ -4988,6 +5012,7 @@ int get_port_flags(ErlDrvPort ix) void erts_raw_port_command(Port* p, byte* buf, Uint len) { int fpe_was_unmasked; + ERTS_MSACC_PUSH_STATE_M(); ERTS_SMP_CHK_NO_PROC_LOCKS; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); @@ -5008,9 +5033,11 @@ void erts_raw_port_command(Port* p, byte* buf, Uint len) DTRACE4(driver_output, "-raw-", port_str, p->name, len); } #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); fpe_was_unmasked = erts_block_fpe(); (*p->drv_ptr->output)((ErlDrvData)p->drv_data, (char*) buf, (int) len); erts_unblock_fpe(fpe_was_unmasked); + ERTS_MSACC_POP_STATE_M(); } int async_ready(Port *p, void* data) @@ -5022,6 +5049,7 @@ int async_ready(Port *p, void* data) if (p) { ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); if (p->drv_ptr->ready_async != NULL) { + ERTS_MSACC_PUSH_AND_SET_STATE_M(ERTS_MSACC_STATE_PORT); #ifdef USE_VM_PROBES if (DTRACE_ENABLED(driver_ready_async)) { DTRACE_FORMAT_COMMON_PID_AND_PORT(ERTS_PORT_GET_CONNECTED(p), p) @@ -5030,6 +5058,7 @@ int async_ready(Port *p, void* data) #endif (*p->drv_ptr->ready_async)((ErlDrvData)p->drv_data, data); need_free = 0; + ERTS_MSACC_POP_STATE_M(); } erts_port_driver_callback_epilogue(p, NULL); @@ -7053,6 +7082,7 @@ void erts_fire_port_monitor(Port *prt, Eterm ref) void (*callback)(ErlDrvData drv_data, ErlDrvMonitor *monitor); ErlDrvMonitor drv_monitor; int fpe_was_unmasked; + ERTS_MSACC_PUSH_STATE_M(); ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); ASSERT(prt->drv_ptr != NULL); @@ -7064,6 +7094,7 @@ void erts_fire_port_monitor(Port *prt, Eterm ref) callback = prt->drv_ptr->process_exit; ASSERT(callback != NULL); ref_to_driver_monitor(ref,&drv_monitor); + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); DRV_MONITOR_UNLOCK_PDL(prt); #ifdef USE_VM_PROBES if (DTRACE_ENABLED(driver_process_exit)) { @@ -7075,6 +7106,7 @@ void erts_fire_port_monitor(Port *prt, Eterm ref) (*callback)((ErlDrvData) (prt->drv_data), &drv_monitor); erts_unblock_fpe(fpe_was_unmasked); DRV_MONITOR_LOCK_PDL(prt); + ERTS_MSACC_POP_STATE_M(); /* remove monitor *after* callback */ rmon = erts_remove_monitor(&ERTS_P_MONITORS(prt), ref); DRV_MONITOR_UNLOCK_PDL(prt); diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index 0ab6661c9f..48cb39333a 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -278,9 +278,14 @@ ErtsMonotonicTime erts_check_next_timeout_time(ErtsSchedulerData *esdp) { ErtsTimerWheel *tiw = esdp->timer_wheel; + ErtsMonotonicTime time; + ERTS_MSACC_DECLARE_CACHE_X(); if (tiw->true_next_timeout_time) return tiw->next_timeout_time; - return find_next_timeout(esdp, tiw, 1, 0, 0); + ERTS_MSACC_PUSH_AND_SET_STATE_CACHED_X(ERTS_MSACC_STATE_TIMERS); + time = find_next_timeout(esdp, tiw, 1, 0, 0); + ERTS_MSACC_POP_STATE_M_X(); + return time; } #ifndef ERTS_TW_DEBUG @@ -336,6 +341,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time) { int tiw_pos_ix, slots, yielded_slot_restarted, yield_count; ErtsMonotonicTime bump_to, tmp_slots, old_pos; + ERTS_MSACC_PUSH_AND_SET_STATE_M_X(ERTS_MSACC_STATE_TIMERS); yield_count = ERTS_TWHEEL_BUMP_YIELD_LIMIT; @@ -371,6 +377,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time) tiw->next_timeout_time = curr_time + ERTS_MONOTONIC_DAY; tiw->pos = bump_to; tiw->yield_slot = ERTS_TWHEEL_SLOT_INACTIVE; + ERTS_MSACC_POP_STATE_M_X(); return; } @@ -382,6 +389,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time) tiw->yield_slot = ERTS_TWHEEL_SLOT_AT_ONCE; tiw->true_next_timeout_time = 1; tiw->next_timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(old_pos); + ERTS_MSACC_POP_STATE_M_X(); return; } @@ -400,8 +408,10 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time) p = tiw->at_once.head; } - if (tiw->pos >= bump_to) + if (tiw->pos >= bump_to) { + ERTS_MSACC_POP_STATE_M_X(); break; + } if (tiw->nto == 0) goto empty_wheel; @@ -478,6 +488,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time) tiw->yield_slot = tiw_pos_ix; tiw->yield_slots_left = slots; tiw->yield_start_pos = old_pos; + ERTS_MSACC_POP_STATE_M_X(); return; /* Yield! */ } @@ -500,6 +511,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time) /* Search at most two seconds ahead... */ (void) find_next_timeout(NULL, tiw, 0, curr_time, ERTS_SEC_TO_MONOTONIC(2)); + ERTS_MSACC_POP_STATE_M_X(); } Uint @@ -569,6 +581,7 @@ erts_twheel_set_timer(ErtsTimerWheel *tiw, ErtsMonotonicTime timeout_pos) { ErtsMonotonicTime timeout_time; + ERTS_MSACC_PUSH_AND_SET_STATE_M_X(ERTS_MSACC_STATE_TIMERS); p->u.func.timeout = timeout; p->u.func.cancel = cancel; @@ -612,6 +625,7 @@ erts_twheel_set_timer(ErtsTimerWheel *tiw, tiw->true_next_timeout_time = 1; tiw->next_timeout_time = timeout_time; } + ERTS_MSACC_POP_STATE_M_X(); } void @@ -620,11 +634,13 @@ erts_twheel_cancel_timer(ErtsTimerWheel *tiw, ErtsTWheelTimer *p) if (p->slot != ERTS_TWHEEL_SLOT_INACTIVE) { ErlCancelProc cancel; void *arg; + ERTS_MSACC_PUSH_AND_SET_STATE_M_X(ERTS_MSACC_STATE_TIMERS); remove_timer(tiw, p); cancel = p->u.func.cancel; arg = p->u.func.arg; if (cancel) (*cancel)(arg); + ERTS_MSACC_POP_STATE_M_X(); } } diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 21fa214364..36ee94111c 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -74,6 +74,7 @@ #include "erl_thr_progress.h" #include "erl_driver.h" #include "erl_alloc.h" +#include "erl_msacc.h" #if !defined(ERTS_POLL_USE_EPOLL) \ && !defined(ERTS_POLL_USE_DEVPOLL) \ @@ -2238,6 +2239,7 @@ static ERTS_INLINE int check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) { int res; + ERTS_MSACC_PUSH_STATE_M(); if (erts_smp_atomic_read_nob(&ps->no_of_user_fds) == 0 && timeout_time == ERTS_POLL_NO_TIMEOUT) { /* Nothing to poll and zero timeout; done... */ @@ -2259,6 +2261,7 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) #ifdef ERTS_SMP erts_thr_progress_prepare_wait(NULL); #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP); timerfd_set(ps, &its); res = epoll_wait(ps->kp_fd, ps->res_events, max_res, -1); res = timerfd_clear(ps, res, max_res); @@ -2268,10 +2271,12 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) } #else /* !ERTS_POLL_USE_TIMERFD */ timeout = (int) get_timeout(ps, 1000, timeout_time); + if (timeout) { #ifdef ERTS_SMP - if (timeout) erts_thr_progress_prepare_wait(NULL); #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP); + } res = epoll_wait(ps->kp_fd, ps->res_events, max_res, timeout); #endif /* !ERTS_POLL_USE_TIMERFD */ #elif ERTS_POLL_USE_KQUEUE /* --- kqueue ------------------------------ */ @@ -2279,10 +2284,12 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) if (max_res > ps->res_events_len) grow_res_events(ps, max_res); timeout = get_timeout_timespec(ps, &ts, timeout_time); + if (timeout) { #ifdef ERTS_SMP - if (timeout) erts_thr_progress_prepare_wait(NULL); #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP); + } res = kevent(ps->kp_fd, NULL, 0, ps->res_events, max_res, &ts); #endif /* ----------------------------------------- */ } @@ -2306,26 +2313,33 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) if (poll_res.dp_nfds > ps->res_events_len) grow_res_events(ps, poll_res.dp_nfds); poll_res.dp_fds = ps->res_events; + if (timeout) { #ifdef ERTS_SMP - if (timeout) erts_thr_progress_prepare_wait(NULL); #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP); + } poll_res.dp_timeout = timeout; res = ioctl(ps->kp_fd, DP_POLL, &poll_res); #elif ERTS_POLL_USE_POLL && defined(HAVE_PPOLL) /* --- ppoll ---------------- */ struct timespec ts; timeout = get_timeout_timespec(ps, &ts, timeout_time); + if (timeout) { #ifdef ERTS_SMP - if (timeout) erts_thr_progress_prepare_wait(NULL); #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP); + } res = ppoll(ps->poll_fds, ps->no_poll_fds, &ts, NULL); #elif ERTS_POLL_USE_POLL /* --- poll --------------------------------- */ timeout = (int) get_timeout(ps, 1000, timeout_time); + + if (timeout) { #ifdef ERTS_SMP - if (timeout) erts_thr_progress_prepare_wait(NULL); #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP); + } res = poll(ps->poll_fds, ps->no_poll_fds, timeout); #elif ERTS_POLL_USE_SELECT /* --- select ------------------------------ */ SysTimeval to; @@ -2334,18 +2348,22 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) ERTS_FD_COPY(&ps->input_fds, &ps->res_input_fds); ERTS_FD_COPY(&ps->output_fds, &ps->res_output_fds); + if (timeout) { #ifdef ERTS_SMP - if (timeout) erts_thr_progress_prepare_wait(NULL); #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP); + } res = ERTS_SELECT(ps->max_fd + 1, &ps->res_input_fds, &ps->res_output_fds, NULL, &to); #ifdef ERTS_SMP - if (timeout) + if (timeout) { erts_thr_progress_finalize_wait(NULL); + ERTS_MSACC_POP_STATE_M(); + } if (res < 0 && errno == EBADF && ERTS_POLLSET_HAVE_UPDATE_REQUESTS(ps)) { @@ -2380,10 +2398,12 @@ check_fd_events(ErtsPollSet ps, ErtsMonotonicTime timeout_time, int max_res) return res; #endif /* ----------------------------------------- */ } + if (timeout) { #ifdef ERTS_SMP - if (timeout) erts_thr_progress_finalize_wait(NULL); #endif + ERTS_MSACC_POP_STATE_M(); + } return res; } } diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 466f4a3b48..660ded297a 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -27,6 +27,7 @@ #include "erl_alloc.h" #include "erl_poll.h" #include "erl_time.h" +#include "erl_msacc.h" /* * Some debug macros @@ -1188,16 +1189,19 @@ int erts_poll_wait(ErtsPollSet ps, if (timeout > 0 && !erts_atomic32_read_nob(&break_waiter_state)) { HANDLE harr[2] = {ps->event_io_ready, break_happened_event}; int num_h = 2; + ERTS_MSACC_PUSH_STATE_M(); HARDDEBUGF(("Start waiting %d [%d]",num_h, (int) timeout)); ERTS_POLLSET_UNLOCK(ps); #ifdef ERTS_SMP erts_thr_progress_prepare_wait(NULL); #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP); WaitForMultipleObjects(num_h, harr, FALSE, timeout); #ifdef ERTS_SMP erts_thr_progress_finalize_wait(NULL); #endif + ERTS_MSACC_POP_STATE_M(); ERTS_POLLSET_LOCK(ps); HARDDEBUGF(("Stop waiting %d [%d]",num_h, (int) timeout)); woke_up(ps); diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index 53c9ba8715..a1b63716a9 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -32,7 +32,7 @@ run_queue_one/1, scheduler_wall_time/1, reductions/1, reductions_big/1, garbage_collection/1, io/1, - badarg/1, run_queues_lengths_active_tasks/1]). + badarg/1, run_queues_lengths_active_tasks/1, msacc/1]). %% Internal exports. @@ -55,7 +55,8 @@ all() -> [{group, wall_clock}, {group, runtime}, reductions, reductions_big, {group, run_queue}, scheduler_wall_time, garbage_collection, io, badarg, - run_queues_lengths_active_tasks]. + run_queues_lengths_active_tasks, + msacc]. groups() -> [{wall_clock, [], @@ -466,3 +467,93 @@ run_queues_lengths_active_tasks(Config) -> TokLoops), ok. + +msacc(doc) -> + "Tests that statistics(microstate_statistics) works."; +msacc(Config) -> + + %% Test if crypto nif is available + Niff = try crypto:strong_rand_bytes(1), ok catch _:_ -> nok end, + TmpFile = filename:join(proplists:get_value(priv_dir,Config),"file.tmp"), + + false = erlang:system_flag(microstate_accounting, true), + + msacc_test(TmpFile), + + true = erlang:system_flag(microstate_accounting, false), + + %% Make sure that all states were triggered at least once + maps:map(fun(nif, 0) -> + case Niff of + ok -> + ct:fail({zero_state,nif}); + nok -> + ok + end; + (aux, 0) -> + %% aux will be zero if we do not have smp support + %% or no async threads + case erlang:system_info(smp_support) orelse + erlang:system_info(thread_pool_size) > 0 of + false -> + ok; + true -> + ct:log("msacc: ~p",[erlang:statistics(microstate_accounting)]), + ct:fail({zero_state,aux}) + end; + (Key, 0) -> + ct:log("msacc: ~p",[erlang:statistics(microstate_accounting)]), + ct:fail({zero_state,Key}); + (_,_) -> ok + end,msacc_sum_states()), + + erlang:system_flag(microstate_accounting, reset), + + msacc_test(TmpFile), + + %% Make sure all counters are zero after stopping and resetting + maps:map(fun(_Key, 0) -> ok; + (Key,_) -> + ct:log("msacc: ~p",[erlang:statistics(microstate_accounting)]), + ct:fail({non_zero_state,Key}) + end,msacc_sum_states()). + +%% This test tries to make sure to trigger all of the different available states +msacc_test(TmpFile) -> + + %% We write some data + [file:write_file(TmpFile,<<0:(1024*1024*8)>>,[raw]) || _ <- lists:seq(1,100)], + + %% Do some ETS operations + Tid = ets:new(table, []), + ets:insert(Tid, {1, hello}), + ets:delete(Tid), + + %% Collect some garbage + [erlang:garbage_collect() || _ <- lists:seq(1,100)], + + %% Send some messages + [begin self() ! {hello},receive _ -> ok end end || _ <- lists:seq(1,100)], + + %% Setup some timers + Refs = [erlang:send_after(10000,self(),ok) || _ <- lists:seq(1,100)], + + %% Do some nif work + catch [crypto:strong_rand_bytes(128) || _ <- lists:seq(1,100)], + + %% Cancel some timers + [erlang:cancel_timer(R) || R <- Refs], + + %% Wait for a while + timer:sleep(100). + +msacc_sum_states() -> + Stats = erlang:statistics(microstate_accounting), + [#{ counters := C }|_] = Stats, + InitialCounters = maps:map(fun(_,_) -> 0 end,C), + lists:foldl(fun(#{ counters := Counters }, Cnt) -> + maps:fold(fun(Key, Value, Acc) -> + NewValue = Value+maps:get(Key,Acc), + maps:update(Key, NewValue, Acc) + end, Cnt, Counters) + end,InitialCounters,Stats). diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 6f56a81eec..ab51cf385c 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2217,6 +2217,16 @@ spawn_opt(_Tuple) -> (io) -> {{input, Input}, {output, Output}} when Input :: non_neg_integer(), Output :: non_neg_integer(); + (microstate_accounting) -> [MSAcc_Thread] | undefined when + MSAcc_Thread :: #{ type => MSAcc_Thread_Type, + id => MSAcc_Thread_Id, + counters => MSAcc_Counters}, + MSAcc_Thread_Type :: scheduler | async | aux, + MSAcc_Thread_Id :: non_neg_integer(), + MSAcc_Counters :: #{ MSAcc_Thread_State => non_neg_integer() }, + MSAcc_Thread_State :: alloc | aux | bif | busy_wait | check_io | + emulator | ets | gc | gc_fullsweep | nif | + other | port | send | sleep | timers; (reductions) -> {Total_Reductions, Reductions_Since_Last_Call} when Total_Reductions :: non_neg_integer(), @@ -2271,6 +2281,9 @@ subtract(_,_) -> (fullsweep_after, Number) -> OldNumber when Number :: non_neg_integer(), OldNumber :: non_neg_integer(); + (microstate_accounting, Action) -> OldState when + Action :: true | false | reset, + OldState :: true | false; (min_heap_size, MinHeapSize) -> OldMinHeapSize when MinHeapSize :: non_neg_integer(), OldMinHeapSize :: non_neg_integer(); diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index c3a14d272d..a15355bb10 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -49,6 +49,9 @@ -export([is_system_process/1]). +-export([await_microstate_accounting_modifications/3, + gather_microstate_accounting_result/2]). + %% Auto import name clash -export([check_process_code/2]). @@ -355,3 +358,28 @@ perf_counter_unit() -> is_system_process(_Pid) -> erlang:nif_error(undefined). + +-spec await_microstate_accounting_modifications(Ref, Result, Threads) -> boolean() when + Ref :: reference(), + Result :: boolean(), + Threads :: pos_integer(). + +await_microstate_accounting_modifications(Ref, Result, Threads) -> + _ = microstate_accounting(Ref,Threads), + Result. + +-spec gather_microstate_accounting_result(Ref, Threads) -> [#{}] when + Ref :: reference(), + Threads :: pos_integer(). + +gather_microstate_accounting_result(Ref, Threads) -> + microstate_accounting(Ref, Threads). + +microstate_accounting(_Ref, 0) -> + []; +microstate_accounting(Ref, Threads) -> + receive + Ref -> microstate_accounting(Ref, Threads - 1); + {Ref, Res} -> + [Res | microstate_accounting(Ref, Threads - 1)] + end. -- cgit v1.2.3 From c62f0216c37340e8716938b9408a40cd5b4e1d62 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 15 Jul 2015 11:34:13 +0200 Subject: erts: Fix msacc win32 debug compile error --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/erl_msacc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 14b81a0a6e..4be311ae82 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1252,7 +1252,7 @@ void process_main(void) Uint64 start_time = 0; /* Monitor long schedule */ BeamInstr* start_time_i = NULL; - ERTS_MSACC_DECLARE_CACHE_X(); /* a cached value of the tsd pointer for msacc */ + ERTS_MSACC_DECLARE_CACHE_X() /* a cached value of the tsd pointer for msacc */ ERL_BITS_DECLARE_STATEP; /* Has to be last declaration */ diff --git a/erts/emulator/beam/erl_msacc.h b/erts/emulator/beam/erl_msacc.h index de72fe9597..1b4b7a408a 100644 --- a/erts/emulator/beam/erl_msacc.h +++ b/erts/emulator/beam/erl_msacc.h @@ -228,7 +228,7 @@ void erts_msacc_init_thread(char *type, int id, int liberty); #define ERTS_MSACC_IS_ENABLED() ERTS_UNLIKELY(erts_msacc_enabled) #define ERTS_MSACC_DECLARE_CACHE() \ ErtsMsAcc *ERTS_MSACC_UPDATE_CACHE(); \ - ERTS_DECLARE_DUMMY(Uint __erts_msacc_state) = ERTS_MSACC_STATE_OTHER + ERTS_DECLARE_DUMMY(Uint __erts_msacc_state) = ERTS_MSACC_STATE_OTHER; #define ERTS_MSACC_IS_ENABLED_CACHED() ERTS_UNLIKELY(__erts_msacc_cache != NULL) #define ERTS_MSACC_UPDATE_CACHE() \ __erts_msacc_cache = erts_msacc_enabled ? ERTS_MSACC_TSD_GET() : NULL -- cgit v1.2.3 From 49458ee51bbbdcb06d776a846ce61a4eb274df04 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 15 Jul 2015 11:34:35 +0200 Subject: erts: use correct function for perf counter on non-x86 --- erts/emulator/sys/unix/erl_unix_sys.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 9f137048de..3a03d6be49 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -289,7 +289,7 @@ erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) } while(0) #define SYS_PERF_COUNTER_UNIT erts_perf_counter_unit() #else -#define sys_perf_counter(ts) *(ts) = erts_get_perf_counter() +#define sys_perf_counter(ts) *(ts) = erts_sys_hrtime() #define SYS_PERF_COUNTER_UNIT 1000000000LL #endif -- cgit v1.2.3 From 2181f97655906dac3b18333c149c384e1dc81c2b Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 15 Jul 2015 13:56:58 +0200 Subject: erts: Fix hrtime for windows --- erts/emulator/sys/win32/sys_time.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index 9e5f78703a..3b4fd26d63 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -397,6 +397,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) erts_sys_time_data__.r.o.os_monotonic_time = os_mtime_func; erts_sys_time_data__.r.o.os_times = os_times_func; + erts_sys_time_data__.r.o.sys_hrtime = sys_hrtime_func; init_resp->os_monotonic_time_unit = time_unit; init_resp->have_os_monotonic_time = 1; init_resp->have_corrected_os_monotonic_time = 0; -- cgit v1.2.3 From dc1e3933e633d9d7527e6df044895d12d3845e14 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 16 Jul 2015 11:24:51 +0200 Subject: erts: Add rdtscp instruction check --- erts/include/internal/ethr_internal.h | 1 - erts/include/internal/ethread.h | 5 +++++ erts/lib_src/common/ethr_aux.c | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/include/internal/ethr_internal.h b/erts/include/internal/ethr_internal.h index d4ded6ff05..693b34df61 100644 --- a/erts/include/internal/ethr_internal.h +++ b/erts/include/internal/ethr_internal.h @@ -92,7 +92,6 @@ void ethr_run_exit_handlers__(void); void ethr_ts_event_destructor__(void *vtsep); #if defined(ETHR_X86_RUNTIME_CONF__) -int ethr_x86_have_cpuid__(void); void ethr_x86_cpuid__(int *eax, int *ebx, int *ecx, int *edx); #endif diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 4eeb7097f4..9956473057 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -282,6 +282,10 @@ ETHR_PROTO_NORETURN__ ethr_fatal_error__(const char *file, (__builtin_expect(ethr_runtime__.conf.have_sse2 != 0, 1)) # define ETHR_X86_RUNTIME_CONF_HAVE_NO_SSE2__ \ (__builtin_expect(ethr_runtime__.conf.have_sse2 == 0, 0)) +# define ETHR_X86_RUNTIME_CONF_HAVE_RDTSCP__ \ + (__builtin_expect(ethr_runtime__.conf.have_rdtscp != 0, 1)) +# define ETHR_X86_RUNTIME_CONF_HAVE_NO_RDTSCP__ \ + (__builtin_expect(ethr_runtime__.conf.have_rdtscp == 0, 0)) #endif #if (defined(__GNUC__) \ @@ -300,6 +304,7 @@ typedef struct { #if defined(ETHR_X86_RUNTIME_CONF__) int have_dw_cmpxchg; int have_sse2; + int have_rdtscp; #endif #if defined(ETHR_PPC_RUNTIME_CONF__) int have_lwsync; diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c index 56fecf81b8..fbd870c2b5 100644 --- a/erts/lib_src/common/ethr_aux.c +++ b/erts/lib_src/common/ethr_aux.c @@ -139,6 +139,21 @@ x86_init(void) #endif /* bit 26 of edx is set if we have sse2 */ ethr_runtime__.conf.have_sse2 = (edx & (1 << 26)); + + /* check if we have extended feature set */ + eax = 0x80000000; + ethr_x86_cpuid__(&eax, &ebx, &ecx, &edx); + + if (eax >= 0x80000001) { + /* Get the extended feature set */ + eax = 0x80000001; + ethr_x86_cpuid__(&eax, &ebx, &ecx, &edx); + } else { + eax = ebx = ecx = edx = 0; + } + + /* bit 27 of edx is set if we have rdtscp */ + ethr_runtime__.conf.have_rdtscp = (edx & (1 << 27)); } #endif /* ETHR_X86_RUNTIME_CONF__ */ -- cgit v1.2.3 From 0399f5fc547ef035c4eb5e383f30b28ae73d936e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 16 Jul 2015 11:27:00 +0200 Subject: erts: Refactor perf counter internal interface perf counter is now part of the function pointer interface and also the function returns the value instead of writing to a memory buffer. --- erts/emulator/beam/beam_emu.c | 4 +- erts/emulator/beam/erl_msacc.c | 14 ++--- erts/emulator/beam/erl_msacc.h | 8 +-- erts/emulator/beam/erl_time.h | 2 +- erts/emulator/beam/erl_time_sup.c | 66 +------------------- erts/emulator/sys/unix/erl_unix_sys.h | 35 +++++------ erts/emulator/sys/unix/sys_time.c | 112 ++++++++++++++++++++++++++++++++++ erts/emulator/sys/win32/erl_win_sys.h | 16 ++++- 8 files changed, 158 insertions(+), 99 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 4be311ae82..9f143c22bf 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4938,10 +4938,10 @@ do { \ it has to be very very fast */ OpCase(i_perf_counter): { BeamInstr* next; - ErtsSysHrTime ts; + ErtsSysPerfCounter ts; PreFetch(0, next); - sys_perf_counter(&ts); + ts = erts_sys_perf_counter(); if (IS_SSMALL(ts)) { r(0) = make_small((Sint)ts); diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c index bf1c06dea7..71e3fd8b6e 100644 --- a/erts/emulator/beam/erl_msacc.c +++ b/erts/emulator/beam/erl_msacc.c @@ -117,7 +117,7 @@ void erts_msacc_init_thread(char *type, int id, int managed) { #ifdef ERTS_MSACC_ALWAYS_ON ERTS_MSACC_TSD_SET(msacc); - sys_perf_counter(&msacc->perf_counter); + msacc->perf_counter = erts_sys_perf_counter(); msacc->state = ERTS_MSACC_STATE_OTHER; #endif } @@ -278,7 +278,7 @@ reply_msacc(void *vmsaccrp) if (msaccrp->action == ERTS_MSACC_ENABLE && !msacc) { msacc = get_msacc(); - sys_perf_counter(&msacc->perf_counter); + msacc->perf_counter = erts_sys_perf_counter(); msacc->state = ERTS_MSACC_STATE_OTHER; @@ -412,9 +412,9 @@ erts_msacc_request(Process *c_p, int action, Eterm *threads) for (i = 0; i < unmanaged_count; i++) { erts_mtx_lock(&unmanaged[i]->mtx); if (unmanaged[i]->perf_counter) { - ErtsSysHrTime perf_counter; + ErtsSysPerfCounter perf_counter; /* if enabled update stats */ - sys_perf_counter(&perf_counter); + perf_counter = erts_sys_perf_counter(); unmanaged[i]->perf_counters[unmanaged[i]->state] += perf_counter - unmanaged[i]->perf_counter; unmanaged[i]->perf_counter = perf_counter; @@ -439,7 +439,7 @@ erts_msacc_request(Process *c_p, int action, Eterm *threads) erts_rwmtx_rlock(&msacc_mutex); for (msacc = msacc_unmanaged; msacc != NULL; msacc = msacc->next) { erts_mtx_lock(&msacc->mtx); - sys_perf_counter(&msacc->perf_counter); + msacc->perf_counter = erts_sys_perf_counter(); /* we assume the unmanaged thread is sleeping */ msacc->state = ERTS_MSACC_STATE_SLEEP; erts_mtx_unlock(&msacc->mtx); @@ -448,12 +448,12 @@ erts_msacc_request(Process *c_p, int action, Eterm *threads) break; } case ERTS_MSACC_DISABLE: { - ErtsSysHrTime perf_counter; + ErtsSysPerfCounter perf_counter; erts_rwmtx_rlock(&msacc_mutex); /* make sure to update stats with latest results */ for (msacc = msacc_unmanaged; msacc != NULL; msacc = msacc->next) { erts_mtx_lock(&msacc->mtx); - sys_perf_counter(&perf_counter); + perf_counter = erts_sys_perf_counter(); msacc->perf_counters[msacc->state] += perf_counter - msacc->perf_counter; msacc->perf_counter = 0; erts_mtx_unlock(&msacc->mtx); diff --git a/erts/emulator/beam/erl_msacc.h b/erts/emulator/beam/erl_msacc.h index 1b4b7a408a..284388f7aa 100644 --- a/erts/emulator/beam/erl_msacc.h +++ b/erts/emulator/beam/erl_msacc.h @@ -121,8 +121,8 @@ typedef struct erl_msacc_t_ ErtsMsAcc; struct erl_msacc_t_ { /* the the values below are protected by mtx iff unmanaged = 1 */ - ErtsSysHrTime perf_counter; - ErtsSysHrTime perf_counters[ERTS_MSACC_STATE_COUNT]; + ErtsSysPerfCounter perf_counter; + ErtsSysPerfCounter perf_counters[ERTS_MSACC_STATE_COUNT]; #ifdef ERTS_MSACC_STATE_COUNTERS Uint64 state_counters[ERTS_MSACC_STATE_COUNT]; #endif @@ -324,14 +324,14 @@ void erts_msacc_set_state_um__(ErtsMsAcc *msacc, Uint new_state, int increment) ERTS_MSACC_INLINE void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) { - ErtsSysHrTime prev_perf_counter; + ErtsSysPerfCounter prev_perf_counter; Sint64 diff; if (new_state == msacc->state) return; prev_perf_counter = msacc->perf_counter; - sys_perf_counter(&msacc->perf_counter); + msacc->perf_counter = erts_sys_perf_counter(); diff = msacc->perf_counter - prev_perf_counter; ASSERT(diff >= 0); msacc->perf_counters[msacc->state] += diff; diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 446adcf4af..5242063550 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -142,7 +142,7 @@ erts_time_unit_conversion(Uint64 value, Uint32 from_time_unit, Uint32 to_time_unit); -ErtsSysHrTime erts_perf_counter_unit(void); +ErtsSysPerfCounter erts_perf_counter_unit(void); #if ERTS_GLB_INLINE_INCL_FUNC_DEF diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 6509c6a805..98159fdf72 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -2444,72 +2444,10 @@ BIF_RETTYPE os_system_time_1(BIF_ALIST_1) BIF_RETTYPE os_perf_counter_0(BIF_ALIST_0) { - ErtsSysHrTime pcounter; - sys_perf_counter(&pcounter); - BIF_RET(make_time_val(BIF_P, pcounter)); + BIF_RET(make_time_val(BIF_P, erts_sys_perf_counter())); } BIF_RETTYPE erts_internal_perf_counter_unit_0(BIF_ALIST_0) { - BIF_RET(make_time_val(BIF_P, SYS_PERF_COUNTER_UNIT)); -} - -/* What resolution to spin to in micro seconds */ -#define RESOLUTION 100 -/* How many iterations to spin */ -#define ITERATIONS 1 -/* How many significant figures to round to */ -#define SIGFIGS 3 - -static ErtsSysHrTime perf_counter_unit = 0; - -static ErtsSysHrTime erts_calculate_perf_counter_unit(void); -static ErtsSysHrTime erts_calculate_perf_counter_unit() { - int i; - ErtsSysHrTime pre, post; - double value = 0; - double round_factor; -#if defined(HAVE_GETHRTIME) && defined(GETHRTIME_WITH_CLOCK_GETTIME) - struct timespec basetime,comparetime; -#define __GETTIME(arg) clock_gettime(CLOCK_MONOTONIC,arg) -#define __GETUSEC(arg) (arg.tv_nsec / 1000) -#else - SysTimeval basetime,comparetime; -#define __GETTIME(arg) sys_gettimeofday(arg) -#define __GETUSEC(arg) arg.tv_usec -#endif - - for (i = 0; i < ITERATIONS; i++) { - /* Make sure usec just flipped over at current resolution */ - __GETTIME(&basetime); - do { - __GETTIME(&comparetime); - } while ((__GETUSEC(basetime) / RESOLUTION) == (__GETUSEC(comparetime) / RESOLUTION)); - - sys_perf_counter(&pre); - - __GETTIME(&basetime); - do { - __GETTIME(&comparetime); - } while ((__GETUSEC(basetime) / RESOLUTION) == (__GETUSEC(comparetime) / RESOLUTION)); - - sys_perf_counter(&post); - - value += post - pre; - } - /* After this value is ticks per us */ - value /= (RESOLUTION*ITERATIONS); - - /* We round to 3 significant figures */ - round_factor = pow(10.0, SIGFIGS - ceil(log10(value))); - value = ((ErtsSysHrTime)(value * round_factor + 0.5)) / round_factor; - - /* convert to ticks per second */ - return 1000000 * value; -} - -ErtsSysHrTime erts_perf_counter_unit() { - if (perf_counter_unit == 0) - perf_counter_unit = erts_calculate_perf_counter_unit(); - return perf_counter_unit; + BIF_RET(make_time_val(BIF_P, erts_sys_perf_counter_unit())); } diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 3a03d6be49..8b1822ca9f 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -161,6 +161,7 @@ typedef long long ErtsSysHrTime; #endif typedef ErtsMonotonicTime ErtsSystemTime; +typedef ErtsSysHrTime ErtsSysPerfCounter; #define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63) #define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN) @@ -209,6 +210,7 @@ ErtsSystemTime erts_os_system_time(void); * It may or may not be monotonic. */ ErtsSysHrTime erts_sys_hrtime(void); +#define ERTS_HRTIME_UNIT (1000*1000*1000) struct erts_sys_time_read_only_data__ { #ifdef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ @@ -217,6 +219,8 @@ struct erts_sys_time_read_only_data__ { #ifdef ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__ void (*os_times)(ErtsMonotonicTime *, ErtsSystemTime *); #endif + ErtsSysPerfCounter (*perf_counter)(void); + ErtsSysPerfCounter perf_counter_unit; int ticks_per_sec; }; @@ -273,25 +277,18 @@ erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) * Functions for getting the performance counter */ -#if defined(__x86_64__) - /* available on all x86_64. Best if used when we have constant_tsc and - nonstop_tsc cpu features. It may have been a good idea to put the - cpuid instruction before the rdtsc, but I decided against it - because it is not really needed for msacc, and it slows it down by - quite a bit. As a result though, this timestamp becomes much less - accurate as it might be re-ordered to be executed way before this - function is called. - */ -#define sys_perf_counter(ts) do { \ - __asm__ __volatile__ ("rdtsc\n\t" \ - "shl $32, %%rdx\n\t" \ - "or %%rdx, %0" : "=a" (*ts) : : "rdx"); \ - } while(0) -#define SYS_PERF_COUNTER_UNIT erts_perf_counter_unit() -#else -#define sys_perf_counter(ts) *(ts) = erts_sys_hrtime() -#define SYS_PERF_COUNTER_UNIT 1000000000LL -#endif +ERTS_GLB_INLINE ErtsSysPerfCounter erts_sys_perf_counter(void); +#define erts_sys_perf_counter_unit() erts_sys_time_data__.r.o.perf_counter_unit + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE ErtsSysPerfCounter +erts_sys_perf_counter() +{ + return (*erts_sys_time_data__.r.o.perf_counter)(); +} + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ /* * Functions for measuring CPU time diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index 6fc4fc7dc4..9738a8c352 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -65,6 +65,8 @@ # include #endif +static void init_perf_counter(void); + /******************* Routines for time measurement *********************/ #undef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ @@ -404,6 +406,8 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) # error Missing erts_os_system_time() implementation #endif + init_perf_counter(); + } void @@ -908,6 +912,114 @@ erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) #endif +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Performance counter functions * +\* */ + + +/* What resolution to spin to in micro seconds */ +#define RESOLUTION 100 +/* How many iterations to spin */ +#define ITERATIONS 1 +/* How many significant figures to round to */ +#define SIGFIGS 3 + +static ErtsSysPerfCounter calculate_perf_counter_unit(void) { + int i; + ErtsSysPerfCounter pre, post; + double value = 0; + double round_factor; +#if defined(HAVE_GETHRTIME) && defined(GETHRTIME_WITH_CLOCK_GETTIME) + struct timespec basetime,comparetime; +#define __GETTIME(arg) clock_gettime(CLOCK_MONOTONIC,arg) +#define __GETUSEC(arg) (arg.tv_nsec / 1000) +#else + SysTimeval basetime,comparetime; +#define __GETTIME(arg) sys_gettimeofday(arg) +#define __GETUSEC(arg) arg.tv_usec +#endif + + for (i = 0; i < ITERATIONS; i++) { + /* Make sure usec just flipped over at current resolution */ + __GETTIME(&basetime); + do { + __GETTIME(&comparetime); + } while ((__GETUSEC(basetime) / RESOLUTION) == (__GETUSEC(comparetime) / RESOLUTION)); + + pre = erts_sys_perf_counter(); + + __GETTIME(&basetime); + do { + __GETTIME(&comparetime); + } while ((__GETUSEC(basetime) / RESOLUTION) == (__GETUSEC(comparetime) / RESOLUTION)); + + post = erts_sys_perf_counter(); + + value += post - pre; + } + /* After this value is ticks per us */ + value /= (RESOLUTION*ITERATIONS); + + /* We round to 3 significant figures */ + round_factor = pow(10.0, SIGFIGS - ceil(log10(value))); + value = ((ErtsSysPerfCounter)(value * round_factor + 0.5)) / round_factor; + + /* convert to ticks per second */ + return 1000000 * value; +} + +static int have_rdtscp(void) +{ +#if defined(ETHR_X86_RUNTIME_CONF__) + /* On early x86 cpu's the tsc varies with + the current speed of the cpu, which means that the time per + tick vary depending on the current load of the cpu. We do not + want this as it would give very scewed numbers when the cpu is + mostly idle. + If we have the rdtscp feature it is a sign that the cpu is + relatively modern, and thus the tsc quite stable so we use it then. + + If this test is not good enough, I don't know what we'll do. + Maybe fallback on erts_sys_hrtime always, but that would be a shame as + rdtsc is about 3 times faster than hrtime... */ + return ETHR_X86_RUNTIME_CONF_HAVE_RDTSCP__; +#else + return 0; +#endif +} + +static ErtsSysPerfCounter rdtsc(void) +{ + /* It may have been a good idea to put the cpuid instruction before + the rdtsc, but I decided against it because it is not really + needed for msacc, and it slows it down by quite a bit (5-7 times slower). + As a result though, this timestamp becomes much less + accurate as it might be re-ordered to be executed way before or after this + function is called. + */ + ErtsSysPerfCounter ts; +#if defined(__x86_64__) + __asm__ __volatile__ ("rdtsc\n\t" + "shl $32, %%rdx\n\t" + "or %%rdx, %0" : "=a" (ts) : : "rdx"); +#elif defined(__i386__) + __asm__ __volatile__ ("rdtsc\n\t" + : "=A" (ts) ); +#endif + return ts; +} + +static void init_perf_counter(void) +{ + if (have_rdtscp()) { + erts_sys_time_data__.r.o.perf_counter = rdtsc; + erts_sys_time_data__.r.o.perf_counter_unit = calculate_perf_counter_unit(); + } else { + erts_sys_time_data__.r.o.perf_counter = erts_sys_hrtime; + erts_sys_time_data__.r.o.perf_counter_unit = ERTS_HRTIME_UNIT; + } +} + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifdef HAVE_GETHRVTIME_PROCFS_IOCTL diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 1ab9eadefd..99c1066ab3 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -183,6 +183,7 @@ typedef LONGLONG ErtsSysHrTime; #endif typedef ErtsMonotonicTime ErtsSystemTime; +typedef ErtsMonotonicTime ErtsSysPerfCounter; ErtsSystemTime erts_os_system_time(void); @@ -213,6 +214,7 @@ ERTS_GLB_INLINE ErtsMonotonicTime erts_os_monotonic_time(void); ERTS_GLB_INLINE void erts_os_times(ErtsMonotonicTime *, ErtsSystemTime *); ERTS_GLB_INLINE ErtsSysHrTime erts_sys_hrtime(void); +ERTS_GLB_INLINE ErtsSysPerfCounter erts_sys_perf_counter(void); #if ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -234,12 +236,22 @@ erts_sys_hrtime(void) return (*erts_sys_time_data__.r.o.sys_hrtime)(); } +ERTS_GLB_INLINE ErtsSysPerfCounter +erts_sys_perf_counter(void) +{ + return (*erts_sys_time_data__.r.o.sys_hrtime)(); +} + +ERTS_GLB_INLINE ErtsSysPerfCounter +erts_sys_perf_counter_unit(void) +{ + return 1000 * 1000 * 1000; +} + #endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ extern void sys_gettimeofday(SysTimeval *tv); extern clock_t sys_times(SysTimes *buffer); -#define sys_perf_counter(ts) *(ts) = erts_sys_hrtime() -#define SYS_PERF_COUNTER_UNIT ERTS_I64_LITERAL(1000000000) extern char *win_build_environment(char *); -- cgit v1.2.3 From 6090f9c7e9b0ddbccef357641c1455475b348e94 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 16 Jul 2015 15:36:44 +0200 Subject: erts: Add power saving cpu feature tests and use them --- erts/emulator/sys/unix/sys_time.c | 16 ++++++------- erts/include/internal/ethread.h | 50 +++++++++++++++++++++++++++++---------- erts/lib_src/common/ethr_aux.c | 29 ++++++++++++++++++----- 3 files changed, 69 insertions(+), 26 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index 9738a8c352..03d39c7ce6 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -971,18 +971,18 @@ static ErtsSysPerfCounter calculate_perf_counter_unit(void) { static int have_rdtscp(void) { #if defined(ETHR_X86_RUNTIME_CONF__) - /* On early x86 cpu's the tsc varies with - the current speed of the cpu, which means that the time per - tick vary depending on the current load of the cpu. We do not - want this as it would give very scewed numbers when the cpu is - mostly idle. - If we have the rdtscp feature it is a sign that the cpu is - relatively modern, and thus the tsc quite stable so we use it then. + /* On early x86 cpu's the tsc varies with the current speed of the cpu, + which means that the time per tick vary depending on the current + load of the cpu. We do not want this as it would give very scewed + numbers when the cpu is mostly idle. + The linux kernel seems to think that checking for constant and + reliable is enough to trust the counter so we do the same. If this test is not good enough, I don't know what we'll do. Maybe fallback on erts_sys_hrtime always, but that would be a shame as rdtsc is about 3 times faster than hrtime... */ - return ETHR_X86_RUNTIME_CONF_HAVE_RDTSCP__; + return ETHR_X86_RUNTIME_CONF_HAVE_CONSTANT_TSC__ && + ETHR_X86_RUNTIME_CONF_HAVE_TSC_RELIABLE__; #else return 0; #endif diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 9956473057..e5c5cdfa33 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -274,18 +274,40 @@ ETHR_PROTO_NORETURN__ ethr_fatal_error__(const char *file, || (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64)))) # define ETHR_X86_RUNTIME_CONF__ -# define ETHR_X86_RUNTIME_CONF_HAVE_DW_CMPXCHG__ \ - (__builtin_expect(ethr_runtime__.conf.have_dw_cmpxchg != 0, 1)) -# define ETHR_X86_RUNTIME_CONF_HAVE_NO_DW_CMPXCHG__ \ - (__builtin_expect(ethr_runtime__.conf.have_dw_cmpxchg == 0, 0)) -# define ETHR_X86_RUNTIME_CONF_HAVE_SSE2__ \ - (__builtin_expect(ethr_runtime__.conf.have_sse2 != 0, 1)) -# define ETHR_X86_RUNTIME_CONF_HAVE_NO_SSE2__ \ - (__builtin_expect(ethr_runtime__.conf.have_sse2 == 0, 0)) -# define ETHR_X86_RUNTIME_CONF_HAVE_RDTSCP__ \ - (__builtin_expect(ethr_runtime__.conf.have_rdtscp != 0, 1)) -# define ETHR_X86_RUNTIME_CONF_HAVE_NO_RDTSCP__ \ - (__builtin_expect(ethr_runtime__.conf.have_rdtscp == 0, 0)) +# define ETHR_X86_RUNTIME_CONF_HAVE_META(feature) \ + (__builtin_expect(ethr_runtime__.conf.have_##feature != 0, 1)) +# define ETHR_X86_RUNTIME_CONF_HAVE_NO_META(feature) \ + (__builtin_expect(ethr_runtime__.conf.have_##feature == 0, 0)) + +# define ETHR_X86_RUNTIME_CONF_HAVE_DW_CMPXCHG__ \ + ETHR_X86_RUNTIME_CONF_HAVE_META(dw_cmpxchg) +# define ETHR_X86_RUNTIME_CONF_HAVE_NO_DW_CMPXCHG__ \ + ETHR_X86_RUNTIME_CONF_HAVE_NO_META(dw_cmpxchg) +# define ETHR_X86_RUNTIME_CONF_HAVE_SSE2__ \ + ETHR_X86_RUNTIME_CONF_HAVE_META(sse2) +# define ETHR_X86_RUNTIME_CONF_HAVE_NO_SSE2__ \ + ETHR_X86_RUNTIME_CONF_HAVE_NO_META(sse2) +# define ETHR_X86_RUNTIME_CONF_HAVE_RDTSCP__ \ + ETHR_X86_RUNTIME_CONF_HAVE_META(rdtscp) +# define ETHR_X86_RUNTIME_CONF_HAVE_NO_RDTSCP__ \ + ETHR_X86_RUNTIME_CONF_HAVE_NO_META(rdtscp) +# define ETHR_X86_RUNTIME_CONF_HAVE_CONSTANT_TSC__ \ + ETHR_X86_RUNTIME_CONF_HAVE_META(constant_tsc) +# define ETHR_X86_RUNTIME_CONF_HAVE_NO_CONSTANT_TSC__ \ + ETHR_X86_RUNTIME_CONF_HAVE_NO_META(nonstop_tsc) +# define ETHR_X86_RUNTIME_CONF_HAVE_NONSTOP_TSC__ \ + ETHR_X86_RUNTIME_CONF_HAVE_META(nonstop_tsc) +# define ETHR_X86_RUNTIME_CONF_HAVE_NO_NONSTOP_TSC__ \ + ETHR_X86_RUNTIME_CONF_HAVE_NO_META(nonstop_tsc) +# define ETHR_X86_RUNTIME_CONF_HAVE_TSC_RELIABLE__ \ + ETHR_X86_RUNTIME_CONF_HAVE_META(tsc_reliable) +# define ETHR_X86_RUNTIME_CONF_HAVE_NO_TSC_RELIABLE_TSC__ \ + ETHR_X86_RUNTIME_CONF_HAVE_NO_META(tsc_reliable) +# define ETHR_X86_RUNTIME_CONF_HAVE_NONSTOP_TSC_S3__ \ + ETHR_X86_RUNTIME_CONF_HAVE_META(nonstop_tsc_s3) +# define ETHR_X86_RUNTIME_CONF_HAVE_NO_NONSTOP_TSC_S3__ \ + ETHR_X86_RUNTIME_CONF_HAVE_NO_META(nonstop_tsc_s3) + #endif #if (defined(__GNUC__) \ @@ -305,6 +327,10 @@ typedef struct { int have_dw_cmpxchg; int have_sse2; int have_rdtscp; + int have_constant_tsc; + int have_tsc_reliable; + int have_nonstop_tsc; + int have_nonstop_tsc_s3; #endif #if defined(ETHR_PPC_RUNTIME_CONF__) int have_lwsync; diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c index fbd870c2b5..3e7aad16c7 100644 --- a/erts/lib_src/common/ethr_aux.c +++ b/erts/lib_src/common/ethr_aux.c @@ -144,16 +144,33 @@ x86_init(void) eax = 0x80000000; ethr_x86_cpuid__(&eax, &ebx, &ecx, &edx); - if (eax >= 0x80000001) { - /* Get the extended feature set */ - eax = 0x80000001; + if (eax < 0x80000001) + return; + + if (eax >= 0x80000007) { + /* Advanced Power Management Information */ + eax = 0x80000007; ethr_x86_cpuid__(&eax, &ebx, &ecx, &edx); - } else { - eax = ebx = ecx = edx = 0; + + /* I got the values below from: + http://lxr.free-electrons.com/source/arch/x86/include/asm/cpufeature.h + They can be gotten from the intel/amd manual as well. + */ + + ethr_runtime__.conf.have_constant_tsc = (edx & (1 << 8)); + ethr_runtime__.conf.have_tsc_reliable = (edx & (1 << 23)); + ethr_runtime__.conf.have_nonstop_tsc = (edx & (1 << 24)); + ethr_runtime__.conf.have_nonstop_tsc_s3 = (edx & (1 << 30)); + } - + + /* Extended Processor Info and Feature Bits */ + eax = 0x80000001; + ethr_x86_cpuid__(&eax, &ebx, &ecx, &edx); + /* bit 27 of edx is set if we have rdtscp */ ethr_runtime__.conf.have_rdtscp = (edx & (1 << 27)); + } #endif /* ETHR_X86_RUNTIME_CONF__ */ -- cgit v1.2.3 From a378360ea8d18264e11f201b6d00ed66b9fcfd32 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 13 Aug 2015 17:24:47 +0200 Subject: erts: Fix msacc testcase on some windowses --- erts/emulator/test/statistics_SUITE.erl | 69 +++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index a1b63716a9..821cb5d897 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -482,30 +482,51 @@ msacc(Config) -> true = erlang:system_flag(microstate_accounting, false), - %% Make sure that all states were triggered at least once - maps:map(fun(nif, 0) -> - case Niff of - ok -> - ct:fail({zero_state,nif}); - nok -> - ok - end; - (aux, 0) -> - %% aux will be zero if we do not have smp support - %% or no async threads - case erlang:system_info(smp_support) orelse - erlang:system_info(thread_pool_size) > 0 of - false -> - ok; - true -> - ct:log("msacc: ~p",[erlang:statistics(microstate_accounting)]), - ct:fail({zero_state,aux}) - end; - (Key, 0) -> - ct:log("msacc: ~p",[erlang:statistics(microstate_accounting)]), - ct:fail({zero_state,Key}); - (_,_) -> ok - end,msacc_sum_states()), + MsaccStats = erlang:statistics(microstate_accounting), + + case os:type() of + {win32, _} -> + %% Some windows have a very poor accuracy on their + %% timing primitives, so we just make sure that + %% some state besides sleep has been triggered. + Sum = lists:sum( + lists:map(fun({sleep, _V}) -> 0; + ({_, V}) -> V + end, maps:to_list(msacc_sum_states())) + ), + if Sum > 0 -> + ok; + true -> + ct:fail({no_states_triggered, MsaccStats}) + end; + _ -> + + %% Make sure that all states were triggered at least once + maps:map(fun(nif, 0) -> + case Niff of + ok -> + ct:fail({zero_state,nif}); + nok -> + ok + end; + (aux, 0) -> + %% aux will be zero if we do not have smp support + %% or no async threads + case erlang:system_info(smp_support) orelse + erlang:system_info(thread_pool_size) > 0 + of + false -> + ok; + true -> + ct:log("msacc: ~p",[MsaccStats]), + ct:fail({zero_state,aux}) + end; + (Key, 0) -> + ct:log("msacc: ~p",[MsaccStats]), + ct:fail({zero_state,Key}); + (_,_) -> ok + end, msacc_sum_states()) + end, erlang:system_flag(microstate_accounting, reset), -- cgit v1.2.3 From 0bf37ca78b6200f8b82b20234f0b882154a094ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 18 Dec 2015 15:26:35 +0100 Subject: Silence compiler --- erts/emulator/beam/beam_bp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 2a23da4f25..5d471d168b 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -864,7 +864,7 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg, Eterm* cpp; int return_to_trace = 0; BeamInstr w; - BeamInstr *cp_save; + BeamInstr *cp_save = c_p->cp; Uint32 flags; Uint need = 0; Eterm* E = c_p->stop; -- cgit v1.2.3 From 5e60a53cf306eb29a1f8396e773205d02a419ef6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 21 Jan 2016 19:57:08 +0100 Subject: erts: Make msacc alloctor type thread safe LONG_LIVED is not thread safe on non-smp and can only be used by scheduler. --- erts/emulator/beam/erl_alloc.types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 6b7eff1428..5f153ac0ab 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -361,7 +361,7 @@ type AINFO_REQ SHORT_LIVED SYSTEM alloc_info_request type SCHED_WTIME_REQ SHORT_LIVED SYSTEM sched_wall_time_request type GC_INFO_REQ SHORT_LIVED SYSTEM gc_info_request type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap -type MSACC LONG_LIVED SYSTEM microstate_accounting +type MSACC DRIVER SYSTEM microstate_accounting # # Types used by system specific code -- cgit v1.2.3 From f12da3c4abe70bd932484895af6e23436b308f53 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 2 Feb 2016 10:51:31 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 49956 -> 49984 bytes erts/preloaded/ebin/erlang.beam | Bin 101404 -> 101988 bytes erts/preloaded/ebin/erts_code_purger.beam | Bin 8744 -> 8764 bytes erts/preloaded/ebin/erts_internal.beam | Bin 8512 -> 9412 bytes erts/preloaded/ebin/init.beam | Bin 44700 -> 44724 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1448 -> 1468 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1320 -> 1340 bytes erts/preloaded/ebin/prim_file.beam | Bin 44884 -> 44908 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72608 -> 72632 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23280 -> 23308 bytes erts/preloaded/ebin/zlib.beam | Bin 14156 -> 14176 bytes 11 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index f007880683..2a54c10273 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index a89de716bd..f1e48b3282 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index 37e6b181d6..3d73b0cb1b 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 2380930767..24fa68506b 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 67159713f5..2f66b5f970 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index fd80258d0e..057fe79054 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 0e51a1bfc7..33f5de7d3f 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 663f5d46e6..b96927b72b 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index d8a8061bae..767a48d781 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 1d69c2e583..2bc85bbcbf 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 2ffd839f8a..0b4afc0a39 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From c96b6c2f58642b457d806c0a8a5bed03d16e35f1 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Thu, 21 Jan 2016 17:20:47 +0100 Subject: Better list_to_integer Now tries to use whole width of signed long (Sint) and this halves amount of multiplications needed to parse long integers. New code is 2-3 times faster than the old code for large inputs (tens and hundreds of digits), behavior should not change for small inputs. Test ran 10k times with GC forced between attempts. Was (R17): 720 el base 10: 0.14682 sec; base 16: 0.192722 sec; base 36: 0.337118 sec. 2800 el base 10: 1.794133 sec; base 16: 2.735106 sec; base 36: 4.761108 sec. 6500 el base 10: 9.316434 sec; base 16: 14.109469 sec; base 36: 25.319263 sec. Now (R19 Dev) 720 el base 10: 0.10265 sec; base 16: 0.10851 sec; base 36: 0.160478 sec. 2800 el base 10: 1.002793 sec; base 16: 1.360649 sec; base 36: 2.174309 sec. 6500 el base 10: 4.722197 sec; base 16: 6.60522 sec; base 36: 10.552795 sec. Added test for corner cases and sign bit corruption. Replaced macros with inline and hid it inside C file to not pollute global namespace Old bug in #define LG2_LOOKUP: Replaced with inline function and table recalculated for all bases 2 to 36 (was 2 to 64) --- erts/emulator/beam/bif.c | 48 ++--- erts/emulator/beam/big.c | 346 +++++++++++++++++++++-------------- erts/emulator/beam/big.h | 22 +-- erts/emulator/test/num_bif_SUITE.erl | 99 +++++++--- 4 files changed, 316 insertions(+), 199 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index d519216fbd..34611ad6ab 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2904,8 +2904,11 @@ BIF_RETTYPE integer_to_list_1(BIF_ALIST_1) /**********************************************************************/ -/* convert a list of ascii ascii integer value to an integer */ - +/* + * Converts a list of ascii base10 digits to an integer fully or partially. + * Returns result and the remaining tail. + * On error returns: {error,not_a_list}, or {error, no_integer} + */ BIF_RETTYPE string_to_integer_1(BIF_ALIST_1) { @@ -2913,9 +2916,8 @@ BIF_RETTYPE string_to_integer_1(BIF_ALIST_1) Eterm tail; Eterm *hp; /* must be a list */ - switch (do_list_to_integer(BIF_P,BIF_ARG_1,&res,&tail)) { - /* HAlloc after do_list_to_integer as it - might HAlloc itself (bignum) */ + switch (erts_list_to_integer(BIF_P, BIF_ARG_1, 10, &res, &tail)) { + /* HAlloc after erts_list_to_integer as it might HAlloc itself (bignum) */ case LTI_BAD_STRUCTURE: hp = HAlloc(BIF_P,3); BIF_RET(TUPLE2(hp, am_error, am_not_a_list)); @@ -2930,13 +2932,14 @@ BIF_RETTYPE string_to_integer_1(BIF_ALIST_1) BIF_RETTYPE list_to_integer_1(BIF_ALIST_1) { - /* Using do_list_to_integer is about twice as fast as using + /* Using erts_list_to_integer is about twice as fast as using erts_chars_to_integer because we do not have to copy the entire list */ Eterm res; Eterm dummy; /* must be a list */ - if (do_list_to_integer(BIF_P,BIF_ARG_1,&res,&dummy) != LTI_ALL_INTEGER) { + if (erts_list_to_integer(BIF_P, BIF_ARG_1, 10, + &res, &dummy) != LTI_ALL_INTEGER) { BIF_ERROR(BIF_P,BADARG); } BIF_RET(res); @@ -2944,14 +2947,12 @@ BIF_RETTYPE list_to_integer_1(BIF_ALIST_1) BIF_RETTYPE list_to_integer_2(BIF_ALIST_2) { - /* Bif implementation is about 50% faster than pure erlang, and since we have erts_chars_to_integer now it is simpler as well. This could be optmized further if we did not have to copy the list to buf. */ int i; - Eterm res; - char *buf = NULL; + Eterm res, dummy; int base; i = erts_list_length(BIF_ARG_1); @@ -2959,31 +2960,16 @@ BIF_RETTYPE list_to_integer_2(BIF_ALIST_2) BIF_ERROR(BIF_P, BADARG); base = signed_val(BIF_ARG_2); - + if (base < 2 || base > 36) BIF_ERROR(BIF_P, BADARG); - /* Take fast path if base it 10 */ - if (base == 10) - return list_to_integer_1(BIF_P,&BIF_ARG_1); - - buf = (char *) erts_alloc(ERTS_ALC_T_TMP, i + 1); - - if (intlist_to_buf(BIF_ARG_1, buf, i) < 0) - goto list_to_integer_1_error; - buf[i] = '\0'; /* null terminal */ - - if ((res = erts_chars_to_integer(BIF_P,buf,i,base)) == THE_NON_VALUE) - goto list_to_integer_1_error; - - erts_free(ERTS_ALC_T_TMP, (void *) buf); + if (erts_list_to_integer(BIF_P, BIF_ARG_1, base, + &res, &dummy) != LTI_ALL_INTEGER) { + BIF_ERROR(BIF_P,BADARG); + } BIF_RET(res); - - list_to_integer_1_error: - erts_free(ERTS_ALC_T_TMP, (void *) buf); - BIF_ERROR(BIF_P, BADARG); - - } +} /**********************************************************************/ diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 29e677d2e5..11838e24ef 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -48,7 +48,7 @@ _t_dst = (dst)+((sz)-1); \ _t_src = (src)+((sz)-1); \ while(_t_sz--) *_t_dst-- = *_t_src--; \ - } \ + } \ } while(0) /* add a and b with carry in + out */ @@ -423,6 +423,25 @@ #endif +/* Forward declaration of lookup tables (See below in this file) used in list to + * integer conversions for different bases. Also used in bignum printing. + */ +static const byte digits_per_sint_lookup[36-1]; +static const byte digits_per_small_lookup[36-1]; +static const Sint largest_power_of_base_lookup[36-1]; + +static ERTS_INLINE byte get_digits_per_signed_int(Uint base) { + return digits_per_sint_lookup[base-2]; +} + +static ERTS_INLINE byte get_digits_per_small(Uint base) { + return digits_per_small_lookup[base-2]; +} + +static ERTS_INLINE Sint get_largest_power_of_base(Uint base) { + return largest_power_of_base_lookup[base-2]; +} + /* ** compare two number vectors */ @@ -1719,8 +1738,10 @@ static Uint write_big(Wterm x, void (*write_func)(void *, char), void *arg) short sign = BIG_SIGN(xp); ErtsDigit rem; Uint n = 0; + const Uint digits_per_Sint = get_digits_per_signed_int(10); + const Sint largest_pow_of_base = get_largest_power_of_base(10); - if (xl == 1 && *dx < D_DECIMAL_BASE) { + if (xl == 1 && *dx < largest_pow_of_base) { rem = *dx; if (rem == 0) { (*write_func)(arg, '0'); n++; @@ -1738,7 +1759,7 @@ static Uint write_big(Wterm x, void (*write_func)(void *, char), void *arg) MOVE_DIGITS(tmp, dx, xl); while(1) { - tmpl = D_div(tmp, tmpl, D_DECIMAL_BASE, tmp, &rem); + tmpl = D_div(tmp, tmpl, largest_pow_of_base, tmp, &rem); if (tmpl == 1 && *tmp == 0) { while(rem) { (*write_func)(arg, (rem % 10)+'0'); n++; @@ -1746,7 +1767,7 @@ static Uint write_big(Wterm x, void (*write_func)(void *, char), void *arg) } break; } else { - int i = D_DECIMAL_EXP; + Uint i = digits_per_Sint; while(i--) { (*write_func)(arg, (rem % 10)+'0'); n++; rem /= 10; @@ -2522,63 +2543,100 @@ int term_equals_2pow32(Eterm x) } } +static ERTS_INLINE int c2int_is_invalid_char(byte ch, int base) { + return (ch < '0' + || (ch > ('0' + base - 1) + && !(base > 10 + && ((ch >= 'a' && ch < ('a' + base - 10)) + || (ch >= 'A' && ch < ('A' + base - 10)))))); +} -#define IS_VALID_CHARACTER(CHAR,BASE) \ - (CHAR < '0' \ - || (CHAR > ('0' + BASE - 1) \ - && !(BASE > 10 \ - && ((CHAR >= 'a' && CHAR < ('a' + BASE - 10)) \ - || (CHAR >= 'A' && CHAR < ('A' + BASE - 10)))))) -#define CHARACTER_FROM_BASE(CHAR) \ - ((CHAR <= '9') ? CHAR - '0' : 10 + ((CHAR <= 'Z') ? CHAR - 'A' : CHAR - 'a')) -#define D_BASE_EXP(BASE) (d_base_exp_lookup[BASE-2]) -#define D_BASE_BASE(BASE) (d_base_base_lookup[BASE-2]) -#define LG2_LOOKUP(BASE) (lg2_lookup[base-2]) +static ERTS_INLINE byte c2int_digit_from_base(byte ch) { + return ch <= '9' ? ch - '0' + : (10 + (ch <= 'Z' ? ch - 'A' : ch - 'a')); +} /* - * for i in 2..64 do - * lg2_lookup[i-2] = log2(i) - * end - * How many bits are needed to store string of size n + * How many bits are needed to store 1 digit of given base in binary + * Wo.Alpha formula: Table [log2[n], {n,2,36}] */ -const double lg2_lookup[] = { 1.0, 1.58496, 2, 2.32193, 2.58496, 2.80735, 3.0, - 3.16993, 3.32193, 3.45943, 3.58496, 3.70044, 3.80735, 3.90689, 4.0, - 4.08746, 4.16993, 4.24793, 4.32193, 4.39232, 4.45943, 4.52356, 4.58496, - 4.64386, 4.70044, 4.75489, 4.80735, 4.85798, 4.90689, 4.9542, 5.0, - 5.04439, 5.08746, 5.12928, 5.16993, 5.20945, 5.24793, 5.2854, 5.32193, - 5.35755, 5.39232, 5.42626, 5.45943, 5.49185, 5.52356, 5.55459, 5.58496, - 5.61471, 5.64386, 5.67243, 5.70044, 5.72792, 5.75489, 5.78136, 5.80735, - 5.83289, 5.85798, 5.88264, 5.90689, 5.93074, 5.9542, 5.97728, 6.0 }; +static const double lg2_lookup[36-1] = { + 1.0, 1.58496, 2.0, 2.32193, 2.58496, 2.80735, 3.0, 3.16993, 3.32193, + 3.45943, 3.58496, 3.70044, 3.80735, 3.90689, 4.0, 4.08746, 4.16993, 4.24793, + 4.32193, 4.39232, 4.45943, 4.52356, 4.58496, 4.64386, 4.70044, 4.75489, + 4.80735, 4.85798, 4.90689, 4.9542, 5.0, 5.04439, 5.08746, 5.12928, 5.16993 +}; +static ERTS_INLINE double lookup_log2(Uint base) { + return lg2_lookup[base - 2]; +} /* - * for i in 2..64 do - * d_base_exp_lookup[i-2] = 31 / lg2_lookup[i-2]; - * end - * How many characters can fit in 31 bits + * How many digits can fit into a signed int (Sint) for given base, we take + * one digit away just to be on the safer side (some corner cases). */ -const byte d_base_exp_lookup[] = { 31, 19, 15, 13, 11, 11, 10, 9, 9, 8, 8, 8, 8, - 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5 }; +static const byte digits_per_sint_lookup[36-1] = { +#if (SIZEOF_VOID_P == 4) + /* Wo.Alpha formula: Table [Trunc[31 / log[2,n]]-1, {n, 2, 36}] */ + 30, 18, 14, 12, 10, 10, 9, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4 +#elif (SIZEOF_VOID_P == 8) + /* Wo.Alpha formula: Table [Trunc[63 / log[2,n]]-1, {n, 2, 36}] */ + 62, 38, 30, 26, 23, 21, 20, 18, 17, 17, 16, 16, 15, 15, 14, 14, 14, 13, 13, + 13, 13, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11 +#else + #error "Please produce a lookup table for the new architecture" +#endif +}; /* - * for i in 2..64 do - * d_base_base_lookup[i-2] = pow(i,d_base_exp_lookup[i-2]); - * end - * How much can the characters which fit in 31 bit represent + * How many digits can fit into Erlang Small (SMALL_BITS-1) counting sign bit */ -const Uint d_base_base_lookup[] = { 2147483648u, 1162261467u, 1073741824u, - 1220703125u, 362797056u, 1977326743u, 1073741824u, 387420489u, - 1000000000u, 214358881u, 429981696u, 815730721u, 1475789056u, - 170859375u, 268435456u, 410338673u, 612220032u, 893871739u, 1280000000u, - 1801088541u, 113379904u, 148035889u, 191102976u, 244140625u, 308915776u, - 387420489u, 481890304u, 594823321u, 729000000u, 887503681u, 1073741824u, - 1291467969u, 1544804416u, 1838265625u, 60466176u, 69343957u, 79235168u, - 90224199u, 102400000u, 115856201u, 130691232u, 147008443u, 164916224u, - 184528125u, 205962976u, 229345007u, 254803968u, 282475249u, 312500000u, - 345025251u, 380204032u, 418195493u, 459165024u, 503284375u, 550731776u, - 601692057u, 656356768u, 714924299u, 777600000u, 844596301u, 916132832u, - 992436543u, 1073741824u }; +static const byte digits_per_small_lookup[36-1] = { +#if (SIZEOF_VOID_P == 4) + /* Wo.Alpha formula: Table [Trunc[27 / log[2,n]]-1, {n, 2, 36}] */ + 27, 17, 13, 11, 10, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 +#elif (SIZEOF_VOID_P == 8) + /* Wo.Alpha formula: Table [Trunc[59 / log[2,n]]-1, {n, 2, 36}] */ + 59, 37, 29, 25, 22, 21, 19, 18, 17, 17, 16, 15, 15, 15, 14, 14, 14, 13, 13, + 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11 +#else + #error "Please produce a lookup table for the new architecture" +#endif +}; + +/* + * Largest power of base which can be represented in a signed int (Sint). + * Calculated by base 2..36 to the power of corresponding element from + * digits_per_sint_lookup. + */ +static const Sint largest_power_of_base_lookup[36-1] = { +#if (SIZEOF_VOID_P == 4) + /* Wo.Alpha formula: Table [Pow[n, Trunc[31 / log[2,n]]-1], {n, 2, 36}] */ + 1073741824, 387420489, 268435456, 244140625, 60466176, 282475249, 134217728, + 43046721, 100000000, 19487171, 35831808, 62748517, 105413504, 11390625, + 16777216, 24137569, 34012224, 47045881, 64000000, 85766121, 5153632, + 6436343,7962624, 9765625, 11881376, 14348907, 17210368, 20511149, 24300000, + 28629151, 33554432, 39135393, 45435424, 52521875, 1679616 +#elif (SIZEOF_VOID_P == 8) + /* Wo.Alpha formula: Table [Pow[n, Trunc[63 / log[2,n]]-1], {n, 2, 36}] + * with LL added after each element manually */ + 4611686018427387904LL, 1350851717672992089LL, 1152921504606846976LL, + 1490116119384765625LL, 789730223053602816LL, 558545864083284007LL, + 1152921504606846976LL, 150094635296999121LL, 100000000000000000LL, + 505447028499293771LL, 184884258895036416LL, 665416609183179841LL, + 155568095557812224LL, 437893890380859375LL, 72057594037927936LL, + 168377826559400929LL, 374813367582081024LL, 42052983462257059LL, + 81920000000000000LL, 154472377739119461LL, 282810057883082752LL, + 21914624432020321LL, 36520347436056576LL, 59604644775390625LL, + 95428956661682176LL, 150094635296999121LL, 232218265089212416LL, + 12200509765705829LL, 17714700000000000LL, 25408476896404831LL, + 36028797018963968LL, 50542106513726817LL, 70188843638032384LL, + 96549157373046875LL, 131621703842267136LL +#else + #error "Please produce a lookup table for the new architecture" +#endif +}; Eterm erts_chars_to_integer(Process *BIF_P, char *bytes, Uint size, const int base) { @@ -2588,8 +2646,11 @@ Eterm erts_chars_to_integer(Process *BIF_P, char *bytes, int neg = 0; byte b; Eterm *hp, *hp_end; - int m; + Sint m; int lg2; + const Uint digits_per_small = get_digits_per_small(base); + const Uint digits_per_Sint = get_digits_per_signed_int(base); + const Sint largest_pow_of_base = get_largest_power_of_base(base); if (size == 0) goto bytebuf_to_integer_1_error; @@ -2604,57 +2665,68 @@ Eterm erts_chars_to_integer(Process *BIF_P, char *bytes, size--; } + /* Trim leading zeroes */ + if (size) { + while (*bytes == '0') { + bytes++; + size--; + if (!size) { + /* All zero! */ + res = make_small(0); + goto bytebuf_to_integer_1_done; + } + } + } + if (size == 0) goto bytebuf_to_integer_1_error; - if (size < SMALL_DIGITS && base <= 10) { - /* * - * Take shortcut if we know that all chars are '0' < b < '9' and - * fit in a small. This improves speed by about 10% over the generic - * small case. - * */ - while (size--) { - b = *bytes++; + if (size < digits_per_small) { + if (base <= 10) { + /* * + * Take shortcut if we know that all chars are '0' < b < '9' and + * fit in a small. This improves speed by about 10% over the generic + * small case. + * */ + while (size--) { + b = *bytes++; - if (b < '0' || b > ('0'+base-1)) - goto bytebuf_to_integer_1_error; + if (b < '0' || b > ('0'+base-1)) + goto bytebuf_to_integer_1_error; - i = i * base + b - '0'; - } + i = i * base + b - '0'; + } - if (neg) - i = -i; - res = make_small(i); - goto bytebuf_to_integer_1_done; + if (neg) + i = -i; + res = make_small(i); + goto bytebuf_to_integer_1_done; + } + + /* Take shortcut if we know it will fit in a small. + * This improves speed by about 30%. + */ + while (size) { + b = *bytes++; + size--; + + if (c2int_is_invalid_char(b, base)) + goto bytebuf_to_integer_1_error; + + i = i * base + c2int_digit_from_base(b); + } + + if (neg) + i = -i; + res = make_small(i); + goto bytebuf_to_integer_1_done; } /* * Calculate the maximum number of bits which will * be needed to represent the binary */ - lg2 = ((size+2)*LG2_LOOKUP(base)+1); - - if (lg2 < SMALL_BITS) { - /* Take shortcut if we know it will fit in a small. - * This improves speed by about 30%. - */ - while (size) { - b = *bytes++; - size--; - - if (IS_VALID_CHARACTER(b,base)) - goto bytebuf_to_integer_1_error; - - i = i * base + CHARACTER_FROM_BASE(b); - - } - - if (neg) - i = -i; - res = make_small(i); - goto bytebuf_to_integer_1_done; - - } + lg2 = ((size+2)*lookup_log2(base)+1); /* Start calculating bignum */ m = (lg2 + D_EXP-1)/D_EXP; @@ -2663,8 +2735,8 @@ Eterm erts_chars_to_integer(Process *BIF_P, char *bytes, hp = HAlloc(BIF_P, m); hp_end = hp + m; - if ((i = (size % D_BASE_EXP(base))) == 0) - i = D_BASE_EXP(base); + if ((i = (size % digits_per_Sint)) == 0) + i = digits_per_Sint; n = size - i; m = 0; @@ -2672,34 +2744,34 @@ Eterm erts_chars_to_integer(Process *BIF_P, char *bytes, while (i--) { b = *bytes++; - if (IS_VALID_CHARACTER(b,base)) { + if (c2int_is_invalid_char(b,base)) { HRelease(BIF_P, hp_end, hp); goto bytebuf_to_integer_1_error; } - m = base * m + CHARACTER_FROM_BASE(b); + m = base * m + c2int_digit_from_base(b); } res = small_to_big(m, hp); while (n) { - i = D_BASE_EXP(base); - n -= D_BASE_EXP(base); + i = digits_per_Sint; + n -= digits_per_Sint; m = 0; while (i--) { b = *bytes++; - if (IS_VALID_CHARACTER(b,base)) { + if (c2int_is_invalid_char(b,base)) { HRelease(BIF_P, hp_end, hp); goto bytebuf_to_integer_1_error; } - m = base * m + CHARACTER_FROM_BASE(b); + m = base * m + c2int_digit_from_base(b); } if (is_small(res)) { res = small_to_big(signed_val(res), hp); } - res = big_times_small(res, D_BASE_BASE(base), hp); + res = big_times_small(res, largest_pow_of_base, hp); if (is_small(res)) { res = small_to_big(signed_val(res), hp); } @@ -2730,31 +2802,35 @@ bytebuf_to_integer_1_error: bytebuf_to_integer_1_done: return res; - } -int do_list_to_integer(Process *p, Eterm orig_list, - Eterm *integer, Eterm *rest) +/* Converts list of digits with given 'base' to integer sequentially. Returns + * result in 'integer_out', remaining tail goes to 'tail_out' and returns result + * code if the list was consumed fully or partially or there was an error + */ +LTI_result_t erts_list_to_integer(Process *BIF_P, Eterm orig_list, + const Uint base, + Eterm *integer_out, Eterm *tail_out) { Sint i = 0; Uint ui = 0; int skip = 0; int neg = 0; Sint n = 0; - int m; + Sint m; int lg2; Eterm res; - Eterm* hp; - Eterm *hp_end; Eterm lst = orig_list; Eterm tail = lst; int error_res = LTI_BAD_STRUCTURE; + const Uint digits_per_small = get_digits_per_small(base); + const Uint digits_per_Sint = get_digits_per_signed_int(base); if (is_nil(lst)) { error_res = LTI_NO_INTEGER; error: - *rest = tail; - *integer = make_small(0); + *tail_out = tail; + *integer_out = make_small(0); return error_res; } if (is_not_list(lst)) @@ -2784,15 +2860,16 @@ int do_list_to_integer(Process *p, Eterm orig_list, /* Calculate size and do type check */ while(1) { + byte ch; if (is_not_small(CAR(list_val(lst)))) { break; } - if (unsigned_val(CAR(list_val(lst))) < '0' || - unsigned_val(CAR(list_val(lst))) > '9') { + ch = unsigned_val(CAR(list_val(lst))); + if (c2int_is_invalid_char(ch, base)) { break; } - ui = ui * 10; - ui = ui + unsigned_val(CAR(list_val(lst))) - '0'; + ui = ui * base; + ui = ui + c2int_digit_from_base(ch); n++; lst = CDR(list_val(lst)); if (is_nil(lst)) { @@ -2810,23 +2887,24 @@ int do_list_to_integer(Process *p, Eterm orig_list, } - /* If n <= 8 then we know it's a small int - ** since 2^27 = 134217728. If n > 8 then we must - ** construct a bignum and let that routine do the checking - */ + /* If length fits inside Sint then we know it's a small int. Else we + * must construct a bignum and let that routine do the checking + */ - if (n <= SMALL_DIGITS) { /* It must be small */ - if (neg) i = -(Sint)ui; - else i = (Sint)ui; + if (n <= digits_per_small) { /* It must be small */ + i = neg ? -(Sint)ui : (Sint)ui; res = make_small(i); } else { - /* Convert from log10 to log2 by multiplying with 1/log10(2)=3.3219 - which we round up to (3 + 1/3) */ - lg2 = (n+1)*3 + (n+1)/3 + 1; + const Sint largest_pow_of_base = get_largest_power_of_base(base); + Eterm *hp; + Eterm *hp_end; + + /* Convert from log_base to log2 using lookup table */ + lg2 = ((n+2)*lookup_log2(base)+1); m = (lg2+D_EXP-1)/D_EXP; /* number of digits */ m = BIG_NEED_SIZE(m); /* number of words + thing */ - hp = HAlloc(p, m); + hp = HAlloc(BIF_P, m); hp_end = hp + m; lst = orig_list; @@ -2834,27 +2912,29 @@ int do_list_to_integer(Process *p, Eterm orig_list, lst = CDR(list_val(lst)); /* load first digits (at least one digit) */ - if ((i = (n % D_DECIMAL_EXP)) == 0) - i = D_DECIMAL_EXP; + if ((i = (n % digits_per_Sint)) == 0) + i = digits_per_Sint; n -= i; m = 0; while(i--) { - m = 10*m + (unsigned_val(CAR(list_val(lst))) - '0'); + m *= base; + m += c2int_digit_from_base(unsigned_val(CAR(list_val(lst)))); lst = CDR(list_val(lst)); } res = small_to_big(m, hp); /* load first digits */ while(n) { - i = D_DECIMAL_EXP; - n -= D_DECIMAL_EXP; + i = digits_per_Sint; + n -= digits_per_Sint; m = 0; while(i--) { - m = 10*m + (unsigned_val(CAR(list_val(lst))) - '0'); + m *= base; + m += c2int_digit_from_base(unsigned_val(CAR(list_val(lst)))); lst = CDR(list_val(lst)); } if (is_small(res)) res = small_to_big(signed_val(res), hp); - res = big_times_small(res, D_DECIMAL_BASE, hp); + res = big_times_small(res, largest_pow_of_base, hp); if (is_small(res)) res = small_to_big(signed_val(res), hp); res = big_plus_small(res, m, hp); @@ -2876,10 +2956,10 @@ int do_list_to_integer(Process *p, Eterm orig_list, hp += (big_arity(res)+1); } } - HRelease(p,hp_end,hp); + HRelease(BIF_P, hp_end, hp); } - *integer = res; - *rest = tail; + *integer_out = res; + *tail_out = tail; if (tail != NIL) { return LTI_SOME_INTEGER; } diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 85807d6eea..9c92de6b55 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -54,9 +54,6 @@ typedef Uint32 ErtsHalfDigit; #error "can not determine machine size" #endif -#define D_DECIMAL_EXP 9 -#define D_DECIMAL_BASE 1000000000 - typedef Uint dsize_t; /* Vector size type */ #define D_EXP (ERTS_SIZEOF_ETERM*8) @@ -173,12 +170,15 @@ Eterm erts_sint64_to_big(Sint64, Eterm **); Eterm erts_chars_to_integer(Process *, char*, Uint, const int); -#define LTI_BAD_STRUCTURE 0 -#define LTI_NO_INTEGER 1 -#define LTI_SOME_INTEGER 2 -#define LTI_ALL_INTEGER 3 - -int do_list_to_integer(Process *p, Eterm orig_list, - Eterm *integer, Eterm *rest); - +/* How list_to_integer classifies the input, was it even a string? */ +typedef enum { + LTI_BAD_STRUCTURE = 0, + LTI_NO_INTEGER = 1, + LTI_SOME_INTEGER = 2, + LTI_ALL_INTEGER = 3 +} LTI_result_t; + +LTI_result_t erts_list_to_integer(Process *BIF_P, Eterm orig_list, + const Uint base, + Eterm *integer_out, Eterm *tail_out); #endif diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index 90b6a36262..d0840fe731 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -1,8 +1,8 @@ %% %% %CopyrightBegin% -%% +%% %% Copyright Ericsson AB 1997-2014. All Rights Reserved. -%% +%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -14,7 +14,7 @@ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -%% +%% %% %CopyrightEnd% %% @@ -36,22 +36,22 @@ %% integer_to_binary/2 %% binary_to_integer/1 --export([all/0, suite/0, groups/0, init_per_suite/1, end_per_suite/1, +-export([all/0, suite/0, groups/0, init_per_suite/1, end_per_suite/1, init_per_group/2, end_per_group/2, t_abs/1, t_float/1, t_float_to_string/1, t_integer_to_string/1, - t_string_to_integer/1, + t_string_to_integer/1, t_list_to_integer_edge_cases/1, t_string_to_float_safe/1, t_string_to_float_risky/1, t_round/1, t_trunc/1 ]). suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> +all() -> [t_abs, t_float, t_float_to_string, t_integer_to_string, {group, t_string_to_float}, t_string_to_integer, t_round, - t_trunc]. + t_trunc, t_list_to_integer_edge_cases]. -groups() -> +groups() -> [{t_string_to_float, [], [t_string_to_float_safe, t_string_to_float_risky]}]. @@ -73,7 +73,7 @@ t_abs(Config) when is_list(Config) -> 5.5 = abs(id(5.5)), 0.0 = abs(id(0.0)), 100.0 = abs(id(-100.0)), - + %% Integers. 5 = abs(id(5)), 0 = abs(id(0)), @@ -93,7 +93,7 @@ t_abs(Config) when is_list(Config) -> BigNum = abs(BigNum), BigNum = abs(-BigNum), ok. - + t_float(Config) when is_list(Config) -> 0.0 = float(id(0)), 2.5 = float(id(2.5)), @@ -109,7 +109,7 @@ t_float(Config) when is_list(Config) -> %% Extremly big bignums. Big = id(list_to_integer(id(lists:duplicate(2000, $1)))), {'EXIT', {badarg, _}} = (catch float(Big)), - + ok. @@ -183,7 +183,7 @@ t_float_to_string(Config) when is_list(Config) -> test_fts("1.2300000000e+20",1.23e20, [{scientific, 10}, compact]), test_fts("1.23000000000000000000e+20",1.23e20, []), ok. - + test_fts(Expect, Float) -> Expect = float_to_list(Float), BinExpect = list_to_binary(Expect), @@ -255,7 +255,7 @@ t_round(Config) when is_list(Config) -> 256 = round(id(255.6)), -1033 = round(id(-1033.3)), -1034 = round(id(-1033.6)), - + % OTP-3722: X = id((1 bsl 27) - 1), MX = -X, @@ -345,9 +345,9 @@ t_integer_to_string(Config) when is_list(Config) -> %% Invalid types lists:foreach(fun(Value) -> - {'EXIT', {badarg, _}} = + {'EXIT', {badarg, _}} = (catch erlang:integer_to_binary(Value)), - {'EXIT', {badarg, _}} = + {'EXIT', {badarg, _}} = (catch erlang:integer_to_list(Value)) end,[atom,1.2,0.0,[$1,[$2]]]), @@ -416,27 +416,27 @@ t_string_to_integer(Config) when is_list(Config) -> %% Invalid types lists:foreach(fun(Value) -> - {'EXIT', {badarg, _}} = + {'EXIT', {badarg, _}} = (catch binary_to_integer(Value)), - {'EXIT', {badarg, _}} = + {'EXIT', {badarg, _}} = (catch erlang:list_to_integer(Value)) end,[atom,1.2,0.0,[$1,[$2]]]), - + % Default base error cases lists:foreach(fun(Value) -> - {'EXIT', {badarg, _}} = + {'EXIT', {badarg, _}} = (catch erlang:binary_to_integer( list_to_binary(Value))), - {'EXIT', {badarg, _}} = + {'EXIT', {badarg, _}} = (catch erlang:list_to_integer(Value)) end,["1.0"," 1"," -1","","+"]), - + % Custom base error cases lists:foreach(fun({Value,Base}) -> - {'EXIT', {badarg, _}} = + {'EXIT', {badarg, _}} = (catch binary_to_integer( list_to_binary(Value),Base)), - {'EXIT', {badarg, _}} = + {'EXIT', {badarg, _}} = (catch erlang:list_to_integer(Value,Base)) end,[{" 1",1},{" 1",37},{"2",2},{"C",11}, {"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111z",16}, @@ -449,10 +449,61 @@ t_string_to_integer(Config) when is_list(Config) -> ok. +%% Tests edge cases for list_to_integer; compares with known good values + +t_list_to_integer_edge_cases(Config) when is_list(Config) -> + %% Take integer literals and compare to their representation in ExtTerm + T = [ + {16, "0", <<131,97,0>>}, + {16, "-0", <<131,97,0>>}, + + {16, "f", <<131,97,15>>}, + {16, "-f", <<131,98,255,255,255,241>>}, + + {16, "0000000000000000000000000000000000000000000000000f", + <<131,97,15>>}, + {16, "-0000000000000000000000000000000000000000000000000f", + <<131,98,255,255,255,241>>}, + + {16, "ffffffff", <<131,110,4,0,255,255,255,255>>}, + {16, "-ffffffff", <<131,110,4,1,255,255,255,255>>}, + + {16, "7fffffff", <<131,110,4,0,255,255,255,127>>}, + {16, "-7fffffff", <<131,98,128,0,0,1>>}, + + {16, "ffffffffffffffff", + <<131,110,8,0,255,255,255,255,255,255,255,255>>}, + {16, "-ffffffffffffffff", + <<131,110,8,1,255,255,255,255,255,255,255,255>>}, + + {16, "7fffffffffffffff", + <<131,110,8,0,255,255,255,255,255,255,255,127>>}, + {16, "-7fffffffffffffff", + <<131,110,8,1,255,255,255,255,255,255,255,127>>}, + + %% Alleged 32-bit corner case (should not happen on 64-bit). At 32-4 + %% bits we may corrupt sign bit and fall out of SMALL_INT range. + {2, "1000000000000000000000000000", <<131,98,8,0,0,0>>}, + {2, "-1000000000000000000000000000", <<131,98,248,0,0,0>>}, + + %% 64-bit corner case (should not happen on 32-bit) at 64-4 bits we + %% corrupt sign bit and fall out of SMALL_INT range (bam! all dead) + {2, "100000000000000000000000000000000000000000000000000000000000", + <<131,110,8,0,0,0,0,0,0,0,0,8>>}, + {2, "-100000000000000000000000000000000000000000000000000000000000", + <<131,110,8,1,0,0,0,0,0,0,0,8>>} + ], + [begin + io:format("~s base ~p vs ~p~n", [Str, Base, Bin]), + FromStr = list_to_integer(Str, Base), + FromStr = binary_to_term(Bin) + end || {Base, Str, Bin} <- T], + ok. + test_sti(Num) -> [begin io:format("Testing ~p:~p",[Num,Base]), - test_sti(Num,Base) + test_sti(Num,Base) end|| Base <- lists:seq(2,36)]. test_sti(Num,Base) -> -- cgit v1.2.3 From 18f0707c218ebdeb6024ecffd7704d3582e0b91c Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 2 Feb 2016 16:20:19 +0100 Subject: Use nano second time unit in tracing --- erts/doc/src/erl_driver.xml | 5 +++-- erts/doc/src/erlang.xml | 9 +++++---- erts/emulator/beam/erl_trace.c | 3 ++- erts/emulator/test/trace_bif_SUITE.erl | 4 ++-- 4 files changed, 12 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index e338e95938..34dc8af238 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -1052,8 +1052,9 @@ typedef struct ErlIOVec {

This function is deprecated! Do not use it! - Use the documented - time measurement functionality + Use erl_drv_monotonic_time() + (perhaps in combination with + erl_drv_time_offset()) instead.

This function reads a timestamp into the memory pointed to by the parameter now. See the description of ErlDrvNowData for diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 79d3f66ea8..c9eb838230 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -7744,7 +7744,8 @@ ok

Timestamps in profile messages will use Erlang monotonic time. The time-stamp (Ts) has the same - format and value as produced by erlang:monotonic_time().

+ format and value as produced by + erlang:monotonic_time(nano_seconds).

runnable_procs @@ -7772,7 +7773,7 @@ ok Erlang monotonic time and a monotonically increasing integer. The time-stamp (Ts) has the same format and value - as produced by {erlang:monotonic_time(), + as produced by {erlang:monotonic_time(nano_seconds), erlang:unique_integer([monotonic])}.

timestamp @@ -8161,7 +8162,7 @@ timestamp() -> Erlang monotonic time time-stamp in all trace messages. The time-stamp (Ts) has the same format and value as produced by - erlang:monotonic_time(). This flag overrides + erlang:monotonic_time(nano_seconds). This flag overrides the cpu_timestamp flag.

strict_monotonic_timestamp @@ -8171,7 +8172,7 @@ timestamp() -> monotonic time and a monotonically increasing integer in all trace messages. The time-stamp (Ts) has the same format and value as produced by - {erlang:monotonic_time(), + {erlang:monotonic_time(nano_seconds), erlang:unique_integer([monotonic])}. This flag overrides the cpu_timestamp flag.

diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 2243639099..8a4c0ab1f2 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -165,7 +165,8 @@ take_timestamp(ErtsTraceTimeStamp *tsp, int ts_type) case ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP: { Uint hsz = 0; ErtsMonotonicTime mtime = erts_get_monotonic_time(NULL); - mtime += ERTS_MONOTONIC_OFFSET_NATIVE; + mtime = ERTS_MONOTONIC_TO_NSEC(mtime); + mtime += ERTS_MONOTONIC_OFFSET_NSEC; hsz = (IS_SSMALL(mtime) ? (Uint) 0 : ERTS_SINT64_HEAP_SIZE((Sint64) mtime)); diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index 760666d077..96b7dd159f 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -314,9 +314,9 @@ receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}, PrevTs, TsTy make_ts(timestamp) -> erlang:now(); make_ts(monotonic_timestamp) -> - erlang:monotonic_time(); + erlang:monotonic_time(nano_seconds); make_ts(strict_monotonic_timestamp) -> - MT = erlang:monotonic_time(), + MT = erlang:monotonic_time(nano_seconds), UMI = erlang:unique_integer([monotonic]), {MT, UMI}. -- cgit v1.2.3 From a4d6c798135440eadfba4832ac3cf77b06b6ac4f Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 4 Feb 2016 15:59:33 +0100 Subject: Fix bug causing run-queue mask to become inconsistent --- erts/emulator/beam/erl_process.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index f50b217d4a..0db81de570 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1091,7 +1091,7 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLGS_GET_USR_PRIO(PSFLGS) \ (((PSFLGS) >> ERTS_PSFLGS_USR_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK) #define ERTS_PSFLGS_GET_PRQ_PRIO(PSFLGS) \ - (((PSFLGS) >> ERTS_PSFLGS_USR_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK) + (((PSFLGS) >> ERTS_PSFLGS_PRQ_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK) /* The sequential tracing token is a tuple of size 5: * -- cgit v1.2.3 From 14680fcc3fb9d0357fe33a94525d08896afed1c5 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Thu, 3 Dec 2015 14:33:53 +0100 Subject: erts: Use Sint instead of int for list lengths This avoids potential integer arithmetic overflow for very large lists. --- erts/emulator/beam/beam_bif_load.c | 6 +++--- erts/emulator/beam/beam_load.c | 4 ++-- erts/emulator/beam/bif.c | 18 +++++++++--------- erts/emulator/beam/erl_bif_info.c | 4 ++-- erts/emulator/beam/erl_bif_lists.c | 8 ++++---- erts/emulator/beam/erl_bif_port.c | 8 ++++---- erts/emulator/beam/erl_process_dict.c | 4 ++-- erts/emulator/beam/erl_utils.h | 2 +- erts/emulator/beam/global.h | 4 ++-- erts/emulator/beam/utils.c | 17 +++++++++-------- 10 files changed, 38 insertions(+), 37 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index a000935388..1b4c022370 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -179,8 +179,8 @@ exception_list(Process* p, Eterm tag, struct m* mp, Sint exceptions) BIF_RETTYPE finish_loading_1(BIF_ALIST_1) { - int i; - int n; + Sint i; + Sint n; struct m* p = NULL; Uint exceptions; Eterm res; @@ -201,7 +201,7 @@ finish_loading_1(BIF_ALIST_1) */ n = erts_list_length(BIF_ARG_1); - if (n == -1) { + if (n < 0) { ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG); goto done; } diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index d367cce212..10f9f7bc2f 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -6219,10 +6219,10 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) BeamInstr* code_base; BeamInstr* fp; byte* info; - int n; + Sint n; int code_size; int rval; - int i; + Sint i; byte* temp_alloc = NULL; byte* bytes; Uint size; diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index e116b10b95..2654785ffc 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2837,7 +2837,7 @@ BIF_RETTYPE list_to_atom_1(BIF_ALIST_1) { Eterm res; char *buf = (char *) erts_alloc(ERTS_ALC_T_TMP, MAX_ATOM_CHARACTERS); - int i = intlist_to_buf(BIF_ARG_1, buf, MAX_ATOM_CHARACTERS); + Sint i = intlist_to_buf(BIF_ARG_1, buf, MAX_ATOM_CHARACTERS); if (i < 0) { erts_free(ERTS_ALC_T_TMP, (void *) buf); @@ -2857,7 +2857,7 @@ BIF_RETTYPE list_to_atom_1(BIF_ALIST_1) BIF_RETTYPE list_to_existing_atom_1(BIF_ALIST_1) { - int i; + Sint i; char *buf = (char *) erts_alloc(ERTS_ALC_T_TMP, MAX_ATOM_CHARACTERS); if ((i = intlist_to_buf(BIF_ARG_1, buf, MAX_ATOM_CHARACTERS)) < 0) { @@ -2964,7 +2964,7 @@ BIF_RETTYPE list_to_integer_2(BIF_ALIST_2) and since we have erts_chars_to_integer now it is simpler as well. This could be optmized further if we did not have to copy the list to buf. */ - int i; + Sint i; Eterm res, dummy; int base; @@ -3288,7 +3288,7 @@ static BIF_RETTYPE do_charbuf_to_float(Process *BIF_P,char *buf) { BIF_RETTYPE list_to_float_1(BIF_ALIST_1) { - int i; + Sint i; Eterm res; char *buf = NULL; @@ -3405,7 +3405,7 @@ BIF_RETTYPE list_to_tuple_1(BIF_ALIST_1) Eterm* cons; Eterm res; Eterm* hp; - int len; + Sint len; if ((len = erts_list_length(list)) < 0 || len > ERTS_MAX_TUPLE_SIZE) { BIF_ERROR(BIF_P, BADARG); @@ -3752,7 +3752,7 @@ BIF_RETTYPE display_string_1(BIF_ALIST_1) { Process* p = BIF_P; Eterm string = BIF_ARG_1; - int len = is_string(string); + Sint len = is_string(string); char *str; if (len <= 0) { @@ -3806,7 +3806,7 @@ BIF_RETTYPE halt_1(BIF_ALIST_1) erl_exit(ERTS_ABORT_EXIT, ""); } else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) { - int i; + Sint i; if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE-1)) < 0) { goto error; @@ -3875,7 +3875,7 @@ BIF_RETTYPE halt_2(BIF_ALIST_2) erl_exit(ERTS_ABORT_EXIT, ""); } else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) { - int i; + Sint i; if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE-1)) < 0) { goto error; @@ -4018,7 +4018,7 @@ BIF_RETTYPE list_to_pid_1(BIF_ALIST_1) { Uint a = 0, b = 0, c = 0; char* cp; - int i; + Sint i; DistEntry *dep = NULL; char *buf = (char *) erts_alloc(ERTS_ALC_T_TMP, 65); /* diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 017339e1f6..bc5c83e542 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1721,7 +1721,7 @@ info_1_tuple(Process* BIF_P, /* Pointer to current process. */ if (arity == 2) { Eterm res = THE_NON_VALUE; char *buf; - int len = is_string(*tp); + Sint len = is_string(*tp); if (len <= 0) return res; buf = (char *) erts_alloc(ERTS_ALC_T_TMP, len+1); @@ -1740,7 +1740,7 @@ info_1_tuple(Process* BIF_P, /* Pointer to current process. */ else { Eterm res = THE_NON_VALUE; char *buf; - int len = is_string(tp[1]); + Sint len = is_string(tp[1]); if (len <= 0) return res; buf = (char *) erts_alloc(ERTS_ALC_T_TMP, len+1); diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c index 5583dcb371..fe64e76575 100644 --- a/erts/emulator/beam/erl_bif_lists.c +++ b/erts/emulator/beam/erl_bif_lists.c @@ -42,7 +42,7 @@ static BIF_RETTYPE append(Process* p, Eterm A, Eterm B) Eterm last; size_t need; Eterm* hp; - int i; + Sint i; if ((i = erts_list_length(A)) < 0) { BIF_ERROR(p, BADARG); @@ -99,9 +99,9 @@ static Eterm subtract(Process* p, Eterm A, Eterm B) Eterm small_vec[SMALL_VEC_SIZE]; /* Preallocated memory for small lists */ Eterm* vec_p; Eterm* vp; - int i; - int n; - int m; + Sint i; + Sint n; + Sint m; if ((n = erts_list_length(A)) < 0) { BIF_ERROR(p, BADARG); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 839abd0424..3acc1d7bbd 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -649,7 +649,7 @@ BIF_RETTYPE port_get_data_1(BIF_ALIST_1) static Port * open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) { - int i; + Sint i; Eterm option; Uint arity; Eterm* tp; @@ -977,8 +977,8 @@ static char **convert_args(Eterm l) { char **pp; char *b; - int n; - int i = 0; + Sint n; + Sint i = 0; Eterm str; if (is_not_list(l) && is_not_nil(l)) { return NULL; @@ -1024,7 +1024,7 @@ static byte* convert_environment(Process* p, Eterm env) Eterm* temp_heap; Eterm* hp; Uint heap_size; - int n; + Sint n; Sint size; byte* bytes; int encoding = erts_get_native_filename_encoding(); diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 84cd81aecf..36d16f7f42 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -749,7 +749,7 @@ static void shrink(Process *p, Eterm* ret) if (lo == NIL) { ARRAY_PUT(pd, pd->splitPosition, hi); } else { - int needed = 4; + Sint needed = 4; if (is_list(hi) && is_list(lo)) { needed = 2*erts_list_length(hi); } @@ -814,7 +814,7 @@ static void grow(Process *p) Eterm *hp; unsigned int pos; unsigned int homeSize; - int needed = 0; + Sint needed = 0; ProcDict *pd = p->dictionary; #ifdef DEBUG Eterm *hp_limit; diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 4058d63eaf..b86786a9c0 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -115,7 +115,7 @@ void erts_silence_warn_unused_result(long unused); int erts_fit_in_bits_int64(Sint64); int erts_fit_in_bits_int32(Sint32); int erts_fit_in_bits_uint(Uint); -int erts_list_length(Eterm); +Sint erts_list_length(Eterm); int erts_is_builtin(Eterm, Eterm, int); Uint32 make_broken_hash(Eterm); Uint32 block_hash(byte *, unsigned, Uint32); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index e8a7573e86..628f36a35e 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1308,7 +1308,7 @@ int erts_utf8_to_latin1(byte* dest, const byte* source, int slen); #define ERTS_UTF8_OK_MAX_CHARS 4 void bin_write(int, void*, byte*, size_t); -int intlist_to_buf(Eterm, char*, int); /* most callers pass plain char*'s */ +Sint intlist_to_buf(Eterm, char*, Sint); /* most callers pass plain char*'s */ struct Sint_buf { #if defined(ARCH_64) @@ -1381,7 +1381,7 @@ ErlDrvSizeT erts_iolist_to_buf(Eterm, char*, ErlDrvSizeT); ErlDrvSizeT erts_iolist_to_buf_yielding(ErtsIOList2BufState *); int erts_iolist_size_yielding(ErtsIOListState *state); int erts_iolist_size(Eterm, ErlDrvSizeT *); -int is_string(Eterm); +Sint is_string(Eterm); void erl_at_exit(void (*) (void*), void*); Eterm collect_memory(Process *); void dump_memory_to_fd(int); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index ef851d840d..35dfee49cd 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -317,10 +317,10 @@ erl_grow_equeue(ErtsEQueue* q, Eterm* default_equeue) * Calculate length of a list. * Returns -1 if not a proper list (i.e. not terminated with NIL) */ -int +Sint erts_list_length(Eterm list) { - int i = 0; + Sint i = 0; while(is_list(list)) { i++; @@ -3930,11 +3930,11 @@ void bin_write(int to, void *to_arg, byte* buf, size_t sz) /* Fill buf with the contents of bytelist list return number of chars in list or -1 for error */ -int -intlist_to_buf(Eterm list, char *buf, int len) +Sint +intlist_to_buf(Eterm list, char *buf, Sint len) { Eterm* listptr; - int sz = 0; + Sint sz = 0; if (is_nil(list)) return 0; @@ -4481,11 +4481,12 @@ int erts_iolist_size(Eterm obj, ErlDrvSizeT* sizep) return iolist_size(0, NULL, obj, sizep); } -/* return 0 if item is not a non-empty flat list of bytes */ -int +/* return 0 if item is not a non-empty flat list of bytes + otherwise return the nonzero length of the list */ +Sint is_string(Eterm list) { - int len = 0; + Sint len = 0; while(is_list(list)) { Eterm* consp = list_val(list); -- cgit v1.2.3 From 7a319cd96f7f4869300b32442ebe892ae557f41c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 8 Feb 2016 15:47:49 +0100 Subject: erts: Fix error cases in enif_get_list_length false if improper list false if length > UINT_MAX --- erts/doc/src/erl_nif.xml | 2 +- erts/emulator/beam/erl_nif.c | 9 +++++++-- erts/emulator/test/nif_SUITE.erl | 4 ++-- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 6 +++++- 4 files changed, 15 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 420c9fea38..be0e406b9c 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -753,7 +753,7 @@ typedef enum { intenif_get_list_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len) Get the length of list term

Set *len to the length of list term and return true, - or return false if term is not a list.

+ or return false if term is not a proper list.

intenif_get_long(ErlNifEnv* env, ERL_NIF_TERM term, long int* ip) Read an long integer term diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index b057ec7770..12aaf4ce53 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -911,8 +911,13 @@ int enif_get_list_cell(ErlNifEnv* env, Eterm term, Eterm* head, Eterm* tail) int enif_get_list_length(ErlNifEnv* env, Eterm term, unsigned* len) { - if (is_not_list(term) && is_not_nil(term)) return 0; - *len = erts_list_length(term); + Sint i; + Uint u; + + if ((i = erts_list_length(term)) < 0) return 0; + u = (Uint)i; + if ((unsigned)u != u) return 0; + *len = u; return 1; } diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index b02a090103..bfec474966 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1398,7 +1398,7 @@ is_checks(Config) when is_list(Config) -> get_length(doc) -> ["Test all enif_get_length functions"]; get_length(Config) when is_list(Config) -> ?line ensure_lib_loaded(Config, 1), - ?line ok = length_test(hejsan, "hejsan", [], [], not_a_list). + ?line ok = length_test(hejsan, "hejsan", [], [], not_a_list, [1,2|3]). ensure_lib_loaded(Config) -> ensure_lib_loaded(Config, 1). @@ -1951,7 +1951,7 @@ last_resource_dtor_call() -> ?nif_stub. make_new_resource(_,_) -> ?nif_stub. check_is(_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub. check_is_exception() -> ?nif_stub. -length_test(_,_,_,_,_) -> ?nif_stub. +length_test(_,_,_,_,_,_) -> ?nif_stub. make_atoms() -> ?nif_stub. make_strings() -> ?nif_stub. make_new_resource_binary(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 8ebce4fef4..1acb270d1f 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -914,6 +914,7 @@ static ERL_NIF_TERM check_is_exception(ErlNifEnv* env, int argc, const ERL_NIF_T * argv[2] empty list * argv[3] not an atom * argv[4] not a list + * argv[5] improper list */ static ERL_NIF_TERM length_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { @@ -934,6 +935,9 @@ static ERL_NIF_TERM length_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg if (enif_get_list_length(env, argv[4], &len)) return enif_make_badarg(env); + if (enif_get_list_length(env, argv[5], &len)) + return enif_make_badarg(env); + return enif_make_atom(env, "ok"); } @@ -2002,7 +2006,7 @@ static ErlNifFunc nif_funcs[] = {"make_new_resource", 2, make_new_resource}, {"check_is", 11, check_is}, {"check_is_exception", 0, check_is_exception}, - {"length_test", 5, length_test}, + {"length_test", 6, length_test}, {"make_atoms", 0, make_atoms}, {"make_strings", 0, make_strings}, {"make_new_resource", 2, make_new_resource}, -- cgit v1.2.3 From 22ec76d8bc0e4dc0017791a7bd700b3d3565311a Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Tue, 9 Feb 2016 10:14:36 +0100 Subject: hipe_sigaltstack: correct initialization of ss.ss_flags SS_ONSTACK may be set in oss, but it's not supposed to be set in ss, and some systems correctly reject that; current Linux kernels accept but ignore it in ss --- erts/emulator/hipe/hipe_x86_signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index 10a40ce901..0ecd13c4bc 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -251,7 +251,7 @@ static void hipe_sigaltstack(void *ss_sp) stack_t ss; ss.ss_sp = ss_sp; - ss.ss_flags = SS_ONSTACK; + ss.ss_flags = 0; ss.ss_size = SIGSTKSZ; if (sigaltstack(&ss, NULL) < 0) { perror("sigaltstack"); -- cgit v1.2.3 From 40695d080b0dc0665b01803768ffc74ed2eca207 Mon Sep 17 00:00:00 2001 From: Michael Santos Date: Sun, 18 Oct 2015 16:20:37 -0400 Subject: epmd: support IPv6 node registration Allow IPv6 nodes to register with and query epmd. On systems with IPv6 support: * epmd listens on both the IPv4 and IPv6 ANY or loopback sockets * the epmd cli client connects to epmd over the IPv6 loopback * distributed nodes started with "-proto_dist inet6_tcp" will register with epmd over IPv6 To work on IPv6 capable systems that have IPv6 support disabled, epmd ignores errors opening the socket if the protocol is not supported. Similarly, the epmd client will fall back to IPv4 if the IPv6 socket is not available. Update the minimum supported version of Windows to Windows Vista to support IPv6. --- erts/configure.in | 2 +- erts/doc/src/epmd.xml | 2 +- erts/doc/src/erl.xml | 22 +++++ erts/epmd/src/epmd.c | 2 +- erts/epmd/src/epmd_cli.c | 30 +++++-- erts/epmd/src/epmd_int.h | 63 ++++++++----- erts/epmd/src/epmd_srv.c | 201 +++++++++++++++++++++++++++++------------- erts/epmd/test/epmd_SUITE.erl | 33 ++++++- 8 files changed, 262 insertions(+), 93 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 4fb725ff00..368c5638ae 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -468,7 +468,7 @@ case $host_os in win32) # The ethread library requires _WIN32_WINNT of at least 0x0403. # -D_WIN32_WINNT=* from CPPFLAGS is saved in ETHR_DEFS. - CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0501 -DWINVER=0x0501" + CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600 -DWINVER=0x0600" ;; darwin*) CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE" diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml index 28fcc8f7af..7f61804bea 100644 --- a/erts/doc/src/epmd.xml +++ b/erts/doc/src/epmd.xml @@ -37,7 +37,7 @@

Erlang Port Mapper Daemon

- +

Starts the port mapper daemon

diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index e8621fecc3..ed3e7e34c4 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -387,6 +387,28 @@

Replaces the path specified in the boot script. See script(4).

+ + +

Specify a protocol for Erlang distribution.

+ + inet_tcp + +

TCP over IPv4 (the default)

+
+ inet_tls + +

distribution over TLS/SSL

+
+ inet6_tcp + +

TCP over IPv6

+
+
+

For example, to start up IPv6 distributed nodes:

+
+% erl -name test@ipv6node.example.com -proto_dist inet6_tcp
+
+

Starts Erlang with a remote shell connected to .

diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 63ec18d939..5513cb2d7e 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -343,7 +343,7 @@ static void run_daemon(EpmdVars *g) for (fd = 0; fd < g->max_conn ; fd++) /* close all files ... */ close(fd); /* Syslog on linux will try to write to whatever if we dont - inform it of that the log is closed. */ + inform it that the log is closed. */ closelog(); /* These shouldn't be needed but for safety... */ diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c index a8fe865d9a..6fc05e153e 100644 --- a/erts/epmd/src/epmd_cli.c +++ b/erts/epmd/src/epmd_cli.c @@ -136,19 +136,33 @@ void epmd_call(EpmdVars *g,int what) static int conn_to_epmd(EpmdVars *g) { struct EPMD_SOCKADDR_IN address; + size_t salen = 0; int connect_sock; - - connect_sock = socket(FAMILY, SOCK_STREAM, 0); - if (connect_sock<0) - goto error; + unsigned short sport = g->port; + +#if defined(EPMD6) + SET_ADDR6(address, in6addr_loopback, sport); + salen = sizeof(struct sockaddr_in6); + + connect_sock = socket(AF_INET6, SOCK_STREAM, 0); + if (connect_sock>=0) { + + if (connect(connect_sock, (struct sockaddr*)&address, salen) == 0) + return connect_sock; - { /* store port number in unsigned short */ - unsigned short sport = g->port; - SET_ADDR(address, EPMD_ADDR_LOOPBACK, sport); + close(connect_sock); } +#endif + SET_ADDR(address, htonl(INADDR_LOOPBACK), sport); + salen = sizeof(struct sockaddr_in); - if (connect(connect_sock, (struct sockaddr*)&address, sizeof address) < 0) + connect_sock = socket(AF_INET, SOCK_STREAM, 0); + if (connect_sock<0) goto error; + + if (connect(connect_sock, (struct sockaddr*)&address, salen) < 0) + goto error; + return connect_sock; error: diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index 26100afc93..09317094c7 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -55,6 +55,7 @@ # ifndef WINDOWS_H_INCLUDES_WINSOCK2_H # include # endif +# include # include # include #endif @@ -130,6 +131,10 @@ # include #endif /* HAVE_SYSTEMD_DAEMON */ +#if defined(HAVE_IN6) && defined(AF_INET6) && defined(HAVE_INET_PTON) +# define EPMD6 +#endif + /* ************************************************************************ */ /* Replace some functions by others by making the function name a macro */ @@ -183,33 +188,53 @@ /* ************************************************************************ */ /* Macros that let us use IPv6 */ -#if defined(HAVE_IN6) && defined(AF_INET6) && defined(EPMD6) +#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 */ + +#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK)) + +#if defined(EPMD6) -#define EPMD_SOCKADDR_IN sockaddr_in6 -#define EPMD_IN_ADDR in6_addr -#define EPMD_S_ADDR s6_addr -#define EPMD_ADDR_LOOPBACK in6addr_loopback.s6_addr -#define EPMD_ADDR_ANY in6addr_any.s6_addr +#define EPMD_SOCKADDR_IN sockaddr_storage #define FAMILY AF_INET6 -#define SET_ADDR(dst, addr, port) do { \ - memset((char*)&(dst), 0, sizeof(dst)); \ - memcpy((char*)&(dst).sin6_addr.s6_addr, (char*)&(addr), 16); \ - (dst).sin6_family = AF_INET6; \ - (dst).sin6_flowinfo = 0; \ - (dst).sin6_port = htons(port); \ +#define SET_ADDR6(dst, addr, port) do { \ + struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&(dst); \ + memset(sa, 0, sizeof(dst)); \ + sa->sin6_family = AF_INET6; \ + sa->sin6_addr = (addr); \ + sa->sin6_port = htons(port); \ } while(0) -#define IS_ADDR_LOOPBACK(addr) \ - (memcmp((addr).s6_addr, in6addr_loopback.s6_addr, 16) == 0) +#define SET_ADDR(dst, addr, port) do { \ + struct sockaddr_in *sa = (struct sockaddr_in *)&(dst); \ + memset(sa, 0, sizeof(dst)); \ + sa->sin_family = AF_INET; \ + sa->sin_addr.s_addr = (addr); \ + sa->sin_port = htons(port); \ + } while(0) #else /* Not IP v6 */ #define EPMD_SOCKADDR_IN sockaddr_in -#define EPMD_IN_ADDR in_addr -#define EPMD_S_ADDR s_addr -#define EPMD_ADDR_LOOPBACK htonl(INADDR_LOOPBACK) -#define EPMD_ADDR_ANY htonl(INADDR_ANY) #define FAMILY AF_INET #define SET_ADDR(dst, addr, port) do { \ @@ -219,8 +244,6 @@ (dst).sin_port = htons(port); \ } while(0) -#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK)) - #endif /* Not IP v6 */ /* ************************************************************************ */ diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 7100855407..55ec0f7b6c 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -76,6 +76,7 @@ static time_t current_time(EpmdVars*); static Connection *conn_init(EpmdVars*); static int conn_open(EpmdVars*,int); +static int conn_local_peer_check(EpmdVars*, int); static int conn_close_fd(EpmdVars*,int); static void node_init(EpmdVars*); @@ -206,10 +207,11 @@ void run(EpmdVars *g) { struct EPMD_SOCKADDR_IN iserv_addr[MAX_LISTEN_SOCKETS]; int listensock[MAX_LISTEN_SOCKETS]; - int num_sockets; + int num_sockets = 0; int i; int opt; unsigned short sport = g->port; + int bound = 0; node_init(g); g->conn = conn_init(g); @@ -252,64 +254,82 @@ void run(EpmdVars *g) if (g->addresses != NULL && /* String contains non-separator characters if: */ g->addresses[strspn(g->addresses," ,")] != '\000') { - char *tmp; - char *token; - int loopback_ok = 0; + char *tmp = NULL; + char *token = NULL; + + /* Always listen on the loopback. */ + SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_LOOPBACK),sport); + num_sockets++; +#if defined(EPMD6) + SET_ADDR6(iserv_addr[num_sockets],in6addr_loopback,sport); + num_sockets++; +#endif - if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL) + if ((tmp = strdup(g->addresses)) == NULL) { dbg_perror(g,"cannot allocate memory"); epmd_cleanup_exit(g,1); } - strcpy(tmp,g->addresses); - for(token = strtok(tmp,", "), num_sockets = 0; + for(token = strtok(tmp,", "); token != NULL; - token = strtok(NULL,", "), num_sockets++) + token = strtok(NULL,", ")) { - struct EPMD_IN_ADDR addr; -#ifdef HAVE_INET_PTON - int ret; + struct in_addr addr; +#if defined(EPMD6) + struct in6_addr addr6; + struct sockaddr_storage *sa = &iserv_addr[num_sockets]; - if ((ret = inet_pton(FAMILY,token,&addr)) == -1) + if (inet_pton(AF_INET6,token,&addr6) == 1) { - dbg_perror(g,"cannot convert IP address to network format"); - epmd_cleanup_exit(g,1); + SET_ADDR6(iserv_addr[num_sockets],addr6,sport); + } + else if (inet_pton(AF_INET,token,&addr) == 1) + { + SET_ADDR(iserv_addr[num_sockets],addr.s_addr,sport); + } + else +#else + if ((addr.s_addr = inet_addr(token)) != INADDR_NONE) + { + SET_ADDR(iserv_addr[num_sockets],addr.s_addr,sport); } - else if (ret == 0) -#elif !defined(EPMD6) - if ((addr.EPMD_S_ADDR = inet_addr(token)) == INADDR_NONE) + else #endif { dbg_tty_printf(g,0,"cannot parse IP address \"%s\"",token); epmd_cleanup_exit(g,1); } +#if defined(EPMD6) + if (sa->ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&addr6)) + continue; + + if (sa->ss_family == AF_INET) +#endif if (IS_ADDR_LOOPBACK(addr)) - loopback_ok = 1; + continue; + + num_sockets++; - if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1) + if (num_sockets >= MAX_LISTEN_SOCKETS) { dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses", MAX_LISTEN_SOCKETS); epmd_cleanup_exit(g,1); } - - SET_ADDR(iserv_addr[num_sockets],addr.EPMD_S_ADDR,sport); } free(tmp); - - if (!loopback_ok) - { - SET_ADDR(iserv_addr[num_sockets],EPMD_ADDR_LOOPBACK,sport); - num_sockets++; - } } else { - SET_ADDR(iserv_addr[0],EPMD_ADDR_ANY,sport); - num_sockets = 1; + SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_ANY),sport); + num_sockets++; +#if defined(EPMD6) + SET_ADDR6(iserv_addr[num_sockets],in6addr_any,sport); + num_sockets++; +#endif } #ifdef HAVE_SYSTEMD_DAEMON } @@ -340,13 +360,39 @@ void run(EpmdVars *g) #endif /* HAVE_SYSTEMD_DAEMON */ for (i = 0; i < num_sockets; i++) { - if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0) + struct sockaddr *sa = (struct sockaddr *)&iserv_addr[i]; +#if defined(EPMD6) + size_t salen = (sa->sa_family == AF_INET6 ? + sizeof(struct sockaddr_in6) : + sizeof(struct sockaddr_in)); +#else + size_t salen = sizeof(struct sockaddr_in); +#endif + + if ((listensock[i] = socket(sa->sa_family,SOCK_STREAM,0)) < 0) { - dbg_perror(g,"error opening stream socket"); - epmd_cleanup_exit(g,1); + switch (errno) { + case EAFNOSUPPORT: + case EPROTONOSUPPORT: + continue; + default: + dbg_perror(g,"error opening stream socket"); + epmd_cleanup_exit(g,1); + } } g->listenfd[i] = listensock[i]; - + +#if HAVE_DECL_IPV6_V6ONLY + opt = 1; + if (sa->sa_family == AF_INET6 && + setsockopt(listensock[i],IPPROTO_IPV6,IPV6_V6ONLY,&opt, + sizeof(opt)) <0) + { + dbg_perror(g,"can't set IPv6 only socket option"); + epmd_cleanup_exit(g,1); + } +#endif + /* * Note that we must not enable the SO_REUSEADDR on Windows, * because addresses will be reused even if they are still in use. @@ -378,8 +424,7 @@ void run(EpmdVars *g) dbg_perror(g,"failed to set non-blocking mode of listening socket %d", listensock[i]); - if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i], - sizeof(iserv_addr[i])) < 0) + if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i], salen) < 0) { if (errno == EADDRINUSE) { @@ -394,12 +439,18 @@ void run(EpmdVars *g) } } + bound++; + if(listen(listensock[i], SOMAXCONN) < 0) { dbg_perror(g,"failed to listen on socket"); epmd_cleanup_exit(g,1); } select_fd_set(g, listensock[i]); } + if (bound == 0) { + dbg_perror(g,"unable to bind any address"); + epmd_cleanup_exit(g,1); + } #ifdef HAVE_SYSTEMD_DAEMON } sd_notifyf(0, "READY=1\n" @@ -1005,15 +1056,6 @@ static int conn_open(EpmdVars *g,int fd) for (i = 0; i < g->max_conn; i++) { if (g->conn[i].open == EPMD_FALSE) { - struct sockaddr_in si; - struct sockaddr_in di; -#ifdef HAVE_SOCKLEN_T - socklen_t st; -#else - int st; -#endif - st = sizeof(si); - g->active_conn++; s = &g->conn[i]; @@ -1024,20 +1066,7 @@ static int conn_open(EpmdVars *g,int fd) s->open = EPMD_TRUE; s->keep = EPMD_FALSE; - /* Determine if connection is from localhost */ - if (getpeername(s->fd,(struct sockaddr*) &si,&st) || - st < sizeof(si)) { - /* Failure to get peername is regarded as non local host */ - s->local_peer = EPMD_FALSE; - } else { - /* Only 127.x.x.x and connections from the host's IP address - allowed, no false positives */ - s->local_peer = - (((((unsigned) ntohl(si.sin_addr.s_addr)) & 0xFF000000U) == - 0x7F000000U) || - (getsockname(s->fd,(struct sockaddr*) &di,&st) ? - EPMD_FALSE : si.sin_addr.s_addr == di.sin_addr.s_addr)); - } + s->local_peer = conn_local_peer_check(g, s->fd); dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" : "Non-local peer connected"); @@ -1045,7 +1074,7 @@ static int conn_open(EpmdVars *g,int fd) s->got = 0; s->mod_time = current_time(g); /* Note activity */ - s->buf = (char *)malloc(INBUF_SIZE); + s->buf = malloc(INBUF_SIZE); if (s->buf == NULL) { dbg_printf(g,0,"epmd: Insufficient memory"); @@ -1063,6 +1092,60 @@ static int conn_open(EpmdVars *g,int fd) return EPMD_FALSE; } +static int conn_local_peer_check(EpmdVars *g, int fd) +{ + struct EPMD_SOCKADDR_IN si; + struct EPMD_SOCKADDR_IN di; + + struct sockaddr_in *si4 = (struct sockaddr_in *)&si; + struct sockaddr_in *di4 = (struct sockaddr_in *)&di; + +#if defined(EPMD6) + struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&si; + struct sockaddr_in6 *di6 = (struct sockaddr_in6 *)&di; +#endif + +#ifdef HAVE_SOCKLEN_T + socklen_t st; +#else + int st; +#endif + + st = sizeof(si); + + /* Determine if connection is from localhost */ + if (getpeername(fd,(struct sockaddr*) &si,&st) || + st > sizeof(si)) { + /* Failure to get peername is regarded as non local host */ + return EPMD_FALSE; + } + + /* Only 127.x.x.x and connections from the host's IP address + allowed, no false positives */ +#if defined(EPMD6) + if (si.ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&(si6->sin6_addr))) + return EPMD_TRUE; + + if (si.ss_family == AF_INET) +#endif + if ((((unsigned) ntohl(si4->sin_addr.s_addr)) & 0xFF000000U) == + 0x7F000000U) + return EPMD_TRUE; + + if (getsockname(fd,(struct sockaddr*) &di,&st)) + return EPMD_FALSE; + +#if defined(EPMD6) + if (si.ss_family == AF_INET6) + return IN6_ARE_ADDR_EQUAL( &(si6->sin6_addr), &(di6->sin6_addr)); + if (si.ss_family == AF_INET) +#endif + return si4->sin_addr.s_addr == di4->sin_addr.s_addr; +#if defined(EPMD6) + return EPMD_FALSE; +#endif +} + static int conn_close_fd(EpmdVars *g,int fd) { int i; diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 4de65500e9..d5837e5b8c 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -43,6 +43,7 @@ -export( [ register_name/1, + register_name_ipv6/1, register_names_1/1, register_names_2/1, register_duplicate_name/1, @@ -113,7 +114,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [register_name, register_names_1, register_names_2, + [register_name, register_name_ipv6, + register_names_1, register_names_2, register_duplicate_name, unicode_name, long_unicode_name, get_port_nr, slow_get_port_nr, unregister_others_name_1, unregister_others_name_2, @@ -172,6 +174,24 @@ register_name(Config) when is_list(Config) -> ?line ok = close(Sock), % Unregister ok. +register_name_ipv6(doc) -> + ["Register a name over IPv6"]; +register_name_ipv6(suite) -> + []; +register_name_ipv6(Config) when is_list(Config) -> + % Test if the host has an IPv6 loopback address + Res = gen_tcp:listen(0, [inet6, {ip, {0,0,0,0,0,0,0,1}}]), + case Res of + {ok,LSock} -> + gen_tcp:close(LSock), + ?line ok = epmdrun(), + ?line {ok,Sock} = register_node6("foobar6"), + ?line ok = close(Sock), % Unregister + ok; + _Error -> + {skip, "Host does not have an IPv6 loopback address"} + end. + register_names_1(doc) -> ["Register and unregister two nodes"]; register_names_1(suite) -> @@ -245,9 +265,14 @@ register_node(Name) -> register_node(Name,Port) -> register_node_v2(Port,$M,0,5,5,Name,""). +register_node6(Name) -> + register_node_v2({0,0,0,0,0,0,0,1},?DUMMY_PORT,$M,0,5,5,Name,""). + register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) -> + register_node_v2("localhost", Port, NodeType, Prot, HVsn, LVsn, Name, Extra). +register_node_v2(Addr, Port, NodeType, Prot, HVsn, LVsn, Name, Extra) -> Req = alive2_req(Port, NodeType, Prot, HVsn, LVsn, Name, Extra), - case send_req(Req) of + case send_req(Req, Addr) of {ok,Sock} -> case recv(Sock,4) of {ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} -> @@ -1186,7 +1211,9 @@ send_direct(Sock, Bytes) -> end. send_req(Req) -> - case connect() of + send_req(Req, "localhost"). +send_req(Req, Addr) -> + case connect(Addr) of {ok,Sock} -> case send(Sock, [size16(Req), Req]) of ok -> -- cgit v1.2.3 From 4ce31a8f72726018b1293986b0f627ca2e6bb7fb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 12 Feb 2016 16:51:30 +0100 Subject: erts: Fix wobbling test failure in nif_SUITE ResA may have been GC'd after its last use. --- erts/emulator/test/nif_SUITE.erl | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 3d478654b1..e10460ce78 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -617,7 +617,6 @@ resource_new_do2(Type) -> ?line {PtrA,BinA} = get_resource(Type, ResA), ?line {PtrB,BinB} = get_resource(Type, ResB), ?line true = (PtrA =/= PtrB), - ?line [] = last_resource_dtor_call(), %% forget ResA and make it garbage {{PtrA,BinA}, {ResB,PtrB,BinB}}. -- cgit v1.2.3 From a7f48d4972be0c7984d5cb0e08e73260c0fdbe1b Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 27 Jan 2016 14:45:32 +0100 Subject: Ensure that work is done on the correct type of schedulers Only the actual call to the dirty nif is allowed to execute on dirty schedulers. The dirty nif is not allowed to execute on normal schedulers if dirty schedulers are available. Arrival of exit signals and system tasks, while a process was scheduled for execution on a dirty scheduler, could mess up the process internal state. Preparation for dirty system task has been made, but is currently unused. --- erts/emulator/beam/erl_nif.c | 15 +- erts/emulator/beam/erl_process.c | 650 +++++++++++++++++++++------------- erts/emulator/beam/erl_process.h | 48 ++- erts/emulator/beam/erl_process_dump.c | 10 +- 4 files changed, 458 insertions(+), 265 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index d7a2076d85..962cb4858f 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1772,12 +1772,10 @@ execute_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep); ep->fp = NULL; + erts_smp_atomic32_read_band_mb(&proc->state, ~(ERTS_PSFLG_DIRTY_CPU_PROC + | ERTS_PSFLG_DIRTY_IO_PROC)); result = (*fp)(env, argc, argv); - erts_smp_atomic32_read_band_mb(&proc->state, - ~(ERTS_PSFLG_DIRTY_CPU_PROC - |ERTS_PSFLG_DIRTY_IO_PROC - |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q - |ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)); + if (erts_refc_dectest(&env->mod_nif->rt_dtor_cnt, 0) == 0 && env->mod_nif->mod == NULL) close_lib(env->mod_nif); /* @@ -1825,13 +1823,6 @@ schedule_dirty_nif(ErlNifEnv* env, int flags, int argc, const ERL_NIF_TERM argv[ a = erts_smp_atomic32_read_acqb(&proc->state); while (1) { n = state = a; - /* - * clear any current dirty flags and dirty queue indicators, - * in case the application is shifting a job from one type - * of dirty scheduler to the other - */ - n &= ~(ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC - |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q); if (flags == ERL_NIF_DIRTY_JOB_CPU_BOUND) n |= ERTS_PSFLG_DIRTY_CPU_PROC; else diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index d583118e7b..1765315ed3 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2969,7 +2969,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) sched_wall_time_change(esdp, thr_prgr_active); while (1) { - ErtsMonotonicTime current_time; + ErtsMonotonicTime current_time = 0; aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work) { @@ -3635,6 +3635,13 @@ check_requeue_process(ErtsRunQueue *rq, int prio_q) return 0; } +static ERTS_INLINE void +free_proxy_proc(Process *proxy) +{ + ASSERT(erts_smp_atomic32_read_nob(&proxy->state) & ERTS_PSFLG_PROXY); + erts_free(ERTS_ALC_T_PROC, proxy); +} + #ifdef ERTS_SMP static ErtsRunQueue * @@ -3949,9 +3956,6 @@ evacuate_run_queue(ErtsRunQueue *rq, erts_aint32_t state; Process *proc; int notify = 0; -#ifdef ERTS_DIRTY_SCHEDULERS - int requeue; -#endif to_rq = NULL; #ifdef ERTS_DIRTY_SCHEDULERS @@ -3966,49 +3970,94 @@ evacuate_run_queue(ErtsRunQueue *rq, proc = dequeue_process(rq, prio_q, &state); while (proc) { -#ifdef ERTS_DIRTY_SCHEDULERS - requeue = 1; + Process *real_proc; + int prio; + erts_aint32_t max_qbit, qbit, real_state; + + prio = ERTS_PSFLGS_GET_PRQ_PRIO(state); + qbit = ((erts_aint32_t) 1) << prio; + + if (!(state & ERTS_PSFLG_PROXY)) { + real_proc = proc; + real_state = state; + } + else { + real_proc = erts_proc_lookup_raw(proc->common.id); + if (!real_proc) { + free_proxy_proc(proc); + goto handle_next_proc; + } + real_state = erts_smp_atomic32_read_acqb(&real_proc->state); + } + + max_qbit = (state >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET); + max_qbit &= ERTS_PSFLGS_QMASK; + max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS; + max_qbit &= -max_qbit; + + if (qbit > max_qbit) { + /* Process already queued with higher prio; drop it... */ + if (real_proc != proc) + free_proxy_proc(proc); + else { + erts_aint32_t clr_bits; +#ifdef DEBUG + erts_aint32_t old; #endif - 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; -#ifdef ERTS_DIRTY_SCHEDULERS - requeue = 0; + + clr_bits = ERTS_PSFLG_IN_RUNQ; + clr_bits |= qbit << ERTS_PSFLGS_IN_PRQ_MASK_OFFSET; + +#ifdef DEBUG + old = +#else + (void) #endif + erts_smp_atomic32_read_band_mb(&proc->state, + ~clr_bits); + ASSERT((old & clr_bits) == clr_bits); + + } + + goto handle_next_proc; } + #ifdef ERTS_DIRTY_SCHEDULERS - else if (state & ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q) { + + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) { + erts_aint32_t dqbit = qbit; #ifdef DEBUG - erts_aint32_t old = -#else - (void) + erts_aint32_t old_dqbit; #endif - erts_smp_atomic32_read_band_nob(&proc->state, - ~(ERTS_PSFLG_DIRTY_CPU_PROC - | ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q)); - /* assert that no other dirty flags are set */ - ASSERT(!(old & (ERTS_PSFLG_DIRTY_IO_PROC|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q))); - } else if (state & ERTS_PSFLG_DIRTY_IO_PROC_IN_Q) { + + if (rq == ERTS_DIRTY_CPU_RUNQ) + dqbit <<= ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET; + else { + ASSERT(rq == ERTS_DIRTY_IO_RUNQ); + dqbit <<= ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET; + } + #ifdef DEBUG - erts_aint32_t old = + old_dqbit = (int) #else - (void) + (void) #endif - erts_smp_atomic32_read_band_nob(&proc->state, - ~(ERTS_PSFLG_DIRTY_IO_PROC - | ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)); - /* assert that no other dirty flags are set */ - ASSERT(!(old & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q))); + erts_smp_atomic32_read_band_mb(&real_proc->dirty_state, + ~dqbit); + ASSERT(old_dqbit & dqbit); } - if (requeue) { -#else - else { #endif + + if (ERTS_PSFLG_BOUND & real_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_PSFLGS_GET_PRQ_PRIO(state); erts_smp_runq_unlock(rq); @@ -4031,6 +4080,8 @@ evacuate_run_queue(ErtsRunQueue *rq, erts_smp_runq_lock(rq); } + + handle_next_proc: proc = dequeue_process(rq, prio_q, &state); } if (notify) @@ -5974,19 +6025,96 @@ make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio) return proxy; } -static ERTS_INLINE void -free_proxy_proc(Process *proxy) -{ - ASSERT(erts_smp_atomic32_read_nob(&proxy->state) & ERTS_PSFLG_PROXY); - erts_free(ERTS_ALC_T_PROC, proxy); -} - #define ERTS_ENQUEUE_NOT 0 #define ERTS_ENQUEUE_NORMAL_QUEUE 1 -#ifdef ERTS_DIRTY_SCHEDULERS #define ERTS_ENQUEUE_DIRTY_CPU_QUEUE 2 #define ERTS_ENQUEUE_DIRTY_IO_QUEUE 3 + +#ifdef ERTS_DIRTY_SCHEDULERS + +static int +check_dirty_enqueue_in_prio_queue(Process *c_p, + erts_aint32_t *newp, + erts_aint32_t actual, + erts_aint32_t aprio, + erts_aint32_t qbit) +{ + int queue; + erts_aint32_t dact, max_qbit; + + /* Termination should be done on an ordinary scheduler */ + if (actual & ERTS_PSFLG_EXITING) { + *newp &= ~ERTS_PSFLGS_DIRTY_WORK; + return ERTS_ENQUEUE_NORMAL_QUEUE; + } + + /* + * If we have system tasks, we enqueue on ordinary run-queue + * and take care of those system tasks first. + */ + if (actual & ERTS_PSFLG_ACTIVE_SYS) + return ERTS_ENQUEUE_NORMAL_QUEUE; + + dact = erts_smp_atomic32_read_mb(&c_p->dirty_state); + if (actual & (ERTS_PSFLG_DIRTY_ACTIVE_SYS + | ERTS_PSFLG_DIRTY_CPU_PROC)) { + max_qbit = ((dact >> ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET) + & ERTS_PDSFLGS_QMASK); + queue = ERTS_ENQUEUE_DIRTY_CPU_QUEUE; + } + else { + ASSERT(actual & ERTS_PSFLG_DIRTY_IO_PROC); + max_qbit = ((dact >> ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET) + & ERTS_PDSFLGS_QMASK); + queue = ERTS_ENQUEUE_DIRTY_IO_QUEUE; + } + + max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS; + max_qbit &= -max_qbit; + + if (qbit >= max_qbit) + return ERTS_ENQUEUE_NOT; /* Already queued in higher or equal prio */ + if ((actual & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLGS_USR_PRIO_MASK)) + != (aprio << ERTS_PSFLGS_USR_PRIO_OFFSET)) { + /* + * Process struct already enqueued, or actual prio not + * equal to user prio, i.e., enqueue using proxy. + */ + return -1*queue; + } + + *newp |= ERTS_PSFLG_IN_RUNQ; + return queue; +} + +static ERTS_INLINE int +fin_dirty_enq_s_change(Process *p, + int pstruct_reserved, + erts_aint32_t enq_prio, + int qmask_offset) +{ + erts_aint32_t qbit = 1 << enq_prio; + qbit <<= qmask_offset; + + if (qbit & erts_smp_atomic32_read_bor_mb(&p->dirty_state, qbit)) { + /* Already enqueue by someone else... */ + if (pstruct_reserved) { + /* We reserved process struct for enqueue; clear it... */ +#ifdef DEBUG + erts_aint32_t old = +#else + (void) #endif + erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_IN_RUNQ); + ASSERT(old & ERTS_PSFLG_IN_RUNQ); + } + return 0; + } + + return !0; +} + +#endif /* ERTS_DIRTY_SCHEDULERS */ static ERTS_INLINE int check_enqueue_in_prio_queue(Process *c_p, @@ -6002,61 +6130,14 @@ check_enqueue_in_prio_queue(Process *c_p, *prq_prio_p = aprio; #ifdef ERTS_DIRTY_SCHEDULERS - if (actual & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) { - /* - * If we have system tasks of a priority higher - * or equal to the user priority, we enqueue - * on ordinary run-queue and take care of - * those system tasks first. - */ - if (actual & ERTS_PSFLG_ACTIVE_SYS) { - erts_aint32_t uprio, stprio, qmask; - uprio = (actual >> ERTS_PSFLGS_USR_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK; - if (aprio < uprio) - goto enqueue_normal_runq; /* system tasks with higher prio */ - erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS); - qmask = c_p->sys_task_qs->qmask; - erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS); - switch (qmask & -qmask) { - case MAX_BIT: - stprio = PRIORITY_MAX; - break; - case HIGH_BIT: - stprio = PRIORITY_HIGH; - break; - case NORMAL_BIT: - stprio = PRIORITY_NORMAL; - break; - case LOW_BIT: - stprio = PRIORITY_LOW; - break; - default: - stprio = PRIORITY_LOW+1; - break; - } - if (stprio <= uprio) - goto enqueue_normal_runq; /* system tasks with higher prio */ - } - - /* Enqueue in dirty run queue if not already enqueued */ - if (actual & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)) - return ERTS_ENQUEUE_NOT; /* already in queue */ - if (actual & ERTS_PSFLG_DIRTY_CPU_PROC) { - *newp |= ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q; - if (actual & ERTS_PSFLG_IN_RUNQ) - return -ERTS_ENQUEUE_DIRTY_CPU_QUEUE; /* use proxy */ - *newp |= ERTS_PSFLG_IN_RUNQ; - return ERTS_ENQUEUE_DIRTY_CPU_QUEUE; - } - *newp |= ERTS_PSFLG_DIRTY_IO_PROC_IN_Q; - if (actual & ERTS_PSFLG_IN_RUNQ) - return -ERTS_ENQUEUE_DIRTY_IO_QUEUE; /* use proxy */ - *newp |= ERTS_PSFLG_IN_RUNQ; - return ERTS_ENQUEUE_DIRTY_IO_QUEUE; + if (actual & ERTS_PSFLGS_DIRTY_WORK) { + int res = check_dirty_enqueue_in_prio_queue(c_p, newp, actual, + aprio, qbit); + if (res != ERTS_ENQUEUE_NORMAL_QUEUE) + return res; } - - enqueue_normal_runq: #endif + max_qbit = (actual >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET) & ERTS_PSFLGS_QMASK; max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS; max_qbit &= -max_qbit; @@ -6088,6 +6169,65 @@ check_enqueue_in_prio_queue(Process *c_p, return ERTS_ENQUEUE_NORMAL_QUEUE; } +static ERTS_INLINE ErtsRunQueue * +select_enqueue_run_queue(int enqueue, int enq_prio, Process *p, erts_aint32_t state) +{ + + switch (enqueue) { + + case ERTS_ENQUEUE_NOT: + + return NULL; + +#ifdef ERTS_DIRTY_SCHEDULERS + + case ERTS_ENQUEUE_DIRTY_CPU_QUEUE: + case -ERTS_ENQUEUE_DIRTY_CPU_QUEUE: + + if (fin_dirty_enq_s_change(p, enqueue > 0, enq_prio, + ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET)) + return ERTS_DIRTY_CPU_RUNQ; + + return NULL; + + + case ERTS_ENQUEUE_DIRTY_IO_QUEUE: + case -ERTS_ENQUEUE_DIRTY_IO_QUEUE: + + if (fin_dirty_enq_s_change(p, enqueue > 0, enq_prio, + ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET)) + return ERTS_DIRTY_IO_RUNQ; + + return NULL; + +#endif + + default: { + ErtsRunQueue* runq; + + ASSERT(enqueue == ERTS_ENQUEUE_NORMAL_QUEUE + || enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE); + + runq = erts_get_runq_proc(p); + +#ifdef ERTS_SMP + if (!(ERTS_PSFLG_BOUND & state)) { + ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio); + if (new_runq) { + RUNQ_SET_RQ(&p->run_queue, new_runq); + runq = new_runq; + } + } +#endif + + ASSERT(runq); + + return runq; + } + } +} + + /* * schedule_out_process() return with c_rq locked. */ @@ -6096,11 +6236,7 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces { erts_aint32_t a, e, n, enq_prio = -1; int enqueue; /* < 0 -> use proxy */ - Process* sched_p; ErtsRunQueue* runq; -#ifdef ERTS_SMP - int check_emigration_need; -#endif a = state; @@ -6112,7 +6248,7 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces enqueue = ERTS_ENQUEUE_NOT; n &= ~(ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS); - if (a & ERTS_PSFLG_ACTIVE_SYS + if (a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS) || (a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); } @@ -6121,16 +6257,17 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces break; } - switch (enqueue) { - case ERTS_ENQUEUE_NOT: + runq = select_enqueue_run_queue(enqueue, enq_prio, p, n); + + if (!runq) { + if (erts_system_profile_flags.runnable_procs) { /* Status lock prevents out of order "runnable proc" trace msgs */ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); - if (!(a & ERTS_PSFLG_ACTIVE_SYS) - && (!(a & ERTS_PSFLG_ACTIVE) - || (a & ERTS_PSFLG_SUSPENDED))) { + if (!(a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS)) + && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) { /* Process inactive */ profile_runnable_proc(p, am_inactive); } @@ -6143,98 +6280,76 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces return 0; -#ifdef ERTS_DIRTY_SCHEDULERS -#ifdef ERTS_SMP - case ERTS_ENQUEUE_DIRTY_CPU_QUEUE: - case -ERTS_ENQUEUE_DIRTY_CPU_QUEUE: - runq = ERTS_DIRTY_CPU_RUNQ; - ASSERT(ERTS_SCHEDULER_IS_DIRTY_CPU(runq->scheduler)); -#ifdef ERTS_SMP - check_emigration_need = 0; -#endif - break; + } + else { + Process* sched_p; - case ERTS_ENQUEUE_DIRTY_IO_QUEUE: - case -ERTS_ENQUEUE_DIRTY_IO_QUEUE: - runq = ERTS_DIRTY_IO_RUNQ; - ASSERT(ERTS_SCHEDULER_IS_DIRTY_IO(runq->scheduler)); -#ifdef ERTS_SMP - check_emigration_need = 0; -#endif - break; -#endif -#endif + ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & (ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_DIRTY_ACTIVE_SYS))); - default: - ASSERT(enqueue == ERTS_ENQUEUE_NORMAL_QUEUE - || enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE); + if (enqueue < 0) + sched_p = make_proxy_proc(proxy, p, enq_prio); + else { + sched_p = p; + if (proxy) + free_proxy_proc(proxy); + } - runq = erts_get_runq_proc(p); -#ifdef ERTS_SMP - check_emigration_need = !(ERTS_PSFLG_BOUND & n); -#endif - break; - } + ASSERT(runq); - ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & ERTS_PSFLG_ACTIVE_SYS)); + erts_smp_runq_lock(runq); - if (enqueue < 0) - sched_p = make_proxy_proc(proxy, p, enq_prio); - else { - sched_p = p; - if (proxy) - free_proxy_proc(proxy); - } + /* Enqueue the process */ + enqueue_process(runq, (int) enq_prio, sched_p); -#ifdef ERTS_SMP - if (check_emigration_need) { - ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio); - if (new_runq) { - RUNQ_SET_RQ(&sched_p->run_queue, new_runq); - runq = new_runq; - } - } -#endif + if (runq == c_rq) + return 1; - ASSERT(runq); + erts_smp_runq_unlock(runq); - erts_smp_runq_lock(runq); + smp_notify_inc_runq(runq); - /* Enqueue the process */ - enqueue_process(runq, (int) enq_prio, sched_p); + erts_smp_runq_lock(c_rq); - if (runq == c_rq) return 1; - erts_smp_runq_unlock(runq); - smp_notify_inc_runq(runq); - erts_smp_runq_lock(c_rq); - return 1; + } + } static ERTS_INLINE void -add2runq(Process *p, erts_aint32_t state, erts_aint32_t prio) +add2runq(int enqueue, erts_aint32_t prio, + Process *proc, erts_aint32_t state, + Process **proxy) { - ErtsRunQueue *runq = erts_get_runq_proc(p); + ErtsRunQueue *runq; -#ifdef ERTS_SMP - if (!(ERTS_PSFLG_BOUND & state)) { - ErtsRunQueue *new_runq = erts_check_emigration_need(runq, (int) prio); - if (new_runq) { - RUNQ_SET_RQ(&p->run_queue, new_runq); - runq = new_runq; - } - } -#endif - ASSERT(runq); + runq = select_enqueue_run_queue(enqueue, prio, proc, state); - erts_smp_runq_lock(runq); + if (runq) { + Process *sched_p; - /* Enqueue the process */ - enqueue_process(runq, (int) prio, p); + if (enqueue > 0) + sched_p = proc; + else { + Process *pxy; - erts_smp_runq_unlock(runq); - smp_notify_inc_runq(runq); + if (!proxy) + pxy = NULL; + else { + pxy = *proxy; + *proxy = NULL; + } + sched_p = make_proxy_proc(pxy, proc, prio); + } + + erts_smp_runq_lock(runq); + + /* Enqueue the process */ + enqueue_process(runq, (int) prio, sched_p); + erts_smp_runq_unlock(runq); + smp_notify_inc_runq(runq); + } } static ERTS_INLINE int @@ -6340,10 +6455,7 @@ schedule_process(Process *p, erts_aint32_t in_state, ErtsProcLocks locks) &state, &enq_prio, locks); - if (enqueue != ERTS_ENQUEUE_NOT) - add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), - state, - enq_prio); + add2runq(enqueue, enq_prio, p, state, NULL); } void @@ -6403,16 +6515,7 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) prof_runnable_procs = 0; } - if (enqueue != ERTS_ENQUEUE_NOT) { - Process *sched_p; - if (enqueue > 0) - sched_p = p; - else { - sched_p = make_proxy_proc(proxy, p, enq_prio); - proxy = NULL; - } - add2runq(sched_p, n, enq_prio); - } + add2runq(enqueue, enq_prio, p, n, &proxy); cleanup: @@ -6508,10 +6611,7 @@ resume_process(Process *p, ErtsProcLocks locks) &state, &enq_prio, locks); - if (enqueue) - add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), - state, - enq_prio); + add2runq(enqueue, enq_prio, p, state, NULL); } int @@ -9361,15 +9461,16 @@ Process *schedule(Process *p, int calls) #ifdef ERTS_SMP ErtsMigrationPaths *mps; ErtsMigrationPath *mp; - ErtsProcList *pnd_xtrs = rq->procs.pending_exiters; - if (erts_proclist_fetch(&pnd_xtrs, NULL)) { + + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + ErtsProcList *pnd_xtrs = rq->procs.pending_exiters; + if (erts_proclist_fetch(&pnd_xtrs, NULL)) { rq->procs.pending_exiters = NULL; erts_smp_runq_unlock(rq); handle_pending_exiters(pnd_xtrs); erts_smp_runq_lock(rq); } - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { if (rq->check_balance_reds <= 0) check_balance(rq); @@ -9564,11 +9665,12 @@ Process *schedule(Process *p, int calls) pick_next_process: { erts_aint32_t psflg_band_mask; int prio_q; - int qmask; + int qmask, qbit; flags = ERTS_RUNQ_FLGS_GET_NOB(rq); qmask = (int) (flags & ERTS_RUNQ_FLGS_PROCS_QMASK); - switch (qmask & -qmask) { + qbit = qmask & -qmask; + switch (qbit) { case MAX_BIT: prio_q = PRIORITY_MAX; break; @@ -9596,20 +9698,11 @@ Process *schedule(Process *p, int calls) ASSERT(p); /* Wrong qmask in rq->flags? */ - psflg_band_mask = ~(((erts_aint32_t) 1) << (ERTS_PSFLGS_GET_PRQ_PRIO(state) - + ERTS_PSFLGS_IN_PRQ_MASK_OFFSET)); - -#ifdef ERTS_DIRTY_SCHEDULERS - ASSERT((state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) != - (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)); - if (state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) { - ASSERT((ERTS_SCHEDULER_IS_DIRTY_CPU(esdp) && (state & ERTS_PSFLG_DIRTY_CPU_PROC)) || - (ERTS_SCHEDULER_IS_DIRTY_IO(esdp) && (state & ERTS_PSFLG_DIRTY_IO_PROC))); - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !(state & ERTS_PSFLG_ACTIVE_SYS)) - goto pick_next_process; - state &= ~(ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q); - } -#endif + if (ERTS_SCHEDULER_IS_DIRTY(esdp)) + psflg_band_mask = ~((erts_aint32_t) 0); + else + psflg_band_mask = ~(((erts_aint32_t) 1) << (ERTS_PSFLGS_GET_PRQ_PRIO(state) + + ERTS_PSFLGS_IN_PRQ_MASK_OFFSET)); if (!(state & ERTS_PSFLG_PROXY)) psflg_band_mask &= ~ERTS_PSFLG_IN_RUNQ; @@ -9632,9 +9725,11 @@ Process *schedule(Process *p, int calls) | ERTS_PSFLG_RUNNING_SYS))) { tmp = state & (ERTS_PSFLG_SUSPENDED | ERTS_PSFLG_PENDING_EXIT - | ERTS_PSFLG_ACTIVE_SYS); + | ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_DIRTY_ACTIVE_SYS); if (tmp != ERTS_PSFLG_SUSPENDED) { - if (state & ERTS_PSFLG_ACTIVE_SYS) + if (state & (ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_DIRTY_ACTIVE_SYS)) new |= ERTS_PSFLG_RUNNING_SYS; else new |= ERTS_PSFLG_RUNNING; @@ -9647,7 +9742,8 @@ Process *schedule(Process *p, int calls) | ERTS_PSFLG_FREE)) || ((state & (ERTS_PSFLG_SUSPENDED | ERTS_PSFLG_PENDING_EXIT - | ERTS_PSFLG_ACTIVE_SYS)) + | ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_DIRTY_ACTIVE_SYS)) == ERTS_PSFLG_SUSPENDED)) { if (state & ERTS_PSFLG_FREE) erts_proc_dec_refc(p); @@ -9666,10 +9762,42 @@ Process *schedule(Process *p, int calls) esdp->current_process = p; + + reds = context_reds; + +#ifdef ERTS_SMP + + erts_smp_runq_unlock(rq); + +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { +#ifdef DEBUG + int old_dqbit; +#endif + int dqbit = qbit; + + if (rq == ERTS_DIRTY_CPU_RUNQ) + dqbit <<= ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET; + else { + ASSERT(rq == ERTS_DIRTY_IO_RUNQ); + dqbit <<= ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET; + } + +#ifdef DEBUG + old_dqbit = (int) +#else + (void) +#endif + erts_smp_atomic32_read_band_mb(&p->dirty_state, ~dqbit); + ASSERT(old_dqbit & dqbit); + } +#endif /* ERTS_DIRTY_SCHEDULERS */ + +#endif /* ERTS_SMP */ + } #ifdef ERTS_SMP - erts_smp_runq_unlock(rq); if (flags & ERTS_RUNQ_FLG_PROTECTED) (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); @@ -9702,15 +9830,56 @@ Process *schedule(Process *p, int calls) erts_smp_spin_unlock(&erts_sched_stat.lock); } - if (ERTS_PROC_PENDING_EXIT(p)) { + ASSERT(!p->scheduler_data); + p->scheduler_data = esdp; + + state = erts_smp_atomic32_read_nob(&p->state); + +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (!!(state & ERTS_PSFLGS_DIRTY_WORK) + & !(state & ERTS_PSFLG_ACTIVE_SYS)) { + /* Migrate to dirty scheduler... */ + sunlock_sched_out_proc: + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + p->fcalls = reds; + goto sched_out_proc; + + } + } + else { + if (state & (ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_PENDING_EXIT + | ERTS_PSFLG_EXITING)) { + /* Migrate to normal scheduler... */ + goto sunlock_sched_out_proc; + } + if ((state & ERTS_PSFLG_DIRTY_ACTIVE_SYS) + && rq == ERTS_DIRTY_IO_RUNQ) { + /* Migrate to dirty cpu scheduler... */ + goto sunlock_sched_out_proc; + } + + ASSERT((state & ERTS_PSFLG_DIRTY_ACTIVE_SYS) + || *p->i == (BeamInstr) em_call_nif); + + ASSERT(rq == ERTS_DIRTY_CPU_RUNQ + ? (state & (ERTS_PSFLG_DIRTY_CPU_PROC + | ERTS_PSFLG_DIRTY_ACTIVE_SYS)) + : (rq == ERTS_DIRTY_IO_RUNQ + && (state & ERTS_PSFLG_DIRTY_IO_PROC))); + } +#endif + + if (state & ERTS_PSFLG_PENDING_EXIT) { erts_handle_pending_exit(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS); state = erts_smp_atomic32_read_nob(&p->state); } - ASSERT(!p->scheduler_data); - p->scheduler_data = esdp; -#endif - reds = context_reds; + +#endif /* ERTS_SMP */ + + p->fcalls = reds; if (IS_TRACED(p)) { if (state & ERTS_PSFLG_EXITING) { @@ -9739,7 +9908,8 @@ Process *schedule(Process *p, int calls) reds -= execute_sys_tasks(p, &state, reds); if (reds <= 0 #ifdef ERTS_DIRTY_SCHEDULERS - || (state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) + || ERTS_SCHEDULER_IS_DIRTY(esdp) + || (state & ERTS_PSFLGS_DIRTY_WORK) #endif ) { p->fcalls = reds; @@ -9787,7 +9957,6 @@ Process *schedule(Process *p, int calls) proxy_p = NULL; } - p->fcalls = reds; ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); /* Never run a suspended process */ @@ -10732,6 +10901,9 @@ static void early_init_process_struct(void *varg, Eterm data) Process *proc = arg->proc; proc->common.id = make_internal_pid(data); +#ifdef ERTS_DIRTY_SCHEDULERS + erts_smp_atomic32_init_nob(&proc->dirty_state, 0); +#endif erts_smp_atomic32_init_relb(&proc->state, arg->state); #ifdef ERTS_SMP @@ -11194,6 +11366,9 @@ void erts_init_empty_process(Process *p) p->last_old_htop = NULL; #endif +#ifdef ERTS_DIRTY_SCHEDULERS + erts_smp_atomic32_init_nob(&p->dirty_state, 0); +#endif erts_smp_atomic32_init_nob(&p->state, (erts_aint32_t) PRIORITY_NORMAL); #ifdef ERTS_SMP @@ -11395,7 +11570,9 @@ set_proc_exiting(Process *p, ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) == ERTS_PROC_LOCKS_ALL); enqueue = change_proc_schedule_state(p, - ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT, + (ERTS_PSFLG_SUSPENDED + | ERTS_PSFLG_PENDING_EXIT + | ERTS_PSFLGS_DIRTY_WORK), ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE, &state, &enq_prio, @@ -11429,10 +11606,7 @@ set_proc_exiting(Process *p, } #endif - if (enqueue) - add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), - state, - enq_prio); + add2runq(enqueue, enq_prio, p, state, NULL); } static ERTS_INLINE erts_aint32_t @@ -11535,6 +11709,11 @@ save_pending_exiter(Process *p) else rq = esdp->run_queue; +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) + rq = ERTS_RUNQ_IX(0); /* Handle on ordinary scheduler */ +#endif + plp = proclist_create(p); erts_smp_runq_lock(rq); @@ -11544,13 +11723,8 @@ save_pending_exiter(Process *p) non_empty_runq(rq); erts_smp_runq_unlock(rq); -#ifdef ERTS_DIRTY_SCHEDULERS - if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) - wake_dirty_schedulers(rq, 0); - else -#endif - wake_scheduler(rq); + wake_scheduler(rq); } #endif diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 10c6fa4a67..32e458aa1d 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1033,6 +1033,9 @@ struct process { ErtsProcSysTaskQs *sys_task_qs; erts_smp_atomic32_t state; /* Process state flags (see ERTS_PSFLG_*) */ +#ifdef ERTS_DIRTY_SCHEDULERS + erts_smp_atomic32_t dirty_state; /* Process dirty state flags (see ERTS_PDSFLG_*) */ +#endif #ifdef ERTS_SMP ErlMessageInQueue msg_inq; @@ -1150,15 +1153,14 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15) #define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16) #define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17) -#ifdef ERTS_DIRTY_SCHEDULERS #define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(18) #define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(19) -#define ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q ERTS_PSFLG_BIT(20) -#define ERTS_PSFLG_DIRTY_IO_PROC_IN_Q ERTS_PSFLG_BIT(21) -#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 22) -#else -#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 18) -#endif +#define ERTS_PSFLG_DIRTY_ACTIVE_SYS ERTS_PSFLG_BIT(20) +#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 20) + +#define ERTS_PSFLGS_DIRTY_WORK (ERTS_PSFLG_DIRTY_CPU_PROC \ + | ERTS_PSFLG_DIRTY_IO_PROC \ + | ERTS_PSFLG_DIRTY_ACTIVE_SYS) #define ERTS_PSFLGS_IN_PRQ_MASK (ERTS_PSFLG_IN_PRQ_MAX \ | ERTS_PSFLG_IN_PRQ_HIGH \ @@ -1170,7 +1172,37 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLGS_GET_USR_PRIO(PSFLGS) \ (((PSFLGS) >> ERTS_PSFLGS_USR_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK) #define ERTS_PSFLGS_GET_PRQ_PRIO(PSFLGS) \ - (((PSFLGS) >> ERTS_PSFLGS_USR_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK) + (((PSFLGS) >> ERTS_PSFLGS_PRQ_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK) + +#ifdef ERTS_DIRTY_SCHEDULERS + +/* + * Flags in the dirty_state field. + */ + +#define ERTS_PDSFLG_IN_CPU_PRQ_MAX (((erts_aint32_t) 1) << 0) +#define ERTS_PDSFLG_IN_CPU_PRQ_HIGH (((erts_aint32_t) 1) << 1) +#define ERTS_PDSFLG_IN_CPU_PRQ_NORMAL (((erts_aint32_t) 1) << 2) +#define ERTS_PDSFLG_IN_CPU_PRQ_LOW (((erts_aint32_t) 1) << 3) +#define ERTS_PDSFLG_IN_IO_PRQ_MAX (((erts_aint32_t) 1) << 4) +#define ERTS_PDSFLG_IN_IO_PRQ_HIGH (((erts_aint32_t) 1) << 5) +#define ERTS_PDSFLG_IN_IO_PRQ_NORMAL (((erts_aint32_t) 1) << 6) +#define ERTS_PDSFLG_IN_IO_PRQ_LOW (((erts_aint32_t) 1) << 7) + +#define ERTS_PDSFLGS_QMASK ERTS_PSFLGS_QMASK +#define ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET 0 +#define ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET ERTS_PSFLGS_QMASK_BITS + +#define ERTS_PDSFLG_IN_CPU_PRQ_MASK (ERTS_PDSFLG_IN_CPU_PRQ_MAX \ + | ERTS_PDSFLG_IN_CPU_PRQ_HIGH \ + | ERTS_PDSFLG_IN_CPU_PRQ_NORMAL\ + | ERTS_PDSFLG_IN_CPU_PRQ_LOW) +#define ERTS_PDSFLG_IN_IO_PRQ_MASK (ERTS_PDSFLG_IN_CPU_PRQ_MAX \ + | ERTS_PDSFLG_IN_CPU_PRQ_HIGH \ + | ERTS_PDSFLG_IN_CPU_PRQ_NORMAL\ + | ERTS_PDSFLG_IN_CPU_PRQ_LOW) +#endif + /* * Static flags that do not change after process creation. diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index 25f0b1ed38..598c1d66ba 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -617,7 +617,7 @@ erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg) { if (psflg) erts_print(to, to_arg, " | "); - for (i = 0; i < ERTS_PSFLG_MAX && psflg; i++) { + for (i = 0; i <= ERTS_PSFLG_MAX && psflg; i++) { erts_aint32_t chk = (1 << i); if (psflg & chk) { switch (chk) { @@ -657,16 +657,12 @@ erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg) { erts_print(to, to_arg, "PROXY"); break; case ERTS_PSFLG_DELAYED_SYS: erts_print(to, to_arg, "DELAYED_SYS"); break; -#ifdef ERTS_DIRTY_SCHEDULERS case ERTS_PSFLG_DIRTY_CPU_PROC: erts_print(to, to_arg, "DIRTY_CPU_PROC"); break; case ERTS_PSFLG_DIRTY_IO_PROC: erts_print(to, to_arg, "DIRTY_IO_PROC"); break; - case ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q: - erts_print(to, to_arg, "DIRTY_CPU_PROC_IN_Q"); break; - case ERTS_PSFLG_DIRTY_IO_PROC_IN_Q: - erts_print(to, to_arg, "DIRTY_IO_PROC_IN_Q"); break; -#endif + case ERTS_PSFLG_DIRTY_ACTIVE_SYS: + erts_print(to, to_arg, "DIRTY_ACTIVE_SYS"); break; default: erts_print(to, to_arg, "UNKNOWN(%d)", chk); break; } -- cgit v1.2.3 From 57cff1f4286a9511926ee37c015b3f6f081d64d0 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Thu, 28 Jan 2016 16:37:33 -0500 Subject: Add dirty scheduler process termination test In scheduler_SUITE add a new test that runs a single dirty I/O scheduler and launches a number of dirty I/O NIF calls that each sleep for 3 seconds. Given the single scheduler, the first of these will run while the rest queue up. Then start killing these processes, and verify they call exit correctly. --- erts/emulator/test/scheduler_SUITE.erl | 50 +++++++++++++++++++++- .../test/scheduler_SUITE_data/Makefile.src | 8 ++++ .../test/scheduler_SUITE_data/scheduler_SUITE.c | 31 ++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 erts/emulator/test/scheduler_SUITE_data/Makefile.src create mode 100644 erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c (limited to 'erts') diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 986a73ebb1..684e8a3b4e 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -56,6 +56,7 @@ scheduler_threads/1, scheduler_suspend/1, dirty_scheduler_threads/1, + dirty_scheduler_exit/1, reader_groups/1]). -define(DEFAULT_TIMEOUT, ?t:minutes(15)). @@ -70,7 +71,7 @@ all() -> equal_and_high_with_part_time_max, equal_with_high, equal_with_high_max, bound_process, {group, scheduler_bind}, scheduler_threads, scheduler_suspend, - dirty_scheduler_threads, + dirty_scheduler_threads, dirty_scheduler_exit, reader_groups]. groups() -> @@ -1166,6 +1167,53 @@ get_dsstate(Config, Cmd) -> stop_node(Node), {DSCPU, DSCPUOnln, DSIO}. +dirty_scheduler_exit(Config) when is_list(Config) -> + try + erlang:system_info(dirty_cpu_schedulers), + dirty_scheduler_exit_test(Config) + catch + error:badarg -> + {skipped, "No dirty scheduler support"} + end. + +dirty_scheduler_exit_test(Config) -> + {ok, Node} = start_node(Config, "+SDio 1"), + [ok] = mcall(Node, + [fun() -> + Path = ?config(data_dir, Config), + Lib = atom_to_list(?MODULE), + ok = erlang:load_nif(filename:join(Path,Lib), []), + ok = test_dirty_scheduler_exit() + end]), + stop_node(Node), + ok. + +test_dirty_scheduler_exit() -> + process_flag(trap_exit,true), + test_dse(10,[]). +test_dse(0,Pids) -> + timer:sleep(100), + kill_dse(Pids,[]); +test_dse(N,Pids) -> + Pid = spawn_link(fun dirty_sleeper/0), + test_dse(N-1,[Pid|Pids]). +kill_dse([],Killed) -> + wait_dse(Killed); +kill_dse([Pid|Pids],AlreadyKilled) -> + exit(Pid,kill), + kill_dse(Pids,[Pid|AlreadyKilled]). +wait_dse([]) -> + ok; +wait_dse([Pid|Pids]) -> + receive + {'EXIT',Pid,killed} -> + ok + end, + wait_dse(Pids). + +dirty_sleeper() -> + erlang:nif_error({error,?MODULE}). + scheduler_suspend(Config) when is_list(Config) -> ?line Dog = ?t:timetrap(?t:minutes(5)), ?line lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end, diff --git a/erts/emulator/test/scheduler_SUITE_data/Makefile.src b/erts/emulator/test/scheduler_SUITE_data/Makefile.src new file mode 100644 index 0000000000..859112cf19 --- /dev/null +++ b/erts/emulator/test/scheduler_SUITE_data/Makefile.src @@ -0,0 +1,8 @@ + +SCHEDULER_LIBS = scheduler_SUITE@dll@ + +all: $(SCHEDULER_LIBS) + +@SHLIB_RULES@ + +$(SCHEDULER_LIBS): scheduler_SUITE.c diff --git a/erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c b/erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c new file mode 100644 index 0000000000..022858c114 --- /dev/null +++ b/erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c @@ -0,0 +1,31 @@ +#include +#include "erl_nif.h" + +static int +load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) +{ + ErlNifSysInfo sys_info; + enif_system_info(&sys_info, sizeof(ErlNifSysInfo)); + if (!sys_info.smp_support || !sys_info.dirty_scheduler_support) + return 1; + return 0; +} + +static ERL_NIF_TERM +dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT + sleep(3); +#endif + return enif_make_atom(env, "ok"); +} + +static ErlNifFunc funcs[] = { +#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT + {"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND} +#else + {"dirty_sleeper", 0, dirty_sleeper, 0} +#endif +}; + +ERL_NIF_INIT(scheduler_SUITE, funcs, &load, NULL, NULL, NULL); -- cgit v1.2.3 From 5d8a987285cf13e8d87731a6284c7df7ed3dac70 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 12 Feb 2016 16:20:23 +0100 Subject: Fix unique_SUITE for dirty schedulers --- erts/emulator/test/unique_SUITE.erl | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl index 6fa634b886..a6884d63a6 100644 --- a/erts/emulator/test/unique_SUITE.erl +++ b/erts/emulator/test/unique_SUITE.erl @@ -267,10 +267,21 @@ calc_sched_bits(NoScheds, Shift) when NoScheds < 1 bsl Shift -> calc_sched_bits(NoScheds, Shift) -> calc_sched_bits(NoScheds, Shift+1). +schedulers() -> + S = erlang:system_info(schedulers), + try + DCPUS = erlang:system_info(dirty_cpu_schedulers), + DIOS = erlang:system_info(dirty_io_schedulers), + S+DCPUS+DIOS + catch + _ : _ -> + S + end. + init_uniqint_info() -> SmallBits = erlang:system_info({wordsize, internal})*8-4, io:format("SmallBits=~p~n", [SmallBits]), - Schedulers = erlang:system_info(schedulers), + Schedulers = schedulers(), io:format("Schedulers=~p~n", [Schedulers]), MinSmall = -1*(1 bsl (SmallBits-1)), io:format("MinSmall=~p~n", [MinSmall]), @@ -337,7 +348,7 @@ check_uniqint(Int, UinqintInfo) -> true -> io:format("OK~n~n", []); false -> - io:format("result UniqInt=~p FAILED~n", [UniqInt]), + io:format("result Int=~p FAILED~n", [Int]), exit(badres) end. -- cgit v1.2.3 From bc1e16e334afc8255b221e3ffbb6f7c54fb91a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 1 Feb 2016 10:50:32 +0100 Subject: Avoid erts_cmp jump in atom, int and float comparisons Given the function definition below: check(X) when X >= 0, X <= 20 -> true. @nox has originally noticed that perfoming lt and ge guard tests were performing slower than they should be. Further investigation revealed that most of the cost was in jumping to the erts_cmp function. This patch brings the operations already inlined in erts_cmp into the emulator, removing the jump cost. After applying these changes, invoking the check/1 function defined above 30000 times with different values from 0 to 20 has fallen from 367us to 213us (measured as average of 3 runs). This is a considerably improvement over Erlang 18 which takes 556us on average. Floats have also dropped their time from 1126us (on Erlang 18) to 613us. --- erts/emulator/beam/atom.c | 2 +- erts/emulator/beam/beam_emu.c | 8 ++++---- erts/emulator/beam/erl_utils.h | 34 ++++++++++++++++++++++++---------- erts/emulator/beam/utils.c | 18 +++++++++--------- 4 files changed, 38 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c index fd2adac676..099c00bcf6 100644 --- a/erts/emulator/beam/atom.c +++ b/erts/emulator/beam/atom.c @@ -176,7 +176,7 @@ atom_alloc(Atom* tmpl) /* * Precompute ordinal value of first 3 bytes + 7 bits. - * This is used by utils.c:cmp_atoms(). + * This is used by utils.c:erts_cmp_atoms(). * We cannot use the full 32 bits of the first 4 bytes, * since we use the sign of the difference between two * ordinal values to represent their relative order. diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 4d7b00b032..659ff3ad2f 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -718,10 +718,10 @@ void** beam_ops; #define NotEqualImmed(X, Y, Action) if (X == Y) { Action; } #define EqualExact(X, Y, Action) if (!EQ(X,Y)) { Action; } #define NotEqualExact(X, Y, Action) if (EQ(X,Y)) { Action; } -#define Equal(X, Y, Action) if (!CMP_EQ(X,Y)) { Action; } -#define NotEqual(X, Y, Action) if (!CMP_NE(X,Y)) { Action; } -#define IsLessThan(X, Y, Action) if (CMP_GE(X, Y)) { Action; } -#define IsGreaterEqual(X, Y, Action) if (CMP_LT(X, Y)) { Action; } +#define Equal(X, Y, Action) CMP_EQ_ACTION(X,Y,Action) +#define NotEqual(X, Y, Action) CMP_NE_ACTION(X,Y,Action) +#define IsLessThan(X, Y, Action) CMP_LT_ACTION(X,Y,Action) +#define IsGreaterEqual(X, Y, Action) CMP_GE_ACTION(X,Y,Action) #define IsFloat(Src, Fail) if (is_not_float(Src)) { Fail; } diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 4058d63eaf..b8746f7edb 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -161,7 +161,9 @@ int eq(Eterm, Eterm); #define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y)))) +int erts_cmp_atoms(Eterm a, Eterm b); Sint erts_cmp(Eterm, Eterm, int, int); +Sint erts_cmp_compound(Eterm, Eterm, int, int); Sint cmp(Eterm a, Eterm b); #define CMP(A,B) erts_cmp(A,B,0,0) #define CMP_TERM(A,B) erts_cmp(A,B,1,0) @@ -174,17 +176,29 @@ Sint cmp(Eterm a, Eterm b); #define cmp_ge(a,b) (CMP((a),(b)) >= 0) #define cmp_gt(a,b) (CMP((a),(b)) > 0) -#define cmp_lt_term(a,b) (CMP_TERM((a),(b)) < 0) -#define cmp_le_term(a,b) (CMP_TERM((a),(b)) <= 0) -#define cmp_ge_term(a,b) (CMP_TERM((a),(b)) >= 0) -#define cmp_gt_term(a,b) (CMP_TERM((a),(b)) > 0) - -#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b))) -#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b))) #define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b))) -#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b))) -#define CMP_LT_TERM(a,b) ((a) != (b) && cmp_lt_term((a),(b))) -#define CMP_GE_TERM(a,b) ((a) == (b) || cmp_ge_term((a),(b))) +#define CMP_EQ_ACTION(X,Y,Action) \ + if ((X) != (Y)) { CMP_SPEC((X),(Y),!=,Action,1); } +#define CMP_NE_ACTION(X,Y,Action) \ + if ((X) == (Y)) { Action; } else { CMP_SPEC((X),(Y),==,Action,1); } +#define CMP_GE_ACTION(X,Y,Action) \ + if ((X) != (Y)) { CMP_SPEC((X),(Y),<,Action,0); } +#define CMP_LT_ACTION(X,Y,Action) \ + if ((X) == (Y)) { Action; } else { CMP_SPEC((X),(Y),>=,Action,0); } + +#define CMP_SPEC(X,Y,Op,Action,EqOnly) \ + if (is_atom(X) && is_atom(Y)) { \ + if (erts_cmp_atoms(X, Y) Op 0) { Action; }; \ + } else if (is_both_small(X, Y)) { \ + if (signed_val(X) Op signed_val(Y)) { Action; }; \ + } else if (is_float(X) && is_float(Y)) { \ + FloatDef af, bf; \ + GET_DOUBLE(X, af); \ + GET_DOUBLE(Y, bf); \ + if (af.fd Op bf.fd) { Action; }; \ + } else { \ + if (erts_cmp_compound(X,Y,0,EqOnly) Op 0) { Action; }; \ + } #endif diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index ef851d840d..6aad1ff778 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2991,7 +2991,7 @@ static int cmpbytes(byte *s1, int l1, byte *s2, int l2) #define float_comp(x,y) (((x)<(y)) ? -1 : (((x)==(y)) ? 0 : 1)) -static int cmp_atoms(Eterm a, Eterm b) +int erts_cmp_atoms(Eterm a, Eterm b) { Atom *aa = atom_tab(atom_val(a)); Atom *bb = atom_tab(atom_val(b)); @@ -3010,12 +3010,12 @@ Sint cmp(Eterm a, Eterm b) return erts_cmp(a, b, 0, 0); } -static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only); +Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only); Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) { if (is_atom(a) && is_atom(b)) { - return cmp_atoms(a, b); + return erts_cmp_atoms(a, b); } else if (is_both_small(a, b)) { return (signed_val(a) - signed_val(b)); } else if (is_float(a) && is_float(b)) { @@ -3032,7 +3032,7 @@ Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) * exact = 1 -> term-based compare * exact = 0 -> arith-based compare */ -static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only) +Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only) { #define PSTACK_TYPE struct erts_cmp_hashmap_state struct erts_cmp_hashmap_state { @@ -3089,7 +3089,7 @@ static Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only) do { \ if((AN) != (BN)) { \ if((AN)->sysname != (BN)->sysname) \ - RETURN_NEQ(cmp_atoms((AN)->sysname, (BN)->sysname)); \ + RETURN_NEQ(erts_cmp_atoms((AN)->sysname, (BN)->sysname)); \ ASSERT((AN)->creation != (BN)->creation); \ RETURN_NEQ(((AN)->creation < (BN)->creation) ? -1 : 1); \ } \ @@ -3107,7 +3107,7 @@ tailrecur_ne: /* deal with majority (?) cases by brute-force */ if (is_atom(a)) { if (is_atom(b)) { - ON_CMP_GOTO(cmp_atoms(a, b)); + ON_CMP_GOTO(erts_cmp_atoms(a, b)); } } else if (is_both_small(a, b)) { ON_CMP_GOTO(signed_val(a) - signed_val(b)); @@ -3341,10 +3341,10 @@ tailrecur_ne: Export* a_exp = *((Export **) (export_val(a) + 1)); Export* b_exp = *((Export **) (export_val(b) + 1)); - if ((j = cmp_atoms(a_exp->code[0], b_exp->code[0])) != 0) { + if ((j = erts_cmp_atoms(a_exp->code[0], b_exp->code[0])) != 0) { RETURN_NEQ(j); } - if ((j = cmp_atoms(a_exp->code[1], b_exp->code[1])) != 0) { + if ((j = erts_cmp_atoms(a_exp->code[1], b_exp->code[1])) != 0) { RETURN_NEQ(j); } ON_CMP_GOTO((Sint) a_exp->code[2] - (Sint) b_exp->code[2]); @@ -3659,7 +3659,7 @@ term_array: /* arrays in 'aa' and 'bb', length in 'i' */ b = *bb++; if (!is_same(a, b)) { if (is_atom(a) && is_atom(b)) { - if ((j = cmp_atoms(a, b)) != 0) { + if ((j = erts_cmp_atoms(a, b)) != 0) { goto not_equal; } } else if (is_both_small(a, b)) { -- cgit v1.2.3 From 345651148351c57b26347ee9a9b50e6e2732e79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 15 Feb 2016 11:00:26 +0100 Subject: Unify comparison macros in erl_utils.h This removes the duplication in having both `cmp_eq` and `CMP_EQ` and normalizes their name to uppercase. --- erts/emulator/beam/erl_bif_op.c | 12 ++++++------ erts/emulator/beam/erl_utils.h | 14 ++++++-------- 2 files changed, 12 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c index d53a9e11ca..0f20ded1d6 100644 --- a/erts/emulator/beam/erl_bif_op.c +++ b/erts/emulator/beam/erl_bif_op.c @@ -89,22 +89,22 @@ BIF_RETTYPE not_1(BIF_ALIST_1) BIF_RETTYPE sgt_2(BIF_ALIST_2) { - BIF_RET(cmp_gt(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); + BIF_RET(CMP_GT(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); } BIF_RETTYPE sge_2(BIF_ALIST_2) { - BIF_RET(cmp_ge(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); + BIF_RET(CMP_GE(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); } BIF_RETTYPE slt_2(BIF_ALIST_2) { - BIF_RET(cmp_lt(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); + BIF_RET(CMP_LT(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); } BIF_RETTYPE sle_2(BIF_ALIST_2) { - BIF_RET(cmp_le(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); + BIF_RET(CMP_LE(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); } BIF_RETTYPE seq_2(BIF_ALIST_2) @@ -114,7 +114,7 @@ BIF_RETTYPE seq_2(BIF_ALIST_2) BIF_RETTYPE seqeq_2(BIF_ALIST_2) { - BIF_RET(cmp_eq(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); + BIF_RET(CMP_EQ(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); } BIF_RETTYPE sneq_2(BIF_ALIST_2) @@ -124,7 +124,7 @@ BIF_RETTYPE sneq_2(BIF_ALIST_2) BIF_RETTYPE sneqeq_2(BIF_ALIST_2) { - BIF_RET(cmp_ne(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); + BIF_RET(CMP_NE(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); } BIF_RETTYPE is_atom_1(BIF_ALIST_1) diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index b8746f7edb..dd7b925bd3 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -169,14 +169,12 @@ Sint cmp(Eterm a, Eterm b); #define CMP_TERM(A,B) erts_cmp(A,B,1,0) #define CMP_EQ_ONLY(A,B) erts_cmp(A,B,0,1) -#define cmp_lt(a,b) (CMP((a),(b)) < 0) -#define cmp_le(a,b) (CMP((a),(b)) <= 0) -#define cmp_eq(a,b) (CMP_EQ_ONLY((a),(b)) == 0) -#define cmp_ne(a,b) (CMP_EQ_ONLY((a),(b)) != 0) -#define cmp_ge(a,b) (CMP((a),(b)) >= 0) -#define cmp_gt(a,b) (CMP((a),(b)) > 0) - -#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b))) +#define CMP_LT(a,b) ((a) != (b) && CMP((a),(b)) < 0) +#define CMP_LE(a,b) ((a) == (b) || CMP((a),(b)) <= 0) +#define CMP_EQ(a,b) ((a) == (b) || CMP_EQ_ONLY((a),(b)) == 0) +#define CMP_NE(a,b) ((a) != (b) && CMP_EQ_ONLY((a),(b)) != 0) +#define CMP_GE(a,b) ((a) == (b) || CMP((a),(b)) >= 0) +#define CMP_GT(a,b) ((a) != (b) && CMP((a),(b)) > 0) #define CMP_EQ_ACTION(X,Y,Action) \ if ((X) != (Y)) { CMP_SPEC((X),(Y),!=,Action,1); } -- cgit v1.2.3 From 4ea67ffdac2629255b1b0ed4e9423823f62c0947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 15 Feb 2016 18:12:48 +0100 Subject: erts: Add BIF erts_internal:system_check/1 This commit implements erts_internal:system_check(schedulers) with the intent of a basic responsiveness test check of the schedulers. --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/bif.tab | 2 + erts/emulator/beam/erl_alloc.types | 2 + erts/emulator/beam/erl_bif_info.c | 15 ++++++ erts/emulator/beam/erl_process.c | 97 ++++++++++++++++++++++++++++++++++++ erts/emulator/beam/erl_process.h | 1 + erts/preloaded/src/erts_internal.erl | 20 ++++++++ 7 files changed, 138 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 07f6492948..cb6d294a41 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -269,6 +269,7 @@ atom getenv atom gather_gc_info_result atom gather_io_bytes atom gather_sched_wall_time_result +atom gather_system_check_result atom getting_linked atom getting_unlinked atom global diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 4f0656d174..3177d5dae7 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -174,6 +174,8 @@ bif erts_internal:time_unit/0 bif erts_internal:is_system_process/1 +bif erts_internal:system_check/1 + # inet_db support bif erlang:port_set_data/2 bif erlang:port_get_data/1 diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 1ecebdeb07..7738531142 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -369,6 +369,7 @@ type AINFO_REQ STANDARD_LOW SYSTEM alloc_info_request type SCHED_WTIME_REQ STANDARD_LOW SYSTEM sched_wall_time_request type GC_INFO_REQ STANDARD_LOW SYSTEM gc_info_request type PORT_DATA_HEAP STANDARD_LOW SYSTEM port_data_heap +type SYS_CHECK_REQ STANDARD_LOW SYSTEM system_check_request +else # "fullword" @@ -389,6 +390,7 @@ type AINFO_REQ SHORT_LIVED SYSTEM alloc_info_request type SCHED_WTIME_REQ SHORT_LIVED SYSTEM sched_wall_time_request type GC_INFO_REQ SHORT_LIVED SYSTEM gc_info_request type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap +type SYS_CHECK_REQ SHORT_LIVED SYSTEM system_check_request +endif diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 414ff6711a..8bf6877bea 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -65,6 +65,7 @@ static Export* gather_io_bytes_trap = NULL; static Export *gather_sched_wall_time_res_trap; static Export *gather_gc_info_res_trap; +static Export *gather_system_check_res_trap; #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) @@ -3784,6 +3785,18 @@ BIF_RETTYPE erts_internal_is_system_process_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); } +BIF_RETTYPE erts_internal_system_check_1(BIF_ALIST_1) +{ + Eterm res; + if (ERTS_IS_ATOM_STR("schedulers", BIF_ARG_1)) { + res = erts_system_check_request(BIF_P); + if (is_non_value(res)) + BIF_RET(am_undefined); + BIF_TRAP1(gather_system_check_res_trap, BIF_P, res); + } + + BIF_ERROR(BIF_P, BADARG); +} static erts_smp_atomic_t hipe_test_reschedule_flag; @@ -4391,6 +4404,8 @@ erts_bif_info_init(void) = erts_export_put(am_erlang, am_gather_gc_info_result, 1); gather_io_bytes_trap = erts_export_put(am_erts_internal, am_gather_io_bytes, 2); + gather_system_check_res_trap + = erts_export_put(am_erts_internal, am_gather_system_check_result, 1); process_info_init(); os_info_init(); } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b24f26138c..8e4a42fbe4 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -967,11 +967,25 @@ typedef struct { erts_smp_atomic32_t refc; } ErtsSchedWallTimeReq; +typedef struct { + Process *proc; + Eterm ref; + Eterm ref_heap[REF_THING_SIZE]; + Uint req_sched; + erts_smp_atomic32_t refc; +} ErtsSystemCheckReq; + + #if !HALFWORD_HEAP ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(swtreq, ErtsSchedWallTimeReq, 5, ERTS_ALC_T_SCHED_WTIME_REQ) + +ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(screq, + ErtsSystemCheckReq, + 5, + ERTS_ALC_T_SYS_CHECK_REQ) #else static ERTS_INLINE ErtsSchedWallTimeReq * swtreq_alloc(void) @@ -985,6 +999,19 @@ swtreq_free(ErtsSchedWallTimeReq *ptr) { erts_free(ERTS_ALC_T_SCHED_WTIME_REQ, ptr); } + +static ERTS_INLINE ErtsSystemCheckReq * +screq_alloc(void) +{ + return erts_alloc(ERTS_ALC_T_SYS_CHECK_REQ, + sizeof(ErtsSystemCheckReq)); +} + +static ERTS_INLINE void +screq_free(ErtsSystemCheckReq *ptr) +{ + erts_free(ERTS_ALC_T_SYS_CHECK_REQ, ptr); +} #endif static void @@ -1118,6 +1145,75 @@ erts_sched_wall_time_request(Process *c_p, int set, int enable) return ref; } +static void +reply_system_check(void *vscrp) +{ + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + ErtsSystemCheckReq *scrp = (ErtsSystemCheckReq *) vscrp; + ErtsProcLocks rp_locks = (scrp->req_sched == esdp->no ? ERTS_PROC_LOCK_MAIN : 0); + Process *rp = scrp->proc; + Eterm msg; + Eterm *hp = NULL; + Eterm **hpp; + Uint sz; + ErlOffHeap *ohp = NULL; + ErlHeapFragment *bp = NULL; + + ASSERT(esdp); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); +#endif + + sz = REF_THING_SIZE; + hp = erts_alloc_message_heap(sz, &bp, &ohp, rp, &rp_locks); + hpp = &hp; + msg = STORE_NC(hpp, ohp, scrp->ref); + + erts_queue_message(rp, &rp_locks, bp, msg, NIL); + + if (scrp->req_sched == esdp->no) + rp_locks &= ~ERTS_PROC_LOCK_MAIN; + + if (rp_locks) + erts_smp_proc_unlock(rp, rp_locks); + + erts_proc_dec_refc(rp); + + if (erts_smp_atomic32_dec_read_nob(&scrp->refc) == 0) + screq_free(vscrp); +} + + +Eterm erts_system_check_request(Process *c_p) { + ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p); + Eterm ref; + ErtsSystemCheckReq *scrp; + Eterm *hp; + + scrp = screq_alloc(); + ref = erts_make_ref(c_p); + hp = &scrp->ref_heap[0]; + + scrp->proc = c_p; + scrp->ref = STORE_NC(&hp, NULL, ref); + scrp->req_sched = esdp->no; + erts_smp_atomic32_init_nob(&scrp->refc, (erts_aint32_t) erts_no_schedulers); + + erts_proc_add_refc(c_p, (Sint) erts_no_schedulers); + +#ifdef ERTS_SMP + if (erts_no_schedulers > 1) + erts_schedule_multi_misc_aux_work(1, + erts_no_schedulers, + reply_system_check, + (void *) scrp); +#endif + + reply_system_check((void *) scrp); + + return ref; +} + static ERTS_INLINE ErtsProcList * proclist_create(Process *p) { @@ -5792,6 +5888,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online init_misc_aux_work(); #if !HALFWORD_HEAP init_swtreq_alloc(); + init_screq_alloc(); #endif erts_atomic32_init_nob(&debug_wait_completed_count, 0); /* debug only */ diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 799e49005c..f1570a835c 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1483,6 +1483,7 @@ void erts_init_scheduling(int, int int erts_set_gc_state(Process *c_p, int enable); Eterm erts_sched_wall_time_request(Process *c_p, int set, int enable); +Eterm erts_system_check_request(Process *c_p); Eterm erts_gc_info_request(Process *c_p); Uint64 erts_get_proc_interval(void); Uint64 erts_ensure_later_proc_interval(Uint64); diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 7ed4efea4b..6db77a8482 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -35,6 +35,9 @@ -export([port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). +-export([system_check/1, + gather_system_check_result/1]). + -export([request_system_task/3]). -export([check_process_code/2]). @@ -197,6 +200,23 @@ request_system_task(_Pid, _Prio, _Request) -> check_process_code(_Module, _OptionList) -> erlang:nif_error(undefined). +-spec system_check(Type) -> 'ok' when + Type :: 'schedulers'. + +system_check(_Type) -> + erlang:nif_error(undefined). + +gather_system_check_result(Ref) when is_reference(Ref) -> + gather_system_check_result(Ref, erlang:system_info(schedulers)). + +gather_system_check_result(_Ref, 0) -> + ok; +gather_system_check_result(Ref, N) -> + receive + Ref -> + gather_system_check_result(Ref, N - 1) + end. + %% term compare where integer() < float() = true -spec cmp_term(A,B) -> Result when -- cgit v1.2.3 From a50a75e4ff842b600ba526a37e64ac8006fa8249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Feb 2016 10:43:51 +0100 Subject: heart: Remove dead code --- erts/etc/common/heart.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index 9571b83ffd..1a826221fb 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -472,10 +472,6 @@ message_loop(erlin_fd, erlout_fd) switch (mp->op) { case HEART_BEAT: timestamp(&last_received); -#ifdef USE_WATCHDOG - /* reset the hardware watchdog timer */ - wd_reset(); -#endif break; case SHUT_DOWN: return R_SHUT_DOWN; -- cgit v1.2.3 From b727eb1d46cc4238e4fe7e6070f41dbfe2d21691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 16 Feb 2016 10:09:04 +0100 Subject: Update preloaded erts_internal.beam --- erts/preloaded/ebin/erts_internal.beam | Bin 5960 -> 6516 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 32d5d70122..21dde7f257 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ -- cgit v1.2.3 From 03fcb7dabf8861e60ffab4121a909b347bccfec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 15 Feb 2016 15:33:12 +0100 Subject: Eliminate use of test_server.hrl and test_server_line.hrl As a first step to removing the test_server application as as its own separate application, change the inclusion of test_server.hrl to an inclusion of ct.hrl and remove the inclusion of test_server_line.hrl. --- erts/emulator/test/a_SUITE.erl | 2 +- erts/emulator/test/after_SUITE.erl | 2 +- erts/emulator/test/alloc_SUITE.erl | 2 +- erts/emulator/test/beam_SUITE.erl | 2 +- erts/emulator/test/beam_literals_SUITE.erl | 2 +- erts/emulator/test/bif_SUITE.erl | 2 +- erts/emulator/test/big_SUITE.erl | 2 +- erts/emulator/test/binary_SUITE.erl | 2 +- erts/emulator/test/bs_bit_binaries_SUITE.erl | 2 +- erts/emulator/test/bs_construct_SUITE.erl | 2 +- erts/emulator/test/bs_match_bin_SUITE.erl | 2 +- erts/emulator/test/bs_match_int_SUITE.erl | 2 +- erts/emulator/test/bs_match_misc_SUITE.erl | 2 +- erts/emulator/test/bs_match_tail_SUITE.erl | 2 +- erts/emulator/test/bs_utf_SUITE.erl | 2 +- erts/emulator/test/busy_port_SUITE.erl | 2 +- erts/emulator/test/call_trace_SUITE.erl | 2 +- erts/emulator/test/code_SUITE.erl | 2 +- erts/emulator/test/code_parallel_load_SUITE.erl | 2 +- erts/emulator/test/crypto_SUITE.erl | 2 +- erts/emulator/test/ddll_SUITE.erl | 2 +- erts/emulator/test/decode_packet_SUITE.erl | 2 +- erts/emulator/test/distribution_SUITE.erl | 2 +- erts/emulator/test/driver_SUITE.erl | 2 +- erts/emulator/test/efile_SUITE.erl | 2 +- erts/emulator/test/erl_drv_thread_SUITE.erl | 2 +- erts/emulator/test/erl_link_SUITE.erl | 2 +- erts/emulator/test/erts_debug_SUITE.erl | 2 +- erts/emulator/test/estone_SUITE.erl | 2 +- erts/emulator/test/evil_SUITE.erl | 2 +- erts/emulator/test/exception_SUITE.erl | 2 +- erts/emulator/test/float_SUITE.erl | 2 +- erts/emulator/test/fun_SUITE.erl | 2 +- erts/emulator/test/fun_r13_SUITE.erl | 2 +- erts/emulator/test/gc_SUITE.erl | 2 +- erts/emulator/test/guard_SUITE.erl | 2 +- erts/emulator/test/hash_SUITE.erl | 2 +- erts/emulator/test/hibernate_SUITE.erl | 2 +- erts/emulator/test/ignore_cores.erl | 2 +- erts/emulator/test/list_bif_SUITE.erl | 2 +- erts/emulator/test/match_spec_SUITE.erl | 2 +- erts/emulator/test/message_queue_data_SUITE.erl | 2 +- erts/emulator/test/module_info_SUITE.erl | 2 +- erts/emulator/test/monitor_SUITE.erl | 2 +- erts/emulator/test/nested_SUITE.erl | 2 +- erts/emulator/test/nif_SUITE.erl | 2 +- erts/emulator/test/nif_SUITE_data/nif_mod.erl | 2 +- erts/emulator/test/nif_SUITE_data/tester.erl | 2 +- erts/emulator/test/node_container_SUITE.erl | 2 +- erts/emulator/test/nofrag_SUITE.erl | 2 +- erts/emulator/test/num_bif_SUITE.erl | 2 +- erts/emulator/test/old_mod.erl | 2 +- erts/emulator/test/old_scheduler_SUITE.erl | 2 +- erts/emulator/test/op_SUITE.erl | 2 +- erts/emulator/test/port_SUITE.erl | 2 +- erts/emulator/test/port_bif_SUITE.erl | 2 +- erts/emulator/test/process_SUITE.erl | 2 +- erts/emulator/test/receive_SUITE.erl | 2 +- erts/emulator/test/ref_SUITE.erl | 2 +- erts/emulator/test/register_SUITE.erl | 2 +- erts/emulator/test/save_calls_SUITE.erl | 2 +- erts/emulator/test/scheduler_SUITE.erl | 2 +- erts/emulator/test/send_term_SUITE.erl | 2 +- erts/emulator/test/sensitive_SUITE.erl | 2 +- erts/emulator/test/signal_SUITE.erl | 2 +- erts/emulator/test/smoke_test_SUITE.erl | 2 +- erts/emulator/test/statistics_SUITE.erl | 2 +- erts/emulator/test/system_info_SUITE.erl | 2 +- erts/emulator/test/system_profile_SUITE.erl | 2 +- erts/emulator/test/time_SUITE.erl | 2 +- erts/emulator/test/timer_bif_SUITE.erl | 2 +- erts/emulator/test/trace_SUITE.erl | 2 +- erts/emulator/test/trace_bif_SUITE.erl | 2 +- erts/emulator/test/trace_call_count_SUITE.erl | 2 +- erts/emulator/test/trace_call_time_SUITE.erl | 2 +- erts/emulator/test/trace_local_SUITE.erl | 2 +- erts/emulator/test/trace_meta_SUITE.erl | 2 +- erts/emulator/test/trace_nif_SUITE.erl | 2 +- erts/emulator/test/trace_port_SUITE.erl | 2 +- erts/emulator/test/tuple_SUITE.erl | 2 +- erts/emulator/test/unique_SUITE.erl | 2 +- erts/emulator/test/z_SUITE.erl | 2 +- erts/epmd/test/epmd_SUITE.erl | 2 +- erts/test/erl_print_SUITE.erl | 2 +- erts/test/erlc_SUITE.erl | 2 +- erts/test/erlexec_SUITE.erl | 2 +- erts/test/ethread_SUITE.erl | 2 +- erts/test/ignore_cores.erl | 2 +- erts/test/install_SUITE.erl | 2 +- erts/test/nt_SUITE.erl | 2 +- erts/test/otp_SUITE.erl | 2 +- erts/test/run_erl_SUITE.erl | 2 +- 92 files changed, 92 insertions(+), 92 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl index 16f060fe34..a9bba9548b 100644 --- a/erts/emulator/test/a_SUITE.erl +++ b/erts/emulator/test/a_SUITE.erl @@ -27,7 +27,7 @@ %%%------------------------------------------------------------------- -module(a_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, long_timers/1, pollset_size/1]). diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl index 5017a83185..879fb03927 100644 --- a/erts/emulator/test/after_SUITE.erl +++ b/erts/emulator/test/after_SUITE.erl @@ -22,7 +22,7 @@ %% Tests receive after. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 516bc873a5..332e2ad67d 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -36,7 +36,7 @@ -export([init_per_testcase/2, end_per_testcase/2]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(DEFAULT_TIMETRAP_SECS, 240). diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl index 706a4a1c16..499b05d658 100644 --- a/erts/emulator/test/beam_SUITE.erl +++ b/erts/emulator/test/beam_SUITE.erl @@ -28,7 +28,7 @@ -export([applied/2]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/beam_literals_SUITE.erl b/erts/emulator/test/beam_literals_SUITE.erl index 9f14ca26e5..29c6a5c7ac 100644 --- a/erts/emulator/test/beam_literals_SUITE.erl +++ b/erts/emulator/test/beam_literals_SUITE.erl @@ -28,7 +28,7 @@ put_list/1, fconv/1, literal_case_expression/1, increment/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index ebc4db53c4..9bba5387fa 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -20,7 +20,7 @@ -module(bif_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl index e8f881f2a4..bb77235253 100644 --- a/erts/emulator/test/big_SUITE.erl +++ b/erts/emulator/test/big_SUITE.erl @@ -34,7 +34,7 @@ -export([init_per_testcase/2, end_per_testcase/2]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index f8f71efecc..c5e3226a13 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -40,7 +40,7 @@ %% phash2(Binary, N) %% --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/bs_bit_binaries_SUITE.erl b/erts/emulator/test/bs_bit_binaries_SUITE.erl index a07fd7609c..0896fad8ed 100644 --- a/erts/emulator/test/bs_bit_binaries_SUITE.erl +++ b/erts/emulator/test/bs_bit_binaries_SUITE.erl @@ -30,7 +30,7 @@ big_binary_to_and_from_list/1,send_and_receive/1, send_and_receive_alot/1,append/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 7ed99f5b4e..1fa7353252 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -31,7 +31,7 @@ copy_writable_binary/1, kostis/1, dynamic/1, bs_add/1, otp_7422/1, zero_width/1, bad_append/1, bs_add_overflow/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/bs_match_bin_SUITE.erl b/erts/emulator/test/bs_match_bin_SUITE.erl index ba79643e69..2185e43498 100644 --- a/erts/emulator/test/bs_match_bin_SUITE.erl +++ b/erts/emulator/test/bs_match_bin_SUITE.erl @@ -24,7 +24,7 @@ init_per_group/2,end_per_group/2, byte_split_binary/1,bit_split_binary/1,match_huge_bin/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/bs_match_int_SUITE.erl b/erts/emulator/test/bs_match_int_SUITE.erl index 368f71978d..48c2b4644e 100644 --- a/erts/emulator/test/bs_match_int_SUITE.erl +++ b/erts/emulator/test/bs_match_int_SUITE.erl @@ -24,7 +24,7 @@ integer/1,signed_integer/1,dynamic/1,more_dynamic/1,mml/1, match_huge_int/1,bignum/1,unaligned_32_bit/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -import(lists, [seq/2]). diff --git a/erts/emulator/test/bs_match_misc_SUITE.erl b/erts/emulator/test/bs_match_misc_SUITE.erl index e875dc859c..b161d9544e 100644 --- a/erts/emulator/test/bs_match_misc_SUITE.erl +++ b/erts/emulator/test/bs_match_misc_SUITE.erl @@ -27,7 +27,7 @@ writable_binary_matched/1,otp_7198/1,unordered_bindings/1, float_middle_endian/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/bs_match_tail_SUITE.erl b/erts/emulator/test/bs_match_tail_SUITE.erl index 58b0d3fef6..baa86e6d4a 100644 --- a/erts/emulator/test/bs_match_tail_SUITE.erl +++ b/erts/emulator/test/bs_match_tail_SUITE.erl @@ -24,7 +24,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2,aligned/1,unaligned/1,zero_tail/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/bs_utf_SUITE.erl b/erts/emulator/test/bs_utf_SUITE.erl index 0625c22163..91e4e30dd2 100644 --- a/erts/emulator/test/bs_utf_SUITE.erl +++ b/erts/emulator/test/bs_utf_SUITE.erl @@ -28,7 +28,7 @@ utf32_illegal_sequences/1, bad_construction/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(FAIL(Expr), ?line fail_check(catch Expr, ??Expr, [])). diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index 6a2588aadd..0e81141907 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -29,7 +29,7 @@ -compile(export_all). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %% Internal exports. -export([init/2]). diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index 064404a038..b9f8fe52f3 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -39,7 +39,7 @@ -export([abbr/1,abbr/2]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(P, 20). diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 1acc4538fb..3d2cc000a8 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -30,7 +30,7 @@ t_copy_literals/1]). -define(line_trace, 1). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl index b7ac0420cd..3998d27d04 100644 --- a/erts/emulator/test/code_parallel_load_SUITE.erl +++ b/erts/emulator/test/code_parallel_load_SUITE.erl @@ -39,7 +39,7 @@ -define(passes, 4). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/crypto_SUITE.erl b/erts/emulator/test/crypto_SUITE.erl index 3622592586..41fe6a226c 100644 --- a/erts/emulator/test/crypto_SUITE.erl +++ b/erts/emulator/test/crypto_SUITE.erl @@ -20,7 +20,7 @@ -module(crypto_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl index cabd6472d4..7ff727bcf5 100644 --- a/erts/emulator/test/ddll_SUITE.erl +++ b/erts/emulator/test/ddll_SUITE.erl @@ -52,7 +52,7 @@ -import(ordsets, [subtract/2]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl index 65ae94d0dc..58a8d390f0 100644 --- a/erts/emulator/test/decode_packet_SUITE.erl +++ b/erts/emulator/test/decode_packet_SUITE.erl @@ -22,7 +22,7 @@ -module(decode_packet_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index d71cedbdc5..7a33dbbb36 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -33,7 +33,7 @@ %% Tests distribution and the tcp driver. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index ce55fe3c52..8eb555a5b7 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -86,7 +86,7 @@ -export([bin_prefix/2]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). % First byte in communication with the timer driver diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index 4d8d89db9b..cb26e8e736 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -24,7 +24,7 @@ -export([do_iter_max_files/2, do_async_dist/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl index 2cd569ce4f..26d00db7c0 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE.erl +++ b/erts/emulator/test/erl_drv_thread_SUITE.erl @@ -25,7 +25,7 @@ -export([basic/1, rwlock/1, tsd/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(DEFAULT_TIMETRAP_SECS, 240). diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index a7a45046ca..56b2c9c6ee 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -29,7 +29,7 @@ -author('rickard.green@uab.ericsson.se'). %-define(line_trace, 1). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl index bbba829501..440a7950a6 100644 --- a/erts/emulator/test/erts_debug_SUITE.erl +++ b/erts/emulator/test/erts_debug_SUITE.erl @@ -19,7 +19,7 @@ %% -module(erts_debug_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index dc8f0aaee9..7be55eca8d 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -46,7 +46,7 @@ run_micro/3,p1/1,ppp/3,macro/2,micros/0]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct_event.hrl"). %% Test suite defines diff --git a/erts/emulator/test/evil_SUITE.erl b/erts/emulator/test/evil_SUITE.erl index d28e4d9596..77ee2128b6 100644 --- a/erts/emulator/test/evil_SUITE.erl +++ b/erts/emulator/test/evil_SUITE.erl @@ -33,7 +33,7 @@ decode_pos_neg_zero/1 ]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index 11caea3698..57ce8fb879 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -29,7 +29,7 @@ -export([bad_guy/2]). -export([crash/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -import(lists, [foreach/2]). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index 8826026f88..bf557f2bca 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -20,7 +20,7 @@ -module(float_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index b18f9f5c6b..6697a86fc5 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -35,7 +35,7 @@ -export([nothing/0]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/fun_r13_SUITE.erl b/erts/emulator/test/fun_r13_SUITE.erl index 7ab5e65cb3..39e8b3c324 100644 --- a/erts/emulator/test/fun_r13_SUITE.erl +++ b/erts/emulator/test/fun_r13_SUITE.erl @@ -26,7 +26,7 @@ init_per_testcase/2,end_per_testcase/2,dist_old_release/1]). -define(default_timeout, ?t:minutes(1)). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl index 1e155e7b09..cb000fd45f 100644 --- a/erts/emulator/test/gc_SUITE.erl +++ b/erts/emulator/test/gc_SUITE.erl @@ -22,7 +22,7 @@ -module(gc_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). diff --git a/erts/emulator/test/guard_SUITE.erl b/erts/emulator/test/guard_SUITE.erl index b3a85c6423..2e03983c4f 100644 --- a/erts/emulator/test/guard_SUITE.erl +++ b/erts/emulator/test/guard_SUITE.erl @@ -25,7 +25,7 @@ test_heap_guards/1, guard_bifs/1, type_tests/1,guard_bif_binary_part/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([init/3]). -import(lists, [member/2]). diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl index 1b2acf48e1..31b10158fb 100644 --- a/erts/emulator/test/hash_SUITE.erl +++ b/erts/emulator/test/hash_SUITE.erl @@ -50,7 +50,7 @@ -define(config(A,B),config(A,B)). -export([config/2]). -else. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -endif. -ifdef(debug). diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl index 4ac8c272db..5138a6ee05 100644 --- a/erts/emulator/test/hibernate_SUITE.erl +++ b/erts/emulator/test/hibernate_SUITE.erl @@ -20,7 +20,7 @@ -module(hibernate_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/ignore_cores.erl b/erts/emulator/test/ignore_cores.erl index 13f34cd10f..e40b91392c 100644 --- a/erts/emulator/test/ignore_cores.erl +++ b/erts/emulator/test/ignore_cores.erl @@ -28,7 +28,7 @@ -module(ignore_cores). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([init/1, fini/1, setup/3, setup/4, restore/1, dir/1]). diff --git a/erts/emulator/test/list_bif_SUITE.erl b/erts/emulator/test/list_bif_SUITE.erl index 9e930822cf..544f9c9c1f 100644 --- a/erts/emulator/test/list_bif_SUITE.erl +++ b/erts/emulator/test/list_bif_SUITE.erl @@ -19,7 +19,7 @@ %% -module(list_bif_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 3f986ca2ed..9e724bdd8c 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -39,7 +39,7 @@ % This test suite assumes that tracing in general works. What we test is % the match spec functionality. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([init_per_testcase/2, end_per_testcase/2]). diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl index 11481409aa..96c41a57b5 100644 --- a/erts/emulator/test/message_queue_data_SUITE.erl +++ b/erts/emulator/test/message_queue_data_SUITE.erl @@ -27,7 +27,7 @@ -export([basic_test/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). init_per_testcase(Case, Config) -> ?line Dog=test_server:timetrap(test_server:minutes(2)), diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index 7c2101ca05..c622a2d8d7 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -20,7 +20,7 @@ -module(module_info_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 4db17969c0..8101908df1 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -20,7 +20,7 @@ -module(monitor_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/nested_SUITE.erl b/erts/emulator/test/nested_SUITE.erl index 7cfa837ee5..ee6bbf6a55 100644 --- a/erts/emulator/test/nested_SUITE.erl +++ b/erts/emulator/test/nested_SUITE.erl @@ -24,7 +24,7 @@ init_per_group/2,end_per_group/2, case_in_case/1, case_in_after/1, catch_in_catch/1, bif_in_bif/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index b02a090103..eecce3a6a8 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -24,7 +24,7 @@ -define(CHECK(Exp,Got), check(Exp,Got,?LINE)). %%-define(CHECK(Exp,Got), ?line Exp = Got). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl index e65d4577c7..aa3c90fe9d 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl @@ -20,7 +20,7 @@ -module(nif_mod). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([load_nif_lib/2, load_nif_lib/3, start/0, lib_version/0, call_history/0, get_priv_data_ptr/0, make_new_resource/2, get_resource/2]). diff --git a/erts/emulator/test/nif_SUITE_data/tester.erl b/erts/emulator/test/nif_SUITE_data/tester.erl index b393e29b82..32b9ef1826 100644 --- a/erts/emulator/test/nif_SUITE_data/tester.erl +++ b/erts/emulator/test/nif_SUITE_data/tester.erl @@ -1,6 +1,6 @@ -module(tester). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([load_nif_lib/2, run/0]). diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index fecaad5232..dcd0428e65 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -30,7 +30,7 @@ %-define(line_trace, 1). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %-compile(export_all). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, diff --git a/erts/emulator/test/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl index 3660a58c56..caa2d30a6c 100644 --- a/erts/emulator/test/nofrag_SUITE.erl +++ b/erts/emulator/test/nofrag_SUITE.erl @@ -20,7 +20,7 @@ -module(nofrag_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index d0840fe731..04a6f9d18d 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -20,7 +20,7 @@ -module(num_bif_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %% Tests the BIFs: %% abs/1 diff --git a/erts/emulator/test/old_mod.erl b/erts/emulator/test/old_mod.erl index 1586a024d8..e714a75954 100644 --- a/erts/emulator/test/old_mod.erl +++ b/erts/emulator/test/old_mod.erl @@ -23,7 +23,7 @@ -export([sort_on_old_node/1, sorter/3]). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). sorter(Receiver, Ref, List) -> Receiver ! {Ref, lists:sort(List)}. diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl index 97c99fe07b..272131cb46 100644 --- a/erts/emulator/test/old_scheduler_SUITE.erl +++ b/erts/emulator/test/old_scheduler_SUITE.erl @@ -20,7 +20,7 @@ -module(old_scheduler_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl index 65a5a4c505..4e15b27231 100644 --- a/erts/emulator/test/op_SUITE.erl +++ b/erts/emulator/test/op_SUITE.erl @@ -20,7 +20,7 @@ -module(op_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index ff75ee86d6..5274da301a 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -102,7 +102,7 @@ -export([otp_3906_forker/5, otp_3906_start_forker_starter/4]). -export([env_slave_main/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index 981899b167..9215d7f720 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -32,7 +32,7 @@ -export([init_per_testcase/2, end_per_testcase/2]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index bfd1e78bf0..862fe78b85 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -26,7 +26,7 @@ %% process_info/1,2 %% register/2 (partially) --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(heap_binary_size, 64). diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl index ccae0df72e..635c3d27c5 100644 --- a/erts/emulator/test/receive_SUITE.erl +++ b/erts/emulator/test/receive_SUITE.erl @@ -22,7 +22,7 @@ %% Tests receive after. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/ref_SUITE.erl b/erts/emulator/test/ref_SUITE.erl index 1042c23d65..6d4a998094 100644 --- a/erts/emulator/test/ref_SUITE.erl +++ b/erts/emulator/test/ref_SUITE.erl @@ -27,7 +27,7 @@ -export([loop_ref/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). init_per_testcase(_, Config) -> ?line Dog=test_server:timetrap(test_server:minutes(2)), diff --git a/erts/emulator/test/register_SUITE.erl b/erts/emulator/test/register_SUITE.erl index 5ecca0f547..53bf02e085 100644 --- a/erts/emulator/test/register_SUITE.erl +++ b/erts/emulator/test/register_SUITE.erl @@ -23,7 +23,7 @@ %-define(line_trace, 1). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %-compile(export_all). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl index 810bc07eed..b40a5f0a56 100644 --- a/erts/emulator/test/save_calls_SUITE.erl +++ b/erts/emulator/test/save_calls_SUITE.erl @@ -20,7 +20,7 @@ -module(save_calls_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0, init_per_suite/1, end_per_suite/1, diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 986a73ebb1..b56b6f6dbf 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -31,7 +31,7 @@ %-define(line_trace, 1). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %-compile(export_all). -export([all/0, suite/0,groups/0,init_per_suite/1, diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index 670865cd3f..63c11519b8 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -26,7 +26,7 @@ -export([generate_external_terms_files/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> Dog=?t:timetrap(?t:minutes(3)), diff --git a/erts/emulator/test/sensitive_SUITE.erl b/erts/emulator/test/sensitive_SUITE.erl index 2e51712737..29517cdcb8 100644 --- a/erts/emulator/test/sensitive_SUITE.erl +++ b/erts/emulator/test/sensitive_SUITE.erl @@ -20,7 +20,7 @@ -module(sensitive_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index 4aa690fb0f..e176fe52d6 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -31,7 +31,7 @@ -define(DEFAULT_TIMEOUT_SECONDS, 120). %-define(line_trace, 1). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 5bb98e5ad9..2df19cd20b 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -20,7 +20,7 @@ -module(smoke_test_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %-compile(export_all). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index aab3d3e3d2..df96b018a1 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -38,7 +38,7 @@ -export([hog/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). init_per_testcase(_, Config) -> ?line Dog = test_server:timetrap(test_server:seconds(300)), diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index 51122e5d55..ae18b280cf 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -31,7 +31,7 @@ %-define(line_trace, 1). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %-compile(export_all). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl index 6e85f8c54e..c63a119f9d 100644 --- a/erts/emulator/test/system_profile_SUITE.erl +++ b/erts/emulator/test/system_profile_SUITE.erl @@ -37,7 +37,7 @@ -export([profiler_process/1, ring_loop/1, port_echo_start/0, list_load/0, run_load/2]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(default_timeout, ?t:minutes(1)). diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index 9bf3b90d91..f6f99a0c81 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -48,7 +48,7 @@ -export([local_to_univ_utc/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([linear_time/1]). diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index 51d59f09f3..f4615d6810 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -33,7 +33,7 @@ % same_time_yielding_with_cancel_other_accessor/1, auto_cancel_yielding/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(SHORT_TIMEOUT, 5000). %% Bif timers as short as this may be pre-allocated -define(TIMEOUT_YIELD_LIMIT, 100). diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 00b90e3c3d..02c2c7a93a 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -38,7 +38,7 @@ system_monitor_long_schedule/1, bad_flag/1, trace_delivered/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %%% Internal exports -export([process/1]). diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index 96b7dd159f..ae98cc7189 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -20,7 +20,7 @@ -module(trace_bif_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). diff --git a/erts/emulator/test/trace_call_count_SUITE.erl b/erts/emulator/test/trace_call_count_SUITE.erl index c7881bbd70..e358791f1f 100644 --- a/erts/emulator/test/trace_call_count_SUITE.erl +++ b/erts/emulator/test/trace_call_count_SUITE.erl @@ -43,7 +43,7 @@ -define(config(A,B),config(A,B)). -export([config/2]). -else. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -endif. -ifdef(debug). diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index f359e1bd80..a802aa12b8 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -58,7 +58,7 @@ %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %% When run in test server. -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 7431099340..503a773545 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -46,7 +46,7 @@ -export([config/2]). -define(DEFAULT_RECEIVE_TIMEOUT, 1000). -else. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(DEFAULT_RECEIVE_TIMEOUT, infinity). -endif. diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl index 3b105ec6fe..8f732f0a5f 100644 --- a/erts/emulator/test/trace_meta_SUITE.erl +++ b/erts/emulator/test/trace_meta_SUITE.erl @@ -46,7 +46,7 @@ -define(config(A,B),config(A,B)). -export([config/2]). -else. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -endif. -ifdef(debug). diff --git a/erts/emulator/test/trace_nif_SUITE.erl b/erts/emulator/test/trace_nif_SUITE.erl index 1cd50350e3..3ac891b1dd 100644 --- a/erts/emulator/test/trace_nif_SUITE.erl +++ b/erts/emulator/test/trace_nif_SUITE.erl @@ -20,7 +20,7 @@ -module(trace_nif_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index a77710205e..b169a264be 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -38,7 +38,7 @@ default_tracer/1, tracer_port_crash/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). test_cases() -> [call_trace, return_trace, send, receive_trace, diff --git a/erts/emulator/test/tuple_SUITE.erl b/erts/emulator/test/tuple_SUITE.erl index f1f077be6b..2c2f93e7ee 100644 --- a/erts/emulator/test/tuple_SUITE.erl +++ b/erts/emulator/test/tuple_SUITE.erl @@ -26,7 +26,7 @@ t_make_tuple_2/1, t_make_upper_boundry_tuple_2/1, t_make_tuple_3/1, t_append_element/1, t_append_element_upper_boundry/1, build_and_match/1, tuple_with_case/1, tuple_in_guard/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %% Tests tuples and the BIFs: %% diff --git a/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl index 6fa634b886..7b8d1492b9 100644 --- a/erts/emulator/test/unique_SUITE.erl +++ b/erts/emulator/test/unique_SUITE.erl @@ -26,7 +26,7 @@ -export([unique_monotonic_integer_white_box/1, unique_integer_white_box/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %-define(P(V), V). -define(P(V), print_ret_val(?FILE, ?LINE, V)). diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index abc353fb01..0037a9a477 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -30,7 +30,7 @@ %-define(line_trace, 1). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %-compile(export_all). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 4de65500e9..0c8f01ef6b 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -18,7 +18,7 @@ %% %CopyrightEnd% %% -module(epmd_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl index 3b0c083702..f0fee49024 100644 --- a/erts/test/erl_print_SUITE.erl +++ b/erts/test/erl_print_SUITE.erl @@ -40,7 +40,7 @@ -export([erlang_display/1, integer/1, float/1, string/1, character/1, snprintf/1, quote/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index c21064fd3f..7e44be1fe0 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -27,7 +27,7 @@ compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1, make_dep_options/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index 9279872d25..6440cbf0d7 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -38,7 +38,7 @@ -export([args_file/1, evil_args_file/1, env/1, args_file_env/1, otp_7461/1, otp_7461_remote/1, otp_8209/1, zdbbl_dist_buf_busy_limit/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). init_per_testcase(Case, Config) -> diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index 388af66b23..8ad2a32278 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -51,7 +51,7 @@ atomic/1, dw_atomic_massage/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). tests() -> [create_join_thread, diff --git a/erts/test/ignore_cores.erl b/erts/test/ignore_cores.erl index 13f34cd10f..e40b91392c 100644 --- a/erts/test/ignore_cores.erl +++ b/erts/test/ignore_cores.erl @@ -28,7 +28,7 @@ -module(ignore_cores). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([init/1, fini/1, setup/3, setup/4, restore/1, dir/1]). diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl index b380b064bd..d6df1aab6b 100644 --- a/erts/test/install_SUITE.erl +++ b/erts/test/install_SUITE.erl @@ -52,7 +52,7 @@ -define(DEFAULT_TIMEOUT, ?t:minutes(1)). -define(JOIN(A,B,C), filename:join(A, B, C)). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -record(inst, {mkdirs = true, symlinks = true, diff --git a/erts/test/nt_SUITE.erl b/erts/test/nt_SUITE.erl index dbae8df7fe..1ddaaaaeb5 100644 --- a/erts/test/nt_SUITE.erl +++ b/erts/test/nt_SUITE.erl @@ -20,7 +20,7 @@ %%% Purpose: Test NT specific utilities -module(nt_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 87d620b180..6777c205b0 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -27,7 +27,7 @@ call_to_size_1/1,call_to_now_0/1,strong_components/1, erl_file_encoding/1,xml_file_encoding/1,runtime_dependencies/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -import(lists, [filter/2,foldl/3,foreach/2]). diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl index 6759d41a2b..e3c563d3d9 100644 --- a/erts/test/run_erl_SUITE.erl +++ b/erts/test/run_erl_SUITE.erl @@ -26,7 +26,7 @@ basic/1,heavy/1,heavier/1,defunct/1]). -export([ping_me_back/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). init_per_testcase(_Case, Config) -> Dog = ?t:timetrap(?t:minutes(2)), -- cgit v1.2.3 From 4e1162bbdf88465a03da165c088ad1256b816956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 15 Feb 2016 16:04:32 +0100 Subject: Makefiles: Remove test_server from include path and code path Since no test suites includede test_server.hrl, there is no need to have test_server in the include path or code path. --- erts/emulator/test/Makefile | 2 +- erts/test/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 8cc47937b7..987a655c3d 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -158,7 +158,7 @@ RELSYSDIR = $(RELEASE_PATH)/emulator_test # FLAGS # ---------------------------------------------------- ERL_MAKE_FLAGS += -ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include +ERL_COMPILE_FLAGS += # ---------------------------------------------------- # Targets diff --git a/erts/test/Makefile b/erts/test/Makefile index 5263d8cd4f..a01d67e34f 100644 --- a/erts/test/Makefile +++ b/erts/test/Makefile @@ -54,7 +54,7 @@ RELSYSDIR = $(RELEASE_PATH)/system_test # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include +ERL_COMPILE_FLAGS += # ---------------------------------------------------- # Targets -- cgit v1.2.3 From 8dbd8cb1fd7fe2fbe9f29fd5acf314f4cfe70895 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 17 Feb 2016 13:06:58 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 49984 -> 49836 bytes erts/preloaded/ebin/erlang.beam | Bin 101988 -> 101968 bytes erts/preloaded/ebin/erts_code_purger.beam | Bin 8724 -> 8704 bytes erts/preloaded/ebin/erts_internal.beam | Bin 9412 -> 9392 bytes erts/preloaded/ebin/init.beam | Bin 44724 -> 44588 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1468 -> 1448 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1340 -> 1320 bytes erts/preloaded/ebin/prim_file.beam | Bin 44908 -> 44800 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72632 -> 72608 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23308 -> 23156 bytes erts/preloaded/ebin/zlib.beam | Bin 14176 -> 14156 bytes 11 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 2a54c10273..ca9e6bd20f 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index f1e48b3282..b353129a34 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index 0b6dc19b66..4188e5fd9b 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index e9fbdfd048..5590f5a911 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 2f66b5f970..8ac7f5b471 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 057fe79054..3cd2515ba8 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 33f5de7d3f..9a208d1545 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index b96927b72b..a386613eef 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 767a48d781..d68d18ecba 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 2bc85bbcbf..01b3b1feb8 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 0b4afc0a39..7252d866bb 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 661aeed3ef57de87330123adf6100e179fd7dad0 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 15 Feb 2016 15:26:45 +0100 Subject: Improve cmpxchg8b inline asm configure test --- erts/aclocal.m4 | 144 +++++++++++++++++++---- erts/include/internal/ethread_header_config.h.in | 4 + erts/include/internal/i386/ethr_dw_atomic.h | 16 ++- 3 files changed, 135 insertions(+), 29 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 3d52538933..3db6ff5b4c 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -2117,45 +2117,126 @@ esac case "$GCC-$host_cpu" in yes-i86pc | yes-i*86 | yes-x86_64 | yes-amd64) + + if test $ac_cv_sizeof_void_p = 4; then + dw_cmpxchg="cmpxchg8b" + else + dw_cmpxchg="cmpxchg16b" + fi + gcc_dw_cmpxchg_asm=no - AC_MSG_CHECKING([for gcc double word cmpxchg asm support]) - AC_TRY_COMPILE([], + gcc_pic_dw_cmpxchg_asm=no + gcc_cflags_pic=no + gcc_cmpxchg8b_pic_no_clobber_ebx=no + gcc_cmpxchg8b_pic_no_clobber_ebx_register_shortage=no + + save_CFLAGS="$CFLAGS" + + # Check if it works out of the box using passed CFLAGS + # and with -fPIC added to CFLAGS if the passed CFLAGS + # doesn't trigger position independent code + pic_cmpxchg=unknown + while true; do + + case $pic_cmpxchg in + yes) pic_text="pic ";; + *) pic_text="";; + esac + + AC_MSG_CHECKING([for gcc $pic_text$dw_cmpxchg plain asm support]) + + plain_cmpxchg=no + AC_TRY_COMPILE([], [ char xchgd; long new[2], xchg[2], *p; __asm__ __volatile__( -#if ETHR_SIZEOF_PTR == 4 && defined(__PIC__) && __PIC__ - "pushl %%ebx\n\t" - "movl %8, %%ebx\n\t" -#endif #if ETHR_SIZEOF_PTR == 4 "lock; cmpxchg8b %0\n\t" #else "lock; cmpxchg16b %0\n\t" #endif "setz %3\n\t" -#if ETHR_SIZEOF_PTR == 4 && defined(__PIC__) && __PIC__ - "popl %%ebx\n\t" -#endif : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=c"(xchgd) - : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "3"(new[1]), -#if ETHR_SIZEOF_PTR == 4 && defined(__PIC__) && __PIC__ - "r"(new[0]) -#else - "b"(new[0]) -#endif + : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "3"(new[1]), "b"(new[0]) : "cc", "memory"); + ], + [plain_cmpxchg=yes]) + + AC_MSG_RESULT([$plain_cmpxchg]) + + if test $pic_cmpxchg = yes; then + gcc_pic_dw_cmpxchg_asm=$plain_cmpxchg + break + fi + + gcc_dw_cmpxchg_asm=$plain_cmpxchg + # If not already compiling to position independent + # code add -fPIC to CFLAGS and do it again. This + # since we want also want to know how to compile + # to position independent code since this might + # cause problems with the use of the EBX register + # as input to the asm on 32-bit x86 and old gcc + # compilers (gcc vsn < 5). + + AC_TRY_COMPILE([], + [ +#if !defined(__PIC__) || !__PIC__ +# error no pic +#endif ], - [gcc_dw_cmpxchg_asm=yes]) - if test $gcc_dw_cmpxchg_asm = no && test $ac_cv_sizeof_void_p = 4; then + [pic_cmpxchg=yes + gcc_cflags_pic=yes], + [pic_cmpxchg=no]) + + if test $pic_cmpxchg = yes; then + gcc_pic_dw_cmpxchg_asm=$gcc_dw_cmpxchg_asm + break + fi + + CFLAGS="$save_CFLAGS -fPIC" + pic_cmpxchg=yes + + done + + if test $gcc_pic_dw_cmpxchg_asm = no && test $ac_cv_sizeof_void_p = 4; then + + AC_MSG_CHECKING([for gcc pic cmpxchg8b asm support with EBX workaround]) + + # Check if we can work around it by managing the ebx + # register explicitly in the asm... + AC_TRY_COMPILE([], + [ + char xchgd; + long new[2], xchg[2], *p; + __asm__ __volatile__( + "pushl %%ebx\n\t" + "movl %8, %%ebx\n\t" + "lock; cmpxchg8b %0\n\t" + "setz %3\n\t" + "popl %%ebx\n\t" + : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=c"(xchgd) + : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "3"(new[1]), "r"(new[0]) + : "cc", "memory"); + ], + [gcc_pic_dw_cmpxchg_asm=yes + gcc_cmpxchg8b_pic_no_clobber_ebx=yes]) + + AC_MSG_RESULT([$gcc_pic_dw_cmpxchg_asm]) + + if test $gcc_pic_dw_cmpxchg_asm = no; then + + AC_MSG_CHECKING([for gcc pic cmpxchg8b asm support with EBX and register shortage workarounds]) + # If no optimization is enabled we sometimes get a + # register shortage. Check if we can work around + # this... + + AC_TRY_COMPILE([], [ char xchgd; long new[2], xchg[2], *p; -#if !defined(__PIC__) || !__PIC__ -# error nope -#endif __asm__ __volatile__( "pushl %%ebx\n\t" "movl (%7), %%ebx\n\t" @@ -2168,12 +2249,27 @@ case "$GCC-$host_cpu" in : "cc", "memory"); ], - [gcc_dw_cmpxchg_asm=yes]) - if test "$gcc_dw_cmpxchg_asm" = "yes"; then - AC_DEFINE(ETHR_CMPXCHG8B_REGISTER_SHORTAGE, 1, [Define if you get a register shortage with cmpxchg8b and position independent code]) + [gcc_pic_dw_cmpxchg_asm=yes + gcc_cmpxchg8b_pic_no_clobber_ebx=yes + gcc_cmpxchg8b_pic_no_clobber_ebx_register_shortage=yes]) + + AC_MSG_RESULT([$gcc_pic_dw_cmpxchg_asm]) fi + + if test $gcc_cflags_pic = yes; then + gcc_dw_cmpxchg_asm=$gcc_pic_dw_cmpxchg_asm + fi + + fi + + CFLAGS="$save_CFLAGS" + + if test "$gcc_cmpxchg8b_pic_no_clobber_ebx" = "yes"; then + AC_DEFINE(ETHR_CMPXCHG8B_PIC_NO_CLOBBER_EBX, 1, [Define if gcc wont let you clobber ebx with cmpxchg8b and position independent code]) + fi + if test "$gcc_cmpxchg8b_pic_no_clobber_ebx_register_shortage" = "yes"; then + AC_DEFINE(ETHR_CMPXCHG8B_REGISTER_SHORTAGE, 1, [Define if you get a register shortage with cmpxchg8b and position independent code]) fi - AC_MSG_RESULT([$gcc_dw_cmpxchg_asm]) if test "$gcc_dw_cmpxchg_asm" = "yes"; then AC_DEFINE(ETHR_GCC_HAVE_DW_CMPXCHG_ASM_SUPPORT, 1, [Define if you use a gcc that supports the double word cmpxchg instruction]) fi;; diff --git a/erts/include/internal/ethread_header_config.h.in b/erts/include/internal/ethread_header_config.h.in index 9cabd0591a..f4b08cfced 100644 --- a/erts/include/internal/ethread_header_config.h.in +++ b/erts/include/internal/ethread_header_config.h.in @@ -166,6 +166,10 @@ /* Define if you use a gcc that supports the double word cmpxchg instruction */ #undef ETHR_GCC_HAVE_DW_CMPXCHG_ASM_SUPPORT +/* Define if gcc wont let you clobber ebx with cmpxchg8b and position + independent code */ +#undef ETHR_CMPXCHG8B_PIC_NO_CLOBBER_EBX + /* Define if you get a register shortage with cmpxchg8b and position independent code */ #undef ETHR_CMPXCHG8B_REGISTER_SHORTAGE diff --git a/erts/include/internal/i386/ethr_dw_atomic.h b/erts/include/internal/i386/ethr_dw_atomic.h index e8c4119ef0..79de5d80da 100644 --- a/erts/include/internal/i386/ethr_dw_atomic.h +++ b/erts/include/internal/i386/ethr_dw_atomic.h @@ -115,13 +115,19 @@ ethr_native_dw_atomic_addr(ethr_native_dw_atomic_t *var) return (ethr_sint_t *) ETHR_DW_NATMC_MEM__(var); } -#if ETHR_SIZEOF_PTR == 4 && defined(__PIC__) && __PIC__ +#if defined(ETHR_CMPXCHG8B_PIC_NO_CLOBBER_EBX) && defined(__PIC__) && __PIC__ +#if ETHR_SIZEOF_PTR != 4 +# error unexpected pic issue +#endif /* * When position independent code is used in 32-bit mode, the EBX register - * is used for storage of global offset table address, and we may not - * use it as input or output in an asm. We need to save and restore the - * EBX register explicitly (for some reason gcc doesn't provide this - * service to us). + * is used for storage of global offset table address. When compiling with + * an old gcc (< vsn 5) we may not use it as input or output in an inline + * asm. We then need to save and restore the EBX register explicitly (for + * some reason old gcc compilers didn't provide this service to us). + * ETHR_CMPXCHG8B_PIC_NO_CLOBBER_EBX will be defined if we need to + * explicitly manage EBX ourselves. + * */ # define ETHR_NO_CLOBBER_EBX__ 1 #else -- cgit v1.2.3 From eb59e961ae05048ea30362aafb7f91db26ceb939 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 17 Feb 2016 16:56:05 +0100 Subject: Improve cmpxchg8b/cmpxchg16b inline asm Clang didn't like that ecx/rcx was mapped to input and output variables of different types. --- erts/aclocal.m4 | 24 ++++++------ erts/include/internal/i386/ethr_dw_atomic.h | 57 +++++++++++++++++++---------- 2 files changed, 49 insertions(+), 32 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 3db6ff5b4c..ec9b66bf29 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -2157,8 +2157,8 @@ case "$GCC-$host_cpu" in "lock; cmpxchg16b %0\n\t" #endif "setz %3\n\t" - : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=c"(xchgd) - : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "3"(new[1]), "b"(new[0]) + : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=q"(xchgd) + : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "c"(new[1]), "b"(new[0]) : "cc", "memory"); ], [plain_cmpxchg=yes]) @@ -2217,8 +2217,8 @@ case "$GCC-$host_cpu" in "lock; cmpxchg8b %0\n\t" "setz %3\n\t" "popl %%ebx\n\t" - : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=c"(xchgd) - : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "3"(new[1]), "r"(new[0]) + : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=q"(xchgd) + : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "c"(new[1]), "r"(new[0]) : "cc", "memory"); ], [gcc_pic_dw_cmpxchg_asm=yes @@ -2238,14 +2238,14 @@ case "$GCC-$host_cpu" in char xchgd; long new[2], xchg[2], *p; __asm__ __volatile__( - "pushl %%ebx\n\t" - "movl (%7), %%ebx\n\t" - "movl 4(%7), %%ecx\n\t" - "lock; cmpxchg8b %0\n\t" - "setz %3\n\t" - "popl %%ebx\n\t" - : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=c"(xchgd) - : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "3"(new) + "pushl %%ebx\n\t" + "movl (%7), %%ebx\n\t" + "movl 4(%7), %%ecx\n\t" + "lock; cmpxchg8b %0\n\t" + "setz %3\n\t" + "popl %%ebx\n\t" + : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=c"(xchgd) + : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "r"(new) : "cc", "memory"); ], diff --git a/erts/include/internal/i386/ethr_dw_atomic.h b/erts/include/internal/i386/ethr_dw_atomic.h index 79de5d80da..5444a6345c 100644 --- a/erts/include/internal/i386/ethr_dw_atomic.h +++ b/erts/include/internal/i386/ethr_dw_atomic.h @@ -157,36 +157,53 @@ ethr_native_dw_atomic_cmpxchg_mb(ethr_native_dw_atomic_t *var, ETHR_DW_DBG_ALIGNED__(p); +#if ETHR_NO_CLOBBER_EBX__ && ETHR_CMPXCHG8B_REGISTER_SHORTAGE + /* + * gcc wont let us use ebx as input and we + * get a register shortage + */ + __asm__ __volatile__( -#if ETHR_NO_CLOBBER_EBX__ "pushl %%ebx\n\t" -# if ETHR_CMPXCHG8B_REGISTER_SHORTAGE "movl (%7), %%ebx\n\t" "movl 4(%7), %%ecx\n\t" -# else - "movl %8, %%ebx\n\t" -# endif -#endif - "lock; cmpxchg" ETHR_DW_CMPXCHG_SFX__ " %0\n\t" + "lock; cmpxchg8b %0\n\t" "setz %3\n\t" -#if ETHR_NO_CLOBBER_EBX__ "popl %%ebx\n\t" -#endif : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=c"(xchgd) - : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), -#if ETHR_NO_CLOBBER_EBX__ -# if ETHR_CMPXCHG8B_REGISTER_SHORTAGE - "3"(new) -# else - "3"(new[1]), - "r"(new[0]) -# endif + : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "r"(new) + : "cc", "memory"); + +#elif ETHR_NO_CLOBBER_EBX__ + /* + * gcc wont let us use ebx as input + */ + + __asm__ __volatile__( + "pushl %%ebx\n\t" + "movl %8, %%ebx\n\t" + "lock; cmpxchg8b %0\n\t" + "setz %3\n\t" + "popl %%ebx\n\t" + : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=q"(xchgd) + : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "c"(new[1]), "r"(new[0]) + : "cc", "memory"); + #else - "3"(new[1]), - "b"(new[0]) -#endif + /* + * gcc lets us place values in the registers where + * we want them + */ + + __asm__ __volatile__( + "lock; cmpxchg" ETHR_DW_CMPXCHG_SFX__ " %0\n\t" + "setz %3\n\t" + : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=q"(xchgd) + : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "c"(new[1]), "b"(new[0]) : "cc", "memory"); +#endif + return (int) xchgd; } -- cgit v1.2.3 From 184a4d38db21cdc3011a5c906f38c880c1ffdc58 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 18 Feb 2016 19:44:24 +0100 Subject: erts: Fix lock checker for process locks Do lock order check *before* trying to seize lock... duh! --- erts/emulator/beam/erl_process_lock.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index a64c993e8f..9c59301086 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -523,6 +523,10 @@ erts_smp_proc_lock__(Process *p, ERTS_LC_ASSERT((locks & ~ERTS_PROC_LOCKS_ALL) == 0); +#ifdef ERTS_ENABLE_LOCK_CHECK + erts_proc_lc_lock(p, locks, file, line); +#endif + old_lflgs = erts_smp_proc_raw_trylock__(p, locks); if (old_lflgs != 0) { @@ -544,9 +548,6 @@ erts_smp_proc_lock__(Process *p, #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, file, line); -#endif #ifdef ERTS_PROC_LOCK_DEBUG erts_proc_lock_op_debug(p, locks, 1); -- cgit v1.2.3 From e1be12434b06fb2594af5cdafc5efc5b9182d8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 30 Nov 2015 15:28:37 +0100 Subject: beam_load.c: Add a function to check for an on_load function We will need a way to check whether an prepared BEAM modules has an on_load function. --- erts/emulator/beam/beam_load.c | 17 +++++++++++++++++ erts/emulator/beam/global.h | 1 + 2 files changed, 18 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 1511a4f935..f115df935f 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -888,6 +888,23 @@ erts_module_for_prepared_code(Binary* magic) } } +/* + * Return a non-zero value if the module has an on_load function, + * or 0 if it does not. + */ + +Eterm +erts_has_code_on_load(Binary* magic) +{ + LoaderState* stp; + + if (ERTS_MAGIC_BIN_DESTRUCTOR(magic) != loader_state_dtor) { + return NIL; + } + stp = ERTS_MAGIC_BIN_DATA(magic); + return stp->on_load ? am_true : am_false; +} + static void free_loader_state(Binary* magic) { diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 2a8bdb6ee3..b174fa3e8a 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1008,6 +1008,7 @@ typedef struct { Binary* erts_alloc_loader_state(void); Eterm erts_module_for_prepared_code(Binary* magic); +Eterm erts_has_code_on_load(Binary* magic); Eterm erts_prepare_loading(Binary* loader_state, Process *c_p, Eterm group_leader, Eterm* modp, byte* code, Uint size); -- cgit v1.2.3 From 9f60b6ea108b625affe5aef745443c8422c8d64f Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Sun, 21 Feb 2016 20:32:40 -0500 Subject: Skip run queue lock check for dirty schedulers The wake_scheduler function asserts that the run queue is not locked, but this assertion sometimes fails for dirty schedulers (in January 2016 a user in erlang-questions reported a dirty schedulers problem related to this). After discussing it with Rickard, we decided modifying the assertion was the most practical way to address the problem. --- erts/emulator/beam/erl_process.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b7499c5b5a..fd29c22810 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -3250,7 +3250,8 @@ wake_scheduler(ErtsRunQueue *rq) * so all code *should* handle this without having * the lock on the run queue. */ - ERTS_SMP_LC_ASSERT(!erts_smp_lc_runq_is_locked(rq)); + ERTS_SMP_LC_ASSERT(!erts_smp_lc_runq_is_locked(rq) + || ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); ssi = rq->scheduler->ssi; -- cgit v1.2.3 From f10621e0cd4321834d072d2c495fc84066f04cd3 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Thu, 18 Feb 2016 15:01:15 +0100 Subject: Fix a few dialyzer warnings --- erts/preloaded/src/prim_file.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index c87b2645ec..ab5359ebbc 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1276,6 +1276,7 @@ lseek_position(_) -> %% Translates the response from the driver into %% {ok, Result} or {error, Reason}. +-dialyzer({no_improper_lists, translate_response/2}). translate_response(?FILE_RESP_OK, []) -> ok; translate_response(?FILE_RESP_ERROR, List) when is_list(List) -> -- cgit v1.2.3 From 6cec93eb4e697fc1451b0b4729fc3d4874581ebd Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 23 Feb 2016 11:24:12 +0100 Subject: erts: Fix install of suspend handler This commit makes sure to setup the suspend handler to matter what +B option is given at the command line. --- erts/emulator/beam/break.c | 4 ++-- erts/emulator/beam/erl_init.c | 1 + erts/emulator/sys/unix/erl_unix_sys.h | 1 + erts/emulator/sys/unix/sys.c | 24 ++++++++++++++++-------- 4 files changed, 20 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 4ce9d24479..8647b621d5 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -684,7 +684,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) crash dump. */ erts_thr_progress_fatal_error_block(&tpd_buf); -#ifdef ERTS_THR_HAVE_SIG_FUNCS +#ifdef ERTS_SYS_SUSPEND_SIGNAL /* * We suspend all scheduler threads so that we can dump some * data about the currently running processes and scheduler data. @@ -818,7 +818,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) #ifdef ERTS_SMP -#if defined(ERTS_THR_HAVE_SIG_FUNCS) +#ifdef ERTS_SYS_SUSPEND_SIGNAL /* We resume all schedulers so that we are in a known safe state when we write the rest of the crash dump */ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index d9c3b0dcf4..bcc54491bd 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -2134,6 +2134,7 @@ erl_start(int argc, char **argv) init_break_handler(); if (replace_intr) erts_replace_intr(); + sys_init_suspend_handler(); #endif boot_argc = argc - i; /* Number of arguments to init */ diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 8d4e98bf3a..b55180c509 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -311,6 +311,7 @@ extern SIGFUNC sys_signal(int, SIGFUNC); extern void sys_sigrelease(int); extern void sys_sigblock(int); extern void sys_stop_cat(void); +extern void sys_init_suspend_handler(void); /* * Handling of floating point exceptions. diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 8d7da3e47e..a57b69aa89 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -226,8 +226,10 @@ static erts_smp_atomic_t sys_misc_mem_sz; static void smp_sig_notify(char c); static int sig_notify_fds[2] = {-1, -1}; +#if !defined(ETHR_UNUSABLE_SIGUSRX) && defined(ERTS_THR_HAVE_SIG_FUNCS) static int sig_suspend_fds[2] = {-1, -1}; #define ERTS_SYS_SUSPEND_SIGNAL SIGUSR2 +#endif #endif @@ -872,7 +874,7 @@ sigusr1_exit(void) #else -#ifdef ERTS_SMP +#ifdef ERTS_SYS_SUSPEND_SIGNAL void sys_thr_suspend(erts_tid_t tid) { erts_thr_kill(tid, ERTS_SYS_SUSPEND_SIGNAL); @@ -900,7 +902,7 @@ static RETSIGTYPE user_signal1(int signum) #endif } -#ifdef ERTS_SMP +#ifdef ERTS_SYS_SUSPEND_SIGNAL #if (defined(SIG_SIGSET) || defined(SIG_SIGNAL)) static RETSIGTYPE suspend_signal(void) #else @@ -913,7 +915,7 @@ static RETSIGTYPE suspend_signal(int signum) res = read(sig_suspend_fds[0], buf, sizeof(int)); } while (res < 0 && errno == EINTR); } -#endif /* #ifdef ERTS_SMP */ +#endif /* #ifdef ERTS_SYS_SUSPEND_SIGNAL */ #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ @@ -966,13 +968,17 @@ void init_break_handler(void) sys_signal(SIGINT, request_break); #ifndef ETHR_UNUSABLE_SIGUSRX sys_signal(SIGUSR1, user_signal1); -#ifdef ERTS_SMP - sys_signal(ERTS_SYS_SUSPEND_SIGNAL, suspend_signal); -#endif /* #ifdef ERTS_SMP */ #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ sys_signal(SIGQUIT, do_quit); } +void sys_init_suspend_handler(void) +{ +#ifdef ERTS_SYS_SUSPEND_SIGNAL + sys_signal(ERTS_SYS_SUSPEND_SIGNAL, suspend_signal); +#endif +} + int sys_max_files(void) { return(max_files); @@ -990,7 +996,7 @@ static void block_signals(void) #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ #endif /* #ifndef ERTS_SMP */ -#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX) +#ifdef ERTS_SYS_SUSPEND_SIGNAL sys_sigblock(ERTS_SYS_SUSPEND_SIGNAL); #endif @@ -1009,7 +1015,7 @@ static void unblock_signals(void) #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */ #endif /* #ifndef ERTS_SMP */ -#if defined(ERTS_SMP) && !defined(ETHR_UNUSABLE_SIGUSRX) +#ifdef ERTS_SYS_SUSPEND_SIGNAL sys_sigrelease(ERTS_SYS_SUSPEND_SIGNAL); #endif @@ -3248,12 +3254,14 @@ init_smp_sig_notify(void) static void init_smp_sig_suspend(void) { +#ifdef ERTS_SYS_SUSPEND_SIGNAL if (pipe(sig_suspend_fds) < 0) { erl_exit(ERTS_ABORT_EXIT, "Failed to create sig_suspend pipe: %s (%d)\n", erl_errno_id(errno), errno); } +#endif } #ifdef __DARWIN__ -- cgit v1.2.3 From db241c69cef8774b9b7afa7e0f0f8dbdcf528a07 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 17 Feb 2016 21:17:38 +0100 Subject: erts: Make literal_alloc documented and configurable Except it cannot be disabled and cannot be multi-threaded. The bit-vector 'erts_literal_vspace_map' on 32-bit is currently only protected by the literal allocator mutex. We could allow multiple instances on 64-bit (I think), but what would be the point? --- erts/doc/src/erts_alloc.xml | 5 ++++- erts/emulator/beam/erl_alloc.c | 31 +++++++++++++++++++++++++++---- erts/emulator/sys/common/erl_mmap.h | 4 +++- erts/etc/common/erlexec.c | 1 + 4 files changed, 35 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 15b78ffa10..75de74523e 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -52,6 +52,8 @@ Allocator used for ETS data. driver_alloc Allocator used for driver data. + literal_alloc + Allocator used for constant terms in Erlang code. sl_alloc Allocator used for memory blocks that are expected to be short-lived. @@ -77,7 +79,7 @@ instead of creating new segments. This in order to reduce the number of system calls made.
-

sys_alloc is always enabled and +

sys_alloc and literal_alloc are always enabled and cannot be disabled. mseg_alloc is always enabled if it is available and an allocator that uses it is enabled. All other allocators can be enabled or disabled. @@ -246,6 +248,7 @@ the currently present allocators:

B: binary_alloc + I: literal_alloc D: std_alloc E: ets_alloc F: fix_alloc diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 806f569c38..14ff13fdb2 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -165,6 +165,8 @@ enum allctr_type { struct au_init { int enable; int thr_spec; + int disable_allowed; + int thr_spec_allowed; int carrier_migration_allowed; enum allctr_type atype; struct { @@ -217,7 +219,7 @@ typedef struct { struct au_init test_alloc; } erts_alc_hndl_args_init_t; -#define ERTS_AU_INIT__ {0, 0, 1, GOODFIT, DEFAULT_ALLCTR_INIT, {1,1,1,1}} +#define ERTS_AU_INIT__ {0, 0, 1, 1, 1, GOODFIT, DEFAULT_ALLCTR_INIT, {1,1,1,1}} #define SET_DEFAULT_ALLOC_OPTS(IP) \ do { \ @@ -294,6 +296,9 @@ set_default_literal_alloc_opts(struct au_init *ip) SET_DEFAULT_ALLOC_OPTS(ip); ip->enable = 1; ip->thr_spec = 0; + ip->disable_allowed = 0; + ip->thr_spec_allowed = 0; + ip->carrier_migration_allowed = 0; ip->atype = BESTFIT; ip->init.bf.ao = 1; ip->init.util.ramv = 0; @@ -1352,9 +1357,17 @@ handle_au_arg(struct au_init *auip, else goto bad_switch; break; - case 'e': - auip->enable = get_bool_value(sub_param+1, argv, ip); + case 'e': { + int e = get_bool_value(sub_param + 1, argv, ip); + if (!auip->disable_allowed && !e) { + if (!u_switch) + bad_value(param, sub_param + 1, "false"); + else + ASSERT(auip->enable); /* ignore */ + } + else auip->enable = e; break; + } case 'l': if (has_prefix("lmbcs", sub_param)) { auip->default_.lmbcs = 0; @@ -1423,7 +1436,14 @@ handle_au_arg(struct au_init *auip, case 't': { int res = get_bool_value(sub_param+1, argv, ip); if (res > 0) { - auip->thr_spec = 1; + if (!auip->thr_spec_allowed) { + if (!u_switch) + bad_value(param, sub_param + 1, "true"); + else + ASSERT(!auip->thr_spec); /* ignore */ + } + else + auip->thr_spec = 1; break; } else if (res == 0) { @@ -1472,6 +1492,9 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) case 'B': handle_au_arg(&init->binary_alloc, &argv[i][3], argv, &i, 0); break; + case 'I': + handle_au_arg(&init->literal_alloc, &argv[i][3], argv, &i, 0); + break; case 'D': handle_au_arg(&init->std_alloc, &argv[i][3], argv, &i, 0); break; diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 61d912fd28..67e131b53e 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -50,8 +50,10 @@ typedef struct { #define ERTS_MMAP_INIT_DEFAULT_INITER \ {{NULL, NULL}, {NULL, NULL}, 0, 1, (1 << 16), 1} +#define ERTS_LITERAL_VIRTUAL_AREA_SIZE (UWORD_CONSTANT(1)*1024*1024*1024) + #define ERTS_MMAP_INIT_LITERAL_INITER \ - {{NULL, NULL}, {NULL, NULL}, 1024*1024*1024, 1, (1 << 16), 0} + {{NULL, NULL}, {NULL, NULL}, ERTS_LITERAL_VIRTUAL_AREA_SIZE, 1, (1 << 16), 0} typedef struct ErtsMemMapper_ ErtsMemMapper; diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index f21671e837..91204fda5c 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -65,6 +65,7 @@ static const char plusM_au_allocs[]= { 'u', /* all alloc_util allocators */ 'B', /* binary_alloc */ + 'I', /* literal_alloc */ 'D', /* std_alloc */ 'E', /* ets_alloc */ 'F', /* fix_alloc */ -- cgit v1.2.3 From abe5967c1964a4ca93f321c6cd564c8650f11a53 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 19 Feb 2016 15:45:18 +0100 Subject: erts: Refactor init of erts_literal_mmapper --- erts/emulator/beam/erl_alloc.c | 3 --- erts/emulator/sys/common/erl_mseg.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 14ff13fdb2..7c880342bd 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -774,9 +774,6 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) #if HAVE_ERTS_MSEG init.mseg.nos = erts_no_schedulers; erts_mseg_init(&init.mseg); -# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) - erts_mmap_init(&erts_literal_mmapper, &init.mseg.literal_mmap); -# endif #endif erts_alcu_init(&init.alloc_util); diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 20695899eb..2f2d7a5dd8 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -1403,6 +1403,9 @@ erts_mseg_init(ErtsMsegInit_t *init) erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms"); erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap); +#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) + erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap); +#endif if (!IS_2POW(GET_PAGE_SIZE)) erl_exit(ERTS_ABORT_EXIT, "erts_mseg: Unexpected page_size %beu\n", GET_PAGE_SIZE); -- cgit v1.2.3 From 8e2a21f1df1140867d0b074ec7a86610d1e1b51e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 22 Feb 2016 18:18:15 +0100 Subject: erts: Add emulator flag +MIscs for literal super carrier size --- erts/doc/src/erts_alloc.xml | 10 ++++++++++ erts/emulator/beam/erl_alloc.c | 11 +++++++++-- erts/etc/common/erlexec.c | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 75de74523e..0965f9b49c 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -566,6 +566,16 @@ set to false, sys_alloc carriers will never be created by allocators using the alloc_util framework. +

The following flag is special for literal_alloc:

+ + ]]> + + literal_alloc super carrier size (in MB). The amount of + virtual address space reserved for literal terms in + Erlang code on 64-bit architectures. The default is 1024 (1GB) + and is usually sufficient. The flag is ignored on 32-bit + architectures. +

Instrumentation flags:

+Mim true|false diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 7c880342bd..a266ea6d19 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1489,8 +1489,15 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) case 'B': handle_au_arg(&init->binary_alloc, &argv[i][3], argv, &i, 0); break; - case 'I': - handle_au_arg(&init->literal_alloc, &argv[i][3], argv, &i, 0); + case 'I': + if (has_prefix("scs", argv[i]+3)) { +#if HAVE_ERTS_MSEG + init->mseg.literal_mmap.scs = +#endif + get_mb_value(argv[i]+6, argv, &i); + } + else + handle_au_arg(&init->literal_alloc, &argv[i][3], argv, &i, 0); break; case 'D': handle_au_arg(&init->std_alloc, &argv[i][3], argv, &i, 0); diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 91204fda5c..54da59e50d 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -122,6 +122,7 @@ static char *plusM_other_switches[] = { "Ym", "Ytp", "Ytt", + "Iscs", NULL }; -- cgit v1.2.3 From d166333d3c0d0584841f90654d63934edd076c5c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 23 Feb 2016 17:22:56 +0100 Subject: typos --- erts/configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 4fb725ff00..c7b095d47a 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -4111,7 +4111,7 @@ AC_ARG_WITH(ssl-rpath, AS_HELP_STRING([--with-ssl-rpath=yes|no|PATHS], [runtime library path for OpenSSL. Default is "yes", which equates to a number of standard locations. If "no", then no runtime - library paths wil be used. Anything else should be a + library paths will be used. Anything else should be a comma separated list of paths.]), [ case X$with_ssl in -- cgit v1.2.3 From a5e66520d92d283d14f1352dcfd0ce889c27a0c5 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 23 Feb 2016 18:31:52 +0100 Subject: Do not wait for main lock when looking up process not running --- erts/emulator/beam/erl_process.c | 102 +++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 31 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b24f26138c..adea745471 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -6462,9 +6462,6 @@ suspend_process(Process *c_p, Process *p) if (suspended) { - ASSERT(!(ERTS_PSFLG_RUNNING & state) - || p == erts_get_current_process()); - if (suspended > 0 && erts_system_profile_flags.runnable_procs) { /* 'state' is before our change... */ @@ -8411,9 +8408,8 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, resume_process(rp, rp_locks); } else { - rp = erts_pid2proc(c_p, c_p_locks|ERTS_PROC_LOCK_STATUS, - pid, pid_locks|ERTS_PROC_LOCK_STATUS); + pid, ERTS_PROC_LOCK_STATUS); if (!rp) { c_p->flags &= ~F_P2PNR_RESCHED; @@ -8422,40 +8418,84 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, ASSERT(!(c_p->flags & F_P2PNR_RESCHED)); - if (suspend) { - if (suspend_process(c_p, rp)) - goto done; - } - else { - if (!((ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS) - & erts_smp_atomic32_read_acqb(&rp->state))) - goto done; + /* + * Suspend the other process in order to prevent + * it from being selected for normal execution. + * This will however not prevent it from being + * selected for execution of a system task. If + * it is selected for execution of a system task + * we might be blocked for quite a while if the + * try-lock below fails. That is, there is room + * for improvement here... + */ - } + if (!suspend_process(c_p, rp)) { + /* Other process running */ - /* Other process running */ + ASSERT(ERTS_PSFLG_RUNNING + & erts_smp_atomic32_read_nob(&rp->state)); - /* - * 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->common.id, handle_pend_sync_suspend); - ASSERT(is_nil(c_p->suspendee)); + running: - /* Suspend c_p; when rp is suspended c_p will be resumed. */ - suspend_process(c_p, c_p); - c_p->flags |= F_P2PNR_RESCHED; + /* + * 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->common.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, ERTS_PROC_LOCK_STATUS); + rp = ERTS_PROC_LOCK_BUSY; + } + else { + ErtsProcLocks need_locks = pid_locks & ~ERTS_PROC_LOCK_STATUS; + if (need_locks && erts_smp_proc_trylock(rp, need_locks) == EBUSY) { + if (ERTS_PSFLG_RUNNING_SYS + & erts_smp_atomic32_read_nob(&rp->state)) { + /* Executing system task... */ + resume_process(rp, ERTS_PROC_LOCK_STATUS); + goto running; + } + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); + /* + * If we are unlucky, the process just got selected for + * execution of a system task. In this case we may be + * blocked here for quite a while... Execution of system + * tasks are fortunately quite rare events. We try to + * avoid this by checking if it is in a state executing + * system tasks (above), but it will not prevent all + * scenarios for a long block here... + */ + rp = erts_pid2proc(c_p, c_p_locks|ERTS_PROC_LOCK_STATUS, + pid, pid_locks|ERTS_PROC_LOCK_STATUS); + if (!rp) + goto done; + } + + /* + * The previous suspend has prevented the process + * from being selected for normal execution regardless + * of locks held or not held on it... + */ + ASSERT(!(ERTS_PSFLG_RUNNING + & erts_smp_atomic32_read_nob(&rp->state))); + + if (!suspend) + resume_process(rp, pid_locks|ERTS_PROC_LOCK_STATUS); } - /* 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: + if (rp && rp != ERTS_PROC_LOCK_BUSY && !(pid_locks & ERTS_PROC_LOCK_STATUS)) erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); if (unlock_c_p_status) -- cgit v1.2.3 From 6bd691c8e528c2e8cf3aded8f3877b18ecff4c58 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 24 Feb 2016 10:10:35 +0100 Subject: Update preloaded module --- erts/preloaded/ebin/prim_file.beam | Bin 44892 -> 44980 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 97170551bf..9256e35553 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ -- cgit v1.2.3 From 56090db3ea417157a749bdd810fc61d117493f1f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 23 Feb 2016 19:45:01 +0100 Subject: erts: Change erl_exit into erts_exit This is mostly a pure refactoring. Except for the buggy cases when calling erlang:halt() with a positive integer in the range -(INT_MIN+2) to -INT_MIN that got confused with ERTS_ABORT_EXIT, ERTS_DUMP_EXIT and ERTS_INTR_EXIT. Outcome OLD erl_exit(n, ) NEW erts_exit(n, ) ------- ------------------- ------------------------------------------- exit(Status) n = -Status <= 0 n = Status >= 0 crashdump+abort n > 0, ignore n n = ERTS_ERROR_EXIT < 0 The outcome of the old ERTS_ABORT_EXIT, ERTS_INTR_EXIT and ERTS_DUMP_EXIT are the same as before (even though their values have changed). --- erts/emulator/beam/beam_catches.c | 6 ++-- erts/emulator/beam/beam_emu.c | 14 ++++---- erts/emulator/beam/bif.c | 34 +++++++++--------- erts/emulator/beam/binary.c | 2 +- erts/emulator/beam/break.c | 12 +++---- erts/emulator/beam/copy.c | 20 +++++------ erts/emulator/beam/dist.c | 10 +++--- erts/emulator/beam/erl_afit_alloc.c | 2 +- erts/emulator/beam/erl_alloc.c | 38 ++++++++++---------- erts/emulator/beam/erl_alloc_util.c | 8 ++--- erts/emulator/beam/erl_ao_firstfit_alloc.c | 2 +- erts/emulator/beam/erl_bestfit_alloc.c | 2 +- erts/emulator/beam/erl_bif_ddll.c | 2 +- erts/emulator/beam/erl_bif_info.c | 12 +++---- erts/emulator/beam/erl_bif_port.c | 4 +-- erts/emulator/beam/erl_cpu_topology.c | 10 +++--- erts/emulator/beam/erl_db.c | 14 ++++---- erts/emulator/beam/erl_db_hash.c | 2 +- erts/emulator/beam/erl_db_tree.c | 2 +- erts/emulator/beam/erl_db_util.c | 18 +++++----- erts/emulator/beam/erl_debug.c | 14 ++++---- erts/emulator/beam/erl_drv_thread.c | 2 +- erts/emulator/beam/erl_fun.c | 2 +- erts/emulator/beam/erl_gc.c | 6 ++-- erts/emulator/beam/erl_goodfit_alloc.c | 4 +-- erts/emulator/beam/erl_init.c | 36 ++++++++----------- erts/emulator/beam/erl_map.c | 32 ++++++++--------- erts/emulator/beam/erl_monitors.c | 2 +- erts/emulator/beam/erl_nif.c | 2 +- erts/emulator/beam/erl_node_tables.c | 4 +-- erts/emulator/beam/erl_port_task.c | 4 +-- erts/emulator/beam/erl_process.c | 42 +++++++++++----------- erts/emulator/beam/erl_process.h | 2 +- erts/emulator/beam/erl_process_dict.c | 8 ++--- erts/emulator/beam/erl_ptab.c | 2 +- erts/emulator/beam/erl_term.c | 2 +- erts/emulator/beam/erl_thr_progress.c | 12 +++---- erts/emulator/beam/erl_thr_queue.c | 6 ++-- erts/emulator/beam/erl_time_sup.c | 6 ++-- erts/emulator/beam/erl_trace.c | 4 +-- erts/emulator/beam/erl_unicode.c | 2 +- erts/emulator/beam/external.c | 18 +++++----- erts/emulator/beam/global.h | 12 +++---- erts/emulator/beam/hash.c | 2 +- erts/emulator/beam/index.c | 4 +-- erts/emulator/beam/io.c | 6 ++-- erts/emulator/beam/register.c | 2 +- erts/emulator/beam/sys.h | 25 ++++++------- erts/emulator/beam/time.c | 2 +- erts/emulator/beam/utils.c | 20 +++++------ erts/emulator/drivers/common/efile_drv.c | 15 +------- erts/emulator/drivers/common/inet_drv.c | 12 +++---- erts/emulator/drivers/win32/win_con.c | 4 +-- erts/emulator/hipe/hipe_bif0.c | 4 +-- erts/emulator/hipe/hipe_mode_switch.c | 6 ++-- erts/emulator/hipe/hipe_native_bif.c | 2 +- erts/emulator/sys/common/erl_mmap.c | 14 ++++---- erts/emulator/sys/common/erl_mseg.c | 4 +-- .../sys/common/erl_os_monotonic_time_extender.c | 2 +- erts/emulator/sys/ose/erl_poll.c | 2 +- erts/emulator/sys/ose/sys.c | 2 +- erts/emulator/sys/ose/sys_float.c | 2 +- erts/emulator/sys/unix/sys.c | 22 ++++++------ erts/emulator/sys/unix/sys_float.c | 2 +- erts/emulator/sys/unix/sys_time.c | 24 ++++++------- erts/emulator/sys/win32/erl_poll.c | 10 +++--- erts/emulator/sys/win32/sys.c | 8 ++--- erts/emulator/sys/win32/sys_interrupt.c | 6 ++-- erts/emulator/sys/win32/sys_time.c | 6 ++-- erts/emulator/valgrind/suppress.patched.3.6.0 | 2 +- erts/emulator/valgrind/suppress.standard | 2 +- 71 files changed, 317 insertions(+), 333 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_catches.c b/erts/emulator/beam/beam_catches.c index c1fd17c65d..7a1f4901aa 100644 --- a/erts/emulator/beam/beam_catches.c +++ b/erts/emulator/beam/beam_catches.c @@ -143,7 +143,7 @@ BeamInstr *beam_catches_car(unsigned i) 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); + erts_exit(ERTS_ERROR_EXIT, "beam_catches_delmod: index %#x is out of range\r\n", i); } return p->beam_catches[i].cp; } @@ -157,10 +157,10 @@ void beam_catches_delmod(unsigned head, BeamInstr *code, unsigned code_bytes, ASSERT((code_ix == erts_active_code_ix()) != bccix[erts_staging_code_ix()].is_staging); for(i = head; i != (unsigned)-1;) { if (i >= p->tabsize) { - erl_exit(1, "beam_catches_delmod: index %#x is out of range\r\n", i); + erts_exit(ERTS_ERROR_EXIT, "beam_catches_delmod: index %#x is out of range\r\n", i); } if( (char*)p->beam_catches[i].cp - (char*)code >= code_bytes ) { - erl_exit(1, + erts_exit(ERTS_ERROR_EXIT, "beam_catches_delmod: item %#x has cp %p which is not " "in module's range [%p,%p[\r\n", i, p->beam_catches[i].cp, code, ((char*)code + code_bytes)); diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 38def5d89f..da201d9b36 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5018,7 +5018,7 @@ do { \ #ifdef NO_FPE_SIGNALS OpCase(fclearerror): OpCase(i_fcheckerror): - erl_exit(1, "fclearerror/i_fcheckerror without fpe signals (beam_emu)"); + erts_exit(ERTS_ERROR_EXIT, "fclearerror/i_fcheckerror without fpe signals (beam_emu)"); # define ERTS_NO_FPE_CHECK_INIT ERTS_FP_CHECK_INIT # define ERTS_NO_FPE_ERROR ERTS_FP_ERROR #else @@ -5176,7 +5176,7 @@ do { \ I = handle_error(c_p, I, reg, NULL); goto post_error_handling; default: - erl_exit(1, "hipe_mode_switch: result %u\n", c_p->def_arg_reg[3]); + erts_exit(ERTS_ERROR_EXIT, "hipe_mode_switch: result %u\n", c_p->def_arg_reg[3]); } } OpCase(hipe_call_count): { @@ -5256,7 +5256,7 @@ do { \ OpCase(label_L): OpCase(on_load): OpCase(line_I): - erl_exit(1, "meta op\n"); + erts_exit(ERTS_ERROR_EXIT, "meta op\n"); /* * One-time initialization of Beam emulator. @@ -5310,7 +5310,7 @@ do { \ } #ifdef NO_JUMP_TABLE default: - erl_exit(1, "unexpected op code %d\n",Go); + erts_exit(ERTS_ERROR_EXIT, "unexpected op code %d\n",Go); } #endif return; /* Never executed */ @@ -5355,7 +5355,7 @@ translate_gc_bif(void* gcf) } else if (gcf == erts_gc_binary_part_3) { return binary_part_3; } else { - erl_exit(1, "bad gc bif"); + erts_exit(ERTS_ERROR_EXIT, "bad gc bif"); } } @@ -5420,7 +5420,7 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, BifFunction bf) Eterm* hp; Eterm Value = c_p->fvalue; Eterm Args = am_true; - c_p->i = pc; /* In case we call erl_exit(). */ + c_p->i = pc; /* In case we call erts_exit(). */ ASSERT(c_p->freason != TRAP); /* Should have been handled earlier. */ @@ -5484,7 +5484,7 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, BifFunction bf) c_p->cp = 0; /* To avoid keeping stale references. */ return new_pc; } - if (c_p->catches > 0) erl_exit(1, "Catch not found"); + if (c_p->catches > 0) erts_exit(ERTS_ERROR_EXIT, "Catch not found"); } ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); terminate_proc(c_p, Value); diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index a8cc19ee1f..a61025d19a 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2216,7 +2216,7 @@ BIF_RETTYPE send_3(BIF_ALIST_3) erts_dsend_export_trap_context(p, ctx)); break; default: - erl_exit(ERTS_ABORT_EXIT, "send_3 invalid result %d\n", (int)result); + erts_exit(ERTS_ABORT_EXIT, "send_3 invalid result %d\n", (int)result); break; } @@ -2260,7 +2260,7 @@ static BIF_RETTYPE dsend_continue_trap_1(BIF_ALIST_1) BIF_TRAP1(&dsend_continue_trap_export, BIF_P, BIF_ARG_1); } default: - erl_exit(ERTS_ABORT_EXIT, "dsend_continue_trap invalid result %d\n", (int)result); + erts_exit(ERTS_ABORT_EXIT, "dsend_continue_trap invalid result %d\n", (int)result); break; } ASSERT(! "Can not arrive here"); @@ -2332,7 +2332,7 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg) erts_dsend_export_trap_context(p, ctx)); break; default: - erl_exit(ERTS_ABORT_EXIT, "invalid send result %d\n", (int)result); + erts_exit(ERTS_ABORT_EXIT, "invalid send result %d\n", (int)result); break; } @@ -3883,7 +3883,7 @@ BIF_RETTYPE erts_debug_display_1(BIF_ALIST_1) erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(64); pres = erts_dsprintf(dsbufp, "%.*T\n", INT_MAX, BIF_ARG_1); if (pres < 0) - erl_exit(1, "Failed to convert term to string: %d (%s)\n", + erts_exit(ERTS_ERROR_EXIT, "Failed to convert term to string: %d (%s)\n", -pres, erl_errno_id(-pres)); hp = HAlloc(BIF_P, 2*dsbufp->str_len); /* we need length * 2 heap words */ res = buf_to_intlist(&hp, dsbufp->str, dsbufp->str_len, NIL); @@ -3905,7 +3905,7 @@ BIF_RETTYPE display_string_1(BIF_ALIST_1) } str = (char *) erts_alloc(ERTS_ALC_T_TMP, sizeof(char)*(len + 1)); if (intlist_to_buf(string, str, len) != len) - erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__); + erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error\n", __FILE__, __LINE__); str[len] = '\0'; erts_fprintf(stderr, "%s", str); erts_free(ERTS_ALC_T_TMP, (void *) str); @@ -3925,7 +3925,7 @@ BIF_RETTYPE display_nl_0(BIF_ALIST_0) BIF_RETTYPE halt_0(BIF_ALIST_0) { VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt()\n")); - erl_halt(0); + erts_halt(0); ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined); } @@ -3941,14 +3941,15 @@ BIF_RETTYPE halt_1(BIF_ALIST_1) Sint code; if (is_small(BIF_ARG_1) && (code = signed_val(BIF_ARG_1)) >= 0) { + int pos_int_code = (int)code & INT_MAX; VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1)); - erl_halt((int)(- code)); + erts_halt(pos_int_code); ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined); } else if (ERTS_IS_ATOM_STR("abort", BIF_ARG_1)) { VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1)); erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erl_exit(ERTS_ABORT_EXIT, ""); + erts_exit(ERTS_ABORT_EXIT, ""); } else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) { int i; @@ -3959,11 +3960,11 @@ BIF_RETTYPE halt_1(BIF_ALIST_1) halt_msg[i] = '\0'; VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1)); erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erl_exit(ERTS_DUMP_EXIT, "%s\n", halt_msg); + erts_exit(ERTS_DUMP_EXIT, "%s\n", halt_msg); } else goto error; - return NIL; /* Pedantic (lint does not know about erl_exit) */ + return NIL; /* Pedantic (lint does not know about erts_exit) */ error: BIF_ERROR(BIF_P, BADARG); } @@ -4002,22 +4003,23 @@ BIF_RETTYPE halt_2(BIF_ALIST_2) goto error; if (is_small(BIF_ARG_1) && (code = signed_val(BIF_ARG_1)) >= 0) { + int pos_int_code = (int)code & INT_MAX; VERBOSE(DEBUG_SYSTEM, ("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2)); if (flush) { - erl_halt((int)(- code)); + erts_halt(pos_int_code); ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined); } else { erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erl_exit((int)(- code), ""); + erts_exit(pos_int_code, ""); } } else if (ERTS_IS_ATOM_STR("abort", BIF_ARG_1)) { VERBOSE(DEBUG_SYSTEM, ("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2)); erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erl_exit(ERTS_ABORT_EXIT, ""); + erts_exit(ERTS_ABORT_EXIT, ""); } else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) { int i; @@ -4029,11 +4031,11 @@ BIF_RETTYPE halt_2(BIF_ALIST_2) VERBOSE(DEBUG_SYSTEM, ("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2)); erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erl_exit(ERTS_DUMP_EXIT, "%s\n", halt_msg); + erts_exit(ERTS_DUMP_EXIT, "%s\n", halt_msg); } else goto error; - return NIL; /* Pedantic (lint does not know about erl_exit) */ + return NIL; /* Pedantic (lint does not know about erts_exit) */ error: BIF_ERROR(BIF_P, BADARG); } @@ -4089,7 +4091,7 @@ term2list_dsprintf(Process *p, Eterm term) erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(64); pres = erts_dsprintf(dsbufp, "%T", term); if (pres < 0) - erl_exit(1, "Failed to convert term to list: %d (%s)\n", + erts_exit(ERTS_ERROR_EXIT, "Failed to convert term to list: %d (%s)\n", -pres, erl_errno_id(-pres)); hp = HAlloc(p, 2*dsbufp->str_len); /* we need length * 2 heap words */ res = buf_to_intlist(&hp, dsbufp->str, dsbufp->str_len, NIL); diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index e670fbf31c..247c8f1122 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -47,7 +47,7 @@ erts_init_binary(void) if ((((UWord) &((Binary *) 0)->orig_bytes[0]) % ((UWord) 8)) != 0) { /* I assume that any compiler should be able to optimize this away. If not, this test is not very expensive... */ - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Internal error: Address of orig_bytes[0] of a Binary" " is *not* 8-byte aligned\n"); } diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 4ce9d24479..0ddf7f4e6d 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -104,7 +104,7 @@ process_killer(void) erts_printf("(k)ill (n)ext (r)eturn:\n"); while(1) { if ((j = sys_get_key(0)) <= 0) - erl_exit(0, ""); + erts_exit(0, ""); switch(j) { case 'k': { ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; @@ -493,7 +493,7 @@ do_break(void) halt immediately if break is called */ mode = erts_read_env("ERL_CONSOLE_MODE"); if (mode && strcmp(mode, "window") != 0) - erl_exit(0, ""); + erts_exit(0, ""); erts_free_read_env(mode); #endif /* __WIN32__ */ @@ -503,7 +503,7 @@ do_break(void) while (1) { if ((i = sys_get_key(0)) <= 0) - erl_exit(0, ""); + erts_exit(0, ""); switch (i) { case 'q': case 'a': @@ -513,9 +513,9 @@ do_break(void) * The usual reason for a read error is Ctrl-C. Treat this as * 'a' to avoid infinite loop. */ - erl_exit(0, ""); + erts_exit(0, ""); case 'A': /* Halt generating crash dump */ - erl_exit(1, "Crash dump requested by user"); + erts_exit(ERTS_ERROR_EXIT, "Crash dump requested by user"); case 'c': return; case 'p': @@ -785,7 +785,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) erts_fdprintf(fd, "Atoms: %d\n", atom_table_size()); #ifdef USE_THREADS - /* We want to note which thread it was that called erl_exit */ + /* We want to note which thread it was that called erts_exit */ if (erts_get_scheduler_data()) { erts_fdprintf(fd, "Calling Thread: scheduler:%d\n", erts_get_scheduler_data()->no); diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 8849dadd00..64be43edb4 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -59,7 +59,7 @@ copy_object(Eterm obj, Process* to) res = copy_struct(obj, size, &hp, &to->off_heap); #ifdef DEBUG if (eq(obj, res) == 0) { - erl_exit(ERTS_ABORT_EXIT, "copy not equal to source\n"); + erts_exit(ERTS_ABORT_EXIT, "copy not equal to source\n"); } #endif return res; @@ -171,7 +171,7 @@ Uint size_object(Eterm obj) } break; default: - erl_exit(ERTS_ABORT_EXIT, "size_object: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); + erts_exit(ERTS_ABORT_EXIT, "size_object: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); } break; case SUB_BINARY_SUBTAG: @@ -202,7 +202,7 @@ Uint size_object(Eterm obj) } break; case BIN_MATCHSTATE_SUBTAG: - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "size_object: matchstate term not allowed"); default: sum += thing_arityval(hdr) + 1; @@ -219,7 +219,7 @@ Uint size_object(Eterm obj) obj = ESTACK_POP(s); break; default: - erl_exit(ERTS_ABORT_EXIT, "size_object: bad tag for %#x\n", obj); + erts_exit(ERTS_ABORT_EXIT, "size_object: bad tag for %#x\n", obj); } } } @@ -272,7 +272,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) goto L_copy_list; case TAG_PRIMARY_BOXED: argp = &res; goto L_copy_boxed; default: - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s, line %d: Internal error in copy_struct: 0x%08x\n", __FILE__, __LINE__,obj); } @@ -331,7 +331,7 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) case TAG_PRIMARY_IMMED1: *tailp = obj; goto L_copy; case TAG_PRIMARY_BOXED: argp = tailp; goto L_copy_boxed; default: - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s, line %d: Internal error in copy_struct: 0x%08x\n", __FILE__, __LINE__,obj); } @@ -512,11 +512,11 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) *argp = make_hashmap_rel(tp, dst_base); break; default: - erl_exit(ERTS_ABORT_EXIT, "copy_struct: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); + erts_exit(ERTS_ABORT_EXIT, "copy_struct: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr)); } break; case BIN_MATCHSTATE_SUBTAG: - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "copy_struct: matchstate term not allowed"); default: i = thing_arityval(hdr)+1; @@ -540,13 +540,13 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) #ifdef DEBUG if (htop != hbot) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Internal error in copy_struct() when copying %T:" " htop=%p != hbot=%p (sz=%beu)\n", org_obj, htop, hbot, org_sz); #else if (htop > hbot) { - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Internal error in copy_struct(): htop, hbot overrun\n"); } #endif diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 7be2b77a3b..787241b960 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -1959,7 +1959,7 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx) goto done; } default: - erl_exit(ERTS_ABORT_EXIT, "dsig_send invalid phase (%d)\n", (int)ctx->phase); + erts_exit(ERTS_ABORT_EXIT, "dsig_send invalid phase (%d)\n", (int)ctx->phase); } } @@ -1980,7 +1980,7 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); if (size > (Uint) INT_MAX) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Absurdly large distribution output data buffer " "(%beu bytes) passed.\n", size); @@ -2020,7 +2020,7 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); if (size > (Uint) INT_MAX) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Absurdly large distribution output data buffer " "(%beu bytes) passed.\n", size); @@ -3382,7 +3382,7 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas continue; break; default: - erl_exit(ERTS_ABORT_EXIT, "Bad node type found\n"); + erts_exit(ERTS_ABORT_EXIT, "Bad node type found\n"); } } @@ -3691,7 +3691,7 @@ erts_processes_monitoring_nodes(Process *c_p) case ERTS_NODES_MON_OPT_TYPES: type = am_all; break; case ERTS_NODES_MON_OPT_TYPE_VISIBLE: type = am_visible; break; case ERTS_NODES_MON_OPT_TYPE_HIDDEN: type = am_hidden; break; - default: erl_exit(ERTS_ABORT_EXIT, "Bad node type found\n"); + default: erts_exit(ERTS_ABORT_EXIT, "Bad node type found\n"); } olist = erts_bld_cons(hpp, szp, erts_bld_tuple(hpp, szp, 2, diff --git a/erts/emulator/beam/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c index 47dafa53c0..4b0541c10e 100644 --- a/erts/emulator/beam/erl_afit_alloc.c +++ b/erts/emulator/beam/erl_afit_alloc.c @@ -241,7 +241,7 @@ info_options(Allctr_t *allctr, if (hpp || szp) { if (!atoms_initialized) - erl_exit(1, "%s:%d: Internal error: Atoms not initialized", + erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error: Atoms not initialized", __FILE__, __LINE__);; res = NIL; diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index c3f4fe5a63..0d88dfa363 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -653,11 +653,11 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0) { int err = errno; char *errstr = err ? strerror(err) : "unknown"; - erl_exit(-1, "Failed to lock physical memory: %s (%d)\n", + erts_exit(1, "Failed to lock physical memory: %s (%d)\n", errstr, err); } #else - erl_exit(-1, "Failed to lock physical memory: Not supported\n"); + erts_exit(1, "Failed to lock physical memory: Not supported\n"); #endif } @@ -806,13 +806,13 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) { if (!erts_allctrs[i].alloc) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Missing alloc function for %s\n", ERTS_ALC_A2AD(i)); if (!erts_allctrs[i].realloc) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Missing realloc function for %s\n", ERTS_ALC_A2AD(i)); if (!erts_allctrs[i].free) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Missing free function for %s\n", ERTS_ALC_A2AD(i)); } @@ -890,7 +890,7 @@ erts_alloc_late_init(void) static void * erts_realloc_fixed_size(ErtsAlcType_t type, void *extra, void *p, Uint size) { - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Attempt to reallocate a block of the fixed size type %s\n", ERTS_ALC_T2TD(type)); } @@ -1012,7 +1012,7 @@ start_au_allocator(ErtsAlcType_t alctr_n, * tspec->size) + ERTS_CACHE_LINE_SIZE - 1)); if (!states) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Failed to allocate allocator states for %salloc\n", init->init.util.name_prefix); tspec->allctr = (Allctr_t **) states; @@ -1040,7 +1040,7 @@ start_au_allocator(ErtsAlcType_t alctr_n, (tot_fix_list_size + ERTS_CACHE_LINE_SIZE - 1)); if (!fix_lists) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Failed to allocate fix lists for %salloc\n", init->init.util.name_prefix); @@ -1114,7 +1114,7 @@ start_au_allocator(ErtsAlcType_t alctr_n, } if (!as) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Failed to start %salloc\n", init->init.util.name_prefix); ASSERT(as == (void *) as0); @@ -1909,7 +1909,7 @@ erts_alc_fatal_error(int error, int func, ErtsAlcType_t n, ...) case ERTS_ALC_O_FREE: op_str = "free"; break; default: op_str = "UNKNOWN"; break; } - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s: %s operation not supported (memory type: \"%s\")\n", allctr_str, op_str, t_str); break; @@ -1923,18 +1923,18 @@ erts_alc_fatal_error(int error, int func, ErtsAlcType_t n, ...) va_start(argp, n); size = va_arg(argp, Uint); va_end(argp); - erl_exit(1, + erts_exit(ERTS_ERROR_EXIT, "%s: Cannot %s %lu bytes of memory (of type \"%s\").\n", allctr_str, op, size, t_str); break; } case ERTS_ALC_E_NOALLCTR: - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "erts_alloc: Unknown allocator type: %d\n", ERTS_ALC_T2A(ERTS_ALC_N2T(n))); break; default: - erl_exit(ERTS_ABORT_EXIT, "erts_alloc: Unknown error: %d\n", error); + erts_exit(ERTS_ABORT_EXIT, "erts_alloc: Unknown error: %d\n", error); break; } } @@ -3189,7 +3189,7 @@ reply_alloc_info(void *vair) make_small(0), ainfo); } else { - erl_exit(ERTS_ABORT_EXIT, "%s:%d: internal error\n", + erts_exit(ERTS_ABORT_EXIT, "%s:%d: internal error\n", __FILE__, __LINE__); } } @@ -3426,7 +3426,7 @@ void *safe_realloc(void *ptr, Uint sz) * Keep alloc_SUITE_data/allocator_test.h updated if changes are made * * to erts_alc_test() * \* */ -#define ERTS_ALC_TEST_ABORT erl_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error\n") +#define ERTS_ALC_TEST_ABORT erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error\n") UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3) { @@ -3888,7 +3888,7 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func) found_type = GET_TYPE_OF_PATTERN(pre_pattern); if (pre_pattern != MK_PATTERN(n)) { if ((FIXED_FENCE_PATTERN_MASK & pre_pattern) != FIXED_FENCE_PATTERN) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "ERROR: Fence at beginning of memory block (p=0x%u) " "clobbered.\n", (UWord) ptr); @@ -3905,12 +3905,12 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func) char *op_str; if ((FIXED_FENCE_PATTERN_MASK & post_pattern) != FIXED_FENCE_PATTERN) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "ERROR: Fence at end of memory block (p=0x%u, sz=%u) " "clobbered.\n", (UWord) ptr, (UWord) sz); if (found_type != GET_TYPE_OF_PATTERN(post_pattern)) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "ERROR: Fence around memory block (p=0x%u, sz=%u) " "clobbered.\n", (UWord) ptr, (UWord) sz); @@ -3933,7 +3933,7 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func) default: op_str = "???"; break; } - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "ERROR: Memory block (p=0x%u, sz=%u) allocated as type \"%s\"," " but %s as type \"%s\".\n", (UWord) ptr, (UWord) sz, ftype, op_str, otype); diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 8229a15824..18312eacde 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -4518,7 +4518,7 @@ make_name_atoms(Allctr_t *allctr) size_t prefix_len = strlen(allctr->name_prefix); if (prefix_len > MAX_ATOM_CHARACTERS + sizeof(realloc) - 1) - erl_exit(1,"Too long allocator name: %salloc\n",allctr->name_prefix); + erts_exit(ERTS_ERROR_EXIT,"Too long allocator name: %salloc\n",allctr->name_prefix); memcpy((void *) buf, (void *) allctr->name_prefix, prefix_len); @@ -5720,7 +5720,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) /* erts_alcu_start assumes that allctr has been zeroed */ if (((UWord)allctr & ERTS_CRR_ALCTR_FLG_MASK) != 0) { - erl_exit(ERTS_ABORT_EXIT, "%s:%d:erts_alcu_start: Alignment error\n", + erts_exit(ERTS_ABORT_EXIT, "%s:%d:erts_alcu_start: Alignment error\n", __FILE__, __LINE__); } @@ -5904,7 +5904,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) if (allctr->thread_safe) erts_mtx_destroy(&allctr->mutex); #endif - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Failed to create main carrier for %salloc\n", init->name_prefix); } @@ -6120,7 +6120,7 @@ erts_alcu_verify_unused(Allctr_t *allctr) if (no) { UWord sz = allctr->sbcs.blocks.curr.size; sz += allctr->mbcs.blocks.curr.size; - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%salloc() used when expected to be unused!\n" "Total amount of blocks allocated: %bpu\n" "Total amount of bytes allocated: %bpu\n", diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c index 19420af8ab..5cd067ff54 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.c +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c @@ -1035,7 +1035,7 @@ info_options(Allctr_t *allctr, if (hpp || szp) { if (!atoms_initialized) - erl_exit(1, "%s:%d: Internal error: Atoms not initialized", + erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error: Atoms not initialized", __FILE__, __LINE__);; res = NIL; diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c index fb853b65ab..f39a18ac88 100644 --- a/erts/emulator/beam/erl_bestfit_alloc.c +++ b/erts/emulator/beam/erl_bestfit_alloc.c @@ -940,7 +940,7 @@ info_options(Allctr_t *allctr, if (hpp || szp) { if (!atoms_initialized) - erl_exit(1, "%s:%d: Internal error: Atoms not initialized", + erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error: Atoms not initialized", __FILE__, __LINE__);; res = NIL; diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index 28bec6325c..f4ef59993a 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -1309,7 +1309,7 @@ static Eterm notify_when_loaded(Process *p, Eterm name_term, char *name, ErtsPro case ERL_DE_FORCE_RELOAD: break; default: - erl_exit(1,"Internal error, unknown state %u in dynamic driver.", drv->handle->status); + erts_exit(ERTS_ERROR_EXIT,"Internal error, unknown state %u in dynamic driver.", drv->handle->status); } p->flags |= F_USING_DDLL; r = add_monitor(p, drv->handle, ERL_DE_PROC_AWAIT_LOAD); diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b44382cde8..0341b809db 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -914,7 +914,7 @@ BIF_RETTYPE process_info_1(BIF_ALIST_1) case ERTS_PI_FAIL_TYPE_AWAIT_EXIT: ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined); default: - erl_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error", __FILE__, __LINE__); + erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error", __FILE__, __LINE__); } } @@ -954,7 +954,7 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2) case ERTS_PI_FAIL_TYPE_AWAIT_EXIT: ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined); default: - erl_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error", + erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error", __FILE__, __LINE__); } } @@ -1759,7 +1759,7 @@ info_1_tuple(Process* BIF_P, /* Pointer to current process. */ return res; buf = (char *) erts_alloc(ERTS_ALC_T_TMP, len+1); if (intlist_to_buf(*tp, buf, len) != len) - erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__); + erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error\n", __FILE__, __LINE__); buf[len] = '\0'; res = erts_instr_dump_memory_map(buf) ? am_true : am_false; erts_free(ERTS_ALC_T_TMP, (void *) buf); @@ -1778,7 +1778,7 @@ info_1_tuple(Process* BIF_P, /* Pointer to current process. */ return res; buf = (char *) erts_alloc(ERTS_ALC_T_TMP, len+1); if (intlist_to_buf(tp[1], buf, len) != len) - erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__); + erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error\n", __FILE__, __LINE__); buf[len] = '\0'; res = erts_instr_dump_stat(buf, 1) ? am_true : am_false; erts_free(ERTS_ALC_T_TMP, (void *) buf); @@ -3765,7 +3765,7 @@ static void broken_halt_test(Eterm bif_arg_2) #if defined(ERTS_HAVE_TRY_CATCH) erts_get_scheduler_data()->run_queue = NULL; #endif - erl_exit(ERTS_DUMP_EXIT, "%T", bif_arg_2); + erts_exit(ERTS_DUMP_EXIT, "%T", bif_arg_2); } @@ -4012,7 +4012,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) BIF_RET(am_true); } else if (ERTS_IS_ATOM_STR("abort", BIF_ARG_1)) { - erl_exit(ERTS_ABORT_EXIT, "%T\n", BIF_ARG_2); + erts_exit(ERTS_ABORT_EXIT, "%T\n", BIF_ARG_2); } else if (ERTS_IS_ATOM_STR("kill_dist_connection", BIF_ARG_1)) { DistEntry *dep = erts_sysname_to_connected_dist_entry(BIF_ARG_2); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index e47d7bcbbb..27c24197ea 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -791,7 +791,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) } else { name_buf = (char *) erts_alloc(ERTS_ALC_T_TMP, i + 1); if (intlist_to_buf(name, name_buf, i) != i) - erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__); + erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error\n", __FILE__, __LINE__); name_buf[i] = '\0'; } driver = &vanilla_driver; @@ -1169,7 +1169,7 @@ static Eterm http_bld_uri(struct packet_callback_args* pca, return erts_bld_tuple(hpp, szp, 3, am_scheme, s1, s2); default: - erl_exit(1, "%s, line %d: type=%u\n", __FILE__, __LINE__, uri->type); + erts_exit(ERTS_ERROR_EXIT, "%s, line %d: type=%u\n", __FILE__, __LINE__, uri->type); } } diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c index 8395f6ecc6..c263eebfcc 100644 --- a/erts/emulator/beam/erl_cpu_topology.c +++ b/erts/emulator/beam/erl_cpu_topology.c @@ -402,7 +402,7 @@ cpu_bind_order_sort(erts_cpu_topology_t *cpudata, break; default: cmp_func = NULL; - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Bad cpu bind type: %d\n", (int) cpu_bind_order); break; @@ -1590,7 +1590,7 @@ get_cpu_topology_term(Process *c_p, int type) } break; default: - erl_exit(ERTS_ABORT_EXIT, "Bad cpu topology type: %d\n", type); + erts_exit(ERTS_ABORT_EXIT, "Bad cpu topology type: %d\n", type); break; } @@ -1967,7 +1967,7 @@ cpu_group_insert(erts_cpu_groups_map_t *map, ix = 0; } while (ix != start); - erl_exit(ERTS_ABORT_EXIT, "Reader groups map full\n"); + erts_exit(ERTS_ABORT_EXIT, "Reader groups map full\n"); } @@ -2290,7 +2290,7 @@ remove_cpu_groups(erts_cpu_groups_callback_t callback, void *arg) prev_cgm = cgm; } - erl_exit(ERTS_ABORT_EXIT, "Cpu groups not found\n"); + erts_exit(ERTS_ABORT_EXIT, "Cpu groups not found\n"); } static int @@ -2320,7 +2320,7 @@ cpu_groups_lookup(erts_cpu_groups_map_t *map, ix = 0; } while (ix != start); - erl_exit(ERTS_ABORT_EXIT, "Logical cpu id %d not found\n", logical); + erts_exit(ERTS_ABORT_EXIT, "Logical cpu id %d not found\n", logical); } static void diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 878ee32b47..83c7ba7bbc 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -1295,7 +1295,7 @@ BIF_RETTYPE ets_rename_2(BIF_ALIST_2) goto badarg; if (!remove_named_tab(tb, 1)) - erl_exit(1,"Could not find named tab %s", tb->common.id); + erts_exit(ERTS_ERROR_EXIT,"Could not find named tab %s", tb->common.id); tb->common.id = tb->common.the_name = BIF_ARG_2; @@ -1581,7 +1581,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2) BIF_P->common.id, make_small(slot)), 0) != DB_ERROR_NONE) { - erl_exit(1,"Could not update ets metadata."); + erts_exit(ERTS_ERROR_EXIT,"Could not update ets metadata."); } db_meta_unlock(meta_pid_to_tab, LCK_WRITE_REC); @@ -2940,7 +2940,7 @@ void init_db(ErtsDbSpinCount db_spin_count) 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).", + erts_exit(ERTS_ERROR_EXIT,"Max limit for ets tabled too high %u (max %u).", db_max_tabs, ((Uint)1)<common.slot)), 0) != DB_ERROR_NONE) { UnUseTmpHeap(3,p); - erl_exit(1,"Could not insert ets metadata in safe_fixtable."); + erts_exit(ERTS_ERROR_EXIT,"Could not insert ets metadata in safe_fixtable."); } UnUseTmpHeap(3,p); db_meta_unlock(meta_pid_to_fixed_tab, LCK_WRITE_REC); diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 98a2e2842a..fa925c94a5 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -3028,7 +3028,7 @@ void db_check_table_hash(DbTable *tbl) if ((list = BUCKET(tb,j)) != 0) { while (list != 0) { if (!is_tuple(make_tuple(list->dbterm.tpl))) { - erl_exit(1, "Bad term in slot %d of ets table", j); + erts_exit(ERTS_ERROR_EXIT, "Bad term in slot %d of ets table", j); } list = list->next; } diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 465aa566ad..61293fbf9a 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -1213,7 +1213,7 @@ static int db_select_count_continue_tree(Process *p, tptr = tuple_val(continuation); if (arityval(*tptr) != 5) - erl_exit(1,"Internal error in ets:select_count/1"); + erts_exit(ERTS_ERROR_EXIT,"Internal error in ets:select_count/1"); lastkey = tptr[2]; end_condition = tptr[3]; diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index dab357a079..af5b611afd 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1946,11 +1946,11 @@ restart: #ifdef DMC_DEBUG if (*heap_fence != FENCE_PATTERN) { - erl_exit(1, "Heap fence overwritten in db_prog_match after op " + erts_exit(ERTS_ERROR_EXIT, "Heap fence overwritten in db_prog_match after op " "0x%08x, overwritten with 0x%08x.", save_op, *heap_fence); } if (*stack_fence != FENCE_PATTERN) { - erl_exit(1, "Stack fence overwritten in db_prog_match after op " + erts_exit(ERTS_ERROR_EXIT, "Stack fence overwritten in db_prog_match after op " "0x%08x, overwritten with 0x%08x.", save_op, *stack_fence); } @@ -2615,7 +2615,7 @@ restart: case matchHalt: goto success; default: - erl_exit(1, "Internal error: unexpected opcode in match program."); + erts_exit(ERTS_ERROR_EXIT, "Internal error: unexpected opcode in match program."); } } fail: @@ -2639,11 +2639,11 @@ success: #ifdef DMC_DEBUG if (*heap_fence != FENCE_PATTERN) { - erl_exit(1, "Heap fence overwritten in db_prog_match after op " + erts_exit(ERTS_ERROR_EXIT, "Heap fence overwritten in db_prog_match after op " "0x%08x, overwritten with 0x%08x.", save_op, *heap_fence); } if (*stack_fence != FENCE_PATTERN) { - erl_exit(1, "Stack fence overwritten in db_prog_match after op " + erts_exit(ERTS_ERROR_EXIT, "Stack fence overwritten in db_prog_match after op " "0x%08x, overwritten with 0x%08x.", save_op, *stack_fence); } @@ -3683,7 +3683,7 @@ static DMCRet dmc_one_term(DMCContext *context, break; } default: - erl_exit(1, "db_match_compile: " + erts_exit(ERTS_ERROR_EXIT, "db_match_compile: " "Bad object on heap: 0x%bex\n", c); } return retOk; @@ -4930,7 +4930,7 @@ static DMCRet dmc_fun(DMCContext *context, DMC_PUSH(*text, matchCall3); break; default: - erl_exit(1,"ets:match() internal error, " + erts_exit(ERTS_ERROR_EXIT,"ets:match() internal error, " "guard with more than 3 arguments."); } DMC_PUSH(*text, (UWord) b->biff); @@ -5210,7 +5210,7 @@ static Uint my_size_object(Eterm t) tmp == am_const) { sum += size_object(tuple_val(t)[2]); } else { - erl_exit(1,"Internal error, sizing unrecognized object in " + erts_exit(ERTS_ERROR_EXIT,"Internal error, sizing unrecognized object in " "(d)ets:match compilation."); } break; @@ -5255,7 +5255,7 @@ static Eterm my_copy_struct(Eterm t, Eterm **hp, ErlOffHeap* off_heap) sz = size_object(b); ret = copy_struct(b,sz,hp,off_heap); } else { - erl_exit(1, "Trying to constant-copy non constant expression " + erts_exit(ERTS_ERROR_EXIT, "Trying to constant-copy non constant expression " "0x%bex in (d)ets:match compilation.", t); } } else { diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index 2dcfb79f00..4928aae9c2 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -255,14 +255,14 @@ void erts_check_stack(Process *p) Eterm *stack_end = p->htop; if (p->stop > stack_start) - erl_exit(1, + erts_exit(ERTS_ERROR_EXIT, "<%lu.%lu.%lu>: Stack underflow\n", internal_pid_channel_no(p->common.id), internal_pid_number(p->common.id), internal_pid_serial(p->common.id)); if (p->stop < stack_end) - erl_exit(1, + erts_exit(ERTS_ERROR_EXIT, "<%lu.%lu.%lu>: Stack overflow\n", internal_pid_channel_no(p->common.id), internal_pid_number(p->common.id), @@ -287,7 +287,7 @@ void erts_check_stack(Process *p) if (in_mbuf) continue; - erl_exit(1, + erts_exit(ERTS_ERROR_EXIT, "<%lu.%lu.%lu>: Wild stack pointer\n", internal_pid_channel_no(p->common.id), internal_pid_number(p->common.id), @@ -372,7 +372,7 @@ void erts_check_memory(Process *p, Eterm *start, Eterm *end) #ifdef DEBUG if (hval == DEBUG_BAD_WORD) { print_untagged_memory(start, end); - erl_exit(1, "Uninitialized HAlloc'ed memory found @ 0x%0*lx!\n", + erts_exit(ERTS_ERROR_EXIT, "Uninitialized HAlloc'ed memory found @ 0x%0*lx!\n", PTR_SIZE,(unsigned long)(pos - 1)); } #endif @@ -385,7 +385,7 @@ void erts_check_memory(Process *p, Eterm *start, Eterm *end) if (verify_eterm(p,hval)) continue; - erl_exit(1, "Wild pointer found @ 0x%0*lx!\n", + erts_exit(ERTS_ERROR_EXIT, "Wild pointer found @ 0x%0*lx!\n", PTR_SIZE,(unsigned long)(pos - 1)); } } @@ -395,11 +395,11 @@ void verify_process(Process *p) #define VERIFY_AREA(name,ptr,sz) { \ int n = (sz); \ while (n--) if(!verify_eterm(p,*(ptr+n))) \ - erl_exit(1,"Wild pointer found in " name " of %T!\n",p->common.id); } + erts_exit(ERTS_ERROR_EXIT,"Wild pointer found in " name " of %T!\n",p->common.id); } #define VERIFY_ETERM(name,eterm) { \ if(!verify_eterm(p,eterm)) \ - erl_exit(1,"Wild pointer found in " name " of %T!\n",p->common.id); } + erts_exit(ERTS_ERROR_EXIT,"Wild pointer found in " name " of %T!\n",p->common.id); } ErlMessage* mp = p->msg.first; diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index e0404eb5c9..184f8e8931 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -43,7 +43,7 @@ fatal_error(int err, char *func) else estr = "Unknown error"; } - erl_exit(ERTS_ABORT_EXIT, "Fatal error in %s: %s [%d]\n", func, estr, err); + erts_exit(ERTS_ABORT_EXIT, "Fatal error in %s: %s [%d]\n", func, estr, err); } #define ERL_DRV_TSD_KEYS_INC 10 diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c index 4268e2d40a..2f13aa364f 100644 --- a/erts/emulator/beam/erl_fun.c +++ b/erts/emulator/beam/erl_fun.c @@ -185,7 +185,7 @@ erts_erase_fun_entry(ErlFunEntry* fe) #endif { if (fe->address != unloaded_fun) - erl_exit(1, + erts_exit(ERTS_ERROR_EXIT, "Internal error: " "Invalid reference count found on #Fun<%T.%d.%d>: " " About to erase fun still referred by code.\n", diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 2f21111a2e..f48c46ca33 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -59,7 +59,7 @@ erts_fprintf(stderr, "stop=%p\n", (p)->stop); \ erts_fprintf(stderr, "htop=%p\n", (p)->htop); \ erts_fprintf(stderr, "heap=%p\n", (p)->heap); \ - erl_exit(ERTS_ABORT_EXIT, "%s, line %d: %T: Overrun stack and heap\n", \ + erts_exit(ERTS_ABORT_EXIT, "%s, line %d: %T: Overrun stack and heap\n", \ __FILE__,__LINE__,(P)->common.id); \ } @@ -268,7 +268,7 @@ erts_next_heap_size(Uint size, Uint offset) low = mid + 1; } } - erl_exit(1, "no next heap size found: %beu, offset %beu\n", size, offset); + erts_exit(ERTS_ERROR_EXIT, "no next heap size found: %beu, offset %beu\n", size, offset); } return 0; } @@ -2788,7 +2788,7 @@ within(Eterm *ptr, Process *p) #define ERTS_CHK_OFFHEAP_ASSERT(EXP) \ do { \ if (!(EXP)) \ - erl_exit(ERTS_ABORT_EXIT, \ + erts_exit(ERTS_ABORT_EXIT, \ "%s:%d: Assertion failed: %s\n", \ __FILE__, __LINE__, #EXP); \ } while (0) diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c index f89f8723d9..9b4aad9d91 100644 --- a/erts/emulator/beam/erl_goodfit_alloc.c +++ b/erts/emulator/beam/erl_goodfit_alloc.c @@ -571,8 +571,8 @@ info_options(Allctr_t *allctr, if (hpp || szp) { if (!atoms_initialized) - erl_exit(1, "%s:%d: Internal error: Atoms not initialized", - __FILE__, __LINE__);; + erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error: Atoms not initialized", + __FILE__, __LINE__); res = NIL; add_2tup(hpp, szp, &res, am.as, am.gf); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index d9c3b0dcf4..e729574ec7 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -269,7 +269,7 @@ this_rel_num(void) i++; this_rel = atoi(&this_rel_str[i]); if (this_rel < 1) - erl_exit(-1, "Unexpected ERLANG_OTP_RELEASE format\n"); + erts_exit(1, "Unexpected ERLANG_OTP_RELEASE format\n"); } return this_rel; } @@ -415,7 +415,7 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** start_mod = erts_atom_put((byte *) modname, sys_strlen(modname), ERTS_ATOM_ENC_LATIN1, 1); if (erts_find_function(start_mod, am_start, 2, erts_active_code_ix()) == NULL) { - erl_exit(5, "No function %s:start/2\n", modname); + erts_exit(ERTS_ERROR_EXIT, "No function %s:start/2\n", modname); } /* @@ -512,12 +512,12 @@ load_preloaded(void) length = preload_p[i].size; module_name = erts_atom_put((byte *) name, sys_strlen(name), ERTS_ATOM_ENC_LATIN1, 1); if ((code = sys_preload_begin(&preload_p[i])) == 0) - erl_exit(1, "Failed to find preloaded code for module %s\n", + erts_exit(ERTS_ERROR_EXIT, "Failed to find preloaded code for module %s\n", name); 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", + erts_exit(ERTS_ERROR_EXIT,"Failed loading preloaded module %s (%T)\n", name, res); i++; } @@ -648,7 +648,7 @@ void erts_usage(void) erts_fprintf(stderr, "Note that if the emulator is started with erlexec (typically\n"); erts_fprintf(stderr, "from the erl script), these flags should be specified with +.\n"); erts_fprintf(stderr, "\n\n"); - erl_exit(-1, ""); + erts_exit(1, ""); } #ifdef USE_THREADS @@ -1445,7 +1445,7 @@ erl_start(int argc, char **argv) } erts_fprintf(stderr, "(" EMULATOR ") emulator version " ERLANG_VERSION "\n"); - erl_exit(0, ""); + erts_exit(0, ""); } break; @@ -2247,23 +2247,17 @@ system_cleanup(int flush_async) } static __decl_noreturn void __noreturn -erl_exit_vv(int n, int flush_async, char *fmt, va_list args1, va_list args2) +erts_exit_vv(int n, int flush_async, char *fmt, va_list args1, va_list args2) { - unsigned int an; - system_cleanup(flush_async); save_statistics(); - if (n < 0) - an = -(unsigned int)n; - else - an = n; if (erts_mtrace_enabled) - erts_mtrace_exit((Uint32) an); + erts_mtrace_exit((Uint32) n); /* Produce an Erlang core dump if error */ - if (((n > 0 && erts_no_crash_dump == 0) || n == ERTS_DUMP_EXIT) + if (((n == ERTS_ERROR_EXIT && erts_no_crash_dump == 0) || n == ERTS_DUMP_EXIT) && erts_initialized) { erl_crash_dump_v((char*) NULL, 0, fmt, args1); } @@ -2276,29 +2270,29 @@ erl_exit_vv(int n, int flush_async, char *fmt, va_list args1, va_list args2) exit(0); else if (n == ERTS_DUMP_EXIT) ERTS_EXIT_AFTER_DUMP(1); - else if (n > 0 || n == ERTS_ABORT_EXIT) + else if (n == ERTS_ERROR_EXIT || n == ERTS_ABORT_EXIT) abort(); - exit(an); + exit(n); } /* Exit without flushing async threads */ -__decl_noreturn void __noreturn erl_exit(int n, char *fmt, ...) +__decl_noreturn void __noreturn erts_exit(int n, char *fmt, ...) { va_list args1, args2; va_start(args1, fmt); va_start(args2, fmt); - erl_exit_vv(n, 0, fmt, args1, args2); + erts_exit_vv(n, 0, fmt, args1, args2); va_end(args2); va_end(args1); } /* Exit after flushing async threads */ -__decl_noreturn void __noreturn erl_exit_flush_async(int n, char *fmt, ...) +__decl_noreturn void __noreturn erts_flush_async_exit(int n, char *fmt, ...) { va_list args1, args2; va_start(args1, fmt); va_start(args2, fmt); - erl_exit_vv(n, 1, fmt, args1, args2); + erts_exit_vv(n, 1, fmt, args1, args2); va_end(args2); va_end(args1); } diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index ff2a355309..3c066cea7b 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -1274,7 +1274,7 @@ recurse: break; } default: - erl_exit(ERTS_ABORT_EXIT, "bad header %ld\r\n", hdrA); + erts_exit(ERTS_ABORT_EXIT, "bad header %ld\r\n", hdrA); } } @@ -1301,7 +1301,7 @@ recurse: break; } default: - erl_exit(ERTS_ABORT_EXIT, "bad header %ld\r\n", hdrB); + erts_exit(ERTS_ABORT_EXIT, "bad header %ld\r\n", hdrB); } } } @@ -1391,7 +1391,7 @@ resume_from_trap: res = make_boxed(nhp); break; default: - erl_exit(ERTS_ABORT_EXIT, "strange mix %d\r\n", sp->mix); + erts_exit(ERTS_ABORT_EXIT, "strange mix %d\r\n", sp->mix); } } @@ -1886,7 +1886,7 @@ void hashmap_iterator_init(ErtsWStack* s, Eterm node, int reverse) { sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); break; default: - erl_exit(ERTS_ABORT_EXIT, "bad header"); + erts_exit(ERTS_ABORT_EXIT, "bad header"); } WSTACK_PUSH3((*s), (UWord)THE_NON_VALUE, /* end marker */ @@ -1923,7 +1923,7 @@ Eterm* hashmap_iterator_next(ErtsWStack* s) { ASSERT(sz < 17); break; default: - erl_exit(ERTS_ABORT_EXIT, "bad header"); + erts_exit(ERTS_ABORT_EXIT, "bad header"); } idx++; @@ -1973,7 +1973,7 @@ Eterm* hashmap_iterator_prev(ErtsWStack* s) { ASSERT(sz < 17); break; default: - erl_exit(1, "bad header"); + erts_exit(ERTS_ERROR_EXIT, "bad header"); } if (idx > sz) @@ -2161,12 +2161,12 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, size += HAMT_HEAD_BITMAP_SZ(n+1); goto unroll; default: - erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); + erts_exit(ERTS_ERROR_EXIT, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); break; } break; default: - erl_exit(1, "bad primary tag %p\r\n", node); + erts_exit(ERTS_ERROR_EXIT, "bad primary tag %p\r\n", node); break; } } @@ -2281,12 +2281,12 @@ Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, res = make_hashmap(nhp); break; default: - erl_exit(1, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); + erts_exit(ERTS_ERROR_EXIT, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); break; } break; default: - erl_exit(1, "bad primary tag %x\r\n", primary_tag(node)); + erts_exit(ERTS_ERROR_EXIT, "bad primary tag %x\r\n", primary_tag(node)); break; } @@ -2404,12 +2404,12 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) { /* not occupied */ goto not_found; default: - erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); + erts_exit(ERTS_ERROR_EXIT, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); break; } break; default: - erl_exit(1, "bad primary tag %p\r\n", node); + erts_exit(ERTS_ERROR_EXIT, "bad primary tag %p\r\n", node); break; } } @@ -2586,7 +2586,7 @@ unroll: res = make_hashmap(nhp); break; default: - erl_exit(1, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); + erts_exit(ERTS_ERROR_EXIT, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); break; } } while(!ESTACK_ISEMPTY(stack)); @@ -2727,7 +2727,7 @@ BIF_RETTYPE erts_internal_map_type_1(BIF_ALIST_1) { case HAMT_SUBTAG_NODE_BITMAP: BIF_RET(AM_hashmap_node); default: - erl_exit(1, "bad header"); + erts_exit(ERTS_ERROR_EXIT, "bad header"); } } BIF_P->fvalue = BIF_ARG_1; @@ -2763,7 +2763,7 @@ BIF_RETTYPE erts_internal_map_hashmap_children_1(BIF_ALIST_1) { ptr += 2; break; default: - erl_exit(1, "bad header\r\n"); + erts_exit(ERTS_ERROR_EXIT, "bad header\r\n"); break; } ASSERT(sz < 17); @@ -2841,7 +2841,7 @@ static Eterm hashmap_info(Process *p, Eterm node) { } break; default: - erl_exit(1, "bad header\r\n"); + erts_exit(ERTS_ERROR_EXIT, "bad header\r\n"); break; } } diff --git a/erts/emulator/beam/erl_monitors.c b/erts/emulator/beam/erl_monitors.c index 7dfa01c8ac..bd899fa2f9 100644 --- a/erts/emulator/beam/erl_monitors.c +++ b/erts/emulator/beam/erl_monitors.c @@ -356,7 +356,7 @@ void erts_add_monitor(ErtsMonitor **root, Uint type, Eterm ref, Eterm pid, tstack[tpos++] = this; this = &((*this)->right); } else { /* Equal key is an error for monitors */ - erl_exit(1,"Insertion of already present monitor!"); + erts_exit(ERTS_ERROR_EXIT,"Insertion of already present monitor!"); break; } } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index d7a2076d85..3e93f9e514 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -330,7 +330,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, #ifdef ERTS_SMP c_p = NULL; #else - erl_exit(ERTS_ABORT_EXIT,"enif_send: env==NULL on non-SMP VM"); + erts_exit(ERTS_ABORT_EXIT,"enif_send: env==NULL on non-SMP VM"); #endif } diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 707de39556..e69bd49c42 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -1050,7 +1050,7 @@ insert_dist_entry(DistEntry *dist, int type, Eterm id, Uint creation) } if(!rdp) - erl_exit(1, + erts_exit(ERTS_ERROR_EXIT, "Reference to non-existing distribution table entry found!\n"); insert_dist_referrer(rdp, type, id, creation); @@ -1111,7 +1111,7 @@ insert_node(ErlNode *node, int type, Eterm id) } if (!rnp) - erl_exit(1, "Reference to non-existing node table entry found!\n"); + erts_exit(ERTS_ERROR_EXIT, "Reference to non-existing node table entry found!\n"); insert_node_referrer(rnp, type, id); } diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 5c38db1cbc..ddcca06b0a 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -1784,7 +1784,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) reds = erts_dist_command(pp, CONTEXT_REDS - pp->reds); break; default: - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Invalid port task type: %d\n", (int) ptp->type); break; @@ -2048,7 +2048,7 @@ begin_port_cleanup(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p) break; } default: - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Invalid port task type: %d\n", (int) ptp->type); } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index d583118e7b..12687deaeb 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -515,7 +515,7 @@ dbg_chk_aux_work_val(erts_aint32_t value) valid |= ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED; if (~valid & value) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Invalid aux_work value found: 0x%x\n", ~valid & value); } @@ -1211,7 +1211,7 @@ erts_sched_finish_poke(ErtsSchedulerSleepInfo *ssi, erts_aint32_t flags) case 0: break; default: - erl_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error\n", + erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error\n", __FILE__, __LINE__); break; } @@ -2195,7 +2195,7 @@ handle_reap_ports(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) erts_port_release(prt); } if (erts_smp_atomic32_dec_read_nob(&erts_halt_progress) == 0) { - erl_exit_flush_async(erts_halt_code, ""); + erts_flush_async_exit(erts_halt_code, ""); } } return aux_work & ~ERTS_SSI_AUX_WORK_REAP_PORTS; @@ -3717,7 +3717,7 @@ immigrate(ErtsRunQueue *c_rq, ErtsMigrationPath *mp) prio = ERTS_PORT_PRIO_LEVEL; break; default: - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d:%s(): Invalid immigrate queue mask", __FILE__, __LINE__, __func__); prio = 0; @@ -3741,7 +3741,7 @@ immigrate(ErtsRunQueue *c_rq, ErtsMigrationPath *mp) rq = erts_port_runq(prt); if (rq) { if (rq != c_rq) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d:%s(): Internal error", __FILE__, __LINE__, __func__); erts_enqueue_port(c_rq, prt); @@ -3932,7 +3932,7 @@ evacuate_run_queue(ErtsRunQueue *rq, prt_rq = erts_port_runq(prt); if (prt_rq) { if (prt_rq != to_rq) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d:%s() internal error\n", __FILE__, __LINE__, __func__); erts_enqueue_port(to_rq, prt); @@ -4133,7 +4133,7 @@ no_procs: return 0; else { if (prt_rq != rq) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d:%s() internal error\n", __FILE__, __LINE__, __func__); *rq_lockedp = 1; @@ -8020,7 +8020,7 @@ sched_thread_func(void *vesdp) process_main(); /* No schedulers should *ever* terminate */ - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Scheduler thread number %beu terminated\n", no); return NULL; @@ -8085,7 +8085,7 @@ sched_dirty_cpu_thread_func(void *vesdp) process_main(); /* No schedulers should *ever* terminate */ - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Dirty CPU scheduler thread number %beu terminated\n", no); return NULL; @@ -8148,7 +8148,7 @@ sched_dirty_io_thread_func(void *vesdp) process_main(); /* No schedulers should *ever* terminate */ - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Dirty I/O scheduler thread number %beu terminated\n", no); return NULL; @@ -8178,12 +8178,12 @@ erts_start_schedulers(void) erts_snprintf(opts.name, 16, "runq_supervisor"); erts_atomic_init_nob(&runq_supervisor_sleeping, 0); if (0 != ethr_event_init(&runq_supervision_event)) - erl_exit(1, "Failed to create run-queue supervision event\n"); + erts_exit(ERTS_ERROR_EXIT, "Failed to create run-queue supervision event\n"); if (0 != ethr_thr_create(&runq_supervisor_tid, runq_supervisor, NULL, &opts)) - erl_exit(1, "Failed to create run-queue supervision thread\n"); + erts_exit(ERTS_ERROR_EXIT, "Failed to create run-queue supervision thread\n"); } #endif @@ -8227,14 +8227,14 @@ erts_start_schedulers(void) erts_snprintf(opts.name, 16, "%d_dirty_cpu_scheduler", ix + 1); res = ethr_thr_create(&esdp->tid,sched_dirty_cpu_thread_func,(void*)esdp,&opts); if (res != 0) - erl_exit(1, "Failed to create dirty cpu scheduler thread %d\n", ix); + erts_exit(ERTS_ERROR_EXIT, "Failed to create dirty cpu scheduler thread %d\n", ix); } for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix); erts_snprintf(opts.name, 16, "%d_dirty_io_scheduler", ix + 1); res = ethr_thr_create(&esdp->tid,sched_dirty_io_thread_func,(void*)esdp,&opts); if (res != 0) - erl_exit(1, "Failed to create dirty io scheduler thread %d\n", ix); + erts_exit(ERTS_ERROR_EXIT, "Failed to create dirty io scheduler thread %d\n", ix); } } #endif @@ -8250,10 +8250,10 @@ erts_start_schedulers(void) res = ethr_thr_create(&aux_tid, aux_thread, NULL, &opts); if (res != 0) - erl_exit(1, "Failed to create aux thread\n"); + erts_exit(ERTS_ERROR_EXIT, "Failed to create aux thread\n"); if (actual < 1) - erl_exit(1, + erts_exit(ERTS_ERROR_EXIT, "Failed to create any scheduler-threads: %s (%d)\n", erl_errno_id(res), res); @@ -12092,7 +12092,7 @@ static void doit_exit_link(ErtsLink *lnk, void *vpcontext) break; default: - erl_exit(1, "bad type in link list\n"); + erts_exit(ERTS_ERROR_EXIT, "bad type in link list\n"); break; } erts_destroy_link(lnk); @@ -12133,7 +12133,7 @@ erts_do_exit_process(Process* p, Eterm reason) #endif if (p->static_flags & ERTS_STC_FLG_SYSTEM_PROC) - erl_exit(ERTS_DUMP_EXIT, "System process %T terminated: %T\n", + erts_exit(ERTS_DUMP_EXIT, "System process %T terminated: %T\n", p->common.id, reason); #ifdef ERTS_SMP @@ -12251,7 +12251,7 @@ erts_continue_exit_process(Process *p) break; case ERTS_SCHDLR_SSPND_EINVAL: default: - erl_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: %d\n", + erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: %d\n", __FILE__, __LINE__, (int) ssr); } } @@ -12748,10 +12748,10 @@ erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp) { * The same global atomic is used as refcount. * * A BIF that calls this should make sure to schedule out to never come back: - * erl_halt((int)(- code)); + * erts_halt(code); * ERTS_BIF_YIELD1(bif_export[BIF_erlang_halt_1], BIF_P, NIL); */ -void erl_halt(int code) +void erts_halt(int code) { if (-1 == erts_smp_atomic32_cmpxchg_acqb(&erts_halt_progress, erts_no_schedulers, diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 10c6fa4a67..a938e1d884 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -2303,6 +2303,6 @@ erts_sched_poke(ErtsSchedulerSleepInfo *ssi) #endif -void erl_halt(int code); +void erts_halt(int code); extern erts_smp_atomic32_t erts_halt_progress; extern int erts_halt_code; diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 8606371bdf..3253ee6b70 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -374,7 +374,7 @@ static void pd_hash_erase(Process *p, Eterm id, Eterm *ret) "display term found in line %d:\n" "%T\n", p->common.id, __LINE__, old); #endif - erl_exit(1, "Damaged process dictionary found during erase/1."); + erts_exit(ERTS_ERROR_EXIT, "Damaged process dictionary found during erase/1."); } if ((range = HASH_RANGE(p->dictionary)) > INITIAL_SIZE && range / 2 > (p->dictionary->numElements)) { @@ -419,7 +419,7 @@ Eterm erts_pd_hash_get(Process *p, Eterm id) "display term found in line %d:\n" "%T\n", p->common.id, __LINE__, tmp); #endif - erl_exit(1, "Damaged process dictionary found during get/1."); + erts_exit(ERTS_ERROR_EXIT, "Damaged process dictionary found during get/1."); } return am_undefined; } @@ -670,7 +670,7 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) "%T\n", p->common.id, __LINE__, old); #endif - erl_exit(1, "Damaged process dictionary found during put/2."); + erts_exit(ERTS_ERROR_EXIT, "Damaged process dictionary found during put/2."); } if (HASH_RANGE(p->dictionary) <= p->dictionary->numElements) { grow(p); @@ -1024,7 +1024,7 @@ static void pd_check(ProcDict *pd) } continue; } else { - erl_exit(1, + erts_exit(ERTS_ERROR_EXIT, "Found tag 0x%08x in process dictionary at position %d", (unsigned long) t, (int) i); } diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c index f7997df051..9ed175fe01 100644 --- a/erts/emulator/beam/erl_ptab.c +++ b/erts/emulator/beam/erl_ptab.c @@ -1255,7 +1255,7 @@ ptab_list_bif_engine(Process *c_p, Eterm *res_accp, Binary *mbp) return 1; default: - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d:ptab_list_bif_engine(): Invalid state: %d\n", __FILE__, __LINE__, (int) ptlbdp->state); } diff --git a/erts/emulator/beam/erl_term.c b/erts/emulator/beam/erl_term.c index e5050bfaa5..bae3385d3f 100644 --- a/erts/emulator/beam/erl_term.c +++ b/erts/emulator/beam/erl_term.c @@ -41,7 +41,7 @@ et_abort(const char *expr, const char *file, unsigned line) * Prevent infinite loop. */ have_been_called = 1; - erl_exit(1, "TYPE ASSERTION FAILED, file %s, line %u: %s\n", file, line, expr); + erts_exit(ERTS_ERROR_EXIT, "TYPE ASSERTION FAILED, file %s, line %u: %s\n", file, line, expr); } #else erts_fprintf(stderr, "TYPE ASSERTION FAILED, file %s, line %u: %s\n", file, line, expr); diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 7148b756e7..7b06fd840f 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -502,7 +502,7 @@ erts_thr_progress_register_unmanaged_thread(ErtsThrPrgrCallbacks *callbacks) if (tpd) { if (!tpd->is_temporary) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d:%s(): Double register of thread\n", __FILE__, __LINE__, __func__); is_blocking = tpd->is_blocking; @@ -524,7 +524,7 @@ erts_thr_progress_register_unmanaged_thread(ErtsThrPrgrCallbacks *callbacks) #endif ASSERT(tpd->id >= 0); if (tpd->id >= intrnl->unmanaged.no) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d:%s(): Too many unmanaged registered threads\n", __FILE__, __LINE__, __func__); @@ -547,7 +547,7 @@ erts_thr_progress_register_managed_thread(ErtsSchedulerData *esdp, if (tpd) { if (!tpd->is_temporary) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d:%s(): Double register of thread\n", __FILE__, __LINE__, __func__); is_blocking = tpd->is_blocking; @@ -568,7 +568,7 @@ erts_thr_progress_register_managed_thread(ErtsSchedulerData *esdp, tpd->id = erts_atomic32_inc_read_nob(&intrnl->misc.data.managed_id); ASSERT(tpd->id >= 0); if (tpd->id >= intrnl->managed.no) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d:%s(): Too many managed registered threads\n", __FILE__, __LINE__, __func__); @@ -1033,7 +1033,7 @@ has_reached_wakeup(ErtsThrPrgrVal wakeup) limit += 1; if (!erts_thr_progress_has_passed__(limit, wakeup)) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Invalid wakeup request value found:" " current=%b64u, wakeup=%b64u, limit=%b64u", current, wakeup, limit); @@ -1102,7 +1102,7 @@ request_wakeup_managed(ErtsThrPrgrData *tpd, ErtsThrPrgrVal value) ix = erts_atomic32_inc_read_nob(&mwd->len) - 1; #if ERTS_THR_PRGR_DBG_CHK_WAKEUP_REQUEST_VALUE if (ix >= intrnl->managed.no) - erl_exit(ERTS_ABORT_EXIT, "Internal error: Too many wakeup requests\n"); + erts_exit(ERTS_ABORT_EXIT, "Internal error: Too many wakeup requests\n"); #endif mwd->id[ix] = tpd->id; diff --git a/erts/emulator/beam/erl_thr_queue.c b/erts/emulator/beam/erl_thr_queue.c index 3a91ca9dbe..7ff456b915 100644 --- a/erts/emulator/beam/erl_thr_queue.c +++ b/erts/emulator/beam/erl_thr_queue.c @@ -224,7 +224,7 @@ ErtsThrQCleanState_t erts_thr_q_destroy(ErtsThrQ_t *q) { if (!q->q.blk) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Trying to destroy not created thread queue\n"); return erts_thr_q_finalize(q); } @@ -589,7 +589,7 @@ enqueue(ErtsThrQ_t *q, void *data, ErtsThrQElement_t *this) #if ERTS_THR_Q_DBG_CHK_DATA if (!data) - erl_exit(ERTS_ABORT_EXIT, "Missing data in enqueue\n"); + erts_exit(ERTS_ABORT_EXIT, "Missing data in enqueue\n"); #endif ASSERT(!q->q.finalizing); @@ -771,7 +771,7 @@ erts_thr_q_dequeue(ErtsThrQ_t *q) #if ERTS_THR_Q_DBG_CHK_DATA head->data.ptr = NULL; if (!res) - erl_exit(ERTS_ABORT_EXIT, "Missing data in dequeue\n"); + erts_exit(ERTS_ABORT_EXIT, "Missing data in dequeue\n"); #endif clean(q, (q->head.deq_fini.automatic diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 7327e0b48c..b88a0a813d 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -286,7 +286,7 @@ read_corrected_time(int os_drift_corrected) ci = time_sup.inf.c.parmon.cdata.insts.curr; else { if (os_mtime < time_sup.inf.c.parmon.cdata.insts.prev.os_mtime) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "OS monotonic time stepped backwards\n"); ci = time_sup.inf.c.parmon.cdata.insts.prev; } @@ -378,7 +378,7 @@ check_time_correction(void *vesdp) erts_smp_rwmtx_runlock(&time_sup.inf.c.parmon.rwmtx); if (os_mtime < ci.os_mtime) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "OS monotonic time stepped backwards\n"); erl_mtime = calc_corrected_erl_mtime(os_mtime, &ci, &mdiff, @@ -795,7 +795,7 @@ finalize_corrected_time_offset(ErtsSystemTime *stimep) erts_smp_rwmtx_runlock(&time_sup.inf.c.parmon.rwmtx); if (os_mtime < ci.os_mtime) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "OS monotonic time stepped backwards\n"); return calc_corrected_erl_mtime(os_mtime, &ci, NULL, diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index e1b03a057f..214d8b641e 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -420,7 +420,7 @@ WRITE_SYS_MSG_TO_PORT(Eterm unused_to, erts_encode_ext(message, &ptr); if (!(ptr <= buffer+size)) { - erl_exit(1, "Internal error in do_send_to_port: %d\n", ptr-buffer); + erts_exit(ERTS_ERROR_EXIT, "Internal error in do_send_to_port: %d\n", ptr-buffer); } #ifndef ERTS_SMP @@ -1098,7 +1098,7 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, case SEQ_TRACE_PRINT: type_atom = am_print; break; case SEQ_TRACE_RECEIVE: type_atom = am_receive; break; default: - erl_exit(1, "invalid type in seq_trace_output_generic: %d:\n", type); + erts_exit(ERTS_ERROR_EXIT, "invalid type in seq_trace_output_generic: %d:\n", type); return; /* To avoid warning */ } diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index 551717139d..36d85b0a22 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -2507,7 +2507,7 @@ void erts_copy_utf8_to_utf16_little(byte *target, byte *bytes, int num_chars) ((Uint) (bytes[3] & ((byte) 0x3F))); bytes += 4; } else { - erl_exit(1,"Internal unicode error in prim_file:internal_name2native/1"); + erts_exit(ERTS_ERROR_EXIT,"Internal unicode error in prim_file:internal_name2native/1"); } *target++ = (byte) (unipoint & 0xFF); *target++ = (byte) ((unipoint >> 8) & 0xFF); diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index a85aa15403..ffe3303796 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -570,7 +570,7 @@ void erts_encode_ext(Eterm term, byte **ext) *ep++ = VERSION_MAGIC; ep = enc_term(NULL, term, ep, TERM_TO_BINARY_DFLAGS, NULL); if (!ep) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d:erts_encode_ext(): Internal data structure error\n", __FILE__, __LINE__); *ext = ep; @@ -1559,7 +1559,7 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar b2t_destroy_context(ctx); if (ctx->u.dc.factory.hp > ctx->u.dc.factory.hp_end) { - erl_exit(1, ":%s, line %d: heap overrun by %d words(s)\n", + erts_exit(ERTS_ERROR_EXIT, ":%s, line %d: heap overrun by %d words(s)\n", __FILE__, __LINE__, ctx->u.dc.factory.hp - ctx->u.dc.factory.hp_end); } erts_factory_close(&ctx->u.dc.factory); @@ -1708,12 +1708,12 @@ erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint fl if ((endp = enc_term(NULL, Term, bytes, flags, NULL)) == NULL) { - erl_exit(1, "%s, line %d: bad term: %x\n", + erts_exit(ERTS_ERROR_EXIT, "%s, line %d: bad term: %x\n", __FILE__, __LINE__, Term); } real_size = endp - bytes; if (real_size > size) { - erl_exit(1, "%s, line %d: buffer overflow: %d word(s)\n", + erts_exit(ERTS_ERROR_EXIT, "%s, line %d: buffer overflow: %d word(s)\n", __FILE__, __LINE__, real_size - size); } @@ -1753,12 +1753,12 @@ erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint fl bytes[0] = VERSION_MAGIC; if ((endp = enc_term(NULL, Term, bytes+1, flags, NULL)) == NULL) { - erl_exit(1, "%s, line %d: bad term: %x\n", + erts_exit(ERTS_ERROR_EXIT, "%s, line %d: bad term: %x\n", __FILE__, __LINE__, Term); } real_size = endp - bytes; if (real_size > size) { - erl_exit(1, "%s, line %d: buffer overflow: %d word(s)\n", + erts_exit(ERTS_ERROR_EXIT, "%s, line %d: buffer overflow: %d word(s)\n", __FILE__, __LINE__, endp - (bytes + size)); } return erts_realloc_binary(bin, real_size); @@ -2650,7 +2650,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, ASSERT(node_sz < 17); break; default: - erl_exit(1, "bad header\r\n"); + erts_exit(ERTS_ERROR_EXIT, "bad header\r\n"); } ptr++; @@ -4110,7 +4110,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, ASSERT(node_sz < 17); break; default: - erl_exit(1, "bad header\r\n"); + erts_exit(ERTS_ERROR_EXIT, "bad header\r\n"); } ptr++; @@ -4202,7 +4202,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, break; default: - erl_exit(1,"Internal data structure error (in encode_size_struct2)%x\n", + erts_exit(ERTS_ERROR_EXIT,"Internal data structure error (in encode_size_struct2)%x\n", obj); } diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index ec9296d034..1b8595fe57 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -428,7 +428,7 @@ void erl_grow_estack(ErtsEStack*, Uint need); #define ESTACK_CHANGE_ALLOCATOR(s,t) \ do { \ if ((s).start != ESTK_DEF_STACK(s)) { \ - erl_exit(1, "Internal error - trying to change allocator " \ + erts_exit(ERTS_ERROR_EXIT, "Internal error - trying to change allocator " \ "type of active estack\n"); \ } \ (s).alloc_type = (t); \ @@ -589,7 +589,7 @@ do { \ #define WSTACK_CHANGE_ALLOCATOR(s,t) \ do { \ if (s.wstart != WSTK_DEF_STACK(s)) { \ - erl_exit(1, "Internal error - trying to change allocator " \ + erts_exit(ERTS_ERROR_EXIT, "Internal error - trying to change allocator " \ "type of active wstack\n"); \ } \ s.alloc_type = (t); \ @@ -781,7 +781,7 @@ ErtsPStack s = { (byte*)PSTK_DEF_STACK(s), /* pstart */ \ #define PSTACK_CHANGE_ALLOCATOR(s,t) \ do { \ if (s.pstart != (byte*)PSTK_DEF_STACK(s)) { \ - erl_exit(1, "Internal error - trying to change allocator " \ + erts_exit(ERTS_ERROR_EXIT, "Internal error - trying to change allocator " \ "type of active pstack\n"); \ } \ s.alloc_type = (t); \ @@ -952,8 +952,8 @@ double erts_get_positive_zero_float(void); /* config.c */ -__decl_noreturn void __noreturn erl_exit(int n, char*, ...); -__decl_noreturn void __noreturn erl_exit_flush_async(int n, char*, ...); +__decl_noreturn void __noreturn erts_exit(int n, char*, ...); +__decl_noreturn void __noreturn erts_flush_async_exit(int n, char*, ...); void erl_error(char*, va_list); /* copy.c */ @@ -1398,7 +1398,7 @@ erts_alloc_message_heap_state(Uint size, #endif if (size > (Uint) INT_MAX) - erl_exit(ERTS_ABORT_EXIT, "HUGE size (%beu)\n", size); + erts_exit(ERTS_ABORT_EXIT, "HUGE size (%beu)\n", size); if ( #if defined(ERTS_SMP) diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c index e0fde337f2..895fe657d1 100644 --- a/erts/emulator/beam/hash.c +++ b/erts/emulator/beam/hash.c @@ -133,7 +133,7 @@ Hash* hash_init(ErtsAlcType_t type, Hash* h, char* name, int size, HashFunctions while (h_size_table[ix] != -1 && h_size_table[ix] < size) ix++; if (h_size_table[ix] == -1) - erl_exit(1, "panic: too large hash table size (%d)\n", size); + erts_exit(ERTS_ERROR_EXIT, "panic: too large hash table size (%d)\n", size); size = h_size_table[ix]; sz = size*sizeof(HashBucket*); diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c index 06d0b5123d..5f6ea14732 100644 --- a/erts/emulator/beam/index.c +++ b/erts/emulator/beam/index.c @@ -84,7 +84,7 @@ index_put_entry(IndexTable* t, void* tmpl) Uint sz; if (ix >= t->limit) { /* A core dump is unnecessary */ - erl_exit(ERTS_DUMP_EXIT, "no more index entries in %s (max=%d)\n", + erts_exit(ERTS_DUMP_EXIT, "no more index entries in %s (max=%d)\n", t->htable.name, t->limit); } sz = INDEX_PAGE_SIZE*sizeof(IndexSlot*); @@ -123,7 +123,7 @@ void erts_index_merge(Hash* src, IndexTable* dst) ix = dst->entries++; if (ix >= dst->size) { if (ix >= dst->limit) { - erl_exit(1, "no more index entries in %s (max=%d)\n", + erts_exit(ERTS_ERROR_EXIT, "no more index entries in %s (max=%d)\n", dst->htable.name, dst->limit); } sz = INDEX_PAGE_SIZE*sizeof(IndexSlot*); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index c64c8802b9..e8b25d42a2 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -3466,7 +3466,7 @@ terminate_port(Port *prt) if ((state & ERTS_PORT_SFLG_HALT) && (erts_smp_atomic32_dec_read_nob(&erts_halt_progress) == 0)) { erts_port_release(prt); /* We will exit and never return */ - erl_exit_flush_async(erts_halt_code, ""); + erts_flush_async_exit(erts_halt_code, ""); } if (is_internal_port(send_closed_port_id)) deliver_result(send_closed_port_id, connected_id, am_closed); @@ -4881,7 +4881,7 @@ void erts_raw_port_command(Port* p, byte* buf, Uint len) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); if (len > (Uint) INT_MAX) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Absurdly large data buffer (%beu bytes) passed to" "output callback of %s driver.\n", len, @@ -7289,7 +7289,7 @@ driver_system_info(ErlDrvSysInfo *sip, size_t si_size) * of ErlDrvSysInfo (introduced in driver version 1.0). */ if (!sip || si_size < ERL_DRV_SYS_INFO_SIZE(smp_support)) - erl_exit(1, + erts_exit(ERTS_ERROR_EXIT, "driver_system_info(%p, %ld) called with invalid arguments\n", sip, si_size); diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c index 7ade8bca0f..020e61d136 100644 --- a/erts/emulator/beam/register.c +++ b/erts/emulator/beam/register.c @@ -125,7 +125,7 @@ static RegProc* reg_alloc(RegProc *tmpl) { RegProc* obj = (RegProc*) erts_alloc(ERTS_ALC_T_REG_PROC, sizeof(RegProc)); if (!obj) { - erl_exit(1, "Can't allocate %d bytes of memory\n", sizeof(RegProc)); + erts_exit(ERTS_ERROR_EXIT, "Can't allocate %d bytes of memory\n", sizeof(RegProc)); } obj->name = tmpl->name; obj->p = tmpl->p; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index ec94e3a596..ae8617f3a0 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -611,15 +611,16 @@ static unsigned long zero_value = 0, one_value = 1; # endif /* !__WIN32__ */ #endif /* WANT_NONBLOCKING */ -__decl_noreturn void __noreturn erl_exit(int n, char*, ...); +__decl_noreturn void __noreturn erts_exit(int n, char*, ...); -/* Some special erl_exit() codes: */ -#define ERTS_INTR_EXIT INT_MIN /* called from signal handler */ -#define ERTS_ABORT_EXIT (INT_MIN + 1) /* no crash dump; only abort() */ -#define ERTS_DUMP_EXIT (INT_MIN + 2) /* crash dump; then exit() */ +/* Some special erts_exit() codes: */ +#define ERTS_INTR_EXIT -1 /* called from signal handler */ +#define ERTS_ABORT_EXIT -2 /* no crash dump; only abort() */ +#define ERTS_DUMP_EXIT -3 /* crash dump; then exit() */ +#define ERTS_ERROR_EXIT -4 /* crash dump; then abort() */ #define ERTS_INTERNAL_ERROR(What) \ - erl_exit(ERTS_ABORT_EXIT, "%s:%d:%s(): Internal error: %s\n", \ + erts_exit(ERTS_ABORT_EXIT, "%s:%d:%s(): Internal error: %s\n", \ __FILE__, __LINE__, __func__, What) Eterm erts_check_io_info(void *p); @@ -929,7 +930,7 @@ erts_refc_inc(erts_refc_t *refcp, erts_aint_t min_val) #ifdef ERTS_REFC_DEBUG erts_aint_t val = erts_smp_atomic_inc_read_nob((erts_smp_atomic_t *) refcp); if (val < min_val) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "erts_refc_inc(): Bad refc found (refc=%ld < %ld)!\n", val, min_val); #else @@ -943,7 +944,7 @@ erts_refc_inctest(erts_refc_t *refcp, erts_aint_t min_val) erts_aint_t val = erts_smp_atomic_inc_read_nob((erts_smp_atomic_t *) refcp); #ifdef ERTS_REFC_DEBUG if (val < min_val) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "erts_refc_inctest(): Bad refc found (refc=%ld < %ld)!\n", val, min_val); #endif @@ -956,7 +957,7 @@ erts_refc_dec(erts_refc_t *refcp, erts_aint_t min_val) #ifdef ERTS_REFC_DEBUG erts_aint_t val = erts_smp_atomic_dec_read_nob((erts_smp_atomic_t *) refcp); if (val < min_val) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "erts_refc_dec(): Bad refc found (refc=%ld < %ld)!\n", val, min_val); #else @@ -970,7 +971,7 @@ erts_refc_dectest(erts_refc_t *refcp, erts_aint_t min_val) erts_aint_t val = erts_smp_atomic_dec_read_nob((erts_smp_atomic_t *) refcp); #ifdef ERTS_REFC_DEBUG if (val < min_val) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "erts_refc_dectest(): Bad refc found (refc=%ld < %ld)!\n", val, min_val); #endif @@ -983,7 +984,7 @@ erts_refc_add(erts_refc_t *refcp, erts_aint_t diff, erts_aint_t min_val) #ifdef ERTS_REFC_DEBUG erts_aint_t val = erts_smp_atomic_add_read_nob((erts_smp_atomic_t *) refcp, diff); if (val < min_val) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "erts_refc_add(%ld): Bad refc found (refc=%ld < %ld)!\n", diff, val, min_val); #else @@ -997,7 +998,7 @@ erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val) erts_aint_t val = erts_smp_atomic_read_nob((erts_smp_atomic_t *) refcp); #ifdef ERTS_REFC_DEBUG if (val < min_val) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "erts_refc_read(): Bad refc found (refc=%ld < %ld)!\n", val, min_val); #endif diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index 0ab6661c9f..988338ed81 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -555,7 +555,7 @@ erts_init_time(int time_correction, ErtsTimeWarpMode time_warp_mode) itime = erts_init_time_sup(time_correction, time_warp_mode); #ifdef TIW_ITIME_IS_CONSTANT if (itime != TIW_ITIME) { - erl_exit(ERTS_ABORT_EXIT, "timer resolution mismatch %d != %d", itime, TIW_ITIME); + erts_exit(ERTS_ABORT_EXIT, "timer resolution mismatch %d != %d", itime, TIW_ITIME); } #else tiw_itime = itime; diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 5286391746..b9ce70e364 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1055,7 +1055,7 @@ tail_recur: } default: - erl_exit(1, "Invalid tag in make_hash(0x%X,0x%X)\n", term, op); + erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash(0x%X,0x%X)\n", term, op); return 0; } if (WSTACK_ISEMPTY(stack)) break; @@ -1328,7 +1328,7 @@ make_hash2(Eterm term) i = hashmap_bitcount(MAP_HEADER_VAL(hdr)); break; default: - erl_exit(1, "bad header"); + erts_exit(ERTS_ERROR_EXIT, "bad header"); } while (i) { if (is_list(*ptr)) { @@ -1491,7 +1491,7 @@ make_hash2(Eterm term) break; default: - erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term); + erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term); } } break; @@ -1522,7 +1522,7 @@ make_hash2(Eterm term) UINT32_HASH(NIL_DEF, HCONST_2); goto hash2_common; default: - erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term); + erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term); } case _TAG_IMMED1_SMALL: { @@ -1538,7 +1538,7 @@ make_hash2(Eterm term) } break; default: - erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term); + erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term); hash2_common: /* Uint32 hash always has the hash value of the previous term, @@ -1733,7 +1733,7 @@ make_internal_hash(Eterm term) i = hashmap_bitcount(MAP_HEADER_VAL(hdr)); break; default: - erl_exit(1, "bad header"); + erts_exit(ERTS_ERROR_EXIT, "bad header"); } while (i) { if (is_list(*ptr)) { @@ -1909,7 +1909,7 @@ make_internal_hash(Eterm term) goto pop_next; } default: - erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term); + erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term); } } break; @@ -1922,7 +1922,7 @@ make_internal_hash(Eterm term) goto pop_next; default: - erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term); + erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term); pop_next: if (ESTACK_ISEMPTY(s)) { @@ -2205,7 +2205,7 @@ tail_recur: } default: - erl_exit(1, "Invalid tag in make_broken_hash\n"); + erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_broken_hash\n"); return 0; } if (WSTACK_ISEMPTY(stack)) break; @@ -2879,7 +2879,7 @@ tailrecur_ne: ASSERT(sz > 0 && sz < 17); break; default: - erl_exit(1, "Unknown hashmap subsubtag\n"); + erts_exit(ERTS_ERROR_EXIT, "Unknown hashmap subsubtag\n"); } goto term_array; } diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 3b6abec25e..05187341a4 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -125,8 +125,6 @@ #include "dtrace-wrapper.h" -void erl_exit(int n, char *fmt, ...); - static ErlDrvSysInfo sys_info; /* For explanation of this var, see comment for same var in erl_async.c */ @@ -515,21 +513,10 @@ struct t_data static void *ef_safe_alloc(Uint s) { void *p = EF_ALLOC(s); - if (!p) erl_exit(1, "efile drv: Can't allocate %lu bytes of memory\n", (unsigned long)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, "efile drv: Can't reallocate %lu bytes of memory\n", (unsigned long)s); + if (!p) erts_exit(ERTS_ERROR_EXIT, "efile drv: Can't allocate %lu bytes of memory\n", (unsigned long)s); return p; } -#endif - /********************************************************************* * ErlIOVec manipulation functions. */ diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 89011d89ad..66b0893682 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1574,7 +1574,7 @@ static const struct in6_addr in6addr_loopback = #endif /* HAVE_IN6 */ /* XXX: is this a driver interface function ??? */ -void erl_exit(int n, char*, ...); +void erts_exit(int n, char*, ...); /* * Malloc wrapper, @@ -1587,7 +1587,7 @@ void erl_exit(int n, char*, ...); static void *alloc_wrapper(ErlDrvSizeT size){ void *ret = driver_alloc(size); if(ret == NULL) - erl_exit(1,"Out of virtual memory in malloc (%s)", __FILE__); + erts_exit(ERTS_ERROR_EXIT,"Out of virtual memory in malloc (%s)", __FILE__); return ret; } #define ALLOC(X) alloc_wrapper(X) @@ -1595,7 +1595,7 @@ static void *alloc_wrapper(ErlDrvSizeT size){ static void *realloc_wrapper(void *current, ErlDrvSizeT size){ void *ret = driver_realloc(current,size); if(ret == NULL) - erl_exit(1,"Out of virtual memory in realloc (%s)", __FILE__); + erts_exit(ERTS_ERROR_EXIT,"Out of virtual memory in realloc (%s)", __FILE__); return ret; } #define REALLOC(X,Y) realloc_wrapper(X,Y) @@ -1856,7 +1856,7 @@ check_double_release(InetDrvBufStk *bs, ErlDrvBinary* buf) int i; for (i = 0; i < bs->buf.pos; ++i) { if (bs->buf.stk[i] == buf) { - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Multiple buffer release in inet_drv, this " "is a bug, save the core and send it to " "support@erlang.ericsson.se!"); @@ -7117,7 +7117,7 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, do { \ ErlDrvSizeT new_need = ((Ptr) - (*dest)) + (Size); \ if (new_need > dest_used) { \ - erl_exit(1,"Internal error in inet_drv, " \ + erts_exit(ERTS_ERROR_EXIT,"Internal error in inet_drv, " \ "miscalculated buffer size"); \ } \ dest_used = new_need; \ @@ -7494,7 +7494,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, do { \ int need; \ if ((Index) > spec_allocated) { \ - erl_exit(1,"Internal error in inet_drv, " \ + erts_exit(ERTS_ERROR_EXIT,"Internal error in inet_drv, " \ "miscalculated buffer size"); \ } \ need = (Index) + (N); \ diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c index 0d63d46698..7fe708dc7b 100644 --- a/erts/emulator/drivers/win32/win_con.c +++ b/erts/emulator/drivers/win32/win_con.c @@ -279,7 +279,7 @@ ConInit(void) } /* - ConNormalExit() is called from erl_exit() when the emulator + ConNormalExit() is called from erts_exit() when the emulator is stopping. If the exit has not been initiated by this console thread (WM_DESTROY or ID_BREAK), the function must invoke the console thread to save the user preferences. @@ -529,7 +529,7 @@ ConThreadInit(LPVOID param) /* PostQuitMessage() results in WM_QUIT which makes GetMessage() return 0 (which stops the main loop). Before we return from - the console thread, the ctrl_handler is called to do erl_exit. + the console thread, the ctrl_handler is called to do erts_exit. */ (*ctrl_handler)(CTRL_CLOSE_EVENT); return msg.wParam; diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index cc68e1f74d..c838150db8 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -406,7 +406,7 @@ BIF_RETTYPE hipe_bifs_enter_code_2(BIF_ALIST_2) nrcallees = arityval(tuple_val(BIF_ARG_2)[0]); else nrcallees = 0; - erl_exit(1, "%s: failed to allocate %lu bytes and %lu trampolines\r\n", + erts_exit(ERTS_ERROR_EXIT, "%s: failed to allocate %lu bytes and %lu trampolines\r\n", __func__, (unsigned long)nrbytes, (unsigned long)nrcallees); } memcpy(address, bytes, nrbytes); @@ -1311,7 +1311,7 @@ static void *hipe_make_stub(Eterm m, Eterm f, unsigned int arity, int is_remote) export_entry = erts_export_get_or_make_stub(m, f, arity); StubAddress = hipe_make_native_stub(export_entry, arity); if (!StubAddress) - erl_exit(1, "hipe_make_stub: code allocation failed\r\n"); + erts_exit(ERTS_ERROR_EXIT, "hipe_make_stub: code allocation failed\r\n"); return StubAddress; } diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 968452a641..9243e029f6 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -108,7 +108,7 @@ static const char *code_str(unsigned code) static void __noreturn hipe_abort(const char *expr, const char *file, unsigned line) { - erl_exit(1, "ASSERTION FAILED, file %s, line %u: %s\r\n", file, line, expr); + erts_exit(ERTS_ERROR_EXIT, "ASSERTION FAILED, file %s, line %u: %s\r\n", file, line, expr); } #define HIPE_ASSERT3(expr, file, line) \ @@ -316,7 +316,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) break; } default: - erl_exit(1, "hipe_mode_switch: cmd %#x\r\n", cmd); + erts_exit(ERTS_ERROR_EXIT, "hipe_mode_switch: cmd %#x\r\n", cmd); } do_return_from_native: DPRINTF("result == %#x (%s)", result, code_str(result)); @@ -560,7 +560,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) goto do_throw_to_native; } default: - erl_exit(1, "hipe_mode_switch: result %#x\r\n", result); + erts_exit(ERTS_ERROR_EXIT, "hipe_mode_switch: result %#x\r\n", result); } HIPE_CHECK_PCB(p); p->def_arg_reg[3] = result; diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index 688378b2fe..9a4d352046 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -171,7 +171,7 @@ void hipe_fclearerror_error(Process *p) #if !defined(NO_FPE_SIGNALS) erts_fp_check_init_error(&p->fp_exception); #else - erl_exit(ERTS_ABORT_EXIT, "Emulated FPE not cleared by HiPE"); + erts_exit(ERTS_ABORT_EXIT, "Emulated FPE not cleared by HiPE"); #endif } diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 754047829f..4180e65fdc 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -1334,7 +1334,7 @@ os_unreserve_physical(char *ptr, UWord size) void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_UNRESERVE_PROT, ERTS_MMAP_UNRESERVE_FLAGS, ERTS_MMAP_FD, 0); if (res == (void *) MAP_FAILED) - erl_exit(ERTS_ABORT_EXIT, "Failed to unreserve memory"); + erts_exit(ERTS_ABORT_EXIT, "Failed to unreserve memory"); } static void * @@ -2126,7 +2126,7 @@ erts_mmap_init(ErtsMMapInit *init) #endif erts_page_inv_mask = pagesize - 1; if (pagesize & erts_page_inv_mask) - erl_exit(-1, "erts_mmap: Invalid pagesize: %bpu\n", + erts_exit(1, "erts_mmap: Invalid pagesize: %bpu\n", pagesize); ERTS_MMAP_OP_RINGBUF_INIT(); @@ -2140,7 +2140,7 @@ erts_mmap_init(ErtsMMapInit *init) #if HAVE_MMAP && !defined(MAP_ANON) mmap_state.mmap_fd = open("/dev/zero", O_RDWR); if (mmap_state.mmap_fd < 0) - erl_exit(-1, "erts_mmap: Failed to open /dev/zero\n"); + erts_exit(1, "erts_mmap: Failed to open /dev/zero\n"); #endif erts_smp_mtx_init(&mmap_state.mtx, "erts_mmap"); @@ -2155,7 +2155,7 @@ erts_mmap_init(ErtsMMapInit *init) sz = end - ptr; start = os_mmap_virtual(ptr, sz); if (!start || start > ptr || start >= end) - erl_exit(-1, + erts_exit(1, "erts_mmap: Failed to create virtual range for super carrier\n"); sz = start - ptr; if (sz) @@ -2193,7 +2193,7 @@ erts_mmap_init(ErtsMMapInit *init) start = os_mmap(NULL, sz, 1); } if (!start) - erl_exit(-1, + erts_exit(1, "erts_mmap: Failed to create super carrier of size %bpu MB\n", init->scs/1024/1024); end = start + sz; @@ -2242,7 +2242,7 @@ erts_mmap_init(ErtsMMapInit *init) if ((desc_size + ERTS_SUPERALIGNED_SIZE + ERTS_PAGEALIGNED_SIZE) > end - start) - erl_exit(-1, "erts_mmap: No space for segments in super carrier\n"); + erts_exit(1, "erts_mmap: No space for segments in super carrier\n"); mmap_state.sa.bot = start; mmap_state.sa.bot += desc_size; @@ -2280,7 +2280,7 @@ erts_mmap_init(ErtsMMapInit *init) */ #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION if (virtual_map && !os_reserve_physical(start, mmap_state.sa.bot - start)) - erl_exit(-1, "erts_mmap: Failed to reserve physical memory for descriptors\n"); + erts_exit(1, "erts_mmap: Failed to reserve physical memory for descriptors\n"); #endif mmap_state.desc.unused_start = start; mmap_state.desc.unused_end = mmap_state.sa.bot; diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 0d51aad863..3398be2b07 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -1490,7 +1490,7 @@ erts_mseg_init(ErtsMsegInit_t *init) #if HALFWORD_HEAP if (sizeof(void *) != 8) - erl_exit(-1,"Halfword emulator cannot be run in 32bit mode"); + erts_exit(1,"Halfword emulator cannot be run in 32bit mode"); init->mmap.virtual_range.start = (char *) sbrk(0); init->mmap.virtual_range.end = (char *) 0x100000000UL; @@ -1500,7 +1500,7 @@ erts_mseg_init(ErtsMsegInit_t *init) erts_mmap_init(&init->mmap); if (!IS_2POW(GET_PAGE_SIZE)) - erl_exit(ERTS_ABORT_EXIT, "erts_mseg: Unexpected page_size %beu\n", GET_PAGE_SIZE); + erts_exit(ERTS_ABORT_EXIT, "erts_mseg: Unexpected page_size %beu\n", GET_PAGE_SIZE); ASSERT((MSEG_ALIGNED_SIZE % GET_PAGE_SIZE) == 0); diff --git a/erts/emulator/sys/common/erl_os_monotonic_time_extender.c b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c index b79485241f..367b22d989 100644 --- a/erts/emulator/sys/common/erl_os_monotonic_time_extender.c +++ b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c @@ -44,7 +44,7 @@ static void *os_monotonic_time_extender(void *vstatep) erts_milli_sleep(sleep_time); } - erl_exit(ERTS_ABORT_EXIT, "os_monotonic_time_extender thread terminating"); + erts_exit(ERTS_ABORT_EXIT, "os_monotonic_time_extender thread terminating"); return NULL; } diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c index 5cee582a00..c690f08dea 100644 --- a/erts/emulator/sys/ose/erl_poll.c +++ b/erts/emulator/sys/ose/erl_poll.c @@ -658,7 +658,7 @@ int erts_poll_wait(ErtsPollSet ps, break; default: res = 0; - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: Invalid wakeup_state=%d\n", __FILE__, __LINE__, (int) wakeup_state); } diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c index bcd0ffa0b6..e354faf02c 100644 --- a/erts/emulator/sys/ose/sys.c +++ b/erts/emulator/sys/ose/sys.c @@ -570,7 +570,7 @@ break_requested(void) fprintf(stderr,"break!\n"); #endif if (ERTS_BREAK_REQUESTED) - erl_exit(ERTS_INTR_EXIT, ""); + erts_exit(ERTS_INTR_EXIT, ""); ERTS_SET_BREAK_REQUESTED; ERTS_CHK_IO_AS_INTR(); /* Make sure we don't sleep in poll */ diff --git a/erts/emulator/sys/ose/sys_float.c b/erts/emulator/sys/ose/sys_float.c index 3d9abc6bd1..51875792ae 100644 --- a/erts/emulator/sys/ose/sys_float.c +++ b/erts/emulator/sys/ose/sys_float.c @@ -90,7 +90,7 @@ void erts_fp_check_init_error(volatile unsigned long *fpexnp) snprintf(buf, sizeof buf, "ERTS_FP_CHECK_INIT at %p: detected unhandled FPE at %p\r\n", __builtin_return_address(0), (void*)*fpexnp); if (write(2, buf, strlen(buf)) <= 0) - erl_exit(ERTS_ABORT_EXIT, "%s", buf); + erts_exit(ERTS_ABORT_EXIT, "%s", buf); *fpexnp = 0; #if defined(__i386__) || defined(__x86_64__) erts_restore_fpu(); diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 8d7da3e47e..d94b37430e 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -638,14 +638,14 @@ erl_sys_init(void) res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz); if (res != 0) { if (res < 0) - erl_exit(-1, + erts_exit(1, "Environment variable BINDIR is not set\n"); if (res > 0) - erl_exit(-1, + erts_exit(1, "Value of environment variable BINDIR is too large\n"); } if (bindir[0] != DIR_SEPARATOR_CHAR) - erl_exit(-1, + erts_exit(1, "Environment variable BINDIR does not contain an" " absolute path\n"); csp_path_sz = (strlen(bindir) @@ -825,7 +825,7 @@ break_requested(void) fprintf(stderr,"break!\n"); #endif if (ERTS_BREAK_REQUESTED) - erl_exit(ERTS_INTR_EXIT, ""); + erts_exit(ERTS_INTR_EXIT, ""); ERTS_SET_BREAK_REQUESTED; ERTS_CHK_IO_AS_INTR(); /* Make sure we don't sleep in poll */ @@ -864,7 +864,7 @@ sigusr1_exit(void) } prepare_crash_dump(secs); - erl_exit(1, "Received SIGUSR1\n"); + erts_exit(ERTS_ERROR_EXIT, "Received SIGUSR1\n"); } #ifdef ETHR_UNUSABLE_SIGUSRX @@ -920,7 +920,7 @@ static RETSIGTYPE suspend_signal(int signum) static void quit_requested(void) { - erl_exit(ERTS_INTR_EXIT, ""); + erts_exit(ERTS_INTR_EXIT, ""); } #if (defined(SIG_SIGSET) || defined(SIG_SIGNAL)) @@ -3165,7 +3165,7 @@ signal_dispatcher_thread_func(void *unused) if (res < 0) { if (errno == EINTR) continue; - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "signal-dispatcher thread got unexpected error: %s (%d)\n", erl_errno_id(errno), errno); @@ -3214,7 +3214,7 @@ signal_dispatcher_thread_func(void *unused) sigusr1_exit(); break; default: - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "signal-dispatcher thread received unknown " "signal notification: '%c'\n", buf[i]); @@ -3233,7 +3233,7 @@ init_smp_sig_notify(void) thr_opts.name = "sys_sig_dispatcher"; if (pipe(sig_notify_fds) < 0) { - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Failed to create signal-dispatcher pipe: %s (%d)\n", erl_errno_id(errno), errno); @@ -3249,7 +3249,7 @@ init_smp_sig_notify(void) static void init_smp_sig_suspend(void) { if (pipe(sig_suspend_fds) < 0) { - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Failed to create sig_suspend pipe: %s (%d)\n", erl_errno_id(errno), errno); @@ -3265,7 +3265,7 @@ static void initialize_darwin_main_thread_pipes(void) { if (pipe(erts_darwin_main_thread_pipe) < 0 || pipe(erts_darwin_main_thread_result_pipe) < 0) { - erl_exit(1,"Fatal error initializing Darwin main thread stealing"); + erts_exit(ERTS_ERROR_EXIT,"Fatal error initializing Darwin main thread stealing"); } } diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c index 1ef9e5eef7..8fe7e599e5 100644 --- a/erts/emulator/sys/unix/sys_float.c +++ b/erts/emulator/sys/unix/sys_float.c @@ -90,7 +90,7 @@ void erts_fp_check_init_error(volatile unsigned long *fpexnp) snprintf(buf, sizeof buf, "ERTS_FP_CHECK_INIT at %p: detected unhandled FPE at %p\r\n", __builtin_return_address(0), (void*)*fpexnp); if (write(2, buf, strlen(buf)) <= 0) - erl_exit(ERTS_ABORT_EXIT, "%s", buf); + erts_exit(ERTS_ABORT_EXIT, "%s", buf); *fpexnp = 0; #if defined(__i386__) || defined(__x86_64__) erts_restore_fpu(); diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index 2e1914f564..95b3daa4ed 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -342,7 +342,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) * times() (CLK_TCK), the resolution is always one millisecond.. */ if ((erts_sys_time_data__.r.o.ticks_per_sec = TICKS_PER_SEC()) < 0) - erl_exit(ERTS_ABORT_EXIT, "Can't get clock ticks/sec\n"); + erts_exit(ERTS_ABORT_EXIT, "Can't get clock ticks/sec\n"); #if defined(OS_MONOTONIC_TIME_USING_TIMES) #if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT @@ -450,7 +450,7 @@ posix_clock_gettime(clockid_t id, char *name) if (clock_gettime(id, &ts) != 0) { int err = errno; char *errstr = err ? strerror(err) : "unknown"; - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "clock_gettime(%s, _) failed: %s (%d)\n", name, errstr, err); } @@ -495,13 +495,13 @@ posix_clock_gettime_times(clockid_t mid, char *mname, if (mres != 0) { char *errstr = merr ? strerror(merr) : "unknown"; - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "clock_gettime(%s, _) failed: %s (%d)\n", mname, errstr, merr); } if (sres != 0) { char *errstr = serr ? strerror(serr) : "unknown"; - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "clock_gettime(%s, _) failed: %s (%d)\n", sname, errstr, serr); } @@ -674,7 +674,7 @@ mach_clocks_init(void) clck_srv_p = &internal_state.r.o.mach.clock.monotonic.srv; kret = host_get_clock_service(host, id, clck_srv_p); if (kret != KERN_SUCCESS) { - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "host_get_clock_service(_, %s, _) failed\n", name); } @@ -686,7 +686,7 @@ mach_clocks_init(void) clck_srv_p = &internal_state.r.o.mach.clock.wall.srv; kret = host_get_clock_service(host, id, clck_srv_p); if (kret != KERN_SUCCESS) { - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "host_get_clock_service(_, %s, _) failed\n", name); } @@ -695,7 +695,7 @@ mach_clocks_init(void) if (atexit(mach_clocks_fini) != 0) { int err = errno; char *errstr = err ? strerror(err) : "unknown"; - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "Failed to register mach_clocks_fini() " "for call at exit: %s (%d)\n", errstr, err); @@ -717,7 +717,7 @@ mach_clock_getres(ErtsMachClock *clk) (clock_attr_t) attr, &cnt); if (kret != KERN_SUCCESS || cnt != 1) { - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "clock_get_attributes(%s, _) failed\n", clk->name); } @@ -735,7 +735,7 @@ mach_clock_get_time(ErtsMachClock *clk) kret = clock_get_time(clk->srv, &time_spec); if (kret != KERN_SUCCESS) - erl_exit(ERTS_ABORT_EXIT, "clock_get_time(%s, _) failed\n", clk->name); + erts_exit(ERTS_ABORT_EXIT, "clock_get_time(%s, _) failed\n", clk->name); return ERTS_TimeSpec2Sint64(&time_spec); } @@ -781,11 +781,11 @@ erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) &sys_time_spec); if (mkret != KERN_SUCCESS) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "clock_get_time(%s, _) failed\n", internal_state.r.o.mach.clock.monotonic.name); if (skret != KERN_SUCCESS) - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "clock_get_time(%s, _) failed\n", internal_state.r.o.mach.clock.wall.name); @@ -850,7 +850,7 @@ erts_os_system_time(void) if (gettimeofday(&tv, NULL) != 0) { int err = errno; char *errstr = err ? strerror(err) : "unknown"; - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "gettimeofday(_, NULL) failed: %s (%d)\n", errstr, err); } diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 466f4a3b48..547d4b1d21 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -436,7 +436,7 @@ wakeup_cause(ErtsPollSet ps) break; default: res = 0; - erl_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: Invalid wakeup_state=%d\n", __FILE__, __LINE__, (int) wakeup_state); } @@ -576,7 +576,7 @@ static void signal_standby(ErtsPollSet ps) --(ps->standby_wait_counter); if (ps->standby_wait_counter < 0) { LeaveCriticalSection(&(ps->standby_crit)); - erl_exit(1,"Standby signalled by more threads than expected"); + erts_exit(ERTS_ERROR_EXIT,"Standby signalled by more threads than expected"); } if (!(ps->standby_wait_counter)) { SetEvent(ps->standby_wait_event); @@ -738,7 +738,7 @@ static void *break_waiter(void *param) erts_mtx_unlock(&break_waiter_lock); break; default: - erl_exit(1,"Unexpected event in break_waiter"); + erts_exit(ERTS_ERROR_EXIT,"Unexpected event in break_waiter"); } } } @@ -1157,7 +1157,7 @@ int erts_poll_wait(ErtsPollSet ps, HARDDEBUGF(("Oups!")); /* Oups, got signalled before we took the lock, can't reset */ if(!is_io_ready(ps)) { - erl_exit(1,"Internal error: " + erts_exit(ERTS_ERROR_EXIT,"Internal error: " "Inconsistent io structures in erl_poll.\n"); } START_WAITER(ps,w); @@ -1215,7 +1215,7 @@ int erts_poll_wait(ErtsPollSet ps, ERTS_SET_BREAK_REQUESTED; break; case BREAK_WAITER_GOT_HALT: - erl_exit(0,""); + erts_exit(0,""); break; default: break; diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index fce76db28f..37d3e16256 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -38,7 +38,7 @@ void erts_sys_init_float(void); void erl_start(int, char**); -void erl_exit(int n, char*, ...); +void erts_exit(int n, char*, ...); void erl_error(char*, va_list); void erl_crash_dump(char*, int, char*, ...); @@ -200,7 +200,7 @@ erts_sys_misc_mem_sz(void) */ void sys_tty_reset(int exit_code) { - if (exit_code > 0) + if (exit_code == ERTS_ERROR_EXIT) ConWaitForExit(); else ConNormalExit(); @@ -3110,13 +3110,13 @@ check_supported_os_version(void) || int_os_version.dwMajorVersion < major || (int_os_version.dwMajorVersion == major && int_os_version.dwMinorVersion < minor)) - erl_exit(-1, + erts_exit(1, "Windows version not supported " "(min required: winnt %d.%d)\n", major, minor); } #else - erl_exit(-1, + erts_exit(1, "Windows version not supported " "(min required: win %d.%d)\n", nt_major, nt_minor); diff --git a/erts/emulator/sys/win32/sys_interrupt.c b/erts/emulator/sys/win32/sys_interrupt.c index d6178de03c..a89211fa8e 100644 --- a/erts/emulator/sys/win32/sys_interrupt.c +++ b/erts/emulator/sys/win32/sys_interrupt.c @@ -81,7 +81,7 @@ BOOL WINAPI ctrl_handler_ignore_break(DWORD dwCtrlType) return TRUE; /* else pour through... */ case CTRL_CLOSE_EVENT: - erl_exit(0, ""); + erts_exit(0, ""); break; } return TRUE; @@ -106,7 +106,7 @@ BOOL WINAPI ctrl_handler_replace_intr(DWORD dwCtrlType) /* else pour through... */ case CTRL_CLOSE_EVENT: case CTRL_SHUTDOWN_EVENT: - erl_exit(0, ""); + erts_exit(0, ""); break; } return TRUE; @@ -133,7 +133,7 @@ BOOL WINAPI ctrl_handler(DWORD dwCtrlType) return TRUE; /* else pour through... */ case CTRL_CLOSE_EVENT: - erl_exit(0, ""); + erts_exit(0, ""); break; } return TRUE; diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index 9e5f78703a..3cf7f7cf07 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -147,7 +147,7 @@ os_monotonic_time_qpc(void) LARGE_INTEGER pc; if (!(*internal_state.r.o.pQueryPerformanceCounter)(&pc)) - erl_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n"); + erts_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n"); return (ErtsMonotonicTime) pc.QuadPart; } @@ -164,7 +164,7 @@ os_times_qpc(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) GetSystemTime(&st); if (!qpcr) - erl_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n"); + erts_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n"); *mtimep = (ErtsMonotonicTime) pc.QuadPart; @@ -251,7 +251,7 @@ sys_hrtime_qpc(void) LARGE_INTEGER pc; if (!(*internal_state.r.o.pQueryPerformanceCounter)(&pc)) - erl_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n"); + erts_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n"); ASSERT(pc.QuadPart > 0); diff --git a/erts/emulator/valgrind/suppress.patched.3.6.0 b/erts/emulator/valgrind/suppress.patched.3.6.0 index 16cecf2dba..fcde4a0123 100644 --- a/erts/emulator/valgrind/suppress.patched.3.6.0 +++ b/erts/emulator/valgrind/suppress.patched.3.6.0 @@ -368,7 +368,7 @@ Memcheck:Addr4 ... fun:erts_print_scheduler_info ... -fun:erl_exit +fun:erts_exit fun:broken_halt_test fun:erts_debug_set_internal_state_2 fun:process_main diff --git a/erts/emulator/valgrind/suppress.standard b/erts/emulator/valgrind/suppress.standard index a1f3f82364..bb07c92fc1 100644 --- a/erts/emulator/valgrind/suppress.standard +++ b/erts/emulator/valgrind/suppress.standard @@ -336,7 +336,7 @@ Memcheck:Addr4 ... fun:erts_print_scheduler_info ... -fun:erl_exit +fun:erts_exit fun:broken_halt_test fun:erts_debug_set_internal_state_2 fun:process_main -- cgit v1.2.3 From 1b094d72ffc56069c72f17c7edd673dbbfe47e39 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 23 Feb 2016 21:05:03 +0100 Subject: erts: Make erlang:halt() accept bignums as Status Just mask away the high bits to get a more tolerant erlang:halt that behaves the same on 32 and 64 bit architectures. --- erts/doc/src/erlang.xml | 3 ++- erts/emulator/beam/bif.c | 12 ++++++------ erts/emulator/beam/big.c | 26 ++++++++++++++++++++++++++ erts/emulator/beam/big.h | 1 + 4 files changed, 35 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index c37ed3bea5..7926157fa5 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1794,7 +1794,8 @@ os_prompt%

On many platforms, the OS supports only status - codes 0-255.

+ codes 0-255. A too large status code will be truncated by clearing + the high bits.

For integer Status, the Erlang runtime system closes all ports and allows async threads to finish their operations before exiting. To exit without such flushing, use diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index a61025d19a..66c2853534 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3938,10 +3938,10 @@ static char halt_msg[HALT_MSG_SIZE]; /* ARGSUSED */ BIF_RETTYPE halt_1(BIF_ALIST_1) { - Sint code; + Uint code; - if (is_small(BIF_ARG_1) && (code = signed_val(BIF_ARG_1)) >= 0) { - int pos_int_code = (int)code & INT_MAX; + if (term_to_Uint_mask(BIF_ARG_1, &code)) { + int pos_int_code = (int) (code & INT_MAX); VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1)); erts_halt(pos_int_code); ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined); @@ -3975,7 +3975,7 @@ BIF_RETTYPE halt_1(BIF_ALIST_1) /* ARGSUSED */ BIF_RETTYPE halt_2(BIF_ALIST_2) { - Sint code; + Uint code; Eterm optlist = BIF_ARG_2; int flush = 1; @@ -4002,8 +4002,8 @@ BIF_RETTYPE halt_2(BIF_ALIST_2) if (is_not_nil(optlist)) goto error; - if (is_small(BIF_ARG_1) && (code = signed_val(BIF_ARG_1)) >= 0) { - int pos_int_code = (int)code & INT_MAX; + if (term_to_Uint_mask(BIF_ARG_1, &code)) { + int pos_int_code = (int) (code & INT_MAX); VERBOSE(DEBUG_SYSTEM, ("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2)); if (flush) { diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 15bcd44fb9..87d3be2b0f 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -2028,6 +2028,32 @@ term_to_Uint(Eterm term, Uint *up) } } +/* same as term_to_Uint() + but also accept larger bignums by masking + */ +int +term_to_Uint_mask(Eterm term, Uint *up) +{ + if (is_small(term)) { + Sint i = signed_val(term); + if (i < 0) { + *up = BADARG; + return 0; + } + *up = (Uint) i; + return 1; + } else if (is_big(term) && !big_sign(term)) { + ErtsDigit* xr = big_v(term); + + ERTS_CT_ASSERT(sizeof(ErtsDigit) == sizeof(Uint)); + *up = (Uint)*xr; /* just pick first word */ + return 1; + } else { + *up = BADARG; + return 0; + } +} + int term_to_UWord(Eterm term, UWord *up) { diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 4aa9724ae3..3c1f1e6b23 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -161,6 +161,7 @@ Eterm bytes_to_big(byte*, dsize_t, int, Eterm*); byte* big_to_bytes(Eterm, byte*); int term_to_Uint(Eterm, Uint*); +int term_to_Uint_mask(Eterm, Uint*); int term_to_UWord(Eterm, UWord*); int term_to_Sint(Eterm, Sint*); #if HAVE_INT64 -- cgit v1.2.3 From 8f4c278b69fe4d613a0b865a2edac43231cad913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 30 Nov 2015 15:35:47 +0100 Subject: Allow erlang:finish_loading/1 to load more than one module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BIFs prepare_loading/2 and finish_loading/1 have been designed to allow fast loading in parallel of many modules. Because of the complications with on_load functions, the initial implementation of finish_loading/1 only allowed a single element in the list of prepared modules. finish_loading/1 does not suspend other processes, but it must wait for all schedulers to pass a write barrier ("thread progress"). The time for all schedulers to pass the write barrier is highly variable, depending on what kind of code they are executing. Therefore, allowing finish_loading/1 to finish the loading for more than one module before passing the write barrier could potentially be much faster than calling finish_loading/1 multiple times. The test case many/1 run on my computer shows that with "heavy load", finish loading of 100 modules in parallel is almost 50 times faster than loading them sequentially. With "light load", the gain is still almost 10 times. Here follows an actual sample of the output from the test case on my computer (an 2012 iMac): Light load ========== Sequential: 22361 µs Parallel: 2586 µs Ratio: 9 Heavy load ========== Sequential: 254512 µs Parallel: 5246 µs Ratio: 49 --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/beam_bif_load.c | 54 ++++++--- erts/emulator/beam/beam_ranges.c | 152 ++++++++++++------------- erts/emulator/beam/code_ix.c | 4 +- erts/emulator/beam/code_ix.h | 2 +- erts/emulator/beam/global.h | 2 +- erts/emulator/beam/module.h | 1 + erts/emulator/test/Makefile | 1 + erts/emulator/test/multi_load_SUITE.erl | 190 ++++++++++++++++++++++++++++++++ 9 files changed, 303 insertions(+), 104 deletions(-) create mode 100644 erts/emulator/test/multi_load_SUITE.erl (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 6fb08ee896..a00b22eac0 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -211,6 +211,7 @@ atom dsend atom dsend_continue_trap atom dunlink atom duplicate_bag +atom duplicated atom dupnames atom einval atom elib_malloc diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 1b4c022370..d426b6eebb 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -86,7 +86,7 @@ BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3) ASSERT(modp->curr.num_breakpoints == 0); } - erts_start_staging_code_ix(); + erts_start_staging_code_ix(1); res = erts_make_stub_module(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); @@ -163,14 +163,13 @@ exception_list(Process* p, Eterm tag, struct m* mp, Sint exceptions) Eterm* hp = HAlloc(p, 3 + 2*exceptions); Eterm res = NIL; - mp += exceptions - 1; while (exceptions > 0) { if (mp->exception) { res = CONS(hp, mp->module, res); hp += 2; exceptions--; } - mp--; + mp++; } return TUPLE2(hp, tag, res); } @@ -202,8 +201,12 @@ finish_loading_1(BIF_ALIST_1) n = erts_list_length(BIF_ARG_1); if (n < 0) { - ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG); - goto done; + badarg: + if (p) { + erts_free(ERTS_ALC_T_LOADER_TMP, p); + } + erts_release_code_write_permission(); + BIF_ERROR(BIF_P, BADARG); } p = erts_alloc(ERTS_ALC_T_LOADER_TMP, n*sizeof(struct m)); @@ -218,29 +221,32 @@ finish_loading_1(BIF_ALIST_1) ProcBin* pb; if (!ERTS_TERM_IS_MAGIC_BINARY(term)) { - ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG); - goto done; + goto badarg; } pb = (ProcBin*) binary_val(term); p[i].code = pb->val; p[i].module = erts_module_for_prepared_code(p[i].code); if (p[i].module == NIL) { - ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG); - goto done; + goto badarg; } BIF_ARG_1 = CDR(cons); } /* * Since we cannot handle atomic loading of a group of modules - * if one or more of them uses on_load, we will only allow one - * element in the list. This limitation is intended to be - * lifted in the future. + * if one or more of them uses on_load, we will only allow + * more than one element in the list if none of the modules + * have an on_load function. */ if (n > 1) { - ERTS_BIF_PREP_ERROR(res, BIF_P, SYSTEM_LIMIT); - goto done; + for (i = 0; i < n; i++) { + if (erts_has_code_on_load(p[i].code) == am_true) { + erts_free(ERTS_ALC_T_LOADER_TMP, p); + erts_release_code_write_permission(); + BIF_ERROR(BIF_P, SYSTEM_LIMIT); + } + } } /* @@ -252,11 +258,27 @@ finish_loading_1(BIF_ALIST_1) */ res = am_ok; - erts_start_staging_code_ix(); + erts_start_staging_code_ix(n); for (i = 0; i < n; i++) { p[i].modp = erts_put_module(p[i].module); + p[i].modp->seen = 0; + } + + exceptions = 0; + for (i = 0; i < n; i++) { + p[i].exception = 0; + if (p[i].modp->seen) { + p[i].exception = 1; + exceptions++; + } + p[i].modp->seen = 1; } + if (exceptions) { + res = exception_list(BIF_P, am_duplicated, p, exceptions); + goto done; + } + for (i = 0; i < n; i++) { if (p[i].modp->curr.num_breakpoints > 0 || p[i].modp->curr.num_traced_exports > 0 || @@ -492,7 +514,7 @@ BIF_RETTYPE delete_module_1(BIF_ALIST_1) } { - erts_start_staging_code_ix(); + erts_start_staging_code_ix(0); code_ix = erts_staging_code_ix(); modp = erts_get_module(BIF_ARG_1, code_ix); if (!modp) { diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c index 5a2b66727a..54c337ee72 100644 --- a/erts/emulator/beam/beam_ranges.c +++ b/erts/emulator/beam/beam_ranges.c @@ -53,6 +53,7 @@ struct ranges { }; static struct ranges r[ERTS_NUM_CODE_IX]; static erts_smp_atomic_t mem_used; +static Range* write_ptr; #ifdef HARD_DEBUG static void check_consistency(struct ranges* p) @@ -72,6 +73,17 @@ static void check_consistency(struct ranges* p) # define CHECK(r) #endif /* HARD_DEBUG */ +static int +rangecompare(Range* a, Range* b) +{ + if (a->start < b->start) { + return -1; + } else if (a->start == b->start) { + return 0; + } else { + return 1; + } +} void erts_init_ranges(void) @@ -88,45 +100,70 @@ erts_init_ranges(void) } void -erts_start_staging_ranges(void) +erts_start_staging_ranges(int num_new) { + ErtsCodeIndex src = erts_active_code_ix(); ErtsCodeIndex dst = erts_staging_code_ix(); + Sint need; if (r[dst].modules) { erts_smp_atomic_add_nob(&mem_used, -r[dst].allocated); erts_free(ERTS_ALC_T_MODULE_REFS, r[dst].modules); - r[dst].modules = NULL; } + + need = r[dst].allocated = r[src].n + num_new; + erts_smp_atomic_add_nob(&mem_used, need); + write_ptr = erts_alloc(ERTS_ALC_T_MODULE_REFS, + need * sizeof(Range)); + r[dst].modules = write_ptr; } void erts_end_staging_ranges(int commit) { - ErtsCodeIndex dst = erts_staging_code_ix(); - - if (commit && r[dst].modules == NULL) { + if (commit) { Sint i; - Sint n; - - /* No modules added, just clone src and remove purged code. */ ErtsCodeIndex src = erts_active_code_ix(); + ErtsCodeIndex dst = erts_staging_code_ix(); + Range* mp; + Sint num_inserted; - erts_smp_atomic_add_nob(&mem_used, r[src].n); - r[dst].modules = erts_alloc(ERTS_ALC_T_MODULE_REFS, - r[src].n * sizeof(Range)); - r[dst].allocated = r[src].n; - n = 0; + mp = r[dst].modules; + num_inserted = write_ptr - mp; for (i = 0; i < r[src].n; i++) { Range* rp = r[src].modules+i; if (rp->start < RANGE_END(rp)) { /* Only insert a module that has not been purged. */ - r[dst].modules[n] = *rp; - n++; + write_ptr->start = rp->start; + erts_smp_atomic_init_nob(&write_ptr->end, + (erts_aint_t)(RANGE_END(rp))); + write_ptr++; + } + } + + /* + * There are num_inserted new range entries (unsorted) at the + * beginning of the modules array, followed by the old entries + * (sorted). We must now sort the entire array. + */ + + r[dst].n = write_ptr - mp; + if (num_inserted > 1) { + qsort(mp, r[dst].n, sizeof(Range), + (int (*)(const void *, const void *)) rangecompare); + } else if (num_inserted == 1) { + /* Sift the new range into place. This is faster than qsort(). */ + Range t = mp[0]; + for (i = 0; i < r[dst].n-1 && t.start > mp[i+1].start; i++) { + mp[i] = mp[i+1]; } + mp[i] = t; } - r[dst].n = n; + r[dst].modules = mp; + CHECK(&r[dst]); erts_smp_atomic_set_nob(&r[dst].mid, - (erts_aint_t) (r[dst].modules + n / 2)); + (erts_aint_t) (r[dst].modules + + r[dst].n / 2)); } } @@ -135,82 +172,29 @@ erts_update_ranges(BeamInstr* code, Uint size) { ErtsCodeIndex dst = erts_staging_code_ix(); ErtsCodeIndex src = erts_active_code_ix(); - Sint i; - Sint n; - Sint need; if (src == dst) { ASSERT(!erts_initialized); /* - * During start-up of system, the indices are the same. - * Handle this by faking a source area. + * During start-up of system, the indices are the same + * and erts_start_staging_ranges() has not been called. */ - src = (src+1) % ERTS_NUM_CODE_IX; - if (r[src].modules) { - erts_smp_atomic_add_nob(&mem_used, -r[src].allocated); - erts_free(ERTS_ALC_T_MODULE_REFS, r[src].modules); + if (r[dst].modules == NULL) { + Sint need = 128; + erts_smp_atomic_add_nob(&mem_used, need); + r[dst].modules = erts_alloc(ERTS_ALC_T_MODULE_REFS, + need * sizeof(Range)); + r[dst].allocated = need; + write_ptr = r[dst].modules; } - r[src] = r[dst]; - r[dst].modules = 0; } - CHECK(&r[src]); - - ASSERT(r[dst].modules == NULL); - need = r[dst].allocated = r[src].n + 1; - erts_smp_atomic_add_nob(&mem_used, need); - r[dst].modules = (Range *) erts_alloc(ERTS_ALC_T_MODULE_REFS, - need * sizeof(Range)); - n = 0; - for (i = 0; i < r[src].n; i++) { - Range* rp = r[src].modules+i; - if (code < rp->start) { - r[dst].modules[n].start = code; - erts_smp_atomic_init_nob(&r[dst].modules[n].end, - (erts_aint_t)(((byte *)code) + size)); - ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < code); - n++; - break; - } - if (rp->start < RANGE_END(rp)) { - /* Only insert a module that has not been purged. */ - r[dst].modules[n].start = rp->start; - erts_smp_atomic_init_nob(&r[dst].modules[n].end, - (erts_aint_t)(RANGE_END(rp))); - ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < rp->start); - n++; - } - } - - while (i < r[src].n) { - Range* rp = r[src].modules+i; - if (rp->start < RANGE_END(rp)) { - /* Only insert a module that has not been purged. */ - r[dst].modules[n].start = rp->start; - erts_smp_atomic_init_nob(&r[dst].modules[n].end, - (erts_aint_t)(RANGE_END(rp))); - ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < rp->start); - n++; - } - i++; - } - - if (n == 0 || code > r[dst].modules[n-1].start) { - r[dst].modules[n].start = code; - erts_smp_atomic_init_nob(&r[dst].modules[n].end, - (erts_aint_t)(((byte *)code) + size)); - ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < code); - n++; - } - - ASSERT(n <= r[src].n+1); - r[dst].n = n; - erts_smp_atomic_set_nob(&r[dst].mid, - (erts_aint_t) (r[dst].modules + n / 2)); - - CHECK(&r[dst]); - CHECK(&r[src]); + ASSERT(r[dst].modules); + write_ptr->start = code; + erts_smp_atomic_init_nob(&(write_ptr->end), + (erts_aint_t)(((byte *)code) + size)); + write_ptr++; } void diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c index 209d008d18..1b0968c55c 100644 --- a/erts/emulator/beam/code_ix.c +++ b/erts/emulator/beam/code_ix.c @@ -65,12 +65,12 @@ void erts_code_ix_init(void) CIX_TRACE("init"); } -void erts_start_staging_code_ix(void) +void erts_start_staging_code_ix(int num_new) { beam_catches_start_staging(); export_start_staging(); module_start_staging(); - erts_start_staging_ranges(); + erts_start_staging_ranges(num_new); CIX_TRACE("start"); } diff --git a/erts/emulator/beam/code_ix.h b/erts/emulator/beam/code_ix.h index 5f00b409ef..7a66211a5b 100644 --- a/erts/emulator/beam/code_ix.h +++ b/erts/emulator/beam/code_ix.h @@ -100,7 +100,7 @@ void erts_release_code_write_permission(void); * Must be followed by calls to either "end" and "commit" or "abort" before * code write permission can be released. */ -void erts_start_staging_code_ix(void); +void erts_start_staging_code_ix(int num_new); /* End the staging. * Preceded by "start" and must be followed by "commit". diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index b174fa3e8a..956efa5e36 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1027,7 +1027,7 @@ Eterm erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info); /* beam_ranges.c */ void erts_init_ranges(void); -void erts_start_staging_ranges(void); +void erts_start_staging_ranges(int num_new); void erts_end_staging_ranges(int commit); void erts_update_ranges(BeamInstr* code, Uint size); void erts_remove_from_ranges(BeamInstr* code); diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h index e66d628ca9..b7468b0926 100644 --- a/erts/emulator/beam/module.h +++ b/erts/emulator/beam/module.h @@ -37,6 +37,7 @@ struct erl_module_instance { typedef struct erl_module { IndexSlot slot; /* Must be located at top of struct! */ int module; /* Atom index for module (not tagged). */ + int seen; /* Used by finish_loading() */ struct erl_module_instance curr; struct erl_module_instance old; /* protected by "old_code" rwlock */ diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 987a655c3d..318db4b45e 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -74,6 +74,7 @@ MODULES= \ match_spec_SUITE \ module_info_SUITE \ monitor_SUITE \ + multi_load_SUITE \ nested_SUITE \ nif_SUITE \ node_container_SUITE \ diff --git a/erts/emulator/test/multi_load_SUITE.erl b/erts/emulator/test/multi_load_SUITE.erl new file mode 100644 index 0000000000..33e6c15f8f --- /dev/null +++ b/erts/emulator/test/multi_load_SUITE.erl @@ -0,0 +1,190 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(multi_load_SUITE). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + many/1,on_load/1,errors/1]). + +-include_lib("common_test/include/ct.hrl"). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [many,on_load,errors]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +many(_Config) -> + Ms = make_modules(100, fun many_module/1), + + io:put_chars("Light load\n" + "=========="), + many_measure(Ms), + + _ = [spawn_link(fun many_worker/0) || _ <- lists:seq(1, 8)], + erlang:yield(), + io:put_chars("Heavy load\n" + "=========="), + many_measure(Ms), + + ok. + +many_module(M) -> + ["-module("++M++").", + "-compile(export_all).", + "f1() -> ok.", + "f2() -> ok.", + "f3() -> ok.", + "f4() -> ok."]. + +many_measure(Ms) -> + many_purge(Ms), + MsPrep1 = prepare_modules(Ms), + Us1 = ms(fun() -> many_load_seq(MsPrep1) end), + many_try_call(Ms), + many_purge(Ms), + MsPrep2 = prepare_modules(Ms), + Us2 = ms(fun() -> many_load_par(MsPrep2) end), + many_try_call(Ms), + io:format("# modules: ~9w\n" + "Sequential: ~9w µs\n" + "Parallel: ~9w µs\n" + "Ratio: ~9w\n", + [length(Ms),Us1,Us2,round(Us1/Us2)]), + ok. + +many_load_seq(Ms) -> + [erlang:finish_loading([M]) || M <- Ms], + ok. + +many_load_par(Ms) -> + erlang:finish_loading(Ms). + +many_purge(Ms) -> + _ = [catch erlang:purge_module(M) || {M,_} <- Ms], + ok. + +many_try_call(Ms) -> + _ = [begin + ok = M:f1(), + ok = M:f2(), + ok = M:f3(), + ok = M:f4() + end || {M,_} <- Ms], + ok. + +many_worker() -> + many_worker(lists:seq(1, 100)). + +many_worker(L) -> + N0 = length(L), + N1 = N0 * N0 * N0, + N2 = N1 div (N0 * N0), + N3 = N2 + 10, + _ = N3 - 10, + many_worker(L). + + +on_load(_Config) -> + On = make_modules(2, fun on_load_module/1), + OnPrep = prepare_modules(On), + {'EXIT',{system_limit,_}} = (catch erlang:finish_loading(OnPrep)), + + Normal = make_modules(1, fun on_load_normal/1), + Mixed = Normal ++ tl(On), + MixedPrep = prepare_modules(Mixed), + {'EXIT',{system_limit,_}} = (catch erlang:finish_loading(MixedPrep)), + + SingleOnPrep = tl(OnPrep), + {on_load,[OnLoadMod]} = erlang:finish_loading(SingleOnPrep), + ok = erlang:call_on_load_function(OnLoadMod), + + ok. + +on_load_module(M) -> + ["-module("++M++").", + "-on_load(f/0).", + "f() -> ok."]. + +on_load_normal(M) -> + ["-module("++M++")."]. + + +errors(_Config) -> + finish_loading_badarg(x), + finish_loading_badarg([x|y]), + finish_loading_badarg([x]), + finish_loading_badarg([<<>>]), + + Mods = make_modules(2, fun errors_module/1), + Ms = lists:sort([M || {M,_} <- Mods]), + Prep = prepare_modules(Mods), + {duplicated,Dups} = erlang:finish_loading(Prep ++ Prep), + Ms = lists:sort(Dups), + ok. + +finish_loading_badarg(Arg) -> + {'EXIT',{badarg,[{erlang,finish_loading,[Arg],_}|_]}} = + (catch erlang:finish_loading(Arg)). + +errors_module(M) -> + ["-module("++M++").", + "-export([f/0]).", + "f() -> ok."]. + +%%% +%%% Common utilities +%%% + +ms(Fun) -> + {Ms,ok} = timer:tc(Fun), + Ms. + +make_modules(0, _) -> + []; +make_modules(N, Fun) -> + U = erlang:unique_integer([positive]), + M0 = "m__" ++ integer_to_list(N) ++ "_" ++ integer_to_list(U), + Contents = Fun(M0), + Forms = [make_form(S) || S <- Contents], + {ok,M,Code} = compile:forms(Forms), + [{M,Code}|make_modules(N-1, Fun)]. + +make_form(S) -> + {ok,Toks,_} = erl_scan:string(S), + {ok,Form} = erl_parse:parse_form(Toks), + Form. + +prepare_modules(Ms) -> + [erlang:prepare_loading(M, Code) || {M,Code} <- Ms]. -- cgit v1.2.3 From 272985d3d1b36833ebd586e5f051ac3a4ab0cd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 3 Dec 2015 15:42:02 +0100 Subject: Add has_prepared_code_on_load/1 BIF --- erts/emulator/beam/beam_bif_load.c | 19 +++++++++++++++++++ erts/emulator/beam/bif.tab | 1 + erts/emulator/test/multi_load_SUITE.erl | 6 ++++++ erts/preloaded/src/erlang.erl | 10 +++++++++- 4 files changed, 35 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index d426b6eebb..0b47fc3586 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -139,6 +139,25 @@ prepare_loading_2(BIF_ALIST_2) BIF_RET(res); } +BIF_RETTYPE +has_prepared_code_on_load_1(BIF_ALIST_1) +{ + Eterm res; + ProcBin* pb; + + if (!ERTS_TERM_IS_MAGIC_BINARY(BIF_ARG_1)) { + error: + BIF_ERROR(BIF_P, BADARG); + } + + pb = (ProcBin*) binary_val(BIF_ARG_1); + res = erts_has_code_on_load(pb->val); + if (res == NIL) { + goto error; + } + BIF_RET(res); +} + struct m { Binary* code; Eterm module; diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 4efc055aaf..5c84b9c0eb 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -649,6 +649,7 @@ bif binary:split/2 bif binary:split/3 bif erts_debug:size_shared/1 bif erts_debug:copy_shared/1 +bif erlang:has_prepared_code_on_load/1 # # Obsolete diff --git a/erts/emulator/test/multi_load_SUITE.erl b/erts/emulator/test/multi_load_SUITE.erl index 33e6c15f8f..784b239116 100644 --- a/erts/emulator/test/multi_load_SUITE.erl +++ b/erts/emulator/test/multi_load_SUITE.erl @@ -126,6 +126,12 @@ on_load(_Config) -> MixedPrep = prepare_modules(Mixed), {'EXIT',{system_limit,_}} = (catch erlang:finish_loading(MixedPrep)), + [false,true] = [erlang:has_prepared_code_on_load(Code) || + Code <- MixedPrep], + {'EXIT',{badarg,_}} = (catch erlang:has_prepared_code_on_load(<<1,2,3>>)), + Magic = ets:match_spec_compile([{'_',[true],['$_']}]), + {'EXIT',{badarg,_}} = (catch erlang:has_prepared_code_on_load(Magic)), + SingleOnPrep = tl(OnPrep), {on_load,[OnLoadMod]} = erlang:finish_loading(SingleOnPrep), ok = erlang:call_on_load_function(OnLoadMod), diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index ab51cf385c..9bf8d13fde 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -105,7 +105,9 @@ -export([garbage_collect/0, garbage_collect/1, garbage_collect/2]). -export([garbage_collect_message_area/0, get/0, get/1, get_keys/0, get_keys/1]). -export([get_module_info/1, get_stacktrace/0, group_leader/0]). --export([group_leader/2, halt/0, halt/1, halt/2, hash/2, hibernate/3]). +-export([group_leader/2]). +-export([halt/0, halt/1, halt/2, hash/2, + has_prepared_code_on_load/1, hibernate/3]). -export([insert_element/3]). -export([integer_to_binary/1, integer_to_list/1]). -export([iolist_size/1, iolist_to_binary/1]). @@ -997,6 +999,12 @@ halt(_Status, _Options) -> hash(_Term, _Range) -> erlang:nif_error(undefined). +%% has_prepared_code_on_load/1 +-spec erlang:has_prepared_code_on_load(PreparedCode) -> boolean() when + PreparedCode :: binary(). +has_prepared_code_on_load(_PreparedCode) -> + erlang:nif_error(undefined). + %% hibernate/3 -spec erlang:hibernate(Module, Function, Args) -> no_return() when Module :: module(), -- cgit v1.2.3 From cd283583f8898b88bbcccc622c733fba82bd3e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 15 Dec 2015 12:20:26 +0100 Subject: Add erl_prim_loader:get_modules/3 When we are going to implement functions that can load many modules at once, we don't the erl_prim_loader server to become a bottleneck. Therefore, we need erl_prim_loader:get_modules/3 that can read many BEAM files in parallel. Note that we will not bother making reading from archive files or using the inet loader efficient. That can be done later if it turns out to be important. --- erts/preloaded/src/erl_prim_loader.erl | 133 +++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 824ed3435d..cbcced5512 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -55,6 +55,9 @@ %% Used by test suites -export([purge_archive_cache/0]). +%% Used by init and the code server. +-export([get_modules/3]). + -include_lib("kernel/include/file.hrl"). -type host() :: atom(). @@ -236,6 +239,16 @@ set_primary_archive(File, ArchiveBin, FileInfo, ParserFun) purge_archive_cache() -> request(purge_archive_cache). + +-spec get_modules([module()], + fun((atom(), string(), binary()) -> + {'ok',any()} | {'error',any()}), + [string()]) -> + {'ok',{[any()],[any()]}}. + +get_modules(Modules, Fun, Path) -> + request({get_modules,{Modules,Fun,Path}}). + request(Req) -> Loader = whereis(erl_prim_loader), Loader ! {self(),Req}, @@ -325,6 +338,8 @@ handle_request(Req, Paths, St0) -> {{ok,Paths},St0}; {get_file,File} -> handle_get_file(St0, Paths, File); + {get_modules,{Modules,Fun,ModPaths}} -> + handle_get_modules(St0, Modules, Fun, ModPaths); {list_dir,Dir} -> handle_list_dir(St0, Dir); {read_file_info,File} -> @@ -464,6 +479,124 @@ efile_timeout_handler(State, _Parent) -> prim_purge_cache(), State. +%%% -------------------------------------------------------- +%%% Read and process severals modules in parallel. +%%% -------------------------------------------------------- + +handle_get_modules(#state{loader=efile}=St, Ms, Process, Paths) -> + Primary = (St#state.prim_state)#prim_state.primary_archive, + Res = case efile_any_archives(Paths, Primary) of + false -> + efile_get_mods_par(Ms, Process, Paths); + true -> + Get = fun efile_get_file_from_port/3, + gm_get_mods(St, Get, Ms, Process, Paths) + end, + {Res,St}; +handle_get_modules(#state{loader=inet}=St, Ms, Process, Paths) -> + Get = fun inet_get_file_from_port/3, + {gm_get_mods(St, Get, Ms, Process, Paths),St}. + +efile_get_mods_par(Ms, Process, Paths) -> + Self = self(), + Ref = make_ref(), + GmSpawn = fun() -> + efile_gm_spawn({Self,Ref}, Ms, Process, Paths) + end, + _ = spawn_link(GmSpawn), + N = length(Ms), + efile_gm_recv(N, Ref, [], []). + +efile_any_archives([H|T], Primary) -> + case name_split(Primary, H) of + {file,_} -> efile_any_archives(T, Primary); + {archive,_,_} -> true + end; +efile_any_archives([], _) -> + false. + +efile_gm_recv(0, _Ref, Succ, Fail) -> + {ok,{Succ,Fail}}; +efile_gm_recv(N, Ref, Succ, Fail) -> + receive + {Ref,Mod,{ok,Res}} -> + efile_gm_recv(N-1, Ref, [{Mod,Res}|Succ], Fail); + {Ref,Mod,{error,Res}} -> + efile_gm_recv(N-1, Ref, Succ, [{Mod,Res}|Fail]) + end. + +efile_gm_spawn(ParentRef, Ms, Process, Paths) -> + efile_gm_spawn_1(0, Ms, ParentRef, Process, Paths). + +efile_gm_spawn_1(N, Ms, ParentRef, Process, Paths) when N >= 32 -> + receive + {'DOWN',_,process,_,_} -> + efile_gm_spawn_1(N-1, Ms, ParentRef, Process, Paths) + end; +efile_gm_spawn_1(N, [M|Ms], ParentRef, Process, Paths) -> + Get = fun() -> efile_gm_get(Paths, M, ParentRef, Process) end, + _ = spawn_monitor(Get), + efile_gm_spawn_1(N+1, Ms, ParentRef, Process, Paths); +efile_gm_spawn_1(_, [], _, _, _) -> + ok. + +efile_gm_get(Paths, Mod, ParentRef, Process) -> + File = atom_to_list(Mod) ++ init:objfile_extension(), + efile_gm_get_1(Paths, File, Mod, ParentRef, Process). + +efile_gm_get_1([P|Ps], File0, Mod, {Parent,Ref}=PR, Process) -> + File = join(P, File0), + Res = try prim_file:read_file(File) of + {ok,Bin} -> + gm_process(Mod, File, Bin, Process); + {error,enoent} -> + efile_gm_get_1(Ps, File0, Mod, PR, Process); + Error -> + check_file_result(get_modules, File, Error), + Error + catch + _:Reason -> + {error,{crash,Reason}} + end, + Parent ! {Ref,Mod,Res}; +efile_gm_get_1([], _, Mod, {Parent,Ref}, _Process) -> + Parent ! {Ref,Mod,{error,enoent}}. + +gm_get_mods(St, Get, Ms, Process, Paths) -> + gm_get_mods(St, Get, Ms, Process, Paths, [], []). + +gm_get_mods(St, Get, [M|Ms], Process, Paths, Succ, Fail) -> + File = atom_to_list(M) ++ init:objfile_extension(), + case gm_arch_get(St, Get, M, File, Paths, Process) of + {ok,Res} -> + gm_get_mods(St, Get, Ms, Process, Paths, + [{M,Res}|Succ], Fail); + {error,Res} -> + gm_get_mods(St, Get, Ms, Process, Paths, + Succ, [{M,Res}|Fail]) + end; +gm_get_mods(_St, _Get, [], _, _, Succ, Fail) -> + {ok,{Succ,Fail}}. + +gm_arch_get(St, Get, Mod, File, Paths, Process) -> + case Get(St, File, Paths) of + {{error,_}=E,_} -> + E; + {{ok,Bin,Full},_} -> + gm_process(Mod, Full, Bin, Process) + end. + +gm_process(Mod, File, Bin, Process) -> + try Process(Mod, File, Bin) of + {ok,_}=Res -> Res; + {error,_}=Res -> Res; + Other -> {error,{bad_return,Other}} + catch + _:Error -> + {error,{crash,Error}} + end. + + %%% -------------------------------------------------------- %%% Functions which handle inet prim_loader %%% -------------------------------------------------------- -- cgit v1.2.3 From b9869e4940c991783be56d12507f127c85b8bac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 19 Feb 2016 09:51:19 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 49836 -> 55528 bytes erts/preloaded/ebin/erlang.beam | Bin 101968 -> 102148 bytes 2 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index ca9e6bd20f..6d777fa811 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index b353129a34..db17c53ff3 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ -- cgit v1.2.3 From 3121171f56c2d4a273b89a9d1d47d9f50e027770 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 25 Feb 2016 18:30:27 +0100 Subject: otp: Add gcc option -Werror=implicit to get errors for missing function and variable declarations. --- erts/configure.in | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index ba735fe921..8d178107a0 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -584,6 +584,7 @@ fi if test "x$GCC" = xyes; then # Treat certain GCC warnings as errors LM_TRY_ENABLE_CFLAG([-Werror=return-type], [WERRORFLAGS]) + LM_TRY_ENABLE_CFLAG([-Werror=implicit], [WERRORFLAGS]) # until the emulator can handle this, I suggest we turn it off! #WFLAGS="-Wall -Wshadow -Wcast-qual -Wmissing-declarations" -- cgit v1.2.3 From 77046ef3d886574a81d0ff517d8c1e6f3b6c0692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 26 Feb 2016 06:35:40 +0100 Subject: otp_SUITE: Remove handling of test_server application --- erts/test/otp_SUITE.erl | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) (limited to 'erts') diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 6777c205b0..2efb5ae200 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -298,7 +298,7 @@ call_to_now_0(Config) when is_list(Config) -> Apps = [asn1,common_test,compiler,debugger,dialyzer, gs,kernel,mnesia,observer,parsetools,reltool, runtime_tools,sasl,stdlib,syntax_tools, - test_server,tools], + tools], not_recommended_calls(Config, Apps, {erlang,now,0}). not_recommended_calls(Config, Apps0, MFA) -> @@ -459,24 +459,7 @@ runtime_dependencies(Config) -> end, {undefined, []}, SAE), - [] = lists:filter(fun ({missing_runtime_dependency, - AppFile, - common_test}) -> - %% The test_server app is contaminated by - %% common_test when run in a source tree. It - %% should however *not* be contaminated - %% when run in an installation. - case {filename:basename(AppFile), - is_run_in_src_tree()} of - {"test_server.app", true} -> - false; - _ -> - true - end; - (_) -> - true - end, - check_apps_deps([AppDep|AppDeps], IgnoreApps)), + check_apps_deps([AppDep|AppDeps], IgnoreApps), case IgnoreApps of [] -> ok; @@ -488,17 +471,6 @@ runtime_dependencies(Config) -> {comment, Comment} end. -is_run_in_src_tree() -> - %% At least currently run_erl is not present in /bin - %% in the source tree, but present in /bin of an - %% ordinary installation. - case file:read_file_info(filename:join([code:root_dir(), - "bin", - "run_erl"])) of - {ok, _} -> false; - {error, _} -> true - end. - have_rdep(_App, [], _Dep) -> false; have_rdep(App, [RDep | RDeps], Dep) -> -- cgit v1.2.3 From 26dab380834cd38fee153f240e12a9c571d29c71 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 29 Feb 2016 11:23:32 +0100 Subject: Fix build wihtout thread support --- erts/emulator/sys/unix/erl_child_setup.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 4e61530cf1..8dd14bbac6 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -61,6 +61,7 @@ #include "erl_driver.h" #include "sys_uds.h" #include "hash.h" +#include "erl_term.h" #include "erl_child_setup.h" #define SET_CLOEXEC(fd) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) -- cgit v1.2.3 From 74932408e62100dec7ece2365ee221e68a809de7 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 26 Feb 2016 11:00:33 +0100 Subject: erts: Fix run_erl syslog prototypes for freebsd --- erts/configure.in | 2 +- erts/etc/unix/run_erl.c | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index ba735fe921..e8d904808c 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1625,7 +1625,7 @@ AC_CHECK_HEADERS(fcntl.h limits.h unistd.h syslog.h dlfcn.h ieeefp.h \ sys/ioctl.h sys/time.h sys/uio.h \ sys/socket.h sys/sockio.h sys/socketio.h \ net/errno.h malloc.h arpa/nameser.h libdlpi.h \ - pty.h util.h utmp.h langinfo.h poll.h sdkddkver.h) + pty.h util.h libutil.h utmp.h langinfo.h poll.h sdkddkver.h) AC_CHECK_MEMBERS([struct ifreq.ifr_hwaddr], [], [], [#ifdef __WIN32__ diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index 44efb975ba..ff9bbbafe2 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -41,7 +41,7 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#ifdef HAVE_WORKING_POSIX_OPENPT +#if defined(HAVE_WORKING_POSIX_OPENPT) && !(defined(__FreeBSD__) || defined(__DragonFly__)) #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif @@ -64,10 +64,6 @@ #include #include -#ifdef __ANDROID__ -# include -#endif - #ifdef HAVE_SYSLOG_H # include #endif @@ -77,6 +73,9 @@ #ifdef HAVE_UTMP_H # include #endif +#ifdef HAVE_LIBUTIL_H +# include +#endif #ifdef HAVE_UTIL_H # include #endif -- cgit v1.2.3 From b1e4836bcfbfdec0504219ed490c5f53860d8106 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 1 Mar 2016 15:19:36 +0100 Subject: Skips any sockets with unsupported protocols. Patch from end of PR 864 Fixes problem reported in the PR by jeckersb In this error case, a negative file descriptor is now saved at listensock[i], which leads to a buffer overflow later on at https://github.com/msantos/otp/blob/epmd-IPv6-node-reg2/erts/epmd/src/epmd_srv.c#L498 when calling FD_ISSET(listensock[i],&read_mask). To reproduce this: Completely disable IPv6 with ipv6.disable=1 on the kernel command line Build with fortified code (RHEL/CentOS default): CFLAGS='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fno-strict-aliasing' --- erts/epmd/src/epmd_srv.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 55ec0f7b6c..e1bac99ef9 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -380,7 +380,7 @@ void run(EpmdVars *g) epmd_cleanup_exit(g,1); } } - g->listenfd[i] = listensock[i]; + g->listenfd[bound++] = listensock[i]; #if HAVE_DECL_IPV6_V6ONLY opt = 1; @@ -439,8 +439,6 @@ void run(EpmdVars *g) } } - bound++; - if(listen(listensock[i], SOMAXCONN) < 0) { dbg_perror(g,"failed to listen on socket"); epmd_cleanup_exit(g,1); @@ -451,6 +449,7 @@ void run(EpmdVars *g) dbg_perror(g,"unable to bind any address"); epmd_cleanup_exit(g,1); } + num_sockets = bound; #ifdef HAVE_SYSTEMD_DAEMON } sd_notifyf(0, "READY=1\n" @@ -495,8 +494,8 @@ void run(EpmdVars *g) } for (i = 0; i < num_sockets; i++) - if (FD_ISSET(listensock[i],&read_mask)) { - if (do_accept(g, listensock[i]) && g->active_conn < g->max_conn) { + if (FD_ISSET(g->listenfd[i],&read_mask)) { + if (do_accept(g, g->listenfd[i]) && g->active_conn < g->max_conn) { /* * The accept() succeeded, and we have at least one file * descriptor still free, which means that another accept() -- cgit v1.2.3 From e1b95c4a48e08fb4bc42626db8614a3cd46d2a5e Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Thu, 3 Mar 2016 10:38:18 +0100 Subject: erts: Use 'bit string' in The Abstract Format document --- erts/doc/src/absform.xml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index ccdecf44ec..13756ddfdc 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -182,7 +182,7 @@

Individual patterns are represented as follows:

If P is an atomic literal L, then Rep(P) = Rep(L). - If P is a binary pattern + If P is a bit string pattern <<P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>>, where each Size_i is an expression that can be evaluated to an integer and each TSL_i is a type specificer list, then @@ -241,12 +241,13 @@

An expression E is one of the following alternatives:

If E is an atomic literal L, then Rep(E) = Rep(L). - If E is a binary comprehension + If E is a bit string comprehension <<E_0 || Q_1, ..., Q_k>>, where each Q_i is a qualifier, then Rep(E) = {bc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}. For Rep(Q), see below. - If E is a binary constructor <<E_1:Size_1/TSL_1, ..., E_k:Size_k/TSL_k>>, + If E is a bit string constructor + <<E_1:Size_1/TSL_1, ..., E_k:Size_k/TSL_k>>, where each Size_i is an expression and each TSL_i is a type specificer list, then Rep(E) = {bin,LINE,[{bin_element,LINE,Rep(E_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(E_k),Rep(Size_k),Rep(TSL_k)}]}. @@ -386,16 +387,17 @@ If Q is a generator P <- E, where P is a pattern and E is an expression, then Rep(Q) = {generate,LINE,Rep(P),Rep(E)}. - If Q is a generator P <= E, where P is + If Q is a bit string generator + P <= E, where P is a pattern and E is an expression, then Rep(Q) = {b_generate,LINE,Rep(P),Rep(E)}.
- Binary Element Type Specifiers -

A type specifier list TSL for a binary element is a sequence of type - specifiers TS_1 - ... - TS_k, and + Bit String Element Type Specifiers +

A type specifier list TSL for a bit string element is a sequence + of type specifiers TS_1 - ... - TS_k, and Rep(TSL) = [Rep(TS_1), ..., Rep(TS_k)].

If TS is a type specifier A, where A is an atom, @@ -473,7 +475,7 @@

A guard test Gt is one of the following alternatives:

If Gt is an atomic literal L, then Rep(Gt) = Rep(L). - If Gt is a binary constructor + If Gt is a bit string constructor <<Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>>, where each Size_i is a guard test and each TSL_i is a type specificer list, then @@ -540,7 +542,7 @@ {ann_type,LINE,[Rep(A),Rep(T_0)]}. If T is an atom or integer literal L, then Rep(T) = Rep(L). - If T is a bitstring type <<_:M,_:_*N>>, + If T is a bit string type <<_:M,_:_*N>>, where M and N are singleton integer types, then Rep(T) = {type,LINE,binary,[Rep(M),Rep(N)]}. If T is the empty list type [], then Rep(T) = -- cgit v1.2.3 From 21f3a5c817d9a2a53670c8ac57c7ec5171bb25ed Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Thu, 3 Mar 2016 13:37:03 +0100 Subject: test: do not divide by zero --- erts/emulator/test/distribution_SUITE.erl | 2 +- erts/emulator/test/estone_SUITE.erl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index d71cedbdc5..151fab0a0e 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -164,7 +164,7 @@ bulk_send(Terms, BinSize) -> ?line stop_node(Node), ?line test_server:timetrap_cancel(Dog), - {comment, integer_to_list(trunc(Size/1024/Elapsed+0.5)) ++ " K/s"}. + {comment, integer_to_list(trunc(Size/1024/max(1,Elapsed)+0.5)) ++ " K/s"}. bulk_sendsend(Terms, BinSize) -> {Rate1, MonitorCount1} = bulk_sendsend2(Terms, BinSize, 5), diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index dc8f0aaee9..d49ab3de4d 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -382,11 +382,11 @@ apply_micro(M) -> {weight_percentage, M#micro.weight}, {loops, M#micro.loops}, {microsecs,MicroSecs}, - {estones, (M#micro.weight * M#micro.weight * ?STONEFACTOR) div MicroSecs}, + {estones, (M#micro.weight * M#micro.weight * ?STONEFACTOR) div max(1,MicroSecs)}, {gcs, GC1 - GC0}, {kilo_word_reclaimed, (Words1 - Words0) div 1000}, {kilo_reductions, Reds div 1000}, - {gc_intensity, gci(Elapsed, GC1 - GC0, Words1 - Words0)}]. + {gc_intensity, gci(max(1,Elapsed), GC1 - GC0, Words1 - Words0)}]. monotonic_time() -> try erlang:monotonic_time() catch error:undef -> erlang:now() end. -- cgit v1.2.3 From e4fd76bdd9dd15547531947aaecf487385a6d796 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 8 Mar 2016 17:15:18 +0100 Subject: erts: Fix alloc_SUITE:rbtree and migration for win64 One little (unsigned long) left behind. --- erts/emulator/beam/erl_alloc_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 18312eacde..d9eaa7b32c 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -6049,7 +6049,7 @@ erts_alcu_test(UWord op, UWord a1, UWord a2) case 0x019: return (UWord) PREV_BLK((Block_t *) a1); case 0x01a: return (UWord) IS_MBC_FIRST_BLK((Allctr_t*)a1, (Block_t *) a2); case 0x01b: return (UWord) sizeof(Unit_t); - case 0x01c: return (unsigned long) BLK_TO_MBC((Block_t*) a1); + case 0x01c: return (UWord) BLK_TO_MBC((Block_t*) a1); case 0x01d: ((Allctr_t*) a1)->add_mbc((Allctr_t*)a1, (Carrier_t*)a2); break; case 0x01e: ((Allctr_t*) a1)->remove_mbc((Allctr_t*)a1, (Carrier_t*)a2); break; #ifdef ERTS_SMP -- cgit v1.2.3 From a062b686d387c15a631a0794ecf9db0d64ea14be Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 9 Mar 2016 18:12:36 +0100 Subject: erts: Fix wobbling tests in nif_SUITE No point in checking tmp_alloc instance 0 as any non-scheduler thread could race us. --- erts/emulator/test/nif_SUITE.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index e10460ce78..2400505159 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1714,7 +1714,9 @@ tmpmem() -> false -> undefined; MemInfo -> MSBCS = lists:foldl( - fun ({instance, _, L}, Acc) -> + fun ({instance, 0, _}, Acc) -> + Acc; % Ignore instance 0 + ({instance, _, L}, Acc) -> {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L), {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L), [MBCS,SBCS | Acc] -- cgit v1.2.3 From 3a8f2995caffacddabb3363453c6a7dbc33d6329 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 10 Mar 2016 13:10:51 +0100 Subject: Prepare release --- erts/doc/src/notes.xml | 19 +++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 9ff2e5de08..24bcc76e4b 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,25 @@

This document describes the changes made to the ERTS application.

+
Erts 6.4.1.6 + +
Fixed Bugs and Malfunctions + + +

+ When calling garbage_collect/[1,2] or + check_process_code/[2,3] from a process with a + higher priority than the priority of the process operated + on, the run queues could end up in an inconsistent state. + This bug has now been fixed.

+

+ Own Id: OTP-13298 Aux Id: OTP-11388

+
+
+
+ +
+
Erts 6.4.1.5
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 98946f4797..2aad279414 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 6.4.1.5 +VSN = 6.4.1.6 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 207a569d4caf80b10a54eec2220ac672a3377f00 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 22 Jan 2016 17:48:49 +0100 Subject: Improved scheduler suspend functionality - The calling process is now suspended while synchronizing scheduler suspends via erlang:system_flag(schedulers_online, _) and erlang:system_flag(multi_scheduling, _), instead of blocking the scheduler thread in the BIF call waiting for the operation to synchronize. Besides releasing the scheduler for other work (or immediate suspend) it also makes it possible to abort the operation by killing the process. - erlang:system_flag(schedulers_online, _) now only wait for normal schedulers to complete before it returns. This since it may take a very long time before all dirty schedulers suspends. - erlang:system_flag(multi_scheduling, block_normal|unblock_normal) which only operate on normal schedulers has been introduced. This since there are use cases where suspend of dirty schedulers are not of interest (hipe loader). - erlang:system_flag(multi_scheduling, block) still blocks all dirty schedulers as well as all normal schedulers except one since it is hard to redefine what multi scheduling block means. - The three operations: - changing amount of schedulers online - blocking/unblocking normal multi scheduling - blocking/unblocking full multi scheduling can now be done in parallel. This is important since otherwise a full multi scheduling block would potentially delay the other operations for a very long time. --- erts/doc/src/erlang.xml | 99 +- erts/emulator/beam/atom.names | 6 + erts/emulator/beam/bif.c | 66 +- erts/emulator/beam/bif.h | 25 +- erts/emulator/beam/erl_bif_info.c | 172 ++- erts/emulator/beam/erl_process.c | 2162 +++++++++++++++----------------- erts/emulator/beam/erl_process.h | 26 +- erts/emulator/test/scheduler_SUITE.erl | 176 ++- erts/preloaded/ebin/erlang.beam | Bin 102148 -> 102200 bytes erts/preloaded/src/erlang.erl | 9 +- 10 files changed, 1415 insertions(+), 1326 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 1f8e89768c..350a8506f5 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6469,32 +6469,44 @@ ok

If multi-scheduling is enabled, more than one scheduler thread is used by the emulator. Multi-scheduling can be - blocked. When multi-scheduling is blocked, only - one scheduler thread schedules Erlang processes.

+ blocked in two different ways. Either all schedulers but + one is blocked, or all normal schedulers but + one is blocked. When only normal schedulers are blocked + dirty schedulers are free to continue to schedule + processes.

If BlockState =:= block, multi-scheduling is - blocked. If BlockState =:= unblock and no one + blocked. That is, one and only one scheduler thread will + execute. If BlockState =:= unblock and no one else blocks multi-scheduling, and this process has blocked only once, multi-scheduling is unblocked.

-

One process can block multi-scheduling multiple times. - If a process has blocked multiple times, it must - unblock exactly as many times as it has blocked before it - has released its multi-scheduling block. If a process that - has blocked multi-scheduling exits, it releases its - blocking of multi-scheduling.

+

If BlockState =:= block_normal, normal + multi-scheduling is blocked. That is, only one normal scheduler + thread will execute, but multiple dirty schedulers may execute. + If BlockState =:= unblock_normal and no one + else blocks normal multi-scheduling, and this process has + blocked only once, normal multi-scheduling is unblocked.

+

One process can block multi-scheduling as well as normal + multi-scheduling multiple times. If a process has blocked + multiple times, it must unblock exactly as many times as it + has blocked before it has released its multi-scheduling + block. If a process that has blocked multi-scheduling or normal + multi scheduling exits, it automatically releases its blocking + of multi-scheduling and normal multi-scheduling.

The return values are disabled, blocked, - or enabled. The returned value describes the - state just after the call to + blocked_normal, or enabled. The returned value + describes the state just after the call to erlang:system_flag(multi_scheduling, BlockState) has been made. For information about the return values, see erlang:system_info(multi_scheduling).

-

Blocking of multi-scheduling is normally not needed. - If you feel that you need to block multi-scheduling, - consider it a few more times again. Blocking multi-scheduling - is only to be used as a last resort, as it is most likely - a very inefficient way to solve the problem.

+

Blocking of multi-scheduling and normal multi-scheduling + is normally not needed. If you feel that you need to use these + features, consider it a few more times again. Blocking + multi-scheduling is only to be used as a last resort, as it is + most likely a very inefficient way to solve the problem.

See also erlang:system_info(multi_scheduling), + erlang:system_info(normal_multi_scheduling_blockers), erlang:system_info(multi_scheduling_blockers), and erlang:system_info(schedulers).

@@ -7329,6 +7341,16 @@ ok where MinHeapSize is the current system-wide minimum heap size for spawned processes.

+ message_queue_data + +

Returns the default value of the message_queue_data + process flag which is either off_heap, on_heap, or mixed. + This default is set by the erl command line argument + +xmqd. For more information on the + message_queue_data process flag, see documentation of + process_flag(message_queue_data, + MQD).

+
min_bin_vheap_size

Returns {min_bin_vheap_size, @@ -7348,7 +7370,8 @@ ok multi_scheduling -

Returns disabled, blocked, or enabled:

+

Returns disabled, blocked, blocked_normal, + or enabled:

disabled @@ -7359,14 +7382,22 @@ ok blocked

The emulator has more than one scheduler thread, - but all scheduler threads except one are blocked, - that is, only one scheduler thread schedules + but all scheduler threads except one are blocked. + That is, only one scheduler thread schedules Erlang processes and executes Erlang code.

+ blocked_normal + +

The emulator has more than one scheduler thread, + but all normal scheduler threads except one are + blocked. Note that dirty schedulers are not + blocked, and may schedule Erlang processes and + execute native code.

+
enabled

The emulator has more than one scheduler thread, - and no scheduler threads are blocked, that is, + and no scheduler threads are blocked. That is, all available scheduler threads schedule Erlang processes and execute Erlang code.

@@ -7374,6 +7405,7 @@ ok

See also erlang:system_flag(multi_scheduling, BlockState), erlang:system_info(multi_scheduling_blockers), + erlang:system_info(normal_multi_scheduling_blockers), and erlang:system_info(schedulers).

@@ -7390,6 +7422,8 @@ ok

See also erlang:system_flag(multi_scheduling, BlockState), erlang:system_info(multi_scheduling), + erlang:system_info(normal_multi_scheduling_blockers), + and erlang:system_info(schedulers).

@@ -7399,15 +7433,23 @@ ok used by the runtime system. It is on the form "<major ver>.<minor ver>".

- message_queue_data + normal_multi_scheduling_blockers -

Returns the default value of the message_queue_data - process flag which is either off_heap, on_heap, or mixed. - This default is set by the erl command line argument - +xmqd. For more information on the - message_queue_data process flag, see documentation of - process_flag(message_queue_data, - MQD).

+ +

Returns a list of Pids when + normal multi-scheduling is blocked (i.e. all normal schedulers + but one is blocked), otherwise the empty list is returned. + The Pids in the list represent all the + processes currently blocking normal multi-scheduling. + A Pid occurs only once in the list, even if + the corresponding process has blocked multiple times.

+

See also + erlang:system_flag(multi_scheduling, BlockState), + erlang:system_info(multi_scheduling), + erlang:system_info(multi_scheduling_blockers), + + and + erlang:system_info(schedulers).

otp_release @@ -7650,6 +7692,7 @@ ok erlang:system_info(scheduler_id), erlang:system_flag(multi_scheduling, BlockState), erlang:system_info(multi_scheduling), + erlang:system_info(normal_multi_scheduling_blockers) and erlang:system_info(multi_scheduling_blockers).

diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index dca8b503bf..169b071cd7 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -125,7 +125,9 @@ atom binary_longest_suffix_trap atom binary_to_list_continue atom binary_to_term_trap atom block +atom block_normal atom blocked +atom blocked_normal atom bm atom bnot atom bor @@ -190,7 +192,9 @@ atom dexit atom depth atom dgroup_leader atom dictionary +atom dirty_cpu atom dirty_cpu_schedulers_online +atom dirty_io atom disable_trace atom disabled atom display_items @@ -305,6 +309,7 @@ atom index atom infinity atom info atom info_msg +atom init atom initial_call atom input atom internal @@ -612,6 +617,7 @@ atom use_stdio atom used atom utf8 atom unblock +atom unblock_normal atom uniq atom unless_suspending atom unloaded diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 6ab75496a0..37bb28c6f8 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4197,22 +4197,33 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) Sint n; if (BIF_ARG_1 == am_multi_scheduling) { - if (BIF_ARG_2 == am_block || BIF_ARG_2 == am_unblock) { + if (BIF_ARG_2 == am_block || BIF_ARG_2 == am_unblock + || BIF_ARG_2 == am_block_normal || BIF_ARG_2 == am_unblock_normal) { #ifndef ERTS_SMP BIF_RET(am_disabled); #else + int block = (BIF_ARG_2 == am_block + || BIF_ARG_2 == am_block_normal); + int normal = (BIF_ARG_2 == am_block_normal + || BIF_ARG_2 == am_unblock_normal); if (erts_no_schedulers == 1) BIF_RET(am_disabled); else { switch (erts_block_multi_scheduling(BIF_P, ERTS_PROC_LOCK_MAIN, - BIF_ARG_2 == am_block, + block, + normal, 0)) { case ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED: BIF_RET(am_blocked); + case ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED: + BIF_RET(am_blocked_normal); case ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED: ERTS_BIF_YIELD_RETURN_X(BIF_P, am_blocked, am_multi_scheduling); + case ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED: + ERTS_BIF_YIELD_RETURN_X(BIF_P, am_blocked_normal, + am_multi_scheduling); case ERTS_SCHDLR_SSPND_DONE: BIF_RET(am_enabled); case ERTS_SCHDLR_SSPND_YIELD_RESTART: @@ -4245,11 +4256,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) switch (erts_set_schedulers_online(BIF_P, ERTS_PROC_LOCK_MAIN, signed_val(BIF_ARG_2), - &old_no -#ifdef ERTS_DIRTY_SCHEDULERS - , 0 -#endif - )) { + &old_no, 0)) { case ERTS_SCHDLR_SSPND_DONE: BIF_RET(make_small(old_no)); case ERTS_SCHDLR_SSPND_YIELD_RESTART: @@ -4619,29 +4626,27 @@ BIF_RETTYPE erts_internal_cmp_term_2(BIF_ALIST_2) { /* * Processes doing yield on return in a bif ends up in bif_return_trap(). */ -static BIF_RETTYPE bif_return_trap( -#ifdef DEBUG - BIF_ALIST_2 -#else - BIF_ALIST_1 -#endif - ) +static BIF_RETTYPE bif_return_trap(BIF_ALIST_2) { -#ifdef DEBUG + Eterm res = BIF_ARG_1; + switch (BIF_ARG_2) { - case am_multi_scheduling: #ifdef ERTS_SMP - erts_dbg_multi_scheduling_return_trap(BIF_P, BIF_ARG_1); -#endif - break; - case am_schedulers_online: + case am_multi_scheduling: { + int msb = erts_is_multi_scheduling_blocked(); + if (msb > 0) + res = am_blocked; + else if (msb < 0) + res = am_blocked_normal; + else + ERTS_INTERNAL_ERROR("Unexpected multi scheduling block state"); break; + } +#endif default: break; } -#endif - - BIF_RET(BIF_ARG_1); + BIF_RET(res); } /* @@ -4746,17 +4751,12 @@ void erts_init_bif(void) erts_smp_atomic_init_nob(&erts_dead_ports_ptr, (erts_aint_t) NULL); /* - * bif_return_trap/1 is a hidden BIF that bifs that need to - * yield the calling process traps to. The only thing it does: - * return the value passed as argument. + * bif_return_trap/2 is a hidden BIF that bifs that need to + * yield the calling process traps to. */ - erts_init_trap_export(&bif_return_trap_export, am_erlang, am_bif_return_trap, -#ifdef DEBUG - 2 -#else - 1 -#endif - , &bif_return_trap); + erts_init_trap_export(&bif_return_trap_export, + am_erlang, am_bif_return_trap, 2, + &bif_return_trap); erts_await_result = erts_export_put(am_erts_internal, am_await_result, diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index a62eddf36b..46af552b39 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -338,37 +338,20 @@ do { \ } while(0) extern Export bif_return_trap_export; -#ifdef DEBUG -#define ERTS_BIF_PREP_YIELD_RETURN_X(RET, P, VAL, DEBUG_VAL) \ +#define ERTS_BIF_PREP_YIELD_RETURN_X(RET, P, VAL, OP) \ do { \ ERTS_VBUMP_ALL_REDS(P); \ - ERTS_BIF_PREP_TRAP2(RET, &bif_return_trap_export, (P), (VAL), \ - (DEBUG_VAL)); \ + ERTS_BIF_PREP_TRAP2(RET, &bif_return_trap_export, (P), (VAL), (OP));\ } while (0) -#else -#define ERTS_BIF_PREP_YIELD_RETURN_X(RET, P, VAL, DEBUG_VAL) \ -do { \ - ERTS_VBUMP_ALL_REDS(P); \ - ERTS_BIF_PREP_TRAP1(RET, &bif_return_trap_export, (P), (VAL)); \ -} while (0) -#endif #define ERTS_BIF_PREP_YIELD_RETURN(RET, P, VAL) \ ERTS_BIF_PREP_YIELD_RETURN_X(RET, (P), (VAL), am_undefined) -#ifdef DEBUG -#define ERTS_BIF_YIELD_RETURN_X(P, VAL, DEBUG_VAL) \ +#define ERTS_BIF_YIELD_RETURN_X(P, VAL, OP) \ do { \ ERTS_VBUMP_ALL_REDS(P); \ - BIF_TRAP2(&bif_return_trap_export, (P), (VAL), (DEBUG_VAL)); \ + BIF_TRAP2(&bif_return_trap_export, (P), (VAL), (OP)); \ } while (0) -#else -#define ERTS_BIF_YIELD_RETURN_X(P, VAL, DEBUG_VAL) \ -do { \ - ERTS_VBUMP_ALL_REDS(P); \ - BIF_TRAP1(&bif_return_trap_export, (P), (VAL)); \ -} while (0) -#endif #define ERTS_BIF_RETURN_YIELD(P) ERTS_VBUMP_ALL_REDS((P)) diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 39eb257c2c..8c748c9bf7 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -320,13 +320,11 @@ erts_print_system_version(int to, void *arg, Process *c_p) char *ov = otp_version; #ifdef ERTS_SMP Uint total, online, active; -#ifdef ERTS_DIRTY_SCHEDULERS Uint dirty_cpu, dirty_cpu_onln, dirty_io; - (void) erts_schedulers_state(&total, &online, &active, &dirty_cpu, &dirty_cpu_onln, &dirty_io, 0); -#else - (void) erts_schedulers_state(&total, &online, &active, NULL, NULL, NULL, 0); -#endif + erts_schedulers_state(&total, &online, &active, + &dirty_cpu, &dirty_cpu_onln, NULL, + &dirty_io, NULL); #endif for (i = 0; i < sizeof(otp_version)-4; i++) { if (ov[i] == '-' && ov[i+1] == 'r' && ov[i+2] == 'c') @@ -2044,12 +2042,18 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) #ifndef ERTS_SMP BIF_RET(am_disabled); #else +#ifndef ERTS_DIRTY_SCHEDULERS if (erts_no_schedulers == 1) BIF_RET(am_disabled); - else { - BIF_RET(erts_is_multi_scheduling_blocked() - ? am_blocked - : am_enabled); + else +#endif + { + int msb = erts_is_multi_scheduling_blocked(); + BIF_RET(!msb + ? am_enabled + : (msb > 0 + ? am_blocked + : am_blocked_normal)); } #endif } else if (BIF_ARG_1 == am_build_type) { @@ -2518,77 +2522,120 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) res = TUPLE3(hp, make_small(1), make_small(1), make_small(1)); BIF_RET(res); #else + Eterm *hp; Uint total, online, active; - switch (erts_schedulers_state(&total, - &online, - &active, - NULL, - NULL, - NULL, - 1)) { - case ERTS_SCHDLR_SSPND_DONE: { - Eterm *hp = HAlloc(BIF_P, 4); - res = TUPLE3(hp, - make_small(total), - make_small(online), - make_small(active)); - BIF_RET(res); + erts_schedulers_state(&total, &online, &active, + NULL, NULL, NULL, NULL, NULL); + hp = HAlloc(BIF_P, 4); + res = TUPLE3(hp, + make_small(total), + make_small(online), + make_small(active)); + BIF_RET(res); +#endif + } else if (ERTS_IS_ATOM_STR("schedulers_state", BIF_ARG_1)) { +#ifndef ERTS_SMP + Eterm *hp = HAlloc(BIF_P, 4); + res = TUPLE3(hp, make_small(1), make_small(1), make_small(1)); + BIF_RET(res); +#else + Eterm *hp; + Uint total, online, active; + erts_schedulers_state(&total, &online, &active, + NULL, NULL, NULL, NULL, NULL); + hp = HAlloc(BIF_P, 4); + res = TUPLE3(hp, + make_small(total), + make_small(online), + make_small(active)); + BIF_RET(res); +#endif + } else if (ERTS_IS_ATOM_STR("all_schedulers_state", BIF_ARG_1)) { +#ifndef ERTS_SMP + Eterm *hp = HAlloc(BIF_P, 2+5); + res = CONS(hp+5, + TUPLE4(hp, + am_normal, + make_small(1), + make_small(1), + make_small(1)), + NIL); + BIF_RET(res); +#else + Eterm *hp, tpl; + Uint sz, total, online, active, + dirty_cpu_total, dirty_cpu_online, dirty_cpu_active, + dirty_io_total, dirty_io_active; + erts_schedulers_state(&total, &online, &active, + &dirty_cpu_total, &dirty_cpu_online, &dirty_cpu_active, + &dirty_io_total, &dirty_io_active); + + sz = 2+5; + if (dirty_cpu_total) + sz += 2+5; + if (dirty_io_total) + sz += 2+5; + + hp = HAlloc(BIF_P, sz); + + res = NIL; + if (dirty_io_total) { + tpl = TUPLE4(hp, + am_dirty_io, + make_small(dirty_io_total), + make_small(dirty_io_total), + make_small(dirty_io_active)); + hp += 5; + res = CONS(hp, tpl, res); + hp += 2; } - case ERTS_SCHDLR_SSPND_YIELD_RESTART: - ERTS_VBUMP_ALL_REDS(BIF_P); - BIF_TRAP1(bif_export[BIF_system_info_1], - BIF_P, BIF_ARG_1); - default: - ASSERT(0); - BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); + if (dirty_cpu_total) { + tpl = TUPLE4(hp, + am_dirty_cpu, + make_small(dirty_cpu_total), + make_small(dirty_cpu_online), + make_small(dirty_cpu_active)); + hp += 5; + res = CONS(hp, tpl, res); + hp += 2; } + tpl = TUPLE4(hp, + am_normal, + make_small(total), + make_small(online), + make_small(active)); + hp += 5; + res = CONS(hp, tpl, res); + BIF_RET(res); #endif } else if (ERTS_IS_ATOM_STR("schedulers_online", BIF_ARG_1)) { #ifndef ERTS_SMP BIF_RET(make_small(1)); #else - Uint total, online, active; - switch (erts_schedulers_state(&total, &online, &active, NULL, NULL, NULL, 1)) { - case ERTS_SCHDLR_SSPND_DONE: - BIF_RET(make_small(online)); - case ERTS_SCHDLR_SSPND_YIELD_RESTART: - ERTS_VBUMP_ALL_REDS(BIF_P); - BIF_TRAP1(bif_export[BIF_system_info_1], - BIF_P, BIF_ARG_1); - default: - ASSERT(0); - BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); - } + Uint online; + erts_schedulers_state(NULL, &online, NULL, NULL, NULL, NULL, NULL, NULL); + BIF_RET(make_small(online)); #endif } else if (ERTS_IS_ATOM_STR("schedulers_active", BIF_ARG_1)) { #ifndef ERTS_SMP BIF_RET(make_small(1)); #else - Uint total, online, active; - switch (erts_schedulers_state(&total, &online, &active, NULL, NULL, NULL, 1)) { - case ERTS_SCHDLR_SSPND_DONE: - BIF_RET(make_small(active)); - case ERTS_SCHDLR_SSPND_YIELD_RESTART: - ERTS_VBUMP_ALL_REDS(BIF_P); - BIF_TRAP1(bif_export[BIF_system_info_1], - BIF_P, BIF_ARG_1); - default: - ASSERT(0); - BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); - } + Uint active; + erts_schedulers_state(NULL, NULL, &active, NULL, NULL, NULL, NULL, NULL); + BIF_RET(make_small(active)); #endif #if defined(ERTS_SMP) && defined(ERTS_DIRTY_SCHEDULERS) } else if (ERTS_IS_ATOM_STR("dirty_cpu_schedulers", BIF_ARG_1)) { Uint dirty_cpu; - erts_schedulers_state(NULL, NULL, NULL, &dirty_cpu, NULL, NULL, 1); + erts_schedulers_state(NULL, NULL, NULL, &dirty_cpu, NULL, NULL, NULL, NULL); BIF_RET(make_small(dirty_cpu)); } else if (ERTS_IS_ATOM_STR("dirty_cpu_schedulers_online", BIF_ARG_1)) { Uint dirty_cpu_onln; - erts_schedulers_state(NULL, NULL, NULL, NULL, &dirty_cpu_onln, NULL, 1); + erts_schedulers_state(NULL, NULL, NULL, NULL, &dirty_cpu_onln, NULL, NULL, NULL); BIF_RET(make_small(dirty_cpu_onln)); } else if (ERTS_IS_ATOM_STR("dirty_io_schedulers", BIF_ARG_1)) { Uint dirty_io; - erts_schedulers_state(NULL, NULL, NULL, NULL, NULL, &dirty_io, 1); + erts_schedulers_state(NULL, NULL, NULL, NULL, NULL, NULL, &dirty_io, NULL); BIF_RET(make_small(dirty_io)); #endif } else if (ERTS_IS_ATOM_STR("run_queues", BIF_ARG_1)) { @@ -2642,7 +2689,16 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) if (erts_no_schedulers == 1) BIF_RET(NIL); else - BIF_RET(erts_multi_scheduling_blockers(BIF_P)); + BIF_RET(erts_multi_scheduling_blockers(BIF_P, 0)); +#endif + } else if (ERTS_IS_ATOM_STR("normal_multi_scheduling_blockers", BIF_ARG_1)) { +#ifndef ERTS_SMP + BIF_RET(NIL); +#else + if (erts_no_schedulers == 1) + BIF_RET(NIL); + else + BIF_RET(erts_multi_scheduling_blockers(BIF_P, 1)); #endif } else if (ERTS_IS_ATOM_STR("modified_timing_level", BIF_ARG_1)) { BIF_RET(ERTS_USE_MODIFIED_TIMING() diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index c69cbc023c..41e471a96a 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -153,10 +153,8 @@ int ERTS_WRITE_UNLIKELY(erts_eager_check_io) = 1; int ERTS_WRITE_UNLIKELY(erts_sched_compact_load); int ERTS_WRITE_UNLIKELY(erts_sched_balance_util) = 0; Uint ERTS_WRITE_UNLIKELY(erts_no_schedulers); -#ifdef ERTS_DIRTY_SCHEDULERS -Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_cpu_schedulers); -Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_io_schedulers); -#endif +Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_cpu_schedulers) = 0; +Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_io_schedulers) = 0; static char *erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_NO_FLAGS] = {0}; int erts_aux_work_no_flags = ERTS_SSI_AUX_WORK_NO_FLAGS; @@ -188,84 +186,176 @@ int erts_disable_proc_not_running_opt; static ErtsAuxWorkData *aux_thread_aux_work_data; -#define ERTS_SCHDLR_SSPND_CHNG_WAITER (((erts_aint32_t) 1) << 0) +#define ERTS_SCHDLR_SSPND_CHNG_NMSB (((erts_aint32_t) 1) << 0) #define ERTS_SCHDLR_SSPND_CHNG_MSB (((erts_aint32_t) 1) << 1) #define ERTS_SCHDLR_SSPND_CHNG_ONLN (((erts_aint32_t) 1) << 2) +#define ERTS_SCHDLR_SSPND_CHNG_DCPU_ONLN (((erts_aint32_t) 1) << 3) -#ifndef DEBUG +typedef enum { + ERTS_SCHED_NORMAL, + ERTS_SCHED_DIRTY_CPU, + ERTS_SCHED_DIRTY_IO +} ErtsSchedType; -#define ERTS_SCHDLR_SSPND_CHNG_SET(VAL, OLD_VAL) \ - erts_smp_atomic32_set_nob(&schdlr_sspnd.changing, (VAL)) +typedef struct { + int ongoing; + ErtsProcList *blckrs; + ErtsProcList *chngq; +} ErtsMultiSchedulingBlock; -#ifdef ERTS_DIRTY_SCHEDULERS -#define ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(VAL, OLD_VAL) \ - erts_smp_atomic32_set_nob(&schdlr_sspnd.dirty_cpu_changing, (VAL)) -#define ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(VAL, OLD_VAL) \ - erts_smp_atomic32_set_nob(&schdlr_sspnd.dirty_io_changing, (VAL)) -#endif +static struct { + erts_smp_mtx_t mtx; + Uint32 online; + Uint32 curr_online; + Uint32 active; + erts_smp_atomic32_t changing; + ErtsProcList *chngq; + Eterm changer; + ErtsMultiSchedulingBlock nmsb; /* Normal multi Scheduling Block */ + ErtsMultiSchedulingBlock msb; /* Multi Scheduling Block */ +} schdlr_sspnd; -#else +#define ERTS_SCHDLR_SSPND_S_BITS 10 +#define ERTS_SCHDLR_SSPND_DCS_BITS 11 +#define ERTS_SCHDLR_SSPND_DIS_BITS 11 -#define ERTS_SCHDLR_SSPND_CHNG_SET(VAL, OLD_VAL) \ -do { \ - erts_aint32_t old_val__; \ - old_val__ = erts_smp_atomic32_xchg_nob(&schdlr_sspnd.changing, \ - (VAL)); \ - ASSERT(old_val__ == (OLD_VAL)); \ -} while (0) +#define ERTS_SCHDLR_SSPND_S_MASK ((1 << ERTS_SCHDLR_SSPND_S_BITS)-1) +#define ERTS_SCHDLR_SSPND_DCS_MASK ((1 << ERTS_SCHDLR_SSPND_DCS_BITS)-1) +#define ERTS_SCHDLR_SSPND_DIS_MASK ((1 << ERTS_SCHDLR_SSPND_DIS_BITS)-1) -#ifdef ERTS_DIRTY_SCHEDULERS -#define ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(VAL, OLD_VAL) \ -do { \ - erts_aint32_t old_val__; \ - old_val__ = erts_smp_atomic32_xchg_nob(&schdlr_sspnd.dirty_cpu_changing, \ - (VAL)); \ - ASSERT(old_val__ == (OLD_VAL)); \ -} while (0) -#define ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(VAL, OLD_VAL) \ -do { \ - erts_aint32_t old_val__; \ - old_val__ = erts_smp_atomic32_xchg_nob(&schdlr_sspnd.dirty_io_changing, \ - (VAL)); \ - ASSERT(old_val__ == (OLD_VAL)); \ -} while (0) -#endif +#define ERTS_SCHDLR_SSPND_S_SHIFT 0 +#define ERTS_SCHDLR_SSPND_DCS_SHIFT (ERTS_SCHDLR_SSPND_S_SHIFT \ + + ERTS_SCHDLR_SSPND_S_BITS) +#define ERTS_SCHDLR_SSPND_DIS_SHIFT (ERTS_SCHDLR_SSPND_DCS_SHIFT \ + + ERTS_SCHDLR_SSPND_DCS_BITS) +#if (ERTS_SCHDLR_SSPND_S_BITS \ + + ERTS_SCHDLR_SSPND_DCS_BITS \ + + ERTS_SCHDLR_SSPND_DIS_BITS) > 32 +# error Wont fit in Uint32 #endif - -static struct { - erts_smp_mtx_t mtx; - erts_smp_cnd_t cnd; - int online; - int curr_online; - int wait_curr_online; -#ifdef ERTS_DIRTY_SCHEDULERS - int dirty_cpu_online; - int dirty_cpu_curr_online; - int dirty_cpu_wait_curr_online; - int dirty_io_online; - int dirty_io_curr_online; - int dirty_io_wait_curr_online; +#if (ERTS_MAX_NO_OF_SCHEDULERS-1) > ERTS_SCHDLR_SSPND_S_MASK +# error Max no schedulers wont fit in its bit-field #endif - erts_smp_atomic32_t changing; - erts_smp_atomic32_t active; -#ifdef ERTS_DIRTY_SCHEDULERS - erts_smp_atomic32_t dirty_cpu_changing; - erts_smp_atomic32_t dirty_cpu_active; - erts_smp_atomic32_t dirty_io_changing; - erts_smp_atomic32_t dirty_io_active; +#if ERTS_MAX_NO_OF_DIRTY_CPU_SCHEDULERS > ERTS_SCHDLR_SSPND_DCS_MASK +# error Max no dirty cpu schedulers wont fit in its bit-field #endif - struct { - int ongoing; - long wait_active; -#ifdef ERTS_DIRTY_SCHEDULERS - long dirty_cpu_wait_active; - long dirty_io_wait_active; +#if ERTS_MAX_NO_OF_DIRTY_IO_SCHEDULERS > ERTS_SCHDLR_SSPND_DIS_MASK +# error Max no dirty io schedulers wont fit in its bit-field #endif - ErtsProcList *procs; - } msb; /* Multi Scheduling Block */ -} schdlr_sspnd; + +#define ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(S, DCS, DIS) \ + ((((Uint32) (((S) & ERTS_SCHDLR_SSPND_S_MASK))-1) \ + << ERTS_SCHDLR_SSPND_S_SHIFT) \ + | ((((Uint32) ((DCS) & ERTS_SCHDLR_SSPND_DCS_MASK)) \ + << ERTS_SCHDLR_SSPND_DCS_SHIFT)) \ + | ((((Uint32) ((DIS) & ERTS_SCHDLR_SSPND_DIS_MASK)) \ + << ERTS_SCHDLR_SSPND_DIS_SHIFT))) + +static void init_scheduler_suspend(void); + +static ERTS_INLINE Uint32 +schdlr_sspnd_get_nscheds(Uint32 *valp, ErtsSchedType type) +{ + Uint32 res = (Uint32) (*valp); + switch (type) { + case ERTS_SCHED_NORMAL: + res >>= ERTS_SCHDLR_SSPND_S_SHIFT; + res &= (Uint32) ERTS_SCHDLR_SSPND_S_MASK; + res++; + break; + case ERTS_SCHED_DIRTY_CPU: + res >>= ERTS_SCHDLR_SSPND_DCS_SHIFT; + res &= (Uint32) ERTS_SCHDLR_SSPND_DCS_MASK; + break; + case ERTS_SCHED_DIRTY_IO: + res >>= ERTS_SCHDLR_SSPND_DIS_SHIFT; + res &= (Uint32) ERTS_SCHDLR_SSPND_DIS_MASK; + break; + default: + ERTS_INTERNAL_ERROR("Invalid scheduler type"); + return 0; + } + + return res; +} + +static ERTS_INLINE void +schdlr_sspnd_dec_nscheds(Uint32 *valp, ErtsSchedType type) +{ + ASSERT(schdlr_sspnd_get_nscheds(valp, type) > 0); + + switch (type) { + case ERTS_SCHED_NORMAL: + *valp -= ((Uint32) 1) << ERTS_SCHDLR_SSPND_S_SHIFT; + break; + case ERTS_SCHED_DIRTY_CPU: + *valp -= ((Uint32) 1) << ERTS_SCHDLR_SSPND_DCS_SHIFT; + break; + case ERTS_SCHED_DIRTY_IO: + *valp -= ((Uint32) 1) << ERTS_SCHDLR_SSPND_DIS_SHIFT; + break; + default: + ERTS_INTERNAL_ERROR("Invalid scheduler type"); + } +} + +static ERTS_INLINE void +schdlr_sspnd_inc_nscheds(Uint32 *valp, ErtsSchedType type) +{ + switch (type) { + case ERTS_SCHED_NORMAL: + ASSERT(schdlr_sspnd_get_nscheds(valp, type) + < ERTS_MAX_NO_OF_SCHEDULERS-1); + *valp += ((Uint32) 1) << ERTS_SCHDLR_SSPND_S_SHIFT; + break; + case ERTS_SCHED_DIRTY_CPU: + ASSERT(schdlr_sspnd_get_nscheds(valp, type) + < ERTS_MAX_NO_OF_DIRTY_CPU_SCHEDULERS); + *valp += ((Uint32) 1) << ERTS_SCHDLR_SSPND_DCS_SHIFT; + break; + case ERTS_SCHED_DIRTY_IO: + ASSERT(schdlr_sspnd_get_nscheds(valp, type) + < ERTS_MAX_NO_OF_DIRTY_IO_SCHEDULERS); + *valp += ((Uint32) 1) << ERTS_SCHDLR_SSPND_DIS_SHIFT; + break; + default: + ERTS_INTERNAL_ERROR("Invalid scheduler type"); + } +} + +static ERTS_INLINE void +schdlr_sspnd_set_nscheds(Uint32 *valp, ErtsSchedType type, Uint32 no) +{ + Uint32 val = *valp; + + switch (type) { + case ERTS_SCHED_NORMAL: + ASSERT(no > 0); + val &= ~(((Uint32) ERTS_SCHDLR_SSPND_S_MASK) + << ERTS_SCHDLR_SSPND_S_SHIFT); + val |= (((no-1) & ((Uint32) ERTS_SCHDLR_SSPND_S_MASK)) + << ERTS_SCHDLR_SSPND_S_SHIFT); + break; + case ERTS_SCHED_DIRTY_CPU: + val &= ~(((Uint32) ERTS_SCHDLR_SSPND_DCS_MASK) + << ERTS_SCHDLR_SSPND_DCS_SHIFT); + val |= ((no & ((Uint32) ERTS_SCHDLR_SSPND_DCS_MASK)) + << ERTS_SCHDLR_SSPND_DCS_SHIFT); + break; + case ERTS_SCHED_DIRTY_IO: + val &= ~(((Uint32) ERTS_SCHDLR_SSPND_DIS_MASK) + << ERTS_SCHDLR_SSPND_DIS_SHIFT); + val |= ((no & ((Uint32) ERTS_SCHDLR_SSPND_DIS_MASK)) + << ERTS_SCHDLR_SSPND_DIS_SHIFT); + break; + default: + ERTS_INTERNAL_ERROR("Invalid scheduler type"); + } + + *valp = val; +} static struct { erts_smp_mtx_t update_mtx; @@ -426,8 +516,10 @@ do { \ do { \ ErtsRunQueue *RQVAR; \ int ix__; \ + int online__ = (int) schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, \ + ERTS_SCHED_NORMAL); \ ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&schdlr_sspnd.mtx)); \ - for (ix__ = 0; ix__ < schdlr_sspnd.online; ix__++) { \ + for (ix__ = 0; ix__ < online__; ix__++) { \ RQVAR = ERTS_RUNQ_IX(ix__); \ erts_smp_runq_lock(RQVAR); \ { DO; } \ @@ -1188,12 +1280,27 @@ proclist_create(Process *p) return plp; } +static ERTS_INLINE ErtsProcList * +proclist_copy(ErtsProcList *plp0) +{ + ErtsProcList *plp1 = proclist_alloc(); + plp1->pid = plp0->pid; + plp1->started_interval = plp0->started_interval; + return plp1; +} + static ERTS_INLINE void proclist_destroy(ErtsProcList *plp) { proclist_free(plp); } +ErtsProcList * +erts_proclist_copy(ErtsProcList *plp) +{ + return proclist_copy(plp); +} + ErtsProcList * erts_proclist_create(Process *p) { @@ -2632,13 +2739,6 @@ sched_active(Uint no, ErtsRunQueue *rq) profile_scheduler(make_small(no), am_active); } -static int ERTS_INLINE -ongoing_multi_scheduling_block(void) -{ - ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&schdlr_sspnd.mtx)); - return schdlr_sspnd.msb.ongoing; -} - static ERTS_INLINE void empty_runq_aux(ErtsRunQueue *rq, Uint32 old_flags) { @@ -3025,8 +3125,10 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) timeout_time = erts_check_next_timeout_time(esdp); current_time = erts_get_monotonic_time(esdp); do_timeout = (current_time >= timeout_time); - } else + } else { + current_time = 0; timeout_time = ERTS_MONOTONIC_TIME_MAX; + } if (do_timeout) { if (!thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); @@ -3836,24 +3938,33 @@ static ERTS_INLINE void resume_run_queue(ErtsRunQueue *rq) { int pix; + Uint32 oflgs; erts_smp_runq_lock(rq); - (void) ERTS_RUNQ_FLGS_READ_BSET(rq, - (ERTS_RUNQ_FLG_OUT_OF_WORK - | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK - | ERTS_RUNQ_FLG_SUSPENDED), - (ERTS_RUNQ_FLG_OUT_OF_WORK - | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK)); + oflgs = ERTS_RUNQ_FLGS_READ_BSET(rq, + (ERTS_RUNQ_FLG_OUT_OF_WORK + | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK + | ERTS_RUNQ_FLG_SUSPENDED), + (ERTS_RUNQ_FLG_OUT_OF_WORK + | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK)); + + if (oflgs & ERTS_RUNQ_FLG_SUSPENDED) { + erts_aint32_t len; + + rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS; + for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) { + len = erts_smp_atomic32_read_dirty(&rq->procs.prio_info[pix].len); + rq->procs.prio_info[pix].max_len = len; + rq->procs.prio_info[pix].reds = 0; + } + len = erts_smp_atomic32_read_dirty(&rq->ports.info.len); + rq->ports.info.max_len = len; + rq->ports.info.reds = 0; + len = erts_smp_atomic32_read_dirty(&rq->len); + rq->max_len = len; - rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS; - for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) { - rq->procs.prio_info[pix].max_len = 0; - rq->procs.prio_info[pix].reds = 0; } - rq->ports.info.max_len = 0; - rq->ports.info.reds = 0; - rq->max_len = 0; erts_smp_runq_unlock(rq); @@ -5632,6 +5743,9 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online char *daww_ptr; size_t daww_sz; size_t size_runqs; +#ifdef ERTS_SMP + erts_aint32_t set_schdlr_sspnd_change_flags; +#endif init_misc_op_list_alloc(); init_proc_sys_task_queues_alloc(); @@ -5869,25 +5983,6 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA, sizeof(ErtsAuxWorkData)); - erts_smp_mtx_init(&schdlr_sspnd.mtx, "schdlr_sspnd"); - erts_smp_cnd_init(&schdlr_sspnd.cnd); - - erts_smp_atomic32_init_nob(&schdlr_sspnd.changing, 0); - schdlr_sspnd.online = no_schedulers_online; - schdlr_sspnd.curr_online = no_schedulers; - schdlr_sspnd.msb.ongoing = 0; - erts_smp_atomic32_init_nob(&schdlr_sspnd.active, no_schedulers); -#ifdef ERTS_DIRTY_SCHEDULERS - erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_cpu_changing, 0); - schdlr_sspnd.dirty_cpu_online = no_dirty_cpu_schedulers_online; - schdlr_sspnd.dirty_cpu_curr_online = no_dirty_cpu_schedulers; - erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_cpu_active, no_dirty_cpu_schedulers); - erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_io_changing, 0); - schdlr_sspnd.dirty_io_online = no_dirty_io_schedulers; - schdlr_sspnd.dirty_io_curr_online = no_dirty_io_schedulers; - erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_io_active, no_dirty_io_schedulers); -#endif - schdlr_sspnd.msb.procs = NULL; init_no_runqs(no_schedulers_online, no_schedulers_online); balance_info.last_active_runqs = no_schedulers; erts_smp_mtx_init(&balance_info.update_mtx, "migration_info_update"); @@ -5902,32 +5997,66 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online init_migration_paths(); - if (no_schedulers_online < no_schedulers) { + init_scheduler_suspend(); + + set_schdlr_sspnd_change_flags = 0; + + schdlr_sspnd_set_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_NORMAL, + no_schedulers_online); + schdlr_sspnd_set_nscheds(&schdlr_sspnd.curr_online, + ERTS_SCHED_NORMAL, + no_schedulers); + schdlr_sspnd_set_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_NORMAL, + no_schedulers); + + if (no_schedulers_online != no_schedulers) { + ASSERT(no_schedulers_online < no_schedulers); + set_schdlr_sspnd_change_flags |= ERTS_SCHDLR_SSPND_CHNG_ONLN; + schdlr_sspnd.changer = am_init; change_no_used_runqs(no_schedulers_online); for (ix = no_schedulers_online; ix < erts_no_run_queues; ix++) suspend_run_queue(ERTS_RUNQ_IX(ix)); } - schdlr_sspnd.wait_curr_online = no_schedulers_online; - schdlr_sspnd.curr_online *= 2; /* Boot strapping... */ - ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN - | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); #ifdef ERTS_DIRTY_SCHEDULERS - schdlr_sspnd.dirty_cpu_wait_curr_online = no_dirty_cpu_schedulers_online; - schdlr_sspnd.dirty_cpu_curr_online *= 2; - ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN - | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); - for (ix = no_dirty_cpu_schedulers_online; ix < no_dirty_cpu_schedulers; ix++) { - ErtsSchedulerData* esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); - erts_smp_atomic32_read_bor_nob(&esdp->ssi->flags, ERTS_SSI_FLG_SUSPENDED); + + schdlr_sspnd_set_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_DIRTY_CPU, + no_dirty_cpu_schedulers_online); + schdlr_sspnd_set_nscheds(&schdlr_sspnd.curr_online, + ERTS_SCHED_DIRTY_CPU, + no_dirty_cpu_schedulers); + schdlr_sspnd_set_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_DIRTY_CPU, + no_dirty_cpu_schedulers); + + if (no_dirty_cpu_schedulers_online != no_dirty_cpu_schedulers) { + ASSERT(no_dirty_cpu_schedulers_online < no_dirty_cpu_schedulers); + set_schdlr_sspnd_change_flags |= ERTS_SCHDLR_SSPND_CHNG_DCPU_ONLN; + for (ix = no_dirty_cpu_schedulers_online; ix < no_dirty_cpu_schedulers; ix++) { + ErtsSchedulerData* esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); + erts_smp_atomic32_read_bor_nob(&esdp->ssi->flags, ERTS_SSI_FLG_SUSPENDED); + } } - schdlr_sspnd.dirty_io_wait_curr_online = no_dirty_io_schedulers; - schdlr_sspnd.dirty_io_curr_online *= 2; - ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN - | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + schdlr_sspnd_set_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_DIRTY_IO, + no_dirty_io_schedulers); + schdlr_sspnd_set_nscheds(&schdlr_sspnd.curr_online, + ERTS_SCHED_DIRTY_IO, + no_dirty_io_schedulers); + schdlr_sspnd_set_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_DIRTY_IO, + no_dirty_io_schedulers); + #endif + if (set_schdlr_sspnd_change_flags) + erts_smp_atomic32_set_nob(&schdlr_sspnd.changing, + set_schdlr_sspnd_change_flags); + erts_smp_atomic32_init_nob(&doing_sys_schedule, 0); init_misc_aux_work(); @@ -5942,10 +6071,8 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online #endif } erts_no_schedulers = 1; -#ifdef ERTS_DIRTY_SCHEDULERS erts_no_dirty_cpu_schedulers = 0; erts_no_dirty_io_schedulers = 0; -#endif #endif erts_smp_atomic32_init_nob(&function_calls, 0); @@ -6705,19 +6832,6 @@ resume_process(Process *p, ErtsProcLocks locks) add2runq(enqueue, enq_prio, p, state, NULL); } -int -erts_get_max_no_executing_schedulers(void) -{ -#ifdef ERTS_SMP - if (erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)) - return (int) erts_no_schedulers; - ERTS_THR_MEMORY_BARRIER; - return (int) erts_smp_atomic32_read_nob(&schdlr_sspnd.active); -#else - return 1; -#endif -} - #ifdef ERTS_SMP static void @@ -6820,42 +6934,82 @@ sched_set_suspended_sleeptype(ErtsSchedulerSleepInfo *ssi) } } -#ifdef ERTS_DIRTY_SCHEDULERS +static void +init_scheduler_suspend(void) +{ + erts_smp_mtx_init(&schdlr_sspnd.mtx, "schdlr_sspnd"); + schdlr_sspnd.online = ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0); + schdlr_sspnd.curr_online = ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0); + schdlr_sspnd.active = ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0); + erts_smp_atomic32_init_nob(&schdlr_sspnd.changing, 0); + schdlr_sspnd.chngq = NULL; + schdlr_sspnd.changer = am_false; + schdlr_sspnd.nmsb.ongoing = 0; + schdlr_sspnd.nmsb.blckrs = NULL; + schdlr_sspnd.nmsb.chngq = NULL; + schdlr_sspnd.msb.ongoing = 0; + schdlr_sspnd.msb.blckrs = NULL; + schdlr_sspnd.msb.chngq = NULL; +} + +typedef struct { + struct { + Eterm chngr; + Eterm nxt; + } onln; + struct { + ErtsProcList *chngrs; + } msb; +} ErtsSchdlrSspndResume; + +static void +schdlr_sspnd_resume_proc(Eterm pid) +{ + Process *p = erts_pid2proc(NULL, 0, pid, ERTS_PROC_LOCK_STATUS); + if (p) { + resume_process(p, ERTS_PROC_LOCK_STATUS); + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + } +} + +static ERTS_INLINE void +schdlr_sspnd_resume_procs(ErtsSchedType sched_type, + ErtsSchdlrSspndResume *resume) +{ + if (is_internal_pid(resume->onln.chngr)) { + schdlr_sspnd_resume_proc(resume->onln.chngr); + resume->onln.chngr = NIL; + } + if (is_internal_pid(resume->onln.nxt)) { + schdlr_sspnd_resume_proc(resume->onln.nxt); + resume->onln.nxt = NIL; + } + while (resume->msb.chngrs) { + ErtsProcList *plp = resume->msb.chngrs; + resume->msb.chngrs = plp->next; + schdlr_sspnd_resume_proc(plp->pid); + proclist_destroy(plp); + } +} static void suspend_scheduler(ErtsSchedulerData *esdp) { erts_aint32_t flgs; erts_aint32_t changing; -#ifdef ERTS_DIRTY_SCHEDULERS - long no = (long) (ERTS_SCHEDULER_IS_DIRTY(esdp) - ? ERTS_DIRTY_SCHEDULER_NO(esdp) - : esdp->no); -#else - long no = (long) esdp->no; -#endif + long no; ErtsSchedulerSleepInfo *ssi = esdp->ssi; - long active_schedulers; int curr_online = 1; - int wake = 0; + ErtsSchdlrSspndResume resume = {{NIL, NIL}, {NULL}}; erts_aint32_t aux_work; int thr_prgr_active = 1; ErtsStuckBoundProcesses sbp = {NULL, NULL}; - int* ss_onlinep; - int* ss_curr_onlinep; - int* ss_wait_curr_onlinep; - long* ss_wait_activep; - long ss_wait_active_target; - erts_smp_atomic32_t* ss_changingp; - erts_smp_atomic32_t* ss_activep; + ErtsSchedType sched_type; + erts_aint32_t online_flag; /* * Schedulers may be suspended in two different ways: * - A scheduler may be suspended since it is not online. - * All schedulers with scheduler ids greater than - * schdlr_sspnd.online are suspended; same for dirty - * schedulers and schdlr_sspnd.dirty_cpu_online and - * schdlr_sspnd.dirty_io_online. * - Multi scheduling is blocked. All schedulers except the * scheduler with scheduler id 1 are suspended, and all * dirty CPU and dirty I/O schedulers are suspended. @@ -6863,27 +7017,43 @@ suspend_scheduler(ErtsSchedulerData *esdp) * Regardless of why a scheduler is suspended, it ends up here. */ - ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp) || no != 1); - #ifdef ERTS_DIRTY_SCHEDULERS if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { + no = ERTS_DIRTY_SCHEDULER_NO(esdp); + if (ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(esdp->run_queue)) { + online_flag = ERTS_SCHDLR_SSPND_CHNG_DCPU_ONLN; + sched_type = ERTS_SCHED_DIRTY_CPU; + } + else { + online_flag = 0; + sched_type = ERTS_SCHED_DIRTY_IO; + } + } + else +#endif + { + online_flag = ERTS_SCHDLR_SSPND_CHNG_ONLN; + no = esdp->no; + sched_type = ERTS_SCHED_NORMAL; + } + + ASSERT(sched_type != ERTS_SCHED_NORMAL || no != 1); + + if (sched_type != ERTS_SCHED_NORMAL) { if (erts_smp_mtx_trylock(&schdlr_sspnd.mtx) == EBUSY) { erts_smp_runq_unlock(esdp->run_queue); erts_smp_mtx_lock(&schdlr_sspnd.mtx); erts_smp_runq_lock(esdp->run_queue); } - if (ongoing_multi_scheduling_block()) + if (schdlr_sspnd.msb.ongoing) evacuate_run_queue(esdp->run_queue, &sbp); - } else -#endif + erts_smp_runq_unlock(esdp->run_queue); + } + else { evacuate_run_queue(esdp->run_queue, &sbp); - erts_smp_runq_unlock(esdp->run_queue); + erts_smp_runq_unlock(esdp->run_queue); -#ifdef ERTS_DIRTY_SCHEDULERS - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) -#endif - { erts_sched_check_cpu_bind_prep_suspend(esdp); if (erts_system_profile_flags.scheduler) @@ -6897,99 +7067,139 @@ suspend_scheduler(ErtsSchedulerData *esdp) flgs = sched_prep_spin_suspended(ssi, ERTS_SSI_FLG_SUSPENDED); if (flgs & ERTS_SSI_FLG_SUSPENDED) { -#ifdef ERTS_DIRTY_SCHEDULERS - if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { - if (ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(esdp->run_queue)) { - active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.dirty_cpu_active); - ASSERT(active_schedulers >= 0); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing); - ss_onlinep = &schdlr_sspnd.dirty_cpu_online; - ss_curr_onlinep = &schdlr_sspnd.dirty_cpu_curr_online; - ss_wait_curr_onlinep = &schdlr_sspnd.dirty_cpu_wait_curr_online; - ss_changingp = &schdlr_sspnd.dirty_cpu_changing; - ss_wait_activep = &schdlr_sspnd.msb.dirty_cpu_wait_active; - ss_activep = &schdlr_sspnd.dirty_cpu_active; - } else { - active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.dirty_io_active); - ASSERT(active_schedulers >= 0); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing); - ss_onlinep = &schdlr_sspnd.dirty_io_online; - ss_curr_onlinep = &schdlr_sspnd.dirty_io_curr_online; - ss_wait_curr_onlinep = &schdlr_sspnd.dirty_io_wait_curr_online; - ss_changingp = &schdlr_sspnd.dirty_io_changing; - ss_wait_activep = &schdlr_sspnd.msb.dirty_io_wait_active; - ss_activep = &schdlr_sspnd.dirty_io_active; - } - ss_wait_active_target = 0; - } - else -#endif - { - active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.active); - ASSERT(active_schedulers >= 1); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); - ss_onlinep = &schdlr_sspnd.online; - ss_curr_onlinep = &schdlr_sspnd.curr_online; - ss_wait_curr_onlinep = &schdlr_sspnd.wait_curr_online; - ss_changingp = &schdlr_sspnd.changing; - ss_wait_activep = &schdlr_sspnd.msb.wait_active; - ss_activep = &schdlr_sspnd.active; - ss_wait_active_target = 1; - } - if (changing & ERTS_SCHDLR_SSPND_CHNG_MSB) { - if (active_schedulers == *ss_wait_activep) - wake = 1; - if (active_schedulers == ss_wait_active_target) { - changing = erts_smp_atomic32_read_band_nob(ss_changingp, - ~ERTS_SCHDLR_SSPND_CHNG_MSB); - changing &= ~ERTS_SCHDLR_SSPND_CHNG_MSB; - } - } + schdlr_sspnd_dec_nscheds(&schdlr_sspnd.active, sched_type); + + ASSERT(schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_NORMAL) >= 1); + + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); while (1) { - if (changing & ERTS_SCHDLR_SSPND_CHNG_ONLN) { + + if (changing & (ERTS_SCHDLR_SSPND_CHNG_NMSB + | ERTS_SCHDLR_SSPND_CHNG_MSB)) { + int i = 0; + ErtsMultiSchedulingBlock *msb[3] = {0}; + if (changing & ERTS_SCHDLR_SSPND_CHNG_NMSB) + msb[i++] = &schdlr_sspnd.nmsb; + if (changing & ERTS_SCHDLR_SSPND_CHNG_MSB) + msb[i++] = &schdlr_sspnd.msb; + + for (i = 0; msb[i]; i++) { + erts_aint32_t clr_flg = 0; + + if (msb[i] == &schdlr_sspnd.nmsb + && schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_NORMAL) == 1) { + clr_flg = ERTS_SCHDLR_SSPND_CHNG_NMSB; + } + else if (schdlr_sspnd.active + == ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0)) { + clr_flg = ERTS_SCHDLR_SSPND_CHNG_MSB; + } + + if (clr_flg) { + ErtsProcList *plp, *end_plp; + changing = erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, + ~clr_flg); + changing &= ~clr_flg; + (void) erts_proclist_fetch(&msb[i]->chngq, &end_plp); + /* resume processes that initiated the multi scheduling block... */ + plp = msb[i]->chngq; + while (plp) { + erts_proclist_store_last(&msb[i]->blckrs, + proclist_copy(plp)); + plp = plp->next; + } + if (end_plp) + end_plp->next = resume.msb.chngrs; + resume.msb.chngrs = msb[i]->chngq; + msb[i]->chngq = NULL; + } + } + } + + if (changing & online_flag) { int changed = 0; - if (no > *ss_onlinep && curr_online) { - (*ss_curr_onlinep)--; + Uint32 st_online; + + st_online = schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, + sched_type); + if (no > st_online && curr_online) { + schdlr_sspnd_dec_nscheds(&schdlr_sspnd.curr_online, + sched_type); curr_online = 0; changed = 1; } - else if (no <= *ss_onlinep && !curr_online) { - (*ss_curr_onlinep)++; + else if (no <= st_online && !curr_online) { + schdlr_sspnd_inc_nscheds(&schdlr_sspnd.curr_online, + sched_type); curr_online = 1; changed = 1; } if (changed - && *ss_curr_onlinep == *ss_wait_curr_onlinep) - wake = 1; - if (*ss_onlinep == *ss_curr_onlinep) { - changing = erts_smp_atomic32_read_band_nob(ss_changingp, - ~ERTS_SCHDLR_SSPND_CHNG_ONLN); - changing &= ~ERTS_SCHDLR_SSPND_CHNG_ONLN; + && (schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, + sched_type) + == schdlr_sspnd_get_nscheds(&schdlr_sspnd.curr_online, + sched_type))) { + ErtsProcList *plp; + changing = erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, + ~online_flag); + changing &= ~online_flag; + if (sched_type == ERTS_SCHED_NORMAL) { + ASSERT(is_internal_pid(schdlr_sspnd.changer) + || schdlr_sspnd.changer == am_init); + /* resume process that initiated this change... */ + resume.onln.chngr = schdlr_sspnd.changer; + plp = erts_proclist_peek_first(schdlr_sspnd.chngq); + if (!plp) + schdlr_sspnd.changer = am_false; + else { + schdlr_sspnd.changer = am_true; /* change right in transit */ + /* resume process that is queued for next change... */ + resume.onln.nxt = plp->pid; + ASSERT(is_internal_pid(resume.onln.nxt)); + } + } } } - if (wake) { - erts_smp_cnd_signal(&schdlr_sspnd.cnd); - wake = 0; - } - - if (curr_online && !ongoing_multi_scheduling_block()) { + if (curr_online + && (sched_type == ERTS_SCHED_NORMAL + ? !(schdlr_sspnd.msb.ongoing|schdlr_sspnd.nmsb.ongoing) + : !schdlr_sspnd.msb.ongoing)) { flgs = erts_smp_atomic32_read_acqb(&ssi->flags); if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) break; } erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + schdlr_sspnd_resume_procs(sched_type, &resume); + while (1) { + ErtsMonotonicTime current_time; erts_aint32_t qmask; erts_aint32_t flgs; qmask = (ERTS_RUNQ_FLGS_GET(esdp->run_queue) & ERTS_RUNQ_FLGS_QMASK); - aux_work = erts_atomic32_read_acqb(&ssi->aux_work); - if (aux_work|qmask) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + + if (sched_type != ERTS_SCHED_NORMAL) { + if (qmask) { + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + erts_smp_runq_lock(esdp->run_queue); + if (schdlr_sspnd.msb.ongoing) + evacuate_run_queue(esdp->run_queue, &sbp); + erts_smp_runq_unlock(esdp->run_queue); + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + } + aux_work = 0; + } + else { + + aux_work = erts_atomic32_read_acqb(&ssi->aux_work); + + if (aux_work|qmask) { if (!thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); @@ -7001,271 +7211,50 @@ suspend_scheduler(ErtsSchedulerData *esdp) if (aux_work && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); - } - if (qmask) { -#ifdef ERTS_DIRTY_SCHEDULERS - if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - erts_smp_runq_lock(esdp->run_queue); - if (ongoing_multi_scheduling_block()) - evacuate_run_queue(esdp->run_queue, &sbp); - erts_smp_runq_unlock(esdp->run_queue); - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - } else -#endif - { + 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) { -#ifdef ERTS_DIRTY_SCHEDULERS - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) -#endif - { - if (thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 0); - sched_wall_time_change(esdp, 0); - } - erts_thr_progress_prepare_wait(esdp); - } - flgs = sched_spin_suspended(ssi, - ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT); - if (flgs == (ERTS_SSI_FLG_SLEEPING - | ERTS_SSI_FLG_WAITING - | ERTS_SSI_FLG_SUSPENDED)) { - flgs = sched_set_suspended_sleeptype(ssi); - if (flgs == (ERTS_SSI_FLG_SLEEPING - | ERTS_SSI_FLG_TSE_SLEEPING - | ERTS_SSI_FLG_WAITING - | ERTS_SSI_FLG_SUSPENDED)) { - int res; + } - do { - res = erts_tse_twait(ssi->event, -1); - } while (res == EINTR); + if (aux_work) { + ASSERT(sched_type == ERTS_SCHED_NORMAL); + current_time = erts_get_monotonic_time(esdp); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); } + erts_bump_timers(esdp->timer_wheel, current_time); } -#ifdef ERTS_DIRTY_SCHEDULERS - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) -#endif - erts_thr_progress_finalize_wait(esdp); } + else { + ErtsMonotonicTime timeout_time; + int do_timeout; - flgs = sched_prep_spin_suspended(ssi, (ERTS_SSI_FLG_WAITING - | ERTS_SSI_FLG_SUSPENDED)); - if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) - break; - changing = erts_smp_atomic32_read_nob(ss_changingp); - if (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER) - break; - } - - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - changing = erts_smp_atomic32_read_nob(ss_changingp); - } + if (sched_type == ERTS_SCHED_NORMAL) { + timeout_time = erts_check_next_timeout_time(esdp); + current_time = erts_get_monotonic_time(esdp); + do_timeout = (current_time >= timeout_time); + } + else { + timeout_time = ERTS_MONOTONIC_TIME_MAX; + current_time = 0; + do_timeout = 0; + } - active_schedulers = erts_smp_atomic32_inc_read_nob(ss_activep); - changing = erts_smp_atomic32_read_nob(ss_changingp); - if ((changing & ERTS_SCHDLR_SSPND_CHNG_MSB) - && *ss_onlinep == active_schedulers) { - erts_smp_atomic32_read_band_nob(ss_changingp, - ~ERTS_SCHDLR_SSPND_CHNG_MSB); - } - -#ifdef ERTS_DIRTY_SCHEDULERS - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) -#endif - ASSERT(no <= *ss_onlinep); - ASSERT(!ongoing_multi_scheduling_block()); - - } - - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - - ASSERT(curr_online); - -#ifdef ERTS_DIRTY_SCHEDULERS - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) -#endif - { - if (erts_system_profile_flags.scheduler) - profile_scheduler(make_small(esdp->no), am_active); - - if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); - sched_wall_time_change(esdp, 1); - } - } - - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) - (void) erts_get_monotonic_time(esdp); - erts_smp_runq_lock(esdp->run_queue); - non_empty_runq(esdp->run_queue); - -#ifdef ERTS_DIRTY_SCHEDULERS - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) -#endif - { - schedule_bound_processes(esdp->run_queue, &sbp); - - erts_sched_check_cpu_bind_post_suspend(esdp); - } -} - -#else /* !ERTS_DIRTY_SCHEDULERS */ - -static void -suspend_scheduler(ErtsSchedulerData *esdp) -{ - erts_aint32_t flgs; - erts_aint32_t changing; - long no = (long) esdp->no; - ErtsSchedulerSleepInfo *ssi = esdp->ssi; - long active_schedulers; - int curr_online = 1; - int wake = 0; - erts_aint32_t aux_work; - int thr_prgr_active = 1; - ErtsStuckBoundProcesses sbp = {NULL, NULL}; - - /* - * Schedulers may be suspended in two different ways: - * - A scheduler may be suspended since it is not online. - * All schedulers with scheduler ids greater than - * schdlr_sspnd.online are suspended. - * - Multi scheduling is blocked. All schedulers except the - * scheduler with scheduler id 1 are suspended. - * - * Regardless of why a scheduler is suspended, it ends up here. - */ - - ASSERT(no != 1); - - evacuate_run_queue(esdp->run_queue, &sbp); - - erts_smp_runq_unlock(esdp->run_queue); - - erts_sched_check_cpu_bind_prep_suspend(esdp); - - if (erts_system_profile_flags.scheduler) - profile_scheduler(make_small(esdp->no), am_inactive); - - sched_wall_time_change(esdp, 0); - - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - - flgs = sched_prep_spin_suspended(ssi, ERTS_SSI_FLG_SUSPENDED); - if (flgs & ERTS_SSI_FLG_SUSPENDED) { - - active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.active); - ASSERT(active_schedulers >= 1); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); - if (changing & ERTS_SCHDLR_SSPND_CHNG_MSB) { - if (active_schedulers == schdlr_sspnd.msb.wait_active) - wake = 1; - if (active_schedulers == 1) { - changing = erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, - ~ERTS_SCHDLR_SSPND_CHNG_MSB); - changing &= ~ERTS_SCHDLR_SSPND_CHNG_MSB; - } - } - - while (1) { - if (changing & ERTS_SCHDLR_SSPND_CHNG_ONLN) { - int changed = 0; - if (no > schdlr_sspnd.online && curr_online) { - schdlr_sspnd.curr_online--; - curr_online = 0; - changed = 1; - } - else if (no <= schdlr_sspnd.online && !curr_online) { - schdlr_sspnd.curr_online++; - curr_online = 1; - changed = 1; - } - if (changed - && schdlr_sspnd.curr_online == schdlr_sspnd.wait_curr_online) - wake = 1; - if (schdlr_sspnd.online == schdlr_sspnd.curr_online) { - changing = erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, - ~ERTS_SCHDLR_SSPND_CHNG_ONLN); - changing &= ~ERTS_SCHDLR_SSPND_CHNG_ONLN; - } - } - - if (wake) { - erts_smp_cnd_signal(&schdlr_sspnd.cnd); - wake = 0; - } - - if (curr_online && !ongoing_multi_scheduling_block()) { - flgs = erts_smp_atomic32_read_acqb(&ssi->flags); - if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) - break; - } - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - - while (1) { - ErtsMonotonicTime current_time; - erts_aint32_t qmask; - erts_aint32_t flgs; - - qmask = (ERTS_RUNQ_FLGS_GET(esdp->run_queue) - & ERTS_RUNQ_FLGS_QMASK); - aux_work = erts_atomic32_read_acqb(&ssi->aux_work); - if (aux_work|qmask) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { - if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); - sched_wall_time_change(esdp, 1); - } - if (aux_work) - aux_work = handle_aux_work(&esdp->aux_work_data, - aux_work, - 1); - 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) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { - current_time = erts_get_monotonic_time(esdp); - if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { - if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); - sched_wall_time_change(esdp, 1); - } - erts_bump_timers(esdp->timer_wheel, current_time); - } - } - } - else { - ErtsMonotonicTime timeout_time; - int do_timeout = 0; - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { - timeout_time = erts_check_next_timeout_time(esdp); - current_time = erts_get_monotonic_time(esdp); - do_timeout = (current_time >= timeout_time); - } else - timeout_time = ERTS_MONOTONIC_TIME_MAX; if (do_timeout) { + ASSERT(sched_type == ERTS_SCHED_NORMAL); if (!thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } } else { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (sched_type == ERTS_SCHED_NORMAL) { if (thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 0); sched_wall_time_change(esdp, 0); @@ -7284,30 +7273,39 @@ suspend_scheduler(ErtsSchedulerData *esdp) | ERTS_SSI_FLG_SUSPENDED)) { int res; - current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : - erts_get_monotonic_time(esdp); + if (sched_type == ERTS_SCHED_NORMAL) + current_time = erts_get_monotonic_time(esdp); + else + current_time = 0; + do { Sint64 timeout; if (current_time >= timeout_time) break; - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (sched_type != ERTS_SCHED_NORMAL) + timeout = -1; + else timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time - current_time - 1) + 1; - } else - timeout = -1; res = erts_tse_twait(ssi->event, timeout); - current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : - erts_get_monotonic_time(esdp); + + if (sched_type == ERTS_SCHED_NORMAL) + current_time = erts_get_monotonic_time(esdp); + else + current_time = 0; + } while (res == EINTR); } } - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) + if (sched_type == ERTS_SCHED_NORMAL) erts_thr_progress_finalize_wait(esdp); } - if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && current_time >= timeout_time) + if (current_time >= timeout_time) { + ASSERT(sched_type == ERTS_SCHED_NORMAL); erts_bump_timers(esdp->timer_wheel, current_time); + } } flgs = sched_prep_spin_suspended(ssi, (ERTS_SSI_FLG_WAITING @@ -7315,7 +7313,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) break; changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); - if (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER) + if (changing) break; } @@ -7323,612 +7321,494 @@ suspend_scheduler(ErtsSchedulerData *esdp) changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); } - active_schedulers = erts_smp_atomic32_inc_read_nob(&schdlr_sspnd.active); + schdlr_sspnd_inc_nscheds(&schdlr_sspnd.active, sched_type); changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); if ((changing & ERTS_SCHDLR_SSPND_CHNG_MSB) - && schdlr_sspnd.online == active_schedulers) { + && schdlr_sspnd.online == schdlr_sspnd.active) { erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, ~ERTS_SCHDLR_SSPND_CHNG_MSB); } - ASSERT(no <= schdlr_sspnd.online); - ASSERT(!ongoing_multi_scheduling_block()); - + ASSERT(no <= schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, sched_type)); + ASSERT((sched_type == ERTS_SCHED_NORMAL + ? !(schdlr_sspnd.msb.ongoing|schdlr_sspnd.nmsb.ongoing) + : !schdlr_sspnd.msb.ongoing)); } erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + ASSERT(!resume.msb.chngrs); + schdlr_sspnd_resume_procs(sched_type, &resume); + ASSERT(curr_online); - if (erts_system_profile_flags.scheduler) - profile_scheduler(make_small(esdp->no), am_active); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (erts_system_profile_flags.scheduler) + profile_scheduler(make_small(esdp->no), am_active); - if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); - sched_wall_time_change(esdp, 1); + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } } + if (sched_type == ERTS_SCHED_NORMAL) + (void) erts_get_monotonic_time(esdp); erts_smp_runq_lock(esdp->run_queue); non_empty_runq(esdp->run_queue); - schedule_bound_processes(esdp->run_queue, &sbp); + if (sched_type == ERTS_SCHED_NORMAL) { + schedule_bound_processes(esdp->run_queue, &sbp); - erts_sched_check_cpu_bind_post_suspend(esdp); + erts_sched_check_cpu_bind_post_suspend(esdp); + } } -#endif - -ErtsSchedSuspendResult +void erts_schedulers_state(Uint *total, Uint *online, Uint *active, Uint *dirty_cpu, Uint *dirty_cpu_online, + Uint *dirty_cpu_active, Uint *dirty_io, - int yield_allowed) + Uint *dirty_io_active) { - int res = ERTS_SCHDLR_SSPND_EINVAL; - erts_aint32_t changing; - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); -#ifdef ERTS_DIRTY_SCHEDULERS - changing |= (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing) - | erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing)); -#endif - if (yield_allowed && (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER)) - res = ERTS_SCHDLR_SSPND_YIELD_RESTART; - else { + if (active || online || dirty_cpu_online + || dirty_cpu_active || dirty_io_active) { + erts_smp_mtx_lock(&schdlr_sspnd.mtx); if (active) - *active = schdlr_sspnd.online; + *active = schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_NORMAL); if (online) - *online = schdlr_sspnd.online; - if (ongoing_multi_scheduling_block() && active) - *active = 1; -#ifdef ERTS_DIRTY_SCHEDULERS + *online = schdlr_sspnd_get_nscheds(&schdlr_sspnd.curr_online, + ERTS_SCHED_NORMAL); + if (dirty_cpu_active) + *dirty_cpu_active = schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_DIRTY_CPU); if (dirty_cpu_online) - *dirty_cpu_online = schdlr_sspnd.dirty_cpu_online; -#endif - res = ERTS_SCHDLR_SSPND_DONE; + *dirty_cpu_online = schdlr_sspnd_get_nscheds(&schdlr_sspnd.curr_online, + ERTS_SCHED_DIRTY_CPU); + if (dirty_io_active) + *dirty_io_active = schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_DIRTY_IO); + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); } - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + if (total) *total = erts_no_schedulers; -#ifdef ERTS_DIRTY_SCHEDULERS if (dirty_cpu) *dirty_cpu = erts_no_dirty_cpu_schedulers; if (dirty_io) *dirty_io = erts_no_dirty_io_schedulers; -#endif - return res; } -#ifdef ERTS_DIRTY_SCHEDULERS - -ErtsSchedSuspendResult -erts_set_schedulers_online(Process *p, - ErtsProcLocks plocks, - Sint new_no, - Sint *old_no -#ifdef ERTS_DIRTY_SCHEDULERS - , int dirty_only -#endif - ) +static void +abort_sched_onln_chng_waitq(Process *p) { - ErtsSchedulerData *esdp; - int ix, res = -1, no, have_unlocked_plocks, end_wait; - erts_aint32_t changing = 0; -#ifdef ERTS_DIRTY_SCHEDULERS - ErtsSchedulerSleepInfo* ssi; - int dirty_no, change_dirty; -#endif - - if (new_no < 1) - return ERTS_SCHDLR_SSPND_EINVAL; -#ifdef ERTS_DIRTY_SCHEDULERS - else if (dirty_only && erts_no_dirty_cpu_schedulers < new_no) - return ERTS_SCHDLR_SSPND_EINVAL; -#endif - else if (erts_no_schedulers < new_no) - return ERTS_SCHDLR_SSPND_EINVAL; - - esdp = ERTS_PROC_GET_SCHDATA(p); - end_wait = 0; + Eterm resume = NIL; erts_smp_mtx_lock(&schdlr_sspnd.mtx); - have_unlocked_plocks = 0; - no = (int) new_no; - -#ifdef ERTS_DIRTY_SCHEDULERS - ASSERT(schdlr_sspnd.dirty_cpu_online <= erts_no_dirty_cpu_schedulers); - if (dirty_only) { - if (no > schdlr_sspnd.online) { - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - return ERTS_SCHDLR_SSPND_EINVAL; +#ifdef DEBUG + { + int found_it = 0; + ErtsProcList *plp = erts_proclist_peek_first(schdlr_sspnd.chngq); + while (plp) { + if (erts_proclist_same(plp, p)) + found_it++; + plp = erts_proclist_peek_next(schdlr_sspnd.chngq, plp); } - dirty_no = no; - } else { - /* - * Adjust the number of dirty CPU schedulers online relative to the - * adjustment made to the number of normal schedulers online. - */ - int total_pct = erts_no_dirty_cpu_schedulers*100/erts_no_schedulers; - int onln_pct = no*total_pct/schdlr_sspnd.online; - dirty_no = schdlr_sspnd.dirty_cpu_online*onln_pct/100; - if (dirty_no == 0) - dirty_no = 1; - ASSERT(dirty_no <= erts_no_dirty_cpu_schedulers); + ASSERT(found_it == !!(p->flags & F_SCHDLR_ONLN_WAITQ)); } #endif - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); -#ifdef ERTS_DIRTY_SCHEDULERS - changing |= erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing); -#endif - if (changing) { - res = ERTS_SCHDLR_SSPND_YIELD_RESTART; - } - else { - int online = *old_no = schdlr_sspnd.online; -#ifdef ERTS_DIRTY_SCHEDULERS - int dirty_online = schdlr_sspnd.dirty_cpu_online; - - if (dirty_only) { - *old_no = schdlr_sspnd.dirty_cpu_online; - if (dirty_no == schdlr_sspnd.dirty_cpu_online) { - res = ERTS_SCHDLR_SSPND_DONE; - } - change_dirty = 1; - } else { -#endif - if (no == schdlr_sspnd.online) { -#ifdef ERTS_DIRTY_SCHEDULERS - dirty_only = 1; - if (dirty_no == schdlr_sspnd.dirty_cpu_online) -#endif - res = ERTS_SCHDLR_SSPND_DONE; -#ifdef ERTS_DIRTY_SCHEDULERS - else - change_dirty = 1; -#endif - } -#ifdef ERTS_DIRTY_SCHEDULERS - else - change_dirty = (dirty_no != schdlr_sspnd.dirty_cpu_online); - } -#endif - if (res == -1) - { - int increase = (no > online); -#ifdef ERTS_DIRTY_SCHEDULERS - if (!dirty_only) { -#endif - ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN - | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); - schdlr_sspnd.online = no; -#ifdef ERTS_DIRTY_SCHEDULERS - } else - increase = (dirty_no > dirty_online); - if (change_dirty) { - ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN - | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); - schdlr_sspnd.dirty_cpu_online = dirty_no; - } -#endif - if (increase) { - int ix; -#ifdef ERTS_DIRTY_SCHEDULERS - if (!dirty_only) { -#endif - schdlr_sspnd.wait_curr_online = no; - if (ongoing_multi_scheduling_block()) { - for (ix = online; ix < no; ix++) - erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix)); - } - else { - if (plocks) { - have_unlocked_plocks = 1; - erts_smp_proc_unlock(p, plocks); - } - change_no_used_runqs(no); - for (ix = online; ix < no; ix++) - resume_run_queue(ERTS_RUNQ_IX(ix)); + if (p->flags & F_SCHDLR_ONLN_WAITQ) { + ErtsProcList *plp = NULL; - for (ix = no; ix < erts_no_run_queues; ix++) - suspend_run_queue(ERTS_RUNQ_IX(ix)); - } -#ifdef ERTS_DIRTY_SCHEDULERS - } - if (change_dirty) { - schdlr_sspnd.dirty_cpu_wait_curr_online = dirty_no; - ASSERT(schdlr_sspnd.dirty_cpu_curr_online != - schdlr_sspnd.dirty_cpu_wait_curr_online); - if (ongoing_multi_scheduling_block()) { - for (ix = dirty_online; ix < dirty_no; ix++) { - ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); - erts_sched_poke(ssi); - } - } else { - for (ix = dirty_online; ix < dirty_no; ix++) { - ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); - scheduler_ssi_resume_wake(ssi); - erts_smp_atomic32_read_band_nob(&ssi->flags, - ~ERTS_SSI_FLG_SUSPENDED); - } - wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); - } - } -#endif - res = ERTS_SCHDLR_SSPND_DONE; - } - else /* if (no < online) */ { -#ifdef ERTS_DIRTY_SCHEDULERS - if (change_dirty) { - schdlr_sspnd.dirty_cpu_wait_curr_online = dirty_no; - ASSERT(schdlr_sspnd.dirty_cpu_curr_online != - schdlr_sspnd.dirty_cpu_wait_curr_online); - if (ongoing_multi_scheduling_block()) { - for (ix = dirty_no; ix < dirty_online; ix++) { - ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); - erts_sched_poke(ssi); - } - } else { - for (ix = dirty_no; ix < dirty_online; ix++) { - ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); - erts_smp_atomic32_read_bor_nob(&ssi->flags, - ERTS_SSI_FLG_SUSPENDED); - } - wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); - } - } - if (dirty_only) { - res = ERTS_SCHDLR_SSPND_DONE; - } + plp = erts_proclist_peek_first(schdlr_sspnd.chngq); + if (plp) { + if (erts_proclist_same(plp, p) + && schdlr_sspnd.changer == am_true) { + p->flags &= ~F_SCHDLR_ONLN_WAITQ; + /* + * Change right was in transit to us; + * transfer it to the next process by + * resuming it... + */ + erts_proclist_remove(&schdlr_sspnd.chngq, plp); + proclist_destroy(plp); + plp = erts_proclist_peek_first(schdlr_sspnd.chngq); + if (plp) + resume = plp->pid; else -#endif - { - if (p->scheduler_data->no <= no) { - res = ERTS_SCHDLR_SSPND_DONE; - schdlr_sspnd.wait_curr_online = no; - } - else { - /* - * Yield! Current process needs to migrate - * before bif returns. - */ - res = ERTS_SCHDLR_SSPND_YIELD_DONE; - schdlr_sspnd.wait_curr_online = no+1; - } - - if (ongoing_multi_scheduling_block()) { - for (ix = no; ix < online; ix++) - erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix)); - } - else { - if (plocks) { - have_unlocked_plocks = 1; - erts_smp_proc_unlock(p, plocks); - } - - change_no_used_runqs(no); - for (ix = no; ix < erts_no_run_queues; ix++) - suspend_run_queue(ERTS_RUNQ_IX(ix)); - - for (ix = no; ix < online; ix++) { - ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); - wake_scheduler(rq); - } - } - } + schdlr_sspnd.changer = am_false; } - -#ifdef ERTS_DIRTY_SCHEDULERS - if (change_dirty) { - while (schdlr_sspnd.dirty_cpu_curr_online != schdlr_sspnd.dirty_cpu_wait_curr_online) - erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); - ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); - erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_cpu_changing, - ~ERTS_SCHDLR_SSPND_CHNG_WAITER); - } - if (!dirty_only) -#endif - { - if (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online) { - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - if (plocks && !have_unlocked_plocks) { - have_unlocked_plocks = 1; - erts_smp_proc_unlock(p, plocks); + else { + do { + if (erts_proclist_same(plp, p)) { + p->flags &= ~F_SCHDLR_ONLN_WAITQ; + erts_proclist_remove(&schdlr_sspnd.chngq, plp); + proclist_destroy(plp); + break; } - erts_thr_progress_active(esdp, 0); - erts_thr_progress_prepare_wait(esdp); - end_wait = 1; - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - } - - while (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online) - erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); - - ASSERT(res != ERTS_SCHDLR_SSPND_DONE - ? (ERTS_SCHDLR_SSPND_CHNG_WAITER - & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)) - : (ERTS_SCHDLR_SSPND_CHNG_WAITER - == erts_smp_atomic32_read_nob(&schdlr_sspnd.changing))); - erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, - ~ERTS_SCHDLR_SSPND_CHNG_WAITER); + plp = erts_proclist_peek_next(schdlr_sspnd.chngq, plp); + } while (plp); } } } erts_smp_mtx_unlock(&schdlr_sspnd.mtx); -#ifdef ERTS_DIRTY_SCHEDULERS - ASSERT(schdlr_sspnd.dirty_cpu_online <= schdlr_sspnd.online); - if (!dirty_only) -#endif - { - if (end_wait) { - erts_thr_progress_finalize_wait(esdp); - erts_thr_progress_active(esdp, 1); - } - if (have_unlocked_plocks) - erts_smp_proc_lock(p, plocks); - } - return res; + if (is_internal_pid(resume)) + schdlr_sspnd_resume_proc(resume); } -#else /* !ERTS_DIRTY_SCHEDULERS */ - ErtsSchedSuspendResult erts_set_schedulers_online(Process *p, ErtsProcLocks plocks, Sint new_no, - Sint *old_no) + Sint *old_no, + int dirty_only) { - ErtsSchedulerData *esdp; - int ix, res, no, have_unlocked_plocks, end_wait; - erts_aint32_t changing; + int resume_proc, ix, res = -1, no, have_unlocked_plocks; + erts_aint32_t changing = 0, change_flags; + int online, increase; + ErtsProcList *plp; +#ifdef ERTS_DIRTY_SCHEDULERS + int dirty_no, change_dirty, dirty_online; +#else + ASSERT(!dirty_only); +#endif - if (new_no < 1 || erts_no_schedulers < new_no) + if (new_no < 1) + return ERTS_SCHDLR_SSPND_EINVAL; + else if (dirty_only && erts_no_dirty_cpu_schedulers < new_no) + return ERTS_SCHDLR_SSPND_EINVAL; + else if (erts_no_schedulers < new_no) return ERTS_SCHDLR_SSPND_EINVAL; - esdp = ERTS_PROC_GET_SCHDATA(p); - end_wait = 0; +#ifdef ERTS_DIRTY_SCHEDULERS + if (dirty_only) + resume_proc = 0; + else +#endif + { + resume_proc = 1; + /* + * If we suspend current process we need to suspend before + * requesting the change; otherwise, we got a resume/suspend + * race... + */ + if (!(plocks & ERTS_PROC_LOCK_STATUS)) + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + suspend_process(p, p); + if (!(plocks & ERTS_PROC_LOCK_STATUS)) + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + } erts_smp_mtx_lock(&schdlr_sspnd.mtx); + change_flags = 0; have_unlocked_plocks = 0; no = (int) new_no; - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); - if (changing) { - res = ERTS_SCHDLR_SSPND_YIELD_RESTART; +#ifdef ERTS_DIRTY_SCHEDULERS + if (!dirty_only) +#endif + { + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); + if (changing & ERTS_SCHDLR_SSPND_CHNG_ONLN) { + enqueue_wait: + p->flags |= F_SCHDLR_ONLN_WAITQ; + plp = proclist_create(p); + erts_proclist_store_last(&schdlr_sspnd.chngq, plp); + resume_proc = 0; + res = ERTS_SCHDLR_SSPND_YIELD_RESTART; + goto done; + } + plp = erts_proclist_peek_first(schdlr_sspnd.chngq); + if (!plp) { + ASSERT(schdlr_sspnd.changer == am_false); + } + else { + ASSERT(schdlr_sspnd.changer == am_true); + if (!erts_proclist_same(plp, p)) + goto enqueue_wait; + p->flags &= ~F_SCHDLR_ONLN_WAITQ; + erts_proclist_remove(&schdlr_sspnd.chngq, plp); + proclist_destroy(plp); + } + } + + *old_no = online = schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_NORMAL); +#ifndef ERTS_DIRTY_SCHEDULERS + if (no == online) { + res = ERTS_SCHDLR_SSPND_DONE; + goto done; } - else { - int online = *old_no = schdlr_sspnd.online; - if (no == schdlr_sspnd.online) { +#else /* ERTS_DIRTY_SCHEDULERS */ + dirty_online = schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_DIRTY_CPU); + if (dirty_only) + *old_no = dirty_online; + + ASSERT(dirty_online <= erts_no_dirty_cpu_schedulers); + + if (dirty_only) { + if (no > online) { + res = ERTS_SCHDLR_SSPND_EINVAL; + goto done; + } + dirty_no = no; + if (dirty_no == dirty_online) { res = ERTS_SCHDLR_SSPND_DONE; + goto done; } - else { - ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN - | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); - schdlr_sspnd.online = no; - if (no > online) { - int ix; - schdlr_sspnd.wait_curr_online = no; - if (ongoing_multi_scheduling_block()) { - for (ix = online; ix < no; ix++) - erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix)); - } - else { - if (plocks) { - have_unlocked_plocks = 1; - erts_smp_proc_unlock(p, plocks); - } - change_no_used_runqs(no); - - for (ix = online; ix < no; ix++) - resume_run_queue(ERTS_RUNQ_IX(ix)); + change_dirty = 1; + } else { + /* + * Adjust the number of dirty CPU schedulers online relative to the + * adjustment made to the number of normal schedulers online. + */ + int total_pct = erts_no_dirty_cpu_schedulers*100/erts_no_schedulers; + int onln_pct = no*total_pct/online; + dirty_no = dirty_online*onln_pct/100; + if (dirty_no == 0) + dirty_no = 1; + ASSERT(dirty_no <= erts_no_dirty_cpu_schedulers); - for (ix = no; ix < erts_no_run_queues; ix++) - suspend_run_queue(ERTS_RUNQ_IX(ix)); - } + if (no != online) + change_dirty = (dirty_no != dirty_online); + else { + dirty_only = 1; + if (dirty_no == dirty_online) { res = ERTS_SCHDLR_SSPND_DONE; + goto done; } - else /* if (no < online) */ { - if (p->scheduler_data->no <= no) { - res = ERTS_SCHDLR_SSPND_DONE; - schdlr_sspnd.wait_curr_online = no; + change_dirty = 1; + } + } + if (change_dirty) { + change_flags |= ERTS_SCHDLR_SSPND_CHNG_DCPU_ONLN; + schdlr_sspnd_set_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_DIRTY_CPU, + dirty_no); + } + + if (dirty_only) + increase = (dirty_no > dirty_online); + else +#endif /* ERTS_DIRTY_SCHEDULERS */ + { + change_flags |= ERTS_SCHDLR_SSPND_CHNG_ONLN; + schdlr_sspnd_set_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_NORMAL, + no); + increase = (no > online); + } + + erts_smp_atomic32_read_bor_nob(&schdlr_sspnd.changing, change_flags); + + res = ERTS_SCHDLR_SSPND_DONE; + if (increase) { + int ix; +#ifdef ERTS_DIRTY_SCHEDULERS + if (change_dirty) { + ErtsSchedulerSleepInfo* ssi; + if (schdlr_sspnd.msb.ongoing) { + for (ix = dirty_online; ix < dirty_no; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_sched_poke(ssi); } - else { - /* - * Yield! Current process needs to migrate - * before bif returns. - */ - res = ERTS_SCHDLR_SSPND_YIELD_DONE; - schdlr_sspnd.wait_curr_online = no+1; + } else { + for (ix = dirty_online; ix < dirty_no; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + scheduler_ssi_resume_wake(ssi); } - - if (ongoing_multi_scheduling_block()) { - for (ix = no; ix < online; ix++) - erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix)); + } + } + if (!dirty_only) +#endif + { + if (schdlr_sspnd.msb.ongoing|schdlr_sspnd.nmsb.ongoing) { + for (ix = online; ix < no; ix++) + erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix)); + } + else { + if (plocks) { + have_unlocked_plocks = 1; + erts_smp_proc_unlock(p, plocks); } - else { - if (plocks) { - have_unlocked_plocks = 1; - erts_smp_proc_unlock(p, plocks); - } + change_no_used_runqs(no); - change_no_used_runqs(no); - for (ix = no; ix < erts_no_run_queues; ix++) - suspend_run_queue(ERTS_RUNQ_IX(ix)); + for (ix = online; ix < no; ix++) + resume_run_queue(ERTS_RUNQ_IX(ix)); - for (ix = no; ix < online; ix++) { - ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); - wake_scheduler(rq); - } + for (ix = no; ix < erts_no_run_queues; ix++) + suspend_run_queue(ERTS_RUNQ_IX(ix)); + } + } + } + else /* if decrease */ { +#ifdef ERTS_DIRTY_SCHEDULERS + if (change_dirty) { + ErtsSchedulerSleepInfo* ssi; + if (schdlr_sspnd.msb.ongoing) { + for (ix = dirty_no; ix < dirty_online; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_sched_poke(ssi); } + } else { + for (ix = dirty_no; ix < dirty_online; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_smp_atomic32_read_bor_nob(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); } - - if (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online) { - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - if (plocks && !have_unlocked_plocks) { + } + if (!dirty_only) +#endif + { + if (schdlr_sspnd.msb.ongoing|schdlr_sspnd.nmsb.ongoing) { + for (ix = no; ix < online; ix++) + erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix)); + } + else { + if (plocks) { have_unlocked_plocks = 1; erts_smp_proc_unlock(p, plocks); } - erts_thr_progress_active(esdp, 0); - erts_thr_progress_prepare_wait(esdp); - end_wait = 1; - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - } - while (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online) - erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); - - ASSERT(res != ERTS_SCHDLR_SSPND_DONE - ? (ERTS_SCHDLR_SSPND_CHNG_WAITER - & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)) - : (ERTS_SCHDLR_SSPND_CHNG_WAITER - == erts_smp_atomic32_read_nob(&schdlr_sspnd.changing))); - erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, - ~ERTS_SCHDLR_SSPND_CHNG_WAITER); + change_no_used_runqs(no); + for (ix = no; ix < erts_no_run_queues; ix++) + suspend_run_queue(ERTS_RUNQ_IX(ix)); + for (ix = no; ix < online; ix++) { + ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); + wake_scheduler(rq); + } + } } } - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - if (end_wait) { - erts_thr_progress_finalize_wait(esdp); - erts_thr_progress_active(esdp, 1); + if (change_flags & ERTS_SCHDLR_SSPND_CHNG_ONLN) { + /* Suspend and wait for requested change to complete... */ + schdlr_sspnd.changer = p->common.id; + resume_proc = 0; + res = ERTS_SCHDLR_SSPND_YIELD_DONE; } + +done: + + ASSERT(schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_DIRTY_CPU) + <= schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_NORMAL)); + + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + if (have_unlocked_plocks) erts_smp_proc_lock(p, plocks); + if (resume_proc) { + if (!(plocks & ERTS_PROC_LOCK_STATUS)) + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + resume_process(p, plocks|ERTS_PROC_LOCK_STATUS); + if (!(plocks & ERTS_PROC_LOCK_STATUS)) + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + } + return res; } -#endif - ErtsSchedSuspendResult -erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) +erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int normal, int all) { - int ix, res, have_unlocked_plocks = 0, online; - erts_aint32_t changing; + int resume_proc, ix, res, have_unlocked_plocks = 0; ErtsProcList *plp; #ifdef ERTS_DIRTY_SCHEDULERS ErtsSchedulerSleepInfo* ssi; #endif + ErtsMultiSchedulingBlock *msbp; + erts_aint32_t chng_flg; + int have_blckd_flg; - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); -#ifdef ERTS_DIRTY_SCHEDULERS - changing |= (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing) - | erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing)); -#endif - if (changing) { - res = ERTS_SCHDLR_SSPND_YIELD_RESTART; /* Yield */ + if (normal) { + chng_flg = ERTS_SCHDLR_SSPND_CHNG_NMSB; + have_blckd_flg = F_HAVE_BLCKD_NMSCHED; + msbp = &schdlr_sspnd.nmsb; + } + else { + chng_flg = ERTS_SCHDLR_SSPND_CHNG_MSB; + have_blckd_flg = F_HAVE_BLCKD_MSCHED; + msbp = &schdlr_sspnd.msb; + } + + /* + * If we suspend current process we need to suspend before + * requesting the change; otherwise, we got a resume/suspend + * race... + */ + if (!on) { + /* We never suspend current process when unblocking... */ + resume_proc = 0; + } + else { + resume_proc = 1; + if (!(plocks & ERTS_PROC_LOCK_STATUS)) + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + suspend_process(p, p); + if (!(plocks & ERTS_PROC_LOCK_STATUS)) + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); } - else if (on) { /* ------ BLOCK ------ */ - if (schdlr_sspnd.msb.procs) { + + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + if (on) { /* ------ BLOCK ------ */ + if (msbp->chngq) { + ASSERT(msbp->ongoing); + p->flags |= have_blckd_flg; + goto wait_until_msb; + } + else if (msbp->blckrs) { + ASSERT(msbp->ongoing); plp = proclist_create(p); - erts_proclist_store_last(&schdlr_sspnd.msb.procs, plp); - p->flags |= F_HAVE_BLCKD_MSCHED; - ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1); -#ifdef ERTS_DIRTY_SCHEDULERS - ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) == 0); - ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active) == 0); -#endif + erts_proclist_store_last(&msbp->blckrs, plp); + p->flags |= have_blckd_flg; + ASSERT(schdlr_sspnd.active == ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0)); ASSERT(p->scheduler_data->no == 1); - res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; - } else { - int online = schdlr_sspnd.online; - p->flags |= F_HAVE_BLCKD_MSCHED; + if (schdlr_sspnd.msb.ongoing) + res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; + else + res = ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED; + } + else { + int online = (int) schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_NORMAL); + ASSERT(!msbp->ongoing); + p->flags |= have_blckd_flg; if (plocks) { have_unlocked_plocks = 1; erts_smp_proc_unlock(p, plocks); } - ASSERT(!ongoing_multi_scheduling_block()); - schdlr_sspnd.msb.ongoing = 1; - if (online == 1) { - res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; - ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1); -#ifdef ERTS_DIRTY_SCHEDULERS - ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) == 1); - ASSERT(!(erts_smp_atomic32_read_nob(&ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(0)->flags) - & ERTS_SSI_FLG_SUSPENDED)); - schdlr_sspnd.msb.dirty_cpu_wait_active = 0; - ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB - | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); - ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(0); - erts_smp_atomic32_read_bor_nob(&ssi->flags, ERTS_SSI_FLG_SUSPENDED); - wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); - while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) - != schdlr_sspnd.msb.dirty_cpu_wait_active) - erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); - ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); - - schdlr_sspnd.msb.dirty_io_wait_active = 0; - ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB - | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); - for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { - ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix); - erts_smp_atomic32_read_bor_nob(&ssi->flags, - ERTS_SSI_FLG_SUSPENDED); - } - wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0); - while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active) - != schdlr_sspnd.msb.dirty_io_wait_active) - erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); - ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); -#endif + ASSERT(!msbp->ongoing); + msbp->ongoing = 1; + if (schdlr_sspnd.active == ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0) + || (normal && schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_NORMAL) == 1)) { ASSERT(p->scheduler_data->no == 1); + plp = proclist_create(p); + erts_proclist_store_last(&msbp->blckrs, plp); + if (schdlr_sspnd.msb.ongoing) + res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; + else + res = ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED; } else { - ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB - | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); - if (p->scheduler_data->no == 1) { - res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; - schdlr_sspnd.msb.wait_active = 1; - } - else { - /* - * Yield! Current process needs to migrate - * before bif returns. - */ - res = ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED; - schdlr_sspnd.msb.wait_active = 2; - } - -#ifdef ERTS_DIRTY_SCHEDULERS - schdlr_sspnd.msb.dirty_cpu_wait_active = 0; - ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB - | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); - for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { - ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); - erts_smp_atomic32_read_bor_nob(&ssi->flags, - ERTS_SSI_FLG_SUSPENDED); - } - wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); - while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) - != schdlr_sspnd.msb.dirty_cpu_wait_active) - erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); - ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); - ASSERT(schdlr_sspnd.dirty_cpu_curr_online == schdlr_sspnd.dirty_cpu_online); - - schdlr_sspnd.msb.dirty_io_wait_active = 0; - ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB - | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); - for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { - ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix); - erts_smp_atomic32_read_bor_nob(&ssi->flags, - ERTS_SSI_FLG_SUSPENDED); - } - wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0); - while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active) - != schdlr_sspnd.msb.dirty_io_wait_active) - erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); - ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); - ASSERT(schdlr_sspnd.dirty_io_curr_online == schdlr_sspnd.dirty_io_online); -#endif + erts_smp_atomic32_read_bor_nob(&schdlr_sspnd.changing, + chng_flg); change_no_used_runqs(1); for (ix = 1; ix < erts_no_run_queues; ix++) suspend_run_queue(ERTS_RUNQ_IX(ix)); @@ -7938,84 +7818,84 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) wake_scheduler(rq); } - if (erts_smp_atomic32_read_nob(&schdlr_sspnd.active) - != schdlr_sspnd.msb.wait_active) { - ErtsSchedulerData *esdp; - - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - - if (plocks && !have_unlocked_plocks) { - have_unlocked_plocks = 1; - erts_smp_proc_unlock(p, plocks); +#ifdef ERTS_DIRTY_SCHEDULERS + if (!normal) { + for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_smp_atomic32_read_bor_nob(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); } + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); - esdp = ERTS_PROC_GET_SCHDATA(p); - - erts_thr_progress_active(esdp, 0); - erts_thr_progress_prepare_wait(esdp); - - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - - while (erts_smp_atomic32_read_nob(&schdlr_sspnd.active) - != schdlr_sspnd.msb.wait_active) - erts_smp_cnd_wait(&schdlr_sspnd.cnd, - &schdlr_sspnd.mtx); - - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - - erts_thr_progress_active(esdp, 1); - erts_thr_progress_finalize_wait(esdp); + for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix); + erts_smp_atomic32_read_bor_nob(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0); + } +#endif - erts_smp_mtx_lock(&schdlr_sspnd.mtx); + wait_until_msb: - } + ASSERT(chng_flg & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)); - ASSERT(res != ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED - ? (ERTS_SCHDLR_SSPND_CHNG_WAITER - & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)) - : (ERTS_SCHDLR_SSPND_CHNG_WAITER - == erts_smp_atomic32_read_nob(&schdlr_sspnd.changing))); - erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, - ~ERTS_SCHDLR_SSPND_CHNG_WAITER); + plp = proclist_create(p); + erts_proclist_store_last(&msbp->chngq, plp); + resume_proc = 0; + if (schdlr_sspnd.msb.ongoing) + res = ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED; + else + res = ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED; } - plp = proclist_create(p); - erts_proclist_store_last(&schdlr_sspnd.msb.procs, plp); ASSERT(p->scheduler_data); } } - else if (!ongoing_multi_scheduling_block()) { - /* unblock not ongoing */ - ASSERT(!schdlr_sspnd.msb.procs); - res = ERTS_SCHDLR_SSPND_DONE; + else if (!msbp->ongoing) { + ASSERT(!msbp->blckrs); + goto unblock_res; } else { /* ------ UNBLOCK ------ */ - if (p->flags & F_HAVE_BLCKD_MSCHED) { - ErtsProcList *plp = erts_proclist_peek_first(schdlr_sspnd.msb.procs); - - while (plp) { - ErtsProcList *tmp_plp = plp; - plp = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp); - if (erts_proclist_same(tmp_plp, p)) { - erts_proclist_remove(&schdlr_sspnd.msb.procs, tmp_plp); - proclist_destroy(tmp_plp); - if (!all) - break; + if (p->flags & have_blckd_flg) { + ErtsProcList *plps[2]; + ErtsProcList *plp; + int limit = 0; + + plps[limit++] = erts_proclist_peek_first(msbp->blckrs); + if (all) + plps[limit++] = erts_proclist_peek_first(msbp->chngq); + + for (ix = 0; ix < limit; ix++) { + plp = plps[ix]; + while (plp) { + ErtsProcList *tmp_plp = plp; + plp = erts_proclist_peek_next(msbp->blckrs, plp); + if (erts_proclist_same(tmp_plp, p)) { + erts_proclist_remove(&msbp->blckrs, tmp_plp); + proclist_destroy(tmp_plp); + if (!all) + break; + } } } } - if (schdlr_sspnd.msb.procs) - res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; - else { - ERTS_SCHDLR_SSPND_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0); - p->flags &= ~F_HAVE_BLCKD_MSCHED; - schdlr_sspnd.msb.ongoing = 0; - if (schdlr_sspnd.online == 1) { + if (!msbp->blckrs && !msbp->chngq) { + int online = (int) schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_NORMAL); + erts_smp_atomic32_read_bor_nob(&schdlr_sspnd.changing, + chng_flg); + p->flags &= ~have_blckd_flg; + msbp->ongoing = 0; + if (online == 1) { /* No normal schedulers to resume */ - ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1); - ERTS_SCHDLR_SSPND_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_MSB); + ASSERT(schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_NORMAL) == 1); +#ifndef ERTS_DIRTY_SCHEDULERS + erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, + ~chng_flg); +#endif } - else { - online = schdlr_sspnd.online; + else if (!(schdlr_sspnd.msb.ongoing|schdlr_sspnd.nmsb.ongoing)) { if (plocks) { have_unlocked_plocks = 1; erts_smp_proc_unlock(p, plocks); @@ -8031,83 +7911,91 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) suspend_run_queue(ERTS_RUNQ_IX(ix)); } #ifdef ERTS_DIRTY_SCHEDULERS - ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0); - schdlr_sspnd.msb.dirty_cpu_wait_active = schdlr_sspnd.dirty_cpu_online; - for (ix = 0; ix < schdlr_sspnd.dirty_cpu_online; ix++) { - ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); - scheduler_ssi_resume_wake(ssi); - erts_smp_atomic32_read_band_nob(&ssi->flags, - ~ERTS_SSI_FLG_SUSPENDED); - } - wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); - - ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0); - schdlr_sspnd.msb.dirty_io_wait_active = erts_no_dirty_io_schedulers; - for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { - ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix); - scheduler_ssi_resume_wake(ssi); - erts_smp_atomic32_read_band_nob(&ssi->flags, - ~ERTS_SSI_FLG_SUSPENDED); - } - wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0); + if (!normal) { + ASSERT(!schdlr_sspnd.msb.ongoing); + online = (int) schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, + ERTS_SCHED_DIRTY_CPU); + for (ix = 0; ix < online; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + scheduler_ssi_resume_wake(ssi); + } + + for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix); + scheduler_ssi_resume_wake(ssi); + } + } #endif - res = ERTS_SCHDLR_SSPND_DONE; } + + unblock_res: + if (schdlr_sspnd.msb.ongoing) + res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; + else if (schdlr_sspnd.nmsb.ongoing) + res = ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED; + else + res = ERTS_SCHDLR_SSPND_DONE; } erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + if (have_unlocked_plocks) erts_smp_proc_lock(p, plocks); - return res; -} -#ifdef DEBUG -void -erts_dbg_multi_scheduling_return_trap(Process *p, Eterm return_value) -{ - if (return_value == am_blocked) { - erts_aint32_t active = erts_smp_atomic32_read_nob(&schdlr_sspnd.active); - ASSERT(1 <= active && active <= 2); - ASSERT(ERTS_PROC_GET_SCHDATA(p)->no == 1); + if (resume_proc) { + if (!(plocks & ERTS_PROC_LOCK_STATUS)) + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + resume_process(p, plocks|ERTS_PROC_LOCK_STATUS); + if (!(plocks & ERTS_PROC_LOCK_STATUS)) + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); } + + return res; } -#endif int erts_is_multi_scheduling_blocked(void) { int res; erts_smp_mtx_lock(&schdlr_sspnd.mtx); - res = schdlr_sspnd.msb.procs != NULL; + if (schdlr_sspnd.msb.blckrs) + res = 1; + else if (schdlr_sspnd.nmsb.blckrs) + res = -1; + else + res = 0; erts_smp_mtx_unlock(&schdlr_sspnd.mtx); return res; } Eterm -erts_multi_scheduling_blockers(Process *p) +erts_multi_scheduling_blockers(Process *p, int normal) { Eterm res = NIL; + ErtsMultiSchedulingBlock *msbp; + + msbp = normal ? &schdlr_sspnd.nmsb : &schdlr_sspnd.msb; erts_smp_mtx_lock(&schdlr_sspnd.mtx); - if (!erts_proclist_is_empty(schdlr_sspnd.msb.procs)) { + if (!erts_proclist_is_empty(msbp->blckrs)) { Eterm *hp, *hp_end; ErtsProcList *plp1, *plp2; Uint max_size = 0; - for (plp1 = erts_proclist_peek_first(schdlr_sspnd.msb.procs); + for (plp1 = erts_proclist_peek_first(msbp->blckrs); plp1; - plp1 = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp1)) { + plp1 = erts_proclist_peek_next(msbp->blckrs, plp1)) { max_size += 2; } ASSERT(max_size); hp = HAlloc(p, max_size); hp_end = hp + max_size; - for (plp1 = erts_proclist_peek_first(schdlr_sspnd.msb.procs); + for (plp1 = erts_proclist_peek_first(msbp->blckrs); plp1; - plp1 = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp1)) { - for (plp2 = erts_proclist_peek_first(schdlr_sspnd.msb.procs); + plp1 = erts_proclist_peek_next(msbp->blckrs, plp1)) { + for (plp2 = erts_proclist_peek_first(msbp->blckrs); plp2->pid != plp1->pid; - plp2 = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp2)); + plp2 = erts_proclist_peek_next(msbp->blckrs, plp2)); if (plp2 == plp1) { res = CONS(hp, plp1->pid, res); hp += 2; @@ -8176,39 +8064,6 @@ sched_thread_func(void *vesdp) #endif erts_thread_init_float(); - if (no == 1) { - erts_thr_progress_active(esdp, 0); - erts_thr_progress_prepare_wait(esdp); - } - - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - - ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.changing) - & ERTS_SCHDLR_SSPND_CHNG_ONLN); - - if (--schdlr_sspnd.curr_online == schdlr_sspnd.wait_curr_online) { - erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, - ~ERTS_SCHDLR_SSPND_CHNG_ONLN); - if (no != 1) -#ifdef ERTS_DIRTY_SCHEDULERS - erts_smp_cnd_broadcast(&schdlr_sspnd.cnd); -#else - erts_smp_cnd_signal(&schdlr_sspnd.cnd); -#endif - } - - if (no == 1) { - while (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online) - erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); - ERTS_SCHDLR_SSPND_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); - } - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - - if (no == 1) { - erts_thr_progress_finalize_wait(esdp); - erts_thr_progress_active(esdp, 1); - } - #ifdef ERTS_DO_VERIFY_UNUSED_TEMP_ALLOC esdp->verify_unused_temp_alloc = erts_alloc_get_verify_unused_temp_alloc( @@ -8263,24 +8118,6 @@ sched_dirty_cpu_thread_func(void *vesdp) #endif erts_thread_init_float(); - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing) - & ERTS_SCHDLR_SSPND_CHNG_ONLN); - - if (--schdlr_sspnd.dirty_cpu_curr_online == schdlr_sspnd.dirty_cpu_wait_curr_online) { - erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_cpu_changing, - ~ERTS_SCHDLR_SSPND_CHNG_ONLN); - if (no != 1) - erts_smp_cnd_broadcast(&schdlr_sspnd.cnd); - } - - if (no == 1) { - while (schdlr_sspnd.dirty_cpu_curr_online != schdlr_sspnd.dirty_cpu_wait_curr_online) - erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); - ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); - } - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - process_main(); /* No schedulers should *ever* terminate */ erts_exit(ERTS_ABORT_EXIT, @@ -8326,24 +8163,6 @@ sched_dirty_io_thread_func(void *vesdp) #endif erts_thread_init_float(); - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing) - & ERTS_SCHDLR_SSPND_CHNG_ONLN); - - if (--schdlr_sspnd.dirty_io_curr_online == schdlr_sspnd.dirty_io_wait_curr_online) { - erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_io_changing, - ~ERTS_SCHDLR_SSPND_CHNG_ONLN); - if (no != 1) - erts_smp_cnd_broadcast(&schdlr_sspnd.cnd); - } - - if (no == 1) { - while (schdlr_sspnd.dirty_io_curr_online != schdlr_sspnd.dirty_io_wait_curr_online) - erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); - ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); - } - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - process_main(); /* No schedulers should *ever* terminate */ erts_exit(ERTS_ABORT_EXIT, @@ -9616,11 +9435,11 @@ Process *schedule(Process *p, int calls) if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { ErtsProcList *pnd_xtrs = rq->procs.pending_exiters; if (erts_proclist_fetch(&pnd_xtrs, NULL)) { - rq->procs.pending_exiters = NULL; - erts_smp_runq_unlock(rq); - handle_pending_exiters(pnd_xtrs); - erts_smp_runq_lock(rq); - } + rq->procs.pending_exiters = NULL; + erts_smp_runq_unlock(rq); + handle_pending_exiters(pnd_xtrs); + erts_smp_runq_lock(rq); + } if (rq->check_balance_reds <= 0) check_balance(rq); @@ -12537,14 +12356,39 @@ erts_continue_exit_process(Process *p) #endif #ifdef ERTS_SMP + if (p->flags & F_SCHDLR_ONLN_WAITQ) + abort_sched_onln_chng_waitq(p); + if (p->flags & F_HAVE_BLCKD_MSCHED) { ErtsSchedSuspendResult ssr; - ssr = erts_block_multi_scheduling(p, ERTS_PROC_LOCK_MAIN, 0, 1); + ssr = erts_block_multi_scheduling(p, ERTS_PROC_LOCK_MAIN, 0, 0, 1); + switch (ssr) { + case ERTS_SCHDLR_SSPND_YIELD_RESTART: + goto yield; + case ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED: + case ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED: + case ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED: + case ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED: + case ERTS_SCHDLR_SSPND_DONE: + case ERTS_SCHDLR_SSPND_YIELD_DONE: + p->flags &= ~F_HAVE_BLCKD_MSCHED; + break; + case ERTS_SCHDLR_SSPND_EINVAL: + default: + erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: %d\n", + __FILE__, __LINE__, (int) ssr); + } + } + if (p->flags & F_HAVE_BLCKD_NMSCHED) { + ErtsSchedSuspendResult ssr; + ssr = erts_block_multi_scheduling(p, ERTS_PROC_LOCK_MAIN, 0, 1, 1); switch (ssr) { case ERTS_SCHDLR_SSPND_YIELD_RESTART: goto yield; case ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED: + case ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED: case ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED: + case ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED: case ERTS_SCHDLR_SSPND_DONE: case ERTS_SCHDLR_SSPND_YIELD_DONE: p->flags &= ~F_HAVE_BLCKD_MSCHED; diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 6b370e630e..c88bd7056c 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -79,10 +79,8 @@ struct ErtsNodesMonitor_; #define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT 0 #define ERTS_MAX_NO_OF_SCHEDULERS 1024 -#ifdef ERTS_DIRTY_SCHEDULERS #define ERTS_MAX_NO_OF_DIRTY_CPU_SCHEDULERS ERTS_MAX_NO_OF_SCHEDULERS #define ERTS_MAX_NO_OF_DIRTY_IO_SCHEDULERS ERTS_MAX_NO_OF_SCHEDULERS -#endif #define ERTS_DEFAULT_MAX_PROCESSES (1 << 18) @@ -246,7 +244,9 @@ extern int erts_sched_thread_suggested_stack_size; typedef enum { ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED, + ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED, ERTS_SCHDLR_SSPND_YIELD_DONE_MSCHED_BLOCKED, + ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED, ERTS_SCHDLR_SSPND_DONE, ERTS_SCHDLR_SSPND_YIELD_RESTART, ERTS_SCHDLR_SSPND_YIELD_DONE, @@ -1339,6 +1339,8 @@ extern int erts_system_profile_ts_type; #define F_OFF_HEAP_MSGQ_CHNG (1 << 14) /* Off heap msg queue changing */ #define F_ABANDONED_HEAP_USE (1 << 15) /* Have usage of abandoned heap */ #define F_DELAY_GC (1 << 16) /* Similar to disable GC (see below) */ +#define F_SCHDLR_ONLN_WAITQ (1 << 17) /* Process enqueued waiting to change schedulers online */ +#define F_HAVE_BLCKD_NMSCHED (1 << 18) /* Process has blocked normal multi-scheduling */ /* * F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent @@ -1533,6 +1535,7 @@ int erts_setup_nif_gc(Process* proc, Eterm** objv, int* nobj); /* see erl_nif.c void erts_destroy_nif_export(void *); /* see erl_nif.c */ ErtsProcList *erts_proclist_create(Process *); +ErtsProcList *erts_proclist_copy(ErtsProcList *); void erts_proclist_destroy(ErtsProcList *); ERTS_GLB_INLINE int erts_proclist_same(ErtsProcList *, Process *); @@ -1718,28 +1721,21 @@ void erts_schedule_complete_off_heap_message_queue_change(Eterm pid); #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) int erts_dbg_check_halloc_lock(Process *p); #endif -#ifdef DEBUG -void erts_dbg_multi_scheduling_return_trap(Process *, Eterm); -#endif -int erts_get_max_no_executing_schedulers(void); #if defined(ERTS_SMP) || defined(ERTS_DIRTY_SCHEDULERS) -ErtsSchedSuspendResult -erts_schedulers_state(Uint *, Uint *, Uint *, Uint *, Uint *, Uint *, int); +void +erts_schedulers_state(Uint *, Uint *, Uint *, Uint *, Uint *, Uint *, Uint *, Uint *); #endif #ifdef ERTS_SMP ErtsSchedSuspendResult erts_set_schedulers_online(Process *p, ErtsProcLocks plocks, Sint new_no, - Sint *old_no -#ifdef ERTS_DIRTY_SCHEDULERS - , int dirty_only -#endif - ); + Sint *old_no, + int dirty_only); ErtsSchedSuspendResult -erts_block_multi_scheduling(Process *, ErtsProcLocks, int, int); +erts_block_multi_scheduling(Process *, ErtsProcLocks, int, int, int); int erts_is_multi_scheduling_blocked(void); -Eterm erts_multi_scheduling_blockers(Process *); +Eterm erts_multi_scheduling_blockers(Process *, int); void erts_start_schedulers(void); void erts_alloc_notify_delayed_dealloc(int); void erts_alloc_ensure_handle_delayed_dealloc_call(int); diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 1f284228db..f998e7d4d2 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -54,6 +54,7 @@ sct_cmd/1, sbt_cmd/1, scheduler_threads/1, + scheduler_suspend_basic/1, scheduler_suspend/1, dirty_scheduler_threads/1, dirty_scheduler_exit/1, @@ -69,8 +70,10 @@ all() -> [equal, few_low, many_low, equal_with_part_time_high, equal_with_part_time_max, equal_and_high_with_part_time_max, equal_with_high, - equal_with_high_max, bound_process, - {group, scheduler_bind}, scheduler_threads, scheduler_suspend, + equal_with_high_max, + bound_process, + {group, scheduler_bind}, scheduler_threads, + scheduler_suspend_basic, scheduler_suspend, dirty_scheduler_threads, dirty_scheduler_exit, reader_groups]. @@ -1131,6 +1134,7 @@ dirty_schedulers_online_test(true) -> dirty_schedulers_online_smp_test(erlang:system_info(schedulers_online)). dirty_schedulers_online_smp_test(SchedOnln) when SchedOnln < 4 -> ok; dirty_schedulers_online_smp_test(SchedOnln) -> + receive after 500 -> ok end, DirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), SchedOnln = DirtyCPUSchedOnln, HalfSchedOnln = SchedOnln div 2, @@ -1139,9 +1143,11 @@ dirty_schedulers_online_smp_test(SchedOnln) -> HalfDirtyCPUSchedOnln = erlang:system_flag(schedulers_online, SchedOnln), DirtyCPUSchedOnln = erlang:system_flag(dirty_cpu_schedulers_online, HalfDirtyCPUSchedOnln), + receive after 500 -> ok end, HalfDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), QrtrDirtyCPUSchedOnln = HalfDirtyCPUSchedOnln div 2, SchedOnln = erlang:system_flag(schedulers_online, HalfSchedOnln), + receive after 500 -> ok end, QrtrDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), ok. @@ -1214,6 +1220,113 @@ wait_dse([Pid|Pids]) -> dirty_sleeper() -> erlang:nif_error({error,?MODULE}). +scheduler_suspend_basic(Config) when is_list(Config) -> + case erlang:system_info(multi_scheduling) of + disabled -> + {skip, "Nothing to test"}; + _ -> + Onln = erlang:system_info(schedulers_online), + try + scheduler_suspend_basic_test() + after + erlang:system_flag(schedulers_online, Onln) + end + end. + +scheduler_suspend_basic_test() -> + %% The receives after setting scheduler states are there + %% since the operation is not fully synchronous. For example, + %% we do not wait for dirty cpu schedulers online to complete + %% before returning from erlang:system_flag(schedulers_online, _). + + erlang:system_flag(schedulers_online, + erlang:system_info(schedulers)), + try + erlang:system_flag(dirty_cpu_schedulers_online, + erlang:system_info(dirty_cpu_schedulers)), + receive after 500 -> ok end + catch + _ : _ -> + ok + end, + + S0 = sched_state(), + io:format("~p~n", [S0]), + {{normal,NTot0,NOnln0,NAct0}, + {dirty_cpu,DCTot0,DCOnln0,DCAct0}, + {dirty_io,DITot0,DIOnln0,DIAct0}} = S0, + enabled = erlang:system_info(multi_scheduling), + + DCOne = case DCTot0 of + 0 -> 0; + _ -> 1 + end, + + blocked_normal = erlang:system_flag(multi_scheduling, block_normal), + blocked_normal = erlang:system_info(multi_scheduling), + {{normal,NTot0,NOnln0,1}, + {dirty_cpu,DCTot0,DCOnln0,DCAct0}, + {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), + + NOnln0 = erlang:system_flag(schedulers_online, 1), + receive after 500 -> ok end, + {{normal,NTot0,1,1}, + {dirty_cpu,DCTot0,DCOne,DCOne}, + {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), + + 1 = erlang:system_flag(schedulers_online, NOnln0), + receive after 500 -> ok end, + {{normal,NTot0,NOnln0,1}, + {dirty_cpu,DCTot0,DCOnln0,DCAct0}, + {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), + + blocked = erlang:system_flag(multi_scheduling, block), + blocked = erlang:system_info(multi_scheduling), + receive after 500 -> ok end, + {{normal,NTot0,NOnln0,1}, + {dirty_cpu,DCTot0,DCOnln0,0}, + {dirty_io,DITot0,DIOnln0,0}} = sched_state(), + + NOnln0 = erlang:system_flag(schedulers_online, 1), + receive after 500 -> ok end, + {{normal,NTot0,1,1}, + {dirty_cpu,DCTot0,DCOne,0}, + {dirty_io,DITot0,DIOnln0,0}} = sched_state(), + + 1 = erlang:system_flag(schedulers_online, NOnln0), + receive after 500 -> ok end, + {{normal,NTot0,NOnln0,1}, + {dirty_cpu,DCTot0,DCOnln0,0}, + {dirty_io,DITot0,DIOnln0,0}} = sched_state(), + + blocked = erlang:system_flag(multi_scheduling, unblock_normal), + blocked = erlang:system_info(multi_scheduling), + {{normal,NTot0,NOnln0,1}, + {dirty_cpu,DCTot0,DCOnln0,0}, + {dirty_io,DITot0,DIOnln0,0}} = sched_state(), + + enabled = erlang:system_flag(multi_scheduling, unblock), + enabled = erlang:system_info(multi_scheduling), + receive after 500 -> ok end, + {{normal,NTot0,NOnln0,NAct0}, + {dirty_cpu,DCTot0,DCOnln0,DCAct0}, + {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), + + NOnln0 = erlang:system_flag(schedulers_online, 1), + receive after 500 -> ok end, + {{normal,NTot0,1,1}, + {dirty_cpu,DCTot0,DCOne,DCOne}, + {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), + + 1 = erlang:system_flag(schedulers_online, NOnln0), + receive after 500 -> ok end, + {{normal,NTot0,NOnln0,NAct0}, + {dirty_cpu,DCTot0,DCOnln0,DCAct0}, + {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(), + + ok. + + scheduler_suspend(Config) when is_list(Config) -> ?line Dog = ?t:timetrap(?t:minutes(5)), ?line lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end, @@ -1238,12 +1351,17 @@ scheduler_suspend_test(Config, Schedulers) -> ?line [ok] = mcall(Node, [fun () -> sst0_loop(300) end]), ?line [ok] = mcall(Node, [fun () -> sst1_loop(300) end]), ?line [ok] = mcall(Node, [fun () -> sst2_loop(300) end]), - ?line [ok, ok, ok, ok, ok] = mcall(Node, - [fun () -> sst0_loop(200) end, - fun () -> sst1_loop(200) end, - fun () -> sst2_loop(200) end, - fun () -> sst2_loop(200) end, - fun () -> sst3_loop(Sched, 200) end]), + ?line [ok] = mcall(Node, [fun () -> sst4_loop(300) end]), + ?line [ok] = mcall(Node, [fun () -> sst5_loop(300) end]), + ?line [ok, ok, ok, ok, + ok, ok, ok] = mcall(Node, + [fun () -> sst0_loop(200) end, + fun () -> sst1_loop(200) end, + fun () -> sst2_loop(200) end, + fun () -> sst2_loop(200) end, + fun () -> sst3_loop(Sched, 200) end, + fun () -> sst4_loop(200) end, + fun () -> sst5_loop(200) end]), ?line [SState] = mcall(Node, [fun () -> case Sched == SchedOnln of false -> @@ -1320,6 +1438,20 @@ sst3_loop_with_dirty_schedulers(S, DS, N) -> erlang:system_flag(dirty_cpu_schedulers_online, DS), sst3_loop_with_dirty_schedulers(S, DS, N-1). +sst4_loop(0) -> + ok; +sst4_loop(N) -> + erlang:system_flag(multi_scheduling, block_normal), + erlang:system_flag(multi_scheduling, unblock_normal), + sst4_loop(N-1). + +sst5_loop(0) -> + ok; +sst5_loop(N) -> + erlang:system_flag(multi_scheduling, block_normal), + erlang:system_flag(multi_scheduling, unblock_normal), + sst5_loop(N-1). + reader_groups(Config) when is_list(Config) -> %% White box testing. These results are correct, but other results %% could be too... @@ -1598,6 +1730,34 @@ reader_groups_map(CPUT, Groups) -> %% Utils %% +sched_state() -> + sched_state(erlang:system_info(all_schedulers_state), + undefined, + {dirty_cpu,0,0,0}, + {dirty_io,0,0,0}). + + +sched_state([], N, DC, DI) -> + try + chk_basic(N), + chk_basic(DC), + chk_basic(DI), + {N, DC, DI} + catch + _ : _ -> + ?t:fail({inconsisten_scheduler_state, {N, DC, DI}}) + end; +sched_state([{normal, _, _, _} = S | Rest], _S, DC, DI) -> + sched_state(Rest, S, DC, DI); +sched_state([{dirty_cpu, _, _, _} = DC | Rest], S, _DC, DI) -> + sched_state(Rest, S, DC, DI); +sched_state([{dirty_io, _, _, _} = DI | Rest], S, DC, _DI) -> + sched_state(Rest, S, DC, DI). + +chk_basic({_Type, Tot, Onln, Act}) -> + true = Tot >= Onln, + true = Onln >= Act. + l(Id) -> {logical, Id}. diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index db17c53ff3..2ea2de4c70 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 9bf8d13fde..dfbd116d6e 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2300,8 +2300,8 @@ subtract(_,_) -> MinBinVHeapSize :: non_neg_integer(), OldMinBinVHeapSize :: non_neg_integer(); (multi_scheduling, BlockState) -> OldBlockState when - BlockState :: block | unblock, - OldBlockState :: block | unblock | enabled; + BlockState :: block | unblock | block_normal | unblock_normal, + OldBlockState :: blocked | disabled | enabled; (scheduler_bind_type, How) -> OldBindType when How :: scheduler_bind_type() | default_bind, OldBindType :: scheduler_bind_type(); @@ -2446,14 +2446,15 @@ tuple_to_list(_Tuple) -> logical_processors_available | logical_processors_online) -> unknown | pos_integer(); (machine) -> string(); + (message_queue_data) -> message_queue_data(); (min_heap_size) -> {min_heap_size, MinHeapSize :: pos_integer()}; (min_bin_vheap_size) -> {min_bin_vheap_size, MinBinVHeapSize :: pos_integer()}; (modified_timing_level) -> integer() | undefined; - (multi_scheduling) -> disabled | blocked | enabled; + (multi_scheduling) -> disabled | blocked | blocked_normal | enabled; (multi_scheduling_blockers) -> [Pid :: pid()]; (nif_version) -> string(); - (message_queue_data) -> message_queue_data(); + (normal_multi_scheduling_blockers) -> [Pid :: pid()]; (otp_release) -> string(); (os_monotonic_time_source) -> [{atom(),term()}]; (os_system_time_source) -> [{atom(),term()}]; -- cgit v1.2.3 From e15789aaf653543e4d7e0bcfdb769ffea67066c7 Mon Sep 17 00:00:00 2001 From: Lars Thorsen Date: Fri, 11 Mar 2016 07:30:01 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 56336 -> 56336 bytes erts/preloaded/ebin/erlang.beam | Bin 102116 -> 102112 bytes erts/preloaded/ebin/erts_internal.beam | Bin 6516 -> 6500 bytes erts/preloaded/ebin/init.beam | Bin 48764 -> 48764 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1468 -> 1468 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1340 -> 1340 bytes erts/preloaded/ebin/prim_file.beam | Bin 44980 -> 45004 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72716 -> 72716 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23424 -> 23424 bytes erts/preloaded/ebin/zlib.beam | Bin 14176 -> 14176 bytes 10 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 4a6fb6109f..f1ae49150b 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 36862802ca..990973c57d 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 21dde7f257..7461e3bc5b 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 9b0fc82bed..0060a2cf42 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index c8166d5ed7..b1868d7e10 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index ddcc3886a4..dc16909d26 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 9256e35553..f6e15ba499 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 72065661c5..673627498e 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 2a0b33279d..813060c41d 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 0d3d1d9343..5ce932aeec 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From c062dfc485ad0d51d648701950f77ef8f51d4f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 8 Mar 2016 19:35:31 +0100 Subject: Modernize use of timetraps --- erts/emulator/test/a_SUITE.erl | 25 +---- erts/emulator/test/after_SUITE.erl | 33 +------ erts/emulator/test/alloc_SUITE.erl | 33 +------ erts/emulator/test/async_ports_SUITE.erl | 37 ++++---- erts/emulator/test/bif_SUITE.erl | 33 +------ erts/emulator/test/big_SUITE.erl | 30 +----- erts/emulator/test/bs_construct_SUITE.erl | 22 +---- erts/emulator/test/bs_match_misc_SUITE.erl | 22 +---- erts/emulator/test/bs_utf_SUITE.erl | 32 +------ erts/emulator/test/busy_port_SUITE.erl | 56 +++-------- erts/emulator/test/call_trace_SUITE.erl | 29 +----- erts/emulator/test/code_parallel_load_SUITE.erl | 45 +++------ erts/emulator/test/crypto_SUITE.erl | 29 +----- erts/emulator/test/ddll_SUITE.erl | 83 +--------------- erts/emulator/test/decode_packet_SUITE.erl | 28 ++---- erts/emulator/test/distribution_SUITE.erl | 54 ++--------- erts/emulator/test/driver_SUITE.erl | 24 ++--- erts/emulator/test/erl_drv_thread_SUITE.erl | 21 +---- erts/emulator/test/erl_link_SUITE.erl | 37 +++----- erts/emulator/test/erts_debug_SUITE.erl | 32 +------ erts/emulator/test/estone_SUITE.erl | 34 +------ erts/emulator/test/evil_SUITE.erl | 35 +------ erts/emulator/test/exception_SUITE.erl | 23 +---- erts/emulator/test/float_SUITE.erl | 30 +----- erts/emulator/test/fun_SUITE.erl | 36 +------ erts/emulator/test/fun_r13_SUITE.erl | 35 +------ erts/emulator/test/gc_SUITE.erl | 32 ++----- erts/emulator/test/guard_SUITE.erl | 23 +---- erts/emulator/test/hash_SUITE.erl | 38 ++------ erts/emulator/test/hibernate_SUITE.erl | 38 ++------ erts/emulator/test/list_bif_SUITE.erl | 34 +------ erts/emulator/test/match_spec_SUITE.erl | 34 +------ erts/emulator/test/message_queue_data_SUITE.erl | 34 +------ erts/emulator/test/module_info_SUITE.erl | 31 +----- erts/emulator/test/monitor_SUITE.erl | 30 +----- erts/emulator/test/mtx_SUITE.erl | 63 +++++-------- erts/emulator/test/nested_SUITE.erl | 22 +---- erts/emulator/test/nif_SUITE.erl | 26 +---- erts/emulator/test/node_container_SUITE.erl | 26 ++--- erts/emulator/test/nofrag_SUITE.erl | 32 +------ erts/emulator/test/old_scheduler_SUITE.erl | 29 +----- erts/emulator/test/op_SUITE.erl | 32 +------ erts/emulator/test/port_SUITE.erl | 120 ++++++------------------ erts/emulator/test/port_bif_SUITE.erl | 34 ++----- erts/emulator/test/process_SUITE.erl | 36 +++---- erts/emulator/test/receive_SUITE.erl | 30 +----- erts/emulator/test/ref_SUITE.erl | 33 +------ erts/emulator/test/register_SUITE.erl | 35 +------ erts/emulator/test/scheduler_SUITE.erl | 32 ++----- erts/emulator/test/send_term_SUITE.erl | 32 +------ erts/emulator/test/sensitive_SUITE.erl | 32 +------ erts/emulator/test/signal_SUITE.erl | 28 ++---- erts/emulator/test/smoke_test_SUITE.erl | 30 +----- erts/emulator/test/statistics_SUITE.erl | 15 +-- erts/emulator/test/system_info_SUITE.erl | 40 ++------ erts/emulator/test/system_profile_SUITE.erl | 41 ++------ erts/emulator/test/timer_bif_SUITE.erl | 23 ++--- erts/emulator/test/trace_SUITE.erl | 114 ++++------------------ erts/emulator/test/trace_call_count_SUITE.erl | 11 +-- erts/emulator/test/trace_call_time_SUITE.erl | 30 ++---- erts/emulator/test/trace_local_SUITE.erl | 59 ++---------- erts/emulator/test/trace_meta_SUITE.erl | 28 ++---- erts/emulator/test/trace_port_SUITE.erl | 39 ++------ erts/emulator/test/unique_SUITE.erl | 27 +----- erts/emulator/test/z_SUITE.erl | 36 +------ 65 files changed, 430 insertions(+), 1897 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl index a9bba9548b..ec0c42db06 100644 --- a/erts/emulator/test/a_SUITE.erl +++ b/erts/emulator/test/a_SUITE.erl @@ -29,30 +29,15 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, long_timers/1, pollset_size/1]). +-export([all/0, suite/0, + long_timers/1, pollset_size/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [long_timers, pollset_size]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - long_timers(doc) -> []; long_timers(suite) -> @@ -106,5 +91,3 @@ display_check_io(ChkIo) -> get_check_io_info() -> z_SUITE:get_check_io_info(). - - diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl index 879fb03927..71edaa25c3 100644 --- a/erts/emulator/test/after_SUITE.erl +++ b/erts/emulator/test/after_SUITE.erl @@ -24,50 +24,25 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, t_after/1, receive_after/1, receive_after_big/1, receive_after_errors/1, receive_var_zero/1, receive_zero/1, multi_timeout/1, receive_after_32bit/1, receive_after_blast/1]). --export([init_per_testcase/2, end_per_testcase/2]). - %% Internal exports. -export([timeout_g/0]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 4}}]. all() -> [t_after, receive_after, receive_after_big, receive_after_errors, receive_var_zero, receive_zero, multi_timeout, receive_after_32bit, receive_after_blast]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(3)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - %% Tests for an old round-off error in 'receive after'." t_after(Config) when is_list(Config) -> ?line spawn(fun frequent_process/0), diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 332e2ad67d..75ae687ffc 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -19,8 +19,7 @@ -module(alloc_SUITE). -author('rickard.green@uab.ericsson.se'). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2]). +-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]). -export([basic/1, coalesce/1, @@ -34,42 +33,20 @@ cpool/1, migration/1]). --export([init_per_testcase/2, end_per_testcase/2]). - -include_lib("common_test/include/ct.hrl"). --define(DEFAULT_TIMETRAP_SECS, 240). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 4}}]. all() -> [basic, coalesce, threads, realloc_copy, bucket_index, bucket_mask, rbtree, mseg_clear_cache, erts_mmap, cpool, migration]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - - init_per_testcase(Case, Config) when is_list(Config) -> - Dog = ?t:timetrap(?t:seconds(?DEFAULT_TIMETRAP_SECS)), - [{watchdog, Dog}, {testcase, Case}, {debug,false} | Config]. + [{testcase, Case},{debug,false}|Config]. end_per_testcase(_Case, Config) when is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/erts/emulator/test/async_ports_SUITE.erl b/erts/emulator/test/async_ports_SUITE.erl index c89b3655ff..9647d87c32 100644 --- a/erts/emulator/test/async_ports_SUITE.erl +++ b/erts/emulator/test/async_ports_SUITE.erl @@ -1,8 +1,10 @@ -module(async_ports_SUITE). --include_lib("common_test/include/ct.hrl"). +-export([all/0, suite/0]). +-export([permanent_busy_test/1]). +-export([run_loop/5]). --compile(export_all). +-include_lib("common_test/include/ct.hrl"). -define(PACKET_SIZE, (10 * 1024 * 8)). -define(CPORT_DELAY, 100). @@ -11,17 +13,15 @@ -define(TEST_PROCS_COUNT, 2). -define(TC_TIMETRAP_SECONDS, 10). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, ?TC_TIMETRAP_SECONDS}}]. all() -> - [ - permanent_busy_test - ]. + [permanent_busy_test]. permanent_busy_test(Config) -> - ct:timetrap({seconds, ?TC_TIMETRAP_SECONDS}), ExePath = filename:join(?config(data_dir, Config), "cport"), - Self = self(), spawn_link( fun() -> @@ -29,17 +29,16 @@ permanent_busy_test(Config) -> Port = open_port(ExePath), - Testers = - lists:map( - fun(_) -> - erlang:spawn_link(?MODULE, run_loop, - [Self, - Port, - Block, - ?TEST_LOOPS_COUNT, - 0]) - end, - lists:seq(1, ?TEST_PROCS_COUNT)), + Testers = lists:map( + fun(_) -> + spawn_link(?MODULE, run_loop, + [Self, + Port, + Block, + ?TEST_LOOPS_COUNT, + 0]) + end, + lists:seq(1, ?TEST_PROCS_COUNT)), Self ! {test_info, Port, Testers}, endless_flush(Port) end), diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 9bba5387fa..ccde904fbc 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -23,9 +23,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, display/1, display_huge/0, erl_bif_types/1,guard_bifs_in_erl_bif_types/1, shadow_comments/1, @@ -35,7 +33,9 @@ atom_to_binary/1,min_max/1, erlang_halt/1, is_builtin/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. all() -> [erl_bif_types, guard_bifs_in_erl_bif_types, shadow_comments, @@ -45,31 +45,6 @@ all() -> atom_to_binary, binary_to_atom, binary_to_existing_atom, min_max, erlang_halt, is_builtin]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(1)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - - display(suite) -> []; display(doc) -> diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl index bb77235253..9b611a136c 100644 --- a/erts/emulator/test/big_SUITE.erl +++ b/erts/emulator/test/big_SUITE.erl @@ -20,8 +20,8 @@ -module(big_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2]). +-export([all/0, suite/0, groups/0]). + -export([t_div/1, eq_28/1, eq_32/1, eq_big/1, eq_math/1, big_literals/1, borders/1, negative/1, big_float_1/1, big_float_2/1, shift_limit_1/1, powmod/1, system_limit/1, toobig/1, otp_6692/1]). @@ -32,11 +32,12 @@ -export([fac/1, fib/1, pow/2, gcd/2, lcm/2]). --export([init_per_testcase/2, end_per_testcase/2]). -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 3}}]. all() -> [t_div, eq_28, eq_32, eq_big, eq_math, big_literals, @@ -46,27 +47,6 @@ all() -> groups() -> [{big_float, [], [big_float_1, big_float_2]}]. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(3)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - %% %% Syntax of data files: %% Expr1 = Expr2. diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 1fa7353252..2d93bace15 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -22,8 +22,7 @@ -module(bs_construct_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, test1/1, test2/1, test3/1, test4/1, test5/1, testf/1, not_used/1, in_guard/1, mem_leak/1, coerce_to_float/1, bjorn/1, @@ -33,7 +32,9 @@ -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 10}}]. all() -> [test1, test2, test3, test4, test5, testf, not_used, @@ -42,21 +43,6 @@ all() -> copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width, bad_append, bs_add_overflow]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - big(1) -> 57285702734876389752897683. diff --git a/erts/emulator/test/bs_match_misc_SUITE.erl b/erts/emulator/test/bs_match_misc_SUITE.erl index b161d9544e..967325842c 100644 --- a/erts/emulator/test/bs_match_misc_SUITE.erl +++ b/erts/emulator/test/bs_match_misc_SUITE.erl @@ -19,8 +19,7 @@ %% -module(bs_match_misc_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, bound_var/1,bound_tail/1,t_float/1,little_float/1,sean/1, kenneth/1,encode_binary/1,native/1,happi/1, size_var/1,wiger/1,x0_context/1,huge_float_field/1, @@ -29,7 +28,9 @@ -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 10}}]. all() -> [bound_var, bound_tail, t_float, little_float, sean, @@ -37,21 +38,6 @@ all() -> x0_context, huge_float_field, writable_binary_matched, otp_7198, unordered_bindings, float_middle_endian]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - bound_var(doc) -> "Test matching of bound variables."; bound_var(Config) when is_list(Config) -> diff --git a/erts/emulator/test/bs_utf_SUITE.erl b/erts/emulator/test/bs_utf_SUITE.erl index 91e4e30dd2..f03115c3c8 100644 --- a/erts/emulator/test/bs_utf_SUITE.erl +++ b/erts/emulator/test/bs_utf_SUITE.erl @@ -20,9 +20,7 @@ -module(bs_utf_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, utf8_roundtrip/1,utf16_roundtrip/1,utf32_roundtrip/1, utf8_illegal_sequences/1,utf16_illegal_sequences/1, utf32_illegal_sequences/1, @@ -32,37 +30,15 @@ -define(FAIL(Expr), ?line fail_check(catch Expr, ??Expr, [])). -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog = ?t:timetrap(?t:minutes(6)), - [{watchdog,Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 6}}]. all() -> [utf8_roundtrip, utf16_roundtrip, utf32_roundtrip, utf8_illegal_sequences, utf16_illegal_sequences, utf32_illegal_sequences, bad_construction]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - utf8_roundtrip(Config) when is_list(Config) -> ?line utf8_roundtrip(0, 16#D7FF), ?line utf8_roundtrip(16#E000, 16#10FFFF), diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index 0e81141907..9bd4f7e136 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -20,8 +20,7 @@ -module(busy_port_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2,end_per_testcase/2, +-export([all/0, suite/0, end_per_testcase/2, io_to_busy/1, message_order/1, send_3/1, system_monitor/1, no_trap_exit/1, no_trap_exit_unlinked/1, trap_exit/1, multiple_writers/1, @@ -34,7 +33,9 @@ %% Internal exports. -export([init/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 4}}]. all() -> [io_to_busy, message_order, send_3, system_monitor, @@ -43,21 +44,6 @@ all() -> scheduling_delay_busy,scheduling_delay_busy_nosuspend, scheduling_busy_link]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - end_per_testcase(_Case, Config) when is_list(Config) -> case whereis(busy_drv_server) of undefined -> @@ -78,15 +64,13 @@ end_per_testcase(_Case, Config) when is_list(Config) -> io_to_busy(suite) -> []; io_to_busy(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(30)), + ct:timetrap({seconds, 30}), ?line start_busy_driver(Config), ?line process_flag(trap_exit, true), ?line Writer = fun_spawn(fun writer/0), ?line Generator = fun_spawn(fun() -> generator(100, Writer) end), ?line wait_for([Writer, Generator]), - - ?line test_server:timetrap_cancel(Dog), ok. generator(N, Writer) -> @@ -132,7 +116,7 @@ forget(_) -> message_order(suite) -> {req, dynamic_loading}; message_order(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), + ct:timetrap({seconds, 10}), ?line start_busy_driver(Config), ?line Self = self(), @@ -149,8 +133,6 @@ message_order(Config) when is_list(Config) -> Other -> test_server:fail({unexpected_message, Other}) end, - - ?line test_server:timetrap_cancel(Dog), ok. send_to_busy_1(Parent) -> @@ -167,7 +149,7 @@ send_to_busy_1(Parent) -> send_3(suite) -> {req,dynamic_loading}; send_3(doc) -> ["Test the BIF send/3"]; send_3(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), + ct:timetrap({seconds, 10}), %% ?line start_busy_driver(Config), ?line {Owner,Slave} = get_slave(), @@ -180,15 +162,13 @@ send_3(Config) when is_list(Config) -> ?line ok = erlang:send(Slave, {Owner,{command,"not busy"}}, [nosuspend]), ?line ok = command(stop), - %% - ?line test_server:timetrap_cancel(Dog), ok. %% Test the erlang:system_monitor(Pid, [busy_port]) system_monitor(suite) -> {req,dynamic_loading}; system_monitor(doc) -> ["Test erlang:system_monitor({Pid,[busy_port]})."]; system_monitor(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), + ct:timetrap({seconds, 10}), ?line Self = self(), %% ?line OldMonitor = erlang:system_monitor(Self, [busy_port]), @@ -226,18 +206,12 @@ system_monitor(Config) when is_list(Config) -> ?line _NewMonitor = erlang:system_monitor(OldMonitor), ?line OldMonitor = erlang:system_monitor(), ?line OldMonitor = erlang:system_monitor(OldMonitor), - %% - ?line test_server:timetrap_cancel(Dog), ok. - - rec(Tag) -> receive X -> X after 1000 -> Tag end. - - %% Assuming the following scenario, %% %% +---------------+ +-----------+ @@ -250,7 +224,7 @@ rec(Tag) -> no_trap_exit(suite) -> []; no_trap_exit(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), + ct:timetrap({seconds, 10}), ?line process_flag(trap_exit, true), ?line Pid = fun_spawn(fun no_trap_exit_process/3, [self(), linked, Config]), @@ -267,8 +241,6 @@ no_trap_exit(Config) when is_list(Config) -> Other2 -> test_server:fail({unexpected_message, Other2}) end, - - ?line test_server:timetrap_cancel(Dog), ok. %% The same scenario as above, but the port has been explicitly @@ -276,7 +248,7 @@ no_trap_exit(Config) when is_list(Config) -> no_trap_exit_unlinked(suite) -> []; no_trap_exit_unlinked(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), + ct:timetrap({seconds, 10}), ?line process_flag(trap_exit, true), ?line Pid = fun_spawn(fun no_trap_exit_process/3, [self(), unlink, Config]), @@ -293,7 +265,6 @@ no_trap_exit_unlinked(Config) when is_list(Config) -> Other2 -> test_server:fail({unexpected_message, Other2}) end, - ?line test_server:timetrap_cancel(Dog), ok. no_trap_exit_process(ResultTo, Link, Config) -> @@ -322,7 +293,7 @@ no_trap_exit_process(ResultTo, Link, Config) -> trap_exit(suite) -> []; trap_exit(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), + ct:timetrap({seconds, 10}), ?line Pid = fun_spawn(fun busy_port_exit_process/2, [self(), Config]), ?line receive {Pid, port_created, Port} -> @@ -339,7 +310,6 @@ trap_exit(Config) when is_list(Config) -> Other2 -> test_server:fail({unexpected_message, Other2}) end, - ?line test_server:timetrap_cancel(Dog), ok. busy_port_exit_process(ResultTo, Config) -> @@ -364,7 +334,7 @@ busy_port_exit_process(ResultTo, Config) -> multiple_writers(suite) -> []; multiple_writers(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), + ct:timetrap({seconds, 10}), ?line start_busy_driver(Config), ?line process_flag(trap_exit, true), @@ -385,8 +355,6 @@ multiple_writers(Config) when is_list(Config) -> %% Unlock the port. The surviving processes should be become runnable. ?line unlock_slave(), ?line wait_for([W2, W4, W5]), - - ?line test_server:timetrap_cancel(Dog), ok. quick_writer() -> diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index b9f8fe52f3..1b67bcd83e 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -21,8 +21,7 @@ -module(call_trace_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, init_per_testcase/2,end_per_testcase/2, hipe/1,process_specs/1,basic/1,flags/1,errors/1,pam/1,change_pam/1, return_trace/1,exception_trace/1,on_load/1,deep_exception/1, @@ -43,7 +42,9 @@ -define(P, 20). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 30}}]. all() -> Common = [errors, on_load], @@ -57,30 +58,10 @@ all() -> false -> NotHipe ++ Common end. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog = ?t:timetrap(?t:seconds(30)), - [{watchdog, Dog}|Config]. + Config. end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - %% Reloading the module will clear all trace patterns, and %% in a debug-compiled emulator run assertions of the counters %% for the number of traced exported functions in this module. diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl index 3998d27d04..95cf9ddac4 100644 --- a/erts/emulator/test/code_parallel_load_SUITE.erl +++ b/erts/emulator/test/code_parallel_load_SUITE.erl @@ -19,46 +19,31 @@ %% -module(code_parallel_load_SUITE). --export([ - all/0, - suite/0, - init_per_suite/1, - end_per_suite/1, - init_per_testcase/2, - end_per_testcase/2 - ]). - --export([ - multiple_load_check_purge_repeat/1, - many_load_distributed_only_once/1 - ]). +-export([all/0, + suite/0, + init_per_testcase/2, + end_per_testcase/2]). + +-export([multiple_load_check_purge_repeat/1, + many_load_distributed_only_once/1]). -define(model, code_parallel_load_SUITE_model). -define(interval, 50). -define(number_of_processes, 160). -define(passes, 4). - -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 4}}]. all() -> - [ - multiple_load_check_purge_repeat, - many_load_distributed_only_once - ]. - - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. + [ multiple_load_check_purge_repeat, + many_load_distributed_only_once ]. init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(3)), - [{watchdog, Dog}|Config]. + Config. end_per_testcase(_Func, Config) -> SConf = ?config(save_config, Config), @@ -72,9 +57,7 @@ end_per_testcase(_Func, Config) -> true -> check_and_purge_processes_code(Pids, ?model); _ -> ok end, - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - + ok. multiple_load_check_purge_repeat(_Conf) -> Ts = [v1,v2,v3,v4,v5,v6], diff --git a/erts/emulator/test/crypto_SUITE.erl b/erts/emulator/test/crypto_SUITE.erl index 41fe6a226c..e9498028cb 100644 --- a/erts/emulator/test/crypto_SUITE.erl +++ b/erts/emulator/test/crypto_SUITE.erl @@ -22,40 +22,23 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, t_md5/1,t_md5_update/1,error/1,unaligned_context/1,random_lists/1, misc_errors/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [t_md5, t_md5_update, error, unaligned_context, random_lists, misc_errors]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - - misc_errors(doc) -> ["Test crc32, adler32 and md5 error cases not covered by other tests"]; misc_errors(suite) -> []; misc_errors(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(2)), + ct:timetrap({minutes, 2}), ?line 1 = erlang:adler32([]), ?line L = lists:duplicate(600,3), ?line 1135871753 = erlang:adler32(L), @@ -78,7 +61,6 @@ misc_errors(Config) when is_list(Config) -> ?line {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(3,3,Big)), ?line {'EXIT', {badarg,_}} = (catch erlang:md5_update(<<"hej">>,<<"hej">>)), ?line {'EXIT', {badarg,_}} = (catch erlang:md5_final(<<"hej">>)), - ?line test_server:timetrap_cancel(Dog), ok. @@ -132,7 +114,7 @@ random_lists(doc) -> random_lists(suite) -> []; random_lists(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(5)), + ct:timetrap({minutes, 5}), ?line Num = erlang:system_info(schedulers_online), ?line B = list_to_binary( lists:duplicate( @@ -232,7 +214,6 @@ random_lists(Config) when is_list(Config) -> fun() -> random_iolist:run2(150,ADLER32_1_L_2,ADLER32_3_L_2) end}, {?LINE, fun() -> random_iolist:run2(150,MD5_1_L_2,MD5_2_L_2) end}], ?line run_in_para(Wlist1,Num), - ?line test_server:timetrap_cancel(Dog), ok. %% diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl index 7ff727bcf5..36c779c8e3 100644 --- a/erts/emulator/test/ddll_SUITE.erl +++ b/erts/emulator/test/ddll_SUITE.erl @@ -31,9 +31,8 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, ddll_test/1, errors/1, - reference_count/1, +-export([all/0, suite/0, + ddll_test/1, errors/1, reference_count/1, kill_port/1, dont_kill_port/1]). -export([unload_on_process_exit/1, delayed_unload_with_ports/1, unload_due_to_process_exit/1, @@ -54,7 +53,9 @@ -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 10}}]. all() -> [ddll_test, errors, reference_count, kill_port, @@ -70,28 +71,11 @@ all() -> no_trap_exit_and_kill_ports, monitor_demonitor, monitor_demonitor_load, new_interface, lock_driver]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - unload_on_process_exit(suite) -> []; unload_on_process_exit(doc) -> ["Check that the driver is unloaded on process exit"]; unload_on_process_exit(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), Parent = self(), @@ -117,7 +101,6 @@ unload_on_process_exit(Config) when is_list(Config) -> end, receive after 500 -> ok end, ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), - ?line test_server:timetrap_cancel(Dog), ok. delayed_unload_with_ports(suite) -> @@ -125,7 +108,6 @@ delayed_unload_with_ports(suite) -> delayed_unload_with_ports(doc) -> ["Check that the driver is unloaded when the last port is closed"]; delayed_unload_with_ports(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line erl_ddll:try_load(Path, echo_drv, []), ?line erl_ddll:try_load(Path, echo_drv, []), @@ -142,7 +124,6 @@ delayed_unload_with_ports(Config) when is_list(Config) -> ?line Port2 ! {self(), close}, ?line ok = receive {Port2,closed} -> ok after 1000 -> false end, ?line ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 1000 -> false end, - ?line test_server:timetrap_cancel(Dog), ok. unload_due_to_process_exit(suite) -> @@ -150,7 +131,6 @@ unload_due_to_process_exit(suite) -> unload_due_to_process_exit(doc) -> ["Check that the driver with ports is unloaded on process exit"]; unload_due_to_process_exit(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line Parent = self(), ?line F3 = fun() -> @@ -176,7 +156,6 @@ unload_due_to_process_exit(Config) when is_list(Config) -> Pid ! go, ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, - ?line test_server:timetrap_cancel(Dog), ok. no_unload_due_to_process_exit(suite) -> @@ -184,7 +163,6 @@ no_unload_due_to_process_exit(suite) -> no_unload_due_to_process_exit(doc) -> ["Check that a driver with driver loaded in another process is not unloaded on process exit"]; no_unload_due_to_process_exit(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line Parent = self(), ?line F3 = fun() -> @@ -213,7 +191,6 @@ no_unload_due_to_process_exit(Config) when is_list(Config) -> ?line ok = receive X -> {error, X} after 300 -> ok end, ?line ok = unload_expect_fast(echo_drv,[]), ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, - ?line test_server:timetrap_cancel(Dog), ok. no_unload_due_to_process_exit_2(suite) -> @@ -221,7 +198,6 @@ no_unload_due_to_process_exit_2(suite) -> no_unload_due_to_process_exit_2(doc) -> ["Check that a driver with open ports in another process is not unloaded on process exit"]; no_unload_due_to_process_exit_2(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line Parent = self(), ?line F3 = fun() -> @@ -250,7 +226,6 @@ no_unload_due_to_process_exit_2(Config) when is_list(Config) -> ?line ok = receive X -> {error, X} after 300 -> ok end, ?line erlang:port_close(Port), ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, - ?line test_server:timetrap_cancel(Dog), ok. unload_reload_thingie(suite) -> @@ -258,7 +233,6 @@ unload_reload_thingie(suite) -> unload_reload_thingie(doc) -> ["Check delayed unload and reload"]; unload_reload_thingie(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line Parent = self(), ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), @@ -295,7 +269,6 @@ unload_reload_thingie(Config) when is_list(Config) -> after 300 -> error end, ?line ok = receive X -> {error, X} after 300 -> ok end, - ?line test_server:timetrap_cancel(Dog), ok. unload_reload_thingie_2(suite) -> @@ -303,7 +276,6 @@ unload_reload_thingie_2(suite) -> unload_reload_thingie_2(doc) -> ["Check delayed unload and reload"]; unload_reload_thingie_2(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line Parent = self(), ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), @@ -339,7 +311,6 @@ unload_reload_thingie_2(Config) when is_list(Config) -> end, ?line ok = unload_expect_fast(echo_drv,[{monitor,pending}]), ?line ok = receive X -> {error, X} after 300 -> ok end, - ?line test_server:timetrap_cancel(Dog), ok. unload_reload_thingie_3(suite) -> @@ -347,7 +318,6 @@ unload_reload_thingie_3(suite) -> unload_reload_thingie_3(doc) -> ["Check delayed unload and reload failure"]; unload_reload_thingie_3(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line Parent = self(), ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), @@ -385,13 +355,11 @@ unload_reload_thingie_3(Config) when is_list(Config) -> ?line {'EXIT',_} = (catch erl_ddll:info(echo_drv, port_count)), ?line {error, not_loaded} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]), ?line ok = receive X -> {error, X} after 300 -> ok end, - ?line test_server:timetrap_cancel(Dog), ok. reload_pending(suite) -> []; reload_pending(doc) -> ["Reload a driver that is pending on a user"]; reload_pending(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line Parent = self(), ?line F3 = fun() -> @@ -437,13 +405,11 @@ reload_pending(Config) when is_list(Config) -> ?line ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end, [{Parent,1}] = erl_ddll:info(echo_drv,processes), ?line ok = receive Z -> {error, Z} after 300 -> ok end, - ?line test_server:timetrap_cancel(Dog), ok. load_fail_init(suite) -> []; load_fail_init(doc) -> ["Tests failure in the init in driver struct."]; load_fail_init(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line PathFailing = ?config(priv_dir, Config), ?line [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path), @@ -464,14 +430,12 @@ load_fail_init(Config) when is_list(Config) -> after 300 -> ok end, - ?line test_server:timetrap_cancel(Dog), ok. reload_pending_fail_init(suite) -> []; reload_pending_fail_init(doc) -> ["Reload a driver that is pending but init fails"]; reload_pending_fail_init(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line PathFailing = ?config(priv_dir, Config), ?line [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path), @@ -525,14 +489,12 @@ reload_pending_fail_init(Config) when is_list(Config) -> ?line {'EXIT',{badarg,_}} = (catch erl_ddll:info(echo_drv,processes)), ?line ok = receive Z -> {error, Z} after 300 -> ok end, - ?line test_server:timetrap_cancel(Dog), ok. reload_pending_kill(suite) -> []; reload_pending_kill(doc) -> ["Reload a driver with kill_ports option " "that is pending on a user"]; reload_pending_kill(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line OldFlag = process_flag(trap_exit,true), ?line Path = ?config(data_dir, Config), ?line Parent = self(), @@ -619,7 +581,6 @@ reload_pending_kill(Config) when is_list(Config) -> io:format("Port = ~w, Port2 = ~w, Port3 = ~w~n",[Port,Port2,Port3]), ?line ok = receive Z -> {error, Z} after 300 -> ok end, ?line process_flag(trap_exit,OldFlag), - ?line test_server:timetrap_cancel(Dog), ok. @@ -638,7 +599,6 @@ forced_port_killing(suite) -> forced_port_killing(doc) -> ["Check kill_ports option to try_unload "]; forced_port_killing(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line OldFlag=process_flag(trap_exit,true), ?line Parent = self(), @@ -666,7 +626,6 @@ forced_port_killing(Config) when is_list(Config) -> ?line ok = receive {'DOWN',Ref1, driver, echo_drv, unloaded} -> ok after 300 -> false end, ?line process_flag(trap_exit,OldFlag), ?line ok = receive X -> {error, X} after 300 -> ok end, - ?line test_server:timetrap_cancel(Dog), ok. no_trap_exit_and_kill_ports(suite) -> @@ -674,7 +633,6 @@ no_trap_exit_and_kill_ports(suite) -> no_trap_exit_and_kill_ports(doc) -> ["Check delayed unload and reload with no trap_exit"]; no_trap_exit_and_kill_ports(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line Parent = self(), ?line OldFlag=process_flag(trap_exit,true), @@ -707,7 +665,6 @@ no_trap_exit_and_kill_ports(Config) when is_list(Config) -> ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, ?line ok = receive {'EXIT',MyPort,driver_unloaded} -> ok after 300 -> error end, ?line process_flag(trap_exit,OldFlag), - ?line test_server:timetrap_cancel(Dog), ok. monitor_demonitor(suite) -> @@ -715,7 +672,6 @@ monitor_demonitor(suite) -> monitor_demonitor(doc) -> ["Check monitor and demonitor of drivers"]; monitor_demonitor(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line erl_ddll:try_load(Path, echo_drv, []), ?line Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}), @@ -725,7 +681,6 @@ monitor_demonitor(Config) when is_list(Config) -> ?line [] = erl_ddll:info(echo_drv,awaiting_unload), ?line erl_ddll:try_unload(echo_drv,[]), ?line ok = receive _ -> error after 300 -> ok end, - ?line test_server:timetrap_cancel(Dog), ok. monitor_demonitor_load(suite) -> @@ -733,7 +688,6 @@ monitor_demonitor_load(suite) -> monitor_demonitor_load(doc) -> ["Check monitor/demonitor of driver loading"]; monitor_demonitor_load(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line {ok,loaded} = erl_ddll:try_load(Path, echo_drv, []), ?line Port = open_port({spawn, echo_drv}, [eof]), @@ -756,7 +710,6 @@ monitor_demonitor_load(Config) when is_list(Config) -> ?line ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> error end, ?line ok = receive _ -> error after 300 -> ok end, ?line ok = unload_expect_fast(echo_drv,[]), - ?line test_server:timetrap_cancel(Dog), ok. new_interface(suite) -> @@ -764,7 +717,6 @@ new_interface(suite) -> new_interface(doc) -> ["Test the new load/unload/reload interface"]; new_interface(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), % Typical scenario ?line ok = erl_ddll:load(Path, echo_drv), @@ -819,14 +771,12 @@ new_interface(Config) when is_list(Config) -> ?line ok = receive X3 -> {error, X3} after 300 -> ok end, ?line ok = erl_ddll:unload(echo_drv), ?line ok = receive {'DOWN', Ref3, driver, echo_drv, unloaded} -> ok after 300 -> error end, - ?line test_server:timetrap_cancel(Dog), ok. ddll_test(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), %?line {error,{already_started,ErlDdllPid}} = erl_ddll:start(), @@ -856,14 +806,11 @@ ddll_test(Config) when is_list(Config) -> ok = unload_echo_driver(L1,L2), %% %?line {error, {already_started, _}} = erl_ddll:start(), - - ?line test_server:timetrap_cancel(Dog), ok. %% Tests errors having to do with bad drivers. errors(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line {ok, L1} = erl_ddll:loaded_drivers(), @@ -885,8 +832,6 @@ errors(Config) when is_list(Config) -> end, ?line {ok, L1} = erl_ddll:loaded_drivers(), - - ?line test_server:timetrap_cancel(Dog), ok. reference_count(doc) -> @@ -894,7 +839,6 @@ reference_count(doc) -> "reaches zero, and that they cannot be unloaded while ", "they are still referenced."]; reference_count(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), %% Spawn a process that loads the driver (and holds a reference @@ -910,8 +854,6 @@ reference_count(Config) when is_list(Config) -> % Verify that the driver was automaticly unloaded when the % process died. ?line {error, not_loaded}=erl_ddll:unload_driver(echo_drv), - - ?line test_server:timetrap_cancel(Dog), ok. % Loads the echo driver, send msg to started, sits and waits to @@ -939,7 +881,6 @@ kill_port(doc) -> ["Test that a port that uses a driver is killed when the ", "process that loaded the driver dies."]; kill_port(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), %% Spawn a process that loads the driver (and holds a reference @@ -969,16 +910,12 @@ kill_port(Config) when is_list(Config) -> after 5000 -> ?line test_server:fail("Echo port did not terminate.") end, - - %% Cleanup and exit. - ?line test_server:timetrap_cancel(Dog), ok. dont_kill_port(doc) -> ["Test that a port that uses a driver is not killed when the ", "process that loaded the driver dies and it's nicely opened."]; dont_kill_port(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), %% Spawn a process that loads the driver (and holds a reference @@ -1012,15 +949,11 @@ dont_kill_port(Config) when is_list(Config) -> after 5000 -> ?line test_server:fail("Echo port did not terminate.") end, - - %% Cleanup and exit. - ?line test_server:timetrap_cancel(Dog), ok. properties(doc) -> ["Test that a process that loaded a driver ", "is the only process that can unload it."]; properties(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), % Let another process load the echo driver. @@ -1044,12 +977,10 @@ properties(Config) when is_list(Config) -> % Unload the driver and terminate dummy process. ?line Pid ! {self(), die}, ?line test_server:sleep(200), % Give time to unload. - ?line test_server:timetrap_cancel(Dog), ok. load_and_unload(doc) -> ["Load two drivers and unload them in load order."]; load_and_unload(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), ?line Path = ?config(data_dir, Config), ?line {ok, Loaded_drivers1} = erl_ddll:loaded_drivers(), ?line ok = erl_ddll:load_driver(Path, echo_drv), @@ -1061,8 +992,6 @@ load_and_unload(Config) when is_list(Config) -> ?line Set2 = ordsets:from_list(Loaded_drivers2), ?line io:format("~p == ~p\n", [Loaded_drivers1, Loaded_drivers2]), ?line [] = ordsets:to_list(ordsets:subtract(Set2, Set1)), - - ?line test_server:timetrap_cancel(Dog), ok. lock_driver(suite) -> @@ -1070,7 +999,6 @@ lock_driver(suite) -> lock_driver(doc) -> ["Check multiple calls to driver_lock_driver"]; lock_driver(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line {ok, _} = erl_ddll:try_load(Path, lock_drv, []), ?line Port1 = open_port({spawn, lock_drv}, [eof]), @@ -1078,7 +1006,6 @@ lock_driver(Config) when is_list(Config) -> ?line true = erl_ddll:info(lock_drv,permanent), ?line erlang:port_close(Port1), ?line erlang:port_close(Port2), - ?line test_server:timetrap_cancel(Dog), ok. diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl index 58a8d390f0..213234af52 100644 --- a/erts/emulator/test/decode_packet_SUITE.erl +++ b/erts/emulator/test/decode_packet_SUITE.erl @@ -24,13 +24,14 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0,groups/0, init_per_testcase/2,end_per_testcase/2, basic/1, packet_size/1, neg/1, http/1, line/1, ssl/1, otp_8536/1, otp_9389/1, otp_9389_line/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. all() -> [basic, packet_size, neg, http, line, ssl, otp_8536, @@ -39,28 +40,13 @@ all() -> groups() -> []. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> rand:seed(exsplus), io:format("*** SEED: ~p ***\n", [rand:export_seed()]), - Dog=?t:timetrap(?t:minutes(1)), - [{watchdog, Dog}|Config]. + Config. -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). +end_per_testcase(_Func, _Config) -> + ok. basic(doc) -> []; basic(suite) -> []; diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index d562f79bb7..6165cf70bc 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -35,8 +35,7 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, groups/0, ping/1, bulk_send_small/1, bulk_send_big/1, bulk_send_bigbig/1, local_send_small/1, local_send_big/1, @@ -58,8 +57,6 @@ bad_dist_ext_control/1, bad_dist_ext_connection_id/1]). --export([init_per_testcase/2, end_per_testcase/2]). - %% Internal exports. -export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0, roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1, @@ -67,7 +64,9 @@ dist_evil_parallel_receiver/0, sendersender/4, sendersender2/4]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 4}}]. all() -> [ping, {group, bulk_send}, {group, local_send}, @@ -90,28 +89,6 @@ groups() -> [bad_dist_ext_receive, bad_dist_ext_process_info, bad_dist_ext_control, bad_dist_ext_connection_id]}]. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - --define(DEFAULT_TIMETRAP, 4*60*1000). - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?DEFAULT_TIMETRAP), - [{watchdog, Dog},{testcase, Func}|Config]. - -end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - ping(doc) -> ["Tests pinging a node in different ways."]; ping(Config) when is_list(Config) -> @@ -151,7 +128,7 @@ bulk_send_bigbig(Config) when is_list(Config) -> ?line bulk_sendsend(32*5, 4). bulk_send(Terms, BinSize) -> - ?line Dog = test_server:timetrap(test_server:seconds(30)), + ct:timetrap({seconds, 30}), ?line io:format("Sending ~w binaries, each of size ~w K", [Terms, BinSize]), @@ -162,8 +139,6 @@ bulk_send(Terms, BinSize) -> ?line {Elapsed, {Terms, Size}} = test_server:timecall(?MODULE, sender, [Recv, Bin, Terms]), ?line stop_node(Node), - - ?line test_server:timetrap_cancel(Dog), {comment, integer_to_list(trunc(Size/1024/max(1,Elapsed)+0.5)) ++ " K/s"}. bulk_sendsend(Terms, BinSize) -> @@ -188,7 +163,7 @@ bulk_sendsend(Terms, BinSize) -> end. bulk_sendsend2(Terms, BinSize, BusyBufSize) -> - ?line Dog = test_server:timetrap(test_server:seconds(30)), + ct:timetrap({seconds, 30}), ?line io:format("Sending ~w binaries, each of size ~w K", [Terms, BinSize]), @@ -214,8 +189,6 @@ bulk_sendsend2(Terms, BinSize, BusyBufSize) -> end, ?line stop_node(NodeRecv), ?line stop_node(NodeSend), - - ?line test_server:timetrap_cancel(Dog), {trunc(SizeN/1024/Elapsed+0.5), MonitorCount}. sender(To, _Bin, 0) -> @@ -335,7 +308,7 @@ receiver2(Num, TotSize) -> link_to_busy(doc) -> "Test that link/1 to a busy distribution port works."; link_to_busy(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), + ct:timetrap({seconds, 60}), ?line {ok, Node} = start_node(link_to_busy), ?line Recv = spawn(Node, erlang, apply, [fun sink/1, [link_to_busy_sink]]), @@ -364,7 +337,6 @@ link_to_busy(Config) when is_list(Config) -> %% Done. ?line stop_node(Node), ?line stop_busy_dist_port_tracer(Tracer), - ?line test_server:timetrap_cancel(Dog), ok. linker(Pid) -> @@ -382,7 +354,7 @@ tail_applied_linker(Pid) -> exit_to_busy(doc) -> "Test that exit/2 to a busy distribution port works."; exit_to_busy(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), + ct:timetrap({seconds, 60}), ?line {ok, Node} = start_node(exit_to_busy), Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of @@ -435,7 +407,6 @@ exit_to_busy(Config) when is_list(Config) -> %% Done. ?line stop_node(Node), ?line stop_busy_dist_port_tracer(Tracer), - ?line test_server:timetrap_cancel(Dog), ok. make_busy_data() -> @@ -1002,8 +973,7 @@ dist_parallel_send(Config) when is_list(Config) -> ?line {ok, SNode} = start_node(dist_parallel_sender), ?line WatchDog = spawn_link( fun () -> - TRef = erlang:start_timer((?DEFAULT_TIMETRAP - div 2), + TRef = erlang:start_timer((2*60*1000), self(), oops), receive @@ -1419,10 +1389,7 @@ bad_dist_structure(doc) -> ["Test dist messages with valid structure (binary to term ok) but malformed" "control content"]; bad_dist_structure(Config) when is_list(Config) -> - %process_flag(trap_exit,true), - ODog = ?config(watchdog, Config), - ?t:timetrap_cancel(ODog), - Dog = ?t:timetrap(?t:seconds(15)), + ct:timetrap({seconds, 15}), ?line {ok, Offender} = start_node(bad_dist_structure_offender), ?line {ok, Victim} = start_node(bad_dist_structure_victim), @@ -1535,7 +1502,6 @@ bad_dist_structure(Config) when is_list(Config) -> ?line P ! done, ?line stop_node(Offender), ?line stop_node(Victim), - ?t:timetrap_cancel(Dog), ok. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 8eb555a5b7..23c5114d40 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -119,22 +119,22 @@ -define(heap_binary_size, 64). init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(2)), case catch erts_debug:get_internal_state(available_internal_state) of true -> ok; _ -> erts_debug:set_internal_state(available_internal_state, true) end, erlang:display({init_per_testcase, Case}), ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)), - [{watchdog, Dog},{testcase, Case}|Config]. + [{testcase, Case}|Config]. end_per_testcase(Case, Config) -> - Dog = ?config(watchdog, Config), erlang:display({end_per_testcase, Case}), ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)), - ?t:timetrap_cancel(Dog). + ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. all() -> %% Keep a_test first and z_test last... [a_test, outputv_errors, outputv_echo, queue_echo, {group, timer}, @@ -264,7 +264,7 @@ build_iolist(N0, Base) -> outputv_echo(doc) -> ["Test echoing data with a driver that supports outputv."]; outputv_echo(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(10)), + ct:timetrap({minutes, 10}), Name = 'outputv_drv', P = start_driver(Config, Name, true), @@ -304,7 +304,6 @@ outputv_echo(Config) when is_list(Config) -> ?line ov_test(P, [int,{bin,17},int,{bin,?heap_binary_size+1}|{bin,3}]), stop_driver(P, Name), - ?line test_server:timetrap_cancel(Dog), ok. ov_test(Port, Template) -> @@ -379,14 +378,12 @@ compare(Got, Expected) -> timer_measure(doc) -> ["Check that timers time out in good time."]; timer_measure(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(1)), Name = 'timer_drv', ?line Port = start_driver(Config, Name, false), ?line try_timeouts(Port, 8997), ?line stop_driver(Port, Name), - ?line test_server:timetrap_cancel(Dog), ok. try_timeouts(_, 0) -> ok; @@ -411,14 +408,12 @@ try_timeouts(Port, Timeout) -> timer_cancel(doc) -> ["Try cancelling timers set in a driver."]; timer_cancel(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(1)), Name = 'timer_drv', ?line Port = start_driver(Config, Name, false), ?line try_cancel(Port, 10000), ?line stop_driver(Port, Name), - ?line test_server:timetrap_cancel(Dog), ok. try_cancel(Port, Timeout) -> @@ -452,7 +447,6 @@ try_cancel(Port, Timeout) -> %% before setting a timer. timer_delay(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(1)), Name = 'timer_drv', ?line Port = start_driver(Config, Name, false), @@ -480,21 +474,18 @@ timer_delay(Config) when is_list(Config) -> end, ?line stop_driver(Port, Name), - ?line test_server:timetrap_cancel(Dog), ok. %% Test that driver_set_timer with new timout really changes %% the timer (ticket OTP-5942), it didn't work before timer_change(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(1)), Name = 'timer_drv', ?line Port = start_driver(Config, Name, false), ?line try_change_timer(Port, 10000), ?line stop_driver(Port, Name), - ?line test_server:timetrap_cancel(Dog), ok. try_change_timer(_Port, 0) -> ok; @@ -534,7 +525,7 @@ queue_echo(Config) when is_list(Config) -> end. queue_echo_1(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(10)), + ct:timetrap({minutes, 10}), Name = 'queue_drv', ?line P = start_driver(Config, Name, true), @@ -566,7 +557,6 @@ queue_echo_1(Config) -> {?PUSHQV, [{bin,0},{list,1},{bin,1},{bin,319}]}]), ?line stop_driver(P, Name), - ?line test_server:timetrap_cancel(Dog), ok. q_echo(Port, SpecList) -> diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl index 26d00db7c0..a56741adc6 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE.erl +++ b/erts/emulator/test/erl_drv_thread_SUITE.erl @@ -20,8 +20,7 @@ -module(erl_drv_thread_SUITE). -author('rickard.s.green@ericsson.com'). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2]). +-export([all/0, suite/0]). -export([basic/1, rwlock/1, tsd/1]). @@ -34,21 +33,6 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [basic, rwlock, tsd]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% @@ -97,7 +81,7 @@ drv_case(Config, CaseName, Command, TimeTrap) when is_list(Config), end. run_drv_case(Config, CaseName, Command, TimeTrap) -> - ?line Dog = test_server:timetrap(test_server:seconds(TimeTrap)), + ct:timetrap({seconds, TimeTrap}), ?line DataDir = ?config(data_dir,Config), case erl_ddll:load_driver(DataDir, CaseName) of ok -> ok; @@ -115,7 +99,6 @@ run_drv_case(Config, CaseName, Command, TimeTrap) -> ok end, ?line ok = erl_ddll:unload_driver(CaseName), - ?line test_server:timetrap_cancel(Dog), ?line Result. receive_drv_result(Port, CaseName) -> diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index 56b2c9c6ee..a3f44e7313 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -31,8 +31,7 @@ %-define(line_trace, 1). -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2]). +-export([all/0, suite/0, init_per_suite/1, end_per_suite/1]). % Test cases -export([links/1, @@ -78,8 +77,9 @@ }). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. all() -> [links, dist_links, monitor_nodes, process_monitors, @@ -87,8 +87,15 @@ all() -> busy_dist_port_link, otp_5772_link, otp_5772_dist_link, otp_5772_monitor, otp_5772_dist_monitor, otp_7946]. -groups() -> - []. +init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> + case catch erts_debug:get_internal_state(available_internal_state) of + true -> ok; + _ -> erts_debug:set_internal_state(available_internal_state, true) + end, + Config. + +end_per_testcase(_Func, _Config) -> + ok. init_per_suite(Config) -> Config. @@ -96,12 +103,6 @@ init_per_suite(Config) -> end_per_suite(_Config) -> catch erts_debug:set_internal_state(available_internal_state, false). -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - links(doc) -> ["Tests node local links"]; links(suite) -> []; @@ -690,18 +691,6 @@ forever(Fun) -> Fun(), forever(Fun). -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - ?line Dog = ?t:timetrap(?t:minutes(1)), - case catch erts_debug:get_internal_state(available_internal_state) of - true -> ok; - _ -> erts_debug:set_internal_state(available_internal_state, true) - end, - ?line [{watchdog, Dog}|Config]. - -end_per_testcase(_Func, Config) -> - ?line Dog = ?config(watchdog, Config), - ?line ?t:timetrap_cancel(Dog). - tp_call(Tp, Fun) -> ?line R = make_ref(), ?line Tp ! {call, self(), R, Fun}, diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl index 440a7950a6..d4814bd8f4 100644 --- a/erts/emulator/test/erts_debug_SUITE.erl +++ b/erts/emulator/test/erts_debug_SUITE.erl @@ -21,41 +21,17 @@ -module(erts_debug_SUITE). -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, test_size/1,flat_size_big/1,df/1,term_type/1, instructions/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 2}}]. all() -> [test_size, flat_size_big, df, instructions, term_type]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(2)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - test_size(Config) when is_list(Config) -> ConsCell1 = id([a|b]), ConsCell2 = id(ConsCell1), diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index 7710424894..2d53cb1cd1 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -19,9 +19,8 @@ -module(estone_SUITE). %% Test functions --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2,estone/1,estone_bench/1]). --export([init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0, groups/0, + estone/1, estone_bench/1]). %% Internal exports for EStone tests -export([lists/1, @@ -49,9 +48,6 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct_event.hrl"). -%% Test suite defines --define(default_timeout, ?t:minutes(10)). - %% EStone defines -define(TOTAL, (3000 * 1000 * 100)). %% 300 secs -define(BIGPROCS, 2). @@ -66,17 +62,9 @@ str}). %% Header string - - -init_per_testcase(_Case, Config) -> - ?line Dog=test_server:timetrap(?default_timeout), - [{watchdog, Dog}|Config]. -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 4}}]. all() -> [estone]. @@ -84,18 +72,6 @@ all() -> groups() -> [{estone_bench, [{repeat,50}],[estone_bench]}]. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - estone(suite) -> []; diff --git a/erts/emulator/test/evil_SUITE.erl b/erts/emulator/test/evil_SUITE.erl index 77ee2128b6..95a5ff3f52 100644 --- a/erts/emulator/test/evil_SUITE.erl +++ b/erts/emulator/test/evil_SUITE.erl @@ -19,9 +19,7 @@ -module(evil_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, heap_frag/1, encode_decode_ext/1, decode_integer_ext/1, @@ -35,7 +33,9 @@ -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 30}}]. all() -> [heap_frag, encode_decode_ext, decode_integer_ext, @@ -43,31 +43,6 @@ all() -> decode_small_big_ext_neg, decode_large_big_ext_neg, decode_too_small, decode_pos_neg_zero]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(_Case, Config) -> - ?line Dog = test_server:timetrap(?t:minutes(0.5)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. - heap_frag(Config) when is_list(Config) -> N = 512, Self = self(), @@ -109,7 +84,6 @@ encode_decode_ext(Config) when is_list(Config) -> ?line enc_dec(11, 16#7fffffffffffffff), % largest i64 ?line enc_dec(11,-16#8000000000000000), % smallest i64 ?line enc_dec(11, 16#ffffffffffffffff), % largest u64 - ok. @@ -388,4 +362,3 @@ rnd_term() -> U0 = rand:uniform(), B = <>, {U0,U0 * 2.5 + 3.14,[U0*2.3,B]}. - diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index 57ce8fb879..c2b2c50cd5 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -20,8 +20,7 @@ -module(exception_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, badmatch/1, pending_errors/1, nil_arith/1, stacktrace/1, nested_stacktrace/1, raise/1, gunilla/1, per/1, exception_with_heap_frag/1, line_numbers/1]). @@ -32,29 +31,15 @@ -include_lib("common_test/include/ct.hrl"). -import(lists, [foreach/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 10}}]. all() -> [badmatch, pending_errors, nil_arith, stacktrace, nested_stacktrace, raise, gunilla, per, exception_with_heap_frag, line_numbers]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -define(try_match(E), catch ?MODULE:bar(), {'EXIT', {{badmatch, nomatch}, _}} = (catch E = id(nomatch))). diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index bf557f2bca..de889baeab 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -22,9 +22,7 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, groups/0, fpe/1,fp_drv/1,fp_drv_thread/1,denormalized/1,match/1, t_mul_add_ops/1, bad_float_unpack/1, write/1, cmp_zero/1, cmp_integer/1, cmp_bignum/1]). @@ -32,16 +30,9 @@ -export([hidden_inf/1]). -export([arith/1]). - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog = ?t:timetrap(?t:minutes(3)), - [{watchdog, Dog},{testcase,Func}|Config]. - -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 3}}]. all() -> [fpe, fp_drv, fp_drv_thread, otp_7178, denormalized, @@ -53,19 +44,6 @@ all() -> groups() -> [{comparison, [parallel], [cmp_zero, cmp_integer, cmp_bignum]}]. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - %% %% OTP-7178, list_to_float on very small numbers should give 0.0 %% instead of exception, i.e. ignore underflow. diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 6697a86fc5..103244d6ca 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -21,11 +21,7 @@ -module(fun_SUITE). -compile({nowarn_deprecated_function, {erlang,hash,2}}). --define(default_timeout, ?t:minutes(1)). - --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, bad_apply/1,bad_fun_call/1,badarity/1,ext_badarity/1, equality/1,ordering/1, fun_to_port/1,t_hash/1,t_phash/1,t_phash2/1,md5/1, @@ -37,7 +33,10 @@ -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. + all() -> [bad_apply, bad_fun_call, badarity, ext_badarity, @@ -46,31 +45,6 @@ all() -> const_propagation, t_arity, t_is_function2, t_fun_info, t_fun_info_mfa]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(_Case, Config) -> - ?line Dog = test_server:timetrap(?default_timeout), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. - bad_apply(doc) -> "Test that the correct EXIT code is returned for all types of bad funs."; bad_apply(suite) -> []; diff --git a/erts/emulator/test/fun_r13_SUITE.erl b/erts/emulator/test/fun_r13_SUITE.erl index 39e8b3c324..32d58355e0 100644 --- a/erts/emulator/test/fun_r13_SUITE.erl +++ b/erts/emulator/test/fun_r13_SUITE.erl @@ -21,43 +21,18 @@ -module(fun_r13_SUITE). -compile(r13). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2,dist_old_release/1]). +-export([all/0, suite/0, + dist_old_release/1]). --define(default_timeout, ?t:minutes(1)). -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. all() -> [dist_old_release]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(_Case, Config) -> - ?line Dog = test_server:timetrap(?default_timeout), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. - dist_old_release(Config) when is_list(Config) -> case ?t:is_release_available("r12b") of true -> do_dist_old(Config); diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl index cb000fd45f..cccd726b1c 100644 --- a/erts/emulator/test/gc_SUITE.erl +++ b/erts/emulator/test/gc_SUITE.erl @@ -23,44 +23,26 @@ -module(gc_SUITE). -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2]). - --define(default_timeout, ?t:minutes(10)). +-export([all/0, suite/0]). -export([grow_heap/1, grow_stack/1, grow_stack_heap/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [grow_heap, grow_stack, grow_stack_heap]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - grow_heap(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) -> - Dog = test_server:timetrap(test_server:minutes(40)), + ct:timetrap({minutes, 40}), ok = grow_heap1(256), ok = grow_heap1(512), ok = grow_heap1(1024), ok = grow_heap1(2048), - test_server:timetrap_cancel(Dog), ok. grow_heap1(Len) -> @@ -89,11 +71,10 @@ 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) -> - Dog = test_server:timetrap(test_server:minutes(80)), + ct:timetrap({minutes, 80}), show_heap("before:"), grow_stack1(200, 0), show_heap("after:"), - test_server:timetrap_cancel(Dog), ok. grow_stack1(0, _) -> @@ -114,10 +95,9 @@ 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) -> - Dog = test_server:timetrap(test_server:minutes(40)), + ct:timetrap({minutes, 40}), grow_stack_heap1(16), grow_stack_heap1(32), - test_server:timetrap_cancel(Dog), ok. grow_stack_heap1(MaxLen) -> diff --git a/erts/emulator/test/guard_SUITE.erl b/erts/emulator/test/guard_SUITE.erl index 2e03983c4f..78c65c5157 100644 --- a/erts/emulator/test/guard_SUITE.erl +++ b/erts/emulator/test/guard_SUITE.erl @@ -20,8 +20,8 @@ -module(guard_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, bad_arith/1, bad_tuple/1, +-export([all/0, suite/0, + bad_arith/1, bad_tuple/1, test_heap_guards/1, guard_bifs/1, type_tests/1,guard_bif_binary_part/1]). @@ -36,21 +36,6 @@ all() -> [bad_arith, bad_tuple, test_heap_guards, guard_bifs, type_tests, guard_bif_binary_part]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - bad_arith(doc) -> "Test that a bad arithmetic operation in a guard works correctly."; bad_arith(Config) when is_list(Config) -> @@ -81,7 +66,7 @@ bad_tuple1(_) -> test_heap_guards(doc) -> ""; test_heap_guards(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(2)), + ct:timetrap({minutes, 2}), ?line process_flag(trap_exit, true), ?line Tuple = {a, tuple, is, built, here, xxx}, @@ -98,7 +83,7 @@ test_heap_guards(Config) when is_list(Config) -> ?line 'try'(fun receive_test/1, [Tuple], [Tuple]), ?line 'try'(fun receive_test/1, [List], [List, List]), ?line 'try'(fun receive_test/1, [a], [a]), - ?line test_server:timetrap_cancel(Dog). + ok. a_case(V) -> case V of diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl index 31b10158fb..4c50131108 100644 --- a/erts/emulator/test/hash_SUITE.erl +++ b/erts/emulator/test/hash_SUITE.erl @@ -70,43 +70,19 @@ config(priv_dir,_) -> ".". -else. %% When run in test server. --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, test_basic/1,test_cmp/1,test_range/1,test_spread/1, test_phash2/1,otp_5292/1,bit_level_binaries/1,otp_7127/1, - test_hash_zero/1, - end_per_testcase/2,init_per_testcase/2]). -init_per_testcase(_Case, Config) -> - Dog=test_server:timetrap(test_server:minutes(10)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. + test_hash_zero/1]). + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 10}}]. all() -> [test_basic, test_cmp, test_range, test_spread, test_phash2, otp_5292, bit_level_binaries, otp_7127, - test_hash_zero - ]. - -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - + test_hash_zero]. test_basic(suite) -> []; diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl index 5138a6ee05..ea4cc79701 100644 --- a/erts/emulator/test/hibernate_SUITE.erl +++ b/erts/emulator/test/hibernate_SUITE.erl @@ -22,45 +22,23 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, basic/1,dynamic_call/1,min_heap_size/1,bad_args/1, - messages_in_queue/1,undefined_mfa/1,no_heap/1,wake_up_and_bif_trap/1]). + messages_in_queue/1,undefined_mfa/1,no_heap/1, + wake_up_and_bif_trap/1]). %% Used by test cases. --export([basic_hibernator/1,dynamic_call_hibernator/2,messages_in_queue_restart/2, no_heap_loop/0,characters_to_list_trap/1]). +-export([basic_hibernator/1,dynamic_call_hibernator/2,messages_in_queue_restart/2, + no_heap_loop/0,characters_to_list_trap/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 3}}]. all() -> [basic, dynamic_call, min_heap_size, bad_args, messages_in_queue, undefined_mfa, no_heap, wake_up_and_bif_trap]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog = ?t:timetrap(?t:minutes(3)), - [{watchdog,Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - %%% %%% Testing the basic functionality of erlang:hibernate/3. %%% diff --git a/erts/emulator/test/list_bif_SUITE.erl b/erts/emulator/test/list_bif_SUITE.erl index 544f9c9c1f..c6221b8c66 100644 --- a/erts/emulator/test/list_bif_SUITE.erl +++ b/erts/emulator/test/list_bif_SUITE.erl @@ -21,44 +21,20 @@ -module(list_bif_SUITE). -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2]). +-export([all/0, suite/0]). -export([hd_test/1,tl_test/1,t_length/1,t_list_to_pid/1, t_list_to_float/1,t_list_to_integer/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. + all() -> [hd_test, tl_test, t_length, t_list_to_pid, t_list_to_float, t_list_to_integer]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(_Case, Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), - [{watchdog,Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. - t_list_to_integer(suite) -> []; t_list_to_integer(doc) -> diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 9e724bdd8c..22425bd66d 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -20,8 +20,7 @@ -module(match_spec_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, not_run/1]). +-export([all/0, suite/0, not_run/1]). -export([test_1/1, test_2/1, test_3/1, bad_match_spec_bin/1, trace_control_word/1, silent/1, silent_no_ms/1, silent_test/1, ms_trace2/1, ms_trace3/1, boxed_and_small/1, @@ -41,18 +40,9 @@ -include_lib("common_test/include/ct.hrl"). --export([init_per_testcase/2, end_per_testcase/2]). - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:seconds(30)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 30}}]. all() -> case test_server:is_native(match_spec_SUITE) of @@ -69,22 +59,6 @@ all() -> true -> [not_run] end. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - not_run(Config) when is_list(Config) -> {skipped, "Native Code"}. diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl index 96c41a57b5..36a8311cd2 100644 --- a/erts/emulator/test/message_queue_data_SUITE.erl +++ b/erts/emulator/test/message_queue_data_SUITE.erl @@ -20,46 +20,20 @@ -module(message_queue_data_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2]). +-export([all/0, suite/0]). -export([basic/1, process_info_messages/1]). -export([basic_test/1]). -include_lib("common_test/include/ct.hrl"). -init_per_testcase(Case, Config) -> - ?line Dog=test_server:timetrap(test_server:minutes(2)), - [{watchdog, Dog}, {testcase, Case}|Config]. - -end_per_testcase(_, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 2}}]. all() -> [basic, process_info_messages]. -groups() -> - []. - -init_per_suite(Config) -> -%% erts_debug:set_internal_state(available_internal_state, true), - Config. - -end_per_suite(_Config) -> -%% erts_debug:set_internal_state(available_internal_state, false), - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - %% %% %% Test cases diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index c622a2d8d7..9f07ff9c0c 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -22,9 +22,7 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, exports/1,functions/1,deleted/1,native/1,info/1]). %%-compile(native). @@ -32,37 +30,16 @@ %% Helper. -export([native_proj/1,native_filter/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 3}}]. all() -> modules(). -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - modules() -> [exports, functions, deleted, native, info]. -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog = ?t:timetrap(?t:minutes(3)), - [{watchdog,Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - %% Should return all functions exported from this module. (local) all_exported() -> All = add_arity(modules()), diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 8101908df1..ce4c40ee02 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -22,19 +22,18 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, groups/0, case_1/1, case_1a/1, case_2/1, case_2a/1, mon_e_1/1, demon_e_1/1, demon_1/1, demon_2/1, demon_3/1, demonitor_flush/1, local_remove_monitor/1, remote_remove_monitor/1, mon_1/1, mon_2/1, large_exit/1, list_cleanup/1, mixer/1, named_down/1, otp_5827/1, monitor_time_offset/1]). --export([init_per_testcase/2, end_per_testcase/2]). - -export([y2/1, g/1, g0/0, g1/0, large_exit_sub/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 15}}]. all() -> [case_1, case_1a, case_2, case_2a, mon_e_1, demon_e_1, @@ -47,27 +46,6 @@ groups() -> [{remove_monitor, [], [local_remove_monitor, remote_remove_monitor]}]. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(15)), - [{watchdog, Dog},{testcase, Func}|Config]. - -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - case_1(doc) -> "A monitors B, B kills A and then exits (yielded core dump)"; case_1(suite) -> []; diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl index 87dace4721..c7c043d8e7 100644 --- a/erts/emulator/test/mtx_SUITE.erl +++ b/erts/emulator/test/mtx_SUITE.erl @@ -29,9 +29,8 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0,suite/0,groups/0, - init_per_group/2,end_per_group/2, init_per_suite/1, - end_per_suite/1, init_per_testcase/2, end_per_testcase/2]). +-export([all/0,suite/0, init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). -export([long_rwlock/1, hammer_ets_rwlock/1, @@ -56,6 +55,28 @@ hammer_sched_freqread_tryrwlock/1, hammer_sched_freqread_tryrwlock_check/1]). +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 15}}]. + +all() -> + [long_rwlock, hammer_rwlock_check, hammer_rwlock, + hammer_tryrwlock_check, hammer_tryrwlock, + hammer_ets_rwlock, hammer_sched_long_rwlock_check, + hammer_sched_long_rwlock, + hammer_sched_long_freqread_rwlock_check, + hammer_sched_long_freqread_rwlock, + hammer_sched_long_tryrwlock_check, + hammer_sched_long_tryrwlock, + hammer_sched_long_freqread_tryrwlock_check, + hammer_sched_long_freqread_tryrwlock, + hammer_sched_rwlock_check, hammer_sched_rwlock, + hammer_sched_freqread_rwlock_check, + hammer_sched_freqread_rwlock, + hammer_sched_tryrwlock_check, hammer_sched_tryrwlock, + hammer_sched_freqread_tryrwlock_check, + hammer_sched_freqread_tryrwlock]. + init_per_suite(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), Lib = filename:join([DataDir, atom_to_list(?MODULE)]), @@ -71,15 +92,13 @@ end_per_suite(Config) when is_list(Config) -> Config. init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?t:minutes(15)), %% Wait for deallocations to complete since we measure %% runtime in test cases. wait_deallocations(), - [{watchdog, Dog}|Config]. + Config. end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog). + ok. wait_deallocations() -> try @@ -90,36 +109,6 @@ wait_deallocations() -> wait_deallocations() end. -suite() -> [{ct_hooks,[ts_install_cth]}]. - -all() -> - [long_rwlock, hammer_rwlock_check, hammer_rwlock, - hammer_tryrwlock_check, hammer_tryrwlock, - hammer_ets_rwlock, hammer_sched_long_rwlock_check, - hammer_sched_long_rwlock, - hammer_sched_long_freqread_rwlock_check, - hammer_sched_long_freqread_rwlock, - hammer_sched_long_tryrwlock_check, - hammer_sched_long_tryrwlock, - hammer_sched_long_freqread_tryrwlock_check, - hammer_sched_long_freqread_tryrwlock, - hammer_sched_rwlock_check, hammer_sched_rwlock, - hammer_sched_freqread_rwlock_check, - hammer_sched_freqread_rwlock, - hammer_sched_tryrwlock_check, hammer_sched_tryrwlock, - hammer_sched_freqread_tryrwlock_check, - hammer_sched_freqread_tryrwlock]. - -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - long_rwlock(Config) when is_list(Config) -> statistics(runtime), LLRes = long_rw_test(), diff --git a/erts/emulator/test/nested_SUITE.erl b/erts/emulator/test/nested_SUITE.erl index ee6bbf6a55..5b59a9f3d7 100644 --- a/erts/emulator/test/nested_SUITE.erl +++ b/erts/emulator/test/nested_SUITE.erl @@ -20,33 +20,19 @@ -module(nested_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, case_in_case/1, case_in_after/1, catch_in_catch/1, bif_in_bif/1]). -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 10}}]. all() -> [case_in_case, case_in_after, catch_in_catch, bif_in_bif]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - case_in_case(suite) -> []; case_in_case(Config) when is_list(Config) -> diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index c6c617f97f..78922af956 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -26,10 +26,9 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, basic/1, reload/1, upgrade/1, heap_frag/1, +-export([all/0, suite/0, + init_per_testcase/2, end_per_testcase/2, + basic/1, reload/1, upgrade/1, heap_frag/1, types/1, many_args/1, binaries/1, get_string/1, get_atom/1, maps/1, api_macros/1, @@ -77,29 +76,10 @@ all() -> nif_monotonic_time, nif_time_offset, nif_convert_time_unit ]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - init_per_testcase(_Case, Config) -> -% ?line Dog = ?t:timetrap(?t:seconds(60*60*24)), Config. end_per_testcase(_Func, _Config) -> - %%Dog = ?config(watchdog, Config), - %%?t:timetrap_cancel(Dog), P1 = code:purge(nif_mod), Del = code:delete(nif_mod), P2 = code:purge(nif_mod), diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index dcd0428e65..30f75b4cbc 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -32,10 +32,8 @@ -include_lib("common_test/include/ct.hrl"). -%-compile(export_all). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, init_per_testcase/2, - end_per_testcase/2, +-export([all/0, suite/0, init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2, node_container_refc_check/1]). -export([term_to_binary_to_term_eq/1, @@ -56,9 +54,10 @@ unique_pid/1, iter_max_procs/1]). --define(DEFAULT_TIMEOUT, ?t:minutes(10)). +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 10}}]. -suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [term_to_binary_to_term_eq, round_trip_eq, cmp, ref_eq, @@ -67,9 +66,6 @@ all() -> timer_refc, otp_4715, pid_wrap, port_wrap, bad_nc, unique_pid, iter_max_procs]. -groups() -> - []. - init_per_suite(Config) -> Config. @@ -78,13 +74,6 @@ end_per_suite(_Config) -> erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value available_internal_state(false). -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - available_internal_state(Bool) when Bool == true; Bool == false -> case {Bool, (catch erts_debug:get_internal_state(available_internal_state))} of @@ -101,13 +90,10 @@ available_internal_state(Bool) when Bool == true; Bool == false -> end. init_per_testcase(_Case, Config) when is_list(Config) -> - Dog = ?t:timetrap(?DEFAULT_TIMEOUT), available_internal_state(true), - [{watchdog, Dog}|Config]. + Config. end_per_testcase(_Case, Config) when is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), ok. %%% diff --git a/erts/emulator/test/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl index caa2d30a6c..14851d376f 100644 --- a/erts/emulator/test/nofrag_SUITE.erl +++ b/erts/emulator/test/nofrag_SUITE.erl @@ -22,9 +22,7 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, error_handler/1,error_handler_apply/1, error_handler_fixed_apply/1,error_handler_fun/1, debug_breakpoint/1]). @@ -32,37 +30,15 @@ %% Exported functions for an error_handler module. -export([undefined_function/3,undefined_lambda/3,breakpoint/3]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 3}}]. all() -> [error_handler, error_handler_apply, error_handler_fixed_apply, error_handler_fun, debug_breakpoint]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog = ?t:timetrap(?t:minutes(3)), - [{watchdog,Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - error_handler(Config) when is_list(Config) -> ?line process_flag(error_handler, ?MODULE), %% The term_to_binary/1 - binary_to_term/1 roundtrip is a good way diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl index 272131cb46..d62928c31d 100644 --- a/erts/emulator/test/old_scheduler_SUITE.erl +++ b/erts/emulator/test/old_scheduler_SUITE.erl @@ -22,14 +22,13 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]). -export([equal/1, many_low/1, few_low/1, max/1, high/1]). --define(default_timeout, ?t:minutes(11)). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 11}}]. all() -> case catch erlang:system_info(modified_timing_level) of @@ -42,21 +41,6 @@ all() -> _ -> [equal, many_low, few_low, max, high] end. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - %%----------------------------------------------------------------------------------- %% TEST SUITE DESCRIPTION @@ -78,18 +62,15 @@ end_per_group(_GroupName, Config) -> %%----------------------------------------------------------------------------------- init_per_testcase(_Case, Config) -> - ?line Dog = test_server:timetrap(?default_timeout), %% main test process needs max prio ?line Prio = process_flag(priority, max), ?line MS = erlang:system_flag(multi_scheduling, block), - [{prio,Prio},{watchdog,Dog},{multi_scheduling, MS}|Config]. + [{prio,Prio},{multi_scheduling, MS}|Config]. end_per_testcase(_Case, Config) -> erlang:system_flag(multi_scheduling, unblock), - Dog=?config(watchdog, Config), Prio=?config(prio, Config), process_flag(priority, Prio), - test_server:timetrap_cancel(Dog), ok. ok(Config) when is_list(Config) -> diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl index 4e15b27231..9948c13ce2 100644 --- a/erts/emulator/test/op_SUITE.erl +++ b/erts/emulator/test/op_SUITE.erl @@ -22,44 +22,20 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, bsl_bsr/1,logical/1,t_not/1,relop_simple/1,relop/1,complex_relop/1]). -export([]). -import(lists, [foldl/3,flatmap/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 3}}]. all() -> [bsl_bsr, logical, t_not, relop_simple, relop, complex_relop]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(3)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - %% Test the bsl and bsr operators. bsl_bsr(Config) when is_list(Config) -> Vs = [unvalue(V) || V <- [-16#8000009-2,-1,0,1,2,73,16#8000000,bad,[]]], diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 5274da301a..365c63b468 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -105,7 +105,9 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 10}}]. all() -> [otp_6224, {group, stream}, basic_ping, slow_writes, @@ -133,9 +135,6 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. - --define(DEFAULT_TIMEOUT, ?t:minutes(5)). - init_per_testcase(Case, Config) -> [{testcase, Case} |Config]. @@ -162,7 +161,7 @@ win_massive(Config) when is_list(Config) -> end. do_win_massive() -> - Dog = test_server:timetrap(test_server:seconds(360)), + ct:timetrap({minutes, 6}), SuiteDir = filename:dirname(code:which(?MODULE)), Ports = " +Q 8192", {ok, Node} = @@ -171,7 +170,6 @@ do_win_massive() -> [{args, " -pa " ++ SuiteDir ++ Ports}]), ok = rpc:call(Node,?MODULE,win_massive_client,[3000]), test_server:stop_node(Node), - test_server:timetrap_cancel(Dog), ok. win_massive_client(N) -> @@ -205,19 +203,14 @@ win_massive_loop(P,N) -> [] end. - - - %% Test that we can send a stream of bytes and get it back. %% We will send only a small amount of data, to avoid deadlock. stream_small(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), stream_ping(Config, 512, "", []), stream_ping(Config, 1777, "", []), stream_ping(Config, 1777, "-s512", []), - test_server:timetrap_cancel(Dog), ok. %% Send big amounts of data (much bigger than the buffer size in port test). @@ -225,22 +218,20 @@ stream_small(Config) when is_list(Config) -> %% non-blocking reads and writes. stream_big(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(180)), + ct:timetrap({seconds, 180}), stream_ping(Config, 43755, "", []), stream_ping(Config, 100000, "", []), stream_ping(Config, 77777, " -s40000", []), - test_server:timetrap_cancel(Dog), ok. %% Sends packet with header size of 1, 2, and 4, with packets of various %% sizes. basic_ping(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(120)), + ct:timetrap({minutes, 2}), ping(Config, sizes(1), 1, "", []), ping(Config, sizes(2), 2, "", []), ping(Config, sizes(4), 4, "", []), - test_server:timetrap_cancel(Dog), ok. %% Let the port program insert delays between characters sent back to @@ -248,17 +239,14 @@ basic_ping(Config) when is_list(Config) -> %% small chunks rather than all at once. slow_writes(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(20)), ping(Config, [8], 4, "-s1", []), ping(Config, [10], 2, "-s2", []), - test_server:timetrap_cancel(Dog), ok. bad_packet(doc) -> ["Test that we get {'EXIT', Port, einval} if we try to send a bigger " "packet than the packet header allows."]; bad_packet(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), PortTest = port_test(Config), process_flag(trap_exit, true), @@ -266,8 +254,6 @@ bad_packet(Config) when is_list(Config) -> bad_packet(PortTest, 1, 257), bad_packet(PortTest, 2, 65536), bad_packet(PortTest, 2, 65537), - - test_server:timetrap_cancel(Dog), ok. bad_packet(PortTest, HeaderSize, PacketSize) -> @@ -288,7 +274,6 @@ make_zero_packet(N) -> %% Test sending bad messages to a port. bad_port_messages(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), PortTest = port_test(Config), process_flag(trap_exit, true), @@ -296,8 +281,6 @@ bad_port_messages(Config) when is_list(Config) -> bad_message(PortTest, {a}), bad_message(PortTest, {self(),{command,bad_command}}), bad_message(PortTest, {self(),{connect,no_pid}}), - - test_server:timetrap_cancel(Dog), ok. bad_message(PortTest, Message) -> @@ -315,7 +298,7 @@ bad_message(PortTest, Message) -> %% Tests the 'binary' option for a port. t_binary(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(300)), + ct:timetrap({seconds, 300}), %% Packet mode. ping(Config, sizes(1), 1, "", [binary]), @@ -326,12 +309,10 @@ t_binary(Config) when is_list(Config) -> stream_ping(Config, 435, "", [binary]), stream_ping(Config, 43755, "", [binary]), stream_ping(Config, 100000, "", [binary]), - - test_server:timetrap_cancel(Dog), ok. name1(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(100)), + ct:timetrap({seconds, 100}), PortTest = port_test(Config), Command = lists:concat([PortTest, " "]), P = open_port({spawn, Command}, []), @@ -348,13 +329,12 @@ name1(Config) when is_list(Config) -> {P, closed} -> ok end, undefined = whereis(myport), - test_server:timetrap_cancel(Dog), ok. %% Test that the 'eof' option works. eof(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(100)), + ct:timetrap({seconds, 100}), PortTest = port_test(Config), Command = lists:concat([PortTest, " -h0 -q"]), P = open_port({spawn, Command}, [eof]), @@ -366,25 +346,23 @@ eof(Config) when is_list(Config) -> receive {P, closed} -> ok end, - test_server:timetrap_cancel(Dog), ok. %% Tests that the 'in' option for a port works. input_only(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(300)), + ct:timetrap({seconds, 300}), expect_input(Config, [0, 1, 10, 13, 127, 128, 255], 1, "", [in]), expect_input(Config, [0, 1, 255, 2048], 2, "", [in]), expect_input(Config, [0, 1, 255, 2048], 4, "", [in]), expect_input(Config, [0, 1, 10, 13, 127, 128, 255], 1, "", [in, binary]), - test_server:timetrap_cancel(Dog), ok. %% Tests that the 'out' option for a port works. output_only(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(100)), + ct:timetrap({seconds, 100}), Dir = ?config(priv_dir, Config), %% First we test that the port program gets the data @@ -399,8 +377,6 @@ output_only(Config) when is_list(Config) -> %% Then we test that any writes to stdout from %% the port program is not sent to erlang output_and_verify(Config, ["-h0"], Data), - - test_server:timetrap_cancel(Dog), ok. output_and_verify(Config, Options, Data) -> @@ -421,11 +397,10 @@ output_and_verify(Config, Options, Data) -> %% Basic test of receiving multiple packages, written in %% one operation by the other end. mul_basic(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(600)), + ct:timetrap({minutes, 10}), expect_input(Config, [0, 1, 255, 10, 13], 1, "", []), expect_input(Config, [0, 10, 13, 1600, 32767, 65535], 2, "", []), expect_input(Config, [10, 70000], 4, "", []), - test_server:timetrap_cancel(Dog), ok. %% Test reading a buffer consisting of several packets, some @@ -434,9 +409,8 @@ mul_basic(Config) when is_list(Config) -> %% delays in between.) mul_slow_writes(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(250)), + ct:timetrap({minutes, 4}), expect_input(Config, [0, 20, 255, 10, 1], 1, "-s64", []), - test_server:timetrap_cancel(Dog), ok. %% Runs several port tests in parallell. Each individual test @@ -444,7 +418,7 @@ mul_slow_writes(Config) when is_list(Config) -> %% should also finish in about 5 seconds. parallell(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(300)), + ct:timetrap({minutes, 5}), Testers = [ fun() -> stream_ping(Config, 1007, "-s100", []) end, fun() -> stream_ping(Config, 10007, "-s1000", []) end, @@ -464,7 +438,6 @@ parallell(Config) when is_list(Config) -> process_flag(trap_exit, true), Pids = lists:map(fun fun_spawn/1, Testers), wait_for(Pids), - test_server:timetrap_cancel(Dog), ok. wait_for([]) -> @@ -483,7 +456,7 @@ wait_for(Pids) -> dying_port(suite) -> []; dying_port(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(150)), + ct:timetrap({minutes, 2}), process_flag(trap_exit, true), P1 = make_dying_port(Config), @@ -504,8 +477,6 @@ dying_port(Config) when is_list(Config) -> wait_for_port_exit(P3), wait_for_port_exit(P4), wait_for_port_exit(P5), - - test_server:timetrap_cancel(Dog), ok. wait_for_port_exit(Port) -> @@ -532,7 +503,7 @@ make_dying_port(Config) when is_list(Config) -> port_program_with_path(suite) -> []; port_program_with_path(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(100)), + ct:timetrap({minutes, 2}), DataDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), @@ -567,7 +538,6 @@ port_program_with_path(Config) when is_list(Config) -> {P, {data, Message}} -> ok end, - test_server:timetrap_cancel(Dog), ok. @@ -575,7 +545,6 @@ 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) -> - 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 @@ -591,13 +560,12 @@ open_input_file_port(Config) when is_list(Config) -> ok end end, - test_server:timetrap_cancel(Dog), ok. %% Tests that files can be written using open_port(Filename, [out]). open_output_file_port(suite) -> []; open_output_file_port(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(100)), + ct:timetrap({minutes, 2}), PrivDir = ?config(priv_dir, Config), %% Create a file with open_port/2 and read it back with @@ -613,8 +581,6 @@ open_output_file_port(Config) when is_list(Config) -> OutputPort ! {self(), close}, {ok, Bin} = file:read_file(MyFile2), FileData2 = binary_to_list(Bin), - - test_server:timetrap_cancel(Dog), ok. %% Tests that all appropriate fd's have been closed in the port program @@ -670,7 +636,7 @@ iter_max_ports(Config) when is_list(Config) -> iter_max_ports_test(Config) -> - Dog = test_server:timetrap(test_server:minutes(30)), + ct:timetrap({minutes, 30}), PortTest = port_test(Config), Command = lists:concat([PortTest, " -h0 -q"]), Iters = case os:type() of @@ -687,7 +653,6 @@ iter_max_ports_test(Config) -> io:format("Result: ~p",[L]), all_equal(L), all_equal(L), - test_server:timetrap_cancel(Dog), {comment, "Max ports: " ++ integer_to_list(hd(L))}. do_iter_max_ports(N, Command) when N > 0 -> @@ -779,14 +744,13 @@ tps_1K(Config) when is_list(Config) -> tps(1024, Config). tps(Size, Config) -> - Dog = test_server:timetrap(test_server:seconds(300)), + ct:timetrap({minutes, 5}), PortTest = port_test(Config), Packet = list_to_binary(random_packet(Size, "e")), Port = open_port({spawn, PortTest}, [binary, {packet, 2}]), Transactions = 10000, {Elapsed, ok} = test_server:timecall(?MODULE, tps, [Port, Packet, Transactions]), - test_server:timetrap_cancel(Dog), {comment, integer_to_list(trunc(Transactions/Elapsed+0.5)) ++ " transactions/s"}. tps(_Port, _Packet, 0) -> ok; @@ -801,8 +765,8 @@ tps(Port, Packet, N) -> %% Line I/O test line(Config) when is_list(Config) -> + ct:timetrap({minutes, 5}), Siz = 110, - Dog = test_server:timetrap(test_server:seconds(300)), Packet1 = random_packet(Siz), Packet2 = random_packet(Siz div 2), %% Test that packets are split into lines @@ -841,7 +805,6 @@ line(Config) when is_list(Config) -> {eol, Packet1}]}], 0, "-d", [{line,Siz}]), %% Test that we get badarg if trying both packet and line bad_argument(Config, [{packet, 5}, {line, 5}]), - test_server:timetrap_cancel(Dog), ok. %%% Redirection of stderr test @@ -850,7 +813,7 @@ 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) -> - Dog = test_server:timetrap(test_server:seconds(60)), + ct:timetrap({minutes, 1}), %% See that it works Packet = random_packet(10), port_expect(Config,[{Packet,[Packet]}], 0, "-e -l10", @@ -858,7 +821,6 @@ stderr_to_stdout(Config) when is_list(Config) -> %% stream_ping(Config, 10, "-e", [stderr_to_stdout]), %% See that it doesn't always happen (will generate garbage on stderr) port_expect(Config,[{Packet,[eof]}], 0, "-e -l10", [line,eof]), - test_server:timetrap_cancel(Dog), ok. @@ -878,7 +840,7 @@ env(suite) -> env(doc) -> ["Test that the 'env' option works"]; env(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(60)), + ct:timetrap({minutes, 1}), Priv = ?config(priv_dir, Config), Temp = filename:join(Priv, "env_fun.bin"), @@ -909,8 +871,6 @@ env(Config) when is_list(Config) -> ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"} || X <- lists:seq(1,150)], env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)), - - test_server:timetrap_cancel(Dog), ok. env_slave(File, Env) -> @@ -1013,7 +973,7 @@ cd(suite) -> cd(doc) -> ["Test that the 'cd' option works"]; cd(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(60)), + ct:timetrap({minutes, 1}), Program = atom_to_list(lib:progname()), DataDir = ?config(data_dir, Config), @@ -1048,8 +1008,6 @@ cd(Config) when is_list(Config) -> Other3 -> test_server:fail({env, Other3}) end, - - test_server:timetrap_cancel(Dog), ok. filename_equal(A, B) -> @@ -1287,7 +1245,7 @@ otp_4389(doc) -> []; otp_4389(Config) when is_list(Config) -> case os:type() of {unix, _} -> - Dog = test_server:timetrap(test_server:seconds(240)), + ct:timetrap({minutes, 4}), TCR = self(), case get_true_cmd() of True when is_list(True) -> @@ -1326,7 +1284,6 @@ otp_4389(Config) when is_list(Config) -> end) end, lists:duplicate(1000,[]))), - test_server:timetrap_cancel(Dog), {comment, "This test case doesn't always fail when the bug that " "it tests for is present (it is most likely to fail on" @@ -1363,11 +1320,10 @@ exit_status(suite) -> exit_status(doc) -> ["Test that the 'exit_status' option works"]; exit_status(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(60)), - port_expect(Config,[{"x", - [{exit_status, 5}]}], - 1, "", [exit_status]), - test_server:timetrap_cancel(Dog), + ct:timetrap({minutes, 1}), + port_expect(Config, + [{"x", [{exit_status, 5}]}], + 1, "", [exit_status]), ok. spawn_driver(suite) -> @@ -1375,7 +1331,6 @@ spawn_driver(suite) -> spawn_driver(doc) -> ["Test spawning a driver specifically"]; spawn_driver(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), Path = ?config(data_dir, Config), ok = load_driver(Path, "echo_drv"), Port = erlang:open_port({spawn_driver, "echo_drv"}, []), @@ -1404,7 +1359,6 @@ spawn_driver(Config) when is_list(Config) -> {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "ls"}, [])), {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "cmd"}, [])), {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, os:find_executable("erl")}, [])), - test_server:timetrap_cancel(Dog), ok. parallelism_option(suite) -> @@ -1412,7 +1366,6 @@ parallelism_option(suite) -> parallelism_option(doc) -> ["Test parallelism option of open_port"]; parallelism_option(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), ?line Path = ?config(data_dir, Config), ?line ok = load_driver(Path, "echo_drv"), ?line Port = erlang:open_port({spawn_driver, "echo_drv"}, @@ -1441,7 +1394,6 @@ parallelism_option(Config) when is_list(Config) -> end, ?line Port2 ! {self(), close}, ?line receive {Port2, closed} -> ok end, - ?line test_server:timetrap_cancel(Dog), ok. spawn_executable(suite) -> @@ -1449,7 +1401,6 @@ spawn_executable(suite) -> spawn_executable(doc) -> ["Test spawning an executable specifically"]; spawn_executable(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), DataDir = ?config(data_dir, Config), EchoArgs1 = filename:join([DataDir,"echo_args"]), ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)), @@ -1555,7 +1506,6 @@ spawn_executable(Config) when is_list(Config) -> {unix,_} -> test_sh_file(SpaceDir) end, - test_server:timetrap_cancel(Dog), ok. unregister_name(Config) when is_list(Config) -> @@ -1691,7 +1641,6 @@ 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) -> - 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"}, []), @@ -1724,7 +1673,6 @@ mix_up_ports(Config) when is_list(Config) -> after 1000 -> ok end, - test_server:timetrap_cancel(Dog), ok. loop(Stop, Stop, Fun) when is_function(Fun) -> @@ -1739,7 +1687,6 @@ 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) -> - 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(), @@ -1753,7 +1700,6 @@ otp_5112(Config) when is_list(Config) -> {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() -> @@ -1787,7 +1733,6 @@ otp_5119(suite) -> otp_5119(doc) -> ["Test that port index is not unnecessarily wrapped"]; otp_5119(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), Path = ?config(data_dir, Config), ok = load_driver(Path, "exit_drv"), PI1 = port_ix(otp_5119_fill_empty_port_tab([])), @@ -1806,7 +1751,6 @@ otp_5119(Config) when is_list(Config) -> ?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) -> @@ -1833,7 +1777,6 @@ port_ix(Port) when is_port(Port) -> otp_6224(doc) -> ["Check that port command failure doesn't crash the emulator"]; otp_6224(suite) -> []; otp_6224(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), Path = ?config(data_dir, Config), ok = load_driver(Path, "failure_drv"), Go = make_ref(), @@ -1855,7 +1798,6 @@ otp_6224(Config) when is_list(Config) -> Reason}) end end, - test_server:timetrap_cancel(Dog), ok. otp_6224_loop() -> @@ -1872,7 +1814,7 @@ exit_status_multi_scheduling_block(Config) when is_list(Config) -> Repeat = 3, case ?t:os_type() of {unix, _} -> - Dog = ?t:timetrap(test_server:minutes(2*Repeat)), + ct:timetrap({minutes, 2*Repeat}), SleepSecs = 6, try lists:foreach(fun (_) -> @@ -1884,7 +1826,6 @@ exit_status_multi_scheduling_block(Config) when is_list(Config) -> %% Wait for the system to recover (regardless %% of success or not) otherwise later testcases %% may unnecessarily fail. - ?t:timetrap_cancel(Dog), receive after SleepSecs+500 -> ok end end; _ -> {skip, "Not implemented for this OS"} @@ -2400,7 +2341,7 @@ 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) -> - Dog = test_server:timetrap(test_server:seconds(100)), + ct:timetrap({minutes, 2}), DataDir = ?config(data_dir, Config), DeadPort = os:find_executable("dead_port", DataDir), Port = open_port({spawn,DeadPort++" 60"},[]), @@ -2410,7 +2351,6 @@ close_deaf_port(Config) when is_list(Config) -> Res = close_deaf_port_1(0, DeadPort), io:format("Waiting for OS procs to terminate...\n"), receive after 5*1000 -> ok end, - test_server:timetrap_cancel(Dog), Res. close_deaf_port_1(200, _) -> diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index 9215d7f720..c7659eed9e 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -21,8 +21,8 @@ -module(port_bif_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, command/1, +-export([all/0, suite/0, groups/0, + command/1, command_e_1/1, command_e_2/1, command_e_3/1, command_e_4/1, port_info1/1, port_info2/1, port_info_os_pid/1, port_info_race/1, @@ -30,11 +30,11 @@ -export([do_command_e_1/1, do_command_e_2/1, do_command_e_4/1]). --export([init_per_testcase/2, end_per_testcase/2]). - -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 10}}]. all() -> [command, {group, port_info}, connect, control, @@ -46,27 +46,6 @@ groups() -> {port_info, [], [port_info1, port_info2, port_info_os_pid, port_info_race]}]. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - - -init_per_testcase(_Func, Config) when is_list(Config) -> - Dog=test_server:timetrap(test_server:minutes(10)), - [{watchdog, Dog}|Config]. -end_per_testcase(_Func, Config) when is_list(Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog). - command(Config) when is_list(Config) -> load_control_drv(Config), @@ -410,7 +389,7 @@ test_op(P, Op) -> <> = list_to_binary(R). echo_to_busy(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), + ct:timetrap({seconds, 10}), load_control_drv(Config), P = open_port({spawn, control_drv}, []), erlang:port_control(P, $b, [1]), % Set to busy. @@ -426,7 +405,6 @@ echo_to_busy(Config) when is_list(Config) -> Other -> test_server:fail({unexpected_message, Other}) end, - test_server:timetrap_cancel(Dog), ok. echoer(P, ReplyTo) -> diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 862fe78b85..d0af5f39b1 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -67,7 +67,9 @@ -export([hangaround/2, processes_bif_test/0, do_processes/1, processes_term_proc_list_test/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 9}}]. all() -> [spawn_with_binaries, t_exit_1, {group, t_exit_2}, @@ -128,12 +130,10 @@ end_per_group(_GroupName, Config) -> Config. init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(10)), - [{watchdog, Dog},{testcase, Func}|Config]. + [{testcase, Func}|Config]. end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). + ok. fun_spawn(Fun) -> spawn_link(erlang, apply, [Fun, []]). @@ -158,11 +158,10 @@ binary_owner(Bin) when is_binary(Bin) -> %% Tests exit/1 with a big message. t_exit_1(Config) when is_list(Config) -> + ct:timetrap({seconds, 20}), start_spawner(), - Dog = test_server:timetrap(test_server:seconds(20)), process_flag(trap_exit, true), test_server:do_times(10, fun t_exit_1/0), - test_server:timetrap_cancel(Dog), stop_spawner(), ok. @@ -176,11 +175,10 @@ t_exit_1() -> %% Tests exit/2 with a lot of data in the exit message. t_exit_2_other(Config) when is_list(Config) -> + ct:timetrap({seconds, 20}), start_spawner(), - Dog = test_server:timetrap(test_server:seconds(20)), process_flag(trap_exit, true), test_server:do_times(10, fun t_exit_2_other/0), - test_server:timetrap_cancel(Dog), stop_spawner(), ok. @@ -194,7 +192,7 @@ t_exit_2_other() -> %% Tests that exit(Pid, normal) does not kill another process.; t_exit_2_other_normal(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(20)), + ct:timetrap({seconds, 20}), process_flag(trap_exit, true), Pid = fun_spawn(fun() -> receive x -> ok end end), exit(Pid, normal), @@ -211,17 +209,15 @@ t_exit_2_other_normal(Config) when is_list(Config) -> ok end, exit(Pid, kill), - test_server:timetrap_cancel(Dog), ok. %% Tests that we can trap an exit message sent with exit/2 from %% the same process. self_exit(Config) when is_list(Config) -> + ct:timetrap({seconds, 10}), start_spawner(), - Dog = test_server:timetrap(test_server:seconds(10)), process_flag(trap_exit, true), test_server:do_times(200, fun self_exit/0), - test_server:timetrap_cancel(Dog), stop_spawner(), ok. @@ -270,11 +266,10 @@ t_exit_2_catch(Config) when is_list(Config) -> %% Tests trapping of an 'EXIT' message generated by a bad argument to %% the abs/1 bif. The 'EXIT' message will intentionally be very big. trap_exit_badarg(Config) when is_list(Config) -> + ct:timetrap({seconds, 10}), start_spawner(), - Dog = test_server:timetrap(test_server:seconds(10)), process_flag(trap_exit, true), test_server:do_times(10, fun trap_exit_badarg/0), - test_server:timetrap_cancel(Dog), stop_spawner(), ok. @@ -320,10 +315,9 @@ big_binary(N, Acc) -> %% Test receiving an EXIT message when spawning a BIF with bad arguments. trap_exit_badarg_in_bif(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), + ct:timetrap({seconds, 10}), process_flag(trap_exit, true), test_server:do_times(10, fun trap_exit_badarg_bif/0), - test_server:timetrap_cancel(Dog), ok. trap_exit_badarg_bif() -> @@ -345,15 +339,13 @@ trap_exit_badarg_bif() -> %% 3) The process will crash the next time it executes 'receive'. exit_and_timeout(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(20)), + ct:timetrap({seconds, 20}), process_flag(trap_exit, true), Parent = self(), Low = fun_spawn(fun() -> eat_low(Parent) end), High = fun_spawn(fun() -> eat_high(Low) end), eat_wait_for(Low, High), - - test_server:timetrap_cancel(Dog), ok. @@ -397,14 +389,12 @@ loop(StopTime) -> %% Tries to send two different exit messages to a process. %% (The second one should be ignored.) exit_twice(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(20)), + ct:timetrap({seconds, 20}), process_flag(trap_exit, true), Low = fun_spawn(fun etwice_low/0), High = fun_spawn(fun() -> etwice_high(Low) end), etwice_wait_for(Low, High), - - test_server:timetrap_cancel(Dog), ok. etwice_wait_for(Low, High) -> diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl index 635c3d27c5..d471bbecc9 100644 --- a/erts/emulator/test/receive_SUITE.erl +++ b/erts/emulator/test/receive_SUITE.erl @@ -24,13 +24,12 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, call_with_huge_message_queue/1,receive_in_between/1]). --export([init_per_testcase/2,end_per_testcase/2]). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 3}}]. all() -> [call_with_huge_message_queue, receive_in_between]. @@ -38,27 +37,6 @@ all() -> groups() -> []. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(3)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - call_with_huge_message_queue(Config) when is_list(Config) -> Pid = spawn_link(fun echo_loop/0), diff --git a/erts/emulator/test/ref_SUITE.erl b/erts/emulator/test/ref_SUITE.erl index 6d4a998094..1bf94b9a3f 100644 --- a/erts/emulator/test/ref_SUITE.erl +++ b/erts/emulator/test/ref_SUITE.erl @@ -20,45 +20,20 @@ -module(ref_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2]). +-export([all/0, suite/0]). -export([wrap_1/1]). -export([loop_ref/1]). -include_lib("common_test/include/ct.hrl"). -init_per_testcase(_, Config) -> - ?line Dog=test_server:timetrap(test_server:minutes(2)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 2}}]. all() -> [wrap_1]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - wrap_1(doc) -> "Check that refs don't wrap around easily."; wrap_1(Config) when is_list(Config) -> ?line spawn_link(?MODULE, loop_ref, [self()]), diff --git a/erts/emulator/test/register_SUITE.erl b/erts/emulator/test/register_SUITE.erl index 53bf02e085..68b36f99c3 100644 --- a/erts/emulator/test/register_SUITE.erl +++ b/erts/emulator/test/register_SUITE.erl @@ -26,44 +26,17 @@ -include_lib("common_test/include/ct.hrl"). %-compile(export_all). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0]). -export([otp_8099/1]). --define(DEFAULT_TIMEOUT, ?t:minutes(2)). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 2}}]. all() -> [otp_8099]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Case, Config) when is_list(Config) -> - Dog = ?t:timetrap(?DEFAULT_TIMEOUT), - [{watchdog, Dog}, {testcase, Case} | Config]. - -end_per_testcase(_Case, Config) when is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. - %% %% Test cases %% diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index f998e7d4d2..03e546c00d 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -34,9 +34,9 @@ -include_lib("common_test/include/ct.hrl"). %-compile(export_all). --export([all/0, suite/0,groups/0,init_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2, end_per_suite/1]). +-export([all/0, suite/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). -export([equal/1, few_low/1, @@ -60,11 +60,9 @@ dirty_scheduler_exit/1, reader_groups/1]). --define(DEFAULT_TIMEOUT, ?t:minutes(15)). - --define(MIN_SCHEDULER_TEST_TIMEOUT, ?t:minutes(1)). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 15}}]. all() -> [equal, few_low, many_low, equal_with_part_time_high, @@ -89,12 +87,6 @@ end_per_suite(Config) -> catch erts_debug:set_internal_state(available_internal_state, false), Config. -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - init_per_testcase(update_cpu_info, Config) -> case os:find_executable("taskset") of false -> @@ -106,15 +98,12 @@ init_per_testcase(Case, Config) when is_list(Config) -> init_per_tc(Case, Config). init_per_tc(Case, Config) -> - Dog = ?t:timetrap(?DEFAULT_TIMEOUT), process_flag(priority, max), erlang:display({'------------', ?MODULE, Case, '------------'}), OkRes = ok, - [{watchdog, Dog}, {testcase, Case}, {ok_res, OkRes} |Config]. + [{testcase, Case}, {ok_res, OkRes} |Config]. end_per_testcase(_Case, Config) when is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), ok. -define(ERTS_RUNQ_CHECK_BALANCE_REDS_PER_SCHED, (2000*2000)). @@ -1328,10 +1317,9 @@ scheduler_suspend_basic_test() -> scheduler_suspend(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:minutes(5)), + ct:timetrap({minutes, 5}), ?line lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end, [64, 32, 16, default]), - ?line ?t:timetrap_cancel(Dog), ?line ok. scheduler_suspend_test(Config, Schedulers) -> ?line Cmd = case Schedulers of @@ -2044,8 +2032,8 @@ do_it(Tracer, Low, Normal, High, Max, RedsPerSchedLimit) -> EndWait = erlang:monotonic_time(milli_seconds), BalanceWait = EndWait-StartWait, erlang:display({balance_wait, BalanceWait}), - Timeout = ?DEFAULT_TIMEOUT - ?t:minutes(4) - BalanceWait, - Res = case Timeout < ?MIN_SCHEDULER_TEST_TIMEOUT of + Timeout = (15 - 4)*60*1000 - BalanceWait, + Res = case Timeout < 60*1000 of true -> stop_work(Low, Normal, High, Max), too_slow; diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index 63c11519b8..de453e090a 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -20,43 +20,19 @@ -module(send_term_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2,basic/1]). --export([init_per_testcase/2,end_per_testcase/2]). +-export([all/0, suite/0]). -export([generate_external_terms_files/1]). -include_lib("common_test/include/ct.hrl"). -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(3)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 3}}]. all() -> [basic]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - basic(Config) when is_list(Config) -> Drv = "send_term_drv", ?line P = start_driver(Config, Drv), diff --git a/erts/emulator/test/sensitive_SUITE.erl b/erts/emulator/test/sensitive_SUITE.erl index 29517cdcb8..a5cc25f55a 100644 --- a/erts/emulator/test/sensitive_SUITE.erl +++ b/erts/emulator/test/sensitive_SUITE.erl @@ -22,9 +22,7 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, stickiness/1,send_trace/1,recv_trace/1,proc_trace/1,call_trace/1, meta_trace/1,running_trace/1,gc_trace/1,seq_trace/1, t_process_info/1,t_process_display/1,save_calls/1]). @@ -33,15 +31,9 @@ -import(lists, [keysearch/3,foreach/2,sort/1]). -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog = ?t:timetrap(?t:minutes(5)), - [{watchdog,Dog}|Config]. - -end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 5}}]. all() -> [stickiness, send_trace, recv_trace, proc_trace, @@ -49,22 +41,6 @@ all() -> seq_trace, t_process_info, t_process_display, save_calls]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - stickiness(Config) when is_list(Config) -> ?line {Tracer,Mref} = spawn_monitor(fun() -> receive after infinity -> ok end diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index e176fe52d6..63996fb9f2 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -28,12 +28,10 @@ -module(signal_SUITE). -author('rickard.s.green@ericsson.com'). --define(DEFAULT_TIMEOUT_SECONDS, 120). - %-define(line_trace, 1). -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2]). +-export([all/0, suite/0,init_per_suite/1, end_per_suite/1]). +-export([init_per_testcase/2, end_per_testcase/2]). % Test cases -export([xm_sig_order/1, @@ -51,16 +49,12 @@ pending_exit_group_leader/1, exit_before_pending_exit/1]). --export([init_per_testcase/2, end_per_testcase/2]). - init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(?DEFAULT_TIMEOUT_SECONDS)), available_internal_state(true), - ?line [{testcase, Func},{watchdog, Dog}|Config]. + [{testcase, Func}|Config]. end_per_testcase(_Func, Config) -> - ?line Dog = ?config(watchdog, Config), - ?line ?t:timetrap_cancel(Dog). + ok. init_per_suite(Config) -> Config. @@ -70,7 +64,9 @@ end_per_suite(_Config) -> catch erts_debug:set_internal_state(not_running_optimization, true), available_internal_state(false). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 2}}]. all() -> [xm_sig_order, pending_exit_unlink_process, @@ -83,16 +79,6 @@ all() -> pending_exit_process_info_2, pending_exit_group_leader, exit_before_pending_exit]. -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - xm_sig_order(doc) -> ["Test that exit signals and messages are received " "in correct order"]; xm_sig_order(suite) -> []; diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 2df19cd20b..751129c7bb 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -23,35 +23,18 @@ -include_lib("common_test/include/ct.hrl"). %-compile(export_all). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]). -export([boot_combo/1, native_atomics/1, jump_table/1]). --define(DEFAULT_TIMEOUT, ?t:minutes(2)). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 2}}]. all() -> [boot_combo, native_atomics, jump_table]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - init_per_testcase(boot_combo = Case, Config) when is_list(Config) -> case erlang:system_info(build_type) of opt -> @@ -63,12 +46,9 @@ init_per_testcase(Case, Config) when is_list(Config) -> init_per_tc(Case, Config). init_per_tc(Case, Config) -> - Dog = ?t:timetrap(?DEFAULT_TIMEOUT), - [{testcase, Case},{watchdog, Dog}|Config]. + [{testcase, Case}|Config]. end_per_testcase(_Case, Config) when is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), ok. %%% diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index df96b018a1..12b35cbcdf 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -41,15 +41,14 @@ -include_lib("common_test/include/ct.hrl"). init_per_testcase(_, Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(300)), - [{watchdog, Dog}|Config]. + Config. -end_per_testcase(_, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), +end_per_testcase(_, _Config) -> ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 4}}]. all() -> [{group, wall_clock}, {group, runtime}, reductions, @@ -77,12 +76,8 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. - - %%% Testing statistics(wall_clock). - - wall_clock_zero_diff(doc) -> "Tests that the 'Wall clock since last call' element of the result " "is zero when statistics(runtime) is called twice in succession."; diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index ae18b280cf..e6bef8f14f 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -33,47 +33,19 @@ -include_lib("common_test/include/ct.hrl"). -%-compile(export_all). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0]). --export([process_count/1, system_version/1, misc_smoke_tests/1, heap_size/1, wordsize/1, memory/1, - ets_limit/1]). +-export([process_count/1, system_version/1, misc_smoke_tests/1, + heap_size/1, wordsize/1, memory/1, ets_limit/1]). --define(DEFAULT_TIMEOUT, ?t:minutes(2)). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 2}}]. all() -> [process_count, system_version, misc_smoke_tests, heap_size, wordsize, memory, ets_limit]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(_Case, Config) when is_list(Config) -> - Dog = ?t:timetrap(?DEFAULT_TIMEOUT), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) when is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. - %%% %%% The test cases ------------------------------------------------------------- %%% diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl index c63a119f9d..4ac48872df 100644 --- a/erts/emulator/test/system_profile_SUITE.erl +++ b/erts/emulator/test/system_profile_SUITE.erl @@ -23,54 +23,25 @@ -module(system_profile_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, system_profile_on_and_off/1, - runnable_procs/1, - runnable_ports/1, + runnable_procs/1, runnable_ports/1, dont_profile_profiler/1, - scheduler/1 - ]). - --export([init_per_testcase/2, end_per_testcase/2]). + scheduler/1]). -export([profiler_process/1, ring_loop/1, port_echo_start/0, list_load/0, run_load/2]). -include_lib("common_test/include/ct.hrl"). --define(default_timeout, ?t:minutes(1)). - -init_per_testcase(_Case, Config) -> - Dog=?t:timetrap(?default_timeout), - [{watchdog, Dog}|Config]. -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. all() -> [system_profile_on_and_off, runnable_procs, runnable_ports, scheduler, dont_profile_profiler]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - %% No specification clause needed for an init function in a conf case!!! %% Test switching system_profiling on and off. diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index f4615d6810..dca44a1891 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -20,8 +20,7 @@ -module(timer_bif_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, init_per_suite/1, end_per_suite/1, init_per_testcase/2,end_per_testcase/2]). -export([start_timer_1/1, send_after_1/1, send_after_2/1, send_after_3/1, cancel_timer_1/1, @@ -40,16 +39,13 @@ -define(AUTO_CANCEL_YIELD_LIMIT, 100). init_per_testcase(_Case, Config) -> - ?line Dog=test_server:timetrap(test_server:seconds(30)), case catch erts_debug:get_internal_state(available_internal_state) of true -> ok; _ -> erts_debug:set_internal_state(available_internal_state, true) end, - [{watchdog, Dog}|Config]. + Config. -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), +end_per_testcase(_Case, _Config) -> ok. init_per_suite(Config) -> @@ -59,7 +55,9 @@ init_per_suite(Config) -> end_per_suite(_Config) -> catch erts_debug:set_internal_state(available_internal_state, false). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 5}}]. all() -> [start_timer_1, send_after_1, send_after_2, @@ -72,15 +70,6 @@ all() -> % same_time_yielding_with_cancel_other_accessor, auto_cancel_yielding]. -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - start_timer_1(doc) -> ["Basic start_timer/3 functionality"]; start_timer_1(Config) when is_list(Config) -> diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 02c2c7a93a..b74d1b383a 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -24,8 +24,8 @@ %%% Tests the trace BIF. %%% --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, receive_trace/1, self_send/1, +-export([all/0, suite/0, + receive_trace/1, self_send/1, timeout_trace/1, send_trace/1, procs_trace/1, dist_procs_trace/1, suspend/1, mutual_suspend/1, suspend_exit/1, suspender_exit/1, @@ -43,7 +43,9 @@ %%% Internal exports -export([process/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 5}}]. all() -> [cpu_timestamp, receive_trace, self_send, timeout_trace, @@ -54,31 +56,13 @@ all() -> set_on_first_spawn, system_monitor_args, more_system_monitor_args, system_monitor_long_gc_1, system_monitor_long_gc_2, system_monitor_large_heap_1, - system_monitor_long_schedule, + system_monitor_long_schedule, system_monitor_large_heap_2, bad_flag, trace_delivered]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - %% No longer testing anything, just reporting whether cpu_timestamp %% is enabled or not. cpu_timestamp(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - %% Test whether cpu_timestamp is implemented on this platform. ?line Works = try erlang:trace(all, true, [cpu_timestamp]) of _ -> @@ -87,8 +71,6 @@ cpu_timestamp(Config) when is_list(Config) -> catch error:badarg -> false end, - - ?line test_server:timetrap_cancel(Dog), {comment,case Works of false -> "cpu_timestamp is NOT implemented/does not work"; true -> "cpu_timestamp works" @@ -98,7 +80,6 @@ cpu_timestamp(Config) when is_list(Config) -> %% Tests that trace(Pid, How, ['receive']) works. receive_trace(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), ?line Receiver = fun_spawn(fun receiver/0), ?line process_flag(trap_exit, true), @@ -121,15 +102,11 @@ receive_trace(Config) when is_list(Config) -> ?line Receiver ! {hello, there}, ?line Receiver ! any_garbage, ?line receive_nothing(), - - %% Done. - ?line test_server:timetrap_cancel(Dog), ok. self_send(doc) -> ["Test that traces are generated for messages sent ", "and received to/from self()."]; self_send(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), ?line Fun = fun(Self, Parent) -> receive go_ahead -> @@ -150,28 +127,21 @@ self_send(Config) when is_list(Config) -> end, ?line receive {trace,SelfSender,send,done,Self} -> ok end, ?line receive done -> ok end, - - ?line test_server:timetrap_cancel(Dog), ok. %% Test that we can receive timeout traces. timeout_trace(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line Process = fun_spawn(fun process/0), ?line 1 = erlang:trace(Process, true, ['receive']), ?line Process ! timeout_please, ?line {trace, Process, 'receive', timeout_please} = receive_first(), ?line {trace, Process, 'receive', timeout} = receive_first(), ?line receive_nothing(), - - ?line test_server:timetrap_cancel(Dog), ok. %% Tests that trace(Pid, How, [send]) works. send_trace(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), ?line process_flag(trap_exit, true), ?line Sender = fun_spawn(fun sender/0), ?line Receiver = fun_spawn(fun receiver/0), @@ -222,14 +192,10 @@ send_trace(Config) when is_list(Config) -> ?line Sender ! {send_please, self(), to_myself_again}, ?line receive to_myself_again -> ok end, ?line receive_nothing(), - - %% Done. - ?line test_server:timetrap_cancel(Dog), ok. %% Test trace(Pid, How, [procs]). procs_trace(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), ?line Name = list_to_atom(atom_to_list(?MODULE)++"_procs_trace"), ?line Self = self(), ?line process_flag(trap_exit, true), @@ -310,14 +276,11 @@ procs_trace(Config) when is_list(Config) -> ?line Proc2 ! {exit_please, Reason2}, ?line {trace, Proc2, exit, Reason2} = receive_first(), ?line receive_nothing(), - %% - %% Done. - ?line test_server:timetrap_cancel(Dog), ok. dist_procs_trace(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(15)), + ct:timetrap({seconds, 15}), ?line OtherName = atom_to_list(?MODULE)++"_dist_procs_trace", ?line {ok, OtherNode} = start_node(OtherName), ?line Self = self(), @@ -379,7 +342,6 @@ dist_procs_trace(Config) when is_list(Config) -> %% %% Done. ?line true = stop_node(OtherNode), - ?line test_server:timetrap_cancel(Dog), ok. @@ -388,7 +350,6 @@ dist_procs_trace(Config) when is_list(Config) -> %% Tests trace(Pid, How, [set_on_spawn]). set_on_spawn(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), ?line Listener = fun_spawn(fun process/0), %% Create and trace a process with the set_on_spawn flag. @@ -407,15 +368,12 @@ set_on_spawn(Config) when is_list(Config) -> [Child11, Child12] = spawn_children(Child1, 2), ?line true = is_send_traced(Child11, Listener, child11), ?line true = is_send_traced(Child12, Listener, child12), - - %% Done. - ?line test_server:timetrap_cancel(Dog), ok. %% Tests trace(Pid, How, [set_on_first_spawn]). set_on_first_spawn(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), + ct:timetrap({seconds, 10}), ?line Listener = fun_spawn(fun process/0), %% Create and trace a process with the set_on_first_spawn flag. @@ -431,16 +389,12 @@ set_on_first_spawn(Config) when is_list(Config) -> ?line false = is_send_traced(Child2, Listener, child2), ?line false = is_send_traced(Child3, Listener, child3), ?line receive_nothing(), - - %% Done. - ?line test_server:timetrap_cancel(Dog), ok. system_monitor_args(doc) -> ["Tests arguments to erlang:system_monitor/0-2)"]; system_monitor_args(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), ?line Self = self(), %% ?line OldMonitor = erlang:system_monitor(undefined), @@ -498,24 +452,17 @@ system_monitor_args(Config) when is_list(Config) -> (catch erlang:system_monitor(Self,[{large_heap,-1}])), ?line {'EXIT',{badarg,_}} = (catch erlang:system_monitor({Self,[{large_heap,atom}]})), - %% Done. - ?line test_server:timetrap_cancel(Dog), ok. more_system_monitor_args(doc) -> ["Tests arguments to erlang:system_monitor/0-2)"]; more_system_monitor_args(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line try_l(64000), ?line try_l(16#7ffffff), ?line try_l(16#3fffffff), ?line try_l(16#7fffffff), ?line try_l(16#ffffffff), - - %% Done. - ?line test_server:timetrap_cancel(Dog), ok. try_l(Val) -> @@ -746,7 +693,7 @@ system_monitor_large_heap_2(Config) when is_list(Config) -> ?line large_heap(LoadFun, true). large_heap(LoadFun, ExpectMonMsg) -> - ?line Dog = test_server:timetrap(test_server:seconds(20)), + ct:timetrap({seconds, 20}), %% ?line Size = 65535, ?line Self = self(), @@ -766,8 +713,6 @@ large_heap(LoadFun, ExpectMonMsg) -> {undefined, true} -> ?line ?t:fail(no_system_monitor_message_received) end, - %% - ?line test_server:timetrap_cancel(Dog), ok. large_heap_check(Pid, Size, Result) -> @@ -844,14 +789,10 @@ spawn_children(Parent, Number, Result) -> suspend(doc) -> "Test erlang:suspend/1 and erlang:resume/1."; suspend(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(2)), - + ct:timetrap({minutes,2}), ?line Worker = fun_spawn(fun worker/0), %% Suspend a process and test that it is suspended. ?line ok = do_suspend(Worker, 10000), - - %% Done. - ?line test_server:timetrap_cancel(Dog), ok. do_suspend(_Pid, 0) -> @@ -880,7 +821,7 @@ mutual_suspend(suite) -> []; mutual_suspend(Config) when is_list(Config) -> ?line TimeoutSecs = 5*60, - ?line Dog = test_server:timetrap(test_server:minutes(TimeoutSecs)), + ct:timetrap({seconds, TimeoutSecs}), ?line Parent = self(), ?line Fun = fun () -> receive @@ -913,8 +854,6 @@ mutual_suspend(Config) when is_list(Config) -> ?line unlink(P2), exit(P2, bang), ?line done = Res1, ?line done = Res2, - %% Done. - ?line test_server:timetrap_cancel(Dog), ?line ok. do_mutual_suspend(_Pid, 0) -> @@ -932,10 +871,9 @@ suspend_exit(doc) -> suspend_exit(suite) -> []; suspend_exit(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(2)), + ct:timetrap({minutes, 2}), rand:seed(exsplus, {4711,17,4711}), ?line do_suspend_exit(5000), - ?line test_server:timetrap_cancel(Dog), ?line ok. do_suspend_exit(0) -> @@ -990,7 +928,7 @@ suspender_exit(doc) -> suspender_exit(suite) -> []; suspender_exit(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(3)), + ct:timetrap({minutes, 3}), ?line P1 = spawn_link(fun () -> receive after infinity -> ok end end), ?line {'EXIT', _} = (catch erlang:resume_process(P1)), ?line {P2, M2} = spawn_monitor( @@ -1093,7 +1031,6 @@ suspender_exit(Config) when is_list(Config) -> end, ?line unlink(P1), ?line exit(P1, bong), - ?line test_server:timetrap_cancel(Dog), ?line ok. suspend_system_limit(doc) -> @@ -1103,12 +1040,11 @@ suspend_system_limit(suite) -> suspend_system_limit(Config) when is_list(Config) -> case os:getenv("ERL_EXTREME_TESTING") of "true" -> - ?line Dog = test_server:timetrap(test_server:minutes(3*60)), + ct:timetrap({minutes, 3*60}), ?line P = spawn_link(fun () -> receive after infinity -> ok end end), ?line suspend_until_system_limit(P), ?line unlink(P), ?line exit(P, bye), - ?line test_server:timetrap_cancel(Dog), ?line ok; _ -> {skip, "Takes too long time for normal testing"} @@ -1163,7 +1099,7 @@ suspend_opts(doc) -> suspend_opts(suite) -> []; suspend_opts(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(3)), + ct:timetrap({minutes, 3}), ?line Self = self(), ?line wait_for_empty_runq(10), ?line Tok = spawn_link(fun () -> @@ -1275,7 +1211,6 @@ suspend_opts(Config) when is_list(Config) -> end, ?line unlink(Tok), ?line exit(Tok, bang), - ?line test_server:timetrap_cancel(Dog), ?line ok. suspend_count(Suspendee) -> @@ -1312,24 +1247,16 @@ repeat_acc(Fun, N, M, Acc) -> suspend_waiting(doc) -> "Test that a waiting process can be suspended."; suspend_waiting(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line Process = fun_spawn(fun process/0), ?line receive after 1 -> ok end, ?line true = erlang:suspend_process(Process), ?line {status, suspended} = process_info(Process, status), - - %% Done. - ?line test_server:timetrap_cancel(Dog), ok. - new_clear(doc) -> "Test that erlang:trace(new, true, ...) is cleared when tracer dies."; new_clear(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line Tracer = spawn(fun receiver/0), ?line 0 = erlang:trace(new, true, [send, {tracer, Tracer}]), ?line {flags, [send]} = erlang:trace_info(new, flags), @@ -1341,10 +1268,6 @@ new_clear(Config) when is_list(Config) -> end, ?line {flags, []} = erlang:trace_info(new, flags), ?line {tracer, []} = erlang:trace_info(new, tracer), - - %% Done. - ?line test_server:timetrap_cancel(Dog), - ok. @@ -1352,7 +1275,6 @@ new_clear(Config) when is_list(Config) -> existing_clear(doc) -> "Test that erlang:trace(all, false, ...) works without tracer."; existing_clear(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), ?line Self = self(), ?line Tracer = fun_spawn(fun receiver/0), @@ -1365,9 +1287,6 @@ existing_clear(Config) when is_list(Config) -> ?line {flags, []} = erlang:trace_info(Self, flags), ?line {tracer, []} = erlang:trace_info(Self, tracer), ?line M = N + 1, % Since trace could not be enabled on the tracer. - - %% Done. - ?line test_server:timetrap_cancel(Dog), ok. bad_flag(doc) -> "Test that an invalid flag cause badarg"; @@ -1382,7 +1301,7 @@ bad_flag(Config) when is_list(Config) -> trace_delivered(doc) -> "Test erlang:trace_delivered/1"; trace_delivered(suite) -> []; trace_delivered(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), + ct:timetrap({minutes, 1}), ?line TokLoops = 10000, ?line Go = make_ref(), ?line Parent = self(), @@ -1401,7 +1320,6 @@ trace_delivered(Config) when is_list(Config) -> Msg -> ?line ?t:fail({unexpected_message, Msg}) after 1000 -> - ?line test_server:timetrap_cancel(Dog), ?line ok end. diff --git a/erts/emulator/test/trace_call_count_SUITE.erl b/erts/emulator/test/trace_call_count_SUITE.erl index e358791f1f..251836831e 100644 --- a/erts/emulator/test/trace_call_count_SUITE.erl +++ b/erts/emulator/test/trace_call_count_SUITE.erl @@ -70,18 +70,17 @@ config(priv_dir,_) -> pause_and_restart/1, combo/1]). init_per_testcase(_Case, Config) -> - ?line Dog=test_server:timetrap(test_server:seconds(30)), - [{watchdog, Dog}|Config]. + Config. -end_per_testcase(_Case, Config) -> +end_per_testcase(_Case, _Config) -> erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]), erlang:trace_pattern(on_load, false, [local,meta,call_count]), erlang:trace(all, false, [all]), - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 4}}]. all() -> case test_server:is_native(trace_call_count_SUITE) of diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index a802aa12b8..59a819fe2a 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -61,29 +61,27 @@ -include_lib("common_test/include/ct.hrl"). %% When run in test server. --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2, not_run/1]). -export([basic/1, on_and_off/1, info/1, pause_and_restart/1, scheduling/1, called_function/1, combo/1, bif/1, nif/1]). init_per_testcase(_Case, Config) -> - ?line Dog=test_server:timetrap(test_server:seconds(400)), erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_time,call_count]), erlang:trace_pattern(on_load, false, [local,meta,call_time,call_count]), timer:now_diff(now(),now()), - [{watchdog, Dog}|Config]. + Config. -end_per_testcase(_Case, Config) -> +end_per_testcase(_Case, _Config) -> erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_time,call_count]), erlang:trace_pattern(on_load, false, [local,meta,call_time,call_count]), erlang:trace(all, false, [all]), - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 10}}]. all() -> case test_server:is_native(trace_call_time_SUITE) of @@ -93,22 +91,6 @@ all() -> combo, bif, nif, called_function, dead_tracer] end. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - not_run(Config) when is_list(Config) -> {skipped,"Native code"}. diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 503a773545..193efba99c 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -29,48 +29,25 @@ -export([exported/1, exported_wrap/1, loop/4, apply_slave_async/5, match/2, clause/2, id/1, undef/1, lists_reverse/2]). -%% -%% Define to run outside of test server -%% -%% (rotten feature) -%% -%%-define(STANDALONE,1). - + %% %% Define for debug output %% %%-define(debug,1). --ifdef(STANDALONE). --define(config(A,B),config(A,B)). --export([config/2]). --define(DEFAULT_RECEIVE_TIMEOUT, 1000). --else. -include_lib("common_test/include/ct.hrl"). -define(DEFAULT_RECEIVE_TIMEOUT, infinity). --endif. -ifdef(debug). --ifdef(STANDALONE). --define(line, erlang:display({?MODULE,?LINE}), ). --endif. -define(dbgformat(A,B),io:format(A,B)). -else. --ifdef(STANDALONE). --define(line, noop, ). --endif. -define(dbgformat(A,B),noop). -endif. --ifdef(STANDALONE). -config(priv_dir,_) -> - ".". --else. - %%% When run in test server %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, basic/1, bit_syntax/1, +-export([all/0, suite/0, + basic/1, bit_syntax/1, return/1, on_and_off/1, systematic_on_off/1, stack_grow/1,info/1, delete/1, exception/1, exception_apply/1, @@ -84,14 +61,12 @@ config(priv_dir,_) -> exception_meta_nocatch_apply_function/1, concurrency/1, init_per_testcase/2, end_per_testcase/2]). + init_per_testcase(_Case, Config) -> - ?line Dog=test_server:timetrap(test_server:minutes(2)), - [{watchdog, Dog}|Config]. + Config. end_per_testcase(_Case, Config) -> shutdown(), - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), %% Reloading the module will clear all trace patterns, and %% in a debug-compiled emulator run assertions of the counters @@ -99,9 +74,9 @@ end_per_testcase(_Case, Config) -> c:l(?MODULE). - - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 2}}]. all() -> case test_server:is_native(trace_local_SUITE) of @@ -122,21 +97,6 @@ all() -> concurrency] end. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - not_run(Config) when is_list(Config) -> {skipped,"Native code"}. @@ -258,9 +218,6 @@ exception_meta_nocatch_apply_function(doc) -> exception_meta_nocatch_apply_function(Config) when is_list(Config) -> exception_test([meta,nocatch,apply,function]). --endif. - - %%% Message patterns and expect functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl index 8f732f0a5f..eababe02fe 100644 --- a/erts/emulator/test/trace_meta_SUITE.erl +++ b/erts/emulator/test/trace_meta_SUITE.erl @@ -66,22 +66,21 @@ config(priv_dir,_) -> ".". -else. %% When run in test server. --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, +-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2, not_run/1]). -export([basic/1, return/1, on_and_off/1, stack_grow/1, info/1, tracer/1, combo/1, nosilent/1]). init_per_testcase(_Case, Config) -> - Dog=test_server:timetrap(test_server:minutes(5)), - [{watchdog, Dog}|Config]. + Config. end_per_testcase(_Case, Config) -> shutdown(), - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 5}}]. all() -> case test_server:is_native(trace_meta_SUITE) of @@ -91,21 +90,6 @@ case test_server:is_native(trace_meta_SUITE) of combo, nosilent] end. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - not_run(Config) when is_list(Config) -> {skipped,"Native code"}. diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index b169a264be..e4c58860ad 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -21,9 +21,7 @@ -module(trace_port_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, call_trace/1, return_trace/1, send/1, @@ -40,7 +38,11 @@ -include_lib("common_test/include/ct.hrl"). -test_cases() -> +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 30}}]. + +all() -> [call_trace, return_trace, send, receive_trace, process_events, schedule, fake_schedule, fake_schedule_after_register, @@ -48,35 +50,6 @@ test_cases() -> fake_schedule_after_getting_unlinked, gc, default_tracer, tracer_port_crash]. -suite() -> [{ct_hooks,[ts_install_cth]}]. - -all() -> - test_cases(). - -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog = ?t:timetrap(?t:seconds(30)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - call_trace(doc) -> "Test sending call trace messages to a port."; call_trace(Config) when is_list(Config) -> case test_server:is_native(?MODULE) orelse diff --git a/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl index bbbcf3fa2a..ac69e283ae 100644 --- a/erts/emulator/test/unique_SUITE.erl +++ b/erts/emulator/test/unique_SUITE.erl @@ -20,9 +20,7 @@ -module(unique_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2]). +-export([all/0, suite/0, init_per_suite/1, end_per_suite/1]). -export([unique_monotonic_integer_white_box/1, unique_integer_white_box/1]). @@ -33,25 +31,14 @@ -define(PRINT(V), print_ret_val(?FILE, ?LINE, V)). - -init_per_testcase(Case, Config) -> - ?line Dog=test_server:timetrap(test_server:minutes(2)), - [{watchdog, Dog}, {testcase, Case}|Config]. - -end_per_testcase(_, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 4}}]. all() -> [unique_monotonic_integer_white_box, unique_integer_white_box]. -groups() -> - []. - init_per_suite(Config) -> erts_debug:set_internal_state(available_internal_state, true), Config. @@ -60,12 +47,6 @@ end_per_suite(_Config) -> erts_debug:set_internal_state(available_internal_state, false), ok. -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - %% %% %% Unique counter white box test case diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index 0037a9a477..3e32f56aeb 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -32,48 +32,20 @@ -include_lib("common_test/include/ct.hrl"). -%-compile(export_all). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, init_per_testcase/2, - end_per_testcase/2]). +-export([all/0, suite/0]). -export([schedulers_alive/1, node_container_refc_check/1, long_timers/1, pollset_size/1, check_io_debug/1, get_check_io_info/0]). --define(DEFAULT_TIMEOUT, ?t:minutes(5)). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 5}}]. all() -> [schedulers_alive, node_container_refc_check, long_timers, pollset_size, check_io_debug]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(_Case, Config) when is_list(Config) -> - Dog = ?t:timetrap(?DEFAULT_TIMEOUT), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) when is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. - %%% %%% The test cases ------------------------------------------------------------- %%% -- cgit v1.2.3 From 9b6b82abfaae479849902ba60c4c54a526840a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 8 Mar 2016 21:01:40 +0100 Subject: Eliminate use of test_server:fail/0,1 --- erts/emulator/test/after_SUITE.erl | 8 +-- erts/emulator/test/alloc_SUITE.erl | 8 +-- erts/emulator/test/beam_SUITE.erl | 9 ++- erts/emulator/test/bif_SUITE.erl | 44 ++++++------ erts/emulator/test/big_SUITE.erl | 5 +- erts/emulator/test/binary_SUITE.erl | 18 +++-- erts/emulator/test/bs_construct_SUITE.erl | 13 ++-- erts/emulator/test/bs_match_int_SUITE.erl | 2 +- erts/emulator/test/bs_match_misc_SUITE.erl | 7 +- erts/emulator/test/bs_utf_SUITE.erl | 22 +++--- erts/emulator/test/busy_port_SUITE.erl | 27 ++++---- erts/emulator/test/call_trace_SUITE.erl | 41 +++++------ erts/emulator/test/code_SUITE.erl | 8 +-- erts/emulator/test/ddll_SUITE.erl | 15 ++-- erts/emulator/test/distribution_SUITE.erl | 26 +++---- erts/emulator/test/driver_SUITE.erl | 104 +++++++++++++--------------- erts/emulator/test/efile_SUITE.erl | 2 +- erts/emulator/test/erl_drv_thread_SUITE.erl | 9 ++- erts/emulator/test/erl_link_SUITE.erl | 10 +-- erts/emulator/test/evil_SUITE.erl | 2 +- erts/emulator/test/exception_SUITE.erl | 8 +-- erts/emulator/test/float_SUITE.erl | 2 +- erts/emulator/test/fun_SUITE.erl | 47 ++++++------- erts/emulator/test/fun_r13_SUITE.erl | 2 +- erts/emulator/test/guard_SUITE.erl | 19 ++--- erts/emulator/test/hibernate_SUITE.erl | 19 +++-- erts/emulator/test/list_bif_SUITE.erl | 22 ++---- erts/emulator/test/match_spec_SUITE.erl | 7 +- erts/emulator/test/monitor_SUITE.erl | 36 +++++----- erts/emulator/test/nested_SUITE.erl | 2 +- erts/emulator/test/nif_SUITE.erl | 32 ++++----- erts/emulator/test/node_container_SUITE.erl | 6 +- erts/emulator/test/old_scheduler_SUITE.erl | 16 ++--- erts/emulator/test/op_SUITE.erl | 2 +- erts/emulator/test/port_SUITE.erl | 103 +++++++++++++-------------- erts/emulator/test/port_bif_SUITE.erl | 39 +++++------ erts/emulator/test/process_SUITE.erl | 48 ++++++------- erts/emulator/test/receive_SUITE.erl | 3 +- erts/emulator/test/ref_SUITE.erl | 2 +- erts/emulator/test/save_calls_SUITE.erl | 2 +- erts/emulator/test/scheduler_SUITE.erl | 6 +- erts/emulator/test/send_term_SUITE.erl | 5 +- erts/emulator/test/sensitive_SUITE.erl | 2 +- erts/emulator/test/signal_SUITE.erl | 4 +- erts/emulator/test/smoke_test_SUITE.erl | 4 +- erts/emulator/test/statistics_SUITE.erl | 6 +- erts/emulator/test/system_profile_SUITE.erl | 8 +-- erts/emulator/test/time_SUITE.erl | 26 ++++--- erts/emulator/test/timer_bif_SUITE.erl | 8 +-- erts/emulator/test/trace_SUITE.erl | 24 +++---- erts/emulator/test/trace_bif_SUITE.erl | 70 ++++++++----------- erts/emulator/test/trace_local_SUITE.erl | 8 +-- erts/emulator/test/trace_meta_SUITE.erl | 8 +-- erts/emulator/test/trace_nif_SUITE.erl | 57 ++++++--------- erts/emulator/test/trace_port_SUITE.erl | 24 +++---- erts/emulator/test/tuple_SUITE.erl | 6 +- erts/emulator/test/unique_SUITE.erl | 2 +- erts/emulator/test/z_SUITE.erl | 10 +-- 58 files changed, 499 insertions(+), 576 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl index 71edaa25c3..40fd2e6500 100644 --- a/erts/emulator/test/after_SUITE.erl +++ b/erts/emulator/test/after_SUITE.erl @@ -57,9 +57,9 @@ t_after(Config) when is_list(Config) -> report(Period, Before, After) -> case erlang:convert_time_unit(After - Before, native, 100*1000) / Period of Percent when Percent > 100.10 -> - test_server:fail({too_inaccurate, Percent}); + ct:fail({too_inaccurate, Percent}); Percent when Percent < 100.0 -> - test_server:fail({too_early, Percent}); + ct:fail({too_early, Percent}); Percent -> Comment = io_lib:format("Elapsed/expected: ~.2f %", [Percent]), {comment, lists:flatten(Comment)} @@ -153,7 +153,7 @@ receive_var_zero(Config) when is_list(Config) -> receive x -> ok; Other -> - ?line ?t:fail({bad_message,Other}) + ct:fail({bad_message,Other}) end. zero() -> 0. @@ -174,7 +174,7 @@ receive_zero(Config) when is_list(Config) -> receive x -> ok; Other -> - ?line ?t:fail({bad_message,Other}) + ct:fail({bad_message,Other}) end. multi_timeout(doc) -> diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 75ae687ffc..6ee2f98884 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -325,11 +325,11 @@ handle_result(_State, Result0) -> flush_log(), case Result0 of {'EXIT', Error} -> - ?line ?t:fail(Error); + ct:fail(Error); {'EXIT', error, Error} -> - ?line ?t:fail(Error); + ct:fail(Error); {failed, Comment} -> - ?line ?t:fail(Comment); + ct:fail(Comment); {skipped, Comment} -> ?line {skipped, Comment}; {succeeded, ""} -> @@ -378,6 +378,6 @@ free_memory() -> TotFree div (1024*1024) catch error : undef -> - ?t:fail({"os_mon not built"}) + ct:fail({"os_mon not built"}) end. diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl index 499b05d658..d3180a40cf 100644 --- a/erts/emulator/test/beam_SUITE.erl +++ b/erts/emulator/test/beam_SUITE.erl @@ -61,7 +61,7 @@ apply_last(Config) when is_list(Config) -> {Pid, finished} -> stack_size(Pid) after 30000 -> - ?t:fail("applied/2 timed out.") + ct:fail("applied/2 timed out.") end, Pid ! die, ?t:format("Size: ~p~n", [Size]), @@ -69,7 +69,7 @@ apply_last(Config) when is_list(Config) -> Size < 700 -> ok; true -> - ?t:fail("10000 apply() grew stack too much.") + ct:fail("10000 apply() grew stack too much.") end, ok. @@ -132,8 +132,7 @@ verify_packed_regs([], _, -1) -> ok; verify_packed_regs([{Term, N}| T], Term, N) -> verify_packed_regs(T, Term, N-1); verify_packed_regs(L, Term, N) -> - ok = io:format("Expected [{~p, ~p}|T]; got\n~p\n", [Term, N, L]), - test_server:fail(). + ct:fail("Expected [{~p, ~p}|T]; got\n~p\n", [Term, N, L]). buildo_mucho(Config) when is_list(Config) -> buildo_mucho_1(), @@ -319,7 +318,7 @@ fconv(Config) when is_list(Config) -> do_fconv(Type) -> try do_fconv(Type, 1.0), - test_server:fail() + ct:fail(no_badarith) catch error:badarith -> ok diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index ccde904fbc..ec8c7155b5 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -93,7 +93,7 @@ erl_bif_types_2(List) -> [_|_] -> io:put_chars("Bifs with bad arity\n"), io:format("~p\n", [BadArity]), - ?line ?t:fail({length(BadArity),bad_arity}) + ?line ct:fail({length(BadArity),bad_arity}) end. erl_bif_types_3(List) -> @@ -117,7 +117,7 @@ erl_bif_types_3(List) -> io:put_chars("Bifs with failing calls to erlang_bif_types:type/3 " "(or with bogus return values):\n"), io:format("~p\n", [BadSmokeTest]), - ?line ?t:fail({length(BadSmokeTest),bad_smoke_test}) + ?line ct:fail({length(BadSmokeTest),bad_smoke_test}) end. guard_bifs_in_erl_bif_types(_Config) -> @@ -138,7 +138,7 @@ guard_bifs_in_erl_bif_types(_Config) -> "The following guard BIFs have no type information " "in erl_bif_types:\n\n", [io_lib:format(" ~p/~p\n", [F,A]) || {F,A} <- Not]]), - ?t:fail() + ct:fail(erl_bif_types) end. shadow_comments(_Config) -> @@ -178,7 +178,7 @@ shadow_comments(_Config) -> "obvious.\n\nThe following comments are missing:\n\n", [io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n", [M,F,A]) || {M,F,A} <- NoComments]]), - ?t:fail() + ct:fail(bif_stub) end, case NoBifSpecs of @@ -192,7 +192,7 @@ shadow_comments(_Config) -> "Therefore, the following comments should be removed:\n\n", [io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n", [M,F,A]) || {M,F,A} <- NoBifSpecs]]), - ?t:fail() + ct:fail(erl_bif_types) end. extract_comments(Mod, Path) -> @@ -214,7 +214,7 @@ ensure_erl_bif_types_compiled() -> case erlang:function_exported(erl_bif_types, module_info, 0) of false -> %% Fail cleanly. - ?t:fail("erl_bif_types not compiled"); + ct:fail("erl_bif_types not compiled"); true -> ok end. @@ -252,7 +252,7 @@ specs(_) -> [_|_] -> io:put_chars("The following BIFs don't have specs:\n"), [print_mfa(MFA) || MFA <- NoSpecs], - ?t:fail() + ct:fail(no_spec) end. is_operator({erlang,F,A}) -> @@ -312,7 +312,7 @@ auto_imports([{erlang,F,A}|T], Errors) -> auto_imports([], 0) -> ok; auto_imports([], Errors) -> - ?t:fail({Errors,inconsistencies}). + ct:fail({Errors,inconsistencies}). extract_functions(M, Abstr) -> [{{M,F,A},Body} || {function,_,F,A,Body} <- Abstr]. @@ -331,7 +331,7 @@ check_stub({_,F,A}, B) -> io:put_chars(erl_pp:function(Func)), io:nl(), io:put_chars("The body should be: erlang:nif_error(undef)"), - ?t:fail() + ct:fail(invalid_body) end. t_list_to_existing_atom(Config) when is_list(Config) -> @@ -340,7 +340,7 @@ t_list_to_existing_atom(Config) when is_list(Config) -> ?line UnlikelyStr = "dsfj923874390867er869fds9864y97jhg3973qerueoru", try ?line list_to_existing_atom(UnlikelyStr), - ?line ?t:fail() + ?line ct:fail(atom_exists) catch error:badarg -> ok end, @@ -364,7 +364,7 @@ os_env(Config) when is_list(Config) -> ?line case os:getenv(EnvVar1) of "" -> ?line ok; false -> ?line ok; - BadVal -> ?line ?t:fail(BadVal) + BadVal -> ?line ct:fail(BadVal) end, true = os:putenv(EnvVar1, "mors"), true = os:unsetenv(EnvVar1), @@ -523,14 +523,14 @@ binary_to_existing_atom(Config) when is_list(Config) -> ?line UnlikelyBin = <<"ou0897979655678dsfj923874390867er869fds973qerueoru">>, try ?line binary_to_existing_atom(UnlikelyBin, latin1), - ?line ?t:fail() + ?line ct:fail(atom_exists) catch error:badarg -> ok end, try ?line binary_to_existing_atom(UnlikelyBin, utf8), - ?line ?t:fail() + ?line ct:fail(atom_exists) catch error:badarg -> ok end, @@ -626,31 +626,31 @@ min_max(Config) when is_list(Config) -> erlang_halt(Config) when is_list(Config) -> try erlang:halt(undefined) of - _-> ?t:fail({erlang,halt,{undefined}}) + _-> ct:fail({erlang,halt,{undefined}}) catch error:badarg -> ok end, try halt(undefined) of - _-> ?t:fail({halt,{undefined}}) + _-> ct:fail({halt,{undefined}}) catch error:badarg -> ok end, try erlang:halt(undefined, []) of - _-> ?t:fail({erlang,halt,{undefined,[]}}) + _-> ct:fail({erlang,halt,{undefined,[]}}) catch error:badarg -> ok end, try halt(undefined, []) of - _-> ?t:fail({halt,{undefined,[]}}) + _-> ct:fail({halt,{undefined,[]}}) catch error:badarg -> ok end, try halt(0, undefined) of - _-> ?t:fail({halt,{0,undefined}}) + _-> ct:fail({halt,{0,undefined}}) catch error:badarg -> ok end, try halt(0, [undefined]) of - _-> ?t:fail({halt,{0,[undefined]}}) + _-> ct:fail({halt,{0,[undefined]}}) catch error:badarg -> ok end, try halt(0, [{undefined,true}]) of - _-> ?t:fail({halt,{0,[{undefined,true}]}}) + _-> ct:fail({halt,{0,[{undefined,true}]}}) catch error:badarg -> ok end, try halt(0, [{flush,undefined}]) of - _-> ?t:fail({halt,{0,[{flush,undefined}]}}) + _-> ct:fail({halt,{0,[{flush,undefined}]}}) catch error:badarg -> ok end, try halt(0, [{flush,true,undefined}]) of - _-> ?t:fail({halt,{0,[{flush,true,undefined}]}}) + _-> ct:fail({halt,{0,[{flush,true,undefined}]}}) catch error:badarg -> ok end, H = hostname(), {ok,N1} = slave:start(H, halt_node1), diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl index 9b611a136c..37e90df60e 100644 --- a/erts/emulator/test/big_SUITE.erl +++ b/erts/emulator/test/big_SUITE.erl @@ -104,7 +104,7 @@ test(File, Nodes) -> file:close(Fd), case Res of {0,Cases} -> {comment, integer_to_list(Cases) ++ " cases"}; - {_,_} -> test_server:fail() + {_,_} -> ct:fail("failed") end. test(File, Fd, Ns) -> @@ -245,8 +245,7 @@ t_div(Config) when is_list(Config) -> {result, Result} -> 'try'(Iter-1, Fun, Result, [0|Filler]); {result, Other} -> - io:format("Expected ~p; got ~p~n", [Result, Other]), - test_server:fail() + ct:fail("Expected ~p; got ~p~n", [Result, Other]) end. init(ReplyTo, Fun, _Filler) -> diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index c5e3226a13..6ace683fe7 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -113,7 +113,7 @@ copy_terms(Config) when is_list(Config) -> Term -> ok; Other -> io:format("Sent: ~P\nGot back:~P", [Term,12,Other,12]), - ?t:fail(bad_term) + ct:fail(bad_term) end end, ?line test_terms(F), @@ -236,7 +236,7 @@ going_center(_List, _Bin, _From, _To) -> compare([X|Rest1], [X|Rest2], Left) when Left > 0 -> ?line compare(Rest1, Rest2, Left-1); compare([_X|_], [_Y|_], _Left) -> - ?line test_server:fail(); + ct:fail("compare fail"); compare(_List, [], 0) -> ok. @@ -405,8 +405,7 @@ test_hash_1(Bin, Sbin, Unaligned, Hash) when is_function(Hash, 2) -> case {Hash(Bin, N),Hash(Sbin, N),Hash(Unaligned, N)} of {H,H,H} -> ok; {H1,H2,H3} -> - io:format("Different hash values: ~p, ~p, ~p\n", [H1,H2,H3]), - ?t:fail() + ct:fail("Different hash values: ~p, ~p, ~p\n", [H1,H2,H3]) end. bad_size(doc) -> "Try bad arguments to size/1."; @@ -505,9 +504,8 @@ external_size(Config) when is_list(Config) -> case {erlang:external_size(Bin),erlang:external_size(Unaligned)} of {X,X} -> ok; {Sz1,Sz2} -> - io:format(" Aligned size: ~p\n", [Sz1]), - io:format("Unaligned size: ~p\n", [Sz2]), - ?line ?t:fail() + ct:fail(" Aligned size: ~p\n" + "Unaligned size: ~p\n", [Sz1,Sz2]) end, true = (erlang:external_size(Bin) =:= erlang:external_size(Bin, [{minor_version, 1}])), true = (erlang:external_size(Unaligned) =:= erlang:external_size(Unaligned, [{minor_version, 1}])). @@ -591,7 +589,7 @@ bad_binary_to_term_2(Config) when is_list(Config) -> {badrpc, {'EXIT', _}} -> ok; _Other -> - test_server:fail({rpcresult, R}) + ct:fail({rpcresult, R}) end, ?line test_server:stop_node(N), ok. @@ -707,7 +705,7 @@ more_bad_terms(Config) when is_list(Config) -> ?line {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)), ok; Other -> - ?line ?t:fail(Other) + ?line ct:fail(Other) end. otp_5484(Config) when is_list(Config) -> @@ -1217,7 +1215,7 @@ gc_test1(Pid) -> receive {Pid,done} -> ok after 10000 -> - ?line ?t:fail() + ct:fail("timeout") end. %% Like split binary, but returns REFC binaries. Only useful for gc_test/1. diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 2d93bace15..42a710aaf9 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -175,7 +175,7 @@ one_test({C_bin, E_bin, Str, Bytes}) when is_list(Bytes) -> true -> io:format("ERROR: Compiled: ~p. Expected ~p. Got ~p.~n", [Str, Bytes, binary_to_list(C_bin)]), - test_server:fail(comp) + ct:fail(comp) end, if E_bin == Bin -> @@ -183,7 +183,7 @@ one_test({C_bin, E_bin, Str, Bytes}) when is_list(Bytes) -> true -> io:format("ERROR: Interpreted: ~p. Expected ~p. Got ~p.~n", [Str, Bytes, binary_to_list(E_bin)]), - test_server:fail(comp) + ct:fail(comp) end; one_test({C_bin, E_bin, Str, Result}) -> io:format(" ~s ~p~n", [Str, C_bin]), @@ -204,7 +204,7 @@ one_test({C_bin, E_bin, Str, Result}) -> io:format("ERROR: Compiled not equal to interpreted:" "~n ~p, ~p.~n", [binary_to_list(C_bin), binary_to_list(E_bin)]), - test_server:fail(comp); + ct:fail(comp); 0 -> ok; %% For situations where the final bits may not matter, like @@ -239,14 +239,14 @@ fail_check({'EXIT',{badarg,_}}, Str, Vars) -> try evaluate(Str, Vars) of Res -> io:format("Interpreted result: ~p", [Res]), - ?t:fail(did_not_fail_in_intepreted_code) + ct:fail(did_not_fail_in_intepreted_code) catch error:badarg -> ok end; fail_check(Res, _, _) -> io:format("Compiled result: ~p", [Res]), - ?t:fail(did_not_fail_in_compiled_code). + ct:fail(did_not_fail_in_compiled_code). %%% Simple working cases test1(suite) -> []; @@ -666,8 +666,7 @@ copy_writable_binary_1(_) -> receive {Pid,Bin0,Bin0} -> ok; Other -> - io:format("Unexpected message: ~p", [Other]), - ?line ?t:fail() + ct:fail("Unexpected message: ~p", [Other]) end, ok. diff --git a/erts/emulator/test/bs_match_int_SUITE.erl b/erts/emulator/test/bs_match_int_SUITE.erl index 48c2b4644e..6f8a5b1916 100644 --- a/erts/emulator/test/bs_match_int_SUITE.erl +++ b/erts/emulator/test/bs_match_int_SUITE.erl @@ -151,7 +151,7 @@ more_dynamic(Config) when is_list(Config) -> io:format("Bin = ~p,", [Bin]), io:format("SkipBef = ~p, N = ~p", [SkipBef,N]), io:format("Expected ~p, got ~p", [Int,Other]), - ?t:fail() + ct:fail(signed_big_endian_fail) end end, ?line more_dynamic1(Signed, erlang:md5(mkbin([43]))), diff --git a/erts/emulator/test/bs_match_misc_SUITE.erl b/erts/emulator/test/bs_match_misc_SUITE.erl index 967325842c..8979a69322 100644 --- a/erts/emulator/test/bs_match_misc_SUITE.erl +++ b/erts/emulator/test/bs_match_misc_SUITE.erl @@ -383,9 +383,9 @@ x0_2(_, Bin) -> x0_3(_, Bin) -> case Bin of <<_:72,7:8,_/binary>> -> - ?line ?t:fail(); + ct:fail(bs_matched_1); <<_:64,0:16,_/binary>> -> - ?line ?t:fail(); + ct:fail(bs_matched_2); <<_:64,42:16,123456:32,_/binary>> -> ok end. @@ -498,8 +498,7 @@ do_otp_7198(FillerSize) -> {'DOWN',Ref,process,Pid,normal} -> ok; {'DOWN',Ref,process,Pid,Reason} -> - io:format("unexpected: ~p", [Reason]), - ?line ?t:fail() + ct:fail("unexpected: ~p", [Reason]) end. do_otp_7198_test(_) -> diff --git a/erts/emulator/test/bs_utf_SUITE.erl b/erts/emulator/test/bs_utf_SUITE.erl index f03115c3c8..5447944306 100644 --- a/erts/emulator/test/bs_utf_SUITE.erl +++ b/erts/emulator/test/bs_utf_SUITE.erl @@ -204,9 +204,9 @@ overlong(_, _, _) -> ok. overlong(Char, NumBytes) when NumBytes < 5 -> case int_to_utf8(Char, NumBytes) of <>=Bin -> - ?t:fail({illegal_encoding_accepted,Bin,Char}); + ct:fail({illegal_encoding_accepted,Bin,Char}); <>=Bin -> - ?t:fail({illegal_encoding_accepted,Bin,Char,OtherChar}); + ct:fail({illegal_encoding_accepted,Bin,Char,OtherChar}); _ -> ok end, overlong(Char, NumBytes+1); @@ -217,7 +217,7 @@ fail(Bin) -> fail_1(make_unaligned(Bin)). fail_1(<>=Bin) -> - ?t:fail({illegal_encoding_accepted,Bin,Char}); + ct:fail({illegal_encoding_accepted,Bin,Char}); fail_1(_) -> ok. @@ -241,9 +241,9 @@ lonely_hi_surrogate(Char, End) when Char =< End -> BinLittle = <>, case {BinBig,BinLittle} of {<>,_} -> - ?t:fail({lonely_hi_surrogate_accepted,Bad}); + ct:fail({lonely_hi_surrogate_accepted,Bad}); {_,<>} -> - ?t:fail({lonely_hi_surrogate_accepted,Bad}); + ct:fail({lonely_hi_surrogate_accepted,Bad}); {_,_} -> ok end, @@ -260,9 +260,9 @@ leading_lo_surrogate(HiSurr, LoSurr, End) when LoSurr =< End -> BinLittle = <>, case {BinBig,BinLittle} of {<>,_} -> - ?t:fail({leading_lo_surrogate_accepted,Bad}); + ct:fail({leading_lo_surrogate_accepted,Bad}); {_,<>} -> - ?t:fail({leading_lo_surrogate_accepted,Bad}); + ct:fail({leading_lo_surrogate_accepted,Bad}); {_,_} -> ok end, @@ -280,9 +280,9 @@ utf32_fail_range(Char, End) when Char =< End -> {'EXIT',_} = (catch <>), case {<>,<>} of {<>,_} -> - ?line ?t:fail(Unexpected); + ?line ct:fail(Unexpected); {_,<>} -> - ?line ?t:fail(Unexpected); + ?line ct:fail(Unexpected); {_,_} -> ok end, utf32_fail_range(Char+1, End); @@ -363,14 +363,14 @@ fail_check({'EXIT',{badarg,_}}, Str, Vars) -> try evaluate(Str, Vars) of Res -> io:format("Interpreted result: ~p", [Res]), - ?t:fail(did_not_fail_in_intepreted_code) + ct:fail(did_not_fail_in_intepreted_code) catch error:badarg -> ok end; fail_check(Res, _, _) -> io:format("Compiled result: ~p", [Res]), - ?t:fail(did_not_fail_in_compiled_code). + ct:fail(did_not_fail_in_compiled_code). evaluate(Str, Vars) -> {ok,Tokens,_} = diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index 9bd4f7e136..172936dd82 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -131,7 +131,7 @@ message_order(Config) when is_list(Config) -> {Busy, first} -> ok; Other -> - test_server:fail({unexpected_message, Other}) + ct:fail({unexpected_message, Other}) end, ok. @@ -233,13 +233,13 @@ no_trap_exit(Config) when is_list(Config) -> io:format("Process ~w created port ~w", [Pid, Port]), ?line exit(Port, die); Other1 -> - test_server:fail({unexpected_message, Other1}) + ct:fail({unexpected_message, Other1}) end, ?line receive {'EXIT', Pid, die} -> ok; Other2 -> - test_server:fail({unexpected_message, Other2}) + ct:fail({unexpected_message, Other2}) end, ok. @@ -257,13 +257,13 @@ no_trap_exit_unlinked(Config) when is_list(Config) -> io:format("Process ~w created port ~w", [Pid, Port]), ?line exit(Port, die); Other1 -> - test_server:fail({unexpected_message, Other1}) + ct:fail({unexpected_message, Other1}) end, ?line receive {'EXIT', Pid, normal} -> ok; Other2 -> - test_server:fail({unexpected_message, Other2}) + ct:fail({unexpected_message, Other2}) end, ok. @@ -302,13 +302,13 @@ trap_exit(Config) when is_list(Config) -> ?line {status, suspended} = process_info(Pid, status), ?line exit(Port, die); Other1 -> - test_server:fail({unexpected_message, Other1}) + ct:fail({unexpected_message, Other1}) end, ?line receive {Pid, ok} -> ok; Other2 -> - test_server:fail({unexpected_message, Other2}) + ct:fail({unexpected_message, Other2}) end, ok. @@ -379,8 +379,7 @@ hs_test(Config, HardBusy) when is_list(Config) -> case erl_ddll:load_driver(Path, DrvName) of ok -> ok; {error, Error} -> - io:format("~s\n", [erl_ddll:format_error(Error)]), - ?line ?t:fail() + ct:fail(erl_ddll:format_error(Error)) end, ?line Port = open_port({spawn, DrvName}, []), @@ -721,8 +720,7 @@ port_scheduling(Scenario,Validation,Path) -> case erl_ddll:load_driver(Path, DrvName) of ok -> ok; {error, Error} -> - io:format("~s\n", [erl_ddll:format_error(Error)]), - ?line ?t:fail() + ct:fail(erl_ddll:format_error(Error)) end, Data = run_scenario(lists:flatten(Scenario),[{drvname,DrvName}]), @@ -828,7 +826,7 @@ wait_for(Pids) -> {'EXIT', Pid, normal} -> wait_for(lists:delete(Pid, Pids)); Other -> - test_server:fail({bad_exit, Other}) + ct:fail({bad_exit, Other}) end. fun_spawn(Fun) -> @@ -869,8 +867,7 @@ load_busy_driver(Config) when is_list(Config) -> case erl_ddll:load_driver(DataDir, "busy_drv") of ok -> ok; {error, Error} -> - io:format("~s\n", [erl_ddll:format_error(Error)]), - ?line ?t:fail() + ct:fail(erl_ddll:format_error(Error)) end. %%% Interface functions. @@ -881,7 +878,7 @@ start_busy_driver(Config) when is_list(Config) -> {Pid, started} -> ok; Other -> - test_server:fail({unexpected_message, Other}) + ct:fail({unexpected_message, Other}) end. unlock_slave() -> diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index 1b67bcd83e..d99bba32cd 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -140,7 +140,7 @@ call_worker(Pid, Arg) -> receive {result,Res} -> Res after 5000 -> - ?line ?t:fail(no_answer_from_worker) + ?line ct:fail(no_answer_from_worker) end. worker_loop() -> @@ -228,7 +228,7 @@ basic() -> ?line ?MODULE:expect({trace,Self,call,{erlang,list_to_integer,["777"]}}), receive Any -> - ?line ?t:fail({unexpected_message,Any}) + ?line ct:fail({unexpected_message,Any}) after 1 -> ok end, @@ -444,9 +444,7 @@ flag_test(Test) -> ok; _Diff -> %% Too large difference. - io:format("Now = ~p\n", [Now]), - io:format("Ts = ~p\n", [Ts]), - ?line ?t:fail() + ct:fail("Now = ~p, Ts = ~p", [Now, Ts]) end, flag_test_cpu_timestamp(Test). @@ -465,8 +463,7 @@ flag_test_cpu_timestamp(Test) -> %% test that all CPU timestamps should pass. ok; _Time -> - io:format("Strange CPU timestamp: ~p", [Ts]), - ?line ?t:fail() + ct:fail("Strange CPU timestamp: ~p", [Ts]) end, io:format("Turned off CPU timestamps") catch @@ -493,7 +490,7 @@ expect_badarg_pid(What, How, Flags) -> Other -> io:format("trace(~p, ~p, ~p) -> ~p", [What,How,Flags,Other]), - ?t:fail({unexpected,Other}) + ct:fail({unexpected,Other}) end. expect_badarg_func(MFA, Pattern) -> @@ -505,7 +502,7 @@ expect_badarg_func(MFA, Pattern) -> Other -> io:format("trace_pattern(~p, ~p) -> ~p", [MFA, Pattern, Other]), - ?t:fail({unexpected,Other}) + ct:fail({unexpected,Other}) end. pam(doc) -> "Basic test of PAM."; @@ -825,7 +822,7 @@ deep_exception() -> %% ?line io:format("== Subtest: ~w", [?LINE]), ?line try lists:reverse(LongImproperList, []) of - R1 -> test_server:fail({returned,abbr(R1)}) + R1 -> ct:fail({returned,abbr(R1)}) catch error:badarg -> ok end, ?line expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) @@ -889,7 +886,7 @@ deep_exception() -> exception_from, {error,badarg}), ?line io:format("== Subtest: ~w", [?LINE]), ?line try apply(lists, reverse, [LongImproperList, []]) of - R2 -> test_server:fail({returned,abbr(R2)}) + R2 -> ct:fail({returned,abbr(R2)}) catch error:badarg -> ok end, ?line expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) @@ -968,7 +965,7 @@ deep_exception() -> exception_from, {error,{badmatch,2}}), ?line io:format("== Subtest: ~w", [?LINE]), ?line try apply(fun() -> lists:reverse(LongImproperList, []) end, []) of - R3 -> test_server:fail({returned,abbr(R3)}) + R3 -> ct:fail({returned,abbr(R3)}) catch error:badarg -> ok end, ?line expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) @@ -1113,7 +1110,7 @@ exception_nocatch() -> get_deep_4_loc(Arg) -> try deep_4(Arg), - ?t:fail(should_not_return_to_here) + ct:fail(should_not_return_to_here) catch _:_ -> [{?MODULE,deep_4,1,Loc0}|_] = erlang:get_stacktrace(), @@ -1182,7 +1179,7 @@ expect() -> case flush() of [] -> ok; Msgs -> - test_server:fail({unexpected,abbr(Msgs)}) + ct:fail({unexpected,abbr(Msgs)}) end. expect({trace_ts,Pid,Type,MFA,Term,ts}=Message) -> @@ -1194,11 +1191,11 @@ expect({trace_ts,Pid,Type,MFA,Term,ts}=Message) -> Ts; _ -> io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]), - test_server:fail({unexpected,abbr([M|flush()])}) + ct:fail({unexpected,abbr([M|flush()])}) end after 5000 -> io:format("Expected ~p; got nothing", [abbr(Message)]), - test_server:fail(no_trace_message) + ct:fail(no_trace_message) end; expect({trace_ts,Pid,Type,MFA,ts}=Message) -> receive @@ -1209,11 +1206,11 @@ expect({trace_ts,Pid,Type,MFA,ts}=Message) -> Ts; _ -> io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]), - test_server:fail({unexpected,abbr([M|flush()])}) + ct:fail({unexpected,abbr([M|flush()])}) end after 5000 -> io:format("Expected ~p; got nothing", [abbr(Message)]), - test_server:fail(no_trace_message) + ct:fail(no_trace_message) end; expect(Validator) when is_function(Validator) -> receive @@ -1226,11 +1223,11 @@ expect(Validator) when is_function(Validator) -> expect(Validator); {unexpected,Message} -> io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]), - test_server:fail({unexpected,abbr([M|flush()])}) + ct:fail({unexpected,abbr([M|flush()])}) end after 5000 -> io:format("Expected ~p; got nothing", [abbr(Validator('_'))]), - test_server:fail(no_trace_message) + ct:fail(no_trace_message) end; expect(Message) -> receive @@ -1241,11 +1238,11 @@ expect(Message) -> Other -> io:format("Expected ~p; got ~p", [abbr(Message),abbr(Other)]), - test_server:fail({unexpected,abbr([Other|flush()])}) + ct:fail({unexpected,abbr([Other|flush()])}) end after 5000 -> io:format("Expected ~p; got nothing", [abbr(Message)]), - test_server:fail(no_trace_message) + ct:fail(no_trace_message) end. trace_info(What, Key) -> diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 3d2cc000a8..a040eee50a 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -288,9 +288,9 @@ do_check_process_code_ets(Config) -> receive funs_dropped -> ok; - Other -> ?t:fail({unexpected,Other}) + Other -> ct:fail({unexpected,Other}) after 10000 -> - ?line ?t:fail(no_funs_dropped_answer) + ?line ct:fail(no_funs_dropped_answer) end, ?line false = erlang:check_process_code(Pid, my_code_test), @@ -477,7 +477,7 @@ constant_pools(Config) when is_list(Config) -> {'EXIT',NoOldHeap,{A,B,C}} -> ok; Other -> - ?line ?t:fail({unexpected,Other}) + ?line ct:fail({unexpected,Other}) end, ?line {module,literals} = erlang:load_module(literals, Code), @@ -567,7 +567,7 @@ constant_refc_binaries(Config) when is_list(Config) -> %% the size of allocated binaries. if Diff > 64*1024 -> - ?t:fail(binary_leak); + ct:fail(binary_leak); true -> ok end. diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl index 36c779c8e3..0288c9d243 100644 --- a/erts/emulator/test/ddll_SUITE.erl +++ b/erts/emulator/test/ddll_SUITE.erl @@ -846,7 +846,7 @@ reference_count(Config) when is_list(Config) -> Pid1=spawn_link(?MODULE, echo_loader, [Path, self()]), receive {Pid1, echo_loaded} -> ok - after 2000 -> test_server:fail("echo_loader failed to start.") + after 2000 -> ct:fail("echo_loader failed to start.") end, Pid1 ! {self(), die}, @@ -891,7 +891,7 @@ kill_port(Config) when is_list(Config) -> ok after 3000 -> ?line exit(Pid1, kill), - ?line test_server:fail("echo_loader failed to start.") + ?line ct:fail("echo_loader failed to start.") end, % Spawn off a port that uses the driver. @@ -908,7 +908,7 @@ kill_port(Config) when is_list(Config) -> {'EXIT', Port, Reason} -> io:format("Port exited with reason ~w", [Reason]) after 5000 -> - ?line test_server:fail("Echo port did not terminate.") + ?line ct:fail("Echo port did not terminate.") end, ok. @@ -926,7 +926,7 @@ dont_kill_port(Config) when is_list(Config) -> ok after 3000 -> ?line exit(Pid1, kill), - ?line test_server:fail("echo_loader failed to start.") + ?line ct:fail("echo_loader failed to start.") end, % Spawn off a port that uses the driver. @@ -947,7 +947,7 @@ dont_kill_port(Config) when is_list(Config) -> {'EXIT', Port, Reason} -> io:format("Port exited with reason ~w", [Reason]) after 5000 -> - ?line test_server:fail("Echo port did not terminate.") + ?line ct:fail("Echo port did not terminate.") end, ok. @@ -960,7 +960,7 @@ properties(Config) when is_list(Config) -> Pid=spawn_link(?MODULE, echo_loader, [Path, self()]), receive {Pid, echo_loaded} -> ok - after 2000 -> test_server:fail("echo_loader failed to start.") + after 2000 -> ct:fail("echo_loader failed to start.") end, % Try to unload the driver from this process (the wrong one). @@ -970,8 +970,7 @@ properties(Config) when is_list(Config) -> true -> ok; false -> - test_server:fail("Unload from wrong process " - "succeeded.") + ct:fail("Unload from wrong process succeeded.") end, % Unload the driver and terminate dummy process. diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 6165cf70bc..fd47b6b235 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -159,7 +159,7 @@ bulk_sendsend(Terms, BinSize) -> {comment,Comment}; true -> io:put_chars(Comment), - ?line ?t:fail(ratio_too_low) + ?line ct:fail(ratio_too_low) end. bulk_sendsend2(Terms, BinSize, BusyBufSize) -> @@ -294,7 +294,7 @@ local_send_legal(Config) when is_list(Config) -> {Times, TotalSize} -> ok; _ -> - test_server:fail("Wrong number of msgs received.") + ct:fail("Wrong number of msgs received.") end, ok. @@ -469,7 +469,7 @@ do_busy_test(Node, Fun) -> {'DOWN', M, process, P, Reason} -> ?t:format("~p died with exit reason ~p~n", [P, Reason]) end, - ?t:fail(premature_death); + ct:fail(premature_death); _ -> %% Don't match arity; it is different in debug and %% optimized emulator @@ -592,9 +592,9 @@ link_to_dead(Config) when is_list(Config) -> {'EXIT', Pid, noproc} -> ok; Other -> - ?line test_server:fail({unexpected_message, Other}) + ?line ct:fail({unexpected_message, Other}) after 5000 -> - ?line test_server:fail(nothing_received) + ?line ct:fail(nothing_received) end, ?line {links, Links} = process_info(self(), links), ?line io:format("Pid=~p, links=~p", [Pid, Links]), @@ -602,7 +602,7 @@ link_to_dead(Config) when is_list(Config) -> ?line stop_node(Node), ?line receive Message -> - ?line test_server:fail({unexpected_message, Message}) + ?line ct:fail({unexpected_message, Message}) after 3000 -> ok end, @@ -629,9 +629,9 @@ link_to_dead_new_node(Config) when is_list(Config) -> {'EXIT', Pid, noproc} -> ok; Other -> - ?line test_server:fail({unexpected_message, Other}) + ?line ct:fail({unexpected_message, Other}) after 5000 -> - ?line test_server:fail(nothing_received) + ?line ct:fail(nothing_received) end, %% Make sure that the link wasn't created. @@ -641,7 +641,7 @@ link_to_dead_new_node(Config) when is_list(Config) -> ?line stop_node(Node), ?line receive Message -> - ?line test_server:fail({unexpected_message, Message}) + ?line ct:fail({unexpected_message, Message}) after 3000 -> ok end, @@ -688,9 +688,9 @@ ref_port_roundtrip(Config) when is_list(Config) -> ok; Other -> ?line io:format("Term after: ~p", [show_term(Term)]), - ?line test_server:fail({unexpected, Other}) + ?line ct:fail({unexpected, Other}) after 10000 -> - ?line test_server:fail(timeout) + ?line ct:fail(timeout) end, ok. @@ -2116,13 +2116,13 @@ verify_still_up(A, B) -> verify_no_down(A, B) -> receive {nodedown, A, B, _} = Msg0 -> - ?t:fail(Msg0) + ct:fail(Msg0) after 0 -> ok end, receive {nodedown, B, A, _} = Msg1 -> - ?t:fail(Msg1) + ct:fail(Msg1) after 0 -> ok end. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 23c5114d40..b00ceaed50 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -357,17 +357,16 @@ ov_send_and_test(Port, Data, ExpectedResult) -> io:format("~p returned ~P", [Port,ReturnData,12]), compare(ReturnData, ExpectedResult); {Port,{data,OtherData}} -> - io:format("~p returned WRONG data ~p", [Port,OtherData]), - ?line test_server:fail(); + ct:fail("~p returned WRONG data ~p", [Port,OtherData]); Wrong -> - ?line test_server:fail({unexpected_port_or_data,Wrong}) + ct:fail({unexpected_port_or_data,Wrong}) end. compare(Got, Expected) -> case {list_to_binary([Got]),list_to_binary([Expected])} of {B,B} -> ok; {_Gb,_Eb} -> - ?t:fail(got_bad_data) + ct:fail(got_bad_data) end. @@ -396,14 +395,14 @@ try_timeouts(Port, Timeout) -> io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed, Timeout]), if Elapsed < Timeout -> - ?line ?t:fail(too_short); + ?line ct:fail(too_short); Elapsed > Timeout + ?delay -> - ?line ?t:fail(too_long); + ?line ct:fail(too_long); true -> try_timeouts(Port, Timeout div 2) end after Timeout + ?delay -> - ?line test_server:fail("driver failed to timeout") + ?line ct:fail("driver failed to timeout") end. timer_cancel(doc) -> ["Try cancelling timers set in a driver."]; @@ -421,12 +420,12 @@ try_cancel(Port, Timeout) -> Port ! {self(),{command,<>}}, receive {Port, {data, [?TIMER]}} -> - ?line test_server:fail("driver timed out before cancelling it") + ?line ct:fail("driver timed out before cancelling it") after Timeout -> Port ! {self(), {command, [?CANCEL_TIMER]}}, receive {Port, {data, [?TIMER]}} -> - ?line test_server:fail("driver timed out after cancelling it"); + ?line ct:fail("driver timed out after cancelling it"); {Port, {data, [?CANCELLED]}} -> ?line Time_milli_secs = erl_millisecs() - T_before, @@ -434,12 +433,12 @@ try_cancel(Port, Timeout) -> [Time_milli_secs, Timeout]), if Time_milli_secs > (Timeout + ?delay) -> - ?line test_server:fail("too long real time"); + ?line ct:fail("too long real time"); Timeout == 0 -> ok; true -> try_cancel(Port, Timeout div 2) end after ?delay -> - test_server:fail("No message from driver") + ct:fail("No message from driver") end end. @@ -465,9 +464,9 @@ timer_delay(Config) when is_list(Config) -> [Elapsed,Timeout]), if Elapsed < Timeout -> - ?line ?t:fail(too_short); + ?line ct:fail(too_short); Elapsed > Timeout + ?delay -> - ?line ?t:fail(too_long); + ?line ct:fail(too_long); true -> ok end @@ -500,14 +499,14 @@ try_change_timer(Port, Timeout) -> io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed,Timeout]), if Elapsed < Timeout -> - ?line ?t:fail(too_short); + ?line ct:fail(too_short); Elapsed > Timeout + ?delay -> - ?line ?t:fail(too_long); + ?line ct:fail(too_long); true -> try_timeouts(Port, Timeout div 2) end after Timeout + ?delay -> - ?line test_server:fail("driver failed to timeout") + ct:fail("driver failed to timeout") end. @@ -635,11 +634,10 @@ feed_driver(Port, [{Method0,Data}|T], Expected_return, Qb_before) -> case Qb_before + Size of Qb_in_driver -> ok; Sum -> - io:format("Qb_before: ~p\n" - "Qb_before+Size: ~p\n" - "Qb_in_driver: ~p", - [Qb_before,Sum,Qb_in_driver]), - ?t:fail() + ct:fail("Qb_before: ~p\n" + "Qb_before+Size: ~p\n" + "Qb_in_driver: ~p", + [Qb_before,Sum,Qb_in_driver]) end, X_return = case Method of ?ENQ -> list_to_binary([Expected_return,Data]); @@ -668,9 +666,7 @@ compare_return(Port, QueuedInPort0, Len_to_get, DeqSize) -> case bytes_queued(Port) of Len_to_get -> ok; BytesInQueue -> - io:format("Len_to_get: ~p", [Len_to_get]), - io:format("Bytes in queue: ~p", [BytesInQueue]), - ?line test_server:fail() + ct:fail("Len_to_get: ~p\nBytes in queue: ~p", [Len_to_get,BytesInQueue]) end, BytesToDequeue = if (DeqSize > Len_to_get) -> Len_to_get; true -> DeqSize @@ -682,10 +678,8 @@ compare_return(Port, QueuedInPort0, Len_to_get, DeqSize) -> <<_:BytesToDequeue/binary,QueuedInPort/binary>> = QueuedInPort0, compare_return(Port, QueuedInPort, Len_to_get - BytesToDequeue, DeqSize); false -> - io:format("Bytes to dequeue: ~p", [BytesToDequeue]), - io:format("Dequeued: ~p", [Dequeued]), - io:format("Queued in port: ~P", [QueuedInPort0,12]), - ?t:fail() + ct:fail("Bytes to dequeue: ~p\nDequeued: ~p\nQueued in port: ~P", + [BytesToDequeue, Dequeued, QueuedInPort0,12]) end. %% bin_prefix(PrefixBinary, Binary) @@ -704,7 +698,7 @@ queue_op(Port, Method, Data) -> bytes_queued(Port) -> case erlang:port_control(Port, ?BYTES_QUEUED, []) of <> -> I; - Bad -> ?t:fail({bad_result,Bad}) + Bad -> ct:fail({bad_result,Bad}) end. deq(Port, Size) -> @@ -774,7 +768,7 @@ io_ready_exit(Config) when is_list(Config) -> ready_input_driver_failure -> ?t:format("Exited in input_ready()~n"), ?line ok; - Error -> ?line ?t:fail(Error) + Error -> ?line ct:fail(Error) end end, receive after 2000 -> ok end, @@ -788,7 +782,7 @@ io_ready_exit(Config) when is_list(Config) -> ?line {skipped, "Not yet implemented for this OS"}; Error -> ?line process_flag(trap_exit, OTE), - ?line ?t:fail({unexpected_control_result, Error}) + ?line ct:fail({unexpected_control_result, Error}) end. @@ -814,7 +808,7 @@ use_fallback_pollset(Config) when is_list(Config) -> {fallback_poll_set_size, N}} when N > 0 -> ?line ok; Error -> - ?line ?t:fail({failed_to_use_fallback, Error}) + ?line ct:fail({failed_to_use_fallback, Error}) end end, ?line {BckupTest, Handel, OkRes} @@ -895,7 +889,7 @@ steal_control_test(Hndl = {erts_poll_info, Before}) -> ?line ok; StopErr -> ?line chk_chkio_port(Port), - ?line ?t:fail({stop_error, StopErr}) + ?line ct:fail({stop_error, StopErr}) end, ?line close_chkio_port(Port), ?line Res; @@ -907,7 +901,7 @@ steal_control_test(Hndl = {erts_poll_info, Before}) -> Before}; StartErr -> ?line chk_chkio_port(Port), - ?line ?t:fail({start_error, StartErr}) + ?line ct:fail({start_error, StartErr}) end. chkio_test_init(Config) when is_list(Config) -> @@ -944,15 +938,15 @@ close_chkio_port(Port) when is_port(Port) -> {'EXIT', Port, normal} -> ok; {'EXIT', Port, Reason} -> - ?t:fail({abnormal_port_exit, Port, Reason}); + ct:fail({abnormal_port_exit, Port, Reason}); {Port, Message} -> - ?t:fail({strange_message_from_port, Message}) + ct:fail({strange_message_from_port, Message}) end. chk_chkio_port(Port) -> receive {'EXIT', Port, Reason} when Reason /= normal -> - ?t:fail({port_exited, Port, Reason}) + ct:fail({port_exited, Port, Reason}) after 0 -> ok end. @@ -998,7 +992,7 @@ chkio_test({erts_poll_info, Before}, end; StopErr -> ?line chk_chkio_port(Port), - ?line ?t:fail({stop_error, StopErr}) + ?line ct:fail({stop_error, StopErr}) end; [$s,$k,$i,$p,$:,$\ |Skip] -> ?line chk_chkio_port(Port), @@ -1008,7 +1002,7 @@ chkio_test({erts_poll_info, Before}, Before}; StartErr -> ?line chk_chkio_port(Port), - ?line ?t:fail({start_error, StartErr}) + ?line ct:fail({start_error, StartErr}) end. verify_chkio_state(Before, After) -> @@ -1117,9 +1111,9 @@ driver_system_info_test(Config, Name) -> [$o,$k,$:,_ | Result] -> ?line check_driver_system_info_result(Result); [$e,$r,$r,$o,$r,$:,_ | Error] -> - ?line ?t:fail(Error); + ?line ct:fail(Error); Unexpected -> - ?line ?t:fail({unexpected_result, Unexpected}) + ?line ct:fail({unexpected_result, Unexpected}) end, ?line stop_driver(Port, Name), ?line ok. @@ -1203,7 +1197,7 @@ check_si_res(["dirty_sched", _Value]) -> true; check_si_res(Unexpected) -> - ?line ?t:fail({unexpected_result, Unexpected}). + ?line ct:fail({unexpected_result, Unexpected}). -define(MON_OP_I_AM_IPID,1). -define(MON_OP_MONITOR_ME,2). @@ -1396,7 +1390,7 @@ ioq_exit_test(Config, TestNo) -> Drv) of ok -> ?line ok; {error, permanent} -> ?line ok; - LoadError -> ?line ?t:fail({load_error, LoadError}) + LoadError -> ?line ct:fail({load_error, LoadError}) end, case open_port({spawn, Drv}, []) of Port when is_port(Port) -> @@ -1410,7 +1404,7 @@ ioq_exit_test(Config, TestNo) -> [$s,$k,$i,$p,$:,$ | Comment] -> ?line throw({skipped, Comment}); [$e,$r,$r,$o,$r,$:,$ | Error] -> - ?line ?t:fail(Error) + ?line ct:fail(Error) after Port ! {self(), close}, receive {Port, closed} -> ok end, @@ -1418,7 +1412,7 @@ ioq_exit_test(Config, TestNo) -> ok end; Error -> - ?line ?t:fail({open_port_failed, Error}) + ?line ct:fail({open_port_failed, Error}) end end catch @@ -1539,7 +1533,7 @@ peek_non_existing_queue(Config) when is_list(Config) -> Drv) of ok -> ?line ok; {error, permanent} -> ?line ok; - LoadError -> ?line ?t:fail({load_error, LoadError}) + LoadError -> ?line ct:fail({load_error, LoadError}) end, case open_port({spawn, Drv}, []) of Port1 when is_port(Port1) -> @@ -1549,13 +1543,13 @@ peek_non_existing_queue(Config) when is_list(Config) -> [$s,$k,$i,$p,$p,$e,$d,$:,$ | SkipReason] -> ?line throw({skipped, SkipReason}); [$e,$r,$r,$o,$r,$:,$ | Error1] -> - ?line ?t:fail(Error1) + ?line ct:fail(Error1) after exit(Port1, kill), receive {'EXIT', Port1, _} -> ok end end; Error1 -> - ?line ?t:fail({open_port1_failed, Error1}) + ?line ct:fail({open_port1_failed, Error1}) end, case open_port({spawn, Drv}, []) of Port2 when is_port(Port2) -> @@ -1563,14 +1557,14 @@ peek_non_existing_queue(Config) when is_list(Config) -> "ok" -> ?line ok; [$e,$r,$r,$o,$r,$:,$ | Error2] -> - ?line ?t:fail(Error2) + ?line ct:fail(Error2) after receive {Port2, test_successful} -> ok end, Port2 ! {self(), close}, receive {Port2, closed} -> ok end end; Error2 -> - ?line ?t:fail({open_port2_failed, Error2}) + ?line ct:fail({open_port2_failed, Error2}) end end catch @@ -1609,7 +1603,7 @@ otp_6879(Config) when is_list(Config) -> {P, ok} -> ?line ok; {P, Error} -> - ?line ?t:fail({P, Error}) + ?line ct:fail({P, Error}) end end, Procs), @@ -1620,7 +1614,7 @@ otp_6879(Config) when is_list(Config) -> ?line ok = otp_6879_call(Port, Data, 10), ?line erlang:port_close(Port); _ -> - ?line ?t:fail(open_port_failed) + ?line ct:fail(open_port_failed) end, ?line erl_ddll:unload_driver(Drv), ?line ok. @@ -2071,7 +2065,7 @@ thr_msg_blast(Config) when is_list(Config) -> End = os:timestamp(), receive Garbage -> - ?t:fail({received_garbage, Port, Garbage}) + ct:fail({received_garbage, Port, Garbage}) after 2000 -> ok end, @@ -2096,7 +2090,7 @@ thr_msg_blast(Config) when is_list(Config) -> "lock checking~n", [?MODULE,?LINE]); false -> - ?t:fail({unexpected_sched_counts, VaLuE_}) + ct:fail({unexpected_sched_counts, VaLuE_}) end end). @@ -2542,7 +2536,7 @@ stop_driver(Port, Name) -> ?line true = erlang:port_close(Port), receive {Port,Message} -> - ?t:fail({strange_message_from_port,Message}) + ct:fail({strange_message_from_port,Message}) after 0 -> ok end, diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index cb26e8e736..79a2d20fe0 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -145,7 +145,7 @@ iter_max_files(Config) when is_list(Config) -> all_equal(L), Head = hd(L), if Head >= 2 -> ok; - true -> ?line test_server:fail(too_few_files) + true -> ct:fail(too_few_files) end, {comment, "Max files: " ++ integer_to_list(hd(L))}. diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl index a56741adc6..3497760515 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE.erl +++ b/erts/emulator/test/erl_drv_thread_SUITE.erl @@ -86,8 +86,7 @@ run_drv_case(Config, CaseName, Command, TimeTrap) -> case erl_ddll:load_driver(DataDir, CaseName) of ok -> ok; {error, Error} -> - io:format("~s\n", [erl_ddll:format_error(Error)]), - ?line ?t:fail() + ct:fail(erl_ddll:format_error(Error)) end, ?line Port = open_port({spawn, atom_to_list(CaseName)}, []), ?line true = is_port(Port), @@ -107,11 +106,11 @@ receive_drv_result(Port, CaseName) -> ?line ?t:format("~s", [Str]), ?line receive_drv_result(Port, CaseName); {'EXIT', Port, Error} -> - ?line ?t:fail(Error); + ?line ct:fail(Error); {'EXIT', error, Error} -> - ?line ?t:fail(Error); + ?line ct:fail(Error); {failed, Port, CaseName, Comment} -> - ?line ?t:fail(Comment); + ?line ct:fail(Comment); {skipped, Port, CaseName, Comment} -> ?line {skipped, Comment}; {succeeded, Port, CaseName, ""} -> diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index a3f44e7313..62ae83f9e2 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -223,7 +223,7 @@ monitor_nodes(Config) when is_list(Config) -> ?line check_monitor_node(self(), B, 0), ?line receive {nodedown, X} -> - ?line ?t:fail({unexpected_nodedown, X}) + ?line ct:fail({unexpected_nodedown, X}) after 0 -> ?line ok end, @@ -242,9 +242,9 @@ process_monitors(Config) when is_list(Config) -> ?line [] = find_erl_monitor(self(), Mon2), ?line receive {'DOWN', Mon1, _, _, _} = Msg -> - ?line ?t:fail({unexpected_down_msg, Msg}); + ?line ct:fail({unexpected_down_msg, Msg}); {'DOWN', Mon2, _, _, _} = Msg -> - ?line ?t:fail({unexpected_down_msg, Msg}) + ?line ct:fail({unexpected_down_msg, Msg}) after 500 -> ?line true = erlang:demonitor(Mon1), ?line true = erlang:demonitor(Mon2), @@ -449,7 +449,7 @@ otp_5772_link_test(Node) -> end, ?line receive {'EXIT', TP1, _} = Exit -> - ?line ?t:fail({got_late_exit_message, Exit}) + ?line ct:fail({got_late_exit_message, Exit}) after 1000 -> ?line ok end, @@ -485,7 +485,7 @@ otp_5772_monitor_test(Node) -> end, ?line receive {'DOWN', M1, _, _, _} = Down -> - ?line ?t:fail({got_late_down_message, Down}) + ?line ct:fail({got_late_down_message, Down}) after 1000 -> ?line ok end, diff --git a/erts/emulator/test/evil_SUITE.erl b/erts/emulator/test/evil_SUITE.erl index 95a5ff3f52..02175e6bed 100644 --- a/erts/emulator/test/evil_SUITE.erl +++ b/erts/emulator/test/evil_SUITE.erl @@ -52,7 +52,7 @@ heap_frag(Config) when is_list(Config) -> ?line Res = my_appender(N); Garbage -> io:format("Garbage: ~p\n", [Garbage]), - ?line ?t:fail(got_garbage) + ct:fail(got_garbage) end. diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index c2b2c50cd5..0face2dfe8 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -110,7 +110,7 @@ pending_catched(First, Second, Expected) -> {'EXIT', Reason} -> pending(Reason, bad_guy, [First, Second], Expected); Other -> - test_server:fail({not_exit, Other}) + ct:fail({not_exit, Other}) end. pending_exit_message(Args, Expected) -> @@ -122,9 +122,9 @@ pending_exit_message(Args, Expected) -> {'EXIT', Pid, Reason} -> pending(Reason, bad_guy, Args, Expected); Other -> - test_server:fail({unexpected_message, Other}) + ct:fail({unexpected_message, Other}) after 10000 -> - test_server:fail(timeout) + ct:fail(timeout) end, process_flag(trap_exit, false). @@ -144,7 +144,7 @@ pending({Code,[{?MODULE,Func,Arity,Loc}|_]}, Func, Args, Code) when length(Args) =:= Arity, is_list(Loc) -> ok; pending(Reason, _Function, _Args, _Code) -> - test_server:fail({bad_exit_reason,Reason}). + ct:fail({bad_exit_reason,Reason}). %% Test that doing arithmetics on [] gives a badarith EXIT and not a crash. diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index de889baeab..9eb4456de0 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -411,7 +411,7 @@ unify(Other, Info) -> -define(epsilon, 1.0e-20). check_epsilon(R,Val) -> if erlang:abs(R-Val) < ?epsilon -> ok; - true -> ?t:fail({R,Val}) + true -> ct:fail({R,Val}) end. t_mul_add_ops(Config) when is_list(Config) -> diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 103244d6ca..9020c7f7b9 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -70,7 +70,7 @@ bad_apply_fc(Fun, Args) -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]); Other -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]), - ?t:fail({bad_result,Other}) + ct:fail({bad_result,Other}) end. bad_apply_badarg(Fun, Args) -> @@ -82,7 +82,7 @@ bad_apply_badarg(Fun, Args) -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]); Other -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]), - ?t:fail({bad_result, Other}) + ct:fail({bad_result, Other}) end. bad_fun_call(doc) -> @@ -109,7 +109,7 @@ bad_call_fc(Fun) -> ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]); Other -> ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]), - ?t:fail({bad_result,Other}) + ct:fail({bad_result,Other}) end. %% Call and apply valid funs with wrong number of arguments. @@ -129,7 +129,7 @@ badarity(Config) when is_list(Config) -> ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]); _ -> ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]), - ?line ?t:fail({bad_result,Res}) + ?line ct:fail({bad_result,Res}) end, %% Apply. @@ -142,7 +142,7 @@ badarity(Config) when is_list(Config) -> ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]); _ -> ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]), - ?line ?t:fail({bad_result,Res2}) + ?line ct:fail({bad_result,Res2}) end, ok. @@ -163,7 +163,7 @@ ext_badarity(Config) when is_list(Config) -> ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]); _ -> ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]), - ?line ?t:fail({bad_result,Res}) + ?line ct:fail({bad_result,Res}) end, %% Apply. @@ -176,7 +176,7 @@ ext_badarity(Config) when is_list(Config) -> ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]); _ -> ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]), - ?line ?t:fail({bad_result,Res2}) + ?line ct:fail({bad_result,Res2}) end, ok. @@ -405,7 +405,7 @@ fun_to_port(Config, IoList) -> Port = open_port(AFile, [out]), case catch port_command(Port, IoList) of {'EXIT',{badarg,_}} -> ok; - Other -> ?t:fail({unexpected_retval,Other}) + Other -> ct:fail({unexpected_retval,Other}) end. build_io_list(0) -> []; @@ -534,7 +534,7 @@ refc(Config) when is_list(Config) -> ?line Pid = spawn_link(fun() -> {refc,4} = erlang:fun_info(F1, refc) end), receive {'EXIT',Pid,normal} -> ok; - Other -> ?line ?t:fail({unexpected,Other}) + Other -> ?line ct:fail({unexpected,Other}) end, ?line process_flag(trap_exit, false), ?line {refc,3} = erlang:fun_info(F1, refc), @@ -609,7 +609,7 @@ refc_dist(Config) when is_list(Config) -> ?line Pid ! F, F2 = receive {'EXIT',Pid,{normal,Fun}} -> Fun; - Other -> ?line ?t:fail({unexpected,Other}) + Other -> ?line ct:fail({unexpected,Other}) end, %% dist.c:net_mess2 have a reference to Fun for a while since %% Fun is passed in an exit signal. Wait until it is gone. @@ -633,7 +633,7 @@ refc_dist_send(Node, F) -> Pid ! {self(),F}, F2 = receive Fun when is_function(Fun) -> Fun; - Other -> ?line ?t:fail({unexpected,Other}) + Other -> ?line ct:fail({unexpected,Other}) end, receive {'EXIT',Pid,normal} -> ok end, %% No reference from dist.c:net_mess2 since Fun is passed @@ -663,7 +663,7 @@ refc_dist_reg_send(Node, F) -> {my_fun_tester,Node} ! {self(),F}, F2 = receive Fun when is_function(Fun) -> Fun; - Other -> ?line ?t:fail({unexpected,Other}) + Other -> ?line ct:fail({unexpected,Other}) end, receive {'EXIT',Pid,normal} -> ok end, @@ -691,7 +691,7 @@ my_cmp({Fun,Fun}) -> ok; my_cmp({Fun1,Fun2}) -> io:format("Fun1: ~p", [erlang:fun_info(Fun1)]), io:format("Fun2: ~p", [erlang:fun_info(Fun2)]), - ?t:fail(). + ct:fail(no_match). t_arity(Config) when is_list(Config) -> ?line 0 = fun_arity(fun() -> ok end), @@ -748,15 +748,14 @@ t_fun_info(Config) when is_list(Config) -> ?line F = fun t_fun_info/1, ?line try F(blurf) of FAny -> - io:format("should fail; returned ~p\n", [FAny]), - ?line ?t:fail() + ct:fail("should fail; returned ~p\n", [FAny]) catch error:function_clause -> ok end, ?line {module,?MODULE} = erlang:fun_info(F, module), ?line case erlang:fun_info(F, name) of undefined -> - ?line ?t:fail(); + ct:fail(no_fun_info); _ -> ok end, ?line {arity,1} = erlang:fun_info(F, arity), @@ -772,8 +771,7 @@ t_fun_info(Config) when is_list(Config) -> ?line FF = fun ?MODULE:t_fun_info/1, ?line try FF(blurf) of FFAny -> - io:format("should fail; returned ~p\n", [FFAny]), - ?line ?t:fail() + ct:fail("should fail; returned ~p\n", [FFAny]) catch error:function_clause -> ok end, @@ -821,8 +819,7 @@ t_fun_info_mfa(Config) when is_list(Config) -> bad_info(Term) -> try erlang:fun_info(Term, module) of Any -> - io:format("should fail; returned ~p\n", [Any]), - ?t:fail() + ict:fail("should fail; returned ~p\n", [Any]) catch error:badarg -> ok end. @@ -833,7 +830,7 @@ verify_undef(Fun, Tag) -> verify_not_undef(Fun, Tag) -> case erlang:fun_info(Fun, Tag) of {Tag,undefined} -> - ?t:fail(); + ct:fail("tag ~w not defined in fun_info", [Tag]); {Tag,_} -> ok end. @@ -858,15 +855,15 @@ spawn_call(Node, AFun) -> Pid ! {AFun,AFun,AFun}, Res = receive {result,R} -> R; - Other -> ?t:fail({bad_message,Other}) + Other -> ct:fail({bad_message,Other}) after 10000 -> - ?t:fail(timeout_waiting_for_result) + ct:fail(timeout_waiting_for_result) end, receive {'EXIT',Pid,normal} -> ok; - Other2 -> ?t:fail({bad_message_waiting_for_exit,Other2}) + Other2 -> ct:fail({bad_message_waiting_for_exit,Other2}) after 10000 -> - ?t:fail(timeout_waiting_for_exit) + ct:fail(timeout_waiting_for_exit) end, Res. diff --git a/erts/emulator/test/fun_r13_SUITE.erl b/erts/emulator/test/fun_r13_SUITE.erl index 32d58355e0..3ee7b90686 100644 --- a/erts/emulator/test/fun_r13_SUITE.erl +++ b/erts/emulator/test/fun_r13_SUITE.erl @@ -65,7 +65,7 @@ do_dist_old(Config) when is_list(Config) -> {ok,Fun,R12BFun} -> ?line [a,b,c] = R12BFun(a); Other -> - ?line ?t:fail({bad_message,Other}) + ?line ct:fail({bad_message,Other}) end, ok. diff --git a/erts/emulator/test/guard_SUITE.erl b/erts/emulator/test/guard_SUITE.erl index 78c65c5157..f43c30e421 100644 --- a/erts/emulator/test/guard_SUITE.erl +++ b/erts/emulator/test/guard_SUITE.erl @@ -130,10 +130,9 @@ a_receive() -> {'EXIT', Pid, {result, Result}} -> ?line 'try'(Iter-1, Fun, Args, Result, [0|Filler]); {result, Other} -> - ?line io:format("Expected ~p; got ~p~n", [Result, Other]), - ?line test_server:fail(); + ct:fail("Expected ~p; got ~p~n", [Result, Other]); Other -> - ?line test_server:fail({unexpected_message, Other}) + ct:fail({unexpected_message, Other}) end. init(Fun, Args, Filler) -> @@ -400,9 +399,7 @@ try_gbif(Id, X, Y) -> {Id, X, Y} -> io:format("guard_bif(~p, ~p, ~p) -- ok", [Id, X, Y]); Other -> - ?line ok = io:format("guard_bif(~p, ~p, ~p) -- bad result: ~p\n", - [Id, X, Y, Other]), - ?line test_server:fail() + ct:fail("guard_bif(~p, ~p, ~p) -- bad result: ~p\n", [Id, X, Y, Other]) end. try_fail_gbif(Id, X, Y) -> @@ -410,9 +407,7 @@ try_fail_gbif(Id, X, Y) -> {'EXIT',{function_clause,[{?MODULE,guard_bif,[Id,X,Y],_}|_]}} -> io:format("guard_bif(~p, ~p, ~p) -- ok", [Id,X,Y]); Other -> - ?line ok = io:format("guard_bif(~p, ~p, ~p) -- bad result: ~p\n", - [Id, X, Y, Other]), - ?line test_server:fail() + ct:fail("guard_bif(~p, ~p, ~p) -- bad result: ~p\n", [Id, X, Y, Other]) end. guard_bif('abs/1', X, Y) when abs(X) == Y -> @@ -455,9 +450,7 @@ type_tests(Config) when is_list(Config) -> {0, N} -> {comment, integer_to_list(N) ++ " standard violation(s)"}; {Errors, Violations} -> - io:format("~p sub test(s) failed, ~p violation(s)", - [Errors, Violations]), - ?line test_server:fail() + ct:fail("~p sub test(s) failed, ~p violation(s)", [Errors, Violations]) end. type_tests([{Test, AllowedTypes}| T], AllTypes) -> @@ -484,7 +477,7 @@ type_tests(Test, [Type|T], Allowed) -> when is_list(Loc) -> ok; {'EXIT',Other} -> - ?line test_server:fail({unexpected_error_reason,Other}); + ct:fail({unexpected_error_reason,Other}); tuple when is_function(Value) -> io:format("Standard violation: Test ~p(~p) should fail", [Test, Value]), diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl index ea4cc79701..177468fdfd 100644 --- a/erts/emulator/test/hibernate_SUITE.erl +++ b/erts/emulator/test/hibernate_SUITE.erl @@ -84,13 +84,13 @@ hibernate_wake_up(N, ExpectedHeapSz, Child) -> if size(Bin) > 1000 -> io:format("~s\n", [binary_to_list(Bin)]), - ?line ?t:fail(stack_is_growing); + ?line ct:fail(stack_is_growing); true -> hibernate_wake_up(N-1, ExpectedHeapSz, Child) end; Other -> ?line io:format("~p\n", [Other]), - ?line ?t:fail(unexpected_message) + ?line ct:fail(unexpected_message) end. basic_hibernator(Info) -> @@ -199,8 +199,7 @@ min_heap_size_1(Config) when is_list(Config) -> AfterSize >= 15000 -> ok end; Other -> - io:format("Unexpected: ~p\n", [Other]), - ?line ?t:fail() + ct:fail("Unexpected: ~p\n", [Other]) end. min_hibernator({Parent,_Ref}) -> @@ -244,7 +243,7 @@ bad_args(Mod, Name, Args) -> io:format("erlang:hibernate(~p, ~p, ~p) -> ~p\n", [Mod,Name,Args,Res]); Other -> io:format("erlang:hibernate(~p, ~p, ~p) -> ~p\n", [Mod,Name,Args,Res]), - ?t:fail({bad_result,Other}) + ct:fail({bad_result,Other}) end. @@ -262,7 +261,7 @@ messages_in_queue(Config) when is_list(Config) -> done -> ok; Other -> ?line io:format("~p\n", [Other]), - ?line ?t:fail(unexpected_message) + ?line ct:fail(unexpected_message) end. messages_in_queue_1(Parent, ExpectedMsg) -> @@ -279,7 +278,7 @@ messages_in_queue_restart(Parent, ExpectedMessage) -> Parent ! done; Other -> io:format("~p\n", [Other]), - ?t:fail(unexpected_message) + ct:fail(unexpected_message) end, ok. @@ -301,7 +300,7 @@ undefined_mfa(Config) when is_list(Config) -> ok; Other -> ?line io:format("~p\n", [Other]), - ?line ?t:fail(unexpected_message) + ?line ct:fail(unexpected_message) end, undefined_mfa_1(). @@ -319,7 +318,7 @@ undefined_mfa_1() -> ok; Other -> ?line io:format("~p\n", [Other]), - ?line ?t:fail(unexpected_message) + ?line ct:fail(unexpected_message) end, ok. @@ -369,7 +368,7 @@ wake_up_and_bif_trap(Config) when is_list(Config) -> ?line receive {ok, Pid0} when Pid0 =:= Pid -> ok after 5000 -> - ?line ?t:fail(process_blocked) + ?line ct:fail(process_blocked) end, ?line unlink(Pid), ?line exit(Pid, bye). diff --git a/erts/emulator/test/list_bif_SUITE.erl b/erts/emulator/test/list_bif_SUITE.erl index c6221b8c66..7410cde99b 100644 --- a/erts/emulator/test/list_bif_SUITE.erl +++ b/erts/emulator/test/list_bif_SUITE.erl @@ -61,9 +61,7 @@ hd_test(Config) when is_list(Config) -> ?line case catch hd(id($h)) of {'EXIT', {badarg, _}} -> ok; Res -> - Str=io_lib:format("hd/1 with incorrect args "++ - "succeeded.~nResult: ~p", [Res]), - test_server:fail(Str) + ct:fail("hd/1 with incorrect args succeeded.~nResult: ~p", [Res]) end, ok. @@ -75,9 +73,7 @@ tl_test(Config) when is_list(Config) -> {'EXIT', {badarg, _}} -> ok; Res -> - Str=io_lib:format("tl/1 with incorrect args "++ - "succeeded.~nResult: ~p", [Res]), - test_server:fail(Str) + ct:fail("tl/1 with incorrect args succeeded.~nResult: ~p", [Res]) end, ok. @@ -98,9 +94,7 @@ t_length(Config) when is_list(Config) -> {'EXIT', {badarg, _}} -> ok; Res -> - Str = io_lib:format("length/1 with incorrect args "++ - "succeeded.~nResult: ~p", [Res]), - ?line test_server:fail(Str) + ct:fail("length/1 with incorrect args succeeded.~nResult: ~p", [Res]) end, ok. @@ -115,10 +109,7 @@ t_list_to_pid(Config) when is_list(Config) -> {'EXIT', {badarg, _}} -> ok; Res -> - Str=io_lib:format("list_to_pid/1 with incorrect "++ - "arg succeeded.~nResult: ~p", - [Res]), - test_server:fail(Str) + ct:fail("list_to_pid/1 with incorrect arg succeeded.~nResult: ~p", [Res]) end, ok. @@ -131,10 +122,7 @@ t_list_to_float(Config) when is_list(Config) -> ?line case catch list_to_float(id("58")) of {'EXIT', {badarg, _}} -> ok; Res -> - Str=io_lib:format("list_to_float with incorrect "++ - "arg succeeded.~nResult: ~p", - [Res]), - test_server:fail(Str) + ct:fail("list_to_float with incorrect arg succeeded.~nResult: ~p", [Res]) end, ok. diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 22425bd66d..1e8bba99af 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -731,7 +731,7 @@ errchk(Pat) -> {'EXIT', {badarg, _}} -> ok; Other -> - test_server:fail({noerror, Other}) + ct:fail({noerror, Other}) end. unary_minus(suite) -> @@ -870,8 +870,7 @@ fpe(Config) when is_list(Config) -> MS = [{{'$1'},[],[{'/','$1',0}]}], case catch (['EXIT','EXIT'] = ets:match_spec_run([{1},{2}],ets:match_spec_compile(MS))) of - {'EXIT',_} -> test_server:fail({error, - "Floating point exceptions faulty"}); + {'EXIT',_} -> ct:fail({error, "Floating point exceptions faulty"}); _ -> ok end. @@ -1034,7 +1033,7 @@ flush(Reason) -> ?t:format("In queue: ~p~n", [M]), flush(Reason) after 17 -> - ?t:fail(Reason) + ct:fail(Reason) end. start_collect(P) -> diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index ce4c40ee02..05dfc34ed7 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -91,7 +91,7 @@ case_2(Config) when is_list(Config) -> ?line receive {'EXIT', _} -> ok; Other -> - test_server:fail({rec, Other}) + ct:fail({rec, Other}) end, ?line expect_down(R, B, normal), ok. @@ -104,7 +104,7 @@ case_2a(Config) when is_list(Config) -> ?line receive {'EXIT', _} -> ok; Other -> - test_server:fail({rec, Other}) + ct:fail({rec, Other}) end, ?line expect_down(R, B, normal), ok. @@ -119,7 +119,7 @@ expect_down(Ref, P) -> {'DOWN', Ref, process, P, Reason} -> Reason; Other -> - test_server:fail({rec, Other}) + ct:fail({rec, Other}) end. expect_down(Ref, P, Reason) -> @@ -127,13 +127,13 @@ expect_down(Ref, P, Reason) -> {'DOWN', Ref, process, P, Reason} -> ok; Other -> - test_server:fail({rec, Other}) + ct:fail({rec, Other}) end. expect_no_msg() -> receive Msg -> - test_server:fail({msg, Msg}) + ct:fail({msg, Msg}) after 0 -> ok end. @@ -166,7 +166,7 @@ mon_error(Type, Item) -> {'EXIT', _} -> ok; Other -> - test_server:fail({err, Other}) + ct:fail({err, Other}) end. %%% Error cases for demonitor/1 @@ -195,7 +195,7 @@ demon_e_1(Config) when is_list(Config) -> ?line demon_error(R2, badarg), ?line P2 ! {self(), stop}; Other2 -> - test_server:fail({rec, Other2}) + ct:fail({rec, Other2}) end, ?line true = test_server:stop_node(N), @@ -206,7 +206,7 @@ demon_error(Ref, Reason) -> {'EXIT', {Reason, _}} -> ok; Other -> - test_server:fail({err, Other}) + ct:fail({err, Other}) end. %%% No-op cases for demonitor/1 @@ -237,7 +237,7 @@ demon_2(Config) when is_list(Config) -> ?line case expect_down(R2, P2) of normal -> ?line ok; noproc -> ?line ok; - BadReason -> ?line ?t:fail({bad_reason, BadReason}) + BadReason -> ?line ct:fail({bad_reason, BadReason}) end, %% OTP-5772 @@ -308,7 +308,7 @@ demonitor_flush_test(Node) -> ?line receive {'DOWN', M, _, _, _} =DM when M == M1, M == M3 -> - ?line ?t:fail({unexpected_down_message, DM}) + ?line ct:fail({unexpected_down_message, DM}) after 100 -> ?line ok end. @@ -395,7 +395,7 @@ mon_1(Config) when is_list(Config) -> ?line case expect_down(R2, P2) of normal -> ?line ok; noproc -> ?line ok; - BadReason -> ?line ?t:fail({bad_reason, BadReason}) + BadReason -> ?line ct:fail({bad_reason, BadReason}) end, ?line {P2A,R2A} = spawn_monitor(timer, sleep, [1]), ?line expect_down(R2A, P2A, normal), @@ -529,7 +529,7 @@ f() -> X == S -> ok; true -> - test_server:fail({X, S}) + ct:fail({X, S}) end; Other -> ?line io:format(" -> ~p~n", [Other]), @@ -801,7 +801,7 @@ otp_5827(Config) when is_list(Config) -> Ok -> ?line ok after 1000 -> - ?line ?t:fail("erlang:monitor/2 hangs") + ?line ct:fail("erlang:monitor/2 hangs") end. monitor_time_offset(Config) when is_list(Config) -> @@ -823,7 +823,7 @@ monitor_time_offset(Config) when is_list(Config) -> {no_change_message_received, P} -> ok; {'DOWN', M, process, P, Reason} -> - ?t:fail(Reason) + ct:fail(Reason) end end, PMs), preliminary = rpc:call(Node, erlang, system_flag, [time_offset, finalize]), @@ -832,7 +832,7 @@ monitor_time_offset(Config) when is_list(Config) -> {change_messages_received, P} -> erlang:demonitor(M, [flush]); {'DOWN', M, process, P, Reason} -> - ?t:fail(Reason) + ct:fail(Reason) end end, PMs), stop_node(Node), @@ -966,7 +966,7 @@ start_jeeves({Name, Node}) {Pid, Ref} -> ok; Other -> - test_server:fail({rec, Other}) + ct:fail({rec, Other}) end, Pid; start_jeeves(Name) when is_atom(Name) -> @@ -983,7 +983,7 @@ ask_jeeves(Pid, Request) when is_pid(Pid) -> {Pid, Response} -> Response; Other -> - test_server:fail({rec, Other}) + ct:fail({rec, Other}) end. @@ -993,7 +993,7 @@ expect_jeeves(Pid, Request, Response) when is_pid(Pid) -> {Pid, Response} -> ok; Other -> - test_server:fail({rec, Other}) + ct:fail({rec, Other}) end. diff --git a/erts/emulator/test/nested_SUITE.erl b/erts/emulator/test/nested_SUITE.erl index 5b59a9f3d7..f9a913ecc8 100644 --- a/erts/emulator/test/nested_SUITE.erl +++ b/erts/emulator/test/nested_SUITE.erl @@ -52,7 +52,7 @@ search_any([Key|Rest], List) -> false -> error; Other -> - test_server:fail({other_result, Other}) + ct:fail({other_result, Other}) end, ?line search_any(Rest, List); search_any([], _) -> diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 78922af956..05c393e65f 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1519,7 +1519,7 @@ consume_timeslice(Config) when is_list(Config) -> io:format("Reductions = ~p~n", [Reductions]), ok; {RedDiff, Reductions} -> - ?t:fail({unexpected_reduction_count, Reductions}) + ct:fail({unexpected_reduction_count, Reductions}) end, none = next_msg(P), @@ -1583,7 +1583,7 @@ dirty_nif_exception(Config) when is_list(Config) -> %% dirty NIF returns the result of enif_make_badarg %% directly call_dirty_nif_exception(1), - ?t:fail(expected_badarg) + ct:fail(expected_badarg) catch error:badarg -> [{?MODULE,call_dirty_nif_exception,[1],_}|_] = @@ -1595,7 +1595,7 @@ dirty_nif_exception(Config) when is_list(Config) -> %% dirty NIF calls enif_make_badarg at some point but then %% returns a value that isn't an exception call_dirty_nif_exception(0), - ?t:fail(expected_badarg) + ct:fail(expected_badarg) catch error:badarg -> [{?MODULE,call_dirty_nif_exception,[0],_}|_] = @@ -1617,7 +1617,7 @@ nif_exception(Config) when is_list(Config) -> %% calls enif_make_badarg at some point but then tries to return a %% value that isn't an exception call_nif_exception(0), - ?t:fail(expected_badarg) + ct:fail(expected_badarg) catch error:badarg -> ok @@ -1630,21 +1630,21 @@ nif_nan_and_inf(Config) when is_list(Config) -> ensure_lib_loaded(Config), try call_nif_nan_or_inf(nan), - ?t:fail(expected_badarg) + ct:fail(expected_badarg) catch error:badarg -> ok end, try call_nif_nan_or_inf(inf), - ?t:fail(expected_badarg) + ct:fail(expected_badarg) catch error:badarg -> ok end, try call_nif_nan_or_inf(tuple), - ?t:fail(expected_badarg) + ct:fail(expected_badarg) catch error:badarg -> ok @@ -1654,14 +1654,14 @@ nif_atom_too_long(Config) when is_list(Config) -> ensure_lib_loaded(Config), try call_nif_atom_too_long(all), - ?t:fail(expected_badarg) + ct:fail(expected_badarg) catch error:badarg -> ok end, try call_nif_atom_too_long(len), - ?t:fail(expected_badarg) + ct:fail(expected_badarg) catch error:badarg -> ok @@ -1721,9 +1721,7 @@ verify_tmpmem(MemInfo) -> ok end; Other -> - io:format("Expected: ~p", [MemInfo]), - io:format("Actual: ~p", [Other]), - ?t:fail() + ct:fail("Expected: ~p\nActual: ~p", [MemInfo, Other]) end. call(Pid,Cmd) -> @@ -1755,7 +1753,7 @@ nif_raise_exceptions(NifFunc) -> lists:foldl(fun(Term, ok) -> try erlang:apply(?MODULE,NifFunc,[Term]), - ?t:fail({expected,Term}) + ct:fail({expected,Term}) catch error:Term -> [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(), @@ -1787,7 +1785,7 @@ chk_mtime([TU|TUs]) -> true = B =< C catch _ : _ -> - ?t:fail({monotonic_time_missmatch, TU, A, B, C}) + ct:fail({monotonic_time_missmatch, TU, A, B, C}) end, chk_mtime(TUs). @@ -1812,7 +1810,7 @@ chk_toffs([TU|TUs]) -> false -> case erlang:system_info(time_warp_mode) of no_time_warp -> - ?t:fail({time_offset_mismatch, TU, TO, NifTO}); + ct:fail({time_offset_mismatch, TU, TO, NifTO}); _ -> %% Most frequent time offset change %% is currently only every 15:th @@ -1823,7 +1821,7 @@ chk_toffs([TU|TUs]) -> true -> ok; false -> - ?t:fail({time_offset_mismatch, TU, TO, NifTO, NTO}) + ct:fail({time_offset_mismatch, TU, TO, NifTO, NTO}) end end end, @@ -1900,7 +1898,7 @@ chk_ctu(Time, FromTU, [ToTU|ToTUs]) -> TN = convert_time_unit(T, FromTU, ToTU), case TE =:= TN of false -> - ?t:fail({conversion_mismatch, FromTU, T, ToTU, TE, TN}); + ct:fail({conversion_mismatch, FromTU, T, ToTU, TE, TN}); true -> chk_ctu(Time, FromTU, ToTUs) end. diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 30f75b4cbc..3bf1e207de 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -953,7 +953,7 @@ nc_refc_check(Node) when is_atom(Node) -> receive {Ref, ErrorMsg, failed} -> ?t:format("~s~n", [ErrorMsg]), - ?t:fail(reference_count_check_failed); + ct:fail(reference_count_check_failed); {Ref, succeded} -> ?t:format("Reference count check of node ~w succeded!~n", [Node]), ok @@ -1035,7 +1035,7 @@ get_node_references({NodeName, Creation} = Node) when is_atom(NodeName), DistRefs, fun (ErrMsg) -> ?t:format("~s", [ErrMsg]), - ?t:fail(reference_count_check_failed) + ct:fail(reference_count_check_failed) end), find_references(Node, NodeRefs). @@ -1047,7 +1047,7 @@ get_dist_references(NodeName) when is_atom(NodeName) -> DistRefs, fun (ErrMsg) -> ?line ?t:format("~s", [ErrMsg]), - ?line ?t:fail(reference_count_check_failed) + ?line ct:fail(reference_count_check_failed) end), ?line find_references(NodeName, DistRefs). diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl index d62928c31d..5fef53a564 100644 --- a/erts/emulator/test/old_scheduler_SUITE.erl +++ b/erts/emulator/test/old_scheduler_SUITE.erl @@ -117,7 +117,7 @@ equal(Config) when is_list(Config) -> %% runtime ratio between normal and low should be ~8 if Ratio < 7.5 ; Ratio > 8.5 -> - ?t:fail({bad_ratio,Ratio}); + ct:fail({bad_ratio,Ratio}); true -> ok(Config) end. @@ -148,7 +148,7 @@ many_low(Config) when is_list(Config) -> io:format("Reports: ~w normal (~w/proc), ~w low (~w/proc). Ratio: ~w~n", [NRs,NAvg,LRs,LAvg,Ratio]), if Ratio < 7.5 ; Ratio > 8.5 -> - ?t:fail({bad_ratio,Ratio}); + ct:fail({bad_ratio,Ratio}); true -> ok(Config) end. @@ -179,7 +179,7 @@ few_low(Config) when is_list(Config) -> io:format("Reports: ~w normal (~w/proc), ~w low (~w/proc). Ratio: ~w~n", [NRs,NAvg,LRs,LAvg,Ratio]), if Ratio < 7.0 ; Ratio > 8.5 -> - ?t:fail({bad_ratio,Ratio}); + ct:fail({bad_ratio,Ratio}); true -> ok(Config) end. @@ -214,7 +214,7 @@ max(Config) when is_list(Config) -> io:format("Reports: ~w max (~w/proc), ~w high (~w/proc). Ratio: ~w~n", [M1Rs,M1Avg,HRs,HAvg,Ratio1]), if Ratio1 < 1.0 -> - ?t:fail({bad_ratio,Ratio1}); + ct:fail({bad_ratio,Ratio1}); true -> ok(Config) end, @@ -232,7 +232,7 @@ max(Config) when is_list(Config) -> io:format("Reports: ~w max (~w/proc), ~w normal (~w/proc). Ratio: ~w~n", [M2Rs,M2Avg,NRs,NAvg,Ratio2]), if Ratio2 < 1.0 -> - ?t:fail({bad_ratio,Ratio2}); + ct:fail({bad_ratio,Ratio2}); true -> ok end, @@ -250,7 +250,7 @@ max(Config) when is_list(Config) -> io:format("Reports: ~w max (~w/proc), ~w low (~w/proc). Ratio: ~w~n", [M3Rs,M3Avg,LRs,LAvg,Ratio3]), if Ratio3 < 1.0 -> - ?t:fail({bad_ratio,Ratio3}); + ct:fail({bad_ratio,Ratio3}); true -> ok(Config) end. @@ -284,7 +284,7 @@ high(Config) when is_list(Config) -> io:format("Reports: ~w high (~w/proc), ~w normal (~w/proc). Ratio: ~w~n", [H1Rs,H1Avg,NRs,NAvg,Ratio1]), if Ratio1 < 1.0 -> - ?t:fail({bad_ratio,Ratio1}); + ct:fail({bad_ratio,Ratio1}); true -> ok end, @@ -302,7 +302,7 @@ high(Config) when is_list(Config) -> io:format("Reports: ~w high (~w/proc), ~w low (~w/proc). Ratio: ~w~n", [H2Rs,H2Avg,LRs,LAvg,Ratio2]), if Ratio2 < 1.0 -> - ?t:fail({bad_ratio,Ratio2}); + ct:fail({bad_ratio,Ratio2}); true -> ok(Config) end. diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl index 9948c13ce2..a12066f9fa 100644 --- a/erts/emulator/test/op_SUITE.erl +++ b/erts/emulator/test/op_SUITE.erl @@ -296,7 +296,7 @@ run_function(Mod, Name) -> case catch Mod:Name() of {'EXIT',Reason} -> io:format("~p", [get(last)]), - ?t:fail({'EXIT',Reason}); + ct:fail({'EXIT',Reason}); _Other -> ok end. diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 365c63b468..25452598e3 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -261,7 +261,7 @@ bad_packet(PortTest, HeaderSize, PacketSize) -> P ! {self(), {command, make_zero_packet(PacketSize)}}, receive {'EXIT', P, einval} -> ok; - Other -> test_server:fail({unexpected_message, Other}) + Other -> ct:fail({unexpected_message, Other}) end. make_zero_packet(0) -> []; @@ -288,7 +288,7 @@ bad_message(PortTest, Message) -> P ! Message, receive {'EXIT',P,badsig} -> ok; - Other -> test_server:fail({unexpected_message, Other}) + Other -> ct:fail({unexpected_message, Other}) end. %% Tests various options (stream and {packet, Number} are implicitly @@ -448,7 +448,7 @@ wait_for(Pids) -> {'EXIT', Pid, normal} -> wait_for(lists:delete(Pid, Pids)); Other -> - test_server:fail({bad_exit, Other}) + ct:fail({bad_exit, Other}) end. %% Tests starting port programs that terminate by themselves. @@ -708,10 +708,10 @@ open_ports(Name, Settings) -> enomem -> []; Other -> - test_server:fail({open_ports, Other}) + ct:fail({open_ports, Other}) end; Other -> - test_server:fail({open_ports, Other}) + ct:fail({open_ports, Other}) end. %% Tests that exit(Port, Term) works (has been known to crash the emulator). @@ -724,7 +724,7 @@ t_exit(Config) when is_list(Config) -> {'EXIT', Pid, die} -> ok; Other -> - test_server:fail({bad_message, Other}) + ct:fail({bad_message, Other}) end. suicide_port(Config) when is_list(Config) -> @@ -760,7 +760,7 @@ tps(Port, Packet, N) -> {Port, {data, Packet}} -> tps(Port, Packet, N-1); Other -> - test_server:fail({bad_message, Other}) + ct:fail({bad_message, Other}) end. %% Line I/O test @@ -893,10 +893,9 @@ env_slave(File, Env, Body) -> {Port,{data,{eol,"ok"}}} -> ok; {Port,{data,{eol,Error}}} -> - io:format("~p\n", [Error]), - test_server:fail(); + ct:fail("eol error ~p\n", [Error]); Other -> - test_server:fail(Other) + ct:fail(Other) end. env_slave_main([File]) -> @@ -989,10 +988,10 @@ cd(Config) when is_list(Config) -> true -> ok; false -> - test_server:fail({cd, String}) + ct:fail({cd, String}) end; Other2 -> - test_server:fail({env, Other2}) + ct:fail({env, Other2}) end, _ = open_port({spawn, Cmd}, [{cd, unicode:characters_to_binary(TestDir)}, @@ -1003,10 +1002,10 @@ cd(Config) when is_list(Config) -> true -> ok; false -> - test_server:fail({cd, String2}) + ct:fail({cd, String2}) end; Other3 -> - test_server:fail({env, Other3}) + ct:fail({env, Other3}) end, ok. @@ -1104,7 +1103,7 @@ otp_3906(Config, OSName) -> succeded -> ok; _ -> - test_server:fail(Result) + ct:fail(Result) end; _ -> {skipped, "No C compiler found"} @@ -1253,7 +1252,7 @@ otp_4389(Config) when is_list(Config) -> fun (P) -> receive {P, ok} -> ok; - {P, Err} -> ?t:fail(Err) + {P, Err} -> ct:fail(Err) end end, lists:map( @@ -1340,7 +1339,7 @@ spawn_driver(Config) when is_list(Config) -> io:format("~p~n", [Msg1]), ok; Other -> - test_server:fail({unexpected, Other}) + ct:fail({unexpected, Other}) end, Port ! {self(), close}, receive {Port, closed} -> ok end, @@ -1352,7 +1351,7 @@ spawn_driver(Config) when is_list(Config) -> io:format("~p~n", [Msg2]), ok; Other2 -> - test_server:fail({unexpected2, Other2}) + ct:fail({unexpected2, Other2}) end, Port2 ! {self(), close}, receive {Port2, closed} -> ok end, @@ -1377,7 +1376,7 @@ parallelism_option(Config) when is_list(Config) -> io:format("~p~n", [Msg1]), ok; Other -> - test_server:fail({unexpected, Other}) + ct:fail({unexpected, Other}) end, ?line Port ! {self(), close}, ?line receive {Port, closed} -> ok end, @@ -1390,7 +1389,7 @@ parallelism_option(Config) when is_list(Config) -> io:format("~p~n", [Msg2]), ok; Other2 -> - test_server:fail({unexpected2, Other2}) + ct:fail({unexpected2, Other2}) end, ?line Port2 ! {self(), close}, ?line receive {Port2, closed} -> ok end, @@ -1650,7 +1649,7 @@ mix_up_ports(Config) when is_list(Config) -> io:format("~p~n", [Msg1]), ok; Other -> - test_server:fail({unexpected, Other}) + ct:fail({unexpected, Other}) end, Port ! {self(), close}, receive {Port, closed} -> ok end, @@ -1669,7 +1668,7 @@ mix_up_ports(Config) when is_list(Config) -> Port ! {self(), {command, "Hello again port!"}}, receive Msg2 -> - test_server:fail({unexpected, Msg2}) + ct:fail({unexpected, Msg2}) after 1000 -> ok end, @@ -1794,7 +1793,7 @@ otp_6224(Config) when is_list(Config) -> case Reason of {driver_failed, _} -> ok; driver_failed -> ok; - _ -> ?t:fail({unexpected_exit_reason, + _ -> ct:fail({unexpected_exit_reason, Reason}) end end, @@ -1873,7 +1872,7 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> Err == enomem -> noop; Error -> - ?t:fail(Error) + ct:fail(Error) end end, lists:seq(1, NoPortsPerProc)), @@ -1904,17 +1903,17 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> PrtSIds), Parent ! {self(), done} end, - Procs = lists:map(fun (N) -> - spawn_opt(ProcFun, - [link, - {scheduler, - (N rem NoSchedsOnln)+1}]) - end, - lists:seq(1, NoProcs)), + Procs = lists:map(fun (N) -> + spawn_opt(ProcFun, + [link, + {scheduler, + (N rem NoSchedsOnln)+1}]) + end, + lists:seq(1, NoProcs)), SIds = lists:map(fun (P) -> - receive {P, started, SIds} -> SIds end - end, - Procs), + receive {P, started, SIds} -> SIds end + end, + Procs), StartedTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000, ?t:format("StartedTime = ~p~n", [StartedTime]), true = StartedTime < SleepSecs, @@ -1929,10 +1928,8 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> {N, N} -> ok; {N, M} -> - ?t:fail("Failed to create ports on all" - ++ integer_to_list(M) ++ " available" - "schedulers. Only created ports on " - ++ integer_to_list(N) ++ " schedulers.") + ct:fail("Failed to create ports on all ~w available" + "schedulers. Only created ports on ~w schedulers.", [M, N]) end. save_sid(SIds) -> @@ -1953,15 +1950,15 @@ sid_proc(SIds) -> verify_multi_scheduling_blocked() -> Procs = lists:map(fun (_) -> - spawn_link(fun () -> sid_proc([]) end) - end, - lists:seq(1, 3*erlang:system_info(schedulers_online))), + spawn_link(fun () -> sid_proc([]) end) + end, + lists:seq(1, 3*erlang:system_info(schedulers_online))), receive after 1000 -> ok end, SIds = lists:map(fun (P) -> - P ! {self(), want_sids}, - receive {P, sids, PSIds} -> PSIds end - end, - Procs), + P ! {self(), want_sids}, + receive {P, sids, PSIds} -> PSIds end + end, + Procs), 1 = length(lists:usort(lists:flatten(SIds))), ok. @@ -2085,7 +2082,7 @@ receive_all(Port, [Expect|Rest]) -> {Port, {data, Other}} -> io:format("Received ~s; expected ~s", [format(Other), format(Expect)]), - test_server:fail(bad_message); + ct:fail(bad_message); Other -> %% (We're not yet prepared for receiving both 'eol' and %% 'exit_status'; remember that they may appear in any order.) @@ -2099,7 +2096,7 @@ receive_all(Port, [Expect|Rest]) -> _ -> %%% io:format("Unexpected message: ~s", [format(Other)]), io:format("Unexpected message: ~w", [Other]), - test_server:fail(unexpected_message) + ct:fail(unexpected_message) end end, receive_all(Port, Rest); @@ -2119,7 +2116,7 @@ stream_receive_all1(Port, Expect) -> Remaining = compare(Data, Expect), stream_receive_all1(Port, Remaining); Other -> - test_server:fail({bad_message, Other}) + ct:fail({bad_message, Other}) end. compare(B1, B2) when is_binary(B1), is_binary(B2), byte_size(B1) =< byte_size(B2) -> @@ -2127,18 +2124,18 @@ compare(B1, B2) when is_binary(B1), is_binary(B2), byte_size(B1) =< byte_size(B2 {B1,Remaining} -> Remaining; _Other -> - test_server:fail(nomatch) + ct:fail(nomatch) end; compare(B1, B2) when is_binary(B1), is_binary(B2) -> - test_server:fail(too_much_data); + ct:fail(too_much_data); compare([X|Rest1], [X|Rest2]) -> compare(Rest1, Rest2); compare([_|_], [_|_]) -> - test_server:fail(nomatch); + ct:fail(nomatch); compare([], Remaining) -> Remaining; compare(_Data, []) -> - test_server:fail(too_much_data). + ct:fail(too_much_data). maybe_to_list(Bin) when is_binary(Bin) -> binary_to_list(Bin); @@ -2324,7 +2321,7 @@ ports_verify(Ports, PortsAfter, EventList) -> ports_verify(Ports, [P | PortsAfter], Tail); [] -> - test_server:fail("Inconsistent snapshot from erlang:ports()") + ct:fail("Inconsistent snapshot from erlang:ports()") end end. diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index c7659eed9e..bdadfc48ac 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -66,10 +66,10 @@ do_command(P, Data) -> {P,{data,Data0}} -> case {list_to_binary(Data0),list_to_binary([Data])} of {B,B} -> ok; - _ -> test_server:fail({unexpected_data,Data0}) + _ -> ct:fail({unexpected_data,Data0}) end; Other -> - test_server:fail({unexpected_message,Other}) + ct:fail({unexpected_message,Other}) end. @@ -85,9 +85,9 @@ command_e_1(Config) when is_list(Config) -> {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> ok; Other -> - test_server:fail(Other) + ct:fail(Other) after 10000 -> - test_server:fail(timeout) + ct:fail(timeout) end, ok. @@ -107,9 +107,9 @@ command_e_2(Config) when is_list(Config) -> {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> ok; Other -> - test_server:fail(Other) + ct:fail(Other) after 10000 -> - test_server:fail(timeout) + ct:fail(timeout) end, ok. @@ -131,9 +131,9 @@ command_e_3(Config) when is_list(Config) -> {'EXIT', Port, einval} when is_port(Port) -> ok; Other -> - test_server:fail(Other) + ct:fail(Other) after 10000 -> - test_server:fail(timeout) + ct:fail(timeout) end, ok. @@ -148,9 +148,9 @@ command_e_4(Config) when is_list(Config) -> {'EXIT', Pid, {einval, _}} when is_pid(Pid) -> ok; Other -> - test_server:fail(Other) + ct:fail(Other) after 10000 -> - test_server:fail(timeout) + ct:fail(timeout) end, ok. @@ -227,7 +227,7 @@ do_port_info_os_pid() -> {os_pid, InfoOSPid} = erlang:port_info(P, os_pid), EchoPidStr = receive {P, {data, EchoPidStr0}} -> EchoPidStr0 - after 10000 -> test_server:fail(timeout) + after 10000 -> ct:fail(timeout) end, {ok, [EchoPid], []} = io_lib:fread("~u\n", EchoPidStr), {value,{os_pid, InfoOSPid}}=lists:keysearch(os_pid, 1, A), @@ -262,10 +262,9 @@ output_test(_, _, Input, Output) when Output > 16#1fffffff -> output_test(P, Bin, Input0, Output0) -> erlang:port_command(P, Bin), receive - {P,{data,Bin}} -> ok; - Other -> - io:format("~p", [Other]), - ?t:fail() + {P,{data,Bin}} -> ok; + Other -> + ct:fail("~p", [Other]) end, Input = Input0 + size(Bin), Output = Output0 + size(Bin), @@ -275,8 +274,8 @@ output_test(P, Bin, Input0, Output0) -> %% We can't test much here, but hopefully a debug-built emulator will crasch %% if there is something wrong with the heap allocation. case erlang:statistics(io) of - {{input,In},{output,Out}} when is_integer(In), is_integer(Out) -> - ok + {{input,In},{output,Out}} when is_integer(In), is_integer(Out) -> + ok end, output_test(P, Bin, Input, Output). @@ -324,7 +323,7 @@ connect(Config) when is_list(Config) -> exit(P, you_should_die), receive {'EXIT',RecPid,you_should_die} -> ok; - Other -> ?line ?t:fail({bad_message,Other}) + Other -> ct:fail({bad_message,Other}) end, %% Done. @@ -401,9 +400,9 @@ echo_to_busy(Config) when is_list(Config) -> {Echoer, done} -> ok; {Echoer, Other} -> - test_server:fail(Other); + ct:fail(Other); Other -> - test_server:fail({unexpected_message, Other}) + ct:fail({unexpected_message, Other}) end, ok. diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index d0af5f39b1..1ee57f07b5 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -198,13 +198,13 @@ t_exit_2_other_normal(Config) when is_list(Config) -> exit(Pid, normal), receive {'EXIT', Pid, Reason} -> - test_server:fail({process_died, Reason}) + ct:fail({process_died, Reason}) after 1000 -> ok end, case process_info(Pid) of undefined -> - test_server:fail(process_died_on_normal); + ct:fail(process_died_on_normal); List when is_list(List) -> ok end, @@ -236,7 +236,7 @@ normal_suicide_exit(Config) when is_list(Config) -> Pid = fun_spawn(fun() -> exit(self(), normal) end), receive {'EXIT', Pid, normal} -> ok; - Other -> test_server:fail({bad_message, Other}) + Other -> ct:fail({bad_message, Other}) end. %% Tests exit(self(), Term) is equivalent to exit(Term) for a process @@ -247,7 +247,7 @@ abnormal_suicide_exit(Config) when is_list(Config) -> Pid = fun_spawn(fun() -> exit(self(), Garbage) end), receive {'EXIT', Pid, Garbage} -> ok; - Other -> test_server:fail({bad_message, Other}) + Other -> ct:fail({bad_message, Other}) end. %% Tests that exit(self(), die) cannot be catched. @@ -256,11 +256,11 @@ t_exit_2_catch(Config) when is_list(Config) -> Pid = fun_spawn(fun() -> catch exit(self(), die) end), receive {'EXIT', Pid, normal} -> - test_server:fail(catch_worked); + ct:fail(catch_worked); {'EXIT', Pid, die} -> ok; Other -> - test_server:fail({bad_message, Other}) + ct:fail({bad_message, Other}) end. %% Tests trapping of an 'EXIT' message generated by a bad argument to @@ -283,7 +283,7 @@ trap_exit_badarg() -> ok; Other -> ok = io:format("Bad EXIT message: ~P", [Other, 30]), - test_server:fail(bad_exit_message) + ct:fail(bad_exit_message) end. bad_guy(Arg) -> @@ -326,7 +326,7 @@ trap_exit_badarg_bif() -> {'EXIT', Pid, {badarg, _}} -> ok; Other -> - test_server:fail({unexpected, Other}) + ct:fail({unexpected, Other}) end. %% The following sequences of events have crasched Beam. @@ -356,7 +356,7 @@ eat_wait_for(Low, High) -> {'EXIT', High, normal} -> eat_wait_for(Low, High); Other -> - test_server:fail({bad_message, Other}) + ct:fail({bad_message, Other}) end. eat_low(_Parent) -> @@ -402,11 +402,11 @@ etwice_wait_for(Low, High) -> {'EXIT', Low, first} -> ok; {'EXIT', Low, Other} -> - test_server:fail({wrong_exit_reason, Other}); + ct:fail({wrong_exit_reason, Other}); {'EXIT', High, normal} -> etwice_wait_for(Low, High); Other -> - test_server:fail({bad_message, Other}) + ct:fail({bad_message, Other}) end. etwice_low() -> @@ -731,7 +731,7 @@ process_info_lock_reschedule(Config) when is_list(Config) -> exit(Target2, bang), OkStatus; {status, BadStatus} -> - ?t:fail(BadStatus) + ct:fail(BadStatus) end. pi_loop(_Name, _Pid, 0) -> @@ -833,7 +833,7 @@ process_info_lock_reschedule3(Config) when is_list(Config) -> exit(Target2, bang), OkStatus; {status, BadStatus} -> - ?t:fail(BadStatus) + ct:fail(BadStatus) end. process_status_exiting(Config) when is_list(Config) -> @@ -977,10 +977,10 @@ bump_reductions(Config) when is_list(Config) -> case R2-R1 of Diff when Diff < 100 -> ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]), - test_server:fail({small_diff, Diff}); + ct:fail({small_diff, Diff}); Diff when Diff > 110 -> ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]), - test_server:fail({big_diff, Diff}); + ct:fail({big_diff, Diff}); Diff -> io:format("~p\n", [Diff]), ok @@ -1109,7 +1109,7 @@ yield_test() -> {Diff, _} -> ok = io:format("R1 = ~w, R2 = ~w, Schedcnt = ~w", [R1, R2, Schedcnt]), - test_server:fail({measurement_error, Diff, Schedcnt}) + ct:fail({measurement_error, Diff, Schedcnt}) end. call_yield() -> @@ -1198,7 +1198,7 @@ yield2(Config) when is_list(Config) -> io:format("Reductions = ~p~n", [Reductions]), ok; {RedDiff, Reductions} -> - ?t:fail({unexpected_reduction_count, Reductions}) + ct:fail({unexpected_reduction_count, Reductions}) end, none = next_tmsg(P), @@ -1460,7 +1460,7 @@ processes_large_tab_test(Config) -> #ptab_list_bif_info{debug_level = Lvl} when Lvl > MaxDbgLvl -> 20; #ptab_list_bif_info{debug_level = Lvl} when Lvl < 0 -> - ?t:fail({debug_level, Lvl}); + ct:fail({debug_level, Lvl}); #ptab_list_bif_info{debug_level = Lvl} -> Lvl end, @@ -1478,7 +1478,7 @@ processes_large_tab_test(Config) -> [processes_bif_info]) of #ptab_list_bif_info{tab_chunks = Chunks} when is_integer(Chunks), Chunks > 1 -> ok; - PBInfo -> ?t:fail(PBInfo) + PBInfo -> ct:fail(PBInfo) end, stop_node(LargeNode), chk_processes_bif_test_res(Res). @@ -1525,7 +1525,7 @@ processes_this_tab(Config) when is_list(Config) -> chk_processes_bif_test_res(ok) -> ok; chk_processes_bif_test_res({comment, _} = Comment) -> Comment; -chk_processes_bif_test_res(Failure) -> ?t:fail(Failure). +chk_processes_bif_test_res(Failure) -> ct:fail(Failure). print_processes_bif_info(#ptab_list_bif_info{min_start_reds = MinStartReds, tab_chunks = TabChunks, @@ -1596,7 +1596,7 @@ processes_unexpected_result(CorrectProcs, Procs) -> end] end, SuperfluousProcs)]), - ?t:fail(unexpected_result). + ct:fail(unexpected_result). hangaround(Cleaner, Type) -> %% Type is only used to distinguish different processes from @@ -2229,7 +2229,7 @@ do_otp_7738_test(Type) -> after 2000 -> I = process_info(R, [status, message_queue_len]), ?t:format("~p~n", [I]), - ?t:fail(no_progress) + ct:fail(no_progress) end, ok. @@ -2316,7 +2316,7 @@ no_priority_inversion2(Config) when is_list(Config) -> RH = request_gc(PL, high), receive {garbage_collect, _, _} -> - ?t:fail(unexpected_gc) + ct:fail(unexpected_gc) after 1000 -> ok end, @@ -2425,7 +2425,7 @@ gc_request_when_gc_disabled(Config) when is_list(Config) -> async = garbage_collect(P, [{async, ReqId}]), receive {garbage_collect, ReqId, Result} -> - ?t:fail({unexpected_gc, Result}); + ct:fail({unexpected_gc, Result}); {P, gc_state, true} -> ok end, diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl index d471bbecc9..6097e54219 100644 --- a/erts/emulator/test/receive_SUITE.erl +++ b/erts/emulator/test/receive_SUITE.erl @@ -55,8 +55,7 @@ call_with_huge_message_queue(Config) when is_list(Config) -> Q when Q < 10 -> ok; Q -> - io:format("Best Q = ~p", [Q]), - ?t:fail() + ct:fail("Best Q = ~p", [Q]) end, ok. diff --git a/erts/emulator/test/ref_SUITE.erl b/erts/emulator/test/ref_SUITE.erl index 1bf94b9a3f..e84d53a89e 100644 --- a/erts/emulator/test/ref_SUITE.erl +++ b/erts/emulator/test/ref_SUITE.erl @@ -39,7 +39,7 @@ wrap_1(Config) when is_list(Config) -> ?line spawn_link(?MODULE, loop_ref, [self()]), ?line receive done -> - test_server:fail(wrapfast) + ct:fail(wrapfast) after 30000 -> ok end, diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl index b40a5f0a56..fdf6e6fda0 100644 --- a/erts/emulator/test/save_calls_SUITE.erl +++ b/erts/emulator/test/save_calls_SUITE.erl @@ -151,7 +151,7 @@ save_calls_1() -> {?MODULE,do_bepp,0}] -> ok; X -> - test_server:fail({l21, X}) + ct:fail({l21, X}) end, ?line erlang:process_flag(self(), save_calls, 10), diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 03e546c00d..776bf172ee 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -821,7 +821,7 @@ update_cpu_info(Config) when is_list(Config) -> ?line unchanged = adjust_schedulers_online(), ?line ok; Fail -> - ?line ?t:fail(Fail) + ?line ct:fail(Fail) end end after @@ -1896,7 +1896,7 @@ chk_result([{low, L, Lmin, _Lmax}, chk_lim(Min, V, Max, _What) when Min =< V, V =< Max -> ok; chk_lim(_Min, V, _Max, What) -> - ?t:fail({bad, What, V}). + ct:fail({bad, What, V}). snd(_Msg, []) -> []; @@ -2259,7 +2259,7 @@ cmp_aux(T0, T1) when is_tuple(T0), is_tuple(T1), size(T0) == size(T1) -> cmp_aux(X, X) -> ok; cmp_aux(F0, F1) -> - ?t:fail({no_match, F0, F1}). + ct:fail({no_match, F0, F1}). cmp_tuple(_T0, _T1, N, Sz) when N > Sz -> ok; diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index de453e090a..1915b44b4b 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -127,8 +127,7 @@ basic(Config) when is_list(Config) -> ?line [] = term(P, 127), ?line receive Any -> - ?line io:format("Unexpected: ~p\n", [Any]), - ?line ?t:fail() + ct:fail("Unexpected: ~p\n", [Any]) after 0 -> ok end, @@ -198,7 +197,7 @@ stop_driver(Port, Name) -> ?line true = erlang:port_close(Port), receive {Port,Message} -> - ?t:fail({strange_message_from_port,Message}) + ct:fail({strange_message_from_port,Message}) after 0 -> ok end, diff --git a/erts/emulator/test/sensitive_SUITE.erl b/erts/emulator/test/sensitive_SUITE.erl index a5cc25f55a..618044c153 100644 --- a/erts/emulator/test/sensitive_SUITE.erl +++ b/erts/emulator/test/sensitive_SUITE.erl @@ -65,7 +65,7 @@ stickiness(Config) when is_list(Config) -> case process_info(self(), dictionary) of {dictionary,[]} -> ok; - {dictionary,_} -> ?line ?t:fail(sensitive_flag_cleared) + {dictionary,_} -> ?line ct:fail(sensitive_flag_cleared) end, NewTracer = spawn_link(fun() -> receive after infinity -> ok end end), diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index 63996fb9f2..e6ec79141b 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -200,7 +200,7 @@ pending_exit_test(From, Type) -> {Pid, Ref, Type} -> ?line ok after 0 -> - ?line ?t:fail(premature_exit) + ?line ct:fail(premature_exit) end, ?line case Type of exit -> @@ -275,7 +275,7 @@ exit_before_pending_exit(Config) when is_list(Config) -> ?line process_flag(trap_exit, OTE), ?line ok; Msg -> - ?line ?t:fail({unexpected_message, Msg}) + ?line ct:fail({unexpected_message, Msg}) end, NoScheds = integer_to_list(erlang:system_info(schedulers_online)), {comment, diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 751129c7bb..7e5345fd49 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -97,7 +97,7 @@ native_atomics(Config) when is_list(Config) -> {value,{DWNAKey, DWNA, _}} = lists:keysearch(DWNAKey, 1, EthreadInfo), case {erlang:system_info(build_type), erlang:system_info(smp_support), NA32, NA64, DWNA} of {opt, true, "no", "no", _} -> - ?t:fail(optimized_smp_runtime_without_native_atomics); + ct:fail(optimized_smp_runtime_without_native_atomics); {_, false, "no", "no", _} -> {comment, "No native atomics"}; _ -> @@ -114,7 +114,7 @@ jump_table(Config) when is_list(Config) -> false -> case erlang:system_info(build_type) of opt -> - ?t:fail(optimized_without_beam_jump_table); + ct:fail(optimized_without_beam_jump_table); BT -> {comment, "No beam jump table, but build type is " ++ atom_to_list(BT)} end diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index 12b35cbcdf..b99d567772 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -91,7 +91,7 @@ wall_clock_zero_diff1(N) when N > 0 -> _ -> wall_clock_zero_diff1(N-1) end; wall_clock_zero_diff1(0) -> - ?line test_server:fail("Difference never zero."). + ct:fail("Difference never zero."). wall_clock_update(doc) -> "Test that the time differences returned by two calls to " @@ -136,7 +136,7 @@ runtime_zero_diff1(N) when N > 0 -> _ -> runtime_zero_diff1(N-1) end; runtime_zero_diff1(0) -> - ?line test_server:fail("statistics(runtime) never returned zero difference"). + ct:fail("statistics(runtime) never returned zero difference"). runtime_update(doc) -> "Test that the statistics(runtime) returns a substanstially " @@ -272,7 +272,7 @@ run_queue_one_test(Config) when is_list(Config) -> ?line receive after 100 -> ok end, % Give hog a head start. ?line case statistics(run_queue) of N when N >= 1 -> ok; - Other -> ?line ?t:fail({unexpected,Other}) + Other -> ct:fail({unexpected,Other}) end, ok. diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl index 4ac48872df..0becf88f7a 100644 --- a/erts/emulator/test/system_profile_SUITE.erl +++ b/erts/emulator/test/system_profile_SUITE.erl @@ -452,7 +452,7 @@ check_ts(no_timestamp, Ts) -> no_timestamp = Ts catch _ : _ -> - ?t:fail({unexpected_timestamp, Ts}) + ct:fail({unexpected_timestamp, Ts}) end, ok; check_ts(timestamp, Ts) -> @@ -463,7 +463,7 @@ check_ts(timestamp, Ts) -> true = is_integer(Us) catch _ : _ -> - ?t:fail({unexpected_timestamp, Ts}) + ct:fail({unexpected_timestamp, Ts}) end, ok; check_ts(monotonic_timestamp, Ts) -> @@ -471,7 +471,7 @@ check_ts(monotonic_timestamp, Ts) -> true = is_integer(Ts) catch _ : _ -> - ?t:fail({unexpected_timestamp, Ts}) + ct:fail({unexpected_timestamp, Ts}) end, ok; check_ts(strict_monotonic_timestamp, Ts) -> @@ -481,7 +481,7 @@ check_ts(strict_monotonic_timestamp, Ts) -> true = is_integer(UMI) catch _ : _ -> - ?t:fail({unexpected_timestamp, Ts}) + ct:fail({unexpected_timestamp, Ts}) end, ok. diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index f6f99a0c81..c27de24690 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -334,7 +334,7 @@ timestamp(Config) when is_list(Config) -> true -> {skip, "Seems to be time warp test run..."}; false -> - test_server:fail(Failure) + ct:fail(Failure) end end. @@ -368,9 +368,7 @@ repeating_timestamp_check(N) -> C < 1000000 -> ok; true -> - test_server:fail( - lists:flatten( - io_lib:format("Strange return from os:timestamp/0 ~w~n",[TS]))) + ct:fail("Strange return from os:timestamp/0 ~w~n",[TS]) end, %% I assume the now and timestamp should not differ more than 1 hour, %% which is safe assuming the system has not had a large time-warp @@ -409,7 +407,7 @@ now_unique(Config) when is_list(Config) -> now_unique(N, Previous, Result) when N > 0 -> ?line case now() of Previous -> - test_server:fail("now/0 returned the same value twice"); + ct:fail("now/0 returned the same value twice"); New -> now_unique(N-1, New, [New|Result]) end; @@ -426,7 +424,7 @@ fast_now_unique(0, _) -> ok; fast_now_unique(N, Then) -> case now() of Then -> - ?line ?t:fail("now/0 returned the same value twice"); + ?line ct:fail("now/0 returned the same value twice"); Now -> fast_now_unique(N-1, Now) end. @@ -474,7 +472,7 @@ now_update1(N) when N > 0 -> _ -> now_update1(N-1) end; now_update1(0) -> - ?line test_server:fail(). + ct:fail("now_update zero"). time_warp_modes(Config) when is_list(Config) -> %% All time warp modes always supported in @@ -551,14 +549,14 @@ check_time_warp_mode(Config, TimeCorrection, TimeWarpMode) -> io:format("Uptime inconsistency", []), case {TimeCorrection, erlang:system_info(time_correction)} of {true, true} -> - ?t:fail(uptime_inconsistency); + ct:fail(uptime_inconsistency); {true, false} -> _ = erlang:time_offset(), receive {'CHANGE', Mon, time_offset, clock_service, _} -> ignore after 1000 -> - ?t:fail(uptime_inconsistency) + ct:fail(uptime_inconsistency) end; _ -> ignore @@ -728,7 +726,7 @@ check_time_offset_res_conv(Mon, Res) -> TORes2 -> case check_time_offset_change(Mon, TO, 1000) of {TO, false} -> - ?t:fail({time_unit_conversion_inconsistency, + ct:fail({time_unit_conversion_inconsistency, TO, TORes, TORes2}); {_NewTO, true} -> ?t:format("time_offset changed", []), @@ -787,7 +785,7 @@ chk_random_values(FR, TR) -> {true, true} -> ok; Failure -> - ?t:fail({Failure, CV, V, FR, TR}) + ct:fail({Failure, CV, V, FR, TR}) end end, lists:foreach(CheckFun, Values). @@ -801,7 +799,7 @@ chk_values_per_value(_FromRes, _ToRes, case ((MinFromValuesPerToValue =< FromValueCount) andalso (FromValueCount =< MaxFromValuesPerToValue)) of false -> - ?t:fail({MinFromValuesPerToValue, + ct:fail({MinFromValuesPerToValue, FromValueCount, MaxFromValuesPerToValue}); true -> @@ -821,7 +819,7 @@ chk_values_per_value(FromRes, ToRes, Value, EndValue, case ((MinFromValuesPerToValue =< FromValueCount) andalso (FromValueCount =< MaxFromValuesPerToValue)) of false -> - ?t:fail({MinFromValuesPerToValue, + ct:fail({MinFromValuesPerToValue, FromValueCount, MaxFromValuesPerToValue}); true -> @@ -878,7 +876,7 @@ do_check_erlang_timestamp(Done, Mon, TO) -> "checking for time_offset change...", []), case check_time_offset_change(Mon, TO, 1000) of {TO, false} -> - ?t:fail(timestamp_inconsistency); + ct:fail(timestamp_inconsistency); {NewTO, true} -> ?t:format("time_offset changed", []), check_erlang_timestamp(Done, Mon, NewTO) diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index dca44a1891..adc25b9426 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -108,7 +108,7 @@ start_timer_big(Config) when is_list(Config) -> Diff when Diff >= 200, Diff < 10000 -> ok; _Diff -> - test_server:fail({big, Big, Left}) + ct:fail({big, Big, Left}) end, ok. @@ -122,7 +122,7 @@ send_after_big(Config) when is_list(Config) -> Diff when Diff >= 200, Diff < 10000 -> ok; _Diff -> - test_server:fail({big, Big, Left}) + ct:fail({big, Big, Left}) end, ok. @@ -232,7 +232,7 @@ read_timer(Config) when is_list(Config) -> Diff when Diff >= 200, Diff < 10000 -> ok; _Diff -> - test_server:fail({big, Big, Left}) + ct:fail({big, Big, Left}) end, process_flag(scheduler, 0), ok. @@ -264,7 +264,7 @@ read_timer_async(Config) when is_list(Config) -> Diff when Diff >= 200, Diff < 10000 -> ok; _Diff -> - test_server:fail({big, Big, Left}) + ct:fail({big, Big, Left}) end, process_flag(scheduler, 0), ok. diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index b74d1b383a..5982383f13 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -524,7 +524,7 @@ do_system_monitor_long_schedule() -> {Self,L} when is_list(L) -> ok after 1000 -> - ?t:fail(no_trace_of_pid) + ct:fail(no_trace_of_pid) end, "ok" = erlang:port_control(Port,1,[]), "ok" = erlang:port_control(Port,2,[]), @@ -532,7 +532,7 @@ do_system_monitor_long_schedule() -> {Port,LL} when is_list(LL) -> ok after 1000 -> - ?t:fail(no_trace_of_port) + ct:fail(no_trace_of_port) end, port_close(Port), erlang:system_monitor(undefined), @@ -613,11 +613,11 @@ long_gc(LoadFun, ExpectMonMsg) -> {ok, true} when Pid =/= Self -> ok; {ok, false} -> - ?line ?t:fail(unexpected_system_monitor_message_received); + ?line ct:fail(unexpected_system_monitor_message_received); {undefined, false} -> ok; {undefined, true} -> - ?line ?t:fail(no_system_monitor_message_received) + ?line ct:fail(no_system_monitor_message_received) end. long_gc_check(Pid, Time, Result) -> @@ -707,11 +707,11 @@ large_heap(LoadFun, ExpectMonMsg) -> {ok, true} when Pid =/= Self -> ?line ok; {ok, false} -> - ?line ?t:fail(unexpected_system_monitor_message_received); + ?line ct:fail(unexpected_system_monitor_message_received); {undefined, false} -> ?line ok; {undefined, true} -> - ?line ?t:fail(no_system_monitor_message_received) + ?line ct:fail(no_system_monitor_message_received) end, ok. @@ -808,7 +808,7 @@ do_suspend(Pid, N) -> {status, runnable} -> ?line ok; {status, running} -> ?line ok; {status, garbage_collecting} -> ?line ok; - ST -> ?line ?t:fail(ST) + ST -> ?line ct:fail(ST) end, ?line erlang:yield(), ?line do_suspend(Pid, N-1). @@ -1070,7 +1070,7 @@ suspend_until_system_limit(P, N, M) -> ?line ?t:format("system limit at ~p~n", [N]), ?line resume_from_system_limit(P, N, 0); Error -> - ?line ?t:fail(Error) + ?line ct:fail(Error) end. resume_from_system_limit(P, 0, _) -> @@ -1221,7 +1221,7 @@ suspend_count(Suspender, Suspendee) -> case lists:keysearch(Suspendee, 1, SList) of {value, {_Suspendee, 0, 0}} -> - ?line ?t:fail({bad_suspendee_list, SList}); + ?line ct:fail({bad_suspendee_list, SList}); {value, {Suspendee, Count, 0}} when is_integer(Count), Count > 0 -> {status, suspended} = process_info(Suspendee, status), Count; @@ -1231,7 +1231,7 @@ suspend_count(Suspender, Suspendee) -> false -> 0; Error -> - ?line ?t:fail({bad_suspendee_list, Error, SList}) + ?line ct:fail({bad_suspendee_list, Error, SList}) end. repeat_acc(Fun, N, Acc) -> @@ -1318,7 +1318,7 @@ trace_delivered(Config) when is_list(Config) -> ?line NoOfTraceMessages = drop_trace_until_down(Tok, Mon), ?line receive Msg -> - ?line ?t:fail({unexpected_message, Msg}) + ?line ct:fail({unexpected_message, Msg}) after 1000 -> ?line ok end. @@ -1363,7 +1363,7 @@ receive_first() -> receive_nothing() -> receive Any -> - test_server:fail({unexpected_message, Any}) + ct:fail({unexpected_message, Any}) after 200 -> ok end. diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index ae98cc7189..e3e4e2e6d8 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -256,59 +256,49 @@ do_trace_bif_return(TsType, TsFlags) -> receive_trace_msg(Mess) -> receive - Mess -> - ok; - Other -> - io:format("Expected: ~p,~nGot: ~p~n", [Mess, Other]), - ?t:fail() + Mess -> + ok; + Other -> + ct:fail("Expected: ~p,~nGot: ~p~n", [Mess, Other]) after 5000 -> - io:format("Expected: ~p,~nGot: timeout~n", [Mess]), - ?t:fail() + ct:fail("Expected: ~p,~nGot: timeout~n", [Mess]) end. receive_trace_msg_ts({trace_ts, Pid, call, {erlang,F,A}}, PrevTs, TsType) -> receive - {trace_ts, Pid, call, {erlang, F, A}, Ts} -> - check_ts(TsType, PrevTs, Ts), - Ts; - Other -> - io:format("Expected: {trace, ~p, call, {~p, ~p, ~p}, TimeStamp}},~n" - "Got: ~p~n", - [Pid, erlang, F, A, Other]), - ?t:fail() - after 5000 -> - io:format("Got timeout~n", []), - ?t:fail() + {trace_ts, Pid, call, {erlang, F, A}, Ts} -> + check_ts(TsType, PrevTs, Ts), + Ts; + Other -> + ct:fail("Expected: {trace, ~p, call, {~p, ~p, ~p}, TimeStamp}},~n" + "Got: ~p~n", + [Pid, erlang, F, A, Other]) + after 5000 -> + ct:fail("Got timeout~n", []) end. receive_trace_msg_ts_return_from({trace_ts, Pid, return_from, {erlang,F,A}}, PrevTs, TsType) -> receive - {trace_ts, Pid, return_from, {erlang, F, A}, _Value, Ts} -> - check_ts(TsType, PrevTs, Ts), - Ts; - Other -> - io:format("Expected: {trace_ts, ~p, return_from, {~p, ~p, ~p}, Value, TimeStamp}},~n" - "Got: ~p~n", - [Pid, erlang, F, A, Other]), - ?t:fail() - after 5000 -> - io:format("Got timeout~n", []), - ?t:fail() + {trace_ts, Pid, return_from, {erlang, F, A}, _Value, Ts} -> + check_ts(TsType, PrevTs, Ts), + Ts; + Other -> + ct:fail("Expected: {trace_ts, ~p, return_from, {~p, ~p, ~p}, Value, TimeStamp}},~n" + "Got: ~p~n", [Pid, erlang, F, A, Other]) + after 5000 -> + ct:fail("Got timeout~n", []) end. receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}, PrevTs, TsType) -> receive - {trace_ts, Pid, return_to, {M, F, A}, Ts} -> - check_ts(TsType, PrevTs, Ts), - Ts; - Other -> - io:format("Expected: {trace_ts, ~p, return_to, {~p, ~p, ~p}, TimeStamp}},~n" - "Got: ~p~n", - [Pid, M, F, A, Other]), - ?t:fail() - after 5000 -> - io:format("Got timeout~n", []), - ?t:fail() + {trace_ts, Pid, return_to, {M, F, A}, Ts} -> + check_ts(TsType, PrevTs, Ts), + Ts; + Other -> + ct:fail("Expected: {trace_ts, ~p, return_to, {~p, ~p, ~p}, TimeStamp}},~n" + "Got: ~p~n", [Pid, M, F, A, Other]) + after 5000 -> + ct:fail("Got timeout~n", []) end. make_ts(timestamp) -> diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 193efba99c..2b142596a3 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -611,9 +611,7 @@ verify_trace_info({LocalFlag,Bool}, _, Local) when is_boolean(Bool) -> Bool = lists:member(LocalFlag, Local) catch error:_ -> - io:format("Line ~p: {~p,~p}, false, ~p\n", - [?LINE,LocalFlag,Bool,Local]), - ?t:fail() + ct:fail("Line ~p: {~p,~p}, false, ~p\n", [?LINE,LocalFlag,Bool,Local]) end; verify_trace_info({meta,Pid}, false, Local) when is_pid(Pid) -> true = lists:member(meta, Local); @@ -1323,12 +1321,12 @@ receive_next(TO) -> M -> M after TO -> - ?t:fail(timeout) + ct:fail(timeout) end. receive_no_next(TO) -> receive M -> - ?t:fail({unexpected_message,[M|flush(TO)]}) + ct:fail({unexpected_message,[M|flush(TO)]}) after TO -> ok end. diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl index eababe02fe..a2db701d36 100644 --- a/erts/emulator/test/trace_meta_SUITE.erl +++ b/erts/emulator/test/trace_meta_SUITE.erl @@ -530,7 +530,7 @@ combo_test() -> {?RT(Slave,{?MODULE,receiver,1}), ?RF(Slave,{erlang,phash2,2},0)} -> ok; - Error1 -> ?t:fail({unexpected_message, Error1}) + Error1 -> ct:fail({unexpected_message, Error1}) end, case {receive_next_bytag(LocalTracer), receive_next_bytag(LocalTracer)} of @@ -540,7 +540,7 @@ combo_test() -> {?RT(Slave,{?MODULE,slave,1}), ?RF(Slave,{?MODULE,receiver,1},Ref)} -> ok; - Error2 -> ?t:fail({unexpected_message, Error2}) + Error2 -> ct:fail({unexpected_message, Error2}) end, shutdown(), ?NM, @@ -729,13 +729,13 @@ receive_next(TO) -> M -> M after TO -> - ?t:fail(timeout) + ct:fail(timeout) end. receive_no_next(TO) -> receive M -> - ?t:fail({unexpected_message, M}) + ct:fail({unexpected_message, M}) after TO -> ok diff --git a/erts/emulator/test/trace_nif_SUITE.erl b/erts/emulator/test/trace_nif_SUITE.erl index 3ac891b1dd..f61e11595f 100644 --- a/erts/emulator/test/trace_nif_SUITE.erl +++ b/erts/emulator/test/trace_nif_SUITE.erl @@ -220,14 +220,12 @@ trace_nif_return(Config) when is_list(Config) -> receive_trace_msg(Mess) -> receive - Mess -> - ok; - Other -> - io:format("Expected: ~p,~nGot: ~p~n", [Mess, Other]), - ?t:fail() + Mess -> + ok; + Other -> + ct:fail("Expected: ~p,~nGot: ~p~n", [Mess, Other]) after 5000 -> - io:format("Expected: ~p,~nGot: timeout~n", [Mess]), - ?t:fail() + ct:fail("Expected: ~p,~nGot: timeout~n", [Mess]) end. receive_nothing() -> @@ -235,44 +233,35 @@ receive_nothing() -> receive_trace_msg_ts({trace_ts, Pid, call, {M,F,A}}) -> receive - {trace_ts, Pid, call, {M, F, A}, _Ts} -> - ok; - Other -> - io:format("Expected: {trace, ~p, call, {~p, ~p, ~p}, TimeStamp}},~n" - "Got: ~p~n", - [Pid, M, F, A, Other]), - ?t:fail() + {trace_ts, Pid, call, {M, F, A}, _Ts} -> + ok; + Other -> + ct:fail("Expected: {trace, ~p, call, {~p, ~p, ~p}, TimeStamp}},~n" + "Got: ~p~n", [Pid, M, F, A, Other]) after 5000 -> - io:format("Got timeout~n", []), - ?t:fail() + ct:fail("Got timeout~n", []) end. receive_trace_msg_ts_return_from({trace_ts, Pid, return_from, {M,F,A}}) -> receive - {trace_ts, Pid, return_from, {M, F, A}, _Value, _Ts} -> - ok; - Other -> - io:format("Expected: {trace_ts, ~p, return_from, {~p, ~p, ~p}, Value, TimeStamp}},~n" - "Got: ~p~n", - [Pid, M, F, A, Other]), - ?t:fail() + {trace_ts, Pid, return_from, {M, F, A}, _Value, _Ts} -> + ok; + Other -> + ct:fail("Expected: {trace_ts, ~p, return_from, {~p, ~p, ~p}, Value, TimeStamp}},~n" + "Got: ~p~n", [Pid, M, F, A, Other]) after 5000 -> - io:format("Got timeout~n", []), - ?t:fail() + ct:fail("Got timeout~n", []) end. receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}) -> receive - {trace_ts, Pid, return_to, {M, F, A}, _Ts} -> - ok; - Other -> - io:format("Expected: {trace_ts, ~p, return_to, {~p, ~p, ~p}, TimeStamp}},~n" - "Got: ~p~n", - [Pid, M, F, A, Other]), - ?t:fail() + {trace_ts, Pid, return_to, {M, F, A}, _Ts} -> + ok; + Other -> + ct:fail("Expected: {trace_ts, ~p, return_to, {~p, ~p, ~p}, TimeStamp}},~n" + "Got: ~p~n", [Pid, M, F, A, Other]) after 5000 -> - io:format("Got timeout~n", []), - ?t:fail() + ct:fail("Got timeout~n", []) end. nif_process() -> diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index e4c58860ad..18a475f04b 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -477,7 +477,7 @@ tracer_port_crash(Config) when is_list(Config) -> Id -> % erts_debug:set_internal_state(available_internal_state, true), % erts_debug:set_internal_state(abort, {trace_port_linker, Id}) - ?t:fail({trace_port_linker, Id}) + ct:fail({trace_port_linker, Id}) end, undefined = process_info(Tracee), ok @@ -498,7 +498,7 @@ expect() -> receive Other -> ok = io:format("Unexpected; got ~p", [Other]), - test_server:fail({unexpected, Other}) + ct:fail({unexpected, Other}) after 200 -> ok end. @@ -510,10 +510,10 @@ expect({trace_ts,E1,E2,info,ts}=Message) -> MessageTs; Other -> io:format("Expected ~p; got ~p", [Message,Other]), - test_server:fail({unexpected,Other}) + ct:fail({unexpected,Other}) after 5000 -> io:format("Expected ~p; got nothing", [Message]), - test_server:fail(no_trace_message) + ct:fail(no_trace_message) end; expect({trace,E1,E2,info}=Message) -> receive @@ -522,10 +522,10 @@ expect({trace,E1,E2,info}=Message) -> MessageTs; Other -> io:format("Expected ~p; got ~p", [Message,Other]), - test_server:fail({unexpected,Other}) + ct:fail({unexpected,Other}) after 5000 -> io:format("Expected ~p; got nothing", [Message]), - test_server:fail(no_trace_message) + ct:fail(no_trace_message) end; expect({trace_ts,E1,E2,E3,ts}=Message) -> receive @@ -534,10 +534,10 @@ expect({trace_ts,E1,E2,E3,ts}=Message) -> MessageTs; Other -> io:format("Expected ~p; got ~p", [Message,Other]), - test_server:fail({unexpected,Other}) + ct:fail({unexpected,Other}) after 5000 -> io:format("Expected ~p; got nothing", [Message]), - test_server:fail(no_trace_message) + ct:fail(no_trace_message) end; expect({trace_ts,E1,E2,E3,E4,ts}=Message) -> receive @@ -546,10 +546,10 @@ expect({trace_ts,E1,E2,E3,E4,ts}=Message) -> MessageTs; Other -> io:format("Expected ~p; got ~p", [Message,Other]), - test_server:fail({unexpected,Other}) + ct:fail({unexpected,Other}) after 5000 -> io:format("Expected ~p; got nothing", [Message]), - test_server:fail(no_trace_message) + ct:fail(no_trace_message) end; expect(Message) -> receive @@ -558,10 +558,10 @@ expect(Message) -> Message; Other -> io:format("Expected ~p; got ~p", [Message,Other]), - test_server:fail({unexpected,Other}) + ct:fail({unexpected,Other}) after 5000 -> io:format("Expected ~p; got nothing", [Message]), - test_server:fail(no_trace_message) + ct:fail(no_trace_message) end. trac(What, On, Flags0) -> diff --git a/erts/emulator/test/tuple_SUITE.erl b/erts/emulator/test/tuple_SUITE.erl index 2c2f93e7ee..fb44be3145 100644 --- a/erts/emulator/test/tuple_SUITE.erl +++ b/erts/emulator/test/tuple_SUITE.erl @@ -259,7 +259,7 @@ t_make_tuple(Size, Element) -> lists:foreach(fun(El) when El =:= Element -> ok; (Other) -> - test_server:fail({got, Other, expected, Element}) + ct:fail({got, Other, expected, Element}) end, tuple_to_list(Tuple)). %% Tests the erlang:make_tuple/3 BIF. @@ -385,14 +385,14 @@ tuple_in_guard(Config) when is_list(Config) -> Tuple1 == {element(1, Tuple2),element(2, Tuple2)} -> ok; true -> - test_server:fail() + ct:fail("failed") end, if Tuple2 == {element(1, Tuple2),element(2, Tuple2), element(3, Tuple2)} -> ok; true -> - test_server:fail() + ct:fail("failed") end, ok. diff --git a/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl index ac69e283ae..96c327c6ce 100644 --- a/erts/emulator/test/unique_SUITE.erl +++ b/erts/emulator/test/unique_SUITE.erl @@ -67,7 +67,7 @@ unique_monotonic_integer_white_box(Config) when is_list(Config) -> Mon = erlang:monitor(process, Test), receive {'DOWN', Mon, process, Test, Error} -> - ?t:fail(Error); + ct:fail(Error); Success -> ok end, diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index 3e32f56aeb..8b38334149 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -63,7 +63,7 @@ schedulers_alive(Config) when is_list(Config) -> ?line ?t:format("Number of schedulers configured: ~p~n", [NoSchedulers]), ?line case erlang:system_info(multi_scheduling) of blocked -> - ?line ?t:fail(multi_scheduling_blocked); + ?line ct:fail(multi_scheduling_blocked); disabled -> ?line ok; enabled -> @@ -90,7 +90,7 @@ schedulers_alive(Config) when is_list(Config) -> ?line erlang:system_flag(multi_scheduling, block), ?line case erlang:system_info(multi_scheduling) of enabled -> - ?line ?t:fail(multi_scheduling_enabled); + ?line ct:fail(multi_scheduling_enabled); blocked -> ?line [Master] = erlang:system_info(multi_scheduling_blockers); disabled -> ?line ok @@ -112,7 +112,7 @@ schedulers_alive(Config) when is_list(Config) -> end, Ps), ?line case erlang:system_flag(multi_scheduling, unblock) of - blocked -> ?line ?t:fail(multi_scheduling_blocked); + blocked -> ?line ct:fail(multi_scheduling_blocked); disabled -> ?line ok; enabled -> ?line ok end, @@ -180,7 +180,7 @@ verify_all_schedulers_used({UsedSIDs, UsedSIDsLen} = State, NoSchedulers) -> UsedSIDsLen -> ?line State; NoSchdlrs when NoSchdlrs < UsedSIDsLen -> - ?line ?t:fail({more_schedulers_used_than_exist, + ?line ct:fail({more_schedulers_used_than_exist, {existing_schedulers, NoSchdlrs}, {used_schedulers, UsedSIDsLen}, {used_scheduler_ids, UsedSIDs}}); @@ -215,7 +215,7 @@ pollset_size(Config) when is_list(Config) -> ?line erlang:demonitor(Mon, [flush]), ?line ICIO; {'DOWN', Mon, _, _, Reason} -> - ?line ?t:fail({non_existing, Name, Reason}) + ?line ct:fail({non_existing, Name, Reason}) end, ?line FinChkIo = get_check_io_info(), ?line io:format("Initial: ~p~nFinal: ~p~n", [InitChkIo, FinChkIo]), -- cgit v1.2.3 From 88ca325fa9fcc0b8953b389b96d1ed4666553ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 9 Mar 2016 16:27:27 +0100 Subject: Replace use of test_server:format/2 with io:format/2 --- erts/emulator/test/alloc_SUITE.erl | 2 +- erts/emulator/test/beam_SUITE.erl | 2 +- erts/emulator/test/distribution_SUITE.erl | 12 +++--- erts/emulator/test/driver_SUITE.erl | 26 ++++++------- erts/emulator/test/erl_drv_thread_SUITE.erl | 2 +- erts/emulator/test/erl_link_SUITE.erl | 4 +- erts/emulator/test/match_spec_SUITE.erl | 14 +++---- erts/emulator/test/mtx_SUITE.erl | 10 ++--- erts/emulator/test/nif_SUITE.erl | 2 +- erts/emulator/test/node_container_SUITE.erl | 26 ++++++------- erts/emulator/test/port_SUITE.erl | 58 ++++++++++++++--------------- erts/emulator/test/process_SUITE.erl | 24 ++++++------ erts/emulator/test/scheduler_SUITE.erl | 26 ++++++------- erts/emulator/test/smoke_test_SUITE.erl | 6 +-- erts/emulator/test/statistics_SUITE.erl | 6 +-- erts/emulator/test/system_info_SUITE.erl | 2 +- erts/emulator/test/time_SUITE.erl | 9 ++--- erts/emulator/test/timer_bif_SUITE.erl | 2 +- erts/emulator/test/trace_SUITE.erl | 8 ++-- erts/emulator/test/z_SUITE.erl | 10 ++--- 20 files changed, 124 insertions(+), 127 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 6ee2f98884..81b52525e3 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -315,7 +315,7 @@ repeat_while_loop(Fun, TRef, I) -> flush_log() -> receive {print, Str} -> - ?t:format("~s", [Str]), + io:format("~s", [Str]), flush_log() after 0 -> ok diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl index d3180a40cf..d2b234ae07 100644 --- a/erts/emulator/test/beam_SUITE.erl +++ b/erts/emulator/test/beam_SUITE.erl @@ -64,7 +64,7 @@ apply_last(Config) when is_list(Config) -> ct:fail("applied/2 timed out.") end, Pid ! die, - ?t:format("Size: ~p~n", [Size]), + io:format("Size: ~p~n", [Size]), if Size < 700 -> ok; diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index fd47b6b235..1425276544 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -462,12 +462,12 @@ do_busy_test(Node, Fun) -> receive after 100 -> ok end, Pinfo = process_info(P, [status, current_function]), unmake_busy(Busy), - ?t:format("~p : ~p~n", [P, Pinfo]), + io:format("~p : ~p~n", [P, Pinfo]), case Pinfo of undefined -> receive {'DOWN', M, process, P, Reason} -> - ?t:format("~p died with exit reason ~p~n", [P, Reason]) + io:format("~p died with exit reason ~p~n", [P, Reason]) end, ct:fail(premature_death); _ -> @@ -477,7 +477,7 @@ do_busy_test(Node, Fun) -> {current_function, {erlang, bif_return_trap, _}}] = Pinfo, receive {'DOWN', M, process, P, Reason} -> - ?t:format("~p died with exit reason ~p~n", [P, Reason]), + io:format("~p died with exit reason ~p~n", [P, Reason]), normal = Reason end end. @@ -2071,7 +2071,7 @@ node_monitor(Master) -> Master ! {nodeup, node(), Node} end, Nodes0), - ?t:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Nodes0]), + io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Nodes0]), node_monitor_loop(Master); false -> net_kernel:monitor_nodes(false, Opts), @@ -2092,7 +2092,7 @@ node_monitor_loop(Master) -> receive {nodeup, Node, _InfoList} = Msg -> Master ! {nodeup, node(), Node}, - ?t:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), + io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), node_monitor_loop(Master); {nodedown, Node, InfoList} = Msg -> Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of @@ -2100,7 +2100,7 @@ node_monitor_loop(Master) -> _ -> undefined end, Master ! {nodedown, node(), Node, Reason}, - ?t:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), + io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), node_monitor_loop(Master) end. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index b00ceaed50..3da99b4090 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -763,10 +763,10 @@ io_ready_exit(Config) when is_list(Config) -> {'EXIT', Port, Reason} -> ?line case Reason of ready_output_driver_failure -> - ?t:format("Exited in output_ready()~n"), + io:format("Exited in output_ready()~n"), ?line ok; ready_input_driver_failure -> - ?t:format("Exited in input_ready()~n"), + io:format("Exited in input_ready()~n"), ?line ok; Error -> ?line ct:fail(Error) end @@ -908,7 +908,7 @@ chkio_test_init(Config) when is_list(Config) -> ?line ChkIo = get_stable_check_io_info(), ?line case catch lists:keysearch(name, 1, ChkIo) of {value, {name, erts_poll}} -> - ?line ?t:format("Before test: ~p~n", [ChkIo]), + ?line io:format("Before test: ~p~n", [ChkIo]), ?line Path = ?config(data_dir, Config), ?line erl_ddll:start(), ?line ok = load_driver(Path, 'chkio_drv'), @@ -925,7 +925,7 @@ chkio_test_fini({chkio_test_result, Res, Before}) -> ?line ok = erl_ddll:unload_driver('chkio_drv'), ?line ok = erl_ddll:stop(), ?line After = get_stable_check_io_info(), - ?line ?t:format("After test: ~p~n", [After]), + ?line io:format("After test: ~p~n", [After]), ?line verify_chkio_state(Before, After), ?line Res. @@ -972,12 +972,12 @@ chkio_test({erts_poll_info, Before}, ?line During = erlang:system_info(check_io), ?line erlang:display(During), ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)), - ?line ?t:format("During test: ~p~n", [During]), + ?line io:format("During test: ~p~n", [During]), ?line chk_chkio_port(Port), ?line case erlang:port_control(Port, ?CHKIO_STOP, "") of Res when is_list(Res) -> ?line chk_chkio_port(Port), - ?line ?t:format("~s", [Res]), + ?line io:format("~s", [Res]), ?line close_chkio_port(Port), ?line Res, ?line case Res of @@ -1119,8 +1119,8 @@ driver_system_info_test(Config, Name) -> ?line ok. check_driver_system_info_result(Result) -> - ?line ?t:format("All names: ~p~n", [?EXPECTED_SYSTEM_INFO_NAMES]), - ?line ?t:format("Result: ~p~n", [Result]), + ?line io:format("All names: ~p~n", [?EXPECTED_SYSTEM_INFO_NAMES]), + ?line io:format("Result: ~p~n", [Result]), ?line {[], Ns, DDVSN} = chk_sis(lists:map(fun (Str) -> string:tokens(Str, "=") end, @@ -1828,9 +1828,9 @@ thread_mseg_alloc_cache_clean(Config) when is_list(Config) -> ?line ok = load_driver(Path, DrvName), ?line Port = open_port({spawn, DrvName}, []), ?line CCI = 1000, - ?line ?t:format("CCI = ~p~n", [CCI]), + ?line io:format("CCI = ~p~n", [CCI]), ?line CCC = mseg_alloc_ccc(), - ?line ?t:format("CCC = ~p~n", [CCC]), + ?line io:format("CCC = ~p~n", [CCC]), ?line thread_mseg_alloc_cache_clean_test(Port, 10, CCI, @@ -1903,7 +1903,7 @@ thread_mseg_alloc_cache_clean_test(Port, N, CCI, Size) -> ?line "ok" = erlang:port_control(Port, 0, integer_to_list(Size)), ?line receive after CCI+500 -> ok end, ?line CCC = mseg_alloc_ccc(), - ?line ?t:format("CCC = ~p~n", [CCC]), + ?line io:format("CCC = ~p~n", [CCC]), ?line true = CCC > OCCC, ?line thread_mseg_alloc_cache_clean_test(Port, N-1, CCI, Size). @@ -2086,7 +2086,7 @@ thr_msg_blast(Config) when is_list(Config) -> false -> case erlang:system_info(lock_checking) of true -> - ?t:format("~p:~p: Ignore bad sched count due to " + io:format("~p:~p: Ignore bad sched count due to " "lock checking~n", [?MODULE,?LINE]); false -> @@ -2338,7 +2338,7 @@ count_pp_sched_stop(Ps) -> PNs = lists:map(fun (P) -> {P, 0} end, Ps), receive {trace_delivered, all, Td} -> ok end, Res = count_proc_sched(Ps, PNs), - ?t:format("Scheduling counts: ~p~n", [Res]), + io:format("Scheduling counts: ~p~n", [Res]), erlang:display({scheduling_counts, Res}), Res. diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl index 3497760515..b0ac1c703c 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE.erl +++ b/erts/emulator/test/erl_drv_thread_SUITE.erl @@ -103,7 +103,7 @@ run_drv_case(Config, CaseName, Command, TimeTrap) -> receive_drv_result(Port, CaseName) -> ?line receive {print, Port, CaseName, Str} -> - ?line ?t:format("~s", [Str]), + ?line io:format("~s", [Str]), ?line receive_drv_result(Port, CaseName); {'EXIT', Port, Error} -> ?line ct:fail(Error); diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index 62ae83f9e2..2a59ca8e99 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -507,7 +507,7 @@ otp_7946(Config) when is_list(Config) -> end), ?line receive {'DOWN', LMon, process, Linker, Reason} -> - ?line ?t:format("Reason=~p~n", [Reason]), + ?line io:format("Reason=~p~n", [Reason]), ?line Reason = noconnection end. @@ -603,7 +603,7 @@ suspend_on_busy_test(Node, Doing, Fun) -> receive after 100 -> ok end, Info = process_info(Tester, [status, current_function]), unmake_busy(Busy), - ?t:format("~p doing ~s: ~p~n", [Tester, Doing, Info]), + io:format("~p doing ~s: ~p~n", [Tester, Doing, Info]), Tester ! {Done, Info} end), receive DoIt -> ok end, diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 1e8bba99af..0e45ebf953 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -989,13 +989,13 @@ collect(P, TMs) -> collect([]) -> receive M -> - ?t:format("Got unexpected: ~p~n", [M]), + io:format("Got unexpected: ~p~n", [M]), flush({got_unexpected,M}) after 17 -> ok end; collect([TM | TMs]) -> - ?t:format( "Expecting: ~p~n", [TM]), + io:format( "Expecting: ~p~n", [TM]), receive M0 -> M = case element(1, M0) of @@ -1008,20 +1008,20 @@ collect([TM | TMs]) -> true -> case (catch TM(M)) of true -> - ?t:format("Got: ~p~n", [M]), + io:format("Got: ~p~n", [M]), collect(TMs); _ -> - ?t:format("Got unexpected: ~p~n", [M]), + io:format("Got unexpected: ~p~n", [M]), flush({got_unexpected,M}) end; false -> case M of TM -> - ?t:format("Got: ~p~n", [M]), + io:format("Got: ~p~n", [M]), collect(TMs); _ -> - ?t:format("Got unexpected: ~p~n", [M]), + io:format("Got unexpected: ~p~n", [M]), flush({got_unexpected,M}) end end @@ -1030,7 +1030,7 @@ collect([TM | TMs]) -> flush(Reason) -> receive M -> - ?t:format("In queue: ~p~n", [M]), + io:format("In queue: ~p~n", [M]), flush(Reason) after 17 -> ct:fail(Reason) diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl index c7c043d8e7..9f0c3bdad0 100644 --- a/erts/emulator/test/mtx_SUITE.erl +++ b/erts/emulator/test/mtx_SUITE.erl @@ -115,7 +115,7 @@ long_rwlock(Config) when is_list(Config) -> {_, RunTime} = statistics(runtime), %% A very short run time is expected, since %% threads in the test mostly wait - ?t:format("RunTime=~p~n", [RunTime]), + io:format("RunTime=~p~n", [RunTime]), ?line true = RunTime < 400, ?line RunTimeStr = "Run-time during test was "++integer_to_list(RunTime)++" ms.", case LLRes of @@ -209,7 +209,7 @@ hammer_sched_rwlock_test(FreqRead, LockCheck, Blocking, WaitLocked, WaitUnlocked false -> WaitLocked*250 end)) div handicap()), - ?t:format("NoLockOps=~p~n", [NoLockOps]), + io:format("NoLockOps=~p~n", [NoLockOps]), Sleep = case Blocking of true -> NoLockOps; false -> NoLockOps div 10 @@ -274,7 +274,7 @@ hammer_sched_rwlock_test(FreqRead, LockCheck, Blocking, WaitLocked, WaitUnlocked {false, _} -> ok; _ -> {_, RunTime} = statistics(runtime), - ?t:format("RunTime=~p~n", [RunTime]), + io:format("RunTime=~p~n", [RunTime]), ?line true = RunTime < 700, {comment, "Run-time during test was " @@ -332,9 +332,9 @@ hammer_ets_rwlock(Config) when is_list(Config) -> 3 -> {2000, 50}; _ -> {200, 50} end, - ?t:format("Procs=~p~nOps=~p~n", [Procs, Ops]), + io:format("Procs=~p~nOps=~p~n", [Procs, Ops]), lists:foreach(fun (XOpts) -> - ?t:format("Running with extra opts: ~p", [XOpts]), + io:format("Running with extra opts: ~p", [XOpts]), hammer_ets_rwlock_test(XOpts, true, 2, Ops, Procs, false) end, diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 05c393e65f..ff54006643 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -309,7 +309,7 @@ eq_cmp(A,B) -> eq_cmp_do({A,B},{A,B}). eq_cmp_do(A,B) -> - %%?t:format("compare ~p and ~p\n",[A,B]), + %%io:format("compare ~p and ~p\n",[A,B]), Eq = (A =:= B), ?line Eq = is_identical(A,B), ?line Cmp = if diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 3bf1e207de..b95a59f2da 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -411,12 +411,12 @@ node_table_gc(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), erts_debug:set_internal_state(node_tab_delayed_delete, 0), ?line PreKnown = nodes(known), - ?line ?t:format("PreKnown = ~p~n", [PreKnown]), + ?line io:format("PreKnown = ~p~n", [PreKnown]), ?line make_node_garbage(0, 200000, 1000, []), ?line PostKnown = nodes(known), ?line PostAreas = erlang:system_info(allocated_areas), - ?line ?t:format("PostKnown = ~p~n", [PostKnown]), - ?line ?t:format("PostAreas = ~p~n", [PostAreas]), + ?line io:format("PostKnown = ~p~n", [PostKnown]), + ?line io:format("PostAreas = ~p~n", [PostAreas]), ?line true = length(PostKnown) =< length(PreKnown), ?line nc_refc_check(node()), erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value @@ -761,17 +761,17 @@ set_next_id(port, N) -> pp_wrap(What) -> ?line N = set_high_pp_next(What), ?line Cre = N + 100, - ?line ?t:format("no creations = ~p~n", [Cre]), + ?line io:format("no creations = ~p~n", [Cre]), ?line PreCre = get_next_id(What), - ?line ?t:format("pre creations = ~p~n", [PreCre]), + ?line io:format("pre creations = ~p~n", [PreCre]), ?line true = is_integer(PreCre), ?line do_pp_creations(What, Cre), ?line PostCre = get_next_id(What), - ?line ?t:format("post creations = ~p~n", [PostCre]), + ?line io:format("post creations = ~p~n", [PostCre]), ?line true = is_integer(PostCre), ?line true = PreCre > PostCre, ?line Now = set_next_id(What, ?MAX_PIDS_PORTS div 2), - ?line ?t:format("reset to = ~p~n", [Now]), + ?line io:format("reset to = ~p~n", [Now]), ?line true = is_integer(Now), ?line ok. @@ -903,7 +903,7 @@ chk_max_proc_line() -> {proc_line_length, PLL, End} -> ?line PC = erlang:system_info(process_count), ?line LP = length(processes()), - ?line ?t:format("proc line length = ~p; " + ?line io:format("proc line length = ~p; " "process count = ~p; " "length processes = ~p~n", [PLL, PC, LP]), @@ -936,7 +936,7 @@ node_container_refc_check(Node) when is_atom(Node) -> nc_refc_check(Node) when is_atom(Node) -> Ref = make_ref(), Self = self(), - ?t:format("Starting reference count check of node ~w~n", [Node]), + io:format("Starting reference count check of node ~w~n", [Node]), spawn_link(Node, fun () -> {{node_references, NodeRefs}, @@ -952,10 +952,10 @@ nc_refc_check(Node) when is_atom(Node) -> end), receive {Ref, ErrorMsg, failed} -> - ?t:format("~s~n", [ErrorMsg]), + io:format("~s~n", [ErrorMsg]), ct:fail(reference_count_check_failed); {Ref, succeded} -> - ?t:format("Reference count check of node ~w succeded!~n", [Node]), + io:format("Reference count check of node ~w succeded!~n", [Node]), ok end. @@ -1034,7 +1034,7 @@ get_node_references({NodeName, Creation} = Node) when is_atom(NodeName), NodeRefs, DistRefs, fun (ErrMsg) -> - ?t:format("~s", [ErrMsg]), + io:format("~s", [ErrMsg]), ct:fail(reference_count_check_failed) end), find_references(Node, NodeRefs). @@ -1046,7 +1046,7 @@ get_dist_references(NodeName) when is_atom(NodeName) -> NodeRefs, DistRefs, fun (ErrMsg) -> - ?line ?t:format("~s", [ErrMsg]), + ?line io:format("~s", [ErrMsg]), ?line ct:fail(reference_count_check_failed) end), ?line find_references(NodeName, DistRefs). diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 25452598e3..0d8cb77161 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1112,22 +1112,22 @@ otp_3906(Config, OSName) -> otp_3906_list_defunct(EmPid, OSName) -> % Guess ps switches to use and what to grep for (could be improved) {Switches, Zombie} = case OSName of - BSD when BSD == darwin; - BSD == openbsd; - BSD == netbsd; - BSD == freebsd -> - {"-ajx", "Z"}; - _ -> - {"-ef", "[dD]efunct"} - end, - test_server:format("Emulator pid: ~s~n" - "Listing of zombie processes:~n" - "~s~n", - [EmPid, - otp_3906_htmlize(os:cmd("ps " - ++ Switches - ++ " | grep " - ++ Zombie))]). + BSD when BSD == darwin; + BSD == openbsd; + BSD == netbsd; + BSD == freebsd -> + {"-ajx", "Z"}; + _ -> + {"-ef", "[dD]efunct"} + end, + io:format("Emulator pid: ~s~n" + "Listing of zombie processes:~n" + "~s~n", + [EmPid, + otp_3906_htmlize(os:cmd("ps " + ++ Switches + ++ " | grep " + ++ Zombie))]). otp_3906_htmlize([]) -> []; @@ -1689,15 +1689,15 @@ otp_5112(Config) when is_list(Config) -> 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]), + io:format("Max ports: ~p~n",[max_ports()]), + io:format("Port: ~p~n",[Port]), {links, Links1} = process_info(self(),links), - ?t:format("Links1: ~p~n",[Links1]), + io:format("Links1: ~p~n",[Links1]), true = lists:member(Port, Links1), Port ! {self(), {command, ""}}, ?line wait_until(fun () -> lists:member(Port, erlang:ports()) == false end), {links, Links2} = process_info(self(),links), - ?t:format("Links2: ~p~n",[Links2]), + io:format("Links2: ~p~n",[Links2]), false = lists:member(Port, Links2), %% This used to fail ok. @@ -1705,13 +1705,13 @@ otp_5112_get_wrapped_port() -> P1 = erlang:open_port({spawn, "exit_drv"}, []), case port_ix(P1) < max_ports() of true -> - ?t:format("Need to wrap port index (~p)~n", [P1]), + io:format("Need to wrap port index (~p)~n", [P1]), otp_5112_wrap_port_ix([P1]), P2 = erlang:open_port({spawn, "exit_drv"}, []), false = port_ix(P2) < max_ports(), P2; false -> - ?t:format("Port index already wrapped (~p)~n", [P1]), + io:format("Port index already wrapped (~p)~n", [P1]), P1 end. @@ -1745,9 +1745,9 @@ otp_5119(Config) when is_list(Config) -> port_ix(erlang:open_port({spawn, "exit_drv"}, []))} end, MaxPorts = max_ports(), - ?t:format("PortIx1 = ~p ~p~n", [PI1, PortIx1]), - ?t:format("PortIx2 = ~p ~p~n", [PI2, PortIx2]), - ?t:format("MaxPorts = ~p~n", [MaxPorts]), + io:format("PortIx1 = ~p ~p~n", [PI1, PortIx1]), + io:format("PortIx2 = ~p ~p~n", [PI2, PortIx2]), + io:format("MaxPorts = ~p~n", [MaxPorts]), true = PortIx2 > PortIx1, true = PortIx2 =< PortIx1 + MaxPorts, ok. @@ -1838,7 +1838,7 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> %% NoSchedsOnln = erlang:system_info(schedulers_online), Parent = self(), - ?t:format("SleepSecs = ~p~n", [SleepSecs]), + io:format("SleepSecs = ~p~n", [SleepSecs]), PortProg = "sleep " ++ integer_to_list(SleepSecs), Start = erlang:monotonic_time(micro_seconds), NoProcs = case NoSchedsOnln of @@ -1851,7 +1851,7 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> TNPorts when TNPorts < ?EXIT_STATUS_MSB_MAX_PORTS -> 20; _ -> ?EXIT_STATUS_MSB_MAX_PORTS div NoProcs end, - ?t:format("NoProcs = ~p~nNoPortsPerProc = ~p~n", + io:format("NoProcs = ~p~nNoPortsPerProc = ~p~n", [NoProcs, NoPortsPerProc]), ProcFun = fun () -> @@ -1915,12 +1915,12 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> end, Procs), StartedTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000, - ?t:format("StartedTime = ~p~n", [StartedTime]), + io:format("StartedTime = ~p~n", [StartedTime]), true = StartedTime < SleepSecs, erlang:system_flag(multi_scheduling, block), lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs), DoneTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000, - ?t:format("DoneTime = ~p~n", [DoneTime]), + io:format("DoneTime = ~p~n", [DoneTime]), true = DoneTime > SleepSecs, ok = verify_multi_scheduling_blocked(), erlang:system_flag(multi_scheduling, unblock), diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 1ee57f07b5..6c733195b7 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -1536,7 +1536,7 @@ print_processes_bif_info(#ptab_list_bif_info{min_start_reds = MinStartReds, term_procs_max_reds = TPMaxReds, conses_per_red = ConsesPerRed, debug_level = DbgLvl}) -> - ?t:format("processes/0 bif info on node ~p:~n" + io:format("processes/0 bif info on node ~p:~n" "Min start reductions = ~p~n" "Process table chunks = ~p~n" "Process table chunks size = ~p~n" @@ -1577,7 +1577,7 @@ processes_unexpected_result(CorrectProcs, Procs) -> status, priority], MissingProcs = CorrectProcs -- Procs, - ?t:format("Missing processes: ~p", + io:format("Missing processes: ~p", [lists:map(fun (Pid) -> [{pid, Pid} | case process_info(Pid, ProcInfo) of @@ -1587,7 +1587,7 @@ processes_unexpected_result(CorrectProcs, Procs) -> end, MissingProcs)]), SuperfluousProcs = Procs -- CorrectProcs, - ?t:format("Superfluous processes: ~p", + io:format("Superfluous processes: ~p", [lists:map(fun (Pid) -> [{pid, Pid} | case process_info(Pid, ProcInfo) of @@ -1701,7 +1701,7 @@ do_processes_bif_test(WantReds, DieTest, Processes) -> DoIt = make_ref(), GetGoing = make_ref(), {NoTestProcs, TestProcs} = spawn_initial_hangarounds(Cleaner), - ?t:format("Testing with ~p processes~n", [NoTestProcs]), + io:format("Testing with ~p processes~n", [NoTestProcs]), SpawnHangAround = fun () -> spawn(?MODULE, hangaround, [Cleaner, new_hangaround]) end, @@ -1743,7 +1743,7 @@ do_processes_bif_test(WantReds, DieTest, Processes) -> Procs = lists:sort(Procs0), CorrectProcs = lists:sort(CorrectProcs0), LengthCorrectProcs = length(CorrectProcs), - ?t:format("~p = length(CorrectProcs)~n", [LengthCorrectProcs]), + io:format("~p = length(CorrectProcs)~n", [LengthCorrectProcs]), true = LengthCorrectProcs > NoTestProcs, case CorrectProcs =:= Procs of true -> @@ -1764,12 +1764,12 @@ do_processes_bif_test(WantReds, DieTest, Processes) -> do_processes_bif_die_test(false, _Processes) -> - ?t:format("Skipping test killing process executing processes/0~n",[]), + io:format("Skipping test killing process executing processes/0~n",[]), ok; do_processes_bif_die_test(true, Processes) -> do_processes_bif_die_test(5, Processes); do_processes_bif_die_test(N, Processes) -> - ?t:format("Doing test killing process executing processes/0~n",[]), + io:format("Doing test killing process executing processes/0~n",[]), try Tester = self(), Oooh_Nooooooo = make_ref(), @@ -1819,8 +1819,8 @@ do_processes_bif_die_test(N, Processes) -> ok catch throw:{kill_in_trap, R} when N > 0 -> - ?t:format("Failed to kill in trap: ~p~n", [R]), - ?t:format("Trying again~n", []), + io:format("Failed to kill in trap: ~p~n", [R]), + io:format("Trying again~n", []), do_processes_bif_die_test(N-1, Processes) end. @@ -1850,7 +1850,7 @@ wait_until_system_recover(Tmr) -> receive {timeout, Tmr, _} -> Comment = "WARNING: Test processes still hanging around!", - ?t:format("~s~n", [Comment]), + io:format("~s~n", [Comment]), put(processes_bif_testcase_comment, Comment), lists:foreach( fun (P) when P == self() -> @@ -1858,7 +1858,7 @@ wait_until_system_recover(Tmr) -> (P) -> case process_info(P, initial_call) of {initial_call,{?MODULE, _, _} = MFA} -> - ?t:format("~p ~p~n", [P, MFA]); + io:format("~p ~p~n", [P, MFA]); {initial_call,{_, _, _}} -> ok; undefined -> @@ -2228,7 +2228,7 @@ do_otp_7738_test(Type) -> ok after 2000 -> I = process_info(R, [status, message_queue_len]), - ?t:format("~p~n", [I]), + io:format("~p~n", [I]), ct:fail(no_progress) end, ok. diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 776bf172ee..38f8dcc8d4 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -526,7 +526,7 @@ bindings(Node, BindType) -> end), receive {Ref, Res} -> - ?t:format("~p: ~p~n", [BindType, Res]), + io:format("~p: ~p~n", [BindType, Res]), unlink(Pid), Res end. @@ -564,7 +564,7 @@ scheduler_bind_types(Config) when is_list(Config) -> ?line ok. scheduler_bind_types_test(Config, Topology, CmdLine, TermLetter) -> - ?line ?t:format("Testing (~p): ~p~n", [TermLetter, Topology]), + ?line io:format("Testing (~p): ~p~n", [TermLetter, Topology]), ?line {ok, Node0} = start_node(Config), ?line _ = rpc:call(Node0, erlang, system_flag, [cpu_topology, Topology]), ?line cmp(Topology, rpc:call(Node0, erlang, system_info, [cpu_topology])), @@ -766,7 +766,7 @@ cpu_topology(Config) when is_list(Config) -> ?line ok. cpu_topology_test(Config, Topology, Cmd) -> - ?line ?t:format("Testing~n ~p~n ~p~n", [Topology, Cmd]), + ?line io:format("Testing~n ~p~n ~p~n", [Topology, Cmd]), ?line cpu_topology_bif_test(Config, Topology), ?line cpu_topology_cmdline_test(Config, Topology, Cmd), ?line ok. @@ -791,7 +791,7 @@ cpu_topology_cmdline_test(Config, Topology, Cmd) -> update_cpu_info(Config) when is_list(Config) -> ?line OldOnline = erlang:system_info(schedulers_online), ?line OldAff = get_affinity_mask(), - ?line ?t:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", + ?line io:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", [OldAff, OldOnline, erlang:system_info(scheduler_bindings)]), ?line case {erlang:system_info(logical_processors_available), OldAff} of {Avail, _} when Avail == unknown; OldAff == unknown -> @@ -816,7 +816,7 @@ update_cpu_info(Config) when is_list(Config) -> {Onln0, Onln1} -> ?line Onln1 = erlang:system_info(schedulers_online), ?line receive after 500 -> ok end, - ?line ?t:format("TEST - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", + ?line io:format("TEST - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", [Aff, Onln1, erlang:system_info(scheduler_bindings)]), ?line unchanged = adjust_schedulers_online(), ?line ok; @@ -829,7 +829,7 @@ update_cpu_info(Config) when is_list(Config) -> adjust_schedulers_online(), erlang:system_flag(schedulers_online, OldOnline), receive after 500 -> ok end, - ?t:format("END - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", + io:format("END - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", [get_affinity_mask(), erlang:system_info(schedulers_online), erlang:system_info(scheduler_bindings)]) @@ -1007,7 +1007,7 @@ sbt_cmd(Config) when is_list(Config) -> end. sbt_test(Config, CpuTCmd, ClBt, Bt, LP) -> - ?line ?t:format("Testing +sbt ~s (~p)~n", [ClBt, Bt]), + ?line io:format("Testing +sbt ~s (~p)~n", [ClBt, Bt]), ?line LPS = integer_to_list(LP), ?line Cmd = CpuTCmd++" +sbt "++ClBt++" +S"++LPS++":"++LPS, ?line {ok, Node} = start_node(Config, Cmd), @@ -1019,7 +1019,7 @@ sbt_test(Config, CpuTCmd, ClBt, Bt, LP) -> erlang, system_info, [scheduler_bindings]), - ?line ?t:format("scheduler bindings: ~p~n", [SB]), + ?line io:format("scheduler bindings: ~p~n", [SB]), ?line BS = case {Bt, erlang:system_info(logical_processors_available)} of {unbound, _} -> 0; {_, Int} when is_integer(Int) -> Int; @@ -1333,7 +1333,7 @@ scheduler_suspend_test(Config, Schedulers) -> ?line [SState] = mcall(Node, [fun () -> erlang:system_info(schedulers_state) end]), - ?line ?t:format("SState=~p~n", [SState]), + ?line io:format("SState=~p~n", [SState]), ?line {Sched, SchedOnln, _SchedAvail} = SState, ?line true = is_integer(Sched), ?line [ok] = mcall(Node, [fun () -> sst0_loop(300) end]), @@ -1824,7 +1824,7 @@ chk_result([{low, L, Lmin, _Lmax}, LNShouldWork, HShouldWork, MShouldWork) -> - ?line ?t:format("~p~n", [Res]), + ?line io:format("~p~n", [Res]), ?line Relax = relax_limits(), case {L, N} of {0, 0} -> @@ -1842,7 +1842,7 @@ chk_result([{low, L, Lmin, _Lmax}, ?line Ratio = Lavg/Navg, ?line LminRatio = Lmin/Lavg, ?line NminRatio = Nmin/Navg, - ?line ?t:format("low min ratio=~p~n" + ?line io:format("low min ratio=~p~n" "normal min ratio=~p~n" "low avg=~p~n" "normal avg=~p~n" @@ -1907,7 +1907,7 @@ snd(Msg, [P|Ps]) -> relax_limits() -> case strange_system_scale() of Scale when Scale > 1 -> - ?t:format("Relaxing limits~n", []), + io:format("Relaxing limits~n", []), true; _ -> false @@ -2247,7 +2247,7 @@ enable_internal_state() -> cmp(X, X) -> ok; cmp(X, Y) -> - ?t:format("cmp failed:~n X=~p~n Y=~p~n", [X,Y]), + io:format("cmp failed:~n X=~p~n Y=~p~n", [X,Y]), cmp_aux(X, Y). diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 7e5345fd49..3c616b4304 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -91,7 +91,7 @@ native_atomics(Config) when is_list(Config) -> NA64Key = "64-bit native atomics", DWNAKey = "Double word native atomics", EthreadInfo = erlang:system_info(ethread_info), - ?t:format("~p~n", [EthreadInfo]), + io:format("~p~n", [EthreadInfo]), {value,{NA32Key, NA32, _}} = lists:keysearch(NA32Key, 1, EthreadInfo), {value,{NA64Key, NA64, _}} = lists:keysearch(NA64Key, 1, EthreadInfo), {value,{DWNAKey, DWNA, _}} = lists:keysearch(DWNAKey, 1, EthreadInfo), @@ -129,7 +129,7 @@ chk_boot(Config, Args, Fun) -> true = os:putenv("ERL_ZFLAGS", Args), Success = make_ref(), Parent = self(), - ?t:format("--- Testing ~s~n", [Args]), + io:format("--- Testing ~s~n", [Args]), {ok, Node} = start_node(Config), Pid = spawn_link(Node, fun () -> Fun(), @@ -139,7 +139,7 @@ chk_boot(Config, Args, Fun) -> {Pid, Success} -> Node = node(Pid), stop_node(Node), - ?t:format("--- Success!~n", []), + io:format("--- Success!~n", []), ok end. diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index b99d567772..c16e0f7cbe 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -106,8 +106,7 @@ wall_clock_update1(N) when N > 0 -> ?line {T2_wc_time, Wc_Diff} = statistics(wall_clock), ?line Wc_Diff = T2_wc_time - T1_wc_time, - ?line test_server:format("Wall clock diff = ~p; should be = 1000..1040~n", - [Wc_Diff]), + ?line io:format("Wall clock diff = ~p; should be = 1000..1040~n", [Wc_Diff]), case ?t:is_debug() of false -> ?line true = Wc_Diff =< 1040; @@ -159,8 +158,7 @@ do_runtime_update(N) -> receive after 1000 -> ok end, ?line {T2,Diff} = statistics(runtime), ?line true = is_integer(T1+T2+Diff0+Diff), - ?line test_server:format("T1 = ~p, T2 = ~p, Diff = ~p, T2-T1 = ~p", - [T1,T2,Diff,T2-T1]), + ?line io:format("T1 = ~p, T2 = ~p, Diff = ~p, T2-T1 = ~p", [T1,T2,Diff,T2-T1]), ?line if T2 - T1 =:= Diff, 900 =< Diff, Diff =< 1500 -> ok; true -> do_runtime_update(N-1) diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index e6bef8f14f..a7db06f2c5 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -92,7 +92,7 @@ check_procs(N) -> check_pc(E) -> ?line P = length(processes()), ?line SI = erlang:system_info(process_count), - ?line ?t:format("E=~p; P=~p; SI=~p~n", [E, P, SI]), + ?line io:format("E=~p; P=~p; SI=~p~n", [E, P, SI]), ?line E = P, ?line P = SI. diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index c27de24690..c225c4d88f 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -465,8 +465,7 @@ now_update1(N) when N > 0 -> ?line Linear_Diff = (T2_linear-T1_linear)*1000000, ?line Now_Diff = T2_now-T1_now, - test_server:format("Localtime diff = ~p; now() diff = ~p", - [Linear_Diff, Now_Diff]), + io:format("Localtime diff = ~p; now() diff = ~p", [Linear_Diff, Now_Diff]), ?line case abs(Linear_Diff - Now_Diff) of Abs_Delta when Abs_Delta =< 40000 -> ok; _ -> now_update1(N-1) @@ -729,7 +728,7 @@ check_time_offset_res_conv(Mon, Res) -> ct:fail({time_unit_conversion_inconsistency, TO, TORes, TORes2}); {_NewTO, true} -> - ?t:format("time_offset changed", []), + io:format("time_offset changed", []), check_time_offset_res_conv(Mon, Res) end end. @@ -872,13 +871,13 @@ do_check_erlang_timestamp(Done, Mon, TO) -> check_erlang_timestamp(Done, Mon, NewTO); false -> io:format("TsMin=~p TsTime=~p TsMax=~p~n", [TsMin, TsTime, TsMax]), - ?t:format("Detected inconsistency; " + io:format("Detected inconsistency; " "checking for time_offset change...", []), case check_time_offset_change(Mon, TO, 1000) of {TO, false} -> ct:fail(timestamp_inconsistency); {NewTO, true} -> - ?t:format("time_offset changed", []), + io:format("time_offset changed", []), check_erlang_timestamp(Done, Mon, NewTO) end end. diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index adc25b9426..704269bc0f 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -362,7 +362,7 @@ evil_timers(Config) when is_list(Config) -> ?line RefList = [make_ref(), make_ref(), make_ref()], ?line BigList = [111111111111, 22222222222222, 333333333333333333], ?line Msg = {BinList,[FunList,{RefList,ExtList,PidList,PortList,BigList}]}, - %% ?line ?t:format("Msg=~p~n",[Msg]), + %% ?line io:format("Msg=~p~n",[Msg]), ?line Prio = process_flag(priority, max), %% diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 5982383f13..5762fd558b 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -847,7 +847,7 @@ mutual_suspend(Config) when is_list(Config) -> end, ?line P1S = process_info(P1, status), ?line P2S = process_info(P2, status), - ?line ?t:format("P1S=~p P2S=~p", [P1S, P2S]), + ?line io:format("P1S=~p P2S=~p", [P1S, P2S]), ?line false = {status, suspended} == P1S, ?line false = {status, suspended} == P2S, ?line unlink(P1), exit(P1, bang), @@ -1067,7 +1067,7 @@ suspend_until_system_limit(P, N, M) -> suspend_until_system_limit(P, N+1, NewM); {'EXIT', R} when R == system_limit; element(1, R) == system_limit -> - ?line ?t:format("system limit at ~p~n", [N]), + ?line io:format("system limit at ~p~n", [N]), ?line resume_from_system_limit(P, N, 0); Error -> ?line ct:fail(Error) @@ -1193,7 +1193,7 @@ suspend_opts(Config) when is_list(Config) -> ?line erlang:resume_process(Tok), ?line erlang:resume_process(Tok), ?line 1 = suspend_count(Tok), - ?line ?t:format("Main suspends: ~p~n" + ?line io:format("Main suspends: ~p~n" "Main async: ~p~n" "Double async: ~p~n" "Async once: ~p~n" @@ -1517,7 +1517,7 @@ issue_non_empty_runq_warning(DeadLine, RQLen) -> end, [], processes()), - ?t:format("WARNING: Unexpected runnable processes in system (waited ~p sec).~n" + io:format("WARNING: Unexpected runnable processes in system (waited ~p sec).~n" " Run queue length: ~p~n" " Self: ~p~n" " Processes info: ~p~n", diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index 8b38334149..ea04790860 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -60,14 +60,14 @@ schedulers_alive(Config) when is_list(Config) -> ?line NoSchedulers = erlang:system_info(schedulers), UsedScheds = try - ?line ?t:format("Number of schedulers configured: ~p~n", [NoSchedulers]), + ?line io:format("Number of schedulers configured: ~p~n", [NoSchedulers]), ?line case erlang:system_info(multi_scheduling) of blocked -> ?line ct:fail(multi_scheduling_blocked); disabled -> ?line ok; enabled -> - ?t:format("Testing blocking process exit~n"), + io:format("Testing blocking process exit~n"), BF = fun () -> blocked = erlang:system_flag(multi_scheduling, block), @@ -86,7 +86,7 @@ schedulers_alive(Config) when is_list(Config) -> ?line [] = erlang:system_info(multi_scheduling_blockers), ?line ok end, - ?t:format("Testing blocked~n"), + io:format("Testing blocked~n"), ?line erlang:system_flag(multi_scheduling, block), ?line case erlang:system_info(multi_scheduling) of enabled -> @@ -120,7 +120,7 @@ schedulers_alive(Config) when is_list(Config) -> %% node_and_dist_references will use emulator interal thread blocking... erts_debug:get_internal_state(node_and_dist_references), erts_debug:set_internal_state(available_internal_state, false), - ?t:format("Testing not blocked~n"), + io:format("Testing not blocked~n"), ?line Ps2 = lists:map( fun (_) -> spawn_link(fun () -> @@ -170,7 +170,7 @@ wait_on_used_scheduler({SIDs, SIDsLen} = State) -> true -> wait_on_used_scheduler(State); false -> - ?t:format("Scheduler ~p used~n", [SID]), + io:format("Scheduler ~p used~n", [SID]), {[SID|SIDs], SIDsLen+1} end end. -- cgit v1.2.3 From 5611e47606d8d691331f2eb4b7ed87bdd8ba9270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 9 Mar 2016 16:52:21 +0100 Subject: Eliminate use of ?config() macro --- erts/emulator/test/a_SUITE.erl | 2 +- erts/emulator/test/alloc_SUITE.erl | 8 ++-- erts/emulator/test/async_ports_SUITE.erl | 2 +- erts/emulator/test/beam_SUITE.erl | 2 +- erts/emulator/test/beam_literals_SUITE.erl | 2 +- erts/emulator/test/big_SUITE.erl | 4 +- erts/emulator/test/binary_SUITE.erl | 6 +-- erts/emulator/test/bs_construct_SUITE.erl | 2 +- erts/emulator/test/busy_port_SUITE.erl | 10 ++--- erts/emulator/test/call_trace_SUITE.erl | 2 +- erts/emulator/test/code_SUITE.erl | 34 ++++++++-------- erts/emulator/test/code_parallel_load_SUITE.erl | 2 +- erts/emulator/test/ddll_SUITE.erl | 54 ++++++++++++------------- erts/emulator/test/distribution_SUITE.erl | 4 +- erts/emulator/test/driver_SUITE.erl | 40 +++++++++--------- erts/emulator/test/efile_SUITE.erl | 4 +- erts/emulator/test/erl_drv_thread_SUITE.erl | 2 +- erts/emulator/test/erts_debug_SUITE.erl | 2 +- erts/emulator/test/estone_SUITE.erl | 4 +- erts/emulator/test/float_SUITE.erl | 6 +-- erts/emulator/test/fun_SUITE.erl | 4 +- erts/emulator/test/ignore_cores.erl | 10 ++--- erts/emulator/test/message_queue_data_SUITE.erl | 2 +- erts/emulator/test/module_info_SUITE.erl | 2 +- erts/emulator/test/monitor_SUITE.erl | 2 +- erts/emulator/test/mtx_SUITE.erl | 2 +- erts/emulator/test/nif_SUITE.erl | 12 +++--- erts/emulator/test/nif_SUITE_data/nif_mod.erl | 2 +- erts/emulator/test/nif_SUITE_data/tester.erl | 2 +- erts/emulator/test/old_scheduler_SUITE.erl | 4 +- erts/emulator/test/port_SUITE.erl | 42 +++++++++---------- erts/emulator/test/port_bif_SUITE.erl | 12 +++--- erts/emulator/test/process_SUITE.erl | 4 +- erts/emulator/test/scheduler_SUITE.erl | 6 +-- erts/emulator/test/send_term_SUITE.erl | 4 +- erts/emulator/test/signal_SUITE.erl | 2 +- erts/emulator/test/smoke_test_SUITE.erl | 2 +- erts/emulator/test/system_info_SUITE.erl | 2 +- erts/emulator/test/system_profile_SUITE.erl | 2 +- erts/emulator/test/time_SUITE.erl | 2 +- erts/emulator/test/trace_SUITE.erl | 2 +- erts/emulator/test/trace_call_time_SUITE.erl | 2 +- erts/emulator/test/trace_local_SUITE.erl | 4 +- erts/emulator/test/trace_nif_SUITE.erl | 2 +- erts/emulator/test/trace_port_SUITE.erl | 2 +- erts/emulator/test/tuple_SUITE.erl | 2 +- erts/emulator/test/unique_SUITE.erl | 2 +- 47 files changed, 164 insertions(+), 164 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl index ec0c42db06..ebaf23b28a 100644 --- a/erts/emulator/test/a_SUITE.erl +++ b/erts/emulator/test/a_SUITE.erl @@ -43,7 +43,7 @@ long_timers(doc) -> long_timers(suite) -> []; long_timers(Config) when is_list(Config) -> - Dir = ?config(data_dir, Config), + Dir = proplists:get_value(data_dir, Config), ?line long_timers_test:start(Dir), ?line {comment, "Testcase started! This test will run in parallel with the " diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 81b52525e3..419f92c4a6 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -182,8 +182,8 @@ drv_case(Config, Mode, NodeOpts) when is_list(Config) -> end. run_drv_case(Config, Mode) -> - DataDir = ?config(data_dir,Config), - CaseName = ?config(testcase,Config), + DataDir = proplists:get_value(data_dir,Config), + CaseName = proplists:get_value(testcase,Config), File = filename:join(DataDir, CaseName), {ok,CaseName,Bin} = compile:file(File, [binary,return_errors]), {module,CaseName} = erlang:load_module(CaseName,Bin), @@ -341,7 +341,7 @@ handle_result(_State, Result0) -> end. start_node(Config, Opts) when is_list(Config), is_list(Opts) -> - case ?config(debug,Config) of + case proplists:get_value(debug,Config) of true -> {ok, node()}; _ -> start_node_1(Config, Opts) end. @@ -350,7 +350,7 @@ start_node_1(Config, Opts) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" - ++ atom_to_list(?config(testcase, Config)) + ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" diff --git a/erts/emulator/test/async_ports_SUITE.erl b/erts/emulator/test/async_ports_SUITE.erl index 9647d87c32..f0f5fb5687 100644 --- a/erts/emulator/test/async_ports_SUITE.erl +++ b/erts/emulator/test/async_ports_SUITE.erl @@ -21,7 +21,7 @@ all() -> [permanent_busy_test]. permanent_busy_test(Config) -> - ExePath = filename:join(?config(data_dir, Config), "cport"), + ExePath = filename:join(proplists:get_value(data_dir, Config), "cport"), Self = self(), spawn_link( fun() -> diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl index d2b234ae07..07dfeb6633 100644 --- a/erts/emulator/test/beam_SUITE.erl +++ b/erts/emulator/test/beam_SUITE.erl @@ -95,7 +95,7 @@ apply_last_bif(Config) when is_list(Config) -> %% Test three high register numbers in a put_list instruction %% (to test whether packing works properly). packed_registers(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Mod = packed_regs, Name = filename:join(PrivDir, atom_to_list(Mod) ++ ".erl"), diff --git a/erts/emulator/test/beam_literals_SUITE.erl b/erts/emulator/test/beam_literals_SUITE.erl index 29c6a5c7ac..4cfc8e7d12 100644 --- a/erts/emulator/test/beam_literals_SUITE.erl +++ b/erts/emulator/test/beam_literals_SUITE.erl @@ -428,7 +428,7 @@ fconv_2(F) when is_float(F) -> 6.0 + F. literal_case_expression(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), + ?line DataDir = proplists:get_value(data_dir, Config), ?line Src = filename:join(DataDir, "literal_case_expression"), ?line {ok,literal_case_expression=Mod,Code} = compile:file(Src, [from_asm,binary]), diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl index 37e90df60e..3cc812784a 100644 --- a/erts/emulator/test/big_SUITE.erl +++ b/erts/emulator/test/big_SUITE.erl @@ -87,7 +87,7 @@ negative(Config) when is_list(Config) -> %% Find test file test_file(Config, Name) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), filename:join(DataDir, Name). %% @@ -256,7 +256,7 @@ big_literals(doc) -> big_literals(Config) when is_list(Config) -> %% Note: The literal test cannot be compiler on a pre-R4 Beam emulator, %% so we compile it now. - ?line DataDir = ?config(data_dir, Config), + ?line DataDir = proplists:get_value(data_dir, Config), ?line Test = filename:join(DataDir, "literal_test"), ?line {ok, Mod, Bin} = compile:file(Test, [binary]), ?line {module, Mod} = code:load_binary(Mod, Mod, Bin), diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 6ace683fe7..576bcd0688 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -697,7 +697,7 @@ corrupter(_Bin, _) -> more_bad_terms(suite) -> []; more_bad_terms(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line BadFile = filename:join(Data, "bad_binary"), ?line ok = io:format("File: ~s\n", [BadFile]), ?line case file:read_file(BadFile) of @@ -999,7 +999,7 @@ ordering(Config) when is_list(Config) -> ?line true = B1 > fun() -> 1 end, ?line true = B1 > fun erlang:send/2, - ?line Path = ?config(priv_dir, Config), + ?line Path = proplists:get_value(priv_dir, Config), ?line AFile = filename:join(Path, "vanilla_file"), ?line Port = open_port(AFile, [out]), ?line true = B1 > Port, @@ -1332,7 +1332,7 @@ robustness(Config) when is_list(Config) -> %% OTP-8180: Test several terms that have been known to crash the emulator. %% (Thanks to Scott Lystig Fritchie.) otp_8180(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line Wc = filename:join(Data, "zzz.*"), Files = filelib:wildcard(Wc), [run_otp_8180(F) || F <- Files], diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 42a710aaf9..bc5fb4f379 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -787,7 +787,7 @@ bs_add(Config) when is_list(Config) -> return], %% Write assembly file and assemble it. - ?line PrivDir = ?config(priv_dir, Config), + ?line PrivDir = proplists:get_value(priv_dir, Config), ?line RootName = filename:join(PrivDir, atom_to_list(Mod)), ?line AsmFile = RootName ++ ".S", ?line {ok,Fd} = file:open(AsmFile, [write]), diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index 172936dd82..ee82356646 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -375,7 +375,7 @@ hs_test(Config, HardBusy) when is_list(Config) -> false -> 'soft_busy_drv' end, ?line erl_ddll:start(), - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), case erl_ddll:load_driver(Path, DrvName) of ok -> ok; {error, Error} -> @@ -525,7 +525,7 @@ scheduling_delay_busy(Config) -> Validation = [{seq,10,lists:seq(1,50)}], - port_scheduling(Scenario,Validation,?config(data_dir,Config)). + port_scheduling(Scenario,Validation,proplists:get_value(data_dir,Config)). scheduling_delay_busy_nosuspend(Config) -> @@ -544,7 +544,7 @@ scheduling_delay_busy_nosuspend(Config) -> Validation = [{eq,10,nosuspend},{seq,20,[1,2]}], - port_scheduling(Scenario,Validation,?config(data_dir,Config)). + port_scheduling(Scenario,Validation,proplists:get_value(data_dir,Config)). scheduling_busy_link(Config) -> @@ -567,7 +567,7 @@ scheduling_busy_link(Config) -> Validation = [{seq,10,[1]}, {seq,20,[{'EXIT',noproc}]}], - port_scheduling(Scenario,Validation,?config(data_dir,Config)). + port_scheduling(Scenario,Validation,proplists:get_value(data_dir,Config)). process_init(DrvName,Owner) -> process_flag(trap_exit,true), @@ -862,7 +862,7 @@ fun_spawn(Fun, Args) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% load_busy_driver(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), + ?line DataDir = proplists:get_value(data_dir, Config), ?line erl_ddll:start(), case erl_ddll:load_driver(DataDir, "busy_drv") of ok -> ok; diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index d99bba32cd..6035de9d75 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -366,7 +366,7 @@ upgrade_do(V1, V2, TraceLocalVersion) -> ok. compile_version(Module, Version, Config) -> - Data = ?config(data_dir, Config), + Data = proplists:get_value(data_dir, Config), File = filename:join(Data, atom_to_list(Module)), {ok,Module,Bin} = compile:file(File, [{d,'VERSION',Version}, binary,report]), diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index a040eee50a..0c09fe6f7b 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -89,7 +89,7 @@ versions(Config) when is_list(Config) -> ok. compile_version(Version, Config) -> - Data = ?config(data_dir, Config), + Data = proplists:get_value(data_dir, Config), File = filename:join(Data, "versions"), {ok,versions,Bin} = compile:file(File, [{d,'VERSION',Version}, binary,report]), @@ -114,7 +114,7 @@ check_version(Pid) -> end. new_binary_types(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), ?line {ok,my_code_test,Bin} = compile:file(File, [binary]), ?line {module,my_code_test} = erlang:load_module(my_code_test, @@ -138,8 +138,8 @@ new_binary_types(Config) when is_list(Config) -> ok. t_check_process_code(Config) when is_list(Config) -> - ?line Priv = ?config(priv_dir, Config), - ?line Data = ?config(data_dir, Config), + ?line Priv = proplists:get_value(priv_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), ?line Code = filename:join(Priv, "my_code_test"), @@ -250,8 +250,8 @@ t_check_process_code_ets(Config) when is_list(Config) -> end. do_check_process_code_ets(Config) -> - ?line Priv = ?config(priv_dir, Config), - ?line Data = ?config(data_dir, Config), + ?line Priv = proplists:get_value(priv_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), ?line erlang:purge_module(my_code_test), @@ -303,7 +303,7 @@ fun_refc(F) -> %% Test the erlang:check_old_code/1 BIF. t_check_old_code(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), ?line erlang:purge_module(my_code_test), @@ -334,7 +334,7 @@ external_fun(Config) when is_list(Config) -> ?line {'EXIT',{undef,_}} = (catch ExtFun(answer)), ?line false = erlang:function_exported(another_code_test, x, 1), ?line false = lists:member(another_code_test, erlang:loaded()), - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "another_code_test"), ?line {ok,another_code_test,Code} = compile:file(File, [binary,report]), ?line {module,another_code_test} = erlang:load_module(another_code_test, Code), @@ -342,7 +342,7 @@ external_fun(Config) when is_list(Config) -> ok. get_chunk(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), ?line {ok,my_code_test,Code} = compile:file(File, [binary]), @@ -367,7 +367,7 @@ get_chunk_ok(Chunk, Code) -> end. module_md5(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), ?line {ok,my_code_test,Code} = compile:file(File, [binary]), @@ -393,7 +393,7 @@ make_stub(Config) when is_list(Config) -> catch erlang:purge_module(my_code_test), MD5 = erlang:md5(<<>>), - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), ?line {ok,my_code_test,Code} = compile:file(File, [binary]), @@ -428,7 +428,7 @@ make_stub_many_funs(Config) when is_list(Config) -> catch erlang:purge_module(many_funs), MD5 = erlang:md5(<<>>), - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "many_funs"), ?line {ok,many_funs,Code} = compile:file(File, [binary]), @@ -451,7 +451,7 @@ make_stub_many_funs(Config) when is_list(Config) -> ok. constant_pools(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "literals"), ?line {ok,literals,Code} = compile:file(File, [report,binary]), ?line {module,literals} = erlang:load_module(literals, @@ -530,7 +530,7 @@ constant_refc_binaries(Config) when is_list(Config) -> io:format("Binary data (bytes) before test: ~p\n", [Bef]), %% Compile the the literals module. - Data = ?config(data_dir, Config), + Data = proplists:get_value(data_dir, Config), File = filename:join(Data, "literals"), {ok,literals,Code} = compile:file(File, [report,binary]), @@ -652,7 +652,7 @@ wait_for_memory_deallocations() -> %% OTP-7559: c_p->cp could contain garbage and create a false dependency %% to a module in a process. (Thanks to Richard Carlsson.) false_dependency(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "cpbugx"), ?line {ok,cpbugx,Code} = compile:file(File, [binary,report]), @@ -731,7 +731,7 @@ coverage(Config) when is_list(Config) -> ok. fun_confusion(Config) when is_list(Config) -> - Data = ?config(data_dir, Config), + Data = proplists:get_value(data_dir, Config), Src = filename:join(Data, "fun_confusion"), Mod = fun_confusion, @@ -757,7 +757,7 @@ compile_load(Mod, Src, Ver) -> t_copy_literals(Config) when is_list(Config) -> %% Compile the the literals module. - Data = ?config(data_dir, Config), + Data = proplists:get_value(data_dir, Config), File = filename:join(Data, "literals"), {ok,literals,Code} = compile:file(File, [report,binary]), {module,literals} = erlang:load_module(literals, Code), diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl index 95cf9ddac4..e9e7000434 100644 --- a/erts/emulator/test/code_parallel_load_SUITE.erl +++ b/erts/emulator/test/code_parallel_load_SUITE.erl @@ -46,7 +46,7 @@ init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> Config. end_per_testcase(_Func, Config) -> - SConf = ?config(save_config, Config), + SConf = proplists:get_value(save_config, Config), Pids = proplists:get_value(purge_pids, SConf), case check_old_code(?model) of diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl index 0288c9d243..5ef751a4cd 100644 --- a/erts/emulator/test/ddll_SUITE.erl +++ b/erts/emulator/test/ddll_SUITE.erl @@ -76,7 +76,7 @@ unload_on_process_exit(suite) -> unload_on_process_exit(doc) -> ["Check that the driver is unloaded on process exit"]; unload_on_process_exit(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), Parent = self(), ?line Pid = spawn(fun() -> @@ -108,7 +108,7 @@ delayed_unload_with_ports(suite) -> delayed_unload_with_ports(doc) -> ["Check that the driver is unloaded when the last port is closed"]; delayed_unload_with_ports(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line erl_ddll:try_load(Path, echo_drv, []), ?line erl_ddll:try_load(Path, echo_drv, []), ?line Port = open_port({spawn, echo_drv}, [eof]), @@ -131,7 +131,7 @@ unload_due_to_process_exit(suite) -> unload_due_to_process_exit(doc) -> ["Check that the driver with ports is unloaded on process exit"]; unload_due_to_process_exit(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), ?line F3 = fun() -> Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), @@ -163,7 +163,7 @@ no_unload_due_to_process_exit(suite) -> no_unload_due_to_process_exit(doc) -> ["Check that a driver with driver loaded in another process is not unloaded on process exit"]; no_unload_due_to_process_exit(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), ?line F3 = fun() -> Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), @@ -198,7 +198,7 @@ no_unload_due_to_process_exit_2(suite) -> no_unload_due_to_process_exit_2(doc) -> ["Check that a driver with open ports in another process is not unloaded on process exit"]; no_unload_due_to_process_exit_2(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), ?line F3 = fun() -> Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), @@ -233,7 +233,7 @@ unload_reload_thingie(suite) -> unload_reload_thingie(doc) -> ["Check delayed unload and reload"]; unload_reload_thingie(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), ?line F3 = fun() -> @@ -276,7 +276,7 @@ unload_reload_thingie_2(suite) -> unload_reload_thingie_2(doc) -> ["Check delayed unload and reload"]; unload_reload_thingie_2(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), ?line F3 = fun() -> @@ -318,7 +318,7 @@ unload_reload_thingie_3(suite) -> unload_reload_thingie_3(doc) -> ["Check delayed unload and reload failure"]; unload_reload_thingie_3(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), ?line F3 = fun() -> @@ -360,7 +360,7 @@ unload_reload_thingie_3(Config) when is_list(Config) -> reload_pending(suite) -> []; reload_pending(doc) -> ["Reload a driver that is pending on a user"]; reload_pending(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), ?line F3 = fun() -> Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), @@ -410,8 +410,8 @@ reload_pending(Config) when is_list(Config) -> load_fail_init(suite) -> []; load_fail_init(doc) -> ["Tests failure in the init in driver struct."]; load_fail_init(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), - ?line PathFailing = ?config(priv_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), + ?line PathFailing = proplists:get_value(priv_dir, Config), ?line [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path), ?line lists:foreach(fun(Name) -> Src = filename:join([Path,Name]), @@ -436,8 +436,8 @@ load_fail_init(Config) when is_list(Config) -> reload_pending_fail_init(suite) -> []; reload_pending_fail_init(doc) -> ["Reload a driver that is pending but init fails"]; reload_pending_fail_init(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), - ?line PathFailing = ?config(priv_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), + ?line PathFailing = proplists:get_value(priv_dir, Config), ?line [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path), ?line lists:foreach(fun(Name) -> Src = filename:join([Path,Name]), @@ -496,7 +496,7 @@ reload_pending_kill(doc) -> ["Reload a driver with kill_ports option " "that is pending on a user"]; reload_pending_kill(Config) when is_list(Config) -> ?line OldFlag = process_flag(trap_exit,true), - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), ?line F3 = fun() -> Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), @@ -599,7 +599,7 @@ forced_port_killing(suite) -> forced_port_killing(doc) -> ["Check kill_ports option to try_unload "]; forced_port_killing(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line OldFlag=process_flag(trap_exit,true), ?line Parent = self(), ?line F3 = fun() -> @@ -633,7 +633,7 @@ no_trap_exit_and_kill_ports(suite) -> no_trap_exit_and_kill_ports(doc) -> ["Check delayed unload and reload with no trap_exit"]; no_trap_exit_and_kill_ports(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), ?line OldFlag=process_flag(trap_exit,true), ?line F3 = fun() -> @@ -672,7 +672,7 @@ monitor_demonitor(suite) -> monitor_demonitor(doc) -> ["Check monitor and demonitor of drivers"]; monitor_demonitor(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line erl_ddll:try_load(Path, echo_drv, []), ?line Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}), ?line Self = self(), @@ -688,7 +688,7 @@ monitor_demonitor_load(suite) -> monitor_demonitor_load(doc) -> ["Check monitor/demonitor of driver loading"]; monitor_demonitor_load(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line {ok,loaded} = erl_ddll:try_load(Path, echo_drv, []), ?line Port = open_port({spawn, echo_drv}, [eof]), ?line Ref = erl_ddll:monitor(driver,{echo_drv,loaded}), @@ -717,7 +717,7 @@ new_interface(suite) -> new_interface(doc) -> ["Test the new load/unload/reload interface"]; new_interface(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), % Typical scenario ?line ok = erl_ddll:load(Path, echo_drv), ?line Port = open_port({spawn, echo_drv}, [eof]), @@ -777,7 +777,7 @@ new_interface(Config) when is_list(Config) -> ddll_test(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), %?line {error,{already_started,ErlDdllPid}} = erl_ddll:start(), %?line ErlDdllPid = whereis(ddll_server), @@ -811,7 +811,7 @@ ddll_test(Config) when is_list(Config) -> %% Tests errors having to do with bad drivers. errors(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line {ok, L1} = erl_ddll:loaded_drivers(), @@ -839,7 +839,7 @@ reference_count(doc) -> "reaches zero, and that they cannot be unloaded while ", "they are still referenced."]; reference_count(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), %% Spawn a process that loads the driver (and holds a reference %% to it). @@ -881,7 +881,7 @@ kill_port(doc) -> ["Test that a port that uses a driver is killed when the ", "process that loaded the driver dies."]; kill_port(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), %% Spawn a process that loads the driver (and holds a reference %% to it). @@ -916,7 +916,7 @@ dont_kill_port(doc) -> ["Test that a port that uses a driver is not killed when the ", "process that loaded the driver dies and it's nicely opened."]; dont_kill_port(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), %% Spawn a process that loads the driver (and holds a reference %% to it). @@ -954,7 +954,7 @@ dont_kill_port(Config) when is_list(Config) -> properties(doc) -> ["Test that a process that loaded a driver ", "is the only process that can unload it."]; properties(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), % Let another process load the echo driver. Pid=spawn_link(?MODULE, echo_loader, [Path, self()]), @@ -980,7 +980,7 @@ properties(Config) when is_list(Config) -> load_and_unload(doc) -> ["Load two drivers and unload them in load order."]; load_and_unload(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line {ok, Loaded_drivers1} = erl_ddll:loaded_drivers(), ?line ok = erl_ddll:load_driver(Path, echo_drv), ?line ok = erl_ddll:load_driver(Path, dummy_drv), @@ -998,7 +998,7 @@ lock_driver(suite) -> lock_driver(doc) -> ["Check multiple calls to driver_lock_driver"]; lock_driver(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line {ok, _} = erl_ddll:try_load(Path, lock_drv, []), ?line Port1 = open_port({spawn, lock_drv}, [eof]), ?line Port2 = open_port({spawn, lock_drv}, [eof]), diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 1425276544..5f2ecdcbe1 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -729,7 +729,7 @@ stop_dist(doc) -> stop_dist(Config) when is_list(Config) -> ?line Str = os:cmd(atom_to_list(lib:progname()) ++ " -noshell -pa " - ++ ?config(data_dir, Config) + ++ proplists:get_value(data_dir, Config) ++ " -s run"), %% The "true" may be followed by an error report, so ignore anything that %% follows it. @@ -1950,7 +1950,7 @@ start_node(Name, Args, Rel) when is_atom(Name), is_list(Rel) -> start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) -> Name = list_to_atom((atom_to_list(?MODULE) ++ "-" - ++ atom_to_list(?config(testcase, Config)) + ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 3da99b4090..37ad0fd79d 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -181,7 +181,7 @@ end_per_group(_GroupName, Config) -> outputv_errors(doc) -> "Test sending bad types to port with an outputv-capable driver."; outputv_errors(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line erl_ddll:start(), ?line ok = load_driver(Path, outputv_drv), @@ -720,7 +720,7 @@ driver_unloaded(Config) when is_list(Config) -> ?line Die = make_ref(), ?line Loader = spawn(fun () -> erl_ddll:start(), - ok = load_driver(?config(data_dir, + ok = load_driver(proplists:get_value(data_dir, Config), Drv), User ! Loaded, @@ -755,7 +755,7 @@ io_ready_exit(Config) when is_list(Config) -> ?line receive dgawd_handler_started -> ok end, ?line Drv = io_ready_exit_drv, ?line erl_ddll:start(), - ?line ok = load_driver(?config(data_dir, Config), Drv), + ?line ok = load_driver(proplists:get_value(data_dir, Config), Drv), ?line Port = open_port({spawn, Drv}, []), ?line case erlang:port_control(Port, 0, "") of "ok" -> @@ -909,7 +909,7 @@ chkio_test_init(Config) when is_list(Config) -> ?line case catch lists:keysearch(name, 1, ChkIo) of {value, {name, erts_poll}} -> ?line io:format("Before test: ~p~n", [ChkIo]), - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line erl_ddll:start(), ?line ok = load_driver(Path, 'chkio_drv'), ?line process_flag(trap_exit, true), @@ -1386,7 +1386,7 @@ ioq_exit_test(Config, TestNo) -> ?line Drv = ioq_exit_drv, ?line try begin - ?line case load_driver(?config(data_dir, Config), + ?line case load_driver(proplists:get_value(data_dir, Config), Drv) of ok -> ?line ok; {error, permanent} -> ?line ok; @@ -1468,8 +1468,8 @@ ioq_exit_event_async(Config) when is_list(Config) -> vsn_mismatch_test(Config, LoadResult) -> - ?line Path = ?config(data_dir, Config), - ?line DrvName = ?config(testcase, Config), + ?line Path = proplists:get_value(data_dir, Config), + ?line DrvName = proplists:get_value(testcase, Config), ?line LoadResult = load_driver(Path, DrvName), ?line case LoadResult of ok -> @@ -1529,7 +1529,7 @@ peek_non_existing_queue(Config) when is_list(Config) -> ?line Drv = peek_non_existing_queue_drv, ?line try begin - ?line case load_driver(?config(data_dir, Config), + ?line case load_driver(proplists:get_value(data_dir, Config), Drv) of ok -> ?line ok; {error, permanent} -> ?line ok; @@ -1581,7 +1581,7 @@ otp_6879(suite) -> otp_6879(Config) when is_list(Config) -> ?line Drv = 'otp_6879_drv', ?line Parent = self(), - ?line ok = load_driver(?config(data_dir, Config), Drv), + ?line ok = load_driver(proplists:get_value(data_dir, Config), Drv), ?line Procs = lists:map( fun (No) -> spawn_link( @@ -1647,7 +1647,7 @@ run_caller_test(Config, Outputv) -> "false"), output end, - ?line ok = load_driver(?config(data_dir, Config), Drv), + ?line ok = load_driver(proplists:get_value(data_dir, Config), Drv), ?line Port = open_port({spawn, Drv}, []), ?line true = is_port(Port), ?line chk_caller(Port, start, self()), @@ -1736,7 +1736,7 @@ smp_select(Config) when is_list(Config) -> smp_select0(Config) -> ?line DrvName = 'chkio_drv', - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), erl_ddll:start(), ?line ok = load_driver(Path, DrvName), Master = self(), @@ -1795,7 +1795,7 @@ driver_select_use(Config) when is_list(Config) -> driver_select_use0(Config) -> ?line DrvName = 'chkio_drv', - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), erl_ddll:start(), ?line ok = load_driver(Path, DrvName), ?line Port = open_port({spawn, DrvName}, []), @@ -1823,7 +1823,7 @@ thread_mseg_alloc_cache_clean(Config) when is_list(Config) -> ?line {skipped, "driver_alloc() using too low single block threshold"}; {true, _MsegAllocInfo, SBCT} -> ?line DrvName = 'thr_alloc_drv', - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line erl_ddll:start(), ?line ok = load_driver(Path, DrvName), ?line Port = open_port({spawn, DrvName}, []), @@ -1908,7 +1908,7 @@ thread_mseg_alloc_cache_clean_test(Port, N, CCI, Size) -> ?line thread_mseg_alloc_cache_clean_test(Port, N-1, CCI, Size). otp_9302(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line erl_ddll:start(), ?line ok = load_driver(Path, otp_9302_drv), ?line Port = open_port({spawn, otp_9302_drv}, []), @@ -1950,7 +1950,7 @@ thr_free_drv(Config) when is_list(Config) -> end. thr_free_drv_do(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line erl_ddll:start(), ?line ok = load_driver(Path, thr_free_drv), ?line MemBefore = driver_alloc_size(), @@ -1982,7 +1982,7 @@ thr_free_drv_control(Port, N) -> end. async_blast(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line erl_ddll:start(), ?line ok = load_driver(Path, async_blast_drv), ?line SchedOnln = erlang:system_info(schedulers_online), @@ -2045,7 +2045,7 @@ thr_msg_blast(Config) when is_list(Config) -> false -> {skipped, "Non-SMP emulator; nothing to test..."}; true -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), erl_ddll:start(), ok = load_driver(Path, thr_msg_blast_drv), MemBefore = driver_alloc_size(), @@ -2124,7 +2124,7 @@ consume_timeslice(Config) when is_list(Config) -> %% the port instead. %% - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), erl_ddll:start(), ok = load_driver(Path, consume_timeslice_drv), Port = open_port({spawn, consume_timeslice_drv}, [{parallelism, false}]), @@ -2518,7 +2518,7 @@ erl_millisecs(MonotonicTime) -> %% Start/stop drivers. start_driver(Config, Name, Binary) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), erl_ddll:start(), %% Load the driver @@ -2566,7 +2566,7 @@ start_node(Config) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" - ++ atom_to_list(?config(testcase, Config)) + ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index 79a2d20fe0..cc061c0cfe 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -90,7 +90,7 @@ file_keys(Dir,Num,FdList,FnList) -> async_dist(doc) -> "Check that the distribution of files over async threads is fair"; async_dist(Config) when is_list(Config) -> - DataDir = ?config(data_dir,Config), + DataDir = proplists:get_value(data_dir,Config), TestFile = filename:join(DataDir, "existing_file"), Dir = filename:dirname(code:which(?MODULE)), AsyncSizes = [7,10,100,255,256,64,63,65], @@ -132,7 +132,7 @@ async_dist(Config) when is_list(Config) -> iter_max_files(suite) -> []; iter_max_files(Config) when is_list(Config) -> - DataDir = ?config(data_dir,Config), + DataDir = proplists:get_value(data_dir,Config), TestFile = filename:join(DataDir, "existing_file"), N = 10, %% Run on a different node in order to set the max ports diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl index b0ac1c703c..7d5e57eb0c 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE.erl +++ b/erts/emulator/test/erl_drv_thread_SUITE.erl @@ -82,7 +82,7 @@ drv_case(Config, CaseName, Command, TimeTrap) when is_list(Config), run_drv_case(Config, CaseName, Command, TimeTrap) -> ct:timetrap({seconds, TimeTrap}), - ?line DataDir = ?config(data_dir,Config), + ?line DataDir = proplists:get_value(data_dir,Config), case erl_ddll:load_driver(DataDir, CaseName) of ok -> ok; {error, Error} -> diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl index d4814bd8f4..e757ebdb3d 100644 --- a/erts/emulator/test/erts_debug_SUITE.erl +++ b/erts/emulator/test/erts_debug_SUITE.erl @@ -157,7 +157,7 @@ term_type(Config) when is_list(Config) -> df(Config) when is_list(Config) -> P0 = pps(), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), ok = file:set_cwd(PrivDir), AllLoaded = [M || {M,_} <- code:all_loaded()], diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index 2d53cb1cd1..33728c3f5a 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -78,7 +78,7 @@ estone(suite) -> estone(doc) -> ["EStone Test"]; estone(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir,Config), + ?line DataDir = proplists:get_value(data_dir,Config), ?line Mhz=get_cpu_speed(os:type(),DataDir), ?line L = ?MODULE:macro(?MODULE:micros(),DataDir), ?line {Total, Stones} = sum_micros(L, 0, 0), @@ -87,7 +87,7 @@ estone(Config) when is_list(Config) -> integer_to_list(Stones) ++ " ESTONES"}. estone_bench(Config) -> - DataDir = ?config(data_dir,Config), + DataDir = proplists:get_value(data_dir,Config), L = ?MODULE:macro(?MODULE:micros(),DataDir), [ct_event:notify( #event{name = benchmark_data, diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index 9eb4456de0..67e14e015e 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -81,12 +81,12 @@ fpe(Config) when is_list(Config) -> -define(ERTS_FP_THREAD_TEST, 1). fp_drv(Config) when is_list(Config) -> - fp_drv_test(?ERTS_FP_CONTROL_TEST, ?config(data_dir, Config)). + fp_drv_test(?ERTS_FP_CONTROL_TEST, proplists:get_value(data_dir, Config)). fp_drv_thread(Config) when is_list(Config) -> %% Run in a separate node since it used to crash the emulator... ?line Parent = self(), - ?line DrvDir = ?config(data_dir, Config), + ?line DrvDir = proplists:get_value(data_dir, Config), ?line {ok,Node} = start_node(Config), ?line Tester = spawn_link(Node, fun () -> @@ -278,7 +278,7 @@ start_node(Config) when is_list(Config) -> ?line Pa = filename:dirname(code:which(?MODULE)), ?line Name = list_to_atom(atom_to_list(?MODULE) ++ "-" - ++ atom_to_list(?config(testcase, Config)) + ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 9020c7f7b9..f89cc623c4 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -326,7 +326,7 @@ ordering(Config) when is_list(Config) -> %% Create a port and ref. - ?line Path = ?config(priv_dir, Config), + ?line Path = proplists:get_value(priv_dir, Config), ?line AFile = filename:join(Path, "vanilla_file"), ?line P = open_port(AFile, [out]), ?line R = make_ref(), @@ -400,7 +400,7 @@ fun_to_port(Config) when is_list(Config) -> ok. fun_to_port(Config, IoList) -> - Path = ?config(priv_dir, Config), + Path = proplists:get_value(priv_dir, Config), AFile = filename:join(Path, "vanilla_file"), Port = open_port(AFile, [out]), case catch port_command(Port, IoList) of diff --git a/erts/emulator/test/ignore_cores.erl b/erts/emulator/test/ignore_cores.erl index e40b91392c..4af5efe834 100644 --- a/erts/emulator/test/ignore_cores.erl +++ b/erts/emulator/test/ignore_cores.erl @@ -53,7 +53,7 @@ init(Config) -> fini(Config) -> #ignore_cores{org_cwd = OrgCWD, org_path = OrgPath, - org_pwd_env = OrgPWD} = ?config(ignore_cores, Config), + org_pwd_env = OrgPWD} = proplists:get_value(ignore_cores, Config), ok = file:set_cwd(OrgCWD), true = code:set_path(OrgPath), case OrgPWD of @@ -70,10 +70,10 @@ setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite), is_list(Config) -> #ignore_cores{org_cwd = OrgCWD, org_path = OrgPath, - org_pwd_env = OrgPWD} = ?config(ignore_cores, Config), + org_pwd_env = OrgPWD} = proplists:get_value(ignore_cores, Config), Path = lists:map(fun (".") -> OrgCWD; (Dir) -> Dir end, OrgPath), true = code:set_path(Path), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), IgnDir = filename:join([PrivDir, atom_to_list(Suite) ++ "_" @@ -119,7 +119,7 @@ restore(Config) -> org_path = OrgPath, org_pwd_env = OrgPWD, ign_dir = IgnDir, - cores_dir = CoresDir} = ?config(ignore_cores, Config), + cores_dir = CoresDir} = proplists:get_value(ignore_cores, Config), try case CoresDir of false -> @@ -155,5 +155,5 @@ restore(Config) -> dir(Config) -> - #ignore_cores{ign_dir = Dir} = ?config(ignore_cores, Config), + #ignore_cores{ign_dir = Dir} = proplists:get_value(ignore_cores, Config), Dir. diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl index 36a8311cd2..bbf6b7bb2c 100644 --- a/erts/emulator/test/message_queue_data_SUITE.erl +++ b/erts/emulator/test/message_queue_data_SUITE.erl @@ -202,7 +202,7 @@ start_node(Config, Opts) when is_list(Config), is_list(Opts) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" - ++ atom_to_list(?config(testcase, Config)) + ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index 9f07ff9c0c..f3738c4023 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -72,7 +72,7 @@ functions(Config) when is_list(Config) -> %% Test that deleted modules cause badarg deleted(Config) when is_list(Config) -> - Data = ?config(data_dir, Config), + Data = proplists:get_value(data_dir, Config), File = filename:join(Data, "module_info_test"), {ok,module_info_test,Code} = compile:file(File, [binary]), {module,module_info_test} = erlang:load_module(module_info_test, Code), diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 05dfc34ed7..d37b76ec20 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -1014,7 +1014,7 @@ start_node(Config) -> start_node(Config, ""). start_node(Config, Args) -> - TestCase = ?config(testcase, Config), + TestCase = proplists:get_value(testcase, Config), PA = filename:dirname(code:which(?MODULE)), ESTime = erlang:monotonic_time(1) + erlang:time_offset(1), Unique = erlang:unique_integer([positive]), diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl index 9f0c3bdad0..a84ca0be56 100644 --- a/erts/emulator/test/mtx_SUITE.erl +++ b/erts/emulator/test/mtx_SUITE.erl @@ -78,7 +78,7 @@ all() -> hammer_sched_freqread_tryrwlock]. init_per_suite(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), Lib = filename:join([DataDir, atom_to_list(?MODULE)]), case {erlang:load_nif(Lib, none),erlang:system_info(threads)} of {{error,_},false} -> diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index ff54006643..6a309b8b7f 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -101,7 +101,7 @@ reload(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "nif_mod"), ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin), @@ -137,7 +137,7 @@ upgrade(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "nif_mod"), ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin), @@ -656,7 +656,7 @@ resource_takeover(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "nif_mod"), ?line {ok,nif_mod,ModBin} = compile:file(File, [binary,return_errors]), ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin), @@ -1036,7 +1036,7 @@ threading(Config) when is_list(Config) -> end. threading_do(Config) -> - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "tester"), ?line {ok,tester,ModBin} = compile:file(File, [binary,return_errors]), ?line {module,tester} = erlang:load_module(tester,ModBin), @@ -1330,7 +1330,7 @@ neg(Config) when is_list(Config) -> ?line {'EXIT',{badarg,_}} = (catch erlang:load_nif(badarg, 0)), ?line {error,{load_failed,_}} = erlang:load_nif("pink_unicorn", 0), - ?line Data = ?config(data_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "nif_mod"), ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin), @@ -1384,7 +1384,7 @@ ensure_lib_loaded(Config) -> ensure_lib_loaded(Config, Ver) -> ?line case lib_version() of undefined -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line Lib = "nif_SUITE." ++ integer_to_list(Ver), ?line ok = erlang:load_nif(filename:join(Path,Lib), []); Ver when is_integer(Ver) -> diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl index aa3c90fe9d..e12db72bea 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl @@ -33,7 +33,7 @@ load_nif_lib(Config, Ver) -> load_nif_lib(Config, Ver, []). load_nif_lib(Config, Ver, LoadInfo) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), erlang:load_nif(filename:join(Path,libname(Ver)), LoadInfo). libname(no_init) -> libname(3); diff --git a/erts/emulator/test/nif_SUITE_data/tester.erl b/erts/emulator/test/nif_SUITE_data/tester.erl index 32b9ef1826..388b5cab61 100644 --- a/erts/emulator/test/nif_SUITE_data/tester.erl +++ b/erts/emulator/test/nif_SUITE_data/tester.erl @@ -6,7 +6,7 @@ load_nif_lib(Config, LibName) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), erlang:load_nif(filename:join(Path,LibName), []). run() -> diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl index 5fef53a564..612487cd35 100644 --- a/erts/emulator/test/old_scheduler_SUITE.erl +++ b/erts/emulator/test/old_scheduler_SUITE.erl @@ -69,12 +69,12 @@ init_per_testcase(_Case, Config) -> end_per_testcase(_Case, Config) -> erlang:system_flag(multi_scheduling, unblock), - Prio=?config(prio, Config), + Prio=proplists:get_value(prio, Config), process_flag(priority, Prio), ok. ok(Config) when is_list(Config) -> - case ?config(multi_scheduling, Config) of + case proplists:get_value(multi_scheduling, Config) of blocked -> {comment, "Multi-scheduling blocked during test. This testcase was not " diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 0d8cb77161..8a52305414 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -363,7 +363,7 @@ input_only(Config) when is_list(Config) -> output_only(Config) when is_list(Config) -> ct:timetrap({seconds, 100}), - Dir = ?config(priv_dir, Config), + Dir = proplists:get_value(priv_dir, Config), %% First we test that the port program gets the data Filename = filename:join(Dir, "output_only_stream"), @@ -504,8 +504,8 @@ make_dying_port(Config) when is_list(Config) -> port_program_with_path(suite) -> []; port_program_with_path(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), - DataDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), %% Create a copy of the port test program in a directory not %% included in PATH (i.e. in priv_dir), with the name 'my_port_test.exe'. @@ -545,7 +545,7 @@ 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) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), %% Create a file with the file driver and read it back using %% open_port/2. @@ -566,7 +566,7 @@ open_input_file_port(Config) when is_list(Config) -> open_output_file_port(suite) -> []; open_output_file_port(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), %% Create a file with open_port/2 and read it back with %% the file driver. @@ -841,7 +841,7 @@ env(doc) -> ["Test that the 'env' option works"]; env(Config) when is_list(Config) -> ct:timetrap({minutes, 1}), - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), Temp = filename:join(Priv, "env_fun.bin"), PluppVal = "dirty monkey", @@ -975,7 +975,7 @@ cd(Config) when is_list(Config) -> ct:timetrap({minutes, 1}), Program = atom_to_list(lib:progname()), - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), TestDir = filename:join(DataDir, "dir"), Cmd = Program ++ " -pz " ++ DataDir ++ " -noshell -s port_test pwd -s erlang halt", @@ -1064,7 +1064,7 @@ otp_3906(Config, OSName) -> case lists:keysearch('CC', 1, Variables) of {value,{'CC', CC}} -> SuiteDir = filename:dirname(code:which(?MODULE)), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Prog = otp_3906_make_prog(CC, PrivDir), {ok, Node} = test_server:start_node(otp_3906, slave, @@ -1330,7 +1330,7 @@ spawn_driver(suite) -> spawn_driver(doc) -> ["Test spawning a driver specifically"]; spawn_driver(Config) when is_list(Config) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "echo_drv"), Port = erlang:open_port({spawn_driver, "echo_drv"}, []), Port ! {self(), {command, "Hello port!"}}, @@ -1365,7 +1365,7 @@ parallelism_option(suite) -> parallelism_option(doc) -> ["Test parallelism option of open_port"]; parallelism_option(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line ok = load_driver(Path, "echo_drv"), ?line Port = erlang:open_port({spawn_driver, "echo_drv"}, [{parallelism, true}]), @@ -1400,7 +1400,7 @@ spawn_executable(suite) -> spawn_executable(doc) -> ["Test spawning an executable specifically"]; spawn_executable(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), EchoArgs1 = filename:join([DataDir,"echo_args"]), ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)), [ExactFile1] = run_echo_args(DataDir,[]), @@ -1427,8 +1427,8 @@ spawn_executable(Config) when is_list(Config) -> [ExactFile1,"hello world","dlrow olleh"] = run_echo_args_2(unicode:characters_to_binary("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\"")), - PrivDir = ?config(priv_dir, Config), - SpaceDir =filename:join([PrivDir,"With Spaces"]), + PrivDir = proplists:get_value(priv_dir, Config), + SpaceDir = filename:join([PrivDir,"With Spaces"]), file:make_dir(SpaceDir), Executable = filename:basename(ExactFile1), file:copy(ExactFile1,filename:join([SpaceDir,Executable])), @@ -1640,7 +1640,7 @@ 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) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "echo_drv"), Port = erlang:open_port({spawn, "echo_drv"}, []), Port ! {self(), {command, "Hello port!"}}, @@ -1686,7 +1686,7 @@ 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) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "exit_drv"), Port = otp_5112_get_wrapped_port(), io:format("Max ports: ~p~n",[max_ports()]), @@ -1732,7 +1732,7 @@ otp_5119(suite) -> otp_5119(doc) -> ["Test that port index is not unnecessarily wrapped"]; otp_5119(Config) when is_list(Config) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "exit_drv"), PI1 = port_ix(otp_5119_fill_empty_port_tab([])), Port2 = erlang:open_port({spawn, "exit_drv"}, []), @@ -1776,7 +1776,7 @@ port_ix(Port) when is_port(Port) -> otp_6224(doc) -> ["Check that port command failure doesn't crash the emulator"]; otp_6224(suite) -> []; otp_6224(Config) when is_list(Config) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "failure_drv"), Go = make_ref(), Failer = spawn(fun () -> @@ -2223,13 +2223,13 @@ fun_spawn(Fun, Args) -> spawn_link(erlang, apply, [Fun, Args]). port_test(Config) when is_list(Config) -> - filename:join(?config(data_dir, Config), "port_test"). + filename:join(proplists:get_value(data_dir, Config), "port_test"). ports(doc) -> "Test that erlang:ports/0 returns a consistent snapshot of ports"; ports(suite) -> []; ports(Config) when is_list(Config) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "exit_drv"), receive after 1000 -> ok end, % Wait for other ports to stabilize @@ -2339,7 +2339,7 @@ close_deaf_port(doc) -> ["Send data to port program that does not read it, then close_deaf_port(suite) -> []; close_deaf_port(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), DeadPort = os:find_executable("dead_port", DataDir), Port = open_port({spawn,DeadPort++" 60"},[]), erlang:port_command(Port,"Hello, can you hear me!?!?"), @@ -2368,7 +2368,7 @@ close_deaf_port_1(N, Cmd) -> %% Hammer from multiple processes a while %% and then abrubtly close the port (OTP-12208). port_setget_data(Config) when is_list(Config) -> - ok = load_driver(?config(data_dir, Config), "echo_drv"), + ok = load_driver(proplists:get_value(data_dir, Config), "echo_drv"), Port = erlang:open_port({spawn_driver, "echo_drv"}, []), NSched = erlang:system_info(schedulers_online), diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index bdadfc48ac..9b794be415 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -76,7 +76,7 @@ do_command(P, Data) -> %% port_command/2: badarg 1st arg command_e_1(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), @@ -98,7 +98,7 @@ do_command_e_1(Program) -> %% port_command/2: badarg 2nd arg command_e_2(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), @@ -120,7 +120,7 @@ do_command_e_2(Program) -> %% port_command/2: Posix signals trapped command_e_3(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), @@ -139,7 +139,7 @@ command_e_3(Config) when is_list(Config) -> %% port_command/2: Posix exit signals not trapped command_e_4(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), @@ -236,7 +236,7 @@ do_port_info_os_pid() -> ok. port_info_race(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), Program = filename:join(DataDir, "port_test"), Top = self(), P1 = open_port({spawn,Program}, [{packet,1}]), @@ -428,7 +428,7 @@ echo(P, Size) -> Packet = erlang:port_control(P, $e, [unaligned_sub_bin(Bin)]). load_control_drv(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), erl_ddll:start(), ok = load_driver(DataDir, "control_drv"). diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 6c733195b7..ff55a0102f 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -118,7 +118,7 @@ init_per_suite(Config) -> [{started_apps, A}|Config]. end_per_suite(Config) -> - As = ?config(started_apps, Config), + As = proplists:get_value(started_apps, Config), lists:foreach(fun (A) -> application:stop(A) end, As), catch erts_debug:set_internal_state(available_internal_state, false), Config. @@ -2495,7 +2495,7 @@ start_node(Config, Args) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" - ++ atom_to_list(?config(testcase, Config)) + ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 38f8dcc8d4..dbbf1c43ac 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -1175,7 +1175,7 @@ dirty_scheduler_exit_test(Config) -> {ok, Node} = start_node(Config, "+SDio 1"), [ok] = mcall(Node, [fun() -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), Lib = atom_to_list(?MODULE), ok = erlang:load_nif(filename:join(Path,Lib), []), ok = test_dirty_scheduler_exit() @@ -1802,7 +1802,7 @@ restore_erl_rel_flags(OldValue) -> ok(too_slow, _Config) -> {comment, "Too slow system to do any actual testing..."}; ok(_Res, Config) -> - ?config(ok_res, Config). + proplists:get_value(ok_res, Config). chk_result(too_slow, _LWorkers, @@ -2227,7 +2227,7 @@ start_node(Config, Args) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" - ++ atom_to_list(?config(testcase, Config)) + ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index 1915b44b4b..ed70d14a1b 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -65,7 +65,7 @@ basic(Config) when is_list(Config) -> ?line {Self,Child} = receive_any(), %% ERL_DRV_EXT2TERM - ?line ExpectExt2Term = expected_ext2term_drv(?config(data_dir, Config)), + ?line ExpectExt2Term = expected_ext2term_drv(proplists:get_value(data_dir, Config)), ?line ExpectExt2Term = term(P, 5), %% ERL_DRV_INT, ERL_DRV_UINT @@ -180,7 +180,7 @@ chk_temp_alloc() -> %% Start/stop drivers. start_driver(Config, Name) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), erl_ddll:start(), ok = load_driver(Path, Name), open_port({spawn, Name}, []). diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index e6ec79141b..0784b1df36 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -503,7 +503,7 @@ repeat(Fun, N) when is_integer(N) -> start_node(Config) -> Name = list_to_atom(atom_to_list(?MODULE) - ++ "-" ++ atom_to_list(?config(testcase, Config)) + ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), Pa = filename:dirname(code:which(?MODULE)), diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 3c616b4304..9b0db358f1 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -150,7 +150,7 @@ start_node(Config, Args) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" - ++ atom_to_list(?config(testcase, Config)) + ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index a7db06f2c5..a740cf3622 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -537,7 +537,7 @@ start_node(Config, Envs) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" - ++ atom_to_list(?config(testcase, Config)) + ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl index 0becf88f7a..7abce1e715 100644 --- a/erts/emulator/test/system_profile_SUITE.erl +++ b/erts/emulator/test/system_profile_SUITE.erl @@ -392,7 +392,7 @@ ring_loop(RelayTo) -> %% API echo(Config) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), erl_ddll:load_driver(Path, echo_drv), Pid = spawn_link(?MODULE, port_echo_start, []), Pid ! {self(), get_ports}, diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index c225c4d88f..cf98247751 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -1042,7 +1042,7 @@ start_node(Config) -> start_node(Config, ""). start_node(Config, Args) -> - TestCase = ?config(testcase, Config), + TestCase = proplists:get_value(testcase, Config), PA = filename:dirname(code:which(?MODULE)), ESTime = erlang:monotonic_time(1) + erlang:time_offset(1), Unique = erlang:unique_integer([positive]), diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 5762fd558b..44d631cfa9 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -507,7 +507,7 @@ system_monitor_long_schedule(suite) -> system_monitor_long_schedule(doc) -> ["Tests erlang:system_monitor(Pid, [{long_schedule,Time}])"]; system_monitor_long_schedule(Config) when is_list(Config) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), erl_ddll:start(), case (catch load_driver(Path, slow_drv)) of ok -> diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 59a819fe2a..907026bf03 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -548,7 +548,7 @@ collect_all_info([]) -> []. %% Local helpers load_nif(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0). diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 2b142596a3..a579fd7b4e 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -738,8 +738,8 @@ info_test() -> ok. delete_test(Config) -> - ?line Priv = ?config(priv_dir, Config), - ?line Data = ?config(data_dir, Config), + ?line Priv = proplists:get_value(priv_dir, Config), + ?line Data = proplists:get_value(data_dir, Config), ?line File = filename:join(Data, "trace_local_dummy"), ?line {ok,trace_local_dummy} = c:c(File, [{outdir,Priv}]), ?line code:purge(trace_local_dummy), diff --git a/erts/emulator/test/trace_nif_SUITE.erl b/erts/emulator/test/trace_nif_SUITE.erl index f61e11595f..4ddcbf8f6b 100644 --- a/erts/emulator/test/trace_nif_SUITE.erl +++ b/erts/emulator/test/trace_nif_SUITE.erl @@ -284,7 +284,7 @@ nif_process() -> nif_process(). load_nif(Config) -> - ?line Path = ?config(data_dir, Config), + ?line Path = proplists:get_value(data_dir, Config), ?line ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0). diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index 18a475f04b..a38fced7cb 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -610,7 +610,7 @@ trace_pid(Pid, On, Flags0) -> Res. start_tracer(Config) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, echo_drv), Self = self(), put(tracer, fun_spawn(fun() -> tracer(Self) end)), diff --git a/erts/emulator/test/tuple_SUITE.erl b/erts/emulator/test/tuple_SUITE.erl index fb44be3145..d59d7d12d3 100644 --- a/erts/emulator/test/tuple_SUITE.erl +++ b/erts/emulator/test/tuple_SUITE.erl @@ -64,7 +64,7 @@ init_per_suite(Config) -> [{started_apps, A}|Config]. end_per_suite(Config) -> - As = ?config(started_apps, Config), + As = proplists:get_value(started_apps, Config), lists:foreach(fun (A) -> application:stop(A) end, As), Config. diff --git a/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl index 96c327c6ce..84e5cf3222 100644 --- a/erts/emulator/test/unique_SUITE.erl +++ b/erts/emulator/test/unique_SUITE.erl @@ -372,7 +372,7 @@ start_node(Config, Opts) when is_list(Config), is_list(Opts) -> ?line B = erlang:unique_integer([positive]), ?line Name = list_to_atom(atom_to_list(?MODULE) ++ "-" - ++ atom_to_list(?config(testcase, Config)) + ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" ++ integer_to_list(A) ++ "-" -- cgit v1.2.3 From 2b73a44c5b720a348fe8001cf024065c14e87651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 9 Mar 2016 18:58:47 +0100 Subject: Replace ?t with test_server The macro ?t is deprecated. Replace its use with 'test_server'. --- erts/emulator/test/alloc_SUITE.erl | 8 ++++---- erts/emulator/test/binary_SUITE.erl | 4 ++-- erts/emulator/test/distribution_SUITE.erl | 2 +- erts/emulator/test/driver_SUITE.erl | 6 +++--- erts/emulator/test/erl_drv_thread_SUITE.erl | 2 +- erts/emulator/test/erl_link_SUITE.erl | 4 ++-- erts/emulator/test/float_SUITE.erl | 4 ++-- erts/emulator/test/fun_r13_SUITE.erl | 5 +++-- erts/emulator/test/ignore_cores.erl | 2 +- erts/emulator/test/message_queue_data_SUITE.erl | 4 ++-- erts/emulator/test/node_container_SUITE.erl | 12 ++++++------ erts/emulator/test/old_mod.erl | 4 ++-- erts/emulator/test/op_SUITE.erl | 2 +- erts/emulator/test/port_SUITE.erl | 2 +- erts/emulator/test/process_SUITE.erl | 4 ++-- erts/emulator/test/scheduler_SUITE.erl | 6 +++--- erts/emulator/test/signal_SUITE.erl | 4 ++-- erts/emulator/test/smoke_test_SUITE.erl | 4 ++-- erts/emulator/test/statistics_SUITE.erl | 4 ++-- erts/emulator/test/system_info_SUITE.erl | 4 ++-- erts/emulator/test/time_SUITE.erl | 2 +- erts/emulator/test/timer_bif_SUITE.erl | 2 +- erts/emulator/test/trace_local_SUITE.erl | 4 ++-- erts/emulator/test/unique_SUITE.erl | 4 ++-- 24 files changed, 50 insertions(+), 49 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 419f92c4a6..b3b0d6d57e 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -99,7 +99,7 @@ migration(Cfg) -> end. erts_mmap(Config) when is_list(Config) -> - case ?t:os_type() of + case test_server:os_type() of {unix, _} -> [erts_mmap_do(Config, SCO, SCRPM, SCRFSD) || SCO <-[true,false], SCRFSD <-[1234,0], SCRPM <- [true,false]]; @@ -162,7 +162,7 @@ drv_case(Config) -> drv_case(Config, one_shot, ""). drv_case(Config, Mode, NodeOpts) when is_list(Config) -> - case ?t:os_type() of + case test_server:os_type() of {Family, _} when Family == unix; Family == win32 -> ?line {ok, Node} = start_node(Config, NodeOpts), ?line Self = self(), @@ -355,11 +355,11 @@ start_node_1(Config, Opts) -> ++ integer_to_list(erlang:system_time(seconds)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), - ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). + test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). stop_node(Node) when Node =:= node() -> ok; stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). free_memory() -> %% Free memory in MB. diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 576bcd0688..65e2d2fdad 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1515,7 +1515,7 @@ cmp_old_impl(Config) when is_list(Config) -> false -> {skipped, "No "++Rel++" available"}; true -> - {ok, Node} = ?t:start_node(list_to_atom(atom_to_list(?MODULE)++"_"++Rel), + {ok, Node} = test_server:start_node(list_to_atom(atom_to_list(?MODULE)++"_"++Rel), peer, [{args, " -setcookie "++Cookie}, {erl, [{release, Rel}]}]), @@ -1557,7 +1557,7 @@ cmp_old_impl(Config) when is_list(Config) -> cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(1000000)))]}), cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(10000000)))]}), - ?t:stop_node(Node), + test_server:stop_node(Node), ok end. diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 5f2ecdcbe1..e341d4ab2b 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -1070,7 +1070,7 @@ atom_roundtrip(Config) when is_list(Config) -> ?line ok. atom_roundtrip_r15b(Config) when is_list(Config) -> - case ?t:is_release_available("r15b") of + case test_server:is_release_available("r15b") of true -> ?line AtomData = atom_data(), ?line verify_atom_data(AtomData), diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 37ad0fd79d..f519634281 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -518,7 +518,7 @@ queue_echo(doc) -> ["1) Queue up data in a driver that uses the full driver_queue API to do this." "2) Get the data back, a random amount at a time."]; queue_echo(Config) when is_list(Config) -> - case ?t:is_native(?MODULE) of + case test_server:is_native(?MODULE) of true -> exit(crashes_native_code); false -> queue_echo_1(Config) end. @@ -2571,10 +2571,10 @@ start_node(Config) when is_list(Config) -> ++ integer_to_list(erlang:system_time(seconds)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), - ?t:start_node(Name, slave, [{args, "-pa "++Pa}]). + test_server:start_node(Name, slave, [{args, "-pa "++Pa}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). wait_deallocations() -> try diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl index 7d5e57eb0c..5c861ae9f0 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE.erl +++ b/erts/emulator/test/erl_drv_thread_SUITE.erl @@ -71,7 +71,7 @@ drv_case(Config, CaseName, Command, TimeTrap) when is_list(Config), is_atom(CaseName), is_list(Command), is_integer(TimeTrap) -> - case ?t:os_type() of + case test_server:os_type() of {Family, _} when Family == unix; Family == win32 -> ?line run_drv_case(Config, CaseName, Command, TimeTrap); SkipOs -> diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index 2a59ca8e99..472232a27d 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -1038,7 +1038,7 @@ start_node(Name) -> start_node(Name, Args) -> ?line Pa = filename:dirname(code:which(?MODULE)), - ?line Res = ?t:start_node(Name, slave, [{args, Args ++ " -pa " ++ Pa}]), + ?line Res = test_server:start_node(Name, slave, [{args, Args ++ " -pa " ++ Pa}]), ?line {ok, Node} = Res, ?line rpc:call(Node, erts_debug, set_internal_state, [available_internal_state, true]), @@ -1046,7 +1046,7 @@ start_node(Name, Args) -> stop_node(Node) -> - ?line ?t:stop_node(Node). + ?line test_server:stop_node(Node). -define(COOKIE, ''). -define(DOP_LINK, 1). diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index 67e14e015e..df44e28e2c 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -283,10 +283,10 @@ start_node(Config) when is_list(Config) -> ++ integer_to_list(erlang:system_time(seconds)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), - ?line ?t:start_node(Name, slave, [{args, "-pa "++Pa}]). + ?line test_server:start_node(Name, slave, [{args, "-pa "++Pa}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). %% Test that operations that might hide infinite intermediate results diff --git a/erts/emulator/test/fun_r13_SUITE.erl b/erts/emulator/test/fun_r13_SUITE.erl index 3ee7b90686..c8146de46a 100644 --- a/erts/emulator/test/fun_r13_SUITE.erl +++ b/erts/emulator/test/fun_r13_SUITE.erl @@ -34,7 +34,7 @@ all() -> [dist_old_release]. dist_old_release(Config) when is_list(Config) -> - case ?t:is_release_available("r12b") of + case test_server:is_release_available("r12b") of true -> do_dist_old(Config); false -> {skip,"No R12B found"} end. @@ -42,7 +42,7 @@ dist_old_release(Config) when is_list(Config) -> do_dist_old(Config) when is_list(Config) -> ?line Pa = filename:dirname(code:which(?MODULE)), Name = fun_dist_r12, - ?line {ok,Node} = ?t:start_node(Name, peer, + ?line {ok,Node} = test_server:start_node(Name, peer, [{args,"-pa "++Pa}, {erl,[{release,"r12b"}]}]), @@ -67,6 +67,7 @@ do_dist_old(Config) when is_list(Config) -> Other -> ?line ct:fail({bad_message,Other}) end, + true = test_server:stop_node(Node), ok. cons(H, T) -> diff --git a/erts/emulator/test/ignore_cores.erl b/erts/emulator/test/ignore_cores.erl index 4af5efe834..7373303a39 100644 --- a/erts/emulator/test/ignore_cores.erl +++ b/erts/emulator/test/ignore_cores.erl @@ -94,7 +94,7 @@ setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite), end, ok = file:write_file(filename:join([IgnDir, "ignore_core_files"]), <<>>), %% cores are dumped in /cores on MacOS X - CoresDir = case {?t:os_type(), filelib:is_dir("/cores")} of + CoresDir = case {test_server:os_type(), filelib:is_dir("/cores")} of {{unix,darwin}, true} -> filelib:fold_files("/cores", "^core.*$", diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl index bbf6b7bb2c..12c42d24aa 100644 --- a/erts/emulator/test/message_queue_data_SUITE.erl +++ b/erts/emulator/test/message_queue_data_SUITE.erl @@ -207,7 +207,7 @@ start_node(Config, Opts) when is_list(Config), is_list(Opts) -> ++ integer_to_list(erlang:system_time(seconds)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), - ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). + test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index b95a59f2da..191dc09670 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -706,7 +706,7 @@ timer_refc(Config) when is_list(Config) -> otp_4715(doc) -> []; otp_4715(suite) -> []; otp_4715(Config) when is_list(Config) -> - case ?t:is_release_available("r9b") of + case test_server:is_release_available("r9b") of true -> otp_4715_1(Config); false -> {skip,"No R9B found"} end. @@ -717,10 +717,10 @@ otp_4715_1(Config) -> ?line run_otp_4715(Config); _ -> ?line Pa = filename:dirname(code:which(?MODULE)), - ?line ?t:run_on_shielded_node(fun () -> - run_otp_4715(Config) - end, - "+R9 -pa " ++ Pa) + ?line test_server:run_on_shielded_node(fun () -> + run_otp_4715(Config) + end, + "+R9 -pa " ++ Pa) end. run_otp_4715(Config) when is_list(Config) -> @@ -741,7 +741,7 @@ pid_wrap(Config) when is_list(Config) -> ?line pp_wrap(pid). port_wrap(doc) -> []; port_wrap(suite) -> []; port_wrap(Config) when is_list(Config) -> - ?line case ?t:os_type() of + ?line case test_server:os_type() of {unix, _} -> ?line pp_wrap(port); _ -> diff --git a/erts/emulator/test/old_mod.erl b/erts/emulator/test/old_mod.erl index e714a75954..03f1ffd67b 100644 --- a/erts/emulator/test/old_mod.erl +++ b/erts/emulator/test/old_mod.erl @@ -37,12 +37,12 @@ sort_on_old_node(List) when is_list(List) -> ++ integer_to_list(X) ++ integer_to_list(Y) ++ integer_to_list(Z)), - ?line {ok, Node} = ?t:start_node(NodeName, + ?line {ok, Node} = test_server:start_node(NodeName, peer, [{args, " -pa " ++ Pa}, {erl, [{release, OldVersion++"b_patched"}]}]), ?line Ref = make_ref(), ?line spawn_link(Node, ?MODULE, sorter, [self(), Ref, List]), ?line SortedPids = receive {Ref, SP} -> SP end, - ?line true = ?t:stop_node(Node), + ?line true = test_server:stop_node(Node), ?line SortedPids. diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl index a12066f9fa..70e0c57827 100644 --- a/erts/emulator/test/op_SUITE.erl +++ b/erts/emulator/test/op_SUITE.erl @@ -257,7 +257,7 @@ run_test_module(Cases, GuardsOk) -> %% Compile, load, and run the generated module. - Native = case ?t:is_native(?MODULE) of + Native = case test_server:is_native(?MODULE) of true -> [native]; false -> [] end, diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 8a52305414..563b311f5f 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1811,7 +1811,7 @@ exit_status_multi_scheduling_block(doc) -> []; exit_status_multi_scheduling_block(suite) -> []; exit_status_multi_scheduling_block(Config) when is_list(Config) -> Repeat = 3, - case ?t:os_type() of + case test_server:os_type() of {unix, _} -> ct:timetrap({minutes, 2*Repeat}), SleepSecs = 6, diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index ff55a0102f..7df7fb516d 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -2500,10 +2500,10 @@ start_node(Config, Args) when is_list(Config) -> ++ integer_to_list(erlang:system_time(seconds)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), - ?t:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). + test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). enable_internal_state() -> case catch erts_debug:get_internal_state(available_internal_state) of diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index dbbf1c43ac..31cc37ad4d 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -876,7 +876,7 @@ get_affinity_mask(_Port, _Status, Affinity) -> Affinity. get_affinity_mask() -> - case ?t:os_type() of + case test_server:os_type() of {unix, linux} -> case catch open_port({spawn, "taskset -p " ++ os:getpid()}, [exit_status]) of @@ -2232,10 +2232,10 @@ start_node(Config, Args) when is_list(Config) -> ++ integer_to_list(erlang:system_time(seconds)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), - ?line ?t:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). + ?line test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). enable_internal_state() -> diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index 0784b1df36..ac6c96a02c 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -507,10 +507,10 @@ start_node(Config) -> ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), Pa = filename:dirname(code:which(?MODULE)), - ?t:start_node(Name, slave, [{args, "-pa " ++ Pa}]). + test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). have_pending_exit() -> have_pending_exit(self()). diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 9b0db358f1..900dd124c0 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -156,8 +156,8 @@ start_node(Config, Args) when is_list(Config) -> ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), Opts = [{args, "-pa "++Pa++" "++Args}], - ?t:start_node(Name, slave, Opts). + test_server:start_node(Name, slave, Opts). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index c16e0f7cbe..8279d374ee 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -107,7 +107,7 @@ wall_clock_update1(N) when N > 0 -> ?line Wc_Diff = T2_wc_time - T1_wc_time, ?line io:format("Wall clock diff = ~p; should be = 1000..1040~n", [Wc_Diff]), - case ?t:is_debug() of + case test_server:is_debug() of false -> ?line true = Wc_Diff =< 1040; true -> @@ -142,7 +142,7 @@ runtime_update(doc) -> "updated difference after running a process that takes all CPU " " power of the Erlang process for a second."; runtime_update(Config) when is_list(Config) -> - case ?t:is_cover() of + case test_server:is_cover() of false -> ?line process_flag(priority, high), do_runtime_update(10); diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index a740cf3622..7d66ea5c5b 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -542,7 +542,7 @@ start_node(Config, Envs) when is_list(Config) -> ++ integer_to_list(erlang:system_time(seconds)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), - ?t:start_node(Name, peer, [{args, "-pa "++Pa}, {env, Envs}]). + test_server:start_node(Name, peer, [{args, "-pa "++Pa}, {env, Envs}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index cf98247751..cdf79e48d0 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -448,7 +448,7 @@ microsecs({Mega_Secs, Secs, Microsecs}) -> %% calls to erlang:localtime(). now_update(Config) when is_list(Config) -> - case ?t:is_debug() of + case test_server:is_debug() of false -> ?line now_update1(10); true -> {skip,"Unreliable in DEBUG build"} end. diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index 704269bc0f..c04c2e465e 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -669,7 +669,7 @@ start_slave() -> ?line Name = atom_to_list(?MODULE) ++ "-" ++ integer_to_list(erlang:system_time(seconds)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive])), - {ok, Node} = ?t:start_node(Name, slave, [{args, "-pa " ++ Pa}]), + {ok, Node} = test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]), Node. stop_slave(Node) -> diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index a579fd7b4e..93ba9a6ba3 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -900,12 +900,12 @@ concurrency(_Config) -> _ <- lists:seq(1, 2*N)], OnAndOff = fun() -> concurrency_on_and_off() end, Ps1 = [spawn_monitor(OnAndOff)|Ps0], - ?t:sleep(1000), + timer:sleep(1000), %% Now spawn off N more processes that turn on off and off %% a local trace pattern. Ps = [spawn_monitor(OnAndOff) || _ <- lists:seq(1, N)] ++ Ps1, - ?t:sleep(1000), + timer:sleep(1000), %% Clean up. [exit(Pid, kill) || {Pid,_} <- Ps], diff --git a/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl index 84e5cf3222..286fa111ee 100644 --- a/erts/emulator/test/unique_SUITE.erl +++ b/erts/emulator/test/unique_SUITE.erl @@ -377,7 +377,7 @@ start_node(Config, Opts) when is_list(Config), is_list(Opts) -> ++ integer_to_list(A) ++ "-" ++ integer_to_list(B)), - ?line ?t:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). + ?line test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). -- cgit v1.2.3 From 33b0c5f37b7b5baa42d4999d1a2be32470fb1bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 10 Mar 2016 17:59:23 +0100 Subject: Eliminate use of doc and suite clauses Those clause are obsolete and never used by common_test. --- erts/emulator/test/a_SUITE.erl | 8 -- erts/emulator/test/after_SUITE.erl | 12 ++- erts/emulator/test/alloc_SUITE.erl | 18 ---- erts/emulator/test/beam_literals_SUITE.erl | 25 +++--- erts/emulator/test/bif_SUITE.erl | 12 +-- erts/emulator/test/big_SUITE.erl | 19 ++--- erts/emulator/test/binary_SUITE.erl | 28 ++----- erts/emulator/test/bs_construct_SUITE.erl | 13 +-- erts/emulator/test/bs_match_bin_SUITE.erl | 4 +- erts/emulator/test/bs_match_int_SUITE.erl | 2 +- erts/emulator/test/bs_match_misc_SUITE.erl | 4 +- erts/emulator/test/bs_match_tail_SUITE.erl | 6 +- erts/emulator/test/busy_port_SUITE.erl | 10 --- erts/emulator/test/call_trace_SUITE.erl | 23 ++---- erts/emulator/test/code_SUITE.erl | 3 +- erts/emulator/test/crypto_SUITE.erl | 24 ++---- erts/emulator/test/ddll_SUITE.erl | 111 +++++++------------------ erts/emulator/test/decode_packet_SUITE.erl | 20 +---- erts/emulator/test/distribution_SUITE.erl | 77 ++++++------------ erts/emulator/test/driver_SUITE.erl | 113 +++----------------------- erts/emulator/test/efile_SUITE.erl | 4 +- erts/emulator/test/erl_drv_thread_SUITE.erl | 6 -- erts/emulator/test/erl_link_SUITE.erl | 34 ++------ erts/emulator/test/estone_SUITE.erl | 5 +- erts/emulator/test/float_SUITE.erl | 5 +- erts/emulator/test/fun_SUITE.erl | 25 ++---- erts/emulator/test/gc_SUITE.erl | 15 ++-- erts/emulator/test/guard_SUITE.erl | 12 ++- erts/emulator/test/hash_SUITE.erl | 38 ++------- erts/emulator/test/hibernate_SUITE.erl | 4 - erts/emulator/test/list_bif_SUITE.erl | 5 +- erts/emulator/test/match_spec_SUITE.erl | 75 +++++------------ erts/emulator/test/monitor_SUITE.erl | 54 ++---------- erts/emulator/test/nested_SUITE.erl | 6 +- erts/emulator/test/nif_SUITE.erl | 68 ++++++---------- erts/emulator/test/node_container_SUITE.erl | 71 ++++------------ erts/emulator/test/old_scheduler_SUITE.erl | 10 --- erts/emulator/test/op_SUITE.erl | 11 ++- erts/emulator/test/port_SUITE.erl | 95 +++++----------------- erts/emulator/test/process_SUITE.erl | 86 +------------------- erts/emulator/test/ref_SUITE.erl | 2 +- erts/emulator/test/save_calls_SUITE.erl | 7 +- erts/emulator/test/signal_SUITE.erl | 21 +---- erts/emulator/test/statistics_SUITE.erl | 65 ++++++--------- erts/emulator/test/system_info_SUITE.erl | 19 +---- erts/emulator/test/system_profile_SUITE.erl | 27 +----- erts/emulator/test/time_SUITE.erl | 10 +-- erts/emulator/test/timer_bif_SUITE.erl | 37 +++------ erts/emulator/test/trace_SUITE.erl | 71 ++++------------ erts/emulator/test/trace_bif_SUITE.erl | 17 ++-- erts/emulator/test/trace_call_count_SUITE.erl | 25 ++---- erts/emulator/test/trace_call_time_SUITE.erl | 45 ++-------- erts/emulator/test/trace_local_SUITE.erl | 73 ++++++----------- erts/emulator/test/trace_meta_SUITE.erl | 36 ++------ erts/emulator/test/trace_nif_SUITE.erl | 14 ++-- erts/emulator/test/trace_port_SUITE.erl | 28 +++---- erts/emulator/test/z_SUITE.erl | 17 +--- 57 files changed, 411 insertions(+), 1264 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl index ebaf23b28a..8efb0fad53 100644 --- a/erts/emulator/test/a_SUITE.erl +++ b/erts/emulator/test/a_SUITE.erl @@ -38,10 +38,6 @@ suite() -> all() -> [long_timers, pollset_size]. -long_timers(doc) -> - []; -long_timers(suite) -> - []; long_timers(Config) when is_list(Config) -> Dir = proplists:get_value(data_dir, Config), ?line long_timers_test:start(Dir), @@ -49,10 +45,6 @@ long_timers(Config) when is_list(Config) -> "Testcase started! This test will run in parallel with the " "erts testsuite and ends in the z_SUITE:long_timers testcase."}. -pollset_size(doc) -> - []; -pollset_size(suite) -> - []; pollset_size(Config) when is_list(Config) -> %% Ensure inet_gethost_native port program started, in order to %% allow other suites to use it... diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl index 40fd2e6500..4efc3b68a0 100644 --- a/erts/emulator/test/after_SUITE.erl +++ b/erts/emulator/test/after_SUITE.erl @@ -71,9 +71,8 @@ frequent_process() -> ?line frequent_process() end. -receive_after(doc) -> - "Test that 'receive after' works (doesn't hang). " - "The test takes 10 seconds to complete."; +%% Test that 'receive after' works (doesn't hang). +%% The test takes 10 seconds to complete. receive_after(Config) when is_list(Config) -> ?line receive_after1(5000). @@ -137,7 +136,7 @@ receive_after_errors(Config) when is_list(Config) -> try_after(Timeout) -> {'EXIT',{timeout_value,_}} = (catch receive after Timeout -> ok end). -receive_var_zero(doc) -> "Test 'after Z', when Z == 0."; +%% Test 'after Z', when Z == 0. receive_var_zero(Config) when is_list(Config) -> self() ! x, self() ! y, @@ -177,9 +176,8 @@ receive_zero(Config) when is_list(Config) -> ct:fail({bad_message,Other}) end. -multi_timeout(doc) -> - "Test for catching invalid assertion in erl_message.c (in queue_message)." - "This failed (dumped core) with debug-compiled emulator."; +%% Test for catching invalid assertion in erl_message.c (in queue_message) +%% This failed (dumped core) with debug-compiled emulator. multi_timeout(Config) when is_list(Config) -> ?line P = spawn(?MODULE, timeout_g, []), ?line P ! a, diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index b3b0d6d57e..cb6c8d6012 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -54,40 +54,22 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %% Testcases %% %% %% -basic(suite) -> []; -basic(doc) -> []; basic(Cfg) -> ?line drv_case(Cfg). -coalesce(suite) -> []; -coalesce(doc) -> []; coalesce(Cfg) -> ?line drv_case(Cfg). -threads(suite) -> []; -threads(doc) -> []; threads(Cfg) -> ?line drv_case(Cfg). -realloc_copy(suite) -> []; -realloc_copy(doc) -> []; realloc_copy(Cfg) -> ?line drv_case(Cfg). -bucket_index(suite) -> []; -bucket_index(doc) -> []; bucket_index(Cfg) -> ?line drv_case(Cfg). -bucket_mask(suite) -> []; -bucket_mask(doc) -> []; bucket_mask(Cfg) -> ?line drv_case(Cfg). -rbtree(suite) -> []; -rbtree(doc) -> []; rbtree(Cfg) -> ?line drv_case(Cfg). -mseg_clear_cache(suite) -> []; -mseg_clear_cache(doc) -> []; mseg_clear_cache(Cfg) -> ?line drv_case(Cfg). -cpool(suite) -> []; -cpool(doc) -> []; cpool(Cfg) -> ?line drv_case(Cfg). migration(Cfg) -> diff --git a/erts/emulator/test/beam_literals_SUITE.erl b/erts/emulator/test/beam_literals_SUITE.erl index 4cfc8e7d12..054579fff2 100644 --- a/erts/emulator/test/beam_literals_SUITE.erl +++ b/erts/emulator/test/beam_literals_SUITE.erl @@ -55,7 +55,7 @@ end_per_group(_GroupName, Config) -> Config. -putting(doc) -> "Test creating lists and tuples containing big number literals."; +%% Test creating lists and tuples containing big number literals. putting(Config) when is_list(Config) -> -773973888575883407313908 = chksum(putting1(8987697898797)). @@ -64,15 +64,14 @@ putting1(X) -> [X|349873987387373], [329878349873|-387394729872], -773973937933873929749873}. -matching_bigs(doc) -> "Test matching of a few big number literals (in Beam," - "select_val/3 will NOT be used)."; +%% Test matching of a few big number literals (in Beam select_val/3 will NOT be used). matching_bigs(Config) when is_list(Config) -> a = matching1(3972907842873739), b = matching1(-389789298378939783333333333333333333784), other = matching1(3141699999999999999999999999999999999), other = matching1(42). -matching_smalls(doc) -> "Test matching small numbers (both positive and negative)."; +%% Test matching small numbers (both positive and negative). matching_smalls(Config) when is_list(Config) -> ?line a = m_small(-42), ?line b = m_small(0), @@ -90,9 +89,8 @@ m_small(-13) -> d; m_small(337848) -> e; m_small(_) -> other. -matching_smalls_jt(doc) -> - "Test matching small numbers (both positive and negative). " - "Make sure that a jump table is used."; +%% Test matching small numbers (both positive and negative). +%% Make sure that a jump table is used. matching_smalls_jt(Config) when is_list(Config) -> ?line a = m_small_jt(-2), ?line b = m_small_jt(-1), @@ -117,8 +115,7 @@ matching1(-389789298378939783333333333333333333784) -> b; matching1(_) -> other. -matching_more_bigs(doc) -> "Test matching of a big number literals (in Beam," - "a select_val/3 instruction will be used)."; +%% Test matching of a big number literals (in Beam, a select_val/3 instruction will be used) matching_more_bigs(Config) when is_list(Config) -> a = matching2(-999766349740978337), b = matching2(9734097866575478), @@ -137,8 +134,7 @@ matching2(13987294872948990) -> d; matching2(777723896192459245) -> e; matching2(_) -> other. -matching_bigs_and_smalls(doc) -> "Test matching of a mix of big numbers and literals."; -matching_bigs_and_smalls(suite) -> []; +%% Test matching of a mix of big numbers and literals. matching_bigs_and_smalls(Config) when is_list(Config) -> a = matching3(38472928723987239873873), b = matching3(0), @@ -159,7 +155,7 @@ matching3(42) -> e; matching3(-4533) -> f; matching3(_) -> other. -badmatch(doc) -> "Test literal badmatches with big number and floats."; +%% Test literal badmatches with big number and floats. badmatch(Config) when is_list(Config) -> %% We are satisfied if we can load this module and run it. Big = id(32984798729847892498297824872982972978239874), @@ -210,8 +206,7 @@ try_case_clause_big() -> error end. -receiving(doc) -> "Test receive with a big number literal (more than 27 bits, " - "less than 32 bits)."; +%% Test receive with a big number literal (more than 27 bits, less than 32 bits). receiving(Config) when is_list(Config) -> Self = self(), spawn(fun() -> Self ! here_is_a_message end), @@ -222,7 +217,7 @@ receiving(Config) when is_list(Config) -> timeout end. -literal_type_tests(doc) -> "Test type tests on literal values."; +%% Test type tests on literal values. literal_type_tests(Config) when is_list(Config) -> %% Generate an Erlang module with all different type of type tests. ?line Tests = make_test([{T, L} || T <- type_tests(), L <- literals()]), diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index ec8c7155b5..2768ad746f 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -45,10 +45,7 @@ all() -> atom_to_binary, binary_to_atom, binary_to_existing_atom, min_max, erlang_halt, is_builtin]. -display(suite) -> - []; -display(doc) -> - ["Uses erlang:display to test that erts_printf does not do deep recursion"]; +%% Uses erlang:display to test that erts_printf does not do deep recursion display(Config) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), {ok, Node} = test_server:start_node(display_huge_term,peer, @@ -351,10 +348,6 @@ t_list_to_existing_atom(Config) when is_list(Config) -> ?line UnlikelyAtom = list_to_existing_atom(UnlikelyStr), ok. -os_env(doc) -> - []; -os_env(suite) -> - []; os_env(Config) when is_list(Config) -> ?line EnvVar1 = "MjhgvFDrresdCghN mnjkUYg vfrD", ?line false = os:getenv(EnvVar1), @@ -383,8 +376,7 @@ os_env_long(Min, Max, Value) -> true = os:unsetenv(EnvVar), ?line os_env_long(Min+1, Max, Value). -otp_7526(doc) -> - ["Test that string:to_integer does not Halloc in wrong order."]; +%% Test that string:to_integer does not Halloc in wrong order. otp_7526(Config) when is_list(Config) -> ok = test_7526(256). diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl index 3cc812784a..57f4e502a5 100644 --- a/erts/emulator/test/big_SUITE.erl +++ b/erts/emulator/test/big_SUITE.erl @@ -75,7 +75,7 @@ eq_math(Config) when is_list(Config) -> test(TestFile). -borders(doc) -> "Tests border cases between small/big."; +%% Tests border cases between small/big. borders(Config) when is_list(Config) -> TestFile = test_file(Config, "borders.dat"), test(TestFile). @@ -251,8 +251,7 @@ t_div(Config) when is_list(Config) -> init(ReplyTo, Fun, _Filler) -> ReplyTo ! {result, Fun()}. -big_literals(doc) -> - "Tests that big-number literals work correctly."; +%% Tests that big-number literals work correctly. big_literals(Config) when is_list(Config) -> %% Note: The literal test cannot be compiler on a pre-R4 Beam emulator, %% so we compile it now. @@ -264,8 +263,7 @@ big_literals(Config) when is_list(Config) -> ok. -big_float_1(doc) -> - ["OTP-2436, part 1"]; +%% OTP-2436, part 1 big_float_1(Config) when is_list(Config) -> %% F is a number very close to a maximum float. ?line F = id(1.7e308), @@ -289,8 +287,7 @@ big_float_1(Config) when is_list(Config) -> ?line true = (F =< I*I), ok. -big_float_2(doc) -> - ["OTP-2436, part 2"]; +%% "OTP-2436, part 2 big_float_2(Config) when is_list(Config) -> ?line F = id(1.7e308), ?line I = trunc(F), @@ -299,8 +296,7 @@ big_float_2(Config) when is_list(Config) -> ?line {'EXIT', _} = (catch 4/(2*I)), ok. -shift_limit_1(doc) -> - ["OTP-3256"]; +%% OTP-3256 shift_limit_1(Config) when is_list(Config) -> ?line case catch (id(1) bsl 100000000) of {'EXIT', {system_limit, _}} -> @@ -360,10 +356,7 @@ toobig() -> <> = A, % should fail ANr band ANr. -otp_6692(suite) -> - []; -otp_6692(doc) -> - ["Tests for DIV/REM bug reported in OTP-6692"]; +%% Tests for DIV/REM bug reported in OTP-6692 otp_6692(Config) when is_list(Config)-> ?line loop1(1,1000). diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 65e2d2fdad..bc1cf62883 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -129,7 +129,6 @@ copy_server(Parent) -> %% Tests list_to_binary/1, binary_to_list/1 and size/1, %% using flat lists. -conversions(suite) -> []; conversions(Config) when is_list(Config) -> ?line test_bin([]), ?line test_bin([1]), @@ -258,7 +257,6 @@ test_deep_bitstr(List) -> Bin = list_to_bitstring(List), {Bin,bitstring_to_list(Bin)}. -bad_list_to_binary(suite) -> []; bad_list_to_binary(Config) when is_list(Config) -> ?line test_bad_bin(atom), ?line test_bad_bin(42), @@ -305,7 +303,7 @@ test_bad_bin(List) -> {'EXIT',{badarg,_}} = (catch list_to_bitstring(List)), {'EXIT',{badarg,_}} = (catch iolist_size(List)). -bad_binary_to_list(doc) -> "Tries binary_to_list/1,3 with bad arguments."; +%% Tries binary_to_list/1,3 with bad arguments. bad_binary_to_list(Config) when is_list(Config) -> ?line bad_bin_to_list(fun(X) -> X * 42 end), @@ -327,7 +325,6 @@ bad_bin_to_list(Bin, First, Last) -> %% Tries to split a binary at all possible positions. -t_split_binary(suite) -> []; t_split_binary(Config) when is_list(Config) -> ?line L = lists:seq(0, ?heap_binary_size-5), %Heap binary. ?line B = list_to_binary(L), @@ -368,8 +365,7 @@ split(L, B, Pos) when Pos > 0 -> split(_L, _B, 0) -> ok. -bad_split(doc) -> "Tries split_binary/2 with bad arguments."; -bad_split(suite) -> []; +%% Tries split_binary/2 with bad arguments. bad_split(Config) when is_list(Config) -> GoodBin = list_to_binary([1,2,3]), ?line bad_split(GoodBin, -1), @@ -383,7 +379,7 @@ bad_split(Config) when is_list(Config) -> bad_split(Bin, Pos) -> {'EXIT',{badarg,_}} = (catch split_binary(Bin, Pos)). -t_hash(doc) -> "Test hash/2 with different type of binaries."; +%% Test hash/2 with different type of binaries. t_hash(Config) when is_list(Config) -> test_hash([]), test_hash([253]), @@ -408,8 +404,7 @@ test_hash_1(Bin, Sbin, Unaligned, Hash) when is_function(Hash, 2) -> ct:fail("Different hash values: ~p, ~p, ~p\n", [H1,H2,H3]) end. -bad_size(doc) -> "Try bad arguments to size/1."; -bad_size(suite) -> []; +%% Try bad arguments to size/1. bad_size(Config) when is_list(Config) -> ?line {'EXIT',{badarg,_}} = (catch size(fun(X) -> X + 33 end)), ok. @@ -580,8 +575,7 @@ build_iolist(N0, Base) -> end. -bad_binary_to_term_2(doc) -> "OTP-4053."; -bad_binary_to_term_2(suite) -> []; +%% OTP-4053 bad_binary_to_term_2(Config) when is_list(Config) -> ?line {ok, N} = test_server:start_node(plopp, slave, []), ?line R = rpc:call(N, erlang, binary_to_term, [<<131,111,255,255,255,0>>]), @@ -594,7 +588,7 @@ bad_binary_to_term_2(Config) when is_list(Config) -> ?line test_server:stop_node(N), ok. -bad_binary_to_term(doc) -> "Try bad input to binary_to_term/1."; +%% Try bad input to binary_to_term/1. bad_binary_to_term(Config) when is_list(Config) -> ?line bad_bin_to_term(an_atom), ?line bad_bin_to_term({an,tuple}), @@ -615,7 +609,7 @@ bad_bin_to_term(BadBin) -> bad_bin_to_term(BadBin,Opts) -> {'EXIT',{badarg,_}} = (catch binary_to_term_stress(BadBin,Opts)). -safe_binary_to_term2(doc) -> "Test safety options for binary_to_term/2"; +%% Test safety options for binary_to_term/2 safe_binary_to_term2(Config) when is_list(Config) -> ?line bad_bin_to_term(<<131,100,0,14,"undefined_atom">>, [safe]), ?line bad_bin_to_term(<<131,100,0,14,"other_bad_atom">>, [safe]), @@ -631,7 +625,6 @@ safe_binary_to_term2(Config) when is_list(Config) -> %% Tests bad input to binary_to_term/1. -bad_terms(suite) -> []; bad_terms(Config) when is_list(Config) -> ?line test_terms(fun corrupter/1), {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,3:32,0,11,22,33>>)), @@ -695,7 +688,6 @@ corrupter(Bin, Pos) when Pos >= 0 -> corrupter(_Bin, _) -> ok. -more_bad_terms(suite) -> []; more_bad_terms(Config) when is_list(Config) -> ?line Data = proplists:get_value(data_dir, Config), ?line BadFile = filename:join(Data, "bad_binary"), @@ -934,8 +926,7 @@ otp_6817_try_bin(Bin) -> %% Will crash if the bug is present. erlang:garbage_collect(). -otp_8117(doc) -> "Some bugs in binary_to_term when 32-bit integers are negative."; -otp_8117(suite) -> []; +%% Some bugs in binary_to_term when 32-bit integers are negative. otp_8117(Config) when is_list(Config) -> [otp_8117_do(Op,-(1 bsl N)) || Op <- ['fun',named_fun,list,tuple], N <- lists:seq(0,31)], @@ -959,8 +950,7 @@ otp_8117_do(tuple,Neg) -> ?line bad_bin_to_term(<<131,104,2,105,Neg:32,97,11,97,12,97,13,97,14>>). -ordering(doc) -> "Tests ordering of binaries."; -ordering(suite) -> []; +%% Tests ordering of binaries. ordering(Config) when is_list(Config) -> B1 = list_to_binary([7,8,9]), B2 = make_sub_binary([1,2,3,4]), diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index bc5fb4f379..9a9066d0f0 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -249,7 +249,6 @@ fail_check(Res, _, _) -> ct:fail(did_not_fail_in_compiled_code). %%% Simple working cases -test1(suite) -> []; test1(Config) when is_list(Config) -> ?line I_13 = i(13), ?line I_big1 = big(1), @@ -271,7 +270,6 @@ gen(N, S, A) -> gen_l(N, S, A) -> [?T(<>, comp(N, A, S))]. -test2(suite) -> []; test2(Config) when is_list(Config) -> ?line test2(0, 8, 2#10101010101010101), ?line test2(0, 8, 2#1111111111). @@ -299,7 +297,6 @@ t3() -> ?N(<<>>) ]. -test3(suite) -> []; test3(Config) when is_list(Config) -> ?line Vars = [], ?line lists:foreach(fun one_test/1, eval_list(t3(), Vars)). @@ -310,7 +307,6 @@ gen_u(N, S, A) -> gen_u_l(N, S, A) -> [?N(<>)]. -test4(suite) -> []; test4(Config) when is_list(Config) -> ?line test4(0, 16, 2#10101010101010101), ?line test4(0, 16, 2#1111111111). @@ -331,8 +327,7 @@ gen_b(N, S, A) -> [?T(<>, binary_to_list(<>))]. -test5(suite) -> []; -test5(doc) -> ["OTP-3995"]; +%% OTP-3995 test5(Config) when is_list(Config) -> ?line test5(0, 8, <<73>>), ?line test5(0, 8, <<68>>). @@ -349,7 +344,6 @@ test5(S, A) -> lists:foreach(fun one_test/1, eval_list(gen_b(N, S, A), Vars)). %%% Failure cases -testf(suite) -> []; testf(Config) when is_list(Config) -> ?line ?FAIL(<<3.14>>), ?line ?FAIL(<<<<1,2>>>>), @@ -399,8 +393,7 @@ testf_1(W, B) -> ?FAIL_VARS(<<3.14:W/float>>, Vars), ?FAIL_VARS(<>, [{'B',B}|Vars]). -not_used(doc) -> - "Test that constructed binaries that are not used will still give an exception."; +%% Test that constructed binaries that are not used will still give an exception. not_used(Config) when is_list(Config) -> ?line ok = not_used1(3, <<"dum">>), ?line {'EXIT',{badarg,_}} = (catch not_used1(3, "dum")), @@ -440,7 +433,7 @@ in_guard(Bin, A, B) when <> == Bin -> 3; in_guard(Bin, A, B) when {a,b,<>} == Bin -> cant_happen; in_guard(_, _, _) -> nope. -mem_leak(doc) -> "Make sure that construction has no memory leak"; +%% Make sure that construction has no memory leak mem_leak(Config) when is_list(Config) -> ?line B = make_bin(16, <<0>>), ?line mem_leak(1024, B), diff --git a/erts/emulator/test/bs_match_bin_SUITE.erl b/erts/emulator/test/bs_match_bin_SUITE.erl index 2185e43498..7b82485216 100644 --- a/erts/emulator/test/bs_match_bin_SUITE.erl +++ b/erts/emulator/test/bs_match_bin_SUITE.erl @@ -47,7 +47,7 @@ end_per_group(_GroupName, Config) -> Config. -byte_split_binary(doc) -> "Tries to split a binary at all byte-aligned positions."; +%% Tries to split a binary at all byte-aligned positions. byte_split_binary(Config) when is_list(Config) -> ?line L = lists:seq(0, 57), ?line B = mkbin(L), @@ -64,7 +64,7 @@ byte_split(L, B, Pos) when Pos >= 0 -> ?line byte_split(L, B, Pos-1); byte_split(_, _, _) -> ok. -bit_split_binary(doc) -> "Tries to split a binary at all positions."; +%% Tries to split a binary at all positions. bit_split_binary(Config) when is_list(Config) -> Fun = fun(Bin, List, SkipBef, N) -> ?line SkipAft = 8*size(Bin) - N - SkipBef, diff --git a/erts/emulator/test/bs_match_int_SUITE.erl b/erts/emulator/test/bs_match_int_SUITE.erl index 6f8a5b1916..562aeef6f6 100644 --- a/erts/emulator/test/bs_match_int_SUITE.erl +++ b/erts/emulator/test/bs_match_int_SUITE.erl @@ -130,7 +130,7 @@ dynamic(Bin, S1, S2, A, B) -> _Other -> erlang:error(badmatch, [Bin,S1,S2,A,B]) end. -more_dynamic(doc) -> "Extract integers at different alignments and of different sizes."; +%% Extract integers at different alignments and of different sizes. more_dynamic(Config) when is_list(Config) -> % Unsigned big-endian numbers. diff --git a/erts/emulator/test/bs_match_misc_SUITE.erl b/erts/emulator/test/bs_match_misc_SUITE.erl index 8979a69322..f32f012085 100644 --- a/erts/emulator/test/bs_match_misc_SUITE.erl +++ b/erts/emulator/test/bs_match_misc_SUITE.erl @@ -39,7 +39,7 @@ all() -> otp_7198, unordered_bindings, float_middle_endian]. -bound_var(doc) -> "Test matching of bound variables."; +%% Test matching of bound variables. bound_var(Config) when is_list(Config) -> ?line ok = bound_var(42, 13, <<42,13>>), ?line nope = bound_var(42, 13, <<42,255>>), @@ -49,7 +49,7 @@ bound_var(Config) when is_list(Config) -> bound_var(A, B, <>) -> ok; bound_var(_, _, _) -> nope. -bound_tail(doc) -> "Test matching of a bound tail."; +%% Test matching of a bound tail. bound_tail(Config) when is_list(Config) -> ?line ok = bound_tail(<<>>, <<13,14>>), ?line ok = bound_tail(<<2,3>>, <<1,1,2,3>>), diff --git a/erts/emulator/test/bs_match_tail_SUITE.erl b/erts/emulator/test/bs_match_tail_SUITE.erl index baa86e6d4a..20129e3556 100644 --- a/erts/emulator/test/bs_match_tail_SUITE.erl +++ b/erts/emulator/test/bs_match_tail_SUITE.erl @@ -47,7 +47,7 @@ end_per_group(_GroupName, Config) -> Config. -aligned(doc) -> "Test aligned tails."; +%% Test aligned tails. aligned(Config) when is_list(Config) -> ?line Tail1 = mkbin([]), ?line {258,Tail1} = al_get_tail_used(mkbin([1,2])), @@ -70,7 +70,7 @@ aligned(Config) when is_list(Config) -> al_get_tail_used(<>) -> {A,T}. al_get_tail_unused(<>) -> A. -unaligned(doc) -> "Test that an non-aligned tail cannot be matched out."; +%% Test that an non-aligned tail cannot be matched out. unaligned(Config) when is_list(Config) -> ?line {'EXIT',{function_clause,_}} = (catch get_tail_used(mkbin([42]))), ?line {'EXIT',{{badmatch,_},_}} = (catch get_dyn_tail_used(mkbin([137]), 3)), @@ -90,7 +90,7 @@ get_dyn_tail_unused(Bin, Sz) -> <> = Bin, A. -zero_tail(doc) -> "Test that zero tails are tested correctly."; +%% Test that zero tails are tested correctly. zero_tail(Config) when is_list(Config) -> ?line 7 = (catch test_zero_tail(mkbin([7]))), ?line {'EXIT',{function_clause,_}} = (catch test_zero_tail(mkbin([1,2]))), diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index ee82356646..ce9fdfa10d 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -62,7 +62,6 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %% Tests I/O operations to a busy port, to make sure a suspended send %% operation is correctly restarted. This used to crash Beam. -io_to_busy(suite) -> []; io_to_busy(Config) when is_list(Config) -> ct:timetrap({seconds, 30}), @@ -114,7 +113,6 @@ forget(_) -> %% Test the interaction of busy ports and message sending. %% This used to cause the wrong message to be received. -message_order(suite) -> {req, dynamic_loading}; message_order(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), @@ -146,8 +144,6 @@ send_to_busy_1(Parent) -> end. %% Test the bif send/3 -send_3(suite) -> {req,dynamic_loading}; -send_3(doc) -> ["Test the BIF send/3"]; send_3(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), %% @@ -165,8 +161,6 @@ send_3(Config) when is_list(Config) -> ok. %% Test the erlang:system_monitor(Pid, [busy_port]) -system_monitor(suite) -> {req,dynamic_loading}; -system_monitor(doc) -> ["Test erlang:system_monitor({Pid,[busy_port]})."]; system_monitor(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), ?line Self = self(), @@ -222,7 +216,6 @@ rec(Tag) -> %% %% tests that the suspended process is killed if the port is killed. -no_trap_exit(suite) -> []; no_trap_exit(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), ?line process_flag(trap_exit, true), @@ -246,7 +239,6 @@ no_trap_exit(Config) when is_list(Config) -> %% The same scenario as above, but the port has been explicitly %% unlinked from the process. -no_trap_exit_unlinked(suite) -> []; no_trap_exit_unlinked(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), ?line process_flag(trap_exit, true), @@ -291,7 +283,6 @@ no_trap_exit_process(ResultTo, Link, Config) -> %% tests that the suspended process is scheduled runnable and %% receives an 'EXIT' message if the port is killed. -trap_exit(suite) -> []; trap_exit(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), ?line Pid = fun_spawn(fun busy_port_exit_process/2, [self(), Config]), @@ -332,7 +323,6 @@ busy_port_exit_process(ResultTo, Config) -> %% This should work even if some of the processes have terminated %% in the meantime. -multiple_writers(suite) -> []; multiple_writers(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), ?line start_busy_driver(Config), diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index 6035de9d75..0ced026c73 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -78,9 +78,7 @@ hipe(Config) when is_list(Config) -> ?line AllFuncs = erlang:trace_pattern({'_','_','_'}, false), ok. -process_specs(doc) -> - "Tests 'all', 'new', and 'existing' for specifying processes."; -process_specs(suite) -> []; +%% Tests 'all', 'new', and 'existing' for specifying processes. process_specs(Config) when is_list(Config) -> ?line Tracer = start_tracer(), ?line {flags,[call]} = trace_info(self(), flags), @@ -268,8 +266,8 @@ foo(X, Y) -> X+Y. %% This test case was written to verify that we do not change %% any behaviour with the introduction of "block-free" upgrade in R16. %% In short: Do not refer to this test case as an authority of how it must work. -upgrade(doc) -> - "Test tracing on module being upgraded"; + +%% Test tracing on module being upgraded upgrade(Config) when is_list(Config) -> V1 = compile_version(my_upgrade_test, 1, Config), V2 = compile_version(my_upgrade_test, 2, Config), @@ -470,8 +468,7 @@ flag_test_cpu_timestamp(Test) -> error:badarg -> ok end. -errors(doc) -> "Test bad arguments for trace/3 and trace_pattern/3."; -errors(suite) -> []; +%% Test bad arguments for trace/3 and trace_pattern/3. errors(Config) when is_list(Config) -> ?line expect_badarg_pid(aaa, true, []), ?line expect_badarg_pid({pid,dum}, false, []), @@ -505,8 +502,7 @@ expect_badarg_func(MFA, Pattern) -> ct:fail({unexpected,Other}) end. -pam(doc) -> "Basic test of PAM."; -pam(suite) -> []; +%% Basic test of PAM. pam(Config) when is_list(Config) -> ?line start_tracer(), ?line Self = self(), @@ -737,8 +733,7 @@ exception_trace() -> ?line {save,me} = X, ok. -on_load(doc) -> "Test the on_load argument for trace_pattern/3."; -on_load(suite) -> []; +%% Test the on_load argument for trace_pattern/3. on_load(Config) when is_list(Config) -> ?line 0 = erlang:trace_pattern(on_load, []), ?line {traced,global} = erlang:trace_info(on_load, traced), @@ -764,8 +759,7 @@ on_load(Config) when is_list(Config) -> -deep_exception(doc) -> "Test the new exception trace."; -deep_exception(suite) -> []; +%% Test the new exception trace. deep_exception(Config) when is_list(Config) -> deep_exception(). @@ -1049,8 +1043,7 @@ deep_expect_N(_Self, 0, exception_from, R) -> -exception_nocatch(doc) -> "Test the new exception trace."; -exception_nocatch(suite) -> []; +%% Test the new exception trace. exception_nocatch(Config) when is_list(Config) -> exception_nocatch(). diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 0c09fe6f7b..f917dc92eb 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -239,8 +239,7 @@ gc() -> gc1(). gc1() -> ok. -t_check_process_code_ets(doc) -> - "Test check_process_code/2 in combination with a fun obtained from an ets table."; +%% Test check_process_code/2 in combination with a fun obtained from an ets table. t_check_process_code_ets(Config) when is_list(Config) -> case test_server:is_native(?MODULE) of true -> diff --git a/erts/emulator/test/crypto_SUITE.erl b/erts/emulator/test/crypto_SUITE.erl index e9498028cb..8ff77b3880 100644 --- a/erts/emulator/test/crypto_SUITE.erl +++ b/erts/emulator/test/crypto_SUITE.erl @@ -33,10 +33,7 @@ all() -> [t_md5, t_md5_update, error, unaligned_context, random_lists, misc_errors]. -misc_errors(doc) -> - ["Test crc32, adler32 and md5 error cases not covered by other tests"]; -misc_errors(suite) -> - []; +%% Test crc32, adler32 and md5 error cases not covered by other tests" misc_errors(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), ?line 1 = erlang:adler32([]), @@ -108,11 +105,7 @@ collect_workers([{L,{Pid,Ref}}|T]) -> exit({error_at_line,L,Other}) end. -random_lists(doc) -> - ["Test crc32, adler32 and md5 on a number of pseudo-randomly generated " - "lists."]; -random_lists(suite) -> - []; +%% Test crc32, adler32 and md5 on a number of pseudo-randomly generated lists. random_lists(Config) when is_list(Config) -> ct:timetrap({minutes, 5}), ?line Num = erlang:system_info(schedulers_online), @@ -216,11 +209,7 @@ random_lists(Config) when is_list(Config) -> ?line run_in_para(Wlist1,Num), ok. -%% -%% -t_md5(doc) -> - ["Generate MD5 message digests and check the result. Examples are " - "from RFC-1321."]; +%% Generate MD5 message digests and check the result. Examples are from RFC-1321. t_md5(Config) when is_list(Config) -> ?line t_md5_test("", "d41d8cd98f00b204e9800998ecf8427e"), ?line t_md5_test("a", "0cc175b9c0f1b6a831c399e269772661"), @@ -238,11 +227,8 @@ t_md5(Config) when is_list(Config) -> "57edf4a22be3c955ac49da2e2107b67a"), ok. -%% -%% -t_md5_update(doc) -> - ["Generate MD5 message using md5_init, md5_update, and md5_final, and" - "check the result. Examples are from RFC-1321."]; +%% Generate MD5 message using md5_init, md5_update, and md5_final, and +%% check the result. Examples are from RFC-1321. t_md5_update(Config) when is_list(Config) -> ?line t_md5_update_1(fun(Str) -> Str end), ?line t_md5_update_1(fun(Str) -> list_to_binary(Str) end), diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl index 5ef751a4cd..54791bfa45 100644 --- a/erts/emulator/test/ddll_SUITE.erl +++ b/erts/emulator/test/ddll_SUITE.erl @@ -71,10 +71,7 @@ all() -> no_trap_exit_and_kill_ports, monitor_demonitor, monitor_demonitor_load, new_interface, lock_driver]. -unload_on_process_exit(suite) -> - []; -unload_on_process_exit(doc) -> - ["Check that the driver is unloaded on process exit"]; +%% Check that the driver is unloaded on process exit unload_on_process_exit(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), @@ -103,10 +100,7 @@ unload_on_process_exit(Config) when is_list(Config) -> ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), ok. -delayed_unload_with_ports(suite) -> - []; -delayed_unload_with_ports(doc) -> - ["Check that the driver is unloaded when the last port is closed"]; +%% Check that the driver is unloaded when the last port is closed delayed_unload_with_ports(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line erl_ddll:try_load(Path, echo_drv, []), @@ -126,10 +120,7 @@ delayed_unload_with_ports(Config) when is_list(Config) -> ?line ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 1000 -> false end, ok. -unload_due_to_process_exit(suite) -> - []; -unload_due_to_process_exit(doc) -> - ["Check that the driver with ports is unloaded on process exit"]; +%% Check that the driver with ports is unloaded on process exit unload_due_to_process_exit(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), @@ -158,10 +149,7 @@ unload_due_to_process_exit(Config) when is_list(Config) -> ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, ok. -no_unload_due_to_process_exit(suite) -> - []; -no_unload_due_to_process_exit(doc) -> - ["Check that a driver with driver loaded in another process is not unloaded on process exit"]; +%% Check that a driver with driver loaded in another process is not unloaded on process exit no_unload_due_to_process_exit(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), @@ -193,10 +181,7 @@ no_unload_due_to_process_exit(Config) when is_list(Config) -> ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, ok. -no_unload_due_to_process_exit_2(suite) -> - []; -no_unload_due_to_process_exit_2(doc) -> - ["Check that a driver with open ports in another process is not unloaded on process exit"]; +%% Check that a driver with open ports in another process is not unloaded on process exit no_unload_due_to_process_exit_2(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), @@ -228,10 +213,7 @@ no_unload_due_to_process_exit_2(Config) when is_list(Config) -> ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, ok. -unload_reload_thingie(suite) -> - []; -unload_reload_thingie(doc) -> - ["Check delayed unload and reload"]; +%% Check delayed unload and reload unload_reload_thingie(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), @@ -271,10 +253,7 @@ unload_reload_thingie(Config) when is_list(Config) -> ?line ok = receive X -> {error, X} after 300 -> ok end, ok. -unload_reload_thingie_2(suite) -> - []; -unload_reload_thingie_2(doc) -> - ["Check delayed unload and reload"]; +%% Check delayed unload and reload unload_reload_thingie_2(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), @@ -313,10 +292,7 @@ unload_reload_thingie_2(Config) when is_list(Config) -> ?line ok = receive X -> {error, X} after 300 -> ok end, ok. -unload_reload_thingie_3(suite) -> - []; -unload_reload_thingie_3(doc) -> - ["Check delayed unload and reload failure"]; +%% Check delayed unload and reload failure unload_reload_thingie_3(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), @@ -357,8 +333,7 @@ unload_reload_thingie_3(Config) when is_list(Config) -> ?line ok = receive X -> {error, X} after 300 -> ok end, ok. -reload_pending(suite) -> []; -reload_pending(doc) -> ["Reload a driver that is pending on a user"]; +%% Reload a driver that is pending on a user reload_pending(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), @@ -407,8 +382,7 @@ reload_pending(Config) when is_list(Config) -> ?line ok = receive Z -> {error, Z} after 300 -> ok end, ok. -load_fail_init(suite) -> []; -load_fail_init(doc) -> ["Tests failure in the init in driver struct."]; +%% Tests failure in the init in driver struct. load_fail_init(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line PathFailing = proplists:get_value(priv_dir, Config), @@ -433,8 +407,7 @@ load_fail_init(Config) when is_list(Config) -> ok. -reload_pending_fail_init(suite) -> []; -reload_pending_fail_init(doc) -> ["Reload a driver that is pending but init fails"]; +%% Reload a driver that is pending but init fails reload_pending_fail_init(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line PathFailing = proplists:get_value(priv_dir, Config), @@ -491,9 +464,7 @@ reload_pending_fail_init(Config) when is_list(Config) -> ?line ok = receive Z -> {error, Z} after 300 -> ok end, ok. -reload_pending_kill(suite) -> []; -reload_pending_kill(doc) -> ["Reload a driver with kill_ports option " - "that is pending on a user"]; +%% Reload a driver with kill_ports option that is pending on a user reload_pending_kill(Config) when is_list(Config) -> ?line OldFlag = process_flag(trap_exit,true), ?line Path = proplists:get_value(data_dir, Config), @@ -584,20 +555,14 @@ reload_pending_kill(Config) when is_list(Config) -> ok. -more_error_codes(suite) -> - []; -more_error_codes(doc) -> - ["Some more error code checking"]; +%% Some more error code checking more_error_codes(Config) when is_list(Config) -> ?line {error,Err} = erl_ddll:try_load("./echo_dr",echo_dr,[]), ?line true = is_list(erl_ddll:format_error(Err)), ?line true = is_list(erl_ddll:format_error(not_loaded)), ok. -forced_port_killing(suite) -> - []; -forced_port_killing(doc) -> - ["Check kill_ports option to try_unload "]; +%% Check kill_ports option to try_unload forced_port_killing(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line OldFlag=process_flag(trap_exit,true), @@ -628,10 +593,7 @@ forced_port_killing(Config) when is_list(Config) -> ?line ok = receive X -> {error, X} after 300 -> ok end, ok. -no_trap_exit_and_kill_ports(suite) -> - []; -no_trap_exit_and_kill_ports(doc) -> - ["Check delayed unload and reload with no trap_exit"]; +%% Check delayed unload and reload with no trap_exit no_trap_exit_and_kill_ports(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line Parent = self(), @@ -667,10 +629,7 @@ no_trap_exit_and_kill_ports(Config) when is_list(Config) -> ?line process_flag(trap_exit,OldFlag), ok. -monitor_demonitor(suite) -> - []; -monitor_demonitor(doc) -> - ["Check monitor and demonitor of drivers"]; +%% Check monitor and demonitor of drivers monitor_demonitor(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line erl_ddll:try_load(Path, echo_drv, []), @@ -683,10 +642,7 @@ monitor_demonitor(Config) when is_list(Config) -> ?line ok = receive _ -> error after 300 -> ok end, ok. -monitor_demonitor_load(suite) -> - []; -monitor_demonitor_load(doc) -> - ["Check monitor/demonitor of driver loading"]; +%% Check monitor/demonitor of driver loading monitor_demonitor_load(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line {ok,loaded} = erl_ddll:try_load(Path, echo_drv, []), @@ -712,10 +668,7 @@ monitor_demonitor_load(Config) when is_list(Config) -> ?line ok = unload_expect_fast(echo_drv,[]), ok. -new_interface(suite) -> - []; -new_interface(doc) -> - ["Test the new load/unload/reload interface"]; +%% Test the new load/unload/reload interface new_interface(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), % Typical scenario @@ -834,10 +787,9 @@ errors(Config) when is_list(Config) -> ?line {ok, L1} = erl_ddll:loaded_drivers(), ok. -reference_count(doc) -> - ["Check that drivers are unloaded when their reference count ", - "reaches zero, and that they cannot be unloaded while ", - "they are still referenced."]; +%% Check that drivers are unloaded when their reference count +%% reaches zero, and that they cannot be unloaded while +%% they are still referenced. reference_count(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), @@ -877,9 +829,8 @@ nice_echo_loader(Path, Starter) -> end. -kill_port(doc) -> - ["Test that a port that uses a driver is killed when the ", - "process that loaded the driver dies."]; +%% Test that a port that uses a driver is killed when the +%% process that loaded the driver dies. kill_port(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), @@ -912,9 +863,8 @@ kill_port(Config) when is_list(Config) -> end, ok. -dont_kill_port(doc) -> - ["Test that a port that uses a driver is not killed when the ", - "process that loaded the driver dies and it's nicely opened."]; +%% Test that a port that uses a driver is not killed when the +%% process that loaded the driver dies and it's nicely opened. dont_kill_port(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), @@ -951,8 +901,8 @@ dont_kill_port(Config) when is_list(Config) -> end, ok. -properties(doc) -> ["Test that a process that loaded a driver ", - "is the only process that can unload it."]; +%% Test that a process that loaded a driver +%% is the only process that can unload it. properties(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), @@ -978,7 +928,7 @@ properties(Config) when is_list(Config) -> ?line test_server:sleep(200), % Give time to unload. ok. -load_and_unload(doc) -> ["Load two drivers and unload them in load order."]; +%% Load two drivers and unload them in load order. load_and_unload(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line {ok, Loaded_drivers1} = erl_ddll:loaded_drivers(), @@ -993,10 +943,7 @@ load_and_unload(Config) when is_list(Config) -> ?line [] = ordsets:to_list(ordsets:subtract(Set2, Set1)), ok. -lock_driver(suite) -> - []; -lock_driver(doc) -> - ["Check multiple calls to driver_lock_driver"]; +%% Check multiple calls to driver_lock_driver lock_driver(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line {ok, _} = erl_ddll:try_load(Path, lock_drv, []), diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl index 213234af52..08e2448146 100644 --- a/erts/emulator/test/decode_packet_SUITE.erl +++ b/erts/emulator/test/decode_packet_SUITE.erl @@ -48,8 +48,6 @@ init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> end_per_testcase(_Func, _Config) -> ok. -basic(doc) -> []; -basic(suite) -> []; basic(Config) when is_list(Config) -> ?line Packet = <<101,22,203,54,175>>, ?line Rest = <<123,34,0,250>>, @@ -213,8 +211,6 @@ pack_ssl(Content, Major, Minor, Body) -> {Res, {ssl_tls,[],C,{Major,Minor}, Data}}. -packet_size(doc) -> []; -packet_size(suite) -> []; packet_size(Config) when is_list(Config) -> ?line Packet = <<101,22,203,54,175>>, ?line Rest = <<123,34,0,250>>, @@ -264,8 +260,6 @@ packet_size(Config) when is_list(Config) -> ok. -neg(doc) -> []; -neg(suite) -> []; neg(Config) when is_list(Config) -> ?line Bin = <<"dummy">>, Fun = fun()->dummy end, @@ -294,8 +288,6 @@ neg(Config) when is_list(Config) -> ok. -http(doc) -> []; -http(suite) -> []; http(Config) when is_list(Config) -> ?line <<"foo">> = http_do(http_request("foo")), ?line <<" bar">> = http_do(http_request(" bar")), @@ -472,8 +464,6 @@ http_uri_variants() -> {"something_else", "something_else", <<"something_else">>}]. -line(doc) -> []; -line(suite) -> []; line(Config) when is_list(Config) -> Text = <<"POST /invalid/url HTTP/1.1\r\n" "Connection: close\r\n" @@ -524,8 +514,6 @@ find_in_binary(Byte, Bin) -> P -> P end. -ssl(doc) -> []; -ssl(suite) -> []; ssl(Config) when is_list(Config) -> Major = 34, Minor = 17, @@ -541,7 +529,7 @@ ssl(Config) when is_list(Config) -> F(v2hello), ok. -otp_8536(doc) -> ["Corrupt sub-binary-strings from httph_bin"]; +%% Corrupt sub-binary-strings from httph_bin otp_8536(Config) when is_list(Config) -> lists:foreach(fun otp_8536_do/1, lists:seq(1,50)), ok. @@ -570,8 +558,7 @@ decode_pkt(Type,Bin,Opts) -> %%io:format(" -> ~p\n",[Res]), Res. -otp_9389(doc) -> ["Verify line_length works correctly for HTTP headers"]; -otp_9389(suite) -> []; +%% Verify line_length works correctly for HTTP headers otp_9389(Config) when is_list(Config) -> Opts = [{packet_size, 16384}, {line_length, 3000}], Pkt = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /", @@ -590,8 +577,7 @@ otp_9389(Config) when is_list(Config) -> erlang:decode_packet(httph, Rest3, Opts), ok. -otp_9389_line(doc) -> ["Verify packet_size works correctly for line mode"]; -otp_9389_line(suite) -> []; +%% Verify packet_size works correctly for line mode otp_9389_line(Config) when is_list(Config) -> Opts = [{packet_size, 20}], Line1 = <<"0123456789012345678\n">>, diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index e341d4ab2b..d4d70632c2 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -89,8 +89,7 @@ groups() -> [bad_dist_ext_receive, bad_dist_ext_process_info, bad_dist_ext_control, bad_dist_ext_connection_id]}]. -ping(doc) -> - ["Tests pinging a node in different ways."]; +%% Tests pinging a node in different ways. ping(Config) when is_list(Config) -> Times = 1024, @@ -250,13 +249,10 @@ receiver(Terms, Size) -> -local_send_big(doc) -> - ["Sends several big message to an non-registered process on ", - "the local node."]; +%% Sends several big message to an non-registered process on the local node. local_send_big(Config) when is_list(Config) -> - Data0=local_send_big(doc)++ - ["Tests sending small and big messages to a non-existing ", - "local registered process."], + Data0= ["Tests sending small and big messages to a non-existing ", + "local registered process."], Data1=[Data0,[Data0, Data0, [Data0], Data0],Data0], Data2=Data0++lists:flatten(Data1)++ list_to_binary(lists:flatten(Data1)), @@ -264,21 +260,18 @@ local_send_big(Config) when is_list(Config) -> ?line test_server:do_times(4096, Func), ok. -local_send_small(doc) -> - ["Sends a small message to an non-registered process on the ", - "local node."]; +%% Sends a small message to an non-registered process on the local node. local_send_small(Config) when is_list(Config) -> Data={some_stupid, "arbitrary", 'Data'}, Func=fun() -> Data= {unregistered_name, node()} ! Data end, ?line test_server:do_times(4096, Func), ok. -local_send_legal(doc) -> - ["Sends data to a registered process on the local node, ", - "as if it was on another node."]; +%% Sends data to a registered process on the local node, as if it was on another node. local_send_legal(Config) when is_list(Config) -> Times=16384, - Data={local_send_legal(doc), local_send_legal(doc)}, + Txt = "Some Not so random Data", + Data={[Txt,Txt,Txt], [Txt,Txt,Txt]}, Pid=spawn(?MODULE,receiver2, [0, 0]) , ?line true=register(registered_process, Pid), @@ -306,7 +299,7 @@ receiver2(Num, TotSize) -> receiver2(Num+1, TotSize+size(Stuff)) end. -link_to_busy(doc) -> "Test that link/1 to a busy distribution port works."; +%% Test that link/1 to a busy distribution port works. link_to_busy(Config) when is_list(Config) -> ct:timetrap({seconds, 60}), ?line {ok, Node} = start_node(link_to_busy), @@ -352,7 +345,7 @@ applied_linker(Pid) -> tail_applied_linker(Pid) -> apply(erlang, link, [Pid]). -exit_to_busy(doc) -> "Test that exit/2 to a busy distribution port works."; +%% Test that exit/2 to a busy distribution port works. exit_to_busy(Config) when is_list(Config) -> ct:timetrap({seconds, 60}), ?line {ok, Node} = start_node(exit_to_busy), @@ -509,9 +502,8 @@ sink1() -> _Any -> sink1() end. -lost_exit(doc) -> - "Test that EXIT and DOWN messages send to another node are not lost if " - "the distribution port is busy."; +%% Test that EXIT and DOWN messages send to another node are not lost if +%% the distribution port is busy. lost_exit(Config) when is_list(Config) -> ?line {ok, Node} = start_node(lost_exit), @@ -575,9 +567,8 @@ dummy_waiter() -> ok end. -link_to_dead(doc) -> - ["Test that linking to a dead remote process gives an EXIT message ", - "AND that the link is teared down."]; +%% Test that linking to a dead remote process gives an EXIT message +%% AND that the link is teared down. link_to_dead(Config) when is_list(Config) -> ?line process_flag(trap_exit, true), ?line {ok, Node} = start_node(link_to_dead), @@ -611,9 +602,8 @@ link_to_dead(Config) when is_list(Config) -> dead_process() -> erlang:error(die). -link_to_dead_new_node(doc) -> - ["Test that linking to a pid on node that has gone and restarted gives ", - "the correct EXIT message (OTP-2304)."]; +%% Test that linking to a pid on node that has gone and restarted gives +%% the correct EXIT message (OTP-2304). link_to_dead_new_node(Config) when is_list(Config) -> ?line process_flag(trap_exit, true), @@ -647,8 +637,7 @@ link_to_dead_new_node(Config) when is_list(Config) -> end, ok. -applied_monitor_node(doc) -> - "Test that monitor_node/2 works when applied."; +%% Test that monitor_node/2 works when applied. applied_monitor_node(Config) when is_list(Config) -> ?line NonExisting = list_to_atom("__non_existing__@" ++ hostname()), @@ -668,9 +657,8 @@ applied_monitor_node(Config) when is_list(Config) -> tail_apply(M, F, A) -> apply(M, F, A). -ref_port_roundtrip(doc) -> - "Test that sending a port or reference to another node and back again " - "doesn't correct them in any way."; +%% Test that sending a port or reference to another node and back again +%% doesn't correct them in any way. ref_port_roundtrip(Config) when is_list(Config) -> ?line process_flag(trap_exit, true), ?line Port = open_port({spawn, efile}, []), @@ -697,9 +685,8 @@ ref_port_roundtrip(Config) when is_list(Config) -> roundtrip(Term) -> exit(Term). -nil_roundtrip(doc) -> - "Test that the smallest external term [] aka NIL can be sent to " - "another node node and back again."; +%% Test that the smallest external term [] aka NIL can be sent to +%% another node node and back again. nil_roundtrip(Config) when is_list(Config) -> ?line process_flag(trap_exit, true), ?line {ok, Node} = start_node(nil_roundtrip), @@ -724,8 +711,7 @@ bounce(Dest) -> show_term(Term) -> binary_to_list(term_to_binary(Term)). -stop_dist(doc) -> - ["Tests behaviour after net_kernel:stop (OTP-2586)."]; +%% Tests behaviour after net_kernel:stop (OTP-2586). stop_dist(Config) when is_list(Config) -> ?line Str = os:cmd(atom_to_list(lib:progname()) ++ " -noshell -pa " @@ -741,20 +727,14 @@ stop_dist(Config) when is_list(Config) -> ok. -trap_bif_1(doc) -> - [""]; trap_bif_1(Config) when is_list(Config) -> ?line {true} = tr1(), ok. -trap_bif_2(doc) -> - [""]; trap_bif_2(Config) when is_list(Config) -> ?line {true} = tr2(), ok. -trap_bif_3(doc) -> - [""]; trap_bif_3(Config) when is_list(Config) -> ?line {hoo} = tr3(), ok. @@ -792,7 +772,8 @@ tr3() -> % * n2 gets pang when pinging n1 % * n2 forces connection by using net_kernel:connect_node (ovverrides) % * n2 gets pong when pinging n1. -dist_auto_connect_once(doc) -> "Test the dist_auto_connect once kernel parameter"; + +%% Test the dist_auto_connect once kernel parameter dist_auto_connect_once(Config) when is_list(Config) -> ?line Sock = start_relay_node(dist_auto_connect_relay_node,[]), ?line NN = inet_rpc_nodename(Sock), @@ -964,10 +945,6 @@ dist_auto_connect_relay(Parent) -> dist_auto_connect_relay(Parent). -dist_parallel_send(doc) -> - []; -dist_parallel_send(suite) -> - []; dist_parallel_send(Config) when is_list(Config) -> ?line {ok, RNode} = start_node(dist_parallel_receiver), ?line {ok, SNode} = start_node(dist_parallel_sender), @@ -1383,11 +1360,7 @@ start_link(Offender,P) -> io:format("Ref is ~p~n",[Ref]), ok. -bad_dist_structure(suite) -> - []; -bad_dist_structure(doc) -> - ["Test dist messages with valid structure (binary to term ok) but malformed" - "control content"]; +%% Test dist messages with valid structure (binary to term ok) but malformed control content bad_dist_structure(Config) when is_list(Config) -> ct:timetrap({seconds, 15}), diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index f519634281..82dd2b87a6 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -179,7 +179,7 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. -outputv_errors(doc) -> "Test sending bad types to port with an outputv-capable driver."; +%% Test sending bad types to port with an outputv-capable driver. outputv_errors(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line erl_ddll:start(), @@ -262,7 +262,7 @@ build_iolist(N0, Base) -> [47,L,L|Seq] end. -outputv_echo(doc) -> ["Test echoing data with a driver that supports outputv."]; +%% Test echoing data with a driver that supports outputv. outputv_echo(Config) when is_list(Config) -> ct:timetrap({minutes, 10}), Name = 'outputv_drv', @@ -375,7 +375,7 @@ compare(Got, Expected) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -timer_measure(doc) -> ["Check that timers time out in good time."]; +%% Check that timers time out in good time. timer_measure(Config) when is_list(Config) -> Name = 'timer_drv', ?line Port = start_driver(Config, Name, false), @@ -405,7 +405,7 @@ try_timeouts(Port, Timeout) -> ?line ct:fail("driver failed to timeout") end. -timer_cancel(doc) -> ["Try cancelling timers set in a driver."]; +%% Try cancelling timers set in a driver. timer_cancel(Config) when is_list(Config) -> Name = 'timer_drv', ?line Port = start_driver(Config, Name, false), @@ -514,9 +514,8 @@ try_change_timer(Port, Timeout) -> %% Queue test suites %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -queue_echo(doc) -> - ["1) Queue up data in a driver that uses the full driver_queue API to do this." - "2) Get the data back, a random amount at a time."]; +%% 1) Queue up data in a driver that uses the full driver_queue API to do this. +%% 2) Get the data back, a random amount at a time. queue_echo(Config) when is_list(Config) -> case test_server:is_native(?MODULE) of true -> exit(crashes_native_code); @@ -708,10 +707,6 @@ read_head(Port, Size) -> erlang:port_control(Port, ?READ_HEAD, <>). -driver_unloaded(doc) -> - []; -driver_unloaded(suite) -> - []; driver_unloaded(Config) when is_list(Config) -> ?line process_flag(trap_exit, true), ?line Drv = timer_drv, @@ -736,8 +731,6 @@ driver_unloaded(Config) when is_list(Config) -> end. -io_ready_exit(doc) -> []; -io_ready_exit(suite) -> []; io_ready_exit(Config) when is_list(Config) -> ?line OTE = process_flag(trap_exit, true), ?line Test = self(), @@ -796,8 +789,6 @@ io_ready_exit(Config) when is_list(Config) -> -define(CHKIO_SMP_SELECT, 7). -define(CHKIO_DRV_USE, 8). -use_fallback_pollset(doc) -> []; -use_fallback_pollset(suite) -> []; use_fallback_pollset(Config) when is_list(Config) -> FlbkFun = fun () -> ChkIoDuring = erlang:system_info(check_io), @@ -837,29 +828,21 @@ use_fallback_pollset(Config) when is_list(Config) -> _ -> ?line OkRes end. -bad_fd_in_pollset(doc) -> []; -bad_fd_in_pollset(suite) -> []; bad_fd_in_pollset(Config) when is_list(Config) -> ?line chkio_test_fini(chkio_test(chkio_test_init(Config), ?CHKIO_BAD_FD_IN_POLLSET, fun () -> ?line sleep(1000) end)). -driver_event(doc) -> []; -driver_event(suite) -> []; driver_event(Config) when is_list(Config) -> ?line chkio_test_fini(chkio_test(chkio_test_init(Config), ?CHKIO_DRIVER_EVENT, fun () -> ?line sleep(1000) end)). -fd_change(doc) -> []; -fd_change(suite) -> []; fd_change(Config) when is_list(Config) -> ?line chkio_test_fini(chkio_test(chkio_test_init(Config), ?CHKIO_FD_CHANGE, fun () -> ?line sleep(1000) end)). -steal_control(doc) -> []; -steal_control(suite) -> []; steal_control(Config) when is_list(Config) -> ?line chkio_test_fini(case chkio_test_init(Config) of {erts_poll_info, _} = Hndl -> @@ -1038,11 +1021,9 @@ get_stable_check_io_info() -> get_stable_check_io_info() end. -otp_6602(doc) -> ["Missed port lock when stealing control of fd from a " - "driver that didn't use the same lock. The lock checker " - "used to trigger on this and dump core."]; -otp_6602(suite) -> - []; +%% Missed port lock when stealing control of fd from a +%% driver that didn't use the same lock. The lock checker +%% used to trigger on this and dump core. otp_6602(Config) when is_list(Config) -> ?line {ok, Node} = start_node(Config), ?line Done = make_ref(), @@ -1084,24 +1065,12 @@ otp_6602(Config) when is_list(Config) -> -define(EXPECTED_SYSTEM_INFO_NAMES, ?EXPECTED_SYSTEM_INFO_NAMES4). -'driver_system_info_base_ver'(doc) -> - []; -'driver_system_info_base_ver'(suite) -> - []; 'driver_system_info_base_ver'(Config) when is_list(Config) -> ?line driver_system_info_test(Config, sys_info_base_drv). -'driver_system_info_prev_ver'(doc) -> - []; -'driver_system_info_prev_ver'(suite) -> - []; 'driver_system_info_prev_ver'(Config) when is_list(Config) -> ?line driver_system_info_test(Config, sys_info_prev_drv). -driver_system_info_current_ver(doc) -> - []; -driver_system_info_current_ver(suite) -> - []; driver_system_info_current_ver(Config) when is_list(Config) -> ?line driver_system_info_test(Config, sys_info_curr_drv). @@ -1205,10 +1174,7 @@ check_si_res(Unexpected) -> -define(MON_OP_MONITOR_ME_LATER,4). -define(MON_OP_DO_DELAYED_MONITOR,5). -driver_monitor(suite) -> - []; -driver_monitor(doc) -> - ["Test monitoring of processes from drivers"]; +%% Test monitoring of processes from drivers driver_monitor(Config) when is_list(Config) -> ?line Name = monitor_drv, ?line Port = start_driver(Config, Name, false), @@ -1421,48 +1387,30 @@ ioq_exit_test(Config, TestNo) -> erl_ddll:unload_driver(Drv) end. -ioq_exit_ready_input(doc) -> []; -ioq_exit_ready_input(suite) -> []; ioq_exit_ready_input(Config) when is_list(Config) -> ioq_exit_test(Config, ?IOQ_EXIT_READY_INPUT). -ioq_exit_ready_output(doc) -> []; -ioq_exit_ready_output(suite) -> []; ioq_exit_ready_output(Config) when is_list(Config) -> ioq_exit_test(Config, ?IOQ_EXIT_READY_OUTPUT). -ioq_exit_timeout(doc) -> []; -ioq_exit_timeout(suite) -> []; ioq_exit_timeout(Config) when is_list(Config) -> ioq_exit_test(Config, ?IOQ_EXIT_TIMEOUT). -ioq_exit_ready_async(doc) -> []; -ioq_exit_ready_async(suite) -> []; ioq_exit_ready_async(Config) when is_list(Config) -> ioq_exit_test(Config, ?IOQ_EXIT_READY_ASYNC). -ioq_exit_event(doc) -> []; -ioq_exit_event(suite) -> []; ioq_exit_event(Config) when is_list(Config) -> ioq_exit_test(Config, ?IOQ_EXIT_EVENT). -ioq_exit_ready_input_async(doc) -> []; -ioq_exit_ready_input_async(suite) -> []; ioq_exit_ready_input_async(Config) when is_list(Config) -> ioq_exit_test(Config, ?IOQ_EXIT_READY_INPUT_ASYNC). -ioq_exit_ready_output_async(doc) -> []; -ioq_exit_ready_output_async(suite) -> []; ioq_exit_ready_output_async(Config) when is_list(Config) -> ioq_exit_test(Config, ?IOQ_EXIT_READY_OUTPUT_ASYNC). -ioq_exit_timeout_async(doc) -> []; -ioq_exit_timeout_async(suite) -> []; ioq_exit_timeout_async(Config) when is_list(Config) -> ioq_exit_test(Config, ?IOQ_EXIT_TIMEOUT_ASYNC). -ioq_exit_event_async(doc) -> []; -ioq_exit_event_async(suite) -> []; ioq_exit_event_async(Config) when is_list(Config) -> ioq_exit_test(Config, ?IOQ_EXIT_EVENT_ASYNC). @@ -1481,33 +1429,21 @@ vsn_mismatch_test(Config, LoadResult) -> ?line ok end. -zero_extended_marker_garb_drv(doc) -> []; -zero_extended_marker_garb_drv(suite) -> []; zero_extended_marker_garb_drv(Config) when is_list(Config) -> vsn_mismatch_test(Config, {error, driver_incorrect_version}). -invalid_extended_marker_drv(doc) -> []; -invalid_extended_marker_drv(suite) -> []; invalid_extended_marker_drv(Config) when is_list(Config) -> vsn_mismatch_test(Config, {error, driver_incorrect_version}). -larger_major_vsn_drv(doc) -> []; -larger_major_vsn_drv(suite) -> []; larger_major_vsn_drv(Config) when is_list(Config) -> vsn_mismatch_test(Config, {error, driver_incorrect_version}). -larger_minor_vsn_drv(doc) -> []; -larger_minor_vsn_drv(suite) -> []; larger_minor_vsn_drv(Config) when is_list(Config) -> vsn_mismatch_test(Config, {error, driver_incorrect_version}). -smaller_major_vsn_drv(doc) -> []; -smaller_major_vsn_drv(suite) -> []; smaller_major_vsn_drv(Config) when is_list(Config) -> vsn_mismatch_test(Config, {error, driver_incorrect_version}). -smaller_minor_vsn_drv(doc) -> []; -smaller_minor_vsn_drv(suite) -> []; smaller_minor_vsn_drv(Config) when is_list(Config) -> DrvVsnStr = erlang:system_info(driver_version), case drv_vsn_str2tup(DrvVsnStr) of @@ -1522,8 +1458,6 @@ smaller_minor_vsn_drv(Config) when is_list(Config) -> -define(PEEK_NONXQ_TEST, 0). -define(PEEK_NONXQ_WAIT, 1). -peek_non_existing_queue(doc) -> []; -peek_non_existing_queue(suite) -> []; peek_non_existing_queue(Config) when is_list(Config) -> ?line OTE = process_flag(trap_exit, true), ?line Drv = peek_non_existing_queue_drv, @@ -1574,10 +1508,6 @@ peek_non_existing_queue(Config) when is_list(Config) -> erl_ddll:unload_driver(Drv) end. -otp_6879(doc) -> - []; -otp_6879(suite) -> - []; otp_6879(Config) when is_list(Config) -> ?line Drv = 'otp_6879_drv', ?line Parent = self(), @@ -1627,10 +1557,6 @@ otp_6879_call(Port, Data, N) -> BadData -> {mismatch, Data, BadData} end. -caller(doc) -> - []; -caller(suite) -> - []; caller(Config) when is_list(Config) -> ?line run_caller_test(Config, false), ?line run_caller_test(Config, true). @@ -1681,10 +1607,7 @@ chk_caller(Port, Callback, ExpectedCaller) -> ExpectedCaller = Caller end. -many_events(suite) -> - []; -many_events(doc) -> - ["Check that many simultaneously signalled events work (win32)"]; +%% Check that many simultaneously signalled events work (win32) many_events(Config) when is_list(Config) -> ?line Name = 'many_events_drv', ?line Port = start_driver(Config, Name, false), @@ -1704,10 +1627,6 @@ many_events(Config) when is_list(Config) -> ?line ok. -missing_callbacks(doc) -> - []; -missing_callbacks(suite) -> - []; missing_callbacks(Config) when is_list(Config) -> ?line Name = 'missing_callback_drv', ?line Port = start_driver(Config, Name, false), @@ -1724,10 +1643,7 @@ missing_callbacks(Config) when is_list(Config) -> ?line stop_driver(Port, Name), ?line ok. -smp_select(doc) -> - ["Test concurrent calls to driver_select."]; -smp_select(suite) -> - []; +%% Test concurrent calls to driver_select. smp_select(Config) when is_list(Config) -> case os:type() of {win32,_} -> {skipped, "Test not implemented for this OS"}; @@ -1783,10 +1699,7 @@ smp_select_wait(Pids, TimeoutMsg) -> end. -driver_select_use(doc) -> - ["Test driver_select() with new ERL_DRV_USE flag."]; -driver_select_use(suite) -> - []; +%% Test driver_select() with new ERL_DRV_USE flag. driver_select_use(Config) when is_list(Config) -> case os:type() of {win32,_} -> {skipped, "Test not implemented for this OS"}; diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index cc061c0cfe..667f1ae9b4 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -87,8 +87,7 @@ file_keys(Dir,Num,FdList,FnList) -> end end. -async_dist(doc) -> - "Check that the distribution of files over async threads is fair"; +%% Check that the distribution of files over async threads is fair async_dist(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir,Config), TestFile = filename:join(DataDir, "existing_file"), @@ -130,7 +129,6 @@ async_dist(Config) when is_list(Config) -> %% that we get the same number of files every time. %% -iter_max_files(suite) -> []; iter_max_files(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir,Config), TestFile = filename:join(DataDir, "existing_file"), diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl index 5c861ae9f0..316dc4b5c5 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE.erl +++ b/erts/emulator/test/erl_drv_thread_SUITE.erl @@ -39,16 +39,10 @@ all() -> %% Testcases %% %% %% -basic(suite) -> []; -basic(doc) -> []; basic(Cfg) -> ?line drv_case(Cfg, basic). -rwlock(suite) -> []; -rwlock(doc) -> []; rwlock(Cfg) -> ?line drv_case(Cfg, rwlock). -tsd(suite) -> []; -tsd(doc) -> []; tsd(Cfg) -> ?line drv_case(Cfg, tsd). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index 472232a27d..d7257ff7aa 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -104,8 +104,7 @@ end_per_suite(_Config) -> catch erts_debug:set_internal_state(available_internal_state, false). -links(doc) -> ["Tests node local links"]; -links(suite) -> []; +%% Tests node local links links(Config) when is_list(Config) -> ?line common_link_test(node(), node()), ?line true = link(self()), @@ -113,8 +112,7 @@ links(Config) when is_list(Config) -> ?line true = unlink(self()), ?line ok. -dist_links(doc) -> ["Tests distributed links"]; -dist_links(suite) -> []; +%% Tests distributed links dist_links(Config) when is_list(Config) -> ?line [NodeName] = get_names(1, dist_link), ?line {ok, Node} = start_node(NodeName), @@ -175,8 +173,7 @@ common_link_test(NodeA, NodeB) -> ?line check_unlink(TP3, TP2), ?line ok. -monitor_nodes(doc) -> ["Tests monitor of nodes"]; -monitor_nodes(suite) -> []; +%% Tests monitor of nodes monitor_nodes(Config) when is_list(Config) -> ?line [An, Bn, Cn, Dn] = get_names(4, dist_link), ?line {ok, A} = start_node(An), @@ -230,8 +227,7 @@ monitor_nodes(Config) when is_list(Config) -> ?line ok. -process_monitors(doc) -> ["Tests node local process monitors"]; -process_monitors(suite) -> []; +%% Tests node local process monitors process_monitors(Config) when is_list(Config) -> ?line common_process_monitors(node(), node()), ?line Mon1 = erlang:monitor(process,self()), @@ -251,8 +247,7 @@ process_monitors(Config) when is_list(Config) -> ?line ok end. -dist_process_monitors(doc) -> ["Tests distributed process monitors"]; -dist_process_monitors(suite) -> []; +%% Tests distributed process monitors dist_process_monitors(Config) when is_list(Config) -> ?line [Name] = get_names(1,dist_process_monitors), ?line {ok, Node} = start_node(Name), @@ -328,10 +323,8 @@ run_common_process_monitors(TP1, TP2) -> ?line ok. -busy_dist_port_monitor(doc) -> ["Tests distributed monitor/2, demonitor/1, " - "and 'DOWN' message over busy distribution " - "port"]; -busy_dist_port_monitor(suite) -> []; +%% Tests distributed monitor/2, demonitor/1, and 'DOWN' message +%% over busy distribution port busy_dist_port_monitor(Config) when is_list(Config) -> ?line Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of @@ -373,9 +366,8 @@ busy_dist_port_monitor(Config) when is_list(Config) -> ?line stop_busy_dist_port_tracer(Tracer), ?line ok. -busy_dist_port_link(doc) -> ["Tests distributed link/1, unlink/1, and 'EXIT'", - " message over busy distribution port"]; -busy_dist_port_link(suite) -> []; +%% Tests distributed link/1, unlink/1, and 'EXIT' +%% message over busy distribution port busy_dist_port_link(Config) when is_list(Config) -> ?line Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of "true" -> start_busy_dist_port_tracer(); @@ -421,13 +413,9 @@ busy_dist_port_link(Config) when is_list(Config) -> ?line ok. -otp_5772_link(doc) -> []; -otp_5772_link(suite) -> []; otp_5772_link(Config) when is_list(Config) -> ?line otp_5772_link_test(node()). -otp_5772_dist_link(doc) -> []; -otp_5772_dist_link(suite) -> []; otp_5772_dist_link(Config) when is_list(Config) -> ?line [An] = get_names(1, otp_5772_dist_link), ?line {ok, A} = start_node(An), @@ -457,13 +445,9 @@ otp_5772_link_test(Node) -> ?line process_flag(priority, Prio), ?line ok. -otp_5772_monitor(doc) -> []; -otp_5772_monitor(suite) -> []; otp_5772_monitor(Config) when is_list(Config) -> ?line otp_5772_monitor_test(node()). -otp_5772_dist_monitor(doc) -> []; -otp_5772_dist_monitor(suite) -> []; otp_5772_dist_monitor(Config) when is_list(Config) -> ?line [An] = get_names(1, otp_5772_dist_monitor), ?line {ok, A} = start_node(An), diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index 33728c3f5a..56b6248b10 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -73,10 +73,7 @@ groups() -> [{estone_bench, [{repeat,50}],[estone_bench]}]. -estone(suite) -> - []; -estone(doc) -> - ["EStone Test"]; +%% EStone Test estone(Config) when is_list(Config) -> ?line DataDir = proplists:get_value(data_dir,Config), ?line Mhz=get_cpu_speed(os:type(),DataDir), diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index df44e28e2c..9fd414ff97 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -48,10 +48,7 @@ groups() -> %% OTP-7178, list_to_float on very small numbers should give 0.0 %% instead of exception, i.e. ignore underflow. %% -otp_7178(suite) -> - []; -otp_7178(doc) -> - ["test that list_to_float on very small numbers give 0.0"]; +%% test that list_to_float on very small numbers give 0.0 otp_7178(Config) when is_list(Config) -> ?line X = list_to_float("1.0e-325"), ?line true = (X < 0.00000001) and (X > -0.00000001), diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index f89cc623c4..db14ffc005 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -45,9 +45,7 @@ all() -> const_propagation, t_arity, t_is_function2, t_fun_info, t_fun_info_mfa]. -bad_apply(doc) -> - "Test that the correct EXIT code is returned for all types of bad funs."; -bad_apply(suite) -> []; +%% Test that the correct EXIT code is returned for all types of bad funs. bad_apply(Config) when is_list(Config) -> ?line bad_apply_fc(42, [0]), ?line bad_apply_fc(xx, [1]), @@ -85,9 +83,7 @@ bad_apply_badarg(Fun, Args) -> ct:fail({bad_result, Other}) end. -bad_fun_call(doc) -> - "Try directly calling bad funs."; -bad_fun_call(suite) -> []; +%% Try directly calling bad funs. bad_fun_call(Config) when is_list(Config) -> ?line bad_call_fc(42), ?line bad_call_fc(xx), @@ -259,7 +255,7 @@ copy_term(Term) -> make_fun(X) -> fun() -> X end. -ordering(doc) -> "Tests ordering of funs."; +%% Tests ordering of funs. ordering(Config) when is_list(Config) -> F1 = make_fun(1, 2), F1_copy = copy_term(F1), @@ -386,8 +382,7 @@ ordering(Config) when is_list(Config) -> make_fun(X, Y) -> fun(A) -> A*X+Y end. -fun_to_port(doc) -> "Try sending funs to ports (should fail)."; -fun_to_port(suite) -> []; +%% Try sending funs to ports (should fail). fun_to_port(Config) when is_list(Config) -> ?line fun_to_port(Config, xxx), ?line fun_to_port(Config, fun() -> 42 end), @@ -417,8 +412,7 @@ build_io_list(N) -> 1 -> [7,L|L] end. -t_hash(doc) -> "Test the hash/2 BIF on funs."; -t_hash(suite) -> []; +%% Test the hash/2 BIF on funs. t_hash(Config) when is_list(Config) -> F1 = fun(_X) -> 1 end, F2 = fun(_X) -> 2 end, @@ -445,8 +439,7 @@ t_hash(Config) when is_list(Config) -> hash(Term) -> erlang:hash(Term, 16#7ffffff). -t_phash(doc) -> "Test the phash/2 BIF on funs."; -t_phash(suite) -> []; +%% Test the phash/2 BIF on funs. t_phash(Config) when is_list(Config) -> F1 = fun(_X) -> 1 end, F2 = fun(_X) -> 2 end, @@ -474,8 +467,7 @@ t_phash(Config) when is_list(Config) -> phash(Term) -> erlang:phash(Term, 16#7ffffff). -t_phash2(doc) -> "Test the phash2/2 BIF on funs."; -t_phash2(suite) -> []; +%% Test the phash2/2 BIF on funs. t_phash2(Config) when is_list(Config) -> F1 = fun(_X) -> 1 end, F2 = fun(_X) -> 2 end, @@ -506,8 +498,7 @@ phash2(Term) -> make_fun(X, Y, Z) -> fun() -> {X,Y,Z} end. -md5(doc) -> "Test that MD5 bifs reject funs properly."; -md5(suite) -> []; +%% Test that MD5 bifs reject funs properly. md5(Config) when is_list(Config) -> _ = size(erlang:md5_init()), diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl index cccd726b1c..8e5f0b05a1 100644 --- a/erts/emulator/test/gc_SUITE.erl +++ b/erts/emulator/test/gc_SUITE.erl @@ -34,9 +34,9 @@ all() -> [grow_heap, grow_stack, grow_stack_heap]. -grow_heap(doc) -> ["Produce a growing list of elements, ", - "for X calls, then drop one item per call", - "until the list is empty."]; +%% Produce a growing list of elements, +%% for X calls, then drop one item per call +%% until the list is empty. grow_heap(Config) when is_list(Config) -> ct:timetrap({minutes, 40}), ok = grow_heap1(256), @@ -68,8 +68,8 @@ grow_heap1([_|List], MaxLen, CurLen, down) -> -grow_stack(doc) -> ["Increase and decrease stack size, and ", - "drop off some garbage from time to time."]; +%% Increase and decrease stack size, and +%% drop off some garbage from time to time. grow_stack(Config) when is_list(Config) -> ct:timetrap({minutes, 80}), show_heap("before:"), @@ -91,9 +91,8 @@ grow_stack1(Recs, CurRecs) -> %% Let's see how BEAM handles this one... -grow_stack_heap(doc) -> ["While growing the heap, bounces the size ", - "of the stack, and while reducing the heap", - "bounces the stack usage."]; +%% While growing the heap, bounces the size of the +%% stack, and while reducing the heap, bounces the stack usage. grow_stack_heap(Config) when is_list(Config) -> ct:timetrap({minutes, 40}), grow_stack_heap1(16), diff --git a/erts/emulator/test/guard_SUITE.erl b/erts/emulator/test/guard_SUITE.erl index f43c30e421..77dcc0f607 100644 --- a/erts/emulator/test/guard_SUITE.erl +++ b/erts/emulator/test/guard_SUITE.erl @@ -37,7 +37,7 @@ all() -> type_tests, guard_bif_binary_part]. -bad_arith(doc) -> "Test that a bad arithmetic operation in a guard works correctly."; +%% Test that a bad arithmetic operation in a guard works correctly. bad_arith(Config) when is_list(Config) -> ?line 5 = bad_arith1(2, 3), ?line 10 = bad_arith1(1, infinity), @@ -49,7 +49,7 @@ bad_arith1(T1, T2) when T1+T2 < 10 -> bad_arith1(_, _) -> 10. -bad_tuple(doc) -> "Test that bad arguments to element/2 are handled correctly."; +%% Test that bad arguments to element/2 are handled correctly. bad_tuple(Config) when is_list(Config) -> ?line error = bad_tuple1(a), ?line error = bad_tuple1({a, b}), @@ -64,7 +64,6 @@ bad_tuple1(T) when element(3, T) == y -> bad_tuple1(_) -> error. -test_heap_guards(doc) -> ""; test_heap_guards(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), @@ -149,8 +148,7 @@ mask_error({'EXIT',{Err,_}}) -> mask_error(Else) -> Else. -guard_bif_binary_part(doc) -> - ["Test the binary_part/2,3 guard BIF's extensively"]; +%% Test the binary_part/2,3 guard BIF's extensively guard_bif_binary_part(Config) when is_list(Config) -> %% Overflow tests that need to be unoptimized ?line badarg = @@ -307,7 +305,7 @@ bptest(_,_,_) -> error. -guard_bifs(doc) -> "Test all guard bifs with nasty (but legal arguments)."; +%% Test all guard bifs with nasty (but legal arguments). guard_bifs(Config) when is_list(Config) -> ?line Big = -237849247829874297658726487367328971246284736473821617265433, ?line Float = 387924.874, @@ -437,7 +435,7 @@ guard_bif('node/0', X, Y) when node() == Y -> guard_bif('node/1', X, Y) when node(X) == Y -> {'node/1', X, Y}. -type_tests(doc) -> "Test the type tests."; +%% Test the type tests. type_tests(Config) when is_list(Config) -> ?line Types = all_types(), ?line Tests = type_test_desc(), diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl index 4c50131108..be254f5543 100644 --- a/erts/emulator/test/hash_SUITE.erl +++ b/erts/emulator/test/hash_SUITE.erl @@ -84,48 +84,29 @@ all() -> test_phash2, otp_5292, bit_level_binaries, otp_7127, test_hash_zero]. -test_basic(suite) -> - []; -test_basic(doc) -> - ["Tests basic functionality of erlang:phash and that the " - "hashes has not changed (neither hash nor phash)"]; +%% Tests basic functionality of erlang:phash and that the +%% hashes has not changed (neither hash nor phash) test_basic(Config) when is_list(Config) -> basic_test(). -test_cmp(suite) -> - []; -test_cmp(doc) -> - ["Compares integer hashes made by erlang:phash with those of a reference " - "implementation"]; +%% Compares integer hashes made by erlang:phash with those of a reference implementation test_cmp(Config) when is_list(Config) -> cmp_test(10000). -test_range(suite) -> - []; -test_range(doc) -> - ["Tests ranges on erlang:phash from 1 to 2^32"]; +%% Tests ranges on erlang:phash from 1 to 2^32 test_range(Config) when is_list(Config) -> range_test(). -test_spread(suite) -> - []; -test_spread(doc) -> - ["Tests that the hashes are spread ok"]; +%% Tests that the hashes are spread ok test_spread(Config) when is_list(Config) -> spread_test(10). -test_phash2(suite) -> - []; -test_phash2(doc) -> - ["Tests phash2"]; +%% Tests phash2 test_phash2(Config) when is_list(Config) -> phash2_test(). -otp_5292(suite) -> - []; -otp_5292(doc) -> - ["Tests hash, phash and phash2 regarding integers."]; +%% Tests hash, phash and phash2 regarding integers. otp_5292(Config) when is_list(Config) -> otp_5292_test(). @@ -133,10 +114,7 @@ otp_5292(Config) when is_list(Config) -> bit_level_binaries(Config) when is_list(Config) -> bit_level_binaries_do(). -otp_7127(suite) -> - []; -otp_7127(doc) -> - ["Tests phash2/1."]; +%% Tests phash2/1. otp_7127(Config) when is_list(Config) -> otp_7127_test(). diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl index 177468fdfd..1d3d8a69de 100644 --- a/erts/emulator/test/hibernate_SUITE.erl +++ b/erts/emulator/test/hibernate_SUITE.erl @@ -329,8 +329,6 @@ bar() -> %% No heap %% -no_heap(doc) -> []; -no_heap(suite) -> []; no_heap(Config) when is_list(Config) -> ?line H = spawn_link(fun () -> clean_dict(), no_heap_loop() end), ?line lists:foreach(fun (_) -> @@ -359,8 +357,6 @@ clean_dict() -> %% Wake up and then immediatly bif trap with a lengthy computation. %% -wake_up_and_bif_trap(doc) -> []; -wake_up_and_bif_trap(suite) -> []; wake_up_and_bif_trap(Config) when is_list(Config) -> ?line Self = self(), ?line Pid = spawn_link(fun() -> erlang:hibernate(?MODULE, characters_to_list_trap, [Self]) end), diff --git a/erts/emulator/test/list_bif_SUITE.erl b/erts/emulator/test/list_bif_SUITE.erl index 7410cde99b..40224723d8 100644 --- a/erts/emulator/test/list_bif_SUITE.erl +++ b/erts/emulator/test/list_bif_SUITE.erl @@ -35,10 +35,7 @@ all() -> [hd_test, tl_test, t_length, t_list_to_pid, t_list_to_float, t_list_to_integer]. -t_list_to_integer(suite) -> - []; -t_list_to_integer(doc) -> - ["tests list_to_integer and string:to_integer"]; +%% Tests list_to_integer and string:to_integer t_list_to_integer(Config) when is_list(Config) -> ?line {'EXIT',{badarg,_}} = (catch list_to_integer("12373281903728109372810937209817320981321ABC")), ?line 12373281903728109372810937209817320981321 = (catch list_to_integer("12373281903728109372810937209817320981321")), diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 0e45ebf953..6e1ca726a6 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -62,9 +62,6 @@ all() -> not_run(Config) when is_list(Config) -> {skipped, "Native Code"}. -test_1(doc) -> - [""]; -test_1(suite) -> []; test_1(Config) when is_list(Config) -> ?line tr(fun() -> ?MODULE:f1(a) end, {?MODULE, f1, 1}, @@ -155,9 +152,6 @@ test_1(Config) when is_list(Config) -> ok. -test_2(doc) -> - [""]; -test_2(suite) -> []; test_2(Config) when is_list(Config) -> ?line tr(fun() -> ?MODULE:f2(a, a) end, {?MODULE, f2, 2}, @@ -166,9 +160,7 @@ test_2(Config) when is_list(Config) -> {return_from, {?MODULE, f2, 2}, {a, a}}]), ok. -test_3(doc) -> - ["Test the enable_trace/2 and caller/0 PAM instructions"]; -test_3(suite) -> []; +%% Test the enable_trace/2 and caller/0 PAM instructions test_3(Config) when is_list(Config) -> ?line Fun1 = fun() -> register(fnoppelklopfer,self()), @@ -189,7 +181,6 @@ test_3(Config) when is_list(Config) -> ?line collect(P1, [{trace, P1, call, {?MODULE, f2, [a, b]}, [true]}]), ?line ok. -otp_9422(doc) -> []; otp_9422(Config) when is_list(Config) -> Laps = 10000, ?line Fun1 = fun() -> otp_9422_tracee() end, @@ -235,11 +226,9 @@ bad_match_spec_bin(Config) when is_list(Config) -> -trace_control_word(doc) -> - ["Test the erlang:system_info(trace_control_word) and ", - "erlang:system_flag(trace_control_word, Value) BIFs, ", - "as well as the get_tcw/0 and set_tcw/1 PAM instructions"]; -trace_control_word(suite) -> []; +%% Test the erlang:system_info(trace_control_word) and +%% erlang:system_flag(trace_control_word, Value) BIFs, +%% as well as the get_tcw/0 and set_tcw/1 PAM instructions trace_control_word(Config) when is_list(Config) -> ?line 32 = Bits = tcw_bits(), ?line High = 1 bsl (Bits - 1), @@ -305,11 +294,8 @@ tcw_bits(Save, Prev, Bits) -> end. - -silent(doc) -> - ["Test the erlang:trace(_, _, [silent]) flag ", - "as well as the silent/0 PAM instruction"]; -silent(suite) -> []; +%% Test the erlang:trace(_, _, [silent]) flag +%% as well as the silent/0 PAM instruction silent(Config) when is_list(Config) -> %% Global call trace ?line tr(fun() -> @@ -371,9 +357,7 @@ silent(Config) when is_list(Config) -> {call, {?MODULE, f1, [d]}, d} ]), ok. -silent_no_ms(doc) -> - ["Test the erlang:trace(_, _, [silent]) flag without match specs"]; -silent_no_ms(suite) -> []; +%% Test the erlang:trace(_, _, [silent]) flag without match specs silent_no_ms(Config) when is_list(Config) -> %% Global call trace %% @@ -479,17 +463,14 @@ silent_no_ms(Config) when is_list(Config) -> {trace,Tracee,return_to,{?MODULE,f3,2}}] end). -silent_test(doc) -> - ["Test that match_spec_test does not activate silent"]; +%% Test that match_spec_test does not activate silent silent_test(_Config) -> {flags,[]} = erlang:trace_info(self(),flags), erlang:match_spec_test([],[{'_',[],[{silent,true}]}],trace), {flags,[]} = erlang:trace_info(self(),flags). -ms_trace2(doc) -> - ["Test the match spec functions {trace/2}"]; -ms_trace2(suite) -> []; +%% Test the match spec functions {trace/2} ms_trace2(Config) when is_list(Config) -> Tracer = self(), %% Meta trace init @@ -563,9 +544,7 @@ ms_trace2(Config) when is_list(Config) -> -ms_trace3(doc) -> - ["Test the match spec functions {trace/3}"]; -ms_trace3(suite) -> []; +%% Test the match spec functions {trace/3} ms_trace3(Config) when is_list(Config) -> TraceeName = 'match_spec_SUITE:ms_trace3', Tracer = self(), @@ -673,9 +652,7 @@ ms_trace3(Config) when is_list(Config) -> -destructive_in_test_bif(doc) -> - ["Test that destructive operations in test bif does not really happen"]; -destructive_in_test_bif(suite) -> []; +%% Test that destructive operations in test bif does not really happen destructive_in_test_bif(Config) when is_list(Config) -> ?line {ok,OldToken,_,_} = erlang:match_spec_test ([], @@ -697,9 +674,7 @@ destructive_in_test_bif(Config) when is_list(Config) -> ([],[{'_',[],[{message,{get_tcw}}]}],trace), ok. -boxed_and_small(doc) -> - ["Test that the comparision between boxed and small does not crash emulator"]; -boxed_and_small(suite) -> []; +%% Test that the comparision between boxed and small does not crash emulator boxed_and_small(Config) when is_list(Config) -> ?line {ok, Node} = start_node(match_spec_suite_other), ?line ok = rpc:call(Node,?MODULE,do_boxed_and_small,[]), @@ -713,9 +688,7 @@ do_boxed_and_small() -> {ok, false, _, _} = erlang:match_spec_test({0,3},[{{make_ref(),'_'},[],['$_']}],table), ok. -faulty_seq_trace(doc) -> - ["Test that faulty seq_trace_call does not crash emulator"]; -faulty_seq_trace(suite) -> []; +%% Test that faulty seq_trace_call does not crash emulator faulty_seq_trace(Config) when is_list(Config) -> ?line {ok, Node} = start_node(match_spec_suite_other), ?line ok = rpc:call(Node,?MODULE,do_faulty_seq_trace,[]), @@ -734,10 +707,7 @@ errchk(Pat) -> ct:fail({noerror, Other}) end. -unary_minus(suite) -> - []; -unary_minus(doc) -> - ["Checks that unary minus works"]; +%% Checks that unary minus works unary_minus(Config) when is_list(Config) -> ?line {ok,true,[],[]} = erlang:match_spec_test (5, @@ -764,10 +734,8 @@ unary_minus(Config) when is_list(Config) -> [true]}], table), ok. -unary_plus(suite) -> - []; -unary_plus(doc) -> - ["Checks that unary plus works"]; + +%% Checks that unary plus works unary_plus(Config) when is_list(Config) -> ?line {ok,true,[],[]} = erlang:match_spec_test (5, @@ -798,10 +766,7 @@ unary_plus(Config) when is_list(Config) -> -guard_exceptions(suite) -> - []; -guard_exceptions(doc) -> - ["Checks that exceptions in guards are handled correctly"]; +%% Checks that exceptions in guards are handled correctly guard_exceptions(Config) when is_list(Config) -> ?line {ok,false,[],[]} = erlang:match_spec_test (5, @@ -862,10 +827,7 @@ guard_exceptions(Config) when is_list(Config) -> ok. -fpe(suite) -> - []; -fpe(doc) -> - ["Checks floating point exceptions in match-specs"]; +%% Checks floating point exceptions in match-specs fpe(Config) when is_list(Config) -> MS = [{{'$1'},[],[{'/','$1',0}]}], case catch (['EXIT','EXIT'] = @@ -874,6 +836,7 @@ fpe(Config) when is_list(Config) -> _ -> ok end. +%% Test maps in match-specs maps(Config) when is_list(Config) -> {ok,#{},[],[]} = erlang:match_spec_test(#{}, [{'_',[],['$_']}], table), {ok,#{},[],[]} = erlang:match_spec_test(#{}, [{#{},[],['$_']}], table), diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index d37b76ec20..5a866e7bfa 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -46,17 +46,14 @@ groups() -> [{remove_monitor, [], [local_remove_monitor, remote_remove_monitor]}]. -case_1(doc) -> - "A monitors B, B kills A and then exits (yielded core dump)"; -case_1(suite) -> []; +%% A monitors B, B kills A and then exits (yielded core dump) case_1(Config) when is_list(Config) -> ?line process_flag(trap_exit, true), ?line spawn_link(?MODULE, g0, []), ?line receive _ -> ok end, ok. -case_1a(doc) -> - "A monitors B, B kills A and then exits (yielded core dump)"; +%% A monitors B, B kills A and then exits (yielded core dump) case_1a(Config) when is_list(Config) -> ?line process_flag(trap_exit, true), ?line spawn_link(?MODULE, g1, []), @@ -82,8 +79,7 @@ g(Parent) -> ?line ok. -case_2(doc) -> - "A monitors B, B demonitors A (yielded core dump)"; +%% A monitors B, B demonitors A (yielded core dump) case_2(Config) when is_list(Config) -> ?line B = spawn(?MODULE, y2, [self()]), ?line R = erlang:monitor(process, B), @@ -96,8 +92,7 @@ case_2(Config) when is_list(Config) -> ?line expect_down(R, B, normal), ok. -case_2a(doc) -> - "A monitors B, B demonitors A (yielded core dump)"; +%% A monitors B, B demonitors A (yielded core dump) case_2a(Config) when is_list(Config) -> ?line {B,R} = spawn_monitor(?MODULE, y2, [self()]), ?line B ! R, @@ -140,9 +135,6 @@ expect_no_msg() -> %%% Error cases for monitor/2 -mon_e_1(doc) -> - "Error cases for monitor/2"; -mon_e_1(suite) -> []; mon_e_1(Config) when is_list(Config) -> ?line {ok, N} = test_server:start_node(hej, slave, []), ?line mon_error(plutt, self()), @@ -171,9 +163,6 @@ mon_error(Type, Item) -> %%% Error cases for demonitor/1 -demon_e_1(doc) -> - "Error cases for demonitor/1"; -demon_e_1(suite) -> []; demon_e_1(Config) when is_list(Config) -> ?line {ok, N} = test_server:start_node(hej, slave, []), ?line demon_error(plutt, badarg), @@ -211,9 +200,6 @@ demon_error(Ref, Reason) -> %%% No-op cases for demonitor/1 -demon_1(doc) -> - "demonitor/1"; -demon_1(suite) -> []; demon_1(Config) when is_list(Config) -> ?line true = erlang:demonitor(make_ref()), ok. @@ -221,9 +207,6 @@ demon_1(Config) when is_list(Config) -> %%% Cases for demonitor/1 -demon_2(doc) -> - "Cases for demonitor/1"; -demon_2(suite) -> []; demon_2(Config) when is_list(Config) -> ?line R1 = erlang:monitor(process, self()), ?line true = erlang:demonitor(R1), @@ -257,9 +240,7 @@ demon_2(Config) when is_list(Config) -> ok. -demon_3(doc) -> - "Distributed case for demonitor/1 (OTP-3499)"; -demon_3(suite) -> []; +%% Distributed case for demonitor/1 (OTP-3499) demon_3(Config) when is_list(Config) -> ?line {ok, N} = test_server:start_node(hej, slave, []), @@ -281,8 +262,6 @@ demon_3(Config) when is_list(Config) -> ok. -demonitor_flush(suite) -> []; -demonitor_flush(doc) -> []; demonitor_flush(Config) when is_list(Config) -> ?line {'EXIT', {badarg, _}} = (catch erlang:demonitor(make_ref(), flush)), ?line {'EXIT', {badarg, _}} = (catch erlang:demonitor(make_ref(), [flus])), @@ -385,9 +364,6 @@ start_remove_monitor_group(Node) -> %%% Cases for monitor/2 -mon_1(doc) -> - "Cases for monitor/2"; -mon_1(suite) -> []; mon_1(Config) when is_list(Config) -> %% Normal case ?line P2 = spawn(timer, sleep, [1]), @@ -431,9 +407,7 @@ mon_1(Config) when is_list(Config) -> ok. -mon_2(doc) -> - "Distributed cases for monitor/2"; -mon_2(suite) -> []; +%% Distributed cases for monitor/2 mon_2(Config) when is_list(Config) -> ?line {ok, N1} = test_server:start_node(hej1, slave, []), @@ -503,9 +477,6 @@ mon_2(Config) when is_list(Config) -> %%% Large exit reason. Crashed first attempt to release R5B. -large_exit(doc) -> - "Large exit reason"; -large_exit(suite) -> []; large_exit(Config) when is_list(Config) -> ?line f(100), ok. @@ -544,10 +515,6 @@ large_exit_sub(S) -> %%% by using erlang:process_info(self(), monitors) %%% and erlang:process_info(self(), monitored_by) -list_cleanup(doc) -> - "Testing of monitor link list cleanup by using " ++ - "erlang:process_info/2"; -list_cleanup(suite) -> []; list_cleanup(Config) when is_list(Config) -> ?line P0 = self(), ?line M = node(), @@ -641,8 +608,6 @@ list_cleanup(Config) when is_list(Config) -> %%% Mixed internal and external monitors -mixer(doc) -> - "Test mixing of internal and external monitors."; mixer(Config) when is_list(Config) -> PA = filename:dirname(code:which(?MODULE)), NN = [j0,j1,j2], @@ -726,9 +691,8 @@ mixer(Config) when is_list(Config) -> [test_server:stop_node(K) || K <- NL0], ok. -named_down(doc) -> ["Test that DOWN message for a named monitor isn't" - " delivered until name has been unregistered"]; -named_down(suite) -> []; +%% Test that DOWN message for a named monitor isn't +%% delivered until name has been unregistered named_down(Config) when is_list(Config) -> ?line Name = list_to_atom(atom_to_list(?MODULE) ++ "-named_down-" @@ -772,8 +736,6 @@ named_down(Config) when is_list(Config) -> ?line process_flag(priority,Prio), ok. -otp_5827(doc) -> []; -otp_5827(suite) -> []; otp_5827(Config) when is_list(Config) -> %% Make a pid with the same nodename but with another creation ?line [CreEnd | RPTail] diff --git a/erts/emulator/test/nested_SUITE.erl b/erts/emulator/test/nested_SUITE.erl index f9a913ecc8..1e7dce4352 100644 --- a/erts/emulator/test/nested_SUITE.erl +++ b/erts/emulator/test/nested_SUITE.erl @@ -68,8 +68,7 @@ case_in_after(Config) when is_list(Config) -> end, ok. -catch_in_catch(doc) -> "Test a catch within a catch in the same function."; -catch_in_catch(suite) -> []; +%% Test a catch within a catch in the same function. catch_in_catch(Config) when is_list(Config) -> ?line {outer, inner_exit} = catcher(), ok. @@ -88,8 +87,7 @@ catcher() -> {outer, Res2} end. -bif_in_bif(doc) -> "Test a BIF call within a BIF call."; -bif_in_bif(suite) -> []; +%% Test a BIF call within a BIF call. bif_in_bif(Config) when is_list(Config) -> Self = self(), put(pid, Self), diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 6a309b8b7f..c5921ca859 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -85,8 +85,7 @@ end_per_testcase(_Func, _Config) -> P2 = code:purge(nif_mod), io:format("fin purged=~p, deleted=~p and then purged=~p\n",[P1,Del,P2]). -basic(doc) -> ["Basic smoke test of load_nif and a simple NIF call"]; -basic(suite) -> []; +%% Basic smoke test of load_nif and a simple NIF call basic(Config) when is_list(Config) -> ensure_lib_loaded(Config), ?line true = (lib_version() =/= undefined), @@ -95,8 +94,7 @@ basic(Config) when is_list(Config) -> ?line true = lists:member(?MODULE, erlang:system_info(taints)), ok. -reload(doc) -> ["Test reload callback in nif lib"]; -reload(suite) -> []; +%% Test reload callback in nif lib reload(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), @@ -131,8 +129,7 @@ reload(Config) when is_list(Config) -> ?line verify_tmpmem(TmpMem), ok. -upgrade(doc) -> ["Test upgrade callback in nif lib"]; -upgrade(suite) -> []; +%% Test upgrade callback in nif lib upgrade(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), @@ -222,8 +219,7 @@ upgrade(Config) when is_list(Config) -> ?line verify_tmpmem(TmpMem), ok. -heap_frag(doc) -> ["Test NIF building heap fragments"]; -heap_frag(suite) -> []; +%% Test NIF building heap fragments heap_frag(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), @@ -240,8 +236,7 @@ heap_frag_do(N, Max) -> L = list_seq(N), heap_frag_do(((N*5) div 4) + 1, Max). -types(doc) -> ["Type tests"]; -types(suite) -> []; +%% Type tests types(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), @@ -325,8 +320,7 @@ eq_cmp_do(A,B) -> ok. -many_args(doc) -> ["Test NIF with many arguments"]; -many_args(suite) -> []; +%% Test NIF with many arguments many_args(Config) when is_list(Config) -> TmpMem = tmpmem(), ?line ensure_lib_loaded(Config ,1), @@ -335,8 +329,7 @@ many_args(Config) when is_list(Config) -> ?line verify_tmpmem(TmpMem), ok. -binaries(doc) -> ["Test NIF binary handling."]; -binaries(suite) -> []; +%% Test NIF binary handling. binaries(Config) when is_list(Config) -> TmpMem = tmpmem(), ?line ensure_lib_loaded(Config, 1), @@ -398,8 +391,7 @@ test_make_sub_bin(Bin) -> ?line <<>> = make_sub_bin(Bin, Size, 0), ok. -get_string(doc) -> ["Test enif_get_string"]; -get_string(suite) -> []; +%% Test enif_get_string get_string(Config) when is_list(Config) -> ?line ensure_lib_loaded(Config, 1), ?line {7, <<"hejsan",0,_:3/binary>>} = string_to_bin("hejsan",10), @@ -413,8 +405,7 @@ get_string(Config) when is_list(Config) -> ?line {0, <<>>} = string_to_bin("",0), ok. -get_atom(doc) -> ["Test enif_get_atom"]; -get_atom(suite) -> []; +%% Test enif_get_atom get_atom(Config) when is_list(Config) -> ?line ensure_lib_loaded(Config, 1), ?line {7, <<"hejsan",0,_:3/binary>>} = atom_to_bin(hejsan,10), @@ -426,8 +417,7 @@ get_atom(Config) when is_list(Config) -> ?line {0, <<>>} = atom_to_bin('',0), ok. -maps(doc) -> ["Test NIF maps handling."]; -maps(suite) -> []; +%% Test NIF maps handling. maps(Config) when is_list(Config) -> TmpMem = tmpmem(), Pairs = [{adam, "bert"}] ++ @@ -474,8 +464,7 @@ maps(Config) when is_list(Config) -> ok. -api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"]; -api_macros(suite) -> []; +%% Test macros enif_make_list and enif_make_tuple api_macros(Config) when is_list(Config) -> ?line ensure_lib_loaded(Config, 1), Expected = {[lists:seq(1,N) || N <- lists:seq(1,9)], @@ -484,8 +473,7 @@ api_macros(Config) when is_list(Config) -> ?line Expected = macros(list_to_tuple(lists:seq(1,9))), ok. -from_array(doc) -> ["enif_make_[tuple|list]_from_array"]; -from_array(suite) -> []; +%% enif_make_[tuple|list]_from_array from_array(Config) when is_list(Config) -> ?line ensure_lib_loaded(Config, 1), lists:foreach(fun(Tpl) -> @@ -495,8 +483,7 @@ from_array(Config) when is_list(Config) -> [{}, {1,2,3}, {[4,5],[],{},{6,7}}, {{}}, {[]}]), ok. -iolist_as_binary(doc) -> ["enif_inspect_iolist_as_binary"]; -iolist_as_binary(suite) -> []; +%% enif_inspect_iolist_as_binary iolist_as_binary(Config) when is_list(Config) -> ?line ensure_lib_loaded(Config, 1), TmpMem = tmpmem(), @@ -514,8 +501,7 @@ iolist_as_binary(Config) when is_list(Config) -> ?line verify_tmpmem(TmpMem), ok. -resource(doc) -> ["Test memory managed objects, aka 'resources'"]; -resource(suite) -> []; +%% Test memory managed objects, aka 'resources' resource(Config) when is_list(Config) -> ?line ensure_lib_loaded(Config, 1), ?line Type = get_resource_type(0), @@ -616,8 +602,7 @@ resource_neg_do(TypeA) -> ?line {'EXIT',{badarg,_}} = (catch get_resource(TypeB, ResA)), ok. -resource_binary(doc) -> ["Test enif_make_resource_binary"]; -resource_binary(suite) -> []; +%% Test enif_make_resource_binary resource_binary(Config) when is_list(Config) -> ?line ensure_lib_loaded(Config, 1), ?line {Ptr,Bin} = resource_binary_do(), @@ -650,8 +635,7 @@ resource_binary_do() -> -define(RT_CREATE,1). -define(RT_TAKEOVER,2). -resource_takeover(doc) -> ["Test resource takeover by module reload and upgrade"]; -resource_takeover(suite) -> []; +%% Test resource takeover by module reload and upgrade resource_takeover(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), @@ -1028,7 +1012,7 @@ resource_holder(Pid,Reply,List) -> resource_holder(List). -threading(doc) -> ["Test the threading API functions (reuse tests from driver API)"]; +%% Test the threading API functions (reuse tests from driver API) threading(Config) when is_list(Config) -> case erlang:system_info(threads) of true -> threading_do(Config); @@ -1050,7 +1034,7 @@ threading_do(Config) -> ?line ok = tester:load_nif_lib(Config, "tsd"), ?line ok = tester:run(). -send(doc) -> ["Test NIF message sending"]; +%% Test NIF message sending send(Config) when is_list(Config) -> ensure_lib_loaded(Config), @@ -1075,14 +1059,14 @@ send(Config) when is_list(Config) -> {ok,0} = send_list_seq(7, DeadPid), ok. -send2(doc) -> ["More NIF message sending"]; +%% More NIF message sending send2(Config) when is_list(Config) -> ensure_lib_loaded(Config), send2_do1(fun send_blob_dbg/2), ok. -send_threaded(doc) -> ["Send msg from user thread"]; +%% Send msg from user thread send_threaded(Config) when is_list(Config) -> case erlang:system_info(smp_support) of true -> @@ -1168,7 +1152,7 @@ forwarder(To, N) -> other_term() -> {fun(X,Y) -> X*Y end, make_ref()}. -send3(doc) -> ["Message sending stress test"]; +%% Message sending stress test send3(Config) when is_list(Config) -> %% Let a number of processes send random message blobs between each other %% using enif_send. Kill and spawn new ones randomly to keep a ~constant @@ -1324,7 +1308,7 @@ send3_new_state(State, Blob) -> _ -> State % Don't store blob end. -neg(doc) -> ["Negative testing of load_nif"]; +%% Negative testing of load_nif neg(Config) when is_list(Config) -> TmpMem = tmpmem(), ?line {'EXIT',{badarg,_}} = (catch erlang:load_nif(badarg, 0)), @@ -1339,7 +1323,7 @@ neg(Config) when is_list(Config) -> ?line verify_tmpmem(TmpMem), ?line ok. -is_checks(doc) -> ["Test all enif_is functions"]; +%% Test all enif_is functions is_checks(Config) when is_list(Config) -> ?line ensure_lib_loaded(Config, 1), ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, @@ -1374,7 +1358,7 @@ is_checks(Config) when is_list(Config) -> ?line ok end. -get_length(doc) -> ["Test all enif_get_length functions"]; +%% Test all enif_get_length functions get_length(Config) when is_list(Config) -> ?line ensure_lib_loaded(Config, 1), ?line ok = length_test(hejsan, "hejsan", [], [], not_a_list, [1,2|3]). @@ -1415,7 +1399,7 @@ reverse_list_test(Config) -> ?line RevList = reverse_list(List), ?line badarg = reverse_list(foo). -otp_9668(doc) -> ["Memory leak of tmp-buffer when inspecting iolist or unaligned binary in unbound environment"]; +%% Memory leak of tmp-buffer when inspecting iolist or unaligned binary in unbound environment otp_9668(Config) -> ensure_lib_loaded(Config, 1), TmpMem = tmpmem(), @@ -1428,7 +1412,7 @@ otp_9668(Config) -> ?line verify_tmpmem(TmpMem), ok. -otp_9828(doc) -> ["Copy of writable binary"]; +%% Copy of writable binary otp_9828(Config) -> ensure_lib_loaded(Config, 1), diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 191dc09670..9879e004d2 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -105,10 +105,8 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %% %% Test case: term_to_binary_to_term_eq %% -term_to_binary_to_term_eq(doc) -> - ["Tests that node container terms that are converted to external format " - "and back stay equal to themselves."]; -term_to_binary_to_term_eq(suite) -> []; +%% Tests that node container terms that are converted to external format +%% and back stay equal to themselves. term_to_binary_to_term_eq(Config) when is_list(Config) -> ?line ThisNode = {node(), erlang:system_info(creation)}, % Get local node containers @@ -151,10 +149,7 @@ term_to_binary_to_term_eq(Config) when is_list(Config) -> %% %% Test case: round_trip_eq %% -round_trip_eq(doc) -> - ["Tests that node containers that are sent beteen nodes stay equal to " - "themselves."]; -round_trip_eq(suite) -> []; +%% Tests that node containers that are sent beteen nodes stay equal to themselves. round_trip_eq(Config) when is_list(Config) -> ?line ThisNode = {node(), erlang:system_info(creation)}, ?line NodeFirstName = get_nodefirstname(), @@ -206,10 +201,7 @@ round_trip_eq(Config) when is_list(Config) -> %% %% Test case: cmp %% -cmp(doc) -> - ["Tests that Erlang term comparison works as it should on node " - "containers."]; -cmp(suite) -> []; +%% Tests that Erlang term comparison works as it should on node containers. cmp(Config) when is_list(Config) -> %% Inter type comparison --------------------------------------------------- @@ -373,8 +365,7 @@ cmp(Config) when is_list(Config) -> %% %% Test case: ref_eq %% -ref_eq(doc) -> ["Test that one word refs \"works\"."]; -ref_eq(suite) -> []; +%% Test that one word refs works ref_eq(Config) when is_list(Config) -> ?line ThisNode = {node(), erlang:system_info(creation)}, ?line AnotherNode = {get_nodename(),2}, @@ -404,9 +395,7 @@ ref_eq(Config) when is_list(Config) -> %% %% Test case: node_table_gc %% -node_table_gc(doc) -> - ["Tests that node tables are garbage collected."]; -node_table_gc(suite) -> []; +%% Tests that node tables are garbage collected. node_table_gc(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), erts_debug:set_internal_state(node_tab_delayed_delete, 0), @@ -474,10 +463,8 @@ make_faked_pid_list(Start, No, Creation, Acc) -> %% %% Test case: dist_link_refc %% -dist_link_refc(doc) -> - ["Tests that external reference counts are incremented and decremented " - "as they should for distributed links"]; -dist_link_refc(suite) -> []; +%% Tests that external reference counts are incremented and decremented +%% as they should for distributed links dist_link_refc(Config) when is_list(Config) -> ?line NodeFirstName = get_nodefirstname(), ?line ?line {ok, Node} = start_node(NodeFirstName), @@ -511,10 +498,8 @@ dist_link_refc(Config) when is_list(Config) -> %% %% Test case: dist_monitor_refc %% -dist_monitor_refc(doc) -> - ["Tests that external reference counts are incremented and decremented " - "as they should for distributed monitors"]; -dist_monitor_refc(suite) -> []; +%% Tests that external reference counts are incremented and decremented +%% as they should for distributed monitors dist_monitor_refc(Config) when is_list(Config) -> ?line NodeFirstName = get_nodefirstname(), ?line {ok, Node} = start_node(NodeFirstName), @@ -566,10 +551,8 @@ dist_monitor_refc(Config) when is_list(Config) -> %% %% Test case: node_controller_refc %% -node_controller_refc(doc) -> - ["Tests that external reference counts are incremented and decremented " - "as they should for entities controlling a connections."]; -node_controller_refc(suite) -> []; +%% Tests that external reference counts are incremented and decremented +%% as they should for entities controlling a connections. node_controller_refc(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), erts_debug:set_internal_state(node_tab_delayed_delete, 0), @@ -606,10 +589,8 @@ node_controller_refc(Config) when is_list(Config) -> %% %% Test case: ets_refc %% -ets_refc(doc) -> - ["Tests that external reference counts are incremented and decremented " - "as they should for data stored in ets tables."]; -ets_refc(suite) -> []; +%% Tests that external reference counts are incremented and decremented +%% as they should for data stored in ets tables. ets_refc(Config) when is_list(Config) -> ?line RNode = {get_nodename(), 1}, ?line RPid = mk_pid(RNode, 4711, 2), @@ -639,10 +620,8 @@ ets_refc(Config) when is_list(Config) -> %% %% Test case: match_spec_refc %% -match_spec_refc(doc) -> - ["Tests that external reference counts are incremented and decremented " - "as they should for data stored in match specifications."]; -match_spec_refc(suite) -> []; +%% Tests that external reference counts are incremented and decremented +%% as they should for data stored in match specifications. match_spec_refc(Config) when is_list(Config) -> ?line RNode = {get_nodename(), 1}, ?line RPid = mk_pid(RNode, 4711, 2), @@ -676,10 +655,8 @@ do_match_spec_test(RNode, RPid, RPort, RRef) -> %% %% Test case: ets_refc %% -timer_refc(doc) -> - ["Tests that external reference counts are incremented and decremented " - "as they should for data stored in bif timers."]; -timer_refc(suite) -> []; +%% Tests that external reference counts are incremented and decremented +%% as they should for data stored in bif timers. timer_refc(Config) when is_list(Config) -> ?line RNode = {get_nodename(), 1}, ?line RPid = mk_pid(RNode, 4711, 2), @@ -703,8 +680,6 @@ timer_refc(Config) when is_list(Config) -> ?line nc_refc_check(node()), ?line ok. -otp_4715(doc) -> []; -otp_4715(suite) -> []; otp_4715(Config) when is_list(Config) -> case test_server:is_release_available("r9b") of true -> otp_4715_1(Config); @@ -734,12 +709,8 @@ run_otp_4715(Config) when is_list(Config) -> ?line R9Sorted = old_mod:sort_on_old_node(PidList), ?line R9Sorted = lists:sort(PidList). -pid_wrap(doc) -> []; -pid_wrap(suite) -> []; pid_wrap(Config) when is_list(Config) -> ?line pp_wrap(pid). -port_wrap(doc) -> []; -port_wrap(suite) -> []; port_wrap(Config) when is_list(Config) -> ?line case test_server:os_type() of {unix, _} -> @@ -810,8 +781,6 @@ do_pp_creations(port, N) when is_integer(N) -> ?line "hej" = os:cmd("echo hej") -- "\n", ?line do_pp_creations(port, N - 1). -bad_nc(doc) -> []; -bad_nc(suite) -> []; bad_nc(Config) when is_list(Config) -> % Make sure emulator don't crash on bad node containers... ?line MaxPidNum = (1 bsl 15) - 1, @@ -851,8 +820,6 @@ bad_nc(Config) when is_list(Config) -> -define(NO_PIDS, 1000000). -unique_pid(doc) -> []; -unique_pid(suite) -> []; unique_pid(Config) when is_list(Config) -> case catch erlang:system_info(modified_timing_level) of Level when is_integer(Level) -> @@ -869,8 +836,6 @@ mkpidlist(0, Ps) -> Ps; mkpidlist(N, Ps) -> mkpidlist(N-1, [spawn(fun () -> ok end)|Ps]). -iter_max_procs(doc) -> []; -iter_max_procs(suite) -> []; iter_max_procs(Config) when is_list(Config) -> ?line NoMoreTests = make_ref(), ?line erlang:send_after(10000, self(), NoMoreTests), diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl index 612487cd35..99c143e2d0 100644 --- a/erts/emulator/test/old_scheduler_SUITE.erl +++ b/erts/emulator/test/old_scheduler_SUITE.erl @@ -84,8 +84,6 @@ ok(Config) when is_list(Config) -> %% Run equal number of low and normal prio processes. -equal(suite) -> []; -equal(doc) -> []; equal(Config) when is_list(Config) -> ?line Self = self(), @@ -125,8 +123,6 @@ equal(Config) when is_list(Config) -> %% Run many low and few normal prio processes. -many_low(suite) -> []; -many_low(doc) -> []; many_low(Config) when is_list(Config) -> ?line Self = self(), Normal = {normal,1}, @@ -156,8 +152,6 @@ many_low(Config) when is_list(Config) -> %% Run few low and many normal prio processes. -few_low(suite) -> []; -few_low(doc) -> []; few_low(Config) when is_list(Config) -> ?line Self = self(), Normal = {normal,1000}, @@ -188,8 +182,6 @@ few_low(Config) when is_list(Config) -> %% Run max prio processes and verify they get at least as much %% runtime as high, normal and low. -max(suite) -> []; -max(doc) -> []; max(Config) when is_list(Config) -> max = process_flag(priority, max), % should already be max (init_per_tc) ?line Self = self(), @@ -259,8 +251,6 @@ max(Config) when is_list(Config) -> %% Run high prio processes and verify they get at least as much %% runtime as normal and low. -high(suite) -> []; -high(doc) -> []; high(Config) when is_list(Config) -> max = process_flag(priority, max), % should already be max (init_per_tc) ?line Self = self(), diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl index 70e0c57827..e9127616a6 100644 --- a/erts/emulator/test/op_SUITE.erl +++ b/erts/emulator/test/op_SUITE.erl @@ -43,7 +43,7 @@ bsl_bsr(Config) when is_list(Config) -> ?line run_test_module(Cases, false), {comment,integer_to_list(length(Cases)) ++ " cases"}. -logical(doc) -> "Test the logical operators and internal BIFs."; +%% Test the logical operators and internal BIFs. logical(Config) when is_list(Config) -> Vs0 = [true,false,bad], Vs = [unvalue(V) || V <- Vs0], @@ -51,13 +51,13 @@ logical(Config) when is_list(Config) -> ?line run_test_module(Cases, false), {comment,integer_to_list(length(Cases)) ++ " cases"}. -t_not(doc) -> "Test the not operator and internal BIFs."; +%% Test the not operator and internal BIFs. t_not(Config) when is_list(Config) -> ?line Cases = [{'not',unvalue(V)} || V <- [true,false,42,bad]], ?line run_test_module(Cases, false), {comment,integer_to_list(length(Cases)) ++ " cases"}. -relop_simple(doc) -> "Test that simlpe relations between relation operators hold."; +%% Test that simlpe relations between relation operators hold. relop_simple(Config) when is_list(Config) -> Big1 = 19738924729729787487784874, Big2 = 38374938373887374983978484, @@ -207,7 +207,7 @@ purify_list([H|T], Acc) -> purify_list(T, [H|Acc]); purify_list(Other, Acc) -> [Other|Acc]. -relop(doc) -> "Test the relational operators and internal BIFs on literals."; +%% Test the relational operators and internal BIFs on literals. relop(Config) when is_list(Config) -> Big1 = -38374938373887374983978484, Big2 = 19738924729729787487784874, @@ -218,8 +218,7 @@ relop(Config) when is_list(Config) -> Ops = ['==', '/=', '=:=', '=/=', '<', '=<', '>', '>='], ?line binop(Ops, Vs). -complex_relop(doc) -> - "Test the relational operators and internal BIFs on lists and tuples."; +%% Test the relational operators and internal BIFs on lists and tuples. complex_relop(Config) when is_list(Config) -> Big = 99678557475484872464269855544643333, Float = float(Big), diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 563b311f5f..7d881a483f 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -243,9 +243,8 @@ slow_writes(Config) when is_list(Config) -> ping(Config, [10], 2, "-s2", []), ok. -bad_packet(doc) -> - ["Test that we get {'EXIT', Port, einval} if we try to send a bigger " - "packet than the packet header allows."]; +%% Test that we get {'EXIT', Port, einval} if we try to send a bigger +%% packet than the packet header allows. bad_packet(Config) when is_list(Config) -> PortTest = port_test(Config), process_flag(trap_exit, true), @@ -454,7 +453,6 @@ wait_for(Pids) -> %% Tests starting port programs that terminate by themselves. %% This used to cause problems on Windows. -dying_port(suite) -> []; dying_port(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), process_flag(trap_exit, true), @@ -501,7 +499,6 @@ make_dying_port(Config) when is_list(Config) -> %% %% This testcase works on Unix, but is not very useful. -port_program_with_path(suite) -> []; port_program_with_path(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), DataDir = proplists:get_value(data_dir, Config), @@ -543,7 +540,6 @@ port_program_with_path(Config) when is_list(Config) -> %% Tests that files can be read using open_port(Filename, [in]). %% This used to fail on Windows. -open_input_file_port(suite) -> []; open_input_file_port(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), @@ -563,7 +559,6 @@ open_input_file_port(Config) when is_list(Config) -> 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) -> ct:timetrap({minutes, 2}), PrivDir = proplists:get_value(priv_dir, Config), @@ -584,7 +579,6 @@ open_output_file_port(Config) when is_list(Config) -> ok. %% Tests that all appropriate fd's have been closed in the port program -count_fds(suite) -> []; count_fds(Config) when is_list(Config) -> case os:type() of {unix, _} -> @@ -620,7 +614,6 @@ count_fds(Config) when is_list(Config) -> %% that we get the same number of ports every time. %% -iter_max_ports(suite) -> []; iter_max_ports(Config) when is_list(Config) -> %% The child_setup program might dump core if we get out of memory. %% This is hard to do anything about and is harmless. We run this test @@ -716,7 +709,6 @@ open_ports(Name, Settings) -> %% Tests that exit(Port, Term) works (has been known to crash the emulator). -t_exit(suite) -> []; t_exit(Config) when is_list(Config) -> process_flag(trap_exit, true), Pid = fun_spawn(fun suicide_port/1, [Config]), @@ -733,13 +725,9 @@ suicide_port(Config) when is_list(Config) -> receive after infinity -> ok end. -tps_16_bytes(doc) -> ""; -tps_16_bytes(suite) -> []; tps_16_bytes(Config) when is_list(Config) -> tps(16, Config). -tps_1K(doc) -> ""; -tps_1K(suite) -> []; tps_1K(Config) when is_list(Config) -> tps(1024, Config). @@ -807,11 +795,7 @@ line(Config) when is_list(Config) -> bad_argument(Config, [{packet, 5}, {line, 5}]), ok. -%%% Redirection of stderr test -stderr_to_stdout(suite) -> - []; -stderr_to_stdout(doc) -> - "Test that redirection of standard error to standard output works."; +%% Test that redirection of standard error to standard output works. stderr_to_stdout(Config) when is_list(Config) -> ct:timetrap({minutes, 1}), %% See that it works @@ -835,10 +819,8 @@ bad_argument(Config, ArgList) -> %% 'env' option %% (Can perhaps be made smaller by calling the other utility functions %% in this module.) -env(suite) -> - []; -env(doc) -> - ["Test that the 'env' option works"]; +%% +%% Test that the 'env' option works env(Config) when is_list(Config) -> ct:timetrap({minutes, 1}), Priv = proplists:get_value(priv_dir, Config), @@ -967,10 +949,8 @@ huge_env(Config) when is_list(Config) -> %% 'cd' option %% (Can perhaps be made smaller by calling the other utility functions %% in this module.) -cd(suite) -> - []; -cd(doc) -> - ["Test that the 'cd' option works"]; +%% +%% Test that the 'cd' option works cd(Config) when is_list(Config) -> ct:timetrap({minutes, 1}), @@ -1036,11 +1016,8 @@ tolower(C) when C >= $A, C =< $Z -> tolower(C) -> C. -otp_3906(suite) -> - []; -otp_3906(doc) -> - ["Tests that child process deaths are managed correctly when there are " - " a large amount of concurrently dying children. See ticket OTP-3906."]; +%% Tests that child process deaths are managed correctly when there are +%% a large amount of concurrently dying children. See ticket OTP-3906. otp_3906(Config) when is_list(Config) -> case os:type() of {unix, OSName} -> @@ -1239,8 +1216,6 @@ otp_3906_forker(N, Parent, Ref, Sup, Prog) -> end. -otp_4389(suite) -> []; -otp_4389(doc) -> []; otp_4389(Config) when is_list(Config) -> case os:type() of {unix, _} -> @@ -1314,10 +1289,8 @@ get_true_cmd() -> end. %% 'exit_status' option -exit_status(suite) -> - []; -exit_status(doc) -> - ["Test that the 'exit_status' option works"]; +%% +%% Test that the 'exit_status' option works exit_status(Config) when is_list(Config) -> ct:timetrap({minutes, 1}), port_expect(Config, @@ -1325,10 +1298,7 @@ exit_status(Config) when is_list(Config) -> 1, "", [exit_status]), ok. -spawn_driver(suite) -> - []; -spawn_driver(doc) -> - ["Test spawning a driver specifically"]; +%% Test spawning a driver specifically spawn_driver(Config) when is_list(Config) -> Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "echo_drv"), @@ -1360,10 +1330,7 @@ spawn_driver(Config) when is_list(Config) -> {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, os:find_executable("erl")}, [])), ok. -parallelism_option(suite) -> - []; -parallelism_option(doc) -> - ["Test parallelism option of open_port"]; +%% Test parallelism option of open_port parallelism_option(Config) when is_list(Config) -> ?line Path = proplists:get_value(data_dir, Config), ?line ok = load_driver(Path, "echo_drv"), @@ -1395,10 +1362,7 @@ parallelism_option(Config) when is_list(Config) -> ?line receive {Port2, closed} -> ok end, ok. -spawn_executable(suite) -> - []; -spawn_executable(doc) -> - ["Test spawning an executable specifically"]; +%% Test spawning an executable specifically spawn_executable(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir, Config), EchoArgs1 = filename:join([DataDir,"echo_args"]), @@ -1635,10 +1599,7 @@ collect_data(Port) -> parse_echo_args_output(Data) -> [lists:last(string:tokens(S,"|")) || S <- string:tokens(Data,"\r\n")]. -mix_up_ports(suite) -> - []; -mix_up_ports(doc) -> - ["Test that the emulator does not mix up ports when the port table wraps"]; +%% Test that the emulator does not mix up ports when the port table wraps mix_up_ports(Config) when is_list(Config) -> Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "echo_drv"), @@ -1680,11 +1641,8 @@ loop(Start, Stop, Fun) when is_function(Fun) -> loop(Fun(Start), Stop, Fun). -otp_5112(suite) -> - []; -otp_5112(doc) -> - ["Test that link to connected process is taken away when port calls", - "driver_exit() also when the port index has wrapped"]; +%% Test that link to connected process is taken away when port calls +%% driver_exit() also when the port index has wrapped otp_5112(Config) when is_list(Config) -> Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "exit_drv"), @@ -1727,10 +1685,7 @@ otp_5112_wrap_port_ix(Ports) -> end. -otp_5119(suite) -> - []; -otp_5119(doc) -> - ["Test that port index is not unnecessarily wrapped"]; +%% Test that port index is not unnecessarily wrapped otp_5119(Config) when is_list(Config) -> Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "exit_drv"), @@ -1773,8 +1728,7 @@ port_ix(Port) when is_port(Port) -> list_to_integer(PortIxStr). -otp_6224(doc) -> ["Check that port command failure doesn't crash the emulator"]; -otp_6224(suite) -> []; +%% Check that port command failure doesn't crash the emulator otp_6224(Config) when is_list(Config) -> Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "failure_drv"), @@ -1807,8 +1761,6 @@ otp_6224_loop() -> -define(EXIT_STATUS_MSB_MAX_PROCS, 64). -define(EXIT_STATUS_MSB_MAX_PORTS, 300). -exit_status_multi_scheduling_block(doc) -> []; -exit_status_multi_scheduling_block(suite) -> []; exit_status_multi_scheduling_block(Config) when is_list(Config) -> Repeat = 3, case test_server:os_type() of @@ -2225,9 +2177,7 @@ fun_spawn(Fun, Args) -> port_test(Config) when is_list(Config) -> filename:join(proplists:get_value(data_dir, Config), "port_test"). - -ports(doc) -> "Test that erlang:ports/0 returns a consistent snapshot of ports"; -ports(suite) -> []; +%% Test that erlang:ports/0 returns a consistent snapshot of ports ports(Config) when is_list(Config) -> Path = proplists:get_value(data_dir, Config), ok = load_driver(Path, "exit_drv"), @@ -2334,9 +2284,8 @@ load_driver(Dir, Driver) -> end. -close_deaf_port(doc) -> ["Send data to port program that does not read it, then close port." - "Primary targeting Windows to test threaded_handle_closer in sys.c"]; -close_deaf_port(suite) -> []; +%% Send data to port program that does not read it, then close port. +%% Primary targeting Windows to test threaded_handle_closer in sys.c close_deaf_port(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), DataDir = proplists:get_value(data_dir, Config), diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 7df7fb516d..5bb216ff79 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -659,10 +659,6 @@ chk_pi_order([],[]) -> chk_pi_order([{Arg, _}| Values], [Arg|Args]) -> chk_pi_order(Values, Args). -process_info_2_list(doc) -> - []; -process_info_2_list(suite) -> - []; process_info_2_list(Config) when is_list(Config) -> Proc = spawn(fun () -> receive after infinity -> ok end end), register(process_SUITE_process_info_2_list1, self()), @@ -695,10 +691,6 @@ process_info_2_list(Config) when is_list(Config) -> lists:foreach(fun ({backtrace, _}) -> ok end, V3), ok. -process_info_lock_reschedule(doc) -> - []; -process_info_lock_reschedule(suite) -> - []; process_info_lock_reschedule(Config) when is_list(Config) -> %% We need a process that is running and an item that requires %% process_info to take the main process lock. @@ -740,10 +732,6 @@ pi_loop(Name, Pid, N) -> {registered_name, Name} = process_info(Pid, registered_name), pi_loop(Name, Pid, N-1). -process_info_lock_reschedule2(doc) -> - []; -process_info_lock_reschedule2(suite) -> - []; process_info_lock_reschedule2(Config) when is_list(Config) -> Parent = self(), Fun = fun () -> @@ -799,10 +787,6 @@ do_pi_msg_len(PT, AT) -> lists:map(fun (_) -> ok end, [a,b,c,d]), {message_queue_len, _} = process_info(element(2,PT), element(2,AT)). -process_info_lock_reschedule3(doc) -> - []; -process_info_lock_reschedule3(suite) -> - []; process_info_lock_reschedule3(Config) when is_list(Config) -> %% We need a process that is running and an item that requires %% process_info to take the main process lock. @@ -1067,8 +1051,7 @@ make_unaligned_sub_binary(Bin0) -> <<0:3,Bin:Sz/binary,31:5>> = id(Bin1), Bin. -yield(doc) -> - "Tests erlang:yield/1."; +%% Tests erlang:yield/1 yield(Config) when is_list(Config) -> case catch erlang:system_info(modified_timing_level) of Level when is_integer(Level) -> @@ -1146,8 +1129,6 @@ schedcnt(stop, {Ref, Pid}) when is_reference(Ref), is_pid(Pid) -> Cnt end. -yield2(doc) -> []; -yield2(suite) -> []; yield2(Config) when is_list(Config) -> Me = self(), Go = make_ref(), @@ -1239,8 +1220,6 @@ fail_register(Name, Process) -> {'EXIT',{badarg,_}} = (catch Name ! anything_goes), ok. -garbage_collect(doc) -> []; -garbage_collect(suite) -> []; garbage_collect(Config) when is_list(Config) -> Prio = process_flag(priority, high), true = erlang:garbage_collect(), @@ -1279,10 +1258,7 @@ garbage_collect(Config) when is_list(Config) -> process_flag(priority, Prio), ok. -process_info_messages(doc) -> - ["This used to cause the nofrag emulator to dump core"]; -process_info_messages(suite) -> - []; +%% This used to cause the nofrag emulator to dump core process_info_messages(Config) when is_list(Config) -> process_info_messages_test(), ok. @@ -1340,10 +1316,6 @@ process_info_messages_test() -> chk_badarg(Fun) -> try Fun(), exit(no_badarg) catch error:badarg -> ok end. -process_flag_badarg(doc) -> - []; -process_flag_badarg(suite) -> - []; process_flag_badarg(Config) when is_list(Config) -> chk_badarg(fun () -> process_flag(gurka, banan) end), chk_badarg(fun () -> process_flag(trap_exit, gurka) end), @@ -1361,8 +1333,6 @@ process_flag_badarg(Config) when is_list(Config) -> -include_lib("stdlib/include/ms_transform.hrl"). -otp_6237(doc) -> []; -otp_6237(suite) -> []; otp_6237(Config) when is_list(Config) -> Slctrs = lists:map(fun (_) -> spawn_link(fun () -> @@ -1429,10 +1399,6 @@ otp_6237_select_loop() -> conses_per_red, debug_level}). -processes_large_tab(doc) -> - []; -processes_large_tab(suite) -> - []; processes_large_tab(Config) when is_list(Config) -> sys_mem_cond_run(2048, fun () -> processes_large_tab_test(Config) end). @@ -1483,10 +1449,6 @@ processes_large_tab_test(Config) -> stop_node(LargeNode), chk_processes_bif_test_res(Res). -processes_default_tab(doc) -> - []; -processes_default_tab(suite) -> - []; processes_default_tab(Config) when is_list(Config) -> sys_mem_cond_run(1024, fun () -> processes_default_tab_test(Config) end). @@ -1496,10 +1458,6 @@ processes_default_tab_test(Config) -> stop_node(DefaultNode), chk_processes_bif_test_res(Res). -processes_small_tab(doc) -> - []; -processes_small_tab(suite) -> - []; processes_small_tab(Config) when is_list(Config) -> {ok, SmallNode} = start_node(Config, "+P 1024"), Res = rpc:call(SmallNode, ?MODULE, processes_bif_test, []), @@ -1508,10 +1466,6 @@ processes_small_tab(Config) when is_list(Config) -> true = PBInfo#ptab_list_bif_info.tab_chunks < 10, chk_processes_bif_test_res(Res). -processes_this_tab(doc) -> - []; -processes_this_tab(suite) -> - []; processes_this_tab(Config) when is_list(Config) -> Mem = case {erlang:system_info(build_type), erlang:system_info(allocator)} of @@ -1874,10 +1828,6 @@ wait_until_system_recover(Tmr) -> receive {timeout, Tmr, _} -> ok after 0 -> ok end, ok. -processes_last_call_trap(doc) -> - []; -processes_last_call_trap(suite) -> - []; processes_last_call_trap(Config) when is_list(Config) -> enable_internal_state(), Processes = fun () -> processes() end, @@ -1900,10 +1850,6 @@ processes_last_call_trap(Config) when is_list(Config) -> my_processes() -> processes(). -processes_apply_trap(doc) -> - []; -processes_apply_trap(suite) -> - []; processes_apply_trap(Config) when is_list(Config) -> enable_internal_state(), PBInfo = erts_debug:get_internal_state(processes_bif_info), @@ -1918,10 +1864,6 @@ processes_apply_trap(Config) when is_list(Config) -> apply(erlang, processes, []) end, lists:seq(1,100)). -processes_gc_trap(doc) -> - []; -processes_gc_trap(suite) -> - []; processes_gc_trap(Config) when is_list(Config) -> Tester = self(), enable_internal_state(), @@ -1960,10 +1902,6 @@ processes_gc_trap(Config) when is_list(Config) -> exit(Suspendee, bang), ok. -process_flag_heap_size(doc) -> - []; -process_flag_heap_size(suite) -> - []; process_flag_heap_size(Config) when is_list(Config) -> HSize = 2586, % must be gc fib+ number VHSize = 318187, % must be gc fib+ number @@ -1975,10 +1913,6 @@ process_flag_heap_size(Config) when is_list(Config) -> VHSize = erlang:process_flag(min_bin_vheap_size, OldVHmin), ok. -spawn_opt_heap_size(doc) -> - []; -spawn_opt_heap_size(suite) -> - []; spawn_opt_heap_size(Config) when is_list(Config) -> HSize = 987, % must be gc fib+ number VHSize = 46422, % must be gc fib+ number @@ -1989,10 +1923,6 @@ spawn_opt_heap_size(Config) when is_list(Config) -> Pid ! stop, ok. -processes_term_proc_list(doc) -> - []; -processes_term_proc_list(suite) -> - []; processes_term_proc_list(Config) when is_list(Config) -> Tester = self(), as_expected = processes_term_proc_list_test(false), @@ -2142,24 +2072,12 @@ processes_term_proc_list_test(MustChk) -> as_expected. -otp_7738_waiting(doc) -> - []; -otp_7738_waiting(suite) -> - []; otp_7738_waiting(Config) when is_list(Config) -> otp_7738_test(waiting). -otp_7738_suspended(doc) -> - []; -otp_7738_suspended(suite) -> - []; otp_7738_suspended(Config) when is_list(Config) -> otp_7738_test(suspended). -otp_7738_resume(doc) -> - []; -otp_7738_resume(suite) -> - []; otp_7738_resume(Config) when is_list(Config) -> otp_7738_test(resume). diff --git a/erts/emulator/test/ref_SUITE.erl b/erts/emulator/test/ref_SUITE.erl index e84d53a89e..e792410edf 100644 --- a/erts/emulator/test/ref_SUITE.erl +++ b/erts/emulator/test/ref_SUITE.erl @@ -34,7 +34,7 @@ suite() -> all() -> [wrap_1]. -wrap_1(doc) -> "Check that refs don't wrap around easily."; +%% Check that refs don't wrap around easily. wrap_1(Config) when is_list(Config) -> ?line spawn_link(?MODULE, loop_ref, [self()]), ?line receive diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl index fdf6e6fda0..3dfd9a16a8 100644 --- a/erts/emulator/test/save_calls_SUITE.erl +++ b/erts/emulator/test/save_calls_SUITE.erl @@ -73,10 +73,7 @@ init_per_testcase(_,Config) -> end_per_testcase(_,_Config) -> ok. -dont_break_reductions(suite) -> - []; -dont_break_reductions(doc) -> - ["Check that save_calls dont break reduction-based scheduling"]; +%% Check that save_calls dont break reduction-based scheduling dont_break_reductions(Config) when is_list(Config) -> ?line RPS1 = reds_per_sched(0), ?line RPS2 = reds_per_sched(20), @@ -125,7 +122,7 @@ trace_handler(Acc,Parent,Client) -> ok end. -save_calls_1(doc) -> "Test call saving."; +%% Test call saving. save_calls_1(Config) when is_list(Config) -> case test_server:is_native(?MODULE) of true -> {skipped,"Native code"}; diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index ac6c96a02c..f8e9bc5ac0 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -79,9 +79,8 @@ all() -> pending_exit_process_info_2, pending_exit_group_leader, exit_before_pending_exit]. -xm_sig_order(doc) -> ["Test that exit signals and messages are received " - "in correct order"]; -xm_sig_order(suite) -> []; + +%% Test that exit signals and messages are received in correct order xm_sig_order(Config) when is_list(Config) -> ?line LNode = node(), ?line repeat(fun () -> xm_sig_order_test(LNode) end, 1000), @@ -114,13 +113,9 @@ xm_sig_order_proc() -> end, xm_sig_order_proc(). -pending_exit_unlink_process(doc) -> []; -pending_exit_unlink_process(suite) -> []; pending_exit_unlink_process(Config) when is_list(Config) -> ?line pending_exit_test(self(), unlink). -pending_exit_unlink_dist_process(doc) -> []; -pending_exit_unlink_dist_process(suite) -> []; pending_exit_unlink_dist_process(Config) when is_list(Config) -> ?line {ok, Node} = start_node(Config), ?line From = spawn(Node, fun () -> receive after infinity -> ok end end), @@ -128,28 +123,18 @@ pending_exit_unlink_dist_process(Config) when is_list(Config) -> ?line stop_node(Node), ?line Res. -pending_exit_unlink_port(doc) -> []; -pending_exit_unlink_port(suite) -> []; pending_exit_unlink_port(Config) when is_list(Config) -> ?line pending_exit_test(hd(erlang:ports()), unlink). -pending_exit_trap_exit(doc) -> []; -pending_exit_trap_exit(suite) -> []; pending_exit_trap_exit(Config) when is_list(Config) -> ?line pending_exit_test(self(), trap_exit). -pending_exit_receive(doc) -> []; -pending_exit_receive(suite) -> []; pending_exit_receive(Config) when is_list(Config) -> ?line pending_exit_test(self(), 'receive'). -pending_exit_exit(doc) -> []; -pending_exit_exit(suite) -> []; pending_exit_exit(Config) when is_list(Config) -> ?line pending_exit_test(self(), exit). -pending_exit_gc(doc) -> []; -pending_exit_gc(suite) -> []; pending_exit_gc(Config) when is_list(Config) -> ?line pending_exit_test(self(), gc). @@ -225,8 +210,6 @@ pending_exit_test(From, Type) -> -exit_before_pending_exit(doc) -> []; -exit_before_pending_exit(suite) -> []; exit_before_pending_exit(Config) when is_list(Config) -> %% This is a testcase testcase very specific to the smp %% implementation as it is of the time of writing. diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index 8279d374ee..af55ded470 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -78,9 +78,8 @@ end_per_group(_GroupName, Config) -> %%% Testing statistics(wall_clock). -wall_clock_zero_diff(doc) -> - "Tests that the 'Wall clock since last call' element of the result " - "is zero when statistics(runtime) is called twice in succession."; +%% Tests that the 'Wall clock since last call' element of the result +%% is zero when statistics(runtime) is called twice in succession. wall_clock_zero_diff(Config) when is_list(Config) -> wall_clock_zero_diff1(16). @@ -93,10 +92,9 @@ wall_clock_zero_diff1(N) when N > 0 -> wall_clock_zero_diff1(0) -> ct:fail("Difference never zero."). -wall_clock_update(doc) -> - "Test that the time differences returned by two calls to " - "statistics(wall_clock) are compatible, and are within a small number " - "of ms of the amount of real time we waited for."; +%% Test that the time differences returned by two calls to +%% statistics(wall_clock) are compatible, and are within a small number +%% of ms of the amount of real time we waited for. wall_clock_update(Config) when is_list(Config) -> wall_clock_update1(6). @@ -122,9 +120,8 @@ wall_clock_update1(0) -> %%% Test statistics(runtime). -runtime_zero_diff(doc) -> - "Tests that the difference between the times returned from two consectuitive " - "calls to statistics(runtime) is zero."; +%% Tests that the difference between the times returned from two consectuitive +%% calls to statistics(runtime) is zero. runtime_zero_diff(Config) when is_list(Config) -> ?line runtime_zero_diff1(16). @@ -137,10 +134,9 @@ runtime_zero_diff1(N) when N > 0 -> runtime_zero_diff1(0) -> ct:fail("statistics(runtime) never returned zero difference"). -runtime_update(doc) -> - "Test that the statistics(runtime) returns a substanstially " - "updated difference after running a process that takes all CPU " - " power of the Erlang process for a second."; +%% Test that the statistics(runtime) returns a substanstially +%% updated difference after running a process that takes all CPU +%% power of the Erlang process for a second. runtime_update(Config) when is_list(Config) -> case test_server:is_cover() of false -> @@ -167,10 +163,9 @@ do_runtime_update(N) -> cpu_heavy() -> cpu_heavy(). -runtime_diff(doc) -> - "Test that the difference between two consecutive absolute runtimes is " - "equal to the last relative runtime. The loop runs a lot of times since " - "the bug which this test case tests for showed up only rarely."; +%% Test that the difference between two consecutive absolute runtimes is +%% equal to the last relative runtime. The loop runs a lot of times since +%% the bug which this test case tests for showed up only rarely. runtime_diff(Config) when is_list(Config) -> runtime_diff1(1000). @@ -196,10 +191,9 @@ do_much(N) -> do_much(N-1). -reductions(doc) -> - "Test that statistics(reductions) is callable, and that " - "Total_Reductions and Reductions_Since_Last_Call make sense. " - "(This to fail on pre-R3A version of JAM."; +%% Test that statistics(reductions) is callable, and that +%% Total_Reductions and Reductions_Since_Last_Call make sense. +%% This to fail on pre-R3A version of JAM. reductions(Config) when is_list(Config) -> {Reductions, _} = statistics(reductions), @@ -227,8 +221,7 @@ build_some_garbage() -> %% a garbage collection in the scheduler. processes(). -reductions_big(doc) -> - "Test that the number of reductions can be returned as a big number."; +%% Test that the number of reductions can be returned as a big number. reductions_big(Config) when is_list(Config) -> ?line reductions_big_loop(), ok. @@ -246,9 +239,8 @@ reductions_big_loop() -> %%% Tests of statistics(run_queue). -run_queue_one(doc) -> - "Tests that statistics(run_queue) returns 1 if we start a " - "CPU-bound process."; +%% Tests that statistics(run_queue) returns 1 if we start a +%% CPU-bound process. run_queue_one(Config) when is_list(Config) -> ?line MS = erlang:system_flag(multi_scheduling, block), ?line run_queue_one_test(Config), @@ -294,8 +286,7 @@ hog_iter(0, Mon) -> %%% Tests of statistics(scheduler_wall_time). -scheduler_wall_time(doc) -> - "Tests that statistics(scheduler_wall_time) works as intended"; +%% Tests that statistics(scheduler_wall_time) works as intended scheduler_wall_time(Config) when is_list(Config) -> %% Should return undefined if system_flag is not turned on yet undefined = statistics(scheduler_wall_time), @@ -360,9 +351,8 @@ load_percentage([{Id, WN, TN}|Ss], [{Id, WP, TP}|Ps]) -> load_percentage([], []) -> []. -garbage_collection(doc) -> - "Tests that statistics(garbage_collection) is callable. " - "It is not clear how to test anything more."; +%% Tests that statistics(garbage_collection) is callable. +%% It is not clear how to test anything more. garbage_collection(Config) when is_list(Config) -> ?line Bin = list_to_binary(lists:duplicate(19999, 42)), ?line case statistics(garbage_collection) of @@ -387,16 +377,14 @@ garbage_collection_1(Gcs0, Bin) -> end end. -io(doc) -> - "Tests that statistics(io) is callable. " - "This could be improved to test something more."; +%% Tests that statistics(io) is callable. +%% This could be improved to test something more. io(Config) when is_list(Config) -> ?line case statistics(io) of {{input,In},{output,Out}} when is_integer(In), is_integer(Out) -> ok end. -badarg(doc) -> - "Tests that some illegal arguments to statistics fails."; +%% Tests that some illegal arguments to statistics fails. badarg(Config) when is_list(Config) -> ?line case catch statistics(1) of {'EXIT', {badarg, _}} -> ok @@ -465,8 +453,7 @@ run_queues_lengths_active_tasks(Config) -> ok. -msacc(doc) -> - "Tests that statistics(microstate_statistics) works."; +%% Tests that statistics(microstate_statistics) works. msacc(Config) -> %% Test if crypto nif is available diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index 7d66ea5c5b..71145d4595 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -50,8 +50,6 @@ all() -> %%% The test cases ------------------------------------------------------------- %%% -process_count(doc) -> []; -process_count(suite) -> []; process_count(Config) when is_list(Config) -> case catch erlang:system_info(modified_timing_level) of Level when is_integer(Level) -> @@ -115,13 +113,9 @@ stop_procs(PMs) -> end, PMs). -system_version(doc) -> []; -system_version(suite) -> []; system_version(Config) when is_list(Config) -> ?line {comment, erlang:system_info(system_version)}. -misc_smoke_tests(doc) -> []; -misc_smoke_tests(suite) -> []; misc_smoke_tests(Config) when is_list(Config) -> ?line true = is_binary(erlang:system_info(info)), ?line true = is_binary(erlang:system_info(procs)), @@ -132,8 +126,6 @@ misc_smoke_tests(Config) when is_list(Config) -> ?line ok. -heap_size(doc) -> []; -heap_size(suite) -> []; heap_size(Config) when is_list(Config) -> ?line {min_bin_vheap_size, VHmin} = erlang:system_info(min_bin_vheap_size), ?line {min_heap_size, Hmin} = erlang:system_info(min_heap_size), @@ -142,10 +134,7 @@ heap_size(Config) when is_list(Config) -> ?line Hmin = proplists:get_value(min_heap_size, GCinf), ok. -wordsize(suite) -> - []; -wordsize(doc) -> - ["Tests the various wordsize variants"]; +%% Tests the various wordsize variants wordsize(Config) when is_list(Config) -> ?line A = erlang:system_info(wordsize), ?line true = is_integer(A), @@ -161,7 +150,7 @@ wordsize(Config) when is_list(Config) -> exit({unexpected_wordsizes,Other}) end. -memory(doc) -> ["Verify that erlang:memory/0 and memory results in crashdump produce are similar"]; +%% Verify that erlang:memory/0 and memory results in crashdump produce are similar memory(Config) when is_list(Config) -> %% %% Verify that erlang:memory/0 and memory results in @@ -501,9 +490,7 @@ mapn(_Fun, 0) -> mapn(Fun, N) -> [Fun(N) | mapn(Fun, N-1)]. -ets_limit(doc) -> - "Verify system_info(ets_limit) reflects max ETS table settings."; -ets_limit(suite) -> []; +%% Verify system_info(ets_limit) reflects max ETS table settings. ets_limit(Config0) when is_list(Config0) -> Config = [{testcase,ets_limit}|Config0], true = is_integer(get_ets_limit(Config)), diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl index 7abce1e715..6d80d436ba 100644 --- a/erts/emulator/test/system_profile_SUITE.erl +++ b/erts/emulator/test/system_profile_SUITE.erl @@ -45,10 +45,6 @@ all() -> %% No specification clause needed for an init function in a conf case!!! %% Test switching system_profiling on and off. -system_profile_on_and_off(suite) -> - []; -system_profile_on_and_off(doc) -> - ["Tests switching system_profiling on and off."]; system_profile_on_and_off(Config) when is_list(Config) -> Pid = start_profiler_process(), @@ -77,12 +73,7 @@ system_profile_on_and_off(Config) when is_list(Config) -> exit(Pid,kill), ok. -%% Test runnable_procs - -runnable_procs(suite) -> - []; -runnable_procs(doc) -> - ["Tests system_profiling with runnable_procs."]; +%% Tests system_profiling with runnable_procs. runnable_procs(Config) when is_list(Config) -> lists:foreach(fun (TsType) -> Arg = case TsType of @@ -119,10 +110,7 @@ do_runnable_procs({TsType, TsTypeFlag}) -> exit(Pid,kill), ok. -runnable_ports(suite) -> - []; -runnable_ports(doc) -> - ["Tests system_profiling with runnable_port."]; +%% Tests system_profiling with runnable_port. runnable_ports(Config) when is_list(Config) -> lists:foreach(fun (TsType) -> Arg = case TsType of @@ -155,10 +143,7 @@ do_runnable_ports({TsType, TsTypeFlag}, Config) -> exit(Pid,kill), ok. -scheduler(suite) -> - []; -scheduler(doc) -> - ["Tests system_profiling with scheduler."]; +%% Tests system_profiling with scheduler. scheduler(Config) when is_list(Config) -> case {erlang:system_info(smp_support), erlang:system_info(schedulers_online)} of {false,_} -> {skipped, "No need for scheduler test when smp support is disabled."}; @@ -180,11 +165,7 @@ scheduler(Config) when is_list(Config) -> strict_monotonic_timestamp]) end. -% the profiler pid should not be profiled -dont_profile_profiler(suite) -> - []; -dont_profile_profiler(doc) -> - ["Ensure system profiler process is not profiled."]; +%% Ensure system profiler process is not profiled. dont_profile_profiler(Config) when is_list(Config) -> Pid = start_profiler_process(), diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index cdf79e48d0..aeddb0d216 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -103,10 +103,7 @@ end_per_group(_GroupName, Config) -> Config. -local_to_univ_utc(suite) -> - []; -local_to_univ_utc(doc) -> - ["Test that DST = true on timezones without DST is ignored"]; +%% Test that DST = true on timezones without DST is ignored local_to_univ_utc(Config) when is_list(Config) -> case os:type() of {unix,_} -> @@ -315,10 +312,7 @@ effective_timezone1(_) -> %% Test (the bif) os:timestamp/0, which is something quite like, but not %% similar to erlang:now... -timestamp(suite) -> - []; -timestamp(doc) -> - ["Test that os:timestamp works."]; +%% Test that os:timestamp works. timestamp(Config) when is_list(Config) -> try repeating_timestamp_check(100000) diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index c04c2e465e..30621e62dc 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -71,7 +71,7 @@ all() -> auto_cancel_yielding]. -start_timer_1(doc) -> ["Basic start_timer/3 functionality"]; +%% Basic start_timer/3 functionality start_timer_1(Config) when is_list(Config) -> Ref1 = erlang:start_timer(1000, self(), plopp), ok = get(1100, {timeout, Ref1, plopp}), @@ -91,14 +91,14 @@ start_timer_1(Config) when is_list(Config) -> no_message = get(900, {timeout, Ref3, plopp}), ok. -send_after_1(doc) -> ["Basic send_after/3 functionality"]; +%% Basic send_after/3 functionality send_after_1(Config) when is_list(Config) -> ?line Ref3 = erlang:send_after(1000, self(), plipp), ?line ok = get(1500, plipp), ?line false = erlang:read_timer(Ref3), ok. -start_timer_big(doc) -> ["Big timeouts for start_timer/3"]; +%% Big timeouts for start_timer/3 start_timer_big(Config) when is_list(Config) -> ?line Big = 1 bsl 31, ?line R = erlang:start_timer(Big, self(), hej), @@ -112,7 +112,7 @@ start_timer_big(Config) when is_list(Config) -> end, ok. -send_after_big(doc) -> ["Big timeouts for send_after/3"]; +%% Big timeouts for send_after/3 send_after_big(Config) when is_list(Config) -> ?line Big = 1 bsl 31, ?line R = erlang:send_after(Big, self(), hej), @@ -126,7 +126,7 @@ send_after_big(Config) when is_list(Config) -> end, ok. -send_after_2(doc) -> ["send_after/3: messages in the right order, kind version"]; +%% send_after/3: messages in the right order, kind version send_after_2(Config) when is_list(Config) -> ?line _ = erlang:send_after(5000, self(), last), ?line _ = erlang:send_after(0, self(), a0), @@ -138,7 +138,7 @@ send_after_2(Config) when is_list(Config) -> ?line [a0,a1,a2,a3,a4,a5,last] = collect(last), ok. -send_after_3(doc) -> ["send_after/3: messages in the right order, worse than send_after_2"]; +%% send_after/3: messages in the right order, worse than send_after_2 send_after_3(Config) when is_list(Config) -> _ = erlang:send_after(100, self(), b1), _ = erlang:send_after(101, self(), b2), @@ -155,13 +155,13 @@ send_after_3(Config) when is_list(Config) -> ok. -cancel_timer_1(doc) -> ["Check trivial cancel_timer/1 behaviour"]; +%% Check trivial cancel_timer/1 behaviour cancel_timer_1(Config) when is_list(Config) -> ?line false = erlang:cancel_timer(make_ref()), ok. -start_timer_e(doc) -> ["Error cases for start_timer/3"]; +%% Error cases for start_timer/3 start_timer_e(Config) when is_list(Config) -> ?line {'EXIT', _} = (catch erlang:start_timer(-4, self(), hej)), ?line {'EXIT', _} = (catch erlang:start_timer(1 bsl 64, @@ -178,8 +178,7 @@ start_timer_e(Config) when is_list(Config) -> ok. -send_after_e(doc) -> ["Error cases for send_after/3"]; -send_after_e(suite) -> []; +%% Error cases for send_after/3 send_after_e(Config) when is_list(Config) -> ?line {'EXIT', _} = (catch erlang:send_after(-4, self(), hej)), ?line {'EXIT', _} = (catch erlang:send_after(1 bsl 64, @@ -194,16 +193,14 @@ send_after_e(Config) when is_list(Config) -> ?line stop_slave(Node), ok. -cancel_timer_e(doc) -> ["Error cases for cancel_timer/1"]; -cancel_timer_e(suite) -> []; +%% Error cases for cancel_timer/1 cancel_timer_e(Config) when is_list(Config) -> ?line {'EXIT', _} = (catch erlang:cancel_timer(1)), ?line {'EXIT', _} = (catch erlang:cancel_timer(self())), ?line {'EXIT', _} = (catch erlang:cancel_timer(a)), ok. -read_timer_trivial(doc) -> ["Trivial and error test cases for read_timer/1."]; -read_timer_trivial(suite) -> []; +%% Trivial and error test cases for read_timer/1. read_timer_trivial(Config) when is_list(Config) -> ?line false = erlang:read_timer(make_ref()), ?line {'EXIT', _} = (catch erlang:read_timer(42)), @@ -212,8 +209,7 @@ read_timer_trivial(Config) when is_list(Config) -> ?line {'EXIT', _} = (catch erlang:read_timer(ab)), ok. -read_timer(doc) -> ["Test that read_timer/1 seems to return the correct values."]; -read_timer(suite) -> []; +%% Test that read_timer/1 seems to return the correct values. read_timer(Config) when is_list(Config) -> process_flag(scheduler, 1), Big = 1 bsl 31, @@ -237,8 +233,7 @@ read_timer(Config) when is_list(Config) -> process_flag(scheduler, 0), ok. -read_timer_async(doc) -> ["Test that read_timer/1 seems to return the correct values."]; -read_timer_async(suite) -> []; +%% Test that read_timer/1 seems to return the correct values. read_timer_async(Config) when is_list(Config) -> process_flag(scheduler, 1), Big = 1 bsl 33, @@ -269,8 +264,6 @@ read_timer_async(Config) when is_list(Config) -> process_flag(scheduler, 0), ok. -cleanup(doc) -> []; -cleanup(suite) -> []; cleanup(Config) when is_list(Config) -> ?line Mem = mem(), %% Timer on dead process @@ -320,8 +313,6 @@ cleanup(Config) when is_list(Config) -> ?line ok. -evil_timers(doc) -> []; -evil_timers(suite) -> []; evil_timers(Config) when is_list(Config) -> %% Create a composite term consisting of at least: %% * externals (remote pids, ports, and refs) @@ -458,8 +449,6 @@ evil_recv_timeouts(TOs, N, M) -> ?line evil_recv_timeouts(TOs, N, M) end. -registered_process(doc) -> []; -registered_process(suite) -> []; registered_process(Config) when is_list(Config) -> ?line Mem = mem(), %% Cancel diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 44d631cfa9..6f3415fa7b 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -104,8 +104,8 @@ receive_trace(Config) when is_list(Config) -> ?line receive_nothing(), ok. -self_send(doc) -> ["Test that traces are generated for messages sent ", - "and received to/from self()."]; +%% Test that traces are generated for messages sent +%% and received to/from self(). self_send(Config) when is_list(Config) -> ?line Fun = fun(Self, Parent) -> receive @@ -392,8 +392,7 @@ set_on_first_spawn(Config) when is_list(Config) -> ok. -system_monitor_args(doc) -> - ["Tests arguments to erlang:system_monitor/0-2)"]; +%% Tests arguments to erlang:system_monitor/0,1,2 system_monitor_args(Config) when is_list(Config) -> ?line Self = self(), %% @@ -455,8 +454,7 @@ system_monitor_args(Config) when is_list(Config) -> ok. -more_system_monitor_args(doc) -> - ["Tests arguments to erlang:system_monitor/0-2)"]; +%% Tests arguments to erlang:system_monitor/0,1,2 more_system_monitor_args(Config) when is_list(Config) -> ?line try_l(64000), ?line try_l(16#7ffffff), @@ -502,10 +500,7 @@ start_monitor() -> erlang:yield(), % Need to be rescheduled for the trace to take ok. -system_monitor_long_schedule(suite) -> - []; -system_monitor_long_schedule(doc) -> - ["Tests erlang:system_monitor(Pid, [{long_schedule,Time}])"]; +%% Tests erlang:system_monitor(Pid, [{long_schedule,Time}]) system_monitor_long_schedule(Config) when is_list(Config) -> Path = proplists:get_value(data_dir, Config), erl_ddll:start(), @@ -541,10 +536,7 @@ do_system_monitor_long_schedule() -> -define(LONG_GC_SLEEP, 670). -system_monitor_long_gc_1(suite) -> - []; -system_monitor_long_gc_1(doc) -> - ["Tests erlang:system_monitor(Pid, [{long_gc,Time}])"]; +%% Tests erlang:system_monitor(Pid, [{long_gc,Time}]) system_monitor_long_gc_1(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), try @@ -566,10 +558,7 @@ system_monitor_long_gc_1(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, false) end. -system_monitor_long_gc_2(suite) -> - []; -system_monitor_long_gc_2(doc) -> - ["Tests erlang:system_monitor(Pid, [{long_gc,Time}])"]; +%% Tests erlang:system_monitor(Pid, [{long_gc,Time}]) system_monitor_long_gc_2(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), try @@ -659,10 +648,7 @@ long_gc_check(Pid, Time, Result) -> Result end. -system_monitor_large_heap_1(suite) -> - []; -system_monitor_large_heap_1(doc) -> - ["Tests erlang:system_monitor(Pid, [{large_heap,Size}])"]; +%% Tests erlang:system_monitor(Pid, [{large_heap,Size}]) system_monitor_large_heap_1(Config) when is_list(Config) -> ?line LoadFun = fun (Size) -> @@ -673,10 +659,7 @@ system_monitor_large_heap_1(Config) when is_list(Config) -> end, ?line large_heap(LoadFun, false). -system_monitor_large_heap_2(suite) -> - []; -system_monitor_large_heap_2(doc) -> - ["Tests erlang:system_monitor(Pid, [{large_heap,Size}])"]; +%% Tests erlang:system_monitor(Pid, [{large_heap,Size}]) system_monitor_large_heap_2(Config) when is_list(Config) -> ?line Parent = self(), ?line LoadFun = @@ -787,7 +770,7 @@ spawn_children(Parent, Number, Result) -> spawn_children(Parent, Number-1, [Child|Result]) end. -suspend(doc) -> "Test erlang:suspend/1 and erlang:resume/1."; +%% Test erlang:suspend/1 and erlang:resume/1. suspend(Config) when is_list(Config) -> ct:timetrap({minutes,2}), ?line Worker = fun_spawn(fun worker/0), @@ -815,10 +798,6 @@ do_suspend(Pid, N) -> -mutual_suspend(doc) -> - []; -mutual_suspend(suite) -> - []; mutual_suspend(Config) when is_list(Config) -> ?line TimeoutSecs = 5*60, ct:timetrap({seconds, TimeoutSecs}), @@ -866,10 +845,6 @@ do_mutual_suspend(Pid, N) -> ?line true = erlang:resume_process(Pid), ?line do_mutual_suspend(Pid, N-1). -suspend_exit(doc) -> - []; -suspend_exit(suite) -> - []; suspend_exit(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), rand:seed(exsplus, {4711,17,4711}), @@ -923,10 +898,6 @@ suspend_exit_work(N) -> chk_suspended(P, Bool, Line) -> {Bool, Line} = {({status, suspended} == process_info(P, status)), Line}. -suspender_exit(doc) -> - []; -suspender_exit(suite) -> - []; suspender_exit(Config) when is_list(Config) -> ct:timetrap({minutes, 3}), ?line P1 = spawn_link(fun () -> receive after infinity -> ok end end), @@ -1033,10 +1004,6 @@ suspender_exit(Config) when is_list(Config) -> ?line exit(P1, bong), ?line ok. -suspend_system_limit(doc) -> - []; -suspend_system_limit(suite) -> - []; suspend_system_limit(Config) when is_list(Config) -> case os:getenv("ERL_EXTREME_TESTING") of "true" -> @@ -1094,10 +1061,6 @@ resume_from_system_limit(P, N, M) -> synced = 0, async_once = 0}). -suspend_opts(doc) -> - []; -suspend_opts(suite) -> - []; suspend_opts(Config) when is_list(Config) -> ct:timetrap({minutes, 3}), ?line Self = self(), @@ -1245,7 +1208,7 @@ repeat_acc(Fun, N, M, Acc) -> %% Tests that waiting process can be suspended %% (bug in R2D and earlier; see OTP-1488). -suspend_waiting(doc) -> "Test that a waiting process can be suspended."; +%% Test that a waiting process can be suspended. suspend_waiting(Config) when is_list(Config) -> ?line Process = fun_spawn(fun process/0), ?line receive after 1 -> ok end, @@ -1254,8 +1217,7 @@ suspend_waiting(Config) when is_list(Config) -> ok. -new_clear(doc) -> - "Test that erlang:trace(new, true, ...) is cleared when tracer dies."; +%% Test that erlang:trace(new, true, ...) is cleared when tracer dies. new_clear(Config) when is_list(Config) -> ?line Tracer = spawn(fun receiver/0), ?line 0 = erlang:trace(new, true, [send, {tracer, Tracer}]), @@ -1272,8 +1234,7 @@ new_clear(Config) when is_list(Config) -> -existing_clear(doc) -> - "Test that erlang:trace(all, false, ...) works without tracer."; +%% Test that erlang:trace(all, false, ...) works without tracer. existing_clear(Config) when is_list(Config) -> ?line Self = self(), @@ -1289,8 +1250,7 @@ existing_clear(Config) when is_list(Config) -> ?line M = N + 1, % Since trace could not be enabled on the tracer. ok. -bad_flag(doc) -> "Test that an invalid flag cause badarg"; -bad_flag(suite) -> []; +%% Test that an invalid flag cause badarg bad_flag(Config) when is_list(Config) -> %% A bad flag could deadlock the SMP emulator in erts-5.5 ?line {'EXIT', {badarg, _}} = (catch erlang:trace(new, @@ -1298,8 +1258,7 @@ bad_flag(Config) when is_list(Config) -> [not_a_valid_flag])), ?line ok. -trace_delivered(doc) -> "Test erlang:trace_delivered/1"; -trace_delivered(suite) -> []; +%% Test erlang:trace_delivered/1 trace_delivered(Config) when is_list(Config) -> ct:timetrap({minutes, 1}), ?line TokLoops = 10000, diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index e3e4e2e6d8..7995c1831a 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -61,8 +61,7 @@ end_per_group(_GroupName, Config) -> not_run(Config) when is_list(Config) -> {skipped,"Native code"}. -trace_on_and_off(doc) -> - "Tests switching tracing on and off."; +%% Tests switching tracing on and off. trace_on_and_off(Config) when is_list(Config) -> ?line Pid = spawn(?MODULE, bif_process, []), ?line Self = self(), @@ -79,11 +78,11 @@ trace_on_and_off(Config) when is_list(Config) -> ?line exit(Pid,kill), ok. -trace_bif(doc) -> "Test tracing BIFs."; +%% Test tracing BIFs. trace_bif(Config) when is_list(Config) -> do_trace_bif([]). -trace_bif_local(doc) -> "Test tracing BIFs with local flag."; +%% Test tracing BIFs with local flag. trace_bif_local(Config) when is_list(Config) -> do_trace_bif([local]). @@ -110,7 +109,7 @@ do_trace_bif(Flags) -> ?line exit(Pid, die), ok. -trace_bif_timestamp(doc) -> "Test tracing BIFs with timestamps."; +%% Test tracing BIFs with timestamps. trace_bif_timestamp(Config) when is_list(Config) -> do_trace_bif_timestamp([], timestamp, [timestamp]), do_trace_bif_timestamp([], timestamp, @@ -123,8 +122,7 @@ trace_bif_timestamp(Config) when is_list(Config) -> [monotonic_timestamp, strict_monotonic_timestamp]), do_trace_bif_timestamp([], monotonic_timestamp, [monotonic_timestamp]). -trace_bif_timestamp_local(doc) -> - "Test tracing BIFs with timestamps and local flag."; +%% Test tracing BIFs with timestamps and local flag. trace_bif_timestamp_local(Config) when is_list(Config) -> do_trace_bif_timestamp([local], timestamp, [timestamp]), do_trace_bif_timestamp([local], timestamp, @@ -182,8 +180,7 @@ do_trace_bif_timestamp(Flags, TsType, TsFlags) -> exit(Pid, die), ok. -trace_bif_return(doc) -> - "Test tracing BIF's with return/return_to trace."; +%% Test tracing BIF's with return/return_to trace. trace_bif_return(Config) when is_list(Config) -> do_trace_bif_return(timestamp, [timestamp]), do_trace_bif_return(timestamp, @@ -347,7 +344,7 @@ bif_process() -> -trace_info_old_code(doc) -> "trace_info on deleted module (OTP-5057)."; +%% trace_info on deleted module (OTP-5057). trace_info_old_code(Config) when is_list(Config) -> ?line MFA = {M,F,0} = {test,foo,0}, ?line Fname = atom_to_list(M)++".erl", diff --git a/erts/emulator/test/trace_call_count_SUITE.erl b/erts/emulator/test/trace_call_count_SUITE.erl index 251836831e..3e7c1020a5 100644 --- a/erts/emulator/test/trace_call_count_SUITE.erl +++ b/erts/emulator/test/trace_call_count_SUITE.erl @@ -108,38 +108,23 @@ end_per_group(_GroupName, Config) -> not_run(Config) when is_list(Config) -> {skipped,"Native code"}. -basic(suite) -> - []; -basic(doc) -> - ["Tests basic call count trace"]; +%% Tests basic call count trace basic(Config) when is_list(Config) -> basic_test(). -on_and_off(suite) -> - []; -on_and_off(doc) -> - ["Tests turning trace parameters on and off"]; +%% Tests turning trace parameters on and off on_and_off(Config) when is_list(Config) -> on_and_off_test(). -info(suite) -> - []; -info(doc) -> - ["Tests the trace_info BIF"]; +%% Tests the trace_info BIF info(Config) when is_list(Config) -> info_test(). -pause_and_restart(suite) -> - []; -pause_and_restart(doc) -> - ["Tests pausing and restarting call counters"]; +%% Tests pausing and restarting call counters pause_and_restart(Config) when is_list(Config) -> pause_and_restart_test(). -combo(suite) -> - []; -combo(doc) -> - ["Tests combining local call trace and meta trace with call count trace"]; +%% Tests combining local call trace and meta trace with call count trace combo(Config) when is_list(Config) -> combo_test(). diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 907026bf03..09eda2845c 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -94,10 +94,7 @@ all() -> not_run(Config) when is_list(Config) -> {skipped,"Native code"}. -basic(suite) -> - []; -basic(doc) -> - ["Tests basic call count trace"]; +%% Tests basic call time trace basic(Config) when is_list(Config) -> ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ?line M = 1000, @@ -123,10 +120,7 @@ basic(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -on_and_off(suite) -> - []; -on_and_off(doc) -> - ["Tests turning trace parameters on and off"]; +%% "Tests turning trace parameters on and off on_and_off(Config) when is_list(Config) -> ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ?line M = 100, @@ -164,10 +158,7 @@ on_and_off(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -info(suite) -> - []; -info(doc) -> - ["Tests the trace_info BIF"]; +%% Tests the trace_info BIF info(Config) when is_list(Config) -> ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), %% @@ -188,10 +179,7 @@ info(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -pause_and_restart(suite) -> - []; -pause_and_restart(doc) -> - ["Tests pausing and restarting call time counters"]; +%% Tests pausing and restarting call time counters pause_and_restart(Config) when is_list(Config) -> ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ?line M = 100, @@ -216,10 +204,7 @@ pause_and_restart(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -scheduling(suite) -> - []; -scheduling(doc) -> - ["Tests in/out scheduling of call time counters"]; +%% Tests in/out scheduling of call time counters scheduling(Config) when is_list(Config) -> ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ?line M = 1000000, @@ -252,10 +237,7 @@ scheduling(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -combo(suite) -> - []; -combo(doc) -> - ["Tests combining local call trace and meta trace with call time trace"]; +%% "Tests combining local call trace and meta trace with call time trace combo(Config) when is_list(Config) -> ?line Self = self(), ?line Nbc = 3, @@ -366,10 +348,7 @@ combo(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bif(suite) -> - []; -bif(doc) -> - ["Tests tracing of bifs"]; +%% Tests tracing of bifs bif(Config) when is_list(Config) -> ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ?line M = 1000000, @@ -398,10 +377,7 @@ bif(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -nif(suite) -> - []; -nif(doc) -> - ["Tests tracing of nifs"]; +%% Tests tracing of nifs nif(Config) when is_list(Config) -> load_nif(Config), ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), @@ -424,10 +400,7 @@ nif(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -called_function(suite) -> - []; -called_function(doc) -> - ["Tests combining nested function calls and that the time accumulates to the right function"]; +%% Tests combining nested function calls and that the time accumulates to the right function called_function(Config) when is_list(Config) -> ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ?line M = 2100, diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 93ba9a6ba3..80ffc18d17 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -101,120 +101,97 @@ all() -> not_run(Config) when is_list(Config) -> {skipped,"Native code"}. -basic(doc) -> - ["Tests basic local call-trace"]; +%% Tests basic local call-trace basic(Config) when is_list(Config) -> basic_test(). -bit_syntax(doc) -> - "OTP-7399: Make sure that code that uses the optimized bit syntax matching " - "can be traced without crashing the emulator."; +%% OTP-7399: Make sure that code that uses the optimized bit syntax matching +%% can be traced without crashing the emulator. bit_syntax(Config) when is_list(Config) -> bit_syntax_test(). -return(doc) -> - ["Tests the different types of return trace"]; +%% Tests the different types of return trace return(Config) when is_list(Config) -> return_test(). -on_and_off(doc) -> - ["Tests turning trace parameters on and off, " - "both for trace and trace_pattern"]; +%% Tests turning trace parameters on and off, +%% both for trace and trace_pattern on_and_off(Config) when is_list(Config) -> on_and_off_test(). -stack_grow(doc) -> - ["Tests the stack growth during return traces"]; +%% Tests the stack growth during return traces stack_grow(Config) when is_list(Config) -> stack_grow_test(). -info(doc) -> - ["Tests the trace_info BIF"]; +%% Tests the trace_info BIF info(Config) when is_list(Config) -> info_test(). -delete(doc) -> - ["Tests putting trace on deleted modules"]; +%% Tests putting trace on deleted modules delete(Config) when is_list(Config) -> delete_test(Config). -exception(doc) -> - ["Tests exception_trace"]; +%% Tests exception_trace exception(Config) when is_list(Config) -> exception_test([]). -exception_apply(doc) -> - ["Tests exception_trace"]; +%% Tests exception_trace exception_apply(Config) when is_list(Config) -> exception_test([apply]). -exception_function(doc) -> - ["Tests exception_trace"]; +%% Tests exception_trace exception_function(Config) when is_list(Config) -> exception_test([function]). -exception_apply_function(doc) -> - ["Tests exception_trace"]; +%% Tests exception_trace exception_apply_function(Config) when is_list(Config) -> exception_test([apply,function]). -exception_nocatch(doc) -> - ["Tests exception_trace"]; +%% Tests exception_trace exception_nocatch(Config) when is_list(Config) -> exception_test([nocatch]). -exception_nocatch_apply(doc) -> - ["Tests exception_trace"]; +%% Tests exception_trace exception_nocatch_apply(Config) when is_list(Config) -> exception_test([nocatch,apply]). -exception_nocatch_function(doc) -> - ["Tests exception_trace"]; +%% Tests exception_trace exception_nocatch_function(Config) when is_list(Config) -> exception_test([nocatch,function]). -exception_nocatch_apply_function(doc) -> - ["Tests exception_trace"]; +%% Tests exception_trace exception_nocatch_apply_function(Config) when is_list(Config) -> exception_test([nocatch,apply,function]). -exception_meta(doc) -> - ["Tests meta exception_trace"]; +%% Tests meta exception_trace exception_meta(Config) when is_list(Config) -> exception_test([meta]). -exception_meta_apply(doc) -> - ["Tests meta exception_trace"]; +%% Tests meta exception_trace exception_meta_apply(Config) when is_list(Config) -> exception_test([meta,apply]). -exception_meta_function(doc) -> - ["Tests meta exception_trace"]; +%% Tests meta exception_trace exception_meta_function(Config) when is_list(Config) -> exception_test([meta,function]). -exception_meta_apply_function(doc) -> - ["Tests meta exception_trace"]; +%% Tests meta exception_trace exception_meta_apply_function(Config) when is_list(Config) -> exception_test([meta,apply,function]). -exception_meta_nocatch(doc) -> - ["Tests meta exception_trace"]; +%% Tests meta exception_trace exception_meta_nocatch(Config) when is_list(Config) -> exception_test([meta,nocatch]). -exception_meta_nocatch_apply(doc) -> - ["Tests meta exception_trace"]; +%% Tests meta exception_trace exception_meta_nocatch_apply(Config) when is_list(Config) -> exception_test([meta,nocatch,apply]). -exception_meta_nocatch_function(doc) -> - ["Tests meta exception_trace"]; +%% Tests meta exception_trace exception_meta_nocatch_function(Config) when is_list(Config) -> exception_test([meta,nocatch,function]). -exception_meta_nocatch_apply_function(doc) -> - ["Tests meta exception_trace"]; +%% Tests meta exception_trace exception_meta_nocatch_apply_function(Config) when is_list(Config) -> exception_test([meta,nocatch,apply,function]). diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl index a2db701d36..dcc2cc807b 100644 --- a/erts/emulator/test/trace_meta_SUITE.erl +++ b/erts/emulator/test/trace_meta_SUITE.erl @@ -94,55 +94,35 @@ end. not_run(Config) when is_list(Config) -> {skipped,"Native code"}. -basic(suite) -> - []; -basic(doc) -> - ["Tests basic meta trace"]; +%% Tests basic meta trace basic(Config) when is_list(Config) -> basic_test(). -return(suite) -> - []; -return(doc) -> - ["Tests return trace"]; +%% Tests return trace return(Config) when is_list(Config) -> return_test(). -on_and_off(suite) -> - []; -on_and_off(doc) -> - ["Tests turning trace parameters on and off"]; +%% Tests turning trace parameters on and off on_and_off(Config) when is_list(Config) -> on_and_off_test(). -stack_grow(doc) -> - ["Tests the stack growth during return traces"]; +%% Tests the stack growth during return traces stack_grow(Config) when is_list(Config) -> stack_grow_test(). -info(doc) -> - ["Tests the trace_info BIF"]; +%% Tests the trace_info BIF info(Config) when is_list(Config) -> info_test(). -tracer(suite) -> - []; -tracer(doc) -> - ["Tests stopping and changing tracer process"]; +%% Tests stopping and changing tracer process tracer(Config) when is_list(Config) -> tracer_test(). -combo(suite) -> - []; -combo(doc) -> - ["Tests combining local call trace with meta trace"]; +%% Tests combining local call trace with meta trace combo(Config) when is_list(Config) -> combo_test(). -nosilent(suite) -> - []; -nosilent(doc) -> - ["Tests that meta trace is not silenced by the silent process flag"]; +%% Tests that meta trace is not silenced by the silent process flag nosilent(Config) when is_list(Config) -> nosilent_test(). diff --git a/erts/emulator/test/trace_nif_SUITE.erl b/erts/emulator/test/trace_nif_SUITE.erl index 4ddcbf8f6b..4cf9069f1d 100644 --- a/erts/emulator/test/trace_nif_SUITE.erl +++ b/erts/emulator/test/trace_nif_SUITE.erl @@ -64,18 +64,18 @@ end_per_group(_GroupName, Config) -> not_run(Config) when is_list(Config) -> {skipped,"Native code"}. -trace_nif(doc) -> "Test tracing NIFs."; +%% Test tracing NIFs. trace_nif(Config) when is_list(Config) -> load_nif(Config), do_trace_nif([]). -trace_nif_local(doc) -> "Test tracing NIFs with local flag."; +%% Test tracing NIFs with local flag. trace_nif_local(Config) when is_list(Config) -> load_nif(Config), do_trace_nif([local]). -trace_nif_meta(doc) -> "Test tracing NIFs with meta flag."; +%% Test tracing NIFs with meta flag. trace_nif_meta(Config) when is_list(Config) -> load_nif(Config), ?line Pid=spawn_link(?MODULE, nif_process, []), @@ -143,13 +143,12 @@ do_trace_nif(Flags) -> ?line exit(Pid, die), ok. -trace_nif_timestamp(doc) -> "Test tracing NIFs with timestamps."; +%% Test tracing NIFs with timestamps. trace_nif_timestamp(Config) when is_list(Config) -> load_nif(Config), do_trace_nif_timestamp([]). -trace_nif_timestamp_local(doc) -> - "Test tracing NIFs with timestamps and local flag."; +%% Test tracing NIFs with timestamps and local flag. trace_nif_timestamp_local(Config) when is_list(Config) -> load_nif(Config), do_trace_nif_timestamp([local]). @@ -191,8 +190,7 @@ do_trace_nif_timestamp(Flags) -> ?line exit(Pid, die), ok. -trace_nif_return(doc) -> - "Test tracing NIF's with return/return_to trace."; +%% Test tracing NIF's with return/return_to trace. trace_nif_return(Config) when is_list(Config) -> load_nif(Config), diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index a38fced7cb..73b32fdf7d 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -50,7 +50,7 @@ all() -> fake_schedule_after_getting_unlinked, gc, default_tracer, tracer_port_crash]. -call_trace(doc) -> "Test sending call trace messages to a port."; +%% Test sending call trace messages to a port. call_trace(Config) when is_list(Config) -> case test_server:is_native(?MODULE) orelse test_server:is_native(lists) of @@ -102,7 +102,7 @@ bs_sum_c(<>, Acc) -> bs_sum_c(T, H+Acc); bs_sum_c(<<>>, Acc) -> Acc. -return_trace(doc) -> "Test the new return trace."; +%% Test the new return trace. return_trace(Config) when is_list(Config) -> case test_server:is_native(?MODULE) orelse test_server:is_native(lists) of @@ -137,7 +137,7 @@ return_trace(Config) when is_list(Config) -> ok end. -send(doc) -> "Test sending send trace messages to a port."; +%% Test sending send trace messages to a port. send(Config) when is_list(Config) -> ?line Tracer = start_tracer(Config), Self = self(), @@ -176,7 +176,7 @@ send(Config) when is_list(Config) -> receive good_bye -> ok end, ok. -receive_trace(doc) -> "Test sending receive traces to a port."; +%% Test sending receive traces to a port. receive_trace(Config) when is_list(Config) -> ?line start_tracer(Config), ?line Receiver = fun_spawn(fun receiver/0), @@ -191,7 +191,7 @@ receive_trace(Config) when is_list(Config) -> ?line expect({trace_ts,Receiver,'receive',Huge,ts}), ok. -process_events(doc) -> "Tests a few process events (like getting linked)."; +%% Tests a few process events (like getting linked). process_events(Config) when is_list(Config) -> ?line start_tracer(Config), Self = self(), @@ -216,7 +216,7 @@ process_events(Config) when is_list(Config) -> ok. -schedule(doc) -> "Test sending scheduling events to a port."; +%% Test sending scheduling events to a port. schedule(Config) when is_list(Config) -> ?line start_tracer(Config), ?line Receiver = fun_spawn(fun receiver/0), @@ -244,7 +244,7 @@ run_fake_sched_test(Fun, Config) when is_function(Fun), is_list(Config) -> ?line Fun(Config) end. -fake_schedule(doc) -> "Tests time compensating fake out/in scheduling."; +%% Tests time compensating fake out/in scheduling. fake_schedule(Config) when is_list(Config) -> ?line run_fake_sched_test(fun fake_schedule_test/1, Config). @@ -306,8 +306,7 @@ fake_schedule_test(Config) when is_list(Config) -> %% ok. -fake_schedule_after_register(doc) -> - "Tests fake out/in scheduling contents."; +%% Tests fake out/in scheduling contents. fake_schedule_after_register(Config) when is_list(Config) -> ?line run_fake_sched_test(fun fake_schedule_after_register_test/1, Config). @@ -334,8 +333,7 @@ fake_schedule_after_register_test(Config) when is_list(Config) -> %% ok. -fake_schedule_after_getting_linked(doc) -> - "Tests fake out/in scheduling contents."; +%% Tests fake out/in scheduling contents. fake_schedule_after_getting_linked(Config) when is_list(Config) -> ?line run_fake_sched_test(fun fake_schedule_after_getting_linked_test/1, Config). @@ -363,8 +361,7 @@ fake_schedule_after_getting_linked_test(Config) when is_list(Config) -> %% ok. -fake_schedule_after_getting_unlinked(doc) -> - "Tests fake out/in scheduling contents."; +%% Tests fake out/in scheduling contents. fake_schedule_after_getting_unlinked(Config) when is_list(Config) -> ?line run_fake_sched_test(fun fake_schedule_after_getting_unlinked_test/1, Config). @@ -393,7 +390,7 @@ fake_schedule_after_getting_unlinked_test(Config) when is_list(Config) -> %% ok. -gc(doc) -> "Test sending garbage collection events to a port."; +%% Test sending garbage collection events to a port. gc(Config) when is_list(Config) -> ?line start_tracer(Config), ?line Garber = fun_spawn(fun garber/0, [{min_heap_size, 5000}]), @@ -412,8 +409,7 @@ gc(Config) when is_list(Config) -> ok. -default_tracer(doc) -> - "Test a port as default tracer."; +%% Test a port as default tracer. default_tracer(Config) when is_list(Config) -> ?line Tracer = start_tracer(Config), ?line TracerMonitor = erlang:monitor(process, Tracer), diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index ea04790860..755fa5ea99 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -50,8 +50,7 @@ all() -> %%% The test cases ------------------------------------------------------------- %%% -schedulers_alive(doc) -> ["Tests that all schedulers are actually used"]; -schedulers_alive(suite) -> []; +%% Tests that all schedulers are actually used schedulers_alive(Config) when is_list(Config) -> ?line Master = self(), ?line NoSchedulersOnline = erlang:system_flag( @@ -189,23 +188,13 @@ verify_all_schedulers_used({UsedSIDs, UsedSIDsLen} = State, NoSchedulers) -> ?line verify_all_schedulers_used(NewState, NoSchedulers) end. -node_container_refc_check(doc) -> []; -node_container_refc_check(suite) -> []; node_container_refc_check(Config) when is_list(Config) -> ?line node_container_SUITE:node_container_refc_check(node()), ?line ok. -long_timers(doc) -> - []; -long_timers(suite) -> - []; long_timers(Config) when is_list(Config) -> ?line ok = long_timers_test:check_result(). -pollset_size(doc) -> - []; -pollset_size(suite) -> - []; pollset_size(Config) when is_list(Config) -> ?line Name = pollset_size_testcase_initial_state_holder, ?line Mon = erlang:monitor(process, Name), @@ -251,10 +240,6 @@ pollset_size(Config) when is_list(Config) -> ++ integer_to_list(FinSize)} end. -check_io_debug(doc) -> - []; -check_io_debug(suite) -> - []; check_io_debug(Config) when is_list(Config) -> ?line case lists:keysearch(name, 1, erlang:system_info(check_io)) of {value, {name, erts_poll}} -> ?line check_io_debug_test(); -- cgit v1.2.3 From 20ae6a9238d3fd09587bd3f4179a8ae25f1e3aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 10 Mar 2016 18:19:16 +0100 Subject: Remove ?line macros --- erts/emulator/test/a_SUITE.erl | 40 +- erts/emulator/test/after_SUITE.erl | 146 +- erts/emulator/test/alloc_SUITE.erl | 43 +- erts/emulator/test/beam_literals_SUITE.erl | 161 +- erts/emulator/test/bif_SUITE.erl | 279 ++- erts/emulator/test/big_SUITE.erl | 92 +- erts/emulator/test/binary_SUITE.erl | 756 +++---- erts/emulator/test/bs_bit_binaries_SUITE.erl | 88 +- erts/emulator/test/bs_construct_SUITE.erl | 280 +-- erts/emulator/test/bs_match_bin_SUITE.erl | 50 +- erts/emulator/test/bs_match_int_SUITE.erl | 90 +- erts/emulator/test/bs_match_misc_SUITE.erl | 201 +- erts/emulator/test/bs_match_tail_SUITE.erl | 69 +- erts/emulator/test/bs_utf_SUITE.erl | 58 +- erts/emulator/test/busy_port_SUITE.erl | 544 +++-- erts/emulator/test/call_trace_SUITE.erl | 1521 +++++++------ erts/emulator/test/code_SUITE.erl | 678 +++--- erts/emulator/test/crypto_SUITE.erl | 344 ++- erts/emulator/test/ddll_SUITE.erl | 1408 ++++++------ erts/emulator/test/decode_packet_SUITE.erl | 458 ++-- erts/emulator/test/distribution_SUITE.erl | 2542 +++++++++++----------- erts/emulator/test/driver_SUITE.erl | 2890 ++++++++++++------------- erts/emulator/test/efile_SUITE.erl | 134 +- erts/emulator/test/erl_drv_thread_SUITE.erl | 86 +- erts/emulator/test/erl_link_SUITE.erl | 1436 ++++++------ erts/emulator/test/estone_SUITE.erl | 14 +- erts/emulator/test/evil_SUITE.erl | 419 ++-- erts/emulator/test/exception_SUITE.erl | 604 +++--- erts/emulator/test/float_SUITE.erl | 281 ++- erts/emulator/test/fun_SUITE.erl | 702 +++--- erts/emulator/test/fun_r13_SUITE.erl | 52 +- erts/emulator/test/guard_SUITE.erl | 268 +-- erts/emulator/test/hibernate_SUITE.erl | 207 +- erts/emulator/test/list_bif_SUITE.erl | 116 +- erts/emulator/test/match_spec_SUITE.erl | 1045 +++++---- erts/emulator/test/monitor_SUITE.erl | 1004 ++++----- erts/emulator/test/mtx_SUITE.erl | 333 ++- erts/emulator/test/nested_SUITE.erl | 66 +- erts/emulator/test/nif_SUITE.erl | 1124 +++++----- erts/emulator/test/nif_SUITE_data/nif_mod.erl | 2 +- erts/emulator/test/nif_SUITE_data/tester.erl | 3 +- erts/emulator/test/node_container_SUITE.erl | 1558 ++++++------- erts/emulator/test/nofrag_SUITE.erl | 72 +- erts/emulator/test/old_mod.erl | 32 +- erts/emulator/test/old_scheduler_SUITE.erl | 304 +-- erts/emulator/test/op_SUITE.erl | 236 +- erts/emulator/test/port_SUITE.erl | 1633 +++++++------- erts/emulator/test/ref_SUITE.erl | 14 +- erts/emulator/test/register_SUITE.erl | 25 +- erts/emulator/test/save_calls_SUITE.erl | 251 +-- erts/emulator/test/scheduler_SUITE.erl | 1650 +++++++------- erts/emulator/test/send_term_SUITE.erl | 139 +- erts/emulator/test/sensitive_SUITE.erl | 471 ++-- erts/emulator/test/signal_SUITE.erl | 381 ++-- erts/emulator/test/statistics_SUITE.erl | 343 ++- erts/emulator/test/system_info_SUITE.erl | 170 +- erts/emulator/test/time_SUITE.erl | 178 +- erts/emulator/test/timer_bif_SUITE.erl | 644 +++--- erts/emulator/test/trace_SUITE.erl | 1920 ++++++++-------- erts/emulator/test/trace_bif_SUITE.erl | 244 +-- erts/emulator/test/trace_call_count_SUITE.erl | 276 +-- erts/emulator/test/trace_call_time_SUITE.erl | 580 ++--- erts/emulator/test/trace_local_SUITE.erl | 1334 ++++++------ erts/emulator/test/trace_nif_SUITE.erl | 262 ++- erts/emulator/test/trace_port_SUITE.erl | 746 +++---- erts/emulator/test/unique_SUITE.erl | 220 +- erts/emulator/test/z_SUITE.erl | 245 +-- 67 files changed, 17137 insertions(+), 17425 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl index 8efb0fad53..5b3a213154 100644 --- a/erts/emulator/test/a_SUITE.erl +++ b/erts/emulator/test/a_SUITE.erl @@ -40,32 +40,30 @@ all() -> long_timers(Config) when is_list(Config) -> Dir = proplists:get_value(data_dir, Config), - ?line long_timers_test:start(Dir), - ?line {comment, - "Testcase started! This test will run in parallel with the " - "erts testsuite and ends in the z_SUITE:long_timers testcase."}. + long_timers_test:start(Dir), + {comment, "Testcase started! This test will run in parallel with the " + "erts testsuite and ends in the z_SUITE:long_timers testcase."}. pollset_size(Config) when is_list(Config) -> %% Ensure inet_gethost_native port program started, in order to %% allow other suites to use it... inet_gethost_native:gethostbyname("localhost"), - ?line Parent = self(), - ?line Go = make_ref(), - ?line spawn(fun () -> - Name = pollset_size_testcase_initial_state_holder, - true = register(Name, self()), - ChkIo = get_check_io_info(), - io:format("Initial: ~p~n", [ChkIo]), - Parent ! Go, - receive - {get_initial_check_io_result, Pid} -> - Pid ! {initial_check_io_result, ChkIo} - end - end), - ?line receive Go -> ok end, - ?line {comment, - "Testcase started! This test will run in parallel with the " - "erts testsuite and ends in the z_SUITE:pollset_size testcase."}. + Parent = self(), + Go = make_ref(), + spawn(fun () -> + Name = pollset_size_testcase_initial_state_holder, + true = register(Name, self()), + ChkIo = get_check_io_info(), + io:format("Initial: ~p~n", [ChkIo]), + Parent ! Go, + receive + {get_initial_check_io_result, Pid} -> + Pid ! {initial_check_io_result, ChkIo} + end + end), + receive Go -> ok end, + {comment, "Testcase started! This test will run in parallel with the " + "erts testsuite and ends in the z_SUITE:pollset_size testcase."}. %% %% Internal functions... diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl index 4efc3b68a0..a72cd8deea 100644 --- a/erts/emulator/test/after_SUITE.erl +++ b/erts/emulator/test/after_SUITE.erl @@ -45,43 +45,43 @@ all() -> %% Tests for an old round-off error in 'receive after'." t_after(Config) when is_list(Config) -> - ?line spawn(fun frequent_process/0), - ?line Period = test_server:minutes(1), - ?line Before = erlang:monotonic_time(), + spawn(fun frequent_process/0), + Period = test_server:minutes(1), + Before = erlang:monotonic_time(), receive - after Period -> - ?line After = erlang:monotonic_time(), - ?line report(Period, Before, After) - end. + after Period -> + After = erlang:monotonic_time(), + report(Period, Before, After) + end. report(Period, Before, After) -> case erlang:convert_time_unit(After - Before, native, 100*1000) / Period of - Percent when Percent > 100.10 -> - ct:fail({too_inaccurate, Percent}); - Percent when Percent < 100.0 -> - ct:fail({too_early, Percent}); - Percent -> - Comment = io_lib:format("Elapsed/expected: ~.2f %", [Percent]), - {comment, lists:flatten(Comment)} + Percent when Percent > 100.10 -> + ct:fail({too_inaccurate, Percent}); + Percent when Percent < 100.0 -> + ct:fail({too_early, Percent}); + Percent -> + Comment = io_lib:format("Elapsed/expected: ~.2f %", [Percent]), + {comment, lists:flatten(Comment)} end. frequent_process() -> receive - after 100 -> - ?line frequent_process() - end. + after 100 -> + frequent_process() + end. %% Test that 'receive after' works (doesn't hang). %% The test takes 10 seconds to complete. receive_after(Config) when is_list(Config) -> - ?line receive_after1(5000). + receive_after1(5000). receive_after1(1) -> - ?line io:format("Testing: receive after ~p~n", [1]), - ?line receive after 1 -> ok end; + io:format("Testing: receive after ~p~n", [1]), + receive after 1 -> ok end; receive_after1(N) -> - ?line io:format("Testing: receive after ~p~n", [N]), - ?line receive after N -> receive_after1(N div 2) end. + io:format("Testing: receive after ~p~n", [N]), + receive after N -> receive_after1(N div 2) end. receive_after_big(Config) when is_list(Config) -> %% Test that 'receive after' with a 32 bit number works. @@ -93,14 +93,14 @@ receive_after_big1(Timeout) -> erlang:yield(), spawn(fun() -> Self ! here_is_a_message end), ok = receive - here_is_a_message -> - ok - after Timeout -> - %% We test that the timeout can be set, - %% not that an timeout occurs after the appropriate delay - %% (48 days, 56 minutes, 48 seconds)! - timeout - end. + here_is_a_message -> + ok + after Timeout -> + %% We test that the timeout can be set, + %% not that an timeout occurs after the appropriate delay + %% (48 days, 56 minutes, 48 seconds)! + timeout + end. receive_after_big2() -> Self = self(), @@ -121,16 +121,16 @@ receive_after_big2() -> %% Test error cases for 'receive after'. receive_after_errors(Config) when is_list(Config) -> - ?line ?TryAfter(-1), - ?line ?TryAfter(0.0), - ?line ?TryAfter(3.14), - ?line ?TryAfter(16#100000000), - ?line ?TryAfter(392347129847294724972398472984729847129874), - ?line ?TryAfter(16#3fffffffffffffff), - ?line ?TryAfter(16#ffffffffffffffff), - ?line ?TryAfter(-16#100000000), - ?line ?TryAfter(-3891278094774921784123987129848), - ?line ?TryAfter(xxx), + ?TryAfter(-1), + ?TryAfter(0.0), + ?TryAfter(3.14), + ?TryAfter(16#100000000), + ?TryAfter(392347129847294724972398472984729847129874), + ?TryAfter(16#3fffffffffffffff), + ?TryAfter(16#ffffffffffffffff), + ?TryAfter(-16#100000000), + ?TryAfter(-3891278094774921784123987129848), + ?TryAfter(xxx), ok. try_after(Timeout) -> @@ -142,12 +142,12 @@ receive_var_zero(Config) when is_list(Config) -> self() ! y, Z = zero(), timeout = receive - z -> ok - after Z -> timeout - end, + z -> ok + after Z -> timeout + end, timeout = receive - after Z -> timeout - end, + after Z -> timeout + end, self() ! w, receive x -> ok; @@ -162,43 +162,43 @@ receive_zero(Config) when is_list(Config) -> self() ! x, self() ! y, timeout = receive - z -> ok - after 0 -> - timeout - end, + z -> ok + after 0 -> + timeout + end, self() ! w, timeout = receive after 0 -> timeout end, receive - x -> ok; - Other -> - ct:fail({bad_message,Other}) + x -> ok; + Other -> + ct:fail({bad_message,Other}) end. %% Test for catching invalid assertion in erl_message.c (in queue_message) %% This failed (dumped core) with debug-compiled emulator. multi_timeout(Config) when is_list(Config) -> - ?line P = spawn(?MODULE, timeout_g, []), - ?line P ! a, - ?line P ! b, - ?line receive - after 1000 -> ok - end, - ?line P ! c, - ?line receive - after 1000 -> ok - end, - ?line P ! d, + P = spawn(?MODULE, timeout_g, []), + P ! a, + P ! b, + receive + after 1000 -> ok + end, + P ! c, + receive + after 1000 -> ok + end, + P ! d, ok. timeout_g() -> - ?line receive - a -> ok + receive + a -> ok + end, + receive + after 100000 -> ok end, - ?line receive - after 100000 -> ok - end, ok. %% OTP-7493: Timeout for 32 bit numbers (such as 16#ffffFFFF) could @@ -237,8 +237,8 @@ receive_after_blast(Config) when is_list(Config) -> TimeoutTime = erlang:monotonic_time(milli_seconds) + 5000, lists:foreach(fun ({P, _}) -> P ! {go, TimeoutTime} end, PMs), lists:foreach(fun ({P, M}) -> - receive - {'DOWN', M, process, P, normal} -> - ok - end - end, PMs). + receive + {'DOWN', M, process, P, normal} -> + ok + end + end, PMs). diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index cb6c8d6012..1f690c5015 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -54,23 +54,23 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %% Testcases %% %% %% -basic(Cfg) -> ?line drv_case(Cfg). +basic(Cfg) -> drv_case(Cfg). -coalesce(Cfg) -> ?line drv_case(Cfg). +coalesce(Cfg) -> drv_case(Cfg). -threads(Cfg) -> ?line drv_case(Cfg). +threads(Cfg) -> drv_case(Cfg). -realloc_copy(Cfg) -> ?line drv_case(Cfg). +realloc_copy(Cfg) -> drv_case(Cfg). -bucket_index(Cfg) -> ?line drv_case(Cfg). +bucket_index(Cfg) -> drv_case(Cfg). -bucket_mask(Cfg) -> ?line drv_case(Cfg). +bucket_mask(Cfg) -> drv_case(Cfg). -rbtree(Cfg) -> ?line drv_case(Cfg). +rbtree(Cfg) -> drv_case(Cfg). -mseg_clear_cache(Cfg) -> ?line drv_case(Cfg). +mseg_clear_cache(Cfg) -> drv_case(Cfg). -cpool(Cfg) -> ?line drv_case(Cfg). +cpool(Cfg) -> drv_case(Cfg). migration(Cfg) -> case erlang:system_info(smp_support) of @@ -86,7 +86,7 @@ erts_mmap(Config) when is_list(Config) -> [erts_mmap_do(Config, SCO, SCRPM, SCRFSD) || SCO <-[true,false], SCRFSD <-[1234,0], SCRPM <- [true,false]]; {SkipOs,_} -> - ?line {skipped, + {skipped, lists:flatten(["Not run on " | io_lib:format("~p",[SkipOs])])} end. @@ -146,19 +146,19 @@ drv_case(Config) -> drv_case(Config, Mode, NodeOpts) when is_list(Config) -> case test_server:os_type() of {Family, _} when Family == unix; Family == win32 -> - ?line {ok, Node} = start_node(Config, NodeOpts), - ?line Self = self(), - ?line Ref = make_ref(), - ?line spawn_link(Node, + {ok, Node} = start_node(Config, NodeOpts), + Self = self(), + Ref = make_ref(), + spawn_link(Node, fun () -> Res = run_drv_case(Config, Mode), Self ! {Ref, Res} end), - ?line Result = receive {Ref, Rslt} -> Rslt end, - ?line stop_node(Node), - ?line Result; + Result = receive {Ref, Rslt} -> Rslt end, + stop_node(Node), + Result; SkipOs -> - ?line {skipped, + {skipped, lists:flatten(["Not run on " | io_lib:format("~p",[SkipOs])])} end. @@ -225,7 +225,6 @@ print_stats(migration) -> io:format("Number of blocks : ~p\n", [Btot]), io:format("Number of carriers: ~p\n", [Ctot]); - print_stats(_) -> ok. tuple_add(T1, T2) -> @@ -313,11 +312,11 @@ handle_result(_State, Result0) -> {failed, Comment} -> ct:fail(Comment); {skipped, Comment} -> - ?line {skipped, Comment}; + {skipped, Comment}; {succeeded, ""} -> - ?line succeeded; + succeeded; {succeeded, Comment} -> - ?line {comment, Comment}; + {comment, Comment}; continue -> continue end. diff --git a/erts/emulator/test/beam_literals_SUITE.erl b/erts/emulator/test/beam_literals_SUITE.erl index 054579fff2..b013ff5fbb 100644 --- a/erts/emulator/test/beam_literals_SUITE.erl +++ b/erts/emulator/test/beam_literals_SUITE.erl @@ -73,13 +73,13 @@ matching_bigs(Config) when is_list(Config) -> %% Test matching small numbers (both positive and negative). matching_smalls(Config) when is_list(Config) -> - ?line a = m_small(-42), - ?line b = m_small(0), - ?line c = m_small(105), - ?line d = m_small(-13), - ?line e = m_small(337848), - ?line other = m_small(324), - ?line other = m_small(-7), + a = m_small(-42), + b = m_small(0), + c = m_small(105), + d = m_small(-13), + e = m_small(337848), + other = m_small(324), + other = m_small(-7), ok. m_small(-42) -> a; @@ -92,13 +92,13 @@ m_small(_) -> other. %% Test matching small numbers (both positive and negative). %% Make sure that a jump table is used. matching_smalls_jt(Config) when is_list(Config) -> - ?line a = m_small_jt(-2), - ?line b = m_small_jt(-1), - ?line c = m_small_jt(0), - ?line d = m_small_jt(2), - ?line e = m_small_jt(3), - ?line other = m_small(324), - ?line other = m_small(-7), + a = m_small_jt(-2), + b = m_small_jt(-1), + c = m_small_jt(0), + d = m_small_jt(2), + e = m_small_jt(3), + other = m_small(324), + other = m_small(-7), ok. m_small_jt(-2) -> a; @@ -160,25 +160,25 @@ badmatch(Config) when is_list(Config) -> %% We are satisfied if we can load this module and run it. Big = id(32984798729847892498297824872982972978239874), Float = id(3.1415927), - ?line catch a = Big, - ?line catch b = Float, - ?line {'EXIT',{{badmatch,3879373498378993387},_}} = + catch a = Big, + catch b = Float, + {'EXIT',{{badmatch,3879373498378993387},_}} = (catch c = 3879373498378993387), - ?line {'EXIT',{{badmatch,7.0},_}} = (catch d = 7.0), - ?line case Big of - Big -> ok - end, - ?line case Float of - Float -> ok - end, + {'EXIT',{{badmatch,7.0},_}} = (catch d = 7.0), + case Big of + Big -> ok + end, + case Float of + Float -> ok + end, ok. case_clause(Config) when is_list(Config) -> - ?line {'EXIT',{{case_clause,337.0},_}} = (catch case_clause_float()), - ?line {'EXIT',{{try_clause,42.0},_}} = (catch try_case_clause_float()), - ?line {'EXIT',{{case_clause,37932749837839747383847398743789348734987},_}} = + {'EXIT',{{case_clause,337.0},_}} = (catch case_clause_float()), + {'EXIT',{{try_clause,42.0},_}} = (catch try_case_clause_float()), + {'EXIT',{{case_clause,37932749837839747383847398743789348734987},_}} = (catch case_clause_big()), - ?line {'EXIT',{{try_clause,977387349872349870423364354398566348},_}} = + {'EXIT',{{try_clause,977387349872349870423364354398566348},_}} = (catch try_case_clause_big()), ok. @@ -220,8 +220,8 @@ receiving(Config) when is_list(Config) -> %% Test type tests on literal values. literal_type_tests(Config) when is_list(Config) -> %% Generate an Erlang module with all different type of type tests. - ?line Tests = make_test([{T, L} || T <- type_tests(), L <- literals()]), - ?line Mod = literal_test, + Tests = make_test([{T, L} || T <- type_tests(), L <- literals()]), + Mod = literal_test, Anno = erl_anno:new(0), Func = {function, Anno, test, 0, [{clause,Anno,[],[],Tests}]}, Form = [{attribute,Anno,module,Mod}, @@ -229,22 +229,22 @@ literal_type_tests(Config) when is_list(Config) -> Func, {eof,Anno}], %% Print generated code for inspection. - ?line lists:foreach(fun (F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Form), + lists:foreach(fun (F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Form), %% Test compile:form/1. This implies full optimization (default). - ?line {ok,Mod,Code1} = compile:forms(Form), - ?line {module,Mod} = code:load_binary(Mod, Mod, Code1), - ?line Mod:test(), - ?line true = code:delete(Mod), - ?line code:purge(Mod), + {ok,Mod,Code1} = compile:forms(Form), + {module,Mod} = code:load_binary(Mod, Mod, Code1), + Mod:test(), + true = code:delete(Mod), + code:purge(Mod), %% Test compile:form/2. Turn off all optimizations. - ?line {ok,Mod,Code2} = compile:forms(Form, [binary,report,time, + {ok,Mod,Code2} = compile:forms(Form, [binary,report,time, no_copt,no_postopt]), - ?line {module,Mod} = code:load_binary(Mod, Mod, Code2), - ?line Mod:test(), - ?line true = code:delete(Mod), - ?line code:purge(Mod), + {module,Mod} = code:load_binary(Mod, Mod, Code2), + Mod:test(), + true = code:delete(Mod), + code:purge(Mod), ok. make_test([{is_function=T,L}|Ts]) -> @@ -294,34 +294,34 @@ type_tests() -> put_list(Config) when is_list(Config) -> %% put_list x0 Literal Reg - ?line [Config|8739757395764] = put_list_rqr(Config), - ?line {[Config|7779757395764],Config} = put_list_rqx(Config), - ?line [Config|98765432100000] = put_list_rqy(Config), + [Config|8739757395764] = put_list_rqr(Config), + {[Config|7779757395764],Config} = put_list_rqx(Config), + [Config|98765432100000] = put_list_rqy(Config), %% put_list x Literal Reg - ?line [Config|16#FFFFF77777137483769] = put_list_xqr(ignore, Config), - ?line {[Config|16#AAAAAFFFFF77777],{a,b},Config} = put_list_xqx({a,b}, Config), - ?line [Config|12777765432979879] = put_list_xqy(ignore, Config), + [Config|16#FFFFF77777137483769] = put_list_xqr(ignore, Config), + {[Config|16#AAAAAFFFFF77777],{a,b},Config} = put_list_xqx({a,b}, Config), + [Config|12777765432979879] = put_list_xqy(ignore, Config), %% put_list y Literal Reg - ?line [Config|17424134793676869867] = put_list_yqr(Config), - ?line {[Config|77424134793676869867],Config} = put_list_yqx(Config), - ?line {Config,[Config|16#BCDEFF4241676869867]} = put_list_yqy(Config), + [Config|17424134793676869867] = put_list_yqr(Config), + {[Config|77424134793676869867],Config} = put_list_yqx(Config), + {Config,[Config|16#BCDEFF4241676869867]} = put_list_yqy(Config), %% put_list Literal x0 Reg - ?line [42.0|Config] = put_list_qrr(Config), - ?line [Config,42.0|Config] = put_list_qrx(Config), - ?line [100.0|Config] = put_list_qry(Config), + [42.0|Config] = put_list_qrr(Config), + [Config,42.0|Config] = put_list_qrx(Config), + [100.0|Config] = put_list_qry(Config), %% put_list Literal x1 Reg - ?line [127.0|Config] = put_list_qxr({ignore,me}, Config), - ?line [Config,130.0|Config] = put_list_qxx(ignore, Config), - ?line [99.0|Config] = put_list_qxy(Config), + [127.0|Config] = put_list_qxr({ignore,me}, Config), + [Config,130.0|Config] = put_list_qxx(ignore, Config), + [99.0|Config] = put_list_qxy(Config), %% put_list Literal y0 Reg - ?line [200.0|Config] = put_list_qyr(Config), - ?line [Config,210.0|Config] = put_list_qyx(Config), - ?line [[300.0|Config]|Config] = put_list_qyy(Config), + [200.0|Config] = put_list_qyr(Config), + [Config,210.0|Config] = put_list_qyx(Config), + [[300.0|Config]|Config] = put_list_qyy(Config), ok. @@ -412,8 +412,8 @@ put_list_qyy(Config) -> [Res|Config]. fconv(Config) when is_list(Config) -> - ?line 5.0 = fconv_1(-34444444450.0), - ?line 13.0 = fconv_2(7.0), + 5.0 = fconv_1(-34444444450.0), + 13.0 = fconv_2(7.0), ok. fconv_1(F) when is_float(F) -> @@ -423,19 +423,18 @@ fconv_2(F) when is_float(F) -> 6.0 + F. literal_case_expression(Config) when is_list(Config) -> - ?line DataDir = proplists:get_value(data_dir, Config), - ?line Src = filename:join(DataDir, "literal_case_expression"), - ?line {ok,literal_case_expression=Mod,Code} = - compile:file(Src, [from_asm,binary]), - ?line {module,Mod} = code:load_binary(Mod, Src, Code), - ?line ok = Mod:x(), - ?line ok = Mod:y(), - ?line ok = Mod:zi1(), - ?line ok = Mod:zi2(), - ?line ok = Mod:za1(), - ?line ok = Mod:za2(), - ?line true = code:delete(Mod), - ?line code:purge(Mod), + DataDir = proplists:get_value(data_dir, Config), + Src = filename:join(DataDir, "literal_case_expression"), + {ok,literal_case_expression=Mod,Code} = compile:file(Src, [from_asm,binary]), + {module,Mod} = code:load_binary(Mod, Src, Code), + ok = Mod:x(), + ok = Mod:y(), + ok = Mod:zi1(), + ok = Mod:zi2(), + ok = Mod:za1(), + ok = Mod:za2(), + true = code:delete(Mod), + code:purge(Mod), ok. %% Test the i_increment instruction. @@ -447,27 +446,27 @@ increment(Config) when is_list(Config) -> Neg32 = -(1 bsl 27), Big32 = id(1 bsl 32), Result32 = (1 bsl 32) + (1 bsl 27), - ?line Result32 = Big32 + (1 bsl 27), - ?line Result32 = Big32 - Neg32, + Result32 = Big32 + (1 bsl 27), + Result32 = Big32 - Neg32, %% Same thing, but for the 64-bit emulator. Neg64 = -(1 bsl 59), Big64 = id(1 bsl 64), Result64 = (1 bsl 64) + (1 bsl 59), - ?line Result64 = Big64 + (1 bsl 59), - ?line Result64 = Big64 - Neg64, + Result64 = Big64 + (1 bsl 59), + Result64 = Big64 - Neg64, %% Test error handling for the i_increment instruction. Bad = id(bad), - ?line {'EXIT',{badarith,_}} = (catch Bad + 42), + {'EXIT',{badarith,_}} = (catch Bad + 42), %% Small operands, but a big result. Res32 = 1 bsl 27, Small32 = id(Res32-1), - ?line Res32 = Small32 + 1, + Res32 = Small32 + 1, Res64 = 1 bsl 59, Small64 = id(Res64-1), - ?line Res64 = Small64 + 1, + Res64 = Small64 + 1, ok. %% Help functions. diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 2768ad746f..43d00e699a 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -90,7 +90,7 @@ erl_bif_types_2(List) -> [_|_] -> io:put_chars("Bifs with bad arity\n"), io:format("~p\n", [BadArity]), - ?line ct:fail({length(BadArity),bad_arity}) + ct:fail({length(BadArity),bad_arity}) end. erl_bif_types_3(List) -> @@ -114,7 +114,7 @@ erl_bif_types_3(List) -> io:put_chars("Bifs with failing calls to erlang_bif_types:type/3 " "(or with bogus return values):\n"), io:format("~p\n", [BadSmokeTest]), - ?line ct:fail({length(BadSmokeTest),bad_smoke_test}) + ct:fail({length(BadSmokeTest),bad_smoke_test}) end. guard_bifs_in_erl_bif_types(_Config) -> @@ -332,32 +332,32 @@ check_stub({_,F,A}, B) -> end. t_list_to_existing_atom(Config) when is_list(Config) -> - ?line all = list_to_existing_atom("all"), - ?line ?MODULE = list_to_existing_atom(?MODULE_STRING), - ?line UnlikelyStr = "dsfj923874390867er869fds9864y97jhg3973qerueoru", + all = list_to_existing_atom("all"), + ?MODULE = list_to_existing_atom(?MODULE_STRING), + UnlikelyStr = "dsfj923874390867er869fds9864y97jhg3973qerueoru", try - ?line list_to_existing_atom(UnlikelyStr), - ?line ct:fail(atom_exists) + list_to_existing_atom(UnlikelyStr), + ct:fail(atom_exists) catch error:badarg -> ok end, %% The compiler has become smarter! We need the call to id/1 in %% the next line. - ?line UnlikelyAtom = list_to_atom(id(UnlikelyStr)), - ?line UnlikelyAtom = list_to_existing_atom(UnlikelyStr), + UnlikelyAtom = list_to_atom(id(UnlikelyStr)), + UnlikelyAtom = list_to_existing_atom(UnlikelyStr), ok. os_env(Config) when is_list(Config) -> - ?line EnvVar1 = "MjhgvFDrresdCghN mnjkUYg vfrD", - ?line false = os:getenv(EnvVar1), - ?line true = os:putenv(EnvVar1, "mors"), - ?line "mors" = os:getenv(EnvVar1), - ?line true = os:putenv(EnvVar1, ""), - ?line case os:getenv(EnvVar1) of - "" -> ?line ok; - false -> ?line ok; - BadVal -> ?line ct:fail(BadVal) + EnvVar1 = "MjhgvFDrresdCghN mnjkUYg vfrD", + false = os:getenv(EnvVar1), + true = os:putenv(EnvVar1, "mors"), + "mors" = os:getenv(EnvVar1), + true = os:putenv(EnvVar1, ""), + case os:getenv(EnvVar1) of + "" -> ok; + false -> ok; + BadVal -> ct:fail(BadVal) end, true = os:putenv(EnvVar1, "mors"), true = os:unsetenv(EnvVar1), @@ -365,16 +365,16 @@ os_env(Config) when is_list(Config) -> true = os:unsetenv(EnvVar1), % unset unset variable %% os:putenv, os:getenv and os:unsetenv currently use a temp %% buffer of size 1024 for storing key+value - ?line os_env_long(1010, 1030, "hej hopp"). + os_env_long(1010, 1030, "hej hopp"). os_env_long(Min, Max, _Value) when Min > Max -> - ?line ok; + ok; os_env_long(Min, Max, Value) -> - ?line EnvVar = lists:duplicate(Min, $X), - ?line true = os:putenv(EnvVar, Value), - ?line Value = os:getenv(EnvVar), + EnvVar = lists:duplicate(Min, $X), + true = os:putenv(EnvVar, Value), + Value = os:getenv(EnvVar), true = os:unsetenv(EnvVar), - ?line os_env_long(Min+1, Max, Value). + os_env_long(Min+1, Max, Value). %% Test that string:to_integer does not Halloc in wrong order. otp_7526(Config) when is_list(Config) -> @@ -391,15 +391,15 @@ do_test_7526(N,M) -> {Self, Ref} = {self(), make_ref()}, T = erlang:make_tuple(M,0), spawn_opt(fun()-> - L = iterate_7526(N, []), - BadList = [X || X <- L, X =/= 9223372036854775808], - BadLen = length(BadList), - M = length(tuple_to_list(T)), - %%io:format("~b bad conversions: ~p~n", [BadLen, BadList]), - Self ! {done, Ref, BadLen} - end, - [link,{fullsweep_after,0}]), - receive {done, Ref, Len} -> Len end. + L = iterate_7526(N, []), + BadList = [X || X <- L, X =/= 9223372036854775808], + BadLen = length(BadList), + M = length(tuple_to_list(T)), + %%io:format("~b bad conversions: ~p~n", [BadLen, BadList]), + Self ! {done, Ref, BadLen} + end, + [link,{fullsweep_after,0}]), + receive {done, Ref, Len} -> Len end. test_7526(0) -> @@ -423,57 +423,53 @@ binary_to_atom(Config) when is_list(Config) -> LongBin = list_to_binary(Long), %% latin1 - ?line '' = test_binary_to_atom(<<>>, latin1), - ?line '\377' = test_binary_to_atom(<<255>>, latin1), - ?line HalfLongAtom = test_binary_to_atom(HalfLongBin, latin1), - ?line LongAtom = test_binary_to_atom(LongBin, latin1), + '' = test_binary_to_atom(<<>>, latin1), + '\377' = test_binary_to_atom(<<255>>, latin1), + HalfLongAtom = test_binary_to_atom(HalfLongBin, latin1), + LongAtom = test_binary_to_atom(LongBin, latin1), %% utf8 - ?line '' = test_binary_to_atom(<<>>, utf8), - ?line HalfLongAtom = test_binary_to_atom(HalfLongBin, utf8), - ?line HalfLongAtom = test_binary_to_atom(HalfLongBin, unicode), - ?line [] = [C || C <- lists:seq(128, 255), + '' = test_binary_to_atom(<<>>, utf8), + HalfLongAtom = test_binary_to_atom(HalfLongBin, utf8), + HalfLongAtom = test_binary_to_atom(HalfLongBin, unicode), + [] = [C || C <- lists:seq(128, 255), begin list_to_atom([C]) =/= test_binary_to_atom(<>, utf8) end], %% badarg failures. - ?line fail_binary_to_atom(atom), - ?line fail_binary_to_atom(42), - ?line fail_binary_to_atom({a,b,c}), - ?line fail_binary_to_atom([1,2,3]), - ?line fail_binary_to_atom([]), - ?line fail_binary_to_atom(42.0), - ?line fail_binary_to_atom(self()), - ?line fail_binary_to_atom(make_ref()), - ?line fail_binary_to_atom(<<0:7>>), - ?line fail_binary_to_atom(<<42:13>>), - ?line ?BADARG(binary_to_atom(id(<<>>), blurf)), - ?line ?BADARG(binary_to_atom(id(<<>>), [])), + fail_binary_to_atom(atom), + fail_binary_to_atom(42), + fail_binary_to_atom({a,b,c}), + fail_binary_to_atom([1,2,3]), + fail_binary_to_atom([]), + fail_binary_to_atom(42.0), + fail_binary_to_atom(self()), + fail_binary_to_atom(make_ref()), + fail_binary_to_atom(<<0:7>>), + fail_binary_to_atom(<<42:13>>), + ?BADARG(binary_to_atom(id(<<>>), blurf)), + ?BADARG(binary_to_atom(id(<<>>), [])), %% Bad UTF8 sequences. - ?line ?BADARG(binary_to_atom(id(<<255>>), utf8)), - ?line ?BADARG(binary_to_atom(id(<<255,0>>), utf8)), - ?line ?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0. - ?line [?BADARG(binary_to_atom(<>, utf8)) || - C <- lists:seq(256, 16#D7FF)], - ?line [?BADARG(binary_to_atom(<>, utf8)) || - C <- lists:seq(16#E000, 16#FFFD)], - ?line [?BADARG(binary_to_atom(<>, utf8)) || - C <- lists:seq(16#10000, 16#8FFFF)], - ?line [?BADARG(binary_to_atom(<>, utf8)) || - C <- lists:seq(16#90000, 16#10FFFF)], + ?BADARG(binary_to_atom(id(<<255>>), utf8)), + ?BADARG(binary_to_atom(id(<<255,0>>), utf8)), + ?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0. + [?BADARG(binary_to_atom(<>, utf8)) || C <- lists:seq(256, 16#D7FF)], + [?BADARG(binary_to_atom(<>, utf8)) || C <- lists:seq(16#E000, 16#FFFD)], + [?BADARG(binary_to_atom(<>, utf8)) || C <- lists:seq(16#10000, 16#8FFFF)], + [?BADARG(binary_to_atom(<>, utf8)) || C <- lists:seq(16#90000, 16#10FFFF)], %% system_limit failures. - ?line ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)), - ?line ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255,0>>), utf8)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, latin1)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, latin1)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, latin1)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, utf8)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, utf8)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, utf8)), + ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)), + ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255,0>>), utf8)), + ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, latin1)), + ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, latin1)), + ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, latin1)), + ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, utf8)), + ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, utf8)), + ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, utf8)), ok. test_binary_to_atom(Bin0, Encoding) -> @@ -486,49 +482,49 @@ test_binary_to_atom(Bin0, Encoding) -> fail_binary_to_atom(Bin) -> try - binary_to_atom(Bin, latin1) + binary_to_atom(Bin, latin1) catch - error:badarg -> - ok + error:badarg -> + ok end, try - binary_to_atom(Bin, utf8) + binary_to_atom(Bin, utf8) catch - error:badarg -> - ok + error:badarg -> + ok end, try - binary_to_existing_atom(Bin, latin1) + binary_to_existing_atom(Bin, latin1) catch - error:badarg -> - ok + error:badarg -> + ok end, try - binary_to_existing_atom(Bin, utf8) + binary_to_existing_atom(Bin, utf8) catch - error:badarg -> - ok + error:badarg -> + ok end. binary_to_existing_atom(Config) when is_list(Config) -> - ?line UnlikelyBin = <<"ou0897979655678dsfj923874390867er869fds973qerueoru">>, + UnlikelyBin = <<"ou0897979655678dsfj923874390867er869fds973qerueoru">>, try - ?line binary_to_existing_atom(UnlikelyBin, latin1), - ?line ct:fail(atom_exists) + binary_to_existing_atom(UnlikelyBin, latin1), + ct:fail(atom_exists) catch error:badarg -> ok end, try - ?line binary_to_existing_atom(UnlikelyBin, utf8), - ?line ct:fail(atom_exists) + binary_to_existing_atom(UnlikelyBin, utf8), + ct:fail(atom_exists) catch error:badarg -> ok end, - ?line UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1), - ?line UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1), + UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1), + UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1), ok. @@ -541,32 +537,32 @@ atom_to_binary(Config) when is_list(Config) -> LongBin = list_to_binary(Long), %% latin1 - ?line <<>> = atom_to_binary('', latin1), - ?line <<"abc">> = atom_to_binary(abc, latin1), - ?line <<127>> = atom_to_binary('\177', latin1), - ?line HalfLongBin = atom_to_binary(HalfLongAtom, latin1), - ?line LongBin = atom_to_binary(LongAtom, latin1), + <<>> = atom_to_binary('', latin1), + <<"abc">> = atom_to_binary(abc, latin1), + <<127>> = atom_to_binary('\177', latin1), + HalfLongBin = atom_to_binary(HalfLongAtom, latin1), + LongBin = atom_to_binary(LongAtom, latin1), %% utf8. - ?line <<>> = atom_to_binary('', utf8), - ?line <<>> = atom_to_binary('', unicode), - ?line <<127>> = atom_to_binary('\177', utf8), - ?line <<"abcdef">> = atom_to_binary(abcdef, utf8), - ?line HalfLongBin = atom_to_binary(HalfLongAtom, utf8), - ?line LongAtomBin = atom_to_binary(LongAtom, utf8), - ?line verify_long_atom_bin(LongAtomBin, 0), + <<>> = atom_to_binary('', utf8), + <<>> = atom_to_binary('', unicode), + <<127>> = atom_to_binary('\177', utf8), + <<"abcdef">> = atom_to_binary(abcdef, utf8), + HalfLongBin = atom_to_binary(HalfLongAtom, utf8), + LongAtomBin = atom_to_binary(LongAtom, utf8), + verify_long_atom_bin(LongAtomBin, 0), %% Failing cases. - ?line fail_atom_to_binary(<<1>>), - ?line fail_atom_to_binary(42), - ?line fail_atom_to_binary({a,b,c}), - ?line fail_atom_to_binary([1,2,3]), - ?line fail_atom_to_binary([]), - ?line fail_atom_to_binary(42.0), - ?line fail_atom_to_binary(self()), - ?line fail_atom_to_binary(make_ref()), - ?line ?BADARG(atom_to_binary(id(a), blurf)), - ?line ?BADARG(atom_to_binary(id(b), [])), + fail_atom_to_binary(<<1>>), + fail_atom_to_binary(42), + fail_atom_to_binary({a,b,c}), + fail_atom_to_binary([1,2,3]), + fail_atom_to_binary([]), + fail_atom_to_binary(42.0), + fail_atom_to_binary(self()), + fail_atom_to_binary(make_ref()), + ?BADARG(atom_to_binary(id(a), blurf)), + ?BADARG(atom_to_binary(id(b), [])), ok. verify_long_atom_bin(<>, I) -> @@ -575,43 +571,42 @@ verify_long_atom_bin(<<>>, 255) -> ok. fail_atom_to_binary(Term) -> try - atom_to_binary(Term, latin1) + atom_to_binary(Term, latin1) catch - error:badarg -> - ok + error:badarg -> + ok end, try - atom_to_binary(Term, utf8) + atom_to_binary(Term, utf8) catch - error:badarg -> - ok + error:badarg -> + ok end. min_max(Config) when is_list(Config) -> - ?line a = erlang:min(id(a), a), - ?line a = erlang:min(id(a), b), - ?line a = erlang:min(id(b), a), - ?line b = erlang:min(id(b), b), - ?line a = erlang:max(id(a), a), - ?line b = erlang:max(id(a), b), - ?line b = erlang:max(id(b), a), - ?line b = erlang:max(id(b), b), - - ?line 42.0 = erlang:min(42.0, 42), - ?line 42.0 = erlang:max(42.0, 42), + a = erlang:min(id(a), a), + a = erlang:min(id(a), b), + a = erlang:min(id(b), a), + b = erlang:min(id(b), b), + a = erlang:max(id(a), a), + b = erlang:max(id(a), b), + b = erlang:max(id(b), a), + b = erlang:max(id(b), b), + + 42.0 = erlang:min(42.0, 42), + 42.0 = erlang:max(42.0, 42), %% And now (R14) they are also autoimported! - ?line a = min(id(a), a), - ?line a = min(id(a), b), - ?line a = min(id(b), a), - ?line b = min(id(b), b), - ?line a = max(id(a), a), - ?line b = max(id(a), b), - ?line b = max(id(b), a), - ?line b = max(id(b), b), - - ?line 42.0 = min(42.0, 42), - ?line 42.0 = max(42.0, 42), - + a = min(id(a), a), + a = min(id(a), b), + a = min(id(b), a), + b = min(id(b), b), + a = max(id(a), a), + b = max(id(a), b), + b = max(id(b), a), + b = max(id(b), b), + + 42.0 = min(42.0, 42), + 42.0 = max(42.0, 42), ok. diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl index 57f4e502a5..d331083661 100644 --- a/erts/emulator/test/big_SUITE.erl +++ b/erts/emulator/test/big_SUITE.erl @@ -99,7 +99,7 @@ test(File) -> test(File, [node()]). test(File, Nodes) -> - ?line {ok,Fd} = file:open(File, [read]), + {ok,Fd} = file:open(File, [read]), Res = test(File, Fd, Nodes), file:close(Fd), case Res of @@ -136,7 +136,7 @@ multi_match(Ns, Expr) -> multi_match(Ns, Expr, []). multi_match([Node|Ns], Expr, Rs) -> - ?line X = rpc:call(Node, big_SUITE, eval, [Expr]), + X = rpc:call(Node, big_SUITE, eval, [Expr]), if X == 0 -> multi_match(Ns, Expr, Rs); true -> multi_match(Ns, Expr, [{Node,X}|Rs]) end; @@ -228,10 +228,10 @@ lcm(Q, R) -> %% Test case t_div cut in from R2D test suite. t_div(Config) when is_list(Config) -> - ?line 'try'(fun() -> 98765432101234 div 98765432101235 end, 0), + 'try'(fun() -> 98765432101234 div 98765432101235 end, 0), % Big remainder, small quotient. - ?line 'try'(fun() -> 339254531512 div 68719476736 end, 4), + 'try'(fun() -> 339254531512 div 68719476736 end, 4), ok. 'try'(Fun, Result) -> @@ -255,50 +255,50 @@ init(ReplyTo, Fun, _Filler) -> big_literals(Config) when is_list(Config) -> %% Note: The literal test cannot be compiler on a pre-R4 Beam emulator, %% so we compile it now. - ?line DataDir = proplists:get_value(data_dir, Config), - ?line Test = filename:join(DataDir, "literal_test"), - ?line {ok, Mod, Bin} = compile:file(Test, [binary]), - ?line {module, Mod} = code:load_binary(Mod, Mod, Bin), - ?line ok = Mod:t(), + DataDir = proplists:get_value(data_dir, Config), + Test = filename:join(DataDir, "literal_test"), + {ok, Mod, Bin} = compile:file(Test, [binary]), + {module, Mod} = code:load_binary(Mod, Mod, Bin), + ok = Mod:t(), ok. %% OTP-2436, part 1 big_float_1(Config) when is_list(Config) -> %% F is a number very close to a maximum float. - ?line F = id(1.7e308), - ?line I = trunc(F), - ?line true = (I == F), - ?line false = (I /= F), - ?line true = (I > F/2), - ?line false = (I =< F/2), - ?line true = (I*2 >= F), - ?line false = (I*2 < F), - ?line true = (I*I > F), - ?line false = (I*I =< F), - - ?line true = (F == I), - ?line false = (F /= I), - ?line false = (F/2 > I), - ?line true = (F/2 =< I), - ?line false = (F >= I*2), - ?line true = (F < I*2), - ?line false = (F > I*I), - ?line true = (F =< I*I), + F = id(1.7e308), + I = trunc(F), + true = (I == F), + false = (I /= F), + true = (I > F/2), + false = (I =< F/2), + true = (I*2 >= F), + false = (I*2 < F), + true = (I*I > F), + false = (I*I =< F), + + true = (F == I), + false = (F /= I), + false = (F/2 > I), + true = (F/2 =< I), + false = (F >= I*2), + true = (F < I*2), + false = (F > I*I), + true = (F =< I*I), ok. %% "OTP-2436, part 2 big_float_2(Config) when is_list(Config) -> - ?line F = id(1.7e308), - ?line I = trunc(F), - ?line {'EXIT', _} = (catch 1/(2*I)), - ?line _Ignore = 2/I, - ?line {'EXIT', _} = (catch 4/(2*I)), + F = id(1.7e308), + I = trunc(F), + {'EXIT', _} = (catch 1/(2*I)), + _Ignore = 2/I, + {'EXIT', _} = (catch 4/(2*I)), ok. %% OTP-3256 shift_limit_1(Config) when is_list(Config) -> - ?line case catch (id(1) bsl 100000000) of + case catch (id(1) bsl 100000000) of {'EXIT', {system_limit, _}} -> ok end, @@ -327,16 +327,16 @@ powmod(A, B, C) -> end. system_limit(Config) when is_list(Config) -> - ?line Maxbig = maxbig(), - ?line {'EXIT',{system_limit,_}} = (catch Maxbig+1), - ?line {'EXIT',{system_limit,_}} = (catch -Maxbig-1), - ?line {'EXIT',{system_limit,_}} = (catch 2*Maxbig), - ?line {'EXIT',{system_limit,_}} = (catch bnot Maxbig), - ?line {'EXIT',{system_limit,_}} = (catch apply(erlang, id('bnot'), [Maxbig])), - ?line {'EXIT',{system_limit,_}} = (catch Maxbig bsl 2), - ?line {'EXIT',{system_limit,_}} = (catch apply(erlang, id('bsl'), [Maxbig,2])), - ?line {'EXIT',{system_limit,_}} = (catch id(1) bsl (1 bsl 45)), - ?line {'EXIT',{system_limit,_}} = (catch id(1) bsl (1 bsl 69)), + Maxbig = maxbig(), + {'EXIT',{system_limit,_}} = (catch Maxbig+1), + {'EXIT',{system_limit,_}} = (catch -Maxbig-1), + {'EXIT',{system_limit,_}} = (catch 2*Maxbig), + {'EXIT',{system_limit,_}} = (catch bnot Maxbig), + {'EXIT',{system_limit,_}} = (catch apply(erlang, id('bnot'), [Maxbig])), + {'EXIT',{system_limit,_}} = (catch Maxbig bsl 2), + {'EXIT',{system_limit,_}} = (catch apply(erlang, id('bsl'), [Maxbig,2])), + {'EXIT',{system_limit,_}} = (catch id(1) bsl (1 bsl 45)), + {'EXIT',{system_limit,_}} = (catch id(1) bsl (1 bsl 69)), ok. maxbig() -> @@ -347,7 +347,7 @@ maxbig() -> id(I) -> I. toobig(Config) when is_list(Config) -> - ?line {'EXIT',{{badmatch,_},_}} = (catch toobig()), + {'EXIT',{{badmatch,_},_}} = (catch toobig()), ok. toobig() -> @@ -358,7 +358,7 @@ toobig() -> %% Tests for DIV/REM bug reported in OTP-6692 otp_6692(Config) when is_list(Config)-> - ?line loop1(1,1000). + loop1(1,1000). fact(N) -> fact(N,1). diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index bc1cf62883..fcd49e43d3 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -106,7 +106,7 @@ end_per_testcase(_Func, _Config) -> copy_terms(Config) when is_list(Config) -> Self = self(), - ?line Pid = spawn_link(fun() -> copy_server(Self) end), + Pid = spawn_link(fun() -> copy_server(Self) end), F = fun(Term) -> Pid ! Term, receive @@ -116,7 +116,7 @@ copy_terms(Config) when is_list(Config) -> ct:fail(bad_term) end end, - ?line test_terms(F), + test_terms(F), ok. copy_server(Parent) -> @@ -130,151 +130,151 @@ copy_server(Parent) -> %% using flat lists. conversions(Config) when is_list(Config) -> - ?line test_bin([]), - ?line test_bin([1]), - ?line test_bin([1, 2]), - ?line test_bin([1, 2, 3]), - ?line test_bin(lists:seq(0, ?heap_binary_size)), - ?line test_bin(lists:seq(0, ?heap_binary_size+1)), - ?line test_bin(lists:seq(0, 255)), - ?line test_bin(lists:duplicate(50000, $@)), + test_bin([]), + test_bin([1]), + test_bin([1, 2]), + test_bin([1, 2, 3]), + test_bin(lists:seq(0, ?heap_binary_size)), + test_bin(lists:seq(0, ?heap_binary_size+1)), + test_bin(lists:seq(0, 255)), + test_bin(lists:duplicate(50000, $@)), %% Binary in list. List = [1,2,3,4,5], - ?line B1 = make_sub_binary(list_to_binary(List)), - ?line 5 = size(B1), - ?line 5 = size(make_unaligned_sub_binary(B1)), - ?line 40 = bit_size(B1), - ?line 40 = bit_size(make_unaligned_sub_binary(B1)), - ?line B2 = list_to_binary([42,B1,19]), - ?line B2 = list_to_binary([42,make_unaligned_sub_binary(B1),19]), - ?line B2 = iolist_to_binary(B2), - ?line B2 = iolist_to_binary(make_unaligned_sub_binary(B2)), - ?line 7 = size(B2), - ?line 7 = size(make_sub_binary(B2)), - ?line 56 = bit_size(B2), - ?line 56 = bit_size(make_sub_binary(B2)), - ?line [42,1,2,3,4,5,19] = binary_to_list(B2), - ?line [42,1,2,3,4,5,19] = binary_to_list(make_sub_binary(B2)), - ?line [42,1,2,3,4,5,19] = binary_to_list(make_unaligned_sub_binary(B2)), - ?line [42,1,2,3,4,5,19] = bitstring_to_list(B2), - ?line [42,1,2,3,4,5,19] = bitstring_to_list(make_sub_binary(B2)), - ?line [42,1,2,3,4,5,19] = bitstring_to_list(make_unaligned_sub_binary(B2)), + B1 = make_sub_binary(list_to_binary(List)), + 5 = size(B1), + 5 = size(make_unaligned_sub_binary(B1)), + 40 = bit_size(B1), + 40 = bit_size(make_unaligned_sub_binary(B1)), + B2 = list_to_binary([42,B1,19]), + B2 = list_to_binary([42,make_unaligned_sub_binary(B1),19]), + B2 = iolist_to_binary(B2), + B2 = iolist_to_binary(make_unaligned_sub_binary(B2)), + 7 = size(B2), + 7 = size(make_sub_binary(B2)), + 56 = bit_size(B2), + 56 = bit_size(make_sub_binary(B2)), + [42,1,2,3,4,5,19] = binary_to_list(B2), + [42,1,2,3,4,5,19] = binary_to_list(make_sub_binary(B2)), + [42,1,2,3,4,5,19] = binary_to_list(make_unaligned_sub_binary(B2)), + [42,1,2,3,4,5,19] = bitstring_to_list(B2), + [42,1,2,3,4,5,19] = bitstring_to_list(make_sub_binary(B2)), + [42,1,2,3,4,5,19] = bitstring_to_list(make_unaligned_sub_binary(B2)), ok. test_bin(List) -> - ?line Size = length(List), - ?line Bin = list_to_binary(List), - ?line Bin = iolist_to_binary(List), - ?line Bin = list_to_bitstring(List), - ?line Size = iolist_size(List), - ?line Size = iolist_size(Bin), - ?line Size = iolist_size(make_unaligned_sub_binary(Bin)), - ?line Size = size(Bin), - ?line Size = size(make_sub_binary(Bin)), - ?line Size = size(make_unaligned_sub_binary(Bin)), - ?line List = binary_to_list(Bin), - ?line List = binary_to_list(make_sub_binary(Bin)), - ?line List = binary_to_list(make_unaligned_sub_binary(Bin)), - ?line List = bitstring_to_list(Bin), - ?line List = bitstring_to_list(make_unaligned_sub_binary(Bin)). + Size = length(List), + Bin = list_to_binary(List), + Bin = iolist_to_binary(List), + Bin = list_to_bitstring(List), + Size = iolist_size(List), + Size = iolist_size(Bin), + Size = iolist_size(make_unaligned_sub_binary(Bin)), + Size = size(Bin), + Size = size(make_sub_binary(Bin)), + Size = size(make_unaligned_sub_binary(Bin)), + List = binary_to_list(Bin), + List = binary_to_list(make_sub_binary(Bin)), + List = binary_to_list(make_unaligned_sub_binary(Bin)), + List = bitstring_to_list(Bin), + List = bitstring_to_list(make_unaligned_sub_binary(Bin)). %% Tests list_to_binary/1, iolist_to_binary/1, list_to_bitstr/1, binary_to_list/1,3, %% bitstr_to_list/1, and size/1, using deep lists. deep_lists(Config) when is_list(Config) -> - ?line test_deep_list(["abc"]), - ?line test_deep_list([[12,13,[123,15]]]), - ?line test_deep_list([[12,13,[lists:seq(0, 255), []]]]), + test_deep_list(["abc"]), + test_deep_list([[12,13,[123,15]]]), + test_deep_list([[12,13,[lists:seq(0, 255), []]]]), ok. test_deep_list(List) -> - ?line FlatList = lists:flatten(List), - ?line Size = length(FlatList), - ?line Bin = list_to_binary(List), - ?line Bin = iolist_to_binary(List), - ?line Bin = iolist_to_binary(Bin), - ?line Bin = list_to_bitstring(List), - ?line Size = size(Bin), - ?line Size = iolist_size(List), - ?line Size = iolist_size(FlatList), - ?line Size = iolist_size(Bin), - ?line Bitsize = bit_size(Bin), - ?line Bitsize = 8*Size, - ?line FlatList = binary_to_list(Bin), - ?line FlatList = bitstring_to_list(Bin), + FlatList = lists:flatten(List), + Size = length(FlatList), + Bin = list_to_binary(List), + Bin = iolist_to_binary(List), + Bin = iolist_to_binary(Bin), + Bin = list_to_bitstring(List), + Size = size(Bin), + Size = iolist_size(List), + Size = iolist_size(FlatList), + Size = iolist_size(Bin), + Bitsize = bit_size(Bin), + Bitsize = 8*Size, + FlatList = binary_to_list(Bin), + FlatList = bitstring_to_list(Bin), io:format("testing plain binary..."), - ?line t_binary_to_list_3(FlatList, Bin, 1, Size), + t_binary_to_list_3(FlatList, Bin, 1, Size), io:format("testing unaligned sub binary..."), - ?line t_binary_to_list_3(FlatList, make_unaligned_sub_binary(Bin), 1, Size). + t_binary_to_list_3(FlatList, make_unaligned_sub_binary(Bin), 1, Size). t_binary_to_list_3(List, Bin, From, To) -> - ?line going_up(List, Bin, From, To), - ?line going_down(List, Bin, From, To), - ?line going_center(List, Bin, From, To). + going_up(List, Bin, From, To), + going_down(List, Bin, From, To), + going_center(List, Bin, From, To). going_up(List, Bin, From, To) when From =< To -> - ?line List = binary_to_list(Bin, From, To), - ?line going_up(tl(List), Bin, From+1, To); + List = binary_to_list(Bin, From, To), + going_up(tl(List), Bin, From+1, To); going_up(_List, _Bin, From, To) when From > To -> ok. going_down(List, Bin, From, To) when To > 0-> - ?line compare(List, binary_to_list(Bin, From, To), To-From+1), - ?line going_down(List, Bin, From, To-1); + compare(List, binary_to_list(Bin, From, To), To-From+1), + going_down(List, Bin, From, To-1); going_down(_List, _Bin, _From, _To) -> ok. going_center(List, Bin, From, To) when From >= To -> - ?line compare(List, binary_to_list(Bin, From, To), To-From+1), - ?line going_center(tl(List), Bin, From+1, To-1); + compare(List, binary_to_list(Bin, From, To), To-From+1), + going_center(tl(List), Bin, From+1, To-1); going_center(_List, _Bin, _From, _To) -> ok. compare([X|Rest1], [X|Rest2], Left) when Left > 0 -> - ?line compare(Rest1, Rest2, Left-1); + compare(Rest1, Rest2, Left-1); compare([_X|_], [_Y|_], _Left) -> ct:fail("compare fail"); compare(_List, [], 0) -> ok. deep_bitstr_lists(Config) when is_list(Config) -> - ?line {<<7:3>>,[<<7:3>>]} = test_deep_bitstr([<<7:3>>]), - ?line {<<42,5:3>>=Bin,[42,<<5:3>>]=List} = test_deep_bitstr([42,<<5:3>>]), - ?line {Bin,List} = test_deep_bitstr([42|<<5:3>>]), - ?line {Bin,List} = test_deep_bitstr([<<42,5:3>>]), - ?line {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>|<<5:3>>]), - ?line {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>,<<5:3>>]), - ?line {Bin,List} = test_deep_bitstr([[<<1:3>>,<<10:5>>],[],<<5:3>>]), - ?line {Bin,List} = test_deep_bitstr([[[<<1:3>>]|<<10:5>>],[],<<5:3>>]), - ?line {Bin,List} = test_deep_bitstr([[<<0:1>>,<<0:1>>,[],<<1:1>>,<<10:5>>], + {<<7:3>>,[<<7:3>>]} = test_deep_bitstr([<<7:3>>]), + {<<42,5:3>>=Bin,[42,<<5:3>>]=List} = test_deep_bitstr([42,<<5:3>>]), + {Bin,List} = test_deep_bitstr([42|<<5:3>>]), + {Bin,List} = test_deep_bitstr([<<42,5:3>>]), + {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>|<<5:3>>]), + {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>,<<5:3>>]), + {Bin,List} = test_deep_bitstr([[<<1:3>>,<<10:5>>],[],<<5:3>>]), + {Bin,List} = test_deep_bitstr([[[<<1:3>>]|<<10:5>>],[],<<5:3>>]), + {Bin,List} = test_deep_bitstr([[<<0:1>>,<<0:1>>,[],<<1:1>>,<<10:5>>], <<1:1>>,<<0:1>>,<<1:1>>]), ok. test_deep_bitstr(List) -> - %%?line {'EXIT',{badarg,_}} = list_to_binary(List), + %%{'EXIT',{badarg,_}} = list_to_binary(List), Bin = list_to_bitstring(List), {Bin,bitstring_to_list(Bin)}. bad_list_to_binary(Config) when is_list(Config) -> - ?line test_bad_bin(atom), - ?line test_bad_bin(42), - ?line test_bad_bin([1|2]), - ?line test_bad_bin([256]), - ?line test_bad_bin([255, [256]]), - ?line test_bad_bin([-1]), - ?line test_bad_bin([atom_in_list]), - ?line test_bad_bin([[<<8>>]|bad_tail]), + test_bad_bin(atom), + test_bad_bin(42), + test_bad_bin([1|2]), + test_bad_bin([256]), + test_bad_bin([255, [256]]), + test_bad_bin([-1]), + test_bad_bin([atom_in_list]), + test_bad_bin([[<<8>>]|bad_tail]), {'EXIT',{badarg,_}} = (catch list_to_binary(id(<<1,2,3>>))), {'EXIT',{badarg,_}} = (catch list_to_binary(id([<<42:7>>]))), {'EXIT',{badarg,_}} = (catch list_to_bitstring(id(<<1,2,3>>))), %% Funs used to be implemented as a type of binary internally. - ?line test_bad_bin(fun(X, Y) -> X*Y end), - ?line test_bad_bin([1,fun(X) -> X + 1 end,2|fun() -> 0 end]), - ?line test_bad_bin([fun(X) -> X + 1 end]), + test_bad_bin(fun(X, Y) -> X*Y end), + test_bad_bin([1,fun(X) -> X + 1 end,2|fun() -> 0 end]), + test_bad_bin([fun(X) -> X + 1 end]), %% Test iolists that do not fit in the address space. %% Unfortunately, it would be too slow to test in a 64-bit emulator. @@ -285,15 +285,15 @@ bad_list_to_binary(Config) when is_list(Config) -> huge_iolists() -> FourGigs = 1 bsl 32, - ?line Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++ + Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++ [1 bsl N || N <- lists:seq(33, 37)], - ?line Base = <<0:(1 bsl 20)/unit:8>>, + Base = <<0:(1 bsl 20)/unit:8>>, [begin L = build_iolist(Sz, Base), - ?line {'EXIT',{system_limit,_}} = (catch list_to_binary([L])), - ?line {'EXIT',{system_limit,_}} = (catch list_to_bitstring([L])), - ?line {'EXIT',{system_limit,_}} = (catch binary:list_to_bin([L])), - ?line {'EXIT',{system_limit,_}} = (catch iolist_to_binary(L)) + {'EXIT',{system_limit,_}} = (catch list_to_binary([L])), + {'EXIT',{system_limit,_}} = (catch list_to_bitstring([L])), + {'EXIT',{system_limit,_}} = (catch binary:list_to_bin([L])), + {'EXIT',{system_limit,_}} = (catch iolist_to_binary(L)) end || Sz <- Sizes], ok. @@ -305,13 +305,13 @@ test_bad_bin(List) -> %% Tries binary_to_list/1,3 with bad arguments. bad_binary_to_list(Config) when is_list(Config) -> - ?line bad_bin_to_list(fun(X) -> X * 42 end), + bad_bin_to_list(fun(X) -> X * 42 end), GoodBin = list_to_binary(lists:seq(1, 10)), - ?line bad_bin_to_list(fun(X) -> X * 44 end, 1, 2), - ?line bad_bin_to_list(GoodBin, 0, 1), - ?line bad_bin_to_list(GoodBin, 2, 1), - ?line bad_bin_to_list(GoodBin, 11, 11), + bad_bin_to_list(fun(X) -> X * 44 end, 1, 2), + bad_bin_to_list(GoodBin, 0, 1), + bad_bin_to_list(GoodBin, 2, 1), + bad_bin_to_list(GoodBin, 11, 11), {'EXIT',{badarg,_}} = (catch binary_to_list(id(<<42:7>>))), ok. @@ -326,54 +326,54 @@ bad_bin_to_list(Bin, First, Last) -> %% Tries to split a binary at all possible positions. t_split_binary(Config) when is_list(Config) -> - ?line L = lists:seq(0, ?heap_binary_size-5), %Heap binary. - ?line B = list_to_binary(L), - ?line split(L, B, size(B)), + L = lists:seq(0, ?heap_binary_size-5), %Heap binary. + B = list_to_binary(L), + split(L, B, size(B)), %% Sub binary of heap binary. - ?line split(L, make_sub_binary(B), size(B)), + split(L, make_sub_binary(B), size(B)), {X,_Y} = split_binary(B, size(B) div 2), - ?line split(binary_to_list(X), X, size(X)), + split(binary_to_list(X), X, size(X)), %% Unaligned sub binary of heap binary. - ?line split(L, make_unaligned_sub_binary(B), size(B)), + split(L, make_unaligned_sub_binary(B), size(B)), {X,_Y} = split_binary(B, size(B) div 2), - ?line split(binary_to_list(X), X, size(X)), + split(binary_to_list(X), X, size(X)), %% Reference-counted binary. - ?line L2 = lists:seq(0, ?heap_binary_size+1), - ?line B2 = list_to_binary(L2), - ?line split(L2, B2, size(B2)), + L2 = lists:seq(0, ?heap_binary_size+1), + B2 = list_to_binary(L2), + split(L2, B2, size(B2)), %% Sub binary of reference-counted binary. - ?line split(L2, make_sub_binary(B2), size(B2)), + split(L2, make_sub_binary(B2), size(B2)), {X2,_Y2} = split_binary(B2, size(B2) div 2), - ?line split(binary_to_list(X2), X2, size(X2)), + split(binary_to_list(X2), X2, size(X2)), %% Unaligned sub binary of reference-counted binary. - ?line split(L2, make_unaligned_sub_binary(B2), size(B2)), + split(L2, make_unaligned_sub_binary(B2), size(B2)), {X2,_Y2} = split_binary(B2, size(B2) div 2), - ?line split(binary_to_list(X2), X2, size(X2)), + split(binary_to_list(X2), X2, size(X2)), ok. split(L, B, Pos) when Pos > 0 -> - ?line {B1, B2} = split_binary(B, Pos), - ?line B1 = list_to_binary(lists:sublist(L, 1, Pos)), - ?line B2 = list_to_binary(lists:nthtail(Pos, L)), - ?line split(L, B, Pos-1); + {B1, B2} = split_binary(B, Pos), + B1 = list_to_binary(lists:sublist(L, 1, Pos)), + B2 = list_to_binary(lists:nthtail(Pos, L)), + split(L, B, Pos-1); split(_L, _B, 0) -> ok. %% Tries split_binary/2 with bad arguments. bad_split(Config) when is_list(Config) -> GoodBin = list_to_binary([1,2,3]), - ?line bad_split(GoodBin, -1), - ?line bad_split(GoodBin, 4), - ?line bad_split(GoodBin, a), + bad_split(GoodBin, -1), + bad_split(GoodBin, 4), + bad_split(GoodBin, a), %% Funs are a kind of binaries. - ?line bad_split(fun(_X) -> 1 end, 1), + bad_split(fun(_X) -> 1 end, 1), ok. bad_split(Bin, Pos) -> @@ -392,9 +392,9 @@ test_hash(List) -> Bin = list_to_binary(List), Sbin = make_sub_binary(List), Unaligned = make_unaligned_sub_binary(Sbin), - ?line test_hash_1(Bin, Sbin, Unaligned, fun erlang:hash/2), - ?line test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash/2), - ?line test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash2/2). + test_hash_1(Bin, Sbin, Unaligned, fun erlang:hash/2), + test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash/2), + test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash2/2). test_hash_1(Bin, Sbin, Unaligned, Hash) when is_function(Hash, 2) -> N = 65535, @@ -406,20 +406,20 @@ test_hash_1(Bin, Sbin, Unaligned, Hash) when is_function(Hash, 2) -> %% Try bad arguments to size/1. bad_size(Config) when is_list(Config) -> - ?line {'EXIT',{badarg,_}} = (catch size(fun(X) -> X + 33 end)), + {'EXIT',{badarg,_}} = (catch size(fun(X) -> X + 33 end)), ok. bad_term_to_binary(Config) when is_list(Config) -> T = id({a,b,c}), - ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, not_a_list)), - ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [blurf])), - ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,-1}])), - ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,10}])), - ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,cucumber}])), - ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed}])), - ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{version,1}|bad_tail])), - ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,-1}])), - ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,x}])), + {'EXIT',{badarg,_}} = (catch term_to_binary(T, not_a_list)), + {'EXIT',{badarg,_}} = (catch term_to_binary(T, [blurf])), + {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,-1}])), + {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,10}])), + {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,cucumber}])), + {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed}])), + {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{version,1}|bad_tail])), + {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,-1}])), + {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,x}])), ok. @@ -458,7 +458,7 @@ terms(Config) when is_list(Config) -> UnalignedC = make_unaligned_sub_binary(BinC), Term = binary_to_term_stress(UnalignedC) end, - ?line test_terms(TestFun), + test_terms(TestFun), ok. terms_compression_levels(Term, UncompressedSz, Level) when Level < 10 -> @@ -470,7 +470,7 @@ terms_compression_levels(Term, UncompressedSz, Level) when Level < 10 -> terms_compression_levels(_, _, _) -> ok. terms_float(Config) when is_list(Config) -> - ?line test_floats(fun(Term) -> + test_floats(fun(Term) -> Bin0 = term_to_binary(Term, [{minor_version,0}]), Term = binary_to_term_stress(Bin0), Bin1 = term_to_binary(Term), @@ -486,16 +486,16 @@ terms_float(Config) when is_list(Config) -> float_middle_endian(Config) when is_list(Config) -> %% Testing for roundtrip is not enough. - ?line <<131,70,63,240,0,0,0,0,0,0>> = term_to_binary(1.0, [{minor_version,1}]), - ?line 1.0 = binary_to_term_stress(<<131,70,63,240,0,0,0,0,0,0>>). + <<131,70,63,240,0,0,0,0,0,0>> = term_to_binary(1.0, [{minor_version,1}]), + 1.0 = binary_to_term_stress(<<131,70,63,240,0,0,0,0,0,0>>). external_size(Config) when is_list(Config) -> %% Build a term whose external size only fits in a big num (on 32-bit CPU). - ?line external_size_1(16#11111111111111117777777777777777888889999, 0, 16#FFFFFFF), + external_size_1(16#11111111111111117777777777777777888889999, 0, 16#FFFFFFF), %% Test that the same binary aligned and unaligned has the same external size. - ?line Bin = iolist_to_binary([1,2,3,96]), - ?line Unaligned = make_unaligned_sub_binary(Bin), + Bin = iolist_to_binary([1,2,3,96]), + Unaligned = make_unaligned_sub_binary(Bin), case {erlang:external_size(Bin),erlang:external_size(Unaligned)} of {X,X} -> ok; {Sz1,Sz2} -> @@ -577,30 +577,30 @@ build_iolist(N0, Base) -> %% OTP-4053 bad_binary_to_term_2(Config) when is_list(Config) -> - ?line {ok, N} = test_server:start_node(plopp, slave, []), - ?line R = rpc:call(N, erlang, binary_to_term, [<<131,111,255,255,255,0>>]), - ?line case R of + {ok, N} = test_server:start_node(plopp, slave, []), + R = rpc:call(N, erlang, binary_to_term, [<<131,111,255,255,255,0>>]), + case R of {badrpc, {'EXIT', _}} -> ok; _Other -> ct:fail({rpcresult, R}) end, - ?line test_server:stop_node(N), + test_server:stop_node(N), ok. %% Try bad input to binary_to_term/1. bad_binary_to_term(Config) when is_list(Config) -> - ?line bad_bin_to_term(an_atom), - ?line bad_bin_to_term({an,tuple}), - ?line bad_bin_to_term({a,list}), - ?line bad_bin_to_term(fun() -> self() end), - ?line bad_bin_to_term(fun(X) -> 42*X end), - ?line bad_bin_to_term(fun(X, Y) -> {X,Y} end), - ?line bad_bin_to_term(fun(X, Y, Z) -> {X,Y,Z} end), - ?line bad_bin_to_term(bit_sized_binary(term_to_binary({you,should,'not',see,this,term}))), + bad_bin_to_term(an_atom), + bad_bin_to_term({an,tuple}), + bad_bin_to_term({a,list}), + bad_bin_to_term(fun() -> self() end), + bad_bin_to_term(fun(X) -> 42*X end), + bad_bin_to_term(fun(X, Y) -> {X,Y} end), + bad_bin_to_term(fun(X, Y, Z) -> {X,Y,Z} end), + bad_bin_to_term(bit_sized_binary(term_to_binary({you,should,'not',see,this,term}))), %% Bad float. - ?line bad_bin_to_term(<<131,70,-1:64>>), + bad_bin_to_term(<<131,70,-1:64>>), ok. bad_bin_to_term(BadBin) -> @@ -611,22 +611,22 @@ bad_bin_to_term(BadBin,Opts) -> %% Test safety options for binary_to_term/2 safe_binary_to_term2(Config) when is_list(Config) -> - ?line bad_bin_to_term(<<131,100,0,14,"undefined_atom">>, [safe]), - ?line bad_bin_to_term(<<131,100,0,14,"other_bad_atom">>, [safe]), + bad_bin_to_term(<<131,100,0,14,"undefined_atom">>, [safe]), + bad_bin_to_term(<<131,100,0,14,"other_bad_atom">>, [safe]), BadHostAtom = <<100,0,14,"badguy@badhost">>, Empty = <<0,0,0,0>>, BadRef = <<131,114,0,3,BadHostAtom/binary,0,<<0,0,0,255>>/binary, Empty/binary,Empty/binary>>, - ?line bad_bin_to_term(BadRef, [safe]), % good ref, with a bad atom - ?line fullsweep_after = binary_to_term_stress(<<131,100,0,15,"fullsweep_after">>, [safe]), % should be a good atom + bad_bin_to_term(BadRef, [safe]), % good ref, with a bad atom + fullsweep_after = binary_to_term_stress(<<131,100,0,15,"fullsweep_after">>, [safe]), % should be a good atom BadExtFun = <<131,113,100,0,4,98,108,117,101,100,0,4,109,111,111,110,97,3>>, - ?line bad_bin_to_term(BadExtFun, [safe]), + bad_bin_to_term(BadExtFun, [safe]), ok. %% Tests bad input to binary_to_term/1. bad_terms(Config) when is_list(Config) -> - ?line test_terms(fun corrupter/1), + test_terms(fun corrupter/1), {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,3:32,0,11,22,33>>)), {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,3:32,9,11,22,33>>)), {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,0:32,1,11,22,33>>)), @@ -659,7 +659,7 @@ corrupter(Term) -> corrupter0(Term). corrupter0(Term) -> - ?line try + try S = io_lib:format("About to corrupt: ~P", [Term,12]), io:put_chars(S) catch @@ -667,41 +667,41 @@ corrupter0(Term) -> io:format("About to corrupt: <= 0 -> - ?line {ShorterBin, Rest} = split_binary(Bin, Pos), - ?line catch binary_to_term_stress(ShorterBin), %% emulator shouldn't crash - ?line MovedBin = list_to_binary([ShorterBin]), - ?line catch binary_to_term_stress(MovedBin), %% emulator shouldn't crash + {ShorterBin, Rest} = split_binary(Bin, Pos), + catch binary_to_term_stress(ShorterBin), %% emulator shouldn't crash + MovedBin = list_to_binary([ShorterBin]), + catch binary_to_term_stress(MovedBin), %% emulator shouldn't crash %% Bit faults, shouldn't crash <> = Rest, Fun = fun(M) -> FaultyByte = Byte bxor M, catch binary_to_term_stress(<>) end, - ?line lists:foreach(Fun,[1,2,4,8,16,32,64,128,255]), - ?line corrupter(Bin, Pos-1); + lists:foreach(Fun,[1,2,4,8,16,32,64,128,255]), + corrupter(Bin, Pos-1); corrupter(_Bin, _) -> ok. more_bad_terms(Config) when is_list(Config) -> - ?line Data = proplists:get_value(data_dir, Config), - ?line BadFile = filename:join(Data, "bad_binary"), - ?line ok = io:format("File: ~s\n", [BadFile]), - ?line case file:read_file(BadFile) of + Data = proplists:get_value(data_dir, Config), + BadFile = filename:join(Data, "bad_binary"), + ok = io:format("File: ~s\n", [BadFile]), + case file:read_file(BadFile) of {ok,Bin} -> - ?line {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)), + {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)), ok; Other -> - ?line ct:fail(Other) + ct:fail(Other) end. otp_5484(Config) when is_list(Config) -> - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( <<131, @@ -714,7 +714,7 @@ otp_5484(Config) when is_list(Config) -> 255, 106>>)), - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( <<131, @@ -726,13 +726,13 @@ otp_5484(Config) when is_list(Config) -> 2, 106>>)), - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( %% A old-type fun in a list containing a bad creator pid. <<131,108,0,0,0,1,117,0,0,0,0,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,255,255,0,25,255,0,0,0,0,100,0,1,116,97,0,98,6,142,121,72,106>>)), - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( %% A new-type fun in a list containing a bad creator pid. @@ -744,7 +744,7 @@ otp_5484(Config) when is_list(Config) -> 106, %[] instead of an atom. 0,0,0,27,0,0,0,0,0,106>>)), - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( %% A new-type fun in a list containing a bad module. @@ -755,7 +755,7 @@ otp_5484(Config) when is_list(Config) -> 107,0,1,64, %String instead of atom (same length). 97,0,98,6,64,82,230,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)), - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( %% A new-type fun in a list containing a bad index. @@ -767,7 +767,7 @@ otp_5484(Config) when is_list(Config) -> 104,0, %Tuple {} instead of integer. 98,6,64,82,230,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)), - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( %% A new-type fun in a list containing a bad unique value. @@ -781,46 +781,46 @@ otp_5484(Config) when is_list(Config) -> 103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)), %% An absurdly large atom. - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress(iolist_to_binary([<<131,100,65000:16>>| lists:duplicate(65000, 42)]))), %% Longer than 255 characters. - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress(iolist_to_binary([<<131,100,256:16>>| lists:duplicate(256, 42)]))), %% OTP-7218. Thanks to Matthew Dempsky. Also make sure that we %% cover the other error cases for external funs (EXPORT_EXT). - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 97,13, %Integer: 13 97,13, %Integer: 13 97,13>>)), %Integer: 13 - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 100,0,1,64, %Atom: '@' 97,13, %Integer: 13 97,13>>)), %Integer: 13 - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 100,0,1,64, %Atom: '@' 100,0,1,64, %Atom: '@' 106>>)), %NIL - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 100,0,1,64, %Atom: '@' 100,0,1,64, %Atom: '@' 98,255,255,255,255>>)), %Integer: -1 - ?line {'EXIT',_} = + {'EXIT',_} = (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP @@ -829,7 +829,7 @@ otp_5484(Config) when is_list(Config) -> 113,97,13,97,13,97,13>>)), %fun 13:13/13 %% Bad funs. - ?line {'EXIT',_} = (catch binary_to_term_stress(fake_fun(0, lists:seq(0, 256)))), + {'EXIT',_} = (catch binary_to_term_stress(fake_fun(0, lists:seq(0, 256)))), ok. fake_fun(Arity, Env0) -> @@ -852,9 +852,9 @@ fake_fun(Arity, Env0) -> %% More bad terms submitted by Matthias Lang. otp_5933(Config) when is_list(Config) -> - ?line try_bad_lengths(<<131,$m>>), %binary - ?line try_bad_lengths(<<131,$n>>), %bignum - ?line try_bad_lengths(<<131,$o>>), %huge bignum + try_bad_lengths(<<131,$m>>), %binary + try_bad_lengths(<<131,$n>>), %bignum + try_bad_lengths(<<131,$o>>), %huge bignum ok. try_bad_lengths(B) -> @@ -873,7 +873,7 @@ otp_6817(Config) when is_list(Config) -> %% Floats are only validated when the heap fragment has been allocated. BadFloat = <<131,99,53,46,48,$X,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,101,45,48,49,0,0,0,0,0>>, - ?line otp_6817_try_bin(BadFloat), + otp_6817_try_bin(BadFloat), %% {Binary,BadFloat}: When the error in float is discovered, a refc-binary %% has been allocated and the list of refc-binaries goes through the @@ -893,7 +893,7 @@ otp_6817(Config) when is_list(Config) -> 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248, 249,250,251,252,253,254,255,99,51,46,49,52,$B,$l,$u,$r,$f,48,48,48,48,48,48, 48,48,49,50,52,51,52,101,43,48,48,0,0,0,0,0>>, - ?line otp_6817_try_bin(BinAndFloat), + otp_6817_try_bin(BinAndFloat), %% {Fun,BadFloat} FunAndFloat = @@ -901,14 +901,14 @@ otp_6817(Config) when is_list(Config) -> 71,8,0,0,0,0,0,0,0,0,100,0,1,116,97,0,98,5,175,169,123,103,100,0,13,110,111, 110,111,100,101,64,110,111,104,111,115,116,0,0,0,41,0,0,0,0,0,99,50,46,55,48, $Y,57,57,57,57,57,57,57,57,57,57,57,57,57,54,52,52,55,101,43,48,48,0,0,0,0,0>>, - ?line otp_6817_try_bin(FunAndFloat), + otp_6817_try_bin(FunAndFloat), %% [ExternalPid|BadFloat] ExtPidAndFloat = <<131,108,0,0,0,1,103,100,0,13,107,97,108,108,101,64,115,116,114,105,100,101, 114,0,0,0,36,0,0,0,0,2,99,48,46,$@,48,48,48,48,48,48,48,48,48,48,48,48,48,48, 48,48,48,48,48,101,43,48,48,0,0,0,0,0>>, - ?line otp_6817_try_bin(ExtPidAndFloat), + otp_6817_try_bin(ExtPidAndFloat), ok. otp_6817_try_bin(Bin) -> @@ -935,19 +935,19 @@ otp_8117(Config) when is_list(Config) -> otp_8117_do('fun',Neg) -> % Fun with negative num_free FunBin = term_to_binary(fun() -> ok end), - ?line <> = FunBin, - ?line bad_bin_to_term(<>); + <> = FunBin, + bad_bin_to_term(<>); otp_8117_do(named_fun,Neg) -> % Named fun with negative num_free FunBin = term_to_binary(fun F() -> F end), - ?line <> = FunBin, - ?line bad_bin_to_term(<>); + <> = FunBin, + bad_bin_to_term(<>); otp_8117_do(list,Neg) -> %% List with negative length - ?line bad_bin_to_term(<<131,104,2,108,Neg:32,97,11,104,1,97,12,97,13,106,97,14>>); + bad_bin_to_term(<<131,104,2,108,Neg:32,97,11,104,1,97,12,97,13,106,97,14>>); otp_8117_do(tuple,Neg) -> %% Tuple with negative arity - ?line bad_bin_to_term(<<131,104,2,105,Neg:32,97,11,97,12,97,13,97,14>>). + bad_bin_to_term(<<131,104,2,105,Neg:32,97,11,97,12,97,13,97,14>>). %% Tests ordering of binaries. @@ -959,54 +959,54 @@ ordering(Config) when is_list(Config) -> %% From R8 binaries are compared as strings. - ?line false = B1 == B2, - ?line false = B1 =:= B2, - ?line true = B1 /= B2, - ?line true = B1 =/= B2, + false = B1 == B2, + false = B1 =:= B2, + true = B1 /= B2, + true = B1 =/= B2, - ?line true = B1 > B2, - ?line true = B2 < B3, - ?line true = B2 =< B1, - ?line true = B2 =< B3, + true = B1 > B2, + true = B2 < B3, + true = B2 =< B1, + true = B2 =< B3, - ?line true = B2 =:= Unaligned, - ?line true = B2 == Unaligned, - ?line true = Unaligned < B3, - ?line true = Unaligned =< B3, + true = B2 =:= Unaligned, + true = B2 == Unaligned, + true = Unaligned < B3, + true = Unaligned =< B3, %% Binaries are greater than all other terms. - ?line true = B1 > 0, - ?line true = B1 > 39827491247298471289473333333333333333333333333333, - ?line true = B1 > -3489274937438742190467869234328742398347, - ?line true = B1 > 3.14, - ?line true = B1 > [], - ?line true = B1 > [a], - ?line true = B1 > {a}, - ?line true = B1 > self(), - ?line true = B1 > make_ref(), - ?line true = B1 > xxx, - ?line true = B1 > fun() -> 1 end, - ?line true = B1 > fun erlang:send/2, - - ?line Path = proplists:get_value(priv_dir, Config), - ?line AFile = filename:join(Path, "vanilla_file"), - ?line Port = open_port(AFile, [out]), - ?line true = B1 > Port, - - ?line true = B1 >= 0, - ?line true = B1 >= 39827491247298471289473333333333333333333333333333, - ?line true = B1 >= -3489274937438742190467869234328742398347, - ?line true = B1 >= 3.14, - ?line true = B1 >= [], - ?line true = B1 >= [a], - ?line true = B1 >= {a}, - ?line true = B1 >= self(), - ?line true = B1 >= make_ref(), - ?line true = B1 >= xxx, - ?line true = B1 >= fun() -> 1 end, - ?line true = B1 >= fun erlang:send/2, - ?line true = B1 >= Port, + true = B1 > 0, + true = B1 > 39827491247298471289473333333333333333333333333333, + true = B1 > -3489274937438742190467869234328742398347, + true = B1 > 3.14, + true = B1 > [], + true = B1 > [a], + true = B1 > {a}, + true = B1 > self(), + true = B1 > make_ref(), + true = B1 > xxx, + true = B1 > fun() -> 1 end, + true = B1 > fun erlang:send/2, + + Path = proplists:get_value(priv_dir, Config), + AFile = filename:join(Path, "vanilla_file"), + Port = open_port(AFile, [out]), + true = B1 > Port, + + true = B1 >= 0, + true = B1 >= 39827491247298471289473333333333333333333333333333, + true = B1 >= -3489274937438742190467869234328742398347, + true = B1 >= 3.14, + true = B1 >= [], + true = B1 >= [a], + true = B1 >= {a}, + true = B1 >= self(), + true = B1 >= make_ref(), + true = B1 >= xxx, + true = B1 >= fun() -> 1 end, + true = B1 >= fun erlang:send/2, + true = B1 >= Port, ok. @@ -1019,153 +1019,153 @@ unaligned_order(Config) when is_list(Config) -> test_unaligned_order(I, J) -> Align = {I,J}, io:format("~p ~p", [I,J]), - ?line true = test_unaligned_order_1('=:=', <<1,2,3,16#AA,16#7C,4,16#5F,5,16#5A>>, + true = test_unaligned_order_1('=:=', <<1,2,3,16#AA,16#7C,4,16#5F,5,16#5A>>, <<1,2,3,16#AA,16#7C,4,16#5F,5,16#5A>>, Align), - ?line false = test_unaligned_order_1('=/=', <<1,2,3>>, <<1,2,3>>, Align), - ?line true = test_unaligned_order_1('==', <<4,5,6>>, <<4,5,6>>, Align), - ?line false = test_unaligned_order_1('/=', <<1,2,3>>, <<1,2,3>>, Align), + false = test_unaligned_order_1('=/=', <<1,2,3>>, <<1,2,3>>, Align), + true = test_unaligned_order_1('==', <<4,5,6>>, <<4,5,6>>, Align), + false = test_unaligned_order_1('/=', <<1,2,3>>, <<1,2,3>>, Align), - ?line true = test_unaligned_order_1('<', <<1,2>>, <<1,2,3>>, Align), - ?line true = test_unaligned_order_1('=<', <<1,2>>, <<1,2,3>>, Align), - ?line true = test_unaligned_order_1('=<', <<1,2,7,8>>, <<1,2,7,8>>, Align), + true = test_unaligned_order_1('<', <<1,2>>, <<1,2,3>>, Align), + true = test_unaligned_order_1('=<', <<1,2>>, <<1,2,3>>, Align), + true = test_unaligned_order_1('=<', <<1,2,7,8>>, <<1,2,7,8>>, Align), ok. test_unaligned_order_1(Op, A, B, {Aa,Ba}) -> erlang:Op(unaligned_sub_bin(A, Aa), unaligned_sub_bin(B, Ba)). test_terms(Test_Func) -> - ?line Test_Func(atom), - ?line Test_Func(''), - ?line Test_Func('a'), - ?line Test_Func('ab'), - ?line Test_Func('abc'), - ?line Test_Func('abcd'), - ?line Test_Func('abcde'), - ?line Test_Func('abcdef'), - ?line Test_Func('abcdefg'), - ?line Test_Func('abcdefgh'), - - ?line Test_Func(fun() -> ok end), + Test_Func(atom), + Test_Func(''), + Test_Func('a'), + Test_Func('ab'), + Test_Func('abc'), + Test_Func('abcd'), + Test_Func('abcde'), + Test_Func('abcdef'), + Test_Func('abcdefg'), + Test_Func('abcdefgh'), + + Test_Func(fun() -> ok end), X = id([a,{b,c},c]), Y = id({x,y,z}), Z = id(1 bsl 8*257), - ?line Test_Func(fun() -> X end), - ?line Test_Func(fun() -> {X,Y} end), - ?line Test_Func([fun() -> {X,Y,Z} end, + Test_Func(fun() -> X end), + Test_Func(fun() -> {X,Y} end), + Test_Func([fun() -> {X,Y,Z} end, fun() -> {Z,X,Y} end, fun() -> {Y,Z,X} end]), - ?line Test_Func({trace_ts,{even_bigger,{some_data,fun() -> ok end}},{1,2,3}}), - ?line Test_Func({trace_ts,{even_bigger,{some_data,<<1,2,3,4,5,6,7,8,9,10>>}}, + Test_Func({trace_ts,{even_bigger,{some_data,fun() -> ok end}},{1,2,3}}), + Test_Func({trace_ts,{even_bigger,{some_data,<<1,2,3,4,5,6,7,8,9,10>>}}, {1,2,3}}), - ?line Test_Func(1), - ?line Test_Func(42), - ?line Test_Func(-23), - ?line Test_Func(256), - ?line Test_Func(25555), - ?line Test_Func(-3333), + Test_Func(1), + Test_Func(42), + Test_Func(-23), + Test_Func(256), + Test_Func(25555), + Test_Func(-3333), - ?line Test_Func(1.0), + Test_Func(1.0), - ?line Test_Func(183749783987483978498378478393874), - ?line Test_Func(-37894183749783987483978498378478393874), + Test_Func(183749783987483978498378478393874), + Test_Func(-37894183749783987483978498378478393874), Very_Big = very_big_num(), - ?line Test_Func(Very_Big), - ?line Test_Func(-Very_Big+1), - - ?line Test_Func([]), - ?line Test_Func("abcdef"), - ?line Test_Func([a, b, 1, 2]), - ?line Test_Func([a|b]), - - ?line Test_Func({}), - ?line Test_Func({1}), - ?line Test_Func({a, b}), - ?line Test_Func({a, b, c}), - ?line Test_Func(list_to_tuple(lists:seq(0, 255))), - ?line Test_Func(list_to_tuple(lists:seq(0, 256))), - - ?line Test_Func(make_ref()), - ?line Test_Func([make_ref(), make_ref()]), - - ?line Test_Func(make_port()), - - ?line Test_Func(make_pid()), - - ?line Test_Func(Bin0 = list_to_binary(lists:seq(0, 14))), - ?line Test_Func(Bin1 = list_to_binary(lists:seq(0, ?heap_binary_size))), - ?line Test_Func(Bin2 = list_to_binary(lists:seq(0, ?heap_binary_size+1))), - ?line Test_Func(Bin3 = list_to_binary(lists:seq(0, 255))), - - ?line Test_Func(make_unaligned_sub_binary(Bin0)), - ?line Test_Func(make_unaligned_sub_binary(Bin1)), - ?line Test_Func(make_unaligned_sub_binary(Bin2)), - ?line Test_Func(make_unaligned_sub_binary(Bin3)), - - ?line Test_Func(make_sub_binary(lists:seq(42, 43))), - ?line Test_Func(make_sub_binary([42,43,44])), - ?line Test_Func(make_sub_binary([42,43,44,45])), - ?line Test_Func(make_sub_binary([42,43,44,45,46])), - ?line Test_Func(make_sub_binary([42,43,44,45,46,47])), - ?line Test_Func(make_sub_binary([42,43,44,45,46,47,48])), - ?line Test_Func(make_sub_binary(lists:seq(42, 49))), - ?line Test_Func(make_sub_binary(lists:seq(0, 14))), - ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size))), - ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size+1))), - ?line Test_Func(make_sub_binary(lists:seq(0, 255))), - - ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 43))), - ?line Test_Func(make_unaligned_sub_binary([42,43,44])), - ?line Test_Func(make_unaligned_sub_binary([42,43,44,45])), - ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46])), - ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47])), - ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47,48])), - ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 49))), - ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 14))), - ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size))), - ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size+1))), - ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 255))), + Test_Func(Very_Big), + Test_Func(-Very_Big+1), + + Test_Func([]), + Test_Func("abcdef"), + Test_Func([a, b, 1, 2]), + Test_Func([a|b]), + + Test_Func({}), + Test_Func({1}), + Test_Func({a, b}), + Test_Func({a, b, c}), + Test_Func(list_to_tuple(lists:seq(0, 255))), + Test_Func(list_to_tuple(lists:seq(0, 256))), + + Test_Func(make_ref()), + Test_Func([make_ref(), make_ref()]), + + Test_Func(make_port()), + + Test_Func(make_pid()), + + Test_Func(Bin0 = list_to_binary(lists:seq(0, 14))), + Test_Func(Bin1 = list_to_binary(lists:seq(0, ?heap_binary_size))), + Test_Func(Bin2 = list_to_binary(lists:seq(0, ?heap_binary_size+1))), + Test_Func(Bin3 = list_to_binary(lists:seq(0, 255))), + + Test_Func(make_unaligned_sub_binary(Bin0)), + Test_Func(make_unaligned_sub_binary(Bin1)), + Test_Func(make_unaligned_sub_binary(Bin2)), + Test_Func(make_unaligned_sub_binary(Bin3)), + + Test_Func(make_sub_binary(lists:seq(42, 43))), + Test_Func(make_sub_binary([42,43,44])), + Test_Func(make_sub_binary([42,43,44,45])), + Test_Func(make_sub_binary([42,43,44,45,46])), + Test_Func(make_sub_binary([42,43,44,45,46,47])), + Test_Func(make_sub_binary([42,43,44,45,46,47,48])), + Test_Func(make_sub_binary(lists:seq(42, 49))), + Test_Func(make_sub_binary(lists:seq(0, 14))), + Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size))), + Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size+1))), + Test_Func(make_sub_binary(lists:seq(0, 255))), + + Test_Func(make_unaligned_sub_binary(lists:seq(42, 43))), + Test_Func(make_unaligned_sub_binary([42,43,44])), + Test_Func(make_unaligned_sub_binary([42,43,44,45])), + Test_Func(make_unaligned_sub_binary([42,43,44,45,46])), + Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47])), + Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47,48])), + Test_Func(make_unaligned_sub_binary(lists:seq(42, 49))), + Test_Func(make_unaligned_sub_binary(lists:seq(0, 14))), + Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size))), + Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size+1))), + Test_Func(make_unaligned_sub_binary(lists:seq(0, 255))), %% Bit level binaries. - ?line Test_Func(<<1:1>>), - ?line Test_Func(<<2:2>>), - ?line Test_Func(<<42:10>>), - ?line Test_Func(list_to_bitstring([<<5:6>>|lists:seq(0, 255)])), + Test_Func(<<1:1>>), + Test_Func(<<2:2>>), + Test_Func(<<42:10>>), + Test_Func(list_to_bitstring([<<5:6>>|lists:seq(0, 255)])), - ?line Test_Func(F = fun(A) -> 42*A end), - ?line Test_Func(lists:duplicate(32, F)), + Test_Func(F = fun(A) -> 42*A end), + Test_Func(lists:duplicate(32, F)), - ?line Test_Func(FF = fun binary_SUITE:all/0), - ?line Test_Func(lists:duplicate(32, FF)), + Test_Func(FF = fun binary_SUITE:all/0), + Test_Func(lists:duplicate(32, FF)), ok. test_floats(Test_Func) -> - ?line Test_Func(5.5), - ?line Test_Func(-15.32), - ?line Test_Func(1.2435e25), - ?line Test_Func(1.2333e-20), - ?line Test_Func(199.0e+15), + Test_Func(5.5), + Test_Func(-15.32), + Test_Func(1.2435e25), + Test_Func(1.2333e-20), + Test_Func(199.0e+15), ok. very_big_num() -> very_big_num(33, 1). very_big_num(Left, Result) when Left > 0 -> - ?line very_big_num(Left-1, Result*256); + very_big_num(Left-1, Result*256); very_big_num(0, Result) -> - ?line Result. + Result. make_port() -> - ?line open_port({spawn, efile}, [eof]). + open_port({spawn, efile}, [eof]). make_pid() -> - ?line spawn_link(?MODULE, sleeper, []). + spawn_link(?MODULE, sleeper, []). sleeper() -> - ?line receive after infinity -> ok end. + receive after infinity -> ok end. %% Test that binaries are garbage collected properly. @@ -1224,7 +1224,7 @@ gc() -> gc1() -> ok. bit_sized_binary_sizes(Config) when is_list(Config) -> - ?line [bsbs_1(A) || A <- lists:seq(1, 8)], + [bsbs_1(A) || A <- lists:seq(1, 8)], ok. bsbs_1(A) -> @@ -1266,13 +1266,13 @@ obsolete_funs(Config) when is_list(Config) -> X = id({1,2,3}), Y = id([a,b,c,d]), Z = id({x,y,z}), - ?line obsolete_fun(fun() -> ok end), - ?line obsolete_fun(fun() -> X end), - ?line obsolete_fun(fun(A) -> {A,X} end), - ?line obsolete_fun(fun() -> {X,Y} end), - ?line obsolete_fun(fun() -> {X,Y,Z} end), + obsolete_fun(fun() -> ok end), + obsolete_fun(fun() -> X end), + obsolete_fun(fun(A) -> {A,X} end), + obsolete_fun(fun() -> {X,Y} end), + obsolete_fun(fun() -> {X,Y,Z} end), - ?line obsolete_fun(fun ?MODULE:all/1), + obsolete_fun(fun ?MODULE:all/1), erts_debug:set_internal_state(available_internal_state, false), ok. @@ -1299,41 +1299,41 @@ no_fun_roundtrip(Term) -> %% but recognized by binary_to_term/1. robustness(Config) when is_list(Config) -> - ?line [] = binary_to_term_stress(<<131,107,0,0>>), %Empty string. - ?line [] = binary_to_term_stress(<<131,108,0,0,0,0,106>>), %Zero-length list. + [] = binary_to_term_stress(<<131,107,0,0>>), %Empty string. + [] = binary_to_term_stress(<<131,108,0,0,0,0,106>>), %Zero-length list. %% {[],a} where [] is a zero-length list. - ?line {[],a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,106,100,0,1,97>>), + {[],a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,106,100,0,1,97>>), %% {42,a} where 42 is a zero-length list with 42 in the tail. - ?line {42,a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,97,42,100,0,1,97>>), + {42,a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,97,42,100,0,1,97>>), %% {{x,y},a} where {x,y} is a zero-length list with {x,y} in the tail. - ?line {{x,y},a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0, + {{x,y},a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0, 104,2,100,0,1,120,100,0,1, 121,100,0,1,97>>), %% Bignums fitting in 32 bits. - ?line 16#7FFFFFFF = binary_to_term_stress(<<131,98,127,255,255,255>>), - ?line -1 = binary_to_term_stress(<<131,98,255,255,255,255>>), + 16#7FFFFFFF = binary_to_term_stress(<<131,98,127,255,255,255>>), + -1 = binary_to_term_stress(<<131,98,255,255,255,255>>), ok. %% OTP-8180: Test several terms that have been known to crash the emulator. %% (Thanks to Scott Lystig Fritchie.) otp_8180(Config) when is_list(Config) -> - ?line Data = proplists:get_value(data_dir, Config), - ?line Wc = filename:join(Data, "zzz.*"), + Data = proplists:get_value(data_dir, Config), + Wc = filename:join(Data, "zzz.*"), Files = filelib:wildcard(Wc), [run_otp_8180(F) || F <- Files], ok. run_otp_8180(Name) -> io:format("~s", [Name]), - ?line {ok,Bins} = file:consult(Name), + {ok,Bins} = file:consult(Name), [begin io:format("~p\n", [Bin]), - ?line {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)) + {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)) end || Bin <- Bins], ok. diff --git a/erts/emulator/test/bs_bit_binaries_SUITE.erl b/erts/emulator/test/bs_bit_binaries_SUITE.erl index 0896fad8ed..dd056d10af 100644 --- a/erts/emulator/test/bs_bit_binaries_SUITE.erl +++ b/erts/emulator/test/bs_bit_binaries_SUITE.erl @@ -57,9 +57,9 @@ end_per_group(_GroupName, Config) -> misc(Config) when is_list(Config) -> - ?line <<1:100>> = id(<<1:100>>), - ?line {ok,ok} = {match(7),match(9)}, - ?line {ok,ok} = {match1(15),match1(31)}, + <<1:100>> = id(<<1:100>>), + {ok,ok} = {match(7),match(9)}, + {ok,ok} = {match1(15),match1(31)}, ok. @@ -76,70 +76,70 @@ match1(N) -> ok. test_bit_size(Config) when is_list(Config) -> - ?line 101 = bit_size(<<1:101>>), - ?line 1001 = bit_size(<<1:1001>>), - ?line 80 = bit_size(<<1:80>>), - ?line 800 = bit_size(<<1:800>>), - ?line Bin = <<0:16#1000000>>, - ?line BigBin = list_to_bitstring([Bin||_ <- lists:seq(1,16#10)]++[<<1:1>>]), - ?line 16#10000001 = erlang:bit_size(BigBin), + 101 = bit_size(<<1:101>>), + 1001 = bit_size(<<1:1001>>), + 80 = bit_size(<<1:80>>), + 800 = bit_size(<<1:800>>), + Bin = <<0:16#1000000>>, + BigBin = list_to_bitstring([Bin||_ <- lists:seq(1,16#10)]++[<<1:1>>]), + 16#10000001 = erlang:bit_size(BigBin), %% Only run these on computers with lots of memory %% HugeBin = list_to_bitstring([BigBin||_ <- lists:seq(1,16#10)]++[<<1:1>>]), %% 16#100000011 = bit_size(HugeBin), - ?line 0 = bit_size(<<>>), + 0 = bit_size(<<>>), ok. horrid_match(Config) when is_list(Config) -> - ?line <<1:4,B:24/bitstring>> = <<1:4,42:24/little>>, - ?line <<42:24/little>> = B, + <<1:4,B:24/bitstring>> = <<1:4,42:24/little>>, + <<42:24/little>> = B, ok. test_bitstr(Config) when is_list(Config) -> - ?line <<1:7,B/bitstring>> = <<1:7,<<1:1,6>>/bitstring>>, - ?line <<1:1,6>> = B, - ?line B = <<1:1,6>>, + <<1:7,B/bitstring>> = <<1:7,<<1:1,6>>/bitstring>>, + <<1:1,6>> = B, + B = <<1:1,6>>, ok. asymmetric_tests(Config) when is_list(Config) -> - ?line <<1:12>> = <<0,1:4>>, - ?line <<0,1:4>> = <<1:12>>, - ?line <<1:1,X/bitstring>> = <<128,255,0,0:2>>, - ?line <<1,254,0,0:1>> = X, - ?line X = <<1,254,0,0:1>>, - ?line <<1:1,X1:25/bitstring>> = <<128,255,0,0:2>>, - ?line <<1,254,0,0:1>> = X1, - ?line X1 = <<1,254,0,0:1>>, + <<1:12>> = <<0,1:4>>, + <<0,1:4>> = <<1:12>>, + <<1:1,X/bitstring>> = <<128,255,0,0:2>>, + <<1,254,0,0:1>> = X, + X = <<1,254,0,0:1>>, + <<1:1,X1:25/bitstring>> = <<128,255,0,0:2>>, + <<1,254,0,0:1>> = X1, + X1 = <<1,254,0,0:1>>, ok. big_asymmetric_tests(Config) when is_list(Config) -> - ?line <<1:875,1:12>> = <<1:875,0,1:4>>, - ?line <<1:875,0,1:4>> = <<1:875,1:12>>, - ?line <<1:1,X/bitstring>> = <<128,255,0,0:2,1:875>>, - ?line <<1,254,0,0:1,1:875>> = X, - ?line X = <<1,254,0,0:1,1:875>>, - ?line <<1:1,X1:900/bitstring>> = <<128,255,0,0:2,1:875>>, - ?line <<1,254,0,0:1,1:875>> = X1, - ?line X1 = <<1,254,0,0:1,1:875>>, + <<1:875,1:12>> = <<1:875,0,1:4>>, + <<1:875,0,1:4>> = <<1:875,1:12>>, + <<1:1,X/bitstring>> = <<128,255,0,0:2,1:875>>, + <<1,254,0,0:1,1:875>> = X, + X = <<1,254,0,0:1,1:875>>, + <<1:1,X1:900/bitstring>> = <<128,255,0,0:2,1:875>>, + <<1,254,0,0:1,1:875>> = X1, + X1 = <<1,254,0,0:1,1:875>>, ok. binary_to_and_from_list(Config) when is_list(Config) -> - ?line <<1,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1,2,3,4,1:1>>)), - ?line [1,2,3,4,<<1:1>>] = bitstring_to_list(<<1,2,3,4,1:1>>), - ?line <<1:1,1,2,3,4>> = list_to_bitstring([<<1:1>>,1,2,3,4]), - ?line [128,129,1,130,<<0:1>>] = bitstring_to_list(<<1:1,1,2,3,4>>), + <<1,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1,2,3,4,1:1>>)), + [1,2,3,4,<<1:1>>] = bitstring_to_list(<<1,2,3,4,1:1>>), + <<1:1,1,2,3,4>> = list_to_bitstring([<<1:1>>,1,2,3,4]), + [128,129,1,130,<<0:1>>] = bitstring_to_list(<<1:1,1,2,3,4>>), ok. big_binary_to_and_from_list(Config) when is_list(Config) -> - ?line <<1:800,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1:800,2,3,4,1:1>>)), - ?line [1,2,3,4|_Rest1] = bitstring_to_list(<<1,2,3,4,1:800,1:1>>), - ?line <<1:801,1,2,3,4>> = list_to_bitstring([<<1:801>>,1,2,3,4]), + <<1:800,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1:800,2,3,4,1:1>>)), + [1,2,3,4|_Rest1] = bitstring_to_list(<<1,2,3,4,1:800,1:1>>), + <<1:801,1,2,3,4>> = list_to_bitstring([<<1:801>>,1,2,3,4]), ok. send_and_receive(Config) when is_list(Config) -> - ?line Bin = <<1,2:7>>, + Bin = <<1,2:7>>, Pid = spawn_link(fun() -> receiver(Bin) end), - ?line Pid ! {self(),<<1:7,8:5,Bin/bitstring>>}, - ?line receive + Pid ! {self(),<<1:7,8:5,Bin/bitstring>>}, + receive ok -> ok end. @@ -176,8 +176,8 @@ receiver_alot(Bin) -> append(Config) when is_list(Config) -> cs_init(), - ?line <<(-1):256/signed-unit:8>> = cs(do_append(id(<<>>), 256*8)), - ?line <<(-1):256/signed-unit:8>> = cs(do_append2(id(<<>>), 256*4)), + <<(-1):256/signed-unit:8>> = cs(do_append(id(<<>>), 256*8)), + <<(-1):256/signed-unit:8>> = cs(do_append2(id(<<>>), 256*4)), <<(-1):256/signed-unit:8>> = cs(do_append3(id(<<>>), 256*8)), cs_end(). diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 9a9066d0f0..0a2acbd4e1 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -54,9 +54,9 @@ r(L) -> -define(T(B, L), {B, ??B, L}). -define(N(B), {B, ??B, unknown}). --define(FAIL(Expr), ?line fail_check(catch Expr, ??Expr, [])). +-define(FAIL(Expr), fail_check(catch Expr, ??Expr, [])). --define(FAIL_VARS(Expr, Vars), ?line fail_check(catch Expr, ??Expr, Vars)). +-define(FAIL_VARS(Expr, Vars), fail_check(catch Expr, ??Expr, Vars)). l(I_13, I_big1) -> [ @@ -250,11 +250,11 @@ fail_check(Res, _, _) -> %%% Simple working cases test1(Config) when is_list(Config) -> - ?line I_13 = i(13), - ?line I_big1 = big(1), - ?line Vars = [{'I_13', I_13}, + I_13 = i(13), + I_big1 = big(1), + Vars = [{'I_13', I_13}, {'I_big1', I_big1}], - ?line lists:foreach(fun one_test/1, eval_list(l(I_13, I_big1), Vars)). + lists:foreach(fun one_test/1, eval_list(l(I_13, I_big1), Vars)). %%% Misc @@ -271,8 +271,8 @@ gen_l(N, S, A) -> [?T(<>, comp(N, A, S))]. test2(Config) when is_list(Config) -> - ?line test2(0, 8, 2#10101010101010101), - ?line test2(0, 8, 2#1111111111). + test2(0, 8, 2#10101010101010101), + test2(0, 8, 2#1111111111). test2(End, End, _) -> ok; @@ -298,8 +298,8 @@ t3() -> ]. test3(Config) when is_list(Config) -> - ?line Vars = [], - ?line lists:foreach(fun one_test/1, eval_list(t3(), Vars)). + Vars = [], + lists:foreach(fun one_test/1, eval_list(t3(), Vars)). gen_u(N, S, A) -> [?N(<>)]. @@ -308,8 +308,8 @@ gen_u_l(N, S, A) -> [?N(<>)]. test4(Config) when is_list(Config) -> - ?line test4(0, 16, 2#10101010101010101), - ?line test4(0, 16, 2#1111111111). + test4(0, 16, 2#10101010101010101), + test4(0, 16, 2#1111111111). test4(End, End, _) -> ok; @@ -329,8 +329,8 @@ gen_b(N, S, A) -> %% OTP-3995 test5(Config) when is_list(Config) -> - ?line test5(0, 8, <<73>>), - ?line test5(0, 8, <<68>>). + test5(0, 8, <<73>>), + test5(0, 8, <<68>>). test5(End, End, _) -> ok; @@ -345,45 +345,45 @@ test5(S, A) -> %%% Failure cases testf(Config) when is_list(Config) -> - ?line ?FAIL(<<3.14>>), - ?line ?FAIL(<<<<1,2>>>>), + ?FAIL(<<3.14>>), + ?FAIL(<<<<1,2>>>>), - ?line ?FAIL(<<2.71/binary>>), - ?line ?FAIL(<<24334/binary>>), - ?line ?FAIL(<<24334344294788947129487129487219847/binary>>), + ?FAIL(<<2.71/binary>>), + ?FAIL(<<24334/binary>>), + ?FAIL(<<24334344294788947129487129487219847/binary>>), BigInt = id(24334344294788947129487129487219847), - ?line ?FAIL_VARS(<>, [{'BigInt',BigInt}]), - ?line ?FAIL_VARS(<<42,BigInt/binary>>, [{'BigInt',BigInt}]), - ?line ?FAIL_VARS(<>, [{'BigInt',BigInt}]), + ?FAIL_VARS(<>, [{'BigInt',BigInt}]), + ?FAIL_VARS(<<42,BigInt/binary>>, [{'BigInt',BigInt}]), + ?FAIL_VARS(<>, [{'BigInt',BigInt}]), %% One negative field size, but the sum of field sizes will be 1 byte. %% Make sure that we reject that properly. I_minus_777 = id(-777), I_minus_2047 = id(-2047), - ?line ?FAIL_VARS(<>, + ?FAIL_VARS(<>, ordsets:from_list([{'I_minus_777',I_minus_777}, {'I_minus_2047',I_minus_2047}])), - ?line ?FAIL(<<<<1,2,3>>/float>>), + ?FAIL(<<<<1,2,3>>/float>>), %% Negative field widths. - ?line testf_1(-8, <<1,2,3,4,5>>), - ?line ?FAIL(<<0:(-(1 bsl 100))>>), + testf_1(-8, <<1,2,3,4,5>>), + ?FAIL(<<0:(-(1 bsl 100))>>), - ?line ?FAIL(<<42:(-16)>>), - ?line ?FAIL(<<3.14:(-8)/float>>), - ?line ?FAIL(<<<<23,56,0,2>>:(-16)/binary>>), - ?line ?FAIL(<<<<23,56,0,2>>:(2.5)/binary>>), - ?line ?FAIL(<<<<23,56,0,2>>:(anka)>>), - ?line ?FAIL(<<<<23,56,0,2>>:(anka)>>), + ?FAIL(<<42:(-16)>>), + ?FAIL(<<3.14:(-8)/float>>), + ?FAIL(<<<<23,56,0,2>>:(-16)/binary>>), + ?FAIL(<<<<23,56,0,2>>:(2.5)/binary>>), + ?FAIL(<<<<23,56,0,2>>:(anka)>>), + ?FAIL(<<<<23,56,0,2>>:(anka)>>), %% Unit failures. - ?line ?FAIL(<<<<1:1>>/binary>>), + ?FAIL(<<<<1:1>>/binary>>), Sz = id(1), - ?line ?FAIL_VARS(<<<<1:Sz>>/binary>>, [{'Sz',Sz}]), - ?line {'EXIT',{badarg,_}} = (catch <<<<1:(id(1))>>/binary>>), - ?line ?FAIL(<<<<7,8,9>>/binary-unit:16>>), - ?line ?FAIL(<<<<7,8,9,3:7>>/binary-unit:16>>), - ?line ?FAIL(<<<<7,8,9,3:7>>/binary-unit:17>>), + ?FAIL_VARS(<<<<1:Sz>>/binary>>, [{'Sz',Sz}]), + {'EXIT',{badarg,_}} = (catch <<<<1:(id(1))>>/binary>>), + ?FAIL(<<<<7,8,9>>/binary-unit:16>>), + ?FAIL(<<<<7,8,9,3:7>>/binary-unit:16>>), + ?FAIL(<<<<7,8,9,3:7>>/binary-unit:17>>), ok. @@ -395,11 +395,11 @@ testf_1(W, B) -> %% Test that constructed binaries that are not used will still give an exception. not_used(Config) when is_list(Config) -> - ?line ok = not_used1(3, <<"dum">>), - ?line {'EXIT',{badarg,_}} = (catch not_used1(3, "dum")), - ?line {'EXIT',{badarg,_}} = (catch not_used2(444, -2)), - ?line {'EXIT',{badarg,_}} = (catch not_used2(444, anka)), - ?line {'EXIT',{badarg,_}} = (catch not_used3(444)), + ok = not_used1(3, <<"dum">>), + {'EXIT',{badarg,_}} = (catch not_used1(3, "dum")), + {'EXIT',{badarg,_}} = (catch not_used2(444, -2)), + {'EXIT',{badarg,_}} = (catch not_used2(444, anka)), + {'EXIT',{badarg,_}} = (catch not_used3(444)), ok. not_used1(I, BinString) -> @@ -415,11 +415,11 @@ not_used3(I) -> ok. in_guard(Config) when is_list(Config) -> - ?line 1 = in_guard(<<16#74ad:16>>, 16#e95, 5), - ?line 2 = in_guard(<<16#3A,16#F7,"hello">>, 16#3AF7, <<"hello">>), - ?line 3 = in_guard(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415), - ?line 3 = in_guard(<<16#FBCD:14,3/float,3:2>>, 16#FBCD, 3), - ?line 3 = in_guard(<<16#FBCD:14,(2 bsl 226)/float,3:2>>, 16#FBCD, 2 bsl 226), + 1 = in_guard(<<16#74ad:16>>, 16#e95, 5), + 2 = in_guard(<<16#3A,16#F7,"hello">>, 16#3AF7, <<"hello">>), + 3 = in_guard(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415), + 3 = in_guard(<<16#FBCD:14,3/float,3:2>>, 16#FBCD, 3), + 3 = in_guard(<<16#FBCD:14,(2 bsl 226)/float,3:2>>, 16#FBCD, 2 bsl 226), nope = in_guard(<<1>>, 42, b), nope = in_guard(<<1>>, a, b), nope = in_guard(<<1,2>>, 1, 1), @@ -435,14 +435,14 @@ in_guard(_, _, _) -> nope. %% Make sure that construction has no memory leak mem_leak(Config) when is_list(Config) -> - ?line B = make_bin(16, <<0>>), - ?line mem_leak(1024, B), + B = make_bin(16, <<0>>), + mem_leak(1024, B), ok. mem_leak(0, _) -> ok; mem_leak(N, B) -> - ?line big_bin(B, <<23>>), - ?line {'EXIT',{badarg,_}} = (catch big_bin(B, bad)), + big_bin(B, <<23>>), + {'EXIT',{badarg,_}} = (catch big_bin(B, bad)), mem_leak(N-1, B). big_bin(B1, B2) -> @@ -456,18 +456,18 @@ make_bin(0, Acc) -> Acc; make_bin(N, Acc) -> make_bin(N-1, <>). -define(COF(Int0), - ?line (fun(Int) -> + (fun(Int) -> true = <> =:= <<(float(Int)):32/float>>, true = <> =:= <<(float(Int)):64/float>> end)(nonliteral(Int0)), - ?line true = <> =:= <<(float(Int0)):32/float>>, - ?line true = <> =:= <<(float(Int0)):64/float>>). + true = <> =:= <<(float(Int0)):32/float>>, + true = <> =:= <<(float(Int0)):64/float>>). -define(COF64(Int0), - ?line (fun(Int) -> + (fun(Int) -> true = <> =:= <<(float(Int)):64/float>> end)(nonliteral(Int0)), - ?line true = <> =:= <<(float(Int0)):64/float>>). + true = <> =:= <<(float(Int0)):64/float>>). nonliteral(X) -> X. @@ -486,7 +486,7 @@ coerce_to_float(Config) when is_list(Config) -> ok. bjorn(Config) when is_list(Config) -> - ?line error = bjorn_1(), + error = bjorn_1(), ok. bjorn_1() -> @@ -514,46 +514,46 @@ do_something() -> throw(blurf). huge_float_field(Config) when is_list(Config) -> - ?line {'EXIT',{badarg,_}} = (catch <<0.0:9/float-unit:8>>), - ?line huge_float_check(catch <<0.0:67108865/float-unit:64>>), - ?line huge_float_check(catch <<0.0:((1 bsl 26)+1)/float-unit:64>>), - ?line huge_float_check(catch <<0.0:(id(67108865))/float-unit:64>>), -%% ?line huge_float_check(catch <<0.0:((1 bsl 60)+1)/float-unit:64>>), - ?line huge_float_check(catch <<3839739387439387383739387987347983:((1 bsl 26)+1)/float-unit:64>>), -%% ?line huge_float_check(catch <<3839739387439387383739387987347983:((1 bsl 60)+1)/float-unit:64>>), + {'EXIT',{badarg,_}} = (catch <<0.0:9/float-unit:8>>), + huge_float_check(catch <<0.0:67108865/float-unit:64>>), + huge_float_check(catch <<0.0:((1 bsl 26)+1)/float-unit:64>>), + huge_float_check(catch <<0.0:(id(67108865))/float-unit:64>>), +%% huge_float_check(catch <<0.0:((1 bsl 60)+1)/float-unit:64>>), + huge_float_check(catch <<3839739387439387383739387987347983:((1 bsl 26)+1)/float-unit:64>>), +%% huge_float_check(catch <<3839739387439387383739387987347983:((1 bsl 60)+1)/float-unit:64>>), ok. huge_float_check({'EXIT',{system_limit,_}}) -> ok; huge_float_check({'EXIT',{badarg,_}}) -> ok. huge_binary(Config) when is_list(Config) -> - ?line 16777216 = size(<<0:(id(1 bsl 26)),(-1):(id(1 bsl 26))>>), - ?line garbage_collect(), + 16777216 = size(<<0:(id(1 bsl 26)),(-1):(id(1 bsl 26))>>), + garbage_collect(), {Shift,Return} = case free_mem() of undefined -> %% This test has to be inlined inside the case to %% use a literal Shift - ?line garbage_collect(), - ?line id(<<0:((1 bsl 32)-1)>>), + garbage_collect(), + id(<<0:((1 bsl 32)-1)>>), {32,ok}; Mb when Mb > 600 -> - ?line garbage_collect(), - ?line id(<<0:((1 bsl 32)-1)>>), + garbage_collect(), + id(<<0:((1 bsl 32)-1)>>), {32,ok}; Mb when Mb > 300 -> - ?line garbage_collect(), - ?line id(<<0:((1 bsl 31)-1)>>), + garbage_collect(), + id(<<0:((1 bsl 31)-1)>>), {31,"Limit huge binaries to 256 Mb"}; _ -> - ?line garbage_collect(), - ?line id(<<0:((1 bsl 30)-1)>>), + garbage_collect(), + id(<<0:((1 bsl 30)-1)>>), {30,"Limit huge binary to 128 Mb"} end, - ?line garbage_collect(), - ?line id(<<0:((1 bsl Shift)-1)>>), - ?line garbage_collect(), - ?line id(<<0:(id((1 bsl Shift)-1))>>), - ?line garbage_collect(), + garbage_collect(), + id(<<0:((1 bsl Shift)-1)>>), + garbage_collect(), + id(<<0:(id((1 bsl Shift)-1))>>), + garbage_collect(), case Return of ok -> ok; Comment -> {comment, Comment} @@ -588,16 +588,16 @@ free_mem() -> system_limit(Config) when is_list(Config) -> WordSize = erlang:system_info(wordsize), BitsPerWord = WordSize * 8, - ?line {'EXIT',{system_limit,_}} = + {'EXIT',{system_limit,_}} = (catch <<0:(id(0)),42:(id(1 bsl BitsPerWord))>>), - ?line {'EXIT',{system_limit,_}} = + {'EXIT',{system_limit,_}} = (catch <<42:(id(1 bsl BitsPerWord)),0:(id(0))>>), - ?line {'EXIT',{system_limit,_}} = + {'EXIT',{system_limit,_}} = (catch <<(id(<<>>))/binary,0:(id(1 bsl 100))>>), %% Would fail to load. - ?line {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 67)>>), - ?line {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 64)+1)>>), + {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 67)>>), + {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 64)+1)>>), case WordSize of 4 -> @@ -607,55 +607,55 @@ system_limit(Config) when is_list(Config) -> end. system_limit_32() -> - ?line {'EXIT',{badarg,_}} = (catch <<42:(-1)>>), - ?line {'EXIT',{badarg,_}} = (catch <<42:(id(-1))>>), - ?line {'EXIT',{badarg,_}} = (catch <<42:(id(-389739873536870912))/unit:8>>), - ?line {'EXIT',{system_limit,_}} = (catch <<42:536870912/unit:8>>), - ?line {'EXIT',{system_limit,_}} = (catch <<42:(id(536870912))/unit:8>>), - ?line {'EXIT',{system_limit,_}} = (catch <<0:(id(8)),42:536870912/unit:8>>), - ?line {'EXIT',{system_limit,_}} = + {'EXIT',{badarg,_}} = (catch <<42:(-1)>>), + {'EXIT',{badarg,_}} = (catch <<42:(id(-1))>>), + {'EXIT',{badarg,_}} = (catch <<42:(id(-389739873536870912))/unit:8>>), + {'EXIT',{system_limit,_}} = (catch <<42:536870912/unit:8>>), + {'EXIT',{system_limit,_}} = (catch <<42:(id(536870912))/unit:8>>), + {'EXIT',{system_limit,_}} = (catch <<0:(id(8)),42:536870912/unit:8>>), + {'EXIT',{system_limit,_}} = (catch <<0:(id(8)),42:(id(536870912))/unit:8>>), %% The size would be silently truncated, resulting in a crash. - ?line {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 35)>>), - ?line {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 32)+1)>>), + {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 35)>>), + {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 32)+1)>>), %% Would fail to load. - ?line {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 43)>>), - ?line {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 40)+1)>>), + {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 43)>>), + {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 40)+1)>>), ok. badarg(Config) when is_list(Config) -> - ?line {'EXIT',{badarg,_}} = + {'EXIT',{badarg,_}} = (catch <<0:(id(1 bsl 100)),0:(id(-1))>>), - ?line {'EXIT',{badarg,_}} = + {'EXIT',{badarg,_}} = (catch <<0:(id(1 bsl 100)),0:(id(-(1 bsl 70)))>>), - ?line {'EXIT',{badarg,_}} = + {'EXIT',{badarg,_}} = (catch <<0:(id(-(1 bsl 70))),0:(id(1 bsl 100))>>), - ?line {'EXIT',{badarg,_}} = + {'EXIT',{badarg,_}} = (catch <<(id(<<>>))/binary,0:(id(-(1 bsl 100)))>>), ok. copy_writable_binary(Config) when is_list(Config) -> - ?line [copy_writable_binary_1(I) || I <- lists:seq(0, 256)], + [copy_writable_binary_1(I) || I <- lists:seq(0, 256)], ok. copy_writable_binary_1(_) -> - ?line Bin0 = <<(id(<<>>))/binary,0,1,2,3,4,5,6,7>>, - ?line SubBin = make_sub_bin(Bin0), - ?line id(<<42,34,55,Bin0/binary>>), %Make reallocation likelier. - ?line Pid = spawn(fun() -> + Bin0 = <<(id(<<>>))/binary,0,1,2,3,4,5,6,7>>, + SubBin = make_sub_bin(Bin0), + id(<<42,34,55,Bin0/binary>>), %Make reallocation likelier. + Pid = spawn(fun() -> copy_writable_binary_holder(Bin0, SubBin) end), - ?line Tab = ets:new(holder, []), - ?line ets:insert(Tab, {17,Bin0}), - ?line ets:insert(Tab, {42,SubBin}), - ?line id(<>), - ?line Pid ! self(), - ?line [{17,Bin0}] = ets:lookup(Tab, 17), - ?line [{42,Bin0}] = ets:lookup(Tab, 42), + Tab = ets:new(holder, []), + ets:insert(Tab, {17,Bin0}), + ets:insert(Tab, {42,SubBin}), + id(<>), + Pid ! self(), + [{17,Bin0}] = ets:lookup(Tab, 17), + [{42,Bin0}] = ets:lookup(Tab, 42), receive {Pid,Bin0,Bin0} -> ok; Other -> @@ -700,8 +700,8 @@ have_250_terabytes_of_ram() -> false. %% give the same result. dynamic(Config) when is_list(Config) -> - ?line dynamic_1(fun dynamic_big/5), - ?line dynamic_1(fun dynamic_little/5), + dynamic_1(fun dynamic_big/5), + dynamic_1(fun dynamic_little/5), ok. dynamic_1(Dynamic) -> @@ -780,32 +780,32 @@ bs_add(Config) when is_list(Config) -> return], %% Write assembly file and assemble it. - ?line PrivDir = proplists:get_value(priv_dir, Config), - ?line RootName = filename:join(PrivDir, atom_to_list(Mod)), - ?line AsmFile = RootName ++ ".S", - ?line {ok,Fd} = file:open(AsmFile, [write]), - ?line [io:format(Fd, "~p. \n", [T]) || T <- Code], - ?line ok = file:close(Fd), - ?line {ok,Mod} = compile:file(AsmFile, [from_asm,report,{outdir,PrivDir}]), - ?line LoadRc = code:load_abs(RootName), - ?line {module,_Module} = LoadRc, + PrivDir = proplists:get_value(priv_dir, Config), + RootName = filename:join(PrivDir, atom_to_list(Mod)), + AsmFile = RootName ++ ".S", + {ok,Fd} = file:open(AsmFile, [write]), + [io:format(Fd, "~p. \n", [T]) || T <- Code], + ok = file:close(Fd), + {ok,Mod} = compile:file(AsmFile, [from_asm,report,{outdir,PrivDir}]), + LoadRc = code:load_abs(RootName), + {module,_Module} = LoadRc, %% Find smallest positive bignum. - ?line SmallestBig = smallest_big(), - ?line io:format("~p\n", [SmallestBig]), - ?line Expected = SmallestBig + N, + SmallestBig = smallest_big(), + io:format("~p\n", [SmallestBig]), + Expected = SmallestBig + N, DoTest = fun() -> exit(Mod:bs_add(SmallestBig, -SmallestBig)) end, - ?line {Pid,Mref} = spawn_monitor(DoTest), + {Pid,Mref} = spawn_monitor(DoTest), receive {'DOWN',Mref,process,Pid,Res} -> ok end, - ?line Expected = Res, + Expected = Res, %% Clean up. - ?line ok = file:delete(AsmFile), - ?line ok = file:delete(code:which(Mod)), + ok = file:delete(AsmFile), + ok = file:delete(code:which(Mod)), ok. @@ -846,17 +846,17 @@ otp_7422_bin(N) when N < 512 -> otp_7422_bin(_) -> ok. zero_width(Config) when is_list(Config) -> - ?line Z = id(0), + Z = id(0), Small = id(42), Big = id(1 bsl 128), - ?line <<>> = <>, - ?line <<>> = <>, - ?line <<>> = <>, - ?line <<>> = <>, + <<>> = <>, + <<>> = <>, + <<>> = <>, + <<>> = <>, - ?line {'EXIT',{badarg,_}} = (catch <>), - ?line {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):Z>>), - ?line {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):0>>), + {'EXIT',{badarg,_}} = (catch <>), + {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):Z>>), + {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):0>>), ok. @@ -913,8 +913,8 @@ bs_add_overflow(Config) -> Large = <<0:((1 bsl 30)-1)>>, {'EXIT',{system_limit,_}} = (catch <>), + Large/bits, Large/bits, Large/bits, Large/bits, + Large/bits>>), ok end. diff --git a/erts/emulator/test/bs_match_bin_SUITE.erl b/erts/emulator/test/bs_match_bin_SUITE.erl index 7b82485216..6f7abf8c4e 100644 --- a/erts/emulator/test/bs_match_bin_SUITE.erl +++ b/erts/emulator/test/bs_match_bin_SUITE.erl @@ -49,31 +49,31 @@ end_per_group(_GroupName, Config) -> %% Tries to split a binary at all byte-aligned positions. byte_split_binary(Config) when is_list(Config) -> - ?line L = lists:seq(0, 57), - ?line B = mkbin(L), - ?line byte_split(L, B, size(B)), - ?line Unaligned = make_unaligned_sub_binary(B), - ?line byte_split(L, Unaligned, size(Unaligned)). + L = lists:seq(0, 57), + B = mkbin(L), + byte_split(L, B, size(B)), + Unaligned = make_unaligned_sub_binary(B), + byte_split(L, Unaligned, size(Unaligned)). byte_split(L, B, Pos) when Pos >= 0 -> - ?line Sz1 = Pos, - ?line Sz2 = size(B) - Pos, - ?line <> = B, - ?line B1 = list_to_binary(lists:sublist(L, 1, Pos)), - ?line B2 = list_to_binary(lists:nthtail(Pos, L)), - ?line byte_split(L, B, Pos-1); + Sz1 = Pos, + Sz2 = size(B) - Pos, + <> = B, + B1 = list_to_binary(lists:sublist(L, 1, Pos)), + B2 = list_to_binary(lists:nthtail(Pos, L)), + byte_split(L, B, Pos-1); byte_split(_, _, _) -> ok. %% Tries to split a binary at all positions. bit_split_binary(Config) when is_list(Config) -> Fun = fun(Bin, List, SkipBef, N) -> - ?line SkipAft = 8*size(Bin) - N - SkipBef, + SkipAft = 8*size(Bin) - N - SkipBef, %%io:format("~p, ~p, ~p", [SkipBef,N,SkipAft]), - ?line <<_:SkipBef,OutBin:N/binary-unit:1,_:SkipAft>> = Bin, - ?line OutBin = make_bin_from_list(List, N) + <<_:SkipBef,OutBin:N/binary-unit:1,_:SkipAft>> = Bin, + OutBin = make_bin_from_list(List, N) end, - ?line bit_split_binary1(Fun, erlang:md5(<<1,2,3>>)), - ?line bit_split_binary1(Fun, + bit_split_binary1(Fun, erlang:md5(<<1,2,3>>)), + bit_split_binary1(Fun, make_unaligned_sub_binary(erlang:md5(<<1,2,3>>))), ok. @@ -119,19 +119,19 @@ make_unaligned_sub_binary(Bin0) -> id(I) -> I. match_huge_bin(Config) when is_list(Config) -> - ?line Bin = <<0:(1 bsl 27),13:8>>, - ?line skip_huge_bin_1(1 bsl 27, Bin), - ?line 16777216 = match_huge_bin_1(1 bsl 27, Bin), + Bin = <<0:(1 bsl 27),13:8>>, + skip_huge_bin_1(1 bsl 27, Bin), + 16777216 = match_huge_bin_1(1 bsl 27, Bin), %% Test overflowing the size of a binary field. - ?line nomatch = overflow_huge_bin_skip_32(Bin), - ?line nomatch = overflow_huge_bin_32(Bin), - ?line nomatch = overflow_huge_bin_skip_64(Bin), - ?line nomatch = overflow_huge_bin_64(Bin), + nomatch = overflow_huge_bin_skip_32(Bin), + nomatch = overflow_huge_bin_32(Bin), + nomatch = overflow_huge_bin_skip_64(Bin), + nomatch = overflow_huge_bin_64(Bin), %% Size in variable - ?line ok = overflow_huge_bin(Bin, lists:seq(25, 32)++lists:seq(50, 64)), - ?line ok = overflow_huge_bin_unit128(Bin, lists:seq(25, 32)++lists:seq(50, 64)), + ok = overflow_huge_bin(Bin, lists:seq(25, 32)++lists:seq(50, 64)), + ok = overflow_huge_bin_unit128(Bin, lists:seq(25, 32)++lists:seq(50, 64)), ok. diff --git a/erts/emulator/test/bs_match_int_SUITE.erl b/erts/emulator/test/bs_match_int_SUITE.erl index 562aeef6f6..42d692c1ba 100644 --- a/erts/emulator/test/bs_match_int_SUITE.erl +++ b/erts/emulator/test/bs_match_int_SUITE.erl @@ -51,22 +51,22 @@ end_per_group(_GroupName, Config) -> integer(Config) when is_list(Config) -> - ?line 0 = get_int(mkbin([])), - ?line 0 = get_int(mkbin([0])), - ?line 42 = get_int(mkbin([42])), - ?line 255 = get_int(mkbin([255])), - ?line 256 = get_int(mkbin([1,0])), - ?line 257 = get_int(mkbin([1,1])), - ?line 258 = get_int(mkbin([1,2])), - ?line 258 = get_int(mkbin([1,2])), - ?line 65534 = get_int(mkbin([255,254])), - ?line 16776455 = get_int(mkbin([255,253,7])), - ?line 4245492555 = get_int(mkbin([253,13,19,75])), - ?line 4294967294 = get_int(mkbin([255,255,255,254])), - ?line 4294967295 = get_int(mkbin([255,255,255,255])), - ?line Eight = [200,1,19,128,222,42,97,111], - ?line cmp128(Eight, uint(Eight)), - ?line fun_clause(catch get_int(mkbin(seq(1,5)))), + 0 = get_int(mkbin([])), + 0 = get_int(mkbin([0])), + 42 = get_int(mkbin([42])), + 255 = get_int(mkbin([255])), + 256 = get_int(mkbin([1,0])), + 257 = get_int(mkbin([1,1])), + 258 = get_int(mkbin([1,2])), + 258 = get_int(mkbin([1,2])), + 65534 = get_int(mkbin([255,254])), + 16776455 = get_int(mkbin([255,253,7])), + 4245492555 = get_int(mkbin([253,13,19,75])), + 4294967294 = get_int(mkbin([255,255,255,254])), + 4294967295 = get_int(mkbin([255,255,255,255])), + Eight = [200,1,19,128,222,42,97,111], + cmp128(Eight, uint(Eight)), + fun_clause(catch get_int(mkbin(seq(1,5)))), ok. get_int(Bin) -> @@ -89,13 +89,13 @@ cmp128(<>, I) -> equal; cmp128(_, _) -> not_equal. signed_integer(Config) when is_list(Config) -> - ?line {no_match,_} = sint(mkbin([])), - ?line {no_match,_} = sint(mkbin([1,2,3])), - ?line 127 = sint(mkbin([127])), - ?line -1 = sint(mkbin([255])), - ?line -128 = sint(mkbin([128])), - ?line 42 = sint(mkbin([42,255])), - ?line 127 = sint(mkbin([127,255])). + {no_match,_} = sint(mkbin([])), + {no_match,_} = sint(mkbin([1,2,3])), + 127 = sint(mkbin([127])), + -1 = sint(mkbin([255])), + -128 = sint(mkbin([128])), + 42 = sint(mkbin([42,255])), + 127 = sint(mkbin([127,255])). sint(Bin) -> case Bin of @@ -139,7 +139,7 @@ more_dynamic(Config) when is_list(Config) -> <<_:SkipBef,Int:N,_:SkipAft>> = Bin, Int = make_int(List, N, 0) end, - ?line more_dynamic1(Unsigned, erlang:md5(mkbin([42]))), + more_dynamic1(Unsigned, erlang:md5(mkbin([42]))), %% Signed big-endian numbers. Signed = fun(Bin, List, SkipBef, N) -> @@ -154,7 +154,7 @@ more_dynamic(Config) when is_list(Config) -> ct:fail(signed_big_endian_fail) end end, - ?line more_dynamic1(Signed, erlang:md5(mkbin([43]))), + more_dynamic1(Signed, erlang:md5(mkbin([43]))), %% Unsigned little-endian numbers. UnsLittle = fun(Bin, List, SkipBef, N) -> @@ -162,7 +162,7 @@ more_dynamic(Config) when is_list(Config) -> <<_:SkipBef,Int:N/little,_:SkipAft>> = Bin, Int = make_int(big_to_little(List, N), N, 0) end, - ?line more_dynamic1(UnsLittle, erlang:md5(mkbin([44]))), + more_dynamic1(UnsLittle, erlang:md5(mkbin([44]))), %% Signed little-endian numbers. SignLittle = fun(Bin, List, SkipBef, N) -> @@ -171,7 +171,7 @@ more_dynamic(Config) when is_list(Config) -> Little = big_to_little(List, N), Int = make_signed_int(Little, N) end, - ?line more_dynamic1(SignLittle, erlang:md5(mkbin([45]))), + more_dynamic1(SignLittle, erlang:md5(mkbin([45]))), ok. @@ -227,23 +227,23 @@ mkbin(L) when is_list(L) -> list_to_binary(L). mml(Config) when is_list(Config) -> - ?line single_byte_binary = mml_choose(<<42>>), - ?line multi_byte_binary = mml_choose(<<42,43>>). + single_byte_binary = mml_choose(<<42>>), + multi_byte_binary = mml_choose(<<42,43>>). mml_choose(<<_A:8>>) -> single_byte_binary; mml_choose(<<_A:8,_T/binary>>) -> multi_byte_binary. match_huge_int(Config) when is_list(Config) -> Sz = 1 bsl 27, - ?line Bin = <<0:Sz,13:8>>, - ?line skip_huge_int_1(Sz, Bin), - ?line 0 = match_huge_int_1(Sz, Bin), + Bin = <<0:Sz,13:8>>, + skip_huge_int_1(Sz, Bin), + 0 = match_huge_int_1(Sz, Bin), %% Test overflowing the size of an integer field. - ?line nomatch = overflow_huge_int_skip_32(Bin), + nomatch = overflow_huge_int_skip_32(Bin), case erlang:system_info(wordsize) of 4 -> - ?line nomatch = overflow_huge_int_32(Bin); + nomatch = overflow_huge_int_32(Bin); 8 -> %% An attempt will be made to allocate heap space for %% the bignum (which will probably fail); only if the @@ -251,15 +251,15 @@ match_huge_int(Config) when is_list(Config) -> %% the binary is too small. ok end, - ?line nomatch = overflow_huge_int_skip_64(Bin), - ?line nomatch = overflow_huge_int_64(Bin), + nomatch = overflow_huge_int_skip_64(Bin), + nomatch = overflow_huge_int_64(Bin), %% Test overflowing the size of an integer field using variables as sizes. - ?line Sizes = case erlang:system_info(wordsize) of + Sizes = case erlang:system_info(wordsize) of 4 -> lists:seq(25, 32); 8 -> [] end ++ lists:seq(50, 64), - ?line ok = overflow_huge_int_unit128(Bin, Sizes), + ok = overflow_huge_int_unit128(Bin, Sizes), ok. @@ -326,19 +326,19 @@ overflow_huge_int_64(<>) -> {8,Int} overflow_huge_int_64(_) -> nomatch. bignum(Config) when is_list(Config) -> - ?line Bin = id(<<42,0:1024/unit:8,43>>), - ?line <<42:1025/little-integer-unit:8,_:8>> = Bin, - ?line <<_:8,43:1025/integer-unit:8>> = Bin, + Bin = id(<<42,0:1024/unit:8,43>>), + <<42:1025/little-integer-unit:8,_:8>> = Bin, + <<_:8,43:1025/integer-unit:8>> = Bin, - ?line BignumBin = id(<<0:512/unit:8,258254417031933722623:9/unit:8>>), - ?line <<258254417031933722623:(512+9)/unit:8>> = BignumBin, + BignumBin = id(<<0:512/unit:8,258254417031933722623:9/unit:8>>), + <<258254417031933722623:(512+9)/unit:8>> = BignumBin, erlang:garbage_collect(), %Search for holes in debug-build. ok. unaligned_32_bit(Config) when is_list(Config) -> %% There used to be a risk for heap overflow (fixed in R11B-5). - ?line L = unaligned_32_bit_1(<<-1:(64*1024)>>), - ?line unaligned_32_bit_verify(L, 1638). + L = unaligned_32_bit_1(<<-1:(64*1024)>>), + unaligned_32_bit_verify(L, 1638). unaligned_32_bit_1(<<1:1,U:32,_:7,T/binary>>) -> [U|unaligned_32_bit_1(T)]; diff --git a/erts/emulator/test/bs_match_misc_SUITE.erl b/erts/emulator/test/bs_match_misc_SUITE.erl index f32f012085..114d7ecb36 100644 --- a/erts/emulator/test/bs_match_misc_SUITE.erl +++ b/erts/emulator/test/bs_match_misc_SUITE.erl @@ -41,9 +41,9 @@ all() -> %% Test matching of bound variables. bound_var(Config) when is_list(Config) -> - ?line ok = bound_var(42, 13, <<42,13>>), - ?line nope = bound_var(42, 13, <<42,255>>), - ?line nope = bound_var(42, 13, <<154,255>>), + ok = bound_var(42, 13, <<42,13>>), + nope = bound_var(42, 13, <<42,255>>), + nope = bound_var(42, 13, <<154,255>>), ok. bound_var(A, B, <>) -> ok; @@ -51,11 +51,11 @@ bound_var(_, _, _) -> nope. %% Test matching of a bound tail. bound_tail(Config) when is_list(Config) -> - ?line ok = bound_tail(<<>>, <<13,14>>), - ?line ok = bound_tail(<<2,3>>, <<1,1,2,3>>), - ?line nope = bound_tail(<<2,3>>, <<1,1,2,7>>), - ?line nope = bound_tail(<<2,3>>, <<1,1,2,3,4>>), - ?line nope = bound_tail(<<2,3>>, <<>>), + ok = bound_tail(<<>>, <<13,14>>), + ok = bound_tail(<<2,3>>, <<1,1,2,3>>), + nope = bound_tail(<<2,3>>, <<1,1,2,7>>), + nope = bound_tail(<<2,3>>, <<1,1,2,3,4>>), + nope = bound_tail(<<2,3>>, <<>>), ok. bound_tail(T, <<_:16,T/binary>>) -> ok; @@ -65,26 +65,26 @@ t_float(Config) when is_list(Config) -> F = f1(), G = f_one(), - ?line G = match_float(<<63,128,0,0>>, 32, 0), - ?line G = match_float(<<63,240,0,0,0,0,0,0>>, 64, 0), + G = match_float(<<63,128,0,0>>, 32, 0), + G = match_float(<<63,240,0,0,0,0,0,0>>, 64, 0), - ?line fcmp(F, match_float(<>, 32, 0)), - ?line fcmp(F, match_float(<>, 64, 0)), - ?line fcmp(F, match_float(<<1:1,F:32/float,127:7>>, 32, 1)), - ?line fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)), - ?line fcmp(F, match_float(<<1:13,F:32/float,127:3>>, 32, 13)), - ?line fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)), + fcmp(F, match_float(<>, 32, 0)), + fcmp(F, match_float(<>, 64, 0)), + fcmp(F, match_float(<<1:1,F:32/float,127:7>>, 32, 1)), + fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)), + fcmp(F, match_float(<<1:13,F:32/float,127:3>>, 32, 13)), + fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)), - ?line {'EXIT',{{badmatch,_},_}} = (catch match_float(<<0,0>>, 16, 0)), - ?line {'EXIT',{{badmatch,_},_}} = (catch match_float(<<0,0>>, 16#7fffffff, 0)), + {'EXIT',{{badmatch,_},_}} = (catch match_float(<<0,0>>, 16, 0)), + {'EXIT',{{badmatch,_},_}} = (catch match_float(<<0,0>>, 16#7fffffff, 0)), ok. float_middle_endian(Config) when is_list(Config) -> F = 9007199254740990.0, % turns to -NaN when word-swapped - ?line fcmp(F, match_float(<>, 64, 0)), - ?line fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)), - ?line fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)), + fcmp(F, match_float(<>, 64, 0)), + fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)), + fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)), ok. @@ -101,15 +101,15 @@ little_float(Config) when is_list(Config) -> F = f2(), G = f_one(), - ?line G = match_float_little(<<0,0,0,0,0,0,240,63>>, 64, 0), - ?line G = match_float_little(<<0,0,128,63>>, 32, 0), + G = match_float_little(<<0,0,0,0,0,0,240,63>>, 64, 0), + G = match_float_little(<<0,0,128,63>>, 32, 0), - ?line fcmp(F, match_float_little(<>, 32, 0)), - ?line fcmp(F, match_float_little(<>, 64, 0)), - ?line fcmp(F, match_float_little(<<1:1,F:32/float-little,127:7>>, 32, 1)), - ?line fcmp(F, match_float_little(<<1:1,F:64/float-little,127:7>>, 64, 1)), - ?line fcmp(F, match_float_little(<<1:13,F:32/float-little,127:3>>, 32, 13)), - ?line fcmp(F, match_float_little(<<1:13,F:64/float-little,127:3>>, 64, 13)), + fcmp(F, match_float_little(<>, 32, 0)), + fcmp(F, match_float_little(<>, 64, 0)), + fcmp(F, match_float_little(<<1:1,F:32/float-little,127:7>>, 32, 1)), + fcmp(F, match_float_little(<<1:1,F:64/float-little,127:7>>, 64, 1)), + fcmp(F, match_float_little(<<1:13,F:32/float-little,127:3>>, 32, 13)), + fcmp(F, match_float_little(<<1:13,F:64/float-little,127:3>>, 64, 13)), ok. @@ -137,16 +137,16 @@ f_one() -> 1.0. sean(Config) when is_list(Config) -> - ?line small = sean1(<<>>), - ?line small = sean1(<<1>>), - ?line small = sean1(<<1,2>>), - ?line small = sean1(<<1,2,3>>), - ?line large = sean1(<<1,2,3,4>>), - - ?line small = sean1(<<4>>), - ?line small = sean1(<<4,5>>), - ?line small = sean1(<<4,5,6>>), - ?line {'EXIT',{function_clause,_}} = (catch sean1(<<4,5,6,7>>)), + small = sean1(<<>>), + small = sean1(<<1>>), + small = sean1(<<1,2>>), + small = sean1(<<1,2,3>>), + large = sean1(<<1,2,3,4>>), + + small = sean1(<<4>>), + small = sean1(<<4,5>>), + small = sean1(<<4,5,6>>), + {'EXIT',{function_clause,_}} = (catch sean1(<<4,5,6,7>>)), ok. sean1(<>) when byte_size(B) < 4 -> small; @@ -278,28 +278,28 @@ getBase64Char(_Else) -> -define(M(F), <> = <>). native(Config) when is_list(Config) -> - ?line ?M(3.14:64/native-float), - ?line ?M(333:16/native), - ?line ?M(38658345:32/native), + ?M(3.14:64/native-float), + ?M(333:16/native), + ?M(38658345:32/native), case <<1:16/native>> of <<0,1>> -> native_big(); <<1,0>> -> native_little() end. native_big() -> - ?line <<37.33:64/native-float>> = <<37.33:64/big-float>>, - ?line <<3974:16/native-integer>> = <<3974:16/big-integer>>, + <<37.33:64/native-float>> = <<37.33:64/big-float>>, + <<3974:16/native-integer>> = <<3974:16/big-integer>>, {comment,"Big endian"}. native_little() -> - ?line <<37869.32343:64/native-float>> = <<37869.32343:64/little-float>>, - ?line <<7974:16/native-integer>> = <<7974:16/little-integer>>, + <<37869.32343:64/native-float>> = <<37869.32343:64/little-float>>, + <<7974:16/native-integer>> = <<7974:16/little-integer>>, {comment,"Little endian"}. happi(Config) when is_list(Config) -> Bin = <<".123">>, - ?line <<"123">> = lex_digits1(Bin, 1, []), - ?line <<"123">> = lex_digits2(Bin, 1, []), + <<"123">> = lex_digits1(Bin, 1, []), + <<"123">> = lex_digits2(Bin, 1, []), ok. lex_digits1(<<$., Rest/binary>>,_Val,_Acc) -> @@ -320,16 +320,16 @@ dec(A) -> A-$0. size_var(Config) when is_list(Config) -> - ?line {<<45>>,<<>>} = split(<<1:16,45>>), - ?line {<<45>>,<<46,47>>} = split(<<1:16,45,46,47>>), - ?line {<<45,46>>,<<47>>} = split(<<2:16,45,46,47>>), + {<<45>>,<<>>} = split(<<1:16,45>>), + {<<45>>,<<46,47>>} = split(<<1:16,45,46,47>>), + {<<45,46>>,<<47>>} = split(<<2:16,45,46,47>>), - ?line {<<45,46,47>>,<<48>>} = split_2(<<16:8,3:16,45,46,47,48>>), + {<<45,46,47>>,<<48>>} = split_2(<<16:8,3:16,45,46,47,48>>), - ?line {<<45,46>>,<<47>>} = split(2, <<2:16,45,46,47>>), - ?line {'EXIT',{function_clause,_}} = (catch split(42, <<2:16,45,46,47>>)), + {<<45,46>>,<<47>>} = split(2, <<2:16,45,46,47>>), + {'EXIT',{function_clause,_}} = (catch split(42, <<2:16,45,46,47>>)), - ?line <<"cdef">> = skip(<<2:8,"abcdef">>), + <<"cdef">> = skip(<<2:8,"abcdef">>), ok. @@ -345,11 +345,11 @@ split_2(<>) -> skip(<>) -> T. wiger(Config) when is_list(Config) -> - ?line ok1 = wcheck(<<3>>), - ?line ok2 = wcheck(<<1,2,3>>), - ?line ok3 = wcheck(<<4>>), - ?line {error,<<1,2,3,4>>} = wcheck(<<1,2,3,4>>), - ?line {error,<<>>} = wcheck(<<>>), + ok1 = wcheck(<<3>>), + ok2 = wcheck(<<1,2,3>>), + ok3 = wcheck(<<4>>), + {error,<<1,2,3,4>>} = wcheck(<<1,2,3,4>>), + {error,<<>>} = wcheck(<<>>), ok. wcheck(<>) when A==3-> @@ -382,24 +382,24 @@ x0_2(_, Bin) -> x0_3(_, Bin) -> case Bin of - <<_:72,7:8,_/binary>> -> - ct:fail(bs_matched_1); - <<_:64,0:16,_/binary>> -> - ct:fail(bs_matched_2); - <<_:64,42:16,123456:32,_/binary>> -> - ok + <<_:72,7:8,_/binary>> -> + ct:fail(bs_matched_1); + <<_:64,0:16,_/binary>> -> + ct:fail(bs_matched_2); + <<_:64,42:16,123456:32,_/binary>> -> + ok end. huge_float_field(Config) when is_list(Config) -> Sz = 1 bsl 27, - ?line Bin = <<0:Sz>>, + Bin = <<0:Sz>>, - ?line nomatch = overflow_huge_float_skip_32(Bin), - ?line nomatch = overflow_huge_float_32(Bin), + nomatch = overflow_huge_float_skip_32(Bin), + nomatch = overflow_huge_float_32(Bin), - ?line ok = overflow_huge_float(Bin, lists:seq(25, 32)++lists:seq(50, 64)), - ?line ok = overflow_huge_float_unit128(Bin, lists:seq(25, 32)++lists:seq(50, 64)), + ok = overflow_huge_float(Bin, lists:seq(25, 32)++lists:seq(50, 64)), + ok = overflow_huge_float_unit128(Bin, lists:seq(25, 32)++lists:seq(50, 64)), ok. overflow_huge_float_skip_32(<<_:4294967296/float,0,_/binary>>) -> 1; % 1 bsl 32 @@ -441,15 +441,15 @@ overflow_huge_float(_, []) -> ok. overflow_huge_float_unit128(Bin, [Sz0|Sizes]) -> Sz = id(1 bsl Sz0), case Bin of - <<_:Sz/float-unit:128,0,_/binary>> -> - {error,Sz}; - _ -> - case Bin of - <> -> - {error,Sz,Var}; - _ -> - overflow_huge_float_unit128(Bin, Sizes) - end + <<_:Sz/float-unit:128,0,_/binary>> -> + {error,Sz}; + _ -> + case Bin of + <> -> + {error,Sz,Var}; + _ -> + overflow_huge_float_unit128(Bin, Sizes) + end end; overflow_huge_float_unit128(_, []) -> ok. @@ -459,25 +459,24 @@ overflow_huge_float_unit128(_, []) -> ok. %% writable_binary_matched(Config) when is_list(Config) -> - ?line WritableBin = create_writeable_binary(), - ?line writable_binary_matched(WritableBin, WritableBin, 500). + WritableBin = create_writeable_binary(), + writable_binary_matched(WritableBin, WritableBin, 500). writable_binary_matched(<<0>>, _, N) -> - if - N =:= 0 -> ok; - true -> - put(grow_heap, [N|get(grow_heap)]), - ?line WritableBin = create_writeable_binary(), - ?line writable_binary_matched(WritableBin, WritableBin, N-1) + if N =:= 0 -> ok; + true -> + put(grow_heap, [N|get(grow_heap)]), + WritableBin = create_writeable_binary(), + writable_binary_matched(WritableBin, WritableBin, N-1) end; writable_binary_matched(<>, WritableBin0, N) -> - ?line WritableBin = writable_binary(WritableBin0, B), + WritableBin = writable_binary(WritableBin0, B), writable_binary_matched(T, WritableBin, N). writable_binary(WritableBin0, B) when is_binary(WritableBin0) -> %% Heavy append to force the binary to move. - ?line WritableBin = <>, - ?line id(<<(id(0)):128/unit:8>>), + WritableBin = <>, + id(<<(id(0)):128/unit:8>>), WritableBin. create_writeable_binary() -> @@ -488,7 +487,7 @@ otp_7198(Config) when is_list(Config) -> %% increase the number of saved positions, the thing word was not updated %% to account for the new size. Therefore, if there was a garbage collection, %% the new slots would be included in the garbage collection. - ?line [do_otp_7198(FillerSize) || FillerSize <- lists:seq(0, 256)], + [do_otp_7198(FillerSize) || FillerSize <- lists:seq(0, 256)], ok. do_otp_7198(FillerSize) -> @@ -525,27 +524,27 @@ otp_7198_scan(<<>>, TokAcc) -> otp_7198_scan(<>, TokAcc) when (D =:= $D orelse D =:= $d) and ((Z =:= $\s) or (Z =:= $() or (Z =:= $))) -> - otp_7198_scan(<>, ['AND' | TokAcc]); + otp_7198_scan(<>, ['AND' | TokAcc]); otp_7198_scan(<>, TokAcc) when (D =:= $D) or (D =:= $d) -> - otp_7198_scan(<<>>, ['AND' | TokAcc]); + otp_7198_scan(<<>>, ['AND' | TokAcc]); otp_7198_scan(<>, TokAcc) when (N =:= $N orelse N =:= $n) and ((Z =:= $\s) or (Z =:= $() or (Z =:= $))) -> - otp_7198_scan(<>, ['NOT' | TokAcc]); + otp_7198_scan(<>, ['NOT' | TokAcc]); otp_7198_scan(<>, TokAcc) when (C >= $A) and (C =< $Z); (C >= $a) and (C =< $z); (C >= $0) and (C =< $9) -> - case Rest of - <<$:, R/binary>> -> - otp_7198_scan(R, [{'FIELD', C} | TokAcc]); - _ -> - otp_7198_scan(Rest, [{'KEYWORD', C} | TokAcc]) - end. + case Rest of + <<$:, R/binary>> -> + otp_7198_scan(R, [{'FIELD', C} | TokAcc]); + _ -> + otp_7198_scan(Rest, [{'KEYWORD', C} | TokAcc]) + end. unordered_bindings(Config) when is_list(Config) -> {<<1,2,3,4>>,<<42,42>>,<<3,3,3>>} = diff --git a/erts/emulator/test/bs_match_tail_SUITE.erl b/erts/emulator/test/bs_match_tail_SUITE.erl index 20129e3556..8392a0df47 100644 --- a/erts/emulator/test/bs_match_tail_SUITE.erl +++ b/erts/emulator/test/bs_match_tail_SUITE.erl @@ -21,8 +21,8 @@ -module(bs_match_tail_SUITE). -author('bjorn@erix.ericsson.se'). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2,aligned/1,unaligned/1,zero_tail/1]). +-export([all/0, suite/0, + aligned/1,unaligned/1,zero_tail/1]). -include_lib("common_test/include/ct.hrl"). @@ -31,40 +31,25 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [aligned, unaligned, zero_tail]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - %% Test aligned tails. aligned(Config) when is_list(Config) -> - ?line Tail1 = mkbin([]), - ?line {258,Tail1} = al_get_tail_used(mkbin([1,2])), - ?line Tail2 = mkbin(lists:seq(1, 127)), - ?line {35091,Tail2} = al_get_tail_used(mkbin([137,19|Tail2])), - - ?line 64896 = al_get_tail_unused(mkbin([253,128])), - ?line 64895 = al_get_tail_unused(mkbin([253,127|lists:seq(42, 255)])), - - ?line Tail3 = mkbin(lists:seq(0, 19)), - ?line {0,Tail1} = get_dyn_tail_used(Tail1, 0), - ?line {0,Tail3} = get_dyn_tail_used(mkbin([Tail3]), 0), - ?line {73,Tail3} = get_dyn_tail_used(mkbin([73|Tail3]), 8), - - ?line 0 = get_dyn_tail_unused(mkbin([]), 0), - ?line 233 = get_dyn_tail_unused(mkbin([233]), 8), - ?line 23 = get_dyn_tail_unused(mkbin([23,22,2]), 8), + Tail1 = mkbin([]), + {258,Tail1} = al_get_tail_used(mkbin([1,2])), + Tail2 = mkbin(lists:seq(1, 127)), + {35091,Tail2} = al_get_tail_used(mkbin([137,19|Tail2])), + + 64896 = al_get_tail_unused(mkbin([253,128])), + 64895 = al_get_tail_unused(mkbin([253,127|lists:seq(42, 255)])), + + Tail3 = mkbin(lists:seq(0, 19)), + {0,Tail1} = get_dyn_tail_used(Tail1, 0), + {0,Tail3} = get_dyn_tail_used(mkbin([Tail3]), 0), + {73,Tail3} = get_dyn_tail_used(mkbin([73|Tail3]), 8), + + 0 = get_dyn_tail_unused(mkbin([]), 0), + 233 = get_dyn_tail_unused(mkbin([233]), 8), + 23 = get_dyn_tail_unused(mkbin([23,22,2]), 8), ok. al_get_tail_used(<>) -> {A,T}. @@ -72,10 +57,10 @@ al_get_tail_unused(<>) -> A. %% Test that an non-aligned tail cannot be matched out. unaligned(Config) when is_list(Config) -> - ?line {'EXIT',{function_clause,_}} = (catch get_tail_used(mkbin([42]))), - ?line {'EXIT',{{badmatch,_},_}} = (catch get_dyn_tail_used(mkbin([137]), 3)), - ?line {'EXIT',{function_clause,_}} = (catch get_tail_unused(mkbin([42,33]))), - ?line {'EXIT',{{badmatch,_},_}} = (catch get_dyn_tail_unused(mkbin([44]), 7)), + {'EXIT',{function_clause,_}} = (catch get_tail_used(mkbin([42]))), + {'EXIT',{{badmatch,_},_}} = (catch get_dyn_tail_used(mkbin([137]), 3)), + {'EXIT',{function_clause,_}} = (catch get_tail_unused(mkbin([42,33]))), + {'EXIT',{{badmatch,_},_}} = (catch get_dyn_tail_unused(mkbin([44]), 7)), ok. get_tail_used(<>) -> {A,T}. @@ -92,9 +77,9 @@ get_dyn_tail_unused(Bin, Sz) -> %% Test that zero tails are tested correctly. zero_tail(Config) when is_list(Config) -> - ?line 7 = (catch test_zero_tail(mkbin([7]))), - ?line {'EXIT',{function_clause,_}} = (catch test_zero_tail(mkbin([1,2]))), - ?line {'EXIT',{function_clause,_}} = (catch test_zero_tail2(mkbin([1,2,3]))), + 7 = (catch test_zero_tail(mkbin([7]))), + {'EXIT',{function_clause,_}} = (catch test_zero_tail(mkbin([1,2]))), + {'EXIT',{function_clause,_}} = (catch test_zero_tail2(mkbin([1,2,3]))), ok. test_zero_tail(<>) -> A. @@ -102,7 +87,3 @@ test_zero_tail(<>) -> A. test_zero_tail2(<<_A:4,_B:4>>) -> ok. mkbin(L) when is_list(L) -> list_to_binary(L). - - - - diff --git a/erts/emulator/test/bs_utf_SUITE.erl b/erts/emulator/test/bs_utf_SUITE.erl index 5447944306..f1c09bfbb0 100644 --- a/erts/emulator/test/bs_utf_SUITE.erl +++ b/erts/emulator/test/bs_utf_SUITE.erl @@ -28,7 +28,7 @@ -include_lib("common_test/include/ct.hrl"). --define(FAIL(Expr), ?line fail_check(catch Expr, ??Expr, [])). +-define(FAIL(Expr), fail_check(catch Expr, ??Expr, [])). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -40,8 +40,8 @@ all() -> utf32_illegal_sequences, bad_construction]. utf8_roundtrip(Config) when is_list(Config) -> - ?line utf8_roundtrip(0, 16#D7FF), - ?line utf8_roundtrip(16#E000, 16#10FFFF), + utf8_roundtrip(0, 16#D7FF), + utf8_roundtrip(16#E000, 16#10FFFF), ok. utf8_roundtrip(First, Last) when First =< Last -> @@ -59,10 +59,9 @@ utf16_roundtrip(Config) when is_list(Config) -> Big = fun utf16_big_roundtrip/1, Little = fun utf16_little_roundtrip/1, PidRefs = [spawn_monitor(fun() -> - do_utf16_roundtrip(Fun) - end) || Fun <- [Big,Little]], - [receive {'DOWN',Ref,process,Pid,Reason} -> normal=Reason end || - {Pid,Ref} <- PidRefs], + do_utf16_roundtrip(Fun) + end) || Fun <- [Big,Little]], + [receive {'DOWN',Ref,process,Pid,Reason} -> normal=Reason end || {Pid,Ref} <- PidRefs], ok. do_utf16_roundtrip(Fun) -> @@ -130,20 +129,20 @@ utf32_little_roundtrip(Char) -> ok. utf8_illegal_sequences(Config) when is_list(Config) -> - ?line fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large. - ?line fail_range(16#D800, 16#DFFF), %Reserved for UTF-16. + fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large. + fail_range(16#D800, 16#DFFF), %Reserved for UTF-16. %% Illegal first character. - ?line [fail(<>) || I <- lists:seq(16#80, 16#BF)], + [fail(<>) || I <- lists:seq(16#80, 16#BF)], %% Short sequences. - ?line short_sequences(16#80, 16#10FFFF), + short_sequences(16#80, 16#10FFFF), %% Overlong sequences. (Using more bytes than necessary %% is not allowed.) - ?line overlong(0, 127, 2), - ?line overlong(128, 16#7FF, 3), - ?line overlong(16#800, 16#FFFF, 4), + overlong(0, 127, 2), + overlong(128, 16#7FF, 3), + overlong(16#800, 16#FFFF, 4), ok. fail_range(Char, End) when Char =< End -> @@ -163,9 +162,9 @@ short_sequences(Char, End) -> short_sequences_1(Char, Step, End) when Char =< End -> CharEnd = lists:min([Char+Step-1,End]), [spawn_monitor(fun() -> - io:format("~p - ~p\n", [Char,CharEnd]), - do_short_sequences(Char, CharEnd) - end)|short_sequences_1(Char+Step, Step, End)]; + io:format("~p - ~p\n", [Char,CharEnd]), + do_short_sequences(Char, CharEnd) + end)|short_sequences_1(Char+Step, Step, End)]; short_sequences_1(_, _, _) -> []. do_short_sequences(Char, End) when Char =< End -> @@ -222,11 +221,11 @@ fail_1(_) -> ok. utf16_illegal_sequences(Config) when is_list(Config) -> - ?line utf16_fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large. - ?line utf16_fail_range(16#D800, 16#DFFF), %Reserved for UTF-16. + utf16_fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large. + utf16_fail_range(16#D800, 16#DFFF), %Reserved for UTF-16. - ?line lonely_hi_surrogate(16#D800, 16#DFFF), - ?line leading_lo_surrogate(16#DC00, 16#DFFF), + lonely_hi_surrogate(16#D800, 16#DFFF), + leading_lo_surrogate(16#DC00, 16#DFFF), ok. @@ -270,20 +269,20 @@ leading_lo_surrogate(HiSurr, LoSurr, End) when LoSurr =< End -> leading_lo_surrogate(_, _, _) -> ok. utf32_illegal_sequences(Config) when is_list(Config) -> - ?line utf32_fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large. - ?line utf32_fail_range(16#D800, 16#DFFF), %Reserved for UTF-16. - ?line utf32_fail_range(-100, -1), + utf32_fail_range(16#10FFFF+1, 16#10FFFF+512), %Too large. + utf32_fail_range(16#D800, 16#DFFF), %Reserved for UTF-16. + utf32_fail_range(-100, -1), ok. utf32_fail_range(Char, End) when Char =< End -> {'EXIT',_} = (catch <>), {'EXIT',_} = (catch <>), case {<>,<>} of - {<>,_} -> - ?line ct:fail(Unexpected); - {_,<>} -> - ?line ct:fail(Unexpected); - {_,_} -> ok + {<>,_} -> + ct:fail(Unexpected); + {_,<>} -> + ct:fail(Unexpected); + {_,_} -> ok end, utf32_fail_range(Char+1, End); utf32_fail_range(_, _) -> ok. @@ -382,4 +381,3 @@ evaluate(Str, Vars) -> end. id(I) -> I. - diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index ce9fdfa10d..c582f8d77b 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -65,11 +65,11 @@ end_per_testcase(_Case, Config) when is_list(Config) -> io_to_busy(Config) when is_list(Config) -> ct:timetrap({seconds, 30}), - ?line start_busy_driver(Config), - ?line process_flag(trap_exit, true), - ?line Writer = fun_spawn(fun writer/0), - ?line Generator = fun_spawn(fun() -> generator(100, Writer) end), - ?line wait_for([Writer, Generator]), + start_busy_driver(Config), + process_flag(trap_exit, true), + Writer = fun_spawn(fun writer/0), + Generator = fun_spawn(fun() -> generator(100, Writer) end), + wait_for([Writer, Generator]), ok. generator(N, Writer) -> @@ -116,21 +116,21 @@ forget(_) -> message_order(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), - ?line start_busy_driver(Config), - ?line Self = self(), - ?line Busy = fun_spawn(fun () -> send_to_busy_1(Self) end), - ?line receive after 1000 -> ok end, - ?line Busy ! first, - ?line Busy ! second, - ?line receive after 1 -> ok end, - ?line unlock_slave(), - ?line Busy ! third, - ?line receive - {Busy, first} -> - ok; - Other -> - ct:fail({unexpected_message, Other}) - end, + start_busy_driver(Config), + Self = self(), + Busy = fun_spawn(fun () -> send_to_busy_1(Self) end), + receive after 1000 -> ok end, + Busy ! first, + Busy ! second, + receive after 1 -> ok end, + unlock_slave(), + Busy ! third, + receive + {Busy, first} -> + ok; + Other -> + ct:fail({unexpected_message, Other}) + end, ok. send_to_busy_1(Parent) -> @@ -147,59 +147,55 @@ send_to_busy_1(Parent) -> send_3(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), %% - ?line start_busy_driver(Config), - ?line {Owner,Slave} = get_slave(), - ?line ok = erlang:send(Slave, {Owner,{command,"set busy"}}, - [nosuspend]), + start_busy_driver(Config), + {Owner,Slave} = get_slave(), + ok = erlang:send(Slave, {Owner,{command,"set busy"}}, [nosuspend]), receive after 100 -> ok end, % ensure command reached port - ?line nosuspend = erlang:send(Slave, {Owner,{command,"busy"}}, - [nosuspend]), - ?line unlock_slave(), - ?line ok = erlang:send(Slave, {Owner,{command,"not busy"}}, - [nosuspend]), - ?line ok = command(stop), + nosuspend = erlang:send(Slave, {Owner,{command,"busy"}}, [nosuspend]), + unlock_slave(), + ok = erlang:send(Slave, {Owner,{command,"not busy"}}, [nosuspend]), + ok = command(stop), ok. %% Test the erlang:system_monitor(Pid, [busy_port]) system_monitor(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), - ?line Self = self(), + Self = self(), %% - ?line OldMonitor = erlang:system_monitor(Self, [busy_port]), - ?line {Self,[busy_port]} = erlang:system_monitor(), - ?line Void = make_ref(), - ?line start_busy_driver(Config), - ?line {Owner,Slave} = get_slave(), - ?line Master = command(get_master), - ?line Parent = self(), - ?line Busy = - spawn_link( - fun() -> - (catch port_command(Slave, "set busy")), - receive {Parent,alpha} -> ok end, - (catch port_command(Slave, "busy")), - (catch port_command(Slave, "free")), - Parent ! {self(),alpha}, - command(lock), - receive {Parent,beta} -> ok end, - command({port_command,"busy"}), - command({port_command,"free"}), - Parent ! {self(),beta} - end), - ?line Void = rec(Void), - ?line Busy ! {self(),alpha}, - ?line {monitor,Busy,busy_port,Slave} = rec(Void), - ?line unlock_slave(), - ?line {Busy,alpha} = rec(Void), - ?line Void = rec(Void), - ?line Busy ! {self(), beta}, - ?line {monitor,Owner,busy_port,Slave} = rec(Void), - ?line port_command(Master, "u"), - ?line {Busy,beta} = rec(Void), - ?line Void = rec(Void), - ?line _NewMonitor = erlang:system_monitor(OldMonitor), - ?line OldMonitor = erlang:system_monitor(), - ?line OldMonitor = erlang:system_monitor(OldMonitor), + OldMonitor = erlang:system_monitor(Self, [busy_port]), + {Self,[busy_port]} = erlang:system_monitor(), + Void = make_ref(), + start_busy_driver(Config), + {Owner,Slave} = get_slave(), + Master = command(get_master), + Parent = self(), + Busy = spawn_link( + fun() -> + (catch port_command(Slave, "set busy")), + receive {Parent,alpha} -> ok end, + (catch port_command(Slave, "busy")), + (catch port_command(Slave, "free")), + Parent ! {self(),alpha}, + command(lock), + receive {Parent,beta} -> ok end, + command({port_command,"busy"}), + command({port_command,"free"}), + Parent ! {self(),beta} + end), + Void = rec(Void), + Busy ! {self(),alpha}, + {monitor,Busy,busy_port,Slave} = rec(Void), + unlock_slave(), + {Busy,alpha} = rec(Void), + Void = rec(Void), + Busy ! {self(), beta}, + {monitor,Owner,busy_port,Slave} = rec(Void), + port_command(Master, "u"), + {Busy,beta} = rec(Void), + Void = rec(Void), + _NewMonitor = erlang:system_monitor(OldMonitor), + OldMonitor = erlang:system_monitor(), + OldMonitor = erlang:system_monitor(OldMonitor), ok. rec(Tag) -> @@ -218,22 +214,21 @@ rec(Tag) -> no_trap_exit(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), - ?line process_flag(trap_exit, true), - ?line Pid = fun_spawn(fun no_trap_exit_process/3, - [self(), linked, Config]), - ?line receive - {Pid, port_created, Port} -> - io:format("Process ~w created port ~w", [Pid, Port]), - ?line exit(Port, die); - Other1 -> - ct:fail({unexpected_message, Other1}) - end, - ?line receive - {'EXIT', Pid, die} -> - ok; - Other2 -> - ct:fail({unexpected_message, Other2}) - end, + process_flag(trap_exit, true), + Pid = fun_spawn(fun no_trap_exit_process/3, [self(), linked, Config]), + receive + {Pid, port_created, Port} -> + io:format("Process ~w created port ~w", [Pid, Port]), + exit(Port, die); + Other1 -> + ct:fail({unexpected_message, Other1}) + end, + receive + {'EXIT', Pid, die} -> + ok; + Other2 -> + ct:fail({unexpected_message, Other2}) + end, ok. %% The same scenario as above, but the port has been explicitly @@ -241,35 +236,35 @@ no_trap_exit(Config) when is_list(Config) -> no_trap_exit_unlinked(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), - ?line process_flag(trap_exit, true), - ?line Pid = fun_spawn(fun no_trap_exit_process/3, - [self(), unlink, Config]), - ?line receive - {Pid, port_created, Port} -> - io:format("Process ~w created port ~w", [Pid, Port]), - ?line exit(Port, die); - Other1 -> - ct:fail({unexpected_message, Other1}) - end, - ?line receive - {'EXIT', Pid, normal} -> - ok; - Other2 -> - ct:fail({unexpected_message, Other2}) - end, + process_flag(trap_exit, true), + Pid = fun_spawn(fun no_trap_exit_process/3, + [self(), unlink, Config]), + receive + {Pid, port_created, Port} -> + io:format("Process ~w created port ~w", [Pid, Port]), + exit(Port, die); + Other1 -> + ct:fail({unexpected_message, Other1}) + end, + receive + {'EXIT', Pid, normal} -> + ok; + Other2 -> + ct:fail({unexpected_message, Other2}) + end, ok. no_trap_exit_process(ResultTo, Link, Config) -> - ?line load_busy_driver(Config), - ?line _Master = open_port({spawn, "busy_drv master"}, [eof]), - ?line Slave = open_port({spawn, "busy_drv slave"}, [eof]), - ?line case Link of - linked -> ok; - unlink -> unlink(Slave) - end, - ?line (catch port_command(Slave, "lock port")), - ?line ResultTo ! {self(), port_created, Slave}, - ?line (catch port_command(Slave, "suspend me")), + load_busy_driver(Config), + _Master = open_port({spawn, "busy_drv master"}, [eof]), + Slave = open_port({spawn, "busy_drv slave"}, [eof]), + case Link of + linked -> ok; + unlink -> unlink(Slave) + end, + (catch port_command(Slave, "lock port")), + ResultTo ! {self(), port_created, Slave}, + (catch port_command(Slave, "suspend me")), ok. %% Assuming the following scenario, @@ -285,17 +280,17 @@ no_trap_exit_process(ResultTo, Link, Config) -> trap_exit(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), - ?line Pid = fun_spawn(fun busy_port_exit_process/2, [self(), Config]), - ?line receive + Pid = fun_spawn(fun busy_port_exit_process/2, [self(), Config]), + receive {Pid, port_created, Port} -> io:format("Process ~w created port ~w", [Pid, Port]), - ?line unlink(Pid), - ?line {status, suspended} = process_info(Pid, status), - ?line exit(Port, die); + unlink(Pid), + {status, suspended} = process_info(Pid, status), + exit(Port, die); Other1 -> ct:fail({unexpected_message, Other1}) end, - ?line receive + receive {Pid, ok} -> ok; Other2 -> @@ -304,13 +299,13 @@ trap_exit(Config) when is_list(Config) -> ok. busy_port_exit_process(ResultTo, Config) -> - ?line process_flag(trap_exit, true), - ?line load_busy_driver(Config), - ?line _Master = open_port({spawn, "busy_drv master"}, [eof]), - ?line Slave = open_port({spawn, "busy_drv slave"}, [eof]), - ?line (catch port_command(Slave, "lock port")), - ?line ResultTo ! {self(), port_created, Slave}, - ?line (catch port_command(Slave, "suspend me")), + process_flag(trap_exit, true), + load_busy_driver(Config), + _Master = open_port({spawn, "busy_drv master"}, [eof]), + Slave = open_port({spawn, "busy_drv slave"}, [eof]), + (catch port_command(Slave, "lock port")), + ResultTo ! {self(), port_created, Slave}, + (catch port_command(Slave, "suspend me")), receive {'EXIT', Slave, die} -> ResultTo ! {self(), ok}; @@ -325,16 +320,16 @@ busy_port_exit_process(ResultTo, Config) -> multiple_writers(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), - ?line start_busy_driver(Config), - ?line process_flag(trap_exit, true), + start_busy_driver(Config), + process_flag(trap_exit, true), %% Start the waiters and make sure they have blocked. - ?line W1 = fun_spawn(fun quick_writer/0), - ?line W2 = fun_spawn(fun quick_writer/0), - ?line W3 = fun_spawn(fun quick_writer/0), - ?line W4 = fun_spawn(fun quick_writer/0), - ?line W5 = fun_spawn(fun quick_writer/0), - ?line test_server:sleep(500), % Make sure writers have blocked. + W1 = fun_spawn(fun quick_writer/0), + W2 = fun_spawn(fun quick_writer/0), + W3 = fun_spawn(fun quick_writer/0), + W4 = fun_spawn(fun quick_writer/0), + W5 = fun_spawn(fun quick_writer/0), + test_server:sleep(500), % Make sure writers have blocked. %% Kill two of the processes. exit(W1, kill), @@ -343,8 +338,8 @@ multiple_writers(Config) when is_list(Config) -> receive {'EXIT', W3, killed} -> ok end, %% Unlock the port. The surviving processes should be become runnable. - ?line unlock_slave(), - ?line wait_for([W2, W4, W5]), + unlock_slave(), + wait_for([W2, W4, W5]), ok. quick_writer() -> @@ -360,199 +355,188 @@ soft_busy_driver(Config) when is_list(Config) -> hs_test(Config, false). hs_test(Config, HardBusy) when is_list(Config) -> - ?line DrvName = case HardBusy of - true -> 'hard_busy_drv'; - false -> 'soft_busy_drv' - end, - ?line erl_ddll:start(), - ?line Path = proplists:get_value(data_dir, Config), + DrvName = case HardBusy of + true -> 'hard_busy_drv'; + false -> 'soft_busy_drv' + end, + erl_ddll:start(), + Path = proplists:get_value(data_dir, Config), case erl_ddll:load_driver(Path, DrvName) of - ok -> ok; - {error, Error} -> + ok -> ok; + {error, Error} -> ct:fail(erl_ddll:format_error(Error)) end, - ?line Port = open_port({spawn, DrvName}, []), - + Port = open_port({spawn, DrvName}, []), + NotSuspended = fun (Proc) -> - chk_not_value({status,suspended}, - process_info(Proc, status)) - end, + chk_not_value({status,suspended}, + process_info(Proc, status)) + end, NotBusyEnd = fun (Proc, Res, Time) -> - receive - {Port, caller, Proc} -> ok - after - 500 -> exit(missing_caller_message) - end, - chk_value({return, true}, Res), - chk_range(0, Time, 100) - end, + receive + {Port, caller, Proc} -> ok + after + 500 -> exit(missing_caller_message) + end, + chk_value({return, true}, Res), + chk_range(0, Time, 100) + end, ForceEnd = fun (Proc, Res, Time) -> - case HardBusy of - false -> - NotBusyEnd(Proc, Res, Time); - true -> - chk_value({error, notsup}, Res), - chk_range(0, Time, 100), - receive - Msg -> exit({unexpected_msg, Msg}) - after - 500 -> ok - end - end - end, + case HardBusy of + false -> + NotBusyEnd(Proc, Res, Time); + true -> + chk_value({error, notsup}, Res), + chk_range(0, Time, 100), + receive + Msg -> exit({unexpected_msg, Msg}) + after + 500 -> ok + end + end + end, BadArg = fun (_Proc, Res, Time) -> - chk_value({error, badarg}, Res), - chk_range(0, Time, 100) - end, + chk_value({error, badarg}, Res), + chk_range(0, Time, 100) + end, %% Not busy %% Not busy; nosuspend option - ?line hs_busy_pcmd(Port, [nosuspend], NotSuspended, NotBusyEnd), + hs_busy_pcmd(Port, [nosuspend], NotSuspended, NotBusyEnd), %% Not busy; force option - ?line hs_busy_pcmd(Port, [force], NotSuspended, ForceEnd), + hs_busy_pcmd(Port, [force], NotSuspended, ForceEnd), %% Not busy; force and nosuspend option - ?line hs_busy_pcmd(Port, [force, nosuspend], NotSuspended, ForceEnd), + hs_busy_pcmd(Port, [force, nosuspend], NotSuspended, ForceEnd), %% Not busy; no option - ?line hs_busy_pcmd(Port, [], NotSuspended, NotBusyEnd), + hs_busy_pcmd(Port, [], NotSuspended, NotBusyEnd), %% Not busy; bad option - ?line hs_busy_pcmd(Port, [bad_option], NotSuspended, BadArg), + hs_busy_pcmd(Port, [bad_option], NotSuspended, BadArg), %% Make busy - ?line erlang:port_control(Port, $B, []), + erlang:port_control(Port, $B, []), %% Busy; nosuspend option - ?line hs_busy_pcmd(Port, [nosuspend], NotSuspended, - fun (_Proc, Res, Time) -> - chk_value({return, false}, Res), - chk_range(0, Time, 100), - receive - Msg -> exit({unexpected_msg, Msg}) - after - 500 -> ok - end - end), + hs_busy_pcmd(Port, [nosuspend], NotSuspended, + fun (_Proc, Res, Time) -> + chk_value({return, false}, Res), + chk_range(0, Time, 100), + receive + Msg -> exit({unexpected_msg, Msg}) + after + 500 -> ok + end + end), %% Busy; force option - ?line hs_busy_pcmd(Port, [force], NotSuspended, ForceEnd), + hs_busy_pcmd(Port, [force], NotSuspended, ForceEnd), %% Busy; force and nosuspend option - ?line hs_busy_pcmd(Port, [force, nosuspend], NotSuspended, ForceEnd), + hs_busy_pcmd(Port, [force, nosuspend], NotSuspended, ForceEnd), %% Busy; bad option - ?line hs_busy_pcmd(Port, [bad_option], NotSuspended, BadArg), + hs_busy_pcmd(Port, [bad_option], NotSuspended, BadArg), %% no option on busy port - ?line hs_busy_pcmd(Port, [], - fun (Proc) -> - receive after 1000 -> ok end, - chk_value({status,suspended}, - process_info(Proc, status)), - - %% Make not busy - erlang:port_control(Port, $N, []) - end, - fun (_Proc, Res, Time) -> - chk_value({return, true}, Res), - chk_range(1000, Time, 2000) - end), - - ?line true = erlang:port_close(Port), - ?line ok = erl_ddll:unload_driver(DrvName), - ?line ok = erl_ddll:stop(), - ?line ok. + hs_busy_pcmd(Port, [], + fun (Proc) -> + receive after 1000 -> ok end, + chk_value({status,suspended}, + process_info(Proc, status)), + + %% Make not busy + erlang:port_control(Port, $N, []) + end, + fun (_Proc, Res, Time) -> + chk_value({return, true}, Res), + chk_range(1000, Time, 2000) + end), + + true = erlang:port_close(Port), + ok = erl_ddll:unload_driver(DrvName), + ok = erl_ddll:stop(), + ok. hs_busy_pcmd(Prt, Opts, StartFun, EndFun) -> Tester = self(), P = spawn_link(fun () -> - erlang:yield(), - Tester ! {self(), doing_port_command}, - Start = erlang:monotonic_time(micro_seconds), - Res = try {return, - port_command(Prt, [], Opts)} - catch Exception:Error -> {Exception, Error} - end, - End = erlang:monotonic_time(micro_seconds), - Time = round((End - Start)/1000), - Tester ! {self(), port_command_result, Res, Time} - end), + erlang:yield(), + Tester ! {self(), doing_port_command}, + Start = erlang:monotonic_time(micro_seconds), + Res = try {return, + port_command(Prt, [], Opts)} + catch Exception:Error -> {Exception, Error} + end, + End = erlang:monotonic_time(micro_seconds), + Time = round((End - Start)/1000), + Tester ! {self(), port_command_result, Res, Time} + end), receive - {P, doing_port_command} -> - ok + {P, doing_port_command} -> + ok end, StartFun(P), receive - {P, port_command_result, Res, Time} -> - EndFun(P, Res, Time) + {P, port_command_result, Res, Time} -> + EndFun(P, Res, Time) end. scheduling_delay_busy(Config) -> - - Scenario = - [{1,{spawn,[{var,drvname},undefined]}}, - {2,{call,[{var,1},open_port]}}, - {3,{spawn,[{var,2},{var,1}]}}, - {0,{ack,[{var,1},{busy,1,250}]}}, - {0,{cast,[{var,3},{command,2}]}}, - [{0,{cast,[{var,3},{command,I}]}} - || I <- lists:seq(3,50)], - {0,{cast,[{var,3},take_control]}}, - {0,{cast,[{var,1},{new_owner,{var,3}}]}}, - {0,{cast,[{var,3},close]}}, - {0,{timer,sleep,[300]}}, - {0,{erlang,port_command,[{var,2},<<$N>>,[force]]}}, - [{0,{cast,[{var,1},{command,I}]}} - || I <- lists:seq(101,127)] - ,{10,{call,[{var,3},get_data]}} - ], + Scenario = [{1,{spawn,[{var,drvname},undefined]}}, + {2,{call,[{var,1},open_port]}}, + {3,{spawn,[{var,2},{var,1}]}}, + {0,{ack,[{var,1},{busy,1,250}]}}, + {0,{cast,[{var,3},{command,2}]}}, + [{0,{cast,[{var,3},{command,I}]}} || I <- lists:seq(3,50)], + {0,{cast,[{var,3},take_control]}}, + {0,{cast,[{var,1},{new_owner,{var,3}}]}}, + {0,{cast,[{var,3},close]}}, + {0,{timer,sleep,[300]}}, + {0,{erlang,port_command,[{var,2},<<$N>>,[force]]}}, + [{0,{cast,[{var,1},{command,I}]}} || I <- lists:seq(101,127)], + {10,{call,[{var,3},get_data]}}], Validation = [{seq,10,lists:seq(1,50)}], port_scheduling(Scenario,Validation,proplists:get_value(data_dir,Config)). scheduling_delay_busy_nosuspend(Config) -> - - Scenario = - [{1,{spawn,[{var,drvname},undefined]}}, - {2,{call,[{var,1},open_port]}}, - {0,{cast,[{var,1},{command,1,100}]}}, - {0,{cast,[{var,1},{busy,2}]}}, - {0,{timer,sleep,[200]}}, % ensure reached port - {10,{call,[{var,1},{command,3,[nosuspend]}]}}, - {0,{timer,sleep,[200]}}, - {0,{erlang,port_command,[{var,2},<<$N>>,[force]]}}, - {0,{cast,[{var,1},close]}}, - {20,{call,[{var,1},get_data]}} - ], + Scenario = [{1,{spawn,[{var,drvname},undefined]}}, + {2,{call,[{var,1},open_port]}}, + {0,{cast,[{var,1},{command,1,100}]}}, + {0,{cast,[{var,1},{busy,2}]}}, + {0,{timer,sleep,[200]}}, % ensure reached port + {10,{call,[{var,1},{command,3,[nosuspend]}]}}, + {0,{timer,sleep,[200]}}, + {0,{erlang,port_command,[{var,2},<<$N>>,[force]]}}, + {0,{cast,[{var,1},close]}}, + {20,{call,[{var,1},get_data]}}], Validation = [{eq,10,nosuspend},{seq,20,[1,2]}], port_scheduling(Scenario,Validation,proplists:get_value(data_dir,Config)). scheduling_busy_link(Config) -> - - Scenario = - [{1,{spawn,[{var,drvname},undefined]}}, - {2,{call,[{var,1},open_port]}}, - {3,{spawn,[{var,2},{var,1}]}}, - {0,{cast,[{var,1},unlink]}}, - {0,{cast,[{var,1},{busy,1}]}}, - {0,{cast,[{var,1},{command,2}]}}, - {0,{cast,[{var,1},link]}}, - {0,{timer,sleep,[1000]}}, - {0,{ack,[{var,3},take_control]}}, - {0,{cast,[{var,1},{new_owner,{var,3}}]}}, - {0,{cast,[{var,3},close]}}, - {10,{call,[{var,3},get_data]}}, - {20,{call,[{var,1},get_exit]}} - ], + Scenario = [{1,{spawn,[{var,drvname},undefined]}}, + {2,{call,[{var,1},open_port]}}, + {3,{spawn,[{var,2},{var,1}]}}, + {0,{cast,[{var,1},unlink]}}, + {0,{cast,[{var,1},{busy,1}]}}, + {0,{cast,[{var,1},{command,2}]}}, + {0,{cast,[{var,1},link]}}, + {0,{timer,sleep,[1000]}}, + {0,{ack,[{var,3},take_control]}}, + {0,{cast,[{var,1},{new_owner,{var,3}}]}}, + {0,{cast,[{var,3},close]}}, + {10,{call,[{var,3},get_data]}}, + {20,{call,[{var,1},get_exit]}}], Validation = [{seq,10,[1]}, {seq,20,[{'EXIT',noproc}]}], @@ -656,11 +640,11 @@ handle_msg(close,Port,Owner,_Data) -> handle_msg(get_data,Port,_Owner,{[],_Exit}) -> %% Wait for data if it has not arrived yet receive - {Port,{data,Data}} -> - Data + {Port,{data,Data}} -> + Data after 2000 -> - pal("~p",[erlang:process_info(self())]), - exit(did_not_get_port_data) + pal("~p",[erlang:process_info(self())]), + exit(did_not_get_port_data) end; handle_msg(get_data,_Port,Owner,{Data,Exit}) -> pal("GetData",[]), @@ -852,8 +836,8 @@ fun_spawn(Fun, Args) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% load_busy_driver(Config) when is_list(Config) -> - ?line DataDir = proplists:get_value(data_dir, Config), - ?line erl_ddll:start(), + DataDir = proplists:get_value(data_dir, Config), + erl_ddll:start(), case erl_ddll:load_driver(DataDir, "busy_drv") of ok -> ok; {error, Error} -> @@ -863,8 +847,8 @@ load_busy_driver(Config) when is_list(Config) -> %%% Interface functions. start_busy_driver(Config) when is_list(Config) -> - ?line Pid = spawn_link(?MODULE, init, [Config, self()]), - ?line receive + Pid = spawn_link(?MODULE, init, [Config, self()]), + receive {Pid, started} -> ok; Other -> @@ -875,15 +859,15 @@ unlock_slave() -> command(unlock). get_slave() -> - ?line command(get_slave). + command(get_slave). %% Internal functions. command(Msg) -> - ?line whereis(busy_drv_server) ! {self(), Msg}, - ?line receive - {busy_drv_reply, Reply} -> - Reply + whereis(busy_drv_server) ! {self(), Msg}, + receive + {busy_drv_reply, Reply} -> + Reply end. %%% Server. diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index 0ced026c73..4ec18aa49e 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -22,22 +22,21 @@ -module(call_trace_SUITE). -export([all/0, suite/0, - init_per_testcase/2,end_per_testcase/2, - hipe/1,process_specs/1,basic/1,flags/1,errors/1,pam/1,change_pam/1, - return_trace/1,exception_trace/1,on_load/1,deep_exception/1, - upgrade/1, - exception_nocatch/1,bit_syntax/1]). + init_per_testcase/2,end_per_testcase/2, + hipe/1,process_specs/1,basic/1,flags/1,errors/1,pam/1,change_pam/1, + return_trace/1,exception_trace/1,on_load/1,deep_exception/1, + upgrade/1, + exception_nocatch/1,bit_syntax/1]). %% Helper functions. -export([bar/0,foo/0,foo/1,foo/2,expect/1,worker_foo/1,pam_foo/2,nasty/0, - id/1,deep/3,deep_1/3,deep_2/2,deep_3/2,deep_4/1,deep_5/1, - bs_sum_a/2,bs_sum_b/2]). + id/1,deep/3,deep_1/3,deep_2/2,deep_3/2,deep_4/1,deep_5/1, + bs_sum_a/2,bs_sum_b/2]). %% Debug -export([abbr/1,abbr/2]). - -include_lib("common_test/include/ct.hrl"). -define(P, 20). @@ -49,13 +48,13 @@ suite() -> all() -> Common = [errors, on_load], NotHipe = [process_specs, basic, flags, pam, change_pam, - upgrade, - return_trace, exception_trace, deep_exception, - exception_nocatch, bit_syntax], + upgrade, + return_trace, exception_trace, deep_exception, + exception_nocatch, bit_syntax], Hipe = [hipe], case test_server:is_native(call_trace_SUITE) of - true -> Hipe ++ Common; - false -> NotHipe ++ Common + true -> Hipe ++ Common; + false -> NotHipe ++ Common end. init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> @@ -69,49 +68,49 @@ end_per_testcase(_Func, Config) -> c:l(?MODULE). hipe(Config) when is_list(Config) -> - ?line 0 = erlang:trace_pattern({?MODULE,worker_foo,1}, true), - ?line 0 = erlang:trace_pattern({?MODULE,worker_foo,1}, true, [local]), - ?line AllFuncs = erlang:trace_pattern({'_','_','_'}, true), + 0 = erlang:trace_pattern({?MODULE,worker_foo,1}, true), + 0 = erlang:trace_pattern({?MODULE,worker_foo,1}, true, [local]), + AllFuncs = erlang:trace_pattern({'_','_','_'}, true), %% Make sure that a traced, exported function can still be found. - ?line true = erlang:function_exported(error_handler, undefined_function, 3), - ?line AllFuncs = erlang:trace_pattern({'_','_','_'}, false), + true = erlang:function_exported(error_handler, undefined_function, 3), + AllFuncs = erlang:trace_pattern({'_','_','_'}, false), ok. %% Tests 'all', 'new', and 'existing' for specifying processes. process_specs(Config) when is_list(Config) -> - ?line Tracer = start_tracer(), - ?line {flags,[call]} = trace_info(self(), flags), - ?line {tracer,Tracer} = trace_info(self(), tracer), - ?line trace_func({?MODULE,worker_foo,1}, []), + Tracer = start_tracer(), + {flags,[call]} = trace_info(self(), flags), + {tracer,Tracer} = trace_info(self(), tracer), + trace_func({?MODULE,worker_foo,1}, []), %% Test the 'new' flag. - ?line {Work1A,Work1B} = start_and_trace(new, [1,2,3], A1B={3,2,1}), + {Work1A,Work1B} = start_and_trace(new, [1,2,3], A1B={3,2,1}), {flags,[]} = trace_info(Work1A, flags), {tracer,[]} = trace_info(Work1A, tracer), {tracer,Tracer} = trace_info(Work1B, tracer), {flags,[call]} = trace_info(Work1B, flags), - ?line expect({trace,Work1B,call,{?MODULE,worker_foo,[A1B]}}), - ?line unlink(Work1B), - ?line Mref = erlang:monitor(process, Work1B), - ?line exit(Work1B, kill), + expect({trace,Work1B,call,{?MODULE,worker_foo,[A1B]}}), + unlink(Work1B), + Mref = erlang:monitor(process, Work1B), + exit(Work1B, kill), receive - {'DOWN',Mref,_,_,_} -> ok + {'DOWN',Mref,_,_,_} -> ok end, - ?line undefined = trace_info(Work1B, flags), - ?line {flags,[]} = trace_info(new, flags), - ?line {tracer,[]} = trace_info(new, tracer), + undefined = trace_info(Work1B, flags), + {flags,[]} = trace_info(new, flags), + {tracer,[]} = trace_info(new, tracer), %% Test the 'existing' flag. - ?line {Work2A,_Work2B} = start_and_trace(existing, A2A=[5,6,7], [7,6,5]), - ?line expect({trace,Work2A,call,{?MODULE,worker_foo,[A2A]}}), + {Work2A,_Work2B} = start_and_trace(existing, A2A=[5,6,7], [7,6,5]), + expect({trace,Work2A,call,{?MODULE,worker_foo,[A2A]}}), %% Test the 'all' flag. - ?line {Work3A,Work3B} = start_and_trace(all, A3A=[12,13], A3B=[13,12]), - ?line expect({trace,Work3A,call,{?MODULE,worker_foo,[A3A]}}), - ?line expect({trace,Work3B,call,{?MODULE,worker_foo,[A3B]}}), - + {Work3A,Work3B} = start_and_trace(all, A3A=[12,13], A3B=[13,12]), + expect({trace,Work3A,call,{?MODULE,worker_foo,[A3A]}}), + expect({trace,Work3B,call,{?MODULE,worker_foo,[A3B]}}), + ok. start_and_trace(Flag, A1, A2) -> @@ -121,33 +120,33 @@ start_and_trace(Flag, A1, A2) -> call_worker(W1, A1), call_worker(W2, A2), case Flag of - new -> - {flags,[call]} = trace_info(new, flags), - {tracer,_} = trace_info(new, tracer); - _Other -> - ok + new -> + {flags,[call]} = trace_info(new, flags), + {tracer,_} = trace_info(new, tracer); + _Other -> + ok end, trace_pid(Flag, false, [call]), {W1,W2}. start_worker() -> - ?line spawn(fun worker_loop/0). + spawn(fun worker_loop/0). call_worker(Pid, Arg) -> Pid ! {self(),{call,Arg}}, receive - {result,Res} -> Res + {result,Res} -> Res after 5000 -> - ?line ct:fail(no_answer_from_worker) + ct:fail(no_answer_from_worker) end. worker_loop() -> receive - {From,{call,Arg}} -> - From ! {result,?MODULE:worker_foo(Arg)}, - worker_loop(); - Other -> - exit({unexpected_message,Other}) + {From,{call,Arg}} -> + From ! {result,?MODULE:worker_foo(Arg)}, + worker_loop(); + Other -> + exit({unexpected_message,Other}) end. worker_foo(_Arg) -> @@ -156,98 +155,98 @@ worker_foo(_Arg) -> %% Basic test of the call tracing (we trace one process). basic(_Config) -> case test_server:is_native(lists) of - true -> {skip,"lists is native"}; - false -> basic() + true -> {skip,"lists is native"}; + false -> basic() end. basic() -> - ?line start_tracer(), - ?line trace_info(self(), flags), - ?line trace_info(self(), tracer), - ?line 0 = trace_func({?MODULE,no_such_function,0}, []), - ?line {traced,undefined} = - trace_info({?MODULE,no_such_function,0}, traced), - ?line {match_spec, undefined} = - trace_info({?MODULE,no_such_function,0}, match_spec), + start_tracer(), + trace_info(self(), flags), + trace_info(self(), tracer), + 0 = trace_func({?MODULE,no_such_function,0}, []), + {traced,undefined} = + trace_info({?MODULE,no_such_function,0}, traced), + {match_spec, undefined} = + trace_info({?MODULE,no_such_function,0}, match_spec), %% Trace some functions... - ?line trace_func({lists,'_','_'}, []), + trace_func({lists,'_','_'}, []), %% Make sure that tracing the same functions more than once %% does not cause any problems. - ?line 3 = trace_func({?MODULE,foo,'_'}, true), - ?line 3 = trace_func({?MODULE,foo,'_'}, true), - ?line 1 = trace_func({?MODULE,bar,0}, true), - ?line 1 = trace_func({?MODULE,bar,0}, true), - ?line {traced,global} = trace_info({?MODULE,bar,0}, traced), - ?line 1 = trace_func({erlang,list_to_integer,1}, true), - ?line {traced,global} = trace_info({erlang,list_to_integer,1}, traced), + 3 = trace_func({?MODULE,foo,'_'}, true), + 3 = trace_func({?MODULE,foo,'_'}, true), + 1 = trace_func({?MODULE,bar,0}, true), + 1 = trace_func({?MODULE,bar,0}, true), + {traced,global} = trace_info({?MODULE,bar,0}, traced), + 1 = trace_func({erlang,list_to_integer,1}, true), + {traced,global} = trace_info({erlang,list_to_integer,1}, traced), %% ... and call them... - ?line AList = [x,y,z], - ?line true = lists:member(y, AList), - ?line foo0 = ?MODULE:foo(), - ?line 4 = ?MODULE:foo(3), - ?line 11 = ?MODULE:foo(7, 4), - ?line ok = ?MODULE:bar(), - ?line 42 = list_to_integer(non_literal("42")), + AList = [x,y,z], + true = lists:member(y, AList), + foo0 = ?MODULE:foo(), + 4 = ?MODULE:foo(3), + 11 = ?MODULE:foo(7, 4), + ok = ?MODULE:bar(), + 42 = list_to_integer(non_literal("42")), %% ... make sure the we got trace messages (but not for ?MODULE:expect/1). - ?line Self = self(), - ?line ?MODULE:expect({trace,Self,call,{lists,member,[y,AList]}}), - ?line ?MODULE:expect({trace,Self,call,{?MODULE,foo,[]}}), - ?line ?MODULE:expect({trace,Self,call,{?MODULE,foo,[3]}}), - ?line ?MODULE:expect({trace,Self,call,{?MODULE,foo,[7,4]}}), - ?line ?MODULE:expect({trace,Self,call,{?MODULE,bar,[]}}), - ?line ?MODULE:expect({trace,Self,call,{erlang,list_to_integer,["42"]}}), + Self = self(), + ?MODULE:expect({trace,Self,call,{lists,member,[y,AList]}}), + ?MODULE:expect({trace,Self,call,{?MODULE,foo,[]}}), + ?MODULE:expect({trace,Self,call,{?MODULE,foo,[3]}}), + ?MODULE:expect({trace,Self,call,{?MODULE,foo,[7,4]}}), + ?MODULE:expect({trace,Self,call,{?MODULE,bar,[]}}), + ?MODULE:expect({trace,Self,call,{erlang,list_to_integer,["42"]}}), %% Turn off trace for this module and call functions... - ?line trace_func({?MODULE,'_','_'}, false), - ?line {traced,false} = trace_info({?MODULE,bar,0}, traced), - ?line foo0 = ?MODULE:foo(), - ?line 4 = ?MODULE:foo(3), - ?line 11 = ?MODULE:foo(7, 4), - ?line ok = ?MODULE:bar(), - ?line [1,2,3,4,5,6,7,8,9,10] = lists:seq(1, 10), - ?line 777 = list_to_integer(non_literal("777")), + trace_func({?MODULE,'_','_'}, false), + {traced,false} = trace_info({?MODULE,bar,0}, traced), + foo0 = ?MODULE:foo(), + 4 = ?MODULE:foo(3), + 11 = ?MODULE:foo(7, 4), + ok = ?MODULE:bar(), + [1,2,3,4,5,6,7,8,9,10] = lists:seq(1, 10), + 777 = list_to_integer(non_literal("777")), %% ... turn on all trace messages... - ?line trace_func({'_','_','_'}, false), - ?line [b,a] = lists:reverse([a,b]), + trace_func({'_','_','_'}, false), + [b,a] = lists:reverse([a,b]), %% Read out the remaing trace messages. - ?line ?MODULE:expect({trace,Self,call,{lists,seq,[1,10]}}), - ?line ?MODULE:expect({trace,Self,call,{erlang,list_to_integer,["777"]}}), + ?MODULE:expect({trace,Self,call,{lists,seq,[1,10]}}), + ?MODULE:expect({trace,Self,call,{erlang,list_to_integer,["777"]}}), receive - Any -> - ?line ct:fail({unexpected_message,Any}) + Any -> + ct:fail({unexpected_message,Any}) after 1 -> - ok + ok end, %% Turn on and then off tracing on all external functions. %% This might cause the emulator to crasch later if it doesn't %% restore all export entries properly. - ?line AllFuncs = trace_func({'_','_','_'}, true), + AllFuncs = trace_func({'_','_','_'}, true), io:format("AllFuncs = ~p", [AllFuncs]), %% Make sure that a traced, exported function can still be found. - ?line true = erlang:function_exported(error_handler, undefined_function, 3), - ?line AllFuncs = trace_func({'_','_','_'}, false), - ?line erlang:trace_delivered(all), + true = erlang:function_exported(error_handler, undefined_function, 3), + AllFuncs = trace_func({'_','_','_'}, false), + erlang:trace_delivered(all), receive - {trace_delivered,_,_} -> ok + {trace_delivered,_,_} -> ok end, c:flush(), % Print the traces messages. c:flush(), % Print the traces messages. - ?line {traced,false} = trace_info({erlang,list_to_integer,1}, traced), + {traced,false} = trace_info({erlang,list_to_integer,1}, traced), ok. @@ -283,8 +282,8 @@ upgrade_do(V1, V2, TraceLocalVersion) -> trace_func({my_upgrade_test,'_','_'}, [], [global]), case TraceLocalVersion of - true -> trace_func({my_upgrade_test,local_version,0}, [], [local]); - _ -> ok + true -> trace_func({my_upgrade_test,local_version,0}, [], [local]); + _ -> ok end, 1 = my_upgrade_test:version(), 1 = my_upgrade_test:do_local(), @@ -299,15 +298,15 @@ upgrade_do(V1, V2, TraceLocalVersion) -> expect({trace,Self,call,{my_upgrade_test,do_local,[]}}), expect({trace,Self,call,{my_upgrade_test,do_real_local,[]}}), case TraceLocalVersion of - true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); - _ -> ok + true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); + _ -> ok end, expect({trace,Self,call,{my_upgrade_test,make_fun_exp,[]}}), expect({trace,Self,call,{my_upgrade_test,make_fun_local,[]}}), expect({trace,Self,call,{my_upgrade_test,version,[]}}), % F1_exp case TraceLocalVersion of - true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F1_loc - _ -> ok + true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F1_loc + _ -> ok end, {module,my_upgrade_test} = erlang:load_module(my_upgrade_test, V2), @@ -329,8 +328,8 @@ upgrade_do(V1, V2, TraceLocalVersion) -> trace_func({my_upgrade_test,'_','_'}, [], [global]), case TraceLocalVersion of - true -> trace_func({my_upgrade_test,local_version,0}, [], [local]); - _ -> ok + true -> trace_func({my_upgrade_test,local_version,0}, [], [local]); + _ -> ok end, 2 = my_upgrade_test:version(), 2 = my_upgrade_test:do_local(), @@ -342,13 +341,13 @@ upgrade_do(V1, V2, TraceLocalVersion) -> expect({trace,Self,call,{my_upgrade_test,do_local,[]}}), expect({trace,Self,call,{my_upgrade_test,do_real_local,[]}}), case TraceLocalVersion of - true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); - _ -> ok + true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); + _ -> ok end, expect({trace,Self,call,{my_upgrade_test,version,[]}}), % F2_exp case TraceLocalVersion of - true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F2_loc - _ -> ok + true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F2_loc + _ -> ok end, true = erlang:delete_module(my_upgrade_test), @@ -367,7 +366,7 @@ compile_version(Module, Version, Config) -> Data = proplists:get_value(data_dir, Config), File = filename:join(Data, atom_to_list(Module)), {ok,Module,Bin} = compile:file(File, [{d,'VERSION',Version}, - binary,report]), + binary,report]), Bin. @@ -376,157 +375,157 @@ compile_version(Module, Version, Config) -> %% Also, test the '{tracer,Pid}' option. flags(_Config) -> case test_server:is_native(filename) of - true -> {skip,"filename is native"}; - false -> flags() + true -> {skip,"filename is native"}; + false -> flags() end. flags() -> - ?line Tracer = start_tracer_loop(), - ?line trace_pid(self(), true, [call,{tracer,Tracer}]), + Tracer = start_tracer_loop(), + trace_pid(self(), true, [call,{tracer,Tracer}]), %% Trace some functions... - ?line trace_func({filename,'_','_'}, true), + trace_func({filename,'_','_'}, true), %% ... and call them... - ?line Self = self(), - ?line filename:absname("nisse"), - ?line ?MODULE:expect({trace,Self,call,{filename,absname,["nisse"]}}), - ?line trace_pid(Self, true, [call,arity]), - ?line filename:absname("kalle"), - ?line filename:absname("kalle", "/root"), - ?line ?MODULE:expect({trace,Self,call,{filename,absname,1}}), - ?line ?MODULE:expect({trace,Self,call,{filename,absname,2}}), - ?line trace_info(Self, flags), + Self = self(), + filename:absname("nisse"), + ?MODULE:expect({trace,Self,call,{filename,absname,["nisse"]}}), + trace_pid(Self, true, [call,arity]), + filename:absname("kalle"), + filename:absname("kalle", "/root"), + ?MODULE:expect({trace,Self,call,{filename,absname,1}}), + ?MODULE:expect({trace,Self,call,{filename,absname,2}}), + trace_info(Self, flags), %% Timestamp + arity. flag_test(fun() -> - ?line trace_pid(Self, true, [timestamp]), - ?line "dum" = filename:basename("/abcd/dum"), - ?line Ts = expect({trace_ts,Self,call,{filename,basename,1},ts}), - ?line trace_info(Self, flags), - Ts - end), + trace_pid(Self, true, [timestamp]), + "dum" = filename:basename("/abcd/dum"), + Ts = expect({trace_ts,Self,call,{filename,basename,1},ts}), + trace_info(Self, flags), + Ts + end), %% Timestamp. - ?line AnArg = "/abcd/hejsan", + AnArg = "/abcd/hejsan", flag_test(fun() -> - ?line trace_pid(Self, false, [arity]), - ?line "hejsan" = filename:basename(AnArg), - ?line Ts = expect({trace_ts,Self,call, - {filename,basename,[AnArg]},ts}), - ?line trace_info(Self, flags), - Ts - end), + trace_pid(Self, false, [arity]), + "hejsan" = filename:basename(AnArg), + Ts = expect({trace_ts,Self,call, + {filename,basename,[AnArg]},ts}), + trace_info(Self, flags), + Ts + end), %% All flags turned off. - ?line trace_pid(Self, false, [timestamp]), - ?line AnotherArg = filename:join(AnArg, "hoppsan"), - ?line "hoppsan" = filename:basename(AnotherArg), - ?line expect({trace,Self,call,{filename,join,[AnArg,"hoppsan"]}}), - ?line expect({trace,Self,call,{filename,basename,[AnotherArg]}}), - ?line trace_info(Self, flags), - + trace_pid(Self, false, [timestamp]), + AnotherArg = filename:join(AnArg, "hoppsan"), + "hoppsan" = filename:basename(AnotherArg), + expect({trace,Self,call,{filename,join,[AnArg,"hoppsan"]}}), + expect({trace,Self,call,{filename,basename,[AnotherArg]}}), + trace_info(Self, flags), + ok. flag_test(Test) -> Now = now(), Ts = Test(), case timer:now_diff(Ts, Now) of - Time when Time < 5*1000000 -> - %% Reasonable short time. - ok; - _Diff -> - %% Too large difference. - ct:fail("Now = ~p, Ts = ~p", [Now, Ts]) + Time when Time < 5*1000000 -> + %% Reasonable short time. + ok; + _Diff -> + %% Too large difference. + ct:fail("Now = ~p, Ts = ~p", [Now, Ts]) end, flag_test_cpu_timestamp(Test). flag_test_cpu_timestamp(Test) -> try erlang:trace(all, true, [cpu_timestamp]) of - _ -> - io:format("CPU timestamps"), - Ts = Test(), - erlang:trace(all, false, [cpu_timestamp]), - Origin = {0,0,0}, - Hour = 3600*1000000, - case timer:now_diff(Ts, Origin) of - Diff when Diff < 4*Hour -> - %% In the worst case, CPU timestamps count from when this - %% Erlang emulator was started. The above test is a conservative - %% test that all CPU timestamps should pass. - ok; - _Time -> - ct:fail("Strange CPU timestamp: ~p", [Ts]) - end, - io:format("Turned off CPU timestamps") + _ -> + io:format("CPU timestamps"), + Ts = Test(), + erlang:trace(all, false, [cpu_timestamp]), + Origin = {0,0,0}, + Hour = 3600*1000000, + case timer:now_diff(Ts, Origin) of + Diff when Diff < 4*Hour -> + %% In the worst case, CPU timestamps count from when this + %% Erlang emulator was started. The above test is a conservative + %% test that all CPU timestamps should pass. + ok; + _Time -> + ct:fail("Strange CPU timestamp: ~p", [Ts]) + end, + io:format("Turned off CPU timestamps") catch - error:badarg -> ok + error:badarg -> ok end. %% Test bad arguments for trace/3 and trace_pattern/3. errors(Config) when is_list(Config) -> - ?line expect_badarg_pid(aaa, true, []), - ?line expect_badarg_pid({pid,dum}, false, []), - ?line expect_badarg_func({'_','_',1}, []), - ?line expect_badarg_func({'_',gosh,1}, []), - ?line expect_badarg_func({xxx,'_',2}, []), - ?line expect_badarg_func({xxx,yyy,b}, glurp), + expect_badarg_pid(aaa, true, []), + expect_badarg_pid({pid,dum}, false, []), + expect_badarg_func({'_','_',1}, []), + expect_badarg_func({'_',gosh,1}, []), + expect_badarg_func({xxx,'_',2}, []), + expect_badarg_func({xxx,yyy,b}, glurp), ok. expect_badarg_pid(What, How, Flags) -> case catch erlang:trace(What, How, Flags) of - {'EXIT',{badarg,Where}} -> - io:format("trace(~p, ~p, ~p) ->\n {'EXIT',{badarg,~p}}", - [What,How,Flags,Where]), - ok; - Other -> - io:format("trace(~p, ~p, ~p) -> ~p", - [What,How,Flags,Other]), - ct:fail({unexpected,Other}) + {'EXIT',{badarg,Where}} -> + io:format("trace(~p, ~p, ~p) ->\n {'EXIT',{badarg,~p}}", + [What,How,Flags,Where]), + ok; + Other -> + io:format("trace(~p, ~p, ~p) -> ~p", + [What,How,Flags,Other]), + ct:fail({unexpected,Other}) end. expect_badarg_func(MFA, Pattern) -> case catch erlang:trace_pattern(MFA, Pattern) of - {'EXIT',{badarg,Where}} -> - io:format("trace_pattern(~p, ~p) ->\n {'EXIT',{badarg,~p}}", - [MFA,Pattern,Where]), - ok; - Other -> - io:format("trace_pattern(~p, ~p) -> ~p", - [MFA, Pattern, Other]), - ct:fail({unexpected,Other}) + {'EXIT',{badarg,Where}} -> + io:format("trace_pattern(~p, ~p) ->\n {'EXIT',{badarg,~p}}", + [MFA,Pattern,Where]), + ok; + Other -> + io:format("trace_pattern(~p, ~p) -> ~p", + [MFA, Pattern, Other]), + ct:fail({unexpected,Other}) end. %% Basic test of PAM. pam(Config) when is_list(Config) -> - ?line start_tracer(), - ?line Self = self(), + start_tracer(), + Self = self(), %% Build the match program. - ?line Prog1 = {[{a,tuple},'$1'],[],[]}, - ?line Prog2 = {[{a,bigger,tuple},'$1'],[],[{message,'$1'}]}, - ?line MatchProg = [Prog1,Prog2], - ?line pam_trace(MatchProg), + Prog1 = {[{a,tuple},'$1'],[],[]}, + Prog2 = {[{a,bigger,tuple},'$1'],[],[{message,'$1'}]}, + MatchProg = [Prog1,Prog2], + pam_trace(MatchProg), %% Do some calls. - ?line ?MODULE:pam_foo(not_a_tuple, [a,b]), - ?line ?MODULE:pam_foo({a,tuple}, [a,list]), - ?line ?MODULE:pam_foo([this,one,will,'not',match], dummy_arg), - ?line LongList = lists:seq(1,10), - ?line ?MODULE:pam_foo({a,bigger,tuple}, LongList), + ?MODULE:pam_foo(not_a_tuple, [a,b]), + ?MODULE:pam_foo({a,tuple}, [a,list]), + ?MODULE:pam_foo([this,one,will,'not',match], dummy_arg), + LongList = lists:seq(1,10), + ?MODULE:pam_foo({a,bigger,tuple}, LongList), %% Check that we get the correct trace messages. - ?line expect({trace,Self,call,{?MODULE,pam_foo,[{a,tuple},[a,list]]}}), - ?line expect({trace,Self,call, - {?MODULE,pam_foo,[{a,bigger,tuple},LongList]}, - LongList}), + expect({trace,Self,call,{?MODULE,pam_foo,[{a,tuple},[a,list]]}}), + expect({trace,Self,call, + {?MODULE,pam_foo,[{a,bigger,tuple},LongList]}, + LongList}), - ?line trace_func({?MODULE,pam_foo,'_'}, false), + trace_func({?MODULE,pam_foo,'_'}, false), ok. pam_trace(Prog) -> @@ -541,38 +540,38 @@ pam_foo(A, B) -> %% Test changing PAM programs for a function. change_pam(_Config) -> case test_server:is_native(lists) of - true -> {skip,"lists is native"}; - false -> change_pam() + true -> {skip,"lists is native"}; + false -> change_pam() end. change_pam() -> - ?line start_tracer(), - ?line Self = self(), + start_tracer(), + Self = self(), %% Install the first match program. %% Test using timestamp at the same time. - ?line trace_pid(Self, true, [call,arity,timestamp]), - ?line Prog1 = [{['$1','$2'],[],[{message,'$1'}]}], - ?line change_pam_trace(Prog1), - ?line [x,y] = lists:append(id([x]), id([y])), - ?line {heap_size,_} = erlang:process_info(Self, heap_size), - ?line expect({trace_ts,Self,call,{lists,append,2},[x],ts}), - ?line expect({trace_ts,Self,call,{erlang,process_info,2},Self,ts}), + trace_pid(Self, true, [call,arity,timestamp]), + Prog1 = [{['$1','$2'],[],[{message,'$1'}]}], + change_pam_trace(Prog1), + [x,y] = lists:append(id([x]), id([y])), + {heap_size,_} = erlang:process_info(Self, heap_size), + expect({trace_ts,Self,call,{lists,append,2},[x],ts}), + expect({trace_ts,Self,call,{erlang,process_info,2},Self,ts}), %% Install a new PAM program. - ?line Prog2 = [{['$1','$2'],[],[{message,'$2'}]}], - ?line change_pam_trace(Prog2), - ?line [xx,yy] = lists:append(id([xx]), id([yy])), - ?line {current_function,_} = erlang:process_info(Self, current_function), - ?line expect({trace_ts,Self,call,{lists,append,2},[yy],ts}), - ?line expect({trace_ts,Self,call,{erlang,process_info,2},current_function,ts}), + Prog2 = [{['$1','$2'],[],[{message,'$2'}]}], + change_pam_trace(Prog2), + [xx,yy] = lists:append(id([xx]), id([yy])), + {current_function,_} = erlang:process_info(Self, current_function), + expect({trace_ts,Self,call,{lists,append,2},[yy],ts}), + expect({trace_ts,Self,call,{erlang,process_info,2},current_function,ts}), - ?line 1 = trace_func({lists,append,2}, false), - ?line 1 = trace_func({erlang,process_info,2}, false), - ?line {match_spec,false} = trace_info({lists,append,2}, match_spec), - ?line {match_spec,false} = trace_info({erlang,process_info,2}, match_spec), + 1 = trace_func({lists,append,2}, false), + 1 = trace_func({erlang,process_info,2}, false), + {match_spec,false} = trace_info({lists,append,2}, match_spec), + {match_spec,false} = trace_info({erlang,process_info,2}, match_spec), ok. @@ -585,71 +584,71 @@ change_pam_trace(Prog) -> return_trace(_Config) -> case test_server:is_native(lists) of - true -> {skip,"lists is native"}; - false -> return_trace() + true -> {skip,"lists is native"}; + false -> return_trace() end. return_trace() -> X = {save,me}, - ?line start_tracer(), - ?line Self = self(), + start_tracer(), + Self = self(), %% Test call and return trace and timestamp. - ?line trace_pid(Self, true, [call,timestamp]), + trace_pid(Self, true, [call,timestamp]), Stupid = {pointless,tuple}, - ?line Prog1 = [{['$1','$2'],[],[{return_trace},{message,{Stupid}}]}], - ?line 1 = trace_func({lists,append,2}, Prog1), - ?line 1 = trace_func({erlang,process_info,2}, Prog1), - ?line {match_spec,Prog1} = trace_info({lists,append,2}, match_spec), - ?line {match_spec,Prog1} = trace_info({erlang,process_info,2}, match_spec), + Prog1 = [{['$1','$2'],[],[{return_trace},{message,{Stupid}}]}], + 1 = trace_func({lists,append,2}, Prog1), + 1 = trace_func({erlang,process_info,2}, Prog1), + {match_spec,Prog1} = trace_info({lists,append,2}, match_spec), + {match_spec,Prog1} = trace_info({erlang,process_info,2}, match_spec), - ?line [x,y] = lists:append(id([x]), id([y])), + [x,y] = lists:append(id([x]), id([y])), Current = {current_function,{?MODULE,return_trace,0}}, - ?line Current = erlang:process_info(Self, current_function), - ?line expect({trace_ts,Self,call,{lists,append,[[x],[y]]},Stupid,ts}), - ?line expect({trace_ts,Self,return_from,{lists,append,2},[x,y],ts}), - ?line expect({trace_ts,Self,call,{erlang,process_info,[Self,current_function]}, - Stupid,ts}), - ?line expect({trace_ts,Self,return_from,{erlang,process_info,2},Current,ts}), + Current = erlang:process_info(Self, current_function), + expect({trace_ts,Self,call,{lists,append,[[x],[y]]},Stupid,ts}), + expect({trace_ts,Self,return_from,{lists,append,2},[x,y],ts}), + expect({trace_ts,Self,call,{erlang,process_info,[Self,current_function]}, + Stupid,ts}), + expect({trace_ts,Self,return_from,{erlang,process_info,2},Current,ts}), %% Try catch/exit. - ?line 1 = trace_func({?MODULE,nasty,0}, [{[],[],[{return_trace},{message,false}]}]), - ?line {'EXIT',good_bye} = (catch ?MODULE:nasty()), - ?line 1 = trace_func({?MODULE,nasty,0}, false), + 1 = trace_func({?MODULE,nasty,0}, [{[],[],[{return_trace},{message,false}]}]), + {'EXIT',good_bye} = (catch ?MODULE:nasty()), + 1 = trace_func({?MODULE,nasty,0}, false), %% Turn off trace. - ?line 1 = trace_func({lists,append,2}, false), - ?line 1 = trace_func({erlang,process_info,2}, false), - ?line {match_spec,false} = trace_info({lists,append,2}, match_spec), - ?line {match_spec,false} = trace_info({erlang,process_info,2}, match_spec), + 1 = trace_func({lists,append,2}, false), + 1 = trace_func({erlang,process_info,2}, false), + {match_spec,false} = trace_info({lists,append,2}, match_spec), + {match_spec,false} = trace_info({erlang,process_info,2}, match_spec), %% No timestamp, no trace message for call. - ?line trace_pid(Self, false, [timestamp]), - ?line Prog2 = [{['$1','$2'],[],[{return_trace},{message,false}]}, - {['$1'],[],[{return_trace},{message,false}]}], - ?line 1 = trace_func({lists,seq,2}, Prog2), - ?line 1 = trace_func({erlang,atom_to_list,1}, Prog2), - ?line {match_spec,Prog2} = trace_info({lists,seq,2}, match_spec), - ?line {match_spec,Prog2} = trace_info({erlang,atom_to_list,1}, match_spec), + trace_pid(Self, false, [timestamp]), + Prog2 = [{['$1','$2'],[],[{return_trace},{message,false}]}, + {['$1'],[],[{return_trace},{message,false}]}], + 1 = trace_func({lists,seq,2}, Prog2), + 1 = trace_func({erlang,atom_to_list,1}, Prog2), + {match_spec,Prog2} = trace_info({lists,seq,2}, match_spec), + {match_spec,Prog2} = trace_info({erlang,atom_to_list,1}, match_spec), - ?line lists:seq(2, 7), - ?line _ = atom_to_list(non_literal(nisse)), - ?line expect({trace,Self,return_from,{lists,seq,2},[2,3,4,5,6,7]}), - ?line expect({trace,Self,return_from,{erlang,atom_to_list,1},"nisse"}), + lists:seq(2, 7), + _ = atom_to_list(non_literal(nisse)), + expect({trace,Self,return_from,{lists,seq,2},[2,3,4,5,6,7]}), + expect({trace,Self,return_from,{erlang,atom_to_list,1},"nisse"}), %% Turn off trace. - ?line 1 = trace_func({lists,seq,2}, false), - ?line 1 = trace_func({erlang,atom_to_list,1}, false), - ?line {match_spec,false} = trace_info({lists,seq,2}, match_spec), - ?line {match_spec,false} = trace_info({erlang,atom_to_list,1}, match_spec), + 1 = trace_func({lists,seq,2}, false), + 1 = trace_func({erlang,atom_to_list,1}, false), + {match_spec,false} = trace_info({lists,seq,2}, match_spec), + {match_spec,false} = trace_info({erlang,atom_to_list,1}, match_spec), + + {save,me} = X, - ?line {save,me} = X, - ok. nasty() -> @@ -657,103 +656,103 @@ nasty() -> exception_trace(_Config) -> case test_server:is_native(lists) of - true -> {skip,"lists is native"}; - false -> exception_trace() + true -> {skip,"lists is native"}; + false -> exception_trace() end. exception_trace() -> X = {save,me}, - ?line start_tracer(), - ?line Self = self(), + start_tracer(), + Self = self(), %% Test call and return trace and timestamp. - ?line trace_pid(Self, true, [call,timestamp]), + trace_pid(Self, true, [call,timestamp]), Stupid = {pointless,tuple}, - ?line Prog1 = [{['$1','$2'],[],[{exception_trace},{message,{Stupid}}]}], - ?line 1 = trace_func({lists,append,2}, Prog1), - ?line 1 = trace_func({erlang,process_info,2}, Prog1), - ?line {match_spec,Prog1} = trace_info({lists,append,2}, match_spec), - ?line {match_spec,Prog1} = - trace_info({erlang,process_info,2}, match_spec), - - ?line [x,y] = lists:append(id([x]), id([y])), + Prog1 = [{['$1','$2'],[],[{exception_trace},{message,{Stupid}}]}], + 1 = trace_func({lists,append,2}, Prog1), + 1 = trace_func({erlang,process_info,2}, Prog1), + {match_spec,Prog1} = trace_info({lists,append,2}, match_spec), + {match_spec,Prog1} = + trace_info({erlang,process_info,2}, match_spec), + + [x,y] = lists:append(id([x]), id([y])), Current = {current_function,{?MODULE,exception_trace,0}}, - ?line Current = erlang:process_info(Self, current_function), - ?line expect({trace_ts,Self,call,{lists,append,[[x],[y]]},Stupid,ts}), - ?line expect({trace_ts,Self,return_from,{lists,append,2},[x,y],ts}), - ?line expect({trace_ts,Self,call,{erlang,process_info, - [Self,current_function]}, - Stupid,ts}), - ?line expect({trace_ts,Self,return_from, - {erlang,process_info,2},Current,ts}), + Current = erlang:process_info(Self, current_function), + expect({trace_ts,Self,call,{lists,append,[[x],[y]]},Stupid,ts}), + expect({trace_ts,Self,return_from,{lists,append,2},[x,y],ts}), + expect({trace_ts,Self,call,{erlang,process_info, + [Self,current_function]}, + Stupid,ts}), + expect({trace_ts,Self,return_from, + {erlang,process_info,2},Current,ts}), %% Try catch/exit. - ?line 1 = trace_func({?MODULE,nasty,0}, - [{[],[],[{exception_trace},{message,false}]}]), - ?line {'EXIT',good_bye} = (catch ?MODULE:nasty()), - ?line expect({trace_ts,Self,exception_from, - {?MODULE,nasty,0},{exit,good_bye},ts}), - ?line 1 = trace_func({?MODULE,nasty,0}, false), + 1 = trace_func({?MODULE,nasty,0}, + [{[],[],[{exception_trace},{message,false}]}]), + {'EXIT',good_bye} = (catch ?MODULE:nasty()), + expect({trace_ts,Self,exception_from, + {?MODULE,nasty,0},{exit,good_bye},ts}), + 1 = trace_func({?MODULE,nasty,0}, false), %% Turn off trace. - ?line 1 = trace_func({lists,append,2}, false), - ?line 1 = trace_func({erlang,process_info,2}, false), - ?line {match_spec,false} = trace_info({lists,append,2}, match_spec), - ?line {match_spec,false} = - trace_info({erlang,process_info,2}, match_spec), + 1 = trace_func({lists,append,2}, false), + 1 = trace_func({erlang,process_info,2}, false), + {match_spec,false} = trace_info({lists,append,2}, match_spec), + {match_spec,false} = + trace_info({erlang,process_info,2}, match_spec), %% No timestamp, no trace message for call. - ?line trace_pid(Self, false, [timestamp]), - ?line Prog2 = [{['$1','$2'],[],[{exception_trace},{message,false}]}, - {['$1'],[],[{exception_trace},{message,false}]}], - ?line 1 = trace_func({lists,seq,2}, Prog2), - ?line 1 = trace_func({erlang,atom_to_list,1}, Prog2), - ?line {match_spec,Prog2} = trace_info({lists,seq,2}, match_spec), - ?line {match_spec,Prog2} = - trace_info({erlang,atom_to_list,1}, match_spec), + trace_pid(Self, false, [timestamp]), + Prog2 = [{['$1','$2'],[],[{exception_trace},{message,false}]}, + {['$1'],[],[{exception_trace},{message,false}]}], + 1 = trace_func({lists,seq,2}, Prog2), + 1 = trace_func({erlang,atom_to_list,1}, Prog2), + {match_spec,Prog2} = trace_info({lists,seq,2}, match_spec), + {match_spec,Prog2} = + trace_info({erlang,atom_to_list,1}, match_spec), - ?line lists:seq(2, 7), - ?line _ = atom_to_list(non_literal(nisse)), - ?line expect({trace,Self,return_from,{lists,seq,2},[2,3,4,5,6,7]}), - ?line expect({trace,Self,return_from,{erlang,atom_to_list,1},"nisse"}), + lists:seq(2, 7), + _ = atom_to_list(non_literal(nisse)), + expect({trace,Self,return_from,{lists,seq,2},[2,3,4,5,6,7]}), + expect({trace,Self,return_from,{erlang,atom_to_list,1},"nisse"}), %% Turn off trace. - ?line 1 = trace_func({lists,seq,2}, false), - ?line 1 = trace_func({erlang,atom_to_list,1}, false), - ?line {match_spec,false} = trace_info({lists,seq,2}, match_spec), - ?line {match_spec,false} = - trace_info({erlang,atom_to_list,1}, match_spec), + 1 = trace_func({lists,seq,2}, false), + 1 = trace_func({erlang,atom_to_list,1}, false), + {match_spec,false} = trace_info({lists,seq,2}, match_spec), + {match_spec,false} = + trace_info({erlang,atom_to_list,1}, match_spec), - ?line expect(), - ?line {save,me} = X, + expect(), + {save,me} = X, ok. %% Test the on_load argument for trace_pattern/3. on_load(Config) when is_list(Config) -> - ?line 0 = erlang:trace_pattern(on_load, []), - ?line {traced,global} = erlang:trace_info(on_load, traced), - ?line {match_spec,[]} = erlang:trace_info(on_load, match_spec), + 0 = erlang:trace_pattern(on_load, []), + {traced,global} = erlang:trace_info(on_load, traced), + {match_spec,[]} = erlang:trace_info(on_load, match_spec), - ?line 0 = erlang:trace_pattern(on_load, true, [local]), - ?line {traced,local} = erlang:trace_info(on_load, traced), - ?line {match_spec,[]} = erlang:trace_info(on_load, match_spec), + 0 = erlang:trace_pattern(on_load, true, [local]), + {traced,local} = erlang:trace_info(on_load, traced), + {match_spec,[]} = erlang:trace_info(on_load, match_spec), - ?line 0 = erlang:trace_pattern(on_load, false, [local]), - ?line {traced,false} = erlang:trace_info(on_load, traced), - ?line {match_spec,false} = erlang:trace_info(on_load, match_spec), + 0 = erlang:trace_pattern(on_load, false, [local]), + {traced,false} = erlang:trace_info(on_load, traced), + {match_spec,false} = erlang:trace_info(on_load, match_spec), - ?line Pam1 = [{[],[],[{message,false}]}], - ?line 0 = erlang:trace_pattern(on_load, Pam1), - ?line {traced,global} = erlang:trace_info(on_load, traced), - ?line {match_spec,Pam1} = erlang:trace_info(on_load, match_spec), + Pam1 = [{[],[],[{message,false}]}], + 0 = erlang:trace_pattern(on_load, Pam1), + {traced,global} = erlang:trace_info(on_load, traced), + {match_spec,Pam1} = erlang:trace_info(on_load, match_spec), - ?line 0 = erlang:trace_pattern(on_load, true, [local]), - ?line 0 = erlang:trace_pattern(on_load, false, [local]), + 0 = erlang:trace_pattern(on_load, true, [local]), + 0 = erlang:trace_pattern(on_load, false, [local]), ok. @@ -764,282 +763,282 @@ deep_exception(Config) when is_list(Config) -> deep_exception(). deep_exception() -> - ?line start_tracer(), - ?line Self = self(), - ?line N = 200000, - ?line LongImproperList = seq(1, N-1, N), - + start_tracer(), + Self = self(), + N = 200000, + LongImproperList = seq(1, N-1, N), + Prog = [{'_',[],[{exception_trace}]}], -%% ?line 1 = trace_pid(Self, true, [call]), - ?line 1 = trace_func({?MODULE,deep,'_'}, Prog), - ?line 1 = trace_func({?MODULE,deep_1,'_'}, Prog), - ?line 1 = trace_func({?MODULE,deep_2,'_'}, Prog), - ?line 1 = trace_func({?MODULE,deep_3,'_'}, Prog), - ?line 1 = trace_func({?MODULE,deep_4,'_'}, Prog), - ?line 1 = trace_func({?MODULE,deep_5,'_'}, Prog), - ?line 1 = trace_func({?MODULE,id,'_'}, Prog), - ?line 1 = trace_func({erlang,'++','_'}, Prog), - ?line 1 = trace_func({erlang,exit,1}, Prog), - ?line 1 = trace_func({erlang,throw,1}, Prog), - ?line 2 = trace_func({erlang,error,'_'}, Prog), - ?line 1 = trace_func({lists,reverse,2}, Prog), - - ?line deep_exception(?LINE, exit, [paprika], 1, - [{trace,Self,call,{erlang,exit,[paprika]}}, - {trace,Self,exception_from,{erlang,exit,1}, - {exit,paprika}}], - exception_from, {exit,paprika}), - ?line deep_exception(?LINE, throw, [3.14], 2, - [{trace,Self,call,{erlang,throw,[3.14]}}, - {trace,Self,exception_from,{erlang,throw,1}, - {throw,3.14}}], - exception_from, {throw,3.14}), - ?line deep_exception(?LINE, error, [{paprika}], 3, - [{trace,Self,call,{erlang,error,[{paprika}]}}, - {trace,Self,exception_from,{erlang,error,1}, - {error,{paprika}}}], - exception_from, {error,{paprika}}), - ?line deep_exception(?LINE, error, ["{paprika}",[]], 3, - [{trace,Self,call,{erlang,error,["{paprika}",[]]}}, - {trace,Self,exception_from,{erlang,error,2}, - {error,"{paprika}"}}], - exception_from, {error,"{paprika}"}), - ?line deep_exception(?LINE, id, [broccoli], 4, [], - return_from, broccoli), - ?line deep_exception( - ?LINE, append, [1,2], 5, - [{trace,Self,call,{erlang,'++',[1,2]}}, - {trace,Self,exception_from,{erlang,'++',2},{error,badarg}}], - exception_from, {error,badarg}), - ?line deep_exception(?LINE, '=', [1,2], 6, [], - exception_from, {error,{badmatch,2}}), + %% 1 = trace_pid(Self, true, [call]), + 1 = trace_func({?MODULE,deep,'_'}, Prog), + 1 = trace_func({?MODULE,deep_1,'_'}, Prog), + 1 = trace_func({?MODULE,deep_2,'_'}, Prog), + 1 = trace_func({?MODULE,deep_3,'_'}, Prog), + 1 = trace_func({?MODULE,deep_4,'_'}, Prog), + 1 = trace_func({?MODULE,deep_5,'_'}, Prog), + 1 = trace_func({?MODULE,id,'_'}, Prog), + 1 = trace_func({erlang,'++','_'}, Prog), + 1 = trace_func({erlang,exit,1}, Prog), + 1 = trace_func({erlang,throw,1}, Prog), + 2 = trace_func({erlang,error,'_'}, Prog), + 1 = trace_func({lists,reverse,2}, Prog), + + deep_exception(?LINE, exit, [paprika], 1, + [{trace,Self,call,{erlang,exit,[paprika]}}, + {trace,Self,exception_from,{erlang,exit,1}, + {exit,paprika}}], + exception_from, {exit,paprika}), + deep_exception(?LINE, throw, [3.14], 2, + [{trace,Self,call,{erlang,throw,[3.14]}}, + {trace,Self,exception_from,{erlang,throw,1}, + {throw,3.14}}], + exception_from, {throw,3.14}), + deep_exception(?LINE, error, [{paprika}], 3, + [{trace,Self,call,{erlang,error,[{paprika}]}}, + {trace,Self,exception_from,{erlang,error,1}, + {error,{paprika}}}], + exception_from, {error,{paprika}}), + deep_exception(?LINE, error, ["{paprika}",[]], 3, + [{trace,Self,call,{erlang,error,["{paprika}",[]]}}, + {trace,Self,exception_from,{erlang,error,2}, + {error,"{paprika}"}}], + exception_from, {error,"{paprika}"}), + deep_exception(?LINE, id, [broccoli], 4, [], + return_from, broccoli), + deep_exception( + ?LINE, append, [1,2], 5, + [{trace,Self,call,{erlang,'++',[1,2]}}, + {trace,Self,exception_from,{erlang,'++',2},{error,badarg}}], + exception_from, {error,badarg}), + deep_exception(?LINE, '=', [1,2], 6, [], + exception_from, {error,{badmatch,2}}), %% - ?line io:format("== Subtest: ~w", [?LINE]), - ?line try lists:reverse(LongImproperList, []) of - R1 -> ct:fail({returned,abbr(R1)}) - catch error:badarg -> ok - end, - ?line expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) - when is_list(L1), is_list(L2), S == Self -> - next; - ({trace,S,exception_from, - {lists,reverse,2},{error,badarg}}) - when S == Self -> - expected; - ('_') -> - {trace,Self,exception_from, - {lists,reverse,2},{error,badarg}}; - (_) -> - {unexpected, - {trace,Self,exception_from, - {lists,reverse,2},{error,badarg}}} - end), - ?line deep_exception(?LINE, deep_5, [1,2], 7, - [{trace,Self,call,{erlang,error,[undef]}}, - {trace,Self,exception_from,{erlang,error,1}, - {error,undef}}], - exception_from, {error,undef}), - ?line deep_exception(?LINE, deep_5, [undef], 8, - [{trace,Self,call,{?MODULE,deep_5,[undef]}}, - {trace,Self,exception_from,{?MODULE,deep_5,1}, - {error,function_clause}}], - exception_from, {error,function_clause}), - + io:format("== Subtest: ~w", [?LINE]), + try lists:reverse(LongImproperList, []) of + R1 -> ct:fail({returned,abbr(R1)}) + catch error:badarg -> ok + end, + expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) + when is_list(L1), is_list(L2), S == Self -> + next; + ({trace,S,exception_from, + {lists,reverse,2},{error,badarg}}) + when S == Self -> + expected; + ('_') -> + {trace,Self,exception_from, + {lists,reverse,2},{error,badarg}}; + (_) -> + {unexpected, + {trace,Self,exception_from, + {lists,reverse,2},{error,badarg}}} + end), + deep_exception(?LINE, deep_5, [1,2], 7, + [{trace,Self,call,{erlang,error,[undef]}}, + {trace,Self,exception_from,{erlang,error,1}, + {error,undef}}], + exception_from, {error,undef}), + deep_exception(?LINE, deep_5, [undef], 8, + [{trace,Self,call,{?MODULE,deep_5,[undef]}}, + {trace,Self,exception_from,{?MODULE,deep_5,1}, + {error,function_clause}}], + exception_from, {error,function_clause}), + %% Apply %% - ?line deep_exception(?LINE, apply, [erlang,error,[[mo|rot]]], 1, - [{trace,Self,call,{erlang,error,[[mo|rot]]}}, - {trace,Self,exception_from,{erlang,error,1}, - {error,[mo|rot]}}], - exception_from, {error,[mo|rot]}), - ?line deep_exception(?LINE, apply, [erlang,error,[[mo|"rot"],[]]], 1, - [{trace,Self,call,{erlang,error,[[mo|"rot"],[]]}}, - {trace,Self,exception_from,{erlang,error,2}, - {error,[mo|"rot"]}}], - exception_from, {error,[mo|"rot"]}), - ?line Morot = make_ref(), - ?line deep_exception(?LINE, apply, [erlang,throw,[Morot]], 3, - [{trace,Self,call,{erlang,throw,[Morot]}}, - {trace,Self,exception_from,{erlang,throw,1}, - {throw,Morot}}], - exception_from, {throw,Morot}), - ?line deep_exception(?LINE, apply, [erlang,exit,[["morot"|Morot]]], 2, - [{trace,Self,call,{erlang,exit,[["morot"|Morot]]}}, - {trace,Self,exception_from,{erlang,exit,1}, - {exit,["morot"|Morot]}}], - exception_from, {exit,["morot"|Morot]}), - ?line deep_exception( - ?LINE, apply, [?MODULE,id,[spenat]], 4, - [{trace,Self,call,{?MODULE,id,[spenat]}}, - {trace,Self,return_from,{?MODULE,id,1},spenat}], - return_from, spenat), - ?line deep_exception( - ?LINE, apply, [erlang,'++',[1,2]], 5, - [{trace,Self,call,{erlang,'++',[1,2]}}, - {trace,Self,exception_from,{erlang,'++',2},{error,badarg}}], - exception_from, {error,badarg}), - ?line io:format("== Subtest: ~w", [?LINE]), - ?line try apply(lists, reverse, [LongImproperList, []]) of - R2 -> ct:fail({returned,abbr(R2)}) - catch error:badarg -> ok - end, - ?line expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) - when is_list(L1), is_list(L2), S == Self -> - next; - ({trace,S,exception_from, - {lists,reverse,2},{error,badarg}}) - when S == Self -> - expected; - ('_') -> - {trace,Self,exception_from, - {lists,reverse,2},{error,badarg}}; - (_) -> - {unexpected, - {trace,Self,exception_from, - {lists,reverse,2},{error,badarg}}} - end), - ?line deep_exception(?LINE, apply, [?MODULE,deep_5,[1,2]], 7, - [{trace,Self,call,{erlang,error,[undef]}}, - {trace,Self,exception_from,{erlang,error,1}, - {error,undef}}], - exception_from, {error,undef}), - ?line deep_exception(?LINE, apply, [?MODULE,deep_5,[undef]], 8, - [{trace,Self,call,{?MODULE,deep_5,[undef]}}, - {trace,Self,exception_from,{?MODULE,deep_5,1}, - {error,function_clause}}], - exception_from, {error,function_clause}), + deep_exception(?LINE, apply, [erlang,error,[[mo|rot]]], 1, + [{trace,Self,call,{erlang,error,[[mo|rot]]}}, + {trace,Self,exception_from,{erlang,error,1}, + {error,[mo|rot]}}], + exception_from, {error,[mo|rot]}), + deep_exception(?LINE, apply, [erlang,error,[[mo|"rot"],[]]], 1, + [{trace,Self,call,{erlang,error,[[mo|"rot"],[]]}}, + {trace,Self,exception_from,{erlang,error,2}, + {error,[mo|"rot"]}}], + exception_from, {error,[mo|"rot"]}), + Morot = make_ref(), + deep_exception(?LINE, apply, [erlang,throw,[Morot]], 3, + [{trace,Self,call,{erlang,throw,[Morot]}}, + {trace,Self,exception_from,{erlang,throw,1}, + {throw,Morot}}], + exception_from, {throw,Morot}), + deep_exception(?LINE, apply, [erlang,exit,[["morot"|Morot]]], 2, + [{trace,Self,call,{erlang,exit,[["morot"|Morot]]}}, + {trace,Self,exception_from,{erlang,exit,1}, + {exit,["morot"|Morot]}}], + exception_from, {exit,["morot"|Morot]}), + deep_exception( + ?LINE, apply, [?MODULE,id,[spenat]], 4, + [{trace,Self,call,{?MODULE,id,[spenat]}}, + {trace,Self,return_from,{?MODULE,id,1},spenat}], + return_from, spenat), + deep_exception( + ?LINE, apply, [erlang,'++',[1,2]], 5, + [{trace,Self,call,{erlang,'++',[1,2]}}, + {trace,Self,exception_from,{erlang,'++',2},{error,badarg}}], + exception_from, {error,badarg}), + io:format("== Subtest: ~w", [?LINE]), + try apply(lists, reverse, [LongImproperList, []]) of + R2 -> ct:fail({returned,abbr(R2)}) + catch error:badarg -> ok + end, + expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) + when is_list(L1), is_list(L2), S == Self -> + next; + ({trace,S,exception_from, + {lists,reverse,2},{error,badarg}}) + when S == Self -> + expected; + ('_') -> + {trace,Self,exception_from, + {lists,reverse,2},{error,badarg}}; + (_) -> + {unexpected, + {trace,Self,exception_from, + {lists,reverse,2},{error,badarg}}} + end), + deep_exception(?LINE, apply, [?MODULE,deep_5,[1,2]], 7, + [{trace,Self,call,{erlang,error,[undef]}}, + {trace,Self,exception_from,{erlang,error,1}, + {error,undef}}], + exception_from, {error,undef}), + deep_exception(?LINE, apply, [?MODULE,deep_5,[undef]], 8, + [{trace,Self,call,{?MODULE,deep_5,[undef]}}, + {trace,Self,exception_from,{?MODULE,deep_5,1}, + {error,function_clause}}], + exception_from, {error,function_clause}), %% Apply of fun %% - ?line deep_exception(?LINE, apply, - [fun () -> - erlang:error([{"palsternacka",3.14},17]) - end, []], 1, - [{trace,Self,call, - {erlang,error,[[{"palsternacka",3.14},17]]}}, - {trace,Self,exception_from,{erlang,error,1}, - {error,[{"palsternacka",3.14},17]}}], - exception_from, {error,[{"palsternacka",3.14},17]}), - ?line deep_exception(?LINE, apply, - [fun () -> - erlang:error(["palsternacka",17], []) - end, []], 1, - [{trace,Self,call, - {erlang,error,[["palsternacka",17],[]]}}, - {trace,Self,exception_from,{erlang,error,2}, - {error,["palsternacka",17]}}], - exception_from, {error,["palsternacka",17]}), - ?line deep_exception(?LINE, apply, - [fun () -> erlang:throw(Self) end, []], 2, - [{trace,Self,call,{erlang,throw,[Self]}}, - {trace,Self,exception_from,{erlang,throw,1}, - {throw,Self}}], - exception_from, {throw,Self}), - ?line deep_exception(?LINE, apply, - [fun () -> - erlang:exit({1,2,3,4,[5,palsternacka]}) - end, []], 3, - [{trace,Self,call, - {erlang,exit,[{1,2,3,4,[5,palsternacka]}]}}, - {trace,Self,exception_from,{erlang,exit,1}, - {exit,{1,2,3,4,[5,palsternacka]}}}], - exception_from, {exit,{1,2,3,4,[5,palsternacka]}}), - ?line deep_exception(?LINE, apply, - [fun () -> ?MODULE:id(bladsallad) end, []], 4, - [{trace,Self,call,{?MODULE,id,[bladsallad]}}, - {trace,Self,return_from,{?MODULE,id,1},bladsallad}], - return_from, bladsallad), - ?line deep_exception(?LINE, apply, - [fun (A, B) -> A ++ B end, [1,2]], 5, - [{trace,Self,call,{erlang,'++',[1,2]}}, - {trace,Self,exception_from, - {erlang,'++',2},{error,badarg}}], - exception_from, {error,badarg}), - ?line deep_exception(?LINE, apply, [fun (A, B) -> A = B end, [1,2]], 6, - [], - exception_from, {error,{badmatch,2}}), - ?line io:format("== Subtest: ~w", [?LINE]), - ?line try apply(fun() -> lists:reverse(LongImproperList, []) end, []) of - R3 -> ct:fail({returned,abbr(R3)}) - catch error:badarg -> ok - end, - ?line expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) - when is_list(L1), is_list(L2), S == Self -> - next; - ({trace,S,exception_from, - {lists,reverse,2},{error,badarg}}) - when S == Self -> - expected; - ('_') -> - {trace,Self,exception_from, - {lists,reverse,2},{error,badarg}}; - (_) -> - {unexpected, - {trace,Self,exception_from, - {lists,reverse,2},{error,badarg}}} - end), - ?line deep_exception(?LINE, apply, - [fun () -> ?MODULE:deep_5(1,2) end, []], 7, - [{trace,Self,call,{erlang,error,[undef]}}, - {trace,Self,exception_from,{erlang,error,1}, - {error,undef}}], - exception_from, {error,undef}), - ?line deep_exception(?LINE, apply, - [fun () -> ?MODULE:deep_5(undef) end, []], 8, - [{trace,Self,call,{?MODULE,deep_5,[undef]}}, - {trace,Self,exception_from,{?MODULE,deep_5,1}, - {error,function_clause}}], - exception_from, {error,function_clause}), - - ?line trace_func({?MODULE,'_','_'}, false), - ?line trace_func({erlang,'_','_'}, false), - ?line trace_func({lists,'_','_'}, false), - ?line expect(), - ?line ok. + deep_exception(?LINE, apply, + [fun () -> + erlang:error([{"palsternacka",3.14},17]) + end, []], 1, + [{trace,Self,call, + {erlang,error,[[{"palsternacka",3.14},17]]}}, + {trace,Self,exception_from,{erlang,error,1}, + {error,[{"palsternacka",3.14},17]}}], + exception_from, {error,[{"palsternacka",3.14},17]}), + deep_exception(?LINE, apply, + [fun () -> + erlang:error(["palsternacka",17], []) + end, []], 1, + [{trace,Self,call, + {erlang,error,[["palsternacka",17],[]]}}, + {trace,Self,exception_from,{erlang,error,2}, + {error,["palsternacka",17]}}], + exception_from, {error,["palsternacka",17]}), + deep_exception(?LINE, apply, + [fun () -> erlang:throw(Self) end, []], 2, + [{trace,Self,call,{erlang,throw,[Self]}}, + {trace,Self,exception_from,{erlang,throw,1}, + {throw,Self}}], + exception_from, {throw,Self}), + deep_exception(?LINE, apply, + [fun () -> + erlang:exit({1,2,3,4,[5,palsternacka]}) + end, []], 3, + [{trace,Self,call, + {erlang,exit,[{1,2,3,4,[5,palsternacka]}]}}, + {trace,Self,exception_from,{erlang,exit,1}, + {exit,{1,2,3,4,[5,palsternacka]}}}], + exception_from, {exit,{1,2,3,4,[5,palsternacka]}}), + deep_exception(?LINE, apply, + [fun () -> ?MODULE:id(bladsallad) end, []], 4, + [{trace,Self,call,{?MODULE,id,[bladsallad]}}, + {trace,Self,return_from,{?MODULE,id,1},bladsallad}], + return_from, bladsallad), + deep_exception(?LINE, apply, + [fun (A, B) -> A ++ B end, [1,2]], 5, + [{trace,Self,call,{erlang,'++',[1,2]}}, + {trace,Self,exception_from, + {erlang,'++',2},{error,badarg}}], + exception_from, {error,badarg}), + deep_exception(?LINE, apply, [fun (A, B) -> A = B end, [1,2]], 6, + [], + exception_from, {error,{badmatch,2}}), + io:format("== Subtest: ~w", [?LINE]), + try apply(fun() -> lists:reverse(LongImproperList, []) end, []) of + R3 -> ct:fail({returned,abbr(R3)}) + catch error:badarg -> ok + end, + expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) + when is_list(L1), is_list(L2), S == Self -> + next; + ({trace,S,exception_from, + {lists,reverse,2},{error,badarg}}) + when S == Self -> + expected; + ('_') -> + {trace,Self,exception_from, + {lists,reverse,2},{error,badarg}}; + (_) -> + {unexpected, + {trace,Self,exception_from, + {lists,reverse,2},{error,badarg}}} + end), + deep_exception(?LINE, apply, + [fun () -> ?MODULE:deep_5(1,2) end, []], 7, + [{trace,Self,call,{erlang,error,[undef]}}, + {trace,Self,exception_from,{erlang,error,1}, + {error,undef}}], + exception_from, {error,undef}), + deep_exception(?LINE, apply, + [fun () -> ?MODULE:deep_5(undef) end, []], 8, + [{trace,Self,call,{?MODULE,deep_5,[undef]}}, + {trace,Self,exception_from,{?MODULE,deep_5,1}, + {error,function_clause}}], + exception_from, {error,function_clause}), + + trace_func({?MODULE,'_','_'}, false), + trace_func({erlang,'_','_'}, false), + trace_func({lists,'_','_'}, false), + expect(), + ok. deep_exception(Line, B, Q, N, Extra, Tag, R) -> - ?line Self = self(), - ?line io:format("== Subtest: ~w", [Line]), - ?line Result = ?MODULE:deep(N, B, Q), - ?line Result = deep_expect(Self, B, Q, N, Extra, Tag, R). + Self = self(), + io:format("== Subtest: ~w", [Line]), + Result = ?MODULE:deep(N, B, Q), + Result = deep_expect(Self, B, Q, N, Extra, Tag, R). deep_expect(Self, B, Q, N, Extra, Tag, R) -> - ?line expect({trace,Self,call,{?MODULE,deep,[N,B,Q]}}), - ?line Result = deep_expect_N(Self, B, Q, N, Extra, Tag, R), - ?line expect({trace,Self,return_from,{?MODULE,deep,3},Result}), - ?line Result. + expect({trace,Self,call,{?MODULE,deep,[N,B,Q]}}), + Result = deep_expect_N(Self, B, Q, N, Extra, Tag, R), + expect({trace,Self,return_from,{?MODULE,deep,3},Result}), + Result. deep_expect_N(Self, B, Q, N, Extra, Tag, R) -> deep_expect_N(Self, B, Q, N, Extra, Tag, R, N). deep_expect_N(Self, B, Q, N, Extra, Tag, R, J) when J > 0 -> - ?line expect({trace,Self,call,{?MODULE,deep_1,[J,B,Q]}}), - ?line deep_expect_N(Self, B, Q, N, Extra, Tag, R, J-1); + expect({trace,Self,call,{?MODULE,deep_1,[J,B,Q]}}), + deep_expect_N(Self, B, Q, N, Extra, Tag, R, J-1); deep_expect_N(Self, B, Q, N, Extra, Tag, R, 0) -> - ?line expect({trace,Self,call,{?MODULE,deep_2,[B,Q]}}), - ?line expect({trace,Self,call,{?MODULE,deep_3,[B,Q]}}), - ?line expect({trace,Self,return_from,{?MODULE,deep_3,2},{B,Q}}), - ?line expect({trace,Self,call,{?MODULE,deep_4,[{B,Q}]}}), - ?line expect({trace,Self,call,{?MODULE,id,[{B,Q}]}}), - ?line expect({trace,Self,return_from,{?MODULE,id,1},{B,Q}}), - ?line deep_expect_Extra(Self, N, Extra, Tag, R), - ?line expect({trace,Self,Tag,{?MODULE,deep_4,1},R}), - ?line expect({trace,Self,Tag,{?MODULE,deep_2,2},R}), - ?line deep_expect_N(Self, N, Tag, R). + expect({trace,Self,call,{?MODULE,deep_2,[B,Q]}}), + expect({trace,Self,call,{?MODULE,deep_3,[B,Q]}}), + expect({trace,Self,return_from,{?MODULE,deep_3,2},{B,Q}}), + expect({trace,Self,call,{?MODULE,deep_4,[{B,Q}]}}), + expect({trace,Self,call,{?MODULE,id,[{B,Q}]}}), + expect({trace,Self,return_from,{?MODULE,id,1},{B,Q}}), + deep_expect_Extra(Self, N, Extra, Tag, R), + expect({trace,Self,Tag,{?MODULE,deep_4,1},R}), + expect({trace,Self,Tag,{?MODULE,deep_2,2},R}), + deep_expect_N(Self, N, Tag, R). deep_expect_Extra(Self, N, [E|Es], Tag, R) -> - ?line expect(E), - ?line deep_expect_Extra(Self, N, Es, Tag, R); + expect(E), + deep_expect_Extra(Self, N, Es, Tag, R); deep_expect_Extra(_Self, _N, [], _Tag, _R) -> - ?line ok. + ok. deep_expect_N(Self, N, Tag, R) when N > 0 -> - ?line expect({trace,Self,Tag,{?MODULE,deep_1,3},R}), - ?line deep_expect_N(Self, N-1, Tag, R); + expect({trace,Self,Tag,{?MODULE,deep_1,3},R}), + deep_expect_N(Self, N-1, Tag, R); deep_expect_N(_Self, 0, return_from, R) -> - ?line {value,R}; + {value,R}; deep_expect_N(_Self, 0, exception_from, R) -> - ?line R. + R. @@ -1053,78 +1052,78 @@ exception_nocatch() -> Deep4LocBadmatch = get_deep_4_loc({'=',[a,b]}), Prog = [{'_',[],[{exception_trace}]}], - ?line 1 = erlang:trace_pattern({?MODULE,deep_1,'_'}, Prog), - ?line 1 = erlang:trace_pattern({?MODULE,deep_2,'_'}, Prog), - ?line 1 = erlang:trace_pattern({?MODULE,deep_3,'_'}, Prog), - ?line 1 = erlang:trace_pattern({?MODULE,deep_4,'_'}, Prog), - ?line 1 = erlang:trace_pattern({?MODULE,deep_5,'_'}, Prog), - ?line 1 = erlang:trace_pattern({?MODULE,id,'_'}, Prog), - ?line 1 = erlang:trace_pattern({erlang,exit,1}, Prog), - ?line 1 = erlang:trace_pattern({erlang,throw,1}, Prog), - ?line 2 = erlang:trace_pattern({erlang,error,'_'}, Prog), - ?line Q1 = {make_ref(),Prog}, - ?line T1 = - exception_nocatch(?LINE, exit, [Q1], 3, - [{trace,t1,call,{erlang,exit,[Q1]}}, - {trace,t1,exception_from,{erlang,exit,1}, - {exit,Q1}}], - exception_from, {exit,Q1}), - ?line expect({trace,T1,exit,Q1}), - ?line Q2 = {cake,14.125}, - ?line T2 = - exception_nocatch(?LINE, throw, [Q2], 2, - [{trace,t2,call,{erlang,throw,[Q2]}}, - {trace,t2,exception_from,{erlang,throw,1}, - {error,{nocatch,Q2}}}], - exception_from, {error,{nocatch,Q2}}), - ?line expect({trace,T2,exit,{{nocatch,Q2},[{erlang,throw,[Q2],[]}, - {?MODULE,deep_4,1, - Deep4LocThrow}]}}), - ?line Q3 = {dump,[dump,{dump}]}, - ?line T3 = - exception_nocatch(?LINE, error, [Q3], 4, - [{trace,t3,call,{erlang,error,[Q3]}}, - {trace,t3,exception_from,{erlang,error,1}, - {error,Q3}}], - exception_from, {error,Q3}), - ?line expect({trace,T3,exit,{Q3,[{erlang,error,[Q3],[]}, - {?MODULE,deep_4,1,Deep4LocError}]}}), - ?line T4 = - exception_nocatch(?LINE, '=', [17,4711], 5, [], - exception_from, {error,{badmatch,4711}}), - ?line expect({trace,T4,exit,{{badmatch,4711}, - [{?MODULE,deep_4,1,Deep4LocBadmatch}]}}), + 1 = erlang:trace_pattern({?MODULE,deep_1,'_'}, Prog), + 1 = erlang:trace_pattern({?MODULE,deep_2,'_'}, Prog), + 1 = erlang:trace_pattern({?MODULE,deep_3,'_'}, Prog), + 1 = erlang:trace_pattern({?MODULE,deep_4,'_'}, Prog), + 1 = erlang:trace_pattern({?MODULE,deep_5,'_'}, Prog), + 1 = erlang:trace_pattern({?MODULE,id,'_'}, Prog), + 1 = erlang:trace_pattern({erlang,exit,1}, Prog), + 1 = erlang:trace_pattern({erlang,throw,1}, Prog), + 2 = erlang:trace_pattern({erlang,error,'_'}, Prog), + Q1 = {make_ref(),Prog}, + T1 = + exception_nocatch(?LINE, exit, [Q1], 3, + [{trace,t1,call,{erlang,exit,[Q1]}}, + {trace,t1,exception_from,{erlang,exit,1}, + {exit,Q1}}], + exception_from, {exit,Q1}), + expect({trace,T1,exit,Q1}), + Q2 = {cake,14.125}, + T2 = + exception_nocatch(?LINE, throw, [Q2], 2, + [{trace,t2,call,{erlang,throw,[Q2]}}, + {trace,t2,exception_from,{erlang,throw,1}, + {error,{nocatch,Q2}}}], + exception_from, {error,{nocatch,Q2}}), + expect({trace,T2,exit,{{nocatch,Q2},[{erlang,throw,[Q2],[]}, + {?MODULE,deep_4,1, + Deep4LocThrow}]}}), + Q3 = {dump,[dump,{dump}]}, + T3 = + exception_nocatch(?LINE, error, [Q3], 4, + [{trace,t3,call,{erlang,error,[Q3]}}, + {trace,t3,exception_from,{erlang,error,1}, + {error,Q3}}], + exception_from, {error,Q3}), + expect({trace,T3,exit,{Q3,[{erlang,error,[Q3],[]}, + {?MODULE,deep_4,1,Deep4LocError}]}}), + T4 = + exception_nocatch(?LINE, '=', [17,4711], 5, [], + exception_from, {error,{badmatch,4711}}), + expect({trace,T4,exit,{{badmatch,4711}, + [{?MODULE,deep_4,1,Deep4LocBadmatch}]}}), %% - ?line erlang:trace_pattern({?MODULE,'_','_'}, false), - ?line erlang:trace_pattern({erlang,'_','_'}, false), - ?line expect(), - ?line ok. + erlang:trace_pattern({?MODULE,'_','_'}, false), + erlang:trace_pattern({erlang,'_','_'}, false), + expect(), + ok. get_deep_4_loc(Arg) -> try - deep_4(Arg), - ct:fail(should_not_return_to_here) + deep_4(Arg), + ct:fail(should_not_return_to_here) catch - _:_ -> - [{?MODULE,deep_4,1,Loc0}|_] = erlang:get_stacktrace(), - Loc0 + _:_ -> + [{?MODULE,deep_4,1,Loc0}|_] = erlang:get_stacktrace(), + Loc0 end. exception_nocatch(Line, B, Q, N, Extra, Tag, R) -> - ?line io:format("== Subtest: ~w", [Line]), - ?line Go = make_ref(), - ?line Tracee = - spawn(fun () -> - receive - Go -> - deep_1(N, B, Q) - end - end), - ?line 1 = erlang:trace(Tracee, true, [call,return_to,procs]), - ?line Tracee ! Go, - ?line deep_expect_N(Tracee, B, Q, N-1, - [setelement(2, T, Tracee) || T <- Extra], Tag, R), - ?line Tracee. + io:format("== Subtest: ~w", [Line]), + Go = make_ref(), + Tracee = + spawn(fun () -> + receive + Go -> + deep_1(N, B, Q) + end + end), + 1 = erlang:trace(Tracee, true, [call,return_to,procs]), + Tracee ! Go, + deep_expect_N(Tracee, B, Q, N-1, + [setelement(2, T, Tracee) || T <- Extra], Tag, R), + Tracee. %% Make sure that code that uses the optimized bit syntax matching %% can be traced without crashing the emulator. (Actually, it seems @@ -1132,22 +1131,22 @@ exception_nocatch(Line, B, Q, N, Extra, Tag, R) -> %% will keep the test case anyway.) bit_syntax(Config) when is_list(Config) -> - ?line start_tracer(), - ?line 1 = trace_func({?MODULE,bs_sum_a,'_'}, []), - ?line 1 = trace_func({?MODULE,bs_sum_b,'_'}, []), + start_tracer(), + 1 = trace_func({?MODULE,bs_sum_a,'_'}, []), + 1 = trace_func({?MODULE,bs_sum_b,'_'}, []), - ?line 6 = call_bs_sum_a(<<1,2,3>>), - ?line 10 = call_bs_sum_b(<<1,2,3,4>>), + 6 = call_bs_sum_a(<<1,2,3>>), + 10 = call_bs_sum_b(<<1,2,3,4>>), - ?line trace_func({?MODULE,'_','_'}, false), - ?line erlang:trace_delivered(all), + trace_func({?MODULE,'_','_'}, false), + erlang:trace_delivered(all), receive - {trace_delivered,_,_} -> ok + {trace_delivered,_,_} -> ok end, - + Self = self(), - ?line expect({trace,Self,call,{?MODULE,bs_sum_a,[<<2,3>>,1]}}), - ?line expect({trace,Self,call,{?MODULE,bs_sum_b,[1,<<2,3,4>>]}}), + expect({trace,Self,call,{?MODULE,bs_sum_a,[<<2,3>>,1]}}), + expect({trace,Self,call,{?MODULE,bs_sum_b,[1,<<2,3,4>>]}}), ok. @@ -1162,7 +1161,7 @@ bs_sum_a(<<>>, Acc) -> Acc. bs_sum_b(Acc, <>) -> bs_sum_b(H+Acc, T); bs_sum_b(Acc, <<>>) -> Acc. - + @@ -1170,98 +1169,98 @@ bs_sum_b(Acc, <<>>) -> Acc. expect() -> case flush() of - [] -> ok; - Msgs -> - ct:fail({unexpected,abbr(Msgs)}) + [] -> ok; + Msgs -> + ct:fail({unexpected,abbr(Msgs)}) end. expect({trace_ts,Pid,Type,MFA,Term,ts}=Message) -> receive - M -> - case M of - {trace_ts,Pid,Type,MFA,Term,Ts}=MessageTs -> - ok = io:format("Expected and got ~p", [abbr(MessageTs)]), - Ts; - _ -> - io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]), - ct:fail({unexpected,abbr([M|flush()])}) - end + M -> + case M of + {trace_ts,Pid,Type,MFA,Term,Ts}=MessageTs -> + ok = io:format("Expected and got ~p", [abbr(MessageTs)]), + Ts; + _ -> + io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]), + ct:fail({unexpected,abbr([M|flush()])}) + end after 5000 -> - io:format("Expected ~p; got nothing", [abbr(Message)]), - ct:fail(no_trace_message) + io:format("Expected ~p; got nothing", [abbr(Message)]), + ct:fail(no_trace_message) end; expect({trace_ts,Pid,Type,MFA,ts}=Message) -> receive - M -> - case M of - {trace_ts,Pid,Type,MFA,Ts} -> - ok = io:format("Expected and got ~p", [abbr(M)]), - Ts; - _ -> - io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]), - ct:fail({unexpected,abbr([M|flush()])}) - end + M -> + case M of + {trace_ts,Pid,Type,MFA,Ts} -> + ok = io:format("Expected and got ~p", [abbr(M)]), + Ts; + _ -> + io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]), + ct:fail({unexpected,abbr([M|flush()])}) + end after 5000 -> - io:format("Expected ~p; got nothing", [abbr(Message)]), - ct:fail(no_trace_message) + io:format("Expected ~p; got nothing", [abbr(Message)]), + ct:fail(no_trace_message) end; expect(Validator) when is_function(Validator) -> receive - M -> - case Validator(M) of - expected -> - ok = io:format("Expected and got ~p", [abbr(M)]); - next -> - ok = io:format("Expected and got ~p", [abbr(M)]), - expect(Validator); - {unexpected,Message} -> - io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]), + M -> + case Validator(M) of + expected -> + ok = io:format("Expected and got ~p", [abbr(M)]); + next -> + ok = io:format("Expected and got ~p", [abbr(M)]), + expect(Validator); + {unexpected,Message} -> + io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]), ct:fail({unexpected,abbr([M|flush()])}) - end + end after 5000 -> - io:format("Expected ~p; got nothing", [abbr(Validator('_'))]), - ct:fail(no_trace_message) + io:format("Expected ~p; got nothing", [abbr(Validator('_'))]), + ct:fail(no_trace_message) end; expect(Message) -> receive - M -> - case M of - Message -> - ok = io:format("Expected and got ~p", [abbr(Message)]); - Other -> - io:format("Expected ~p; got ~p", - [abbr(Message),abbr(Other)]), - ct:fail({unexpected,abbr([Other|flush()])}) - end + M -> + case M of + Message -> + ok = io:format("Expected and got ~p", [abbr(Message)]); + Other -> + io:format("Expected ~p; got ~p", + [abbr(Message),abbr(Other)]), + ct:fail({unexpected,abbr([Other|flush()])}) + end after 5000 -> - io:format("Expected ~p; got nothing", [abbr(Message)]), - ct:fail(no_trace_message) + io:format("Expected ~p; got nothing", [abbr(Message)]), + ct:fail(no_trace_message) end. trace_info(What, Key) -> get(tracer) ! {apply,self(),{erlang,trace_info,[What,Key]}}, Res = receive - {apply_result,Result} -> Result - end, + {apply_result,Result} -> Result + end, ok = io:format("erlang:trace_info(~p, ~p) -> ~p", - [What,Key,Res]), + [What,Key,Res]), Res. - + trace_func(MFA, MatchSpec) -> trace_func(MFA, MatchSpec, []). trace_func(MFA, MatchSpec, Flags) -> get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA, MatchSpec, Flags]}}, Res = receive - {apply_result,Result} -> Result - end, + {apply_result,Result} -> Result + end, ok = io:format("trace_pattern(~p, ~p, ~p) -> ~p", [MFA,MatchSpec,Flags,Res]), Res. trace_pid(Pid, On, Flags) -> get(tracer) ! {apply,self(),{erlang,trace,[Pid,On,Flags]}}, Res = receive - {apply_result,Result} -> Result - end, + {apply_result,Result} -> Result + end, ok = io:format("trace(~p, ~p, ~p) -> ~p", [Pid,On,Flags,Res]), Res. @@ -1281,19 +1280,19 @@ tracer(RelayTo) -> tracer_loop(RelayTo) -> receive - {apply,From,{M,F,A}} -> - From ! {apply_result,apply(M, F, A)}, - tracer_loop(RelayTo); - Msg -> - RelayTo ! Msg, - tracer_loop(RelayTo) + {apply,From,{M,F,A}} -> + From ! {apply_result,apply(M, F, A)}, + tracer_loop(RelayTo); + Msg -> + RelayTo ! Msg, + tracer_loop(RelayTo) end. id(I) -> I. deep(N, Class, Reason) -> try ?MODULE:deep_1(N, Class, Reason) of - Value -> {value,Value} + Value -> {value,Value} catch C:R -> {C,R} end. @@ -1310,30 +1309,30 @@ deep_3(Class, Reason) -> deep_4(CR) -> case ?MODULE:id(CR) of - {exit,[Reason]} -> - erlang:exit(Reason); - {throw,[Reason]} -> - erlang:throw(Reason); - {error,[Reason,Arglist]} -> - erlang:error(Reason, Arglist); - {error,[Reason]} -> - erlang:error(Reason); - {id,[Reason]} -> - Reason; - {reverse,[A,B]} -> - lists:reverse(A, B); - {append,[A,B]} -> - A ++ B; - {apply,[Fun,Args]} -> - erlang:apply(Fun, Args); - {apply,[M,F,Args]} -> - erlang:apply(M, F, Args); - {deep_5,[A,B]} -> - ?MODULE:deep_5(A, B); - {deep_5,[A]} -> - ?MODULE:deep_5(A); - {'=',[A,B]} -> - A = B + {exit,[Reason]} -> + erlang:exit(Reason); + {throw,[Reason]} -> + erlang:throw(Reason); + {error,[Reason,Arglist]} -> + erlang:error(Reason, Arglist); + {error,[Reason]} -> + erlang:error(Reason); + {id,[Reason]} -> + Reason; + {reverse,[A,B]} -> + lists:reverse(A, B); + {append,[A,B]} -> + A ++ B; + {apply,[Fun,Args]} -> + erlang:apply(Fun, Args); + {apply,[M,F,Args]} -> + erlang:apply(M, F, Args); + {deep_5,[A,B]} -> + ?MODULE:deep_5(A, B); + {deep_5,[A]} -> + ?MODULE:deep_5(A); + {'=',[A,B]} -> + A = B end. deep_5(A) when is_integer(A) -> @@ -1341,9 +1340,9 @@ deep_5(A) when is_integer(A) -> flush() -> receive X -> - [X|flush()] + [X|flush()] after 1000 -> - [] + [] end. %% Abbreviate large complex terms @@ -1366,10 +1365,10 @@ abbr_tuple(_, _, _, R) -> %% abbr_list(_, 0, R) -> case io_lib:printable_list(R) of - true -> - reverse(R, "..."); - false -> - reverse(R, '...') + true -> + reverse(R, "..."); + false -> + reverse(R, '...') end; abbr_list([H|T], N, R) -> M = N-1, diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index f917dc92eb..db285a3977 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -19,14 +19,13 @@ %% -module(code_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - versions/1,new_binary_types/1, - t_check_process_code/1,t_check_old_code/1, - t_check_process_code_ets/1, - external_fun/1,get_chunk/1,module_md5/1,make_stub/1, - make_stub_many_funs/1,constant_pools/1,constant_refc_binaries/1, - false_dependency/1,coverage/1,fun_confusion/1, +-export([all/0, suite/0, init_per_suite/1, end_per_suite/1, + versions/1,new_binary_types/1, + t_check_process_code/1,t_check_old_code/1, + t_check_process_code_ets/1, + external_fun/1,get_chunk/1,module_md5/1,make_stub/1, + make_stub_many_funs/1,constant_pools/1,constant_refc_binaries/1, + false_dependency/1,coverage/1,fun_confusion/1, t_copy_literals/1]). -define(line_trace, 1). @@ -41,9 +40,6 @@ all() -> constant_pools, constant_refc_binaries, false_dependency, coverage, fun_confusion, t_copy_literals]. -groups() -> - []. - init_per_suite(Config) -> erts_debug:set_internal_state(available_internal_state, true), Config. @@ -52,12 +48,6 @@ end_per_suite(_Config) -> catch erts_debug:set_internal_state(available_internal_state, false), ok. -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - %% Make sure that only two versions of a module can be loaded. versions(Config) when is_list(Config) -> V1 = compile_version(1, Config), @@ -78,10 +68,10 @@ versions(Config) when is_list(Config) -> _ = monitor(process, P1), _ = monitor(process, P2), receive - {'DOWN',_,process,P1,normal} -> ok + {'DOWN',_,process,P1,normal} -> ok end, receive - {'DOWN',_,process,P2,normal} -> ok + {'DOWN',_,process,P2,normal} -> ok end, true = erlang:purge_module(versions), true = erlang:delete_module(versions), @@ -92,81 +82,81 @@ compile_version(Version, Config) -> Data = proplists:get_value(data_dir, Config), File = filename:join(Data, "versions"), {ok,versions,Bin} = compile:file(File, [{d,'VERSION',Version}, - binary,report]), + binary,report]), Bin. load_version(Code, Ver) -> case erlang:load_module(versions, Code) of - {module,versions} -> - Pid = spawn_link(versions, loop, []), - Ver = versions:version(), - Ver = check_version(Pid), - {ok,Pid,Ver}; - Error -> - Error + {module,versions} -> + Pid = spawn_link(versions, loop, []), + Ver = versions:version(), + Ver = check_version(Pid), + {ok,Pid,Ver}; + Error -> + Error end. check_version(Pid) -> Pid ! {self(),version}, receive - {Pid,version,Version} -> - Version + {Pid,version,Version} -> + Version end. new_binary_types(Config) when is_list(Config) -> - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "my_code_test"), - ?line {ok,my_code_test,Bin} = compile:file(File, [binary]), - ?line {module,my_code_test} = erlang:load_module(my_code_test, - make_sub_binary(Bin)), - ?line true = erlang:delete_module(my_code_test), - ?line true = erlang:purge_module(my_code_test), - - ?line {module,my_code_test} = erlang:load_module(my_code_test, - make_unaligned_sub_binary(Bin)), - ?line true = erlang:delete_module(my_code_test), - ?line true = erlang:purge_module(my_code_test), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "my_code_test"), + {ok,my_code_test,Bin} = compile:file(File, [binary]), + {module,my_code_test} = erlang:load_module(my_code_test, + make_sub_binary(Bin)), + true = erlang:delete_module(my_code_test), + true = erlang:purge_module(my_code_test), + + {module,my_code_test} = erlang:load_module(my_code_test, + make_unaligned_sub_binary(Bin)), + true = erlang:delete_module(my_code_test), + true = erlang:purge_module(my_code_test), %% Try heap binaries and bad binaries. - ?line {error,badfile} = erlang:load_module(my_code_test, <<1,2>>), - ?line {error,badfile} = erlang:load_module(my_code_test, - make_sub_binary(<<1,2>>)), - ?line {error,badfile} = erlang:load_module(my_code_test, - make_unaligned_sub_binary(<<1,2>>)), - ?line {'EXIT',{badarg,_}} = (catch erlang:load_module(my_code_test, - bit_sized_binary(Bin))), + {error,badfile} = erlang:load_module(my_code_test, <<1,2>>), + {error,badfile} = erlang:load_module(my_code_test, + make_sub_binary(<<1,2>>)), + {error,badfile} = erlang:load_module(my_code_test, + make_unaligned_sub_binary(<<1,2>>)), + {'EXIT',{badarg,_}} = (catch erlang:load_module(my_code_test, + bit_sized_binary(Bin))), ok. t_check_process_code(Config) when is_list(Config) -> - ?line Priv = proplists:get_value(priv_dir, Config), - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "my_code_test"), - ?line Code = filename:join(Priv, "my_code_test"), + Priv = proplists:get_value(priv_dir, Config), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "my_code_test"), + Code = filename:join(Priv, "my_code_test"), - ?line {ok,my_code_test} = c:c(File, [{outdir,Priv}]), + {ok,my_code_test} = c:c(File, [{outdir,Priv}]), - ?line MyFun = fun(X, Y) -> X + Y end, %Confuse things. - ?line F = my_code_test:make_fun(42), - ?line 2 = fun_refc(F), - ?line MyFun2 = fun(X, Y) -> X * Y end, %Confuse things. - ?line 44 = F(2), + MyFun = fun(X, Y) -> X + Y end, %Confuse things. + F = my_code_test:make_fun(42), + 2 = fun_refc(F), + MyFun2 = fun(X, Y) -> X * Y end, %Confuse things. + 44 = F(2), %% Delete the module and call the fun again. - ?line true = erlang:delete_module(my_code_test), - ?line 2 = fun_refc(F), - ?line 45 = F(3), - ?line {'EXIT',{undef,_}} = (catch my_code_test:make_fun(33)), + true = erlang:delete_module(my_code_test), + 2 = fun_refc(F), + 45 = F(3), + {'EXIT',{undef,_}} = (catch my_code_test:make_fun(33)), %% The fun should still be there, preventing purge. - ?line true = erlang:check_process_code(self(), my_code_test), + true = erlang:check_process_code(self(), my_code_test), gc(), gc(), %Place funs on the old heap. - ?line true = erlang:check_process_code(self(), my_code_test), + true = erlang:check_process_code(self(), my_code_test), %% Using the funs here guarantees that they will not be prematurely garbed. - ?line 48 = F(6), - ?line 3 = MyFun(1, 2), - ?line 12 = MyFun2(3, 4), + 48 = F(6), + 3 = MyFun(1, 2), + 12 = MyFun2(3, 4), %% Kill all funs. t_check_process_code1(Code, []). @@ -174,64 +164,64 @@ t_check_process_code(Config) when is_list(Config) -> %% The real fun was killed, but we have some fakes which look similar. t_check_process_code1(Code, Fakes) -> - ?line MyFun = fun(X, Y) -> X + Y + 1 end, %Confuse things. - ?line false = erlang:check_process_code(self(), my_code_test), - ?line 4 = MyFun(1, 2), + MyFun = fun(X, Y) -> X + Y + 1 end, %Confuse things. + false = erlang:check_process_code(self(), my_code_test), + 4 = MyFun(1, 2), t_check_process_code2(Code, Fakes). t_check_process_code2(Code, _) -> - ?line false = erlang:check_process_code(self(), my_code_test), - ?line true = erlang:purge_module(my_code_test), + false = erlang:check_process_code(self(), my_code_test), + true = erlang:purge_module(my_code_test), %% In the next test we will load the same module twice. - ?line {module,my_code_test} = code:load_abs(Code), - ?line F = my_code_test:make_fun(37), - ?line 2 = fun_refc(F), - ?line false = erlang:check_process_code(self(), my_code_test), - ?line {module,my_code_test} = code:load_abs(Code), - ?line 2 = fun_refc(F), + {module,my_code_test} = code:load_abs(Code), + F = my_code_test:make_fun(37), + 2 = fun_refc(F), + false = erlang:check_process_code(self(), my_code_test), + {module,my_code_test} = code:load_abs(Code), + 2 = fun_refc(F), %% Still false because the fun with the same identify is found %% in the current code. - ?line false = erlang:check_process_code(self(), my_code_test), - + false = erlang:check_process_code(self(), my_code_test), + %% Some fake funs in the same module should not do any difference. - ?line false = erlang:check_process_code(self(), my_code_test), + false = erlang:check_process_code(self(), my_code_test), 38 = F(1), t_check_process_code3(Code, F, []). t_check_process_code3(Code, F, Fakes) -> Pid = spawn_link(fun() -> body(F, Fakes) end), - ?line true = erlang:purge_module(my_code_test), - ?line false = erlang:check_process_code(self(), my_code_test), - ?line false = erlang:check_process_code(Pid, my_code_test), + true = erlang:purge_module(my_code_test), + false = erlang:check_process_code(self(), my_code_test), + false = erlang:check_process_code(Pid, my_code_test), - ?line true = erlang:delete_module(my_code_test), - ?line true = erlang:check_process_code(self(), my_code_test), - ?line true = erlang:check_process_code(Pid, my_code_test), + true = erlang:delete_module(my_code_test), + true = erlang:check_process_code(self(), my_code_test), + true = erlang:check_process_code(Pid, my_code_test), 39 = F(2), t_check_process_code4(Code, Pid). t_check_process_code4(_Code, Pid) -> Pid ! drop_funs, receive after 1 -> ok end, - ?line false = erlang:check_process_code(Pid, my_code_test), + false = erlang:check_process_code(Pid, my_code_test), ok. body(F, Fakes) -> receive - jog -> - 40 = F(3), - erlang:garbage_collect(), - body(F, Fakes); - drop_funs -> - dropped_body() + jog -> + 40 = F(3), + erlang:garbage_collect(), + body(F, Fakes); + drop_funs -> + dropped_body() end. dropped_body() -> receive - X -> exit(X) + X -> exit(X) end. gc() -> @@ -242,57 +232,57 @@ gc1() -> ok. %% Test check_process_code/2 in combination with a fun obtained from an ets table. t_check_process_code_ets(Config) when is_list(Config) -> case test_server:is_native(?MODULE) of - true -> - {skip,"Native code"}; - false -> - do_check_process_code_ets(Config) + true -> + {skip,"Native code"}; + false -> + do_check_process_code_ets(Config) end. do_check_process_code_ets(Config) -> - ?line Priv = proplists:get_value(priv_dir, Config), - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "my_code_test"), - - ?line erlang:purge_module(my_code_test), - ?line erlang:delete_module(my_code_test), - ?line {ok,my_code_test} = c:c(File, [{outdir,Priv}]), - - ?line T = ets:new(my_code_test, []), - ?line ets:insert(T, {7,my_code_test:make_fun(107)}), - ?line ets:insert(T, {8,my_code_test:make_fun(108)}), - ?line erlang:delete_module(my_code_test), - ?line false = erlang:check_process_code(self(), my_code_test), + Priv = proplists:get_value(priv_dir, Config), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "my_code_test"), + + erlang:purge_module(my_code_test), + erlang:delete_module(my_code_test), + {ok,my_code_test} = c:c(File, [{outdir,Priv}]), + + T = ets:new(my_code_test, []), + ets:insert(T, {7,my_code_test:make_fun(107)}), + ets:insert(T, {8,my_code_test:make_fun(108)}), + erlang:delete_module(my_code_test), + false = erlang:check_process_code(self(), my_code_test), Body = fun() -> - [{7,F1}] = ets:lookup(T, 7), - [{8,F2}] = ets:lookup(T, 8), - IdleLoop = fun() -> receive _X -> ok end end, - RecLoop = fun(Again) -> - receive - call -> 110 = F1(3), - 100 = F2(-8), - Again(Again); - {drop_funs,To} -> - To ! funs_dropped, - IdleLoop() - end - end, - true = erlang:check_process_code(self(), my_code_test), - RecLoop(RecLoop) - end, - ?line Pid = spawn_link(Body), + [{7,F1}] = ets:lookup(T, 7), + [{8,F2}] = ets:lookup(T, 8), + IdleLoop = fun() -> receive _X -> ok end end, + RecLoop = fun(Again) -> + receive + call -> 110 = F1(3), + 100 = F2(-8), + Again(Again); + {drop_funs,To} -> + To ! funs_dropped, + IdleLoop() + end + end, + true = erlang:check_process_code(self(), my_code_test), + RecLoop(RecLoop) + end, + Pid = spawn_link(Body), receive after 1 -> ok end, - ?line true = erlang:check_process_code(Pid, my_code_test), + true = erlang:check_process_code(Pid, my_code_test), Pid ! call, Pid ! {drop_funs,self()}, receive - funs_dropped -> ok; - Other -> ct:fail({unexpected,Other}) + funs_dropped -> ok; + Other -> ct:fail({unexpected,Other}) after 10000 -> - ?line ct:fail(no_funs_dropped_answer) + ct:fail(no_funs_dropped_answer) end, - ?line false = erlang:check_process_code(Pid, my_code_test), + false = erlang:check_process_code(Pid, my_code_test), ok. fun_refc(F) -> @@ -302,89 +292,89 @@ fun_refc(F) -> %% Test the erlang:check_old_code/1 BIF. t_check_old_code(Config) when is_list(Config) -> - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "my_code_test"), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "my_code_test"), + + erlang:purge_module(my_code_test), + erlang:delete_module(my_code_test), + catch erlang:purge_module(my_code_test), + + false = erlang:check_old_code(my_code_test), - ?line erlang:purge_module(my_code_test), - ?line erlang:delete_module(my_code_test), - ?line catch erlang:purge_module(my_code_test), + {ok,my_code_test,Code} = compile:file(File, [binary]), + {module,my_code_test} = code:load_binary(my_code_test, File, Code), - ?line false = erlang:check_old_code(my_code_test), + false = erlang:check_old_code(my_code_test), + {module,my_code_test} = code:load_binary(my_code_test, File, Code), + true = erlang:check_old_code(my_code_test), - ?line {ok,my_code_test,Code} = compile:file(File, [binary]), - ?line {module,my_code_test} = code:load_binary(my_code_test, File, Code), - - ?line false = erlang:check_old_code(my_code_test), - ?line {module,my_code_test} = code:load_binary(my_code_test, File, Code), - ?line true = erlang:check_old_code(my_code_test), + true = erlang:purge_module(my_code_test), + true = erlang:delete_module(my_code_test), + true = erlang:purge_module(my_code_test), - ?line true = erlang:purge_module(my_code_test), - ?line true = erlang:delete_module(my_code_test), - ?line true = erlang:purge_module(my_code_test), + {'EXIT',_} = (catch erlang:check_old_code([])), - ?line {'EXIT',_} = (catch erlang:check_old_code([])), - ok. external_fun(Config) when is_list(Config) -> - ?line false = erlang:function_exported(another_code_test, x, 1), + false = erlang:function_exported(another_code_test, x, 1), AnotherCodeTest = id(another_code_test), ExtFun = fun AnotherCodeTest:x/1, - ?line {'EXIT',{undef,_}} = (catch ExtFun(answer)), - ?line false = erlang:function_exported(another_code_test, x, 1), - ?line false = lists:member(another_code_test, erlang:loaded()), - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "another_code_test"), - ?line {ok,another_code_test,Code} = compile:file(File, [binary,report]), - ?line {module,another_code_test} = erlang:load_module(another_code_test, Code), - ?line 42 = ExtFun(answer), + {'EXIT',{undef,_}} = (catch ExtFun(answer)), + false = erlang:function_exported(another_code_test, x, 1), + false = lists:member(another_code_test, erlang:loaded()), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "another_code_test"), + {ok,another_code_test,Code} = compile:file(File, [binary,report]), + {module,another_code_test} = erlang:load_module(another_code_test, Code), + 42 = ExtFun(answer), ok. get_chunk(Config) when is_list(Config) -> - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "my_code_test"), - ?line {ok,my_code_test,Code} = compile:file(File, [binary]), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "my_code_test"), + {ok,my_code_test,Code} = compile:file(File, [binary]), %% Should work. - ?line Chunk = get_chunk_ok("Atom", Code), - ?line Chunk = get_chunk_ok("Atom", make_sub_binary(Code)), - ?line Chunk = get_chunk_ok("Atom", make_unaligned_sub_binary(Code)), + Chunk = get_chunk_ok("Atom", Code), + Chunk = get_chunk_ok("Atom", make_sub_binary(Code)), + Chunk = get_chunk_ok("Atom", make_unaligned_sub_binary(Code)), %% Should fail. - ?line {'EXIT',{badarg,_}} = (catch code:get_chunk(bit_sized_binary(Code), "Atom")), - ?line {'EXIT',{badarg,_}} = (catch code:get_chunk(Code, "bad chunk id")), + {'EXIT',{badarg,_}} = (catch code:get_chunk(bit_sized_binary(Code), "Atom")), + {'EXIT',{badarg,_}} = (catch code:get_chunk(Code, "bad chunk id")), %% Invalid beam code or missing chunk should return 'undefined'. - ?line undefined = code:get_chunk(<<"not a beam module">>, "Atom"), - ?line undefined = code:get_chunk(Code, "XXXX"), + undefined = code:get_chunk(<<"not a beam module">>, "Atom"), + undefined = code:get_chunk(Code, "XXXX"), ok. get_chunk_ok(Chunk, Code) -> case code:get_chunk(Code, Chunk) of - Bin when is_binary(Bin) -> Bin + Bin when is_binary(Bin) -> Bin end. module_md5(Config) when is_list(Config) -> - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "my_code_test"), - ?line {ok,my_code_test,Code} = compile:file(File, [binary]), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "my_code_test"), + {ok,my_code_test,Code} = compile:file(File, [binary]), %% Should work. - ?line Chunk = module_md5_ok(Code), - ?line Chunk = module_md5_ok(make_sub_binary(Code)), - ?line Chunk = module_md5_ok(make_unaligned_sub_binary(Code)), + Chunk = module_md5_ok(Code), + Chunk = module_md5_ok(make_sub_binary(Code)), + Chunk = module_md5_ok(make_unaligned_sub_binary(Code)), %% Should fail. - ?line {'EXIT',{badarg,_}} = (catch code:module_md5(bit_sized_binary(Code))), + {'EXIT',{badarg,_}} = (catch code:module_md5(bit_sized_binary(Code))), %% Invalid beam code should return 'undefined'. - ?line undefined = code:module_md5(<<"not a beam module">>), + undefined = code:module_md5(<<"not a beam module">>), ok. - + module_md5_ok(Code) -> case code:module_md5(Code) of - Bin when is_binary(Bin), size(Bin) =:= 16 -> Bin + Bin when is_binary(Bin), size(Bin) =:= 16 -> Bin end. @@ -392,106 +382,106 @@ make_stub(Config) when is_list(Config) -> catch erlang:purge_module(my_code_test), MD5 = erlang:md5(<<>>), - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "my_code_test"), - ?line {ok,my_code_test,Code} = compile:file(File, [binary]), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "my_code_test"), + {ok,my_code_test,Code} = compile:file(File, [binary]), - ?line my_code_test = code:make_stub_module(my_code_test, Code, {[],[],MD5}), - ?line true = erlang:delete_module(my_code_test), - ?line true = erlang:purge_module(my_code_test), + my_code_test = code:make_stub_module(my_code_test, Code, {[],[],MD5}), + true = erlang:delete_module(my_code_test), + true = erlang:purge_module(my_code_test), - ?line my_code_test = code:make_stub_module(my_code_test, - make_unaligned_sub_binary(Code), - {[],[],MD5}), - ?line true = erlang:delete_module(my_code_test), - ?line true = erlang:purge_module(my_code_test), + my_code_test = code:make_stub_module(my_code_test, + make_unaligned_sub_binary(Code), + {[],[],MD5}), + true = erlang:delete_module(my_code_test), + true = erlang:purge_module(my_code_test), - ?line my_code_test = code:make_stub_module(my_code_test, zlib:gzip(Code), - {[],[],MD5}), - ?line true = erlang:delete_module(my_code_test), - ?line true = erlang:purge_module(my_code_test), + my_code_test = code:make_stub_module(my_code_test, zlib:gzip(Code), + {[],[],MD5}), + true = erlang:delete_module(my_code_test), + true = erlang:purge_module(my_code_test), %% Should fail. - ?line {'EXIT',{badarg,_}} = - (catch code:make_stub_module(my_code_test, <<"bad">>, {[],[],MD5})), - ?line {'EXIT',{badarg,_}} = - (catch code:make_stub_module(my_code_test, - bit_sized_binary(Code), - {[],[],MD5})), - ?line {'EXIT',{badarg,_}} = - (catch code:make_stub_module(my_code_test_with_wrong_name, - Code, {[],[],MD5})), + {'EXIT',{badarg,_}} = + (catch code:make_stub_module(my_code_test, <<"bad">>, {[],[],MD5})), + {'EXIT',{badarg,_}} = + (catch code:make_stub_module(my_code_test, + bit_sized_binary(Code), + {[],[],MD5})), + {'EXIT',{badarg,_}} = + (catch code:make_stub_module(my_code_test_with_wrong_name, + Code, {[],[],MD5})), ok. make_stub_many_funs(Config) when is_list(Config) -> catch erlang:purge_module(many_funs), MD5 = erlang:md5(<<>>), - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "many_funs"), - ?line {ok,many_funs,Code} = compile:file(File, [binary]), - - ?line many_funs = code:make_stub_module(many_funs, Code, {[],[],MD5}), - ?line true = erlang:delete_module(many_funs), - ?line true = erlang:purge_module(many_funs), - ?line many_funs = code:make_stub_module(many_funs, - make_unaligned_sub_binary(Code), - {[],[],MD5}), - ?line true = erlang:delete_module(many_funs), - ?line true = erlang:purge_module(many_funs), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "many_funs"), + {ok,many_funs,Code} = compile:file(File, [binary]), + + many_funs = code:make_stub_module(many_funs, Code, {[],[],MD5}), + true = erlang:delete_module(many_funs), + true = erlang:purge_module(many_funs), + many_funs = code:make_stub_module(many_funs, + make_unaligned_sub_binary(Code), + {[],[],MD5}), + true = erlang:delete_module(many_funs), + true = erlang:purge_module(many_funs), %% Should fail. - ?line {'EXIT',{badarg,_}} = - (catch code:make_stub_module(many_funs, <<"bad">>, {[],[],MD5})), - ?line {'EXIT',{badarg,_}} = - (catch code:make_stub_module(many_funs, - bit_sized_binary(Code), - {[],[],MD5})), + {'EXIT',{badarg,_}} = + (catch code:make_stub_module(many_funs, <<"bad">>, {[],[],MD5})), + {'EXIT',{badarg,_}} = + (catch code:make_stub_module(many_funs, + bit_sized_binary(Code), + {[],[],MD5})), ok. constant_pools(Config) when is_list(Config) -> - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "literals"), - ?line {ok,literals,Code} = compile:file(File, [report,binary]), - ?line {module,literals} = erlang:load_module(literals, - make_sub_binary(Code)), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "literals"), + {ok,literals,Code} = compile:file(File, [report,binary]), + {module,literals} = erlang:load_module(literals, + make_sub_binary(Code)), %% Initialize. - ?line A = literals:a(), - ?line B = literals:b(), - ?line C = literals:huge_bignum(), - ?line process_flag(trap_exit, true), + A = literals:a(), + B = literals:b(), + C = literals:huge_bignum(), + process_flag(trap_exit, true), Self = self(), %% Have a process WITHOUT old heap that references the literals %% in the 'literals' module. - ?line NoOldHeap = spawn_link(fun() -> no_old_heap(Self) end), + NoOldHeap = spawn_link(fun() -> no_old_heap(Self) end), receive go -> ok end, - ?line true = erlang:delete_module(literals), - ?line false = erlang:check_process_code(NoOldHeap, literals), - ?line erlang:check_process_code(self(), literals), - ?line true = erlang:purge_module(literals), - ?line NoOldHeap ! done, - ?line receive - {'EXIT',NoOldHeap,{A,B,C}} -> - ok; - Other -> - ?line ct:fail({unexpected,Other}) - end, - ?line {module,literals} = erlang:load_module(literals, Code), + true = erlang:delete_module(literals), + false = erlang:check_process_code(NoOldHeap, literals), + erlang:check_process_code(self(), literals), + true = erlang:purge_module(literals), + NoOldHeap ! done, + receive + {'EXIT',NoOldHeap,{A,B,C}} -> + ok; + Other -> + ct:fail({unexpected,Other}) + end, + {module,literals} = erlang:load_module(literals, Code), %% Have a process WITH an old heap that references the literals %% in the 'literals' module. - ?line OldHeap = spawn_link(fun() -> old_heap(Self) end), + OldHeap = spawn_link(fun() -> old_heap(Self) end), receive go -> ok end, - ?line true = erlang:delete_module(literals), - ?line false = erlang:check_process_code(OldHeap, literals), - ?line erlang:check_process_code(self(), literals), - ?line erlang:purge_module(literals), - ?line OldHeap ! done, + true = erlang:delete_module(literals), + false = erlang:check_process_code(OldHeap, literals), + erlang:check_process_code(self(), literals), + erlang:purge_module(literals), + OldHeap ! done, receive - {'EXIT',OldHeap,{A,B,C,[1,2,3|_]=Seq}} when length(Seq) =:= 16 -> - ok + {'EXIT',OldHeap,{A,B,C,[1,2,3|_]=Seq}} when length(Seq) =:= 16 -> + ok end. no_old_heap(Parent) -> @@ -500,8 +490,8 @@ no_old_heap(Parent) -> Res = {A,B,literals:huge_bignum()}, Parent ! go, receive - done -> - exit(Res) + done -> + exit(Res) end. old_heap(Parent) -> @@ -511,16 +501,16 @@ old_heap(Parent) -> create_old_heap(), Parent ! go, receive - done -> - exit(Res) + done -> + exit(Res) end. create_old_heap() -> case process_info(self(), [heap_size,total_heap_size]) of - [{heap_size,Sz},{total_heap_size,Total}] when Sz < Total -> - ok; - _ -> - create_old_heap() + [{heap_size,Sz},{total_heap_size,Total}] when Sz < Total -> + ok; + _ -> + create_old_heap() end. constant_refc_binaries(Config) when is_list(Config) -> @@ -554,29 +544,29 @@ constant_refc_binaries(Config) when is_list(Config) -> io:format("Binary data (bytes) after test: ~p", [Aft]), Diff = Aft - Bef, if - Diff < 0 -> - io:format("~p less bytes", [abs(Diff)]); - Diff > 0 -> - io:format("~p more bytes", [Diff]); - true -> - ok + Diff < 0 -> + io:format("~p less bytes", [abs(Diff)]); + Diff > 0 -> + io:format("~p more bytes", [Diff]); + true -> + ok end, %% Test for leaks. We must accept some natural variations in %% the size of allocated binaries. if - Diff > 64*1024 -> - ct:fail(binary_leak); - true -> - ok + Diff > 64*1024 -> + ct:fail(binary_leak); + true -> + ok end. memory_binary() -> try - erlang:memory(binary) + erlang:memory(binary) catch - error:notsup -> - 0 + error:notsup -> + 0 end. provoke_mem_leak(0, _, _) -> ok; @@ -586,19 +576,19 @@ provoke_mem_leak(N, Code, Check) -> %% Create several processes with references to the literal binary. Self = self(), Pids = [spawn_link(fun() -> - create_binaries(Self, NumRefs, Check) - end) || NumRefs <- lists:seq(1, 10)], + create_binaries(Self, NumRefs, Check) + end) || NumRefs <- lists:seq(1, 10)], [receive {started,Pid} -> ok end || Pid <- Pids], %% Make the code old and remove references to the constant pool %% in all processes. true = erlang:delete_module(literals), Ms = [spawn_monitor(fun() -> - false = erlang:check_process_code(Pid, literals) - end) || Pid <- Pids], + false = erlang:check_process_code(Pid, literals) + end) || Pid <- Pids], [receive - {'DOWN',R,process,P,normal} -> - ok + {'DOWN',R,process,P,normal} -> + ok end || {P,R} <- Ms], %% Purge the code. @@ -606,14 +596,14 @@ provoke_mem_leak(N, Code, Check) -> %% Tell the processes that the code has been purged. [begin - monitor(process, Pid), - Pid ! purged + monitor(process, Pid), + Pid ! purged end || Pid <- Pids], %% Wait for all processes to terminate. [receive - {'DOWN',_,process,Pid,normal} -> - ok + {'DOWN',_,process,Pid,normal} -> + ok end || Pid <- Pids], %% We now expect that the binary has been deallocated. @@ -625,108 +615,108 @@ create_binaries(Parent, NumRefs, Check) -> {bits,Bits} = literals:bits(), Parent ! {started,self()}, receive - purged -> - %% The code has been purged. Now make sure that - %% the binaries haven't been corrupted. - Check = erlang:md5(Bin), - [Bin = B || B <- Bins], - <<42:13,Bin/binary>> = Bits, - - %% Remove all references to the binaries - %% Doing it explicitly like this ensures that - %% the binaries are gone when the parent process - %% receives the 'DOWN' message. - erlang:garbage_collect() + purged -> + %% The code has been purged. Now make sure that + %% the binaries haven't been corrupted. + Check = erlang:md5(Bin), + [Bin = B || B <- Bins], + <<42:13,Bin/binary>> = Bits, + + %% Remove all references to the binaries + %% Doing it explicitly like this ensures that + %% the binaries are gone when the parent process + %% receives the 'DOWN' message. + erlang:garbage_collect() end. wait_for_memory_deallocations() -> try - erts_debug:set_internal_state(wait, deallocations) + erts_debug:set_internal_state(wait, deallocations) catch - error:undef -> - erts_debug:set_internal_state(available_internal_state, true), - wait_for_memory_deallocations() + error:undef -> + erts_debug:set_internal_state(available_internal_state, true), + wait_for_memory_deallocations() end. %% OTP-7559: c_p->cp could contain garbage and create a false dependency %% to a module in a process. (Thanks to Richard Carlsson.) false_dependency(Config) when is_list(Config) -> - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "cpbugx"), - ?line {ok,cpbugx,Code} = compile:file(File, [binary,report]), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "cpbugx"), + {ok,cpbugx,Code} = compile:file(File, [binary,report]), do_false_dependency(fun cpbugx:before/0, Code), do_false_dependency(fun cpbugx:before2/0, Code), do_false_dependency(fun cpbugx:before3/0, Code), -%% %% Spawn process. Make sure it has called cpbugx:before/0 and returned. -%% Parent = self(), -%% ?line Pid = spawn_link(fun() -> false_dependency_loop(Parent) end), -%% ?line receive initialized -> ok end, + %% %% Spawn process. Make sure it has called cpbugx:before/0 and returned. + %% Parent = self(), + %% Pid = spawn_link(fun() -> false_dependency_loop(Parent) end), + %% receive initialized -> ok end, -%% %% Reload the module. Make sure the process is still alive. -%% ?line {module,cpbugx} = erlang:load_module(cpbugx, Bin), -%% ?line io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))), -%% ?line true = is_process_alive(Pid), + %% %% Reload the module. Make sure the process is still alive. + %% {module,cpbugx} = erlang:load_module(cpbugx, Bin), + %% io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))), + %% true = is_process_alive(Pid), -%% %% There should not be any dependency to cpbugx. -%% ?line false = erlang:check_process_code(Pid, cpbugx), - + %% %% There should not be any dependency to cpbugx. + %% false = erlang:check_process_code(Pid, cpbugx), -%% %% Kill the process. -%% ?line unlink(Pid), exit(Pid, kill), + + %% %% Kill the process. + %% unlink(Pid), exit(Pid, kill), ok. do_false_dependency(Init, Code) -> - ?line {module,cpbugx} = erlang:load_module(cpbugx, Code), + {module,cpbugx} = erlang:load_module(cpbugx, Code), %% Spawn process. Make sure it has the appropriate init function %% and returned. CP should not contain garbage after the return. Parent = self(), - ?line Pid = spawn_link(fun() -> false_dependency_loop(Parent, Init, true) end), - ?line receive initialized -> ok end, + Pid = spawn_link(fun() -> false_dependency_loop(Parent, Init, true) end), + receive initialized -> ok end, %% Reload the module. Make sure the process is still alive. - ?line {module,cpbugx} = erlang:load_module(cpbugx, Code), - ?line io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))), - ?line true = is_process_alive(Pid), + {module,cpbugx} = erlang:load_module(cpbugx, Code), + io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))), + true = is_process_alive(Pid), %% There should not be any dependency to cpbugx. - ?line false = erlang:check_process_code(Pid, cpbugx), + false = erlang:check_process_code(Pid, cpbugx), %% Kill the process and completely unload the code. - ?line unlink(Pid), exit(Pid, kill), - ?line true = erlang:purge_module(cpbugx), - ?line true = erlang:delete_module(cpbugx), - ?line code:is_module_native(cpbugx), % test is_module_native on deleted code - ?line true = erlang:purge_module(cpbugx), - ?line code:is_module_native(cpbugx), % test is_module_native on purged code + unlink(Pid), exit(Pid, kill), + true = erlang:purge_module(cpbugx), + true = erlang:delete_module(cpbugx), + code:is_module_native(cpbugx), % test is_module_native on deleted code + true = erlang:purge_module(cpbugx), + code:is_module_native(cpbugx), % test is_module_native on purged code ok. - + false_dependency_loop(Parent, Init, SendInitAck) -> Init(), case SendInitAck of - true -> Parent ! initialized; - false -> void - %% Just send one init-ack. I guess the point of this test - %% wasn't to fill parents msg-queue (?). Seen to cause - %% out-of-mem (on halfword-vm for some reason) by - %% 91 million msg in queue. /sverker + true -> Parent ! initialized; + false -> void + %% Just send one init-ack. I guess the point of this test + %% wasn't to fill parents msg-queue (?). Seen to cause + %% out-of-mem (on halfword-vm for some reason) by + %% 91 million msg in queue. /sverker end, receive - _ -> false_dependency_loop(Parent, Init, false) + _ -> false_dependency_loop(Parent, Init, false) end. coverage(Config) when is_list(Config) -> - ?line code:is_module_native(?MODULE), - ?line {'EXIT',{badarg,_}} = (catch erlang:purge_module({a,b,c})), - ?line {'EXIT',{badarg,_}} = (catch code:is_module_native({a,b,c})), - ?line {'EXIT',{badarg,_}} = (catch erlang:check_process_code(not_a_pid, ?MODULE)), - ?line {'EXIT',{badarg,_}} = (catch erlang:check_process_code(self(), [not_a_module])), - ?line {'EXIT',{badarg,_}} = (catch erlang:delete_module([a,b,c])), - ?line {'EXIT',{badarg,_}} = (catch erlang:module_loaded(42)), + code:is_module_native(?MODULE), + {'EXIT',{badarg,_}} = (catch erlang:purge_module({a,b,c})), + {'EXIT',{badarg,_}} = (catch code:is_module_native({a,b,c})), + {'EXIT',{badarg,_}} = (catch erlang:check_process_code(not_a_pid, ?MODULE)), + {'EXIT',{badarg,_}} = (catch erlang:check_process_code(self(), [not_a_module])), + {'EXIT',{badarg,_}} = (catch erlang:delete_module([a,b,c])), + {'EXIT',{badarg,_}} = (catch erlang:module_loaded(42)), ok. fun_confusion(Config) when is_list(Config) -> diff --git a/erts/emulator/test/crypto_SUITE.erl b/erts/emulator/test/crypto_SUITE.erl index 8ff77b3880..6212997272 100644 --- a/erts/emulator/test/crypto_SUITE.erl +++ b/erts/emulator/test/crypto_SUITE.erl @@ -23,8 +23,8 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, - t_md5/1,t_md5_update/1,error/1,unaligned_context/1,random_lists/1, - misc_errors/1]). + t_md5/1,t_md5_update/1,error/1,unaligned_context/1,random_lists/1, + misc_errors/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -36,28 +36,28 @@ all() -> %% Test crc32, adler32 and md5 error cases not covered by other tests" misc_errors(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), - ?line 1 = erlang:adler32([]), - ?line L = lists:duplicate(600,3), - ?line 1135871753 = erlang:adler32(L), - ?line L2 = lists:duplicate(22000,3), - ?line 1100939744 = erlang:adler32(L2), - ?line {'EXIT', {badarg,_}} = (catch erlang:adler32(L++[a])), - ?line {'EXIT', {badarg,_}} = (catch erlang:crc32(L++[a])), - ?line {'EXIT', {badarg,_}} = (catch erlang:crc32([1,2,3|<<25:7>>])), - ?line {'EXIT', {badarg,_}} = (catch erlang:crc32([1,2,3|4])), - ?line Big = 111111111111111111111111111111, - ?line {'EXIT', {badarg,_}} = (catch erlang:crc32(Big,<<"hej">>)), - ?line {'EXIT', {badarg,_}} = (catch erlang:crc32(25,[1,2,3|4])), - ?line {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(Big,3,3)), - ?line {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(3,Big,3)), - ?line {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(3,3,Big)), - ?line {'EXIT', {badarg,_}} = (catch erlang:adler32(Big,<<"hej">>)), - ?line {'EXIT', {badarg,_}} = (catch erlang:adler32(25,[1,2,3|4])), - ?line {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(Big,3,3)), - ?line {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(3,Big,3)), - ?line {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(3,3,Big)), - ?line {'EXIT', {badarg,_}} = (catch erlang:md5_update(<<"hej">>,<<"hej">>)), - ?line {'EXIT', {badarg,_}} = (catch erlang:md5_final(<<"hej">>)), + 1 = erlang:adler32([]), + L = lists:duplicate(600,3), + 1135871753 = erlang:adler32(L), + L2 = lists:duplicate(22000,3), + 1100939744 = erlang:adler32(L2), + {'EXIT', {badarg,_}} = (catch erlang:adler32(L++[a])), + {'EXIT', {badarg,_}} = (catch erlang:crc32(L++[a])), + {'EXIT', {badarg,_}} = (catch erlang:crc32([1,2,3|<<25:7>>])), + {'EXIT', {badarg,_}} = (catch erlang:crc32([1,2,3|4])), + Big = 111111111111111111111111111111, + {'EXIT', {badarg,_}} = (catch erlang:crc32(Big,<<"hej">>)), + {'EXIT', {badarg,_}} = (catch erlang:crc32(25,[1,2,3|4])), + {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(Big,3,3)), + {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(3,Big,3)), + {'EXIT', {badarg,_}} = (catch erlang:crc32_combine(3,3,Big)), + {'EXIT', {badarg,_}} = (catch erlang:adler32(Big,<<"hej">>)), + {'EXIT', {badarg,_}} = (catch erlang:adler32(25,[1,2,3|4])), + {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(Big,3,3)), + {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(3,Big,3)), + {'EXIT', {badarg,_}} = (catch erlang:adler32_combine(3,3,Big)), + {'EXIT', {badarg,_}} = (catch erlang:md5_update(<<"hej">>,<<"hej">>)), + {'EXIT', {badarg,_}} = (catch erlang:md5_final(<<"hej">>)), ok. @@ -72,7 +72,7 @@ nicesplit(N,L) -> nicesplit(0,Tail,Acc) -> {lists:reverse(Acc),Tail}; nicesplit(_,[],Acc) -> - {lists:reverse(Acc),[]}; + {lists:reverse(Acc),[]}; nicesplit(N,[H|Tail],Acc) -> nicesplit(N-1,Tail,[H|Acc]). @@ -81,17 +81,17 @@ run_in_para([],_) -> run_in_para(FunList,Schedulers) -> {ThisTime,NextTime} = nicesplit(Schedulers,FunList), case length(ThisTime) of - 1 -> - [{L,Fun}] = ThisTime, - try - Fun() + 1 -> + [{L,Fun}] = ThisTime, + try + Fun() catch - _:Reason -> - exit({error_at_line,L,Reason}) - end; + _:Reason -> + exit({error_at_line,L,Reason}) + end; _ -> - These = [ {L,erlang:spawn_monitor(F)} || {L,F} <- ThisTime ], - collect_workers(These) + These = [ {L,erlang:spawn_monitor(F)} || {L,F} <- ThisTime ], + collect_workers(These) end, run_in_para(NextTime,Schedulers). @@ -99,147 +99,147 @@ collect_workers([]) -> ok; collect_workers([{L,{Pid,Ref}}|T]) -> receive - {'DOWN',Ref,process,Pid,normal} -> - collect_workers(T); - {'DOWN',Ref,process,Pid,Other} -> - exit({error_at_line,L,Other}) + {'DOWN',Ref,process,Pid,normal} -> + collect_workers(T); + {'DOWN',Ref,process,Pid,Other} -> + exit({error_at_line,L,Other}) end. %% Test crc32, adler32 and md5 on a number of pseudo-randomly generated lists. random_lists(Config) when is_list(Config) -> ct:timetrap({minutes, 5}), - ?line Num = erlang:system_info(schedulers_online), - ?line B = list_to_binary( - lists:duplicate( - (erlang:system_info(context_reductions)*10) - 50,$!)), - ?line CRC32_1 = fun(L) -> erlang:crc32(L) end, - ?line CRC32_2 = fun(L) -> ?REF:crc32(L) end, - ?line ADLER32_1 = fun(L) -> erlang:adler32(L) end, - ?line ADLER32_2 = fun(L) -> ?REF:adler32(L) end, - ?line MD5_1 = fun(L) -> erlang:md5(L) end, - ?line MD5_2 = fun(L) -> ?REF:md5_final( - ?REF:md5_update(?REF:md5_init(),L)) end, - ?line MD5_3 = fun(L) -> erlang:md5_final( - erlang:md5_update(erlang:md5_init(),L)) end, - ?line CRC32_1_L = fun(L) -> erlang:crc32([B|L]) end, - ?line CRC32_2_L = fun(L) -> ?REF:crc32([B|L]) end, - ?line ADLER32_1_L = fun(L) -> erlang:adler32([B|L]) end, - ?line ADLER32_2_L = fun(L) -> ?REF:adler32([B|L]) end, - ?line MD5_1_L = fun(L) -> erlang:md5([B|L]) end, - ?line MD5_2_L = fun(L) -> ?REF:md5_final( - ?REF:md5_update(?REF:md5_init(),[B|L])) end, - ?line MD5_3_L = fun(L) -> erlang:md5_final( - erlang:md5_update( - erlang:md5_init(),[B|L])) end, - ?line Wlist0 = - [{?LINE, fun() -> random_iolist:run(150, CRC32_1, CRC32_2) end}, - {?LINE, fun() -> random_iolist:run(150, ADLER32_1, ADLER32_2) end}, - {?LINE, fun() -> random_iolist:run(150,MD5_1,MD5_2) end}, - {?LINE, fun() -> random_iolist:run(150,MD5_1,MD5_3) end}, - {?LINE, fun() -> random_iolist:run(150, CRC32_1_L, CRC32_2_L) end}, - {?LINE, - fun() -> random_iolist:run(150, ADLER32_1_L, ADLER32_2_L) end}, - {?LINE, fun() -> random_iolist:run(150,MD5_1_L,MD5_2_L) end}, - {?LINE, fun() -> random_iolist:run(150,MD5_1_L,MD5_3_L) end}], - ?line run_in_para(Wlist0,Num), - ?line CRC32_1_2 = fun(L1,L2) -> erlang:crc32([L1,L2]) end, - ?line CRC32_2_2 = fun(L1,L2) -> erlang:crc32(erlang:crc32(L1),L2) end, - ?line CRC32_3_2 = fun(L1,L2) -> erlang:crc32_combine( - erlang:crc32(L1), - erlang:crc32(L2), - erlang:iolist_size(L2)) - end, - ?line ADLER32_1_2 = fun(L1,L2) -> erlang:adler32([L1,L2]) end, - ?line ADLER32_2_2 = fun(L1,L2) -> erlang:adler32( - erlang:adler32(L1),L2) end, - ?line ADLER32_3_2 = fun(L1,L2) -> erlang:adler32_combine( - erlang:adler32(L1), - erlang:adler32(L2), - erlang:iolist_size(L2)) - end, - ?line MD5_1_2 = fun(L1,L2) -> erlang:md5([L1,L2]) end, - ?line MD5_2_2 = fun(L1,L2) -> - erlang:md5_final( - erlang:md5_update( - erlang:md5_update( - erlang:md5_init(), - L1), - L2)) - end, - ?line CRC32_1_L_2 = fun(L1,L2) -> erlang:crc32([[B|L1],[B|L2]]) end, - ?line CRC32_2_L_2 = fun(L1,L2) -> erlang:crc32( - erlang:crc32([B|L1]),[B|L2]) end, - ?line CRC32_3_L_2 = fun(L1,L2) -> erlang:crc32_combine( - erlang:crc32([B|L1]), - erlang:crc32([B|L2]), - erlang:iolist_size([B|L2])) - end, - ?line ADLER32_1_L_2 = fun(L1,L2) -> erlang:adler32([[B|L1],[B|L2]]) end, - ?line ADLER32_2_L_2 = fun(L1,L2) -> erlang:adler32( - erlang:adler32([B|L1]), - [B|L2]) - end, - ?line ADLER32_3_L_2 = fun(L1,L2) -> erlang:adler32_combine( - erlang:adler32([B|L1]), - erlang:adler32([B|L2]), - erlang:iolist_size([B|L2])) - end, - ?line MD5_1_L_2 = fun(L1,L2) -> erlang:md5([[B|L1],[B|L2]]) end, - ?line MD5_2_L_2 = fun(L1,L2) -> - erlang:md5_final( - erlang:md5_update( - erlang:md5_update( - erlang:md5_init(), - [B|L1]), - [B|L2])) - end, - ?line Wlist1 = - [{?LINE, fun() -> random_iolist:run2(150,CRC32_1_2,CRC32_2_2) end}, - {?LINE, fun() -> random_iolist:run2(150,CRC32_1_2,CRC32_3_2) end}, - {?LINE, fun() -> random_iolist:run2(150,ADLER32_1_2,ADLER32_2_2) end}, - {?LINE, fun() -> random_iolist:run2(150,ADLER32_1_2,ADLER32_3_2) end}, - {?LINE, fun() -> random_iolist:run2(150,MD5_1_2,MD5_2_2) end}, - {?LINE, fun() -> random_iolist:run2(150,CRC32_1_L_2,CRC32_2_L_2) end}, - {?LINE, fun() -> random_iolist:run2(150,CRC32_1_L_2,CRC32_3_L_2) end}, - {?LINE, - fun() -> random_iolist:run2(150,ADLER32_1_L_2,ADLER32_2_L_2) end}, - {?LINE, - fun() -> random_iolist:run2(150,ADLER32_1_L_2,ADLER32_3_L_2) end}, - {?LINE, fun() -> random_iolist:run2(150,MD5_1_L_2,MD5_2_L_2) end}], - ?line run_in_para(Wlist1,Num), + Num = erlang:system_info(schedulers_online), + B = list_to_binary( + lists:duplicate( + (erlang:system_info(context_reductions)*10) - 50,$!)), + CRC32_1 = fun(L) -> erlang:crc32(L) end, + CRC32_2 = fun(L) -> ?REF:crc32(L) end, + ADLER32_1 = fun(L) -> erlang:adler32(L) end, + ADLER32_2 = fun(L) -> ?REF:adler32(L) end, + MD5_1 = fun(L) -> erlang:md5(L) end, + MD5_2 = fun(L) -> ?REF:md5_final( + ?REF:md5_update(?REF:md5_init(),L)) end, + MD5_3 = fun(L) -> erlang:md5_final( + erlang:md5_update(erlang:md5_init(),L)) end, + CRC32_1_L = fun(L) -> erlang:crc32([B|L]) end, + CRC32_2_L = fun(L) -> ?REF:crc32([B|L]) end, + ADLER32_1_L = fun(L) -> erlang:adler32([B|L]) end, + ADLER32_2_L = fun(L) -> ?REF:adler32([B|L]) end, + MD5_1_L = fun(L) -> erlang:md5([B|L]) end, + MD5_2_L = fun(L) -> ?REF:md5_final( + ?REF:md5_update(?REF:md5_init(),[B|L])) end, + MD5_3_L = fun(L) -> erlang:md5_final( + erlang:md5_update( + erlang:md5_init(),[B|L])) end, + Wlist0 = + [{?LINE, fun() -> random_iolist:run(150, CRC32_1, CRC32_2) end}, + {?LINE, fun() -> random_iolist:run(150, ADLER32_1, ADLER32_2) end}, + {?LINE, fun() -> random_iolist:run(150,MD5_1,MD5_2) end}, + {?LINE, fun() -> random_iolist:run(150,MD5_1,MD5_3) end}, + {?LINE, fun() -> random_iolist:run(150, CRC32_1_L, CRC32_2_L) end}, + {?LINE, + fun() -> random_iolist:run(150, ADLER32_1_L, ADLER32_2_L) end}, + {?LINE, fun() -> random_iolist:run(150,MD5_1_L,MD5_2_L) end}, + {?LINE, fun() -> random_iolist:run(150,MD5_1_L,MD5_3_L) end}], + run_in_para(Wlist0,Num), + CRC32_1_2 = fun(L1,L2) -> erlang:crc32([L1,L2]) end, + CRC32_2_2 = fun(L1,L2) -> erlang:crc32(erlang:crc32(L1),L2) end, + CRC32_3_2 = fun(L1,L2) -> erlang:crc32_combine( + erlang:crc32(L1), + erlang:crc32(L2), + erlang:iolist_size(L2)) + end, + ADLER32_1_2 = fun(L1,L2) -> erlang:adler32([L1,L2]) end, + ADLER32_2_2 = fun(L1,L2) -> erlang:adler32( + erlang:adler32(L1),L2) end, + ADLER32_3_2 = fun(L1,L2) -> erlang:adler32_combine( + erlang:adler32(L1), + erlang:adler32(L2), + erlang:iolist_size(L2)) + end, + MD5_1_2 = fun(L1,L2) -> erlang:md5([L1,L2]) end, + MD5_2_2 = fun(L1,L2) -> + erlang:md5_final( + erlang:md5_update( + erlang:md5_update( + erlang:md5_init(), + L1), + L2)) + end, + CRC32_1_L_2 = fun(L1,L2) -> erlang:crc32([[B|L1],[B|L2]]) end, + CRC32_2_L_2 = fun(L1,L2) -> erlang:crc32( + erlang:crc32([B|L1]),[B|L2]) end, + CRC32_3_L_2 = fun(L1,L2) -> erlang:crc32_combine( + erlang:crc32([B|L1]), + erlang:crc32([B|L2]), + erlang:iolist_size([B|L2])) + end, + ADLER32_1_L_2 = fun(L1,L2) -> erlang:adler32([[B|L1],[B|L2]]) end, + ADLER32_2_L_2 = fun(L1,L2) -> erlang:adler32( + erlang:adler32([B|L1]), + [B|L2]) + end, + ADLER32_3_L_2 = fun(L1,L2) -> erlang:adler32_combine( + erlang:adler32([B|L1]), + erlang:adler32([B|L2]), + erlang:iolist_size([B|L2])) + end, + MD5_1_L_2 = fun(L1,L2) -> erlang:md5([[B|L1],[B|L2]]) end, + MD5_2_L_2 = fun(L1,L2) -> + erlang:md5_final( + erlang:md5_update( + erlang:md5_update( + erlang:md5_init(), + [B|L1]), + [B|L2])) + end, + Wlist1 = + [{?LINE, fun() -> random_iolist:run2(150,CRC32_1_2,CRC32_2_2) end}, + {?LINE, fun() -> random_iolist:run2(150,CRC32_1_2,CRC32_3_2) end}, + {?LINE, fun() -> random_iolist:run2(150,ADLER32_1_2,ADLER32_2_2) end}, + {?LINE, fun() -> random_iolist:run2(150,ADLER32_1_2,ADLER32_3_2) end}, + {?LINE, fun() -> random_iolist:run2(150,MD5_1_2,MD5_2_2) end}, + {?LINE, fun() -> random_iolist:run2(150,CRC32_1_L_2,CRC32_2_L_2) end}, + {?LINE, fun() -> random_iolist:run2(150,CRC32_1_L_2,CRC32_3_L_2) end}, + {?LINE, + fun() -> random_iolist:run2(150,ADLER32_1_L_2,ADLER32_2_L_2) end}, + {?LINE, + fun() -> random_iolist:run2(150,ADLER32_1_L_2,ADLER32_3_L_2) end}, + {?LINE, fun() -> random_iolist:run2(150,MD5_1_L_2,MD5_2_L_2) end}], + run_in_para(Wlist1,Num), ok. %% Generate MD5 message digests and check the result. Examples are from RFC-1321. t_md5(Config) when is_list(Config) -> - ?line t_md5_test("", "d41d8cd98f00b204e9800998ecf8427e"), - ?line t_md5_test("a", "0cc175b9c0f1b6a831c399e269772661"), - ?line t_md5_test("abc", "900150983cd24fb0d6963f7d28e17f72"), - ?line t_md5_test(["message ","digest"], "f96b697d7cb7938d525a2f31aaf161d0"), - ?line t_md5_test(["message ",unaligned_sub_bin(<<"digest">>)], - "f96b697d7cb7938d525a2f31aaf161d0"), - ?line t_md5_test("abcdefghijklmnopqrstuvwxyz", - "c3fcd3d76192e4007dfb496cca67e13b"), - ?line t_md5_test("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789", - "d174ab98d277d9f5a5611c2c9f419d9f"), - ?line t_md5_test("12345678901234567890123456789012345678901234567890" - "123456789012345678901234567890", - "57edf4a22be3c955ac49da2e2107b67a"), + t_md5_test("", "d41d8cd98f00b204e9800998ecf8427e"), + t_md5_test("a", "0cc175b9c0f1b6a831c399e269772661"), + t_md5_test("abc", "900150983cd24fb0d6963f7d28e17f72"), + t_md5_test(["message ","digest"], "f96b697d7cb7938d525a2f31aaf161d0"), + t_md5_test(["message ",unaligned_sub_bin(<<"digest">>)], + "f96b697d7cb7938d525a2f31aaf161d0"), + t_md5_test("abcdefghijklmnopqrstuvwxyz", + "c3fcd3d76192e4007dfb496cca67e13b"), + t_md5_test("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789", + "d174ab98d277d9f5a5611c2c9f419d9f"), + t_md5_test("12345678901234567890123456789012345678901234567890" + "123456789012345678901234567890", + "57edf4a22be3c955ac49da2e2107b67a"), ok. %% Generate MD5 message using md5_init, md5_update, and md5_final, and %% check the result. Examples are from RFC-1321. t_md5_update(Config) when is_list(Config) -> - ?line t_md5_update_1(fun(Str) -> Str end), - ?line t_md5_update_1(fun(Str) -> list_to_binary(Str) end), - ?line t_md5_update_1(fun(Str) -> unaligned_sub_bin(list_to_binary(Str)) end), + t_md5_update_1(fun(Str) -> Str end), + t_md5_update_1(fun(Str) -> list_to_binary(Str) end), + t_md5_update_1(fun(Str) -> unaligned_sub_bin(list_to_binary(Str)) end), ok. t_md5_update_1(Tr) when is_function(Tr, 1) -> Ctx = erlang:md5_init(), Ctx1 = erlang:md5_update(Ctx, Tr("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), Ctx2 = erlang:md5_update(Ctx1, Tr("abcdefghijklmnopqrstuvwxyz" - "0123456789")), + "0123456789")), m(erlang:md5_final(Ctx2), hexstr2bin("d174ab98d277d9f5a5611c2c9f419d9f")), ok. @@ -247,28 +247,28 @@ t_md5_update_1(Tr) when is_function(Tr, 1) -> %% %% error(Config) when is_list(Config) -> - ?line {'EXIT',{badarg,_}} = (catch erlang:md5(bit_sized_binary(<<"abc">>))), - ?line Ctx0 = erlang:md5_init(), - ?line {'EXIT',{badarg,_}} = - (catch erlang:md5_update(Ctx0, bit_sized_binary(<<"abcfjldjd">>))), - ?line {'EXIT',{badarg,_}} = - (catch erlang:md5_update(Ctx0, ["something",bit_sized_binary(<<"abcfjldjd">>)])), - ?line {'EXIT',{badarg,_}} = - (catch erlang:md5_update(bit_sized_binary(Ctx0), "something")), - ?line {'EXIT',{badarg,_}} = (catch erlang:md5_final(bit_sized_binary(Ctx0))), - ?line m(erlang:md5_final(Ctx0), hexstr2bin("d41d8cd98f00b204e9800998ecf8427e")), + {'EXIT',{badarg,_}} = (catch erlang:md5(bit_sized_binary(<<"abc">>))), + Ctx0 = erlang:md5_init(), + {'EXIT',{badarg,_}} = + (catch erlang:md5_update(Ctx0, bit_sized_binary(<<"abcfjldjd">>))), + {'EXIT',{badarg,_}} = + (catch erlang:md5_update(Ctx0, ["something",bit_sized_binary(<<"abcfjldjd">>)])), + {'EXIT',{badarg,_}} = + (catch erlang:md5_update(bit_sized_binary(Ctx0), "something")), + {'EXIT',{badarg,_}} = (catch erlang:md5_final(bit_sized_binary(Ctx0))), + m(erlang:md5_final(Ctx0), hexstr2bin("d41d8cd98f00b204e9800998ecf8427e")), ok. %% %% unaligned_context(Config) when is_list(Config) -> - ?line Ctx0 = erlang:md5_init(), - ?line Ctx1 = erlang:md5_update(unaligned_sub_bin(Ctx0), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"), - ?line Ctx = erlang:md5_update(unaligned_sub_bin(Ctx1), - "abcdefghijklmnopqrstuvwxyz0123456789"), - ?line m(erlang:md5_final(unaligned_sub_bin(Ctx)), - hexstr2bin("d174ab98d277d9f5a5611c2c9f419d9f")), + Ctx0 = erlang:md5_init(), + Ctx1 = erlang:md5_update(unaligned_sub_bin(Ctx0), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"), + Ctx = erlang:md5_update(unaligned_sub_bin(Ctx1), + "abcdefghijklmnopqrstuvwxyz0123456789"), + m(erlang:md5_final(unaligned_sub_bin(Ctx)), + hexstr2bin("d174ab98d277d9f5a5611c2c9f419d9f")), ok. %% @@ -314,5 +314,3 @@ bit_sized_binary(Bin0) -> Bin. id(I) -> I. - - diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl index 54791bfa45..6982178827 100644 --- a/erts/emulator/test/ddll_SUITE.erl +++ b/erts/emulator/test/ddll_SUITE.erl @@ -33,18 +33,18 @@ -export([all/0, suite/0, ddll_test/1, errors/1, reference_count/1, - kill_port/1, dont_kill_port/1]). + kill_port/1, dont_kill_port/1]). -export([unload_on_process_exit/1, delayed_unload_with_ports/1, - unload_due_to_process_exit/1, - no_unload_due_to_process_exit/1, no_unload_due_to_process_exit_2/1, - unload_reload_thingie/1, unload_reload_thingie_2/1, - unload_reload_thingie_3/1, reload_pending/1, reload_pending_kill/1, - load_fail_init/1, - reload_pending_fail_init/1, - more_error_codes/1, forced_port_killing/1, - no_trap_exit_and_kill_ports/1, - monitor_demonitor/1, monitor_demonitor_load/1, new_interface/1, - lock_driver/1]). + unload_due_to_process_exit/1, + no_unload_due_to_process_exit/1, no_unload_due_to_process_exit_2/1, + unload_reload_thingie/1, unload_reload_thingie_2/1, + unload_reload_thingie_3/1, reload_pending/1, reload_pending_kill/1, + load_fail_init/1, + reload_pending_fail_init/1, + more_error_codes/1, forced_port_killing/1, + no_trap_exit_and_kill_ports/1, + monitor_demonitor/1, monitor_demonitor_load/1, new_interface/1, + lock_driver/1]). % Private exports -export([echo_loader/2, nice_echo_loader/2 ,properties/1, load_and_unload/1]). @@ -73,927 +73,929 @@ all() -> %% Check that the driver is unloaded on process exit unload_on_process_exit(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), + Path = proplists:get_value(data_dir, Config), + false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), Parent = self(), - ?line Pid = spawn(fun() -> - receive go -> ok end, - erl_ddll:try_load(Path, echo_drv, []), - Parent ! gone, - receive go -> ok end, - erl_ddll:loaded_drivers(), - exit(banan) - end), - ?line Ref = erlang:monitor(process,Pid), - ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), + Pid = spawn(fun() -> + receive go -> ok end, + erl_ddll:try_load(Path, echo_drv, []), + Parent ! gone, + receive go -> ok end, + erl_ddll:loaded_drivers(), + exit(banan) + end), + Ref = erlang:monitor(process,Pid), + false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), Pid ! go, - ?line receive - gone -> ok + receive + gone -> ok end, - ?line true = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), + true = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), Pid ! go, - ?line receive - {'DOWN', Ref, process, Pid, banan} -> - ok + receive + {'DOWN', Ref, process, Pid, banan} -> + ok end, receive after 500 -> ok end, - ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), + false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), ok. %% Check that the driver is unloaded when the last port is closed delayed_unload_with_ports(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line erl_ddll:try_load(Path, echo_drv, []), - ?line erl_ddll:try_load(Path, echo_drv, []), - ?line Port = open_port({spawn, echo_drv}, [eof]), - ?line 1 = erl_ddll:info(echo_drv, port_count), - ?line Port2 = open_port({spawn, echo_drv}, [eof]), - ?line 2 = erl_ddll:info(echo_drv, port_count), - ?line {ok,pending_process} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]), - ?line {ok,pending_driver,Ref} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]), - ?line ok = receive _ -> false after 0 -> ok end, - ?line Port ! {self(), close}, - ?line ok = receive {Port,closed} -> ok after 1000 -> false end, - ?line 1 = erl_ddll:info(echo_drv, port_count), - ?line Port2 ! {self(), close}, - ?line ok = receive {Port2,closed} -> ok after 1000 -> false end, - ?line ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 1000 -> false end, + Path = proplists:get_value(data_dir, Config), + erl_ddll:try_load(Path, echo_drv, []), + erl_ddll:try_load(Path, echo_drv, []), + Port = open_port({spawn, echo_drv}, [eof]), + 1 = erl_ddll:info(echo_drv, port_count), + Port2 = open_port({spawn, echo_drv}, [eof]), + 2 = erl_ddll:info(echo_drv, port_count), + {ok,pending_process} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]), + {ok,pending_driver,Ref} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]), + ok = receive _ -> false after 0 -> ok end, + Port ! {self(), close}, + ok = receive {Port,closed} -> ok after 1000 -> false end, + 1 = erl_ddll:info(echo_drv, port_count), + Port2 ! {self(), close}, + ok = receive {Port2,closed} -> ok after 1000 -> false end, + ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 1000 -> false end, ok. %% Check that the driver with ports is unloaded on process exit unload_due_to_process_exit(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line Parent = self(), - ?line F3 = fun() -> - Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), - receive X -> Parent ! {got,X} end - end, - ?line Pid = spawn(fun() -> - receive go -> ok end, - {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), - spawn(F3), - receive go -> ok end, - _Port = open_port({spawn, echo_drv}, [eof]), - _Port2 = open_port({spawn, echo_drv}, [eof]), - exit(banan) - end), - ?line Ref = erlang:monitor(process,Pid), + Path = proplists:get_value(data_dir, Config), + Parent = self(), + F3 = fun() -> + Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), + receive X -> Parent ! {got,X} end + end, + Pid = spawn(fun() -> + receive go -> ok end, + {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), + spawn(F3), + receive go -> ok end, + _Port = open_port({spawn, echo_drv}, [eof]), + _Port2 = open_port({spawn, echo_drv}, [eof]), + exit(banan) + end), + Ref = erlang:monitor(process,Pid), Pid ! go, - ?line {ok,Ref2} = receive - R when is_reference(R) -> {ok,R}; - Other -> {error, Other} - after 500 -> {error, timeout} - end, + {ok,Ref2} = receive + R when is_reference(R) -> {ok,R}; + Other -> {error, Other} + after 500 -> {error, timeout} + end, Pid ! go, - ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, - ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, + ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, + ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, ok. %% Check that a driver with driver loaded in another process is not unloaded on process exit no_unload_due_to_process_exit(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line Parent = self(), - ?line F3 = fun() -> - Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), - receive X -> Parent ! {got,X} end - end, - ?line Pid = spawn(fun() -> - receive go -> ok end, - {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), - spawn(F3), - receive go -> ok end, - _Port = open_port({spawn, echo_drv}, [eof]), - _Port2 = open_port({spawn, echo_drv}, [eof]), - exit(banan) - end), - ?line Ref = erlang:monitor(process,Pid), + Path = proplists:get_value(data_dir, Config), + Parent = self(), + F3 = fun() -> + Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), + receive X -> Parent ! {got,X} end + end, + Pid = spawn(fun() -> + receive go -> ok end, + {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), + spawn(F3), + receive go -> ok end, + _Port = open_port({spawn, echo_drv}, [eof]), + _Port2 = open_port({spawn, echo_drv}, [eof]), + exit(banan) + end), + Ref = erlang:monitor(process,Pid), Pid ! go, - ?line {ok,Ref2} = receive - R when is_reference(R) -> {ok,R}; - Other -> {error, Other} - after 500 -> {error, timeout} - end, - ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []), + {ok,Ref2} = receive + R when is_reference(R) -> {ok,R}; + Other -> {error, Other} + after 500 -> {error, timeout} + end, + {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []), Pid ! go, - ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, - ?line ok = receive X -> {error, X} after 300 -> ok end, - ?line ok = unload_expect_fast(echo_drv,[]), - ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, + ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, + ok = receive X -> {error, X} after 300 -> ok end, + ok = unload_expect_fast(echo_drv,[]), + ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, ok. %% Check that a driver with open ports in another process is not unloaded on process exit no_unload_due_to_process_exit_2(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line Parent = self(), - ?line F3 = fun() -> - Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), - receive X -> Parent ! {got,X} end - end, - ?line Pid = spawn(fun() -> - receive go -> ok end, - {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), - spawn(F3), - receive go -> ok end, - _Port = open_port({spawn, echo_drv}, [eof]), - _Port2 = open_port({spawn, echo_drv}, [eof]), - exit(banan) - end), - ?line Ref = erlang:monitor(process,Pid), + Path = proplists:get_value(data_dir, Config), + Parent = self(), + F3 = fun() -> + Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), + receive X -> Parent ! {got,X} end + end, + Pid = spawn(fun() -> + receive go -> ok end, + {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), + spawn(F3), + receive go -> ok end, + _Port = open_port({spawn, echo_drv}, [eof]), + _Port2 = open_port({spawn, echo_drv}, [eof]), + exit(banan) + end), + Ref = erlang:monitor(process,Pid), Pid ! go, - ?line {ok,Ref2} = receive - R when is_reference(R) -> {ok,R}; - Other -> {error, Other} - after 500 -> {error, timeout} - end, - ?line Port = open_port({spawn, echo_drv}, [eof]), + {ok,Ref2} = receive + R when is_reference(R) -> {ok,R}; + Other -> {error, Other} + after 500 -> {error, timeout} + end, + Port = open_port({spawn, echo_drv}, [eof]), Pid ! go, - ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, - ?line ok = receive X -> {error, X} after 300 -> ok end, - ?line erlang:port_close(Port), - ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, + ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, + ok = receive X -> {error, X} after 300 -> ok end, + erlang:port_close(Port), + ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, ok. %% Check delayed unload and reload unload_reload_thingie(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line Parent = self(), - ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), - ?line F3 = fun() -> - Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}), - receive X -> Parent ! {got,X} end - end, - ?line Pid = spawn(fun() -> - receive go -> ok end, - _Port = open_port({spawn, echo_drv}, [eof]), - spawn(F3), - receive go -> ok end, - exit(banan) - end), - ?line Ref = erlang:monitor(process,Pid), + Path = proplists:get_value(data_dir, Config), + Parent = self(), + {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), + F3 = fun() -> + Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}), + receive X -> Parent ! {got,X} end + end, + Pid = spawn(fun() -> + receive go -> ok end, + _Port = open_port({spawn, echo_drv}, [eof]), + spawn(F3), + receive go -> ok end, + exit(banan) + end), + Ref = erlang:monitor(process,Pid), + Pid ! go, + {ok,Ref2} = receive + R when is_reference(R) -> {ok,R}; + Other -> {error, Other} + after 500 -> {error, timeout} + end, + {ok,pending_driver,Ref3} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]), + Ref4 = erl_ddll:monitor(driver,{echo_drv,loaded}), + ok = receive {'DOWN',Ref4, driver,echo_drv,load_cancelled} -> ok after 1000 -> false end, + {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []), + ok = receive {'UP',Ref3, driver,echo_drv,unload_cancelled} -> ok after 1000 -> false end, Pid ! go, - ?line {ok,Ref2} = receive - R when is_reference(R) -> {ok,R}; - Other -> {error, Other} - after 500 -> {error, timeout} - end, - ?line {ok,pending_driver,Ref3} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]), - ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,loaded}), - ?line ok = receive {'DOWN',Ref4, driver,echo_drv,load_cancelled} -> ok after 1000 -> false end, - ?line {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []), - ?line ok = receive {'UP',Ref3, driver,echo_drv,unload_cancelled} -> ok after 1000 -> false end, - ?line Pid ! go, - ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, - ?line [{Parent,1}] = erl_ddll:info(echo_drv, processes), - ?line 0 = erl_ddll:info(echo_drv, port_count), - ?line ok = unload_expect_fast(echo_drv,[{monitor,pending}]), - ?line ok = receive - {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok - after 300 -> error - end, - ?line ok = receive X -> {error, X} after 300 -> ok end, + ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, + [{Parent,1}] = erl_ddll:info(echo_drv, processes), + 0 = erl_ddll:info(echo_drv, port_count), + ok = unload_expect_fast(echo_drv,[{monitor,pending}]), + ok = receive + {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok + after 300 -> error + end, + ok = receive X -> {error, X} after 300 -> ok end, ok. %% Check delayed unload and reload unload_reload_thingie_2(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line Parent = self(), - ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), - ?line F3 = fun() -> - Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}), - receive X -> Parent ! {got,X} end - end, - ?line Pid = spawn(fun() -> - receive go -> ok end, - _Port = open_port({spawn, echo_drv}, [eof]), - spawn(F3), - receive go -> ok end, - exit(banan) - end), - ?line Ref = erlang:monitor(process,Pid), + Path = proplists:get_value(data_dir, Config), + Parent = self(), + {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), + F3 = fun() -> + Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}), + receive X -> Parent ! {got,X} end + end, + Pid = spawn(fun() -> + receive go -> ok end, + _Port = open_port({spawn, echo_drv}, [eof]), + spawn(F3), + receive go -> ok end, + exit(banan) + end), + Ref = erlang:monitor(process,Pid), + Pid ! go, + {ok,Ref2} = receive + R when is_reference(R) -> {ok,R}; + Other -> {error, Other} + after 500 -> {error, timeout} + end, + {ok,pending_driver,Ref3} = erl_ddll:try_load(Path, echo_drv, + [{monitor,pending_driver},{reload,pending_driver}]), + Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}), Pid ! go, - ?line {ok,Ref2} = receive - R when is_reference(R) -> {ok,R}; - Other -> {error, Other} - after 500 -> {error, timeout} - end, - ?line {ok,pending_driver,Ref3} = erl_ddll:try_load(Path,echo_drv,[{monitor,pending_driver},{reload,pending_driver}]), - ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}), - ?line Pid ! go, - ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, - ?line ok = receive {'DOWN',Ref4, driver,echo_drv,unloaded} -> ok after 1000 -> false end, - ?line ok = receive {'UP',Ref3, driver,echo_drv,loaded} -> ok after 1000 -> false end, - ?line [{Parent,1}] = erl_ddll:info(echo_drv, processes), - ?line 0 = erl_ddll:info(echo_drv, port_count), - ?line ok = receive - {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok - after 300 -> error - end, - ?line ok = unload_expect_fast(echo_drv,[{monitor,pending}]), - ?line ok = receive X -> {error, X} after 300 -> ok end, + ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, + ok = receive {'DOWN',Ref4, driver,echo_drv,unloaded} -> ok after 1000 -> false end, + ok = receive {'UP',Ref3, driver,echo_drv,loaded} -> ok after 1000 -> false end, + [{Parent,1}] = erl_ddll:info(echo_drv, processes), + 0 = erl_ddll:info(echo_drv, port_count), + ok = receive + {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok + after 300 -> error + end, + ok = unload_expect_fast(echo_drv,[{monitor,pending}]), + ok = receive X -> {error, X} after 300 -> ok end, ok. %% Check delayed unload and reload failure unload_reload_thingie_3(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line Parent = self(), - ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), - ?line F3 = fun() -> - Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), - receive X -> Parent ! {got,X} end - end, - ?line Pid = spawn(fun() -> - receive go -> ok end, - _Port = open_port({spawn, echo_drv}, [eof]), - spawn(F3), - receive go -> ok end, - exit(banan) - end), - ?line Ref = erlang:monitor(process,Pid), + Path = proplists:get_value(data_dir, Config), + Parent = self(), + {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), + F3 = fun() -> + Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), + receive X -> Parent ! {got,X} end + end, + Pid = spawn(fun() -> + receive go -> ok end, + _Port = open_port({spawn, echo_drv}, [eof]), + spawn(F3), + receive go -> ok end, + exit(banan) + end), + Ref = erlang:monitor(process,Pid), Pid ! go, - ?line {ok,Ref2} = receive - R when is_reference(R) -> {ok,R}; - Other -> {error, Other} - after 500 -> {error, timeout} - end, - ?line {ok,pending_driver,Ref3} = erl_ddll:try_load(filename:join([Path,"skrumpf"]),echo_drv,[{monitor,pending_driver},{reload,pending_driver}]), - ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}), - ?line Pid ! go, - ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, - ?line ok = receive - {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok - after 300 -> error - end, - ?line ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> false end, - ?line ok = receive - {'DOWN',Ref3, driver,echo_drv,{load_failure,_}} -> ok - after 1000 -> false - end, - ?line {'EXIT',_} = (catch erl_ddll:info(echo_drv, port_count)), - ?line {error, not_loaded} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]), - ?line ok = receive X -> {error, X} after 300 -> ok end, + {ok,Ref2} = receive + R when is_reference(R) -> {ok,R}; + Other -> {error, Other} + after 500 -> {error, timeout} + end, + {ok,pending_driver,Ref3} = erl_ddll:try_load(filename:join([Path,"skrumpf"]), echo_drv, + [{monitor,pending_driver},{reload,pending_driver}]), + Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}), + Pid ! go, + ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, + ok = receive + {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok + after 300 -> error + end, + ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> false end, + ok = receive + {'DOWN',Ref3, driver,echo_drv,{load_failure,_}} -> ok + after 1000 -> false + end, + {'EXIT',_} = (catch erl_ddll:info(echo_drv, port_count)), + {error, not_loaded} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]), + ok = receive X -> {error, X} after 300 -> ok end, ok. %% Reload a driver that is pending on a user reload_pending(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line Parent = self(), - ?line F3 = fun() -> - Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), - receive X -> Parent ! {got,X} end - end, - ?line Pid = spawn(fun() -> - receive go -> ok end, - {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), - spawn(F3), - receive go -> ok end, - _Port = open_port({spawn, echo_drv}, [eof]), - _Port2 = open_port({spawn, echo_drv}, [eof]), - Parent ! opened, - receive go -> ok end, - exit(banan) - end), - ?line Ref = erlang:monitor(process,Pid), + Path = proplists:get_value(data_dir, Config), + Parent = self(), + F3 = fun() -> + Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), + receive X -> Parent ! {got,X} end + end, + Pid = spawn(fun() -> + receive go -> ok end, + {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), + spawn(F3), + receive go -> ok end, + _Port = open_port({spawn, echo_drv}, [eof]), + _Port2 = open_port({spawn, echo_drv}, [eof]), + Parent ! opened, + receive go -> ok end, + exit(banan) + end), + Ref = erlang:monitor(process,Pid), Pid ! go, - ?line {ok,Ref2} = receive - R when is_reference(R) -> {ok,R}; - Other -> {error, Other} - after 500 -> {error, timeout} - end, - ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []), - ?line Port = open_port({spawn, echo_drv}, [eof]), + {ok,Ref2} = receive + R when is_reference(R) -> {ok,R}; + Other -> {error, Other} + after 500 -> {error, timeout} + end, + {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []), + Port = open_port({spawn, echo_drv}, [eof]), Pid ! go, - ?line receive opened -> ok end, - ?line {error, pending_process} = - erl_ddll:try_load(Path, echo_drv, - [{reload,pending_driver}, - {monitor,pending_driver}]), - ?line {ok, pending_process, Ref3} = - erl_ddll:try_load(Path, echo_drv, - [{reload,pending}, - {monitor,pending}]), - ?line ok = receive X -> {error, X} after 300 -> ok end, + receive opened -> ok end, + {error, pending_process} = + erl_ddll:try_load(Path, echo_drv, + [{reload,pending_driver}, + {monitor,pending_driver}]), + {ok, pending_process, Ref3} = + erl_ddll:try_load(Path, echo_drv, + [{reload,pending}, + {monitor,pending}]), + ok = receive X -> {error, X} after 300 -> ok end, Pid ! go, - ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, - ?line ok = receive Y -> {error, Y} after 300 -> ok end, - ?line erlang:port_close(Port), - ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, - ?line ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end, + ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, + ok = receive Y -> {error, Y} after 300 -> ok end, + erlang:port_close(Port), + ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, + ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end, [{Parent,1}] = erl_ddll:info(echo_drv,processes), - ?line ok = receive Z -> {error, Z} after 300 -> ok end, + ok = receive Z -> {error, Z} after 300 -> ok end, ok. %% Tests failure in the init in driver struct. load_fail_init(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line PathFailing = proplists:get_value(priv_dir, Config), - ?line [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path), - ?line lists:foreach(fun(Name) -> - Src = filename:join([Path,Name]), - Ext = filename:extension(Name), - Dst =filename:join([PathFailing,"echo_drv"++Ext]), - file:delete(Dst), - {ok,_} = file:copy(Src,Dst) - end, - AllFailInits), - ?line [_|_] = filelib:wildcard("echo_drv.*",PathFailing), - ?line {error, driver_init_failed} = erl_ddll:try_load(PathFailing, - echo_drv, - [{monitor,pending}]), - ?line ok = receive XX -> - {unexpected,XX} - after 300 -> - ok - end, + Path = proplists:get_value(data_dir, Config), + PathFailing = proplists:get_value(priv_dir, Config), + [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path), + lists:foreach(fun(Name) -> + Src = filename:join([Path,Name]), + Ext = filename:extension(Name), + Dst =filename:join([PathFailing,"echo_drv"++Ext]), + file:delete(Dst), + {ok,_} = file:copy(Src,Dst) + end, + AllFailInits), + [_|_] = filelib:wildcard("echo_drv.*",PathFailing), + {error, driver_init_failed} = erl_ddll:try_load(PathFailing, + echo_drv, + [{monitor,pending}]), + ok = receive XX -> + {unexpected,XX} + after 300 -> + ok + end, ok. %% Reload a driver that is pending but init fails reload_pending_fail_init(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line PathFailing = proplists:get_value(priv_dir, Config), - ?line [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path), - ?line lists:foreach(fun(Name) -> - Src = filename:join([Path,Name]), - Ext = filename:extension(Name), - Dst =filename:join([PathFailing,"echo_drv"++Ext]), - file:delete(Dst), - {ok,_} = file:copy(Src,Dst) - end, - AllFailInits), - ?line [_|_] = filelib:wildcard("echo_drv.*",PathFailing), - ?line Parent = self(), - ?line F3 = fun() -> - Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), - receive X -> Parent ! {got,X} end - end, - ?line Pid = spawn(fun() -> - receive go -> ok end, - {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), - spawn(F3), - receive go -> ok end, - _Port = open_port({spawn, echo_drv}, [eof]), - _Port2 = open_port({spawn, echo_drv}, [eof]), - Parent ! opened, - receive go -> ok end, - exit(banan) - end), - ?line Ref = erlang:monitor(process,Pid), + Path = proplists:get_value(data_dir, Config), + PathFailing = proplists:get_value(priv_dir, Config), + [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path), + lists:foreach(fun(Name) -> + Src = filename:join([Path,Name]), + Ext = filename:extension(Name), + Dst =filename:join([PathFailing,"echo_drv"++Ext]), + file:delete(Dst), + {ok,_} = file:copy(Src,Dst) + end, + AllFailInits), + [_|_] = filelib:wildcard("echo_drv.*",PathFailing), + Parent = self(), + F3 = fun() -> + Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), + receive X -> Parent ! {got,X} end + end, + Pid = spawn(fun() -> + receive go -> ok end, + {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), + spawn(F3), + receive go -> ok end, + _Port = open_port({spawn, echo_drv}, [eof]), + _Port2 = open_port({spawn, echo_drv}, [eof]), + Parent ! opened, + receive go -> ok end, + exit(banan) + end), + Ref = erlang:monitor(process,Pid), Pid ! go, - ?line {ok,Ref2} = receive - R when is_reference(R) -> {ok,R}; - Other -> {error, Other} - after 500 -> {error, timeout} - end, - ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []), - ?line Port = open_port({spawn, echo_drv}, [eof]), + {ok,Ref2} = receive + R when is_reference(R) -> {ok,R}; + Other -> {error, Other} + after 500 -> {error, timeout} + end, + {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []), + Port = open_port({spawn, echo_drv}, [eof]), Pid ! go, - ?line receive opened -> ok end, - ?line {ok, pending_process, Ref3} = - erl_ddll:try_load(PathFailing, echo_drv, - [{reload,pending}, - {monitor,pending}]), - ?line ok = receive X -> {error, X} after 300 -> ok end, + receive opened -> ok end, + {ok, pending_process, Ref3} = + erl_ddll:try_load(PathFailing, echo_drv, + [{reload,pending}, + {monitor,pending}]), + ok = receive X -> {error, X} after 300 -> ok end, Pid ! go, - ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, - ?line ok = receive Y -> {error, Y} after 300 -> ok end, - ?line erlang:port_close(Port), - ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, - ?line ok = receive {'DOWN', Ref3, driver, echo_drv, {load_failure,driver_init_failed}} -> ok after 300 -> error end, - ?line {'EXIT',{badarg,_}} = (catch erl_ddll:info(echo_drv,processes)), - - ?line ok = receive Z -> {error, Z} after 300 -> ok end, + ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, + ok = receive Y -> {error, Y} after 300 -> ok end, + erlang:port_close(Port), + ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, + ok = receive {'DOWN', Ref3, driver, echo_drv, {load_failure,driver_init_failed}} -> ok after 300 -> error end, + {'EXIT',{badarg,_}} = (catch erl_ddll:info(echo_drv,processes)), + + ok = receive Z -> {error, Z} after 300 -> ok end, ok. %% Reload a driver with kill_ports option that is pending on a user reload_pending_kill(Config) when is_list(Config) -> - ?line OldFlag = process_flag(trap_exit,true), - ?line Path = proplists:get_value(data_dir, Config), - ?line Parent = self(), - ?line F3 = fun() -> - Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), - receive X -> Parent ! {got,X} end - end, - ?line Pid = spawn(fun() -> - process_flag(trap_exit,true), - receive go -> ok end, - {ok, loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]), - spawn(F3), - receive go -> ok end, - Port = open_port({spawn, echo_drv}, [eof]), - Port2 = open_port({spawn, echo_drv}, [eof]), - Parent ! opened, - receive go -> ok end, - receive - {'EXIT', Port2, driver_unloaded} -> - Parent ! first_exit - end, - receive - {'EXIT', Port, driver_unloaded} -> - Parent ! second_exit - end, - receive go -> ok end, - exit(banan) - end), - ?line Ref = erlang:monitor(process,Pid), + OldFlag = process_flag(trap_exit,true), + Path = proplists:get_value(data_dir, Config), + Parent = self(), + F3 = fun() -> + Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), + receive X -> Parent ! {got,X} end + end, + Pid = spawn(fun() -> + process_flag(trap_exit,true), + receive go -> ok end, + {ok, loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]), + spawn(F3), + receive go -> ok end, + Port = open_port({spawn, echo_drv}, [eof]), + Port2 = open_port({spawn, echo_drv}, [eof]), + Parent ! opened, + receive go -> ok end, + receive + {'EXIT', Port2, driver_unloaded} -> + Parent ! first_exit + end, + receive + {'EXIT', Port, driver_unloaded} -> + Parent ! second_exit + end, + receive go -> ok end, + exit(banan) + end), + Ref = erlang:monitor(process,Pid), Pid ! go, - ?line {ok,Ref2} = receive - R when is_reference(R) -> {ok,R}; - Other -> {error, Other} - after 500 -> {error, timeout} - end, - ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]), - ?line {error,inconsistent} = erl_ddll:try_load(Path, echo_drv, []), - ?line Port = open_port({spawn, echo_drv}, [eof]), + {ok,Ref2} = receive + R when is_reference(R) -> {ok,R}; + Other -> {error, Other} + after 500 -> {error, timeout} + end, + {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]), + {error,inconsistent} = erl_ddll:try_load(Path, echo_drv, []), + Port = open_port({spawn, echo_drv}, [eof]), Pid ! go, - ?line receive opened -> ok end, - ?line {error, pending_process} = - erl_ddll:try_load(Path, echo_drv, - [{driver_options,[kill_ports]}, - {reload,pending_driver}, - {monitor,pending_driver}]), - ?line {ok, pending_process, Ref3} = - erl_ddll:try_load(Path, echo_drv, - [{driver_options,[kill_ports]}, - {reload,pending}, - {monitor,pending}]), - ?line ok = receive - {'EXIT', Port, driver_unloaded} -> - ok - after 300 -> error - end, + receive opened -> ok end, + {error, pending_process} = + erl_ddll:try_load(Path, echo_drv, + [{driver_options,[kill_ports]}, + {reload,pending_driver}, + {monitor,pending_driver}]), + {ok, pending_process, Ref3} = + erl_ddll:try_load(Path, echo_drv, + [{driver_options,[kill_ports]}, + {reload,pending}, + {monitor,pending}]), + ok = receive + {'EXIT', Port, driver_unloaded} -> + ok + after 300 -> error + end, Pid ! go, - ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, - ?line ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end, - ?line [_,_] = erl_ddll:info(echo_drv,processes), - ?line ok = receive first_exit -> ok after 300 -> error end, - ?line ok = receive second_exit -> ok after 300 -> error end, - ?line 0 = erl_ddll:info(echo_drv,port_count), - ?line ok = receive X -> {error, X} after 300 -> ok end, + ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, + ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end, + [_,_] = erl_ddll:info(echo_drv,processes), + ok = receive first_exit -> ok after 300 -> error end, + ok = receive second_exit -> ok after 300 -> error end, + 0 = erl_ddll:info(echo_drv,port_count), + ok = receive X -> {error, X} after 300 -> ok end, Pid ! go, - ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, - ?line ok = receive Y -> {error, Y} after 300 -> ok end, - ?line Port2 = open_port({spawn, echo_drv}, [eof]), - ?line true = is_port(Port2), + ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, + ok = receive Y -> {error, Y} after 300 -> ok end, + Port2 = open_port({spawn, echo_drv}, [eof]), + true = is_port(Port2), + [{Parent,1}] = erl_ddll:info(echo_drv,processes), + 1 = erl_ddll:info(echo_drv,port_count), + erlang:port_close(Port2), + ok = receive {'EXIT', Port2, normal} -> ok after 300 -> error end, + 0 = erl_ddll:info(echo_drv,port_count), [{Parent,1}] = erl_ddll:info(echo_drv,processes), - ?line 1 = erl_ddll:info(echo_drv,port_count), - ?line erlang:port_close(Port2), - ?line ok = receive {'EXIT', Port2, normal} -> ok after 300 -> error end, - ?line 0 = erl_ddll:info(echo_drv,port_count), - ?line [{Parent,1}] = erl_ddll:info(echo_drv,processes), - ?line Port3 = open_port({spawn, echo_drv}, [eof]), - ?line {ok, pending_driver, Ref4} = - erl_ddll:try_unload(echo_drv,[{monitor,pending_driver}]), - ?line ok = receive - {'EXIT', Port3, driver_unloaded} -> - ok - after 300 -> error - end, - ?line ok = receive {'DOWN', Ref4, driver, echo_drv, unloaded} -> ok after 300 -> error end, + Port3 = open_port({spawn, echo_drv}, [eof]), + {ok, pending_driver, Ref4} = + erl_ddll:try_unload(echo_drv,[{monitor,pending_driver}]), + ok = receive + {'EXIT', Port3, driver_unloaded} -> + ok + after 300 -> error + end, + ok = receive {'DOWN', Ref4, driver, echo_drv, unloaded} -> ok after 300 -> error end, io:format("Port = ~w, Port2 = ~w, Port3 = ~w~n",[Port,Port2,Port3]), - ?line ok = receive Z -> {error, Z} after 300 -> ok end, - ?line process_flag(trap_exit,OldFlag), + ok = receive Z -> {error, Z} after 300 -> ok end, + process_flag(trap_exit,OldFlag), ok. %% Some more error code checking more_error_codes(Config) when is_list(Config) -> - ?line {error,Err} = erl_ddll:try_load("./echo_dr",echo_dr,[]), - ?line true = is_list(erl_ddll:format_error(Err)), - ?line true = is_list(erl_ddll:format_error(not_loaded)), + {error,Err} = erl_ddll:try_load("./echo_dr",echo_dr,[]), + true = is_list(erl_ddll:format_error(Err)), + true = is_list(erl_ddll:format_error(not_loaded)), ok. %% Check kill_ports option to try_unload forced_port_killing(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line OldFlag=process_flag(trap_exit,true), - ?line Parent = self(), - ?line F3 = fun() -> - Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), - receive X -> Parent ! {got,X} end - end, - ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), - ?line spawn(F3), - ?line {ok,Ref2} = receive - R when is_reference(R) -> {ok,R}; - Other -> {error, Other} - after 500 -> {error, timeout} - end, - ?line Port = open_port({spawn, echo_drv}, [eof]), - ?line Port2 = open_port({spawn, echo_drv}, [eof]), - ?line {ok, pending_driver, Ref1} = - erl_ddll:try_unload(echo_drv,[{monitor,pending_driver},kill_ports]), - ?line ok = receive - {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok - after 300 -> error - end, - ?line ok = receive {'EXIT',Port,driver_unloaded} -> ok after 300 -> false end, - ?line ok = receive {'EXIT',Port2,driver_unloaded} -> ok after 300 -> false end, - ?line ok = receive {'DOWN',Ref1, driver, echo_drv, unloaded} -> ok after 300 -> false end, - ?line process_flag(trap_exit,OldFlag), - ?line ok = receive X -> {error, X} after 300 -> ok end, + Path = proplists:get_value(data_dir, Config), + OldFlag=process_flag(trap_exit,true), + Parent = self(), + F3 = fun() -> + Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), + receive X -> Parent ! {got,X} end + end, + {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), + spawn(F3), + {ok,Ref2} = receive + R when is_reference(R) -> {ok,R}; + Other -> {error, Other} + after 500 -> {error, timeout} + end, + Port = open_port({spawn, echo_drv}, [eof]), + Port2 = open_port({spawn, echo_drv}, [eof]), + {ok, pending_driver, Ref1} = + erl_ddll:try_unload(echo_drv,[{monitor,pending_driver},kill_ports]), + ok = receive + {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok + after 300 -> error + end, + ok = receive {'EXIT',Port,driver_unloaded} -> ok after 300 -> false end, + ok = receive {'EXIT',Port2,driver_unloaded} -> ok after 300 -> false end, + ok = receive {'DOWN',Ref1, driver, echo_drv, unloaded} -> ok after 300 -> false end, + process_flag(trap_exit,OldFlag), + ok = receive X -> {error, X} after 300 -> ok end, ok. %% Check delayed unload and reload with no trap_exit no_trap_exit_and_kill_ports(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line Parent = self(), - ?line OldFlag=process_flag(trap_exit,true), - ?line F3 = fun() -> - Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), - receive X -> Parent ! {got,X} end - end, - ?line Pid = spawn(fun() -> - process_flag(trap_exit,false), - receive go -> ok end, - {ok, loaded} = erl_ddll:try_load(Path, echo_drv, - [{driver_options,[kill_ports]}]), - spawn(F3), - receive go -> ok end, - _Port = open_port({spawn, echo_drv}, [eof]), - _Port2 = open_port({spawn, echo_drv}, [eof]), - exit(banan) - end), - ?line Ref = erlang:monitor(process,Pid), + Path = proplists:get_value(data_dir, Config), + Parent = self(), + OldFlag=process_flag(trap_exit,true), + F3 = fun() -> + Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), + receive X -> Parent ! {got,X} end + end, + Pid = spawn(fun() -> + process_flag(trap_exit,false), + receive go -> ok end, + {ok, loaded} = erl_ddll:try_load(Path, echo_drv, + [{driver_options,[kill_ports]}]), + spawn(F3), + receive go -> ok end, + _Port = open_port({spawn, echo_drv}, [eof]), + _Port2 = open_port({spawn, echo_drv}, [eof]), + exit(banan) + end), + Ref = erlang:monitor(process,Pid), Pid ! go, - ?line {ok,Ref2} = receive - R when is_reference(R) -> {ok,R}; - Other -> {error, Other} - after 500 -> {error, timeout} - end, - ?line {error, inconsistent} = erl_ddll:try_load(Path, echo_drv, []), - ?line MyPort = open_port({spawn, echo_drv}, [eof]), + {ok,Ref2} = receive + R when is_reference(R) -> {ok,R}; + Other -> {error, Other} + after 500 -> {error, timeout} + end, + {error, inconsistent} = erl_ddll:try_load(Path, echo_drv, []), + MyPort = open_port({spawn, echo_drv}, [eof]), Pid ! go, - ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, - ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, - ?line ok = receive {'EXIT',MyPort,driver_unloaded} -> ok after 300 -> error end, - ?line process_flag(trap_exit,OldFlag), + ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, + ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, + ok = receive {'EXIT',MyPort,driver_unloaded} -> ok after 300 -> error end, + process_flag(trap_exit,OldFlag), ok. %% Check monitor and demonitor of drivers monitor_demonitor(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line erl_ddll:try_load(Path, echo_drv, []), - ?line Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}), - ?line Self = self(), - ?line [{Self,1}] = erl_ddll:info(echo_drv,awaiting_unload), - ?line true = erl_ddll:demonitor(Ref), - ?line [] = erl_ddll:info(echo_drv,awaiting_unload), - ?line erl_ddll:try_unload(echo_drv,[]), - ?line ok = receive _ -> error after 300 -> ok end, + Path = proplists:get_value(data_dir, Config), + erl_ddll:try_load(Path, echo_drv, []), + Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}), + Self = self(), + [{Self,1}] = erl_ddll:info(echo_drv,awaiting_unload), + true = erl_ddll:demonitor(Ref), + [] = erl_ddll:info(echo_drv,awaiting_unload), + erl_ddll:try_unload(echo_drv,[]), + ok = receive _ -> error after 300 -> ok end, ok. %% Check monitor/demonitor of driver loading monitor_demonitor_load(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line {ok,loaded} = erl_ddll:try_load(Path, echo_drv, []), - ?line Port = open_port({spawn, echo_drv}, [eof]), - ?line Ref = erl_ddll:monitor(driver,{echo_drv,loaded}), - ?line ok = receive {'UP',Ref,driver,echo_drv,loaded} -> ok after 500 -> error end, - ?line {ok, pending_driver} = erl_ddll:try_unload(echo_drv,[]), - ?line Ref2 = erl_ddll:monitor(driver,{echo_drv,loaded}), - ?line ok = receive {'DOWN',Ref2,driver,echo_drv,load_cancelled} -> ok after 0 -> error end, - ?line {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []), - ?line {ok, pending_driver} = - erl_ddll:try_load(Path, echo_drv, [{reload,pending_driver}]), - ?line Ref3 = erl_ddll:monitor(driver,{echo_drv,loaded}), - ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}), - ?line ok = receive _ -> error after 300 -> ok end, - ?line Self = self(), - ?line [{Self,1}] = erl_ddll:info(echo_drv,awaiting_load), - ?line true = erl_ddll:demonitor(Ref3), - ?line [] = erl_ddll:info(echo_drv,awaiting_load), - ?line erlang:port_close(Port), - ?line ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> error end, - ?line ok = receive _ -> error after 300 -> ok end, - ?line ok = unload_expect_fast(echo_drv,[]), + Path = proplists:get_value(data_dir, Config), + {ok,loaded} = erl_ddll:try_load(Path, echo_drv, []), + Port = open_port({spawn, echo_drv}, [eof]), + Ref = erl_ddll:monitor(driver,{echo_drv,loaded}), + ok = receive {'UP',Ref,driver,echo_drv,loaded} -> ok after 500 -> error end, + {ok, pending_driver} = erl_ddll:try_unload(echo_drv,[]), + Ref2 = erl_ddll:monitor(driver,{echo_drv,loaded}), + ok = receive {'DOWN',Ref2,driver,echo_drv,load_cancelled} -> ok after 0 -> error end, + {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []), + {ok, pending_driver} = + erl_ddll:try_load(Path, echo_drv, [{reload,pending_driver}]), + Ref3 = erl_ddll:monitor(driver,{echo_drv,loaded}), + Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}), + ok = receive _ -> error after 300 -> ok end, + Self = self(), + [{Self,1}] = erl_ddll:info(echo_drv,awaiting_load), + true = erl_ddll:demonitor(Ref3), + [] = erl_ddll:info(echo_drv,awaiting_load), + erlang:port_close(Port), + ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> error end, + ok = receive _ -> error after 300 -> ok end, + ok = unload_expect_fast(echo_drv,[]), ok. %% Test the new load/unload/reload interface new_interface(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), + Path = proplists:get_value(data_dir, Config), % Typical scenario - ?line ok = erl_ddll:load(Path, echo_drv), - ?line Port = open_port({spawn, echo_drv}, [eof]), - ?line ok = erl_ddll:unload(echo_drv), - ?line Port ! {self(), {command, "text"}}, - ?line ok = receive - {Port, {data, "text"}} -> ok; - _ -> error - after - 1000 -> error - end, - ?line Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}), - ?line ok = receive X -> {error, X} after 300 -> ok end, - ?line erlang:port_close(Port), - ?line ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 300 -> error end, + ok = erl_ddll:load(Path, echo_drv), + Port = open_port({spawn, echo_drv}, [eof]), + ok = erl_ddll:unload(echo_drv), + Port ! {self(), {command, "text"}}, + ok = receive + {Port, {data, "text"}} -> ok; + _ -> error + after + 1000 -> error + end, + Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}), + ok = receive X -> {error, X} after 300 -> ok end, + erlang:port_close(Port), + ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 300 -> error end, % More than one user - ?line ok = erl_ddll:load(Path, echo_drv), - ?line Ref2 = erl_ddll:monitor(driver,{echo_drv,unloaded}), - ?line ok = erl_ddll:load(Path, echo_drv), - ?line ok = erl_ddll:load(Path, echo_drv), - ?line Port2 = open_port({spawn, echo_drv}, [eof]), - ?line ok = erl_ddll:unload(echo_drv), - ?line Port2 ! {self(), {command, "text"}}, - ?line ok = receive - {Port2, {data, "text"}} -> ok; - _ -> error - after - 1000 -> error - end, - ?line ok = erl_ddll:unload(echo_drv), - ?line Port2 ! {self(), {command, "text"}}, - ?line ok = receive - {Port2, {data, "text"}} -> ok; - _ -> error - after - 1000 -> error - end, - ?line ok = erl_ddll:unload(echo_drv), - ?line Port2 ! {self(), {command, "text"}}, - ?line ok = receive - {Port2, {data, "text"}} -> ok; - _ -> error - after - 1000 -> error - end, - ?line ok = receive X2 -> {error, X2} after 300 -> ok end, - ?line ok = erl_ddll:load(Path, echo_drv), - ?line ok = receive {'UP', Ref2, driver, echo_drv, unload_cancelled} -> ok after 300 -> error end, - ?line Ref3 = erl_ddll:monitor(driver,{echo_drv,unloaded_only}), - ?line erlang:port_close(Port2), - ?line ok = receive X3 -> {error, X3} after 300 -> ok end, - ?line ok = erl_ddll:unload(echo_drv), - ?line ok = receive {'DOWN', Ref3, driver, echo_drv, unloaded} -> ok after 300 -> error end, + ok = erl_ddll:load(Path, echo_drv), + Ref2 = erl_ddll:monitor(driver,{echo_drv,unloaded}), + ok = erl_ddll:load(Path, echo_drv), + ok = erl_ddll:load(Path, echo_drv), + Port2 = open_port({spawn, echo_drv}, [eof]), + ok = erl_ddll:unload(echo_drv), + Port2 ! {self(), {command, "text"}}, + ok = receive + {Port2, {data, "text"}} -> ok; + _ -> error + after + 1000 -> error + end, + ok = erl_ddll:unload(echo_drv), + Port2 ! {self(), {command, "text"}}, + ok = receive + {Port2, {data, "text"}} -> ok; + _ -> error + after + 1000 -> error + end, + ok = erl_ddll:unload(echo_drv), + Port2 ! {self(), {command, "text"}}, + ok = receive + {Port2, {data, "text"}} -> ok; + _ -> error + after + 1000 -> error + end, + ok = receive X2 -> {error, X2} after 300 -> ok end, + ok = erl_ddll:load(Path, echo_drv), + ok = receive {'UP', Ref2, driver, echo_drv, unload_cancelled} -> ok after 300 -> error end, + Ref3 = erl_ddll:monitor(driver,{echo_drv,unloaded_only}), + erlang:port_close(Port2), + ok = receive X3 -> {error, X3} after 300 -> ok end, + ok = erl_ddll:unload(echo_drv), + ok = receive {'DOWN', Ref3, driver, echo_drv, unloaded} -> ok after 300 -> error end, ok. - - + + ddll_test(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), + Path = proplists:get_value(data_dir, Config), - %?line {error,{already_started,ErlDdllPid}} = erl_ddll:start(), - %?line ErlDdllPid = whereis(ddll_server), + %{error,{already_started,ErlDdllPid}} = erl_ddll:start(), + %ErlDdllPid = whereis(ddll_server), %% Load the echo driver and verify that it was loaded. {ok,L1,L2}=load_echo_driver(Path), %% Verify that the driver works. - ?line Port = open_port({spawn, echo_drv}, [eof]), - ?line {hej, "hopp",4711,123445567436543653} = - erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}), - ?line {hej, "hopp",4711,123445567436543653} = - erlang:port_call(Port,47,{hej, "hopp",4711,123445567436543653}), - ?line Port ! {self(), {command, "text"}}, - ?line 1 = receive - {Port, {data, "text"}} -> 1; - _Other -> 2 - after - 1000 -> 2 - end, - ?line Port ! {self(), close}, - ?line receive {Port, closed} -> ok end, - -%% %% Unload the driver and verify that it was unloaded. - ok = unload_echo_driver(L1,L2), - -%% %?line {error, {already_started, _}} = erl_ddll:start(), + Port = open_port({spawn, echo_drv}, [eof]), + {hej, "hopp",4711,123445567436543653} = + erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}), + {hej, "hopp",4711,123445567436543653} = + erlang:port_call(Port,47,{hej, "hopp",4711,123445567436543653}), + Port ! {self(), {command, "text"}}, + 1 = receive + {Port, {data, "text"}} -> 1; + _Other -> 2 + after + 1000 -> 2 + end, + Port ! {self(), close}, + receive {Port, closed} -> ok end, + + %% %% Unload the driver and verify that it was unloaded. + ok = unload_echo_driver(L1,L2), + + %% %{error, {already_started, _}} = erl_ddll:start(), ok. %% Tests errors having to do with bad drivers. errors(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), + Path = proplists:get_value(data_dir, Config), - ?line {ok, L1} = erl_ddll:loaded_drivers(), + {ok, L1} = erl_ddll:loaded_drivers(), - ?line {error, {open_error, _}} = erl_ddll:load_driver(Path, bad_name), - ?line {error, driver_init_failed} = erl_ddll:load_driver(Path, initfail_drv), - ?line {error, bad_driver_name} = erl_ddll:load_driver(Path, wrongname_drv), + {error, {open_error, _}} = erl_ddll:load_driver(Path, bad_name), + {error, driver_init_failed} = erl_ddll:load_driver(Path, initfail_drv), + {error, bad_driver_name} = erl_ddll:load_driver(Path, wrongname_drv), %% We assume that there is a statically linked driver named "ddll": - ?line {error, linked_in_driver} = erl_ddll:unload_driver(efile), - ?line {error, not_loaded} = erl_ddll:unload_driver("__pucko_driver__"), - + {error, linked_in_driver} = erl_ddll:unload_driver(efile), + {error, not_loaded} = erl_ddll:unload_driver("__pucko_driver__"), + case os:type() of - {unix, _} -> - ?line {error, no_driver_init} = - erl_ddll:load_driver(Path, noinit_drv); - _ -> - ok + {unix, _} -> + {error, no_driver_init} = + erl_ddll:load_driver(Path, noinit_drv); + _ -> + ok end, - ?line {ok, L1} = erl_ddll:loaded_drivers(), + {ok, L1} = erl_ddll:loaded_drivers(), ok. %% Check that drivers are unloaded when their reference count %% reaches zero, and that they cannot be unloaded while %% they are still referenced. reference_count(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), + Path = proplists:get_value(data_dir, Config), %% Spawn a process that loads the driver (and holds a reference %% to it). Pid1=spawn_link(?MODULE, echo_loader, [Path, self()]), receive - {Pid1, echo_loaded} -> ok + {Pid1, echo_loaded} -> ok after 2000 -> ct:fail("echo_loader failed to start.") end, Pid1 ! {self(), die}, - ?line test_server:sleep(200), % Give time to unload. + test_server:sleep(200), % Give time to unload. % Verify that the driver was automaticly unloaded when the % process died. - ?line {error, not_loaded}=erl_ddll:unload_driver(echo_drv), + {error, not_loaded}=erl_ddll:unload_driver(echo_drv), ok. % Loads the echo driver, send msg to started, sits and waits to % get a signal to die, then unloads the driver and terminates. echo_loader(Path, Starter) -> - ?line {ok, L1, L2}=load_echo_driver(Path), - ?line Starter ! {self(), echo_loaded}, + {ok, L1, L2}=load_echo_driver(Path), + Starter ! {self(), echo_loaded}, receive - {Starter, die} -> - ?line unload_echo_driver(L1,L2) + {Starter, die} -> + unload_echo_driver(L1,L2) end. % Loads the echo driver, send msg to started, sits and waits to % get a signal to die, then unloads the driver and terminates. nice_echo_loader(Path, Starter) -> - ?line {ok, L1, L2}=load_nice_echo_driver(Path), - ?line Starter ! {self(), echo_loaded}, + {ok, L1, L2}=load_nice_echo_driver(Path), + Starter ! {self(), echo_loaded}, receive - {Starter, die} -> - ?line unload_echo_driver(L1,L2) + {Starter, die} -> + unload_echo_driver(L1,L2) end. %% Test that a port that uses a driver is killed when the %% process that loaded the driver dies. kill_port(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), + Path = proplists:get_value(data_dir, Config), %% Spawn a process that loads the driver (and holds a reference %% to it). - ?line Pid1=spawn(?MODULE, echo_loader, [Path, self()]), - ?line receive - {Pid1, echo_loaded} -> - ok - after 3000 -> - ?line exit(Pid1, kill), - ?line ct:fail("echo_loader failed to start.") - end, + Pid1=spawn(?MODULE, echo_loader, [Path, self()]), + receive + {Pid1, echo_loaded} -> + ok + after 3000 -> + exit(Pid1, kill), + ct:fail("echo_loader failed to start.") + end, % Spawn off a port that uses the driver. - ?line Port = open_port({spawn, echo_drv}, [eof]), + Port = open_port({spawn, echo_drv}, [eof]), % Kill the process / unload the driver. - ?line process_flag(trap_exit, true), - ?line exit(Pid1, kill), - ?line test_server:sleep(200), % Give some time to unload. - ?line {error, not_loaded} = erl_ddll:unload_driver(echo_drv), + process_flag(trap_exit, true), + exit(Pid1, kill), + test_server:sleep(200), % Give some time to unload. + {error, not_loaded} = erl_ddll:unload_driver(echo_drv), % See if the port is killed. receive - {'EXIT', Port, Reason} -> - io:format("Port exited with reason ~w", [Reason]) + {'EXIT', Port, Reason} -> + io:format("Port exited with reason ~w", [Reason]) after 5000 -> - ?line ct:fail("Echo port did not terminate.") + ct:fail("Echo port did not terminate.") end, ok. %% Test that a port that uses a driver is not killed when the %% process that loaded the driver dies and it's nicely opened. dont_kill_port(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), + Path = proplists:get_value(data_dir, Config), %% Spawn a process that loads the driver (and holds a reference %% to it). - ?line Pid1=spawn(?MODULE, nice_echo_loader, [Path, self()]), - ?line receive - {Pid1, echo_loaded} -> - ok - after 3000 -> - ?line exit(Pid1, kill), - ?line ct:fail("echo_loader failed to start.") - end, + Pid1=spawn(?MODULE, nice_echo_loader, [Path, self()]), + receive + {Pid1, echo_loaded} -> + ok + after 3000 -> + exit(Pid1, kill), + ct:fail("echo_loader failed to start.") + end, % Spawn off a port that uses the driver. - ?line Port = open_port({spawn, echo_drv}, [eof]), + Port = open_port({spawn, echo_drv}, [eof]), % Kill the process / unload the driver. - ?line process_flag(trap_exit, true), - ?line exit(Pid1, kill), - ?line test_server:sleep(200), % Give some time to unload. - ?line {hej, "hopp",4711,123445567436543653} = - erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}), - ?line [] = erl_ddll:info(echo_drv,processes), + process_flag(trap_exit, true), + exit(Pid1, kill), + test_server:sleep(200), % Give some time to unload. + {hej, "hopp",4711,123445567436543653} = + erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}), + [] = erl_ddll:info(echo_drv,processes), %% unload should work with no owner - ?line ok = erl_ddll:unload_driver(echo_drv), %Kill ports while at it + ok = erl_ddll:unload_driver(echo_drv), %Kill ports while at it % See if the port is killed. receive - {'EXIT', Port, Reason} -> - io:format("Port exited with reason ~w", [Reason]) + {'EXIT', Port, Reason} -> + io:format("Port exited with reason ~w", [Reason]) after 5000 -> - ?line ct:fail("Echo port did not terminate.") + ct:fail("Echo port did not terminate.") end, ok. %% Test that a process that loaded a driver %% is the only process that can unload it. properties(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), + Path = proplists:get_value(data_dir, Config), % Let another process load the echo driver. Pid=spawn_link(?MODULE, echo_loader, [Path, self()]), receive - {Pid, echo_loaded} -> ok + {Pid, echo_loaded} -> ok after 2000 -> ct:fail("echo_loader failed to start.") end, % Try to unload the driver from this process (the wrong one). - ?line {error, _} = erl_ddll:unload_driver(echo_drv), - ?line {ok, Drivers} = erl_ddll:loaded_drivers(), - ?line case lists:member("echo_drv", Drivers) of - true -> - ok; - false -> - ct:fail("Unload from wrong process succeeded.") - end, + {error, _} = erl_ddll:unload_driver(echo_drv), + {ok, Drivers} = erl_ddll:loaded_drivers(), + case lists:member("echo_drv", Drivers) of + true -> + ok; + false -> + ct:fail("Unload from wrong process succeeded.") + end, % Unload the driver and terminate dummy process. - ?line Pid ! {self(), die}, - ?line test_server:sleep(200), % Give time to unload. + Pid ! {self(), die}, + test_server:sleep(200), % Give time to unload. ok. %% Load two drivers and unload them in load order. load_and_unload(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line {ok, Loaded_drivers1} = erl_ddll:loaded_drivers(), - ?line ok = erl_ddll:load_driver(Path, echo_drv), - ?line ok = erl_ddll:load_driver(Path, dummy_drv), - ?line ok = erl_ddll:unload_driver(echo_drv), - ?line ok = erl_ddll:unload_driver(dummy_drv), - ?line {ok, Loaded_drivers2} = erl_ddll:loaded_drivers(), - ?line Set1 = ordsets:from_list(Loaded_drivers1), - ?line Set2 = ordsets:from_list(Loaded_drivers2), - ?line io:format("~p == ~p\n", [Loaded_drivers1, Loaded_drivers2]), - ?line [] = ordsets:to_list(ordsets:subtract(Set2, Set1)), + Path = proplists:get_value(data_dir, Config), + {ok, Loaded_drivers1} = erl_ddll:loaded_drivers(), + ok = erl_ddll:load_driver(Path, echo_drv), + ok = erl_ddll:load_driver(Path, dummy_drv), + ok = erl_ddll:unload_driver(echo_drv), + ok = erl_ddll:unload_driver(dummy_drv), + {ok, Loaded_drivers2} = erl_ddll:loaded_drivers(), + Set1 = ordsets:from_list(Loaded_drivers1), + Set2 = ordsets:from_list(Loaded_drivers2), + io:format("~p == ~p\n", [Loaded_drivers1, Loaded_drivers2]), + [] = ordsets:to_list(ordsets:subtract(Set2, Set1)), ok. %% Check multiple calls to driver_lock_driver lock_driver(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line {ok, _} = erl_ddll:try_load(Path, lock_drv, []), - ?line Port1 = open_port({spawn, lock_drv}, [eof]), - ?line Port2 = open_port({spawn, lock_drv}, [eof]), - ?line true = erl_ddll:info(lock_drv,permanent), - ?line erlang:port_close(Port1), - ?line erlang:port_close(Port2), + Path = proplists:get_value(data_dir, Config), + {ok, _} = erl_ddll:try_load(Path, lock_drv, []), + Port1 = open_port({spawn, lock_drv}, [eof]), + Port2 = open_port({spawn, lock_drv}, [eof]), + true = erl_ddll:info(lock_drv,permanent), + erlang:port_close(Port1), + erlang:port_close(Port2), ok. - + % Load and unload the echo_drv driver. % Make sure that the driver doesn't exist before we load it, % and that it exists before we unload it. load_echo_driver(Path) -> - ?line {ok, L1} = erl_ddll:loaded_drivers(), - ?line ok = erl_ddll:load_driver(Path, echo_drv), - ?line {ok, L2} = erl_ddll:loaded_drivers(), - ?line ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2), - ordsets:from_list(L1))), + {ok, L1} = erl_ddll:loaded_drivers(), + ok = erl_ddll:load_driver(Path, echo_drv), + {ok, L2} = erl_ddll:loaded_drivers(), + ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2), + ordsets:from_list(L1))), {ok,L1,L2}. load_nice_echo_driver(Path) -> - ?line {ok, L1} = erl_ddll:loaded_drivers(), - ?line ok = erl_ddll:load(Path, echo_drv), - ?line {ok, L2} = erl_ddll:loaded_drivers(), - ?line ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2), - ordsets:from_list(L1))), + {ok, L1} = erl_ddll:loaded_drivers(), + ok = erl_ddll:load(Path, echo_drv), + {ok, L2} = erl_ddll:loaded_drivers(), + ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2), + ordsets:from_list(L1))), {ok,L1,L2}. unload_echo_driver(L1,L2) -> - ?line {ok, L2} = erl_ddll:loaded_drivers(), - ?line ok = erl_ddll:unload_driver(echo_drv), - ?line {ok, L3} = erl_ddll:loaded_drivers(), - ?line [] = ordsets:to_list(subtract(ordsets:from_list(L3), - ordsets:from_list(L1))), + {ok, L2} = erl_ddll:loaded_drivers(), + ok = erl_ddll:unload_driver(echo_drv), + {ok, L3} = erl_ddll:loaded_drivers(), + [] = ordsets:to_list(subtract(ordsets:from_list(L3), + ordsets:from_list(L1))), ok. unload_expect_fast(Driver,XFlags) -> {ok, pending_driver, Ref} = - erl_ddll:try_unload(Driver, - [{monitor,pending_driver}]++XFlags), + erl_ddll:try_unload(Driver, + [{monitor,pending_driver}]++XFlags), receive - {'DOWN', Ref, driver, Driver, unloaded} -> - case lists:member(atom_to_list(Driver),element(2,erl_ddll:loaded_drivers())) of - true -> - {error, {still_there, Driver}}; - false -> - ok - end + {'DOWN', Ref, driver, Driver, unloaded} -> + case lists:member(atom_to_list(Driver),element(2,erl_ddll:loaded_drivers())) of + true -> + {error, {still_there, Driver}}; + false -> + ok + end after 1000 -> - {error,{unable_to_unload, Driver}} + {error,{unable_to_unload, Driver}} end. diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl index 08e2448146..ad592f7147 100644 --- a/erts/emulator/test/decode_packet_SUITE.erl +++ b/erts/emulator/test/decode_packet_SUITE.erl @@ -25,8 +25,8 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0, - init_per_testcase/2,end_per_testcase/2, - basic/1, packet_size/1, neg/1, http/1, line/1, ssl/1, otp_8536/1, + init_per_testcase/2,end_per_testcase/2, + basic/1, packet_size/1, neg/1, http/1, line/1, ssl/1, otp_8536/1, otp_9389/1, otp_9389_line/1]). suite() -> @@ -49,40 +49,40 @@ end_per_testcase(_Func, _Config) -> ok. basic(Config) when is_list(Config) -> - ?line Packet = <<101,22,203,54,175>>, - ?line Rest = <<123,34,0,250>>, - ?line Bin = <>, - ?line {ok, Bin, <<>>} = decode_pkt(raw,Bin), + Packet = <<101,22,203,54,175>>, + Rest = <<123,34,0,250>>, + Bin = <>, + {ok, Bin, <<>>} = decode_pkt(raw,Bin), - ?line {more, 5+1} = decode_pkt(1,<<5,1,2,3,4>>), - ?line {more, 5+2} = decode_pkt(2,<<0,5,1,2,3,4>>), - ?line {more, 5+4} = decode_pkt(4,<<0,0,0,5,1,2,3,4>>), + {more, 5+1} = decode_pkt(1,<<5,1,2,3,4>>), + {more, 5+2} = decode_pkt(2,<<0,5,1,2,3,4>>), + {more, 5+4} = decode_pkt(4,<<0,0,0,5,1,2,3,4>>), - ?line {more, undefined} = decode_pkt(1,<<>>), - ?line {more, undefined} = decode_pkt(2,<<0>>), - ?line {more, undefined} = decode_pkt(4,<<0,0,0>>), + {more, undefined} = decode_pkt(1,<<>>), + {more, undefined} = decode_pkt(2,<<0>>), + {more, undefined} = decode_pkt(4,<<0,0,0>>), Types = [1,2,4,asn1,sunrm,cdr,fcgi,tpkt,ssl_tls], %% Run tests for different header types and bit offsets. lists:foreach(fun({Type,Bits})->basic_pack(Type,Packet,Rest,Bits), - more_length(Type,Packet,Bits) end, - [{T,B} || T<-Types, B<-lists:seq(0,32)]), + more_length(Type,Packet,Bits) end, + [{T,B} || T<-Types, B<-lists:seq(0,32)]), ok. basic_pack(Type,Body,Rest,BitOffs) -> - ?line {Bin,Unpacked,_} = pack(Type,Body,Rest,BitOffs), - ?line {ok, Unpacked, Rest} = decode_pkt(Type,Bin), + {Bin,Unpacked,_} = pack(Type,Body,Rest,BitOffs), + {ok, Unpacked, Rest} = decode_pkt(Type,Bin), case Rest of - <<>> -> ok; - _ -> - ?line <<_:1,NRest/bits>> = Rest, - basic_pack(Type,Body,NRest,BitOffs) + <<>> -> ok; + _ -> + <<_:1,NRest/bits>> = Rest, + basic_pack(Type,Body,NRest,BitOffs) end. more_length(Type,Body,BitOffs) -> - ?line {Bin,_,_} = pack(Type,Body,<<>>,BitOffs), + {Bin,_,_} = pack(Type,Body,<<>>,BitOffs), HdrSize = byte_size(Bin) - byte_size(Body), more_length_do(Type,HdrSize,Bin,byte_size(Bin)). @@ -91,17 +91,17 @@ more_length_do(_,_,_,0) -> more_length_do(Type,HdrSize,Bin,Size) -> TrySize = (Size*3) div 4, NSize = if TrySize < HdrSize -> Size - 1; - true -> TrySize - end, + true -> TrySize + end, {B1,_} = split_binary(Bin,NSize), - ?line {more, Length} = decode_pkt(Type,B1), + {more, Length} = decode_pkt(Type,B1), case Length of - L when L=:=byte_size(Bin) -> ok; - undefined when NSize ok + L when L=:=byte_size(Bin) -> ok; + undefined when NSize ok end, more_length_do(Type,HdrSize,Bin,NSize). - + pack(Type,Packet,Rest) -> {Bin,Unpacked} = pack(Type,Packet), @@ -133,28 +133,28 @@ pack(4,Bin) -> {<>, Bin}; pack(asn1,Bin) -> Ident = case rand:uniform(3) of - 1 -> <<17>>; - 2 -> <<16#1f,16#81,17>>; - 3 -> <<16#1f,16#81,16#80,16#80,17>> - end, + 1 -> <<17>>; + 2 -> <<16#1f,16#81,17>>; + 3 -> <<16#1f,16#81,16#80,16#80,17>> + end, Psz = byte_size(Bin), Length = case rand:uniform(4) of - 1 when Psz < 128 -> - <>; - R when R=<2 andalso Psz < 16#10000 -> - <<16#82,Psz:16>>; - R when R=<3 andalso Psz < 16#1000000 -> - <<16#83,Psz:24>>; - _ when Psz < 16#100000000 -> - <<16#84,Psz:32>> - end, + 1 when Psz < 128 -> + <>; + R when R=<2 andalso Psz < 16#10000 -> + <<16#82,Psz:16>>; + R when R=<3 andalso Psz < 16#1000000 -> + <<16#83,Psz:24>>; + _ when Psz < 16#100000000 -> + <<16#84,Psz:32>> + end, Res = <>, {Res,Res}; pack(sunrm,Bin) -> Psz = byte_size(Bin), Res = if Psz < 16#80000000 -> - <> - end, + <> + end, {Res,Res}; pack(cdr,Bin) -> GIOP = <<"GIOP">>, @@ -163,9 +163,9 @@ pack(cdr,Bin) -> MType = rand:uniform(256) - 1, Psz = byte_size(Bin), Res = case rand:uniform(2) of - 1 -> <>; - 2 -> <> - end, + 1 -> <>; + 2 -> <> + end, {Res,Res}; pack(fcgi,Bin) -> Ver = 1, @@ -175,10 +175,10 @@ pack(fcgi,Bin) -> Psz = byte_size(Bin), Reserv = rand:uniform(256) - 1, Padd = case PaddSz of - 0 -> <<>>; - _ -> list_to_binary([rand:uniform(256)-1 - || _<- lists:seq(1,PaddSz)]) - end, + 0 -> <<>>; + _ -> list_to_binary([rand:uniform(256)-1 + || _<- lists:seq(1,PaddSz)]) + end, Res = <>, {<>, Res}; pack(tpkt,Bin) -> @@ -189,149 +189,149 @@ pack(tpkt,Bin) -> {Res, Res}; pack(ssl_tls,Bin) -> Content = case (rand:uniform(256) - 1) of - C when C<128 -> C; - _ -> v2hello - end, + C when C<128 -> C; + _ -> v2hello + end, Major = rand:uniform(256) - 1, Minor = rand:uniform(256) - 1, pack_ssl(Content,Major,Minor,Bin). pack_ssl(Content, Major, Minor, Body) -> case Content of - v2hello -> - Size = byte_size(Body), - Res = <<1:1,(Size+3):15, 1:8, Major:8, Minor:8, Body/binary>>, - C = 22, - Data = <<1:8, (Size+2):24, Major:8, Minor:8, Body/binary>>; - C when is_integer(C) -> - Size = byte_size(Body), - Res = <>, - Data = Body + v2hello -> + Size = byte_size(Body), + Res = <<1:1,(Size+3):15, 1:8, Major:8, Minor:8, Body/binary>>, + C = 22, + Data = <<1:8, (Size+2):24, Major:8, Minor:8, Body/binary>>; + C when is_integer(C) -> + Size = byte_size(Body), + Res = <>, + Data = Body end, {Res, {ssl_tls,[],C,{Major,Minor}, Data}}. packet_size(Config) when is_list(Config) -> - ?line Packet = <<101,22,203,54,175>>, - ?line Rest = <<123,34,0,250>>, + Packet = <<101,22,203,54,175>>, + Rest = <<123,34,0,250>>, F = fun({Type,Max})-> - ?line {Bin,Unpacked} = pack(Type,Packet,Rest), - ?line case decode_pkt(Type,Bin,[{packet_size,Max}]) of - {ok,Unpacked,Rest} when Max=:=0; Max>=byte_size(Packet) -> - ok; - {error,_} when Max - ok; - {error,_} when Type=:=fcgi, Max=/=0 -> - %% packet includes random amount of padding - ok - end - end, - ?line lists:foreach(F, [{T,D} || T<-[1,2,4,asn1,sunrm,cdr,fcgi,tpkt,ssl_tls], - D<-lists:seq(0, byte_size(Packet)*2)]), + {Bin,Unpacked} = pack(Type,Packet,Rest), + case decode_pkt(Type,Bin,[{packet_size,Max}]) of + {ok,Unpacked,Rest} when Max=:=0; Max>=byte_size(Packet) -> + ok; + {error,_} when Max + ok; + {error,_} when Type=:=fcgi, Max=/=0 -> + %% packet includes random amount of padding + ok + end + end, + lists:foreach(F, [{T,D} || T<-[1,2,4,asn1,sunrm,cdr,fcgi,tpkt,ssl_tls], + D<-lists:seq(0, byte_size(Packet)*2)]), %% Test OTP-8102, "negative" 4-byte sizes. lists:foreach(fun(Size) -> - ?line {error,_} = decode_pkt(4,<>) - end, - lists:seq(-10,-1)), + {error,_} = decode_pkt(4,<>) + end, + lists:seq(-10,-1)), %% Test OTP-9389, long HTTP header lines. Opts = [{packet_size, 128}], Pkt = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /", string:chars($Y, 64), "\r\n\r\n"]), <> = Pkt, - ?line {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} = - erlang:decode_packet(http, Pkt1, Opts), - ?line {ok, {http_header,_,'Host',_,"localhost"}, Rest2} = - erlang:decode_packet(httph, Rest1, Opts), - ?line {more, undefined} = erlang:decode_packet(httph, Rest2, Opts), - ?line {ok, {http_header,_,"Link",_,_}, _} = - erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts), + {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} = + erlang:decode_packet(http, Pkt1, Opts), + {ok, {http_header,_,'Host',_,"localhost"}, Rest2} = + erlang:decode_packet(httph, Rest1, Opts), + {more, undefined} = erlang:decode_packet(httph, Rest2, Opts), + {ok, {http_header,_,"Link",_,_}, _} = + erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts), Pkt3 = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /", string:chars($Y, 129), "\r\n\r\n"]), - ?line {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest3} = - erlang:decode_packet(http, Pkt3, Opts), - ?line {ok, {http_header,_,'Host',_,"localhost"}, Rest4} = - erlang:decode_packet(httph, Rest3, Opts), - ?line {error, invalid} = erlang:decode_packet(httph, Rest4, Opts), + {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest3} = + erlang:decode_packet(http, Pkt3, Opts), + {ok, {http_header,_,'Host',_,"localhost"}, Rest4} = + erlang:decode_packet(httph, Rest3, Opts), + {error, invalid} = erlang:decode_packet(httph, Rest4, Opts), ok. neg(Config) when is_list(Config) -> - ?line Bin = <<"dummy">>, + Bin = <<"dummy">>, Fun = fun()->dummy end, - + BadargF = fun(T,B,Opts)-> {'EXIT',{badarg,_}} = (catch decode_pkt(T,B,Opts)) end, %% Invalid Type args lists:foreach(fun(T)-> BadargF(T,Bin,[]) end, - [3,-1,5,2.0,{2},unknown,[],"line",Bin,Fun,self()]), + [3,-1,5,2.0,{2},unknown,[],"line",Bin,Fun,self()]), %% Invalid Bin args lists:foreach(fun(B)-> BadargF(0,B,[]) end, - [3,2.0,unknown,[],"Bin",[Bin],{Bin},Fun,self()]), + [3,2.0,unknown,[],"Bin",[Bin],{Bin},Fun,self()]), %% Invalid options InvOpts = [2,false,self(),Bin,"Options",Fun, - packet_size,{packet_size},{packet_size,0,false}, - {packet_size,-1},{packet_size,100.0},{packet_size,false}, - {line_length,-1},{line_length,100.0},{line_length,false}], + packet_size,{packet_size},{packet_size,0,false}, + {packet_size,-1},{packet_size,100.0},{packet_size,false}, + {line_length,-1},{line_length,100.0},{line_length,false}], lists:foreach(fun(Opt)-> BadargF(0,Bin,Opt), - BadargF(0,Bin,[Opt]), - BadargF(0,Bin,[Opt,{packet_size,1000}]), - BadargF(0,Bin,[{packet_size,1000},Opt]) end, - InvOpts), + BadargF(0,Bin,[Opt]), + BadargF(0,Bin,[Opt,{packet_size,1000}]), + BadargF(0,Bin,[{packet_size,1000},Opt]) end, + InvOpts), ok. http(Config) when is_list(Config) -> - ?line <<"foo">> = http_do(http_request("foo")), - ?line <<" bar">> = http_do(http_request(" bar")), - ?line <<"Hello!">> = http_do(http_response("Hello!")), + <<"foo">> = http_do(http_request("foo")), + <<" bar">> = http_do(http_request(" bar")), + <<"Hello!">> = http_do(http_response("Hello!")), %% Test all known header atoms Val = "dummy value", ValB = list_to_binary(Val), Rest = <<"Rest">>, HdrF = fun(Str,N) -> - ?line StrA = list_to_atom(Str), - ?line StrB = list_to_binary(Str), - ?line Bin = <>, - ?line {ok, {http_header,N,StrA,undefined,Val}, Rest} = decode_pkt(httph,Bin), - ?line {ok, {http_header,N,StrA,undefined,ValB}, Rest} = decode_pkt(httph_bin,Bin), - ?line N + 1 - end, - ?line lists:foldl(HdrF, 1, http_hdr_strings()), + StrA = list_to_atom(Str), + StrB = list_to_binary(Str), + Bin = <>, + {ok, {http_header,N,StrA,undefined,Val}, Rest} = decode_pkt(httph,Bin), + {ok, {http_header,N,StrA,undefined,ValB}, Rest} = decode_pkt(httph_bin,Bin), + N + 1 + end, + lists:foldl(HdrF, 1, http_hdr_strings()), %% Test all known method atoms MethF = fun(Meth) -> - ?line MethA = list_to_atom(Meth), - ?line MethB = list_to_binary(Meth), - ?line Bin = <>, - ?line {ok, {http_request,MethA,{abs_path,"/invalid/url"},{1,0}}, - Rest} = decode_pkt(http,Bin), - ?line {ok, {http_request,MethA,{abs_path,<<"/invalid/url">>},{1,0}}, - Rest} = decode_pkt(http_bin,Bin) - end, - ?line lists:foreach(MethF, http_meth_strings()), + MethA = list_to_atom(Meth), + MethB = list_to_binary(Meth), + Bin = <>, + {ok, {http_request,MethA,{abs_path,"/invalid/url"},{1,0}}, + Rest} = decode_pkt(http,Bin), + {ok, {http_request,MethA,{abs_path,<<"/invalid/url">>},{1,0}}, + Rest} = decode_pkt(http_bin,Bin) + end, + lists:foreach(MethF, http_meth_strings()), %% Test all uri variants UriF = fun({Str,ResL,ResB}) -> - Bin = <<"GET ",(list_to_binary(Str))/binary," HTTP/1.1\r\n",Rest/binary>>, - {ok, {http_request, 'GET', ResL, {1,1}}, Rest} = decode_pkt(http,Bin), - {ok, {http_request, 'GET', ResB, {1,1}}, Rest} = decode_pkt(http_bin,Bin) - end, + Bin = <<"GET ",(list_to_binary(Str))/binary," HTTP/1.1\r\n",Rest/binary>>, + {ok, {http_request, 'GET', ResL, {1,1}}, Rest} = decode_pkt(http,Bin), + {ok, {http_request, 'GET', ResB, {1,1}}, Rest} = decode_pkt(http_bin,Bin) + end, lists:foreach(UriF, http_uri_variants()), %% Response with empty phrase - ?line {ok,{http_response,{1,1},200,[]},<<>>} = decode_pkt(http, <<"HTTP/1.1 200\r\n">>, []), - ?line {ok,{http_response,{1,1},200,<<>>},<<>>} = decode_pkt(http_bin, <<"HTTP/1.1 200\r\n">>, []), + {ok,{http_response,{1,1},200,[]},<<>>} = decode_pkt(http, <<"HTTP/1.1 200\r\n">>, []), + {ok,{http_response,{1,1},200,<<>>},<<>>} = decode_pkt(http_bin, <<"HTTP/1.1 200\r\n">>, []), ok. - + http_with_bin(http) -> http_bin; http_with_bin(httph) -> @@ -342,8 +342,8 @@ http_do(Tup) -> http_do({Bin, []}, _) -> Bin; http_do({Bin,[{_Line,PL,PB}|Tail]}, Type) -> - ?line {ok, PL, Rest} = decode_pkt(Type,Bin), - ?line {ok, PB, Rest} = decode_pkt(http_with_bin(Type),Bin), + {ok, PL, Rest} = decode_pkt(Type,Bin), + {ok, PB, Rest} = decode_pkt(http_with_bin(Type),Bin), %% Same tests again but as SubBin PreLen = rand:uniform(64), @@ -353,77 +353,77 @@ http_do({Bin,[{_Line,PL,PB}|Tail]}, Type) -> Orig = <>, BinLen = bit_size(Bin), <<_:PreLen, SubBin:BinLen/bits, _/bits>> = Orig, % Make SubBin - ?line SubBin = Bin, % just to make sure + SubBin = Bin, % just to make sure - ?line {ok, PL, Rest} = decode_pkt(Type,SubBin), - ?line {ok, PB, Rest} = decode_pkt(http_with_bin(Type),SubBin), + {ok, PL, Rest} = decode_pkt(Type,SubBin), + {ok, PB, Rest} = decode_pkt(http_with_bin(Type),SubBin), http_do({Rest, Tail}, httph). http_request(Msg) -> QnA = [{"POST /invalid/url HTTP/1.1\r\n", - {http_request, 'POST', {abs_path, "/invalid/url" }, {1,1}}, - {http_request, 'POST', {abs_path,<<"/invalid/url">>}, {1,1}}}, - {"Connection: close\r\n", - {http_header,2,'Connection',undefined, "close"}, - {http_header,2,'Connection',undefined,<<"close">>}}, - {"Host\t : localhost:8000\r\n", % white space before : - {http_header,14,'Host',undefined, "localhost:8000"}, - {http_header,14,'Host',undefined,<<"localhost:8000">>}}, - {"User-Agent: perl post\r\n", - {http_header,24,'User-Agent',undefined, "perl post"}, - {http_header,24,'User-Agent',undefined,<<"perl post">>}}, - {"Content-Length: 4\r\n", - {http_header,38,'Content-Length',undefined, "4"}, - {http_header,38,'Content-Length',undefined,<<"4">>}}, - {"Content-Type: text/xml; charset=utf-8\r\n", - {http_header,42,'Content-Type',undefined, "text/xml; charset=utf-8"}, - {http_header,42,'Content-Type',undefined,<<"text/xml; charset=utf-8">>}}, - {"Other-Field: with some text\r\n", - {http_header,0, "Other-Field" ,undefined, "with some text"}, - {http_header,0,<<"Other-Field">>,undefined,<<"with some text">>}}, - {"Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY: with some text\r\n", - {http_header,0, "Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely" ,undefined, "with some text"}, - {http_header,0,<<"Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely">>,undefined,<<"with some text">>}}, - {"Multi-Line: Once upon a time in a land far far away,\r\n" - " there lived a princess imprisoned in the highest tower\r\n" - " of the most haunted castle.\r\n", - {http_header,0, "Multi-Line" ,undefined, "Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle."}, - {http_header,0,<<"Multi-Line">>,undefined,<<"Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle.">>}}, - {"\r\n", - http_eoh, - http_eoh}], + {http_request, 'POST', {abs_path, "/invalid/url" }, {1,1}}, + {http_request, 'POST', {abs_path,<<"/invalid/url">>}, {1,1}}}, + {"Connection: close\r\n", + {http_header,2,'Connection',undefined, "close"}, + {http_header,2,'Connection',undefined,<<"close">>}}, + {"Host\t : localhost:8000\r\n", % white space before : + {http_header,14,'Host',undefined, "localhost:8000"}, + {http_header,14,'Host',undefined,<<"localhost:8000">>}}, + {"User-Agent: perl post\r\n", + {http_header,24,'User-Agent',undefined, "perl post"}, + {http_header,24,'User-Agent',undefined,<<"perl post">>}}, + {"Content-Length: 4\r\n", + {http_header,38,'Content-Length',undefined, "4"}, + {http_header,38,'Content-Length',undefined,<<"4">>}}, + {"Content-Type: text/xml; charset=utf-8\r\n", + {http_header,42,'Content-Type',undefined, "text/xml; charset=utf-8"}, + {http_header,42,'Content-Type',undefined,<<"text/xml; charset=utf-8">>}}, + {"Other-Field: with some text\r\n", + {http_header,0, "Other-Field" ,undefined, "with some text"}, + {http_header,0,<<"Other-Field">>,undefined,<<"with some text">>}}, + {"Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY: with some text\r\n", + {http_header,0, "Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely" ,undefined, "with some text"}, + {http_header,0,<<"Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely">>,undefined,<<"with some text">>}}, + {"Multi-Line: Once upon a time in a land far far away,\r\n" + " there lived a princess imprisoned in the highest tower\r\n" + " of the most haunted castle.\r\n", + {http_header,0, "Multi-Line" ,undefined, "Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle."}, + {http_header,0,<<"Multi-Line">>,undefined,<<"Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle.">>}}, + {"\r\n", + http_eoh, + http_eoh}], Bin = lists:foldl(fun({Line,_,_},Acc) -> LineBin = list_to_binary(Line), - <> end, - <<"">>, QnA), + <> end, + <<"">>, QnA), MsgBin = list_to_binary(Msg), {<>, QnA}. http_response(Msg) -> QnA = [{"HTTP/1.0 404 Object Not Found\r\n", - {http_response, {1,0}, 404, "Object Not Found"}, - {http_response, {1,0}, 404, <<"Object Not Found">>}}, - {"Server: inets/4.7.16\r\n", - {http_header, 30, 'Server', undefined, "inets/4.7.16"}, - {http_header, 30, 'Server', undefined, <<"inets/4.7.16">>}}, - {"Date: Fri, 04 Jul 2008 17:16:22 GMT\r\n", - {http_header, 3, 'Date', undefined, "Fri, 04 Jul 2008 17:16:22 GMT"}, - {http_header, 3, 'Date', undefined, <<"Fri, 04 Jul 2008 17:16:22 GMT">>}}, - {"Content-Type: text/html\r\n", - {http_header, 42, 'Content-Type', undefined, "text/html"}, - {http_header, 42, 'Content-Type', undefined, <<"text/html">>}}, - {"Content-Length: 207\r\n", - {http_header, 38, 'Content-Length', undefined, "207"}, - {http_header, 38, 'Content-Length', undefined, <<"207">>}}, - {"\r\n", - http_eoh, - http_eoh}], + {http_response, {1,0}, 404, "Object Not Found"}, + {http_response, {1,0}, 404, <<"Object Not Found">>}}, + {"Server: inets/4.7.16\r\n", + {http_header, 30, 'Server', undefined, "inets/4.7.16"}, + {http_header, 30, 'Server', undefined, <<"inets/4.7.16">>}}, + {"Date: Fri, 04 Jul 2008 17:16:22 GMT\r\n", + {http_header, 3, 'Date', undefined, "Fri, 04 Jul 2008 17:16:22 GMT"}, + {http_header, 3, 'Date', undefined, <<"Fri, 04 Jul 2008 17:16:22 GMT">>}}, + {"Content-Type: text/html\r\n", + {http_header, 42, 'Content-Type', undefined, "text/html"}, + {http_header, 42, 'Content-Type', undefined, <<"text/html">>}}, + {"Content-Length: 207\r\n", + {http_header, 38, 'Content-Length', undefined, "207"}, + {http_header, 38, 'Content-Length', undefined, <<"207">>}}, + {"\r\n", + http_eoh, + http_eoh}], Bin = lists:foldl(fun({Line,_,_},Acc) -> LineBin = list_to_binary(Line), - <> end, - <<"">>, QnA), + <> end, + <<"">>, QnA), MsgBin = list_to_binary(Msg), {<>, QnA}. @@ -466,52 +466,52 @@ http_uri_variants() -> line(Config) when is_list(Config) -> Text = <<"POST /invalid/url HTTP/1.1\r\n" - "Connection: close\r\n" - "Host\t : localhost:8000\r\n" - "User-Agent: perl post\r\n" - "Content-Length: 4\r\n" - "Content-Type: text/xml; charset=utf-8\r\n" - "Other-Field: with some text\r\n" - "Multi-Line: Once upon a time in a land far far away,\r\n" - " there lived a princess imprisoned in the highest tower\r\n" - " of the most haunted castle.\r\n" - "\r\nThe residue">>, + "Connection: close\r\n" + "Host\t : localhost:8000\r\n" + "User-Agent: perl post\r\n" + "Content-Length: 4\r\n" + "Content-Type: text/xml; charset=utf-8\r\n" + "Other-Field: with some text\r\n" + "Multi-Line: Once upon a time in a land far far away,\r\n" + " there lived a princess imprisoned in the highest tower\r\n" + " of the most haunted castle.\r\n" + "\r\nThe residue">>, lists:foreach(fun(MaxLen) -> line_do(Text,MaxLen) end, - [0,7,19,29,37]), + [0,7,19,29,37]), ok. line_do(Bin,MaxLen) -> Res = decode_pkt(line,Bin,[{line_length,MaxLen}]), MyRes = decode_line(Bin,MaxLen), - ?line MyRes = Res, + MyRes = Res, case Res of - {ok,_,Rest} -> - line_do(Rest,MaxLen); - {more,undefined} -> - ok + {ok,_,Rest} -> + line_do(Rest,MaxLen); + {more,undefined} -> + ok end. - + % Emulates decode_packet(line,Bin,[{line_length,MaxLen}]) decode_line(Bin,MaxLen) -> - ?line case find_in_binary($\n,Bin) of - notfound when MaxLen>0 andalso byte_size(Bin) >= MaxLen -> - {LineB,Rest} = split_binary(Bin,MaxLen), - {ok,LineB,Rest}; - notfound -> - {more,undefined}; - Pos when MaxLen>0 andalso Pos > MaxLen -> - {LineB,Rest} = split_binary(Bin,MaxLen), - {ok,LineB,Rest}; - Pos -> - {LineB,Rest} = split_binary(Bin,Pos), - {ok,LineB,Rest} + case find_in_binary($\n,Bin) of + notfound when MaxLen>0 andalso byte_size(Bin) >= MaxLen -> + {LineB,Rest} = split_binary(Bin,MaxLen), + {ok,LineB,Rest}; + notfound -> + {more,undefined}; + Pos when MaxLen>0 andalso Pos > MaxLen -> + {LineB,Rest} = split_binary(Bin,MaxLen), + {ok,LineB,Rest}; + Pos -> + {LineB,Rest} = split_binary(Bin,Pos), + {ok,LineB,Rest} end. find_in_binary(Byte, Bin) -> case string:chr(binary_to_list(Bin),Byte) of - 0 -> notfound; - P -> P + 0 -> notfound; + P -> P end. ssl(Config) when is_list(Config) -> @@ -521,10 +521,10 @@ ssl(Config) when is_list(Config) -> Rest = <<23,123,203,12,234>>, F = fun(Content) -> - {Packet,Unpacked} = pack_ssl(Content, Major, Minor, Body), - Bin = <>, - ?line {ok, Unpacked, Rest} = decode_pkt(ssl_tls, Bin) - end, + {Packet,Unpacked} = pack_ssl(Content, Major, Minor, Body), + Bin = <>, + {ok, Unpacked, Rest} = decode_pkt(ssl_tls, Bin) + end, F(25), F(v2hello), ok. @@ -542,7 +542,7 @@ otp_8536_do(N) -> Bin = <>, io:format("Bin='~p'\n",[Bin]), - ?line {ok,{http_header,0,Hdr2,undefined,Data2},<<"\r\n">>} = decode_pkt(httph_bin, Bin, []), + {ok,{http_header,0,Hdr2,undefined,Data2},<<"\r\n">>} = decode_pkt(httph_bin, Bin, []), %% Do something to trash the C-stack, how about another decode_packet: decode_pkt(httph_bin,<>, []), @@ -566,15 +566,15 @@ otp_9389(Config) when is_list(Config) -> "\r\nContent-Length: 0\r\n\r\n"]), <> = Pkt, {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} = - erlang:decode_packet(http, Pkt1, Opts), + erlang:decode_packet(http, Pkt1, Opts), {ok, {http_header,_,'Host',_,"localhost"}, Rest2} = - erlang:decode_packet(httph, Rest1, Opts), + erlang:decode_packet(httph, Rest1, Opts), {more, undefined} = erlang:decode_packet(httph, Rest2, Opts), {ok, {http_header,_,"Link",_,Link}, Rest3} = - erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts), + erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts), true = (length(Link) > 8000), {ok, {http_header,_,'Content-Length',_,"0"}, <<"\r\n">>} = - erlang:decode_packet(httph, Rest3, Opts), + erlang:decode_packet(httph, Rest3, Opts), ok. %% Verify packet_size works correctly for line mode @@ -584,7 +584,7 @@ otp_9389_line(Config) when is_list(Config) -> Line2 = <<"0123456789\n">>, Line3 = <<"01234567890123456789\n">>, Pkt = list_to_binary([Line1, Line2, Line3]), - ?line {ok, Line1, Rest1} = erlang:decode_packet(line, Pkt, Opts), - ?line {ok, Line2, Rest2} = erlang:decode_packet(line, Rest1, Opts), - ?line {error, invalid} = erlang:decode_packet(line, Rest2, Opts), + {ok, Line1, Rest1} = erlang:decode_packet(line, Pkt, Opts), + {ok, Line2, Rest2} = erlang:decode_packet(line, Rest1, Opts), + {error, invalid} = erlang:decode_packet(line, Rest2, Opts), ok. diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index d4d70632c2..f116ec979b 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -36,32 +36,32 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, groups/0, - ping/1, bulk_send_small/1, - bulk_send_big/1, bulk_send_bigbig/1, - local_send_small/1, local_send_big/1, - local_send_legal/1, link_to_busy/1, exit_to_busy/1, - lost_exit/1, link_to_dead/1, link_to_dead_new_node/1, - applied_monitor_node/1, ref_port_roundtrip/1, nil_roundtrip/1, - trap_bif_1/1, trap_bif_2/1, trap_bif_3/1, - stop_dist/1, - dist_auto_connect_never/1, dist_auto_connect_once/1, - dist_parallel_send/1, - atom_roundtrip/1, - unicode_atom_roundtrip/1, - atom_roundtrip_r15b/1, - contended_atom_cache_entry/1, - contended_unicode_atom_cache_entry/1, - bad_dist_structure/1, - bad_dist_ext_receive/1, - bad_dist_ext_process_info/1, - bad_dist_ext_control/1, - bad_dist_ext_connection_id/1]). + ping/1, bulk_send_small/1, + bulk_send_big/1, bulk_send_bigbig/1, + local_send_small/1, local_send_big/1, + local_send_legal/1, link_to_busy/1, exit_to_busy/1, + lost_exit/1, link_to_dead/1, link_to_dead_new_node/1, + applied_monitor_node/1, ref_port_roundtrip/1, nil_roundtrip/1, + trap_bif_1/1, trap_bif_2/1, trap_bif_3/1, + stop_dist/1, + dist_auto_connect_never/1, dist_auto_connect_once/1, + dist_parallel_send/1, + atom_roundtrip/1, + unicode_atom_roundtrip/1, + atom_roundtrip_r15b/1, + contended_atom_cache_entry/1, + contended_unicode_atom_cache_entry/1, + bad_dist_structure/1, + bad_dist_ext_receive/1, + bad_dist_ext_process_info/1, + bad_dist_ext_control/1, + bad_dist_ext_connection_id/1]). %% Internal exports. -export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0, - roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1, - dist_parallel_sender/3, dist_parallel_receiver/0, - dist_evil_parallel_receiver/0, + roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1, + dist_parallel_sender/3, dist_parallel_receiver/0, + dist_evil_parallel_receiver/0, sendersender/4, sendersender2/4]). suite() -> @@ -96,48 +96,47 @@ ping(Config) when is_list(Config) -> %% Ping a non-existing node many times. This used to crash the emulator %% on Windows. - ?line Host = hostname(), - ?line BadName = list_to_atom("__pucko__@" ++ Host), - ?line io:format("Pinging ~s (assumed to not exist)", [BadName]), - ?line test_server:do_times(Times, fun() -> pang = net_adm:ping(BadName) - end), + Host = hostname(), + BadName = list_to_atom("__pucko__@" ++ Host), + io:format("Pinging ~s (assumed to not exist)", [BadName]), + test_server:do_times(Times, fun() -> pang = net_adm:ping(BadName) + end), %% Pings another node. - ?line {ok, OtherNode} = start_node(distribution_SUITE_other), - ?line io:format("Pinging ~s (assumed to exist)", [OtherNode]), - ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(OtherNode) end), - ?line stop_node(OtherNode), + {ok, OtherNode} = start_node(distribution_SUITE_other), + io:format("Pinging ~s (assumed to exist)", [OtherNode]), + test_server:do_times(Times, fun() -> pong = net_adm:ping(OtherNode) end), + stop_node(OtherNode), %% Pings our own node many times. - ?line Node = node(), - ?line io:format("Pinging ~s (the same node)", [Node]), - ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(Node) end), + Node = node(), + io:format("Pinging ~s (the same node)", [Node]), + test_server:do_times(Times, fun() -> pong = net_adm:ping(Node) end), ok. bulk_send_small(Config) when is_list(Config) -> - ?line bulk_send(64, 32). + bulk_send(64, 32). bulk_send_big(Config) when is_list(Config) -> - ?line bulk_send(32, 64). + bulk_send(32, 64). bulk_send_bigbig(Config) when is_list(Config) -> - ?line bulk_sendsend(32*5, 4). + bulk_sendsend(32*5, 4). bulk_send(Terms, BinSize) -> ct:timetrap({seconds, 30}), - ?line io:format("Sending ~w binaries, each of size ~w K", - [Terms, BinSize]), - ?line {ok, Node} = start_node(bulk_receiver), - ?line Recv = spawn(Node, erlang, apply, [fun receiver/2, [0, 0]]), - ?line Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)), - ?line Size = Terms*size(Bin), - ?line {Elapsed, {Terms, Size}} = test_server:timecall(?MODULE, sender, - [Recv, Bin, Terms]), - ?line stop_node(Node), + io:format("Sending ~w binaries, each of size ~w K", [Terms, BinSize]), + {ok, Node} = start_node(bulk_receiver), + Recv = spawn(Node, erlang, apply, [fun receiver/2, [0, 0]]), + Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)), + Size = Terms*size(Bin), + {Elapsed, {Terms, Size}} = test_server:timecall(?MODULE, sender, + [Recv, Bin, Terms]), + stop_node(Node), {comment, integer_to_list(trunc(Size/1024/max(1,Elapsed)+0.5)) ++ " K/s"}. bulk_sendsend(Terms, BinSize) -> @@ -147,29 +146,29 @@ bulk_sendsend(Terms, BinSize) -> true -> MonitorCount1 / MonitorCount2 end, Comment = integer_to_list(Rate1) ++ " K/s, " ++ - integer_to_list(Rate2) ++ " K/s, " ++ - integer_to_list(MonitorCount1) ++ " monitor msgs, " ++ - integer_to_list(MonitorCount2) ++ " monitor msgs, " ++ - float_to_list(Ratio) ++ " monitor ratio", + integer_to_list(Rate2) ++ " K/s, " ++ + integer_to_list(MonitorCount1) ++ " monitor msgs, " ++ + integer_to_list(MonitorCount2) ++ " monitor msgs, " ++ + float_to_list(Ratio) ++ " monitor ratio", if - %% A somewhat arbitrary ratio, but hopefully one that will - %% accommodate a wide range of CPU speeds. - Ratio > 8.0 -> - {comment,Comment}; - true -> - io:put_chars(Comment), - ?line ct:fail(ratio_too_low) + %% A somewhat arbitrary ratio, but hopefully one that will + %% accommodate a wide range of CPU speeds. + Ratio > 8.0 -> + {comment,Comment}; + true -> + io:put_chars(Comment), + ct:fail(ratio_too_low) end. bulk_sendsend2(Terms, BinSize, BusyBufSize) -> ct:timetrap({seconds, 30}), - ?line io:format("Sending ~w binaries, each of size ~w K", - [Terms, BinSize]), - ?line {ok, NodeRecv} = start_node(bulk_receiver), - ?line Recv = spawn(NodeRecv, erlang, apply, [fun receiver/2, [0, 0]]), - ?line Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)), - %%?line Size = Terms*size(Bin), + io:format("Sending ~w binaries, each of size ~w K", + [Terms, BinSize]), + {ok, NodeRecv} = start_node(bulk_receiver), + Recv = spawn(NodeRecv, erlang, apply, [fun receiver/2, [0, 0]]), + Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)), + %%Size = Terms*size(Bin), %% SLF LEFT OFF HERE. %% When the caller uses small hunks, like 4k via @@ -180,21 +179,21 @@ bulk_sendsend2(Terms, BinSize, BusyBufSize) -> %% default busy size and "+zdbbl 5", and if the 5 case gets %% "many many more" monitor messages, then we know we're working. - ?line {ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++ integer_to_list(BusyBufSize)), - ?line _Send = spawn(NodeSend, erlang, apply, [fun sendersender/4, [self(), Recv, Bin, Terms]]), - ?line {Elapsed, {_TermsN, SizeN}, MonitorCount} = - receive {sendersender, BigRes} -> + {ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++ integer_to_list(BusyBufSize)), + _Send = spawn(NodeSend, erlang, apply, [fun sendersender/4, [self(), Recv, Bin, Terms]]), + {Elapsed, {_TermsN, SizeN}, MonitorCount} = + receive {sendersender, BigRes} -> BigRes - end, - ?line stop_node(NodeRecv), - ?line stop_node(NodeSend), + end, + stop_node(NodeRecv), + stop_node(NodeSend), {trunc(SizeN/1024/Elapsed+0.5), MonitorCount}. sender(To, _Bin, 0) -> To ! {done, self()}, receive - Any -> - Any + Any -> + Any end; sender(To, Bin, Left) -> To ! {term, Bin}, @@ -205,9 +204,9 @@ sender(To, Bin, Left) -> sendersender(Parent, To, Bin, Left) -> erlang:system_monitor(self(), [busy_dist_port]), [spawn(fun() -> sendersender2(To, Bin, Left, false) end) || - _ <- lists:seq(1,1)], + _ <- lists:seq(1,1)], {USec, {Res, MonitorCount}} = - timer:tc(?MODULE, sendersender2, [To, Bin, Left, true]), + timer:tc(?MODULE, sendersender2, [To, Bin, Left, true]), Parent ! {sendersender, {USec/1000000, Res, MonitorCount}}. sendersender2(To, Bin, Left, SendDone) -> @@ -215,22 +214,22 @@ sendersender2(To, Bin, Left, SendDone) -> sendersender3(To, _Bin, 0, SendDone, MonitorCount) -> if SendDone -> - To ! {done, self()}; + To ! {done, self()}; true -> - ok + ok end, receive {monitor, _Pid, _Type, _Info} -> sendersender3(To, _Bin, 0, SendDone, MonitorCount + 1) after 0 -> - if SendDone -> - receive - Any when is_tuple(Any), size(Any) == 2 -> - {Any, MonitorCount} - end; - true -> - exit(normal) - end + if SendDone -> + receive + Any when is_tuple(Any), size(Any) == 2 -> + {Any, MonitorCount} + end; + true -> + exit(normal) + end end; sendersender3(To, Bin, Left, SendDone, MonitorCount) -> To ! {term, Bin}, @@ -241,10 +240,10 @@ sendersender3(To, Bin, Left, SendDone, MonitorCount) -> receiver(Terms, Size) -> receive - {term, Bin} -> - receiver(Terms+1, Size+size(Bin)); - {done, ReplyTo} -> - ReplyTo ! {Terms, Size} + {term, Bin} -> + receiver(Terms+1, Size+size(Bin)); + {done, ReplyTo} -> + ReplyTo ! {Terms, Size} end. @@ -255,16 +254,16 @@ local_send_big(Config) when is_list(Config) -> "local registered process."], Data1=[Data0,[Data0, Data0, [Data0], Data0],Data0], Data2=Data0++lists:flatten(Data1)++ - list_to_binary(lists:flatten(Data1)), + list_to_binary(lists:flatten(Data1)), Func=fun() -> Data2= {arbitrary_name, node()} ! Data2 end, - ?line test_server:do_times(4096, Func), + test_server:do_times(4096, Func), ok. %% Sends a small message to an non-registered process on the local node. local_send_small(Config) when is_list(Config) -> Data={some_stupid, "arbitrary", 'Data'}, Func=fun() -> Data= {unregistered_name, node()} ! Data end, - ?line test_server:do_times(4096, Func), + test_server:do_times(4096, Func), ok. %% Sends data to a registered process on the local node, as if it was on another node. @@ -273,42 +272,42 @@ local_send_legal(Config) when is_list(Config) -> Txt = "Some Not so random Data", Data={[Txt,Txt,Txt], [Txt,Txt,Txt]}, Pid=spawn(?MODULE,receiver2, [0, 0]) , - ?line true=register(registered_process, Pid), + true=register(registered_process, Pid), Func=fun() -> Data={registered_process, node()} ! Data end, TotalSize=size(Data)*Times, - ?line test_server:do_times(Times, Func), + test_server:do_times(Times, Func), % Check that all msgs really came through. Me=self(), - ?line {done, Me}= - {registered_process, node()} ! {done, Me}, + {done, Me}= + {registered_process, node()} ! {done, Me}, receive - {Times, TotalSize} -> - ok; - _ -> - ct:fail("Wrong number of msgs received.") + {Times, TotalSize} -> + ok; + _ -> + ct:fail("Wrong number of msgs received.") end, ok. receiver2(Num, TotSize) -> receive - {done, ReplyTo} -> - ReplyTo ! {Num, TotSize}; - Stuff -> - receiver2(Num+1, TotSize+size(Stuff)) + {done, ReplyTo} -> + ReplyTo ! {Num, TotSize}; + Stuff -> + receiver2(Num+1, TotSize+size(Stuff)) end. %% Test that link/1 to a busy distribution port works. link_to_busy(Config) when is_list(Config) -> ct:timetrap({seconds, 60}), - ?line {ok, Node} = start_node(link_to_busy), - ?line Recv = spawn(Node, erlang, apply, [fun sink/1, [link_to_busy_sink]]), + {ok, Node} = start_node(link_to_busy), + Recv = spawn(Node, erlang, apply, [fun sink/1, [link_to_busy_sink]]), Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of - "true" -> start_busy_dist_port_tracer(); - _ -> false - end, + "true" -> start_busy_dist_port_tracer(); + _ -> false + end, %% We will spawn off a process which will try to link to the other %% node. The linker process will not actually run until this @@ -317,19 +316,19 @@ link_to_busy(Config) when is_list(Config) -> %% process will block, too, because of the because busy port, %% and will later be restarted. - ?line do_busy_test(Node, fun () -> linker(Recv) end), + do_busy_test(Node, fun () -> linker(Recv) end), %% Same thing, but we apply link/1 instead of calling it directly. - ?line do_busy_test(Node, fun () -> applied_linker(Recv) end), + do_busy_test(Node, fun () -> applied_linker(Recv) end), %% Same thing again, but we apply link/1 in the tail of a function. - ?line do_busy_test(Node, fun () -> tail_applied_linker(Recv) end), + do_busy_test(Node, fun () -> tail_applied_linker(Recv) end), %% Done. - ?line stop_node(Node), - ?line stop_busy_dist_port_tracer(Tracer), + stop_node(Node), + stop_busy_dist_port_tracer(Tracer), ok. linker(Pid) -> @@ -344,16 +343,16 @@ applied_linker(Pid) -> tail_applied_linker(Pid) -> apply(erlang, link, [Pid]). - + %% Test that exit/2 to a busy distribution port works. exit_to_busy(Config) when is_list(Config) -> ct:timetrap({seconds, 60}), - ?line {ok, Node} = start_node(exit_to_busy), + {ok, Node} = start_node(exit_to_busy), Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of - "true" -> start_busy_dist_port_tracer(); - _ -> false - end, + "true" -> start_busy_dist_port_tracer(); + _ -> false + end, %% We will spawn off a process which will try to exit a process on %% the other node. That process will not actually run until this @@ -362,58 +361,58 @@ exit_to_busy(Config) when is_list(Config) -> %% too, because of the busy distribution port, and will be allowed %% to continue when the port becomes non-busy. - ?line Recv1 = spawn(Node, fun () -> sink(exit_to_busy_sink) end), - ?line M1 = erlang:monitor(process, Recv1), - ?line do_busy_test(Node, fun () -> joey_killer(Recv1) end), - ?line receive - {'DOWN', M1, process, Recv1, R1} -> - ?line joey_said_die = R1 - end, + Recv1 = spawn(Node, fun () -> sink(exit_to_busy_sink) end), + M1 = erlang:monitor(process, Recv1), + do_busy_test(Node, fun () -> joey_killer(Recv1) end), + receive + {'DOWN', M1, process, Recv1, R1} -> + joey_said_die = R1 + end, %% Same thing, but tail call to exit/2. - ?line Recv2 = spawn(Node, fun () -> sink(exit_to_busy_sink) end), - ?line M2 = erlang:monitor(process, Recv2), - ?line do_busy_test(Node, fun () -> tail_joey_killer(Recv2) end), - ?line receive - {'DOWN', M2, process, Recv2, R2} -> - ?line joey_said_die = R2 - end, + Recv2 = spawn(Node, fun () -> sink(exit_to_busy_sink) end), + M2 = erlang:monitor(process, Recv2), + do_busy_test(Node, fun () -> tail_joey_killer(Recv2) end), + receive + {'DOWN', M2, process, Recv2, R2} -> + joey_said_die = R2 + end, %% Same thing, but we apply exit/2 instead of calling it directly. - ?line Recv3 = spawn(Node, fun () -> sink(exit_to_busy_sink) end), - ?line M3 = erlang:monitor(process, Recv3), - ?line do_busy_test(Node, fun () -> applied_joey_killer(Recv3) end), - ?line receive - {'DOWN', M3, process, Recv3, R3} -> - ?line joey_said_die = R3 - end, + Recv3 = spawn(Node, fun () -> sink(exit_to_busy_sink) end), + M3 = erlang:monitor(process, Recv3), + do_busy_test(Node, fun () -> applied_joey_killer(Recv3) end), + receive + {'DOWN', M3, process, Recv3, R3} -> + joey_said_die = R3 + end, %% Same thing again, but we apply exit/2 in the tail of a function. - ?line Recv4 = spawn(Node, fun () -> sink(exit_to_busy_sink) end), - ?line M4 = erlang:monitor(process, Recv4), - ?line do_busy_test(Node, fun () -> tail_applied_joey_killer(Recv4) end), - ?line receive - {'DOWN', M4, process, Recv4, R4} -> - ?line joey_said_die = R4 - end, - + Recv4 = spawn(Node, fun () -> sink(exit_to_busy_sink) end), + M4 = erlang:monitor(process, Recv4), + do_busy_test(Node, fun () -> tail_applied_joey_killer(Recv4) end), + receive + {'DOWN', M4, process, Recv4, R4} -> + joey_said_die = R4 + end, + %% Done. - ?line stop_node(Node), - ?line stop_busy_dist_port_tracer(Tracer), + stop_node(Node), + stop_busy_dist_port_tracer(Tracer), ok. make_busy_data() -> Size = 1024*1024, Key = '__busy__port__data__', case get(Key) of - undefined -> - Data = list_to_binary(lists:duplicate(Size, 253)), - put(Key, Data), - Data; - Data -> - true = is_binary(Data), - true = size(Data) == Size, - Data + undefined -> + Data = list_to_binary(lists:duplicate(Size, 253)), + put(Key, Data), + Data; + Data -> + true = is_binary(Data), + true = size(Data) == Size, + Data end. make_busy(Node, Time) when is_integer(Time) -> @@ -422,27 +421,27 @@ make_busy(Node, Time) when is_integer(Time) -> Data = make_busy_data(), %% first make port busy Pid = spawn_link(fun () -> - forever(fun () -> - dport_reg_send(Node, - '__noone__', - Data) - end) - end), + forever(fun () -> + dport_reg_send(Node, + '__noone__', + Data) + end) + end), receive after Own -> ok end, until(fun () -> - case process_info(Pid, status) of - {status, suspended} -> true; - _ -> false - end - end), + case process_info(Pid, status) of + {status, suspended} -> true; + _ -> false + end + end), %% then dist entry make_busy(Node, [nosuspend], Data), Pid. make_busy(Node, Opts, Data) -> case erlang:send({'__noone__', Node}, Data, Opts) of - nosuspend -> nosuspend; - _ -> make_busy(Node, Opts, Data) + nosuspend -> nosuspend; + _ -> make_busy(Node, Opts, Data) end. unmake_busy(Pid) -> @@ -457,27 +456,27 @@ do_busy_test(Node, Fun) -> unmake_busy(Busy), io:format("~p : ~p~n", [P, Pinfo]), case Pinfo of - undefined -> - receive - {'DOWN', M, process, P, Reason} -> - io:format("~p died with exit reason ~p~n", [P, Reason]) - end, - ct:fail(premature_death); - _ -> - %% Don't match arity; it is different in debug and - %% optimized emulator - [{status, suspended}, - {current_function, {erlang, bif_return_trap, _}}] = Pinfo, - receive - {'DOWN', M, process, P, Reason} -> - io:format("~p died with exit reason ~p~n", [P, Reason]), - normal = Reason - end + undefined -> + receive + {'DOWN', M, process, P, Reason} -> + io:format("~p died with exit reason ~p~n", [P, Reason]) + end, + ct:fail(premature_death); + _ -> + %% Don't match arity; it is different in debug and + %% optimized emulator + [{status, suspended}, + {current_function, {erlang, bif_return_trap, _}}] = Pinfo, + receive + {'DOWN', M, process, P, Reason} -> + io:format("~p died with exit reason ~p~n", [P, Reason]), + normal = Reason + end end. remote_is_process_alive(Pid) -> rpc:call(node(Pid), erlang, is_process_alive, - [Pid]). + [Pid]). joey_killer(Pid) -> exit(Pid, joey_said_die), @@ -499,159 +498,159 @@ sink(Name) -> sink1() -> receive - _Any -> sink1() + _Any -> sink1() end. %% Test that EXIT and DOWN messages send to another node are not lost if %% the distribution port is busy. lost_exit(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(lost_exit), + {ok, Node} = start_node(lost_exit), Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of - "true" -> start_busy_dist_port_tracer(); - _ -> false - end, + "true" -> start_busy_dist_port_tracer(); + _ -> false + end, Self = self(), Die = make_ref(), - ?line R1 = spawn(fun () -> receive after infinity -> ok end end), - ?line MR1 = erlang:monitor(process, R1), - - ?line {L1, ML1} = spawn_monitor(fun() -> - link(R1), - Self ! {self(), linked}, - receive - Die -> - exit(controlled_suicide) - end - end), - - ?line R2 = spawn(fun () -> - M = erlang:monitor(process, L1), - receive - {'DOWN', M, process, L1, R} -> - Self ! {self(), got_down_message, L1, R} - end - end), - - ?line receive {L1, linked} -> ok end, - + R1 = spawn(fun () -> receive after infinity -> ok end end), + MR1 = erlang:monitor(process, R1), + + {L1, ML1} = spawn_monitor(fun() -> + link(R1), + Self ! {self(), linked}, + receive + Die -> + exit(controlled_suicide) + end + end), + + R2 = spawn(fun () -> + M = erlang:monitor(process, L1), + receive + {'DOWN', M, process, L1, R} -> + Self ! {self(), got_down_message, L1, R} + end + end), + + receive {L1, linked} -> ok end, + Busy = make_busy(Node, 2000), receive after 100 -> ok end, L1 ! Die, - ?line receive - {'DOWN', ML1, process, L1, RL1} -> - ?line controlled_suicide = RL1 - end, + receive + {'DOWN', ML1, process, L1, RL1} -> + controlled_suicide = RL1 + end, receive after 500 -> ok end, unmake_busy(Busy), - ?line receive - {'DOWN', MR1, process, R1, RR1} -> - ?line controlled_suicide = RR1 - end, - - ?line receive - {R2, got_down_message, L1, RR2} -> - ?line controlled_suicide = RR2 - end, + receive + {'DOWN', MR1, process, R1, RR1} -> + controlled_suicide = RR1 + end, + + receive + {R2, got_down_message, L1, RR2} -> + controlled_suicide = RR2 + end, %% Done. - ?line stop_busy_dist_port_tracer(Tracer), - ?line stop_node(Node), + stop_busy_dist_port_tracer(Tracer), + stop_node(Node), ok. dummy_waiter() -> receive after infinity -> - ok + ok end. %% Test that linking to a dead remote process gives an EXIT message %% AND that the link is teared down. link_to_dead(Config) when is_list(Config) -> - ?line process_flag(trap_exit, true), - ?line {ok, Node} = start_node(link_to_dead), -% ?line monitor_node(Node, true), - ?line net_adm:ping(Node), %% Ts_cross_server workaround. - ?line Pid = spawn(Node, ?MODULE, dead_process, []), + process_flag(trap_exit, true), + {ok, Node} = start_node(link_to_dead), + % monitor_node(Node, true), + net_adm:ping(Node), %% Ts_cross_server workaround. + Pid = spawn(Node, ?MODULE, dead_process, []), receive after 5000 -> ok end, - ?line link(Pid), - ?line receive - {'EXIT', Pid, noproc} -> - ok; - Other -> - ?line ct:fail({unexpected_message, Other}) - after 5000 -> - ?line ct:fail(nothing_received) - end, - ?line {links, Links} = process_info(self(), links), - ?line io:format("Pid=~p, links=~p", [Pid, Links]), - ?line false = lists:member(Pid, Links), - ?line stop_node(Node), - ?line receive - Message -> - ?line ct:fail({unexpected_message, Message}) - after 3000 -> - ok - end, + link(Pid), + receive + {'EXIT', Pid, noproc} -> + ok; + Other -> + ct:fail({unexpected_message, Other}) + after 5000 -> + ct:fail(nothing_received) + end, + {links, Links} = process_info(self(), links), + io:format("Pid=~p, links=~p", [Pid, Links]), + false = lists:member(Pid, Links), + stop_node(Node), + receive + Message -> + ct:fail({unexpected_message, Message}) + after 3000 -> + ok + end, ok. - + dead_process() -> erlang:error(die). %% Test that linking to a pid on node that has gone and restarted gives %% the correct EXIT message (OTP-2304). link_to_dead_new_node(Config) when is_list(Config) -> - ?line process_flag(trap_exit, true), + process_flag(trap_exit, true), %% Start the node, get a Pid and stop the node again. - ?line {ok, Node} = start_node(link_to_dead_new_node), - ?line Pid = spawn(Node, ?MODULE, dead_process, []), - ?line stop_node(Node), + {ok, Node} = start_node(link_to_dead_new_node), + Pid = spawn(Node, ?MODULE, dead_process, []), + stop_node(Node), %% Start a new node with the same name. - ?line {ok, Node} = start_node(link_to_dead_new_node), - ?line link(Pid), - ?line receive - {'EXIT', Pid, noproc} -> - ok; - Other -> - ?line ct:fail({unexpected_message, Other}) - after 5000 -> - ?line ct:fail(nothing_received) - end, + {ok, Node} = start_node(link_to_dead_new_node), + link(Pid), + receive + {'EXIT', Pid, noproc} -> + ok; + Other -> + ct:fail({unexpected_message, Other}) + after 5000 -> + ct:fail(nothing_received) + end, %% Make sure that the link wasn't created. - ?line {links, Links} = process_info(self(), links), - ?line io:format("Pid=~p, links=~p", [Pid, Links]), - ?line false = lists:member(Pid, Links), - ?line stop_node(Node), - ?line receive - Message -> - ?line ct:fail({unexpected_message, Message}) - after 3000 -> - ok - end, + {links, Links} = process_info(self(), links), + io:format("Pid=~p, links=~p", [Pid, Links]), + false = lists:member(Pid, Links), + stop_node(Node), + receive + Message -> + ct:fail({unexpected_message, Message}) + after 3000 -> + ok + end, ok. %% Test that monitor_node/2 works when applied. applied_monitor_node(Config) when is_list(Config) -> - ?line NonExisting = list_to_atom("__non_existing__@" ++ hostname()), + NonExisting = list_to_atom("__non_existing__@" ++ hostname()), %% Tail-recursive call to apply (since the node is non-existing, %% there will be a trap). - ?line true = tail_apply(erlang, monitor_node, [NonExisting, true]), - ?line [{nodedown, NonExisting}] = test_server:messages_get(), + true = tail_apply(erlang, monitor_node, [NonExisting, true]), + [{nodedown, NonExisting}] = test_server:messages_get(), %% Ordinary call (with trap). - ?line true = apply(erlang, monitor_node, [NonExisting, true]), - ?line [{nodedown, NonExisting}] = test_server:messages_get(), - + true = apply(erlang, monitor_node, [NonExisting, true]), + [{nodedown, NonExisting}] = test_server:messages_get(), + ok. tail_apply(M, F, A) -> @@ -660,26 +659,26 @@ tail_apply(M, F, A) -> %% Test that sending a port or reference to another node and back again %% doesn't correct them in any way. ref_port_roundtrip(Config) when is_list(Config) -> - ?line process_flag(trap_exit, true), - ?line Port = open_port({spawn, efile}, []), - ?line Ref = make_ref(), - ?line {ok, Node} = start_node(ref_port_roundtrip), - ?line net_adm:ping(Node), - ?line Term = {Port, Ref}, - ?line io:format("Term before: ~p", [show_term(Term)]), - ?line Pid = spawn_link(Node, ?MODULE, roundtrip, [Term]), - ?line receive after 5000 -> ok end, - ?line stop_node(Node), - ?line receive - {'EXIT', Pid, {Port, Ref}} -> - ?line io:format("Term after: ~p", [show_term(Term)]), - ok; - Other -> - ?line io:format("Term after: ~p", [show_term(Term)]), - ?line ct:fail({unexpected, Other}) - after 10000 -> - ?line ct:fail(timeout) - end, + process_flag(trap_exit, true), + Port = open_port({spawn, efile}, []), + Ref = make_ref(), + {ok, Node} = start_node(ref_port_roundtrip), + net_adm:ping(Node), + Term = {Port, Ref}, + io:format("Term before: ~p", [show_term(Term)]), + Pid = spawn_link(Node, ?MODULE, roundtrip, [Term]), + receive after 5000 -> ok end, + stop_node(Node), + receive + {'EXIT', Pid, {Port, Ref}} -> + io:format("Term after: ~p", [show_term(Term)]), + ok; + Other -> + io:format("Term after: ~p", [show_term(Term)]), + ct:fail({unexpected, Other}) + after 10000 -> + ct:fail(timeout) + end, ok. roundtrip(Term) -> @@ -688,24 +687,24 @@ roundtrip(Term) -> %% Test that the smallest external term [] aka NIL can be sent to %% another node node and back again. nil_roundtrip(Config) when is_list(Config) -> - ?line process_flag(trap_exit, true), - ?line {ok, Node} = start_node(nil_roundtrip), - ?line net_adm:ping(Node), - ?line Pid = spawn_link(Node, ?MODULE, bounce, [self()]), - ?line Pid ! [], - ?line receive - [] -> - ?line receive - {'EXIT', Pid, []} -> - ?line stop_node(Node), - ok - end - end. + process_flag(trap_exit, true), + {ok, Node} = start_node(nil_roundtrip), + net_adm:ping(Node), + Pid = spawn_link(Node, ?MODULE, bounce, [self()]), + Pid ! [], + receive + [] -> + receive + {'EXIT', Pid, []} -> + stop_node(Node), + ok + end + end. bounce(Dest) -> receive Msg -> - Dest ! Msg, - exit(Msg) + Dest ! Msg, + exit(Msg) end. show_term(Term) -> @@ -713,13 +712,13 @@ show_term(Term) -> %% Tests behaviour after net_kernel:stop (OTP-2586). stop_dist(Config) when is_list(Config) -> - ?line Str = os:cmd(atom_to_list(lib:progname()) - ++ " -noshell -pa " - ++ proplists:get_value(data_dir, Config) - ++ " -s run"), + Str = os:cmd(atom_to_list(lib:progname()) + ++ " -noshell -pa " + ++ proplists:get_value(data_dir, Config) + ++ " -s run"), %% The "true" may be followed by an error report, so ignore anything that %% follows it. - ?line "true\n"++_ = Str, + "true\n"++_ = Str, %% "May fail on FreeBSD due to differently configured name lookup - ask Arndt", %% if you can find him. @@ -728,30 +727,30 @@ stop_dist(Config) when is_list(Config) -> trap_bif_1(Config) when is_list(Config) -> - ?line {true} = tr1(), + {true} = tr1(), ok. trap_bif_2(Config) when is_list(Config) -> - ?line {true} = tr2(), + {true} = tr2(), ok. trap_bif_3(Config) when is_list(Config) -> - ?line {hoo} = tr3(), + {hoo} = tr3(), ok. tr1() -> - ?line NonExisting = 'abc@boromir', - ?line X = erlang:monitor_node(NonExisting, true), + NonExisting = 'abc@boromir', + X = erlang:monitor_node(NonExisting, true), {X}. tr2() -> - ?line NonExisting = 'abc@boromir', - ?line X = apply(erlang, monitor_node, [NonExisting, true]), + NonExisting = 'abc@boromir', + X = apply(erlang, monitor_node, [NonExisting, true]), {X}. tr3() -> - ?line NonExisting = 'abc@boromir', - ?line X = {NonExisting, glirp} ! hoo, + NonExisting = 'abc@boromir', + X = {NonExisting, glirp} ! hoo, {X}. @@ -775,58 +774,58 @@ tr3() -> %% Test the dist_auto_connect once kernel parameter dist_auto_connect_once(Config) when is_list(Config) -> - ?line Sock = start_relay_node(dist_auto_connect_relay_node,[]), - ?line NN = inet_rpc_nodename(Sock), - ?line Sock2 = start_relay_node(dist_auto_connect_once_node, - "-kernel dist_auto_connect once"), - ?line NN2 = inet_rpc_nodename(Sock2), - ?line {ok,[]} = do_inet_rpc(Sock,erlang,nodes,[]), - ?line {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]), - ?line {ok,[NN2]} = do_inet_rpc(Sock,erlang,nodes,[]), - ?line {ok,[NN]} = do_inet_rpc(Sock2,erlang,nodes,[]), - ?line [_,HostPartPeer] = string:tokens(atom_to_list(NN),"@"), - ?line [_,MyHostPart] = string:tokens(atom_to_list(node()),"@"), + Sock = start_relay_node(dist_auto_connect_relay_node,[]), + NN = inet_rpc_nodename(Sock), + Sock2 = start_relay_node(dist_auto_connect_once_node, + "-kernel dist_auto_connect once"), + NN2 = inet_rpc_nodename(Sock2), + {ok,[]} = do_inet_rpc(Sock,erlang,nodes,[]), + {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]), + {ok,[NN2]} = do_inet_rpc(Sock,erlang,nodes,[]), + {ok,[NN]} = do_inet_rpc(Sock2,erlang,nodes,[]), + [_,HostPartPeer] = string:tokens(atom_to_list(NN),"@"), + [_,MyHostPart] = string:tokens(atom_to_list(node()),"@"), % Give net_kernel a chance to change the state of the node to up to. - ?line receive after 1000 -> ok end, + receive after 1000 -> ok end, case HostPartPeer of - MyHostPart -> - ?line ok = stop_relay_node(Sock), - ?line {ok,pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]); - _ -> - ?line {ok, true} = do_inet_rpc(Sock,net_kernel,disconnect,[NN2]), - receive - after 500 -> ok - end + MyHostPart -> + ok = stop_relay_node(Sock), + {ok,pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]); + _ -> + {ok, true} = do_inet_rpc(Sock,net_kernel,disconnect,[NN2]), + receive + after 500 -> ok + end end, - ?line {ok, []} = do_inet_rpc(Sock2,erlang,nodes,[]), + {ok, []} = do_inet_rpc(Sock2,erlang,nodes,[]), Sock3 = case HostPartPeer of - MyHostPart -> - ?line start_relay_node(dist_auto_connect_relay_node,[]); - _ -> - Sock - end, - ?line TS1 = timestamp(), - ?line {ok, pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]), - ?line TS2 = timestamp(), + MyHostPart -> + start_relay_node(dist_auto_connect_relay_node,[]); + _ -> + Sock + end, + TS1 = timestamp(), + {ok, pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]), + TS2 = timestamp(), RefT = net_kernel:connecttime() - 1000, - ?line true = ((TS2 - TS1) < RefT), - ?line TS3 = timestamp(), - ?line {ok, true} = do_inet_rpc(Sock2,erlang,monitor_node, - [NN,true,[allow_passive_connect]]), - ?line TS4 = timestamp(), - ?line true = ((TS4 - TS3) > RefT), - ?line {ok, pong} = do_inet_rpc(Sock3,net_adm,ping,[NN2]), - ?line {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]), - ?line {ok, true} = do_inet_rpc(Sock3,net_kernel,disconnect,[NN2]), + true = ((TS2 - TS1) < RefT), + TS3 = timestamp(), + {ok, true} = do_inet_rpc(Sock2,erlang,monitor_node, + [NN,true,[allow_passive_connect]]), + TS4 = timestamp(), + true = ((TS4 - TS3) > RefT), + {ok, pong} = do_inet_rpc(Sock3,net_adm,ping,[NN2]), + {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]), + {ok, true} = do_inet_rpc(Sock3,net_kernel,disconnect,[NN2]), receive after 500 -> ok end, - ?line {ok, pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]), - ?line {ok, true} = do_inet_rpc(Sock2,net_kernel,connect_node,[NN]), - ?line {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]), - ?line stop_relay_node(Sock3), - ?line stop_relay_node(Sock2). - + {ok, pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]), + {ok, true} = do_inet_rpc(Sock2,net_kernel,connect_node,[NN]), + {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]), + stop_relay_node(Sock3), + stop_relay_node(Sock2). + %% Start a relay node and a lonely (dist_auto_connect never) node. @@ -835,51 +834,51 @@ dist_auto_connect_once(Config) when is_list(Config) -> %% Result is sent here through relay node. dist_auto_connect_never(Config) when is_list(Config) -> Self = self(), - ?line {ok, RelayNode} = - start_node(dist_auto_connect_relay), - ?line spawn(RelayNode, - fun() -> - register(dist_auto_connect_relay, self()), - dist_auto_connect_relay(Self) - end), - ?line {ok, Handle} = dist_auto_connect_start(dist_auto_connect, never), - ?line Result = - receive - {do_dist_auto_connect, ok} -> - ok; - {do_dist_auto_connect, Error} -> - {error, Error}; - Other -> - {error, Other} - after 32000 -> - timeout - end, - ?line stop_node(RelayNode), - ?line Stopped = dist_auto_connect_stop(Handle), - ?line Junk = - receive - {do_dist_auto_connect, _} = J -> - J - after 0 -> - ok - end, - ?line {ok, ok, ok} = {Result, Stopped, Junk}, + {ok, RelayNode} = + start_node(dist_auto_connect_relay), + spawn(RelayNode, + fun() -> + register(dist_auto_connect_relay, self()), + dist_auto_connect_relay(Self) + end), + {ok, Handle} = dist_auto_connect_start(dist_auto_connect, never), + Result = + receive + {do_dist_auto_connect, ok} -> + ok; + {do_dist_auto_connect, Error} -> + {error, Error}; + Other -> + {error, Other} + after 32000 -> + timeout + end, + stop_node(RelayNode), + Stopped = dist_auto_connect_stop(Handle), + Junk = + receive + {do_dist_auto_connect, _} = J -> + J + after 0 -> + ok + end, + {ok, ok, ok} = {Result, Stopped, Junk}, ok. do_dist_auto_connect([never]) -> Node = list_to_atom("dist_auto_connect_relay@" ++ hostname()), io:format("~p:do_dist_auto_connect([false]) Node=~p~n", - [?MODULE, Node]), + [?MODULE, Node]), Ping = net_adm:ping(Node), io:format("~p:do_dist_auto_connect([false]) Ping=~p~n", - [?MODULE, Ping]), + [?MODULE, Ping]), Result = case Ping of - pang -> ok; - _ -> {error, Ping} - end, + pang -> ok; + _ -> {error, Ping} + end, io:format("~p:do_dist_auto_connect([false]) Result=~p~n", - [?MODULE, Result]), + [?MODULE, Result]), net_kernel:connect_node(Node), catch {dist_auto_connect_relay, Node} ! {do_dist_auto_connect, Result}; % receive after 1000 -> ok end, @@ -887,10 +886,10 @@ do_dist_auto_connect([never]) -> do_dist_auto_connect(Arg) -> io:format("~p:do_dist_auto_connect(~p)~n", - [?MODULE, Arg]), + [?MODULE, Arg]), receive after 10000 -> ok end, halt(). - + dist_auto_connect_start(Name, Value) when is_atom(Name) -> dist_auto_connect_start(atom_to_list(Name), Value); @@ -900,16 +899,16 @@ dist_auto_connect_start(Name, Value) when is_list(Name), is_atom(Value) -> ValueStr = atom_to_list(Value), Cookie = atom_to_list(erlang:get_cookie()), Cmd = lists:concat( - [%"xterm -e ", - atom_to_list(lib:progname()), -% " -noinput ", - " -detached ", - long_or_short(), " ", Name, - " -setcookie ", Cookie, - " -pa ", ModuleDir, - " -s ", atom_to_list(?MODULE), - " do_dist_auto_connect ", ValueStr, - " -kernel dist_auto_connect ", ValueStr]), + [%"xterm -e ", + atom_to_list(lib:progname()), + % " -noinput ", + " -detached ", + long_or_short(), " ", Name, + " -setcookie ", Cookie, + " -pa ", ModuleDir, + " -s ", atom_to_list(?MODULE), + " do_dist_auto_connect ", ValueStr, + " -kernel dist_auto_connect ", ValueStr]), io:format("~p:dist_auto_connect_start() cmd: ~p~n", [?MODULE, Cmd]), Port = open_port({spawn, Cmd}, [stream]), {ok, {Port, Node}}. @@ -927,97 +926,83 @@ dist_auto_connect_stop(Port, _Node, Pid, N) when is_integer(N), N =< 0 -> Result; dist_auto_connect_stop(Port, Node, Pid, N) when is_integer(N) -> case net_adm:ping(Node) of - pong -> - receive after 100 -> ok end, - dist_auto_connect_stop(Port, Node, Pid, N-100); - pang -> - exit(Pid, normal), - catch erlang:port_close(Port), - io:format("~p:dist_auto_connect_stop() ok~n", [?MODULE]), - ok + pong -> + receive after 100 -> ok end, + dist_auto_connect_stop(Port, Node, Pid, N-100); + pang -> + exit(Pid, normal), + catch erlang:port_close(Port), + io:format("~p:dist_auto_connect_stop() ok~n", [?MODULE]), + ok end. dist_auto_connect_relay(Parent) -> receive X -> - catch Parent ! X + catch Parent ! X end, dist_auto_connect_relay(Parent). dist_parallel_send(Config) when is_list(Config) -> - ?line {ok, RNode} = start_node(dist_parallel_receiver), - ?line {ok, SNode} = start_node(dist_parallel_sender), - ?line WatchDog = spawn_link( - fun () -> - TRef = erlang:start_timer((2*60*1000), - self(), - oops), - receive - {timeout, TRef, _ } -> - spawn(SNode, - fun () -> - abort(timeout) - end), - spawn(RNode, - fun () -> - abort(timeout) - end) -%% rpc:cast(SNode, erlang, halt, -%% ["Timetrap (sender)"]), -%% rpc:cast(RNode, erlang, halt, -%% ["Timetrap (receiver)"]) - end - end), - ?line MkSndrs = fun (Receiver) -> - lists:map(fun (_) -> - spawn_link(SNode, - ?MODULE, - dist_parallel_sender, - [self(), - Receiver, - 1000]) - end, - lists:seq(1, 64)) - end, - ?line SndrsStart = fun (Sndrs) -> - Parent = self(), - spawn_link( - SNode, - fun () -> - lists:foreach(fun (P) -> - P ! {go, Parent} - end, - Sndrs) - end) - end, - ?line SndrsWait = fun (Sndrs) -> - lists:foreach(fun (P) -> - receive {P, done} -> ok end - end, - Sndrs) - end, - ?line DPR = spawn_link(RNode, ?MODULE, dist_parallel_receiver, []), - ?line Sndrs1 = MkSndrs(DPR), - ?line SndrsStart(Sndrs1), - ?line SndrsWait(Sndrs1), - ?line unlink(DPR), - ?line exit(DPR, bang), - - ?line DEPR = spawn_link(RNode, ?MODULE, dist_evil_parallel_receiver, []), - ?line Sndrs2 = MkSndrs(DEPR), - ?line SndrsStart(Sndrs2), - ?line SndrsWait(Sndrs2), - ?line unlink(DEPR), - ?line exit(DEPR, bang), - - ?line unlink(WatchDog), - ?line exit(WatchDog, bang), - - ?line stop_node(RNode), - ?line stop_node(SNode), - - ?line ok. + {ok, RNode} = start_node(dist_parallel_receiver), + {ok, SNode} = start_node(dist_parallel_sender), + WatchDog = spawn_link( + fun () -> + TRef = erlang:start_timer((2*60*1000), self(), oops), + receive + {timeout, TRef, _ } -> + spawn(SNode, fun () -> abort(timeout) end), + spawn(RNode, fun () -> abort(timeout) end) + %% rpc:cast(SNode, erlang, halt, + %% ["Timetrap (sender)"]), + %% rpc:cast(RNode, erlang, halt, + %% ["Timetrap (receiver)"]) + end + end), + MkSndrs = fun (Receiver) -> + lists:map(fun (_) -> + spawn_link(SNode, + ?MODULE, + dist_parallel_sender, + [self(), Receiver, 1000]) + end, lists:seq(1, 64)) + end, + SndrsStart = fun (Sndrs) -> + Parent = self(), + spawn_link(SNode, + fun () -> + lists:foreach(fun (P) -> + P ! {go, Parent} + end, Sndrs) + end) + end, + SndrsWait = fun (Sndrs) -> + lists:foreach(fun (P) -> + receive {P, done} -> ok end + end, Sndrs) + end, + DPR = spawn_link(RNode, ?MODULE, dist_parallel_receiver, []), + Sndrs1 = MkSndrs(DPR), + SndrsStart(Sndrs1), + SndrsWait(Sndrs1), + unlink(DPR), + exit(DPR, bang), + + DEPR = spawn_link(RNode, ?MODULE, dist_evil_parallel_receiver, []), + Sndrs2 = MkSndrs(DEPR), + SndrsStart(Sndrs2), + SndrsWait(Sndrs2), + unlink(DEPR), + exit(DEPR, bang), + + unlink(WatchDog), + exit(WatchDog, bang), + + stop_node(RNode), + stop_node(SNode), + + ok. do_dist_parallel_sender(Parent, _Receiver, 0) -> Parent ! {self(), done}; @@ -1039,71 +1024,71 @@ dist_evil_parallel_receiver() -> dist_evil_parallel_receiver(). atom_roundtrip(Config) when is_list(Config) -> - ?line AtomData = atom_data(), - ?line verify_atom_data(AtomData), - ?line {ok, Node} = start_node(Config), - ?line do_atom_roundtrip(Node, AtomData), - ?line stop_node(Node), - ?line ok. + AtomData = atom_data(), + verify_atom_data(AtomData), + {ok, Node} = start_node(Config), + do_atom_roundtrip(Node, AtomData), + stop_node(Node), + ok. atom_roundtrip_r15b(Config) when is_list(Config) -> case test_server:is_release_available("r15b") of - true -> - ?line AtomData = atom_data(), - ?line verify_atom_data(AtomData), - ?line {ok, Node} = start_node(Config, [], "r15b"), - ?line do_atom_roundtrip(Node, AtomData), - ?line stop_node(Node), - ?line ok; - false -> - ?line {skip,"No OTP R15B available"} + true -> + AtomData = atom_data(), + verify_atom_data(AtomData), + {ok, Node} = start_node(Config, [], "r15b"), + do_atom_roundtrip(Node, AtomData), + stop_node(Node), + ok; + false -> + {skip,"No OTP R15B available"} end. unicode_atom_roundtrip(Config) when is_list(Config) -> - ?line AtomData = unicode_atom_data(), - ?line verify_atom_data(AtomData), - ?line {ok, Node} = start_node(Config), - ?line do_atom_roundtrip(Node, AtomData), - ?line stop_node(Node), - ?line ok. + AtomData = unicode_atom_data(), + verify_atom_data(AtomData), + {ok, Node} = start_node(Config), + do_atom_roundtrip(Node, AtomData), + stop_node(Node), + ok. do_atom_roundtrip(Node, AtomData) -> - ?line Parent = self(), - ?line Proc = spawn_link(Node, fun () -> verify_atom_data_loop(Parent) end), - ?line Proc ! {self(), AtomData}, - ?line receive {Proc, AD1} -> AtomData = AD1 end, - ?line Proc ! {self(), AtomData}, - ?line receive {Proc, AD2} -> AtomData = AD2 end, - ?line RevAtomData = lists:reverse(AtomData), - ?line Proc ! {self(), RevAtomData}, - ?line receive {Proc, RAD1} -> RevAtomData = RAD1 end, - ?line unlink(Proc), - ?line exit(Proc, bang), - ?line ok. + Parent = self(), + Proc = spawn_link(Node, fun () -> verify_atom_data_loop(Parent) end), + Proc ! {self(), AtomData}, + receive {Proc, AD1} -> AtomData = AD1 end, + Proc ! {self(), AtomData}, + receive {Proc, AD2} -> AtomData = AD2 end, + RevAtomData = lists:reverse(AtomData), + Proc ! {self(), RevAtomData}, + receive {Proc, RAD1} -> RevAtomData = RAD1 end, + unlink(Proc), + exit(Proc, bang), + ok. verify_atom_data_loop(From) -> receive - {From, AtomData} -> - verify_atom_data(AtomData), - From ! {self(), AtomData}, - verify_atom_data_loop(From) + {From, AtomData} -> + verify_atom_data(AtomData), + From ! {self(), AtomData}, + verify_atom_data_loop(From) end. atom_data() -> lists:map(fun (N) -> - ATxt = "a"++integer_to_list(N), - {list_to_atom(ATxt), ATxt} - end, - lists:seq(1, 2000)). + ATxt = "a"++integer_to_list(N), + {list_to_atom(ATxt), ATxt} + end, + lists:seq(1, 2000)). verify_atom_data(AtomData) -> lists:foreach(fun ({Atom, AtomTxt}) when is_atom(Atom) -> - AtomTxt = atom_to_list(Atom); - ({PPR, AtomTxt}) -> - % Pid, Port, or Ref - AtomTxt = atom_to_list(node(PPR)) - end, - AtomData). + AtomTxt = atom_to_list(Atom); + ({PPR, AtomTxt}) -> + % Pid, Port, or Ref + AtomTxt = atom_to_list(node(PPR)) + end, + AtomData). uc_atom_tup(ATxt) -> Atom = string_to_atom(ATxt), @@ -1156,9 +1141,8 @@ unicode_atom_data() -> uc_atom_tup(lists:seq(65500, 65754)), uc_atom_tup(lists:seq(65500, 65563)) | lists:map(fun (N) -> - uc_atom_tup(lists:seq(64000+N, 64254+N)) - end, - lists:seq(1, 2000))]. + uc_atom_tup(lists:seq(64000+N, 64254+N)) + end, lists:seq(1, 2000))]. contended_atom_cache_entry(Config) when is_list(Config) -> contended_atom_cache_entry_test(Config, latin1). @@ -1167,79 +1151,77 @@ contended_unicode_atom_cache_entry(Config) when is_list(Config) -> contended_atom_cache_entry_test(Config, unicode). contended_atom_cache_entry_test(Config, Type) -> - ?line TestServer = self(), - ?line ProcessPairs = 10, - ?line Msgs = 100000, - ?line {ok, SNode} = start_node(Config), - ?line {ok, RNode} = start_node(Config), - ?line Success = make_ref(), - ?line spawn_link( - SNode, - fun () -> - erts_debug:set_internal_state(available_internal_state, - true), - Master = self(), - CIX = get_cix(), - TestAtoms = case Type of - latin1 -> - get_conflicting_atoms(CIX, - ProcessPairs); - unicode -> - get_conflicting_unicode_atoms(CIX, - ProcessPairs) - end, - io:format("Testing with the following atoms all using " - "cache index ~p:~n ~w~n", - [CIX, TestAtoms]), - Ps = lists:map( - fun (A) -> - Ref = make_ref(), - R = spawn_link( - RNode, - fun () -> - Atom = receive - {Ref, txt, ATxt} -> - case Type of - latin1 -> - list_to_atom(ATxt); - unicode -> - string_to_atom(ATxt) - end - end, - receive_ref_atom(Ref, - Atom, - Msgs), - Master ! {self(), success} - end), - S = spawn_link( - SNode, - fun () -> - receive go -> ok end, - R ! {Ref, - txt, - atom_to_list(A)}, - send_ref_atom(R, Ref, A, Msgs) - end), - {S, R} - end, - TestAtoms), - lists:foreach(fun ({S, _}) -> - S ! go - end, - Ps), - lists:foreach(fun ({_, R}) -> - receive {R, success} -> ok end - end, - Ps), - TestServer ! Success - end), - ?line receive - Success -> - ok - end, - ?line stop_node(SNode), - ?line stop_node(RNode), - ?line ok. + TestServer = self(), + ProcessPairs = 10, + Msgs = 100000, + {ok, SNode} = start_node(Config), + {ok, RNode} = start_node(Config), + Success = make_ref(), + spawn_link( + SNode, + fun () -> + erts_debug:set_internal_state(available_internal_state, + true), + Master = self(), + CIX = get_cix(), + TestAtoms = case Type of + latin1 -> + get_conflicting_atoms(CIX, + ProcessPairs); + unicode -> + get_conflicting_unicode_atoms(CIX, + ProcessPairs) + end, + io:format("Testing with the following atoms all using " + "cache index ~p:~n ~w~n", + [CIX, TestAtoms]), + Ps = lists:map( + fun (A) -> + Ref = make_ref(), + R = spawn_link(RNode, + fun () -> + Atom = receive + {Ref, txt, ATxt} -> + case Type of + latin1 -> + list_to_atom(ATxt); + unicode -> + string_to_atom(ATxt) + end + end, + receive_ref_atom(Ref, + Atom, + Msgs), + Master ! {self(), success} + end), + S = spawn_link(SNode, + fun () -> + receive go -> ok end, + R ! {Ref, + txt, + atom_to_list(A)}, + send_ref_atom(R, Ref, A, Msgs) + end), + {S, R} + end, + TestAtoms), + lists:foreach(fun ({S, _}) -> + S ! go + end, + Ps), + lists:foreach(fun ({_, R}) -> + receive {R, success} -> ok end + end, + Ps), + TestServer ! Success + end), + receive + Success -> + ok + end, + stop_node(SNode), + stop_node(RNode), + ok. send_ref_atom(_To, _Ref, _Atom, 0) -> ok; @@ -1251,11 +1233,11 @@ receive_ref_atom(_Ref, _Atom, 0) -> ok; receive_ref_atom(Ref, Atom, N) -> receive - {Ref, Value} -> - Atom = Value + {Ref, Value} -> + Atom = Value end, receive_ref_atom(Ref, Atom, N-1). - + get_cix() -> get_cix(1000). @@ -1263,34 +1245,34 @@ get_cix(CIX) when is_integer(CIX), CIX < 0 -> get_cix(0); get_cix(CIX) when is_integer(CIX) -> get_cix(CIX, - unwanted_cixs(), - erts_debug:get_internal_state(max_atom_out_cache_index)). + unwanted_cixs(), + erts_debug:get_internal_state(max_atom_out_cache_index)). get_cix(CIX, Unwanted, MaxCIX) when CIX > MaxCIX -> get_cix(0, Unwanted, MaxCIX); get_cix(CIX, Unwanted, MaxCIX) -> case lists:member(CIX, Unwanted) of - true -> get_cix(CIX+1, Unwanted, MaxCIX); - false -> CIX + true -> get_cix(CIX+1, Unwanted, MaxCIX); + false -> CIX end. unwanted_cixs() -> lists:map(fun (Node) -> - erts_debug:get_internal_state({atom_out_cache_index, - Node}) - end, - nodes()). - - + erts_debug:get_internal_state({atom_out_cache_index, + Node}) + end, + nodes()). + + get_conflicting_atoms(_CIX, 0) -> []; get_conflicting_atoms(CIX, N) -> Atom = list_to_atom("atom" ++ integer_to_list(erlang:unique_integer([positive]))), case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of - CIX -> - [Atom|get_conflicting_atoms(CIX, N-1)]; - _ -> - get_conflicting_atoms(CIX, N) + CIX -> + [Atom|get_conflicting_atoms(CIX, N-1)]; + _ -> + get_conflicting_atoms(CIX, N) end. get_conflicting_unicode_atoms(_CIX, 0) -> @@ -1298,10 +1280,10 @@ get_conflicting_unicode_atoms(_CIX, 0) -> get_conflicting_unicode_atoms(CIX, N) -> Atom = string_to_atom([16#1f608] ++ "atom" ++ integer_to_list(erlang:unique_integer([positive]))), case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of - CIX -> - [Atom|get_conflicting_unicode_atoms(CIX, N-1)]; - _ -> - get_conflicting_unicode_atoms(CIX, N) + CIX -> + [Atom|get_conflicting_unicode_atoms(CIX, N-1)]; + _ -> + get_conflicting_unicode_atoms(CIX, N) end. -define(COOKIE, ''). @@ -1323,40 +1305,40 @@ get_conflicting_unicode_atoms(CIX, N) -> -define(DOP_MONITOR_P_EXIT, 21). start_monitor(Offender,P) -> - ?line Parent = self(), - ?line Q = spawn(Offender, - fun () -> - Ref = erlang:monitor(process,P), - Parent ! {self(),ref,Ref}, - receive - just_stay_alive -> ok - end - end), - ?line Ref = receive - {Q,ref,R} -> - R - after 5000 -> - error - end, + Parent = self(), + Q = spawn(Offender, + fun () -> + Ref = erlang:monitor(process,P), + Parent ! {self(),ref,Ref}, + receive + just_stay_alive -> ok + end + end), + Ref = receive + {Q,ref,R} -> + R + after 5000 -> + error + end, io:format("Ref is ~p~n",[Ref]), ok. start_link(Offender,P) -> - ?line Parent = self(), - ?line Q = spawn(Offender, - fun () -> - process_flag(trap_exit,true), - link(P), - Parent ! {self(),ref,P}, - receive - just_stay_alive -> ok - end - end), - ?line Ref = receive - {Q,ref,R} -> - R - after 5000 -> - error - end, + Parent = self(), + Q = spawn(Offender, + fun () -> + process_flag(trap_exit,true), + link(P), + Parent ! {self(),ref,P}, + receive + just_stay_alive -> ok + end + end), + Ref = receive + {Q,ref,R} -> + R + after 5000 -> + error + end, io:format("Ref is ~p~n",[Ref]), ok. @@ -1364,433 +1346,433 @@ start_link(Offender,P) -> bad_dist_structure(Config) when is_list(Config) -> ct:timetrap({seconds, 15}), - ?line {ok, Offender} = start_node(bad_dist_structure_offender), - ?line {ok, Victim} = start_node(bad_dist_structure_victim), - ?line start_node_monitors([Offender,Victim]), - ?line Parent = self(), - ?line P = spawn(Victim, - fun () -> - process_flag(trap_exit,true), - Parent ! {self(), started}, - receive check_msgs -> ok end, - bad_dist_struct_check_msgs([one, - two]), - Parent ! {self(), messages_checked}, - receive done -> ok end - end), - ?line receive {P, started} -> ok end, - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line verify_up(Offender, Victim), - ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])), - ?line start_monitor(Offender,P), - ?line P ! one, - ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line start_monitor(Offender,P), - ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal,normal},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line start_link(Offender,P), - ?line send_bad_structure(Offender, P,{?DOP_LINK},0), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line start_link(Offender,P), - ?line send_bad_structure(Offender, P,{?DOP_UNLINK,'replace'},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line start_link(Offender,P), - ?line send_bad_structure(Offender, P,{?DOP_UNLINK,'replace',make_ref()},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line start_link(Offender,P), - ?line send_bad_structure(Offender, P,{?DOP_UNLINK,make_ref(),P},0), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line start_link(Offender,P), - ?line send_bad_structure(Offender, P,{?DOP_UNLINK,normal,normal},0), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line start_monitor(Offender,P), - ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line start_monitor(Offender,P), - ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P,normal},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line start_monitor(Offender,P), - ?line send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line start_monitor(Offender,P), - ?line send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P,normal},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_EXIT,'replace',P},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_EXIT,make_ref(),normal,normal},0), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_EXIT_TT,'replace',token,P},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_EXIT_TT,make_ref(),token,normal,normal},0), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_EXIT2,'replace',P},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_EXIT2,make_ref(),normal,normal},0), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_EXIT2_TT,'replace',token,P},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_EXIT2_TT,make_ref(),token,normal,normal},0), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace'},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace','atomic'},2), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace',P},0), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name},2,{message}), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name,token},0,{message}), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace',''},2,{message}), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',P},0,{message}), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name},0,{message}), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name,{token}},2,{message}), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_SEND_TT,'',P},0,{message}), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_SEND_TT,'',name,token},0,{message}), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_SEND,''},0,{message}), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_SEND,'',name},0,{message}), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line send_bad_structure(Offender, P,{?DOP_SEND,'',P,{token}},0,{message}), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line P ! two, - ?line P ! check_msgs, - ?line receive - {P, messages_checked} -> ok - after 5000 -> - exit(victim_is_dead) - end, - - ?line {message_queue_len, 0} - = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), - - ?line unlink(P), - ?line P ! done, - ?line stop_node(Offender), - ?line stop_node(Victim), + {ok, Offender} = start_node(bad_dist_structure_offender), + {ok, Victim} = start_node(bad_dist_structure_victim), + start_node_monitors([Offender,Victim]), + Parent = self(), + P = spawn(Victim, + fun () -> + process_flag(trap_exit,true), + Parent ! {self(), started}, + receive check_msgs -> ok end, + bad_dist_struct_check_msgs([one, + two]), + Parent ! {self(), messages_checked}, + receive done -> ok end + end), + receive {P, started} -> ok end, + pong = rpc:call(Victim, net_adm, ping, [Offender]), + verify_up(Offender, Victim), + true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])), + start_monitor(Offender,P), + P ! one, + send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_monitor(Offender,P), + send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal,normal},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_link(Offender,P), + send_bad_structure(Offender, P,{?DOP_LINK},0), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_link(Offender,P), + send_bad_structure(Offender, P,{?DOP_UNLINK,'replace'},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_link(Offender,P), + send_bad_structure(Offender, P,{?DOP_UNLINK,'replace',make_ref()},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_link(Offender,P), + send_bad_structure(Offender, P,{?DOP_UNLINK,make_ref(),P},0), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_link(Offender,P), + send_bad_structure(Offender, P,{?DOP_UNLINK,normal,normal},0), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_monitor(Offender,P), + send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_monitor(Offender,P), + send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P,normal},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_monitor(Offender,P), + send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_monitor(Offender,P), + send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P,normal},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_EXIT,'replace',P},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_EXIT,make_ref(),normal,normal},0), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_EXIT_TT,'replace',token,P},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_EXIT_TT,make_ref(),token,normal,normal},0), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_EXIT2,'replace',P},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_EXIT2,make_ref(),normal,normal},0), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_EXIT2_TT,'replace',token,P},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_EXIT2_TT,make_ref(),token,normal,normal},0), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace'},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace','atomic'},2), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace',P},0), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name},2,{message}), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name,token},0,{message}), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace',''},2,{message}), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',P},0,{message}), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name},0,{message}), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name,{token}},2,{message}), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_SEND_TT,'',P},0,{message}), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_SEND_TT,'',name,token},0,{message}), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_SEND,''},0,{message}), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_SEND,'',name},0,{message}), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_SEND,'',P,{token}},0,{message}), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + P ! two, + P ! check_msgs, + receive + {P, messages_checked} -> ok + after 5000 -> + exit(victim_is_dead) + end, + + {message_queue_len, 0} + = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), + + unlink(P), + P ! done, + stop_node(Offender), + stop_node(Victim), ok. bad_dist_ext_receive(Config) when is_list(Config) -> - ?line {ok, Offender} = start_node(bad_dist_ext_receive_offender), - ?line {ok, Victim} = start_node(bad_dist_ext_receive_victim), - ?line start_node_monitors([Offender,Victim]), - - ?line Parent = self(), - - ?line P = spawn_link(Victim, - fun () -> - Parent ! {self(), started}, - receive check_msgs -> ok end, - bad_dist_ext_check_msgs([one, - two, - three]), - Parent ! {self(), messages_checked}, - receive done -> ok end - end), - - ?line receive {P, started} -> ok end, - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line verify_up(Offender, Victim), - ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])), - ?line P ! one, - ?line send_bad_msg(Offender, P), - ?line P ! two, - ?line verify_down(Offender, connection_closed, Victim, killed), - ?line {message_queue_len, 2} - = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), - - ?line Suspended = make_ref(), - ?line S = spawn(Victim, - fun () -> - erlang:suspend_process(P), - Parent ! Suspended, - receive after infinity -> ok end - end), - ?line MS = erlang:monitor(process, S), - ?line receive Suspended -> ok end, - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line verify_up(Offender, Victim), - ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])), - ?line send_bad_msgs(Offender, P, 5), - ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])), - ?line P ! three, - ?line send_bad_msgs(Offender, P, 5), + {ok, Offender} = start_node(bad_dist_ext_receive_offender), + {ok, Victim} = start_node(bad_dist_ext_receive_victim), + start_node_monitors([Offender,Victim]), + + Parent = self(), + + P = spawn_link(Victim, + fun () -> + Parent ! {self(), started}, + receive check_msgs -> ok end, + bad_dist_ext_check_msgs([one, + two, + three]), + Parent ! {self(), messages_checked}, + receive done -> ok end + end), + + receive {P, started} -> ok end, + pong = rpc:call(Victim, net_adm, ping, [Offender]), + verify_up(Offender, Victim), + true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])), + P ! one, + send_bad_msg(Offender, P), + P ! two, + verify_down(Offender, connection_closed, Victim, killed), + {message_queue_len, 2} + = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), + + Suspended = make_ref(), + S = spawn(Victim, + fun () -> + erlang:suspend_process(P), + Parent ! Suspended, + receive after infinity -> ok end + end), + MS = erlang:monitor(process, S), + receive Suspended -> ok end, + pong = rpc:call(Victim, net_adm, ping, [Offender]), + verify_up(Offender, Victim), + true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])), + send_bad_msgs(Offender, P, 5), + true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])), + P ! three, + send_bad_msgs(Offender, P, 5), %% Make sure bad msgs has reached Victim - ?line rpc:call(Offender, rpc, call, [Victim, erlang, node, []]), - - ?line verify_still_up(Offender, Victim), - ?line {message_queue_len, 13} - = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), - - ?line exit(S, bang), - ?line receive {'DOWN', MS, process, S, bang} -> ok end, - ?line verify_down(Offender, connection_closed, Victim, killed), - ?line {message_queue_len, 3} - = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), - - ?line P ! check_msgs, - ?line receive {P, messages_checked} -> ok end, - - ?line {message_queue_len, 0} - = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), - - ?line P ! done, - ?line unlink(P), - ?line verify_no_down(Offender, Victim), - ?line stop_node(Offender), - ?line stop_node(Victim). + rpc:call(Offender, rpc, call, [Victim, erlang, node, []]), + + verify_still_up(Offender, Victim), + {message_queue_len, 13} + = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), + + exit(S, bang), + receive {'DOWN', MS, process, S, bang} -> ok end, + verify_down(Offender, connection_closed, Victim, killed), + {message_queue_len, 3} + = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), + + P ! check_msgs, + receive {P, messages_checked} -> ok end, + + {message_queue_len, 0} + = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), + + P ! done, + unlink(P), + verify_no_down(Offender, Victim), + stop_node(Offender), + stop_node(Victim). bad_dist_ext_process_info(Config) when is_list(Config) -> - ?line {ok, Offender} = start_node(bad_dist_ext_process_info_offender), - ?line {ok, Victim} = start_node(bad_dist_ext_process_info_victim), - ?line start_node_monitors([Offender,Victim]), - - ?line Parent = self(), - ?line P = spawn_link(Victim, - fun () -> - Parent ! {self(), started}, - receive check_msgs -> ok end, - bad_dist_ext_check_msgs([one, two]), - Parent ! {self(), messages_checked}, - receive done -> ok end - end), - - ?line receive {P, started} -> ok end, - ?line P ! one, - - ?line Suspended = make_ref(), - ?line S = spawn(Victim, - fun () -> - erlang:suspend_process(P), - Parent ! Suspended, - receive after infinity -> ok end - end), - - ?line receive Suspended -> ok end, - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line verify_up(Offender, Victim), - ?line send_bad_msgs(Offender, P, 5), - - ?line P ! two, - ?line send_bad_msgs(Offender, P, 5), + {ok, Offender} = start_node(bad_dist_ext_process_info_offender), + {ok, Victim} = start_node(bad_dist_ext_process_info_victim), + start_node_monitors([Offender,Victim]), + + Parent = self(), + P = spawn_link(Victim, + fun () -> + Parent ! {self(), started}, + receive check_msgs -> ok end, + bad_dist_ext_check_msgs([one, two]), + Parent ! {self(), messages_checked}, + receive done -> ok end + end), + + receive {P, started} -> ok end, + P ! one, + + Suspended = make_ref(), + S = spawn(Victim, + fun () -> + erlang:suspend_process(P), + Parent ! Suspended, + receive after infinity -> ok end + end), + + receive Suspended -> ok end, + pong = rpc:call(Victim, net_adm, ping, [Offender]), + verify_up(Offender, Victim), + send_bad_msgs(Offender, P, 5), + + P ! two, + send_bad_msgs(Offender, P, 5), %% Make sure bad msgs has reached Victim - ?line rpc:call(Offender, rpc, call, [Victim, erlang, node, []]), - - ?line verify_still_up(Offender, Victim), - ?line {message_queue_len, 12} - = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), - ?line verify_still_up(Offender, Victim), - ?line [{message_queue_len, 2}, - {messages, [one, two]}] - = rpc:call(Victim, erlang, process_info, [P, [message_queue_len, - messages]]), - ?line verify_down(Offender, connection_closed, Victim, killed), - - ?line P ! check_msgs, - ?line exit(S, bang), - ?line receive {P, messages_checked} -> ok end, - - ?line {message_queue_len, 0} - = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), - - ?line P ! done, - ?line unlink(P), - ?line verify_no_down(Offender, Victim), - ?line stop_node(Offender), - ?line stop_node(Victim). + rpc:call(Offender, rpc, call, [Victim, erlang, node, []]), + + verify_still_up(Offender, Victim), + {message_queue_len, 12} + = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), + verify_still_up(Offender, Victim), + [{message_queue_len, 2}, + {messages, [one, two]}] + = rpc:call(Victim, erlang, process_info, [P, [message_queue_len, + messages]]), + verify_down(Offender, connection_closed, Victim, killed), + + P ! check_msgs, + exit(S, bang), + receive {P, messages_checked} -> ok end, + + {message_queue_len, 0} + = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), + + P ! done, + unlink(P), + verify_no_down(Offender, Victim), + stop_node(Offender), + stop_node(Victim). bad_dist_ext_control(Config) when is_list(Config) -> - ?line {ok, Offender} = start_node(bad_dist_ext_control_offender), - ?line {ok, Victim} = start_node(bad_dist_ext_control_victim), - ?line start_node_monitors([Offender,Victim]), + {ok, Offender} = start_node(bad_dist_ext_control_offender), + {ok, Victim} = start_node(bad_dist_ext_control_victim), + start_node_monitors([Offender,Victim]), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line verify_up(Offender, Victim), - ?line send_bad_dhdr(Offender, Victim), - ?line verify_down(Offender, connection_closed, Victim, killed), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + verify_up(Offender, Victim), + send_bad_dhdr(Offender, Victim), + verify_down(Offender, connection_closed, Victim, killed), - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line verify_up(Offender, Victim), - ?line send_bad_ctl(Offender, Victim), - ?line verify_down(Offender, connection_closed, Victim, killed), + pong = rpc:call(Victim, net_adm, ping, [Offender]), + verify_up(Offender, Victim), + send_bad_ctl(Offender, Victim), + verify_down(Offender, connection_closed, Victim, killed), - ?line verify_no_down(Offender, Victim), - ?line stop_node(Offender), - ?line stop_node(Victim). + verify_no_down(Offender, Victim), + stop_node(Offender), + stop_node(Victim). bad_dist_ext_connection_id(Config) when is_list(Config) -> - ?line {ok, Offender} = start_node(bad_dist_ext_connection_id_offender), - ?line {ok, Victim} = start_node(bad_dist_ext_connection_id_victim), - ?line start_node_monitors([Offender,Victim]), - - ?line Parent = self(), - ?line P = spawn_link(Victim, - fun () -> - Parent ! {self(), started}, - receive check_msgs -> ok end, - bad_dist_ext_check_msgs([]), - Parent ! {self(), messages_checked}, - receive done -> ok end - end), - - ?line receive {P, started} -> ok end, - ?line Suspended = make_ref(), - ?line S = spawn(Victim, - fun () -> - erlang:suspend_process(P), - Parent ! Suspended, - receive after infinity -> ok end - end), - ?line MS = erlang:monitor(process, S), - ?line receive Suspended -> ok end, - ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), - ?line verify_up(Offender, Victim), - ?line send_bad_msg(Offender, P), + {ok, Offender} = start_node(bad_dist_ext_connection_id_offender), + {ok, Victim} = start_node(bad_dist_ext_connection_id_victim), + start_node_monitors([Offender,Victim]), + + Parent = self(), + P = spawn_link(Victim, + fun () -> + Parent ! {self(), started}, + receive check_msgs -> ok end, + bad_dist_ext_check_msgs([]), + Parent ! {self(), messages_checked}, + receive done -> ok end + end), + + receive {P, started} -> ok end, + Suspended = make_ref(), + S = spawn(Victim, + fun () -> + erlang:suspend_process(P), + Parent ! Suspended, + receive after infinity -> ok end + end), + MS = erlang:monitor(process, S), + receive Suspended -> ok end, + pong = rpc:call(Victim, net_adm, ping, [Offender]), + verify_up(Offender, Victim), + send_bad_msg(Offender, P), %% Make sure bad msg has reached Victim - ?line rpc:call(Offender, rpc, call, [Victim, erlang, node, []]), + rpc:call(Offender, rpc, call, [Victim, erlang, node, []]), - ?line {message_queue_len, 1} - = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), + {message_queue_len, 1} + = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), - ?line true = rpc:call(Offender, net_kernel, disconnect, [Victim]), - ?line verify_down(Offender, disconnect, Victim, connection_closed), - ?line pong = rpc:call(Offender, net_adm, ping, [Victim]), + true = rpc:call(Offender, net_kernel, disconnect, [Victim]), + verify_down(Offender, disconnect, Victim, connection_closed), + pong = rpc:call(Offender, net_adm, ping, [Victim]), - ?line verify_up(Offender, Victim), + verify_up(Offender, Victim), %% We have a new connection between Offender and Victim, bad message %% should not bring it down. - ?line {message_queue_len, 1} - = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), + {message_queue_len, 1} + = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), - ?line exit(S, bang), - ?line receive {'DOWN', MS, process, S, bang} -> ok end, + exit(S, bang), + receive {'DOWN', MS, process, S, bang} -> ok end, %% Wait for a while (if the connection is taken down it might take a %% while). - ?line receive after 2000 -> ok end, - ?line verify_still_up(Offender, Victim), + receive after 2000 -> ok end, + verify_still_up(Offender, Victim), - ?line P ! check_msgs, - ?line receive {P, messages_checked} -> ok end, + P ! check_msgs, + receive {P, messages_checked} -> ok end, - ?line {message_queue_len, 0} - = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), - - ?line verify_still_up(Offender, Victim), - ?line P ! done, - ?line unlink(P), - ?line verify_no_down(Offender, Victim), - ?line stop_node(Offender), - ?line stop_node(Victim). + {message_queue_len, 0} + = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), + + verify_still_up(Offender, Victim), + P ! done, + unlink(P), + verify_no_down(Offender, Victim), + stop_node(Offender), + stop_node(Victim). bad_dist_struct_check_msgs([]) -> receive - Msg -> - exit({unexpected_message, Msg}) + Msg -> + exit({unexpected_message, Msg}) after 0 -> - ok + ok end; bad_dist_struct_check_msgs([M|Ms]) -> receive - {'EXIT',_,_} = EM -> - io:format("Ignoring exit message: ~p~n",[EM]), - bad_dist_struct_check_msgs([M|Ms]); - Msg -> - M = Msg, - bad_dist_struct_check_msgs(Ms) + {'EXIT',_,_} = EM -> + io:format("Ignoring exit message: ~p~n",[EM]), + bad_dist_struct_check_msgs([M|Ms]); + Msg -> + M = Msg, + bad_dist_struct_check_msgs(Ms) end. bad_dist_ext_check_msgs([]) -> receive - Msg -> - exit({unexpected_message, Msg}) + Msg -> + exit({unexpected_message, Msg}) after 0 -> - ok + ok end; bad_dist_ext_check_msgs([M|Ms]) -> receive - Msg -> - M = Msg, - bad_dist_ext_check_msgs(Ms) + Msg -> + M = Msg, + bad_dist_ext_check_msgs(Ms) end. - + dport_reg_send(Node, Name, Msg) -> DPrt = case dport(Node) of - undefined -> - pong = net_adm:ping(Node), - dport(Node); - Prt -> - Prt - end, + undefined -> + pong = net_adm:ping(Node), + dport(Node); + Prt -> + Prt + end, port_command(DPrt, [dmsg_hdr(), - dmsg_ext({?DOP_REG_SEND, - self(), - ?COOKIE, - Name}), - dmsg_ext(Msg)]). + dmsg_ext({?DOP_REG_SEND, + self(), + ?COOKIE, + Name}), + dmsg_ext(Msg)]). dport_send(To, Msg) -> Node = node(To), DPrt = case dport(Node) of - undefined -> - pong = net_adm:ping(Node), - dport(Node); - Prt -> - Prt - end, + undefined -> + pong = net_adm:ping(Node), + dport(Node); + Prt -> + Prt + end, port_command(DPrt, [dmsg_hdr(), - dmsg_ext({?DOP_SEND, - ?COOKIE, - To}), - dmsg_ext(Msg)]). + dmsg_ext({?DOP_SEND, + ?COOKIE, + To}), + dmsg_ext(Msg)]). send_bad_structure(Offender,Victim,Bad,WhereToPutSelf) -> send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,[]). send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) -> Parent = self(), Done = make_ref(), spawn(Offender, - fun () -> - Node = node(Victim), - pong = net_adm:ping(Node), - DPrt = dport(Node), - Bad1 = case WhereToPutSelf of - 0 -> - Bad; - N when N > 0 -> - setelement(N,Bad,self()) - end, - DData = [dmsg_hdr(), - dmsg_ext(Bad1)] ++ - case PayLoad of - [] -> []; - _Other -> [dmsg_ext(PayLoad)] - end, - port_command(DPrt, DData), - Parent ! {DData,Done} - end), + fun () -> + Node = node(Victim), + pong = net_adm:ping(Node), + DPrt = dport(Node), + Bad1 = case WhereToPutSelf of + 0 -> + Bad; + N when N > 0 -> + setelement(N,Bad,self()) + end, + DData = [dmsg_hdr(), + dmsg_ext(Bad1)] ++ + case PayLoad of + [] -> []; + _Other -> [dmsg_ext(PayLoad)] + end, + port_command(DPrt, DData), + Parent ! {DData,Done} + end), receive - {WhatSent,Done} -> - io:format("Offender sent ~p~n",[WhatSent]), - ok + {WhatSent,Done} -> + io:format("Offender sent ~p~n",[WhatSent]), + ok after 5000 -> - exit(unable_to_send) + exit(unable_to_send) end. - + %% send_bad_msgs(): %% Send a valid distribution header and control message @@ -1800,21 +1782,21 @@ send_bad_msg(BadNode, To) -> send_bad_msgs(BadNode, To, 1). send_bad_msgs(BadNode, To, Repeat) when is_atom(BadNode), - is_pid(To), - is_integer(Repeat) -> + is_pid(To), + is_integer(Repeat) -> Parent = self(), Done = make_ref(), spawn_link(BadNode, - fun () -> - Node = node(To), - pong = net_adm:ping(Node), - DPrt = dport(Node), - DData = [dmsg_hdr(), - dmsg_ext({?DOP_SEND, ?COOKIE, To}), - dmsg_bad_atom_cache_ref()], - repeat(fun () -> port_command(DPrt, DData) end, Repeat), - Parent ! Done - end), + fun () -> + Node = node(To), + pong = net_adm:ping(Node), + DPrt = dport(Node), + DData = [dmsg_hdr(), + dmsg_ext({?DOP_SEND, ?COOKIE, To}), + dmsg_bad_atom_cache_ref()], + repeat(fun () -> port_command(DPrt, DData) end, Repeat), + Parent ! Done + end), receive Done -> ok end. %% send_bad_ctl(): @@ -1823,24 +1805,24 @@ send_bad_ctl(BadNode, ToNode) when is_atom(BadNode), is_atom(ToNode) -> Parent = self(), Done = make_ref(), spawn_link(BadNode, - fun () -> - pong = net_adm:ping(ToNode), - %% We creat a valid ctl msg and replace an - %% atom with an invalid atom cache reference - <<131,Replace/binary>> = term_to_binary(replace), - Ctl = dmsg_ext({?DOP_REG_SEND, - self(), - ?COOKIE, - replace}), - CtlBeginSize = size(Ctl) - size(Replace), - <> = Ctl, - port_command(dport(ToNode), - [dmsg_fake_hdr2(), - CtlBegin, - dmsg_bad_atom_cache_ref(), - dmsg_ext({a, message})]), - Parent ! Done - end), + fun () -> + pong = net_adm:ping(ToNode), + %% We creat a valid ctl msg and replace an + %% atom with an invalid atom cache reference + <<131,Replace/binary>> = term_to_binary(replace), + Ctl = dmsg_ext({?DOP_REG_SEND, + self(), + ?COOKIE, + replace}), + CtlBeginSize = size(Ctl) - size(Replace), + <> = Ctl, + port_command(dport(ToNode), + [dmsg_fake_hdr2(), + CtlBegin, + dmsg_bad_atom_cache_ref(), + dmsg_ext({a, message})]), + Parent ! Done + end), receive Done -> ok end. %% send_bad_dhr(): @@ -1849,17 +1831,17 @@ send_bad_dhdr(BadNode, ToNode) when is_atom(BadNode), is_atom(ToNode) -> Parent = self(), Done = make_ref(), spawn_link(BadNode, - fun () -> - pong = net_adm:ping(ToNode), - port_command(dport(ToNode), dmsg_bad_hdr()), - Parent ! Done - end), + fun () -> + pong = net_adm:ping(ToNode), + port_command(dport(ToNode), dmsg_bad_hdr()), + Parent ! Done + end), receive Done -> ok end. dport(Node) when is_atom(Node) -> case catch erts_debug:get_internal_state(available_internal_state) of - true -> true; - _ -> erts_debug:set_internal_state(available_internal_state, true) + true -> true; + _ -> erts_debug:set_internal_state(available_internal_state, true) end, erts_debug:get_internal_state({dist_port, Node}). @@ -1872,7 +1854,7 @@ dmsg_bad_hdr() -> [131, % Version Magic $D, % Dist header 255]. % 255 atom references - + %% dmsg_fake_hdr1() -> %% A = <<"fake header atom 1">>, @@ -1913,21 +1895,21 @@ start_node(Name, Args, Rel) when is_atom(Name), is_list(Rel) -> Pa = filename:dirname(code:which(?MODULE)), Cookie = atom_to_list(erlang:get_cookie()), RelArg = case Rel of - [] -> []; - _ -> [{erl,[{release,Rel}]}] - end, + [] -> []; + _ -> [{erl,[{release,Rel}]}] + end, test_server:start_node(Name, slave, - [{args, - Args++" -setcookie "++Cookie++" -pa \""++Pa++"\""} - | RelArg]); + [{args, + Args++" -setcookie "++Cookie++" -pa \""++Pa++"\""} + | RelArg]); start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) -> Name = list_to_atom((atom_to_list(?MODULE) - ++ "-" - ++ atom_to_list(proplists:get_value(testcase, Config)) - ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) - ++ "-" - ++ integer_to_list(erlang:unique_integer([positive])))), + ++ "-" + ++ atom_to_list(proplists:get_value(testcase, Config)) + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive])))), start_node(Name, Args, Rel). stop_node(Node) -> @@ -1938,13 +1920,13 @@ freeze_node(Node, MS) -> DoingIt = make_ref(), Freezer = self(), spawn_link(Node, - fun () -> - erts_debug:set_internal_state(available_internal_state, - true), - dport_send(Freezer, DoingIt), - receive after Own -> ok end, - erts_debug:set_internal_state(block, MS+Own) - end), + fun () -> + erts_debug:set_internal_state(available_internal_state, + true), + dport_send(Freezer, DoingIt), + receive after Own -> ok end, + erts_debug:set_internal_state(block, MS+Own) + end), receive DoingIt -> ok end, receive after Own -> ok end. @@ -1955,46 +1937,46 @@ do_inet_rpc({_,_,Sock},M,F,A) -> Bin = term_to_binary({M,F,A}), gen_tcp:send(Sock,Bin), case gen_tcp:recv(Sock,0) of - {ok, Bin2} -> - T = binary_to_term(Bin2), - {ok,T}; - Else -> - {error, Else} + {ok, Bin2} -> + T = binary_to_term(Bin2), + {ok,T}; + Else -> + {error, Else} end. inet_rpc_server([Host, PortList]) -> Port = list_to_integer(PortList), {ok, Sock} = gen_tcp:connect(Host, Port,[binary, {packet, 4}, - {active, false}]), + {active, false}]), inet_rpc_server_loop(Sock). inet_rpc_server_loop(Sock) -> case gen_tcp:recv(Sock,0) of - {ok, Bin} -> - {M,F,A} = binary_to_term(Bin), - Res = (catch apply(M,F,A)), - RB = term_to_binary(Res), - gen_tcp:send(Sock,RB), - inet_rpc_server_loop(Sock); - _ -> - erlang:halt() + {ok, Bin} -> + {M,F,A} = binary_to_term(Bin), + Res = (catch apply(M,F,A)), + RB = term_to_binary(Res), + gen_tcp:send(Sock,RB), + inet_rpc_server_loop(Sock); + _ -> + erlang:halt() end. - + start_relay_node(Node, Args) -> Pa = filename:dirname(code:which(?MODULE)), Cookie = "NOT"++atom_to_list(erlang:get_cookie()), {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4}, - {active, false}]), + {active, false}]), {ok, Port} = inet:port(LSock), {ok, Host} = inet:gethostname(), RunArg = "-run " ++ atom_to_list(?MODULE) ++ " inet_rpc_server " ++ - Host ++ " " ++ integer_to_list(Port), + Host ++ " " ++ integer_to_list(Port), {ok, NN} = - test_server:start_node(Node, peer, - [{args, Args ++ - " -setcookie "++Cookie++" -pa "++Pa++" "++ - RunArg}]), + test_server:start_node(Node, peer, + [{args, Args ++ + " -setcookie "++Cookie++" -pa "++Pa++" "++ + RunArg}]), [N,H] = string:tokens(atom_to_list(NN),"@"), {ok, Sock} = gen_tcp:accept(LSock), pang = net_adm:ping(NN), @@ -2009,28 +1991,28 @@ wait_dead(N,H,0) -> {error,{not_dead,N,H}}; wait_dead(N,H,X) -> case erl_epmd:port_please(N,H) of - {port,_,_} -> - receive - after 1000 -> - ok - end, - wait_dead(N,H,X-1); - noport -> - ok; - Else -> - {error, {unexpected, Else}} + {port,_,_} -> + receive + after 1000 -> + ok + end, + wait_dead(N,H,X-1); + noport -> + ok; + Else -> + {error, {unexpected, Else}} end. - + start_node_monitors(Nodes) -> Master = self(), lists:foreach(fun (Node) -> - spawn(Node, - fun () -> - node_monitor(Master) - end) - end, - Nodes), + spawn(Node, + fun () -> + node_monitor(Master) + end) + end, + Nodes), ok. node_monitor(Master) -> @@ -2039,42 +2021,42 @@ node_monitor(Master) -> net_kernel:monitor_nodes(true, Opts), Nodes1 = nodes(connected), case lists:sort(Nodes0) == lists:sort(Nodes1) of - true -> - lists:foreach(fun (Node) -> - Master ! {nodeup, node(), Node} - end, - Nodes0), - io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Nodes0]), - node_monitor_loop(Master); - false -> - net_kernel:monitor_nodes(false, Opts), - flush_node_changes(), - node_monitor(Master) + true -> + lists:foreach(fun (Node) -> + Master ! {nodeup, node(), Node} + end, + Nodes0), + io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Nodes0]), + node_monitor_loop(Master); + false -> + net_kernel:monitor_nodes(false, Opts), + flush_node_changes(), + node_monitor(Master) end. flush_node_changes() -> receive - {NodeChange, _Node, _InfoList} when NodeChange == nodeup; - NodeChange == nodedown -> - flush_node_changes() + {NodeChange, _Node, _InfoList} when NodeChange == nodeup; + NodeChange == nodedown -> + flush_node_changes() after 0 -> - ok + ok end. node_monitor_loop(Master) -> receive - {nodeup, Node, _InfoList} = Msg -> - Master ! {nodeup, node(), Node}, - io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), - node_monitor_loop(Master); - {nodedown, Node, InfoList} = Msg -> - Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of - {value, {nodedown_reason, R}} -> R; - _ -> undefined - end, - Master ! {nodedown, node(), Node, Reason}, - io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), - node_monitor_loop(Master) + {nodeup, Node, _InfoList} = Msg -> + Master ! {nodeup, node(), Node}, + io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), + node_monitor_loop(Master); + {nodedown, Node, InfoList} = Msg -> + Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of + {value, {nodedown_reason, R}} -> R; + _ -> undefined + end, + Master ! {nodedown, node(), Node, Reason}, + io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), + node_monitor_loop(Master) end. verify_up(A, B) -> @@ -2088,16 +2070,16 @@ verify_still_up(A, B) -> verify_no_down(A, B) -> receive - {nodedown, A, B, _} = Msg0 -> - ct:fail(Msg0) + {nodedown, A, B, _} = Msg0 -> + ct:fail(Msg0) after 0 -> - ok + ok end, receive - {nodedown, B, A, _} = Msg1 -> - ct:fail(Msg1) + {nodedown, B, A, _} = Msg1 -> + ct:fail(Msg1) after 0 -> - ok + ok end. %% verify_down(A, B) -> @@ -2106,12 +2088,12 @@ verify_no_down(A, B) -> verify_down(A, ReasonA, B, ReasonB) -> receive - {nodedown, A, B, _} = Msg0 -> - {nodedown, A, B, ReasonA} = Msg0 + {nodedown, A, B, _} = Msg0 -> + {nodedown, A, B, ReasonA} = Msg0 end, receive - {nodedown, B, A, _} = Msg1 -> - {nodedown, B, A, ReasonB} = Msg1 + {nodedown, B, A, _} = Msg1 -> + {nodedown, B, A, ReasonB} = Msg1 end, ok. @@ -2131,17 +2113,17 @@ from(_, []) -> []. long_or_short() -> case net_kernel:longnames() of - true -> " -name "; - false -> " -sname " + true -> " -name "; + false -> " -sname " end. until(Fun) -> case Fun() of - true -> - ok; - false -> - receive after 10 -> ok end, - until(Fun) + true -> + ok; + false -> + receive after 10 -> ok end, + until(Fun) end. forever(Fun) -> @@ -2166,9 +2148,9 @@ stop_busy_dist_port_tracer(_) -> busy_dist_port_tracer() -> receive - {monitor, _SuspendedProcess, busy_dist_port, _Port} = M -> - erlang:display(M), - busy_dist_port_tracer() + {monitor, _SuspendedProcess, busy_dist_port, _Port} = M -> + erlang:display(M), + busy_dist_port_tracer() end. repeat(_Fun, 0) -> @@ -2181,38 +2163,38 @@ string_to_atom_ext(String) -> Utf8List = string_to_utf8_list(String), Len = length(Utf8List), case Len < 256 of - true -> - [?SMALL_ATOM_UTF8_EXT, Len | Utf8List]; - false -> - [?ATOM_UTF8_EXT, Len bsr 8, Len band 16#ff | Utf8List] + true -> + [?SMALL_ATOM_UTF8_EXT, Len | Utf8List]; + false -> + [?ATOM_UTF8_EXT, Len bsr 8, Len band 16#ff | Utf8List] end. string_to_atom(String) -> binary_to_term(list_to_binary([?VERSION_MAGIC - | string_to_atom_ext(String)])). + | string_to_atom_ext(String)])). string_to_utf8_list([]) -> []; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 0 =< CP, - CP =< 16#7F -> + 0 =< CP, + CP =< 16#7F -> [CP | string_to_utf8_list(CPs)]; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 16#80 =< CP, - CP =< 16#7FF -> + 16#80 =< CP, + CP =< 16#7FF -> [16#C0 bor (CP bsr 6), 16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)]; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 16#800 =< CP, - CP =< 16#FFFF -> + 16#800 =< CP, + CP =< 16#FFFF -> [16#E0 bor (CP bsr 12), 16#80 bor (16#3F band (CP bsr 6)), 16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)]; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 16#10000 =< CP, - CP =< 16#10FFFF -> + 16#10000 =< CP, + CP =< 16#10FFFF -> [16#F0 bor (CP bsr 18), 16#80 bor (16#3F band (CP bsr 12)), 16#80 bor (16#3F band (CP bsr 6)), @@ -2222,47 +2204,47 @@ string_to_utf8_list([CP|CPs]) when is_integer(CP), utf8_list_to_string([]) -> []; utf8_list_to_string([B|Bs]) when is_integer(B), - 0 =< B, - B =< 16#7F -> + 0 =< B, + B =< 16#7F -> [B | utf8_list_to_string(Bs)]; utf8_list_to_string([B0, B1 | Bs]) when is_integer(B0), - 16#C0 =< B0, - B0 =< 16#DF, - is_integer(B1), - 16#80 =< B1, - B1 =< 16#BF -> + 16#C0 =< B0, + B0 =< 16#DF, + is_integer(B1), + 16#80 =< B1, + B1 =< 16#BF -> [(((B0 band 16#1F) bsl 6) - bor (B1 band 16#3F)) + bor (B1 band 16#3F)) | utf8_list_to_string(Bs)]; utf8_list_to_string([B0, B1, B2 | Bs]) when is_integer(B0), - 16#E0 =< B0, - B0 =< 16#EF, - is_integer(B1), - 16#80 =< B1, - B1 =< 16#BF, - is_integer(B2), - 16#80 =< B2, - B2 =< 16#BF -> + 16#E0 =< B0, + B0 =< 16#EF, + is_integer(B1), + 16#80 =< B1, + B1 =< 16#BF, + is_integer(B2), + 16#80 =< B2, + B2 =< 16#BF -> [(((B0 band 16#F) bsl 12) - bor ((B1 band 16#3F) bsl 6) - bor (B2 band 16#3F)) + bor ((B1 band 16#3F) bsl 6) + bor (B2 band 16#3F)) | utf8_list_to_string(Bs)]; utf8_list_to_string([B0, B1, B2, B3 | Bs]) when is_integer(B0), - 16#F0 =< B0, - B0 =< 16#F7, - is_integer(B1), - 16#80 =< B1, - B1 =< 16#BF, - is_integer(B2), - 16#80 =< B2, - B2 =< 16#BF, - is_integer(B3), - 16#80 =< B3, - B3 =< 16#BF -> + 16#F0 =< B0, + B0 =< 16#F7, + is_integer(B1), + 16#80 =< B1, + B1 =< 16#BF, + is_integer(B2), + 16#80 =< B2, + B2 =< 16#BF, + is_integer(B3), + 16#80 =< B3, + B3 =< 16#BF -> [(((B0 band 16#7) bsl 18) - bor ((B1 band 16#3F) bsl 12) - bor ((B2 band 16#3F) bsl 6) - bor (B3 band 16#3F)) + bor ((B1 band 16#3F) bsl 12) + bor ((B2 band 16#3F) bsl 6) + bor (B3 band 16#3F)) | utf8_list_to_string(Bs)]. mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) -> @@ -2270,17 +2252,17 @@ mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) -> mk_pid({NodeNameExt, Creation}, Number, Serial); mk_pid({NodeNameExt, Creation}, Number, Serial) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PID_EXT, - NodeNameExt, - uint32_be(Number), - uint32_be(Serial), - uint8(Creation)])) of - Pid when is_pid(Pid) -> - Pid; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?PID_EXT, + NodeNameExt, + uint32_be(Number), + uint32_be(Serial), + uint8(Creation)])) of + Pid when is_pid(Pid) -> + Pid; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. mk_port({NodeName, Creation}, Number) when is_atom(NodeName) -> @@ -2288,59 +2270,59 @@ mk_port({NodeName, Creation}, Number) when is_atom(NodeName) -> mk_port({NodeNameExt, Creation}, Number); mk_port({NodeNameExt, Creation}, Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PORT_EXT, - NodeNameExt, - uint32_be(Number), - uint8(Creation)])) of - Port when is_port(Port) -> - Port; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?PORT_EXT, + NodeNameExt, + uint32_be(Number), + uint8(Creation)])) of + Port when is_port(Port) -> + Port; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. mk_ref({NodeName, Creation}, [Number] = NL) when is_atom(NodeName), - is_integer(Creation), - is_integer(Number) -> + is_integer(Creation), + is_integer(Number) -> <> = term_to_binary(NodeName), mk_ref({NodeNameExt, Creation}, NL); mk_ref({NodeNameExt, Creation}, [Number]) when is_integer(Creation), - is_integer(Number) -> + is_integer(Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?REFERENCE_EXT, - NodeNameExt, - uint32_be(Number), - uint8(Creation)])) of - Ref when is_reference(Ref) -> - Ref; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_ref, [{NodeNameExt, Creation}, [Number]]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?REFERENCE_EXT, + NodeNameExt, + uint32_be(Number), + uint8(Creation)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeNameExt, Creation}, [Number]]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end; mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName), - is_integer(Creation), - is_list(Numbers) -> + is_integer(Creation), + is_list(Numbers) -> <> = term_to_binary(NodeName), mk_ref({NodeNameExt, Creation}, Numbers); mk_ref({NodeNameExt, Creation}, Numbers) when is_integer(Creation), - is_list(Numbers) -> + is_list(Numbers) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?NEW_REFERENCE_EXT, - uint16_be(length(Numbers)), - NodeNameExt, - uint8(Creation), - lists:map(fun (N) -> - uint32_be(N) - end, - Numbers)])) of - Ref when is_reference(Ref) -> - Ref; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?NEW_REFERENCE_EXT, + uint16_be(length(Numbers)), + NodeNameExt, + uint8(Creation), + lists:map(fun (N) -> + uint32_be(N) + end, + Numbers)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 82dd2b87a6..c589470f5b 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -29,60 +29,60 @@ -module(driver_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, - end_per_suite/1, init_per_group/2,end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, - - a_test/1, - outputv_echo/1, - timer_measure/1, - timer_cancel/1, - timer_change/1, - timer_delay/1, - queue_echo/1, - outputv_errors/1, - driver_unloaded/1, - io_ready_exit/1, - use_fallback_pollset/1, - bad_fd_in_pollset/1, - driver_event/1, - fd_change/1, - steal_control/1, - otp_6602/1, - driver_system_info_base_ver/1, - driver_system_info_prev_ver/1, - driver_system_info_current_ver/1, - driver_monitor/1, - - ioq_exit_ready_input/1, - ioq_exit_ready_output/1, - ioq_exit_timeout/1, - ioq_exit_ready_async/1, - ioq_exit_event/1, - ioq_exit_ready_input_async/1, - ioq_exit_ready_output_async/1, - ioq_exit_timeout_async/1, - ioq_exit_event_async/1, - zero_extended_marker_garb_drv/1, - invalid_extended_marker_drv/1, - larger_major_vsn_drv/1, - larger_minor_vsn_drv/1, - smaller_major_vsn_drv/1, - smaller_minor_vsn_drv/1, - peek_non_existing_queue/1, - otp_6879/1, - caller/1, - many_events/1, - missing_callbacks/1, - smp_select/1, - driver_select_use/1, - thread_mseg_alloc_cache_clean/1, - otp_9302/1, - thr_free_drv/1, - async_blast/1, - thr_msg_blast/1, - consume_timeslice/1, - z_test/1]). + end_per_suite/1, init_per_group/2,end_per_group/2, + init_per_testcase/2, + end_per_testcase/2, + + a_test/1, + outputv_echo/1, + timer_measure/1, + timer_cancel/1, + timer_change/1, + timer_delay/1, + queue_echo/1, + outputv_errors/1, + driver_unloaded/1, + io_ready_exit/1, + use_fallback_pollset/1, + bad_fd_in_pollset/1, + driver_event/1, + fd_change/1, + steal_control/1, + otp_6602/1, + driver_system_info_base_ver/1, + driver_system_info_prev_ver/1, + driver_system_info_current_ver/1, + driver_monitor/1, + + ioq_exit_ready_input/1, + ioq_exit_ready_output/1, + ioq_exit_timeout/1, + ioq_exit_ready_async/1, + ioq_exit_event/1, + ioq_exit_ready_input_async/1, + ioq_exit_ready_output_async/1, + ioq_exit_timeout_async/1, + ioq_exit_event_async/1, + zero_extended_marker_garb_drv/1, + invalid_extended_marker_drv/1, + larger_major_vsn_drv/1, + larger_minor_vsn_drv/1, + smaller_major_vsn_drv/1, + smaller_minor_vsn_drv/1, + peek_non_existing_queue/1, + otp_6879/1, + caller/1, + many_events/1, + missing_callbacks/1, + smp_select/1, + driver_select_use/1, + thread_mseg_alloc_cache_clean/1, + otp_9302/1, + thr_free_drv/1, + async_blast/1, + thr_msg_blast/1, + consume_timeslice/1, + z_test/1]). -export([bin_prefix/2]). @@ -120,16 +120,16 @@ init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> case catch erts_debug:get_internal_state(available_internal_state) of - true -> ok; - _ -> erts_debug:set_internal_state(available_internal_state, true) + true -> ok; + _ -> erts_debug:set_internal_state(available_internal_state, true) end, erlang:display({init_per_testcase, Case}), - ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)), + 0 = element(1, erts_debug:get_internal_state(check_io_debug)), [{testcase, Case}|Config]. end_per_testcase(Case, Config) -> erlang:display({end_per_testcase, Case}), - ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)), + 0 = element(1, erts_debug:get_internal_state(check_io_debug)), ok. suite() -> @@ -181,40 +181,40 @@ end_per_group(_GroupName, Config) -> %% Test sending bad types to port with an outputv-capable driver. outputv_errors(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line erl_ddll:start(), - ?line ok = load_driver(Path, outputv_drv), + Path = proplists:get_value(data_dir, Config), + erl_ddll:start(), + ok = load_driver(Path, outputv_drv), outputv_bad_types(fun(T) -> - ?line outputv_errors_1(T), - ?line outputv_errors_1([1|T]), - ?line L = [1,2,3], - ?line outputv_errors_1([L,T]), - ?line outputv_errors_1([L|T]) - end), + outputv_errors_1(T), + outputv_errors_1([1|T]), + L = [1,2,3], + outputv_errors_1([L,T]), + outputv_errors_1([L|T]) + end), outputv_errors_1(42), %% Test iolists that do not fit in the address space. %% Unfortunately, it would be too slow to test in a 64-bit emulator. case erlang:system_info(wordsize) of - 4 -> outputv_huge_iolists(); - _ -> ok + 4 -> outputv_huge_iolists(); + _ -> ok end. outputv_bad_types(Test) -> Types = [-1,256,atom,42.0,{a,b,c},make_ref(),fun() -> 42 end, - [1|2],<<1:1>>,<<1:9>>,<<1:15>>], + [1|2],<<1:1>>,<<1:9>>,<<1:15>>], _ = [Test(Type) || Type <- Types], ok. outputv_huge_iolists() -> FourGigs = 1 bsl 32, - ?line Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++ - [1 bsl N || N <- lists:seq(33, 37)], - ?line Base = <<0:(1 bsl 20)/unit:8>>, + Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++ + [1 bsl N || N <- lists:seq(33, 37)], + Base = <<0:(1 bsl 20)/unit:8>>, [begin - ?line L = build_iolist(Sz, Base), - ?line outputv_errors_1(L) + L = build_iolist(Sz, Base), + outputv_errors_1(L) end || Sz <- Sizes], ok. @@ -225,41 +225,41 @@ outputv_errors_1(Term) -> build_iolist(N, Base) when N < 16 -> case rand:uniform(3) of - 1 -> - <> = Base, - Bin; - _ -> - lists:seq(1, N) + 1 -> + <> = Base, + Bin; + _ -> + lists:seq(1, N) end; build_iolist(N, Base) when N =< byte_size(Base) -> case rand:uniform(3) of - 1 -> - <> = Base, - Bin; - 2 -> - <> = Base, - [Bin]; - 3 -> - case N rem 2 of - 0 -> - L = build_iolist(N div 2, Base), - [L,L]; - 1 -> - L = build_iolist(N div 2, Base), - [L,L,45] - end + 1 -> + <> = Base, + Bin; + 2 -> + <> = Base, + [Bin]; + 3 -> + case N rem 2 of + 0 -> + L = build_iolist(N div 2, Base), + [L,L]; + 1 -> + L = build_iolist(N div 2, Base), + [L,L,45] + end end; build_iolist(N0, Base) -> Small = rand:uniform(15), Seq = lists:seq(1, Small), N = N0 - Small, case N rem 2 of - 0 -> - L = build_iolist(N div 2, Base), - [L,L|Seq]; - 1 -> - L = build_iolist(N div 2, Base), - [47,L,L|Seq] + 0 -> + L = build_iolist(N div 2, Base), + [L,L|Seq]; + 1 -> + L = build_iolist(N div 2, Base), + [47,L,L|Seq] end. %% Test echoing data with a driver that supports outputv. @@ -268,40 +268,40 @@ outputv_echo(Config) when is_list(Config) -> Name = 'outputv_drv', P = start_driver(Config, Name, true), - ?line ov_test(P, {bin,0}), - ?line ov_test(P, {bin,1}), - ?line ov_test(P, {bin,2}), - ?line ov_test(P, {bin,3}), - ?line ov_test(P, {bin,4}), - ?line ov_test(P, {bin,5}), - ?line ov_test(P, {bin,6}), - ?line ov_test(P, {bin,7}), - ?line ov_test(P, {bin,8}), - ?line ov_test(P, {bin,15}), - ?line ov_test(P, {bin,16}), - ?line ov_test(P, {bin,17}), - - ?line ov_test(P, {list,0}), - ?line ov_test(P, {list,1}), - ?line ov_test(P, {list,2}), - ?line ov_test(P, [int,int,{list,0},int]), - ?line ov_test(P, [int,int,{list,1},int]), - ?line ov_test(P, [int,int,{list,2}]), - ?line ov_test(P, [{list,3},int,int,{list,2}]), - ?line ov_test(P, {list,33}), - - ?line ov_test(P, [{bin,0}]), - ?line ov_test(P, [{bin,1}]), - ?line ov_test(P, [{bin,2}]), - ?line ov_test(P, [{bin,3}]), - ?line ov_test(P, [{bin,4}]), - ?line ov_test(P, [{bin,5}]), - ?line ov_test(P, [{bin,6},int]), - ?line ov_test(P, [int,{bin,3}]), - ?line ov_test(P, [int|{bin,4}]), - ?line ov_test(P, [{bin,17},int,{bin,13}|{bin,3}]), - - ?line ov_test(P, [int,{bin,17},int,{bin,?heap_binary_size+1}|{bin,3}]), + ov_test(P, {bin,0}), + ov_test(P, {bin,1}), + ov_test(P, {bin,2}), + ov_test(P, {bin,3}), + ov_test(P, {bin,4}), + ov_test(P, {bin,5}), + ov_test(P, {bin,6}), + ov_test(P, {bin,7}), + ov_test(P, {bin,8}), + ov_test(P, {bin,15}), + ov_test(P, {bin,16}), + ov_test(P, {bin,17}), + + ov_test(P, {list,0}), + ov_test(P, {list,1}), + ov_test(P, {list,2}), + ov_test(P, [int,int,{list,0},int]), + ov_test(P, [int,int,{list,1},int]), + ov_test(P, [int,int,{list,2}]), + ov_test(P, [{list,3},int,int,{list,2}]), + ov_test(P, {list,33}), + + ov_test(P, [{bin,0}]), + ov_test(P, [{bin,1}]), + ov_test(P, [{bin,2}]), + ov_test(P, [{bin,3}]), + ov_test(P, [{bin,4}]), + ov_test(P, [{bin,5}]), + ov_test(P, [{bin,6},int]), + ov_test(P, [int,{bin,3}]), + ov_test(P, [int|{bin,4}]), + ov_test(P, [{bin,17},int,{bin,13}|{bin,3}]), + + ov_test(P, [int,{bin,17},int,{bin,?heap_binary_size+1}|{bin,3}]), stop_driver(P, Name), ok. @@ -309,9 +309,9 @@ outputv_echo(Config) when is_list(Config) -> ov_test(Port, Template) -> Self = self(), spawn_opt(erlang, apply, [fun () -> ov_test(Self, Port, Template) end,[]], - [link,{fullsweep_after,0}]), + [link,{fullsweep_after,0}]), receive - done -> ok + done -> ok end. ov_test(Parent, Port, Template) -> @@ -353,20 +353,20 @@ ov_send_and_test(Port, Data, ExpectedResult) -> io:format("~p ! ~P", [Port,Data,12]), Port ! {self(),{command,Data}}, receive - {Port,{data,ReturnData}} -> - io:format("~p returned ~P", [Port,ReturnData,12]), - compare(ReturnData, ExpectedResult); - {Port,{data,OtherData}} -> + {Port,{data,ReturnData}} -> + io:format("~p returned ~P", [Port,ReturnData,12]), + compare(ReturnData, ExpectedResult); + {Port,{data,OtherData}} -> ct:fail("~p returned WRONG data ~p", [Port,OtherData]); - Wrong -> + Wrong -> ct:fail({unexpected_port_or_data,Wrong}) end. compare(Got, Expected) -> case {list_to_binary([Got]),list_to_binary([Expected])} of - {B,B} -> ok; - {_Gb,_Eb} -> - ct:fail(got_bad_data) + {B,B} -> ok; + {_Gb,_Eb} -> + ct:fail(got_bad_data) end. @@ -378,68 +378,68 @@ compare(Got, Expected) -> %% Check that timers time out in good time. timer_measure(Config) when is_list(Config) -> Name = 'timer_drv', - ?line Port = start_driver(Config, Name, false), + Port = start_driver(Config, Name, false), - ?line try_timeouts(Port, 8997), + try_timeouts(Port, 8997), - ?line stop_driver(Port, Name), + stop_driver(Port, Name), ok. try_timeouts(_, 0) -> ok; try_timeouts(Port, Timeout) -> - ?line TimeBefore = erlang:monotonic_time(), - ?line erlang:port_command(Port, <>), + TimeBefore = erlang:monotonic_time(), + erlang:port_command(Port, <>), receive - {Port,{data,[?TIMER]}} -> - ?line Elapsed = erl_millisecs() - erl_millisecs(TimeBefore), - io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed, Timeout]), - if - Elapsed < Timeout -> - ?line ct:fail(too_short); - Elapsed > Timeout + ?delay -> - ?line ct:fail(too_long); - true -> - try_timeouts(Port, Timeout div 2) - end + {Port,{data,[?TIMER]}} -> + Elapsed = erl_millisecs() - erl_millisecs(TimeBefore), + io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed, Timeout]), + if + Elapsed < Timeout -> + ct:fail(too_short); + Elapsed > Timeout + ?delay -> + ct:fail(too_long); + true -> + try_timeouts(Port, Timeout div 2) + end after Timeout + ?delay -> - ?line ct:fail("driver failed to timeout") + ct:fail("driver failed to timeout") end. %% Try cancelling timers set in a driver. timer_cancel(Config) when is_list(Config) -> Name = 'timer_drv', - ?line Port = start_driver(Config, Name, false), + Port = start_driver(Config, Name, false), - ?line try_cancel(Port, 10000), + try_cancel(Port, 10000), - ?line stop_driver(Port, Name), + stop_driver(Port, Name), ok. - + try_cancel(Port, Timeout) -> - ?line T_before = erl_millisecs(), + T_before = erl_millisecs(), Port ! {self(),{command,<>}}, receive - {Port, {data, [?TIMER]}} -> - ?line ct:fail("driver timed out before cancelling it") + {Port, {data, [?TIMER]}} -> + ct:fail("driver timed out before cancelling it") after Timeout -> - Port ! {self(), {command, [?CANCEL_TIMER]}}, - receive - {Port, {data, [?TIMER]}} -> - ?line ct:fail("driver timed out after cancelling it"); - {Port, {data, [?CANCELLED]}} -> - ?line Time_milli_secs = erl_millisecs() - T_before, - - io:format("Time_milli_secs: ~p Timeout: ~p\n", - [Time_milli_secs, Timeout]), - if - Time_milli_secs > (Timeout + ?delay) -> - ?line ct:fail("too long real time"); - Timeout == 0 -> ok; - true -> try_cancel(Port, Timeout div 2) - end - after ?delay -> - ct:fail("No message from driver") - end + Port ! {self(), {command, [?CANCEL_TIMER]}}, + receive + {Port, {data, [?TIMER]}} -> + ct:fail("driver timed out after cancelling it"); + {Port, {data, [?CANCELLED]}} -> + Time_milli_secs = erl_millisecs() - T_before, + + io:format("Time_milli_secs: ~p Timeout: ~p\n", + [Time_milli_secs, Timeout]), + if + Time_milli_secs > (Timeout + ?delay) -> + ct:fail("too long real time"); + Timeout == 0 -> ok; + true -> try_cancel(Port, Timeout div 2) + end + after ?delay -> + ct:fail("No message from driver") + end end. %% Test that timers don't time out too early if we do a sleep @@ -447,32 +447,32 @@ try_cancel(Port, Timeout) -> timer_delay(Config) when is_list(Config) -> Name = 'timer_drv', - ?line Port = start_driver(Config, Name, false), + Port = start_driver(Config, Name, false), - ?line TimeBefore = erlang:monotonic_time(), + TimeBefore = erlang:monotonic_time(), Timeout0 = 350, - ?line erlang:port_command(Port, <>), + erlang:port_command(Port, <>), Timeout = Timeout0 + - case os:type() of - {win32,_} -> 0; %Driver doesn't sleep on Windows. - _ -> 1000 - end, + case os:type() of + {win32,_} -> 0; %Driver doesn't sleep on Windows. + _ -> 1000 + end, receive - {Port,{data,[?TIMER]}} -> - ?line Elapsed = erl_millisecs() - erl_millisecs(TimeBefore), - io:format("Elapsed time: ~p Timeout: ~p\n", - [Elapsed,Timeout]), - if - Elapsed < Timeout -> - ?line ct:fail(too_short); - Elapsed > Timeout + ?delay -> - ?line ct:fail(too_long); - true -> - ok - end + {Port,{data,[?TIMER]}} -> + Elapsed = erl_millisecs() - erl_millisecs(TimeBefore), + io:format("Elapsed time: ~p Timeout: ~p\n", + [Elapsed,Timeout]), + if + Elapsed < Timeout -> + ct:fail(too_short); + Elapsed > Timeout + ?delay -> + ct:fail(too_long); + true -> + ok + end end, - ?line stop_driver(Port, Name), + stop_driver(Port, Name), ok. %% Test that driver_set_timer with new timout really changes @@ -480,31 +480,31 @@ timer_delay(Config) when is_list(Config) -> timer_change(Config) when is_list(Config) -> Name = 'timer_drv', - ?line Port = start_driver(Config, Name, false), + Port = start_driver(Config, Name, false), - ?line try_change_timer(Port, 10000), + try_change_timer(Port, 10000), - ?line stop_driver(Port, Name), + stop_driver(Port, Name), ok. - + try_change_timer(_Port, 0) -> ok; try_change_timer(Port, Timeout) -> - ?line Timeout_3 = Timeout*3, - ?line TimeBefore = erlang:monotonic_time(), - ?line erlang:port_command(Port, <>), - ?line erlang:port_command(Port, <>), + Timeout_3 = Timeout*3, + TimeBefore = erlang:monotonic_time(), + erlang:port_command(Port, <>), + erlang:port_command(Port, <>), receive - {Port,{data,[?TIMER]}} -> - ?line Elapsed = erl_millisecs() - erl_millisecs(TimeBefore), - io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed,Timeout]), - if - Elapsed < Timeout -> - ?line ct:fail(too_short); - Elapsed > Timeout + ?delay -> - ?line ct:fail(too_long); - true -> - try_timeouts(Port, Timeout div 2) - end + {Port,{data,[?TIMER]}} -> + Elapsed = erl_millisecs() - erl_millisecs(TimeBefore), + io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed,Timeout]), + if + Elapsed < Timeout -> + ct:fail(too_short); + Elapsed > Timeout + ?delay -> + ct:fail(too_long); + true -> + try_timeouts(Port, Timeout div 2) + end after Timeout + ?delay -> ct:fail("driver failed to timeout") end. @@ -518,43 +518,43 @@ try_change_timer(Port, Timeout) -> %% 2) Get the data back, a random amount at a time. queue_echo(Config) when is_list(Config) -> case test_server:is_native(?MODULE) of - true -> exit(crashes_native_code); - false -> queue_echo_1(Config) + true -> exit(crashes_native_code); + false -> queue_echo_1(Config) end. queue_echo_1(Config) -> ct:timetrap({minutes, 10}), Name = 'queue_drv', - ?line P = start_driver(Config, Name, true), - - ?line q_echo(P, [{?ENQ, {list,1}}, - {?ENQ, {list,0}}, - {?ENQ, {bin,0}}, - {?ENQ, {bin,1}}, - {?ENQ, {bin,2}}, - {?ENQ, {bin,3}}, - {?ENQ, {bin,4}}, - {?ENQ, {bin,5}}, - {?ENQ, {bin,600}}, - {?PUSHQ, {list,0}}, - {?PUSHQ, {list,1}}, - {?PUSHQ, {bin,0}}, - {?PUSHQ, {bin,1}}, - {?PUSHQ, {bin,888}}, - {?ENQ_BIN, {bin,0}}, - {?ENQ_BIN, {bin,1}}, - {?ENQ_BIN, {bin,2}}, - {?ENQ_BIN, {bin,3}}, - {?ENQ_BIN, {bin,4}}, - {?ENQ_BIN, {bin,777}}, - {?PUSHQ_BIN, {bin,0}}, - {?PUSHQ_BIN, {bin,1}}, - {?PUSHQ_BIN, {bin,334}}, - {?ENQV, [{bin,0},{list,1},{bin,1},{bin,555}]}, - {?ENQV, [{bin,0},{list,1},{bin,1}]}, - {?PUSHQV, [{bin,0},{list,1},{bin,1},{bin,319}]}]), - - ?line stop_driver(P, Name), + P = start_driver(Config, Name, true), + + q_echo(P, [{?ENQ, {list,1}}, + {?ENQ, {list,0}}, + {?ENQ, {bin,0}}, + {?ENQ, {bin,1}}, + {?ENQ, {bin,2}}, + {?ENQ, {bin,3}}, + {?ENQ, {bin,4}}, + {?ENQ, {bin,5}}, + {?ENQ, {bin,600}}, + {?PUSHQ, {list,0}}, + {?PUSHQ, {list,1}}, + {?PUSHQ, {bin,0}}, + {?PUSHQ, {bin,1}}, + {?PUSHQ, {bin,888}}, + {?ENQ_BIN, {bin,0}}, + {?ENQ_BIN, {bin,1}}, + {?ENQ_BIN, {bin,2}}, + {?ENQ_BIN, {bin,3}}, + {?ENQ_BIN, {bin,4}}, + {?ENQ_BIN, {bin,777}}, + {?PUSHQ_BIN, {bin,0}}, + {?PUSHQ_BIN, {bin,1}}, + {?PUSHQ_BIN, {bin,334}}, + {?ENQV, [{bin,0},{list,1},{bin,1},{bin,555}]}, + {?ENQV, [{bin,0},{list,1},{bin,1}]}, + {?PUSHQV, [{bin,0},{list,1},{bin,1},{bin,319}]}]), + + stop_driver(P, Name), ok. q_echo(Port, SpecList) -> @@ -594,7 +594,7 @@ q_echo(Port, SpecList) -> feed_and_dequeue(Port, HeapData, 2), feed_and_dequeue(Port, HeapData, 3), feed_and_dequeue(Port, HeapData, 4), - + io:format("\n"). feed_and_dequeue(Port, Data, DeqSize) -> @@ -614,9 +614,9 @@ feed_driver(Port, [], ExpectedInPort, Qb) -> {ExpectedInPort,Qb}; feed_driver(Port, [{Method0,Data}|T], Expected_return, Qb_before) -> Method = case Method0 of - ?RANDOM -> uniform(6)-1; - Other -> Other - end, + ?RANDOM -> uniform(6)-1; + Other -> Other + end, Size = size(list_to_binary([Data])), %% *********************************************************************** @@ -631,21 +631,21 @@ feed_driver(Port, [{Method0,Data}|T], Expected_return, Qb_before) -> Qb_in_driver = bytes_queued(Port), case Qb_before + Size of - Qb_in_driver -> ok; - Sum -> + Qb_in_driver -> ok; + Sum -> ct:fail("Qb_before: ~p\n" "Qb_before+Size: ~p\n" "Qb_in_driver: ~p", [Qb_before,Sum,Qb_in_driver]) end, X_return = case Method of - ?ENQ -> list_to_binary([Expected_return,Data]); - ?PUSHQ -> list_to_binary([Data,Expected_return]); - ?PUSHQ_BIN -> list_to_binary([Data,Expected_return]); - ?ENQ_BIN -> list_to_binary([Expected_return,Data]); - ?PUSHQV -> list_to_binary([Data,Expected_return]); - ?ENQV -> list_to_binary([Expected_return,Data]) - end, + ?ENQ -> list_to_binary([Expected_return,Data]); + ?PUSHQ -> list_to_binary([Data,Expected_return]); + ?PUSHQ_BIN -> list_to_binary([Data,Expected_return]); + ?ENQ_BIN -> list_to_binary([Expected_return,Data]); + ?PUSHQV -> list_to_binary([Data,Expected_return]); + ?ENQV -> list_to_binary([Expected_return,Data]) + end, feed_driver(Port, T, X_return, Qb_before + Size). %% method_name(0) -> pushq; @@ -663,20 +663,20 @@ compare_return(Port, _Data_list, 0, _Back_len) -> 0 = bytes_queued(Port); compare_return(Port, QueuedInPort0, Len_to_get, DeqSize) -> case bytes_queued(Port) of - Len_to_get -> ok; - BytesInQueue -> + Len_to_get -> ok; + BytesInQueue -> ct:fail("Len_to_get: ~p\nBytes in queue: ~p", [Len_to_get,BytesInQueue]) end, BytesToDequeue = if (DeqSize > Len_to_get) -> Len_to_get; - true -> DeqSize - end, + true -> DeqSize + end, Dequeued = read_head(Port, BytesToDequeue), case bin_prefix(Dequeued, QueuedInPort0) of - true -> - deq(Port, BytesToDequeue), - <<_:BytesToDequeue/binary,QueuedInPort/binary>> = QueuedInPort0, - compare_return(Port, QueuedInPort, Len_to_get - BytesToDequeue, DeqSize); - false -> + true -> + deq(Port, BytesToDequeue), + <<_:BytesToDequeue/binary,QueuedInPort/binary>> = QueuedInPort0, + compare_return(Port, QueuedInPort, Len_to_get - BytesToDequeue, DeqSize); + false -> ct:fail("Bytes to dequeue: ~p\nDequeued: ~p\nQueued in port: ~P", [BytesToDequeue, Dequeued, QueuedInPort0,12]) end. @@ -696,8 +696,8 @@ queue_op(Port, Method, Data) -> bytes_queued(Port) -> case erlang:port_control(Port, ?BYTES_QUEUED, []) of - <> -> I; - Bad -> ct:fail({bad_result,Bad}) + <> -> I; + Bad -> ct:fail({bad_result,Bad}) end. deq(Port, Size) -> @@ -708,76 +708,76 @@ read_head(Port, Size) -> driver_unloaded(Config) when is_list(Config) -> - ?line process_flag(trap_exit, true), - ?line Drv = timer_drv, - ?line User = self(), - ?line Loaded = make_ref(), - ?line Die = make_ref(), - ?line Loader = spawn(fun () -> - erl_ddll:start(), - ok = load_driver(proplists:get_value(data_dir, - Config), - Drv), - User ! Loaded, - receive Die -> exit(bye) end - end), - ?line receive Loaded -> ok end, - ?line Port = open_port({spawn, Drv}, []), - ?line Loader ! Die, - ?line receive - {'EXIT', Port, Reason} -> - ?line driver_unloaded = Reason - %% Reason used to be -1 - end. - + process_flag(trap_exit, true), + Drv = timer_drv, + User = self(), + Loaded = make_ref(), + Die = make_ref(), + Loader = spawn(fun () -> + erl_ddll:start(), + ok = load_driver(proplists:get_value(data_dir, + Config), + Drv), + User ! Loaded, + receive Die -> exit(bye) end + end), + receive Loaded -> ok end, + Port = open_port({spawn, Drv}, []), + Loader ! Die, + receive + {'EXIT', Port, Reason} -> + driver_unloaded = Reason + %% Reason used to be -1 + end. + io_ready_exit(Config) when is_list(Config) -> - ?line OTE = process_flag(trap_exit, true), - ?line Test = self(), - ?line Dgawd = spawn(fun () -> - ok = dgawd_handler:install(), - Mon = erlang:monitor(process, Test), - Test ! dgawd_handler_started, - receive - {'DOWN', Mon, _, _, _} -> ok; - stop_dgawd_handler -> ok - end, - dgawd_handler:restore(), - Test ! dgawd_handler_stopped - end), - ?line receive dgawd_handler_started -> ok end, - ?line Drv = io_ready_exit_drv, - ?line erl_ddll:start(), - ?line ok = load_driver(proplists:get_value(data_dir, Config), Drv), - ?line Port = open_port({spawn, Drv}, []), - ?line case erlang:port_control(Port, 0, "") of - "ok" -> - receive - {'EXIT', Port, Reason} -> - ?line case Reason of - ready_output_driver_failure -> - io:format("Exited in output_ready()~n"), - ?line ok; - ready_input_driver_failure -> - io:format("Exited in input_ready()~n"), - ?line ok; - Error -> ?line ct:fail(Error) - end - end, - receive after 2000 -> ok end, - ?line false = dgawd_handler:got_dgawd_report(), - ?line Dgawd ! stop_dgawd_handler, - ?line receive dgawd_handler_stopped -> ok end, - ?line process_flag(trap_exit, OTE), - ?line ok; - "nyiftos" -> - ?line process_flag(trap_exit, OTE), - ?line {skipped, "Not yet implemented for this OS"}; - Error -> - ?line process_flag(trap_exit, OTE), - ?line ct:fail({unexpected_control_result, Error}) - end. - + OTE = process_flag(trap_exit, true), + Test = self(), + Dgawd = spawn(fun () -> + ok = dgawd_handler:install(), + Mon = erlang:monitor(process, Test), + Test ! dgawd_handler_started, + receive + {'DOWN', Mon, _, _, _} -> ok; + stop_dgawd_handler -> ok + end, + dgawd_handler:restore(), + Test ! dgawd_handler_stopped + end), + receive dgawd_handler_started -> ok end, + Drv = io_ready_exit_drv, + erl_ddll:start(), + ok = load_driver(proplists:get_value(data_dir, Config), Drv), + Port = open_port({spawn, Drv}, []), + case erlang:port_control(Port, 0, "") of + "ok" -> + receive + {'EXIT', Port, Reason} -> + case Reason of + ready_output_driver_failure -> + io:format("Exited in output_ready()~n"), + ok; + ready_input_driver_failure -> + io:format("Exited in input_ready()~n"), + ok; + Error -> ct:fail(Error) + end + end, + receive after 2000 -> ok end, + false = dgawd_handler:got_dgawd_report(), + Dgawd ! stop_dgawd_handler, + receive dgawd_handler_stopped -> ok end, + process_flag(trap_exit, OTE), + ok; + "nyiftos" -> + process_flag(trap_exit, OTE), + {skipped, "Not yet implemented for this OS"}; + Error -> + process_flag(trap_exit, OTE), + ct:fail({unexpected_control_result, Error}) + end. + -define(CHKIO_STOP, 0). -define(CHKIO_USE_FALLBACK_POLLSET, 1). @@ -791,126 +791,126 @@ io_ready_exit(Config) when is_list(Config) -> use_fallback_pollset(Config) when is_list(Config) -> FlbkFun = fun () -> - ChkIoDuring = erlang:system_info(check_io), - case lists:keysearch(fallback_poll_set_size, - 1, - ChkIoDuring) of - {value, - {fallback_poll_set_size, N}} when N > 0 -> - ?line ok; - Error -> - ?line ct:fail({failed_to_use_fallback, Error}) - end - end, - ?line {BckupTest, Handel, OkRes} - = case chkio_test_init(Config) of - {erts_poll_info, ChkIo} = Hndl -> - case lists:keysearch(fallback, 1, ChkIo) of - {value, {fallback, B}} when B =/= false -> - ?line {FlbkFun, Hndl, ok}; - _ -> - ?line {fun () -> ok end, - Hndl, - {comment, - "This implementation does not use " - "a fallback pollset"}} - end; - Skip -> - {fun () -> ok end, Skip, ok} - end, - ?line case chkio_test_fini(chkio_test(Handel, - ?CHKIO_USE_FALLBACK_POLLSET, - fun () -> - ?line sleep(1000), - ?line BckupTest() - end)) of - {skipped, _} = Res -> ?line Res; - _ -> ?line OkRes - end. + ChkIoDuring = erlang:system_info(check_io), + case lists:keysearch(fallback_poll_set_size, + 1, + ChkIoDuring) of + {value, + {fallback_poll_set_size, N}} when N > 0 -> + ok; + Error -> + ct:fail({failed_to_use_fallback, Error}) + end + end, + {BckupTest, Handel, OkRes} + = case chkio_test_init(Config) of + {erts_poll_info, ChkIo} = Hndl -> + case lists:keysearch(fallback, 1, ChkIo) of + {value, {fallback, B}} when B =/= false -> + {FlbkFun, Hndl, ok}; + _ -> + {fun () -> ok end, + Hndl, + {comment, + "This implementation does not use " + "a fallback pollset"}} + end; + Skip -> + {fun () -> ok end, Skip, ok} + end, + case chkio_test_fini(chkio_test(Handel, + ?CHKIO_USE_FALLBACK_POLLSET, + fun () -> + sleep(1000), + BckupTest() + end)) of + {skipped, _} = Res -> Res; + _ -> OkRes + end. bad_fd_in_pollset(Config) when is_list(Config) -> - ?line chkio_test_fini(chkio_test(chkio_test_init(Config), - ?CHKIO_BAD_FD_IN_POLLSET, - fun () -> ?line sleep(1000) end)). + chkio_test_fini(chkio_test(chkio_test_init(Config), + ?CHKIO_BAD_FD_IN_POLLSET, + fun () -> sleep(1000) end)). driver_event(Config) when is_list(Config) -> - ?line chkio_test_fini(chkio_test(chkio_test_init(Config), - ?CHKIO_DRIVER_EVENT, - fun () -> ?line sleep(1000) end)). + chkio_test_fini(chkio_test(chkio_test_init(Config), + ?CHKIO_DRIVER_EVENT, + fun () -> sleep(1000) end)). fd_change(Config) when is_list(Config) -> - ?line chkio_test_fini(chkio_test(chkio_test_init(Config), - ?CHKIO_FD_CHANGE, - fun () -> ?line sleep(1000) end)). + chkio_test_fini(chkio_test(chkio_test_init(Config), + ?CHKIO_FD_CHANGE, + fun () -> sleep(1000) end)). steal_control(Config) when is_list(Config) -> - ?line chkio_test_fini(case chkio_test_init(Config) of - {erts_poll_info, _} = Hndl -> - ?line steal_control_test(Hndl); - Skip -> - ?line Skip - end). + chkio_test_fini(case chkio_test_init(Config) of + {erts_poll_info, _} = Hndl -> + steal_control_test(Hndl); + Skip -> + Skip + end). steal_control_test(Hndl = {erts_poll_info, Before}) -> - ?line Port = open_chkio_port(), - ?line case erlang:port_control(Port, ?CHKIO_STEAL_AUX, "") of - [$f,$d,$s,$:| _] = FdList -> - ?line chk_chkio_port(Port), - sleep(500), - ?line chk_chkio_port(Port), - ?line Res = chkio_test(Hndl, - ?CHKIO_STEAL, - FdList, - fun () -> - ?line chk_chkio_port(Port), - ?line sleep(500), - ?line chk_chkio_port(Port) - end), - ?line case erlang:port_control(Port, ?CHKIO_STOP, "") of - "ok" -> - ?line chk_chkio_port(Port), - ?line ok; - StopErr -> - ?line chk_chkio_port(Port), - ?line ct:fail({stop_error, StopErr}) - end, - ?line close_chkio_port(Port), - ?line Res; - [$s,$k,$i,$p,$:,$\ |Skip] -> - ?line chk_chkio_port(Port), - ?line close_chkio_port(Port), - {chkio_test_result, - {skipped, Skip}, - Before}; - StartErr -> - ?line chk_chkio_port(Port), - ?line ct:fail({start_error, StartErr}) - end. + Port = open_chkio_port(), + case erlang:port_control(Port, ?CHKIO_STEAL_AUX, "") of + [$f,$d,$s,$:| _] = FdList -> + chk_chkio_port(Port), + sleep(500), + chk_chkio_port(Port), + Res = chkio_test(Hndl, + ?CHKIO_STEAL, + FdList, + fun () -> + chk_chkio_port(Port), + sleep(500), + chk_chkio_port(Port) + end), + case erlang:port_control(Port, ?CHKIO_STOP, "") of + "ok" -> + chk_chkio_port(Port), + ok; + StopErr -> + chk_chkio_port(Port), + ct:fail({stop_error, StopErr}) + end, + close_chkio_port(Port), + Res; + [$s,$k,$i,$p,$:,$\ |Skip] -> + chk_chkio_port(Port), + close_chkio_port(Port), + {chkio_test_result, + {skipped, Skip}, + Before}; + StartErr -> + chk_chkio_port(Port), + ct:fail({start_error, StartErr}) + end. chkio_test_init(Config) when is_list(Config) -> - ?line ChkIo = get_stable_check_io_info(), - ?line case catch lists:keysearch(name, 1, ChkIo) of - {value, {name, erts_poll}} -> - ?line io:format("Before test: ~p~n", [ChkIo]), - ?line Path = proplists:get_value(data_dir, Config), - ?line erl_ddll:start(), - ?line ok = load_driver(Path, 'chkio_drv'), - ?line process_flag(trap_exit, true), - ?line {erts_poll_info, ChkIo}; - _ -> - ?line {skipped, "Test written to test erts_poll() which isn't used"} - end. - + ChkIo = get_stable_check_io_info(), + case catch lists:keysearch(name, 1, ChkIo) of + {value, {name, erts_poll}} -> + io:format("Before test: ~p~n", [ChkIo]), + Path = proplists:get_value(data_dir, Config), + erl_ddll:start(), + ok = load_driver(Path, 'chkio_drv'), + process_flag(trap_exit, true), + {erts_poll_info, ChkIo}; + _ -> + {skipped, "Test written to test erts_poll() which isn't used"} + end. + chkio_test_fini({skipped, _} = Res) -> Res; chkio_test_fini({chkio_test_result, Res, Before}) -> - ?line ok = erl_ddll:unload_driver('chkio_drv'), - ?line ok = erl_ddll:stop(), - ?line After = get_stable_check_io_info(), - ?line io:format("After test: ~p~n", [After]), - ?line verify_chkio_state(Before, After), - ?line Res. + ok = erl_ddll:unload_driver('chkio_drv'), + ok = erl_ddll:stop(), + After = get_stable_check_io_info(), + io:format("After test: ~p~n", [After]), + verify_chkio_state(Before, After), + Res. open_chkio_port() -> open_port({spawn, 'chkio_drv'}, []). @@ -918,255 +918,255 @@ open_chkio_port() -> close_chkio_port(Port) when is_port(Port) -> true = erlang:port_close(Port), receive - {'EXIT', Port, normal} -> - ok; - {'EXIT', Port, Reason} -> - ct:fail({abnormal_port_exit, Port, Reason}); - {Port, Message} -> - ct:fail({strange_message_from_port, Message}) + {'EXIT', Port, normal} -> + ok; + {'EXIT', Port, Reason} -> + ct:fail({abnormal_port_exit, Port, Reason}); + {Port, Message} -> + ct:fail({strange_message_from_port, Message}) end. chk_chkio_port(Port) -> receive - {'EXIT', Port, Reason} when Reason /= normal -> - ct:fail({port_exited, Port, Reason}) + {'EXIT', Port, Reason} when Reason /= normal -> + ct:fail({port_exited, Port, Reason}) after 0 -> - ok + ok end. - + chkio_test({skipped, _} = Res, _Test, _Fun) -> - ?line Res; + Res; chkio_test({erts_poll_info, _Before} = EPI, Test, Fun) when is_integer(Test) -> chkio_test(EPI, Test, "", Fun). chkio_test({skipped, _} = Res, _Test, _TestArgs, _Fun) -> - ?line Res; + Res; chkio_test({erts_poll_info, Before}, - Test, - TestArgs, - Fun) when is_integer(Test), - is_list(TestArgs) -> - ?line Port = open_chkio_port(), - ?line case erlang:port_control(Port, Test, TestArgs) of - "ok" -> - ?line chk_chkio_port(Port), - ?line Fun(), - ?line During = erlang:system_info(check_io), - ?line erlang:display(During), - ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)), - ?line io:format("During test: ~p~n", [During]), - ?line chk_chkio_port(Port), - ?line case erlang:port_control(Port, ?CHKIO_STOP, "") of - Res when is_list(Res) -> - ?line chk_chkio_port(Port), - ?line io:format("~s", [Res]), - ?line close_chkio_port(Port), - ?line Res, - ?line case Res of - [$c,$o,$m,$m,$e,$n,$t,$:,$\ |Cmnt] -> - ?line {chkio_test_result, - {comment, Cmnt}, - Before}; - _ -> - ?line {chkio_test_result, - Res, - Before} - end; - StopErr -> - ?line chk_chkio_port(Port), - ?line ct:fail({stop_error, StopErr}) - end; - [$s,$k,$i,$p,$:,$\ |Skip] -> - ?line chk_chkio_port(Port), - ?line close_chkio_port(Port), - {chkio_test_result, - {skipped, Skip}, - Before}; - StartErr -> - ?line chk_chkio_port(Port), - ?line ct:fail({start_error, StartErr}) - end. + Test, + TestArgs, + Fun) when is_integer(Test), + is_list(TestArgs) -> + Port = open_chkio_port(), + case erlang:port_control(Port, Test, TestArgs) of + "ok" -> + chk_chkio_port(Port), + Fun(), + During = erlang:system_info(check_io), + erlang:display(During), + 0 = element(1, erts_debug:get_internal_state(check_io_debug)), + io:format("During test: ~p~n", [During]), + chk_chkio_port(Port), + case erlang:port_control(Port, ?CHKIO_STOP, "") of + Res when is_list(Res) -> + chk_chkio_port(Port), + io:format("~s", [Res]), + close_chkio_port(Port), + Res, + case Res of + [$c,$o,$m,$m,$e,$n,$t,$:,$\ |Cmnt] -> + {chkio_test_result, + {comment, Cmnt}, + Before}; + _ -> + {chkio_test_result, + Res, + Before} + end; + StopErr -> + chk_chkio_port(Port), + ct:fail({stop_error, StopErr}) + end; + [$s,$k,$i,$p,$:,$\ |Skip] -> + chk_chkio_port(Port), + close_chkio_port(Port), + {chkio_test_result, + {skipped, Skip}, + Before}; + StartErr -> + chk_chkio_port(Port), + ct:fail({start_error, StartErr}) + end. verify_chkio_state(Before, After) -> - ?line TotSetSize = lists:keysearch(total_poll_set_size, 1, Before), - ?line TotSetSize = lists:keysearch(total_poll_set_size, 1, After), - ?line case lists:keysearch(fallback, 1, Before) of - {value,{fallback,false}} -> - ?line ok; - _ -> - ?line BckupSetSize = lists:keysearch(fallback_poll_set_size, - 1, - Before), - ?line BckupSetSize = lists:keysearch(fallback_poll_set_size, - 1, - After) - end, - ?line ok. + TotSetSize = lists:keysearch(total_poll_set_size, 1, Before), + TotSetSize = lists:keysearch(total_poll_set_size, 1, After), + case lists:keysearch(fallback, 1, Before) of + {value,{fallback,false}} -> + ok; + _ -> + BckupSetSize = lists:keysearch(fallback_poll_set_size, + 1, + Before), + BckupSetSize = lists:keysearch(fallback_poll_set_size, + 1, + After) + end, + ok. get_stable_check_io_info() -> ChkIo = erlang:system_info(check_io), PendUpdNo = case lists:keysearch(pending_updates, 1, ChkIo) of - {value, {pending_updates, PendNo}} -> - PendNo; - false -> - 0 - end, + {value, {pending_updates, PendNo}} -> + PendNo; + false -> + 0 + end, {value, {active_fds, ActFds}} = lists:keysearch(active_fds, 1, ChkIo), case {PendUpdNo, ActFds} of - {0, 0} -> - ChkIo; - _ -> - receive after 10 -> ok end, - get_stable_check_io_info() + {0, 0} -> + ChkIo; + _ -> + receive after 10 -> ok end, + get_stable_check_io_info() end. %% Missed port lock when stealing control of fd from a %% driver that didn't use the same lock. The lock checker %% used to trigger on this and dump core. otp_6602(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(Config), - ?line Done = make_ref(), - ?line Parent = self(), - ?line Tester = spawn_link(Node, - fun () -> - %% Inet driver use port locking... - {ok, S} = gen_udp:open(0), - {ok, Fd} = inet:getfd(S), - %% Steal fd (lock checker used to - %% trigger here). - {ok, _S2} = gen_udp:open(0,[{fd,Fd}]), - Parent ! Done - end), - ?line receive Done -> ok end, - ?line unlink(Tester), - ?line stop_node(Node), - ?line ok. + {ok, Node} = start_node(Config), + Done = make_ref(), + Parent = self(), + Tester = spawn_link(Node, + fun () -> + %% Inet driver use port locking... + {ok, S} = gen_udp:open(0), + {ok, Fd} = inet:getfd(S), + %% Steal fd (lock checker used to + %% trigger here). + {ok, _S2} = gen_udp:open(0,[{fd,Fd}]), + Parent ! Done + end), + receive Done -> ok end, + unlink(Tester), + stop_node(Node), + ok. -define(EXPECTED_SYSTEM_INFO_NAMES1, - ["drv_drv_vsn", - "emu_drv_vsn", - "erts_vsn", - "otp_vsn", - "thread", - "smp"]). + ["drv_drv_vsn", + "emu_drv_vsn", + "erts_vsn", + "otp_vsn", + "thread", + "smp"]). -define(EXPECTED_SYSTEM_INFO_NAMES2, - (?EXPECTED_SYSTEM_INFO_NAMES1 ++ - ["async_thrs", - "sched_thrs"])). + (?EXPECTED_SYSTEM_INFO_NAMES1 ++ + ["async_thrs", + "sched_thrs"])). -define(EXPECTED_SYSTEM_INFO_NAMES3, - (?EXPECTED_SYSTEM_INFO_NAMES2 ++ - ["emu_nif_vsn"])). + (?EXPECTED_SYSTEM_INFO_NAMES2 ++ + ["emu_nif_vsn"])). -define(EXPECTED_SYSTEM_INFO_NAMES4, - (?EXPECTED_SYSTEM_INFO_NAMES3 ++ - ["dirty_sched"])). + (?EXPECTED_SYSTEM_INFO_NAMES3 ++ + ["dirty_sched"])). -define(EXPECTED_SYSTEM_INFO_NAMES, ?EXPECTED_SYSTEM_INFO_NAMES4). 'driver_system_info_base_ver'(Config) when is_list(Config) -> - ?line driver_system_info_test(Config, sys_info_base_drv). + driver_system_info_test(Config, sys_info_base_drv). 'driver_system_info_prev_ver'(Config) when is_list(Config) -> - ?line driver_system_info_test(Config, sys_info_prev_drv). + driver_system_info_test(Config, sys_info_prev_drv). driver_system_info_current_ver(Config) when is_list(Config) -> - ?line driver_system_info_test(Config, sys_info_curr_drv). + driver_system_info_test(Config, sys_info_curr_drv). driver_system_info_test(Config, Name) -> - ?line Port = start_driver(Config, Name, false), - ?line case erlang:port_control(Port, 0, []) of - [$o,$k,$:,_ | Result] -> - ?line check_driver_system_info_result(Result); - [$e,$r,$r,$o,$r,$:,_ | Error] -> - ?line ct:fail(Error); - Unexpected -> - ?line ct:fail({unexpected_result, Unexpected}) - end, - ?line stop_driver(Port, Name), - ?line ok. + Port = start_driver(Config, Name, false), + case erlang:port_control(Port, 0, []) of + [$o,$k,$:,_ | Result] -> + check_driver_system_info_result(Result); + [$e,$r,$r,$o,$r,$:,_ | Error] -> + ct:fail(Error); + Unexpected -> + ct:fail({unexpected_result, Unexpected}) + end, + stop_driver(Port, Name), + ok. check_driver_system_info_result(Result) -> - ?line io:format("All names: ~p~n", [?EXPECTED_SYSTEM_INFO_NAMES]), - ?line io:format("Result: ~p~n", [Result]), - ?line {[], Ns, DDVSN} = chk_sis(lists:map(fun (Str) -> - string:tokens(Str, "=") - end, - string:tokens(Result, " ")), - ?EXPECTED_SYSTEM_INFO_NAMES), - ?line case {DDVSN, - drv_vsn_str2tup(erlang:system_info(driver_version))} of - {DDVSN, DDVSN} -> - ?line [] = Ns; - %% {{1, 0}, _} -> - %% ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES - %% -- ?EXPECTED_SYSTEM_INFO_NAMES1), - %% ?line ExpNs = lists:sort(Ns); - %% {{1, 1}, _} -> - %% ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES - %% -- ?EXPECTED_SYSTEM_INFO_NAMES2), - %% ?line ExpNs = lists:sort(Ns); - {{3, 0}, _} -> - ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES - -- ?EXPECTED_SYSTEM_INFO_NAMES3), - ?line ExpNs = lists:sort(Ns) - end. + io:format("All names: ~p~n", [?EXPECTED_SYSTEM_INFO_NAMES]), + io:format("Result: ~p~n", [Result]), + {[], Ns, DDVSN} = chk_sis(lists:map(fun (Str) -> + string:tokens(Str, "=") + end, + string:tokens(Result, " ")), + ?EXPECTED_SYSTEM_INFO_NAMES), + case {DDVSN, + drv_vsn_str2tup(erlang:system_info(driver_version))} of + {DDVSN, DDVSN} -> + [] = Ns; + %% {{1, 0}, _} -> + %% ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES + %% -- ?EXPECTED_SYSTEM_INFO_NAMES1), + %% ExpNs = lists:sort(Ns); + %% {{1, 1}, _} -> + %% ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES + %% -- ?EXPECTED_SYSTEM_INFO_NAMES2), + %% ExpNs = lists:sort(Ns); + {{3, 0}, _} -> + ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES + -- ?EXPECTED_SYSTEM_INFO_NAMES3), + ExpNs = lists:sort(Ns) + end. chk_sis(SIs, Ns) -> chk_sis(SIs, Ns, unknown). chk_sis(SIs, [], DDVSN) -> - ?line {SIs, [], DDVSN}; + {SIs, [], DDVSN}; chk_sis([], Ns, DDVSN) -> - ?line {[], Ns, DDVSN}; + {[], Ns, DDVSN}; chk_sis([[N, _] = SI| SIs], Ns, DDVSN) -> - ?line true = lists:member(N, Ns), - ?line case check_si_res(SI) of - {driver_version, NewDDVSN} -> - ?line chk_sis(SIs, lists:delete(N, Ns), NewDDVSN); - _ -> - ?line chk_sis(SIs, lists:delete(N, Ns), DDVSN) - end. + true = lists:member(N, Ns), + case check_si_res(SI) of + {driver_version, NewDDVSN} -> + chk_sis(SIs, lists:delete(N, Ns), NewDDVSN); + _ -> + chk_sis(SIs, lists:delete(N, Ns), DDVSN) + end. %% Data in first version of driver_system_info() (driver version 1.0) check_si_res(["drv_drv_vsn", Value]) -> - ?line DDVSN = drv_vsn_str2tup(Value), - ?line {Major, DMinor} = DDVSN, - ?line {Major, EMinor} = drv_vsn_str2tup(erlang:system_info(driver_version)), - ?line true = DMinor =< EMinor, - ?line {driver_version, DDVSN}; + DDVSN = drv_vsn_str2tup(Value), + {Major, DMinor} = DDVSN, + {Major, EMinor} = drv_vsn_str2tup(erlang:system_info(driver_version)), + true = DMinor =< EMinor, + {driver_version, DDVSN}; check_si_res(["emu_drv_vsn", Value]) -> - ?line Value = erlang:system_info(driver_version); + Value = erlang:system_info(driver_version); check_si_res(["erts_vsn", Value]) -> - ?line Value = erlang:system_info(version); + Value = erlang:system_info(version); check_si_res(["otp_vsn", Value]) -> - ?line Value = erlang:system_info(otp_release); + Value = erlang:system_info(otp_release); check_si_res(["thread", "true"]) -> - ?line true = erlang:system_info(threads); + true = erlang:system_info(threads); check_si_res(["thread", "false"]) -> - ?line false = erlang:system_info(threads); + false = erlang:system_info(threads); check_si_res(["smp", "true"]) -> - ?line true = erlang:system_info(smp_support); + true = erlang:system_info(smp_support); check_si_res(["smp", "false"]) -> - ?line false = erlang:system_info(smp_support); + false = erlang:system_info(smp_support); %% Data added in second version of driver_system_info() (driver version 1.1) check_si_res(["async_thrs", Value]) -> - ?line Value = integer_to_list(erlang:system_info(thread_pool_size)); + Value = integer_to_list(erlang:system_info(thread_pool_size)); check_si_res(["sched_thrs", Value]) -> - ?line Value = integer_to_list(erlang:system_info(schedulers)); + Value = integer_to_list(erlang:system_info(schedulers)); %% Data added in 3rd version of driver_system_info() (driver version 1.5) check_si_res(["emu_nif_vsn", Value]) -> - ?line Value = erlang:system_info(nif_version); + Value = erlang:system_info(nif_version); %% Data added in 4th version of driver_system_info() (driver version 3.1) check_si_res(["dirty_sched", _Value]) -> true; check_si_res(Unexpected) -> - ?line ct:fail({unexpected_result, Unexpected}). + ct:fail({unexpected_result, Unexpected}). -define(MON_OP_I_AM_IPID,1). -define(MON_OP_MONITOR_ME,2). @@ -1176,166 +1176,166 @@ check_si_res(Unexpected) -> %% Test monitoring of processes from drivers driver_monitor(Config) when is_list(Config) -> - ?line Name = monitor_drv, - ?line Port = start_driver(Config, Name, false), - ?line "ok" = port_control(Port,?MON_OP_I_AM_IPID,[]), - ?line "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]), - ?line "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), - ?line {monitors, []} = erlang:port_info(Port,monitors), - - ?line "ok:"++Id1 = port_control(Port,?MON_OP_MONITOR_ME_LATER,[]), - ?line {monitored_by, []} = process_info(self(),monitored_by), - ?line "ok" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id1), - ?line {monitored_by, [Port]} = process_info(self(),monitored_by), - ?line "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), - ?line {monitored_by, []} = process_info(self(),monitored_by), - - ?line "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]), - ?line Me = self(), - ?line {Pid1,Ref1} = - spawn_monitor(fun() -> - Me ! port_control(Port,?MON_OP_MONITOR_ME,[]), - Me ! process_info(self(),monitored_by), - Me ! erlang:port_info(Port,monitors) - end), - ?line ok = receive - "ok" -> - ok - after 1000 -> - timeout - end, - ?line ok = receive - {monitored_by, L} -> - L2 = lists:sort(L), - L3 = lists:sort([Me,Port]), - case L2 of - L3 -> - ok; - _ -> - mismatch - end - after 1000 -> - timeout - end, - ?line ok = receive - {monitors, LL} -> - LL2 = lists:sort(LL), - LL3 = lists:sort([{process,Me},{process,Pid1}]), - case LL2 of - LL3 -> - ok; - _ -> - mismatch - end - after 1000 -> - timeout - end, - ?line ok = receive - {'DOWN', Ref1, process, Pid1, _} -> - ok - after 1000 -> - timeout - end, - ?line ok = receive - {monitor_fired,Port,Pid1} -> - ok - after 1000 -> - timeout - end, - ?line "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), - ?line {monitors,[]} = erlang:port_info(Port,monitors), - ?line {monitored_by, []} = process_info(self(),monitored_by), - - ?line "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]), - ?line {Pid2,Ref2} = - spawn_monitor(fun() -> - receive go -> ok end, - Me ! port_control(Port,?MON_OP_MONITOR_ME_LATER,[]), - Me ! process_info(self(),monitored_by), - Me ! erlang:port_info(Port,monitors) - end), - ?line Pid2 ! go, - ?line {ok,Id2} = receive - "ok:"++II -> - {ok,II} - after 1000 -> - timeout - end, - ?line ok = receive - {monitored_by, [Me]} -> - ok - after 1000 -> - timeout - end, - ?line ok = receive - {monitors, [{process,Me}]} -> - ok - after 1000 -> - timeout - end, - ?line ok = receive - {'DOWN', Ref2, process, Pid2, _} -> - ok - after 1000 -> - timeout - end, - ?line "noproc" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id2), - ?line {monitors,[{process,Me}]} = erlang:port_info(Port,monitors), - ?line "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), - ?line "not_monitored" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), - ?line {monitors,[]} = erlang:port_info(Port,monitors), - ?line {monitored_by, []} = process_info(self(),monitored_by), - - - ?line "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]), - ?line {Pid3,Ref3} = - spawn_monitor(fun() -> - receive go -> ok end, - Me ! port_control(Port,?MON_OP_MONITOR_ME_LATER,[]), - Me ! process_info(self(),monitored_by), - Me ! erlang:port_info(Port,monitors) , - receive die -> ok end - end), - ?line Pid3 ! go, - ?line {ok,Id3} = receive - "ok:"++III -> - {ok,III} - after 1000 -> - timeout - end, - ?line ok = receive - {monitored_by, [Me]} -> - ok - after 1000 -> - timeout - end, - ?line ok = receive - {monitors, [{process,Me}]} -> - ok - after 1000 -> - timeout - end, - ?line "ok" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id3), - ?line LLL1 = lists:sort([{process,Me},{process,Pid3}]), - ?line {monitors,LLL2} = erlang:port_info(Port,monitors), - ?line LLL1 = lists:sort(LLL2), - ?line "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), - ?line {monitors,[{process,Pid3}]} = erlang:port_info(Port,monitors), - ?line Pid3 ! die, - ?line ok = receive - {'DOWN', Ref3, process, Pid3, _} -> - ok - after 1000 -> - timeout - end, - ?line "not_found" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id2), - ?line {monitors,[]} = erlang:port_info(Port,monitors), - ?line "not_monitored" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), - ?line {monitors,[]} = erlang:port_info(Port,monitors), - ?line {monitored_by, []} = process_info(self(),monitored_by), - - ?line stop_driver(Port, Name), - ?line ok. + Name = monitor_drv, + Port = start_driver(Config, Name, false), + "ok" = port_control(Port,?MON_OP_I_AM_IPID,[]), + "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]), + "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), + {monitors, []} = erlang:port_info(Port,monitors), + + "ok:"++Id1 = port_control(Port,?MON_OP_MONITOR_ME_LATER,[]), + {monitored_by, []} = process_info(self(),monitored_by), + "ok" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id1), + {monitored_by, [Port]} = process_info(self(),monitored_by), + "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), + {monitored_by, []} = process_info(self(),monitored_by), + + "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]), + Me = self(), + {Pid1,Ref1} = + spawn_monitor(fun() -> + Me ! port_control(Port,?MON_OP_MONITOR_ME,[]), + Me ! process_info(self(),monitored_by), + Me ! erlang:port_info(Port,monitors) + end), + ok = receive + "ok" -> + ok + after 1000 -> + timeout + end, + ok = receive + {monitored_by, L} -> + L2 = lists:sort(L), + L3 = lists:sort([Me,Port]), + case L2 of + L3 -> + ok; + _ -> + mismatch + end + after 1000 -> + timeout + end, + ok = receive + {monitors, LL} -> + LL2 = lists:sort(LL), + LL3 = lists:sort([{process,Me},{process,Pid1}]), + case LL2 of + LL3 -> + ok; + _ -> + mismatch + end + after 1000 -> + timeout + end, + ok = receive + {'DOWN', Ref1, process, Pid1, _} -> + ok + after 1000 -> + timeout + end, + ok = receive + {monitor_fired,Port,Pid1} -> + ok + after 1000 -> + timeout + end, + "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), + {monitors,[]} = erlang:port_info(Port,monitors), + {monitored_by, []} = process_info(self(),monitored_by), + + "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]), + {Pid2,Ref2} = + spawn_monitor(fun() -> + receive go -> ok end, + Me ! port_control(Port,?MON_OP_MONITOR_ME_LATER,[]), + Me ! process_info(self(),monitored_by), + Me ! erlang:port_info(Port,monitors) + end), + Pid2 ! go, + {ok,Id2} = receive + "ok:"++II -> + {ok,II} + after 1000 -> + timeout + end, + ok = receive + {monitored_by, [Me]} -> + ok + after 1000 -> + timeout + end, + ok = receive + {monitors, [{process,Me}]} -> + ok + after 1000 -> + timeout + end, + ok = receive + {'DOWN', Ref2, process, Pid2, _} -> + ok + after 1000 -> + timeout + end, + "noproc" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id2), + {monitors,[{process,Me}]} = erlang:port_info(Port,monitors), + "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), + "not_monitored" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), + {monitors,[]} = erlang:port_info(Port,monitors), + {monitored_by, []} = process_info(self(),monitored_by), + + + "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]), + {Pid3,Ref3} = + spawn_monitor(fun() -> + receive go -> ok end, + Me ! port_control(Port,?MON_OP_MONITOR_ME_LATER,[]), + Me ! process_info(self(),monitored_by), + Me ! erlang:port_info(Port,monitors) , + receive die -> ok end + end), + Pid3 ! go, + {ok,Id3} = receive + "ok:"++III -> + {ok,III} + after 1000 -> + timeout + end, + ok = receive + {monitored_by, [Me]} -> + ok + after 1000 -> + timeout + end, + ok = receive + {monitors, [{process,Me}]} -> + ok + after 1000 -> + timeout + end, + "ok" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id3), + LLL1 = lists:sort([{process,Me},{process,Pid3}]), + {monitors,LLL2} = erlang:port_info(Port,monitors), + LLL1 = lists:sort(LLL2), + "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), + {monitors,[{process,Pid3}]} = erlang:port_info(Port,monitors), + Pid3 ! die, + ok = receive + {'DOWN', Ref3, process, Pid3, _} -> + ok + after 1000 -> + timeout + end, + "not_found" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id2), + {monitors,[]} = erlang:port_info(Port,monitors), + "not_monitored" = port_control(Port,?MON_OP_DEMONITOR_ME,[]), + {monitors,[]} = erlang:port_info(Port,monitors), + {monitored_by, []} = process_info(self(),monitored_by), + + stop_driver(Port, Name), + ok. -define(IOQ_EXIT_READY_INPUT, 1). @@ -1349,43 +1349,43 @@ driver_monitor(Config) when is_list(Config) -> -define(IOQ_EXIT_EVENT_ASYNC, 9). ioq_exit_test(Config, TestNo) -> - ?line Drv = ioq_exit_drv, - ?line try - begin - ?line case load_driver(proplists:get_value(data_dir, Config), - Drv) of - ok -> ?line ok; - {error, permanent} -> ?line ok; - LoadError -> ?line ct:fail({load_error, LoadError}) - end, - case open_port({spawn, Drv}, []) of - Port when is_port(Port) -> - try port_control(Port, TestNo, "") of - "ok" -> - ?line ok; - "nyiftos" -> - ?line throw({skipped, - "Not yet implemented for " - "this OS"}); - [$s,$k,$i,$p,$:,$ | Comment] -> - ?line throw({skipped, Comment}); - [$e,$r,$r,$o,$r,$:,$ | Error] -> - ?line ct:fail(Error) - after - Port ! {self(), close}, - receive {Port, closed} -> ok end, - false = lists:member(Port, erlang:ports()), - ok - end; - Error -> - ?line ct:fail({open_port_failed, Error}) - end - end - catch - throw:Term -> ?line Term - after - erl_ddll:unload_driver(Drv) - end. + Drv = ioq_exit_drv, + try + begin + case load_driver(proplists:get_value(data_dir, Config), + Drv) of + ok -> ok; + {error, permanent} -> ok; + LoadError -> ct:fail({load_error, LoadError}) + end, + case open_port({spawn, Drv}, []) of + Port when is_port(Port) -> + try port_control(Port, TestNo, "") of + "ok" -> + ok; + "nyiftos" -> + throw({skipped, + "Not yet implemented for " + "this OS"}); + [$s,$k,$i,$p,$:,$ | Comment] -> + throw({skipped, Comment}); + [$e,$r,$r,$o,$r,$:,$ | Error] -> + ct:fail(Error) + after + Port ! {self(), close}, + receive {Port, closed} -> ok end, + false = lists:member(Port, erlang:ports()), + ok + end; + Error -> + ct:fail({open_port_failed, Error}) + end + end + catch + throw:Term -> Term + after + erl_ddll:unload_driver(Drv) + end. ioq_exit_ready_input(Config) when is_list(Config) -> ioq_exit_test(Config, ?IOQ_EXIT_READY_INPUT). @@ -1416,18 +1416,18 @@ ioq_exit_event_async(Config) when is_list(Config) -> vsn_mismatch_test(Config, LoadResult) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line DrvName = proplists:get_value(testcase, Config), - ?line LoadResult = load_driver(Path, DrvName), - ?line case LoadResult of - ok -> - ?line Port = open_port({spawn, DrvName}, []), - ?line true = is_port(Port), - ?line true = port_close(Port), - ?line ok = erl_ddll:unload_driver(DrvName); - _ -> - ?line ok - end. + Path = proplists:get_value(data_dir, Config), + DrvName = proplists:get_value(testcase, Config), + LoadResult = load_driver(Path, DrvName), + case LoadResult of + ok -> + Port = open_port({spawn, DrvName}, []), + true = is_port(Port), + true = port_close(Port), + ok = erl_ddll:unload_driver(DrvName); + _ -> + ok + end. zero_extended_marker_garb_drv(Config) when is_list(Config) -> vsn_mismatch_test(Config, {error, driver_incorrect_version}). @@ -1447,565 +1447,565 @@ smaller_major_vsn_drv(Config) when is_list(Config) -> smaller_minor_vsn_drv(Config) when is_list(Config) -> DrvVsnStr = erlang:system_info(driver_version), case drv_vsn_str2tup(DrvVsnStr) of - {_, 0} -> - {skipped, - "Cannot perform test when minor driver version is 0. " - "Current driver version is " ++ DrvVsnStr ++ "."}; - _ -> - vsn_mismatch_test(Config, ok) + {_, 0} -> + {skipped, + "Cannot perform test when minor driver version is 0. " + "Current driver version is " ++ DrvVsnStr ++ "."}; + _ -> + vsn_mismatch_test(Config, ok) end. -define(PEEK_NONXQ_TEST, 0). -define(PEEK_NONXQ_WAIT, 1). peek_non_existing_queue(Config) when is_list(Config) -> - ?line OTE = process_flag(trap_exit, true), - ?line Drv = peek_non_existing_queue_drv, - ?line try - begin - ?line case load_driver(proplists:get_value(data_dir, Config), - Drv) of - ok -> ?line ok; - {error, permanent} -> ?line ok; - LoadError -> ?line ct:fail({load_error, LoadError}) - end, - case open_port({spawn, Drv}, []) of - Port1 when is_port(Port1) -> - try port_control(Port1, ?PEEK_NONXQ_TEST, "") of - "ok" -> - ?line ok; - [$s,$k,$i,$p,$p,$e,$d,$:,$ | SkipReason] -> - ?line throw({skipped, SkipReason}); - [$e,$r,$r,$o,$r,$:,$ | Error1] -> - ?line ct:fail(Error1) - after - exit(Port1, kill), - receive {'EXIT', Port1, _} -> ok end - end; - Error1 -> - ?line ct:fail({open_port1_failed, Error1}) - end, - case open_port({spawn, Drv}, []) of - Port2 when is_port(Port2) -> - try port_control(Port2, ?PEEK_NONXQ_WAIT, "") of - "ok" -> - ?line ok; - [$e,$r,$r,$o,$r,$:,$ | Error2] -> - ?line ct:fail(Error2) - after - receive {Port2, test_successful} -> ok end, - Port2 ! {self(), close}, - receive {Port2, closed} -> ok end - end; - Error2 -> - ?line ct:fail({open_port2_failed, Error2}) - end - end - catch - throw:Term -> ?line Term - after - process_flag(trap_exit, OTE), - erl_ddll:unload_driver(Drv) - end. + OTE = process_flag(trap_exit, true), + Drv = peek_non_existing_queue_drv, + try + begin + case load_driver(proplists:get_value(data_dir, Config), + Drv) of + ok -> ok; + {error, permanent} -> ok; + LoadError -> ct:fail({load_error, LoadError}) + end, + case open_port({spawn, Drv}, []) of + Port1 when is_port(Port1) -> + try port_control(Port1, ?PEEK_NONXQ_TEST, "") of + "ok" -> + ok; + [$s,$k,$i,$p,$p,$e,$d,$:,$ | SkipReason] -> + throw({skipped, SkipReason}); + [$e,$r,$r,$o,$r,$:,$ | Error1] -> + ct:fail(Error1) + after + exit(Port1, kill), + receive {'EXIT', Port1, _} -> ok end + end; + Error1 -> + ct:fail({open_port1_failed, Error1}) + end, + case open_port({spawn, Drv}, []) of + Port2 when is_port(Port2) -> + try port_control(Port2, ?PEEK_NONXQ_WAIT, "") of + "ok" -> + ok; + [$e,$r,$r,$o,$r,$:,$ | Error2] -> + ct:fail(Error2) + after + receive {Port2, test_successful} -> ok end, + Port2 ! {self(), close}, + receive {Port2, closed} -> ok end + end; + Error2 -> + ct:fail({open_port2_failed, Error2}) + end + end + catch + throw:Term -> Term + after + process_flag(trap_exit, OTE), + erl_ddll:unload_driver(Drv) + end. otp_6879(Config) when is_list(Config) -> - ?line Drv = 'otp_6879_drv', - ?line Parent = self(), - ?line ok = load_driver(proplists:get_value(data_dir, Config), Drv), - ?line Procs = lists:map( - fun (No) -> - spawn_link( - fun () -> - case open_port({spawn, Drv}, []) of - Port when is_port(Port) -> - Res = otp_6879_call(Port, No, 10000), - erlang:port_close(Port), - Parent ! {self(), Res}; - _ -> - Parent ! {self(), - open_port_failed} - end - end) - end, - lists:seq(1,10)), - ?line lists:foreach(fun (P) -> - ?line receive - {P, ok} -> - ?line ok; - {P, Error} -> - ?line ct:fail({P, Error}) - end - end, - Procs), + Drv = 'otp_6879_drv', + Parent = self(), + ok = load_driver(proplists:get_value(data_dir, Config), Drv), + Procs = lists:map( + fun (No) -> + spawn_link( + fun () -> + case open_port({spawn, Drv}, []) of + Port when is_port(Port) -> + Res = otp_6879_call(Port, No, 10000), + erlang:port_close(Port), + Parent ! {self(), Res}; + _ -> + Parent ! {self(), + open_port_failed} + end + end) + end, + lists:seq(1,10)), + lists:foreach(fun (P) -> + receive + {P, ok} -> + ok; + {P, Error} -> + ct:fail({P, Error}) + end + end, + Procs), %% Also try it when input exceeds default buffer (256 bytes) - ?line Data = lists:seq(1, 1000), - ?line case open_port({spawn, Drv}, []) of - Port when is_port(Port) -> - ?line ok = otp_6879_call(Port, Data, 10), - ?line erlang:port_close(Port); - _ -> - ?line ct:fail(open_port_failed) - end, - ?line erl_ddll:unload_driver(Drv), - ?line ok. + Data = lists:seq(1, 1000), + case open_port({spawn, Drv}, []) of + Port when is_port(Port) -> + ok = otp_6879_call(Port, Data, 10), + erlang:port_close(Port); + _ -> + ct:fail(open_port_failed) + end, + erl_ddll:unload_driver(Drv), + ok. otp_6879_call(_Port, _Data, 0) -> ok; otp_6879_call(Port, Data, N) -> case catch erlang:port_call(Port, 0, Data) of - Data -> otp_6879_call(Port, Data, N-1); - BadData -> {mismatch, Data, BadData} + Data -> otp_6879_call(Port, Data, N-1); + BadData -> {mismatch, Data, BadData} end. caller(Config) when is_list(Config) -> - ?line run_caller_test(Config, false), - ?line run_caller_test(Config, true). - + run_caller_test(Config, false), + run_caller_test(Config, true). + run_caller_test(Config, Outputv) -> - ?line Drv = 'caller_drv', - ?line Cmd = case Outputv of - true -> - ?line os:putenv("CALLER_DRV_USE_OUTPUTV", - "true"), - outputv; - false -> - ?line os:putenv("CALLER_DRV_USE_OUTPUTV", - "false"), - output - end, - ?line ok = load_driver(proplists:get_value(data_dir, Config), Drv), - ?line Port = open_port({spawn, Drv}, []), - ?line true = is_port(Port), - ?line chk_caller(Port, start, self()), - ?line chk_caller(Port, - Cmd, - spawn_link( - fun () -> - port_command(Port, "") - end)), - ?line Port ! {self(), {command, ""}}, - ?line chk_caller(Port, Cmd, self()), - ?line chk_caller(Port, - control, - spawn_link( - fun () -> - port_control(Port, 0, "") - end)), - ?line chk_caller(Port, - call, - spawn_link( - fun () -> - erlang:port_call(Port, 0, "") - end)), - ?line true = port_close(Port), - ?line erl_ddll:unload_driver(Drv), - ?line ok. + Drv = 'caller_drv', + Cmd = case Outputv of + true -> + os:putenv("CALLER_DRV_USE_OUTPUTV", + "true"), + outputv; + false -> + os:putenv("CALLER_DRV_USE_OUTPUTV", + "false"), + output + end, + ok = load_driver(proplists:get_value(data_dir, Config), Drv), + Port = open_port({spawn, Drv}, []), + true = is_port(Port), + chk_caller(Port, start, self()), + chk_caller(Port, + Cmd, + spawn_link( + fun () -> + port_command(Port, "") + end)), + Port ! {self(), {command, ""}}, + chk_caller(Port, Cmd, self()), + chk_caller(Port, + control, + spawn_link( + fun () -> + port_control(Port, 0, "") + end)), + chk_caller(Port, + call, + spawn_link( + fun () -> + erlang:port_call(Port, 0, "") + end)), + true = port_close(Port), + erl_ddll:unload_driver(Drv), + ok. chk_caller(Port, Callback, ExpectedCaller) -> receive - {caller, Port, Callback, Caller} -> - ExpectedCaller = Caller + {caller, Port, Callback, Caller} -> + ExpectedCaller = Caller end. %% Check that many simultaneously signalled events work (win32) many_events(Config) when is_list(Config) -> - ?line Name = 'many_events_drv', - ?line Port = start_driver(Config, Name, false), + Name = 'many_events_drv', + Port = start_driver(Config, Name, false), Number = "1000", Port ! {self(), {command, Number}}, receive - {Port, {data,Number}} -> - ?line receive %% Just to make sure the emulator does not crash - %% after this case is run (if faulty) - after 2000 -> - ok - end + {Port, {data,Number}} -> + receive %% Just to make sure the emulator does not crash + %% after this case is run (if faulty) + after 2000 -> + ok + end after 1000 -> - ?line exit(the_driver_does_not_respond) + exit(the_driver_does_not_respond) end, - ?line stop_driver(Port, Name), - ?line ok. - + stop_driver(Port, Name), + ok. + missing_callbacks(Config) when is_list(Config) -> - ?line Name = 'missing_callback_drv', - ?line Port = start_driver(Config, Name, false), + Name = 'missing_callback_drv', + Port = start_driver(Config, Name, false), - ?line Port ! {self(), {command, "tjenix"}}, - ?line true = erlang:port_command(Port, "halloj"), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(Port, 4711, "mors")), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_call(Port, 17, "hej")), + Port ! {self(), {command, "tjenix"}}, + true = erlang:port_command(Port, "halloj"), + {'EXIT', {badarg, _}} = (catch erlang:port_control(Port, 4711, "mors")), + {'EXIT', {badarg, _}} = (catch erlang:port_call(Port, 17, "hej")), - ?line %% Give the (non-existing) ready_output(), ready_input(), event(), - ?line %% and timeout() some time to be called. - ?line receive after 1000 -> ok end, + %% Give the (non-existing) ready_output(), ready_input(), event(), + %% and timeout() some time to be called. + receive after 1000 -> ok end, - ?line stop_driver(Port, Name), - ?line ok. + stop_driver(Port, Name), + ok. %% Test concurrent calls to driver_select. smp_select(Config) when is_list(Config) -> case os:type() of - {win32,_} -> {skipped, "Test not implemented for this OS"}; - _ -> smp_select0(Config) + {win32,_} -> {skipped, "Test not implemented for this OS"}; + _ -> smp_select0(Config) end. - + smp_select0(Config) -> - ?line DrvName = 'chkio_drv', + DrvName = 'chkio_drv', Path = proplists:get_value(data_dir, Config), erl_ddll:start(), - ?line ok = load_driver(Path, DrvName), + ok = load_driver(Path, DrvName), Master = self(), ProcFun = fun()-> io:format("Worker ~p starting\n",[self()]), - ?line Port = open_port({spawn, DrvName}, []), - smp_select_loop(Port, 100000), - sleep(1000), % wait for driver to handle pending events - ?line true = erlang:port_close(Port), - Master ! {ok,self()}, - io:format("Worker ~p finished\n",[self()]) - end, - ?line Pids = lists:map(fun(_) -> spawn_link(ProcFun) end, - lists:seq(1,4)), + Port = open_port({spawn, DrvName}, []), + smp_select_loop(Port, 100000), + sleep(1000), % wait for driver to handle pending events + true = erlang:port_close(Port), + Master ! {ok,self()}, + io:format("Worker ~p finished\n",[self()]) + end, + Pids = lists:map(fun(_) -> spawn_link(ProcFun) end, + lists:seq(1,4)), TimeoutMsg = make_ref(), {ok,TRef} = timer:send_after(5*1000, TimeoutMsg), % Limit test duration on slow machines smp_select_wait(Pids, TimeoutMsg), timer:cancel(TRef), - ?line ok = erl_ddll:unload_driver(DrvName), - ?line ok = erl_ddll:stop(), + ok = erl_ddll:unload_driver(DrvName), + ok = erl_ddll:stop(), ok. smp_select_loop(_, 0) -> ok; smp_select_loop(Port, N) -> - ?line "ok" = erlang:port_control(Port, ?CHKIO_SMP_SELECT, []), + "ok" = erlang:port_control(Port, ?CHKIO_SMP_SELECT, []), receive - stop -> - io:format("Worker ~p stopped with ~p laps left\n",[self(), N]), - ok + stop -> + io:format("Worker ~p stopped with ~p laps left\n",[self(), N]), + ok after 0 -> - smp_select_loop(Port, N-1) + smp_select_loop(Port, N-1) end. smp_select_wait([], _) -> ok; smp_select_wait(Pids, TimeoutMsg) -> receive - {ok,Pid} when is_pid(Pid) -> - smp_select_wait(lists:delete(Pid,Pids), TimeoutMsg); - TimeoutMsg -> - lists:foreach(fun(Pid)-> Pid ! stop end, - Pids), - smp_select_wait(Pids, TimeoutMsg) + {ok,Pid} when is_pid(Pid) -> + smp_select_wait(lists:delete(Pid,Pids), TimeoutMsg); + TimeoutMsg -> + lists:foreach(fun(Pid)-> Pid ! stop end, + Pids), + smp_select_wait(Pids, TimeoutMsg) end. %% Test driver_select() with new ERL_DRV_USE flag. driver_select_use(Config) when is_list(Config) -> case os:type() of - {win32,_} -> {skipped, "Test not implemented for this OS"}; - _ -> driver_select_use0(Config) + {win32,_} -> {skipped, "Test not implemented for this OS"}; + _ -> driver_select_use0(Config) end. - + driver_select_use0(Config) -> - ?line DrvName = 'chkio_drv', + DrvName = 'chkio_drv', Path = proplists:get_value(data_dir, Config), erl_ddll:start(), - ?line ok = load_driver(Path, DrvName), - ?line Port = open_port({spawn, DrvName}, []), - ?line "ok" = erlang:port_control(Port, ?CHKIO_DRV_USE, []), - ?line {Port,{data,"TheEnd"}} = receive Msg -> Msg - after 10000 -> timeout end, - ?line true = erlang:port_close(Port), - ?line ok = erl_ddll:unload_driver(DrvName), - ?line ok = erl_ddll:stop(), + ok = load_driver(Path, DrvName), + Port = open_port({spawn, DrvName}, []), + "ok" = erlang:port_control(Port, ?CHKIO_DRV_USE, []), + {Port,{data,"TheEnd"}} = receive Msg -> Msg + after 10000 -> timeout end, + true = erlang:port_close(Port), + ok = erl_ddll:unload_driver(DrvName), + ok = erl_ddll:stop(), ok. thread_mseg_alloc_cache_clean(Config) when is_list(Config) -> case {erlang:system_info(threads), - erlang:system_info({allocator,mseg_alloc}), - driver_alloc_sbct()} of - {_, false, _} -> - ?line {skipped, "No mseg_alloc"}; - {false, _, _} -> - ?line {skipped, "No threads"}; - {_, _, false} -> - ?line {skipped, "driver_alloc() not using the alloc_util framework"}; - {_, _, SBCT} when is_integer(SBCT), SBCT > 10*1024*1024 -> - ?line {skipped, "driver_alloc() using too large single block threshold"}; - {_, _, 0} -> - ?line {skipped, "driver_alloc() using too low single block threshold"}; - {true, _MsegAllocInfo, SBCT} -> - ?line DrvName = 'thr_alloc_drv', - ?line Path = proplists:get_value(data_dir, Config), - ?line erl_ddll:start(), - ?line ok = load_driver(Path, DrvName), - ?line Port = open_port({spawn, DrvName}, []), - ?line CCI = 1000, - ?line io:format("CCI = ~p~n", [CCI]), - ?line CCC = mseg_alloc_ccc(), - ?line io:format("CCC = ~p~n", [CCC]), - ?line thread_mseg_alloc_cache_clean_test(Port, - 10, - CCI, - SBCT+100), - ?line true = erlang:port_close(Port), - ?line ok = erl_ddll:unload_driver(DrvName), - ?line ok = erl_ddll:stop(), - ?line ok + erlang:system_info({allocator,mseg_alloc}), + driver_alloc_sbct()} of + {_, false, _} -> + {skipped, "No mseg_alloc"}; + {false, _, _} -> + {skipped, "No threads"}; + {_, _, false} -> + {skipped, "driver_alloc() not using the alloc_util framework"}; + {_, _, SBCT} when is_integer(SBCT), SBCT > 10*1024*1024 -> + {skipped, "driver_alloc() using too large single block threshold"}; + {_, _, 0} -> + {skipped, "driver_alloc() using too low single block threshold"}; + {true, _MsegAllocInfo, SBCT} -> + DrvName = 'thr_alloc_drv', + Path = proplists:get_value(data_dir, Config), + erl_ddll:start(), + ok = load_driver(Path, DrvName), + Port = open_port({spawn, DrvName}, []), + CCI = 1000, + io:format("CCI = ~p~n", [CCI]), + CCC = mseg_alloc_ccc(), + io:format("CCC = ~p~n", [CCC]), + thread_mseg_alloc_cache_clean_test(Port, + 10, + CCI, + SBCT+100), + true = erlang:port_close(Port), + ok = erl_ddll:unload_driver(DrvName), + ok = erl_ddll:stop(), + ok end. mseg_alloc_cci(MsegAllocInfo) -> - ?line {value,{options, OL}} - = lists:keysearch(options, 1, MsegAllocInfo), - ?line {value,{cci,CCI}} = lists:keysearch(cci,1,OL), - ?line CCI. + {value,{options, OL}} + = lists:keysearch(options, 1, MsegAllocInfo), + {value,{cci,CCI}} = lists:keysearch(cci,1,OL), + CCI. mseg_alloc_ccc() -> mseg_alloc_ccc(mseg_inst_info(0)). mseg_alloc_ccc(MsegAllocInfo) -> - ?line {value,{memkind, MKL}} = lists:keysearch(memkind,1,MsegAllocInfo), - ?line {value,{calls, CL}} = lists:keysearch(calls, 1, MKL), - ?line {value,{mseg_check_cache, GigaCCC, CCC}} - = lists:keysearch(mseg_check_cache, 1, CL), - ?line GigaCCC*1000000000 + CCC. + {value,{memkind, MKL}} = lists:keysearch(memkind,1,MsegAllocInfo), + {value,{calls, CL}} = lists:keysearch(calls, 1, MKL), + {value,{mseg_check_cache, GigaCCC, CCC}} + = lists:keysearch(mseg_check_cache, 1, CL), + GigaCCC*1000000000 + CCC. mseg_alloc_cached_segments() -> mseg_alloc_cached_segments(mseg_inst_info(0)). mseg_alloc_cached_segments(MsegAllocInfo) -> MemName = "all memory", - ?line [{memkind,DrvMem}] - = lists:filter(fun(E) -> case E of - {memkind, [{name, MemName} | _]} -> true; - _ -> false - end end, MsegAllocInfo), - ?line {value,{status, SL}} - = lists:keysearch(status, 1, DrvMem), - ?line {value,{cached_segments, CS}} - = lists:keysearch(cached_segments, 1, SL), - ?line CS. + [{memkind,DrvMem}] + = lists:filter(fun(E) -> case E of + {memkind, [{name, MemName} | _]} -> true; + _ -> false + end end, MsegAllocInfo), + {value,{status, SL}} + = lists:keysearch(status, 1, DrvMem), + {value,{cached_segments, CS}} + = lists:keysearch(cached_segments, 1, SL), + CS. mseg_inst_info(I) -> {value, {instance, I, Value}} - = lists:keysearch(I, - 2, - erlang:system_info({allocator,mseg_alloc})), + = lists:keysearch(I, + 2, + erlang:system_info({allocator,mseg_alloc})), Value. driver_alloc_sbct() -> {_, _, _, As} = erlang:system_info(allocator), case lists:keysearch(driver_alloc, 1, As) of - {value,{driver_alloc,DAOPTs}} -> - case lists:keysearch(sbct, 1, DAOPTs) of - {value,{sbct,SBCT}} -> - SBCT; - _ -> - false - end; - _ -> - false + {value,{driver_alloc,DAOPTs}} -> + case lists:keysearch(sbct, 1, DAOPTs) of + {value,{sbct,SBCT}} -> + SBCT; + _ -> + false + end; + _ -> + false end. thread_mseg_alloc_cache_clean_test(_Port, 0, _CCI, _Size) -> - ?line ok; + ok; thread_mseg_alloc_cache_clean_test(Port, N, CCI, Size) -> - ?line wait_until(fun () -> 0 == mseg_alloc_cached_segments() end), - ?line receive after CCI+500 -> ok end, - ?line OCCC = mseg_alloc_ccc(), - ?line "ok" = erlang:port_control(Port, 0, integer_to_list(Size)), - ?line receive after CCI+500 -> ok end, - ?line CCC = mseg_alloc_ccc(), - ?line io:format("CCC = ~p~n", [CCC]), - ?line true = CCC > OCCC, - ?line thread_mseg_alloc_cache_clean_test(Port, N-1, CCI, Size). + wait_until(fun () -> 0 == mseg_alloc_cached_segments() end), + receive after CCI+500 -> ok end, + OCCC = mseg_alloc_ccc(), + "ok" = erlang:port_control(Port, 0, integer_to_list(Size)), + receive after CCI+500 -> ok end, + CCC = mseg_alloc_ccc(), + io:format("CCC = ~p~n", [CCC]), + true = CCC > OCCC, + thread_mseg_alloc_cache_clean_test(Port, N-1, CCI, Size). otp_9302(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line erl_ddll:start(), - ?line ok = load_driver(Path, otp_9302_drv), - ?line Port = open_port({spawn, otp_9302_drv}, []), - ?line true = is_port(Port), - ?line port_command(Port, ""), - ?line {msg, block} = get_port_msg(Port, infinity), - ?line {msg, job} = get_port_msg(Port, infinity), - ?line C = case erlang:system_info(thread_pool_size) of - 0 -> - ?line {msg, cancel} = get_port_msg(Port, infinity), - ?line {msg, job} = get_port_msg(Port, infinity), - ?line false; - _ -> - case get_port_msg(Port, infinity) of - {msg, cancel} -> %% Cancel always fail in Rel >= 15 - ?line {msg, job} = get_port_msg(Port, infinity), - ?line false; - {msg, job} -> - ?line ok, - ?line true - end - end, - ?line {msg, end_of_jobs} = get_port_msg(Port, infinity), - ?line no_msg = get_port_msg(Port, 2000), - ?line port_close(Port), - ?line case C of - true -> - ?line {comment, "Async job cancelled"}; - false -> - ?line {comment, "Async job not cancelled"} - end. + Path = proplists:get_value(data_dir, Config), + erl_ddll:start(), + ok = load_driver(Path, otp_9302_drv), + Port = open_port({spawn, otp_9302_drv}, []), + true = is_port(Port), + port_command(Port, ""), + {msg, block} = get_port_msg(Port, infinity), + {msg, job} = get_port_msg(Port, infinity), + C = case erlang:system_info(thread_pool_size) of + 0 -> + {msg, cancel} = get_port_msg(Port, infinity), + {msg, job} = get_port_msg(Port, infinity), + false; + _ -> + case get_port_msg(Port, infinity) of + {msg, cancel} -> %% Cancel always fail in Rel >= 15 + {msg, job} = get_port_msg(Port, infinity), + false; + {msg, job} -> + ok, + true + end + end, + {msg, end_of_jobs} = get_port_msg(Port, infinity), + no_msg = get_port_msg(Port, 2000), + port_close(Port), + case C of + true -> + {comment, "Async job cancelled"}; + false -> + {comment, "Async job not cancelled"} + end. thr_free_drv(Config) when is_list(Config) -> case erlang:system_info(threads) of - false -> - {skipped, "No thread support"}; - true -> - thr_free_drv_do(Config) + false -> + {skipped, "No thread support"}; + true -> + thr_free_drv_do(Config) end. thr_free_drv_do(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line erl_ddll:start(), - ?line ok = load_driver(Path, thr_free_drv), - ?line MemBefore = driver_alloc_size(), -% io:format("SID=~p", [erlang:system_info(scheduler_id)]), - ?line Port = open_port({spawn, thr_free_drv}, []), - ?line MemPeek = driver_alloc_size(), - ?line true = is_port(Port), - ?line ok = thr_free_drv_control(Port, 0), - ?line port_close(Port), - ?line MemAfter = driver_alloc_size(), - ?line io:format("MemPeek=~p~n", [MemPeek]), - ?line io:format("MemBefore=~p, MemAfter=~p~n", [MemBefore, MemAfter]), - ?line MemBefore = MemAfter, - ?line case MemPeek of - undefined -> ok; - _ -> - ?line true = MemPeek > MemBefore - end, - ?line ok. + Path = proplists:get_value(data_dir, Config), + erl_ddll:start(), + ok = load_driver(Path, thr_free_drv), + MemBefore = driver_alloc_size(), + % io:format("SID=~p", [erlang:system_info(scheduler_id)]), + Port = open_port({spawn, thr_free_drv}, []), + MemPeek = driver_alloc_size(), + true = is_port(Port), + ok = thr_free_drv_control(Port, 0), + port_close(Port), + MemAfter = driver_alloc_size(), + io:format("MemPeek=~p~n", [MemPeek]), + io:format("MemBefore=~p, MemAfter=~p~n", [MemBefore, MemAfter]), + MemBefore = MemAfter, + case MemPeek of + undefined -> ok; + _ -> + true = MemPeek > MemBefore + end, + ok. thr_free_drv_control(Port, N) -> case erlang:port_control(Port, 0, "") of - "done" -> - ok; - "more" -> - erlang:yield(), -% io:format("N=~p, SID=~p", [N, erlang:system_info(scheduler_id)]), - thr_free_drv_control(Port, N+1) + "done" -> + ok; + "more" -> + erlang:yield(), + % io:format("N=~p, SID=~p", [N, erlang:system_info(scheduler_id)]), + thr_free_drv_control(Port, N+1) end. - + async_blast(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line erl_ddll:start(), - ?line ok = load_driver(Path, async_blast_drv), - ?line SchedOnln = erlang:system_info(schedulers_online), - ?line MemBefore = driver_alloc_size(), - ?line Start = os:timestamp(), - ?line Blast = fun () -> - Port = open_port({spawn, async_blast_drv}, []), - true = is_port(Port), - port_command(Port, ""), - receive - {Port, done} -> - ok - end, - port_close(Port) - end, - ?line Ps = lists:map(fun (N) -> - spawn_opt(Blast, - [{scheduler, - (N rem SchedOnln)+ 1}, - monitor]) - end, - lists:seq(1, 100)), - ?line MemMid = driver_alloc_size(), - ?line lists:foreach(fun ({Pid, Mon}) -> - receive - {'DOWN',Mon,process,Pid,_} -> ok - end - end, Ps), - ?line End = os:timestamp(), - ?line MemAfter = driver_alloc_size(), - ?line io:format("MemBefore=~p, MemMid=~p, MemAfter=~p~n", - [MemBefore, MemMid, MemAfter]), - ?line AsyncBlastTime = timer:now_diff(End,Start)/1000000, - ?line io:format("AsyncBlastTime=~p~n", [AsyncBlastTime]), - ?line MemBefore = MemAfter, - ?line erlang:display({async_blast_time, AsyncBlastTime}), - ?line ok. + Path = proplists:get_value(data_dir, Config), + erl_ddll:start(), + ok = load_driver(Path, async_blast_drv), + SchedOnln = erlang:system_info(schedulers_online), + MemBefore = driver_alloc_size(), + Start = os:timestamp(), + Blast = fun () -> + Port = open_port({spawn, async_blast_drv}, []), + true = is_port(Port), + port_command(Port, ""), + receive + {Port, done} -> + ok + end, + port_close(Port) + end, + Ps = lists:map(fun (N) -> + spawn_opt(Blast, + [{scheduler, + (N rem SchedOnln)+ 1}, + monitor]) + end, + lists:seq(1, 100)), + MemMid = driver_alloc_size(), + lists:foreach(fun ({Pid, Mon}) -> + receive + {'DOWN',Mon,process,Pid,_} -> ok + end + end, Ps), + End = os:timestamp(), + MemAfter = driver_alloc_size(), + io:format("MemBefore=~p, MemMid=~p, MemAfter=~p~n", + [MemBefore, MemMid, MemAfter]), + AsyncBlastTime = timer:now_diff(End,Start)/1000000, + io:format("AsyncBlastTime=~p~n", [AsyncBlastTime]), + MemBefore = MemAfter, + erlang:display({async_blast_time, AsyncBlastTime}), + ok. thr_msg_blast_receiver(_Port, N, N) -> ok; thr_msg_blast_receiver(Port, N, Max) -> receive - {Port, hi} -> - thr_msg_blast_receiver(Port, N+1, Max) + {Port, hi} -> + thr_msg_blast_receiver(Port, N+1, Max) end. thr_msg_blast_receiver_proc(Port, Max, Parent, Done) -> case port_control(Port, 0, "") of - "receiver" -> - spawn(fun () -> - thr_msg_blast_receiver_proc(Port, Max+1, Parent, Done) - end), - thr_msg_blast_receiver(Port, 0, Max); - "done" -> - Parent ! Done + "receiver" -> + spawn(fun () -> + thr_msg_blast_receiver_proc(Port, Max+1, Parent, Done) + end), + thr_msg_blast_receiver(Port, 0, Max); + "done" -> + Parent ! Done end. thr_msg_blast(Config) when is_list(Config) -> case erlang:system_info(smp_support) of - false -> - {skipped, "Non-SMP emulator; nothing to test..."}; - true -> - Path = proplists:get_value(data_dir, Config), - erl_ddll:start(), - ok = load_driver(Path, thr_msg_blast_drv), - MemBefore = driver_alloc_size(), - Start = os:timestamp(), - Port = open_port({spawn, thr_msg_blast_drv}, []), - true = is_port(Port), - Done = make_ref(), - Me = self(), - spawn(fun () -> - thr_msg_blast_receiver_proc(Port, 1, Me, Done) - end), - receive - Done -> ok - end, - ok = thr_msg_blast_receiver(Port, 0, 32*10000), - port_close(Port), - End = os:timestamp(), - receive - Garbage -> - ct:fail({received_garbage, Port, Garbage}) - after 2000 -> - ok - end, - MemAfter = driver_alloc_size(), - io:format("MemBefore=~p, MemAfter=~p~n", - [MemBefore, MemAfter]), - ThrMsgBlastTime = timer:now_diff(End,Start)/1000000, - io:format("ThrMsgBlastTime=~p~n", [ThrMsgBlastTime]), - MemBefore = MemAfter, - Res = {thr_msg_blast_time, ThrMsgBlastTime}, - erlang:display(Res), - Res + false -> + {skipped, "Non-SMP emulator; nothing to test..."}; + true -> + Path = proplists:get_value(data_dir, Config), + erl_ddll:start(), + ok = load_driver(Path, thr_msg_blast_drv), + MemBefore = driver_alloc_size(), + Start = os:timestamp(), + Port = open_port({spawn, thr_msg_blast_drv}, []), + true = is_port(Port), + Done = make_ref(), + Me = self(), + spawn(fun () -> + thr_msg_blast_receiver_proc(Port, 1, Me, Done) + end), + receive + Done -> ok + end, + ok = thr_msg_blast_receiver(Port, 0, 32*10000), + port_close(Port), + End = os:timestamp(), + receive + Garbage -> + ct:fail({received_garbage, Port, Garbage}) + after 2000 -> + ok + end, + MemAfter = driver_alloc_size(), + io:format("MemBefore=~p, MemAfter=~p~n", + [MemBefore, MemAfter]), + ThrMsgBlastTime = timer:now_diff(End,Start)/1000000, + io:format("ThrMsgBlastTime=~p~n", [ThrMsgBlastTime]), + MemBefore = MemAfter, + Res = {thr_msg_blast_time, ThrMsgBlastTime}, + erlang:display(Res), + Res end. -define(IN_RANGE(LoW_, VaLuE_, HiGh_), - case in_range(LoW_, VaLuE_, HiGh_) of - true -> ok; - false -> - case erlang:system_info(lock_checking) of - true -> - io:format("~p:~p: Ignore bad sched count due to " - "lock checking~n", - [?MODULE,?LINE]); - false -> - ct:fail({unexpected_sched_counts, VaLuE_}) - end - end). + case in_range(LoW_, VaLuE_, HiGh_) of + true -> ok; + false -> + case erlang:system_info(lock_checking) of + true -> + io:format("~p:~p: Ignore bad sched count due to " + "lock checking~n", + [?MODULE,?LINE]); + false -> + ct:fail({unexpected_sched_counts, VaLuE_}) + end + end). consume_timeslice(Config) when is_list(Config) -> @@ -2047,18 +2047,18 @@ consume_timeslice(Config) when is_list(Config) -> "enabled" = port_control(Port, $E, ""), Proc1 = spawn_link(fun () -> - receive Go -> ok end, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}} - end), + receive Go -> ok end, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}} + end), receive after 100 -> ok end, count_pp_sched_start(), Proc1 ! Go, @@ -2069,18 +2069,18 @@ consume_timeslice(Config) when is_list(Config) -> "disabled" = port_control(Port, $D, ""), Proc2 = spawn_link(fun () -> - receive Go -> ok end, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}}, - Port ! {Parent, {command, ""}} - end), + receive Go -> ok end, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}}, + Port ! {Parent, {command, ""}} + end), receive after 100 -> ok end, count_pp_sched_start(), Proc2 ! Go, @@ -2091,18 +2091,18 @@ consume_timeslice(Config) when is_list(Config) -> "enabled" = port_control(Port, $E, ""), Proc3 = spawn_link(fun () -> - receive Go -> ok end, - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, "") - end), + receive Go -> ok end, + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, "") + end), count_pp_sched_start(), Proc3 ! Go, wait_command_msgs(Port, 10), @@ -2112,18 +2112,18 @@ consume_timeslice(Config) when is_list(Config) -> "disabled" = port_control(Port, $D, ""), Proc4 = spawn_link(fun () -> - receive Go -> ok end, - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, ""), - port_command(Port, "") - end), + receive Go -> ok end, + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, ""), + port_command(Port, "") + end), count_pp_sched_start(), Proc4 ! Go, wait_command_msgs(Port, 10), @@ -2135,43 +2135,43 @@ consume_timeslice(Config) when is_list(Config) -> %% If only one scheduler use port with parallelism set to true, %% in order to trigger scheduling of command signals Port2 = case SOnl of - 1 -> - Port ! {self(), close}, - receive {Port, closed} -> ok end, - open_port({spawn, consume_timeslice_drv}, - [{parallelism, true}]); - _ -> - process_flag(scheduler, 1), - 1 = erlang:system_info(scheduler_id), - Port - end, + 1 -> + Port ! {self(), close}, + receive {Port, closed} -> ok end, + open_port({spawn, consume_timeslice_drv}, + [{parallelism, true}]); + _ -> + process_flag(scheduler, 1), + 1 = erlang:system_info(scheduler_id), + Port + end, count_pp_sched_start(), "enabled" = port_control(Port2, $E, ""), W5 = case SOnl of - 1 -> - false; - _ -> - W1= spawn_opt(fun () -> - 2 = erlang:system_info(scheduler_id), - "sleeped" = port_control(Port2, $S, "") - end, [link,{scheduler,2}]), - receive after 100 -> ok end, - W1 - end, + 1 -> + false; + _ -> + W1= spawn_opt(fun () -> + 2 = erlang:system_info(scheduler_id), + "sleeped" = port_control(Port2, $S, "") + end, [link,{scheduler,2}]), + receive after 100 -> ok end, + W1 + end, Proc5 = spawn_opt(fun () -> - receive Go -> ok end, - 1 = erlang:system_info(scheduler_id), - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}} - end, [link,{scheduler,1}]), + receive Go -> ok end, + 1 = erlang:system_info(scheduler_id), + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}} + end, [link,{scheduler,1}]), receive after 100 -> ok end, Proc5 ! Go, wait_procs_exit([W5, Proc5]), @@ -2179,34 +2179,34 @@ consume_timeslice(Config) when is_list(Config) -> [{Port2, Sprt5}, {Proc5, Sproc5}] = count_pp_sched_stop([Port2, Proc5]), ?IN_RANGE(2, Sproc5, 3), ?IN_RANGE(6, Sprt5, 20), - + count_pp_sched_start(), "disabled" = port_control(Port2, $D, ""), W6 = case SOnl of - 1 -> - false; - _ -> - W2= spawn_opt(fun () -> - 2 = erlang:system_info(scheduler_id), - "sleeped" = port_control(Port2, $S, "") - end, [link,{scheduler,2}]), - receive after 100 -> ok end, - W2 - end, + 1 -> + false; + _ -> + W2= spawn_opt(fun () -> + 2 = erlang:system_info(scheduler_id), + "sleeped" = port_control(Port2, $S, "") + end, [link,{scheduler,2}]), + receive after 100 -> ok end, + W2 + end, Proc6 = spawn_opt(fun () -> - receive Go -> ok end, - 1 = erlang:system_info(scheduler_id), - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}}, - Port2 ! {Parent, {command, ""}} - end, [link,{scheduler,1}]), + receive Go -> ok end, + 1 = erlang:system_info(scheduler_id), + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}}, + Port2 ! {Parent, {command, ""}} + end, [link,{scheduler,1}]), receive after 100 -> ok end, Proc6 ! Go, wait_procs_exit([W6, Proc6]), @@ -2226,19 +2226,19 @@ wait_command_msgs(_, 0) -> ok; wait_command_msgs(Port, N) -> receive - {Port, command} -> - wait_command_msgs(Port, N-1) + {Port, command} -> + wait_command_msgs(Port, N-1) end. in_range(Low, Val, High) when is_integer(Low), - is_integer(Val), - is_integer(High), - Low =< Val, - Val =< High -> + is_integer(Val), + is_integer(High), + Low =< Val, + Val =< High -> true; in_range(Low, Val, High) when is_integer(Low), - is_integer(Val), - is_integer(High) -> + is_integer(Val), + is_integer(High) -> false. count_pp_sched_start() -> @@ -2264,22 +2264,22 @@ do_inc_pn(P, [PN|PNs]) -> inc_pn(P, PNs) -> try - do_inc_pn(P, PNs) + do_inc_pn(P, PNs) catch - throw:undefined -> PNs + throw:undefined -> PNs end. count_proc_sched(Ps, PNs) -> receive - TT when element(1, TT) == trace, element(3, TT) == in -> -% erlang:display(TT), - count_proc_sched(Ps, inc_pn(element(2, TT), PNs)); - TT when element(1, TT) == trace, element(3, TT) == out -> - count_proc_sched(Ps, PNs) + TT when element(1, TT) == trace, element(3, TT) == in -> + % erlang:display(TT), + count_proc_sched(Ps, inc_pn(element(2, TT), PNs)); + TT when element(1, TT) == trace, element(3, TT) == out -> + count_proc_sched(Ps, PNs) after 0 -> - PNs + PNs end. - + a_test(Config) when is_list(Config) -> check_io_debug(). @@ -2293,7 +2293,7 @@ z_test(Config) when is_list(Config) -> check_io_debug() -> get_stable_check_io_info(), {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs} = CheckIoDebug - = erts_debug:get_internal_state(check_io_debug), + = erts_debug:get_internal_state(check_io_debug), HasGetHost = has_gethost(), ct:log("check_io_debug: ~p~n" "HasGetHost: ~p",[CheckIoDebug, HasGetHost]), @@ -2335,26 +2335,26 @@ wait_procs_exit([]) -> wait_procs_exit([P|Ps]) when is_pid(P) -> Mon = erlang:monitor(process, P), receive - {'DOWN', Mon, process, P, _} -> - wait_procs_exit(Ps) + {'DOWN', Mon, process, P, _} -> + wait_procs_exit(Ps) end; wait_procs_exit([_|Ps]) -> wait_procs_exit(Ps). get_port_msg(Port, Timeout) -> receive - {Port, What} -> - {msg, What} + {Port, What} -> + {msg, What} after Timeout -> - no_msg + no_msg end. wait_until(Fun) -> case Fun() of - true -> ok; - false -> - receive after 100 -> ok end, - wait_until(Fun) + true -> ok; + false -> + receive after 100 -> ok end, + wait_until(Fun) end. drv_vsn_str2tup(Str) -> @@ -2385,11 +2385,11 @@ transform_bins(_Transform, Other) -> Other. make_sub_binaries(Term) -> MakeSub = fun(Bin0) -> - Bin1 = <<243:8,0:3,Bin0/binary,31:5,19:8>>, - Sz = size(Bin0), - <<243:8,0:3,Bin:Sz/binary,31:5,19:8>> = id(Bin1), - Bin - end, + Bin1 = <<243:8,0:3,Bin0/binary,31:5,19:8>>, + Sz = size(Bin0), + <<243:8,0:3,Bin:Sz/binary,31:5,19:8>> = id(Bin1), + Bin + end, transform_bins(MakeSub, Term). id(I) -> I. @@ -2439,31 +2439,31 @@ start_driver(Config, Name, Binary) -> %% open port. case Binary of - true -> - open_port({spawn, Name}, [binary]); - false -> - open_port({spawn, Name}, []) + true -> + open_port({spawn, Name}, [binary]); + false -> + open_port({spawn, Name}, []) end. stop_driver(Port, Name) -> - ?line true = erlang:port_close(Port), + true = erlang:port_close(Port), receive - {Port,Message} -> - ct:fail({strange_message_from_port,Message}) + {Port,Message} -> + ct:fail({strange_message_from_port,Message}) after 0 -> - ok + ok end, %% Unload the driver. ok = erl_ddll:unload_driver(Name), - ?line ok = erl_ddll:stop(). + ok = erl_ddll:stop(). load_driver(Dir, Driver) -> case erl_ddll:load_driver(Dir, Driver) of - ok -> ok; - {error, Error} = Res -> - io:format("~s\n", [erl_ddll:format_error(Error)]), - Res + ok -> ok; + {error, Error} = Res -> + io:format("~s\n", [erl_ddll:format_error(Error)]), + Res end. sleep() -> @@ -2478,12 +2478,12 @@ sleep(Ms) when is_integer(Ms), Ms >= 0 -> start_node(Config) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ atom_to_list(proplists:get_value(testcase, Config)) - ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) - ++ "-" - ++ integer_to_list(erlang:unique_integer([positive]))), + ++ "-" + ++ atom_to_list(proplists:get_value(testcase, Config)) + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, "-pa "++Pa}]). stop_node(Node) -> @@ -2491,37 +2491,37 @@ stop_node(Node) -> wait_deallocations() -> try - erts_debug:set_internal_state(wait, deallocations) + erts_debug:set_internal_state(wait, deallocations) catch error:undef -> - erts_debug:set_internal_state(available_internal_state, true), - wait_deallocations() + erts_debug:set_internal_state(available_internal_state, true), + wait_deallocations() end. driver_alloc_size() -> case erlang:system_info(smp_support) of - true -> - ok; - false -> - %% driver_alloc also used by elements in lock-free queues, - %% give these some time to be deallocated... - receive after 100 -> ok end + true -> + ok; + false -> + %% driver_alloc also used by elements in lock-free queues, + %% give these some time to be deallocated... + receive after 100 -> ok end end, wait_deallocations(), case erlang:system_info({allocator_sizes, driver_alloc}) of - false -> - undefined; - MemInfo -> - CS = lists:foldl( - fun ({instance, _, L}, Acc) -> - {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L), - {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L), - [MBCS,SBCS | Acc] - end, - [], - MemInfo), - lists:foldl( - fun(L, Sz0) -> - {value,{_,Sz,_,_}} = lists:keysearch(blocks_size, 1, L), - Sz0+Sz - end, 0, CS) + false -> + undefined; + MemInfo -> + CS = lists:foldl( + fun ({instance, _, L}, Acc) -> + {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L), + {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L), + [MBCS,SBCS | Acc] + end, + [], + MemInfo), + lists:foldl( + fun(L, Sz0) -> + {value,{_,Sz,_,_}} = lists:keysearch(blocks_size, 1, L), + Sz0+Sz + end, 0, CS) end. diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index 667f1ae9b4..cefa35a3ee 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -18,8 +18,7 @@ %% %CopyrightEnd% -module(efile_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2]). +-export([all/0, suite/0]). -export([iter_max_files/1, async_dist/1]). -export([do_iter_max_files/2, do_async_dist/1]). @@ -31,21 +30,6 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [iter_max_files, async_dist]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - do_async_dist(Dir) -> X = 100, AT = erlang:system_info(thread_pool_size), @@ -70,21 +54,21 @@ file_keys(Dir,Num,FdList,FnList) -> Name = "dummy"++integer_to_list(Num), FN = filename:join([Dir,Name]), case file:open(FN,[write,raw]) of - {ok,FD} -> - {file_descriptor,prim_file,{Port,_}} = FD, - <> = - iolist_to_binary(erlang:port_control(Port,$K,[])), - [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])]; - {error,_} -> - % Try freeing up FD's if there are any - case FdList of - [] -> - exit({cannot_open_file,FN}); - _ -> - [ file:close(FD) || FD <- FdList ], - [ file:delete(F) || F <- FnList ], - file_keys(Dir,Num,[],[]) - end + {ok,FD} -> + {file_descriptor,prim_file,{Port,_}} = FD, + <> = + iolist_to_binary(erlang:port_control(Port,$K,[])), + [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])]; + {error,_} -> + % Try freeing up FD's if there are any + case FdList of + [] -> + exit({cannot_open_file,FN}); + _ -> + [ file:close(FD) || FD <- FdList ], + [ file:delete(F) || F <- FnList ], + file_keys(Dir,Num,[],[]) + end end. %% Check that the distribution of files over async threads is fair @@ -96,32 +80,32 @@ async_dist(Config) when is_list(Config) -> Max = 0.5, lists:foreach(fun(Size) -> - {ok,Node} = - test_server:start_node - (test_iter_max_files,slave, - [{args, - "+A "++integer_to_list(Size)++ - " -pa " ++ Dir}]), - {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist, - [DataDir]), - test_server:stop_node(Node), - if - SD > Max -> - io:format("Bad async queue distribution for " - "~p async threads:~n" - " Standard deviation is ~p~n" - " Key distribution:~n ~lp~n", - [Size,SD,Distr]), - exit({bad_async_dist,Size,SD,Distr}); - true -> - io:format("OK async queue distribution for " - "~p async threads:~n" - " Standard deviation is ~p~n" - " Key distribution:~n ~lp~n", - [Size,SD,Distr]), - ok - end - end, AsyncSizes), + {ok,Node} = + test_server:start_node + (test_iter_max_files,slave, + [{args, + "+A "++integer_to_list(Size)++ + " -pa " ++ Dir}]), + {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist, + [DataDir]), + test_server:stop_node(Node), + if + SD > Max -> + io:format("Bad async queue distribution for " + "~p async threads:~n" + " Standard deviation is ~p~n" + " Key distribution:~n ~lp~n", + [Size,SD,Distr]), + exit({bad_async_dist,Size,SD,Distr}); + true -> + io:format("OK async queue distribution for " + "~p async threads:~n" + " Standard deviation is ~p~n" + " Key distribution:~n ~lp~n", + [Size,SD,Distr]), + ok + end + end, AsyncSizes), ok. %% @@ -136,46 +120,46 @@ iter_max_files(Config) when is_list(Config) -> %% Run on a different node in order to set the max ports Dir = filename:dirname(code:which(?MODULE)), {ok,Node} = test_server:start_node(test_iter_max_files,slave, - [{args,"+Q 1524 -pa " ++ Dir}]), + [{args,"+Q 1524 -pa " ++ Dir}]), L = rpc:call(Node,?MODULE,do_iter_max_files,[N, TestFile]), test_server:stop_node(Node), io:format("Number of files opened in each test:~n~w\n", [L]), all_equal(L), Head = hd(L), if Head >= 2 -> ok; - true -> ct:fail(too_few_files) + true -> ct:fail(too_few_files) end, {comment, "Max files: " ++ integer_to_list(hd(L))}. do_iter_max_files(N, Name) when N > 0 -> - ?line [max_files(Name)| do_iter_max_files(N-1, Name)]; + [max_files(Name)| do_iter_max_files(N-1, Name)]; do_iter_max_files(_, _) -> []. all_equal([E, E| T]) -> - ?line all_equal([E| T]); + all_equal([E| T]); all_equal([_]) -> ok; all_equal([]) -> ok. - + max_files(Name) -> - ?line Fds = open_files(Name), - ?line N = length(Fds), - ?line close_files(Fds), + Fds = open_files(Name), + N = length(Fds), + close_files(Fds), N. close_files([Fd| Fds]) -> - ?line file:close(Fd), - ?line close_files(Fds); + file:close(Fd), + close_files(Fds); close_files([]) -> ok. open_files(Name) -> - ?line case file:open(Name, [read,raw]) of - {ok, Fd} -> - [Fd| open_files(Name)]; - {error, _Reason} -> -% io:format("Error reason: ~p", [_Reason]), - [] - end. + case file:open(Name, [read,raw]) of + {ok, Fd} -> + [Fd| open_files(Name)]; + {error, _Reason} -> + % io:format("Error reason: ~p", [_Reason]), + [] + end. diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl index 316dc4b5c5..41a761229c 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE.erl +++ b/erts/emulator/test/erl_drv_thread_SUITE.erl @@ -39,11 +39,11 @@ all() -> %% Testcases %% %% %% -basic(Cfg) -> ?line drv_case(Cfg, basic). +basic(Cfg) -> drv_case(Cfg, basic). -rwlock(Cfg) -> ?line drv_case(Cfg, rwlock). +rwlock(Cfg) -> drv_case(Cfg, rwlock). -tsd(Cfg) -> ?line drv_case(Cfg, tsd). +tsd(Cfg) -> drv_case(Cfg, tsd). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% @@ -59,56 +59,54 @@ drv_case(Config, CaseName, Command) when is_list(Command) -> drv_case(Config, CaseName, Command, ?DEFAULT_TIMETRAP_SECS). drv_case(Config, CaseName, TimeTrap, Command) when is_list(Command), - is_integer(TimeTrap) -> + is_integer(TimeTrap) -> drv_case(Config, CaseName, Command, TimeTrap); drv_case(Config, CaseName, Command, TimeTrap) when is_list(Config), - is_atom(CaseName), - is_list(Command), - is_integer(TimeTrap) -> + is_atom(CaseName), + is_list(Command), + is_integer(TimeTrap) -> case test_server:os_type() of - {Family, _} when Family == unix; Family == win32 -> - ?line run_drv_case(Config, CaseName, Command, TimeTrap); - SkipOs -> - ?line {skipped, - lists:flatten(["Not run on " - | io_lib:format("~p",[SkipOs])])} + {Family, _} when Family == unix; Family == win32 -> + run_drv_case(Config, CaseName, Command, TimeTrap); + SkipOs -> + {skipped, lists:flatten(["Not run on " | io_lib:format("~p",[SkipOs])])} end. run_drv_case(Config, CaseName, Command, TimeTrap) -> ct:timetrap({seconds, TimeTrap}), - ?line DataDir = proplists:get_value(data_dir,Config), + DataDir = proplists:get_value(data_dir,Config), case erl_ddll:load_driver(DataDir, CaseName) of - ok -> ok; - {error, Error} -> + ok -> ok; + {error, Error} -> ct:fail(erl_ddll:format_error(Error)) end, - ?line Port = open_port({spawn, atom_to_list(CaseName)}, []), - ?line true = is_port(Port), - ?line Port ! {self(), {command, Command}}, - ?line Result = receive_drv_result(Port, CaseName), - ?line Port ! {self(), close}, - ?line receive - {Port, closed} -> - ok - end, - ?line ok = erl_ddll:unload_driver(CaseName), - ?line Result. + Port = open_port({spawn, atom_to_list(CaseName)}, []), + true = is_port(Port), + Port ! {self(), {command, Command}}, + Result = receive_drv_result(Port, CaseName), + Port ! {self(), close}, + receive + {Port, closed} -> + ok + end, + ok = erl_ddll:unload_driver(CaseName), + Result. receive_drv_result(Port, CaseName) -> - ?line receive - {print, Port, CaseName, Str} -> - ?line io:format("~s", [Str]), - ?line receive_drv_result(Port, CaseName); - {'EXIT', Port, Error} -> - ?line ct:fail(Error); - {'EXIT', error, Error} -> - ?line ct:fail(Error); - {failed, Port, CaseName, Comment} -> - ?line ct:fail(Comment); - {skipped, Port, CaseName, Comment} -> - ?line {skipped, Comment}; - {succeeded, Port, CaseName, ""} -> - ?line succeeded; - {succeeded, Port, CaseName, Comment} -> - ?line {comment, Comment} - end. + receive + {print, Port, CaseName, Str} -> + io:format("~s", [Str]), + receive_drv_result(Port, CaseName); + {'EXIT', Port, Error} -> + ct:fail(Error); + {'EXIT', error, Error} -> + ct:fail(Error); + {failed, Port, CaseName, Comment} -> + ct:fail(Comment); + {skipped, Port, CaseName, Comment} -> + {skipped, Comment}; + {succeeded, Port, CaseName, ""} -> + succeeded; + {succeeded, Port, CaseName, Comment} -> + {comment, Comment} + end. diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index d7257ff7aa..56fdfc35c0 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -35,17 +35,17 @@ % Test cases -export([links/1, - dist_links/1, - monitor_nodes/1, - process_monitors/1, - dist_process_monitors/1, - busy_dist_port_monitor/1, - busy_dist_port_link/1, - otp_5772_link/1, - otp_5772_dist_link/1, - otp_5772_monitor/1, - otp_5772_dist_monitor/1, - otp_7946/1]). + dist_links/1, + monitor_nodes/1, + process_monitors/1, + dist_process_monitors/1, + busy_dist_port_monitor/1, + busy_dist_port_link/1, + otp_5772_link/1, + otp_5772_dist_link/1, + otp_5772_monitor/1, + otp_5772_dist_monitor/1, + otp_7946/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -64,17 +64,15 @@ -record(erl_link, {type = ?LINK_UNDEF, - pid = [], - targets = []}). + pid = [], + targets = []}). % This is to be kept in sync with erl_bif_info.c (make_monitor_list) --record(erl_monitor, { - type, % MON_ORIGIN or MON_TARGET (1 or 3) - ref, - pid, % Process or nodename - name = [] % registered name or [] - }). +-record(erl_monitor, {type, % MON_ORIGIN or MON_TARGET (1 or 3) + ref, + pid, % Process or nodename + name = []}). % registered name or [] suite() -> @@ -89,8 +87,8 @@ all() -> init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> case catch erts_debug:get_internal_state(available_internal_state) of - true -> ok; - _ -> erts_debug:set_internal_state(available_internal_state, true) + true -> ok; + _ -> erts_debug:set_internal_state(available_internal_state, true) end, Config. @@ -106,394 +104,394 @@ end_per_suite(_Config) -> %% Tests node local links links(Config) when is_list(Config) -> - ?line common_link_test(node(), node()), - ?line true = link(self()), - ?line [] = find_erl_link(self(), ?LINK_PID, self()), - ?line true = unlink(self()), - ?line ok. + common_link_test(node(), node()), + true = link(self()), + [] = find_erl_link(self(), ?LINK_PID, self()), + true = unlink(self()), + ok. %% Tests distributed links dist_links(Config) when is_list(Config) -> - ?line [NodeName] = get_names(1, dist_link), - ?line {ok, Node} = start_node(NodeName), - ?line common_link_test(node(), Node), - ?line TP4 = spawn(?MODULE, test_proc, []), - ?line TP5 = spawn(?MODULE, test_proc, []), - ?line TP6 = spawn(Node, ?MODULE, test_proc, []), - ?line true = tp_call(TP6, fun() -> link(TP4) end), - ?line check_link(TP4, TP6), - ?line true = tp_call(TP5, - fun() -> - process_flag(trap_exit,true), - link(TP6) - end), - ?line check_link(TP5, TP6), - ?line rpc:cast(Node, erlang, halt, []), - ?line wait_until(fun () -> ?line is_proc_dead(TP4) end), - ?line check_unlink(TP4, TP6), - ?line true = tp_call(TP5, - fun() -> - receive - {'EXIT', TP6, noconnection} -> - true - end - end), - ?line check_unlink(TP5, TP6), - ?line tp_cast(TP5, fun() -> exit(normal) end), - ?line ok. + [NodeName] = get_names(1, dist_link), + {ok, Node} = start_node(NodeName), + common_link_test(node(), Node), + TP4 = spawn(?MODULE, test_proc, []), + TP5 = spawn(?MODULE, test_proc, []), + TP6 = spawn(Node, ?MODULE, test_proc, []), + true = tp_call(TP6, fun() -> link(TP4) end), + check_link(TP4, TP6), + true = tp_call(TP5, + fun() -> + process_flag(trap_exit,true), + link(TP6) + end), + check_link(TP5, TP6), + rpc:cast(Node, erlang, halt, []), + wait_until(fun () -> is_proc_dead(TP4) end), + check_unlink(TP4, TP6), + true = tp_call(TP5, + fun() -> + receive + {'EXIT', TP6, noconnection} -> + true + end + end), + check_unlink(TP5, TP6), + tp_cast(TP5, fun() -> exit(normal) end), + ok. common_link_test(NodeA, NodeB) -> - ?line TP1 = spawn(NodeA, ?MODULE, test_proc, []), - ?line check_unlink(TP1, self()), - ?line TP2 = tp_call(TP1, - fun () -> - spawn_link(NodeB, ?MODULE, test_proc, []) - end), - ?line check_link(TP1, TP2), - ?line true = tp_call(TP2, fun() -> unlink(TP1) end), - ?line check_unlink(TP1, TP2), - ?line true = tp_call(TP2, fun() -> link(TP1) end), - ?line check_link(TP1, TP2), - ?line false = tp_call(TP2, fun() -> process_flag(trap_exit, true) end), - ?line tp_cast(TP1, fun () -> exit(died) end), - ?line true = tp_call(TP2, fun() -> - receive - {'EXIT', TP1, died} -> - true - end - end), - ?line check_unlink(TP1, TP2), - ?line TP3 = tp_call(TP2, - fun () -> - spawn_link(NodeA, ?MODULE, test_proc, []) - end), - ?line check_link(TP3, TP2), - ?line tp_cast(TP2, fun() -> exit(died) end), - ?line wait_until(fun () -> ?line is_proc_dead(TP3) end), - ?line check_unlink(TP3, TP2), - ?line ok. + TP1 = spawn(NodeA, ?MODULE, test_proc, []), + check_unlink(TP1, self()), + TP2 = tp_call(TP1, + fun () -> + spawn_link(NodeB, ?MODULE, test_proc, []) + end), + check_link(TP1, TP2), + true = tp_call(TP2, fun() -> unlink(TP1) end), + check_unlink(TP1, TP2), + true = tp_call(TP2, fun() -> link(TP1) end), + check_link(TP1, TP2), + false = tp_call(TP2, fun() -> process_flag(trap_exit, true) end), + tp_cast(TP1, fun () -> exit(died) end), + true = tp_call(TP2, fun() -> + receive + {'EXIT', TP1, died} -> + true + end + end), + check_unlink(TP1, TP2), + TP3 = tp_call(TP2, + fun () -> + spawn_link(NodeA, ?MODULE, test_proc, []) + end), + check_link(TP3, TP2), + tp_cast(TP2, fun() -> exit(died) end), + wait_until(fun () -> is_proc_dead(TP3) end), + check_unlink(TP3, TP2), + ok. %% Tests monitor of nodes monitor_nodes(Config) when is_list(Config) -> - ?line [An, Bn, Cn, Dn] = get_names(4, dist_link), - ?line {ok, A} = start_node(An), - ?line {ok, B} = start_node(Bn), - ?line C = list_to_atom(lists:concat([Cn, "@", hostname()])), - ?line D = list_to_atom(lists:concat([Dn, "@", hostname()])), - ?line 0 = no_of_monitor_node(self(), A), - ?line 0 = no_of_monitor_node(self(), B), - ?line monitor_node(A, true), - ?line monitor_node(B, true), - ?line monitor_node(D, true), - ?line monitor_node(D, true), + [An, Bn, Cn, Dn] = get_names(4, dist_link), + {ok, A} = start_node(An), + {ok, B} = start_node(Bn), + C = list_to_atom(lists:concat([Cn, "@", hostname()])), + D = list_to_atom(lists:concat([Dn, "@", hostname()])), + 0 = no_of_monitor_node(self(), A), + 0 = no_of_monitor_node(self(), B), + monitor_node(A, true), + monitor_node(B, true), + monitor_node(D, true), + monitor_node(D, true), %% Has been known to crash the emulator. - ?line {memory,_} = process_info(self(), memory), - - ?line monitor_node(A, false), - ?line monitor_node(B, true), - ?line monitor_node(C, true), - ?line monitor_node(C, false), - ?line monitor_node(C, true), - ?line monitor_node(B, true), - ?line monitor_node(A, false), - ?line monitor_node(B, true), - ?line monitor_node(B, false), - ?line monitor_node(A, true), - ?line check_monitor_node(self(), A, 1), - ?line check_monitor_node(self(), B, 3), - ?line check_monitor_node(self(), C, 0), - ?line check_monitor_node(self(), D, 0), - ?line receive {nodedown, C} -> ok end, - ?line receive {nodedown, C} -> ok end, - ?line receive {nodedown, C} -> ok end, - ?line receive {nodedown, D} -> ok end, - ?line receive {nodedown, D} -> ok end, - ?line stop_node(A), - ?line receive {nodedown, A} -> ok end, - ?line check_monitor_node(self(), A, 0), - ?line check_monitor_node(self(), B, 3), - ?line stop_node(B), - ?line receive {nodedown, B} -> ok end, - ?line receive {nodedown, B} -> ok end, - ?line receive {nodedown, B} -> ok end, - ?line check_monitor_node(self(), B, 0), - ?line receive - {nodedown, X} -> - ?line ct:fail({unexpected_nodedown, X}) - after 0 -> - ?line ok - end, - ?line ok. - + {memory,_} = process_info(self(), memory), + + monitor_node(A, false), + monitor_node(B, true), + monitor_node(C, true), + monitor_node(C, false), + monitor_node(C, true), + monitor_node(B, true), + monitor_node(A, false), + monitor_node(B, true), + monitor_node(B, false), + monitor_node(A, true), + check_monitor_node(self(), A, 1), + check_monitor_node(self(), B, 3), + check_monitor_node(self(), C, 0), + check_monitor_node(self(), D, 0), + receive {nodedown, C} -> ok end, + receive {nodedown, C} -> ok end, + receive {nodedown, C} -> ok end, + receive {nodedown, D} -> ok end, + receive {nodedown, D} -> ok end, + stop_node(A), + receive {nodedown, A} -> ok end, + check_monitor_node(self(), A, 0), + check_monitor_node(self(), B, 3), + stop_node(B), + receive {nodedown, B} -> ok end, + receive {nodedown, B} -> ok end, + receive {nodedown, B} -> ok end, + check_monitor_node(self(), B, 0), + receive + {nodedown, X} -> + ct:fail({unexpected_nodedown, X}) + after 0 -> + ok + end, + ok. + %% Tests node local process monitors process_monitors(Config) when is_list(Config) -> - ?line common_process_monitors(node(), node()), - ?line Mon1 = erlang:monitor(process,self()), - ?line [] = find_erl_monitor(self(), Mon1), - ?line [Name] = get_names(1, process_monitors), - ?line true = register(Name, self()), - ?line Mon2 = erlang:monitor(process, Name), - ?line [] = find_erl_monitor(self(), Mon2), - ?line receive - {'DOWN', Mon1, _, _, _} = Msg -> - ?line ct:fail({unexpected_down_msg, Msg}); - {'DOWN', Mon2, _, _, _} = Msg -> - ?line ct:fail({unexpected_down_msg, Msg}) - after 500 -> - ?line true = erlang:demonitor(Mon1), - ?line true = erlang:demonitor(Mon2), - ?line ok - end. + common_process_monitors(node(), node()), + Mon1 = erlang:monitor(process,self()), + [] = find_erl_monitor(self(), Mon1), + [Name] = get_names(1, process_monitors), + true = register(Name, self()), + Mon2 = erlang:monitor(process, Name), + [] = find_erl_monitor(self(), Mon2), + receive + {'DOWN', Mon1, _, _, _} = Msg -> + ct:fail({unexpected_down_msg, Msg}); + {'DOWN', Mon2, _, _, _} = Msg -> + ct:fail({unexpected_down_msg, Msg}) + after 500 -> + true = erlang:demonitor(Mon1), + true = erlang:demonitor(Mon2), + ok + end. %% Tests distributed process monitors dist_process_monitors(Config) when is_list(Config) -> - ?line [Name] = get_names(1,dist_process_monitors), - ?line {ok, Node} = start_node(Name), - ?line common_process_monitors(node(), Node), - ?line TP1 = spawn(Node, ?MODULE, test_proc, []), - ?line R1 = erlang:monitor(process, TP1), - ?line TP1O = get_down_object(TP1, self()), - ?line check_process_monitor(self(), TP1, R1), - ?line tp_cast(TP1, fun () -> halt() end), - ?line receive - {'DOWN',R1,process,TP1O,noconnection} -> - ?line ok - end, - ?line check_process_demonitor(self(), TP1, R1), - ?line R2 = erlang:monitor(process, TP1), - ?line receive - {'DOWN',R2,process,TP1O,noconnection} -> - ?line ok - end, - ?line check_process_demonitor(self(), TP1, R2), - ?line ok. + [Name] = get_names(1,dist_process_monitors), + {ok, Node} = start_node(Name), + common_process_monitors(node(), Node), + TP1 = spawn(Node, ?MODULE, test_proc, []), + R1 = erlang:monitor(process, TP1), + TP1O = get_down_object(TP1, self()), + check_process_monitor(self(), TP1, R1), + tp_cast(TP1, fun () -> halt() end), + receive + {'DOWN',R1,process,TP1O,noconnection} -> + ok + end, + check_process_demonitor(self(), TP1, R1), + R2 = erlang:monitor(process, TP1), + receive + {'DOWN',R2,process,TP1O,noconnection} -> + ok + end, + check_process_demonitor(self(), TP1, R2), + ok. common_process_monitors(NodeA, NodeB) -> - ?line TP1 = spawn(NodeA, ?MODULE, test_proc, []), - ?line TP2 = spawn(NodeB, ?MODULE, test_proc, []), - ?line run_common_process_monitors(TP1, TP2), - ?line TP3 = spawn(NodeA, ?MODULE, test_proc, []), - ?line TP4 = spawn(NodeB, ?MODULE, test_proc, []), - ?line [TP4N] = get_names(1, common_process_monitors), - ?line true = tp_call(TP4, fun () -> register(TP4N,self()) end), - ?line run_common_process_monitors(TP3, - case node() == node(TP4) of - true -> TP4N; - false -> {TP4N, node(TP4)} - end), - ?line ok. + TP1 = spawn(NodeA, ?MODULE, test_proc, []), + TP2 = spawn(NodeB, ?MODULE, test_proc, []), + run_common_process_monitors(TP1, TP2), + TP3 = spawn(NodeA, ?MODULE, test_proc, []), + TP4 = spawn(NodeB, ?MODULE, test_proc, []), + [TP4N] = get_names(1, common_process_monitors), + true = tp_call(TP4, fun () -> register(TP4N,self()) end), + run_common_process_monitors(TP3, + case node() == node(TP4) of + true -> TP4N; + false -> {TP4N, node(TP4)} + end), + ok. run_common_process_monitors(TP1, TP2) -> - ?line R1 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end), - ?line check_process_monitor(TP1, TP2, R1), - - ?line tp_call(TP2, fun () -> catch erlang:demonitor(R1) end), - ?line check_process_monitor(TP1, TP2, R1), - - ?line true = tp_call(TP1, fun () -> erlang:demonitor(R1) end), - ?line check_process_demonitor(TP1, TP2, R1), - - ?line R2 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end), - ?line TP2O = get_down_object(TP2, TP1), - ?line check_process_monitor(TP1, TP2, R2), - ?line tp_cast(TP2, fun () -> exit(bye) end), - ?line wait_until(fun () -> ?line is_proc_dead(TP2) end), - ?line ok = tp_call(TP1, fun () -> - ?line receive - {'DOWN',R2,process,TP2O,bye} -> - ?line ok - end - end), - ?line check_process_demonitor(TP1, TP2, R2), - - ?line R3 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end), - ?line ok = tp_call(TP1, fun () -> - ?line receive - {'DOWN',R3,process,TP2O,noproc} -> - ?line ok - end - end), - ?line check_process_demonitor(TP1, TP2, R3), - - ?line tp_cast(TP1, fun () -> exit(normal) end), - ?line wait_until(fun () -> ?line is_proc_dead(TP1) end), - ?line ok. - + R1 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end), + check_process_monitor(TP1, TP2, R1), + + tp_call(TP2, fun () -> catch erlang:demonitor(R1) end), + check_process_monitor(TP1, TP2, R1), + + true = tp_call(TP1, fun () -> erlang:demonitor(R1) end), + check_process_demonitor(TP1, TP2, R1), + + R2 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end), + TP2O = get_down_object(TP2, TP1), + check_process_monitor(TP1, TP2, R2), + tp_cast(TP2, fun () -> exit(bye) end), + wait_until(fun () -> is_proc_dead(TP2) end), + ok = tp_call(TP1, fun () -> + receive + {'DOWN',R2,process,TP2O,bye} -> + ok + end + end), + check_process_demonitor(TP1, TP2, R2), + + R3 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end), + ok = tp_call(TP1, fun () -> + receive + {'DOWN',R3,process,TP2O,noproc} -> + ok + end + end), + check_process_demonitor(TP1, TP2, R3), + + tp_cast(TP1, fun () -> exit(normal) end), + wait_until(fun () -> is_proc_dead(TP1) end), + ok. + %% Tests distributed monitor/2, demonitor/1, and 'DOWN' message %% over busy distribution port busy_dist_port_monitor(Config) when is_list(Config) -> - ?line Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of - "true" -> start_busy_dist_port_tracer(); - _ -> false - end, + Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of + "true" -> start_busy_dist_port_tracer(); + _ -> false + end, - ?line [An] = get_names(1, busy_dist_port_monitor), - ?line {ok, A} = start_node(An), - ?line TP1 = spawn(A, ?MODULE, test_proc, []), + [An] = get_names(1, busy_dist_port_monitor), + {ok, A} = start_node(An), + TP1 = spawn(A, ?MODULE, test_proc, []), %% Check monitor over busy port - ?line M1 = suspend_on_busy_test(A, - "erlang:monitor(process, TP1)", - fun () -> erlang:monitor(process, TP1) end), - ?line check_process_monitor(self(), TP1, M1), + M1 = suspend_on_busy_test(A, + "erlang:monitor(process, TP1)", + fun () -> erlang:monitor(process, TP1) end), + check_process_monitor(self(), TP1, M1), %% Check demonitor over busy port - ?line suspend_on_busy_test(A, - "erlang:demonitor(M1)", - fun () -> erlang:demonitor(M1) end), - ?line check_process_demonitor(self(), TP1, M1), + suspend_on_busy_test(A, + "erlang:demonitor(M1)", + fun () -> erlang:demonitor(M1) end), + check_process_demonitor(self(), TP1, M1), %% Check down message over busy port - ?line TP2 = spawn(?MODULE, test_proc, []), - ?line M2 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end), - ?line check_process_monitor(TP1, TP2, M2), - ?line Ref = make_ref(), - ?line Busy = make_busy(A, 1000), - ?line receive after 100 -> ok end, - ?line tp_cast(TP2, fun () -> exit(Ref) end), - ?line receive after 100 -> ok end, - ?line unmake_busy(Busy), - ?line Ref = tp_call(TP1, fun () -> - receive - {'DOWN', M2, process, TP2, Ref} -> - Ref - end - end), - ?line tp_cast(TP1, fun () -> exit(normal) end), - ?line stop_node(A), - ?line stop_busy_dist_port_tracer(Tracer), - ?line ok. + TP2 = spawn(?MODULE, test_proc, []), + M2 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end), + check_process_monitor(TP1, TP2, M2), + Ref = make_ref(), + Busy = make_busy(A, 1000), + receive after 100 -> ok end, + tp_cast(TP2, fun () -> exit(Ref) end), + receive after 100 -> ok end, + unmake_busy(Busy), + Ref = tp_call(TP1, fun () -> + receive + {'DOWN', M2, process, TP2, Ref} -> + Ref + end + end), + tp_cast(TP1, fun () -> exit(normal) end), + stop_node(A), + stop_busy_dist_port_tracer(Tracer), + ok. %% Tests distributed link/1, unlink/1, and 'EXIT' %% message over busy distribution port busy_dist_port_link(Config) when is_list(Config) -> - ?line Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of - "true" -> start_busy_dist_port_tracer(); - _ -> false - end, - - ?line [An] = get_names(1, busy_dist_port_link), - ?line {ok, A} = start_node(An), - ?line TP1 = spawn(A, ?MODULE, test_proc, []), + Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of + "true" -> start_busy_dist_port_tracer(); + _ -> false + end, + + [An] = get_names(1, busy_dist_port_link), + {ok, A} = start_node(An), + TP1 = spawn(A, ?MODULE, test_proc, []), %% Check link over busy port - ?line suspend_on_busy_test(A, - "link(TP1)", - fun () -> link(TP1) end), - ?line check_link(self(), TP1), + suspend_on_busy_test(A, + "link(TP1)", + fun () -> link(TP1) end), + check_link(self(), TP1), %% Check unlink over busy port - ?line suspend_on_busy_test(A, - "unlink(TP1)", - fun () -> unlink(TP1) end), - ?line check_unlink(self(), TP1), + suspend_on_busy_test(A, + "unlink(TP1)", + fun () -> unlink(TP1) end), + check_unlink(self(), TP1), %% Check trap exit message over busy port - ?line TP2 = spawn(?MODULE, test_proc, []), - ?line ok = tp_call(TP1, fun () -> - process_flag(trap_exit, true), - link(TP2), - ok - end), - ?line check_link(TP1, TP2), - ?line Ref = make_ref(), - ?line Busy = make_busy(A, 1000), - ?line receive after 100 -> ok end, - ?line tp_cast(TP2, fun () -> exit(Ref) end), - ?line receive after 100 -> ok end, - ?line unmake_busy(Busy), - ?line Ref = tp_call(TP1, fun () -> - receive - {'EXIT', TP2, Ref} -> - Ref - end - end), - ?line tp_cast(TP1, fun () -> exit(normal) end), - ?line stop_node(A), - ?line stop_busy_dist_port_tracer(Tracer), - ?line ok. + TP2 = spawn(?MODULE, test_proc, []), + ok = tp_call(TP1, fun () -> + process_flag(trap_exit, true), + link(TP2), + ok + end), + check_link(TP1, TP2), + Ref = make_ref(), + Busy = make_busy(A, 1000), + receive after 100 -> ok end, + tp_cast(TP2, fun () -> exit(Ref) end), + receive after 100 -> ok end, + unmake_busy(Busy), + Ref = tp_call(TP1, fun () -> + receive + {'EXIT', TP2, Ref} -> + Ref + end + end), + tp_cast(TP1, fun () -> exit(normal) end), + stop_node(A), + stop_busy_dist_port_tracer(Tracer), + ok. otp_5772_link(Config) when is_list(Config) -> - ?line otp_5772_link_test(node()). + otp_5772_link_test(node()). otp_5772_dist_link(Config) when is_list(Config) -> - ?line [An] = get_names(1, otp_5772_dist_link), - ?line {ok, A} = start_node(An), - ?line otp_5772_link_test(A), - ?line stop_node(A). + [An] = get_names(1, otp_5772_dist_link), + {ok, A} = start_node(An), + otp_5772_link_test(A), + stop_node(A). otp_5772_link_test(Node) -> - ?line Prio = process_flag(priority, high), - ?line TE = process_flag(trap_exit, true), - ?line TP1 = spawn_opt(Node, ?MODULE, test_proc, [], - [link, {priority, low}]), + Prio = process_flag(priority, high), + TE = process_flag(trap_exit, true), + TP1 = spawn_opt(Node, ?MODULE, test_proc, [], + [link, {priority, low}]), exit(TP1, bang), unlink(TP1), - ?line receive - {'EXIT', TP1, _} -> - ?line ok - after 0 -> - ?line ok - end, - ?line receive - {'EXIT', TP1, _} = Exit -> - ?line ct:fail({got_late_exit_message, Exit}) - after 1000 -> - ?line ok - end, - ?line process_flag(trap_exit, TE), - ?line process_flag(priority, Prio), - ?line ok. + receive + {'EXIT', TP1, _} -> + ok + after 0 -> + ok + end, + receive + {'EXIT', TP1, _} = Exit -> + ct:fail({got_late_exit_message, Exit}) + after 1000 -> + ok + end, + process_flag(trap_exit, TE), + process_flag(priority, Prio), + ok. otp_5772_monitor(Config) when is_list(Config) -> - ?line otp_5772_monitor_test(node()). + otp_5772_monitor_test(node()). otp_5772_dist_monitor(Config) when is_list(Config) -> - ?line [An] = get_names(1, otp_5772_dist_monitor), - ?line {ok, A} = start_node(An), - ?line otp_5772_monitor_test(A), - ?line stop_node(A), - ?line ok. + [An] = get_names(1, otp_5772_dist_monitor), + {ok, A} = start_node(An), + otp_5772_monitor_test(A), + stop_node(A), + ok. otp_5772_monitor_test(Node) -> - ?line Prio = process_flag(priority, high), - ?line TP1 = spawn_opt(Node, ?MODULE, test_proc, [], [{priority, low}]), - ?line M1 = erlang:monitor(process, TP1), - ?line exit(TP1, bang), - ?line erlang:demonitor(M1), - ?line receive - {'DOWN', M1, _, _, _} -> - ?line ok - after 0 -> - ?line ok - end, - ?line receive - {'DOWN', M1, _, _, _} = Down -> - ?line ct:fail({got_late_down_message, Down}) - after 1000 -> - ?line ok - end, - ?line process_flag(priority, Prio), - ?line ok. + Prio = process_flag(priority, high), + TP1 = spawn_opt(Node, ?MODULE, test_proc, [], [{priority, low}]), + M1 = erlang:monitor(process, TP1), + exit(TP1, bang), + erlang:demonitor(M1), + receive + {'DOWN', M1, _, _, _} -> + ok + after 0 -> + ok + end, + receive + {'DOWN', M1, _, _, _} = Down -> + ct:fail({got_late_down_message, Down}) + after 1000 -> + ok + end, + process_flag(priority, Prio), + ok. otp_7946(Config) when is_list(Config) -> - ?line [NodeName] = get_names(1, otp_7946), - ?line {ok, Node} = start_node(NodeName), - ?line Proc = rpc:call(Node, erlang, whereis, [net_kernel]), - ?line Mon = erlang:monitor(process, Proc), - ?line rpc:cast(Node, erlang, halt, []), - ?line receive {'DOWN', Mon, process, Proc , _} -> ok end, - ?line {Linker, LMon} = spawn_monitor(fun () -> - link(Proc), - receive - after infinity -> ok - end - end), - ?line receive - {'DOWN', LMon, process, Linker, Reason} -> - ?line io:format("Reason=~p~n", [Reason]), - ?line Reason = noconnection - end. + [NodeName] = get_names(1, otp_7946), + {ok, Node} = start_node(NodeName), + Proc = rpc:call(Node, erlang, whereis, [net_kernel]), + Mon = erlang:monitor(process, Proc), + rpc:cast(Node, erlang, halt, []), + receive {'DOWN', Mon, process, Proc , _} -> ok end, + {Linker, LMon} = spawn_monitor(fun () -> + link(Proc), + receive + after infinity -> ok + end + end), + receive + {'DOWN', LMon, process, Linker, Reason} -> + io:format("Reason=~p~n", [Reason]), + Reason = noconnection + end. %% %% -- Internal utils -------------------------------------------------------- @@ -504,27 +502,27 @@ otp_7946(Config) when is_list(Config) -> busy_data() -> case get(?BUSY_DATA_KEY) of - undefined -> - set_busy_data([]); - Data -> - true = is_binary(Data), - true = size(Data) == ?BUSY_DATA_SIZE, - Data + undefined -> + set_busy_data([]); + Data -> + true = is_binary(Data), + true = size(Data) == ?BUSY_DATA_SIZE, + Data end. set_busy_data(SetData) -> case get(?BUSY_DATA_KEY) of - undefined -> - Data = case SetData of - D when is_binary(D), size(D) == ?BUSY_DATA_SIZE -> - SetData; - _ -> - list_to_binary(lists:duplicate(?BUSY_DATA_SIZE, 253)) - end, - put(?BUSY_DATA_KEY, Data), - Data; - OldData -> - OldData + undefined -> + Data = case SetData of + D when is_binary(D), size(D) == ?BUSY_DATA_SIZE -> + SetData; + _ -> + list_to_binary(lists:duplicate(?BUSY_DATA_SIZE, 253)) + end, + put(?BUSY_DATA_KEY, Data), + Data; + OldData -> + OldData end. freeze_node(Node, MS) -> @@ -532,13 +530,13 @@ freeze_node(Node, MS) -> DoingIt = make_ref(), Freezer = self(), spawn_link(Node, - fun () -> - erts_debug:set_internal_state(available_internal_state, - true), - dport_send(Freezer, DoingIt), - receive after Own -> ok end, - erts_debug:set_internal_state(block, MS+Own) - end), + fun () -> + erts_debug:set_internal_state(available_internal_state, + true), + dport_send(Freezer, DoingIt), + receive after Own -> ok end, + erts_debug:set_internal_state(block, MS+Own) + end), receive DoingIt -> ok end, receive after Own -> ok end. @@ -548,27 +546,27 @@ make_busy(Node, Time) when is_integer(Time) -> Data = busy_data(), %% first make port busy Pid = spawn_link(fun () -> - forever(fun () -> - dport_reg_send(Node, - '__noone__', - Data) - end) - end), + forever(fun () -> + dport_reg_send(Node, + '__noone__', + Data) + end) + end), receive after Own -> ok end, wait_until(fun () -> - case process_info(Pid, status) of - {status, suspended} -> true; - _ -> false - end - end), + case process_info(Pid, status) of + {status, suspended} -> true; + _ -> false + end + end), %% then dist entry make_busy(Node, [nosuspend], Data), Pid. make_busy(Node, Opts, Data) -> case erlang:send({'__noone__', Node}, Data, Opts) of - nosuspend -> nosuspend; - _ -> make_busy(Node, Opts, Data) + nosuspend -> nosuspend; + _ -> make_busy(Node, Opts, Data) end. unmake_busy(Pid) -> @@ -581,33 +579,33 @@ suspend_on_busy_test(Node, Doing, Fun) -> Done = make_ref(), Data = busy_data(), spawn_link(fun () -> - set_busy_data(Data), - Busy = make_busy(Node, 1000), - Tester ! DoIt, - receive after 100 -> ok end, - Info = process_info(Tester, [status, current_function]), - unmake_busy(Busy), - io:format("~p doing ~s: ~p~n", [Tester, Doing, Info]), - Tester ! {Done, Info} - end), + set_busy_data(Data), + Busy = make_busy(Node, 1000), + Tester ! DoIt, + receive after 100 -> ok end, + Info = process_info(Tester, [status, current_function]), + unmake_busy(Busy), + io:format("~p doing ~s: ~p~n", [Tester, Doing, Info]), + Tester ! {Done, Info} + end), receive DoIt -> ok end, Res = Fun(), receive - {Done, MyInfo} -> - %% Don't match arity; it is different in - %% debug and optimized emulator - [{status, suspended}, - {current_function, {erlang, bif_return_trap, _}}] = MyInfo, - ok + {Done, MyInfo} -> + %% Don't match arity; it is different in + %% debug and optimized emulator + [{status, suspended}, + {current_function, {erlang, bif_return_trap, _}}] = MyInfo, + ok end, Res. % get_node(Name) when is_atom(Name) -> -% ?line node(); +% node(); % get_node({Name, Node}) when is_atom(Name) -> -% ?line Node; +% Node; % get_node(NC) when is_pid(NC); is_port(NC); is_reference(NC) -> -% ?line node(NC). +% node(NC). get_down_object(Item, _) when is_pid(Item) -> Item; @@ -622,78 +620,78 @@ get_down_object(Item, Watcher) when is_atom(Item), is_atom(Watcher) -> is_proc_dead(P) -> case is_proc_alive(P) of - true -> false; - false -> true + true -> false; + false -> true end. is_proc_alive(Pid) when is_pid(Pid), node(Pid) == node() -> - ?line is_process_alive(Pid); + is_process_alive(Pid); is_proc_alive(Name) when is_atom(Name) -> - ?line case catch whereis(Name) of - Pid when is_pid(Pid) -> - ?line is_proc_alive(Pid); - _ -> - ?line false - end; + case catch whereis(Name) of + Pid when is_pid(Pid) -> + is_proc_alive(Pid); + _ -> + false + end; is_proc_alive({Name, Node}) when is_atom(Name), Node == node() -> - ?line is_proc_alive(Name); + is_proc_alive(Name); is_proc_alive(Proc) -> - ?line is_remote_proc_alive(Proc). + is_remote_proc_alive(Proc). is_remote_proc_alive({Name, Node}) when is_atom(Name), is_atom(Node) -> - ?line is_remote_proc_alive(Name, Node); + is_remote_proc_alive(Name, Node); is_remote_proc_alive(Pid) when is_pid(Pid) -> - ?line is_remote_proc_alive(Pid, node(Pid)); + is_remote_proc_alive(Pid, node(Pid)); is_remote_proc_alive(_) -> - ?line false. + false. is_remote_proc_alive(PN, Node) -> - ?line S = self(), - ?line R = make_ref(), - ?line monitor_node(Node, true), - ?line _P = spawn(Node, fun () -> S ! {R, is_proc_alive(PN)} end), - ?line receive - {R, Bool} -> - ?line monitor_node(Node, false), - ?line Bool; - {nodedown, Node} -> - ?line false - end. + S = self(), + R = make_ref(), + monitor_node(Node, true), + _P = spawn(Node, fun () -> S ! {R, is_proc_alive(PN)} end), + receive + {R, Bool} -> + monitor_node(Node, false), + Bool; + {nodedown, Node} -> + false + end. wait_until(Fun) -> - ?line case Fun() of - true -> - ?line ok; - _ -> - ?line receive - after 100 -> - ?line wait_until(Fun) - end - end. + case Fun() of + true -> + ok; + _ -> + receive + after 100 -> + wait_until(Fun) + end + end. forever(Fun) -> Fun(), forever(Fun). tp_call(Tp, Fun) -> - ?line R = make_ref(), - ?line Tp ! {call, self(), R, Fun}, - ?line receive - {R, Res} -> - ?line Res - end. + R = make_ref(), + Tp ! {call, self(), R, Fun}, + receive + {R, Res} -> + Res + end. tp_cast(Tp, Fun) -> - ?line Tp ! {cast, Fun}. + Tp ! {cast, Fun}. test_proc() -> - ?line receive - {call, From, Ref, Fun} -> - ?line From ! {Ref, Fun()}; - {cast, Fun} -> - ?line Fun() - end, - ?line test_proc(). + receive + {call, From, Ref, Fun} -> + From ! {Ref, Fun()}; + {cast, Fun} -> + Fun() + end, + test_proc(). expand_link_list([#erl_link{type = ?LINK_NODE, targets = N} = Rec | T]) -> lists:duplicate(N,Rec#erl_link{targets = []}) ++ expand_link_list(T); @@ -701,7 +699,7 @@ expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}]} = Rec | T]) -> [Rec#erl_link{targets = [Pid]} | expand_link_list(T)]; expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}|TT]} = Rec | T]) -> [ Rec#erl_link{targets = [Pid]} | expand_link_list( - [Rec#erl_link{targets = TT} | T])]; + [Rec#erl_link{targets = TT} | T])]; expand_link_list([#erl_link{targets = []} = Rec | T]) -> [Rec | expand_link_list(T)]; expand_link_list([]) -> @@ -709,19 +707,19 @@ expand_link_list([]) -> get_local_link_list(Obj) -> case catch erts_debug:get_internal_state({link_list, Obj}) of - LL when is_list(LL) -> - expand_link_list(LL); - _ -> - [] + LL when is_list(LL) -> + expand_link_list(LL); + _ -> + [] end. get_remote_link_list(Node, Obj) -> case catch rpc:call(Node, erts_debug, get_internal_state, - [{link_list, Obj}]) of - LL when is_list(LL) -> - expand_link_list(LL); - _ -> - [] + [{link_list, Obj}]) of + LL when is_list(LL) -> + expand_link_list(LL); + _ -> + [] end. @@ -731,30 +729,30 @@ get_link_list({Node, DistEntry}) when is_atom(Node), is_atom(DistEntry) -> get_remote_link_list(Node, DistEntry); get_link_list(P) when is_pid(P); is_port(P) -> case node(P) of - Node when Node == node() -> - get_local_link_list(P); - Node -> - get_remote_link_list(Node, P) - end; + Node when Node == node() -> + get_local_link_list(P); + Node -> + get_remote_link_list(Node, P) + end; get_link_list(undefined) -> []. get_local_monitor_list(Obj) -> case catch erts_debug:get_internal_state({monitor_list, Obj}) of - LL when is_list(LL) -> - LL; - _ -> - [] - end. + LL when is_list(LL) -> + LL; + _ -> + [] + end. get_remote_monitor_list(Node, Obj) -> case catch rpc:call(Node, erts_debug, get_internal_state, - [{monitor_list, Obj}]) of - LL when is_list(LL) -> - LL; - _ -> - [] - end. + [{monitor_list, Obj}]) of + LL when is_list(LL) -> + LL; + _ -> + [] + end. get_monitor_list({Node, DistEntry}) when Node == node(), is_atom(DistEntry) -> @@ -763,242 +761,242 @@ get_monitor_list({Node, DistEntry}) when is_atom(Node), is_atom(DistEntry) -> get_remote_monitor_list(Node, DistEntry); get_monitor_list(P) when is_pid(P) -> case node(P) of - Node when Node == node() -> - get_local_monitor_list(P); - Node -> - get_remote_monitor_list(Node, P) - end; + Node when Node == node() -> + get_local_monitor_list(P); + Node -> + get_remote_monitor_list(Node, P) + end; get_monitor_list(undefined) -> []. find_erl_monitor(Pid, Ref) when is_reference(Ref) -> lists:foldl(fun (#erl_monitor{ref = R} = EL, Acc) when R == Ref -> - [EL|Acc]; - (_, Acc) -> - Acc - end, - [], - get_monitor_list(Pid)). + [EL|Acc]; + (_, Acc) -> + Acc + end, + [], + get_monitor_list(Pid)). % find_erl_link(Obj, Ref) when is_reference(Ref) -> -% ?line lists:foldl(fun (#erl_link{ref = R} = EL, Acc) when R == Ref -> -% ?line [EL|Acc]; +% lists:foldl(fun (#erl_link{ref = R} = EL, Acc) when R == Ref -> +% [EL|Acc]; % (_, Acc) -> -% ?line Acc +% Acc % end, % [], % get_link_list(Obj)). find_erl_link(Obj, Type, [Item, Data]) when is_pid(Item); - is_port(Item); - is_atom(Item) -> + is_port(Item); + is_atom(Item) -> lists:foldl(fun (#erl_link{type = T, pid = I, targets = D} = EL, - Acc) when T == Type, I == Item -> - case Data of - D -> - [EL|Acc]; - [] -> - [EL|Acc]; - _ -> - Acc - end; - (_, Acc) -> - Acc - end, - [], - get_link_list(Obj)); + Acc) when T == Type, I == Item -> + case Data of + D -> + [EL|Acc]; + [] -> + [EL|Acc]; + _ -> + Acc + end; + (_, Acc) -> + Acc + end, + [], + get_link_list(Obj)); find_erl_link(Obj, Type, Item) when is_pid(Item); is_port(Item); is_atom(Item) -> find_erl_link(Obj, Type, [Item, []]). - + check_link(A, B) -> - ?line [#erl_link{type = ?LINK_PID, - pid = B, - targets = []}] = find_erl_link(A, ?LINK_PID, B), - ?line [#erl_link{type = ?LINK_PID, - pid = A, - targets = []}] = find_erl_link(B, ?LINK_PID, A), - ?line case node(A) == node(B) of - false -> - ?line [#erl_link{type = ?LINK_PID, - pid = A, - targets = [B]}] = find_erl_link({node(A), - node(B)}, - ?LINK_PID, - [A, [B]]), - ?line [#erl_link{type = ?LINK_PID, - pid = B, - targets = [A]}] = find_erl_link({node(B), - node(A)}, - ?LINK_PID, - [B, [A]]); - true -> - ?line [] = find_erl_link({node(A), node(B)}, - ?LINK_PID, - [A, [B]]), - ?line [] = find_erl_link({node(B), node(A)}, - ?LINK_PID, - [B, [A]]) - end, - ?line ok. + [#erl_link{type = ?LINK_PID, + pid = B, + targets = []}] = find_erl_link(A, ?LINK_PID, B), + [#erl_link{type = ?LINK_PID, + pid = A, + targets = []}] = find_erl_link(B, ?LINK_PID, A), + case node(A) == node(B) of + false -> + [#erl_link{type = ?LINK_PID, + pid = A, + targets = [B]}] = find_erl_link({node(A), + node(B)}, + ?LINK_PID, + [A, [B]]), + [#erl_link{type = ?LINK_PID, + pid = B, + targets = [A]}] = find_erl_link({node(B), + node(A)}, + ?LINK_PID, + [B, [A]]); + true -> + [] = find_erl_link({node(A), node(B)}, + ?LINK_PID, + [A, [B]]), + [] = find_erl_link({node(B), node(A)}, + ?LINK_PID, + [B, [A]]) + end, + ok. check_unlink(A, B) -> - ?line [] = find_erl_link(A, ?LINK_PID, B), - ?line [] = find_erl_link(B, ?LINK_PID, A), - ?line [] = find_erl_link({node(A), node(B)}, ?LINK_PID, [A, [B]]), - ?line [] = find_erl_link({node(B), node(A)}, ?LINK_PID, [B, [A]]), - ?line ok. + [] = find_erl_link(A, ?LINK_PID, B), + [] = find_erl_link(B, ?LINK_PID, A), + [] = find_erl_link({node(A), node(B)}, ?LINK_PID, [A, [B]]), + [] = find_erl_link({node(B), node(A)}, ?LINK_PID, [B, [A]]), + ok. check_process_monitor(From, {Name, Node}, Ref) when is_pid(From), - is_atom(Name), - Node == node(From), - is_reference(Ref) -> - ?line check_process_monitor(From, Name, Ref); + is_atom(Name), + Node == node(From), + is_reference(Ref) -> + check_process_monitor(From, Name, Ref); check_process_monitor(From, {Name, Node}, Ref) when is_pid(From), - is_atom(Name), - is_atom(Node), - is_reference(Ref) -> - ?line MonitoredPid = rpc:call(Node, erlang, whereis, [Name]), - ?line [#erl_monitor{type = ?MON_ORIGIN, - ref = Ref, - pid = Node, - name = Name}] = find_erl_monitor(From, Ref), - ?line [#erl_monitor{type = ?MON_TARGET, - ref = Ref, - pid = From, - name = Name}] = find_erl_monitor({node(From), Node}, Ref), - ?line [#erl_monitor{type = ?MON_ORIGIN, - ref = Ref, - pid = MonitoredPid, - name = Name}] = find_erl_monitor({Node, node(From)}, Ref), - ?line [#erl_monitor{type = ?MON_TARGET, - ref = Ref, - pid = From, - name = Name}] = find_erl_monitor(MonitoredPid, Ref), - ?line ok; + is_atom(Name), + is_atom(Node), + is_reference(Ref) -> + MonitoredPid = rpc:call(Node, erlang, whereis, [Name]), + [#erl_monitor{type = ?MON_ORIGIN, + ref = Ref, + pid = Node, + name = Name}] = find_erl_monitor(From, Ref), + [#erl_monitor{type = ?MON_TARGET, + ref = Ref, + pid = From, + name = Name}] = find_erl_monitor({node(From), Node}, Ref), + [#erl_monitor{type = ?MON_ORIGIN, + ref = Ref, + pid = MonitoredPid, + name = Name}] = find_erl_monitor({Node, node(From)}, Ref), + [#erl_monitor{type = ?MON_TARGET, + ref = Ref, + pid = From, + name = Name}] = find_erl_monitor(MonitoredPid, Ref), + ok; check_process_monitor(From, Name, Ref) when is_pid(From), - is_atom(Name), - undefined /= Name, - is_reference(Ref) -> - ?line MonitoredPid = rpc:call(node(From), erlang, whereis, [Name]), - - ?line [#erl_monitor{type = ?MON_ORIGIN, - ref = Ref, - pid = MonitoredPid, - name = Name}] = find_erl_monitor(From, Ref), - - - ?line [#erl_monitor{type = ?MON_TARGET, - ref = Ref, - pid = From, - name = Name}] = find_erl_monitor(MonitoredPid,Ref), + is_atom(Name), + undefined /= Name, + is_reference(Ref) -> + MonitoredPid = rpc:call(node(From), erlang, whereis, [Name]), + + [#erl_monitor{type = ?MON_ORIGIN, + ref = Ref, + pid = MonitoredPid, + name = Name}] = find_erl_monitor(From, Ref), + + + [#erl_monitor{type = ?MON_TARGET, + ref = Ref, + pid = From, + name = Name}] = find_erl_monitor(MonitoredPid,Ref), ok; check_process_monitor(From, To, Ref) when is_pid(From), - is_pid(To), - is_reference(Ref) -> - ?line OriMon = [#erl_monitor{type = ?MON_ORIGIN, - ref = Ref, - pid = To}], - - ?line OriMon = find_erl_monitor(From, Ref), - - ?line TargMon = [#erl_monitor{type = ?MON_TARGET, - ref = Ref, - pid = From}], - ?line TargMon = find_erl_monitor(To, Ref), - - - ?line case node(From) == node(To) of - false -> - ?line TargMon = find_erl_monitor({node(From), node(To)}, Ref), - ?line OriMon = find_erl_monitor({node(To), node(From)}, Ref); - true -> - ?line [] = find_erl_monitor({node(From), node(From)}, Ref) - end, - ?line ok. + is_pid(To), + is_reference(Ref) -> + OriMon = [#erl_monitor{type = ?MON_ORIGIN, + ref = Ref, + pid = To}], + + OriMon = find_erl_monitor(From, Ref), + + TargMon = [#erl_monitor{type = ?MON_TARGET, + ref = Ref, + pid = From}], + TargMon = find_erl_monitor(To, Ref), + + + case node(From) == node(To) of + false -> + TargMon = find_erl_monitor({node(From), node(To)}, Ref), + OriMon = find_erl_monitor({node(To), node(From)}, Ref); + true -> + [] = find_erl_monitor({node(From), node(From)}, Ref) + end, + ok. check_process_demonitor(From, {undefined, Node}, Ref) when is_pid(From), - is_reference(Ref) -> - ?line [] = find_erl_monitor(From, Ref), - ?line case node(From) == Node of - false -> - ?line [] = find_erl_monitor({node(From), Node}, Ref), - ?line [] = find_erl_monitor({Node, node(From)}, Ref); - true -> - ?line [] = find_erl_monitor({Node, Node}, Ref) - end, - ?line ok; + is_reference(Ref) -> + [] = find_erl_monitor(From, Ref), + case node(From) == Node of + false -> + [] = find_erl_monitor({node(From), Node}, Ref), + [] = find_erl_monitor({Node, node(From)}, Ref); + true -> + [] = find_erl_monitor({Node, Node}, Ref) + end, + ok; check_process_demonitor(From, {Name, Node}, Ref) when is_pid(From), - is_atom(Name), - Node == node(From), - is_reference(Ref) -> - ?line MonitoredPid = rpc:call(Node, erlang, whereis, [Name]), - ?line case rpc:call(Node, erlang, whereis, [Name]) of - undefined -> - ?line check_process_demonitor(From, {undefined, Node}, Ref); - MonitoredPid -> - ?line check_process_demonitor(From, MonitoredPid, Ref) - end; + is_atom(Name), + Node == node(From), + is_reference(Ref) -> + MonitoredPid = rpc:call(Node, erlang, whereis, [Name]), + case rpc:call(Node, erlang, whereis, [Name]) of + undefined -> + check_process_demonitor(From, {undefined, Node}, Ref); + MonitoredPid -> + check_process_demonitor(From, MonitoredPid, Ref) + end; check_process_demonitor(From, {Name, Node}, Ref) when is_pid(From), - is_atom(Name), - is_atom(Node), - is_reference(Ref) -> - ?line MonitoredPid = rpc:call(Node, erlang, whereis, [Name]), - ?line [] = find_erl_monitor(From, Ref), - ?line [] = find_erl_monitor({node(From), Node}, Ref), - ?line [] = find_erl_monitor({Node, node(From)}, Ref), - ?line [] = find_erl_monitor(MonitoredPid, Ref), - ?line ok; + is_atom(Name), + is_atom(Node), + is_reference(Ref) -> + MonitoredPid = rpc:call(Node, erlang, whereis, [Name]), + [] = find_erl_monitor(From, Ref), + [] = find_erl_monitor({node(From), Node}, Ref), + [] = find_erl_monitor({Node, node(From)}, Ref), + [] = find_erl_monitor(MonitoredPid, Ref), + ok; check_process_demonitor(From, undefined, Ref) when is_pid(From), - is_reference(Ref) -> - ?line [] = find_erl_monitor(From, Ref), - ?line case node(From) == node() of - false -> - ?line [] = find_erl_monitor({node(From), node()}, Ref), - ?line [] = find_erl_monitor({node(), node(From)}, Ref); - true -> - ?line [] = find_erl_monitor({node(), node()}, Ref) - end, - ?line ok; + is_reference(Ref) -> + [] = find_erl_monitor(From, Ref), + case node(From) == node() of + false -> + [] = find_erl_monitor({node(From), node()}, Ref), + [] = find_erl_monitor({node(), node(From)}, Ref); + true -> + [] = find_erl_monitor({node(), node()}, Ref) + end, + ok; check_process_demonitor(From, Name, Ref) when is_pid(From), - is_atom(Name), - undefined /= Name, - is_reference(Ref) -> - ?line check_process_demonitor(From, {Name, node()}, Ref); + is_atom(Name), + undefined /= Name, + is_reference(Ref) -> + check_process_demonitor(From, {Name, node()}, Ref); check_process_demonitor(From, To, Ref) when is_pid(From), - is_pid(To), - is_reference(Ref) -> - ?line [] = find_erl_monitor(From, Ref), - ?line [] = find_erl_monitor(To, Ref), - ?line case node(From) == node(To) of - false -> - ?line [] = find_erl_monitor({node(From), node(To)}, Ref), - ?line [] = find_erl_monitor({node(To), node(From)}, Ref); - true -> - ?line [] = find_erl_monitor({node(From), node(From)}, Ref) - end, - ?line ok. + is_pid(To), + is_reference(Ref) -> + [] = find_erl_monitor(From, Ref), + [] = find_erl_monitor(To, Ref), + case node(From) == node(To) of + false -> + [] = find_erl_monitor({node(From), node(To)}, Ref), + [] = find_erl_monitor({node(To), node(From)}, Ref); + true -> + [] = find_erl_monitor({node(From), node(From)}, Ref) + end, + ok. no_of_monitor_node(From, Node) when is_pid(From), is_atom(Node) -> - ?line length(find_erl_link(From, ?LINK_NODE, Node)). + length(find_erl_link(From, ?LINK_NODE, Node)). check_monitor_node(From, Node, No) when is_pid(From), - is_atom(Node), - is_integer(No), - No >= 0 -> - ?line LL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = Node}), - ?line DLL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = From}), - ?line LL = find_erl_link(From, ?LINK_NODE, Node), - ?line DLL = find_erl_link({node(From), Node}, ?LINK_NODE, From), - ?line ok. + is_atom(Node), + is_integer(No), + No >= 0 -> + LL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = Node}), + DLL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = From}), + LL = find_erl_link(From, ?LINK_NODE, Node), + DLL = find_erl_link({node(From), Node}, ?LINK_NODE, From), + ok. hostname() -> - ?line from($@, atom_to_list(node())). + from($@, atom_to_list(node())). from(H, [H | T]) -> T; from(H, [_ | T]) -> from(H, T); @@ -1010,27 +1008,27 @@ get_names(0, _, Acc) -> Acc; get_names(N, T, Acc) -> get_names(N-1, T, [list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ atom_to_list(T) - ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) - ++ "-" - ++ integer_to_list(erlang:unique_integer([positive]))) | Acc]). + ++ "-" + ++ atom_to_list(T) + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive]))) | Acc]). start_node(Name) -> - ?line start_node(Name, ""). + start_node(Name, ""). start_node(Name, Args) -> - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line Res = test_server:start_node(Name, slave, [{args, Args ++ " -pa " ++ Pa}]), - ?line {ok, Node} = Res, - ?line rpc:call(Node, erts_debug, set_internal_state, - [available_internal_state, true]), - ?line Res. - + Pa = filename:dirname(code:which(?MODULE)), + Res = test_server:start_node(Name, slave, [{args, Args ++ " -pa " ++ Pa}]), + {ok, Node} = Res, + rpc:call(Node, erts_debug, set_internal_state, + [available_internal_state, true]), + Res. + stop_node(Node) -> - ?line test_server:stop_node(Node). + test_server:stop_node(Node). -define(COOKIE, ''). -define(DOP_LINK, 1). @@ -1053,37 +1051,37 @@ stop_node(Node) -> dport_send(To, Msg) -> Node = node(To), DPrt = case dport(Node) of - undefined -> - pong = net_adm:ping(Node), - dport(Node); - Prt -> - Prt - end, + undefined -> + pong = net_adm:ping(Node), + dport(Node); + Prt -> + Prt + end, port_command(DPrt, [dmsg_hdr(), - dmsg_ext({?DOP_SEND, - ?COOKIE, - To}), - dmsg_ext(Msg)]). + dmsg_ext({?DOP_SEND, + ?COOKIE, + To}), + dmsg_ext(Msg)]). dport_reg_send(Node, Name, Msg) -> DPrt = case dport(Node) of - undefined -> - pong = net_adm:ping(Node), - dport(Node); - Prt -> - Prt - end, + undefined -> + pong = net_adm:ping(Node), + dport(Node); + Prt -> + Prt + end, port_command(DPrt, [dmsg_hdr(), - dmsg_ext({?DOP_REG_SEND, - self(), - ?COOKIE, - Name}), - dmsg_ext(Msg)]). + dmsg_ext({?DOP_REG_SEND, + self(), + ?COOKIE, + Name}), + dmsg_ext(Msg)]). dport(Node) when is_atom(Node) -> case catch erts_debug:get_internal_state(available_internal_state) of - true -> true; - _ -> erts_debug:set_internal_state(available_internal_state, true) + true -> true; + _ -> erts_debug:set_internal_state(available_internal_state, true) end, erts_debug:get_internal_state({dist_port, Node}). @@ -1109,11 +1107,7 @@ stop_busy_dist_port_tracer(_) -> busy_dist_port_tracer() -> receive - {monitor, _SuspendedProcess, busy_dist_port, _Port} = M -> - erlang:display(M), - busy_dist_port_tracer() + {monitor, _SuspendedProcess, busy_dist_port, _Port} = M -> + erlang:display(M), + busy_dist_port_tracer() end. - - - - diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index 56b6248b10..93728b299b 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -75,13 +75,12 @@ groups() -> %% EStone Test estone(Config) when is_list(Config) -> - ?line DataDir = proplists:get_value(data_dir,Config), - ?line Mhz=get_cpu_speed(os:type(),DataDir), - ?line L = ?MODULE:macro(?MODULE:micros(),DataDir), - ?line {Total, Stones} = sum_micros(L, 0, 0), - ?line pp(Mhz,Total,Stones,L), - ?line {comment,Mhz ++ " MHz, " ++ - integer_to_list(Stones) ++ " ESTONES"}. + DataDir = proplists:get_value(data_dir,Config), + Mhz=get_cpu_speed(os:type(),DataDir), + L = ?MODULE:macro(?MODULE:micros(),DataDir), + {Total, Stones} = sum_micros(L, 0, 0), + pp(Mhz,Total,Stones,L), + {comment,Mhz ++ " MHz, " ++ integer_to_list(Stones) ++ " ESTONES"}. estone_bench(Config) -> DataDir = proplists:get_value(data_dir,Config), @@ -1109,4 +1108,3 @@ wait_for_pids([P|Tail]) -> send_procs([P|Tail], Msg) -> P ! Msg, send_procs(Tail, Msg); send_procs([], _) -> ok. - diff --git a/erts/emulator/test/evil_SUITE.erl b/erts/emulator/test/evil_SUITE.erl index 02175e6bed..5f5a0ef305 100644 --- a/erts/emulator/test/evil_SUITE.erl +++ b/erts/emulator/test/evil_SUITE.erl @@ -20,16 +20,15 @@ -module(evil_SUITE). -export([all/0, suite/0, - heap_frag/1, - encode_decode_ext/1, - decode_integer_ext/1, - decode_small_big_ext/1, - decode_large_big_ext/1, - decode_small_big_ext_neg/1, - decode_large_big_ext_neg/1, - decode_too_small/1, - decode_pos_neg_zero/1 - ]). + heap_frag/1, + encode_decode_ext/1, + decode_integer_ext/1, + decode_small_big_ext/1, + decode_large_big_ext/1, + decode_small_big_ext_neg/1, + decode_large_big_ext_neg/1, + decode_too_small/1, + decode_pos_neg_zero/1]). -include_lib("common_test/include/ct.hrl"). @@ -46,12 +45,12 @@ all() -> heap_frag(Config) when is_list(Config) -> N = 512, Self = self(), - ?line Pid = spawn_link(fun() -> appender(Self, N) end), + Pid = spawn_link(fun() -> appender(Self, N) end), receive - {Pid,Res} -> - ?line Res = my_appender(N); - Garbage -> - io:format("Garbage: ~p\n", [Garbage]), + {Pid,Res} -> + Res = my_appender(N); + Garbage -> + io:format("Garbage: ~p\n", [Garbage]), ct:fail(got_garbage) end. @@ -62,28 +61,28 @@ heap_frag(Config) when is_list(Config) -> %% These test cases are not "evil" but the next test case is.... encode_decode_ext(Config) when is_list(Config) -> - ?line enc_dec( 2, 0), % SMALL_INTEGER_EXT smallest - ?line enc_dec( 2, 255), % SMALL_INTEGER_EXT largest - ?line enc_dec( 5, 256), % INTEGER_EXT smallest pos (*) - ?line enc_dec( 5, -1), % INTEGER_EXT largest neg - - ?line enc_dec( 5, 16#07ffffff), % INTEGER_EXT largest (28 bits) - ?line enc_dec( 5,-16#08000000), % INTEGER_EXT smallest - ?line enc_dec( 7, 16#08000000), % SMALL_BIG_EXT smallest pos(*) - ?line enc_dec( 7,-16#08000001), % SMALL_BIG_EXT largest neg (*) - - ?line enc_dec( 7, 16#7fffffff), % SMALL_BIG_EXT largest i32 - ?line enc_dec( 7,-16#80000000), % SMALL_BIG_EXT smallest i32 - - ?line enc_dec( 7, 16#80000000), % SMALL_BIG_EXT u32 - ?line enc_dec( 7, 16#ffffffff), % SMALL_BIG_EXT largest u32 - - ?line enc_dec( 9, 16#7fffffffffff), % largest i48 - ?line enc_dec( 9,-16#800000000000), % smallest i48 - ?line enc_dec( 9, 16#ffffffffffff), % largest u48 - ?line enc_dec(11, 16#7fffffffffffffff), % largest i64 - ?line enc_dec(11,-16#8000000000000000), % smallest i64 - ?line enc_dec(11, 16#ffffffffffffffff), % largest u64 + enc_dec( 2, 0), % SMALL_INTEGER_EXT smallest + enc_dec( 2, 255), % SMALL_INTEGER_EXT largest + enc_dec( 5, 256), % INTEGER_EXT smallest pos (*) + enc_dec( 5, -1), % INTEGER_EXT largest neg + + enc_dec( 5, 16#07ffffff), % INTEGER_EXT largest (28 bits) + enc_dec( 5,-16#08000000), % INTEGER_EXT smallest + enc_dec( 7, 16#08000000), % SMALL_BIG_EXT smallest pos(*) + enc_dec( 7,-16#08000001), % SMALL_BIG_EXT largest neg (*) + + enc_dec( 7, 16#7fffffff), % SMALL_BIG_EXT largest i32 + enc_dec( 7,-16#80000000), % SMALL_BIG_EXT smallest i32 + + enc_dec( 7, 16#80000000), % SMALL_BIG_EXT u32 + enc_dec( 7, 16#ffffffff), % SMALL_BIG_EXT largest u32 + + enc_dec( 9, 16#7fffffffffff), % largest i48 + enc_dec( 9,-16#800000000000), % smallest i48 + enc_dec( 9, 16#ffffffffffff), % largest u48 + enc_dec(11, 16#7fffffffffffffff), % largest i64 + enc_dec(11,-16#8000000000000000), % smallest i64 + enc_dec(11, 16#ffffffffffffffff), % largest u64 ok. @@ -99,213 +98,213 @@ encode_decode_ext(Config) when is_list(Config) -> %% erl_interface, i.e. not how it is encoded in the test case below. decode_integer_ext(Config) when is_list(Config) -> - ?line decode( 0, <<131,98, 0:32>>), % SMALL_INTEGER_EXT - ?line decode( 42, <<131,98, 42:32>>), % SMALL_INTEGER_EXT - ?line decode(255, <<131,98,255:32>>), % SMALL_INTEGER_EXT - ?line decode( 16#08000000, <<131,98, 16#08000000:32>>), % SMALL_BIG_EXT - ?line decode(-16#08000001, <<131,98,-16#08000001:32>>), % SMALL_BIG_EXT - ?line decode( 16#7fffffff, <<131,98, 16#7fffffff:32>>), % SMALL_BIG_EXT - ?line decode(-16#80000000, <<131,98,-16#80000000:32>>), % SMALL_BIG_EXT + decode( 0, <<131,98, 0:32>>), % SMALL_INTEGER_EXT + decode( 42, <<131,98, 42:32>>), % SMALL_INTEGER_EXT + decode(255, <<131,98,255:32>>), % SMALL_INTEGER_EXT + decode( 16#08000000, <<131,98, 16#08000000:32>>), % SMALL_BIG_EXT + decode(-16#08000001, <<131,98,-16#08000001:32>>), % SMALL_BIG_EXT + decode( 16#7fffffff, <<131,98, 16#7fffffff:32>>), % SMALL_BIG_EXT + decode(-16#80000000, <<131,98,-16#80000000:32>>), % SMALL_BIG_EXT ok. decode_small_big_ext(Config) when is_list(Config) -> - ?line decode(256,<<131,110,2,0,0,1>>), % INTEGER_EXT - ?line decode(16#07ffffff,<<131,110,4,0,255,255,255,7>>), % INTEGER_EXT - ?line decode(16#7fffffff,<<131,110,4,0,255,255,255,127>>), % SMALL_BIG_EXT - - ?line decode(42,<<131,110,1,0,42>>), % SMALL_INTEGER_EXT - ?line decode(42,<<131,110,2,0,42,0>>), % Redundant zeros from now on - ?line decode(42,<<131,110,3,0,42,0,0>>), - ?line decode(42,<<131,110,4,0,42,0,0,0>>), - ?line decode(42,<<131,110,5,0,42,0,0,0,0>>), - ?line decode(42,<<131,110,6,0,42,0,0,0,0,0>>), - ?line decode(42,<<131,110,7,0,42,0,0,0,0,0,0>>), - ?line decode(42,<<131,110,8,0,42,0,0,0,0,0,0,0>>), + decode(256,<<131,110,2,0,0,1>>), % INTEGER_EXT + decode(16#07ffffff,<<131,110,4,0,255,255,255,7>>), % INTEGER_EXT + decode(16#7fffffff,<<131,110,4,0,255,255,255,127>>), % SMALL_BIG_EXT + + decode(42,<<131,110,1,0,42>>), % SMALL_INTEGER_EXT + decode(42,<<131,110,2,0,42,0>>), % Redundant zeros from now on + decode(42,<<131,110,3,0,42,0,0>>), + decode(42,<<131,110,4,0,42,0,0,0>>), + decode(42,<<131,110,5,0,42,0,0,0,0>>), + decode(42,<<131,110,6,0,42,0,0,0,0,0>>), + decode(42,<<131,110,7,0,42,0,0,0,0,0,0>>), + decode(42,<<131,110,8,0,42,0,0,0,0,0,0,0>>), ok. decode_large_big_ext(Config) when is_list(Config) -> - ?line decode(256,<<131,111,2:32,0,0,1>>), % INTEGER_EXT - ?line decode(16#07ffffff,<<131,111,4:32,0,255,255,255,7>>), % INTEG_EXT - ?line decode(16#7fffffff,<<131,111,4:32,0,255,255,255,127>>), % SMA_BIG - ?line decode(16#ffffffff,<<131,111,4:32,0,255,255,255,255>>), % SMA_BIG + decode(256,<<131,111,2:32,0,0,1>>), % INTEGER_EXT + decode(16#07ffffff,<<131,111,4:32,0,255,255,255,7>>), % INTEG_EXT + decode(16#7fffffff,<<131,111,4:32,0,255,255,255,127>>), % SMA_BIG + decode(16#ffffffff,<<131,111,4:32,0,255,255,255,255>>), % SMA_BIG N = largest_small_big(), - ?line decode(N,<<131,111,255:32,0,N:2040/little>>), % SMALL_BIG_EXT - - ?line decode(42,<<131,111,1:32,0,42>>), - ?line decode(42,<<131,111,2:32,0,42,0>>), % Redundant zeros from now on - ?line decode(42,<<131,111,3:32,0,42,0,0>>), - ?line decode(42,<<131,111,4:32,0,42,0,0,0>>), - ?line decode(42,<<131,111,5:32,0,42,0,0,0,0>>), - ?line decode(42,<<131,111,6:32,0,42,0,0,0,0,0>>), - ?line decode(42,<<131,111,7:32,0,42,0,0,0,0,0,0>>), - ?line decode(42,<<131,111,8:32,0,42,0,0,0,0,0,0,0>>), + decode(N,<<131,111,255:32,0,N:2040/little>>), % SMALL_BIG_EXT + + decode(42,<<131,111,1:32,0,42>>), + decode(42,<<131,111,2:32,0,42,0>>), % Redundant zeros from now on + decode(42,<<131,111,3:32,0,42,0,0>>), + decode(42,<<131,111,4:32,0,42,0,0,0>>), + decode(42,<<131,111,5:32,0,42,0,0,0,0>>), + decode(42,<<131,111,6:32,0,42,0,0,0,0,0>>), + decode(42,<<131,111,7:32,0,42,0,0,0,0,0,0>>), + decode(42,<<131,111,8:32,0,42,0,0,0,0,0,0,0>>), ok. decode_small_big_ext_neg(Config) when is_list(Config) -> - ?line decode(-1,<<131,110,1,1,1>>), % INTEGER_EXT - ?line decode(-16#08000000,<<131,110,4,1,0,0,0,8>>), % INTEGER_EXT - ?line decode(-16#80000000,<<131,110,4,1,0,0,0,128>>), % SMALL_BIG_EXT - ?line decode(-16#ffffffff,<<131,110,4,1,255,255,255,255>>), % SMALL_BIG_EXT + decode(-1,<<131,110,1,1,1>>), % INTEGER_EXT + decode(-16#08000000,<<131,110,4,1,0,0,0,8>>), % INTEGER_EXT + decode(-16#80000000,<<131,110,4,1,0,0,0,128>>), % SMALL_BIG_EXT + decode(-16#ffffffff,<<131,110,4,1,255,255,255,255>>), % SMALL_BIG_EXT N = largest_small_big(), - ?line decode(-N,<<131,111,255:32,1,N:2040/little>>), % SMALL_BIG_EXT - - ?line decode(-42,<<131,110,1,1,42>>), - ?line decode(-42,<<131,110,2,1,42,0>>), % Redundant zeros from now on - ?line decode(-42,<<131,110,3,1,42,0,0>>), - ?line decode(-42,<<131,110,4,1,42,0,0,0>>), - ?line decode(-42,<<131,110,5,1,42,0,0,0,0>>), - ?line decode(-42,<<131,110,6,1,42,0,0,0,0,0>>), - ?line decode(-42,<<131,110,7,1,42,0,0,0,0,0,0>>), - ?line decode(-42,<<131,110,8,1,42,0,0,0,0,0,0,0>>), + decode(-N,<<131,111,255:32,1,N:2040/little>>), % SMALL_BIG_EXT + + decode(-42,<<131,110,1,1,42>>), + decode(-42,<<131,110,2,1,42,0>>), % Redundant zeros from now on + decode(-42,<<131,110,3,1,42,0,0>>), + decode(-42,<<131,110,4,1,42,0,0,0>>), + decode(-42,<<131,110,5,1,42,0,0,0,0>>), + decode(-42,<<131,110,6,1,42,0,0,0,0,0>>), + decode(-42,<<131,110,7,1,42,0,0,0,0,0,0>>), + decode(-42,<<131,110,8,1,42,0,0,0,0,0,0,0>>), ok. decode_large_big_ext_neg(Config) when is_list(Config) -> - ?line decode(-1,<<131,111,1:32,1,1>>), % INTEGER_EXT - ?line decode(-16#08000000,<<131,111,4:32,1,0,0,0,8>>), % INTEGER_EXT - ?line decode(-16#80000000,<<131,111,4:32,1,0,0,0,128>>), % SMALL_BIG_EXT - - ?line decode(-42,<<131,111,1:32,1,42>>), - ?line decode(-42,<<131,111,2:32,1,42,0>>), % Redundant zeros from now on - ?line decode(-42,<<131,111,3:32,1,42,0,0>>), - ?line decode(-42,<<131,111,4:32,1,42,0,0,0>>), - ?line decode(-42,<<131,111,5:32,1,42,0,0,0,0>>), - ?line decode(-42,<<131,111,6:32,1,42,0,0,0,0,0>>), - ?line decode(-42,<<131,111,7:32,1,42,0,0,0,0,0,0>>), - ?line decode(-42,<<131,111,8:32,1,42,0,0,0,0,0,0,0>>), + decode(-1,<<131,111,1:32,1,1>>), % INTEGER_EXT + decode(-16#08000000,<<131,111,4:32,1,0,0,0,8>>), % INTEGER_EXT + decode(-16#80000000,<<131,111,4:32,1,0,0,0,128>>), % SMALL_BIG_EXT + + decode(-42,<<131,111,1:32,1,42>>), + decode(-42,<<131,111,2:32,1,42,0>>), % Redundant zeros from now on + decode(-42,<<131,111,3:32,1,42,0,0>>), + decode(-42,<<131,111,4:32,1,42,0,0,0>>), + decode(-42,<<131,111,5:32,1,42,0,0,0,0>>), + decode(-42,<<131,111,6:32,1,42,0,0,0,0,0>>), + decode(-42,<<131,111,7:32,1,42,0,0,0,0,0,0>>), + decode(-42,<<131,111,8:32,1,42,0,0,0,0,0,0,0>>), ok. decode_pos_neg_zero(Config) when is_list(Config) -> - ?line decode( 0, <<131,110,0,0>>), % SMALL_BIG_EXT (positive zero) - ?line decode( 0, <<131,110,1,0,0>>), % SMALL_BIG_EXT (positive zero) - ?line decode( 0, <<131,110,0,1>>), % SMALL_BIG_EXT (negative zero) - ?line decode( 0, <<131,110,1,1,0>>), % SMALL_BIG_EXT (negative zero) + decode( 0, <<131,110,0,0>>), % SMALL_BIG_EXT (positive zero) + decode( 0, <<131,110,1,0,0>>), % SMALL_BIG_EXT (positive zero) + decode( 0, <<131,110,0,1>>), % SMALL_BIG_EXT (negative zero) + decode( 0, <<131,110,1,1,0>>), % SMALL_BIG_EXT (negative zero) - ?line decode( 0, <<131,111,0:32,0>>), % SMALL_BIG_EXT (positive zero) - ?line decode( 0, <<131,111,1:32,0,0>>), % SMALL_BIG_EXT (positive zero) - ?line decode( 0, <<131,111,0:32,1>>), % SMALL_BIG_EXT (negative zero) - ?line decode( 0, <<131,111,1:32,1,0>>), % SMALL_BIG_EXT (negative zero) + decode( 0, <<131,111,0:32,0>>), % SMALL_BIG_EXT (positive zero) + decode( 0, <<131,111,1:32,0,0>>), % SMALL_BIG_EXT (positive zero) + decode( 0, <<131,111,0:32,1>>), % SMALL_BIG_EXT (negative zero) + decode( 0, <<131,111,1:32,1,0>>), % SMALL_BIG_EXT (negative zero) N = largest_small_big(), - ?line decode( N,<<131,110,255,0,N:2040/little>>), % largest SMALL_BIG_EXT - ?line decode(-N,<<131,110,255,1,N:2040/little>>), % largest SMALL_BIG_EXT + decode( N,<<131,110,255,0,N:2040/little>>), % largest SMALL_BIG_EXT + decode(-N,<<131,110,255,1,N:2040/little>>), % largest SMALL_BIG_EXT ok. %% Test to decode uncompleted encodings for all in "erl_ext_dist.txt" decode_too_small(Config) when is_list(Config) -> - ?line decode_badarg(<<131, 97>>), - ?line decode_badarg(<<131, 98>>), - ?line decode_badarg(<<131, 98, 0>>), - ?line decode_badarg(<<131, 98, 0, 0>>), - ?line decode_badarg(<<131, 98, 0, 0, 0>>), - ?line decode_badarg(<<131, 99>>), - ?line decode_badarg(<<131, 99, 0>>), - ?line decode_badarg(<<131, 99, 0:240>>), - - ?line decode_badarg(<<131,100>>), - ?line decode_badarg(<<131,100, 1:16/big>>), - ?line decode_badarg(<<131,100, 2:16/big>>), - ?line decode_badarg(<<131,100, 2:16/big, "A">>), + decode_badarg(<<131, 97>>), + decode_badarg(<<131, 98>>), + decode_badarg(<<131, 98, 0>>), + decode_badarg(<<131, 98, 0, 0>>), + decode_badarg(<<131, 98, 0, 0, 0>>), + decode_badarg(<<131, 99>>), + decode_badarg(<<131, 99, 0>>), + decode_badarg(<<131, 99, 0:240>>), + + decode_badarg(<<131,100>>), + decode_badarg(<<131,100, 1:16/big>>), + decode_badarg(<<131,100, 2:16/big>>), + decode_badarg(<<131,100, 2:16/big, "A">>), % FIXME node name "A" seem ok, should it be? -% ?line decode_badarg(<<131,101,100,1:16/big,"A",42:32/big,0>>), - - ?line decode_badarg(<<131,101>>), - ?line decode_badarg(<<131,101,106>>), - ?line decode_badarg(<<131,101,255>>), - ?line decode_badarg(<<131,101,106,42:8/big>>), - ?line decode_badarg(<<131,101,106,42:16/big>>), - ?line decode_badarg(<<131,101,255,42:24/big>>), - ?line decode_badarg(<<131,101,255,42:32/big,0>>), - ?line decode_badarg(<<131,101,100,1:16/big,"A">>), - ?line decode_badarg(<<131,101,100,1:16/big,"A",42:32/big>>), - - ?line decode_badarg(<<131,102>>), - ?line decode_badarg(<<131,102,106,42:32/big,0>>), - ?line decode_badarg(<<131,102,255,42:32/big,0>>), - ?line decode_badarg(<<131,102,100,1:16/big,"A">>), - ?line decode_badarg(<<131,102,100,1:16/big,"A",42:32/big>>), - - ?line decode_badarg(<<131,103>>), - ?line decode_badarg(<<131,103,106,42:32/big,0>>), - ?line decode_badarg(<<131,103,255,42:32/big,0>>), - ?line decode_badarg(<<131,103,100,1:16/big,"A">>), - ?line decode_badarg(<<131,103,100,1:16/big,"A",42:32/big>>), - ?line decode_badarg(<<131,103,100,1:16/big,"A",4:32/big,2:32/big>>), - - ?line decode_badarg(<<131,104>>), - ?line decode_badarg(<<131,104, 1>>), - ?line decode_badarg(<<131,104, 2, 106>>), - ?line decode_badarg(<<131,105, 1:32/big>>), - ?line decode_badarg(<<131,105, 2:32/big, 106>>), - - ?line decode_badarg(<<131,107>>), - ?line decode_badarg(<<131,107, 1:16/big>>), - ?line decode_badarg(<<131,107, 2:16/big>>), - ?line decode_badarg(<<131,107, 2:16/big, "A">>), - - ?line decode_badarg(<<131,108>>), - ?line decode_badarg(<<131,108, 1:32/big>>), - ?line decode_badarg(<<131,108, 2:32/big>>), - ?line decode_badarg(<<131,108, 2:32/big, 106>>), % FIXME don't use NIL - - ?line decode_badarg(<<131,109>>), - ?line decode_badarg(<<131,109, 1:32/big>>), - ?line decode_badarg(<<131,109, 2:32/big>>), - ?line decode_badarg(<<131,109, 2:32/big, 42>>), + % decode_badarg(<<131,101,100,1:16/big,"A",42:32/big,0>>), + + decode_badarg(<<131,101>>), + decode_badarg(<<131,101,106>>), + decode_badarg(<<131,101,255>>), + decode_badarg(<<131,101,106,42:8/big>>), + decode_badarg(<<131,101,106,42:16/big>>), + decode_badarg(<<131,101,255,42:24/big>>), + decode_badarg(<<131,101,255,42:32/big,0>>), + decode_badarg(<<131,101,100,1:16/big,"A">>), + decode_badarg(<<131,101,100,1:16/big,"A",42:32/big>>), + + decode_badarg(<<131,102>>), + decode_badarg(<<131,102,106,42:32/big,0>>), + decode_badarg(<<131,102,255,42:32/big,0>>), + decode_badarg(<<131,102,100,1:16/big,"A">>), + decode_badarg(<<131,102,100,1:16/big,"A",42:32/big>>), + + decode_badarg(<<131,103>>), + decode_badarg(<<131,103,106,42:32/big,0>>), + decode_badarg(<<131,103,255,42:32/big,0>>), + decode_badarg(<<131,103,100,1:16/big,"A">>), + decode_badarg(<<131,103,100,1:16/big,"A",42:32/big>>), + decode_badarg(<<131,103,100,1:16/big,"A",4:32/big,2:32/big>>), + + decode_badarg(<<131,104>>), + decode_badarg(<<131,104, 1>>), + decode_badarg(<<131,104, 2, 106>>), + decode_badarg(<<131,105, 1:32/big>>), + decode_badarg(<<131,105, 2:32/big, 106>>), + + decode_badarg(<<131,107>>), + decode_badarg(<<131,107, 1:16/big>>), + decode_badarg(<<131,107, 2:16/big>>), + decode_badarg(<<131,107, 2:16/big, "A">>), + + decode_badarg(<<131,108>>), + decode_badarg(<<131,108, 1:32/big>>), + decode_badarg(<<131,108, 2:32/big>>), + decode_badarg(<<131,108, 2:32/big, 106>>), % FIXME don't use NIL + + decode_badarg(<<131,109>>), + decode_badarg(<<131,109, 1:32/big>>), + decode_badarg(<<131,109, 2:32/big>>), + decode_badarg(<<131,109, 2:32/big, 42>>), N = largest_small_big(), - ?line decode_badarg(<<131,110>>), - ?line decode_badarg(<<131,110,1>>), - ?line decode_badarg(<<131,110,1,0>>), - ?line decode_badarg(<<131,110,1,1>>), - ?line decode_badarg(<<131,110,2,0,42>>), - ?line decode_badarg(<<131,110,2,1,42>>), - ?line decode_badarg(<<131,110,255,0,N:2032/little>>), - ?line decode_badarg(<<131,110,255,1,N:2032/little>>), - - ?line decode_badarg(<<131,111>>), - ?line decode_badarg(<<131,111, 1:32/big>>), - ?line decode_badarg(<<131,111, 1:32/big,0>>), - ?line decode_badarg(<<131,111, 1:32/big,1>>), - ?line decode_badarg(<<131,111, 2:32/big,0,42>>), - ?line decode_badarg(<<131,111, 2:32/big,1,42>>), - ?line decode_badarg(<<131,111,256:32/big,0,N:2032/little>>), - ?line decode_badarg(<<131,111,256:32/big,1,N:2032/little>>), - ?line decode_badarg(<<131,111,256:32/big,0,N:2040/little>>), - ?line decode_badarg(<<131,111,256:32/big,1,N:2040/little>>), - ?line decode_badarg(<<131,111,257:32/big,0,N:2048/little>>), - ?line decode_badarg(<<131,111,257:32/big,1,N:2048/little>>), + decode_badarg(<<131,110>>), + decode_badarg(<<131,110,1>>), + decode_badarg(<<131,110,1,0>>), + decode_badarg(<<131,110,1,1>>), + decode_badarg(<<131,110,2,0,42>>), + decode_badarg(<<131,110,2,1,42>>), + decode_badarg(<<131,110,255,0,N:2032/little>>), + decode_badarg(<<131,110,255,1,N:2032/little>>), + + decode_badarg(<<131,111>>), + decode_badarg(<<131,111, 1:32/big>>), + decode_badarg(<<131,111, 1:32/big,0>>), + decode_badarg(<<131,111, 1:32/big,1>>), + decode_badarg(<<131,111, 2:32/big,0,42>>), + decode_badarg(<<131,111, 2:32/big,1,42>>), + decode_badarg(<<131,111,256:32/big,0,N:2032/little>>), + decode_badarg(<<131,111,256:32/big,1,N:2032/little>>), + decode_badarg(<<131,111,256:32/big,0,N:2040/little>>), + decode_badarg(<<131,111,256:32/big,1,N:2040/little>>), + decode_badarg(<<131,111,257:32/big,0,N:2048/little>>), + decode_badarg(<<131,111,257:32/big,1,N:2048/little>>), % Emulator dies if trying to create large bignum.... -% ?line decode_badarg(<<131,111,16#ffffffff:32/big,0>>), -% ?line decode_badarg(<<131,111,16#ffffffff:32/big,1>>), - - ?line decode_badarg(<<131, 78>>), - ?line decode_badarg(<<131, 78, 42>>), - ?line decode_badarg(<<131, 78, 42, 1>>), - ?line decode_badarg(<<131, 78, 42, 1:16/big>>), - ?line decode_badarg(<<131, 78, 42, 2:16/big>>), - ?line decode_badarg(<<131, 78, 42, 2:16/big, "A">>), - - ?line decode_badarg(<<131, 67>>), - - ?line decode_badarg(<<131,114>>), - ?line decode_badarg(<<131,114,0>>), - ?line decode_badarg(<<131,114,1:16/big>>), - ?line decode_badarg(<<131,114,1:16/big,100>>), - ?line decode_badarg(<<131,114,1:16/big,100,1:16/big>>), - ?line decode_badarg(<<131,114,1:16/big,100,1:16/big,"A">>), - ?line decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0>>), - ?line decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:8>>), - ?line decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:16>>), - ?line decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:24>>), - - ?line decode_badarg(<<131,117>>), % FIXME needs more tests + % decode_badarg(<<131,111,16#ffffffff:32/big,0>>), + % decode_badarg(<<131,111,16#ffffffff:32/big,1>>), + + decode_badarg(<<131, 78>>), + decode_badarg(<<131, 78, 42>>), + decode_badarg(<<131, 78, 42, 1>>), + decode_badarg(<<131, 78, 42, 1:16/big>>), + decode_badarg(<<131, 78, 42, 2:16/big>>), + decode_badarg(<<131, 78, 42, 2:16/big, "A">>), + + decode_badarg(<<131, 67>>), + + decode_badarg(<<131,114>>), + decode_badarg(<<131,114,0>>), + decode_badarg(<<131,114,1:16/big>>), + decode_badarg(<<131,114,1:16/big,100>>), + decode_badarg(<<131,114,1:16/big,100,1:16/big>>), + decode_badarg(<<131,114,1:16/big,100,1:16/big,"A">>), + decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0>>), + decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:8>>), + decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:16>>), + decode_badarg(<<131,114,1:16/big,100,1:16/big,"A",0,42:24>>), + + decode_badarg(<<131,117>>), % FIXME needs more tests ok. @@ -354,7 +353,7 @@ my_appender_1(N, T0) -> U = rnd_term(), T = [U|T0], my_appender_1(N-1, T). - + seed() -> rand:seed(exsplus, {3172,9815,20129}). diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index 0face2dfe8..1cad7e7920 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -21,9 +21,9 @@ -module(exception_SUITE). -export([all/0, suite/0, - badmatch/1, pending_errors/1, nil_arith/1, + badmatch/1, pending_errors/1, nil_arith/1, stacktrace/1, nested_stacktrace/1, raise/1, gunilla/1, per/1, - exception_with_heap_frag/1, line_numbers/1]). + exception_with_heap_frag/1, line_numbers/1]). -export([bad_guy/2]). -export([crash/1]). @@ -41,31 +41,31 @@ all() -> exception_with_heap_frag, line_numbers]. -define(try_match(E), - catch ?MODULE:bar(), - {'EXIT', {{badmatch, nomatch}, _}} = (catch E = id(nomatch))). + catch ?MODULE:bar(), + {'EXIT', {{badmatch, nomatch}, _}} = (catch E = id(nomatch))). %% Test that deliberately bad matches are reported correctly. badmatch(Config) when is_list(Config) -> - ?line ?try_match(a), - ?line ?try_match(42), - ?line ?try_match({a, b, c}), - ?line ?try_match([]), - ?line ?try_match(1.0), + ?try_match(a), + ?try_match(42), + ?try_match({a, b, c}), + ?try_match([]), + ?try_match(1.0), ok. %% Test various exceptions, in the presence of a previous error suppressed %% in a guard. pending_errors(Config) when is_list(Config) -> - ?line pending(e_badmatch, {badmatch, b}), - ?line pending(x, function_clause), - ?line pending(e_case, {case_clause, xxx}), - ?line pending(e_if, if_clause), - ?line pending(e_badarith, badarith), - ?line pending(e_undef, undef), - ?line pending(e_timeoutval, timeout_value), - ?line pending(e_badarg, badarg), - ?line pending(e_badarg_spawn, badarg), + pending(e_badmatch, {badmatch, b}), + pending(x, function_clause), + pending(e_case, {case_clause, xxx}), + pending(e_if, if_clause), + pending(e_badarith, badarith), + pending(e_undef, undef), + pending(e_timeoutval, timeout_value), + pending(e_badarg, badarg), + pending(e_badarg_spawn, badarg), ok. bad_guy(pe_badarith, Other) when Other+1 == 0 -> % badarith (suppressed) @@ -74,11 +74,11 @@ bad_guy(pe_badarg, Other) when length(Other) > 0 -> % badarg (suppressed) ok; bad_guy(_, e_case) -> case id(xxx) of - ok -> ok + ok -> ok end; % case_clause bad_guy(_, e_if) -> if - a == b -> ok + a == b -> ok end; % if_clause bad_guy(_, e_badarith) -> 1+b; % badarith @@ -86,9 +86,9 @@ bad_guy(_, e_undef) -> non_existing_module:foo(); % undef bad_guy(_, e_timeoutval) -> receive - after arne -> % timeout_value - ok - end; + after arne -> % timeout_value + ok + end; bad_guy(_, e_badarg) -> node(xxx); % badarg bad_guy(_, e_badarg_spawn) -> @@ -107,30 +107,30 @@ pending(First, Second, Expected) -> pending_catched(First, Second, Expected) -> ok = io:format("Catching bad_guy(~p, ~p)", [First, Second]), case catch bad_guy(First, Second) of - {'EXIT', Reason} -> - pending(Reason, bad_guy, [First, Second], Expected); - Other -> - ct:fail({not_exit, Other}) + {'EXIT', Reason} -> + pending(Reason, bad_guy, [First, Second], Expected); + Other -> + ct:fail({not_exit, Other}) end. pending_exit_message(Args, Expected) -> ok = io:format("Trapping EXITs from spawn_link(~p, ~p, ~p)", - [?MODULE, bad_guy, Args]), + [?MODULE, bad_guy, Args]), process_flag(trap_exit, true), Pid = spawn_link(?MODULE, bad_guy, Args), receive - {'EXIT', Pid, Reason} -> - pending(Reason, bad_guy, Args, Expected); - Other -> - ct:fail({unexpected_message, Other}) + {'EXIT', Pid, Reason} -> + pending(Reason, bad_guy, Args, Expected); + Other -> + ct:fail({unexpected_message, Other}) after 10000 -> - ct:fail(timeout) + ct:fail(timeout) end, process_flag(trap_exit, false). pending({badarg,[{erlang,Bif,BifArgs,Loc1}, - {?MODULE,Func,Arity,Loc2}|_]}, - Func, Args, _Code) + {?MODULE,Func,Arity,Loc2}|_]}, + Func, Args, _Code) when is_atom(Bif), is_list(BifArgs), length(Args) =:= Arity, is_list(Loc1), is_list(Loc2) -> ok; @@ -149,62 +149,62 @@ pending(Reason, _Function, _Args, _Code) -> %% Test that doing arithmetics on [] gives a badarith EXIT and not a crash. nil_arith(Config) when is_list(Config) -> - ?line ba_plus_minus_times([], []), - - ?line ba_plus_minus_times([], 0), - ?line ba_plus_minus_times([], 42), - ?line ba_plus_minus_times([], 38724978123478923784), - ?line ba_plus_minus_times([], 38.72), - - ?line ba_plus_minus_times(0, []), - ?line ba_plus_minus_times(334, []), - ?line ba_plus_minus_times(387249797813478923784, []), - ?line ba_plus_minus_times(344.22, []), - - ?line ba_div_rem([], []), - - ?line ba_div_rem([], 0), - ?line ba_div_rem([], 1), - ?line ba_div_rem([], 42), - ?line ba_div_rem([], 38724978123478923784), - ?line ba_div_rem(344.22, []), - - ?line ba_div_rem(0, []), - ?line ba_div_rem(1, []), - ?line ba_div_rem(334, []), - ?line ba_div_rem(387249797813478923784, []), - ?line ba_div_rem(344.22, []), - - ?line ba_div_rem(344.22, 0.0), - ?line ba_div_rem(1, 0.0), - ?line ba_div_rem(392873498733971, 0.0), - - ?line ba_bop([], []), - ?line ba_bop(0, []), - ?line ba_bop(42, []), - ?line ba_bop(-42342742987343, []), - ?line ba_bop(238.342, []), - ?line ba_bop([], 0), - ?line ba_bop([], -243), - ?line ba_bop([], 243), - ?line ba_bop([], 2438724982478933), - ?line ba_bop([], 3987.37), - - ?line ba_bnot([]), - ?line ba_bnot(23.33), - - ?line ba_shift([], []), - ?line ba_shift([], 0), - ?line ba_shift([], 4), - ?line ba_shift([], -4), - ?line ba_shift([], 2343333333333), - ?line ba_shift([], -333333333), - ?line ba_shift([], 234.00), - ?line ba_shift(23, []), - ?line ba_shift(0, []), - ?line ba_shift(-3433443433433323, []), - ?line ba_shift(433443433433323, []), - ?line ba_shift(343.93, []), + ba_plus_minus_times([], []), + + ba_plus_minus_times([], 0), + ba_plus_minus_times([], 42), + ba_plus_minus_times([], 38724978123478923784), + ba_plus_minus_times([], 38.72), + + ba_plus_minus_times(0, []), + ba_plus_minus_times(334, []), + ba_plus_minus_times(387249797813478923784, []), + ba_plus_minus_times(344.22, []), + + ba_div_rem([], []), + + ba_div_rem([], 0), + ba_div_rem([], 1), + ba_div_rem([], 42), + ba_div_rem([], 38724978123478923784), + ba_div_rem(344.22, []), + + ba_div_rem(0, []), + ba_div_rem(1, []), + ba_div_rem(334, []), + ba_div_rem(387249797813478923784, []), + ba_div_rem(344.22, []), + + ba_div_rem(344.22, 0.0), + ba_div_rem(1, 0.0), + ba_div_rem(392873498733971, 0.0), + + ba_bop([], []), + ba_bop(0, []), + ba_bop(42, []), + ba_bop(-42342742987343, []), + ba_bop(238.342, []), + ba_bop([], 0), + ba_bop([], -243), + ba_bop([], 243), + ba_bop([], 2438724982478933), + ba_bop([], 3987.37), + + ba_bnot([]), + ba_bnot(23.33), + + ba_shift([], []), + ba_shift([], 0), + ba_shift([], 4), + ba_shift([], -4), + ba_shift([], 2343333333333), + ba_shift([], -333333333), + ba_shift([], 234.00), + ba_shift(23, []), + ba_shift(0, []), + ba_shift(-3433443433433323, []), + ba_shift(433443433433323, []), + ba_shift(343.93, []), ok. ba_plus_minus_times(A, B) -> @@ -236,7 +236,7 @@ ba_shift(A, B) -> {'EXIT', {badarith, _}} = (catch A bsl B), io:format("~p bsr ~p", [A, B]), {'EXIT', {badarith, _}} = (catch A bsr B). - + ba_bnot(A) -> io:format("bnot ~p", [A]), {'EXIT', {badarith, _}} = (catch bnot A). @@ -245,38 +245,38 @@ ba_bnot(A) -> stacktrace(Conf) when is_list(Conf) -> Tag = make_ref(), - ?line {_,Mref} = spawn_monitor(fun() -> exit({Tag,erlang:get_stacktrace()}) end), - ?line {Tag,[]} = receive {'DOWN',Mref,_,_,Info} -> Info end, + {_,Mref} = spawn_monitor(fun() -> exit({Tag,erlang:get_stacktrace()}) end), + {Tag,[]} = receive {'DOWN',Mref,_,_,Info} -> Info end, V = [make_ref()|self()], - ?line {value2,{caught1,badarg,[{erlang,abs,[V],_}|_]=St1}} = - stacktrace_1({'abs',V}, error, {value,V}), - ?line St1 = erase(stacktrace1), - ?line St1 = erase(stacktrace2), - ?line St1 = erlang:get_stacktrace(), - ?line {caught2,{error,badarith},[{?MODULE,my_add,2,_}|_]=St2} = - stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}), - ?line [{?MODULE,my_div,2,_}|_] = erase(stacktrace1), - ?line St2 = erase(stacktrace2), - ?line St2 = erlang:get_stacktrace(), - ?line {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]=St3} = - stacktrace_1({value,V}, error, {value,V}), - ?line St3 = erase(stacktrace1), - ?line St3 = erase(stacktrace2), - ?line St3 = erlang:get_stacktrace(), - ?line {caught2,{throw,V},[{?MODULE,foo,1,_}|_]=St4} = - stacktrace_1({value,V}, error, {throw,V}), - ?line [{?MODULE,stacktrace_1,3,_}|_] = erase(stacktrace1), - ?line St4 = erase(stacktrace2), - ?line St4 = erlang:get_stacktrace(), + {value2,{caught1,badarg,[{erlang,abs,[V],_}|_]=St1}} = + stacktrace_1({'abs',V}, error, {value,V}), + St1 = erase(stacktrace1), + St1 = erase(stacktrace2), + St1 = erlang:get_stacktrace(), + {caught2,{error,badarith},[{?MODULE,my_add,2,_}|_]=St2} = + stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}), + [{?MODULE,my_div,2,_}|_] = erase(stacktrace1), + St2 = erase(stacktrace2), + St2 = erlang:get_stacktrace(), + {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]=St3} = + stacktrace_1({value,V}, error, {value,V}), + St3 = erase(stacktrace1), + St3 = erase(stacktrace2), + St3 = erlang:get_stacktrace(), + {caught2,{throw,V},[{?MODULE,foo,1,_}|_]=St4} = + stacktrace_1({value,V}, error, {throw,V}), + [{?MODULE,stacktrace_1,3,_}|_] = erase(stacktrace1), + St4 = erase(stacktrace2), + St4 = erlang:get_stacktrace(), try - ?line stacktrace_2() + stacktrace_2() catch - error:{badmatch,_} -> - [{?MODULE,stacktrace_2,0,_}, - {?MODULE,stacktrace,1,_}|_] = - erlang:get_stacktrace(), - ok + error:{badmatch,_} -> + [{?MODULE,stacktrace_2,0,_}, + {?MODULE,stacktrace,1,_}|_] = + erlang:get_stacktrace(), + ok end. stacktrace_1(X, C1, Y) -> @@ -288,7 +288,7 @@ stacktrace_1(X, C1, Y) -> C1:D1 -> {caught1,D1,erlang:get_stacktrace()} after put(stacktrace1, erlang:get_stacktrace()), - foo(Y) + foo(Y) end of V2 -> {value2,V2} catch @@ -304,21 +304,21 @@ stacktrace_2() -> nested_stacktrace(Conf) when is_list(Conf) -> V = [{make_ref()}|[self()]], - ?line value1 = - nested_stacktrace_1({{value,{V,x1}},void,{V,x1}}, - {void,void,void}), - ?line {caught1, - [{?MODULE,my_add,2,_}|_], - value2, - [{?MODULE,my_add,2,_}|_]} = - nested_stacktrace_1({{'add',{V,x1}},error,badarith}, - {{value,{V,x2}},void,{V,x2}}), - ?line {caught1, - [{?MODULE,my_add,2,_}|_], - {caught2,[{erlang,abs,[V],_}|_]}, - [{erlang,abs,[V],_}|_]} = - nested_stacktrace_1({{'add',{V,x1}},error,badarith}, - {{'abs',V},error,badarg}), + value1 = + nested_stacktrace_1({{value,{V,x1}},void,{V,x1}}, + {void,void,void}), + {caught1, + [{?MODULE,my_add,2,_}|_], + value2, + [{?MODULE,my_add,2,_}|_]} = + nested_stacktrace_1({{'add',{V,x1}},error,badarith}, + {{value,{V,x2}},void,{V,x2}}), + {caught1, + [{?MODULE,my_add,2,_}|_], + {caught2,[{erlang,abs,[V],_}|_]}, + [{erlang,abs,[V],_}|_]} = + nested_stacktrace_1({{'add',{V,x1}},error,badarith}, + {{'abs',V},error,badarg}), ok. nested_stacktrace_1({X1,C1,V1}, {X2,C2,V2}) -> @@ -326,64 +326,64 @@ nested_stacktrace_1({X1,C1,V1}, {X2,C2,V2}) -> V1 -> value1 catch C1:V1 -> - S1 = erlang:get_stacktrace(), + S1 = erlang:get_stacktrace(), T2 = - try foo(X2) of - V2 -> value2 - catch - C2:V2 -> {caught2,erlang:get_stacktrace()} - end, + try foo(X2) of + V2 -> value2 + catch + C2:V2 -> {caught2,erlang:get_stacktrace()} + end, {caught1,S1,T2,erlang:get_stacktrace()} end. raise(Conf) when is_list(Conf) -> - ?line erase(raise), - ?line A = - try - ?line try foo({'div',{1,0}}) - catch - error:badarith -> - put(raise, A0 = erlang:get_stacktrace()), - ?line erlang:raise(error, badarith, A0) - end - catch - error:badarith -> - ?line A1 = erlang:get_stacktrace(), - ?line A1 = get(raise) - end, - ?line A = erlang:get_stacktrace(), - ?line A = get(raise), - ?line [{?MODULE,my_div,2,_}|_] = A, + erase(raise), + A = + try + try foo({'div',{1,0}}) + catch + error:badarith -> + put(raise, A0 = erlang:get_stacktrace()), + erlang:raise(error, badarith, A0) + end + catch + error:badarith -> + A1 = erlang:get_stacktrace(), + A1 = get(raise) + end, + A = erlang:get_stacktrace(), + A = get(raise), + [{?MODULE,my_div,2,_}|_] = A, %% N = 8, % Must be even - ?line N = erlang:system_flag(backtrace_depth, N), - ?line B = odd_even(N, []), - ?line try even(N) - catch error:function_clause -> ok - end, - ?line B = erlang:get_stacktrace(), + N = erlang:system_flag(backtrace_depth, N), + B = odd_even(N, []), + try even(N) + catch error:function_clause -> ok + end, + B = erlang:get_stacktrace(), %% - ?line C0 = odd_even(N+1, []), - ?line C = lists:sublist(C0, N), - ?line try odd(N+1) - catch error:function_clause -> ok - end, - ?line C = erlang:get_stacktrace(), - ?line try erlang:raise(error, function_clause, C0) - catch error:function_clause -> ok - end, - ?line C = erlang:get_stacktrace(), + C0 = odd_even(N+1, []), + C = lists:sublist(C0, N), + try odd(N+1) + catch error:function_clause -> ok + end, + C = erlang:get_stacktrace(), + try erlang:raise(error, function_clause, C0) + catch error:function_clause -> ok + end, + C = erlang:get_stacktrace(), ok. odd_even(N, R) when is_integer(N), N > 1 -> odd_even(N-1, - [if (N rem 2) == 0 -> - {?MODULE,even,1,[{file,"odd_even.erl"},{line,3}]}; - true -> - {?MODULE,odd,1,[{file,"odd_even.erl"},{line,6}]} - end|R]); + [if (N rem 2) == 0 -> + {?MODULE,even,1,[{file,"odd_even.erl"},{line,3}]}; + true -> + {?MODULE,odd,1,[{file,"odd_even.erl"},{line,6}]} + end|R]); odd_even(1, R) -> [{?MODULE,odd,[1],[{file,"odd_even.erl"},{line,5}]}|R]. @@ -413,18 +413,18 @@ my_add(A, B) -> my_abs(X) -> abs(X). gunilla(Config) when is_list(Config) -> - ?line {throw,kalle} = gunilla_1(), - ?line [] = erlang:get_stacktrace(), + {throw,kalle} = gunilla_1(), + [] = erlang:get_stacktrace(), ok. gunilla_1() -> try try arne() - after - pelle - end + after + pelle + end catch - C:R -> - {C,R} + C:R -> + {C,R} end. arne() -> @@ -433,18 +433,18 @@ arne() -> per(Config) when is_list(Config) -> try - t1(0,pad,0), - t2(0,pad,0) + t1(0,pad,0), + t2(0,pad,0) catch - error:badarith -> - ok + error:badarith -> + ok end. t1(_,X,_) -> - (1 bsl X) + 1. + (1 bsl X) + 1. t2(_,X,_) -> - (X bsl 1) + 1. + (X bsl 1) + 1. %% %% Make sure that even if a BIF builds an heap fragment, then causes an exception, @@ -456,155 +456,155 @@ exception_with_heap_frag(Config) when is_list(Config) -> %% Floats are only validated when the heap fragment has been allocated. BadFloat = <<131,99,53,46,48,$X,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,101,45,48,49,0,0,0,0,0>>, - ?line do_exception_with_heap_frag(BadFloat, Sizes), + do_exception_with_heap_frag(BadFloat, Sizes), %% {Binary,BadFloat}: When the error in float is discovered, a refc-binary %% has been allocated and the list of refc-binaries goes through the %% heap fragment. BinAndFloat = - <<131,104,2,109,0,0,1,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, - 21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45, - 46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70, - 71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, - 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115, - 116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134, - 135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153, - 154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172, - 173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, - 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210, - 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229, - 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248, - 249,250,251,252,253,254,255,99,51,46,49,52,$B,$l,$u,$r,$f,48,48,48,48,48,48, - 48,48,49,50,52,51,52,101,43,48,48,0,0,0,0,0>>, - ?line do_exception_with_heap_frag(BinAndFloat, Sizes), + <<131,104,2,109,0,0,1,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45, + 46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70, + 71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115, + 116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134, + 135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153, + 154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172, + 173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210, + 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229, + 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248, + 249,250,251,252,253,254,255,99,51,46,49,52,$B,$l,$u,$r,$f,48,48,48,48,48,48, + 48,48,49,50,52,51,52,101,43,48,48,0,0,0,0,0>>, + do_exception_with_heap_frag(BinAndFloat, Sizes), %% {Fun,BadFloat} FunAndFloat = - <<131,104,2,112,0,0,0,66,0,238,239,135,138,137,216,89,57,22,111,52,126,16,84, - 71,8,0,0,0,0,0,0,0,0,100,0,1,116,97,0,98,5,175,169,123,103,100,0,13,110,111, - 110,111,100,101,64,110,111,104,111,115,116,0,0,0,41,0,0,0,0,0,99,50,46,55,48, - $Y,57,57,57,57,57,57,57,57,57,57,57,57,57,54,52,52,55,101,43,48,48,0,0,0,0,0>>, - ?line do_exception_with_heap_frag(FunAndFloat, Sizes), + <<131,104,2,112,0,0,0,66,0,238,239,135,138,137,216,89,57,22,111,52,126,16,84, + 71,8,0,0,0,0,0,0,0,0,100,0,1,116,97,0,98,5,175,169,123,103,100,0,13,110,111, + 110,111,100,101,64,110,111,104,111,115,116,0,0,0,41,0,0,0,0,0,99,50,46,55,48, + $Y,57,57,57,57,57,57,57,57,57,57,57,57,57,54,52,52,55,101,43,48,48,0,0,0,0,0>>, + do_exception_with_heap_frag(FunAndFloat, Sizes), %% [ExternalPid|BadFloat] ExtPidAndFloat = - <<131,108,0,0,0,1,103,100,0,13,107,97,108,108,101,64,115,116,114,105,100,101, - 114,0,0,0,36,0,0,0,0,2,99,48,46,$@,48,48,48,48,48,48,48,48,48,48,48,48,48,48, - 48,48,48,48,48,101,43,48,48,0,0,0,0,0>>, - ?line do_exception_with_heap_frag(ExtPidAndFloat, Sizes), - + <<131,108,0,0,0,1,103,100,0,13,107,97,108,108,101,64,115,116,114,105,100,101, + 114,0,0,0,36,0,0,0,0,2,99,48,46,$@,48,48,48,48,48,48,48,48,48,48,48,48,48,48, + 48,48,48,48,48,101,43,48,48,0,0,0,0,0>>, + do_exception_with_heap_frag(ExtPidAndFloat, Sizes), + ok. do_exception_with_heap_frag(Bin, [Sz|Sizes]) -> Filler = erlang:make_tuple(Sz, a), spawn(fun() -> - try - binary_to_term(Bin) - catch - _:_ -> - %% term_to_binary/1 is an easy way to traverse the - %% entire stacktrace term to make sure that every part - %% of it is OK. - term_to_binary(erlang:get_stacktrace()) - end, - id(Filler) - end), + try + binary_to_term(Bin) + catch + _:_ -> + %% term_to_binary/1 is an easy way to traverse the + %% entire stacktrace term to make sure that every part + %% of it is OK. + term_to_binary(erlang:get_stacktrace()) + end, + id(Filler) + end), do_exception_with_heap_frag(Bin, Sizes); do_exception_with_heap_frag(_, []) -> ok. line_numbers(Config) when is_list(Config) -> {'EXIT',{{case_clause,bad_tag}, - [{?MODULE,line1,2, - [{file,"fake_file.erl"},{line,3}]}, - {?MODULE,line_numbers,1,_}|_]}} = - (catch line1(bad_tag, 0)), + [{?MODULE,line1,2, + [{file,"fake_file.erl"},{line,3}]}, + {?MODULE,line_numbers,1,_}|_]}} = + (catch line1(bad_tag, 0)), {'EXIT',{badarith, - [{?MODULE,line1,2, - [{file,"fake_file.erl"},{line,5}]}, - {?MODULE,line_numbers,1,_}|_]}} = - (catch line1(a, not_an_integer)), + [{?MODULE,line1,2, + [{file,"fake_file.erl"},{line,5}]}, + {?MODULE,line_numbers,1,_}|_]}} = + (catch line1(a, not_an_integer)), {'EXIT',{{badmatch,{ok,1}}, - [{?MODULE,line1,2, - [{file,"fake_file.erl"},{line,7}]}, - {?MODULE,line_numbers,1,_}|_]}} = - (catch line1(a, 0)), + [{?MODULE,line1,2, + [{file,"fake_file.erl"},{line,7}]}, + {?MODULE,line_numbers,1,_}|_]}} = + (catch line1(a, 0)), {'EXIT',{crash, - [{?MODULE,crash,1, - [{file,"fake_file.erl"},{line,14}]}, - {?MODULE,line_numbers,1,_}|_]}} = - (catch line1(a, 41)), + [{?MODULE,crash,1, + [{file,"fake_file.erl"},{line,14}]}, + {?MODULE,line_numbers,1,_}|_]}} = + (catch line1(a, 41)), ModFile = ?MODULE_STRING++".erl", [{?MODULE,maybe_crash,1,[{file,"call.erl"},{line,28}]}, {?MODULE,call1,0,[{file,"call.erl"},{line,14}]}, {?MODULE,close_calls,1,[{file,"call.erl"},{line,5}]}, {?MODULE,line_numbers,1,[{file,ModFile},{line,_}]}|_] = - close_calls(call1), + close_calls(call1), [{?MODULE,maybe_crash,1,[{file,"call.erl"},{line,28}]}, {?MODULE,call2,0,[{file,"call.erl"},{line,18}]}, {?MODULE,close_calls,1,[{file,"call.erl"},{line,6}]}, {?MODULE,line_numbers,1,[{file,ModFile},{line,_}]}|_] = - close_calls(call2), + close_calls(call2), [{?MODULE,maybe_crash,1,[{file,"call.erl"},{line,28}]}, {?MODULE,call3,0,[{file,"call.erl"},{line,22}]}, {?MODULE,close_calls,1,[{file,"call.erl"},{line,7}]}, {?MODULE,line_numbers,1,[{file,ModFile},{line,_}]}|_] = - close_calls(call3), + close_calls(call3), no_crash = close_calls(other), <<0,0>> = build_binary1(16), {'EXIT',{badarg, - [{?MODULE,build_binary1,1, - [{file,"bit_syntax.erl"},{line,72503}]}, - {?MODULE,line_numbers,1, - [{file,ModFile},{line,_}]}|_]}} = - (catch build_binary1(bad_size)), + [{?MODULE,build_binary1,1, + [{file,"bit_syntax.erl"},{line,72503}]}, + {?MODULE,line_numbers,1, + [{file,ModFile},{line,_}]}|_]}} = + (catch build_binary1(bad_size)), <<7,1,2,3>> = build_binary2(8, <<1,2,3>>), {'EXIT',{badarg, - [{?MODULE,build_binary2,2, - [{file,"bit_syntax.erl"},{line,72507}]}, - {?MODULE,line_numbers,1, - [{file,ModFile},{line,_}]}|_]}} = - (catch build_binary2(bad_size, <<>>)), + [{?MODULE,build_binary2,2, + [{file,"bit_syntax.erl"},{line,72507}]}, + {?MODULE,line_numbers,1, + [{file,ModFile},{line,_}]}|_]}} = + (catch build_binary2(bad_size, <<>>)), {'EXIT',{badarg, - [{erlang,bit_size,[bad_binary],[]}, - {?MODULE,build_binary2,2, - [{file,"bit_syntax.erl"},{line,72507}]}, - {?MODULE,line_numbers,1, - [{file,ModFile},{line,_}]}|_]}} = - (catch build_binary2(8, bad_binary)), + [{erlang,bit_size,[bad_binary],[]}, + {?MODULE,build_binary2,2, + [{file,"bit_syntax.erl"},{line,72507}]}, + {?MODULE,line_numbers,1, + [{file,ModFile},{line,_}]}|_]}} = + (catch build_binary2(8, bad_binary)), <<"abc",357:16>> = build_binary3(<<"abc">>), {'EXIT',{badarg,[{?MODULE,build_binary3,1, - [{file,"bit_syntax.erl"},{line,72511}]}, - {?MODULE,line_numbers,1, - [{file,ModFile},{line,_}]}|_]}} = - (catch build_binary3(no_binary)), + [{file,"bit_syntax.erl"},{line,72511}]}, + {?MODULE,line_numbers,1, + [{file,ModFile},{line,_}]}|_]}} = + (catch build_binary3(no_binary)), {'EXIT',{function_clause, - [{?MODULE,do_call_abs,[y,y], - [{file,"gc_bif.erl"},{line,18}]}, - {?MODULE,line_numbers,1,_}|_]}} = - (catch do_call_abs(y, y)), + [{?MODULE,do_call_abs,[y,y], + [{file,"gc_bif.erl"},{line,18}]}, + {?MODULE,line_numbers,1,_}|_]}} = + (catch do_call_abs(y, y)), {'EXIT',{badarg, - [{erlang,abs,[[]],[]}, - {?MODULE,do_call_abs,2, - [{file,"gc_bif.erl"},{line,19}]}, - {?MODULE,line_numbers,1,_}|_]}} = - (catch do_call_abs(x, [])), + [{erlang,abs,[[]],[]}, + {?MODULE,do_call_abs,2, + [{file,"gc_bif.erl"},{line,19}]}, + {?MODULE,line_numbers,1,_}|_]}} = + (catch do_call_abs(x, [])), {'EXIT',{{badmatch,"42"}, - [{MODULE,applied_bif_1,1,[{file,"applied_bif.erl"},{line,5}]}, - {?MODULE,line_numbers,1,_}|_]}} = - (catch applied_bif_1(42)), + [{MODULE,applied_bif_1,1,[{file,"applied_bif.erl"},{line,5}]}, + {?MODULE,line_numbers,1,_}|_]}} = + (catch applied_bif_1(42)), {'EXIT',{{badmatch,{current_location, - {?MODULE,applied_bif_2,0, - [{file,"applied_bif.erl"},{line,9}]}}}, - [{MODULE,applied_bif_2,0,[{file,"applied_bif.erl"},{line,10}]}, - {?MODULE,line_numbers,1,_}|_]}} = - (catch applied_bif_2()), + {?MODULE,applied_bif_2,0, + [{file,"applied_bif.erl"},{line,9}]}}}, + [{MODULE,applied_bif_2,0,[{file,"applied_bif.erl"},{line,10}]}, + {?MODULE,line_numbers,1,_}|_]}} = + (catch applied_bif_2()), ok. @@ -631,13 +631,13 @@ odd(N) when is_integer(N), N > 1, (N rem 2) == 1 -> -file("fake_file.erl", 1). %Line 1 line1(Tag, X) -> %Line 2 case Tag of %Line 3 - a -> - Y = X + 1, %Line 5 - Res = id({ok,Y}), %Line 6 - ?MODULE:crash({ok,42} = Res); %Line 7 - b -> - x = id(x), %Line 9 - ok %Line 10 + a -> + Y = X + 1, %Line 5 + Res = id({ok,Y}), %Line 6 + ?MODULE:crash({ok,42} = Res); %Line 7 + b -> + x = id(x), %Line 9 + ok %Line 10 end. %Line 11 crash(_) -> %Line 13 @@ -647,12 +647,12 @@ crash(_) -> %Line 13 close_calls(Where) -> %Line 2 put(where_to_crash, Where), %Line 3 try - call1(), %Line 5 - call2(), %Line 6 - call3(), %Line 7 - no_crash %Line 8 + call1(), %Line 5 + call2(), %Line 6 + call3(), %Line 7 + no_crash %Line 8 catch error:crash -> - erlang:get_stacktrace() %Line 10 + erlang:get_stacktrace() %Line 10 end. %Line 11 call1() -> %Line 13 @@ -669,10 +669,10 @@ call3() -> %Line 21 maybe_crash(Name) -> %Line 25 case get(where_to_crash) of %Line 26 - Name -> - erlang:error(crash); %Line 28 - _ -> - ok %Line 30 + Name -> + erlang:error(crash); %Line 28 + _ -> + ok %Line 30 end. -file("bit_syntax.erl", 72500). %Line 72500 diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index 9fd414ff97..78dec8c725 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -23,9 +23,9 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, groups/0, - fpe/1,fp_drv/1,fp_drv_thread/1,denormalized/1,match/1, + fpe/1,fp_drv/1,fp_drv_thread/1,denormalized/1,match/1, t_mul_add_ops/1, - bad_float_unpack/1, write/1, cmp_zero/1, cmp_integer/1, cmp_bignum/1]). + bad_float_unpack/1, write/1, cmp_zero/1, cmp_integer/1, cmp_bignum/1]). -export([otp_7178/1]). -export([hidden_inf/1]). -export([arith/1]). @@ -38,8 +38,7 @@ all() -> [fpe, fp_drv, fp_drv_thread, otp_7178, denormalized, match, bad_float_unpack, write, {group, comparison} ,hidden_inf - ,arith, t_mul_add_ops - ]. + ,arith, t_mul_add_ops]. groups() -> [{comparison, [parallel], [cmp_zero, cmp_integer, cmp_bignum]}]. @@ -50,11 +49,11 @@ groups() -> %% %% test that list_to_float on very small numbers give 0.0 otp_7178(Config) when is_list(Config) -> - ?line X = list_to_float("1.0e-325"), - ?line true = (X < 0.00000001) and (X > -0.00000001), - ?line Y = list_to_float("1.0e-325325325"), - ?line true = (Y < 0.00000001) and (Y > -0.00000001), - ?line {'EXIT', {badarg,_}} = (catch list_to_float("1.0e83291083210")), + X = list_to_float("1.0e-325"), + true = (X < 0.00000001) and (X > -0.00000001), + Y = list_to_float("1.0e-325325325"), + true = (Y < 0.00000001) and (Y > -0.00000001), + {'EXIT', {badarg,_}} = (catch list_to_float("1.0e83291083210")), ok. %% Forces floating point exceptions and tests that subsequent, legal, @@ -62,15 +61,15 @@ otp_7178(Config) when is_list(Config) -> %% Strollo. fpe(Config) when is_list(Config) -> - ?line 0.0 = math:log(1.0), - ?line {'EXIT', {badarith, _}} = (catch math:log(-1.0)), - ?line 0.0 = math:log(1.0), - ?line {'EXIT', {badarith, _}} = (catch math:log(0.0)), - ?line 0.0 = math:log(1.0), - ?line {'EXIT',{badarith,_}} = (catch 3.23e133 * id(3.57e257)), - ?line 0.0 = math:log(1.0), - ?line {'EXIT',{badarith,_}} = (catch 5.0/id(0.0)), - ?line 0.0 = math:log(1.0), + 0.0 = math:log(1.0), + {'EXIT', {badarith, _}} = (catch math:log(-1.0)), + 0.0 = math:log(1.0), + {'EXIT', {badarith, _}} = (catch math:log(0.0)), + 0.0 = math:log(1.0), + {'EXIT',{badarith,_}} = (catch 3.23e133 * id(3.57e257)), + 0.0 = math:log(1.0), + {'EXIT',{badarith,_}} = (catch 5.0/id(0.0)), + 0.0 = math:log(1.0), ok. @@ -82,66 +81,66 @@ fp_drv(Config) when is_list(Config) -> fp_drv_thread(Config) when is_list(Config) -> %% Run in a separate node since it used to crash the emulator... - ?line Parent = self(), - ?line DrvDir = proplists:get_value(data_dir, Config), - ?line {ok,Node} = start_node(Config), - ?line Tester = spawn_link(Node, - fun () -> - Parent ! - {self(), - fp_drv_test(?ERTS_FP_THREAD_TEST, - DrvDir)} - end), - ?line Result = receive {Tester, Res} -> Res end, - ?line stop_node(Node), - ?line Result. + Parent = self(), + DrvDir = proplists:get_value(data_dir, Config), + {ok,Node} = start_node(Config), + Tester = spawn_link(Node, + fun () -> + Parent ! + {self(), + fp_drv_test(?ERTS_FP_THREAD_TEST, + DrvDir)} + end), + Result = receive {Tester, Res} -> Res end, + stop_node(Node), + Result. fp_drv_test(Test, DrvDir) -> - ?line Drv = fp_drv, - ?line try - begin - ?line case erl_ddll:load_driver(DrvDir, Drv) of - ok -> - ok; - {error, permanent} -> - ok; - {error, LoadError} -> - exit({load_error, - erl_ddll:format_error(LoadError)}); - LoadError -> - exit({load_error, LoadError}) - end, - case open_port({spawn, Drv}, []) of - Port when is_port(Port) -> - try port_control(Port, Test, "") of - "ok" -> - 0.0 = math:log(1.0), - ok; - [$s,$k,$i,$p,$:,$ | Reason] -> - {skipped, Reason}; - Error -> - exit(Error) - after - Port ! {self(), close}, - receive {Port, closed} -> ok end, - false = lists:member(Port, erlang:ports()), - ok - end; - Error -> - exit({open_port_failed, Error}) - end - end - catch - throw:Term -> ?line Term - after - erl_ddll:unload_driver(Drv) - end. + Drv = fp_drv, + try + begin + case erl_ddll:load_driver(DrvDir, Drv) of + ok -> + ok; + {error, permanent} -> + ok; + {error, LoadError} -> + exit({load_error, + erl_ddll:format_error(LoadError)}); + LoadError -> + exit({load_error, LoadError}) + end, + case open_port({spawn, Drv}, []) of + Port when is_port(Port) -> + try port_control(Port, Test, "") of + "ok" -> + 0.0 = math:log(1.0), + ok; + [$s,$k,$i,$p,$:,$ | Reason] -> + {skipped, Reason}; + Error -> + exit(Error) + after + Port ! {self(), close}, + receive {Port, closed} -> ok end, + false = lists:member(Port, erlang:ports()), + ok + end; + Error -> + exit({open_port_failed, Error}) + end + end + catch + throw:Term -> Term + after + erl_ddll:unload_driver(Drv) + end. denormalized(Config) when is_list(Config) -> - ?line Denormalized = 1.0e-307 / 1000, - ?line roundtrip(Denormalized), - ?line NegDenormalized = -1.0e-307 / 1000, - ?line roundtrip(NegDenormalized), + Denormalized = 1.0e-307 / 1000, + roundtrip(Denormalized), + NegDenormalized = -1.0e-307 / 1000, + roundtrip(NegDenormalized), ok. roundtrip(N) -> @@ -149,12 +148,12 @@ roundtrip(N) -> N = binary_to_term(term_to_binary(N, [{minor_version,1}])). match(Config) when is_list(Config) -> - ?line one = match_1(1.0), - ?line two = match_1(2.0), - ?line a_lot = match_1(1000.0), - ?line {'EXIT',_} = (catch match_1(0.5)), + one = match_1(1.0), + two = match_1(2.0), + a_lot = match_1(1000.0), + {'EXIT',_} = (catch match_1(0.5)), ok. - + match_1(1.0) -> one; match_1(2.0) -> two; match_1(1000.0) -> a_lot. @@ -162,8 +161,8 @@ match_1(1000.0) -> a_lot. %% Thanks to Per Gustafsson. bad_float_unpack(Config) when is_list(Config) -> - ?line Bin = <<-1:64>>, - ?line -1 = bad_float_unpack_match(Bin), + Bin = <<-1:64>>, + -1 = bad_float_unpack_match(Bin), ok. bad_float_unpack_match(<>) -> F; @@ -215,72 +214,72 @@ span_cmp(Axis, Incr, Length) -> %% Diff: How much the float and int should differ when comparing span_cmp(Axis, Incr, Length, Diff) -> [begin - cmp(round(Axis*-1.0)+Diff+I*Incr,Axis*-1.0+I*Incr), - cmp(Axis*-1.0+I*Incr,round(Axis*-1.0)-Diff+I*Incr) + cmp(round(Axis*-1.0)+Diff+I*Incr,Axis*-1.0+I*Incr), + cmp(Axis*-1.0+I*Incr,round(Axis*-1.0)-Diff+I*Incr) end || I <- lists:seq((Length div 2)*-1,(Length div 2))], [begin - cmp(round(Axis)+Diff+I*Incr,Axis+I*Incr), - cmp(Axis+I*Incr,round(Axis)-Diff+I*Incr) + cmp(round(Axis)+Diff+I*Incr,Axis+I*Incr), + cmp(Axis+I*Incr,round(Axis)-Diff+I*Incr) end || I <- lists:seq((Length div 2)*-1,(Length div 2))]. cmp(Big,Small) when is_float(Big) -> BigGtSmall = lists:flatten( - io_lib:format("~f > ~p",[Big,Small])), + io_lib:format("~f > ~p",[Big,Small])), BigLtSmall = lists:flatten( - io_lib:format("~f < ~p",[Big,Small])), + io_lib:format("~f < ~p",[Big,Small])), BigEqSmall = lists:flatten( - io_lib:format("~f == ~p",[Big,Small])), + io_lib:format("~f == ~p",[Big,Small])), SmallGtBig = lists:flatten( - io_lib:format("~p > ~f",[Small,Big])), + io_lib:format("~p > ~f",[Small,Big])), SmallLtBig = lists:flatten( - io_lib:format("~p < ~f",[Small,Big])), + io_lib:format("~p < ~f",[Small,Big])), SmallEqBig = lists:flatten( - io_lib:format("~p == ~f",[Small,Big])), + io_lib:format("~p == ~f",[Small,Big])), cmp(Big,Small,BigGtSmall,BigLtSmall,SmallGtBig,SmallLtBig, - SmallEqBig,BigEqSmall); + SmallEqBig,BigEqSmall); cmp(Big,Small) when is_float(Small) -> BigGtSmall = lists:flatten( - io_lib:format("~p > ~f",[Big,Small])), + io_lib:format("~p > ~f",[Big,Small])), BigLtSmall = lists:flatten( - io_lib:format("~p < ~f",[Big,Small])), + io_lib:format("~p < ~f",[Big,Small])), BigEqSmall = lists:flatten( - io_lib:format("~p == ~f",[Big,Small])), + io_lib:format("~p == ~f",[Big,Small])), SmallGtBig = lists:flatten( - io_lib:format("~f > ~p",[Small,Big])), + io_lib:format("~f > ~p",[Small,Big])), SmallLtBig = lists:flatten( - io_lib:format("~f < ~p",[Small,Big])), + io_lib:format("~f < ~p",[Small,Big])), SmallEqBig = lists:flatten( - io_lib:format("~f == ~p",[Small,Big])), + io_lib:format("~f == ~p",[Small,Big])), cmp(Big,Small,BigGtSmall,BigLtSmall,SmallGtBig,SmallLtBig, - SmallEqBig,BigEqSmall). + SmallEqBig,BigEqSmall). cmp(Big,Small,BigGtSmall,BigLtSmall,SmallGtBig,SmallLtBig, SmallEqBig,BigEqSmall) -> {_,_,_,true} = {Big,Small,BigGtSmall, - Big > Small}, + Big > Small}, {_,_,_,false} = {Big,Small,BigLtSmall, - Big < Small}, + Big < Small}, {_,_,_,false} = {Big,Small,SmallGtBig, - Small > Big}, + Small > Big}, {_,_,_,true} = {Big,Small,SmallLtBig, - Small < Big}, + Small < Big}, {_,_,_,false} = {Big,Small,SmallEqBig, - Small == Big}, + Small == Big}, {_,_,_,false} = {Big,Small,BigEqSmall, - Big == Small}. + Big == Small}. id(I) -> I. - + start_node(Config) when is_list(Config) -> - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line Name = list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ atom_to_list(proplists:get_value(testcase, Config)) - ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) - ++ "-" - ++ integer_to_list(erlang:unique_integer([positive]))), - ?line test_server:start_node(Name, slave, [{args, "-pa "++Pa}]). + Pa = filename:dirname(code:which(?MODULE)), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(proplists:get_value(testcase, Config)) + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive]))), + test_server:start_node(Name, slave, [{args, "-pa "++Pa}]). stop_node(Node) -> test_server:stop_node(Node). @@ -293,8 +292,8 @@ hidden_inf(Config) when is_list(Config) -> ZeroN = id(ZeroP) * (-1), [hidden_inf_1(A, B, Z, 9.23e307) || A <- [1.0, -1.0, 3.1415, -0.00001000131, 3.57e257, ZeroP, ZeroN], - B <- [1.0, -1.0, 3.1415, -0.00001000131, 3.57e257, ZeroP, ZeroN], - Z <- [ZeroP, ZeroN]], + B <- [1.0, -1.0, 3.1415, -0.00001000131, 3.57e257, ZeroP, ZeroN], + Z <- [ZeroP, ZeroN]], ok. hidden_inf_1(A, B, Zero, Huge) -> @@ -329,17 +328,15 @@ arith(_Config) -> bignum = erts_internal:term_type(SMALL_MIN - 1), L = [0, 0.0, FloatNegZero, 1, 1.0, 17, 17.0, 0.17, - FLOAT_MIN, FLOAT_MAX, - SMALL_MAX, SMALL_MAX+1, - SMALL_MIN, SMALL_MIN-1, - BIG1_MAX, BIG1_MAX+1, - BIG2_MAX, BIG2_MAX+1, - trunc(FLOAT_MAX), trunc(FLOAT_MAX)+1, trunc(FLOAT_MAX)*2, - - immed_badarg, - "list badarg", - {"boxed badarg"} - ], + FLOAT_MIN, FLOAT_MAX, + SMALL_MAX, SMALL_MAX+1, + SMALL_MIN, SMALL_MIN-1, + BIG1_MAX, BIG1_MAX+1, + BIG2_MAX, BIG2_MAX+1, + trunc(FLOAT_MAX), trunc(FLOAT_MAX)+1, trunc(FLOAT_MAX)*2, + immed_badarg, + "list badarg", + {"boxed badarg"}], foreach_pair(fun(A,B) -> do_bin_ops(A,B) end, L). @@ -350,21 +347,21 @@ foreach_pair(F, L) -> do_bin_ops(A, B) -> Fun = fun(Op) -> - Op(A,B), - is_number(A) andalso Op(-A,B), - is_number(B) andalso Op(A,-B), - is_number(A) andalso is_number(B) andalso Op(-A,-B) - end, + Op(A,B), + is_number(A) andalso Op(-A,B), + is_number(B) andalso Op(A,-B), + is_number(A) andalso is_number(B) andalso Op(-A,-B) + end, lists:foreach(Fun, - [fun op_add/2, fun op_sub/2, fun op_mul/2, fun op_div/2]). + [fun op_add/2, fun op_sub/2, fun op_mul/2, fun op_div/2]). op_add(A, B) -> Info = [A,B], R = unify(catch A + B, Info), R = unify(my_apply(erlang,'+',[A,B]), Info), case R of - _ when A + B =:= element(1,R) -> ok; - {{'EXIT',badarith}, Info} -> ok + _ when A + B =:= element(1,R) -> ok; + {{'EXIT',badarith}, Info} -> ok end. op_sub(A, B) -> @@ -372,8 +369,8 @@ op_sub(A, B) -> R = unify(catch A - B, Info), R = unify(my_apply(erlang,'-',[A,B]), Info), case R of - _ when A - B =:= element(1,R) -> ok; - {{'EXIT',badarith}, Info} -> ok + _ when A - B =:= element(1,R) -> ok; + {{'EXIT',badarith}, Info} -> ok end. op_mul(A, B) -> @@ -381,8 +378,8 @@ op_mul(A, B) -> R = unify(catch A * B, Info), R = unify(my_apply(erlang,'*',[A,B]), Info), case R of - _ when A * B =:= element(1,R) -> ok; - {{'EXIT',badarith}, Info} -> ok + _ when A * B =:= element(1,R) -> ok; + {{'EXIT',badarith}, Info} -> ok end. op_div(A, B) -> @@ -390,8 +387,8 @@ op_div(A, B) -> R = unify(catch A / B, Info), R = unify(my_apply(erlang,'/',[A,B]), Info), case R of - _ when A / B =:= element(1,R) -> ok; - {{'EXIT',badarith}, Info} -> ok + _ when A / B =:= element(1,R) -> ok; + {{'EXIT',badarith}, Info} -> ok end. my_apply(M, F, A) -> diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index db14ffc005..044c6aabca 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -47,16 +47,16 @@ all() -> %% Test that the correct EXIT code is returned for all types of bad funs. bad_apply(Config) when is_list(Config) -> - ?line bad_apply_fc(42, [0]), - ?line bad_apply_fc(xx, [1]), - ?line bad_apply_fc({}, [2]), - ?line bad_apply_fc({1}, [3]), - ?line bad_apply_fc({1,2,3}, [4]), - ?line bad_apply_fc({1,2,3}, [5]), - ?line bad_apply_fc({1,2,3,4}, [6]), - ?line bad_apply_fc({1,2,3,4,5,6}, [7]), - ?line bad_apply_fc({1,2,3,4,5}, [8]), - ?line bad_apply_badarg({1,2}, [9]), + bad_apply_fc(42, [0]), + bad_apply_fc(xx, [1]), + bad_apply_fc({}, [2]), + bad_apply_fc({1}, [3]), + bad_apply_fc({1,2,3}, [4]), + bad_apply_fc({1,2,3}, [5]), + bad_apply_fc({1,2,3,4}, [6]), + bad_apply_fc({1,2,3,4,5,6}, [7]), + bad_apply_fc({1,2,3,4,5}, [8]), + bad_apply_badarg({1,2}, [9]), ok. bad_apply_fc(Fun, Args) -> @@ -85,16 +85,16 @@ bad_apply_badarg(Fun, Args) -> %% Try directly calling bad funs. bad_fun_call(Config) when is_list(Config) -> - ?line bad_call_fc(42), - ?line bad_call_fc(xx), - ?line bad_call_fc({}), - ?line bad_call_fc({1}), - ?line bad_call_fc({1,2,3}), - ?line bad_call_fc({1,2,3}), - ?line bad_call_fc({1,2,3,4}), - ?line bad_call_fc({1,2,3,4,5,6}), - ?line bad_call_fc({1,2,3,4,5}), - ?line bad_call_fc({1,2}), + bad_call_fc(42), + bad_call_fc(xx), + bad_call_fc({}), + bad_call_fc({1}), + bad_call_fc({1,2,3}), + bad_call_fc({1,2,3}), + bad_call_fc({1,2,3,4}), + bad_call_fc({1,2,3,4,5,6}), + bad_call_fc({1,2,3,4,5}), + bad_call_fc({1,2}), ok. bad_call_fc(Fun) -> @@ -111,68 +111,68 @@ bad_call_fc(Fun) -> %% Call and apply valid funs with wrong number of arguments. badarity(Config) when is_list(Config) -> - ?line Fun = fun() -> ok end, - ?line Stupid = {stupid,arguments}, - ?line Args = [some,{stupid,arguments},here], + Fun = fun() -> ok end, + Stupid = {stupid,arguments}, + Args = [some,{stupid,arguments},here], %% Simple call. - ?line Res = (catch Fun(some, Stupid, here)), + Res = (catch Fun(some, Stupid, here)), erlang:garbage_collect(), erlang:yield(), case Res of {'EXIT',{{badarity,{Fun,Args}},_}} -> - ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]); + ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]); _ -> - ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]), - ?line ct:fail({bad_result,Res}) + ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]), + ct:fail({bad_result,Res}) end, %% Apply. - ?line Res2 = (catch apply(Fun, Args)), + Res2 = (catch apply(Fun, Args)), erlang:garbage_collect(), erlang:yield(), case Res2 of {'EXIT',{{badarity,{Fun,Args}},_}} -> - ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]); + ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]); _ -> - ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]), - ?line ct:fail({bad_result,Res2}) + ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]), + ct:fail({bad_result,Res2}) end, ok. %% Call and apply valid external funs with wrong number of arguments. ext_badarity(Config) when is_list(Config) -> - ?line Fun = fun ?MODULE:nothing/0, - ?line Stupid = {stupid,arguments}, - ?line Args = [some,{stupid,arguments},here], + Fun = fun ?MODULE:nothing/0, + Stupid = {stupid,arguments}, + Args = [some,{stupid,arguments},here], %% Simple call. - ?line Res = (catch Fun(some, Stupid, here)), + Res = (catch Fun(some, Stupid, here)), erlang:garbage_collect(), erlang:yield(), case Res of {'EXIT',{{badarity,{Fun,Args}},_}} -> - ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]); + ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]); _ -> - ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]), - ?line ct:fail({bad_result,Res}) + ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]), + ct:fail({bad_result,Res}) end, %% Apply. - ?line Res2 = (catch apply(Fun, Args)), + Res2 = (catch apply(Fun, Args)), erlang:garbage_collect(), erlang:yield(), case Res2 of {'EXIT',{{badarity,{Fun,Args}},_}} -> - ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]); + ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]); _ -> - ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]), - ?line ct:fail({bad_result,Res2}) + ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]), + ct:fail({bad_result,Res2}) end, ok. @@ -184,29 +184,29 @@ nothing() -> equality(Config) when is_list(Config) -> F0 = fun() -> 1 end, F0_copy = copy_term(F0), - ?line true = eq(F0, F0), - ?line true = eq(F0, F0_copy), + true = eq(F0, F0), + true = eq(F0, F0_copy), %% Compare different arities. F1 = fun(X) -> X + 1 end, - ?line true = eq(F1, F1), - ?line false = eq(F0, F1), - ?line false = eq(F0_copy, F1), + true = eq(F1, F1), + false = eq(F0, F1), + false = eq(F0_copy, F1), %% Compare different environments. G1 = make_fun(1), G2 = make_fun(2), - ?line true = eq(G1, G1), - ?line true = eq(G2, G2), - ?line false = eq(G1, G2), - ?line false = eq(G2, G1), + true = eq(G1, G1), + true = eq(G2, G2), + false = eq(G1, G2), + false = eq(G2, G1), G1_copy = copy_term(G1), - ?line true = eq(G1, G1_copy), + true = eq(G1, G1_copy), %% Compare fun with binaries. B = list_to_binary([7,8,9]), - ?line false = eq(B, G1), - ?line false = eq(G1, B), + false = eq(B, G1), + false = eq(G1, B), %% Compare external funs. FF0 = fun aa:blurf/0, @@ -216,23 +216,23 @@ equality(Config) when is_list(Config) -> FF3 = fun erlang:exit/2, FF4 = fun z:ff/0, - ?line true = eq(FF0, FF0), - ?line true = eq(FF0, FF0_copy), - ?line true = eq(FF1, FF1), - ?line true = eq(FF2, FF2), - ?line true = eq(FF3, FF3), - ?line true = eq(FF4, FF4), - ?line false = eq(FF0, FF1), - ?line false = eq(FF0, FF2), - ?line false = eq(FF0, FF3), - ?line false = eq(FF0, FF4), - ?line false = eq(FF1, FF0), - ?line false = eq(FF1, FF2), - ?line false = eq(FF1, FF3), - ?line false = eq(FF1, FF4), - ?line false = eq(FF2, FF3), - ?line false = eq(FF2, FF4), - ?line false = eq(FF3, FF4), + true = eq(FF0, FF0), + true = eq(FF0, FF0_copy), + true = eq(FF1, FF1), + true = eq(FF2, FF2), + true = eq(FF3, FF3), + true = eq(FF4, FF4), + false = eq(FF0, FF1), + false = eq(FF0, FF2), + false = eq(FF0, FF3), + false = eq(FF0, FF4), + false = eq(FF1, FF0), + false = eq(FF1, FF2), + false = eq(FF1, FF3), + false = eq(FF1, FF4), + false = eq(FF2, FF3), + false = eq(FF2, FF4), + false = eq(FF3, FF4), %% EEP37 H1 = fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end, @@ -268,115 +268,115 @@ ordering(Config) when is_list(Config) -> FF3 = fun erlang:exit/2, FF4 = fun z:ff/0, - ?line true = FF0 < FF1, - ?line true = FF1 < FF2, - ?line true = FF2 < FF3, - ?line true = FF3 < FF4, + true = FF0 < FF1, + true = FF1 < FF2, + true = FF2 < FF3, + true = FF3 < FF4, - ?line true = FF0 > F1, - ?line true = FF0 > F2, - ?line true = FF0 > F3, - ?line true = FF4 > F1, - ?line true = FF4 > F2, - ?line true = FF4 > F3, + true = FF0 > F1, + true = FF0 > F2, + true = FF0 > F3, + true = FF4 > F1, + true = FF4 > F2, + true = FF4 > F3, - ?line true = F1 == F1, - ?line true = F1 == F1_copy, - ?line true = F1 /= F2, + true = F1 == F1, + true = F1 == F1_copy, + true = F1 /= F2, - ?line true = F1 < F2, - ?line true = F2 > F1, - ?line true = F2 < F3, - ?line true = F3 > F2, + true = F1 < F2, + true = F2 > F1, + true = F2 < F3, + true = F3 > F2, - ?line false = F1 > F2, - ?line false = F2 > F3, + false = F1 > F2, + false = F2 > F3, %% Compare with binaries. B = list_to_binary([7,8,9,10]), - ?line false = B == F1, - ?line false = F1 == B, + false = B == F1, + false = F1 == B, - ?line true = F1 < B, - ?line true = B > F2, + true = F1 < B, + true = B > F2, - ?line false = F1 > B, - ?line false = B < F2, + false = F1 > B, + false = B < F2, - ?line false = F1 >= B, - ?line false = B =< F2, + false = F1 >= B, + false = B =< F2, %% Compare module funs with binaries. - ?line false = B == FF1, - ?line false = FF1 == B, + false = B == FF1, + false = FF1 == B, - ?line true = FF1 < B, - ?line true = B > FF2, + true = FF1 < B, + true = B > FF2, - ?line false = FF1 > B, - ?line false = B < FF2, + false = FF1 > B, + false = B < FF2, - ?line false = FF1 >= B, - ?line false = B =< FF2, + false = FF1 >= B, + false = B =< FF2, %% Create a port and ref. - ?line Path = proplists:get_value(priv_dir, Config), - ?line AFile = filename:join(Path, "vanilla_file"), - ?line P = open_port(AFile, [out]), - ?line R = make_ref(), + Path = proplists:get_value(priv_dir, Config), + AFile = filename:join(Path, "vanilla_file"), + P = open_port(AFile, [out]), + R = make_ref(), %% Compare funs with ports and refs. - ?line true = R < F3, - ?line true = F3 > R, - ?line true = F3 < P, - ?line true = P > F3, + true = R < F3, + true = F3 > R, + true = F3 < P, + true = P > F3, - ?line true = R =< F3, - ?line true = F3 >= R, - ?line true = F3 =< P, - ?line true = P >= F3, + true = R =< F3, + true = F3 >= R, + true = F3 =< P, + true = P >= F3, - ?line false = R > F3, - ?line false = F3 < R, - ?line false = F3 > P, - ?line false = P < F3, + false = R > F3, + false = F3 < R, + false = F3 > P, + false = P < F3, %% Compare funs with conses and nils. - ?line true = F1 < [a], - ?line true = F1 < [], - ?line true = [a,b] > F1, - ?line true = [] > F1, + true = F1 < [a], + true = F1 < [], + true = [a,b] > F1, + true = [] > F1, - ?line false = [1] < F1, - ?line false = [] < F1, - ?line false = F1 > [2], - ?line false = F1 > [], + false = [1] < F1, + false = [] < F1, + false = F1 > [2], + false = F1 > [], - ?line false = [1] =< F1, - ?line false = [] =< F1, - ?line false = F1 >= [2], - ?line false = F1 >= [], + false = [1] =< F1, + false = [] =< F1, + false = F1 >= [2], + false = F1 >= [], %% Compare module funs with conses and nils. - ?line true = FF1 < [a], - ?line true = FF1 < [], - ?line true = [a,b] > FF1, - ?line true = [] > FF1, + true = FF1 < [a], + true = FF1 < [], + true = [a,b] > FF1, + true = [] > FF1, - ?line false = [1] < FF1, - ?line false = [] < FF1, - ?line false = FF1 > [2], - ?line false = FF1 > [], + false = [1] < FF1, + false = [] < FF1, + false = FF1 > [2], + false = FF1 > [], - ?line false = [1] =< FF1, - ?line false = [] =< FF1, - ?line false = FF1 >= [2], - ?line false = FF1 >= [], + false = [1] =< FF1, + false = [] =< FF1, + false = FF1 >= [2], + false = FF1 >= [], ok. make_fun(X, Y) -> @@ -384,14 +384,14 @@ make_fun(X, Y) -> %% Try sending funs to ports (should fail). fun_to_port(Config) when is_list(Config) -> - ?line fun_to_port(Config, xxx), - ?line fun_to_port(Config, fun() -> 42 end), - ?line fun_to_port(Config, [fun() -> 43 end]), - ?line fun_to_port(Config, [1,fun() -> 44 end]), - ?line fun_to_port(Config, [0,1|fun() -> 45 end]), + fun_to_port(Config, xxx), + fun_to_port(Config, fun() -> 42 end), + fun_to_port(Config, [fun() -> 43 end]), + fun_to_port(Config, [1,fun() -> 44 end]), + fun_to_port(Config, [0,1|fun() -> 45 end]), B64K = build_io_list(65536), - ?line fun_to_port(Config, [B64K,fun() -> 45 end]), - ?line fun_to_port(Config, [B64K|fun() -> 45 end]), + fun_to_port(Config, [B64K,fun() -> 45 end]), + fun_to_port(Config, [B64K|fun() -> 45 end]), ok. fun_to_port(Config, IoList) -> @@ -416,24 +416,24 @@ build_io_list(N) -> t_hash(Config) when is_list(Config) -> F1 = fun(_X) -> 1 end, F2 = fun(_X) -> 2 end, - ?line true = hash(F1) /= hash(F2), + true = hash(F1) /= hash(F2), G1 = make_fun(1, 2, 3), G2 = make_fun(1, 2, 3), G3 = make_fun(1, 2, 4), - ?line true = hash(G1) == hash(G2), - ?line true = hash(G2) /= hash(G3), + true = hash(G1) == hash(G2), + true = hash(G2) /= hash(G3), FF0 = fun erlang:abs/1, FF1 = fun erlang:exit/1, FF2 = fun erlang:exit/2, FF3 = fun blurf:exit/2, - ?line true = hash(FF0) =/= hash(FF1), - ?line true = hash(FF0) =/= hash(FF2), - ?line true = hash(FF0) =/= hash(FF3), - ?line true = hash(FF1) =/= hash(FF2), - ?line true = hash(FF1) =/= hash(FF3), - ?line true = hash(FF2) =/= hash(FF3), + true = hash(FF0) =/= hash(FF1), + true = hash(FF0) =/= hash(FF2), + true = hash(FF0) =/= hash(FF3), + true = hash(FF1) =/= hash(FF2), + true = hash(FF1) =/= hash(FF3), + true = hash(FF2) =/= hash(FF3), ok. hash(Term) -> @@ -443,24 +443,24 @@ hash(Term) -> t_phash(Config) when is_list(Config) -> F1 = fun(_X) -> 1 end, F2 = fun(_X) -> 2 end, - ?line true = phash(F1) /= phash(F2), + true = phash(F1) /= phash(F2), G1 = make_fun(1, 2, 3), G2 = make_fun(1, 2, 3), G3 = make_fun(1, 2, 4), - ?line true = phash(G1) == phash(G2), - ?line true = phash(G2) /= phash(G3), + true = phash(G1) == phash(G2), + true = phash(G2) /= phash(G3), FF0 = fun erlang:abs/1, FF1 = fun erlang:exit/1, FF2 = fun erlang:exit/2, FF3 = fun blurf:exit/2, - ?line true = phash(FF0) =/= phash(FF1), - ?line true = phash(FF0) =/= phash(FF2), - ?line true = phash(FF0) =/= phash(FF3), - ?line true = phash(FF1) =/= phash(FF2), - ?line true = phash(FF1) =/= phash(FF3), - ?line true = phash(FF2) =/= phash(FF3), + true = phash(FF0) =/= phash(FF1), + true = phash(FF0) =/= phash(FF2), + true = phash(FF0) =/= phash(FF3), + true = phash(FF1) =/= phash(FF2), + true = phash(FF1) =/= phash(FF3), + true = phash(FF2) =/= phash(FF3), ok. @@ -471,24 +471,24 @@ phash(Term) -> t_phash2(Config) when is_list(Config) -> F1 = fun(_X) -> 1 end, F2 = fun(_X) -> 2 end, - ?line true = phash2(F1) /= phash2(F2), + true = phash2(F1) /= phash2(F2), G1 = make_fun(1, 2, 3), G2 = make_fun(1, 2, 3), G3 = make_fun(1, 2, 4), - ?line true = phash2(G1) == phash2(G2), - ?line true = phash2(G2) /= phash2(G3), + true = phash2(G1) == phash2(G2), + true = phash2(G2) /= phash2(G3), FF0 = fun erlang:abs/1, FF1 = fun erlang:exit/1, FF2 = fun erlang:exit/2, FF3 = fun blurf:exit/2, - ?line true = phash2(FF0) =/= phash2(FF1), - ?line true = phash2(FF0) =/= phash2(FF2), - ?line true = phash2(FF0) =/= phash2(FF3), - ?line true = phash2(FF1) =/= phash2(FF2), - ?line true = phash2(FF1) =/= phash2(FF3), - ?line true = phash2(FF2) =/= phash2(FF3), + true = phash2(FF0) =/= phash2(FF1), + true = phash2(FF0) =/= phash2(FF2), + true = phash2(FF0) =/= phash2(FF3), + true = phash2(FF1) =/= phash2(FF2), + true = phash2(FF1) =/= phash2(FF3), + true = phash2(FF2) =/= phash2(FF3), ok. @@ -503,46 +503,46 @@ md5(Config) when is_list(Config) -> _ = size(erlang:md5_init()), %% Try funs in the i/o list. - ?line bad_md5(fun(_X) -> 42 end), - ?line bad_md5([fun(_X) -> 43 end]), - ?line bad_md5([1,fun(_X) -> 44 end]), - ?line bad_md5([1|fun(_X) -> 45 end]), - ?line B64K = build_io_list(65536), - ?line bad_md5([B64K,fun(_X) -> 46 end]), - ?line bad_md5([B64K|fun(_X) -> 46 end]), + bad_md5(fun(_X) -> 42 end), + bad_md5([fun(_X) -> 43 end]), + bad_md5([1,fun(_X) -> 44 end]), + bad_md5([1|fun(_X) -> 45 end]), + B64K = build_io_list(65536), + bad_md5([B64K,fun(_X) -> 46 end]), + bad_md5([B64K|fun(_X) -> 46 end]), ok. bad_md5(Bad) -> {'EXIT',{badarg,_}} = (catch erlang:md5(Bad)). refc(Config) when is_list(Config) -> - ?line F1 = fun_factory(2), - ?line {refc,2} = erlang:fun_info(F1, refc), - ?line F2 = fun_factory(42), - ?line {refc,3} = erlang:fun_info(F1, refc), + F1 = fun_factory(2), + {refc,2} = erlang:fun_info(F1, refc), + F2 = fun_factory(42), + {refc,3} = erlang:fun_info(F1, refc), - ?line process_flag(trap_exit, true), - ?line Pid = spawn_link(fun() -> {refc,4} = erlang:fun_info(F1, refc) end), + process_flag(trap_exit, true), + Pid = spawn_link(fun() -> {refc,4} = erlang:fun_info(F1, refc) end), receive {'EXIT',Pid,normal} -> ok; - Other -> ?line ct:fail({unexpected,Other}) + Other -> ct:fail({unexpected,Other}) end, - ?line process_flag(trap_exit, false), - ?line {refc,3} = erlang:fun_info(F1, refc), + process_flag(trap_exit, false), + {refc,3} = erlang:fun_info(F1, refc), %% Garbage collect. Only the F2 fun will be left. - ?line 7 = F1(5), - ?line true = erlang:garbage_collect(), - ?line 40 = F2(-2), - ?line {refc,2} = erlang:fun_info(F2, refc), + 7 = F1(5), + true = erlang:garbage_collect(), + 40 = F2(-2), + {refc,2} = erlang:fun_info(F2, refc), ok. fun_factory(Const) -> fun(X) -> X + Const end. refc_ets(Config) when is_list(Config) -> - ?line F = fun(X) -> X + 33 end, - ?line {refc,2} = erlang:fun_info(F, refc), + F = fun(X) -> X + 33 end, + {refc,2} = erlang:fun_info(F, refc), refc_ets_set(F, [set]), refc_ets_set(F, [ordered_set]), @@ -551,115 +551,112 @@ refc_ets(Config) when is_list(Config) -> ok. refc_ets_set(F1, Options) -> - ?line io:format("~p", [Options]), - ?line Tab = ets:new(kalle, Options), - ?line true = ets:insert(Tab, {a_key,F1}), - ?line 3 = fun_refc(F1), - ?line [{a_key,F3}] = ets:lookup(Tab, a_key), - ?line 4 = fun_refc(F1), - ?line true = ets:insert(Tab, {a_key,not_a_fun}), - ?line 3 = fun_refc(F1), - ?line true = ets:insert(Tab, {another_key,F1}), - ?line 4 = fun_refc(F1), - ?line true = ets:delete(Tab), - ?line 3 = fun_refc(F1), - ?line 10 = F3(-23), - ?line true = erlang:garbage_collect(), - ?line 2 = fun_refc(F1), + io:format("~p", [Options]), + Tab = ets:new(kalle, Options), + true = ets:insert(Tab, {a_key,F1}), + 3 = fun_refc(F1), + [{a_key,F3}] = ets:lookup(Tab, a_key), + 4 = fun_refc(F1), + true = ets:insert(Tab, {a_key,not_a_fun}), + 3 = fun_refc(F1), + true = ets:insert(Tab, {another_key,F1}), + 4 = fun_refc(F1), + true = ets:delete(Tab), + 3 = fun_refc(F1), + 10 = F3(-23), + true = erlang:garbage_collect(), + 2 = fun_refc(F1), ok. refc_ets_bag(F1, Options) -> - ?line io:format("~p", [Options]), - ?line Tab = ets:new(kalle, Options), - ?line true = ets:insert(Tab, {a_key,F1}), - ?line 3 = fun_refc(F1), - ?line [{a_key,F3}] = ets:lookup(Tab, a_key), - ?line 4 = fun_refc(F1), - ?line true = ets:insert(Tab, {a_key,not_a_fun}), - ?line 4 = fun_refc(F1), - ?line true = ets:insert(Tab, {another_key,F1}), - ?line 5 = fun_refc(F1), - ?line true = ets:delete(Tab), - ?line 3 = fun_refc(F1), - ?line 10 = F3(-23), - ?line true = erlang:garbage_collect(), - ?line 2 = fun_refc(F1), + io:format("~p", [Options]), + Tab = ets:new(kalle, Options), + true = ets:insert(Tab, {a_key,F1}), + 3 = fun_refc(F1), + [{a_key,F3}] = ets:lookup(Tab, a_key), + 4 = fun_refc(F1), + true = ets:insert(Tab, {a_key,not_a_fun}), + 4 = fun_refc(F1), + true = ets:insert(Tab, {another_key,F1}), + 5 = fun_refc(F1), + true = ets:delete(Tab), + 3 = fun_refc(F1), + 10 = F3(-23), + true = erlang:garbage_collect(), + 2 = fun_refc(F1), ok. refc_dist(Config) when is_list(Config) -> - ?line {ok,Node} = start_node(fun_SUITE_refc_dist), - ?line process_flag(trap_exit, true), - ?line Pid = spawn_link(Node, - fun() -> receive - Fun when is_function(Fun) -> - 2 = fun_refc(Fun), - exit({normal,Fun}) end - end), - ?line F = fun() -> 42 end, - ?line 2 = fun_refc(F), - ?line Pid ! F, + {ok,Node} = start_node(fun_SUITE_refc_dist), + process_flag(trap_exit, true), + Pid = spawn_link(Node, fun() -> receive + Fun when is_function(Fun) -> + 2 = fun_refc(Fun), + exit({normal,Fun}) end + end), + F = fun() -> 42 end, + 2 = fun_refc(F), + Pid ! F, F2 = receive {'EXIT',Pid,{normal,Fun}} -> Fun; - Other -> ?line ct:fail({unexpected,Other}) + Other -> ct:fail({unexpected,Other}) end, %% dist.c:net_mess2 have a reference to Fun for a while since %% Fun is passed in an exit signal. Wait until it is gone. - ?line wait_until(fun () -> 4 =/= fun_refc(F2) end), - ?line 3 = fun_refc(F2), - ?line true = erlang:garbage_collect(), - ?line 2 = fun_refc(F), + wait_until(fun () -> 4 =/= fun_refc(F2) end), + 3 = fun_refc(F2), + true = erlang:garbage_collect(), + 2 = fun_refc(F), refc_dist_send(Node, F). refc_dist_send(Node, F) -> - ?line Pid = spawn_link(Node, - fun() -> receive - {To,Fun} when is_function(Fun) -> - wait_until(fun () -> - 2 =:= fun_refc(Fun) - end), - To ! Fun - end - end), - ?line 2 = fun_refc(F), + Pid = spawn_link(Node, fun() -> receive + {To,Fun} when is_function(Fun) -> + wait_until(fun () -> + 2 =:= fun_refc(Fun) + end), + To ! Fun + end + end), + 2 = fun_refc(F), Pid ! {self(),F}, F2 = receive Fun when is_function(Fun) -> Fun; - Other -> ?line ct:fail({unexpected,Other}) + Other -> ct:fail({unexpected,Other}) end, receive {'EXIT',Pid,normal} -> ok end, %% No reference from dist.c:net_mess2 since Fun is passed %% in an ordinary message. - ?line 3 = fun_refc(F), - ?line 3 = fun_refc(F2), + 3 = fun_refc(F), + 3 = fun_refc(F2), refc_dist_reg_send(Node, F). refc_dist_reg_send(Node, F) -> - ?line true = erlang:garbage_collect(), - ?line 2 = fun_refc(F), - ?line Ref = make_ref(), - ?line Me = self(), - ?line Pid = spawn_link(Node, - fun() -> - true = register(my_fun_tester, self()), - Me ! Ref, - receive - {Me,Fun} when is_function(Fun) -> - 2 = fun_refc(Fun), - Me ! Fun - end - end), + true = erlang:garbage_collect(), + 2 = fun_refc(F), + Ref = make_ref(), + Me = self(), + Pid = spawn_link(Node, fun() -> + true = register(my_fun_tester, self()), + Me ! Ref, + receive + {Me,Fun} when is_function(Fun) -> + 2 = fun_refc(Fun), + Me ! Fun + end + end), erlang:yield(), - ?line 2 = fun_refc(F), + 2 = fun_refc(F), receive Ref -> ok end, {my_fun_tester,Node} ! {self(),F}, F2 = receive Fun when is_function(Fun) -> Fun; - Other -> ?line ct:fail({unexpected,Other}) + Other -> ct:fail({unexpected,Other}) end, receive {'EXIT',Pid,normal} -> ok end, - ?line 3 = fun_refc(F), - ?line 3 = fun_refc(F2), + 3 = fun_refc(F), + 3 = fun_refc(F2), ok. fun_refc(F) -> @@ -667,15 +664,15 @@ fun_refc(F) -> Count. const_propagation(Config) when is_list(Config) -> - ?line Fun1 = fun start_node/1, - ?line 2 = fun_refc(Fun1), - ?line Fun2 = Fun1, - ?line my_cmp({Fun1,Fun2}), - - ?line Fun3 = fun() -> ok end, - ?line 2 = fun_refc(Fun3), - ?line Fun4 = Fun3, - ?line my_cmp({Fun3,Fun4}), + Fun1 = fun start_node/1, + 2 = fun_refc(Fun1), + Fun2 = Fun1, + my_cmp({Fun1,Fun2}), + + Fun3 = fun() -> ok end, + 2 = fun_refc(Fun3), + Fun4 = Fun3, + my_cmp({Fun3,Fun4}), ok. my_cmp({Fun,Fun}) -> ok; @@ -685,49 +682,49 @@ my_cmp({Fun1,Fun2}) -> ct:fail(no_match). t_arity(Config) when is_list(Config) -> - ?line 0 = fun_arity(fun() -> ok end), - ?line 0 = fun_arity(fun() -> Config end), - ?line 1 = fun_arity(fun(X) -> X+1 end), - ?line 1 = fun_arity(fun(X) -> Config =:= X end), + 0 = fun_arity(fun() -> ok end), + 0 = fun_arity(fun() -> Config end), + 1 = fun_arity(fun(X) -> X+1 end), + 1 = fun_arity(fun(X) -> Config =:= X end), A = id(42), %% Test that the arity is transferred properly. - ?line process_flag(trap_exit, true), - ?line {ok,Node} = start_node(fun_test_arity), - ?line hello_world = spawn_call(Node, fun() -> hello_world end), - ?line 0 = spawn_call(Node, fun(X) -> X end), - ?line 42 = spawn_call(Node, fun(_X) -> A end), - ?line 43 = spawn_call(Node, fun(X, Y) -> A+X+Y end), - ?line 1 = spawn_call(Node, fun(X, Y) -> X+Y end), - ?line 45 = spawn_call(Node, fun(X, Y, Z) -> A+X+Y+Z end), + process_flag(trap_exit, true), + {ok,Node} = start_node(fun_test_arity), + hello_world = spawn_call(Node, fun() -> hello_world end), + 0 = spawn_call(Node, fun(X) -> X end), + 42 = spawn_call(Node, fun(_X) -> A end), + 43 = spawn_call(Node, fun(X, Y) -> A+X+Y end), + 1 = spawn_call(Node, fun(X, Y) -> X+Y end), + 45 = spawn_call(Node, fun(X, Y, Z) -> A+X+Y+Z end), ok. t_is_function2(Config) when is_list(Config) -> false = is_function(id({a,b}), 0), false = is_function(id({a,b}), 234343434333433433), - ?line true = is_function(fun() -> ok end, 0), - ?line true = is_function(fun(_) -> ok end, 1), - ?line false = is_function(fun(_) -> ok end, 0), + true = is_function(fun() -> ok end, 0), + true = is_function(fun(_) -> ok end, 1), + false = is_function(fun(_) -> ok end, 0), - ?line true = is_function(fun erlang:abs/1, 1), - ?line true = is_function(fun erlang:abs/99, 99), - ?line false = is_function(fun erlang:abs/1, 0), - ?line false = is_function(fun erlang:abs/99, 0), + true = is_function(fun erlang:abs/1, 1), + true = is_function(fun erlang:abs/99, 99), + false = is_function(fun erlang:abs/1, 0), + false = is_function(fun erlang:abs/99, 0), - ?line false = is_function(id(self()), 0), - ?line false = is_function(id({a,b,c}), 0), - ?line false = is_function(id({a}), 0), - ?line false = is_function(id([a,b,c]), 0), + false = is_function(id(self()), 0), + false = is_function(id({a,b,c}), 0), + false = is_function(id({a}), 0), + false = is_function(id([a,b,c]), 0), %% Bad arity argument. - ?line bad_arity(a), - ?line bad_arity(-1), - ?line bad_arity(-9738974938734938793873498378), - ?line bad_arity([]), - ?line bad_arity(fun() -> ok end), - ?line bad_arity({}), - ?line bad_arity({a,b}), - ?line bad_arity(self()), + bad_arity(a), + bad_arity(-1), + bad_arity(-9738974938734938793873498378), + bad_arity([]), + bad_arity(fun() -> ok end), + bad_arity({}), + bad_arity({a,b}), + bad_arity(self()), ok. bad_arity(A) -> @@ -736,57 +733,57 @@ bad_arity(A) -> ok. t_fun_info(Config) when is_list(Config) -> - ?line F = fun t_fun_info/1, - ?line try F(blurf) of + F = fun t_fun_info/1, + try F(blurf) of FAny -> ct:fail("should fail; returned ~p\n", [FAny]) catch error:function_clause -> ok end, - ?line {module,?MODULE} = erlang:fun_info(F, module), - ?line case erlang:fun_info(F, name) of + {module,?MODULE} = erlang:fun_info(F, module), + case erlang:fun_info(F, name) of undefined -> ct:fail(no_fun_info); _ -> ok end, - ?line {arity,1} = erlang:fun_info(F, arity), - ?line {env,[]} = erlang:fun_info(F, env), - ?line verify_not_undef(F, index), - ?line verify_not_undef(F, uniq), - ?line verify_not_undef(F, new_index), - ?line verify_not_undef(F, new_uniq), - ?line verify_not_undef(F, refc), - ?line {'EXIT',_} = (catch erlang:fun_info(F, blurf)), + {arity,1} = erlang:fun_info(F, arity), + {env,[]} = erlang:fun_info(F, env), + verify_not_undef(F, index), + verify_not_undef(F, uniq), + verify_not_undef(F, new_index), + verify_not_undef(F, new_uniq), + verify_not_undef(F, refc), + {'EXIT',_} = (catch erlang:fun_info(F, blurf)), %% Module fun. - ?line FF = fun ?MODULE:t_fun_info/1, - ?line try FF(blurf) of + FF = fun ?MODULE:t_fun_info/1, + try FF(blurf) of FFAny -> ct:fail("should fail; returned ~p\n", [FFAny]) catch error:function_clause -> ok end, - ?line {module,?MODULE} = erlang:fun_info(FF, module), - ?line {name,t_fun_info} = erlang:fun_info(FF, name), - ?line {arity,1} = erlang:fun_info(FF, arity), - ?line {env,[]} = erlang:fun_info(FF, env), - ?line verify_undef(FF, index), - ?line verify_undef(FF, uniq), - ?line verify_undef(FF, new_index), - ?line verify_undef(FF, new_uniq), - ?line verify_undef(FF, refc), - ?line {'EXIT',_} = (catch erlang:fun_info(FF, blurf)), + {module,?MODULE} = erlang:fun_info(FF, module), + {name,t_fun_info} = erlang:fun_info(FF, name), + {arity,1} = erlang:fun_info(FF, arity), + {env,[]} = erlang:fun_info(FF, env), + verify_undef(FF, index), + verify_undef(FF, uniq), + verify_undef(FF, new_index), + verify_undef(FF, new_uniq), + verify_undef(FF, refc), + {'EXIT',_} = (catch erlang:fun_info(FF, blurf)), %% Not fun. - ?line bad_info(abc), - ?line bad_info(42), - ?line bad_info({fun erlang:list_to_integer/1}), - ?line bad_info([42]), - ?line bad_info([]), - ?line bad_info(self()), - ?line bad_info(<<>>), - ?line bad_info(<<1,2>>), + bad_info(abc), + bad_info(42), + bad_info({fun erlang:list_to_integer/1}), + bad_info([42]), + bad_info([]), + bad_info(self()), + bad_info(<<>>), + bad_info(<<1,2>>), ok. t_fun_info_mfa(Config) when is_list(Config) -> @@ -873,6 +870,3 @@ wait_until(Fun) -> true -> ok; _ -> receive after 100 -> wait_until(Fun) end end. - -% stop_node(Node) -> -% test_server:stop_node(Node). diff --git a/erts/emulator/test/fun_r13_SUITE.erl b/erts/emulator/test/fun_r13_SUITE.erl index c8146de46a..d66026705b 100644 --- a/erts/emulator/test/fun_r13_SUITE.erl +++ b/erts/emulator/test/fun_r13_SUITE.erl @@ -22,7 +22,7 @@ -compile(r13). -export([all/0, suite/0, - dist_old_release/1]). + dist_old_release/1]). -include_lib("common_test/include/ct.hrl"). @@ -35,38 +35,38 @@ all() -> dist_old_release(Config) when is_list(Config) -> case test_server:is_release_available("r12b") of - true -> do_dist_old(Config); - false -> {skip,"No R12B found"} + true -> do_dist_old(Config); + false -> {skip,"No R12B found"} end. do_dist_old(Config) when is_list(Config) -> - ?line Pa = filename:dirname(code:which(?MODULE)), + Pa = filename:dirname(code:which(?MODULE)), Name = fun_dist_r12, - ?line {ok,Node} = test_server:start_node(Name, peer, - [{args,"-pa "++Pa}, - {erl,[{release,"r12b"}]}]), + {ok,Node} = test_server:start_node(Name, peer, + [{args,"-pa "++Pa}, + {erl,[{release,"r12b"}]}]), - ?line Pid = spawn_link(Node, - fun() -> - receive - Fun when is_function(Fun) -> - R12BFun = fun(H) -> cons(H, [b,c]) end, - Fun(Fun, R12BFun) - end - end), + Pid = spawn_link(Node, + fun() -> + receive + Fun when is_function(Fun) -> + R12BFun = fun(H) -> cons(H, [b,c]) end, + Fun(Fun, R12BFun) + end + end), Self = self(), Fun = fun(F, R12BFun) -> - {pid,Self} = erlang:fun_info(F, pid), - {module,?MODULE} = erlang:fun_info(F, module), - Self ! {ok,F,R12BFun} - end, - ?line Pid ! Fun, - ?line receive - {ok,Fun,R12BFun} -> - ?line [a,b,c] = R12BFun(a); - Other -> - ?line ct:fail({bad_message,Other}) - end, + {pid,Self} = erlang:fun_info(F, pid), + {module,?MODULE} = erlang:fun_info(F, module), + Self ! {ok,F,R12BFun} + end, + Pid ! Fun, + receive + {ok,Fun,R12BFun} -> + [a,b,c] = R12BFun(a); + Other -> + ct:fail({bad_message,Other}) + end, true = test_server:stop_node(Node), ok. diff --git a/erts/emulator/test/guard_SUITE.erl b/erts/emulator/test/guard_SUITE.erl index 77dcc0f607..6ad4456e6b 100644 --- a/erts/emulator/test/guard_SUITE.erl +++ b/erts/emulator/test/guard_SUITE.erl @@ -39,9 +39,9 @@ all() -> %% Test that a bad arithmetic operation in a guard works correctly. bad_arith(Config) when is_list(Config) -> - ?line 5 = bad_arith1(2, 3), - ?line 10 = bad_arith1(1, infinity), - ?line 10 = bad_arith1(infinity, 1), + 5 = bad_arith1(2, 3), + 10 = bad_arith1(1, infinity), + 10 = bad_arith1(infinity, 1), ok. bad_arith1(T1, T2) when T1+T2 < 10 -> @@ -51,10 +51,10 @@ bad_arith1(_, _) -> %% Test that bad arguments to element/2 are handled correctly. bad_tuple(Config) when is_list(Config) -> - ?line error = bad_tuple1(a), - ?line error = bad_tuple1({a, b}), - ?line x = bad_tuple1({x, b}), - ?line y = bad_tuple1({a, b, y}), + error = bad_tuple1(a), + error = bad_tuple1({a, b}), + x = bad_tuple1({x, b}), + y = bad_tuple1({a, b, y}), ok. bad_tuple1(T) when element(1, T) == x -> @@ -67,21 +67,21 @@ bad_tuple1(_) -> test_heap_guards(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), - ?line process_flag(trap_exit, true), - ?line Tuple = {a, tuple, is, built, here, xxx}, - ?line List = [a, list, is, built, here], + process_flag(trap_exit, true), + Tuple = {a, tuple, is, built, here, xxx}, + List = [a, list, is, built, here], - ?line 'try'(fun a_case/1, [Tuple], [Tuple]), - ?line 'try'(fun a_case/1, [List], [List, List]), - ?line 'try'(fun a_case/1, [a], [a]), + 'try'(fun a_case/1, [Tuple], [Tuple]), + 'try'(fun a_case/1, [List], [List, List]), + 'try'(fun a_case/1, [a], [a]), - ?line 'try'(fun an_if/1, [Tuple], [Tuple]), - ?line 'try'(fun an_if/1, [List], [List, List]), - ?line 'try'(fun an_if/1, [a], [a]), + 'try'(fun an_if/1, [Tuple], [Tuple]), + 'try'(fun an_if/1, [List], [List, List]), + 'try'(fun an_if/1, [a], [a]), - ?line 'try'(fun receive_test/1, [Tuple], [Tuple]), - ?line 'try'(fun receive_test/1, [List], [List, List]), - ?line 'try'(fun receive_test/1, [a], [a]), + 'try'(fun receive_test/1, [Tuple], [Tuple]), + 'try'(fun receive_test/1, [List], [List, List]), + 'try'(fun receive_test/1, [a], [a]), ok. a_case(V) -> @@ -127,7 +127,7 @@ a_receive() -> Pid = spawn_link(?MODULE, init, [Fun,Args,list_to_tuple(Filler)]), receive {'EXIT', Pid, {result, Result}} -> - ?line 'try'(Iter-1, Fun, Args, Result, [0|Filler]); + 'try'(Iter-1, Fun, Args, Result, [0|Filler]); {result, Other} -> ct:fail("Expected ~p; got ~p~n", [Result, Other]); Other -> @@ -151,11 +151,11 @@ mask_error(Else) -> %% Test the binary_part/2,3 guard BIF's extensively guard_bif_binary_part(Config) when is_list(Config) -> %% Overflow tests that need to be unoptimized - ?line badarg = + badarg = ?MASK_ERROR( binary_part(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF, -16#7FFFFFFFFFFFFFFF-1})), - ?line badarg = + badarg = ?MASK_ERROR( binary_part(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF, 16#7FFFFFFFFFFFFFFF})), @@ -180,66 +180,66 @@ guard_bif_binary_part(Config) when is_list(Config) -> do_binary_part_guard() -> - ?line 1 = bptest(<<1,2,3>>), - ?line 2 = bptest(<<2,1,3>>), - ?line error = bptest(<<1>>), - ?line error = bptest(<<>>), - ?line error = bptest(apa), - ?line 3 = bptest(<<2,3,3>>), + 1 = bptest(<<1,2,3>>), + 2 = bptest(<<2,1,3>>), + error = bptest(<<1>>), + error = bptest(<<>>), + error = bptest(apa), + 3 = bptest(<<2,3,3>>), % With one variable (pos) - ?line 1 = bptest(<<1,2,3>>,1), - ?line 2 = bptest(<<2,1,3>>,1), - ?line error = bptest(<<1>>,1), - ?line error = bptest(<<>>,1), - ?line error = bptest(apa,1), - ?line 3 = bptest(<<2,3,3>>,1), + 1 = bptest(<<1,2,3>>,1), + 2 = bptest(<<2,1,3>>,1), + error = bptest(<<1>>,1), + error = bptest(<<>>,1), + error = bptest(apa,1), + 3 = bptest(<<2,3,3>>,1), % With one variable (length) - ?line 1 = bptesty(<<1,2,3>>,1), - ?line 2 = bptesty(<<2,1,3>>,1), - ?line error = bptesty(<<1>>,1), - ?line error = bptesty(<<>>,1), - ?line error = bptesty(apa,1), - ?line 3 = bptesty(<<2,3,3>>,2), + 1 = bptesty(<<1,2,3>>,1), + 2 = bptesty(<<2,1,3>>,1), + error = bptesty(<<1>>,1), + error = bptesty(<<>>,1), + error = bptesty(apa,1), + 3 = bptesty(<<2,3,3>>,2), % With one variable (whole tuple) - ?line 1 = bptestx(<<1,2,3>>,{1,1}), - ?line 2 = bptestx(<<2,1,3>>,{1,1}), - ?line error = bptestx(<<1>>,{1,1}), - ?line error = bptestx(<<>>,{1,1}), - ?line error = bptestx(apa,{1,1}), - ?line 3 = bptestx(<<2,3,3>>,{1,2}), + 1 = bptestx(<<1,2,3>>,{1,1}), + 2 = bptestx(<<2,1,3>>,{1,1}), + error = bptestx(<<1>>,{1,1}), + error = bptestx(<<>>,{1,1}), + error = bptestx(apa,{1,1}), + 3 = bptestx(<<2,3,3>>,{1,2}), % With two variables - ?line 1 = bptest(<<1,2,3>>,1,1), - ?line 2 = bptest(<<2,1,3>>,1,1), - ?line error = bptest(<<1>>,1,1), - ?line error = bptest(<<>>,1,1), - ?line error = bptest(apa,1,1), - ?line 3 = bptest(<<2,3,3>>,1,2), + 1 = bptest(<<1,2,3>>,1,1), + 2 = bptest(<<2,1,3>>,1,1), + error = bptest(<<1>>,1,1), + error = bptest(<<>>,1,1), + error = bptest(apa,1,1), + 3 = bptest(<<2,3,3>>,1,2), % Direct (autoimported) call, these will be evaluated by the compiler... - ?line <<2>> = binary_part(<<1,2,3>>,1,1), - ?line <<1>> = binary_part(<<2,1,3>>,1,1), + <<2>> = binary_part(<<1,2,3>>,1,1), + <<1>> = binary_part(<<2,1,3>>,1,1), % Compiler warnings due to constant evaluation expected (3) - ?line badarg = ?MASK_ERROR(binary_part(<<1>>,1,1)), - ?line badarg = ?MASK_ERROR(binary_part(<<>>,1,1)), - ?line badarg = ?MASK_ERROR(binary_part(apa,1,1)), - ?line <<3,3>> = binary_part(<<2,3,3>>,1,2), + badarg = ?MASK_ERROR(binary_part(<<1>>,1,1)), + badarg = ?MASK_ERROR(binary_part(<<>>,1,1)), + badarg = ?MASK_ERROR(binary_part(apa,1,1)), + <<3,3>> = binary_part(<<2,3,3>>,1,2), % Direct call through apply - ?line <<2>> = apply(erlang,binary_part,[<<1,2,3>>,1,1]), - ?line <<1>> = apply(erlang,binary_part,[<<2,1,3>>,1,1]), + <<2>> = apply(erlang,binary_part,[<<1,2,3>>,1,1]), + <<1>> = apply(erlang,binary_part,[<<2,1,3>>,1,1]), % Compiler warnings due to constant evaluation expected (3) - ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<1>>,1,1])), - ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<>>,1,1])), - ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[apa,1,1])), - ?line <<3,3>> = apply(erlang,binary_part,[<<2,3,3>>,1,2]), + badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<1>>,1,1])), + badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<>>,1,1])), + badarg = ?MASK_ERROR(apply(erlang,binary_part,[apa,1,1])), + <<3,3>> = apply(erlang,binary_part,[<<2,3,3>>,1,2]), % Constant propagation - ?line Bin = <<1,2,3>>, - ?line ok = if + Bin = <<1,2,3>>, + ok = if binary_part(Bin,1,1) =:= <<2>> -> ok; %% Compiler warning, clause cannot match (expected) true -> error end, - ?line ok = if + ok = if binary_part(Bin,{1,1}) =:= <<2>> -> ok; %% Compiler warning, clause cannot match (expected) @@ -307,89 +307,89 @@ bptest(_,_,_) -> %% Test all guard bifs with nasty (but legal arguments). guard_bifs(Config) when is_list(Config) -> - ?line Big = -237849247829874297658726487367328971246284736473821617265433, - ?line Float = 387924.874, + Big = -237849247829874297658726487367328971246284736473821617265433, + Float = 387924.874, %% Succeding use of guard bifs. - ?line try_gbif('abs/1', Big, -Big), - ?line try_gbif('float/1', Big, float(Big)), - ?line try_gbif('float/1', Big, float(id(Big))), - ?line try_gbif('trunc/1', Float, 387924.0), - ?line try_gbif('round/1', Float, 387925.0), - ?line try_gbif('length/1', [], 0), + try_gbif('abs/1', Big, -Big), + try_gbif('float/1', Big, float(Big)), + try_gbif('float/1', Big, float(id(Big))), + try_gbif('trunc/1', Float, 387924.0), + try_gbif('round/1', Float, 387925.0), + try_gbif('length/1', [], 0), - ?line try_gbif('length/1', [a], 1), - ?line try_gbif('length/1', [a, b], 2), - ?line try_gbif('length/1', lists:seq(0, 31), 32), + try_gbif('length/1', [a], 1), + try_gbif('length/1', [a, b], 2), + try_gbif('length/1', lists:seq(0, 31), 32), - ?line try_gbif('hd/1', [a], a), - ?line try_gbif('hd/1', [a, b], a), + try_gbif('hd/1', [a], a), + try_gbif('hd/1', [a, b], a), - ?line try_gbif('tl/1', [a], []), - ?line try_gbif('tl/1', [a, b], [b]), - ?line try_gbif('tl/1', [a, b, c], [b, c]), + try_gbif('tl/1', [a], []), + try_gbif('tl/1', [a, b], [b]), + try_gbif('tl/1', [a, b, c], [b, c]), - ?line try_gbif('size/1', {}, 0), - ?line try_gbif('size/1', {a}, 1), - ?line try_gbif('size/1', {a, b}, 2), - ?line try_gbif('size/1', {a, b, c}, 3), - ?line try_gbif('size/1', list_to_binary([]), 0), - ?line try_gbif('size/1', list_to_binary([1]), 1), - ?line try_gbif('size/1', list_to_binary([1, 2]), 2), - ?line try_gbif('size/1', list_to_binary([1, 2, 3]), 3), + try_gbif('size/1', {}, 0), + try_gbif('size/1', {a}, 1), + try_gbif('size/1', {a, b}, 2), + try_gbif('size/1', {a, b, c}, 3), + try_gbif('size/1', list_to_binary([]), 0), + try_gbif('size/1', list_to_binary([1]), 1), + try_gbif('size/1', list_to_binary([1, 2]), 2), + try_gbif('size/1', list_to_binary([1, 2, 3]), 3), - ?line try_gbif('bit_size/1', <<0:7>>, 7), + try_gbif('bit_size/1', <<0:7>>, 7), - ?line try_gbif('element/2', {x}, {1, x}), - ?line try_gbif('element/2', {x, y}, {1, x}), - ?line try_gbif('element/2', {x, y}, {2, y}), + try_gbif('element/2', {x}, {1, x}), + try_gbif('element/2', {x, y}, {1, x}), + try_gbif('element/2', {x, y}, {2, y}), - ?line try_gbif('self/0', 0, self()), - ?line try_gbif('node/0', 0, node()), - ?line try_gbif('node/1', self(), node()), + try_gbif('self/0', 0, self()), + try_gbif('node/0', 0, node()), + try_gbif('node/1', self(), node()), %% Failing use of guard bifs. - ?line try_fail_gbif('abs/1', Big, 1), - ?line try_fail_gbif('abs/1', [], 1), + try_fail_gbif('abs/1', Big, 1), + try_fail_gbif('abs/1', [], 1), - ?line try_fail_gbif('float/1', Big, 42), - ?line try_fail_gbif('float/1', [], 42), + try_fail_gbif('float/1', Big, 42), + try_fail_gbif('float/1', [], 42), - ?line try_fail_gbif('trunc/1', Float, 0.0), - ?line try_fail_gbif('trunc/1', [], 0.0), + try_fail_gbif('trunc/1', Float, 0.0), + try_fail_gbif('trunc/1', [], 0.0), - ?line try_fail_gbif('round/1', Float, 1.0), - ?line try_fail_gbif('round/1', [], a), + try_fail_gbif('round/1', Float, 1.0), + try_fail_gbif('round/1', [], a), - ?line try_fail_gbif('length/1', [], 1), - ?line try_fail_gbif('length/1', [a], 0), - ?line try_fail_gbif('length/1', a, 0), - ?line try_fail_gbif('length/1', {a}, 0), + try_fail_gbif('length/1', [], 1), + try_fail_gbif('length/1', [a], 0), + try_fail_gbif('length/1', a, 0), + try_fail_gbif('length/1', {a}, 0), - ?line try_fail_gbif('hd/1', [], 0), - ?line try_fail_gbif('hd/1', [a], x), - ?line try_fail_gbif('hd/1', x, x), + try_fail_gbif('hd/1', [], 0), + try_fail_gbif('hd/1', [a], x), + try_fail_gbif('hd/1', x, x), - ?line try_fail_gbif('tl/1', [], 0), - ?line try_fail_gbif('tl/1', [a], x), - ?line try_fail_gbif('tl/1', x, x), + try_fail_gbif('tl/1', [], 0), + try_fail_gbif('tl/1', [a], x), + try_fail_gbif('tl/1', x, x), - ?line try_fail_gbif('size/1', {}, 1), - ?line try_fail_gbif('size/1', [], 0), - ?line try_fail_gbif('size/1', [a], 1), - ?line try_fail_gbif('size/1', fun() -> 1 end, 0), - ?line try_fail_gbif('size/1', fun() -> 1 end, 1), + try_fail_gbif('size/1', {}, 1), + try_fail_gbif('size/1', [], 0), + try_fail_gbif('size/1', [a], 1), + try_fail_gbif('size/1', fun() -> 1 end, 0), + try_fail_gbif('size/1', fun() -> 1 end, 1), - ?line try_fail_gbif('element/2', {}, {1, x}), - ?line try_fail_gbif('element/2', {x}, {1, y}), - ?line try_fail_gbif('element/2', [], {1, z}), + try_fail_gbif('element/2', {}, {1, x}), + try_fail_gbif('element/2', {x}, {1, y}), + try_fail_gbif('element/2', [], {1, z}), - ?line try_fail_gbif('self/0', 0, list_to_pid("<0.0.0>")), - ?line try_fail_gbif('node/0', 0, xxxx), - ?line try_fail_gbif('node/1', self(), xxx), - ?line try_fail_gbif('node/1', yyy, xxx), + try_fail_gbif('self/0', 0, list_to_pid("<0.0.0>")), + try_fail_gbif('node/0', 0, xxxx), + try_fail_gbif('node/1', self(), xxx), + try_fail_gbif('node/1', yyy, xxx), ok. try_gbif(Id, X, Y) -> @@ -437,12 +437,12 @@ guard_bif('node/1', X, Y) when node(X) == Y -> %% Test the type tests. type_tests(Config) when is_list(Config) -> - ?line Types = all_types(), - ?line Tests = type_test_desc(), - ?line put(errors, 0), - ?line put(violations, 0), - ?line type_tests(Tests, Types), - ?line case {get(errors), get(violations)} of + Types = all_types(), + Tests = type_test_desc(), + put(errors, 0), + put(violations, 0), + type_tests(Tests, Types), + case {get(errors), get(violations)} of {0, 0} -> ok; {0, N} -> diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl index 1d3d8a69de..45f0bdc4da 100644 --- a/erts/emulator/test/hibernate_SUITE.erl +++ b/erts/emulator/test/hibernate_SUITE.erl @@ -47,9 +47,9 @@ basic(Config) when is_list(Config) -> Ref = make_ref(), Info = {self(),Ref}, ExpectedHeapSz = erts_debug:size([Info]), - ?line Child = spawn_link(fun() -> basic_hibernator(Info) end), - ?line hibernate_wake_up(100, ExpectedHeapSz, Child), - ?line Child ! please_quit_now, + Child = spawn_link(fun() -> basic_hibernator(Info) end), + hibernate_wake_up(100, ExpectedHeapSz, Child), + Child ! please_quit_now, ok. hibernate_wake_up(0, _, _) -> ok; @@ -63,35 +63,35 @@ hibernate_wake_up(N, ExpectedHeapSz, Child) -> end; 1 -> ok end, - ?line Child ! {hibernate,self()}, - ?line wait_until(fun () -> + Child ! {hibernate,self()}, + wait_until(fun () -> {current_function,{erlang,hibernate,3}} == process_info(Child, current_function) end), - ?line {message_queue_len,0} = process_info(Child, message_queue_len), - ?line {status,waiting} = process_info(Child, status), - ?line {heap_size,ExpectedHeapSz} = process_info(Child, heap_size), + {message_queue_len,0} = process_info(Child, message_queue_len), + {status,waiting} = process_info(Child, status), + {heap_size,ExpectedHeapSz} = process_info(Child, heap_size), io:format("Before hibernation: ~p After hibernation: ~p\n", [Before,ExpectedHeapSz]), - ?line Child ! {whats_up,self()}, - ?line receive - {all_fine,X,Child,_Ref} -> - if - N =:= 1 -> io:format("~p\n", [X]); - true -> ok - end, - {backtrace,Bin} = process_info(Child, backtrace), - if - size(Bin) > 1000 -> - io:format("~s\n", [binary_to_list(Bin)]), - ?line ct:fail(stack_is_growing); - true -> - hibernate_wake_up(N-1, ExpectedHeapSz, Child) - end; - Other -> - ?line io:format("~p\n", [Other]), - ?line ct:fail(unexpected_message) - end. + Child ! {whats_up,self()}, + receive + {all_fine,X,Child,_Ref} -> + if + N =:= 1 -> io:format("~p\n", [X]); + true -> ok + end, + {backtrace,Bin} = process_info(Child, backtrace), + if + size(Bin) > 1000 -> + io:format("~s\n", [binary_to_list(Bin)]), + ct:fail(stack_is_growing); + true -> + hibernate_wake_up(N-1, ExpectedHeapSz, Child) + end; + Other -> + io:format("~p\n", [Other]), + ct:fail(unexpected_message) + end. basic_hibernator(Info) -> {catchlevel,0} = process_info(self(), catchlevel), @@ -143,9 +143,9 @@ dynamic_call(Config) when is_list(Config) -> Ref = make_ref(), Info = {self(),Ref}, ExpectedHeapSz = erts_debug:size([Info]), - ?line Child = spawn_link(fun() -> ?MODULE:dynamic_call_hibernator(Info, hibernate) end), - ?line hibernate_wake_up(100, ExpectedHeapSz, Child), - ?line Child ! please_quit_now, + Child = spawn_link(fun() -> ?MODULE:dynamic_call_hibernator(Info, hibernate) end), + hibernate_wake_up(100, ExpectedHeapSz, Child), + Child ! please_quit_now, ok. dynamic_call_hibernator(Info, Function) -> @@ -173,31 +173,30 @@ min_heap_size(Config) when is_list(Config) -> end. min_heap_size_1(Config) when is_list(Config) -> - ?line erlang:trace(new, true, [call]), + erlang:trace(new, true, [call]), MFA = {?MODULE,min_hibernator,1}, - ?line 1 = erlang:trace_pattern(MFA, true, [local]), + 1 = erlang:trace_pattern(MFA, true, [local]), Ref = make_ref(), Info = {self(),Ref}, - ?line Child = spawn_opt(fun() -> min_hibernator(Info) end, + Child = spawn_opt(fun() -> min_hibernator(Info) end, [{min_heap_size,15000},link]), receive - {trace,Child,call,{?MODULE,min_hibernator,_}} -> - ?line 1 = erlang:trace_pattern(MFA, false, [local]), - ?line erlang:trace(new, false, [call]) + {trace,Child,call,{?MODULE,min_hibernator,_}} -> + 1 = erlang:trace_pattern(MFA, false, [local]), + erlang:trace(new, false, [call]) end, {heap_size,HeapSz} = process_info(Child, heap_size), io:format("Heap size: ~p\n", [HeapSz]), - ?line if - HeapSz < 20 -> ok - end, - ?line Child ! wake_up, + if + HeapSz < 20 -> ok + end, + Child ! wake_up, receive {heap_size,AfterSize} -> io:format("Heap size after wakeup: ~p\n", [AfterSize]), - ?line - if - AfterSize >= 15000 -> ok - end; + if + AfterSize >= 15000 -> ok + end; Other -> ct:fail("Unexpected: ~p\n", [Other]) end. @@ -216,23 +215,23 @@ min_hibernator_recv(Parent) -> %%% bad_args(Config) when is_list(Config) -> - ?line bad_args(?MODULE, {name,glurf}, [0]), - ?line {'EXIT',{system_limit,_}} = + bad_args(?MODULE, {name,glurf}, [0]), + {'EXIT',{system_limit,_}} = (catch erlang:hibernate(x, y, lists:duplicate(5122, xxx))), - ?line bad_args(42, name, [0]), - ?line bad_args(xx, 42, [1]), - ?line bad_args(xx, 42, glurf), - ?line bad_args(xx, 42, {}), - ?line bad_args({}, name, [2]), - ?line bad_args({1}, name, [3]), - ?line bad_args({1,2,3}, name, [4]), - ?line bad_args({1,2,3}, name, [5]), - ?line bad_args({1,2,3,4}, name, [6]), - ?line bad_args({1,2,3,4,5,6}, name,[7]), - ?line bad_args({1,2,3,4,5}, name, [8]), - ?line bad_args({1,2}, name, [9]), - ?line bad_args([1,2], name, [9]), - ?line bad_args(55.0, name, [9]), + bad_args(42, name, [0]), + bad_args(xx, 42, [1]), + bad_args(xx, 42, glurf), + bad_args(xx, 42, {}), + bad_args({}, name, [2]), + bad_args({1}, name, [3]), + bad_args({1,2,3}, name, [4]), + bad_args({1,2,3}, name, [5]), + bad_args({1,2,3,4}, name, [6]), + bad_args({1,2,3,4,5,6}, name,[7]), + bad_args({1,2,3,4,5}, name, [8]), + bad_args({1,2}, name, [9]), + bad_args([1,2], name, [9]), + bad_args(55.0, name, [9]), ok. bad_args(Mod, Name, Args) -> @@ -260,8 +259,8 @@ messages_in_queue(Config) when is_list(Config) -> receive done -> ok; Other -> - ?line io:format("~p\n", [Other]), - ?line ct:fail(unexpected_message) + io:format("~p\n", [Other]), + ct:fail(unexpected_message) end. messages_in_queue_1(Parent, ExpectedMsg) -> @@ -273,13 +272,13 @@ messages_in_queue_1(Parent, ExpectedMsg) -> [Parent,ExpectedMsg]). messages_in_queue_restart(Parent, ExpectedMessage) -> - ?line receive - ExpectedMessage -> - Parent ! done; - Other -> - io:format("~p\n", [Other]), - ct:fail(unexpected_message) - end, + receive + ExpectedMessage -> + Parent ! done; + Other -> + io:format("~p\n", [Other]), + ct:fail(unexpected_message) + end, ok. @@ -289,36 +288,36 @@ messages_in_queue_restart(Parent, ExpectedMessage) -> %%% undefined_mfa(Config) when is_list(Config) -> - ?line process_flag(trap_exit, true), - ?line Pid = spawn_link(fun() -> + process_flag(trap_exit, true), + Pid = spawn_link(fun() -> %% Will be a call_only instruction. erlang:hibernate(?MODULE, blarf, []) end), - ?line Pid ! {a,message}, - ?line receive - {'EXIT',Pid,{undef,Undef}} -> - io:format("~p\n", [Undef]), - ok; - Other -> - ?line io:format("~p\n", [Other]), - ?line ct:fail(unexpected_message) - end, + Pid ! {a,message}, + receive + {'EXIT',Pid,{undef,Undef}} -> + io:format("~p\n", [Undef]), + ok; + Other -> + io:format("~p\n", [Other]), + ct:fail(unexpected_message) + end, undefined_mfa_1(). undefined_mfa_1() -> - ?line Pid = spawn_link(fun() -> + Pid = spawn_link(fun() -> %% Force a call_last instruction by calling bar() %% (if that is not obvious). bar(), erlang:hibernate(?MODULE, blarf, []) end), - ?line Pid ! {another,message}, - ?line receive + Pid ! {another,message}, + receive {'EXIT',Pid,{undef,Undef}} -> io:format("~p\n", [Undef]), ok; Other -> - ?line io:format("~p\n", [Other]), - ?line ct:fail(unexpected_message) + io:format("~p\n", [Other]), + ct:fail(unexpected_message) end, ok. @@ -330,20 +329,16 @@ bar() -> %% no_heap(Config) when is_list(Config) -> - ?line H = spawn_link(fun () -> clean_dict(), no_heap_loop() end), - ?line lists:foreach(fun (_) -> - wait_until(fun () -> is_hibernated(H) end), - ?line [{heap_size,1}, - {total_heap_size,1}] - = process_info(H, - [heap_size, - total_heap_size]), - receive after 10 -> ok end, - H ! again - end, - lists:seq(1, 100)), - ?line unlink(H), - ?line exit(H, bye). + H = spawn_link(fun () -> clean_dict(), no_heap_loop() end), + lists:foreach(fun (_) -> + wait_until(fun () -> is_hibernated(H) end), + [{heap_size,1}, {total_heap_size,1}] + = process_info(H, [heap_size, total_heap_size]), + receive after 10 -> ok end, + H ! again + end, lists:seq(1, 100)), + unlink(H), + exit(H, bye). no_heap_loop() -> flush(), @@ -358,16 +353,16 @@ clean_dict() -> %% wake_up_and_bif_trap(Config) when is_list(Config) -> - ?line Self = self(), - ?line Pid = spawn_link(fun() -> erlang:hibernate(?MODULE, characters_to_list_trap, [Self]) end), - ?line Pid ! wakeup, - ?line receive + Self = self(), + Pid = spawn_link(fun() -> erlang:hibernate(?MODULE, characters_to_list_trap, [Self]) end), + Pid ! wakeup, + receive {ok, Pid0} when Pid0 =:= Pid -> ok after 5000 -> - ?line ct:fail(process_blocked) + ct:fail(process_blocked) end, - ?line unlink(Pid), - ?line exit(Pid, bye). + unlink(Pid), + exit(Pid, bye). %% Lengthy computation that traps (in characters_to_list_trap_3). characters_to_list_trap(Parent) -> diff --git a/erts/emulator/test/list_bif_SUITE.erl b/erts/emulator/test/list_bif_SUITE.erl index 40224723d8..1f64a6a8e5 100644 --- a/erts/emulator/test/list_bif_SUITE.erl +++ b/erts/emulator/test/list_bif_SUITE.erl @@ -37,92 +37,90 @@ all() -> %% Tests list_to_integer and string:to_integer t_list_to_integer(Config) when is_list(Config) -> - ?line {'EXIT',{badarg,_}} = (catch list_to_integer("12373281903728109372810937209817320981321ABC")), - ?line 12373281903728109372810937209817320981321 = (catch list_to_integer("12373281903728109372810937209817320981321")), - ?line 12373 = (catch list_to_integer("12373")), - ?line -12373 = (catch list_to_integer("-12373")), - ?line 12373 = (catch list_to_integer("+12373")), - ?line {'EXIT',{badarg,_}} = ( catch list_to_integer(abc)), - ?line {'EXIT',{badarg,_}} = (catch list_to_integer("")), - ?line {12373281903728109372810937209817320981321,"ABC"} = string:to_integer("12373281903728109372810937209817320981321ABC"), - ?line {-12373281903728109372810937209817320981321,"ABC"} = string:to_integer("-12373281903728109372810937209817320981321ABC"), - ?line {12,[345]} = string:to_integer([$1,$2,345]), - ?line {12,[a]} = string:to_integer([$1,$2,a]), - ?line {error,no_integer} = string:to_integer([$A]), - ?line {error,not_a_list} = string:to_integer($A), + {'EXIT',{badarg,_}} = (catch list_to_integer("12373281903728109372810937209817320981321ABC")), + 12373281903728109372810937209817320981321 = (catch list_to_integer("12373281903728109372810937209817320981321")), + 12373 = (catch list_to_integer("12373")), + -12373 = (catch list_to_integer("-12373")), + 12373 = (catch list_to_integer("+12373")), + {'EXIT',{badarg,_}} = ( catch list_to_integer(abc)), + {'EXIT',{badarg,_}} = (catch list_to_integer("")), + {12373281903728109372810937209817320981321,"ABC"} = string:to_integer("12373281903728109372810937209817320981321ABC"), + {-12373281903728109372810937209817320981321,"ABC"} = string:to_integer("-12373281903728109372810937209817320981321ABC"), + {12,[345]} = string:to_integer([$1,$2,345]), + {12,[a]} = string:to_integer([$1,$2,a]), + {error,no_integer} = string:to_integer([$A]), + {error,not_a_list} = string:to_integer($A), ok. %% Test hd/1 with correct and incorrect arguments. hd_test(Config) when is_list(Config) -> - ?line $h = hd(id("hejsan")), - ?line case catch hd(id($h)) of - {'EXIT', {badarg, _}} -> ok; - Res -> - ct:fail("hd/1 with incorrect args succeeded.~nResult: ~p", [Res]) - end, + $h = hd(id("hejsan")), + case catch hd(id($h)) of + {'EXIT', {badarg, _}} -> ok; + Res -> + ct:fail("hd/1 with incorrect args succeeded.~nResult: ~p", [Res]) + end, ok. %% Test tl/1 with correct and incorrect arguments. tl_test(Config) when is_list(Config) -> - ?line "ejsan" = tl(id("hejsan")), - ?line case catch tl(id(104)) of - {'EXIT', {badarg, _}} -> - ok; - Res -> - ct:fail("tl/1 with incorrect args succeeded.~nResult: ~p", [Res]) - end, + "ejsan" = tl(id("hejsan")), + case catch tl(id(104)) of + {'EXIT', {badarg, _}} -> + ok; + Res -> + ct:fail("tl/1 with incorrect args succeeded.~nResult: ~p", [Res]) + end, ok. %% Test length/1 with correct and incorrect arguments. t_length(Config) when is_list(Config) -> - ?line 0 = length(""), - ?line 0 = length([]), - ?line 1 = length([1]), - ?line 2 = length([1,a]), - ?line 2 = length("ab"), - ?line 3 = length("abc"), - ?line 4 = length(id([x|"abc"])), - ?line 6 = length("hejsan"), - ?line {'EXIT',{badarg,_}} = (catch length(id([a,b|c]))), - ?line case catch length({tuple}) of - {'EXIT', {badarg, _}} -> - ok; - Res -> - ct:fail("length/1 with incorrect args succeeded.~nResult: ~p", [Res]) - end, + 0 = length(""), + 0 = length([]), + 1 = length([1]), + 2 = length([1,a]), + 2 = length("ab"), + 3 = length("abc"), + 4 = length(id([x|"abc"])), + 6 = length("hejsan"), + {'EXIT',{badarg,_}} = (catch length(id([a,b|c]))), + case catch length({tuple}) of + {'EXIT', {badarg, _}} -> + ok; + Res -> + ct:fail("length/1 with incorrect args succeeded.~nResult: ~p", [Res]) + end, ok. %% Test list_to_pid/1 with correct and incorrect arguments. t_list_to_pid(Config) when is_list(Config) -> - ?line Me = self(), - ?line MyListedPid = pid_to_list(Me), - ?line Me = list_to_pid(MyListedPid), - ?line case catch list_to_pid(id("Incorrect list")) of - {'EXIT', {badarg, _}} -> - ok; - Res -> - ct:fail("list_to_pid/1 with incorrect arg succeeded.~nResult: ~p", [Res]) - end, + Me = self(), + MyListedPid = pid_to_list(Me), + Me = list_to_pid(MyListedPid), + case catch list_to_pid(id("Incorrect list")) of + {'EXIT', {badarg, _}} -> + ok; + Res -> + ct:fail("list_to_pid/1 with incorrect arg succeeded.~nResult: ~p", [Res]) + end, ok. %% Test list_to_float/1 with correct and incorrect arguments. t_list_to_float(Config) when is_list(Config) -> - ?line 5.89000 = list_to_float(id("5.89")), - ?line 5.89898 = list_to_float(id("5.89898")), - ?line case catch list_to_float(id("58")) of - {'EXIT', {badarg, _}} -> ok; - Res -> - ct:fail("list_to_float with incorrect arg succeeded.~nResult: ~p", [Res]) - end, + 5.89000 = list_to_float(id("5.89")), + 5.89898 = list_to_float(id("5.89898")), + case catch list_to_float(id("58")) of + {'EXIT', {badarg, _}} -> ok; + Res -> + ct:fail("list_to_float with incorrect arg succeeded.~nResult: ~p", [Res]) + end, ok. id(I) -> I. - - diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 6e1ca726a6..baa6eb8fe0 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -63,83 +63,83 @@ not_run(Config) when is_list(Config) -> {skipped, "Native Code"}. test_1(Config) when is_list(Config) -> - ?line tr(fun() -> ?MODULE:f1(a) end, - {?MODULE, f1, 1}, - [], - [{call, {?MODULE, f1, [a]}}]), - - ?line tr(fun() -> ?MODULE:f2(a, a) end, - {?MODULE, f2, 2}, - [{['$1','$1'],[{is_atom, '$1'}],[]}], - [{call, {?MODULE, f2, [a, a]}}]), - - ?line tr(fun() -> ?MODULE:f2(a, a) end, - {?MODULE, f2, 2}, - [{['$1','$1'],[{is_atom, '$1'}],[{message, false}]}], - []), - - ?line tr(fun() -> ?MODULE:f2(a, a) end, - {?MODULE, f2, 2}, - [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711}]}], - [{call, {?MODULE, f2, [a, a]}, 4711}]), + tr(fun() -> ?MODULE:f1(a) end, + {?MODULE, f1, 1}, + [], + [{call, {?MODULE, f1, [a]}}]), + + tr(fun() -> ?MODULE:f2(a, a) end, + {?MODULE, f2, 2}, + [{['$1','$1'],[{is_atom, '$1'}],[]}], + [{call, {?MODULE, f2, [a, a]}}]), + + tr(fun() -> ?MODULE:f2(a, a) end, + {?MODULE, f2, 2}, + [{['$1','$1'],[{is_atom, '$1'}],[{message, false}]}], + []), + + tr(fun() -> ?MODULE:f2(a, a) end, + {?MODULE, f2, 2}, + [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711}]}], + [{call, {?MODULE, f2, [a, a]}, 4711}]), Ref = make_ref(), - ?line tr(fun() -> ?MODULE:f2(Ref, Ref) end, - {?MODULE, f2, 2}, - [{[Ref,'$1'],[{is_reference, '$1'}],[{message, 4711}]}], - [{call, {?MODULE, f2, [Ref, Ref]}, 4711}]), - ?line tr(fun() -> ?MODULE:f2(Ref, Ref) end, - {?MODULE, f2, 2}, - [{['$1',Ref],[{is_reference, '$1'}],[{message, 4711}]}], - [{call, {?MODULE, f2, [Ref, Ref]}, 4711}]), - - ?line tr(fun() -> ?MODULE:f2(a, a) end, - {?MODULE, f2, 2}, - [{['$0','$0'],[{is_atom, '$0'}],[{message, 4711}]}], - [{call, {?MODULE, f2, [a, a]}, 4711}]), - - ?line tr(fun() -> ?MODULE:f2(a, b) end, - {?MODULE, f2, 2}, - [{['_','_'],[],[]}], - [{call, {?MODULE, f2, [a, b]}}]), - - ?line tr(fun() -> ?MODULE:f2(a, b) end, - {?MODULE, f2, 2}, - [{['_','_'],[],[{message, '$_'}]}], - [{call, {?MODULE, f2, [a, b]}, [a, b]}]), - - ?line tr(fun() -> ?MODULE:f2(a, '$_') end, - {?MODULE, f2, 2}, - [{['$1','$_'],[{is_atom, '$1'}],[]}], - [{call, {?MODULE, f2, [a, '$_']}}]), - - ?line tr(fun() -> ?MODULE:f1({a}) end, - {?MODULE, f1, 1}, - [{['$1'],[{'==', '$1', {const, {a}}}],[]}], - [{call, {?MODULE, f1, [{a}]}}]), - - ?line tr(fun() -> ?MODULE:f1({a}) end, - {?MODULE, f1, 1}, - [{['$1'],[{'==', '$1', {{a}}}],[]}], - [{call, {?MODULE, f1, [{a}]}}]), - -%% Undocumented, currently. - ?line tr(fun() -> ?MODULE:f2(a, a) end, - {?MODULE, f2, 2}, - [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711}, - {message, true}]}], - [{call, {?MODULE, f2, [a, a]}}]), - - ?line tr(fun() -> ?MODULE:f2(a, a) end, - {?MODULE, f2, 2}, - [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711}, - {message, false}]}], - []), - - ?line tr(fun() -> ?MODULE:f2(a, a) end, - {?MODULE, f2, 2}, - [{['$1','$1'],[{is_atom, '$1'}],[kakalorum]}], - [{call, {?MODULE, f2, [a, a]}}]), + tr(fun() -> ?MODULE:f2(Ref, Ref) end, + {?MODULE, f2, 2}, + [{[Ref,'$1'],[{is_reference, '$1'}],[{message, 4711}]}], + [{call, {?MODULE, f2, [Ref, Ref]}, 4711}]), + tr(fun() -> ?MODULE:f2(Ref, Ref) end, + {?MODULE, f2, 2}, + [{['$1',Ref],[{is_reference, '$1'}],[{message, 4711}]}], + [{call, {?MODULE, f2, [Ref, Ref]}, 4711}]), + + tr(fun() -> ?MODULE:f2(a, a) end, + {?MODULE, f2, 2}, + [{['$0','$0'],[{is_atom, '$0'}],[{message, 4711}]}], + [{call, {?MODULE, f2, [a, a]}, 4711}]), + + tr(fun() -> ?MODULE:f2(a, b) end, + {?MODULE, f2, 2}, + [{['_','_'],[],[]}], + [{call, {?MODULE, f2, [a, b]}}]), + + tr(fun() -> ?MODULE:f2(a, b) end, + {?MODULE, f2, 2}, + [{['_','_'],[],[{message, '$_'}]}], + [{call, {?MODULE, f2, [a, b]}, [a, b]}]), + + tr(fun() -> ?MODULE:f2(a, '$_') end, + {?MODULE, f2, 2}, + [{['$1','$_'],[{is_atom, '$1'}],[]}], + [{call, {?MODULE, f2, [a, '$_']}}]), + + tr(fun() -> ?MODULE:f1({a}) end, + {?MODULE, f1, 1}, + [{['$1'],[{'==', '$1', {const, {a}}}],[]}], + [{call, {?MODULE, f1, [{a}]}}]), + + tr(fun() -> ?MODULE:f1({a}) end, + {?MODULE, f1, 1}, + [{['$1'],[{'==', '$1', {{a}}}],[]}], + [{call, {?MODULE, f1, [{a}]}}]), + + %% Undocumented, currently. + tr(fun() -> ?MODULE:f2(a, a) end, + {?MODULE, f2, 2}, + [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711}, + {message, true}]}], + [{call, {?MODULE, f2, [a, a]}}]), + + tr(fun() -> ?MODULE:f2(a, a) end, + {?MODULE, f2, 2}, + [{['$1','$1'],[{is_atom, '$1'}],[{message, 4711}, + {message, false}]}], + []), + + tr(fun() -> ?MODULE:f2(a, a) end, + {?MODULE, f2, 2}, + [{['$1','$1'],[{is_atom, '$1'}],[kakalorum]}], + [{call, {?MODULE, f2, [a, a]}}]), %% Verify that 'process_dump' can handle a matchstate on the stack. tr(fun() -> fbinmatch(<<0>>, 0) end, @@ -147,50 +147,49 @@ test_1(Config) when is_list(Config) -> [{['_'],[],[{message, {process_dump}}]}], [fun({trace, _, call, {?MODULE, f1, [0]}, _Bin}) -> true end]), -% Error cases - ?line errchk([{['$1','$1'],[{is_atom, '$1'}],[{banka, kanin}]}]), - + % Error cases + errchk([{['$1','$1'],[{is_atom, '$1'}],[{banka, kanin}]}]), ok. test_2(Config) when is_list(Config) -> - ?line tr(fun() -> ?MODULE:f2(a, a) end, - {?MODULE, f2, 2}, - [{['$1','$1'],[{is_atom, '$1'}],[{return_trace}]}], - [{call, {?MODULE, f2, [a, a]}}, - {return_from, {?MODULE, f2, 2}, {a, a}}]), + tr(fun() -> ?MODULE:f2(a, a) end, + {?MODULE, f2, 2}, + [{['$1','$1'],[{is_atom, '$1'}],[{return_trace}]}], + [{call, {?MODULE, f2, [a, a]}}, + {return_from, {?MODULE, f2, 2}, {a, a}}]), ok. %% Test the enable_trace/2 and caller/0 PAM instructions test_3(Config) when is_list(Config) -> - ?line Fun1 = fun() -> + Fun1 = fun() -> register(fnoppelklopfer,self()), ?MODULE:f2(a, b), ?MODULE:f2(a, b) end, - ?line P1 = spawn(?MODULE, runner, [self(), Fun1]), - ?line Pat = [{['$1','$1'],[],[{message, - [{enable_trace, P1, call},{caller}]}]}, - {['_','_'],[],[{message, - [{disable_trace, fnoppelklopfer, call}]}]}], - ?line Fun2 = fun() -> ?MODULE:f3(a, a) end, - ?line P2 = spawn(?MODULE, runner, [self(), Fun2]), - ?line erlang:trace(P2, true, [call]), - ?line erlang:trace_pattern({?MODULE, f2, 2}, Pat), - ?line collect(P2, [{trace, P2, call, {?MODULE, f2, [a, a]}, [true, + P1 = spawn(?MODULE, runner, [self(), Fun1]), + Pat = [{['$1','$1'],[],[{message, + [{enable_trace, P1, call},{caller}]}]}, + {['_','_'],[],[{message, + [{disable_trace, fnoppelklopfer, call}]}]}], + Fun2 = fun() -> ?MODULE:f3(a, a) end, + P2 = spawn(?MODULE, runner, [self(), Fun2]), + erlang:trace(P2, true, [call]), + erlang:trace_pattern({?MODULE, f2, 2}, Pat), + collect(P2, [{trace, P2, call, {?MODULE, f2, [a, a]}, [true, {?MODULE,f3,2}]}]), - ?line collect(P1, [{trace, P1, call, {?MODULE, f2, [a, b]}, [true]}]), - ?line ok. + collect(P1, [{trace, P1, call, {?MODULE, f2, [a, b]}, [true]}]), + ok. otp_9422(Config) when is_list(Config) -> Laps = 10000, - ?line Fun1 = fun() -> otp_9422_tracee() end, - ?line P1 = spawn_link(?MODULE, loop_runner, [self(), Fun1, Laps]), + Fun1 = fun() -> otp_9422_tracee() end, + P1 = spawn_link(?MODULE, loop_runner, [self(), Fun1, Laps]), io:format("spawned ~p as tracee\n", [P1]), - ?line erlang:trace(P1, true, [call, silent]), + erlang:trace(P1, true, [call, silent]), - ?line Fun2 = fun() -> otp_9422_trace_changer() end, - ?line P2 = spawn_link(?MODULE, loop_runner, [self(), Fun2, Laps]), + Fun2 = fun() -> otp_9422_trace_changer() end, + P2 = spawn_link(?MODULE, loop_runner, [self(), Fun2, Laps]), io:format("spawned ~p as trace_changer\n", [P2]), start_collect(P1), @@ -209,9 +208,9 @@ otp_9422_tracee() -> otp_9422_trace_changer() -> Pat1 = [{[a], [], [{enable_trace, arity}]}], - ?line erlang:trace_pattern({?MODULE, f1, 1}, Pat1), + erlang:trace_pattern({?MODULE, f1, 1}, Pat1), Pat2 = [{[b], [], [{disable_trace, arity}]}], - ?line erlang:trace_pattern({?MODULE, f1, 1}, Pat2). + erlang:trace_pattern({?MODULE, f1, 1}, Pat2). @@ -230,131 +229,131 @@ bad_match_spec_bin(Config) when is_list(Config) -> %% erlang:system_flag(trace_control_word, Value) BIFs, %% as well as the get_tcw/0 and set_tcw/1 PAM instructions trace_control_word(Config) when is_list(Config) -> - ?line 32 = Bits = tcw_bits(), - ?line High = 1 bsl (Bits - 1), - ?line erlang:system_flag(trace_control_word, 17), - ?line tr(fun() -> ?MODULE:f1(a) end, - {?MODULE, f1, 1}, - [{'_',[{'=:=', {get_tcw}, 17}],[]}], - [{call, {?MODULE, f1, [a]}}]), - ?line tr(fun() -> ?MODULE:f1(a) end, - {?MODULE, f1, 1}, - [{'_',[{'=:=', {get_tcw}, 18}],[]}], - []), - ?line erlang:system_flag(trace_control_word, High), - ?line tr(fun() -> ?MODULE:f1(a) end, - {?MODULE, f1, 1}, - [{'_',[{'=:=', {get_tcw}, High}],[]}], - [{call, {?MODULE, f1, [a]}}]), - ?line erlang:system_flag(trace_control_word, 0), - ?line tr(fun() -> - ?MODULE:f1(a), - ?MODULE:f1(start), - ?MODULE:f1(b), - ?MODULE:f1(c), - ?MODULE:f1(high), - ?MODULE:f1(d), - ?MODULE:f1(stop), - ?MODULE:f1(e) - end, - {?MODULE, f1, 1}, - [{[start], - [], - [{message, {set_tcw, 17}}]}, - {[stop], - [], - [{message, {set_tcw, 0}}]}, - {[high], - [], - [{message, {set_tcw, High}}]}, - {['_'], - [{'>', {get_tcw}, 0}], - [{set_tcw, {'+', 1, {get_tcw}}}, {message, {get_tcw}}] }], - [{call, {?MODULE, f1, [start]}, 0}, - {call, {?MODULE, f1, [b]}, 18}, - {call, {?MODULE, f1, [c]}, 19}, - {call, {?MODULE, f1, [high]}, 19}, - {call, {?MODULE, f1, [d]}, High + 1}, - {call, {?MODULE, f1, [stop]}, High + 1}]), - ?line 0 = erlang:system_info(trace_control_word), + 32 = Bits = tcw_bits(), + High = 1 bsl (Bits - 1), + erlang:system_flag(trace_control_word, 17), + tr(fun() -> ?MODULE:f1(a) end, + {?MODULE, f1, 1}, + [{'_',[{'=:=', {get_tcw}, 17}],[]}], + [{call, {?MODULE, f1, [a]}}]), + tr(fun() -> ?MODULE:f1(a) end, + {?MODULE, f1, 1}, + [{'_',[{'=:=', {get_tcw}, 18}],[]}], + []), + erlang:system_flag(trace_control_word, High), + tr(fun() -> ?MODULE:f1(a) end, + {?MODULE, f1, 1}, + [{'_',[{'=:=', {get_tcw}, High}],[]}], + [{call, {?MODULE, f1, [a]}}]), + erlang:system_flag(trace_control_word, 0), + tr(fun() -> + ?MODULE:f1(a), + ?MODULE:f1(start), + ?MODULE:f1(b), + ?MODULE:f1(c), + ?MODULE:f1(high), + ?MODULE:f1(d), + ?MODULE:f1(stop), + ?MODULE:f1(e) + end, + {?MODULE, f1, 1}, + [{[start], + [], + [{message, {set_tcw, 17}}]}, + {[stop], + [], + [{message, {set_tcw, 0}}]}, + {[high], + [], + [{message, {set_tcw, High}}]}, + {['_'], + [{'>', {get_tcw}, 0}], + [{set_tcw, {'+', 1, {get_tcw}}}, {message, {get_tcw}}] }], + [{call, {?MODULE, f1, [start]}, 0}, + {call, {?MODULE, f1, [b]}, 18}, + {call, {?MODULE, f1, [c]}, 19}, + {call, {?MODULE, f1, [high]}, 19}, + {call, {?MODULE, f1, [d]}, High + 1}, + {call, {?MODULE, f1, [stop]}, High + 1}]), + 0 = erlang:system_info(trace_control_word), ok. tcw_bits() -> - ?line tcw_bits(erlang:system_flag(trace_control_word, 0), 0, 0). + tcw_bits(erlang:system_flag(trace_control_word, 0), 0, 0). tcw_bits(Save, Prev, Bits) -> - ?line Curr = 1 bsl Bits, - ?line case catch erlang:system_flag(trace_control_word, Curr) of - {'EXIT' , {badarg, _}} -> - ?line Prev = erlang:system_flag(trace_control_word, Save), - Bits; - Prev -> - ?line Curr = erlang:system_info(trace_control_word), - tcw_bits(Save, Curr, Bits+1) - end. + Curr = 1 bsl Bits, + case catch erlang:system_flag(trace_control_word, Curr) of + {'EXIT' , {badarg, _}} -> + Prev = erlang:system_flag(trace_control_word, Save), + Bits; + Prev -> + Curr = erlang:system_info(trace_control_word), + tcw_bits(Save, Curr, Bits+1) + end. %% Test the erlang:trace(_, _, [silent]) flag %% as well as the silent/0 PAM instruction silent(Config) when is_list(Config) -> %% Global call trace - ?line tr(fun() -> - ?MODULE:f1(a), % No trace - not active - ?MODULE:f1(miss), % No trace - no activation - ?MODULE:f1(b), % No trace - still not active - ?MODULE:f1(start), % Trace - activation - ?MODULE:f1(c), % Trace - active - f1(d), % No trace - local call - ?MODULE:f1(miss), % Trace - no inactivation - ?MODULE:f1(e), % Trace - still active - ?MODULE:f1(stop), % No trace - inactivation - ?MODULE:f1(f) % No trace - not active - end, - {?MODULE, f1, 1}, - [call, silent], - [{[start], - [], - [{silent, false}, {message, start}]}, - {[stop], - [], - [{silent, true}, {message, stop}]}, - {[miss], - [], - [{silent, neither_true_nor_false}, {message, miss}]}, - {['$1'], - [], - [{message, '$1'}] }], - [global], - [{call, {?MODULE, f1, [start]}, start}, - {call, {?MODULE, f1, [c]}, c}, - {call, {?MODULE, f1, [miss]}, miss}, - {call, {?MODULE, f1, [e]}, e} ]), + tr(fun() -> + ?MODULE:f1(a), % No trace - not active + ?MODULE:f1(miss), % No trace - no activation + ?MODULE:f1(b), % No trace - still not active + ?MODULE:f1(start), % Trace - activation + ?MODULE:f1(c), % Trace - active + f1(d), % No trace - local call + ?MODULE:f1(miss), % Trace - no inactivation + ?MODULE:f1(e), % Trace - still active + ?MODULE:f1(stop), % No trace - inactivation + ?MODULE:f1(f) % No trace - not active + end, + {?MODULE, f1, 1}, + [call, silent], + [{[start], + [], + [{silent, false}, {message, start}]}, + {[stop], + [], + [{silent, true}, {message, stop}]}, + {[miss], + [], + [{silent, neither_true_nor_false}, {message, miss}]}, + {['$1'], + [], + [{message, '$1'}] }], + [global], + [{call, {?MODULE, f1, [start]}, start}, + {call, {?MODULE, f1, [c]}, c}, + {call, {?MODULE, f1, [miss]}, miss}, + {call, {?MODULE, f1, [e]}, e} ]), %% Local call trace - ?line tr(fun() -> - ?MODULE:f1(a), % No trace - not active - f1(b), % No trace - not active - ?MODULE:f1(start), % Trace - activation - ?MODULE:f1(c), % Trace - active - f1(d), % Trace - active - f1(stop), % No trace - inactivation - ?MODULE:f1(e), % No trace - not active - f1(f) % No trace - not active - end, - {?MODULE, f1, 1}, - [call, silent], - [{[start], - [], - [{silent, false}, {message, start}]}, - {[stop], - [], - [{silent, true}, {message, stop}]}, - {['$1'], - [], - [{message, '$1'}] }], - [local], - [{call, {?MODULE, f1, [start]}, start}, - {call, {?MODULE, f1, [c]}, c}, - {call, {?MODULE, f1, [d]}, d} ]), + tr(fun() -> + ?MODULE:f1(a), % No trace - not active + f1(b), % No trace - not active + ?MODULE:f1(start), % Trace - activation + ?MODULE:f1(c), % Trace - active + f1(d), % Trace - active + f1(stop), % No trace - inactivation + ?MODULE:f1(e), % No trace - not active + f1(f) % No trace - not active + end, + {?MODULE, f1, 1}, + [call, silent], + [{[start], + [], + [{silent, false}, {message, start}]}, + {[stop], + [], + [{silent, true}, {message, stop}]}, + {['$1'], + [], + [{message, '$1'}] }], + [local], + [{call, {?MODULE, f1, [start]}, start}, + {call, {?MODULE, f1, [c]}, c}, + {call, {?MODULE, f1, [d]}, d} ]), ok. %% Test the erlang:trace(_, _, [silent]) flag without match specs @@ -363,105 +362,81 @@ silent_no_ms(Config) when is_list(Config) -> %% %% Trace f2/2 and erlang:integer_to_list/1 without match spec %% and use match spec on f1/1 to control silent flag. - ?line tr( - fun () -> - ?MODULE:f1(a), - ?MODULE:f2(b, c), - _ = erlang:integer_to_list(id(1)), - ?MODULE:f3(d, e), - ?MODULE:f1(start), - ?MODULE:f2(f, g), - _ = erlang:integer_to_list(id(2)), - ?MODULE:f3(h, i), - ?MODULE:f1(stop), - ?MODULE:f2(j, k), - _ = erlang:integer_to_list(id(3)), - ?MODULE:f3(l, m) - end, - fun (Tracee) -> - ?line 1 = - erlang:trace(Tracee, true, - [call,silent,return_to]), - ?line 1 = - erlang:trace_pattern( - {?MODULE,f2,2}, - [], - [global]), - ?line 1 = - erlang:trace_pattern( - {erlang,integer_to_list,1}, - [], - [global]), - ?line 1 = - erlang:trace_pattern( - {?MODULE,f1,1}, - [{[start],[],[{silent,false}]}, - {[stop],[],[{silent,true}]}], - [global]), - %% - %% Expected: (no return_to for global call trace) - %% - ?line - [{trace,Tracee,call,{?MODULE,f1,[start]}}, - {trace,Tracee,call,{?MODULE,f2,[f,g]}}, - {trace,Tracee,call,{erlang,integer_to_list,[2]}}, - {trace,Tracee,call,{?MODULE,f2,[h,i]}}] - end), + tr( + fun () -> + ?MODULE:f1(a), + ?MODULE:f2(b, c), + _ = erlang:integer_to_list(id(1)), + ?MODULE:f3(d, e), + ?MODULE:f1(start), + ?MODULE:f2(f, g), + _ = erlang:integer_to_list(id(2)), + ?MODULE:f3(h, i), + ?MODULE:f1(stop), + ?MODULE:f2(j, k), + _ = erlang:integer_to_list(id(3)), + ?MODULE:f3(l, m) + end, + fun (Tracee) -> + 1 = erlang:trace(Tracee, true, [call,silent,return_to]), + 1 = erlang:trace_pattern( {?MODULE,f2,2}, [], [global]), + 1 = erlang:trace_pattern( {erlang,integer_to_list,1}, [], [global]), + 1 = erlang:trace_pattern( + {?MODULE,f1,1}, + [{[start],[],[{silent,false}]}, + {[stop],[],[{silent,true}]}], + [global]), + %% + %% Expected: (no return_to for global call trace) + %% + [{trace,Tracee,call,{?MODULE,f1,[start]}}, + {trace,Tracee,call,{?MODULE,f2,[f,g]}}, + {trace,Tracee,call,{erlang,integer_to_list,[2]}}, + {trace,Tracee,call,{?MODULE,f2,[h,i]}}] + end), %% Local call trace %% %% Trace f2/2 and erlang:integer_to_list/1 without match spec %% and use match spec on f1/1 to control silent flag. - ?line tr( - fun () -> - ?MODULE:f1(a), - ?MODULE:f2(b, c), - _ = erlang:integer_to_list(id(1)), - ?MODULE:f3(d, e), - ?MODULE:f1(start), - ?MODULE:f2(f, g), - _ = erlang:integer_to_list(id(2)), - ?MODULE:f3(h, i), - ?MODULE:f1(stop), - ?MODULE:f2(j, k), - _ = erlang:integer_to_list(id(3)), - ?MODULE:f3(l, m) - end, - fun (Tracee) -> - ?line 1 = - erlang:trace(Tracee, true, - [call,silent,return_to]), - ?line 1 = - erlang:trace_pattern( - {?MODULE,f2,2}, - [], - [local]), - ?line 1 = - erlang:trace_pattern( - {erlang,integer_to_list,1}, - [], - [local]), - ?line 1 = - erlang:trace_pattern( - {?MODULE,f1,1}, - [{[start],[],[{silent,false}]}, - {[stop],[],[{silent,true}]}], - [local]), - %% - %% Expected: - %% - ?line - [{trace,Tracee,call,{?MODULE,f1,[start]}}, - {trace,Tracee,return_to, - {?MODULE,'-silent_no_ms/1-fun-2-',0}}, - {trace,Tracee,call,{?MODULE,f2,[f,g]}}, - {trace,Tracee,return_to, - {?MODULE,'-silent_no_ms/1-fun-2-',0}}, - {trace,Tracee,call,{erlang,integer_to_list,[2]}}, - {trace,Tracee,return_to, - {?MODULE,'-silent_no_ms/1-fun-2-',0}}, - {trace,Tracee,call,{?MODULE,f2,[h,i]}}, - {trace,Tracee,return_to,{?MODULE,f3,2}}] - end). + tr( + fun () -> + ?MODULE:f1(a), + ?MODULE:f2(b, c), + _ = erlang:integer_to_list(id(1)), + ?MODULE:f3(d, e), + ?MODULE:f1(start), + ?MODULE:f2(f, g), + _ = erlang:integer_to_list(id(2)), + ?MODULE:f3(h, i), + ?MODULE:f1(stop), + ?MODULE:f2(j, k), + _ = erlang:integer_to_list(id(3)), + ?MODULE:f3(l, m) + end, + fun (Tracee) -> + 1 = erlang:trace(Tracee, true, [call,silent,return_to]), + 1 = erlang:trace_pattern( {?MODULE,f2,2}, [], [local]), + 1 = erlang:trace_pattern( {erlang,integer_to_list,1}, [], [local]), + 1 = erlang:trace_pattern( + {?MODULE,f1,1}, + [{[start],[],[{silent,false}]}, + {[stop],[],[{silent,true}]}], + [local]), + %% + %% Expected: + %% + [{trace,Tracee,call,{?MODULE,f1,[start]}}, + {trace,Tracee,return_to, + {?MODULE,'-silent_no_ms/1-fun-2-',0}}, + {trace,Tracee,call,{?MODULE,f2,[f,g]}}, + {trace,Tracee,return_to, + {?MODULE,'-silent_no_ms/1-fun-2-',0}}, + {trace,Tracee,call,{erlang,integer_to_list,[2]}}, + {trace,Tracee,return_to, + {?MODULE,'-silent_no_ms/1-fun-2-',0}}, + {trace,Tracee,call,{?MODULE,f2,[h,i]}}, + {trace,Tracee,return_to,{?MODULE,f3,2}}] + end). %% Test that match_spec_test does not activate silent silent_test(_Config) -> @@ -478,68 +453,53 @@ ms_trace2(Config) when is_list(Config) -> %% Trace global f1/1, local f2/2, global erlang:integer_to_list/1 %% without match spec. Use match spec functions %% {trace/2} to control trace through fn/2,3. - ?line tr( - fun () -> - ?MODULE:f1(a), - ?MODULE:f2(b, c), - _ = erlang:integer_to_list(id(1)), - ?MODULE:f3(d, e), - fn([all], [call,return_to,{tracer,Tracer}]), - ?MODULE:f1(f), - f2(g, h), - f1(i), - _ = erlang:integer_to_list(id(2)), - ?MODULE:f3(j, k), - fn([call,return_to], []), - ?MODULE:f1(l), - ?MODULE:f2(m, n), - _ = erlang:integer_to_list(id(3)), - ?MODULE:f3(o, p) - end, - fun (Tracee) -> - ?line 1 = - erlang:trace(Tracee, false, [all]), - ?line 1 = - erlang:trace_pattern( - {?MODULE,f1,1}, - [], - [global]), - ?line 1 = - erlang:trace_pattern( - {?MODULE,f2,2}, - [], - [local]), - ?line 1 = - erlang:trace_pattern( - {erlang,integer_to_list,1}, - [], - [global]), - ?line 3 = - erlang:trace_pattern( - {?MODULE,fn,'_'}, - [{['$1','$2'],[], - [{trace,'$1','$2'},{message,ms_trace2}]}], - [meta]), - %% - %% Expected: (no return_to for global call trace) - %% - ?line Origin = {match_spec_SUITE,'-ms_trace2/1-fun-0-',1}, - ?line - [{trace_ts,Tracee,call, - {?MODULE,fn, - [[all],[call,return_to,{tracer,Tracer}]]}, - ms_trace2}, - {trace,Tracee,call,{?MODULE,f1,[f]}}, - {trace,Tracee,call,{?MODULE,f2,[g,h]}}, - {trace,Tracee,return_to,Origin}, - {trace,Tracee,call,{erlang,integer_to_list,[2]}}, - {trace,Tracee,call,{?MODULE,f2,[j,k]}}, - {trace,Tracee,return_to,{?MODULE,f3,2}}, - {trace_ts,Tracee,call, - {?MODULE,fn, - [[call,return_to],[]]}, - ms_trace2}] - end), + tr( + fun () -> + ?MODULE:f1(a), + ?MODULE:f2(b, c), + _ = erlang:integer_to_list(id(1)), + ?MODULE:f3(d, e), + fn([all], [call,return_to,{tracer,Tracer}]), + ?MODULE:f1(f), + f2(g, h), + f1(i), + _ = erlang:integer_to_list(id(2)), + ?MODULE:f3(j, k), + fn([call,return_to], []), + ?MODULE:f1(l), + ?MODULE:f2(m, n), + _ = erlang:integer_to_list(id(3)), + ?MODULE:f3(o, p) + end, + fun (Tracee) -> + 1 = erlang:trace(Tracee, false, [all]), + 1 = erlang:trace_pattern( {?MODULE,f1,1}, [], [global]), + 1 = erlang:trace_pattern( {?MODULE,f2,2}, [], [local]), + 1 = erlang:trace_pattern( {erlang,integer_to_list,1}, [], [global]), + 3 = erlang:trace_pattern( + {?MODULE,fn,'_'}, + [{['$1','$2'],[], + [{trace,'$1','$2'},{message,ms_trace2}]}], + [meta]), + %% + %% Expected: (no return_to for global call trace) + %% + Origin = {match_spec_SUITE,'-ms_trace2/1-fun-0-',1}, + [{trace_ts,Tracee,call, + {?MODULE,fn, + [[all],[call,return_to,{tracer,Tracer}]]}, + ms_trace2}, + {trace,Tracee,call,{?MODULE,f1,[f]}}, + {trace,Tracee,call,{?MODULE,f2,[g,h]}}, + {trace,Tracee,return_to,Origin}, + {trace,Tracee,call,{erlang,integer_to_list,[2]}}, + {trace,Tracee,call,{?MODULE,f2,[j,k]}}, + {trace,Tracee,return_to,{?MODULE,f3,2}}, + {trace_ts,Tracee,call, + {?MODULE,fn, + [[call,return_to],[]]}, + ms_trace2}] + end), ok. @@ -555,130 +515,115 @@ ms_trace3(Config) when is_list(Config) -> %% {trace/2} to control trace through fn/2,3. Tag = make_ref(), Controller = - spawn_link( - fun () -> - receive - {Tracee,Tag,start} -> - fn(TraceeName, [all], - [call,return_to,send,'receive', - {tracer,Tracer}]), - Tracee ! {self(),Tag,started}, - receive {Tracee,Tag,stop_1} -> ok end, - fn(Tracee, [call,return_to], []), - Tracee ! {self(),Tag,stopped_1}, - receive {Tracee,Tag,stop_2} -> ok end, - fn(Tracee, [all], []), - Tracee ! {self(),Tag,stopped_2} - end - end), - ?line tr( - fun () -> %% Action - register(TraceeName, self()), - ?MODULE:f1(a), - ?MODULE:f2(b, c), - _ = erlang:integer_to_list(id(1)), - ?MODULE:f3(d, e), - Controller ! {self(),Tag,start}, - receive {Controller,Tag,started} -> ok end, - ?MODULE:f1(f), - f2(g, h), - f1(i), - _ = erlang:integer_to_list(id(2)), - ?MODULE:f3(j, k), - Controller ! {self(),Tag,stop_1}, - receive {Controller,Tag,stopped_1} -> ok end, - ?MODULE:f1(l), - ?MODULE:f2(m, n), - _ = erlang:integer_to_list(id(3)), - ?MODULE:f3(o, p), - Controller ! {self(),Tag,stop_2}, - receive {Controller,Tag,stopped_2} -> ok end, - ?MODULE:f1(q), - ?MODULE:f2(r, s), - _ = erlang:integer_to_list(id(4)), - ?MODULE:f3(t, u) - end, - - fun (Tracee) -> %% Startup - ?line 1 = - erlang:trace(Tracee, false, [all]), - ?line 1 = - erlang:trace_pattern( - {?MODULE,f1,1}, - [], - [global]), - ?line 1 = - erlang:trace_pattern( - {?MODULE,f2,2}, - [], - [local]), - ?line 1 = - erlang:trace_pattern( - {erlang,integer_to_list,1}, - [], - [global]), - ?line 3 = - erlang:trace_pattern( - {?MODULE,fn,'_'}, - [{['$1','$2','$3'],[], - [{trace,'$1','$2','$3'},{message,Tag}]}], - [meta]), - %% - %% Expected: (no return_to for global call trace) - %% - ?line Origin = {match_spec_SUITE,'-ms_trace3/1-fun-1-',2}, - ?line - [{trace_ts,Controller,call, - {?MODULE,fn,[TraceeName,[all], - [call,return_to,send,'receive', - {tracer,Tracer}]]}, - Tag}, - {trace,Tracee,'receive',{Controller,Tag,started}}, - {trace,Tracee,call,{?MODULE,f1,[f]}}, - {trace,Tracee,call,{?MODULE,f2,[g,h]}}, - {trace,Tracee,return_to,Origin}, - {trace,Tracee,call,{erlang,integer_to_list,[2]}}, - {trace,Tracee,call,{?MODULE,f2,[j,k]}}, - {trace,Tracee,return_to,{?MODULE,f3,2}}, - {trace,Tracee,send,{Tracee,Tag,stop_1},Controller}, - {trace_ts,Controller,call, - {?MODULE,fn,[Tracee,[call,return_to],[]]}, - Tag}, - {trace_ts,Controller,call, - {?MODULE,fn,[Tracee,[all],[]]}, - Tag}] - end), + spawn_link( + fun () -> + receive + {Tracee,Tag,start} -> + fn(TraceeName, [all], + [call,return_to,send,'receive', + {tracer,Tracer}]), + Tracee ! {self(),Tag,started}, + receive {Tracee,Tag,stop_1} -> ok end, + fn(Tracee, [call,return_to], []), + Tracee ! {self(),Tag,stopped_1}, + receive {Tracee,Tag,stop_2} -> ok end, + fn(Tracee, [all], []), + Tracee ! {self(),Tag,stopped_2} + end + end), + tr( + fun () -> %% Action + register(TraceeName, self()), + ?MODULE:f1(a), + ?MODULE:f2(b, c), + _ = erlang:integer_to_list(id(1)), + ?MODULE:f3(d, e), + Controller ! {self(),Tag,start}, + receive {Controller,Tag,started} -> ok end, + ?MODULE:f1(f), + f2(g, h), + f1(i), + _ = erlang:integer_to_list(id(2)), + ?MODULE:f3(j, k), + Controller ! {self(),Tag,stop_1}, + receive {Controller,Tag,stopped_1} -> ok end, + ?MODULE:f1(l), + ?MODULE:f2(m, n), + _ = erlang:integer_to_list(id(3)), + ?MODULE:f3(o, p), + Controller ! {self(),Tag,stop_2}, + receive {Controller,Tag,stopped_2} -> ok end, + ?MODULE:f1(q), + ?MODULE:f2(r, s), + _ = erlang:integer_to_list(id(4)), + ?MODULE:f3(t, u) + end, + + fun (Tracee) -> %% Startup + 1 = erlang:trace(Tracee, false, [all]), + 1 = erlang:trace_pattern( {?MODULE,f1,1}, [], [global]), + 1 = erlang:trace_pattern( {?MODULE,f2,2}, [], [local]), + 1 = erlang:trace_pattern( {erlang,integer_to_list,1}, [], [global]), + 3 = erlang:trace_pattern( + {?MODULE,fn,'_'}, + [{['$1','$2','$3'],[], + [{trace,'$1','$2','$3'},{message,Tag}]}], + [meta]), + %% + %% Expected: (no return_to for global call trace) + %% + Origin = {match_spec_SUITE,'-ms_trace3/1-fun-1-',2}, + [{trace_ts,Controller,call, + {?MODULE,fn,[TraceeName,[all], + [call,return_to,send,'receive', + {tracer,Tracer}]]}, + Tag}, + {trace,Tracee,'receive',{Controller,Tag,started}}, + {trace,Tracee,call,{?MODULE,f1,[f]}}, + {trace,Tracee,call,{?MODULE,f2,[g,h]}}, + {trace,Tracee,return_to,Origin}, + {trace,Tracee,call,{erlang,integer_to_list,[2]}}, + {trace,Tracee,call,{?MODULE,f2,[j,k]}}, + {trace,Tracee,return_to,{?MODULE,f3,2}}, + {trace,Tracee,send,{Tracee,Tag,stop_1},Controller}, + {trace_ts,Controller,call, + {?MODULE,fn,[Tracee,[call,return_to],[]]}, + Tag}, + {trace_ts,Controller,call, + {?MODULE,fn,[Tracee,[all],[]]}, + Tag}] + end), ok. %% Test that destructive operations in test bif does not really happen destructive_in_test_bif(Config) when is_list(Config) -> - ?line {ok,OldToken,_,_} = erlang:match_spec_test + {ok,OldToken,_,_} = erlang:match_spec_test ([], [{'_',[],[{message,{get_seq_token}}]}],trace), - ?line {ok,_,_,_} = erlang:match_spec_test + {ok,_,_,_} = erlang:match_spec_test ([], [{'_',[],[{message,{set_seq_token, label, 1}}]}], trace), - ?line {ok,OldToken,_,_} = erlang:match_spec_test + {ok,OldToken,_,_} = erlang:match_spec_test ([], [{'_',[],[{message,{get_seq_token}}]}],trace), - ?line {ok, OldTCW,_,_} = erlang:match_spec_test + {ok, OldTCW,_,_} = erlang:match_spec_test ([],[{'_',[],[{message,{get_tcw}}]}],trace), - ?line {ok,OldTCW,_,_} = erlang:match_spec_test + {ok,OldTCW,_,_} = erlang:match_spec_test ([], [{'_',[],[{message,{set_tcw, OldTCW+1}}]}], trace), - ?line {ok, OldTCW,_,_} = erlang:match_spec_test + {ok, OldTCW,_,_} = erlang:match_spec_test ([],[{'_',[],[{message,{get_tcw}}]}],trace), ok. %% Test that the comparision between boxed and small does not crash emulator boxed_and_small(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(match_spec_suite_other), - ?line ok = rpc:call(Node,?MODULE,do_boxed_and_small,[]), - ?line stop_node(Node), + {ok, Node} = start_node(match_spec_suite_other), + ok = rpc:call(Node,?MODULE,do_boxed_and_small,[]), + stop_node(Node), ok. do_boxed_and_small() -> @@ -690,9 +635,9 @@ do_boxed_and_small() -> %% Test that faulty seq_trace_call does not crash emulator faulty_seq_trace(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(match_spec_suite_other), - ?line ok = rpc:call(Node,?MODULE,do_faulty_seq_trace,[]), - ?line stop_node(Node), + {ok, Node} = start_node(match_spec_suite_other), + ok = rpc:call(Node,?MODULE,do_faulty_seq_trace,[]), + stop_node(Node), ok. do_faulty_seq_trace() -> @@ -709,25 +654,25 @@ errchk(Pat) -> %% Checks that unary minus works unary_minus(Config) when is_list(Config) -> - ?line {ok,true,[],[]} = erlang:match_spec_test + {ok,true,[],[]} = erlang:match_spec_test (5, [{'$1', [{'<',{'-','$1'},-4}], [true]}], table), - ?line {ok,false,[],[]} = erlang:match_spec_test + {ok,false,[],[]} = erlang:match_spec_test (5, [{'$1', [{'<',{'-','$1'},-6}], [true]}], table), - ?line {ok,true,[],[]} = erlang:match_spec_test + {ok,true,[],[]} = erlang:match_spec_test (5, [{'$1', [{'=:=',{'-','$1',2},3}], [true]}], table), - ?line {ok,false,[],[]} = erlang:match_spec_test + {ok,false,[],[]} = erlang:match_spec_test (hej, [{'$1', [{'=/=',{'-','$1'},0}], @@ -737,25 +682,25 @@ unary_minus(Config) when is_list(Config) -> %% Checks that unary plus works unary_plus(Config) when is_list(Config) -> - ?line {ok,true,[],[]} = erlang:match_spec_test + {ok,true,[],[]} = erlang:match_spec_test (5, [{'$1', [{'<',{'+','$1'},6}], [true]}], table), - ?line {ok,false,[],[]} = erlang:match_spec_test + {ok,false,[],[]} = erlang:match_spec_test (5, [{'$1', [{'<',{'+','$1'},4}], [true]}], table), - ?line {ok,true,[],[]} = erlang:match_spec_test + {ok,true,[],[]} = erlang:match_spec_test (5, [{'$1', [{'=:=',{'+','$1',2},7}], [true]}], table), - ?line {ok,false,[],[]} = erlang:match_spec_test + {ok,false,[],[]} = erlang:match_spec_test (hej, [{'$1', [{'=/=',{'+','$1'},0}], @@ -768,48 +713,48 @@ unary_plus(Config) when is_list(Config) -> %% Checks that exceptions in guards are handled correctly guard_exceptions(Config) when is_list(Config) -> - ?line {ok,false,[],[]} = erlang:match_spec_test + {ok,false,[],[]} = erlang:match_spec_test (5, [{'$1', [{'or',{is_integer,'$1'},{'or','$1','$1'}}], [true]}], table), - ?line {ok,true,[],[]} = erlang:match_spec_test + {ok,true,[],[]} = erlang:match_spec_test (5, [{'$1', [{'orelse',{is_integer,'$1'}, {'or','$1','$1'}}], [true]}], table), - ?line {ok,false,[],[]} = erlang:match_spec_test + {ok,false,[],[]} = erlang:match_spec_test (5, [{'$1', [{'orelse',{'or','$1',true}, {is_integer,'$1'}}], [true]}], table), - ?line {ok,false,[],[]} = erlang:match_spec_test + {ok,false,[],[]} = erlang:match_spec_test (5, [{'$1', [{'or',{is_integer,'$1'}, {'orelse','$1',true}}], [true]}], table), - ?line {ok,true,[],[]} = erlang:match_spec_test + {ok,true,[],[]} = erlang:match_spec_test (5, [{'$1', [{'or',{is_integer,'$1'}, {'orelse',true,'$1'}}], [true]}], table), - ?line {ok,true,[],[]} = erlang:match_spec_test + {ok,true,[],[]} = erlang:match_spec_test (5, [{'$1', [{'or',{is_integer,'$1'}, {'andalso',false,'$1'}}], [true]}], table), - ?line {ok,false,[],[]} = erlang:match_spec_test + {ok,false,[],[]} = erlang:match_spec_test (5, [{'$1', [{'or',{is_integer,'$1'}, @@ -817,7 +762,7 @@ guard_exceptions(Config) when is_list(Config) -> [true]}], table), - ?line {ok,false,[],[]} = erlang:match_spec_test + {ok,false,[],[]} = erlang:match_spec_test (5, [{'$1', [{'or',{is_integer,'$1'}, @@ -916,11 +861,11 @@ moving_labels(Config) when is_list(Config) -> %% point at their correct target. %% Ms = [{{'$1','$2'},[],[{{ok,{'andalso','$1','$2'},[1,2,3]}}]}], - ?line {ok,{ok,false,[1,2,3]},[],[]} = + {ok,{ok,false,[1,2,3]},[],[]} = erlang:match_spec_test({true,false}, Ms, table), Ms2 = [{{'$1','$2'},[],[{{ok,{'orelse','$1','$2'},[1,2,3]}}]}], - ?line {ok,{ok,true,[1,2,3]},[],[]} = + {ok,{ok,true,[1,2,3]},[],[]} = erlang:match_spec_test({true,false}, Ms2, table), ok. @@ -931,13 +876,13 @@ tr(Fun, MFA, Pat, Expected) -> tr(Fun, MFA, TraceFlags, Pat, PatFlags, Expected0) -> tr(Fun, fun(P) -> - erlang:trace(P, true, TraceFlags), - erlang:trace_pattern(MFA, Pat, PatFlags), - lists:map( - fun(X) when is_function(X,1) -> X; - (X) -> list_to_tuple([trace, P | tuple_to_list(X)]) - end, - Expected0) + erlang:trace(P, true, TraceFlags), + erlang:trace_pattern(MFA, Pat, PatFlags), + lists:map( + fun(X) when is_function(X,1) -> X; + (X) -> list_to_tuple([trace, P | tuple_to_list(X)]) + end, + Expected0) end). tr(RunFun, ControlFun) -> @@ -960,43 +905,43 @@ collect([]) -> collect([TM | TMs]) -> io:format( "Expecting: ~p~n", [TM]), receive - M0 -> - M = case element(1, M0) of - trace_ts -> - list_to_tuple(lists:reverse( - tl(lists:reverse(tuple_to_list(M0))))); - _ -> M0 - end, - case is_function(TM,1) of - true -> - case (catch TM(M)) of - true -> - io:format("Got: ~p~n", [M]), - collect(TMs); - _ -> - io:format("Got unexpected: ~p~n", [M]), - flush({got_unexpected,M}) - end; - - false -> - case M of - TM -> - io:format("Got: ~p~n", [M]), - collect(TMs); - _ -> - io:format("Got unexpected: ~p~n", [M]), - flush({got_unexpected,M}) - end - end + M0 -> + M = case element(1, M0) of + trace_ts -> + list_to_tuple(lists:reverse( + tl(lists:reverse(tuple_to_list(M0))))); + _ -> M0 + end, + case is_function(TM,1) of + true -> + case (catch TM(M)) of + true -> + io:format("Got: ~p~n", [M]), + collect(TMs); + _ -> + io:format("Got unexpected: ~p~n", [M]), + flush({got_unexpected,M}) + end; + + false -> + case M of + TM -> + io:format("Got: ~p~n", [M]), + collect(TMs); + _ -> + io:format("Got unexpected: ~p~n", [M]), + flush({got_unexpected,M}) + end + end end. flush(Reason) -> receive - M -> - io:format("In queue: ~p~n", [M]), - flush(Reason) + M -> + io:format("In queue: ~p~n", [M]), + flush(Reason) after 17 -> - ct:fail(Reason) + ct:fail(Reason) end. start_collect(P) -> @@ -1007,33 +952,33 @@ stop_collect(P) -> stop_collect(P, Order) -> P ! {Order, self()}, receive - {gone, P} -> - ok + {gone, P} -> + ok end. runner(Collector, Fun) -> receive - {go, Collector} -> - go + {go, Collector} -> + go end, Fun(), receive - {done, Collector} -> - Collector ! {gone, self()} + {done, Collector} -> + Collector ! {gone, self()} end. loop_runner(Collector, Fun, Laps) -> receive - {go, Collector} -> - go + {go, Collector} -> + go end, loop_runner_cont(Collector, Fun, 0, Laps). loop_runner_cont(Collector, _Fun, Laps, Laps) -> receive - {done, Collector} -> ok; - {abort, Collector} -> ok + {done, Collector} -> ok; + {abort, Collector} -> ok end, io:format("loop_runner ~p exit after ~p laps\n", [self(), Laps]), Collector ! {gone, self()}; @@ -1041,11 +986,11 @@ loop_runner_cont(Collector, _Fun, Laps, Laps) -> loop_runner_cont(Collector, Fun, N, Laps) -> Fun(), receive - {abort, Collector} -> - io:format("loop_runner ~p aborted after ~p of ~p laps\n", [self(), N+1, Laps]), - Collector ! {gone, self()} + {abort, Collector} -> + io:format("loop_runner ~p aborted after ~p of ~p laps\n", [self(), N+1, Laps]), + Collector ! {gone, self()} after 0 -> - loop_runner_cont(Collector, Fun, N+1, Laps) + loop_runner_cont(Collector, Fun, N+1, Laps) end. @@ -1077,7 +1022,7 @@ start_node(Name) -> Pa = filename:dirname(code:which(?MODULE)), Cookie = atom_to_list(erlang:get_cookie()), test_server:start_node(Name, slave, - [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]). + [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]). stop_node(Node) -> test_server:stop_node(Node). diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 5a866e7bfa..82e3a36c1e 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -23,11 +23,11 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, groups/0, - case_1/1, case_1a/1, case_2/1, case_2a/1, mon_e_1/1, demon_e_1/1, demon_1/1, - demon_2/1, demon_3/1, demonitor_flush/1, - local_remove_monitor/1, remote_remove_monitor/1, mon_1/1, mon_2/1, - large_exit/1, list_cleanup/1, mixer/1, named_down/1, otp_5827/1, - monitor_time_offset/1]). + case_1/1, case_1a/1, case_2/1, case_2a/1, mon_e_1/1, demon_e_1/1, demon_1/1, + demon_2/1, demon_3/1, demonitor_flush/1, + local_remove_monitor/1, remote_remove_monitor/1, mon_1/1, mon_2/1, + large_exit/1, list_cleanup/1, mixer/1, named_down/1, otp_5827/1, + monitor_time_offset/1]). -export([y2/1, g/1, g0/0, g1/0, large_exit_sub/1]). @@ -48,101 +48,101 @@ groups() -> %% A monitors B, B kills A and then exits (yielded core dump) case_1(Config) when is_list(Config) -> - ?line process_flag(trap_exit, true), - ?line spawn_link(?MODULE, g0, []), - ?line receive _ -> ok end, + process_flag(trap_exit, true), + spawn_link(?MODULE, g0, []), + receive _ -> ok end, ok. %% A monitors B, B kills A and then exits (yielded core dump) case_1a(Config) when is_list(Config) -> - ?line process_flag(trap_exit, true), - ?line spawn_link(?MODULE, g1, []), - ?line receive _ -> ok end, + process_flag(trap_exit, true), + spawn_link(?MODULE, g1, []), + receive _ -> ok end, ok. g0() -> - ?line B = spawn(?MODULE, g, [self()]), - ?line erlang:monitor(process, B), - ?line B ! ok, - ?line receive ok -> ok end, + B = spawn(?MODULE, g, [self()]), + erlang:monitor(process, B), + B ! ok, + receive ok -> ok end, ok. g1() -> - ?line {B,_} = spawn_monitor(?MODULE, g, [self()]), - ?line B ! ok, - ?line receive ok -> ok end, + {B,_} = spawn_monitor(?MODULE, g, [self()]), + B ! ok, + receive ok -> ok end, ok. g(Parent) -> - ?line receive ok -> ok end, - ?line exit(Parent, foo), - ?line ok. + receive ok -> ok end, + exit(Parent, foo), + ok. %% A monitors B, B demonitors A (yielded core dump) case_2(Config) when is_list(Config) -> - ?line B = spawn(?MODULE, y2, [self()]), - ?line R = erlang:monitor(process, B), - ?line B ! R, - ?line receive - {'EXIT', _} -> ok; - Other -> - ct:fail({rec, Other}) - end, - ?line expect_down(R, B, normal), + B = spawn(?MODULE, y2, [self()]), + R = erlang:monitor(process, B), + B ! R, + receive + {'EXIT', _} -> ok; + Other -> + ct:fail({rec, Other}) + end, + expect_down(R, B, normal), ok. %% A monitors B, B demonitors A (yielded core dump) case_2a(Config) when is_list(Config) -> - ?line {B,R} = spawn_monitor(?MODULE, y2, [self()]), - ?line B ! R, - ?line receive - {'EXIT', _} -> ok; - Other -> - ct:fail({rec, Other}) - end, - ?line expect_down(R, B, normal), + {B,R} = spawn_monitor(?MODULE, y2, [self()]), + B ! R, + receive + {'EXIT', _} -> ok; + Other -> + ct:fail({rec, Other}) + end, + expect_down(R, B, normal), ok. y2(Parent) -> - ?line R = receive T -> T end, - ?line Parent ! (catch erlang:demonitor(R)), + R = receive T -> T end, + Parent ! (catch erlang:demonitor(R)), ok. expect_down(Ref, P) -> receive - {'DOWN', Ref, process, P, Reason} -> - Reason; - Other -> - ct:fail({rec, Other}) + {'DOWN', Ref, process, P, Reason} -> + Reason; + Other -> + ct:fail({rec, Other}) end. expect_down(Ref, P, Reason) -> receive - {'DOWN', Ref, process, P, Reason} -> - ok; - Other -> - ct:fail({rec, Other}) + {'DOWN', Ref, process, P, Reason} -> + ok; + Other -> + ct:fail({rec, Other}) end. expect_no_msg() -> receive - Msg -> - ct:fail({msg, Msg}) + Msg -> + ct:fail({msg, Msg}) after 0 -> - ok + ok end. %%% Error cases for monitor/2 mon_e_1(Config) when is_list(Config) -> - ?line {ok, N} = test_server:start_node(hej, slave, []), - ?line mon_error(plutt, self()), - ?line mon_error(process, [bingo]), - ?line mon_error(process, {rex, N, junk}), - ?line mon_error(process, 1), + {ok, N} = test_server:start_node(hej, slave, []), + mon_error(plutt, self()), + mon_error(process, [bingo]), + mon_error(process, {rex, N, junk}), + mon_error(process, 1), - ?line true = test_server:stop_node(N), + true = test_server:stop_node(N), ok. %%% We would also like to have a test case that tries to monitor something @@ -155,142 +155,142 @@ mon_e_1(Config) when is_list(Config) -> mon_error(Type, Item) -> case catch erlang:monitor(Type, Item) of - {'EXIT', _} -> - ok; - Other -> - ct:fail({err, Other}) + {'EXIT', _} -> + ok; + Other -> + ct:fail({err, Other}) end. %%% Error cases for demonitor/1 demon_e_1(Config) when is_list(Config) -> - ?line {ok, N} = test_server:start_node(hej, slave, []), - ?line demon_error(plutt, badarg), - ?line demon_error(1, badarg), + {ok, N} = test_server:start_node(hej, slave, []), + demon_error(plutt, badarg), + demon_error(1, badarg), %% Demonitor with ref created at other node - ?line R1 = rpc:call(N, erlang, make_ref, []), - ?line demon_error(R1, badarg), + R1 = rpc:call(N, erlang, make_ref, []), + demon_error(R1, badarg), %% Demonitor with ref created at wrong monitor link end - ?line P0 = self(), - ?line P2 = spawn( - fun() -> - P0 ! {self(), ref, erlang:monitor(process,P0)}, - receive {P0, stop} -> ok end - end ), - ?line receive - {P2, ref, R2} -> - ?line demon_error(R2, badarg), - ?line P2 ! {self(), stop}; - Other2 -> - ct:fail({rec, Other2}) - end, - - ?line true = test_server:stop_node(N), + P0 = self(), + P2 = spawn( + fun() -> + P0 ! {self(), ref, erlang:monitor(process,P0)}, + receive {P0, stop} -> ok end + end ), + receive + {P2, ref, R2} -> + demon_error(R2, badarg), + P2 ! {self(), stop}; + Other2 -> + ct:fail({rec, Other2}) + end, + + true = test_server:stop_node(N), ok. demon_error(Ref, Reason) -> case catch erlang:demonitor(Ref) of - {'EXIT', {Reason, _}} -> - ok; - Other -> - ct:fail({err, Other}) + {'EXIT', {Reason, _}} -> + ok; + Other -> + ct:fail({err, Other}) end. %%% No-op cases for demonitor/1 demon_1(Config) when is_list(Config) -> - ?line true = erlang:demonitor(make_ref()), + true = erlang:demonitor(make_ref()), ok. %%% Cases for demonitor/1 demon_2(Config) when is_list(Config) -> - ?line R1 = erlang:monitor(process, self()), - ?line true = erlang:demonitor(R1), + R1 = erlang:monitor(process, self()), + true = erlang:demonitor(R1), %% Extra demonitor - ?line true = erlang:demonitor(R1), - ?line expect_no_msg(), + true = erlang:demonitor(R1), + expect_no_msg(), %% Normal 'DOWN' - ?line P2 = spawn(timer, sleep, [1]), - ?line R2 = erlang:monitor(process, P2), - ?line case expect_down(R2, P2) of - normal -> ?line ok; - noproc -> ?line ok; - BadReason -> ?line ct:fail({bad_reason, BadReason}) - end, - -%% OTP-5772 -% %% 'DOWN' before demonitor -% ?line P3 = spawn(timer, sleep, [100000]), -% ?line R3 = erlang:monitor(process, P3), -% ?line exit(P3, frop), -% ?line erlang:demonitor(R3), -% ?line expect_down(R3, P3, frop), + P2 = spawn(timer, sleep, [1]), + R2 = erlang:monitor(process, P2), + case expect_down(R2, P2) of + normal -> ok; + noproc -> ok; + BadReason -> ct:fail({bad_reason, BadReason}) + end, + + %% OTP-5772 + % %% 'DOWN' before demonitor + % P3 = spawn(timer, sleep, [100000]), + % R3 = erlang:monitor(process, P3), + % exit(P3, frop), + % erlang:demonitor(R3), + % expect_down(R3, P3, frop), %% Demonitor before 'DOWN' - ?line P4 = spawn(timer, sleep, [100000]), - ?line R4 = erlang:monitor(process, P4), - ?line erlang:demonitor(R4), - ?line exit(P4, frop), - ?line expect_no_msg(), + P4 = spawn(timer, sleep, [100000]), + R4 = erlang:monitor(process, P4), + erlang:demonitor(R4), + exit(P4, frop), + expect_no_msg(), ok. %% Distributed case for demonitor/1 (OTP-3499) demon_3(Config) when is_list(Config) -> - ?line {ok, N} = test_server:start_node(hej, slave, []), + {ok, N} = test_server:start_node(hej, slave, []), %% 'DOWN' before demonitor - ?line P2 = spawn(N, timer, sleep, [100000]), - ?line R2 = erlang:monitor(process, P2), - ?line true = test_server:stop_node(N), - ?line true = erlang:demonitor(R2), - ?line expect_down(R2, P2, noconnection), + P2 = spawn(N, timer, sleep, [100000]), + R2 = erlang:monitor(process, P2), + true = test_server:stop_node(N), + true = erlang:demonitor(R2), + expect_down(R2, P2, noconnection), - ?line {ok, N2} = test_server:start_node(hej, slave, []), + {ok, N2} = test_server:start_node(hej, slave, []), %% Demonitor before 'DOWN' - ?line P3 = spawn(N2, timer, sleep, [100000]), - ?line R3 = erlang:monitor(process, P3), - ?line true = erlang:demonitor(R3), - ?line true = test_server:stop_node(N2), - ?line expect_no_msg(), + P3 = spawn(N2, timer, sleep, [100000]), + R3 = erlang:monitor(process, P3), + true = erlang:demonitor(R3), + true = test_server:stop_node(N2), + expect_no_msg(), ok. demonitor_flush(Config) when is_list(Config) -> - ?line {'EXIT', {badarg, _}} = (catch erlang:demonitor(make_ref(), flush)), - ?line {'EXIT', {badarg, _}} = (catch erlang:demonitor(make_ref(), [flus])), - ?line {'EXIT', {badarg, _}} = (catch erlang:demonitor(x, [flush])), - ?line {ok, N} = test_server:start_node(demonitor_flush, slave, []), - ?line ok = demonitor_flush_test(N), - ?line true = test_server:stop_node(N), - ?line ok = demonitor_flush_test(node()). - + {'EXIT', {badarg, _}} = (catch erlang:demonitor(make_ref(), flush)), + {'EXIT', {badarg, _}} = (catch erlang:demonitor(make_ref(), [flus])), + {'EXIT', {badarg, _}} = (catch erlang:demonitor(x, [flush])), + {ok, N} = test_server:start_node(demonitor_flush, slave, []), + ok = demonitor_flush_test(N), + true = test_server:stop_node(N), + ok = demonitor_flush_test(node()). + demonitor_flush_test(Node) -> - ?line P = spawn(Node, timer, sleep, [100000]), - ?line M1 = erlang:monitor(process, P), - ?line M2 = erlang:monitor(process, P), - ?line M3 = erlang:monitor(process, P), - ?line M4 = erlang:monitor(process, P), - ?line true = erlang:demonitor(M1, [flush, flush]), - ?line exit(P, bang), - ?line receive {'DOWN', M2, process, P, bang} -> ok end, - ?line receive after 100 -> ok end, - ?line true = erlang:demonitor(M3, [flush]), - ?line true = erlang:demonitor(M4, []), - ?line receive {'DOWN', M4, process, P, bang} -> ok end, - ?line receive - {'DOWN', M, _, _, _} =DM when M == M1, - M == M3 -> - ?line ct:fail({unexpected_down_message, DM}) - after 100 -> - ?line ok - end. + P = spawn(Node, timer, sleep, [100000]), + M1 = erlang:monitor(process, P), + M2 = erlang:monitor(process, P), + M3 = erlang:monitor(process, P), + M4 = erlang:monitor(process, P), + true = erlang:demonitor(M1, [flush, flush]), + exit(P, bang), + receive {'DOWN', M2, process, P, bang} -> ok end, + receive after 100 -> ok end, + true = erlang:demonitor(M3, [flush]), + true = erlang:demonitor(M4, []), + receive {'DOWN', M4, process, P, bang} -> ok end, + receive + {'DOWN', M, _, _, _} =DM when M == M1, + M == M3 -> + ct:fail({unexpected_down_message, DM}) + after 100 -> + ok + end. -define(RM_MON_GROUPS, 100). -define(RM_MON_GPROCS, 100). @@ -298,33 +298,33 @@ demonitor_flush_test(Node) -> local_remove_monitor(Config) when is_list(Config) -> Gs = generate(fun () -> start_remove_monitor_group(node()) end, - ?RM_MON_GROUPS), + ?RM_MON_GROUPS), {True, False} = lists:foldl(fun (G, {T, F}) -> - receive - {rm_mon_res, G, {GT, GF}} -> - {T+GT, F+GF} - end - end, - {0, 0}, - Gs), + receive + {rm_mon_res, G, {GT, GF}} -> + {T+GT, F+GF} + end + end, + {0, 0}, + Gs), erlang:display({local_remove_monitor, True, False}), {comment, "True = "++integer_to_list(True)++"; False = "++integer_to_list(False)}. - + remote_remove_monitor(Config) when is_list(Config) -> - ?line {ok, N} = test_server:start_node(demonitor_flush, slave, []), + {ok, N} = test_server:start_node(demonitor_flush, slave, []), Gs = generate(fun () -> start_remove_monitor_group(node()) end, - ?RM_MON_GROUPS), + ?RM_MON_GROUPS), {True, False} = lists:foldl(fun (G, {T, F}) -> - receive - {rm_mon_res, G, {GT, GF}} -> - {T+GT, F+GF} - end - end, - {0, 0}, - Gs), + receive + {rm_mon_res, G, {GT, GF}} -> + {T+GT, F+GF} + end + end, + {0, 0}, + Gs), erlang:display({remote_remove_monitor, True, False}), - ?line true = test_server:stop_node(N), + true = test_server:stop_node(N), {comment, "True = "++integer_to_list(True)++"; False = "++integer_to_list(False)}. @@ -332,153 +332,153 @@ start_remove_monitor_group(Node) -> Master = self(), spawn_link( fun () -> - Ms = generate(fun () -> - P = spawn(Node, fun () -> ok end), - erlang:monitor(process, P) - end, ?RM_MON_GPROCS), - Res = lists:foldl(fun (M, {T, F}) -> - case erlang:demonitor(M, [info]) of - true -> - receive - {'DOWN', M, _, _, _} -> - exit(down_msg_found) - after 0 -> - ok - end, - {T+1, F}; - false -> - receive - {'DOWN', M, _, _, _} -> - ok - after 0 -> - exit(no_down_msg_found) - end, - {T, F+1} - end - end, - {0,0}, - Ms), - Master ! {rm_mon_res, self(), Res} + Ms = generate(fun () -> + P = spawn(Node, fun () -> ok end), + erlang:monitor(process, P) + end, ?RM_MON_GPROCS), + Res = lists:foldl(fun (M, {T, F}) -> + case erlang:demonitor(M, [info]) of + true -> + receive + {'DOWN', M, _, _, _} -> + exit(down_msg_found) + after 0 -> + ok + end, + {T+1, F}; + false -> + receive + {'DOWN', M, _, _, _} -> + ok + after 0 -> + exit(no_down_msg_found) + end, + {T, F+1} + end + end, + {0,0}, + Ms), + Master ! {rm_mon_res, self(), Res} end). - - + + %%% Cases for monitor/2 mon_1(Config) when is_list(Config) -> %% Normal case - ?line P2 = spawn(timer, sleep, [1]), - ?line R2 = erlang:monitor(process, P2), - ?line case expect_down(R2, P2) of - normal -> ?line ok; - noproc -> ?line ok; - BadReason -> ?line ct:fail({bad_reason, BadReason}) - end, - ?line {P2A,R2A} = spawn_monitor(timer, sleep, [1]), - ?line expect_down(R2A, P2A, normal), + P2 = spawn(timer, sleep, [1]), + R2 = erlang:monitor(process, P2), + case expect_down(R2, P2) of + normal -> ok; + noproc -> ok; + BadReason -> ct:fail({bad_reason, BadReason}) + end, + {P2A,R2A} = spawn_monitor(timer, sleep, [1]), + expect_down(R2A, P2A, normal), %% 'DOWN' with other reason - ?line P3 = spawn(timer, sleep, [100000]), - ?line R3 = erlang:monitor(process, P3), - ?line exit(P3, frop), - ?line expect_down(R3, P3, frop), - ?line {P3A,R3A} = spawn_monitor(timer, sleep, [100000]), - ?line exit(P3A, frop), - ?line expect_down(R3A, P3A, frop), + P3 = spawn(timer, sleep, [100000]), + R3 = erlang:monitor(process, P3), + exit(P3, frop), + expect_down(R3, P3, frop), + {P3A,R3A} = spawn_monitor(timer, sleep, [100000]), + exit(P3A, frop), + expect_down(R3A, P3A, frop), %% Monitor fails because process is dead - ?line R4 = erlang:monitor(process, P3), - ?line expect_down(R4, P3, noproc), + R4 = erlang:monitor(process, P3), + expect_down(R4, P3, noproc), %% Normal case (named process) - ?line P5 = start_jeeves(jeeves), - ?line R5 = erlang:monitor(process, jeeves), - ?line tell_jeeves(P5, stop), - ?line expect_down(R5, {jeeves, node()}, normal), + P5 = start_jeeves(jeeves), + R5 = erlang:monitor(process, jeeves), + tell_jeeves(P5, stop), + expect_down(R5, {jeeves, node()}, normal), %% 'DOWN' with other reason and node explicit activation - ?line P6 = start_jeeves(jeeves), - ?line R6 = erlang:monitor(process, {jeeves, node()}), - ?line tell_jeeves(P6, {exit, frop}), - ?line expect_down(R6, {jeeves, node()}, frop), + P6 = start_jeeves(jeeves), + R6 = erlang:monitor(process, {jeeves, node()}), + tell_jeeves(P6, {exit, frop}), + expect_down(R6, {jeeves, node()}, frop), %% Monitor (named process) fails because process is dead - ?line R7 = erlang:monitor(process, {jeeves, node()}), - ?line expect_down(R7, {jeeves, node()}, noproc), + R7 = erlang:monitor(process, {jeeves, node()}), + expect_down(R7, {jeeves, node()}, noproc), ok. %% Distributed cases for monitor/2 mon_2(Config) when is_list(Config) -> - ?line {ok, N1} = test_server:start_node(hej1, slave, []), + {ok, N1} = test_server:start_node(hej1, slave, []), %% Normal case - ?line P2 = spawn(N1, timer, sleep, [4000]), - ?line R2 = erlang:monitor(process, P2), - ?line expect_down(R2, P2, normal), + P2 = spawn(N1, timer, sleep, [4000]), + R2 = erlang:monitor(process, P2), + expect_down(R2, P2, normal), %% 'DOWN' with other reason - ?line P3 = spawn(N1, timer, sleep, [100000]), - ?line R3 = erlang:monitor(process, P3), - ?line exit(P3, frop), - ?line expect_down(R3, P3, frop), + P3 = spawn(N1, timer, sleep, [100000]), + R3 = erlang:monitor(process, P3), + exit(P3, frop), + expect_down(R3, P3, frop), %% Monitor fails because process is dead - ?line R4 = erlang:monitor(process, P3), - ?line expect_down(R4, P3, noproc), + R4 = erlang:monitor(process, P3), + expect_down(R4, P3, noproc), %% Other node goes down - ?line P5 = spawn(N1, timer, sleep, [100000]), - ?line R5 = erlang:monitor(process, P5), + P5 = spawn(N1, timer, sleep, [100000]), + R5 = erlang:monitor(process, P5), - ?line true = test_server:stop_node(N1), + true = test_server:stop_node(N1), - ?line expect_down(R5, P5, noconnection), + expect_down(R5, P5, noconnection), %% Monitor fails because other node is dead - ?line P6 = spawn(N1, timer, sleep, [100000]), - ?line R6 = erlang:monitor(process, P6), - ?line R6_Reason = expect_down(R6, P6), - ?line true = (R6_Reason == noconnection) orelse (R6_Reason == noproc), + P6 = spawn(N1, timer, sleep, [100000]), + R6 = erlang:monitor(process, P6), + R6_Reason = expect_down(R6, P6), + true = (R6_Reason == noconnection) orelse (R6_Reason == noproc), %% Start a new node that can load code in this module - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, N2} = test_server:start_node - (hej2, slave, [{args, "-pa " ++ PA}]), + PA = filename:dirname(code:which(?MODULE)), + {ok, N2} = test_server:start_node + (hej2, slave, [{args, "-pa " ++ PA}]), %% Normal case (named process) - ?line P7 = start_jeeves({jeeves, N2}), - ?line R7 = erlang:monitor(process, {jeeves, N2}), - ?line tell_jeeves(P7, stop), - ?line expect_down(R7, {jeeves, N2}, normal), + P7 = start_jeeves({jeeves, N2}), + R7 = erlang:monitor(process, {jeeves, N2}), + tell_jeeves(P7, stop), + expect_down(R7, {jeeves, N2}, normal), %% 'DOWN' with other reason (named process) - ?line P8 = start_jeeves({jeeves, N2}), - ?line R8 = erlang:monitor(process, {jeeves, N2}), - ?line tell_jeeves(P8, {exit, frop}), - ?line expect_down(R8, {jeeves, N2}, frop), + P8 = start_jeeves({jeeves, N2}), + R8 = erlang:monitor(process, {jeeves, N2}), + tell_jeeves(P8, {exit, frop}), + expect_down(R8, {jeeves, N2}, frop), %% Monitor (named process) fails because process is dead - ?line R9 = erlang:monitor(process, {jeeves, N2}), - ?line expect_down(R9, {jeeves, N2}, noproc), + R9 = erlang:monitor(process, {jeeves, N2}), + expect_down(R9, {jeeves, N2}, noproc), %% Other node goes down (named process) - ?line _P10 = start_jeeves({jeeves, N2}), - ?line R10 = erlang:monitor(process, {jeeves, N2}), + _P10 = start_jeeves({jeeves, N2}), + R10 = erlang:monitor(process, {jeeves, N2}), - ?line true = test_server:stop_node(N2), + true = test_server:stop_node(N2), - ?line expect_down(R10, {jeeves, N2}, noconnection), + expect_down(R10, {jeeves, N2}, noconnection), %% Monitor (named process) fails because other node is dead - ?line R11 = erlang:monitor(process, {jeeves, N2}), - ?line expect_down(R11, {jeeves, N2}, noconnection), + R11 = erlang:monitor(process, {jeeves, N2}), + expect_down(R11, {jeeves, N2}, noconnection), ok. %%% Large exit reason. Crashed first attempt to release R5B. large_exit(Config) when is_list(Config) -> - ?line f(100), + f(100), ok. f(0) -> @@ -488,23 +488,23 @@ f(N) -> f(N-1). f() -> - ?line S0 = {big, tuple, with, [list, 4563784278]}, - ?line S = {S0, term_to_binary(S0)}, - ?line P = spawn(?MODULE, large_exit_sub, [S]), - ?line R = erlang:monitor(process, P), - ?line P ! hej, + S0 = {big, tuple, with, [list, 4563784278]}, + S = {S0, term_to_binary(S0)}, + P = spawn(?MODULE, large_exit_sub, [S]), + R = erlang:monitor(process, P), + P ! hej, receive - {'DOWN', R, process, P, X} -> - ?line io:format(" -> ~p~n", [X]), - if - X == S -> - ok; - true -> - ct:fail({X, S}) - end; - Other -> - ?line io:format(" -> ~p~n", [Other]), - exit({answer, Other}) + {'DOWN', R, process, P, X} -> + io:format(" -> ~p~n", [X]), + if + X == S -> + ok; + true -> + ct:fail({X, S}) + end; + Other -> + io:format(" -> ~p~n", [Other]), + exit({answer, Other}) end. large_exit_sub(S) -> @@ -516,96 +516,96 @@ large_exit_sub(S) -> %%% and erlang:process_info(self(), monitored_by) list_cleanup(Config) when is_list(Config) -> - ?line P0 = self(), - ?line M = node(), - ?line PA = filename:dirname(code:which(?MODULE)), - ?line true = register(master_bertie, self()), + P0 = self(), + M = node(), + PA = filename:dirname(code:which(?MODULE)), + true = register(master_bertie, self()), %% Normal local case, monitor and demonitor - ?line P1 = start_jeeves(jeeves), - ?line {[], []} = monitors(), - ?line expect_jeeves(P1, monitors, {monitors, {[], []}}), - ?line R1a = erlang:monitor(process, P1), - ?line {[{process, P1}], []} = monitors(), - ?line expect_jeeves(P1, monitors, {monitors, {[], [P0]}}), - ?line true = erlang:demonitor(R1a), - ?line expect_no_msg(), - ?line {[], []} = monitors(), - ?line expect_jeeves(P1, monitors, {monitors, {[], []}}), + P1 = start_jeeves(jeeves), + {[], []} = monitors(), + expect_jeeves(P1, monitors, {monitors, {[], []}}), + R1a = erlang:monitor(process, P1), + {[{process, P1}], []} = monitors(), + expect_jeeves(P1, monitors, {monitors, {[], [P0]}}), + true = erlang:demonitor(R1a), + expect_no_msg(), + {[], []} = monitors(), + expect_jeeves(P1, monitors, {monitors, {[], []}}), %% Remonitor named and try again, now exiting the monitored process - ?line R1b = erlang:monitor(process, jeeves), - ?line {[{process, {jeeves, M}}], []} = monitors(), - ?line expect_jeeves(P1, monitors, {monitors, {[], [P0]}}), - ?line tell_jeeves(P1, stop), - ?line expect_down(R1b, {jeeves, node()}, normal), - ?line {[], []} = monitors(), + R1b = erlang:monitor(process, jeeves), + {[{process, {jeeves, M}}], []} = monitors(), + expect_jeeves(P1, monitors, {monitors, {[], [P0]}}), + tell_jeeves(P1, stop), + expect_down(R1b, {jeeves, node()}, normal), + {[], []} = monitors(), %% Slightly weird local case - the monitoring process crashes - ?line P2 = start_jeeves(jeeves), - ?line {[], []} = monitors(), - ?line expect_jeeves(P2, monitors, {monitors, {[], []}}), - ?line {monitor_process, _R2} = - ask_jeeves(P2, {monitor_process, master_bertie}), - ?line {[], [P2]} = monitors(), - ?line expect_jeeves(P2, monitors, - {monitors, {[{process, {master_bertie, node()}}], []}}), - ?line tell_jeeves(P2, {exit, frop}), + P2 = start_jeeves(jeeves), + {[], []} = monitors(), + expect_jeeves(P2, monitors, {monitors, {[], []}}), + {monitor_process, _R2} = + ask_jeeves(P2, {monitor_process, master_bertie}), + {[], [P2]} = monitors(), + expect_jeeves(P2, monitors, + {monitors, {[{process, {master_bertie, node()}}], []}}), + tell_jeeves(P2, {exit, frop}), timer:sleep(2000), - ?line {[], []} = monitors(), + {[], []} = monitors(), %% Start a new node that can load code in this module - ?line {ok, J} = test_server:start_node - (jeeves, slave, [{args, "-pa " ++ PA}]), + {ok, J} = test_server:start_node + (jeeves, slave, [{args, "-pa " ++ PA}]), %% Normal remote case, monitor and demonitor - ?line P3 = start_jeeves({jeeves, J}), - ?line {[], []} = monitors(), - ?line expect_jeeves(P3, monitors, {monitors, {[], []}}), - ?line R3a = erlang:monitor(process, P3), - ?line {[{process, P3}], []} = monitors(), - ?line expect_jeeves(P3, monitors, {monitors, {[], [P0]}}), - ?line true = erlang:demonitor(R3a), - ?line expect_no_msg(), - ?line {[], []} = monitors(), - ?line expect_jeeves(P3, monitors, {monitors, {[], []}}), + P3 = start_jeeves({jeeves, J}), + {[], []} = monitors(), + expect_jeeves(P3, monitors, {monitors, {[], []}}), + R3a = erlang:monitor(process, P3), + {[{process, P3}], []} = monitors(), + expect_jeeves(P3, monitors, {monitors, {[], [P0]}}), + true = erlang:demonitor(R3a), + expect_no_msg(), + {[], []} = monitors(), + expect_jeeves(P3, monitors, {monitors, {[], []}}), %% Remonitor named and try again, now exiting the monitored process - ?line R3b = erlang:monitor(process, {jeeves, J}), - ?line {[{process, {jeeves, J}}], []} = monitors(), - ?line expect_jeeves(P3, monitors, {monitors, {[], [P0]}}), - ?line tell_jeeves(P3, stop), - ?line expect_down(R3b, {jeeves, J}, normal), - ?line {[], []} = monitors(), + R3b = erlang:monitor(process, {jeeves, J}), + {[{process, {jeeves, J}}], []} = monitors(), + expect_jeeves(P3, monitors, {monitors, {[], [P0]}}), + tell_jeeves(P3, stop), + expect_down(R3b, {jeeves, J}, normal), + {[], []} = monitors(), %% Slightly weird remote case - the monitoring process crashes - ?line P4 = start_jeeves({jeeves, J}), - ?line {[], []} = monitors(), - ?line expect_jeeves(P4, monitors, {monitors, {[], []}}), - ?line {monitor_process, _R4} = - ask_jeeves(P4, {monitor_process, {master_bertie, M}}), - ?line {[], [P4]} = monitors(), - ?line expect_jeeves(P4, monitors, - {monitors, {[{process, {master_bertie, M}}], []}} ), - ?line tell_jeeves(P4, {exit, frop}), + P4 = start_jeeves({jeeves, J}), + {[], []} = monitors(), + expect_jeeves(P4, monitors, {monitors, {[], []}}), + {monitor_process, _R4} = + ask_jeeves(P4, {monitor_process, {master_bertie, M}}), + {[], [P4]} = monitors(), + expect_jeeves(P4, monitors, + {monitors, {[{process, {master_bertie, M}}], []}} ), + tell_jeeves(P4, {exit, frop}), timer:sleep(2000), - ?line {[], []} = monitors(), - + {[], []} = monitors(), + %% Now, the monitoring remote node crashes - ?line P5 = start_jeeves({jeeves, J}), - ?line {[], []} = monitors(), - ?line expect_jeeves(P5, monitors, {monitors, {[], []}}), - ?line {monitor_process, _R5} = - ask_jeeves(P5, {monitor_process, P0}), - ?line {[], [P5]} = monitors(), - ?line expect_jeeves(P5, monitors, - {monitors, {[{process, P0}], []}} ), - ?line test_server:stop_node(J), + P5 = start_jeeves({jeeves, J}), + {[], []} = monitors(), + expect_jeeves(P5, monitors, {monitors, {[], []}}), + {monitor_process, _R5} = + ask_jeeves(P5, {monitor_process, P0}), + {[], [P5]} = monitors(), + expect_jeeves(P5, monitors, + {monitors, {[{process, P0}], []}} ), + test_server:stop_node(J), timer:sleep(4000), - ?line {[], []} = monitors(), - - ?line true = unregister(master_bertie), + {[], []} = monitors(), + + true = unregister(master_bertie), ok. - + %%% Mixed internal and external monitors mixer(Config) when is_list(Config) -> @@ -694,109 +694,109 @@ mixer(Config) when is_list(Config) -> %% Test that DOWN message for a named monitor isn't %% delivered until name has been unregistered named_down(Config) when is_list(Config) -> - ?line Name = list_to_atom(atom_to_list(?MODULE) - ++ "-named_down-" - ++ integer_to_list(erlang:system_time(seconds)) - ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), - ?line Prio = process_flag(priority,high), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-named_down-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), + Prio = process_flag(priority,high), %% Spawn a bunch of high prio cpu bound processes to prevent %% normal prio processes from terminating during the next %% 500 ms... - ?line Self = self(), - ?line spawn_opt(fun () -> - WFun = fun - (F, hej) -> F(F, hopp); - (F, hopp) -> F(F, hej) - end, - NoSchedulers = erlang:system_info(schedulers_online), - lists:foreach(fun (_) -> - spawn_opt(fun () -> - WFun(WFun, - hej) - end, - [{priority,high}, - link]) - end, - lists:seq(1, NoSchedulers)), - receive after 500 -> ok end, - unlink(Self), - exit(bang) - end, - [{priority,high}, link]), - ?line NamedProc = spawn_link(fun () -> - receive after infinity -> ok end - end), - ?line true = register(Name, NamedProc), - ?line unlink(NamedProc), - ?line exit(NamedProc, bang), - ?line Mon = erlang:monitor(process, Name), - ?line receive {'DOWN',Mon, _, _, _} -> ok end, - ?line true = register(Name, self()), - ?line true = unregister(Name), - ?line process_flag(priority,Prio), + Self = self(), + spawn_opt(fun () -> + WFun = fun + (F, hej) -> F(F, hopp); +(F, hopp) -> F(F, hej) + end, + NoSchedulers = erlang:system_info(schedulers_online), + lists:foreach(fun (_) -> + spawn_opt(fun () -> + WFun(WFun, + hej) + end, + [{priority,high}, + link]) + end, + lists:seq(1, NoSchedulers)), + receive after 500 -> ok end, + unlink(Self), + exit(bang) + end, + [{priority,high}, link]), + NamedProc = spawn_link(fun () -> + receive after infinity -> ok end + end), + true = register(Name, NamedProc), + unlink(NamedProc), + exit(NamedProc, bang), + Mon = erlang:monitor(process, Name), + receive {'DOWN',Mon, _, _, _} -> ok end, + true = register(Name, self()), + true = unregister(Name), + process_flag(priority,Prio), ok. otp_5827(Config) when is_list(Config) -> %% Make a pid with the same nodename but with another creation - ?line [CreEnd | RPTail] - = lists:reverse(binary_to_list(term_to_binary(self()))), - ?line NewCreEnd = case CreEnd of - 0 -> 1; - 1 -> 2; - _ -> CreEnd - 1 - end, - ?line OtherCreationPid - = binary_to_term(list_to_binary(lists:reverse([NewCreEnd | RPTail]))), + [CreEnd | RPTail] + = lists:reverse(binary_to_list(term_to_binary(self()))), + NewCreEnd = case CreEnd of + 0 -> 1; + 1 -> 2; + _ -> CreEnd - 1 + end, + OtherCreationPid + = binary_to_term(list_to_binary(lists:reverse([NewCreEnd | RPTail]))), %% If the bug is present erlang:monitor(process, OtherCreationPid) %% will hang... - ?line Parent = self(), - ?line Ok = make_ref(), - ?line spawn(fun () -> - Mon = erlang:monitor(process, OtherCreationPid), - % Should get the DOWN message right away - receive - {'DOWN', Mon, process, OtherCreationPid, noproc} -> - Parent ! Ok - end - end), - ?line receive - Ok -> - ?line ok - after 1000 -> - ?line ct:fail("erlang:monitor/2 hangs") - end. + Parent = self(), + Ok = make_ref(), + spawn(fun () -> + Mon = erlang:monitor(process, OtherCreationPid), + % Should get the DOWN message right away + receive + {'DOWN', Mon, process, OtherCreationPid, noproc} -> + Parent ! Ok + end + end), + receive + Ok -> + ok + after 1000 -> + ct:fail("erlang:monitor/2 hangs") + end. monitor_time_offset(Config) when is_list(Config) -> {ok, Node} = start_node(Config, "+C single_time_warp"), Me = self(), PMs = lists:map(fun (_) -> - Pid = spawn(Node, - fun () -> - check_monitor_time_offset(Me) - end), - {Pid, erlang:monitor(process, Pid)} - end, - lists:seq(1, 100)), + Pid = spawn(Node, + fun () -> + check_monitor_time_offset(Me) + end), + {Pid, erlang:monitor(process, Pid)} + end, + lists:seq(1, 100)), lists:foreach(fun ({P, _M}) -> - P ! check_no_change_message - end, PMs), + P ! check_no_change_message + end, PMs), lists:foreach(fun ({P, M}) -> - receive - {no_change_message_received, P} -> - ok; - {'DOWN', M, process, P, Reason} -> - ct:fail(Reason) - end - end, PMs), + receive + {no_change_message_received, P} -> + ok; + {'DOWN', M, process, P, Reason} -> + ct:fail(Reason) + end + end, PMs), preliminary = rpc:call(Node, erlang, system_flag, [time_offset, finalize]), lists:foreach(fun ({P, M}) -> - receive - {change_messages_received, P} -> - erlang:demonitor(M, [flush]); - {'DOWN', M, process, P, Reason} -> - ct:fail(Reason) - end - end, PMs), + receive + {change_messages_received, P} -> + erlang:demonitor(M, [flush]); + {'DOWN', M, process, P, Reason} -> + ct:fail(Reason) + end + end, PMs), stop_node(Node), ok. @@ -807,42 +807,42 @@ check_monitor_time_offset(Leader) -> Mon4 = erlang:monitor(time_offset, clock_service), erlang:demonitor(Mon2, [flush]), - + Mon5 = erlang:monitor(time_offset, clock_service), Mon6 = erlang:monitor(time_offset, clock_service), Mon7 = erlang:monitor(time_offset, clock_service), receive check_no_change_message -> ok end, receive - {'CHANGE', _, time_offset, clock_service, _} -> - exit(unexpected_change_message_received) + {'CHANGE', _, time_offset, clock_service, _} -> + exit(unexpected_change_message_received) after 0 -> - Leader ! {no_change_message_received, self()} + Leader ! {no_change_message_received, self()} end, receive after 100 -> ok end, erlang:demonitor(Mon4, [flush]), receive - {'CHANGE', Mon3, time_offset, clock_service, _} -> - ok + {'CHANGE', Mon3, time_offset, clock_service, _} -> + ok end, receive - {'CHANGE', Mon6, time_offset, clock_service, _} -> - ok + {'CHANGE', Mon6, time_offset, clock_service, _} -> + ok end, erlang:demonitor(Mon5, [flush]), receive - {'CHANGE', Mon7, time_offset, clock_service, _} -> - ok + {'CHANGE', Mon7, time_offset, clock_service, _} -> + ok end, receive - {'CHANGE', Mon1, time_offset, clock_service, _} -> - ok + {'CHANGE', Mon1, time_offset, clock_service, _} -> + ok end, receive - {'CHANGE', _, time_offset, clock_service, _} -> - exit(unexpected_change_message_received) + {'CHANGE', _, time_offset, clock_service, _} -> + exit(unexpected_change_message_received) after 1000 -> - ok + ok end, Leader ! {change_messages_received, self()}. @@ -856,17 +856,17 @@ wait_for_m(Monitors, MonitoredBy, N) -> {monitors,M0} = process_info(self(),monitors), {monitored_by,MB0} = process_info(self(),monitored_by), case lists:sort(M0) of - Monitors -> - case lists:sort(MB0) of - MonitoredBy -> - ok; - _ -> - receive after 100 -> ok end, - wait_for_m(Monitors,MonitoredBy,N-1) - end; - _ -> - receive after 100 -> ok end, - wait_for_m(Monitors,MonitoredBy,N-1) + Monitors -> + case lists:sort(MB0) of + MonitoredBy -> + ok; + _ -> + receive after 100 -> ok end, + wait_for_m(Monitors,MonitoredBy,N-1) + end; + _ -> + receive after 100 -> ok end, + wait_for_m(Monitors,MonitoredBy,N-1) end. % All permutations of a list... @@ -890,32 +890,32 @@ jeeves(Parent, Name, Ref) when is_pid(Parent), (is_atom(Name) or (Name =:= [])), is_reference(Ref) -> %%io:format("monitor_SUITE:jeeves(~p, ~p)~n", [Parent, Name]), case Name of - Atom when is_atom(Atom) -> - register(Name, self()); - [] -> - ok + Atom when is_atom(Atom) -> + register(Name, self()); + [] -> + ok end, Parent ! {self(), Ref}, jeeves_loop(Parent). jeeves_loop(Parent) -> receive - {Parent, monitors} -> - Parent ! {self(), {monitors, monitors()}}, - jeeves_loop(Parent); - {Parent, {monitor_process, P}} -> - Parent ! {self(), {monitor_process, - catch erlang:monitor(process, P) }}, - jeeves_loop(Parent); - {Parent, {demonitor, Ref}} -> - Parent ! {self(), {demonitor, catch erlang:demonitor(Ref)}}, - jeeves_loop(Parent); - {Parent, stop} -> - ok; - {Parent, {exit, Reason}} -> - exit(Reason); - Other -> - io:format("~p:jeeves_loop received ~p~n", [?MODULE, Other]) + {Parent, monitors} -> + Parent ! {self(), {monitors, monitors()}}, + jeeves_loop(Parent); + {Parent, {monitor_process, P}} -> + Parent ! {self(), {monitor_process, + catch erlang:monitor(process, P) }}, + jeeves_loop(Parent); + {Parent, {demonitor, Ref}} -> + Parent ! {self(), {demonitor, catch erlang:demonitor(Ref)}}, + jeeves_loop(Parent); + {Parent, stop} -> + ok; + {Parent, {exit, Reason}} -> + exit(Reason); + Other -> + io:format("~p:jeeves_loop received ~p~n", [?MODULE, Other]) end. @@ -925,10 +925,10 @@ start_jeeves({Name, Node}) Ref = make_ref(), Pid = spawn(Node, fun() -> jeeves(Parent, Name, Ref) end), receive - {Pid, Ref} -> - ok; - Other -> - ct:fail({rec, Other}) + {Pid, Ref} -> + ok; + Other -> + ct:fail({rec, Other}) end, Pid; start_jeeves(Name) when is_atom(Name) -> @@ -942,20 +942,20 @@ tell_jeeves(Pid, What) when is_pid(Pid) -> ask_jeeves(Pid, Request) when is_pid(Pid) -> Pid ! {self(), Request}, receive - {Pid, Response} -> - Response; - Other -> - ct:fail({rec, Other}) + {Pid, Response} -> + Response; + Other -> + ct:fail({rec, Other}) end. expect_jeeves(Pid, Request, Response) when is_pid(Pid) -> Pid ! {self(), Request}, receive - {Pid, Response} -> - ok; - Other -> - ct:fail({rec, Other}) + {Pid, Response} -> + ok; + Other -> + ct:fail({rec, Other}) end. @@ -981,15 +981,15 @@ start_node(Config, Args) -> ESTime = erlang:monotonic_time(1) + erlang:time_offset(1), Unique = erlang:unique_integer([positive]), Name = list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ atom_to_list(TestCase) - ++ "-" - ++ integer_to_list(ESTime) - ++ "-" - ++ integer_to_list(Unique)), + ++ "-" + ++ atom_to_list(TestCase) + ++ "-" + ++ integer_to_list(ESTime) + ++ "-" + ++ integer_to_list(Unique)), test_server:start_node(Name, - slave, - [{args, "-pa " ++ PA ++ " " ++ Args}]). + slave, + [{args, "-pa " ++ PA ++ " " ++ Args}]). stop_node(Node) -> test_server:stop_node(Node). diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl index a84ca0be56..a17b11f3bf 100644 --- a/erts/emulator/test/mtx_SUITE.erl +++ b/erts/emulator/test/mtx_SUITE.erl @@ -116,8 +116,8 @@ long_rwlock(Config) when is_list(Config) -> %% A very short run time is expected, since %% threads in the test mostly wait io:format("RunTime=~p~n", [RunTime]), - ?line true = RunTime < 400, - ?line RunTimeStr = "Run-time during test was "++integer_to_list(RunTime)++" ms.", + true = RunTime < 400, + RunTimeStr = "Run-time during test was "++integer_to_list(RunTime)++" ms.", case LLRes of ok -> {comment, RunTimeStr}; @@ -187,100 +187,100 @@ hammer_sched_long_freqread_tryrwlock_check(Config) when is_list(Config) -> hammer_sched_rwlock_test(FreqRead, LockCheck, Blocking, WaitLocked, WaitUnlocked) -> case create_rwlock(FreqRead, LockCheck) of - enotsup -> - {skipped, "Not supported."}; - RWLock -> - Onln = erlang:system_info(schedulers_online), - NWPs = case Onln div 3 of - 1 -> case Onln < 4 of - true -> 1; - false -> 2 - end; - X -> X - end, - NRPs = Onln - NWPs, - NoLockOps = ((((50000000 div Onln) - div case {Blocking, WaitLocked} of - {false, 0} -> 1; - _ -> 10 - end) - div (case WaitLocked == 0 of - true -> 1; - false -> WaitLocked*250 - end)) - div handicap()), - io:format("NoLockOps=~p~n", [NoLockOps]), - Sleep = case Blocking of - true -> NoLockOps; - false -> NoLockOps div 10 - end, - WPs = lists:map( - fun (Sched) -> - spawn_opt( - fun () -> - io:format("Writer on scheduler ~p.~n", - [Sched]), - Sched = erlang:system_info(scheduler_id), - receive go -> gone end, - hammer_sched_rwlock_proc(RWLock, - Blocking, - true, - WaitLocked, - WaitUnlocked, - NoLockOps, - Sleep), - Sched = erlang:system_info(scheduler_id) - end, - [link, {scheduler, Sched}]) - end, - lists:seq(1, NWPs)), - RPs = lists:map( - fun (Sched) -> - spawn_opt( - fun () -> - io:format("Reader on scheduler ~p.~n", - [Sched]), - Sched = erlang:system_info(scheduler_id), - receive go -> gone end, - hammer_sched_rwlock_proc(RWLock, - Blocking, - false, - WaitLocked, - WaitUnlocked, - NoLockOps, - Sleep), - Sched = erlang:system_info(scheduler_id) - end, - [link, {scheduler, Sched}]) - end, - lists:seq(NWPs + 1, NWPs + NRPs)), - Procs = WPs ++ RPs, - case {Blocking, WaitLocked} of - {_, 0} -> ok; - {false, _} -> ok; - _ -> statistics(runtime) - end, - lists:foreach(fun (P) -> P ! go end, Procs), - lists:foreach(fun (P) -> - M = erlang:monitor(process, P), - receive - {'DOWN', M, process, P, _} -> - ok - end - end, - Procs), - case {Blocking, WaitLocked} of - {_, 0} -> ok; - {false, _} -> ok; - _ -> - {_, RunTime} = statistics(runtime), - io:format("RunTime=~p~n", [RunTime]), - ?line true = RunTime < 700, - {comment, - "Run-time during test was " - ++ integer_to_list(RunTime) - ++ " ms."} - end + enotsup -> + {skipped, "Not supported."}; + RWLock -> + Onln = erlang:system_info(schedulers_online), + NWPs = case Onln div 3 of + 1 -> case Onln < 4 of + true -> 1; + false -> 2 + end; + X -> X + end, + NRPs = Onln - NWPs, + NoLockOps = ((((50000000 div Onln) + div case {Blocking, WaitLocked} of + {false, 0} -> 1; + _ -> 10 + end) + div (case WaitLocked == 0 of + true -> 1; + false -> WaitLocked*250 + end)) + div handicap()), + io:format("NoLockOps=~p~n", [NoLockOps]), + Sleep = case Blocking of + true -> NoLockOps; + false -> NoLockOps div 10 + end, + WPs = lists:map( + fun (Sched) -> + spawn_opt( + fun () -> + io:format("Writer on scheduler ~p.~n", + [Sched]), + Sched = erlang:system_info(scheduler_id), + receive go -> gone end, + hammer_sched_rwlock_proc(RWLock, + Blocking, + true, + WaitLocked, + WaitUnlocked, + NoLockOps, + Sleep), + Sched = erlang:system_info(scheduler_id) + end, + [link, {scheduler, Sched}]) + end, + lists:seq(1, NWPs)), + RPs = lists:map( + fun (Sched) -> + spawn_opt( + fun () -> + io:format("Reader on scheduler ~p.~n", + [Sched]), + Sched = erlang:system_info(scheduler_id), + receive go -> gone end, + hammer_sched_rwlock_proc(RWLock, + Blocking, + false, + WaitLocked, + WaitUnlocked, + NoLockOps, + Sleep), + Sched = erlang:system_info(scheduler_id) + end, + [link, {scheduler, Sched}]) + end, + lists:seq(NWPs + 1, NWPs + NRPs)), + Procs = WPs ++ RPs, + case {Blocking, WaitLocked} of + {_, 0} -> ok; + {false, _} -> ok; + _ -> statistics(runtime) + end, + lists:foreach(fun (P) -> P ! go end, Procs), + lists:foreach(fun (P) -> + M = erlang:monitor(process, P), + receive + {'DOWN', M, process, P, _} -> + ok + end + end, + Procs), + case {Blocking, WaitLocked} of + {_, 0} -> ok; + {false, _} -> ok; + _ -> + {_, RunTime} = statistics(runtime), + io:format("RunTime=~p~n", [RunTime]), + true = RunTime < 700, + {comment, + "Run-time during test was " + ++ integer_to_list(RunTime) + ++ " ms."} + end end. hammer_sched_rwlock_proc(_RWLock, @@ -397,65 +397,65 @@ hammer_ets_rwlock_init(_T, _N) -> hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) -> receive after 100 -> ok end, {TP, TM} = spawn_monitor( - fun () -> - _L = repeat_list( - fun () -> - Caller = self(), - T = fun () -> - Parent = self(), - hammer_ets_rwlock_put_data(), - T=ets:new(x, [public | XOpts]), - hammer_ets_rwlock_init(T, 0), - Ps0 = repeat_list( - fun () -> - spawn_link( - fun () -> - hammer_ets_rwlock_put_data(), - receive go -> ok end, - hammer_ets_rwlock_ops(T, UW, N, C, C, N), - Parent ! {done, self()}, - receive after infinity -> ok end - end) - end, - NP - case SC of - false -> 0; - _ -> 1 - end), - Ps = case SC of - false -> Ps0; - _ -> [spawn_link(fun () -> - hammer_ets_rwlock_put_data(), - receive go -> ok end, - hammer_ets_rwlock_ops(T, UW, N, SC, SC, N), - Parent ! {done, self()}, - receive after infinity -> ok end - end) | Ps0] - end, - Start = erlang:monotonic_time(), - lists:foreach(fun (P) -> P ! go end, Ps), - lists:foreach(fun (P) -> receive {done, P} -> ok end end, Ps), - Stop = erlang:monotonic_time(), - lists:foreach(fun (P) -> - unlink(P), - exit(P, bang), - M = erlang:monitor(process, P), - receive - {'DOWN', M, process, P, _} -> ok - end - end, Ps), - Res = (Stop-Start)/erlang:convert_time_unit(1,seconds,native), - Caller ! {?MODULE, self(), Res} - end, - TP = spawn_link(T), - receive - {?MODULE, TP, Res} -> - Res - end - end, - ?HAMMER_ETS_RWLOCK_REPEAT_TIMES) - end), + fun () -> + _L = repeat_list( + fun () -> + Caller = self(), + T = fun () -> + Parent = self(), + hammer_ets_rwlock_put_data(), + T=ets:new(x, [public | XOpts]), + hammer_ets_rwlock_init(T, 0), + Ps0 = repeat_list( + fun () -> + spawn_link( + fun () -> + hammer_ets_rwlock_put_data(), + receive go -> ok end, + hammer_ets_rwlock_ops(T, UW, N, C, C, N), + Parent ! {done, self()}, + receive after infinity -> ok end + end) + end, + NP - case SC of + false -> 0; + _ -> 1 + end), + Ps = case SC of + false -> Ps0; + _ -> [spawn_link(fun () -> + hammer_ets_rwlock_put_data(), + receive go -> ok end, + hammer_ets_rwlock_ops(T, UW, N, SC, SC, N), + Parent ! {done, self()}, + receive after infinity -> ok end + end) | Ps0] + end, + Start = erlang:monotonic_time(), + lists:foreach(fun (P) -> P ! go end, Ps), + lists:foreach(fun (P) -> receive {done, P} -> ok end end, Ps), + Stop = erlang:monotonic_time(), + lists:foreach(fun (P) -> + unlink(P), + exit(P, bang), + M = erlang:monitor(process, P), + receive + {'DOWN', M, process, P, _} -> ok + end + end, Ps), + Res = (Stop-Start)/erlang:convert_time_unit(1,seconds,native), + Caller ! {?MODULE, self(), Res} + end, + TP = spawn_link(T), + receive + {?MODULE, TP, Res} -> + Res + end + end, + ?HAMMER_ETS_RWLOCK_REPEAT_TIMES) + end), receive - {'DOWN', TM, process, TP, _} -> ok + {'DOWN', TM, process, TP, _} -> ok end. repeat_list(Fun, N) -> @@ -469,18 +469,17 @@ repeat_list(Fun, N, Acc) -> handicap() -> X0 = case catch (erlang:system_info(logical_processors_available) >= - erlang:system_info(schedulers_online)) of - true -> 1; - _ -> 2 - end, + erlang:system_info(schedulers_online)) of + true -> 1; + _ -> 2 + end, case erlang:system_info(build_type) of - opt -> - X0; - ReallySlow when ReallySlow == debug; - ReallySlow == valgrind; - ReallySlow == purify -> - X0*3; - _Slow -> - X0*2 + opt -> + X0; + ReallySlow when ReallySlow == debug; + ReallySlow == valgrind; + ReallySlow == purify -> + X0*3; + _Slow -> + X0*2 end. - diff --git a/erts/emulator/test/nested_SUITE.erl b/erts/emulator/test/nested_SUITE.erl index 1e7dce4352..c13e19f857 100644 --- a/erts/emulator/test/nested_SUITE.erl +++ b/erts/emulator/test/nested_SUITE.erl @@ -21,7 +21,7 @@ -module(nested_SUITE). -export([all/0, suite/0, - case_in_case/1, case_in_after/1, catch_in_catch/1, bif_in_bif/1]). + case_in_case/1, case_in_after/1, catch_in_catch/1, bif_in_bif/1]). -include_lib("common_test/include/ct.hrl"). @@ -36,55 +36,55 @@ all() -> case_in_case(suite) -> []; case_in_case(Config) when is_list(Config) -> - ?line done = search_any([a], [{a, 1}]), - ?line done = search_any([x], [{a, 1}]), + done = search_any([a], [{a, 1}]), + done = search_any([x], [{a, 1}]), ok. search_any([Key|Rest], List) -> - ?line case case lists:keysearch(Key, 1, List) of - {value, _} -> - true; - _ -> - false - end of - true -> - ok; - false -> - error; - Other -> - ct:fail({other_result, Other}) - end, - ?line search_any(Rest, List); + case case lists:keysearch(Key, 1, List) of + {value, _} -> + true; + _ -> + false + end of + true -> + ok; + false -> + error; + Other -> + ct:fail({other_result, Other}) + end, + search_any(Rest, List); search_any([], _) -> done. case_in_after(suite) -> []; case_in_after(Config) when is_list(Config) -> receive - after case {x, y, z} of - {x, y, z} -> 0 - end -> - ok - end, + after case {x, y, z} of + {x, y, z} -> 0 + end -> + ok + end, ok. %% Test a catch within a catch in the same function. catch_in_catch(Config) when is_list(Config) -> - ?line {outer, inner_exit} = catcher(), + {outer, inner_exit} = catcher(), ok. catcher() -> case (catch - case (catch ?MODULE:non_existing()) of % bogus function - {'EXIT', _} -> - inner_exit; - Res1 -> - {inner, Res1} - end) of - {'EXIT', _} -> - outer_exit; - Res2 -> - {outer, Res2} + case (catch ?MODULE:non_existing()) of % bogus function + {'EXIT', _} -> + inner_exit; + Res1 -> + {inner, Res1} + end) of + {'EXIT', _} -> + outer_exit; + Res2 -> + {outer, Res2} end. %% Test a BIF call within a BIF call. diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index c5921ca859..84f5699890 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -22,7 +22,7 @@ %%-define(line_trace,true). -define(CHECK(Exp,Got), check(Exp,Got,?LINE)). -%%-define(CHECK(Exp,Got), ?line Exp = Got). +%%-define(CHECK(Exp,Got), Exp = Got). -include_lib("common_test/include/ct.hrl"). @@ -42,8 +42,7 @@ dirty_nif_exception/1, call_dirty_nif_exception/1, nif_schedule/1, nif_exception/1, call_nif_exception/1, nif_nan_and_inf/1, nif_atom_too_long/1, - nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1 - ]). + nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1]). -export([many_args_100/100]). @@ -88,10 +87,10 @@ end_per_testcase(_Func, _Config) -> %% Basic smoke test of load_nif and a simple NIF call basic(Config) when is_list(Config) -> ensure_lib_loaded(Config), - ?line true = (lib_version() =/= undefined), - ?line [{load,1,1,101},{lib_version,1,2,102}] = call_history(), - ?line [] = call_history(), - ?line true = lists:member(?MODULE, erlang:system_info(taints)), + true = (lib_version() =/= undefined), + [{load,1,1,101},{lib_version,1,2,102}] = call_history(), + [] = call_history(), + true = lists:member(?MODULE, erlang:system_info(taints)), ok. %% Test reload callback in nif lib @@ -99,34 +98,34 @@ reload(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "nif_mod"), - ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), - ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "nif_mod"), + {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), + {module,nif_mod} = erlang:load_module(nif_mod,Bin), - ?line ok = nif_mod:load_nif_lib(Config, 1), + ok = nif_mod:load_nif_lib(Config, 1), - ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), - ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), + hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), + [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), - ?line ok = nif_mod:load_nif_lib(Config, 2), - ?line 2 = nif_mod:lib_version(), - ?line [{reload,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(), + ok = nif_mod:load_nif_lib(Config, 2), + 2 = nif_mod:lib_version(), + [{reload,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(), - ?line ok = nif_mod:load_nif_lib(Config, 1), - ?line 1 = nif_mod:lib_version(), - ?line [{reload,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(), + ok = nif_mod:load_nif_lib(Config, 1), + 1 = nif_mod:lib_version(), + [{reload,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(), - ?line true = erlang:delete_module(nif_mod), - ?line [] = nif_mod_call_history(), + true = erlang:delete_module(nif_mod), + [] = nif_mod_call_history(), - %%?line false= check_process_code(Pid, nif_mod), - ?line true = erlang:purge_module(nif_mod), - ?line [{unload,1,3,103}] = nif_mod_call_history(), + %%false= check_process_code(Pid, nif_mod), + true = erlang:purge_module(nif_mod), + [{unload,1,3,103}] = nif_mod_call_history(), - ?line true = lists:member(?MODULE, erlang:system_info(taints)), - ?line true = lists:member(nif_mod, erlang:system_info(taints)), - ?line verify_tmpmem(TmpMem), + true = lists:member(?MODULE, erlang:system_info(taints)), + true = lists:member(nif_mod, erlang:system_info(taints)), + verify_tmpmem(TmpMem), ok. %% Test upgrade callback in nif lib @@ -134,89 +133,89 @@ upgrade(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "nif_mod"), - ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), - ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "nif_mod"), + {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), + {module,nif_mod} = erlang:load_module(nif_mod,Bin), - ?line ok = nif_mod:load_nif_lib(Config, 1), - ?line {Pid,MRef} = nif_mod:start(), - ?line 1 = call(Pid,lib_version), + ok = nif_mod:load_nif_lib(Config, 1), + {Pid,MRef} = nif_mod:start(), + 1 = call(Pid,lib_version), - ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), - ?line [{load,1,1,101},{lib_version,1,2,102},{get_priv_data_ptr,1,3,103}] = nif_mod_call_history(), + hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), + [{load,1,1,101},{lib_version,1,2,102},{get_priv_data_ptr,1,3,103}] = nif_mod_call_history(), %% Module upgrade with same lib-version - ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin), - ?line undefined = nif_mod:lib_version(), - ?line 1 = call(Pid,lib_version), - ?line [{lib_version,1,4,104}] = nif_mod_call_history(), + {module,nif_mod} = erlang:load_module(nif_mod,Bin), + undefined = nif_mod:lib_version(), + 1 = call(Pid,lib_version), + [{lib_version,1,4,104}] = nif_mod_call_history(), - ?line ok = nif_mod:load_nif_lib(Config, 1), - ?line 1 = nif_mod:lib_version(), - ?line [{upgrade,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(), + ok = nif_mod:load_nif_lib(Config, 1), + 1 = nif_mod:lib_version(), + [{upgrade,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(), - ?line upgraded = call(Pid,upgrade), - ?line false = check_process_code(Pid, nif_mod), - ?line true = erlang:purge_module(nif_mod), - ?line [{unload,1,7,107}] = nif_mod_call_history(), + upgraded = call(Pid,upgrade), + false = check_process_code(Pid, nif_mod), + true = erlang:purge_module(nif_mod), + [{unload,1,7,107}] = nif_mod_call_history(), - ?line 1 = nif_mod:lib_version(), - ?line [{lib_version,1,8,108}] = nif_mod_call_history(), + 1 = nif_mod:lib_version(), + [{lib_version,1,8,108}] = nif_mod_call_history(), - ?line true = erlang:delete_module(nif_mod), - ?line [] = nif_mod_call_history(), + true = erlang:delete_module(nif_mod), + [] = nif_mod_call_history(), - ?line Pid ! die, - ?line {'DOWN', MRef, process, Pid, normal} = receive_any(), - ?line false = check_process_code(Pid, nif_mod), - ?line true = erlang:purge_module(nif_mod), - ?line [{unload,1,9,109}] = nif_mod_call_history(), + Pid ! die, + {'DOWN', MRef, process, Pid, normal} = receive_any(), + false = check_process_code(Pid, nif_mod), + true = erlang:purge_module(nif_mod), + [{unload,1,9,109}] = nif_mod_call_history(), %% Module upgrade with different lib version - ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin), - ?line undefined = nif_mod:lib_version(), - ?line {Pid2,MRef2} = nif_mod:start(), - ?line undefined = call(Pid2,lib_version), - - ?line ok = nif_mod:load_nif_lib(Config, 1), - ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), - ?line 1 = call(Pid2,lib_version), - ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102},{lib_version,1,3,103}] = nif_mod_call_history(), - - ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin), - ?line undefined = nif_mod:lib_version(), - ?line [] = nif_mod_call_history(), - ?line 1 = call(Pid2,lib_version), - ?line [{lib_version,1,4,104}] = nif_mod_call_history(), - - ?line ok = nif_mod:load_nif_lib(Config, 2), - ?line 2 = nif_mod:lib_version(), - ?line [{upgrade,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(), - - ?line 1 = call(Pid2,lib_version), - ?line [{lib_version,1,5,105}] = nif_mod_call_history(), - - ?line upgraded = call(Pid2,upgrade), - ?line false = check_process_code(Pid2, nif_mod), - ?line true = erlang:purge_module(nif_mod), - ?line [{unload,1,6,106}] = nif_mod_call_history(), - - ?line 2 = nif_mod:lib_version(), - ?line [{lib_version,2,3,203}] = nif_mod_call_history(), - - ?line true = erlang:delete_module(nif_mod), - ?line [] = nif_mod_call_history(), - - ?line Pid2 ! die, - ?line {'DOWN', MRef2, process, Pid2, normal} = receive_any(), - ?line false= check_process_code(Pid2, nif_mod), - ?line true = erlang:purge_module(nif_mod), - ?line [{unload,2,4,204}] = nif_mod_call_history(), - - ?line true = lists:member(?MODULE, erlang:system_info(taints)), - ?line true = lists:member(nif_mod, erlang:system_info(taints)), - ?line verify_tmpmem(TmpMem), + {module,nif_mod} = erlang:load_module(nif_mod,Bin), + undefined = nif_mod:lib_version(), + {Pid2,MRef2} = nif_mod:start(), + undefined = call(Pid2,lib_version), + + ok = nif_mod:load_nif_lib(Config, 1), + hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), + 1 = call(Pid2,lib_version), + [{load,1,1,101},{get_priv_data_ptr,1,2,102},{lib_version,1,3,103}] = nif_mod_call_history(), + + {module,nif_mod} = erlang:load_module(nif_mod,Bin), + undefined = nif_mod:lib_version(), + [] = nif_mod_call_history(), + 1 = call(Pid2,lib_version), + [{lib_version,1,4,104}] = nif_mod_call_history(), + + ok = nif_mod:load_nif_lib(Config, 2), + 2 = nif_mod:lib_version(), + [{upgrade,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(), + + 1 = call(Pid2,lib_version), + [{lib_version,1,5,105}] = nif_mod_call_history(), + + upgraded = call(Pid2,upgrade), + false = check_process_code(Pid2, nif_mod), + true = erlang:purge_module(nif_mod), + [{unload,1,6,106}] = nif_mod_call_history(), + + 2 = nif_mod:lib_version(), + [{lib_version,2,3,203}] = nif_mod_call_history(), + + true = erlang:delete_module(nif_mod), + [] = nif_mod_call_history(), + + Pid2 ! die, + {'DOWN', MRef2, process, Pid2, normal} = receive_any(), + false= check_process_code(Pid2, nif_mod), + true = erlang:purge_module(nif_mod), + [{unload,2,4,204}] = nif_mod_call_history(), + + true = lists:member(?MODULE, erlang:system_info(taints)), + true = lists:member(nif_mod, erlang:system_info(taints)), + verify_tmpmem(TmpMem), ok. %% Test NIF building heap fragments @@ -225,7 +224,7 @@ heap_frag(Config) when is_list(Config) -> ensure_lib_loaded(Config), heap_frag_do(1,1000000), - ?line verify_tmpmem(TmpMem), + verify_tmpmem(TmpMem), ok. heap_frag_do(N, Max) when N > Max -> @@ -240,7 +239,7 @@ heap_frag_do(N, Max) -> types(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), - ?line ok = type_test(), + ok = type_test(), lists:foreach(fun(Tpl) -> Lst = erlang:tuple_to_list(Tpl), Lst = tuple_2_list(Tpl) @@ -265,18 +264,18 @@ types(Config) when is_list(Config) -> R1 = echo_int(I), %%io:format("echo_int(~p) -> ~p\n", [I, R1]), R2 = my_echo_int(I, Limits), - ?line R1 = R2, - ?line true = (R1 =:= R2), - ?line true = (R1 == R2) + R1 = R2, + true = (R1 =:= R2), + true = (R1 == R2) end, int_list()), - ?line verify_tmpmem(TmpMem), - ?line true = (compare(-1294536544000, -1178704800000) < 0), - ?line true = (compare(-1178704800000, -1294536544000) > 0), - ?line true = (compare(-295147905179352825856, -36893488147419103232) < 0), - ?line true = (compare(-36893488147419103232, -295147905179352825856) > 0), - ?line true = (compare(-29514790517935282585612345678, -36893488147419103232) < 0), - ?line true = (compare(-36893488147419103232, -29514790517935282585612345678) > 0), + verify_tmpmem(TmpMem), + true = (compare(-1294536544000, -1178704800000) < 0), + true = (compare(-1178704800000, -1294536544000) > 0), + true = (compare(-295147905179352825856, -36893488147419103232) < 0), + true = (compare(-36893488147419103232, -295147905179352825856) > 0), + true = (compare(-29514790517935282585612345678, -36893488147419103232) < 0), + true = (compare(-36893488147419103232, -29514790517935282585612345678) > 0), ok. int_list() -> @@ -306,13 +305,13 @@ eq_cmp(A,B) -> eq_cmp_do(A,B) -> %%io:format("compare ~p and ~p\n",[A,B]), Eq = (A =:= B), - ?line Eq = is_identical(A,B), - ?line Cmp = if + Eq = is_identical(A,B), + Cmp = if A < B -> -1; A == B -> 0; A > B -> 1 end, - ?line Cmp = case compare(A,B) of + Cmp = case compare(A,B) of C when is_integer(C), C < 0 -> -1; 0 -> 0; C when is_integer(C) -> 1 @@ -323,42 +322,42 @@ eq_cmp_do(A,B) -> %% Test NIF with many arguments many_args(Config) when is_list(Config) -> TmpMem = tmpmem(), - ?line ensure_lib_loaded(Config ,1), - ?line ok = apply(?MODULE,many_args_100,lists:seq(1,100)), - ?line ok = many_args_100(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100), - ?line verify_tmpmem(TmpMem), + ensure_lib_loaded(Config ,1), + ok = apply(?MODULE,many_args_100,lists:seq(1,100)), + ok = many_args_100(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100), + verify_tmpmem(TmpMem), ok. %% Test NIF binary handling. binaries(Config) when is_list(Config) -> TmpMem = tmpmem(), - ?line ensure_lib_loaded(Config, 1), - ?line RefcBin = list_to_binary(lists:seq(1,255)), - ?line RefcBin = clone_bin(RefcBin), - ?line HeapBin = list_to_binary(lists:seq(1,20)), - ?line HeapBin = clone_bin(HeapBin), - ?line <<_:8,Sub1:6/binary,_/binary>> = RefcBin, - ?line <<_:8,Sub2:6/binary,_/binary>> = HeapBin, - ?line Sub1 = Sub2, - ?line Sub1 = clone_bin(Sub1), - ?line Sub2 = clone_bin(Sub2), - ?line <<_:9,Sub3:6/binary,_/bitstring>> = RefcBin, - ?line <<_:9,Sub4:6/binary,_/bitstring>> = HeapBin, - ?line Sub3 = Sub4, - ?line Sub3 = clone_bin(Sub3), - ?line Sub4 = clone_bin(Sub4), + ensure_lib_loaded(Config, 1), + RefcBin = list_to_binary(lists:seq(1,255)), + RefcBin = clone_bin(RefcBin), + HeapBin = list_to_binary(lists:seq(1,20)), + HeapBin = clone_bin(HeapBin), + <<_:8,Sub1:6/binary,_/binary>> = RefcBin, + <<_:8,Sub2:6/binary,_/binary>> = HeapBin, + Sub1 = Sub2, + Sub1 = clone_bin(Sub1), + Sub2 = clone_bin(Sub2), + <<_:9,Sub3:6/binary,_/bitstring>> = RefcBin, + <<_:9,Sub4:6/binary,_/bitstring>> = HeapBin, + Sub3 = Sub4, + Sub3 = clone_bin(Sub3), + Sub4 = clone_bin(Sub4), %% When NIFs get bitstring support - %%?line <<_:8,Sub5:27/bitstring,_/bitstring>> = RefcBin, - %%?line <<_:8,Sub6:27/bitstring,_/bitstring>> = HeapBin, - %%?line Sub5 = Sub6, - %%?line Sub5 = clone_bin(Sub5), - %%?line Sub6 = clone_bin(Sub6), - %%?line <<_:9,Sub7:27/bitstring,_/bitstring>> = RefcBin, - %%?line <<_:9,Sub8:27/bitstring,_/bitstring>> = HeapBin, - %%?line Sub7 = Sub8, - %%?line Sub7 = clone_bin(Sub7), - %%?line Sub8 = clone_bin(Sub8), - %%?line <<>> = clone_bin(<<>>), + %%<<_:8,Sub5:27/bitstring,_/bitstring>> = RefcBin, + %%<<_:8,Sub6:27/bitstring,_/bitstring>> = HeapBin, + %%Sub5 = Sub6, + %%Sub5 = clone_bin(Sub5), + %%Sub6 = clone_bin(Sub6), + %%<<_:9,Sub7:27/bitstring,_/bitstring>> = RefcBin, + %%<<_:9,Sub8:27/bitstring,_/bitstring>> = HeapBin, + %%Sub7 = Sub8, + %%Sub7 = clone_bin(Sub7), + %%Sub8 = clone_bin(Sub8), + %%<<>> = clone_bin(<<>>), <<_:8,SubBinA:200/binary,_/binary>> = RefcBin, <<_:9,SubBinB:200/binary,_/bitstring>> = RefcBin, @@ -371,50 +370,50 @@ binaries(Config) when is_list(Config) -> test_make_sub_bin(SubBinC), test_make_sub_bin(SubBinD), - ?line verify_tmpmem(TmpMem), + verify_tmpmem(TmpMem), ok. test_make_sub_bin(Bin) -> Size = byte_size(Bin), Rest10 = Size - 10, Rest1 = Size - 1, - ?line Bin = make_sub_bin(Bin, 0, Size), + Bin = make_sub_bin(Bin, 0, Size), <<_:10/binary,Sub0:Rest10/binary>> = Bin, - ?line Sub0 = make_sub_bin(Bin, 10, Rest10), + Sub0 = make_sub_bin(Bin, 10, Rest10), <> = Bin, - ?line Sub1 = make_sub_bin(Bin, 0, 10), + Sub1 = make_sub_bin(Bin, 0, 10), <<_:7/binary,Sub2:10/binary,_/binary>> = Bin, - ?line Sub2 = make_sub_bin(Bin, 7, 10), - ?line <<>> = make_sub_bin(Bin, 0, 0), - ?line <<>> = make_sub_bin(Bin, 10, 0), - ?line <<>> = make_sub_bin(Bin, Rest1, 0), - ?line <<>> = make_sub_bin(Bin, Size, 0), + Sub2 = make_sub_bin(Bin, 7, 10), + <<>> = make_sub_bin(Bin, 0, 0), + <<>> = make_sub_bin(Bin, 10, 0), + <<>> = make_sub_bin(Bin, Rest1, 0), + <<>> = make_sub_bin(Bin, Size, 0), ok. %% Test enif_get_string get_string(Config) when is_list(Config) -> - ?line ensure_lib_loaded(Config, 1), - ?line {7, <<"hejsan",0,_:3/binary>>} = string_to_bin("hejsan",10), - ?line {7, <<"hejsan",0,_>>} = string_to_bin("hejsan",8), - ?line {7, <<"hejsan",0>>} = string_to_bin("hejsan",7), - ?line {-6, <<"hejsa",0>>} = string_to_bin("hejsan",6), - ?line {-5, <<"hejs",0>>} = string_to_bin("hejsan",5), - ?line {-1, <<0>>} = string_to_bin("hejsan",1), - ?line {0, <<>>} = string_to_bin("hejsan",0), - ?line {1, <<0>>} = string_to_bin("",1), - ?line {0, <<>>} = string_to_bin("",0), + ensure_lib_loaded(Config, 1), + {7, <<"hejsan",0,_:3/binary>>} = string_to_bin("hejsan",10), + {7, <<"hejsan",0,_>>} = string_to_bin("hejsan",8), + {7, <<"hejsan",0>>} = string_to_bin("hejsan",7), + {-6, <<"hejsa",0>>} = string_to_bin("hejsan",6), + {-5, <<"hejs",0>>} = string_to_bin("hejsan",5), + {-1, <<0>>} = string_to_bin("hejsan",1), + {0, <<>>} = string_to_bin("hejsan",0), + {1, <<0>>} = string_to_bin("",1), + {0, <<>>} = string_to_bin("",0), ok. %% Test enif_get_atom get_atom(Config) when is_list(Config) -> - ?line ensure_lib_loaded(Config, 1), - ?line {7, <<"hejsan",0,_:3/binary>>} = atom_to_bin(hejsan,10), - ?line {7, <<"hejsan",0,_>>} = atom_to_bin(hejsan,8), - ?line {7, <<"hejsan",0>>} = atom_to_bin(hejsan,7), - ?line {0, <<_:6/binary>>} = atom_to_bin(hejsan,6), - ?line {0, <<>>} = atom_to_bin(hejsan,0), - ?line {1, <<0>>} = atom_to_bin('',1), - ?line {0, <<>>} = atom_to_bin('',0), + ensure_lib_loaded(Config, 1), + {7, <<"hejsan",0,_:3/binary>>} = atom_to_bin(hejsan,10), + {7, <<"hejsan",0,_>>} = atom_to_bin(hejsan,8), + {7, <<"hejsan",0>>} = atom_to_bin(hejsan,7), + {0, <<_:6/binary>>} = atom_to_bin(hejsan,6), + {0, <<>>} = atom_to_bin(hejsan,0), + {1, <<0>>} = atom_to_bin('',1), + {0, <<>>} = atom_to_bin('',0), ok. %% Test NIF maps handling. @@ -466,26 +465,26 @@ maps(Config) when is_list(Config) -> %% Test macros enif_make_list and enif_make_tuple api_macros(Config) when is_list(Config) -> - ?line ensure_lib_loaded(Config, 1), + ensure_lib_loaded(Config, 1), Expected = {[lists:seq(1,N) || N <- lists:seq(1,9)], [list_to_tuple(lists:seq(1,N)) || N <- lists:seq(1,9)] }, - ?line Expected = macros(list_to_tuple(lists:seq(1,9))), + Expected = macros(list_to_tuple(lists:seq(1,9))), ok. %% enif_make_[tuple|list]_from_array from_array(Config) when is_list(Config) -> - ?line ensure_lib_loaded(Config, 1), + ensure_lib_loaded(Config, 1), lists:foreach(fun(Tpl) -> Lst = tuple_to_list(Tpl), - ?line {Lst,Tpl} = tuple_2_list_and_tuple(Tpl) + {Lst,Tpl} = tuple_2_list_and_tuple(Tpl) end, [{}, {1,2,3}, {[4,5],[],{},{6,7}}, {{}}, {[]}]), ok. %% enif_inspect_iolist_as_binary iolist_as_binary(Config) when is_list(Config) -> - ?line ensure_lib_loaded(Config, 1), + ensure_lib_loaded(Config, 1), TmpMem = tmpmem(), List = [<<"hejsan">>, <<>>, [], [17], [<<>>], [127,128,255,0], @@ -494,17 +493,17 @@ iolist_as_binary(Config) when is_list(Config) -> lists:foreach(fun(IoL) -> B1 = erlang:iolist_to_binary(IoL), - ?line B2 = iolist_2_bin(IoL), - ?line B1 = B2 + B2 = iolist_2_bin(IoL), + B1 = B2 end, List), - ?line verify_tmpmem(TmpMem), + verify_tmpmem(TmpMem), ok. %% Test memory managed objects, aka 'resources' resource(Config) when is_list(Config) -> - ?line ensure_lib_loaded(Config, 1), - ?line Type = get_resource_type(0), + ensure_lib_loaded(Config, 1), + Type = get_resource_type(0), resource_hugo(Type), resource_otto(Type), resource_new(Type), @@ -514,75 +513,75 @@ resource(Config) when is_list(Config) -> resource_hugo(Type) -> DtorCall = resource_hugo_do(Type), erlang:garbage_collect(), - ?line DtorCall = last_resource_dtor_call(), + DtorCall = last_resource_dtor_call(), ok. resource_hugo_do(Type) -> HugoBin = <<"Hugo Hacker">>, - ?line HugoPtr = alloc_resource(Type, HugoBin), - ?line Hugo = make_resource(HugoPtr), - ?line <<>> = Hugo, + HugoPtr = alloc_resource(Type, HugoBin), + Hugo = make_resource(HugoPtr), + <<>> = Hugo, release_resource(HugoPtr), erlang:garbage_collect(), - ?line {HugoPtr,HugoBin} = get_resource(Type,Hugo), + {HugoPtr,HugoBin} = get_resource(Type,Hugo), Pid = spawn_link(fun() -> - receive {Pid, Type, Resource, Ptr, Bin} -> - Pid ! {self(), got_it}, - receive {Pid, check_it} -> - ?line {Ptr,Bin} = get_resource(Type,Resource), - Pid ! {self(), ok} - end - end - end), + receive {Pid, Type, Resource, Ptr, Bin} -> + Pid ! {self(), got_it}, + receive {Pid, check_it} -> + {Ptr,Bin} = get_resource(Type,Resource), + Pid ! {self(), ok} + end + end + end), Pid ! {self(), Type, Hugo, HugoPtr, HugoBin}, - ?line {Pid, got_it} = receive_any(), + {Pid, got_it} = receive_any(), erlang:garbage_collect(), % just to make our ProcBin move in memory Pid ! {self(), check_it}, - ?line {Pid, ok} = receive_any(), - ?line [] = last_resource_dtor_call(), - ?line {HugoPtr,HugoBin} = get_resource(Type,Hugo), + {Pid, ok} = receive_any(), + [] = last_resource_dtor_call(), + {HugoPtr,HugoBin} = get_resource(Type,Hugo), {HugoPtr, HugoBin, 1}. resource_otto(Type) -> {OttoPtr, OttoBin} = resource_otto_do(Type), erlang:garbage_collect(), - ?line [] = last_resource_dtor_call(), + [] = last_resource_dtor_call(), release_resource(OttoPtr), - ?line {OttoPtr,OttoBin,1} = last_resource_dtor_call(), + {OttoPtr,OttoBin,1} = last_resource_dtor_call(), ok. resource_otto_do(Type) -> OttoBin = <<"Otto Ordonnans">>, - ?line OttoPtr = alloc_resource(Type, OttoBin), - ?line Otto = make_resource(OttoPtr), - ?line <<>> = Otto, + OttoPtr = alloc_resource(Type, OttoBin), + Otto = make_resource(OttoPtr), + <<>> = Otto, %% forget resource term but keep referenced by NIF {OttoPtr, OttoBin}. resource_new(Type) -> - ?line {PtrB,BinB} = resource_new_do1(Type), + {PtrB,BinB} = resource_new_do1(Type), erlang:garbage_collect(), - ?line {PtrB,BinB,1} = last_resource_dtor_call(), + {PtrB,BinB,1} = last_resource_dtor_call(), ok. resource_new_do1(Type) -> - ?line {{PtrA,BinA}, {ResB,PtrB,BinB}} = resource_new_do2(Type), + {{PtrA,BinA}, {ResB,PtrB,BinB}} = resource_new_do2(Type), erlang:garbage_collect(), - ?line {PtrA,BinA,1} = last_resource_dtor_call(), - ?line {PtrB,BinB} = get_resource(Type, ResB), + {PtrA,BinA,1} = last_resource_dtor_call(), + {PtrB,BinB} = get_resource(Type, ResB), %% forget ResB and make it garbage {PtrB,BinB}. resource_new_do2(Type) -> BinA = <<"NewA">>, BinB = <<"NewB">>, - ?line ResA = make_new_resource(Type, BinA), - ?line ResB = make_new_resource(Type, BinB), - ?line <<>> = ResA, - ?line <<>> = ResB, - ?line {PtrA,BinA} = get_resource(Type, ResA), - ?line {PtrB,BinB} = get_resource(Type, ResB), - ?line true = (PtrA =/= PtrB), + ResA = make_new_resource(Type, BinA), + ResB = make_new_resource(Type, BinB), + <<>> = ResA, + <<>> = ResB, + {PtrA,BinA} = get_resource(Type, ResA), + {PtrB,BinB} = get_resource(Type, ResB), + true = (PtrA =/= PtrB), %% forget ResA and make it garbage {{PtrA,BinA}, {ResB,PtrB,BinB}}. @@ -591,21 +590,21 @@ resource_neg(TypeA) -> catch exit(42), % dummy exception to purge saved stacktraces from earlier exception erlang:garbage_collect(), - ?line {_,_,2} = last_resource_dtor_call(), + {_,_,2} = last_resource_dtor_call(), ok. resource_neg_do(TypeA) -> TypeB = get_resource_type(1), ResA = make_new_resource(TypeA, <<"Arnold">>), ResB= make_new_resource(TypeB, <<"Bobo">>), - ?line {'EXIT',{badarg,_}} = (catch get_resource(TypeA, ResB)), - ?line {'EXIT',{badarg,_}} = (catch get_resource(TypeB, ResA)), + {'EXIT',{badarg,_}} = (catch get_resource(TypeA, ResB)), + {'EXIT',{badarg,_}} = (catch get_resource(TypeB, ResA)), ok. %% Test enif_make_resource_binary resource_binary(Config) when is_list(Config) -> - ?line ensure_lib_loaded(Config, 1), - ?line {Ptr,Bin} = resource_binary_do(), + ensure_lib_loaded(Config, 1), + {Ptr,Bin} = resource_binary_do(), erlang:garbage_collect(), Last = last_resource_dtor_call(), ?CHECK({Ptr,Bin,1}, Last), @@ -613,22 +612,22 @@ resource_binary(Config) when is_list(Config) -> resource_binary_do() -> Bin = <<"Hej Hopp i lingonskogen">>, - ?line {Ptr,ResBin1} = make_new_resource_binary(Bin), - ?line ResBin1 = Bin, - ?line ResInfo = {Ptr,_} = get_resource(binary_resource_type,ResBin1), + {Ptr,ResBin1} = make_new_resource_binary(Bin), + ResBin1 = Bin, + ResInfo = {Ptr,_} = get_resource(binary_resource_type,ResBin1), Papa = self(), Forwarder = spawn_link(fun() -> forwarder(Papa) end), io:format("sending to forwarder pid=~p\n",[Forwarder]), Forwarder ! ResBin1, ResBin2 = receive_any(), - ?line ResBin2 = ResBin1, - ?line ResInfo = get_resource(binary_resource_type,ResBin2), + ResBin2 = ResBin1, + ResInfo = get_resource(binary_resource_type,ResBin2), Forwarder ! terminate, - ?line {Forwarder, 1} = receive_any(), + {Forwarder, 1} = receive_any(), erlang:garbage_collect(), - ?line ResInfo = get_resource(binary_resource_type,ResBin1), - ?line ResInfo = get_resource(binary_resource_type,ResBin2), + ResInfo = get_resource(binary_resource_type,ResBin1), + ResInfo = get_resource(binary_resource_type,ResBin2), ResInfo. @@ -640,30 +639,30 @@ resource_takeover(Config) when is_list(Config) -> TmpMem = tmpmem(), ensure_lib_loaded(Config), - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "nif_mod"), - ?line {ok,nif_mod,ModBin} = compile:file(File, [binary,return_errors]), - ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin), - - ?line ok = nif_mod:load_nif_lib(Config, 1, - [{resource_type, 0, ?RT_CREATE, "resource_type_A",resource_dtor_A, - ?RT_CREATE}, - {resource_type, 1, ?RT_CREATE, "resource_type_null_A",null, - ?RT_CREATE}, - {resource_type, 2, ?RT_CREATE bor ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, - ?RT_CREATE}, - {resource_type, 3, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B, - ?RT_CREATE}, - {resource_type, 4, ?RT_CREATE, "resource_type_null_goneX",null, - ?RT_CREATE}, - {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A, - ?RT_TAKEOVER} - ]), - - ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), - ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "nif_mod"), + {ok,nif_mod,ModBin} = compile:file(File, [binary,return_errors]), + {module,nif_mod} = erlang:load_module(nif_mod,ModBin), - ?line {Holder, _MRef} = spawn_opt(fun resource_holder/0, [link, monitor]), + ok = nif_mod:load_nif_lib(Config, 1, + [{resource_type, 0, ?RT_CREATE, "resource_type_A",resource_dtor_A, + ?RT_CREATE}, + {resource_type, 1, ?RT_CREATE, "resource_type_null_A",null, + ?RT_CREATE}, + {resource_type, 2, ?RT_CREATE bor ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, + ?RT_CREATE}, + {resource_type, 3, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B, + ?RT_CREATE}, + {resource_type, 4, ?RT_CREATE, "resource_type_null_goneX",null, + ?RT_CREATE}, + {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A, + ?RT_TAKEOVER} + ]), + + hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), + [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), + + {Holder, _MRef} = spawn_opt(fun resource_holder/0, [link, monitor]), {A1,BinA1} = make_resource(0,Holder,"A1"), {A2,BinA2} = make_resource(0,Holder,"A2"), @@ -683,91 +682,91 @@ resource_takeover(Config) when is_list(Config) -> {NGX1,_BinNGX1} = make_resource(4,Holder,"NGX1"), {NGX2,_BinNGX2} = make_resource(4,Holder,"NGX2"), - ?line [] = nif_mod_call_history(), + [] = nif_mod_call_history(), - ?line ok = forget_resource(A1), - ?line [{{resource_dtor_A_v1,BinA1},1,3,103}] = nif_mod_call_history(), + ok = forget_resource(A1), + [{{resource_dtor_A_v1,BinA1},1,3,103}] = nif_mod_call_history(), - ?line ok = forget_resource(NA1), - ?line [] = nif_mod_call_history(), % no dtor + ok = forget_resource(NA1), + [] = nif_mod_call_history(), % no dtor - ?line ok = forget_resource(AN1), + ok = forget_resource(AN1), ?CHECK([{{resource_dtor_A_v1,BinAN1},1,4,104}] , nif_mod_call_history()), - ?line ok = forget_resource(BGX1), + ok = forget_resource(BGX1), ?CHECK([{{resource_dtor_B_v1,BinBGX1},1,5,105}], nif_mod_call_history()), - ?line ok = forget_resource(NGX1), + ok = forget_resource(NGX1), ?CHECK([], nif_mod_call_history()), % no dtor - ?line ok = nif_mod:load_nif_lib(Config, 2, - [{resource_type, 0, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, - ?RT_TAKEOVER}, - {resource_type, 1, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A, - ?RT_TAKEOVER}, - {resource_type, 2, ?RT_TAKEOVER, "resource_type_A_null",null, - ?RT_TAKEOVER}, - {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A, - ?RT_TAKEOVER}, - {resource_type, null, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B, - ?RT_CREATE}, - {resource_type, null, ?RT_CREATE, "resource_type_null_goneX",null, - ?RT_CREATE}, - {resource_type, 3, ?RT_CREATE, "resource_type_B_goneY",resource_dtor_B, - ?RT_CREATE}, - {resource_type, 4, ?RT_CREATE, "resource_type_null_goneY",null, - ?RT_CREATE} - ]), + ok = nif_mod:load_nif_lib(Config, 2, + [{resource_type, 0, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 1, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 2, ?RT_TAKEOVER, "resource_type_A_null",null, + ?RT_TAKEOVER}, + {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, null, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B, + ?RT_CREATE}, + {resource_type, null, ?RT_CREATE, "resource_type_null_goneX",null, + ?RT_CREATE}, + {resource_type, 3, ?RT_CREATE, "resource_type_B_goneY",resource_dtor_B, + ?RT_CREATE}, + {resource_type, 4, ?RT_CREATE, "resource_type_null_goneY",null, + ?RT_CREATE} + ]), ?CHECK([{reload,2,1,201}], nif_mod_call_history()), - ?line BinA2 = read_resource(0,A2), - ?line ok = forget_resource(A2), + BinA2 = read_resource(0,A2), + ok = forget_resource(A2), ?CHECK([{{resource_dtor_A_v2,BinA2},2,2,202}], nif_mod_call_history()), - ?line ok = forget_resource(NA2), + ok = forget_resource(NA2), ?CHECK([{{resource_dtor_A_v2,BinNA2},2,3,203}], nif_mod_call_history()), - ?line ok = forget_resource(AN2), + ok = forget_resource(AN2), ?CHECK([], nif_mod_call_history()), % no dtor - ?line ok = forget_resource(BGX2), % calling dtor in orphan library v1 still loaded + ok = forget_resource(BGX2), % calling dtor in orphan library v1 still loaded ?CHECK([{{resource_dtor_B_v1,BinBGX2},1,6,106}], nif_mod_call_history()), % How to test that lib v1 is closed here? - ?line ok = forget_resource(NGX2), + ok = forget_resource(NGX2), ?CHECK([], nif_mod_call_history()), % no dtor {BGY1,BinBGY1} = make_resource(3,Holder,"BGY1"), {NGY1,_BinNGY1} = make_resource(4,Holder,"NGY1"), %% Module upgrade with same lib-version - ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin), - ?line undefined = nif_mod:lib_version(), - ?line ok = nif_mod:load_nif_lib(Config, 2, - [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B, - ?RT_TAKEOVER}, - {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, - ?RT_TAKEOVER}, - {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, - ?RT_TAKEOVER}, - {resource_type, null, ?RT_TAKEOVER, "Pink elephant", resource_dtor_A, - ?RT_TAKEOVER}, - {resource_type, 3, ?RT_CREATE, "resource_type_B_goneZ",resource_dtor_B, - ?RT_CREATE}, - {resource_type, 4, ?RT_CREATE, "resource_type_null_goneZ",null, - ?RT_CREATE} - ]), - - ?line 2 = nif_mod:lib_version(), + {module,nif_mod} = erlang:load_module(nif_mod,ModBin), + undefined = nif_mod:lib_version(), + ok = nif_mod:load_nif_lib(Config, 2, + [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B, + ?RT_TAKEOVER}, + {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, + ?RT_TAKEOVER}, + {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, null, ?RT_TAKEOVER, "Pink elephant", resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 3, ?RT_CREATE, "resource_type_B_goneZ",resource_dtor_B, + ?RT_CREATE}, + {resource_type, 4, ?RT_CREATE, "resource_type_null_goneZ",null, + ?RT_CREATE} + ]), + + 2 = nif_mod:lib_version(), ?CHECK([{upgrade,2,4,204},{lib_version,2,5,205}], nif_mod_call_history()), - ?line ok = forget_resource(A3), + ok = forget_resource(A3), ?CHECK([{{resource_dtor_B_v2,BinA3},2,6,206}], nif_mod_call_history()), - ?line ok = forget_resource(NA3), + ok = forget_resource(NA3), ?CHECK([], nif_mod_call_history()), - ?line ok = forget_resource(AN3), + ok = forget_resource(AN3), ?CHECK([{{resource_dtor_A_v2,BinAN3},2,7,207}], nif_mod_call_history()), {A4,BinA4} = make_resource(2,Holder, "A4"), @@ -777,19 +776,19 @@ resource_takeover(Config) when is_list(Config) -> {BGZ1,BinBGZ1} = make_resource(3,Holder,"BGZ1"), {NGZ1,_BinNGZ1} = make_resource(4,Holder,"NGZ1"), - ?line false = code:purge(nif_mod), - ?line [] = nif_mod_call_history(), + false = code:purge(nif_mod), + [] = nif_mod_call_history(), - ?line ok = forget_resource(NGY1), - ?line [] = nif_mod_call_history(), + ok = forget_resource(NGY1), + [] = nif_mod_call_history(), - ?line ok = forget_resource(BGY1), % calling dtor in orphan library v2 still loaded - ?line [{{resource_dtor_B_v2,BinBGY1},2,8,208},{unload,2,9,209}] = nif_mod_call_history(), + ok = forget_resource(BGY1), % calling dtor in orphan library v2 still loaded + [{{resource_dtor_B_v2,BinBGY1},2,8,208},{unload,2,9,209}] = nif_mod_call_history(), %% Module upgrade with other lib-version - ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin), - ?line undefined = nif_mod:lib_version(), - ?line ok = nif_mod:load_nif_lib(Config, 1, + {module,nif_mod} = erlang:load_module(nif_mod,ModBin), + undefined = nif_mod:lib_version(), + ok = nif_mod:load_nif_lib(Config, 1, [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, ?RT_TAKEOVER}, {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A, @@ -800,28 +799,28 @@ resource_takeover(Config) when is_list(Config) -> ?RT_TAKEOVER} ]), - ?line 1 = nif_mod:lib_version(), - ?line [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(), + 1 = nif_mod:lib_version(), + [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(), - %%?line false= check_process_code(Pid, nif_mod), - ?line false = code:purge(nif_mod), + %%false= check_process_code(Pid, nif_mod), + false = code:purge(nif_mod), %% no unload here as we still have instances with destructors - ?line [] = nif_mod_call_history(), + [] = nif_mod_call_history(), - ?line ok = forget_resource(BGZ1), % calling dtor in orphan library v2 still loaded - ?line [{{resource_dtor_B_v2,BinBGZ1},2,10,210},{unload,2,11,211}] = nif_mod_call_history(), + ok = forget_resource(BGZ1), % calling dtor in orphan library v2 still loaded + [{{resource_dtor_B_v2,BinBGZ1},2,10,210},{unload,2,11,211}] = nif_mod_call_history(), - ?line ok = forget_resource(NGZ1), - ?line [] = nif_mod_call_history(), + ok = forget_resource(NGZ1), + [] = nif_mod_call_history(), - ?line ok = forget_resource(A4), - ?line [{{resource_dtor_A_v1,BinA4},1,3,103}] = nif_mod_call_history(), + ok = forget_resource(A4), + [{{resource_dtor_A_v1,BinA4},1,3,103}] = nif_mod_call_history(), - ?line ok = forget_resource(NA4), - ?line [{{resource_dtor_A_v1,BinNA4},1,4,104}] = nif_mod_call_history(), + ok = forget_resource(NA4), + [{{resource_dtor_A_v1,BinNA4},1,4,104}] = nif_mod_call_history(), - ?line ok = forget_resource(AN4), - ?line [] = nif_mod_call_history(), + ok = forget_resource(AN4), + [] = nif_mod_call_history(), %% @@ -943,8 +942,8 @@ resource_takeover(Config) when is_list(Config) -> {return, 0} % SUCCESS ]), - ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), - ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), + hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), + [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), {NA7,BinNA7} = make_resource(0, Holder, "NA7"), {AN7,BinAN7} = make_resource(1, Holder, "AN7"), @@ -955,15 +954,15 @@ resource_takeover(Config) when is_list(Config) -> ok = forget_resource(AN7), [] = nif_mod_call_history(), - ?line true = lists:member(?MODULE, erlang:system_info(taints)), - ?line true = lists:member(nif_mod, erlang:system_info(taints)), - ?line verify_tmpmem(TmpMem), + true = lists:member(?MODULE, erlang:system_info(taints)), + true = lists:member(nif_mod, erlang:system_info(taints)), + verify_tmpmem(TmpMem), ok. make_resource(Type,Holder,Str) when is_list(Str) -> Bin = list_to_binary(Str), A1 = make_resource_do(Type,Holder,Bin), - ?line Bin = read_resource(Type,A1), + Bin = read_resource(Type,A1), {A1,Bin}. make_resource_do(Type, Holder, Bin) -> @@ -990,7 +989,7 @@ resource_holder(List) -> %%io:format("resource_holder got ~p with list = ~p\n", [Msg,List]), case Msg of {Pid, make, Type, Bin} -> - ?line Resource = nif_mod:make_new_resource(Type, Bin), + Resource = nif_mod:make_new_resource(Type, Bin), Id = {make_ref(),Bin}, Pid ! {self(), make_ok, Id}, resource_holder([{Id,Resource} | List]); @@ -1020,19 +1019,19 @@ threading(Config) when is_list(Config) -> end. threading_do(Config) -> - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "tester"), - ?line {ok,tester,ModBin} = compile:file(File, [binary,return_errors]), - ?line {module,tester} = erlang:load_module(tester,ModBin), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "tester"), + {ok,tester,ModBin} = compile:file(File, [binary,return_errors]), + {module,tester} = erlang:load_module(tester,ModBin), - ?line ok = tester:load_nif_lib(Config, "basic"), - ?line ok = tester:run(), + ok = tester:load_nif_lib(Config, "basic"), + ok = tester:run(), - ?line ok = tester:load_nif_lib(Config, "rwlock"), - ?line ok = tester:run(), + ok = tester:load_nif_lib(Config, "rwlock"), + ok = tester:run(), - ?line ok = tester:load_nif_lib(Config, "tsd"), - ?line ok = tester:run(). + ok = tester:load_nif_lib(Config, "tsd"), + ok = tester:run(). %% Test NIF message sending send(Config) when is_list(Config) -> @@ -1040,22 +1039,22 @@ send(Config) when is_list(Config) -> N = 1500, List = lists:seq(1,N), - ?line {ok,1} = send_list_seq(N, self), - ?line {ok,1} = send_list_seq(N, self()), - ?line List = receive_any(), - ?line List = receive_any(), + {ok,1} = send_list_seq(N, self), + {ok,1} = send_list_seq(N, self()), + List = receive_any(), + List = receive_any(), Papa = self(), - spawn_link(fun() -> ?line {ok,1} = send_list_seq(N, Papa) end), - ?line List = receive_any(), + spawn_link(fun() -> {ok,1} = send_list_seq(N, Papa) end), + List = receive_any(), - ?line {ok, 1, BlobS} = send_new_blob(self(), other_term()), - ?line BlobR = receive_any(), + {ok, 1, BlobS} = send_new_blob(self(), other_term()), + BlobR = receive_any(), io:format("Sent ~p\nGot ~p\n", [BlobS, BlobR]), - ?line BlobR = BlobS, + BlobR = BlobS, %% send to dead pid {DeadPid, DeadMon} = spawn_monitor(fun() -> void end), - ?line {'DOWN', DeadMon, process, DeadPid, normal} = receive_any(), + {'DOWN', DeadMon, process, DeadPid, normal} = receive_any(), {ok,0} = send_list_seq(7, DeadPid), ok. @@ -1087,44 +1086,44 @@ send2_do1(SendBlobF) -> io:format("sending to forwarder pid=~p\n",[Forwarder]), send2_do2(SendBlobF, Forwarder), Forwarder ! terminate, - ?line {Forwarder, 4} = receive_any(), + {Forwarder, 4} = receive_any(), ok. send2_do2(SendBlobF, To) -> MsgEnv = alloc_msgenv(), repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []), - ?line {ok,1,Blob0} = SendBlobF(MsgEnv, To), - ?line Blob1 = receive_any(), - ?line Blob1 = Blob0, + {ok,1,Blob0} = SendBlobF(MsgEnv, To), + Blob1 = receive_any(), + Blob1 = Blob0, clear_msgenv(MsgEnv), repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []), - ?line {ok,1,Blob2} = SendBlobF(MsgEnv, To), - ?line Blob3 = receive_any(), - ?line Blob3 = Blob2, + {ok,1,Blob2} = SendBlobF(MsgEnv, To), + Blob3 = receive_any(), + Blob3 = Blob2, clear_msgenv(MsgEnv), repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []), clear_msgenv(MsgEnv), repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []), - ?line {ok,1,Blob4} = SendBlobF(MsgEnv, To), - ?line Blob5 = receive_any(), - ?line Blob5 = Blob4, + {ok,1,Blob4} = SendBlobF(MsgEnv, To), + Blob5 = receive_any(), + Blob5 = Blob4, clear_msgenv(MsgEnv), clear_msgenv(MsgEnv), repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []), - ?line {ok,1,Blob6} = SendBlobF(MsgEnv, To), - ?line Blob7 = receive_any(), - ?line Blob7 = Blob6, + {ok,1,Blob6} = SendBlobF(MsgEnv, To), + Blob7 = receive_any(), + Blob7 = Blob6, ok. send_blob_thread_and_join(MsgEnv, To) -> - ?line {ok,Blob} = send_blob_thread_dbg(MsgEnv, To, no_join), - ?line {ok,SendRes} = join_send_thread(MsgEnv), + {ok,Blob} = send_blob_thread_dbg(MsgEnv, To, no_join), + {ok,SendRes} = join_send_thread(MsgEnv), {ok,SendRes,Blob}. send_blob_dbg(MsgEnv, To) -> @@ -1160,10 +1159,10 @@ send3(Config) when is_list(Config) -> rand:seed(exsplus), io:format("seed: ~p\n",[rand:export_seed()]), ets:new(nif_SUITE,[named_table,public]), - ?line true = ets:insert(nif_SUITE,{send3,0,0,0,0}), + true = ets:insert(nif_SUITE,{send3,0,0,0,0}), timer:send_after(10000, timeout), % Run for 10 seconds SpawnCnt = send3_controller(0, [], [], 20), - ?line [{_,Rcv,SndOk,SndFail,Balance}] = ets:lookup(nif_SUITE,send3), + [{_,Rcv,SndOk,SndFail,Balance}] = ets:lookup(nif_SUITE,send3), io:format("spawns=~p received=~p, sent=~p send-failure=~p balance=~p\n", [SpawnCnt,Rcv,SndOk,SndFail,Balance]), ets:delete(nif_SUITE). @@ -1197,7 +1196,7 @@ send3_controller(SpawnCnt0, Mons0, Pids0, Tick) -> true -> {NewPid,Mon} = spawn_opt(fun send3_proc/0, [link,monitor]), lists:foreach(fun(P) -> P ! {is_born,NewPid} end, Pids0), - ?line Balance = ets:lookup_element(nif_SUITE,send3,5), + Balance = ets:lookup_element(nif_SUITE,send3,5), Inject = (Balance =< 0), case Inject of true -> ok; @@ -1223,7 +1222,7 @@ send3_proc(Pids0, Counters={Rcv,SndOk,SndFail}, State0) -> receive {pids, Pids1, Inject} -> %%io:format("~p: got ~p Inject=~p\n", [self(), Pids1, Inject]), - ?line Pids0 = [self()], + Pids0 = [self()], Pids2 = [self() | Pids1], case Inject of true -> send3_proc_send(Pids2, Counters, State0); @@ -1311,93 +1310,93 @@ send3_new_state(State, Blob) -> %% Negative testing of load_nif neg(Config) when is_list(Config) -> TmpMem = tmpmem(), - ?line {'EXIT',{badarg,_}} = (catch erlang:load_nif(badarg, 0)), - ?line {error,{load_failed,_}} = erlang:load_nif("pink_unicorn", 0), + {'EXIT',{badarg,_}} = (catch erlang:load_nif(badarg, 0)), + {error,{load_failed,_}} = erlang:load_nif("pink_unicorn", 0), - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "nif_mod"), - ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), - ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "nif_mod"), + {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), + {module,nif_mod} = erlang:load_module(nif_mod,Bin), - ?line {error,{bad_lib,_}} = nif_mod:load_nif_lib(Config, no_init), - ?line verify_tmpmem(TmpMem), - ?line ok. + {error,{bad_lib,_}} = nif_mod:load_nif_lib(Config, no_init), + verify_tmpmem(TmpMem), + ok. %% Test all enif_is functions is_checks(Config) when is_list(Config) -> - ?line ensure_lib_loaded(Config, 1), - ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, + ensure_lib_loaded(Config, 1), + ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, self(), hd(erlang:ports()), [], [1,9,9,8], {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 12), - ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, + ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, self(), hd(erlang:ports()), [], [1,9,9,8], {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -12), - ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, + ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, self(), hd(erlang:ports()), [], [1,9,9,8], {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 18446744073709551617), - ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, + ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, self(), hd(erlang:ports()), [], [1,9,9,8], {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551617), - ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, + ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, self(), hd(erlang:ports()), [], [1,9,9,8], {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 99.146), - ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, + ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, self(), hd(erlang:ports()), [], [1,9,9,8], {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -99.146), - ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, + ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, self(), hd(erlang:ports()), [], [1,9,9,8], {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 18446744073709551616.2e2), - ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, + ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, self(), hd(erlang:ports()), [], [1,9,9,8], {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551616.2e2), try - ?line check_is_exception(), - ?line throw(expected_badarg) + check_is_exception(), + throw(expected_badarg) catch error:badarg -> - ?line ok + ok end. %% Test all enif_get_length functions get_length(Config) when is_list(Config) -> - ?line ensure_lib_loaded(Config, 1), - ?line ok = length_test(hejsan, "hejsan", [], [], not_a_list, [1,2|3]). + ensure_lib_loaded(Config, 1), + ok = length_test(hejsan, "hejsan", [], [], not_a_list, [1,2|3]). ensure_lib_loaded(Config) -> ensure_lib_loaded(Config, 1). ensure_lib_loaded(Config, Ver) -> - ?line case lib_version() of + case lib_version() of undefined -> - ?line Path = proplists:get_value(data_dir, Config), - ?line Lib = "nif_SUITE." ++ integer_to_list(Ver), - ?line ok = erlang:load_nif(filename:join(Path,Lib), []); + Path = proplists:get_value(data_dir, Config), + Lib = "nif_SUITE." ++ integer_to_list(Ver), + ok = erlang:load_nif(filename:join(Path,Lib), []); Ver when is_integer(Ver) -> ok end. make_atom(Config) when is_list(Config) -> - ?line ensure_lib_loaded(Config, 1), + ensure_lib_loaded(Config, 1), An0Atom = an0atom, An0Atom0 = 'an\000atom\000', - ?line Atoms = make_atoms(), - ?line 7 = size(Atoms), - ?line Atoms = {An0Atom,An0Atom,An0Atom,An0Atom0,An0Atom,An0Atom,An0Atom0}. + Atoms = make_atoms(), + 7 = size(Atoms), + Atoms = {An0Atom,An0Atom,An0Atom,An0Atom0,An0Atom,An0Atom,An0Atom0}. make_string(Config) when is_list(Config) -> - ?line ensure_lib_loaded(Config, 1), - ?line Strings = make_strings(), - ?line 5 = size(Strings), + ensure_lib_loaded(Config, 1), + Strings = make_strings(), + 5 = size(Strings), A0String = "a0string", A0String0 = [$a,0,$s,$t,$r,$i,$n,$g,0], AStringWithAccents = [$E,$r,$l,$a,$n,$g,$ ,16#e4,$r,$ ,$e,$t,$t,$ ,$g,$e,$n,$e,$r,$e,$l,$l,$t,$ ,$p,$r,$o,$g,$r,$a,$m,$s,$p,$r,16#e5,$k], - ?line Strings = {A0String,A0String,A0String,A0String0, AStringWithAccents}. + Strings = {A0String,A0String,A0String,A0String0, AStringWithAccents}. reverse_list_test(Config) -> - ?line ensure_lib_loaded(Config, 1), + ensure_lib_loaded(Config, 1), List = lists:seq(1,100), RevList = lists:reverse(List), - ?line RevList = reverse_list(List), - ?line badarg = reverse_list(foo). + RevList = reverse_list(List), + badarg = reverse_list(foo). %% Memory leak of tmp-buffer when inspecting iolist or unaligned binary in unbound environment otp_9668(Config) -> @@ -1409,13 +1408,12 @@ otp_9668(Config) -> <<_:5/bitstring,UnalignedBin:10/binary,_/bitstring>> = <<"Abuse me as unaligned">>, otp_9668_nif(UnalignedBin), - ?line verify_tmpmem(TmpMem), + verify_tmpmem(TmpMem), ok. %% Copy of writable binary otp_9828(Config) -> ensure_lib_loaded(Config, 1), - otp_9828_loop(<<"I'm alive!">>, 1000). otp_9828_loop(Bin, 0) -> @@ -1434,68 +1432,68 @@ consume_timeslice(Config) when is_list(Config) -> Done = make_ref(), DummyMFA = {?MODULE,dummy_call,1}, P = spawn(fun () -> - receive Go -> ok end, - {reductions, R1} = process_info(self(), reductions), - 1 = consume_timeslice_nif(100, false), - dummy_call(111), - 0 = consume_timeslice_nif(90, false), - dummy_call(222), - 1 = consume_timeslice_nif(10, false), - dummy_call(333), - 0 = consume_timeslice_nif(25, false), - 0 = consume_timeslice_nif(25, false), - 0 = consume_timeslice_nif(25, false), - 1 = consume_timeslice_nif(25, false), - 0 = consume_timeslice_nif(25, false), - - ok = case consume_timeslice_nif(1, true) of - Cnt when Cnt > 70, Cnt < 80 -> ok; - Other -> Other - end, - dummy_call(444), - - {reductions, R2} = process_info(self(), reductions), - Me ! {RedDiff, R2 - R1}, - exit(Done) - end), + receive Go -> ok end, + {reductions, R1} = process_info(self(), reductions), + 1 = consume_timeslice_nif(100, false), + dummy_call(111), + 0 = consume_timeslice_nif(90, false), + dummy_call(222), + 1 = consume_timeslice_nif(10, false), + dummy_call(333), + 0 = consume_timeslice_nif(25, false), + 0 = consume_timeslice_nif(25, false), + 0 = consume_timeslice_nif(25, false), + 1 = consume_timeslice_nif(25, false), + 0 = consume_timeslice_nif(25, false), + + ok = case consume_timeslice_nif(1, true) of + Cnt when Cnt > 70, Cnt < 80 -> ok; + Other -> Other + end, + dummy_call(444), + + {reductions, R2} = process_info(self(), reductions), + Me ! {RedDiff, R2 - R1}, + exit(Done) + end), erlang:yield(), erlang:trace_pattern(DummyMFA, [], [local]), - ?line 1 = erlang:trace(P, true, [call, running, procs, {tracer, self()}]), + 1 = erlang:trace(P, true, [call, running, procs, {tracer, self()}]), P ! Go, %% receive Go -> ok end, - ?line {trace, P, in, _} = next_tmsg(P), + {trace, P, in, _} = next_tmsg(P), %% consume_timeslice_nif(100), %% dummy_call(111) - ?line {trace, P, out, _} = next_tmsg(P), - ?line {trace, P, in, _} = next_tmsg(P), - ?line {trace, P, call, {?MODULE,dummy_call,[111]}} = next_tmsg(P), + {trace, P, out, _} = next_tmsg(P), + {trace, P, in, _} = next_tmsg(P), + {trace, P, call, {?MODULE,dummy_call,[111]}} = next_tmsg(P), %% consume_timeslice_nif(90), %% dummy_call(222) - ?line {trace, P, call, {?MODULE,dummy_call,[222]}} = next_tmsg(P), + {trace, P, call, {?MODULE,dummy_call,[222]}} = next_tmsg(P), %% consume_timeslice_nif(10), %% dummy_call(333) - ?line {trace, P, out, _} = next_tmsg(P), - ?line {trace, P, in, _} = next_tmsg(P), - ?line {trace, P, call, {?MODULE,dummy_call,[333]}} = next_tmsg(P), + {trace, P, out, _} = next_tmsg(P), + {trace, P, in, _} = next_tmsg(P), + {trace, P, call, {?MODULE,dummy_call,[333]}} = next_tmsg(P), %% 25,25,25,25, 25 - ?line {trace, P, out, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P), - ?line {trace, P, in, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P), + {trace, P, out, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P), + {trace, P, in, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P), %% consume_timeslice(1,true) %% dummy_call(444) - ?line {trace, P, out, DummyMFA} = next_tmsg(P), - ?line {trace, P, in, DummyMFA} = next_tmsg(P), - ?line {trace, P, call, {?MODULE,dummy_call,[444]}} = next_tmsg(P), + {trace, P, out, DummyMFA} = next_tmsg(P), + {trace, P, in, DummyMFA} = next_tmsg(P), + {trace, P, call, {?MODULE,dummy_call,[444]}} = next_tmsg(P), %% exit(Done) - ?line {trace, P, exit, Done} = next_tmsg(P), + {trace, P, exit, Done} = next_tmsg(P), ExpReds = (100 + 90 + 10 + 25*5 + 75) * CONTEXT_REDS div 100, receive @@ -1560,38 +1558,38 @@ dirty_nif_send(Config) when is_list(Config) -> dirty_nif_exception(Config) when is_list(Config) -> try erlang:system_info(dirty_cpu_schedulers) of - N when is_integer(N) -> - ensure_lib_loaded(Config), - try - %% this checks that the expected exception occurs when the - %% dirty NIF returns the result of enif_make_badarg - %% directly - call_dirty_nif_exception(1), - ct:fail(expected_badarg) - catch - error:badarg -> - [{?MODULE,call_dirty_nif_exception,[1],_}|_] = - erlang:get_stacktrace(), - ok - end, - try - %% this checks that the expected exception occurs when the - %% dirty NIF calls enif_make_badarg at some point but then - %% returns a value that isn't an exception - call_dirty_nif_exception(0), - ct:fail(expected_badarg) - catch - error:badarg -> - [{?MODULE,call_dirty_nif_exception,[0],_}|_] = - erlang:get_stacktrace(), - ok - end, - %% this checks that a dirty NIF can raise various terms as - %% exceptions - ok = nif_raise_exceptions(call_dirty_nif_exception) + N when is_integer(N) -> + ensure_lib_loaded(Config), + try + %% this checks that the expected exception occurs when the + %% dirty NIF returns the result of enif_make_badarg + %% directly + call_dirty_nif_exception(1), + ct:fail(expected_badarg) + catch + error:badarg -> + [{?MODULE,call_dirty_nif_exception,[1],_}|_] = + erlang:get_stacktrace(), + ok + end, + try + %% this checks that the expected exception occurs when the + %% dirty NIF calls enif_make_badarg at some point but then + %% returns a value that isn't an exception + call_dirty_nif_exception(0), + ct:fail(expected_badarg) + catch + error:badarg -> + [{?MODULE,call_dirty_nif_exception,[0],_}|_] = + erlang:get_stacktrace(), + ok + end, + %% this checks that a dirty NIF can raise various terms as + %% exceptions + ok = nif_raise_exceptions(call_dirty_nif_exception) catch - error:badarg -> - {skipped,"No dirty scheduler support"} + error:badarg -> + {skipped,"No dirty scheduler support"} end. nif_exception(Config) when is_list(Config) -> @@ -1653,19 +1651,19 @@ nif_atom_too_long(Config) when is_list(Config) -> next_msg(_Pid) -> receive - M -> M + M -> M after 100 -> - none + none end. next_tmsg(Pid) -> receive TMsg when is_tuple(TMsg), - element(1, TMsg) == trace, - element(2, TMsg) == Pid -> - TMsg - after 100 -> - none - end. + element(1, TMsg) == trace, + element(2, TMsg) == Pid -> + TMsg + after 100 -> + none + end. dummy_call(_) -> ok. @@ -1733,17 +1731,17 @@ check(Exp,Got,Line) -> nif_raise_exceptions(NifFunc) -> ExcTerms = [{error, test}, "a string", <<"a binary">>, - 42, [1,2,3,4,5], [{p,1},{p,2},{p,3}]], + 42, [1,2,3,4,5], [{p,1},{p,2},{p,3}]], lists:foldl(fun(Term, ok) -> - try - erlang:apply(?MODULE,NifFunc,[Term]), - ct:fail({expected,Term}) - catch - error:Term -> - [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(), - ok - end - end, ok, ExcTerms). + try + erlang:apply(?MODULE,NifFunc,[Term]), + ct:fail({expected,Term}) + catch + error:Term -> + [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(), + ok + end + end, ok, ExcTerms). -define(ERL_NIF_TIME_ERROR, -9223372036854775808). -define(TIME_UNITS, [seconds, milli_seconds, micro_seconds, nano_seconds]). @@ -1789,25 +1787,25 @@ chk_toffs([TU|TUs]) -> TO = erlang:time_offset(TU), NifTO = time_offset(TU), case TO =:= NifTO of - true -> - ok; - false -> - case erlang:system_info(time_warp_mode) of - no_time_warp -> - ct:fail({time_offset_mismatch, TU, TO, NifTO}); - _ -> - %% Most frequent time offset change - %% is currently only every 15:th - %% second so this should currently - %% work... - NTO = erlang:time_offset(TU), - case NifTO =:= NTO of - true -> - ok; - false -> - ct:fail({time_offset_mismatch, TU, TO, NifTO, NTO}) - end - end + true -> + ok; + false -> + case erlang:system_info(time_warp_mode) of + no_time_warp -> + ct:fail({time_offset_mismatch, TU, TO, NifTO}); + _ -> + %% Most frequent time offset change + %% is currently only every 15:th + %% second so this should currently + %% work... + NTO = erlang:time_offset(TU), + case NifTO =:= NTO of + true -> + ok; + false -> + ct:fail({time_offset_mismatch, TU, TO, NifTO, NTO}) + end + end end, chk_toffs(TUs). @@ -1816,47 +1814,47 @@ nif_convert_time_unit(Config) -> ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, seconds), ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, invalid_time_unit), lists:foreach(fun (Offset) -> - lists:foreach(fun (Diff) -> - chk_ctu(Diff+(Offset*1000*1000*1000)) - end, - [999999999999, - 99999999999, - 9999999999, - 999999999, - 99999999, - 9999999, - 999999, - 99999, - 999, - 99, - 9, - 1, - 11, - 101, - 1001, - 10001, - 100001, - 1000001, - 10000001, - 100000001, - 1000000001, - 100000000001, - 1000000000001, - 5, - 50, - 500, - 5000, - 50000, - 500000, - 5000000, - 50000000, - 500000000, - 5000000000, - 50000000000, - 500000000000]) - end, - [-4711, -1000, -475, -5, -4, -3, -2, -1, 0, - 1, 2, 3, 4, 5, 475, 1000, 4711]), + lists:foreach(fun (Diff) -> + chk_ctu(Diff+(Offset*1000*1000*1000)) + end, + [999999999999, + 99999999999, + 9999999999, + 999999999, + 99999999, + 9999999, + 999999, + 99999, + 999, + 99, + 9, + 1, + 11, + 101, + 1001, + 10001, + 100001, + 1000001, + 10000001, + 100000001, + 1000000001, + 100000000001, + 1000000000001, + 5, + 50, + 500, + 5000, + 50000, + 500000, + 5000000, + 50000000, + 500000000, + 5000000000, + 50000000000, + 500000000000]) + end, + [-4711, -1000, -475, -5, -4, -3, -2, -1, 0, + 1, 2, 3, 4, 5, 475, 1000, 4711]), ctu_loop(1000000). ctu_loop(0) -> diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl index e12db72bea..b6de046388 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl @@ -33,7 +33,7 @@ load_nif_lib(Config, Ver) -> load_nif_lib(Config, Ver, []). load_nif_lib(Config, Ver, LoadInfo) -> - ?line Path = proplists:get_value(data_dir, Config), + Path = proplists:get_value(data_dir, Config), erlang:load_nif(filename:join(Path,libname(Ver)), LoadInfo). libname(no_init) -> libname(3); diff --git a/erts/emulator/test/nif_SUITE_data/tester.erl b/erts/emulator/test/nif_SUITE_data/tester.erl index 388b5cab61..f955f99c9a 100644 --- a/erts/emulator/test/nif_SUITE_data/tester.erl +++ b/erts/emulator/test/nif_SUITE_data/tester.erl @@ -4,9 +4,8 @@ -export([load_nif_lib/2, run/0]). - load_nif_lib(Config, LibName) -> - ?line Path = proplists:get_value(data_dir, Config), + Path = proplists:get_value(data_dir, Config), erlang:load_nif(filename:join(Path,LibName), []). run() -> diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 9879e004d2..71400142af 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -33,26 +33,26 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, init_per_suite/1, end_per_suite/1, - init_per_testcase/2, end_per_testcase/2, - node_container_refc_check/1]). + init_per_testcase/2, end_per_testcase/2, + node_container_refc_check/1]). -export([term_to_binary_to_term_eq/1, - round_trip_eq/1, - cmp/1, - ref_eq/1, - node_table_gc/1, - dist_link_refc/1, - dist_monitor_refc/1, - node_controller_refc/1, - ets_refc/1, - match_spec_refc/1, - timer_refc/1, - otp_4715/1, - pid_wrap/1, - port_wrap/1, - bad_nc/1, - unique_pid/1, - iter_max_procs/1]). + round_trip_eq/1, + cmp/1, + ref_eq/1, + node_table_gc/1, + dist_link_refc/1, + dist_monitor_refc/1, + node_controller_refc/1, + ets_refc/1, + match_spec_refc/1, + timer_refc/1, + otp_4715/1, + pid_wrap/1, + port_wrap/1, + bad_nc/1, + unique_pid/1, + iter_max_procs/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -76,17 +76,17 @@ end_per_suite(_Config) -> available_internal_state(Bool) when Bool == true; Bool == false -> case {Bool, - (catch erts_debug:get_internal_state(available_internal_state))} of - {true, true} -> - true; - {false, true} -> - erts_debug:set_internal_state(available_internal_state, false), - true; - {true, _} -> - erts_debug:set_internal_state(available_internal_state, true), - false; - {false, _} -> - false + (catch erts_debug:get_internal_state(available_internal_state))} of + {true, true} -> + true; + {false, true} -> + erts_debug:set_internal_state(available_internal_state, false), + true; + {true, _} -> + erts_debug:set_internal_state(available_internal_state, true), + false; + {false, _} -> + false end. init_per_testcase(_Case, Config) when is_list(Config) -> @@ -108,42 +108,42 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %% Tests that node container terms that are converted to external format %% and back stay equal to themselves. term_to_binary_to_term_eq(Config) when is_list(Config) -> - ?line ThisNode = {node(), erlang:system_info(creation)}, + ThisNode = {node(), erlang:system_info(creation)}, % Get local node containers - ?line LPid = self(), - ?line LXPid = mk_pid(ThisNode, 32767, 8191), - ?line LPort = hd(erlang:ports()), - ?line LXPort = mk_port(ThisNode, 268435455), - ?line LLRef = make_ref(), - ?line LHLRef = mk_ref(ThisNode, [47, 11]), - ?line LSRef = mk_ref(ThisNode, [4711]), + LPid = self(), + LXPid = mk_pid(ThisNode, 32767, 8191), + LPort = hd(erlang:ports()), + LXPort = mk_port(ThisNode, 268435455), + LLRef = make_ref(), + LHLRef = mk_ref(ThisNode, [47, 11]), + LSRef = mk_ref(ThisNode, [4711]), % Test local nc:s - ?line LPid = binary_to_term(term_to_binary(LPid)), - ?line LXPid = binary_to_term(term_to_binary(LXPid)), - ?line LPort = binary_to_term(term_to_binary(LPort)), - ?line LXPort = binary_to_term(term_to_binary(LXPort)), - ?line LLRef = binary_to_term(term_to_binary(LLRef)), - ?line LHLRef = binary_to_term(term_to_binary(LHLRef)), - ?line LSRef = binary_to_term(term_to_binary(LSRef)), + LPid = binary_to_term(term_to_binary(LPid)), + LXPid = binary_to_term(term_to_binary(LXPid)), + LPort = binary_to_term(term_to_binary(LPort)), + LXPort = binary_to_term(term_to_binary(LXPort)), + LLRef = binary_to_term(term_to_binary(LLRef)), + LHLRef = binary_to_term(term_to_binary(LHLRef)), + LSRef = binary_to_term(term_to_binary(LSRef)), % Get remote node containers - ?line RNode = {get_nodename(), 3}, - ?line RPid = mk_pid(RNode, 4711, 1), - ?line RXPid = mk_pid(RNode, 32767, 8191), - ?line RPort = mk_port(RNode, 4711), - ?line RXPort = mk_port(RNode, 268435455), - ?line RLRef = mk_ref(RNode, [4711, 4711, 4711]), - ?line RHLRef = mk_ref(RNode, [4711, 4711]), - ?line RSRef = mk_ref(RNode, [4711]), + RNode = {get_nodename(), 3}, + RPid = mk_pid(RNode, 4711, 1), + RXPid = mk_pid(RNode, 32767, 8191), + RPort = mk_port(RNode, 4711), + RXPort = mk_port(RNode, 268435455), + RLRef = mk_ref(RNode, [4711, 4711, 4711]), + RHLRef = mk_ref(RNode, [4711, 4711]), + RSRef = mk_ref(RNode, [4711]), % Test remote nc:s - ?line RPid = binary_to_term(term_to_binary(RPid)), - ?line RXPid = binary_to_term(term_to_binary(RXPid)), - ?line RPort = binary_to_term(term_to_binary(RPort)), - ?line RXPort = binary_to_term(term_to_binary(RXPort)), - ?line RLRef = binary_to_term(term_to_binary(RLRef)), - ?line RHLRef = binary_to_term(term_to_binary(RHLRef)), - ?line RSRef = binary_to_term(term_to_binary(RSRef)), - ?line nc_refc_check(node()), - ?line ok. + RPid = binary_to_term(term_to_binary(RPid)), + RXPid = binary_to_term(term_to_binary(RXPid)), + RPort = binary_to_term(term_to_binary(RPort)), + RXPort = binary_to_term(term_to_binary(RXPort)), + RLRef = binary_to_term(term_to_binary(RLRef)), + RHLRef = binary_to_term(term_to_binary(RHLRef)), + RSRef = binary_to_term(term_to_binary(RSRef)), + nc_refc_check(node()), + ok. %% @@ -151,51 +151,51 @@ term_to_binary_to_term_eq(Config) when is_list(Config) -> %% %% Tests that node containers that are sent beteen nodes stay equal to themselves. round_trip_eq(Config) when is_list(Config) -> - ?line ThisNode = {node(), erlang:system_info(creation)}, - ?line NodeFirstName = get_nodefirstname(), - ?line ?line {ok, Node} = start_node(NodeFirstName), - ?line Self = self(), - ?line RPid = spawn_link(Node, - fun () -> - receive - {Self, Data} -> - Self ! {self(), Data} - end - end), - ?line SentPid = self(), - ?line SentXPid = mk_pid(ThisNode, 17471, 8190), - ?line SentPort = hd(erlang:ports()), - ?line SentXPort = mk_port(ThisNode, 268435451), - ?line SentLRef = make_ref(), - ?line SentHLRef = mk_ref(ThisNode, [4711, 17]), - ?line SentSRef = mk_ref(ThisNode, [4711]), - ?line RPid ! {Self, {SentPid, - SentXPid, - SentPort, - SentXPort, - SentLRef, - SentHLRef, - SentSRef}}, + ThisNode = {node(), erlang:system_info(creation)}, + NodeFirstName = get_nodefirstname(), + {ok, Node} = start_node(NodeFirstName), + Self = self(), + RPid = spawn_link(Node, + fun () -> + receive + {Self, Data} -> + Self ! {self(), Data} + end + end), + SentPid = self(), + SentXPid = mk_pid(ThisNode, 17471, 8190), + SentPort = hd(erlang:ports()), + SentXPort = mk_port(ThisNode, 268435451), + SentLRef = make_ref(), + SentHLRef = mk_ref(ThisNode, [4711, 17]), + SentSRef = mk_ref(ThisNode, [4711]), + RPid ! {Self, {SentPid, + SentXPid, + SentPort, + SentXPort, + SentLRef, + SentHLRef, + SentSRef}}, receive - {RPid, {RecPid, - RecXPid, - RecPort, - RecXPort, - RecLRef, - RecHLRef, - RecSRef}} -> - ?line stop_node(Node), - ?line SentPid = RecPid, - ?line SentXPid = RecXPid, - ?line SentPort = RecPort, - ?line SentXPort = RecXPort, - ?line SentLRef = RecLRef, - ?line SentHLRef = RecHLRef, - ?line SentSRef = RecSRef, - ?line nc_refc_check(node()), - ?line ok + {RPid, {RecPid, + RecXPid, + RecPort, + RecXPort, + RecLRef, + RecHLRef, + RecSRef}} -> + stop_node(Node), + SentPid = RecPid, + SentXPid = RecXPid, + SentPort = RecPort, + SentXPort = RecXPort, + SentLRef = RecLRef, + SentHLRef = RecHLRef, + SentSRef = RecSRef, + nc_refc_check(node()), + ok end. - + %% @@ -212,103 +212,103 @@ cmp(Config) when is_list(Config) -> IRef = make_ref(), ERef = mk_ref({get_nodename(), 2}, [1,2,3]), - + IPid = self(), EPid = mk_pid(RNode, 1, 2), IPort = hd(erlang:ports()), EPort = mk_port(RNode, 1), - + %% Test pids ---------------------------------------------------- - ?line true = 1 < IPid, - ?line true = 1.3 < IPid, - ?line true = (1 bsl 64) < IPid, - ?line true = an_atom < IPid, - ?line true = IRef < IPid, - ?line true = ERef < IPid, - ?line true = fun () -> a_fun end < IPid, - ?line true = IPort < IPid, - ?line true = EPort < IPid, - ?line true = IPid < {a, tuple}, - ?line true = IPid < [], - ?line true = IPid < [a|cons], - ?line true = IPid < <<"a binary">>, - - ?line true = 1 < EPid, - ?line true = 1.3 < EPid, - ?line true = (1 bsl 64) < EPid, - ?line true = an_atom < EPid, - ?line true = IRef < EPid, - ?line true = ERef < EPid, - ?line true = fun () -> a_fun end < EPid, - ?line true = IPort < EPid, - ?line true = EPort < EPid, - ?line true = EPid < {a, tuple}, - ?line true = EPid < [], - ?line true = EPid < [a|cons], - ?line true = EPid < <<"a binary">>, + true = 1 < IPid, + true = 1.3 < IPid, + true = (1 bsl 64) < IPid, + true = an_atom < IPid, + true = IRef < IPid, + true = ERef < IPid, + true = fun () -> a_fun end < IPid, + true = IPort < IPid, + true = EPort < IPid, + true = IPid < {a, tuple}, + true = IPid < [], + true = IPid < [a|cons], + true = IPid < <<"a binary">>, + + true = 1 < EPid, + true = 1.3 < EPid, + true = (1 bsl 64) < EPid, + true = an_atom < EPid, + true = IRef < EPid, + true = ERef < EPid, + true = fun () -> a_fun end < EPid, + true = IPort < EPid, + true = EPort < EPid, + true = EPid < {a, tuple}, + true = EPid < [], + true = EPid < [a|cons], + true = EPid < <<"a binary">>, %% Test ports -------------------------------------------------- - ?line true = 1 < IPort, - ?line true = 1.3 < IPort, - ?line true = (1 bsl 64) < IPort, - ?line true = an_atom < IPort, - ?line true = IRef < IPort, - ?line true = ERef < IPort, - ?line true = fun () -> a_fun end < IPort, - ?line true = IPort < IPid, - ?line true = IPort < EPid, - ?line true = IPort < {a, tuple}, - ?line true = IPort < [], - ?line true = IPort < [a|cons], - ?line true = IPort < <<"a binary">>, - - ?line true = 1 < EPort, - ?line true = 1.3 < EPort, - ?line true = (1 bsl 64) < EPort, - ?line true = an_atom < EPort, - ?line true = IRef < EPort, - ?line true = ERef < EPort, - ?line true = fun () -> a_fun end < EPort, - ?line true = EPort < IPid, - ?line true = EPort < EPid, - ?line true = EPort < {a, tuple}, - ?line true = EPort < [], - ?line true = EPort < [a|cons], - ?line true = EPort < <<"a binary">>, + true = 1 < IPort, + true = 1.3 < IPort, + true = (1 bsl 64) < IPort, + true = an_atom < IPort, + true = IRef < IPort, + true = ERef < IPort, + true = fun () -> a_fun end < IPort, + true = IPort < IPid, + true = IPort < EPid, + true = IPort < {a, tuple}, + true = IPort < [], + true = IPort < [a|cons], + true = IPort < <<"a binary">>, + + true = 1 < EPort, + true = 1.3 < EPort, + true = (1 bsl 64) < EPort, + true = an_atom < EPort, + true = IRef < EPort, + true = ERef < EPort, + true = fun () -> a_fun end < EPort, + true = EPort < IPid, + true = EPort < EPid, + true = EPort < {a, tuple}, + true = EPort < [], + true = EPort < [a|cons], + true = EPort < <<"a binary">>, %% Test refs ---------------------------------------------------- - ?line true = 1 < IRef, - ?line true = 1.3 < IRef, - ?line true = (1 bsl 64) < IRef, - ?line true = an_atom < IRef, - ?line true = IRef < fun () -> a_fun end, - ?line true = IRef < IPort, - ?line true = IRef < EPort, - ?line true = IRef < IPid, - ?line true = IRef < EPid, - ?line true = IRef < {a, tuple}, - ?line true = IRef < [], - ?line true = IRef < [a|cons], - ?line true = IRef < <<"a binary">>, - - ?line true = 1 < ERef, - ?line true = 1.3 < ERef, - ?line true = (1 bsl 64) < ERef, - ?line true = an_atom < ERef, - ?line true = ERef < fun () -> a_fun end, - ?line true = ERef < IPort, - ?line true = ERef < EPort, - ?line true = ERef < IPid, - ?line true = ERef < EPid, - ?line true = ERef < {a, tuple}, - ?line true = ERef < [], - ?line true = ERef < [a|cons], - ?line true = ERef < <<"a binary">>, + true = 1 < IRef, + true = 1.3 < IRef, + true = (1 bsl 64) < IRef, + true = an_atom < IRef, + true = IRef < fun () -> a_fun end, + true = IRef < IPort, + true = IRef < EPort, + true = IRef < IPid, + true = IRef < EPid, + true = IRef < {a, tuple}, + true = IRef < [], + true = IRef < [a|cons], + true = IRef < <<"a binary">>, + + true = 1 < ERef, + true = 1.3 < ERef, + true = (1 bsl 64) < ERef, + true = an_atom < ERef, + true = ERef < fun () -> a_fun end, + true = ERef < IPort, + true = ERef < EPort, + true = ERef < IPid, + true = ERef < EPid, + true = ERef < {a, tuple}, + true = ERef < [], + true = ERef < [a|cons], + true = ERef < <<"a binary">>, %% Intra type comparison --------------------------------------------------- - + %% Test pids ---------------------------------------------------- %% @@ -316,13 +316,13 @@ cmp(Config) when is_list(Config) -> %% serial, number, nodename, creation %% - ?line Pid = mk_pid({b@b, 2}, 4711, 1), + Pid = mk_pid({b@b, 2}, 4711, 1), - ?line true = mk_pid({a@b, 1}, 4710, 2) > Pid, - ?line true = mk_pid({a@b, 1}, 4712, 1) > Pid, - ?line true = mk_pid({c@b, 1}, 4711, 1) > Pid, - ?line true = mk_pid({b@b, 3}, 4711, 1) > Pid, - ?line true = mk_pid({b@b, 2}, 4711, 1) =:= Pid, + true = mk_pid({a@b, 1}, 4710, 2) > Pid, + true = mk_pid({a@b, 1}, 4712, 1) > Pid, + true = mk_pid({c@b, 1}, 4711, 1) > Pid, + true = mk_pid({b@b, 3}, 4711, 1) > Pid, + true = mk_pid({b@b, 2}, 4711, 1) =:= Pid, %% Test ports --------------------------------------------------- %% @@ -334,12 +334,12 @@ cmp(Config) when is_list(Config) -> %% Significance used to be: dist_slot, number, %% creation. - ?line Port = mk_port({b@b, 2}, 4711), + Port = mk_port({b@b, 2}, 4711), - ?line true = mk_port({c@b, 1}, 4710) > Port, - ?line true = mk_port({b@b, 3}, 4710) > Port, - ?line true = mk_port({b@b, 2}, 4712) > Port, - ?line true = mk_port({b@b, 2}, 4711) =:= Port, + true = mk_port({c@b, 1}, 4710) > Port, + true = mk_port({b@b, 3}, 4710) > Port, + true = mk_port({b@b, 2}, 4712) > Port, + true = mk_port({b@b, 2}, 4711) =:= Port, %% Test refs ---------------------------------------------------- %% Significance (most -> least): @@ -351,14 +351,14 @@ cmp(Config) when is_list(Config) -> %% creation. %% - ?line Ref = mk_ref({b@b, 2}, [4711, 4711, 4711]), + Ref = mk_ref({b@b, 2}, [4711, 4711, 4711]), - ?line true = mk_ref({c@b, 1}, [4710, 4710, 4710]) > Ref, - ?line true = mk_ref({b@b, 3}, [4710, 4710, 4710]) > Ref, - ?line true = mk_ref({b@b, 2}, [4710, 4710, 4712]) > Ref, - ?line true = mk_ref({b@b, 2}, [4710, 4712, 4711]) > Ref, - ?line true = mk_ref({b@b, 2}, [4712, 4711, 4711]) > Ref, - ?line true = mk_ref({b@b, 2}, [4711, 4711, 4711]) =:= Ref, + true = mk_ref({c@b, 1}, [4710, 4710, 4710]) > Ref, + true = mk_ref({b@b, 3}, [4710, 4710, 4710]) > Ref, + true = mk_ref({b@b, 2}, [4710, 4710, 4712]) > Ref, + true = mk_ref({b@b, 2}, [4710, 4712, 4711]) > Ref, + true = mk_ref({b@b, 2}, [4712, 4711, 4711]) > Ref, + true = mk_ref({b@b, 2}, [4711, 4711, 4711]) =:= Ref, ok. @@ -367,31 +367,31 @@ cmp(Config) when is_list(Config) -> %% %% Test that one word refs works ref_eq(Config) when is_list(Config) -> - ?line ThisNode = {node(), erlang:system_info(creation)}, - ?line AnotherNode = {get_nodename(),2}, - ?line LLongRef = mk_ref(ThisNode, [4711, 0, 0]), - ?line LHalfLongRef = mk_ref(ThisNode, [4711, 0]), - ?line LShortRef = mk_ref(ThisNode, [4711]), - ?line true = LLongRef =:= LShortRef, - ?line true = LLongRef =:= LHalfLongRef, - ?line true = LLongRef =:= LLongRef, - ?line true = LHalfLongRef =:= LShortRef, - ?line true = LHalfLongRef =:= LHalfLongRef, - ?line true = LShortRef =:= LShortRef, - ?line false = LShortRef == mk_ref(ThisNode, [4711, 0, 1]), % Not any more - ?line RLongRef = mk_ref(AnotherNode, [4711, 0, 0]), - ?line RHalfLongRef = mk_ref(AnotherNode, [4711, 0]), - ?line RShortRef = mk_ref(AnotherNode, [4711]), - ?line true = RLongRef =:= RShortRef, - ?line true = RLongRef =:= RHalfLongRef, - ?line true = RLongRef =:= RLongRef, - ?line true = RHalfLongRef =:= RShortRef, - ?line true = RHalfLongRef =:= RHalfLongRef, - ?line true = RShortRef =:= RShortRef, - ?line false = RShortRef == mk_ref(AnotherNode, [4711, 0, 1]), % Not any more - ?line nc_refc_check(node()), - ?line ok. - + ThisNode = {node(), erlang:system_info(creation)}, + AnotherNode = {get_nodename(),2}, + LLongRef = mk_ref(ThisNode, [4711, 0, 0]), + LHalfLongRef = mk_ref(ThisNode, [4711, 0]), + LShortRef = mk_ref(ThisNode, [4711]), + true = LLongRef =:= LShortRef, + true = LLongRef =:= LHalfLongRef, + true = LLongRef =:= LLongRef, + true = LHalfLongRef =:= LShortRef, + true = LHalfLongRef =:= LHalfLongRef, + true = LShortRef =:= LShortRef, + false = LShortRef == mk_ref(ThisNode, [4711, 0, 1]), % Not any more + RLongRef = mk_ref(AnotherNode, [4711, 0, 0]), + RHalfLongRef = mk_ref(AnotherNode, [4711, 0]), + RShortRef = mk_ref(AnotherNode, [4711]), + true = RLongRef =:= RShortRef, + true = RLongRef =:= RHalfLongRef, + true = RLongRef =:= RLongRef, + true = RHalfLongRef =:= RShortRef, + true = RHalfLongRef =:= RHalfLongRef, + true = RShortRef =:= RShortRef, + false = RShortRef == mk_ref(AnotherNode, [4711, 0, 1]), % Not any more + nc_refc_check(node()), + ok. + %% %% Test case: node_table_gc %% @@ -399,48 +399,48 @@ ref_eq(Config) when is_list(Config) -> node_table_gc(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), erts_debug:set_internal_state(node_tab_delayed_delete, 0), - ?line PreKnown = nodes(known), - ?line io:format("PreKnown = ~p~n", [PreKnown]), - ?line make_node_garbage(0, 200000, 1000, []), - ?line PostKnown = nodes(known), - ?line PostAreas = erlang:system_info(allocated_areas), - ?line io:format("PostKnown = ~p~n", [PostKnown]), - ?line io:format("PostAreas = ~p~n", [PostAreas]), - ?line true = length(PostKnown) =< length(PreKnown), - ?line nc_refc_check(node()), + PreKnown = nodes(known), + io:format("PreKnown = ~p~n", [PreKnown]), + make_node_garbage(0, 200000, 1000, []), + PostKnown = nodes(known), + PostAreas = erlang:system_info(allocated_areas), + io:format("PostKnown = ~p~n", [PostKnown]), + io:format("PostAreas = ~p~n", [PostAreas]), + true = length(PostKnown) =< length(PreKnown), + nc_refc_check(node()), erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value - ?line ok. + ok. make_node_garbage(N, L, I, Ps) when N < L -> - ?line Self = self(), - ?line P = spawn_link(fun () -> - % Generate two node entries and one dist - % entry per node name - ?line PL1 = make_faked_pid_list(N, - I div 2, - 1), - ?line put(a, PL1), - ?line PL2 = make_faked_pid_list(N, - I div 2, - 2), - ?line put(b, PL2), - ?line Self ! {self(), length(nodes(known))} - end), - ?line receive - {P, KnownLength} -> - ?line true = KnownLength >= I div 2 - end, - ?line make_node_garbage(N+(I div 2)*2, L, I, [P|Ps]); + Self = self(), + P = spawn_link(fun () -> + % Generate two node entries and one dist + % entry per node name + PL1 = make_faked_pid_list(N, + I div 2, + 1), + put(a, PL1), + PL2 = make_faked_pid_list(N, + I div 2, + 2), + put(b, PL2), + Self ! {self(), length(nodes(known))} + end), + receive + {P, KnownLength} -> + true = KnownLength >= I div 2 + end, + make_node_garbage(N+(I div 2)*2, L, I, [P|Ps]); make_node_garbage(_, _, _, Ps) -> %% Cleanup garbage... ProcIsCleanedUp - = fun (Proc) -> - undefined == erts_debug:get_internal_state({process_status, - Proc}) - end, + = fun (Proc) -> + undefined == erts_debug:get_internal_state({process_status, + Proc}) + end, lists:foreach(fun (P) -> wait_until(fun () -> ProcIsCleanedUp(P) end) end, - Ps), - ?line ok. + Ps), + ok. make_faked_pid_list(Start, No, Creation) -> @@ -450,15 +450,15 @@ make_faked_pid_list(_Start, 0, _Creation, Acc) -> Acc; make_faked_pid_list(Start, No, Creation, Acc) -> make_faked_pid_list(Start+1, - No-1, - Creation, - [mk_pid({"faked_node-" - ++ integer_to_list(Start rem 50000) - ++ "@" - ++ atom_to_list(?MODULE), - Creation}, - 4711, - 3) | Acc]). + No-1, + Creation, + [mk_pid({"faked_node-" + ++ integer_to_list(Start rem 50000) + ++ "@" + ++ atom_to_list(?MODULE), + Creation}, + 4711, + 3) | Acc]). %% %% Test case: dist_link_refc @@ -466,33 +466,33 @@ make_faked_pid_list(Start, No, Creation, Acc) -> %% Tests that external reference counts are incremented and decremented %% as they should for distributed links dist_link_refc(Config) when is_list(Config) -> - ?line NodeFirstName = get_nodefirstname(), - ?line ?line {ok, Node} = start_node(NodeFirstName), - ?line RP = spawn_execer(Node), - ?line LP = spawn_link_execer(node()), - ?line true = sync_exec(RP, fun () -> link(LP) end), - ?line wait_until(fun () -> - ?line {links, Links} = process_info(LP, links), - ?line lists:member(RP, Links) - end), - ?line NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end), - ?line 1 = reference_type_count( - link, - refering_entity_id({process, LP}, - get_node_references({Node, NodeCre}))), - ?line exec(RP, fun() -> exit(normal) end), - ?line wait_until(fun () -> - ?line {links, Links} = process_info(LP, links), - ?line not lists:member(RP, Links) - end), - ?line 0 = reference_type_count( - link, - refering_entity_id({process, LP}, - get_node_references({Node, NodeCre}))), - ?line exit(LP, normal), - ?line stop_node(Node), - ?line nc_refc_check(node()), - ?line ok. + NodeFirstName = get_nodefirstname(), + {ok, Node} = start_node(NodeFirstName), + RP = spawn_execer(Node), + LP = spawn_link_execer(node()), + true = sync_exec(RP, fun () -> link(LP) end), + wait_until(fun () -> + {links, Links} = process_info(LP, links), + lists:member(RP, Links) + end), + NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end), + 1 = reference_type_count( + link, + refering_entity_id({process, LP}, + get_node_references({Node, NodeCre}))), + exec(RP, fun() -> exit(normal) end), + wait_until(fun () -> + {links, Links} = process_info(LP, links), + not lists:member(RP, Links) + end), + 0 = reference_type_count( + link, + refering_entity_id({process, LP}, + get_node_references({Node, NodeCre}))), + exit(LP, normal), + stop_node(Node), + nc_refc_check(node()), + ok. %% @@ -501,51 +501,51 @@ dist_link_refc(Config) when is_list(Config) -> %% Tests that external reference counts are incremented and decremented %% as they should for distributed monitors dist_monitor_refc(Config) when is_list(Config) -> - ?line NodeFirstName = get_nodefirstname(), - ?line {ok, Node} = start_node(NodeFirstName), - ?line RP = spawn_execer(Node), - ?line LP = spawn_link_execer(node()), - ?line RMon = sync_exec(RP, fun () -> erlang:monitor(process, LP) end), - ?line true = is_reference(RMon), - ?line LMon = sync_exec(LP, fun () -> erlang:monitor(process, RP) end), - ?line true = is_reference(LMon), - ?line NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end), - ?line wait_until(fun () -> - ?line {monitored_by, MonBy} - = process_info(LP, monitored_by), - ?line {monitors, Mon} - = process_info(LP, monitors), - ?line (lists:member(RP, MonBy) - and lists:member({process,RP}, Mon)) - end), - ?line 3 = reference_type_count( - monitor, - refering_entity_id({process, LP}, - get_node_references({Node, NodeCre}))), - ?line exec(RP, fun () -> exit(normal) end), - ?line wait_until(fun () -> - ?line {monitored_by, MonBy} - = process_info(LP, monitored_by), - ?line {monitors, Mon} - = process_info(LP, monitors), - ?line ((not lists:member(RP, MonBy)) - and (not lists:member({process,RP}, Mon))) - end), - ?line ok = sync_exec(LP, - fun () -> - receive - {'DOWN', LMon, process, _, _} -> - ok - end - end), - ?line 0 = reference_type_count( - link, - refering_entity_id({process, LP}, - get_node_references({Node, NodeCre}))), - ?line exit(LP, normal), - ?line stop_node(Node), - ?line nc_refc_check(node()), - ?line ok. + NodeFirstName = get_nodefirstname(), + {ok, Node} = start_node(NodeFirstName), + RP = spawn_execer(Node), + LP = spawn_link_execer(node()), + RMon = sync_exec(RP, fun () -> erlang:monitor(process, LP) end), + true = is_reference(RMon), + LMon = sync_exec(LP, fun () -> erlang:monitor(process, RP) end), + true = is_reference(LMon), + NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end), + wait_until(fun () -> + {monitored_by, MonBy} + = process_info(LP, monitored_by), + {monitors, Mon} + = process_info(LP, monitors), + (lists:member(RP, MonBy) + and lists:member({process,RP}, Mon)) + end), + 3 = reference_type_count( + monitor, + refering_entity_id({process, LP}, + get_node_references({Node, NodeCre}))), + exec(RP, fun () -> exit(normal) end), + wait_until(fun () -> + {monitored_by, MonBy} + = process_info(LP, monitored_by), + {monitors, Mon} + = process_info(LP, monitors), + ((not lists:member(RP, MonBy)) + and (not lists:member({process,RP}, Mon))) + end), + ok = sync_exec(LP, + fun () -> + receive + {'DOWN', LMon, process, _, _} -> + ok + end + end), + 0 = reference_type_count( + link, + refering_entity_id({process, LP}, + get_node_references({Node, NodeCre}))), + exit(LP, normal), + stop_node(Node), + nc_refc_check(node()), + ok. %% @@ -556,35 +556,35 @@ dist_monitor_refc(Config) when is_list(Config) -> node_controller_refc(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), erts_debug:set_internal_state(node_tab_delayed_delete, 0), - ?line NodeFirstName = get_nodefirstname(), - ?line ?line {ok, Node} = start_node(NodeFirstName), - ?line true = lists:member(Node, nodes()), - ?line 1 = reference_type_count(control, get_dist_references(Node)), - ?line P = spawn_link_execer(node()), - ?line Node - = sync_exec(P, - fun () -> - put(remote_net_kernel, - rpc:call(Node,erlang,whereis,[net_kernel])), - node(get(remote_net_kernel)) - end), - ?line Creation = rpc:call(Node, erlang, system_info, [creation]), - ?line monitor_node(Node,true), - ?line stop_node(Node), - ?line receive {nodedown, Node} -> ok end, - ?line DistRefs = get_dist_references(Node), - ?line true = reference_type_count(node, DistRefs) > 0, - ?line 0 = reference_type_count(control, DistRefs), + NodeFirstName = get_nodefirstname(), + {ok, Node} = start_node(NodeFirstName), + true = lists:member(Node, nodes()), + 1 = reference_type_count(control, get_dist_references(Node)), + P = spawn_link_execer(node()), + Node + = sync_exec(P, + fun () -> + put(remote_net_kernel, + rpc:call(Node,erlang,whereis,[net_kernel])), + node(get(remote_net_kernel)) + end), + Creation = rpc:call(Node, erlang, system_info, [creation]), + monitor_node(Node,true), + stop_node(Node), + receive {nodedown, Node} -> ok end, + DistRefs = get_dist_references(Node), + true = reference_type_count(node, DistRefs) > 0, + 0 = reference_type_count(control, DistRefs), % Get rid of all references to Node - ?line exec(P, fun () -> exit(normal) end), - ?line wait_until(fun () -> not is_process_alive(P) end), + exec(P, fun () -> exit(normal) end), + wait_until(fun () -> not is_process_alive(P) end), lists:foreach(fun (Proc) -> garbage_collect(Proc) end, processes()), - ?line false = get_node_references({Node,Creation}), - ?line false = get_dist_references(Node), - ?line false = lists:member(Node, nodes(known)), - ?line nc_refc_check(node()), + false = get_node_references({Node,Creation}), + false = get_dist_references(Node), + false = lists:member(Node, nodes(known)), + nc_refc_check(node()), erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value - ?line ok. + ok. %% %% Test case: ets_refc @@ -592,30 +592,30 @@ node_controller_refc(Config) when is_list(Config) -> %% Tests that external reference counts are incremented and decremented %% as they should for data stored in ets tables. ets_refc(Config) when is_list(Config) -> - ?line RNode = {get_nodename(), 1}, - ?line RPid = mk_pid(RNode, 4711, 2), - ?line RPort = mk_port(RNode, 4711), - ?line RRef = mk_ref(RNode, [4711, 47, 11]), - ?line Tab = ets:new(ets_refc, []), - ?line 0 = reference_type_count(ets, get_node_references(RNode)), - ?line true = ets:insert(Tab, [{a, self()}, - {b, RPid}, - {c, hd(erlang:ports())}, - {d, RPort}, - {e, make_ref()}]), - ?line 2 = reference_type_count(ets, get_node_references(RNode)), - ?line true = ets:insert(Tab, {f, RRef}), - ?line 3 = reference_type_count(ets, get_node_references(RNode)), - ?line true = ets:delete(Tab, d), - ?line 2 = reference_type_count(ets, get_node_references(RNode)), - ?line true = ets:delete_all_objects(Tab), - ?line 0 = reference_type_count(ets, get_node_references(RNode)), - ?line true = ets:insert(Tab, [{b, RPid}, {e, make_ref()}]), - ?line 1 = reference_type_count(ets, get_node_references(RNode)), - ?line true = ets:delete(Tab), - ?line 0 = reference_type_count(ets, get_node_references(RNode)), - ?line nc_refc_check(node()), - ?line ok. + RNode = {get_nodename(), 1}, + RPid = mk_pid(RNode, 4711, 2), + RPort = mk_port(RNode, 4711), + RRef = mk_ref(RNode, [4711, 47, 11]), + Tab = ets:new(ets_refc, []), + 0 = reference_type_count(ets, get_node_references(RNode)), + true = ets:insert(Tab, [{a, self()}, + {b, RPid}, + {c, hd(erlang:ports())}, + {d, RPort}, + {e, make_ref()}]), + 2 = reference_type_count(ets, get_node_references(RNode)), + true = ets:insert(Tab, {f, RRef}), + 3 = reference_type_count(ets, get_node_references(RNode)), + true = ets:delete(Tab, d), + 2 = reference_type_count(ets, get_node_references(RNode)), + true = ets:delete_all_objects(Tab), + 0 = reference_type_count(ets, get_node_references(RNode)), + true = ets:insert(Tab, [{b, RPid}, {e, make_ref()}]), + 1 = reference_type_count(ets, get_node_references(RNode)), + true = ets:delete(Tab), + 0 = reference_type_count(ets, get_node_references(RNode)), + nc_refc_check(node()), + ok. %% %% Test case: match_spec_refc @@ -623,34 +623,34 @@ ets_refc(Config) when is_list(Config) -> %% Tests that external reference counts are incremented and decremented %% as they should for data stored in match specifications. match_spec_refc(Config) when is_list(Config) -> - ?line RNode = {get_nodename(), 1}, - ?line RPid = mk_pid(RNode, 4711, 2), - ?line RPort = mk_port(RNode, 4711), - ?line RRef = mk_ref(RNode, [4711, 47, 11]), - ?line ok = do_match_spec_test(RNode, RPid, RPort, RRef), - ?line garbage_collect(), - ?line NodeRefs = get_node_references(RNode), - ?line 0 = reference_type_count(binary, NodeRefs), - ?line 0 = reference_type_count(ets, NodeRefs), - ?line nc_refc_check(node()), - ?line ok. + RNode = {get_nodename(), 1}, + RPid = mk_pid(RNode, 4711, 2), + RPort = mk_port(RNode, 4711), + RRef = mk_ref(RNode, [4711, 47, 11]), + ok = do_match_spec_test(RNode, RPid, RPort, RRef), + garbage_collect(), + NodeRefs = get_node_references(RNode), + 0 = reference_type_count(binary, NodeRefs), + 0 = reference_type_count(ets, NodeRefs), + nc_refc_check(node()), + ok. do_match_spec_test(RNode, RPid, RPort, RRef) -> - ?line Tab = ets:new(match_spec_refc, []), - ?line true = ets:insert(Tab, [{a, RPid, RPort, RRef}, - {b, self(), RPort, RRef}, - {c, RPid, RPort, make_ref()}, - {d, RPid, RPort, RRef}]), - ?line {M1, C1} = ets:select(Tab, [{{'$1',RPid,RPort,RRef},[],['$1']}], 1), - ?line NodeRefs = get_node_references(RNode), - ?line 3 = reference_type_count(binary, NodeRefs), - ?line 10 = reference_type_count(ets, NodeRefs), - ?line {M2, C2} = ets:select(C1), - ?line '$end_of_table' = ets:select(C2), - ?line ets:delete(Tab), - ?line [a,d] = lists:sort(M1++M2), - ?line ok. - + Tab = ets:new(match_spec_refc, []), + true = ets:insert(Tab, [{a, RPid, RPort, RRef}, + {b, self(), RPort, RRef}, + {c, RPid, RPort, make_ref()}, + {d, RPid, RPort, RRef}]), + {M1, C1} = ets:select(Tab, [{{'$1',RPid,RPort,RRef},[],['$1']}], 1), + NodeRefs = get_node_references(RNode), + 3 = reference_type_count(binary, NodeRefs), + 10 = reference_type_count(ets, NodeRefs), + {M2, C2} = ets:select(C1), + '$end_of_table' = ets:select(C2), + ets:delete(Tab), + [a,d] = lists:sort(M1++M2), + ok. + %% %% Test case: ets_refc @@ -658,66 +658,66 @@ do_match_spec_test(RNode, RPid, RPort, RRef) -> %% Tests that external reference counts are incremented and decremented %% as they should for data stored in bif timers. timer_refc(Config) when is_list(Config) -> - ?line RNode = {get_nodename(), 1}, - ?line RPid = mk_pid(RNode, 4711, 2), - ?line RPort = mk_port(RNode, 4711), - ?line RRef = mk_ref(RNode, [4711, 47, 11]), - ?line 0 = reference_type_count(timer, get_node_references(RNode)), - ?line Pid = spawn(fun () -> receive after infinity -> ok end end), - ?line erlang:start_timer(10000, Pid, {RPid, RPort, RRef}), - ?line 3 = reference_type_count(timer, get_node_references(RNode)), - ?line exit(Pid, kill), - ?line Mon = erlang:monitor(process, Pid), - ?line receive {'DOWN', Mon, process, Pid, _} -> ok end, - ?line 0 = reference_type_count(timer, get_node_references(RNode)), - ?line erlang:send_after(500, Pid, {timer, RPid, RPort, RRef}), - ?line 0 = reference_type_count(timer, get_node_references(RNode)), - ?line erlang:send_after(500, self(), {timer, RPid, RPort, RRef}), - ?line erlang:send_after(400, bananfluga, {timer, RPid, RPort, RRef}), - ?line 6 = reference_type_count(timer, get_node_references(RNode)), - ?line receive {timer, RPid, RPort, RRef} -> ok end, - ?line 0 = reference_type_count(timer, get_node_references(RNode)), - ?line nc_refc_check(node()), - ?line ok. + RNode = {get_nodename(), 1}, + RPid = mk_pid(RNode, 4711, 2), + RPort = mk_port(RNode, 4711), + RRef = mk_ref(RNode, [4711, 47, 11]), + 0 = reference_type_count(timer, get_node_references(RNode)), + Pid = spawn(fun () -> receive after infinity -> ok end end), + erlang:start_timer(10000, Pid, {RPid, RPort, RRef}), + 3 = reference_type_count(timer, get_node_references(RNode)), + exit(Pid, kill), + Mon = erlang:monitor(process, Pid), + receive {'DOWN', Mon, process, Pid, _} -> ok end, + 0 = reference_type_count(timer, get_node_references(RNode)), + erlang:send_after(500, Pid, {timer, RPid, RPort, RRef}), + 0 = reference_type_count(timer, get_node_references(RNode)), + erlang:send_after(500, self(), {timer, RPid, RPort, RRef}), + erlang:send_after(400, bananfluga, {timer, RPid, RPort, RRef}), + 6 = reference_type_count(timer, get_node_references(RNode)), + receive {timer, RPid, RPort, RRef} -> ok end, + 0 = reference_type_count(timer, get_node_references(RNode)), + nc_refc_check(node()), + ok. otp_4715(Config) when is_list(Config) -> case test_server:is_release_available("r9b") of - true -> otp_4715_1(Config); - false -> {skip,"No R9B found"} + true -> otp_4715_1(Config); + false -> {skip,"No R9B found"} end. otp_4715_1(Config) -> case erlang:system_info(compat_rel) of - 9 -> - ?line run_otp_4715(Config); - _ -> - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line test_server:run_on_shielded_node(fun () -> - run_otp_4715(Config) - end, - "+R9 -pa " ++ Pa) + 9 -> + run_otp_4715(Config); + _ -> + Pa = filename:dirname(code:which(?MODULE)), + test_server:run_on_shielded_node(fun () -> + run_otp_4715(Config) + end, + "+R9 -pa " ++ Pa) end. run_otp_4715(Config) when is_list(Config) -> - ?line erts_debug:set_internal_state(available_internal_state, true), - ?line PidList = [mk_pid({a@b, 1}, 4710, 2), - mk_pid({a@b, 1}, 4712, 1), - mk_pid({c@b, 1}, 4711, 1), - mk_pid({b@b, 3}, 4711, 1), - mk_pid({b@b, 2}, 4711, 1)], + erts_debug:set_internal_state(available_internal_state, true), + PidList = [mk_pid({a@b, 1}, 4710, 2), + mk_pid({a@b, 1}, 4712, 1), + mk_pid({c@b, 1}, 4711, 1), + mk_pid({b@b, 3}, 4711, 1), + mk_pid({b@b, 2}, 4711, 1)], - ?line R9Sorted = old_mod:sort_on_old_node(PidList), - ?line R9Sorted = lists:sort(PidList). + R9Sorted = old_mod:sort_on_old_node(PidList), + R9Sorted = lists:sort(PidList). -pid_wrap(Config) when is_list(Config) -> ?line pp_wrap(pid). +pid_wrap(Config) when is_list(Config) -> pp_wrap(pid). port_wrap(Config) when is_list(Config) -> - ?line case test_server:os_type() of - {unix, _} -> - ?line pp_wrap(port); - _ -> - ?line {skip, "Only run on unix"} - end. + case test_server:os_type() of + {unix, _} -> + pp_wrap(port); + _ -> + {skip, "Only run on unix"} + end. get_next_id(pid) -> erts_debug:get_internal_state(next_pid); @@ -730,91 +730,91 @@ set_next_id(port, N) -> erts_debug:set_internal_state(next_port, N). pp_wrap(What) -> - ?line N = set_high_pp_next(What), - ?line Cre = N + 100, - ?line io:format("no creations = ~p~n", [Cre]), - ?line PreCre = get_next_id(What), - ?line io:format("pre creations = ~p~n", [PreCre]), - ?line true = is_integer(PreCre), - ?line do_pp_creations(What, Cre), - ?line PostCre = get_next_id(What), - ?line io:format("post creations = ~p~n", [PostCre]), - ?line true = is_integer(PostCre), - ?line true = PreCre > PostCre, - ?line Now = set_next_id(What, ?MAX_PIDS_PORTS div 2), - ?line io:format("reset to = ~p~n", [Now]), - ?line true = is_integer(Now), - ?line ok. + N = set_high_pp_next(What), + Cre = N + 100, + io:format("no creations = ~p~n", [Cre]), + PreCre = get_next_id(What), + io:format("pre creations = ~p~n", [PreCre]), + true = is_integer(PreCre), + do_pp_creations(What, Cre), + PostCre = get_next_id(What), + io:format("post creations = ~p~n", [PostCre]), + true = is_integer(PostCre), + true = PreCre > PostCre, + Now = set_next_id(What, ?MAX_PIDS_PORTS div 2), + io:format("reset to = ~p~n", [Now]), + true = is_integer(Now), + ok. set_high_pp_next(What) -> - ?line set_high_pp_next(What, ?MAX_PIDS_PORTS-1). - + set_high_pp_next(What, ?MAX_PIDS_PORTS-1). + set_high_pp_next(What, N) -> - ?line M = set_next_id(What, N), - ?line true = is_integer(M), - ?line case {M >= N, M =< ?MAX_PIDS_PORTS} of - {true, true} -> - ?line ?MAX_PIDS_PORTS - M + 1; - _ -> - ?line set_high_pp_next(What, N - 100) - end. + M = set_next_id(What, N), + true = is_integer(M), + case {M >= N, M =< ?MAX_PIDS_PORTS} of + {true, true} -> + ?MAX_PIDS_PORTS - M + 1; + _ -> + set_high_pp_next(What, N - 100) + end. do_pp_creations(_What, N) when is_integer(N), N =< 0 -> - ?line done; + done; do_pp_creations(pid, N) when is_integer(N) -> %% Create new pid and make sure it works... - ?line Me = self(), - ?line Ref = make_ref(), - ?line Pid = spawn_link(fun () -> - receive - Ref -> - Me ! Ref - end - end), - ?line Pid ! Ref, - ?line receive - Ref -> - ?line do_pp_creations(pid, N - 1) - end; + Me = self(), + Ref = make_ref(), + Pid = spawn_link(fun () -> + receive + Ref -> + Me ! Ref + end + end), + Pid ! Ref, + receive + Ref -> + do_pp_creations(pid, N - 1) + end; do_pp_creations(port, N) when is_integer(N) -> %% Create new port and make sure it works... - ?line "hej" = os:cmd("echo hej") -- "\n", - ?line do_pp_creations(port, N - 1). + "hej" = os:cmd("echo hej") -- "\n", + do_pp_creations(port, N - 1). bad_nc(Config) when is_list(Config) -> % Make sure emulator don't crash on bad node containers... - ?line MaxPidNum = (1 bsl 15) - 1, - ?line MaxPidSer = ?MAX_PIDS_PORTS bsr 15, - ?line ThisNode = {node(), erlang:system_info(creation)}, - ?line {'EXIT', {badarg, mk_pid, _}} - = (catch mk_pid(ThisNode, MaxPidNum + 1, 17)), - ?line {'EXIT', {badarg, mk_pid, _}} - = (catch mk_pid(ThisNode, 4711, MaxPidSer + 1)), - ?line {'EXIT', {badarg, mk_port, _}} - = (catch mk_port(ThisNode, ?MAX_PIDS_PORTS + 1)), - ?line {'EXIT', {badarg, mk_ref, _}} - = (catch mk_ref(ThisNode,[(1 bsl 18), 4711, 4711])), - ?line {'EXIT', {badarg, mk_ref, _}} - = (catch mk_ref(ThisNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])), - ?line RemNode = {x@y, 2}, - ?line {'EXIT', {badarg, mk_pid, _}} - = (catch mk_pid(RemNode, MaxPidNum + 1, MaxPidSer)), - ?line {'EXIT', {badarg, mk_pid, _}} - = (catch mk_pid(RemNode, MaxPidNum, MaxPidSer + 1)), - ?line {'EXIT', {badarg, mk_port, _}} - = (catch mk_port(RemNode, ?MAX_PIDS_PORTS + 1)), - ?line {'EXIT', {badarg, mk_ref, _}} - = (catch mk_ref(RemNode, [(1 bsl 18), 4711, 4711])), - ?line {'EXIT', {badarg, mk_ref, _}} - = (catch mk_ref(RemNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])), - ?line BadNode = {x@y, 4}, - ?line {'EXIT', {badarg, mk_pid, _}} - = (catch mk_pid(BadNode, 4711, 17)), - ?line {'EXIT', {badarg, mk_port, _}} - = (catch mk_port(BadNode, 4711)), - ?line {'EXIT', {badarg, mk_ref, _}} - = (catch mk_ref(BadNode, [4711, 4711, 17])), - ?line ok. + MaxPidNum = (1 bsl 15) - 1, + MaxPidSer = ?MAX_PIDS_PORTS bsr 15, + ThisNode = {node(), erlang:system_info(creation)}, + {'EXIT', {badarg, mk_pid, _}} + = (catch mk_pid(ThisNode, MaxPidNum + 1, 17)), + {'EXIT', {badarg, mk_pid, _}} + = (catch mk_pid(ThisNode, 4711, MaxPidSer + 1)), + {'EXIT', {badarg, mk_port, _}} + = (catch mk_port(ThisNode, ?MAX_PIDS_PORTS + 1)), + {'EXIT', {badarg, mk_ref, _}} + = (catch mk_ref(ThisNode,[(1 bsl 18), 4711, 4711])), + {'EXIT', {badarg, mk_ref, _}} + = (catch mk_ref(ThisNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])), + RemNode = {x@y, 2}, + {'EXIT', {badarg, mk_pid, _}} + = (catch mk_pid(RemNode, MaxPidNum + 1, MaxPidSer)), + {'EXIT', {badarg, mk_pid, _}} + = (catch mk_pid(RemNode, MaxPidNum, MaxPidSer + 1)), + {'EXIT', {badarg, mk_port, _}} + = (catch mk_port(RemNode, ?MAX_PIDS_PORTS + 1)), + {'EXIT', {badarg, mk_ref, _}} + = (catch mk_ref(RemNode, [(1 bsl 18), 4711, 4711])), + {'EXIT', {badarg, mk_ref, _}} + = (catch mk_ref(RemNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])), + BadNode = {x@y, 4}, + {'EXIT', {badarg, mk_pid, _}} + = (catch mk_pid(BadNode, 4711, 17)), + {'EXIT', {badarg, mk_port, _}} + = (catch mk_port(BadNode, 4711)), + {'EXIT', {badarg, mk_ref, _}} + = (catch mk_ref(BadNode, [4711, 4711, 17])), + ok. @@ -822,69 +822,69 @@ bad_nc(Config) when is_list(Config) -> unique_pid(Config) when is_list(Config) -> case catch erlang:system_info(modified_timing_level) of - Level when is_integer(Level) -> - {skip, - "Modified timing (level " ++ integer_to_list(Level) - ++ ") is enabled. spawn() is too slow for this " - " test when modified timing is enabled."}; - _ -> - ?line ?NO_PIDS = length(lists:usort(mkpidlist(?NO_PIDS, []))), - ?line ok + Level when is_integer(Level) -> + {skip, + "Modified timing (level " ++ integer_to_list(Level) + ++ ") is enabled. spawn() is too slow for this " + " test when modified timing is enabled."}; + _ -> + ?NO_PIDS = length(lists:usort(mkpidlist(?NO_PIDS, []))), + ok end. - + mkpidlist(0, Ps) -> Ps; mkpidlist(N, Ps) -> mkpidlist(N-1, [spawn(fun () -> ok end)|Ps]). iter_max_procs(Config) when is_list(Config) -> - ?line NoMoreTests = make_ref(), - ?line erlang:send_after(10000, self(), NoMoreTests), - ?line Res = chk_max_proc_line(), - ?line Res = chk_max_proc_line(), - ?line done = chk_max_proc_line_until(NoMoreTests, Res), - ?line {comment, - io_lib:format("max processes = ~p; " - "process line length = ~p", - [element(2, Res), element(1, Res)])}. - - + NoMoreTests = make_ref(), + erlang:send_after(10000, self(), NoMoreTests), + Res = chk_max_proc_line(), + Res = chk_max_proc_line(), + done = chk_max_proc_line_until(NoMoreTests, Res), + {comment, + io_lib:format("max processes = ~p; " + "process line length = ~p", + [element(2, Res), element(1, Res)])}. + + max_proc_line(Root, Parent, N) -> Me = self(), case catch spawn_link(fun () -> max_proc_line(Root, Me, N+1) end) of - {'EXIT', {system_limit, _}} when Root /= self() -> - Root ! {proc_line_length, N, self()}, - receive remove_proc_line -> Parent ! {exiting, Me} end; - P when is_pid(P), Root =/= self() -> - receive {exiting, P} -> Parent ! {exiting, Me} end; - P when is_pid(P) -> - P; - Unexpected -> - exit({unexpected_spawn_result, Unexpected}) + {'EXIT', {system_limit, _}} when Root /= self() -> + Root ! {proc_line_length, N, self()}, + receive remove_proc_line -> Parent ! {exiting, Me} end; + P when is_pid(P), Root =/= self() -> + receive {exiting, P} -> Parent ! {exiting, Me} end; + P when is_pid(P) -> + P; + Unexpected -> + exit({unexpected_spawn_result, Unexpected}) end. chk_max_proc_line() -> - ?line Child = max_proc_line(self(), self(), 0), - ?line receive - {proc_line_length, PLL, End} -> - ?line PC = erlang:system_info(process_count), - ?line LP = length(processes()), - ?line io:format("proc line length = ~p; " - "process count = ~p; " - "length processes = ~p~n", - [PLL, PC, LP]), - ?line End ! remove_proc_line, - ?line PC = LP, - ?line receive {exiting, Child} -> ok end, - ?line {PLL, PC} - end. + Child = max_proc_line(self(), self(), 0), + receive + {proc_line_length, PLL, End} -> + PC = erlang:system_info(process_count), + LP = length(processes()), + io:format("proc line length = ~p; " + "process count = ~p; " + "length processes = ~p~n", + [PLL, PC, LP]), + End ! remove_proc_line, + PC = LP, + receive {exiting, Child} -> ok end, + {PLL, PC} + end. chk_max_proc_line_until(NoMoreTests, Res) -> receive - NoMoreTests -> - ?line done + NoMoreTests -> + done after 0 -> - ?line Res = chk_max_proc_line(), - ?line chk_max_proc_line_until(NoMoreTests, Res) + Res = chk_max_proc_line(), + chk_max_proc_line_until(NoMoreTests, Res) end. %% @@ -903,124 +903,124 @@ nc_refc_check(Node) when is_atom(Node) -> Self = self(), io:format("Starting reference count check of node ~w~n", [Node]), spawn_link(Node, - fun () -> - {{node_references, NodeRefs}, - {dist_references, DistRefs}} = ?ND_REFS, - check_nd_refc({node(), erlang:system_info(creation)}, - NodeRefs, - DistRefs, - fun (ErrMsg) -> - Self ! {Ref, ErrMsg, failed}, - exit(normal) - end), - Self ! {Ref, succeded} - end), + fun () -> + {{node_references, NodeRefs}, + {dist_references, DistRefs}} = ?ND_REFS, + check_nd_refc({node(), erlang:system_info(creation)}, + NodeRefs, + DistRefs, + fun (ErrMsg) -> + Self ! {Ref, ErrMsg, failed}, + exit(normal) + end), + Self ! {Ref, succeded} + end), receive - {Ref, ErrorMsg, failed} -> - io:format("~s~n", [ErrorMsg]), - ct:fail(reference_count_check_failed); - {Ref, succeded} -> - io:format("Reference count check of node ~w succeded!~n", [Node]), - ok + {Ref, ErrorMsg, failed} -> + io:format("~s~n", [ErrorMsg]), + ct:fail(reference_count_check_failed); + {Ref, succeded} -> + io:format("Reference count check of node ~w succeded!~n", [Node]), + ok end. check_nd_refc({ThisNodeName, ThisCreation}, NodeRefs, DistRefs, Fail) -> case catch begin - check_refc(ThisNodeName,ThisCreation,"node table",NodeRefs), - check_refc(ThisNodeName,ThisCreation,"dist table",DistRefs), - ok - end of - ok -> - ok; - {'EXIT', Reason} -> - {Y,Mo,D} = date(), - {H,Mi,S} = time(), - ErrMsg = io_lib:format("~n" - "*** Reference count check of node ~w " - "failed (~p) at ~w~w~w ~w:~w:~w~n" - "*** Node table references:~n ~p~n" - "*** Dist table references:~n ~p~n", - [node(), Reason, Y, Mo, D, H, Mi, S, - NodeRefs, DistRefs]), - Fail(lists:flatten(ErrMsg)) + check_refc(ThisNodeName,ThisCreation,"node table",NodeRefs), + check_refc(ThisNodeName,ThisCreation,"dist table",DistRefs), + ok + end of + ok -> + ok; + {'EXIT', Reason} -> + {Y,Mo,D} = date(), + {H,Mi,S} = time(), + ErrMsg = io_lib:format("~n" + "*** Reference count check of node ~w " + "failed (~p) at ~w~w~w ~w:~w:~w~n" + "*** Node table references:~n ~p~n" + "*** Dist table references:~n ~p~n", + [node(), Reason, Y, Mo, D, H, Mi, S, + NodeRefs, DistRefs]), + Fail(lists:flatten(ErrMsg)) end. check_refc(ThisNodeName,ThisCreation,Table,EntryList) when is_list(EntryList) -> lists:foreach( fun ({Entry, Refc, ReferrerList}) -> - {DelayedDeleteTimer, - FoundRefs} = - lists:foldl( - fun ({Referrer, ReferencesList}, {DDT, A1}) -> - {case Referrer of - {system,delayed_delete_timer} -> - true; - _ -> - DDT - end, - A1 + lists:foldl(fun ({_T,Rs},A2) -> - A2+Rs - end, - 0, - ReferencesList)} - end, - {false, 0}, - ReferrerList), - - %% Reference count equals found references? - case {Refc, FoundRefs, DelayedDeleteTimer} of - {X, X, _} -> - ok; - {0, 1, true} -> - ok; - _ -> - exit({invalid_reference_count, Table, Entry}) - end, - - %% All entries in table referred to? - case {Entry, Refc} of - {ThisNodeName, 0} -> ok; - {{ThisNodeName, ThisCreation}, 0} -> ok; - {_, 0} when DelayedDeleteTimer == false -> - exit({not_referred_entry_in_table, Table, Entry}); - {_, _} -> ok - end + {DelayedDeleteTimer, + FoundRefs} = + lists:foldl( + fun ({Referrer, ReferencesList}, {DDT, A1}) -> + {case Referrer of + {system,delayed_delete_timer} -> + true; + _ -> + DDT + end, + A1 + lists:foldl(fun ({_T,Rs},A2) -> + A2+Rs + end, + 0, + ReferencesList)} + end, + {false, 0}, + ReferrerList), + + %% Reference count equals found references? + case {Refc, FoundRefs, DelayedDeleteTimer} of + {X, X, _} -> + ok; + {0, 1, true} -> + ok; + _ -> + exit({invalid_reference_count, Table, Entry}) + end, + + %% All entries in table referred to? + case {Entry, Refc} of + {ThisNodeName, 0} -> ok; + {{ThisNodeName, ThisCreation}, 0} -> ok; + {_, 0} when DelayedDeleteTimer == false -> + exit({not_referred_entry_in_table, Table, Entry}); + {_, _} -> ok + end end, EntryList), ok. get_node_references({NodeName, Creation} = Node) when is_atom(NodeName), - is_integer(Creation) -> + is_integer(Creation) -> {{node_references, NodeRefs}, - {dist_references, DistRefs}} = ?ND_REFS, + {dist_references, DistRefs}} = ?ND_REFS, check_nd_refc({node(), erlang:system_info(creation)}, - NodeRefs, - DistRefs, - fun (ErrMsg) -> - io:format("~s", [ErrMsg]), - ct:fail(reference_count_check_failed) - end), + NodeRefs, + DistRefs, + fun (ErrMsg) -> + io:format("~s", [ErrMsg]), + ct:fail(reference_count_check_failed) + end), find_references(Node, NodeRefs). get_dist_references(NodeName) when is_atom(NodeName) -> - ?line {{node_references, NodeRefs}, - {dist_references, DistRefs}} = ?ND_REFS, - ?line check_nd_refc({node(), erlang:system_info(creation)}, - NodeRefs, - DistRefs, - fun (ErrMsg) -> - ?line io:format("~s", [ErrMsg]), - ?line ct:fail(reference_count_check_failed) - end), - ?line find_references(NodeName, DistRefs). + {{node_references, NodeRefs}, + {dist_references, DistRefs}} = ?ND_REFS, + check_nd_refc({node(), erlang:system_info(creation)}, + NodeRefs, + DistRefs, + fun (ErrMsg) -> + io:format("~s", [ErrMsg]), + ct:fail(reference_count_check_failed) + end), + find_references(NodeName, DistRefs). find_references(N, NRefList) -> case lists:keysearch(N, 1, NRefList) of - {value, {N, _, ReferrersList}} -> ReferrersList; - _ -> false - end. + {value, {N, _, ReferrersList}} -> ReferrersList; + _ -> false + end. %% Currently unused % refering_entity_type(RefererType, ReferingEntities) -> @@ -1032,7 +1032,7 @@ find_references(N, NRefList) -> % ReferingEntities). refering_entity_id(ReferingEntityId, [{ReferingEntityId,_} = ReferingEntity - | _ReferingEntities]) -> + | _ReferingEntities]) -> ReferingEntity; refering_entity_id(ReferingEntityId, [_ | ReferingEntities]) -> refering_entity_id(ReferingEntityId, ReferingEntities); @@ -1045,34 +1045,34 @@ reference_type_count(Type, {_, _ReferenceCountList} = ReferingEntity) -> reference_type_count(Type, [ReferingEntity]); reference_type_count(Type, ReferingEntities) when is_list(ReferingEntities) -> lists:foldl(fun ({_, ReferenceCountList}, Acc1) -> - lists:foldl(fun ({T, N}, Acc2) when T == Type -> - N + Acc2; - (_, Acc2) -> - Acc2 - end, - Acc1, - ReferenceCountList) - end, - 0, - ReferingEntities). + lists:foldl(fun ({T, N}, Acc2) when T == Type -> + N + Acc2; + (_, Acc2) -> + Acc2 + end, + Acc1, + ReferenceCountList) + end, + 0, + ReferingEntities). start_node(Name, Args) -> - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line Res = test_server:start_node(Name, - slave, - [{args, "-pa "++Pa++" "++Args}]), - ?line {ok, Node} = Res, - ?line rpc:call(Node, erts_debug, set_internal_state, - [available_internal_state, true]), - ?line Res. - + Pa = filename:dirname(code:which(?MODULE)), + Res = test_server:start_node(Name, + slave, + [{args, "-pa "++Pa++" "++Args}]), + {ok, Node} = Res, + rpc:call(Node, erts_debug, set_internal_state, + [available_internal_state, true]), + Res. + start_node(Name) -> - ?line start_node(Name, ""). + start_node(Name, ""). stop_node(Node) -> - ?line nc_refc_check(Node), - ?line true = test_server:stop_node(Node). + nc_refc_check(Node), + true = test_server:stop_node(Node). hostname() -> from($@, atom_to_list(node())). @@ -1083,25 +1083,25 @@ from(_H, []) -> []. wait_until(Pred) -> case Pred() of - true -> ok; - false -> receive after 100 -> wait_until(Pred) end + true -> ok; + false -> receive after 100 -> wait_until(Pred) end end. get_nodefirstname_string() -> atom_to_list(?MODULE) - ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) - ++ "-" - ++ integer_to_list(erlang:unique_integer([positive])). + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive])). get_nodefirstname() -> list_to_atom(get_nodefirstname_string()). get_nodename() -> list_to_atom(get_nodefirstname_string() - ++ "@" - ++ hostname()). - + ++ "@" + ++ hostname()). + -define(VERSION_MAGIC, 131). @@ -1138,88 +1138,88 @@ mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) -> mk_pid({atom_to_list(NodeName), Creation}, Number, Serial); mk_pid({NodeName, Creation}, Number, Serial) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PID_EXT, - ?ATOM_EXT, - uint16_be(length(NodeName)), - NodeName, - uint32_be(Number), - uint32_be(Serial), - uint8(Creation)])) of - Pid when is_pid(Pid) -> - Pid; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?PID_EXT, + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + uint32_be(Serial), + uint8(Creation)])) of + Pid when is_pid(Pid) -> + Pid; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. mk_port({NodeName, Creation}, Number) when is_atom(NodeName) -> mk_port({atom_to_list(NodeName), Creation}, Number); mk_port({NodeName, Creation}, Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PORT_EXT, - ?ATOM_EXT, - uint16_be(length(NodeName)), - NodeName, - uint32_be(Number), - uint8(Creation)])) of - Port when is_port(Port) -> - Port; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_port, [{NodeName, Creation}, Number]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?PORT_EXT, + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + uint8(Creation)])) of + Port when is_port(Port) -> + Port; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_port, [{NodeName, Creation}, Number]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName), - is_integer(Creation), - is_list(Numbers) -> + is_integer(Creation), + is_list(Numbers) -> mk_ref({atom_to_list(NodeName), Creation}, Numbers); mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName), - is_integer(Creation), - is_integer(Number) -> + is_integer(Creation), + is_integer(Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?REFERENCE_EXT, - ?ATOM_EXT, - uint16_be(length(NodeName)), - NodeName, - uint32_be(Number), - uint8(Creation)])) of - Ref when is_reference(Ref) -> - Ref; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?REFERENCE_EXT, + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + uint8(Creation)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end; mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName), - is_integer(Creation), - is_list(Numbers) -> + is_integer(Creation), + is_list(Numbers) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?NEW_REFERENCE_EXT, - uint16_be(length(Numbers)), - ?ATOM_EXT, - uint16_be(length(NodeName)), - NodeName, - uint8(Creation), - lists:map(fun (N) -> - uint32_be(N) - end, - Numbers)])) of - Ref when is_reference(Ref) -> - Ref; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?NEW_REFERENCE_EXT, + uint16_be(length(Numbers)), + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint8(Creation), + lists:map(fun (N) -> + uint32_be(N) + end, + Numbers)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. exec_loop() -> receive - {exec_fun, Fun} when is_function(Fun) -> - Fun(); - {sync_exec_fun, From, Fun} when is_pid(From), is_function(Fun) -> - From ! {sync_exec_fun_res, self(), Fun()} + {exec_fun, Fun} when is_function(Fun) -> + Fun(); + {sync_exec_fun, From, Fun} when is_pid(From), is_function(Fun) -> + From ! {sync_exec_fun_res, self(), Fun()} end, exec_loop(). @@ -1235,6 +1235,6 @@ exec(Pid, Fun) when is_pid(Pid), is_function(Fun) -> sync_exec(Pid, Fun) when is_pid(Pid), is_function(Fun) -> Pid ! {sync_exec_fun, self(), Fun}, receive - {sync_exec_fun_res, Pid, Res} -> - Res + {sync_exec_fun_res, Pid, Res} -> + Res end. diff --git a/erts/emulator/test/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl index 14851d376f..9cc851f9cf 100644 --- a/erts/emulator/test/nofrag_SUITE.erl +++ b/erts/emulator/test/nofrag_SUITE.erl @@ -23,9 +23,9 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, - error_handler/1,error_handler_apply/1, - error_handler_fixed_apply/1,error_handler_fun/1, - debug_breakpoint/1]). + error_handler/1,error_handler_apply/1, + error_handler_fixed_apply/1,error_handler_fun/1, + debug_breakpoint/1]). %% Exported functions for an error_handler module. -export([undefined_function/3,undefined_lambda/3,breakpoint/3]). @@ -40,13 +40,13 @@ all() -> debug_breakpoint]. error_handler(Config) when is_list(Config) -> - ?line process_flag(error_handler, ?MODULE), + process_flag(error_handler, ?MODULE), %% The term_to_binary/1 - binary_to_term/1 roundtrip is a good way %% to traverse the entire term. - ?line Term = collect(1024), - ?line Term = binary_to_term(term_to_binary(Term)), - ?line 1024 = length(Term), - ?line [[a,b,c,d,[e,f,g]]] = lists:usort(Term), + Term = collect(1024), + Term = binary_to_term(term_to_binary(Term)), + 1024 = length(Term), + [[a,b,c,d,[e,f,g]]] = lists:usort(Term), ok. collect(0) -> @@ -81,25 +81,25 @@ collect_apply(N, Mod) -> [C|Res]. error_handler_apply(Config) when is_list(Config) -> - ?line process_flag(error_handler, ?MODULE), + process_flag(error_handler, ?MODULE), %% The term_to_binary/1 - binary_to_term/1 roundtrip is a good way %% to traverse the entire term. - ?line Term = collect_apply(1024, fooblurfbar), - ?line Term = binary_to_term(term_to_binary(Term)), - ?line 1024 = length(Term), - ?line [[{a,42},b,c,d,[e,f,g]]] = lists:usort(Term), + Term = collect_apply(1024, fooblurfbar), + Term = binary_to_term(term_to_binary(Term)), + 1024 = length(Term), + [[{a,42},b,c,d,[e,f,g]]] = lists:usort(Term), ok. error_handler_fixed_apply(Config) when is_list(Config) -> - ?line process_flag(error_handler, ?MODULE), + process_flag(error_handler, ?MODULE), %% The term_to_binary/1 - binary_to_term/1 roundtrip is a good way %% to traverse the entire term. - ?line Term = collect_fixed_apply(1024, fooblurfbar), - ?line Term = binary_to_term(term_to_binary(Term)), - ?line 1024 = length(Term), - ?line [[{a,2},b,c,d,[e,f,g]]] = lists:usort(Term), + Term = collect_fixed_apply(1024, fooblurfbar), + Term = binary_to_term(term_to_binary(Term)), + 1024 = length(Term), + [[{a,2},b,c,d,[e,f,g]]] = lists:usort(Term), ok. collect_fixed_apply(0, _) -> @@ -121,19 +121,19 @@ undefined_function(_Mod, _Name, Args) -> Args. error_handler_fun(Config) when is_list(Config) -> - ?line process_flag(error_handler, ?MODULE), + process_flag(error_handler, ?MODULE), %% fun(A, B, C) -> {A,B,C,X} end in module foobarblurf. B = <<131,112,0,0,0,84,3,109,96,69,208,5,175,207,75,36,93,112,218,232,222,22,251,0, - 0,0,0,0,0,0,1,100,0,11,102,111,111,98,97,114,98,108,117,114,102,97,0,98,5, - 244,197,144,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116, - 0,0,0,46,0,0,0,0,0,104,3,97,1,97,2,97,3>>, - ?line Fun = binary_to_term(B), - ?line Term = collect_fun(1024, Fun), - ?line Term = binary_to_term(term_to_binary(Term)), - ?line 1024 = length(Term), - ?line [[{foo,bar},{99,1.0},[e,f,g]]] = lists:usort(Term), - ?line {env,[{1,2,3}]} = erlang:fun_info(Fun, env), + 0,0,0,0,0,0,1,100,0,11,102,111,111,98,97,114,98,108,117,114,102,97,0,98,5, + 244,197,144,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116, + 0,0,0,46,0,0,0,0,0,104,3,97,1,97,2,97,3>>, + Fun = binary_to_term(B), + Term = collect_fun(1024, Fun), + Term = binary_to_term(term_to_binary(Term)), + 1024 = length(Term), + [[{foo,bar},{99,1.0},[e,f,g]]] = lists:usort(Term), + {env,[{1,2,3}]} = erlang:fun_info(Fun, env), ok. collect_fun(0, _) -> @@ -155,13 +155,13 @@ undefined_lambda(foobarblurf, Fun, Args) when is_function(Fun) -> Args. debug_breakpoint(Config) when is_list(Config) -> - ?line process_flag(error_handler, ?MODULE), - ?line erts_debug:breakpoint({?MODULE,foobar,5}, true), - ?line Term = break_collect(1024), - ?line Term = binary_to_term(term_to_binary(Term)), - ?line 1024 = length(Term), - ?line [[a,b,c,{d,e},[f,g,h]]] = lists:usort(Term), - ?line erts_debug:breakpoint({?MODULE,foobar,5}, false), + process_flag(error_handler, ?MODULE), + erts_debug:breakpoint({?MODULE,foobar,5}, true), + Term = break_collect(1024), + Term = binary_to_term(term_to_binary(Term)), + 1024 = length(Term), + [[a,b,c,{d,e},[f,g,h]]] = lists:usort(Term), + erts_debug:breakpoint({?MODULE,foobar,5}, false), ok. break_collect(0) -> @@ -178,5 +178,3 @@ foobar(_, _, _, _, _) -> exit(dont_execute_me). id(I) -> I. - - diff --git a/erts/emulator/test/old_mod.erl b/erts/emulator/test/old_mod.erl index 03f1ffd67b..0da40ec430 100644 --- a/erts/emulator/test/old_mod.erl +++ b/erts/emulator/test/old_mod.erl @@ -30,19 +30,19 @@ sorter(Receiver, Ref, List) -> sort_on_old_node(List) when is_list(List) -> OldVersion = "r10", - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line {X, Y, Z} = now(), - ?line NodeName = list_to_atom(OldVersion - ++ "_" - ++ integer_to_list(X) - ++ integer_to_list(Y) - ++ integer_to_list(Z)), - ?line {ok, Node} = test_server:start_node(NodeName, - peer, - [{args, " -pa " ++ Pa}, - {erl, [{release, OldVersion++"b_patched"}]}]), - ?line Ref = make_ref(), - ?line spawn_link(Node, ?MODULE, sorter, [self(), Ref, List]), - ?line SortedPids = receive {Ref, SP} -> SP end, - ?line true = test_server:stop_node(Node), - ?line SortedPids. + Pa = filename:dirname(code:which(?MODULE)), + {X, Y, Z} = now(), + NodeName = list_to_atom(OldVersion + ++ "_" + ++ integer_to_list(X) + ++ integer_to_list(Y) + ++ integer_to_list(Z)), + {ok, Node} = test_server:start_node(NodeName, + peer, + [{args, " -pa " ++ Pa}, + {erl, [{release, OldVersion++"b_patched"}]}]), + Ref = make_ref(), + spawn_link(Node, ?MODULE, sorter, [self(), Ref, List]), + SortedPids = receive {Ref, SP} -> SP end, + true = test_server:stop_node(Node), + SortedPids. diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl index 99c143e2d0..9d40417d0a 100644 --- a/erts/emulator/test/old_scheduler_SUITE.erl +++ b/erts/emulator/test/old_scheduler_SUITE.erl @@ -23,7 +23,7 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, - init_per_testcase/2, end_per_testcase/2]). + init_per_testcase/2, end_per_testcase/2]). -export([equal/1, many_low/1, few_low/1, max/1, high/1]). suite() -> @@ -32,13 +32,13 @@ suite() -> all() -> case catch erlang:system_info(modified_timing_level) of - Level when is_integer(Level) -> - {skipped, - "Modified timing (level " ++ - integer_to_list(Level) ++ - ") is enabled. Testcases gets messed " - "up by modfied timing."}; - _ -> [equal, many_low, few_low, max, high] + Level when is_integer(Level) -> + {skipped, + "Modified timing (level " ++ + integer_to_list(Level) ++ + ") is enabled. Testcases gets messed " + "up by modfied timing."}; + _ -> [equal, many_low, few_low, max, high] end. @@ -63,8 +63,8 @@ all() -> init_per_testcase(_Case, Config) -> %% main test process needs max prio - ?line Prio = process_flag(priority, max), - ?line MS = erlang:system_flag(multi_scheduling, block), + Prio = process_flag(priority, max), + MS = erlang:system_flag(multi_scheduling, block), [{prio,Prio},{multi_scheduling, MS}|Config]. end_per_testcase(_Case, Config) -> @@ -75,17 +75,17 @@ end_per_testcase(_Case, Config) -> ok(Config) when is_list(Config) -> case proplists:get_value(multi_scheduling, Config) of - blocked -> - {comment, - "Multi-scheduling blocked during test. This testcase was not " - "written to work with multiple schedulers."}; - _ -> ok + blocked -> + {comment, + "Multi-scheduling blocked during test. This testcase was not " + "written to work with multiple schedulers."}; + _ -> ok end. %% Run equal number of low and normal prio processes. equal(Config) when is_list(Config) -> - ?line Self = self(), + Self = self(), %% specify number of test processes to run Normal = {normal,500}, @@ -95,87 +95,87 @@ equal(Config) when is_list(Config) -> Time = 30, %% start controllers - ?line Receiver = - spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end), - ?line Starter = - spawn(fun() -> starter(Normal, Low, Receiver) end), + Receiver = + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end), + Starter = + spawn(fun() -> starter(Normal, Low, Receiver) end), %% receive test data from Receiver - ?line {NRs,NAvg,LRs,LAvg,Ratio} = - receive - {Receiver,Res} -> Res - end, + {NRs,NAvg,LRs,LAvg,Ratio} = + receive + {Receiver,Res} -> Res + end, %% stop controllers and test processes - ?line exit(Starter, kill), - ?line exit(Receiver, kill), + exit(Starter, kill), + exit(Receiver, kill), io:format("Reports: ~w normal (~w/proc), ~w low (~w/proc). Ratio: ~w~n", - [NRs,NAvg,LRs,LAvg,Ratio]), + [NRs,NAvg,LRs,LAvg,Ratio]), %% runtime ratio between normal and low should be ~8 if Ratio < 7.5 ; Ratio > 8.5 -> - ct:fail({bad_ratio,Ratio}); + ct:fail({bad_ratio,Ratio}); true -> - ok(Config) + ok(Config) end. %% Run many low and few normal prio processes. many_low(Config) when is_list(Config) -> - ?line Self = self(), + Self = self(), Normal = {normal,1}, Low = {low,1000}, %% specify time of test (in seconds) Time = 30, - ?line Receiver = - spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end), - ?line Starter = - spawn(fun() -> starter(Normal, Low, Receiver) end), - ?line {NRs,NAvg,LRs,LAvg,Ratio} = - receive - {Receiver,Res} -> Res - end, - ?line exit(Starter, kill), - ?line exit(Receiver, kill), + Receiver = + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end), + Starter = + spawn(fun() -> starter(Normal, Low, Receiver) end), + {NRs,NAvg,LRs,LAvg,Ratio} = + receive + {Receiver,Res} -> Res + end, + exit(Starter, kill), + exit(Receiver, kill), io:format("Reports: ~w normal (~w/proc), ~w low (~w/proc). Ratio: ~w~n", - [NRs,NAvg,LRs,LAvg,Ratio]), + [NRs,NAvg,LRs,LAvg,Ratio]), if Ratio < 7.5 ; Ratio > 8.5 -> - ct:fail({bad_ratio,Ratio}); + ct:fail({bad_ratio,Ratio}); true -> - ok(Config) + ok(Config) end. %% Run few low and many normal prio processes. few_low(Config) when is_list(Config) -> - ?line Self = self(), + Self = self(), Normal = {normal,1000}, Low = {low,1}, %% specify time of test (in seconds) Time = 30, - ?line Receiver = - spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end), - ?line Starter = - spawn(fun() -> starter(Normal, Low, Receiver) end), - ?line {NRs,NAvg,LRs,LAvg,Ratio} = - receive - {Receiver,Res} -> Res - end, - ?line exit(Starter, kill), - ?line exit(Receiver, kill), + Receiver = + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end), + Starter = + spawn(fun() -> starter(Normal, Low, Receiver) end), + {NRs,NAvg,LRs,LAvg,Ratio} = + receive + {Receiver,Res} -> Res + end, + exit(Starter, kill), + exit(Receiver, kill), io:format("Reports: ~w normal (~w/proc), ~w low (~w/proc). Ratio: ~w~n", - [NRs,NAvg,LRs,LAvg,Ratio]), + [NRs,NAvg,LRs,LAvg,Ratio]), if Ratio < 7.0 ; Ratio > 8.5 -> - ct:fail({bad_ratio,Ratio}); + ct:fail({bad_ratio,Ratio}); true -> - ok(Config) + ok(Config) end. @@ -184,7 +184,7 @@ few_low(Config) when is_list(Config) -> max(Config) when is_list(Config) -> max = process_flag(priority, max), % should already be max (init_per_tc) - ?line Self = self(), + Self = self(), Max = {max,2}, High = {high,2}, Normal = {normal,100}, @@ -193,58 +193,58 @@ max(Config) when is_list(Config) -> %% specify time of test (in seconds) Time = 30, - ?line Receiver1 = - spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, High) end), - ?line Starter1 = - spawn(fun() -> starter(Max, High, Receiver1) end), - ?line {M1Rs,M1Avg,HRs,HAvg,Ratio1} = - receive - {Receiver1,Res1} -> Res1 - end, - ?line exit(Starter1, kill), - ?line exit(Receiver1, kill), + Receiver1 = + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, High) end), + Starter1 = + spawn(fun() -> starter(Max, High, Receiver1) end), + {M1Rs,M1Avg,HRs,HAvg,Ratio1} = + receive + {Receiver1,Res1} -> Res1 + end, + exit(Starter1, kill), + exit(Receiver1, kill), io:format("Reports: ~w max (~w/proc), ~w high (~w/proc). Ratio: ~w~n", - [M1Rs,M1Avg,HRs,HAvg,Ratio1]), + [M1Rs,M1Avg,HRs,HAvg,Ratio1]), if Ratio1 < 1.0 -> - ct:fail({bad_ratio,Ratio1}); + ct:fail({bad_ratio,Ratio1}); true -> - ok(Config) + ok(Config) end, - ?line Receiver2 = - spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Normal) end), - ?line Starter2 = - spawn(fun() -> starter(Max, Normal, Receiver2) end), - ?line {M2Rs,M2Avg,NRs,NAvg,Ratio2} = - receive - {Receiver2,Res2} -> Res2 - end, - ?line exit(Starter2, kill), - ?line exit(Receiver2, kill), + Receiver2 = + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Normal) end), + Starter2 = + spawn(fun() -> starter(Max, Normal, Receiver2) end), + {M2Rs,M2Avg,NRs,NAvg,Ratio2} = + receive + {Receiver2,Res2} -> Res2 + end, + exit(Starter2, kill), + exit(Receiver2, kill), io:format("Reports: ~w max (~w/proc), ~w normal (~w/proc). Ratio: ~w~n", - [M2Rs,M2Avg,NRs,NAvg,Ratio2]), + [M2Rs,M2Avg,NRs,NAvg,Ratio2]), if Ratio2 < 1.0 -> - ct:fail({bad_ratio,Ratio2}); + ct:fail({bad_ratio,Ratio2}); true -> - ok + ok end, - ?line Receiver3 = - spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Low) end), - ?line Starter3 = - spawn(fun() -> starter(Max, Low, Receiver3) end), - ?line {M3Rs,M3Avg,LRs,LAvg,Ratio3} = - receive - {Receiver3,Res3} -> Res3 - end, - ?line exit(Starter3, kill), - ?line exit(Receiver3, kill), + Receiver3 = + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Low) end), + Starter3 = + spawn(fun() -> starter(Max, Low, Receiver3) end), + {M3Rs,M3Avg,LRs,LAvg,Ratio3} = + receive + {Receiver3,Res3} -> Res3 + end, + exit(Starter3, kill), + exit(Receiver3, kill), io:format("Reports: ~w max (~w/proc), ~w low (~w/proc). Ratio: ~w~n", - [M3Rs,M3Avg,LRs,LAvg,Ratio3]), + [M3Rs,M3Avg,LRs,LAvg,Ratio3]), if Ratio3 < 1.0 -> - ct:fail({bad_ratio,Ratio3}); + ct:fail({bad_ratio,Ratio3}); true -> - ok(Config) + ok(Config) end. @@ -253,7 +253,7 @@ max(Config) when is_list(Config) -> high(Config) when is_list(Config) -> max = process_flag(priority, max), % should already be max (init_per_tc) - ?line Self = self(), + Self = self(), High = {high,2}, Normal = {normal,100}, Low = {low,100}, @@ -261,40 +261,40 @@ high(Config) when is_list(Config) -> %% specify time of test (in seconds) Time = 30, - ?line Receiver1 = - spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Normal) end), - ?line Starter1 = - spawn(fun() -> starter(High, Normal, Receiver1) end), - ?line {H1Rs,H1Avg,NRs,NAvg,Ratio1} = - receive - {Receiver1,Res1} -> Res1 - end, - ?line exit(Starter1, kill), - ?line exit(Receiver1, kill), + Receiver1 = + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Normal) end), + Starter1 = + spawn(fun() -> starter(High, Normal, Receiver1) end), + {H1Rs,H1Avg,NRs,NAvg,Ratio1} = + receive + {Receiver1,Res1} -> Res1 + end, + exit(Starter1, kill), + exit(Receiver1, kill), io:format("Reports: ~w high (~w/proc), ~w normal (~w/proc). Ratio: ~w~n", - [H1Rs,H1Avg,NRs,NAvg,Ratio1]), + [H1Rs,H1Avg,NRs,NAvg,Ratio1]), if Ratio1 < 1.0 -> - ct:fail({bad_ratio,Ratio1}); + ct:fail({bad_ratio,Ratio1}); true -> - ok + ok end, - ?line Receiver2 = - spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Low) end), - ?line Starter2 = - spawn(fun() -> starter(High, Low, Receiver2) end), - ?line {H2Rs,H2Avg,LRs,LAvg,Ratio2} = - receive - {Receiver2,Res2} -> Res2 - end, - ?line exit(Starter2, kill), - ?line exit(Receiver2, kill), + Receiver2 = + spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Low) end), + Starter2 = + spawn(fun() -> starter(High, Low, Receiver2) end), + {H2Rs,H2Avg,LRs,LAvg,Ratio2} = + receive + {Receiver2,Res2} -> Res2 + end, + exit(Starter2, kill), + exit(Receiver2, kill), io:format("Reports: ~w high (~w/proc), ~w low (~w/proc). Ratio: ~w~n", - [H2Rs,H2Avg,LRs,LAvg,Ratio2]), + [H2Rs,H2Avg,LRs,LAvg,Ratio2]), if Ratio2 < 1.0 -> - ct:fail({bad_ratio,Ratio2}); + ct:fail({bad_ratio,Ratio2}); true -> - ok(Config) + ok(Config) end. @@ -309,38 +309,38 @@ receiver(T0, TimeSec, Main, {P1,P1N}, {P2,P2N}) -> %% uncomment lines below to get life sign (debug) receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 0) -> -% T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, milli_seconds), -% erlang:display({round(T/1000),P1Rs,P2Rs}), + % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, milli_seconds), + % erlang:display({round(T/1000),P1Rs,P2Rs}), receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 100000); receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, C) -> Remain = Time - erlang:convert_time_unit(erlang:monotonic_time() - T0, - native, milli_seconds), % test time remaining + native, milli_seconds), % test time remaining Remain1 = if Remain < 0 -> - 0; - true -> - Remain - end, + 0; + true -> + Remain + end, {P1Rs1,P2Rs1} = - receive - {_Pid,P1} -> % report from a P1 process - {P1Rs+1,P2Rs}; - {_Pid,P2} -> % report from a P2 process - {P1Rs,P2Rs+1} - after Remain1 -> - {P1Rs,P2Rs} - end, + receive + {_Pid,P1} -> % report from a P1 process + {P1Rs+1,P2Rs}; + {_Pid,P2} -> % report from a P2 process + {P1Rs,P2Rs+1} + after Remain1 -> + {P1Rs,P2Rs} + end, if Remain > 0 -> % keep going - receiver(T0, Time, Main, P1,P1N,P1Rs1, P2,P2N,P2Rs1, C-1); + receiver(T0, Time, Main, P1,P1N,P1Rs1, P2,P2N,P2Rs1, C-1); true -> % finish - %% calculate results and send to main test process - P1Avg = P1Rs1/P1N, - P2Avg = P2Rs1/P2N, - Ratio = if P2Avg < 1.0 -> P1Avg; - true -> P1Avg/P2Avg - end, - Main ! {self(),{P1Rs1,round(P1Avg),P2Rs1,round(P2Avg),Ratio}}, - flush_loop() + %% calculate results and send to main test process + P1Avg = P1Rs1/P1N, + P2Avg = P2Rs1/P2N, + Ratio = if P2Avg < 1.0 -> P1Avg; + true -> P1Avg/P2Avg + end, + Main ! {self(),{P1Rs1,round(P1Avg),P2Rs1,round(P2Avg),Ratio}}, + flush_loop() end. starter({P1,P1N}, {P2,P2N}, Receiver) -> @@ -366,8 +366,8 @@ p_loop(100, Prio, Receiver) -> receive after 0 -> ok end, %% if Receiver gone, we're done case is_process_alive(Receiver) of - false -> exit(bye); - true -> ok + false -> exit(bye); + true -> ok end, %% send report Receiver ! {self(),Prio}, @@ -375,10 +375,10 @@ p_loop(100, Prio, Receiver) -> p_loop(N, Prio, Receiver) -> p_loop(N+1, Prio, Receiver). - + flush_loop() -> receive _ -> - ok + ok end, flush_loop(). diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl index e9127616a6..562cf1c92d 100644 --- a/erts/emulator/test/op_SUITE.erl +++ b/erts/emulator/test/op_SUITE.erl @@ -23,7 +23,7 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, - bsl_bsr/1,logical/1,t_not/1,relop_simple/1,relop/1,complex_relop/1]). + bsl_bsr/1,logical/1,t_not/1,relop_simple/1,relop/1,complex_relop/1]). -export([]). -import(lists, [foldl/3,flatmap/2]). @@ -40,7 +40,7 @@ all() -> bsl_bsr(Config) when is_list(Config) -> Vs = [unvalue(V) || V <- [-16#8000009-2,-1,0,1,2,73,16#8000000,bad,[]]], Cases = [{Op,X,Y} || Op <- ['bsr','bsl'], X <- Vs, Y <- Vs], - ?line run_test_module(Cases, false), + run_test_module(Cases, false), {comment,integer_to_list(length(Cases)) ++ " cases"}. %% Test the logical operators and internal BIFs. @@ -48,13 +48,13 @@ logical(Config) when is_list(Config) -> Vs0 = [true,false,bad], Vs = [unvalue(V) || V <- Vs0], Cases = [{Op,X,Y} || Op <- ['and','or','xor'], X <- Vs, Y <- Vs], - ?line run_test_module(Cases, false), + run_test_module(Cases, false), {comment,integer_to_list(length(Cases)) ++ " cases"}. %% Test the not operator and internal BIFs. t_not(Config) when is_list(Config) -> - ?line Cases = [{'not',unvalue(V)} || V <- [true,false,42,bad]], - ?line run_test_module(Cases, false), + Cases = [{'not',unvalue(V)} || V <- [true,false,42,bad]], + run_test_module(Cases, false), {comment,integer_to_list(length(Cases)) ++ " cases"}. %% Test that simlpe relations between relation operators hold. @@ -66,52 +66,52 @@ relop_simple(Config) when is_list(Config) -> T1 = erlang:make_tuple(3,87), T2 = erlang:make_tuple(3,87), Terms = [-F2,Big2,-F1,-Big1,-33,-33.0,0,0.0,42,42.0,Big1,F1,Big2,F2,a,b, - {T1,a},{T2,b},[T1,Big1],[T2,Big2]], - - ?line Combos = [{V1,V2} || V1 <- Terms, V2 <- Terms], - + {T1,a},{T2,b},[T1,Big1],[T2,Big2]], + + Combos = [{V1,V2} || V1 <- Terms, V2 <- Terms], + lists:foreach(fun({A,B}) -> relop_simple_do(A,B) end, - Combos), + Combos), repeat(fun() -> - Size = rand:uniform(100), - Rnd1 = make_rand_term(Size), - {Rnd2,0} = clone_and_mutate(Rnd1, rand:uniform(Size)), - relop_simple_do(Rnd1,Rnd2) - end, - 1000), + Size = rand:uniform(100), + Rnd1 = make_rand_term(Size), + {Rnd2,0} = clone_and_mutate(Rnd1, rand:uniform(Size)), + relop_simple_do(Rnd1,Rnd2) + end, + 1000), ok. relop_simple_do(V1,V2) -> %%io:format("compare ~p\n and ~p\n",[V1,V2]), L = V1 < V2, - ?line L = not (V1 >= V2), - ?line L = V2 > V1, - ?line L = not (V2 =< V1), + L = not (V1 >= V2), + L = V2 > V1, + L = not (V2 =< V1), G = V1 > V2, - ?line G = not (V1 =< V2), - ?line G = V2 < V1, - ?line G = not (V2 >= V1), - + G = not (V1 =< V2), + G = V2 < V1, + G = not (V2 >= V1), + ID = V1 =:= V2, - ?line ID = V2 =:= V1, - ?line ID = not (V1 =/= V2), - ?line ID = not (V2 =/= V1), - + ID = V2 =:= V1, + ID = not (V1 =/= V2), + ID = not (V2 =/= V1), + EQ = V1 == V2, - ?line EQ = V2 == V1, - ?line EQ = not (V1 /= V2), - ?line EQ = not (V2 /= V1), - - ?line case {L, EQ, ID, G, cmp_emu(V1,V2)} of - { true, false, false, false, -1} -> ok; - {false, true, false, false, 0} -> ok; - {false, true, true, false, 0} -> ok; - {false, false, false, true, +1} -> ok - end. - + EQ = V2 == V1, + EQ = not (V1 /= V2), + EQ = not (V2 /= V1), + + case {L, EQ, ID, G, cmp_emu(V1,V2)} of + { true, false, false, false, -1} -> ok; + {false, true, false, false, 0} -> ok; + {false, true, true, false, 0} -> ok; + {false, false, false, true, +1} -> ok + end. + %% Emulate internal "cmp" cmp_emu(A,B) when is_tuple(A), is_tuple(B) -> SA = size(A), @@ -122,8 +122,8 @@ cmp_emu(A,B) when is_tuple(A), is_tuple(B) -> end; cmp_emu([A|TA],[B|TB]) -> case cmp_emu(A,B) of - 0 -> cmp_emu(TA,TB); - CMP -> CMP + 0 -> cmp_emu(TA,TB); + CMP -> CMP end; cmp_emu(A,B) -> %% We cheat and use real "cmp" for the primitive types. @@ -131,35 +131,35 @@ cmp_emu(A,B) -> A > B -> +1; true -> 0 end. - + make_rand_term(1) -> make_rand_term_single(); make_rand_term(Arity) -> case rand:uniform(3) of - 1 -> - make_rand_list(Arity); - 2 -> - list_to_tuple(make_rand_list(Arity)); - 3 -> - {Car,Rest} = make_rand_term_rand_size(Arity), - [Car|make_rand_term(Rest)] + 1 -> + make_rand_list(Arity); + 2 -> + list_to_tuple(make_rand_list(Arity)); + 3 -> + {Car,Rest} = make_rand_term_rand_size(Arity), + [Car|make_rand_term(Rest)] end. make_rand_term_single() -> Range = 1 bsl rand:uniform(200), case rand:uniform(12) of - 1 -> random; - 2 -> uniform; - 3 -> rand:uniform(Range) - (Range div 2); - 4 -> Range * (rand:uniform() - 0.5); - 5 -> 0; - 6 -> 0.0; - 7 -> make_ref(); - 8 -> self(); - 9 -> term_to_binary(rand:uniform(Range)); - 10 -> fun(X) -> X*Range end; - 11 -> fun(X) -> X/Range end; - 12 -> [] + 1 -> random; + 2 -> uniform; + 3 -> rand:uniform(Range) - (Range div 2); + 4 -> Range * (rand:uniform() - 0.5); + 5 -> 0; + 6 -> 0.0; + 7 -> make_ref(); + 8 -> self(); + 9 -> term_to_binary(rand:uniform(Range)); + 10 -> fun(X) -> X*Range end; + 11 -> fun(X) -> X/Range end; + 12 -> [] end. make_rand_term_rand_size(1) -> @@ -172,7 +172,7 @@ make_rand_list(0) -> []; make_rand_list(Arity) -> {Term, Rest} = make_rand_term_rand_size(Arity), [Term | make_rand_list(Rest)]. - + clone_and_mutate(Term, 0) -> {clone(Term), 0}; @@ -195,17 +195,17 @@ clone(Term) -> my_list_to_tuple(List) -> try list_to_tuple(List) catch - error:badarg -> - %%io:format("my_list_to_tuple got badarg exception.\n"), - list_to_tuple(purify_list(List)) + error:badarg -> + %%io:format("my_list_to_tuple got badarg exception.\n"), + list_to_tuple(purify_list(List)) end. - + purify_list(List) -> lists:reverse(purify_list(List, [])). purify_list([], Acc) -> Acc; purify_list([H|T], Acc) -> purify_list(T, [H|Acc]); purify_list(Other, Acc) -> [Other|Acc]. - + %% Test the relational operators and internal BIFs on literals. relop(Config) when is_list(Config) -> @@ -214,9 +214,9 @@ relop(Config) when is_list(Config) -> F1 = float(Big1), F2 = float(Big2), Vs0 = [a,b,-33,-33.0,0,0.0,42,42.0,Big1,Big2,F1,F2], - ?line Vs = [unvalue(V) || V <- Vs0], + Vs = [unvalue(V) || V <- Vs0], Ops = ['==', '/=', '=:=', '=/=', '<', '=<', '>', '>='], - ?line binop(Ops, Vs). + binop(Ops, Vs). %% Test the relational operators and internal BIFs on lists and tuples. complex_relop(Config) when is_list(Config) -> @@ -225,51 +225,51 @@ complex_relop(Config) when is_list(Config) -> Vs0 = [an_atom,42.0,42,Big,Float], Vs = flatmap(fun(X) -> [unvalue({X}),unvalue([X])] end, Vs0), Ops = ['==', '/=', '=:=', '=/=', '<', '=<', '>', '>='], - ?line binop(Ops, Vs). + binop(Ops, Vs). binop(Ops, Vs) -> - Run = fun(Op, N) -> ?line Cases = [{Op,V1,V2} || V1 <- Vs, V2 <- Vs], - ?line run_test_module(Cases, true), - N + length(Cases) end, - ?line NumCases = foldl(Run, 0, Ops), + Run = fun(Op, N) -> Cases = [{Op,V1,V2} || V1 <- Vs, V2 <- Vs], + run_test_module(Cases, true), + N + length(Cases) end, + NumCases = foldl(Run, 0, Ops), {comment,integer_to_list(NumCases) ++ " cases"}. - + run_test_module(Cases, GuardsOk) -> - ?line Es = [expr(C) || C <- Cases], - ?line Ok = unvalue(ok), - ?line Gts = case GuardsOk of - true -> - Ges = [guard_expr(C) || C <- Cases], - ?line lists:foldr(fun guard_test/2, [Ok], Ges); - false -> - [Ok] - end, - ?line Fun1 = make_function(guard_tests, Gts), - ?line Bts = lists:foldr(fun body_test/2, [Ok], Es), - ?line Fun2 = make_function(body_tests, Bts), - ?line Bbts = lists:foldr(fun internal_bif/2, [Ok], Es), - ?line Fun3 = make_function(bif_tests, Bbts), - ?line Id = {function,1,id,1,[{clause,1,[{var,1,'I'}],[],[{var,1,'I'}]}]}, + Es = [expr(C) || C <- Cases], + Ok = unvalue(ok), + Gts = case GuardsOk of + true -> + Ges = [guard_expr(C) || C <- Cases], + lists:foldr(fun guard_test/2, [Ok], Ges); + false -> + [Ok] + end, + Fun1 = make_function(guard_tests, Gts), + Bts = lists:foldr(fun body_test/2, [Ok], Es), + Fun2 = make_function(body_tests, Bts), + Bbts = lists:foldr(fun internal_bif/2, [Ok], Es), + Fun3 = make_function(bif_tests, Bbts), + Id = {function,1,id,1,[{clause,1,[{var,1,'I'}],[],[{var,1,'I'}]}]}, Module0 = make_module(op_tests, [Fun1,Fun2,Fun3,Id]), Module = erl_parse:new_anno(Module0), - ?line lists:foreach(fun(F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Module), + lists:foreach(fun(F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Module), %% Compile, load, and run the generated module. Native = case test_server:is_native(?MODULE) of - true -> [native]; - false -> [] - end, - ?line {ok,Mod,Code1} = compile:forms(Module, [time|Native]), - ?line code:delete(Mod), - ?line code:purge(Mod), - ?line {module,Mod} = code:load_binary(Mod, Mod, Code1), - ?line run_function(Mod, guard_tests), - ?line run_function(Mod, body_tests), - ?line run_function(Mod, bif_tests), - - ?line true = code:delete(Mod), - ?line code:purge(Mod), + true -> [native]; + false -> [] + end, + {ok,Mod,Code1} = compile:forms(Module, [time|Native]), + code:delete(Mod), + code:purge(Mod), + {module,Mod} = code:load_binary(Mod, Mod, Code1), + run_function(Mod, guard_tests), + run_function(Mod, body_tests), + run_function(Mod, bif_tests), + + true = code:delete(Mod), + code:purge(Mod), ok. @@ -293,19 +293,19 @@ guard_expr({Op,X,Y}) -> run_function(Mod, Name) -> case catch Mod:Name() of - {'EXIT',Reason} -> - io:format("~p", [get(last)]), - ct:fail({'EXIT',Reason}); - _Other -> - ok + {'EXIT',Reason} -> + io:format("~p", [get(last)]), + ct:fail({'EXIT',Reason}); + _Other -> + ok end. - + guard_test({E,Expr,Res}, Tail) -> True = unvalue(true), [save_term(Expr), {match,1,unvalue(Res), {'if',1,[{clause,1,[],[[E]],[True]}, - {clause,1,[],[[True]],[unvalue(false)]}]}}|Tail]. + {clause,1,[],[[True]],[unvalue(false)]}]}}|Tail]. body_test({E,Expr,{'EXIT',_}}, Tail) -> [save_term(Expr), @@ -331,8 +331,8 @@ internal_bif(Op, Args, Expr, Res, Tail) -> save_term(Term) -> {call,1, - {atom,1,put}, - [{atom,1,last},unvalue(Term)]}. + {atom,1,put}, + [{atom,1,last},unvalue(Term)]}. make_module(Name, Funcs) -> [{attribute,1,module,Name}, @@ -342,18 +342,18 @@ make_module(Name, Funcs) -> make_function(Name, Body) -> {function,1,Name,0,[{clause,1,[],[],Body}]}. - + eval(E0) -> E = erl_parse:new_anno(E0), - ?line case catch erl_eval:exprs(E, []) of - {'EXIT',Reason} -> {'EXIT',Reason}; - {value,Val,_Bs} -> Val - end. + case catch erl_eval:exprs(E, []) of + {'EXIT',Reason} -> {'EXIT',Reason}; + {value,Val,_Bs} -> Val + end. unvalue(V) -> Abstr = erl_parse:abstract(V), erl_parse:anno_to_term(Abstr). - + value({nil,_}) -> []; value({integer,_,X}) -> X; value({string,_,X}) -> X; diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 7d881a483f..c3ea0fb5c9 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -75,25 +75,25 @@ -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2, - init_per_suite/1, end_per_suite/1, - stream_small/1, stream_big/1, - basic_ping/1, slow_writes/1, bad_packet/1, bad_port_messages/1, - mul_basic/1, mul_slow_writes/1, - dying_port/1, port_program_with_path/1, - open_input_file_port/1, open_output_file_port/1, + init_per_testcase/2, end_per_testcase/2, + init_per_suite/1, end_per_suite/1, + stream_small/1, stream_big/1, + basic_ping/1, slow_writes/1, bad_packet/1, bad_port_messages/1, + mul_basic/1, mul_slow_writes/1, + dying_port/1, port_program_with_path/1, + open_input_file_port/1, open_output_file_port/1, count_fds/1, - iter_max_ports/1, eof/1, input_only/1, output_only/1, - name1/1, - t_binary/1, parallell/1, t_exit/1, - env/1, huge_env/1, bad_env/1, cd/1, exit_status/1, - tps_16_bytes/1, tps_1K/1, line/1, stderr_to_stdout/1, - otp_3906/1, otp_4389/1, win_massive/1, win_massive_client/1, - mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1, - exit_status_multi_scheduling_block/1, ports/1, - spawn_driver/1, spawn_executable/1, close_deaf_port/1, - port_setget_data/1, - unregister_name/1, parallelism_option/1]). + iter_max_ports/1, eof/1, input_only/1, output_only/1, + name1/1, + t_binary/1, parallell/1, t_exit/1, + env/1, huge_env/1, bad_env/1, cd/1, exit_status/1, + tps_16_bytes/1, tps_1K/1, line/1, stderr_to_stdout/1, + otp_3906/1, otp_4389/1, win_massive/1, win_massive_client/1, + mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1, + exit_status_multi_scheduling_block/1, ports/1, + spawn_driver/1, spawn_executable/1, close_deaf_port/1, + port_setget_data/1, + unregister_name/1, parallelism_option/1]). -export([do_iter_max_ports/2]). @@ -154,10 +154,10 @@ end_per_suite(Config) when is_list(Config) -> %% on a Windows machine given the correct environment. win_massive(Config) when is_list(Config) -> case os:type() of - {win32,_} -> - do_win_massive(); - _ -> - {skip,"Only on Windows."} + {win32,_} -> + do_win_massive(); + _ -> + {skip,"Only on Windows."} end. do_win_massive() -> @@ -165,44 +165,44 @@ do_win_massive() -> SuiteDir = filename:dirname(code:which(?MODULE)), Ports = " +Q 8192", {ok, Node} = - test_server:start_node(win_massive, - slave, - [{args, " -pa " ++ SuiteDir ++ Ports}]), + test_server:start_node(win_massive, + slave, + [{args, " -pa " ++ SuiteDir ++ Ports}]), ok = rpc:call(Node,?MODULE,win_massive_client,[3000]), test_server:stop_node(Node), ok. - + win_massive_client(N) -> {ok,P}=gen_tcp:listen(?WIN_MASSIVE_PORT,[{reuseaddr,true}]), L = win_massive_loop(P,N), Len = length(L), lists:foreach(fun(E) -> - gen_tcp:close(E) - end, - L), + gen_tcp:close(E) + end, + L), case Len div 2 of - N -> - ok; - _Else -> - {too_few, Len} + N -> + ok; + _Else -> + {too_few, Len} end. win_massive_loop(_,0) -> []; win_massive_loop(P,N) -> case (catch gen_tcp:connect("localhost",?WIN_MASSIVE_PORT,[])) of - {ok,A} -> - case (catch gen_tcp:accept(P)) of - {ok,B} -> - %erlang:display(N), - [A,B|win_massive_loop(P,N-1)]; - _Else -> - [A] - end; - _Else0 -> - [] + {ok,A} -> + case (catch gen_tcp:accept(P)) of + {ok,B} -> + %erlang:display(N), + [A,B|win_massive_loop(P,N-1)]; + _Else -> + [A] + end; + _Else0 -> + [] end. - + %% Test that we can send a stream of bytes and get it back. %% We will send only a small amount of data, to avoid deadlock. @@ -259,8 +259,8 @@ bad_packet(PortTest, HeaderSize, PacketSize) -> P = open_port({spawn, PortTest}, [{packet, HeaderSize}]), P ! {self(), {command, make_zero_packet(PacketSize)}}, receive - {'EXIT', P, einval} -> ok; - Other -> ct:fail({unexpected_message, Other}) + {'EXIT', P, einval} -> ok; + Other -> ct:fail({unexpected_message, Other}) end. make_zero_packet(0) -> []; @@ -286,8 +286,8 @@ bad_message(PortTest, Message) -> P = open_port({spawn,PortTest}, []), P ! Message, receive - {'EXIT',P,badsig} -> ok; - Other -> ct:fail({unexpected_message, Other}) + {'EXIT',P,badsig} -> ok; + Other -> ct:fail({unexpected_message, Other}) end. %% Tests various options (stream and {packet, Number} are implicitly @@ -419,21 +419,21 @@ mul_slow_writes(Config) when is_list(Config) -> parallell(Config) when is_list(Config) -> ct:timetrap({minutes, 5}), Testers = [ - fun() -> stream_ping(Config, 1007, "-s100", []) end, - fun() -> stream_ping(Config, 10007, "-s1000", []) end, - fun() -> stream_ping(Config, 10007, "-s1000", []) end, + fun() -> stream_ping(Config, 1007, "-s100", []) end, + fun() -> stream_ping(Config, 10007, "-s1000", []) end, + fun() -> stream_ping(Config, 10007, "-s1000", []) end, - fun() -> expect_input(Config, [21, 22, 23, 24, 25], 1, - "-s10", [in]) end, + fun() -> expect_input(Config, [21, 22, 23, 24, 25], 1, + "-s10", [in]) end, - fun() -> ping(Config, [10], 1, "-d", []) end, - fun() -> ping(Config, [20000], 2, "-d", []) end, - fun() -> ping(Config, [101], 1, "-s10", []) end, - fun() -> ping(Config, [1001], 2, "-s100", []) end, - fun() -> ping(Config, [10001], 4, "-s1000", []) end, + fun() -> ping(Config, [10], 1, "-d", []) end, + fun() -> ping(Config, [20000], 2, "-d", []) end, + fun() -> ping(Config, [101], 1, "-s10", []) end, + fun() -> ping(Config, [1001], 2, "-s100", []) end, + fun() -> ping(Config, [10001], 4, "-s1000", []) end, - fun() -> ping(Config, [501, 501], 2, "-s100", []) end, - fun() -> ping(Config, [11, 12, 13, 14, 11], 1, "-s5", []) end], + fun() -> ping(Config, [501, 501], 2, "-s100", []) end, + fun() -> ping(Config, [11, 12, 13, 14, 11], 1, "-s5", []) end], process_flag(trap_exit, true), Pids = lists:map(fun fun_spawn/1, Testers), wait_for(Pids), @@ -444,10 +444,10 @@ wait_for([]) -> wait_for(Pids) -> io:format("Waiting for ~p", [Pids]), receive - {'EXIT', Pid, normal} -> - wait_for(lists:delete(Pid, Pids)); - Other -> - ct:fail({bad_exit, Other}) + {'EXIT', Pid, normal} -> + wait_for(lists:delete(Pid, Pids)); + Other -> + ct:fail({bad_exit, Other}) end. %% Tests starting port programs that terminate by themselves. @@ -479,8 +479,8 @@ dying_port(Config) when is_list(Config) -> wait_for_port_exit(Port) -> receive - {'EXIT', Port, _} -> - ok + {'EXIT', Port, _} -> + ok end. make_dying_port(Config) when is_list(Config) -> @@ -503,17 +503,17 @@ port_program_with_path(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), DataDir = proplists:get_value(data_dir, Config), PrivDir = proplists:get_value(priv_dir, Config), - + %% Create a copy of the port test program in a directory not %% included in PATH (i.e. in priv_dir), with the name 'my_port_test.exe'. %% Also, place a file named 'my_port_test' in the same directory. %% This used to confuse the CreateProcess() call in spawn driver. %% (On Unix, there will be a single file created, which will be %% a copy of the port program.) - + PortTest = os:find_executable("port_test", DataDir), io:format("os:find_executable(~p, ~p) returned ~p", - ["port_test", DataDir, PortTest]), + ["port_test", DataDir, PortTest]), {ok, PortTestPgm} = file:read_file(PortTest), NewName = filename:join(PrivDir, filename:basename(PortTest)), RedHerring = filename:rootname(NewName), @@ -521,12 +521,12 @@ port_program_with_path(Config) when is_list(Config) -> ok = file:write_file(NewName, PortTestPgm), ok = file:write_file_info(NewName, #file_info{mode=8#111}), PgmWithPathAndNoExt = filename:rootname(NewName), - + %% Open the port using the path to the copied port test program, %% but without the .exe extension, and verified that it was started. %% %% If the bug is present the open_port call will fail with badarg. - + Command = lists:concat([PgmWithPathAndNoExt, " -h2"]), P = open_port({spawn, Command}, [{packet, 2}]), Message = "echo back to me", @@ -542,7 +542,7 @@ port_program_with_path(Config) when is_list(Config) -> %% This used to fail on Windows. open_input_file_port(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), - + %% Create a file with the file driver and read it back using %% open_port/2. @@ -550,11 +550,11 @@ open_input_file_port(Config) when is_list(Config) -> FileData1 = "An input file", ok = file:write_file(MyFile1, FileData1), case open_port(MyFile1, [in]) of - InputPort when is_port(InputPort) -> - receive - {InputPort, {data, FileData1}} -> - ok - end + InputPort when is_port(InputPort) -> + receive + {InputPort, {data, FileData1}} -> + ok + end end, ok. @@ -597,9 +597,9 @@ count_fds(Config) when is_list(Config) -> {ok, Written} = file:read_file(Filename), Written end, - <<4:32/native>> = RunTest([out, nouse_stdio]), - <<4:32/native>> = RunTest([in, nouse_stdio]), - <<5:32/native>> = RunTest([in, out, nouse_stdio]), + <<4:32/native>> = RunTest([out, nouse_stdio]), + <<4:32/native>> = RunTest([in, nouse_stdio]), + <<5:32/native>> = RunTest([in, out, nouse_stdio]), <<3:32/native>> = RunTest([out, use_stdio]), <<3:32/native>> = RunTest([in, use_stdio]), <<3:32/native>> = RunTest([in, out, use_stdio]), @@ -622,24 +622,24 @@ iter_max_ports(Config) when is_list(Config) -> %% Config2 = ignore_cores:setup(?MODULE, iter_max_ports, Config, true), try - iter_max_ports_test(Config2) + iter_max_ports_test(Config2) after - ignore_cores:restore(Config2) + ignore_cores:restore(Config2) end. - - + + iter_max_ports_test(Config) -> ct:timetrap({minutes, 30}), PortTest = port_test(Config), Command = lists:concat([PortTest, " -h0 -q"]), Iters = case os:type() of - {win32,_} -> 4; - _ -> 10 - end, + {win32,_} -> 4; + _ -> 10 + end, %% Run on a different node in order to limit the effect if this test fails. Dir = filename:dirname(code:which(?MODULE)), {ok,Node} = test_server:start_node(test_iter_max_socks,slave, - [{args,"+Q 2048 -pa " ++ Dir}]), + [{args,"+Q 2048 -pa " ++ Dir}]), L = rpc:call(Node,?MODULE,do_iter_max_ports,[Iters, Command]), test_server:stop_node(Node), @@ -669,8 +669,8 @@ max_ports(Command) -> close_ports([P|Ps]) -> P ! {self(), close}, receive - {P,closed} -> - ok + {P,closed} -> + ok end, close_ports(Ps); close_ports([]) -> @@ -688,23 +688,23 @@ open_ports(Name, Settings) -> test_server:sleep(5) end, case catch open_port(Name, Settings) of - P when is_port(P) -> - [P| open_ports(Name, Settings)]; - {'EXIT', {Code, _}} -> - case Code of - enfile -> - []; - emfile -> - []; - system_limit -> - []; - enomem -> - []; - Other -> - ct:fail({open_ports, Other}) - end; - Other -> - ct:fail({open_ports, Other}) + P when is_port(P) -> + [P| open_ports(Name, Settings)]; + {'EXIT', {Code, _}} -> + case Code of + enfile -> + []; + emfile -> + []; + system_limit -> + []; + enomem -> + []; + Other -> + ct:fail({open_ports, Other}) + end; + Other -> + ct:fail({open_ports, Other}) end. %% Tests that exit(Port, Term) works (has been known to crash the emulator). @@ -713,10 +713,10 @@ t_exit(Config) when is_list(Config) -> process_flag(trap_exit, true), Pid = fun_spawn(fun suicide_port/1, [Config]), receive - {'EXIT', Pid, die} -> - ok; - Other -> - ct:fail({bad_message, Other}) + {'EXIT', Pid, die} -> + ok; + Other -> + ct:fail({bad_message, Other}) end. suicide_port(Config) when is_list(Config) -> @@ -738,17 +738,17 @@ tps(Size, Config) -> Port = open_port({spawn, PortTest}, [binary, {packet, 2}]), Transactions = 10000, {Elapsed, ok} = test_server:timecall(?MODULE, tps, - [Port, Packet, Transactions]), + [Port, Packet, Transactions]), {comment, integer_to_list(trunc(Transactions/Elapsed+0.5)) ++ " transactions/s"}. tps(_Port, _Packet, 0) -> ok; tps(Port, Packet, N) -> port_command(Port, Packet), receive - {Port, {data, Packet}} -> - tps(Port, Packet, N-1); - Other -> - ct:fail({bad_message, Other}) + {Port, {data, Packet}} -> + tps(Port, Packet, N-1); + Other -> + ct:fail({bad_message, Other}) end. %% Line I/O test @@ -759,38 +759,38 @@ line(Config) when is_list(Config) -> Packet2 = random_packet(Siz div 2), %% Test that packets are split into lines port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2, - io_lib:nl()]), - [{eol, Packet1}, {eol, Packet2}]}], - 0, "", [{line,Siz}]), + io_lib:nl()]), + [{eol, Packet1}, {eol, Packet2}]}], + 0, "", [{line,Siz}]), %% Test the same for binaries port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2, - io_lib:nl()]), - [{eol, Packet1}, {eol, Packet2}]}], - 0, "", [{line,Siz},binary]), + io_lib:nl()]), + [{eol, Packet1}, {eol, Packet2}]}], + 0, "", [{line,Siz},binary]), %% Test that too long lines get split port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet1, - Packet2, io_lib:nl()]), - [{eol, Packet1}, {noeol, Packet1}, - {eol, Packet2}]}], 0, "", [{line,Siz}]), + Packet2, io_lib:nl()]), + [{eol, Packet1}, {noeol, Packet1}, + {eol, Packet2}]}], 0, "", [{line,Siz}]), %% Test that last output from closing port program gets received. L1 = lists:append([Packet1, io_lib:nl(), Packet2]), S1 = lists:flatten(io_lib:format("-l~w", [length(L1)])), io:format("S1 = ~w, L1 = ~w~n", [S1,L1]), port_expect(Config,[{L1, - [{eol, Packet1}, {noeol, Packet2}, eof]}], 0, - S1, [{line,Siz},eof]), + [{eol, Packet1}, {noeol, Packet2}, eof]}], 0, + S1, [{line,Siz},eof]), %% Test that lonely Don't get treated as newlines port_expect(Config,[{lists:append([Packet1, [13], Packet2, - io_lib:nl()]), - [{noeol, Packet1}, {eol, [13 |Packet2]}]}], - 0, "", [{line,Siz}]), + io_lib:nl()]), + [{noeol, Packet1}, {eol, [13 |Packet2]}]}], + 0, "", [{line,Siz}]), %% Test that packets get built up to lines (delayed output from %% port program) port_expect(Config,[{Packet2,[]}, - {lists:append([Packet2, io_lib:nl(), - Packet1, io_lib:nl()]), - [{eol, lists:append(Packet2, Packet2)}, - {eol, Packet1}]}], 0, "-d", [{line,Siz}]), + {lists:append([Packet2, io_lib:nl(), + Packet1, io_lib:nl()]), + [{eol, lists:append(Packet2, Packet2)}, + {eol, Packet1}]}], 0, "-d", [{line,Siz}]), %% Test that we get badarg if trying both packet and line bad_argument(Config, [{packet, 5}, {line, 5}]), ok. @@ -801,7 +801,7 @@ stderr_to_stdout(Config) when is_list(Config) -> %% See that it works Packet = random_packet(10), port_expect(Config,[{Packet,[Packet]}], 0, "-e -l10", - [stderr_to_stdout]), + [stderr_to_stdout]), %% stream_ping(Config, 10, "-e", [stderr_to_stdout]), %% See that it doesn't always happen (will generate garbage on stderr) port_expect(Config,[{Packet,[eof]}], 0, "-e -l10", [line,eof]), @@ -811,10 +811,10 @@ stderr_to_stdout(Config) when is_list(Config) -> bad_argument(Config, ArgList) -> PortTest = port_test(Config), case catch open_port({spawn, PortTest}, ArgList) of - {'EXIT', {badarg, _}} -> - ok + {'EXIT', {badarg, _}} -> + ok end. - + %% 'env' option %% (Can perhaps be made smaller by calling the other utility functions @@ -833,34 +833,34 @@ env(Config) when is_list(Config) -> os:putenv(Long, "nisse"), env_slave(Temp, [{"plupp",PluppVal}, - {"DIR_PLUPP","###glurfrik"}], - fun() -> - PluppVal = os:getenv("plupp"), - "###glurfrik" = os:getenv("DIR_PLUPP"), - "nisse" = os:getenv(Long) - end), + {"DIR_PLUPP","###glurfrik"}], + fun() -> + PluppVal = os:getenv("plupp"), + "###glurfrik" = os:getenv("DIR_PLUPP"), + "nisse" = os:getenv(Long) + end), env_slave(Temp, [{"must_define_something","some_value"}, - {"certainly_not_existing",false}, - {"ends_with_equal", "value="}, - {Long,false}, - {"glurf","a glorfy string"}]), + {"certainly_not_existing",false}, + {"ends_with_equal", "value="}, + {Long,false}, + {"glurf","a glorfy string"}]), %% A lot of non existing variables (mingled with existing) NotExistingList = [{lists:flatten(io_lib:format("V~p_not_existing",[X])),false} - || X <- lists:seq(1,150)], + || X <- lists:seq(1,150)], ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"} - || X <- lists:seq(1,150)], + || X <- lists:seq(1,150)], env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)), ok. env_slave(File, Env) -> F = fun() -> - lists:foreach(fun({Name,Val}) -> - Val = os:getenv(Name) - end, Env) - end, + lists:foreach(fun({Name,Val}) -> + Val = os:getenv(Name) + end, Env) + end, env_slave(File, Env, F). env_slave(File, Env, Body) -> @@ -868,26 +868,26 @@ env_slave(File, Env, Body) -> Program = atom_to_list(lib:progname()), Dir = filename:dirname(code:which(?MODULE)), Cmd = Program ++ " -pz " ++ Dir ++ - " -noinput -run " ++ ?MODULE_STRING ++ " env_slave_main " ++ - File ++ " -run erlang halt", + " -noinput -run " ++ ?MODULE_STRING ++ " env_slave_main " ++ + File ++ " -run erlang halt", Port = open_port({spawn, Cmd}, [{env,Env},{line,256}]), receive - {Port,{data,{eol,"ok"}}} -> - ok; - {Port,{data,{eol,Error}}} -> - ct:fail("eol error ~p\n", [Error]); - Other -> - ct:fail(Other) + {Port,{data,{eol,"ok"}}} -> + ok; + {Port,{data,{eol,Error}}} -> + ct:fail("eol error ~p\n", [Error]); + Other -> + ct:fail(Other) end. env_slave_main([File]) -> {ok,Body0} = file:read_file(File), Body = binary_to_term(Body0), case Body() of - {'EXIT',Reason} -> - io:format("Error: ~p\n", [Reason]); - _ -> - io:format("ok\n") + {'EXIT',Reason} -> + io:format("Error: ~p\n", [Reason]); + _ -> + io:format("ok\n") end, init:stop(). @@ -909,23 +909,23 @@ bad_env(Config) when is_list(Config) -> try_bad_env(Env) -> try open_port({spawn,"ls"}, [{env,Env}]) catch - error:badarg -> ok + error:badarg -> ok end. %% Test that we can handle a very very large environment gracefully. huge_env(Config) when is_list(Config) -> Vars = case os:type() of - {win32,_} -> 500; - _ -> + {win32,_} -> 500; + _ -> %% We create a huge environment, %% 20000 variables is about 25MB %% which seems to be the limit on Linux. 20000 - end, + end, Env = [{[$a + I div (25*25*25*25) rem 25, - $a + I div (25*25*25) rem 25, - $a + I div (25*25) rem 25, - $a+I div 25 rem 25, $a+I rem 25], + $a + I div (25*25*25) rem 25, + $a + I div (25*25) rem 25, + $a+I div 25 rem 25, $a+I rem 25], lists:duplicate(100,$a+I rem 25)} || I <- lists:seq(1,Vars)], try erlang:open_port({spawn,"ls"},[exit_status, {env, Env}]) of @@ -939,9 +939,9 @@ huge_env(Config) when is_list(Config) -> true = is_integer(N) end catch E:R -> - %% Have to catch the error here, as printing the stackdump - %% in the ct log is way to heavy for some test machines. - ct:fail("Open port failed ~p:~p",[E,R]) + %% Have to catch the error here, as printing the stackdump + %% in the ct log is way to heavy for some test machines. + ct:fail("Open port failed ~p:~p",[E,R]) end. @@ -958,43 +958,43 @@ cd(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir, Config), TestDir = filename:join(DataDir, "dir"), Cmd = Program ++ " -pz " ++ DataDir ++ - " -noshell -s port_test pwd -s erlang halt", + " -noshell -s port_test pwd -s erlang halt", _ = open_port({spawn, Cmd}, - [{cd, TestDir}, - {line, 256}]), + [{cd, TestDir}, + {line, 256}]), receive - {_, {data, {eol, String}}} -> - case filename_equal(String, TestDir) of - true -> - ok; - false -> - ct:fail({cd, String}) - end; - Other2 -> - ct:fail({env, Other2}) + {_, {data, {eol, String}}} -> + case filename_equal(String, TestDir) of + true -> + ok; + false -> + ct:fail({cd, String}) + end; + Other2 -> + ct:fail({env, Other2}) end, _ = open_port({spawn, Cmd}, - [{cd, unicode:characters_to_binary(TestDir)}, - {line, 256}]), + [{cd, unicode:characters_to_binary(TestDir)}, + {line, 256}]), receive - {_, {data, {eol, String2}}} -> - case filename_equal(String2, TestDir) of - true -> - ok; - false -> - ct:fail({cd, String2}) - end; - Other3 -> - ct:fail({env, Other3}) + {_, {data, {eol, String2}}} -> + case filename_equal(String2, TestDir) of + true -> + ok; + false -> + ct:fail({cd, String2}) + end; + Other3 -> + ct:fail({env, Other3}) end, ok. filename_equal(A, B) -> case os:type() of - {win32, _} -> - win_filename_equal(A, B); - _ -> - A == B + {win32, _} -> + win_filename_equal(A, B); + _ -> + A == B end. win_filename_equal([], []) -> @@ -1005,10 +1005,10 @@ win_filename_equal(_, []) -> false; win_filename_equal([C1 | Rest1], [C2 | Rest2]) -> case tolower(C1) == tolower(C2) of - true -> - win_filename_equal(Rest1, Rest2); - false -> - false + true -> + win_filename_equal(Rest1, Rest2); + false -> + false end. tolower(C) when C >= $A, C =< $Z -> @@ -1020,10 +1020,10 @@ tolower(C) -> %% a large amount of concurrently dying children. See ticket OTP-3906. otp_3906(Config) when is_list(Config) -> case os:type() of - {unix, OSName} -> - otp_3906(Config, OSName); - _ -> - {skipped, "Only run on Unix systems"} + {unix, OSName} -> + otp_3906(Config, OSName); + _ -> + {skipped, "Only run on Unix systems"} end. -define(OTP_3906_CHILDREN, 1000). @@ -1036,54 +1036,54 @@ otp_3906(Config) when is_list(Config) -> otp_3906(Config, OSName) -> DataDir = filename:dirname(proplists:get_value(data_dir,Config)), {ok, Variables} = file:consult( - filename:join([DataDir,"..","..", - "test_server","variables"])), + filename:join([DataDir,"..","..", + "test_server","variables"])), case lists:keysearch('CC', 1, Variables) of - {value,{'CC', CC}} -> - SuiteDir = filename:dirname(code:which(?MODULE)), - PrivDir = proplists:get_value(priv_dir, Config), - Prog = otp_3906_make_prog(CC, PrivDir), - {ok, Node} = test_server:start_node(otp_3906, - slave, - [{args, " -pa " ++ SuiteDir}, - {linked, false}]), - OP = process_flag(priority, max), - OTE = process_flag(trap_exit, true), - FS = spawn_link(Node, - ?MODULE, - otp_3906_start_forker_starter, - [?OTP_3906_CHILDREN, [], self(), Prog]), - Result = receive - {'EXIT', _ForkerStarter, Reason} -> - {failed, Reason}; - {emulator_pid, EmPid} -> - case otp_3906_wait_result(FS, 0, 0) of - {succeded, - ?OTP_3906_CHILDREN, - ?OTP_3906_CHILDREN} -> - succeded; - {succeded, Forked, Exited} -> - otp_3906_list_defunct(EmPid, OSName), - {failed, - {mismatch, - {forked, Forked}, - {exited, Exited}}}; - Res -> - otp_3906_list_defunct(EmPid, OSName), - Res - end - end, - process_flag(trap_exit, OTE), - process_flag(priority, OP), - test_server:stop_node(Node), - case Result of - succeded -> - ok; - _ -> - ct:fail(Result) - end; - _ -> - {skipped, "No C compiler found"} + {value,{'CC', CC}} -> + SuiteDir = filename:dirname(code:which(?MODULE)), + PrivDir = proplists:get_value(priv_dir, Config), + Prog = otp_3906_make_prog(CC, PrivDir), + {ok, Node} = test_server:start_node(otp_3906, + slave, + [{args, " -pa " ++ SuiteDir}, + {linked, false}]), + OP = process_flag(priority, max), + OTE = process_flag(trap_exit, true), + FS = spawn_link(Node, + ?MODULE, + otp_3906_start_forker_starter, + [?OTP_3906_CHILDREN, [], self(), Prog]), + Result = receive + {'EXIT', _ForkerStarter, Reason} -> + {failed, Reason}; + {emulator_pid, EmPid} -> + case otp_3906_wait_result(FS, 0, 0) of + {succeded, + ?OTP_3906_CHILDREN, + ?OTP_3906_CHILDREN} -> + succeded; + {succeded, Forked, Exited} -> + otp_3906_list_defunct(EmPid, OSName), + {failed, + {mismatch, + {forked, Forked}, + {exited, Exited}}}; + Res -> + otp_3906_list_defunct(EmPid, OSName), + Res + end + end, + process_flag(trap_exit, OTE), + process_flag(priority, OP), + test_server:stop_node(Node), + case Result of + succeded -> + ok; + _ -> + ct:fail(Result) + end; + _ -> + {skipped, "No C compiler found"} end. otp_3906_list_defunct(EmPid, OSName) -> @@ -1110,9 +1110,9 @@ otp_3906_htmlize([]) -> []; otp_3906_htmlize([C | Cs]) -> case [C] of - "<" -> "<" ++ otp_3906_htmlize(Cs); - ">" -> ">" ++ otp_3906_htmlize(Cs); - _ -> [C | otp_3906_htmlize(Cs)] + "<" -> "<" ++ otp_3906_htmlize(Cs); + ">" -> ">" ++ otp_3906_htmlize(Cs); + _ -> [C | otp_3906_htmlize(Cs)] end. otp_3906_make_prog(CC, PrivDir) -> @@ -1120,12 +1120,12 @@ otp_3906_make_prog(CC, PrivDir) -> TrgtFileName = filename:join(PrivDir, ?OTP_3906_PROGNAME), {ok, SrcFile} = file:open(SrcFileName, write), io:format(SrcFile, - "int ~n" - "main(void) ~n" - "{ ~n" - " return ~p; ~n" - "} ~n", - [?OTP_3906_EXIT_STATUS]), + "int ~n" + "main(void) ~n" + "{ ~n" + " return ~p; ~n" + "} ~n", + [?OTP_3906_EXIT_STATUS]), file:close(SrcFile), os:cmd(CC ++ " " ++ SrcFileName ++ " -o " ++ TrgtFileName), TrgtFileName. @@ -1133,21 +1133,21 @@ otp_3906_make_prog(CC, PrivDir) -> otp_3906_wait_result(ForkerStarter, F, E) -> receive - {'EXIT', ForkerStarter, Reason} -> - {failed, {Reason, {forked, F}, {exited, E}}}; - forked -> - otp_3906_wait_result(ForkerStarter, F+1, E); - exited -> - otp_3906_wait_result(ForkerStarter, F, E+1); - tick -> - otp_3906_wait_result(ForkerStarter, F, E); - succeded -> - {succeded, F, E} + {'EXIT', ForkerStarter, Reason} -> + {failed, {Reason, {forked, F}, {exited, E}}}; + forked -> + otp_3906_wait_result(ForkerStarter, F+1, E); + exited -> + otp_3906_wait_result(ForkerStarter, F, E+1); + tick -> + otp_3906_wait_result(ForkerStarter, F, E); + succeded -> + {succeded, F, E} after - ?OTP_3906_TICK_TIMEOUT -> - unlink(ForkerStarter), - exit(ForkerStarter, timeout), - {failed, {timeout, {forked, F}, {exited, E}}} + ?OTP_3906_TICK_TIMEOUT -> + unlink(ForkerStarter), + exit(ForkerStarter, timeout), + {failed, {timeout, {forked, F}, {exited, E}}} end. otp_3906_collect([], _) -> @@ -1157,17 +1157,17 @@ otp_3906_collect(RefList, Sup) -> otp_3906_collect_one(RefList, Sup) -> receive - Ref when is_reference(Ref) -> - Sup ! tick, - lists:delete(Ref, RefList) + Ref when is_reference(Ref) -> + Sup ! tick, + lists:delete(Ref, RefList) end. - + otp_3906_start_forker(N, Sup, Prog) -> Ref = make_ref(), spawn_opt(?MODULE, - otp_3906_forker, - [N, self(), Ref, Sup, Prog], - [link, {priority, max}]), + otp_3906_forker, + [N, self(), Ref, Sup, Prog], + [link, {priority, max}]), Ref. otp_3906_start_forker_starter(N, RefList, Sup, Prog) -> @@ -1186,18 +1186,18 @@ otp_3906_forker_starter(N, RefList, Sup, Prog) otp_3906_forker_starter(N, RefList, Sup, Prog) when is_integer(N), N > ?OTP_3906_OSP_P_ERLP -> otp_3906_forker_starter(N-?OTP_3906_OSP_P_ERLP, - [otp_3906_start_forker(?OTP_3906_OSP_P_ERLP, - Sup, - Prog)|RefList], - Sup, - Prog); + [otp_3906_start_forker(?OTP_3906_OSP_P_ERLP, + Sup, + Prog)|RefList], + Sup, + Prog); otp_3906_forker_starter(N, RefList, Sup, Prog) when is_integer(N) -> otp_3906_forker_starter(0, - [otp_3906_start_forker(N, - Sup, - Prog)|RefList], - Sup, - Prog). + [otp_3906_start_forker(N, + Sup, + Prog)|RefList], + Sup, + Prog). otp_3906_forker(0, Parent, Ref, _, _) -> unlink(Parent), @@ -1206,87 +1206,87 @@ otp_3906_forker(N, Parent, Ref, Sup, Prog) -> Port = erlang:open_port({spawn, Prog}, [exit_status, in]), Sup ! forked, receive - {Port, {exit_status, ?OTP_3906_EXIT_STATUS}} -> - Sup ! exited, - otp_3906_forker(N-1, Parent, Ref, Sup, Prog); - {Port, Res} -> - exit(Res); - Other -> - exit(Other) + {Port, {exit_status, ?OTP_3906_EXIT_STATUS}} -> + Sup ! exited, + otp_3906_forker(N-1, Parent, Ref, Sup, Prog); + {Port, Res} -> + exit(Res); + Other -> + exit(Other) end. otp_4389(Config) when is_list(Config) -> case os:type() of - {unix, _} -> + {unix, _} -> ct:timetrap({minutes, 4}), - TCR = self(), - case get_true_cmd() of - True when is_list(True) -> - lists:foreach( - fun (P) -> - receive - {P, ok} -> ok; - {P, Err} -> ct:fail(Err) - end - end, - lists:map( - fun(_) -> - spawn_link( - fun() -> - process_flag(trap_exit, true), - case catch open_port({spawn, True}, - [stream,exit_status]) of - P when is_port(P) -> - receive - {P,{exit_status,_}} -> - TCR ! {self(),ok}; - {'EXIT',_,{R2,_}} when R2 == emfile; - R2 == eagain; - R2 == enomem -> - TCR ! {self(),ok}; - Err2 -> - TCR ! {self(),{msg,Err2}} - end; - {'EXIT',{R1,_}} when R1 == emfile; - R1 == eagain; - R1 == enomem -> - TCR ! {self(),ok}; - Err1 -> - TCR ! {self(), {open_port,Err1}} - end - end) - end, - lists:duplicate(1000,[]))), - {comment, - "This test case doesn't always fail when the bug that " - "it tests for is present (it is most likely to fail on" - " a multi processor machine). If the test case fails it" - " will fail by deadlocking the emulator."}; - _ -> - {skipped, "\"true\" command not found"} - end; - _ -> - {skip,"Only run on Unix"} + TCR = self(), + case get_true_cmd() of + True when is_list(True) -> + lists:foreach( + fun (P) -> + receive + {P, ok} -> ok; + {P, Err} -> ct:fail(Err) + end + end, + lists:map( + fun(_) -> + spawn_link( + fun() -> + process_flag(trap_exit, true), + case catch open_port({spawn, True}, + [stream,exit_status]) of + P when is_port(P) -> + receive + {P,{exit_status,_}} -> + TCR ! {self(),ok}; + {'EXIT',_,{R2,_}} when R2 == emfile; + R2 == eagain; + R2 == enomem -> + TCR ! {self(),ok}; + Err2 -> + TCR ! {self(),{msg,Err2}} + end; + {'EXIT',{R1,_}} when R1 == emfile; + R1 == eagain; + R1 == enomem -> + TCR ! {self(),ok}; + Err1 -> + TCR ! {self(), {open_port,Err1}} + end + end) + end, + lists:duplicate(1000,[]))), + {comment, + "This test case doesn't always fail when the bug that " + "it tests for is present (it is most likely to fail on" + " a multi processor machine). If the test case fails it" + " will fail by deadlocking the emulator."}; + _ -> + {skipped, "\"true\" command not found"} + end; + _ -> + {skip,"Only run on Unix"} end. get_true_cmd() -> DoFileExist = fun (FileName) -> - case file:read_file_info(FileName) of - {ok, _} -> throw(FileName); - _ -> not_found - end - end, + case file:read_file_info(FileName) of + {ok, _} -> throw(FileName); + _ -> not_found + end + end, catch begin - %% First check in /usr/bin and /bin - DoFileExist("/usr/bin/true"), - DoFileExist("/bin/true"), - %% Try which - case filename:dirname(os:cmd("which true")) of - "." -> not_found; - TrueDir -> filename:join(TrueDir, "true") - end - end. + %% First check in /usr/bin and /bin + DoFileExist("/usr/bin/true"), + DoFileExist("/bin/true"), + %% Try which + case filename:dirname(os:cmd("which true")) of + "." -> not_found; + TrueDir -> filename:join(TrueDir, "true") + end + end. %% 'exit_status' option %% @@ -1305,24 +1305,24 @@ spawn_driver(Config) when is_list(Config) -> 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 -> - ct:fail({unexpected, Other}) - end, + {Port, {data, "Hello port!"}} = Msg1 -> + io:format("~p~n", [Msg1]), + ok; + Other -> + ct:fail({unexpected, Other}) + end, Port ! {self(), close}, receive {Port, closed} -> ok end, Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, - []), + []), receive - {Port2, {data, "Hello port?"}} = Msg2 -> - io:format("~p~n", [Msg2]), - ok; - Other2 -> - ct:fail({unexpected2, Other2}) - end, + {Port2, {data, "Hello port?"}} = Msg2 -> + io:format("~p~n", [Msg2]), + ok; + Other2 -> + ct:fail({unexpected2, Other2}) + end, Port2 ! {self(), close}, receive {Port2, closed} -> ok end, {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "ls"}, [])), @@ -1332,34 +1332,34 @@ spawn_driver(Config) when is_list(Config) -> %% Test parallelism option of open_port parallelism_option(Config) when is_list(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line ok = load_driver(Path, "echo_drv"), - ?line Port = erlang:open_port({spawn_driver, "echo_drv"}, - [{parallelism, true}]), - ?line {parallelism, true} = erlang:port_info(Port, parallelism), - ?line Port ! {self(), {command, "Hello port!"}}, - ?line receive - {Port, {data, "Hello port!"}} = Msg1 -> - io:format("~p~n", [Msg1]), - ok; - Other -> - ct:fail({unexpected, Other}) - end, - ?line Port ! {self(), close}, - ?line receive {Port, closed} -> ok end, - - ?line Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, - [{parallelism, false}]), - ?line {parallelism, false} = erlang:port_info(Port2, parallelism), - ?line receive - {Port2, {data, "Hello port?"}} = Msg2 -> - io:format("~p~n", [Msg2]), - ok; - Other2 -> - ct:fail({unexpected2, Other2}) - end, - ?line Port2 ! {self(), close}, - ?line receive {Port2, closed} -> ok end, + Path = proplists:get_value(data_dir, Config), + ok = load_driver(Path, "echo_drv"), + Port = erlang:open_port({spawn_driver, "echo_drv"}, + [{parallelism, true}]), + {parallelism, true} = erlang:port_info(Port, parallelism), + Port ! {self(), {command, "Hello port!"}}, + receive + {Port, {data, "Hello port!"}} = Msg1 -> + io:format("~p~n", [Msg1]), + ok; + Other -> + ct:fail({unexpected, Other}) + end, + Port ! {self(), close}, + receive {Port, closed} -> ok end, + + Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, + [{parallelism, false}]), + {parallelism, false} = erlang:port_info(Port2, parallelism), + receive + {Port2, {data, "Hello port?"}} = Msg2 -> + io:format("~p~n", [Msg2]), + ok; + Other2 -> + ct:fail({unexpected2, Other2}) + end, + Port2 ! {self(), close}, + receive {Port2, closed} -> ok end, ok. %% Test spawning an executable specifically @@ -1374,22 +1374,22 @@ spawn_executable(Config) when is_list(Config) -> ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]), ["echo_arguments"] = run_echo_args(DataDir,[binary, "echo_arguments"]), [ExactFile1,"hello world","dlrow olleh"] = - run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]), + run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]), [ExactFile1] = run_echo_args(DataDir,[default]), [ExactFile1] = run_echo_args(DataDir,[binary, default]), [ExactFile1,"hello world","dlrow olleh"] = - run_echo_args(DataDir,[switch_order,ExactFile1,"hello world", - "dlrow olleh"]), + run_echo_args(DataDir,[switch_order,ExactFile1,"hello world", + "dlrow olleh"]), [ExactFile1,"hello world","dlrow olleh"] = - run_echo_args(DataDir,[binary,switch_order,ExactFile1,"hello world", - "dlrow olleh"]), + run_echo_args(DataDir,[binary,switch_order,ExactFile1,"hello world", + "dlrow olleh"]), [ExactFile1,"hello world","dlrow olleh"] = - run_echo_args(DataDir,[default,"hello world","dlrow olleh"]), + run_echo_args(DataDir,[default,"hello world","dlrow olleh"]), [ExactFile1,"hello world","dlrow olleh"] = - run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""), + run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile1,"hello world","dlrow olleh"] = - run_echo_args_2(unicode:characters_to_binary("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\"")), + run_echo_args_2(unicode:characters_to_binary("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\"")), PrivDir = proplists:get_value(priv_dir, Config), SpaceDir = filename:join([PrivDir,"With Spaces"]), @@ -1403,33 +1403,32 @@ spawn_executable(Config) when is_list(Config) -> ["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"]), + run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]), [ExactFile2,"hello world","dlrow olleh"] = - run_echo_args(SpaceDir,[binary, ExactFile2,"hello world","dlrow olleh"]), + run_echo_args(SpaceDir,[binary, ExactFile2,"hello world","dlrow olleh"]), [ExactFile2,"hello \"world\"","\"dlrow\" olleh"] = - run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]), + run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]), [ExactFile2,"hello \"world\"","\"dlrow\" olleh"] = - run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]), + run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]), [ExactFile2] = run_echo_args(SpaceDir,[default]), [ExactFile2,"hello world","dlrow olleh"] = - run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world", - "dlrow olleh"]), + run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world", "dlrow olleh"]), [ExactFile2,"hello world","dlrow olleh"] = - run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]), + run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]), [ExactFile2,"hello world","dlrow olleh"] = - run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""), + run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile2,"hello world","dlrow olleh"] = - run_echo_args_2(unicode:characters_to_binary("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\"")), + run_echo_args_2(unicode:characters_to_binary("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\"")), ExeExt = - case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of - "exe" -> - ".exe"; - _ -> - "" - end, + case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of + "exe" -> + ".exe"; + _ -> + "" + end, Executable2 = "spoky name"++ExeExt, file:copy(ExactFile1,filename:join([SpaceDir,Executable2])), ExactFile3 = filename:nativename(filename:join([SpaceDir,Executable2])), @@ -1438,36 +1437,36 @@ spawn_executable(Config) when is_list(Config) -> ["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"]), + run_echo_args(SpaceDir,Executable2,[ExactFile3,"hello world","dlrow olleh"]), [ExactFile3] = run_echo_args(SpaceDir,Executable2,[default]), [ExactFile3,"hello world","dlrow olleh"] = - run_echo_args(SpaceDir,Executable2, - [switch_order,ExactFile3,"hello world", - "dlrow olleh"]), + run_echo_args(SpaceDir,Executable2, + [switch_order,ExactFile3,"hello world", + "dlrow olleh"]), [ExactFile3,"hello world","dlrow olleh"] = - run_echo_args(SpaceDir,Executable2, - [default,"hello world","dlrow olleh"]), + run_echo_args(SpaceDir,Executable2, + [default,"hello world","dlrow olleh"]), [ExactFile3,"hello world","dlrow olleh"] = - run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""), + run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile3,"hello world","dlrow olleh"] = - run_echo_args_2(unicode:characters_to_binary("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\"")), + run_echo_args_2(unicode:characters_to_binary("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\"")), {'EXIT',{enoent,_}} = (catch run_echo_args(SpaceDir,"fnurflmonfi", - [default,"hello world", - "dlrow olleh"])), + [default,"hello world", + "dlrow olleh"])), NonExec = "kronxfrt"++ExeExt, file:write_file(filename:join([SpaceDir,NonExec]), - <<"Not an executable">>), + <<"Not an executable">>), {'EXIT',{eacces,_}} = (catch run_echo_args(SpaceDir,NonExec, - [default,"hello world", - "dlrow olleh"])), + [default,"hello world", + "dlrow olleh"])), {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"cmd"},[])), {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"sh"},[])), case os:type() of - {win32,_} -> - test_bat_file(SpaceDir); - {unix,_} -> - test_sh_file(SpaceDir) + {win32,_} -> + test_bat_file(SpaceDir); + {unix,_} -> + test_sh_file(SpaceDir) end, ok. @@ -1479,29 +1478,29 @@ test_bat_file(Dir) -> FN = "tf.bat", Full = filename:join([Dir,FN]), D = [<<"@echo off\r\n">>, - <<"echo argv[0]:^|%0^|\r\n">>, - <<"if \"%1\" == \"\" goto done\r\n">>, - <<"echo argv[1]:^|%1^|\r\n">>, - <<"if \"%2\" == \"\" goto done\r\n">>, - <<"echo argv[2]:^|%2^|\r\n">>, - <<"if \"%3\" == \"\" goto done\r\n">>, - <<"echo argv[3]:^|%3^|\r\n">>, - <<"if \"%4\" == \"\" goto done\r\n">>, - <<"echo argv[4]:^|%4^|\r\n">>, - <<"if \"%5\" == \"\" goto done\r\n">>, - <<"echo argv[5]:^|%5^|\r\n">>, - <<"\r\n">>, - <<":done\r\n">>, - <<"\r\n">>], + <<"echo argv[0]:^|%0^|\r\n">>, + <<"if \"%1\" == \"\" goto done\r\n">>, + <<"echo argv[1]:^|%1^|\r\n">>, + <<"if \"%2\" == \"\" goto done\r\n">>, + <<"echo argv[2]:^|%2^|\r\n">>, + <<"if \"%3\" == \"\" goto done\r\n">>, + <<"echo argv[3]:^|%3^|\r\n">>, + <<"if \"%4\" == \"\" goto done\r\n">>, + <<"echo argv[4]:^|%4^|\r\n">>, + <<"if \"%5\" == \"\" goto done\r\n">>, + <<"echo argv[5]:^|%5^|\r\n">>, + <<"\r\n">>, + <<":done\r\n">>, + <<"\r\n">>], file:write_file(Full,list_to_binary(D)), EF = filename:basename(FN), [DN,"hello","world"] = - run_echo_args(Dir,FN, - [default,"hello","world"]), + run_echo_args(Dir,FN, + [default,"hello","world"]), %% The arg0 argumant should be ignored when running batch files [DN,"hello","world"] = - run_echo_args(Dir,FN, - ["knaskurt","hello","world"]), + run_echo_args(Dir,FN, + ["knaskurt","hello","world"]), EF = filename:basename(DN), ok. @@ -1509,40 +1508,40 @@ test_sh_file(Dir) -> FN = "tf.sh", Full = filename:join([Dir,FN]), D = [<<"#! /bin/sh\n">>, - <<"echo 'argv[0]:|'$0'|'\n">>, - <<"i=1\n">>, - <<"while [ '!' -z \"$1\" ]; do\n">>, - <<" echo 'argv['$i']:|'\"$1\"'|'\n">>, - <<" shift\n">>, - <<" i=`expr $i + 1`\n">>, - <<"done\n">>], + <<"echo 'argv[0]:|'$0'|'\n">>, + <<"i=1\n">>, + <<"while [ '!' -z \"$1\" ]; do\n">>, + <<" echo 'argv['$i']:|'\"$1\"'|'\n">>, + <<" shift\n">>, + <<" i=`expr $i + 1`\n">>, + <<"done\n">>], file:write_file(Full,list_to_binary(D)), chmodplusx(Full), [Full,"hello","world"] = - run_echo_args(Dir,FN, - [default,"hello","world"]), + run_echo_args(Dir,FN, + [default,"hello","world"]), [Full,"hello","world of spaces"] = - run_echo_args(Dir,FN, - [default,"hello","world of spaces"]), + run_echo_args(Dir,FN, + [default,"hello","world of spaces"]), file:write_file(filename:join([Dir,"testfile1"]),<<"testdata1">>), file:write_file(filename:join([Dir,"testfile2"]),<<"testdata2">>), Pattern = filename:join([Dir,"testfile*"]), L = filelib:wildcard(Pattern), 2 = length(L), [Full,"hello",Pattern] = - run_echo_args(Dir,FN, - [default,"hello",Pattern]), - ok. + run_echo_args(Dir,FN, + [default,"hello",Pattern]), + ok. + - chmodplusx(Filename) -> case file:read_file_info(Filename) of - {ok,FI} -> - FI2 = FI#file_info{mode = ((FI#file_info.mode) bor 8#00100)}, - file:write_file_info(Filename,FI2); - _ -> - ok + {ok,FI} -> + FI2 = FI#file_info{mode = ((FI#file_info.mode) bor 8#00100)}, + file:write_file_info(Filename,FI2); + _ -> + ok end. run_echo_args_2(FullnameAndArgs) -> @@ -1551,7 +1550,7 @@ run_echo_args_2(FullnameAndArgs) -> Port ! {self(), close}, receive {Port, closed} -> ok end, parse_echo_args_output(Data). - + run_echo_args(Where,Args) -> run_echo_args(Where,"echo_args",Args). @@ -1559,9 +1558,9 @@ run_echo_args(Where,Prog,Args) -> {Binary, ArgvArg} = pack_argv(Args), Command0 = filename:join([Where,Prog]), Command = case Binary of - true -> unicode:characters_to_binary(Command0); - false -> Command0 - end, + true -> unicode:characters_to_binary(Command0); + false -> Command0 + end, Port = open_port({spawn_executable,Command},ArgvArg++[eof]), Data = collect_data(Port), Port ! {self(), close}, @@ -1575,14 +1574,14 @@ pack_argv(Args) -> pack_argv(Args, Binary) -> case Args of - [] -> - []; - [default|T] -> - [{args,[make_bin(Arg,Binary) || Arg <- T]}]; - [switch_order,H|T] -> - [{args,[make_bin(Arg,Binary) || Arg <- T]},{arg0,make_bin(H,Binary)}]; - [H|T] -> - [{arg0,make_bin(H,Binary)},{args,[make_bin(Arg,Binary) || Arg <- T]}] + [] -> + []; + [default|T] -> + [{args,[make_bin(Arg,Binary) || Arg <- T]}]; + [switch_order,H|T] -> + [{args,[make_bin(Arg,Binary) || Arg <- T]},{arg0,make_bin(H,Binary)}]; + [H|T] -> + [{arg0,make_bin(H,Binary)},{args,[make_bin(Arg,Binary) || Arg <- T]}] end. make_bin(Str, false) -> Str; @@ -1590,10 +1589,10 @@ make_bin(Str, true) -> unicode:characters_to_binary(Str). collect_data(Port) -> receive - {Port, {data, Data}} -> - Data ++ collect_data(Port); - {Port, eof} -> - [] + {Port, {data, Data}} -> + Data ++ collect_data(Port); + {Port, eof} -> + [] end. parse_echo_args_output(Data) -> @@ -1606,33 +1605,33 @@ mix_up_ports(Config) when is_list(Config) -> 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 -> - ct:fail({unexpected, Other}) - end, + {Port, {data, "Hello port!"}} = Msg1 -> + io:format("~p~n", [Msg1]), + ok; + Other -> + ct:fail({unexpected, Other}) + end, Port ! {self(), close}, receive {Port, closed} -> ok end, loop(start, done, - fun(P) -> - Q = - (catch erlang:open_port({spawn, "echo_drv"}, [])), -%% io:format("~p ", [Q]), - if is_port(Q) -> - Q; - true -> - io:format("~p~n", [P]), - done - end - end), + fun(P) -> + Q = + (catch erlang:open_port({spawn, "echo_drv"}, [])), + %% io:format("~p ", [Q]), + if is_port(Q) -> + Q; + true -> + io:format("~p~n", [P]), + done + end + end), Port ! {self(), {command, "Hello again port!"}}, receive - Msg2 -> - ct:fail({unexpected, Msg2}) - after 1000 -> - ok - end, + Msg2 -> + ct:fail({unexpected, Msg2}) + after 1000 -> + ok + end, ok. loop(Stop, Stop, Fun) when is_function(Fun) -> @@ -1653,7 +1652,7 @@ otp_5112(Config) when is_list(Config) -> io:format("Links1: ~p~n",[Links1]), true = lists:member(Port, Links1), Port ! {self(), {command, ""}}, - ?line wait_until(fun () -> lists:member(Port, erlang:ports()) == false end), + wait_until(fun () -> lists:member(Port, erlang:ports()) == false end), {links, Links2} = process_info(self(),links), io:format("Links2: ~p~n",[Links2]), false = lists:member(Port, Links2), %% This used to fail @@ -1662,27 +1661,27 @@ otp_5112(Config) when is_list(Config) -> otp_5112_get_wrapped_port() -> P1 = erlang:open_port({spawn, "exit_drv"}, []), case port_ix(P1) < max_ports() of - true -> - io:format("Need to wrap port index (~p)~n", [P1]), - otp_5112_wrap_port_ix([P1]), - P2 = erlang:open_port({spawn, "exit_drv"}, []), - false = port_ix(P2) < max_ports(), - P2; - false -> - io:format("Port index already wrapped (~p)~n", [P1]), - P1 - end. + true -> + io:format("Need to wrap port index (~p)~n", [P1]), + otp_5112_wrap_port_ix([P1]), + P2 = erlang:open_port({spawn, "exit_drv"}, []), + false = port_ix(P2) < max_ports(), + P2; + false -> + io:format("Port index already wrapped (~p)~n", [P1]), + P1 + end. otp_5112_wrap_port_ix(Ports) -> case (catch erlang:open_port({spawn, "exit_drv"}, [])) of - Port when is_port(Port) -> - otp_5112_wrap_port_ix([Port|Ports]); - _ -> - %% Port table now full; empty port table - lists:foreach(fun (P) -> P ! {self(), close} end, - Ports), - ok - end. + Port when is_port(Port) -> + otp_5112_wrap_port_ix([Port|Ports]); + _ -> + %% Port table now full; empty port table + lists:foreach(fun (P) -> P ! {self(), close} end, + Ports), + ok + end. %% Test that port index is not unnecessarily wrapped @@ -1693,12 +1692,12 @@ otp_5119(Config) when is_list(Config) -> Port2 = erlang:open_port({spawn, "exit_drv"}, []), PI2 = port_ix(Port2), {PortIx1, PortIx2} = case PI2 > PI1 of - true -> - {PI1, PI2}; - false -> - {port_ix(otp_5119_fill_empty_port_tab([Port2])), - port_ix(erlang:open_port({spawn, "exit_drv"}, []))} - end, + true -> + {PI1, PI2}; + false -> + {port_ix(otp_5119_fill_empty_port_tab([Port2])), + port_ix(erlang:open_port({spawn, "exit_drv"}, []))} + end, MaxPorts = max_ports(), io:format("PortIx1 = ~p ~p~n", [PI1, PortIx1]), io:format("PortIx2 = ~p ~p~n", [PI2, PortIx2]), @@ -1709,22 +1708,22 @@ otp_5119(Config) when is_list(Config) -> otp_5119_fill_empty_port_tab(Ports) -> case (catch erlang:open_port({spawn, "exit_drv"}, [])) of - Port when is_port(Port) -> - otp_5119_fill_empty_port_tab([Port|Ports]); - _ -> - %% Port table now full; empty port table - lists:foreach(fun (P) -> P ! {self(), close} end, - Ports), - [LastPort|_] = Ports, - LastPort - end. + Port when is_port(Port) -> + otp_5119_fill_empty_port_tab([Port|Ports]); + _ -> + %% Port table now full; empty port table + lists:foreach(fun (P) -> P ! {self(), close} end, + Ports), + [LastPort|_] = Ports, + LastPort + end. max_ports() -> erlang:system_info(port_limit). port_ix(Port) when is_port(Port) -> ["#Port",_,PortIxStr] = string:tokens(erlang:port_to_list(Port), - "<.>"), + "<.>"), list_to_integer(PortIxStr). @@ -1734,25 +1733,25 @@ otp_6224(Config) when is_list(Config) -> ok = load_driver(Path, "failure_drv"), Go = make_ref(), Failer = spawn(fun () -> - receive Go -> ok end, - Port = open_port({spawn, "failure_drv"}, - []), - Port ! {self(), {command, "Fail, please!"}}, - otp_6224_loop() - end), + receive Go -> ok end, + Port = open_port({spawn, "failure_drv"}, + []), + Port ! {self(), {command, "Fail, please!"}}, + otp_6224_loop() + end), Mon = erlang:monitor(process, Failer), Failer ! Go, receive - {'DOWN', Mon, process, Failer, Reason} -> - case Reason of - {driver_failed, _} -> ok; - driver_failed -> ok; - _ -> ct:fail({unexpected_exit_reason, - Reason}) - end - end, + {'DOWN', Mon, process, Failer, Reason} -> + case Reason of + {driver_failed, _} -> ok; + driver_failed -> ok; + _ -> ct:fail({unexpected_exit_reason, + Reason}) + end + end, ok. - + otp_6224_loop() -> receive _ -> ok after 0 -> ok end, otp_6224_loop(). @@ -1764,23 +1763,23 @@ otp_6224_loop() -> exit_status_multi_scheduling_block(Config) when is_list(Config) -> Repeat = 3, case test_server:os_type() of - {unix, _} -> - ct:timetrap({minutes, 2*Repeat}), - SleepSecs = 6, - try - lists:foreach(fun (_) -> - exit_status_msb_test(Config, - SleepSecs) - end, - lists:seq(1, Repeat)) - after - %% Wait for the system to recover (regardless - %% of success or not) otherwise later testcases - %% may unnecessarily fail. - receive after SleepSecs+500 -> ok end - end; - _ -> {skip, "Not implemented for this OS"} - end. + {unix, _} -> + ct:timetrap({minutes, 2*Repeat}), + SleepSecs = 6, + try + lists:foreach(fun (_) -> + exit_status_msb_test(Config, + SleepSecs) + end, + lists:seq(1, Repeat)) + after + %% Wait for the system to recover (regardless + %% of success or not) otherwise later testcases + %% may unnecessarily fail. + receive after SleepSecs+500 -> ok end + end; + _ -> {skip, "Not implemented for this OS"} + end. exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> %% @@ -1794,74 +1793,74 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> PortProg = "sleep " ++ integer_to_list(SleepSecs), Start = erlang:monotonic_time(micro_seconds), NoProcs = case NoSchedsOnln of - NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS -> - NProcs; - _ -> - ?EXIT_STATUS_MSB_MAX_PROCS - end, + NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS -> + NProcs; + _ -> + ?EXIT_STATUS_MSB_MAX_PROCS + end, NoPortsPerProc = case 20*NoProcs of - TNPorts when TNPorts < ?EXIT_STATUS_MSB_MAX_PORTS -> 20; - _ -> ?EXIT_STATUS_MSB_MAX_PORTS div NoProcs - end, + TNPorts when TNPorts < ?EXIT_STATUS_MSB_MAX_PORTS -> 20; + _ -> ?EXIT_STATUS_MSB_MAX_PORTS div NoProcs + end, io:format("NoProcs = ~p~nNoPortsPerProc = ~p~n", - [NoProcs, NoPortsPerProc]), + [NoProcs, NoPortsPerProc]), ProcFun - = fun () -> - PrtSIds = lists:map( - fun (_) -> - erlang:yield(), - case catch open_port({spawn, PortProg}, - [exit_status]) of - Prt when is_port(Prt) -> - {Prt, - erlang:system_info(scheduler_id)}; - {'EXIT', {Err, _}} when Err == eagain; - Err == emfile; - Err == enomem -> - noop; - {'EXIT', Err} when Err == eagain; - Err == emfile; - Err == enomem -> - noop; - Error -> - ct:fail(Error) - end - end, - lists:seq(1, NoPortsPerProc)), - SIds = lists:filter(fun (noop) -> false; - (_) -> true - end, - lists:map(fun (noop) -> noop; - ({_, SId}) -> SId - end, - PrtSIds)), - process_flag(scheduler, 0), - Parent ! {self(), started, SIds}, - lists:foreach( - fun (noop) -> - noop; - ({Port, _}) -> - receive - {Port, {exit_status, 0}} -> - ok; - {Port, {exit_status, Status}} when Status > 128 -> - %% Sometimes happens when we have created - %% too many ports. - ok; - {Port, {exit_status, _}} = ESMsg -> - {Port, {exit_status, 0}} = ESMsg - end - end, - PrtSIds), - Parent ! {self(), done} - end, - Procs = lists:map(fun (N) -> - spawn_opt(ProcFun, - [link, - {scheduler, - (N rem NoSchedsOnln)+1}]) + = fun () -> + PrtSIds = lists:map( + fun (_) -> + erlang:yield(), + case catch open_port({spawn, PortProg}, + [exit_status]) of + Prt when is_port(Prt) -> + {Prt, + erlang:system_info(scheduler_id)}; + {'EXIT', {Err, _}} when Err == eagain; + Err == emfile; + Err == enomem -> + noop; + {'EXIT', Err} when Err == eagain; + Err == emfile; + Err == enomem -> + noop; + Error -> + ct:fail(Error) + end end, - lists:seq(1, NoProcs)), + lists:seq(1, NoPortsPerProc)), + SIds = lists:filter(fun (noop) -> false; + (_) -> true + end, + lists:map(fun (noop) -> noop; + ({_, SId}) -> SId + end, + PrtSIds)), + process_flag(scheduler, 0), + Parent ! {self(), started, SIds}, + lists:foreach( + fun (noop) -> + noop; + ({Port, _}) -> + receive + {Port, {exit_status, 0}} -> + ok; + {Port, {exit_status, Status}} when Status > 128 -> + %% Sometimes happens when we have created + %% too many ports. + ok; + {Port, {exit_status, _}} = ESMsg -> + {Port, {exit_status, 0}} = ESMsg + end + end, + PrtSIds), + Parent ! {self(), done} + end, + Procs = lists:map(fun (N) -> + spawn_opt(ProcFun, + [link, + {scheduler, + (N rem NoSchedsOnln)+1}]) + end, + lists:seq(1, NoProcs)), SIds = lists:map(fun (P) -> receive {P, started, SIds} -> SIds end end, @@ -1877,27 +1876,27 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> ok = verify_multi_scheduling_blocked(), erlang:system_flag(multi_scheduling, unblock), case {length(lists:usort(lists:flatten(SIds))), NoSchedsOnln} of - {N, N} -> - ok; - {N, M} -> - ct:fail("Failed to create ports on all ~w available" - "schedulers. Only created ports on ~w schedulers.", [M, N]) - end. + {N, N} -> + ok; + {N, M} -> + ct:fail("Failed to create ports on all ~w available" + "schedulers. Only created ports on ~w schedulers.", [M, N]) + end. save_sid(SIds) -> SId = erlang:system_info(scheduler_id), case lists:member(SId, SIds) of - true -> SIds; - false -> [SId|SIds] + true -> SIds; + false -> [SId|SIds] end. sid_proc(SIds) -> NewSIds = save_sid(SIds), receive - {From, want_sids} -> - From ! {self(), sids, NewSIds} + {From, want_sids} -> + From ! {self(), sids, NewSIds} after 0 -> - sid_proc(NewSIds) + sid_proc(NewSIds) end. verify_multi_scheduling_blocked() -> @@ -1913,8 +1912,8 @@ verify_multi_scheduling_blocked() -> Procs), 1 = length(lists:usort(lists:flatten(SIds))), ok. - - + + %%% Pinging functions. stream_ping(Config, Size, CmdLine, Options) -> @@ -1923,10 +1922,10 @@ stream_ping(Config, Size, CmdLine, Options) -> ping(Config, Sizes, HSize, CmdLine, Options) -> Actions = lists:map(fun(Size) -> - [$p|Packet] = random_packet(Size, "ping"), - {[$p|Packet], [[$P|Packet]]} - end, - Sizes), + [$p|Packet] = random_packet(Size, "ping"), + {[$p|Packet], [[$P|Packet]]} + end, + Sizes), port_expect(Config, Actions, HSize, CmdLine, Options). %% expect_input(Sizes, HSize, CmdLine, Options) @@ -1948,7 +1947,7 @@ expect_input1(Config, [Size|Rest], Params, Expect, ReplyCommand) -> expect_input1(Config, [], {HSize, CmdLine0, Options}, Expect, ReplyCommand) -> CmdLine = build_cmd_line(CmdLine0, ReplyCommand, []), port_expect(Config, [{false, lists:reverse(Expect)}], - HSize, CmdLine, Options). + HSize, CmdLine, Options). build_cmd_line(FixedCmdLine, [Cmd|Rest], []) -> build_cmd_line(FixedCmdLine, Rest, [Cmd]); @@ -1971,15 +1970,15 @@ build_cmd_line(FixedCmdLine, [], Result) -> %% Returns the port. port_expect(Config, Actions, HSize, CmdLine, Options0) -> -% io:format("port_expect(~p, ~p, ~p, ~p)", -% [Actions, HSize, CmdLine, Options0]), + % io:format("port_expect(~p, ~p, ~p, ~p)", + % [Actions, HSize, CmdLine, Options0]), PortTest = port_test(Config), Cmd = lists:concat([PortTest, " -h", HSize, " ", CmdLine]), PortType = - case HSize of - 0 -> stream; - _ -> {packet, HSize} - end, + case HSize of + 0 -> stream; + _ -> {packet, HSize} + end, Options = [PortType|Options0], io:format("open_port({spawn, ~p}, ~p)", [Cmd, Options]), Port = open_port({spawn, Cmd}, Options), @@ -1990,11 +1989,11 @@ port_expect(Port, [{Send, Expects}|Rest], Options) when is_list(Expects) -> port_send(Port, Send), IsBinaryPort = lists:member(binary, Options), Receiver = - case {lists:member(stream, Options), line_option(Options)} of - {false, _} -> fun receive_all/2; - {true,false} -> fun stream_receive_all/2; - {_, true} -> fun receive_all/2 - end, + case {lists:member(stream, Options), line_option(Options)} of + {false, _} -> fun receive_all/2; + {true,false} -> fun stream_receive_all/2; + {_, true} -> fun receive_all/2 + end, Receiver(Port, maybe_to_binary(Expects, IsBinaryPort)), port_expect(Port, Rest, Options); port_expect(_, [], _) -> @@ -2022,34 +2021,34 @@ maybe_to_binary(Expects, false) -> port_send(_Port, false) -> ok; port_send(Port, Send) when is_list(Send) -> -% io:format("port_send(~p, ~p)", [Port, Send]), + % io:format("port_send(~p, ~p)", [Port, Send]), Port ! {self(), {command, Send}}. receive_all(Port, [Expect|Rest]) -> -% io:format("receive_all(~p, [~p|Rest])", [Port, Expect]), + % io:format("receive_all(~p, [~p|Rest])", [Port, Expect]), receive - {Port, {data, Expect}} -> - io:format("Received ~s", [format(Expect)]), - ok; - {Port, {data, Other}} -> - io:format("Received ~s; expected ~s", - [format(Other), format(Expect)]), - ct:fail(bad_message); - Other -> - %% (We're not yet prepared for receiving both 'eol' and - %% 'exit_status'; remember that they may appear in any order.) - case {Expect, Rest, Other} of - {eof, [], {Port, eof}} -> - io:format("Received soft EOF.",[]), - ok; - {{exit_status, S}, [], {Port, {exit_status, S}}} -> - io:format("Received exit status ~p.",[S]), - ok; - _ -> -%%% io:format("Unexpected message: ~s", [format(Other)]), - io:format("Unexpected message: ~w", [Other]), - ct:fail(unexpected_message) - end + {Port, {data, Expect}} -> + io:format("Received ~s", [format(Expect)]), + ok; + {Port, {data, Other}} -> + io:format("Received ~s; expected ~s", + [format(Other), format(Expect)]), + ct:fail(bad_message); + Other -> + %% (We're not yet prepared for receiving both 'eol' and + %% 'exit_status'; remember that they may appear in any order.) + case {Expect, Rest, Other} of + {eof, [], {Port, eof}} -> + io:format("Received soft EOF.",[]), + ok; + {{exit_status, S}, [], {Port, {exit_status, S}}} -> + io:format("Received exit status ~p.",[S]), + ok; + _ -> + %%% io:format("Unexpected message: ~s", [format(Other)]), + io:format("Unexpected message: ~w", [Other]), + ct:fail(unexpected_message) + end end, receive_all(Port, Rest); receive_all(_Port, []) -> @@ -2064,19 +2063,19 @@ stream_receive_all1(_Port, []) -> ok; stream_receive_all1(Port, Expect) -> receive - {Port, {data, Data}} -> - Remaining = compare(Data, Expect), - stream_receive_all1(Port, Remaining); - Other -> - ct:fail({bad_message, Other}) + {Port, {data, Data}} -> + Remaining = compare(Data, Expect), + stream_receive_all1(Port, Remaining); + Other -> + ct:fail({bad_message, Other}) end. compare(B1, B2) when is_binary(B1), is_binary(B2), byte_size(B1) =< byte_size(B2) -> case split_binary(B2, size(B1)) of - {B1,Remaining} -> - Remaining; - _Other -> - ct:fail(nomatch) + {B1,Remaining} -> + Remaining; + _Other -> + ct:fail(nomatch) end; compare(B1, B2) when is_binary(B1), is_binary(B2) -> ct:fail(too_much_data); @@ -2098,10 +2097,10 @@ format({Eol,List}) -> io_lib:format("tuple<~w,~s>",[Eol, maybe_to_list(List)]); format(List) when is_list(List) -> case list_at_least(50, List) of - true -> - io_lib:format("\"~-50s...\"", [List]); - false -> - io_lib:format("~p", [List]) + true -> + io_lib:format("\"~-50s...\"", [List]); + false -> + io_lib:format("~p", [List]) end; format(Bin) when is_binary(Bin), size(Bin) >= 50 -> io_lib:format("binary<~-50s...>", [binary_to_list(Bin, 1, 50)]); @@ -2128,17 +2127,17 @@ build_packet(0, Result, _NextChar) -> lists:reverse(Result); build_packet(Left, Result, NextChar0) -> NextChar = - if - NextChar0 >= 126 -> - 33; - true -> - NextChar0+1 - end, + if + NextChar0 >= 126 -> + 33; + true -> + NextChar0+1 + end, build_packet(Left-1, [NextChar0|Result], NextChar). sizes() -> [10, 13, 64, 127, 128, 255, 256, 1023, 1024, - 32767, 32768, 65535, 65536]. + 32767, 32768, 65535, 65536]. sizes(Header_Size) -> sizes(Header_Size, sizes(), []). @@ -2160,11 +2159,11 @@ random_char(Chars) -> uniform(N) -> case rand:export_seed() of - undefined -> - rand:seed(exsplus), - io:format("Random seed = ~p\n", [rand:export_seed()]); - _ -> - ok + undefined -> + rand:seed(exsplus), + io:format("Random seed = ~p\n", [rand:export_seed()]); + _ -> + ok end, rand:uniform(N). @@ -2206,7 +2205,7 @@ ports_snapshots(Iter, TrafficPid, OtherPorts) -> TrafficPid ! {self(), stop}, receive {TrafficPid, EventList, TrafficPorts} -> ok end, - + %%io:format("Snapshot=~p\n", [Snapshot]), ports_verify(Snapshot, OtherPorts ++ TrafficPorts, EventList), @@ -2218,69 +2217,69 @@ ports_traffic(MaxPorts) -> ports_traffic_stopped(MaxPorts, {PortList, PortCnt}) -> receive - start -> - %%io:format("Traffic started in ~p\n",[self()]), - ports_traffic_started(MaxPorts, {PortList, PortCnt}, []); - {Pid,die} -> - lists:foreach(fun(Port)-> erlang:port_close(Port) end, - PortList), - Pid ! {self(),dead} + start -> + %%io:format("Traffic started in ~p\n",[self()]), + ports_traffic_started(MaxPorts, {PortList, PortCnt}, []); + {Pid,die} -> + lists:foreach(fun(Port)-> erlang:port_close(Port) end, + PortList), + Pid ! {self(),dead} end. ports_traffic_started(MaxPorts, {PortList, PortCnt}, EventList) -> receive - {Pid, stop} -> - %%io:format("Traffic stopped in ~p\n",[self()]), - Pid ! {self(), EventList, PortList}, - ports_traffic_stopped(MaxPorts, {PortList, PortCnt}) + {Pid, stop} -> + %%io:format("Traffic stopped in ~p\n",[self()]), + Pid ! {self(), EventList, PortList}, + ports_traffic_stopped(MaxPorts, {PortList, PortCnt}) after 0 -> - ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) + ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) end. ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) -> N = uniform(MaxPorts), case N > PortCnt of - true -> % Open port - P = open_port({spawn, "exit_drv"}, []), - %%io:format("Created port ~p\n",[P]), - ports_traffic_started(MaxPorts, {[P|PortList], PortCnt+1}, - [{open,P}|EventList]); - - false -> % Close port - P = lists:nth(N, PortList), - %%io:format("Close port ~p\n",[P]), - true = erlang:port_close(P), - ports_traffic_started(MaxPorts, {lists:delete(P,PortList), PortCnt-1}, - [{close,P}|EventList]) + true -> % Open port + P = open_port({spawn, "exit_drv"}, []), + %%io:format("Created port ~p\n",[P]), + ports_traffic_started(MaxPorts, {[P|PortList], PortCnt+1}, + [{open,P}|EventList]); + + false -> % Close port + P = lists:nth(N, PortList), + %%io:format("Close port ~p\n",[P]), + true = erlang:port_close(P), + ports_traffic_started(MaxPorts, {lists:delete(P,PortList), PortCnt-1}, + [{close,P}|EventList]) end. ports_verify(Ports, PortsAfter, EventList) -> %%io:format("Candidate=~p\nEvents=~p\n", [PortsAfter, EventList]), case lists:sort(Ports) =:= lists:sort(PortsAfter) of - true -> - io:format("Snapshot of ~p ports verified ok.\n",[length(Ports)]), - ok; - false -> - %% Note that we track the event list "backwards", undoing open/close: - case EventList of - [{open,P} | Tail] -> - ports_verify(Ports, lists:delete(P,PortsAfter), Tail); - - [{close,P} | Tail] -> - ports_verify(Ports, [P | PortsAfter], Tail); - - [] -> - ct:fail("Inconsistent snapshot from erlang:ports()") - end + true -> + io:format("Snapshot of ~p ports verified ok.\n",[length(Ports)]), + ok; + false -> + %% Note that we track the event list "backwards", undoing open/close: + case EventList of + [{open,P} | Tail] -> + ports_verify(Ports, lists:delete(P,PortsAfter), Tail); + + [{close,P} | Tail] -> + ports_verify(Ports, [P | PortsAfter], Tail); + + [] -> + ct:fail("Inconsistent snapshot from erlang:ports()") + end end. load_driver(Dir, Driver) -> case erl_ddll:load_driver(Dir, Driver) of - ok -> ok; - {error, Error} = Res -> - io:format("~s\n", [erl_ddll:format_error(Error)]), - Res + ok -> ok; + {error, Error} = Res -> + io:format("~s\n", [erl_ddll:format_error(Error)]), + Res end. @@ -2304,13 +2303,13 @@ close_deaf_port_1(200, _) -> close_deaf_port_1(N, Cmd) -> Timeout = integer_to_list(rand:uniform(5*1000)), try open_port({spawn_executable,Cmd},[{args,[Timeout]}]) of - Port -> - erlang:port_command(Port,"Hello, can you hear me!?!?"), - port_close(Port), - close_deaf_port_1(N+1, Cmd) + Port -> + erlang:port_command(Port,"Hello, can you hear me!?!?"), + port_close(Port), + close_deaf_port_1(N+1, Cmd) catch - _:eagain -> - {comment, "Could not spawn more than " ++ integer_to_list(N) ++ " OS processes."} + _:eagain -> + {comment, "Could not spawn more than " ++ integer_to_list(N) ++ " OS processes."} end. %% Test undocumented port_set_data/2 and port_get_data/1 @@ -2322,57 +2321,57 @@ port_setget_data(Config) when is_list(Config) -> NSched = erlang:system_info(schedulers_online), HeapData = {1,2,3,<<"A heap binary">>,fun()->"This is fun"end, - list_to_binary(lists:seq(1,100))}, + list_to_binary(lists:seq(1,100))}, PRs = lists:map(fun(I) -> - spawn_opt(fun() -> port_setget_data_hammer(Port,HeapData,false,1) end, - [monitor, {scheduler, I rem NSched}]) - end, - lists:seq(1,10)), + spawn_opt(fun() -> port_setget_data_hammer(Port,HeapData,false,1) end, + [monitor, {scheduler, I rem NSched}]) + end, + lists:seq(1,10)), receive after 100 -> ok end, Papa = self(), lists:foreach(fun({Pid,_}) -> Pid ! {Papa,prepare_for_close} end, PRs), lists:foreach(fun({Pid,_}) -> - receive {Pid,prepare_for_close} -> ok end - end, - PRs), + receive {Pid,prepare_for_close} -> ok end + end, + PRs), port_close(Port), lists:foreach(fun({Pid,Ref}) -> - receive {'DOWN', Ref, process, Pid, normal} -> ok end - end, - PRs), + receive {'DOWN', Ref, process, Pid, normal} -> ok end + end, + PRs), ok. port_setget_data_hammer(Port, HeapData, IsSet0, N) -> Rand = rand:uniform(3), IsSet1 = try case Rand of - 1 -> true = erlang:port_set_data(Port, atom), true; - 2 -> true = erlang:port_set_data(Port, HeapData), true; - 3 -> case erlang:port_get_data(Port) of - atom -> true; - HeapData -> true; - undefined -> false=IsSet0 - end - end - catch - error:badarg -> - true = get(prepare_for_close), - io:format("~p did ~p rounds before port closed\n", [self(), N]), - exit(normal) - end, + 1 -> true = erlang:port_set_data(Port, atom), true; + 2 -> true = erlang:port_set_data(Port, HeapData), true; + 3 -> case erlang:port_get_data(Port) of + atom -> true; + HeapData -> true; + undefined -> false=IsSet0 + end + end + catch + error:badarg -> + true = get(prepare_for_close), + io:format("~p did ~p rounds before port closed\n", [self(), N]), + exit(normal) + end, receive {Papa, prepare_for_close} -> - put(prepare_for_close, true), - Papa ! {self(),prepare_for_close} + put(prepare_for_close, true), + Papa ! {self(),prepare_for_close} after 0 -> - ok + ok end, port_setget_data_hammer(Port, HeapData, IsSet1, N+1). wait_until(Fun) -> case catch Fun() of - true -> - ok; - _ -> - receive after 100 -> ok end, - wait_until(Fun) + true -> + ok; + _ -> + receive after 100 -> ok end, + wait_until(Fun) end. diff --git a/erts/emulator/test/ref_SUITE.erl b/erts/emulator/test/ref_SUITE.erl index e792410edf..287a2ffb73 100644 --- a/erts/emulator/test/ref_SUITE.erl +++ b/erts/emulator/test/ref_SUITE.erl @@ -36,13 +36,13 @@ all() -> %% Check that refs don't wrap around easily. wrap_1(Config) when is_list(Config) -> - ?line spawn_link(?MODULE, loop_ref, [self()]), - ?line receive - done -> - ct:fail(wrapfast) - after 30000 -> - ok - end, + spawn_link(?MODULE, loop_ref, [self()]), + receive + done -> + ct:fail(wrapfast) + after 30000 -> + ok + end, ok. loop_ref(Parent) -> diff --git a/erts/emulator/test/register_SUITE.erl b/erts/emulator/test/register_SUITE.erl index 68b36f99c3..ad8a9c29f3 100644 --- a/erts/emulator/test/register_SUITE.erl +++ b/erts/emulator/test/register_SUITE.erl @@ -56,25 +56,20 @@ otp_8099(Config) when is_list(Config) -> otp_8099_test(0) -> ok; otp_8099_test(N) -> - ?line P = spawn(fun () -> otp_8099_proc() end), - ?line case catch register(?OTP_8099_NAME, P) of + P = spawn(fun () -> otp_8099_proc() end), + case catch register(?OTP_8099_NAME, P) of true -> - ?line ok; + ok; _ -> - ?line OP = whereis(?OTP_8099_NAME), - ?line (catch unregister(?OTP_8099_NAME)), - ?line (catch exit(OP, kill)), - ?line true = (catch register(?OTP_8099_NAME, P)) + OP = whereis(?OTP_8099_NAME), + (catch unregister(?OTP_8099_NAME)), + (catch exit(OP, kill)), + true = (catch register(?OTP_8099_NAME, P)) end, - ?line P = whereis(?OTP_8099_NAME), - ?line exit(P, kill), - ?line otp_8099_test(N-1). + P = whereis(?OTP_8099_NAME), + exit(P, kill), + otp_8099_test(N-1). otp_8099_proc() -> receive _ -> ok end, otp_8099_proc(). - -%% -%% Utils -%% - diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl index 3dfd9a16a8..bbdc2e6688 100644 --- a/erts/emulator/test/save_calls_SUITE.erl +++ b/erts/emulator/test/save_calls_SUITE.erl @@ -22,10 +22,7 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0, - init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2]). +-export([all/0, suite/0, init_per_testcase/2,end_per_testcase/2]). -export([save_calls_1/1,dont_break_reductions/1]). @@ -36,36 +33,21 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [save_calls_1, dont_break_reductions]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - init_per_testcase(dont_break_reductions,Config) -> %% Skip on --enable-native-libs as hipe rescedules after each %% function call. case erlang:system_info(hipe_architecture) of - undefined -> - Config; - Architecture -> - {lists, ListsBinary, _ListsFilename} = code:get_object_code(lists), - ChunkName = hipe_unified_loader:chunk_name(Architecture), - NativeChunk = beam_lib:chunks(ListsBinary, [ChunkName]), - case NativeChunk of - {ok,{_,[{_,Bin}]}} when is_binary(Bin) -> - {skip,"Does not work for --enable-native-libs"}; - {error, beam_lib, _} -> Config - end + undefined -> + Config; + Architecture -> + {lists, ListsBinary, _ListsFilename} = code:get_object_code(lists), + ChunkName = hipe_unified_loader:chunk_name(Architecture), + NativeChunk = beam_lib:chunks(ListsBinary, [ChunkName]), + case NativeChunk of + {ok,{_,[{_,Bin}]}} when is_binary(Bin) -> + {skip,"Does not work for --enable-native-libs"}; + {error, beam_lib, _} -> Config + end end; init_per_testcase(_,Config) -> Config. @@ -75,86 +57,86 @@ end_per_testcase(_,_Config) -> %% Check that save_calls dont break reduction-based scheduling dont_break_reductions(Config) when is_list(Config) -> - ?line RPS1 = reds_per_sched(0), - ?line RPS2 = reds_per_sched(20), - ?line Diff = abs(RPS1 - RPS2), - ?line true = (Diff < (0.05 * RPS1)), + RPS1 = reds_per_sched(0), + RPS2 = reds_per_sched(20), + Diff = abs(RPS1 - RPS2), + true = (Diff < (0.05 * RPS1)), ok. reds_per_sched(SaveCalls) -> - ?line Parent = self(), - ?line HowMany = 10000, - ?line Pid = spawn(fun() -> - process_flag(save_calls,SaveCalls), - receive - go -> - carmichaels_below(HowMany), - Parent ! erlang:process_info(self(),reductions) - end - end), - ?line TH = spawn(fun() -> trace_handler(0,Parent,Pid) end), - ?line erlang:trace(Pid, true,[running,procs,{tracer,TH}]), - ?line Pid ! go, - ?line {Sched,Reds} = receive - {accumulated,X} -> - receive {reductions,Y} -> - {X,Y} - after 30000 -> - timeout - end - after 30000 -> - timeout - end, - ?line Reds div Sched. + Parent = self(), + HowMany = 10000, + Pid = spawn(fun() -> + process_flag(save_calls,SaveCalls), + receive + go -> + carmichaels_below(HowMany), + Parent ! erlang:process_info(self(),reductions) + end + end), + TH = spawn(fun() -> trace_handler(0,Parent,Pid) end), + erlang:trace(Pid, true,[running,procs,{tracer,TH}]), + Pid ! go, + {Sched,Reds} = receive + {accumulated,X} -> + receive {reductions,Y} -> + {X,Y} + after 30000 -> + timeout + end + after 30000 -> + timeout + end, + Reds div Sched. trace_handler(Acc,Parent,Client) -> receive - {trace,Client,out,_} -> - trace_handler(Acc+1,Parent,Client); - {trace,Client,exit,_} -> - Parent ! {accumulated, Acc}; - _ -> - trace_handler(Acc,Parent,Client) + {trace,Client,out,_} -> + trace_handler(Acc+1,Parent,Client); + {trace,Client,exit,_} -> + Parent ! {accumulated, Acc}; + _ -> + trace_handler(Acc,Parent,Client) after 10000 -> - ok + ok end. %% Test call saving. save_calls_1(Config) when is_list(Config) -> case test_server:is_native(?MODULE) of - true -> {skipped,"Native code"}; - false -> save_calls_1() + true -> {skipped,"Native code"}; + false -> save_calls_1() end. - + save_calls_1() -> - ?line erlang:process_flag(self(), save_calls, 0), - ?line {last_calls, false} = process_info(self(), last_calls), - - ?line erlang:process_flag(self(), save_calls, 10), - ?line {last_calls, _L1} = process_info(self(), last_calls), - ?line ?MODULE:do_bipp(), - ?line {last_calls, L2} = process_info(self(), last_calls), - ?line L21 = lists:filter(fun is_local_function/1, L2), - ?line case L21 of - [{?MODULE,do_bipp,0}, - timeout, - 'send', - {?MODULE,do_bopp,1}, - 'receive', - timeout, - {?MODULE,do_bepp,0}] -> - ok; - X -> - ct:fail({l21, X}) - end, - - ?line erlang:process_flag(self(), save_calls, 10), - ?line {last_calls, L3} = process_info(self(), last_calls), - ?line L31 = lists:filter(fun is_local_function/1, L3), - ?line [] = L31, + erlang:process_flag(self(), save_calls, 0), + {last_calls, false} = process_info(self(), last_calls), + + erlang:process_flag(self(), save_calls, 10), + {last_calls, _L1} = process_info(self(), last_calls), + ?MODULE:do_bipp(), + {last_calls, L2} = process_info(self(), last_calls), + L21 = lists:filter(fun is_local_function/1, L2), + case L21 of + [{?MODULE,do_bipp,0}, + timeout, + 'send', + {?MODULE,do_bopp,1}, + 'receive', + timeout, + {?MODULE,do_bepp,0}] -> + ok; + X -> + ct:fail({l21, X}) + end, + + erlang:process_flag(self(), save_calls, 10), + {last_calls, L3} = process_info(self(), last_calls), + L31 = lists:filter(fun is_local_function/1, L3), + [] = L31, ok. do_bipp() -> @@ -169,7 +151,7 @@ do_bapp() -> do_bopp(T) -> receive - X -> X + X -> X after T -> ok end. @@ -193,18 +175,18 @@ carmichaels_below(N,N2) when N >= N2 -> 0; carmichaels_below(N,N2) -> X = case fast_prime(N,10) of - false -> 0; - true -> - case fast_prime2(N,10) of - true -> - %io:format("Prime: ~p~n",[N]), - 0; - false -> - io:format("Carmichael: ~p (dividable by ~p)~n", - [N,smallest_divisor(N)]), - 1 - end - end, + false -> 0; + true -> + case fast_prime2(N,10) of + true -> + %io:format("Prime: ~p~n",[N]), + 0; + false -> + io:format("Carmichael: ~p (dividable by ~p)~n", + [N,smallest_divisor(N)]), + 1 + end + end, X+carmichaels_below(N+2,N2). expmod(_,E,_) when E == 0 -> @@ -228,30 +210,30 @@ do_fast_prime(_N,0) -> true; do_fast_prime(N,Times) -> case fermat(N) of - true -> - do_fast_prime(N,Times-1); - false -> - false + true -> + do_fast_prime(N,Times-1); + false -> + false end. - + fast_prime(N,T) -> do_fast_prime(N,T). expmod2(_,E,_) when E == 0 -> 1; expmod2(Base,Exp,Mod) when (Exp rem 2) == 0 -> -%% Uncomment the code below to simulate scheduling bug! -% case erlang:process_info(self(),last_calls) of -% {last_calls,false} -> ok; -% _ -> erlang:yield() -% end, + %% Uncomment the code below to simulate scheduling bug! + % case erlang:process_info(self(),last_calls) of + % {last_calls,false} -> ok; + % _ -> erlang:yield() + % end, X = expmod2(Base,Exp div 2,Mod), Y=(X*X) rem Mod, if - Y == 1, X =/= 1, X =/= (Mod - 1) -> - 0; - true -> - Y rem Mod + Y == 1, X =/= 1, X =/= (Mod - 1) -> + 0; + true -> + Y rem Mod end; expmod2(Base,Exp,Mod) -> (Base * expmod2(Base,Exp - 1,Mod)) rem Mod. @@ -266,12 +248,12 @@ do_fast_prime2(_N,0) -> true; do_fast_prime2(N,Times) -> case miller_rabbin(N) of - true -> - do_fast_prime2(N,Times-1); - false -> - false + true -> + do_fast_prime2(N,Times-1); + false -> + false end. - + fast_prime2(N,T) -> do_fast_prime2(N,T). @@ -280,17 +262,16 @@ smallest_divisor(N) -> find_divisor(N,TD) -> if - TD*TD > N -> - N; - true -> - case divides(TD,N) of - true -> - TD; - false -> - find_divisor(N,TD+1) - end + TD*TD > N -> + N; + true -> + case divides(TD,N) of + true -> + TD; + false -> + find_divisor(N,TD+1) + end end. divides(A,B) -> (B rem A) == 0. - diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 31cc37ad4d..64c280b198 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -123,130 +123,130 @@ many_low(Config) when is_list(Config) -> low_normal_test(Config, 2*active_schedulers(), 1000). low_normal_test(Config, NW, LW) -> - ?line Tracer = start_tracer(), - ?line Low = workers(LW, low), - ?line Normal = workers(NW, normal), - ?line Res = do_it(Tracer, Low, Normal, [], []), - ?line chk_result(Res, LW, NW, 0, 0, true, false, false), - ?line workers_exit([Low, Normal]), - ?line ok(Res, Config). + Tracer = start_tracer(), + Low = workers(LW, low), + Normal = workers(NW, normal), + Res = do_it(Tracer, Low, Normal, [], []), + chk_result(Res, LW, NW, 0, 0, true, false, false), + workers_exit([Low, Normal]), + ok(Res, Config). equal_with_part_time_high(Config) when is_list(Config) -> - ?line NW = 500, - ?line LW = 500, - ?line HW = 1, - ?line Tracer = start_tracer(), - ?line Normal = workers(NW, normal), - ?line Low = workers(LW, low), - ?line High = part_time_workers(HW, high), - ?line Res = do_it(Tracer, Low, Normal, High, []), - ?line chk_result(Res, LW, NW, HW, 0, true, true, false), - ?line workers_exit([Low, Normal, High]), - ?line ok(Res, Config). + NW = 500, + LW = 500, + HW = 1, + Tracer = start_tracer(), + Normal = workers(NW, normal), + Low = workers(LW, low), + High = part_time_workers(HW, high), + Res = do_it(Tracer, Low, Normal, High, []), + chk_result(Res, LW, NW, HW, 0, true, true, false), + workers_exit([Low, Normal, High]), + ok(Res, Config). equal_and_high_with_part_time_max(Config) when is_list(Config) -> - ?line NW = 500, - ?line LW = 500, - ?line HW = 500, - ?line MW = 1, - ?line Tracer = start_tracer(), - ?line Low = workers(LW, low), - ?line Normal = workers(NW, normal), - ?line High = workers(HW, high), - ?line Max = part_time_workers(MW, max), - ?line Res = do_it(Tracer, Low, Normal, High, Max), - ?line chk_result(Res, LW, NW, HW, MW, false, true, true), - ?line workers_exit([Low, Normal, Max]), - ?line ok(Res, Config). + NW = 500, + LW = 500, + HW = 500, + MW = 1, + Tracer = start_tracer(), + Low = workers(LW, low), + Normal = workers(NW, normal), + High = workers(HW, high), + Max = part_time_workers(MW, max), + Res = do_it(Tracer, Low, Normal, High, Max), + chk_result(Res, LW, NW, HW, MW, false, true, true), + workers_exit([Low, Normal, Max]), + ok(Res, Config). equal_with_part_time_max(Config) when is_list(Config) -> - ?line NW = 500, - ?line LW = 500, - ?line MW = 1, - ?line Tracer = start_tracer(), - ?line Low = workers(LW, low), - ?line Normal = workers(NW, normal), - ?line Max = part_time_workers(MW, max), - ?line Res = do_it(Tracer, Low, Normal, [], Max), - ?line chk_result(Res, LW, NW, 0, MW, true, false, true), - ?line workers_exit([Low, Normal, Max]), - ?line ok(Res, Config). + NW = 500, + LW = 500, + MW = 1, + Tracer = start_tracer(), + Low = workers(LW, low), + Normal = workers(NW, normal), + Max = part_time_workers(MW, max), + Res = do_it(Tracer, Low, Normal, [], Max), + chk_result(Res, LW, NW, 0, MW, true, false, true), + workers_exit([Low, Normal, Max]), + ok(Res, Config). equal_with_high(Config) when is_list(Config) -> - ?line NW = 500, - ?line LW = 500, - ?line HW = 1, - ?line Tracer = start_tracer(), - ?line Low = workers(LW, low), - ?line Normal = workers(NW, normal), - ?line High = workers(HW, high), - ?line Res = do_it(Tracer, Low, Normal, High, []), - ?line LNExe = case active_schedulers() of + NW = 500, + LW = 500, + HW = 1, + Tracer = start_tracer(), + Low = workers(LW, low), + Normal = workers(NW, normal), + High = workers(HW, high), + Res = do_it(Tracer, Low, Normal, High, []), + LNExe = case active_schedulers() of S when S =< HW -> false; _ -> true end, - ?line chk_result(Res, LW, NW, HW, 0, LNExe, true, false), - ?line workers_exit([Low, Normal, High]), - ?line ok(Res, Config). + chk_result(Res, LW, NW, HW, 0, LNExe, true, false), + workers_exit([Low, Normal, High]), + ok(Res, Config). equal_with_high_max(Config) when is_list(Config) -> - ?line NW = 500, - ?line LW = 500, - ?line HW = 1, - ?line MW = 1, - ?line Tracer = start_tracer(), - ?line Normal = workers(NW, normal), - ?line Low = workers(LW, low), - ?line High = workers(HW, high), - ?line Max = workers(MW, max), - ?line Res = do_it(Tracer, Low, Normal, High, Max), - ?line {LNExe, HExe} = case active_schedulers() of + NW = 500, + LW = 500, + HW = 1, + MW = 1, + Tracer = start_tracer(), + Normal = workers(NW, normal), + Low = workers(LW, low), + High = workers(HW, high), + Max = workers(MW, max), + Res = do_it(Tracer, Low, Normal, High, Max), + {LNExe, HExe} = case active_schedulers() of S when S =< MW -> {false, false}; S when S =< (MW + HW) -> {false, true}; _ -> {true, true} end, - ?line chk_result(Res, LW, NW, HW, MW, LNExe, HExe, true), - ?line workers_exit([Low, Normal, Max]), - ?line ok(Res, Config). + chk_result(Res, LW, NW, HW, MW, LNExe, HExe, true), + workers_exit([Low, Normal, Max]), + ok(Res, Config). bound_process(Config) when is_list(Config) -> case erlang:system_info(run_queues) == erlang:system_info(schedulers) of - true -> - ?line NStartBase = 20000, - ?line NStart = case {erlang:system_info(debug_compiled), - erlang:system_info(lock_checking)} of - {true, true} -> NStartBase div 100; - {_, true} -> NStartBase div 10; - _ -> NStartBase - end, - ?line MStart = 100, - ?line Seq = lists:seq(1, 100), - ?line Tester = self(), - ?line Procs = lists:map( - fun (N) when N rem 2 == 0 -> - spawn_opt(fun () -> - bound_loop(NStart, - NStart, - MStart, - 1), - Tester ! {self(), done} - end, - [{scheduler, 1}, link]); - (_N) -> - spawn_link(fun () -> - bound_loop(NStart, - NStart, - MStart, - false), - Tester ! {self(), done} - end) - end, - Seq), - ?line lists:foreach(fun (P) -> receive {P, done} -> ok end end, - Procs), - ?line ok; - false -> - {skipped, "Functionality not supported"} + true -> + NStartBase = 20000, + NStart = case {erlang:system_info(debug_compiled), + erlang:system_info(lock_checking)} of + {true, true} -> NStartBase div 100; + {_, true} -> NStartBase div 10; + _ -> NStartBase + end, + MStart = 100, + Seq = lists:seq(1, 100), + Tester = self(), + Procs = lists:map( + fun (N) when N rem 2 == 0 -> + spawn_opt(fun () -> + bound_loop(NStart, + NStart, + MStart, + 1), + Tester ! {self(), done} + end, + [{scheduler, 1}, link]); + (_N) -> + spawn_link(fun () -> + bound_loop(NStart, + NStart, + MStart, + false), + Tester ! {self(), done} + end) + end, + Seq), + lists:foreach(fun (P) -> receive {P, done} -> ok end end, + Procs), + ok; + false -> + {skipped, "Functionality not supported"} end. bound_loop(_, 0, 0, _) -> @@ -480,59 +480,59 @@ bound_loop(NS, N, M, Sched) -> ":L30-31t0-1c15n3p0"). -define(TOPOLOGY_F_TERM, - [{processor,[{node,[{core,[{thread,{logical,0}}, - {thread,{logical,1}}]}, - {core,[{thread,{logical,2}}, - {thread,{logical,3}}]}, - {core,[{thread,{logical,4}}, - {thread,{logical,5}}]}, - {core,[{thread,{logical,6}}, - {thread,{logical,7}}]}]}, - {node,[{core,[{thread,{logical,8}}, - {thread,{logical,9}}]}, - {core,[{thread,{logical,10}}, - {thread,{logical,11}}]}, - {core,[{thread,{logical,12}}, - {thread,{logical,13}}]}, - {core,[{thread,{logical,14}}, - {thread,{logical,15}}]}]}, - {node,[{core,[{thread,{logical,16}}, - {thread,{logical,17}}]}, - {core,[{thread,{logical,18}}, - {thread,{logical,19}}]}, - {core,[{thread,{logical,20}}, - {thread,{logical,21}}]}, - {core,[{thread,{logical,22}}, - {thread,{logical,23}}]}]}, - {node,[{core,[{thread,{logical,24}}, - {thread,{logical,25}}]}, - {core,[{thread,{logical,26}}, - {thread,{logical,27}}]}, - {core,[{thread,{logical,28}}, - {thread,{logical,29}}]}, - {core,[{thread,{logical,30}}, - {thread,{logical,31}}]}]}]}]). + [{processor,[{node,[{core,[{thread,{logical,0}}, + {thread,{logical,1}}]}, + {core,[{thread,{logical,2}}, + {thread,{logical,3}}]}, + {core,[{thread,{logical,4}}, + {thread,{logical,5}}]}, + {core,[{thread,{logical,6}}, + {thread,{logical,7}}]}]}, + {node,[{core,[{thread,{logical,8}}, + {thread,{logical,9}}]}, + {core,[{thread,{logical,10}}, + {thread,{logical,11}}]}, + {core,[{thread,{logical,12}}, + {thread,{logical,13}}]}, + {core,[{thread,{logical,14}}, + {thread,{logical,15}}]}]}, + {node,[{core,[{thread,{logical,16}}, + {thread,{logical,17}}]}, + {core,[{thread,{logical,18}}, + {thread,{logical,19}}]}, + {core,[{thread,{logical,20}}, + {thread,{logical,21}}]}, + {core,[{thread,{logical,22}}, + {thread,{logical,23}}]}]}, + {node,[{core,[{thread,{logical,24}}, + {thread,{logical,25}}]}, + {core,[{thread,{logical,26}}, + {thread,{logical,27}}]}, + {core,[{thread,{logical,28}}, + {thread,{logical,29}}]}, + {core,[{thread,{logical,30}}, + {thread,{logical,31}}]}]}]}]). bindings(Node, BindType) -> Parent = self(), Ref = make_ref(), Pid = spawn_link(Node, - fun () -> - enable_internal_state(), - Res = (catch erts_debug:get_internal_state( - {fake_scheduler_bindings, - BindType})), - Parent ! {Ref, Res} - end), + fun () -> + enable_internal_state(), + Res = (catch erts_debug:get_internal_state( + {fake_scheduler_bindings, + BindType})), + Parent ! {Ref, Res} + end), receive - {Ref, Res} -> - io:format("~p: ~p~n", [BindType, Res]), - unlink(Pid), - Res + {Ref, Res} -> + io:format("~p: ~p~n", [BindType, Res]), + unlink(Pid), + Res end. scheduler_bind_types(Config) when is_list(Config) -> - ?line OldRelFlags = clear_erl_rel_flags(), + OldRelFlags = clear_erl_rel_flags(), try scheduler_bind_types_test(Config, ?TOPOLOGY_A_TERM, @@ -561,267 +561,267 @@ scheduler_bind_types(Config) when is_list(Config) -> after restore_erl_rel_flags(OldRelFlags) end, - ?line ok. + ok. scheduler_bind_types_test(Config, Topology, CmdLine, TermLetter) -> - ?line io:format("Testing (~p): ~p~n", [TermLetter, Topology]), - ?line {ok, Node0} = start_node(Config), - ?line _ = rpc:call(Node0, erlang, system_flag, [cpu_topology, Topology]), - ?line cmp(Topology, rpc:call(Node0, erlang, system_info, [cpu_topology])), - ?line check_bind_types(Node0, TermLetter), - ?line stop_node(Node0), - ?line {ok, Node1} = start_node(Config, CmdLine), - ?line cmp(Topology, rpc:call(Node1, erlang, system_info, [cpu_topology])), - ?line check_bind_types(Node1, TermLetter), - ?line stop_node(Node1). + io:format("Testing (~p): ~p~n", [TermLetter, Topology]), + {ok, Node0} = start_node(Config), + _ = rpc:call(Node0, erlang, system_flag, [cpu_topology, Topology]), + cmp(Topology, rpc:call(Node0, erlang, system_info, [cpu_topology])), + check_bind_types(Node0, TermLetter), + stop_node(Node0), + {ok, Node1} = start_node(Config, CmdLine), + cmp(Topology, rpc:call(Node1, erlang, system_info, [cpu_topology])), + check_bind_types(Node1, TermLetter), + stop_node(Node1). check_bind_types(Node, a) -> - ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} - = bindings(Node, no_spread), - ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} - = bindings(Node, thread_spread), - ?line {0,4,8,12,2,6,10,14,1,5,9,13,3,7,11,15} - = bindings(Node, processor_spread), - ?line {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15} - = bindings(Node, spread), - ?line {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15} - = bindings(Node, no_node_thread_spread), - ?line {0,4,2,6,1,5,3,7,8,12,10,14,9,13,11,15} - = bindings(Node, no_node_processor_spread), - ?line {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15} - = bindings(Node, thread_no_node_processor_spread), - ?line {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15} - = bindings(Node, default_bind), - ?line ok; + {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} + = bindings(Node, no_spread), + {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} + = bindings(Node, thread_spread), + {0,4,8,12,2,6,10,14,1,5,9,13,3,7,11,15} + = bindings(Node, processor_spread), + {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15} + = bindings(Node, spread), + {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15} + = bindings(Node, no_node_thread_spread), + {0,4,2,6,1,5,3,7,8,12,10,14,9,13,11,15} + = bindings(Node, no_node_processor_spread), + {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15} + = bindings(Node, thread_no_node_processor_spread), + {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15} + = bindings(Node, default_bind), + ok; check_bind_types(Node, b) -> - ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} - = bindings(Node, no_spread), - ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} - = bindings(Node, thread_spread), - ?line {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15} - = bindings(Node, processor_spread), - ?line {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15} - = bindings(Node, spread), - ?line {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15} - = bindings(Node, no_node_thread_spread), - ?line {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15} - = bindings(Node, no_node_processor_spread), - ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} - = bindings(Node, thread_no_node_processor_spread), - ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} - = bindings(Node, default_bind), - ?line ok; + {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} + = bindings(Node, no_spread), + {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} + = bindings(Node, thread_spread), + {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15} + = bindings(Node, processor_spread), + {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15} + = bindings(Node, spread), + {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15} + = bindings(Node, no_node_thread_spread), + {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15} + = bindings(Node, no_node_processor_spread), + {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} + = bindings(Node, thread_no_node_processor_spread), + {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} + = bindings(Node, default_bind), + ok; check_bind_types(Node, c) -> - ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, - 25,26,27,28,29,30,31} = bindings(Node, no_spread), - ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15, - 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread), - ?line {0,4,8,16,20,24,2,6,10,18,22,26,12,28,14,30,1,5,9,17,21,25, - 3,7,11,19,23,27,13,29,15,31} = bindings(Node, processor_spread), - ?line {0,8,16,24,4,20,12,28,2,10,18,26,6,22,14,30,1,9,17,25,5,21,13,29,3,11, - 19,27,7,23,15,31} = bindings(Node, spread), - ?line {0,2,4,6,1,3,5,7,8,10,9,11,12,14,13,15,16,18,20,22,17,19,21,23,24,26, - 25,27,28,30,29,31} = bindings(Node, no_node_thread_spread), - ?line {0,4,2,6,1,5,3,7,8,10,9,11,12,14,13,15,16,20,18,22,17,21,19,23,24,26, - 25,27,28,30,29,31} = bindings(Node, no_node_processor_spread), - ?line {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21, - 19,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread), - ?line {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21, - 19,23,25,27,29,31} = bindings(Node, default_bind), - ?line ok; + {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, + 25,26,27,28,29,30,31} = bindings(Node, no_spread), + {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15, + 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread), + {0,4,8,16,20,24,2,6,10,18,22,26,12,28,14,30,1,5,9,17,21,25, + 3,7,11,19,23,27,13,29,15,31} = bindings(Node, processor_spread), + {0,8,16,24,4,20,12,28,2,10,18,26,6,22,14,30,1,9,17,25,5,21,13,29,3,11, + 19,27,7,23,15,31} = bindings(Node, spread), + {0,2,4,6,1,3,5,7,8,10,9,11,12,14,13,15,16,18,20,22,17,19,21,23,24,26, + 25,27,28,30,29,31} = bindings(Node, no_node_thread_spread), + {0,4,2,6,1,5,3,7,8,10,9,11,12,14,13,15,16,20,18,22,17,21,19,23,24,26, + 25,27,28,30,29,31} = bindings(Node, no_node_processor_spread), + {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21, + 19,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread), + {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21, + 19,23,25,27,29,31} = bindings(Node, default_bind), + ok; check_bind_types(Node, d) -> - ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, - 25,26,27,28,29,30,31} = bindings(Node, no_spread), - ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15, - 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread), - ?line {0,8,12,16,24,28,2,10,14,18,26,30,4,20,6,22,1,9,13,17,25,29,3,11,15, - 19,27,31,5,21,7,23} = bindings(Node, processor_spread), - ?line {0,8,16,24,12,28,4,20,2,10,18,26,14,30,6,22,1,9,17,25,13,29,5,21,3,11, - 19,27,15,31,7,23} = bindings(Node, spread), - ?line {0,2,1,3,4,6,5,7,8,10,12,14,9,11,13,15,16,18,17,19,20,22,21,23,24,26, - 28,30,25,27,29,31} = bindings(Node, no_node_thread_spread), - ?line {0,2,1,3,4,6,5,7,8,12,10,14,9,13,11,15,16,18,17,19,20,22,21,23,24,28, - 26,30,25,29,27,31} = bindings(Node, no_node_processor_spread), - ?line {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19, - 21,23,25,29,27,31} = bindings(Node, thread_no_node_processor_spread), - ?line {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19, - 21,23,25,29,27,31} = bindings(Node, default_bind), - ?line ok; + {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, + 25,26,27,28,29,30,31} = bindings(Node, no_spread), + {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15, + 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread), + {0,8,12,16,24,28,2,10,14,18,26,30,4,20,6,22,1,9,13,17,25,29,3,11,15, + 19,27,31,5,21,7,23} = bindings(Node, processor_spread), + {0,8,16,24,12,28,4,20,2,10,18,26,14,30,6,22,1,9,17,25,13,29,5,21,3,11, + 19,27,15,31,7,23} = bindings(Node, spread), + {0,2,1,3,4,6,5,7,8,10,12,14,9,11,13,15,16,18,17,19,20,22,21,23,24,26, + 28,30,25,27,29,31} = bindings(Node, no_node_thread_spread), + {0,2,1,3,4,6,5,7,8,12,10,14,9,13,11,15,16,18,17,19,20,22,21,23,24,28, + 26,30,25,29,27,31} = bindings(Node, no_node_processor_spread), + {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19, + 21,23,25,29,27,31} = bindings(Node, thread_no_node_processor_spread), + {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19, + 21,23,25,29,27,31} = bindings(Node, default_bind), + ok; check_bind_types(Node, e) -> - ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} - = bindings(Node, no_spread), - ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} - = bindings(Node, thread_spread), - ?line {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15} - = bindings(Node, processor_spread), - ?line {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15} - = bindings(Node, spread), - ?line {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15} - = bindings(Node, no_node_thread_spread), - ?line {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15} - = bindings(Node, no_node_processor_spread), - ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} - = bindings(Node, thread_no_node_processor_spread), - ?line {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} - = bindings(Node, default_bind), - ?line ok; + {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} + = bindings(Node, no_spread), + {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} + = bindings(Node, thread_spread), + {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15} + = bindings(Node, processor_spread), + {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15} + = bindings(Node, spread), + {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15} + = bindings(Node, no_node_thread_spread), + {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15} + = bindings(Node, no_node_processor_spread), + {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} + = bindings(Node, thread_no_node_processor_spread), + {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15} + = bindings(Node, default_bind), + ok; check_bind_types(Node, f) -> - ?line {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, - 25,26,27,28,29,30,31} = bindings(Node, no_spread), - ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15, - 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread), - ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13, - 15,17,19,21,23,25,27,29,31} = bindings(Node, processor_spread), - ?line {0,8,16,24,2,10,18,26,4,12,20,28,6,14,22,30,1,9,17,25,3,11,19,27,5,13, - 21,29,7,15,23,31} = bindings(Node, spread), - ?line {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26, - 28,30,25,27,29,31} = bindings(Node, no_node_thread_spread), - ?line {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26, - 28,30,25,27,29,31} = bindings(Node, no_node_processor_spread), - ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19, - 21,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread), - ?line {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19, - 21,23,25,27,29,31} = bindings(Node, default_bind), - ?line ok; + {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, + 25,26,27,28,29,30,31} = bindings(Node, no_spread), + {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15, + 17,19,21,23,25,27,29,31} = bindings(Node, thread_spread), + {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13, + 15,17,19,21,23,25,27,29,31} = bindings(Node, processor_spread), + {0,8,16,24,2,10,18,26,4,12,20,28,6,14,22,30,1,9,17,25,3,11,19,27,5,13, + 21,29,7,15,23,31} = bindings(Node, spread), + {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26, + 28,30,25,27,29,31} = bindings(Node, no_node_thread_spread), + {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26, + 28,30,25,27,29,31} = bindings(Node, no_node_processor_spread), + {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19, + 21,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread), + {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19, + 21,23,25,27,29,31} = bindings(Node, default_bind), + ok; check_bind_types(Node, _) -> - ?line bindings(Node, no_spread), - ?line bindings(Node, thread_spread), - ?line bindings(Node, processor_spread), - ?line bindings(Node, spread), - ?line bindings(Node, no_node_thread_spread), - ?line bindings(Node, no_node_processor_spread), - ?line bindings(Node, thread_no_node_processor_spread), - ?line bindings(Node, default_bind), - ?line ok. + bindings(Node, no_spread), + bindings(Node, thread_spread), + bindings(Node, processor_spread), + bindings(Node, spread), + bindings(Node, no_node_thread_spread), + bindings(Node, no_node_processor_spread), + bindings(Node, thread_no_node_processor_spread), + bindings(Node, default_bind), + ok. cpu_topology(Config) when is_list(Config) -> - ?line OldRelFlags = clear_erl_rel_flags(), + OldRelFlags = clear_erl_rel_flags(), try - ?line cpu_topology_test( - Config, - [{node,[{processor,[{core,{logical,0}}, - {core,{logical,1}}]}]}, - {processor,[{node,[{core,{logical,2}}, - {core,{logical,3}}]}]}, - {node,[{processor,[{core,{logical,4}}, - {core,{logical,5}}]}]}, - {processor,[{node,[{core,{logical,6}}, - {core,{logical,7}}]}]}], - "+sct " - "L0-1c0-1p0n0" - ":L2-3c0-1n1p1" - ":L4-5c0-1p2n2" - ":L6-7c0-1n3p3"), - ?line cpu_topology_test( - Config, - [{node,[{processor,[{core,{logical,0}}, - {core,{logical,1}}]}, - {processor,[{core,{logical,2}}, - {core,{logical,3}}]}]}, - {processor,[{node,[{core,{logical,4}}, - {core,{logical,5}}]}, - {node,[{core,{logical,6}}, - {core,{logical,7}}]}]}, - {node,[{processor,[{core,{logical,8}}, - {core,{logical,9}}]}, - {processor,[{core,{logical,10}}, - {core,{logical,11}}]}]}, - {processor,[{node,[{core,{logical,12}}, - {core,{logical,13}}]}, - {node,[{core,{logical,14}}, - {core,{logical,15}}]}]}], - "+sct " - "L0-1c0-1p0n0" - ":L2-3c0-1p1n0" - ":L4-5c0-1n1p2" - ":L6-7c2-3n2p2" - ":L8-9c0-1p3n3" - ":L10-11c0-1p4n3" - ":L12-13c0-1n4p5" - ":L14-15c2-3n5p5"), - ?line cpu_topology_test( - Config, - [{node,[{processor,[{core,{logical,0}}, - {core,{logical,1}}]}]}, - {processor,[{node,[{core,{logical,2}}, - {core,{logical,3}}]}]}, - {processor,[{node,[{core,{logical,4}}, - {core,{logical,5}}]}]}, - {node,[{processor,[{core,{logical,6}}, - {core,{logical,7}}]}]}, - {node,[{processor,[{core,{logical,8}}, - {core,{logical,9}}]}]}, - {processor,[{node,[{core,{logical,10}}, - {core,{logical,11}}]}]}], - "+sct " - "L0-1c0-1p0n0" - ":L2-3c0-1n1p1" - ":L4-5c0-1n2p2" - ":L6-7c0-1p3n3" - ":L8-9c0-1p4n4" - ":L10-11c0-1n5p5") + cpu_topology_test( + Config, + [{node,[{processor,[{core,{logical,0}}, + {core,{logical,1}}]}]}, + {processor,[{node,[{core,{logical,2}}, + {core,{logical,3}}]}]}, + {node,[{processor,[{core,{logical,4}}, + {core,{logical,5}}]}]}, + {processor,[{node,[{core,{logical,6}}, + {core,{logical,7}}]}]}], + "+sct " + "L0-1c0-1p0n0" + ":L2-3c0-1n1p1" + ":L4-5c0-1p2n2" + ":L6-7c0-1n3p3"), + cpu_topology_test( + Config, + [{node,[{processor,[{core,{logical,0}}, + {core,{logical,1}}]}, + {processor,[{core,{logical,2}}, + {core,{logical,3}}]}]}, + {processor,[{node,[{core,{logical,4}}, + {core,{logical,5}}]}, + {node,[{core,{logical,6}}, + {core,{logical,7}}]}]}, + {node,[{processor,[{core,{logical,8}}, + {core,{logical,9}}]}, + {processor,[{core,{logical,10}}, + {core,{logical,11}}]}]}, + {processor,[{node,[{core,{logical,12}}, + {core,{logical,13}}]}, + {node,[{core,{logical,14}}, + {core,{logical,15}}]}]}], + "+sct " + "L0-1c0-1p0n0" + ":L2-3c0-1p1n0" + ":L4-5c0-1n1p2" + ":L6-7c2-3n2p2" + ":L8-9c0-1p3n3" + ":L10-11c0-1p4n3" + ":L12-13c0-1n4p5" + ":L14-15c2-3n5p5"), + cpu_topology_test( + Config, + [{node,[{processor,[{core,{logical,0}}, + {core,{logical,1}}]}]}, + {processor,[{node,[{core,{logical,2}}, + {core,{logical,3}}]}]}, + {processor,[{node,[{core,{logical,4}}, + {core,{logical,5}}]}]}, + {node,[{processor,[{core,{logical,6}}, + {core,{logical,7}}]}]}, + {node,[{processor,[{core,{logical,8}}, + {core,{logical,9}}]}]}, + {processor,[{node,[{core,{logical,10}}, + {core,{logical,11}}]}]}], + "+sct " + "L0-1c0-1p0n0" + ":L2-3c0-1n1p1" + ":L4-5c0-1n2p2" + ":L6-7c0-1p3n3" + ":L8-9c0-1p4n4" + ":L10-11c0-1n5p5") after - restore_erl_rel_flags(OldRelFlags) + restore_erl_rel_flags(OldRelFlags) end, - ?line ok. + ok. cpu_topology_test(Config, Topology, Cmd) -> - ?line io:format("Testing~n ~p~n ~p~n", [Topology, Cmd]), - ?line cpu_topology_bif_test(Config, Topology), - ?line cpu_topology_cmdline_test(Config, Topology, Cmd), - ?line ok. + io:format("Testing~n ~p~n ~p~n", [Topology, Cmd]), + cpu_topology_bif_test(Config, Topology), + cpu_topology_cmdline_test(Config, Topology, Cmd), + ok. cpu_topology_bif_test(_Config, false) -> - ?line ok; + ok; cpu_topology_bif_test(Config, Topology) -> - ?line {ok, Node} = start_node(Config), - ?line _ = rpc:call(Node, erlang, system_flag, [cpu_topology, Topology]), - ?line cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])), - ?line stop_node(Node), - ?line ok. + {ok, Node} = start_node(Config), + _ = rpc:call(Node, erlang, system_flag, [cpu_topology, Topology]), + cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])), + stop_node(Node), + ok. cpu_topology_cmdline_test(_Config, _Topology, false) -> - ?line ok; + ok; cpu_topology_cmdline_test(Config, Topology, Cmd) -> - ?line {ok, Node} = start_node(Config, Cmd), - ?line cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])), - ?line stop_node(Node), - ?line ok. + {ok, Node} = start_node(Config, Cmd), + cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])), + stop_node(Node), + ok. update_cpu_info(Config) when is_list(Config) -> - ?line OldOnline = erlang:system_info(schedulers_online), - ?line OldAff = get_affinity_mask(), - ?line io:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", + OldOnline = erlang:system_info(schedulers_online), + OldAff = get_affinity_mask(), + io:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", [OldAff, OldOnline, erlang:system_info(scheduler_bindings)]), - ?line case {erlang:system_info(logical_processors_available), OldAff} of + case {erlang:system_info(logical_processors_available), OldAff} of {Avail, _} when Avail == unknown; OldAff == unknown -> %% Nothing much to test; just a smoke test case erlang:system_info(update_cpu_info) of - unchanged -> ?line ok; - changed -> ?line ok + unchanged -> ok; + changed -> ok end; _ -> try - ?line adjust_schedulers_online(), + adjust_schedulers_online(), case erlang:system_info(schedulers_online) of 1 -> %% Nothing much to test; just a smoke test - ?line ok; + ok; Onln0 -> %% unset least significant bit - ?line Aff = (OldAff band (OldAff - 1)), - ?line set_affinity_mask(Aff), - ?line Onln1 = Onln0 - 1, - ?line case adjust_schedulers_online() of + Aff = (OldAff band (OldAff - 1)), + set_affinity_mask(Aff), + Onln1 = Onln0 - 1, + case adjust_schedulers_online() of {Onln0, Onln1} -> - ?line Onln1 = erlang:system_info(schedulers_online), - ?line receive after 500 -> ok end, - ?line io:format("TEST - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", + Onln1 = erlang:system_info(schedulers_online), + receive after 500 -> ok end, + io:format("TEST - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", [Aff, Onln1, erlang:system_info(scheduler_bindings)]), - ?line unchanged = adjust_schedulers_online(), - ?line ok; + unchanged = adjust_schedulers_online(), + ok; Fail -> - ?line ct:fail(Fail) + ct:fail(Fail) end end after @@ -920,21 +920,21 @@ set_affinity_mask(Mask) -> end. sct_cmd(Config) when is_list(Config) -> - ?line Topology = ?TOPOLOGY_A_TERM, - ?line OldRelFlags = clear_erl_rel_flags(), + Topology = ?TOPOLOGY_A_TERM, + OldRelFlags = clear_erl_rel_flags(), try - ?line {ok, Node} = start_node(Config, ?TOPOLOGY_A_CMD), - ?line cmp(Topology, + {ok, Node} = start_node(Config, ?TOPOLOGY_A_CMD), + cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])), - ?line cmp(Topology, + cmp(Topology, rpc:call(Node, erlang, system_flag, [cpu_topology, Topology])), - ?line cmp(Topology, + cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])), - ?line stop_node(Node) + stop_node(Node) after restore_erl_rel_flags(OldRelFlags) end, - ?line ok. + ok. -define(BIND_TYPES, [{"u", unbound}, @@ -958,7 +958,7 @@ sbt_cmd(Config) when is_list(Config) -> end, case Bind of notsup -> - ?line {skipped, "Binding of schedulers not supported"}; + {skipped, "Binding of schedulers not supported"}; go_for_it -> CpuTCmd = case erlang:system_info({cpu_topology,detected}) of undefined -> @@ -981,14 +981,14 @@ sbt_cmd(Config) when is_list(Config) -> end, case CpuTCmd of false -> - ?line {skipped, "Don't know how to create cpu topology"}; + {skipped, "Don't know how to create cpu topology"}; _ -> case erlang:system_info(logical_processors) of LP when is_integer(LP) -> OldRelFlags = clear_erl_rel_flags(), try lists:foreach(fun ({ClBt, Bt}) -> - ?line sbt_test(Config, + sbt_test(Config, CpuTCmd, ClBt, Bt, @@ -998,44 +998,44 @@ sbt_cmd(Config) when is_list(Config) -> after restore_erl_rel_flags(OldRelFlags) end, - ?line ok; + ok; _ -> - ?line {skipped, + {skipped, "Don't know the amount of logical processors"} end end end. sbt_test(Config, CpuTCmd, ClBt, Bt, LP) -> - ?line io:format("Testing +sbt ~s (~p)~n", [ClBt, Bt]), - ?line LPS = integer_to_list(LP), - ?line Cmd = CpuTCmd++" +sbt "++ClBt++" +S"++LPS++":"++LPS, - ?line {ok, Node} = start_node(Config, Cmd), - ?line Bt = rpc:call(Node, + io:format("Testing +sbt ~s (~p)~n", [ClBt, Bt]), + LPS = integer_to_list(LP), + Cmd = CpuTCmd++" +sbt "++ClBt++" +S"++LPS++":"++LPS, + {ok, Node} = start_node(Config, Cmd), + Bt = rpc:call(Node, erlang, system_info, [scheduler_bind_type]), - ?line SB = rpc:call(Node, + SB = rpc:call(Node, erlang, system_info, [scheduler_bindings]), - ?line io:format("scheduler bindings: ~p~n", [SB]), - ?line BS = case {Bt, erlang:system_info(logical_processors_available)} of + io:format("scheduler bindings: ~p~n", [SB]), + BS = case {Bt, erlang:system_info(logical_processors_available)} of {unbound, _} -> 0; {_, Int} when is_integer(Int) -> Int; {_, _} -> LP end, - ?line lists:foldl(fun (S, 0) -> - ?line unbound = S, + lists:foldl(fun (S, 0) -> + unbound = S, 0; (S, N) -> - ?line true = is_integer(S), + true = is_integer(S), N-1 end, BS, tuple_to_list(SB)), - ?line stop_node(Node), - ?line ok. + stop_node(Node), + ok. scheduler_threads(Config) when is_list(Config) -> SmpSupport = erlang:system_info(smp_support), @@ -1318,51 +1318,52 @@ scheduler_suspend_basic_test() -> scheduler_suspend(Config) when is_list(Config) -> ct:timetrap({minutes, 5}), - ?line lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end, + lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end, [64, 32, 16, default]), - ?line ok. + ok. scheduler_suspend_test(Config, Schedulers) -> - ?line Cmd = case Schedulers of + Cmd = case Schedulers of default -> ""; _ -> S = integer_to_list(Schedulers), "+S"++S++":"++S end, - ?line {ok, Node} = start_node(Config, Cmd), - ?line [SState] = mcall(Node, [fun () -> - erlang:system_info(schedulers_state) - end]), - ?line io:format("SState=~p~n", [SState]), - ?line {Sched, SchedOnln, _SchedAvail} = SState, - ?line true = is_integer(Sched), - ?line [ok] = mcall(Node, [fun () -> sst0_loop(300) end]), - ?line [ok] = mcall(Node, [fun () -> sst1_loop(300) end]), - ?line [ok] = mcall(Node, [fun () -> sst2_loop(300) end]), - ?line [ok] = mcall(Node, [fun () -> sst4_loop(300) end]), - ?line [ok] = mcall(Node, [fun () -> sst5_loop(300) end]), - ?line [ok, ok, ok, ok, - ok, ok, ok] = mcall(Node, - [fun () -> sst0_loop(200) end, - fun () -> sst1_loop(200) end, - fun () -> sst2_loop(200) end, - fun () -> sst2_loop(200) end, - fun () -> sst3_loop(Sched, 200) end, - fun () -> sst4_loop(200) end, - fun () -> sst5_loop(200) end]), - ?line [SState] = mcall(Node, [fun () -> - case Sched == SchedOnln of - false -> - Sched = erlang:system_flag( - schedulers_online, - SchedOnln); - true -> - ok - end, - erlang:system_info(schedulers_state) - end]), - ?line stop_node(Node), - ?line ok. + {ok, Node} = start_node(Config, Cmd), + [SState] = mcall(Node, [fun () -> + erlang:system_info(schedulers_state) + end]), + + io:format("SState=~p~n", [SState]), + {Sched, SchedOnln, _SchedAvail} = SState, + true = is_integer(Sched), + [ok] = mcall(Node, [fun () -> sst0_loop(300) end]), + [ok] = mcall(Node, [fun () -> sst1_loop(300) end]), + [ok] = mcall(Node, [fun () -> sst2_loop(300) end]), + [ok] = mcall(Node, [fun () -> sst4_loop(300) end]), + [ok] = mcall(Node, [fun () -> sst5_loop(300) end]), + [ok, ok, ok, ok, + ok, ok, ok] = mcall(Node, + [fun () -> sst0_loop(200) end, + fun () -> sst1_loop(200) end, + fun () -> sst2_loop(200) end, + fun () -> sst2_loop(200) end, + fun () -> sst3_loop(Sched, 200) end, + fun () -> sst4_loop(200) end, + fun () -> sst5_loop(200) end]), + [SState] = mcall(Node, [fun () -> + case Sched == SchedOnln of + false -> + Sched = erlang:system_flag( + schedulers_online, + SchedOnln); + true -> + ok + end, + erlang:system_info(schedulers_state) + end]), + stop_node(Node), + ok. sst0_loop(0) -> @@ -1446,264 +1447,263 @@ reader_groups(Config) when is_list(Config) -> %% The actual tilepro64 topology CPUT0 = [{processor,[{node,[{core,{logical,0}}, - {core,{logical,1}}, - {core,{logical,2}}, - {core,{logical,8}}, - {core,{logical,9}}, - {core,{logical,10}}, - {core,{logical,11}}, - {core,{logical,16}}, - {core,{logical,17}}, - {core,{logical,18}}, - {core,{logical,19}}, - {core,{logical,24}}, - {core,{logical,25}}, - {core,{logical,27}}, - {core,{logical,29}}]}, - {node,[{core,{logical,3}}, - {core,{logical,4}}, - {core,{logical,5}}, - {core,{logical,6}}, - {core,{logical,7}}, - {core,{logical,12}}, - {core,{logical,13}}, - {core,{logical,14}}, - {core,{logical,15}}, - {core,{logical,20}}, - {core,{logical,21}}, - {core,{logical,22}}, - {core,{logical,23}}, - {core,{logical,28}}, - {core,{logical,30}}]}, - {node,[{core,{logical,31}}, - {core,{logical,36}}, - {core,{logical,37}}, - {core,{logical,38}}, - {core,{logical,44}}, - {core,{logical,45}}, - {core,{logical,46}}, - {core,{logical,47}}, - {core,{logical,51}}, - {core,{logical,52}}, - {core,{logical,53}}, - {core,{logical,54}}, - {core,{logical,55}}, - {core,{logical,60}}, - {core,{logical,61}}]}, - {node,[{core,{logical,26}}, - {core,{logical,32}}, - {core,{logical,33}}, - {core,{logical,34}}, - {core,{logical,35}}, - {core,{logical,39}}, - {core,{logical,40}}, - {core,{logical,41}}, - {core,{logical,42}}, - {core,{logical,43}}, - {core,{logical,48}}, - {core,{logical,49}}, - {core,{logical,50}}, - {core,{logical,58}}]}]}], - - ?line [{0,1},{1,1},{2,1},{3,3},{4,3},{5,3},{6,3},{7,3},{8,1},{9,1},{10,1}, - {11,1},{12,3},{13,3},{14,4},{15,4},{16,2},{17,2},{18,2},{19,2}, - {20,4},{21,4},{22,4},{23,4},{24,2},{25,2},{26,7},{27,2},{28,4}, - {29,2},{30,4},{31,5},{32,7},{33,7},{34,7},{35,7},{36,5},{37,5}, - {38,5},{39,7},{40,7},{41,8},{42,8},{43,8},{44,5},{45,5},{46,5}, - {47,6},{48,8},{49,8},{50,8},{51,6},{52,6},{53,6},{54,6},{55,6}, - {58,8},{60,6},{61,6}] - = reader_groups_map(CPUT0, 8), + {core,{logical,1}}, + {core,{logical,2}}, + {core,{logical,8}}, + {core,{logical,9}}, + {core,{logical,10}}, + {core,{logical,11}}, + {core,{logical,16}}, + {core,{logical,17}}, + {core,{logical,18}}, + {core,{logical,19}}, + {core,{logical,24}}, + {core,{logical,25}}, + {core,{logical,27}}, + {core,{logical,29}}]}, + {node,[{core,{logical,3}}, + {core,{logical,4}}, + {core,{logical,5}}, + {core,{logical,6}}, + {core,{logical,7}}, + {core,{logical,12}}, + {core,{logical,13}}, + {core,{logical,14}}, + {core,{logical,15}}, + {core,{logical,20}}, + {core,{logical,21}}, + {core,{logical,22}}, + {core,{logical,23}}, + {core,{logical,28}}, + {core,{logical,30}}]}, + {node,[{core,{logical,31}}, + {core,{logical,36}}, + {core,{logical,37}}, + {core,{logical,38}}, + {core,{logical,44}}, + {core,{logical,45}}, + {core,{logical,46}}, + {core,{logical,47}}, + {core,{logical,51}}, + {core,{logical,52}}, + {core,{logical,53}}, + {core,{logical,54}}, + {core,{logical,55}}, + {core,{logical,60}}, + {core,{logical,61}}]}, + {node,[{core,{logical,26}}, + {core,{logical,32}}, + {core,{logical,33}}, + {core,{logical,34}}, + {core,{logical,35}}, + {core,{logical,39}}, + {core,{logical,40}}, + {core,{logical,41}}, + {core,{logical,42}}, + {core,{logical,43}}, + {core,{logical,48}}, + {core,{logical,49}}, + {core,{logical,50}}, + {core,{logical,58}}]}]}], + + [{0,1},{1,1},{2,1},{3,3},{4,3},{5,3},{6,3},{7,3},{8,1},{9,1},{10,1}, + {11,1},{12,3},{13,3},{14,4},{15,4},{16,2},{17,2},{18,2},{19,2}, + {20,4},{21,4},{22,4},{23,4},{24,2},{25,2},{26,7},{27,2},{28,4}, + {29,2},{30,4},{31,5},{32,7},{33,7},{34,7},{35,7},{36,5},{37,5}, + {38,5},{39,7},{40,7},{41,8},{42,8},{43,8},{44,5},{45,5},{46,5}, + {47,6},{48,8},{49,8},{50,8},{51,6},{52,6},{53,6},{54,6},{55,6}, + {58,8},{60,6},{61,6}] + = reader_groups_map(CPUT0, 8), CPUT1 = [n([p([c([t(l(0)),t(l(1)),t(l(2)),t(l(3))]), - c([t(l(4)),t(l(5)),t(l(6)),t(l(7))]), - c([t(l(8)),t(l(9)),t(l(10)),t(l(11))]), - c([t(l(12)),t(l(13)),t(l(14)),t(l(15))])]), - p([c([t(l(16)),t(l(17)),t(l(18)),t(l(19))]), - c([t(l(20)),t(l(21)),t(l(22)),t(l(23))]), - c([t(l(24)),t(l(25)),t(l(26)),t(l(27))]), - c([t(l(28)),t(l(29)),t(l(30)),t(l(31))])])]), - n([p([c([t(l(32)),t(l(33)),t(l(34)),t(l(35))]), - c([t(l(36)),t(l(37)),t(l(38)),t(l(39))]), - c([t(l(40)),t(l(41)),t(l(42)),t(l(43))]), - c([t(l(44)),t(l(45)),t(l(46)),t(l(47))])]), - p([c([t(l(48)),t(l(49)),t(l(50)),t(l(51))]), - c([t(l(52)),t(l(53)),t(l(54)),t(l(55))]), - c([t(l(56)),t(l(57)),t(l(58)),t(l(59))]), - c([t(l(60)),t(l(61)),t(l(62)),t(l(63))])])]), - n([p([c([t(l(64)),t(l(65)),t(l(66)),t(l(67))]), - c([t(l(68)),t(l(69)),t(l(70)),t(l(71))]), - c([t(l(72)),t(l(73)),t(l(74)),t(l(75))]), - c([t(l(76)),t(l(77)),t(l(78)),t(l(79))])]), - p([c([t(l(80)),t(l(81)),t(l(82)),t(l(83))]), - c([t(l(84)),t(l(85)),t(l(86)),t(l(87))]), - c([t(l(88)),t(l(89)),t(l(90)),t(l(91))]), - c([t(l(92)),t(l(93)),t(l(94)),t(l(95))])])]), - n([p([c([t(l(96)),t(l(97)),t(l(98)),t(l(99))]), - c([t(l(100)),t(l(101)),t(l(102)),t(l(103))]), - c([t(l(104)),t(l(105)),t(l(106)),t(l(107))]), - c([t(l(108)),t(l(109)),t(l(110)),t(l(111))])]), - p([c([t(l(112)),t(l(113)),t(l(114)),t(l(115))]), - c([t(l(116)),t(l(117)),t(l(118)),t(l(119))]), - c([t(l(120)),t(l(121)),t(l(122)),t(l(123))]), - c([t(l(124)),t(l(125)),t(l(126)),t(l(127))])])])], - - ?line [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3}, - {10,3},{11,3},{12,4},{13,4},{14,4},{15,4},{16,5},{17,5},{18,5}, - {19,5},{20,6},{21,6},{22,6},{23,6},{24,7},{25,7},{26,7},{27,7}, - {28,8},{29,8},{30,8},{31,8},{32,9},{33,9},{34,9},{35,9},{36,10}, - {37,10},{38,10},{39,10},{40,11},{41,11},{42,11},{43,11},{44,12}, - {45,12},{46,12},{47,12},{48,13},{49,13},{50,13},{51,13},{52,14}, - {53,14},{54,14},{55,14},{56,15},{57,15},{58,15},{59,15},{60,16}, - {61,16},{62,16},{63,16},{64,17},{65,17},{66,17},{67,17},{68,18}, - {69,18},{70,18},{71,18},{72,19},{73,19},{74,19},{75,19},{76,20}, - {77,20},{78,20},{79,20},{80,21},{81,21},{82,21},{83,21},{84,22}, - {85,22},{86,22},{87,22},{88,23},{89,23},{90,23},{91,23},{92,24}, - {93,24},{94,24},{95,24},{96,25},{97,25},{98,25},{99,25},{100,26}, - {101,26},{102,26},{103,26},{104,27},{105,27},{106,27},{107,27}, - {108,28},{109,28},{110,28},{111,28},{112,29},{113,29},{114,29}, - {115,29},{116,30},{117,30},{118,30},{119,30},{120,31},{121,31}, - {122,31},{123,31},{124,32},{125,32},{126,32},{127,32}] - = reader_groups_map(CPUT1, 128), - - ?line [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1}, - {11,1},{12,1},{13,1},{14,1},{15,1},{16,1},{17,1},{18,1},{19,1}, - {20,1},{21,1},{22,1},{23,1},{24,1},{25,1},{26,1},{27,1},{28,1}, - {29,1},{30,1},{31,1},{32,1},{33,1},{34,1},{35,1},{36,1},{37,1}, - {38,1},{39,1},{40,1},{41,1},{42,1},{43,1},{44,1},{45,1},{46,1}, - {47,1},{48,1},{49,1},{50,1},{51,1},{52,1},{53,1},{54,1},{55,1}, - {56,1},{57,1},{58,1},{59,1},{60,1},{61,1},{62,1},{63,1},{64,2}, - {65,2},{66,2},{67,2},{68,2},{69,2},{70,2},{71,2},{72,2},{73,2}, - {74,2},{75,2},{76,2},{77,2},{78,2},{79,2},{80,2},{81,2},{82,2}, - {83,2},{84,2},{85,2},{86,2},{87,2},{88,2},{89,2},{90,2},{91,2}, - {92,2},{93,2},{94,2},{95,2},{96,2},{97,2},{98,2},{99,2},{100,2}, - {101,2},{102,2},{103,2},{104,2},{105,2},{106,2},{107,2},{108,2}, - {109,2},{110,2},{111,2},{112,2},{113,2},{114,2},{115,2},{116,2}, - {117,2},{118,2},{119,2},{120,2},{121,2},{122,2},{123,2},{124,2}, - {125,2},{126,2},{127,2}] - = reader_groups_map(CPUT1, 2), - - ?line [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3},{10,3}, - {11,3},{12,3},{13,3},{14,3},{15,3},{16,4},{17,4},{18,4},{19,4}, - {20,4},{21,4},{22,4},{23,4},{24,5},{25,5},{26,5},{27,5},{28,5}, - {29,5},{30,5},{31,5},{32,6},{33,6},{34,6},{35,6},{36,6},{37,6}, - {38,6},{39,6},{40,7},{41,7},{42,7},{43,7},{44,7},{45,7},{46,7}, - {47,7},{48,8},{49,8},{50,8},{51,8},{52,8},{53,8},{54,8},{55,8}, - {56,9},{57,9},{58,9},{59,9},{60,9},{61,9},{62,9},{63,9},{64,10}, - {65,10},{66,10},{67,10},{68,10},{69,10},{70,10},{71,10},{72,11}, - {73,11},{74,11},{75,11},{76,11},{77,11},{78,11},{79,11},{80,12}, - {81,12},{82,12},{83,12},{84,12},{85,12},{86,12},{87,12},{88,13}, - {89,13},{90,13},{91,13},{92,13},{93,13},{94,13},{95,13},{96,14}, - {97,14},{98,14},{99,14},{100,14},{101,14},{102,14},{103,14}, - {104,15},{105,15},{106,15},{107,15},{108,15},{109,15},{110,15}, - {111,15},{112,16},{113,16},{114,16},{115,16},{116,16},{117,16}, - {118,16},{119,16},{120,17},{121,17},{122,17},{123,17},{124,17}, - {125,17},{126,17},{127,17}] - = reader_groups_map(CPUT1, 17), - - ?line [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1}, - {11,1},{12,1},{13,1},{14,1},{15,1},{16,2},{17,2},{18,2},{19,2}, - {20,2},{21,2},{22,2},{23,2},{24,2},{25,2},{26,2},{27,2},{28,2}, - {29,2},{30,2},{31,2},{32,3},{33,3},{34,3},{35,3},{36,3},{37,3}, - {38,3},{39,3},{40,3},{41,3},{42,3},{43,3},{44,3},{45,3},{46,3}, - {47,3},{48,4},{49,4},{50,4},{51,4},{52,4},{53,4},{54,4},{55,4}, - {56,4},{57,4},{58,4},{59,4},{60,4},{61,4},{62,4},{63,4},{64,5}, - {65,5},{66,5},{67,5},{68,5},{69,5},{70,5},{71,5},{72,5},{73,5}, - {74,5},{75,5},{76,5},{77,5},{78,5},{79,5},{80,6},{81,6},{82,6}, - {83,6},{84,6},{85,6},{86,6},{87,6},{88,6},{89,6},{90,6},{91,6}, - {92,6},{93,6},{94,6},{95,6},{96,7},{97,7},{98,7},{99,7},{100,7}, - {101,7},{102,7},{103,7},{104,7},{105,7},{106,7},{107,7},{108,7}, - {109,7},{110,7},{111,7},{112,7},{113,7},{114,7},{115,7},{116,7}, - {117,7},{118,7},{119,7},{120,7},{121,7},{122,7},{123,7},{124,7}, - {125,7},{126,7},{127,7}] - = reader_groups_map(CPUT1, 7), - - ?line CPUT2 = [p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))]), - p([t(l(5)),t(l(6)),t(l(7)),t(l(8)),t(l(9))]), - p([t(l(10))]), - p([c(l(11)),c(l(12)),c(l(13))]), - p([c(l(14)),c(l(15))])], - - ?line [{0,1},{1,1},{2,1},{3,1},{4,1}, - {5,2},{6,2},{7,2},{8,2},{9,2}, - {10,3}, - {11,4},{12,4},{13,4}, - {14,5},{15,5}] = reader_groups_map(CPUT2, 5), - - - ?line [{0,1},{1,1},{2,2},{3,2},{4,2}, - {5,3},{6,3},{7,3},{8,3},{9,3}, - {10,4}, - {11,5},{12,5},{13,5}, - {14,6},{15,6}] = reader_groups_map(CPUT2, 6), - - ?line [{0,1},{1,1},{2,2},{3,2},{4,2}, - {5,3},{6,3},{7,3},{8,3},{9,3}, - {10,4}, - {11,5},{12,6},{13,6}, - {14,7},{15,7}] = reader_groups_map(CPUT2, 7), - - ?line [{0,1},{1,1},{2,2},{3,2},{4,2}, - {5,3},{6,3},{7,3},{8,3},{9,3}, - {10,4}, - {11,5},{12,6},{13,6}, - {14,7},{15,8}] = reader_groups_map(CPUT2, 8), - - ?line [{0,1},{1,2},{2,2},{3,3},{4,3}, - {5,4},{6,4},{7,4},{8,4},{9,4}, - {10,5}, - {11,6},{12,7},{13,7}, - {14,8},{15,9}] = reader_groups_map(CPUT2, 9), - - ?line [{0,1},{1,2},{2,2},{3,3},{4,3}, - {5,4},{6,4},{7,4},{8,4},{9,4}, - {10,5}, - {11,6},{12,7},{13,8}, - {14,9},{15,10}] = reader_groups_map(CPUT2, 10), - - ?line [{0,1},{1,2},{2,3},{3,4},{4,4}, - {5,5},{6,5},{7,5},{8,5},{9,5}, - {10,6}, - {11,7},{12,8},{13,9}, - {14,10},{15,11}] = reader_groups_map(CPUT2, 11), - - ?line [{0,1},{1,2},{2,3},{3,4},{4,5}, - {5,6},{6,6},{7,6},{8,6},{9,6}, - {10,7}, - {11,8},{12,9},{13,10}, - {14,11},{15,12}] = reader_groups_map(CPUT2, 100), + c([t(l(4)),t(l(5)),t(l(6)),t(l(7))]), + c([t(l(8)),t(l(9)),t(l(10)),t(l(11))]), + c([t(l(12)),t(l(13)),t(l(14)),t(l(15))])]), + p([c([t(l(16)),t(l(17)),t(l(18)),t(l(19))]), + c([t(l(20)),t(l(21)),t(l(22)),t(l(23))]), + c([t(l(24)),t(l(25)),t(l(26)),t(l(27))]), + c([t(l(28)),t(l(29)),t(l(30)),t(l(31))])])]), + n([p([c([t(l(32)),t(l(33)),t(l(34)),t(l(35))]), + c([t(l(36)),t(l(37)),t(l(38)),t(l(39))]), + c([t(l(40)),t(l(41)),t(l(42)),t(l(43))]), + c([t(l(44)),t(l(45)),t(l(46)),t(l(47))])]), + p([c([t(l(48)),t(l(49)),t(l(50)),t(l(51))]), + c([t(l(52)),t(l(53)),t(l(54)),t(l(55))]), + c([t(l(56)),t(l(57)),t(l(58)),t(l(59))]), + c([t(l(60)),t(l(61)),t(l(62)),t(l(63))])])]), + n([p([c([t(l(64)),t(l(65)),t(l(66)),t(l(67))]), + c([t(l(68)),t(l(69)),t(l(70)),t(l(71))]), + c([t(l(72)),t(l(73)),t(l(74)),t(l(75))]), + c([t(l(76)),t(l(77)),t(l(78)),t(l(79))])]), + p([c([t(l(80)),t(l(81)),t(l(82)),t(l(83))]), + c([t(l(84)),t(l(85)),t(l(86)),t(l(87))]), + c([t(l(88)),t(l(89)),t(l(90)),t(l(91))]), + c([t(l(92)),t(l(93)),t(l(94)),t(l(95))])])]), + n([p([c([t(l(96)),t(l(97)),t(l(98)),t(l(99))]), + c([t(l(100)),t(l(101)),t(l(102)),t(l(103))]), + c([t(l(104)),t(l(105)),t(l(106)),t(l(107))]), + c([t(l(108)),t(l(109)),t(l(110)),t(l(111))])]), + p([c([t(l(112)),t(l(113)),t(l(114)),t(l(115))]), + c([t(l(116)),t(l(117)),t(l(118)),t(l(119))]), + c([t(l(120)),t(l(121)),t(l(122)),t(l(123))]), + c([t(l(124)),t(l(125)),t(l(126)),t(l(127))])])])], + + [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3}, + {10,3},{11,3},{12,4},{13,4},{14,4},{15,4},{16,5},{17,5},{18,5}, + {19,5},{20,6},{21,6},{22,6},{23,6},{24,7},{25,7},{26,7},{27,7}, + {28,8},{29,8},{30,8},{31,8},{32,9},{33,9},{34,9},{35,9},{36,10}, + {37,10},{38,10},{39,10},{40,11},{41,11},{42,11},{43,11},{44,12}, + {45,12},{46,12},{47,12},{48,13},{49,13},{50,13},{51,13},{52,14}, + {53,14},{54,14},{55,14},{56,15},{57,15},{58,15},{59,15},{60,16}, + {61,16},{62,16},{63,16},{64,17},{65,17},{66,17},{67,17},{68,18}, + {69,18},{70,18},{71,18},{72,19},{73,19},{74,19},{75,19},{76,20}, + {77,20},{78,20},{79,20},{80,21},{81,21},{82,21},{83,21},{84,22}, + {85,22},{86,22},{87,22},{88,23},{89,23},{90,23},{91,23},{92,24}, + {93,24},{94,24},{95,24},{96,25},{97,25},{98,25},{99,25},{100,26}, + {101,26},{102,26},{103,26},{104,27},{105,27},{106,27},{107,27}, + {108,28},{109,28},{110,28},{111,28},{112,29},{113,29},{114,29}, + {115,29},{116,30},{117,30},{118,30},{119,30},{120,31},{121,31}, + {122,31},{123,31},{124,32},{125,32},{126,32},{127,32}] + = reader_groups_map(CPUT1, 128), + + [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1}, + {11,1},{12,1},{13,1},{14,1},{15,1},{16,1},{17,1},{18,1},{19,1}, + {20,1},{21,1},{22,1},{23,1},{24,1},{25,1},{26,1},{27,1},{28,1}, + {29,1},{30,1},{31,1},{32,1},{33,1},{34,1},{35,1},{36,1},{37,1}, + {38,1},{39,1},{40,1},{41,1},{42,1},{43,1},{44,1},{45,1},{46,1}, + {47,1},{48,1},{49,1},{50,1},{51,1},{52,1},{53,1},{54,1},{55,1}, + {56,1},{57,1},{58,1},{59,1},{60,1},{61,1},{62,1},{63,1},{64,2}, + {65,2},{66,2},{67,2},{68,2},{69,2},{70,2},{71,2},{72,2},{73,2}, + {74,2},{75,2},{76,2},{77,2},{78,2},{79,2},{80,2},{81,2},{82,2}, + {83,2},{84,2},{85,2},{86,2},{87,2},{88,2},{89,2},{90,2},{91,2}, + {92,2},{93,2},{94,2},{95,2},{96,2},{97,2},{98,2},{99,2},{100,2}, + {101,2},{102,2},{103,2},{104,2},{105,2},{106,2},{107,2},{108,2}, + {109,2},{110,2},{111,2},{112,2},{113,2},{114,2},{115,2},{116,2}, + {117,2},{118,2},{119,2},{120,2},{121,2},{122,2},{123,2},{124,2}, + {125,2},{126,2},{127,2}] + = reader_groups_map(CPUT1, 2), + + [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3},{10,3}, + {11,3},{12,3},{13,3},{14,3},{15,3},{16,4},{17,4},{18,4},{19,4}, + {20,4},{21,4},{22,4},{23,4},{24,5},{25,5},{26,5},{27,5},{28,5}, + {29,5},{30,5},{31,5},{32,6},{33,6},{34,6},{35,6},{36,6},{37,6}, + {38,6},{39,6},{40,7},{41,7},{42,7},{43,7},{44,7},{45,7},{46,7}, + {47,7},{48,8},{49,8},{50,8},{51,8},{52,8},{53,8},{54,8},{55,8}, + {56,9},{57,9},{58,9},{59,9},{60,9},{61,9},{62,9},{63,9},{64,10}, + {65,10},{66,10},{67,10},{68,10},{69,10},{70,10},{71,10},{72,11}, + {73,11},{74,11},{75,11},{76,11},{77,11},{78,11},{79,11},{80,12}, + {81,12},{82,12},{83,12},{84,12},{85,12},{86,12},{87,12},{88,13}, + {89,13},{90,13},{91,13},{92,13},{93,13},{94,13},{95,13},{96,14}, + {97,14},{98,14},{99,14},{100,14},{101,14},{102,14},{103,14}, + {104,15},{105,15},{106,15},{107,15},{108,15},{109,15},{110,15}, + {111,15},{112,16},{113,16},{114,16},{115,16},{116,16},{117,16}, + {118,16},{119,16},{120,17},{121,17},{122,17},{123,17},{124,17}, + {125,17},{126,17},{127,17}] + = reader_groups_map(CPUT1, 17), + + [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1}, + {11,1},{12,1},{13,1},{14,1},{15,1},{16,2},{17,2},{18,2},{19,2}, + {20,2},{21,2},{22,2},{23,2},{24,2},{25,2},{26,2},{27,2},{28,2}, + {29,2},{30,2},{31,2},{32,3},{33,3},{34,3},{35,3},{36,3},{37,3}, + {38,3},{39,3},{40,3},{41,3},{42,3},{43,3},{44,3},{45,3},{46,3}, + {47,3},{48,4},{49,4},{50,4},{51,4},{52,4},{53,4},{54,4},{55,4}, + {56,4},{57,4},{58,4},{59,4},{60,4},{61,4},{62,4},{63,4},{64,5}, + {65,5},{66,5},{67,5},{68,5},{69,5},{70,5},{71,5},{72,5},{73,5}, + {74,5},{75,5},{76,5},{77,5},{78,5},{79,5},{80,6},{81,6},{82,6}, + {83,6},{84,6},{85,6},{86,6},{87,6},{88,6},{89,6},{90,6},{91,6}, + {92,6},{93,6},{94,6},{95,6},{96,7},{97,7},{98,7},{99,7},{100,7}, + {101,7},{102,7},{103,7},{104,7},{105,7},{106,7},{107,7},{108,7}, + {109,7},{110,7},{111,7},{112,7},{113,7},{114,7},{115,7},{116,7}, + {117,7},{118,7},{119,7},{120,7},{121,7},{122,7},{123,7},{124,7}, + {125,7},{126,7},{127,7}] + = reader_groups_map(CPUT1, 7), + + CPUT2 = [p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))]), + p([t(l(5)),t(l(6)),t(l(7)),t(l(8)),t(l(9))]), + p([t(l(10))]), + p([c(l(11)),c(l(12)),c(l(13))]), + p([c(l(14)),c(l(15))])], + + [{0,1},{1,1},{2,1},{3,1},{4,1}, + {5,2},{6,2},{7,2},{8,2},{9,2}, + {10,3}, + {11,4},{12,4},{13,4}, + {14,5},{15,5}] = reader_groups_map(CPUT2, 5), + + + [{0,1},{1,1},{2,2},{3,2},{4,2}, + {5,3},{6,3},{7,3},{8,3},{9,3}, + {10,4}, + {11,5},{12,5},{13,5}, + {14,6},{15,6}] = reader_groups_map(CPUT2, 6), + + [{0,1},{1,1},{2,2},{3,2},{4,2}, + {5,3},{6,3},{7,3},{8,3},{9,3}, + {10,4}, + {11,5},{12,6},{13,6}, + {14,7},{15,7}] = reader_groups_map(CPUT2, 7), + + [{0,1},{1,1},{2,2},{3,2},{4,2}, + {5,3},{6,3},{7,3},{8,3},{9,3}, + {10,4}, + {11,5},{12,6},{13,6}, + {14,7},{15,8}] = reader_groups_map(CPUT2, 8), + + [{0,1},{1,2},{2,2},{3,3},{4,3}, + {5,4},{6,4},{7,4},{8,4},{9,4}, + {10,5}, + {11,6},{12,7},{13,7}, + {14,8},{15,9}] = reader_groups_map(CPUT2, 9), + + [{0,1},{1,2},{2,2},{3,3},{4,3}, + {5,4},{6,4},{7,4},{8,4},{9,4}, + {10,5}, + {11,6},{12,7},{13,8}, + {14,9},{15,10}] = reader_groups_map(CPUT2, 10), + + [{0,1},{1,2},{2,3},{3,4},{4,4}, + {5,5},{6,5},{7,5},{8,5},{9,5}, + {10,6}, + {11,7},{12,8},{13,9}, + {14,10},{15,11}] = reader_groups_map(CPUT2, 11), + + [{0,1},{1,2},{2,3},{3,4},{4,5}, + {5,6},{6,6},{7,6},{8,6},{9,6}, + {10,7}, + {11,8},{12,9},{13,10}, + {14,11},{15,12}] = reader_groups_map(CPUT2, 100), CPUT3 = [p([t(l(5)),t(l(6)),t(l(7)),t(l(8)),t(l(9))]), - p([t(l(10))]), - p([c(l(11)),c(l(12)),c(l(13))]), - p([c(l(14)),c(l(15))]), - p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))])], + p([t(l(10))]), + p([c(l(11)),c(l(12)),c(l(13))]), + p([c(l(14)),c(l(15))]), + p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))])], - ?line [{0,5},{1,5},{2,6},{3,6},{4,6}, - {5,1},{6,1},{7,1},{8,1},{9,1}, - {10,2},{11,3},{12,3},{13,3}, - {14,4},{15,4}] = reader_groups_map(CPUT3, 6), + [{0,5},{1,5},{2,6},{3,6},{4,6}, + {5,1},{6,1},{7,1},{8,1},{9,1}, + {10,2},{11,3},{12,3},{13,3}, + {14,4},{15,4}] = reader_groups_map(CPUT3, 6), CPUT4 = [p([t(l(0)),t(l(1)),t(l(2)),t(l(3)),t(l(4))]), - p([t(l(5))]), - p([c(l(6)),c(l(7)),c(l(8))]), - p([c(l(9)),c(l(10))]), - p([c(l(11)),c(l(12)),c(l(13)),c(l(14)),c(l(15))])], - - ?line [{0,1},{1,1},{2,1},{3,1},{4,1}, - {5,2}, - {6,3},{7,3},{8,3}, - {9,4},{10,4}, - {11,5},{12,5},{13,6},{14,6},{15,6}] = reader_groups_map(CPUT4, 6), - - ?line [{0,1},{1,1},{2,1},{3,1},{4,1}, - {5,2}, - {6,3},{7,4},{8,4}, - {9,5},{10,5}, - {11,6},{12,6},{13,7},{14,7},{15,7}] = reader_groups_map(CPUT4, 7), - - ?line [{0,1},{65535,2}] = reader_groups_map([c(l(0)),c(l(65535))], 10), - - ?line ok. + p([t(l(5))]), + p([c(l(6)),c(l(7)),c(l(8))]), + p([c(l(9)),c(l(10))]), + p([c(l(11)),c(l(12)),c(l(13)),c(l(14)),c(l(15))])], + + [{0,1},{1,1},{2,1},{3,1},{4,1}, + {5,2}, + {6,3},{7,3},{8,3}, + {9,4},{10,4}, + {11,5},{12,5},{13,6},{14,6},{15,6}] = reader_groups_map(CPUT4, 6), + + [{0,1},{1,1},{2,1},{3,1},{4,1}, + {5,2}, + {6,3},{7,4},{8,4}, + {9,5},{10,5}, + {11,6},{12,6},{13,7},{14,7},{15,7}] = reader_groups_map(CPUT4, 7), + + [{0,1},{65535,2}] = reader_groups_map([c(l(0)),c(l(65535))], 10), + ok. reader_groups_map(CPUT, Groups) -> @@ -1764,21 +1764,21 @@ n(X) -> mcall(Node, Funs) -> Parent = self(), Refs = lists:map(fun (Fun) -> - Ref = make_ref(), - spawn_link(Node, - fun () -> - Res = Fun(), - unlink(Parent), - Parent ! {Ref, Res} - end), - Ref - end, Funs), + Ref = make_ref(), + spawn_link(Node, + fun () -> + Res = Fun(), + unlink(Parent), + Parent ! {Ref, Res} + end), + Ref + end, Funs), lists:map(fun (Ref) -> - receive - {Ref, Res} -> - Res - end - end, Refs). + receive + {Ref, Res} -> + Res + end + end, Refs). erl_rel_flag_var() -> "ERL_OTP"++erlang:system_info(otp_release)++"_FLAGS". @@ -1805,91 +1805,91 @@ ok(_Res, Config) -> proplists:get_value(ok_res, Config). chk_result(too_slow, - _LWorkers, - _NWorkers, - _HWorkers, - _MWorkers, - _LNShouldWork, - _HShouldWork, - _MShouldWork) -> - ?line ok; + _LWorkers, + _NWorkers, + _HWorkers, + _MWorkers, + _LNShouldWork, + _HShouldWork, + _MShouldWork) -> + ok; chk_result([{low, L, Lmin, _Lmax}, - {normal, N, Nmin, _Nmax}, - {high, H, Hmin, _Hmax}, - {max, M, Mmin, _Mmax}] = Res, - LWorkers, - NWorkers, - HWorkers, - MWorkers, - LNShouldWork, - HShouldWork, - MShouldWork) -> - ?line io:format("~p~n", [Res]), - ?line Relax = relax_limits(), + {normal, N, Nmin, _Nmax}, + {high, H, Hmin, _Hmax}, + {max, M, Mmin, _Mmax}] = Res, + LWorkers, + NWorkers, + HWorkers, + MWorkers, + LNShouldWork, + HShouldWork, + MShouldWork) -> + io:format("~p~n", [Res]), + Relax = relax_limits(), case {L, N} of - {0, 0} -> - ?line false = LNShouldWork; - _ -> - ?line {LminRatioLim, - NminRatioLim, - LNRatioLimMin, - LNRatioLimMax} = case Relax of - false -> {0.5, 0.5, 0.05, 0.25}; - true -> {0.05, 0.05, 0.01, 0.4} - end, - ?line Lavg = L/LWorkers, - ?line Navg = N/NWorkers, - ?line Ratio = Lavg/Navg, - ?line LminRatio = Lmin/Lavg, - ?line NminRatio = Nmin/Navg, - ?line io:format("low min ratio=~p~n" - "normal min ratio=~p~n" - "low avg=~p~n" - "normal avg=~p~n" - "low/normal ratio=~p~n", - [LminRatio, NminRatio, Lavg, Navg, Ratio]), - erlang:display({low_min_ratio, LminRatio}), - erlang:display({normal_min_ratio, NminRatio}), - erlang:display({low_avg, Lavg}), - erlang:display({normal_avg, Navg}), - erlang:display({low_normal_ratio, Ratio}), - ?line chk_lim(LminRatioLim, LminRatio, 1.0, low_min_ratio), - ?line chk_lim(NminRatioLim, NminRatio, 1.0, normal_min_ratio), - ?line chk_lim(LNRatioLimMin, Ratio, LNRatioLimMax, low_normal_ratio), - ?line true = LNShouldWork, - ?line ok + {0, 0} -> + false = LNShouldWork; + _ -> + {LminRatioLim, + NminRatioLim, + LNRatioLimMin, + LNRatioLimMax} = case Relax of + false -> {0.5, 0.5, 0.05, 0.25}; + true -> {0.05, 0.05, 0.01, 0.4} + end, + Lavg = L/LWorkers, + Navg = N/NWorkers, + Ratio = Lavg/Navg, + LminRatio = Lmin/Lavg, + NminRatio = Nmin/Navg, + io:format("low min ratio=~p~n" + "normal min ratio=~p~n" + "low avg=~p~n" + "normal avg=~p~n" + "low/normal ratio=~p~n", + [LminRatio, NminRatio, Lavg, Navg, Ratio]), + erlang:display({low_min_ratio, LminRatio}), + erlang:display({normal_min_ratio, NminRatio}), + erlang:display({low_avg, Lavg}), + erlang:display({normal_avg, Navg}), + erlang:display({low_normal_ratio, Ratio}), + chk_lim(LminRatioLim, LminRatio, 1.0, low_min_ratio), + chk_lim(NminRatioLim, NminRatio, 1.0, normal_min_ratio), + chk_lim(LNRatioLimMin, Ratio, LNRatioLimMax, low_normal_ratio), + true = LNShouldWork, + ok end, case H of - 0 -> - ?line false = HShouldWork; - _ -> - ?line HminRatioLim = case Relax of - false -> 0.5; - true -> 0.1 - end, - ?line Havg = H/HWorkers, - ?line HminRatio = Hmin/Havg, - erlang:display({high_min_ratio, HminRatio}), - ?line chk_lim(HminRatioLim, HminRatio, 1.0, high_min_ratio), - ?line true = HShouldWork, - ?line ok + 0 -> + false = HShouldWork; + _ -> + HminRatioLim = case Relax of + false -> 0.5; + true -> 0.1 + end, + Havg = H/HWorkers, + HminRatio = Hmin/Havg, + erlang:display({high_min_ratio, HminRatio}), + chk_lim(HminRatioLim, HminRatio, 1.0, high_min_ratio), + true = HShouldWork, + ok end, case M of - 0 -> - ?line false = MShouldWork; - _ -> - ?line MminRatioLim = case Relax of - false -> 0.5; - true -> 0.1 - end, - ?line Mavg = M/MWorkers, - ?line MminRatio = Mmin/Mavg, - erlang:display({max_min_ratio, MminRatio}), - ?line chk_lim(MminRatioLim, MminRatio, 1.0, max_min_ratio), - ?line true = MShouldWork, - ?line ok + 0 -> + false = MShouldWork; + _ -> + MminRatioLim = case Relax of + false -> 0.5; + true -> 0.1 + end, + Mavg = M/MWorkers, + MminRatio = Mmin/Mavg, + erlang:display({max_min_ratio, MminRatio}), + chk_lim(MminRatioLim, MminRatio, 1.0, max_min_ratio), + true = MShouldWork, + ok end, - ?line ok. + ok. @@ -2103,55 +2103,55 @@ part_time_workers(N, Prio) -> tracer(Low, Normal, High, Max) -> receive - {tracees, Prio, Tracees} -> - save_tracees(Prio, Tracees), - case Prio of - low -> tracer(Tracees++Low, Normal, High, Max); - normal -> tracer(Low, Tracees++Normal, High, Max); - high -> tracer(Low, Normal, Tracees++High, Max); - max -> tracer(Low, Normal, High, Tracees++Max) - end; - {get_result, Ref, Who} -> - Delivered = erlang:trace_delivered(all), - receive - {trace_delivered, all, Delivered} -> - ok - end, - {Lc, Nc, Hc, Mc} = read_trace(), - GetMinMax - = fun (Prio, Procs) -> - LargeNum = 1 bsl 64, - case lists:foldl(fun (P, {Mn, Mx} = MnMx) -> - {Prio, C} = get(P), - case C < Mn of - true -> - case C > Mx of - true -> - {C, C}; - false -> - {C, Mx} - end; - false -> - case C > Mx of - true -> {Mn, C}; - false -> MnMx - end - end - end, - {LargeNum, 0}, - Procs) of - {LargeNum, 0} -> {0, 0}; - Res -> Res - end - end, - {Lmin, Lmax} = GetMinMax(low, Low), - {Nmin, Nmax} = GetMinMax(normal, Normal), - {Hmin, Hmax} = GetMinMax(high, High), - {Mmin, Mmax} = GetMinMax(max, Max), - Who ! {trace_result, Ref, [{low, Lc, Lmin, Lmax}, - {normal, Nc, Nmin, Nmax}, - {high, Hc, Hmin, Hmax}, - {max, Mc, Mmin, Mmax}]} + {tracees, Prio, Tracees} -> + save_tracees(Prio, Tracees), + case Prio of + low -> tracer(Tracees++Low, Normal, High, Max); + normal -> tracer(Low, Tracees++Normal, High, Max); + high -> tracer(Low, Normal, Tracees++High, Max); + max -> tracer(Low, Normal, High, Tracees++Max) + end; + {get_result, Ref, Who} -> + Delivered = erlang:trace_delivered(all), + receive + {trace_delivered, all, Delivered} -> + ok + end, + {Lc, Nc, Hc, Mc} = read_trace(), + GetMinMax + = fun (Prio, Procs) -> + LargeNum = 1 bsl 64, + case lists:foldl(fun (P, {Mn, Mx} = MnMx) -> + {Prio, C} = get(P), + case C < Mn of + true -> + case C > Mx of + true -> + {C, C}; + false -> + {C, Mx} + end; + false -> + case C > Mx of + true -> {Mn, C}; + false -> MnMx + end + end + end, + {LargeNum, 0}, + Procs) of + {LargeNum, 0} -> {0, 0}; + Res -> Res + end + end, + {Lmin, Lmax} = GetMinMax(low, Low), + {Nmin, Nmax} = GetMinMax(normal, Normal), + {Hmin, Hmax} = GetMinMax(high, High), + {Mmin, Mmax} = GetMinMax(max, Max), + Who ! {trace_result, Ref, [{low, Lc, Lmin, Lmax}, + {normal, Nc, Nmin, Nmax}, + {high, Hc, Hmin, Hmax}, + {max, Mc, Mmin, Mmax}]} end. read_trace() -> @@ -2232,7 +2232,7 @@ start_node(Config, Args) when is_list(Config) -> ++ integer_to_list(erlang:system_time(seconds)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), - ?line test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). + test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). stop_node(Node) -> test_server:stop_node(Node). diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index ed70d14a1b..6f14963e9d 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -35,10 +35,10 @@ all() -> basic(Config) when is_list(Config) -> Drv = "send_term_drv", - ?line P = start_driver(Config, Drv), + P = start_driver(Config, Drv), - ?line [] = term(P, 0), - ?line Self = self(), + [] = term(P, 0), + Self = self(), {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self,#{}} = term(P, 1), Map41 = maps:from_list([{blurf, 42}, @@ -52,36 +52,36 @@ basic(Config) when is_list(Config) -> {3.1416, Self}, {#{}, blurf}]), Map42 = term(P, 42), - ?line Deep = lists:seq(0, 199), - ?line Deep = term(P, 2), - ?line {B1,B2} = term(P, 3), - ?line B1 = list_to_binary(lists:seq(0, 255)), - ?line B2 = list_to_binary(lists:seq(23, 255-17)), + Deep = lists:seq(0, 199), + Deep = term(P, 2), + {B1,B2} = term(P, 3), + B1 = list_to_binary(lists:seq(0, 255)), + B2 = list_to_binary(lists:seq(23, 255-17)), %% Pid sending. We need another process. - ?line Child = spawn_link(fun() -> + Child = spawn_link(fun() -> erlang:port_command(P, [4]) end), - ?line {Self,Child} = receive_any(), + {Self,Child} = receive_any(), %% ERL_DRV_EXT2TERM - ?line ExpectExt2Term = expected_ext2term_drv(proplists:get_value(data_dir, Config)), - ?line ExpectExt2Term = term(P, 5), + ExpectExt2Term = expected_ext2term_drv(proplists:get_value(data_dir, Config)), + ExpectExt2Term = term(P, 5), %% ERL_DRV_INT, ERL_DRV_UINT - ?line case erlang:system_info({wordsize, external}) of + case erlang:system_info({wordsize, external}) of 4 -> - ?line {-1, 4294967295} = term(P, 6); + {-1, 4294967295} = term(P, 6); 8 -> - ?line {-1, 18446744073709551615} = term(P, 6) + {-1, 18446744073709551615} = term(P, 6) end, %% ERL_DRV_BUF2BINARY - ?line ExpectedBinTup = {<<>>, + ExpectedBinTup = {<<>>, <<>>, list_to_binary(lists:duplicate(17,17)), list_to_binary(lists:duplicate(1024,17))}, - ?line ExpectedBinTup = term(P, 7), + ExpectedBinTup = term(P, 7), %% single terms Singles = [{[], 8}, % ERL_DRV_NIL @@ -116,34 +116,34 @@ basic(Config) when is_list(Config) -> {-20233590931456, 38}, % ERL_DRV_INT64 {-9223372036854775808, 39}, {#{}, 40}], % ERL_DRV_MAP - ?line {Terms, Ops} = lists:unzip(Singles), - ?line Terms = term(P,Ops), + {Terms, Ops} = lists:unzip(Singles), + Terms = term(P,Ops), AFloat = term(P, 26), % ERL_DRV_FLOAT - ?line true = AFloat < 0.001, - ?line true = AFloat > -0.001, + true = AFloat < 0.001, + true = AFloat > -0.001, %% Failure cases. - ?line [] = term(P, 127), - ?line receive + [] = term(P, 127), + receive Any -> ct:fail("Unexpected: ~p\n", [Any]) after 0 -> ok end, - ?line ok = chk_temp_alloc(), + ok = chk_temp_alloc(), %% In a private heap system, verify that there are no binaries %% left for the process. - ?line erlang:garbage_collect(), %Get rid of binaries. + erlang:garbage_collect(), %Get rid of binaries. case erlang:system_info(heap_type) of private -> - ?line {binary,[]} = process_info(self(), binary); + {binary,[]} = process_info(self(), binary); _ -> ok end, - ?line stop_driver(P, Drv), + stop_driver(P, Drv), ok. term(P, Op) -> @@ -159,22 +159,22 @@ chk_temp_alloc() -> case erlang:system_info({allocator,temp_alloc}) of false -> %% Temp alloc is not enabled - ?line ok; + ok; TIL -> %% Verify that we havn't got anything allocated by temp_alloc lists:foreach( fun ({instance, _, TI}) -> - ?line {value, {mbcs, MBCInfo}} + {value, {mbcs, MBCInfo}} = lists:keysearch(mbcs, 1, TI), - ?line {value, {blocks, 0, _, _}} + {value, {blocks, 0, _, _}} = lists:keysearch(blocks, 1, MBCInfo), - ?line {value, {sbcs, SBCInfo}} + {value, {sbcs, SBCInfo}} = lists:keysearch(sbcs, 1, TI), - ?line {value, {blocks, 0, _, _}} + {value, {blocks, 0, _, _}} = lists:keysearch(blocks, 1, SBCInfo) end, TIL), - ?line ok + ok end. @@ -194,7 +194,7 @@ load_driver(Dir, Driver) -> end. stop_driver(Port, Name) -> - ?line true = erlang:port_close(Port), + true = erlang:port_close(Port), receive {Port,Message} -> ct:fail({strange_message_from_port,Message}) @@ -204,7 +204,7 @@ stop_driver(Port, Name) -> %% Unload the driver. ok = erl_ddll:unload_driver(Name), - ?line ok = erl_ddll:stop(). + ok = erl_ddll:stop(). get_external_terms(DataDir) -> {ok, Bin} = file:read_file([DataDir, "ext_terms.bin"]), @@ -236,37 +236,36 @@ generate_external_terms_files(BaseDir) -> RPort = hd(rpc:call(Node, erlang, ports, [])), true = is_port(RPort), slave:stop(Node), - Terms = - [{4711, -4711, [an_atom, "a list"]}, - [1000000000000000000000,-1111111111111111, "blupp!", blipp], - {RPid, {RRef, RPort}, self(), hd(erlang:ports()), make_ref()}, - {{}, [], [], fun () -> ok end, <<"hej hopp trallalaaaaaaaaaaaaaaa">>}, - [44444444444444444444444,-44444444444, "b!", blippppppp], - {4711, RPid, {RRef, RPort}, -4711, [an_atom, "a list"]}, - {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())}, - {4711, -4711, [an_atom, "a list"]}, - {4711, -4711, [atom, "list"]}, - {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())}, - {4444444444444444444,-44444, {{{{{{{{{{{{}}}}}}}}}}}}, make_ref()}, - {444444444444444444444,-44444, [[[[[[[[[[[1]]]]]]]]]]], make_ref()}, - {444444444444444444,-44444, {{{{{{{{{{{{2}}}}}}}}}}}}, make_ref()}, - {4444444444444444444444,-44444, {{{{{{{{{{{{3}}}}}}}}}}}}, make_ref()}, - {44444444444444444444,-44444, {{{{{{{{{{{{4}}}}}}}}}}}}, make_ref()}, - {4444444444444444,-44444, [[[[[[[[[[[5]]]]]]]]]]], make_ref()}, - {444444444444444444444,-44444, {{{{{{{{{{{{6}}}}}}}}}}}}, make_ref()}, - {444444444444444,-44444, {{{{{{{{{{{{7}}}}}}}}}}}}, make_ref()}, - {4444444444444444444,-44444, {{{{{{{{{{{{8}}}}}}}}}}}}, make_ref()}, - #{}, - #{1 => 11, 2 => 22, 3 => 33}, - maps:from_list([{K,K*11} || K <- lists:seq(1,100)])], + Terms = [{4711, -4711, [an_atom, "a list"]}, + [1000000000000000000000,-1111111111111111, "blupp!", blipp], + {RPid, {RRef, RPort}, self(), hd(erlang:ports()), make_ref()}, + {{}, [], [], fun () -> ok end, <<"hej hopp trallalaaaaaaaaaaaaaaa">>}, + [44444444444444444444444,-44444444444, "b!", blippppppp], + {4711, RPid, {RRef, RPort}, -4711, [an_atom, "a list"]}, + {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())}, + {4711, -4711, [an_atom, "a list"]}, + {4711, -4711, [atom, "list"]}, + {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())}, + {4444444444444444444,-44444, {{{{{{{{{{{{}}}}}}}}}}}}, make_ref()}, + {444444444444444444444,-44444, [[[[[[[[[[[1]]]]]]]]]]], make_ref()}, + {444444444444444444,-44444, {{{{{{{{{{{{2}}}}}}}}}}}}, make_ref()}, + {4444444444444444444444,-44444, {{{{{{{{{{{{3}}}}}}}}}}}}, make_ref()}, + {44444444444444444444,-44444, {{{{{{{{{{{{4}}}}}}}}}}}}, make_ref()}, + {4444444444444444,-44444, [[[[[[[[[[[5]]]]]]]]]]], make_ref()}, + {444444444444444444444,-44444, {{{{{{{{{{{{6}}}}}}}}}}}}, make_ref()}, + {444444444444444,-44444, {{{{{{{{{{{{7}}}}}}}}}}}}, make_ref()}, + {4444444444444444444,-44444, {{{{{{{{{{{{8}}}}}}}}}}}}, make_ref()}, + #{}, + #{1 => 11, 2 => 22, 3 => 33}, + maps:from_list([{K,K*11} || K <- lists:seq(1,100)])], ok = file:write_file(filename:join([BaseDir, - "send_term_SUITE_data", - "ext_terms.bin"]), - term_to_binary(Terms, [compressed])), + "send_term_SUITE_data", + "ext_terms.bin"]), + term_to_binary(Terms, [compressed])), {ok, IoDev} = file:open(filename:join([BaseDir, - "send_term_SUITE_data", - "ext_terms.h"]), - [write]), + "send_term_SUITE_data", + "ext_terms.h"]), + [write]), write_ext_terms_h(IoDev, Terms), file:close(IoDev). @@ -276,12 +275,12 @@ write_ext_terms_h(IoDev, Terms) -> io:format(IoDev, "#define EXT_TERMS_H__~n",[]), {ExtTerms, MaxSize} = make_ext_terms(Terms), io:format(IoDev, - "static struct {~n" - " unsigned char ext[~p];~n" - " int ext_size;~n" - " unsigned char cext[~p];~n" - " int cext_size;~n" - "} ext_terms[] = {~n",[MaxSize, MaxSize]), + "static struct {~n" + " unsigned char ext[~p];~n" + " int ext_size;~n" + " unsigned char cext[~p];~n" + " int cext_size;~n" + "} ext_terms[] = {~n",[MaxSize, MaxSize]), E = write_ext_terms_h(IoDev, ExtTerms, 0), io:format(IoDev, "};~n",[]), io:format(IoDev, "#define NO_OF_EXT_TERMS ~p~n", [E]), diff --git a/erts/emulator/test/sensitive_SUITE.erl b/erts/emulator/test/sensitive_SUITE.erl index 618044c153..e2236774a7 100644 --- a/erts/emulator/test/sensitive_SUITE.erl +++ b/erts/emulator/test/sensitive_SUITE.erl @@ -23,9 +23,9 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, - stickiness/1,send_trace/1,recv_trace/1,proc_trace/1,call_trace/1, - meta_trace/1,running_trace/1,gc_trace/1,seq_trace/1, - t_process_info/1,t_process_display/1,save_calls/1]). + stickiness/1,send_trace/1,recv_trace/1,proc_trace/1,call_trace/1, + meta_trace/1,running_trace/1,gc_trace/1,seq_trace/1, + t_process_info/1,t_process_display/1,save_calls/1]). -export([remote_process_display/0,an_exported_function/1]). @@ -42,147 +42,147 @@ all() -> save_calls]. stickiness(Config) when is_list(Config) -> - ?line {Tracer,Mref} = spawn_monitor(fun() -> - receive after infinity -> ok end - end), - ?line false = process_flag(sensitive, true), + {Tracer,Mref} = spawn_monitor(fun() -> + receive after infinity -> ok end + end), + false = process_flag(sensitive, true), put(foo, bar), Flags = sort([send,'receive',procs,call,running,garbage_collection, - set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link]), - ?line foreach(fun(F) -> - 1 = erlang:trace(self(), true, [F,{tracer,Tracer}]) - end, Flags), - ?line foreach(fun(F) -> - 1 = erlang:trace(self(), false, [F,{tracer,Tracer}]) - end, Flags), - ?line 1 = erlang:trace(self(), true, [{tracer,Tracer}|Flags]), - ?line 1 = erlang:trace(self(), false, [{tracer,Tracer}|Flags]), - - ?line {messages,[]} = process_info(Tracer, messages), + set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link]), + foreach(fun(F) -> + 1 = erlang:trace(self(), true, [F,{tracer,Tracer}]) + end, Flags), + foreach(fun(F) -> + 1 = erlang:trace(self(), false, [F,{tracer,Tracer}]) + end, Flags), + 1 = erlang:trace(self(), true, [{tracer,Tracer}|Flags]), + 1 = erlang:trace(self(), false, [{tracer,Tracer}|Flags]), + + {messages,[]} = process_info(Tracer, messages), exit(Tracer, kill), receive {'DOWN',Mref,_,_,_} -> ok end, - + case process_info(self(), dictionary) of - {dictionary,[]} -> ok; - {dictionary,_} -> ?line ct:fail(sensitive_flag_cleared) + {dictionary,[]} -> ok; + {dictionary,_} -> ct:fail(sensitive_flag_cleared) end, NewTracer = spawn_link(fun() -> receive after infinity -> ok end end), - ?line 1 = erlang:trace(self(), true, [{tracer,NewTracer}|Flags]), - ?line Flags = sort(element(2, erlang:trace_info(self(), flags))), - ?line {tracer,NewTracer} = erlang:trace_info(self(), tracer), + 1 = erlang:trace(self(), true, [{tracer,NewTracer}|Flags]), + Flags = sort(element(2, erlang:trace_info(self(), flags))), + {tracer,NewTracer} = erlang:trace_info(self(), tracer), %% Process still sensitive. Tracer should disappear when we clear %% all trace flags. - ?line 1 = erlang:trace(self(), false, [{tracer,NewTracer}|Flags]), - ?line {tracer,[]} = erlang:trace_info(self(), tracer), + 1 = erlang:trace(self(), false, [{tracer,NewTracer}|Flags]), + {tracer,[]} = erlang:trace_info(self(), tracer), - ?line unlink(NewTracer), exit(NewTracer, kill), + unlink(NewTracer), exit(NewTracer, kill), ok. send_trace(Config) when is_list(Config) -> - ?line {Dead,Mref} = spawn_monitor(fun() -> ok end), + {Dead,Mref} = spawn_monitor(fun() -> ok end), receive {'DOWN',Mref,_,_,_} -> ok end, - ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end), - ?line Sink = spawn_link(fun() -> receive after infinity -> ok end end), + Tracer = spawn_link(fun() -> receive after infinity -> ok end end), + Sink = spawn_link(fun() -> receive after infinity -> ok end end), Self = self(), - ?line 1 = erlang:trace(self(), true, [send,{tracer,Tracer}]), - ?line Dead ! before, - ?line Sink ! before, - ?line false = process_flag(sensitive, true), - ?line Sink ! {blurf,lists:seq(1, 50)}, - ?line true = process_flag(sensitive, true), - ?line Sink ! lists:seq(1, 100), - ?line Dead ! forget_me, - ?line true = process_flag(sensitive, false), - ?line Sink ! after1, - ?line false = process_flag(sensitive, false), - ?line Sink ! after2, - ?line Dead ! after2, - ?line wait_trace(Self), - - ?line {messages,Messages} = process_info(Tracer, messages), - ?line [{trace,Self,send_to_non_existing_process,before,Dead}, - {trace,Self,send,before,Sink}, - {trace,Self,send,after1,Sink}, - {trace,Self,send,after2,Sink}, - {trace,Self,send_to_non_existing_process,after2,Dead}] = Messages, - - ?line unlink(Tracer), exit(Tracer, kill), - ?line unlink(Sink), exit(Sink, kill), + 1 = erlang:trace(self(), true, [send,{tracer,Tracer}]), + Dead ! before, + Sink ! before, + false = process_flag(sensitive, true), + Sink ! {blurf,lists:seq(1, 50)}, + true = process_flag(sensitive, true), + Sink ! lists:seq(1, 100), + Dead ! forget_me, + true = process_flag(sensitive, false), + Sink ! after1, + false = process_flag(sensitive, false), + Sink ! after2, + Dead ! after2, + wait_trace(Self), + + {messages,Messages} = process_info(Tracer, messages), + [{trace,Self,send_to_non_existing_process,before,Dead}, + {trace,Self,send,before,Sink}, + {trace,Self,send,after1,Sink}, + {trace,Self,send,after2,Sink}, + {trace,Self,send_to_non_existing_process,after2,Dead}] = Messages, + + unlink(Tracer), exit(Tracer, kill), + unlink(Sink), exit(Sink, kill), ok. recv_trace(Config) when is_list(Config) -> Parent = self(), - ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end), - ?line Sender = spawn_link(fun() -> recv_trace_sender(Parent) end), + Tracer = spawn_link(fun() -> receive after infinity -> ok end end), + Sender = spawn_link(fun() -> recv_trace_sender(Parent) end), - ?line 1 = erlang:trace(self(), true, ['receive',{tracer,Tracer}]), + 1 = erlang:trace(self(), true, ['receive',{tracer,Tracer}]), Sender ! 1, receive a -> wait_trace(Sender) end, - ?line false = process_flag(sensitive, true), + false = process_flag(sensitive, true), Sender ! 2, receive {b,[x,y,z]} -> wait_trace(Sender) end, - - ?line true = process_flag(sensitive, false), + + true = process_flag(sensitive, false), Sender ! 3, receive c -> wait_trace(Sender) end, - - ?line {messages,Messages} = process_info(Tracer, messages), + + {messages,Messages} = process_info(Tracer, messages), [{trace,Parent,'receive',a}, {trace,Parent,'receive',{trace_delivered,_,_}}, {trace,Parent,'receive',c}|_] = Messages, - ?line unlink(Tracer), exit(Tracer, kill), - ?line unlink(Sender), exit(Sender, kill), + unlink(Tracer), exit(Tracer, kill), + unlink(Sender), exit(Sender, kill), ok. recv_trace_sender(Pid) -> receive - 1 -> Pid ! a; - 2 -> Pid ! {b,[x,y,z]}; - 3 -> Pid ! c + 1 -> Pid ! a; + 2 -> Pid ! {b,[x,y,z]}; + 3 -> Pid ! c end, recv_trace_sender(Pid). proc_trace(Config) when is_list(Config) -> Self = self(), - ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end), + Tracer = spawn_link(fun() -> receive after infinity -> ok end end), - ?line 1 = erlang:trace(self(), true, [procs,{tracer,Tracer}]), - ?line false = process_flag(sensitive, true), + 1 = erlang:trace(self(), true, [procs,{tracer,Tracer}]), + false = process_flag(sensitive, true), spawn(fun() -> ok end), - ?line register(nisse, self()), - ?line unregister(nisse), - ?line link(Tracer), - ?line unlink(Tracer), - ?line Linker0 = spawn_link(fun() -> ok end), + register(nisse, self()), + unregister(nisse), + link(Tracer), + unlink(Tracer), + Linker0 = spawn_link(fun() -> ok end), Mref0 = erlang:monitor(process, Linker0), - ?line {_,Mref} = spawn_monitor(fun() -> link(Self), unlink(Self) end), + {_,Mref} = spawn_monitor(fun() -> link(Self), unlink(Self) end), receive {'DOWN',Mref0,_,_,_} -> ok end, receive {'DOWN',Mref,_,_,_} -> ok end, - ?line true = process_flag(sensitive, false), + true = process_flag(sensitive, false), Dead = spawn(fun() -> ok end), - ?line register(arne, self()), - ?line unregister(arne), - ?line {Linker,Mref2} = spawn_monitor(fun() -> link(Self), unlink(Self) end), + register(arne, self()), + unregister(arne), + {Linker,Mref2} = spawn_monitor(fun() -> link(Self), unlink(Self) end), receive {'DOWN',Mref2,_,_,_} -> ok end, - ?line Last = spawn_link(fun() -> ok end), + Last = spawn_link(fun() -> ok end), receive after 10 -> ok end, - ?line wait_trace(all), - ?line {messages,Messages} = process_info(Tracer, messages), + wait_trace(all), + {messages,Messages} = process_info(Tracer, messages), [{trace,Self,spawn,Dead,{erlang,apply,_}}, {trace,Self,register,arne}, {trace,Self,unregister,arne}, @@ -193,80 +193,80 @@ proc_trace(Config) when is_list(Config) -> {trace,Self,link,Last}, {trace,Self,getting_unlinked,Last}] = Messages, - ?line unlink(Tracer), exit(Tracer, kill), + unlink(Tracer), exit(Tracer, kill), ok. call_trace(Config) when is_list(Config) -> Self = self(), - ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end), - - ?line 1 = erlang:trace(self(), true, [call,{tracer,Tracer}]), - ?line 1 = erlang:trace_pattern({?MODULE,an_exported_function,1}, - true, [global]), - ?line 1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [global]), - ?line 1 = erlang:trace_pattern({erlang,binary_to_list,1}, true, [local]), - ?line Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [local]), - - ?line false = process_flag(sensitive, true), - ?line {ok,42} = a_local_function(42), - ?line 7 = an_exported_function(6), - ?line <<7,8,9,10>> = list_to_binary(id([7,8,9,10])), - ?line [42,43] = binary_to_list(id(<<42,43>>)), - ?line true = process_flag(sensitive, false), - - ?line {ok,{a,b}} = a_local_function({a,b}), - ?line 1 = an_exported_function(0), - ?line <<1,2,3>> = list_to_binary(id([1,2,3])), - ?line [42,43,44] = binary_to_list(id(<<42,43,44>>)), - - ?line wait_trace(Self), - - ?line {messages,Messages} = process_info(Tracer, messages), - ?line [{trace,Self,call,{?MODULE,a_local_function,[{a,b}]}}, - {trace,Self,call,{?MODULE,an_exported_function,[0]}}, - {trace,Self,call,{?MODULE,id,[_]}}, - {trace,Self,call,{erlang,list_to_binary,[[1,2,3]]}}, - {trace,Self,call,{sensitive_SUITE,id,[<<42,43,44>>]}}, - {trace,Self,call,{erlang,binary_to_list,[<<42,43,44>>]}}, - {trace,Self,call,{?MODULE,wait_trace,[Self]}}] = Messages, - - ?line Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [local]), - ?line erlang:trace_pattern({erlang,'_','_'}, false, [local]), - ?line erlang:trace_pattern({'_','_','_'}, false, [global]), - - ?line unlink(Tracer), exit(Tracer, kill), + Tracer = spawn_link(fun() -> receive after infinity -> ok end end), + + 1 = erlang:trace(self(), true, [call,{tracer,Tracer}]), + 1 = erlang:trace_pattern({?MODULE,an_exported_function,1}, + true, [global]), + 1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [global]), + 1 = erlang:trace_pattern({erlang,binary_to_list,1}, true, [local]), + Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [local]), + + false = process_flag(sensitive, true), + {ok,42} = a_local_function(42), + 7 = an_exported_function(6), + <<7,8,9,10>> = list_to_binary(id([7,8,9,10])), + [42,43] = binary_to_list(id(<<42,43>>)), + true = process_flag(sensitive, false), + + {ok,{a,b}} = a_local_function({a,b}), + 1 = an_exported_function(0), + <<1,2,3>> = list_to_binary(id([1,2,3])), + [42,43,44] = binary_to_list(id(<<42,43,44>>)), + + wait_trace(Self), + + {messages,Messages} = process_info(Tracer, messages), + [{trace,Self,call,{?MODULE,a_local_function,[{a,b}]}}, + {trace,Self,call,{?MODULE,an_exported_function,[0]}}, + {trace,Self,call,{?MODULE,id,[_]}}, + {trace,Self,call,{erlang,list_to_binary,[[1,2,3]]}}, + {trace,Self,call,{sensitive_SUITE,id,[<<42,43,44>>]}}, + {trace,Self,call,{erlang,binary_to_list,[<<42,43,44>>]}}, + {trace,Self,call,{?MODULE,wait_trace,[Self]}}] = Messages, + + Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [local]), + erlang:trace_pattern({erlang,'_','_'}, false, [local]), + erlang:trace_pattern({'_','_','_'}, false, [global]), + + unlink(Tracer), exit(Tracer, kill), ok. meta_trace(Config) when is_list(Config) -> Self = self(), - ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end), - - ?line Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [{meta,Tracer}]), - ?line 1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [{meta,Tracer}]), - - ?line false = process_flag(sensitive, true), - ?line {ok,blurf} = a_local_function(blurf), - ?line 100 = an_exported_function(99), - ?line <<8,9,10>> = list_to_binary(id([8,9,10])), - ?line true = process_flag(sensitive, false), - - ?line {ok,{x,y}} = a_local_function({x,y}), - ?line 1 = an_exported_function(0), - ?line <<10>> = list_to_binary(id([10])), - ?line wait_trace(Self), - - ?line Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [meta]), - ?line 1 = erlang:trace_pattern({erlang,list_to_binary,1}, false, [meta]), - ?line a_local_function(0), - - ?line {messages,Messages} = process_info(Tracer, messages), - ?line [{trace_ts,Self,call,{?MODULE,a_local_function,[{x,y}]},{_,_,_}}, - {trace_ts,Self,call,{?MODULE,an_exported_function,[0]},{_,_,_}}, - {trace_ts,Self,call,{?MODULE,id,[_]},{_,_,_}}, - {trace_ts,Self,call,{erlang,list_to_binary,[[10]]},{_,_,_}}, - {trace_ts,Self,call,{?MODULE,wait_trace,[Self]},{_,_,_}}] = Messages, - - ?line unlink(Tracer), exit(Tracer, kill), + Tracer = spawn_link(fun() -> receive after infinity -> ok end end), + + Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [{meta,Tracer}]), + 1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [{meta,Tracer}]), + + false = process_flag(sensitive, true), + {ok,blurf} = a_local_function(blurf), + 100 = an_exported_function(99), + <<8,9,10>> = list_to_binary(id([8,9,10])), + true = process_flag(sensitive, false), + + {ok,{x,y}} = a_local_function({x,y}), + 1 = an_exported_function(0), + <<10>> = list_to_binary(id([10])), + wait_trace(Self), + + Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [meta]), + 1 = erlang:trace_pattern({erlang,list_to_binary,1}, false, [meta]), + a_local_function(0), + + {messages,Messages} = process_info(Tracer, messages), + [{trace_ts,Self,call,{?MODULE,a_local_function,[{x,y}]},{_,_,_}}, + {trace_ts,Self,call,{?MODULE,an_exported_function,[0]},{_,_,_}}, + {trace_ts,Self,call,{?MODULE,id,[_]},{_,_,_}}, + {trace_ts,Self,call,{erlang,list_to_binary,[[10]]},{_,_,_}}, + {trace_ts,Self,call,{?MODULE,wait_trace,[Self]},{_,_,_}}] = Messages, + + unlink(Tracer), exit(Tracer, kill), ok. a_local_function(A) -> @@ -277,66 +277,66 @@ an_exported_function(X) -> running_trace(Config) when is_list(Config) -> Self = self(), - ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end), + Tracer = spawn_link(fun() -> receive after infinity -> ok end end), - ?line false = process_flag(sensitive, true), - ?line 1 = erlang:trace(Self, true, [running,{tracer,Tracer}]), + false = process_flag(sensitive, true), + 1 = erlang:trace(Self, true, [running,{tracer,Tracer}]), erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(), - ?line true = process_flag(sensitive, false), + true = process_flag(sensitive, false), erlang:yield(), - ?line 1 = erlang:trace(Self, false, [running,{tracer,Tracer}]), + 1 = erlang:trace(Self, false, [running,{tracer,Tracer}]), - ?line wait_trace(Self), - ?line {messages,Messages} = process_info(Tracer, messages), - ?line [{trace,Self,out,{sensitive_SUITE,running_trace,1}}, - {trace,Self,in,{sensitive_SUITE,running_trace,1}}] = Messages, + wait_trace(Self), + {messages,Messages} = process_info(Tracer, messages), + [{trace,Self,out,{sensitive_SUITE,running_trace,1}}, + {trace,Self,in,{sensitive_SUITE,running_trace,1}}] = Messages, - ?line unlink(Tracer), exit(Tracer, kill), + unlink(Tracer), exit(Tracer, kill), ok. gc_trace(Config) when is_list(Config) -> Self = self(), - ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end), + Tracer = spawn_link(fun() -> receive after infinity -> ok end end), - ?line false = process_flag(sensitive, true), - ?line 1 = erlang:trace(Self, true, [garbage_collection,{tracer,Tracer}]), + false = process_flag(sensitive, true), + 1 = erlang:trace(Self, true, [garbage_collection,{tracer,Tracer}]), erlang:garbage_collect(), erlang:garbage_collect(), erlang:garbage_collect(), erlang:garbage_collect(), erlang:garbage_collect(), erlang:garbage_collect(), erlang:garbage_collect(), erlang:garbage_collect(), - ?line true = process_flag(sensitive, false), + true = process_flag(sensitive, false), erlang:garbage_collect(), - ?line 1 = erlang:trace(Self, false, [garbage_collection,{tracer,Tracer}]), + 1 = erlang:trace(Self, false, [garbage_collection,{tracer,Tracer}]), - ?line wait_trace(Self), - ?line {messages,Messages} = process_info(Tracer, messages), - ?line [{trace,Self,gc_start,_},{trace,Self,gc_end,_}] = Messages, + wait_trace(Self), + {messages,Messages} = process_info(Tracer, messages), + [{trace,Self,gc_start,_},{trace,Self,gc_end,_}] = Messages, - ?line unlink(Tracer), exit(Tracer, kill), + unlink(Tracer), exit(Tracer, kill), ok. seq_trace(Config) when is_list(Config) -> Self = self(), - ?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end), - ?line seq_trace:set_system_tracer(Tracer), - - ?line false = process_flag(sensitive, true), - - ?line Echo = spawn_link(fun() -> - receive - {Pid,Message} -> - Pid ! {reply,Message} - end - end), - ?line Sender = spawn_link(fun() -> - seq_trace:set_token(label, 42), - seq_trace:set_token('receive', true), - seq_trace:set_token(send, true), - seq_trace:set_token(print, true), - seq_trace:print(42, "trace started"), - Self ! blurf - end), + Tracer = spawn_link(fun() -> receive after infinity -> ok end end), + seq_trace:set_system_tracer(Tracer), + + false = process_flag(sensitive, true), + + Echo = spawn_link(fun() -> + receive + {Pid,Message} -> + Pid ! {reply,Message} + end + end), + Sender = spawn_link(fun() -> + seq_trace:set_token(label, 42), + seq_trace:set_token('receive', true), + seq_trace:set_token(send, true), + seq_trace:set_token(print, true), + seq_trace:print(42, "trace started"), + Self ! blurf + end), seq_trace:set_token(label, 17), seq_trace:set_token('receive', true), seq_trace:set_token(send, true), @@ -346,49 +346,49 @@ seq_trace(Config) when is_list(Config) -> receive {reply,hello} -> ok end, receive blurf -> ok end, - ?line wait_trace(all), + wait_trace(all), + + {messages,Messages} = process_info(Tracer, messages), + [{seq_trace,17,{'receive',{0,2},Self,Echo,{Self,hello}}}, + {seq_trace,17,{send,{2,3},Echo,Self,{reply,hello}}}] = + [M || {seq_trace,17,_}=M <- Messages], - ?line {messages,Messages} = process_info(Tracer, messages), - ?line [{seq_trace,17,{'receive',{0,2},Self,Echo,{Self,hello}}}, - {seq_trace,17,{send,{2,3},Echo,Self,{reply,hello}}}] = - [M || {seq_trace,17,_}=M <- Messages], + [{seq_trace,42,{print,{0,1},Sender,[],"trace started"}}, + {seq_trace,42,{send,{0,2},Sender,Self,blurf}}] = + [M || {seq_trace,42,_}=M <- Messages], - ?line [{seq_trace,42,{print,{0,1},Sender,[],"trace started"}}, - {seq_trace,42,{send,{0,2},Sender,Self,blurf}}] = - [M || {seq_trace,42,_}=M <- Messages], - - ?line unlink(Tracer), exit(Tracer, kill), - ?line unlink(Echo), exit(Echo, kill), - ?line unlink(Sender), exit(Sender, kill), + unlink(Tracer), exit(Tracer, kill), + unlink(Echo), exit(Echo, kill), + unlink(Sender), exit(Sender, kill), ok. t_process_info(Config) when is_list(Config) -> Parent = self(), - ?line Pid = spawn_link(fun() -> - put(foo, bar), - false = process_flag(sensitive, true), - Parent ! go, - receive - revert -> - true = process_flag(sensitive, false), - Parent ! go_again, - receive never -> ok end - end end), + Pid = spawn_link(fun() -> + put(foo, bar), + false = process_flag(sensitive, true), + Parent ! go, + receive + revert -> + true = process_flag(sensitive, false), + Parent ! go_again, + receive never -> ok end + end end), receive go -> ok end, - ?line put(foo, bar), - ?line self() ! Pid ! {i,am,a,message}, + put(foo, bar), + self() ! Pid ! {i,am,a,message}, - ?line false = process_flag(sensitive, true), - ?line t_process_info_suppressed(self()), - ?line t_process_info_suppressed(Pid), + false = process_flag(sensitive, true), + t_process_info_suppressed(self()), + t_process_info_suppressed(Pid), - ?line true = process_flag(sensitive, false), + true = process_flag(sensitive, false), Pid ! revert, receive go_again -> ok end, - ?line t_process_info_normal(self()), - ?line t_process_info_normal(Pid), + t_process_info_normal(self()), + t_process_info_normal(Pid), ok. t_process_info_suppressed(Pid) -> @@ -399,7 +399,7 @@ t_process_info_suppressed(Pid) -> t_process_info_normal(Pid) -> {value,{foo,bar}} = keysearch(foo, 1, my_process_info(Pid, dictionary)), case process_info(Pid, backtrace) of - {backtrace,Bin} when size(Bin) > 20 -> ok + {backtrace,Bin} when size(Bin) > 20 -> ok end, [{i,am,a,message}] = my_process_info(Pid, messages). @@ -407,16 +407,16 @@ my_process_info(Pid, Tag) -> {Tag,Value} = process_info(Pid, Tag), All = process_info(Pid), case keysearch(Tag, 1, All) of - false -> Value; - {value,{Tag,Value}} -> Value + false -> Value; + {value,{Tag,Value}} -> Value end. t_process_display(Config) when is_list(Config) -> - ?line Dir = filename:dirname(code:which(?MODULE)), - ?line Cmd = atom_to_list(lib:progname()) ++ " -noinput -pa " ++ Dir ++ - " -run " ++ ?MODULE_STRING ++ " remote_process_display", - ?line io:put_chars(Cmd), - ?line P = open_port({spawn,Cmd}, [in,stderr_to_stdout,eof]), + Dir = filename:dirname(code:which(?MODULE)), + Cmd = atom_to_list(lib:progname()) ++ " -noinput -pa " ++ Dir ++ + " -run " ++ ?MODULE_STRING ++ " remote_process_display", + io:put_chars(Cmd), + P = open_port({spawn,Cmd}, [in,stderr_to_stdout,eof]), <<"done",_/binary>> = get_all(P), ok. @@ -432,27 +432,26 @@ get_all(P) -> get_all(P, Acc) -> receive - {P,{data,S}} -> - get_all(P, [Acc|S]); - {P,eof} -> - iolist_to_binary(Acc) + {P,{data,S}} -> + get_all(P, [Acc|S]); + {P,eof} -> + iolist_to_binary(Acc) end. save_calls(Config) when is_list(Config) -> process_flag(save_calls, 10), false = process_flag(sensitive, true), - ?line {last_calls,LastCalls} = process_info(self(), last_calls), - ?line [{erlang,process_flag,2}] = LastCalls, - ?line [2,4,6] = lists:map(fun(E) -> 2*E end, [1,2,3]), - ?line {last_calls,LastCalls} = process_info(self(), last_calls), + {last_calls,LastCalls} = process_info(self(), last_calls), + [{erlang,process_flag,2}] = LastCalls, + [2,4,6] = lists:map(fun(E) -> 2*E end, [1,2,3]), + {last_calls,LastCalls} = process_info(self(), last_calls), ok. wait_trace(Pid) -> Ref = erlang:trace_delivered(Pid), receive - {trace_delivered,Pid,Ref} -> ok + {trace_delivered,Pid,Ref} -> ok end. - + id(I) -> I. - diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index f8e9bc5ac0..63397fbbb4 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -82,27 +82,27 @@ all() -> %% Test that exit signals and messages are received in correct order xm_sig_order(Config) when is_list(Config) -> - ?line LNode = node(), - ?line repeat(fun () -> xm_sig_order_test(LNode) end, 1000), - ?line {ok, RNode} = start_node(Config), - ?line repeat(fun () -> xm_sig_order_test(RNode) end, 1000), - ?line stop_node(RNode), - ?line ok. + LNode = node(), + repeat(fun () -> xm_sig_order_test(LNode) end, 1000), + {ok, RNode} = start_node(Config), + repeat(fun () -> xm_sig_order_test(RNode) end, 1000), + stop_node(RNode), + ok. xm_sig_order_test(Node) -> - ?line P = spawn(Node, fun () -> xm_sig_order_proc() end), - ?line M = erlang:monitor(process, P), - ?line P ! may_reach, - ?line P ! may_reach, - ?line P ! may_reach, - ?line exit(P, good_signal_order), - ?line P ! may_not_reach, - ?line P ! may_not_reach, - ?line P ! may_not_reach, - ?line receive + P = spawn(Node, fun () -> xm_sig_order_proc() end), + M = erlang:monitor(process, P), + P ! may_reach, + P ! may_reach, + P ! may_reach, + exit(P, good_signal_order), + P ! may_not_reach, + P ! may_not_reach, + P ! may_not_reach, + receive {'DOWN', M, process, P, R} -> - ?line good_signal_order = R + good_signal_order = R end. xm_sig_order_proc() -> @@ -114,99 +114,96 @@ xm_sig_order_proc() -> xm_sig_order_proc(). pending_exit_unlink_process(Config) when is_list(Config) -> - ?line pending_exit_test(self(), unlink). + pending_exit_test(self(), unlink). pending_exit_unlink_dist_process(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(Config), - ?line From = spawn(Node, fun () -> receive after infinity -> ok end end), - ?line Res = pending_exit_test(From, unlink), - ?line stop_node(Node), - ?line Res. + {ok, Node} = start_node(Config), + From = spawn(Node, fun () -> receive after infinity -> ok end end), + Res = pending_exit_test(From, unlink), + stop_node(Node), + Res. pending_exit_unlink_port(Config) when is_list(Config) -> - ?line pending_exit_test(hd(erlang:ports()), unlink). + pending_exit_test(hd(erlang:ports()), unlink). pending_exit_trap_exit(Config) when is_list(Config) -> - ?line pending_exit_test(self(), trap_exit). + pending_exit_test(self(), trap_exit). pending_exit_receive(Config) when is_list(Config) -> - ?line pending_exit_test(self(), 'receive'). + pending_exit_test(self(), 'receive'). pending_exit_exit(Config) when is_list(Config) -> - ?line pending_exit_test(self(), exit). + pending_exit_test(self(), exit). pending_exit_gc(Config) when is_list(Config) -> - ?line pending_exit_test(self(), gc). + pending_exit_test(self(), gc). pending_exit_test(From, Type) -> - ?line case catch erlang:system_info(smp_support) of - true -> - ?line OTE = process_flag(trap_exit, true), - ?line Ref = make_ref(), - ?line Master = self(), - ?line ExitBySignal = case Type of - gc -> - lists:duplicate(10000, - exit_by_signal); - _ -> - exit_by_signal - end, - ?line Pid = spawn_link( - fun () -> - receive go -> ok end, - false = have_pending_exit(), - exit = fake_exit(From, - self(), - ExitBySignal), - true = have_pending_exit(), - Master ! {self(), Ref, Type}, - case Type of - gc -> - force_gc(), - erlang:yield(); - unlink -> - unlink(From); - trap_exit -> - process_flag(trap_exit, true); - 'receive' -> - receive _ -> ok - after 0 -> ok - end; - exit -> - ok - end, - exit(exit_by_myself) - end), - ?line Mon = erlang:monitor(process, Pid), - ?line Pid ! go, - ?line Reason = receive - {'DOWN', Mon, process, Pid, R} -> - ?line receive - {Pid, Ref, Type} -> - ?line ok - after 0 -> - ?line ct:fail(premature_exit) - end, - ?line case Type of - exit -> - ?line exit_by_myself = R; - _ -> - ?line ExitBySignal = R - end - end, - ?line receive - {'EXIT', Pid, R2} -> - ?line Reason = R2 - end, - ?line process_flag(trap_exit, OTE), - ?line ok, - {comment, - "Test only valid with current SMP emulator."}; - _ -> - {skipped, - "SMP support not enabled. " - "Test only valid with current SMP emulator."} - end. + case catch erlang:system_info(smp_support) of + true -> + OTE = process_flag(trap_exit, true), + Ref = make_ref(), + Master = self(), + ExitBySignal = case Type of + gc -> + lists:duplicate(10000, + exit_by_signal); + _ -> + exit_by_signal + end, + Pid = spawn_link( + fun () -> + receive go -> ok end, + false = have_pending_exit(), + exit = fake_exit(From, + self(), + ExitBySignal), + true = have_pending_exit(), + Master ! {self(), Ref, Type}, + case Type of + gc -> + force_gc(), + erlang:yield(); + unlink -> + unlink(From); + trap_exit -> + process_flag(trap_exit, true); + 'receive' -> + receive _ -> ok + after 0 -> ok + end; + exit -> + ok + end, + exit(exit_by_myself) + end), + Mon = erlang:monitor(process, Pid), + Pid ! go, + Reason = receive + {'DOWN', Mon, process, Pid, R} -> + receive + {Pid, Ref, Type} -> + ok + after 0 -> + ct:fail(premature_exit) + end, + case Type of + exit -> + exit_by_myself = R; + _ -> + ExitBySignal = R + end + end, + receive + {'EXIT', Pid, R2} -> + Reason = R2 + end, + process_flag(trap_exit, OTE), + ok, + {comment, "Test only valid with current SMP emulator."}; + _ -> + {skipped, "SMP support not enabled. Test only valid with current SMP emulator."} + end. @@ -216,49 +213,49 @@ exit_before_pending_exit(Config) when is_list(Config) -> %% %% The testcase tries to check that a process can %% exit by itself even though it has a pending exit. - ?line OTE = process_flag(trap_exit, true), - ?line Master = self(), - ?line Tester = spawn_link( - fun () -> - Opts = case {erlang:system_info(run_queues), - erlang:system_info(schedulers_online)} of - {RQ, SO} when RQ =:= 1; SO =:= 1 -> []; - _ -> - process_flag(scheduler, 1), - [{scheduler, 2}] - end, - P = self(), - Exiter = spawn_opt(fun () -> - receive - {exit_me, P, R} -> - exit(P, R) - end - end, Opts), - erlang:yield(), - Exiter ! {exit_me, self(), exited_by_exiter}, - %% We want to get a pending exit - %% before we exit ourselves. We - %% don't want to be scheduled out - %% since we will then see the - %% pending exit. - %% - %% Do something that takes - %% relatively long time but - %% consumes few reductions... - repeat(fun() -> erlang:system_info(procs) end,10), - %% ... then exit. - Master ! {self(), - pending_exit, - have_pending_exit()}, - exit(exited_by_myself) - end), - ?line PendingExit = receive {Tester, pending_exit, PE} -> PE end, - ?line receive + OTE = process_flag(trap_exit, true), + Master = self(), + Tester = spawn_link( + fun () -> + Opts = case {erlang:system_info(run_queues), + erlang:system_info(schedulers_online)} of + {RQ, SO} when RQ =:= 1; SO =:= 1 -> []; + _ -> + process_flag(scheduler, 1), + [{scheduler, 2}] + end, + P = self(), + Exiter = spawn_opt(fun () -> + receive + {exit_me, P, R} -> + exit(P, R) + end + end, Opts), + erlang:yield(), + Exiter ! {exit_me, self(), exited_by_exiter}, + %% We want to get a pending exit + %% before we exit ourselves. We + %% don't want to be scheduled out + %% since we will then see the + %% pending exit. + %% + %% Do something that takes + %% relatively long time but + %% consumes few reductions... + repeat(fun() -> erlang:system_info(procs) end,10), + %% ... then exit. + Master ! {self(), + pending_exit, + have_pending_exit()}, + exit(exited_by_myself) + end), + PendingExit = receive {Tester, pending_exit, PE} -> PE end, + receive {'EXIT', Tester, exited_by_myself} -> - ?line process_flag(trap_exit, OTE), - ?line ok; + process_flag(trap_exit, OTE), + ok; Msg -> - ?line ct:fail({unexpected_message, Msg}) + ct:fail({unexpected_message, Msg}) end, NoScheds = integer_to_list(erlang:system_info(schedulers_online)), {comment, @@ -273,101 +270,101 @@ exit_before_pending_exit(Config) when is_list(Config) -> -define(PE_INFO_REPEAT, 100). pending_exit_is_process_alive(Config) when is_list(Config) -> - ?line S = exit_op_test_init(), - ?line TestFun = fun (P) -> false = is_process_alive(P) end, - ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), - ?line verify_pending_exit_success(S), - ?line comment(). + S = exit_op_test_init(), + TestFun = fun (P) -> false = is_process_alive(P) end, + repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), + verify_pending_exit_success(S), + comment(). pending_exit_process_info_1(Config) when is_list(Config) -> - ?line S = exit_op_test_init(), - ?line TestFun = fun (P) -> + S = exit_op_test_init(), + TestFun = fun (P) -> undefined = process_info(P) end, - ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), - ?line verify_pending_exit_success(S), - ?line comment(). + repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), + verify_pending_exit_success(S), + comment(). pending_exit_process_info_2(Config) when is_list(Config) -> - ?line S0 = exit_op_test_init(), - ?line repeated_exit_op_test(fun (P) -> + S0 = exit_op_test_init(), + repeated_exit_op_test(fun (P) -> undefined = process_info(P, messages) end, ?PE_INFO_REPEAT), - ?line S1 = verify_pending_exit_success(S0), - ?line repeated_exit_op_test(fun (P) -> + S1 = verify_pending_exit_success(S0), + repeated_exit_op_test(fun (P) -> undefined = process_info(P, status) end, ?PE_INFO_REPEAT), - ?line S2 = verify_pending_exit_success(S1), - ?line repeated_exit_op_test(fun (P) -> + S2 = verify_pending_exit_success(S1), + repeated_exit_op_test(fun (P) -> undefined = process_info(P, links) end, ?PE_INFO_REPEAT), - ?line S3 = verify_pending_exit_success(S2), - ?line repeated_exit_op_test(fun (P) -> + S3 = verify_pending_exit_success(S2), + repeated_exit_op_test(fun (P) -> undefined = process_info(P, [messages]) end, ?PE_INFO_REPEAT), - ?line S4 = verify_pending_exit_success(S3), - ?line repeated_exit_op_test(fun (P) -> + S4 = verify_pending_exit_success(S3), + repeated_exit_op_test(fun (P) -> undefined = process_info(P, [status]) end, ?PE_INFO_REPEAT), - ?line S5 = verify_pending_exit_success(S4), - ?line repeated_exit_op_test(fun (P) -> + S5 = verify_pending_exit_success(S4), + repeated_exit_op_test(fun (P) -> undefined = process_info(P, [links]) end, ?PE_INFO_REPEAT), - ?line S6 = verify_pending_exit_success(S5), - ?line repeated_exit_op_test(fun (P) -> + S6 = verify_pending_exit_success(S5), + repeated_exit_op_test(fun (P) -> undefined = process_info(P, [status, links]) end, ?PE_INFO_REPEAT), - ?line S7 = verify_pending_exit_success(S6), - ?line repeated_exit_op_test(fun (P) -> + S7 = verify_pending_exit_success(S6), + repeated_exit_op_test(fun (P) -> undefined = process_info(P, [messages, status]) end, ?PE_INFO_REPEAT), - ?line S8 = verify_pending_exit_success(S7), - ?line repeated_exit_op_test(fun (P) -> + S8 = verify_pending_exit_success(S7), + repeated_exit_op_test(fun (P) -> undefined = process_info(P, [messages, links]) end, ?PE_INFO_REPEAT), - ?line S9 = verify_pending_exit_success(S8), - ?line repeated_exit_op_test( + S9 = verify_pending_exit_success(S8), + repeated_exit_op_test( fun (P) -> undefined = process_info(P, [message_queue_len, status]) end, ?PE_INFO_REPEAT), - ?line S10 = verify_pending_exit_success(S9), - ?line repeated_exit_op_test(fun (P) -> + S10 = verify_pending_exit_success(S9), + repeated_exit_op_test(fun (P) -> undefined = process_info(P, [messages, links, status]) end, ?PE_INFO_REPEAT), - ?line verify_pending_exit_success(S10), - ?line comment(). + verify_pending_exit_success(S10), + comment(). pending_exit_process_display(Config) when is_list(Config) -> - ?line S = exit_op_test_init(), - ?line TestFun = fun (P) -> + S = exit_op_test_init(), + TestFun = fun (P) -> badarg = try erlang:process_display(P, backtrace) catch error:badarg -> badarg end end, - ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), - ?line verify_pending_exit_success(S), - ?line comment(). + repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), + verify_pending_exit_success(S), + comment(). pending_exit_group_leader(Config) when is_list(Config) -> - ?line S = exit_op_test_init(), - ?line TestFun = fun (P) -> + S = exit_op_test_init(), + TestFun = fun (P) -> badarg = try group_leader(self(), P) catch error:badarg -> badarg end end, - ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), - ?line verify_pending_exit_success(S), - ?line comment(). + repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), + verify_pending_exit_success(S), + comment(). %% %% -- Internal utils -------------------------------------------------------- @@ -509,15 +506,15 @@ fake_exit(From, To, Reason) -> available_internal_state(Bool) when Bool == true; Bool == false -> case {Bool, - (catch erts_debug:get_internal_state(available_internal_state))} of - {true, true} -> - true; - {false, true} -> - erts_debug:set_internal_state(available_internal_state, false), - true; - {true, _} -> - erts_debug:set_internal_state(available_internal_state, true), - false; - {false, _} -> - false + (catch erts_debug:get_internal_state(available_internal_state))} of + {true, true} -> + true; + {false, true} -> + erts_debug:set_internal_state(available_internal_state, false), + true; + {true, _} -> + erts_debug:set_internal_state(available_internal_state, true), + false; + {false, _} -> + false end. diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index af55ded470..7d5b07f21f 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -22,10 +22,7 @@ %% Tests the statistics/1 bif. --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, +-export([all/0, suite/0, groups/0, wall_clock_zero_diff/1, wall_clock_update/1, runtime_zero_diff/1, runtime_update/1, runtime_diff/1, @@ -40,12 +37,6 @@ -include_lib("common_test/include/ct.hrl"). -init_per_testcase(_, Config) -> - Config. - -end_per_testcase(_, _Config) -> - ok. - suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 4}}]. @@ -64,18 +55,6 @@ groups() -> [runtime_zero_diff, runtime_update, runtime_diff]}, {run_queue, [], [run_queue_one]}]. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - %%% Testing statistics(wall_clock). %% Tests that the 'Wall clock since last call' element of the result @@ -84,10 +63,10 @@ wall_clock_zero_diff(Config) when is_list(Config) -> wall_clock_zero_diff1(16). wall_clock_zero_diff1(N) when N > 0 -> - ?line {Time, _} = statistics(wall_clock), - ?line case statistics(wall_clock) of - {Time, 0} -> ok; - _ -> wall_clock_zero_diff1(N-1) + {Time, _} = statistics(wall_clock), + case statistics(wall_clock) of + {Time, 0} -> ok; + _ -> wall_clock_zero_diff1(N-1) end; wall_clock_zero_diff1(0) -> ct:fail("Difference never zero."). @@ -99,19 +78,19 @@ wall_clock_update(Config) when is_list(Config) -> wall_clock_update1(6). wall_clock_update1(N) when N > 0 -> - ?line {T1_wc_time, _} = statistics(wall_clock), - ?line receive after 1000 -> ok end, - ?line {T2_wc_time, Wc_Diff} = statistics(wall_clock), + {T1_wc_time, _} = statistics(wall_clock), + receive after 1000 -> ok end, + {T2_wc_time, Wc_Diff} = statistics(wall_clock), - ?line Wc_Diff = T2_wc_time - T1_wc_time, - ?line io:format("Wall clock diff = ~p; should be = 1000..1040~n", [Wc_Diff]), + Wc_Diff = T2_wc_time - T1_wc_time, + io:format("Wall clock diff = ~p; should be = 1000..1040~n", [Wc_Diff]), case test_server:is_debug() of - false -> - ?line true = Wc_Diff =< 1040; - true -> - ?line true = Wc_Diff =< 2000 %Be more tolerant in debug-compiled emulator. + false -> + true = Wc_Diff =< 1040; + true -> + true = Wc_Diff =< 2000 %Be more tolerant in debug-compiled emulator. end, - ?line true = Wc_Diff >= 1000, + true = Wc_Diff >= 1000, wall_clock_update1(N-1); wall_clock_update1(0) -> ok. @@ -123,14 +102,14 @@ wall_clock_update1(0) -> %% Tests that the difference between the times returned from two consectuitive %% calls to statistics(runtime) is zero. runtime_zero_diff(Config) when is_list(Config) -> - ?line runtime_zero_diff1(16). + runtime_zero_diff1(16). runtime_zero_diff1(N) when N > 0 -> - ?line {T1, _} = statistics(runtime), - ?line case statistics(runtime) of - {T1, 0} -> ok; - _ -> runtime_zero_diff1(N-1) - end; + {T1, _} = statistics(runtime), + case statistics(runtime) of + {T1, 0} -> ok; + _ -> runtime_zero_diff1(N-1) + end; runtime_zero_diff1(0) -> ct:fail("statistics(runtime) never returned zero difference"). @@ -139,27 +118,27 @@ runtime_zero_diff1(0) -> %% power of the Erlang process for a second. runtime_update(Config) when is_list(Config) -> case test_server:is_cover() of - false -> - ?line process_flag(priority, high), - do_runtime_update(10); - true -> - {skip,"Cover-compiled"} + false -> + process_flag(priority, high), + do_runtime_update(10); + true -> + {skip,"Cover-compiled"} end. do_runtime_update(0) -> {comment,"Never close enough"}; do_runtime_update(N) -> - ?line {T1,Diff0} = statistics(runtime), - ?line spawn_link(fun cpu_heavy/0), + {T1,Diff0} = statistics(runtime), + spawn_link(fun cpu_heavy/0), receive after 1000 -> ok end, - ?line {T2,Diff} = statistics(runtime), - ?line true = is_integer(T1+T2+Diff0+Diff), - ?line io:format("T1 = ~p, T2 = ~p, Diff = ~p, T2-T1 = ~p", [T1,T2,Diff,T2-T1]), - ?line if - T2 - T1 =:= Diff, 900 =< Diff, Diff =< 1500 -> ok; - true -> do_runtime_update(N-1) - end. - + {T2,Diff} = statistics(runtime), + true = is_integer(T1+T2+Diff0+Diff), + io:format("T1 = ~p, T2 = ~p, Diff = ~p, T2-T1 = ~p", [T1,T2,Diff,T2-T1]), + if + T2 - T1 =:= Diff, 900 =< Diff, Diff =< 1500 -> ok; + true -> do_runtime_update(N-1) + end. + cpu_heavy() -> cpu_heavy(). @@ -170,10 +149,10 @@ runtime_diff(Config) when is_list(Config) -> runtime_diff1(1000). runtime_diff1(N) when N > 0 -> - ?line {T1_wc_time, _} = statistics(runtime), - ?line do_much(), - ?line {T2_wc_time, Wc_Diff} = statistics(runtime), - ?line Wc_Diff = T2_wc_time - T1_wc_time, + {T1_wc_time, _} = statistics(runtime), + do_much(), + {T2_wc_time, Wc_Diff} = statistics(runtime), + Wc_Diff = T2_wc_time - T1_wc_time, runtime_diff1(N-1); runtime_diff1(0) -> ok. @@ -206,13 +185,13 @@ reductions(Config) when is_list(Config) -> reductions(300, Reductions, Mask). reductions(N, Previous, Mask) when N > 0 -> - ?line {Reductions, Diff} = statistics(reductions), - ?line build_some_garbage(), - ?line if Reductions > 0 -> ok end, - ?line if Diff >= 0 -> ok end, + {Reductions, Diff} = statistics(reductions), + build_some_garbage(), + if Reductions > 0 -> ok end, + if Diff >= 0 -> ok end, io:format("Previous = ~p, Reductions = ~p, Diff = ~p, DiffShouldBe = ~p", - [Previous, Reductions, Diff, (Reductions-Previous) band Mask]), - ?line if Reductions == ((Previous+Diff) band Mask) -> reductions(N-1, Reductions, Mask) end; + [Previous, Reductions, Diff, (Reductions-Previous) band Mask]), + if Reductions == ((Previous+Diff) band Mask) -> reductions(N-1, Reductions, Mask) end; reductions(0, _, _) -> ok. @@ -223,16 +202,16 @@ build_some_garbage() -> %% Test that the number of reductions can be returned as a big number. reductions_big(Config) when is_list(Config) -> - ?line reductions_big_loop(), + reductions_big_loop(), ok. reductions_big_loop() -> erlang:yield(), case statistics(reductions) of - {Red, Diff} when Red >= 16#7ffFFFF -> - ok = io:format("Reductions = ~w, Diff = ~w", [Red, Diff]); - _ -> - reductions_big_loop() + {Red, Diff} when Red >= 16#7ffFFFF -> + ok = io:format("Reductions = ~w, Diff = ~w", [Red, Diff]); + _ -> + reductions_big_loop() end. @@ -242,47 +221,47 @@ reductions_big_loop() -> %% Tests that statistics(run_queue) returns 1 if we start a %% CPU-bound process. run_queue_one(Config) when is_list(Config) -> - ?line MS = erlang:system_flag(multi_scheduling, block), - ?line run_queue_one_test(Config), - ?line erlang:system_flag(multi_scheduling, unblock), + MS = erlang:system_flag(multi_scheduling, block), + run_queue_one_test(Config), + erlang:system_flag(multi_scheduling, unblock), case MS of - blocked -> - {comment, - "Multi-scheduling blocked during test. This test-case " - "was not written to work with multiple schedulers."}; - _ -> ok + blocked -> + {comment, + "Multi-scheduling blocked during test. This test-case " + "was not written to work with multiple schedulers."}; + _ -> ok end. - + run_queue_one_test(Config) when is_list(Config) -> - ?line _Hog = spawn_link(?MODULE, hog, [self()]), - ?line receive - hog_started -> ok - end, - ?line receive after 100 -> ok end, % Give hog a head start. - ?line case statistics(run_queue) of - N when N >= 1 -> ok; - Other -> ct:fail({unexpected,Other}) - end, + _Hog = spawn_link(?MODULE, hog, [self()]), + receive + hog_started -> ok + end, + receive after 100 -> ok end, % Give hog a head start. + case statistics(run_queue) of + N when N >= 1 -> ok; + Other -> ct:fail({unexpected,Other}) + end, ok. %% CPU-bound process, going at low priority. It will always be ready %% to run. hog(Pid) -> - ?line process_flag(priority, low), - ?line Pid ! hog_started, - ?line Mon = erlang:monitor(process, Pid), - ?line hog_iter(0, Mon). + process_flag(priority, low), + Pid ! hog_started, + Mon = erlang:monitor(process, Pid), + hog_iter(0, Mon). hog_iter(N, Mon) when N > 0 -> receive - {'DOWN', Mon, _, _, _} -> ok + {'DOWN', Mon, _, _, _} -> ok after 0 -> - ?line hog_iter(N-1, Mon) + hog_iter(N-1, Mon) end; hog_iter(0, Mon) -> - ?line hog_iter(10000, Mon). + hog_iter(10000, Mon). %%% Tests of statistics(scheduler_wall_time). @@ -293,51 +272,51 @@ scheduler_wall_time(Config) when is_list(Config) -> %% Turn on statistics false = erlang:system_flag(scheduler_wall_time, true), try - Schedulers = erlang:system_info(schedulers_online), - %% Let testserver and everyone else finish their work - timer:sleep(1500), - %% Empty load - EmptyLoad = get_load(), - {false, _} = {lists:any(fun(Load) -> Load > 50 end, EmptyLoad),EmptyLoad}, - MeMySelfAndI = self(), - StartHog = fun() -> - Pid = spawn(?MODULE, hog, [self()]), - receive hog_started -> MeMySelfAndI ! go end, - Pid - end, - P1 = StartHog(), - %% Max on one, the other schedulers empty (hopefully) - %% Be generous the process can jump between schedulers - %% which is ok and we don't want the test to fail for wrong reasons - _L1 = [S1Load|EmptyScheds1] = get_load(), - {true,_} = {S1Load > 50,S1Load}, - {false,_} = {lists:any(fun(Load) -> Load > 50 end, EmptyScheds1),EmptyScheds1}, - {true,_} = {lists:sum(EmptyScheds1) < 60,EmptyScheds1}, - - %% 50% load - HalfHogs = [StartHog() || _ <- lists:seq(1, (Schedulers-1) div 2)], - HalfLoad = lists:sum(get_load()) div Schedulers, - if Schedulers < 2, HalfLoad > 80 -> ok; %% Ok only one scheduler online and one hog - %% We want roughly 50% load - HalfLoad > 40, HalfLoad < 60 -> ok; - true -> exit({halfload, HalfLoad}) - end, - - %% 100% load - LastHogs = [StartHog() || _ <- lists:seq(1, Schedulers div 2)], - FullScheds = get_load(), - {false,_} = {lists:any(fun(Load) -> Load < 80 end, FullScheds),FullScheds}, - FullLoad = lists:sum(FullScheds) div Schedulers, - if FullLoad > 90 -> ok; - true -> exit({fullload, FullLoad}) - end, - - [exit(Pid, kill) || Pid <- [P1|HalfHogs++LastHogs]], - AfterLoad = get_load(), - {false,_} = {lists:any(fun(Load) -> Load > 25 end, AfterLoad),AfterLoad}, - true = erlang:system_flag(scheduler_wall_time, false) + Schedulers = erlang:system_info(schedulers_online), + %% Let testserver and everyone else finish their work + timer:sleep(1500), + %% Empty load + EmptyLoad = get_load(), + {false, _} = {lists:any(fun(Load) -> Load > 50 end, EmptyLoad),EmptyLoad}, + MeMySelfAndI = self(), + StartHog = fun() -> + Pid = spawn(?MODULE, hog, [self()]), + receive hog_started -> MeMySelfAndI ! go end, + Pid + end, + P1 = StartHog(), + %% Max on one, the other schedulers empty (hopefully) + %% Be generous the process can jump between schedulers + %% which is ok and we don't want the test to fail for wrong reasons + _L1 = [S1Load|EmptyScheds1] = get_load(), + {true,_} = {S1Load > 50,S1Load}, + {false,_} = {lists:any(fun(Load) -> Load > 50 end, EmptyScheds1),EmptyScheds1}, + {true,_} = {lists:sum(EmptyScheds1) < 60,EmptyScheds1}, + + %% 50% load + HalfHogs = [StartHog() || _ <- lists:seq(1, (Schedulers-1) div 2)], + HalfLoad = lists:sum(get_load()) div Schedulers, + if Schedulers < 2, HalfLoad > 80 -> ok; %% Ok only one scheduler online and one hog + %% We want roughly 50% load + HalfLoad > 40, HalfLoad < 60 -> ok; + true -> exit({halfload, HalfLoad}) + end, + + %% 100% load + LastHogs = [StartHog() || _ <- lists:seq(1, Schedulers div 2)], + FullScheds = get_load(), + {false,_} = {lists:any(fun(Load) -> Load < 80 end, FullScheds),FullScheds}, + FullLoad = lists:sum(FullScheds) div Schedulers, + if FullLoad > 90 -> ok; + true -> exit({fullload, FullLoad}) + end, + + [exit(Pid, kill) || Pid <- [P1|HalfHogs++LastHogs]], + AfterLoad = get_load(), + {false,_} = {lists:any(fun(Load) -> Load > 25 end, AfterLoad),AfterLoad}, + true = erlang:system_flag(scheduler_wall_time, false) after - erlang:system_flag(scheduler_wall_time, false) + erlang:system_flag(scheduler_wall_time, false) end. get_load() -> @@ -354,56 +333,56 @@ load_percentage([], []) -> []. %% Tests that statistics(garbage_collection) is callable. %% It is not clear how to test anything more. garbage_collection(Config) when is_list(Config) -> - ?line Bin = list_to_binary(lists:duplicate(19999, 42)), - ?line case statistics(garbage_collection) of - {Gcs0,R,0} when is_integer(Gcs0), is_integer(R) -> - ?line io:format("Reclaimed: ~p", [R]), - ?line Gcs = garbage_collection_1(Gcs0, Bin), - ?line io:format("Reclaimed: ~p", - [element(2, statistics(garbage_collection))]), - {comment,integer_to_list(Gcs-Gcs0)++" GCs"} - end. + Bin = list_to_binary(lists:duplicate(19999, 42)), + case statistics(garbage_collection) of + {Gcs0,R,0} when is_integer(Gcs0), is_integer(R) -> + io:format("Reclaimed: ~p", [R]), + Gcs = garbage_collection_1(Gcs0, Bin), + io:format("Reclaimed: ~p", + [element(2, statistics(garbage_collection))]), + {comment,integer_to_list(Gcs-Gcs0)++" GCs"} + end. garbage_collection_1(Gcs0, Bin) -> case statistics(garbage_collection) of - {Gcs,Reclaimed,0} when Gcs >= Gcs0 -> - if - Reclaimed > 16#7ffffff -> - Gcs; - true -> - _ = binary_to_list(Bin), - erlang:garbage_collect(), - garbage_collection_1(Gcs, Bin) - end + {Gcs,Reclaimed,0} when Gcs >= Gcs0 -> + if + Reclaimed > 16#7ffffff -> + Gcs; + true -> + _ = binary_to_list(Bin), + erlang:garbage_collect(), + garbage_collection_1(Gcs, Bin) + end end. %% Tests that statistics(io) is callable. %% This could be improved to test something more. io(Config) when is_list(Config) -> - ?line case statistics(io) of - {{input,In},{output,Out}} when is_integer(In), is_integer(Out) -> ok - end. + case statistics(io) of + {{input,In},{output,Out}} when is_integer(In), is_integer(Out) -> ok + end. %% Tests that some illegal arguments to statistics fails. badarg(Config) when is_list(Config) -> - ?line case catch statistics(1) of - {'EXIT', {badarg, _}} -> ok - end, - ?line case catch statistics(bad_atom) of - {'EXIT', {badarg, _}} -> ok - end. + case catch statistics(1) of + {'EXIT', {badarg, _}} -> ok + end, + case catch statistics(bad_atom) of + {'EXIT', {badarg, _}} -> ok + end. tok_loop() -> tok_loop(). run_queues_lengths_active_tasks(Config) -> TokLoops = lists:map(fun (_) -> - spawn_opt(fun () -> - tok_loop() - end, - [link, {priority, low}]) - end, - lists:seq(1,10)), + spawn_opt(fun () -> + tok_loop() + end, + [link, {priority, low}]) + end, + lists:seq(1,10)), TRQLs0 = statistics(total_run_queue_lengths), TATs0 = statistics(total_active_tasks), @@ -446,10 +425,10 @@ run_queues_lengths_active_tasks(Config) -> erlang:system_flag(schedulers_online, SO), lists:foreach(fun (P) -> - unlink(P), - exit(P, bang) - end, - TokLoops), + unlink(P), + exit(P, bang) + end, + TokLoops), ok. @@ -479,9 +458,9 @@ msacc(Config) -> end, maps:to_list(msacc_sum_states())) ), if Sum > 0 -> - ok; + ok; true -> - ct:fail({no_states_triggered, MsaccStats}) + ct:fail({no_states_triggered, MsaccStats}) end; _ -> @@ -497,7 +476,7 @@ msacc(Config) -> %% aux will be zero if we do not have smp support %% or no async threads case erlang:system_info(smp_support) orelse - erlang:system_info(thread_pool_size) > 0 + erlang:system_info(thread_pool_size) > 0 of false -> ok; @@ -561,4 +540,4 @@ msacc_sum_states() -> NewValue = Value+maps:get(Key,Acc), maps:update(Key, NewValue, Acc) end, Cnt, Counters) - end,InitialCounters,Stats). + end,InitialCounters,Stats). diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index 71145d4595..ffea5bc15b 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -62,37 +62,37 @@ process_count(Config) when is_list(Config) -> end. process_count_test() -> - ?line OldPrio = process_flag(priority, max), - ?line check_procs(10), - ?line check_procs(11234), - ?line check_procs(57), - ?line check_procs(1030), - ?line check_procs(687), - ?line check_procs(7923), - ?line check_procs(5302), - ?line check_procs(12456), - ?line check_procs(14), - ?line check_procs(1125), - ?line check_procs(236), - ?line check_procs(125), - ?line check_procs(2346), - ?line process_flag(priority, OldPrio), - ?line ok. + OldPrio = process_flag(priority, max), + check_procs(10), + check_procs(11234), + check_procs(57), + check_procs(1030), + check_procs(687), + check_procs(7923), + check_procs(5302), + check_procs(12456), + check_procs(14), + check_procs(1125), + check_procs(236), + check_procs(125), + check_procs(2346), + process_flag(priority, OldPrio), + ok. check_procs(N) -> - ?line CP = length(processes()), - ?line Procs = start_procs(N), - ?line check_pc(CP+N), - ?line stop_procs(Procs), - ?line check_pc(CP). + CP = length(processes()), + Procs = start_procs(N), + check_pc(CP+N), + stop_procs(Procs), + check_pc(CP). check_pc(E) -> - ?line P = length(processes()), - ?line SI = erlang:system_info(process_count), - ?line io:format("E=~p; P=~p; SI=~p~n", [E, P, SI]), - ?line E = P, - ?line P = SI. + P = length(processes()), + SI = erlang:system_info(process_count), + io:format("E=~p; P=~p; SI=~p~n", [E, P, SI]), + E = P, + P = SI. start_procs(N) -> lists:map(fun (_) -> @@ -114,33 +114,33 @@ stop_procs(PMs) -> system_version(Config) when is_list(Config) -> - ?line {comment, erlang:system_info(system_version)}. + {comment, erlang:system_info(system_version)}. misc_smoke_tests(Config) when is_list(Config) -> - ?line true = is_binary(erlang:system_info(info)), - ?line true = is_binary(erlang:system_info(procs)), - ?line true = is_binary(erlang:system_info(loaded)), - ?line true = is_binary(erlang:system_info(dist)), - ?line ok = try erlang:system_info({cpu_topology,erts_get_cpu_topology_error_case}), fail catch error:badarg -> ok end, + true = is_binary(erlang:system_info(info)), + true = is_binary(erlang:system_info(procs)), + true = is_binary(erlang:system_info(loaded)), + true = is_binary(erlang:system_info(dist)), + ok = try erlang:system_info({cpu_topology,erts_get_cpu_topology_error_case}), fail catch error:badarg -> ok end, true = lists:member(erlang:system_info(tolerant_timeofday), [enabled, disabled]), - ?line ok. + ok. heap_size(Config) when is_list(Config) -> - ?line {min_bin_vheap_size, VHmin} = erlang:system_info(min_bin_vheap_size), - ?line {min_heap_size, Hmin} = erlang:system_info(min_heap_size), - ?line GCinf = erlang:system_info(garbage_collection), - ?line VHmin = proplists:get_value(min_bin_vheap_size, GCinf), - ?line Hmin = proplists:get_value(min_heap_size, GCinf), + {min_bin_vheap_size, VHmin} = erlang:system_info(min_bin_vheap_size), + {min_heap_size, Hmin} = erlang:system_info(min_heap_size), + GCinf = erlang:system_info(garbage_collection), + VHmin = proplists:get_value(min_bin_vheap_size, GCinf), + Hmin = proplists:get_value(min_heap_size, GCinf), ok. %% Tests the various wordsize variants wordsize(Config) when is_list(Config) -> - ?line A = erlang:system_info(wordsize), - ?line true = is_integer(A), - ?line A = erlang:system_info({wordsize,internal}), - ?line B = erlang:system_info({wordsize,external}), - ?line true = A =< B, + A = erlang:system_info(wordsize), + true = is_integer(A), + A = erlang:system_info({wordsize,internal}), + B = erlang:system_info({wordsize,external}), + true = A =< B, case {B,A} of {4,4} -> {comment, "True 32-bit emulator"}; @@ -205,8 +205,7 @@ memory_test(_Config) -> end) end, 1000 div erlang:system_info(schedulers_online)) - end, - []), + end, []), cmp_memory(MWs, "spawn procs"), Ps = lists:flatten(DPs), @@ -214,14 +213,12 @@ memory_test(_Config) -> mem_workers_call(MWs, fun () -> lists:foreach(fun (P) -> link(P) end, Ps) - end, - []), + end, []), cmp_memory(MWs, "link procs"), mem_workers_call(MWs, fun () -> lists:foreach(fun (P) -> unlink(P) end, Ps) - end, - []), + end, []), cmp_memory(MWs, "unlink procs"), mem_workers_call(MWs, @@ -238,8 +235,7 @@ memory_test(_Config) -> true = is_reference(Tmr), put('BIF_TMRS', [Tmr|Tmrs]) end, Ps) - end, - []), + end, []), cmp_memory(MWs, "start BIF timer procs"), mem_workers_call(MWs, @@ -250,8 +246,7 @@ memory_test(_Config) -> end, get('BIF_TMRS')), put('BIF_TMRS', undefined), garbage_collect() - end, - []), + end, []), erts_debug:set_internal_state(wait, deallocations), cmp_memory(MWs, "cancel BIF timer procs"), @@ -260,8 +255,7 @@ memory_test(_Config) -> lists:map(fun (P) -> monitor(process, P) end, Ps) - end, - []), + end, []), cmp_memory(MWs, "monitor procs"), Ms = lists:flatten(DMs), mem_workers_call(MWs, @@ -269,8 +263,7 @@ memory_test(_Config) -> lists:foreach(fun (M) -> demonitor(M) end, Ms) - end, - []), + end, []), cmp_memory(MWs, "demonitor procs"), mem_workers_call(MWs, @@ -278,8 +271,7 @@ memory_test(_Config) -> lists:foreach(fun (P) -> P ! {a, "message", make_ref()} end, Ps) - end, - []), + end, []), cmp_memory(MWs, "message procs"), mem_workers_call(MWs, @@ -302,8 +294,7 @@ memory_test(_Config) -> fun () -> put(binary_data, mapn(fun (_) -> list_to_binary(lists:duplicate(256,$?)) end, 100)) - end, - []), + end, []), cmp_memory(MWs, "store binary data"), @@ -311,8 +302,7 @@ memory_test(_Config) -> fun () -> put(binary_data, false), garbage_collect() - end, - []), + end, []), cmp_memory(MWs, "release binary data"), mem_workers_call(MWs, @@ -320,8 +310,7 @@ memory_test(_Config) -> list_to_atom("an ugly atom "++integer_to_list(erlang:system_info(scheduler_id))), list_to_atom("another ugly atom "++integer_to_list(erlang:system_info(scheduler_id))), list_to_atom("yet another ugly atom "++integer_to_list(erlang:system_info(scheduler_id))) - end, - []), + end, []), cmp_memory(MWs, "new atoms"), @@ -332,16 +321,14 @@ memory_test(_Config) -> ets:insert(T, {banan, lists:seq(1,1024)}), ets:insert(T, {appelsin, make_ref()}), put(ets_id, T) - end, - []), + end, []), cmp_memory(MWs, "store ets data"), mem_workers_call(MWs, fun () -> ets:delete(get(ets_id)), put(ets_id, false) - end, - []), + end, []), cmp_memory(MWs, "remove ets data"), lists:foreach(fun (MW) -> @@ -351,8 +338,7 @@ memory_test(_Config) -> receive {'DOWN', Mon, _, _, _} -> ok end - end, - MWs), + end, MWs), ok. mem_worker() -> @@ -367,22 +353,19 @@ mem_worker() -> mem_workers_call(MWs, Fun, Args) -> lists:foreach(fun (MW) -> - MW ! {call, self(), Fun, Args} - end, - MWs), + MW ! {call, self(), Fun, Args} + end, MWs), lists:map(fun (MW) -> - receive - {reply, MW, Res} -> - Res - end - end, - MWs). + receive + {reply, MW, Res} -> + Res + end + end, MWs). mem_workers_cast(MWs, Fun, Args) -> lists:foreach(fun (MW) -> MW ! {cast, self(), Fun, Args} - end, - MWs). + end, MWs). spawn_mem_workers() -> spawn_mem_workers(erlang:system_info(schedulers_online)). @@ -395,7 +378,6 @@ spawn_mem_workers(N) -> link]) | spawn_mem_workers(N-1)]. - mem_get(X, Mem) -> case lists:keyfind(X, 1, Mem) of {X, Val} -> Val; @@ -463,25 +445,25 @@ cmp_memory(MWs, Str) -> "crash dump memory = ~p~n", [Str, EM, EDM]), - ?line check_sane_memory(EM), - ?line check_sane_memory(EDM), + check_sane_memory(EM), + check_sane_memory(EDM), %% We expect these to always give us exactly the same result - ?line cmp_memory(atom, EM, EDM, 1), - ?line cmp_memory(atom_used, EM, EDM, 1), - ?line cmp_memory(binary, EM, EDM, 1), - ?line cmp_memory(code, EM, EDM, 1), - ?line cmp_memory(ets, EM, EDM, 1), + cmp_memory(atom, EM, EDM, 1), + cmp_memory(atom_used, EM, EDM, 1), + cmp_memory(binary, EM, EDM, 1), + cmp_memory(code, EM, EDM, 1), + cmp_memory(ets, EM, EDM, 1), %% Total, processes, processes_used, and system will seldom %% give us exactly the same result since the two readings %% aren't taken atomically. - ?line cmp_memory(total, EM, EDM, 1.05), - ?line cmp_memory(processes, EM, EDM, 1.05), - ?line cmp_memory(processes_used, EM, EDM, 1.05), - ?line cmp_memory(system, EM, EDM, 1.05), + cmp_memory(total, EM, EDM, 1.05), + cmp_memory(processes, EM, EDM, 1.05), + cmp_memory(processes_used, EM, EDM, 1.05), + cmp_memory(system, EM, EDM, 1.05), ok. diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index aeddb0d216..e6da5e89fa 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -108,24 +108,24 @@ local_to_univ_utc(Config) when is_list(Config) -> case os:type() of {unix,_} -> %% TZ variable has a meaning - ?line {ok, Node} = + {ok, Node} = test_server:start_node(local_univ_utc,peer, [{args, "-env TZ UTC"}]), - ?line {{2008,8,1},{0,0,0}} = + {{2008,8,1},{0,0,0}} = rpc:call(Node, erlang,localtime_to_universaltime, [{{2008, 8, 1}, {0, 0, 0}}, false]), - ?line {{2008,8,1},{0,0,0}} = + {{2008,8,1},{0,0,0}} = rpc:call(Node, erlang,localtime_to_universaltime, [{{2008, 8, 1}, {0, 0, 0}}, true]), - ?line [{{2008,8,1},{0,0,0}}] = + [{{2008,8,1},{0,0,0}}] = rpc:call(Node, calendar,local_time_to_universal_time_dst, [{{2008, 8, 1}, {0, 0, 0}}]), - ?line test_server:stop_node(Node), + test_server:stop_node(Node), ok; _ -> {skip,"Only valid on Unix"} @@ -135,24 +135,24 @@ local_to_univ_utc(Config) when is_list(Config) -> %% Tests conversion from univeral to local time. univ_to_local(Config) when is_list(Config) -> - ?line test_univ_to_local(test_data()). + test_univ_to_local(test_data()). test_univ_to_local([{Utc, Local}|Rest]) -> - ?line io:format("Testing ~p => ~p~n", [Local, Utc]), - ?line Local = erlang:universaltime_to_localtime(Utc), - ?line test_univ_to_local(Rest); + io:format("Testing ~p => ~p~n", [Local, Utc]), + Local = erlang:universaltime_to_localtime(Utc), + test_univ_to_local(Rest); test_univ_to_local([]) -> ok. %% Tests conversion from local to universal time. local_to_univ(Config) when is_list(Config) -> - ?line test_local_to_univ(test_data()). + test_local_to_univ(test_data()). test_local_to_univ([{Utc, Local}|Rest]) -> - ?line io:format("Testing ~p => ~p~n", [Utc, Local]), - ?line Utc = erlang:localtime_to_universaltime(Local), - ?line test_local_to_univ(Rest); + io:format("Testing ~p => ~p~n", [Utc, Local]), + Utc = erlang:localtime_to_universaltime(Local), + test_local_to_univ(Rest); test_local_to_univ([]) -> ok. @@ -160,11 +160,11 @@ test_local_to_univ([]) -> %% generate a badarg. bad_univ_to_local(Config) when is_list(Config) -> - ?line bad_test_univ_to_local(bad_dates()). + bad_test_univ_to_local(bad_dates()). bad_test_univ_to_local([Utc|Rest]) -> - ?line io:format("Testing ~p~n", [Utc]), - ?line case catch erlang:universaltime_to_localtime(Utc) of + io:format("Testing ~p~n", [Utc]), + case catch erlang:universaltime_to_localtime(Utc) of {'EXIT', {badarg, _}} -> bad_test_univ_to_local(Rest) end; bad_test_univ_to_local([]) -> @@ -174,11 +174,11 @@ bad_test_univ_to_local([]) -> %% generate a badarg. bad_local_to_univ(Config) when is_list(Config) -> - ?line bad_test_local_to_univ(bad_dates()). + bad_test_local_to_univ(bad_dates()). bad_test_local_to_univ([Local|Rest]) -> - ?line io:format("Testing ~p~n", [Local]), - ?line case catch erlang:localtime_to_universaltime(Local) of + io:format("Testing ~p~n", [Local]), + case catch erlang:localtime_to_universaltime(Local) of {'EXIT', {badarg, _}} -> bad_test_local_to_univ(Rest) end; bad_test_local_to_univ([]) -> @@ -224,13 +224,13 @@ consistency(Config) when is_list(Config) -> %% Daylight-saving calculations are incorrect from the last %% Sunday of March and October to the end of the month. - ?line ok = compare_date_time_and_localtime(16), - ?line ok = compare_local_and_universal(16). + ok = compare_date_time_and_localtime(16), + ok = compare_local_and_universal(16). compare_date_time_and_localtime(Times) when Times > 0 -> - ?line {Year, Mon, Day} = date(), - ?line {Hour, Min, Sec} = time(), - ?line case erlang:localtime() of + {Year, Mon, Day} = date(), + {Hour, Min, Sec} = time(), + case erlang:localtime() of {{Year, Mon, Day}, {Hour, Min, Sec}} -> ok; _ -> compare_date_time_and_localtime(Times-1) end; @@ -395,22 +395,22 @@ repeating_timestamp_check(N) -> %% times (in microseconds). now_unique(Config) when is_list(Config) -> - ?line now_unique(1000, now(), []), - ?line fast_now_unique(100000, now()). + now_unique(1000, now(), []), + fast_now_unique(100000, now()). now_unique(N, Previous, Result) when N > 0 -> - ?line case now() of + case now() of Previous -> ct:fail("now/0 returned the same value twice"); New -> now_unique(N-1, New, [New|Result]) end; now_unique(0, _, [Then|Rest]) -> - ?line now_calc_increment(Rest, microsecs(Then), []). + now_calc_increment(Rest, microsecs(Then), []). now_calc_increment([Then|Rest], Previous, _Result) -> - ?line This = microsecs(Then), - ?line now_calc_increment(Rest, This, [Previous-This]); + This = microsecs(Then), + now_calc_increment(Rest, This, [Previous-This]); now_calc_increment([], _, Differences) -> {comment, "Median increment: " ++ integer_to_list(median(Differences))}. @@ -418,15 +418,15 @@ fast_now_unique(0, _) -> ok; fast_now_unique(N, Then) -> case now() of Then -> - ?line ct:fail("now/0 returned the same value twice"); + ct:fail("now/0 returned the same value twice"); Now -> fast_now_unique(N-1, Now) end. median(Unsorted_List) -> - ?line Length = length(Unsorted_List), - ?line List = lists:sort(Unsorted_List), - ?line case Length rem 2 of + Length = length(Unsorted_List), + List = lists:sort(Unsorted_List), + case Length rem 2 of 0 -> % Even length. [A, B] = lists:nthtail((Length div 2)-1, List), (A+B)/2; @@ -443,24 +443,24 @@ microsecs({Mega_Secs, Secs, Microsecs}) -> now_update(Config) when is_list(Config) -> case test_server:is_debug() of - false -> ?line now_update1(10); + false -> now_update1(10); true -> {skip,"Unreliable in DEBUG build"} end. now_update1(N) when N > 0 -> - ?line T1_linear = linear_time(erlang:localtime()), - ?line T1_now = microsecs(now()), + T1_linear = linear_time(erlang:localtime()), + T1_now = microsecs(now()), - ?line receive after 1008 -> ok end, + receive after 1008 -> ok end, - ?line T2_linear = linear_time(erlang:localtime()), - ?line T2_now = microsecs(now()), + T2_linear = linear_time(erlang:localtime()), + T2_now = microsecs(now()), - ?line Linear_Diff = (T2_linear-T1_linear)*1000000, - ?line Now_Diff = T2_now-T1_now, + Linear_Diff = (T2_linear-T1_linear)*1000000, + Now_Diff = T2_now-T1_now, io:format("Localtime diff = ~p; now() diff = ~p", [Linear_Diff, Now_Diff]), - ?line case abs(Linear_Diff - Now_Diff) of + case abs(Linear_Diff - Now_Diff) of Abs_Delta when Abs_Delta =< 40000 -> ok; _ -> now_update1(N-1) end; @@ -772,15 +772,15 @@ chk_random_values(FR, TR) -> Values = lists:map(fun (_) -> rand:uniform(1 bsl 65) - (1 bsl 64) end, lists:seq(1, 100000)), CheckFun = fun (V) -> - CV = erlang:convert_time_unit(V, FR, TR), - case {(FR*CV) div TR =< V, - (FR*(CV+1)) div TR >= V} of - {true, true} -> - ok; - Failure -> - ct:fail({Failure, CV, V, FR, TR}) - end - end, + CV = erlang:convert_time_unit(V, FR, TR), + case {(FR*CV) div TR =< V, + (FR*(CV+1)) div TR >= V} of + {true, true} -> + ok; + Failure -> + ct:fail({Failure, CV, V, FR, TR}) + end + end, lists:foreach(CheckFun, Values). @@ -802,28 +802,28 @@ chk_values_per_value(FromRes, ToRes, Value, EndValue, MinFromValuesPerToValue, MaxFromValuesPerToValue, ToValue, FromValueCount) -> case erlang:convert_time_unit(Value, FromRes, ToRes) of - ToValue -> - chk_values_per_value(FromRes, ToRes, - Value+1, EndValue, - MinFromValuesPerToValue, - MaxFromValuesPerToValue, - ToValue, FromValueCount+1); - NewToValue -> - case ((MinFromValuesPerToValue =< FromValueCount) - andalso (FromValueCount =< MaxFromValuesPerToValue)) of - false -> - ct:fail({MinFromValuesPerToValue, - FromValueCount, - MaxFromValuesPerToValue}); - true -> -% io:format("~p -> ~p [~p]~n", -% [Value, NewToValue, FromValueCount]), - chk_values_per_value(FromRes, ToRes, - Value+1, EndValue, - MinFromValuesPerToValue, - MaxFromValuesPerToValue, - NewToValue, 1) - end + ToValue -> + chk_values_per_value(FromRes, ToRes, + Value+1, EndValue, + MinFromValuesPerToValue, + MaxFromValuesPerToValue, + ToValue, FromValueCount+1); + NewToValue -> + case ((MinFromValuesPerToValue =< FromValueCount) + andalso (FromValueCount =< MaxFromValuesPerToValue)) of + false -> + ct:fail({MinFromValuesPerToValue, + FromValueCount, + MaxFromValuesPerToValue}); + true -> + % io:format("~p -> ~p [~p]~n", + % [Value, NewToValue, FromValueCount]), + chk_values_per_value(FromRes, ToRes, + Value+1, EndValue, + MinFromValuesPerToValue, + MaxFromValuesPerToValue, + NewToValue, 1) + end end. erlang_timestamp(Config) when is_list(Config) -> @@ -836,11 +836,11 @@ erlang_timestamp(Config) when is_list(Config) -> check_erlang_timestamp(Done, Mon, TO) -> receive - {timeout, Done, timeout} -> - erlang:demonitor(Mon, [flush]), - ok + {timeout, Done, timeout} -> + erlang:demonitor(Mon, [flush]), + ok after 0 -> - do_check_erlang_timestamp(Done, Mon, TO) + do_check_erlang_timestamp(Done, Mon, TO) end. do_check_erlang_timestamp(Done, Mon, TO) -> @@ -912,13 +912,13 @@ test_data() -> _ -> {?timezone,?dst_timezone} end, - ?line test_data(nondst_dates(), TZ) ++ + test_data(nondst_dates(), TZ) ++ test_data(dst_dates(), DSTTZ) ++ crossover_test_data(crossover_dates(), TZ). %% test_data1() -> -%% ?line test_data(nondst_dates(), ?timezone) ++ +%% test_data(nondst_dates(), ?timezone) ++ %% test_data(dst_dates(), ?dst_timezone) ++ %% crossover_test_data(crossover_dates(), ?timezone). @@ -926,16 +926,16 @@ crossover_test_data([{Year, Month, Day}|Rest], TimeZone) when TimeZone > 0 -> Hour = 23, Min = 35, Sec = 55, - ?line Utc = {{Year, Month, Day}, {Hour, Min, Sec}}, - ?line Local = {{Year, Month, Day+1}, {Hour+TimeZone-24, Min, Sec}}, - ?line [{Utc, Local}|crossover_test_data(Rest, TimeZone)]; + Utc = {{Year, Month, Day}, {Hour, Min, Sec}}, + Local = {{Year, Month, Day+1}, {Hour+TimeZone-24, Min, Sec}}, + [{Utc, Local}|crossover_test_data(Rest, TimeZone)]; crossover_test_data([{Year, Month, Day}|Rest], TimeZone) when TimeZone < 0 -> Hour = 0, Min = 23, Sec = 12, - ?line Utc = {{Year, Month, Day}, {Hour, Min, Sec}}, - ?line Local = {{Year, Month, Day-1}, {Hour+TimeZone+24, Min, Sec}}, - ?line [{Utc, Local}|crossover_test_data(Rest, TimeZone)]; + Utc = {{Year, Month, Day}, {Hour, Min, Sec}}, + Local = {{Year, Month, Day-1}, {Hour+TimeZone+24, Min, Sec}}, + [{Utc, Local}|crossover_test_data(Rest, TimeZone)]; crossover_test_data([], _) -> []. @@ -943,9 +943,9 @@ test_data([Date|Rest], TimeZone) -> Hour = 12, Min = 45, Sec = 7, - ?line Utc = {Date, {Hour, Min, Sec}}, - ?line Local = {Date, {Hour+TimeZone, Min, Sec}}, - ?line [{Utc, Local}|test_data(Rest, TimeZone)]; + Utc = {Date, {Hour, Min, Sec}}, + Local = {Date, {Hour+TimeZone, Min, Sec}}, + [{Utc, Local}|test_data(Rest, TimeZone)]; test_data([], _) -> []. diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index 30621e62dc..fa72700604 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -93,49 +93,49 @@ start_timer_1(Config) when is_list(Config) -> %% Basic send_after/3 functionality send_after_1(Config) when is_list(Config) -> - ?line Ref3 = erlang:send_after(1000, self(), plipp), - ?line ok = get(1500, plipp), - ?line false = erlang:read_timer(Ref3), + Ref3 = erlang:send_after(1000, self(), plipp), + ok = get(1500, plipp), + false = erlang:read_timer(Ref3), ok. %% Big timeouts for start_timer/3 start_timer_big(Config) when is_list(Config) -> - ?line Big = 1 bsl 31, - ?line R = erlang:start_timer(Big, self(), hej), - ?line timer:sleep(200), - ?line Left = erlang:cancel_timer(R), - ?line case Big - Left of - Diff when Diff >= 200, Diff < 10000 -> - ok; - _Diff -> - ct:fail({big, Big, Left}) - end, + Big = 1 bsl 31, + R = erlang:start_timer(Big, self(), hej), + timer:sleep(200), + Left = erlang:cancel_timer(R), + case Big - Left of + Diff when Diff >= 200, Diff < 10000 -> + ok; + _Diff -> + ct:fail({big, Big, Left}) + end, ok. %% Big timeouts for send_after/3 send_after_big(Config) when is_list(Config) -> - ?line Big = 1 bsl 31, - ?line R = erlang:send_after(Big, self(), hej), - ?line timer:sleep(200), - ?line Left = erlang:cancel_timer(R), - ?line case Big - Left of - Diff when Diff >= 200, Diff < 10000 -> - ok; - _Diff -> - ct:fail({big, Big, Left}) - end, + Big = 1 bsl 31, + R = erlang:send_after(Big, self(), hej), + timer:sleep(200), + Left = erlang:cancel_timer(R), + case Big - Left of + Diff when Diff >= 200, Diff < 10000 -> + ok; + _Diff -> + ct:fail({big, Big, Left}) + end, ok. %% send_after/3: messages in the right order, kind version send_after_2(Config) when is_list(Config) -> - ?line _ = erlang:send_after(5000, self(), last), - ?line _ = erlang:send_after(0, self(), a0), - ?line _ = erlang:send_after(200, self(), a2), - ?line _ = erlang:send_after(100, self(), a1), - ?line _ = erlang:send_after(500, self(), a5), - ?line _ = erlang:send_after(300, self(), a3), - ?line _ = erlang:send_after(400, self(), a4), - ?line [a0,a1,a2,a3,a4,a5,last] = collect(last), + _ = erlang:send_after(5000, self(), last), + _ = erlang:send_after(0, self(), a0), + _ = erlang:send_after(200, self(), a2), + _ = erlang:send_after(100, self(), a1), + _ = erlang:send_after(500, self(), a5), + _ = erlang:send_after(300, self(), a3), + _ = erlang:send_after(400, self(), a4), + [a0,a1,a2,a3,a4,a5,last] = collect(last), ok. %% send_after/3: messages in the right order, worse than send_after_2 @@ -146,67 +146,67 @@ send_after_3(Config) when is_list(Config) -> _ = erlang:send_after(103, self(), last), [b1, b2, b3, last] = collect(last), -% This behaviour is not guaranteed: -% ?line _ = erlang:send_after(100, self(), c1), -% ?line _ = erlang:send_after(100, self(), c2), -% ?line _ = erlang:send_after(100, self(), c3), -% ?line _ = erlang:send_after(100, self(), last), -% ?line [c1, c2, c3, last] = collect(last), + % This behaviour is not guaranteed: + % _ = erlang:send_after(100, self(), c1), + % _ = erlang:send_after(100, self(), c2), + % _ = erlang:send_after(100, self(), c3), + % _ = erlang:send_after(100, self(), last), + % [c1, c2, c3, last] = collect(last), ok. %% Check trivial cancel_timer/1 behaviour cancel_timer_1(Config) when is_list(Config) -> - ?line false = erlang:cancel_timer(make_ref()), + false = erlang:cancel_timer(make_ref()), ok. %% Error cases for start_timer/3 start_timer_e(Config) when is_list(Config) -> - ?line {'EXIT', _} = (catch erlang:start_timer(-4, self(), hej)), - ?line {'EXIT', _} = (catch erlang:start_timer(1 bsl 64, - self(), hej)), + {'EXIT', _} = (catch erlang:start_timer(-4, self(), hej)), + {'EXIT', _} = (catch erlang:start_timer(1 bsl 64, + self(), hej)), - ?line {'EXIT', _} = (catch erlang:start_timer(4.5, self(), hej)), - ?line {'EXIT', _} = (catch erlang:start_timer(a, self(), hej)), + {'EXIT', _} = (catch erlang:start_timer(4.5, self(), hej)), + {'EXIT', _} = (catch erlang:start_timer(a, self(), hej)), - ?line Node = start_slave(), - ?line Pid = spawn(Node, timer, sleep, [10000]), - ?line {'EXIT', _} = (catch erlang:start_timer(1000, Pid, hej)), - ?line stop_slave(Node), + Node = start_slave(), + Pid = spawn(Node, timer, sleep, [10000]), + {'EXIT', _} = (catch erlang:start_timer(1000, Pid, hej)), + stop_slave(Node), ok. %% Error cases for send_after/3 send_after_e(Config) when is_list(Config) -> - ?line {'EXIT', _} = (catch erlang:send_after(-4, self(), hej)), - ?line {'EXIT', _} = (catch erlang:send_after(1 bsl 64, - self(), hej)), + {'EXIT', _} = (catch erlang:send_after(-4, self(), hej)), + {'EXIT', _} = (catch erlang:send_after(1 bsl 64, + self(), hej)), - ?line {'EXIT', _} = (catch erlang:send_after(4.5, self(), hej)), - ?line {'EXIT', _} = (catch erlang:send_after(a, self(), hej)), + {'EXIT', _} = (catch erlang:send_after(4.5, self(), hej)), + {'EXIT', _} = (catch erlang:send_after(a, self(), hej)), - ?line Node = start_slave(), - ?line Pid = spawn(Node, timer, sleep, [10000]), - ?line {'EXIT', _} = (catch erlang:send_after(1000, Pid, hej)), - ?line stop_slave(Node), + Node = start_slave(), + Pid = spawn(Node, timer, sleep, [10000]), + {'EXIT', _} = (catch erlang:send_after(1000, Pid, hej)), + stop_slave(Node), ok. %% Error cases for cancel_timer/1 cancel_timer_e(Config) when is_list(Config) -> - ?line {'EXIT', _} = (catch erlang:cancel_timer(1)), - ?line {'EXIT', _} = (catch erlang:cancel_timer(self())), - ?line {'EXIT', _} = (catch erlang:cancel_timer(a)), + {'EXIT', _} = (catch erlang:cancel_timer(1)), + {'EXIT', _} = (catch erlang:cancel_timer(self())), + {'EXIT', _} = (catch erlang:cancel_timer(a)), ok. %% Trivial and error test cases for read_timer/1. read_timer_trivial(Config) when is_list(Config) -> - ?line false = erlang:read_timer(make_ref()), - ?line {'EXIT', _} = (catch erlang:read_timer(42)), - ?line {'EXIT', _} = (catch erlang:read_timer(423497834744444444457667444444)), - ?line {'EXIT', _} = (catch erlang:read_timer(self())), - ?line {'EXIT', _} = (catch erlang:read_timer(ab)), + false = erlang:read_timer(make_ref()), + {'EXIT', _} = (catch erlang:read_timer(42)), + {'EXIT', _} = (catch erlang:read_timer(423497834744444444457667444444)), + {'EXIT', _} = (catch erlang:read_timer(self())), + {'EXIT', _} = (catch erlang:read_timer(ab)), ok. %% Test that read_timer/1 seems to return the correct values. @@ -219,16 +219,16 @@ read_timer(Config) when is_list(Config) -> Left = erlang:read_timer(R), Left2 = erlang:cancel_timer(R), case Left == Left2 of - true -> ok; - false -> Left = Left2 + 1 + true -> ok; + false -> Left = Left2 + 1 end, false = erlang:read_timer(R), case Big - Left of - Diff when Diff >= 200, Diff < 10000 -> - ok; - _Diff -> - ct:fail({big, Big, Left}) + Diff when Diff >= 200, Diff < 10000 -> + ok; + _Diff -> + ct:fail({big, Big, Left}) end, process_flag(scheduler, 0), ok. @@ -250,67 +250,67 @@ read_timer_async(Config) when is_list(Config) -> {read_timer, R, Left} = receive_one(), {cancel_timer, R, Left2} = receive_one(), case Left == Left2 of - true -> ok; - false -> Left = Left2 + 1 + true -> ok; + false -> Left = Left2 + 1 end, {read_timer, R, false} = receive_one(), case Big - Left of - Diff when Diff >= 200, Diff < 10000 -> - ok; - _Diff -> - ct:fail({big, Big, Left}) + Diff when Diff >= 200, Diff < 10000 -> + ok; + _Diff -> + ct:fail({big, Big, Left}) end, process_flag(scheduler, 0), ok. cleanup(Config) when is_list(Config) -> - ?line Mem = mem(), + Mem = mem(), %% Timer on dead process - ?line P1 = spawn(fun () -> ok end), - ?line wait_until(fun () -> process_is_cleaned_up(P1) end), - ?line T1 = erlang:start_timer(?SHORT_TIMEOUT*2, P1, "hej"), - ?line T2 = erlang:send_after(?SHORT_TIMEOUT*2, P1, "hej"), + P1 = spawn(fun () -> ok end), + wait_until(fun () -> process_is_cleaned_up(P1) end), + T1 = erlang:start_timer(?SHORT_TIMEOUT*2, P1, "hej"), + T2 = erlang:send_after(?SHORT_TIMEOUT*2, P1, "hej"), receive after 1000 -> ok end, - ?line Mem = mem(), - ?line false = erlang:read_timer(T1), - ?line false = erlang:read_timer(T2), - ?line Mem = mem(), + Mem = mem(), + false = erlang:read_timer(T1), + false = erlang:read_timer(T2), + Mem = mem(), %% Process dies before timeout - ?line P2 = spawn(fun () -> receive after (?SHORT_TIMEOUT div 10) -> ok end end), - ?line T3 = erlang:start_timer(?SHORT_TIMEOUT*2, P2, "hej"), - ?line T4 = erlang:send_after(?SHORT_TIMEOUT*2, P2, "hej"), - ?line true = mem_larger_than(Mem), - ?line true = is_integer(erlang:read_timer(T3)), - ?line true = is_integer(erlang:read_timer(T4)), - ?line wait_until(fun () -> process_is_cleaned_up(P2) end), + P2 = spawn(fun () -> receive after (?SHORT_TIMEOUT div 10) -> ok end end), + T3 = erlang:start_timer(?SHORT_TIMEOUT*2, P2, "hej"), + T4 = erlang:send_after(?SHORT_TIMEOUT*2, P2, "hej"), + true = mem_larger_than(Mem), + true = is_integer(erlang:read_timer(T3)), + true = is_integer(erlang:read_timer(T4)), + wait_until(fun () -> process_is_cleaned_up(P2) end), receive after 1000 -> ok end, - ?line false = erlang:read_timer(T3), - ?line false = erlang:read_timer(T4), - ?line Mem = mem(), + false = erlang:read_timer(T3), + false = erlang:read_timer(T4), + Mem = mem(), %% Cancel timer - ?line P3 = spawn(fun () -> receive after ?SHORT_TIMEOUT*4 -> ok end end), - ?line T5 = erlang:start_timer(?SHORT_TIMEOUT*2, P3, "hej"), - ?line T6 = erlang:send_after(?SHORT_TIMEOUT*2, P3, "hej"), - ?line true = mem_larger_than(Mem), - ?line true = is_integer(erlang:cancel_timer(T5)), - ?line true = is_integer(erlang:cancel_timer(T6)), - ?line false = erlang:read_timer(T5), - ?line false = erlang:read_timer(T6), - ?line exit(P3, kill), - ?line wait_until(fun () -> process_is_cleaned_up(P3) end), - ?line Mem = mem(), + P3 = spawn(fun () -> receive after ?SHORT_TIMEOUT*4 -> ok end end), + T5 = erlang:start_timer(?SHORT_TIMEOUT*2, P3, "hej"), + T6 = erlang:send_after(?SHORT_TIMEOUT*2, P3, "hej"), + true = mem_larger_than(Mem), + true = is_integer(erlang:cancel_timer(T5)), + true = is_integer(erlang:cancel_timer(T6)), + false = erlang:read_timer(T5), + false = erlang:read_timer(T6), + exit(P3, kill), + wait_until(fun () -> process_is_cleaned_up(P3) end), + Mem = mem(), %% Timeout - ?line Ref = make_ref(), - ?line T7 = erlang:start_timer(?SHORT_TIMEOUT+1, self(), Ref), - ?line T8 = erlang:send_after(?SHORT_TIMEOUT+1, self(), Ref), - ?line true = mem_larger_than(Mem), - ?line true = is_integer(erlang:read_timer(T7)), - ?line true = is_integer(erlang:read_timer(T8)), - ?line receive {timeout, T7, Ref} -> ok end, - ?line receive Ref -> ok end, - ?line Mem = mem(), - ?line ok. + Ref = make_ref(), + T7 = erlang:start_timer(?SHORT_TIMEOUT+1, self(), Ref), + T8 = erlang:send_after(?SHORT_TIMEOUT+1, self(), Ref), + true = mem_larger_than(Mem), + true = is_integer(erlang:read_timer(T7)), + true = is_integer(erlang:read_timer(T8)), + receive {timeout, T7, Ref} -> ok end, + receive Ref -> ok end, + Mem = mem(), + ok. evil_timers(Config) when is_list(Config) -> @@ -324,38 +324,38 @@ evil_timers(Config) when is_list(Config) -> %% * lists %% since data of these types have to be adjusted if moved %% in memory - ?line Self = self(), - ?line R1 = make_ref(), - ?line Node = start_slave(), - ?line spawn_link(Node, - fun () -> - Self ! {R1, - [lists:sublist(erlang:ports(), 3), - [make_ref(), make_ref(), make_ref()], - lists:sublist(processes(), 3), - [fun () -> gurka end, - fun (A) -> A + 1 end, - fun (A, B) -> A + B end]]} - end), - ?line ExtList = receive {R1, L} -> L end, - ?line stop_slave(Node), - ?line BinList = [<<"bla">>, - <<"blipp">>, - <<"blupp">>, - list_to_binary(lists:duplicate(1000000,$a)), - list_to_binary(lists:duplicate(1000000,$b)), - list_to_binary(lists:duplicate(1000000,$c))], - ?line FunList = [fun () -> gurka end, - fun (A) -> A + 1 end, - fun (A, B) -> A + B end], - ?line PidList = lists:sublist(processes(), 3), - ?line PortList = lists:sublist(erlang:ports(), 3), - ?line RefList = [make_ref(), make_ref(), make_ref()], - ?line BigList = [111111111111, 22222222222222, 333333333333333333], - ?line Msg = {BinList,[FunList,{RefList,ExtList,PidList,PortList,BigList}]}, - %% ?line io:format("Msg=~p~n",[Msg]), - - ?line Prio = process_flag(priority, max), + Self = self(), + R1 = make_ref(), + Node = start_slave(), + spawn_link(Node, + fun () -> + Self ! {R1, + [lists:sublist(erlang:ports(), 3), + [make_ref(), make_ref(), make_ref()], + lists:sublist(processes(), 3), + [fun () -> gurka end, + fun (A) -> A + 1 end, + fun (A, B) -> A + B end]]} + end), + ExtList = receive {R1, L} -> L end, + stop_slave(Node), + BinList = [<<"bla">>, + <<"blipp">>, + <<"blupp">>, + list_to_binary(lists:duplicate(1000000,$a)), + list_to_binary(lists:duplicate(1000000,$b)), + list_to_binary(lists:duplicate(1000000,$c))], + FunList = [fun () -> gurka end, + fun (A) -> A + 1 end, + fun (A, B) -> A + B end], + PidList = lists:sublist(processes(), 3), + PortList = lists:sublist(erlang:ports(), 3), + RefList = [make_ref(), make_ref(), make_ref()], + BigList = [111111111111, 22222222222222, 333333333333333333], + Msg = {BinList,[FunList,{RefList,ExtList,PidList,PortList,BigList}]}, + %% io:format("Msg=~p~n",[Msg]), + + Prio = process_flag(priority, max), %% %% In the smp case there are four major cases we want to test: %% @@ -368,8 +368,8 @@ evil_timers(Config) when is_list(Config) -> %% be allocated in the previously allocated message buffer along %% with Msg, i.e. the previously allocated message buffer will be %% reallocated and potentially moved. - ?line TimeOutMsgs0 = evil_setup_timers(200, Self, Msg), - ?line RecvTimeOutMsgs0 = evil_recv_timeouts(200), + TimeOutMsgs0 = evil_setup_timers(200, Self, Msg), + RecvTimeOutMsgs0 = evil_recv_timeouts(200), %% 2. A timer started with erlang:start_timer(Time, Receiver, Msg), %% where Msg is an immediate term, expires, and the receivers main %% lock *can not* be acquired immediately (typically when the @@ -377,8 +377,8 @@ evil_timers(Config) when is_list(Config) -> %% %% The wrap tuple will in this case be allocated in a new %% message buffer. - ?line TimeOutMsgs1 = evil_setup_timers(200, Self, immediate), - ?line RecvTimeOutMsgs1 = evil_recv_timeouts(200), + TimeOutMsgs1 = evil_setup_timers(200, Self, immediate), + RecvTimeOutMsgs1 = evil_recv_timeouts(200), %% 3. A timer started with erlang:start_timer(Time, Receiver, Msg), %% where Msg is a composite term, expires, and the receivers main %% lock *can* be acquired immediately (typically when the receiver @@ -387,13 +387,13 @@ evil_timers(Config) when is_list(Config) -> %% The wrap tuple will in this case be allocated on the receivers %% heap, and Msg is passed in the previously allocated message %% buffer. - ?line R2 = make_ref(), - ?line spawn_link(fun () -> - Self ! {R2, evil_setup_timers(200, Self, Msg)} - end), - ?line receive after 1000 -> ok end, - ?line TimeOutMsgs2 = receive {R2, TOM2} -> TOM2 end, - ?line RecvTimeOutMsgs2 = evil_recv_timeouts(200), + R2 = make_ref(), + spawn_link(fun () -> + Self ! {R2, evil_setup_timers(200, Self, Msg)} + end), + receive after 1000 -> ok end, + TimeOutMsgs2 = receive {R2, TOM2} -> TOM2 end, + RecvTimeOutMsgs2 = evil_recv_timeouts(200), %% 4. A timer started with erlang:start_timer(Time, Receiver, Msg), %% where Msg is an immediate term, expires, and the Receivers main %% lock *can* be acquired immediately (typically when the receiver @@ -401,107 +401,107 @@ evil_timers(Config) when is_list(Config) -> %% %% The wrap tuple will in this case be allocated on the receivers %% heap. - ?line R3 = make_ref(), - ?line spawn_link(fun () -> - Self ! {R3, evil_setup_timers(200,Self,immediate)} - end), - ?line receive after 1000 -> ok end, - ?line TimeOutMsgs3 = receive {R3, TOM3} -> TOM3 end, - ?line RecvTimeOutMsgs3 = evil_recv_timeouts(200), + R3 = make_ref(), + spawn_link(fun () -> + Self ! {R3, evil_setup_timers(200,Self,immediate)} + end), + receive after 1000 -> ok end, + TimeOutMsgs3 = receive {R3, TOM3} -> TOM3 end, + RecvTimeOutMsgs3 = evil_recv_timeouts(200), %% Garge collection will hopefully crash the emulator if something %% is wrong... - ?line garbage_collect(), - ?line garbage_collect(), - ?line garbage_collect(), + garbage_collect(), + garbage_collect(), + garbage_collect(), %% Make sure we got the timeouts we expected %% %% Note timeouts are *not* guaranteed to be delivered in order - ?line ok = match(lists:sort(RecvTimeOutMsgs0), lists:sort(TimeOutMsgs0)), - ?line ok = match(lists:sort(RecvTimeOutMsgs1), lists:sort(TimeOutMsgs1)), - ?line ok = match(lists:sort(RecvTimeOutMsgs2), lists:sort(TimeOutMsgs2)), - ?line ok = match(lists:sort(RecvTimeOutMsgs3), lists:sort(TimeOutMsgs3)), + ok = match(lists:sort(RecvTimeOutMsgs0), lists:sort(TimeOutMsgs0)), + ok = match(lists:sort(RecvTimeOutMsgs1), lists:sort(TimeOutMsgs1)), + ok = match(lists:sort(RecvTimeOutMsgs2), lists:sort(TimeOutMsgs2)), + ok = match(lists:sort(RecvTimeOutMsgs3), lists:sort(TimeOutMsgs3)), - ?line process_flag(priority, Prio), - ?line ok. + process_flag(priority, Prio), + ok. evil_setup_timers(N, Receiver, Msg) -> - ?line evil_setup_timers(0, N, Receiver, Msg, []). + evil_setup_timers(0, N, Receiver, Msg, []). evil_setup_timers(N, N, _Receiver, _Msg, TOs) -> - ?line TOs; + TOs; evil_setup_timers(N, Max, Receiver, Msg, TOs) -> - ?line TRef = erlang:start_timer(N, Receiver, Msg), - ?line evil_setup_timers(N+1, Max, Receiver, Msg, [{timeout,TRef,Msg}|TOs]). + TRef = erlang:start_timer(N, Receiver, Msg), + evil_setup_timers(N+1, Max, Receiver, Msg, [{timeout,TRef,Msg}|TOs]). evil_recv_timeouts(M) -> - ?line evil_recv_timeouts([], 0, M). + evil_recv_timeouts([], 0, M). evil_recv_timeouts(TOs, N, N) -> - ?line TOs; + TOs; evil_recv_timeouts(TOs, N, M) -> - ?line receive - {timeout, _, _} = TO -> - ?line evil_recv_timeouts([TO|TOs], N+1, M) - after 0 -> - ?line evil_recv_timeouts(TOs, N, M) - end. + receive + {timeout, _, _} = TO -> + evil_recv_timeouts([TO|TOs], N+1, M) + after 0 -> + evil_recv_timeouts(TOs, N, M) + end. registered_process(Config) when is_list(Config) -> - ?line Mem = mem(), + Mem = mem(), %% Cancel - ?line T1 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, "hej"), - ?line T2 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, "hej"), - ?line undefined = whereis(?MODULE), - ?line true = mem_larger_than(Mem), - ?line true = is_integer(erlang:cancel_timer(T1)), - ?line true = is_integer(erlang:cancel_timer(T2)), - ?line false = erlang:read_timer(T1), - ?line false = erlang:read_timer(T2), - ?line Mem = mem(), + T1 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, "hej"), + T2 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, "hej"), + undefined = whereis(?MODULE), + true = mem_larger_than(Mem), + true = is_integer(erlang:cancel_timer(T1)), + true = is_integer(erlang:cancel_timer(T2)), + false = erlang:read_timer(T1), + false = erlang:read_timer(T2), + Mem = mem(), %% Timeout register after start - ?line Ref1 = make_ref(), - ?line T3 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref1), - ?line T4 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref1), - ?line undefined = whereis(?MODULE), - ?line true = mem_larger_than(Mem), - ?line true = is_integer(erlang:read_timer(T3)), - ?line true = is_integer(erlang:read_timer(T4)), - ?line true = register(?MODULE, self()), - ?line receive {timeout, T3, Ref1} -> ok end, - ?line receive Ref1 -> ok end, - ?line Mem = mem(), + Ref1 = make_ref(), + T3 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref1), + T4 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref1), + undefined = whereis(?MODULE), + true = mem_larger_than(Mem), + true = is_integer(erlang:read_timer(T3)), + true = is_integer(erlang:read_timer(T4)), + true = register(?MODULE, self()), + receive {timeout, T3, Ref1} -> ok end, + receive Ref1 -> ok end, + Mem = mem(), %% Timeout register before start - ?line Ref2 = make_ref(), - ?line T5 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref2), - ?line T6 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref2), - ?line true = mem_larger_than(Mem), - ?line true = is_integer(erlang:read_timer(T5)), - ?line true = is_integer(erlang:read_timer(T6)), - ?line receive {timeout, T5, Ref2} -> ok end, - ?line receive Ref2 -> ok end, - ?line Mem = mem(), - ?line true = unregister(?MODULE), - ?line ok. + Ref2 = make_ref(), + T5 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref2), + T6 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref2), + true = mem_larger_than(Mem), + true = is_integer(erlang:read_timer(T5)), + true = is_integer(erlang:read_timer(T6)), + receive {timeout, T5, Ref2} -> ok end, + receive Ref2 -> ok end, + Mem = mem(), + true = unregister(?MODULE), + ok. same_time_yielding(Config) when is_list(Config) -> Mem = mem(), SchdlrsOnln = erlang:system_info(schedulers_online), Tmo = erlang:monotonic_time(milli_seconds) + 3000, Tmrs = lists:map(fun (I) -> - process_flag(scheduler, (I rem SchdlrsOnln) + 1), - erlang:start_timer(Tmo, self(), hej, [{abs, true}]) - end, - lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)), + process_flag(scheduler, (I rem SchdlrsOnln) + 1), + erlang:start_timer(Tmo, self(), hej, [{abs, true}]) + end, + lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)), true = mem_larger_than(Mem), lists:foreach(fun (Tmr) -> receive {timeout, Tmr, hej} -> ok end end, Tmrs), Done = erlang:monotonic_time(milli_seconds), true = Done >= Tmo, case erlang:system_info(build_type) of - opt -> true = Done < Tmo + 200; - _ -> true = Done < Tmo + 1000 + opt -> true = Done < Tmo + 200; + _ -> true = Done < Tmo + 1000 end, Mem = mem(), ok. @@ -517,19 +517,19 @@ same_time_yielding_with_cancel_other(Config) when is_list(Config) -> do_cancel_tmrs(Tmo, Tmrs, Tester) -> BeginCancel = erlang:convert_time_unit(Tmo, - milli_seconds, - micro_seconds) - 100, + milli_seconds, + micro_seconds) - 100, busy_wait_until(fun () -> - erlang:monotonic_time(micro_seconds) >= BeginCancel - end), + erlang:monotonic_time(micro_seconds) >= BeginCancel + end), lists:foreach(fun (Tmr) -> - erlang:cancel_timer(Tmr, - [{async, true}, - {info, true}]) - end, Tmrs), + erlang:cancel_timer(Tmr, + [{async, true}, + {info, true}]) + end, Tmrs), case Tester == self() of - true -> ok; - false -> forward_msgs(Tester) + true -> ok; + false -> forward_msgs(Tester) end. same_time_yielding_with_cancel_test(Other, Accessor) -> @@ -538,57 +538,57 @@ same_time_yielding_with_cancel_test(Other, Accessor) -> Tmo = erlang:monotonic_time(milli_seconds) + 3000, Tester = self(), Cancelor = case Other of - false -> - Tester; - true -> - spawn(fun () -> - receive - {timers, Tmrs} -> - do_cancel_tmrs(Tmo, Tmrs, Tester) - end - end) - end, + false -> + Tester; + true -> + spawn(fun () -> + receive + {timers, Tmrs} -> + do_cancel_tmrs(Tmo, Tmrs, Tester) + end + end) + end, Opts = case Accessor of - false -> [{abs, true}]; - true -> [{accessor, Cancelor}, {abs, true}] - end, + false -> [{abs, true}]; + true -> [{accessor, Cancelor}, {abs, true}] + end, Tmrs = lists:map(fun (I) -> - process_flag(scheduler, (I rem SchdlrsOnln) + 1), - erlang:start_timer(Tmo, self(), hej, Opts) - end, - lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)), + process_flag(scheduler, (I rem SchdlrsOnln) + 1), + erlang:start_timer(Tmo, self(), hej, Opts) + end, + lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)), true = mem_larger_than(Mem), case Other of - false -> - do_cancel_tmrs(Tmo, Tmrs, Tester); - true -> - Cancelor ! {timers, Tmrs} + false -> + do_cancel_tmrs(Tmo, Tmrs, Tester); + true -> + Cancelor ! {timers, Tmrs} end, {Tmos, Cncls} = lists:foldl(fun (Tmr, {T, C}) -> - receive - {timeout, Tmr, hej} -> - receive - {cancel_timer, Tmr, Info} -> - false = Info, - {T+1, C} - end; - {cancel_timer, Tmr, false} -> - receive - {timeout, Tmr, hej} -> - {T+1, C} - end; - {cancel_timer, Tmr, TimeLeft} -> - true = is_integer(TimeLeft), - {T, C+1} - end - end, - {0, 0}, - Tmrs), + receive + {timeout, Tmr, hej} -> + receive + {cancel_timer, Tmr, Info} -> + false = Info, + {T+1, C} + end; + {cancel_timer, Tmr, false} -> + receive + {timeout, Tmr, hej} -> + {T+1, C} + end; + {cancel_timer, Tmr, TimeLeft} -> + true = is_integer(TimeLeft), + {T, C+1} + end + end, + {0, 0}, + Tmrs), io:format("Timeouts: ~p Cancels: ~p~n", [Tmos, Cncls]), Mem = mem(), case Other of - true -> exit(Cancelor, bang); - false -> ok + true -> exit(Cancelor, bang); + false -> ok end, {comment, "Timeouts: " ++ integer_to_list(Tmos) ++ " Cancels: " @@ -598,16 +598,16 @@ auto_cancel_yielding(Config) when is_list(Config) -> Mem = mem(), SchdlrsOnln = erlang:system_info(schedulers_online), P = spawn(fun () -> - lists:foreach( - fun (I) -> - process_flag(scheduler, (I rem SchdlrsOnln)+1), - erlang:start_timer((1 bsl 28)+I*10, self(), hej) - end, - lists:seq(1, - ((?AUTO_CANCEL_YIELD_LIMIT*3+1) - *SchdlrsOnln))), - receive after infinity -> ok end - end), + lists:foreach( + fun (I) -> + process_flag(scheduler, (I rem SchdlrsOnln)+1), + erlang:start_timer((1 bsl 28)+I*10, self(), hej) + end, + lists:seq(1, + ((?AUTO_CANCEL_YIELD_LIMIT*3+1) + *SchdlrsOnln))), + receive after infinity -> ok end + end), true = mem_larger_than(Mem), exit(P, bang), wait_until(fun () -> process_is_cleaned_up(P) end), @@ -619,45 +619,45 @@ process_is_cleaned_up(P) when is_pid(P) -> wait_until(Pred) when is_function(Pred) -> case catch Pred() of - true -> ok; - _ -> receive after 50 -> ok end, wait_until(Pred) + true -> ok; + _ -> receive after 50 -> ok end, wait_until(Pred) end. busy_wait_until(Pred) when is_function(Pred) -> case catch Pred() of - true -> ok; - _ -> busy_wait_until(Pred) + true -> ok; + _ -> busy_wait_until(Pred) end. forward_msgs(To) -> receive - Msg -> - To ! Msg + Msg -> + To ! Msg end, forward_msgs(To). get(Time, Msg) -> receive - Msg -> - ok + Msg -> + ok after Time - -> - no_message + -> + no_message end. get_msg() -> receive - Msg -> - {ok, Msg} + Msg -> + {ok, Msg} after 0 -> - empty + empty end. start_slave() -> - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line Name = atom_to_list(?MODULE) - ++ "-" ++ integer_to_list(erlang:system_time(seconds)) - ++ "-" ++ integer_to_list(erlang:unique_integer([positive])), + Pa = filename:dirname(code:which(?MODULE)), + Name = atom_to_list(?MODULE) + ++ "-" ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" ++ integer_to_list(erlang:unique_integer([positive])), {ok, Node} = test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]), Node. @@ -669,18 +669,18 @@ collect(Last) -> receive_one() -> receive - Msg -> - Msg + Msg -> + Msg end. collect(Last, Msgs0) -> Msg = receive_one(), Msgs = Msgs0 ++ [Msg], case Msg of - Last -> - Msgs; - _ -> - collect(Last, Msgs) + Last -> + Msgs; + _ -> + collect(Last, Msgs) end. match(X, X) -> @@ -729,8 +729,8 @@ mem() -> erts_debug:set_internal_state(wait, timer_cancellations), erts_debug:set_internal_state(wait, deallocations), case mem_get() of - {-1, -1} -> no_fix_alloc; - {A, U} -> io:format("mem = ~p ~p~n", [A, U]), U + {-1, -1} -> no_fix_alloc; + {A, U} -> io:format("mem = ~p ~p~n", [A, U]), U end. mem_get() -> @@ -743,8 +743,8 @@ mem_recv(0, _Ref, AU) -> AU; mem_recv(N, Ref, AU) -> receive - {Ref, _, IL} -> - mem_recv(N-1, Ref, mem_parse_ilists(IL, AU)) + {Ref, _, IL} -> + mem_recv(N-1, Ref, mem_parse_ilists(IL, AU)) end. @@ -757,19 +757,19 @@ mem_parse_ilist({fix_alloc, false}, _) -> {-1, -1}; mem_parse_ilist({fix_alloc, _, IDL}, {A, U}) -> case lists:keyfind(fix_types, 1, IDL) of - {fix_types, TL} -> - {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0), - {ThisA + A, ThisU + U}; - {fix_types, Mask, TL} -> - {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0), - {(ThisA + A) band Mask , (ThisU + U) band Mask} + {fix_types, TL} -> + {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0), + {ThisA + A, ThisU + U}; + {fix_types, Mask, TL} -> + {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0), + {(ThisA + A) band Mask , (ThisU + U) band Mask} end. mem_get_btm_aus([], A, U) -> {A, U}; mem_get_btm_aus([{BtmType, BtmA, BtmU} | Types], - A, U) when BtmType == bif_timer; - BtmType == accessor_bif_timer -> + A, U) when BtmType == bif_timer; + BtmType == accessor_bif_timer -> mem_get_btm_aus(Types, BtmA+A, BtmU+U); mem_get_btm_aus([_|Types], A, U) -> mem_get_btm_aus(Types, A, U). diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 6f3415fa7b..dcd5e95434 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -64,93 +64,93 @@ all() -> %% is enabled or not. cpu_timestamp(Config) when is_list(Config) -> %% Test whether cpu_timestamp is implemented on this platform. - ?line Works = try erlang:trace(all, true, [cpu_timestamp]) of - _ -> - ?line erlang:trace(all, false, [cpu_timestamp]), - true - catch - error:badarg -> false - end, + Works = try erlang:trace(all, true, [cpu_timestamp]) of + _ -> + erlang:trace(all, false, [cpu_timestamp]), + true + catch + error:badarg -> false + end, {comment,case Works of - false -> "cpu_timestamp is NOT implemented/does not work"; - true -> "cpu_timestamp works" - end}. + false -> "cpu_timestamp is NOT implemented/does not work"; + true -> "cpu_timestamp works" + end}. %% Tests that trace(Pid, How, ['receive']) works. receive_trace(Config) when is_list(Config) -> - ?line Receiver = fun_spawn(fun receiver/0), - ?line process_flag(trap_exit, true), + Receiver = fun_spawn(fun receiver/0), + process_flag(trap_exit, true), %% Trace the process; make sure that we receive the trace messages. - ?line 1 = erlang:trace(Receiver, true, ['receive']), - ?line Hello = {hello, world}, - ?line Receiver ! Hello, - ?line {trace, Receiver, 'receive', Hello} = receive_first(), - ?line Hello2 = {hello, again, world}, - ?line Receiver ! Hello2, - ?line {trace, Receiver, 'receive', Hello2} = receive_first(), - ?line receive_nothing(), + 1 = erlang:trace(Receiver, true, ['receive']), + Hello = {hello, world}, + Receiver ! Hello, + {trace, Receiver, 'receive', Hello} = receive_first(), + Hello2 = {hello, again, world}, + Receiver ! Hello2, + {trace, Receiver, 'receive', Hello2} = receive_first(), + receive_nothing(), %% Another process should not be able to trace Receiver. - ?line Intruder = fun_spawn(fun() -> erlang:trace(Receiver, true, ['receive']) end), - ?line {'EXIT', Intruder, {badarg, _}} = receive_first(), + Intruder = fun_spawn(fun() -> erlang:trace(Receiver, true, ['receive']) end), + {'EXIT', Intruder, {badarg, _}} = receive_first(), %% Untrace the process; we should not receive anything. - ?line 1 = erlang:trace(Receiver, false, ['receive']), - ?line Receiver ! {hello, there}, - ?line Receiver ! any_garbage, - ?line receive_nothing(), + 1 = erlang:trace(Receiver, false, ['receive']), + Receiver ! {hello, there}, + Receiver ! any_garbage, + receive_nothing(), ok. %% Test that traces are generated for messages sent %% and received to/from self(). self_send(Config) when is_list(Config) -> - ?line Fun = - fun(Self, Parent) -> receive - go_ahead -> - self() ! from_myself, - Self(Self, Parent); - from_myself -> - Parent ! done - end - end, - ?line Self = self(), - ?line SelfSender = fun_spawn(Fun, [Fun, Self]), - ?line erlang:trace(SelfSender, true, ['receive', 'send']), - ?line SelfSender ! go_ahead, - ?line receive {trace, SelfSender, 'receive', go_ahead} -> ok end, - ?line receive {trace, SelfSender, 'receive', from_myself} -> ok end, - ?line receive - {trace,SelfSender,send,from_myself,SelfSender} -> ok - end, - ?line receive {trace,SelfSender,send,done,Self} -> ok end, - ?line receive done -> ok end, + Fun = + fun(Self, Parent) -> receive + go_ahead -> + self() ! from_myself, + Self(Self, Parent); + from_myself -> + Parent ! done + end + end, + Self = self(), + SelfSender = fun_spawn(Fun, [Fun, Self]), + erlang:trace(SelfSender, true, ['receive', 'send']), + SelfSender ! go_ahead, + receive {trace, SelfSender, 'receive', go_ahead} -> ok end, + receive {trace, SelfSender, 'receive', from_myself} -> ok end, + receive + {trace,SelfSender,send,from_myself,SelfSender} -> ok + end, + receive {trace,SelfSender,send,done,Self} -> ok end, + receive done -> ok end, ok. %% Test that we can receive timeout traces. timeout_trace(Config) when is_list(Config) -> - ?line Process = fun_spawn(fun process/0), - ?line 1 = erlang:trace(Process, true, ['receive']), - ?line Process ! timeout_please, - ?line {trace, Process, 'receive', timeout_please} = receive_first(), - ?line {trace, Process, 'receive', timeout} = receive_first(), - ?line receive_nothing(), + Process = fun_spawn(fun process/0), + 1 = erlang:trace(Process, true, ['receive']), + Process ! timeout_please, + {trace, Process, 'receive', timeout_please} = receive_first(), + {trace, Process, 'receive', timeout} = receive_first(), + receive_nothing(), ok. %% Tests that trace(Pid, How, [send]) works. send_trace(Config) when is_list(Config) -> - ?line process_flag(trap_exit, true), - ?line Sender = fun_spawn(fun sender/0), - ?line Receiver = fun_spawn(fun receiver/0), + process_flag(trap_exit, true), + Sender = fun_spawn(fun sender/0), + Receiver = fun_spawn(fun receiver/0), %% Check that a message sent to another process is traced. - ?line 1 = erlang:trace(Sender, true, [send]), - ?line Sender ! {send_please, Receiver, to_receiver}, - ?line {trace, Sender, send, to_receiver, Receiver} = receive_first(), - ?line receive_nothing(), + 1 = erlang:trace(Sender, true, [send]), + Sender ! {send_please, Receiver, to_receiver}, + {trace, Sender, send, to_receiver, Receiver} = receive_first(), + receive_nothing(), %% Check that a message sent to another registered process is traced. register(?MODULE,Receiver), @@ -160,11 +160,11 @@ send_trace(Config) when is_list(Config) -> unregister(?MODULE), %% Check that a message sent to this process is traced. - ?line Sender ! {send_please, self(), to_myself}, - ?line receive to_myself -> ok end, - ?line Self = self(), - ?line {trace, Sender, send, to_myself, Self} = receive_first(), - ?line receive_nothing(), + Sender ! {send_please, self(), to_myself}, + receive to_myself -> ok end, + Self = self(), + {trace, Sender, send, to_myself, Self} = receive_first(), + receive_nothing(), %% Check that a message sent to dead process is traced. {Pid,Ref} = spawn_monitor(fun() -> ok end), @@ -182,166 +182,166 @@ send_trace(Config) when is_list(Config) -> receive_nothing(), %% Another process should not be able to trace Sender. - ?line Intruder = fun_spawn(fun() -> erlang:trace(Sender, true, [send]) end), - ?line {'EXIT', Intruder, {badarg, _}} = receive_first(), + Intruder = fun_spawn(fun() -> erlang:trace(Sender, true, [send]) end), + {'EXIT', Intruder, {badarg, _}} = receive_first(), %% Untrace the sender process and make sure that we receive no more %% trace messages. - ?line 1 = erlang:trace(Sender, false, [send]), - ?line Sender ! {send_please, Receiver, to_receiver}, - ?line Sender ! {send_please, self(), to_myself_again}, - ?line receive to_myself_again -> ok end, - ?line receive_nothing(), + 1 = erlang:trace(Sender, false, [send]), + Sender ! {send_please, Receiver, to_receiver}, + Sender ! {send_please, self(), to_myself_again}, + receive to_myself_again -> ok end, + receive_nothing(), ok. %% Test trace(Pid, How, [procs]). procs_trace(Config) when is_list(Config) -> - ?line Name = list_to_atom(atom_to_list(?MODULE)++"_procs_trace"), - ?line Self = self(), - ?line process_flag(trap_exit, true), + Name = list_to_atom(atom_to_list(?MODULE)++"_procs_trace"), + Self = self(), + process_flag(trap_exit, true), %% - ?line Proc1 = spawn_link(?MODULE, process, [Self]), - ?line io:format("Proc1 = ~p ~n", [Proc1]), - ?line Proc2 = spawn(?MODULE, process, [Self]), - ?line io:format("Proc2 = ~p ~n", [Proc2]), + Proc1 = spawn_link(?MODULE, process, [Self]), + io:format("Proc1 = ~p ~n", [Proc1]), + Proc2 = spawn(?MODULE, process, [Self]), + io:format("Proc2 = ~p ~n", [Proc2]), %% - ?line 1 = erlang:trace(Proc1, true, [procs]), - ?line MFA = {?MODULE, process, [Self]}, + 1 = erlang:trace(Proc1, true, [procs]), + MFA = {?MODULE, process, [Self]}, %% %% spawn, link - ?line Proc1 ! {spawn_link_please, Self, MFA}, - ?line Proc3 = receive {spawned, Proc1, P3} -> P3 end, - ?line {trace, Proc1, spawn, Proc3, MFA} = receive_first(), - ?line io:format("Proc3 = ~p ~n", [Proc3]), - ?line {trace, Proc1, link, Proc3} = receive_first(), - ?line receive_nothing(), + Proc1 ! {spawn_link_please, Self, MFA}, + Proc3 = receive {spawned, Proc1, P3} -> P3 end, + {trace, Proc1, spawn, Proc3, MFA} = receive_first(), + io:format("Proc3 = ~p ~n", [Proc3]), + {trace, Proc1, link, Proc3} = receive_first(), + receive_nothing(), %% %% getting_unlinked by exit() - ?line Proc1 ! {trap_exit_please, true}, - ?line Reason3 = make_ref(), - ?line Proc1 ! {send_please, Proc3, {exit_please, Reason3}}, - ?line receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end, - ?line {trace, Proc1, getting_unlinked, Proc3} = receive_first(), - ?line Proc1 ! {trap_exit_please, false}, - ?line receive_nothing(), + Proc1 ! {trap_exit_please, true}, + Reason3 = make_ref(), + Proc1 ! {send_please, Proc3, {exit_please, Reason3}}, + receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end, + {trace, Proc1, getting_unlinked, Proc3} = receive_first(), + Proc1 ! {trap_exit_please, false}, + receive_nothing(), %% %% link - ?line Proc1 ! {link_please, Proc2}, - ?line {trace, Proc1, link, Proc2} = receive_first(), - ?line receive_nothing(), + Proc1 ! {link_please, Proc2}, + {trace, Proc1, link, Proc2} = receive_first(), + receive_nothing(), %% %% unlink - ?line Proc1 ! {unlink_please, Proc2}, - ?line {trace, Proc1, unlink, Proc2} = receive_first(), - ?line receive_nothing(), + Proc1 ! {unlink_please, Proc2}, + {trace, Proc1, unlink, Proc2} = receive_first(), + receive_nothing(), %% %% getting_linked - ?line Proc2 ! {link_please, Proc1}, - ?line {trace, Proc1, getting_linked, Proc2} = receive_first(), - ?line receive_nothing(), + Proc2 ! {link_please, Proc1}, + {trace, Proc1, getting_linked, Proc2} = receive_first(), + receive_nothing(), %% %% getting_unlinked - ?line Proc2 ! {unlink_please, Proc1}, - ?line {trace, Proc1, getting_unlinked, Proc2} = receive_first(), - ?line receive_nothing(), + Proc2 ! {unlink_please, Proc1}, + {trace, Proc1, getting_unlinked, Proc2} = receive_first(), + receive_nothing(), %% %% register - ?line true = register(Name, Proc1), - ?line {trace, Proc1, register, Name} = receive_first(), - ?line receive_nothing(), + true = register(Name, Proc1), + {trace, Proc1, register, Name} = receive_first(), + receive_nothing(), %% %% unregister - ?line true = unregister(Name), - ?line {trace, Proc1, unregister, Name} = receive_first(), - ?line receive_nothing(), + true = unregister(Name), + {trace, Proc1, unregister, Name} = receive_first(), + receive_nothing(), %% %% exit (with registered name, due to link) - ?line Reason4 = make_ref(), - ?line Proc1 ! {spawn_link_please, Self, MFA}, - ?line Proc4 = receive {spawned, Proc1, P4} -> P4 end, - ?line {trace, Proc1, spawn, Proc4, MFA} = receive_first(), - ?line io:format("Proc4 = ~p ~n", [Proc4]), - ?line {trace, Proc1, link, Proc4} = receive_first(), - ?line Proc1 ! {register_please, Name, Proc1}, - ?line {trace, Proc1, register, Name} = receive_first(), - ?line Proc4 ! {exit_please, Reason4}, - ?line receive {'EXIT', Proc1, Reason4} -> ok end, - ?line {trace, Proc1, exit, Reason4} = receive_first(), - ?line {trace, Proc1, unregister, Name} = receive_first(), - ?line receive_nothing(), + Reason4 = make_ref(), + Proc1 ! {spawn_link_please, Self, MFA}, + Proc4 = receive {spawned, Proc1, P4} -> P4 end, + {trace, Proc1, spawn, Proc4, MFA} = receive_first(), + io:format("Proc4 = ~p ~n", [Proc4]), + {trace, Proc1, link, Proc4} = receive_first(), + Proc1 ! {register_please, Name, Proc1}, + {trace, Proc1, register, Name} = receive_first(), + Proc4 ! {exit_please, Reason4}, + receive {'EXIT', Proc1, Reason4} -> ok end, + {trace, Proc1, exit, Reason4} = receive_first(), + {trace, Proc1, unregister, Name} = receive_first(), + receive_nothing(), %% %% exit (not linked to tracing process) - ?line 1 = erlang:trace(Proc2, true, [procs]), - ?line Reason2 = make_ref(), - ?line Proc2 ! {exit_please, Reason2}, - ?line {trace, Proc2, exit, Reason2} = receive_first(), - ?line receive_nothing(), + 1 = erlang:trace(Proc2, true, [procs]), + Reason2 = make_ref(), + Proc2 ! {exit_please, Reason2}, + {trace, Proc2, exit, Reason2} = receive_first(), + receive_nothing(), ok. dist_procs_trace(Config) when is_list(Config) -> ct:timetrap({seconds, 15}), - ?line OtherName = atom_to_list(?MODULE)++"_dist_procs_trace", - ?line {ok, OtherNode} = start_node(OtherName), - ?line Self = self(), - ?line process_flag(trap_exit, true), + OtherName = atom_to_list(?MODULE)++"_dist_procs_trace", + {ok, OtherNode} = start_node(OtherName), + Self = self(), + process_flag(trap_exit, true), %% - ?line Proc1 = spawn_link(?MODULE, process, [Self]), - ?line io:format("Proc1 = ~p ~n", [Proc1]), - ?line Proc2 = spawn(OtherNode, ?MODULE, process, [Self]), - ?line io:format("Proc2 = ~p ~n", [Proc2]), + Proc1 = spawn_link(?MODULE, process, [Self]), + io:format("Proc1 = ~p ~n", [Proc1]), + Proc2 = spawn(OtherNode, ?MODULE, process, [Self]), + io:format("Proc2 = ~p ~n", [Proc2]), %% - ?line 1 = erlang:trace(Proc1, true, [procs]), - ?line MFA = {?MODULE, process, [Self]}, + 1 = erlang:trace(Proc1, true, [procs]), + MFA = {?MODULE, process, [Self]}, %% %% getting_unlinked by exit() - ?line Proc1 ! {spawn_link_please, Self, OtherNode, MFA}, - ?line Proc1 ! {trap_exit_please, true}, - ?line Proc3 = receive {spawned, Proc1, P3} -> P3 end, - ?line io:format("Proc3 = ~p ~n", [Proc3]), - ?line {trace, Proc1, getting_linked, Proc3} = receive_first(), - ?line Reason3 = make_ref(), - ?line Proc1 ! {send_please, Proc3, {exit_please, Reason3}}, - ?line receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end, - ?line {trace, Proc1, getting_unlinked, Proc3} = receive_first(), - ?line Proc1 ! {trap_exit_please, false}, - ?line receive_nothing(), + Proc1 ! {spawn_link_please, Self, OtherNode, MFA}, + Proc1 ! {trap_exit_please, true}, + Proc3 = receive {spawned, Proc1, P3} -> P3 end, + io:format("Proc3 = ~p ~n", [Proc3]), + {trace, Proc1, getting_linked, Proc3} = receive_first(), + Reason3 = make_ref(), + Proc1 ! {send_please, Proc3, {exit_please, Reason3}}, + receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end, + {trace, Proc1, getting_unlinked, Proc3} = receive_first(), + Proc1 ! {trap_exit_please, false}, + receive_nothing(), %% %% link - ?line Proc1 ! {link_please, Proc2}, - ?line {trace, Proc1, link, Proc2} = receive_first(), - ?line receive_nothing(), + Proc1 ! {link_please, Proc2}, + {trace, Proc1, link, Proc2} = receive_first(), + receive_nothing(), %% %% unlink - ?line Proc1 ! {unlink_please, Proc2}, - ?line {trace, Proc1, unlink, Proc2} = receive_first(), - ?line receive_nothing(), + Proc1 ! {unlink_please, Proc2}, + {trace, Proc1, unlink, Proc2} = receive_first(), + receive_nothing(), %% %% getting_linked - ?line Proc2 ! {link_please, Proc1}, - ?line {trace, Proc1, getting_linked, Proc2} = receive_first(), - ?line receive_nothing(), + Proc2 ! {link_please, Proc1}, + {trace, Proc1, getting_linked, Proc2} = receive_first(), + receive_nothing(), %% %% getting_unlinked - ?line Proc2 ! {unlink_please, Proc1}, - ?line {trace, Proc1, getting_unlinked, Proc2} = receive_first(), - ?line receive_nothing(), + Proc2 ! {unlink_please, Proc1}, + {trace, Proc1, getting_unlinked, Proc2} = receive_first(), + receive_nothing(), %% %% exit (with registered name, due to link) - ?line Name = list_to_atom(OtherName), - ?line Reason2 = make_ref(), - ?line Proc1 ! {link_please, Proc2}, - ?line {trace, Proc1, link, Proc2} = receive_first(), - ?line Proc1 ! {register_please, Name, Proc1}, - ?line {trace, Proc1, register, Name} = receive_first(), - ?line Proc2 ! {exit_please, Reason2}, - ?line receive {'EXIT', Proc1, Reason2} -> ok end, - ?line {trace, Proc1, exit, Reason2} = receive_first(), - ?line {trace, Proc1, unregister, Name} = receive_first(), - ?line receive_nothing(), + Name = list_to_atom(OtherName), + Reason2 = make_ref(), + Proc1 ! {link_please, Proc2}, + {trace, Proc1, link, Proc2} = receive_first(), + Proc1 ! {register_please, Name, Proc1}, + {trace, Proc1, register, Name} = receive_first(), + Proc2 ! {exit_please, Reason2}, + receive {'EXIT', Proc1, Reason2} -> ok end, + {trace, Proc1, exit, Reason2} = receive_first(), + {trace, Proc1, unregister, Name} = receive_first(), + receive_nothing(), %% %% Done. - ?line true = stop_node(OtherNode), + true = stop_node(OtherNode), ok. @@ -350,117 +350,117 @@ dist_procs_trace(Config) when is_list(Config) -> %% Tests trace(Pid, How, [set_on_spawn]). set_on_spawn(Config) when is_list(Config) -> - ?line Listener = fun_spawn(fun process/0), + Listener = fun_spawn(fun process/0), %% Create and trace a process with the set_on_spawn flag. %% Make sure it is traced. - ?line Father_SOS = fun_spawn(fun process/0), - ?line 1 = erlang:trace(Father_SOS, true, [send, set_on_spawn]), - ?line true = is_send_traced(Father_SOS, Listener, sos_father), + Father_SOS = fun_spawn(fun process/0), + 1 = erlang:trace(Father_SOS, true, [send, set_on_spawn]), + true = is_send_traced(Father_SOS, Listener, sos_father), %% Have the process spawn of two children and test that they %% are traced. - ?line [Child1, Child2] = spawn_children(Father_SOS, 2), - ?line true = is_send_traced(Child1, Listener, child1), - ?line true = is_send_traced(Child2, Listener, child2), + [Child1, Child2] = spawn_children(Father_SOS, 2), + true = is_send_traced(Child1, Listener, child1), + true = is_send_traced(Child2, Listener, child2), %% Second generation. [Child11, Child12] = spawn_children(Child1, 2), - ?line true = is_send_traced(Child11, Listener, child11), - ?line true = is_send_traced(Child12, Listener, child12), + true = is_send_traced(Child11, Listener, child11), + true = is_send_traced(Child12, Listener, child12), ok. %% Tests trace(Pid, How, [set_on_first_spawn]). set_on_first_spawn(Config) when is_list(Config) -> ct:timetrap({seconds, 10}), - ?line Listener = fun_spawn(fun process/0), + Listener = fun_spawn(fun process/0), %% Create and trace a process with the set_on_first_spawn flag. %% Make sure it is traced. - ?line Parent = fun_spawn(fun process/0), - ?line 1 = erlang:trace(Parent, true, [send, set_on_first_spawn]), - ?line is_send_traced(Parent, Listener, sos_father), + Parent = fun_spawn(fun process/0), + 1 = erlang:trace(Parent, true, [send, set_on_first_spawn]), + is_send_traced(Parent, Listener, sos_father), %% Have the process spawn off three children and test that the %% first is traced. - ?line [Child1, Child2, Child3] = spawn_children(Parent, 3), - ?line true = is_send_traced(Child1, Listener, child1), - ?line false = is_send_traced(Child2, Listener, child2), - ?line false = is_send_traced(Child3, Listener, child3), - ?line receive_nothing(), + [Child1, Child2, Child3] = spawn_children(Parent, 3), + true = is_send_traced(Child1, Listener, child1), + false = is_send_traced(Child2, Listener, child2), + false = is_send_traced(Child3, Listener, child3), + receive_nothing(), ok. %% Tests arguments to erlang:system_monitor/0,1,2 system_monitor_args(Config) when is_list(Config) -> - ?line Self = self(), + Self = self(), %% - ?line OldMonitor = erlang:system_monitor(undefined), - ?line undefined = erlang:system_monitor(Self, [{long_gc,0}]), - ?line MinT = case erlang:system_monitor() of - {Self,[{long_gc,T}]} when is_integer(T), T > 0 -> T; - Other1 -> test_server:fault(Other1) - end, - ?line {Self,[{long_gc,MinT}]} = erlang:system_monitor(), - ?line {Self,[{long_gc,MinT}]} = - erlang:system_monitor({Self,[{large_heap,0}]}), - ?line MinN = case erlang:system_monitor() of - {Self,[{large_heap,N}]} when is_integer(N), N > 0 -> N; - Other2 -> test_server:fault(Other2) - end, - ?line {Self,[{large_heap,MinN}]} = erlang:system_monitor(), - ?line {Self,[{large_heap,MinN}]} = - erlang:system_monitor(Self, [busy_port]), - ?line {Self,[busy_port]} = erlang:system_monitor(), - ?line {Self,[busy_port]} = - erlang:system_monitor({Self,[busy_dist_port]}), - ?line {Self,[busy_dist_port]} = erlang:system_monitor(), - ?line All = lists:sort([busy_port,busy_dist_port, - {long_gc,1},{large_heap,65535}]), - ?line {Self,[busy_dist_port]} = erlang:system_monitor(Self, All), - ?line {Self,A1} = erlang:system_monitor(), - ?line All = lists:sort(A1), - ?line {Self,A1} = erlang:system_monitor(Self, []), - ?line Pid = spawn(fun () -> receive {Self,die} -> exit(die) end end), - ?line Mref = erlang:monitor(process, Pid), - ?line undefined = erlang:system_monitor(Pid, All), - ?line {Pid,A2} = erlang:system_monitor(), - ?line All = lists:sort(A2), - ?line Pid ! {Self,die}, - ?line receive {'DOWN',Mref,_,_,_} -> ok end, - ?line undefined = erlang:system_monitor(OldMonitor), - ?line erlang:yield(), - ?line OldMonitor = erlang:system_monitor(), + OldMonitor = erlang:system_monitor(undefined), + undefined = erlang:system_monitor(Self, [{long_gc,0}]), + MinT = case erlang:system_monitor() of + {Self,[{long_gc,T}]} when is_integer(T), T > 0 -> T; + Other1 -> test_server:fault(Other1) + end, + {Self,[{long_gc,MinT}]} = erlang:system_monitor(), + {Self,[{long_gc,MinT}]} = + erlang:system_monitor({Self,[{large_heap,0}]}), + MinN = case erlang:system_monitor() of + {Self,[{large_heap,N}]} when is_integer(N), N > 0 -> N; + Other2 -> test_server:fault(Other2) + end, + {Self,[{large_heap,MinN}]} = erlang:system_monitor(), + {Self,[{large_heap,MinN}]} = + erlang:system_monitor(Self, [busy_port]), + {Self,[busy_port]} = erlang:system_monitor(), + {Self,[busy_port]} = + erlang:system_monitor({Self,[busy_dist_port]}), + {Self,[busy_dist_port]} = erlang:system_monitor(), + All = lists:sort([busy_port,busy_dist_port, + {long_gc,1},{large_heap,65535}]), + {Self,[busy_dist_port]} = erlang:system_monitor(Self, All), + {Self,A1} = erlang:system_monitor(), + All = lists:sort(A1), + {Self,A1} = erlang:system_monitor(Self, []), + Pid = spawn(fun () -> receive {Self,die} -> exit(die) end end), + Mref = erlang:monitor(process, Pid), + undefined = erlang:system_monitor(Pid, All), + {Pid,A2} = erlang:system_monitor(), + All = lists:sort(A2), + Pid ! {Self,die}, + receive {'DOWN',Mref,_,_,_} -> ok end, + undefined = erlang:system_monitor(OldMonitor), + erlang:yield(), + OldMonitor = erlang:system_monitor(), %% - ?line {'EXIT',{badarg,_}} = (catch erlang:system_monitor(atom)), - ?line {'EXIT',{badarg,_}} = (catch erlang:system_monitor({})), - ?line {'EXIT',{badarg,_}} = (catch erlang:system_monitor({1})), - ?line {'EXIT',{badarg,_}} = (catch erlang:system_monitor({1,2,3})), - ?line {'EXIT',{badarg,_}} = - (catch erlang:system_monitor({Self,atom})), - ?line {'EXIT',{badarg,_}} = - (catch erlang:system_monitor(atom, atom)), - ?line {'EXIT',{badarg,_}} = - (catch erlang:system_monitor({Self,[busy_port|busy_dist_port]})), - ?line {'EXIT',{badarg,_}} = - (catch erlang:system_monitor(Self, [{long_gc,-1}])), - ?line {'EXIT',{badarg,_}} = - (catch erlang:system_monitor({Self,[{long_gc,atom}]})), - ?line {'EXIT',{badarg,_}} = - (catch erlang:system_monitor(Self,[{large_heap,-1}])), - ?line {'EXIT',{badarg,_}} = - (catch erlang:system_monitor({Self,[{large_heap,atom}]})), + {'EXIT',{badarg,_}} = (catch erlang:system_monitor(atom)), + {'EXIT',{badarg,_}} = (catch erlang:system_monitor({})), + {'EXIT',{badarg,_}} = (catch erlang:system_monitor({1})), + {'EXIT',{badarg,_}} = (catch erlang:system_monitor({1,2,3})), + {'EXIT',{badarg,_}} = + (catch erlang:system_monitor({Self,atom})), + {'EXIT',{badarg,_}} = + (catch erlang:system_monitor(atom, atom)), + {'EXIT',{badarg,_}} = + (catch erlang:system_monitor({Self,[busy_port|busy_dist_port]})), + {'EXIT',{badarg,_}} = + (catch erlang:system_monitor(Self, [{long_gc,-1}])), + {'EXIT',{badarg,_}} = + (catch erlang:system_monitor({Self,[{long_gc,atom}]})), + {'EXIT',{badarg,_}} = + (catch erlang:system_monitor(Self,[{large_heap,-1}])), + {'EXIT',{badarg,_}} = + (catch erlang:system_monitor({Self,[{large_heap,atom}]})), ok. %% Tests arguments to erlang:system_monitor/0,1,2 more_system_monitor_args(Config) when is_list(Config) -> - ?line try_l(64000), - ?line try_l(16#7ffffff), - ?line try_l(16#3fffffff), - ?line try_l(16#7fffffff), - ?line try_l(16#ffffffff), + try_l(64000), + try_l(16#7ffffff), + try_l(16#3fffffff), + try_l(16#7fffffff), + try_l(16#ffffffff), ok. try_l(Val) -> @@ -468,29 +468,29 @@ try_l(Val) -> Arbitrary1 = 77777, Arbitrary2 = 88888, - ?line erlang:system_monitor(undefined), + erlang:system_monitor(undefined), - ?line undefined = erlang:system_monitor(Self, [{long_gc,Val},{large_heap,Arbitrary1}]), + undefined = erlang:system_monitor(Self, [{long_gc,Val},{large_heap,Arbitrary1}]), - ?line {Self,Comb0} = erlang:system_monitor(Self, [{long_gc,Arbitrary2},{large_heap,Val}]), - ?line [{large_heap,Arbitrary1},{long_gc,Val}] = lists:sort(Comb0), + {Self,Comb0} = erlang:system_monitor(Self, [{long_gc,Arbitrary2},{large_heap,Val}]), + [{large_heap,Arbitrary1},{long_gc,Val}] = lists:sort(Comb0), - ?line {Self,Comb1} = erlang:system_monitor(undefined), - ?line [{large_heap,Val},{long_gc,Arbitrary2}] = lists:sort(Comb1). + {Self,Comb1} = erlang:system_monitor(undefined), + [{large_heap,Val},{long_gc,Arbitrary2}] = lists:sort(Comb1). monitor_sys(Parent) -> receive - {monitor,Pid,long_schedule,Data} when is_pid(Pid) -> - io:format("Long schedule of ~w: ~w~n",[Pid,Data]), - Parent ! {Pid,Data}, - monitor_sys(Parent); - {monitor,Port,long_schedule,Data} when is_port(Port) -> - {name,Name} = erlang:port_info(Port,name), - io:format("Long schedule of ~w (~p): ~w~n",[Port,Name,Data]), - Parent ! {Port,Data}, - monitor_sys(Parent); - Other -> - erlang:display(Other) + {monitor,Pid,long_schedule,Data} when is_pid(Pid) -> + io:format("Long schedule of ~w: ~w~n",[Pid,Data]), + Parent ! {Pid,Data}, + monitor_sys(Parent); + {monitor,Port,long_schedule,Data} when is_port(Port) -> + {name,Name} = erlang:port_info(Port,name), + io:format("Long schedule of ~w (~p): ~w~n",[Port,Name,Data]), + Parent ! {Port,Data}, + monitor_sys(Parent); + Other -> + erlang:display(Other) end. start_monitor() -> @@ -505,10 +505,10 @@ system_monitor_long_schedule(Config) when is_list(Config) -> Path = proplists:get_value(data_dir, Config), erl_ddll:start(), case (catch load_driver(Path, slow_drv)) of - ok -> - do_system_monitor_long_schedule(); - _Error -> - {skip, "Unable to load slow_drv (windows or no usleep()?)"} + ok -> + do_system_monitor_long_schedule(); + _Error -> + {skip, "Unable to load slow_drv (windows or no usleep()?)"} end. do_system_monitor_long_schedule() -> start_monitor(), @@ -516,18 +516,18 @@ do_system_monitor_long_schedule() -> "ok" = erlang:port_control(Port,0,[]), Self = self(), receive - {Self,L} when is_list(L) -> - ok + {Self,L} when is_list(L) -> + ok after 1000 -> - ct:fail(no_trace_of_pid) + ct:fail(no_trace_of_pid) end, "ok" = erlang:port_control(Port,1,[]), "ok" = erlang:port_control(Port,2,[]), receive - {Port,LL} when is_list(LL) -> - ok + {Port,LL} when is_list(LL) -> + ok after 1000 -> - ct:fail(no_trace_of_port) + ct:fail(no_trace_of_port) end, port_close(Port), erlang:system_monitor(undefined), @@ -540,196 +540,196 @@ do_system_monitor_long_schedule() -> system_monitor_long_gc_1(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), try - case erts_debug:get_internal_state(force_heap_frags) of - true -> - {skip,"emulator with FORCE_HEAP_FRAGS defined"}; - false -> - %% Add ?LONG_GC_SLEEP ms to all gc - ?line erts_debug:set_internal_state(test_long_gc_sleep, - ?LONG_GC_SLEEP), - ?line LoadFun = fun () -> - garbage_collect(), - self() - end, - ?line long_gc(LoadFun, false) - end + case erts_debug:get_internal_state(force_heap_frags) of + true -> + {skip,"emulator with FORCE_HEAP_FRAGS defined"}; + false -> + %% Add ?LONG_GC_SLEEP ms to all gc + erts_debug:set_internal_state(test_long_gc_sleep, + ?LONG_GC_SLEEP), + LoadFun = fun () -> + garbage_collect(), + self() + end, + long_gc(LoadFun, false) + end after - erts_debug:set_internal_state(test_long_gc_sleep, 0), - erts_debug:set_internal_state(available_internal_state, false) + erts_debug:set_internal_state(test_long_gc_sleep, 0), + erts_debug:set_internal_state(available_internal_state, false) end. %% Tests erlang:system_monitor(Pid, [{long_gc,Time}]) system_monitor_long_gc_2(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), try - case erts_debug:get_internal_state(force_heap_frags) of - true -> - {skip,"emulator with FORCE_HEAP_FRAGS defined"}; - false -> - %% Add ?LONG_GC_SLEEP ms to all gc - ?line erts_debug:set_internal_state(test_long_gc_sleep, - ?LONG_GC_SLEEP), - ?line Parent = self(), - ?line LoadFun = - fun () -> - Ref = make_ref(), - Pid = - spawn_link( - fun () -> - garbage_collect(), - Parent ! {Ref, self()} - end), - receive {Ref, Pid} -> Pid end - end, - ?line long_gc(LoadFun, true), - ?line long_gc(LoadFun, true), - ?line long_gc(LoadFun, true) - end + case erts_debug:get_internal_state(force_heap_frags) of + true -> + {skip,"emulator with FORCE_HEAP_FRAGS defined"}; + false -> + %% Add ?LONG_GC_SLEEP ms to all gc + erts_debug:set_internal_state(test_long_gc_sleep, + ?LONG_GC_SLEEP), + Parent = self(), + LoadFun = + fun () -> + Ref = make_ref(), + Pid = + spawn_link( + fun () -> + garbage_collect(), + Parent ! {Ref, self()} + end), + receive {Ref, Pid} -> Pid end + end, + long_gc(LoadFun, true), + long_gc(LoadFun, true), + long_gc(LoadFun, true) + end after - erts_debug:set_internal_state(test_long_gc_sleep, 0), - erts_debug:set_internal_state(available_internal_state, false) + erts_debug:set_internal_state(test_long_gc_sleep, 0), + erts_debug:set_internal_state(available_internal_state, false) end. long_gc(LoadFun, ExpectMonMsg) -> - ?line Self = self(), - ?line Time = 1, - ?line OldMonitor = erlang:system_monitor(Self, [{long_gc,Time}]), - ?line Pid = LoadFun(), - ?line Ref = erlang:trace_delivered(Pid), - ?line receive {trace_delivered, Pid, Ref} -> ok end, - ?line {Self,[{long_gc,Time}]} = erlang:system_monitor(OldMonitor), - ?line case {long_gc_check(Pid, Time, undefined), ExpectMonMsg} of - {ok, true} when Pid =/= Self -> - ok; - {ok, false} -> - ?line ct:fail(unexpected_system_monitor_message_received); - {undefined, false} -> - ok; - {undefined, true} -> - ?line ct:fail(no_system_monitor_message_received) - end. + Self = self(), + Time = 1, + OldMonitor = erlang:system_monitor(Self, [{long_gc,Time}]), + Pid = LoadFun(), + Ref = erlang:trace_delivered(Pid), + receive {trace_delivered, Pid, Ref} -> ok end, + {Self,[{long_gc,Time}]} = erlang:system_monitor(OldMonitor), + case {long_gc_check(Pid, Time, undefined), ExpectMonMsg} of + {ok, true} when Pid =/= Self -> + ok; + {ok, false} -> + ct:fail(unexpected_system_monitor_message_received); + {undefined, false} -> + ok; + {undefined, true} -> + ct:fail(no_system_monitor_message_received) + end. long_gc_check(Pid, Time, Result) -> receive - {monitor,Pid,long_gc,L} = Monitor -> - case lists:foldl( - fun (_, error) -> - error; - ({timeout,T}, N) when is_integer(T), - Time =< T, T =< 10*?LONG_GC_SLEEP -> - %% OTP-7622. The time T must be within reasonable limits - %% for the test to pass. - N-1; - ({heap_size,_}, N) -> - N-1; - ({old_heap_size,_}, N) -> - N-1; - ({stack_size,_}, N) -> - N-1; - ({mbuf_size,_}, N) -> - N-1; - ({heap_block_size,_}, N) -> - N-1; - ({old_heap_block_size,_}, N) -> - N-1; - (_, _) -> - error - end, 7, L) of - 0 -> - long_gc_check(Pid, Time, ok); - error -> - {error,Monitor} - end; - {monitor,_,long_gc,_} -> - long_gc_check(Pid, Time, Result); - Other -> - {error,Other} + {monitor,Pid,long_gc,L} = Monitor -> + case lists:foldl( + fun (_, error) -> + error; + ({timeout,T}, N) when is_integer(T), + Time =< T, T =< 10*?LONG_GC_SLEEP -> + %% OTP-7622. The time T must be within reasonable limits + %% for the test to pass. + N-1; + ({heap_size,_}, N) -> + N-1; + ({old_heap_size,_}, N) -> + N-1; + ({stack_size,_}, N) -> + N-1; + ({mbuf_size,_}, N) -> + N-1; + ({heap_block_size,_}, N) -> + N-1; + ({old_heap_block_size,_}, N) -> + N-1; + (_, _) -> + error + end, 7, L) of + 0 -> + long_gc_check(Pid, Time, ok); + error -> + {error,Monitor} + end; + {monitor,_,long_gc,_} -> + long_gc_check(Pid, Time, Result); + Other -> + {error,Other} after 0 -> - Result + Result end. %% Tests erlang:system_monitor(Pid, [{large_heap,Size}]) system_monitor_large_heap_1(Config) when is_list(Config) -> - ?line LoadFun = - fun (Size) -> - List = seq(1,2*Size), - garbage_collect(), - true = lists:prefix([1], List), - self() - end, - ?line large_heap(LoadFun, false). + LoadFun = + fun (Size) -> + List = seq(1,2*Size), + garbage_collect(), + true = lists:prefix([1], List), + self() + end, + large_heap(LoadFun, false). %% Tests erlang:system_monitor(Pid, [{large_heap,Size}]) system_monitor_large_heap_2(Config) when is_list(Config) -> - ?line Parent = self(), - ?line LoadFun = - fun (Size) -> - Ref = make_ref(), - Pid = - spawn_opt(fun () -> - garbage_collect(), - Parent ! {Ref, self()} - end, - [link, {min_heap_size, 2*Size}]), - receive {Ref, Pid} -> Pid end - end, - ?line large_heap(LoadFun, true). + Parent = self(), + LoadFun = + fun (Size) -> + Ref = make_ref(), + Pid = + spawn_opt(fun () -> + garbage_collect(), + Parent ! {Ref, self()} + end, + [link, {min_heap_size, 2*Size}]), + receive {Ref, Pid} -> Pid end + end, + large_heap(LoadFun, true). large_heap(LoadFun, ExpectMonMsg) -> ct:timetrap({seconds, 20}), %% - ?line Size = 65535, - ?line Self = self(), - ?line NewMonitor = {Self,[{large_heap,Size}]}, - ?line OldMonitor = erlang:system_monitor(NewMonitor), - ?line Pid = LoadFun(Size), - ?line Ref = erlang:trace_delivered(Pid), - ?line receive {trace_delivered, Pid, Ref} -> ok end, - ?line {Self,[{large_heap,Size}]} = erlang:system_monitor(OldMonitor), - ?line case {large_heap_check(Pid, Size, undefined), ExpectMonMsg} of - {ok, true} when Pid =/= Self -> - ?line ok; - {ok, false} -> - ?line ct:fail(unexpected_system_monitor_message_received); - {undefined, false} -> - ?line ok; - {undefined, true} -> - ?line ct:fail(no_system_monitor_message_received) - end, + Size = 65535, + Self = self(), + NewMonitor = {Self,[{large_heap,Size}]}, + OldMonitor = erlang:system_monitor(NewMonitor), + Pid = LoadFun(Size), + Ref = erlang:trace_delivered(Pid), + receive {trace_delivered, Pid, Ref} -> ok end, + {Self,[{large_heap,Size}]} = erlang:system_monitor(OldMonitor), + case {large_heap_check(Pid, Size, undefined), ExpectMonMsg} of + {ok, true} when Pid =/= Self -> + ok; + {ok, false} -> + ct:fail(unexpected_system_monitor_message_received); + {undefined, false} -> + ok; + {undefined, true} -> + ct:fail(no_system_monitor_message_received) + end, ok. large_heap_check(Pid, Size, Result) -> receive - {monitor,Pid,large_heap,L} = Monitor -> - case lists:foldl( - fun (_, error) -> - error; - ({heap_size,_}, N) -> - N-1; - ({old_heap_size,_}, N) -> - N-1; - ({stack_size,_}, N) -> - N-1; - ({mbuf_size,_}, N) -> - N-1; - ({heap_block_size,_}, N) -> - N-1; - ({old_heap_block_size,_}, N) -> - N-1; - (_, _) -> - error - end, 6, L) of - 0 -> - large_heap_check(Pid, Size, ok); - error -> - {error,Monitor} - end; - {monitor,_,large_heap,_} -> - large_heap_check(Pid, Size, Result); - Other -> - {error,Other} + {monitor,Pid,large_heap,L} = Monitor -> + case lists:foldl( + fun (_, error) -> + error; + ({heap_size,_}, N) -> + N-1; + ({old_heap_size,_}, N) -> + N-1; + ({stack_size,_}, N) -> + N-1; + ({mbuf_size,_}, N) -> + N-1; + ({heap_block_size,_}, N) -> + N-1; + ({old_heap_block_size,_}, N) -> + N-1; + (_, _) -> + error + end, 6, L) of + 0 -> + large_heap_check(Pid, Size, ok); + error -> + {error,Monitor} + end; + {monitor,_,large_heap,_} -> + large_heap_check(Pid, Size, Result); + Other -> + {error,Other} after 0 -> - Result + Result end. seq(N, M) -> @@ -744,11 +744,11 @@ seq(N, M, R) -> is_send_traced(Pid, Listener, Msg) -> Pid ! {send_please, Listener, Msg}, receive - Any -> - {trace, Pid, send, Msg, Listener} = Any, - true + Any -> + {trace, Pid, send, Msg, Listener} = Any, + true after 1000 -> - false + false end. %% This procedure assumes that the Parent process is send traced. @@ -762,131 +762,131 @@ spawn_children(Parent, Number, Result) -> Self = self(), Parent ! {spawn_please, Self, fun process/0}, Child = - receive - {trace, Parent, send, {spawned, Pid}, Self} -> Pid - end, receive - {spawned, Child} -> - spawn_children(Parent, Number-1, [Child|Result]) + {trace, Parent, send, {spawned, Pid}, Self} -> Pid + end, + receive + {spawned, Child} -> + spawn_children(Parent, Number-1, [Child|Result]) end. %% Test erlang:suspend/1 and erlang:resume/1. suspend(Config) when is_list(Config) -> ct:timetrap({minutes,2}), - ?line Worker = fun_spawn(fun worker/0), + Worker = fun_spawn(fun worker/0), %% Suspend a process and test that it is suspended. - ?line ok = do_suspend(Worker, 10000), + ok = do_suspend(Worker, 10000), ok. do_suspend(_Pid, 0) -> - ?line ok; + ok; do_suspend(Pid, N) -> %% Suspend a process and test that it is suspended. - ?line true = erlang:suspend_process(Pid), - ?line {status, suspended} = process_info(Pid, status), + true = erlang:suspend_process(Pid), + {status, suspended} = process_info(Pid, status), %% Unsuspend the process and make sure it starts working. - ?line true = erlang:resume_process(Pid), - ?line case process_info(Pid, status) of - {status, runnable} -> ?line ok; - {status, running} -> ?line ok; - {status, garbage_collecting} -> ?line ok; - ST -> ?line ct:fail(ST) - end, - ?line erlang:yield(), - ?line do_suspend(Pid, N-1). + true = erlang:resume_process(Pid), + case process_info(Pid, status) of + {status, runnable} -> ok; + {status, running} -> ok; + {status, garbage_collecting} -> ok; + ST -> ct:fail(ST) + end, + erlang:yield(), + do_suspend(Pid, N-1). mutual_suspend(Config) when is_list(Config) -> - ?line TimeoutSecs = 5*60, + TimeoutSecs = 5*60, ct:timetrap({seconds, TimeoutSecs}), - ?line Parent = self(), - ?line Fun = fun () -> - receive - {go, Pid} -> - do_mutual_suspend(Pid, 100000) - end, - Parent ! {done, self()}, - receive after infinity -> ok end - end, - ?line P1 = spawn_link(Fun), - ?line P2 = spawn_link(Fun), - ?line T1 = erlang:start_timer((TimeoutSecs - 5)*1000, self(), oops), - ?line T2 = erlang:start_timer((TimeoutSecs - 5)*1000, self(), oops), - ?line P1 ! {go, P2}, - ?line P2 ! {go, P1}, - ?line Res1 = receive - {done, P1} -> done; - {timeout,T1,_} -> timeout - end, - ?line Res2 = receive - {done, P2} -> done; - {timeout,T2,_} -> timeout - end, - ?line P1S = process_info(P1, status), - ?line P2S = process_info(P2, status), - ?line io:format("P1S=~p P2S=~p", [P1S, P2S]), - ?line false = {status, suspended} == P1S, - ?line false = {status, suspended} == P2S, - ?line unlink(P1), exit(P1, bang), - ?line unlink(P2), exit(P2, bang), - ?line done = Res1, - ?line done = Res2, - ?line ok. - + Parent = self(), + Fun = fun () -> + receive + {go, Pid} -> + do_mutual_suspend(Pid, 100000) + end, + Parent ! {done, self()}, + receive after infinity -> ok end + end, + P1 = spawn_link(Fun), + P2 = spawn_link(Fun), + T1 = erlang:start_timer((TimeoutSecs - 5)*1000, self(), oops), + T2 = erlang:start_timer((TimeoutSecs - 5)*1000, self(), oops), + P1 ! {go, P2}, + P2 ! {go, P1}, + Res1 = receive + {done, P1} -> done; + {timeout,T1,_} -> timeout + end, + Res2 = receive + {done, P2} -> done; + {timeout,T2,_} -> timeout + end, + P1S = process_info(P1, status), + P2S = process_info(P2, status), + io:format("P1S=~p P2S=~p", [P1S, P2S]), + false = {status, suspended} == P1S, + false = {status, suspended} == P2S, + unlink(P1), exit(P1, bang), + unlink(P2), exit(P2, bang), + done = Res1, + done = Res2, + ok. + do_mutual_suspend(_Pid, 0) -> - ?line ok; + ok; do_mutual_suspend(Pid, N) -> %% Suspend a process and test that it is suspended. - ?line true = erlang:suspend_process(Pid), - ?line {status, suspended} = process_info(Pid, status), + true = erlang:suspend_process(Pid), + {status, suspended} = process_info(Pid, status), %% Unsuspend the process. - ?line true = erlang:resume_process(Pid), - ?line do_mutual_suspend(Pid, N-1). + true = erlang:resume_process(Pid), + do_mutual_suspend(Pid, N-1). suspend_exit(Config) when is_list(Config) -> ct:timetrap({minutes, 2}), rand:seed(exsplus, {4711,17,4711}), - ?line do_suspend_exit(5000), - ?line ok. + do_suspend_exit(5000), + ok. do_suspend_exit(0) -> - ?line ok; + ok; do_suspend_exit(N) -> Work = rand:uniform(50), - ?line Parent = self(), - ?line {Suspendee, Mon2} - = spawn_monitor(fun () -> - suspend_exit_work(Work), - exit(normal) - end), - ?line {Suspender, Mon1} - = spawn_monitor( - fun () -> - suspend_exit_work(Work div 2), - Parent ! {doing_suspend, self()}, - case catch erlang:suspend_process(Suspendee) of - {'EXIT', _} -> - ok; - true -> - ?line erlang:resume_process(Suspendee) - end - end), - ?line receive - {doing_suspend, Suspender} -> - case N rem 2 of - 0 -> exit(Suspender, bang); - 1 -> ok - end - end, - ?line receive {'DOWN', Mon1, process, Suspender, _} -> ok end, - ?line receive {'DOWN', Mon2, process, Suspendee, _} -> ok end, - ?line do_suspend_exit(N-1). - - - - + Parent = self(), + {Suspendee, Mon2} + = spawn_monitor(fun () -> + suspend_exit_work(Work), + exit(normal) + end), + {Suspender, Mon1} + = spawn_monitor( + fun () -> + suspend_exit_work(Work div 2), + Parent ! {doing_suspend, self()}, + case catch erlang:suspend_process(Suspendee) of + {'EXIT', _} -> + ok; + true -> + erlang:resume_process(Suspendee) + end + end), + receive + {doing_suspend, Suspender} -> + case N rem 2 of + 0 -> exit(Suspender, bang); + 1 -> ok + end + end, + receive {'DOWN', Mon1, process, Suspender, _} -> ok end, + receive {'DOWN', Mon2, process, Suspendee, _} -> ok end, + do_suspend_exit(N-1). + + + + suspend_exit_work(0) -> ok; suspend_exit_work(N) -> @@ -900,303 +900,303 @@ chk_suspended(P, Bool, Line) -> suspender_exit(Config) when is_list(Config) -> ct:timetrap({minutes, 3}), - ?line P1 = spawn_link(fun () -> receive after infinity -> ok end end), - ?line {'EXIT', _} = (catch erlang:resume_process(P1)), - ?line {P2, M2} = spawn_monitor( - fun () -> - ?CHK_SUSPENDED(P1, false), - erlang:suspend_process(P1), - ?CHK_SUSPENDED(P1, true), - erlang:suspend_process(P1), - erlang:suspend_process(P1), - erlang:suspend_process(P1), - ?CHK_SUSPENDED(P1, true), - erlang:resume_process(P1), - erlang:resume_process(P1), - erlang:resume_process(P1), - ?CHK_SUSPENDED(P1, true), - erlang:resume_process(P1), - ?CHK_SUSPENDED(P1, false), - erlang:suspend_process(P1), - erlang:suspend_process(P1), - erlang:suspend_process(P1), - ?CHK_SUSPENDED(P1, true), - exit(bang) - end), - ?line receive - {'DOWN', M2,process,P2,R2} -> - ?line bang = R2, - ?line ?CHK_SUSPENDED(P1, false) - end, - ?line Parent = self(), - ?line {P3, M3} = spawn_monitor( - fun () -> - erlang:suspend_process(P1), - ?CHK_SUSPENDED(P1, true), - Parent ! self(), - receive after infinity -> ok end - end), - ?line {P4, M4} = spawn_monitor( - fun () -> - erlang:suspend_process(P1), - ?CHK_SUSPENDED(P1, true), - Parent ! self(), - receive after infinity -> ok end - end), - ?line {P5, M5} = spawn_monitor( - fun () -> - erlang:suspend_process(P1), - ?CHK_SUSPENDED(P1, true), - Parent ! self(), - receive after infinity -> ok end - end), - ?line {P6, M6} = spawn_monitor( - fun () -> - erlang:suspend_process(P1), - ?CHK_SUSPENDED(P1, true), - Parent ! self(), - receive after infinity -> ok end - end), - ?line {P7, M7} = spawn_monitor( - fun () -> - erlang:suspend_process(P1), - ?CHK_SUSPENDED(P1, true), - Parent ! self(), - receive after infinity -> ok end - end), - ?line receive P3 -> ok end, - ?line receive P4 -> ok end, - ?line receive P5 -> ok end, - ?line receive P6 -> ok end, - ?line receive P7 -> ok end, - ?line ?CHK_SUSPENDED(P1, true), - ?line exit(P3, bang), - ?line receive - {'DOWN',M3,process,P3,R3} -> - ?line bang = R3, - ?line ?CHK_SUSPENDED(P1, true) - end, - ?line exit(P4, bang), - ?line receive - {'DOWN',M4,process,P4,R4} -> - ?line bang = R4, - ?line ?CHK_SUSPENDED(P1, true) - end, - ?line exit(P5, bang), - ?line receive - {'DOWN',M5,process,P5,R5} -> - ?line bang = R5, - ?line ?CHK_SUSPENDED(P1, true) - end, - ?line exit(P6, bang), - ?line receive - {'DOWN',M6,process,P6,R6} -> - ?line bang = R6, - ?line ?CHK_SUSPENDED(P1, true) - end, - ?line exit(P7, bang), - ?line receive - {'DOWN',M7,process,P7,R7} -> - ?line bang = R7, - ?line ?CHK_SUSPENDED(P1, false) - end, - ?line unlink(P1), - ?line exit(P1, bong), - ?line ok. - + P1 = spawn_link(fun () -> receive after infinity -> ok end end), + {'EXIT', _} = (catch erlang:resume_process(P1)), + {P2, M2} = spawn_monitor( + fun () -> + ?CHK_SUSPENDED(P1, false), + erlang:suspend_process(P1), + ?CHK_SUSPENDED(P1, true), + erlang:suspend_process(P1), + erlang:suspend_process(P1), + erlang:suspend_process(P1), + ?CHK_SUSPENDED(P1, true), + erlang:resume_process(P1), + erlang:resume_process(P1), + erlang:resume_process(P1), + ?CHK_SUSPENDED(P1, true), + erlang:resume_process(P1), + ?CHK_SUSPENDED(P1, false), + erlang:suspend_process(P1), + erlang:suspend_process(P1), + erlang:suspend_process(P1), + ?CHK_SUSPENDED(P1, true), + exit(bang) + end), + receive + {'DOWN', M2,process,P2,R2} -> + bang = R2, + ?CHK_SUSPENDED(P1, false) + end, + Parent = self(), + {P3, M3} = spawn_monitor( + fun () -> + erlang:suspend_process(P1), + ?CHK_SUSPENDED(P1, true), + Parent ! self(), + receive after infinity -> ok end + end), + {P4, M4} = spawn_monitor( + fun () -> + erlang:suspend_process(P1), + ?CHK_SUSPENDED(P1, true), + Parent ! self(), + receive after infinity -> ok end + end), + {P5, M5} = spawn_monitor( + fun () -> + erlang:suspend_process(P1), + ?CHK_SUSPENDED(P1, true), + Parent ! self(), + receive after infinity -> ok end + end), + {P6, M6} = spawn_monitor( + fun () -> + erlang:suspend_process(P1), + ?CHK_SUSPENDED(P1, true), + Parent ! self(), + receive after infinity -> ok end + end), + {P7, M7} = spawn_monitor( + fun () -> + erlang:suspend_process(P1), + ?CHK_SUSPENDED(P1, true), + Parent ! self(), + receive after infinity -> ok end + end), + receive P3 -> ok end, + receive P4 -> ok end, + receive P5 -> ok end, + receive P6 -> ok end, + receive P7 -> ok end, + ?CHK_SUSPENDED(P1, true), + exit(P3, bang), + receive + {'DOWN',M3,process,P3,R3} -> + bang = R3, + ?CHK_SUSPENDED(P1, true) + end, + exit(P4, bang), + receive + {'DOWN',M4,process,P4,R4} -> + bang = R4, + ?CHK_SUSPENDED(P1, true) + end, + exit(P5, bang), + receive + {'DOWN',M5,process,P5,R5} -> + bang = R5, + ?CHK_SUSPENDED(P1, true) + end, + exit(P6, bang), + receive + {'DOWN',M6,process,P6,R6} -> + bang = R6, + ?CHK_SUSPENDED(P1, true) + end, + exit(P7, bang), + receive + {'DOWN',M7,process,P7,R7} -> + bang = R7, + ?CHK_SUSPENDED(P1, false) + end, + unlink(P1), + exit(P1, bong), + ok. + suspend_system_limit(Config) when is_list(Config) -> case os:getenv("ERL_EXTREME_TESTING") of - "true" -> + "true" -> ct:timetrap({minutes, 3*60}), - ?line P = spawn_link(fun () -> receive after infinity -> ok end end), - ?line suspend_until_system_limit(P), - ?line unlink(P), - ?line exit(P, bye), - ?line ok; - _ -> - {skip, "Takes too long time for normal testing"} + P = spawn_link(fun () -> receive after infinity -> ok end end), + suspend_until_system_limit(P), + unlink(P), + exit(P, bye), + ok; + _ -> + {skip, "Takes too long time for normal testing"} end. suspend_until_system_limit(P) -> - ?line suspend_until_system_limit(P, 0, 0). + suspend_until_system_limit(P, 0, 0). suspend_until_system_limit(P, N, M) -> NewM = case M of - 1 -> - ?line ?CHK_SUSPENDED(P, true), 2; - 1000000 -> - erlang:display(N), 1; - _ -> - M+1 - end, - ?line case catch erlang:suspend_process(P) of - true -> - suspend_until_system_limit(P, N+1, NewM); - {'EXIT', R} when R == system_limit; - element(1, R) == system_limit -> - ?line io:format("system limit at ~p~n", [N]), - ?line resume_from_system_limit(P, N, 0); - Error -> - ?line ct:fail(Error) - end. + 1 -> + ?CHK_SUSPENDED(P, true), 2; + 1000000 -> + erlang:display(N), 1; + _ -> + M+1 + end, + case catch erlang:suspend_process(P) of + true -> + suspend_until_system_limit(P, N+1, NewM); + {'EXIT', R} when R == system_limit; + element(1, R) == system_limit -> + io:format("system limit at ~p~n", [N]), + resume_from_system_limit(P, N, 0); + Error -> + ct:fail(Error) + end. resume_from_system_limit(P, 0, _) -> - ?line ?CHK_SUSPENDED(P, false), - ?line {'EXIT', _} = (catch erlang:resume_process(P)), - ?line ok; + ?CHK_SUSPENDED(P, false), + {'EXIT', _} = (catch erlang:resume_process(P)), + ok; resume_from_system_limit(P, N, M) -> - ?line NewM = case M of - 1 -> - ?line ?CHK_SUSPENDED(P, true), 2; - 1000000 -> - erlang:display(N), 1; - _ -> - M+1 - end, - ?line erlang:resume_process(P), - ?line resume_from_system_limit(P, N-1, NewM). + NewM = case M of + 1 -> + ?CHK_SUSPENDED(P, true), 2; + 1000000 -> + erlang:display(N), 1; + _ -> + M+1 + end, + erlang:resume_process(P), + resume_from_system_limit(P, N-1, NewM). -record(susp_info, {async = 0, - dbl_async = 0, - synced = 0, - async_once = 0}). + dbl_async = 0, + synced = 0, + async_once = 0}). suspend_opts(Config) when is_list(Config) -> ct:timetrap({minutes, 3}), - ?line Self = self(), - ?line wait_for_empty_runq(10), - ?line Tok = spawn_link(fun () -> - Self ! self(), - tok_trace_loop(Self, 0, 1000000000) - end), - ?line TC = 1000, - ?line receive Tok -> ok end, - ?line SF = fun (N, #susp_info {async = A, - dbl_async = AA, - synced = S, - async_once = AO} = Acc) -> - ?line erlang:suspend_process(Tok, [asynchronous]), - ?line Res = case {suspend_count(Tok), N rem 4} of - {0, 2} -> - ?line erlang:suspend_process(Tok, - [asynchronous]), - case suspend_count(Tok) of - 2 -> - ?line erlang:resume_process(Tok), - ?line Acc#susp_info{async = A+1}; - 0 -> - ?line erlang:resume_process(Tok), - ?line Acc#susp_info{async = A+1, - dbl_async = AA+1} - end; - {0, 1} -> - ?line erlang:suspend_process(Tok, - [asynchronous, - unless_suspending]), - case suspend_count(Tok) of - 1 -> - ?line Acc#susp_info{async = A+1}; - 0 -> - ?line Acc#susp_info{async = A+1, - async_once = AO+1} - end; - {0, 0} -> - ?line erlang:suspend_process(Tok, - [unless_suspending]), - ?line 1 = suspend_count(Tok), - ?line Acc#susp_info{async = A+1, - synced = S+1}; - {0, _} -> - ?line Acc#susp_info{async = A+1}; - _ -> - Acc - end, - ?line erlang:resume_process(Tok), - ?line erlang:yield(), - ?line Res - end, - ?line SI = repeat_acc(SF, TC, #susp_info{}), - ?line erlang:suspend_process(Tok, [asynchronous]), + Self = self(), + wait_for_empty_runq(10), + Tok = spawn_link(fun () -> + Self ! self(), + tok_trace_loop(Self, 0, 1000000000) + end), + TC = 1000, + receive Tok -> ok end, + SF = fun (N, #susp_info {async = A, + dbl_async = AA, + synced = S, + async_once = AO} = Acc) -> + erlang:suspend_process(Tok, [asynchronous]), + Res = case {suspend_count(Tok), N rem 4} of + {0, 2} -> + erlang:suspend_process(Tok, + [asynchronous]), + case suspend_count(Tok) of + 2 -> + erlang:resume_process(Tok), + Acc#susp_info{async = A+1}; + 0 -> + erlang:resume_process(Tok), + Acc#susp_info{async = A+1, + dbl_async = AA+1} + end; + {0, 1} -> + erlang:suspend_process(Tok, + [asynchronous, + unless_suspending]), + case suspend_count(Tok) of + 1 -> + Acc#susp_info{async = A+1}; + 0 -> + Acc#susp_info{async = A+1, + async_once = AO+1} + end; + {0, 0} -> + erlang:suspend_process(Tok, + [unless_suspending]), + 1 = suspend_count(Tok), + Acc#susp_info{async = A+1, + synced = S+1}; + {0, _} -> + Acc#susp_info{async = A+1}; + _ -> + Acc + end, + erlang:resume_process(Tok), + erlang:yield(), + Res + end, + SI = repeat_acc(SF, TC, #susp_info{}), + erlang:suspend_process(Tok, [asynchronous]), %% Verify that it eventually suspends - ?line WaitTime0 = 10, - ?line WaitTime1 = case {erlang:system_info(debug_compiled), - erlang:system_info(lock_checking)} of - {false, false} -> - WaitTime0; - {false, true} -> - WaitTime0*5; - _ -> - WaitTime0*10 - end, - ?line WaitTime = case {erlang:system_info(schedulers_online), - erlang:system_info(logical_processors)} of - {Schdlrs, CPUs} when is_integer(CPUs), - Schdlrs =< CPUs -> - WaitTime1; - _ -> - WaitTime1*10 - end, - ?line receive after WaitTime -> ok end, - ?line 1 = suspend_count(Tok), - ?line erlang:suspend_process(Tok, [asynchronous]), - ?line 2 = suspend_count(Tok), - ?line erlang:suspend_process(Tok, [asynchronous]), - ?line 3 = suspend_count(Tok), - ?line erlang:suspend_process(Tok), - ?line 4 = suspend_count(Tok), - ?line erlang:suspend_process(Tok), - ?line 5 = suspend_count(Tok), - ?line erlang:suspend_process(Tok, [unless_suspending]), - ?line 5 = suspend_count(Tok), - ?line erlang:suspend_process(Tok, [unless_suspending, - asynchronous]), - ?line 5 = suspend_count(Tok), - ?line erlang:resume_process(Tok), - ?line erlang:resume_process(Tok), - ?line erlang:resume_process(Tok), - ?line erlang:resume_process(Tok), - ?line 1 = suspend_count(Tok), - ?line io:format("Main suspends: ~p~n" - "Main async: ~p~n" - "Double async: ~p~n" - "Async once: ~p~n" - "Synced: ~p~n", - [TC, - SI#susp_info.async, - SI#susp_info.dbl_async, - SI#susp_info.async_once, - SI#susp_info.synced]), - ?line case erlang:system_info(schedulers_online) of - 1 -> - ?line ok; - _ -> - ?line true = SI#susp_info.async =/= 0 - end, - ?line unlink(Tok), - ?line exit(Tok, bang), - ?line ok. + WaitTime0 = 10, + WaitTime1 = case {erlang:system_info(debug_compiled), + erlang:system_info(lock_checking)} of + {false, false} -> + WaitTime0; + {false, true} -> + WaitTime0*5; + _ -> + WaitTime0*10 + end, + WaitTime = case {erlang:system_info(schedulers_online), + erlang:system_info(logical_processors)} of + {Schdlrs, CPUs} when is_integer(CPUs), + Schdlrs =< CPUs -> + WaitTime1; + _ -> + WaitTime1*10 + end, + receive after WaitTime -> ok end, + 1 = suspend_count(Tok), + erlang:suspend_process(Tok, [asynchronous]), + 2 = suspend_count(Tok), + erlang:suspend_process(Tok, [asynchronous]), + 3 = suspend_count(Tok), + erlang:suspend_process(Tok), + 4 = suspend_count(Tok), + erlang:suspend_process(Tok), + 5 = suspend_count(Tok), + erlang:suspend_process(Tok, [unless_suspending]), + 5 = suspend_count(Tok), + erlang:suspend_process(Tok, [unless_suspending, + asynchronous]), + 5 = suspend_count(Tok), + erlang:resume_process(Tok), + erlang:resume_process(Tok), + erlang:resume_process(Tok), + erlang:resume_process(Tok), + 1 = suspend_count(Tok), + io:format("Main suspends: ~p~n" + "Main async: ~p~n" + "Double async: ~p~n" + "Async once: ~p~n" + "Synced: ~p~n", + [TC, + SI#susp_info.async, + SI#susp_info.dbl_async, + SI#susp_info.async_once, + SI#susp_info.synced]), + case erlang:system_info(schedulers_online) of + 1 -> + ok; + _ -> + true = SI#susp_info.async =/= 0 + end, + unlink(Tok), + exit(Tok, bang), + ok. suspend_count(Suspendee) -> suspend_count(self(), Suspendee). suspend_count(Suspender, Suspendee) -> {suspending, SList} = process_info(Suspender, suspending), - + case lists:keysearch(Suspendee, 1, SList) of - {value, {_Suspendee, 0, 0}} -> - ?line ct:fail({bad_suspendee_list, SList}); - {value, {Suspendee, Count, 0}} when is_integer(Count), Count > 0 -> - {status, suspended} = process_info(Suspendee, status), - Count; - {value, {Suspendee, 0, Outstanding}} when is_integer(Outstanding), - Outstanding > 0 -> - 0; - false -> - 0; - Error -> - ?line ct:fail({bad_suspendee_list, Error, SList}) + {value, {_Suspendee, 0, 0}} -> + ct:fail({bad_suspendee_list, SList}); + {value, {Suspendee, Count, 0}} when is_integer(Count), Count > 0 -> + {status, suspended} = process_info(Suspendee, status), + Count; + {value, {Suspendee, 0, Outstanding}} when is_integer(Outstanding), + Outstanding > 0 -> + 0; + false -> + 0; + Error -> + ct:fail({bad_suspendee_list, Error, SList}) end. - + repeat_acc(Fun, N, Acc) -> repeat_acc(Fun, 0, N, Acc). @@ -1204,100 +1204,100 @@ repeat_acc(_Fun, N, N, Acc) -> Acc; repeat_acc(Fun, N, M, Acc) -> repeat_acc(Fun, N+1, M, Fun(N, Acc)). - + %% Tests that waiting process can be suspended %% (bug in R2D and earlier; see OTP-1488). %% Test that a waiting process can be suspended. suspend_waiting(Config) when is_list(Config) -> - ?line Process = fun_spawn(fun process/0), - ?line receive after 1 -> ok end, - ?line true = erlang:suspend_process(Process), - ?line {status, suspended} = process_info(Process, status), + Process = fun_spawn(fun process/0), + receive after 1 -> ok end, + true = erlang:suspend_process(Process), + {status, suspended} = process_info(Process, status), ok. %% Test that erlang:trace(new, true, ...) is cleared when tracer dies. new_clear(Config) when is_list(Config) -> - ?line Tracer = spawn(fun receiver/0), - ?line 0 = erlang:trace(new, true, [send, {tracer, Tracer}]), - ?line {flags, [send]} = erlang:trace_info(new, flags), - ?line {tracer, Tracer} = erlang:trace_info(new, tracer), - ?line Mref = erlang:monitor(process, Tracer), - ?line true = exit(Tracer, done), + Tracer = spawn(fun receiver/0), + 0 = erlang:trace(new, true, [send, {tracer, Tracer}]), + {flags, [send]} = erlang:trace_info(new, flags), + {tracer, Tracer} = erlang:trace_info(new, tracer), + Mref = erlang:monitor(process, Tracer), + true = exit(Tracer, done), receive - {'DOWN',Mref,_,_,_} -> ok + {'DOWN',Mref,_,_,_} -> ok end, - ?line {flags, []} = erlang:trace_info(new, flags), - ?line {tracer, []} = erlang:trace_info(new, tracer), + {flags, []} = erlang:trace_info(new, flags), + {tracer, []} = erlang:trace_info(new, tracer), ok. %% Test that erlang:trace(all, false, ...) works without tracer. existing_clear(Config) when is_list(Config) -> - ?line Self = self(), - - ?line Tracer = fun_spawn(fun receiver/0), - ?line N = erlang:trace(existing, true, [send, {tracer, Tracer}]), - ?line {flags, [send]} = erlang:trace_info(Self, flags), - ?line {tracer, Tracer} = erlang:trace_info(Self, tracer), - ?line M = erlang:trace(all, false, [all]), - ?line io:format("Started trace on ~p processes and stopped on ~p~n", - [N, M]), - ?line {flags, []} = erlang:trace_info(Self, flags), - ?line {tracer, []} = erlang:trace_info(Self, tracer), - ?line M = N + 1, % Since trace could not be enabled on the tracer. + Self = self(), + + Tracer = fun_spawn(fun receiver/0), + N = erlang:trace(existing, true, [send, {tracer, Tracer}]), + {flags, [send]} = erlang:trace_info(Self, flags), + {tracer, Tracer} = erlang:trace_info(Self, tracer), + M = erlang:trace(all, false, [all]), + io:format("Started trace on ~p processes and stopped on ~p~n", + [N, M]), + {flags, []} = erlang:trace_info(Self, flags), + {tracer, []} = erlang:trace_info(Self, tracer), + M = N + 1, % Since trace could not be enabled on the tracer. ok. %% Test that an invalid flag cause badarg bad_flag(Config) when is_list(Config) -> %% A bad flag could deadlock the SMP emulator in erts-5.5 - ?line {'EXIT', {badarg, _}} = (catch erlang:trace(new, - true, - [not_a_valid_flag])), - ?line ok. + {'EXIT', {badarg, _}} = (catch erlang:trace(new, + true, + [not_a_valid_flag])), + ok. %% Test erlang:trace_delivered/1 trace_delivered(Config) when is_list(Config) -> ct:timetrap({minutes, 1}), - ?line TokLoops = 10000, - ?line Go = make_ref(), - ?line Parent = self(), - ?line Tok = spawn(fun () -> - receive Go -> gone end, - tok_trace_loop(Parent, 0, TokLoops) - end), - ?line 1 = erlang:trace(Tok, true, [procs]), - ?line Mon = erlang:monitor(process, Tok), - ?line NoOfTraceMessages = 4*TokLoops + 1, - ?line io:format("Expect a total of ~p trace messages~n", - [NoOfTraceMessages]), - ?line Tok ! Go, - ?line NoOfTraceMessages = drop_trace_until_down(Tok, Mon), - ?line receive - Msg -> - ?line ct:fail({unexpected_message, Msg}) - after 1000 -> - ?line ok - end. + TokLoops = 10000, + Go = make_ref(), + Parent = self(), + Tok = spawn(fun () -> + receive Go -> gone end, + tok_trace_loop(Parent, 0, TokLoops) + end), + 1 = erlang:trace(Tok, true, [procs]), + Mon = erlang:monitor(process, Tok), + NoOfTraceMessages = 4*TokLoops + 1, + io:format("Expect a total of ~p trace messages~n", + [NoOfTraceMessages]), + Tok ! Go, + NoOfTraceMessages = drop_trace_until_down(Tok, Mon), + receive + Msg -> + ct:fail({unexpected_message, Msg}) + after 1000 -> + ok + end. drop_trace_until_down(Proc, Mon) -> drop_trace_until_down(Proc, Mon, false, 0, 0). drop_trace_until_down(Proc, Mon, TDRef, N, D) -> case receive Msg -> Msg end of - {trace_delivered, Proc, TDRef} -> - io:format("~p trace messages on 'DOWN'~n", [D]), - io:format("Got a total of ~p trace messages~n", [N]), - N; - {'DOWN', Mon, process, Proc, _} -> - Ref = erlang:trace_delivered(Proc), - drop_trace_until_down(Proc, Mon, Ref, N, N); - Trace when is_tuple(Trace), - element(1, Trace) == trace, - element(2, Trace) == Proc -> - drop_trace_until_down(Proc, Mon, TDRef, N+1, D) + {trace_delivered, Proc, TDRef} -> + io:format("~p trace messages on 'DOWN'~n", [D]), + io:format("Got a total of ~p trace messages~n", [N]), + N; + {'DOWN', Mon, process, Proc, _} -> + Ref = erlang:trace_delivered(Proc), + drop_trace_until_down(Proc, Mon, Ref, N, N); + Trace when is_tuple(Trace), + element(1, Trace) == trace, + element(2, Trace) == Proc -> + drop_trace_until_down(Proc, Mon, TDRef, N+1, D) end. tok_trace_loop(_, N, N) -> @@ -1314,17 +1314,17 @@ tok_trace_loop(Parent, N, M) -> receive_first() -> receive - Any -> Any + Any -> Any end. %% Ensures that there is no message in the message queue. receive_nothing() -> receive - Any -> - ct:fail({unexpected_message, Any}) + Any -> + ct:fail({unexpected_message, Any}) after 200 -> - ok + ok end. @@ -1332,39 +1332,39 @@ receive_nothing() -> process(Dest) -> receive - {send_please, To, What} -> - To ! What, - process(Dest); - {spawn_link_please, ReplyTo, {M, F, A}} -> - Pid = spawn_link(M, F, A), - ReplyTo ! {spawned, self(), Pid}, - process(Dest); - {spawn_link_please, ReplyTo, Node, {M, F, A}} -> - Pid = spawn_link(Node, M, F, A), - ReplyTo ! {spawned, self(), Pid}, - process(Dest); - {link_please, Pid} -> - link(Pid), - process(Dest); - {unlink_please, Pid} -> - unlink(Pid), - process(Dest); - {register_please, Name, Pid} -> - register(Name, Pid), - process(Dest); - {unregister_please, Name} -> - unregister(Name), - process(Dest); - {exit_please, Reason} -> - exit(Reason); - {trap_exit_please, State} -> - process_flag(trap_exit, State), - process(Dest); - Other -> - Dest ! {self(), Other}, - process(Dest) + {send_please, To, What} -> + To ! What, + process(Dest); + {spawn_link_please, ReplyTo, {M, F, A}} -> + Pid = spawn_link(M, F, A), + ReplyTo ! {spawned, self(), Pid}, + process(Dest); + {spawn_link_please, ReplyTo, Node, {M, F, A}} -> + Pid = spawn_link(Node, M, F, A), + ReplyTo ! {spawned, self(), Pid}, + process(Dest); + {link_please, Pid} -> + link(Pid), + process(Dest); + {unlink_please, Pid} -> + unlink(Pid), + process(Dest); + {register_please, Name, Pid} -> + register(Name, Pid), + process(Dest); + {unregister_please, Name} -> + unregister(Name), + process(Dest); + {exit_please, Reason} -> + exit(Reason); + {trap_exit_please, State} -> + process_flag(trap_exit, State), + process(Dest); + Other -> + Dest ! {self(), Other}, + process(Dest) after 3000 -> - exit(timeout) + exit(timeout) end. @@ -1372,17 +1372,17 @@ process(Dest) -> process() -> receive - {spawn_please, ReplyTo, Fun} -> - Pid = fun_spawn(Fun), - ReplyTo ! {spawned, Pid}, - process(); - {send_please, To, What} -> - To ! What, - process(); - timeout_please -> - receive after 1 -> process() end; - _Other -> - process() + {spawn_please, ReplyTo, Fun} -> + Pid = fun_spawn(Fun), + ReplyTo ! {spawned, Pid}, + process(); + {send_please, To, What} -> + To ! What, + process(); + timeout_please -> + receive after 1 -> process() end; + _Other -> + process() end. @@ -1390,9 +1390,9 @@ process() -> sender() -> receive - {send_please, To, What} -> - To ! What, - sender() + {send_please, To, What} -> + To ! What, + sender() end. @@ -1400,7 +1400,7 @@ sender() -> receiver() -> receive - _Any -> receiver() + _Any -> receiver() end. %% Works as long as it receives CPU time. Will always be RUNNABLE. @@ -1422,7 +1422,7 @@ start_node(Name) -> Pa = filename:dirname(code:which(?MODULE)), Cookie = atom_to_list(erlang:get_cookie()), test_server:start_node(Name, slave, - [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]). + [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]). stop_node(Node) -> test_server:stop_node(Node). @@ -1430,11 +1430,11 @@ stop_node(Node) -> wait_for_empty_runq(DeadLine) -> case statistics(run_queue) of - 0 -> true; - RQLen -> - erlang:display("Waiting for empty run queue"), - MSDL = DeadLine*1000, - wait_for_empty_runq(MSDL, MSDL, RQLen) + 0 -> true; + RQLen -> + erlang:display("Waiting for empty run queue"), + MSDL = DeadLine*1000, + wait_for_empty_runq(MSDL, MSDL, RQLen) end. wait_for_empty_runq(DeadLine, Left, RQLen) when Left =< 0 -> @@ -1445,48 +1445,48 @@ wait_for_empty_runq(DeadLine, Left, _RQLen) -> UntilDeadLine = Left - Wait, receive after Wait -> ok end, case statistics(run_queue) of - 0 -> - erlang:display("Waited for " - ++ integer_to_list(DeadLine - - UntilDeadLine) - ++ " ms for empty run queue."), - true; - NewRQLen -> - wait_for_empty_runq(DeadLine, - UntilDeadLine, - NewRQLen) + 0 -> + erlang:display("Waited for " + ++ integer_to_list(DeadLine + - UntilDeadLine) + ++ " ms for empty run queue."), + true; + NewRQLen -> + wait_for_empty_runq(DeadLine, + UntilDeadLine, + NewRQLen) end. issue_non_empty_runq_warning(DeadLine, RQLen) -> PIs = lists:foldl( - fun (P, Acc) -> - case process_info(P, - [status, - initial_call, - current_function, - registered_name, - reductions, - message_queue_len]) of - [{status, Runnable} | _] = PI when Runnable /= waiting, - Runnable /= suspended -> - [[{pid, P} | PI] | Acc]; - _ -> - Acc - end - end, - [], - processes()), + fun (P, Acc) -> + case process_info(P, + [status, + initial_call, + current_function, + registered_name, + reductions, + message_queue_len]) of + [{status, Runnable} | _] = PI when Runnable /= waiting, + Runnable /= suspended -> + [[{pid, P} | PI] | Acc]; + _ -> + Acc + end + end, + [], + processes()), io:format("WARNING: Unexpected runnable processes in system (waited ~p sec).~n" - " Run queue length: ~p~n" - " Self: ~p~n" - " Processes info: ~p~n", - [DeadLine div 1000, RQLen, self(), PIs]), + " Run queue length: ~p~n" + " Self: ~p~n" + " Processes info: ~p~n", + [DeadLine div 1000, RQLen, self(), PIs]), receive after 1000 -> ok end. load_driver(Dir, Driver) -> case erl_ddll:load_driver(Dir, Driver) of - ok -> ok; - {error, Error} = Res -> - io:format("~s\n", [erl_ddll:format_error(Error)]), - Res + ok -> ok; + {error, Error} = Res -> + io:format("~s\n", [erl_ddll:format_error(Error)]), + Res end. diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index 7995c1831a..b774210456 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -22,8 +22,7 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2]). +-export([all/0, suite/0]). -export([trace_bif/1, trace_bif_timestamp/1, trace_on_and_off/1, trace_bif_local/1, trace_bif_timestamp_local/1, trace_bif_return/1, not_run/1, @@ -42,40 +41,25 @@ all() -> trace_bif_return, trace_info_old_code] end. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - not_run(Config) when is_list(Config) -> {skipped,"Native code"}. %% Tests switching tracing on and off. trace_on_and_off(Config) when is_list(Config) -> - ?line Pid = spawn(?MODULE, bif_process, []), - ?line Self = self(), - ?line 1 = erlang:trace(Pid, true, [call,timestamp]), - ?line {flags, Flags} = erlang:trace_info(Pid,flags), - ?line [call,timestamp] = lists:sort(Flags), - ?line {tracer, Self} = erlang:trace_info(Pid,tracer), - ?line 1 = erlang:trace(Pid, false, [timestamp]), - ?line {flags,[call]} = erlang:trace_info(Pid,flags), - ?line {tracer, Self} = erlang:trace_info(Pid,tracer), - ?line 1 = erlang:trace(Pid, false, [call]), - ?line {flags,[]} = erlang:trace_info(Pid,flags), - ?line {tracer, []} = erlang:trace_info(Pid,tracer), - ?line exit(Pid,kill), + Pid = spawn(?MODULE, bif_process, []), + Self = self(), + 1 = erlang:trace(Pid, true, [call,timestamp]), + {flags, Flags} = erlang:trace_info(Pid,flags), + [call,timestamp] = lists:sort(Flags), + {tracer, Self} = erlang:trace_info(Pid,tracer), + 1 = erlang:trace(Pid, false, [timestamp]), + {flags,[call]} = erlang:trace_info(Pid,flags), + {tracer, Self} = erlang:trace_info(Pid,tracer), + 1 = erlang:trace(Pid, false, [call]), + {flags,[]} = erlang:trace_info(Pid,flags), + {tracer, []} = erlang:trace_info(Pid,tracer), + exit(Pid,kill), ok. %% Test tracing BIFs. @@ -87,52 +71,52 @@ trace_bif_local(Config) when is_list(Config) -> do_trace_bif([local]). do_trace_bif(Flags) -> - ?line Pid = spawn(?MODULE, bif_process, []), - ?line 1 = erlang:trace(Pid, true, [call]), - ?line erlang:trace_pattern({erlang,'_','_'}, [], Flags), - ?line Pid ! {do_bif, time, []}, - ?line receive_trace_msg({trace,Pid,call,{erlang,time, []}}), - ?line Pid ! {do_bif, statistics, [runtime]}, - ?line receive_trace_msg({trace,Pid,call, - {erlang,statistics, [runtime]}}), - - ?line Pid ! {do_time_bif}, - ?line receive_trace_msg({trace,Pid,call, - {erlang,time, []}}), - - ?line Pid ! {do_statistics_bif}, - ?line receive_trace_msg({trace,Pid,call, - {erlang,statistics, [runtime]}}), - - ?line 1 = erlang:trace(Pid, false, [call]), - ?line erlang:trace_pattern({erlang,'_','_'}, false, Flags), - ?line exit(Pid, die), + Pid = spawn(?MODULE, bif_process, []), + 1 = erlang:trace(Pid, true, [call]), + erlang:trace_pattern({erlang,'_','_'}, [], Flags), + Pid ! {do_bif, time, []}, + receive_trace_msg({trace,Pid,call,{erlang,time, []}}), + Pid ! {do_bif, statistics, [runtime]}, + receive_trace_msg({trace,Pid,call, + {erlang,statistics, [runtime]}}), + + Pid ! {do_time_bif}, + receive_trace_msg({trace,Pid,call, + {erlang,time, []}}), + + Pid ! {do_statistics_bif}, + receive_trace_msg({trace,Pid,call, + {erlang,statistics, [runtime]}}), + + 1 = erlang:trace(Pid, false, [call]), + erlang:trace_pattern({erlang,'_','_'}, false, Flags), + exit(Pid, die), ok. %% Test tracing BIFs with timestamps. trace_bif_timestamp(Config) when is_list(Config) -> do_trace_bif_timestamp([], timestamp, [timestamp]), do_trace_bif_timestamp([], timestamp, - [timestamp, - monotonic_timestamp, - strict_monotonic_timestamp]), + [timestamp, + monotonic_timestamp, + strict_monotonic_timestamp]), do_trace_bif_timestamp([], strict_monotonic_timestamp, - [strict_monotonic_timestamp]), + [strict_monotonic_timestamp]), do_trace_bif_timestamp([], strict_monotonic_timestamp, - [monotonic_timestamp, strict_monotonic_timestamp]), + [monotonic_timestamp, strict_monotonic_timestamp]), do_trace_bif_timestamp([], monotonic_timestamp, [monotonic_timestamp]). - + %% Test tracing BIFs with timestamps and local flag. trace_bif_timestamp_local(Config) when is_list(Config) -> do_trace_bif_timestamp([local], timestamp, [timestamp]), do_trace_bif_timestamp([local], timestamp, - [timestamp, - monotonic_timestamp, - strict_monotonic_timestamp]), + [timestamp, + monotonic_timestamp, + strict_monotonic_timestamp]), do_trace_bif_timestamp([local], strict_monotonic_timestamp, - [strict_monotonic_timestamp]), + [strict_monotonic_timestamp]), do_trace_bif_timestamp([local], strict_monotonic_timestamp, - [monotonic_timestamp, strict_monotonic_timestamp]), + [monotonic_timestamp, strict_monotonic_timestamp]), do_trace_bif_timestamp([local], monotonic_timestamp, [monotonic_timestamp]). do_trace_bif_timestamp(Flags, TsType, TsFlags) -> @@ -144,22 +128,22 @@ do_trace_bif_timestamp(Flags, TsType, TsFlags) -> Ts0 = make_ts(TsType), Pid ! {do_bif, time, []}, Ts1 = receive_trace_msg_ts({trace_ts,Pid,call,{erlang,time,[]}}, - Ts0,TsType), + Ts0,TsType), Pid ! {do_bif, statistics, [runtime]}, Ts2 = receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,statistics, [runtime]}}, - Ts1, TsType), + {erlang,statistics, [runtime]}}, + Ts1, TsType), Pid ! {do_time_bif}, Ts3 = receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,time, []}}, - Ts2, TsType), + {erlang,time, []}}, + Ts2, TsType), Pid ! {do_statistics_bif}, Ts4 = receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,statistics, [runtime]}}, - Ts3, TsType), + {erlang,statistics, [runtime]}}, + Ts3, TsType), check_ts(TsType, Ts4, make_ts(TsType)), @@ -168,11 +152,11 @@ do_trace_bif_timestamp(Flags, TsType, TsFlags) -> Pid ! {do_statistics_bif}, receive_trace_msg({trace,Pid,call, - {erlang,statistics, [runtime]}}), + {erlang,statistics, [runtime]}}), Pid ! {do_bif, statistics, [runtime]}, receive_trace_msg({trace,Pid,call, - {erlang,statistics, [runtime]}}), + {erlang,statistics, [runtime]}}), 1 = erlang:trace(Pid, false, [call]), erlang:trace_pattern({erlang,'_','_'}, false, Flags), @@ -184,13 +168,13 @@ do_trace_bif_timestamp(Flags, TsType, TsFlags) -> trace_bif_return(Config) when is_list(Config) -> do_trace_bif_return(timestamp, [timestamp]), do_trace_bif_return(timestamp, - [timestamp, - monotonic_timestamp, - strict_monotonic_timestamp]), + [timestamp, + monotonic_timestamp, + strict_monotonic_timestamp]), do_trace_bif_return(strict_monotonic_timestamp, - [strict_monotonic_timestamp]), + [strict_monotonic_timestamp]), do_trace_bif_return(strict_monotonic_timestamp, - [monotonic_timestamp, strict_monotonic_timestamp]), + [monotonic_timestamp, strict_monotonic_timestamp]), do_trace_bif_return(monotonic_timestamp, [monotonic_timestamp]). do_trace_bif_return(TsType, TsFlags) -> @@ -198,59 +182,59 @@ do_trace_bif_return(TsType, TsFlags) -> Pid=spawn(?MODULE, bif_process, []), 1 = erlang:trace(Pid, true, [call,return_to]++TsFlags), erlang:trace_pattern({erlang,'_','_'}, [{'_',[],[{return_trace}]}], - [local]), + [local]), Ts0 = make_ts(TsType), Pid ! {do_bif, time, []}, Ts1 = receive_trace_msg_ts({trace_ts,Pid,call,{erlang,time,[]}}, - Ts0, TsType), + Ts0, TsType), Ts2 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, - {erlang,time,0}}, - Ts1, TsType), + {erlang,time,0}}, + Ts1, TsType), Ts3 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, - {?MODULE, bif_process,0}}, - Ts2, TsType), - - + {?MODULE, bif_process,0}}, + Ts2, TsType), + + Pid ! {do_bif, statistics, [runtime]}, Ts4 = receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,statistics, [runtime]}}, - Ts3, TsType), + {erlang,statistics, [runtime]}}, + Ts3, TsType), Ts5 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, - {erlang,statistics,1}}, - Ts4, TsType), + {erlang,statistics,1}}, + Ts4, TsType), Ts6 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, - {?MODULE, bif_process,0}}, - Ts5, TsType), - - + {?MODULE, bif_process,0}}, + Ts5, TsType), + + Pid ! {do_time_bif}, Ts7 = receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,time, []}}, - Ts6, TsType), + {erlang,time, []}}, + Ts6, TsType), Ts8 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, - {erlang,time,0}}, - Ts7, TsType), + {erlang,time,0}}, + Ts7, TsType), Ts9 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, - {?MODULE, bif_process,0}}, - Ts8, TsType), + {?MODULE, bif_process,0}}, + Ts8, TsType), Pid ! {do_statistics_bif}, Ts10 = receive_trace_msg_ts({trace_ts,Pid,call, - {erlang,statistics, [runtime]}}, - Ts9, TsType), + {erlang,statistics, [runtime]}}, + Ts9, TsType), Ts11 = receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, - {erlang,statistics,1}}, - Ts10, TsType), + {erlang,statistics,1}}, + Ts10, TsType), Ts12 = receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, - {?MODULE, bif_process,0}}, - Ts11, TsType), + {?MODULE, bif_process,0}}, + Ts11, TsType), check_ts(TsType, Ts12, make_ts(TsType)), ok. - - + + receive_trace_msg(Mess) -> receive Mess -> @@ -327,37 +311,37 @@ check_ts(strict_monotonic_timestamp, PrevTs, Ts) -> bif_process() -> receive - {do_bif, Name, Args} -> - apply(erlang, Name, Args), - bif_process(); - {do_time_bif} -> - %% Match the return value to ensure that the time() call - %% is not optimized away. - {_,_,_} = time(), - bif_process(); - {do_statistics_bif} -> - statistics(runtime), - bif_process(); - _Stuff -> - bif_process() + {do_bif, Name, Args} -> + apply(erlang, Name, Args), + bif_process(); + {do_time_bif} -> + %% Match the return value to ensure that the time() call + %% is not optimized away. + {_,_,_} = time(), + bif_process(); + {do_statistics_bif} -> + statistics(runtime), + bif_process(); + _Stuff -> + bif_process() end. - + %% trace_info on deleted module (OTP-5057). trace_info_old_code(Config) when is_list(Config) -> - ?line MFA = {M,F,0} = {test,foo,0}, - ?line Fname = atom_to_list(M)++".erl", - ?line AbsForms = - [{attribute,a(1),module,M}, % -module(M). - {attribute,a(2),export,[{F,0}]}, % -export([F/0]). - {function,a(3),F,0, % F() -> - [{clause,a(4),[],[],[{atom,a(4),F}]}]}], % F. + MFA = {M,F,0} = {test,foo,0}, + Fname = atom_to_list(M)++".erl", + AbsForms = + [{attribute,a(1),module,M}, % -module(M). + {attribute,a(2),export,[{F,0}]}, % -export([F/0]). + {function,a(3),F,0, % F() -> + [{clause,a(4),[],[],[{atom,a(4),F}]}]}], % F. %% - ?line {ok,M,Mbin} = compile:forms(AbsForms), - ?line {module,M} = code:load_binary(M, Fname, Mbin), - ?line true = erlang:delete_module(M), - ?line {traced,undefined} = erlang:trace_info(MFA, traced), + {ok,M,Mbin} = compile:forms(AbsForms), + {module,M} = code:load_binary(M, Fname, Mbin), + true = erlang:delete_module(M), + {traced,undefined} = erlang:trace_info(MFA, traced), ok. a(L) -> diff --git a/erts/emulator/test/trace_call_count_SUITE.erl b/erts/emulator/test/trace_call_count_SUITE.erl index 3e7c1020a5..c849668e84 100644 --- a/erts/emulator/test/trace_call_count_SUITE.erl +++ b/erts/emulator/test/trace_call_count_SUITE.erl @@ -145,168 +145,168 @@ combo(Config) when is_list(Config) -> %%% basic_test() -> - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), - ?line M = 1000, + P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), + M = 1000, %% - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]), - ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_count]), - ?line L = seq(1, M, fun(X) -> X+1 end), - ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line {call_count,0} = erlang:trace_info({?MODULE,seq_r,3}, call_count), - ?line Lr = seq_r(1, M, fun(X) -> X+1 end), - ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line {call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count), - ?line {call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count), - ?line L = lists:reverse(Lr), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]), + 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_count]), + L = seq(1, M, fun(X) -> X+1 end), + {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), + {call_count,0} = erlang:trace_info({?MODULE,seq_r,3}, call_count), + Lr = seq_r(1, M, fun(X) -> X+1 end), + {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), + {call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count), + {call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count), + L = lists:reverse(Lr), %% - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), + P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% on_and_off_test() -> - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), - ?line M = 100, + P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), + M = 100, %% - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]), - ?line L = seq(1, M, fun(X) -> X+1 end), - ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line N = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_count]), - ?line L = seq(1, M, fun(X) -> X+1 end), - ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line P = erlang:trace_pattern({'_','_','_'}, true, [call_count]), - ?line L = seq(1, M, fun(X) -> X+1 end), - ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]), - ?line {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line L = seq(1, M, fun(X) -> X+1 end), - ?line {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line {call_count,0} = erlang:trace_info({?MODULE,seq_r,4}, call_count), - ?line Lr = seq_r(1, M, fun(X) -> X+1 end), - ?line {call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count), - ?line N = erlang:trace_pattern({?MODULE,'_','_'}, false, [call_count]), - ?line {call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count), - ?line Lr = seq_r(1, M, fun(X) -> X+1 end), - ?line {call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count), - ?line L = lists:reverse(Lr), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]), + L = seq(1, M, fun(X) -> X+1 end), + {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), + N = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_count]), + L = seq(1, M, fun(X) -> X+1 end), + {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), + P = erlang:trace_pattern({'_','_','_'}, true, [call_count]), + L = seq(1, M, fun(X) -> X+1 end), + {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]), + {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count), + L = seq(1, M, fun(X) -> X+1 end), + {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count), + {call_count,0} = erlang:trace_info({?MODULE,seq_r,4}, call_count), + Lr = seq_r(1, M, fun(X) -> X+1 end), + {call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count), + N = erlang:trace_pattern({?MODULE,'_','_'}, false, [call_count]), + {call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count), + Lr = seq_r(1, M, fun(X) -> X+1 end), + {call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count), + L = lists:reverse(Lr), %% - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), + P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% info_test() -> - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), + P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), %% - ?line 1 = erlang:trace_pattern({?MODULE,seq,3}, true, [call_count]), - ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]), - ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line {all,[_|_]=L} = erlang:trace_info({?MODULE,seq,3}, all), - ?line {value,{call_count,0}} = lists:keysearch(call_count, 1, L), - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]), - ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]), - ?line {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line {all,false} = erlang:trace_info({?MODULE,seq,3}, all), + 1 = erlang:trace_pattern({?MODULE,seq,3}, true, [call_count]), + {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]), + {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count), + {all,[_|_]=L} = erlang:trace_info({?MODULE,seq,3}, all), + {value,{call_count,0}} = lists:keysearch(call_count, 1, L), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]), + {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]), + {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count), + {all,false} = erlang:trace_info({?MODULE,seq,3}, all), %% - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), + P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% pause_and_restart_test() -> - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), - ?line M = 100, + P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), + M = 100, %% - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]), - ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line L = seq(1, M, fun(X) -> X+1 end), - ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]), - ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line L = seq(1, M, fun(X) -> X+1 end), - ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]), - ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count), - ?line L = seq(1, M, fun(X) -> X+1 end), - ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]), + {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count), + L = seq(1, M, fun(X) -> X+1 end), + {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]), + {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), + L = seq(1, M, fun(X) -> X+1 end), + {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]), + {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count), + L = seq(1, M, fun(X) -> X+1 end), + {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count), %% - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), + P = erlang:trace_pattern({'_','_','_'}, false, [call_count]), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% combo_test() -> - ?line Self = self(), - - ?line MetaMatchSpec = [{'_',[],[{return_trace}]}], - ?line Flags = lists:sort([call, return_to]), - ?line LocalTracer = spawn_link(fun () -> relay_n(5, Self) end), - ?line MetaTracer = spawn_link(fun () -> relay_n(9, Self) end), - ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, [], [local]), - ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, - MetaMatchSpec, - [{meta,MetaTracer}, call_count]), - ?line 1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]), + Self = self(), + + MetaMatchSpec = [{'_',[],[{return_trace}]}], + Flags = lists:sort([call, return_to]), + LocalTracer = spawn_link(fun () -> relay_n(5, Self) end), + MetaTracer = spawn_link(fun () -> relay_n(9, Self) end), + 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, [], [local]), + 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, + MetaMatchSpec, + [{meta,MetaTracer}, call_count]), + 1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]), %% - ?line {traced,local} = - erlang:trace_info({?MODULE,seq_r,3}, traced), - ?line {match_spec,[]} = - erlang:trace_info({?MODULE,seq_r,3}, match_spec), - ?line {meta,MetaTracer} = - erlang:trace_info({?MODULE,seq_r,3}, meta), - ?line {meta_match_spec,MetaMatchSpec} = - erlang:trace_info({?MODULE,seq_r,3}, meta_match_spec), - ?line {call_count,0} = - erlang:trace_info({?MODULE,seq_r,3}, call_count), + {traced,local} = + erlang:trace_info({?MODULE,seq_r,3}, traced), + {match_spec,[]} = + erlang:trace_info({?MODULE,seq_r,3}, match_spec), + {meta,MetaTracer} = + erlang:trace_info({?MODULE,seq_r,3}, meta), + {meta_match_spec,MetaMatchSpec} = + erlang:trace_info({?MODULE,seq_r,3}, meta_match_spec), + {call_count,0} = + erlang:trace_info({?MODULE,seq_r,3}, call_count), %% - ?line {all,[_|_]=TraceInfo} = - erlang:trace_info({?MODULE,seq_r,3}, all), - ?line {value,{traced,local}} = - lists:keysearch(traced, 1, TraceInfo), - ?line {value,{match_spec,[]}} = - lists:keysearch(match_spec, 1, TraceInfo), - ?line {value,{meta,MetaTracer}} = - lists:keysearch(meta, 1, TraceInfo), - ?line {value,{meta_match_spec,MetaMatchSpec}} = - lists:keysearch(meta_match_spec, 1, TraceInfo), - ?line {value,{call_count,0}} = - lists:keysearch(call_count, 1, TraceInfo), + {all,[_|_]=TraceInfo} = + erlang:trace_info({?MODULE,seq_r,3}, all), + {value,{traced,local}} = + lists:keysearch(traced, 1, TraceInfo), + {value,{match_spec,[]}} = + lists:keysearch(match_spec, 1, TraceInfo), + {value,{meta,MetaTracer}} = + lists:keysearch(meta, 1, TraceInfo), + {value,{meta_match_spec,MetaMatchSpec}} = + lists:keysearch(meta_match_spec, 1, TraceInfo), + {value,{call_count,0}} = + lists:keysearch(call_count, 1, TraceInfo), %% - ?line [3,2,1] = seq_r(1, 3, fun(X) -> X+1 end), + [3,2,1] = seq_r(1, 3, fun(X) -> X+1 end), %% - ?line List = collect(100), - ?line {MetaR, LocalR} = - lists:foldl( - fun ({P,X}, {M,L}) when P == MetaTracer -> - {[X|M],L}; - ({P,X}, {M,L}) when P == LocalTracer -> - {M,[X|L]} - end, - {[],[]}, - List), - ?line Meta = lists:reverse(MetaR), - ?line Local = lists:reverse(LocalR), - ?line [?CTT(Self,{?MODULE,seq_r,[1,3,_]}), - ?CTT(Self,{?MODULE,seq_r,[1,3,_,[]]}), - ?CTT(Self,{?MODULE,seq_r,[2,3,_,[1]]}), - ?CTT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}), - ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), - ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), - ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), - ?RFT(Self,{?MODULE,seq_r,3},[3,2,1])] = Meta, - ?line [?CT(Self,{?MODULE,seq_r,[1,3,_]}), - ?CT(Self,{?MODULE,seq_r,[1,3,_,[]]}), - ?CT(Self,{?MODULE,seq_r,[2,3,_,[1]]}), - ?CT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}), - ?RT(Self,{?MODULE,combo_test,0})] = Local, - ?line {call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count), - ?line {call_count,3} = erlang:trace_info({?MODULE,seq_r,4}, call_count), + List = collect(100), + {MetaR, LocalR} = + lists:foldl( + fun ({P,X}, {M,L}) when P == MetaTracer -> + {[X|M],L}; + ({P,X}, {M,L}) when P == LocalTracer -> + {M,[X|L]} + end, + {[],[]}, + List), + Meta = lists:reverse(MetaR), + Local = lists:reverse(LocalR), + [?CTT(Self,{?MODULE,seq_r,[1,3,_]}), + ?CTT(Self,{?MODULE,seq_r,[1,3,_,[]]}), + ?CTT(Self,{?MODULE,seq_r,[2,3,_,[1]]}), + ?CTT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}), + ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), + ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), + ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), + ?RFT(Self,{?MODULE,seq_r,3},[3,2,1])] = Meta, + [?CT(Self,{?MODULE,seq_r,[1,3,_]}), + ?CT(Self,{?MODULE,seq_r,[1,3,_,[]]}), + ?CT(Self,{?MODULE,seq_r,[2,3,_,[1]]}), + ?CT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}), + ?RT(Self,{?MODULE,combo_test,0})] = Local, + {call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count), + {call_count,3} = erlang:trace_info({?MODULE,seq_r,4}, call_count), %% - ?line erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]), - ?line erlang:trace_pattern(on_load, false, [local,meta,call_count]), - ?line erlang:trace(all, false, [all]), + erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]), + erlang:trace_pattern(on_load, false, [local,meta,call_count]), + erlang:trace(all, false, [all]), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -336,8 +336,8 @@ relay_n(0, _) -> ok; relay_n(N, Dest) -> receive Msg -> - Dest ! {self(), Msg}, - relay_n(N-1, Dest) + Dest ! {self(), Msg}, + relay_n(N-1, Dest) end. @@ -351,15 +351,15 @@ collect(Time) -> collect(A, 0) -> receive - Mess -> - collect([Mess | A], 0) + Mess -> + collect([Mess | A], 0) after 0 -> - A + A end; collect(A, Ref) -> receive - {timeout, Ref, done} -> - collect(A, 0); - Mess -> - collect([Mess | A], Ref) + {timeout, Ref, done} -> + collect(A, 0); + Mess -> + collect([Mess | A], Ref) end. diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 09eda2845c..38972c9c02 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -96,25 +96,25 @@ not_run(Config) when is_list(Config) -> %% Tests basic call time trace basic(Config) when is_list(Config) -> - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - ?line M = 1000, + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + M = 1000, %% - ?line 1 = erlang:trace_pattern({?MODULE,seq, '_'}, true, [call_time]), - ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_time]), - ?line Pid = setup(), - ?line {L, T1} = execute(Pid, fun() -> seq(1, M, fun(X) -> (X+1) end) end), - ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1), - ?line ok = check_trace_info({?MODULE, seq_r, 3}, [], none), - - ?line {Lr, T2} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> (X+1) end) end), - ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1), - ?line ok = check_trace_info({?MODULE, seq_r, 3}, [{Pid, 1, 0, 0}], T2/M), - ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Pid, M, 0, 0}], T2), - ?line L = lists:reverse(Lr), + 1 = erlang:trace_pattern({?MODULE,seq, '_'}, true, [call_time]), + 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_time]), + Pid = setup(), + {L, T1} = execute(Pid, fun() -> seq(1, M, fun(X) -> (X+1) end) end), + ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1), + ok = check_trace_info({?MODULE, seq_r, 3}, [], none), + + {Lr, T2} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> (X+1) end) end), + ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1), + ok = check_trace_info({?MODULE, seq_r, 3}, [{Pid, 1, 0, 0}], T2/M), + ok = check_trace_info({?MODULE, seq_r, 4}, [{Pid, M, 0, 0}], T2), + L = lists:reverse(Lr), %% - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - ?line Pid ! quit, + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + Pid ! quit, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -122,257 +122,257 @@ basic(Config) when is_list(Config) -> %% "Tests turning trace parameters on and off on_and_off(Config) when is_list(Config) -> - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - ?line M = 100, + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + M = 100, %% - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_time]), - ?line Pid = setup(), - ?line {L, T1} = execute(Pid, {?MODULE, seq, [1, M, fun(X) -> X+1 end]}), - ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1), - - ?line N = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_time]), - ?line {L, T2} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), - ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T2), - - ?line P = erlang:trace_pattern({'_','_','_'}, true, [call_time]), - ?line {L, T3} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), - ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T3), - - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_time]), - ?line ok = check_trace_info({?MODULE, seq, 3}, false, none), - ?line {L, _T4} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), - ?line ok = check_trace_info({?MODULE, seq, 3}, false, none), - ?line ok = check_trace_info({?MODULE, seq_r, 4}, [], none), - ?line {Lr, T5} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> X+1 end) end), - ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Pid,M,0,0}], T5), - - ?line N = erlang:trace_pattern({?MODULE,'_','_'}, false, [call_time]), - ?line ok = check_trace_info({?MODULE, seq_r, 4}, false, none), - ?line {Lr, _T6} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> X+1 end) end), - ?line ok = check_trace_info({?MODULE, seq_r, 4}, false, none), - ?line L = lists:reverse(Lr), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_time]), + Pid = setup(), + {L, T1} = execute(Pid, {?MODULE, seq, [1, M, fun(X) -> X+1 end]}), + ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1), + + N = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_time]), + {L, T2} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), + ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T2), + + P = erlang:trace_pattern({'_','_','_'}, true, [call_time]), + {L, T3} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), + ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T3), + + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_time]), + ok = check_trace_info({?MODULE, seq, 3}, false, none), + {L, _T4} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), + ok = check_trace_info({?MODULE, seq, 3}, false, none), + ok = check_trace_info({?MODULE, seq_r, 4}, [], none), + {Lr, T5} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> X+1 end) end), + ok = check_trace_info({?MODULE, seq_r, 4}, [{Pid,M,0,0}], T5), + + N = erlang:trace_pattern({?MODULE,'_','_'}, false, [call_time]), + ok = check_trace_info({?MODULE, seq_r, 4}, false, none), + {Lr, _T6} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> X+1 end) end), + ok = check_trace_info({?MODULE, seq_r, 4}, false, none), + L = lists:reverse(Lr), %% - ?line Pid ! quit, - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + Pid ! quit, + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Tests the trace_info BIF info(Config) when is_list(Config) -> - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), %% - ?line 1 = erlang:trace_pattern({?MODULE,seq,3}, true, [call_time]), - ?line {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time), - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_time]), - ?line {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time), - ?line {all,[_|_]=L} = erlang:trace_info({?MODULE,seq,3}, all), - ?line {value,{call_time,[]}} = lists:keysearch(call_time, 1, L), - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_time]), - ?line {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time), - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_time]), - ?line {call_time,false} = erlang:trace_info({?MODULE,seq,3}, call_time), - ?line {all,false} = erlang:trace_info({?MODULE,seq,3}, all), + 1 = erlang:trace_pattern({?MODULE,seq,3}, true, [call_time]), + {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_time]), + {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time), + {all,[_|_]=L} = erlang:trace_info({?MODULE,seq,3}, all), + {value,{call_time,[]}} = lists:keysearch(call_time, 1, L), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_time]), + {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_time]), + {call_time,false} = erlang:trace_info({?MODULE,seq,3}, call_time), + {all,false} = erlang:trace_info({?MODULE,seq,3}, all), %% - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Tests pausing and restarting call time counters pause_and_restart(Config) when is_list(Config) -> - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - ?line M = 100, - ?line Pid = setup(), + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + M = 100, + Pid = setup(), %% - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_time]), - ?line ok = check_trace_info({?MODULE, seq, 3}, [], none), - ?line {L, T1} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), - ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T1), - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_time]), - ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T1), - ?line {L, T2} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), - ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T2), - ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_time]), - ?line ok = check_trace_info({?MODULE, seq, 3}, [], none), - ?line {L, T3} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), - ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T3), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_time]), + ok = check_trace_info({?MODULE, seq, 3}, [], none), + {L, T1} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), + ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T1), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_time]), + ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T1), + {L, T2} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), + ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T2), + 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_time]), + ok = check_trace_info({?MODULE, seq, 3}, [], none), + {L, T3} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), + ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T3), %% - ?line Pid ! quit, - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + Pid ! quit, + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Tests in/out scheduling of call time counters scheduling(Config) when is_list(Config) -> - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - ?line M = 1000000, - ?line Np = erlang:system_info(schedulers_online), - ?line F = 12, + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + M = 1000000, + Np = erlang:system_info(schedulers_online), + F = 12, %% setup load processes %% (single, no internal calls) - ?line erlang:trace_pattern({?MODULE,loaded,1}, true, [call_time]), + erlang:trace_pattern({?MODULE,loaded,1}, true, [call_time]), - ?line Pids = [setup() || _ <- lists:seq(1, F*Np)], - ?line {_Ls,T1} = execute(Pids, {?MODULE,loaded,[M]}), - ?line [Pid ! quit || Pid <- Pids], + Pids = [setup() || _ <- lists:seq(1, F*Np)], + {_Ls,T1} = execute(Pids, {?MODULE,loaded,[M]}), + [Pid ! quit || Pid <- Pids], %% logic dictates that each process will get ~ 1/F of the schedulers time - ?line {call_time, CT} = erlang:trace_info({?MODULE,loaded,1}, call_time), - - ?line lists:foreach(fun (Pid) -> - ?line ok = case check_process_time(lists:keysearch(Pid, 1, CT), M, F, T1) of - schedule_time_error -> - test_server:comment("Warning: Failed time ratio"), - ok; - Other -> Other - end - end, Pids), - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + {call_time, CT} = erlang:trace_info({?MODULE,loaded,1}, call_time), + + lists:foreach(fun (Pid) -> + ok = case check_process_time(lists:keysearch(Pid, 1, CT), M, F, T1) of + schedule_time_error -> + test_server:comment("Warning: Failed time ratio"), + ok; + Other -> Other + end + end, Pids), + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% "Tests combining local call trace and meta trace with call time trace combo(Config) when is_list(Config) -> - ?line Self = self(), - ?line Nbc = 3, - ?line MetaMs = [{'_',[],[{return_trace}]}], - ?line Flags = lists:sort([call, return_to]), - ?line LocalTracer = spawn_link(fun () -> relay_n(5 + Nbc + 3, Self) end), - ?line MetaTracer = spawn_link(fun () -> relay_n(9 + Nbc + 3, Self) end), - ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, [], [local]), - ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_time]), - ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, MetaMs, [{meta,MetaTracer}]), - ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_count]), + Self = self(), + Nbc = 3, + MetaMs = [{'_',[],[{return_trace}]}], + Flags = lists:sort([call, return_to]), + LocalTracer = spawn_link(fun () -> relay_n(5 + Nbc + 3, Self) end), + MetaTracer = spawn_link(fun () -> relay_n(9 + Nbc + 3, Self) end), + 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, [], [local]), + 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_time]), + 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, MetaMs, [{meta,MetaTracer}]), + 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_count]), % bifs - ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, [], [local]), - ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]), - ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, MetaMs, [{meta,MetaTracer}]), + 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, [], [local]), + 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]), + 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, MetaMs, [{meta,MetaTracer}]), %% not implemented - %?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]), + %2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]), - ?line 1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]), + 1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]), %% - ?line {traced,local} = - erlang:trace_info({?MODULE,seq_r,3}, traced), - ?line {match_spec,[]} = - erlang:trace_info({?MODULE,seq_r,3}, match_spec), - ?line {meta,MetaTracer} = - erlang:trace_info({?MODULE,seq_r,3}, meta), - ?line {meta_match_spec,MetaMs} = - erlang:trace_info({?MODULE,seq_r,3}, meta_match_spec), - ?line ok = check_trace_info({?MODULE, seq_r, 3}, [], none), + {traced,local} = + erlang:trace_info({?MODULE,seq_r,3}, traced), + {match_spec,[]} = + erlang:trace_info({?MODULE,seq_r,3}, match_spec), + {meta,MetaTracer} = + erlang:trace_info({?MODULE,seq_r,3}, meta), + {meta_match_spec,MetaMs} = + erlang:trace_info({?MODULE,seq_r,3}, meta_match_spec), + ok = check_trace_info({?MODULE, seq_r, 3}, [], none), %% check empty trace_info for ?MODULE:seq_r/3 - ?line {all,[_|_]=TraceInfo} = erlang:trace_info({?MODULE,seq_r,3}, all), - ?line {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfo), - ?line {value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfo), - ?line {value,{meta,MetaTracer}} = lists:keysearch(meta, 1, TraceInfo), - ?line {value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfo), - ?line {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfo), - ?line {value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfo), + {all,[_|_]=TraceInfo} = erlang:trace_info({?MODULE,seq_r,3}, all), + {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfo), + {value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfo), + {value,{meta,MetaTracer}} = lists:keysearch(meta, 1, TraceInfo), + {value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfo), + {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfo), + {value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfo), %% check empty trace_info for erlang:term_to_binary/1 - ?line {all, [_|_] = TraceInfoBif} = erlang:trace_info({erlang, term_to_binary, 1}, all), - ?line {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfoBif), - ?line {value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfoBif), - ?line {value,{meta, MetaTracer}} = lists:keysearch(meta, 1, TraceInfoBif), - ?line {value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfoBif), + {all, [_|_] = TraceInfoBif} = erlang:trace_info({erlang, term_to_binary, 1}, all), + {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfoBif), + {value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfoBif), + {value,{meta, MetaTracer}} = lists:keysearch(meta, 1, TraceInfoBif), + {value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfoBif), %% not implemented - ?line {value,{call_count,false}} = lists:keysearch(call_count, 1, TraceInfoBif), - %?line {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif), - ?line {value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfoBif), + {value,{call_count,false}} = lists:keysearch(call_count, 1, TraceInfoBif), + %{value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif), + {value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfoBif), %% - ?line [3,2,1] = seq_r(1, 3, fun(X) -> X+1 end), - ?line T0 = erlang:monotonic_time(), - ?line with_bif(Nbc), - ?line T1 = erlang:monotonic_time(), - ?line TimeB = erlang:convert_time_unit(T1-T0, native, micro_seconds), + [3,2,1] = seq_r(1, 3, fun(X) -> X+1 end), + T0 = erlang:monotonic_time(), + with_bif(Nbc), + T1 = erlang:monotonic_time(), + TimeB = erlang:convert_time_unit(T1-T0, native, micro_seconds), %% - ?line List = collect(100), - ?line {MetaR, LocalR} = - lists:foldl( - fun ({P,X}, {M,L}) when P == MetaTracer -> - {[X|M],L}; - ({P,X}, {M,L}) when P == LocalTracer -> - {M,[X|L]} - end, - {[],[]}, - List), - ?line Meta = lists:reverse(MetaR), - ?line Local = lists:reverse(LocalR), - - ?line [?CTT(Self,{?MODULE,seq_r,[1,3,_]}), - ?CTT(Self,{?MODULE,seq_r,[1,3,_,[]]}), - ?CTT(Self,{?MODULE,seq_r,[2,3,_,[1]]}), - ?CTT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}), - ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), - ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), - ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), - ?RFT(Self,{?MODULE,seq_r,3},[3,2,1]), - ?CTT(Self,{erlang,term_to_binary,[3]}), % bif - ?RFT(Self,{erlang,term_to_binary,1},<<131,97,3>>), - ?CTT(Self,{erlang,term_to_binary,[2]}), - ?RFT(Self,{erlang,term_to_binary,1},<<131,97,2>>) - ] = Meta, - - ?line [?CT(Self,{?MODULE,seq_r,[1,3,_]}), - ?CT(Self,{?MODULE,seq_r,[1,3,_,[]]}), - ?CT(Self,{?MODULE,seq_r,[2,3,_,[1]]}), - ?CT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}), - ?RT(Self,{?MODULE,combo,1}), - ?CT(Self,{erlang,term_to_binary,[3]}), % bif - ?RT(Self,{?MODULE,with_bif,1}), - ?CT(Self,{erlang,term_to_binary,[2]}), - ?RT(Self,{?MODULE,with_bif,1}) - ] = Local, - - ?line ok = check_trace_info({?MODULE, seq_r, 3}, [{Self,1,0,0}], 1), - ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Self,3,0,0}], 1), - ?line ok = check_trace_info({?MODULE, seq_r, 3}, [{Self,1,0,0}], 1), - ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Self,3,0,0}], 1), - ?line ok = check_trace_info({erlang, term_to_binary, 1}, [{self(), Nbc - 1, 0, 0}], TimeB), + List = collect(100), + {MetaR, LocalR} = + lists:foldl( + fun ({P,X}, {M,L}) when P == MetaTracer -> + {[X|M],L}; + ({P,X}, {M,L}) when P == LocalTracer -> + {M,[X|L]} + end, + {[],[]}, + List), + Meta = lists:reverse(MetaR), + Local = lists:reverse(LocalR), + + [?CTT(Self,{?MODULE,seq_r,[1,3,_]}), + ?CTT(Self,{?MODULE,seq_r,[1,3,_,[]]}), + ?CTT(Self,{?MODULE,seq_r,[2,3,_,[1]]}), + ?CTT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}), + ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), + ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), + ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), + ?RFT(Self,{?MODULE,seq_r,3},[3,2,1]), + ?CTT(Self,{erlang,term_to_binary,[3]}), % bif + ?RFT(Self,{erlang,term_to_binary,1},<<131,97,3>>), + ?CTT(Self,{erlang,term_to_binary,[2]}), + ?RFT(Self,{erlang,term_to_binary,1},<<131,97,2>>) + ] = Meta, + + [?CT(Self,{?MODULE,seq_r,[1,3,_]}), + ?CT(Self,{?MODULE,seq_r,[1,3,_,[]]}), + ?CT(Self,{?MODULE,seq_r,[2,3,_,[1]]}), + ?CT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}), + ?RT(Self,{?MODULE,combo,1}), + ?CT(Self,{erlang,term_to_binary,[3]}), % bif + ?RT(Self,{?MODULE,with_bif,1}), + ?CT(Self,{erlang,term_to_binary,[2]}), + ?RT(Self,{?MODULE,with_bif,1}) + ] = Local, + + ok = check_trace_info({?MODULE, seq_r, 3}, [{Self,1,0,0}], 1), + ok = check_trace_info({?MODULE, seq_r, 4}, [{Self,3,0,0}], 1), + ok = check_trace_info({?MODULE, seq_r, 3}, [{Self,1,0,0}], 1), + ok = check_trace_info({?MODULE, seq_r, 4}, [{Self,3,0,0}], 1), + ok = check_trace_info({erlang, term_to_binary, 1}, [{self(), Nbc - 1, 0, 0}], TimeB), %% - ?line erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_time]), - ?line erlang:trace_pattern(on_load, false, [local,meta,call_time]), - ?line erlang:trace(all, false, [all]), + erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_time]), + erlang:trace_pattern(on_load, false, [local,meta,call_time]), + erlang:trace(all, false, [all]), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Tests tracing of bifs bif(Config) when is_list(Config) -> - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - ?line M = 1000000, + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + M = 1000000, %% - ?line 2 = erlang:trace_pattern({erlang, binary_to_term, '_'}, true, [call_time]), - ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]), - ?line Pid = setup(), - ?line {L, T1} = execute(Pid, fun() -> with_bif(M) end), + 2 = erlang:trace_pattern({erlang, binary_to_term, '_'}, true, [call_time]), + 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]), + Pid = setup(), + {L, T1} = execute(Pid, fun() -> with_bif(M) end), - ?line ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M - 1, 0, 0}], T1/2), - ?line ok = check_trace_info({erlang, term_to_binary, 1}, [{Pid, M - 1, 0, 0}], T1/2), + ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M - 1, 0, 0}], T1/2), + ok = check_trace_info({erlang, term_to_binary, 1}, [{Pid, M - 1, 0, 0}], T1/2), % disable term2binary - ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, false, [call_time]), + 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, false, [call_time]), - ?line {L, T2} = execute(Pid, fun() -> with_bif(M) end), + {L, T2} = execute(Pid, fun() -> with_bif(M) end), - ?line ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M*2 - 2, 0, 0}], T1/2 + T2), - ?line ok = check_trace_info({erlang, term_to_binary, 1}, false, none), + ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M*2 - 2, 0, 0}], T1/2 + T2), + ok = check_trace_info({erlang, term_to_binary, 1}, false, none), %% - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - ?line Pid ! quit, + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + Pid ! quit, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -380,50 +380,50 @@ bif(Config) when is_list(Config) -> %% Tests tracing of nifs nif(Config) when is_list(Config) -> load_nif(Config), - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - ?line M = 1000000, + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + M = 1000000, %% - ?line 1 = erlang:trace_pattern({?MODULE, nif_dec, '_'}, true, [call_time]), - ?line 1 = erlang:trace_pattern({?MODULE, with_nif, '_'}, true, [call_time]), - ?line Pid = setup(), - ?line {_, T1} = execute(Pid, fun() -> with_nif(M) end), + 1 = erlang:trace_pattern({?MODULE, nif_dec, '_'}, true, [call_time]), + 1 = erlang:trace_pattern({?MODULE, with_nif, '_'}, true, [call_time]), + Pid = setup(), + {_, T1} = execute(Pid, fun() -> with_nif(M) end), % the nif is called M - 1 times, the last time the function with 'with_nif' % returns ok and does not call the nif. - ?line ok = check_trace_info({?MODULE, nif_dec, 1}, [{Pid, M-1, 0, 0}], T1/5*4), - ?line ok = check_trace_info({?MODULE, with_nif, 1}, [{Pid, M, 0, 0}], T1/5), + ok = check_trace_info({?MODULE, nif_dec, 1}, [{Pid, M-1, 0, 0}], T1/5*4), + ok = check_trace_info({?MODULE, with_nif, 1}, [{Pid, M, 0, 0}], T1/5), %% - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - ?line Pid ! quit, + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + Pid ! quit, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Tests combining nested function calls and that the time accumulates to the right function called_function(Config) when is_list(Config) -> - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - ?line M = 2100, - ?line Pid = setup(), + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + M = 2100, + Pid = setup(), %% - ?line 1 = erlang:trace_pattern({?MODULE,a_function,'_'}, true, [call_time]), - ?line {L, T1} = execute(Pid, {?MODULE, a_function, [M]}), - ?line ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M, 0, 0}], T1), + 1 = erlang:trace_pattern({?MODULE,a_function,'_'}, true, [call_time]), + {L, T1} = execute(Pid, {?MODULE, a_function, [M]}), + ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M, 0, 0}], T1), - ?line 1 = erlang:trace_pattern({?MODULE,a_called_function,'_'}, true, [call_time]), - ?line {L, T2} = execute(Pid, {?MODULE, a_function, [M]}), - ?line ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M+M, 0, 0}], T1 + M*?SINGLE_CALL_US_TIME), - ?line ok = check_trace_info({?MODULE, a_called_function, 1}, [{Pid, M, 0, 0}], T2), + 1 = erlang:trace_pattern({?MODULE,a_called_function,'_'}, true, [call_time]), + {L, T2} = execute(Pid, {?MODULE, a_function, [M]}), + ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M+M, 0, 0}], T1 + M*?SINGLE_CALL_US_TIME), + ok = check_trace_info({?MODULE, a_called_function, 1}, [{Pid, M, 0, 0}], T2), - ?line 1 = erlang:trace_pattern({?MODULE,dec,'_'}, true, [call_time]), - ?line {L, T3} = execute(Pid, {?MODULE, a_function, [M]}), - ?line ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M+M+M, 0, 0}], T1 + (M+M)*?SINGLE_CALL_US_TIME), - ?line ok = check_trace_info({?MODULE, a_called_function, 1}, [{Pid, M+M, 0, 0}], T2 + M*?SINGLE_CALL_US_TIME ), - ?line ok = check_trace_info({?MODULE, dec, 1}, [{Pid, M, 0, 0}], T3), + 1 = erlang:trace_pattern({?MODULE,dec,'_'}, true, [call_time]), + {L, T3} = execute(Pid, {?MODULE, a_function, [M]}), + ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M+M+M, 0, 0}], T1 + (M+M)*?SINGLE_CALL_US_TIME), + ok = check_trace_info({?MODULE, a_called_function, 1}, [{Pid, M+M, 0, 0}], T2 + M*?SINGLE_CALL_US_TIME ), + ok = check_trace_info({?MODULE, dec, 1}, [{Pid, M, 0, 0}], T3), - ?line Pid ! quit, - ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), + Pid ! quit, + P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -437,8 +437,8 @@ dead_tracer(Config) when is_list(Config) -> Ref = erlang:monitor(process, FirstTracer), FirstTracer ! quit, receive - {'DOWN',Ref,process,FirstTracer,normal} -> - ok + {'DOWN',Ref,process,FirstTracer,normal} -> + ok end, erlang:yield(), @@ -468,26 +468,26 @@ dead_tracer(Config) when is_list(Config) -> other_than_self(Info) -> [{Pid,MFA} || {MFA,[{Pid,_,_,_}]} <- Info, - Pid =/= self()]. + Pid =/= self()]. tell_tracer(Tracer, Fun) -> Tracer ! {execute,self(),Fun}, receive - {Tracer,executed} -> - ok + {Tracer,executed} -> + ok end. tracer() -> spawn_link(fun Loop() -> - receive - quit -> - ok; - {execute,From,Fun} -> - Fun(), - From ! {self(),executed}, - Loop() - end - end). + receive + quit -> + ok; + {execute,From,Fun} -> + Fun(), + From ! {self(),executed}, + Loop() + end + end). turn_on_tracing(Pid) -> _ = erlang:trace(Pid, true, [call,set_on_spawn]), @@ -497,18 +497,18 @@ turn_on_tracing(Pid) -> collect_all_info() -> collect_all_info([{?MODULE,F,A} || {F,A} <- module_info(functions)] ++ - erlang:system_info(snifs)). + erlang:system_info(snifs)). collect_all_info([MFA|T]) -> CallTime = erlang:trace_info(MFA, call_time), erlang:trace_pattern(MFA, restart, [call_time]), case CallTime of - {call_time,false} -> - collect_all_info(T); - {call_time,[]} -> - collect_all_info(T); - {call_time,[_|_]=List} -> - [{MFA,List}|collect_all_info(T)] + {call_time,false} -> + collect_all_info(T); + {call_time,[]} -> + collect_all_info(T); + {call_time,[_|_]=List} -> + [{MFA,List}|collect_all_info(T)] end; collect_all_info([]) -> []. @@ -521,8 +521,8 @@ collect_all_info([]) -> []. %% Local helpers load_nif(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - ?line ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0). + Path = proplists:get_value(data_dir, Config), + ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0). %% Stack recursive seq @@ -569,39 +569,39 @@ seq_r(Start, Stop, Succ, R) -> % Check call time tracing data and print mismatches check_trace_info(Mfa, [{Pid, C,_,_}] = Expect, Time) -> case erlang:trace_info(Mfa, call_time) of - % Time tests are somewhat problematic. We want to know if Time (EXPECTED_TIME) and S*1000000 + Us (ACTUAL_TIME) - % is the same. - % If the ratio EXPECTED_TIME/ACTUAL_TIME is ~ 1 or if EXPECTED_TIME - ACTUAL_TIME is near zero, the test is ok. - {call_time,[{Pid,C,S,Us}]} when S >= 0, Us >= 0, abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR; abs(Time - S*1000000 - Us) < ?US_ERROR -> - ok; - {call_time,[{Pid,C,S,Us}]} -> - Sum = S*1000000 + Us, - io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, should be 1.0)~n", - [Mfa, Expect, Time, S, Us, Sum, Time, Sum - Time, Time/Sum]), - time_error; - Other -> - io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~p~n", [ Mfa, Expect, Time, Other]), - time_count_error + % Time tests are somewhat problematic. We want to know if Time (EXPECTED_TIME) and S*1000000 + Us (ACTUAL_TIME) + % is the same. + % If the ratio EXPECTED_TIME/ACTUAL_TIME is ~ 1 or if EXPECTED_TIME - ACTUAL_TIME is near zero, the test is ok. + {call_time,[{Pid,C,S,Us}]} when S >= 0, Us >= 0, abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR; abs(Time - S*1000000 - Us) < ?US_ERROR -> + ok; + {call_time,[{Pid,C,S,Us}]} -> + Sum = S*1000000 + Us, + io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, should be 1.0)~n", + [Mfa, Expect, Time, S, Us, Sum, Time, Sum - Time, Time/Sum]), + time_error; + Other -> + io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~p~n", [ Mfa, Expect, Time, Other]), + time_count_error end; check_trace_info(Mfa, Expect, _) -> case erlang:trace_info(Mfa, call_time) of - {call_time, Expect} -> - ok; - Other -> - io:format("Expected ~p -> {call_time, ~p}~n - got ~p~n", [Mfa, Expect, Other]), - result_not_expected_error + {call_time, Expect} -> + ok; + Other -> + io:format("Expected ~p -> {call_time, ~p}~n - got ~p~n", [Mfa, Expect, Other]), + result_not_expected_error end. %check process time check_process_time({value,{Pid, M, S, Us}}, M, F, Time) -> - ?line Sum = S*1000000 + Us, + Sum = S*1000000 + Us, if - abs(1 - (F/(Time/Sum))) < ?R_ERROR -> - ok; - true -> - io:format("- Pid ~p, Got ratio ~.2f, expected ratio ~w~n", [Pid, Time/Sum,F]), - schedule_time_error + abs(1 - (F/(Time/Sum))) < ?R_ERROR -> + ok; + true -> + io:format("- Pid ~p, Got ratio ~.2f, expected ratio ~w~n", [Pid, Time/Sum,F]), + schedule_time_error end; check_process_time(Other, M, _, _) -> io:format(" - Got ~p, expected count ~w~n", [Other, M]), @@ -614,8 +614,8 @@ relay_n(0, _) -> ok; relay_n(N, Dest) -> receive Msg -> - Dest ! {self(), Msg}, - relay_n(N-1, Dest) + Dest ! {self(), Msg}, + relay_n(N-1, Dest) end. @@ -629,17 +629,17 @@ collect(Time) -> collect(A, 0) -> receive - Mess -> - collect([Mess | A], 0) + Mess -> + collect([Mess | A], 0) after 0 -> - A + A end; collect(A, Ref) -> receive - {timeout, Ref, done} -> - collect(A, 0); - Mess -> - collect([Mess | A], Ref) + {timeout, Ref, done} -> + collect(A, 0); + Mess -> + collect([Mess | A], Ref) end. setup() -> @@ -667,12 +667,12 @@ execute(P, Mfa) -> loop() -> receive - quit -> - ok; - {Pid, execute, Fun } when is_function(Fun) -> - Pid ! {self(), answer, erlang:apply(Fun, [])}, - loop(); - {Pid, execute, {M, F, A}} -> - Pid ! {self(), answer, erlang:apply(M, F, A)}, - loop() + quit -> + ok; + {Pid, execute, Fun } when is_function(Fun) -> + Pid ! {self(), answer, erlang:apply(Fun, [])}, + loop(); + {Pid, execute, {M, F, A}} -> + Pid ! {self(), answer, erlang:apply(M, F, A)}, + loop() end. diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 80ffc18d17..24864dcbef 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -37,30 +37,30 @@ -include_lib("common_test/include/ct.hrl"). -define(DEFAULT_RECEIVE_TIMEOUT, infinity). - + -ifdef(debug). -define(dbgformat(A,B),io:format(A,B)). -else. -define(dbgformat(A,B),noop). -endif. - + %%% When run in test server %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -export([all/0, suite/0, - basic/1, bit_syntax/1, - return/1, on_and_off/1, systematic_on_off/1, - stack_grow/1,info/1, delete/1, - exception/1, exception_apply/1, - exception_function/1, exception_apply_function/1, - exception_nocatch/1, exception_nocatch_apply/1, - exception_nocatch_function/1, exception_nocatch_apply_function/1, - exception_meta/1, exception_meta_apply/1, - exception_meta_function/1, exception_meta_apply_function/1, - exception_meta_nocatch/1, exception_meta_nocatch_apply/1, - exception_meta_nocatch_function/1, - exception_meta_nocatch_apply_function/1, - concurrency/1, - init_per_testcase/2, end_per_testcase/2]). + basic/1, bit_syntax/1, + return/1, on_and_off/1, systematic_on_off/1, + stack_grow/1,info/1, delete/1, + exception/1, exception_apply/1, + exception_function/1, exception_apply_function/1, + exception_nocatch/1, exception_nocatch_apply/1, + exception_nocatch_function/1, exception_nocatch_apply_function/1, + exception_meta/1, exception_meta_apply/1, + exception_meta_function/1, exception_meta_apply_function/1, + exception_meta_nocatch/1, exception_meta_nocatch_apply/1, + exception_meta_nocatch_function/1, + exception_meta_nocatch_apply_function/1, + concurrency/1, + init_per_testcase/2, end_per_testcase/2]). init_per_testcase(_Case, Config) -> Config. @@ -80,21 +80,21 @@ suite() -> all() -> case test_server:is_native(trace_local_SUITE) of - true -> [not_run]; - false -> - [basic, bit_syntax, return, on_and_off, systematic_on_off, - stack_grow, - info, delete, exception, exception_apply, - exception_function, exception_apply_function, - exception_nocatch, exception_nocatch_apply, - exception_nocatch_function, - exception_nocatch_apply_function, exception_meta, - exception_meta_apply, exception_meta_function, - exception_meta_apply_function, exception_meta_nocatch, - exception_meta_nocatch_apply, - exception_meta_nocatch_function, - exception_meta_nocatch_apply_function, - concurrency] + true -> [not_run]; + false -> + [basic, bit_syntax, return, on_and_off, systematic_on_off, + stack_grow, + info, delete, exception, exception_apply, + exception_function, exception_apply_function, + exception_nocatch, exception_nocatch_apply, + exception_nocatch_function, + exception_nocatch_apply_function, exception_meta, + exception_meta_apply, exception_meta_function, + exception_meta_apply_function, exception_meta_nocatch, + exception_meta_nocatch_apply, + exception_meta_nocatch_function, + exception_meta_nocatch_apply_function, + concurrency] end. @@ -113,20 +113,20 @@ bit_syntax(Config) when is_list(Config) -> %% Tests the different types of return trace return(Config) when is_list(Config) -> return_test(). - + %% Tests turning trace parameters on and off, %% both for trace and trace_pattern on_and_off(Config) when is_list(Config) -> on_and_off_test(). - + %% Tests the stack growth during return traces stack_grow(Config) when is_list(Config) -> stack_grow_test(). - + %% Tests the trace_info BIF info(Config) when is_list(Config) -> info_test(). - + %% Tests putting trace on deleted modules delete(Config) when is_list(Config) -> delete_test(Config). @@ -243,28 +243,28 @@ expect_pid(Pid, Msg) when is_tuple(Msg) -> same(Msg, expect_receive(Pid)); expect_pid(Pid, Fun) when is_function(Fun, 1) -> case Fun(expect_receive(Pid)) of - next -> - expect_pid(Pid, Fun); - done -> - ok; - Other -> - expect_pid(Pid, Other) + next -> + expect_pid(Pid, Fun); + done -> + ok; + Other -> + expect_pid(Pid, Other) end. expect_receive(Pid) when is_pid(Pid) -> receive - Msg when is_tuple(Msg), - element(1, Msg) == trace, - element(2, Msg) =/= Pid; - %% - is_tuple(Msg), - element(1, Msg) == trace_ts, - element(2, Msg) =/= Pid -> - expect_receive(Pid); - Msg -> - expect_msg(Pid, Msg) + Msg when is_tuple(Msg), + element(1, Msg) == trace, + element(2, Msg) =/= Pid; + %% + is_tuple(Msg), + element(1, Msg) == trace_ts, + element(2, Msg) =/= Pid -> + expect_receive(Pid); + Msg -> + expect_msg(Pid, Msg) after 100 -> - {nm} + {nm} end. expect_msg(P, ?pCT(P,M,F,Args)) -> {ct,{M,F},Args}; @@ -277,18 +277,18 @@ expect_msg(P, ?pRT(P,M,F,Arity)) -> {rt,{M,F,Arity}}; expect_msg(P, ?pRTT(P,M,F,Arity)) -> {rtt,{M,F,Arity}}; expect_msg(P, Msg) when is_tuple(Msg) -> case tuple_to_list(Msg) of - [trace,P|T] -> - list_to_tuple([trace|T]); - [trace_ts,P|[_|_]=T] -> - list_to_tuple([trace_ts|reverse(tl(reverse(T)))]); - _ -> - Msg + [trace,P|T] -> + list_to_tuple([trace|T]); + [trace_ts,P|[_|_]=T] -> + list_to_tuple([trace_ts|reverse(tl(reverse(T)))]); + _ -> + Msg end. same(A, B) -> case [A|B] of - [X|X] -> - ok + [X|X] -> + ok end. @@ -296,73 +296,73 @@ same(A, B) -> %%% tests %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% basic_test() -> - ?line setup([call]), + setup([call]), NumMatches = erlang:trace_pattern({?MODULE,'_','_'},[],[local]), NumMatches = erlang:trace_pattern({?MODULE,'_','_'},[],[local]), - ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported,[1]), - ?line ?CT(?MODULE,local,[1]), - ?line ?CT(?MODULE,local2,[1]), - ?line ?CT(?MODULE,local_tail,[1]), - ?line erlang:trace_pattern({?MODULE,'_','_'},[],[]), - ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line [1,1,1,1] = lambda_slave(fun() -> - exported_wrap(1) - end), - ?line ?NM, - ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]), - ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), - ?line [1,1,1,1] = lambda_slave(fun() -> - exported_wrap(1) - end), - ?line ?CT(?MODULE,_,_), %% The fun - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported,[1]), - ?line ?CT(?MODULE,local,[1]), - ?line ?CT(?MODULE,local2,[1]), - ?line ?CT(?MODULE,local_tail,[1]), - ?line erlang:trace_pattern({?MODULE,'_','_'},false,[local]), - ?line shutdown(), - ?line ?NM, + erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported,[1]), + ?CT(?MODULE,local,[1]), + ?CT(?MODULE,local2,[1]), + ?CT(?MODULE,local_tail,[1]), + erlang:trace_pattern({?MODULE,'_','_'},[],[]), + erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported_wrap,[1]), + [1,1,1,1] = lambda_slave(fun() -> + exported_wrap(1) + end), + ?NM, + erlang:trace_pattern({?MODULE,'_','_'},[],[local]), + erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), + [1,1,1,1] = lambda_slave(fun() -> + exported_wrap(1) + end), + ?CT(?MODULE,_,_), %% The fun + ?CT(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported,[1]), + ?CT(?MODULE,local,[1]), + ?CT(?MODULE,local2,[1]), + ?CT(?MODULE,local_tail,[1]), + erlang:trace_pattern({?MODULE,'_','_'},false,[local]), + shutdown(), + ?NM, ok. %% OTP-7399. bit_syntax_test() -> - ?line setup([call]), - ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]), - ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), - - ?line lambda_slave(fun() -> - 6 = bs_sum_a(<<1,2,3>>, 0), - 10 = bs_sum_b(0, <<1,2,3,4>>), - 26 = bs_sum_c(<<3:4,5:4,7:4,11:4>>, 0) - end), - ?line ?CT(?MODULE,_,[]), %Ignore call to the fun. - - ?line ?CT(?MODULE,bs_sum_a,[<<1,2,3>>,0]), - ?line ?CT(?MODULE,bs_sum_a,[<<2,3>>,1]), - ?line ?CT(?MODULE,bs_sum_a,[<<3>>,3]), - ?line ?CT(?MODULE,bs_sum_a,[<<>>,6]), - - ?line ?CT(?MODULE,bs_sum_b,[0,<<1,2,3,4>>]), - ?line ?CT(?MODULE,bs_sum_b,[1,<<2,3,4>>]), - ?line ?CT(?MODULE,bs_sum_b,[3,<<3,4>>]), - ?line ?CT(?MODULE,bs_sum_b,[6,<<4>>]), - ?line ?CT(?MODULE,bs_sum_b,[10,<<>>]), - - ?line ?CT(?MODULE,bs_sum_c,[<<3:4,5:4,7:4,11:4>>, 0]), - ?line ?CT(?MODULE,bs_sum_c,[<<5:4,7:4,11:4>>, 3]), - ?line ?CT(?MODULE,bs_sum_c,[<<7:4,11:4>>, 8]), - ?line ?CT(?MODULE,bs_sum_c,[<<11:4>>, 15]), - ?line ?CT(?MODULE,bs_sum_c,[<<>>, 26]), - - ?line erlang:trace_pattern({?MODULE,'_','_'},false,[local]), - ?line shutdown(), - ?line ?NM, + setup([call]), + erlang:trace_pattern({?MODULE,'_','_'},[],[local]), + erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), + + lambda_slave(fun() -> + 6 = bs_sum_a(<<1,2,3>>, 0), + 10 = bs_sum_b(0, <<1,2,3,4>>), + 26 = bs_sum_c(<<3:4,5:4,7:4,11:4>>, 0) + end), + ?CT(?MODULE,_,[]), %Ignore call to the fun. + + ?CT(?MODULE,bs_sum_a,[<<1,2,3>>,0]), + ?CT(?MODULE,bs_sum_a,[<<2,3>>,1]), + ?CT(?MODULE,bs_sum_a,[<<3>>,3]), + ?CT(?MODULE,bs_sum_a,[<<>>,6]), + + ?CT(?MODULE,bs_sum_b,[0,<<1,2,3,4>>]), + ?CT(?MODULE,bs_sum_b,[1,<<2,3,4>>]), + ?CT(?MODULE,bs_sum_b,[3,<<3,4>>]), + ?CT(?MODULE,bs_sum_b,[6,<<4>>]), + ?CT(?MODULE,bs_sum_b,[10,<<>>]), + + ?CT(?MODULE,bs_sum_c,[<<3:4,5:4,7:4,11:4>>, 0]), + ?CT(?MODULE,bs_sum_c,[<<5:4,7:4,11:4>>, 3]), + ?CT(?MODULE,bs_sum_c,[<<7:4,11:4>>, 8]), + ?CT(?MODULE,bs_sum_c,[<<11:4>>, 15]), + ?CT(?MODULE,bs_sum_c,[<<>>, 26]), + + erlang:trace_pattern({?MODULE,'_','_'},false,[local]), + shutdown(), + ?NM, ok. @@ -376,149 +376,149 @@ bs_sum_c(<>, Acc) -> bs_sum_c(T, H+Acc); bs_sum_c(<<>>, Acc) -> Acc. return_test() -> - ?line setup([call]), - ?line erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}], - [local]), - ?line erlang:trace_pattern({erlang,hash,'_'},[{'_',[],[{return_trace}]}], - [local]), - ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported,[1]), - ?line ?CT(?MODULE,local,[1]), - ?line ?CT(?MODULE,local2,[1]), - ?line ?CT(?MODULE,local_tail,[1]), - ?line ?CT(erlang,hash,[1,1]), - ?line ?RF(erlang,hash,2,1), - ?line ?RF(?MODULE,local_tail,1,[1,1]), - ?line ?RF(?MODULE,local2,1,[1,1]), - ?line ?RF(?MODULE,local,1,[1,1,1]), - ?line ?RF(?MODULE,exported,1,[1,1,1,1]), - ?line ?RF(?MODULE,exported_wrap,1,[1,1,1,1]), - ?line shutdown(), - ?line setup([call,return_to]), - ?line erlang:trace_pattern({?MODULE,'_','_'},[], - [local]), - ?line erlang:trace_pattern({erlang,hash,'_'},[], - [local]), - ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported,[1]), - ?line ?CT(?MODULE,local,[1]), - ?line ?CT(?MODULE,local2,[1]), - ?line ?CT(?MODULE,local_tail,[1]), - ?line ?CT(erlang,hash,[1,1]), - ?line ?RT(?MODULE,local_tail,1), - ?line ?RT(?MODULE,local,1), - ?line ?RT(?MODULE,exported,1), - ?line ?RT(?MODULE,slave,2), - ?line shutdown(), - ?line setup([call,return_to]), - ?line erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}], - [local]), - ?line erlang:trace_pattern({erlang,hash,'_'},[{'_',[],[{return_trace}]}], - [local]), - ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported,[1]), - ?line ?CT(?MODULE,local,[1]), - ?line ?CT(?MODULE,local2,[1]), - ?line ?CT(?MODULE,local_tail,[1]), - ?line ?CT(erlang,hash,[1,1]), - ?line ?RF(erlang,hash,2,1), - ?line ?RT(?MODULE,local_tail,1), - ?line ?RF(?MODULE,local_tail,1,[1,1]), - ?line ?RF(?MODULE,local2,1,[1,1]), - ?line ?RT(?MODULE,local,1), - ?line ?RF(?MODULE,local,1,[1,1,1]), - ?line ?RT(?MODULE,exported,1), - ?line ?RF(?MODULE,exported,1,[1,1,1,1]), - ?line ?RF(?MODULE,exported_wrap,1,[1,1,1,1]), - ?line ?RT(?MODULE,slave,2), - ?line shutdown(), - ?line ?NM, + setup([call]), + erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}], + [local]), + erlang:trace_pattern({erlang,hash,'_'},[{'_',[],[{return_trace}]}], + [local]), + erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported,[1]), + ?CT(?MODULE,local,[1]), + ?CT(?MODULE,local2,[1]), + ?CT(?MODULE,local_tail,[1]), + ?CT(erlang,hash,[1,1]), + ?RF(erlang,hash,2,1), + ?RF(?MODULE,local_tail,1,[1,1]), + ?RF(?MODULE,local2,1,[1,1]), + ?RF(?MODULE,local,1,[1,1,1]), + ?RF(?MODULE,exported,1,[1,1,1,1]), + ?RF(?MODULE,exported_wrap,1,[1,1,1,1]), + shutdown(), + setup([call,return_to]), + erlang:trace_pattern({?MODULE,'_','_'},[], + [local]), + erlang:trace_pattern({erlang,hash,'_'},[], + [local]), + erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported,[1]), + ?CT(?MODULE,local,[1]), + ?CT(?MODULE,local2,[1]), + ?CT(?MODULE,local_tail,[1]), + ?CT(erlang,hash,[1,1]), + ?RT(?MODULE,local_tail,1), + ?RT(?MODULE,local,1), + ?RT(?MODULE,exported,1), + ?RT(?MODULE,slave,2), + shutdown(), + setup([call,return_to]), + erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}], + [local]), + erlang:trace_pattern({erlang,hash,'_'},[{'_',[],[{return_trace}]}], + [local]), + erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported,[1]), + ?CT(?MODULE,local,[1]), + ?CT(?MODULE,local2,[1]), + ?CT(?MODULE,local_tail,[1]), + ?CT(erlang,hash,[1,1]), + ?RF(erlang,hash,2,1), + ?RT(?MODULE,local_tail,1), + ?RF(?MODULE,local_tail,1,[1,1]), + ?RF(?MODULE,local2,1,[1,1]), + ?RT(?MODULE,local,1), + ?RF(?MODULE,local,1,[1,1,1]), + ?RT(?MODULE,exported,1), + ?RF(?MODULE,exported,1,[1,1,1,1]), + ?RF(?MODULE,exported_wrap,1,[1,1,1,1]), + ?RT(?MODULE,slave,2), + shutdown(), + ?NM, ok. on_and_off_test() -> - ?line Pid = setup([call]), - ?line 1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[local]), - ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), - ?line LocalTail = fun() -> - local_tail(1) - end, - ?line [1,1] = lambda_slave(LocalTail), - ?line ?CT(?MODULE,local_tail,[1]), - ?line erlang:trace(Pid,true,[return_to]), - ?line [1,1] = lambda_slave(LocalTail), - ?line ?CT(?MODULE,local_tail,[1]), - ?line ?RT(?MODULE,_,_), - ?line 0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]), - ?line [1,1] = lambda_slave(LocalTail), - ?line ?NM, - ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[global]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[local]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line ?RT(?MODULE,slave,2), - ?line 1 = erlang:trace_pattern({erlang,hash,2},[],[local]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line ?CT(erlang,hash,[1,1]), - ?line ?RT(?MODULE,local_tail,1), - ?line ?RT(?MODULE,slave,2), - ?line erlang:trace(Pid,true,[timestamp]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CTT(?MODULE,exported_wrap,[1]), - ?line ?CTT(erlang,hash,[1,1]), - ?line ?RTT(?MODULE,local_tail,1), - ?line ?RTT(?MODULE,slave,2), - ?line erlang:trace(Pid,false,[return_to,timestamp]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line ?CT(erlang,hash,[1,1]), - ?line erlang:trace(Pid,true,[return_to]), - ?line 1 = erlang:trace_pattern({erlang,hash,2},[],[]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line ?CT(erlang,hash,[1,1]), - ?line ?RT(?MODULE,slave,2), - ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[]), - ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), - ?line ?CT(?MODULE,exported_wrap,[1]), - ?line ?CT(erlang,hash,[1,1]), - ?line shutdown(), - ?line erlang:trace_pattern({'_','_','_'},false,[local]), - ?line N = erlang:trace_pattern({erlang,'_','_'},true,[local]), - ?line case erlang:trace_pattern({erlang,'_','_'},false,[local]) of - N -> - ok; - Else -> - exit({number_mismatch, {expected, N}, {got, Else}}) - end, - ?line case erlang:trace_pattern({erlang,'_','_'},false,[local]) of - N -> - ok; - Else2 -> - exit({number_mismatch, {expected, N}, {got, Else2}}) - end, - ?line M = erlang:trace_pattern({erlang,'_','_'},true,[]), - ?line case erlang:trace_pattern({erlang,'_','_'},false,[]) of - M -> - ok; - Else3 -> - exit({number_mismatch, {expected, N}, {got, Else3}}) - end, - ?line case erlang:trace_pattern({erlang,'_','_'},false,[]) of - M -> - ok; - Else4 -> - exit({number_mismatch, {expected, N}, {got, Else4}}) - end, - ?line ?NM, + Pid = setup([call]), + 1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[local]), + erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), + LocalTail = fun() -> + local_tail(1) + end, + [1,1] = lambda_slave(LocalTail), + ?CT(?MODULE,local_tail,[1]), + erlang:trace(Pid,true,[return_to]), + [1,1] = lambda_slave(LocalTail), + ?CT(?MODULE,local_tail,[1]), + ?RT(?MODULE,_,_), + 0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]), + [1,1] = lambda_slave(LocalTail), + ?NM, + 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[global]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported_wrap,[1]), + 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[local]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported_wrap,[1]), + ?RT(?MODULE,slave,2), + 1 = erlang:trace_pattern({erlang,hash,2},[],[local]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported_wrap,[1]), + ?CT(erlang,hash,[1,1]), + ?RT(?MODULE,local_tail,1), + ?RT(?MODULE,slave,2), + erlang:trace(Pid,true,[timestamp]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CTT(?MODULE,exported_wrap,[1]), + ?CTT(erlang,hash,[1,1]), + ?RTT(?MODULE,local_tail,1), + ?RTT(?MODULE,slave,2), + erlang:trace(Pid,false,[return_to,timestamp]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported_wrap,[1]), + ?CT(erlang,hash,[1,1]), + erlang:trace(Pid,true,[return_to]), + 1 = erlang:trace_pattern({erlang,hash,2},[],[]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported_wrap,[1]), + ?CT(erlang,hash,[1,1]), + ?RT(?MODULE,slave,2), + 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[]), + [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), + ?CT(?MODULE,exported_wrap,[1]), + ?CT(erlang,hash,[1,1]), + shutdown(), + erlang:trace_pattern({'_','_','_'},false,[local]), + N = erlang:trace_pattern({erlang,'_','_'},true,[local]), + case erlang:trace_pattern({erlang,'_','_'},false,[local]) of + N -> + ok; + Else -> + exit({number_mismatch, {expected, N}, {got, Else}}) + end, + case erlang:trace_pattern({erlang,'_','_'},false,[local]) of + N -> + ok; + Else2 -> + exit({number_mismatch, {expected, N}, {got, Else2}}) + end, + M = erlang:trace_pattern({erlang,'_','_'},true,[]), + case erlang:trace_pattern({erlang,'_','_'},false,[]) of + M -> + ok; + Else3 -> + exit({number_mismatch, {expected, N}, {got, Else3}}) + end, + case erlang:trace_pattern({erlang,'_','_'},false,[]) of + M -> + ok; + Else4 -> + exit({number_mismatch, {expected, N}, {got, Else4}}) + end, + ?NM, ok. systematic_on_off(Config) when is_list(Config) -> @@ -568,12 +568,12 @@ systematic_on_off_1(Local) -> verify_trace_info(Global, Local) -> case erlang:trace_info({?MODULE,exported_wrap,1}, all) of - {all,false} -> - false = Global, - [] = Local; - {all,Ps} -> - io:format("~p\n", [Ps]), - [verify_trace_info(P, Global, Local) || P <- Ps] + {all,false} -> + false = Global, + [] = Local; + {all,Ps} -> + io:format("~p\n", [Ps]), + [verify_trace_info(P, Global, Local) || P <- Ps] end, global_call(Global, Local), local_call(Local), @@ -585,9 +585,9 @@ verify_trace_info({match_spec,[]}, _, _) -> ok; verify_trace_info({meta_match_spec,[]}, _, _) -> ok; verify_trace_info({LocalFlag,Bool}, _, Local) when is_boolean(Bool) -> try - Bool = lists:member(LocalFlag, Local) + Bool = lists:member(LocalFlag, Local) catch - error:_ -> + error:_ -> ct:fail("Line ~p: {~p,~p}, false, ~p\n", [?LINE,LocalFlag,Bool,Local]) end; verify_trace_info({meta,Pid}, false, Local) when is_pid(Pid) -> @@ -600,10 +600,10 @@ verify_trace_info({call_count,_}, false, Local) -> global_call(Global, Local) -> apply_slave(?MODULE, exported_wrap, [global_call]), case Global of - false -> - recv_local_call(Local, [global_call]); - true -> - ?CT(?MODULE, exported_wrap, [global_call]) + false -> + recv_local_call(Local, [global_call]); + true -> + ?CT(?MODULE, exported_wrap, [global_call]) end. local_call(Local) -> @@ -612,16 +612,16 @@ local_call(Local) -> recv_local_call(Local, Args) -> case lists:member(local, Local) of - false -> - ok; - true -> - ?CT(?MODULE, exported_wrap, Args) + false -> + ok; + true -> + ?CT(?MODULE, exported_wrap, Args) end, case lists:member(meta, Local) of - false -> - ok; - true -> - ?CTT(?MODULE, exported_wrap, Args) + false -> + ok; + true -> + ?CTT(?MODULE, exported_wrap, Args) end, ok. @@ -630,99 +630,99 @@ combinations([_]=One) -> combinations([H|T]) -> Cs = combinations(T), [[H|C] || C <- Cs] ++ Cs. - + stack_grow_test() -> - ?line setup([call,return_to]), - ?line 1 = erlang:trace_pattern({?MODULE,loop,4}, - [{'_',[],[{return_trace}]}],[local]), - ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), - ?line Num = 1 bsl 15, - ?line Fun = - fun(_F,0) -> ok; - (F,N) -> - receive _A -> - receive _B -> - receive _C -> - F(F,N-1) - end - end - end - end, - ?line apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]), - ?line Fun(Fun,Num + 1), - ?line ?NM, + setup([call,return_to]), + 1 = erlang:trace_pattern({?MODULE,loop,4}, + [{'_',[],[{return_trace}]}],[local]), + erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), + Num = 1 bsl 15, + Fun = + fun(_F,0) -> ok; + (F,N) -> + receive _A -> + receive _B -> + receive _C -> + F(F,N-1) + end + end + end + end, + apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]), + Fun(Fun,Num + 1), + ?NM, ok. info_test() -> - ?line Flags1 = lists:sort([call,return_to]), - ?line Pid = setup(Flags1), - ?line Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]}, - {'_',[],[]}], - ?line erlang:trace_pattern({?MODULE,exported_wrap,1},Prog,[local]), - ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), - ?line Self = self(), - ?line {flags,L} = erlang:trace_info(Pid,flags), - ?line case lists:sort(L) of - Flags1 -> - ok; - Wrong1 -> - exit({bad_result, {erlang,trace_info,[Pid,flags]}, - {expected, Flags1}, {got, Wrong1}}) - end, - ?line {tracer,Tracer} = erlang:trace_info(Pid,tracer), - ?line case Tracer of - Self -> - ok; - Wrong2 -> - exit({bad_result, {erlang,trace_info,[Pid,tracer]}, - {expected, Self}, {got, Wrong2}}) - end, - ?line {traced,local} = erlang:trace_info({?MODULE,exported_wrap,1},traced), - ?line {match_spec, MS} = - erlang:trace_info({?MODULE,exported_wrap,1},match_spec), - ?line case MS of - Prog -> - ok; - Wrong3 -> - exit({bad_result, {erlang,trace_info, - [{?MODULE,exported_wrap,1}, - match_spec]}, - {expected, Prog}, {got, Wrong3}}) - end, - ?line erlang:garbage_collect(self()), - ?line receive - after 1 -> - ok - end, - ?line io:format("~p~n",[MS]), - ?line {match_spec,MS2} = - erlang:trace_info({?MODULE,exported_wrap,1},match_spec), - ?line io:format("~p~n",[MS2]), - ?line erlang:trace_pattern({?MODULE,exported_wrap,1},[],[]), - ?line {traced,global} = - erlang:trace_info({?MODULE,exported_wrap,1},traced), - ?line {match_spec,[]} = - erlang:trace_info({?MODULE,exported_wrap,1},match_spec), - ?line {traced,undefined} = - erlang:trace_info({?MODULE,exported_wrap,2},traced), - ?line {match_spec,undefined} = - erlang:trace_info({?MODULE,exported_wrap,2},match_spec), - ?line {traced,false} = erlang:trace_info({?MODULE,exported,1},traced), - ?line {match_spec,false} = - erlang:trace_info({?MODULE,exported,1},match_spec), - ?line shutdown(), + Flags1 = lists:sort([call,return_to]), + Pid = setup(Flags1), + Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]}, + {'_',[],[]}], + erlang:trace_pattern({?MODULE,exported_wrap,1},Prog,[local]), + erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), + Self = self(), + {flags,L} = erlang:trace_info(Pid,flags), + case lists:sort(L) of + Flags1 -> + ok; + Wrong1 -> + exit({bad_result, {erlang,trace_info,[Pid,flags]}, + {expected, Flags1}, {got, Wrong1}}) + end, + {tracer,Tracer} = erlang:trace_info(Pid,tracer), + case Tracer of + Self -> + ok; + Wrong2 -> + exit({bad_result, {erlang,trace_info,[Pid,tracer]}, + {expected, Self}, {got, Wrong2}}) + end, + {traced,local} = erlang:trace_info({?MODULE,exported_wrap,1},traced), + {match_spec, MS} = + erlang:trace_info({?MODULE,exported_wrap,1},match_spec), + case MS of + Prog -> + ok; + Wrong3 -> + exit({bad_result, {erlang,trace_info, + [{?MODULE,exported_wrap,1}, + match_spec]}, + {expected, Prog}, {got, Wrong3}}) + end, + erlang:garbage_collect(self()), + receive + after 1 -> + ok + end, + io:format("~p~n",[MS]), + {match_spec,MS2} = + erlang:trace_info({?MODULE,exported_wrap,1},match_spec), + io:format("~p~n",[MS2]), + erlang:trace_pattern({?MODULE,exported_wrap,1},[],[]), + {traced,global} = + erlang:trace_info({?MODULE,exported_wrap,1},traced), + {match_spec,[]} = + erlang:trace_info({?MODULE,exported_wrap,1},match_spec), + {traced,undefined} = + erlang:trace_info({?MODULE,exported_wrap,2},traced), + {match_spec,undefined} = + erlang:trace_info({?MODULE,exported_wrap,2},match_spec), + {traced,false} = erlang:trace_info({?MODULE,exported,1},traced), + {match_spec,false} = + erlang:trace_info({?MODULE,exported,1},match_spec), + shutdown(), ok. delete_test(Config) -> - ?line Priv = proplists:get_value(priv_dir, Config), - ?line Data = proplists:get_value(data_dir, Config), - ?line File = filename:join(Data, "trace_local_dummy"), - ?line {ok,trace_local_dummy} = c:c(File, [{outdir,Priv}]), - ?line code:purge(trace_local_dummy), - ?line code:delete(trace_local_dummy), - ?line 0 = erlang:trace_pattern({trace_local_dummy,'_','_'},true,[local]), - ?line ?NM, + Priv = proplists:get_value(priv_dir, Config), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "trace_local_dummy"), + {ok,trace_local_dummy} = c:c(File, [{outdir,Priv}]), + code:purge(trace_local_dummy), + code:delete(trace_local_dummy), + 0 = erlang:trace_pattern({trace_local_dummy,'_','_'},true,[local]), + ?NM, ok. @@ -730,34 +730,34 @@ delete_test(Config) -> %%% exception_test %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% exception_test(Opts) -> - ?line {ProcFlags,PatFlags} = - case proplists:get_bool(meta, Opts) of - true -> {[timestamp],[meta]}; - false -> {[call,return_to,timestamp],[local]} - end, - ?line case proplists:get_bool(nocatch, Opts) of - false -> - ?line Exceptions = exceptions(), - ?line exception_test_setup(ProcFlags, PatFlags), - ?line lists:foreach( - fun ({Func,Args}) -> - ?line exception_test(Opts, Func, Args) - end, - Exceptions), - ?line shutdown(); - true -> - ?line Exceptions = exceptions(), - ?line lists:foreach( - fun ({Func,Args}) -> - ?line exception_test_setup( - [procs|ProcFlags], - PatFlags), - ?line exception_test(Opts, Func, Args), - ?line shutdown() - end, - Exceptions) - end, - ?line ok. + {ProcFlags,PatFlags} = + case proplists:get_bool(meta, Opts) of + true -> {[timestamp],[meta]}; + false -> {[call,return_to,timestamp],[local]} + end, + case proplists:get_bool(nocatch, Opts) of + false -> + Exceptions = exceptions(), + exception_test_setup(ProcFlags, PatFlags), + lists:foreach( + fun ({Func,Args}) -> + exception_test(Opts, Func, Args) + end, + Exceptions), + shutdown(); + true -> + Exceptions = exceptions(), + lists:foreach( + fun ({Func,Args}) -> + exception_test_setup( + [procs|ProcFlags], + PatFlags), + exception_test(Opts, Func, Args), + shutdown() + end, + Exceptions) + end, + ok. exceptions() -> Ref = make_ref(), @@ -780,65 +780,65 @@ exceptions() -> {{?MODULE,lists_reverse}, [LL,[]]}]. exception_test_setup(ProcFlags, PatFlags) -> - ?line Pid = setup(ProcFlags), - ?line io:format("=== exception_test_setup(~p, ~p): ~p~n", - [ProcFlags,PatFlags,Pid]), - ?line Mprog = [{'_',[],[{exception_trace}]}], - ?line erlang:trace_pattern({?MODULE,'_','_'}, Mprog, PatFlags), - ?line erlang:trace_pattern({?MODULE,slave,'_'},false,PatFlags), - ?line [1,1,1,1,1] = - [erlang:trace_pattern({erlang,F,A}, Mprog, PatFlags) - || {F,A} <- [{exit,1},{error,1},{error,2},{throw,1},{'++',2}]], - ?line 1 = erlang:trace_pattern({lists,reverse,2}, Mprog, PatFlags), - ?line ok. + Pid = setup(ProcFlags), + io:format("=== exception_test_setup(~p, ~p): ~p~n", + [ProcFlags,PatFlags,Pid]), + Mprog = [{'_',[],[{exception_trace}]}], + erlang:trace_pattern({?MODULE,'_','_'}, Mprog, PatFlags), + erlang:trace_pattern({?MODULE,slave,'_'},false,PatFlags), + [1,1,1,1,1] = + [erlang:trace_pattern({erlang,F,A}, Mprog, PatFlags) + || {F,A} <- [{exit,1},{error,1},{error,2},{throw,1},{'++',2}]], + 1 = erlang:trace_pattern({lists,reverse,2}, Mprog, PatFlags), + ok. -record(exc_opts, {nocatch=false, meta=false}). exception_test(Opts, Func0, Args0) -> - ?line io:format("=== exception_test(~p, ~p, ~p)~n", - [Opts,Func0,abbr(Args0)]), - ?line Apply = proplists:get_bool(apply, Opts), - ?line Function = proplists:get_bool(function, Opts), - ?line Nocatch = proplists:get_bool(nocatch, Opts), - ?line Meta = proplists:get_bool(meta, Opts), - ?line ExcOpts = #exc_opts{nocatch=Nocatch,meta=Meta}, - + io:format("=== exception_test(~p, ~p, ~p)~n", + [Opts,Func0,abbr(Args0)]), + Apply = proplists:get_bool(apply, Opts), + Function = proplists:get_bool(function, Opts), + Nocatch = proplists:get_bool(nocatch, Opts), + Meta = proplists:get_bool(meta, Opts), + ExcOpts = #exc_opts{nocatch=Nocatch,meta=Meta}, + %% Func0 and Args0 are for the innermost call, now we will %% wrap them in wrappers... - ?line {Func1,Args1} = - case Function of - true -> {fun exc/2,[Func0,Args0]}; - false -> {Func0,Args0} - end, - - ?line {Func,Args} = - case Apply of - true -> {{erlang,apply},[Func1,Args1]}; - false -> {Func1,Args1} - end, - - ?line R1 = exc_slave(ExcOpts, Func, Args), - ?line Stack2 = [{?MODULE,exc_top,3,[]},{?MODULE,slave,2,[]}], - ?line Stack3 = [{?MODULE,exc,2,[]}|Stack2], - ?line Rs = - case x_exc_top(ExcOpts, Func, Args) of % Emulation - {crash,{Reason,Stack}}=R when is_list(Stack) -> - [R, - {crash,{Reason,Stack++Stack2}}, - {crash,{Reason,Stack++Stack3}}]; - R -> - [R] - end, - ?line exception_validate(R1, Rs), - ?line case R1 of - {crash,Crash} -> - ?line expect({trace_ts,exit,Crash}); - _ when not Meta -> - ?line expect({rtt,{?MODULE,slave,2}}); - _ -> - ok - end, - ?line expect({nm}). + {Func1,Args1} = + case Function of + true -> {fun exc/2,[Func0,Args0]}; + false -> {Func0,Args0} + end, + + {Func,Args} = + case Apply of + true -> {{erlang,apply},[Func1,Args1]}; + false -> {Func1,Args1} + end, + + R1 = exc_slave(ExcOpts, Func, Args), + Stack2 = [{?MODULE,exc_top,3,[]},{?MODULE,slave,2,[]}], + Stack3 = [{?MODULE,exc,2,[]}|Stack2], + Rs = + case x_exc_top(ExcOpts, Func, Args) of % Emulation + {crash,{Reason,Stack}}=R when is_list(Stack) -> + [R, + {crash,{Reason,Stack++Stack2}}, + {crash,{Reason,Stack++Stack3}}]; + R -> + [R] + end, + exception_validate(R1, Rs), + case R1 of + {crash,Crash} -> + expect({trace_ts,exit,Crash}); + _ when not Meta -> + expect({rtt,{?MODULE,slave,2}}); + _ -> + ok + end, + expect({nm}). exception_validate(R0, Rs0) -> R = clean_location(R0), @@ -847,16 +847,16 @@ exception_validate(R0, Rs0) -> exception_validate_1(R1, [R2|Rs]) -> case [R1|R2] of - [R|R] -> - ok; - [{crash,{badarg,[{lists,reverse,[L1a,L1b],_}|T]}}| - {crash,{badarg,[{lists,reverse,[L2a,L2b],_}|T]}}] -> - same({crash,{badarg,[{lists,reverse, - [lists:reverse(L1b, L1a),[]],[]}|T]}}, - {crash,{badarg,[{lists,reverse, - [lists:reverse(L2b, L2a),[]],[]}|T]}}); - _ when is_list(Rs), Rs =/= [] -> - exception_validate(R1, Rs) + [R|R] -> + ok; + [{crash,{badarg,[{lists,reverse,[L1a,L1b],_}|T]}}| + {crash,{badarg,[{lists,reverse,[L2a,L2b],_}|T]}}] -> + same({crash,{badarg,[{lists,reverse, + [lists:reverse(L1b, L1a),[]],[]}|T]}}, + {crash,{badarg,[{lists,reverse, + [lists:reverse(L2b, L2a),[]],[]}|T]}}); + _ when is_list(Rs), Rs =/= [] -> + exception_validate(R1, Rs) end. clean_location({crash,{Reason,Stk0}}) -> @@ -874,7 +874,7 @@ concurrency(_Config) -> %% if an aligned word-sized write is not atomic. Ps0 = [spawn_monitor(fun() -> infinite_loop() end) || - _ <- lists:seq(1, 2*N)], + _ <- lists:seq(1, 2*N)], OnAndOff = fun() -> concurrency_on_and_off() end, Ps1 = [spawn_monitor(OnAndOff)|Ps0], timer:sleep(1000), @@ -887,7 +887,7 @@ concurrency(_Config) -> %% Clean up. [exit(Pid, kill) || {Pid,_} <- Ps], [receive - {'DOWN',Ref,process,Pid,killed} -> ok + {'DOWN',Ref,process,Pid,killed} -> ok end || {Pid,Ref} <- Ps], erlang:trace_pattern({?MODULE,infinite_loop,0}, false, [local]), ok. @@ -931,16 +931,16 @@ local_tail(Val) -> exc_top(ExcOpts, Func, Args) -> case ExcOpts#exc_opts.nocatch of - false -> - try exc_jump(Func, Args) of - Value -> - {value,Value} - catch - Class:Reason -> - {Class,Reason} - end; - true -> - {value,exc_jump(Func, Args)} + false -> + try exc_jump(Func, Args) of + Value -> + {value,Value} + catch + Class:Reason -> + {Class,Reason} + end; + true -> + {value,exc_jump(Func, Args)} end. %% x_* functions emulate the non-x_* ones. @@ -949,42 +949,42 @@ exc_top(ExcOpts, Func, Args) -> %% The only possible place for exception %% is below exc/2. x_exc_top(ExcOpts, Func, Args) -> - ?line Rtt = not ExcOpts#exc_opts.meta, - ?line expect({ctt,{?MODULE,exc_top},[ExcOpts,Func,Args]}), - ?line case x_exc_jump(ExcOpts, Func, Args) of - Result when not ExcOpts#exc_opts.nocatch -> - ?line expect([Rtt,{rtt,{?MODULE,exc_top,3}}, - ?LINE,{rft,{?MODULE,exc_top,3},Result}]), - ?line Result; - {value,_}=Result -> - - ?line expect([Rtt,{rtt,{?MODULE,exc_top,3}}, - ?LINE,{rft,{?MODULE,exc_top,3},Result}]), - ?line Result; - {exit,Reason}=CR -> - ?line expect({eft,{?MODULE,exc_top,3},CR}), - ?line {crash,Reason}; - {error,Reason}=CR -> - ?line expect({eft,{?MODULE,exc_top,3},CR}), - ?line {crash,{Reason,x_exc_stacktrace()}}; - CR -> - ?line expect({eft,{?MODULE,exc_top,3},CR}), - ?line {crash,CR} - end. + Rtt = not ExcOpts#exc_opts.meta, + expect({ctt,{?MODULE,exc_top},[ExcOpts,Func,Args]}), + case x_exc_jump(ExcOpts, Func, Args) of + Result when not ExcOpts#exc_opts.nocatch -> + expect([Rtt,{rtt,{?MODULE,exc_top,3}}, + ?LINE,{rft,{?MODULE,exc_top,3},Result}]), + Result; + {value,_}=Result -> + + expect([Rtt,{rtt,{?MODULE,exc_top,3}}, + ?LINE,{rft,{?MODULE,exc_top,3},Result}]), + Result; + {exit,Reason}=CR -> + expect({eft,{?MODULE,exc_top,3},CR}), + {crash,Reason}; + {error,Reason}=CR -> + expect({eft,{?MODULE,exc_top,3},CR}), + {crash,{Reason,x_exc_stacktrace()}}; + CR -> + expect({eft,{?MODULE,exc_top,3},CR}), + {crash,CR} + end. exc_jump(Func, Args) -> exc(Func, Args, jump). x_exc_jump(ExcOpts, Func, Args) -> - ?line expect({ctt,{?MODULE,exc_jump},[Func,Args]}), - ?line case x_exc(ExcOpts, Func, Args, jump) of - {value,Value}=Result -> - ?line expect({rft,{?MODULE,exc_jump,2},Value}), - ?line Result; - CR -> - ?line expect({eft,{?MODULE,exc_jump,2},CR}), - ?line CR - end. + expect({ctt,{?MODULE,exc_jump},[Func,Args]}), + case x_exc(ExcOpts, Func, Args, jump) of + {value,Value}=Result -> + expect({rft,{?MODULE,exc_jump,2},Value}), + Result; + CR -> + expect({eft,{?MODULE,exc_jump,2},CR}), + CR + end. exc(Func, Args, jump) -> exc(Func, Args, do); @@ -992,25 +992,25 @@ exc(Func, Args, do) -> exc(Func, Args). x_exc(ExcOpts, Func, Args, jump) -> - ?line expect({ctt,{?MODULE,exc},[Func,Args,jump]}), - ?line case x_exc(ExcOpts, Func, Args, do) of - {value,Value}=Result -> - ?line expect({rft,{?MODULE,exc,3},Value}), - ?line Result; - CR -> - ?line expect({eft,{?MODULE,exc,3},CR}), - ?line CR - end; + expect({ctt,{?MODULE,exc},[Func,Args,jump]}), + case x_exc(ExcOpts, Func, Args, do) of + {value,Value}=Result -> + expect({rft,{?MODULE,exc,3},Value}), + Result; + CR -> + expect({eft,{?MODULE,exc,3},CR}), + CR + end; x_exc(ExcOpts, Func, Args, do) -> - ?line expect({ctt,{?MODULE,exc},[Func,Args,do]}), - ?line case x_exc(ExcOpts, Func, Args) of - {value,Value}=Result -> - ?line expect({rft,{?MODULE,exc,3},Value}), - ?line Result; - CR -> - ?line expect({eft,{?MODULE,exc,3},CR}), - ?line CR - end. + expect({ctt,{?MODULE,exc},[Func,Args,do]}), + case x_exc(ExcOpts, Func, Args) of + {value,Value}=Result -> + expect({rft,{?MODULE,exc,3},Value}), + Result; + CR -> + expect({eft,{?MODULE,exc,3},CR}), + CR + end. exc({erlang,apply}, [{M,F},A]) -> erlang:apply(M, F, id(A)); @@ -1040,114 +1040,114 @@ exc(Func, [A,B]) when is_function(Func, 2) -> Func(A, id(B)). x_exc(ExcOpts, {erlang,apply}=Func0, [{_,_}=Func,Args]=Args0) -> - ?line expect({ctt,{?MODULE,exc},[Func0,Args0]}), - ?line x_exc_body(ExcOpts, Func, Args, true); + expect({ctt,{?MODULE,exc},[Func0,Args0]}), + x_exc_body(ExcOpts, Func, Args, true); x_exc(ExcOpts, {erlang,apply}=Func0, [Func,Args]=Args0) when is_function(Func, 2)-> - ?line expect({ctt,{?MODULE,exc},[Func0,Args0]}), - ?line x_exc_func(ExcOpts, Func, Args, Args); + expect({ctt,{?MODULE,exc},[Func0,Args0]}), + x_exc_func(ExcOpts, Func, Args, Args); x_exc(ExcOpts, {_,_}=Func, Args) -> - ?line expect({ctt,{?MODULE,exc},[Func,Args]}), - ?line x_exc_body(ExcOpts, Func, Args, false); + expect({ctt,{?MODULE,exc},[Func,Args]}), + x_exc_body(ExcOpts, Func, Args, false); x_exc(ExcOpts, Func0, [_,Args]=Args0) when is_function(Func0, 2) -> - ?line expect({ctt,{?MODULE,exc},[Func0,Args0]}), - ?line x_exc_func(ExcOpts, Func0, Args0, Args). + expect({ctt,{?MODULE,exc},[Func0,Args0]}), + x_exc_func(ExcOpts, Func0, Args0, Args). x_exc_func(ExcOpts, Func, [Func1,Args1]=Args, Id) -> %% Assumes the called fun =:= fun exc/2, %% will utterly fail otherwise. - ?line Rtt = not ExcOpts#exc_opts.meta, - ?line {module,M} = erlang:fun_info(Func, module), - ?line {name,F} = erlang:fun_info(Func, name), - ?line expect([{ctt,{?MODULE,id},[Id]}, - ?LINE,{rft,{?MODULE,id,1},Id}, - ?LINE,Rtt,{rtt,{?MODULE,exc,2}}, - ?LINE,{ctt,{M,F},Args}]), - ?line case x_exc(ExcOpts, Func1, Args1) of - {value,Value}=Result -> - ?line expect([{rft,{M,F,2},Value}, - ?LINE,{rft,{?MODULE,exc,2},Value}]), - ?line Result; - CR -> - ?line expect([{eft,{M,F,2},CR}, - ?LINE,{eft,{?MODULE,exc,2},CR}]), - ?line CR - end. + Rtt = not ExcOpts#exc_opts.meta, + {module,M} = erlang:fun_info(Func, module), + {name,F} = erlang:fun_info(Func, name), + expect([{ctt,{?MODULE,id},[Id]}, + ?LINE,{rft,{?MODULE,id,1},Id}, + ?LINE,Rtt,{rtt,{?MODULE,exc,2}}, + ?LINE,{ctt,{M,F},Args}]), + case x_exc(ExcOpts, Func1, Args1) of + {value,Value}=Result -> + expect([{rft,{M,F,2},Value}, + ?LINE,{rft,{?MODULE,exc,2},Value}]), + Result; + CR -> + expect([{eft,{M,F,2},CR}, + ?LINE,{eft,{?MODULE,exc,2},CR}]), + CR + end. x_exc_body(ExcOpts, {M,F}=Func, Args, Apply) -> - ?line Nocatch = ExcOpts#exc_opts.nocatch, - ?line Rtt = not ExcOpts#exc_opts.meta, - ?line Id = case Apply of - true -> Args; - false -> lists:last(Args) - end, - ?line expect([{ctt,{?MODULE,id},[Id]}, - ?LINE,{rft,{?MODULE,id,1},Id}, - ?LINE,Rtt,{rtt,{?MODULE,exc,2}}, - ?LINE,{ctt,{M,F},Args}]), - ?line Arity = length(Args), - ?line try exc(Func, Args) of - Value -> - ?line x_exc_value(Rtt, M, F, Args, Arity, Value), - ?line case expect() of - {rtt,{M,F,Arity}} when Rtt, Apply -> - %% We may get the above when - %% applying a BIF. - ?line expect({rft,{?MODULE,exc,2},Value}); - {rtt,{?MODULE,exc,2}} when Rtt, not Apply -> - %% We may get the above when - %% calling a BIF. - ?line expect({rft,{?MODULE,exc,2},Value}); - {rft,{?MODULE,exc,2},Value} -> - ?line ok - end, - ?line {value,Value} - catch - Thrown when Nocatch -> - ?line CR = {error,{nocatch,Thrown}}, - ?line x_exc_exception(Rtt, M, F, Args, Arity, CR), - ?line expect({eft,{?MODULE,exc,2},CR}), - ?line CR; - Class:Reason -> - ?line CR = {Class,Reason}, - ?line x_exc_exception(Rtt, M, F, Args, Arity, CR), - ?line expect({eft,{?MODULE,exc,2},CR}), - ?line CR - end. + Nocatch = ExcOpts#exc_opts.nocatch, + Rtt = not ExcOpts#exc_opts.meta, + Id = case Apply of + true -> Args; + false -> lists:last(Args) + end, + expect([{ctt,{?MODULE,id},[Id]}, + ?LINE,{rft,{?MODULE,id,1},Id}, + ?LINE,Rtt,{rtt,{?MODULE,exc,2}}, + ?LINE,{ctt,{M,F},Args}]), + Arity = length(Args), + try exc(Func, Args) of + Value -> + x_exc_value(Rtt, M, F, Args, Arity, Value), + case expect() of + {rtt,{M,F,Arity}} when Rtt, Apply -> + %% We may get the above when + %% applying a BIF. + expect({rft,{?MODULE,exc,2},Value}); + {rtt,{?MODULE,exc,2}} when Rtt, not Apply -> + %% We may get the above when + %% calling a BIF. + expect({rft,{?MODULE,exc,2},Value}); + {rft,{?MODULE,exc,2},Value} -> + ok + end, + {value,Value} + catch + Thrown when Nocatch -> + CR = {error,{nocatch,Thrown}}, + x_exc_exception(Rtt, M, F, Args, Arity, CR), + expect({eft,{?MODULE,exc,2},CR}), + CR; + Class:Reason -> + CR = {Class,Reason}, + x_exc_exception(Rtt, M, F, Args, Arity, CR), + expect({eft,{?MODULE,exc,2},CR}), + CR + end. x_exc_value(Rtt, ?MODULE, lists_reverse, [La,Lb], 2, R) -> - ?line L = lists:reverse(Lb, La), - ?line expect([fun ({ctt,{lists,reverse},[L1,L2]}) -> - ?line same(L, lists:reverse(L2, L1)), - ?line next; - (Msg) -> - ?line same({rft,{lists,reverse,2},R}, Msg), - ?line same(R, lists:reverse(L, [])), - ?line done - end, - ?LINE,Rtt,{rtt,{?MODULE,lists_reverse,2}}, - ?LINE,{rft,{?MODULE,lists_reverse,2},R}]); + L = lists:reverse(Lb, La), + expect([fun ({ctt,{lists,reverse},[L1,L2]}) -> + same(L, lists:reverse(L2, L1)), + next; + (Msg) -> + same({rft,{lists,reverse,2},R}, Msg), + same(R, lists:reverse(L, [])), + done + end, + ?LINE,Rtt,{rtt,{?MODULE,lists_reverse,2}}, + ?LINE,{rft,{?MODULE,lists_reverse,2},R}]); x_exc_value(_Rtt, M, F, _, Arity, Value) -> - ?line expect({rft,{M,F,Arity},Value}). + expect({rft,{M,F,Arity},Value}). x_exc_exception(_Rtt, ?MODULE, lists_reverse, [La,Lb], 2, CR) -> - ?line L = lists:reverse(Lb, La), - ?line expect([fun ({ctt,{lists,reverse},[L1,L2]}) -> - ?line same(L, lists:reverse(L2, L1)), - ?line next; - (Msg) -> - ?line same({eft,{lists,reverse,2},CR}, Msg), - ?line done - end, - ?LINE,{eft,{?MODULE,lists_reverse,2},CR}]); + L = lists:reverse(Lb, La), + expect([fun ({ctt,{lists,reverse},[L1,L2]}) -> + same(L, lists:reverse(L2, L1)), + next; + (Msg) -> + same({eft,{lists,reverse,2},CR}, Msg), + done + end, + ?LINE,{eft,{?MODULE,lists_reverse,2},CR}]); x_exc_exception(Rtt, ?MODULE, undef, [_], 1, {Class,Reason}=CR) -> - ?line expect([{ctt,{erlang,Class},[Reason]}, - ?LINE,{eft,{erlang,Class,1},CR}, - ?LINE,Rtt,{rtt,{error_handler,crash,1}}, - ?LINE,{eft,{?MODULE,undef,1},CR}]); + expect([{ctt,{erlang,Class},[Reason]}, + ?LINE,{eft,{erlang,Class,1},CR}, + ?LINE,Rtt,{rtt,{error_handler,crash,1}}, + ?LINE,{eft,{?MODULE,undef,1},CR}]); x_exc_exception(_Rtt, M, F, _, Arity, CR) -> - ?line expect({eft,{M,F,Arity},CR}). + expect({eft,{M,F,Arity},CR}). x_exc_stacktrace() -> x_exc_stacktrace(erlang:get_stacktrace()). @@ -1184,24 +1184,24 @@ lists_reverse(A, B) -> slave(Dest, Sync) -> Dest ! Sync, receive - {From,Tag,{apply,M,F,A}} when is_pid(From) -> - ?line ?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]), - ?line Res = apply(M,F,A), - ?line ?dbgformat("done Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]), - From ! {Tag,Res}, - slave(From, Tag); - {From,Tag,{lambda,Fun}} when is_pid(From) -> - Res = Fun(), - From ! {Tag,Res}, - slave(From, Tag); - {From,Tag,{exc_top,Catch,Func,Args}} when is_pid(From) -> - ?line ?dbgformat("Exc: ~p ~p~p ~n",[Catch,Func,Args]), - ?line Res = exc_top(Catch, Func, Args), - ?line ?dbgformat("done Exc: ~p ~p~p ~n",[Catch,Func,Args]), - From ! {Tag,Res}, - slave(From,Tag); - die -> - exit(normal) + {From,Tag,{apply,M,F,A}} when is_pid(From) -> + ?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]), + Res = apply(M,F,A), + ?dbgformat("done Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]), + From ! {Tag,Res}, + slave(From, Tag); + {From,Tag,{lambda,Fun}} when is_pid(From) -> + Res = Fun(), + From ! {Tag,Res}, + slave(From, Tag); + {From,Tag,{exc_top,Catch,Func,Args}} when is_pid(From) -> + ?dbgformat("Exc: ~p ~p~p ~n",[Catch,Func,Args]), + Res = exc_top(Catch, Func, Args), + ?dbgformat("done Exc: ~p ~p~p ~n",[Catch,Func,Args]), + From ! {Tag,Res}, + slave(From,Tag); + die -> + exit(normal) end. setup(ProcFlags) -> @@ -1212,30 +1212,30 @@ setup(ProcFlags) -> Pid = spawn(fun () -> slave(Self, Sync) end), Mref = erlang:monitor(process, Pid), receive - Sync -> - put(slave, {Pid,Mref}), - case ProcFlags of - [] -> ok; - _ -> - erlang:trace(Pid, true, ProcFlags) - end, - Pid + Sync -> + put(slave, {Pid,Mref}), + case ProcFlags of + [] -> ok; + _ -> + erlang:trace(Pid, true, ProcFlags) + end, + Pid end. shutdown() -> trace_off(), {Pid,Mref} = get(slave), try erlang:is_process_alive(Pid) of - true -> - Pid ! die, - receive - {'DOWN',Mref,process,Pid,Reason} -> - Reason - end; - _ -> - not_alive + true -> + Pid ! die, + receive + {'DOWN',Mref,process,Pid,Reason} -> + Reason + end; + _ -> + not_alive catch _:_ -> - undefined + undefined end. trace_off() -> @@ -1243,7 +1243,7 @@ trace_off() -> erlang:trace_pattern({'_','_','_'},false,[local]), erlang:trace_pattern({'_','_','_'},false,[meta]), erlang:trace(all, false, [all]). - + apply_slave_async(M,F,A) -> {Pid,Mref} = get(slave), @@ -1264,8 +1264,8 @@ lambda_slave(Fun) -> exc_slave(Opts, Func, Args) -> try request({exc_top,Opts,Func,Args}) catch - Reason -> - {crash,Reason} + Reason -> + {crash,Reason} end. request(Request) -> @@ -1276,13 +1276,13 @@ request(Request) -> result(Tag, Mref) -> receive - {Tag,Result} -> - receive - Tag -> - Result - end; - {'DOWN',Mref,process,_Pid,Reason} -> - throw(Reason) + {Tag,Result} -> + receive + Tag -> + Result + end; + {'DOWN',Mref,process,_Pid,Reason} -> + throw(Reason) end. @@ -1295,25 +1295,25 @@ receive_next() -> receive_next(TO) -> receive - M -> - M + M -> + M after TO -> - ct:fail(timeout) + ct:fail(timeout) end. receive_no_next(TO) -> receive M -> - ct:fail({unexpected_message,[M|flush(TO)]}) + ct:fail({unexpected_message,[M|flush(TO)]}) after TO -> - ok + ok end. flush(T) -> receive - M -> - [M|flush(T)] + M -> + [M|flush(T)] after T -> - [] + [] end. @@ -1348,19 +1348,19 @@ abbr(Term, _) -> Term. %% abbr_tuple(Tuple, N, J) when J =< size(Tuple) -> if J > N; N =< 0 -> - ['...']; + ['...']; true -> - [abbr(element(J, Tuple), N-1)|abbr_tuple(Tuple, J+1, N)] + [abbr(element(J, Tuple), N-1)|abbr_tuple(Tuple, J+1, N)] end; abbr_tuple(_, _, _) -> []. %% abbr_list(_, 0, R) -> case io_lib:printable_list(R) of - true -> - reverse(R, "..."); - false -> - reverse(R, '...') + true -> + reverse(R, "..."); + false -> + reverse(R, '...') end; abbr_list([H|T], N, R) -> M = N-1, diff --git a/erts/emulator/test/trace_nif_SUITE.erl b/erts/emulator/test/trace_nif_SUITE.erl index 4cf9069f1d..49757a414d 100644 --- a/erts/emulator/test/trace_nif_SUITE.erl +++ b/erts/emulator/test/trace_nif_SUITE.erl @@ -22,8 +22,7 @@ -include_lib("common_test/include/ct.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2]). +-export([all/0, suite/0]). -export([trace_nif/1, trace_nif_timestamp/1, trace_nif_local/1, @@ -45,29 +44,13 @@ all() -> trace_nif_return] end. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - not_run(Config) when is_list(Config) -> {skipped,"Native code"}. %% Test tracing NIFs. trace_nif(Config) when is_list(Config) -> load_nif(Config), - + do_trace_nif([]). %% Test tracing NIFs with local flag. @@ -78,69 +61,69 @@ trace_nif_local(Config) when is_list(Config) -> %% Test tracing NIFs with meta flag. trace_nif_meta(Config) when is_list(Config) -> load_nif(Config), - ?line Pid=spawn_link(?MODULE, nif_process, []), - ?line erlang:trace_pattern({?MODULE,nif,'_'}, [], [meta]), - - ?line Pid ! {apply_nif, nif, []}, - ?line receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}), - - ?line Pid ! {apply_nif, nif, ["Arg1"]}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {?MODULE,nif, ["Arg1"]}}), - - ?line Pid ! {call_nif, nif, []}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {?MODULE,nif, []}}), - - ?line Pid ! {call_nif, nif, ["Arg1"]}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {?MODULE,nif, ["Arg1"]}}), + Pid=spawn_link(?MODULE, nif_process, []), + erlang:trace_pattern({?MODULE,nif,'_'}, [], [meta]), + + Pid ! {apply_nif, nif, []}, + receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}), + + Pid ! {apply_nif, nif, ["Arg1"]}, + receive_trace_msg_ts({trace_ts,Pid,call, + {?MODULE,nif, ["Arg1"]}}), + + Pid ! {call_nif, nif, []}, + receive_trace_msg_ts({trace_ts,Pid,call, + {?MODULE,nif, []}}), + + Pid ! {call_nif, nif, ["Arg1"]}, + receive_trace_msg_ts({trace_ts,Pid,call, + {?MODULE,nif, ["Arg1"]}}), ok. do_trace_nif(Flags) -> - ?line Pid = spawn(?MODULE, nif_process, []), - ?line 1 = erlang:trace(Pid, true, [call]), - ?line erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags), - ?line Pid ! {apply_nif, nif, []}, - ?line receive_trace_msg({trace,Pid,call,{?MODULE,nif, []}}), - ?line Pid ! {apply_nif, nif, ["Arg1"]}, - ?line receive_trace_msg({trace,Pid,call,{?MODULE,nif, ["Arg1"]}}), + Pid = spawn(?MODULE, nif_process, []), + 1 = erlang:trace(Pid, true, [call]), + erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags), + Pid ! {apply_nif, nif, []}, + receive_trace_msg({trace,Pid,call,{?MODULE,nif, []}}), + Pid ! {apply_nif, nif, ["Arg1"]}, + receive_trace_msg({trace,Pid,call,{?MODULE,nif, ["Arg1"]}}), + + Pid ! {call_nif, nif, []}, + receive_trace_msg({trace, Pid, call, {?MODULE,nif, []}}), - ?line Pid ! {call_nif, nif, []}, - ?line receive_trace_msg({trace, Pid, call, {?MODULE,nif, []}}), + Pid ! {call_nif, nif, ["Arg1"]}, + receive_trace_msg({trace, Pid, call, {?MODULE,nif, ["Arg1"]}}), - ?line Pid ! {call_nif, nif, ["Arg1"]}, - ?line receive_trace_msg({trace, Pid, call, {?MODULE,nif, ["Arg1"]}}), - %% Switch off - ?line 1 = erlang:trace(Pid, false, [call]), + 1 = erlang:trace(Pid, false, [call]), - ?line Pid ! {apply_nif, nif, []}, + Pid ! {apply_nif, nif, []}, receive_nothing(), - ?line Pid ! {apply_nif, nif, ["Arg1"]}, + Pid ! {apply_nif, nif, ["Arg1"]}, receive_nothing(), - ?line Pid ! {call_nif, nif, []}, + Pid ! {call_nif, nif, []}, receive_nothing(), - ?line Pid ! {call_nif, nif, ["Arg1"]}, + Pid ! {call_nif, nif, ["Arg1"]}, receive_nothing(), %% Switch on again - ?line 1 = erlang:trace(Pid, true, [call]), - ?line erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags), - ?line Pid ! {apply_nif, nif, []}, - ?line receive_trace_msg({trace,Pid,call,{?MODULE,nif, []}}), - ?line Pid ! {apply_nif, nif, ["Arg1"]}, - ?line receive_trace_msg({trace,Pid,call,{?MODULE,nif, ["Arg1"]}}), - - ?line Pid ! {call_nif, nif, []}, - ?line receive_trace_msg({trace, Pid, call, {?MODULE,nif, []}}), - - ?line Pid ! {call_nif, nif, ["Arg1"]}, - ?line receive_trace_msg({trace, Pid, call, {?MODULE,nif, ["Arg1"]}}), - - ?line 1 = erlang:trace(Pid, false, [call]), - ?line erlang:trace_pattern({?MODULE,nif,'_'}, false, Flags), - ?line exit(Pid, die), + 1 = erlang:trace(Pid, true, [call]), + erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags), + Pid ! {apply_nif, nif, []}, + receive_trace_msg({trace,Pid,call,{?MODULE,nif, []}}), + Pid ! {apply_nif, nif, ["Arg1"]}, + receive_trace_msg({trace,Pid,call,{?MODULE,nif, ["Arg1"]}}), + + Pid ! {call_nif, nif, []}, + receive_trace_msg({trace, Pid, call, {?MODULE,nif, []}}), + + Pid ! {call_nif, nif, ["Arg1"]}, + receive_trace_msg({trace, Pid, call, {?MODULE,nif, ["Arg1"]}}), + + 1 = erlang:trace(Pid, false, [call]), + erlang:trace_pattern({?MODULE,nif,'_'}, false, Flags), + exit(Pid, die), ok. %% Test tracing NIFs with timestamps. @@ -154,65 +137,65 @@ trace_nif_timestamp_local(Config) when is_list(Config) -> do_trace_nif_timestamp([local]). do_trace_nif_timestamp(Flags) -> - ?line Pid=spawn(?MODULE, nif_process, []), - ?line 1 = erlang:trace(Pid, true, [call,timestamp]), - ?line erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags), - - ?line Pid ! {apply_nif, nif, []}, - ?line receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}), - - ?line Pid ! {apply_nif, nif, ["Arg1"]}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {?MODULE,nif, ["Arg1"]}}), - - ?line Pid ! {call_nif, nif, []}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {?MODULE,nif, []}}), - - ?line Pid ! {call_nif, nif, ["Arg1"]}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {?MODULE,nif, ["Arg1"]}}), - + Pid=spawn(?MODULE, nif_process, []), + 1 = erlang:trace(Pid, true, [call,timestamp]), + erlang:trace_pattern({?MODULE,nif,'_'}, [], Flags), + + Pid ! {apply_nif, nif, []}, + receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}), + + Pid ! {apply_nif, nif, ["Arg1"]}, + receive_trace_msg_ts({trace_ts,Pid,call, + {?MODULE,nif, ["Arg1"]}}), + + Pid ! {call_nif, nif, []}, + receive_trace_msg_ts({trace_ts,Pid,call, + {?MODULE,nif, []}}), + + Pid ! {call_nif, nif, ["Arg1"]}, + receive_trace_msg_ts({trace_ts,Pid,call, + {?MODULE,nif, ["Arg1"]}}), + %% We should be able to turn off the timestamp. - ?line 1 = erlang:trace(Pid, false, [timestamp]), - - ?line Pid ! {call_nif, nif, []}, - ?line receive_trace_msg({trace,Pid,call, - {?MODULE,nif, []}}), - - ?line Pid ! {apply_nif, nif, ["tjoho"]}, - ?line receive_trace_msg({trace,Pid,call, - {?MODULE,nif, ["tjoho"]}}), - - ?line 1 = erlang:trace(Pid, false, [call]), - ?line erlang:trace_pattern({erlang,'_','_'}, false, Flags), - - ?line exit(Pid, die), + 1 = erlang:trace(Pid, false, [timestamp]), + + Pid ! {call_nif, nif, []}, + receive_trace_msg({trace,Pid,call, + {?MODULE,nif, []}}), + + Pid ! {apply_nif, nif, ["tjoho"]}, + receive_trace_msg({trace,Pid,call, + {?MODULE,nif, ["tjoho"]}}), + + 1 = erlang:trace(Pid, false, [call]), + erlang:trace_pattern({erlang,'_','_'}, false, Flags), + + exit(Pid, die), ok. %% Test tracing NIF's with return/return_to trace. trace_nif_return(Config) when is_list(Config) -> load_nif(Config), - ?line Pid=spawn(?MODULE, nif_process, []), - ?line 1 = erlang:trace(Pid, true, [call,timestamp,return_to]), - ?line erlang:trace_pattern({?MODULE,nif,'_'}, [{'_',[],[{return_trace}]}], - [local]), - - ?line Pid ! {apply_nif, nif, []}, - ?line receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}), - ?line receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, - {?MODULE,nif,0}}), - ?line receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, - {?MODULE, nif_process,0}}), - - ?line Pid ! {call_nif, nif, ["Arg1"]}, - ?line receive_trace_msg_ts({trace_ts,Pid,call, - {?MODULE,nif, ["Arg1"]}}), - ?line receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, - {?MODULE,nif,1}}), - ?line receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, - {?MODULE, nif_process,0}}), + Pid=spawn(?MODULE, nif_process, []), + 1 = erlang:trace(Pid, true, [call,timestamp,return_to]), + erlang:trace_pattern({?MODULE,nif,'_'}, [{'_',[],[{return_trace}]}], + [local]), + + Pid ! {apply_nif, nif, []}, + receive_trace_msg_ts({trace_ts,Pid,call,{?MODULE,nif,[]}}), + receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, + {?MODULE,nif,0}}), + receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, + {?MODULE, nif_process,0}}), + + Pid ! {call_nif, nif, ["Arg1"]}, + receive_trace_msg_ts({trace_ts,Pid,call, + {?MODULE,nif, ["Arg1"]}}), + receive_trace_msg_ts_return_from({trace_ts,Pid,return_from, + {?MODULE,nif,1}}), + receive_trace_msg_ts_return_to({trace_ts,Pid,return_to, + {?MODULE, nif_process,0}}), ok. @@ -227,7 +210,7 @@ receive_trace_msg(Mess) -> end. receive_nothing() -> - ?line timeout = receive M -> M after 100 -> timeout end. + timeout = receive M -> M after 100 -> timeout end. receive_trace_msg_ts({trace_ts, Pid, call, {M,F,A}}) -> receive @@ -264,27 +247,27 @@ receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}) -> nif_process() -> receive - {apply_nif, Name, Args} -> - ?line {ok,Args} = apply(?MODULE, Name, Args); - - {call_nif, Name, []} -> - ?line {ok, []} = ?MODULE:Name(); - - {call_nif, Name, [A1]} -> - ?line {ok, [A1]} = ?MODULE:Name(A1); - - {call_nif, Name, [A1,A2]} -> - ?line {ok,[A1,A2]} = ?MODULE:Name(A1,A2); - - {call_nif, Name, [A1,A2,A3]} -> - ?line {ok,[A1,A2,A3]} = ?MODULE:Name(A1,A2,A3) + {apply_nif, Name, Args} -> + {ok,Args} = apply(?MODULE, Name, Args); + + {call_nif, Name, []} -> + {ok, []} = ?MODULE:Name(); + + {call_nif, Name, [A1]} -> + {ok, [A1]} = ?MODULE:Name(A1); + + {call_nif, Name, [A1,A2]} -> + {ok,[A1,A2]} = ?MODULE:Name(A1,A2); + + {call_nif, Name, [A1,A2,A3]} -> + {ok,[A1,A2,A3]} = ?MODULE:Name(A1,A2,A3) end, nif_process(). load_nif(Config) -> - ?line Path = proplists:get_value(data_dir, Config), - - ?line ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0). + Path = proplists:get_value(data_dir, Config), + + ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0). nif() -> @@ -292,4 +275,3 @@ nif() -> nif(A1) -> {"Stub1",[A1]}. %exit(["nif/1 stub called",A1]). - diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index 73b32fdf7d..d33f18543a 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -53,49 +53,49 @@ all() -> %% Test sending call trace messages to a port. call_trace(Config) when is_list(Config) -> case test_server:is_native(?MODULE) orelse - test_server:is_native(lists) of - true -> - {skip,"Native code"}; - false -> - ?line start_tracer(Config), - Self = self(), - ?line trace_func({lists,reverse,1}, []), - ?line trace_pid(Self, true, [call]), - ?line trace_info(Self, flags), - ?line trace_info(Self, tracer), - ?line [b,a] = lists:reverse([a,b]), - ?line expect({trace,Self,call,{lists,reverse,[[a,b]]}}), - - ?line trace_pid(Self, true, [timestamp]), - ?line trace_info(Self, flags), - ?line Huge = huge_data(), - ?line lists:reverse(Huge), - ?line expect({trace_ts,Self,call,{lists,reverse,[Huge]},ts}), - - ?line trace_pid(Self, true, [arity]), - ?line trace_info(Self, flags), - ?line [y,x] = lists:reverse([x,y]), - ?line expect({trace_ts,Self,call,{lists,reverse,1},ts}), - - ?line trace_pid(Self, false, [timestamp]), - ?line trace_info(Self, flags), - ?line [z,y,x] = lists:reverse([x,y,z]), - ?line expect({trace,Self,call,{lists,reverse,1}}), - - %% OTP-7399. Delayed sub-binary creation optimization. - ?line trace_pid(Self, false, [arity]), - ?line trace_info(Self, flags), - ?line trace_func({?MODULE,bs_sum_c,2}, [], [local]), - ?line 26 = bs_sum_c(<<3:4,5:4,7:4,11:4>>, 0), - ?line trace_func({?MODULE,bs_sum_c,2}, false, [local]), - ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<3:4,5:4,7:4,11:4>>,0]}}), - ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<5:4,7:4,11:4>>,3]}}), - ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<7:4,11:4>>,8]}}), - ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<11:4>>,15]}}), - ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<>>,26]}}), - - ?line trace_func({lists,reverse,1}, false), - ok + test_server:is_native(lists) of + true -> + {skip,"Native code"}; + false -> + start_tracer(Config), + Self = self(), + trace_func({lists,reverse,1}, []), + trace_pid(Self, true, [call]), + trace_info(Self, flags), + trace_info(Self, tracer), + [b,a] = lists:reverse([a,b]), + expect({trace,Self,call,{lists,reverse,[[a,b]]}}), + + trace_pid(Self, true, [timestamp]), + trace_info(Self, flags), + Huge = huge_data(), + lists:reverse(Huge), + expect({trace_ts,Self,call,{lists,reverse,[Huge]},ts}), + + trace_pid(Self, true, [arity]), + trace_info(Self, flags), + [y,x] = lists:reverse([x,y]), + expect({trace_ts,Self,call,{lists,reverse,1},ts}), + + trace_pid(Self, false, [timestamp]), + trace_info(Self, flags), + [z,y,x] = lists:reverse([x,y,z]), + expect({trace,Self,call,{lists,reverse,1}}), + + %% OTP-7399. Delayed sub-binary creation optimization. + trace_pid(Self, false, [arity]), + trace_info(Self, flags), + trace_func({?MODULE,bs_sum_c,2}, [], [local]), + 26 = bs_sum_c(<<3:4,5:4,7:4,11:4>>, 0), + trace_func({?MODULE,bs_sum_c,2}, false, [local]), + expect({trace,Self,call,{?MODULE,bs_sum_c,[<<3:4,5:4,7:4,11:4>>,0]}}), + expect({trace,Self,call,{?MODULE,bs_sum_c,[<<5:4,7:4,11:4>>,3]}}), + expect({trace,Self,call,{?MODULE,bs_sum_c,[<<7:4,11:4>>,8]}}), + expect({trace,Self,call,{?MODULE,bs_sum_c,[<<11:4>>,15]}}), + expect({trace,Self,call,{?MODULE,bs_sum_c,[<<>>,26]}}), + + trace_func({lists,reverse,1}, false), + ok end. bs_sum_c(<>, Acc) -> bs_sum_c(T, H+Acc); @@ -105,72 +105,72 @@ bs_sum_c(<<>>, Acc) -> Acc. %% Test the new return trace. return_trace(Config) when is_list(Config) -> case test_server:is_native(?MODULE) orelse - test_server:is_native(lists) of - true -> - {skip,"Native code"}; - false -> - ?line start_tracer(Config), - Self = self(), - MFA = {lists,reverse,1}, - - %% Plain (no timestamp, small data). - - ?line trace_func(MFA, [{['$1'],[],[{return_trace}, - {message,false}]}]), - ?line trace_pid(Self, true, [call]), - ?line trace_info(Self, flags), - ?line trace_info(Self, tracer), - ?line trace_info(MFA, match_spec), - ?line {traced,global} = trace_info(MFA, traced), - ?line [b,a] = lists:reverse([a,b]), - ?line expect({trace,Self,return_from,MFA,[b,a]}), - - %% Timestamp, huge data. - ?line trace_pid(Self, true, [timestamp]), - ?line Result = lists:reverse(huge_data()), - ?line expect({trace_ts,Self,return_from,MFA,Result,ts}), - - %% Turn off trace. - ?line trace_func(MFA, false), - ?line trace_info(MFA, match_spec), - ?line {traced,false} = trace_info(MFA, traced), - ok + test_server:is_native(lists) of + true -> + {skip,"Native code"}; + false -> + start_tracer(Config), + Self = self(), + MFA = {lists,reverse,1}, + + %% Plain (no timestamp, small data). + + trace_func(MFA, [{['$1'],[],[{return_trace}, + {message,false}]}]), + trace_pid(Self, true, [call]), + trace_info(Self, flags), + trace_info(Self, tracer), + trace_info(MFA, match_spec), + {traced,global} = trace_info(MFA, traced), + [b,a] = lists:reverse([a,b]), + expect({trace,Self,return_from,MFA,[b,a]}), + + %% Timestamp, huge data. + trace_pid(Self, true, [timestamp]), + Result = lists:reverse(huge_data()), + expect({trace_ts,Self,return_from,MFA,Result,ts}), + + %% Turn off trace. + trace_func(MFA, false), + trace_info(MFA, match_spec), + {traced,false} = trace_info(MFA, traced), + ok end. %% Test sending send trace messages to a port. send(Config) when is_list(Config) -> - ?line Tracer = start_tracer(Config), + Tracer = start_tracer(Config), Self = self(), - ?line Sender = fun_spawn(fun sender/0), - ?line trac(Sender, true, [send]), + Sender = fun_spawn(fun sender/0), + trac(Sender, true, [send]), %% Simple message, no timestamp. - ?line Bin = list_to_binary(lists:seq(1, 10)), - ?line Msg = {some_data,Bin}, + Bin = list_to_binary(lists:seq(1, 10)), + Msg = {some_data,Bin}, Sender ! {send_please,self(),Msg}, receive Msg -> ok end, - ?line expect({trace,Sender,send,Msg,Self}), + expect({trace,Sender,send,Msg,Self}), %% Timestamp. BiggerMsg = {even_bigger,Msg}, - ?line trac(Sender, true, [send,timestamp]), + trac(Sender, true, [send,timestamp]), Sender ! {send_please,self(),BiggerMsg}, receive BiggerMsg -> ok end, - ?line expect({trace_ts,Sender,send,BiggerMsg,Self,ts}), + expect({trace_ts,Sender,send,BiggerMsg,Self,ts}), %% Huge message. - ?line HugeMsg = huge_data(), + HugeMsg = huge_data(), Sender ! {send_please,self(),HugeMsg}, receive HugeMsg -> ok end, - ?line expect({trace_ts,Sender,send,HugeMsg,Self,ts}), + expect({trace_ts,Sender,send,HugeMsg,Self,ts}), %% Kill trace port and force a trace. The emulator should not crasch. - ?line unlink(Tracer), - ?line exit(Tracer, kill), + unlink(Tracer), + exit(Tracer, kill), erlang:yield(), % Make sure that port gets killed. Sender ! {send_please,Self,good_bye}, receive good_bye -> ok end, @@ -178,55 +178,55 @@ send(Config) when is_list(Config) -> %% Test sending receive traces to a port. receive_trace(Config) when is_list(Config) -> - ?line start_tracer(Config), - ?line Receiver = fun_spawn(fun receiver/0), - ?line trac(Receiver, true, ['receive']), + start_tracer(Config), + Receiver = fun_spawn(fun receiver/0), + trac(Receiver, true, ['receive']), Receiver ! {hello,world}, - ?line expect({trace,Receiver,'receive',{hello,world}}), + expect({trace,Receiver,'receive',{hello,world}}), - ?line trac(Receiver, true, ['receive',timestamp]), + trac(Receiver, true, ['receive',timestamp]), Huge = {hello,huge_data()}, Receiver ! {hello,huge_data()}, - ?line expect({trace_ts,Receiver,'receive',Huge,ts}), + expect({trace_ts,Receiver,'receive',Huge,ts}), ok. %% Tests a few process events (like getting linked). process_events(Config) when is_list(Config) -> - ?line start_tracer(Config), + start_tracer(Config), Self = self(), - ?line Receiver = fun_spawn(fun receiver/0), - ?line trac(Receiver, true, [procs]), + Receiver = fun_spawn(fun receiver/0), + trac(Receiver, true, [procs]), unlink(Receiver), %It is already linked. - ?line expect({trace,Receiver,getting_unlinked,Self}), + expect({trace,Receiver,getting_unlinked,Self}), link(Receiver), - ?line expect({trace,Receiver,getting_linked,Self}), - ?line trac(Receiver, true, [procs,timestamp]), + expect({trace,Receiver,getting_linked,Self}), + trac(Receiver, true, [procs,timestamp]), unlink(Receiver), - ?line expect({trace_ts,Receiver,getting_unlinked,Self,ts}), + expect({trace_ts,Receiver,getting_unlinked,Self,ts}), link(Receiver), - ?line expect({trace_ts,Receiver,getting_linked,Self,ts}), + expect({trace_ts,Receiver,getting_linked,Self,ts}), unlink(Receiver), - ?line expect({trace_ts,Receiver,getting_unlinked,Self,ts}), + expect({trace_ts,Receiver,getting_unlinked,Self,ts}), Huge = huge_data(), exit(Receiver, Huge), - ?line expect({trace_ts,Receiver,exit,Huge,ts}), + expect({trace_ts,Receiver,exit,Huge,ts}), ok. %% Test sending scheduling events to a port. schedule(Config) when is_list(Config) -> - ?line start_tracer(Config), - ?line Receiver = fun_spawn(fun receiver/0), - ?line trac(Receiver, true, [running]), + start_tracer(Config), + Receiver = fun_spawn(fun receiver/0), + trac(Receiver, true, [running]), Receiver ! hi, expect({trace,Receiver,in,{?MODULE,receiver,0}}), expect({trace,Receiver,out,{?MODULE,receiver,0}}), - ?line trac(Receiver, true, [running,timestamp]), + trac(Receiver, true, [running,timestamp]), Receiver ! hi_again, expect({trace_ts,Receiver,in,{?MODULE,receiver,0},ts}), @@ -235,174 +235,174 @@ schedule(Config) when is_list(Config) -> ok. run_fake_sched_test(Fun, Config) when is_function(Fun), is_list(Config) -> - ?line case catch erlang:system_info(smp_support) of - true -> - ?line {skipped, - "No need for faked schedule out/in trace messages " - "when smp support is enabled"}; - _ -> - ?line Fun(Config) - end. + case catch erlang:system_info(smp_support) of + true -> + {skipped, + "No need for faked schedule out/in trace messages " + "when smp support is enabled"}; + _ -> + Fun(Config) + end. %% Tests time compensating fake out/in scheduling. fake_schedule(Config) when is_list(Config) -> - ?line run_fake_sched_test(fun fake_schedule_test/1, Config). + run_fake_sched_test(fun fake_schedule_test/1, Config). fake_schedule_test(Config) when is_list(Config) -> - ?line Tracer = start_tracer(Config), - ?line Port = get(tracer_port), - ?line General = fun_spawn(fun general/0), + Tracer = start_tracer(Config), + Port = get(tracer_port), + General = fun_spawn(fun general/0), %% - ?line trac(General, true, [send, running]), + trac(General, true, [send, running]), %% %% Test that fake out/in scheduling is not generated unless %% both 'running' and 'timestamp' is active. - ?line [] = erlang:port_control(Port, $h, []), - ?line General ! nop, - ?line expect({trace, General, in, {?MODULE, general, 0}}), - ?line expect({trace, General, out, {?MODULE, general, 0}}), - ?line expect(), + [] = erlang:port_control(Port, $h, []), + General ! nop, + expect({trace, General, in, {?MODULE, general, 0}}), + expect({trace, General, out, {?MODULE, general, 0}}), + expect(), %% - ?line trac(General, false, [running]), - ?line trac(General, true, [timestamp]), + trac(General, false, [running]), + trac(General, true, [timestamp]), %% - ?line Ref1 = make_ref(), - ?line Msg1 = {Port, {data, term_to_binary(Ref1)}}, - ?line [] = erlang:port_control(Port, $h, []), - ?line General ! {send, Tracer, Msg1}, - ?line expect({trace_ts, General, send, Msg1, Tracer, ts}), - ?line expect(Ref1), - ?line expect(), + Ref1 = make_ref(), + Msg1 = {Port, {data, term_to_binary(Ref1)}}, + [] = erlang:port_control(Port, $h, []), + General ! {send, Tracer, Msg1}, + expect({trace_ts, General, send, Msg1, Tracer, ts}), + expect(Ref1), + expect(), %% - ?line trac(General, true, [running]), + trac(General, true, [running]), %% %% Test that fake out/in scheduling can be generated by the driver - ?line Ref2 = make_ref(), - ?line Msg2 = {Port, {data, term_to_binary(Ref2)}}, - ?line [] = erlang:port_control(Port, $h, []), - ?line General ! {send, Tracer, Msg2}, - ?line {_,_,_,_,Ts} = - expect({trace_ts, General, in, {?MODULE, general, 0}, ts}), - ?line expect({trace_ts, General, out, 0, Ts}), - ?line expect({trace_ts, General, in, 0, ts}), - ?line expect({trace_ts, General, send, Msg2, Tracer, ts}), - ?line expect(Ref2), - ?line expect({trace_ts, General, out, {?MODULE, general, 0}, ts}), - ?line expect(), + Ref2 = make_ref(), + Msg2 = {Port, {data, term_to_binary(Ref2)}}, + [] = erlang:port_control(Port, $h, []), + General ! {send, Tracer, Msg2}, + {_,_,_,_,Ts} = + expect({trace_ts, General, in, {?MODULE, general, 0}, ts}), + expect({trace_ts, General, out, 0, Ts}), + expect({trace_ts, General, in, 0, ts}), + expect({trace_ts, General, send, Msg2, Tracer, ts}), + expect(Ref2), + expect({trace_ts, General, out, {?MODULE, general, 0}, ts}), + expect(), %% %% Test that fake out/in scheduling is not generated after an %% 'out' scheduling event - ?line Ref3 = make_ref(), - ?line Msg3 = {Port, {data, term_to_binary(Ref3)}}, - ?line General ! {apply, {erlang, port_control, [Port, $h, []]}}, - ?line expect({trace_ts, General, in, {?MODULE, general, 0}, ts}), - ?line expect({trace_ts, General, out, {?MODULE, general, 0}, ts}), - ?line General ! {send, Tracer, Msg3}, - ?line expect({trace_ts, General, in, {?MODULE, general, 0}, ts}), - ?line expect({trace_ts, General, send, Msg3, Tracer, ts}), - ?line expect(Ref3), - ?line expect({trace_ts, General, out, {?MODULE, general, 0}, ts}), - ?line expect(), + Ref3 = make_ref(), + Msg3 = {Port, {data, term_to_binary(Ref3)}}, + General ! {apply, {erlang, port_control, [Port, $h, []]}}, + expect({trace_ts, General, in, {?MODULE, general, 0}, ts}), + expect({trace_ts, General, out, {?MODULE, general, 0}, ts}), + General ! {send, Tracer, Msg3}, + expect({trace_ts, General, in, {?MODULE, general, 0}, ts}), + expect({trace_ts, General, send, Msg3, Tracer, ts}), + expect(Ref3), + expect({trace_ts, General, out, {?MODULE, general, 0}, ts}), + expect(), %% ok. %% Tests fake out/in scheduling contents. fake_schedule_after_register(Config) when is_list(Config) -> - ?line run_fake_sched_test(fun fake_schedule_after_register_test/1, Config). + run_fake_sched_test(fun fake_schedule_after_register_test/1, Config). fake_schedule_after_register_test(Config) when is_list(Config) -> - ?line start_tracer(Config), - ?line Port = get(tracer_port), - ?line G1 = fun_spawn(fun general/0), - ?line G2 = fun_spawn(fun general/0), + start_tracer(Config), + Port = get(tracer_port), + G1 = fun_spawn(fun general/0), + G2 = fun_spawn(fun general/0), %% - ?line trac(G1, true, [running, timestamp, procs]), - ?line trac(G2, true, [running, timestamp]), + trac(G1, true, [running, timestamp, procs]), + trac(G2, true, [running, timestamp]), %% %% Test fake out/in scheduling after certain messages - ?line erlang:yield(), - ?line G2 ! {apply, {erlang, port_control, [Port, $h, []]}}, - ?line G2 ! {apply, {erlang, register, [fake_schedule_after_register, G1]}}, - ?line expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}), - ?line {_,_,_,_,Ts} = - expect({trace_ts, G1, register, fake_schedule_after_register, ts}), - ?line expect({trace_ts, G2, out, 0, Ts}), - ?line expect({trace_ts, G2, in, 0, ts}), - ?line expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}), - ?line expect(), + erlang:yield(), + G2 ! {apply, {erlang, port_control, [Port, $h, []]}}, + G2 ! {apply, {erlang, register, [fake_schedule_after_register, G1]}}, + expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}), + {_,_,_,_,Ts} = + expect({trace_ts, G1, register, fake_schedule_after_register, ts}), + expect({trace_ts, G2, out, 0, Ts}), + expect({trace_ts, G2, in, 0, ts}), + expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}), + expect(), %% ok. %% Tests fake out/in scheduling contents. fake_schedule_after_getting_linked(Config) when is_list(Config) -> - ?line run_fake_sched_test(fun fake_schedule_after_getting_linked_test/1, - Config). + run_fake_sched_test(fun fake_schedule_after_getting_linked_test/1, + Config). fake_schedule_after_getting_linked_test(Config) when is_list(Config) -> - ?line start_tracer(Config), - ?line Port = get(tracer_port), - ?line G1 = fun_spawn(fun general/0), - ?line G2 = fun_spawn(fun general/0), + start_tracer(Config), + Port = get(tracer_port), + G1 = fun_spawn(fun general/0), + G2 = fun_spawn(fun general/0), %% - ?line trac(G1, true, [running, timestamp, procs]), - ?line trac(G2, true, [running, timestamp]), + trac(G1, true, [running, timestamp, procs]), + trac(G2, true, [running, timestamp]), %% %% Test fake out/in scheduling after certain messages - ?line erlang:yield(), - ?line G2 ! {apply, {erlang, port_control, [Port, $h, []]}}, - ?line G2 ! {apply, {erlang, link, [G1]}}, - ?line expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}), - ?line {_,_,_,_,Ts} = - expect({trace_ts, G1, getting_linked, G2, ts}), - ?line expect({trace_ts, G2, out, 0, Ts}), - ?line expect({trace_ts, G2, in, 0, ts}), - ?line expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}), - ?line expect(), + erlang:yield(), + G2 ! {apply, {erlang, port_control, [Port, $h, []]}}, + G2 ! {apply, {erlang, link, [G1]}}, + expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}), + {_,_,_,_,Ts} = + expect({trace_ts, G1, getting_linked, G2, ts}), + expect({trace_ts, G2, out, 0, Ts}), + expect({trace_ts, G2, in, 0, ts}), + expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}), + expect(), %% ok. %% Tests fake out/in scheduling contents. fake_schedule_after_getting_unlinked(Config) when is_list(Config) -> - ?line run_fake_sched_test(fun fake_schedule_after_getting_unlinked_test/1, - Config). + run_fake_sched_test(fun fake_schedule_after_getting_unlinked_test/1, + Config). fake_schedule_after_getting_unlinked_test(Config) when is_list(Config) -> - ?line start_tracer(Config), - ?line Port = get(tracer_port), - ?line G1 = fun_spawn(fun general/0), - ?line G2 = fun_spawn(fun general/0), + start_tracer(Config), + Port = get(tracer_port), + G1 = fun_spawn(fun general/0), + G2 = fun_spawn(fun general/0), %% - ?line trac(G1, true, [running, procs]), - ?line trac(G2, true, [running, timestamp]), + trac(G1, true, [running, procs]), + trac(G2, true, [running, timestamp]), %% %% Test fake out/in scheduling after certain messages - ?line erlang:yield(), - ?line G2 ! {apply, {erlang, link, [G1]}}, - ?line G2 ! {apply, {erlang, port_control, [Port, $h, []]}}, - ?line G2 ! {apply, {erlang, unlink, [G1]}}, - ?line expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}), - ?line expect({trace, G1, getting_linked, G2}), - ?line expect({trace, G1, getting_unlinked, G2}), - ?line expect({trace_ts, G2, out, 0, ts}), - ?line expect({trace_ts, G2, in, 0, ts}), - ?line expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}), - ?line expect(), + erlang:yield(), + G2 ! {apply, {erlang, link, [G1]}}, + G2 ! {apply, {erlang, port_control, [Port, $h, []]}}, + G2 ! {apply, {erlang, unlink, [G1]}}, + expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}), + expect({trace, G1, getting_linked, G2}), + expect({trace, G1, getting_unlinked, G2}), + expect({trace_ts, G2, out, 0, ts}), + expect({trace_ts, G2, in, 0, ts}), + expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}), + expect(), %% ok. %% Test sending garbage collection events to a port. gc(Config) when is_list(Config) -> - ?line start_tracer(Config), - ?line Garber = fun_spawn(fun garber/0, [{min_heap_size, 5000}]), - ?line trac(Garber, true, [garbage_collection]), - ?line trace_info(Garber, flags), + start_tracer(Config), + Garber = fun_spawn(fun garber/0, [{min_heap_size, 5000}]), + trac(Garber, true, [garbage_collection]), + trace_info(Garber, flags), - ?line trace_info(Garber, flags), + trace_info(Garber, flags), Garber ! hi, expect({trace,Garber,gc_start,info}), expect({trace,Garber,gc_end,info}), - ?line trac(Garber, true, [garbage_collection,timestamp]), + trac(Garber, true, [garbage_collection,timestamp]), Garber ! hi, expect({trace_ts,Garber,gc_start,info,ts}), expect({trace_ts,Garber,gc_end,info,ts}), @@ -411,72 +411,72 @@ gc(Config) when is_list(Config) -> %% Test a port as default tracer. default_tracer(Config) when is_list(Config) -> - ?line Tracer = start_tracer(Config), - ?line TracerMonitor = erlang:monitor(process, Tracer), - ?line Port = get(tracer_port), + Tracer = start_tracer(Config), + TracerMonitor = erlang:monitor(process, Tracer), + Port = get(tracer_port), %% - ?line N = erlang:trace(all, true, [send, {tracer, Port}]), - ?line {flags, [send]} = erlang:trace_info(self(), flags), - ?line {tracer, Port} = erlang:trace_info(self(), tracer), - ?line {flags, [send]} = erlang:trace_info(new, flags), - ?line {tracer, Port} = erlang:trace_info(new, tracer), - ?line G1 = fun_spawn(fun general/0), - ?line {flags, [send]} = erlang:trace_info(G1, flags), - ?line {tracer, Port} = erlang:trace_info(G1, tracer), - ?line unlink(Tracer), - ?line exit(Port, done), - ?line receive - {'DOWN', TracerMonitor, process, Tracer, TracerExitReason} -> - ?line done = TracerExitReason - end, - ?line {flags, []} = erlang:trace_info(self(), flags), - ?line {tracer, []} = erlang:trace_info(self(), tracer), - ?line {flags, []} = erlang:trace_info(new, flags), - ?line {tracer, []} = erlang:trace_info(new, tracer), - ?line M = erlang:trace(all, false, [all]), - ?line {flags, []} = erlang:trace_info(self(), flags), - ?line {tracer, []} = erlang:trace_info(self(), tracer), - ?line {flags, []} = erlang:trace_info(G1, flags), - ?line {tracer, []} = erlang:trace_info(G1, tracer), - ?line G1 ! {apply,{erlang,exit,[normal]}}, - ?line io:format("~p = ~p.~n", [M, N]), - ?line M = N, + N = erlang:trace(all, true, [send, {tracer, Port}]), + {flags, [send]} = erlang:trace_info(self(), flags), + {tracer, Port} = erlang:trace_info(self(), tracer), + {flags, [send]} = erlang:trace_info(new, flags), + {tracer, Port} = erlang:trace_info(new, tracer), + G1 = fun_spawn(fun general/0), + {flags, [send]} = erlang:trace_info(G1, flags), + {tracer, Port} = erlang:trace_info(G1, tracer), + unlink(Tracer), + exit(Port, done), + receive + {'DOWN', TracerMonitor, process, Tracer, TracerExitReason} -> + done = TracerExitReason + end, + {flags, []} = erlang:trace_info(self(), flags), + {tracer, []} = erlang:trace_info(self(), tracer), + {flags, []} = erlang:trace_info(new, flags), + {tracer, []} = erlang:trace_info(new, tracer), + M = erlang:trace(all, false, [all]), + {flags, []} = erlang:trace_info(self(), flags), + {tracer, []} = erlang:trace_info(self(), tracer), + {flags, []} = erlang:trace_info(G1, flags), + {tracer, []} = erlang:trace_info(G1, tracer), + G1 ! {apply,{erlang,exit,[normal]}}, + io:format("~p = ~p.~n", [M, N]), + M = N, ok. tracer_port_crash(Config) when is_list(Config) -> case test_server:is_native(?MODULE) orelse - test_server:is_native(lists) of - true -> - {skip,"Native code"}; - false -> - Tr = start_tracer(Config), - Port = get(tracer_port), - Tracee = spawn(fun () -> - register(trace_port_linker, self()), - link(Port), - receive go -> ok end, - lists:reverse([1,b,c]), - receive die -> ok end - end), - Tr ! {unlink_tracer_port, self()}, - receive {unlinked_tracer_port, Tr} -> ok end, - port_control(Port, $c, []), %% Make port commands crash tracer port... - trace_func({lists,reverse,1}, []), - trace_pid(Tracee, true, [call]), - trace_info(Tracee, flags), - trace_info(self(), tracer), - Tracee ! go, - receive after 1000 -> ok end, - case whereis(trace_port_linker) of - undefined -> - ok; - Id -> -% erts_debug:set_internal_state(available_internal_state, true), -% erts_debug:set_internal_state(abort, {trace_port_linker, Id}) - ct:fail({trace_port_linker, Id}) - end, - undefined = process_info(Tracee), - ok + test_server:is_native(lists) of + true -> + {skip,"Native code"}; + false -> + Tr = start_tracer(Config), + Port = get(tracer_port), + Tracee = spawn(fun () -> + register(trace_port_linker, self()), + link(Port), + receive go -> ok end, + lists:reverse([1,b,c]), + receive die -> ok end + end), + Tr ! {unlink_tracer_port, self()}, + receive {unlinked_tracer_port, Tr} -> ok end, + port_control(Port, $c, []), %% Make port commands crash tracer port... + trace_func({lists,reverse,1}, []), + trace_pid(Tracee, true, [call]), + trace_info(Tracee, flags), + trace_info(self(), tracer), + Tracee ! go, + receive after 1000 -> ok end, + case whereis(trace_port_linker) of + undefined -> + ok; + Id -> + % erts_debug:set_internal_state(available_internal_state, true), + % erts_debug:set_internal_state(abort, {trace_port_linker, Id}) + ct:fail({trace_port_linker, Id}) + end, + undefined = process_info(Tracee), + ok end. %%% Help functions. @@ -492,106 +492,106 @@ huge_data(N) -> expect() -> receive - Other -> - ok = io:format("Unexpected; got ~p", [Other]), - ct:fail({unexpected, Other}) + Other -> + ok = io:format("Unexpected; got ~p", [Other]), + ct:fail({unexpected, Other}) after 200 -> - ok + ok end. expect({trace_ts,E1,E2,info,ts}=Message) -> receive - {trace_ts,E1,E2,_Info,_Ts}=MessageTs -> - ok = io:format("Expected and got ~p", [MessageTs]), - MessageTs; - Other -> - io:format("Expected ~p; got ~p", [Message,Other]), - ct:fail({unexpected,Other}) + {trace_ts,E1,E2,_Info,_Ts}=MessageTs -> + ok = io:format("Expected and got ~p", [MessageTs]), + MessageTs; + Other -> + io:format("Expected ~p; got ~p", [Message,Other]), + ct:fail({unexpected,Other}) after 5000 -> - io:format("Expected ~p; got nothing", [Message]), - ct:fail(no_trace_message) + io:format("Expected ~p; got nothing", [Message]), + ct:fail(no_trace_message) end; expect({trace,E1,E2,info}=Message) -> receive - {trace,E1,E2,_Info}=MessageTs -> - ok = io:format("Expected and got ~p", [MessageTs]), - MessageTs; - Other -> - io:format("Expected ~p; got ~p", [Message,Other]), - ct:fail({unexpected,Other}) + {trace,E1,E2,_Info}=MessageTs -> + ok = io:format("Expected and got ~p", [MessageTs]), + MessageTs; + Other -> + io:format("Expected ~p; got ~p", [Message,Other]), + ct:fail({unexpected,Other}) after 5000 -> - io:format("Expected ~p; got nothing", [Message]), - ct:fail(no_trace_message) + io:format("Expected ~p; got nothing", [Message]), + ct:fail(no_trace_message) end; expect({trace_ts,E1,E2,E3,ts}=Message) -> receive - {trace_ts,E1,E2,E3,_Ts}=MessageTs -> - ok = io:format("Expected and got ~p", [MessageTs]), - MessageTs; - Other -> - io:format("Expected ~p; got ~p", [Message,Other]), - ct:fail({unexpected,Other}) + {trace_ts,E1,E2,E3,_Ts}=MessageTs -> + ok = io:format("Expected and got ~p", [MessageTs]), + MessageTs; + Other -> + io:format("Expected ~p; got ~p", [Message,Other]), + ct:fail({unexpected,Other}) after 5000 -> - io:format("Expected ~p; got nothing", [Message]), - ct:fail(no_trace_message) + io:format("Expected ~p; got nothing", [Message]), + ct:fail(no_trace_message) end; expect({trace_ts,E1,E2,E3,E4,ts}=Message) -> receive - {trace_ts,E1,E2,E3,E4,_Ts}=MessageTs -> - ok = io:format("Expected and got ~p", [MessageTs]), - MessageTs; - Other -> - io:format("Expected ~p; got ~p", [Message,Other]), - ct:fail({unexpected,Other}) + {trace_ts,E1,E2,E3,E4,_Ts}=MessageTs -> + ok = io:format("Expected and got ~p", [MessageTs]), + MessageTs; + Other -> + io:format("Expected ~p; got ~p", [Message,Other]), + ct:fail({unexpected,Other}) after 5000 -> - io:format("Expected ~p; got nothing", [Message]), - ct:fail(no_trace_message) + io:format("Expected ~p; got nothing", [Message]), + ct:fail(no_trace_message) end; expect(Message) -> receive - Message -> - ok = io:format("Expected and got ~p", [Message]), - Message; - Other -> - io:format("Expected ~p; got ~p", [Message,Other]), - ct:fail({unexpected,Other}) + Message -> + ok = io:format("Expected and got ~p", [Message]), + Message; + Other -> + io:format("Expected ~p; got ~p", [Message,Other]), + ct:fail({unexpected,Other}) after 5000 -> - io:format("Expected ~p; got nothing", [Message]), - ct:fail(no_trace_message) + io:format("Expected ~p; got nothing", [Message]), + ct:fail(no_trace_message) end. trac(What, On, Flags0) -> Flags = [{tracer,get(tracer_port)}|Flags0], get(tracer) ! {apply,self(),{erlang,trace,[What,On,Flags]}}, Res = receive - {apply_result,Result} -> Result - end, + {apply_result,Result} -> Result + end, ok = io:format("erlang:trace(~p, ~p, ~p) -> ~p", - [What,On,Flags,Res]), + [What,On,Flags,Res]), Res. - + trace_info(What, Key) -> get(tracer) ! {apply,self(),{erlang,trace_info,[What,Key]}}, Res = receive - {apply_result,Result} -> Result - end, + {apply_result,Result} -> Result + end, ok = io:format("erlang:trace_info(~p, ~p) -> ~p", - [What,Key,Res]), + [What,Key,Res]), Res. - + trace_func(MFA, MatchProg) -> get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA,MatchProg]}}, Res = receive - {apply_result,Result} -> Result - end, + {apply_result,Result} -> Result + end, ok = io:format("erlang:trace_pattern(~p, ~p) -> ~p", [MFA,MatchProg,Res]), Res. trace_func(MFA, MatchProg, Flags) -> get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA,MatchProg,Flags]}}, Res = receive - {apply_result,Result} -> Result - end, + {apply_result,Result} -> Result + end, ok = io:format("erlang:trace_pattern(~p, ~p) -> ~p", [MFA,MatchProg,Res]), Res. @@ -599,10 +599,10 @@ trace_pid(Pid, On, Flags0) -> Flags = [{tracer,get(tracer_port)}|Flags0], get(tracer) ! {apply,self(),{erlang,trace,[Pid,On,Flags]}}, Res = receive - {apply_result,Result} -> Result - end, + {apply_result,Result} -> Result + end, ok = io:format("erlang:trace(~p, ~p, ~p) -> ~p", - [Pid,On,Flags,Res]), + [Pid,On,Flags,Res]), Res. start_tracer(Config) -> @@ -611,17 +611,17 @@ start_tracer(Config) -> Self = self(), put(tracer, fun_spawn(fun() -> tracer(Self) end)), receive - {started,Port} -> - put(tracer_port, Port) + {started,Port} -> + put(tracer_port, Port) end, get(tracer). load_driver(Dir, Driver) -> case erl_ddll:load_driver(Dir, Driver) of - ok -> ok; - {error, Error} = Res -> - io:format("~s\n", [erl_ddll:format_error(Error)]), - Res + ok -> ok; + {error, Error} = Res -> + io:format("~s\n", [erl_ddll:format_error(Error)]), + Res end. tracer(RelayTo) -> @@ -631,18 +631,18 @@ tracer(RelayTo) -> tracer_loop(RelayTo, Port) -> receive - {apply,From,{M,F,A}} -> - From ! {apply_result,apply(M, F, A)}, - tracer_loop(RelayTo, Port); - {Port,{data,Msg}} -> - RelayTo ! binary_to_term(Msg), - tracer_loop(RelayTo, Port); - {unlink_tracer_port, From} -> - unlink(Port), - From ! {unlinked_tracer_port, self()}, - tracer_loop(RelayTo, Port); - Other -> - exit({bad_message,Other}) + {apply,From,{M,F,A}} -> + From ! {apply_result,apply(M, F, A)}, + tracer_loop(RelayTo, Port); + {Port,{data,Msg}} -> + RelayTo ! binary_to_term(Msg), + tracer_loop(RelayTo, Port); + {unlink_tracer_port, From} -> + unlink(Port), + From ! {unlinked_tracer_port, self()}, + tracer_loop(RelayTo, Port); + Other -> + exit({bad_message,Other}) end. fun_spawn(Fun) -> @@ -666,43 +666,43 @@ fun_spawn(Fun, Opts) -> sender() -> receive - {send_please, To, What} -> - To ! What, - sender() + {send_please, To, What} -> + To ! What, + sender() end. %% Just consumes messages from its message queue. receiver() -> receive - _Any -> receiver() + _Any -> receiver() end. %% Does a garbage collection when it receives a message. garber() -> receive - _Any -> - lists:seq(1, 100), - erlang:garbage_collect(), - garber() + _Any -> + lists:seq(1, 100), + erlang:garbage_collect(), + garber() end. %% All-purpose process general() -> receive - {apply, {M, F, Args}} -> - erlang:apply(M, F, Args), - general(); - {send, Dest, Msg} -> - Dest ! Msg, - general(); - {call_f_1, Arg} -> - f(Arg), - general(); - nop -> - general() + {apply, {M, F, Args}} -> + erlang:apply(M, F, Args), + general(); + {send, Dest, Msg} -> + Dest ! Msg, + general(); + {call_f_1, Arg} -> + f(Arg), + general(); + nop -> + general() end. f(Arg) -> diff --git a/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl index 286fa111ee..e8537e6152 100644 --- a/erts/emulator/test/unique_SUITE.erl +++ b/erts/emulator/test/unique_SUITE.erl @@ -61,15 +61,15 @@ unique_monotonic_integer_white_box(Config) when is_list(Config) -> %% the system when moving the strict monotonic counter %% around in a non-strict monotonic way... Test = spawn(Node, - fun () -> - unique_monotonic_integer_white_box_test(TestServer, Success) - end), + fun () -> + unique_monotonic_integer_white_box_test(TestServer, Success) + end), Mon = erlang:monitor(process, Test), receive - {'DOWN', Mon, process, Test, Error} -> - ct:fail(Error); - Success -> - ok + {'DOWN', Mon, process, Test, Error} -> + ct:fail(Error); + Success -> + ok end, erlang:demonitor(Mon, [flush]), stop_node(Node), @@ -77,9 +77,9 @@ unique_monotonic_integer_white_box(Config) when is_list(Config) -> set_unique_monotonic_integer_state(MinCounter, NextValue) -> true = erts_debug:set_internal_state(unique_monotonic_integer_state, - NextValue-MinCounter-1). - - + NextValue-MinCounter-1). + + unique_monotonic_integer_white_box_test(TestServer, Success) -> erts_debug:set_internal_state(available_internal_state, true), @@ -111,10 +111,10 @@ unique_monotonic_integer_white_box_test(TestServer, Success) -> ?PRINT({max_counter, MaxCounter}), case WordSize of - 4 -> - MinCounter = MinSint64; - 8 -> - MinCounter = MinSmall + 4 -> + MinCounter = MinSint64; + 8 -> + MinCounter = MinSmall end, StartState = erts_debug:get_internal_state(unique_monotonic_integer_state), @@ -122,20 +122,20 @@ unique_monotonic_integer_white_box_test(TestServer, Success) -> %% Verify that we get expected results over all internal limits... case MinCounter < MinSmall of - false -> - 8 = WordSize, - ok; - true -> - 4 = WordSize, - ?PRINT(over_min_small), - set_unique_monotonic_integer_state(MinCounter, MinSmall-2), - true = (?P(erlang:unique_integer([monotonic])) == MinSmall - 2), - true = (?P(erlang:unique_integer([monotonic])) == MinSmall - 1), - true = (?P(erlang:unique_integer([monotonic])) == MinSmall), - true = (?P(erlang:unique_integer([monotonic])) == MinSmall + 1), - true = (?P(erlang:unique_integer([monotonic])) == MinSmall + 2), - garbage_collect(), - ok + false -> + 8 = WordSize, + ok; + true -> + 4 = WordSize, + ?PRINT(over_min_small), + set_unique_monotonic_integer_state(MinCounter, MinSmall-2), + true = (?P(erlang:unique_integer([monotonic])) == MinSmall - 2), + true = (?P(erlang:unique_integer([monotonic])) == MinSmall - 1), + true = (?P(erlang:unique_integer([monotonic])) == MinSmall), + true = (?P(erlang:unique_integer([monotonic])) == MinSmall + 1), + true = (?P(erlang:unique_integer([monotonic])) == MinSmall + 2), + garbage_collect(), + ok end, ?PRINT(over_zero), %% Not really an interesting limit, but... @@ -157,27 +157,27 @@ unique_monotonic_integer_white_box_test(TestServer, Success) -> garbage_collect(), case MaxCounter > MaxSint64 of - false -> - 4 = WordSize, - ok; - true -> - 8 = WordSize, - ?PRINT(over_max_sint64), - set_unique_monotonic_integer_state(MinCounter, MaxSint64-2), - true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 - 2), - true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 - 1), - true = (?P(erlang:unique_integer([monotonic])) == MaxSint64), - true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 + 1), - true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 + 2), - garbage_collect() + false -> + 4 = WordSize, + ok; + true -> + 8 = WordSize, + ?PRINT(over_max_sint64), + set_unique_monotonic_integer_state(MinCounter, MaxSint64-2), + true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 - 2), + true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 - 1), + true = (?P(erlang:unique_integer([monotonic])) == MaxSint64), + true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 + 1), + true = (?P(erlang:unique_integer([monotonic])) == MaxSint64 + 2), + garbage_collect() end, ?PRINT(over_max_min_counter), set_unique_monotonic_integer_state(MinCounter, if MaxCounter == MaxSint64 -> - MaxCounter-2; - true -> - MinCounter-3 - end), + MaxCounter-2; + true -> + MinCounter-3 + end), true = (?P(erlang:unique_integer([monotonic])) == MaxCounter - 2), true = (?P(erlang:unique_integer([monotonic])) == MaxCounter - 1), true = (?P(erlang:unique_integer([monotonic])) == MaxCounter), @@ -189,7 +189,7 @@ unique_monotonic_integer_white_box_test(TestServer, Success) -> %% Restore initial state and hope we didn't mess it up for the %% system... true = erts_debug:set_internal_state(unique_monotonic_integer_state, - StartState), + StartState), TestServer ! Success. @@ -200,16 +200,16 @@ unique_monotonic_integer_white_box_test(TestServer, Success) -> %% -record(uniqint_info, {min_int, - max_int, - max_small, - schedulers, - sched_bits}). + max_int, + max_small, + schedulers, + sched_bits}). unique_integer_white_box(Config) when is_list(Config) -> UinqintInfo = init_uniqint_info(), #uniqint_info{min_int = MinInt, - max_int = MaxInt, - max_small = MaxSmall} = UinqintInfo, + max_int = MaxInt, + max_small = MaxSmall} = UinqintInfo, io:format("****************************************************~n", []), io:format("*** Around MIN_UNIQ_INT ~p ***~n", [MinInt]), io:format("****************************************************~n", []), @@ -239,7 +239,7 @@ unique_integer_white_box(Config) when is_list(Config) -> io:format("****************************************************~n", []), check_unique_integer_around(MaxInt, UinqintInfo), ok. - + %%% Internal unique_integer_white_box/1 test case @@ -251,12 +251,12 @@ calc_sched_bits(NoScheds, Shift) -> schedulers() -> S = erlang:system_info(schedulers), try - DCPUS = erlang:system_info(dirty_cpu_schedulers), - DIOS = erlang:system_info(dirty_io_schedulers), - S+DCPUS+DIOS + DCPUS = erlang:system_info(dirty_cpu_schedulers), + DIOS = erlang:system_info(dirty_io_schedulers), + S+DCPUS+DIOS catch - _ : _ -> - S + _ : _ -> + S end. init_uniqint_info() -> @@ -273,33 +273,33 @@ init_uniqint_info() -> MaxInt = ((((1 bsl 64) - 1) bsl SchedBits) bor Schedulers) + MinSmall, io:format("MaxInt=~p~n", [MaxInt]), #uniqint_info{min_int = MinSmall, - max_int = MaxInt, - max_small = MaxSmall, - schedulers = Schedulers, - sched_bits = SchedBits}. + max_int = MaxInt, + max_small = MaxSmall, + schedulers = Schedulers, + sched_bits = SchedBits}. valid_uniqint(Int, #uniqint_info{min_int = MinInt} = UinqintInfo) when Int < MinInt -> valid_uniqint(MinInt, UinqintInfo); valid_uniqint(Int, #uniqint_info{min_int = MinInt, - sched_bits = SchedBits, - schedulers = Scheds}) -> + sched_bits = SchedBits, + schedulers = Scheds}) -> Int1 = Int - MinInt, {Inc, ThreadNo} = case Int1 band ((1 bsl SchedBits) - 1) of - TN when TN > Scheds -> - {1, Scheds}; - TN -> - {0, TN} - end, + TN when TN > Scheds -> + {1, Scheds}; + TN -> + {0, TN} + end, Counter = ((Int1 bsr SchedBits) + Inc) rem (1 bsl 64), ((Counter bsl SchedBits) bor ThreadNo) + MinInt. smaller_valid_uniqint(Int, UinqintInfo) -> Cand = Int-1, case valid_uniqint(Cand, UinqintInfo) of - RI when RI < Int -> - RI; - _ -> - smaller_valid_uniqint(Cand, UinqintInfo) + RI when RI < Int -> + RI; + _ -> + smaller_valid_uniqint(Cand, UinqintInfo) end. int32_to_bigendian_list(Int) -> @@ -310,7 +310,7 @@ int32_to_bigendian_list(Int) -> Int band 16#ff]. mk_uniqint(Int, #uniqint_info {min_int = MinInt, - sched_bits = SchedBits} = _UinqintInfo) -> + sched_bits = SchedBits} = _UinqintInfo) -> Int1 = Int - MinInt, ThrId = Int1 band ((1 bsl SchedBits) - 1), Value = (Int1 bsr SchedBits) band ((1 bsl 64) - 1), @@ -326,36 +326,36 @@ check_uniqint(Int, UinqintInfo) -> UniqInt = mk_uniqint(Int, UinqintInfo), io:format("UniqInt=~p ", [UniqInt]), case UniqInt =:= Int of - true -> - io:format("OK~n~n", []); - false -> - io:format("result Int=~p FAILED~n", [Int]), - exit(badres) + true -> + io:format("OK~n~n", []); + false -> + io:format("result Int=~p FAILED~n", [Int]), + exit(badres) end. check_unique_integer_around(Int, #uniqint_info{min_int = MinInt, - max_int = MaxInt} = UinqintInfo) -> + max_int = MaxInt} = UinqintInfo) -> {Start, End} = case {Int =< MinInt+100, Int >= MaxInt-100} of - {true, false} -> - {MinInt, MinInt+100}; - {false, false} -> - {smaller_valid_uniqint(Int-100, UinqintInfo), - valid_uniqint(Int+100, UinqintInfo)}; - {false, true} -> - {MaxInt-100, MaxInt} - end, + {true, false} -> + {MinInt, MinInt+100}; + {false, false} -> + {smaller_valid_uniqint(Int-100, UinqintInfo), + valid_uniqint(Int+100, UinqintInfo)}; + {false, true} -> + {MaxInt-100, MaxInt} + end, lists:foldl(fun (I, OldRefInt) -> - RefInt = valid_uniqint(I, UinqintInfo), - case OldRefInt =:= RefInt of - true -> - ok; - false -> - check_uniqint(RefInt, UinqintInfo) - end, - RefInt - end, - none, - lists:seq(Start, End)). + RefInt = valid_uniqint(I, UinqintInfo), + case OldRefInt =:= RefInt of + true -> + ok; + false -> + check_uniqint(RefInt, UinqintInfo) + end, + RefInt + end, + none, + lists:seq(Start, End)). %% helpers @@ -367,17 +367,17 @@ print_ret_val(File, Line, Value) -> start_node(Config) -> start_node(Config, []). start_node(Config, Opts) when is_list(Config), is_list(Opts) -> - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line A = erlang:monotonic_time(1) + erlang:time_offset(1), - ?line B = erlang:unique_integer([positive]), - ?line Name = list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ atom_to_list(proplists:get_value(testcase, Config)) - ++ "-" - ++ integer_to_list(A) - ++ "-" - ++ integer_to_list(B)), - ?line test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). + Pa = filename:dirname(code:which(?MODULE)), + A = erlang:monotonic_time(1) + erlang:time_offset(1), + B = erlang:unique_integer([positive]), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(proplists:get_value(testcase, Config)) + ++ "-" + ++ integer_to_list(A) + ++ "-" + ++ integer_to_list(B)), + test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). stop_node(Node) -> test_server:stop_node(Node). diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index 755fa5ea99..93a03e8f91 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -52,97 +52,95 @@ all() -> %% Tests that all schedulers are actually used schedulers_alive(Config) when is_list(Config) -> - ?line Master = self(), - ?line NoSchedulersOnline = erlang:system_flag( - schedulers_online, - erlang:system_info(schedulers)), - ?line NoSchedulers = erlang:system_info(schedulers), + Master = self(), + NoSchedulersOnline = erlang:system_flag( + schedulers_online, + erlang:system_info(schedulers)), + NoSchedulers = erlang:system_info(schedulers), UsedScheds = - try - ?line io:format("Number of schedulers configured: ~p~n", [NoSchedulers]), - ?line case erlang:system_info(multi_scheduling) of - blocked -> - ?line ct:fail(multi_scheduling_blocked); - disabled -> - ?line ok; - enabled -> - io:format("Testing blocking process exit~n"), - BF = fun () -> - blocked = erlang:system_flag(multi_scheduling, - block), - Master ! {self(), blocking}, - receive after infinity -> ok end - end, - ?line Blocker = spawn_link(BF), - ?line Mon = erlang:monitor(process, Blocker), - ?line receive {Blocker, blocking} -> ok end, - ?line [Blocker] - = erlang:system_info(multi_scheduling_blockers), - ?line unlink(Blocker), - ?line exit(Blocker, kill), - ?line receive {'DOWN', Mon, _, _, _} -> ok end, - ?line enabled = erlang:system_info(multi_scheduling), - ?line [] = erlang:system_info(multi_scheduling_blockers), - ?line ok - end, - io:format("Testing blocked~n"), - ?line erlang:system_flag(multi_scheduling, block), - ?line case erlang:system_info(multi_scheduling) of - enabled -> - ?line ct:fail(multi_scheduling_enabled); - blocked -> - ?line [Master] = erlang:system_info(multi_scheduling_blockers); - disabled -> ?line ok - end, - ?line Ps = lists:map( - fun (_) -> - spawn_link(fun () -> - run_on_schedulers(none, - [], - Master) - end) - end, - lists:seq(1,NoSchedulers)), - ?line receive after 1000 -> ok end, - ?line {_, 1} = verify_all_schedulers_used({[],0}, 1), - ?line lists:foreach(fun (P) -> - unlink(P), - exit(P, bang) - end, - Ps), - ?line case erlang:system_flag(multi_scheduling, unblock) of - blocked -> ?line ct:fail(multi_scheduling_blocked); - disabled -> ?line ok; - enabled -> ?line ok - end, - erts_debug:set_internal_state(available_internal_state, true), - %% node_and_dist_references will use emulator interal thread blocking... - erts_debug:get_internal_state(node_and_dist_references), - erts_debug:set_internal_state(available_internal_state, false), - io:format("Testing not blocked~n"), - ?line Ps2 = lists:map( - fun (_) -> - spawn_link(fun () -> - run_on_schedulers(none, - [], - Master) - end) - end, - lists:seq(1,NoSchedulers)), - ?line receive after 1000 -> ok end, - ?line {_, NoSIDs} = verify_all_schedulers_used({[],0},NoSchedulers), - ?line lists:foreach(fun (P) -> - unlink(P), - exit(P, bang) - end, - Ps2), - NoSIDs - after - NoSchedulers = erlang:system_flag(schedulers_online, - NoSchedulersOnline), - NoSchedulersOnline = erlang:system_info(schedulers_online) - end, - ?line {comment, "Number of schedulers " ++ integer_to_list(UsedScheds)}. + try + io:format("Number of schedulers configured: ~p~n", [NoSchedulers]), + case erlang:system_info(multi_scheduling) of + blocked -> + ct:fail(multi_scheduling_blocked); + disabled -> + ok; + enabled -> + io:format("Testing blocking process exit~n"), + BF = fun () -> + blocked = erlang:system_flag(multi_scheduling, + block), + Master ! {self(), blocking}, + receive after infinity -> ok end + end, + Blocker = spawn_link(BF), + Mon = erlang:monitor(process, Blocker), + receive {Blocker, blocking} -> ok end, + [Blocker] + = erlang:system_info(multi_scheduling_blockers), + unlink(Blocker), + exit(Blocker, kill), + receive {'DOWN', Mon, _, _, _} -> ok end, + enabled = erlang:system_info(multi_scheduling), + [] = erlang:system_info(multi_scheduling_blockers), + ok + end, + io:format("Testing blocked~n"), + erlang:system_flag(multi_scheduling, block), + case erlang:system_info(multi_scheduling) of + enabled -> + ct:fail(multi_scheduling_enabled); + blocked -> + [Master] = erlang:system_info(multi_scheduling_blockers); + disabled -> ok + end, + Ps = lists:map( + fun (_) -> + spawn_link(fun () -> + run_on_schedulers(none, + [], + Master) + end) + end, + lists:seq(1,NoSchedulers)), + receive after 1000 -> ok end, + {_, 1} = verify_all_schedulers_used({[],0}, 1), + lists:foreach(fun (P) -> + unlink(P), + exit(P, bang) + end, Ps), + case erlang:system_flag(multi_scheduling, unblock) of + blocked -> ct:fail(multi_scheduling_blocked); + disabled -> ok; + enabled -> ok + end, + erts_debug:set_internal_state(available_internal_state, true), + %% node_and_dist_references will use emulator interal thread blocking... + erts_debug:get_internal_state(node_and_dist_references), + erts_debug:set_internal_state(available_internal_state, false), + io:format("Testing not blocked~n"), + Ps2 = lists:map( + fun (_) -> + spawn_link(fun () -> + run_on_schedulers(none, + [], + Master) + end) + end, + lists:seq(1,NoSchedulers)), + receive after 1000 -> ok end, + {_, NoSIDs} = verify_all_schedulers_used({[],0},NoSchedulers), + lists:foreach(fun (P) -> + unlink(P), + exit(P, bang) + end, Ps2), + NoSIDs + after + NoSchedulers = erlang:system_flag(schedulers_online, + NoSchedulersOnline), + NoSchedulersOnline = erlang:system_info(schedulers_online) + end, + {comment, "Number of schedulers " ++ integer_to_list(UsedScheds)}. run_on_schedulers(LastSID, SIDs, ReportTo) -> @@ -175,50 +173,50 @@ wait_on_used_scheduler({SIDs, SIDsLen} = State) -> end. verify_all_schedulers_used({UsedSIDs, UsedSIDsLen} = State, NoSchedulers) -> - ?line case NoSchedulers of + case NoSchedulers of UsedSIDsLen -> - ?line State; + State; NoSchdlrs when NoSchdlrs < UsedSIDsLen -> - ?line ct:fail({more_schedulers_used_than_exist, + ct:fail({more_schedulers_used_than_exist, {existing_schedulers, NoSchdlrs}, {used_schedulers, UsedSIDsLen}, {used_scheduler_ids, UsedSIDs}}); _ -> - ?line NewState = wait_on_used_scheduler(State), - ?line verify_all_schedulers_used(NewState, NoSchedulers) + NewState = wait_on_used_scheduler(State), + verify_all_schedulers_used(NewState, NoSchedulers) end. node_container_refc_check(Config) when is_list(Config) -> - ?line node_container_SUITE:node_container_refc_check(node()), - ?line ok. + node_container_SUITE:node_container_refc_check(node()), + ok. long_timers(Config) when is_list(Config) -> - ?line ok = long_timers_test:check_result(). + ok = long_timers_test:check_result(). pollset_size(Config) when is_list(Config) -> - ?line Name = pollset_size_testcase_initial_state_holder, - ?line Mon = erlang:monitor(process, Name), - ?line (catch Name ! {get_initial_check_io_result, self()}), - ?line InitChkIo = receive + Name = pollset_size_testcase_initial_state_holder, + Mon = erlang:monitor(process, Name), + (catch Name ! {get_initial_check_io_result, self()}), + InitChkIo = receive {initial_check_io_result, ICIO} -> - ?line erlang:demonitor(Mon, [flush]), - ?line ICIO; + erlang:demonitor(Mon, [flush]), + ICIO; {'DOWN', Mon, _, _, Reason} -> - ?line ct:fail({non_existing, Name, Reason}) + ct:fail({non_existing, Name, Reason}) end, - ?line FinChkIo = get_check_io_info(), - ?line io:format("Initial: ~p~nFinal: ~p~n", [InitChkIo, FinChkIo]), - ?line InitPollsetSize = lists:keysearch(total_poll_set_size, 1, InitChkIo), - ?line FinPollsetSize = lists:keysearch(total_poll_set_size, 1, FinChkIo), + FinChkIo = get_check_io_info(), + io:format("Initial: ~p~nFinal: ~p~n", [InitChkIo, FinChkIo]), + InitPollsetSize = lists:keysearch(total_poll_set_size, 1, InitChkIo), + FinPollsetSize = lists:keysearch(total_poll_set_size, 1, FinChkIo), HasGethost = case has_gethost() of true -> 1; _ -> 0 end, - ?line case InitPollsetSize =:= FinPollsetSize of + case InitPollsetSize =:= FinPollsetSize of true -> case InitPollsetSize of {value, {total_poll_set_size, Size}} -> - ?line {comment, + {comment, "Pollset size: " ++ integer_to_list(Size)}; _ -> - ?line {skipped, + {skipped, "Pollset size information not available"} end; false -> @@ -227,13 +225,13 @@ pollset_size(Config) when is_list(Config) -> %% that is ok as long as there are at least 2 %% descriptors (dist listen socket and %% epmd socket) in the pollset. - ?line {value, {total_poll_set_size, InitSize}} + {value, {total_poll_set_size, InitSize}} = InitPollsetSize, - ?line {value, {total_poll_set_size, FinSize}} + {value, {total_poll_set_size, FinSize}} = FinPollsetSize, - ?line true = FinSize < (InitSize + HasGethost), - ?line true = 2 =< FinSize, - ?line {comment, + true = FinSize < (InitSize + HasGethost), + true = 2 =< FinSize, + {comment, "Start pollset size: " ++ integer_to_list(InitSize) ++ " End pollset size: " @@ -241,9 +239,9 @@ pollset_size(Config) when is_list(Config) -> end. check_io_debug(Config) when is_list(Config) -> - ?line case lists:keysearch(name, 1, erlang:system_info(check_io)) of - {value, {name, erts_poll}} -> ?line check_io_debug_test(); - _ -> ?line {skipped, "Not implemented in this emulator"} + case lists:keysearch(name, 1, erlang:system_info(check_io)) of + {value, {name, erts_poll}} -> check_io_debug_test(); + _ -> {skipped, "Not implemented in this emulator"} end. check_io_debug_test() -> @@ -266,8 +264,8 @@ check_io_debug_test() -> %% port returns an EAGAIN ok end, - ?line 0 = NoDrvEvStructs, - ?line ok. + 0 = NoDrvEvStructs, + ok. has_gethost() -> has_gethost(erlang:ports()). @@ -313,6 +311,3 @@ get_check_io_info() -> receive after 100 -> ok end, get_check_io_info() end. - - - -- cgit v1.2.3 From 7358c113e9cbcfc2ce722169801b1cb2f9005922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 10 Mar 2016 20:12:22 +0100 Subject: Cleaner map_SUITE --- erts/emulator/test/map_SUITE.erl | 266 +++++++++++++++++---------------------- 1 file changed, 116 insertions(+), 150 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 5e9814be60..956b82335c 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -17,73 +17,70 @@ %% %CopyrightEnd% %% -module(map_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2 - ]). - --export([ - t_build_and_match_literals/1, t_build_and_match_literals_large/1, - t_update_literals/1, t_update_literals_large/1, - t_match_and_update_literals/1, t_match_and_update_literals_large/1, - t_update_map_expressions/1, - t_update_assoc/1, t_update_assoc_large/1, - t_update_exact/1, t_update_exact_large/1, - t_guard_bifs/1, - t_guard_sequence/1, t_guard_sequence_large/1, - t_guard_update/1, t_guard_update_large/1, - t_guard_receive/1, t_guard_receive_large/1, - t_guard_fun/1, - t_update_deep/1, - t_list_comprehension/1, - t_map_sort_literals/1, - t_map_equal/1, - t_map_compare/1, - t_map_size/1, - t_is_map/1, - - %% Specific Map BIFs - t_bif_map_get/1, - t_bif_map_find/1, - t_bif_map_is_key/1, - t_bif_map_keys/1, - t_bif_map_merge/1, - t_bif_map_new/1, - t_bif_map_put/1, - t_bif_map_remove/1, - t_bif_map_update/1, - t_bif_map_values/1, - t_bif_map_to_list/1, - t_bif_map_from_list/1, - - %% erlang - t_erlang_hash/1, - t_map_encode_decode/1, - t_gc_rare_map_overflow/1, - - %% non specific BIF related - t_bif_build_and_check/1, - t_bif_merge_and_check/1, - - %% maps module not bifs - t_maps_fold/1, - t_maps_map/1, - t_maps_size/1, - t_maps_without/1, - - %% misc - t_hashmap_balance/1, - t_erts_internal_order/1, - t_erts_internal_hash/1, - t_pdict/1, - t_ets/1, - t_dets/1, - t_tracing/1, - - %% instruction-level tests - t_has_map_fields/1, - y_regs/1, - badmap_17/1 - ]). +-export([all/0, suite/0]). +-compile({nowarn_deprecated_function, {erlang,hash,2}}). + +-export([t_build_and_match_literals/1, t_build_and_match_literals_large/1, + t_update_literals/1, t_update_literals_large/1, + t_match_and_update_literals/1, t_match_and_update_literals_large/1, + t_update_map_expressions/1, + t_update_assoc/1, t_update_assoc_large/1, + t_update_exact/1, t_update_exact_large/1, + t_guard_bifs/1, + t_guard_sequence/1, t_guard_sequence_large/1, + t_guard_update/1, t_guard_update_large/1, + t_guard_receive/1, t_guard_receive_large/1, + t_guard_fun/1, + t_update_deep/1, + t_list_comprehension/1, + t_map_sort_literals/1, + t_map_equal/1, + t_map_compare/1, + t_map_size/1, + t_is_map/1, + + %% Specific Map BIFs + t_bif_map_get/1, + t_bif_map_find/1, + t_bif_map_is_key/1, + t_bif_map_keys/1, + t_bif_map_merge/1, + t_bif_map_new/1, + t_bif_map_put/1, + t_bif_map_remove/1, + t_bif_map_update/1, + t_bif_map_values/1, + t_bif_map_to_list/1, + t_bif_map_from_list/1, + + %% erlang + t_erlang_hash/1, + t_map_encode_decode/1, + t_gc_rare_map_overflow/1, + + %% non specific BIF related + t_bif_build_and_check/1, + t_bif_merge_and_check/1, + + %% maps module not bifs + t_maps_fold/1, + t_maps_map/1, + t_maps_size/1, + t_maps_without/1, + + %% misc + t_hashmap_balance/1, + t_erts_internal_order/1, + t_erts_internal_hash/1, + t_pdict/1, + t_ets/1, + t_dets/1, + t_tracing/1, + + %% instruction-level tests + t_has_map_fields/1, + y_regs/1, + badmap_17/1]). -include_lib("stdlib/include/ms_transform.hrl"). @@ -96,65 +93,55 @@ suite() -> []. -all() -> [ - t_build_and_match_literals, t_build_and_match_literals_large, - t_update_literals, t_update_literals_large, - t_match_and_update_literals, t_match_and_update_literals_large, - t_update_map_expressions, - t_update_assoc, t_update_assoc_large, - t_update_exact, t_update_exact_large, - t_guard_bifs, - t_guard_sequence, t_guard_sequence_large, - t_guard_update, t_guard_update_large, - t_guard_receive, t_guard_receive_large, - t_guard_fun, t_list_comprehension, - t_update_deep, - t_map_equal, t_map_compare, - t_map_sort_literals, - - %% Specific Map BIFs - t_bif_map_get,t_bif_map_find,t_bif_map_is_key, - t_bif_map_keys, t_bif_map_merge, t_bif_map_new, - t_bif_map_put, - t_bif_map_remove, t_bif_map_update, - t_bif_map_values, - t_bif_map_to_list, t_bif_map_from_list, - - %% erlang - t_erlang_hash, t_map_encode_decode, - t_gc_rare_map_overflow, - t_map_size, t_is_map, - - %% non specific BIF related - t_bif_build_and_check, - t_bif_merge_and_check, - - %% maps module - t_maps_fold, t_maps_map, - t_maps_size, t_maps_without, - - - %% Other functions - t_hashmap_balance, - t_erts_internal_order, - t_erts_internal_hash, - t_pdict, - t_ets, - t_tracing, - - %% instruction-level tests - t_has_map_fields, - y_regs, - badmap_17 - ]. - -groups() -> []. - -init_per_suite(Config) -> Config. -end_per_suite(_Config) -> ok. - -init_per_group(_GroupName, Config) -> Config. -end_per_group(_GroupName, Config) -> Config. +all() -> [t_build_and_match_literals, t_build_and_match_literals_large, + t_update_literals, t_update_literals_large, + t_match_and_update_literals, t_match_and_update_literals_large, + t_update_map_expressions, + t_update_assoc, t_update_assoc_large, + t_update_exact, t_update_exact_large, + t_guard_bifs, + t_guard_sequence, t_guard_sequence_large, + t_guard_update, t_guard_update_large, + t_guard_receive, t_guard_receive_large, + t_guard_fun, t_list_comprehension, + t_update_deep, + t_map_equal, t_map_compare, + t_map_sort_literals, + + %% Specific Map BIFs + t_bif_map_get,t_bif_map_find,t_bif_map_is_key, + t_bif_map_keys, t_bif_map_merge, t_bif_map_new, + t_bif_map_put, + t_bif_map_remove, t_bif_map_update, + t_bif_map_values, + t_bif_map_to_list, t_bif_map_from_list, + + %% erlang + t_erlang_hash, t_map_encode_decode, + t_gc_rare_map_overflow, + t_map_size, t_is_map, + + %% non specific BIF related + t_bif_build_and_check, + t_bif_merge_and_check, + + %% maps module + t_maps_fold, t_maps_map, + t_maps_size, t_maps_without, + + + %% Other functions + t_hashmap_balance, + t_erts_internal_order, + t_erts_internal_hash, + t_pdict, + t_ets, + t_tracing, + + %% instruction-level tests + t_has_map_fields, + y_regs, + badmap_17]. %% tests @@ -1707,7 +1694,6 @@ term_gen_recursive(Leafs, Flags, Depth) -> {K1,K2} = term_gen_recursive(Leafs, Flags, Depth+1), {V1,V2} = term_gen_recursive(Leafs, Flags, Depth+1), - %%ok = check_keys(K1,K2, 0), {maps:put(K1,V1, Acc1), maps:put(K2,V2, Acc2)} end, {maps:new(), maps:new()}, @@ -1735,26 +1721,6 @@ term_gen_recursive(Leafs, Flags, Depth) -> end end. -check_keys(K1, K2, _) when K1 =:= K2 -> - case erlang:phash3(K1) =:= erlang:phash3(K2) of - true -> ok; - false -> - io:format("Same keys with different hash values !!!\nK1 = ~p\nK2 = ~p\n", [K1,K2]), - error - end; -check_keys(K1, K2, 0) -> - case {erlang:phash3(K1), erlang:phash3(K2)} of - {H,H} -> check_keys(K1, K2, 1); - {_,_} -> ok - end; -check_keys(K1, K2, L) when L < 10 -> - case {erlang:phash3([L|K1]), erlang:phash3([L|K2])} of - {H,H} -> check_keys(K1, K2, L+1); - {_,_} -> ok - end; -check_keys(K1, K2, L) -> - io:format("Same hash value at level ~p !!!\nK1 = ~p\nK2 = ~p\n", [L,K1,K2]), - error. %% BIFs t_bif_map_get(Config) when is_list(Config) -> @@ -2993,7 +2959,7 @@ id(I) -> I. %% OTP-13146 %% Provoke major GC with a lot of "fat" maps on external format in msg queue %% causing heap fragments to be allocated. -t_gc_rare_map_overflow(Config) -> +t_gc_rare_map_overflow(Config) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), {ok, Node} = test_server:start_node(gc_rare_map_overflow, slave, [{args, "-pa \""++Pa++"\""}]), erts_debug:set_internal_state(available_internal_state, true), @@ -3013,7 +2979,7 @@ t_gc_rare_map_overflow(Config) -> unlink(Echo), %% Test fatmap in exit signal - Exiter = spawn_link(Node, fun Loop() -> receive {From,Msg} -> + Exiter = spawn_link(Node, fun Loop() -> receive {_From,Msg} -> "not_a_map" = Msg % badmatch! end, Loop() @@ -3031,7 +2997,7 @@ t_gc_rare_map_overflow(Config) -> t_gc_rare_map_overflow_do(Echo, FatMap, GcFun) -> Master = self(), - true = receive M -> false after 0 -> true end, % assert empty msg queue + true = receive _M -> false after 0 -> true end, % assert empty msg queue Echo ! {Master, token}, repeat(1000, fun(_) -> Echo ! {Master, FatMap} end, void), -- cgit v1.2.3 From b145292b2e6661014b426320941e6b5db764b595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 10 Mar 2016 20:15:05 +0100 Subject: Fix send_term_SUITE --- erts/emulator/test/send_term_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index 6f14963e9d..75161389b1 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -20,7 +20,7 @@ -module(send_term_SUITE). --export([all/0, suite/0]). +-export([all/0, suite/0, basic/1]). -export([generate_external_terms_files/1]). -- cgit v1.2.3 From ea6311e952b6e6b374c76c35b4851de98838cd25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 11 Mar 2016 15:38:50 +0100 Subject: Fix module_info_SUITE --- erts/emulator/test/module_info_SUITE.erl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index f3738c4023..f8208c5866 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -43,12 +43,10 @@ modules() -> %% Should return all functions exported from this module. (local) all_exported() -> All = add_arity(modules()), - lists:sort([{all,0},{suite,0},{groups,0}, - {init_per_suite,1},{end_per_suite,1}, - {init_per_group,2},{end_per_group,2}, - {init_per_testcase,2},{end_per_testcase,2}, - {module_info,0},{module_info,1},{native_proj,1}, - {native_filter,1}|All]). + lists:sort([{all,0},{suite,0}, + {module_info,0},{module_info,1}, + {native_proj,1}, + {native_filter,1}|All]). %% Should return all functions in this module. (local) all_functions() -> -- cgit v1.2.3 From 0f8afe80c6582f7affd17f36dc9cb48cc7946713 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 14 Mar 2016 10:46:23 +0100 Subject: Prepare release --- erts/doc/src/notes.xml | 186 +++++++++++++++++++++++++++++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 187 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index a726cc7b97..acd816a81c 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -32,6 +32,192 @@

This document describes the changes made to the ERTS application.

+
Erts 7.3 + +
Fixed Bugs and Malfunctions + + +

+ The '-path' flag to 'erl' has been documented. This flag + replaces the path specified in the boot script. It has + always existed, but was earlier only documented in SASL + (script).

+

+ Own Id: OTP-13060

+
+ +

+ The call_time tracing functionality internally + used a time based on OS system time in order to measure + call time which could cause erroneous results if OS + system time was changed during tracing.

+

+ This functionality now use Erlang monotonic time in order + to measure time. Besides fixing the erroneous results due + to OS system time being used, the results are often also + better since Erlang monotonic time often has better + accuracy and precision.

+

+ Own Id: OTP-13216

+
+ +

+ Fix behaviour of -delay_write command line switch of + epmd, which is used for debugging - in some cases epmd + was sleeping twice the requested amount of time.

+

+ Own Id: OTP-13220

+
+ +

+ Fix race between timeout and exit signal that could cause + a process to ignore the exit signal and continue + execution. Bug exist since OTP 18.0.

+

+ Own Id: OTP-13245

+
+ +

+ Fix bug in erlang:halt/1,2 for large exit status + values, causing either badarg (on 32-bit) or exit + with a crash dump and/or core dump (on 64-bit). Make + erlang:halt/1,2 tolerate any non negative integer + as exit status and truncate high order bits if the OS + does not support it.

+

+ Own Id: OTP-13251 Aux Id: ERL-49

+
+ +

+ gen_tcp:accept/2 + was not time + warp safe. This since it used the same time as + returned by erlang:now/0 + when calculating timeout. This has now been fixed.

+

+ Own Id: OTP-13254 Aux Id: OTP-11997, OTP-13222

+
+ +

+ Fix faulty error handling when writing to a compressed + file.

+

+ Own Id: OTP-13270

+
+ +

+ Fix sendfile usage for large files on FreeBSD

+

+ Own Id: OTP-13271

+
+ +

+ Fix bug that could cause + process_info(P,current_location) to crash emulator + for hipe compiled modules.

+

+ Own Id: OTP-13282 Aux Id: ERL-79

+
+ +

+ Out of memory errors have been changed to cause an exit + instead of abort.

+

+ Own Id: OTP-13292

+
+ +

+ When calling garbage_collect/[1,2] or + check_process_code/[2,3] from a process with a + higher priority than the priority of the process operated + on, the run queues could end up in an inconsistent state. + This bug has now been fixed.

+

+ Own Id: OTP-13298 Aux Id: OTP-11388

+
+ +

+ A workaround for an issue with older gcc versions (less + than 5) and inline assembly on 32-bit x86 caused an + emulator crash when it had been compiled with a newer gcc + version. An improved configure test, run when + building OTP, now detects whether the workaround should + be used or not.

+

+ Own Id: OTP-13326 Aux Id: ERL-80

+
+
+
+ + +
Improvements and New Features + + +

Introduced new statistics functionality in order to + more efficiently retrieve information about run able and + active processes and ports. For more information see:

+ statistics(total_run_queue_lengths) + statistics(run_queue_lengths) + statistics(total_active_tasks) + statistics(active_tasks) + +

+ Own Id: OTP-13201

+
+ +

+ Time warp safety improvements.

+

+ Introduced the options monotonic_timestamp, and + strict_monotonic_timestamp to the trace, + sequential trace, and system profile functionality. This + since the already existing timestamp option is not + time warp safe.

+

+ Introduced the option safe_fixed_monotonic_time to + ets:info/2 and dets:info/2. This since the + already existing safe_fixed option is not time + warp safe.

+

+ Own Id: OTP-13222 Aux Id: OTP-11997

+
+ +

+ Fix a register race where down nodes goes undetected in + epmd

+

+ Own Id: OTP-13301

+
+ +

+ Improved the gcc inline assembly implementing double word + atomic compare and exchange on x86/x86_64 so that it also + can be used when compiling with clang.

+

+ Own Id: OTP-13336

+
+ +

+ An optimization preventing a long wait for a scheduler + thread looking up information about a process executing + on another scheduler thread had unintentionally been lost + in erts-5.10 (OTP R16A). This optimization has now been + reintroduced.

+

+ Own Id: OTP-13365 Aux Id: OTP-9892

+
+
+
+ +
+
Erts 7.2.1
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index 94b5d0b169..a42b7d758e 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 7.2.1 +VSN = 7.3 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 81a7427b7c4ddf91675e6257324bbc8d609b17e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 14 Mar 2016 15:32:29 +0100 Subject: Tweak port_SUITE --- erts/emulator/test/port_SUITE.erl | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index c3ea0fb5c9..328641f5b9 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -74,7 +74,7 @@ %% --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, +-export([all/0, suite/0, groups/0, init_per_testcase/2, end_per_testcase/2, init_per_suite/1, end_per_suite/1, stream_small/1, stream_big/1, @@ -129,12 +129,6 @@ groups() -> {multiple_packets, [], [mul_basic, mul_slow_writes]}, {tps, [], [tps_16_bytes, tps_1K]}]. -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - init_per_testcase(Case, Config) -> [{testcase, Case} |Config]. @@ -914,6 +908,7 @@ try_bad_env(Env) -> %% Test that we can handle a very very large environment gracefully. huge_env(Config) when is_list(Config) -> + ct:timetrap({seconds, 30}), Vars = case os:type() of {win32,_} -> 500; _ -> -- cgit v1.2.3 From 61167468c2bedd4b6832662145272dc3f94805d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 14 Mar 2016 16:00:11 +0100 Subject: Tweak bs_construct_SUITE --- erts/emulator/test/bs_construct_SUITE.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 0a2acbd4e1..8deb0c7422 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -527,6 +527,7 @@ huge_float_check({'EXIT',{system_limit,_}}) -> ok; huge_float_check({'EXIT',{badarg,_}}) -> ok. huge_binary(Config) when is_list(Config) -> + ct:timetrap({seconds, 30}), 16777216 = size(<<0:(id(1 bsl 26)),(-1):(id(1 bsl 26))>>), garbage_collect(), {Shift,Return} = case free_mem() of -- cgit v1.2.3 From a31eab5469b7740d7aa30b3135a9b834ba958f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 4 Mar 2016 16:03:15 +0100 Subject: erts: Increase ttsl_drv logging capabilities --- erts/emulator/drivers/unix/ttsl_drv.c | 148 +++++++++++++++++++++++----------- 1 file changed, 100 insertions(+), 48 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index 25cad37e25..abfc52d3bc 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -199,7 +199,7 @@ static void my_debug_printf(char *fmt, ...) erts_vsnprintf(buffer,1024,fmt,args); va_end(args); erts_fprintf(debuglog,"%s\n",buffer); - //erts_printf("Debuglog = %s\n",buffer); + /*erts_printf("Debuglog = %s\n",buffer);*/ } #else @@ -261,15 +261,17 @@ static int ttysl_init(void) if (debuglog != NULL) setbuf(debuglog,NULL); } - DEBUGLOG(("Debuglog = %s(0x%ld)\n",dl,(long) debuglog)); + DEBUGLOG(("ttysl_init: Debuglog = %s(0x%ld)\n",dl,(long) debuglog)); } #endif + DEBUGLOG(("ttysl_init: ttysl_port = %d\n", ttysl_port)); return 0; } static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) { #ifndef HAVE_TERMCAP + DEBUGLOG(("ttysl_start: failure - no TERMCAP configured!\n")); return ERL_DRV_ERROR_GENERAL; #else char *s, *t, *l; @@ -277,36 +279,46 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) int flag; extern int using_oldshell; /* set this to let the rest of erts know */ + DEBUGLOG(("ttysl_start: driver input \"%s\", ttysl_port = %d (-1 expected)", buf, ttysl_port)); utf8buf_size = 0; - if (ttysl_port != (ErlDrvPort)-1) - return ERL_DRV_ERROR_GENERAL; + if (ttysl_port != (ErlDrvPort)-1) { + DEBUGLOG(("ttysl_start: failure with ttysl_port = %d, not initialized properly?\n", ttysl_port)); + return ERL_DRV_ERROR_GENERAL; + } - if (!isatty(0) || !isatty(1)) + DEBUGLOG(("ttysl_start: isatty(0) = %d (1 expected), isatty(1) = %d (1 expected)", isatty(0), isatty(1))); + if (!isatty(0) || !isatty(1)) { + DEBUGLOG(("ttysl_start: failure in isatty, isatty(0) = %d, isatty(1) = %d", isatty(0), isatty(1))); return ERL_DRV_ERROR_GENERAL; + } /* Set the terminal modes to default leave as is. */ canon = echo = sig = 0; /* Parse the input parameters. */ for (s = strchr(buf, ' '); s; s = t) { - s++; - /* Find end of this argument (start of next) and insert NUL. */ - if ((t = strchr(s, ' '))) { - *t = '\0'; - } - if ((flag = ((*s == '+') ? 1 : ((*s == '-') ? -1 : 0)))) { - if (s[1] == 'c') canon = flag; - if (s[1] == 'e') echo = flag; - if (s[1] == 's') sig = flag; - } - else if ((ttysl_fd = open(s, O_RDWR, 0)) < 0) - return ERL_DRV_ERROR_GENERAL; + s++; + /* Find end of this argument (start of next) and insert NUL. */ + if ((t = strchr(s, ' '))) { + *t = '\0'; + } + if ((flag = ((*s == '+') ? 1 : ((*s == '-') ? -1 : 0)))) { + if (s[1] == 'c') canon = flag; + if (s[1] == 'e') echo = flag; + if (s[1] == 's') sig = flag; + } + else if ((ttysl_fd = open(s, O_RDWR, 0)) < 0) { + DEBUGLOG(("ttysl_start: failed to open ttysl_fd, open(%s, O_RDWR, 0)) = %d\n", s, ttysl_fd)); + return ERL_DRV_ERROR_GENERAL; + } } + if (ttysl_fd < 0) ttysl_fd = 0; if (tty_init(ttysl_fd, canon, echo, sig) < 0 || - tty_set(ttysl_fd) < 0) { + tty_set(ttysl_fd) < 0) { + DEBUGLOG(("ttysl_start: failed init tty or set tty\n")); ttysl_port = (ErlDrvPort)-1; tty_reset(ttysl_fd); return ERL_DRV_ERROR_GENERAL; @@ -314,6 +326,7 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) /* Set up smart line and termcap stuff. */ if (!start_lbuf() || !start_termcap()) { + DEBUGLOG(("ttysl_start: failed to start_lbuf or start_termcap\n")); stop_lbuf(); /* Must free this */ tty_reset(ttysl_fd); return ERL_DRV_ERROR_GENERAL; @@ -335,10 +348,10 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) l = setlocale(LC_CTYPE, ""); /* Set international environment */ if (l != NULL) { utf8_mode = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0); - DEBUGLOG(("setlocale: %s\n",l)); + DEBUGLOG(("ttysl_start: setlocale: %s",l)); } #endif - DEBUGLOG(("utf8_mode is %s\n",(utf8_mode) ? "on" : "off")); + DEBUGLOG(("ttysl_start: utf8_mode is %s",(utf8_mode) ? "on" : "off")); sys_signal(SIGCONT, cont); sys_signal(SIGWINCH, winch); @@ -348,6 +361,7 @@ static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) /* we need to know this when we enter the break handler */ using_oldshell = 0; + DEBUGLOG(("ttysl_start: successful start\n")); return (ErlDrvData)ttysl_port; /* Nothing important to return */ #endif /* HAVE_TERMCAP */ } @@ -418,6 +432,7 @@ static ErlDrvSSizeT ttysl_control(ErlDrvData drv_data, static void ttysl_stop(ErlDrvData ttysl_data) { + DEBUGLOG(("ttysl_stop: ttysl_port = %d\n",ttysl_port)); if (ttysl_port != (ErlDrvPort)-1) { stop_lbuf(); stop_termcap(); @@ -617,12 +632,13 @@ static int check_buf_size(byte *s, int n) int ch; int size = 10; + DEBUGLOG(("check_buf_size: n = %d",n)); while(pos < n) { /* Indata is always UTF-8 */ if ((ch = pick_utf8(s,n,&pos)) < 0) { /* XXX temporary allow invalid chars */ ch = (int) s[pos]; - DEBUGLOG(("Invalid UTF8:%d",ch)); + DEBUGLOG(("check_buf_size: Invalid UTF8:%d",ch)); ++pos; } if (utf8_mode) { /* That is, terminal is UTF8 compliant */ @@ -630,7 +646,7 @@ static int check_buf_size(byte *s, int n) #ifdef HAVE_WCWIDTH int width; #endif - DEBUGLOG(("Printable(UTF-8:%d):%d",pos,ch)); + DEBUGLOG(("check_buf_size: Printable(UTF-8:%d):%d",pos,ch)); size++; #ifdef HAVE_WCWIDTH if ((width = wcwidth(ch)) > 1) { @@ -640,21 +656,21 @@ static int check_buf_size(byte *s, int n) } else if (ch == '\t') { size += 8; } else { - DEBUGLOG(("Magic(UTF-8:%d):%d",pos,ch)); + DEBUGLOG(("check_buf_size: Magic(UTF-8:%d):%d",pos,ch)); size += 2; } } else { if (ch <= 255 && isprint(ch)) { - DEBUGLOG(("Printable:%d",ch)); + DEBUGLOG(("check_buf_size: Printable:%d",ch)); size++; } else if (ch == '\t') size += 8; else if (ch >= 128) { - DEBUGLOG(("Non printable:%d",ch)); + DEBUGLOG(("check_buf_size: Non printable:%d",ch)); size += (octal_or_hex_positions(ch) + 1); } else { - DEBUGLOG(("Magic:%d",ch)); + DEBUGLOG(("check_buf_size: Magic:%d",ch)); size += 2; } } @@ -664,10 +680,12 @@ static int check_buf_size(byte *s, int n) lbuf_size = size + lpos + BUFSIZ; if ((lbuf = driver_realloc(lbuf, lbuf_size * sizeof(Uint32))) == NULL) { + DEBUGLOG(("check_buf_size: alloc failure of %d bytes", lbuf_size * sizeof(Uint32))); driver_failure(ttysl_port, -1); return(0); } } + DEBUGLOG(("check_buf_size: success\n")); return(1); } @@ -685,6 +703,8 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun if (lpos > MAXSIZE) put_chars((byte*)"\n", 1); + DEBUGLOG(("ttysl_from_erlang: OP = %d", buf[0])); + switch (buf[0]) { case OP_PUTC_SYNC: /* Using sync means that we have to send an ok to the @@ -695,7 +715,7 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun the port_command. */ /* fall through */ case OP_PUTC: - DEBUGLOG(("OP: Putc(%lu)",(unsigned long) count-1)); + DEBUGLOG(("ttysl_from_erlang: OP: Putc(%lu)",(unsigned long) count-1)); if (check_buf_size((byte*)buf+1, count-1) == 0) return; put_chars((byte*)buf+1, count-1); @@ -738,6 +758,7 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun ERL_DRV_USE|ERL_DRV_WRITE,1); break; } else { + DEBUGLOG(("ttysl_from_erlang: driver failure in writev(%d,..) = %d (errno = %d)\n", ttysl_fd, written, errno)); driver_failure_posix(ttysl_port, errno); return; } @@ -774,6 +795,9 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) { ErlDrvSizeT sz; iov = driver_peekq(ttysl_port,&qlen); + + DEBUGLOG(("ttysl_to_tty: qlen = %d", qlen)); + if (iov) written = writev(ttysl_fd, iov, qlen > MAXIOV ? MAXIOV : qlen); else @@ -782,6 +806,7 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) { if (errno == EINTR) { continue; } else if (errno != ERRNO_BLOCK){ + DEBUGLOG(("ttysl_to_tty: driver failure in writev(%d,..) = %d (errno = %d)\n", ttysl_fd, written, errno)); driver_failure_posix(ttysl_port, errno); } break; @@ -804,6 +829,7 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) { /* flush has been called, which means we should terminate when queue is empty. This will not send any exit message */ + DEBUGLOG(("ttysl_to_tty: ttysl_terminate normal\n")); driver_failure_atom(ttysl_port, "normal"); break; } @@ -814,6 +840,7 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) { } static void ttysl_flush_tty(ErlDrvData ttysl_data) { + DEBUGLOG(("ttysl_flush_tty: ..")); ttysl_terminate = 1; return; } @@ -834,6 +861,8 @@ static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) p += utf8buf_size; utf8buf_size = 0; } + + DEBUGLOG(("ttysl_from_tty: remainder = %d", left)); if ((i = read((int)(SWord)fd, (char *) p, left)) >= 0) { if (p != b) { @@ -847,7 +876,7 @@ static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) utf8buf_size = i -pos; memcpy(utf8buf,b+pos,utf8buf_size); } else if (ch == -1) { - DEBUGLOG(("Giving up on UTF8 mode, invalid character")); + DEBUGLOG(("ttysl_from_tty: Giving up on UTF8 mode, invalid character")); utf8_mode = 0; goto latin_terminal; } @@ -864,6 +893,7 @@ static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) } } } else { + DEBUGLOG(("ttysl_from_tty: driver failure in read(%d,..) = %d\n", (int)(SWord)fd, i)); driver_failure(ttysl_port, -1); } } @@ -1155,7 +1185,7 @@ static int write_buf(Uint32 *s, int n) byte *octbuff; byte octtmp[256]; int octbytes; - DEBUGLOG(("Escaped: %d", ch)); + DEBUGLOG(("write_buf: Escaped: %d", ch)); octbytes = octal_or_hex_positions(ch); if (octbytes > 256) { octbuff = driver_alloc(octbytes); @@ -1164,11 +1194,11 @@ static int write_buf(Uint32 *s, int n) } octbytes = 0; octal_or_hex_format(ch, octbuff, &octbytes); - DEBUGLOG(("octbytes: %d", octbytes)); + DEBUGLOG(("write_buf: octbytes: %d", octbytes)); outc('\\'); for (i = 0; i < octbytes; ++i) { outc(lastput = octbuff[i]); - DEBUGLOG(("outc: %d", (int) lastput)); + DEBUGLOG(("write_buf: outc: %d", (int) lastput)); } n -= octbytes+1; s += octbytes+1; @@ -1180,7 +1210,7 @@ static int write_buf(Uint32 *s, int n) --n; s++; #endif } else { - DEBUGLOG(("Very unexpected character %d",(int) *s)); + DEBUGLOG(("write_buf: Very unexpected character %d",(int) *s)); ++n; --s; } @@ -1242,6 +1272,9 @@ static int start_termcap(void) size_t envsz = 1024; char *env = NULL; char *c; + int tres; + + DEBUGLOG(("start_termcap: ..")); capbuf = driver_alloc(1024); if (!capbuf) @@ -1249,9 +1282,10 @@ static int start_termcap(void) eres = erl_drv_getenv("TERM", capbuf, &envsz); if (eres == 0) env = capbuf; - else if (eres < 0) + else if (eres < 0) { + DEBUGLOG(("start_termcap: failure in erl_drv_getenv(\"TERM\", ..) = %d\n", eres)); goto false; - else /* if (eres > 1) */ { + } else /* if (eres > 1) */ { char *envbuf = driver_alloc(envsz); if (!envbuf) goto false; @@ -1261,7 +1295,8 @@ static int start_termcap(void) if (eres == 0) break; newenvbuf = driver_realloc(envbuf, envsz); - if (eres < 0 || !newenvbuf) { + if (eres < 0 || !newenvbuf) { + DEBUGLOG(("start_termcap: failure in erl_drv_getenv(\"TERM\", ..) = %d or realloc buf == %p\n", eres, newenvbuf)); env = newenvbuf ? newenvbuf : envbuf; goto false; } @@ -1269,8 +1304,10 @@ static int start_termcap(void) } env = envbuf; } - if (tgetent((char*)lbuf, env) <= 0) - goto false; + if ((tres = tgetent((char*)lbuf, env)) <= 0) { + DEBUGLOG(("start_termcap: failure in tgetent(..) = %d\n", tres)); + goto false; + } if (env != capbuf) { env = NULL; driver_free(env); @@ -1286,8 +1323,11 @@ static int start_termcap(void) if (!(left = tgetflag("bs") ? "\b" : tgetstr("bc", &c))) left = "\b"; /* Can't happen - but does on Solaris 2 */ right = tgetstr("nd", &c); - if (up && down && left && right) - return TRUE; + if (up && down && left && right) { + DEBUGLOG(("start_termcap: successful start\n")); + return TRUE; + } + DEBUGLOG(("start_termcap: failed start\n")); false: if (env && env != capbuf) driver_free(env); @@ -1364,10 +1404,13 @@ static void update_cols(void) static struct termios tty_smode, tty_rmode; -static int tty_init(int fd, int canon, int echo, int sig) -{ - if (tcgetattr(fd, &tty_rmode) < 0) - return -1; +static int tty_init(int fd, int canon, int echo, int sig) { + int tres; + DEBUGLOG(("tty_init: fd = %d, canon = %d, echo = %d, sig = %d", fd, canon, echo, sig)); + if ((tres = tcgetattr(fd, &tty_rmode)) < 0) { + DEBUGLOG(("tty_init: failure in tcgetattr(%d,..) = %d\n", fd, tres)); + return -1; + } tty_smode = tty_rmode; /* Default characteristics for all usage including termcap output. */ @@ -1420,6 +1463,7 @@ static int tty_init(int fd, int canon, int echo, int sig) #endif tty_smode.c_lflag &= ~(ISIG|IEXTEN); } + DEBUGLOG(("tty_init: successful init\n")); return 0; } @@ -1430,20 +1474,25 @@ static int tty_init(int fd, int canon, int echo, int sig) static int tty_set(int fd) { - DEBUGF(("Setting tty...\n")); + int tres; + DEBUGF(("tty_set: Setting tty...\n")); - if (tcsetattr(fd, TCSANOW, &tty_smode) < 0) + if ((tres = tcsetattr(fd, TCSANOW, &tty_smode)) < 0) { + DEBUGLOG(("tty_set: failure in tcgetattr(%d,..) = %d\n", fd, tres)); return(-1); + } return(0); } static int tty_reset(int fd) /* of terminal device */ { - DEBUGF(("Resetting tty...\n")); + int tres; + DEBUGF(("tty_reset: Resetting tty...\n")); - if (tcsetattr(fd, TCSANOW, &tty_rmode) < 0) + if ((tres = tcsetattr(fd, TCSANOW, &tty_rmode)) < 0) { + DEBUGLOG(("tty_reset: failure in tcsetattr(%d,..) = %d\n", fd, tres)); return(-1); - + } return(0); } @@ -1458,6 +1507,7 @@ static int tty_reset(int fd) /* of terminal device */ static RETSIGTYPE suspend(int sig) { if (tty_reset(ttysl_fd) < 0) { + DEBUGLOG(("signal: failure in suspend(%d), can't reset tty %d\n", sig, ttysl_fd)); fprintf(stderr,"Can't reset tty \n"); exit(1); } @@ -1469,6 +1519,7 @@ static RETSIGTYPE suspend(int sig) sys_signal(sig, suspend); /* Reset signal handler */ if (tty_set(ttysl_fd) < 0) { + DEBUGLOG(("signal: failure in suspend(%d), can't set tty %d\n", sig, ttysl_fd)); fprintf(stderr,"Can't set tty raw \n"); exit(1); } @@ -1479,6 +1530,7 @@ static RETSIGTYPE suspend(int sig) static RETSIGTYPE cont(int sig) { if (tty_set(ttysl_fd) < 0) { + DEBUGLOG(("signal: failure in cont(%d), can't set tty raw %d\n", sig, ttysl_fd)); fprintf(stderr,"Can't set tty raw\n"); exit(1); } -- cgit v1.2.3 From a426da990b999ba8a44aec739e412f7f8722df24 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 26 Feb 2016 09:57:43 +0100 Subject: Fix dirty scheduler tc on windows --- erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'erts') diff --git a/erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c b/erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c index 022858c114..ab4863337f 100644 --- a/erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c +++ b/erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c @@ -1,4 +1,6 @@ +#ifndef __WIN32__ #include +#endif #include "erl_nif.h" static int @@ -15,7 +17,11 @@ static ERL_NIF_TERM dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT +#ifdef __WIN32__ + Sleep(3000); +#else sleep(3); +#endif #endif return enif_make_atom(env, "ok"); } -- cgit v1.2.3 From 3c7e658ac4c497cd154bea4178fd560561c78d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 26 Feb 2016 18:10:38 +0100 Subject: erts: Use fcntl(fd, F_FULLFSYNC) instead of fdatasync on Mac OSX The syscall fdatasync does not work as intended on Mac OSX. Both the function fsync and fdatasync now uses fcntl(fd, F_FULLFSYNC) on Mac OSX. --- erts/emulator/drivers/unix/unix_efile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index 00da48b107..ac9b681d03 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -467,7 +467,7 @@ int efile_fdatasync(Efile_error *errInfo, /* Where to return error codes. */ int fd) /* File descriptor for file to sync data. */ { -#ifdef HAVE_FDATASYNC +#if defined(HAVE_FDATASYNC) && !defined(__DARWIN__) return check_error(fdatasync(fd), errInfo); #else return efile_fsync(errInfo, fd); -- cgit v1.2.3 From 99b8c76f52558a1522f8ecc9efef7b3633656c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 15 Mar 2016 12:51:50 +0100 Subject: run_erl: Don't define _XOPEN_SOURCE on OS X On MacOS X, defining _XOPEN_SOURCE usually *removes* features from header files. Therefore, we should not set _XOPEN_SOURCE to 600 since that will remove the prototype for vsyslog(). Setting it to an empty value or not including it will ensure that the vsyslog() prototype is included. --- erts/etc/unix/run_erl.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index 44efb975ba..22302d539a 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -42,9 +42,14 @@ # include "config.h" #endif #ifdef HAVE_WORKING_POSIX_OPENPT -#ifndef _XOPEN_SOURCE -#define _XOPEN_SOURCE 600 -#endif +# ifndef _XOPEN_SOURCE + /* On OS X, we must leave _XOPEN_SOURCE undefined in order for + * the prototype of vsyslog() to be included. + */ +# ifndef __APPLE__ +# define _XOPEN_SOURCE 600 +# endif +# endif #endif #include #include -- cgit v1.2.3 From b0421f3d267a99da21466050fd5715b7869f38c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 15 Mar 2016 08:10:32 +0100 Subject: configure.in: Fix compilation error in ethread.c 3121171f56c2 made warnings for missing prototypes into compilation errors. That broke building on MacOS X, because prototypes for the pthread_getname_np() and pthread_setname_np() functions included by ethread.c are missing. The prototypes for those functions *are* located in pthread.h, but are not included if _XOPEN_SOURCE is defined. Therefore, we must undefine _XOPEN_SOURCE. 6e606582a6 is the commit that started to define _XOPEN_SOURCE on MacOS X in order to mend floating-point exceptions. Since fp exceptions again are broken and disabled by default, it is safe to remove the definition of _XOPEN_SOURCE. --- erts/configure.in | 3 --- 1 file changed, 3 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 021780ecc2..854bdbdc15 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -476,9 +476,6 @@ case $host_os in # -D_WIN32_WINNT=* from CPPFLAGS is saved in ETHR_DEFS. CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600 -DWINVER=0x0600" ;; - darwin*) - CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE" - ;; *) ;; esac -- cgit v1.2.3 From 6664eed554974336909d3ffe03f20349cc4c38fd Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Tue, 15 Mar 2016 15:19:56 +0100 Subject: update copyright-year --- erts/AUTHORS | 2 +- erts/Makefile.in | 2 +- erts/aclocal.m4 | 2 +- erts/autoconf/configure.vxworks | 2 +- erts/autoconf/vxworks/sed.general | 2 +- erts/autoconf/vxworks/sed.vxworks_cpu32 | 2 +- erts/autoconf/vxworks/sed.vxworks_ppc32 | 2 +- erts/autoconf/vxworks/sed.vxworks_ppc603 | 2 +- erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall | 2 +- erts/autoconf/vxworks/sed.vxworks_ppc860 | 2 +- erts/autoconf/vxworks/sed.vxworks_simlinux | 2 +- erts/autoconf/vxworks/sed.vxworks_simso | 2 +- erts/autoconf/vxworks/sed.vxworks_sparc | 2 +- erts/configure.in | 2 +- erts/doc/Makefile | 2 +- erts/doc/src/Makefile | 2 +- erts/doc/src/alt_dist.xml | 2 +- erts/doc/src/book.xml | 2 +- erts/doc/src/communication.xml | 2 +- erts/doc/src/crash_dump.xml | 2 +- erts/doc/src/driver.xml | 2 +- erts/doc/src/epmd.xml | 2 +- erts/doc/src/erl.xml | 2 +- erts/doc/src/erl_driver.xml | 2 +- erts/doc/src/erl_nif.xml | 2 +- erts/doc/src/erl_prim_loader.xml | 2 +- erts/doc/src/erlang.xml | 2 +- erts/doc/src/erlc.xml | 2 +- erts/doc/src/erlsrv.xml | 2 +- erts/doc/src/erts_alloc.xml | 2 +- erts/doc/src/inet_cfg.xml | 2 +- erts/doc/src/init.xml | 2 +- erts/doc/src/match_spec.xml | 2 +- erts/doc/src/notes.xml | 2 +- erts/doc/src/notes_history.xml | 2 +- erts/doc/src/part.xml | 2 +- erts/doc/src/part_notes.xml | 2 +- erts/doc/src/part_notes_history.xml | 2 +- erts/doc/src/ref_man.xml | 2 +- erts/doc/src/run_erl.xml | 2 +- erts/doc/src/start.xml | 2 +- erts/doc/src/start_erl.xml | 2 +- erts/doc/src/tty.xml | 2 +- erts/doc/src/werl.xml | 2 +- erts/doc/src/zlib.xml | 2 +- erts/emulator/Makefile | 2 +- erts/emulator/Makefile.in | 2 +- erts/emulator/beam/atom.c | 2 +- erts/emulator/beam/atom.h | 2 +- erts/emulator/beam/atom.names | 2 +- erts/emulator/beam/beam_bif_load.c | 2 +- erts/emulator/beam/beam_bp.c | 2 +- erts/emulator/beam/beam_bp.h | 2 +- erts/emulator/beam/beam_catches.c | 2 +- erts/emulator/beam/beam_catches.h | 2 +- erts/emulator/beam/beam_debug.c | 2 +- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/beam_load.c | 2 +- erts/emulator/beam/beam_load.h | 2 +- erts/emulator/beam/beam_ranges.c | 2 +- erts/emulator/beam/benchmark.c | 2 +- erts/emulator/beam/benchmark.h | 2 +- erts/emulator/beam/bif.c | 2 +- erts/emulator/beam/bif.h | 2 +- erts/emulator/beam/bif.tab | 2 +- erts/emulator/beam/big.c | 2 +- erts/emulator/beam/big.h | 2 +- erts/emulator/beam/binary.c | 2 +- erts/emulator/beam/break.c | 2 +- erts/emulator/beam/code_ix.c | 2 +- erts/emulator/beam/code_ix.h | 2 +- erts/emulator/beam/copy.c | 2 +- erts/emulator/beam/dist.c | 2 +- erts/emulator/beam/dist.h | 2 +- erts/emulator/beam/dtrace-wrapper.h | 2 +- erts/emulator/beam/elib_memmove.c | 2 +- erts/emulator/beam/erl_afit_alloc.c | 2 +- erts/emulator/beam/erl_afit_alloc.h | 2 +- erts/emulator/beam/erl_alloc.c | 2 +- erts/emulator/beam/erl_alloc.h | 2 +- erts/emulator/beam/erl_alloc.types | 2 +- erts/emulator/beam/erl_alloc_util.c | 2 +- erts/emulator/beam/erl_alloc_util.h | 2 +- erts/emulator/beam/erl_ao_firstfit_alloc.c | 2 +- erts/emulator/beam/erl_ao_firstfit_alloc.h | 2 +- erts/emulator/beam/erl_arith.c | 2 +- erts/emulator/beam/erl_async.c | 2 +- erts/emulator/beam/erl_async.h | 2 +- erts/emulator/beam/erl_bestfit_alloc.c | 2 +- erts/emulator/beam/erl_bestfit_alloc.h | 2 +- erts/emulator/beam/erl_bif_binary.c | 2 +- erts/emulator/beam/erl_bif_chksum.c | 2 +- erts/emulator/beam/erl_bif_ddll.c | 2 +- erts/emulator/beam/erl_bif_guard.c | 2 +- erts/emulator/beam/erl_bif_info.c | 2 +- erts/emulator/beam/erl_bif_lists.c | 2 +- erts/emulator/beam/erl_bif_op.c | 2 +- erts/emulator/beam/erl_bif_os.c | 2 +- erts/emulator/beam/erl_bif_port.c | 2 +- erts/emulator/beam/erl_bif_re.c | 2 +- erts/emulator/beam/erl_bif_trace.c | 2 +- erts/emulator/beam/erl_bif_unique.c | 2 +- erts/emulator/beam/erl_bif_unique.h | 2 +- erts/emulator/beam/erl_binary.h | 2 +- erts/emulator/beam/erl_bits.c | 2 +- erts/emulator/beam/erl_bits.h | 2 +- erts/emulator/beam/erl_cpu_topology.c | 2 +- erts/emulator/beam/erl_cpu_topology.h | 2 +- erts/emulator/beam/erl_db.c | 2 +- erts/emulator/beam/erl_db.h | 2 +- erts/emulator/beam/erl_db_hash.c | 2 +- erts/emulator/beam/erl_db_hash.h | 2 +- erts/emulator/beam/erl_db_tree.c | 2 +- erts/emulator/beam/erl_db_tree.h | 2 +- erts/emulator/beam/erl_db_util.c | 2 +- erts/emulator/beam/erl_db_util.h | 2 +- erts/emulator/beam/erl_debug.c | 2 +- erts/emulator/beam/erl_driver.h | 2 +- erts/emulator/beam/erl_drv_nif.h | 2 +- erts/emulator/beam/erl_drv_thread.c | 2 +- erts/emulator/beam/erl_fun.c | 2 +- erts/emulator/beam/erl_fun.h | 2 +- erts/emulator/beam/erl_gc.c | 2 +- erts/emulator/beam/erl_gc.h | 2 +- erts/emulator/beam/erl_goodfit_alloc.c | 2 +- erts/emulator/beam/erl_goodfit_alloc.h | 2 +- erts/emulator/beam/erl_hl_timer.c | 2 +- erts/emulator/beam/erl_init.c | 2 +- erts/emulator/beam/erl_instrument.c | 2 +- erts/emulator/beam/erl_instrument.h | 2 +- erts/emulator/beam/erl_lock_check.c | 2 +- erts/emulator/beam/erl_lock_check.h | 2 +- erts/emulator/beam/erl_lock_count.c | 2 +- erts/emulator/beam/erl_lock_count.h | 2 +- erts/emulator/beam/erl_map.c | 2 +- erts/emulator/beam/erl_map.h | 2 +- erts/emulator/beam/erl_math.c | 2 +- erts/emulator/beam/erl_message.c | 2 +- erts/emulator/beam/erl_message.h | 2 +- erts/emulator/beam/erl_monitors.c | 2 +- erts/emulator/beam/erl_monitors.h | 2 +- erts/emulator/beam/erl_mtrace.c | 2 +- erts/emulator/beam/erl_mtrace.h | 2 +- erts/emulator/beam/erl_nif.c | 2 +- erts/emulator/beam/erl_nif.h | 2 +- erts/emulator/beam/erl_nif_api_funcs.h | 2 +- erts/emulator/beam/erl_node_container_utils.h | 2 +- erts/emulator/beam/erl_node_tables.c | 2 +- erts/emulator/beam/erl_node_tables.h | 2 +- erts/emulator/beam/erl_port.h | 2 +- erts/emulator/beam/erl_port_task.c | 2 +- erts/emulator/beam/erl_port_task.h | 2 +- erts/emulator/beam/erl_printf_term.c | 2 +- erts/emulator/beam/erl_printf_term.h | 2 +- erts/emulator/beam/erl_process.c | 2 +- erts/emulator/beam/erl_process.h | 2 +- erts/emulator/beam/erl_process_dict.c | 2 +- erts/emulator/beam/erl_process_dict.h | 2 +- erts/emulator/beam/erl_process_dump.c | 2 +- erts/emulator/beam/erl_process_lock.c | 2 +- erts/emulator/beam/erl_process_lock.h | 2 +- erts/emulator/beam/erl_ptab.c | 2 +- erts/emulator/beam/erl_ptab.h | 2 +- erts/emulator/beam/erl_sched_spec_pre_alloc.c | 2 +- erts/emulator/beam/erl_sched_spec_pre_alloc.h | 2 +- erts/emulator/beam/erl_smp.h | 2 +- erts/emulator/beam/erl_sock.h | 2 +- erts/emulator/beam/erl_sys_driver.h | 2 +- erts/emulator/beam/erl_term.c | 2 +- erts/emulator/beam/erl_term.h | 2 +- erts/emulator/beam/erl_thr_progress.c | 2 +- erts/emulator/beam/erl_thr_progress.h | 2 +- erts/emulator/beam/erl_thr_queue.c | 2 +- erts/emulator/beam/erl_thr_queue.h | 2 +- erts/emulator/beam/erl_threads.h | 2 +- erts/emulator/beam/erl_time.h | 2 +- erts/emulator/beam/erl_time_sup.c | 2 +- erts/emulator/beam/erl_trace.c | 2 +- erts/emulator/beam/erl_trace.h | 2 +- erts/emulator/beam/erl_unicode.c | 2 +- erts/emulator/beam/erl_unicode.h | 2 +- erts/emulator/beam/erl_unicode_normalize.h | 2 +- erts/emulator/beam/erl_utils.h | 2 +- erts/emulator/beam/erl_vm.h | 2 +- erts/emulator/beam/erl_zlib.c | 2 +- erts/emulator/beam/erl_zlib.h | 2 +- erts/emulator/beam/erlang_dtrace.d | 2 +- erts/emulator/beam/error.h | 2 +- erts/emulator/beam/export.c | 2 +- erts/emulator/beam/export.h | 2 +- erts/emulator/beam/external.c | 2 +- erts/emulator/beam/external.h | 2 +- erts/emulator/beam/global.h | 2 +- erts/emulator/beam/hash.c | 2 +- erts/emulator/beam/hash.h | 2 +- erts/emulator/beam/index.c | 2 +- erts/emulator/beam/index.h | 2 +- erts/emulator/beam/io.c | 2 +- erts/emulator/beam/module.c | 2 +- erts/emulator/beam/module.h | 2 +- erts/emulator/beam/ops.tab | 2 +- erts/emulator/beam/packet_parser.c | 2 +- erts/emulator/beam/packet_parser.h | 2 +- erts/emulator/beam/register.c | 2 +- erts/emulator/beam/register.h | 2 +- erts/emulator/beam/safe_hash.c | 2 +- erts/emulator/beam/safe_hash.h | 2 +- erts/emulator/beam/sys.h | 2 +- erts/emulator/beam/time.c | 2 +- erts/emulator/beam/utils.c | 2 +- erts/emulator/beam/version.h | 2 +- erts/emulator/drivers/common/efile_drv.c | 2 +- erts/emulator/drivers/common/erl_efile.h | 2 +- erts/emulator/drivers/common/gzio.h | 2 +- erts/emulator/drivers/common/gzio_zutil.h | 2 +- erts/emulator/drivers/common/inet_drv.c | 2 +- erts/emulator/drivers/common/ram_file_drv.c | 2 +- erts/emulator/drivers/common/zlib_drv.c | 2 +- erts/emulator/drivers/unix/bin_drv.c | 2 +- erts/emulator/drivers/unix/multi_drv.c | 2 +- erts/emulator/drivers/unix/sig_drv.c | 2 +- erts/emulator/drivers/unix/ttsl_drv.c | 2 +- erts/emulator/drivers/unix/unix_efile.c | 2 +- erts/emulator/drivers/vxworks/vxworks_resolv.c | 2 +- erts/emulator/drivers/win32/registry_drv.c | 2 +- erts/emulator/drivers/win32/ttsl_drv.c | 2 +- erts/emulator/drivers/win32/win_con.c | 2 +- erts/emulator/drivers/win32/win_con.h | 2 +- erts/emulator/drivers/win32/win_efile.c | 2 +- erts/emulator/hipe/TODO | 2 +- erts/emulator/hipe/elf64ppc.x | 2 +- erts/emulator/hipe/hipe_amd64.c | 2 +- erts/emulator/hipe/hipe_amd64.h | 2 +- erts/emulator/hipe/hipe_amd64.tab | 2 +- erts/emulator/hipe/hipe_amd64_asm.m4 | 2 +- erts/emulator/hipe/hipe_amd64_bifs.m4 | 2 +- erts/emulator/hipe/hipe_amd64_gc.h | 2 +- erts/emulator/hipe/hipe_amd64_glue.S | 2 +- erts/emulator/hipe/hipe_amd64_glue.h | 2 +- erts/emulator/hipe/hipe_amd64_primops.h | 2 +- erts/emulator/hipe/hipe_arch.h | 2 +- erts/emulator/hipe/hipe_arm.c | 2 +- erts/emulator/hipe/hipe_arm.h | 2 +- erts/emulator/hipe/hipe_arm.tab | 2 +- erts/emulator/hipe/hipe_arm_asm.m4 | 2 +- erts/emulator/hipe/hipe_arm_bifs.m4 | 2 +- erts/emulator/hipe/hipe_arm_gc.h | 2 +- erts/emulator/hipe/hipe_arm_glue.S | 2 +- erts/emulator/hipe/hipe_arm_glue.h | 2 +- erts/emulator/hipe/hipe_arm_primops.h | 2 +- erts/emulator/hipe/hipe_bif0.c | 2 +- erts/emulator/hipe/hipe_bif0.h | 2 +- erts/emulator/hipe/hipe_bif0.tab | 2 +- erts/emulator/hipe/hipe_bif1.c | 2 +- erts/emulator/hipe/hipe_bif1.h | 2 +- erts/emulator/hipe/hipe_bif1.tab | 2 +- erts/emulator/hipe/hipe_bif2.c | 2 +- erts/emulator/hipe/hipe_bif2.tab | 2 +- erts/emulator/hipe/hipe_bif64.c | 2 +- erts/emulator/hipe/hipe_bif64.h | 2 +- erts/emulator/hipe/hipe_bif64.tab | 2 +- erts/emulator/hipe/hipe_bif_list.m4 | 2 +- erts/emulator/hipe/hipe_debug.c | 2 +- erts/emulator/hipe/hipe_debug.h | 2 +- erts/emulator/hipe/hipe_gbif_list.h | 2 +- erts/emulator/hipe/hipe_gc.c | 2 +- erts/emulator/hipe/hipe_gc.h | 2 +- erts/emulator/hipe/hipe_mkliterals.c | 2 +- erts/emulator/hipe/hipe_mode_switch.c | 2 +- erts/emulator/hipe/hipe_mode_switch.h | 2 +- erts/emulator/hipe/hipe_native_bif.c | 2 +- erts/emulator/hipe/hipe_native_bif.h | 2 +- erts/emulator/hipe/hipe_ops.tab | 2 +- erts/emulator/hipe/hipe_ppc.c | 2 +- erts/emulator/hipe/hipe_ppc.h | 2 +- erts/emulator/hipe/hipe_ppc.tab | 2 +- erts/emulator/hipe/hipe_ppc64.tab | 2 +- erts/emulator/hipe/hipe_ppc_asm.m4 | 2 +- erts/emulator/hipe/hipe_ppc_bifs.m4 | 2 +- erts/emulator/hipe/hipe_ppc_gc.h | 2 +- erts/emulator/hipe/hipe_ppc_glue.S | 2 +- erts/emulator/hipe/hipe_ppc_glue.h | 2 +- erts/emulator/hipe/hipe_ppc_primops.h | 2 +- erts/emulator/hipe/hipe_primops.h | 2 +- erts/emulator/hipe/hipe_process.h | 2 +- erts/emulator/hipe/hipe_risc_gc.h | 2 +- erts/emulator/hipe/hipe_risc_glue.h | 2 +- erts/emulator/hipe/hipe_risc_stack.c | 2 +- erts/emulator/hipe/hipe_signal.h | 2 +- erts/emulator/hipe/hipe_sparc.c | 2 +- erts/emulator/hipe/hipe_sparc.h | 2 +- erts/emulator/hipe/hipe_sparc.tab | 2 +- erts/emulator/hipe/hipe_sparc_asm.m4 | 2 +- erts/emulator/hipe/hipe_sparc_bifs.m4 | 2 +- erts/emulator/hipe/hipe_sparc_gc.h | 2 +- erts/emulator/hipe/hipe_sparc_glue.S | 2 +- erts/emulator/hipe/hipe_sparc_glue.h | 2 +- erts/emulator/hipe/hipe_sparc_primops.h | 2 +- erts/emulator/hipe/hipe_stack.c | 2 +- erts/emulator/hipe/hipe_stack.h | 2 +- erts/emulator/hipe/hipe_x86.c | 2 +- erts/emulator/hipe/hipe_x86.h | 2 +- erts/emulator/hipe/hipe_x86.tab | 2 +- erts/emulator/hipe/hipe_x86_abi.txt | 2 +- erts/emulator/hipe/hipe_x86_asm.m4 | 2 +- erts/emulator/hipe/hipe_x86_bifs.m4 | 2 +- erts/emulator/hipe/hipe_x86_gc.h | 2 +- erts/emulator/hipe/hipe_x86_glue.S | 2 +- erts/emulator/hipe/hipe_x86_glue.h | 2 +- erts/emulator/hipe/hipe_x86_primops.h | 2 +- erts/emulator/hipe/hipe_x86_signal.c | 2 +- erts/emulator/hipe/hipe_x86_stack.c | 2 +- erts/emulator/internal_doc/dec.erl | 2 +- erts/emulator/internal_doc/erl_ext_dist.txt | 2 +- erts/emulator/pcre/pcre.mk | 2 +- erts/emulator/sys/common/erl_check_io.c | 2 +- erts/emulator/sys/common/erl_check_io.h | 2 +- erts/emulator/sys/common/erl_mmap.c | 2 +- erts/emulator/sys/common/erl_mmap.h | 2 +- erts/emulator/sys/common/erl_mseg.c | 2 +- erts/emulator/sys/common/erl_mseg.h | 2 +- erts/emulator/sys/common/erl_mtrace_sys_wrap.c | 2 +- erts/emulator/sys/common/erl_os_monotonic_time_extender.c | 2 +- erts/emulator/sys/common/erl_poll.c | 2 +- erts/emulator/sys/common/erl_poll.h | 2 +- erts/emulator/sys/common/erl_sys_common_misc.c | 2 +- erts/emulator/sys/common/erl_util_queue.h | 2 +- erts/emulator/sys/unix/driver_int.h | 2 +- erts/emulator/sys/unix/erl_child_setup.c | 2 +- erts/emulator/sys/unix/erl_main.c | 2 +- erts/emulator/sys/unix/erl_unix_sys.h | 2 +- erts/emulator/sys/unix/erl_unix_sys_ddll.c | 2 +- erts/emulator/sys/unix/sys.c | 2 +- erts/emulator/sys/unix/sys_drivers.c | 2 +- erts/emulator/sys/unix/sys_float.c | 2 +- erts/emulator/sys/unix/sys_time.c | 2 +- erts/emulator/sys/unix/sys_uds.c | 2 +- erts/emulator/sys/unix/sys_uds.h | 2 +- erts/emulator/sys/win32/dosmap.c | 2 +- erts/emulator/sys/win32/driver_int.h | 2 +- erts/emulator/sys/win32/erl_main.c | 2 +- erts/emulator/sys/win32/erl_poll.c | 2 +- erts/emulator/sys/win32/erl_win32_sys_ddll.c | 2 +- erts/emulator/sys/win32/erl_win_dyn_driver.h | 2 +- erts/emulator/sys/win32/erl_win_sys.h | 2 +- erts/emulator/sys/win32/sys.c | 2 +- erts/emulator/sys/win32/sys_env.c | 2 +- erts/emulator/sys/win32/sys_float.c | 2 +- erts/emulator/sys/win32/sys_interrupt.c | 2 +- erts/emulator/sys/win32/sys_time.c | 2 +- erts/emulator/test/Makefile | 2 +- erts/emulator/test/a_SUITE.erl | 2 +- erts/emulator/test/after_SUITE.erl | 2 +- erts/emulator/test/alloc_SUITE.erl | 2 +- erts/emulator/test/alloc_SUITE_data/cpool.c | 2 +- erts/emulator/test/alloc_SUITE_data/migration.c | 2 +- erts/emulator/test/beam_SUITE.erl | 2 +- erts/emulator/test/beam_literals_SUITE.erl | 2 +- erts/emulator/test/bif_SUITE.erl | 2 +- erts/emulator/test/big_SUITE.erl | 2 +- erts/emulator/test/big_SUITE_data/literal_test.erl | 2 +- erts/emulator/test/binary_SUITE.erl | 2 +- erts/emulator/test/bs_bincomp_SUITE.erl | 2 +- erts/emulator/test/bs_bit_binaries_SUITE.erl | 2 +- erts/emulator/test/bs_construct_SUITE.erl | 2 +- erts/emulator/test/bs_match_bin_SUITE.erl | 2 +- erts/emulator/test/bs_match_int_SUITE.erl | 2 +- erts/emulator/test/bs_match_misc_SUITE.erl | 2 +- erts/emulator/test/bs_match_tail_SUITE.erl | 2 +- erts/emulator/test/bs_utf_SUITE.erl | 2 +- erts/emulator/test/busy_port_SUITE.erl | 2 +- erts/emulator/test/busy_port_SUITE_data/Makefile.src | 2 +- erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c | 2 +- erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c | 2 +- erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c | 2 +- erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c | 2 +- erts/emulator/test/call_trace_SUITE.erl | 2 +- erts/emulator/test/code_SUITE.erl | 2 +- erts/emulator/test/code_SUITE_data/another_code_test.erl | 2 +- erts/emulator/test/code_SUITE_data/cpbugx.erl | 2 +- erts/emulator/test/code_SUITE_data/fun_confusion.erl | 2 +- erts/emulator/test/code_SUITE_data/literals.erl | 2 +- erts/emulator/test/code_SUITE_data/many_funs.erl | 2 +- erts/emulator/test/code_SUITE_data/my_code_test.erl | 2 +- erts/emulator/test/code_SUITE_data/versions.erl | 2 +- erts/emulator/test/code_parallel_load_SUITE.erl | 2 +- erts/emulator/test/crypto_SUITE.erl | 2 +- erts/emulator/test/crypto_reference.erl | 2 +- erts/emulator/test/ddll_SUITE.erl | 2 +- erts/emulator/test/decode_packet_SUITE.erl | 2 +- erts/emulator/test/dgawd_handler.erl | 2 +- erts/emulator/test/distribution_SUITE.erl | 2 +- erts/emulator/test/distribution_SUITE_data/run.erl | 2 +- erts/emulator/test/driver_SUITE.erl | 2 +- erts/emulator/test/driver_SUITE_data/async_blast_drv.c | 2 +- erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c | 2 +- erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c | 2 +- erts/emulator/test/driver_SUITE_data/otp_9302_drv.c | 2 +- erts/emulator/test/driver_SUITE_data/thr_free_drv.c | 2 +- erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c | 2 +- erts/emulator/test/efile_SUITE.erl | 2 +- erts/emulator/test/erl_drv_thread_SUITE.erl | 2 +- erts/emulator/test/erl_link_SUITE.erl | 2 +- erts/emulator/test/erts_debug_SUITE.erl | 2 +- erts/emulator/test/estone_SUITE.erl | 2 +- erts/emulator/test/evil_SUITE.erl | 2 +- erts/emulator/test/exception_SUITE.erl | 2 +- erts/emulator/test/float_SUITE.erl | 2 +- erts/emulator/test/float_SUITE_data/has_fpe_bug.erl | 2 +- erts/emulator/test/fun_SUITE.erl | 2 +- erts/emulator/test/fun_r13_SUITE.erl | 2 +- erts/emulator/test/gc_SUITE.erl | 2 +- erts/emulator/test/guard_SUITE.erl | 2 +- erts/emulator/test/hash_SUITE.erl | 2 +- erts/emulator/test/hibernate_SUITE.erl | 2 +- erts/emulator/test/ignore_cores.erl | 2 +- erts/emulator/test/list_bif_SUITE.erl | 2 +- erts/emulator/test/long_timers_test.erl | 2 +- erts/emulator/test/match_spec_SUITE.erl | 2 +- erts/emulator/test/message_queue_data_SUITE.erl | 2 +- erts/emulator/test/module_info_SUITE.erl | 2 +- erts/emulator/test/monitor_SUITE.erl | 2 +- erts/emulator/test/mtx_SUITE.erl | 2 +- erts/emulator/test/mtx_SUITE_data/Makefile.src | 2 +- erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c | 2 +- erts/emulator/test/multi_load_SUITE.erl | 2 +- erts/emulator/test/nested_SUITE.erl | 2 +- erts/emulator/test/nif_SUITE.erl | 2 +- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 2 +- erts/emulator/test/nif_SUITE_data/nif_mod.c | 2 +- erts/emulator/test/nif_SUITE_data/nif_mod.erl | 2 +- erts/emulator/test/node_container_SUITE.erl | 2 +- erts/emulator/test/nofrag_SUITE.erl | 2 +- erts/emulator/test/num_bif_SUITE.erl | 2 +- erts/emulator/test/old_mod.erl | 2 +- erts/emulator/test/old_scheduler_SUITE.erl | 2 +- erts/emulator/test/op_SUITE.erl | 2 +- erts/emulator/test/port_SUITE.erl | 2 +- erts/emulator/test/port_SUITE_data/dead_port.c | 2 +- erts/emulator/test/port_SUITE_data/port_test.erl | 2 +- erts/emulator/test/port_bif_SUITE.erl | 2 +- erts/emulator/test/process_SUITE.erl | 2 +- erts/emulator/test/pseudoknot_SUITE.erl | 2 +- erts/emulator/test/random_iolist.erl | 2 +- erts/emulator/test/receive_SUITE.erl | 2 +- erts/emulator/test/ref_SUITE.erl | 2 +- erts/emulator/test/register_SUITE.erl | 2 +- erts/emulator/test/save_calls_SUITE.erl | 2 +- erts/emulator/test/scheduler_SUITE.erl | 2 +- erts/emulator/test/send_term_SUITE.erl | 2 +- erts/emulator/test/sensitive_SUITE.erl | 2 +- erts/emulator/test/signal_SUITE.erl | 2 +- erts/emulator/test/smoke_test_SUITE.erl | 2 +- erts/emulator/test/statistics_SUITE.erl | 2 +- erts/emulator/test/system_info_SUITE.erl | 2 +- erts/emulator/test/system_profile_SUITE.erl | 2 +- erts/emulator/test/time_SUITE.erl | 2 +- erts/emulator/test/timer_bif_SUITE.erl | 2 +- erts/emulator/test/trace_SUITE.erl | 2 +- erts/emulator/test/trace_bif_SUITE.erl | 2 +- erts/emulator/test/trace_call_count_SUITE.erl | 2 +- erts/emulator/test/trace_call_time_SUITE.erl | 2 +- erts/emulator/test/trace_local_SUITE.erl | 2 +- erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl | 2 +- erts/emulator/test/trace_meta_SUITE.erl | 2 +- erts/emulator/test/trace_nif_SUITE.erl | 2 +- erts/emulator/test/trace_port_SUITE.erl | 2 +- erts/emulator/test/tuple_SUITE.erl | 2 +- erts/emulator/test/unique_SUITE.erl | 2 +- erts/emulator/test/z_SUITE.erl | 2 +- erts/emulator/utils/beam_makeops | 2 +- erts/emulator/utils/beam_strip | 2 +- erts/emulator/utils/count | 2 +- erts/emulator/utils/loaded | 2 +- erts/emulator/utils/make_alloc_types | 2 +- erts/emulator/utils/make_compiler_flags | 2 +- erts/emulator/utils/make_driver_tab | 2 +- erts/emulator/utils/make_preload | 2 +- erts/emulator/utils/make_tables | 2 +- erts/emulator/utils/make_version | 2 +- erts/emulator/utils/mkver.c | 2 +- erts/emulator/zlib/zlib.mk | 2 +- erts/epmd/Makefile | 2 +- erts/epmd/epmd.mk | 2 +- erts/epmd/src/Makefile | 2 +- erts/epmd/src/Makefile.in | 2 +- erts/epmd/src/epmd.c | 2 +- erts/epmd/src/epmd.h | 2 +- erts/epmd/src/epmd_cli.c | 2 +- erts/epmd/src/epmd_int.h | 2 +- erts/epmd/src/epmd_srv.c | 2 +- erts/epmd/test/Makefile | 2 +- erts/epmd/test/epmd_SUITE.erl | 2 +- erts/etc/Makefile | 2 +- erts/etc/common/Makefile | 2 +- erts/etc/common/Makefile.in | 2 +- erts/etc/common/ct_run.c | 2 +- erts/etc/common/dialyzer.c | 2 +- erts/etc/common/erlc.c | 2 +- erts/etc/common/erlexec.c | 2 +- erts/etc/common/escript.c | 2 +- erts/etc/common/heart.c | 2 +- erts/etc/common/inet_gethost.c | 2 +- erts/etc/common/typer.c | 2 +- erts/etc/unix/Install.src | 2 +- erts/etc/unix/Makefile | 2 +- erts/etc/unix/README | 2 +- erts/etc/unix/RELNOTES | 2 +- erts/etc/unix/cerl.src | 2 +- erts/etc/unix/dyn_erl.c | 2 +- erts/etc/unix/erl.src.src | 2 +- erts/etc/unix/etp-commands.in | 2 +- erts/etc/unix/etp-thr.py | 2 +- erts/etc/unix/etp_commands.erl | 2 +- erts/etc/unix/etp_commands.mk | 2 +- erts/etc/unix/format_man_pages | 2 +- erts/etc/unix/run_erl.h | 2 +- erts/etc/unix/safe_string.c | 2 +- erts/etc/unix/safe_string.h | 2 +- erts/etc/unix/setuid_socket_wrap.c | 2 +- erts/etc/unix/start.src | 2 +- erts/etc/unix/start_erl.src | 2 +- erts/etc/win32/Install.c | 2 +- erts/etc/win32/Makefile | 2 +- erts/etc/win32/Nmakefile.start_erl | 2 +- erts/etc/win32/beam.rc | 2 +- erts/etc/win32/cygwin_tools/erl | 2 +- erts/etc/win32/cygwin_tools/erlc | 2 +- erts/etc/win32/cygwin_tools/javac.sh | 2 +- erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh | 2 +- erts/etc/win32/cygwin_tools/make_local_ini.sh | 2 +- erts/etc/win32/cygwin_tools/mingw/ar.sh | 2 +- erts/etc/win32/cygwin_tools/mingw/cc.sh | 2 +- erts/etc/win32/cygwin_tools/mingw/coffix.c | 2 +- erts/etc/win32/cygwin_tools/mingw/emu_cc.sh | 2 +- erts/etc/win32/cygwin_tools/mingw/ld.sh | 2 +- erts/etc/win32/cygwin_tools/mingw/mc.sh | 2 +- erts/etc/win32/cygwin_tools/mingw/rc.sh | 2 +- erts/etc/win32/cygwin_tools/vc/ar.sh | 2 +- erts/etc/win32/cygwin_tools/vc/cc.sh | 2 +- erts/etc/win32/cygwin_tools/vc/cc_wrap.c | 2 +- erts/etc/win32/cygwin_tools/vc/coffix.c | 2 +- erts/etc/win32/cygwin_tools/vc/emu_cc.sh | 2 +- erts/etc/win32/cygwin_tools/vc/ld.sh | 2 +- erts/etc/win32/cygwin_tools/vc/ld_wrap.c | 2 +- erts/etc/win32/cygwin_tools/vc/mc.sh | 2 +- erts/etc/win32/cygwin_tools/vc/rc.sh | 2 +- erts/etc/win32/erl.c | 2 +- erts/etc/win32/erl.rc | 2 +- erts/etc/win32/erl_log.c | 2 +- erts/etc/win32/erlsrv/erlsrv_global.h | 2 +- erts/etc/win32/erlsrv/erlsrv_interactive.c | 2 +- erts/etc/win32/erlsrv/erlsrv_interactive.h | 2 +- erts/etc/win32/erlsrv/erlsrv_main.c | 2 +- erts/etc/win32/erlsrv/erlsrv_registry.c | 2 +- erts/etc/win32/erlsrv/erlsrv_registry.h | 2 +- erts/etc/win32/erlsrv/erlsrv_service.c | 2 +- erts/etc/win32/erlsrv/erlsrv_service.h | 2 +- erts/etc/win32/erlsrv/erlsrv_util.c | 2 +- erts/etc/win32/erlsrv/erlsrv_util.h | 2 +- erts/etc/win32/init_file.c | 2 +- erts/etc/win32/init_file.h | 2 +- erts/etc/win32/msys_tools/erl | 2 +- erts/etc/win32/msys_tools/erlc | 2 +- erts/etc/win32/msys_tools/javac.sh | 2 +- erts/etc/win32/msys_tools/make_bootstrap_ini.sh | 2 +- erts/etc/win32/msys_tools/make_local_ini.sh | 2 +- erts/etc/win32/msys_tools/vc/ar.sh | 2 +- erts/etc/win32/msys_tools/vc/cc.sh | 2 +- erts/etc/win32/msys_tools/vc/coffix.c | 2 +- erts/etc/win32/msys_tools/vc/emu_cc.sh | 2 +- erts/etc/win32/msys_tools/vc/ld.sh | 2 +- erts/etc/win32/msys_tools/vc/mc.sh | 2 +- erts/etc/win32/msys_tools/vc/rc.sh | 2 +- erts/etc/win32/nsis/Makefile | 2 +- erts/etc/win32/nsis/dll_version_helper.sh | 2 +- erts/etc/win32/nsis/erlang20.nsi | 2 +- erts/etc/win32/nsis/find_redist.sh | 2 +- erts/etc/win32/port_entry.c | 2 +- erts/etc/win32/resource.h | 2 +- erts/etc/win32/start_erl.c | 2 +- erts/etc/win32/win_erlexec.c | 2 +- erts/example/Makefile | 2 +- erts/example/matrix_nif.c | 2 +- erts/example/matrix_nif.erl | 2 +- erts/example/next_perm.cc | 2 +- erts/example/next_perm.erl | 2 +- erts/example/pg_async.c | 2 +- erts/example/pg_async.erl | 2 +- erts/example/pg_async2.c | 2 +- erts/example/pg_async2.erl | 2 +- erts/example/pg_encode.c | 2 +- erts/example/pg_encode.h | 2 +- erts/example/pg_encode2.c | 2 +- erts/example/pg_encode2.h | 2 +- erts/example/pg_sync.c | 2 +- erts/example/pg_sync.erl | 2 +- erts/include/erl_fixed_size_int_types.h | 2 +- erts/include/erl_int_sizes_config.h.in | 2 +- erts/include/erl_memory_trace_parser.h | 2 +- erts/include/erl_native_features_config.h.in | 2 +- erts/include/internal/README | 2 +- erts/include/internal/erl_errno.h | 2 +- erts/include/internal/erl_memory_trace_protocol.h | 2 +- erts/include/internal/erl_misc_utils.h | 2 +- erts/include/internal/erl_printf.h | 2 +- erts/include/internal/erl_printf_format.h | 2 +- erts/include/internal/erts_internal.mk.in | 2 +- erts/include/internal/ethr_atomics.h | 2 +- erts/include/internal/ethr_internal.h | 2 +- erts/include/internal/ethr_mutex.h | 2 +- erts/include/internal/ethr_optimized_fallbacks.h | 2 +- erts/include/internal/ethread.mk.in | 2 +- erts/include/internal/ethread_header_config.h.in | 2 +- erts/include/internal/ethread_inline.h | 2 +- erts/include/internal/i386/atomic.h | 2 +- erts/include/internal/i386/ethr_dw_atomic.h | 2 +- erts/include/internal/i386/ethr_membar.h | 2 +- erts/include/internal/i386/ethread.h | 2 +- erts/include/internal/i386/rwlock.h | 2 +- erts/include/internal/i386/spinlock.h | 2 +- erts/include/internal/libatomic_ops/ethr_atomic.h | 2 +- erts/include/internal/libatomic_ops/ethr_dw_atomic.h | 2 +- erts/include/internal/libatomic_ops/ethr_membar.h | 2 +- erts/include/internal/libatomic_ops/ethread.h | 2 +- erts/include/internal/ppc32/atomic.h | 2 +- erts/include/internal/ppc32/ethr_membar.h | 2 +- erts/include/internal/ppc32/ethread.h | 2 +- erts/include/internal/ppc32/rwlock.h | 2 +- erts/include/internal/ppc32/spinlock.h | 2 +- erts/include/internal/pthread/ethr_event.h | 2 +- erts/include/internal/sparc32/atomic.h | 2 +- erts/include/internal/sparc32/ethr_membar.h | 2 +- erts/include/internal/sparc32/ethread.h | 2 +- erts/include/internal/sparc32/rwlock.h | 2 +- erts/include/internal/sparc32/spinlock.h | 2 +- erts/include/internal/sparc64/ethread.h | 2 +- erts/include/internal/tile/atomic.h | 2 +- erts/include/internal/tile/ethr_membar.h | 2 +- erts/include/internal/tile/ethread.h | 2 +- erts/include/internal/win/ethr_atomic.h | 2 +- erts/include/internal/win/ethr_dw_atomic.h | 2 +- erts/include/internal/win/ethr_event.h | 2 +- erts/include/internal/win/ethr_membar.h | 2 +- erts/include/internal/win/ethread.h | 2 +- erts/include/internal/x86_64/ethread.h | 2 +- erts/lib/internal/README | 2 +- erts/lib_src/Makefile | 2 +- erts/lib_src/Makefile.in | 2 +- erts/lib_src/common/erl_memory_trace_parser.c | 2 +- erts/lib_src/common/erl_misc_utils.c | 2 +- erts/lib_src/common/erl_printf.c | 2 +- erts/lib_src/common/erl_printf_format.c | 2 +- erts/lib_src/common/ethr_atomics.c | 2 +- erts/lib_src/common/ethr_aux.c | 2 +- erts/lib_src/common/ethr_cbf.c | 2 +- erts/lib_src/common/ethr_mutex.c | 2 +- erts/lib_src/pthread/ethr_event.c | 2 +- erts/lib_src/pthread/ethr_x86_sse2_asm.c | 2 +- erts/lib_src/pthread/ethread.c | 2 +- erts/lib_src/utils/make_atomics_api | 2 +- erts/lib_src/win/ethr_event.c | 2 +- erts/lib_src/win/ethread.c | 2 +- erts/preloaded/Makefile | 2 +- erts/preloaded/src/Makefile | 2 +- erts/preloaded/src/add_abstract_code | 2 +- erts/preloaded/src/erl_prim_loader.erl | 2 +- erts/preloaded/src/erlang.erl | 2 +- erts/preloaded/src/erts.app.src | 2 +- erts/preloaded/src/erts_internal.erl | 2 +- erts/preloaded/src/init.erl | 2 +- erts/preloaded/src/otp_ring0.erl | 2 +- erts/preloaded/src/prim_eval.S | 2 +- erts/preloaded/src/prim_eval.erl | 2 +- erts/preloaded/src/prim_inet.erl | 2 +- erts/preloaded/src/prim_zip.erl | 2 +- erts/preloaded/src/zip_internal.hrl | 2 +- erts/preloaded/src/zlib.erl | 2 +- erts/start_scripts/Makefile | 2 +- erts/start_scripts/no_dot_erlang.rel.src | 2 +- erts/start_scripts/start_all_example.rel.src | 2 +- erts/start_scripts/start_clean.rel.src | 2 +- erts/start_scripts/start_sasl.rel.src | 2 +- erts/test/Makefile | 2 +- erts/test/erl_print_SUITE.erl | 2 +- erts/test/erl_print_SUITE_data/Makefile.src | 2 +- erts/test/erl_print_SUITE_data/character_test.h | 2 +- erts/test/erl_print_SUITE_data/erl_print_tests.c | 2 +- erts/test/erl_print_SUITE_data/integer_64_test.h | 2 +- erts/test/erl_print_SUITE_data/integer_test.h | 2 +- erts/test/erl_print_SUITE_data/snprintf_test.h | 2 +- erts/test/erl_print_SUITE_data/string_test.h | 2 +- erts/test/erlc_SUITE.erl | 2 +- erts/test/erlc_SUITE_data/include/erl_test.hrl | 2 +- erts/test/erlc_SUITE_data/src/erl_test_bad.erl | 2 +- erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl | 2 +- erts/test/erlc_SUITE_data/src/erl_test_ok.erl | 2 +- erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl | 2 +- erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl | 2 +- erts/test/erlexec_SUITE.erl | 2 +- erts/test/erlexec_SUITE_data/Makefile.src | 2 +- erts/test/erlexec_SUITE_data/erlexec_tests.c | 2 +- erts/test/ethread_SUITE.erl | 2 +- erts/test/ethread_SUITE_data/Makefile.src | 2 +- erts/test/ethread_SUITE_data/ethread_tests.c | 2 +- erts/test/ignore_cores.erl | 2 +- erts/test/install_SUITE.erl | 2 +- erts/test/nt_SUITE.erl | 2 +- erts/test/nt_SUITE_data/Makefile.src | 2 +- erts/test/nt_SUITE_data/nt_info.c | 2 +- erts/test/otp_SUITE.erl | 2 +- erts/test/run_erl_SUITE.erl | 2 +- erts/test/run_erl_SUITE_data/defuncter.pl | 2 +- erts/test/run_erl_SUITE_data/run_erl_test.pl | 2 +- erts/test/upgrade_SUITE.erl | 2 +- erts/test/upgrade_SUITE_data/start.src | 2 +- erts/test/utils/gccifier.c | 2 +- erts/test/utils/gccifier.sh | 2 +- erts/test/z_SUITE.erl | 2 +- 719 files changed, 719 insertions(+), 719 deletions(-) (limited to 'erts') diff --git a/erts/AUTHORS b/erts/AUTHORS index d8746f65b2..5555502099 100644 --- a/erts/AUTHORS +++ b/erts/AUTHORS @@ -1,7 +1,7 @@ %CopyrightBegin% - Copyright Ericsson AB 1999-2009. All Rights Reserved. + Copyright Ericsson AB 1999-2016. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/erts/Makefile.in b/erts/Makefile.in index feed00dad5..3052dc3065 100644 --- a/erts/Makefile.in +++ b/erts/Makefile.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2013. All Rights Reserved. +# Copyright Ericsson AB 2006-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 017fdbd589..86799186fd 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1,7 +1,7 @@ dnl dnl %CopyrightBegin% dnl -dnl Copyright Ericsson AB 1998-2015. All Rights Reserved. +dnl Copyright Ericsson AB 1998-2016. All Rights Reserved. dnl dnl Licensed under the Apache License, Version 2.0 (the "License"); dnl you may not use this file except in compliance with the License. diff --git a/erts/autoconf/configure.vxworks b/erts/autoconf/configure.vxworks index 96dd1f8401..a13e0a6c56 100755 --- a/erts/autoconf/configure.vxworks +++ b/erts/autoconf/configure.vxworks @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2011. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general index 5adee4db45..96a70e4148 100644 --- a/erts/autoconf/vxworks/sed.general +++ b/erts/autoconf/vxworks/sed.general @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2013. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/autoconf/vxworks/sed.vxworks_cpu32 b/erts/autoconf/vxworks/sed.vxworks_cpu32 index e3d54246ab..71663676e7 100644 --- a/erts/autoconf/vxworks/sed.vxworks_cpu32 +++ b/erts/autoconf/vxworks/sed.vxworks_cpu32 @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2010. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc32 b/erts/autoconf/vxworks/sed.vxworks_ppc32 index 012760e127..2146e862fd 100644 --- a/erts/autoconf/vxworks/sed.vxworks_ppc32 +++ b/erts/autoconf/vxworks/sed.vxworks_ppc32 @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2013. All Rights Reserved. +# Copyright Ericsson AB 2006-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc603 b/erts/autoconf/vxworks/sed.vxworks_ppc603 index 55af52571b..fca1ba76d9 100644 --- a/erts/autoconf/vxworks/sed.vxworks_ppc603 +++ b/erts/autoconf/vxworks/sed.vxworks_ppc603 @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2000-2010. All Rights Reserved. +# Copyright Ericsson AB 2000-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall b/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall index e0c0891aeb..51c589d79a 100644 --- a/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall +++ b/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2010. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc860 b/erts/autoconf/vxworks/sed.vxworks_ppc860 index 8828339e34..485504e706 100644 --- a/erts/autoconf/vxworks/sed.vxworks_ppc860 +++ b/erts/autoconf/vxworks/sed.vxworks_ppc860 @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2010. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/autoconf/vxworks/sed.vxworks_simlinux b/erts/autoconf/vxworks/sed.vxworks_simlinux index 950fb79ec5..10cd7bbb82 100644 --- a/erts/autoconf/vxworks/sed.vxworks_simlinux +++ b/erts/autoconf/vxworks/sed.vxworks_simlinux @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2008-2013. All Rights Reserved. +# Copyright Ericsson AB 2008-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/autoconf/vxworks/sed.vxworks_simso b/erts/autoconf/vxworks/sed.vxworks_simso index 6f2796f04e..cd30f8c2b2 100644 --- a/erts/autoconf/vxworks/sed.vxworks_simso +++ b/erts/autoconf/vxworks/sed.vxworks_simso @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2013. All Rights Reserved. +# Copyright Ericsson AB 2005-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/autoconf/vxworks/sed.vxworks_sparc b/erts/autoconf/vxworks/sed.vxworks_sparc index e67c34af26..a3758423e8 100644 --- a/erts/autoconf/vxworks/sed.vxworks_sparc +++ b/erts/autoconf/vxworks/sed.vxworks_sparc @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2009. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/configure.in b/erts/configure.in index 021780ecc2..814eaaba88 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. -*-m4-*- dnl %CopyrightBegin% dnl -dnl Copyright Ericsson AB 1997-2015. All Rights Reserved. +dnl Copyright Ericsson AB 1997-2016. All Rights Reserved. dnl dnl Licensed under the Apache License, Version 2.0 (the "License"); dnl you may not use this file except in compliance with the License. diff --git a/erts/doc/Makefile b/erts/doc/Makefile index d415e544f3..f26a43592e 100644 --- a/erts/doc/Makefile +++ b/erts/doc/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2009. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile index 83f4c58560..e02e89238e 100644 --- a/erts/doc/src/Makefile +++ b/erts/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2012. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml index 2263302707..e283acc1b4 100644 --- a/erts/doc/src/alt_dist.xml +++ b/erts/doc/src/alt_dist.xml @@ -4,7 +4,7 @@
- 20002013 + 20002016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/book.xml b/erts/doc/src/book.xml index 12eda03ee5..a0780c91d9 100644 --- a/erts/doc/src/book.xml +++ b/erts/doc/src/book.xml @@ -4,7 +4,7 @@
- 19972013 + 19972016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/communication.xml b/erts/doc/src/communication.xml index 3deea3e4af..1eb05310e9 100644 --- a/erts/doc/src/communication.xml +++ b/erts/doc/src/communication.xml @@ -4,7 +4,7 @@
- 20122013 + 20122016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index 61c9159823..0b827ae583 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -4,7 +4,7 @@
- 19992013 + 19992016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/driver.xml b/erts/doc/src/driver.xml index a68e87d3b3..4bef5e1388 100644 --- a/erts/doc/src/driver.xml +++ b/erts/doc/src/driver.xml @@ -4,7 +4,7 @@
- 20012013 + 20012016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml index 7f61804bea..d9f580d081 100644 --- a/erts/doc/src/epmd.xml +++ b/erts/doc/src/epmd.xml @@ -4,7 +4,7 @@
- 19962013 + 19962016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 096af096dc..e13470c83c 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -4,7 +4,7 @@
- 19962015 + 19962016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 241d4131d5..175b7f6bfb 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -4,7 +4,7 @@
- 20012015 + 20012016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index be0e406b9c..5481a81bf0 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -4,7 +4,7 @@
- 20012015 + 20012016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml index 8f66e07ae1..d3ece37cc5 100644 --- a/erts/doc/src/erl_prim_loader.xml +++ b/erts/doc/src/erl_prim_loader.xml @@ -4,7 +4,7 @@
- 19962013 + 19962016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 350a8506f5..ef577c82bf 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4,7 +4,7 @@
- 19962015 + 19962016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml index 9fc5864413..a64927fec2 100644 --- a/erts/doc/src/erlc.xml +++ b/erts/doc/src/erlc.xml @@ -4,7 +4,7 @@
- 19972013 + 19972016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv.xml index ccb8b2dd76..fb00444aa4 100644 --- a/erts/doc/src/erlsrv.xml +++ b/erts/doc/src/erlsrv.xml @@ -4,7 +4,7 @@
- 19982013 + 19982016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 0965f9b49c..05be35e0af 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -4,7 +4,7 @@
- 20022015 + 20022016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/inet_cfg.xml b/erts/doc/src/inet_cfg.xml index 5caf232a62..027fe600d7 100644 --- a/erts/doc/src/inet_cfg.xml +++ b/erts/doc/src/inet_cfg.xml @@ -4,7 +4,7 @@
- 20042013 + 20042016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index 2a33096d04..84a5aea335 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -4,7 +4,7 @@
- 19962013 + 19962016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml index 08dad8cc10..975f01cf2c 100644 --- a/erts/doc/src/match_spec.xml +++ b/erts/doc/src/match_spec.xml @@ -4,7 +4,7 @@
- 19992013 + 19992016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index acd816a81c..9e0cf2354f 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -4,7 +4,7 @@
- 20042015 + 20042016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/notes_history.xml b/erts/doc/src/notes_history.xml index 0886ae4039..0bc2ab1383 100644 --- a/erts/doc/src/notes_history.xml +++ b/erts/doc/src/notes_history.xml @@ -4,7 +4,7 @@
- 20062013 + 20062016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/part.xml b/erts/doc/src/part.xml index 2f5eca93db..b2abfc62ca 100644 --- a/erts/doc/src/part.xml +++ b/erts/doc/src/part.xml @@ -4,7 +4,7 @@
- 19962013 + 19962016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/part_notes.xml b/erts/doc/src/part_notes.xml index 83bb479715..e579b7635d 100644 --- a/erts/doc/src/part_notes.xml +++ b/erts/doc/src/part_notes.xml @@ -4,7 +4,7 @@
- 20042013 + 20042016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/part_notes_history.xml b/erts/doc/src/part_notes_history.xml index 055d1681d5..277683a2b5 100644 --- a/erts/doc/src/part_notes_history.xml +++ b/erts/doc/src/part_notes_history.xml @@ -4,7 +4,7 @@
- 20062013 + 20062016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/ref_man.xml b/erts/doc/src/ref_man.xml index ac589f8cb5..aa245ec08a 100644 --- a/erts/doc/src/ref_man.xml +++ b/erts/doc/src/ref_man.xml @@ -4,7 +4,7 @@
- 19962013 + 19962016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/run_erl.xml b/erts/doc/src/run_erl.xml index faec3c68c1..6b0fef7c0a 100644 --- a/erts/doc/src/run_erl.xml +++ b/erts/doc/src/run_erl.xml @@ -4,7 +4,7 @@
- 19992013 + 19992016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/start.xml b/erts/doc/src/start.xml index 386fbe6e88..adacf5b98d 100644 --- a/erts/doc/src/start.xml +++ b/erts/doc/src/start.xml @@ -4,7 +4,7 @@
- 19992013 + 19992016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/start_erl.xml b/erts/doc/src/start_erl.xml index 62610b43b0..243aeaa717 100644 --- a/erts/doc/src/start_erl.xml +++ b/erts/doc/src/start_erl.xml @@ -4,7 +4,7 @@
- 19982013 + 19982016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/tty.xml b/erts/doc/src/tty.xml index cd46d1203c..b2866c82cf 100644 --- a/erts/doc/src/tty.xml +++ b/erts/doc/src/tty.xml @@ -4,7 +4,7 @@
- 19962013 + 19962016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/werl.xml b/erts/doc/src/werl.xml index 9e7ad584eb..1a3cb6f502 100644 --- a/erts/doc/src/werl.xml +++ b/erts/doc/src/werl.xml @@ -4,7 +4,7 @@
- 19982013 + 19982016 Ericsson AB. All Rights Reserved. diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index 0a641346d9..861661043f 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -4,7 +4,7 @@
- 20052013 + 20052016 Ericsson AB. All Rights Reserved. diff --git a/erts/emulator/Makefile b/erts/emulator/Makefile index 550e6e6f5b..65fdbdb747 100644 --- a/erts/emulator/Makefile +++ b/erts/emulator/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2009. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 12148ad9c7..232448d1a3 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2013. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c index 099c00bcf6..a5e778e4aa 100644 --- a/erts/emulator/beam/atom.c +++ b/erts/emulator/beam/atom.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/atom.h b/erts/emulator/beam/atom.h index 2c002ca92f..fbd0528009 100644 --- a/erts/emulator/beam/atom.h +++ b/erts/emulator/beam/atom.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 169b071cd7..6080f733ec 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2013. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 0b47fc3586..320dee6668 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2013. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 5d471d168b..86e1fdc2fb 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2013. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index 2b89d6fc71..f9eca94e2a 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2013. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/beam_catches.c b/erts/emulator/beam/beam_catches.c index 7a1f4901aa..cd592c7e5e 100644 --- a/erts/emulator/beam/beam_catches.c +++ b/erts/emulator/beam/beam_catches.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2013. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/beam_catches.h b/erts/emulator/beam/beam_catches.h index 59ee64d033..8eb2165ac9 100644 --- a/erts/emulator/beam/beam_catches.h +++ b/erts/emulator/beam/beam_catches.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2013. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index e37bd4d78c..61149721fe 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index a390422040..04cb1fbdf9 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index f115df935f..95709db914 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index 22ab71c868..6fd9b4ff2a 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2013. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c index 54c337ee72..55342a38c6 100644 --- a/erts/emulator/beam/beam_ranges.c +++ b/erts/emulator/beam/beam_ranges.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012. All Rights Reserved. + * Copyright Ericsson AB 2012-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/benchmark.c b/erts/emulator/beam/benchmark.c index 44f5c760c2..c8409784ef 100644 --- a/erts/emulator/beam/benchmark.c +++ b/erts/emulator/beam/benchmark.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2012. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/benchmark.h b/erts/emulator/beam/benchmark.h index 0fb0b93f12..0272896f4f 100644 --- a/erts/emulator/beam/benchmark.h +++ b/erts/emulator/beam/benchmark.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2012. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 37bb28c6f8..3ca98a4066 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index 46af552b39..546e3830b9 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index a6670c10e8..9bb77190d6 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2013. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index c738a9ecf5..4baee7900b 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index ee51e5d313..464acd67f6 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index 3fd284b589..cfd8fbe2f5 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 8c039ef132..057ec885e2 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c index 1b0968c55c..9da750e366 100644 --- a/erts/emulator/beam/code_ix.c +++ b/erts/emulator/beam/code_ix.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012. All Rights Reserved. + * Copyright Ericsson AB 2012-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/code_ix.h b/erts/emulator/beam/code_ix.h index 7a66211a5b..584a605771 100644 --- a/erts/emulator/beam/code_ix.h +++ b/erts/emulator/beam/code_ix.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2013. All Rights Reserved. + * Copyright Ericsson AB 2012-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index b38ebe066b..ccc4cbad43 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2012. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index fa385f105d..32a4c9400d 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index fb777d9ac1..3e6e4710c3 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/dtrace-wrapper.h b/erts/emulator/beam/dtrace-wrapper.h index d343dc5ab0..6f70d5961e 100644 --- a/erts/emulator/beam/dtrace-wrapper.h +++ b/erts/emulator/beam/dtrace-wrapper.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 2011-2012. + * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 2011-2016. * All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/erts/emulator/beam/elib_memmove.c b/erts/emulator/beam/elib_memmove.c index d4ca30158e..2f45f69026 100644 --- a/erts/emulator/beam/elib_memmove.c +++ b/erts/emulator/beam/elib_memmove.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c index 4b0541c10e..eda3ad870a 100644 --- a/erts/emulator/beam/erl_afit_alloc.c +++ b/erts/emulator/beam/erl_afit_alloc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_afit_alloc.h b/erts/emulator/beam/erl_afit_alloc.h index ef050ff50e..74258e284a 100644 --- a/erts/emulator/beam/erl_afit_alloc.h +++ b/erts/emulator/beam/erl_afit_alloc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 490e0c0915..d58651fa86 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2013. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index 71e4713624..b2ca0deb02 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2013. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 2932adca84..b7fa978896 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2014. All Rights Reserved. +# Copyright Ericsson AB 2003-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 5e7dd7cce8..6812551075 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2013. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index b7d717ed23..b8bdf971c6 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2013. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c index 5cd067ff54..fbe4724047 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.c +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.h b/erts/emulator/beam/erl_ao_firstfit_alloc.h index 4200f20622..7349c6ab19 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.h +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_arith.c b/erts/emulator/beam/erl_arith.c index 3671025d22..861532f241 100644 --- a/erts/emulator/beam/erl_arith.c +++ b/erts/emulator/beam/erl_arith.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2010. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index cdeeb5281b..bf18eef730 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2013. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_async.h b/erts/emulator/beam/erl_async.h index 65538bcef0..473c7686e5 100644 --- a/erts/emulator/beam/erl_async.h +++ b/erts/emulator/beam/erl_async.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c index f39a18ac88..379cee39a1 100644 --- a/erts/emulator/beam/erl_bestfit_alloc.c +++ b/erts/emulator/beam/erl_bestfit_alloc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bestfit_alloc.h b/erts/emulator/beam/erl_bestfit_alloc.h index b315518b88..3a5f51f5dc 100644 --- a/erts/emulator/beam/erl_bestfit_alloc.h +++ b/erts/emulator/beam/erl_bestfit_alloc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index aec72bd61a..6e10980b6b 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2013. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c index e3074d6309..9417803e14 100644 --- a/erts/emulator/beam/erl_bif_chksum.c +++ b/erts/emulator/beam/erl_bif_chksum.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2013. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index f006856b6a..3529a66383 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c index 4a9a6a5fcd..b42d2dc28b 100644 --- a/erts/emulator/beam/erl_bif_guard.c +++ b/erts/emulator/beam/erl_bif_guard.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2011. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 8c748c9bf7..d4499db291 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2013. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c index fe64e76575..fcccb5a4a0 100644 --- a/erts/emulator/beam/erl_bif_lists.c +++ b/erts/emulator/beam/erl_bif_lists.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2011. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c index 0f20ded1d6..aecb8bf0c1 100644 --- a/erts/emulator/beam/erl_bif_op.c +++ b/erts/emulator/beam/erl_bif_op.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2013. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_os.c b/erts/emulator/beam/erl_bif_os.c index 2333ca0851..46777d3aa5 100644 --- a/erts/emulator/beam/erl_bif_os.c +++ b/erts/emulator/beam/erl_bif_os.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2012. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 86bcf43678..648f33744a 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index 7f7cd376ac..f4daecd2a4 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2013. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 71a7079b09..a9443ee8df 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2013. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_unique.c b/erts/emulator/beam/erl_bif_unique.c index c4a39b8897..08b0baf989 100644 --- a/erts/emulator/beam/erl_bif_unique.c +++ b/erts/emulator/beam/erl_bif_unique.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2014. All Rights Reserved. + * Copyright Ericsson AB 2014-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bif_unique.h b/erts/emulator/beam/erl_bif_unique.h index 37d5d91c39..5eb2e2a619 100644 --- a/erts/emulator/beam/erl_bif_unique.h +++ b/erts/emulator/beam/erl_bif_unique.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2014. All Rights Reserved. + * Copyright Ericsson AB 2014-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index e181b5555d..fdc5efea98 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2013. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 11d83686a3..6bf52fb303 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2013. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_bits.h b/erts/emulator/beam/erl_bits.h index 8b7807fbd9..1c2a090f07 100644 --- a/erts/emulator/beam/erl_bits.h +++ b/erts/emulator/beam/erl_bits.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2011. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c index c263eebfcc..28aaeeb479 100644 --- a/erts/emulator/beam/erl_cpu_topology.c +++ b/erts/emulator/beam/erl_cpu_topology.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2013. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h index f3fbfc6da0..45324ac4a0 100644 --- a/erts/emulator/beam/erl_cpu_topology.h +++ b/erts/emulator/beam/erl_cpu_topology.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2013. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 441dbdde4f..615d23402b 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h index a589af784c..1d26c49652 100644 --- a/erts/emulator/beam/erl_db.h +++ b/erts/emulator/beam/erl_db.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 240b79fc60..74979f984a 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2012. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h index 66d8ec71d9..e654363cd5 100644 --- a/erts/emulator/beam/erl_db_hash.h +++ b/erts/emulator/beam/erl_db_hash.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2014. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 0f642f0867..02d211a4bb 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_db_tree.h b/erts/emulator/beam/erl_db_tree.h index 6098387f5d..72749ead1e 100644 --- a/erts/emulator/beam/erl_db_tree.h +++ b/erts/emulator/beam/erl_db_tree.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 973be58420..be14386b14 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2014. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 911ed37aef..5d7946a525 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index 2902c98864..3e3bfa03a2 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 0234b9b0e4..97a69140c3 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2014. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h index f6b946ae82..2700b62854 100644 --- a/erts/emulator/beam/erl_drv_nif.h +++ b/erts/emulator/beam/erl_drv_nif.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index 184f8e8931..92edce5176 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2011. All Rights Reserved. + * Copyright Ericsson AB 2007-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c index ed555aa92f..6ce1376c81 100644 --- a/erts/emulator/beam/erl_fun.c +++ b/erts/emulator/beam/erl_fun.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2010. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h index 0024b1ff71..8c4deea7a0 100644 --- a/erts/emulator/beam/erl_fun.h +++ b/erts/emulator/beam/erl_fun.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2012. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 0d125c5598..62417fa05c 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2014. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index 2cedd9361f..40b7c5d12c 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2011. All Rights Reserved. + * Copyright Ericsson AB 2007-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c index 9b4aad9d91..223ba193da 100644 --- a/erts/emulator/beam/erl_goodfit_alloc.c +++ b/erts/emulator/beam/erl_goodfit_alloc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_goodfit_alloc.h b/erts/emulator/beam/erl_goodfit_alloc.h index ababdbd0a1..76dd558234 100644 --- a/erts/emulator/beam/erl_goodfit_alloc.h +++ b/erts/emulator/beam/erl_goodfit_alloc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 491d2f8c84..1a621863bb 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2015. All Rights Reserved. + * Copyright Ericsson AB 2015-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 86c4eeb484..2f03a37b79 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2013. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c index 12a72ad839..f84c63e7a4 100644 --- a/erts/emulator/beam/erl_instrument.c +++ b/erts/emulator/beam/erl_instrument.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_instrument.h b/erts/emulator/beam/erl_instrument.h index cb3b1920d3..1f04c91d5e 100644 --- a/erts/emulator/beam/erl_instrument.h +++ b/erts/emulator/beam/erl_instrument.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2009. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 598bf84c0b..9231fb1b34 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2013. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_lock_check.h b/erts/emulator/beam/erl_lock_check.h index 66251ef4e8..18296d1fec 100644 --- a/erts/emulator/beam/erl_lock_check.h +++ b/erts/emulator/beam/erl_lock_check.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2012. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index bd00480ba2..a00e0f0fff 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2012. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h index 4cc6a5c695..3e8dcefe69 100644 --- a/erts/emulator/beam/erl_lock_count.h +++ b/erts/emulator/beam/erl_lock_count.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2012. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 4b6931f848..b74eb04b5b 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2014. All Rights Reserved. + * Copyright Ericsson AB 2014-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 052fa99f03..a40070d00d 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2014. All Rights Reserved. + * Copyright Ericsson AB 2014-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_math.c b/erts/emulator/beam/erl_math.c index b46cc37495..fc0aaed18a 100644 --- a/erts/emulator/beam/erl_math.c +++ b/erts/emulator/beam/erl_math.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 88efb2c59f..a6bef42f77 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2012. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 60035d15ae..ad4e65274c 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2012. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_monitors.c b/erts/emulator/beam/erl_monitors.c index bd899fa2f9..910598690d 100644 --- a/erts/emulator/beam/erl_monitors.c +++ b/erts/emulator/beam/erl_monitors.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2013. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_monitors.h b/erts/emulator/beam/erl_monitors.h index 901ed8940b..9e2beedea3 100644 --- a/erts/emulator/beam/erl_monitors.h +++ b/erts/emulator/beam/erl_monitors.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2013. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c index fdaf02f37a..e275867928 100644 --- a/erts/emulator/beam/erl_mtrace.c +++ b/erts/emulator/beam/erl_mtrace.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_mtrace.h b/erts/emulator/beam/erl_mtrace.h index b34eab3c4a..776c70a819 100644 --- a/erts/emulator/beam/erl_mtrace.h +++ b/erts/emulator/beam/erl_mtrace.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2009. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 8580bc2689..d0ac74d22d 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2014. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index c9c8b1d0af..dd4f85723d 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2014. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 1448a508a2..a444045357 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2014. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_node_container_utils.h b/erts/emulator/beam/erl_node_container_utils.h index b6a3531e28..0c76c6fe7d 100644 --- a/erts/emulator/beam/erl_node_container_utils.h +++ b/erts/emulator/beam/erl_node_container_utils.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 8617f42d7b..6679393287 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index fb2f2a5407..8ee2708875 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2012. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index fa97707a87..7510f94df1 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2013. All Rights Reserved. + * Copyright Ericsson AB 2012-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index e85395e062..fefaede079 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h index 335f7a77d5..a11fc6c9dc 100644 --- a/erts/emulator/beam/erl_port_task.h +++ b/erts/emulator/beam/erl_port_task.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 45ba4371dc..1a579704a8 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2014. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_printf_term.h b/erts/emulator/beam/erl_printf_term.h index 4618457f27..8a30286fd8 100644 --- a/erts/emulator/beam/erl_printf_term.h +++ b/erts/emulator/beam/erl_printf_term.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2013. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a706ebf595..2648ed0cde 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index c88bd7056c..15a524a9e6 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 487b944fc0..d8c2eaba94 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2013. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h index dac214c8a1..387562058c 100644 --- a/erts/emulator/beam/erl_process_dict.h +++ b/erts/emulator/beam/erl_process_dict.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index 39c36ee7a9..cd62593945 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index 0bee2c848c..e1f470d381 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2012. All Rights Reserved. + * Copyright Ericsson AB 2007-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index 9c59301086..ac324c6f3e 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2012. All Rights Reserved. + * Copyright Ericsson AB 2007-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c index 9ed175fe01..22830a19c4 100644 --- a/erts/emulator/beam/erl_ptab.c +++ b/erts/emulator/beam/erl_ptab.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2013. All Rights Reserved. + * Copyright Ericsson AB 2012-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h index 8fd961e3ce..97df8bc3fc 100644 --- a/erts/emulator/beam/erl_ptab.h +++ b/erts/emulator/beam/erl_ptab.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2013. All Rights Reserved. + * Copyright Ericsson AB 2012-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c index caec24bc03..cab4bd73db 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2012. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.h b/erts/emulator/beam/erl_sched_spec_pre_alloc.h index 4d07b0f674..7808d7d438 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.h +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2012. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_smp.h b/erts/emulator/beam/erl_smp.h index 5fc5e989a6..713ed50b86 100644 --- a/erts/emulator/beam/erl_smp.h +++ b/erts/emulator/beam/erl_smp.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2013. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_sock.h b/erts/emulator/beam/erl_sock.h index 7be6062115..3429a52d7e 100644 --- a/erts/emulator/beam/erl_sock.h +++ b/erts/emulator/beam/erl_sock.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2009. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_sys_driver.h b/erts/emulator/beam/erl_sys_driver.h index 4031eef0aa..d46e88cb05 100644 --- a/erts/emulator/beam/erl_sys_driver.h +++ b/erts/emulator/beam/erl_sys_driver.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_term.c b/erts/emulator/beam/erl_term.c index d2343912b4..9cbd156800 100644 --- a/erts/emulator/beam/erl_term.c +++ b/erts/emulator/beam/erl_term.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2013. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 2b28762db5..811127a1cb 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2014. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 7b06fd840f..542541165b 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2013. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h index b89cc4c267..b2894ba1fe 100644 --- a/erts/emulator/beam/erl_thr_progress.h +++ b/erts/emulator/beam/erl_thr_progress.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2013. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_thr_queue.c b/erts/emulator/beam/erl_thr_queue.c index 7ff456b915..b813041d9a 100644 --- a/erts/emulator/beam/erl_thr_queue.c +++ b/erts/emulator/beam/erl_thr_queue.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2013. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_thr_queue.h b/erts/emulator/beam/erl_thr_queue.h index 27a6d03224..68356582e5 100644 --- a/erts/emulator/beam/erl_thr_queue.h +++ b/erts/emulator/beam/erl_thr_queue.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2013. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index e689547d1d..eccd49f2a9 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 5242063550..a1c4220633 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2011. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index f3f528eaf6..947def7398 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2015. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 41876b8c62..2defd0a2eb 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2014. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index a0058264d7..1da27ee128 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2013. All Rights Reserved. + * Copyright Ericsson AB 2012-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index 36d85b0a22..3c3536c021 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2013. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_unicode.h b/erts/emulator/beam/erl_unicode.h index 4c25d89b7c..e01eaa787e 100644 --- a/erts/emulator/beam/erl_unicode.h +++ b/erts/emulator/beam/erl_unicode.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2009. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_unicode_normalize.h b/erts/emulator/beam/erl_unicode_normalize.h index 16c62db50e..21e2a52544 100644 --- a/erts/emulator/beam/erl_unicode_normalize.h +++ b/erts/emulator/beam/erl_unicode_normalize.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2010. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 640969daf0..81800752f0 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2014. All Rights Reserved. + * Copyright Ericsson AB 2012-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 98f27a1725..6226522740 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_zlib.c b/erts/emulator/beam/erl_zlib.c index e44e86c39c..944ff2e35f 100644 --- a/erts/emulator/beam/erl_zlib.c +++ b/erts/emulator/beam/erl_zlib.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erl_zlib.h b/erts/emulator/beam/erl_zlib.h index 2ca1e3735e..c83c6f291f 100644 --- a/erts/emulator/beam/erl_zlib.h +++ b/erts/emulator/beam/erl_zlib.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d index c682f76e3a..73ef5a108a 100644 --- a/erts/emulator/beam/erlang_dtrace.d +++ b/erts/emulator/beam/erlang_dtrace.d @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 2011-2012. + * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 2011-2016. * All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/erts/emulator/beam/error.h b/erts/emulator/beam/error.h index cba8672c68..6c33b12dd0 100644 --- a/erts/emulator/beam/error.h +++ b/erts/emulator/beam/error.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2010. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c index 581efe6eec..02c24557c1 100644 --- a/erts/emulator/beam/export.c +++ b/erts/emulator/beam/export.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h index a8bc9d2f66..8c81cbd410 100644 --- a/erts/emulator/beam/export.h +++ b/erts/emulator/beam/export.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index aac1490f0c..6bb62d1040 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index d12051c6b4..52a0757a1c 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index d7edf451be..1d3704f0af 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c index 5a0b93f693..e255b961f1 100644 --- a/erts/emulator/beam/hash.c +++ b/erts/emulator/beam/hash.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/hash.h b/erts/emulator/beam/hash.h index e94aaa0a84..9f773d8faa 100644 --- a/erts/emulator/beam/hash.h +++ b/erts/emulator/beam/hash.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c index 5f6ea14732..26d6c04ea0 100644 --- a/erts/emulator/beam/index.c +++ b/erts/emulator/beam/index.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2012. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/index.h b/erts/emulator/beam/index.h index 99b2bdfab0..218779c33b 100644 --- a/erts/emulator/beam/index.h +++ b/erts/emulator/beam/index.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 797e4cdadc..6179720f97 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c index f5c7b177d3..3eb11f1693 100644 --- a/erts/emulator/beam/module.c +++ b/erts/emulator/beam/module.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h index b7468b0926..4e12731d85 100644 --- a/erts/emulator/beam/module.h +++ b/erts/emulator/beam/module.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 9e53b4bfcc..a1f021d7e0 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2013. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c index a737a86f14..f14910bc72 100644 --- a/erts/emulator/beam/packet_parser.c +++ b/erts/emulator/beam/packet_parser.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2013. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/packet_parser.h b/erts/emulator/beam/packet_parser.h index 717d905fad..358d650804 100644 --- a/erts/emulator/beam/packet_parser.h +++ b/erts/emulator/beam/packet_parser.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2009. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c index ebc35b0c4d..071f6aee08 100644 --- a/erts/emulator/beam/register.c +++ b/erts/emulator/beam/register.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/register.h b/erts/emulator/beam/register.h index 144536f34b..88ab7b7bf1 100644 --- a/erts/emulator/beam/register.h +++ b/erts/emulator/beam/register.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2012. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/safe_hash.c b/erts/emulator/beam/safe_hash.c index 3f039c8dfd..30b26a7296 100644 --- a/erts/emulator/beam/safe_hash.c +++ b/erts/emulator/beam/safe_hash.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2011. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/safe_hash.h b/erts/emulator/beam/safe_hash.h index a11370813c..6910b33004 100644 --- a/erts/emulator/beam/safe_hash.h +++ b/erts/emulator/beam/safe_hash.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2009. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 03b9088adc..52624a76e8 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index 0ce4f443f3..6f15082130 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 66fcfcf6ce..384b340a1c 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/beam/version.h b/erts/emulator/beam/version.h index 004725eaf5..0fa775fe8c 100644 --- a/erts/emulator/beam/version.h +++ b/erts/emulator/beam/version.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 3088dfd572..a9adbfb8be 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h index be5a891486..42300c9ba4 100644 --- a/erts/emulator/drivers/common/erl_efile.h +++ b/erts/emulator/drivers/common/erl_efile.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2013. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/common/gzio.h b/erts/emulator/drivers/common/gzio.h index a6fe2fb6f5..ee0ebe7bd8 100644 --- a/erts/emulator/drivers/common/gzio.h +++ b/erts/emulator/drivers/common/gzio.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/common/gzio_zutil.h b/erts/emulator/drivers/common/gzio_zutil.h index 9eefb86637..b229ae4efd 100644 --- a/erts/emulator/drivers/common/gzio_zutil.h +++ b/erts/emulator/drivers/common/gzio_zutil.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index b219669ce5..e87d141ddb 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2015. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/common/ram_file_drv.c b/erts/emulator/drivers/common/ram_file_drv.c index 9cb44d0b7e..bcdfe6a186 100644 --- a/erts/emulator/drivers/common/ram_file_drv.c +++ b/erts/emulator/drivers/common/ram_file_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2011. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/common/zlib_drv.c b/erts/emulator/drivers/common/zlib_drv.c index 364048174c..440ba956d8 100644 --- a/erts/emulator/drivers/common/zlib_drv.c +++ b/erts/emulator/drivers/common/zlib_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/unix/bin_drv.c b/erts/emulator/drivers/unix/bin_drv.c index 21fb398907..4b633bb0cf 100644 --- a/erts/emulator/drivers/unix/bin_drv.c +++ b/erts/emulator/drivers/unix/bin_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/unix/multi_drv.c b/erts/emulator/drivers/unix/multi_drv.c index 7f8c2d9a0d..eddc57d4d4 100644 --- a/erts/emulator/drivers/unix/multi_drv.c +++ b/erts/emulator/drivers/unix/multi_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/unix/sig_drv.c b/erts/emulator/drivers/unix/sig_drv.c index e6f2ecc494..68ad6b9156 100644 --- a/erts/emulator/drivers/unix/sig_drv.c +++ b/erts/emulator/drivers/unix/sig_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index abfc52d3bc..a925de4dfd 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index ac9b681d03..1949007222 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2013. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/vxworks/vxworks_resolv.c b/erts/emulator/drivers/vxworks/vxworks_resolv.c index a7de53c692..c2cdf4a90b 100644 --- a/erts/emulator/drivers/vxworks/vxworks_resolv.c +++ b/erts/emulator/drivers/vxworks/vxworks_resolv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/win32/registry_drv.c b/erts/emulator/drivers/win32/registry_drv.c index 4e396aa8d9..a03a030df8 100644 --- a/erts/emulator/drivers/win32/registry_drv.c +++ b/erts/emulator/drivers/win32/registry_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2013. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/win32/ttsl_drv.c b/erts/emulator/drivers/win32/ttsl_drv.c index 4bd766a8a8..99e7fb25a4 100644 --- a/erts/emulator/drivers/win32/ttsl_drv.c +++ b/erts/emulator/drivers/win32/ttsl_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c index 7fe708dc7b..0bcccfe405 100644 --- a/erts/emulator/drivers/win32/win_con.c +++ b/erts/emulator/drivers/win32/win_con.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2012. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/win32/win_con.h b/erts/emulator/drivers/win32/win_con.h index 3f4e89a195..7a642cd7ed 100644 --- a/erts/emulator/drivers/win32/win_con.h +++ b/erts/emulator/drivers/win32/win_con.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2009. All Rights Reserved. + * Copyright Ericsson AB 2007-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index d95f29f49d..2d366b5833 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2014. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/TODO b/erts/emulator/hipe/TODO index dd9760ea1c..0c28616a30 100644 --- a/erts/emulator/hipe/TODO +++ b/erts/emulator/hipe/TODO @@ -1,7 +1,7 @@ %CopyrightBegin% - Copyright Ericsson AB 2004-2009. All Rights Reserved. + Copyright Ericsson AB 2004-2016. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/elf64ppc.x b/erts/emulator/hipe/elf64ppc.x index 3cd4010cb3..46d2632970 100644 --- a/erts/emulator/hipe/elf64ppc.x +++ b/erts/emulator/hipe/elf64ppc.x @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_amd64.c b/erts/emulator/hipe/hipe_amd64.c index 1bb336637d..55bfd26b29 100644 --- a/erts/emulator/hipe/hipe_amd64.c +++ b/erts/emulator/hipe/hipe_amd64.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_amd64.h b/erts/emulator/hipe/hipe_amd64.h index 09b55a1243..750a26fcb9 100644 --- a/erts/emulator/hipe/hipe_amd64.h +++ b/erts/emulator/hipe/hipe_amd64.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_amd64.tab b/erts/emulator/hipe/hipe_amd64.tab index 70287034a0..e5ff31b985 100644 --- a/erts/emulator/hipe/hipe_amd64.tab +++ b/erts/emulator/hipe/hipe_amd64.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2011. All Rights Reserved. +# Copyright Ericsson AB 2004-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_amd64_asm.m4 b/erts/emulator/hipe/hipe_amd64_asm.m4 index ecb25ec5c8..2c0fbbee2d 100644 --- a/erts/emulator/hipe/hipe_amd64_asm.m4 +++ b/erts/emulator/hipe/hipe_amd64_asm.m4 @@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4 index 602e71c195..9cf3bf74fd 100644 --- a/erts/emulator/hipe/hipe_amd64_bifs.m4 +++ b/erts/emulator/hipe/hipe_amd64_bifs.m4 @@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2012. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_amd64_gc.h b/erts/emulator/hipe/hipe_amd64_gc.h index e7a156c910..6b95d54904 100644 --- a/erts/emulator/hipe/hipe_amd64_gc.h +++ b/erts/emulator/hipe/hipe_amd64_gc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S index 6fb93bce53..b37ed3c68a 100644 --- a/erts/emulator/hipe/hipe_amd64_glue.S +++ b/erts/emulator/hipe/hipe_amd64_glue.S @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_amd64_glue.h b/erts/emulator/hipe/hipe_amd64_glue.h index e54b8b3a3f..089658f4b5 100644 --- a/erts/emulator/hipe/hipe_amd64_glue.h +++ b/erts/emulator/hipe/hipe_amd64_glue.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_amd64_primops.h b/erts/emulator/hipe/hipe_amd64_primops.h index 23d8202e46..94c39a9433 100644 --- a/erts/emulator/hipe/hipe_amd64_primops.h +++ b/erts/emulator/hipe/hipe_amd64_primops.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_arch.h b/erts/emulator/hipe/hipe_arch.h index 21dabc7cb6..6f959815bb 100644 --- a/erts/emulator/hipe/hipe_arch.h +++ b/erts/emulator/hipe/hipe_arch.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2011. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c index 01512e9aa1..f8ef468341 100644 --- a/erts/emulator/hipe/hipe_arm.c +++ b/erts/emulator/hipe/hipe_arm.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2012. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_arm.h b/erts/emulator/hipe/hipe_arm.h index a0e07cd6ce..b5fe6317c9 100644 --- a/erts/emulator/hipe/hipe_arm.h +++ b/erts/emulator/hipe/hipe_arm.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_arm.tab b/erts/emulator/hipe/hipe_arm.tab index bcac2cd79d..152319edbd 100644 --- a/erts/emulator/hipe/hipe_arm.tab +++ b/erts/emulator/hipe/hipe_arm.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2011. All Rights Reserved. +# Copyright Ericsson AB 2005-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_arm_asm.m4 b/erts/emulator/hipe/hipe_arm_asm.m4 index 8ce8600f97..ae9ec752bb 100644 --- a/erts/emulator/hipe/hipe_arm_asm.m4 +++ b/erts/emulator/hipe/hipe_arm_asm.m4 @@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4 index 3efe961964..d9c9952dbf 100644 --- a/erts/emulator/hipe/hipe_arm_bifs.m4 +++ b/erts/emulator/hipe/hipe_arm_bifs.m4 @@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2012. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_arm_gc.h b/erts/emulator/hipe/hipe_arm_gc.h index dfffd6c630..e8eb900978 100644 --- a/erts/emulator/hipe/hipe_arm_gc.h +++ b/erts/emulator/hipe/hipe_arm_gc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S index e4ce9b75ef..49ffa8b1d8 100644 --- a/erts/emulator/hipe/hipe_arm_glue.S +++ b/erts/emulator/hipe/hipe_arm_glue.S @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_arm_glue.h b/erts/emulator/hipe/hipe_arm_glue.h index 39bc27ec5f..dffc5aef03 100644 --- a/erts/emulator/hipe/hipe_arm_glue.h +++ b/erts/emulator/hipe/hipe_arm_glue.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_arm_primops.h b/erts/emulator/hipe/hipe_arm_primops.h index 0e8f84dfb9..c080b67699 100644 --- a/erts/emulator/hipe/hipe_arm_primops.h +++ b/erts/emulator/hipe/hipe_arm_primops.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 70354aa9af..48cfafb8c5 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif0.h b/erts/emulator/hipe/hipe_bif0.h index 5527c6c3ed..c9a8216368 100644 --- a/erts/emulator/hipe/hipe_bif0.h +++ b/erts/emulator/hipe/hipe_bif0.h @@ -2,7 +2,7 @@ * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab index 5ce254314a..99237aae05 100644 --- a/erts/emulator/hipe/hipe_bif0.tab +++ b/erts/emulator/hipe/hipe_bif0.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2011. All Rights Reserved. +# Copyright Ericsson AB 2001-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif1.c b/erts/emulator/hipe/hipe_bif1.c index a2682992a4..08adbd474e 100644 --- a/erts/emulator/hipe/hipe_bif1.c +++ b/erts/emulator/hipe/hipe_bif1.c @@ -2,7 +2,7 @@ * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2012. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif1.h b/erts/emulator/hipe/hipe_bif1.h index 80cdeb4a82..e12bb1a1f3 100644 --- a/erts/emulator/hipe/hipe_bif1.h +++ b/erts/emulator/hipe/hipe_bif1.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif1.tab b/erts/emulator/hipe/hipe_bif1.tab index 6c4e05afdb..c5b452f199 100644 --- a/erts/emulator/hipe/hipe_bif1.tab +++ b/erts/emulator/hipe/hipe_bif1.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2009. All Rights Reserved. +# Copyright Ericsson AB 2001-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c index 2328e69886..dfd34e31d4 100644 --- a/erts/emulator/hipe/hipe_bif2.c +++ b/erts/emulator/hipe/hipe_bif2.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2012. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif2.tab b/erts/emulator/hipe/hipe_bif2.tab index a29e1fbdbb..bbcb577be0 100644 --- a/erts/emulator/hipe/hipe_bif2.tab +++ b/erts/emulator/hipe/hipe_bif2.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2012. All Rights Reserved. +# Copyright Ericsson AB 2001-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif64.c b/erts/emulator/hipe/hipe_bif64.c index b97cf2b7ee..a1a3c81bfe 100644 --- a/erts/emulator/hipe/hipe_bif64.c +++ b/erts/emulator/hipe/hipe_bif64.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif64.h b/erts/emulator/hipe/hipe_bif64.h index 856b4003dc..a97ce311d3 100644 --- a/erts/emulator/hipe/hipe_bif64.h +++ b/erts/emulator/hipe/hipe_bif64.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif64.tab b/erts/emulator/hipe/hipe_bif64.tab index 3062ab5848..eefca379a0 100644 --- a/erts/emulator/hipe/hipe_bif64.tab +++ b/erts/emulator/hipe/hipe_bif64.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2011. All Rights Reserved. +# Copyright Ericsson AB 2004-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 7240280345..29095a5389 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2014. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c index a48578c468..ba41f34012 100644 --- a/erts/emulator/hipe/hipe_debug.c +++ b/erts/emulator/hipe/hipe_debug.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_debug.h b/erts/emulator/hipe/hipe_debug.h index bf10d5a2a8..c510eac949 100644 --- a/erts/emulator/hipe/hipe_debug.h +++ b/erts/emulator/hipe/hipe_debug.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_gbif_list.h b/erts/emulator/hipe/hipe_gbif_list.h index dc93741a1d..424b001298 100644 --- a/erts/emulator/hipe/hipe_gbif_list.h +++ b/erts/emulator/hipe/hipe_gbif_list.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c index 2e19bf88bf..d0619a0609 100644 --- a/erts/emulator/hipe/hipe_gc.c +++ b/erts/emulator/hipe/hipe_gc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2012. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_gc.h b/erts/emulator/hipe/hipe_gc.h index e373324cdd..5b5eb29175 100644 --- a/erts/emulator/hipe/hipe_gc.h +++ b/erts/emulator/hipe/hipe_gc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index dfa5313739..0d3493ec6c 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2012. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 804c458b6c..43144e75ec 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -2,7 +2,7 @@ * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2014. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h index 620cc6356b..c40077d558 100644 --- a/erts/emulator/hipe/hipe_mode_switch.h +++ b/erts/emulator/hipe/hipe_mode_switch.h @@ -2,7 +2,7 @@ * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index 0ec417999b..00b572029a 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_native_bif.h b/erts/emulator/hipe/hipe_native_bif.h index 55a0d3bb1b..a02d26087b 100644 --- a/erts/emulator/hipe/hipe_native_bif.h +++ b/erts/emulator/hipe/hipe_native_bif.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_ops.tab b/erts/emulator/hipe/hipe_ops.tab index d021c72ac9..96e4c0da91 100644 --- a/erts/emulator/hipe/hipe_ops.tab +++ b/erts/emulator/hipe/hipe_ops.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2011. All Rights Reserved. +# Copyright Ericsson AB 2001-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_ppc.c b/erts/emulator/hipe/hipe_ppc.c index 3f86de626d..9b2048c457 100644 --- a/erts/emulator/hipe/hipe_ppc.c +++ b/erts/emulator/hipe/hipe_ppc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_ppc.h b/erts/emulator/hipe/hipe_ppc.h index 777c9384d5..e86c122fb1 100644 --- a/erts/emulator/hipe/hipe_ppc.h +++ b/erts/emulator/hipe/hipe_ppc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_ppc.tab b/erts/emulator/hipe/hipe_ppc.tab index 5bf2320cbe..274612d9ce 100644 --- a/erts/emulator/hipe/hipe_ppc.tab +++ b/erts/emulator/hipe/hipe_ppc.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2011. All Rights Reserved. +# Copyright Ericsson AB 2004-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_ppc64.tab b/erts/emulator/hipe/hipe_ppc64.tab index 301a9752f7..a291185b57 100644 --- a/erts/emulator/hipe/hipe_ppc64.tab +++ b/erts/emulator/hipe/hipe_ppc64.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2011. All Rights Reserved. +# Copyright Ericsson AB 2005-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_ppc_asm.m4 b/erts/emulator/hipe/hipe_ppc_asm.m4 index ea74923d1f..be25d65725 100644 --- a/erts/emulator/hipe/hipe_ppc_asm.m4 +++ b/erts/emulator/hipe/hipe_ppc_asm.m4 @@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4 index 28518827ec..57b4208bee 100644 --- a/erts/emulator/hipe/hipe_ppc_bifs.m4 +++ b/erts/emulator/hipe/hipe_ppc_bifs.m4 @@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2012. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_ppc_gc.h b/erts/emulator/hipe/hipe_ppc_gc.h index 0854d9e950..a6ec1338ff 100644 --- a/erts/emulator/hipe/hipe_ppc_gc.h +++ b/erts/emulator/hipe/hipe_ppc_gc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_ppc_glue.S b/erts/emulator/hipe/hipe_ppc_glue.S index 6e2022603c..44351dc06c 100644 --- a/erts/emulator/hipe/hipe_ppc_glue.S +++ b/erts/emulator/hipe/hipe_ppc_glue.S @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_ppc_glue.h b/erts/emulator/hipe/hipe_ppc_glue.h index 93ca39fcb3..2bfa10298c 100644 --- a/erts/emulator/hipe/hipe_ppc_glue.h +++ b/erts/emulator/hipe/hipe_ppc_glue.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_ppc_primops.h b/erts/emulator/hipe/hipe_ppc_primops.h index ce3dda9eb3..540605de9f 100644 --- a/erts/emulator/hipe/hipe_ppc_primops.h +++ b/erts/emulator/hipe/hipe_ppc_primops.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_primops.h b/erts/emulator/hipe/hipe_primops.h index 0bec677574..4fcbc9df38 100644 --- a/erts/emulator/hipe/hipe_primops.h +++ b/erts/emulator/hipe/hipe_primops.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_process.h b/erts/emulator/hipe/hipe_process.h index 60d09ea1c9..21c4239753 100644 --- a/erts/emulator/hipe/hipe_process.h +++ b/erts/emulator/hipe/hipe_process.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_risc_gc.h b/erts/emulator/hipe/hipe_risc_gc.h index 770ec93459..315f8e7f9f 100644 --- a/erts/emulator/hipe/hipe_risc_gc.h +++ b/erts/emulator/hipe/hipe_risc_gc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2011. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_risc_glue.h b/erts/emulator/hipe/hipe_risc_glue.h index 51db7eac01..0284265307 100644 --- a/erts/emulator/hipe/hipe_risc_glue.h +++ b/erts/emulator/hipe/hipe_risc_glue.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2011. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_risc_stack.c b/erts/emulator/hipe/hipe_risc_stack.c index fd04421381..dc98c96b8f 100644 --- a/erts/emulator/hipe/hipe_risc_stack.c +++ b/erts/emulator/hipe/hipe_risc_stack.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2011. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_signal.h b/erts/emulator/hipe/hipe_signal.h index b2f461797d..5d8621135b 100644 --- a/erts/emulator/hipe/hipe_signal.h +++ b/erts/emulator/hipe/hipe_signal.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2011. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_sparc.c b/erts/emulator/hipe/hipe_sparc.c index bd95bc5d98..23020f34ee 100644 --- a/erts/emulator/hipe/hipe_sparc.c +++ b/erts/emulator/hipe/hipe_sparc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2011. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_sparc.h b/erts/emulator/hipe/hipe_sparc.h index 0b74b2db26..25d513e988 100644 --- a/erts/emulator/hipe/hipe_sparc.h +++ b/erts/emulator/hipe/hipe_sparc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2011. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_sparc.tab b/erts/emulator/hipe/hipe_sparc.tab index 2f528c0607..bc90d38168 100644 --- a/erts/emulator/hipe/hipe_sparc.tab +++ b/erts/emulator/hipe/hipe_sparc.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2011. All Rights Reserved. +# Copyright Ericsson AB 2004-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_sparc_asm.m4 b/erts/emulator/hipe/hipe_sparc_asm.m4 index 227a1658b4..8a9a516eab 100644 --- a/erts/emulator/hipe/hipe_sparc_asm.m4 +++ b/erts/emulator/hipe/hipe_sparc_asm.m4 @@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2011. All Rights Reserved. + * Copyright Ericsson AB 2007-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_sparc_bifs.m4 b/erts/emulator/hipe/hipe_sparc_bifs.m4 index a7bbe5b2cc..2e886ec1d1 100644 --- a/erts/emulator/hipe/hipe_sparc_bifs.m4 +++ b/erts/emulator/hipe/hipe_sparc_bifs.m4 @@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_sparc_gc.h b/erts/emulator/hipe/hipe_sparc_gc.h index 99e55df1ce..eea0268ed5 100644 --- a/erts/emulator/hipe/hipe_sparc_gc.h +++ b/erts/emulator/hipe/hipe_sparc_gc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_sparc_glue.S b/erts/emulator/hipe/hipe_sparc_glue.S index 7e47ffb52f..077db9b8ad 100644 --- a/erts/emulator/hipe/hipe_sparc_glue.S +++ b/erts/emulator/hipe/hipe_sparc_glue.S @@ -2,7 +2,7 @@ * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_sparc_glue.h b/erts/emulator/hipe/hipe_sparc_glue.h index 95ce1e5b4a..3a1795f408 100644 --- a/erts/emulator/hipe/hipe_sparc_glue.h +++ b/erts/emulator/hipe/hipe_sparc_glue.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_sparc_primops.h b/erts/emulator/hipe/hipe_sparc_primops.h index e42bcbad62..2ecfa1293e 100644 --- a/erts/emulator/hipe/hipe_sparc_primops.h +++ b/erts/emulator/hipe/hipe_sparc_primops.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_stack.c b/erts/emulator/hipe/hipe_stack.c index e088b45c57..e2e6eb74b1 100644 --- a/erts/emulator/hipe/hipe_stack.c +++ b/erts/emulator/hipe/hipe_stack.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2011. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h index f575b97ce3..4ea7d5c031 100644 --- a/erts/emulator/hipe/hipe_stack.h +++ b/erts/emulator/hipe/hipe_stack.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2012. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86.c b/erts/emulator/hipe/hipe_x86.c index 19b70afced..3d25646231 100644 --- a/erts/emulator/hipe/hipe_x86.c +++ b/erts/emulator/hipe/hipe_x86.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86.h b/erts/emulator/hipe/hipe_x86.h index 30dc5666ae..8967793171 100644 --- a/erts/emulator/hipe/hipe_x86.h +++ b/erts/emulator/hipe/hipe_x86.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2011. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86.tab b/erts/emulator/hipe/hipe_x86.tab index 55fb03cde8..b922f05165 100644 --- a/erts/emulator/hipe/hipe_x86.tab +++ b/erts/emulator/hipe/hipe_x86.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2011. All Rights Reserved. +# Copyright Ericsson AB 2004-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86_abi.txt b/erts/emulator/hipe/hipe_x86_abi.txt index b74fcef127..31a4a0e121 100644 --- a/erts/emulator/hipe/hipe_x86_abi.txt +++ b/erts/emulator/hipe/hipe_x86_abi.txt @@ -1,7 +1,7 @@ %CopyrightBegin% - Copyright Ericsson AB 2001-2011. All Rights Reserved. + Copyright Ericsson AB 2001-2016. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86_asm.m4 b/erts/emulator/hipe/hipe_x86_asm.m4 index 3457574622..91c60382eb 100644 --- a/erts/emulator/hipe/hipe_x86_asm.m4 +++ b/erts/emulator/hipe/hipe_x86_asm.m4 @@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2011. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4 index 0c0a8da6eb..b8ac5046d5 100644 --- a/erts/emulator/hipe/hipe_x86_bifs.m4 +++ b/erts/emulator/hipe/hipe_x86_bifs.m4 @@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2012. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86_gc.h b/erts/emulator/hipe/hipe_x86_gc.h index a8770ef4f9..c22b28c2d5 100644 --- a/erts/emulator/hipe/hipe_x86_gc.h +++ b/erts/emulator/hipe/hipe_x86_gc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2013. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86_glue.S b/erts/emulator/hipe/hipe_x86_glue.S index cf382c480d..8d6e377730 100644 --- a/erts/emulator/hipe/hipe_x86_glue.S +++ b/erts/emulator/hipe/hipe_x86_glue.S @@ -2,7 +2,7 @@ * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86_glue.h b/erts/emulator/hipe/hipe_x86_glue.h index 6c123e8938..818d7444e2 100644 --- a/erts/emulator/hipe/hipe_x86_glue.h +++ b/erts/emulator/hipe/hipe_x86_glue.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86_primops.h b/erts/emulator/hipe/hipe_x86_primops.h index 3e057059bc..68a284203e 100644 --- a/erts/emulator/hipe/hipe_x86_primops.h +++ b/erts/emulator/hipe/hipe_x86_primops.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index 0ecd13c4bc..50d08b96d3 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -2,7 +2,7 @@ * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2014. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/hipe/hipe_x86_stack.c b/erts/emulator/hipe/hipe_x86_stack.c index 6629713a05..f1559b1451 100644 --- a/erts/emulator/hipe/hipe_x86_stack.c +++ b/erts/emulator/hipe/hipe_x86_stack.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/internal_doc/dec.erl b/erts/emulator/internal_doc/dec.erl index e64e37a7d9..8ce83fa402 100644 --- a/erts/emulator/internal_doc/dec.erl +++ b/erts/emulator/internal_doc/dec.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/internal_doc/erl_ext_dist.txt b/erts/emulator/internal_doc/erl_ext_dist.txt index 23b7d0d8e5..0b9a783069 100644 --- a/erts/emulator/internal_doc/erl_ext_dist.txt +++ b/erts/emulator/internal_doc/erl_ext_dist.txt @@ -1,7 +1,7 @@ %CopyrightBegin% - Copyright Ericsson AB 1997-2009. All Rights Reserved. + Copyright Ericsson AB 1997-2016. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/erts/emulator/pcre/pcre.mk b/erts/emulator/pcre/pcre.mk index b844b77214..38b91237a2 100644 --- a/erts/emulator/pcre/pcre.mk +++ b/erts/emulator/pcre/pcre.mk @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2012. All Rights Reserved. +# Copyright Ericsson AB 2012-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index f87196d724..08eae03858 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_check_io.h b/erts/emulator/sys/common/erl_check_io.h index c8675a7d9d..14f1ea3f43 100644 --- a/erts/emulator/sys/common/erl_check_io.h +++ b/erts/emulator/sys/common/erl_check_io.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2011. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 889d6f3868..867ab6f3c1 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2013. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 67e131b53e..1341a0b7eb 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2013. All Rights Reserved. + * Copyright Ericsson AB 2013-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 286130bece..c8b72c6e77 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2013. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index 2acd8f8505..7615d19f09 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2013. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_mtrace_sys_wrap.c b/erts/emulator/sys/common/erl_mtrace_sys_wrap.c index a8c575835a..fc871f94f1 100644 --- a/erts/emulator/sys/common/erl_mtrace_sys_wrap.c +++ b/erts/emulator/sys/common/erl_mtrace_sys_wrap.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2009. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_os_monotonic_time_extender.c b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c index 367b22d989..d53190fdd5 100644 --- a/erts/emulator/sys/common/erl_os_monotonic_time_extender.c +++ b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2015. All Rights Reserved. + * Copyright Ericsson AB 2015-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 36ee94111c..e394d84f73 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index bc2c681876..c16122610d 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c index e292741bfa..79f87eb3a9 100644 --- a/erts/emulator/sys/common/erl_sys_common_misc.c +++ b/erts/emulator/sys/common/erl_sys_common_misc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/common/erl_util_queue.h b/erts/emulator/sys/common/erl_util_queue.h index b50e062dd5..73293e0225 100644 --- a/erts/emulator/sys/common/erl_util_queue.h +++ b/erts/emulator/sys/common/erl_util_queue.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2013. All Rights Reserved. + * Copyright Ericsson AB 2013-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/unix/driver_int.h b/erts/emulator/sys/unix/driver_int.h index a6b9085245..840b832878 100644 --- a/erts/emulator/sys/unix/driver_int.h +++ b/erts/emulator/sys/unix/driver_int.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 8dd14bbac6..6beb316350 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2015. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/unix/erl_main.c b/erts/emulator/sys/unix/erl_main.c index c417bea622..972b93a505 100644 --- a/erts/emulator/sys/unix/erl_main.c +++ b/erts/emulator/sys/unix/erl_main.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2009. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 8b1822ca9f..9f958abc34 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2011. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/unix/erl_unix_sys_ddll.c b/erts/emulator/sys/unix/erl_unix_sys_ddll.c index daed5af1b6..8f1ceac883 100644 --- a/erts/emulator/sys/unix/erl_unix_sys_ddll.c +++ b/erts/emulator/sys/unix/erl_unix_sys_ddll.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 0a18e54389..249c7a89b2 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 82fea1b330..ac39b8a389 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c index 8fe7e599e5..9d2a768778 100644 --- a/erts/emulator/sys/unix/sys_float.c +++ b/erts/emulator/sys/unix/sys_float.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index 8ce02506ab..60f8decd96 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/unix/sys_uds.c b/erts/emulator/sys/unix/sys_uds.c index 015d0346a1..dd0a3b03ff 100644 --- a/erts/emulator/sys/unix/sys_uds.c +++ b/erts/emulator/sys/unix/sys_uds.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2009. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/unix/sys_uds.h b/erts/emulator/sys/unix/sys_uds.h index 844a2804d8..a598102d5c 100644 --- a/erts/emulator/sys/unix/sys_uds.h +++ b/erts/emulator/sys/unix/sys_uds.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2009. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/dosmap.c b/erts/emulator/sys/win32/dosmap.c index a98065c666..95fbba384b 100644 --- a/erts/emulator/sys/win32/dosmap.c +++ b/erts/emulator/sys/win32/dosmap.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/driver_int.h b/erts/emulator/sys/win32/driver_int.h index b931da52f5..50097d3fd2 100644 --- a/erts/emulator/sys/win32/driver_int.h +++ b/erts/emulator/sys/win32/driver_int.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/erl_main.c b/erts/emulator/sys/win32/erl_main.c index 21d9a58da6..9eee300f48 100644 --- a/erts/emulator/sys/win32/erl_main.c +++ b/erts/emulator/sys/win32/erl_main.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2009. All Rights Reserved. + * Copyright Ericsson AB 2000-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 9c1bef5f4f..94f3840b5f 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2011. All Rights Reserved. + * Copyright Ericsson AB 2007-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/erl_win32_sys_ddll.c b/erts/emulator/sys/win32/erl_win32_sys_ddll.c index 7c24a77e31..274133a346 100644 --- a/erts/emulator/sys/win32/erl_win32_sys_ddll.c +++ b/erts/emulator/sys/win32/erl_win32_sys_ddll.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h index 9c699fdba0..6f28d513c2 100644 --- a/erts/emulator/sys/win32/erl_win_dyn_driver.h +++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 99c1066ab3..7bdfac168b 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2014. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index ea7523ddc2..93e073d78f 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2014. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c index 67b6e55377..21ef71ad9a 100644 --- a/erts/emulator/sys/win32/sys_env.c +++ b/erts/emulator/sys/win32/sys_env.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2012. All Rights Reserved. + * Copyright Ericsson AB 2002-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/sys_float.c b/erts/emulator/sys/win32/sys_float.c index 86e822da5b..a2b9bd1263 100644 --- a/erts/emulator/sys/win32/sys_float.c +++ b/erts/emulator/sys/win32/sys_float.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2013. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/sys_interrupt.c b/erts/emulator/sys/win32/sys_interrupt.c index a89211fa8e..df838960eb 100644 --- a/erts/emulator/sys/win32/sys_interrupt.c +++ b/erts/emulator/sys/win32/sys_interrupt.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2012. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index 9a9d90610d..e8c67b3928 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2013. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 318db4b45e..b42bac0ea8 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2012. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl index 5b3a213154..880c5a5821 100644 --- a/erts/emulator/test/a_SUITE.erl +++ b/erts/emulator/test/a_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl index a72cd8deea..4f20ad3656 100644 --- a/erts/emulator/test/after_SUITE.erl +++ b/erts/emulator/test/after_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 1f690c5015..6fa53f3587 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2013. All Rights Reserved. +%% Copyright Ericsson AB 2003-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/alloc_SUITE_data/cpool.c b/erts/emulator/test/alloc_SUITE_data/cpool.c index 73026cc758..0c41f4d747 100644 --- a/erts/emulator/test/alloc_SUITE_data/cpool.c +++ b/erts/emulator/test/alloc_SUITE_data/cpool.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2013. All Rights Reserved. + * Copyright Ericsson AB 2013-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/alloc_SUITE_data/migration.c b/erts/emulator/test/alloc_SUITE_data/migration.c index b006360043..b9a4de03b3 100644 --- a/erts/emulator/test/alloc_SUITE_data/migration.c +++ b/erts/emulator/test/alloc_SUITE_data/migration.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2014. All Rights Reserved. + * Copyright Ericsson AB 2014-2016. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl index 07dfeb6633..c55420666c 100644 --- a/erts/emulator/test/beam_SUITE.erl +++ b/erts/emulator/test/beam_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2013. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/beam_literals_SUITE.erl b/erts/emulator/test/beam_literals_SUITE.erl index b013ff5fbb..09761263e2 100644 --- a/erts/emulator/test/beam_literals_SUITE.erl +++ b/erts/emulator/test/beam_literals_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2015. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 43d00e699a..26bb416bf0 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl index d331083661..402751393a 100644 --- a/erts/emulator/test/big_SUITE.erl +++ b/erts/emulator/test/big_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/big_SUITE_data/literal_test.erl b/erts/emulator/test/big_SUITE_data/literal_test.erl index 1620693bfa..17c4db467a 100644 --- a/erts/emulator/test/big_SUITE_data/literal_test.erl +++ b/erts/emulator/test/big_SUITE_data/literal_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index fcd49e43d3..1c7d278bb0 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2014. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/bs_bincomp_SUITE.erl b/erts/emulator/test/bs_bincomp_SUITE.erl index 8836fe40ae..c481e93e41 100644 --- a/erts/emulator/test/bs_bincomp_SUITE.erl +++ b/erts/emulator/test/bs_bincomp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/bs_bit_binaries_SUITE.erl b/erts/emulator/test/bs_bit_binaries_SUITE.erl index dd056d10af..d393bc5b91 100644 --- a/erts/emulator/test/bs_bit_binaries_SUITE.erl +++ b/erts/emulator/test/bs_bit_binaries_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 8deb0c7422..941cb435f7 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/bs_match_bin_SUITE.erl b/erts/emulator/test/bs_match_bin_SUITE.erl index 6f7abf8c4e..f5c996ae9e 100644 --- a/erts/emulator/test/bs_match_bin_SUITE.erl +++ b/erts/emulator/test/bs_match_bin_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/bs_match_int_SUITE.erl b/erts/emulator/test/bs_match_int_SUITE.erl index 42d692c1ba..a7bd4b8ac3 100644 --- a/erts/emulator/test/bs_match_int_SUITE.erl +++ b/erts/emulator/test/bs_match_int_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/bs_match_misc_SUITE.erl b/erts/emulator/test/bs_match_misc_SUITE.erl index 114d7ecb36..17759d78f3 100644 --- a/erts/emulator/test/bs_match_misc_SUITE.erl +++ b/erts/emulator/test/bs_match_misc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/bs_match_tail_SUITE.erl b/erts/emulator/test/bs_match_tail_SUITE.erl index 8392a0df47..cbebc554c7 100644 --- a/erts/emulator/test/bs_match_tail_SUITE.erl +++ b/erts/emulator/test/bs_match_tail_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/bs_utf_SUITE.erl b/erts/emulator/test/bs_utf_SUITE.erl index f1c09bfbb0..a344f5c456 100644 --- a/erts/emulator/test/bs_utf_SUITE.erl +++ b/erts/emulator/test/bs_utf_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index c582f8d77b..bb0632ae08 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/busy_port_SUITE_data/Makefile.src b/erts/emulator/test/busy_port_SUITE_data/Makefile.src index 0f2842e515..ae6378a6ff 100644 --- a/erts/emulator/test/busy_port_SUITE_data/Makefile.src +++ b/erts/emulator/test/busy_port_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2013. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c b/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c index f83fa1eeaa..c4e0f13f06 100644 --- a/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c +++ b/erts/emulator/test/busy_port_SUITE_data/hard_busy_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c b/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c index be913cf56e..ffaca18e90 100644 --- a/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c +++ b/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c b/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c index 40e42b6ac2..296b3f21de 100644 --- a/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c +++ b/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c b/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c index 3c5bafb451..8a98f050f1 100644 --- a/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c +++ b/erts/emulator/test/busy_port_SUITE_data/soft_busy_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index 4ec18aa49e..2159546054 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index db285a3977..29b95ef674 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/code_SUITE_data/another_code_test.erl b/erts/emulator/test/code_SUITE_data/another_code_test.erl index f6f9e32996..5708ec682c 100644 --- a/erts/emulator/test/code_SUITE_data/another_code_test.erl +++ b/erts/emulator/test/code_SUITE_data/another_code_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/code_SUITE_data/cpbugx.erl b/erts/emulator/test/code_SUITE_data/cpbugx.erl index ea01ce411b..ae2075c867 100644 --- a/erts/emulator/test/code_SUITE_data/cpbugx.erl +++ b/erts/emulator/test/code_SUITE_data/cpbugx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/code_SUITE_data/fun_confusion.erl b/erts/emulator/test/code_SUITE_data/fun_confusion.erl index 8d42937d3c..35279f241d 100644 --- a/erts/emulator/test/code_SUITE_data/fun_confusion.erl +++ b/erts/emulator/test/code_SUITE_data/fun_confusion.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/code_SUITE_data/literals.erl b/erts/emulator/test/code_SUITE_data/literals.erl index a36bfe09dd..7c3b0ebe73 100644 --- a/erts/emulator/test/code_SUITE_data/literals.erl +++ b/erts/emulator/test/code_SUITE_data/literals.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/code_SUITE_data/many_funs.erl b/erts/emulator/test/code_SUITE_data/many_funs.erl index e832f271d0..ada570feee 100644 --- a/erts/emulator/test/code_SUITE_data/many_funs.erl +++ b/erts/emulator/test/code_SUITE_data/many_funs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/code_SUITE_data/my_code_test.erl b/erts/emulator/test/code_SUITE_data/my_code_test.erl index 57d867a5ac..d2386157d6 100644 --- a/erts/emulator/test/code_SUITE_data/my_code_test.erl +++ b/erts/emulator/test/code_SUITE_data/my_code_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/code_SUITE_data/versions.erl b/erts/emulator/test/code_SUITE_data/versions.erl index 0e2d92c8f1..56407e877a 100644 --- a/erts/emulator/test/code_SUITE_data/versions.erl +++ b/erts/emulator/test/code_SUITE_data/versions.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl index e9e7000434..827add71e5 100644 --- a/erts/emulator/test/code_parallel_load_SUITE.erl +++ b/erts/emulator/test/code_parallel_load_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012-2014. All Rights Reserved. +%% Copyright Ericsson AB 2012-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/crypto_SUITE.erl b/erts/emulator/test/crypto_SUITE.erl index 6212997272..afb1be7332 100644 --- a/erts/emulator/test/crypto_SUITE.erl +++ b/erts/emulator/test/crypto_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/crypto_reference.erl b/erts/emulator/test/crypto_reference.erl index 7797eacd75..950b0c1560 100644 --- a/erts/emulator/test/crypto_reference.erl +++ b/erts/emulator/test/crypto_reference.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl index 6982178827..93b6f2d956 100644 --- a/erts/emulator/test/ddll_SUITE.erl +++ b/erts/emulator/test/ddll_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl index ad592f7147..54ee4d5567 100644 --- a/erts/emulator/test/decode_packet_SUITE.erl +++ b/erts/emulator/test/decode_packet_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/dgawd_handler.erl b/erts/emulator/test/dgawd_handler.erl index bba69ef87e..52cdd26427 100644 --- a/erts/emulator/test/dgawd_handler.erl +++ b/erts/emulator/test/dgawd_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index f116ec979b..2fb74a9b7c 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/distribution_SUITE_data/run.erl b/erts/emulator/test/distribution_SUITE_data/run.erl index d5ed139369..f574b2c02c 100644 --- a/erts/emulator/test/distribution_SUITE_data/run.erl +++ b/erts/emulator/test/distribution_SUITE_data/run.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index c589470f5b..f134a197aa 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2014. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/driver_SUITE_data/async_blast_drv.c b/erts/emulator/test/driver_SUITE_data/async_blast_drv.c index a1008afcae..1432bc42c1 100644 --- a/erts/emulator/test/driver_SUITE_data/async_blast_drv.c +++ b/erts/emulator/test/driver_SUITE_data/async_blast_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2013. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c b/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c index 192ac02d3e..142ae46247 100644 --- a/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c +++ b/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2013. All Rights Reserved. + * Copyright Ericsson AB 2012-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c index e2221b9e17..d87c2bec93 100644 --- a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c +++ b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2013. All Rights Reserved. + * Copyright Ericsson AB 2007-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c index fdf8e4c0ad..37cb93fb3a 100644 --- a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c +++ b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2014. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/driver_SUITE_data/thr_free_drv.c b/erts/emulator/test/driver_SUITE_data/thr_free_drv.c index 54205f190e..48fe5fa435 100644 --- a/erts/emulator/test/driver_SUITE_data/thr_free_drv.c +++ b/erts/emulator/test/driver_SUITE_data/thr_free_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c b/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c index f7a7cc2b8e..56183c9484 100644 --- a/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c +++ b/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012. All Rights Reserved. + * Copyright Ericsson AB 2012-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index cefa35a3ee..6bb8487c4e 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl index 41a761229c..7529b65227 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE.erl +++ b/erts/emulator/test/erl_drv_thread_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index 56fdfc35c0..93d2065ba3 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl index e757ebdb3d..23871585f7 100644 --- a/erts/emulator/test/erts_debug_SUITE.erl +++ b/erts/emulator/test/erts_debug_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index 93728b299b..1180a45585 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2013. All Rights Reserved. +%% Copyright Ericsson AB 2002-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/evil_SUITE.erl b/erts/emulator/test/evil_SUITE.erl index 5f5a0ef305..9416ac7a02 100644 --- a/erts/emulator/test/evil_SUITE.erl +++ b/erts/emulator/test/evil_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index 1cad7e7920..76e3556bc4 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index 78dec8c725..e85addae3a 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl b/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl index 79ab74dfff..26837de274 100644 --- a/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl +++ b/erts/emulator/test/float_SUITE_data/has_fpe_bug.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 044c6aabca..26fa955e3c 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/fun_r13_SUITE.erl b/erts/emulator/test/fun_r13_SUITE.erl index d66026705b..a45ed08b9d 100644 --- a/erts/emulator/test/fun_r13_SUITE.erl +++ b/erts/emulator/test/fun_r13_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl index 8e5f0b05a1..79c229a34d 100644 --- a/erts/emulator/test/gc_SUITE.erl +++ b/erts/emulator/test/gc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/guard_SUITE.erl b/erts/emulator/test/guard_SUITE.erl index 6ad4456e6b..e155e5f49f 100644 --- a/erts/emulator/test/guard_SUITE.erl +++ b/erts/emulator/test/guard_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl index be254f5543..a39d101b0d 100644 --- a/erts/emulator/test/hash_SUITE.erl +++ b/erts/emulator/test/hash_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl index 45f0bdc4da..6f8ce02266 100644 --- a/erts/emulator/test/hibernate_SUITE.erl +++ b/erts/emulator/test/hibernate_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/ignore_cores.erl b/erts/emulator/test/ignore_cores.erl index 7373303a39..fde65bf5c4 100644 --- a/erts/emulator/test/ignore_cores.erl +++ b/erts/emulator/test/ignore_cores.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/list_bif_SUITE.erl b/erts/emulator/test/list_bif_SUITE.erl index 1f64a6a8e5..514dd2f412 100644 --- a/erts/emulator/test/list_bif_SUITE.erl +++ b/erts/emulator/test/list_bif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/long_timers_test.erl b/erts/emulator/test/long_timers_test.erl index 9415e1cced..7c055a31f9 100644 --- a/erts/emulator/test/long_timers_test.erl +++ b/erts/emulator/test/long_timers_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index baa6eb8fe0..6b8d14b487 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2014. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl index 12c42d24aa..6efca5b39e 100644 --- a/erts/emulator/test/message_queue_data_SUITE.erl +++ b/erts/emulator/test/message_queue_data_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2014. All Rights Reserved. +%% Copyright Ericsson AB 2014-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index f8208c5866..ba9b564fdc 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 82e3a36c1e..8955e62df5 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl index a17b11f3bf..1493e52655 100644 --- a/erts/emulator/test/mtx_SUITE.erl +++ b/erts/emulator/test/mtx_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/mtx_SUITE_data/Makefile.src b/erts/emulator/test/mtx_SUITE_data/Makefile.src index dc880118f1..1816dc6798 100644 --- a/erts/emulator/test/mtx_SUITE_data/Makefile.src +++ b/erts/emulator/test/mtx_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2013. All Rights Reserved. +# Copyright Ericsson AB 2010-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c b/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c index 1911291448..e011aadce9 100644 --- a/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c +++ b/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/multi_load_SUITE.erl b/erts/emulator/test/multi_load_SUITE.erl index 784b239116..737215d090 100644 --- a/erts/emulator/test/multi_load_SUITE.erl +++ b/erts/emulator/test/multi_load_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/nested_SUITE.erl b/erts/emulator/test/nested_SUITE.erl index c13e19f857..7af2873ce2 100644 --- a/erts/emulator/test/nested_SUITE.erl +++ b/erts/emulator/test/nested_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 84f5699890..e12aa4f950 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2014. All Rights Reserved. +%% Copyright Ericsson AB 2010-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 1acb270d1f..72fe02cfdf 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2014. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c index f7e729e2b6..fd8a0d0595 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.c +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2014. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl index b6de046388..eec1bb8858 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 71400142af..6a1d2e2f0a 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2012. All Rights Reserved. +%% Copyright Ericsson AB 2002-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl index 9cc851f9cf..8b1519ae36 100644 --- a/erts/emulator/test/nofrag_SUITE.erl +++ b/erts/emulator/test/nofrag_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index 04a6f9d18d..d1c9648017 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2014. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/old_mod.erl b/erts/emulator/test/old_mod.erl index 0da40ec430..866aba79bb 100644 --- a/erts/emulator/test/old_mod.erl +++ b/erts/emulator/test/old_mod.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2010. All Rights Reserved. +%% Copyright Ericsson AB 2003-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl index 9d40417d0a..f91d84beea 100644 --- a/erts/emulator/test/old_scheduler_SUITE.erl +++ b/erts/emulator/test/old_scheduler_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl index 562cf1c92d..61637421ff 100644 --- a/erts/emulator/test/op_SUITE.erl +++ b/erts/emulator/test/op_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2015. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 328641f5b9..87fb0f0110 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2014. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/port_SUITE_data/dead_port.c b/erts/emulator/test/port_SUITE_data/dead_port.c index c859dbc402..26f09f33c7 100644 --- a/erts/emulator/test/port_SUITE_data/dead_port.c +++ b/erts/emulator/test/port_SUITE_data/dead_port.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2013. All Rights Reserved. + * Copyright Ericsson AB 2001-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/port_SUITE_data/port_test.erl b/erts/emulator/test/port_SUITE_data/port_test.erl index b07038e73d..406d376b26 100644 --- a/erts/emulator/test/port_SUITE_data/port_test.erl +++ b/erts/emulator/test/port_SUITE_data/port_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index 9b794be415..e1e1ec9fb9 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 5bb216ff79..007f6df5d0 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/pseudoknot_SUITE.erl b/erts/emulator/test/pseudoknot_SUITE.erl index 58ef3cd563..ed4d40ac65 100644 --- a/erts/emulator/test/pseudoknot_SUITE.erl +++ b/erts/emulator/test/pseudoknot_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/random_iolist.erl b/erts/emulator/test/random_iolist.erl index 6da7da04de..555f063e0a 100644 --- a/erts/emulator/test/random_iolist.erl +++ b/erts/emulator/test/random_iolist.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl index 6097e54219..83653a7a36 100644 --- a/erts/emulator/test/receive_SUITE.erl +++ b/erts/emulator/test/receive_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/ref_SUITE.erl b/erts/emulator/test/ref_SUITE.erl index 287a2ffb73..5f519d522e 100644 --- a/erts/emulator/test/ref_SUITE.erl +++ b/erts/emulator/test/ref_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/register_SUITE.erl b/erts/emulator/test/register_SUITE.erl index ad8a9c29f3..43ae749498 100644 --- a/erts/emulator/test/register_SUITE.erl +++ b/erts/emulator/test/register_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl index bbdc2e6688..afda3f0eae 100644 --- a/erts/emulator/test/save_calls_SUITE.erl +++ b/erts/emulator/test/save_calls_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 64c280b198..1341e43b0d 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index 75161389b1..8afe4e4ac1 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2014. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/sensitive_SUITE.erl b/erts/emulator/test/sensitive_SUITE.erl index e2236774a7..b7ff4c109c 100644 --- a/erts/emulator/test/sensitive_SUITE.erl +++ b/erts/emulator/test/sensitive_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index 63397fbbb4..0b11fa13f5 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 900dd124c0..042c7225d5 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2012. All Rights Reserved. +%% Copyright Ericsson AB 2011-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index 7d5b07f21f..71ef003b25 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index ffea5bc15b..f31d474c20 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl index 6d80d436ba..a56b394765 100644 --- a/erts/emulator/test/system_profile_SUITE.erl +++ b/erts/emulator/test/system_profile_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index e6da5e89fa..76d440529f 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index fa72700604..a5f11bd959 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2013. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index dcd5e95434..7aece926e3 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index b774210456..a2edf0662d 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2014. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/trace_call_count_SUITE.erl b/erts/emulator/test/trace_call_count_SUITE.erl index c849668e84..5f871835bc 100644 --- a/erts/emulator/test/trace_call_count_SUITE.erl +++ b/erts/emulator/test/trace_call_count_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 38972c9c02..40c8bc4340 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 24864dcbef..8901beebca 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl b/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl index a5947de4aa..a886323302 100644 --- a/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl +++ b/erts/emulator/test/trace_local_SUITE_data/trace_local_dummy.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl index dcc2cc807b..b6a6fd5404 100644 --- a/erts/emulator/test/trace_meta_SUITE.erl +++ b/erts/emulator/test/trace_meta_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/trace_nif_SUITE.erl b/erts/emulator/test/trace_nif_SUITE.erl index 49757a414d..8d5bff2a48 100644 --- a/erts/emulator/test/trace_nif_SUITE.erl +++ b/erts/emulator/test/trace_nif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index d33f18543a..98dc96bfa7 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/tuple_SUITE.erl b/erts/emulator/test/tuple_SUITE.erl index d59d7d12d3..79b681b4d1 100644 --- a/erts/emulator/test/tuple_SUITE.erl +++ b/erts/emulator/test/tuple_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl index e8537e6152..c5aa80c7b4 100644 --- a/erts/emulator/test/unique_SUITE.erl +++ b/erts/emulator/test/unique_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2014. All Rights Reserved. +%% Copyright Ericsson AB 2014-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index 93a03e8f91..d1085c1958 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index f805e7cc64..78183f613c 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1998-2012. All Rights Reserved. +# Copyright Ericsson AB 1998-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/utils/beam_strip b/erts/emulator/utils/beam_strip index 0e7bc46b63..2a2d761940 100755 --- a/erts/emulator/utils/beam_strip +++ b/erts/emulator/utils/beam_strip @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2009. All Rights Reserved. +# Copyright Ericsson AB 2001-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/utils/count b/erts/emulator/utils/count index c4dd42c949..e0a5e8bb9a 100755 --- a/erts/emulator/utils/count +++ b/erts/emulator/utils/count @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/utils/loaded b/erts/emulator/utils/loaded index ab77ffdb6c..5e0a29c014 100644 --- a/erts/emulator/utils/loaded +++ b/erts/emulator/utils/loaded @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/emulator/utils/make_alloc_types b/erts/emulator/utils/make_alloc_types index 925b9d5810..33afe139a2 100755 --- a/erts/emulator/utils/make_alloc_types +++ b/erts/emulator/utils/make_alloc_types @@ -3,7 +3,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2009. All Rights Reserved. +# Copyright Ericsson AB 2003-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/utils/make_compiler_flags b/erts/emulator/utils/make_compiler_flags index d75ea0817e..47491a4832 100755 --- a/erts/emulator/utils/make_compiler_flags +++ b/erts/emulator/utils/make_compiler_flags @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# Copyright Ericsson AB 1999-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/utils/make_driver_tab b/erts/emulator/utils/make_driver_tab index 3203c7110a..646959e8b2 100755 --- a/erts/emulator/utils/make_driver_tab +++ b/erts/emulator/utils/make_driver_tab @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2013. All Rights Reserved. +# Copyright Ericsson AB 1999-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/utils/make_preload b/erts/emulator/utils/make_preload index 62c4419589..f489bc2a39 100755 --- a/erts/emulator/utils/make_preload +++ b/erts/emulator/utils/make_preload @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2012. All Rights Reserved. +# Copyright Ericsson AB 1999-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/utils/make_tables b/erts/emulator/utils/make_tables index 233e95f176..c158778f43 100755 --- a/erts/emulator/utils/make_tables +++ b/erts/emulator/utils/make_tables @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2013. All Rights Reserved. +# Copyright Ericsson AB 1999-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/utils/make_version b/erts/emulator/utils/make_version index 37bdff181a..e02a42c66d 100755 --- a/erts/emulator/utils/make_version +++ b/erts/emulator/utils/make_version @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# Copyright Ericsson AB 1999-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/emulator/utils/mkver.c b/erts/emulator/utils/mkver.c index 6641873712..6183246433 100644 --- a/erts/emulator/utils/mkver.c +++ b/erts/emulator/utils/mkver.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/emulator/zlib/zlib.mk b/erts/emulator/zlib/zlib.mk index 53d7badd64..3f0d64d250 100644 --- a/erts/emulator/zlib/zlib.mk +++ b/erts/emulator/zlib/zlib.mk @@ -4,7 +4,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2011-2012. All Rights Reserved. +# Copyright Ericsson AB 2011-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/epmd/Makefile b/erts/epmd/Makefile index 25a33462ee..d3308ddedc 100644 --- a/erts/epmd/Makefile +++ b/erts/epmd/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1998-2009. All Rights Reserved. +# Copyright Ericsson AB 1998-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/epmd/epmd.mk b/erts/epmd/epmd.mk index 08245b784e..b1fd04dc04 100644 --- a/erts/epmd/epmd.mk +++ b/erts/epmd/epmd.mk @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1998-2009. All Rights Reserved. +# Copyright Ericsson AB 1998-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/epmd/src/Makefile b/erts/epmd/src/Makefile index 3e09a40566..4ae13fe05a 100644 --- a/erts/epmd/src/Makefile +++ b/erts/epmd/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1998-2009. All Rights Reserved. +# Copyright Ericsson AB 1998-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in index 1266be44cb..da4370d5f9 100644 --- a/erts/epmd/src/Makefile.in +++ b/erts/epmd/src/Makefile.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1998-2012. All Rights Reserved. +# Copyright Ericsson AB 1998-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 324ae6ac40..b36f4ccd40 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -2,7 +2,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/epmd/src/epmd.h b/erts/epmd/src/epmd.h index 10483bb5a2..cffcd4ae7a 100644 --- a/erts/epmd/src/epmd.h +++ b/erts/epmd/src/epmd.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2010. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c index 6fc05e153e..6fd27d46ea 100644 --- a/erts/epmd/src/epmd_cli.c +++ b/erts/epmd/src/epmd_cli.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index e127eff097..26b3e3379d 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -2,7 +2,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 75a33f28cb..38ed137575 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -2,7 +2,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/epmd/test/Makefile b/erts/epmd/test/Makefile index 7c5302a2f1..ad1315440f 100644 --- a/erts/epmd/test/Makefile +++ b/erts/epmd/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1998-2012. All Rights Reserved. +# Copyright Ericsson AB 1998-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 44d3c5a880..763984267a 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2013. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/etc/Makefile b/erts/etc/Makefile index 9a14cee89c..788dfff132 100644 --- a/erts/etc/Makefile +++ b/erts/etc/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# Copyright Ericsson AB 1999-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/common/Makefile b/erts/etc/common/Makefile index bbf51d0efd..bad1d3ddb0 100644 --- a/erts/etc/common/Makefile +++ b/erts/etc/common/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2009. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index 05d925f19f..cb053a1b7c 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2014. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/common/ct_run.c b/erts/etc/common/ct_run.c index 548514ee6c..ca5cc27d2f 100644 --- a/erts/etc/common/ct_run.c +++ b/erts/etc/common/ct_run.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2013. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c index c45626606c..6072f632d6 100644 --- a/erts/etc/common/dialyzer.c +++ b/erts/etc/common/dialyzer.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c index f9d909e01c..6fb490950e 100644 --- a/erts/etc/common/erlc.c +++ b/erts/etc/common/erlc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2013. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 54da59e50d..086c5af8c7 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c index 7fd02ed436..3b245d79d6 100644 --- a/erts/etc/common/escript.c +++ b/erts/etc/common/escript.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2013. All Rights Reserved. + * Copyright Ericsson AB 2007-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index 1a826221fb..e931ae4641 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c index e298c5e7f7..cffb2bfd1c 100644 --- a/erts/etc/common/inet_gethost.c +++ b/erts/etc/common/inet_gethost.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2012. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/common/typer.c b/erts/etc/common/typer.c index 0aa0996808..c3e49d8a27 100644 --- a/erts/etc/common/typer.c +++ b/erts/etc/common/typer.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2011. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src index 6634ae31d3..e71308edbe 100644 --- a/erts/etc/unix/Install.src +++ b/erts/etc/unix/Install.src @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2013. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/Makefile b/erts/etc/unix/Makefile index 04ae11de3b..2fa9cd047b 100644 --- a/erts/etc/unix/Makefile +++ b/erts/etc/unix/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2013. All Rights Reserved. +# Copyright Ericsson AB 2013-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/README b/erts/etc/unix/README index 6bda610a03..adc6db4300 100644 --- a/erts/etc/unix/README +++ b/erts/etc/unix/README @@ -1,7 +1,7 @@ %CopyrightBegin% - Copyright Ericsson AB 1996-2009. All Rights Reserved. + Copyright Ericsson AB 1996-2016. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/RELNOTES b/erts/etc/unix/RELNOTES index 629867d2ae..7b4a1746fe 100644 --- a/erts/etc/unix/RELNOTES +++ b/erts/etc/unix/RELNOTES @@ -1,7 +1,7 @@ %CopyrightBegin% - Copyright Ericsson AB 1996-2009. All Rights Reserved. + Copyright Ericsson AB 1996-2016. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 2a806bb2f1..c5422ab2ed 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2013. All Rights Reserved. +# Copyright Ericsson AB 2003-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/dyn_erl.c b/erts/etc/unix/dyn_erl.c index 4eebfae50a..d6d2201648 100644 --- a/erts/etc/unix/dyn_erl.c +++ b/erts/etc/unix/dyn_erl.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/erl.src.src b/erts/etc/unix/erl.src.src index 94c6f9f854..959c099e8f 100644 --- a/erts/etc/unix/erl.src.src +++ b/erts/etc/unix/erl.src.src @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2012. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 7b554e71f2..0d45efd606 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2014. All Rights Reserved. +# Copyright Ericsson AB 2005-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/etp-thr.py b/erts/etc/unix/etp-thr.py index 16bc7f4016..fb82dcaf1f 100644 --- a/erts/etc/unix/etp-thr.py +++ b/erts/etc/unix/etp-thr.py @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2013. All Rights Reserved. +# Copyright Ericsson AB 2013-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/etp_commands.erl b/erts/etc/unix/etp_commands.erl index fe16a71876..fe8f2dd556 100644 --- a/erts/etc/unix/etp_commands.erl +++ b/erts/etc/unix/etp_commands.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/etp_commands.mk b/erts/etc/unix/etp_commands.mk index def6f7bda0..983ee9f919 100644 --- a/erts/etc/unix/etp_commands.mk +++ b/erts/etc/unix/etp_commands.mk @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2009. All Rights Reserved. +# Copyright Ericsson AB 2005-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/format_man_pages b/erts/etc/unix/format_man_pages index 7abe65cecb..54f2d90c28 100644 --- a/erts/etc/unix/format_man_pages +++ b/erts/etc/unix/format_man_pages @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2010. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/run_erl.h b/erts/etc/unix/run_erl.h index cc70a98e52..83bdfdfb19 100644 --- a/erts/etc/unix/run_erl.h +++ b/erts/etc/unix/run_erl.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2009. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/safe_string.c b/erts/etc/unix/safe_string.c index a5c11d41d8..666022dc61 100644 --- a/erts/etc/unix/safe_string.c +++ b/erts/etc/unix/safe_string.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2009. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/safe_string.h b/erts/etc/unix/safe_string.h index 5a471f10de..cafd3fc71a 100644 --- a/erts/etc/unix/safe_string.h +++ b/erts/etc/unix/safe_string.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2009. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/setuid_socket_wrap.c b/erts/etc/unix/setuid_socket_wrap.c index 59ed8eae6f..461c69ee3e 100644 --- a/erts/etc/unix/setuid_socket_wrap.c +++ b/erts/etc/unix/setuid_socket_wrap.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/start.src b/erts/etc/unix/start.src index 377f5e85c8..bdd146951f 100644 --- a/erts/etc/unix/start.src +++ b/erts/etc/unix/start.src @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2009. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/unix/start_erl.src b/erts/etc/unix/start_erl.src index b889101783..34e0369710 100644 --- a/erts/etc/unix/start_erl.src +++ b/erts/etc/unix/start_erl.src @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2009. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c index 82bae947d4..43930ff284 100644 --- a/erts/etc/win32/Install.c +++ b/erts/etc/win32/Install.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2013. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/Makefile b/erts/etc/win32/Makefile index 12c04fc9a5..c6376ebe74 100644 --- a/erts/etc/win32/Makefile +++ b/erts/etc/win32/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2012. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/Nmakefile.start_erl b/erts/etc/win32/Nmakefile.start_erl index cf83713bab..00d22461fb 100644 --- a/erts/etc/win32/Nmakefile.start_erl +++ b/erts/etc/win32/Nmakefile.start_erl @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1998-2009. All Rights Reserved. +# Copyright Ericsson AB 1998-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/beam.rc b/erts/etc/win32/beam.rc index 9e137ecd62..0aaabf1097 100644 --- a/erts/etc/win32/beam.rc +++ b/erts/etc/win32/beam.rc @@ -1,7 +1,7 @@ // // %CopyrightBegin% // -// Copyright Ericsson AB 1997-2009. All Rights Reserved. +// Copyright Ericsson AB 1997-2016. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/erl b/erts/etc/win32/cygwin_tools/erl index 51a7be5584..897bbfac87 100755 --- a/erts/etc/win32/cygwin_tools/erl +++ b/erts/etc/win32/cygwin_tools/erl @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/erlc b/erts/etc/win32/cygwin_tools/erlc index 588b53b1be..eda9fdb7fc 100755 --- a/erts/etc/win32/cygwin_tools/erlc +++ b/erts/etc/win32/cygwin_tools/erlc @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/javac.sh b/erts/etc/win32/cygwin_tools/javac.sh index f2f97ef152..f53e5a3cc6 100755 --- a/erts/etc/win32/cygwin_tools/javac.sh +++ b/erts/etc/win32/cygwin_tools/javac.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh b/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh index 0f87f3d077..377c0dda6c 100755 --- a/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh +++ b/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2009. All Rights Reserved. +# Copyright Ericsson AB 2003-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/make_local_ini.sh b/erts/etc/win32/cygwin_tools/make_local_ini.sh index 0bcb362ffe..e5db98468b 100755 --- a/erts/etc/win32/cygwin_tools/make_local_ini.sh +++ b/erts/etc/win32/cygwin_tools/make_local_ini.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2009. All Rights Reserved. +# Copyright Ericsson AB 2003-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/mingw/ar.sh b/erts/etc/win32/cygwin_tools/mingw/ar.sh index 2ebfe4e435..de166284d8 100755 --- a/erts/etc/win32/cygwin_tools/mingw/ar.sh +++ b/erts/etc/win32/cygwin_tools/mingw/ar.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2009. All Rights Reserved. +# Copyright Ericsson AB 2006-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/mingw/cc.sh b/erts/etc/win32/cygwin_tools/mingw/cc.sh index 5993f70686..0b321a5099 100755 --- a/erts/etc/win32/cygwin_tools/mingw/cc.sh +++ b/erts/etc/win32/cygwin_tools/mingw/cc.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2009. All Rights Reserved. +# Copyright Ericsson AB 2006-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/mingw/coffix.c b/erts/etc/win32/cygwin_tools/mingw/coffix.c index 383c422008..ff3f3de3d1 100644 --- a/erts/etc/win32/cygwin_tools/mingw/coffix.c +++ b/erts/etc/win32/cygwin_tools/mingw/coffix.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh b/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh index 8da91dd0ef..5f510467fe 100755 --- a/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh +++ b/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2009. All Rights Reserved. +# Copyright Ericsson AB 2006-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/mingw/ld.sh b/erts/etc/win32/cygwin_tools/mingw/ld.sh index 16caf0b6d2..8b7e1a54c4 100755 --- a/erts/etc/win32/cygwin_tools/mingw/ld.sh +++ b/erts/etc/win32/cygwin_tools/mingw/ld.sh @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2009. All Rights Reserved. +# Copyright Ericsson AB 2006-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/mingw/mc.sh b/erts/etc/win32/cygwin_tools/mingw/mc.sh index 4462bfc5d3..8228f8699e 100755 --- a/erts/etc/win32/cygwin_tools/mingw/mc.sh +++ b/erts/etc/win32/cygwin_tools/mingw/mc.sh @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2009. All Rights Reserved. +# Copyright Ericsson AB 2006-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/mingw/rc.sh b/erts/etc/win32/cygwin_tools/mingw/rc.sh index b8a2d2fbcf..de1b15a432 100755 --- a/erts/etc/win32/cygwin_tools/mingw/rc.sh +++ b/erts/etc/win32/cygwin_tools/mingw/rc.sh @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2009. All Rights Reserved. +# Copyright Ericsson AB 2006-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/vc/ar.sh b/erts/etc/win32/cygwin_tools/vc/ar.sh index e0bd1bd5ca..0989cc878b 100755 --- a/erts/etc/win32/cygwin_tools/vc/ar.sh +++ b/erts/etc/win32/cygwin_tools/vc/ar.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/vc/cc.sh b/erts/etc/win32/cygwin_tools/vc/cc.sh index 651b6e098d..9eeb004f0e 100755 --- a/erts/etc/win32/cygwin_tools/vc/cc.sh +++ b/erts/etc/win32/cygwin_tools/vc/cc.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/vc/cc_wrap.c b/erts/etc/win32/cygwin_tools/vc/cc_wrap.c index b42e0e1037..198f3b5649 100644 --- a/erts/etc/win32/cygwin_tools/vc/cc_wrap.c +++ b/erts/etc/win32/cygwin_tools/vc/cc_wrap.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2009. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/vc/coffix.c b/erts/etc/win32/cygwin_tools/vc/coffix.c index 0633c6ddea..bf1096c425 100644 --- a/erts/etc/win32/cygwin_tools/vc/coffix.c +++ b/erts/etc/win32/cygwin_tools/vc/coffix.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/vc/emu_cc.sh b/erts/etc/win32/cygwin_tools/vc/emu_cc.sh index fb6ee2d7a2..343cd366a6 100755 --- a/erts/etc/win32/cygwin_tools/vc/emu_cc.sh +++ b/erts/etc/win32/cygwin_tools/vc/emu_cc.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/vc/ld.sh b/erts/etc/win32/cygwin_tools/vc/ld.sh index ff538122b2..2b7d7c6694 100755 --- a/erts/etc/win32/cygwin_tools/vc/ld.sh +++ b/erts/etc/win32/cygwin_tools/vc/ld.sh @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2010. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/vc/ld_wrap.c b/erts/etc/win32/cygwin_tools/vc/ld_wrap.c index 000c13befd..94b5c38751 100644 --- a/erts/etc/win32/cygwin_tools/vc/ld_wrap.c +++ b/erts/etc/win32/cygwin_tools/vc/ld_wrap.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2009. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/vc/mc.sh b/erts/etc/win32/cygwin_tools/vc/mc.sh index 2de5cbba9b..c88f2ff1a7 100755 --- a/erts/etc/win32/cygwin_tools/vc/mc.sh +++ b/erts/etc/win32/cygwin_tools/vc/mc.sh @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/cygwin_tools/vc/rc.sh b/erts/etc/win32/cygwin_tools/vc/rc.sh index 414ffa0448..286ebb03f0 100755 --- a/erts/etc/win32/cygwin_tools/vc/rc.sh +++ b/erts/etc/win32/cygwin_tools/vc/rc.sh @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2010. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erl.c b/erts/etc/win32/erl.c index 59693955a5..b230aa6a40 100644 --- a/erts/etc/win32/erl.c +++ b/erts/etc/win32/erl.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2009. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erl.rc b/erts/etc/win32/erl.rc index e8848e7969..772213ac86 100644 --- a/erts/etc/win32/erl.rc +++ b/erts/etc/win32/erl.rc @@ -1,7 +1,7 @@ // // %CopyrightBegin% // -// Copyright Ericsson AB 1998-2009. All Rights Reserved. +// Copyright Ericsson AB 1998-2016. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erl_log.c b/erts/etc/win32/erl_log.c index 2a873dffac..de0d8e39f0 100644 --- a/erts/etc/win32/erl_log.c +++ b/erts/etc/win32/erl_log.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erlsrv/erlsrv_global.h b/erts/etc/win32/erlsrv/erlsrv_global.h index f535599cf1..fc6166fc7c 100644 --- a/erts/etc/win32/erlsrv/erlsrv_global.h +++ b/erts/etc/win32/erlsrv/erlsrv_global.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.c b/erts/etc/win32/erlsrv/erlsrv_interactive.c index d2236ac9f7..c616ef86e3 100644 --- a/erts/etc/win32/erlsrv/erlsrv_interactive.c +++ b/erts/etc/win32/erlsrv/erlsrv_interactive.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.h b/erts/etc/win32/erlsrv/erlsrv_interactive.h index a83f5a4b85..03cf4b3339 100644 --- a/erts/etc/win32/erlsrv/erlsrv_interactive.h +++ b/erts/etc/win32/erlsrv/erlsrv_interactive.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2011. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erlsrv/erlsrv_main.c b/erts/etc/win32/erlsrv/erlsrv_main.c index caca18de00..521e7687f4 100644 --- a/erts/etc/win32/erlsrv/erlsrv_main.c +++ b/erts/etc/win32/erlsrv/erlsrv_main.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.c b/erts/etc/win32/erlsrv/erlsrv_registry.c index f95f4ef074..eb2a8c567c 100644 --- a/erts/etc/win32/erlsrv/erlsrv_registry.c +++ b/erts/etc/win32/erlsrv/erlsrv_registry.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.h b/erts/etc/win32/erlsrv/erlsrv_registry.h index 3aa265686a..885d021497 100644 --- a/erts/etc/win32/erlsrv/erlsrv_registry.h +++ b/erts/etc/win32/erlsrv/erlsrv_registry.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erlsrv/erlsrv_service.c b/erts/etc/win32/erlsrv/erlsrv_service.c index d9fa165355..f5d5c0b174 100644 --- a/erts/etc/win32/erlsrv/erlsrv_service.c +++ b/erts/etc/win32/erlsrv/erlsrv_service.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2013. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erlsrv/erlsrv_service.h b/erts/etc/win32/erlsrv/erlsrv_service.h index c87292325c..4e1148cac2 100644 --- a/erts/etc/win32/erlsrv/erlsrv_service.h +++ b/erts/etc/win32/erlsrv/erlsrv_service.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erlsrv/erlsrv_util.c b/erts/etc/win32/erlsrv/erlsrv_util.c index 800395ff12..f719082d37 100644 --- a/erts/etc/win32/erlsrv/erlsrv_util.c +++ b/erts/etc/win32/erlsrv/erlsrv_util.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/erlsrv/erlsrv_util.h b/erts/etc/win32/erlsrv/erlsrv_util.h index 1afcd1dd7e..97cc91834f 100644 --- a/erts/etc/win32/erlsrv/erlsrv_util.h +++ b/erts/etc/win32/erlsrv/erlsrv_util.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/init_file.c b/erts/etc/win32/init_file.c index 93d82b1823..147e299798 100644 --- a/erts/etc/win32/init_file.c +++ b/erts/etc/win32/init_file.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2009. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/init_file.h b/erts/etc/win32/init_file.h index 404b5fd03b..df33d23a35 100644 --- a/erts/etc/win32/init_file.h +++ b/erts/etc/win32/init_file.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2009. All Rights Reserved. + * Copyright Ericsson AB 2003-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/erl b/erts/etc/win32/msys_tools/erl index 110d48c769..a2fe924e78 100644 --- a/erts/etc/win32/msys_tools/erl +++ b/erts/etc/win32/msys_tools/erl @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/erlc b/erts/etc/win32/msys_tools/erlc index b50090b6de..45d466743e 100644 --- a/erts/etc/win32/msys_tools/erlc +++ b/erts/etc/win32/msys_tools/erlc @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/javac.sh b/erts/etc/win32/msys_tools/javac.sh index 5b51648a19..6a8b245601 100644 --- a/erts/etc/win32/msys_tools/javac.sh +++ b/erts/etc/win32/msys_tools/javac.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/make_bootstrap_ini.sh b/erts/etc/win32/msys_tools/make_bootstrap_ini.sh index 1797f67c78..59fc6fc09a 100644 --- a/erts/etc/win32/msys_tools/make_bootstrap_ini.sh +++ b/erts/etc/win32/msys_tools/make_bootstrap_ini.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2011. All Rights Reserved. +# Copyright Ericsson AB 2003-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/make_local_ini.sh b/erts/etc/win32/msys_tools/make_local_ini.sh index 11f722e7f8..046b3e9d00 100644 --- a/erts/etc/win32/msys_tools/make_local_ini.sh +++ b/erts/etc/win32/msys_tools/make_local_ini.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2011. All Rights Reserved. +# Copyright Ericsson AB 2003-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/vc/ar.sh b/erts/etc/win32/msys_tools/vc/ar.sh index 4c98e3cc29..a33c954c0f 100644 --- a/erts/etc/win32/msys_tools/vc/ar.sh +++ b/erts/etc/win32/msys_tools/vc/ar.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/vc/cc.sh b/erts/etc/win32/msys_tools/vc/cc.sh index 72005862ed..2b0482e876 100644 --- a/erts/etc/win32/msys_tools/vc/cc.sh +++ b/erts/etc/win32/msys_tools/vc/cc.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/vc/coffix.c b/erts/etc/win32/msys_tools/vc/coffix.c index 4f21cfc389..bf1096c425 100644 --- a/erts/etc/win32/msys_tools/vc/coffix.c +++ b/erts/etc/win32/msys_tools/vc/coffix.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2011. All Rights Reserved. + * Copyright Ericsson AB 1999-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/vc/emu_cc.sh b/erts/etc/win32/msys_tools/vc/emu_cc.sh index 10d59214ea..2de3a07aca 100644 --- a/erts/etc/win32/msys_tools/vc/emu_cc.sh +++ b/erts/etc/win32/msys_tools/vc/emu_cc.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/vc/ld.sh b/erts/etc/win32/msys_tools/vc/ld.sh index 11b2fc077b..8917251f51 100644 --- a/erts/etc/win32/msys_tools/vc/ld.sh +++ b/erts/etc/win32/msys_tools/vc/ld.sh @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/vc/mc.sh b/erts/etc/win32/msys_tools/vc/mc.sh index 14b5ebaa8f..a074a1a89f 100644 --- a/erts/etc/win32/msys_tools/vc/mc.sh +++ b/erts/etc/win32/msys_tools/vc/mc.sh @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/msys_tools/vc/rc.sh b/erts/etc/win32/msys_tools/vc/rc.sh index 1f8ade17cb..3d1186ca91 100644 --- a/erts/etc/win32/msys_tools/vc/rc.sh +++ b/erts/etc/win32/msys_tools/vc/rc.sh @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/nsis/Makefile b/erts/etc/win32/nsis/Makefile index 64f44ff86d..0b4e0d0359 100644 --- a/erts/etc/win32/nsis/Makefile +++ b/erts/etc/win32/nsis/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2012. All Rights Reserved. +# Copyright Ericsson AB 2003-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/nsis/dll_version_helper.sh b/erts/etc/win32/nsis/dll_version_helper.sh index 86e36f62c9..9eafb6ce0e 100755 --- a/erts/etc/win32/nsis/dll_version_helper.sh +++ b/erts/etc/win32/nsis/dll_version_helper.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2007-2013. All Rights Reserved. +# Copyright Ericsson AB 2007-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/nsis/erlang20.nsi b/erts/etc/win32/nsis/erlang20.nsi index bf6ba0b9a6..66746b684d 100644 --- a/erts/etc/win32/nsis/erlang20.nsi +++ b/erts/etc/win32/nsis/erlang20.nsi @@ -7,7 +7,7 @@ ; ; %CopyrightBegin% ; -; Copyright Ericsson AB 2012. All Rights Reserved. +; Copyright Ericsson AB 2012-2016. All Rights Reserved. ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/nsis/find_redist.sh b/erts/etc/win32/nsis/find_redist.sh index 03e92b21c7..c070ad469a 100755 --- a/erts/etc/win32/nsis/find_redist.sh +++ b/erts/etc/win32/nsis/find_redist.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2007-2011. All Rights Reserved. +# Copyright Ericsson AB 2007-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/port_entry.c b/erts/etc/win32/port_entry.c index 5681a2a548..8b1d3a44b8 100644 --- a/erts/etc/win32/port_entry.c +++ b/erts/etc/win32/port_entry.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2012. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/resource.h b/erts/etc/win32/resource.h index 32d8b8885d..e59baadb50 100644 --- a/erts/etc/win32/resource.h +++ b/erts/etc/win32/resource.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/start_erl.c b/erts/etc/win32/start_erl.c index a4437c2f6b..07bcd19b81 100644 --- a/erts/etc/win32/start_erl.c +++ b/erts/etc/win32/start_erl.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2012. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/etc/win32/win_erlexec.c b/erts/etc/win32/win_erlexec.c index f2460197e6..39dac358e9 100644 --- a/erts/etc/win32/win_erlexec.c +++ b/erts/etc/win32/win_erlexec.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2011. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/example/Makefile b/erts/example/Makefile index cc5bcce191..f00321ac45 100644 --- a/erts/example/Makefile +++ b/erts/example/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2009. All Rights Reserved. +# Copyright Ericsson AB 2006-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/example/matrix_nif.c b/erts/example/matrix_nif.c index dfe446e879..6452084eb7 100644 --- a/erts/example/matrix_nif.c +++ b/erts/example/matrix_nif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/example/matrix_nif.erl b/erts/example/matrix_nif.erl index d56b358247..bdc7228ac0 100644 --- a/erts/example/matrix_nif.erl +++ b/erts/example/matrix_nif.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/example/next_perm.cc b/erts/example/next_perm.cc index c7b7096e7b..882af4cd1e 100644 --- a/erts/example/next_perm.cc +++ b/erts/example/next_perm.cc @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2011. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/example/next_perm.erl b/erts/example/next_perm.erl index d414470f3a..b6f47b975c 100644 --- a/erts/example/next_perm.erl +++ b/erts/example/next_perm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/example/pg_async.c b/erts/example/pg_async.c index cd6bc9e0c2..3167ce5227 100644 --- a/erts/example/pg_async.c +++ b/erts/example/pg_async.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/example/pg_async.erl b/erts/example/pg_async.erl index 20ee94f61a..d34d03cf09 100644 --- a/erts/example/pg_async.erl +++ b/erts/example/pg_async.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/example/pg_async2.c b/erts/example/pg_async2.c index 9eb3ec9d54..ee772f4447 100644 --- a/erts/example/pg_async2.c +++ b/erts/example/pg_async2.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/example/pg_async2.erl b/erts/example/pg_async2.erl index 082852f617..9398f4ccbe 100644 --- a/erts/example/pg_async2.erl +++ b/erts/example/pg_async2.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/example/pg_encode.c b/erts/example/pg_encode.c index e1ec4abb1d..1efc4c1eaf 100644 --- a/erts/example/pg_encode.c +++ b/erts/example/pg_encode.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/example/pg_encode.h b/erts/example/pg_encode.h index df3f8fcaaa..213e20198e 100644 --- a/erts/example/pg_encode.h +++ b/erts/example/pg_encode.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/example/pg_encode2.c b/erts/example/pg_encode2.c index cdf8e71e44..df5ec9771b 100644 --- a/erts/example/pg_encode2.c +++ b/erts/example/pg_encode2.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/example/pg_encode2.h b/erts/example/pg_encode2.h index df3f8fcaaa..213e20198e 100644 --- a/erts/example/pg_encode2.h +++ b/erts/example/pg_encode2.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/example/pg_sync.c b/erts/example/pg_sync.c index 88096671a5..81b99f98d3 100644 --- a/erts/example/pg_sync.c +++ b/erts/example/pg_sync.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/example/pg_sync.erl b/erts/example/pg_sync.erl index 76fb27332e..29a975727a 100644 --- a/erts/example/pg_sync.erl +++ b/erts/example/pg_sync.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/include/erl_fixed_size_int_types.h b/erts/include/erl_fixed_size_int_types.h index dfaea5650b..9f47bdd797 100644 --- a/erts/include/erl_fixed_size_int_types.h +++ b/erts/include/erl_fixed_size_int_types.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2009. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/erl_int_sizes_config.h.in b/erts/include/erl_int_sizes_config.h.in index 88c74cdeff..e0e60f0e2f 100644 --- a/erts/include/erl_int_sizes_config.h.in +++ b/erts/include/erl_int_sizes_config.h.in @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/erl_memory_trace_parser.h b/erts/include/erl_memory_trace_parser.h index 426ff05061..3170ebc0d0 100644 --- a/erts/include/erl_memory_trace_parser.h +++ b/erts/include/erl_memory_trace_parser.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2009. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/erl_native_features_config.h.in b/erts/include/erl_native_features_config.h.in index 2bd1d96229..59a5dde09e 100644 --- a/erts/include/erl_native_features_config.h.in +++ b/erts/include/erl_native_features_config.h.in @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/README b/erts/include/internal/README index fca0c5e489..a79b241556 100644 --- a/erts/include/internal/README +++ b/erts/include/internal/README @@ -1,7 +1,7 @@ %CopyrightBegin% - Copyright Ericsson AB 2004-2009. All Rights Reserved. + Copyright Ericsson AB 2004-2016. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/erts/include/internal/erl_errno.h b/erts/include/internal/erl_errno.h index 33bfbe3d51..1ae045805e 100644 --- a/erts/include/internal/erl_errno.h +++ b/erts/include/internal/erl_errno.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/erl_memory_trace_protocol.h b/erts/include/internal/erl_memory_trace_protocol.h index b86e2de278..d3e0bcc1f4 100644 --- a/erts/include/internal/erl_memory_trace_protocol.h +++ b/erts/include/internal/erl_memory_trace_protocol.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2009. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/erl_misc_utils.h b/erts/include/internal/erl_misc_utils.h index 7ab7a26838..a4a5d1d510 100644 --- a/erts/include/internal/erl_misc_utils.h +++ b/erts/include/internal/erl_misc_utils.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2010. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/erl_printf.h b/erts/include/internal/erl_printf.h index 3846828fb9..c4565dfafc 100644 --- a/erts/include/internal/erl_printf.h +++ b/erts/include/internal/erl_printf.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/erl_printf_format.h b/erts/include/internal/erl_printf_format.h index 953022017a..4f969bdbcb 100644 --- a/erts/include/internal/erl_printf_format.h +++ b/erts/include/internal/erl_printf_format.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2013. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/erts_internal.mk.in b/erts/include/internal/erts_internal.mk.in index 76aab59c38..8faa33135e 100644 --- a/erts/include/internal/erts_internal.mk.in +++ b/erts/include/internal/erts_internal.mk.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2009. All Rights Reserved. +# Copyright Ericsson AB 2009-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ethr_atomics.h b/erts/include/internal/ethr_atomics.h index f366c2d0ad..06568201ad 100644 --- a/erts/include/internal/ethr_atomics.h +++ b/erts/include/internal/ethr_atomics.h @@ -10,7 +10,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2012. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ethr_internal.h b/erts/include/internal/ethr_internal.h index 693b34df61..6657c8affc 100644 --- a/erts/include/internal/ethr_internal.h +++ b/erts/include/internal/ethr_internal.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h index b402a139f5..a510a2c97f 100644 --- a/erts/include/internal/ethr_mutex.h +++ b/erts/include/internal/ethr_mutex.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ethr_optimized_fallbacks.h b/erts/include/internal/ethr_optimized_fallbacks.h index 6ef4e2bace..8c27a9ba5b 100644 --- a/erts/include/internal/ethr_optimized_fallbacks.h +++ b/erts/include/internal/ethr_optimized_fallbacks.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ethread.mk.in b/erts/include/internal/ethread.mk.in index 89924a3215..486a7a9401 100644 --- a/erts/include/internal/ethread.mk.in +++ b/erts/include/internal/ethread.mk.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2009. All Rights Reserved. +# Copyright Ericsson AB 2004-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ethread_header_config.h.in b/erts/include/internal/ethread_header_config.h.in index f4b08cfced..6309f10439 100644 --- a/erts/include/internal/ethread_header_config.h.in +++ b/erts/include/internal/ethread_header_config.h.in @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2015. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ethread_inline.h b/erts/include/internal/ethread_inline.h index 3ba910d993..8e6bcfc4a8 100644 --- a/erts/include/internal/ethread_inline.h +++ b/erts/include/internal/ethread_inline.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2014. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/i386/atomic.h b/erts/include/internal/i386/atomic.h index 6a6435e58d..52ef1762d5 100644 --- a/erts/include/internal/i386/atomic.h +++ b/erts/include/internal/i386/atomic.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/i386/ethr_dw_atomic.h b/erts/include/internal/i386/ethr_dw_atomic.h index 5444a6345c..91acdb0483 100644 --- a/erts/include/internal/i386/ethr_dw_atomic.h +++ b/erts/include/internal/i386/ethr_dw_atomic.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/i386/ethr_membar.h b/erts/include/internal/i386/ethr_membar.h index 97ae5eda2c..d1b72cd538 100644 --- a/erts/include/internal/i386/ethr_membar.h +++ b/erts/include/internal/i386/ethr_membar.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/i386/ethread.h b/erts/include/internal/i386/ethread.h index 23dcd1dc19..fef1674c7e 100644 --- a/erts/include/internal/i386/ethread.h +++ b/erts/include/internal/i386/ethread.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/i386/rwlock.h b/erts/include/internal/i386/rwlock.h index 9859338eab..8d22bac7e9 100644 --- a/erts/include/internal/i386/rwlock.h +++ b/erts/include/internal/i386/rwlock.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/i386/spinlock.h b/erts/include/internal/i386/spinlock.h index e010684d14..1a8e359981 100644 --- a/erts/include/internal/i386/spinlock.h +++ b/erts/include/internal/i386/spinlock.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/libatomic_ops/ethr_atomic.h b/erts/include/internal/libatomic_ops/ethr_atomic.h index 828210036c..da3b15a878 100644 --- a/erts/include/internal/libatomic_ops/ethr_atomic.h +++ b/erts/include/internal/libatomic_ops/ethr_atomic.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2014. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/libatomic_ops/ethr_dw_atomic.h b/erts/include/internal/libatomic_ops/ethr_dw_atomic.h index ce9b251cbe..8643600fa5 100644 --- a/erts/include/internal/libatomic_ops/ethr_dw_atomic.h +++ b/erts/include/internal/libatomic_ops/ethr_dw_atomic.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2014. All Rights Reserved. + * Copyright Ericsson AB 2014-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/libatomic_ops/ethr_membar.h b/erts/include/internal/libatomic_ops/ethr_membar.h index 7d2b807586..1d3d332c90 100644 --- a/erts/include/internal/libatomic_ops/ethr_membar.h +++ b/erts/include/internal/libatomic_ops/ethr_membar.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/libatomic_ops/ethread.h b/erts/include/internal/libatomic_ops/ethread.h index e34f43bb46..4adc31ed2a 100644 --- a/erts/include/internal/libatomic_ops/ethread.h +++ b/erts/include/internal/libatomic_ops/ethread.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ppc32/atomic.h b/erts/include/internal/ppc32/atomic.h index 572b0e5191..198f057b3f 100644 --- a/erts/include/internal/ppc32/atomic.h +++ b/erts/include/internal/ppc32/atomic.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ppc32/ethr_membar.h b/erts/include/internal/ppc32/ethr_membar.h index fe77721cd9..88ba4a2ea5 100644 --- a/erts/include/internal/ppc32/ethr_membar.h +++ b/erts/include/internal/ppc32/ethr_membar.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ppc32/ethread.h b/erts/include/internal/ppc32/ethread.h index 25e85c58bd..bead019f41 100644 --- a/erts/include/internal/ppc32/ethread.h +++ b/erts/include/internal/ppc32/ethread.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ppc32/rwlock.h b/erts/include/internal/ppc32/rwlock.h index aee232f79d..6493629e49 100644 --- a/erts/include/internal/ppc32/rwlock.h +++ b/erts/include/internal/ppc32/rwlock.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/ppc32/spinlock.h b/erts/include/internal/ppc32/spinlock.h index 829db6a135..3b35cafcb2 100644 --- a/erts/include/internal/ppc32/spinlock.h +++ b/erts/include/internal/ppc32/spinlock.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/pthread/ethr_event.h b/erts/include/internal/pthread/ethr_event.h index deb4b29686..6e470bf6cf 100644 --- a/erts/include/internal/pthread/ethr_event.h +++ b/erts/include/internal/pthread/ethr_event.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2011. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/sparc32/atomic.h b/erts/include/internal/sparc32/atomic.h index 0b535242ad..032943817d 100644 --- a/erts/include/internal/sparc32/atomic.h +++ b/erts/include/internal/sparc32/atomic.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/sparc32/ethr_membar.h b/erts/include/internal/sparc32/ethr_membar.h index 6133de5eb7..fb8ceef12b 100644 --- a/erts/include/internal/sparc32/ethr_membar.h +++ b/erts/include/internal/sparc32/ethr_membar.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/sparc32/ethread.h b/erts/include/internal/sparc32/ethread.h index 513d9f8773..8d49465008 100644 --- a/erts/include/internal/sparc32/ethread.h +++ b/erts/include/internal/sparc32/ethread.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/sparc32/rwlock.h b/erts/include/internal/sparc32/rwlock.h index 44de6113b7..1cc516cdad 100644 --- a/erts/include/internal/sparc32/rwlock.h +++ b/erts/include/internal/sparc32/rwlock.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/sparc32/spinlock.h b/erts/include/internal/sparc32/spinlock.h index 695fa112b6..ae3b1c9dee 100644 --- a/erts/include/internal/sparc32/spinlock.h +++ b/erts/include/internal/sparc32/spinlock.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/sparc64/ethread.h b/erts/include/internal/sparc64/ethread.h index 5f518e5596..6018e738b5 100644 --- a/erts/include/internal/sparc64/ethread.h +++ b/erts/include/internal/sparc64/ethread.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2009. All Rights Reserved. + * Copyright Ericsson AB 2007-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/tile/atomic.h b/erts/include/internal/tile/atomic.h index 1a2881442e..7f5f83bcc7 100644 --- a/erts/include/internal/tile/atomic.h +++ b/erts/include/internal/tile/atomic.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2011. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/tile/ethr_membar.h b/erts/include/internal/tile/ethr_membar.h index ccb420d558..50ea2c0265 100644 --- a/erts/include/internal/tile/ethr_membar.h +++ b/erts/include/internal/tile/ethr_membar.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/tile/ethread.h b/erts/include/internal/tile/ethread.h index 577d275965..0d69673a1c 100644 --- a/erts/include/internal/tile/ethread.h +++ b/erts/include/internal/tile/ethread.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2011. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/win/ethr_atomic.h b/erts/include/internal/win/ethr_atomic.h index f17526a94d..32c28f692d 100644 --- a/erts/include/internal/win/ethr_atomic.h +++ b/erts/include/internal/win/ethr_atomic.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/win/ethr_dw_atomic.h b/erts/include/internal/win/ethr_dw_atomic.h index 8bed4fb26e..a6b26ab7bb 100644 --- a/erts/include/internal/win/ethr_dw_atomic.h +++ b/erts/include/internal/win/ethr_dw_atomic.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/win/ethr_event.h b/erts/include/internal/win/ethr_event.h index 458565b9ea..9ee78183ab 100644 --- a/erts/include/internal/win/ethr_event.h +++ b/erts/include/internal/win/ethr_event.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2011. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/win/ethr_membar.h b/erts/include/internal/win/ethr_membar.h index 9cba6b605d..c018f6d869 100644 --- a/erts/include/internal/win/ethr_membar.h +++ b/erts/include/internal/win/ethr_membar.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/win/ethread.h b/erts/include/internal/win/ethread.h index 2fda028825..773be08a9b 100644 --- a/erts/include/internal/win/ethread.h +++ b/erts/include/internal/win/ethread.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/include/internal/x86_64/ethread.h b/erts/include/internal/x86_64/ethread.h index 8887b8d77f..7fc5481629 100644 --- a/erts/include/internal/x86_64/ethread.h +++ b/erts/include/internal/x86_64/ethread.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib/internal/README b/erts/lib/internal/README index 9beba10bc2..9e5cab26d3 100644 --- a/erts/lib/internal/README +++ b/erts/lib/internal/README @@ -1,7 +1,7 @@ %CopyrightBegin% - Copyright Ericsson AB 2004-2009. All Rights Reserved. + Copyright Ericsson AB 2004-2016. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/erts/lib_src/Makefile b/erts/lib_src/Makefile index 632b8a0b09..882a050ffd 100644 --- a/erts/lib_src/Makefile +++ b/erts/lib_src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2009. All Rights Reserved. +# Copyright Ericsson AB 2004-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in index 74e32ccdce..6e2f236bdf 100644 --- a/erts/lib_src/Makefile.in +++ b/erts/lib_src/Makefile.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2013. All Rights Reserved. +# Copyright Ericsson AB 2004-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/lib_src/common/erl_memory_trace_parser.c b/erts/lib_src/common/erl_memory_trace_parser.c index a81068089e..0232708ad1 100644 --- a/erts/lib_src/common/erl_memory_trace_parser.c +++ b/erts/lib_src/common/erl_memory_trace_parser.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c index 053217304b..8186463b9c 100644 --- a/erts/lib_src/common/erl_misc_utils.c +++ b/erts/lib_src/common/erl_misc_utils.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2013. All Rights Reserved. + * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c index 387a104a7a..b5e90dfeef 100644 --- a/erts/lib_src/common/erl_printf.c +++ b/erts/lib_src/common/erl_printf.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2012. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c index e7d5d4413e..3daa066fd3 100644 --- a/erts/lib_src/common/erl_printf_format.c +++ b/erts/lib_src/common/erl_printf_format.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2013. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/common/ethr_atomics.c b/erts/lib_src/common/ethr_atomics.c index 42c078377d..1594d78f5e 100644 --- a/erts/lib_src/common/ethr_atomics.c +++ b/erts/lib_src/common/ethr_atomics.c @@ -10,7 +10,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2012. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c index 3e7aad16c7..420efd725f 100644 --- a/erts/lib_src/common/ethr_aux.c +++ b/erts/lib_src/common/ethr_aux.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2014. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/common/ethr_cbf.c b/erts/lib_src/common/ethr_cbf.c index e79ec2b40c..037559be22 100644 --- a/erts/lib_src/common/ethr_cbf.c +++ b/erts/lib_src/common/ethr_cbf.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/common/ethr_mutex.c b/erts/lib_src/common/ethr_mutex.c index a596e6c31c..5e7e7b2f32 100644 --- a/erts/lib_src/common/ethr_mutex.c +++ b/erts/lib_src/common/ethr_mutex.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2013. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c index 0629b4dfcd..9a26ab6bd2 100644 --- a/erts/lib_src/pthread/ethr_event.c +++ b/erts/lib_src/pthread/ethr_event.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2010. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/pthread/ethr_x86_sse2_asm.c b/erts/lib_src/pthread/ethr_x86_sse2_asm.c index 7ce5a6d98a..bdcf3ac1c3 100644 --- a/erts/lib_src/pthread/ethr_x86_sse2_asm.c +++ b/erts/lib_src/pthread/ethr_x86_sse2_asm.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/pthread/ethread.c b/erts/lib_src/pthread/ethread.c index ef11559654..29bffa7e12 100644 --- a/erts/lib_src/pthread/ethread.c +++ b/erts/lib_src/pthread/ethread.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/utils/make_atomics_api b/erts/lib_src/utils/make_atomics_api index 4b37e3fa74..f960b97c87 100755 --- a/erts/lib_src/utils/make_atomics_api +++ b/erts/lib_src/utils/make_atomics_api @@ -4,7 +4,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2012. All Rights Reserved. +%% Copyright Ericsson AB 2011-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/lib_src/win/ethr_event.c b/erts/lib_src/win/ethr_event.c index 6951a216c5..383f7c876e 100644 --- a/erts/lib_src/win/ethr_event.c +++ b/erts/lib_src/win/ethr_event.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2011. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/lib_src/win/ethread.c b/erts/lib_src/win/ethread.c index 22b0b4040c..e0f19f7ba1 100644 --- a/erts/lib_src/win/ethread.c +++ b/erts/lib_src/win/ethread.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/preloaded/Makefile b/erts/preloaded/Makefile index fbe62d57bb..e8935d4410 100644 --- a/erts/preloaded/Makefile +++ b/erts/preloaded/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2008-2009. All Rights Reserved. +# Copyright Ericsson AB 2008-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 31383dda83..1e3de9f1d7 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2008-2013. All Rights Reserved. +# Copyright Ericsson AB 2008-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/add_abstract_code b/erts/preloaded/src/add_abstract_code index 4f479db2e8..943987872e 100644 --- a/erts/preloaded/src/add_abstract_code +++ b/erts/preloaded/src/add_abstract_code @@ -4,7 +4,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index cbcced5512..86ab4b30ef 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index dfbd116d6e..484b00413d 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index e53b6e5bab..98e0224a5f 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 330fcc4a9c..769757ba75 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012-2013. All Rights Reserved. +%% Copyright Ericsson AB 2012-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index ed65c57c0d..f8345ef219 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/otp_ring0.erl b/erts/preloaded/src/otp_ring0.erl index 3158fc7d21..62a60fffe2 100644 --- a/erts/preloaded/src/otp_ring0.erl +++ b/erts/preloaded/src/otp_ring0.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/prim_eval.S b/erts/preloaded/src/prim_eval.S index 1b7b00a7c9..e7f09a870c 100644 --- a/erts/preloaded/src/prim_eval.S +++ b/erts/preloaded/src/prim_eval.S @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/prim_eval.erl b/erts/preloaded/src/prim_eval.erl index 732e22468e..22e924f9e9 100644 --- a/erts/preloaded/src/prim_eval.erl +++ b/erts/preloaded/src/prim_eval.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index bd74831bb7..4872ffd00c 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/prim_zip.erl b/erts/preloaded/src/prim_zip.erl index c4b949afcb..b1ddbbe173 100644 --- a/erts/preloaded/src/prim_zip.erl +++ b/erts/preloaded/src/prim_zip.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/zip_internal.hrl b/erts/preloaded/src/zip_internal.hrl index d5cf52fae4..2769ca152d 100644 --- a/erts/preloaded/src/zip_internal.hrl +++ b/erts/preloaded/src/zip_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index 473ad649c7..fa0f28c5c3 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2013. All Rights Reserved. +%% Copyright Ericsson AB 2003-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile index 8025681924..dfd8153f32 100644 --- a/erts/start_scripts/Makefile +++ b/erts/start_scripts/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2013. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/start_scripts/no_dot_erlang.rel.src b/erts/start_scripts/no_dot_erlang.rel.src index bcc9fa9e8a..04e5fbf741 100644 --- a/erts/start_scripts/no_dot_erlang.rel.src +++ b/erts/start_scripts/no_dot_erlang.rel.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2014. All Rights Reserved. +%% Copyright Ericsson AB 2013-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/start_scripts/start_all_example.rel.src b/erts/start_scripts/start_all_example.rel.src index 2c4deb4485..a44f3e2925 100644 --- a/erts/start_scripts/start_all_example.rel.src +++ b/erts/start_scripts/start_all_example.rel.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2014. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/start_scripts/start_clean.rel.src b/erts/start_scripts/start_clean.rel.src index 25519deb17..ad468aa9df 100644 --- a/erts/start_scripts/start_clean.rel.src +++ b/erts/start_scripts/start_clean.rel.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2014. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/start_scripts/start_sasl.rel.src b/erts/start_scripts/start_sasl.rel.src index 9cf417ade5..23b6a89e1d 100644 --- a/erts/start_scripts/start_sasl.rel.src +++ b/erts/start_scripts/start_sasl.rel.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2014. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/Makefile b/erts/test/Makefile index a01d67e34f..1fe230adaf 100644 --- a/erts/test/Makefile +++ b/erts/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2014. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl index f0fee49024..1c11b442d4 100644 --- a/erts/test/erl_print_SUITE.erl +++ b/erts/test/erl_print_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/erl_print_SUITE_data/Makefile.src b/erts/test/erl_print_SUITE_data/Makefile.src index e6ea5cc6b9..69ff434c56 100644 --- a/erts/test/erl_print_SUITE_data/Makefile.src +++ b/erts/test/erl_print_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2012. All Rights Reserved. +# Copyright Ericsson AB 2005-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/test/erl_print_SUITE_data/character_test.h b/erts/test/erl_print_SUITE_data/character_test.h index 9ff032cb07..82310ee8e7 100644 --- a/erts/test/erl_print_SUITE_data/character_test.h +++ b/erts/test/erl_print_SUITE_data/character_test.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/test/erl_print_SUITE_data/erl_print_tests.c b/erts/test/erl_print_SUITE_data/erl_print_tests.c index fb23dc35a6..2fb7d1ff25 100644 --- a/erts/test/erl_print_SUITE_data/erl_print_tests.c +++ b/erts/test/erl_print_SUITE_data/erl_print_tests.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2012. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/test/erl_print_SUITE_data/integer_64_test.h b/erts/test/erl_print_SUITE_data/integer_64_test.h index 0c3e7b98a8..4bfc91334d 100644 --- a/erts/test/erl_print_SUITE_data/integer_64_test.h +++ b/erts/test/erl_print_SUITE_data/integer_64_test.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/test/erl_print_SUITE_data/integer_test.h b/erts/test/erl_print_SUITE_data/integer_test.h index b91f3622d6..b3744928b7 100644 --- a/erts/test/erl_print_SUITE_data/integer_test.h +++ b/erts/test/erl_print_SUITE_data/integer_test.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/test/erl_print_SUITE_data/snprintf_test.h b/erts/test/erl_print_SUITE_data/snprintf_test.h index c612a65521..77692304a3 100644 --- a/erts/test/erl_print_SUITE_data/snprintf_test.h +++ b/erts/test/erl_print_SUITE_data/snprintf_test.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/test/erl_print_SUITE_data/string_test.h b/erts/test/erl_print_SUITE_data/string_test.h index 0e257888e6..bfe4215d8a 100644 --- a/erts/test/erl_print_SUITE_data/string_test.h +++ b/erts/test/erl_print_SUITE_data/string_test.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2005-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index 7e44be1fe0..1c602ec87e 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/erlc_SUITE_data/include/erl_test.hrl b/erts/test/erlc_SUITE_data/include/erl_test.hrl index e7d096d2c1..70aecc4762 100644 --- a/erts/test/erlc_SUITE_data/include/erl_test.hrl +++ b/erts/test/erlc_SUITE_data/include/erl_test.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/erlc_SUITE_data/src/erl_test_bad.erl b/erts/test/erlc_SUITE_data/src/erl_test_bad.erl index b8c4ee2786..cbfe81705f 100644 --- a/erts/test/erlc_SUITE_data/src/erl_test_bad.erl +++ b/erts/test/erlc_SUITE_data/src/erl_test_bad.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl index f043fbebc4..604bb16bd6 100644 --- a/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl +++ b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/erlc_SUITE_data/src/erl_test_ok.erl b/erts/test/erlc_SUITE_data/src/erl_test_ok.erl index a82eda95b3..f1a48cbf18 100644 --- a/erts/test/erlc_SUITE_data/src/erl_test_ok.erl +++ b/erts/test/erlc_SUITE_data/src/erl_test_ok.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl b/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl index de17b903d6..da488b7232 100644 --- a/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl +++ b/erts/test/erlc_SUITE_data/src/yecc_test_bad.yrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl b/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl index 9433dcb90d..feb067e34b 100644 --- a/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl +++ b/erts/test/erlc_SUITE_data/src/yecc_test_ok.yrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index 6440cbf0d7..55108df3c7 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/erlexec_SUITE_data/Makefile.src b/erts/test/erlexec_SUITE_data/Makefile.src index 145aaedd64..641dff1e30 100644 --- a/erts/test/erlexec_SUITE_data/Makefile.src +++ b/erts/test/erlexec_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2008-2012. All Rights Reserved. +# Copyright Ericsson AB 2008-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/test/erlexec_SUITE_data/erlexec_tests.c b/erts/test/erlexec_SUITE_data/erlexec_tests.c index 569bf7bcc4..bd28d2900c 100644 --- a/erts/test/erlexec_SUITE_data/erlexec_tests.c +++ b/erts/test/erlexec_SUITE_data/erlexec_tests.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2010. All Rights Reserved. + * Copyright Ericsson AB 2008-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index 8ad2a32278..2675dc84d9 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/ethread_SUITE_data/Makefile.src b/erts/test/ethread_SUITE_data/Makefile.src index e8b9c79576..cf675b92a3 100644 --- a/erts/test/ethread_SUITE_data/Makefile.src +++ b/erts/test/ethread_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2012. All Rights Reserved. +# Copyright Ericsson AB 2004-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/test/ethread_SUITE_data/ethread_tests.c b/erts/test/ethread_SUITE_data/ethread_tests.c index b51771c736..fe7f92b012 100644 --- a/erts/test/ethread_SUITE_data/ethread_tests.c +++ b/erts/test/ethread_SUITE_data/ethread_tests.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/test/ignore_cores.erl b/erts/test/ignore_cores.erl index e40b91392c..576bb812e8 100644 --- a/erts/test/ignore_cores.erl +++ b/erts/test/ignore_cores.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl index d6df1aab6b..ad525a28e5 100644 --- a/erts/test/install_SUITE.erl +++ b/erts/test/install_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2012. All Rights Reserved. +%% Copyright Ericsson AB 2010-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/nt_SUITE.erl b/erts/test/nt_SUITE.erl index 1ddaaaaeb5..820cf85e0a 100644 --- a/erts/test/nt_SUITE.erl +++ b/erts/test/nt_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2013. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/nt_SUITE_data/Makefile.src b/erts/test/nt_SUITE_data/Makefile.src index 26da26b195..2317828337 100644 --- a/erts/test/nt_SUITE_data/Makefile.src +++ b/erts/test/nt_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1998-2009. All Rights Reserved. +# Copyright Ericsson AB 1998-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/test/nt_SUITE_data/nt_info.c b/erts/test/nt_SUITE_data/nt_info.c index 41d9a44c18..8ef52cad2d 100644 --- a/erts/test/nt_SUITE_data/nt_info.c +++ b/erts/test/nt_SUITE_data/nt_info.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 2efb5ae200..efc675939e 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2015. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl index e3c563d3d9..b637ca152d 100644 --- a/erts/test/run_erl_SUITE.erl +++ b/erts/test/run_erl_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/run_erl_SUITE_data/defuncter.pl b/erts/test/run_erl_SUITE_data/defuncter.pl index 666d4cca41..0b6771a8bb 100644 --- a/erts/test/run_erl_SUITE_data/defuncter.pl +++ b/erts/test/run_erl_SUITE_data/defuncter.pl @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2009. All Rights Reserved. +# Copyright Ericsson AB 2006-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/test/run_erl_SUITE_data/run_erl_test.pl b/erts/test/run_erl_SUITE_data/run_erl_test.pl index b9e3f0a363..9560fa3c14 100644 --- a/erts/test/run_erl_SUITE_data/run_erl_test.pl +++ b/erts/test/run_erl_SUITE_data/run_erl_test.pl @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2009. All Rights Reserved. +# Copyright Ericsson AB 2006-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index 83cd2359d8..004559b2d2 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2014. All Rights Reserved. +%% Copyright Ericsson AB 2014-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/erts/test/upgrade_SUITE_data/start.src b/erts/test/upgrade_SUITE_data/start.src index 7098a6919a..67d8de8c9e 100644 --- a/erts/test/upgrade_SUITE_data/start.src +++ b/erts/test/upgrade_SUITE_data/start.src @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2014. All Rights Reserved. +# Copyright Ericsson AB 2014-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/test/utils/gccifier.c b/erts/test/utils/gccifier.c index ca022eb390..0c3ef915fb 100644 --- a/erts/test/utils/gccifier.c +++ b/erts/test/utils/gccifier.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2012. All Rights Reserved. + * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/erts/test/utils/gccifier.sh b/erts/test/utils/gccifier.sh index 24b4d2f335..9311e34300 100755 --- a/erts/test/utils/gccifier.sh +++ b/erts/test/utils/gccifier.sh @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2012. All Rights Reserved. +# Copyright Ericsson AB 2005-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl index 7f3260e4cb..16f32b11b1 100644 --- a/erts/test/z_SUITE.erl +++ b/erts/test/z_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. -- cgit v1.2.3 From 2deb875adb4a921e758223a45fcd2e2df89124bc Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 15 Mar 2016 15:51:38 +0100 Subject: erts: Fix erts_debug:set_internal_state(wait,deallocations) that could hang if concurrent deallocations was initiated. --- erts/emulator/beam/erl_alloc.types | 2 ++ erts/emulator/beam/erl_process.c | 57 ++++++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 2932adca84..14067283bd 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -343,6 +343,8 @@ type SSB SHORT_LIVED PROCESSES ssb +endif +type DEBUG SHORT_LIVED SYSTEM debugging + type DDLL_PROCESS STANDARD SYSTEM ddll_processes type MONITOR_LH STANDARD PROCESSES monitor_lh type NLINK_LH STANDARD PROCESSES nlink_lh diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a706ebf595..d2185c4fbc 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2211,8 +2211,7 @@ setup_thr_debug_wait_completed(void *vproc) if (debug_wait_completed_flags & ERTS_DEBUG_WAIT_COMPLETED_DEALLOCATIONS) { erts_alloc_fix_alloc_shrink(awdp->sched_id, 0); wait_flags |= (ERTS_SSI_AUX_WORK_DD - | ERTS_SSI_AUX_WORK_DD_THR_PRGR - | ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP); + | ERTS_SSI_AUX_WORK_DD_THR_PRGR); #ifdef ERTS_SMP aux_work_flags |= ERTS_SSI_AUX_WORK_DD; #endif @@ -2220,8 +2219,7 @@ setup_thr_debug_wait_completed(void *vproc) if (debug_wait_completed_flags & ERTS_DEBUG_WAIT_COMPLETED_TIMER_CANCELLATIONS) { wait_flags |= (ERTS_SSI_AUX_WORK_CNCLD_TMRS - | ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR - | ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP); + | ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR); #ifdef ERTS_SMP if (awdp->esdp && !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)) aux_work_flags |= ERTS_SSI_AUX_WORK_CNCLD_TMRS; @@ -2235,26 +2233,42 @@ setup_thr_debug_wait_completed(void *vproc) awdp->debug.wait_completed.arg = vproc; } -static void -prep_setup_thr_debug_wait_completed(void *vproc) +struct debug_lop { + ErtsThrPrgrLaterOp lop; + Process *proc; +}; + +static void later_thr_debug_wait_completed(void *vlop) { + struct debug_lop *lop = vlop; erts_aint32_t count = (erts_aint32_t) erts_no_schedulers; #ifdef ERTS_SMP count += 1; /* aux thread */ #endif if (erts_atomic32_dec_read_mb(&debug_wait_completed_count) == count) { - /* scheduler threads */ - erts_schedule_multi_misc_aux_work(0, - erts_no_schedulers, - setup_thr_debug_wait_completed, - vproc); + /* scheduler threads */ + erts_schedule_multi_misc_aux_work(0, + erts_no_schedulers, + setup_thr_debug_wait_completed, + lop->proc); #ifdef ERTS_SMP - /* aux_thread */ - erts_schedule_misc_aux_work(0, - setup_thr_debug_wait_completed, - vproc); + /* aux_thread */ + erts_schedule_misc_aux_work(0, + setup_thr_debug_wait_completed, + lop->proc); #endif } + erts_free(ERTS_ALC_T_DEBUG, lop); +} + + +static void +init_thr_debug_wait_completed(void *vproc) +{ + struct debug_lop* lop = erts_alloc(ERTS_ALC_T_DEBUG, + sizeof(struct debug_lop)); + lop->proc = vproc; + erts_schedule_thr_prgr_later_op(later_thr_debug_wait_completed, lop, &lop->lop); } @@ -2264,7 +2278,7 @@ erts_debug_wait_completed(Process *c_p, int flags) /* Only one process at a time can do this */ erts_aint32_t count = (erts_aint32_t) (2*erts_no_schedulers); #ifdef ERTS_SMP - count += 2; /* aux thread */ + count += 1; /* aux thread */ #endif if (0 == erts_atomic32_cmpxchg_mb(&debug_wait_completed_count, count, @@ -2272,17 +2286,12 @@ erts_debug_wait_completed(Process *c_p, int flags) debug_wait_completed_flags = flags; erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL); erts_proc_inc_refc(c_p); - /* scheduler threads */ + + /* First flush later-ops on all scheduler threads */ erts_schedule_multi_misc_aux_work(0, erts_no_schedulers, - prep_setup_thr_debug_wait_completed, + init_thr_debug_wait_completed, (void *) c_p); -#ifdef ERTS_SMP - /* aux_thread */ - erts_schedule_misc_aux_work(0, - prep_setup_thr_debug_wait_completed, - (void *) c_p); -#endif return 1; } return 0; -- cgit v1.2.3 From 9516f34338d1e701949ec28ab8a5d0ace8b5e65d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Mar 2016 18:00:22 +0100 Subject: Fix ttsl_drv logging without TERMCAP --- erts/emulator/drivers/unix/ttsl_drv.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index abfc52d3bc..4f15ce0980 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -264,14 +264,12 @@ static int ttysl_init(void) DEBUGLOG(("ttysl_init: Debuglog = %s(0x%ld)\n",dl,(long) debuglog)); } #endif - DEBUGLOG(("ttysl_init: ttysl_port = %d\n", ttysl_port)); return 0; } static ErlDrvData ttysl_start(ErlDrvPort port, char* buf) { #ifndef HAVE_TERMCAP - DEBUGLOG(("ttysl_start: failure - no TERMCAP configured!\n")); return ERL_DRV_ERROR_GENERAL; #else char *s, *t, *l; -- cgit v1.2.3 From aae682cebfbb747565b5a6536f646e6c8d9e3b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 Mar 2016 10:09:04 +0100 Subject: init: Correct spec for fetch_loaded/1 fetch_loaded/1 returns a list of tuples, not a list of atoms. --- erts/preloaded/src/init.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index ed65c57c0d..5de3732c20 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -129,7 +129,7 @@ bs2ss(L) -> get_status() -> request(get_status). --spec fetch_loaded() -> [atom()]. +-spec fetch_loaded() -> [{module(),file:filename()}]. fetch_loaded() -> request(fetch_loaded). -- cgit v1.2.3 From 4e0c08b7b7658531af6151702372e2b9bdd8d8fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 Mar 2016 10:09:50 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/init.beam | Bin 44588 -> 44608 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 8ac7f5b471..9d2dd38f3a 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ -- cgit v1.2.3 From 395fbd8d02cb314b8ae57b9c02b4293037ca6890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 15 Mar 2016 08:31:27 +0100 Subject: Take out (parts of) broken fp exception support for MacOS X Floating-point exception support on MacOS X has never been especially reliable, and has therefore been disabled by default for a long time. The fpe support is now broken. Therefore, take out the unnecessary test for modern mcontext in configure (whatever that means) and the associated code in sys_float.c. Add #error directives to sys_float.c to make it clear that fpe is not supported. It seems to risky to mess with the mess of #ifdef's, so we will not attempt to remove all fpe support code for MacOS X. --- erts/configure.in | 38 -------------------------------------- erts/emulator/sys/unix/sys_float.c | 26 +++----------------------- 2 files changed, 3 insertions(+), 61 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 854bdbdc15..cae3843465 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2807,44 +2807,6 @@ if test "$cross_compiling" != "yes" && test X${enable_hipe} != Xno; then fi fi -case $ARCH-$OPSYS in - amd64-darwin*|x86-darwin*) - AC_MSG_CHECKING([For modern (leopard) style mcontext_t]) - AC_TRY_COMPILE([ - #include - #include - #include - #include - #include - #include - #include - ],[ - #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) - #define __DARWIN__ 1 - #endif - - #ifndef __DARWIN__ - #error inpossible - #else - - mcontext_t mc = NULL; - int x = mc->__fs.__fpu_mxcsr; - - #endif - ],darwin_mcontext_leopard=yes, - darwin_mcontext_leopard=no) - if test X"$darwin_mcontext_leopard" = X"yes"; then - AC_DEFINE(DARWIN_MODERN_MCONTEXT,[],[Modern style mcontext_t in MacOSX]) - AC_MSG_RESULT(yes) - else - AC_MSG_RESULT(no) - fi - ;; - *) - darwin_mcontext_leopard=no - ;; -esac - if test X${enable_fp_exceptions} = Xauto ; then case $host_os in *linux*) diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c index 8fe7e599e5..89e2484b17 100644 --- a/erts/emulator/sys/unix/sys_float.c +++ b/erts/emulator/sys/unix/sys_float.c @@ -499,18 +499,8 @@ static int mask_fpe(void) #define mc_pc(mc) ((mc)->gregs[REG_RIP]) #elif defined(__linux__) && defined(__i386__) #define mc_pc(mc) ((mc)->gregs[REG_EIP]) -#elif defined(__DARWIN__) && defined(__i386__) -#ifdef DARWIN_MODERN_MCONTEXT -#define mc_pc(mc) ((mc)->__ss.__eip) -#else -#define mc_pc(mc) ((mc)->ss.eip) -#endif -#elif defined(__DARWIN__) && defined(__x86_64__) -#ifdef DARWIN_MODERN_MCONTEXT -#define mc_pc(mc) ((mc)->__ss.__rip) -#else -#define mc_pc(mc) ((mc)->ss.rip) -#endif +#elif defined(__DARWIN__) +# error "Floating-point exceptions not supported on MacOS X" #elif defined(__FreeBSD__) && defined(__x86_64__) #define mc_pc(mc) ((mc)->mc_rip) #elif defined(__FreeBSD__) && defined(__i386__) @@ -575,17 +565,7 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc) regs[PT_FPSCR] = 0x80|0x40|0x10; /* VE, OE, ZE; not UE or XE */ #endif #elif defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__)) -#ifdef DARWIN_MODERN_MCONTEXT - mcontext_t mc = uc->uc_mcontext; - pc = mc_pc(mc); - mc->__fs.__fpu_mxcsr = 0x1F80; - *(unsigned short *)&mc->__fs.__fpu_fsw &= ~0xFF; -#else - mcontext_t mc = uc->uc_mcontext; - pc = mc_pc(mc); - mc->fs.fpu_mxcsr = 0x1F80; - *(unsigned short *)&mc->fs.fpu_fsw &= ~0xFF; -#endif /* DARWIN_MODERN_MCONTEXT */ +# error "Floating-point exceptions not supported on MacOS X" #elif defined(__DARWIN__) && defined(__ppc__) mcontext_t mc = uc->uc_mcontext; pc = mc->ss.srr0; -- cgit v1.2.3 From aaecaac6a392eb8bc6362d1a523858846ac8d670 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 16 Mar 2016 15:35:44 +0100 Subject: erts: Create erl_crash.dump when out of memory This was accidentally removed in commit cd6903be0740db --- erts/emulator/beam/erl_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 0877c24404..5f8cd2bbf7 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1923,7 +1923,7 @@ erts_alc_fatal_error(int error, int func, ErtsAlcType_t n, ...) va_start(argp, n); size = va_arg(argp, Uint); va_end(argp); - erts_exit(1, + erts_exit(ERTS_DUMP_EXIT, "%s: Cannot %s %lu bytes of memory (of type \"%s\").\n", allctr_str, op, size, t_str); break; -- cgit v1.2.3 From 0fe04b07b4d15c9671d6665ac5304bffd0f63d23 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 16 Mar 2016 13:42:04 +0100 Subject: Unbreak process_info(Pid,last_calls) --- erts/emulator/beam/erl_bif_info.c | 2 +- erts/emulator/test/save_calls_SUITE.erl | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 2c232c6c03..ac7a70c642 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1534,7 +1534,7 @@ process_info_aux(Process *BIF_P, } case am_last_calls: { - struct saved_calls *scb = ERTS_PROC_GET_SAVED_CALLS_BUF(BIF_P); + struct saved_calls *scb = ERTS_PROC_GET_SAVED_CALLS_BUF(rp); if (!scb) { hp = HAlloc(BIF_P, 3); res = am_false; diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl index 544d841f16..4e50fdc898 100644 --- a/erts/emulator/test/save_calls_SUITE.erl +++ b/erts/emulator/test/save_calls_SUITE.erl @@ -156,8 +156,19 @@ save_calls_1() -> ?line erlang:process_flag(self(), save_calls, 10), ?line {last_calls, L3} = process_info(self(), last_calls), + true = (L3 /= false), ?line L31 = lists:filter(fun is_local_function/1, L3), ?line [] = L31, + erlang:process_flag(self(), save_calls, 0), + + %% Also check that it works on another process ... + Pid = spawn(fun () -> receive after infinity -> ok end end), + erlang:process_flag(Pid, save_calls, 10), + {last_calls, L4} = process_info(Pid, last_calls), + true = (L4 /= false), + L41 = lists:filter(fun is_local_function/1, L4), + [] = L41, + exit(Pid,kill), ok. do_bipp() -> -- cgit v1.2.3 From c17eec673d8e7761712e3a4bfc520e9aea5e74c8 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 16 Mar 2016 17:17:11 +0100 Subject: Fix premature timeouts for ethread events on Linux --- erts/lib_src/pthread/ethr_event.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c index 0629b4dfcd..69e7be342c 100644 --- a/erts/lib_src/pthread/ethr_event.c +++ b/erts/lib_src/pthread/ethr_event.c @@ -94,6 +94,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) tsp = NULL; } else { +#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME + start = ethr_get_monotonic_time(); +#endif tsp = &ts; time = timeout; if (spincount == 0) { @@ -102,9 +105,6 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) goto return_event_on; goto set_timeout; } -#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - start = ethr_get_monotonic_time(); -#endif } while (1) { -- cgit v1.2.3 From da035ea5b65610bd9d19765609ed924c3fe0bdd1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 16 Mar 2016 17:34:05 +0100 Subject: erts: Fix LOW_WRITE section for non llvm os x compilation --- erts/emulator/beam/sys.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 03b9088adc..44735c0ec0 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -138,10 +138,10 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType; #endif #if ERTS_AT_LEAST_GCC_VSN__(2, 96, 0) -#ifndef __llvm__ -# define ERTS_WRITE_UNLIKELY(X) X __attribute__ ((section ("ERTS_LOW_WRITE") )) -#else +#if (defined(__APPLE__) && defined(__MACH__)) || defined(__DARWIN__) # define ERTS_WRITE_UNLIKELY(X) X __attribute__ ((section ("__DATA,ERTS_LOW_WRITE") )) +#else +# define ERTS_WRITE_UNLIKELY(X) X __attribute__ ((section ("ERTS_LOW_WRITE") )) #endif #else # define ERTS_WRITE_UNLIKELY(X) X -- cgit v1.2.3 From c54c6243423b55602dd7c204f2351852132d4a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 10 Mar 2016 15:42:33 +0100 Subject: init: Load modules in parallel using the new loader BIFs Use erlang:prepare_loading/1 and erlang:finish_loading/1 to load modules in parallel to potentially decrease start-up times. --- erts/preloaded/src/erl_prim_loader.erl | 11 +++- erts/preloaded/src/init.erl | 116 +++++++++++++++++++-------------- 2 files changed, 77 insertions(+), 50 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index cbcced5512..641abae7b1 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -56,7 +56,7 @@ -export([purge_archive_cache/0]). %% Used by init and the code server. --export([get_modules/3]). +-export([get_modules/2,get_modules/3]). -include_lib("kernel/include/file.hrl"). @@ -239,6 +239,13 @@ set_primary_archive(File, ArchiveBin, FileInfo, ParserFun) purge_archive_cache() -> request(purge_archive_cache). +-spec get_modules([module()], + fun((atom(), string(), binary()) -> + {'ok',any()} | {'error',any()})) -> + {'ok',{[any()],[any()]}}. + +get_modules(Modules, Fun) -> + request({get_modules,{Modules,Fun}}). -spec get_modules([module()], fun((atom(), string(), binary()) -> @@ -338,6 +345,8 @@ handle_request(Req, Paths, St0) -> {{ok,Paths},St0}; {get_file,File} -> handle_get_file(St0, Paths, File); + {get_modules,{Modules,Fun}} -> + handle_get_modules(St0, Modules, Fun, Paths); {get_modules,{Modules,Fun,ModPaths}} -> handle_get_modules(St0, Modules, Fun, ModPaths); {list_dir,Dir} -> diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 5de3732c20..915f1183d6 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -297,9 +297,9 @@ crash(String, List) -> -spec boot_loop(pid(), state()) -> no_return(). boot_loop(BootPid, State) -> receive - {BootPid,loaded,ModLoaded} -> - Loaded = State#state.loaded, - boot_loop(BootPid,State#state{loaded = [ModLoaded|Loaded]}); + {BootPid,loaded,NewlyLoaded} -> + Loaded = NewlyLoaded ++ State#state.loaded, + boot_loop(BootPid, State#state{loaded = Loaded}); {BootPid,started,KernelPid} -> boot_loop(BootPid, new_kernelpid(KernelPid, BootPid, State)); {BootPid,progress,started} -> @@ -338,12 +338,25 @@ boot_loop(BootPid, State) -> end. ensure_loaded(Module, Loaded) -> + case erlang:module_loaded(Module) of + true -> + {{module, Module}, Loaded}; + false -> + do_ensure_loaded(Module, Loaded) + end. + +do_ensure_loaded(Module, Loaded) -> File = atom_to_list(Module) ++ objfile_extension(), - case catch load_mod(Module,File) of - {ok, FullName} -> - {{module, Module}, [{Module, FullName}|Loaded]}; - Res -> - {Res, Loaded} + case erl_prim_loader:get_file(File) of + {ok,BinCode,FullName} -> + case do_load_module(Module, BinCode) of + ok -> + {{module, Module}, [{Module, FullName}|Loaded]}; + error -> + {error, [{Module, FullName}|Loaded]} + end; + Error -> + {Error, Loaded} end. %% Tell subscribed processes the system has started. @@ -842,13 +855,6 @@ eval_script([{kernel_load_completed}|T], #es{load_mode=Mode}=Es0) -> _ -> Es0#es{prim_load=false} end, eval_script(T, Es); -eval_script([{primLoad,[Mod]}|T], #es{prim_load=true}=Es) -> - %% Common special case (loading of error_handler). Nothing - %% to gain by parallel loading. - File = atom_to_list(Mod) ++ objfile_extension(), - {ok,Full} = load_mod(Mod, File), - init ! {self(),loaded,{Mod,Full}}, % Tell init about loaded module - eval_script(T, Es); eval_script([{primLoad,Mods}|T], #es{init=Init,prim_load=PrimLoad}=Es) when is_list(Mods) -> case PrimLoad of @@ -873,14 +879,44 @@ eval_script([], #es{}) -> eval_script(What, #es{}) -> exit({'unexpected command in bootfile',What}). -load_modules([Mod|Mods], Init) -> - File = atom_to_list(Mod) ++ objfile_extension(), - {ok,Full} = load_mod(Mod,File), - Init ! {self(),loaded,{Mod,Full}}, %Tell init about loaded module - load_modules(Mods, Init); -load_modules([], _) -> +load_modules(Mods0, Init) -> + Mods = [M || M <- Mods0, not erlang:module_loaded(M)], + F = prepare_loading_fun(), + case erl_prim_loader:get_modules(Mods, F) of + {ok,{Prep0,[]}} -> + Prep = [Code || {_,{prepared,Code,_}} <- Prep0], + ok = erlang:finish_loading(Prep), + Loaded = [{Mod,Full} || {Mod,{_,_,Full}} <- Prep0], + Init ! {self(),loaded,Loaded}, + Beams = [{M,Beam,Full} || {M,{on_load,Beam,Full}} <- Prep0], + load_rest(Beams, Init); + {ok,{_,[_|_]=Errors}} -> + Ms = [M || {M,_} <- Errors], + exit({load_failed,Ms}) + end. + +load_rest([{Mod,Beam,Full}|T], Init) -> + do_load_module(Mod, Beam), + Init ! {self(),loaded,[{Mod,Full}]}, + load_rest(T, Init); +load_rest([], _) -> ok. +prepare_loading_fun() -> + fun(Mod, FullName, Beam) -> + case erlang:prepare_loading(Mod, Beam) of + Prepared when is_binary(Prepared) -> + case erlang:has_prepared_code_on_load(Prepared) of + true -> + {ok,{on_load,Beam,FullName}}; + false -> + {ok,{prepared,Prepared,FullName}} + end; + {error,_}=Error -> + Error + end + end. + make_path(Pa, Pz, Path, Vars) -> append([Pa,append([fix_path(Path,Vars),Pz])]). @@ -1033,35 +1069,17 @@ start_it([_|_]=MFA) -> [M,F|Args] -> M:F(Args) % Args is a list end. -%% -%% Fetch a module and load it into the system. -%% -load_mod(Mod, File) -> - case erlang:module_loaded(Mod) of - false -> - case erl_prim_loader:get_file(File) of - {ok,BinCode,FullName} -> - load_mod_code(Mod, BinCode, FullName); - _ -> - exit({'cannot load',Mod,get_file}) - end; - _ -> % Already loaded. - {ok,File} - end. +%% Load a module. -load_mod_code(Mod, BinCode, FullName) -> - case erlang:module_loaded(Mod) of - false -> - case erlang:load_module(Mod, BinCode) of - {module,Mod} -> {ok,FullName}; - {error,on_load} -> - ?ON_LOAD_HANDLER ! {loaded,Mod}, - {ok,FullName}; - Other -> - exit({'cannot load',Mod,Other}) - end; - _ -> % Already loaded. - {ok,FullName} +do_load_module(Mod, BinCode) -> + case erlang:load_module(Mod, BinCode) of + {module,Mod} -> + ok; + {error,on_load} -> + ?ON_LOAD_HANDLER ! {loaded,Mod}, + ok; + _ -> + error end. %% -------------------------------------------------------- -- cgit v1.2.3 From 3c3c984aa9443a1ac1464473fd50e721e4d43b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 17 Mar 2016 10:25:13 +0100 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 55528 -> 55792 bytes erts/preloaded/ebin/init.beam | Bin 44608 -> 46200 bytes 2 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 6d777fa811..f224178c4f 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 9d2dd38f3a..7d73ca2234 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ -- cgit v1.2.3 From b4b74abfd0d5b22c6c2a8ba589e12df800e15e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 17 Mar 2016 10:08:47 +0100 Subject: configure: Remove obsolete --enable-darwin-* options Remove the options: --enable-darwin-universal --enable-darwin-64bit The --enable-darwin-universal option turns on universal binaries (Intel/PPC). It does not work on modern releases of OS X, and OTP 19 will most not likely build on an OS X release that still supports universal builds. The --enable-darwin-64bit option is not needed, because 64-bit builds are default on modern OS X releases. There is also the generic --enable-m64-build option. --- erts/configure.in | 101 +++++------------------------------------------------- 1 file changed, 9 insertions(+), 92 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index cae3843465..dae886b855 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -215,24 +215,6 @@ AS_HELP_STRING([--enable-fp-exceptions], esac ],enable_fp_exceptions=auto) -AC_ARG_ENABLE(darwin-universal, -AS_HELP_STRING([--enable-darwin-universal], - [build universal binaries on darwin i386]), -[ case "$enableval" in - no) enable_darwin_universal=no ;; - *) enable_darwin_univeral=yes ;; - esac -],enable_darwin_universal=no) - - -AC_ARG_ENABLE(darwin-64bit, -AS_HELP_STRING([--enable-darwin-64bit], [build 64bit binaries on darwin]), -[ case "$enableval" in - no) enable_darwin_64bit=no ;; - *) enable_darwin_64bit=yes ;; - esac -],enable_darwin_64bit=no) - AC_ARG_ENABLE(m64-build, AS_HELP_STRING([--enable-m64-build], [build 64bit binaries using the -m64 flag to (g)cc]), @@ -247,12 +229,7 @@ AS_HELP_STRING([--enable-m32-build], [build 32bit binaries using the -m32 flag to (g)cc]), [ case "$enableval" in no) enable_m32_build=no ;; - *) - if test X${enable_darwin_64bit} = Xyes -o X${enable_m64_build} = Xyes; - then - AC_MSG_ERROR([(--enable-darwin-64bit or --enable-m64-build) and --enable-m32-build are mutually exclusive]) ; - fi ; - enable_m32_build=yes ;; + *) enable_m32_build=yes ;; esac ],enable_m32_build=no) @@ -377,42 +354,7 @@ AC_MSG_CHECKING([OTP version]) AC_MSG_RESULT([$OTP_VERSION]) AC_SUBST(OTP_VERSION) -dnl OK, we might have darwin switches off different kinds, lets -dnl check it all before continuing. -TMPSYS=`uname -s`-`uname -m` -if test X${enable_darwin_universal} = Xyes; then - if test X${enable_darwin_64bit} = Xyes; then - AC_MSG_ERROR([--enable-darwin-universal and --enable-darwin-64bit mutually exclusive]) - fi - enable_hipe=no - case $CFLAGS in - *-arch\ ppc*) - ;; - *) - CFLAGS="-arch ppc $CFLAGS" - ;; - esac - case $CFLAGS in - *-arch\ i386*) - ;; - *) - CFLAGS="-arch i386 $CFLAGS" - ;; - esac -fi -if test X${enable_darwin_64bit} = Xyes; then - case "$TMPSYS" in - Darwin-i386|Darwin-x86_64) - ;; - Darwin*) - AC_MSG_ERROR([--enable-darwin-64bit only supported on x86 hosts]) - ;; - *) - AC_MSG_ERROR([--enable-darwin-64bit only supported on Darwin]) - ;; - esac -fi -if test X${enable_darwin_64bit} = Xyes -o X${enable_m64_build} = Xyes; then +if test X${enable_m64_build} = Xyes; then case $CFLAGS in *-m64*) ;; @@ -735,32 +677,13 @@ case $ARCH-$OPSYS in esac ;; *-darwin*) - if test X${enable_darwin_universal} = Xyes; then - AC_MSG_NOTICE([Adjusting LDFLAGS for universal binaries]) - - case $LDFLAGS in - *-arch\ ppc*) - ;; - *) - LDFLAGS="-arch ppc $LDFLAGS" - ;; - esac - case $LDFLAGS in - *-arch\ i386*) - ;; - *) - LDFLAGS="-arch i386 $LDFLAGS" - ;; - esac - else - case $LDFLAGS in - *-m32*) - ;; - *) - LDFLAGS="-m32 $LDFLAGS" - ;; - esac - fi + case $LDFLAGS in + *-m32*) + ;; + *) + LDFLAGS="-m32 $LDFLAGS" + ;; + esac ;; *) if test X${enable_m64_build} = Xyes; then @@ -3747,14 +3670,8 @@ case $host_os in DED_LDFLAGS="-m64 $DED_LDFLAGS" ;; *) - if test X${enable_darwin_universal} != Xyes; then - DED_LDFLAGS="-m32 $DED_LDFLAGS" - fi ;; esac - if test X${enable_darwin_universal} = Xyes; then - DED_LDFLAGS="-arch ppc -arch i386 $DED_LDFLAGS" - fi DED_LD="$CC" DED_LD_FLAG_RUNTIME_LIBRARY_PATH="$CFLAG_RUNTIME_LIBRARY_PATH" ;; -- cgit v1.2.3 From 3c2d5d0dccd34cba89880cd4c1ded616d19696c2 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 17 Mar 2016 21:54:40 +0100 Subject: Allow delayed gc while scheduled out --- erts/emulator/beam/erl_process.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a706ebf595..7fcf977e08 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -9324,8 +9324,6 @@ Process *schedule(Process *p, int calls) } else { sched_out_proc: - ASSERT(!(p->flags & F_DELAY_GC)); - #ifdef ERTS_SMP ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); esdp = p->scheduler_data; @@ -9886,15 +9884,24 @@ Process *schedule(Process *p, int calls) #endif if (state & ERTS_PSFLG_RUNNING_SYS) { - reds -= execute_sys_tasks(p, &state, reds); - if (reds <= 0 + /* + * GC is normally never delayed when a process + * is scheduled out, but might be when executing + * hand written beam assembly in + * prim_eval:'receive'. If GC is delayed we are + * not allowed to execute system tasks. + */ + if (!(p->flags & F_DELAY_GC)) { + reds -= execute_sys_tasks(p, &state, reds); + if (reds <= 0 #ifdef ERTS_DIRTY_SCHEDULERS - || ERTS_SCHEDULER_IS_DIRTY(esdp) - || (state & ERTS_PSFLGS_DIRTY_WORK) + || ERTS_SCHEDULER_IS_DIRTY(esdp) + || (state & ERTS_PSFLGS_DIRTY_WORK) #endif - ) { - p->fcalls = reds; - goto sched_out_proc; + ) { + p->fcalls = reds; + goto sched_out_proc; + } } ASSERT(state & ERTS_PSFLG_RUNNING_SYS); @@ -9924,7 +9931,7 @@ Process *schedule(Process *p, int calls) } if (ERTS_IS_GC_DESIRED(p)) { - if (!(state & ERTS_PSFLG_EXITING) && !(p->flags & F_DISABLE_GC)) { + if (!(state & ERTS_PSFLG_EXITING) && !(p->flags & (F_DELAY_GC|F_DISABLE_GC))) { reds -= erts_garbage_collect_nobump(p, 0, p->arg_reg, p->arity); if (reds <= 0) { p->fcalls = reds; -- cgit v1.2.3 From 0988d9aa2089fdcab9e8261c8953d2ba1867c28e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 20 Nov 2015 15:24:47 +0100 Subject: erts: Fix pthread_setname_np warning on osx --- erts/include/internal/ethread.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index e5c5cdfa33..b23644d361 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -112,6 +112,10 @@ int ethr_assert_failed(const char *file, int line, const char *func, char *a); #error "_GNU_SOURCE not defined. Please, compile all files with -D_GNU_SOURCE." #endif +#ifdef ETHR_HAVE_PTHREAD_SETNAME_NP_1 +#define _DARWIN_C_SOURCE +#endif + #if defined(ETHR_NEED_NPTL_PTHREAD_H) #include #elif defined(ETHR_HAVE_MIT_PTHREAD_H) -- cgit v1.2.3 From 455cbcf2230715b37da14573cb9ba60a68c24a26 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 18 Mar 2016 11:55:48 +0100 Subject: Fix implementation of dropped signal to port --- erts/emulator/beam/io.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 8c67f731f4..9a67e05bfa 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1501,8 +1501,19 @@ erts_schedule_proc2port_signal(Process *c_p, erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); if (sched_res != 0) { - if (refp) + if (refp) { + /* + * We need to restore the message queue save + * pointer to the beginning of the message queue + * since the caller now wont wait for a message + * containing the reference created above... + */ + ASSERT(c_p); + erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); + JOIN_MESSAGE(c_p); + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); *refp = NIL; + } return ERTS_PORT_OP_DROPPED; } return ERTS_PORT_OP_SCHEDULED; -- cgit v1.2.3 From dfa3b2414384f1ee0cdc062a23df5f1094a7069e Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Thu, 24 Mar 2016 13:28:53 +0100 Subject: erts: std_alloc is not thread safe on non-smp --- erts/emulator/beam/erl_alloc.types | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 14067283bd..7a5f821ed8 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -258,7 +258,6 @@ type PRTSD STANDARD SYSTEM port_specific_data type CPUDATA LONG_LIVED SYSTEM cpu_data type TMP_CPU_IDS SHORT_LIVED SYSTEM tmp_cpu_ids type EXT_TERM_DATA SHORT_LIVED PROCESSES external_term_data -type ZLIB STANDARD SYSTEM zlib type CPU_GRPS_MAP LONG_LIVED SYSTEM cpu_groups_map type AUX_WORK_TMO LONG_LIVED SYSTEM aux_work_timeouts type MISC_AUX_WORK_Q LONG_LIVED SYSTEM misc_aux_work_q @@ -288,8 +287,10 @@ type THR_Q_LL LONG_LIVED SYSTEM long_lived_thr_queue +if smp type ASYNC SHORT_LIVED SYSTEM async +type ZLIB STANDARD SYSTEM zlib +else -# sl_alloc is not thread safe in non smp build; therefore, we use driver_alloc +# sl/std_alloc is not thread safe in non smp build; therefore, we use driver_alloc +type ZLIB DRIVER SYSTEM zlib type ASYNC DRIVER SYSTEM async +endif -- cgit v1.2.3 From 65a8470afa7b2210fba348910f4cac066855791e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 24 Mar 2016 15:25:20 +0100 Subject: erts: Fix etp pid print on big endian --- erts/etc/unix/etp-commands.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 7b554e71f2..cfacc8b79b 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -776,7 +776,7 @@ define etp-pid-1 if ($etp_pid_1 & 0xF) == 0x3 if (etp_arch_bits == 64) if (etp_big_endian) - set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 36) & 0x0fffffff) + set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 35) & 0x0fffffff) else set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 4) & 0x0fffffff) end -- cgit v1.2.3 From ab884c3fdda00479d636de82d68ffbd8628c5c20 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 24 Mar 2016 16:20:51 +0100 Subject: Improve process/port specific data management --- erts/emulator/beam/beam_bp.c | 6 +- erts/emulator/beam/bif.c | 6 +- erts/emulator/beam/erl_nif.c | 2 +- erts/emulator/beam/erl_port.h | 47 +++++++++--- erts/emulator/beam/erl_process.c | 132 +++++++++++++--------------------- erts/emulator/beam/erl_process.h | 83 +++++++++++---------- erts/emulator/beam/erl_process_dump.c | 2 +- erts/emulator/beam/io.c | 8 ++- 8 files changed, 146 insertions(+), 140 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 5d471d168b..74c9d3ee53 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -974,7 +974,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt) if (pbt == 0) { /* First call of process to instrumented function */ pbt = Alloc(sizeof(process_breakpoint_time_t)); - (void) ERTS_PROC_SET_CALL_TIME(c_p, ERTS_PROC_LOCK_MAIN, pbt); + (void) ERTS_PROC_SET_CALL_TIME(c_p, pbt); } else { ASSERT(pbt->pc); /* add time to previous code */ @@ -1598,9 +1598,7 @@ bp_time_unref(BpDataTime* bdt) h_p = erts_pid2proc(NULL, 0, item->pid, ERTS_PROC_LOCK_MAIN); if (h_p) { - pbt = ERTS_PROC_SET_CALL_TIME(h_p, - ERTS_PROC_LOCK_MAIN, - NULL); + pbt = ERTS_PROC_SET_CALL_TIME(h_p, NULL); if (pbt) { Free(pbt); } diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 37bb28c6f8..97d690db9f 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1567,7 +1567,7 @@ static BIF_RETTYPE process_flag_aux(Process *BIF_P, scb->n = 0; } - scb = ERTS_PROC_SET_SAVED_CALLS_BUF(rp, ERTS_PROC_LOCK_MAIN, scb); + scb = ERTS_PROC_SET_SAVED_CALLS_BUF(rp, scb); if (!scb) old_value = make_small(0); @@ -1595,9 +1595,7 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2) if (is_not_atom(BIF_ARG_2)) { goto error; } - old_value = erts_proc_set_error_handler(BIF_P, - ERTS_PROC_LOCK_MAIN, - BIF_ARG_2); + old_value = erts_proc_set_error_handler(BIF_P, BIF_ARG_2); BIF_RET(old_value); } else if (BIF_ARG_1 == am_priority) { diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 8580bc2689..855d5deea1 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1653,7 +1653,7 @@ allocate_nif_sched_data(Process* proc, int argc) ep->exp.addressv[i] = &ep->exp.code[3]; } ep->exp.code[3] = (BeamInstr) em_call_nif; - (void) ERTS_PROC_SET_NIF_TRAP_EXPORT(proc, ERTS_PROC_LOCK_MAIN, ep); + (void) ERTS_PROC_SET_NIF_TRAP_EXPORT(proc, ep); return ep; } diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index fa97707a87..c25e08545e 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -185,7 +185,7 @@ struct _erl_drv_port { int control_flags; /* Flags for port_control() */ ErlDrvPDL port_data_lock; - ErtsPrtSD *psd; /* Port specific data */ + erts_smp_atomic_t psd; /* Port specific data */ int reds; /* Only used while executing driver callbacks */ struct { @@ -252,22 +252,51 @@ ERTS_GLB_INLINE void *erts_prtsd_set(Port *p, int ix, void *new); ERTS_GLB_INLINE void * erts_prtsd_get(Port *prt, int ix) { - return prt->psd ? prt->psd->data[ix] : NULL; + ErtsPrtSD *psd = (ErtsPrtSD *) erts_smp_atomic_read_nob(&prt->psd); + if (!psd) + return NULL; + ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER; + return psd->data[ix]; } ERTS_GLB_INLINE void * erts_prtsd_set(Port *prt, int ix, void *data) { - if (prt->psd) { - void *old = prt->psd->data[ix]; - prt->psd->data[ix] = data; + ErtsPrtSD *psd, *new_psd; + void *old; + int i; + + psd = (ErtsPrtSD *) erts_smp_atomic_read_nob(&prt->psd); + + if (psd) { +#ifdef ERTS_SMP +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_MEMBAR(ETHR_LoadStore|ETHR_StoreStore); +#else + ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore|ETHR_StoreStore); +#endif +#endif + old = psd->data[ix]; + psd->data[ix] = data; return old; } - else { - prt->psd = erts_alloc(ERTS_ALC_T_PRTSD, sizeof(ErtsPrtSD)); - prt->psd->data[ix] = data; + + if (!data) return NULL; - } + + new_psd = erts_alloc(ERTS_ALC_T_PRTSD, sizeof(ErtsPrtSD)); + for (i = 0; i < ERTS_PRTSD_SIZE; i++) + new_psd->data[i] = NULL; + psd = (ErtsPrtSD *) erts_smp_atomic_cmpxchg_mb(&prt->psd, + (erts_aint_t) new_psd, + (erts_aint_t) NULL); + if (psd) + erts_free(ERTS_ALC_T_PRTSD, new_psd); + else + psd = new_psd; + old = psd->data[ix]; + psd->data[ix] = data; + return old; } #endif diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index d2185c4fbc..758f350fa9 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -687,45 +687,36 @@ erts_pre_init_process(void) = "DEBUG_WAIT_COMPLETED"; #ifdef ERTS_ENABLE_LOCK_CHECK - { - int ix; - erts_psd_required_locks[ERTS_PSD_ERROR_HANDLER].get_locks - = ERTS_PSD_ERROR_HANDLER_BUF_GET_LOCKS; - erts_psd_required_locks[ERTS_PSD_ERROR_HANDLER].set_locks - = ERTS_PSD_ERROR_HANDLER_BUF_SET_LOCKS; + erts_psd_required_locks[ERTS_PSD_ERROR_HANDLER].get_locks + = ERTS_PSD_ERROR_HANDLER_BUF_GET_LOCKS; + erts_psd_required_locks[ERTS_PSD_ERROR_HANDLER].set_locks + = ERTS_PSD_ERROR_HANDLER_BUF_SET_LOCKS; - erts_psd_required_locks[ERTS_PSD_SAVED_CALLS_BUF].get_locks - = ERTS_PSD_SAVED_CALLS_BUF_GET_LOCKS; - erts_psd_required_locks[ERTS_PSD_SAVED_CALLS_BUF].set_locks - = ERTS_PSD_SAVED_CALLS_BUF_SET_LOCKS; + erts_psd_required_locks[ERTS_PSD_SAVED_CALLS_BUF].get_locks + = ERTS_PSD_SAVED_CALLS_BUF_GET_LOCKS; + erts_psd_required_locks[ERTS_PSD_SAVED_CALLS_BUF].set_locks + = ERTS_PSD_SAVED_CALLS_BUF_SET_LOCKS; - erts_psd_required_locks[ERTS_PSD_SCHED_ID].get_locks - = ERTS_PSD_SCHED_ID_GET_LOCKS; - erts_psd_required_locks[ERTS_PSD_SCHED_ID].set_locks - = ERTS_PSD_SCHED_ID_SET_LOCKS; + erts_psd_required_locks[ERTS_PSD_SCHED_ID].get_locks + = ERTS_PSD_SCHED_ID_GET_LOCKS; + erts_psd_required_locks[ERTS_PSD_SCHED_ID].set_locks + = ERTS_PSD_SCHED_ID_SET_LOCKS; - erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].get_locks - = ERTS_PSD_CALL_TIME_BP_GET_LOCKS; - erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].set_locks - = ERTS_PSD_CALL_TIME_BP_SET_LOCKS; + erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].get_locks + = ERTS_PSD_CALL_TIME_BP_GET_LOCKS; + erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].set_locks + = ERTS_PSD_CALL_TIME_BP_SET_LOCKS; - erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].get_locks - = ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS; - erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks - = ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS; + erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].get_locks + = ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS; + erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks + = ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS; - erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].get_locks - = ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS; - erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].set_locks - = ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS; - - /* Check that we have locks for all entries */ - for (ix = 0; ix < ERTS_PSD_SIZE; ix++) { - ERTS_SMP_LC_ASSERT(erts_psd_required_locks[ix].get_locks); - ERTS_SMP_LC_ASSERT(erts_psd_required_locks[ix].set_locks); - } - } + erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].get_locks + = ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS; + erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].set_locks + = ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS; #endif } @@ -1314,46 +1305,25 @@ erts_proclist_destroy(ErtsProcList *plp) } void * -erts_psd_set_init(Process *p, ErtsProcLocks plocks, int ix, void *data) +erts_psd_set_init(Process *p, int ix, void *data) { void *old; - ErtsProcLocks xplocks; - int refc = 0; - ErtsPSD *psd = erts_alloc(ERTS_ALC_T_PSD, sizeof(ErtsPSD)); + ErtsPSD *psd, *new_psd; int i; - for (i = 0; i < ERTS_PSD_SIZE; i++) - psd->data[i] = NULL; - ERTS_SMP_LC_ASSERT(plocks); - ERTS_SMP_LC_ASSERT(plocks == erts_proc_lc_my_proc_locks(p)); + new_psd = erts_alloc(ERTS_ALC_T_PSD, sizeof(ErtsPSD)); + for (i = 0; i < ERTS_PSD_SIZE; i++) + new_psd->data[i] = NULL; - xplocks = ERTS_PROC_LOCKS_ALL; - xplocks &= ~plocks; - if (xplocks && erts_smp_proc_trylock(p, xplocks) == EBUSY) { - if (xplocks & ERTS_PROC_LOCK_MAIN) { - erts_proc_inc_refc(p); - erts_smp_proc_unlock(p, plocks); - erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL); - refc = 1; - } - else { - if (plocks & ERTS_PROC_LOCKS_ALL_MINOR) - erts_smp_proc_unlock(p, plocks & ERTS_PROC_LOCKS_ALL_MINOR); - erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); - } - } - if (!p->psd) - p->psd = psd; - if (xplocks) - erts_smp_proc_unlock(p, xplocks); - if (refc) - erts_proc_dec_refc(p); - ASSERT(p->psd); - if (p->psd != psd) - erts_free(ERTS_ALC_T_PSD, psd); - old = p->psd->data[ix]; - p->psd->data[ix] = data; - ERTS_SMP_LC_ASSERT(plocks == erts_proc_lc_my_proc_locks(p)); + psd = (ErtsPSD *) erts_smp_atomic_cmpxchg_mb(&p->psd, + (erts_aint_t) new_psd, + (erts_aint_t) NULL); + if (psd) + erts_free(ERTS_ALC_T_PSD, new_psd); + else + psd = new_psd; + old = psd->data[ix]; + psd->data[ix] = data; return old; } @@ -9798,10 +9768,7 @@ Process *schedule(Process *p, int calls) if (erts_sched_stat.enabled) { int prio; - UWord old = ERTS_PROC_SCHED_ID(p, - (ERTS_PROC_LOCK_MAIN - | ERTS_PROC_LOCK_STATUS), - (UWord) esdp->no); + UWord old = ERTS_PROC_SCHED_ID(p, (UWord) esdp->no); int migrated = old && old != esdp->no; #ifdef ERTS_DIRTY_SCHEDULERS @@ -10465,7 +10432,7 @@ save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio) qs->q[PRIORITY_NORMAL] = NULL; qs->q[PRIORITY_LOW] = NULL; qs->q[prio] = st; - (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, ERTS_PROC_LOCK_MAIN, qs); + (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, qs); } else { if (!qs->q[prio]) { @@ -10612,7 +10579,7 @@ erts_set_gc_state(Process *c_p, int enable) erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS); - (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, ERTS_PROC_LOCK_MAIN, NULL); + (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, NULL); if (dgc_tsk_qs) proc_sys_task_queues_free(dgc_tsk_qs); @@ -11098,7 +11065,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->mbuf = NULL; p->msg_frag = NULL; p->mbuf_sz = 0; - p->psd = NULL; + erts_smp_atomic_init_nob(&p->psd, (erts_aint_t) NULL); p->dictionary = NULL; p->seq_trace_lastcnt = 0; p->seq_trace_clock = 0; @@ -11267,7 +11234,7 @@ void erts_init_empty_process(Process *p) p->mbuf = NULL; p->msg_frag = NULL; p->mbuf_sz = 0; - p->psd = NULL; + erts_smp_atomic_init_nob(&p->psd, (erts_aint_t) NULL); ERTS_P_MONITORS(p) = NULL; ERTS_P_LINKS(p) = NULL; /* List of links */ p->nodes_monitors = NULL; @@ -11437,14 +11404,17 @@ static void delete_process(Process* p) { Eterm *heap; + ErtsPSD *psd; VERBOSE(DEBUG_PROCESSES, ("Removing process: %T\n",p->common.id)); VERBOSE(DEBUG_SHCOPY, ("[pid=%T] delete process: %p %p %p %p\n", p->common.id, HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p))); /* Cleanup psd */ - if (p->psd) - erts_free(ERTS_ALC_T_PSD, p->psd); + psd = (ErtsPSD *) erts_smp_atomic_read_nob(&p->psd); + + if (psd) + erts_free(ERTS_ALC_T_PSD, psd); /* Clean binaries and funs */ erts_cleanup_offheap(&p->off_heap); @@ -12523,9 +12493,9 @@ erts_continue_exit_process(Process *p) } dep = (p->flags & F_DISTRIBUTION) ? erts_this_dist_entry : NULL; - scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, ERTS_PROC_LOCKS_ALL, NULL); - pbt = ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCKS_ALL, NULL); - nif_export = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, ERTS_PROC_LOCKS_ALL, NULL); + scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL); + pbt = ERTS_PROC_SET_CALL_TIME(p, NULL); + nif_export = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, NULL); erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL); #ifdef BM_COUNTERS diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index c88bd7056c..c07337b325 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -818,8 +818,8 @@ typedef struct { #define ERTS_PSD_ERROR_HANDLER_BUF_GET_LOCKS ERTS_PROC_LOCK_MAIN #define ERTS_PSD_ERROR_HANDLER_BUF_SET_LOCKS ERTS_PROC_LOCK_MAIN -#define ERTS_PSD_SAVED_CALLS_BUF_GET_LOCKS ERTS_PROC_LOCK_MAIN -#define ERTS_PSD_SAVED_CALLS_BUF_SET_LOCKS ERTS_PROC_LOCK_MAIN +#define ERTS_PSD_SAVED_CALLS_BUF_GET_LOCKS ((ErtsProcLocks) 0) +#define ERTS_PSD_SAVED_CALLS_BUF_SET_LOCKS ((ErtsProcLocks) 0) #define ERTS_PSD_SCHED_ID_GET_LOCKS ERTS_PROC_LOCK_STATUS #define ERTS_PSD_SCHED_ID_SET_LOCKS ERTS_PROC_LOCK_STATUS @@ -830,8 +830,8 @@ typedef struct { #define ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS ERTS_PROC_LOCK_MAIN #define ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS ERTS_PROC_LOCK_MAIN -#define ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS ERTS_PROC_LOCK_MAIN -#define ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS ERTS_PROC_LOCK_MAIN +#define ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS ((ErtsProcLocks) 0) +#define ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS ((ErtsProcLocks) 0) typedef struct { ErtsProcLocks get_locks; @@ -1026,7 +1026,7 @@ struct process { ErlHeapFragment* live_hf_end; ErtsMessage *msg_frag; /* Pointer to message fragment list */ Uint mbuf_sz; /* Total size of heap fragments and message fragments */ - ErtsPSD *psd; /* Rarely used process specific data */ + erts_smp_atomic_t psd; /* Rarely used process specific data */ Uint64 bin_vheap_sz; /* Virtual heap block size for binaries */ Uint64 bin_old_vheap_sz; /* Virtual old heap block size for binaries */ @@ -1903,18 +1903,19 @@ do { \ #define ERTS_SMP_LC_CHK_RUNQ_LOCK(RQ, L) #endif -void *erts_psd_set_init(Process *p, ErtsProcLocks plocks, int ix, void *data); +void *erts_psd_set_init(Process *p, int ix, void *data); ERTS_GLB_INLINE void * erts_psd_get(Process *p, int ix); ERTS_GLB_INLINE void * -erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *new); +erts_psd_set(Process *p, int ix, void *new); #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE void * erts_psd_get(Process *p, int ix) { + ErtsPSD *psd; #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) ErtsProcLocks locks = erts_proc_lc_my_proc_locks(p); if (ERTS_LC_PSD_ANY_LOCK == erts_psd_required_locks[ix].get_locks) @@ -1925,17 +1926,19 @@ erts_psd_get(Process *p, int ix) || erts_thr_progress_is_blocking()); } #endif + + psd = (ErtsPSD *) erts_smp_atomic_read_nob(&p->psd); ASSERT(0 <= ix && ix < ERTS_PSD_SIZE); - return p->psd ? p->psd->data[ix] : NULL; + if (!psd) + return NULL; + ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER; + return psd->data[ix]; } - -/* - * NOTE: erts_psd_set() might release and reacquire locks on 'p'. - */ ERTS_GLB_INLINE void * -erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *data) +erts_psd_set(Process *p, int ix, void *data) { + ErtsPSD *psd; #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) ErtsProcLocks locks = erts_proc_lc_my_proc_locks(p); if (ERTS_LC_PSD_ANY_LOCK == erts_psd_required_locks[ix].set_locks) @@ -1946,50 +1949,56 @@ erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *data) || erts_thr_progress_is_blocking()); } #endif + psd = (ErtsPSD *) erts_smp_atomic_read_nob(&p->psd); ASSERT(0 <= ix && ix < ERTS_PSD_SIZE); - if (p->psd) { - void *old = p->psd->data[ix]; - p->psd->data[ix] = data; + if (psd) { + void *old; +#ifdef ERTS_SMP +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_MEMBAR(ETHR_LoadStore|ETHR_StoreStore); +#else + ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore|ETHR_StoreStore); +#endif +#endif + old = psd->data[ix]; + psd->data[ix] = data; return old; } - else { - if (!data) - return NULL; - else - return erts_psd_set_init(p, plocks, ix, data); - } + + if (!data) + return NULL; + + return erts_psd_set_init(p, ix, data); } #endif -#define ERTS_PROC_SCHED_ID(P, L, ID) \ - ((UWord) erts_psd_set((P), (L), ERTS_PSD_SCHED_ID, (void *) (ID))) +#define ERTS_PROC_SCHED_ID(P, ID) \ + ((UWord) erts_psd_set((P), ERTS_PSD_SCHED_ID, (void *) (ID))) #define ERTS_PROC_GET_SAVED_CALLS_BUF(P) \ ((struct saved_calls *) erts_psd_get((P), ERTS_PSD_SAVED_CALLS_BUF)) -#define ERTS_PROC_SET_SAVED_CALLS_BUF(P, L, SCB) \ - ((struct saved_calls *) erts_psd_set((P), (L), ERTS_PSD_SAVED_CALLS_BUF, (void *) (SCB))) +#define ERTS_PROC_SET_SAVED_CALLS_BUF(P, SCB) \ + ((struct saved_calls *) erts_psd_set((P), ERTS_PSD_SAVED_CALLS_BUF, (void *) (SCB))) #define ERTS_PROC_GET_CALL_TIME(P) \ ((process_breakpoint_time_t *) erts_psd_get((P), ERTS_PSD_CALL_TIME_BP)) -#define ERTS_PROC_SET_CALL_TIME(P, L, PBT) \ - ((process_breakpoint_time_t *) erts_psd_set((P), (L), ERTS_PSD_CALL_TIME_BP, (void *) (PBT))) +#define ERTS_PROC_SET_CALL_TIME(P, PBT) \ + ((process_breakpoint_time_t *) erts_psd_set((P), ERTS_PSD_CALL_TIME_BP, (void *) (PBT))) #define ERTS_PROC_GET_DELAYED_GC_TASK_QS(P) \ ((ErtsProcSysTaskQs *) erts_psd_get((P), ERTS_PSD_DELAYED_GC_TASK_QS)) -#define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, L, PBT) \ - ((ErtsProcSysTaskQs *) erts_psd_set((P), (L), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT))) +#define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, PBT) \ + ((ErtsProcSysTaskQs *) erts_psd_set((P), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT))) #define ERTS_PROC_GET_NIF_TRAP_EXPORT(P) \ erts_psd_get((P), ERTS_PSD_NIF_TRAP_EXPORT) -#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, L, NTE) \ - erts_psd_set((P), (L), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE)) +#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, NTE) \ + erts_psd_set((P), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE)) ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p); -ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p, - ErtsProcLocks plocks, - Eterm handler); +ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p, Eterm handler); #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE Eterm @@ -2005,13 +2014,13 @@ erts_proc_get_error_handler(Process *p) } ERTS_GLB_INLINE Eterm -erts_proc_set_error_handler(Process *p, ErtsProcLocks plocks, Eterm handler) +erts_proc_set_error_handler(Process *p, Eterm handler) { void *old_val; void *new_val; ASSERT(is_atom(handler)); new_val = (handler == am_error_handler) ? NULL : (void *) (UWord) handler; - old_val = erts_psd_set(p, plocks, ERTS_PSD_ERROR_HANDLER, new_val); + old_val = erts_psd_set(p, ERTS_PSD_ERROR_HANDLER, new_val); if (!old_val) return am_error_handler; else { diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index 39c36ee7a9..73552b28de 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -103,7 +103,7 @@ Uint erts_process_memory(Process *p, int incl_msg_inq) { size += p->arity * sizeof(p->arg_reg[0]); } - if (p->psd) + if (erts_smp_atomic_read_nob(&p->psd) != (erts_aint_t) NULL) size += sizeof(ErtsPSD); scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 797e4cdadc..cc58423938 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -397,7 +397,7 @@ static Port *create_port(char *name, prt->common.u.alive.reg = NULL; ERTS_PTMR_INIT(prt); erts_port_task_handle_init(&prt->timeout_task); - prt->psd = NULL; + erts_smp_atomic_init_nob(&prt->psd, (erts_aint_t) NULL); prt->async_open_port = NULL; prt->drv_data = (SWord) 0; prt->os_pid = -1; @@ -3520,6 +3520,7 @@ terminate_port(Port *prt) Eterm connected_id = NIL /* Initialize to silence compiler */; erts_driver_t *drv; erts_aint32_t state; + ErtsPrtSD *psd; ERTS_SMP_CHK_NO_PROC_LOCKS; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); @@ -3573,8 +3574,9 @@ terminate_port(Port *prt) erts_cleanup_port_data(prt); - if (prt->psd) - erts_free(ERTS_ALC_T_PRTSD, prt->psd); + psd = (ErtsPrtSD *) erts_smp_atomic_read_nob(&prt->psd); + if (psd) + erts_free(ERTS_ALC_T_PRTSD, psd); ASSERT(prt->dist_entry == NULL); -- cgit v1.2.3 From 303bacbea8e977743c9e6b02c8d9fabe081de669 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 29 Mar 2016 09:57:33 +0200 Subject: erts: Fix incorrect non-smp debug assert --- erts/emulator/beam/erl_alloc_util.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 5e7dd7cce8..33b6c83bae 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -858,8 +858,9 @@ void* erts_alcu_literal_32_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) { void* res; - ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL - && !allctr->t && allctr->thread_safe); + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL && + allctr->t == 0); + ERTS_SMP_LC_ASSERT(allctr->thread_safe); res = erts_alcu_mseg_alloc(allctr, size_p, flags); if (res) @@ -872,8 +873,9 @@ erts_alcu_literal_32_mseg_realloc(Allctr_t *allctr, void *seg, Uint old_size, Uint *new_size_p) { void* res; - ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL - && !allctr->t && allctr->thread_safe); + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL && + allctr->t == 0); + ERTS_SMP_LC_ASSERT(allctr->thread_safe); if (seg && old_size) clear_literal_range(seg, old_size); @@ -887,8 +889,9 @@ void erts_alcu_literal_32_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags) { - ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL - && !allctr->t && allctr->thread_safe); + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL && + allctr->t == 0); + ERTS_SMP_LC_ASSERT(allctr->thread_safe); erts_alcu_mseg_dealloc(allctr, seg, size, flags); @@ -996,8 +999,9 @@ void* erts_alcu_literal_32_sys_alloc(Allctr_t *allctr, Uint size, int superalign) { void* res; - ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL - && !allctr->t && allctr->thread_safe); + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL && + allctr->t == 0); + ERTS_SMP_LC_ASSERT(allctr->thread_safe); res = erts_alcu_sys_alloc(allctr, size, 1); if (res) @@ -1009,8 +1013,9 @@ void* erts_alcu_literal_32_sys_realloc(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign) { void* res; - ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL - && !allctr->t && allctr->thread_safe); + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL && + allctr->t == 0); + ERTS_SMP_LC_ASSERT(allctr->thread_safe); if (ptr && old_size) clear_literal_range(ptr, old_size); @@ -1023,8 +1028,9 @@ erts_alcu_literal_32_sys_realloc(Allctr_t *allctr, void *ptr, Uint size, Uint ol void erts_alcu_literal_32_sys_dealloc(Allctr_t *allctr, void *ptr, Uint size, int superalign) { - ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL - && !allctr->t && allctr->thread_safe); + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL && + allctr->t == 0); + ERTS_SMP_LC_ASSERT(allctr->thread_safe); erts_alcu_sys_dealloc(allctr, ptr, size, 1); -- cgit v1.2.3 From da9b3a0825a3243aecfd849e11d1e37e5ccc96af Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 8 Dec 2015 17:41:12 +0100 Subject: erts: inline tag_val_def The tag_val_def function was called and multiple switch statements had to be traversed in term.c, and then a big switch in the calling code to branch on the term types. By inlining the switches are merged by the compiler and a lot fewer branches have to be taken. Benchmarks show that this increases performance of enc_term by as much as 10%. --- erts/emulator/beam/erl_term.c | 90 ------------------------------------------- erts/emulator/beam/erl_term.h | 81 ++++++++++++++++++++++++++++++++++++-- erts/emulator/sys/win32/sys.c | 3 +- 3 files changed, 80 insertions(+), 94 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_term.c b/erts/emulator/beam/erl_term.c index d2343912b4..72a1e0b6ec 100644 --- a/erts/emulator/beam/erl_term.c +++ b/erts/emulator/beam/erl_term.c @@ -59,96 +59,6 @@ erts_set_literal_tag(Eterm *term, Eterm *hp_start, Eterm hsz) #endif } -__decl_noreturn static void __noreturn -et_abort(const char *expr, const char *file, unsigned line) -{ -#ifdef EXIT_ON_ET_ABORT - static int have_been_called = 0; - - if (have_been_called) { - abort(); - } else { - /* - * Prevent infinite loop. - */ - have_been_called = 1; - erts_exit(ERTS_ERROR_EXIT, "TYPE ASSERTION FAILED, file %s, line %u: %s\n", file, line, expr); - } -#else - erts_fprintf(stderr, "TYPE ASSERTION FAILED, file %s, line %u: %s\n", file, line, expr); - abort(); -#endif -} - -#if ET_DEBUG -#define ET_ASSERT(expr,file,line) \ -do { \ - if (!(expr)) \ - et_abort(#expr, file, line); \ -} while(0) -#else -#define ET_ASSERT(expr,file,line) do { } while(0) -#endif - -#if ET_DEBUG -unsigned tag_val_def_debug(Wterm x, const char *file, unsigned line) -#else -unsigned tag_val_def(Wterm x) -#define file __FILE__ -#define line __LINE__ -#endif -{ - static char msg[32]; - - switch (x & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_LIST: - ET_ASSERT(_list_precond(x),file,line); - return LIST_DEF; - case TAG_PRIMARY_BOXED: { - Eterm hdr = *boxed_val(x); - ET_ASSERT(is_header(hdr),file,line); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE): return TUPLE_DEF; - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): return BIG_DEF; - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): return BIG_DEF; - case (_TAG_HEADER_REF >> _TAG_PRIMARY_SIZE): return REF_DEF; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): return FLOAT_DEF; - case (_TAG_HEADER_EXPORT >> _TAG_PRIMARY_SIZE): return EXPORT_DEF; - case (_TAG_HEADER_FUN >> _TAG_PRIMARY_SIZE): return FUN_DEF; - case (_TAG_HEADER_EXTERNAL_PID >> _TAG_PRIMARY_SIZE): return EXTERNAL_PID_DEF; - case (_TAG_HEADER_EXTERNAL_PORT >> _TAG_PRIMARY_SIZE): return EXTERNAL_PORT_DEF; - case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE): return EXTERNAL_REF_DEF; - case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): return MAP_DEF; - case (_TAG_HEADER_REFC_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; - case (_TAG_HEADER_HEAP_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; - case (_TAG_HEADER_SUB_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; - case (_TAG_HEADER_BIN_MATCHSTATE >> _TAG_PRIMARY_SIZE): return MATCHSTATE_DEF; - } - - break; - } - case TAG_PRIMARY_IMMED1: { - switch ((x & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_PID >> _TAG_PRIMARY_SIZE): return PID_DEF; - case (_TAG_IMMED1_PORT >> _TAG_PRIMARY_SIZE): return PORT_DEF; - case (_TAG_IMMED1_IMMED2 >> _TAG_PRIMARY_SIZE): { - switch ((x & _TAG_IMMED2_MASK) >> _TAG_IMMED1_SIZE) { - case (_TAG_IMMED2_ATOM >> _TAG_IMMED1_SIZE): return ATOM_DEF; - case (_TAG_IMMED2_NIL >> _TAG_IMMED1_SIZE): return NIL_DEF; - } - break; - } - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): return SMALL_DEF; - } - break; - } - } - erts_snprintf(msg, sizeof(msg), "tag_val_def: %#lx", (unsigned long) x); - et_abort(msg, file, line); -#undef file -#undef line -} - /* * XXX: define NUMBER_CODE() here when new representation is used */ diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 2b28762db5..0a71534790 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -1129,11 +1129,11 @@ _ET_DECLARE_CHECKED(Uint,loader_y_reg_index,Uint) #define FIRST_VACANT_TAG_DEF 0x12 #if ET_DEBUG -extern unsigned tag_val_def_debug(Wterm, const char*, unsigned); -#define tag_val_def(x) tag_val_def_debug((x),__FILE__,__LINE__) +ERTS_GLB_INLINE unsigned tag_val_def(Wterm, const char*, unsigned); #else -extern unsigned tag_val_def(Wterm); +ERTS_GLB_INLINE unsigned tag_val_def(Wterm); #endif + #define not_eq_tags(X,Y) (tag_val_def((X)) ^ tag_val_def((Y))) #define NUMBER_CODE(x,y) ((tag_val_def(x) << 5) | tag_val_def(y)) @@ -1152,5 +1152,80 @@ extern unsigned tag_val_def(Wterm); void erts_set_literal_tag(Eterm *term, Eterm *hp_start, Eterm hsz); +#if ET_DEBUG +#define ET_ASSERT(expr,file,line) \ +do { \ + if (!(expr)) \ + erl_assert_error("TYPE ASSERTION: " #expr, __FUNCTION__, file, line); \ +} while(0) +#else +#define ET_ASSERT(expr,file,line) do { } while(0) +#endif + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +#if ET_DEBUG +ERTS_GLB_INLINE unsigned tag_val_def(Wterm x, const char *file, unsigned line) +#else +ERTS_GLB_INLINE unsigned tag_val_def(Wterm x) +#define file __FILE__ +#define line __LINE__ +#endif +{ + static char *msg = "tag_val_def error"; + + switch (x & _TAG_PRIMARY_MASK) { + case TAG_PRIMARY_LIST: + ET_ASSERT(_list_precond(x),file,line); + return LIST_DEF; + case TAG_PRIMARY_BOXED: { + Eterm hdr = *boxed_val(x); + ET_ASSERT(is_header(hdr),file,line); + switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { + case (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE): return TUPLE_DEF; + case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): return BIG_DEF; + case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): return BIG_DEF; + case (_TAG_HEADER_REF >> _TAG_PRIMARY_SIZE): return REF_DEF; + case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): return FLOAT_DEF; + case (_TAG_HEADER_EXPORT >> _TAG_PRIMARY_SIZE): return EXPORT_DEF; + case (_TAG_HEADER_FUN >> _TAG_PRIMARY_SIZE): return FUN_DEF; + case (_TAG_HEADER_EXTERNAL_PID >> _TAG_PRIMARY_SIZE): return EXTERNAL_PID_DEF; + case (_TAG_HEADER_EXTERNAL_PORT >> _TAG_PRIMARY_SIZE): return EXTERNAL_PORT_DEF; + case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE): return EXTERNAL_REF_DEF; + case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): return MAP_DEF; + case (_TAG_HEADER_REFC_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_HEAP_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_SUB_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_BIN_MATCHSTATE >> _TAG_PRIMARY_SIZE): return MATCHSTATE_DEF; + } + + break; + } + case TAG_PRIMARY_IMMED1: { + switch ((x & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { + case (_TAG_IMMED1_PID >> _TAG_PRIMARY_SIZE): return PID_DEF; + case (_TAG_IMMED1_PORT >> _TAG_PRIMARY_SIZE): return PORT_DEF; + case (_TAG_IMMED1_IMMED2 >> _TAG_PRIMARY_SIZE): { + switch ((x & _TAG_IMMED2_MASK) >> _TAG_IMMED1_SIZE) { + case (_TAG_IMMED2_ATOM >> _TAG_IMMED1_SIZE): return ATOM_DEF; + case (_TAG_IMMED2_NIL >> _TAG_IMMED1_SIZE): return NIL_DEF; + } + break; + } + case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): return SMALL_DEF; + } + break; + } + } + erl_assert_error(msg, __FUNCTION__, file, line); +#undef file +#undef line +} +#endif + +#if ET_DEBUG +#define tag_val_def(X) tag_val_def(X, __FILE__, __LINE__) +#endif + #endif /* __ERL_TERM_H */ diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index ea7523ddc2..391bbd4cbc 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -3079,6 +3079,8 @@ erl_bin_write(buf, sz, max) } } +#endif /* DEBUG */ + void erl_assert_error(const char* expr, const char* func, const char* file, int line) { @@ -3094,7 +3096,6 @@ erl_assert_error(const char* expr, const char* func, const char* file, int line) DebugBreak(); } -#endif /* DEBUG */ static void check_supported_os_version(void) -- cgit v1.2.3 From 4a736966eba5398a22697db6ca67e1e3dd3cd0f2 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 10 Dec 2015 10:58:33 +0100 Subject: erts: Add enif_cpu/now_time and enif_make_unique_integer --- erts/doc/src/erl_nif.xml | 201 ++++++++++++++++---------- erts/emulator/beam/erl_bif_unique.c | 20 +-- erts/emulator/beam/erl_bif_unique.h | 12 +- erts/emulator/beam/erl_nif.c | 45 ++++++ erts/emulator/beam/erl_nif.h | 5 + erts/emulator/beam/erl_nif_api_funcs.h | 6 + erts/emulator/beam/erl_trace.c | 5 +- erts/emulator/test/nif_SUITE.erl | 62 +++++++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 36 ++++- 9 files changed, 297 insertions(+), 95 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index be0e406b9c..e62f5792a5 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -591,6 +591,21 @@ typedef enum { + ErlNifUniqueInteger + +

An enumeration of the properties that can be requested from + + enif_unique_integer.

+ + ERL_NIF_UNIQUE_POSITIVE +

A positive integer

+ ERL_NIF_UNIQUE_MONOTONIC +

A + strictly + monotonically increasing integer corresponding to creation time

+
+
+
@@ -689,7 +704,49 @@ typedef enum { a number of repeated NIF-calls without the need to create threads. See also the warning text at the beginning of this document.

+ + + + + ErlNifTimeenif_convert_time_unit(ErlNifTime val, ErlNifTimeUnit from, ErlNifTimeUnit to) + Convert time unit of a time value + + +

Arguments:

+ + val + Value to convert time unit for. + from + Time unit of val. + to + Time unit of returned value. + +

Converts the val value of time unit from to + the corresponding value of time unit to. The result is + rounded using the floor function.

+

Returns ERL_NIF_TIME_ERROR if called with an invalid + time unit argument.

+

See also:

+ + ErlNifTime + ErlNifTimeUnit + +
+ + + ERL_NIF_TERMenif_cpu_time(ErlNifEnv *) + + +

Returns the CPU time in the same format as erlang:timestamp(). + The CPU time is the time the current logical cpu has spent executing since + some arbitrary point in the past. + If the OS does not support fetching of this value enif_cpu_time + invokes enif_make_badarg. +

+
+
+ intenif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)

Same as erl_drv_equal_tids. @@ -1195,6 +1252,24 @@ typedef enum { Create an unsigned integer term

Create an integer term from an unsigned 64-bit integer.

+ + ERL_NIF_TERMenif_make_unique_integer(ErlNifEnv *env, ErlNifUniqueInteger properties) + + +

Returns a unique integer with the same properties as given by erlang:unique_integer/1.

+

env is the environment to create the integer in.

+

+ ERL_NIF_UNIQUE_POSITIVE and ERL_NIF_UNIQUE_MONOTONIC can + be passed as the second argument to change the properties of the + integer returned. It is possible to combine them by or:ing the + two values together. +

+

See also:

+ + ErlNifUniqueInteger + +
+
ERL_NIF_TERMenif_make_ulong(ErlNifEnv* env, unsigned long i) Create an integer term from an unsigned long int

Create an integer term from an unsigned long int.

@@ -1265,6 +1340,34 @@ enif_map_iterator_destroy(env, &iter); or false if the iterator is positioned at the head (before the first entry).

+ + + ErlNifTimeenif_monotonic_time(ErlNifTimeUnit time_unit) + Get Erlang Monotonic Time + + +

Arguments:

+ + time_unit + Time unit of returned value. + +

+ Returns + Erlang + monotonic time. Note that it is not uncommon with + negative values. +

+

Returns ERL_NIF_TIME_ERROR if called with an invalid + time unit argument, or if called from a thread that is not a + scheduler thread.

+

See also:

+ + ErlNifTime + ErlNifTimeUnit + +
+
+ ErlNifMutex *enif_mutex_create(char *name)

Same as erl_drv_mutex_create. @@ -1290,6 +1393,11 @@ enif_map_iterator_destroy(env, &iter);

Same as erl_drv_mutex_unlock.

+ ERL_NIF_TERMenif_now_time(ErlNifEnv *env) + +

Retuns an erlang:now() timestamp. + The enif_now_time function is deprecated.

+
ErlNifResourceType *enif_open_resource_type(ErlNifEnv* env, const char* module_str, const char* name, ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried) @@ -1496,54 +1604,6 @@ enif_map_iterator_destroy(env, &iter);

Same as erl_drv_thread_self.

- intenif_tsd_key_create(char *name, ErlNifTSDKey *key) - -

Same as erl_drv_tsd_key_create. -

-
- voidenif_tsd_key_destroy(ErlNifTSDKey key) - -

Same as erl_drv_tsd_key_destroy. -

-
- void *enif_tsd_get(ErlNifTSDKey key) - -

Same as erl_drv_tsd_get. -

-
- voidenif_tsd_set(ErlNifTSDKey key, void *data) - -

Same as erl_drv_tsd_set. -

-
- - - - ErlNifTimeenif_monotonic_time(ErlNifTimeUnit time_unit) - Get Erlang Monotonic Time - - -

Arguments:

- - time_unit - Time unit of returned value. - -

- Returns - Erlang - monotonic time. Note that it is not uncommon with - negative values. -

-

Returns ERL_NIF_TIME_ERROR if called with an invalid - time unit argument, or if called from a thread that is not a - scheduler thread.

-

See also:

- - ErlNifTime - ErlNifTimeUnit - -
-
ErlNifTimeenif_time_offset(ErlNifTimeUnit time_unit) @@ -1571,33 +1631,26 @@ enif_map_iterator_destroy(env, &iter); - - ErlNifTimeenif_convert_time_unit(ErlNifTime val, ErlNifTimeUnit from, ErlNifTimeUnit to) - Convert time unit of a time value - - -

Arguments:

- - val - Value to convert time unit for. - from - Time unit of val. - to - Time unit of returned value. - -

Converts the val value of time unit from to - the corresponding value of time unit to. The result is - rounded using the floor function.

-

Returns ERL_NIF_TIME_ERROR if called with an invalid - time unit argument.

-

See also:

- - ErlNifTime - ErlNifTimeUnit - -
+ intenif_tsd_key_create(char *name, ErlNifTSDKey *key) + +

Same as erl_drv_tsd_key_create. +

+
+ voidenif_tsd_key_destroy(ErlNifTSDKey key) + +

Same as erl_drv_tsd_key_destroy. +

+
+ void *enif_tsd_get(ErlNifTSDKey key) + +

Same as erl_drv_tsd_get. +

+
+ voidenif_tsd_set(ErlNifTSDKey key, void *data) + +

Same as erl_drv_tsd_set. +

-
SEE ALSO diff --git a/erts/emulator/beam/erl_bif_unique.c b/erts/emulator/beam/erl_bif_unique.c index c4a39b8897..c9c8561f64 100644 --- a/erts/emulator/beam/erl_bif_unique.c +++ b/erts/emulator/beam/erl_bif_unique.c @@ -266,17 +266,19 @@ static ERTS_INLINE Eterm unique_integer_bif(Process *c_p, int positive) } Uint -erts_raw_unique_integer_heap_size(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]) +erts_raw_unique_integer_heap_size(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES], + int positive) { Uint sz; - bld_unique_integer_term(NULL, &sz, val[0], val[1], 0); + bld_unique_integer_term(NULL, &sz, val[0], val[1], positive); return sz; } Eterm -erts_raw_make_unique_integer(Eterm **hpp, Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]) +erts_raw_make_unique_integer(Eterm **hpp, Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES], + int positive) { - return bld_unique_integer_term(hpp, NULL, val[0], val[1], 0); + return bld_unique_integer_term(hpp, NULL, val[0], val[1], positive); } void @@ -426,16 +428,16 @@ erts_raw_get_unique_monotonic_integer(void) } Uint -erts_raw_unique_monotonic_integer_heap_size(Sint64 raw) +erts_raw_unique_monotonic_integer_heap_size(Sint64 raw, int positive) { - return get_unique_monotonic_integer_heap_size(raw, 0); + return get_unique_monotonic_integer_heap_size(raw, positive); } Eterm -erts_raw_make_unique_monotonic_integer_value(Eterm **hpp, Sint64 raw) +erts_raw_make_unique_monotonic_integer_value(Eterm **hpp, Sint64 raw, int positive) { - Uint hsz = get_unique_monotonic_integer_heap_size(raw, 0); - Eterm res = make_unique_monotonic_integer_value(*hpp, hsz, raw, 0); + Uint hsz = get_unique_monotonic_integer_heap_size(raw, positive); + Eterm res = make_unique_monotonic_integer_value(*hpp, hsz, raw, positive); *hpp += hsz; return res; } diff --git a/erts/emulator/beam/erl_bif_unique.h b/erts/emulator/beam/erl_bif_unique.h index 37d5d91c39..94fd6163f2 100644 --- a/erts/emulator/beam/erl_bif_unique.h +++ b/erts/emulator/beam/erl_bif_unique.h @@ -41,8 +41,9 @@ void erts_make_ref_in_array(Uint32 ref[ERTS_MAX_REF_NUMBERS]); * not necessarily correspond to the end result. */ Sint64 erts_raw_get_unique_monotonic_integer(void); -Uint erts_raw_unique_monotonic_integer_heap_size(Sint64 raw); -Eterm erts_raw_make_unique_monotonic_integer_value(Eterm **hpp, Sint64 raw); +Uint erts_raw_unique_monotonic_integer_heap_size(Sint64 raw, int positive); +Eterm erts_raw_make_unique_monotonic_integer_value(Eterm **hpp, Sint64 raw, + int positive); Sint64 erts_get_min_unique_monotonic_integer(void); @@ -53,8 +54,11 @@ Eterm erts_debug_get_unique_monotonic_integer_state(Process *c_p); #define ERTS_UNIQUE_INT_RAW_VALUES 2 #define ERTS_MAX_UNIQUE_INT_HEAP_SIZE ERTS_UINT64_ARRAY_TO_BIG_MAX_HEAP_SZ(2) -Uint erts_raw_unique_integer_heap_size(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]); -Eterm erts_raw_make_unique_integer(Eterm **hpp, Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]); +Uint erts_raw_unique_integer_heap_size(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES], + int positive); +Eterm erts_raw_make_unique_integer(Eterm **hpp, + Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES], + int postive); void erts_raw_get_unique_integer(Uint64 val[ERTS_UNIQUE_INT_RAW_VALUES]); Sint64 erts_get_min_unique_integer(void); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 8580bc2689..703c96777a 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1158,6 +1158,51 @@ int enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM term, ERL_NIF_TERM *list return 1; } +ERL_NIF_TERM +enif_now_time(ErlNifEnv *env) +{ + Uint mega, sec, micro; + Eterm *hp; + get_now(&mega, &sec, µ); + hp = alloc_heap(env, 4); + return TUPLE3(hp, make_small(mega), make_small(sec), make_small(micro)); +} + +ERL_NIF_TERM +enif_cpu_time(ErlNifEnv *env) +{ +#ifdef HAVE_ERTS_NOW_CPU + Uint mega, sec, micro; + Eterm *hp; + erts_get_now_cpu(&mega, &sec, µ); + hp = alloc_heap(env, 4); + return TUPLE3(hp, make_small(mega), make_small(sec), make_small(micro)); +#else + return enif_make_badarg(env); +#endif +} + +ERL_NIF_TERM +enif_make_unique_integer(ErlNifEnv *env, ErlNifUniqueInteger properties) +{ + int monotonic = properties & ERL_NIF_UNIQUE_MONOTONIC; + int positive = properties & ERL_NIF_UNIQUE_POSITIVE; + Eterm *hp; + Uint hsz; + + if (monotonic) { + Sint64 raw_unique = erts_raw_get_unique_monotonic_integer(); + hsz = erts_raw_unique_monotonic_integer_heap_size(raw_unique, positive); + hp = alloc_heap(env, hsz); + return erts_raw_make_unique_monotonic_integer_value(&hp, raw_unique, positive); + } else { + Uint64 raw_unique[ERTS_UNIQUE_INT_RAW_VALUES]; + erts_raw_get_unique_integer(raw_unique); + hsz = erts_raw_unique_integer_heap_size(raw_unique, positive); + hp = alloc_heap(env, hsz); + return erts_raw_make_unique_integer(&hp, raw_unique, positive); + } +} ErlNifMutex* enif_mutex_create(char *name) { return erl_drv_mutex_create(name); } void enif_mutex_destroy(ErlNifMutex *mtx) { erl_drv_mutex_destroy(mtx); } diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index c9c8b1d0af..959fcb5412 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -197,6 +197,11 @@ typedef enum { ERL_NIF_MAP_ITERATOR_TAIL = ERL_NIF_MAP_ITERATOR_LAST } ErlNifMapIteratorEntry; +typedef enum { + ERL_NIF_UNIQUE_POSITIVE = (1 << 0), + ERL_NIF_UNIQUE_MONOTONIC = (1 << 1) +} ErlNifUniqueInteger; + #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct { diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 1448a508a2..3fe60e3df8 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -163,6 +163,9 @@ ERL_NIF_API_FUNC_DECL(int,enif_getenv,(const char* key, char* value, size_t* val ERL_NIF_API_FUNC_DECL(ErlNifTime, enif_monotonic_time, (ErlNifTimeUnit)); ERL_NIF_API_FUNC_DECL(ErlNifTime, enif_time_offset, (ErlNifTimeUnit)); ERL_NIF_API_FUNC_DECL(ErlNifTime, enif_convert_time_unit, (ErlNifTime, ErlNifTimeUnit, ErlNifTimeUnit)); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_now_time, (ErlNifEnv *env)); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_cpu_time, (ErlNifEnv *env)); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_unique_integer, (ErlNifEnv *env, ErlNifUniqueInteger properties)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! @@ -318,6 +321,9 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_monotonic_time ERL_NIF_API_FUNC_MACRO(enif_monotonic_time) # define enif_time_offset ERL_NIF_API_FUNC_MACRO(enif_time_offset) # define enif_convert_time_unit ERL_NIF_API_FUNC_MACRO(enif_convert_time_unit) +# define enif_now_time ERL_NIF_API_FUNC_MACRO(enif_now_time) +# define enif_cpu_time ERL_NIF_API_FUNC_MACRO(enif_cpu_time) +# define enif_make_unique_integer ERL_NIF_API_FUNC_MACRO(enif_make_unique_integer) /* ** ADD NEW ENTRIES HERE (before this comment) diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 41876b8c62..9033c1b75c 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -176,7 +176,7 @@ take_timestamp(ErtsTraceTimeStamp *tsp, int ts_type) hsz += 3; /* 2-tuple */ raw_unique = erts_raw_get_unique_monotonic_integer(); tsp->u.monotonic.raw_unique = raw_unique; - hsz += erts_raw_unique_monotonic_integer_heap_size(raw_unique); + hsz += erts_raw_unique_monotonic_integer_heap_size(raw_unique, 0); } return hsz; } @@ -216,8 +216,7 @@ write_timestamp(ErtsTraceTimeStamp *tsp, Eterm **hpp) return emtime; raw = tsp->u.monotonic.raw_unique; - unique = erts_raw_make_unique_monotonic_integer_value(hpp, - raw); + unique = erts_raw_make_unique_monotonic_integer_value(hpp, raw, 0); res = TUPLE2(*hpp, emtime, unique); *hpp += 3; return res; diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 84f5699890..d3bf91092f 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -42,7 +42,9 @@ dirty_nif_exception/1, call_dirty_nif_exception/1, nif_schedule/1, nif_exception/1, call_nif_exception/1, nif_nan_and_inf/1, nif_atom_too_long/1, - nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1]). + nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1, + nif_now_time/1, nif_cpu_time/1, nif_unique_integer/1 + ]). -export([many_args_100/100]). @@ -72,7 +74,8 @@ all() -> otp_9668, consume_timeslice, nif_schedule, dirty_nif, dirty_nif_send, dirty_nif_exception, nif_exception, nif_nan_and_inf, nif_atom_too_long, - nif_monotonic_time, nif_time_offset, nif_convert_time_unit + nif_monotonic_time, nif_time_offset, nif_convert_time_unit, + nif_now_time, nif_cpu_time, nif_unique_integer ]. init_per_testcase(_Case, Config) -> @@ -1885,6 +1888,57 @@ chk_ctu(Time, FromTU, [ToTU|ToTUs]) -> chk_ctu(Time, FromTU, ToTUs) end. +nif_now_time(Config) -> + ensure_lib_loaded(Config), + + N1 = now(), + NifN1 = now_time(), + NifN2 = now_time(), + N2 = now(), + true = N1 < NifN1, + true = NifN1 < NifN2, + true = NifN2 < N2. + +nif_cpu_time(Config) -> + ensure_lib_loaded(Config), + + try cpu_time() of + {_, _, _} -> + ok + catch error:badarg -> + {comment, "cpu_time not supported"} + end. + +nif_unique_integer(Config) -> + ensure_lib_loaded(Config), + + UM1 = erlang:unique_integer([monotonic]), + UM2 = unique_integer_nif([monotonic]), + UM3 = erlang:unique_integer([monotonic]), + + true = UM1 < UM2, + true = UM2 < UM3, + + UMP1 = erlang:unique_integer([monotonic, positive]), + UMP2 = unique_integer_nif([monotonic, positive]), + UMP3 = erlang:unique_integer([monotonic, positive]), + + true = 0 =< UMP1, + true = UMP1 < UMP2, + true = UMP2 < UMP3, + + UP1 = erlang:unique_integer([positive]), + UP2 = unique_integer_nif([positive]), + UP3 = erlang:unique_integer([positive]), + + true = 0 =< UP1, + true = 0 =< UP2, + true = 0 =< UP3, + + true = is_integer(unique_integer_nif([])), + true = is_integer(unique_integer_nif([])), + true = is_integer(unique_integer_nif([])). + %% The NIFs: lib_version() -> undefined. call_history() -> ?nif_stub. @@ -1942,6 +1996,7 @@ call_dirty_nif_zero_args() -> ?nif_stub. call_nif_exception(_) -> ?nif_stub. call_nif_nan_or_inf(_) -> ?nif_stub. call_nif_atom_too_long(_) -> ?nif_stub. +unique_integer_nif(_) -> ?nif_stub. %% maps is_map_nif(_) -> ?nif_stub. @@ -1958,7 +2013,8 @@ sorted_list_from_maps_nif(_) -> ?nif_stub. monotonic_time(_) -> ?nif_stub. time_offset(_) -> ?nif_stub. convert_time_unit(_,_,_) -> ?nif_stub. - +now_time() -> ?nif_stub. +cpu_time() -> ?nif_stub. nif_stub_error(Line) -> exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 1acb270d1f..1960689590 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1978,6 +1978,36 @@ static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TE return enif_make_int64(env, enif_convert_time_unit(val, from, to)); } +static ERL_NIF_TERM now_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return enif_now_time(env); +} + +static ERL_NIF_TERM cpu_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return enif_cpu_time(env); +} + +static ERL_NIF_TERM unique_integer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM atom_pos = enif_make_atom(env,"positive"), + atom_mon = enif_make_atom(env,"monotonic"); + ERL_NIF_TERM opts = argv[0], opt; + ErlNifUniqueInteger properties = 0; + + while (!enif_is_empty_list(env, opts)) { + if (!enif_get_list_cell(env, opts, &opt, &opts)) + return enif_make_badarg(env); + + if (enif_compare(opt, atom_pos) == 0) + properties |= ERL_NIF_UNIQUE_POSITIVE; + if (enif_compare(opt, atom_mon) == 0) + properties |= ERL_NIF_UNIQUE_MONOTONIC; + } + + return enif_make_unique_integer(env, properties); +} + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -2050,8 +2080,10 @@ static ErlNifFunc nif_funcs[] = {"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif}, {"monotonic_time", 1, monotonic_time}, {"time_offset", 1, time_offset}, - {"convert_time_unit", 3, convert_time_unit} + {"convert_time_unit", 3, convert_time_unit}, + {"now_time", 0, now_time}, + {"cpu_time", 0, cpu_time}, + {"unique_integer_nif", 1, unique_integer} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) - -- cgit v1.2.3 From 348f3f2ee2d2707e30658c3600e05309ad0e72bf Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 10 Dec 2015 10:59:34 +0100 Subject: erts: Add enif_is_process/port_alive --- erts/doc/src/erl_nif.xml | 25 ++++++++++ erts/emulator/beam/erl_nif.c | 70 ++++++++++++++++++++++++++- erts/emulator/beam/erl_nif.h | 7 ++- erts/emulator/beam/erl_nif_api_funcs.h | 6 +++ erts/emulator/test/nif_SUITE.erl | 25 +++++++++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 26 +++++++++- 6 files changed, 154 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index e62f5792a5..6befdc124b 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -532,6 +532,14 @@ typedef struct { environment. ErlNifPid is an opaque type.

+ ErlNifPort + +

ErlNifPort is a port identifier. In contrast to + port id terms (instances of ERL_NIF_TERM), ErlNifPort's are self + contained and not bound to any + environment. ErlNifPort + is an opaque type.

+
ErlNifResourceType @@ -801,6 +809,12 @@ typedef enum { pid variable *pid from it and return true. Otherwise return false. No check if the process is alive is done.

+ intenif_get_local_port(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPort* port_id) + Read an local port term +

If term is the port of a node local port, initialize the + port variable *port_id from it and return true. Otherwise return false. + No check if the port is alive is done.

+
intenif_get_list_cell(ErlNifEnv* env, ERL_NIF_TERM list, ERL_NIF_TERM* head, ERL_NIF_TERM* tail) Get head and tail from a list

Set *head and *tail from @@ -969,6 +983,17 @@ typedef enum { Determine if a term is a port

Return true if term is a port.

+ intenif_is_port_alive(ErlNifEnv* env, ErlNifPort *port_id) + Determine if a local port is alive or not. +

Return true if port_id is currently alive.

+

This function can only be used in a from a NIF-calling thread.

+
+ intenif_is_process_alive(ErlNifEnv* env, ErlNifPid *pid) + Determine if a local process is alive or not. +

Return true if pid is currently alive.

+

This function is only thread-safe when the emulator with SMP support is used. + It can only be used in a non-SMP emulator from a NIF-calling thread.

+
intenif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term) Determine if a term is a reference

Return true if term is a reference.

diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 703c96777a..8440fd26b5 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -404,12 +404,28 @@ static int is_offheap(const ErlOffHeap* oh) ErlNifPid* enif_self(ErlNifEnv* caller_env, ErlNifPid* pid) { + if (caller_env->proc->common.id == ERTS_INVALID_PID) + return NULL; pid->pid = caller_env->proc->common.id; return pid; } + int enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid) { - return is_internal_pid(term) ? (pid->pid=term, 1) : 0; + if (is_internal_pid(term)) { + pid->pid=term; + return 1; + } + return 0; +} + +int enif_get_local_port(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPort* port) +{ + if (is_internal_port(term)) { + port->port_id=term; + return 1; + } + return 0; } int enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term) @@ -1158,6 +1174,58 @@ int enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM term, ERL_NIF_TERM *list return 1; } +int enif_is_process_alive(ErlNifEnv* env, ErlNifPid *proc) +{ + ErtsProcLocks rp_locks = 0; /* We don't need any locks, + just to check if it is alive */ + Eterm target = proc->pid; + Process* rp; + Process* c_p; + int scheduler = erts_get_scheduler_id() != 0; + + if (env != NULL) { + c_p = env->proc; + if (target == c_p->common.id) { + /* We are alive! */ + return 1; + } + } + else { +#ifdef ERTS_SMP + c_p = NULL; +#else + erts_exit(ERTS_ABORT_EXIT,"enif_is_process_alive: " + "env==NULL on non-SMP VM"); +#endif + } + + rp = (scheduler + ? erts_proc_lookup(target) + : erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN, + target, rp_locks, ERTS_P2P_FLG_INC_REFC)); + if (rp == NULL) { + ASSERT(env == NULL || target != c_p->common.id); + return 0; + } else { + if (!scheduler) + erts_proc_dec_refc(rp); + return 1; + } +} + +int enif_is_port_alive(ErlNifEnv *env, ErlNifPort *port) +{ + /* only allowed if called from scheduler */ + if (erts_get_scheduler_id() == 0) + erts_exit(ERTS_ABORT_EXIT,"enif_is_port_alive: called from non-scheduler"); + + return erts_port_lookup( + port->port_id, + (erts_port_synchronous_ops + ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + : ERTS_PORT_SFLGS_INVALID_LOOKUP)) != NULL; +} + ERL_NIF_TERM enif_now_time(ErlNifEnv *env) { diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 959fcb5412..6ee22a693c 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -150,7 +150,12 @@ typedef enum typedef struct { ERL_NIF_TERM pid; /* internal, may change */ -}ErlNifPid; +} ErlNifPid; + +typedef struct +{ + ERL_NIF_TERM port_id; /* internal, may change */ +}ErlNifPort; typedef ErlDrvSysInfo ErlNifSysInfo; diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 3fe60e3df8..21864c8b35 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -166,6 +166,9 @@ ERL_NIF_API_FUNC_DECL(ErlNifTime, enif_convert_time_unit, (ErlNifTime, ErlNifTim ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_now_time, (ErlNifEnv *env)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_cpu_time, (ErlNifEnv *env)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_unique_integer, (ErlNifEnv *env, ErlNifUniqueInteger properties)); +ERL_NIF_API_FUNC_DECL(int, enif_is_process_alive, (ErlNifEnv *env, ErlNifPid *pid)); +ERL_NIF_API_FUNC_DECL(int, enif_is_port_alive, (ErlNifEnv *env, ErlNifPort *port_id)); +ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, ErlNifPort* port_id)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! @@ -324,6 +327,9 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_now_time ERL_NIF_API_FUNC_MACRO(enif_now_time) # define enif_cpu_time ERL_NIF_API_FUNC_MACRO(enif_cpu_time) # define enif_make_unique_integer ERL_NIF_API_FUNC_MACRO(enif_make_unique_integer) +# define enif_is_process_alive ERL_NIF_API_FUNC_MACRO(enif_is_process_alive) +# define enif_is_port_alive ERL_NIF_API_FUNC_MACRO(enif_is_port_alive) +# define enif_get_local_port ERL_NIF_API_FUNC_MACRO(enif_get_local_port) /* ** ADD NEW ENTRIES HERE (before this comment) diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index d3bf91092f..3202986ce7 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -43,7 +43,8 @@ nif_exception/1, call_nif_exception/1, nif_nan_and_inf/1, nif_atom_too_long/1, nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1, - nif_now_time/1, nif_cpu_time/1, nif_unique_integer/1 + nif_now_time/1, nif_cpu_time/1, nif_unique_integer/1, + nif_is_process_alive/1, nif_is_port_alive/1 ]). -export([many_args_100/100]). @@ -75,7 +76,8 @@ all() -> nif_schedule, dirty_nif, dirty_nif_send, dirty_nif_exception, nif_exception, nif_nan_and_inf, nif_atom_too_long, nif_monotonic_time, nif_time_offset, nif_convert_time_unit, - nif_now_time, nif_cpu_time, nif_unique_integer + nif_now_time, nif_cpu_time, nif_unique_integer, + nif_is_process_alive, nif_is_port_alive ]. init_per_testcase(_Case, Config) -> @@ -1939,6 +1941,23 @@ nif_unique_integer(Config) -> true = is_integer(unique_integer_nif([])), true = is_integer(unique_integer_nif([])). +nif_is_process_alive(Config) -> + ensure_lib_loaded(Config), + + {Pid,_} = spawn_monitor(fun() -> receive ok -> nok end end), + true = is_process_alive_nif(Pid), + exit(Pid, die), + receive _ -> ok end, %% Clear monitor + false = is_process_alive_nif(Pid). + +nif_is_port_alive(Config) -> + ensure_lib_loaded(Config), + + Port = open_port({spawn,"/bin/sh -s unix:cmd"},[stderr_to_stdout,eof]), + true = is_port_alive_nif(Port), + port_close(Port), + false = is_port_alive_nif(Port). + %% The NIFs: lib_version() -> undefined. call_history() -> ?nif_stub. @@ -1997,6 +2016,8 @@ call_nif_exception(_) -> ?nif_stub. call_nif_nan_or_inf(_) -> ?nif_stub. call_nif_atom_too_long(_) -> ?nif_stub. unique_integer_nif(_) -> ?nif_stub. +is_process_alive_nif(_) -> ?nif_stub. +is_port_alive_nif(_) -> ?nif_stub. %% maps is_map_nif(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 1960689590..2ce0168788 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -30,6 +30,7 @@ static int static_cntA; /* zero by default */ static int static_cntB = NIF_SUITE_LIB_VER * 100; static ERL_NIF_TERM atom_false; +static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_self; static ERL_NIF_TERM atom_ok; static ERL_NIF_TERM atom_join; @@ -138,6 +139,7 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) msgenv_dtor, ERL_NIF_RT_CREATE, NULL); atom_false = enif_make_atom(env,"false"); + atom_true = enif_make_atom(env,"true"); atom_self = enif_make_atom(env,"self"); atom_ok = enif_make_atom(env,"ok"); atom_join = enif_make_atom(env,"join"); @@ -2008,6 +2010,26 @@ static ERL_NIF_TERM unique_integer(ErlNifEnv* env, int argc, const ERL_NIF_TERM return enif_make_unique_integer(env, properties); } +static ERL_NIF_TERM is_process_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifPid pid; + if (!enif_get_local_pid(env, argv[0], &pid)) + return enif_make_badarg(env); + if (enif_is_process_alive(env, &pid)) + return atom_true; + return atom_false; +} + +static ERL_NIF_TERM is_port_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifPort port; + if (!enif_get_local_port(env, argv[0], &port)) + return enif_make_badarg(env); + if (enif_is_port_alive(env, &port)) + return atom_true; + return atom_false; +} + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -2083,7 +2105,9 @@ static ErlNifFunc nif_funcs[] = {"convert_time_unit", 3, convert_time_unit}, {"now_time", 0, now_time}, {"cpu_time", 0, cpu_time}, - {"unique_integer_nif", 1, unique_integer} + {"unique_integer_nif", 1, unique_integer}, + {"is_process_alive_nif", 1, is_process_alive}, + {"is_port_alive_nif", 1, is_port_alive} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) -- cgit v1.2.3 From 1bd56e2b5141a3afdca4e854e9b667807bf4e2f3 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 17 Dec 2015 14:14:54 +0100 Subject: erts: Add enif_term_to_binary and enif_binary_to_term --- erts/doc/src/erl_nif.xml | 24 +++++++++ erts/emulator/beam/erl_nif.c | 70 +++++++++++++++++++++++++++ erts/emulator/beam/erl_nif_api_funcs.h | 4 ++ erts/emulator/test/nif_SUITE.erl | 25 +++++++++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 53 +++++++++++++++++++- 5 files changed, 173 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 6befdc124b..47d84bb813 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -655,6 +655,18 @@ typedef enum { have been allocated with enif_alloc_env.

+ intenif_binary_to_term(ErlNifEnv *env, ErlNifBinary *bin, ERL_NIF_TERM *term) + Convert a term from the external format + +

Returns a term that is the result of decoding bin + according to the Erlang external term format.

+

See also:

+ + erlang:binary_to_term/1 + enif_term_to_binary + +
+
intenif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs) Compare two terms

Return an integer less than, equal to, or greater than @@ -1599,6 +1611,18 @@ enif_map_iterator_destroy(env, &iter);

Same as driver_system_info.

+ intenif_term_to_binary(ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin) + Convert a term to the external format + +

Returns a binary data object that is the result of encoding term + according to the Erlang external term format.

+

See also:

+ + erlang:term_to_binary/1 + enif_binary_to_term + +
+
intenif_thread_create(char *name,ErlNifTid *tid,void * (*func)(void *),void *args,ErlNifThreadOpts *opts)

Same as erl_drv_thread_create. diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 8440fd26b5..21bf752bb6 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -638,6 +638,76 @@ unsigned char* enif_make_new_binary(ErlNifEnv* env, size_t size, return binary_bytes(*termp); } +int enif_term_to_binary(ErlNifEnv *dst_env, ERL_NIF_TERM term, + ErlNifBinary *bin) +{ + Sint size; + byte *bp; + Binary* refbin; + + size = erts_encode_ext_size(term); + if (!enif_alloc_binary(size, bin)) + return 0; + + refbin = bin->ref_bin; + + bp = bin->data; + + erts_encode_ext(term, &bp); + + bin->size = bp - bin->data; + refbin->orig_size = bin->size; + + ASSERT(bin->data + bin->size == bp); + + return 1; +} + +int enif_binary_to_term(ErlNifEnv *dst_env, ErlNifBinary *bin, + ERL_NIF_TERM *term) +{ + Sint size; + ErtsHeapFactory factory; + + if ((size = erts_decode_ext_size(bin->data, bin->size)) < 0) + return 0; + + if (size > 0) { + byte *bp; + + if (is_internal_pid(dst_env->proc->common.id)) { + flush_env(dst_env); + erts_factory_proc_prealloc_init(&factory, dst_env->proc, size); + cache_env(dst_env); + } else { + + /* this is guaranteed to create a heap fragment */ + if (!alloc_heap(dst_env, size)) + return 0; + + erts_factory_heap_frag_init(&factory, dst_env->heap_frag); + } + + bp = bin->data; + *term = erts_decode_ext(&factory, &bp); + + if (is_non_value(*term)) { + return 0; + } + + erts_factory_close(&factory); + } + else { + erts_factory_dummy_init(&factory); + *term = erts_decode_ext(&factory, &bin->data); + if (is_non_value(*term)) { + return 0; + } + ASSERT(is_immed(*term)); + } + return 1; +} + int enif_is_identical(Eterm lhs, Eterm rhs) { return EQ(lhs,rhs); diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 21864c8b35..0333e95331 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -169,6 +169,8 @@ ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_unique_integer, (ErlNifEnv *env, E ERL_NIF_API_FUNC_DECL(int, enif_is_process_alive, (ErlNifEnv *env, ErlNifPid *pid)); ERL_NIF_API_FUNC_DECL(int, enif_is_port_alive, (ErlNifEnv *env, ErlNifPort *port_id)); ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, ErlNifPort* port_id)); +ERL_NIF_API_FUNC_DECL(int, enif_term_to_binary, (ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)); +ERL_NIF_API_FUNC_DECL(int, enif_binary_to_term, (ErlNifEnv *env, ErlNifBinary *bin, ERL_NIF_TERM *term)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! @@ -330,6 +332,8 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_is_process_alive ERL_NIF_API_FUNC_MACRO(enif_is_process_alive) # define enif_is_port_alive ERL_NIF_API_FUNC_MACRO(enif_is_port_alive) # define enif_get_local_port ERL_NIF_API_FUNC_MACRO(enif_get_local_port) +# define enif_term_to_binary ERL_NIF_API_FUNC_MACRO(enif_term_to_binary) +# define enif_binary_to_term ERL_NIF_API_FUNC_MACRO(enif_binary_to_term) /* ** ADD NEW ENTRIES HERE (before this comment) diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 3202986ce7..af25af9eeb 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -44,7 +44,8 @@ nif_nan_and_inf/1, nif_atom_too_long/1, nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1, nif_now_time/1, nif_cpu_time/1, nif_unique_integer/1, - nif_is_process_alive/1, nif_is_port_alive/1 + nif_is_process_alive/1, nif_is_port_alive/1, + nif_term_to_binary/1, nif_binary_to_term/1 ]). -export([many_args_100/100]). @@ -77,7 +78,8 @@ all() -> nif_exception, nif_nan_and_inf, nif_atom_too_long, nif_monotonic_time, nif_time_offset, nif_convert_time_unit, nif_now_time, nif_cpu_time, nif_unique_integer, - nif_is_process_alive, nif_is_port_alive + nif_is_process_alive, nif_is_port_alive, + nif_term_to_binary, nif_binary_to_term ]. init_per_testcase(_Case, Config) -> @@ -1958,6 +1960,23 @@ nif_is_port_alive(Config) -> port_close(Port), false = is_port_alive_nif(Port). +nif_term_to_binary(Config) -> + ensure_lib_loaded(Config), + T = {#{ok => nok}, <<0:8096>>, lists:seq(1,100)}, + Bin = term_to_binary(T), + ct:log("~p",[Bin]), + Bin = term_to_binary_nif(T, undefined), + true = term_to_binary_nif(T, self()), + receive Bin -> ok end. + +nif_binary_to_term(Config) -> + ensure_lib_loaded(Config), + T = {#{ok => nok}, <<0:8096>>, lists:seq(1,100)}, + Bin = term_to_binary(T), + T = binary_to_term_nif(Bin, undefined), + true = binary_to_term_nif(Bin, self()), + receive T -> ok end. + %% The NIFs: lib_version() -> undefined. call_history() -> ?nif_stub. @@ -2018,6 +2037,8 @@ call_nif_atom_too_long(_) -> ?nif_stub. unique_integer_nif(_) -> ?nif_stub. is_process_alive_nif(_) -> ?nif_stub. is_port_alive_nif(_) -> ?nif_stub. +term_to_binary_nif(_, _) -> ?nif_stub. +binary_to_term_nif(_, _) -> ?nif_stub. %% maps is_map_nif(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 2ce0168788..9db7614405 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -2030,6 +2030,55 @@ static ERL_NIF_TERM is_port_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM a return atom_false; } +static ERL_NIF_TERM term_to_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary bin; + ErlNifPid pid; + ErlNifEnv *msg_env = env; + ERL_NIF_TERM term; + + if (enif_get_local_pid(env, argv[1], &pid)) + msg_env = enif_alloc_env(); + + if (!enif_term_to_binary(msg_env, argv[0], &bin)) + return enif_make_badarg(env); + + term = enif_make_binary(msg_env, &bin); + + if (msg_env != env) { + enif_send(env, &pid, msg_env, term); + enif_free_env(msg_env); + return atom_true; + } else { + return term; + } +} + +static ERL_NIF_TERM binary_to_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary bin; + ERL_NIF_TERM term; + ErlNifPid pid; + ErlNifEnv *msg_env = env; + + if (enif_get_local_pid(env, argv[1], &pid)) + msg_env = enif_alloc_env(); + + if (!enif_inspect_binary(env, argv[0], &bin)) + return enif_make_badarg(env); + + if (!enif_binary_to_term(env, &bin, &term)) + return enif_make_badarg(env); + + if (msg_env != env) { + enif_send(env, &pid, msg_env, term); + enif_free_env(msg_env); + return atom_true; + } else { + return term; + } +} + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -2107,7 +2156,9 @@ static ErlNifFunc nif_funcs[] = {"cpu_time", 0, cpu_time}, {"unique_integer_nif", 1, unique_integer}, {"is_process_alive_nif", 1, is_process_alive}, - {"is_port_alive_nif", 1, is_port_alive} + {"is_port_alive_nif", 1, is_port_alive}, + {"term_to_binary_nif", 2, term_to_binary}, + {"binary_to_term_nif", 2, binary_to_term} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) -- cgit v1.2.3 From 209c5cf22b5cdc70eb48e6afdcddfa7132471aab Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 1 Feb 2016 11:01:40 +0100 Subject: erts: Add enif_port_command --- erts/doc/src/erl_nif.xml | 32 ++++ erts/emulator/beam/erl_nif.c | 23 +++ erts/emulator/beam/erl_nif_api_funcs.h | 2 + erts/emulator/beam/erl_port.h | 2 + erts/emulator/beam/erl_port_task.c | 30 +++- erts/emulator/beam/erl_port_task.h | 2 + erts/emulator/beam/io.c | 223 ++++++++++++++++++++++++-- erts/emulator/test/nif_SUITE.erl | 36 ++++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 15 +- 9 files changed, 346 insertions(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 47d84bb813..81b6eed24a 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -1464,6 +1464,38 @@ enif_map_iterator_destroy(env, &iter); and upgrade.

+ intenif_port_command(ErlNifEnv* env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg) + Send a port_command to to_port + +

This function works the same as erlang:port_command/2 + except that it is always completely asynchronous. This call may return false + if it detects that the port is already dead, otherwise it will return true. +

+ + env + The environment of the calling process. May not be NULL. + *to_port + The port id of the receiving port. The port id should refer to a + port on the local node. + msg_env + The environment of the message term. Can be a process + independent environment allocated with + enif_alloc_env or NULL. + msg + The message term to send. The same limitations apply as on the + payload to erlang:port_command/2. + +

Using a msg_env of NULL is an optimization which groups together + calls to enif_alloc_env, enif_make_copy, enif_port_command + and enif_free_env into one call. This optimization is only usefull + when a majority of the terms are to be copied from env to the msg_env.

+

The call may return false if it detects that the command failed for some reason. Otherwise true is returned.

+

See also:

+ + enif_get_local_port + +
+
void *enif_priv_data(ErlNifEnv* env) Get the private data of a NIF library

Return the pointer to the private data that was set by load, diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 21bf752bb6..6ed89780b4 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -375,6 +375,29 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, return 1; } +int +enif_port_command(ErlNifEnv *env, const ErlNifPort* to_port, + ErlNifEnv *msg_env, ERL_NIF_TERM msg) +{ + + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + int scheduler = esdp ? esdp->no : 0; + Port *prt; + + if (scheduler == 0 || !env) + return 0; + + prt = erts_port_lookup(to_port->port_id, + (erts_port_synchronous_ops + ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + : ERTS_PORT_SFLGS_INVALID_LOOKUP)); + + if (!prt) + return 0; + + return erts_port_output_async(prt, env->proc->common.id, msg); +} + ERL_NIF_TERM enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term) { Uint sz; diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 0333e95331..32afb53bfc 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -171,6 +171,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_is_port_alive, (ErlNifEnv *env, ErlNifPort *port ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, ErlNifPort* port_id)); ERL_NIF_API_FUNC_DECL(int, enif_term_to_binary, (ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)); ERL_NIF_API_FUNC_DECL(int, enif_binary_to_term, (ErlNifEnv *env, ErlNifBinary *bin, ERL_NIF_TERM *term)); +ERL_NIF_API_FUNC_DECL(int, enif_port_command, (ErlNifEnv *env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! @@ -334,6 +335,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_get_local_port ERL_NIF_API_FUNC_MACRO(enif_get_local_port) # define enif_term_to_binary ERL_NIF_API_FUNC_MACRO(enif_term_to_binary) # define enif_binary_to_term ERL_NIF_API_FUNC_MACRO(enif_binary_to_term) +# define enif_port_command ERL_NIF_API_FUNC_MACRO(enif_port_command) /* ** ADD NEW ENTRIES HERE (before this comment) diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index fa97707a87..dbc6ead216 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -949,6 +949,8 @@ ErtsPortOpResult erts_port_control(Process *, Port *, unsigned int, Eterm, Eterm ErtsPortOpResult erts_port_call(Process *, Port *, unsigned int, Eterm, Eterm *); ErtsPortOpResult erts_port_info(Process *, Port *, Eterm, Eterm *); +int erts_port_output_async(Port *, Eterm, Eterm); + /* * Signals from ports to ports. Used by sys drivers. */ diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index e85395e062..b200344af5 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -180,10 +180,9 @@ p2p_sig_data_to_task(ErtsProc2PortSigData *sigdp) return ptp; } -ErtsProc2PortSigData * -erts_port_task_alloc_p2p_sig_data(void) +static ERTS_INLINE ErtsProc2PortSigData * +p2p_sig_data_init(ErtsPortTask *ptp) { - ErtsPortTask *ptp = port_task_alloc(); ptp->type = ERTS_PORT_TASK_PROC_SIG; ptp->u.alive.flags = ERTS_PT_FLG_SIG_DEP; @@ -194,6 +193,31 @@ erts_port_task_alloc_p2p_sig_data(void) return &ptp->u.alive.td.psig.data; } +ErtsProc2PortSigData * +erts_port_task_alloc_p2p_sig_data(void) +{ + ErtsPortTask *ptp = port_task_alloc(); + + return p2p_sig_data_init(ptp); +} + +ErtsProc2PortSigData * +erts_port_task_alloc_p2p_sig_data_extra(size_t extra, void **extra_ptr) +{ + ErtsPortTask *ptp = erts_alloc(ERTS_ALC_T_PORT_TASK, + sizeof(ErtsPortTask) + extra); + + *extra_ptr = ptp+1; + + return p2p_sig_data_init(ptp); +} + +void +erts_port_task_free_p2p_sig_data(ErtsProc2PortSigData *sigdp) +{ + schedule_port_task_free(p2p_sig_data_to_task(sigdp)); +} + static ERTS_INLINE Eterm task_caller(ErtsPortTask *ptp) { diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h index 335f7a77d5..56cef4e352 100644 --- a/erts/emulator/beam/erl_port_task.h +++ b/erts/emulator/beam/erl_port_task.h @@ -269,6 +269,8 @@ int erts_port_task_schedule(Eterm, void erts_port_task_free_port(Port *); int erts_port_is_scheduled(Port *); ErtsProc2PortSigData *erts_port_task_alloc_p2p_sig_data(void); +ErtsProc2PortSigData *erts_port_task_alloc_p2p_sig_data_extra(size_t extra, void **extra_ptr); +void erts_port_task_free_p2p_sig_data(ErtsProc2PortSigData *sigdp); #ifdef ERTS_SMP void erts_enqueue_port(ErtsRunQueue *rq, Port *pp); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 797e4cdadc..093334aab7 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1749,7 +1749,6 @@ cleanup_scheduled_outputv(ErlIOVec *ev, ErlDrvBinary *cbinp) driver_free_binary(ev->binv[i]); if (cbinp) driver_free_binary(cbinp); - erts_free(ERTS_ALC_T_DRV_CMD_DATA, ev); } static int @@ -1887,6 +1886,188 @@ port_sig_output(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *si return ERTS_PORT_REDS_CMD_OUTPUT; } + +/* + * This erts_port_output will always create a port task. + * The call is treated as a port_command call, i.e. no + * badsig i generated if the input in invalid. However + * an error_logger message is generated. + */ +int +erts_port_output_async(Port *prt, Eterm from, Eterm list) +{ + + ErtsPortOpResult res; + ErtsProc2PortSigData *sigdp; + erts_driver_t *drv = prt->drv_ptr; + size_t size; + int task_flags; + ErtsProc2PortSigCallback port_sig_callback; + ErlDrvBinary *cbin = NULL; + ErlIOVec *evp = NULL; + char *buf = NULL; + ErtsPortTaskHandle *ns_pthp; + + if (drv->outputv) { + ErlIOVec ev; + SysIOVec* ivp; + ErlDrvBinary** bvp; + int vsize; + Uint csize; + Uint pvsize; + Uint pcsize; + size_t iov_offset, binv_offset, alloc_size; + Uint blimit = 0; + char *ptr; + int i; + + Eterm* bptr = NULL; + Uint offset; + + if (is_binary(list)) { + /* We optimize for when we get a procbin without offset */ + Eterm real_bin; + int bitoffs; + int bitsize; + ERTS_GET_REAL_BIN(list, real_bin, offset, bitoffs, bitsize); + bptr = binary_val(real_bin); + if (*bptr == HEADER_PROC_BIN && bitoffs == 0) { + size = binary_size(list); + vsize = 1; + } else + bptr = NULL; + } + + if (!bptr) { + if (io_list_vec_len(list, &vsize, &csize, &pvsize, &pcsize, &size)) + goto bad_value; + + /* To pack or not to pack (small binaries) ...? */ + if (vsize >= SMALL_WRITE_VEC) { + /* Do pack */ + vsize = pvsize + 1; + csize = pcsize; + blimit = ERL_SMALL_IO_BIN_LIMIT; + } + cbin = driver_alloc_binary(csize); + if (!cbin) + erts_alloc_enomem(ERTS_ALC_T_DRV_BINARY, ERTS_SIZEOF_Binary(csize)); + } + + + iov_offset = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(ErlIOVec)); + binv_offset = iov_offset; + binv_offset += ERTS_ALC_DATA_ALIGN_SIZE((vsize+1)*sizeof(SysIOVec)); + alloc_size = binv_offset; + alloc_size += (vsize+1)*sizeof(ErlDrvBinary *); + + sigdp = erts_port_task_alloc_p2p_sig_data_extra(alloc_size, (void**)&ptr); + + evp = (ErlIOVec *) ptr; + ivp = evp->iov = (SysIOVec *) (ptr + iov_offset); + bvp = evp->binv = (ErlDrvBinary **) (ptr + binv_offset); + + ivp[0].iov_base = NULL; + ivp[0].iov_len = 0; + bvp[0] = NULL; + + if (bptr) { + ProcBin* pb = (ProcBin *) bptr; + + ivp[1].iov_base = pb->bytes+offset; + ivp[1].iov_len = size; + bvp[1] = Binary2ErlDrvBinary(pb->val); + + evp->vsize = 1; + } else { + + evp->vsize = io_list_to_vec(list, ivp+1, bvp+1, cbin, blimit); + if (evp->vsize < 0) { + if (evp != &ev) + erts_free(ERTS_ALC_T_DRV_CMD_DATA, evp); + driver_free_binary(cbin); + goto bad_value; + } + } +#if 0 + /* This assertion may say something useful, but it can + be falsified during the emulator test suites. */ + ASSERT(evp->vsize == vsize); +#endif + evp->vsize++; + evp->size = size; /* total size */ + + /* Need to increase refc on all binaries */ + for (i = 1; i < evp->vsize; i++) + if (bvp[i]) + driver_binary_inc_refc(bvp[i]); + + sigdp->flags = ERTS_P2P_SIG_TYPE_OUTPUTV; + sigdp->u.outputv.from = from; + sigdp->u.outputv.evp = evp; + sigdp->u.outputv.cbinp = cbin; + port_sig_callback = port_sig_outputv; + } else { + ErlDrvSizeT ERTS_DECLARE_DUMMY(r); + + /* + * Apperently there exist code that write 1 byte to + * much in buffer. Where it resides I don't know, but + * we can live with one byte extra allocated... + */ + + if (erts_iolist_size(list, &size)) + goto bad_value; + + buf = erts_alloc(ERTS_ALC_T_DRV_CMD_DATA, size + 1); + + r = erts_iolist_to_buf(list, buf, size); + ASSERT(ERTS_IOLIST_TO_BUF_SUCCEEDED(r)); + + sigdp = erts_port_task_alloc_p2p_sig_data(); + sigdp->flags = ERTS_P2P_SIG_TYPE_OUTPUT; + sigdp->u.output.from = from; + sigdp->u.output.bufp = buf; + sigdp->u.output.size = size; + port_sig_callback = port_sig_output; + } + sigdp->flags = 0; + ns_pthp = NULL; + task_flags = 0; + + res = erts_schedule_proc2port_signal(NULL, + prt, + ERTS_INVALID_PID, + NULL, + sigdp, + task_flags, + ns_pthp, + port_sig_callback); + + if (res != ERTS_PORT_OP_SCHEDULED) { + if (drv->outputv) + cleanup_scheduled_outputv(evp, cbin); + else + cleanup_scheduled_output(buf); + return 1; + } + return 1; + +bad_value: + + /* + * We call badsig directly here as this function is called with + * the main lock of the calling process still held. + * At the moment this operation is always not a bang_op, so + * only an error_logger message should be generated, no badsig. + */ + + badsig_received(0, prt, erts_atomic32_read_nob(&prt->state), 1); + + return 0; + +} + ErtsPortOpResult erts_port_output(Process *c_p, int flags, @@ -1896,7 +2077,7 @@ erts_port_output(Process *c_p, Eterm *refp) { ErtsPortOpResult res; - ErtsProc2PortSigData *sigdp; + ErtsProc2PortSigData *sigdp = NULL; erts_driver_t *drv = prt->drv_ptr; size_t size; int try_call; @@ -1978,10 +2159,13 @@ erts_port_output(Process *c_p, evp = &ev; } else { - char *ptr = erts_alloc((try_call - ? ERTS_ALC_T_TMP - : ERTS_ALC_T_DRV_CMD_DATA), alloc_size); - + char *ptr; + if (try_call) { + ptr = erts_alloc(ERTS_ALC_T_TMP, alloc_size); + } else { + sigdp = erts_port_task_alloc_p2p_sig_data_extra( + alloc_size, (void**)&ptr); + } evp = (ErlIOVec *) ptr; ivp = evp->iov = (SysIOVec *) (ptr + iov_offset); bvp = evp->binv = (ErlDrvBinary **) (ptr + binv_offset); @@ -2010,9 +2194,12 @@ erts_port_output(Process *c_p, bvp[0] = NULL; evp->vsize = io_list_to_vec(list, ivp+1, bvp+1, cbin, blimit); if (evp->vsize < 0) { - if (evp != &ev) - erts_free(try_call ? ERTS_ALC_T_TMP : ERTS_ALC_T_DRV_CMD_DATA, - evp); + if (evp != &ev) { + if (try_call) + erts_free(ERTS_ALC_T_TMP, evp); + else + erts_port_task_free_p2p_sig_data(sigdp); + } driver_free_binary(cbin); goto bad_value; } @@ -2064,8 +2251,10 @@ erts_port_output(Process *c_p, /* Fall through... */ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: driver_free_binary(cbin); - if (evp != &ev) + if (evp != &ev) { + ASSERT(!sigdp); erts_free(ERTS_ALC_T_TMP, evp); + } if (try_call_res != ERTS_TRY_IMM_DRV_CALL_OK) return ERTS_PORT_OP_DROPPED; if (c_p) @@ -2076,8 +2265,10 @@ erts_port_output(Process *c_p, if (async_nosuspend && (sched_flags & (busy_flgs|ERTS_PTS_FLG_EXIT))) { driver_free_binary(cbin); - if (evp != &ev) + if (evp != &ev) { + ASSERT(!sigdp); erts_free(ERTS_ALC_T_TMP, evp); + } return ((sched_flags & ERTS_PTS_FLG_EXIT) ? ERTS_PORT_OP_DROPPED : ERTS_PORT_OP_BUSY); @@ -2092,9 +2283,16 @@ erts_port_output(Process *c_p, if (bvp[i]) driver_binary_inc_refc(bvp[i]); - new_evp = erts_alloc(ERTS_ALC_T_DRV_CMD_DATA, alloc_size); + /* The port task and iovec is allocated in the + same structure as an optimization. This + is especially important in erts_port_output_async + of when !try_call */ + ASSERT(sigdp == NULL); + sigdp = erts_port_task_alloc_p2p_sig_data_extra( + alloc_size, (void**)&new_evp); if (evp != &ev) { + /* Copy from TMP alloc to port task */ sys_memcpy((void *) new_evp, (void *) evp, alloc_size); new_evp->iov = (SysIOVec *) (((char *) new_evp) + iov_offset); @@ -2142,7 +2340,6 @@ erts_port_output(Process *c_p, evp = new_evp; } - sigdp = erts_port_task_alloc_p2p_sig_data(); sigdp->flags = ERTS_P2P_SIG_TYPE_OUTPUTV; sigdp->u.outputv.from = from; sigdp->u.outputv.evp = evp; diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index af25af9eeb..bbd523b4ef 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -45,7 +45,8 @@ nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1, nif_now_time/1, nif_cpu_time/1, nif_unique_integer/1, nif_is_process_alive/1, nif_is_port_alive/1, - nif_term_to_binary/1, nif_binary_to_term/1 + nif_term_to_binary/1, nif_binary_to_term/1, + nif_port_command/1 ]). -export([many_args_100/100]). @@ -79,7 +80,8 @@ all() -> nif_monotonic_time, nif_time_offset, nif_convert_time_unit, nif_now_time, nif_cpu_time, nif_unique_integer, nif_is_process_alive, nif_is_port_alive, - nif_term_to_binary, nif_binary_to_term + nif_term_to_binary, nif_binary_to_term, + nif_port_command ]. init_per_testcase(_Case, Config) -> @@ -1977,6 +1979,35 @@ nif_binary_to_term(Config) -> true = binary_to_term_nif(Bin, self()), receive T -> ok end. +nif_port_command(Config) -> + ensure_lib_loaded(Config), + + Port = open_port({spawn,"/bin/sh -s unix:cmd"},[stderr_to_stdout,eof]), + true = port_command_nif(Port, "echo hello\n"), + receive {Port,{data,"hello\n"}} -> ok + after 1000 -> ct:fail(timeout) end, + + RefcBin = lists:flatten([lists:duplicate(100, "hello"),"\n"]), + true = port_command_nif(Port, iolist_to_binary(["echo ",RefcBin])), + receive {Port,{data,RefcBin}} -> ok + after 1000 -> ct:fail(timeout) end, + + %% Test that invalid arguments correctly returns + %% badarg and that the port survives. + {'EXIT', {badarg, _}} = (catch port_command_nif(Port, [ok])), + + IoList = [lists:duplicate(100,<<"hello">>),"\n"], + true = port_command_nif(Port, ["echo ",IoList]), + FlatIoList = binary_to_list(iolist_to_binary(IoList)), + receive {Port,{data,FlatIoList}} -> ok + after 1000 -> ct:fail(timeout) end, + + port_close(Port), + + {'EXIT', {badarg, _}} = (catch port_command_nif(Port, "echo hello\n")), + + ok. + %% The NIFs: lib_version() -> undefined. call_history() -> ?nif_stub. @@ -2039,6 +2070,7 @@ is_process_alive_nif(_) -> ?nif_stub. is_port_alive_nif(_) -> ?nif_stub. term_to_binary_nif(_, _) -> ?nif_stub. binary_to_term_nif(_, _) -> ?nif_stub. +port_command_nif(_, _) -> ?nif_stub. %% maps is_map_nif(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 9db7614405..317f440257 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -2079,6 +2079,18 @@ static ERL_NIF_TERM binary_to_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM } } +static ERL_NIF_TERM port_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifPort port; + + if (!enif_get_local_port(env, argv[0], &port)) + return enif_make_badarg(env); + + if (!enif_port_command(env, &port, NULL, argv[1])) + return enif_make_badarg(env); + return atom_true; +} + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -2158,7 +2170,8 @@ static ErlNifFunc nif_funcs[] = {"is_process_alive_nif", 1, is_process_alive}, {"is_port_alive_nif", 1, is_port_alive}, {"term_to_binary_nif", 2, term_to_binary}, - {"binary_to_term_nif", 2, binary_to_term} + {"binary_to_term_nif", 2, binary_to_term}, + {"port_command_nif", 2, port_command} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) -- cgit v1.2.3 From a2a86dadc648dda68b5221a7c1d83b9238be1e25 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 12 Feb 2016 18:52:20 +0100 Subject: erts: Improve enif_binary_to_term * Accept a raw data buffer instead of ErlNifBinary * Accept option ERL_NIF_BIN2TERM_SAFE * Return number of read bytes --- erts/doc/src/erl_nif.xml | 37 ++++++++++++++++++------ erts/emulator/beam/beam_load.c | 8 +++--- erts/emulator/beam/erl_nif.c | 41 ++++++++++++++------------- erts/emulator/beam/erl_nif.h | 4 +++ erts/emulator/beam/erl_nif_api_funcs.h | 2 +- erts/emulator/beam/external.c | 12 ++++++-- erts/emulator/beam/external.h | 2 +- erts/emulator/beam/io.c | 4 +-- erts/emulator/test/nif_SUITE.erl | 17 ++++++++--- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 20 ++++++++----- 10 files changed, 97 insertions(+), 50 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 81b6eed24a..7a8325c200 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -524,6 +524,18 @@ typedef struct {

Note that ErlNifBinary is a semi-opaque type and you are only allowed to read fields size and data.

+ + ErlNifBinaryToTerm + +

An enumeration of the options that can be given to + enif_binary_to_term. + For default behavior, use the value 0.

+ + ERL_NIF_BIN2TERM_SAFE +

Use this option when receiving data from untrusted sources.

+
+
+ ErlNifPid

ErlNifPid is a process identifier (pid). In contrast to @@ -655,16 +667,23 @@ typedef enum { have been allocated with enif_alloc_env.

- intenif_binary_to_term(ErlNifEnv *env, ErlNifBinary *bin, ERL_NIF_TERM *term) - Convert a term from the external format + size_tenif_binary_to_term(ErlNifEnv *env, const unsigned char* data, size_t size, ERL_NIF_TERM *term, ErlNifBinaryToTerm opts) + Create a term from the external format -

Returns a term that is the result of decoding bin - according to the Erlang external term format.

-

See also:

- - erlang:binary_to_term/1 - enif_term_to_binary - +

Create a term that is the result of decoding the binary data + at data, which must be encoded according to the Erlang external term format. + No more than size bytes are read from data. Argument opts + correspond to the second argument to + erlang:binary_to_term/2, and must be either 0 or + ERL_NIF_BIN2TERM_SAFE.

+

On success, store the resulting term at *term and return + the actual number of bytes read. Return zero if decoding fails or if opts + is invalid.

+

See also: + ErlNifBinaryToTerm, + erlang:binary_to_term/2 and + enif_term_to_binary. +

intenif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs) diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index f115df935f..d3d278fb81 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -1548,7 +1548,7 @@ read_literal_table(LoaderState* stp) erts_factory_heap_frag_init(&factory, new_literal_fragment(heap_size)); factory.alloc_type = ERTS_ALC_T_PREPARED_CODE; - val = erts_decode_ext(&factory, &p); + val = erts_decode_ext(&factory, &p, 0); if (is_non_value(val)) { LoadError1(stp, "literal %d: bad external format", i); @@ -1559,7 +1559,7 @@ read_literal_table(LoaderState* stp) } else { erts_factory_dummy_init(&factory); - val = erts_decode_ext(&factory, &p); + val = erts_decode_ext(&factory, &p, 0); if (is_non_value(val)) { LoadError1(stp, "literal %d: bad external format", i); } @@ -5719,7 +5719,7 @@ attributes_for_module(Process* p, /* Process whose heap to use. */ if (ext != NULL) { ErtsHeapFactory factory; erts_factory_proc_prealloc_init(&factory, p, code_hdr->attr_size_on_heap); - result = erts_decode_ext(&factory, &ext); + result = erts_decode_ext(&factory, &ext, 0); if (is_value(result)) { erts_factory_close(&factory); } @@ -5742,7 +5742,7 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */ if (ext != NULL) { ErtsHeapFactory factory; erts_factory_proc_prealloc_init(&factory, p, code_hdr->compile_size_on_heap); - result = erts_decode_ext(&factory, &ext); + result = erts_decode_ext(&factory, &ext, 0); if (is_value(result)) { erts_factory_close(&factory); } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 6ed89780b4..d8f2272c6d 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -686,17 +686,25 @@ int enif_term_to_binary(ErlNifEnv *dst_env, ERL_NIF_TERM term, return 1; } -int enif_binary_to_term(ErlNifEnv *dst_env, ErlNifBinary *bin, - ERL_NIF_TERM *term) +size_t enif_binary_to_term(ErlNifEnv *dst_env, + const unsigned char* data, + size_t data_sz, + ERL_NIF_TERM *term, + ErlNifBinaryToTerm opts) { Sint size; ErtsHeapFactory factory; + byte *bp = (byte*) data; - if ((size = erts_decode_ext_size(bin->data, bin->size)) < 0) + ERTS_CT_ASSERT(ERL_NIF_BIN2TERM_SAFE == ERTS_DIST_EXT_BTT_SAFE); + + if (opts & ~ERL_NIF_BIN2TERM_SAFE) { + return 0; + } + if ((size = erts_decode_ext_size(bp, data_sz)) < 0) return 0; if (size > 0) { - byte *bp; if (is_internal_pid(dst_env->proc->common.id)) { flush_env(dst_env); @@ -710,25 +718,18 @@ int enif_binary_to_term(ErlNifEnv *dst_env, ErlNifBinary *bin, erts_factory_heap_frag_init(&factory, dst_env->heap_frag); } + } else { + erts_factory_dummy_init(&factory); + } - bp = bin->data; - *term = erts_decode_ext(&factory, &bp); - - if (is_non_value(*term)) { - return 0; - } + *term = erts_decode_ext(&factory, &bp, (Uint32)opts); - erts_factory_close(&factory); - } - else { - erts_factory_dummy_init(&factory); - *term = erts_decode_ext(&factory, &bin->data); - if (is_non_value(*term)) { - return 0; - } - ASSERT(is_immed(*term)); + if (is_non_value(*term)) { + return 0; } - return 1; + erts_factory_close(&factory); + ASSERT(bp > data); + return bp - data; } int enif_is_identical(Eterm lhs, Eterm rhs) diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 6ee22a693c..bc1f59bc90 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -207,6 +207,10 @@ typedef enum { ERL_NIF_UNIQUE_MONOTONIC = (1 << 1) } ErlNifUniqueInteger; +typedef enum { + ERL_NIF_BIN2TERM_SAFE = 0x20000000 +} ErlNifBinaryToTerm; + #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct { diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 32afb53bfc..35058afe7c 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -170,7 +170,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_is_process_alive, (ErlNifEnv *env, ErlNifPid *pi ERL_NIF_API_FUNC_DECL(int, enif_is_port_alive, (ErlNifEnv *env, ErlNifPort *port_id)); ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, ErlNifPort* port_id)); ERL_NIF_API_FUNC_DECL(int, enif_term_to_binary, (ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)); -ERL_NIF_API_FUNC_DECL(int, enif_binary_to_term, (ErlNifEnv *env, ErlNifBinary *bin, ERL_NIF_TERM *term)); +ERL_NIF_API_FUNC_DECL(size_t, enif_binary_to_term, (ErlNifEnv *env, const unsigned char* data, size_t sz, ERL_NIF_TERM *term, unsigned int opts)); ERL_NIF_API_FUNC_DECL(int, enif_port_command, (ErlNifEnv *env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)); /* diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index aac1490f0c..f5eb13421d 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -967,15 +967,23 @@ erts_decode_dist_ext(ErtsHeapFactory* factory, return THE_NON_VALUE; } -Eterm erts_decode_ext(ErtsHeapFactory* factory, byte **ext) +Eterm erts_decode_ext(ErtsHeapFactory* factory, byte **ext, Uint32 flags) { + ErtsDistExternal ede, *edep; Eterm obj; byte *ep = *ext; if (*ep++ != VERSION_MAGIC) { erts_factory_undo(factory); return THE_NON_VALUE; } - ep = dec_term(NULL, factory, ep, &obj, NULL); + if (flags) { + ASSERT(flags == ERTS_DIST_EXT_BTT_SAFE); + ede.flags = flags; /* a dummy struct just for the flags */ + edep = &ede; + } else { + edep = NULL; + } + ep = dec_term(edep, factory, ep, &obj, NULL); if (!ep) { #ifdef DEBUG bin_write(ERTS_PRINT_STDERR,NULL,*ext,500); diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index d12051c6b4..87eff2fe9f 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -191,7 +191,7 @@ Eterm erts_decode_dist_ext(ErtsHeapFactory* factory, ErtsDistExternal *); Sint erts_decode_ext_size(byte*, Uint); Sint erts_decode_ext_size_ets(byte*, Uint); -Eterm erts_decode_ext(ErtsHeapFactory*, byte**); +Eterm erts_decode_ext(ErtsHeapFactory*, byte**, Uint32 flags); Eterm erts_decode_ext_ets(ErtsHeapFactory*, byte*); Eterm erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 093334aab7..c7b21c0386 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4570,7 +4570,7 @@ port_sig_call(Port *prt, (void) erts_factory_message_create(&factory, rp, &rp_locks, hsz); endp = (byte *) resp_bufp; - msg = erts_decode_ext(&factory, &endp); + msg = erts_decode_ext(&factory, &endp, 0); if (is_value(msg)) { hp = erts_produce_heap(&factory, 3, @@ -4689,7 +4689,7 @@ erts_port_call(Process* c_p, hsz += 3; erts_factory_proc_prealloc_init(&factory, c_p, hsz); endp = (byte *) resp_bufp; - term = erts_decode_ext(&factory, &endp); + term = erts_decode_ext(&factory, &endp, 0); if (term == THE_NON_VALUE) return ERTS_PORT_OP_BADARG; hp = erts_produce_heap(&factory,3,0); diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index bbd523b4ef..4dcfbf35ca 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1971,13 +1971,22 @@ nif_term_to_binary(Config) -> true = term_to_binary_nif(T, self()), receive Bin -> ok end. +-define(ERL_NIF_BIN2TERM_SAFE, 16#20000000). + nif_binary_to_term(Config) -> ensure_lib_loaded(Config), T = {#{ok => nok}, <<0:8096>>, lists:seq(1,100)}, Bin = term_to_binary(T), - T = binary_to_term_nif(Bin, undefined), - true = binary_to_term_nif(Bin, self()), - receive T -> ok end. + Len = byte_size(Bin), + {Len,T} = binary_to_term_nif(Bin, undefined, 0), + Len = binary_to_term_nif(Bin, self(), 0), + T = receive M -> M after 1000 -> timeout end, + + {Len, T} = binary_to_term_nif(Bin, undefined, ?ERL_NIF_BIN2TERM_SAFE), + false = binary_to_term_nif(<<131,100,0,14,"undefined_atom">>, + undefined, ?ERL_NIF_BIN2TERM_SAFE), + false = binary_to_term_nif(Bin, undefined, 1), + ok. nif_port_command(Config) -> ensure_lib_loaded(Config), @@ -2069,7 +2078,7 @@ unique_integer_nif(_) -> ?nif_stub. is_process_alive_nif(_) -> ?nif_stub. is_port_alive_nif(_) -> ?nif_stub. term_to_binary_nif(_, _) -> ?nif_stub. -binary_to_term_nif(_, _) -> ?nif_stub. +binary_to_term_nif(_, _, _) -> ?nif_stub. port_command_nif(_, _) -> ?nif_stub. %% maps diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 317f440257..b3c6cc5ba3 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -2057,25 +2057,31 @@ static ERL_NIF_TERM term_to_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM static ERL_NIF_TERM binary_to_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ErlNifBinary bin; - ERL_NIF_TERM term; + ERL_NIF_TERM term, ret_term; ErlNifPid pid; ErlNifEnv *msg_env = env; + unsigned int opts; + ErlNifUInt64 ret; if (enif_get_local_pid(env, argv[1], &pid)) msg_env = enif_alloc_env(); - if (!enif_inspect_binary(env, argv[0], &bin)) + if (!enif_inspect_binary(env, argv[0], &bin) + || !enif_get_uint(env, argv[2], &opts)) return enif_make_badarg(env); - if (!enif_binary_to_term(env, &bin, &term)) - return enif_make_badarg(env); + ret = enif_binary_to_term(msg_env, bin.data, bin.size, &term, + (ErlNifBinaryToTerm)opts); + if (!ret) + return atom_false; + ret_term = enif_make_uint64(env, ret); if (msg_env != env) { enif_send(env, &pid, msg_env, term); enif_free_env(msg_env); - return atom_true; + return ret_term; } else { - return term; + return enif_make_tuple2(env, ret_term, term); } } @@ -2170,7 +2176,7 @@ static ErlNifFunc nif_funcs[] = {"is_process_alive_nif", 1, is_process_alive}, {"is_port_alive_nif", 1, is_port_alive}, {"term_to_binary_nif", 2, term_to_binary}, - {"binary_to_term_nif", 2, binary_to_term}, + {"binary_to_term_nif", 3, binary_to_term}, {"port_command_nif", 2, port_command} }; -- cgit v1.2.3 From 42a7116dc64892ef4bf7a1483aa9df82d9a34439 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 12 Feb 2016 18:59:44 +0100 Subject: erts: Polish erl_nif docs --- erts/doc/src/erl_nif.xml | 69 ++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 37 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 7a8325c200..1e95634d1b 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -614,13 +614,13 @@ typedef enum { ErlNifUniqueInteger

An enumeration of the properties that can be requested from - - enif_unique_integer.

+ enif_unique_integer. + For default properties, use the value 0.

ERL_NIF_UNIQUE_POSITIVE -

A positive integer

+

Return only positive integers

ERL_NIF_UNIQUE_MONOTONIC -

A +

Return only strictly monotonically increasing integer corresponding to creation time

@@ -765,11 +765,10 @@ typedef enum { rounded using the floor function.

Returns ERL_NIF_TIME_ERROR if called with an invalid time unit argument.

-

See also:

- - ErlNifTime - ErlNifTimeUnit - +

See also: + ErlNifTime and + ErlNifTimeUnit. +

@@ -842,7 +841,7 @@ typedef enum {
intenif_get_local_port(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPort* port_id) Read an local port term -

If term is the port of a node local port, initialize the +

If term identifies a node local port, initialize the port variable *port_id from it and return true. Otherwise return false. No check if the port is alive is done.

@@ -1074,7 +1073,7 @@ typedef enum { enif_is_exception, but not to any other NIF API function.

See also: enif_has_pending_exception - and enif_raise_exception + and enif_raise_exception.

In earlier versions (older than erts-7.0, OTP 18) the return value from enif_make_badarg had to be returned from the NIF. This @@ -1320,10 +1319,9 @@ typedef enum { integer returned. It is possible to combine them by or:ing the two values together.

-

See also:

- - ErlNifUniqueInteger - +

See also: + ErlNifUniqueInteger. +

ERL_NIF_TERMenif_make_ulong(ErlNifEnv* env, unsigned long i) @@ -1408,7 +1406,7 @@ enif_map_iterator_destroy(env, &iter); Time unit of returned value.

- Returns + Returns the current Erlang monotonic time. Note that it is not uncommon with negative values. @@ -1416,11 +1414,10 @@ enif_map_iterator_destroy(env, &iter);

Returns ERL_NIF_TIME_ERROR if called with an invalid time unit argument, or if called from a thread that is not a scheduler thread.

-

See also:

- - ErlNifTime - ErlNifTimeUnit - +

See also: + ErlNifTime and + ErlNifTimeUnit. +

@@ -1509,10 +1506,7 @@ enif_map_iterator_destroy(env, &iter); and enif_free_env into one call. This optimization is only usefull when a majority of the terms are to be copied from env to the msg_env.

The call may return false if it detects that the command failed for some reason. Otherwise true is returned.

-

See also:

- - enif_get_local_port - +

See also: enif_get_local_port.

void *enif_priv_data(ErlNifEnv* env) @@ -1649,6 +1643,8 @@ enif_map_iterator_destroy(env, &iter); of cleared for reuse with enif_clear_env.

This function is only thread-safe when the emulator with SMP support is used. It can only be used in a non-SMP emulator from a NIF-calling thread.

+

Passing msg_env as NULL is only supported since + erts-8.0 (OTP 19).

unsignedenif_sizeof_resource(void* obj) @@ -1665,13 +1661,13 @@ enif_map_iterator_destroy(env, &iter); intenif_term_to_binary(ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin) Convert a term to the external format -

Returns a binary data object that is the result of encoding term - according to the Erlang external term format.

-

See also:

- - erlang:term_to_binary/1 - enif_binary_to_term - +

Allocates a new binary with enif_alloc_binary + and stores the result of encoding term according to the Erlang external term format.

+

Returns true on success or false if allocation failed.

+

See also: + erlang:term_to_binary/1 and + enif_binary_to_term. +

intenif_thread_create(char *name,ErlNifTid *tid,void * (*func)(void *),void *args,ErlNifThreadOpts *opts) @@ -1723,11 +1719,10 @@ enif_map_iterator_destroy(env, &iter);

Returns ERL_NIF_TIME_ERROR if called with an invalid time unit argument, or if called from a thread that is not a scheduler thread.

-

See also:

- - ErlNifTime - ErlNifTimeUnit - +

See also: + ErlNifTime and + ErlNifTimeUnit. +

-- cgit v1.2.3 From 043ab9055917a4f6d89f1fa2788079e0618928c4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 15 Feb 2016 19:23:12 +0100 Subject: erts: Remove printout when dec_term fails in DEBUG --- erts/emulator/beam/external.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index f5eb13421d..10f03636ec 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -985,9 +985,6 @@ Eterm erts_decode_ext(ErtsHeapFactory* factory, byte **ext, Uint32 flags) } ep = dec_term(edep, factory, ep, &obj, NULL); if (!ep) { -#ifdef DEBUG - bin_write(ERTS_PRINT_STDERR,NULL,*ext,500); -#endif return THE_NON_VALUE; } *ext = ep; -- cgit v1.2.3 From 1c630ab8793425aed1e487d25c71b8bbd9325e8b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 15 Feb 2016 19:28:52 +0100 Subject: erts: Fix bug in enif_term_to_binary Wait until after dec_term and factory_close to do cache_env(), otherwise we will cache the wrong state. --- erts/emulator/beam/erl_nif.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index d8f2272c6d..c6ece8c1c7 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -705,19 +705,8 @@ size_t enif_binary_to_term(ErlNifEnv *dst_env, return 0; if (size > 0) { - - if (is_internal_pid(dst_env->proc->common.id)) { - flush_env(dst_env); - erts_factory_proc_prealloc_init(&factory, dst_env->proc, size); - cache_env(dst_env); - } else { - - /* this is guaranteed to create a heap fragment */ - if (!alloc_heap(dst_env, size)) - return 0; - - erts_factory_heap_frag_init(&factory, dst_env->heap_frag); - } + flush_env(dst_env); + erts_factory_proc_prealloc_init(&factory, dst_env->proc, size); } else { erts_factory_dummy_init(&factory); } @@ -728,6 +717,8 @@ size_t enif_binary_to_term(ErlNifEnv *dst_env, return 0; } erts_factory_close(&factory); + cache_env(dst_env); + ASSERT(bp > data); return bp - data; } -- cgit v1.2.3 From 0138a122226b770749218abd77a2931977af5a47 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 15 Mar 2016 17:19:59 +0100 Subject: erts: Fix windows nif port tests --- erts/emulator/test/nif_SUITE.erl | 28 ++++++------ erts/emulator/test/nif_SUITE_data/Makefile.src | 3 +- erts/emulator/test/nif_SUITE_data/echo_drv.c | 62 ++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 erts/emulator/test/nif_SUITE_data/echo_drv.c (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 4dcfbf35ca..a185b72341 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1374,14 +1374,16 @@ get_length(Config) when is_list(Config) -> ensure_lib_loaded(Config) -> ensure_lib_loaded(Config, 1). ensure_lib_loaded(Config, Ver) -> + Path = ?config(data_dir, Config), case lib_version() of - undefined -> - Path = proplists:get_value(data_dir, Config), - Lib = "nif_SUITE." ++ integer_to_list(Ver), - ok = erlang:load_nif(filename:join(Path,Lib), []); - Ver when is_integer(Ver) -> - ok - end. + undefined -> + Lib = "nif_SUITE." ++ integer_to_list(Ver), + ok = erlang:load_nif(filename:join(Path,Lib), []); + Ver when is_integer(Ver) -> + ok + end, + erl_ddll:try_load(Path, echo_drv, []), + ok. make_atom(Config) when is_list(Config) -> ensure_lib_loaded(Config, 1), @@ -1957,7 +1959,7 @@ nif_is_process_alive(Config) -> nif_is_port_alive(Config) -> ensure_lib_loaded(Config), - Port = open_port({spawn,"/bin/sh -s unix:cmd"},[stderr_to_stdout,eof]), + Port = open_port({spawn,echo_drv},[eof]), true = is_port_alive_nif(Port), port_close(Port), false = is_port_alive_nif(Port). @@ -1991,13 +1993,13 @@ nif_binary_to_term(Config) -> nif_port_command(Config) -> ensure_lib_loaded(Config), - Port = open_port({spawn,"/bin/sh -s unix:cmd"},[stderr_to_stdout,eof]), - true = port_command_nif(Port, "echo hello\n"), + Port = open_port({spawn,echo_drv},[eof]), + true = port_command_nif(Port, "hello\n"), receive {Port,{data,"hello\n"}} -> ok after 1000 -> ct:fail(timeout) end, RefcBin = lists:flatten([lists:duplicate(100, "hello"),"\n"]), - true = port_command_nif(Port, iolist_to_binary(["echo ",RefcBin])), + true = port_command_nif(Port, iolist_to_binary(RefcBin)), receive {Port,{data,RefcBin}} -> ok after 1000 -> ct:fail(timeout) end, @@ -2006,14 +2008,14 @@ nif_port_command(Config) -> {'EXIT', {badarg, _}} = (catch port_command_nif(Port, [ok])), IoList = [lists:duplicate(100,<<"hello">>),"\n"], - true = port_command_nif(Port, ["echo ",IoList]), + true = port_command_nif(Port, [IoList]), FlatIoList = binary_to_list(iolist_to_binary(IoList)), receive {Port,{data,FlatIoList}} -> ok after 1000 -> ct:fail(timeout) end, port_close(Port), - {'EXIT', {badarg, _}} = (catch port_command_nif(Port, "echo hello\n")), + {'EXIT', {badarg, _}} = (catch port_command_nif(Port, "hello\n")), ok. diff --git a/erts/emulator/test/nif_SUITE_data/Makefile.src b/erts/emulator/test/nif_SUITE_data/Makefile.src index ab4ff77add..fbb8978771 100644 --- a/erts/emulator/test/nif_SUITE_data/Makefile.src +++ b/erts/emulator/test/nif_SUITE_data/Makefile.src @@ -4,8 +4,7 @@ NIF_LIBS = nif_SUITE.1@dll@ \ nif_mod.2@dll@ \ nif_mod.3@dll@ -all: $(NIF_LIBS) basic@dll@ rwlock@dll@ tsd@dll@ - +all: $(NIF_LIBS) basic@dll@ rwlock@dll@ tsd@dll@ echo_drv@dll@ @SHLIB_RULES@ diff --git a/erts/emulator/test/nif_SUITE_data/echo_drv.c b/erts/emulator/test/nif_SUITE_data/echo_drv.c new file mode 100644 index 0000000000..2b3510c641 --- /dev/null +++ b/erts/emulator/test/nif_SUITE_data/echo_drv.c @@ -0,0 +1,62 @@ +#include +#include "erl_driver.h" + +static ErlDrvPort erlang_port; +static ErlDrvData echo_start(ErlDrvPort, char *); +static void from_erlang(ErlDrvData, char*, ErlDrvSizeT); +static ErlDrvSSizeT echo_call(ErlDrvData drv_data, unsigned int command, + char *buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen, unsigned *ret_flags); +static ErlDrvEntry echo_driver_entry = { + NULL, /* Init */ + echo_start, + NULL, /* Stop */ + from_erlang, + NULL, /* Ready input */ + NULL, /* Ready output */ + "echo_drv", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + echo_call, + NULL, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, + NULL, + NULL, + NULL +}; + +DRIVER_INIT(echo_drv) +{ + return &echo_driver_entry; +} + +static ErlDrvData +echo_start(ErlDrvPort port, char *buf) +{ + return (ErlDrvData) port; +} + +static void +from_erlang(ErlDrvData data, char *buf, ErlDrvSizeT count) +{ + driver_output((ErlDrvPort) data, buf, count); +} + +static ErlDrvSSizeT +echo_call(ErlDrvData drv_data, unsigned int command, + char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen, + unsigned *ret_flags) +{ + *rbuf = buf; + *ret_flags |= DRIVER_CALL_KEEP_BUFFER; + return len; +} + -- cgit v1.2.3 From ed81bb9ed8114d2059783e2c2fdae526d3a36e1e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 15 Feb 2016 19:26:54 +0100 Subject: erts: Fix bug in enif_send Let cache_env() set env->heap_frag to same as MBUF(p) as it is in any other case. --- erts/emulator/beam/erl_nif.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c6ece8c1c7..beef6983cd 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -209,6 +209,7 @@ static void flush_env(ErlNifEnv* env) */ static void cache_env(ErlNifEnv* env) { + env->heap_frag = MBUF(env->proc); if (env->heap_frag == NULL) { ASSERT(env->hp_end == HEAP_LIMIT(env->proc)); ASSERT(env->hp <= HEAP_TOP(env->proc)); @@ -216,10 +217,6 @@ static void cache_env(ErlNifEnv* env) env->hp = HEAP_TOP(env->proc); } else { - ASSERT(env->hp_end != HEAP_LIMIT(env->proc)); - ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); - env->heap_frag = MBUF(env->proc); - ASSERT(env->heap_frag != NULL); env->hp = env->heap_frag->mem + env->heap_frag->used_size; env->hp_end = env->heap_frag->mem + env->heap_frag->alloc_size; } -- cgit v1.2.3 From 16b921ad1c847ba0754adc10d2d45b17973dcd19 Mon Sep 17 00:00:00 2001 From: Alexey Lebedeff Date: Tue, 29 Mar 2016 20:30:22 +0300 Subject: Don't send unasked for systemd notifications Suppose we have some erlang system that uses systemd unit with Type=notify - so this should send startup confirmation itself. But if systemd-enabled epmd will be started as a first step of that system startup, empd startup confirmation will be misinterpeted by systemd. And our erlang service will be considered 'ready' to early. Also this will interefere with systemd MAINPID detection: systemd will be monitoring `epmd` process instead of `beam` one. For example, rabbitmq works around this issue by starting epmd using separate short-lived beam process, with NOTIFY_SOCKET environment variable reset - only in this way we could be sure that epmd will not interfere with rabbit startup sequence. This patch disables indiscriminate confirmation sending, and does it only when it was explicitly asked to do so. --- erts/epmd/src/epmd.c | 6 ++++-- erts/epmd/src/epmd_srv.c | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 5513cb2d7e..4740ce8534 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -592,8 +592,10 @@ void epmd_cleanup_exit(EpmdVars *g, int exitval) free(g->argv); } #ifdef HAVE_SYSTEMD_DAEMON - sd_notifyf(0, "STATUS=Exited.\n" - "ERRNO=%i", exitval); + if (g->is_systemd){ + sd_notifyf(0, "STATUS=Exited.\n" + "ERRNO=%i", exitval); + } #endif /* HAVE_SYSTEMD_DAEMON */ exit(exitval); } diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index e1bac99ef9..59d59ad0f7 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -452,9 +452,11 @@ void run(EpmdVars *g) num_sockets = bound; #ifdef HAVE_SYSTEMD_DAEMON } - sd_notifyf(0, "READY=1\n" - "STATUS=Processing port mapping requests...\n" - "MAINPID=%lu", (unsigned long) getpid()); + if (g->is_systemd) { + sd_notifyf(0, "READY=1\n" + "STATUS=Processing port mapping requests...\n" + "MAINPID=%lu", (unsigned long) getpid()); + } #endif /* HAVE_SYSTEMD_DAEMON */ dbg_tty_printf(g,2,"entering the main select() loop"); -- cgit v1.2.3 From d166fec5d5c901a93e21a1ea7b3165b6fe68d320 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 30 Mar 2016 15:32:22 +0200 Subject: erts: Fix "hanging" VM caused by exiting tty_sl driver Bug introduced on master in a31eab5469b7740d. --- erts/emulator/drivers/unix/ttsl_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index 4f15ce0980..35ccfa589a 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -823,12 +823,13 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) { if (sz == 0) { driver_select(ttysl_port,(ErlDrvEvent)(long)ttysl_fd, ERL_DRV_WRITE,0); - if (ttysl_terminate) + if (ttysl_terminate) { /* flush has been called, which means we should terminate when queue is empty. This will not send any exit message */ DEBUGLOG(("ttysl_to_tty: ttysl_terminate normal\n")); driver_failure_atom(ttysl_port, "normal"); + } break; } } -- cgit v1.2.3 From f0a1185b95bc8d5c20954cbf5f70767f1f354cc8 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 30 Mar 2016 18:25:20 +0200 Subject: Fix scheduling of system tasks --- erts/emulator/beam/erl_process.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 1fb8502ebe..4530317710 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -6684,10 +6684,8 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) erts_aint32_t e; n = e = a; - if (a & ERTS_PSFLG_FREE) { - res = 0; + if (a & ERTS_PSFLG_FREE) goto cleanup; /* We don't want to schedule free processes... */ - } enqueue = ERTS_ENQUEUE_NOT; n |= ERTS_PSFLG_ACTIVE_SYS; -- cgit v1.2.3 From 7d06e5ecd47ef071eb8c4e58ead70e79fa4b02b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 31 Mar 2016 06:55:31 +0200 Subject: Fix unsafe transformation of apply/3 with fixed arguments 62473daf introduced an unsafe optimization in the loader. See the comments in the test case for an explanation of the problem. --- erts/emulator/beam/beam_load.c | 7 ++++++ erts/emulator/beam/ops.tab | 2 +- erts/emulator/test/beam_SUITE.erl | 45 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 50 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index d3d278fb81..16cbdbffea 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -2734,6 +2734,13 @@ same_label(LoaderState* stp, GenOpArg Target, GenOpArg Label) Target.val == Label.val; } +static int +is_killed_apply(LoaderState* stp, GenOpArg Reg, GenOpArg Live) +{ + return Reg.type == TAG_x && Live.type == TAG_u && + Live.val+2 <= Reg.val; +} + static int is_killed(LoaderState* stp, GenOpArg Reg, GenOpArg Live) { diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 9e53b4bfcc..772460c177 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -303,7 +303,7 @@ move_window5 x x x x x y # Swap registers. move R1=x Tmp=x | move R2=xy R1 | move Tmp R2 => swap_temp R1 R2 Tmp -swap_temp R1 R2 Tmp | line Loc | apply Live | is_killed(Tmp, Live) => \ +swap_temp R1 R2 Tmp | line Loc | apply Live | is_killed_apply(Tmp, Live) => \ swap R1 R2 | line Loc | apply Live swap_temp R1 R2 Tmp | line Loc | call Live Addr | is_killed(Tmp, Live) => \ diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl index 07dfeb6633..f61ab431e9 100644 --- a/erts/emulator/test/beam_SUITE.erl +++ b/erts/emulator/test/beam_SUITE.erl @@ -24,9 +24,9 @@ init_per_group/2,end_per_group/2, packed_registers/1, apply_last/1, apply_last_bif/1, buildo_mucho/1, heap_sizes/1, big_lists/1, fconv/1, - select_val/1]). + select_val/1, swap_temp_apply/1]). --export([applied/2]). +-export([applied/2,swap_temp_applied/1]). -include_lib("common_test/include/ct.hrl"). @@ -34,7 +34,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [packed_registers, apply_last, apply_last_bif, - buildo_mucho, heap_sizes, big_lists, select_val]. + buildo_mucho, heap_sizes, big_lists, select_val, + swap_temp_apply]. groups() -> []. @@ -346,3 +347,41 @@ do_select_val(X) -> Int when is_integer(Int) -> integer end. + +swap_temp_apply(_Config) -> + {swap_temp_applied,42} = do_swap_temp_apply(41), + not_an_integer = do_swap_temp_apply(not_an_integer), + ok. + +do_swap_temp_apply(Msg) -> + case swap_temp_apply_function(Msg) of + undefined -> Msg; + Type -> + %% The following sequence: + %% move {x,0} {x,2} + %% move {y,0} {x,0} + %% move {x,2} {y,0} + %% apply 1 + %% + %% Would be incorrectly transformed to: + %% swap {x,0} {y,0} + %% apply 1 + %% + %% ({x,1} is the module, {x,2} the function to be applied). + %% + %% If the instructions are to be transformed, the correct + %% transformation is: + %% + %% swap_temp {x,0} {y,0} {x,2} + %% apply 1 + Fields = ?MODULE:Type(Msg), + {Type,Fields} + end. + +swap_temp_apply_function(Int) when is_integer(Int) -> + swap_temp_applied; +swap_temp_apply_function(_) -> + undefined. + +swap_temp_applied(Int) -> + Int+1. -- cgit v1.2.3 From d97dc2c64be5530b22c8e4f9e11d246285ea8a17 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 31 Mar 2016 16:06:10 +0200 Subject: Fix bad refc management of process struct --- erts/emulator/beam/erl_process.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b4b97d7df1..2b468a9ad9 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -9293,17 +9293,18 @@ Process *schedule(Process *p, int calls) | ERTS_PSFLG_PENDING_EXIT | ERTS_PSFLG_ACTIVE_SYS)) == ERTS_PSFLG_SUSPENDED)) { - if (state & ERTS_PSFLG_FREE) { + if (proxy_p) { + free_proxy_proc(proxy_p); + proxy_p = NULL; + } + else if (state & ERTS_PSFLG_FREE) { + /* free and not queued by proxy */ #ifdef ERTS_SMP erts_smp_proc_dec_refc(p); #else erts_free_proc(p); #endif } - if (proxy_p) { - free_proxy_proc(proxy_p); - proxy_p = NULL; - } goto pick_next_process; } state = new; -- cgit v1.2.3 From 3168f03a80b548094032a33c90a7fd0638493b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 31 Mar 2016 16:42:29 +0200 Subject: Refactor time_t in efile_drv --- erts/emulator/drivers/common/efile_drv.c | 12 ++++++------ erts/emulator/drivers/common/erl_efile.h | 6 +++--- erts/emulator/drivers/unix/unix_efile.c | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 3088dfd572..ee14bd8bba 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -2900,12 +2900,12 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(buf + 9*4) + FILENAME_CHARSIZE); - d->info.mode = get_int32(buf + 0 * 4); - d->info.uid = get_int32(buf + 1 * 4); - d->info.gid = get_int32(buf + 2 * 4); - d->info.accessTime = (time_t)((Sint64)get_int64(buf + 3 * 4)); - d->info.modifyTime = (time_t)((Sint64)get_int64(buf + 5 * 4)); - d->info.cTime = (time_t)((Sint64)get_int64(buf + 7 * 4)); + d->info.mode = get_int32(buf + 0 * 4); + d->info.uid = get_int32(buf + 1 * 4); + d->info.gid = get_int32(buf + 2 * 4); + d->info.accessTime = get_int64(buf + 3 * 4); + d->info.modifyTime = get_int64(buf + 5 * 4); + d->info.cTime = get_int64(buf + 7 * 4); FILENAME_COPY(d->b, buf + 9*4); #ifdef USE_VM_PROBES diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h index be5a891486..7ffeed6b9d 100644 --- a/erts/emulator/drivers/common/erl_efile.h +++ b/erts/emulator/drivers/common/erl_efile.h @@ -105,9 +105,9 @@ typedef struct _Efile_info { Uint32 inode; /* Inode number. */ Uint32 uid; /* User id of owner. */ Uint32 gid; /* Group id of owner. */ - time_t accessTime; /* Last time the file was accessed. */ - time_t modifyTime; /* Last time the file was modified. */ - time_t cTime; /* Creation time (Windows) or last + Sint64 accessTime; /* Last time the file was accessed. */ + Sint64 modifyTime; /* Last time the file was modified. */ + Sint64 cTime; /* Creation time (Windows) or last * inode change (Unix). */ } Efile_info; diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index ac9b681d03..81ed1996df 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -537,9 +537,9 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, else pInfo->type = FT_OTHER; - pInfo->accessTime = statbuf.st_atime; - pInfo->modifyTime = statbuf.st_mtime; - pInfo->cTime = statbuf.st_ctime; + pInfo->accessTime = (Sint64)statbuf.st_atime; + pInfo->modifyTime = (Sint64)statbuf.st_mtime; + pInfo->cTime = (Sint64)statbuf.st_ctime; pInfo->mode = statbuf.st_mode; pInfo->links = statbuf.st_nlink; @@ -578,8 +578,8 @@ efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) } } - tval.actime = pInfo->accessTime; - tval.modtime = pInfo->modifyTime; + tval.actime = (time_t)pInfo->accessTime; + tval.modtime = (time_t)pInfo->modifyTime; return check_error(utime(name, &tval), errInfo); } -- cgit v1.2.3 From f86eabba45b351890ea8a12bd976c02b799c0e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 1 Apr 2016 16:16:46 +0200 Subject: erts: Don't search for non-existing Map keys twice * For maps:get/2,3 and maps:find/2, searching for an immediate key, e.g. an atom, the search was performed twice if the key did not exist in the map. --- erts/emulator/beam/erl_map.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 4b6931f848..6b747256e1 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -196,13 +196,13 @@ erts_maps_get(Eterm key, Eterm map) return &vs[i]; } } - } - - for (i = 0; i < n; i++) { - if (EQ(ks[i], key)) { - return &vs[i]; - } - } + } else { + for (i = 0; i < n; i++) { + if (EQ(ks[i], key)) { + return &vs[i]; + } + } + } return NULL; } ASSERT(is_hashmap(map)); -- cgit v1.2.3 From c048886458ce68280ba32647a93a04902c929988 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 1 Apr 2016 20:11:34 +0200 Subject: erts: Fix race for process_flag(trap_exit,true) and a concurrent exit signal. We now actually guarantee that the process will not die from exit signal *after* the call to process_flag(trap_exit,true) has returned. The race is narrow and probably quite hard to observe even if you manage to provoke it. Has only been confirmed with the help of return trace and a sleep in send_exit_signal(). Solution: Seize status lock to prevent send_exit_signal() from reading an old status (without TRAP_EXIT) and then writing PENDING_EXIT after TRAP_EXIT has been set by process_flag_2(). --- erts/emulator/beam/bif.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index b43137597e..11c694233f 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1566,14 +1566,17 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2) * true. For more info, see implementation of * erts_send_exit_signal(). */ + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCKS_XSIG_SEND); if (trap_exit) state = erts_smp_atomic32_read_bor_mb(&BIF_P->state, ERTS_PSFLG_TRAP_EXIT); else state = erts_smp_atomic32_read_band_mb(&BIF_P->state, ~ERTS_PSFLG_TRAP_EXIT); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCKS_XSIG_SEND); + #ifdef ERTS_SMP - if (ERTS_PROC_PENDING_EXIT(BIF_P)) { + if (state & ERTS_PSFLG_PENDING_EXIT) { erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN); ERTS_BIF_EXITED(BIF_P); } -- cgit v1.2.3 From 7765727317721d5de5949a5f39e0211f3b920da7 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 1 Apr 2016 20:19:12 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ erts/vsn.mk | 2 +- 2 files changed, 66 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index acd816a81c..7ccddf4ff0 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -32,6 +32,71 @@

This document describes the changes made to the ERTS application.

+
Erts 7.3.1 + +
Fixed Bugs and Malfunctions + + +

+ process_info(Pid, last_calls) did not work for + Pid /= self().

+

+ Own Id: OTP-13418

+
+ +

+ Make sure to create a crash dump when running out of + memory. This was accidentally removed in the erts-7.3 + release.

+

+ Own Id: OTP-13419

+
+ +

+ Schedulers could be woken by a premature timeout on + Linux. This premature wakeup was however harmless.

+

+ Own Id: OTP-13420

+
+ +

+ A process communicating with a port via one of the + erlang:port_* BIFs could potentially end up in an + inconsistent state if the port terminated during the + communication. When this occurred the process could later + block in a receive even though it had messages + matching in its message queue.

+

+ This bug was introduced in erts version 5.10 (OTP R16A).

+

+ Own Id: OTP-13424 Aux Id: OTP-10336

+
+ +

+ The reference count of a process structure could under + rare circumstances be erroneously managed. When this + happened invalid memory accesses occurred.

+

+ Own Id: OTP-13446

+
+ +

+ Fix race between process_flag(trap_exit,true) and + a received exit signal.

+

+ A process could terminate due to exit signal even though + process_flag(trap_exit,true) had returned. A very + specific timing between call to process_flag/2 and + exit signal from another scheduler was required for this + to happen.

+

+ Own Id: OTP-13452

+
+
+
+ +
+
Erts 7.3
Fixed Bugs and Malfunctions diff --git a/erts/vsn.mk b/erts/vsn.mk index a42b7d758e..89c3ab8edb 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 7.3 +VSN = 7.3.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 -- cgit v1.2.3 From 5ae46e823a8a52ed4e5b960ff62975894b1a8302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 29 Mar 2016 09:10:13 +0200 Subject: Handle multi-giga byte writes to files Test cases that write 4Gb to a file at once would fail on OS X and FreeBSD. By running a simple test program on OS X (El Capitan 10.11.4/Darwin 15.4.0), I found that writev() can handle more than 4Gb of data, while write() only can handle less than 2Gb. (Note that efile_drv.c will use write() if there is only one element in the io vector, and writev() if there is more than one.) It is tempting to attempt to piggy-back on the existing mechanism for segmenting write operations in efile_drv.c, but because of the complex code I find it too dangerous, both from a correctness and performance perspective. Instead do the change in unix_efile.c, which is considerably simpler. --- erts/emulator/drivers/unix/unix_efile.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index ac9b681d03..c097bc1950 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -638,12 +638,21 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */ do { w = writev(fd, &iov[cnt], b); } while (w < 0 && errno == EINTR); + if (w < 0 && errno == EINVAL) { + goto single_write; + } } else + single_write: /* Degenerated io vector - use regular write */ #endif { do { - w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len); + size_t iov_len = iov[cnt].iov_len; + size_t limit = 1024*1024*1024; /* 1GB */ + if (iov_len > limit) { + iov_len = limit; + } + w = write(fd, iov[cnt].iov_base, iov_len); } while (w < 0 && errno == EINTR); ASSERT(w <= iov[cnt].iov_len || (w == -1 && errno != EINTR)); -- cgit v1.2.3 From 6b17c34656457bf0da708f5c719adabbbd0a00cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 5 Apr 2016 16:13:05 +0200 Subject: Remove ?line macros --- erts/test/erl_print_SUITE.erl | 458 +++++++++++++++---------------- erts/test/erlc_SUITE.erl | 318 +++++++++++---------- erts/test/erlexec_SUITE.erl | 170 ++++++------ erts/test/ethread_SUITE.erl | 112 ++++---- erts/test/install_SUITE.erl | 247 +++++++++-------- erts/test/nt_SUITE.erl | 621 +++++++++++++++++++++--------------------- erts/test/otp_SUITE.erl | 502 +++++++++++++++++----------------- erts/test/run_erl_SUITE.erl | 111 ++++---- 8 files changed, 1259 insertions(+), 1280 deletions(-) (limited to 'erts') diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl index f0fee49024..ceec4f4259 100644 --- a/erts/test/erl_print_SUITE.erl +++ b/erts/test/erl_print_SUITE.erl @@ -33,12 +33,12 @@ -define(DEFAULT_TIMEOUT, ?t:minutes(10)). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2, end_per_testcase/2]). --export([erlang_display/1, integer/1, float/1, - string/1, character/1, snprintf/1, quote/1]). +-export([erlang_display/1, integer/1, float/1, + string/1, character/1, snprintf/1, quote/1]). -include_lib("common_test/include/ct.hrl"). @@ -76,105 +76,105 @@ test_cases() -> erlang_display(doc) -> []; erlang_display(suite) -> []; erlang_display(Config) when is_list(Config) -> - ?line put(erlang_display_test, ok), + put(erlang_display_test, ok), OAIS = erts_debug:set_internal_state(available_internal_state, true), %% atoms - ?line chk_display(atom, "atom"), - ?line chk_display(true, "true"), - ?line chk_display(false, "false"), - ?line chk_display('DOWN', "'DOWN'"), - ?line chk_display('EXIT', "'EXIT'"), - ?line chk_display('asdDofw $@{}][', "'asdDofw $@{}]['"), + chk_display(atom, "atom"), + chk_display(true, "true"), + chk_display(false, "false"), + chk_display('DOWN', "'DOWN'"), + chk_display('EXIT', "'EXIT'"), + chk_display('asdDofw $@{}][', "'asdDofw $@{}]['"), %% integers - ?line chk_display(0, "0"), - ?line chk_display(1, "1"), - ?line chk_display(4711, "4711"), - ?line chk_display(((1 bsl 27) - 1), "134217727"), - ?line chk_display((1 bsl 27), "134217728"), - ?line chk_display((1 bsl 32), "4294967296"), - ?line chk_display(11111111111, "11111111111"), - ?line chk_display((1 bsl 59) - 1, "576460752303423487"), - ?line chk_display(1 bsl 59, "576460752303423488"), - ?line chk_display(111111111111111111111, "111111111111111111111"), - ?line chk_display(123456789012345678901234567890, - "123456789012345678901234567890"), - ?line chk_display(1 bsl 10000, str_1_bsl_10000()), - ?line chk_display(-1, "-1"), - ?line chk_display(-4711, "-4711"), - ?line chk_display(-(1 bsl 27), "-134217728"), - ?line chk_display(-((1 bsl 27) + 1), "-134217729"), - ?line chk_display(-(1 bsl 32), "-4294967296"), - ?line chk_display(-11111111111, "-11111111111"), - ?line chk_display(-(1 bsl 59), "-576460752303423488"), - ?line chk_display(-((1 bsl 59) + 1), "-576460752303423489"), - ?line chk_display(-111111111111111111111, "-111111111111111111111"), - ?line chk_display(-123456789012345678901234567890, - "-123456789012345678901234567890"), - ?line chk_display(-(1 bsl 10000), [$- | str_1_bsl_10000()]), - - ?line MyCre = my_cre(), + chk_display(0, "0"), + chk_display(1, "1"), + chk_display(4711, "4711"), + chk_display(((1 bsl 27) - 1), "134217727"), + chk_display((1 bsl 27), "134217728"), + chk_display((1 bsl 32), "4294967296"), + chk_display(11111111111, "11111111111"), + chk_display((1 bsl 59) - 1, "576460752303423487"), + chk_display(1 bsl 59, "576460752303423488"), + chk_display(111111111111111111111, "111111111111111111111"), + chk_display(123456789012345678901234567890, + "123456789012345678901234567890"), + chk_display(1 bsl 10000, str_1_bsl_10000()), + chk_display(-1, "-1"), + chk_display(-4711, "-4711"), + chk_display(-(1 bsl 27), "-134217728"), + chk_display(-((1 bsl 27) + 1), "-134217729"), + chk_display(-(1 bsl 32), "-4294967296"), + chk_display(-11111111111, "-11111111111"), + chk_display(-(1 bsl 59), "-576460752303423488"), + chk_display(-((1 bsl 59) + 1), "-576460752303423489"), + chk_display(-111111111111111111111, "-111111111111111111111"), + chk_display(-123456789012345678901234567890, + "-123456789012345678901234567890"), + chk_display(-(1 bsl 10000), [$- | str_1_bsl_10000()]), + + MyCre = my_cre(), %% pids - ?line chk_display(mk_pid_xstr({node(), MyCre}, 4711, 42)), - ?line chk_display(mk_pid_xstr({node(), oth_cre(MyCre)}, 4711, 42)), - ?line chk_display(mk_pid_xstr({node(), oth_cre(oth_cre(MyCre))}, 4711, 42)), + chk_display(mk_pid_xstr({node(), MyCre}, 4711, 42)), + chk_display(mk_pid_xstr({node(), oth_cre(MyCre)}, 4711, 42)), + chk_display(mk_pid_xstr({node(), oth_cre(oth_cre(MyCre))}, 4711, 42)), - ?line chk_display(mk_pid_xstr({a@b, MyCre}, 4711, 42)), - ?line chk_display(mk_pid_xstr({a@b, oth_cre(MyCre)}, 4711, 42)), - ?line chk_display(mk_pid_xstr({a@b, oth_cre(oth_cre(MyCre))}, 4711, 42)), + chk_display(mk_pid_xstr({a@b, MyCre}, 4711, 42)), + chk_display(mk_pid_xstr({a@b, oth_cre(MyCre)}, 4711, 42)), + chk_display(mk_pid_xstr({a@b, oth_cre(oth_cre(MyCre))}, 4711, 42)), %% ports - ?line chk_display(mk_port_xstr({node(), MyCre}, 4711)), - ?line chk_display(mk_port_xstr({node(), oth_cre(MyCre)}, 4711)), - ?line chk_display(mk_port_xstr({node(), oth_cre(oth_cre(MyCre))}, 4711)), + chk_display(mk_port_xstr({node(), MyCre}, 4711)), + chk_display(mk_port_xstr({node(), oth_cre(MyCre)}, 4711)), + chk_display(mk_port_xstr({node(), oth_cre(oth_cre(MyCre))}, 4711)), - ?line chk_display(mk_port_xstr({c@d, MyCre}, 4711)), - ?line chk_display(mk_port_xstr({c@d, oth_cre(MyCre)}, 4711)), - ?line chk_display(mk_port_xstr({c@d, oth_cre(oth_cre(MyCre))}, 4711)), + chk_display(mk_port_xstr({c@d, MyCre}, 4711)), + chk_display(mk_port_xstr({c@d, oth_cre(MyCre)}, 4711)), + chk_display(mk_port_xstr({c@d, oth_cre(oth_cre(MyCre))}, 4711)), %% refs - ?line chk_display(mk_ref_xstr({node(), MyCre}, [1,2,3])), - ?line chk_display(mk_ref_xstr({node(), oth_cre(MyCre)}, [1,2,3])), - ?line chk_display(mk_ref_xstr({node(), oth_cre(oth_cre(MyCre))}, [1,2,3])), + chk_display(mk_ref_xstr({node(), MyCre}, [1,2,3])), + chk_display(mk_ref_xstr({node(), oth_cre(MyCre)}, [1,2,3])), + chk_display(mk_ref_xstr({node(), oth_cre(oth_cre(MyCre))}, [1,2,3])), - ?line chk_display(mk_ref_xstr({e@f, MyCre},[1,2,3] )), - ?line chk_display(mk_ref_xstr({e@f, oth_cre(MyCre)}, [1,2,3])), - ?line chk_display(mk_ref_xstr({e@f, oth_cre(oth_cre(MyCre))}, [1,2,3])), + chk_display(mk_ref_xstr({e@f, MyCre},[1,2,3] )), + chk_display(mk_ref_xstr({e@f, oth_cre(MyCre)}, [1,2,3])), + chk_display(mk_ref_xstr({e@f, oth_cre(oth_cre(MyCre))}, [1,2,3])), %% Compund terms - ?line {Pid, PidStr} = mk_pid_xstr({x@y, oth_cre(MyCre)}, 4712, 41), - ?line {Port, PortStr} = mk_port_xstr({x@y, oth_cre(MyCre)}, 4712), - ?line {Ref, RefStr} = mk_ref_xstr({e@f, oth_cre(MyCre)}, [11,12,13]), - - ?line chk_display({atom,-4711,Ref,{"hej",[Pid,222222222222222222222222,Port,4711]}}, - "{atom,-4711,"++RefStr++",{\"hej\",["++PidStr++",222222222222222222222222,"++PortStr++",4711]}}"), - ?line chk_display({{{{{{{{{{{{{{{{{{{{{{{hi}}}}}}}}}}}}}}}}}}}}}}}, - "{{{{{{{{{{{{{{{{{{{{{{{hi}}}}}}}}}}}}}}}}}}}}}}}"), - ?line chk_display([[[[[[[[[[[[[[[[[[[[[[[yo]]]]]]]]]]]]]]]]]]]]]]], - "[[[[[[[[[[[[[[[[[[[[[[[yo]]]]]]]]]]]]]]]]]]]]]]]"), - ?line chk_display({[{[{[{[{[{[{[{[{[{[{[{[ii]}]}]}]}]}]}]}]}]}]}]}]}, - "{[{[{[{[{[{[{[{[{[{[{[{[ii]}]}]}]}]}]}]}]}]}]}]}]}"), - ?line chk_display([], "[]"), % Not really a compound term :) - ?line chk_display([a|b], "[a|b]"), - ?line chk_display([a,b,c|z], "[a,b,c|z]"), - ?line chk_display([a,b,c], "[a,b,c]"), - ?line chk_display([Pid,Port,Ref], - "["++PidStr++","++PortStr++","++RefStr++"]"), - ?line chk_display("abcdefghijklmnopqrstuvwxyz", - "\"abcdefghijklmnopqrstuvwxyz\""), - ?line chk_display("ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""), - ?line chk_display("H E J", "\"H E J\""), - ?line chk_display("asdDofw $@{}][", "\"asdDofw $@{}][\""), - + {Pid, PidStr} = mk_pid_xstr({x@y, oth_cre(MyCre)}, 4712, 41), + {Port, PortStr} = mk_port_xstr({x@y, oth_cre(MyCre)}, 4712), + {Ref, RefStr} = mk_ref_xstr({e@f, oth_cre(MyCre)}, [11,12,13]), + + chk_display({atom,-4711,Ref,{"hej",[Pid,222222222222222222222222,Port,4711]}}, + "{atom,-4711,"++RefStr++",{\"hej\",["++PidStr++",222222222222222222222222,"++PortStr++",4711]}}"), + chk_display({{{{{{{{{{{{{{{{{{{{{{{hi}}}}}}}}}}}}}}}}}}}}}}}, + "{{{{{{{{{{{{{{{{{{{{{{{hi}}}}}}}}}}}}}}}}}}}}}}}"), + chk_display([[[[[[[[[[[[[[[[[[[[[[[yo]]]]]]]]]]]]]]]]]]]]]]], + "[[[[[[[[[[[[[[[[[[[[[[[yo]]]]]]]]]]]]]]]]]]]]]]]"), + chk_display({[{[{[{[{[{[{[{[{[{[{[{[ii]}]}]}]}]}]}]}]}]}]}]}]}, + "{[{[{[{[{[{[{[{[{[{[{[{[ii]}]}]}]}]}]}]}]}]}]}]}]}"), + chk_display([], "[]"), % Not really a compound term :) + chk_display([a|b], "[a|b]"), + chk_display([a,b,c|z], "[a,b,c|z]"), + chk_display([a,b,c], "[a,b,c]"), + chk_display([Pid,Port,Ref], + "["++PidStr++","++PortStr++","++RefStr++"]"), + chk_display("abcdefghijklmnopqrstuvwxyz", + "\"abcdefghijklmnopqrstuvwxyz\""), + chk_display("ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""), + chk_display("H E J", "\"H E J\""), + chk_display("asdDofw $@{}][", "\"asdDofw $@{}][\""), + %% %% TODO: Check binaries, fun and floats... %% erts_debug:set_internal_state(available_internal_state, OAIS), - ?line ok = get(erlang_display_test). + ok = get(erlang_display_test). get_chnl_no(NodeName) when is_atom(NodeName) -> erts_debug:get_internal_state({channel_number, NodeName}). @@ -182,20 +182,20 @@ get_chnl_no(NodeName) when is_atom(NodeName) -> chk_display(Term, Expect) when is_list(Expect) -> Dstr = erts_debug:display(Term), case Expect ++ io_lib:nl() of - Dstr -> - ?t:format("Test of \"~p\" succeeded.~n" - " Expected and got: ~s~n", - [Term, io_lib:write_string(Dstr)]); - DoExpect -> - ?t:format("***~n" - "*** Test of \"~p\" failed!~n" - "*** Expected: ~s~n" - "*** Got: ~s~n" - "***~n", - [Term, - io_lib:write_string(DoExpect), - io_lib:write_string(Dstr)]), - put(erlang_display_test, failed) + Dstr -> + ?t:format("Test of \"~p\" succeeded.~n" + " Expected and got: ~s~n", + [Term, io_lib:write_string(Dstr)]); + DoExpect -> + ?t:format("***~n" + "*** Test of \"~p\" failed!~n" + "*** Expected: ~s~n" + "*** Got: ~s~n" + "***~n", + [Term, + io_lib:write_string(DoExpect), + io_lib:write_string(Dstr)]), + put(erlang_display_test, failed) end. chk_display({Term, Expect}) -> @@ -204,20 +204,20 @@ chk_display({Term, Expect}) -> mk_pid_xstr({NodeName, Creation}, Number, Serial) -> Pid = mk_pid({NodeName, Creation}, Number, Serial), XStr = "<" ++ integer_to_list(get_chnl_no(NodeName)) - ++ "." ++ integer_to_list(Number) - ++ "." ++ integer_to_list(Serial) ++ ">", + ++ "." ++ integer_to_list(Number) + ++ "." ++ integer_to_list(Serial) ++ ">", {Pid, XStr}. mk_port_xstr({NodeName, Creation}, Number) -> Port = mk_port({NodeName, Creation}, Number), XStr = "#Port<" ++ integer_to_list(get_chnl_no(NodeName)) - ++ "." ++ integer_to_list(Number) ++ ">", + ++ "." ++ integer_to_list(Number) ++ ">", {Port, XStr}. mk_ref_xstr({NodeName, Creation}, Numbers) -> Ref = mk_ref({NodeName, Creation}, Numbers), XStr = "#Ref<" ++ integer_to_list(get_chnl_no(NodeName)) - ++ ref_numbers_xstr(Numbers) ++ ">", + ++ ref_numbers_xstr(Numbers) ++ ">", {Ref, XStr}. ref_numbers_xstr([]) -> @@ -242,7 +242,7 @@ ref_numbers_xstr([N | Ns]) -> default_testcase_impl(doc) -> []; default_testcase_impl(suite) -> []; -default_testcase_impl(Config) when is_list(Config) -> ?line run_case(Config). +default_testcase_impl(Config) when is_list(Config) -> run_case(Config). init_per_testcase(Case, Config) -> Dog = ?t:timetrap(?DEFAULT_TIMEOUT), @@ -260,62 +260,62 @@ end_per_testcase(_Case, Config) -> -define(PID_MARKER, $E,$P,$-,$T,$E,$S,$T,$-,$P,$I,$D). port_prog_killer(EProc, OSProc) when is_pid(EProc), is_list(OSProc) -> - ?line process_flag(trap_exit, true), - ?line Ref = erlang:monitor(process, EProc), - ?line receive - {'DOWN', Ref, _, _, Reason} when is_tuple(Reason), - element(1, Reason) - == timetrap_timeout -> - ?line Cmd = "kill -9 " ++ OSProc, - ?line ?t:format("Test case timed out. " - "Trying to kill port program.~n" - " Executing: ~p~n", [Cmd]), - ?line case os:cmd(Cmd) of - [] -> - ok; - OsCmdRes -> - ?line ?t:format(" ~s", [OsCmdRes]) - end; - {'DOWN', Ref, _, _, _} -> - %% OSProc is assumed to have terminated by itself - ?line ok - end. + process_flag(trap_exit, true), + Ref = erlang:monitor(process, EProc), + receive + {'DOWN', Ref, _, _, Reason} when is_tuple(Reason), + element(1, Reason) + == timetrap_timeout -> + Cmd = "kill -9 " ++ OSProc, + ?t:format("Test case timed out. " + "Trying to kill port program.~n" + " Executing: ~p~n", [Cmd]), + case os:cmd(Cmd) of + [] -> + ok; + OsCmdRes -> + ?t:format(" ~s", [OsCmdRes]) + end; + {'DOWN', Ref, _, _, _} -> + %% OSProc is assumed to have terminated by itself + ok + end. get_line(_Port, eol, Data) -> - ?line Data; + Data; get_line(Port, noeol, Data) -> - ?line receive - {Port, {data, {Flag, NextData}}} -> - ?line get_line(Port, Flag, Data ++ NextData); - {Port, eof} -> - ?line ?t:fail(port_prog_unexpectedly_closed) - end. + receive + {Port, {data, {Flag, NextData}}} -> + get_line(Port, Flag, Data ++ NextData); + {Port, eof} -> + ?t:fail(port_prog_unexpectedly_closed) + end. read_case_data(Port, TestCase) -> - ?line receive - {Port, {data, {eol, [?SUCCESS_MARKER]}}} -> - ?line ok; - {Port, {data, {Flag, [?SUCCESS_MARKER | CommentStart]}}} -> - ?line {comment, get_line(Port, Flag, CommentStart)}; - {Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} -> - ?line {skipped, get_line(Port, Flag, CommentStart)}; - {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} -> - ?line ?t:fail(get_line(Port, Flag, ReasonStart)); - {Port, {data, {eol, [?PID_MARKER | PidStr]}}} -> - ?line ?t:format("Port program pid: ~s~n", [PidStr]), - ?line CaseProc = self(), - ?line _ = list_to_integer(PidStr), % Sanity check - spawn_opt(fun () -> - port_prog_killer(CaseProc, PidStr) - end, - [{priority, max}, link]), - read_case_data(Port, TestCase); - {Port, {data, {Flag, LineStart}}} -> - ?line ?t:format("~s~n", [get_line(Port, Flag, LineStart)]), - read_case_data(Port, TestCase); - {Port, eof} -> - ?line ?t:fail(port_prog_unexpectedly_closed) - end. + receive + {Port, {data, {eol, [?SUCCESS_MARKER]}}} -> + ok; + {Port, {data, {Flag, [?SUCCESS_MARKER | CommentStart]}}} -> + {comment, get_line(Port, Flag, CommentStart)}; + {Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} -> + {skipped, get_line(Port, Flag, CommentStart)}; + {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} -> + ?t:fail(get_line(Port, Flag, ReasonStart)); + {Port, {data, {eol, [?PID_MARKER | PidStr]}}} -> + ?t:format("Port program pid: ~s~n", [PidStr]), + CaseProc = self(), + _ = list_to_integer(PidStr), % Sanity check + spawn_opt(fun () -> + port_prog_killer(CaseProc, PidStr) + end, + [{priority, max}, link]), + read_case_data(Port, TestCase); + {Port, {data, {Flag, LineStart}}} -> + ?t:format("~s~n", [get_line(Port, Flag, LineStart)]), + read_case_data(Port, TestCase); + {Port, eof} -> + ?t:fail(port_prog_unexpectedly_closed) + end. run_case(Config) -> run_case(Config, ""). @@ -326,25 +326,25 @@ run_case(Config, TestArgs) -> run_case(Config, TestArgs, Fun) -> Test = atom_to_list(?config(testcase, Config)), TestProg = filename:join([?config(data_dir, Config), - ?TESTPROG - ++ "." - ++ atom_to_list(erlang:system_info(threads))]), + ?TESTPROG + ++ "." + ++ atom_to_list(erlang:system_info(threads))]), Cmd = TestProg ++ " " ++ Test ++ " " ++ TestArgs, case catch open_port({spawn, Cmd}, [stream, - use_stdio, - stderr_to_stdout, - eof, - {line, 1024}]) of - Port when is_port(Port) -> - ?line Fun(Port), - ?line CaseResult = read_case_data(Port, Test), - ?line receive - {Port, eof} -> - ?line ok - end, - ?line CaseResult; - Error -> - ?line ?t:fail({open_port_failed, Error}) + use_stdio, + stderr_to_stdout, + eof, + {line, 1024}]) of + Port when is_port(Port) -> + Fun(Port), + CaseResult = read_case_data(Port, Test), + receive + {Port, eof} -> + ok + end, + CaseResult; + Error -> + ?t:fail({open_port_failed, Error}) end. @@ -382,80 +382,80 @@ mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) -> mk_pid({atom_to_list(NodeName), Creation}, Number, Serial); mk_pid({NodeName, Creation}, Number, Serial) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PID_EXT, - ?ATOM_EXT, - uint16_be(length(NodeName)), - NodeName, - uint32_be(Number), - uint32_be(Serial), - uint8(Creation)])) of - Pid when is_pid(Pid) -> - Pid; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?PID_EXT, + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + uint32_be(Serial), + uint8(Creation)])) of + Pid when is_pid(Pid) -> + Pid; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. mk_port({NodeName, Creation}, Number) when is_atom(NodeName) -> mk_port({atom_to_list(NodeName), Creation}, Number); mk_port({NodeName, Creation}, Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PORT_EXT, - ?ATOM_EXT, - uint16_be(length(NodeName)), - NodeName, - uint32_be(Number), - uint8(Creation)])) of - Port when is_port(Port) -> - Port; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_port, [{NodeName, Creation}, Number]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?PORT_EXT, + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + uint8(Creation)])) of + Port when is_port(Port) -> + Port; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_port, [{NodeName, Creation}, Number]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName), - is_integer(Creation), - is_list(Numbers) -> + is_integer(Creation), + is_list(Numbers) -> mk_ref({atom_to_list(NodeName), Creation}, Numbers); mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName), - is_integer(Creation), - is_integer(Number) -> + is_integer(Creation), + is_integer(Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?REFERENCE_EXT, - ?ATOM_EXT, - uint16_be(length(NodeName)), - NodeName, - uint32_be(Number), - uint8(Creation)])) of - Ref when is_reference(Ref) -> - Ref; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?REFERENCE_EXT, + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + uint8(Creation)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end; mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName), - is_integer(Creation), - is_list(Numbers) -> + is_integer(Creation), + is_list(Numbers) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?NEW_REFERENCE_EXT, - uint16_be(length(Numbers)), - ?ATOM_EXT, - uint16_be(length(NodeName)), - NodeName, - uint8(Creation), - lists:map(fun (N) -> - uint32_be(N) - end, - Numbers)])) of - Ref when is_reference(Ref) -> - Ref; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?NEW_REFERENCE_EXT, + uint16_be(length(Numbers)), + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint8(Creation), + lists:map(fun (N) -> + uint32_be(N) + end, + Numbers)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. my_cre() -> erlang:system_info(creation). diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index 7e44be1fe0..19f78255c8 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -22,10 +22,10 @@ %% Tests the erlc command by compiling various types of files. -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, compile_erl/1, - compile_yecc/1, compile_script/1, - compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1, - make_dep_options/1]). + init_per_group/2,end_per_group/2, compile_erl/1, + compile_yecc/1, compile_script/1, + compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1, + make_dep_options/1]). -include_lib("common_test/include/ct.hrl"). @@ -57,113 +57,109 @@ end_per_group(_GroupName, Config) -> %% Tests that compiling Erlang source code works. compile_erl(Config) when is_list(Config) -> - ?line {SrcDir, OutDir, Cmd} = get_cmd(Config), - ?line FileName = filename:join(SrcDir, "erl_test_ok.erl"), + {SrcDir, OutDir, Cmd} = get_cmd(Config), + FileName = filename:join(SrcDir, "erl_test_ok.erl"), %% By default, warnings are now turned on. - ?line run(Config, Cmd, FileName, "", - ["Warning: function foo/0 is unused\$", - "_OK_"]), + run(Config, Cmd, FileName, "", + ["Warning: function foo/0 is unused\$", "_OK_"]), %% Test that the compiled file is where it should be, %% and that it is runnable. - ?line {module, erl_test_ok} = code:load_abs(filename:join(OutDir, - "erl_test_ok")), - ?line 42 = erl_test_ok:shoe_size(#person{shoe_size=42}), - ?line code:purge(erl_test_ok), + {module, erl_test_ok} = code:load_abs(filename:join(OutDir, "erl_test_ok")), + 42 = erl_test_ok:shoe_size(#person{shoe_size=42}), + code:purge(erl_test_ok), %% Try disabling warnings. - ?line run(Config, Cmd, FileName, "-W0", ["_OK_"]), + run(Config, Cmd, FileName, "-W0", ["_OK_"]), %% Try treating warnings as errors. - ?line run(Config, Cmd, FileName, "-Werror", - ["compile: warnings being treated as errors\$", - "function foo/0 is unused\$", - "_ERROR_"]), + run(Config, Cmd, FileName, "-Werror", + ["compile: warnings being treated as errors\$", + "function foo/0 is unused\$", "_ERROR_"]), %% Check a bad file. - ?line BadFile = filename:join(SrcDir, "erl_test_bad.erl"), - ?line run(Config, Cmd, BadFile, "", ["function non_existing/1 undefined\$", - "_ERROR_"]), + BadFile = filename:join(SrcDir, "erl_test_bad.erl"), + run(Config, Cmd, BadFile, "", ["function non_existing/1 undefined\$", + "_ERROR_"]), ok. %% Test that compiling yecc source code works. compile_yecc(Config) when is_list(Config) -> - ?line {SrcDir, _, OutDir} = get_dirs(Config), - ?line Cmd = erlc() ++ " -o" ++ OutDir ++ " ", - ?line FileName = filename:join(SrcDir, "yecc_test_ok.yrl"), - ?line run(Config, Cmd, FileName, "-W0", ["_OK_"]), - ?line true = exists(filename:join(OutDir, "yecc_test_ok.erl")), - - ?line BadFile = filename:join(SrcDir, "yecc_test_bad.yrl"), - ?line run(Config, Cmd, BadFile, "-W0", - ["rootsymbol form is not a nonterminal\$", - "undefined nonterminal: form\$", - "Nonterminals is missing\$", - "_ERROR_"]), - ?line exists(filename:join(OutDir, "yecc_test_ok.erl")), - + {SrcDir, _, OutDir} = get_dirs(Config), + Cmd = erlc() ++ " -o" ++ OutDir ++ " ", + FileName = filename:join(SrcDir, "yecc_test_ok.yrl"), + run(Config, Cmd, FileName, "-W0", ["_OK_"]), + true = exists(filename:join(OutDir, "yecc_test_ok.erl")), + + BadFile = filename:join(SrcDir, "yecc_test_bad.yrl"), + run(Config, Cmd, BadFile, "-W0", + ["rootsymbol form is not a nonterminal\$", + "undefined nonterminal: form\$", + "Nonterminals is missing\$", + "_ERROR_"]), + exists(filename:join(OutDir, "yecc_test_ok.erl")), ok. %% Test that compiling start scripts works. compile_script(Config) when is_list(Config) -> - ?line {SrcDir, OutDir, Cmd} = get_cmd(Config), - ?line FileName = filename:join(SrcDir, "start_ok.script"), - ?line run(Config, Cmd, FileName, "", ["_OK_"]), - ?line true = exists(filename:join(OutDir, "start_ok.boot")), + {SrcDir, OutDir, Cmd} = get_cmd(Config), + FileName = filename:join(SrcDir, "start_ok.script"), + run(Config, Cmd, FileName, "", ["_OK_"]), + true = exists(filename:join(OutDir, "start_ok.boot")), - ?line BadFile = filename:join(SrcDir, "start_bad.script"), - ?line run(Config, Cmd, BadFile, "", ["syntax error before:", "_ERROR_"]), + BadFile = filename:join(SrcDir, "start_bad.script"), + run(Config, Cmd, BadFile, "", ["syntax error before:", "_ERROR_"]), ok. %% Test that compiling SNMP mibs works. compile_mib(Config) when is_list(Config) -> - ?line {SrcDir, OutDir, Cmd} = get_cmd(Config), - ?line FileName = filename:join(SrcDir, "GOOD-MIB.mib"), - ?line run(Config, Cmd, FileName, "", ["_OK_"]), - ?line Output = filename:join(OutDir, "GOOD-MIB.bin"), - ?line true = exists(Output), + {SrcDir, OutDir, Cmd} = get_cmd(Config), + FileName = filename:join(SrcDir, "GOOD-MIB.mib"), + run(Config, Cmd, FileName, "", ["_OK_"]), + Output = filename:join(OutDir, "GOOD-MIB.bin"), + true = exists(Output), %% Try -W option. - ?line ok = file:delete(Output), - ?line run(Config, Cmd, FileName, "-W", - ["_OK_"]), - ?line true = exists(Output), + ok = file:delete(Output), + run(Config, Cmd, FileName, "-W", + ["_OK_"]), + true = exists(Output), %% Try -W option and more verbose. - ?line ok = file:delete(Output), - ?line case test_server:os_type() of - {unix,_} -> - ?line run(Config, Cmd, FileName, "-W +'{verbosity,info}'", - ["\\[GOOD-MIB[.]mib\\]\\[INF\\]: No accessfunction for 'sysDescr' => using default", - "_OK_"]), - ?line true = exists(Output), - ?line ok = file:delete(Output); - _ -> ok %Don't bother -- too much work. - end, + ok = file:delete(Output), + case test_server:os_type() of + {unix,_} -> + run(Config, Cmd, FileName, "-W +'{verbosity,info}'", + ["\\[GOOD-MIB[.]mib\\]\\[INF\\]: No accessfunction for 'sysDescr' => using default", + "_OK_"]), + true = exists(Output), + ok = file:delete(Output); + _ -> ok %Don't bother -- too much work. + end, %% Try a bad file. - ?line BadFile = filename:join(SrcDir, "BAD-MIB.mib"), - ?line run(Config, Cmd, BadFile, "", - ["BAD-MIB.mib: 1: syntax error before: mibs\$", - "compilation_failed_ERROR_"]), + BadFile = filename:join(SrcDir, "BAD-MIB.mib"), + run(Config, Cmd, BadFile, "", + ["BAD-MIB.mib: 1: syntax error before: mibs\$", + "compilation_failed_ERROR_"]), %% Make sure that no -I option works. - ?line NewCmd = erlc() ++ " -o" ++ OutDir ++ " ", - ?line run(Config, NewCmd, FileName, "", ["_OK_"]), - ?line true = exists(Output), + NewCmd = erlc() ++ " -o" ++ OutDir ++ " ", + run(Config, NewCmd, FileName, "", ["_OK_"]), + true = exists(Output), ok. @@ -171,91 +167,91 @@ compile_mib(Config) when is_list(Config) -> %% shell script with redirected input). good_citizen(Config) when is_list(Config) -> case os:type() of - {unix, _} -> - ?line PrivDir = ?config(priv_dir, Config), - ?line Answer = filename:join(PrivDir, "answer"), - ?line Script = filename:join(PrivDir, "test_script"), - ?line Test = filename:join(PrivDir, "test.erl"), - ?line S = ["#! /bin/sh\n", "erlc ", Test, "\n", - "read reply\n", "echo $reply\n"], - ?line ok = file:write_file(Script, S), - ?line ok = file:write_file(Test, "-module(test).\n"), - ?line Cmd = "echo y | sh " ++ Script ++ " > " ++ Answer, - ?line os:cmd(Cmd), - ?line {ok, Answer0} = file:read_file(Answer), - ?line [$y|_] = binary_to_list(Answer0), - ok; - _ -> - {skip, "Unix specific"} + {unix, _} -> + PrivDir = ?config(priv_dir, Config), + Answer = filename:join(PrivDir, "answer"), + Script = filename:join(PrivDir, "test_script"), + Test = filename:join(PrivDir, "test.erl"), + S = ["#! /bin/sh\n", "erlc ", Test, "\n", + "read reply\n", "echo $reply\n"], + ok = file:write_file(Script, S), + ok = file:write_file(Test, "-module(test).\n"), + Cmd = "echo y | sh " ++ Script ++ " > " ++ Answer, + os:cmd(Cmd), + {ok, Answer0} = file:read_file(Answer), + [$y|_] = binary_to_list(Answer0), + ok; + _ -> + {skip, "Unix specific"} end. %% Make sure that compiling an Erlang module deep down in %% in a directory with more than 255 characters works. deep_cwd(Config) when is_list(Config) -> case os:type() of - {unix, _} -> - PrivDir = ?config(priv_dir, Config), - deep_cwd_1(PrivDir); - _ -> - {skip, "Only a problem on Unix"} + {unix, _} -> + PrivDir = ?config(priv_dir, Config), + deep_cwd_1(PrivDir); + _ -> + {skip, "Only a problem on Unix"} end. deep_cwd_1(PrivDir) -> - ?line DeepDir0 = filename:join(PrivDir, lists:duplicate(128, $a)), - ?line DeepDir = filename:join(DeepDir0, lists:duplicate(128, $b)), - ?line ok = file:make_dir(DeepDir0), - ?line ok = file:make_dir(DeepDir), - ?line ok = file:set_cwd(DeepDir), - ?line ok = file:write_file("test.erl", "-module(test).\n\n"), - ?line io:format("~s\n", [os:cmd("erlc test.erl")]), - ?line true = filelib:is_file("test.beam"), + DeepDir0 = filename:join(PrivDir, lists:duplicate(128, $a)), + DeepDir = filename:join(DeepDir0, lists:duplicate(128, $b)), + ok = file:make_dir(DeepDir0), + ok = file:make_dir(DeepDir), + ok = file:set_cwd(DeepDir), + ok = file:write_file("test.erl", "-module(test).\n\n"), + io:format("~s\n", [os:cmd("erlc test.erl")]), + true = filelib:is_file("test.beam"), ok. %% Test that a large number of command line switches does not %% overflow the argument buffer arg_overflow(Config) when is_list(Config) -> - ?line {SrcDir, _OutDir, Cmd} = get_cmd(Config), - ?line FileName = filename:join(SrcDir, "erl_test_ok.erl"), + {SrcDir, _OutDir, Cmd} = get_cmd(Config), + FileName = filename:join(SrcDir, "erl_test_ok.erl"), %% Each -D option will be expanded to three arguments when %% invoking 'erl'. - ?line NumDOptions = num_d_options(), - ?line Args = lists:flatten([ ["-D", integer_to_list(N, 36), "=1 "] || - N <- lists:seq(1, NumDOptions) ]), - ?line run(Config, Cmd, FileName, Args, - ["Warning: function foo/0 is unused\$", - "_OK_"]), + NumDOptions = num_d_options(), + Args = lists:flatten([ ["-D", integer_to_list(N, 36), "=1 "] || + N <- lists:seq(1, NumDOptions) ]), + run(Config, Cmd, FileName, Args, + ["Warning: function foo/0 is unused\$", + "_OK_"]), ok. num_d_options() -> case {os:type(),os:version()} of - {{win32,_},_} -> - %% The maximum size of a command line in the command - %% shell on Windows is 8191 characters. - %% Each -D option is expanded to "@dv NN 1", i.e. - %% 8 characters. (Numbers up to 1295 can be expressed - %% as two 36-base digits.) - 1000; - {{unix,linux},Version} when Version < {2,6,23} -> - %% On some older 64-bit versions of Linux, the maximum number - %% of arguments is 16383. - %% See: http://www.in-ulm.de/~mascheck/various/argmax/ - 5440; - {{unix,darwin},{Major,_,_}} when Major >= 11 -> - %% "getconf ARG_MAX" still reports 262144 (as in previous - %% version of MacOS X), but the useful space seem to have - %% shrunk significantly (or possibly the number of arguments). - %% 7673 - 7500; - {_,_} -> - 12000 + {{win32,_},_} -> + %% The maximum size of a command line in the command + %% shell on Windows is 8191 characters. + %% Each -D option is expanded to "@dv NN 1", i.e. + %% 8 characters. (Numbers up to 1295 can be expressed + %% as two 36-base digits.) + 1000; + {{unix,linux},Version} when Version < {2,6,23} -> + %% On some older 64-bit versions of Linux, the maximum number + %% of arguments is 16383. + %% See: http://www.in-ulm.de/~mascheck/various/argmax/ + 5440; + {{unix,darwin},{Major,_,_}} when Major >= 11 -> + %% "getconf ARG_MAX" still reports 262144 (as in previous + %% version of MacOS X), but the useful space seem to have + %% shrunk significantly (or possibly the number of arguments). + %% 7673 + 7500; + {_,_} -> + 12000 end. erlc() -> case os:find_executable("erlc") of - false -> - test_server:fail("Can't find erlc"); - Erlc -> - "\"" ++ Erlc ++ "\"" + false -> + test_server:fail("Can't find erlc"); + Erlc -> + "\"" ++ Erlc ++ "\"" end. make_dep_options(Config) -> @@ -264,30 +260,30 @@ make_dep_options(Config) -> DepRE = ["/erl_test_ok[.]beam: \\\\$", - "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", - "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", - "_OK_"], + "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", + "_OK_"], DepRETarget = - ["^target: \\\\$", - "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", - "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", - "_OK_"], + ["^target: \\\\$", + "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", + "_OK_"], DepREMP = - ["/erl_test_ok[.]beam: \\\\$", - "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", - "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", - [], - "/system_test/erlc_SUITE_data/include/erl_test.hrl:$", - "_OK_"], + ["/erl_test_ok[.]beam: \\\\$", + "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", + [], + "/system_test/erlc_SUITE_data/include/erl_test.hrl:$", + "_OK_"], DepREMissing = - ["/erl_test_missing_header[.]beam: \\\\$", - "/system_test/erlc_SUITE_data/src/erl_test_missing_header[.]erl \\\\$", - "/system_test/erlc_SUITE_data/include/erl_test[.]hrl \\\\$", - "missing.hrl$", - "_OK_"], + ["/erl_test_missing_header[.]beam: \\\\$", + "/system_test/erlc_SUITE_data/src/erl_test_missing_header[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl \\\\$", + "missing.hrl$", + "_OK_"], %% Test plain -M run(Config, Cmd, FileName, "-M", DepRE), @@ -309,7 +305,7 @@ make_dep_options(Config) -> %% Test -MF File -MT Target TargetDepFile = filename:join(OutDir, "target.deps"), run(Config, Cmd, FileName, "-MF "++TargetDepFile++" -MT target", - ["_OK_"]), + ["_OK_"]), {ok,TargetBin} = file:read_file(TargetDepFile), verify_result(binary_to_list(TargetBin)++["_OK_"], DepRETarget), @@ -358,12 +354,12 @@ split([], Current, Lines) -> match_messages([Msg|Rest1], [Regexp|Rest2]) -> case re:run(Msg, Regexp, [{capture,none}, unicode]) of - match -> - ok; - nomatch -> - io:format("Not matching: ~s\n", [Msg]), - io:format("Regexp : ~s\n", [Regexp]), - test_server:fail(message_mismatch) + match -> + ok; + nomatch -> + io:format("Not matching: ~s\n", [Msg]), + io:format("Regexp : ~s\n", [Regexp]), + test_server:fail(message_mismatch) end, match_messages(Rest1, Rest2); match_messages([], [Expect|Rest]) -> @@ -374,17 +370,17 @@ match_messages([], []) -> ok. get_cmd(Cfg) -> - ?line {SrcDir, IncDir, OutDir} = get_dirs(Cfg), - ?line Cmd = erlc() ++ " -I" ++ IncDir ++ " -o" ++ OutDir ++ " ", + {SrcDir, IncDir, OutDir} = get_dirs(Cfg), + Cmd = erlc() ++ " -I" ++ IncDir ++ " -o" ++ OutDir ++ " ", {SrcDir, OutDir, Cmd}. get_dirs(Cfg) -> - ?line DataDir = ?config(data_dir, Cfg), - ?line PrivDir = ?config(priv_dir, Cfg), - ?line SrcDir = filename:join(DataDir, "src"), - ?line IncDir = filename:join(DataDir, "include"), + DataDir = ?config(data_dir, Cfg), + PrivDir = ?config(priv_dir, Cfg), + SrcDir = filename:join(DataDir, "src"), + IncDir = filename:join(DataDir, "include"), {SrcDir, IncDir, PrivDir}. - + exists(Name) -> filelib:is_file(Name). @@ -405,7 +401,7 @@ run_command(Config, Cmd) -> run_command(Dir, {win32, _}, Cmd) -> BatchFile = filename:join(Dir, "run.bat"), Run = re:replace(filename:rootname(BatchFile), "/", "\\", - [global,{return,list}]), + [global,{return,list}]), {BatchFile, Run, ["@echo off\r\n", diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index 6440cbf0d7..91fb79bc80 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -81,17 +81,17 @@ otp_8209(doc) -> otp_8209(suite) -> []; otp_8209(Config) when is_list(Config) -> - ?line {ok,[[PName]]} = init:get_argument(progname), - ?line SNameS = "erlexec_test_01", - ?line SName = list_to_atom(SNameS++"@"++ + {ok,[[PName]]} = init:get_argument(progname), + SNameS = "erlexec_test_01", + SName = list_to_atom(SNameS++"@"++ hd(tl(string:tokens(atom_to_list(node()),"@")))), - ?line Cmd = PName ++ " dummy_param -sname "++SNameS++" -setcookie "++ + Cmd = PName ++ " dummy_param -sname "++SNameS++" -setcookie "++ atom_to_list(erlang:get_cookie()), - ?line open_port({spawn,Cmd},[]), - ?line pong = loop_ping(SName,40), - ?line {ok,[[_]]} = rpc:call(SName,init,get_argument,[home]), - ?line ["dummy_param"] = rpc:call(SName,init,get_plain_arguments,[]), - ?line ok = cleanup_nodes(), + open_port({spawn,Cmd},[]), + pong = loop_ping(SName,40), + {ok,[[_]]} = rpc:call(SName,init,get_argument,[home]), + ["dummy_param"] = rpc:call(SName,init,get_plain_arguments,[]), + ok = cleanup_nodes(), ok. cleanup_nodes() -> @@ -126,14 +126,13 @@ loop_ping(Node,N) -> args_file(doc) -> []; args_file(suite) -> []; args_file(Config) when is_list(Config) -> - ?line AFN1 = privfile("1", Config), - ?line AFN2 = privfile("2", Config), - ?line AFN3 = privfile("3", Config), - ?line AFN4 = privfile("4", Config), - ?line AFN5 = privfile("5", Config), - ?line AFN6 = privfile("6", Config), - ?line write_file(AFN1, - "-MiscArg2~n" + AFN1 = privfile("1", Config), + AFN2 = privfile("2", Config), + AFN3 = privfile("3", Config), + AFN4 = privfile("4", Config), + AFN5 = privfile("5", Config), + AFN6 = privfile("6", Config), + write_file(AFN1, "-MiscArg2~n" "# a comment +\\#1000~n" "+\\#200 # another comment~n" "~n" @@ -145,7 +144,7 @@ args_file(Config) when is_list(Config) -> "+\\#700~n" "-extra +XtraArg6~n", [AFN2]), - ?line write_file(AFN2, + write_file(AFN2, "-MiscArg3~n" "+\\#300~n" "-args_file ~s~n" @@ -156,61 +155,61 @@ args_file(Config) when is_list(Config) -> "-args_file ~s~n" "-extra +XtraArg5~n", [AFN3, AFN4, AFN5, AFN6]), - ?line write_file(AFN3, + write_file(AFN3, "# comment again~n" " -MiscArg4 +\\#400 -extra +XtraArg1"), - ?line write_file(AFN4, + write_file(AFN4, " -MiscArg6 +\\#600 -extra +XtraArg2~n" "+XtraArg3~n" "+XtraArg4~n" "# comment again~n"), - ?line write_file(AFN5, ""), - ?line write_file(AFN6, "-extra # +XtraArg10~n"), - ?line CmdLine = "+#100 -MiscArg1 " + write_file(AFN5, ""), + write_file(AFN6, "-extra # +XtraArg10~n"), + CmdLine = "+#100 -MiscArg1 " ++ "-args_file " ++ AFN1 ++ " +#800 -MiscArg8 -extra +XtraArg7 +XtraArg8", - ?line {Emu, Misc, Extra} = emu_args(CmdLine), - ?line verify_args(["-#100", "-#200", "-#300", "-#400", + {Emu, Misc, Extra} = emu_args(CmdLine), + verify_args(["-#100", "-#200", "-#300", "-#400", "-#500", "-#600", "-#700", "-#800"], Emu), - ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4", + verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4", "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"], Misc), - ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", + verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", "+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"], Extra), - ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10", + verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10", "-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4", "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8", "+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", "+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"], Emu), - ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10", + verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10", "-#100", "-#200", "-#300", "-#400", "-#500", "-#600", "-#700", "-#800", "+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", "+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"], Misc), - ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10", + verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10", "-#100", "-#200", "-#300", "-#400", "-#500", "-#600", "-#700", "-#800", "-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4", "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"], Extra), - ?line ok. + ok. evil_args_file(doc) -> []; evil_args_file(suite) -> []; evil_args_file(Config) when is_list(Config) -> - ?line Lim = 300, - ?line FNums = lists:seq(1, Lim), + Lim = 300, + FNums = lists:seq(1, Lim), lists:foreach(fun (End) when End == Lim -> - ?line AFN = privfile(integer_to_list(End), Config), - ?line write_file(AFN, + AFN = privfile(integer_to_list(End), Config), + write_file(AFN, "-MiscArg~p ", [End]); (I) -> - ?line AFNX = privfile(integer_to_list(I), Config), - ?line AFNY = privfile(integer_to_list(I+1), Config), + AFNX = privfile(integer_to_list(I), Config), + AFNY = privfile(integer_to_list(I+1), Config), {Frmt, Args} = case I rem 2 of 0 -> @@ -220,61 +219,61 @@ evil_args_file(Config) when is_list(Config) -> {"-MiscArg~p -args_file ~s", [I, AFNY]} end, - ?line write_file(AFNX, Frmt, Args) + write_file(AFNX, Frmt, Args) end, FNums), - ?line {_Emu, Misc, _Extra} = emu_args("-args_file " + {_Emu, Misc, _Extra} = emu_args("-args_file " ++ privfile("1", Config)), - ?line ANums = FNums + ANums = FNums ++ lists:reverse(lists:filter(fun (I) when I == Lim -> false; (I) when I rem 2 == 0 -> true; (_) -> false end, FNums)), - ?line verify_args(lists:map(fun (I) -> "-MiscArg"++integer_to_list(I) end, + verify_args(lists:map(fun (I) -> "-MiscArg"++integer_to_list(I) end, ANums), Misc), - ?line ok. + ok. env(doc) -> []; env(suite) -> []; env(Config) when is_list(Config) -> - ?line os:putenv("ERL_AFLAGS", "-MiscArg1 +#100 -extra +XtraArg1 +XtraArg2"), - ?line CmdLine = "+#200 -MiscArg2 -extra +XtraArg3 +XtraArg4", - ?line os:putenv("ERL_FLAGS", "-MiscArg3 +#300 -extra +XtraArg5"), - ?line os:putenv("ERL_ZFLAGS", "-MiscArg4 +#400 -extra +XtraArg6"), - ?line {Emu, Misc, Extra} = emu_args(CmdLine), - ?line verify_args(["-#100", "-#200", "-#300", "-#400"], Emu), - ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4"], + os:putenv("ERL_AFLAGS", "-MiscArg1 +#100 -extra +XtraArg1 +XtraArg2"), + CmdLine = "+#200 -MiscArg2 -extra +XtraArg3 +XtraArg4", + os:putenv("ERL_FLAGS", "-MiscArg3 +#300 -extra +XtraArg5"), + os:putenv("ERL_ZFLAGS", "-MiscArg4 +#400 -extra +XtraArg6"), + {Emu, Misc, Extra} = emu_args(CmdLine), + verify_args(["-#100", "-#200", "-#300", "-#400"], Emu), + verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4"], Misc), - ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", + verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", "+XtraArg5", "+XtraArg6"], Extra), - ?line ok. + ok. args_file_env(doc) -> []; args_file_env(suite) -> []; args_file_env(Config) when is_list(Config) -> - ?line AFN1 = privfile("1", Config), - ?line AFN2 = privfile("2", Config), - ?line write_file(AFN1, "-MiscArg2 +\\#200 -extra +XtraArg1"), - ?line write_file(AFN2, "-MiscArg3 +\\#400 -extra +XtraArg3"), - ?line os:putenv("ERL_AFLAGS", + AFN1 = privfile("1", Config), + AFN2 = privfile("2", Config), + write_file(AFN1, "-MiscArg2 +\\#200 -extra +XtraArg1"), + write_file(AFN2, "-MiscArg3 +\\#400 -extra +XtraArg3"), + os:putenv("ERL_AFLAGS", "-MiscArg1 +#100 -args_file "++AFN1++ " -extra +XtraArg2"), - ?line CmdLine = "+#300 -args_file "++AFN2++" -MiscArg4 -extra +XtraArg4", - ?line os:putenv("ERL_FLAGS", "-MiscArg5 +#500 -extra +XtraArg5"), - ?line os:putenv("ERL_ZFLAGS", "-MiscArg6 +#600 -extra +XtraArg6"), - ?line {Emu, Misc, Extra} = emu_args(CmdLine), - ?line verify_args(["-#100", "-#200", "-#300", "-#400", + CmdLine = "+#300 -args_file "++AFN2++" -MiscArg4 -extra +XtraArg4", + os:putenv("ERL_FLAGS", "-MiscArg5 +#500 -extra +XtraArg5"), + os:putenv("ERL_ZFLAGS", "-MiscArg6 +#600 -extra +XtraArg6"), + {Emu, Misc, Extra} = emu_args(CmdLine), + verify_args(["-#100", "-#200", "-#300", "-#400", "-#500", "-#600"], Emu), - ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4", + verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4", "-MiscArg5", "-MiscArg6"], Misc), - ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", + verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", "+XtraArg5", "+XtraArg6"], Extra), - ?line ok. + ok. %% Make sure "erl -detached" survives when parent process group gets killed otp_7461(doc) -> []; @@ -304,7 +303,7 @@ otp_7461_do(Config) -> io:format("alive=~p node=~p\n",[is_alive(), node()]), TestProg = filename:join([?config(data_dir, Config), "erlexec_tests"]), {ok, [[ErlProg]]} = init:get_argument(progname), - ?line Cmd = TestProg ++ " " ++ ErlProg ++ + Cmd = TestProg ++ " " ++ ErlProg ++ " -detached -sname " ++ get_nodename(otp_7461) ++ " -setcookie " ++ atom_to_list(erlang:get_cookie()) ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ @@ -314,29 +313,31 @@ otp_7461_do(Config) -> %% open_port fork+exec io:format("spawn port prog ~p\n",[Cmd]), - ?line Port = open_port({spawn, Cmd}, [eof]), + Port = open_port({spawn, Cmd}, [eof]), io:format("Wait for node to connect...\n",[]), - ?line {nodeup, Slave} = receive Msg -> Msg + {nodeup, Slave} = receive Msg -> Msg after 20*1000 -> timeout end, io:format("Node alive: ~p\n", [Slave]), - ?line pong = net_adm:ping(Slave), + pong = net_adm:ping(Slave), io:format("Ping ok towards ~p\n", [Slave]), - ?line Port ! { self(), {command, "K"}}, % Kill child process group - ?line {Port, {data, "K"}} = receive Msg2 -> Msg2 end, - ?line port_close(Port), + Port ! { self(), {command, "K"}}, % Kill child process group + {Port, {data, "K"}} = receive Msg2 -> Msg2 end, + port_close(Port), %% Now the actual test. Detached node should still be alive. - ?line pong = net_adm:ping(Slave), + pong = net_adm:ping(Slave), io:format("Ping still ok towards ~p\n", [Slave]), %% Halt node - ?line rpc:cast(Slave, ?MODULE, otp_7461_remote, [[halt, self()]]), + rpc:cast(Slave, ?MODULE, otp_7461_remote, [[halt, self()]]), - ?line {nodedown, Slave} = receive Msg3 -> Msg3 - after 20*1000 -> timeout end, + {nodedown, Slave} = receive + Msg3 -> Msg3 + after 20*1000 -> timeout + end, io:format("Node dead: ~p\n", [Slave]), ok. @@ -356,17 +357,17 @@ zdbbl_dist_buf_busy_limit(suite) -> zdbbl_dist_buf_busy_limit(Config) when is_list(Config) -> LimKB = 1122233, LimB = LimKB*1024, - ?line {ok,[[PName]]} = init:get_argument(progname), - ?line SNameS = "erlexec_test_02", - ?line SName = list_to_atom(SNameS++"@"++ + {ok,[[PName]]} = init:get_argument(progname), + SNameS = "erlexec_test_02", + SName = list_to_atom(SNameS++"@"++ hd(tl(string:tokens(atom_to_list(node()),"@")))), - ?line Cmd = PName ++ " -sname "++SNameS++" -setcookie "++ + Cmd = PName ++ " -sname "++SNameS++" -setcookie "++ atom_to_list(erlang:get_cookie()) ++ " +zdbbl " ++ integer_to_list(LimKB), - ?line open_port({spawn,Cmd},[]), - ?line pong = loop_ping(SName,40), - ?line LimB = rpc:call(SName,erlang,system_info,[dist_buf_busy_limit]), - ?line ok = cleanup_node(SNameS, 10), + open_port({spawn,Cmd},[]), + pong = loop_ping(SName,40), + LimB = rpc:call(SName,erlang,system_info,[dist_buf_busy_limit]), + ok = cleanup_node(SNameS, 10), ok. @@ -430,8 +431,7 @@ verify_not_args(Xs, Ys) -> true -> exit({arg_present, X}); false -> ok end - end, - Xs). + end, Xs). emu_args(CmdLineArgs) -> io:format("CmdLineArgs = ~ts~n", [CmdLineArgs]), diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index 8ad2a32278..91adbc05c0 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -238,62 +238,62 @@ end_per_testcase(_Case, Config) -> -define(PID_MARKER, $E,$T,$H,$R,$-,$T,$E,$S,$T,$-,$P,$I,$D). port_prog_killer(EProc, OSProc) when is_pid(EProc), is_list(OSProc) -> - ?line process_flag(trap_exit, true), - ?line Ref = erlang:monitor(process, EProc), - ?line receive - {'DOWN', Ref, _, _, Reason} when is_tuple(Reason), - element(1, Reason) - == timetrap_timeout -> - ?line Cmd = "kill -9 " ++ OSProc, - ?line ?t:format("Test case timed out. " - "Trying to kill port program.~n" - " Executing: ~p~n", [Cmd]), - ?line case os:cmd(Cmd) of - [] -> - ok; - OsCmdRes -> - ?line ?t:format(" ~s", [OsCmdRes]) - end; - {'DOWN', Ref, _, _, _} -> - %% OSProc is assumed to have terminated by itself - ?line ok - end. + process_flag(trap_exit, true), + Ref = erlang:monitor(process, EProc), + receive + {'DOWN', Ref, _, _, Reason} when is_tuple(Reason), + element(1, Reason) + == timetrap_timeout -> + Cmd = "kill -9 " ++ OSProc, + ?t:format("Test case timed out. " + "Trying to kill port program.~n" + " Executing: ~p~n", [Cmd]), + case os:cmd(Cmd) of + [] -> + ok; + OsCmdRes -> + ?t:format(" ~s", [OsCmdRes]) + end; + %% OSProc is assumed to have terminated by itself + {'DOWN', Ref, _, _, _} -> + ok + end. get_line(_Port, eol, Data) -> - ?line Data; + Data; get_line(Port, noeol, Data) -> - ?line receive + receive {Port, {data, {Flag, NextData}}} -> - ?line get_line(Port, Flag, Data ++ NextData); + get_line(Port, Flag, Data ++ NextData); {Port, eof} -> - ?line ?t:fail(port_prog_unexpectedly_closed) + ?t:fail(port_prog_unexpectedly_closed) end. read_case_data(Port, TestCase) -> - ?line receive - {Port, {data, {eol, [?SUCCESS_MARKER]}}} -> - ?line ok; - {Port, {data, {Flag, [?SUCCESS_MARKER | CommentStart]}}} -> - ?line {comment, get_line(Port, Flag, CommentStart)}; - {Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} -> - ?line {skipped, get_line(Port, Flag, CommentStart)}; - {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} -> - ?line ?t:fail(get_line(Port, Flag, ReasonStart)); - {Port, {data, {eol, [?PID_MARKER | PidStr]}}} -> - ?line ?t:format("Port program pid: ~s~n", [PidStr]), - ?line CaseProc = self(), - ?line _ = list_to_integer(PidStr), % Sanity check - spawn_opt(fun () -> - port_prog_killer(CaseProc, PidStr) - end, - [{priority, max}, link]), - read_case_data(Port, TestCase); - {Port, {data, {Flag, LineStart}}} -> - ?line ?t:format("~s~n", [get_line(Port, Flag, LineStart)]), - read_case_data(Port, TestCase); - {Port, eof} -> - ?line ?t:fail(port_prog_unexpectedly_closed) - end. + receive + {Port, {data, {eol, [?SUCCESS_MARKER]}}} -> + ok; + {Port, {data, {Flag, [?SUCCESS_MARKER | CommentStart]}}} -> + {comment, get_line(Port, Flag, CommentStart)}; + {Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} -> + {skipped, get_line(Port, Flag, CommentStart)}; + {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} -> + ?t:fail(get_line(Port, Flag, ReasonStart)); + {Port, {data, {eol, [?PID_MARKER | PidStr]}}} -> + ?t:format("Port program pid: ~s~n", [PidStr]), + CaseProc = self(), + _ = list_to_integer(PidStr), % Sanity check + spawn_opt(fun () -> + port_prog_killer(CaseProc, PidStr) + end, + [{priority, max}, link]), + read_case_data(Port, TestCase); + {Port, {data, {Flag, LineStart}}} -> + ?t:format("~s~n", [get_line(Port, Flag, LineStart)]), + read_case_data(Port, TestCase); + {Port, eof} -> + ?t:fail(port_prog_unexpectedly_closed) + end. run_case(Config, Test, TestArgs) -> run_case(Config, Test, TestArgs, fun (_Port) -> ok end). @@ -307,17 +307,13 @@ run_case(Config, Test, TestArgs, Fun) -> eof, {line, 1024}]) of Port when is_port(Port) -> - ?line Fun(Port), - ?line CaseResult = read_case_data(Port, Test), - ?line receive + Fun(Port), + CaseResult = read_case_data(Port, Test), + receive {Port, eof} -> - ?line ok + ok end, - ?line CaseResult; + CaseResult; Error -> - ?line ?t:fail({open_port_failed, Error}) + ?t:fail({open_port_failed, Error}) end. - - - - diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl index d6df1aab6b..1832289337 100644 --- a/erts/test/install_SUITE.erl +++ b/erts/test/install_SUITE.erl @@ -97,29 +97,29 @@ end_per_group(_GroupName, Config) -> %% bin_default(Config) when is_list(Config) -> - ?line E = "/usr/local", - ?line Bs = "/usr/local/bin", - ?line Be = Bs, - ?line EBs = "/usr/local/lib/erlang/bin", - ?line EBe = EBs, - ?line RP = "../lib/erlang/bin", + E = "/usr/local", + Bs = "/usr/local/bin", + Be = Bs, + EBs = "/usr/local/lib/erlang/bin", + EBe = EBs, + RP = "../lib/erlang/bin", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, bindir_symlinks = BSL, symlinks = SL}) -> - ?line B = join([TP, D, EP, Be]), + B = join([TP, D, EP, Be]), Expct = case {SL, BSL} of {false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {false, _} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,D,EP,EBe])}}; {true, "absolute"} -> - ?line {ok,{absolute,B,join([TP,EP,EBe])}}; + {ok,{absolute,B,join([TP,EP,EBe])}}; {true, _} -> - ?line {ok,{relative,B,RP}} + {ok,{relative,B,RP}} end, expect(Expct, Res) end, @@ -128,30 +128,30 @@ bin_default(Config) when is_list(Config) -> erlang_bindir = EBs}, ChkRes). bin_default_dirty(Config) when is_list(Config) -> - ?line E = "/usr/./local/lib/..", - ?line Bs = "/usr/local//lib/../lib/erlang/../../bin", - ?line Be = "/usr/local/lib/../lib/erlang/../../bin", - ?line EBs = "/usr/local/lib/../lib/erlang/../erlang/bin/x/y/../..//", - ?line EBe = "/usr/local/lib/../lib/erlang/../erlang/bin/x/y/../..", - ?line RP = "../lib/erlang/bin", + E = "/usr/./local/lib/..", + Bs = "/usr/local//lib/../lib/erlang/../../bin", + Be = "/usr/local/lib/../lib/erlang/../../bin", + EBs = "/usr/local/lib/../lib/erlang/../erlang/bin/x/y/../..//", + EBe = "/usr/local/lib/../lib/erlang/../erlang/bin/x/y/../..", + RP = "../lib/erlang/bin", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, bindir_symlinks = BSL, symlinks = SL}) -> - ?line B = join([TP, D, EP, Be]), + B = join([TP, D, EP, Be]), Expct = case {SL, BSL} of {false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {false, _} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,D,EP,EBe])}}; {true, "absolute"} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,EP,EBe])}}; {true, _} -> - ?line {ok,{relative,B,RP}} + {ok,{relative,B,RP}} end, expect(Expct, Res) end, @@ -161,29 +161,29 @@ bin_default_dirty(Config) when is_list(Config) -> bin_outside_eprfx(Config) when is_list(Config) -> - ?line E = "/usr/local", - ?line Bs = "/usr/bin", - ?line Be = Bs, - ?line EBs = "/usr/local/lib/erlang/bin", - ?line EBe = EBs, - ?line RP = "../local/lib/erlang/bin", + E = "/usr/local", + Bs = "/usr/bin", + Be = Bs, + EBs = "/usr/local/lib/erlang/bin", + EBe = EBs, + RP = "../local/lib/erlang/bin", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, bindir_symlinks = BSL, symlinks = SL}) -> - ?line B = join([TP, D, EP, Be]), + B = join([TP, D, EP, Be]), Expct = case {SL, BSL} of {false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {false, _} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,D,EP,EBe])}}; {true, "relative"} -> - ?line {ok,{relative,B,RP}}; + {ok,{relative,B,RP}}; {true, _} -> - ?line {ok,{absolute,B,join([TP,EP,EBe])}} + {ok,{absolute,B,join([TP,EP,EBe])}} end, expect(Expct, Res) end, @@ -193,29 +193,29 @@ bin_outside_eprfx(Config) when is_list(Config) -> bin_outside_eprfx_dirty(Config) when is_list(Config) -> - ?line E = "/usr/local/lib/..", - ?line Bs = "/usr/local/lib/../../bin", - ?line Be = Bs, - ?line EBs = "/usr/local/lib/erlang/bin", - ?line EBe = EBs, - ?line RP = "../local/lib/erlang/bin", + E = "/usr/local/lib/..", + Bs = "/usr/local/lib/../../bin", + Be = Bs, + EBs = "/usr/local/lib/erlang/bin", + EBe = EBs, + RP = "../local/lib/erlang/bin", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, bindir_symlinks = BSL, symlinks = SL}) -> - ?line B = join([TP, D, EP, Be]), + B = join([TP, D, EP, Be]), Expct = case {SL, BSL} of {false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {false, _} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,D,EP,EBe])}}; {true, "relative"} -> - ?line {ok,{relative,B,RP}}; + {ok,{relative,B,RP}}; {true, _} -> - ?line {ok,{absolute,B,join([TP,EP,EBe])}} + {ok,{absolute,B,join([TP,EP,EBe])}} end, expect(Expct, Res) end, @@ -224,33 +224,33 @@ bin_outside_eprfx_dirty(Config) when is_list(Config) -> erlang_bindir = EBs}, ChkRes). bin_unreasonable_path(Config) when is_list(Config) -> - ?line E = "/usr/local/../../..", - ?line Bs = "/usr/local/../../../bin", - ?line Be = Bs, - ?line EBs = "/usr/local/../../../bin_unreasonable_path/usr/local/lib/erlang/bin", - ?line EBe = EBs, - ?line RP = "../bin_unreasonable_path/usr/local/lib/erlang/bin", + E = "/usr/local/../../..", + Bs = "/usr/local/../../../bin", + Be = Bs, + EBs = "/usr/local/../../../bin_unreasonable_path/usr/local/lib/erlang/bin", + EBe = EBs, + RP = "../bin_unreasonable_path/usr/local/lib/erlang/bin", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, bindir_symlinks = BSL, symlinks = SL}) -> - ?line B = join([TP, D, EP, Be]), + B = join([TP, D, EP, Be]), Expct = case {TP, SL, BSL} of {_, false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {_, false, _} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,D,EP,EBe])}}; {"", true, "relative"} -> {error, unreasonable_path}; {"", true, _} -> - ?line {ok,{absolute,B,join([TP,EP,EBe])}}; + {ok,{absolute,B,join([TP,EP,EBe])}}; {_, true, "absolute"} -> - ?line {ok,{absolute,B,join([TP,EP,EBe])}}; + {ok,{absolute,B,join([TP,EP,EBe])}}; _ -> - ?line {ok,{relative,B,RP}} + {ok,{relative,B,RP}} end, expect(Expct, Res) end, @@ -270,28 +270,28 @@ bin_unreachable_absolute(Config) when is_list(Config) -> ok = file:write_file(Erlc, "erlc"), ok = file:make_symlink("../../../opt/local/lib/erlang/usr", join([TDir, "/usr/local/lib/erlang"])), - ?line E = "/usr/local", - ?line Bs = "/usr/local/bin", - ?line Be = Bs, - ?line EBs = "/usr/local/lib/erlang/../bin", - ?line EBe = EBs, + E = "/usr/local", + Bs = "/usr/local/bin", + Be = Bs, + EBs = "/usr/local/lib/erlang/../bin", + EBe = EBs, ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, bindir_symlinks = BSL, symlinks = SL}) -> - ?line B = join([TP, D, EP, Be]), + B = join([TP, D, EP, Be]), Expct = case {SL, BSL} of {false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {false, _} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,D,EP,EBe])}}; {true, "relative"} -> {error, unreachable_absolute}; {true, _} -> - ?line {ok,{absolute,B,join([TP,EP,EBe])}} + {ok,{absolute,B,join([TP,EP,EBe])}} end, expect(Expct, Res) end, @@ -311,28 +311,28 @@ bin_unreachable_relative(Config) when is_list(Config) -> ok = file:make_symlink("../../opt/local/bin", join([TDir, "/usr/local/bin"])), - ?line E = "/usr/local", - ?line Bs = "/usr/local/bin", - ?line Be = Bs, - ?line EBs = "/usr/local/lib/erlang/bin", - ?line EBe = EBs, + E = "/usr/local", + Bs = "/usr/local/bin", + Be = Bs, + EBs = "/usr/local/lib/erlang/bin", + EBe = EBs, ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, bindir_symlinks = BSL, symlinks = SL}) -> - ?line B = join([TP, D, EP, Be]), + B = join([TP, D, EP, Be]), Expct = case {SL, BSL} of {false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {false, _} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,D,EP,EBe])}}; {true, "relative"} -> {error, unreachable_relative}; {true, _} -> - ?line {ok,{absolute,B,join([TP,EP,EBe])}} + {ok,{absolute,B,join([TP,EP,EBe])}} end, expect(Expct, Res) end, @@ -350,29 +350,29 @@ bin_ok_symlink(Config) when is_list(Config) -> ok = file:write_file(Erlc, "erlc"), ok = file:make_symlink("../../opt/local/lib", join([TDir, "/usr/local/lib"])), - ?line E = "/usr/local", - ?line Bs = "/usr/local/bin", - ?line Be = Bs, - ?line EBs = "/usr/local/lib/erlang/bin", - ?line EBe = EBs, - ?line RP = "../lib/erlang/bin", + E = "/usr/local", + Bs = "/usr/local/bin", + Be = Bs, + EBs = "/usr/local/lib/erlang/bin", + EBe = EBs, + RP = "../lib/erlang/bin", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, bindir_symlinks = BSL, symlinks = SL}) -> - ?line B = join([TP, D, EP, Be]), + B = join([TP, D, EP, Be]), Expct = case {SL, BSL} of {false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {false, _} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,D,EP,EBe])}}; {true, "absolute"} -> - ?line {ok,{absolute,B,join([TP,EP,EBe])}}; + {ok,{absolute,B,join([TP,EP,EBe])}}; {true, _} -> - ?line {ok,{relative,B,RP}} + {ok,{relative,B,RP}} end, expect(Expct, Res) end, @@ -417,29 +417,29 @@ bin_not_abs(Config) when is_list(Config) -> 'bin white space'(Config) when is_list(Config) -> - ?line E = "/u s r/local", - ?line Bs = "/u s r/local/b i n", - ?line Be = Bs, - ?line EBs = "/u s r/local/lib/erl ang/bin", - ?line EBe = EBs, - ?line RP = "../lib/erl ang/bin", + E = "/u s r/local", + Bs = "/u s r/local/b i n", + Be = Bs, + EBs = "/u s r/local/lib/erl ang/bin", + EBe = EBs, + RP = "../lib/erl ang/bin", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, bindir_symlinks = BSL, symlinks = SL}) -> - ?line B = join([TP, D, EP, Be]), + B = join([TP, D, EP, Be]), Expct = case {SL, BSL} of {false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {false, _} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,D,EP,EBe])}}; {true, "absolute"} -> - ?line {ok,{absolute,B,join([TP,EP,EBe])}}; + {ok,{absolute,B,join([TP,EP,EBe])}}; {true, _} -> - ?line {ok,{relative,B,RP}} + {ok,{relative,B,RP}} end, expect(Expct, Res) end, @@ -448,29 +448,29 @@ bin_not_abs(Config) when is_list(Config) -> erlang_bindir = EBs}, ChkRes). bin_dirname_fail(Config) when is_list(Config) -> - ?line E = "/opt", - ?line Bs = "/opt/lib/../bin", - ?line Be = Bs, - ?line EBs = "/opt/lib/erlang/otp/bin", - ?line EBe = EBs, - ?line CMDPRFX = "PATH=\""++?config(data_dir,Config)++":"++os:getenv("PATH")++"\"", + E = "/opt", + Bs = "/opt/lib/../bin", + Be = Bs, + EBs = "/opt/lib/erlang/otp/bin", + EBe = EBs, + CMDPRFX = "PATH=\""++?config(data_dir,Config)++":"++os:getenv("PATH")++"\"", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, bindir_symlinks = BSL, symlinks = SL}) -> - ?line B = join([TP, D, EP, Be]), + B = join([TP, D, EP, Be]), Expct = case {SL, BSL} of {false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {false, _} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,D,EP,EBe])}}; {true, "relative"} -> - ?line {error, dirname_failed}; + {error, dirname_failed}; {true, _} -> - ?line {ok,{absolute,B,join([TP,EP,EBe])}} + {ok,{absolute,B,join([TP,EP,EBe])}} end, expect(Expct, Res) end, @@ -480,30 +480,30 @@ bin_dirname_fail(Config) when is_list(Config) -> erlang_bindir = EBs}, ChkRes). bin_no_use_dirname_fail(Config) when is_list(Config) -> - ?line E = "/opt", - ?line Bs = "/opt/bin", - ?line Be = Bs, - ?line EBs = "/opt/lib/erlang/otp/bin", - ?line EBe = EBs, - ?line RP = "../lib/erlang/otp/bin", - ?line CMDPRFX = "PATH=\""++?config(data_dir,Config)++":"++os:getenv("PATH")++"\"", + E = "/opt", + Bs = "/opt/bin", + Be = Bs, + EBs = "/opt/lib/erlang/otp/bin", + EBe = EBs, + RP = "../lib/erlang/otp/bin", + CMDPRFX = "PATH=\""++?config(data_dir,Config)++":"++os:getenv("PATH")++"\"", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, bindir_symlinks = BSL, symlinks = SL}) -> - ?line B = join([TP, D, EP, Be]), + B = join([TP, D, EP, Be]), Expct = case {SL, BSL} of {false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {false, _} -> - ?line {ok,{absolute, + {ok,{absolute, B,join([TP,D,EP,EBe])}}; {true, "absolute"} -> - ?line {ok,{absolute,B,join([TP,EP,EBe])}}; + {ok,{absolute,B,join([TP,EP,EBe])}}; {true, _} -> - ?line {ok,{relative,B,RP}} + {ok,{relative,B,RP}} end, expect(Expct, Res) end, @@ -525,13 +525,13 @@ bin_no_srcfile(Config) when is_list(Config) -> Expct = case {SL, BSL} of {false, _} when BSL == "relative"; BSL == "absolute" -> - ?line {error, no_ln_s}; + {error, no_ln_s}; {false,_} -> - ?line {error,{no_srcfile, Erlc}}; + {error,{no_srcfile, Erlc}}; {true, "absolute"} -> - ?line {error,{no_srcfile, Erlc}}; + {error,{no_srcfile, Erlc}}; {true, _} -> - ?line {error,{no_srcfile, RP_Erlc}} + {error,{no_srcfile, RP_Erlc}} end, expect(Expct, Res) end, @@ -731,4 +731,3 @@ join([""|Ds]) -> join(Ds); join([D|Ds]) -> "/" ++ string:strip(D, both, $/) ++ join(Ds). - diff --git a/erts/test/nt_SUITE.erl b/erts/test/nt_SUITE.erl index 1ddaaaaeb5..f40a8114a0 100644 --- a/erts/test/nt_SUITE.erl +++ b/erts/test/nt_SUITE.erl @@ -24,12 +24,12 @@ -include_lib("kernel/include/file.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2,init_per_testcase/2, - end_per_testcase/2,nt/1,handle_eventlog/2, - middleman/1,service_basic/1, service_env/1, user_env/1, synced/1, - service_prio/1, - logout/1, debug/1, restart/1, restart_always/1,stopaction/1, - shutdown_io/0,do_shutdown_io/0]). + init_per_group/2,end_per_group/2,init_per_testcase/2, + end_per_testcase/2,nt/1,handle_eventlog/2, + middleman/1,service_basic/1, service_env/1, user_env/1, synced/1, + service_prio/1, + logout/1, debug/1, restart/1, restart_always/1,stopaction/1, + shutdown_io/0,do_shutdown_io/0]). -define(TEST_TIMEOUT, ?t:seconds(180)). -define(TEST_SERVICES, [1,2,3,4,5,6,7,8,9,10,11]). @@ -38,11 +38,11 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> case os:type() of - {win32, nt} -> - [nt, service_basic, service_env, user_env, synced, - service_prio, logout, debug, restart, restart_always, - stopaction]; - _ -> [nt] + {win32, nt} -> + [nt, service_basic, service_env, user_env, synced, + service_prio, logout, debug, restart, restart_always, + stopaction]; + _ -> [nt] end. groups() -> @@ -67,9 +67,9 @@ init_per_testcase(_Func, Config) -> end_per_testcase(_Func, Config) -> lists:foreach(fun(X) -> - catch remove_service("test_service_" ++ - integer_to_list(X)) end, - ?TEST_SERVICES), + catch remove_service("test_service_" ++ integer_to_list(X)) + end, + ?TEST_SERVICES), Dog = ?config(watchdog, Config), catch test_server:timetrap_cancel(Dog), ok. @@ -80,19 +80,19 @@ erlsrv() -> recv_prog_output(Port) -> receive - {Port, {data, {eol,Data}}} -> - %%io:format("Got data: ~s~n", [Data]), - [ Data | recv_prog_output(Port)]; - _X -> - %%io:format("Got data: ~p~n", [_X]), - Port ! {self(), close}, - receive - _ -> - [] - end + {Port, {data, {eol,Data}}} -> + %%io:format("Got data: ~s~n", [Data]), + [ Data | recv_prog_output(Port)]; + _X -> + %%io:format("Got data: ~p~n", [_X]), + Port ! {self(), close}, + receive + _ -> + [] + end end. - + %%% X == parameters to erlsrv %%% returns command output without stderr do_command(X) -> @@ -100,11 +100,11 @@ do_command(X) -> Port = open_port({spawn, erlsrv() ++ " " ++ X}, [stream, {line, 100}, eof, in]), Res = recv_prog_output(Port), case Res of - [] -> - failed; - _Y -> - %%io:format("~p~n",[_Y]), - ok + [] -> + failed; + _Y -> + %%io:format("~p~n",[_Y]), + ok end. @@ -123,13 +123,13 @@ do_wait_for_it(_,0) -> false; do_wait_for_it(FullName,N) -> case net_adm:ping(FullName) of - pong -> - true; - _ -> - receive - after 1000 -> - do_wait_for_it(FullName,N-1) - end + pong -> + true; + _ -> + receive + after 1000 -> + do_wait_for_it(FullName,N-1) + end end. wait_for_node(Name) -> @@ -139,7 +139,7 @@ wait_for_node(Name) -> make_full_name(Name) -> [_,Suffix] = string:tokens(atom_to_list(node()),"@"), list_to_atom(Name ++ "@" ++ Suffix). - + %%% The following tests are only run on NT: @@ -147,38 +147,37 @@ service_basic(doc) -> ["Check some basic (cosmetic) service parameters"]; service_basic(suite) -> []; service_basic(Config) when is_list(Config) -> - ?line Name = "test_service_20", - ?line IntName = Name++"_internal", - ?line Service = [{servicename,Name}, - {args, ["-setcookie", - atom_to_list(erlang:get_cookie())]}, - {internalservicename,IntName}, - {comment,"Epic comment"}], - ?line ok = erlsrv:store_service(Service), - ?line start_service(Name), - ?line true = wait_for_node(Name), - ?line S2 = erlsrv:get_service(Name), - ?line {value,{comment,"Epic comment"}} = lists:keysearch(comment,1,S2), - ?line {value,{internalservicename,IntName}} = - lists:keysearch(internalservicename,1,S2), - ?line S3 = lists:keyreplace(comment,1,S2,{comment,"Basic comment"}), - ?line S4 = lists:keyreplace(internalservicename,1,S3, - {internalservicename,"WillNotHappen"}), - ?line ok = erlsrv:store_service(S4), - ?line S5 = erlsrv:get_service(Name), - ?line {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S5), - ?line {value,{internalservicename,IntName}} = - lists:keysearch(internalservicename,1,S5), - ?line NewName = "test_service_21", - ?line S6 = erlsrv:new_service(NewName,S5,[]), % should remove - % internalservicename - ?line ok = erlsrv:store_service(S6), - ?line S7 = erlsrv:get_service(NewName), - ?line {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S7), - ?line {value,{internalservicename,[$t,$e,$s,$t | _]}} = - lists:keysearch(internalservicename,1,S7), - ?line remove_service(Name), - ?line remove_service(NewName), + Name = "test_service_20", + IntName = Name++"_internal", + Service = [{servicename,Name}, + {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}, + {internalservicename,IntName}, + {comment,"Epic comment"}], + ok = erlsrv:store_service(Service), + start_service(Name), + true = wait_for_node(Name), + S2 = erlsrv:get_service(Name), + {value,{comment,"Epic comment"}} = lists:keysearch(comment,1,S2), + {value,{internalservicename,IntName}} = + lists:keysearch(internalservicename,1,S2), + S3 = lists:keyreplace(comment,1,S2,{comment,"Basic comment"}), + S4 = lists:keyreplace(internalservicename,1,S3, + {internalservicename,"WillNotHappen"}), + ok = erlsrv:store_service(S4), + S5 = erlsrv:get_service(Name), + {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S5), + {value,{internalservicename,IntName}} = + lists:keysearch(internalservicename,1,S5), + NewName = "test_service_21", + S6 = erlsrv:new_service(NewName,S5,[]), % should remove + % internalservicename + ok = erlsrv:store_service(S6), + S7 = erlsrv:get_service(NewName), + {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S7), + {value,{internalservicename,[$t,$e,$s,$t | _]}} = + lists:keysearch(internalservicename,1,S7), + remove_service(Name), + remove_service(NewName), ok. service_env(doc) -> @@ -186,225 +185,215 @@ service_env(doc) -> "erlang process created by erlsrv."]; service_env(suite) -> []; service_env(Config) when is_list(Config) -> - ?line Name = "test_service_2", - ?line Service = [{servicename,Name}, - {args, ["-setcookie", - atom_to_list(erlang:get_cookie())]}], - ?line ok = erlsrv:store_service(Service), - ?line start_service(Name), - ?line true = wait_for_node(Name), - ?line Name = rpc:call(make_full_name(Name),os,getenv, - ["ERLSRV_SERVICE_NAME"]), - ?line "erlsrv.exe" = filename:basename( - hd( - string:tokens( - rpc:call(make_full_name(Name), - os, - getenv, - ["ERLSRV_EXECUTABLE"]), - "\""))), - ?line remove_service(Name), + Name = "test_service_2", + Service = [{servicename,Name}, + {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}], + ok = erlsrv:store_service(Service), + start_service(Name), + true = wait_for_node(Name), + Name = rpc:call(make_full_name(Name),os,getenv, + ["ERLSRV_SERVICE_NAME"]), + "erlsrv.exe" = filename:basename( + hd( + string:tokens( + rpc:call(make_full_name(Name), + os, + getenv, + ["ERLSRV_EXECUTABLE"]), + "\""))), + remove_service(Name), ok. user_env(doc) -> ["Check that the user defined environment is ADDED to the service's"++ " normal dito."]; user_env(suite) -> []; user_env(Config) when is_list(Config) -> - ?line Name = "test_service_3", - ?line Service = [{servicename,Name},{env,[{"HUBBA","BUBBA"}]}, - {args, ["-setcookie", - atom_to_list(erlang:get_cookie())]}], - ?line ok = erlsrv:store_service(Service), - ?line start_service(Name), - ?line true = wait_for_node(Name), - ?line true = rpc:call(make_full_name(Name),os,getenv, - ["SystemDrive"]) =/= false, - ?line "BUBBA" = rpc:call(make_full_name(Name),os,getenv,["HUBBA"]), - ?line remove_service(Name), + Name = "test_service_3", + Service = [{servicename,Name},{env,[{"HUBBA","BUBBA"}]}, + {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}], + ok = erlsrv:store_service(Service), + start_service(Name), + true = wait_for_node(Name), + true = rpc:call(make_full_name(Name),os,getenv, + ["SystemDrive"]) =/= false, + "BUBBA" = rpc:call(make_full_name(Name),os,getenv,["HUBBA"]), + remove_service(Name), ok. synced(doc) -> ["Check that services are stopped and started syncronous and that"++ " failed stopactions kill the erlang machine anyway."]; synced(suite) -> []; synced(Config) when is_list(Config) -> - ?line Name0 = "test_service_4", - ?line Service0 = [{servicename,Name0}, - {machine, "N:\\nickeNyfikenPaSjukhus"}], - ?line ok = erlsrv:store_service(Service0), - ?line true = (catch start_service(Name0)) =/= ok, - ?line remove_service(Name0), - ?line Name = "test_service_5", - ?line Service = [{servicename,Name}, - {stopaction,"erlang:info(garbage_collection)."}, - {args, ["-setcookie", - atom_to_list(erlang:get_cookie())]}], - ?line ok = erlsrv:store_service(Service), - ?line start_service(Name), - ?line true = wait_for_node(Name), - ?line T1 = calendar:datetime_to_gregorian_seconds( - calendar:universal_time()), - ?line stop_service(Name), - ?line Diff1 = calendar:datetime_to_gregorian_seconds( - calendar:universal_time()) - T1, - ?line true = Diff1 > 30, - ?line start_service(Name), - ?line true = wait_for_node(Name), - ?line T2 = calendar:datetime_to_gregorian_seconds( - calendar:universal_time()), - ?line remove_service(Name), - ?line Diff2 = calendar:datetime_to_gregorian_seconds( - calendar:universal_time()) - T2, - ?line true = Diff2 > 30, + Name0 = "test_service_4", + Service0 = [{servicename,Name0}, + {machine, "N:\\nickeNyfikenPaSjukhus"}], + ok = erlsrv:store_service(Service0), + true = (catch start_service(Name0)) =/= ok, + remove_service(Name0), + Name = "test_service_5", + Service = [{servicename,Name}, + {stopaction,"erlang:info(garbage_collection)."}, + {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}], + ok = erlsrv:store_service(Service), + start_service(Name), + true = wait_for_node(Name), + T1 = calendar:datetime_to_gregorian_seconds( + calendar:universal_time()), + stop_service(Name), + Diff1 = calendar:datetime_to_gregorian_seconds( + calendar:universal_time()) - T1, + true = Diff1 > 30, + start_service(Name), + true = wait_for_node(Name), + T2 = calendar:datetime_to_gregorian_seconds( + calendar:universal_time()), + remove_service(Name), + Diff2 = calendar:datetime_to_gregorian_seconds( + calendar:universal_time()) - T2, + true = Diff2 > 30, ok. service_prio(doc) -> ["Check that a service with higher prio create port programs with " "higher prio."]; service_prio(suite) -> []; service_prio(Config) when is_list(Config) -> - ?line Name = "test_service_6", - ?line Service = [{servicename,Name},{prio,"high"}, - {env, [{"HEART_COMMAND","echo off"}]}, - {args, ["-setcookie", - atom_to_list(erlang:get_cookie()), - "-heart"]}], - ?line ok = erlsrv:store_service(Service), - ?line {ok, OldProcs} = get_current_procs(Config), - ?line start_service(Name), - ?line {ok, NewProcs} = get_current_procs(Config), + Name = "test_service_6", + Service = [{servicename,Name},{prio,"high"}, + {env, [{"HEART_COMMAND","echo off"}]}, + {args, ["-setcookie", atom_to_list(erlang:get_cookie()), + "-heart"]}], + ok = erlsrv:store_service(Service), + {ok, OldProcs} = get_current_procs(Config), + start_service(Name), + {ok, NewProcs} = get_current_procs(Config), timer:sleep(2000), - ?line {ok, NewProcs2} = get_current_procs(Config), - ?line remove_service(Name), - ?line Diff = arrived_procs(OldProcs,NewProcs), + {ok, NewProcs2} = get_current_procs(Config), + remove_service(Name), + Diff = arrived_procs(OldProcs,NewProcs), io:format("NewProcs ~p~n after sleep~n ~p~n",[Diff, arrived_procs(OldProcs,NewProcs2)]), %% Not really correct, could fail if another heart is %% started at the same time... - ?line {value, {"heart.exe",_,"high"}} = - lists:keysearch("heart.exe",1,Diff), + {value, {"heart.exe",_,"high"}} = lists:keysearch("heart.exe",1,Diff), ok. logout(doc) -> ["Check that logout does not kill services"]; logout(suite) -> []; logout(Config) when is_list(Config) -> - ?line {comment, "Have to be run manually by registering a service with " ++ - "heart, logout and log in again and then examine that the heart " ++ - "process id is not changed."}. + {comment, "Have to be run manually by registering a service with " ++ + "heart, logout and log in again and then examine that the heart " ++ + "process id is not changed."}. debug(doc) -> ["Check the debug options to erlsrv."]; debug(suite) -> []; debug(Config) when is_list(Config) -> - ?line Name0 = "test_service_7", + Name0 = "test_service_7", %% We used to set the privdir as temporary directory, but for some %% reason we don't seem to have write access to that directory, %% so we'll use the directory specified in the next line. - ?line TempDir = "C:/TEMP", - ?line Service0 = [{servicename,Name0}, - {workdir,filename:nativename(TempDir)}, - {debugtype,"reuse"}, - {args, ["-setcookie", - atom_to_list(erlang:get_cookie())]}], - ?line ok = erlsrv:store_service(Service0), - ?line T1 = calendar:datetime_to_gregorian_seconds( - calendar:local_time()), + TempDir = "C:/TEMP", + Service0 = [{servicename,Name0}, + {workdir,filename:nativename(TempDir)}, + {debugtype,"reuse"}, + {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}], + ok = erlsrv:store_service(Service0), + T1 = calendar:datetime_to_gregorian_seconds( + calendar:local_time()), %% sleep a little - ?line receive after 2000 -> ok end, - ?line start_service(Name0), - ?line true = wait_for_node(Name0), - ?line LF = filename:join(TempDir, Name0++".debug"), - ?line {ok,Info0} = file:read_file_info(LF), - ?line T2 = calendar:datetime_to_gregorian_seconds( - Info0#file_info.mtime), - ?line true = T2 > T1, - ?line remove_service(Name0), - ?line file:delete(LF), - ?line Name1 = "test_service_8", - ?line Service1 = [{servicename,Name1}, - {workdir, filename:nativename(TempDir)}, - {debugtype,"new"}, - {args, ["-setcookie", - atom_to_list(erlang:get_cookie())]}], - ?line ok = erlsrv:store_service(Service1), - ?line T3 = calendar:datetime_to_gregorian_seconds( - calendar:local_time()), + receive after 2000 -> ok end, + start_service(Name0), + true = wait_for_node(Name0), + LF = filename:join(TempDir, Name0++".debug"), + {ok,Info0} = file:read_file_info(LF), + T2 = calendar:datetime_to_gregorian_seconds( + Info0#file_info.mtime), + true = T2 > T1, + remove_service(Name0), + file:delete(LF), + Name1 = "test_service_8", + Service1 = [{servicename,Name1}, + {workdir, filename:nativename(TempDir)}, + {debugtype,"new"}, + {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}], + ok = erlsrv:store_service(Service1), + T3 = calendar:datetime_to_gregorian_seconds( + calendar:local_time()), %% sleep a little - ?line receive after 2000 -> ok end, - ?line NF = next_logfile(TempDir, Name1), - ?line start_service(Name1), - ?line true = wait_for_node(Name1), - ?line {ok,Info1} = file:read_file_info(NF), - ?line T4 = calendar:datetime_to_gregorian_seconds( - Info1#file_info.mtime), - ?line true = T4 > T3, - ?line remove_service(Name1), - ?line file:delete(NF), + receive after 2000 -> ok end, + NF = next_logfile(TempDir, Name1), + start_service(Name1), + true = wait_for_node(Name1), + {ok,Info1} = file:read_file_info(NF), + T4 = calendar:datetime_to_gregorian_seconds( + Info1#file_info.mtime), + true = T4 > T3, + remove_service(Name1), + file:delete(NF), ok. restart(doc) -> ["Check the restart options to erlsrv"]; restart(suite) -> []; restart(Config) when is_list(Config) -> - ?line Name = "test_service_9", - ?line Service = [{servicename,Name}, - {workdir, filename:nativename(logdir(Config))}, - {onfail,"restart"}, - {args, ["-setcookie", - atom_to_list(erlang:get_cookie())]}], - ?line ok = erlsrv:store_service(Service), - ?line start_service(Name), - ?line true = wait_for_node(Name), - ?line receive after 20000 -> ok end, - ?line rpc:call(make_full_name(Name),erlang,halt,[]), - ?line receive after 1000 -> ok end, - ?line true = wait_for_node(Name), - ?line rpc:call(make_full_name(Name),erlang,halt,[]), - ?line receive after 1000 -> ok end, - ?line false = wait_for_node(Name), - ?line remove_service(Name), + Name = "test_service_9", + Service = [{servicename,Name}, + {workdir, filename:nativename(logdir(Config))}, + {onfail,"restart"}, + {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}], + ok = erlsrv:store_service(Service), + start_service(Name), + true = wait_for_node(Name), + receive after 20000 -> ok end, + rpc:call(make_full_name(Name),erlang,halt,[]), + receive after 1000 -> ok end, + true = wait_for_node(Name), + rpc:call(make_full_name(Name),erlang,halt,[]), + receive after 1000 -> ok end, + false = wait_for_node(Name), + remove_service(Name), ok. restart_always(doc) -> ["Check the restart options to erlsrv"]; restart_always(suite) -> []; restart_always(Config) when is_list(Config) -> - ?line Name = "test_service_10", - ?line Service = [{servicename,Name}, - {workdir, filename:nativename(logdir(Config))}, - {onfail,"restart_always"}, - {args, ["-setcookie", - atom_to_list(erlang:get_cookie())]}], - ?line ok = erlsrv:store_service(Service), - ?line start_service(Name), - ?line true = wait_for_node(Name), - ?line rpc:call(make_full_name(Name),erlang,halt,[]), - ?line receive after 1000 -> ok end, - ?line true = wait_for_node(Name), - ?line rpc:call(make_full_name(Name),erlang,halt,[]), - ?line receive after 1000 -> ok end, - ?line true = wait_for_node(Name), - ?line remove_service(Name), + Name = "test_service_10", + Service = [{servicename,Name}, + {workdir, filename:nativename(logdir(Config))}, + {onfail,"restart_always"}, + {args, ["-setcookie", atom_to_list(erlang:get_cookie())]}], + ok = erlsrv:store_service(Service), + start_service(Name), + true = wait_for_node(Name), + rpc:call(make_full_name(Name),erlang,halt,[]), + receive after 1000 -> ok end, + true = wait_for_node(Name), + rpc:call(make_full_name(Name),erlang,halt,[]), + receive after 1000 -> ok end, + true = wait_for_node(Name), + remove_service(Name), ok. stopaction(doc) -> ["Check that stopaction does not hang output while shutting down"]; stopaction(suite) -> []; stopaction(Config) when is_list(Config) -> - ?line Name = "test_service_11", + Name = "test_service_11", %% Icky, I prepend the first element in the codepath, cause %% I "suppose" it's the one to where I am. - ?line Service = [{servicename,Name}, - {stopaction,atom_to_list(?MODULE) ++ ":shutdown_io()."}, - {args, ["-setcookie", - atom_to_list(erlang:get_cookie()), - "-pa", hd(code:get_path())]}], - ?line ok = erlsrv:store_service(Service), - ?line start_service(Name), - ?line true = wait_for_node(Name), - ?line T1 = calendar:datetime_to_gregorian_seconds( - calendar:universal_time()), - ?line stop_service(Name), - ?line Diff1 = calendar:datetime_to_gregorian_seconds( - calendar:universal_time()) - T1, - ?line true = Diff1 < 30, - ?line remove_service(Name), + Service = [{servicename,Name}, + {stopaction,atom_to_list(?MODULE) ++ ":shutdown_io()."}, + {args, ["-setcookie", atom_to_list(erlang:get_cookie()), + "-pa", hd(code:get_path())]}], + ok = erlsrv:store_service(Service), + start_service(Name), + true = wait_for_node(Name), + T1 = calendar:datetime_to_gregorian_seconds( + calendar:universal_time()), + stop_service(Name), + Diff1 = calendar:datetime_to_gregorian_seconds( + calendar:universal_time()) - T1, + true = Diff1 < 30, + remove_service(Name), ok. @@ -417,31 +406,31 @@ nt(suite) -> []; nt(Config) when is_list(Config) -> case os:type() of - {win32,nt} -> - nt_run(); - _ -> - {skipped, "This test case is intended for Win NT only."} + {win32,nt} -> + nt_run(); + _ -> + {skipped, "This test case is intended for Win NT only."} end. nt_run() -> - ?line start_all(), - ?line create_service("test_service_1"), - ?line R = start_look_for_single("System","ErlSrv","Informational", - ".*test_service_1.*started.*"), - ?line start_service("test_service_1"), - ?line Res = look_for_single(R), - ?line io:format("Result from eventlog: ~p~n", - [Res]), - ?line remove_service("test_service_1"), - ?line stop_all(), + start_all(), + create_service("test_service_1"), + R = start_look_for_single("System","ErlSrv","Informational", + ".*test_service_1.*started.*"), + start_service("test_service_1"), + Res = look_for_single(R), + io:format("Result from eventlog: ~p~n", + [Res]), + remove_service("test_service_1"), + stop_all(), ok. start_all() -> Pid1 = spawn_link(?MODULE,middleman,[[]]), register(?MODULE,Pid1), _Pid2 = nteventlog:start("log_testing", - {?MODULE,handle_eventlog,[Pid1]}). + {?MODULE,handle_eventlog,[Pid1]}). stop_all() -> ?MODULE ! stop, @@ -454,10 +443,10 @@ start_look_for_single(Cat,Fac,Sev,MessRE) -> look_for_single(Ref) -> receive - {Ref,Time,Mes} -> - {Time,Mes} + {Ref,Time,Mes} -> + {Time,Mes} after 60000 -> - timeout + timeout end. @@ -468,25 +457,25 @@ handle_eventlog(Mes,Pid) -> %%% Waitfor = [{Pid, Ref, {Category,Facility,Severity,MessageRE}} ...] middleman(Waitfor) -> receive - {Time,Category,Facility,Severity,Message} -> - io:format("Middleman got ~s...", [Message]), - case match_event({Time,Category,Facility,Severity,Message}, - Waitfor) of - {ok, {Pid,Ref,Time,Mes}, Rest} -> - io:format("matched~n"), - Pid ! {Ref,Time,Mes}, - middleman(Rest); - _ -> - io:format("no match~n"), - middleman(Waitfor) - end; - {lookfor, X} -> - io:format("Middleman told to look for ~p~n", [X]), - middleman([X|Waitfor]); - stop -> - stopped; - _ -> - middleman(Waitfor) + {Time,Category,Facility,Severity,Message} -> + io:format("Middleman got ~s...", [Message]), + case match_event({Time,Category,Facility,Severity,Message}, + Waitfor) of + {ok, {Pid,Ref,Time,Mes}, Rest} -> + io:format("matched~n"), + Pid ! {Ref,Time,Mes}, + middleman(Rest); + _ -> + io:format("no match~n"), + middleman(Waitfor) + end; + {lookfor, X} -> + io:format("Middleman told to look for ~p~n", [X]), + middleman([X|Waitfor]); + stop -> + stopped; + _ -> + middleman(Waitfor) end. @@ -495,81 +484,81 @@ match_event(_X, []) -> nomatch; match_event({Time,Cat,Fac,Sev,Mes},[{Pid,Ref,{Cat,Fac,Sev,MesRE}} | Tail]) -> case re:run(Mes,MesRE,[{capture,none}]) of - match -> - %%io:format("Match!~n"), - {ok,{Pid,Ref,Time,Mes},Tail}; - nomatch -> - %%io:format("No match~n"), - case match_event({Time,Cat,Fac,Sev,Mes},Tail) of - {ok,X,Rest} -> - {ok,X,[{Pid,Ref,{Cat,Fac,Sev,MesRE}} | Rest]}; - X -> - X - end + match -> + %%io:format("Match!~n"), + {ok,{Pid,Ref,Time,Mes},Tail}; + nomatch -> + %%io:format("No match~n"), + case match_event({Time,Cat,Fac,Sev,Mes},Tail) of + {ok,X,Rest} -> + {ok,X,[{Pid,Ref,{Cat,Fac,Sev,MesRE}} | Rest]}; + X -> + X + end end; match_event(X,[Y | T]) -> %%io:format("X == ~p, Y == ~p~n",[X,Y]), case match_event(X,T) of - {ok,Z,R} -> - {ok,Z,[Y|R]}; - XX -> - XX + {ok,Z,R} -> + {ok,Z,[Y|R]}; + XX -> + XX end. arrived_procs(_,[]) -> []; arrived_procs(OldProcs,[{Executable, Pid, Priority} | TNewProcs]) -> case lists:keysearch(Pid,2,OldProcs) of - {value, _} -> - arrived_procs(OldProcs, TNewProcs); - false -> - [{Executable, Pid, Priority} | arrived_procs(OldProcs, TNewProcs)] + {value, _} -> + arrived_procs(OldProcs, TNewProcs); + false -> + [{Executable, Pid, Priority} | arrived_procs(OldProcs, TNewProcs)] end. - + get_current_procs(Config) -> - ?line P = open_port({spawn,nt_info(Config) ++ " -E"}, - [{line,10000}]), - ?line L = receive - {P,{data,{eol,D}}} -> - D; - _ -> "error. " - end, - ?line P ! {self(), close}, - ?line receive - {P, closed} -> ok - end, - ?line {done,{ok,Tok,_},_} = erl_scan:tokens([],L,0), - ?line erl_parse:parse_term(Tok). + P = open_port({spawn,nt_info(Config) ++ " -E"}, + [{line,10000}]), + L = receive + {P,{data,{eol,D}}} -> + D; + _ -> "error. " + end, + P ! {self(), close}, + receive + {P, closed} -> ok + end, + {done,{ok,Tok,_},_} = erl_scan:tokens([],L,0), + erl_parse:parse_term(Tok). nt_info(Config) when is_list(Config) -> - ?line "\"" ++ filename:join(?config(data_dir, Config), "nt_info") ++ "\"". + "\"" ++ filename:join(?config(data_dir, Config), "nt_info") ++ "\"". logdir(Config) -> - ?line ?config(priv_dir, Config). + ?config(priv_dir, Config). look_for_next(Template,L,N) -> - ?line FN = Template ++ integer_to_list(N), - ?line case lists:member(FN,L) of - true -> - ?line look_for_next(Template,L,N+1); - false -> - ?line FN + FN = Template ++ integer_to_list(N), + case lists:member(FN,L) of + true -> + look_for_next(Template,L,N+1); + false -> + FN end. next_logfile(LD, Servicename) -> - ?line {ok, Files} = file:list_dir(LD), - ?line Ftmpl = Servicename ++ ".debug.", - ?line filename:join(LD,look_for_next(Ftmpl,Files,1)). + {ok, Files} = file:list_dir(LD), + Ftmpl = Servicename ++ ".debug.", + filename:join(LD,look_for_next(Ftmpl,Files,1)). %%% Functions run by the service do_shutdown_io() -> receive after 2000 -> - io:format("IO in shutting down...~n"), - erlang:halt() + io:format("IO in shutting down...~n"), + erlang:halt() end. shutdown_io() -> diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 2efb5ae200..11f70e8465 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -21,11 +21,11 @@ -module(otp_SUITE). -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, - init_per_suite/1,end_per_suite/1]). + init_per_suite/1,end_per_suite/1]). -export([undefined_functions/1,deprecated_not_in_obsolete/1, - obsolete_but_not_deprecated/1,call_to_deprecated/1, + obsolete_but_not_deprecated/1,call_to_deprecated/1, call_to_size_1/1,call_to_now_0/1,strong_components/1, - erl_file_encoding/1,xml_file_encoding/1,runtime_dependencies/1]). + erl_file_encoding/1,xml_file_encoding/1,runtime_dependencies/1]). -include_lib("common_test/include/ct.hrl"). @@ -54,23 +54,23 @@ init_per_suite(Config) -> Dog = test_server:timetrap(?t:minutes(10)), Root = code:root_dir(), Server = daily_xref, - ?line xref:start(Server), - ?line xref:set_default(Server, [{verbose,false}, - {warnings,false}, - {builtins,true}]), - ?line {ok,_Relname} = xref:add_release(Server, Root, {name,otp}), + xref:start(Server), + xref:set_default(Server, [{verbose,false}, + {warnings,false}, + {builtins,true}]), + {ok,_Relname} = xref:add_release(Server, Root, {name,otp}), %% If we are running the tests in the source tree, the ERTS application %% is not in the code path. We must add it explicitly. case code:lib_dir(erts) of - {error,bad_name} -> - Erts = filename:join([code:root_dir(),"erts","preloaded","ebin"]), - ?line {ok,_} = xref:add_directory(Server, Erts, []); - _ -> - ok + {error,bad_name} -> + Erts = filename:join([code:root_dir(),"erts","preloaded","ebin"]), + {ok,_} = xref:add_directory(Server, Erts, []); + _ -> + ok end, - - ?line ?t:timetrap_cancel(Dog), + + ?t:timetrap_cancel(Dog), [{xref_server,Server}|Config]. end_per_suite(Config) -> @@ -83,11 +83,11 @@ undefined_functions(Config) when is_list(Config) -> %% Exclude calls from generated modules in the SSL application. ExcludeFrom = "SSL-PKIX|PKIX.*|ssl_pkix_oid", - ?line UndefS = xref_base:analysis(undefined_function_calls), - ?line Q = io_lib:format("Undef = ~s," - "ExcludedFrom = ~p:_/_," - "Undef - Undef | ExcludedFrom", - [UndefS,ExcludeFrom]), + UndefS = xref_base:analysis(undefined_function_calls), + Q = io_lib:format("Undef = ~s," + "ExcludedFrom = ~p:_/_," + "Undef - Undef | ExcludedFrom", + [UndefS,ExcludeFrom]), {ok,Undef0} = xref:q(Server, lists:flatten(Q)), Undef1 = hipe_filter(Undef0), Undef2 = ssl_crypto_filter(Undef1), @@ -99,124 +99,124 @@ undefined_functions(Config) when is_list(Config) -> Undef = diameter_filter(Undef7), case Undef of - [] -> ok; - _ -> - Fd = open_log(Config, "undefined_functions"), - foreach(fun ({MFA1,MFA2}) -> - io:format("~s calls undefined ~s", - [format_mfa(Server, MFA1), - format_mfa(MFA2)]), - io:format(Fd, "~s ~s\n", - [format_mfa(Server, MFA1), - format_mfa(MFA2)]) - end, Undef), - close_log(Fd), - ?line ?t:fail({length(Undef),undefined_functions_in_otp}) + [] -> ok; + _ -> + Fd = open_log(Config, "undefined_functions"), + foreach(fun ({MFA1,MFA2}) -> + io:format("~s calls undefined ~s", + [format_mfa(Server, MFA1), + format_mfa(MFA2)]), + io:format(Fd, "~s ~s\n", + [format_mfa(Server, MFA1), + format_mfa(MFA2)]) + end, Undef), + close_log(Fd), + ?t:fail({length(Undef),undefined_functions_in_otp}) end. hipe_filter(Undef) -> case erlang:system_info(hipe_architecture) of - undefined -> - filter(fun ({_,{hipe_bifs,_,_}}) -> false; - ({_,{hipe,_,_}}) -> false; - ({_,{hipe_consttab,_,_}}) -> false; - ({_,{hipe_converters,_,_}}) -> false; - ({{code,_,_},{Mod,_,_}}) -> - not is_hipe_module(Mod); - ({{code_server,_,_},{Mod,_,_}}) -> - not is_hipe_module(Mod); - ({{compile,_,_},{Mod,_,_}}) -> - not is_hipe_module(Mod); - ({{hipe,_,_},{Mod,_,_}}) -> - %% See comment for the next clause. - not is_hipe_module(Mod); - ({{cerl_to_icode,translate_flags1,2}, - {hipe_rtl_arch,endianess,0}}) -> - false; - ({{Caller,_,_},{Callee,_,_}}) -> - %% Part of the hipe application is here - %% for the sake of Dialyzer. There are many - %% undefined calls within the hipe application. - not is_hipe_module(Caller) orelse - not is_hipe_module(Callee); - (_) -> true - end, Undef); - _Arch -> - filter(fun ({{Mod,_,_},{hipe_bifs,write_u64,2}}) -> - %% Unavailable except in 64 bit AMD. Ignore it. - not is_hipe_module(Mod); - (_) -> true - end, Undef) + undefined -> + filter(fun ({_,{hipe_bifs,_,_}}) -> false; + ({_,{hipe,_,_}}) -> false; + ({_,{hipe_consttab,_,_}}) -> false; + ({_,{hipe_converters,_,_}}) -> false; + ({{code,_,_},{Mod,_,_}}) -> + not is_hipe_module(Mod); + ({{code_server,_,_},{Mod,_,_}}) -> + not is_hipe_module(Mod); + ({{compile,_,_},{Mod,_,_}}) -> + not is_hipe_module(Mod); + ({{hipe,_,_},{Mod,_,_}}) -> + %% See comment for the next clause. + not is_hipe_module(Mod); + ({{cerl_to_icode,translate_flags1,2}, + {hipe_rtl_arch,endianess,0}}) -> + false; + ({{Caller,_,_},{Callee,_,_}}) -> + %% Part of the hipe application is here + %% for the sake of Dialyzer. There are many + %% undefined calls within the hipe application. + not is_hipe_module(Caller) orelse + not is_hipe_module(Callee); + (_) -> true + end, Undef); + _Arch -> + filter(fun ({{Mod,_,_},{hipe_bifs,write_u64,2}}) -> + %% Unavailable except in 64 bit AMD. Ignore it. + not is_hipe_module(Mod); + (_) -> true + end, Undef) end. is_hipe_module(Mod) -> case atom_to_list(Mod) of - "hipe_"++_ -> true; - _ -> false + "hipe_"++_ -> true; + _ -> false end. ssl_crypto_filter(Undef) -> case {app_exists(crypto),app_exists(ssl)} of - {false,false} -> - filter(fun({_,{ssl,_,_}}) -> false; - ({_,{crypto,_,_}}) -> false; - ({_,{ssh,_,_}}) -> false; - ({_,{ssh_connection,_,_}}) -> false; - ({_,{ssh_sftp,_,_}}) -> false; - (_) -> true - end, Undef); - {_,_} -> Undef + {false,false} -> + filter(fun({_,{ssl,_,_}}) -> false; + ({_,{crypto,_,_}}) -> false; + ({_,{ssh,_,_}}) -> false; + ({_,{ssh_connection,_,_}}) -> false; + ({_,{ssh_sftp,_,_}}) -> false; + (_) -> true + end, Undef); + {_,_} -> Undef end. edoc_filter(Undef) -> %% Filter away function call that is catched. filter(fun({{edoc_lib,uri_get_http,1},{http,request_sync,2}}) -> false; - (_) -> true - end, Undef). + (_) -> true + end, Undef). eunit_filter(Undef) -> filter(fun({{eunit_test,wrapper_test_exported_,0}, - {eunit_test,nonexisting_function,0}}) -> false; - (_) -> true - end, Undef). + {eunit_test,nonexisting_function,0}}) -> false; + (_) -> true + end, Undef). dialyzer_filter(Undef) -> case app_exists(dialyzer) of - false -> - filter(fun({_,{dialyzer_callgraph,_,_}}) -> false; - ({_,{dialyzer_codeserver,_,_}}) -> false; - ({_,{dialyzer_contracts,_,_}}) -> false; - ({_,{dialyzer_cl_parse,_,_}}) -> false; - ({_,{dialyzer_timing,_,_}}) -> false; - ({_,{dialyzer_plt,_,_}}) -> false; - ({_,{dialyzer_succ_typings,_,_}}) -> false; - ({_,{dialyzer_utils,_,_}}) -> false; - (_) -> true - end, Undef); - _ -> Undef + false -> + filter(fun({_,{dialyzer_callgraph,_,_}}) -> false; + ({_,{dialyzer_codeserver,_,_}}) -> false; + ({_,{dialyzer_contracts,_,_}}) -> false; + ({_,{dialyzer_cl_parse,_,_}}) -> false; + ({_,{dialyzer_timing,_,_}}) -> false; + ({_,{dialyzer_plt,_,_}}) -> false; + ({_,{dialyzer_succ_typings,_,_}}) -> false; + ({_,{dialyzer_utils,_,_}}) -> false; + (_) -> true + end, Undef); + _ -> Undef end. wx_filter(Undef) -> case app_exists(wx) of - false -> - filter(fun({_,{MaybeWxModule,_,_}}) -> - case atom_to_list(MaybeWxModule) of - "wx"++_ -> false; - _ -> true - end - end, Undef); - _ -> Undef + false -> + filter(fun({_,{MaybeWxModule,_,_}}) -> + case atom_to_list(MaybeWxModule) of + "wx"++_ -> false; + _ -> true + end + end, Undef); + _ -> Undef end. - + gs_filter(Undef) -> case code:lib_dir(gs) of - {error,bad_name} -> - filter(fun({_,{gs,_,_}}) -> false; - ({_,{gse,_,_}}) -> false; + {error,bad_name} -> + filter(fun({_,{gs,_,_}}) -> false; + ({_,{gse,_,_}}) -> false; ({_,{tool_utils,_,_}}) -> false; - (_) -> true - end, Undef); - _ -> Undef + (_) -> true + end, Undef); + _ -> Undef end. diameter_filter(Undef) -> @@ -229,76 +229,76 @@ diameter_filter(Undef) -> false; ({{diameter_lib,_,_},{erlang,time_offset,0}}) -> false; - (_) -> true - end, Undef). + (_) -> true + end, Undef). deprecated_not_in_obsolete(Config) when is_list(Config) -> - ?line Server = ?config(xref_server, Config), - ?line {ok,DeprecatedFunctions} = xref:q(Server, "DF"), - - ?line L = foldl(fun({M,F,A}=MFA, Acc) -> - case otp_internal:obsolete(M, F, A) of - no -> [MFA|Acc]; - _ -> Acc - end - end, [], DeprecatedFunctions), + Server = ?config(xref_server, Config), + {ok,DeprecatedFunctions} = xref:q(Server, "DF"), + + L = foldl(fun({M,F,A}=MFA, Acc) -> + case otp_internal:obsolete(M, F, A) of + no -> [MFA|Acc]; + _ -> Acc + end + end, [], DeprecatedFunctions), case L of - [] -> ok; - _ -> - io:put_chars("The following functions have -deprecated() attributes,\n" - "but are not listed in otp_internal:obsolete/3.\n"), - print_mfas(group_leader(), Server, L), - Fd = open_log(Config, "deprecated_not_obsolete"), - print_mfas(Fd, Server, L), - close_log(Fd), - ?line ?t:fail({length(L),deprecated_but_not_obsolete}) + [] -> ok; + _ -> + io:put_chars("The following functions have -deprecated() attributes,\n" + "but are not listed in otp_internal:obsolete/3.\n"), + print_mfas(group_leader(), Server, L), + Fd = open_log(Config, "deprecated_not_obsolete"), + print_mfas(Fd, Server, L), + close_log(Fd), + ?t:fail({length(L),deprecated_but_not_obsolete}) end. obsolete_but_not_deprecated(Config) when is_list(Config) -> - ?line Server = ?config(xref_server, Config), - ?line {ok,NotDeprecated} = xref:q(Server, "X - DF"), + Server = ?config(xref_server, Config), + {ok,NotDeprecated} = xref:q(Server, "X - DF"), - ?line L = foldl(fun({M,F,A}=MFA, Acc) -> - case otp_internal:obsolete(M, F, A) of - no -> Acc; - _ -> [MFA|Acc] - end - end, [], NotDeprecated), + L = foldl(fun({M,F,A}=MFA, Acc) -> + case otp_internal:obsolete(M, F, A) of + no -> Acc; + _ -> [MFA|Acc] + end + end, [], NotDeprecated), case L of - [] -> ok; - _ -> - io:put_chars("The following functions are listed " - "in otp_internal:obsolete/3,\n" - "but don't have -deprecated() attributes.\n"), - print_mfas(group_leader(), Server, L), - Fd = open_log(Config, "obsolete_not_deprecated"), - print_mfas(Fd, Server, L), - close_log(Fd), - ?line ?t:fail({length(L),obsolete_but_not_deprecated}) + [] -> ok; + _ -> + io:put_chars("The following functions are listed " + "in otp_internal:obsolete/3,\n" + "but don't have -deprecated() attributes.\n"), + print_mfas(group_leader(), Server, L), + Fd = open_log(Config, "obsolete_not_deprecated"), + print_mfas(Fd, Server, L), + close_log(Fd), + ?t:fail({length(L),obsolete_but_not_deprecated}) end. - + call_to_deprecated(Config) when is_list(Config) -> Server = ?config(xref_server, Config), - ?line {ok,DeprecatedCalls} = xref:q(Server, "strict(E || DF)"), + {ok,DeprecatedCalls} = xref:q(Server, "strict(E || DF)"), foreach(fun ({MFA1,MFA2}) -> - io:format("~s calls deprecated ~s", - [format_mfa(MFA1),format_mfa(MFA2)]) - end, DeprecatedCalls), + io:format("~s calls deprecated ~s", + [format_mfa(MFA1),format_mfa(MFA2)]) + end, DeprecatedCalls), {comment,integer_to_list(length(DeprecatedCalls))++" calls to deprecated functions"}. call_to_size_1(Config) when is_list(Config) -> %% Applications that do not call erlang:size/1: Apps = [asn1,compiler,debugger,kernel,observer,parsetools, - runtime_tools,stdlib,tools], + runtime_tools,stdlib,tools], not_recommended_calls(Config, Apps, {erlang,size,1}). call_to_now_0(Config) when is_list(Config) -> %% Applications that do not call erlang:now/1: Apps = [asn1,common_test,compiler,debugger,dialyzer, - gs,kernel,mnesia,observer,parsetools,reltool, - runtime_tools,sasl,stdlib,syntax_tools, - tools], + gs,kernel,mnesia,observer,parsetools,reltool, + runtime_tools,sasl,stdlib,syntax_tools, + tools], not_recommended_calls(Config, Apps, {erlang,now,0}). not_recommended_calls(Config, Apps0, MFA) -> @@ -315,14 +315,14 @@ not_recommended_calls(Config, Apps0, MFA) -> {ok,CallsToMFA} = xref:q(Server, lists:flatten(Q2)), case CallsToMFA of - [] -> + [] -> ok; - _ -> + _ -> io:format("These calls are not allowed:\n"), - foreach(fun ({MFA1,MFA2}) -> - io:format("~s calls non-recommended ~s", - [format_mfa(MFA1),format_mfa(MFA2)]) - end, CallsToMFA) + foreach(fun ({MFA1,MFA2}) -> + io:format("~s calls non-recommended ~s", + [format_mfa(MFA1),format_mfa(MFA2)]) + end, CallsToMFA) end, %% Enumerate calls to MFA from other applications than @@ -338,7 +338,7 @@ not_recommended_calls(Config, Apps0, MFA) -> end, Calls) end, case CallsToMFA of - [] -> + [] -> SkippedApps = ordsets:subtract(ordsets:from_list(Apps0), ordsets:from_list(Apps)), case SkippedApps of @@ -350,8 +350,8 @@ not_recommended_calls(Config, Apps0, MFA) -> [string:join(AppStrings, ", ")]), {comment, Mess} end; - _ -> - ?t:fail({length(CallsToMFA),calls_to_size_1}) + _ -> + ?t:fail({length(CallsToMFA),calls_to_size_1}) end. is_present_application(Name, Server) -> @@ -363,7 +363,7 @@ is_present_application(Name, Server) -> strong_components(Config) when is_list(Config) -> Server = ?config(xref_server, Config), - ?line {ok,Cs} = xref:q(Server, "components AE"), + {ok,Cs} = xref:q(Server, "components AE"), io:format("\n\nStrong components:\n\n~p\n", [Cs]), ok. @@ -371,41 +371,41 @@ erl_file_encoding(_Config) -> Root = code:root_dir(), Wc = filename:join([Root,"**","*.erl"]), ErlFiles = ordsets:subtract(ordsets:from_list(filelib:wildcard(Wc)), - release_files(Root, "*.erl")), + release_files(Root, "*.erl")), {ok, MP} = re:compile(".*lib/(ic)|(orber)|(cos).*", [unicode]), Fs = [F || F <- ErlFiles, - filter_use_latin1_coding(F, MP), - case epp:read_encoding(F) of - none -> false; - _ -> true - end], + filter_use_latin1_coding(F, MP), + case epp:read_encoding(F) of + none -> false; + _ -> true + end], case Fs of - [] -> - ok; - [_|_] -> - io:put_chars("Files with \"coding:\":\n"), - [io:put_chars(F) || F <- Fs], - ?t:fail() + [] -> + ok; + [_|_] -> + io:put_chars("Files with \"coding:\":\n"), + [io:put_chars(F) || F <- Fs], + ?t:fail() end. filter_use_latin1_coding(F, MP) -> case re:run(F, MP) of - nomatch -> - true; + nomatch -> + true; {match, _} -> - false + false end. xml_file_encoding(_Config) -> XmlFiles = xml_files(), Fs = [F || F <- XmlFiles, is_bad_encoding(F)], case Fs of - [] -> - ok; - [_|_] -> - io:put_chars("Encoding should be \"utf-8\" or \"UTF-8\":\n"), - [io:put_chars(F) || F <- Fs], - ?t:fail() + [] -> + ok; + [_|_] -> + io:put_chars("Encoding should be \"utf-8\" or \"UTF-8\":\n"), + [io:put_chars(F) || F <- Fs], + ?t:fail() end. xml_files() -> @@ -417,7 +417,7 @@ xml_files() -> XmerlWc = filename:join([Root,"lib","xmerl","**","*.xml"]), XmerlXmlFiles = ordsets:from_list(filelib:wildcard(XmerlWc)), Ignore = ordsets:union([TestXmlFiles,XmerlXmlFiles, - release_files(Root, "*.xml")]), + release_files(Root, "*.xml")]), ordsets:subtract(AllXmlFiles, Ignore). release_files(Root, Ext) -> @@ -427,12 +427,12 @@ release_files(Root, Ext) -> is_bad_encoding(File) -> {ok,Bin} = file:read_file(File), case Bin of - <<"> -> - false; - <<"> -> - false; - _ -> - true + <<"> -> + false; + <<"> -> + false; + _ -> + true end. runtime_dependencies(Config) -> @@ -449,26 +449,26 @@ runtime_dependencies(Config) -> SAE = lists:keysort(1, AE), put(ignored_failures, []), {AppDep, AppDeps} = lists:foldl(fun ({App, App}, Acc) -> - Acc; - ({App, Dep}, {undefined, []}) -> - {{App, [Dep]}, []}; - ({App, Dep}, {{App, Deps}, AppDeps}) -> - {{App, [Dep|Deps]}, AppDeps}; - ({App, Dep}, {AppDep, AppDeps}) -> - {{App, [Dep]}, [AppDep | AppDeps]} - end, - {undefined, []}, - SAE), + Acc; + ({App, Dep}, {undefined, []}) -> + {{App, [Dep]}, []}; + ({App, Dep}, {{App, Deps}, AppDeps}) -> + {{App, [Dep|Deps]}, AppDeps}; + ({App, Dep}, {AppDep, AppDeps}) -> + {{App, [Dep]}, [AppDep | AppDeps]} + end, + {undefined, []}, + SAE), check_apps_deps([AppDep|AppDeps], IgnoreApps), case IgnoreApps of - [] -> - ok; - _ -> - Comment = lists:flatten(io_lib:format("Ignored applications: ~p " - "Ignored failures: ~p", - [IgnoreApps, - get(ignored_failures)])), - {comment, Comment} + [] -> + ok; + _ -> + Comment = lists:flatten(io_lib:format("Ignored applications: ~p " + "Ignored failures: ~p", + [IgnoreApps, + get(ignored_failures)])), + {comment, Comment} end. have_rdep(_App, [], _Dep) -> @@ -476,11 +476,11 @@ have_rdep(_App, [], _Dep) -> have_rdep(App, [RDep | RDeps], Dep) -> [AppStr, _VsnStr] = string:tokens(RDep, "-"), case Dep == list_to_atom(AppStr) of - true -> - io:format("~p -> ~s~n", [App, RDep]), - true; - false -> - have_rdep(App, RDeps, Dep) + true -> + io:format("~p -> ~s~n", [App, RDep]), + true; + false -> + have_rdep(App, RDeps, Dep) end. check_app_deps(_App, _AppFile, _AFDeps, [], _IgnoreApps) -> @@ -488,17 +488,17 @@ check_app_deps(_App, _AppFile, _AFDeps, [], _IgnoreApps) -> check_app_deps(App, AppFile, AFDeps, [XRDep | XRDeps], IgnoreApps) -> ResOtherDeps = check_app_deps(App, AppFile, AFDeps, XRDeps, IgnoreApps), case have_rdep(App, AFDeps, XRDep) of - true -> - ResOtherDeps; - false -> - Failure = {missing_runtime_dependency, AppFile, XRDep}, - case lists:member(App, IgnoreApps) of - true -> - put(ignored_failures, [Failure | get(ignored_failures)]), - ResOtherDeps; - false -> - [Failure | ResOtherDeps] - end + true -> + ResOtherDeps; + false -> + Failure = {missing_runtime_dependency, AppFile, XRDep}, + case lists:member(App, IgnoreApps) of + true -> + put(ignored_failures, [Failure | get(ignored_failures)]), + ResOtherDeps; + false -> + [Failure | ResOtherDeps] + end end. check_apps_deps([], _IgnoreApps) -> @@ -508,24 +508,24 @@ check_apps_deps([{App, Deps}|AppDeps], IgnoreApps) -> AppFile = code:where_is_file(atom_to_list(App) ++ ".app"), {ok,[{application, App, Info}]} = file:consult(AppFile), case lists:keyfind(runtime_dependencies, 1, Info) of - {runtime_dependencies, RDeps} -> - check_app_deps(App, AppFile, RDeps, Deps, IgnoreApps) - ++ ResOtherApps; - false -> - Failure = {missing_runtime_dependencies_key, AppFile}, - case lists:member(App, IgnoreApps) of - true -> - put(ignored_failures, [Failure | get(ignored_failures)]), - ResOtherApps; - false -> - [Failure | ResOtherApps] - end + {runtime_dependencies, RDeps} -> + check_app_deps(App, AppFile, RDeps, Deps, IgnoreApps) + ++ ResOtherApps; + false -> + Failure = {missing_runtime_dependencies_key, AppFile}, + case lists:member(App, IgnoreApps) of + true -> + put(ignored_failures, [Failure | get(ignored_failures)]), + ResOtherApps; + false -> + [Failure | ResOtherApps] + end end. %%% %%% Common help functions. %%% - + print_mfas(Fd, Server, MFAs) -> [io:format(Fd, "~s\n", [format_mfa(Server, MFA)]) || MFA <- MFAs], ok. @@ -537,9 +537,9 @@ format_mfa(Server, MFA) -> MFAString = format_mfa(MFA), AQ = "(App)" ++ MFAString, AppPrefix = case xref:q(Server, AQ) of - {ok,[App]} -> "[" ++ atom_to_list(App) ++ "]"; - _ -> "" - end, + {ok,[App]} -> "[" ++ atom_to_list(App) ++ "]"; + _ -> "" + end, AppPrefix ++ MFAString. open_log(Config, Name) -> @@ -554,13 +554,13 @@ close_log(Fd) -> app_exists(AppAtom) -> case code:lib_dir(AppAtom) of - {error,bad_name} -> - false; - Path -> - case file:read_file_info(filename:join(Path,"ebin")) of - {ok,_} -> - true; - _ -> - false - end + {error,bad_name} -> + false; + Path -> + case file:read_file_info(filename:join(Path,"ebin")) of + {ok,_} -> + true; + _ -> + false + end end. diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl index e3c563d3d9..1602466f05 100644 --- a/erts/test/run_erl_SUITE.erl +++ b/erts/test/run_erl_SUITE.erl @@ -65,16 +65,16 @@ basic(Config) when is_list(Config) -> end. basic_1(Config) -> - ?line {Node,Pipe} = do_run_erl(Config, "basic"), + {Node,Pipe} = do_run_erl(Config, "basic"), - ?line ToErl = open_port({spawn,"to_erl "++Pipe}, []), - ?line erlang:port_command(ToErl, "halt().\r\n"), + ToErl = open_port({spawn,"to_erl "++Pipe}, []), + erlang:port_command(ToErl, "halt().\r\n"), receive {nodedown,Node} -> - ?line io:format("Down: ~p\n", [Node]) + io:format("Down: ~p\n", [Node]) after 10000 -> - ?line ?t:fail() + ?t:fail() end, ok. @@ -86,28 +86,28 @@ heavy(Config) when is_list(Config) -> end. heavy_1(Config) -> - ?line {Node,Pipe} = do_run_erl(Config, "heavy"), + {Node,Pipe} = do_run_erl(Config, "heavy"), - ?line ToErl = open_port({spawn,"to_erl "++Pipe}, []), + ToErl = open_port({spawn,"to_erl "++Pipe}, []), IoFormat = "io:format(\"~s\n\", [lists:duplicate(10000, 10)]).\r\n", - ?line erlang:port_command(ToErl, IoFormat), - ?line erlang:port_command(ToErl, IoFormat), - ?line erlang:port_command(ToErl, IoFormat), - ?line erlang:port_command(ToErl, "init:stop().\r\n"), + erlang:port_command(ToErl, IoFormat), + erlang:port_command(ToErl, IoFormat), + erlang:port_command(ToErl, IoFormat), + erlang:port_command(ToErl, "init:stop().\r\n"), receive {nodedown,Node} -> - ?line io:format("Down: ~p\n", [Node]) + io:format("Down: ~p\n", [Node]) after 10000 -> - ?line ?t:fail() + ?t:fail() end, - ?line case count_new_lines(ToErl, 0) of + case count_new_lines(ToErl, 0) of Nls when Nls > 30000 -> ok; Nls -> - ?line io:format("new_lines: ~p\n", [Nls]), - ?line ?t:fail() + io:format("new_lines: ~p\n", [Nls]), + ?t:fail() end. @@ -137,16 +137,16 @@ heavier(Config) when is_list(Config) -> end. heavier_1(Config) -> - ?line {Node,Pipe} = do_run_erl(Config, "heavier"), + {Node,Pipe} = do_run_erl(Config, "heavier"), - ?line ToErl = open_port({spawn,"to_erl "++Pipe}, []), + ToErl = open_port({spawn,"to_erl "++Pipe}, []), io:format("ToErl = ~p\n", [ToErl]), Seed = {1,555,42}, rand:seed(exsplus, Seed), SeedCmd = lists:flatten(io_lib:format("rand:seed(exsplus, ~p). \r\n", [Seed])), - ?line io:format("~p\n", [SeedCmd]), - ?line erlang:port_command(ToErl, SeedCmd), + io:format("~p\n", [SeedCmd]), + erlang:port_command(ToErl, SeedCmd), Iter = 1000, MaxLen = 2048, @@ -165,19 +165,19 @@ heavier_1(Config) -> "F(F,"++integer_to_list(Iter)++")."++" \r\n", - ?line io:format("~p\n", [Random]), - ?line erlang:port_command(ToErl, Random), + io:format("~p\n", [Random]), + erlang:port_command(ToErl, Random), %% Finish. - ?line erlang:port_command(ToErl, "init:stop().\r\n"), - ?line receive_all(Iter, ToErl, MaxLen), + erlang:port_command(ToErl, "init:stop().\r\n"), + receive_all(Iter, ToErl, MaxLen), receive {nodedown,Node} -> - ?line io:format("Down: ~p\n", [Node]) + io:format("Down: ~p\n", [Node]) after 10000 -> - ?line c:flush(), - ?line ?t:fail() + c:flush(), + ?t:fail() end, ok. @@ -206,7 +206,7 @@ receive_all_2(Iter, {NumChars,Pattern}, Line0, ToErl, MaxLen) -> after 10000 -> io:format("Timeout waiting for\n~p\ngot\n~p\n", [Pattern, Line]), - ?line ?t:fail() + ?t:fail() end end. @@ -241,49 +241,48 @@ defunct_1(Config) -> end. defunct_2(Config, Perl) -> - ?line Data = ?config(data_dir, Config), - ?line RunErlTest = filename:join(Data, "run_erl_test.pl"), - ?line Defuncter = filename:join(Data, "defuncter.pl"), - ?line Priv = ?config(priv_dir, Config), - ?line LogDir = filename:join(Priv, "defunct"), - ?line ok = file:make_dir(LogDir), - ?line Pipe = LogDir ++ "/", - ?line RunErl = os:find_executable(run_erl), - ?line Cmd = Perl ++ " " ++ RunErlTest ++ " \"" ++ RunErl ++ "\" " ++ + Data = ?config(data_dir, Config), + RunErlTest = filename:join(Data, "run_erl_test.pl"), + Defuncter = filename:join(Data, "defuncter.pl"), + Priv = ?config(priv_dir, Config), + LogDir = filename:join(Priv, "defunct"), + ok = file:make_dir(LogDir), + Pipe = LogDir ++ "/", + RunErl = os:find_executable(run_erl), + Cmd = Perl ++ " " ++ RunErlTest ++ " \"" ++ RunErl ++ "\" " ++ Defuncter ++ " " ++ Pipe ++ " " ++ LogDir, - ?line io:format("~p", [Cmd]), - ?line Res = os:cmd(Cmd), - ?line io:format("~p\n", [Res]), + io:format("~p", [Cmd]), + Res = os:cmd(Cmd), + io:format("~p\n", [Res]), "OK"++_ = Res, ok. %%% Utilities. do_run_erl(Config, Case) -> - ?line Priv = ?config(priv_dir, Config), - ?line LogDir = filename:join(Priv, Case), - ?line ok = file:make_dir(LogDir), - ?line Pipe = LogDir ++ "/", - ?line NodeName = "run_erl_node_" ++ Case, - ?line Cmd = "run_erl "++Pipe++" "++LogDir++" \"erl -sname " ++ NodeName ++ + Priv = ?config(priv_dir, Config), + LogDir = filename:join(Priv, Case), + ok = file:make_dir(LogDir), + Pipe = LogDir ++ "/", + NodeName = "run_erl_node_" ++ Case, + Cmd = "run_erl "++Pipe++" "++LogDir++" \"erl -sname " ++ NodeName ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ " -s " ++ ?MODULE_STRING ++ " ping_me_back " ++ atom_to_list(node()) ++ "\"", - ?line io:format("~p\n", [Cmd]), + io:format("~p\n", [Cmd]), - ?line net_kernel:monitor_nodes(true), - ?line open_port({spawn,Cmd}, []), - ?line [_,Host] = string:tokens(atom_to_list(node()), "@"), - ?line Node = list_to_atom(NodeName++"@"++Host), + net_kernel:monitor_nodes(true), + open_port({spawn,Cmd}, []), + [_,Host] = string:tokens(atom_to_list(node()), "@"), + Node = list_to_atom(NodeName++"@"++Host), receive {nodeup,Node} -> - ?line io:format("Up: ~p\n", [Node]); + io:format("Up: ~p\n", [Node]); Other -> - ?line io:format("Unexpected: ~p\n", [Other]), - ?line ?t:fail() + io:format("Unexpected: ~p\n", [Other]), + ?t:fail() after 10000 -> - ?line ?t:fail() + ?t:fail() end, - {Node,Pipe}. -- cgit v1.2.3 From 120a586a5d7d13e0d022dd773537856d30482e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 5 Apr 2016 16:47:27 +0200 Subject: Modernize use of timetraps --- erts/test/erl_print_SUITE.erl | 45 +++++++--------------------------- erts/test/erlexec_SUITE.erl | 38 ++++++----------------------- erts/test/ethread_SUITE.erl | 57 ++++++++++++------------------------------- erts/test/install_SUITE.erl | 31 ++++++----------------- erts/test/nt_SUITE.erl | 38 +++++++---------------------- erts/test/otp_SUITE.erl | 19 +++------------ erts/test/run_erl_SUITE.erl | 46 +++++++++------------------------- erts/test/z_SUITE.erl | 38 +++-------------------------- 8 files changed, 69 insertions(+), 243 deletions(-) (limited to 'erts') diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl index ceec4f4259..5699a906aa 100644 --- a/erts/test/erl_print_SUITE.erl +++ b/erts/test/erl_print_SUITE.erl @@ -28,51 +28,33 @@ -module(erl_print_SUITE). -author('rickard.s.green@ericsson.com'). - -%-define(line_trace, 1). - --define(DEFAULT_TIMEOUT, ?t:minutes(10)). - --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]). -export([erlang_display/1, integer/1, float/1, string/1, character/1, snprintf/1, quote/1]). -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 10}}]. all() -> - test_cases(). - -groups() -> - []. + [erlang_display, integer, float, string, character, + snprintf, quote]. -init_per_suite(Config) -> - Config. +init_per_testcase(Case, Config) -> + [{testcase, Case}|Config]. -end_per_suite(_Config) -> +end_per_testcase(_Case, _Config) -> ok. -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - %% %% %% Test cases %% %% -test_cases() -> - [erlang_display, integer, float, string, character, - snprintf, quote]. - erlang_display(doc) -> []; erlang_display(suite) -> []; erlang_display(Config) when is_list(Config) -> @@ -244,15 +226,6 @@ default_testcase_impl(doc) -> []; default_testcase_impl(suite) -> []; default_testcase_impl(Config) when is_list(Config) -> run_case(Config). -init_per_testcase(Case, Config) -> - Dog = ?t:timetrap(?DEFAULT_TIMEOUT), - [{testcase, Case}, {watchdog, Dog} |Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. - -define(TESTPROG, "erl_print_tests"). -define(FAILED_MARKER, $E,$P,$-,$T,$E,$S,$T,$-,$F,$A,$I,$L,$U,$R,$E). -define(SKIPPED_MARKER, $E,$P,$-,$T,$E,$S,$T,$-,$S,$K,$I,$P). diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index 91fb79bc80..d90daf78c3 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -27,54 +27,32 @@ %%%------------------------------------------------------------------- -module(erlexec_SUITE). +-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]). -%-define(line_trace, 1). - --define(DEFAULT_TIMEOUT, ?t:minutes(1)). - --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2]). - --export([args_file/1, evil_args_file/1, env/1, args_file_env/1, otp_7461/1, otp_7461_remote/1, otp_8209/1, zdbbl_dist_buf_busy_limit/1]). +-export([args_file/1, evil_args_file/1, env/1, args_file_env/1, + otp_7461/1, otp_7461_remote/1, otp_8209/1, + zdbbl_dist_buf_busy_limit/1]). -include_lib("common_test/include/ct.hrl"). - init_per_testcase(Case, Config) -> - Dog = ?t:timetrap(?DEFAULT_TIMEOUT), SavedEnv = save_env(), - [{testcase, Case}, {watchdog, Dog}, {erl_flags_env, SavedEnv} |Config]. + [{testcase, Case},{erl_flags_env, SavedEnv}|Config]. end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), SavedEnv = ?config(erl_flags_env, Config), restore_env(SavedEnv), cleanup_nodes(), - ?t:timetrap_cancel(Dog), ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. all() -> [args_file, evil_args_file, env, args_file_env, otp_7461, otp_8209, zdbbl_dist_buf_busy_limit]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - otp_8209(doc) -> ["Test that plain first argument does not " "destroy -home switch [OTP-8209]"]; diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index 91adbc05c0..889aeb8969 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -28,13 +28,7 @@ -module(ethread_SUITE). -author('rickard.s.green@ericsson.com'). -%-define(line_trace, 1). - --define(DEFAULT_TIMEOUT, ?t:minutes(10)). - --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]). -export([create_join_thread/1, equal_tids/1, @@ -53,7 +47,11 @@ -include_lib("common_test/include/ct.hrl"). -tests() -> +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 10}}]. + +all() -> [create_join_thread, equal_tids, mutex, @@ -69,26 +67,19 @@ tests() -> atomic, dw_atomic_massage]. -suite() -> [{ct_hooks,[ts_install_cth]}]. - -all() -> - tests(). - -groups() -> - []. - -init_per_suite(Config) -> - Config. +init_per_testcase(Case, Config) -> + case inet:gethostname() of + {ok,"fenris"} when Case == max_threads -> + %% Cannot use os:type+os:version as not all + %% solaris10 machines are buggy. + {skip, "This machine is buggy"}; + _Else -> + Config + end. -end_per_suite(_Config) -> +end_per_testcase(_Case, _Config) -> ok. -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - %% %% %% The test-cases @@ -215,22 +206,6 @@ dw_atomic_massage(Config) -> %% %% -init_per_testcase(Case, Config) -> - case inet:gethostname() of - {ok,"fenris"} when Case == max_threads -> - %% Cannot use os:type+os:version as not all - %% solaris10 machines are buggy. - {skip, "This machine is buggy"}; - _Else -> - Dog = ?t:timetrap(?DEFAULT_TIMEOUT), - [{watchdog, Dog}|Config] - end. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. - -define(TESTPROG, "ethread_tests"). -define(FAILED_MARKER, $E,$T,$H,$R,$-,$T,$E,$S,$T,$-,$F,$A,$I,$L,$U,$R,$E). -define(SKIPPED_MARKER, $E,$T,$H,$R,$-,$T,$E,$S,$T,$-,$S,$K,$I,$P). diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl index 1832289337..11a009a48b 100644 --- a/erts/test/install_SUITE.erl +++ b/erts/test/install_SUITE.erl @@ -28,11 +28,9 @@ %%%------------------------------------------------------------------- -module(install_SUITE). -%-define(line_trace, 1). - --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, - init_per_suite/1, end_per_suite/1, - init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0, + init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). -export([bin_default/1, bin_default_dirty/1, @@ -49,7 +47,6 @@ bin_dirname_fail/1, bin_no_use_dirname_fail/1]). --define(DEFAULT_TIMEOUT, ?t:minutes(1)). -define(JOIN(A,B,C), filename:join(A, B, C)). -include_lib("common_test/include/ct.hrl"). @@ -77,20 +74,13 @@ dont_need_symlink_cases() -> bin_unreasonable_path, 'bin white space', bin_no_srcfile]. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. all() -> dont_need_symlink_cases() ++ need_symlink_cases(). -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - %% %% The test cases @@ -586,18 +576,13 @@ init_per_testcase_aux(false, OsType, Case, Config) -> true -> {skip, "Cannot create symbolic links"} end; init_per_testcase_aux(true, _OsType, Case, Config) -> - Dog = ?t:timetrap(?DEFAULT_TIMEOUT), - [{watchdog, Dog}, - {testcase, Case}, + [{testcase, Case}, {test_dir, make_dirs(?config(priv_dir, Config), atom_to_list(Case))} | Config]. -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), +end_per_testcase(_Case, _Config) -> ok. - make_dirs(Root, Suffix) -> do_make_dirs(Root, string:tokens(Suffix, [$/])). diff --git a/erts/test/nt_SUITE.erl b/erts/test/nt_SUITE.erl index f40a8114a0..3a8d8be197 100644 --- a/erts/test/nt_SUITE.erl +++ b/erts/test/nt_SUITE.erl @@ -23,18 +23,19 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2,init_per_testcase/2, - end_per_testcase/2,nt/1,handle_eventlog/2, +-export([all/0, suite/0, + init_per_testcase/2, end_per_testcase/2, + nt/1,handle_eventlog/2, middleman/1,service_basic/1, service_env/1, user_env/1, synced/1, service_prio/1, logout/1, debug/1, restart/1, restart_always/1,stopaction/1, shutdown_io/0,do_shutdown_io/0]). --define(TEST_TIMEOUT, ?t:seconds(180)). -define(TEST_SERVICES, [1,2,3,4,5,6,7,8,9,10,11]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 3}}]. all() -> case os:type() of @@ -45,33 +46,13 @@ all() -> _ -> [nt] end. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - init_per_testcase(_Func, Config) -> - Dog = test_server:timetrap(?TEST_TIMEOUT), - [{watchdog, Dog} | Config]. + Config. -end_per_testcase(_Func, Config) -> +end_per_testcase(_Func, _Config) -> lists:foreach(fun(X) -> catch remove_service("test_service_" ++ integer_to_list(X)) - end, - ?TEST_SERVICES), - Dog = ?config(watchdog, Config), - catch test_server:timetrap_cancel(Dog), + end, ?TEST_SERVICES), ok. erlsrv() -> @@ -92,7 +73,6 @@ recv_prog_output(Port) -> end end. - %%% X == parameters to erlsrv %%% returns command output without stderr do_command(X) -> diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 11f70e8465..17beecb77d 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -20,7 +20,7 @@ -module(otp_SUITE). --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, +-export([all/0, suite/0, init_per_suite/1,end_per_suite/1]). -export([undefined_functions/1,deprecated_not_in_obsolete/1, obsolete_but_not_deprecated/1,call_to_deprecated/1, @@ -31,7 +31,9 @@ -import(lists, [filter/2,foldl/3,foreach/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 10}}]. all() -> [undefined_functions, deprecated_not_in_obsolete, @@ -40,18 +42,7 @@ all() -> erl_file_encoding, xml_file_encoding, runtime_dependencies]. -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - init_per_suite(Config) -> - Dog = test_server:timetrap(?t:minutes(10)), Root = code:root_dir(), Server = daily_xref, xref:start(Server), @@ -69,8 +60,6 @@ init_per_suite(Config) -> _ -> ok end, - - ?t:timetrap_cancel(Dog), [{xref_server,Server}|Config]. end_per_suite(Config) -> diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl index 1602466f05..f31f76b8bc 100644 --- a/erts/test/run_erl_SUITE.erl +++ b/erts/test/run_erl_SUITE.erl @@ -20,43 +20,19 @@ -module(run_erl_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, - basic/1,heavy/1,heavier/1,defunct/1]). +-export([all/0, suite/0]). +-export([basic/1,heavy/1,heavier/1,defunct/1]). -export([ping_me_back/1]). -include_lib("common_test/include/ct.hrl"). -init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?t:minutes(2)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 2}}]. all() -> [basic, heavy, heavier, defunct]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - basic(Config) when is_list(Config) -> case os:type() of @@ -103,12 +79,12 @@ heavy_1(Config) -> end, case count_new_lines(ToErl, 0) of - Nls when Nls > 30000 -> - ok; - Nls -> - io:format("new_lines: ~p\n", [Nls]), - ?t:fail() - end. + Nls when Nls > 30000 -> + ok; + Nls -> + io:format("new_lines: ~p\n", [Nls]), + ?t:fail() + end. ping_me_back([Node]) when is_atom(Node) -> diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl index 7f3260e4cb..e6ce90ac3e 100644 --- a/erts/test/z_SUITE.erl +++ b/erts/test/z_SUITE.erl @@ -24,8 +24,6 @@ %% This suite expects to be run as the last suite of all suites. %% -%-define(line_trace, 1). - -include_lib("kernel/include/file.hrl"). -record(core_search_conf, {search_dir, @@ -34,47 +32,19 @@ file, run_by_ts}). --define(DEFAULT_TIMEOUT, ?t:minutes(5)). - --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0]). -export([search_for_core_files/1, core_files/1]). -include_lib("common_test/include/ct.hrl"). - -init_per_testcase(Case, Config) -> - Dog = ?t:timetrap(?DEFAULT_TIMEOUT), - [{testcase, Case}, {watchdog, Dog} |Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 5}}]. all() -> [core_files]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - core_files(doc) -> []; -- cgit v1.2.3 From d86548e01746aa14074b4df4c157f6d06f3be697 Mon Sep 17 00:00:00 2001 From: John Eckersberg Date: Wed, 16 Dec 2015 11:03:42 -0500 Subject: Add patch to crash dump on large distribution https://bugzilla.redhat.com/show_bug.cgi?id=1291822 https://bugzilla.redhat.com/show_bug.cgi?id=1291855 https://bugzilla.redhat.com/show_bug.cgi?id=1291856 https://bugzilla.redhat.com/show_bug.cgi?id=1291857 --- erts/emulator/beam/dist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index fa385f105d..3cfd164224 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -1963,7 +1963,7 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); if (size > (Uint) INT_MAX) - erts_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_DUMP_EXIT, "Absurdly large distribution output data buffer " "(%beu bytes) passed.\n", size); @@ -2003,7 +2003,7 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); if (size > (Uint) INT_MAX) - erts_exit(ERTS_ABORT_EXIT, + erts_exit(ERTS_DUMP_EXIT, "Absurdly large distribution output data buffer " "(%beu bytes) passed.\n", size); -- cgit v1.2.3 From f1bd16d883b6236dff83d7784ebd858ef29e2c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 Apr 2016 15:46:49 +0200 Subject: Eliminate use of ?config() macro --- erts/test/erl_print_SUITE.erl | 4 ++-- erts/test/erlc_SUITE.erl | 10 +++++----- erts/test/erlexec_SUITE.erl | 8 ++++---- erts/test/ethread_SUITE.erl | 2 +- erts/test/ignore_cores.erl | 10 +++++----- erts/test/install_SUITE.erl | 36 ++++++++++++++++++------------------ erts/test/nt_SUITE.erl | 4 ++-- erts/test/otp_SUITE.erl | 18 +++++++++--------- erts/test/run_erl_SUITE.erl | 6 +++--- erts/test/upgrade_SUITE.erl | 18 +++++++++--------- 10 files changed, 58 insertions(+), 58 deletions(-) (limited to 'erts') diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl index 5699a906aa..9ef68155bc 100644 --- a/erts/test/erl_print_SUITE.erl +++ b/erts/test/erl_print_SUITE.erl @@ -297,8 +297,8 @@ run_case(Config, TestArgs) -> run_case(Config, TestArgs, fun (_Port) -> ok end). run_case(Config, TestArgs, Fun) -> - Test = atom_to_list(?config(testcase, Config)), - TestProg = filename:join([?config(data_dir, Config), + Test = atom_to_list(proplists:get_value(testcase, Config)), + TestProg = filename:join([proplists:get_value(data_dir, Config), ?TESTPROG ++ "." ++ atom_to_list(erlang:system_info(threads))]), diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index 19f78255c8..eca8319153 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -168,7 +168,7 @@ compile_mib(Config) when is_list(Config) -> good_citizen(Config) when is_list(Config) -> case os:type() of {unix, _} -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Answer = filename:join(PrivDir, "answer"), Script = filename:join(PrivDir, "test_script"), Test = filename:join(PrivDir, "test.erl"), @@ -190,7 +190,7 @@ good_citizen(Config) when is_list(Config) -> deep_cwd(Config) when is_list(Config) -> case os:type() of {unix, _} -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), deep_cwd_1(PrivDir); _ -> {skip, "Only a problem on Unix"} @@ -375,8 +375,8 @@ get_cmd(Cfg) -> {SrcDir, OutDir, Cmd}. get_dirs(Cfg) -> - DataDir = ?config(data_dir, Cfg), - PrivDir = ?config(priv_dir, Cfg), + DataDir = proplists:get_value(data_dir, Cfg), + PrivDir = proplists:get_value(priv_dir, Cfg), SrcDir = filename:join(DataDir, "src"), IncDir = filename:join(DataDir, "include"), {SrcDir, IncDir, PrivDir}. @@ -392,7 +392,7 @@ exists(Name) -> %% a non-zero exit status. run_command(Config, Cmd) -> - TmpDir = filename:join(?config(priv_dir, Config), "tmp"), + TmpDir = filename:join(proplists:get_value(priv_dir, Config), "tmp"), file:make_dir(TmpDir), {RunFile, Run, Script} = run_command(TmpDir, os:type(), Cmd), ok = file:write_file(filename:join(TmpDir, RunFile), unicode:characters_to_binary(Script)), diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index d90daf78c3..969270e21b 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -40,7 +40,7 @@ init_per_testcase(Case, Config) -> [{testcase, Case},{erl_flags_env, SavedEnv}|Config]. end_per_testcase(_Case, Config) -> - SavedEnv = ?config(erl_flags_env, Config), + SavedEnv = proplists:get_value(erl_flags_env, Config), restore_env(SavedEnv), cleanup_nodes(), ok. @@ -279,7 +279,7 @@ otp_7461(Config) when is_list(Config) -> otp_7461_do(Config) -> io:format("alive=~p node=~p\n",[is_alive(), node()]), - TestProg = filename:join([?config(data_dir, Config), "erlexec_tests"]), + TestProg = filename:join([proplists:get_value(data_dir, Config), "erlexec_tests"]), {ok, [[ErlProg]]} = init:get_argument(progname), Cmd = TestProg ++ " " ++ ErlProg ++ " -detached -sname " ++ get_nodename(otp_7461) ++ @@ -383,8 +383,8 @@ restore_env({erl_flags, AFlgs, Flgs, RFlgs, ZFlgs}) -> ok. privfile(Name, Config) -> - filename:join([?config(priv_dir, Config), - atom_to_list(?config(testcase, Config)) ++ "." ++ Name]). + filename:join([proplists:get_value(priv_dir, Config), + atom_to_list(proplists:get_value(testcase, Config)) ++ "." ++ Name]). write_file(FileName, Frmt) -> write_file(FileName, Frmt, []). diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index 889aeb8969..0cef913986 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -274,7 +274,7 @@ run_case(Config, Test, TestArgs) -> run_case(Config, Test, TestArgs, fun (_Port) -> ok end). run_case(Config, Test, TestArgs, Fun) -> - TestProg = filename:join([?config(data_dir, Config), ?TESTPROG]), + TestProg = filename:join([proplists:get_value(data_dir, Config), ?TESTPROG]), Cmd = TestProg ++ " " ++ Test ++ " " ++ TestArgs, case catch open_port({spawn, Cmd}, [stream, use_stdio, diff --git a/erts/test/ignore_cores.erl b/erts/test/ignore_cores.erl index e40b91392c..4af5efe834 100644 --- a/erts/test/ignore_cores.erl +++ b/erts/test/ignore_cores.erl @@ -53,7 +53,7 @@ init(Config) -> fini(Config) -> #ignore_cores{org_cwd = OrgCWD, org_path = OrgPath, - org_pwd_env = OrgPWD} = ?config(ignore_cores, Config), + org_pwd_env = OrgPWD} = proplists:get_value(ignore_cores, Config), ok = file:set_cwd(OrgCWD), true = code:set_path(OrgPath), case OrgPWD of @@ -70,10 +70,10 @@ setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite), is_list(Config) -> #ignore_cores{org_cwd = OrgCWD, org_path = OrgPath, - org_pwd_env = OrgPWD} = ?config(ignore_cores, Config), + org_pwd_env = OrgPWD} = proplists:get_value(ignore_cores, Config), Path = lists:map(fun (".") -> OrgCWD; (Dir) -> Dir end, OrgPath), true = code:set_path(Path), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), IgnDir = filename:join([PrivDir, atom_to_list(Suite) ++ "_" @@ -119,7 +119,7 @@ restore(Config) -> org_path = OrgPath, org_pwd_env = OrgPWD, ign_dir = IgnDir, - cores_dir = CoresDir} = ?config(ignore_cores, Config), + cores_dir = CoresDir} = proplists:get_value(ignore_cores, Config), try case CoresDir of false -> @@ -155,5 +155,5 @@ restore(Config) -> dir(Config) -> - #ignore_cores{ign_dir = Dir} = ?config(ignore_cores, Config), + #ignore_cores{ign_dir = Dir} = proplists:get_value(ignore_cores, Config), Dir. diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl index 11a009a48b..0010753edb 100644 --- a/erts/test/install_SUITE.erl +++ b/erts/test/install_SUITE.erl @@ -249,7 +249,7 @@ bin_unreasonable_path(Config) when is_list(Config) -> erlang_bindir = EBs}, ChkRes). bin_unreachable_absolute(Config) when is_list(Config) -> - TDir = ?config(test_dir, Config), + TDir = proplists:get_value(test_dir, Config), make_dirs(TDir, "/opt/local/lib/erlang/usr/bin"), make_dirs(TDir, "/opt/local/lib/erlang/bin"), Erl = join([TDir, "/opt/local/lib/erlang/bin/erl"]), @@ -290,7 +290,7 @@ bin_unreachable_absolute(Config) when is_list(Config) -> erlang_bindir = EBs}, ChkRes). bin_unreachable_relative(Config) when is_list(Config) -> - TDir = ?config(test_dir, Config), + TDir = proplists:get_value(test_dir, Config), make_dirs(TDir, "/opt/local/lib/erlang/bin"), make_dirs(TDir, "/opt/local/bin"), make_dirs(TDir, "/usr/local/lib/erlang/bin"), @@ -331,7 +331,7 @@ bin_unreachable_relative(Config) when is_list(Config) -> erlang_bindir = EBs}, ChkRes). bin_ok_symlink(Config) when is_list(Config) -> - TDir = ?config(test_dir, Config), + TDir = proplists:get_value(test_dir, Config), make_dirs(TDir, "/usr/local/bin"), make_dirs(TDir, "/opt/local/lib/erlang/bin"), Erl = join([TDir, "/opt/local/lib/erlang/bin/erl"]), @@ -371,7 +371,7 @@ bin_ok_symlink(Config) when is_list(Config) -> erlang_bindir = EBs}, ChkRes). bin_same_dir(Config) when is_list(Config) -> - TDir = ?config(test_dir, Config), + TDir = proplists:get_value(test_dir, Config), make_dirs(TDir, "/usr/local/bin"), make_dirs(TDir, "/usr/local/lib"), ok = file:make_symlink("..", join([TDir, "/usr/local/lib/erlang"])), @@ -443,7 +443,7 @@ bin_dirname_fail(Config) when is_list(Config) -> Be = Bs, EBs = "/opt/lib/erlang/otp/bin", EBe = EBs, - CMDPRFX = "PATH=\""++?config(data_dir,Config)++":"++os:getenv("PATH")++"\"", + CMDPRFX = "PATH=\""++proplists:get_value(data_dir,Config)++":"++os:getenv("PATH")++"\"", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, @@ -476,7 +476,7 @@ bin_no_use_dirname_fail(Config) when is_list(Config) -> EBs = "/opt/lib/erlang/otp/bin", EBe = EBs, RP = "../lib/erlang/otp/bin", - CMDPRFX = "PATH=\""++?config(data_dir,Config)++":"++os:getenv("PATH")++"\"", + CMDPRFX = "PATH=\""++proplists:get_value(data_dir,Config)++":"++os:getenv("PATH")++"\"", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, @@ -503,7 +503,7 @@ bin_no_use_dirname_fail(Config) when is_list(Config) -> erlang_bindir = EBs}, ChkRes). bin_no_srcfile(Config) when is_list(Config) -> - TDir = ?config(test_dir, Config), + TDir = proplists:get_value(test_dir, Config), make_dirs(TDir, "/opt/local/bin"), make_dirs(TDir, "/opt/local/lib/erlang/bin"), Erl = join([TDir, "/opt/local/lib/erlang/bin/erl"]), @@ -549,7 +549,7 @@ expect(X, Y) -> ?t:fail({X,Y}). init_per_suite(Config) -> - PD = ?config(priv_dir, Config), + PD = proplists:get_value(priv_dir, Config), SymLinks = case ?t:os_type() of {win32, _} -> false; _ -> @@ -566,7 +566,7 @@ end_per_suite(_Config) -> ok. init_per_testcase(Case, Config) -> - init_per_testcase_aux(?config(symlinks,Config),?t:os_type(),Case,Config). + init_per_testcase_aux(proplists:get_value(symlinks,Config),?t:os_type(),Case,Config). init_per_testcase_aux(_, {win32, _}, _Case, _Config) -> {skip, "Not on windows"}; @@ -577,7 +577,7 @@ init_per_testcase_aux(false, OsType, Case, Config) -> end; init_per_testcase_aux(true, _OsType, Case, Config) -> [{testcase, Case}, - {test_dir, make_dirs(?config(priv_dir, Config), atom_to_list(Case))} + {test_dir, make_dirs(proplists:get_value(priv_dir, Config), atom_to_list(Case))} | Config]. end_per_testcase(_Case, _Config) -> @@ -601,9 +601,9 @@ install_bin(Config, #inst{mkdirs = MkDirs, exec_prefix = EXEC_PREFIX, bindir = BINDIR, erlang_bindir = ERLANG_BINDIR} = Inst, ChkRes) -> - PDir = ?config(priv_dir, Config), - TDir = ?config(test_dir, Config), - TD = atom_to_list(?config(testcase, Config)), + PDir = proplists:get_value(priv_dir, Config), + TDir = proplists:get_value(test_dir, Config), + TD = atom_to_list(proplists:get_value(testcase, Config)), case MkDirs of false -> ok; true -> @@ -626,7 +626,7 @@ install_bin(Config, #inst{mkdirs = MkDirs, bindir = join([TDir, BINDIR]), erlang_bindir = join([TDir, ERLANG_BINDIR])}, ChkRes), - case ?config(symlinks, Config) of + case proplists:get_value(symlinks, Config) of true -> ok; false -> {comment, "No symlink tests run, since symlinks not working"} end. @@ -649,7 +649,7 @@ install_bin2(Config, Inst, ChkRes) -> install_bin3(Config, Inst#inst{symlinks = false, ln_s = "cp -p", bindir_symlinks = "absolute"}, ChkRes), - case ?config(symlinks, Config) of + case proplists:get_value(symlinks, Config) of true -> install_bin3(Config, Inst#inst{symlinks = true, ln_s = "ln -s"}, ChkRes), @@ -675,9 +675,9 @@ install_bin3(Config, erlang_bindir = ERLANG_BINDIR, bindir_symlinks = BINDIR_SYMLINKS} = Inst, ChkRes) -> - Test = ?config(testcase, Config), - DDir = ?config(data_dir, Config), - TDir = ?config(test_dir, Config), + Test = proplists:get_value(testcase, Config), + DDir = proplists:get_value(data_dir, Config), + TDir = proplists:get_value(test_dir, Config), InstallBin = filename:join(DDir, "install_bin"), ResFile = filename:join(TDir, atom_to_list(Test) ++ "-result.txt"), Cmd = CMD_PRFX ++ " " diff --git a/erts/test/nt_SUITE.erl b/erts/test/nt_SUITE.erl index 3a8d8be197..cdce035438 100644 --- a/erts/test/nt_SUITE.erl +++ b/erts/test/nt_SUITE.erl @@ -512,11 +512,11 @@ get_current_procs(Config) -> erl_parse:parse_term(Tok). nt_info(Config) when is_list(Config) -> - "\"" ++ filename:join(?config(data_dir, Config), "nt_info") ++ "\"". + "\"" ++ filename:join(proplists:get_value(data_dir, Config), "nt_info") ++ "\"". logdir(Config) -> - ?config(priv_dir, Config). + proplists:get_value(priv_dir, Config). look_for_next(Template,L,N) -> FN = Template ++ integer_to_list(N), diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 17beecb77d..05c08207be 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -63,12 +63,12 @@ init_per_suite(Config) -> [{xref_server,Server}|Config]. end_per_suite(Config) -> - Server = ?config(xref_server, Config), + Server = proplists:get_value(xref_server, Config), catch xref:stop(Server), Config. undefined_functions(Config) when is_list(Config) -> - Server = ?config(xref_server, Config), + Server = proplists:get_value(xref_server, Config), %% Exclude calls from generated modules in the SSL application. ExcludeFrom = "SSL-PKIX|PKIX.*|ssl_pkix_oid", @@ -222,7 +222,7 @@ diameter_filter(Undef) -> end, Undef). deprecated_not_in_obsolete(Config) when is_list(Config) -> - Server = ?config(xref_server, Config), + Server = proplists:get_value(xref_server, Config), {ok,DeprecatedFunctions} = xref:q(Server, "DF"), L = foldl(fun({M,F,A}=MFA, Acc) -> @@ -244,7 +244,7 @@ deprecated_not_in_obsolete(Config) when is_list(Config) -> end. obsolete_but_not_deprecated(Config) when is_list(Config) -> - Server = ?config(xref_server, Config), + Server = proplists:get_value(xref_server, Config), {ok,NotDeprecated} = xref:q(Server, "X - DF"), L = foldl(fun({M,F,A}=MFA, Acc) -> @@ -268,7 +268,7 @@ obsolete_but_not_deprecated(Config) when is_list(Config) -> end. call_to_deprecated(Config) when is_list(Config) -> - Server = ?config(xref_server, Config), + Server = proplists:get_value(xref_server, Config), {ok,DeprecatedCalls} = xref:q(Server, "strict(E || DF)"), foreach(fun ({MFA1,MFA2}) -> io:format("~s calls deprecated ~s", @@ -291,7 +291,7 @@ call_to_now_0(Config) when is_list(Config) -> not_recommended_calls(Config, Apps, {erlang,now,0}). not_recommended_calls(Config, Apps0, MFA) -> - Server = ?config(xref_server, Config), + Server = proplists:get_value(xref_server, Config), Apps = [App || App <- Apps0, is_present_application(App, Server)], @@ -351,7 +351,7 @@ is_present_application(Name, Server) -> end. strong_components(Config) when is_list(Config) -> - Server = ?config(xref_server, Config), + Server = proplists:get_value(xref_server, Config), {ok,Cs} = xref:q(Server, "components AE"), io:format("\n\nStrong components:\n\n~p\n", [Cs]), ok. @@ -433,7 +433,7 @@ runtime_dependencies(Config) -> %% Verify that (at least) OTP application runtime dependencies found %% by xref are listed in the runtime_dependencies field of the .app file %% of each application. - Server = ?config(xref_server, Config), + Server = proplists:get_value(xref_server, Config), {ok, AE} = xref:q(Server, "AE"), SAE = lists:keysort(1, AE), put(ignored_failures, []), @@ -532,7 +532,7 @@ format_mfa(Server, MFA) -> AppPrefix ++ MFAString. open_log(Config, Name) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), RunDir = filename:dirname(filename:dirname(PrivDir)), Path = filename:join(RunDir, "system_"++Name++".log"), {ok,Fd} = file:open(Path, [write]), diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl index f31f76b8bc..2474492a3b 100644 --- a/erts/test/run_erl_SUITE.erl +++ b/erts/test/run_erl_SUITE.erl @@ -217,10 +217,10 @@ defunct_1(Config) -> end. defunct_2(Config, Perl) -> - Data = ?config(data_dir, Config), + Data = proplists:get_value(data_dir, Config), RunErlTest = filename:join(Data, "run_erl_test.pl"), Defuncter = filename:join(Data, "defuncter.pl"), - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), LogDir = filename:join(Priv, "defunct"), ok = file:make_dir(LogDir), Pipe = LogDir ++ "/", @@ -236,7 +236,7 @@ defunct_2(Config, Perl) -> %%% Utilities. do_run_erl(Config, Case) -> - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), LogDir = filename:join(Priv, Case), ok = file:make_dir(LogDir), Pipe = LogDir ++ "/", diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl index 83cd2359d8..1d5be8f018 100644 --- a/erts/test/upgrade_SUITE.erl +++ b/erts/test/upgrade_SUITE.erl @@ -50,12 +50,12 @@ init_per_suite(Config) -> %% Fake release, no applications {skip, "Need a real release running to create other releases"}; _ -> - rm_rf(filename:join([?config(data_dir,Config),priv_dir])), + rm_rf(filename:join([proplists:get_value(data_dir,Config),priv_dir])), Config end. init_per_testcase(Case,Config) -> - PrivDir = filename:join([?config(data_dir,Config),priv_dir,Case]), + PrivDir = filename:join([proplists:get_value(data_dir,Config),priv_dir,Case]), CreateDir = filename:join([PrivDir,create]), InstallDir = filename:join([PrivDir,install]), ok = filelib:ensure_dir(filename:join(CreateDir,"*")), @@ -66,10 +66,10 @@ init_per_testcase(Case,Config) -> end_per_testcase(_Case,Config) -> Nodes = nodes(), [test_server:stop_node(Node) || Node <- Nodes], - case ?config(tc_status,Config) of + case proplists:get_value(tc_status,Config) of ok -> %% Note that priv_dir here is per test case! - rm_rf(?config(priv_dir,Config)); + rm_rf(proplists:get_value(priv_dir,Config)); _fail -> %% Test case data can be found under DataDir/priv_dir/Case ok @@ -115,15 +115,15 @@ upgrade_test(FromVsn,ToVsn,Config) -> case OldRel of false -> %% Note that priv_dir here is per test case! - rm_rf(?config(priv_dir,Config)), + rm_rf(proplists:get_value(priv_dir,Config)), {skip, "no previous release available"}; _ -> upgrade_test1(FromVsn,ToVsn,[{old_rel,OldRel}|Config]) end. upgrade_test1(FromVsn,ToVsn,Config) -> - CreateDir = ?config(create_dir,Config), - InstallDir = ?config(install_dir,Config), + CreateDir = proplists:get_value(create_dir,Config), + InstallDir = proplists:get_value(install_dir,Config), FromRelName = "otp-"++FromVsn, ToRelName = "otp-"++ToVsn, @@ -141,7 +141,7 @@ upgrade_test1(FromVsn,ToVsn,Config) -> %%% - chmod 'start' and 'start_erl' target_system(RelName0,RelVsn,CreateDir,InstallDir,Config) -> {ok,Node} = test_server:start_node(list_to_atom(RelName0),peer, - [{erl,[?config(old_rel,Config)]}]), + [{erl,[proplists:get_value(old_rel,Config)]}]), {RelName,Apps,ErtsVsn} = create_relfile(Node,CreateDir,RelName0,RelVsn), @@ -184,7 +184,7 @@ target_system(RelName0,RelVsn,CreateDir,InstallDir,Config) -> write_file(SysConfig, "[]."), %% Insert 'start' script from data_dir - modified to add sname and heart - copy_file(filename:join(?config(data_dir,Config),"start.src"), + copy_file(filename:join(proplists:get_value(data_dir,Config),"start.src"), filename:join(ErtsBinDir,"start.src")), ok = file:change_mode(filename:join(ErtsBinDir,"start.src"),8#0755), -- cgit v1.2.3 From 4b507534f27c343fe2b53f07bbe52e94c81e381f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 7 Dec 2015 17:10:37 +0100 Subject: ops.tab: Remove useless transformation The transformation on the following line will do the job. --- erts/emulator/beam/ops.tab | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 772460c177..78000160e3 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1539,7 +1539,6 @@ gen_minus p Live Reg=d Int=i Dst | negation_is_small(Int) => \ # GCing arithmetic instructions. # -gen_plus Fail Live Y=y X=x Dst => i_plus Fail Live X Y Dst gen_plus Fail Live S1 S2 Dst => i_plus Fail Live S1 S2 Dst gen_minus Fail Live S1 S2 Dst => i_minus Fail Live S1 S2 Dst -- cgit v1.2.3 From ec26a0c56cb6e28cc5a35ef72116275e5eeef823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 1 Apr 2016 14:03:51 +0200 Subject: Refactor calls to transform_engine() We used to set last_op_next and last_op to NULL just in case. Setting last_op_next to causes a rescan of the instructions to find the last instruction in the chain, so we would want to avoid that unless really necessary. --- erts/emulator/beam/beam_load.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 16cbdbffea..2f2b433999 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -2028,11 +2028,7 @@ load_code(LoaderState* stp) ASSERT(arity == last_op->arity); do_transform: - if (stp->genop == NULL) { - last_op_next = NULL; - goto get_next_instr; - } - + ASSERT(stp->genop != NULL); if (gen_opc[stp->genop->op].transform != -1) { int need; tmp_op = stp->genop; @@ -2045,25 +2041,34 @@ load_code(LoaderState* stp) } switch (transform_engine(stp)) { case TE_FAIL: - last_op_next = NULL; - last_op = NULL; + /* + * No transformation found. stp->genop != NULL and + * last_op_next is still valid. Go ahead and load + * the instruction. + */ break; case TE_OK: + /* + * Some transformation was applied. last_op_next is + * no longer valid and stp->genop may be NULL. + * Try to transform again. + */ + if (stp->genop == NULL) { + last_op_next = &stp->genop; + goto get_next_instr; + } last_op_next = NULL; - last_op = NULL; goto do_transform; case TE_SHORT_WINDOW: - last_op_next = NULL; - last_op = NULL; + /* + * No transformation applied. stp->genop != NULL and + * last_op_next is still valid. Fetch a new instruction + * before trying the transformation again. + */ goto get_next_instr; } } - if (stp->genop == NULL) { - last_op_next = NULL; - goto get_next_instr; - } - /* * From the collected generic instruction, find the specific * instruction. @@ -2584,7 +2589,10 @@ load_code(LoaderState* stp) { GenOp* next = stp->genop->next; FREE_GENOP(stp, stp->genop); - stp->genop = next; + if ((stp->genop = next) == NULL) { + last_op_next = &stp->genop; + goto get_next_instr; + } goto do_transform; } } -- cgit v1.2.3 From 66668a6504fe3d6ed33ce87b1d4c1a76dae1a987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 Apr 2016 16:15:24 +0200 Subject: Eliminate use of test_server:fail/0,1 --- erts/test/erl_print_SUITE.erl | 8 ++++---- erts/test/erlc_SUITE.erl | 11 +++++------ erts/test/ethread_SUITE.erl | 16 ++++++++-------- erts/test/install_SUITE.erl | 2 +- erts/test/otp_SUITE.erl | 12 ++++++------ erts/test/run_erl_SUITE.erl | 18 +++++++----------- erts/test/z_SUITE.erl | 2 +- 7 files changed, 32 insertions(+), 37 deletions(-) (limited to 'erts') diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl index 9ef68155bc..a6d287a318 100644 --- a/erts/test/erl_print_SUITE.erl +++ b/erts/test/erl_print_SUITE.erl @@ -261,7 +261,7 @@ get_line(Port, noeol, Data) -> {Port, {data, {Flag, NextData}}} -> get_line(Port, Flag, Data ++ NextData); {Port, eof} -> - ?t:fail(port_prog_unexpectedly_closed) + ct:fail(port_prog_unexpectedly_closed) end. read_case_data(Port, TestCase) -> @@ -273,7 +273,7 @@ read_case_data(Port, TestCase) -> {Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} -> {skipped, get_line(Port, Flag, CommentStart)}; {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} -> - ?t:fail(get_line(Port, Flag, ReasonStart)); + ct:fail(get_line(Port, Flag, ReasonStart)); {Port, {data, {eol, [?PID_MARKER | PidStr]}}} -> ?t:format("Port program pid: ~s~n", [PidStr]), CaseProc = self(), @@ -287,7 +287,7 @@ read_case_data(Port, TestCase) -> ?t:format("~s~n", [get_line(Port, Flag, LineStart)]), read_case_data(Port, TestCase); {Port, eof} -> - ?t:fail(port_prog_unexpectedly_closed) + ct:fail(port_prog_unexpectedly_closed) end. run_case(Config) -> @@ -317,7 +317,7 @@ run_case(Config, TestArgs, Fun) -> end, CaseResult; Error -> - ?t:fail({open_port_failed, Error}) + ct:fail({open_port_failed, Error}) end. diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index eca8319153..0750bf2836 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -249,7 +249,7 @@ num_d_options() -> erlc() -> case os:find_executable("erlc") of false -> - test_server:fail("Can't find erlc"); + ct:fail("Can't find erlc"); Erlc -> "\"" ++ Erlc ++ "\"" end. @@ -359,13 +359,13 @@ match_messages([Msg|Rest1], [Regexp|Rest2]) -> nomatch -> io:format("Not matching: ~s\n", [Msg]), io:format("Regexp : ~s\n", [Regexp]), - test_server:fail(message_mismatch) + ct:fail(message_mismatch) end, match_messages(Rest1, Rest2); match_messages([], [Expect|Rest]) -> - test_server:fail({too_few_messages, [Expect|Rest]}); + ct:fail({too_few_messages, [Expect|Rest]}); match_messages([Msg|Rest], []) -> - test_server:fail({too_many_messages, [Msg|Rest]}); + ct:fail({too_many_messages, [Msg|Rest]}); match_messages([], []) -> ok. @@ -422,5 +422,4 @@ run_command(Dir, {unix, _}, Cmd) -> " *) echo '_ERROR_';;\n", "esac\n"]}; run_command(_Dir, Other, _Cmd) -> - M = io_lib:format("Don't know how to test exit code for ~p", [Other]), - test_server:fail(lists:flatten(M)). + ct:fail("Don't know how to test exit code for ~p", [Other]). diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index 0cef913986..1c1a2f3836 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -241,7 +241,7 @@ get_line(Port, noeol, Data) -> {Port, {data, {Flag, NextData}}} -> get_line(Port, Flag, Data ++ NextData); {Port, eof} -> - ?t:fail(port_prog_unexpectedly_closed) + ct:fail(port_prog_unexpectedly_closed) end. read_case_data(Port, TestCase) -> @@ -253,7 +253,7 @@ read_case_data(Port, TestCase) -> {Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} -> {skipped, get_line(Port, Flag, CommentStart)}; {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} -> - ?t:fail(get_line(Port, Flag, ReasonStart)); + ct:fail(get_line(Port, Flag, ReasonStart)); {Port, {data, {eol, [?PID_MARKER | PidStr]}}} -> ?t:format("Port program pid: ~s~n", [PidStr]), CaseProc = self(), @@ -267,7 +267,7 @@ read_case_data(Port, TestCase) -> ?t:format("~s~n", [get_line(Port, Flag, LineStart)]), read_case_data(Port, TestCase); {Port, eof} -> - ?t:fail(port_prog_unexpectedly_closed) + ct:fail(port_prog_unexpectedly_closed) end. run_case(Config, Test, TestArgs) -> @@ -284,11 +284,11 @@ run_case(Config, Test, TestArgs, Fun) -> Port when is_port(Port) -> Fun(Port), CaseResult = read_case_data(Port, Test), - receive - {Port, eof} -> - ok - end, + receive + {Port, eof} -> + ok + end, CaseResult; Error -> - ?t:fail({open_port_failed, Error}) + ct:fail({open_port_failed, Error}) end. diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl index 0010753edb..cf5a145755 100644 --- a/erts/test/install_SUITE.erl +++ b/erts/test/install_SUITE.erl @@ -546,7 +546,7 @@ expect(X, Y) -> ?t:format("expected: ~p~n", [X]), ?t:format("got : ~p~n", [Y]), ?t:format("-----------------------------------------------~n", []), - ?t:fail({X,Y}). + ct:fail({X,Y}). init_per_suite(Config) -> PD = proplists:get_value(priv_dir, Config), diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index 05c08207be..7ca0f50263 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -100,7 +100,7 @@ undefined_functions(Config) when is_list(Config) -> format_mfa(MFA2)]) end, Undef), close_log(Fd), - ?t:fail({length(Undef),undefined_functions_in_otp}) + ct:fail({length(Undef),undefined_functions_in_otp}) end. hipe_filter(Undef) -> @@ -240,7 +240,7 @@ deprecated_not_in_obsolete(Config) when is_list(Config) -> Fd = open_log(Config, "deprecated_not_obsolete"), print_mfas(Fd, Server, L), close_log(Fd), - ?t:fail({length(L),deprecated_but_not_obsolete}) + ct:fail({length(L),deprecated_but_not_obsolete}) end. obsolete_but_not_deprecated(Config) when is_list(Config) -> @@ -264,7 +264,7 @@ obsolete_but_not_deprecated(Config) when is_list(Config) -> Fd = open_log(Config, "obsolete_not_deprecated"), print_mfas(Fd, Server, L), close_log(Fd), - ?t:fail({length(L),obsolete_but_not_deprecated}) + ct:fail({length(L),obsolete_but_not_deprecated}) end. call_to_deprecated(Config) when is_list(Config) -> @@ -340,7 +340,7 @@ not_recommended_calls(Config, Apps0, MFA) -> {comment, Mess} end; _ -> - ?t:fail({length(CallsToMFA),calls_to_size_1}) + ct:fail({length(CallsToMFA),calls_to_size_1}) end. is_present_application(Name, Server) -> @@ -374,7 +374,7 @@ erl_file_encoding(_Config) -> [_|_] -> io:put_chars("Files with \"coding:\":\n"), [io:put_chars(F) || F <- Fs], - ?t:fail() + ct:fail(failed) end. filter_use_latin1_coding(F, MP) -> @@ -394,7 +394,7 @@ xml_file_encoding(_Config) -> [_|_] -> io:put_chars("Encoding should be \"utf-8\" or \"UTF-8\":\n"), [io:put_chars(F) || F <- Fs], - ?t:fail() + ct:fail(failed) end. xml_files() -> diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl index 2474492a3b..5cd0068280 100644 --- a/erts/test/run_erl_SUITE.erl +++ b/erts/test/run_erl_SUITE.erl @@ -50,7 +50,7 @@ basic_1(Config) -> {nodedown,Node} -> io:format("Down: ~p\n", [Node]) after 10000 -> - ?t:fail() + ct:fail(timeout) end, ok. @@ -75,15 +75,14 @@ heavy_1(Config) -> {nodedown,Node} -> io:format("Down: ~p\n", [Node]) after 10000 -> - ?t:fail() + ct:fail(timeout) end, case count_new_lines(ToErl, 0) of Nls when Nls > 30000 -> ok; Nls -> - io:format("new_lines: ~p\n", [Nls]), - ?t:fail() + ct:fail("new_lines: ~p\n", [Nls]) end. @@ -153,7 +152,7 @@ heavier_1(Config) -> io:format("Down: ~p\n", [Node]) after 10000 -> c:flush(), - ?t:fail() + ct:fail(timeout) end, ok. @@ -180,9 +179,7 @@ receive_all_2(Iter, {NumChars,Pattern}, Line0, ToErl, MaxLen) -> %%io:format("Recv: ~p\n", [S]), receive_all_2(Iter, {NumChars,Pattern}, Line++S, ToErl, MaxLen) after 10000 -> - io:format("Timeout waiting for\n~p\ngot\n~p\n", - [Pattern, Line]), - ?t:fail() + ct:fail("Timeout waiting for\n~p\ngot\n~p\n", [Pattern, Line]) end end. @@ -256,9 +253,8 @@ do_run_erl(Config, Case) -> {nodeup,Node} -> io:format("Up: ~p\n", [Node]); Other -> - io:format("Unexpected: ~p\n", [Other]), - ?t:fail() + ct:fail("Unexpected: ~p\n", [Other]) after 10000 -> - ?t:fail() + ct:fail(timeout) end, {Node,Pipe}. diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl index e6ce90ac3e..204f393e93 100644 --- a/erts/test/z_SUITE.erl +++ b/erts/test/z_SUITE.erl @@ -324,7 +324,7 @@ core_file_search(#core_search_conf{search_dir = Base, case {RunByTS, ICores, FCores} of {true, [], []} -> ok; {true, _, []} -> {comment, Res}; - {true, _, _} -> ?t:fail(Res); + {true, _, _} -> ct:fail(Res); _ -> Res end end. -- cgit v1.2.3 From 7c9936f44c70bdcb965a78fcf525f97846fb32a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 Apr 2016 16:18:11 +0200 Subject: Replace use of test_server:format/2 with io:format/2 --- erts/test/erl_print_SUITE.erl | 12 ++++++------ erts/test/ethread_SUITE.erl | 8 ++++---- erts/test/install_SUITE.erl | 16 ++++++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl index a6d287a318..d52caeb9d6 100644 --- a/erts/test/erl_print_SUITE.erl +++ b/erts/test/erl_print_SUITE.erl @@ -165,11 +165,11 @@ chk_display(Term, Expect) when is_list(Expect) -> Dstr = erts_debug:display(Term), case Expect ++ io_lib:nl() of Dstr -> - ?t:format("Test of \"~p\" succeeded.~n" + io:format("Test of \"~p\" succeeded.~n" " Expected and got: ~s~n", [Term, io_lib:write_string(Dstr)]); DoExpect -> - ?t:format("***~n" + io:format("***~n" "*** Test of \"~p\" failed!~n" "*** Expected: ~s~n" "*** Got: ~s~n" @@ -240,14 +240,14 @@ port_prog_killer(EProc, OSProc) when is_pid(EProc), is_list(OSProc) -> element(1, Reason) == timetrap_timeout -> Cmd = "kill -9 " ++ OSProc, - ?t:format("Test case timed out. " + io:format("Test case timed out. " "Trying to kill port program.~n" " Executing: ~p~n", [Cmd]), case os:cmd(Cmd) of [] -> ok; OsCmdRes -> - ?t:format(" ~s", [OsCmdRes]) + io:format(" ~s", [OsCmdRes]) end; {'DOWN', Ref, _, _, _} -> %% OSProc is assumed to have terminated by itself @@ -275,7 +275,7 @@ read_case_data(Port, TestCase) -> {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} -> ct:fail(get_line(Port, Flag, ReasonStart)); {Port, {data, {eol, [?PID_MARKER | PidStr]}}} -> - ?t:format("Port program pid: ~s~n", [PidStr]), + io:format("Port program pid: ~s~n", [PidStr]), CaseProc = self(), _ = list_to_integer(PidStr), % Sanity check spawn_opt(fun () -> @@ -284,7 +284,7 @@ read_case_data(Port, TestCase) -> [{priority, max}, link]), read_case_data(Port, TestCase); {Port, {data, {Flag, LineStart}}} -> - ?t:format("~s~n", [get_line(Port, Flag, LineStart)]), + io:format("~s~n", [get_line(Port, Flag, LineStart)]), read_case_data(Port, TestCase); {Port, eof} -> ct:fail(port_prog_unexpectedly_closed) diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index 1c1a2f3836..fa85ee9b68 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -220,14 +220,14 @@ port_prog_killer(EProc, OSProc) when is_pid(EProc), is_list(OSProc) -> element(1, Reason) == timetrap_timeout -> Cmd = "kill -9 " ++ OSProc, - ?t:format("Test case timed out. " + io:format("Test case timed out. " "Trying to kill port program.~n" " Executing: ~p~n", [Cmd]), case os:cmd(Cmd) of [] -> ok; OsCmdRes -> - ?t:format(" ~s", [OsCmdRes]) + io:format(" ~s", [OsCmdRes]) end; %% OSProc is assumed to have terminated by itself {'DOWN', Ref, _, _, _} -> @@ -255,7 +255,7 @@ read_case_data(Port, TestCase) -> {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} -> ct:fail(get_line(Port, Flag, ReasonStart)); {Port, {data, {eol, [?PID_MARKER | PidStr]}}} -> - ?t:format("Port program pid: ~s~n", [PidStr]), + io:format("Port program pid: ~s~n", [PidStr]), CaseProc = self(), _ = list_to_integer(PidStr), % Sanity check spawn_opt(fun () -> @@ -264,7 +264,7 @@ read_case_data(Port, TestCase) -> [{priority, max}, link]), read_case_data(Port, TestCase); {Port, {data, {Flag, LineStart}}} -> - ?t:format("~s~n", [get_line(Port, Flag, LineStart)]), + io:format("~s~n", [get_line(Port, Flag, LineStart)]), read_case_data(Port, TestCase); {Port, eof} -> ct:fail(port_prog_unexpectedly_closed) diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl index cf5a145755..4f244d2476 100644 --- a/erts/test/install_SUITE.erl +++ b/erts/test/install_SUITE.erl @@ -539,13 +539,13 @@ bin_no_srcfile(Config) when is_list(Config) -> %% expect(X, X) -> - ?t:format("result: ~p~n", [X]), - ?t:format("-----------------------------------------------~n", []), + io:format("result: ~p~n", [X]), + io:format("-----------------------------------------------~n", []), ok; expect(X, Y) -> - ?t:format("expected: ~p~n", [X]), - ?t:format("got : ~p~n", [Y]), - ?t:format("-----------------------------------------------~n", []), + io:format("expected: ~p~n", [X]), + io:format("got : ~p~n", [Y]), + io:format("-----------------------------------------------~n", []), ct:fail({X,Y}). init_per_suite(Config) -> @@ -690,7 +690,7 @@ install_bin3(Config, ++ "\" --exec-prefix \"" ++ EXEC_PREFIX ++ "\" --test-file \"" ++ ResFile ++ "\" erl erlc", - ?t:format("CMD_PRFX = \"~s\"~n" + io:format("CMD_PRFX = \"~s\"~n" "LN_S = \"~s\"~n" "BINDIR_SYMLINKS = \"~s\"~n" "exec_prefix = \"~s\"~n" @@ -701,9 +701,9 @@ install_bin3(Config, [CMD_PRFX, LN_S, BINDIR_SYMLINKS, EXEC_PREFIX, BINDIR, ERLANG_BINDIR, EXTRA_PREFIX, DESTDIR]), - ?t:format("$ ~s~n", [Cmd]), + io:format("$ ~s~n", [Cmd]), CmdOutput = os:cmd(Cmd), - ?t:format("~s~n", [CmdOutput]), + io:format("~s~n", [CmdOutput]), ChkRes(case file:consult(ResFile) of {ok, [Res]} -> Res; Err -> exit({result, Err}) -- cgit v1.2.3 From 51f4cb8a43162df3aa2caade4535db31fb979b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 Apr 2016 16:35:57 +0200 Subject: Eliminate use of doc and suite clauses --- erts/test/erl_print_SUITE.erl | 4 --- erts/test/erlexec_SUITE.erl | 22 ++------------ erts/test/ethread_SUITE.erl | 70 +++++++++---------------------------------- erts/test/nt_SUITE.erl | 58 +++++++++++++---------------------- erts/test/z_SUITE.erl | 5 ---- 5 files changed, 37 insertions(+), 122 deletions(-) (limited to 'erts') diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl index d52caeb9d6..f68a85b3e5 100644 --- a/erts/test/erl_print_SUITE.erl +++ b/erts/test/erl_print_SUITE.erl @@ -55,8 +55,6 @@ end_per_testcase(_Case, _Config) -> %% %% -erlang_display(doc) -> []; -erlang_display(suite) -> []; erlang_display(Config) when is_list(Config) -> put(erlang_display_test, ok), OAIS = erts_debug:set_internal_state(available_internal_state, true), @@ -222,8 +220,6 @@ ref_numbers_xstr([N | Ns]) -> %% %% -default_testcase_impl(doc) -> []; -default_testcase_impl(suite) -> []; default_testcase_impl(Config) when is_list(Config) -> run_case(Config). -define(TESTPROG, "erl_print_tests"). diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index 969270e21b..a80d3b43a5 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -53,11 +53,8 @@ all() -> [args_file, evil_args_file, env, args_file_env, otp_7461, otp_8209, zdbbl_dist_buf_busy_limit]. -otp_8209(doc) -> - ["Test that plain first argument does not " - "destroy -home switch [OTP-8209]"]; -otp_8209(suite) -> - []; +%% Test that plain first argument does not +%% destroy -home switch [OTP-8209] otp_8209(Config) when is_list(Config) -> {ok,[[PName]]} = init:get_argument(progname), SNameS = "erlexec_test_01", @@ -101,8 +98,6 @@ loop_ping(Node,N) -> pong end. -args_file(doc) -> []; -args_file(suite) -> []; args_file(Config) when is_list(Config) -> AFN1 = privfile("1", Config), AFN2 = privfile("2", Config), @@ -175,8 +170,6 @@ args_file(Config) when is_list(Config) -> Extra), ok. -evil_args_file(doc) -> []; -evil_args_file(suite) -> []; evil_args_file(Config) when is_list(Config) -> Lim = 300, FNums = lists:seq(1, Lim), @@ -214,8 +207,6 @@ evil_args_file(Config) when is_list(Config) -> -env(doc) -> []; -env(suite) -> []; env(Config) when is_list(Config) -> os:putenv("ERL_AFLAGS", "-MiscArg1 +#100 -extra +XtraArg1 +XtraArg2"), CmdLine = "+#200 -MiscArg2 -extra +XtraArg3 +XtraArg4", @@ -230,8 +221,6 @@ env(Config) when is_list(Config) -> Extra), ok. -args_file_env(doc) -> []; -args_file_env(suite) -> []; args_file_env(Config) when is_list(Config) -> AFN1 = privfile("1", Config), AFN2 = privfile("2", Config), @@ -254,8 +243,6 @@ args_file_env(Config) when is_list(Config) -> ok. %% Make sure "erl -detached" survives when parent process group gets killed -otp_7461(doc) -> []; -otp_7461(suite) -> []; otp_7461(Config) when is_list(Config) -> case os:type() of {unix,_} -> @@ -328,10 +315,7 @@ otp_7461_remote([halt, Pid]) -> io:format("halt order from ~p to node ~p\n",[Pid,node()]), halt(). -zdbbl_dist_buf_busy_limit(doc) -> - ["Check +zdbbl flag"]; -zdbbl_dist_buf_busy_limit(suite) -> - []; +%% Check +zdbbl flag zdbbl_dist_buf_busy_limit(Config) when is_list(Config) -> LimKB = 1122233, LimB = LimKB*1024, diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index fa85ee9b68..7bea9e0ecf 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -86,52 +86,31 @@ end_per_testcase(_Case, _Config) -> %% %% -create_join_thread(doc) -> - ["Tests ethr_thr_create and ethr_thr_join."]; -create_join_thread(suite) -> - []; +%% Tests ethr_thr_create and ethr_thr_join. create_join_thread(Config) -> run_case(Config, "create_join_thread", ""). -equal_tids(doc) -> - ["Tests ethr_equal_tids."]; -equal_tids(suite) -> - []; +%% Tests ethr_equal_tids. equal_tids(Config) -> run_case(Config, "equal_tids", ""). -mutex(doc) -> - ["Tests mutexes."]; -mutex(suite) -> - []; +%% Tests mutexes. mutex(Config) -> run_case(Config, "mutex", ""). -try_lock_mutex(doc) -> - ["Tests try lock on mutex."]; -try_lock_mutex(suite) -> - []; +%% Tests try lock on mutex. try_lock_mutex(Config) -> run_case(Config, "try_lock_mutex", ""). -cond_wait(doc) -> - ["Tests ethr_cond_wait with ethr_cond_signal and ethr_cond_broadcast."]; -cond_wait(suite) -> - []; +%% Tests ethr_cond_wait with ethr_cond_signal and ethr_cond_broadcast. cond_wait(Config) -> run_case(Config, "cond_wait", ""). -broadcast(doc) -> - ["Tests that a ethr_cond_broadcast really wakes up all waiting threads"]; -broadcast(suite) -> - []; +%% Tests that a ethr_cond_broadcast really wakes up all waiting threads broadcast(Config) -> run_case(Config, "broadcast", ""). -detached_thread(doc) -> - ["Tests detached threads."]; -detached_thread(suite) -> - []; +%% Tests detached threads. detached_thread(Config) -> case {os:type(), os:version()} of {{unix,darwin}, {9, _, _}} -> @@ -143,10 +122,7 @@ detached_thread(Config) -> run_case(Config, "detached_thread", "") end. -max_threads(doc) -> - ["Tests maximum number of threads."]; -max_threads(suite) -> - []; +%% Tests maximum number of threads. max_threads(Config) -> case {os:type(), os:version()} of {{unix,darwin}, {9, _, _}} -> @@ -158,45 +134,27 @@ max_threads(Config) -> run_case(Config, "max_threads", "") end. -tsd(doc) -> - ["Tests thread specific data."]; -tsd(suite) -> - []; +%% Tests thread specific data. tsd(Config) -> run_case(Config, "tsd", ""). -spinlock(doc) -> - ["Tests spinlocks."]; -spinlock(suite) -> - []; +%% Tests spinlocks. spinlock(Config) -> run_case(Config, "spinlock", ""). -rwspinlock(doc) -> - ["Tests rwspinlocks."]; -rwspinlock(suite) -> - []; +%% Tests rwspinlocks. rwspinlock(Config) -> run_case(Config, "rwspinlock", ""). -rwmutex(doc) -> - ["Tests rwmutexes."]; -rwmutex(suite) -> - []; +%% Tests rwmutexes. rwmutex(Config) -> run_case(Config, "rwmutex", ""). -atomic(doc) -> - ["Tests atomics."]; -atomic(suite) -> - []; +%% Tests atomics. atomic(Config) -> run_case(Config, "atomic", ""). -dw_atomic_massage(doc) -> - ["Massage double word atomics"]; -dw_atomic_massage(suite) -> - []; +%% Massage double word atomics dw_atomic_massage(Config) -> run_case(Config, "dw_atomic_massage", ""). diff --git a/erts/test/nt_SUITE.erl b/erts/test/nt_SUITE.erl index cdce035438..be24018b5d 100644 --- a/erts/test/nt_SUITE.erl +++ b/erts/test/nt_SUITE.erl @@ -123,9 +123,7 @@ make_full_name(Name) -> %%% The following tests are only run on NT: -service_basic(doc) -> - ["Check some basic (cosmetic) service parameters"]; -service_basic(suite) -> []; +%% Check some basic (cosmetic) service parameters service_basic(Config) when is_list(Config) -> Name = "test_service_20", IntName = Name++"_internal", @@ -160,10 +158,8 @@ service_basic(Config) when is_list(Config) -> remove_service(NewName), ok. -service_env(doc) -> - ["Check that service name and executable is in the environment of the " ++ - "erlang process created by erlsrv."]; -service_env(suite) -> []; +%% Check that service name and executable is in the environment of the +%% erlang process created by erlsrv. service_env(Config) when is_list(Config) -> Name = "test_service_2", Service = [{servicename,Name}, @@ -183,10 +179,9 @@ service_env(Config) when is_list(Config) -> "\""))), remove_service(Name), ok. -user_env(doc) -> - ["Check that the user defined environment is ADDED to the service's"++ - " normal dito."]; -user_env(suite) -> []; + +%% Check that the user defined environment is ADDED to the service's +%% normal dito. user_env(Config) when is_list(Config) -> Name = "test_service_3", Service = [{servicename,Name},{env,[{"HUBBA","BUBBA"}]}, @@ -199,10 +194,9 @@ user_env(Config) when is_list(Config) -> "BUBBA" = rpc:call(make_full_name(Name),os,getenv,["HUBBA"]), remove_service(Name), ok. -synced(doc) -> - ["Check that services are stopped and started syncronous and that"++ - " failed stopactions kill the erlang machine anyway."]; -synced(suite) -> []; + +%% Check that services are stopped and started syncronous and that +%% failed stopactions kill the erlang machine anyway. synced(Config) when is_list(Config) -> Name0 = "test_service_4", Service0 = [{servicename,Name0}, @@ -232,10 +226,9 @@ synced(Config) when is_list(Config) -> calendar:universal_time()) - T2, true = Diff2 > 30, ok. -service_prio(doc) -> - ["Check that a service with higher prio create port programs with " - "higher prio."]; -service_prio(suite) -> []; + +%% Check that a service with higher prio create port programs with +%% higher prio. service_prio(Config) when is_list(Config) -> Name = "test_service_6", Service = [{servicename,Name},{prio,"high"}, @@ -255,16 +248,14 @@ service_prio(Config) when is_list(Config) -> %% started at the same time... {value, {"heart.exe",_,"high"}} = lists:keysearch("heart.exe",1,Diff), ok. -logout(doc) -> - ["Check that logout does not kill services"]; -logout(suite) -> []; + +%% Check that logout does not kill services logout(Config) when is_list(Config) -> {comment, "Have to be run manually by registering a service with " ++ "heart, logout and log in again and then examine that the heart " ++ "process id is not changed."}. -debug(doc) -> - ["Check the debug options to erlsrv."]; -debug(suite) -> []; + +%% Check the debug options to erlsrv. debug(Config) when is_list(Config) -> Name0 = "test_service_7", @@ -311,9 +302,7 @@ debug(Config) when is_list(Config) -> file:delete(NF), ok. -restart(doc) -> - ["Check the restart options to erlsrv"]; -restart(suite) -> []; +%% Check the restart options to erlsrv restart(Config) when is_list(Config) -> Name = "test_service_9", Service = [{servicename,Name}, @@ -333,9 +322,7 @@ restart(Config) when is_list(Config) -> remove_service(Name), ok. -restart_always(doc) -> - ["Check the restart options to erlsrv"]; -restart_always(suite) -> []; +%% Check the restart options to erlsrv restart_always(Config) when is_list(Config) -> Name = "test_service_10", Service = [{servicename,Name}, @@ -353,9 +340,8 @@ restart_always(Config) when is_list(Config) -> true = wait_for_node(Name), remove_service(Name), ok. -stopaction(doc) -> - ["Check that stopaction does not hang output while shutting down"]; -stopaction(suite) -> []; + +%% Check that stopaction does not hang output while shutting down stopaction(Config) when is_list(Config) -> Name = "test_service_11", %% Icky, I prepend the first element in the codepath, cause @@ -380,10 +366,6 @@ stopaction(Config) when is_list(Config) -> %%% This test is run on all platforms, but just gives a comment on %%% other platforms than NT. -nt(doc) -> - ["Run NT specific tests."]; -nt(suite) -> - []; nt(Config) when is_list(Config) -> case os:type() of {win32,nt} -> diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl index 204f393e93..de3e1c24a4 100644 --- a/erts/test/z_SUITE.erl +++ b/erts/test/z_SUITE.erl @@ -45,11 +45,6 @@ suite() -> all() -> [core_files]. - -core_files(doc) -> - []; -core_files(suite) -> - []; core_files(Config) when is_list(Config) -> case os:type() of {win32, _} -> -- cgit v1.2.3 From 336489820c3d3ea89813d9cfd1e0cb667d3a7707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 11 Aug 2015 18:00:21 +0200 Subject: erts: Teach lttng to configure and build system Introduce a wrapper API for lttng. --- erts/configure.in | 59 +++++++++++++++++++++++---- erts/emulator/Makefile.in | 4 +- erts/emulator/beam/erl_bif_info.c | 3 ++ erts/emulator/beam/erlang_lttng.c | 32 +++++++++++++++ erts/emulator/beam/erlang_lttng.h | 36 +++++++++++++++++ erts/emulator/beam/lttng-wrapper.h | 83 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 208 insertions(+), 9 deletions(-) create mode 100644 erts/emulator/beam/erlang_lttng.c create mode 100644 erts/emulator/beam/erlang_lttng.h create mode 100644 erts/emulator/beam/lttng-wrapper.h (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index cae3843465..979106f01b 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -257,8 +257,8 @@ AS_HELP_STRING([--enable-m32-build], ],enable_m32_build=no) AC_ARG_WITH(dynamic-trace, -AS_HELP_STRING([--with-dynamic-trace={dtrace|systemtap}], - [specify use of dynamic trace framework, dtrace or systemtap]) +AS_HELP_STRING([--with-dynamic-trace={dtrace|lttng|systemtap}], + [specify use of dynamic trace framework, dtrace, lttng or systemtap]) AS_HELP_STRING([--without-dynamic-trace], [don't enable any dynamic tracing (default)])) @@ -268,6 +268,10 @@ fi case "$with_dynamic_trace" in no) DYNAMIC_TRACE_FRAMEWORK=;; + lttng) + AC_DEFINE(USE_LTTNG,[1], + [Define if you want to use lttng for dynamic tracing]) + DYNAMIC_TRACE_FRAMEWORK=lttng;; dtrace) AC_DEFINE(USE_DTRACE,[1], [Define if you want to use dtrace for dynamic tracing]) @@ -303,10 +307,12 @@ AS_HELP_STRING([--enable-vm-probes], fi) AC_SUBST(USE_VM_PROBES) -if test X"$use_vm_probes" = X"yes"; then - USE_VM_PROBES=yes - AC_DEFINE(USE_VM_PROBES,[1], - [Define to enable VM dynamic trace probes]) +if test X"$DYNAMIC_TRACE_FRAMEWORK" != X"lttng"; then + if test X"$use_vm_probes" = X"yes"; then + USE_VM_PROBES=yes + AC_DEFINE(USE_VM_PROBES,[1], + [Define to enable VM dynamic trace probes]) + fi fi AC_ARG_WITH(assumed-cache-line-size, @@ -3839,14 +3845,20 @@ dnl LM_FIND_EMU_CC dnl -dnl DTrace +dnl DTrace & LTTNG dnl case $DYNAMIC_TRACE_FRAMEWORK in dtrace|systemtap) AC_CHECK_TOOL(DTRACE, dtrace, none) test "$DTRACE" = "none" && AC_MSG_ERROR([No dtrace utility found.]); + enable_lttng_test=no enable_dtrace_test=yes;; - *) enable_dtrace_test=no;; + lttng) + enable_lttng_test=yes + enable_dtrace_test=no;; + *) + enable_lttng_test=no + enable_dtrace_test=no;; esac AC_SUBST(DTRACE) @@ -3913,6 +3925,37 @@ if test "$enable_dtrace_test" = "yes" ; then fi fi +if test "$enable_lttng_test" = "yes" ; then + AC_CHECK_HEADERS(lttng/tracepoint.h) + AC_CHECK_HEADERS(lttng/tracepoint-event.h) + dnl The macro tracepoint_enabled is not present in older lttng versions + dnl checking for tracepoint_enabled + AC_MSG_CHECKING([for tracepoint_enabled in lttng/tracepoint.h]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include + #define TRACEPOINT_PROVIDER com_ericsson_otp + TRACEPOINT_EVENT( + com_ericsson_otp, + dummy, + TP_ARGS(int, my_int), + TP_FIELDS(ctf_integer(int, my_int, my_int))) + #define TRACEPOINT_CREATE_PROBES + #define TRACEPOINT_DEFINE], + [if(tracepoint_enabled(com_ericsson_otp,dummy)) do {} while(0)])], + [AC_MSG_RESULT([yes])], + [AC_MSG_ERROR([no (must be present)])]) + if test "x$ac_cv_header_lttng_tracepoint_h" = "xyes" \ + -a "x$ac_cv_header_lttng_tracepoint_event_h" = "xyes"; then + # No straight forward way to test for liblttng-ust when no public symbol exists, + # just add the lib. + LIBS="$LIBS -llttng-ust -ldl" + else + AC_MSG_ERROR([No LTTng support found.]) + fi +fi + + dnl dnl SSL, SSH and CRYPTO need the OpenSSL libraries dnl diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 12148ad9c7..3a5cfddc30 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -779,6 +779,8 @@ RUN_OBJS = \ $(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o \ $(OBJDIR)/erl_msacc.o +LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o + ifeq ($(TARGET),win32) DRV_OBJS = \ $(OBJDIR)/registry_drv.o \ @@ -885,7 +887,7 @@ ifdef HIPE_ENABLED EXTRA_BASE_OBJS += $(HIPE_OBJS) endif -BASE_OBJS = $(EMU_OBJS) $(RUN_OBJS) $(OS_OBJS) $(EXTRA_BASE_OBJS) +BASE_OBJS = $(EMU_OBJS) $(RUN_OBJS) $(OS_OBJS) $(EXTRA_BASE_OBJS) $(LTTNG_OBJS) before_DTrace_OBJS = $(BASE_OBJS) $(DRV_OBJS) diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 8c748c9bf7..5c03e33b74 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -126,6 +126,9 @@ static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE #ifdef ERTS_FRMPTR " [frame-pointer]" #endif +#ifdef USE_LTTNG + " [lttng]" +#endif #ifdef USE_DTRACE " [dtrace]" #endif diff --git a/erts/emulator/beam/erlang_lttng.c b/erts/emulator/beam/erlang_lttng.c new file mode 100644 index 0000000000..fce40eedc1 --- /dev/null +++ b/erts/emulator/beam/erlang_lttng.c @@ -0,0 +1,32 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2016. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef USE_LTTNG +#define TRACEPOINT_CREATE_PROBES +/* + * The header containing our TRACEPOINT_EVENTs. + */ +#define TRACEPOINT_DEFINE +#include "erlang_lttng.h" +#endif /* USE_LTTNG */ diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h new file mode 100644 index 0000000000..48956cf242 --- /dev/null +++ b/erts/emulator/beam/erlang_lttng.h @@ -0,0 +1,36 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2016. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifdef USE_LTTNG +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER com_ericsson_otp + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "erlang_lttng.h" + +#if !defined(__ERLANG_LTTNG_H__) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define __ERLANG_LTTNG_H__ + +#include + + +#endif /* __ERLANG_LTTNG_H__ */ +#include +#endif /* USE_LTTNG */ diff --git a/erts/emulator/beam/lttng-wrapper.h b/erts/emulator/beam/lttng-wrapper.h new file mode 100644 index 0000000000..8362dc555e --- /dev/null +++ b/erts/emulator/beam/lttng-wrapper.h @@ -0,0 +1,83 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2016. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef __LTTNG_WRAPPER_H__ +#define __LTTNG_WRAPPER_H__ + +#ifdef USE_LTTNG + +#include "erlang_lttng.h" +#define USE_LTTNG_VM_TRACEPOINTS + +#define LTTNG_BUFFER_SZ (256) +#define LTTNG_PROC_BUFFER_SZ (16) +#define LTTNG_PORT_BUFFER_SZ (20) + +#define lttng_decl_procbuf(Name) \ + char Name[LTTNG_PROC_BUFFER_SZ] + +#define lttng_decl_portbuf(Name) \ + char Name[LTTNG_PORT_BUFFER_SZ] + +#define lttng_pid_to_str(pid, name) \ + erts_snprintf(name, LTTNG_PROC_BUFFER_SZ, "%T", (pid)) + +#define lttng_portid_to_str(pid, name) \ + erts_snprintf(name, LTTNG_PORT_BUFFER_SZ, "%T", (pid)) + +#define lttng_proc_to_str(p, name) \ + lttng_pid_to_str(((p) ? (p)->common.id : ERTS_INVALID_PID), name) + +#define lttng_port_to_str(p, name) \ + lttng_portid_to_str(((p) ? (p)->common.id : ERTS_INVALID_PORT), name) + +/* ErtsRunQueue->ErtsSchedulerData->Uint */ +#define lttng_rq_to_id(RQ) \ + (RQ)->scheduler->no + +#define LTTNG_ENABLED(Name) \ + tracepoint_enabled(com_ericsson_otp, Name) + +/* include a special LTTNG_DO for do_tracepoint ? */ +#define LTTNG1(Name, Arg1) \ + tracepoint(com_ericsson_otp, Name, (Arg1)) + +#define LTTNG2(Name, Arg1, Arg2) \ + tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2)) + +#define LTTNG3(Name, Arg1, Arg2, Arg3) \ + tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3)) + +#define LTTNG4(Name, Arg1, Arg2, Arg3, Arg4) \ + tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4)) + +#define LTTNG5(Name, Arg1, Arg2, Arg3, Arg4, Arg5) \ + tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) + +#else /* USE_LTTNG */ + +#define LTTNG1(Name, Arg1) do {} while(0) +#define LTTNG2(Name, Arg1, Arg2) do {} while(0) +#define LTTNG3(Name, Arg1, Arg2, Arg3) do {} while(0) +#define LTTNG4(Name, Arg1, Arg2, Arg3, Arg4) do {} while(0) +#define LTTNG5(Name, Arg1, Arg2, Arg3, Arg4, Arg5) do {} while(0) + +#endif /* USE_LTTNG */ +#endif /* __LTTNG_WRAPPER_H__ */ -- cgit v1.2.3 From 955cd62970391b40123f2b9dee3bb3380233c17e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 28 Jan 2016 11:45:34 +0100 Subject: erts: Update lttng-wrapper with mfa conversion --- erts/emulator/beam/lttng-wrapper.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/lttng-wrapper.h b/erts/emulator/beam/lttng-wrapper.h index 8362dc555e..294872c365 100644 --- a/erts/emulator/beam/lttng-wrapper.h +++ b/erts/emulator/beam/lttng-wrapper.h @@ -29,6 +29,7 @@ #define LTTNG_BUFFER_SZ (256) #define LTTNG_PROC_BUFFER_SZ (16) #define LTTNG_PORT_BUFFER_SZ (20) +#define LTTNG_MFA_BUFFER_SZ (256) #define lttng_decl_procbuf(Name) \ char Name[LTTNG_PROC_BUFFER_SZ] @@ -36,6 +37,12 @@ #define lttng_decl_portbuf(Name) \ char Name[LTTNG_PORT_BUFFER_SZ] +#define lttng_decl_mfabuf(Name) \ + char Name[LTTNG_MFA_BUFFER_SZ] + +#define lttng_decl_carrier_stats(Name) \ + lttng_carrier_stats_t Name##_STATSTRUCT, *Name = &Name##_STATSTRUCT + #define lttng_pid_to_str(pid, name) \ erts_snprintf(name, LTTNG_PROC_BUFFER_SZ, "%T", (pid)) @@ -48,6 +55,23 @@ #define lttng_port_to_str(p, name) \ lttng_portid_to_str(((p) ? (p)->common.id : ERTS_INVALID_PORT), name) +#define lttng_mfa_to_str(m,f,a, Name) \ + erts_snprintf(Name, LTTNG_MFA_BUFFER_SZ, "%T:%T/%lu", (Eterm)(m), (Eterm)(f), (Uint)(a)) + +#define lttng_proc_to_mfa_str(p, Name) \ + do { \ + if (ERTS_PROC_IS_EXITING((p))) { \ + strcpy(Name, ""); \ + } else { \ + BeamInstr *_fptr = find_function_from_pc((p)->i); \ + if (_fptr) { \ + lttng_mfa_to_str(_fptr[0],_fptr[1],_fptr[2], Name); \ + } else { \ + strcpy(Name, ""); \ + } \ + } \ + } while(0) + /* ErtsRunQueue->ErtsSchedulerData->Uint */ #define lttng_rq_to_id(RQ) \ (RQ)->scheduler->no -- cgit v1.2.3 From ce5d152cd863b68dcc2b5c7a566d1e5e1a5c5dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 14 Aug 2015 18:20:51 +0200 Subject: erts: Add lttng tracepoints for memory carriers * carrier_create * carrier_destroy * carrier_pool_put * carrier_pool_get --- erts/emulator/beam/erl_alloc.h | 8 ++- erts/emulator/beam/erl_alloc_util.c | 33 +++++++++++++ erts/emulator/beam/erl_alloc_util.h | 13 +++++ erts/emulator/beam/erlang_lttng.h | 99 +++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index 71e4713624..ee2013bd93 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -235,9 +235,9 @@ void *erts_alloc(ErtsAlcType_t type, Uint size) void *res; ERTS_MSACC_PUSH_AND_SET_STATE_X(ERTS_MSACC_STATE_ALLOC); res = (*erts_allctrs[ERTS_ALC_T2A(type)].alloc)( - ERTS_ALC_T2N(type), - erts_allctrs[ERTS_ALC_T2A(type)].extra, - size); + ERTS_ALC_T2N(type), + erts_allctrs[ERTS_ALC_T2A(type)].extra, + size); if (!res) erts_alloc_n_enomem(ERTS_ALC_T2N(type), size); ERTS_MSACC_POP_STATE_X(); @@ -564,5 +564,3 @@ NAME##_free(TYPE *p) \ #undef ERTS_ALC_ATTRIBUTES #endif /* #ifndef ERL_ALLOC_H__ */ - - diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 5e7dd7cce8..6e682019ba 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -52,6 +52,7 @@ #ifdef ERTS_ENABLE_LOCK_COUNT #include "erl_lock_count.h" #endif +#include "lttng-wrapper.h" #if defined(ERTS_ALLOC_UTIL_HARD_DEBUG) && defined(__GNUC__) #warning "* * * * * * * * * *" @@ -3125,6 +3126,7 @@ cpool_insert(Allctr_t *allctr, Carrier_t *crr) erts_smp_atomic_set_wb(&crr->allctr, ((erts_aint_t) allctr)|ERTS_CRR_ALCTR_FLG_IN_POOL); + LTTNG3(carrier_pool_put, ERTS_ALC_A2AD(allctr->alloc_no), allctr->ix, CARRIER_SZ(crr)); } static void @@ -3240,6 +3242,7 @@ cpool_fetch(Allctr_t *allctr, UWord size) first_old_traitor = allctr->cpool.traitor_list.next; cpool_entrance = NULL; + LTTNG3(carrier_pool_get, ERTS_ALC_A2AD(allctr->alloc_no), allctr->ix, (unsigned long)size); /* * Search my own pooled_list, * i.e my abandoned carriers that were in the pool last time I checked. @@ -3925,6 +3928,21 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) } +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(carrier_create)) { + lttng_decl_carrier_stats(mbc_stats); + lttng_decl_carrier_stats(sbc_stats); + LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->mbcs), mbc_stats); + LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->sbcs), sbc_stats); + LTTNG5(carrier_create, + ERTS_ALC_A2AD(allctr->alloc_no), + allctr->ix, + crr_sz, + mbc_stats, + sbc_stats); + } +#endif + DEBUG_SAVE_ALIGNMENT(crr); return blk; } @@ -4148,6 +4166,21 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk, Carrier_t **busy_pcrr_pp) allctr->remove_mbc(allctr, crr); } +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(carrier_destroy)) { + lttng_decl_carrier_stats(mbc_stats); + lttng_decl_carrier_stats(sbc_stats); + LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->mbcs), mbc_stats); + LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->sbcs), sbc_stats); + LTTNG5(carrier_destroy, + ERTS_ALC_A2AD(allctr->alloc_no), + allctr->ix, + crr_sz, + mbc_stats, + sbc_stats); + } +#endif + #ifdef ERTS_SMP schedule_dealloc_carrier(allctr, crr); #else diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index b7d717ed23..afdff1a71e 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -30,6 +30,7 @@ #endif #include "erl_mseg.h" +#include "lttng-wrapper.h" #define ERTS_AU_PREF_ALLOC_BITS 11 #define ERTS_AU_MAX_PREF_ALLOC_INSTANCES (1 << ERTS_AU_PREF_ALLOC_BITS) @@ -417,6 +418,18 @@ typedef struct { } blocks; } CarriersStats_t; +#ifdef USE_LTTNG_VM_TRACEPOINTS +#define LTTNG_CARRIER_STATS_TO_LTTNG_STATS(CSP, LSP) \ + do { \ + (LSP)->carriers.size = (CSP)->curr.norm.mseg.size \ + + (CSP)->curr.norm.sys_alloc.size; \ + (LSP)->carriers.no = (CSP)->curr.norm.mseg.no \ + + (CSP)->curr.norm.sys_alloc.no; \ + (LSP)->blocks.size = (CSP)->blocks.curr.size; \ + (LSP)->blocks.no = (CSP)->blocks.curr.no; \ + } while (0) +#endif + #ifdef ERTS_SMP typedef union ErtsAllctrDDBlock_t_ ErtsAllctrDDBlock_t; diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h index 48956cf242..2fab6fb698 100644 --- a/erts/emulator/beam/erlang_lttng.h +++ b/erts/emulator/beam/erlang_lttng.h @@ -31,6 +31,105 @@ #include +#ifndef LTTNG_CARRIER_STATS +#define LTTNG_CARRIER_STATS +typedef struct { + unsigned long no; + unsigned long size; +} lttng_stat_values_t; + +typedef struct { + lttng_stat_values_t carriers; + lttng_stat_values_t blocks; +} lttng_carrier_stats_t; +#endif + + +/* Memory Allocator */ + +TRACEPOINT_EVENT( + com_ericsson_otp, + carrier_create, + TP_ARGS( + const char*, type, + int, instance, + unsigned long, size, + lttng_carrier_stats_t *, mbcs, + lttng_carrier_stats_t *, sbcs + ), + TP_FIELDS( + ctf_string(type, type) + ctf_integer(int, instance, instance) + ctf_integer(unsigned long, size, size) + ctf_float(float, mbc_ratio, (float)mbcs->blocks.size/(float)mbcs->carriers.size) + ctf_integer(unsigned long, mbc_carriers, mbcs->carriers.no) + ctf_integer(unsigned long, mbc_carriers_size, mbcs->carriers.size) + ctf_integer(unsigned long, mbc_blocks, mbcs->blocks.no) + ctf_integer(unsigned long, mbc_blocks_size, mbcs->blocks.size) + ctf_integer(unsigned long, sbc_carriers, sbcs->carriers.no) + ctf_integer(unsigned long, sbc_carriers_size, sbcs->carriers.size) + ctf_integer(unsigned long, sbc_blocks, sbcs->blocks.no) + ctf_integer(unsigned long, sbc_blocks_size, sbcs->blocks.size) + ) +) + + +TRACEPOINT_EVENT( + com_ericsson_otp, + carrier_destroy, + TP_ARGS( + const char*, type, + int, instance, + unsigned long, size, + lttng_carrier_stats_t *, mbcs, + lttng_carrier_stats_t *, sbcs + ), + TP_FIELDS( + ctf_string(type, type) + ctf_integer(int, instance, instance) + ctf_integer(unsigned long, size, size) + ctf_float(float, mbc_ratio, (float)mbcs->blocks.size/(float)mbcs->carriers.size) + ctf_integer(unsigned long, mbc_carriers, mbcs->carriers.no) + ctf_integer(unsigned long, mbc_carriers_size, mbcs->carriers.size) + ctf_integer(unsigned long, mbc_blocks, mbcs->blocks.no) + ctf_integer(unsigned long, mbc_blocks_size, mbcs->blocks.size) + ctf_integer(unsigned long, sbc_carriers, sbcs->carriers.no) + ctf_integer(unsigned long, sbc_carriers_size, sbcs->carriers.size) + ctf_integer(unsigned long, sbc_blocks, sbcs->blocks.no) + ctf_integer(unsigned long, sbc_blocks_size, sbcs->blocks.size) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + carrier_pool_put, + TP_ARGS( + const char*, name, + int, instance, + unsigned long, size + ), + TP_FIELDS( + ctf_string(type, name) + ctf_integer(int, instance, instance) + ctf_integer(unsigned long, size, size) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + carrier_pool_get, + TP_ARGS( + const char*, name, + int, instance, + unsigned long, size + ), + TP_FIELDS( + ctf_string(type, name) + ctf_integer(int, instance, instance) + ctf_integer(unsigned long, size, size) + ) +) + #endif /* __ERLANG_LTTNG_H__ */ #include #endif /* USE_LTTNG */ -- cgit v1.2.3 From 73344dc2a5451c6f2e4b1ea4f69de17aa358e88f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 17 Aug 2015 15:25:26 +0200 Subject: erts: Add lttng tracepoints for scheduler events * scheduler_poll --- erts/emulator/beam/erl_process.c | 8 +++++++- erts/emulator/beam/erlang_lttng.h | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a706ebf595..46b45b1d3e 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -43,6 +43,7 @@ #include "erl_thr_queue.h" #include "erl_async.h" #include "dtrace-wrapper.h" +#include "lttng-wrapper.h" #include "erl_ptab.h" #include "erl_bif_unique.h" #define ERTS_WANT_TIMER_WHEEL_API @@ -3238,6 +3239,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO); ASSERT(!erts_port_task_have_outstanding_io_tasks()); + LTTNG2(scheduler_poll, esdp->no, 1); erl_sys_schedule(1); /* Might give us something to do */ ERTS_MSACC_POP_STATE_M(); @@ -3361,6 +3363,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ASSERT(!erts_port_task_have_outstanding_io_tasks()); ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO); + LTTNG2(scheduler_poll, esdp->no, 0); erl_sys_schedule(0); @@ -9581,7 +9584,10 @@ Process *schedule(Process *p, int calls) erts_sys_schedule_interrupt(0); #endif erts_smp_runq_unlock(rq); - ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO); + + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO); + LTTNG2(scheduler_poll, esdp->no, 1); + erl_sys_schedule(1); ERTS_MSACC_POP_STATE_M(); diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h index 2fab6fb698..5b5dc5548e 100644 --- a/erts/emulator/beam/erlang_lttng.h +++ b/erts/emulator/beam/erlang_lttng.h @@ -30,6 +30,20 @@ #include +/* Schedulers */ + +TRACEPOINT_EVENT( + com_ericsson_otp, + scheduler_poll, + TP_ARGS( + int, id, + int, runnable + ), + TP_FIELDS( + ctf_integer(int, scheduler, id) + ctf_integer(int, runnable, runnable) + ) +) #ifndef LTTNG_CARRIER_STATS #define LTTNG_CARRIER_STATS -- cgit v1.2.3 From d31e4e33ac73c0c3239a3bf8a2919165bbed9e88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 22 Jan 2016 11:52:13 +0100 Subject: erts: Add lttng tracepoints for drivers * driver_event * driver_flush * driver_finish * driver_init * driver_output * driver_outputv * driver_process_exit * driver_ready_async * driver_ready_input * driver_ready_output * driver_start * driver_stop * driver_stop_select * driver_timeout --- erts/emulator/beam/erl_bif_ddll.c | 2 + erts/emulator/beam/erl_port_task.c | 17 +++ erts/emulator/beam/erlang_lttng.h | 249 ++++++++++++++++++++++++++++++++ erts/emulator/beam/io.c | 92 +++++++++++- erts/emulator/sys/common/erl_check_io.c | 3 + 5 files changed, 362 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index f006856b6a..b526eda41d 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -48,6 +48,7 @@ #include "erl_version.h" #include "erl_bif_unique.h" #include "dtrace-wrapper.h" +#include "lttng-wrapper.h" #ifdef ERTS_SMP #define DDLL_SMP 1 @@ -1619,6 +1620,7 @@ static int do_unload_driver_entry(DE_Handle *dh, Eterm *save_name) if (q->finish) { int fpe_was_unmasked = erts_block_fpe(); DTRACE1(driver_finish, q->name); + LTTNG1(driver_finish, q->name); (*(q->finish))(); erts_unblock_fpe(fpe_was_unmasked); } diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index e85395e062..377e47adde 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -35,6 +35,7 @@ #include "dist.h" #include "erl_check_io.h" #include "dtrace-wrapper.h" +#include "lttng-wrapper.h" #include /* @@ -69,6 +70,18 @@ static void chk_task_queues(Port *pp, ErtsPortTask *execq, int processing_busy_q #else #define DTRACE_DRIVER(PROBE_NAME, PP) do {} while(0) #endif +#ifdef USE_LTTNG_VM_TRACEPOINTS +#define LTTNG_DRIVER(TRACEPOINT, PP) \ + if (LTTNG_ENABLED(TRACEPOINT)) { \ + lttng_decl_portbuf(port_str); \ + lttng_decl_procbuf(proc_str); \ + lttng_pid_to_str(ERTS_PORT_GET_CONNECTED(PP), proc_str); \ + lttng_port_to_str((PP), port_str); \ + LTTNG3(TRACEPOINT, proc_str, port_str, (PP)->name); \ + } +#else +#define LTTNG_DRIVER(TRACEPOINT, PP) do {} while(0) +#endif #define ERTS_SMP_LC_VERIFY_RQ(RQ, PP) \ do { \ @@ -1728,6 +1741,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) reds = ERTS_PORT_REDS_TIMEOUT; if (!(state & ERTS_PORT_SFLGS_DEAD)) { DTRACE_DRIVER(driver_timeout, pp); + LTTNG_DRIVER(driver_timeout, pp); (*pp->drv_ptr->timeout)((ErlDrvData) pp->drv_data); } } @@ -1736,6 +1750,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) reds = ERTS_PORT_REDS_INPUT; ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0); DTRACE_DRIVER(driver_ready_input, pp); + LTTNG_DRIVER(driver_ready_input, pp); /* NOTE some windows drivers use ->ready_input for input and output */ (*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data, @@ -1747,6 +1762,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) reds = ERTS_PORT_REDS_OUTPUT; ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0); DTRACE_DRIVER(driver_ready_output, pp); + LTTNG_DRIVER(driver_ready_output, pp); (*pp->drv_ptr->ready_output)((ErlDrvData) pp->drv_data, ptp->u.alive.td.io.event); reset_executed_io_task_handle(ptp); @@ -1756,6 +1772,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) reds = ERTS_PORT_REDS_EVENT; ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0); DTRACE_DRIVER(driver_event, pp); + LTTNG_DRIVER(driver_event, pp); (*pp->drv_ptr->event)((ErlDrvData) pp->drv_data, ptp->u.alive.td.io.event, ptp->u.alive.td.io.event_data); diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h index 5b5dc5548e..b5cd90d93e 100644 --- a/erts/emulator/beam/erlang_lttng.h +++ b/erts/emulator/beam/erlang_lttng.h @@ -59,6 +59,255 @@ typedef struct { #endif +/* Port and Driver Scheduling */ + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_start, + TP_ARGS( + char*, pid, + char*, driver, + char*, port + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(driver, driver) + ctf_string(port, port) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_init, + TP_ARGS( + char*, driver, + int, major, + int, minor, + int, flags + ), + TP_FIELDS( + ctf_string(driver, driver) + ctf_integer(int, major, major) + ctf_integer(int, minor, minor) + ctf_integer(int, flags, flags) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_outputv, + TP_ARGS( + char*, pid, + char*, port, + char*, driver, + size_t, bytes + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(port, port) + ctf_string(driver, driver) + ctf_integer(size_t, bytes, bytes) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_output, + TP_ARGS( + char*, pid, + char*, port, + char*, driver, + size_t, bytes + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(port, port) + ctf_string(driver, driver) + ctf_integer(size_t, bytes, bytes) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_ready_input, + TP_ARGS( + char*, pid, + char*, port, + char*, driver + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(port, port) + ctf_string(driver, driver) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_ready_output, + TP_ARGS( + char*, pid, + char*, port, + char*, driver + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(port, port) + ctf_string(driver, driver) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_event, + TP_ARGS( + char*, pid, + char*, port, + char*, driver + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(port, port) + ctf_string(driver, driver) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_timeout, + TP_ARGS( + char*, pid, + char*, port, + char*, driver + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(port, port) + ctf_string(driver, driver) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_stop_select, + TP_ARGS( + char*, driver + ), + TP_FIELDS( + ctf_string(driver, driver) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_flush, + TP_ARGS( + char*, pid, + char*, port, + char*, driver + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(port, port) + ctf_string(driver, driver) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_stop, + TP_ARGS( + char*, pid, + char*, driver, + char*, port + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(driver, driver) + ctf_string(port, port) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_process_exit, + TP_ARGS( + char*, pid, + char*, port, + char*, driver + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(port, port) + ctf_string(driver, driver) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_ready_async, + TP_ARGS( + char*, pid, + char*, port, + char*, driver + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(port, port) + ctf_string(driver, driver) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_finish, + TP_ARGS( + char*, driver + ), + TP_FIELDS( + ctf_string(driver, driver) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_call, + TP_ARGS( + char*, pid, + char*, port, + char*, driver, + unsigned int, command, + size_t, bytes + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(port, port) + ctf_string(driver, driver) + ctf_integer(unsigned int, command, command) + ctf_integer(size_t, bytes, bytes) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + driver_control, + TP_ARGS( + char*, pid, + char*, port, + char*, driver, + unsigned int, command, + size_t, bytes + ), + TP_FIELDS( + ctf_string(pid, pid) + ctf_string(port, port) + ctf_string(driver, driver) + ctf_integer(unsigned int, command, command) + ctf_integer(size_t, bytes, bytes) + ) +) + + /* Memory Allocator */ TRACEPOINT_EVENT( diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 797e4cdadc..b213be23db 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -47,6 +47,7 @@ #define ERTS_WANT_EXTERNAL_TAGS #include "external.h" #include "dtrace-wrapper.h" +#include "lttng-wrapper.h" #include "erl_map.h" #include "erl_bif_unique.h" #include "erl_hl_timer.h" @@ -717,7 +718,19 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ DTRACE3(driver_start, process_str, driver->name, port_str); } #endif + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); + +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(driver_start)) { + lttng_decl_portbuf(port_str); + lttng_decl_procbuf(proc_str); + lttng_pid_to_str(pid, proc_str); + lttng_port_to_str(port, port_str); + LTTNG3(driver_start, proc_str, driver->name, port_str); + } +#endif + fpe_was_unmasked = erts_block_fpe(); drv_data = (*driver->start)(ERTS_Port2ErlDrvPort(port), name, opts); if (((SWord) drv_data) == -1) @@ -1724,6 +1737,15 @@ call_driver_outputv(int bang_op, DTRACE4(driver_outputv, process_str, port_str, prt->name, size); } #endif +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(driver_outputv)) { + lttng_decl_portbuf(port_str); + lttng_decl_procbuf(proc_str); + lttng_pid_to_str(caller, proc_str); + lttng_port_to_str(prt, port_str); + LTTNG4(driver_outputv, proc_str, port_str, prt->name, size); + } +#endif prt->caller = caller; (*drv->outputv)((ErlDrvData) prt->drv_data, evp); @@ -1826,6 +1848,15 @@ call_driver_output(int bang_op, DTRACE4(driver_output, process_str, port_str, prt->name, size); } #endif +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(driver_output)) { + lttng_decl_portbuf(port_str); + lttng_decl_procbuf(proc_str); + lttng_pid_to_str(caller, proc_str); + lttng_port_to_str(prt, port_str); + LTTNG4(driver_output, proc_str, port_str, prt->name, size); + } +#endif prt->caller = caller; (*drv->output)((ErlDrvData) prt->drv_data, bufp, size); @@ -1949,7 +1980,6 @@ erts_port_output(Process *c_p, DTRACE4(port_command, process_str, port_str, prt->name, "command"); } #endif - if (drv->outputv) { ErlIOVec ev; SysIOVec iv[SMALL_WRITE_VEC]; @@ -3489,6 +3519,17 @@ static void flush_port(Port *p) DTRACE3(driver_flush, process_str, port_str, p->name); } #endif +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(driver_flush)) { + lttng_decl_portbuf(port_str); + lttng_decl_procbuf(proc_str); + lttng_pid_to_str(ERTS_PORT_GET_CONNECTED(p), proc_str); + lttng_port_to_str(p, port_str); + LTTNG3(driver_flush, proc_str, port_str, p->name); + } +#endif + + if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) { trace_sched_ports_where(p, am_in, am_flush); } @@ -3551,6 +3592,16 @@ terminate_port(Port *prt) DTRACE3(driver_stop, process_str, drv->name, port_str); } #endif +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(driver_stop)) { + lttng_decl_portbuf(port_str); + lttng_decl_procbuf(proc_str); + lttng_pid_to_str(connected_id, proc_str); + lttng_port_to_str(prt, port_str); + LTTNG3(driver_stop, proc_str, drv->name, port_str); + } +#endif + (*drv->stop)((ErlDrvData)prt->drv_data); erts_unblock_fpe(fpe_was_unmasked); ERTS_MSACC_POP_STATE_M(); @@ -3881,6 +3932,16 @@ call_driver_control(Eterm caller, ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(driver_control)) { + lttng_decl_procbuf(proc_str); + lttng_decl_portbuf(port_str); + lttng_pid_to_str(caller, proc_str); + lttng_port_to_str(prt, port_str); + LTTNG5(driver_control, proc_str, port_str, prt->name, command, size); + } +#endif + prt->caller = caller; cres = prt->drv_ptr->control((ErlDrvData) prt->drv_data, command, @@ -4294,6 +4355,15 @@ call_driver_call(Eterm caller, DTRACE5(driver_call, process_str, port_str, prt->name, command, size); } #endif +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(driver_call)) { + lttng_decl_procbuf(proc_str); + lttng_decl_portbuf(port_str); + lttng_pid_to_str(caller,proc_str); + lttng_port_to_str(prt, port_str); + LTTNG5(driver_call, proc_str, port_str, prt->name, command, size); + } +#endif ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); @@ -5055,6 +5125,15 @@ int async_ready(Port *p, void* data) DTRACE_FORMAT_COMMON_PID_AND_PORT(ERTS_PORT_GET_CONNECTED(p), p) DTRACE3(driver_ready_async, process_str, port_str, p->name); } +#endif +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(driver_ready_async)) { + lttng_decl_portbuf(port_str); + lttng_decl_procbuf(proc_str); + lttng_pid_to_str(ERTS_PORT_GET_CONNECTED(p), proc_str); + lttng_port_to_str(p, port_str); + LTTNG3(driver_ready_async, proc_str, port_str, p->name); + } #endif (*p->drv_ptr->ready_async)((ErlDrvData)p->drv_data, data); need_free = 0; @@ -7101,6 +7180,15 @@ void erts_fire_port_monitor(Port *prt, Eterm ref) DTRACE_FORMAT_COMMON_PID_AND_PORT(ERTS_PORT_GET_CONNECTED(prt), prt) DTRACE3(driver_process_exit, process_str, port_str, prt->name); } +#endif +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(driver_process_exit)) { + lttng_decl_portbuf(port_str); + lttng_decl_procbuf(proc_str); + lttng_pid_to_str(ERTS_PORT_GET_CONNECTED(prt), proc_str); + lttng_port_to_str(prt, port_str); + LTTNG3(driver_process_exit, proc_str, port_str, prt->name); + } #endif fpe_was_unmasked = erts_block_fpe(); (*callback)((ErlDrvData) (prt->drv_data), &drv_monitor); @@ -7579,6 +7667,8 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle) int fpe_was_unmasked = erts_block_fpe(); DTRACE4(driver_init, drv->name, drv->version.major, drv->version.minor, drv->flags); + LTTNG4(driver_init, drv->name, drv->version.major, drv->version.minor, + drv->flags); res = (*de->init)(); erts_unblock_fpe(fpe_was_unmasked); return res; diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index f87196d724..0d5043fa2a 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -39,6 +39,7 @@ #include "erl_check_io.h" #include "erl_thr_progress.h" #include "dtrace-wrapper.h" +#include "lttng-wrapper.h" #define ERTS_WANT_TIMER_WHEEL_API #include "erl_time.h" @@ -395,6 +396,7 @@ forget_removed(struct pollset_info* psi) if (drv_ptr) { int was_unmasked = erts_block_fpe(); DTRACE1(driver_stop_select, drv_ptr->name); + LTTNG1(driver_stop_select, drv_ptr->name); (*drv_ptr->stop_select) ((ErlDrvEvent) fd, NULL); erts_unblock_fpe(was_unmasked); if (drv_ptr->handle) { @@ -1055,6 +1057,7 @@ done_unknown: if (stop_select_fn) { int was_unmasked = erts_block_fpe(); DTRACE1(driver_stop_select, name); + LTTNG1(driver_stop_select, "unknown"); (*stop_select_fn)(e, NULL); erts_unblock_fpe(was_unmasked); } -- cgit v1.2.3 From 8735e04e031284bd73d0cf5b9fddebf624623c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 19 Aug 2015 17:43:33 +0200 Subject: erts: Add lttng tracepoints for async pool queue * aio_pool_get * aio_pool_add --- erts/emulator/beam/erl_async.c | 16 ++++++++++++++++ erts/emulator/beam/erl_thr_queue.c | 32 ++++++++++++++++++++++++++++++++ erts/emulator/beam/erl_thr_queue.h | 4 ++++ erts/emulator/beam/erlang_lttng.h | 28 ++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index cdeeb5281b..69240f7886 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -28,6 +28,7 @@ #include "erl_thr_queue.h" #include "erl_async.h" #include "dtrace-wrapper.h" +#include "lttng-wrapper.h" #define ERTS_MAX_ASYNC_READY_CALLS_IN_SEQ 20 @@ -281,6 +282,13 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q) #endif erts_thr_q_enqueue(&q->thr_q, a); +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(aio_pool_add)) { + lttng_decl_portbuf(port_str); + lttng_portid_to_str(a->port, port_str); + LTTNG2(aio_pool_add, port_str, -1); + } +#endif #ifdef USE_VM_PROBES if (DTRACE_ENABLED(aio_pool_add)) { DTRACE_CHARBUF(port_str, 16); @@ -317,6 +325,14 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, if (saved_fin_deq) erts_thr_q_append_finalize_dequeue_data(&a->q.fin_deq, &fin_deq); #endif +#ifdef USE_LTTNG_VM_TRACEPOINTS + if (LTTNG_ENABLED(aio_pool_get)) { + lttng_decl_portbuf(port_str); + int length = erts_thr_q_length_dirty(q); + lttng_portid_to_str(a->port, port_str); + LTTNG2(aio_pool_get, port_str, length); + } +#endif #ifdef USE_VM_PROBES if (DTRACE_ENABLED(aio_pool_get)) { DTRACE_CHARBUF(port_str, 16); diff --git a/erts/emulator/beam/erl_thr_queue.c b/erts/emulator/beam/erl_thr_queue.c index 7ff456b915..30c9d70c59 100644 --- a/erts/emulator/beam/erl_thr_queue.c +++ b/erts/emulator/beam/erl_thr_queue.c @@ -780,3 +780,35 @@ erts_thr_q_dequeue(ErtsThrQ_t *q) return res; #endif } + +#ifdef USE_LTTNG_VM_TRACEPOINTS +int +erts_thr_q_length_dirty(ErtsThrQ_t *q) +{ + int n = 0; +#ifndef USE_THREADS + void *res; + ErtsThrQElement_t *tmp; + + for (tmp = q->first; tmp != NULL; tmp = tmp->next) { + n++; + } +#else + ErtsThrQElement_t *e; + erts_aint_t inext; + + e = ErtsThrQDirtyReadEl(&q->head.head); + inext = erts_atomic_read_acqb(&e->next); + + while (inext != ERTS_AINT_NULL) { + e = (ErtsThrQElement_t *) inext; + if (e != &q->tail.data.marker) { + /* don't count marker */ + n++; + } + inext = erts_atomic_read_acqb(&e->next); + } +#endif + return n; +} +#endif diff --git a/erts/emulator/beam/erl_thr_queue.h b/erts/emulator/beam/erl_thr_queue.h index 27a6d03224..f5e5522948 100644 --- a/erts/emulator/beam/erl_thr_queue.h +++ b/erts/emulator/beam/erl_thr_queue.h @@ -190,6 +190,10 @@ void erts_thr_q_append_finalize_dequeue_data(ErtsThrQFinDeQ_t *, int erts_thr_q_finalize_dequeue(ErtsThrQFinDeQ_t *); void erts_thr_q_finalize_dequeue_state_init(ErtsThrQFinDeQ_t *); +#ifdef USE_LTTNG_VM_TRACEPOINTS +int erts_thr_q_length_dirty(ErtsThrQ_t *); +#endif + #ifdef ERTS_SMP ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_q_need_thr_progress(ErtsThrQ_t *q); #endif diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h index b5cd90d93e..a202ce68c9 100644 --- a/erts/emulator/beam/erlang_lttng.h +++ b/erts/emulator/beam/erlang_lttng.h @@ -307,6 +307,34 @@ TRACEPOINT_EVENT( ) ) +/* Async pool */ + +TRACEPOINT_EVENT( + com_ericsson_otp, + aio_pool_get, + TP_ARGS( + char*, port, + int, length + ), + TP_FIELDS( + ctf_string(port, port) + ctf_integer(int, length, length) + ) +) + +TRACEPOINT_EVENT( + com_ericsson_otp, + aio_pool_add, + TP_ARGS( + char*, port, + int, length + ), + TP_FIELDS( + ctf_string(port, port) + ctf_integer(int, length, length) + ) +) + /* Memory Allocator */ -- cgit v1.2.3 From 977d50fcbc119a2d6d3bf2512605e62a3f22741b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 21 Jan 2016 10:33:40 +0100 Subject: Refactor and fix dtrace define in erl_message --- erts/emulator/beam/erl_message.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 88efb2c59f..bc0a55068b 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -766,17 +766,7 @@ erts_send_message(Process* sender, utag = DT_UTAG(sender); else utag = copy_struct(DT_UTAG(sender), dt_utag_size, &hp, ohp); -#ifdef DTRACE_TAG_HARDDEBUG - erts_fprintf(stderr, - "Dtrace -> (%T) Spreading tag (%T) with " - "message %T!\r\n",sender->common.id, utag, message); -#endif } -#endif - BM_MESSAGE_COPIED(msize); - BM_SWAP_TIMER(copy,send); - -#ifdef USE_VM_PROBES if (DTRACE_ENABLED(message_send)) { if (have_seqtrace(stoken)) { tok_label = signed_val(SEQ_TRACE_T_LABEL(stoken)); @@ -787,6 +777,9 @@ erts_send_message(Process* sender, msize, tok_label, tok_lastcnt, tok_serial); } #endif + BM_MESSAGE_COPIED(msize); + BM_SWAP_TIMER(copy,send); + } else { Eterm *hp; @@ -822,8 +815,10 @@ erts_send_message(Process* sender, BM_MESSAGE_COPIED(msz); BM_SWAP_TIMER(copy,send); } +#ifdef USE_VM_PROBES DTRACE6(message_send, sender_name, receiver_name, msize, tok_label, tok_lastcnt, tok_serial); +#endif } res = queue_message(sender, -- cgit v1.2.3 From 84f2e9b3b1bb3990a7bea7b9d45768ee1a820804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Mar 2016 15:06:06 +0100 Subject: erts: Extend erlang:system_info/1 with lttng Let erlang:system_info(dynamic_trace) be able to return 'lttng' if enabled. --- erts/emulator/beam/erl_bif_info.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 5c03e33b74..dd796199bb 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2751,6 +2751,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) #elif defined(USE_SYSTEMTAP) DECL_AM(systemtap); BIF_RET(AM_systemtap); +#elif defined(USE_LTTNG) + DECL_AM(lttng); + BIF_RET(AM_lttng); #else BIF_RET(am_none); #endif -- cgit v1.2.3 From 9e3dd3743153e79583c876809af8e16ab582f185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 18 Mar 2016 19:40:35 +0100 Subject: Add lttng testcases --- erts/emulator/test/Makefile | 1 + erts/emulator/test/lttng_SUITE.erl | 499 +++++++++++++++++++++++ erts/emulator/test/lttng_SUITE_data/Makefile.src | 7 + erts/emulator/test/lttng_SUITE_data/caller_drv.c | 159 ++++++++ 4 files changed, 666 insertions(+) create mode 100644 erts/emulator/test/lttng_SUITE.erl create mode 100644 erts/emulator/test/lttng_SUITE_data/Makefile.src create mode 100644 erts/emulator/test/lttng_SUITE_data/caller_drv.c (limited to 'erts') diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 318db4b45e..0f716c11a1 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -70,6 +70,7 @@ MODULES= \ hash_SUITE \ hibernate_SUITE \ list_bif_SUITE \ + lttng_SUITE \ map_SUITE \ match_spec_SUITE \ module_info_SUITE \ diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl new file mode 100644 index 0000000000..d0f6292d5b --- /dev/null +++ b/erts/emulator/test/lttng_SUITE.erl @@ -0,0 +1,499 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(lttng_SUITE). + +-export([all/0, suite/0]). +-export([init_per_suite/1, end_per_suite/1]). +-export([init_per_testcase/2, end_per_testcase/2]). + +-export([t_lttng_list/1, + t_carrier_pool/1, + t_memory_carrier/1, + t_async_io_pool/1, + t_driver_control_ready_async/1, + t_driver_start_stop/1, + t_driver_ready_input_output/1, + t_driver_timeout/1, + t_driver_caller/1, + t_driver_flush/1, + t_scheduler_poll/1]). + +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 10}}]. + +all() -> + [t_lttng_list, + t_carrier_pool, + t_async_io_pool, + t_driver_start_stop, + t_driver_ready_input_output, + t_driver_control_ready_async, + t_driver_timeout, + t_driver_caller, + t_driver_flush, + t_scheduler_poll, + t_memory_carrier]. + + +init_per_suite(Config) -> + case erlang:system_info(dynamic_trace) of + lttng -> + ensure_lttng_stopped("--all"), + Config; + _ -> + {skip, "No LTTng configured on system."} + end. + +end_per_suite(_Config) -> + ensure_lttng_stopped("--all"), + ok. + +init_per_testcase(Case, Config) -> + Name = atom_to_list(Case), + ok = ensure_lttng_started(Name, Config), + [{session, Name}|Config]. + +end_per_testcase(Case, _Config) -> + Name = atom_to_list(Case), + ok = ensure_lttng_stopped(Name), + ok. + +%% Not tested yet +%% com_ericsson_otp:driver_process_exit +%% com_ericsson_otp:driver_event + +%% tracepoints +%% +%% com_ericsson_otp:carrier_pool_get +%% com_ericsson_otp:carrier_pool_put +%% com_ericsson_otp:carrier_destroy +%% com_ericsson_otp:carrier_create +%% com_ericsson_otp:aio_pool_add +%% com_ericsson_otp:aio_pool_get +%% com_ericsson_otp:driver_control +%% com_ericsson_otp:driver_call +%% com_ericsson_otp:driver_finish +%% com_ericsson_otp:driver_ready_async +%% com_ericsson_otp:driver_process_exit +%% com_ericsson_otp:driver_stop +%% com_ericsson_otp:driver_flush +%% com_ericsson_otp:driver_stop_select +%% com_ericsson_otp:driver_timeout +%% com_ericsson_otp:driver_event +%% com_ericsson_otp:driver_ready_output +%% com_ericsson_otp:driver_ready_input +%% com_ericsson_otp:driver_output +%% com_ericsson_otp:driver_outputv +%% com_ericsson_otp:driver_init +%% com_ericsson_otp:driver_start +%% com_ericsson_otp:scheduler_poll + +%% +%% Testcases +%% + +t_lttng_list(_Config) -> + {ok, _} = cmd("lttng list -u"), + ok. + +%% com_ericsson_otp:carrier_pool_get +%% com_ericsson_otp:carrier_pool_put +t_carrier_pool(Config) -> + case have_carriers() of + false -> + {skip, "No Memory Carriers configured on system."}; + true -> + ok = lttng_start_event("com_ericsson_otp:carrier_pool*", Config), + + ok = ets_load(), + + Res = lttng_stop_and_view(Config), + ok = check_tracepoint("com_ericsson_otp:carrier_pool_get", Res), + ok = check_tracepoint("com_ericsson_otp:carrier_pool_put", Res), + ok + end. + +%% com_ericsson_otp:carrier_destroy +%% com_ericsson_otp:carrier_create +t_memory_carrier(Config) -> + case have_carriers() of + false -> + {skip, "No Memory Carriers configured on system."}; + true -> + ok = lttng_start_event("com_ericsson_otp:carrier_*", Config), + + ok = ets_load(), + + Res = lttng_stop_and_view(Config), + ok = check_tracepoint("com_ericsson_otp:carrier_destroy", Res), + ok = check_tracepoint("com_ericsson_otp:carrier_create", Res), + ok + end. + +%% com_ericsson_otp:aio_pool_add +%% com_ericsson_otp:aio_pool_get +t_async_io_pool(Config) -> + case have_async_threads() of + false -> + {skip, "No Async Threads configured on system."}; + true -> + ok = lttng_start_event("com_ericsson_otp:aio_pool_*", Config), + + Path1 = proplists:get_value(priv_dir, Config), + {ok, [[Path2]]} = init:get_argument(home), + {ok, _} = file:list_dir(Path1), + {ok, _} = file:list_dir(Path2), + {ok, _} = file:list_dir(Path1), + {ok, _} = file:list_dir(Path2), + + Res = lttng_stop_and_view(Config), + ok = check_tracepoint("com_ericsson_otp:aio_pool_add", Res), + ok = check_tracepoint("com_ericsson_otp:aio_pool_get", Res), + ok + end. + + +%% com_ericsson_otp:driver_start +%% com_ericsson_otp:driver_stop +t_driver_start_stop(Config) -> + ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + Path = proplists:get_value(priv_dir, Config), + Name = filename:join(Path, "sometext.txt"), + Bin = txt(), + ok = file:write_file(Name, Bin), + {ok, Bin} = file:read_file(Name), + Res = lttng_stop_and_view(Config), + ok = check_tracepoint("com_ericsson_otp:driver_start", Res), + ok = check_tracepoint("com_ericsson_otp:driver_stop", Res), + ok = check_tracepoint("com_ericsson_otp:driver_control", Res), + ok = check_tracepoint("com_ericsson_otp:driver_outputv", Res), + ok = check_tracepoint("com_ericsson_otp:driver_ready_async", Res), + ok. + +%% com_ericsson_otp:driver_control +%% com_ericsson_otp:driver_outputv +%% com_ericsson_otp:driver_ready_async +t_driver_control_ready_async(Config) -> + ok = lttng_start_event("com_ericsson_otp:driver_control", Config), + ok = lttng_start_event("com_ericsson_otp:driver_outputv", Config), + ok = lttng_start_event("com_ericsson_otp:driver_ready_async", Config), + Path = proplists:get_value(priv_dir, Config), + Name = filename:join(Path, "sometext.txt"), + Bin = txt(), + ok = file:write_file(Name, Bin), + {ok, Bin} = file:read_file(Name), + Res = lttng_stop_and_view(Config), + ok = check_tracepoint("com_ericsson_otp:driver_control", Res), + ok = check_tracepoint("com_ericsson_otp:driver_outputv", Res), + ok = check_tracepoint("com_ericsson_otp:driver_ready_async", Res), + ok. + +%% com_ericsson_otp:driver_ready_input +%% com_ericsson_otp:driver_ready_output +t_driver_ready_input_output(Config) -> + ok = lttng_start_event("com_ericsson_otp:driver_ready_*", Config), + Me = self(), + Pid = spawn_link(fun() -> tcp_server(Me, active) end), + receive {Pid, accept} -> ok end, + Bin = txt(), + Sz = byte_size(Bin), + + {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2}]), + ok = gen_tcp:send(Sock, <>), + ok = gen_tcp:send(Sock, <>), + ok = gen_tcp:close(Sock), + receive {Pid, done} -> ok end, + + Res = lttng_stop_and_view(Config), + ok = check_tracepoint("com_ericsson_otp:driver_ready_input", Res), + ok = check_tracepoint("com_ericsson_otp:driver_ready_output", Res), + ok. + + +%% com_ericsson_otp:driver_stop_select +%% com_ericsson_otp:driver_timeout +t_driver_timeout(Config) -> + ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + Me = self(), + Pid = spawn_link(fun() -> tcp_server(Me, timeout) end), + receive {Pid, accept} -> ok end, + {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary]), + ok = gen_tcp:send(Sock, <<"hej">>), + receive {Pid, done} -> ok end, + ok = gen_tcp:close(Sock), + Res = lttng_stop_and_view(Config), + ok = check_tracepoint("com_ericsson_otp:driver_timeout", Res), + ok = check_tracepoint("com_ericsson_otp:driver_stop_select", Res), + ok. + +%% com_ericsson_otp:driver_call +%% com_ericsson_otp:driver_output +%% com_ericsson_otp:driver_init +%% com_ericsson_otp:driver_finish +t_driver_caller(Config) -> + ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + + Drv = 'caller_drv', + os:putenv("CALLER_DRV_USE_OUTPUTV", "false"), + + ok = load_driver(proplists:get_value(data_dir, Config), Drv), + Port = open_port({spawn, Drv}, []), + true = is_port(Port), + + chk_caller(Port, start, self()), + chk_caller(Port, output, spawn_link(fun() -> + port_command(Port, "") + end)), + Port ! {self(), {command, ""}}, + chk_caller(Port, output, self()), + chk_caller(Port, control, spawn_link(fun () -> + port_control(Port, 0, "") + end)), + chk_caller(Port, call, spawn_link(fun() -> + erlang:port_call(Port, 0, "") + end)), + + true = port_close(Port), + erl_ddll:unload_driver(Drv), + + Res = lttng_stop_and_view(Config), + ok = check_tracepoint("com_ericsson_otp:driver_call", Res), + ok = check_tracepoint("com_ericsson_otp:driver_output", Res), + ok = check_tracepoint("com_ericsson_otp:driver_init", Res), + ok = check_tracepoint("com_ericsson_otp:driver_finish", Res), + ok. + +%% com_ericsson_otp:scheduler_poll +t_scheduler_poll(Config) -> + ok = lttng_start_event("com_ericsson_otp:scheduler_poll", Config), + + ok = memory_load(), + + Res = lttng_stop_and_view(Config), + ok = check_tracepoint("com_ericsson_otp:scheduler_poll", Res), + ok. + +%% com_ericsson_otp:driver_flush +t_driver_flush(Config) -> + ok = lttng_start_event("com_ericsson_otp:driver_flush", Config), + + Me = self(), + Pid = spawn_link(fun() -> tcp_server(Me, passive_no_read) end), + receive {Pid, accept} -> ok end, + Bin = iolist_to_binary([txt() || _ <- lists:seq(1,100)]), + Sz = byte_size(Bin), + + %% We want to create a scenario where sendings stalls and we + %% queue packets in the driver. + %% When we close the socket it has to flush the queue. + {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2}, + {send_timeout, 10}, + {sndbuf, 10000000}]), + Pids = [spawn_link(fun() -> + gen_tcp:send(Sock, <>), + Me ! {self(), ok} + end) || _ <- lists:seq(1,100)], + [receive {P, ok} -> ok end || P <- Pids], + ok = gen_tcp:close(Sock), + Pid ! die, + receive {Pid, done} -> ok end, + + Res = lttng_stop_and_view(Config), + ok = check_tracepoint("com_ericsson_otp:driver_flush", Res), + ok. + +%% +%% AUX +%% + +chk_caller(Port, Callback, ExpectedCaller) -> + receive + {caller, Port, Callback, Caller} -> + ExpectedCaller = Caller + end. + + +ets_load() -> + Tid = ets:new(ets_load, [public,set]), + N = erlang:system_info(schedulers_online), + Pids = [spawn_link(fun() -> ets_shuffle(Tid) end) || _ <- lists:seq(1,N)], + ok = ets_kill(Pids, 500), + ok. + + +ets_kill([], _) -> ok; +ets_kill([Pid|Pids], Time) -> + timer:sleep(Time), + Pid ! done, + ets_kill(Pids, Time). + +ets_shuffle(Tid) -> + Payload = lists:duplicate(100, $x), + ets_shuffle(Tid, 100, Payload). +ets_shuffle(Tid, I, Data) -> + ets_shuffle(Tid, I, I, Data, Data). + +ets_shuffle(Tid, 0, N, _, Data) -> + ets_shuffle(Tid, N, N, Data, Data); +ets_shuffle(Tid, I, N, Data, Data0) -> + receive + done -> ok + after 0 -> + Key = rand:uniform(1000), + Data1 = [I|Data], + ets:insert(Tid, {Key, Data1}), + ets_shuffle(Tid, I - 1, N, Data1, Data0) + end. + + + + +memory_load() -> + Me = self(), + Pids0 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)], + timer:sleep(50), + Pids1 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)], + [receive {Pid, done} -> ok end || Pid <- Pids0 ++ Pids1], + timer:sleep(500), + ok. + +memory_loop(Parent, N, Bin) -> + memory_loop(Parent, N, Bin, []). + +memory_loop(Parent, 0, _Bin, _) -> + Parent ! {self(), done}; +memory_loop(Parent, N, Bin0, Ls) -> + Bin = binary:copy(<>), + memory_loop(Parent, N - 1, Bin, [a,b,c|Ls]). + +tcp_server(Pid, Type) -> + {ok, LSock} = gen_tcp:listen(5679, [binary, + {reuseaddr, true}, + {active, false}]), + Pid ! {self(), accept}, + {ok, Sock} = gen_tcp:accept(LSock), + case Type of + passive_no_read -> + receive die -> ok end; + active -> + inet:setopts(Sock, [{active, once}, {packet,2}]), + receive Msg1 -> io:format("msg1: ~p~n", [Msg1]) end, + inet:setopts(Sock, [{active, once}, {packet,2}]), + receive Msg2 -> io:format("msg2: ~p~n", [Msg2]) end, + ok = gen_tcp:close(Sock); + timeout -> + Res = gen_tcp:recv(Sock, 2000, 1000), + io:format("res ~p~n", [Res]) + end, + Pid ! {self(), done}, + ok. + +txt() -> + <<"%% tracepoints\n" + "%%\n" + "%% com_ericsson_otp:carrier_pool_get\n" + "%% com_ericsson_otp:carrier_pool_put\n" + "%% com_ericsson_otp:carrier_destroy\n" + "%% com_ericsson_otp:carrier_create\n" + "%% com_ericsson_otp:aio_pool_add\n" + "%% com_ericsson_otp:aio_pool_get\n" + "%% com_ericsson_otp:driver_control\n" + "%% com_ericsson_otp:driver_call\n" + "%% com_ericsson_otp:driver_finish\n" + "%% com_ericsson_otp:driver_ready_async\n" + "%% com_ericsson_otp:driver_process_exit\n" + "%% com_ericsson_otp:driver_stop\n" + "%% com_ericsson_otp:driver_flush\n" + "%% com_ericsson_otp:driver_stop_select\n" + "%% com_ericsson_otp:driver_timeout\n" + "%% com_ericsson_otp:driver_event\n" + "%% com_ericsson_otp:driver_ready_output\n" + "%% com_ericsson_otp:driver_ready_input\n" + "%% com_ericsson_otp:driver_output\n" + "%% com_ericsson_otp:driver_outputv\n" + "%% com_ericsson_otp:driver_init\n" + "%% com_ericsson_otp:driver_start\n" + "%% com_ericsson_otp:scheduler_poll">>. + +load_driver(Dir, Driver) -> + case erl_ddll:load_driver(Dir, Driver) of + ok -> ok; + {error, Error} = Res -> + io:format("~s\n", [erl_ddll:format_error(Error)]), + Res + end. + +%% check + +have_carriers() -> + Cap = element(3,erlang:system_info(allocator)), + case Cap -- [sys_alloc,sys_aligned_alloc] of + [] -> false; + _ -> true + end. + +have_async_threads() -> + Tps = erlang:system_info(thread_pool_size), + if Tps =:= 0 -> false; + true -> true + end. + +%% lttng +lttng_stop_and_view(Config) -> + Path = proplists:get_value(priv_dir, Config), + Name = proplists:get_value(session, Config), + {ok,_} = cmd("lttng stop " ++ Name), + {ok,Res} = cmd("lttng view " ++ Name ++ " --trace-path=" ++ Path), + Res. + +check_tracepoint(TP, Data) -> + case re:run(Data, TP, [global]) of + {match, _} -> ok; + _ -> notfound + end. + +lttng_start_event(Event, Config) -> + Name = proplists:get_value(session, Config), + {ok, _} = cmd("lttng enable-event -u " ++ Event ++ " --session=" ++ Name), + {ok, _} = cmd("lttng start " ++ Name), + ok. + +ensure_lttng_started(Name, Config) -> + Out = case proplists:get_value(priv_dir, Config) of + undefined -> []; + Path -> "--output="++Path++" " + end, + {ok,_} = cmd("lttng create " ++ Out ++ Name), + ok. + +ensure_lttng_stopped(Name) -> + {ok,_} = cmd("lttng stop"), + {ok,_} = cmd("lttng destroy " ++ Name), + ok. + +cmd(Cmd) -> + io:format("<< ~ts~n", [Cmd]), + Res = os:cmd(Cmd), + io:format(">> ~ts~n", [Res]), + {ok,Res}. diff --git a/erts/emulator/test/lttng_SUITE_data/Makefile.src b/erts/emulator/test/lttng_SUITE_data/Makefile.src new file mode 100644 index 0000000000..fe7a1b6ef3 --- /dev/null +++ b/erts/emulator/test/lttng_SUITE_data/Makefile.src @@ -0,0 +1,7 @@ + +MISC_DRVS = caller_drv@dll@ + + +all: $(MISC_DRVS) + +@SHLIB_RULES@ diff --git a/erts/emulator/test/lttng_SUITE_data/caller_drv.c b/erts/emulator/test/lttng_SUITE_data/caller_drv.c new file mode 100644 index 0000000000..86fd0a2995 --- /dev/null +++ b/erts/emulator/test/lttng_SUITE_data/caller_drv.c @@ -0,0 +1,159 @@ +/* ``Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +#include +#include +#include "erl_driver.h" + +static int init(); +static void stop(ErlDrvData drv_data); +static void finish(); +static void flush(ErlDrvData drv_data); +static ErlDrvData start(ErlDrvPort port, char *command); +static void output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len); +static void outputv(ErlDrvData drv_data, ErlIOVec *ev); +static ErlDrvSSizeT control(ErlDrvData drv_data, + unsigned int command, char *buf, + ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen); +static ErlDrvSSizeT call(ErlDrvData drv_data, + unsigned int command, + char *buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen, + unsigned int *flags); + +static ErlDrvEntry caller_drv_entry = { + init, + start, + stop, + output, + NULL /* ready_input */, + NULL /* ready_output */, + "caller_drv", + finish, + NULL /* handle */, + control, + NULL /* timeout */, + outputv, + NULL /* ready_async */, + flush, + call, + NULL /* event */, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL /* handle2 */, + NULL /* handle_monitor */ +}; + +DRIVER_INIT(caller_drv) +{ + char buf[10]; + size_t bufsz = sizeof(buf); + char *use_outputv; + use_outputv = (erl_drv_getenv("CALLER_DRV_USE_OUTPUTV", buf, &bufsz) == 0 + ? buf + : "false"); + if (strcmp(use_outputv, "true") != 0) + caller_drv_entry.outputv = NULL; + return &caller_drv_entry; +} + +void +send_caller(ErlDrvData drv_data, char *func) +{ + int res; + ErlDrvPort port = (ErlDrvPort) drv_data; + ErlDrvTermData msg[] = { + ERL_DRV_ATOM, driver_mk_atom("caller"), + ERL_DRV_PORT, driver_mk_port(port), + ERL_DRV_ATOM, driver_mk_atom(func), + ERL_DRV_PID, driver_caller(port), + ERL_DRV_TUPLE, (ErlDrvTermData) 4 + }; + res = erl_drv_output_term(driver_mk_port(port), msg, sizeof(msg)/sizeof(ErlDrvTermData)); + if (res <= 0) + driver_failure_atom(port, "erl_drv_output_term failed"); +} + +static int +init() { + return 0; +} + +static void +stop(ErlDrvData drv_data) +{ + +} + +static void +flush(ErlDrvData drv_data) +{ + +} + +static void +finish() +{ + +} + +static ErlDrvData +start(ErlDrvPort port, char *command) +{ + send_caller((ErlDrvData) port, "start"); + return (ErlDrvData) port; +} + +static void +output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) +{ + send_caller(drv_data, "output"); +} + +static void +outputv(ErlDrvData drv_data, ErlIOVec *ev) +{ + send_caller(drv_data, "outputv"); +} + +static ErlDrvSSizeT +control(ErlDrvData drv_data, + unsigned int command, char *buf, + ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) +{ + send_caller(drv_data, "control"); + return 0; +} + +static ErlDrvSSizeT +call(ErlDrvData drv_data, + unsigned int command, + char *buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen, + unsigned int *flags) +{ + /* echo call */ + if (len > rlen) + *rbuf = driver_alloc(len); + memcpy((void *) *rbuf, (void *) buf, len); + send_caller(drv_data, "call"); + return len; +} -- cgit v1.2.3 From 58762e934f603b3fe036aa3b9d8a5930b77ae5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 5 Apr 2016 18:56:16 +0200 Subject: erts: Don't use ratio in carrier lttng tracepoints --- erts/emulator/beam/erlang_lttng.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h index a202ce68c9..43ceeda671 100644 --- a/erts/emulator/beam/erlang_lttng.h +++ b/erts/emulator/beam/erlang_lttng.h @@ -352,7 +352,6 @@ TRACEPOINT_EVENT( ctf_string(type, type) ctf_integer(int, instance, instance) ctf_integer(unsigned long, size, size) - ctf_float(float, mbc_ratio, (float)mbcs->blocks.size/(float)mbcs->carriers.size) ctf_integer(unsigned long, mbc_carriers, mbcs->carriers.no) ctf_integer(unsigned long, mbc_carriers_size, mbcs->carriers.size) ctf_integer(unsigned long, mbc_blocks, mbcs->blocks.no) @@ -379,7 +378,6 @@ TRACEPOINT_EVENT( ctf_string(type, type) ctf_integer(int, instance, instance) ctf_integer(unsigned long, size, size) - ctf_float(float, mbc_ratio, (float)mbcs->blocks.size/(float)mbcs->carriers.size) ctf_integer(unsigned long, mbc_carriers, mbcs->carriers.no) ctf_integer(unsigned long, mbc_carriers_size, mbcs->carriers.size) ctf_integer(unsigned long, mbc_blocks, mbcs->blocks.no) -- cgit v1.2.3 From 57551877d85ad7659201235e27498be42809fefb Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 18 Feb 2016 13:52:54 +0100 Subject: erts: Add enif_send with NULL as msg env This is an optimization for reducing the number of heap fragments allocated when sending a message where the majority of the message payload is on the sending process' heap. --- erts/doc/src/erl_nif.xml | 4 ++- erts/emulator/beam/erl_nif.c | 36 ++++++++++++++++----------- erts/emulator/test/nif_SUITE.erl | 13 ++++++++-- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 13 ++++++++++ 4 files changed, 48 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 1e95634d1b..1bfd98f664 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -1632,7 +1632,7 @@ enif_map_iterator_destroy(env, &iter); msg_env The environment of the message term. Must be a process independent environment allocated with - enif_alloc_env. + enif_alloc_env or NULL. msg The message term to send. @@ -1641,6 +1641,8 @@ enif_map_iterator_destroy(env, &iter); msg) will be invalidated by a successful call to enif_send. The environment should either be freed with enif_free_env of cleared for reuse with enif_clear_env.

+

If msg_env is set to NULL the msg term is copied and + the original term and its environemt is still valid after the call.

This function is only thread-safe when the emulator with SMP support is used. It can only be used in a non-SMP emulator from a NIF-calling thread.

Passing msg_env as NULL is only supported since diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index d3030070b7..1012ced44b 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -312,7 +312,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, Process* rp; Process* c_p; ErtsMessage *mp; - ErlHeapFragment* frags; Eterm receiver = to_pid->pid; int flush_me = 0; int scheduler = erts_get_scheduler_id() != 0; @@ -340,25 +339,32 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ASSERT(env == NULL || receiver != c_p->common.id); return 0; } - flush_env(msg_env); - frags = menv->env.heap_frag; - ASSERT(frags == MBUF(&menv->phony_proc)); - if (frags != NULL) { - /* Move all offheap's from phony proc to the first fragment. - Quick and dirty... */ - ASSERT(!is_offheap(&frags->off_heap)); - frags->off_heap = MSO(&menv->phony_proc); - clear_offheap(&MSO(&menv->phony_proc)); - menv->env.heap_frag = NULL; - MBUF(&menv->phony_proc) = NULL; + if (menv) { + flush_env(msg_env); + mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = menv->env.heap_frag; + ASSERT(mp->data.heap_frag == MBUF(&menv->phony_proc)); + if (mp->data.heap_frag != NULL) { + /* Move all offheap's from phony proc to the first fragment. + Quick and dirty... */ + ASSERT(!is_offheap(&mp->data.heap_frag->off_heap)); + mp->data.heap_frag->off_heap = MSO(&menv->phony_proc); + clear_offheap(&MSO(&menv->phony_proc)); + menv->env.heap_frag = NULL; + MBUF(&menv->phony_proc) = NULL; + } + ASSERT(!is_offheap(&MSO(&menv->phony_proc))); + } else { + Uint sz = size_object(msg); + Eterm *hp; + mp = erts_alloc_message(sz, &hp); + msg = copy_struct(msg, sz, &hp, &mp->hfrag.off_heap); + ASSERT(hp == mp->hfrag.mem+mp->hfrag.used_size); } - ASSERT(!is_offheap(&MSO(&menv->phony_proc))); if (flush_me) { flush_env(env); /* Needed for ERTS_HOLE_CHECK */ } - mp = erts_alloc_message(0, NULL); - mp->data.heap_frag = frags; erts_queue_message(rp, &rp_locks, mp, msg, am_undefined); if (c_p == rp) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index a185b72341..aa512b2360 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1282,9 +1282,10 @@ send3_make_blob() -> repeat(N bsr 1, fun(_) -> grow_blob(MsgEnv,other_term(),rand:uniform(1 bsl 20)) end, void), - case (N band 1) of + case (N band 3) of 0 -> {term,copy_blob(MsgEnv)}; - 1 -> {msgenv,MsgEnv} + 1 -> {copy,copy_blob(MsgEnv)}; + _ -> {msgenv,MsgEnv} end end. @@ -1297,6 +1298,9 @@ send3_send(Pid, Msg) -> send3_send_nif(Pid, {term,Blob}) -> %%io:format("~p send term nif\n",[self()]), send_term(Pid, {blob, Blob}) =:= 1; +send3_send_nif(Pid, {copy,Blob}) -> + %%io:format("~p send term nif\n",[self()]), + send_copy_term(Pid, {blob, Blob}) =:= 1; send3_send_nif(Pid, {msgenv,MsgEnv}) -> %%io:format("~p send blob nif\n",[self()]), send3_blob(MsgEnv, Pid, blob) =:= 1. @@ -1305,6 +1309,10 @@ send3_send_bang(Pid, {term,Blob}) -> %%io:format("~p send term bang\n",[self()]), Pid ! {blob, Blob}, true; +send3_send_bang(Pid, {copy,Blob}) -> + %%io:format("~p send term bang\n",[self()]), + Pid ! {blob, Blob}, + true; send3_send_bang(Pid, {msgenv,MsgEnv}) -> %%io:format("~p send blob bang\n",[self()]), Pid ! {blob, copy_blob(MsgEnv)}, @@ -2062,6 +2070,7 @@ send_blob_thread(_,_,_) -> ?nif_stub. join_send_thread(_) -> ?nif_stub. copy_blob(_) -> ?nif_stub. send_term(_,_) -> ?nif_stub. +send_copy_term(_,_) -> ?nif_stub. reverse_list(_) -> ?nif_stub. echo_int(_) -> ?nif_stub. type_sizes() -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index b3c6cc5ba3..e369fb3386 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1466,6 +1466,18 @@ static ERL_NIF_TERM send_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ return enif_make_int(env, ret); } +static ERL_NIF_TERM send_copy_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifEnv* menv; + ErlNifPid pid; + int ret; + if (!enif_get_local_pid(env, argv[0], &pid)) { + return enif_make_badarg(env); + } + ret = enif_send(env, &pid, NULL, argv[1]); + return enif_make_int(env, ret); +} + static ERL_NIF_TERM reverse_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM rev_list; @@ -2142,6 +2154,7 @@ static ErlNifFunc nif_funcs[] = {"join_send_thread", 1, join_send_thread}, {"copy_blob", 1, copy_blob}, {"send_term", 2, send_term}, + {"send_copy_term", 2, send_copy_term}, {"reverse_list",1, reverse_list}, {"echo_int", 1, echo_int}, {"type_sizes", 0, type_sizes}, -- cgit v1.2.3 From 6d51b25958393d95dee32baafd708aa3909ddb5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 1 Apr 2016 16:07:37 +0200 Subject: Eliminate allocation of variables in transform_engine() When an instruction with a variable number operands (such as select_val) is seen of the left side of a transformation, the 'next_arg' instruction will allocate a buffer to fit all variables and all operands will be copied into the buffer. Very often, the 'commit' instruction will never be reached because of a test or predicate failing or because of a short window; in that case, the variable buffer will be deallocated. Note that originally there were only few instructions with a variable number of operands, but now common operations such as tuple building also have a variable number of operands. To avoid those frequent allocations and deallocations, modify the 'next_arg' instruction to only save a pointer to the first of the "rest" arguments. Also move the deallocation of the instructions on the left side from the 'commit' instruction to the 'end' instruction to ensure that 'store_rest_args' will still work. --- erts/emulator/beam/beam_load.c | 94 ++++++++++++---------------------------- erts/emulator/utils/beam_makeops | 65 ++++++++++----------------- 2 files changed, 49 insertions(+), 110 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 2f2b433999..c6c35e74c9 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4813,31 +4813,25 @@ transform_engine(LoaderState* st) Uint op; int ap; /* Current argument. */ Uint* restart; /* Where to restart if current match fails. */ - GenOpArg def_vars[TE_MAX_VARS]; /* Default buffer for variables. */ - GenOpArg* var = def_vars; - int num_vars = 0; + GenOpArg var[TE_MAX_VARS]; /* Buffer for variables. */ + GenOpArg* rest_args = NULL; + int num_rest_args = 0; int i; /* General index. */ Uint mask; GenOp* instr; + GenOp* first = st->genop; + GenOp* keep = NULL; Uint* pc; - int rval; static Uint restart_fail[1] = {TOP_fail}; - ASSERT(gen_opc[st->genop->op].transform != -1); - pc = op_transform + gen_opc[st->genop->op].transform; - restart = pc; + ASSERT(gen_opc[first->op].transform != -1); + restart = op_transform + gen_opc[first->op].transform; restart: - if (var != def_vars) { - erts_free(ERTS_ALC_T_LOADER_TMP, (void *) var); - var = def_vars; - } ASSERT(restart != NULL); pc = restart; ASSERT(*pc < NUM_TOPS); /* Valid instruction? */ - instr = st->genop; - -#define RETURN(r) rval = (r); goto do_return; + instr = first; #ifdef DEBUG restart = NULL; @@ -4855,7 +4849,7 @@ transform_engine(LoaderState* st) * We'll need at least one more instruction to decide whether * this combination matches or not. */ - RETURN(TE_SHORT_WINDOW); + return TE_SHORT_WINDOW; } if (*pc++ != instr->op) goto restart; @@ -5017,19 +5011,9 @@ transform_engine(LoaderState* st) #if defined(TOP_rest_args) case TOP_rest_args: { - int n = *pc++; int formal_arity = gen_opc[instr->op].arity; - int j = formal_arity; - - num_vars = n + (instr->arity - formal_arity); - var = erts_alloc(ERTS_ALC_T_LOADER_TMP, - num_vars * sizeof(GenOpArg)); - for (i = 0; i < n; i++) { - var[i] = def_vars[i]; - } - while (i < num_vars) { - var[i++] = instr->a[j++]; - } + num_rest_args = instr->arity - formal_arity; + rest_args = instr->a + formal_arity; } break; #endif @@ -5038,16 +5022,8 @@ transform_engine(LoaderState* st) break; case TOP_commit: instr = instr->next; /* The next_instr was optimized away. */ - - /* - * The left-hand side of this transformation matched. - * Delete all matched instructions. - */ - while (st->genop != instr) { - GenOp* next = st->genop->next; - FREE_GENOP(st, st->genop); - st->genop = next; - } + keep = instr; + st->genop = instr; #ifdef DEBUG instr = 0; #endif @@ -5077,22 +5053,19 @@ transform_engine(LoaderState* st) lastp = &((*lastp)->next); } - instr = instr->next; /* The next_instr was optimized away. */ - - /* - * The left-hand side of this transformation matched. - * Delete all matched instructions. - */ - while (st->genop != instr) { - GenOp* next = st->genop->next; - FREE_GENOP(st, st->genop); - st->genop = next; - } - *lastp = st->genop; + keep = instr->next; /* The next_instr was optimized away. */ + *lastp = keep; st->genop = new_instr; } - RETURN(TE_OK); + /* FALLTHROUGH */ #endif + case TOP_end: + while (first != keep) { + GenOp* next = first->next; + FREE_GENOP(st, first); + first = next; + } + return TE_OK; case TOP_new_instr: /* * Note that the instructions are generated in reverse order. @@ -5123,14 +5096,10 @@ transform_engine(LoaderState* st) #if defined(TOP_store_rest_args) case TOP_store_rest_args: { - int n = *pc++; - int num_extra = num_vars - n; - - ASSERT(n <= num_vars); - GENOP_ARITY(instr, instr->arity+num_extra); + GENOP_ARITY(instr, instr->arity+num_rest_args); memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg)); - memcpy(instr->a+ap, var+n, num_extra*sizeof(GenOpArg)); - ap += num_extra; + memcpy(instr->a+ap, rest_args, num_rest_args*sizeof(GenOpArg)); + ap += num_rest_args; } break; #endif @@ -5142,21 +5111,12 @@ transform_engine(LoaderState* st) case TOP_try_me_else_fail: restart = restart_fail; break; - case TOP_end: - RETURN(TE_OK); case TOP_fail: - RETURN(TE_FAIL); + return TE_FAIL; default: ASSERT(0); } } -#undef RETURN - - do_return: - if (var != def_vars) { - erts_free(ERTS_ALC_T_LOADER_TMP, (void *) var); - } - return rval; } static void diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index f805e7cc64..86bfb5d746 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -1504,8 +1504,6 @@ sub tr_gen_from { my($var_num) = 0; my(@code); my($min_window) = 0; - my(@fix_rest_args); - my(@fix_pred_funcs); my($op, $ref); # Loop variables. my $where = "left side of transformation in line $line: "; my %var_used = %$used_ref; @@ -1530,8 +1528,17 @@ sub tr_gen_from { my $var; my(@args); - push(@fix_pred_funcs, scalar(@code)); - push(@code, [$name, @ops]); + foreach $var (@ops) { + error($where, "variable '$var' unbound") + unless defined $var{$var}; + if ($var_type{$var} eq 'scalar') { + push(@args, "var[$var{$var}]"); + } else { + push(@args, "rest_args"); + } + } + my $pi = tr_next_index(\@pred_table, \%pred_table, $name, @args); + push(@code, make_op("$name()", 'pred', $pi)); next; } @@ -1595,12 +1602,16 @@ sub tr_gen_from { $may_fail = 1; push(@code, &make_op($var, 'is_same_var', $var{$var})); } elsif ($type eq '*') { - # - # Reserve a hole for a 'rest_args' instruction. - # + foreach my $type (values %var_type) { + error("only one use of a '*' variable is " . + "allowed on the left hand side of " . + "a transformation") + if $type eq 'array'; + } $ignored_var = ''; - push(@fix_rest_args, scalar(@code)); - push(@code, $var); + $var{$var} = 'unnumbered'; + $var_type{$var} = 'array'; + push(@code, make_op($var, 'rest_args')); } elsif ($var_used{$var}) { $ignored_var = ''; $var_type{$var} = 'scalar'; @@ -1629,38 +1640,6 @@ sub tr_gen_from { # push(@code, make_op($may_fail ? '' : 'always reached', 'commit')); - # - # If there is an rest_args instruction, we must insert its correct - # variable number (higher than any other). - # - my $index; - &error("only one use of a '*' variable is allowed on the left hand side of a transformation") - if @fix_rest_args > 1; - foreach $index (@fix_rest_args) { - my $var = $code[$index]; - $var{$var} = $var_num++; - $var_type{$var} = 'array'; - splice(@code, $index, 1, &make_op($var, 'rest_args', $var{$var})); - } - - foreach $index (@fix_pred_funcs) { - my($name, @ops) = @{$code[$index]}; - my(@args); - my $var; - - foreach $var (@ops) { - &error($where, "variable '$var' unbound") - unless defined $var{$var}; - if ($var_type{$var} eq 'scalar') { - push(@args, "var[$var{$var}]"); - } else { - push(@args, "var+$var{$var}"); - } - } - my $pi = tr_next_index(\@pred_table, \%pred_table, $name, @args); - splice(@code, $index, 1, make_op("$name()", 'pred', $pi)); - } - $te_max_vars = $var_num if $te_max_vars < $var_num; [$min_window, \%var, \%var_type, \@code]; @@ -1697,7 +1676,7 @@ sub tr_gen_to { if ($var_type{$var} eq 'scalar') { push(@args, "var[$var{$var}]"); } else { - push(@args, "var+$var{$var}"); + push(@args, "rest_args"); } } pop(@code); # Get rid of 'commit' instruction @@ -1725,7 +1704,7 @@ sub tr_gen_to { my($var, $type, $type_val) = @$op; if ($type eq '*') { - push(@code, make_op($var, 'store_rest_args', $var{$var})); + push(@code, make_op($var, 'store_rest_args')); } elsif ($var ne '') { &error($where, "variable '$var' unbound") unless defined $var{$var}; -- cgit v1.2.3 From c6cabe0b76dda183d209498e1e4e13e3407dcf9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 7 Dec 2015 14:51:44 +0100 Subject: Simplify window management for the transformation engine Generic instructions have a min_window field. Its purpose is to avoid calling transform_engine() when there are too few instructions in the current "transformation window" for a transformation to succeed. Currently it does not do much good since the window size will be decremented by one before being used. The reason for the subtraction is probably that in some circumstances in the past, the loader could read past the end of the BEAM module while attempting to fetch instructions to increase the window size. Therefore, it would not be safe to just remove the subtraction by one. The simplest and safest solution seems to always ensure that there are always at least TWO instructions when calling transform_engine(). That will be safe, as long as a BEAM module is always finished with an int_code_end/0 that is not involved in any transformation. --- erts/emulator/beam/beam_load.c | 16 ++++++++-------- erts/emulator/beam/beam_load.h | 1 - erts/emulator/utils/beam_makeops | 13 +++---------- 3 files changed, 11 insertions(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index c6c35e74c9..5d03c98657 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -2030,14 +2030,14 @@ load_code(LoaderState* stp) do_transform: ASSERT(stp->genop != NULL); if (gen_opc[stp->genop->op].transform != -1) { - int need; - tmp_op = stp->genop; - - for (need = gen_opc[stp->genop->op].min_window-1; need > 0; need--) { - if (tmp_op == NULL) { - goto get_next_instr; - } - tmp_op = tmp_op->next; + if (stp->genop->next == NULL) { + /* + * Simple heuristic: Most transformations requires + * at least two instructions, so make sure that + * there are. That will reduce the number of + * TE_SHORT_WINDOWs. + */ + goto get_next_instr; } switch (transform_engine(stp)) { case TE_FAIL: diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index 22ab71c868..68f4b96893 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -33,7 +33,6 @@ typedef struct gen_op_entry { int specific; int num_specific; int transform; - int min_window; } GenOpEntry; extern GenOpEntry gen_opc[]; diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 86bfb5d746..8f99fdb201 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -113,7 +113,6 @@ my @if_line; # my $te_max_vars = 0; # Max number of variables ever needed. my %gen_transform; -my %min_window; my %match_engine_ops; # All opcodes for the match engine. my %gen_transform_offset; my @transformations; @@ -382,7 +381,6 @@ while (<>) { $gen_arity{$name} = $arity; $gen_to_spec{"$name/$arity"} = undef; $num_specific{"$name/$arity"} = 0; - $min_window{"$name/$arity"} = 255; $obsolete[$op_num] = defined $obsolete; } else { # Unnumbered generic operation. push(@unnumbered_generic, [$name, $arity]); @@ -440,7 +438,6 @@ $num_file_opcodes = @gen_opname; $gen_arity{$name} = $arity; $gen_to_spec{"$name/$arity"} = undef; $num_specific{"$name/$arity"} = 0; - $min_window{"$name/$arity"} = 255; } } @@ -607,7 +604,7 @@ sub emulator_output { $is_transformed{$name,$arity} or error("instruction $key has no specific instruction"); $spec_op = -1 unless defined $spec_op; - &init_item($name, $arity, $spec_op, $num_specific, $tr, $min_window{$key}); + &init_item($name, $arity, $spec_op, $num_specific, $tr); } } print "};\n"; @@ -1503,7 +1500,6 @@ sub tr_gen_from { my(%var_type); my($var_num) = 0; my(@code); - my($min_window) = 0; my($op, $ref); # Loop variables. my $where = "left side of transformation in line $line: "; my %var_used = %$used_ref; @@ -1551,7 +1547,6 @@ sub tr_gen_from { $opnum = $gen_opnum{$name,$arity}; push(@code, make_op("$name/$arity", 'next_instr', $opnum)); - $min_window++; foreach $op (@ops) { my($var, $type, $type_val, $cond, $val) = @$op; my $ignored_var = "$var (ignored)"; @@ -1642,12 +1637,12 @@ sub tr_gen_from { $te_max_vars = $var_num if $te_max_vars < $var_num; - [$min_window, \%var, \%var_type, \@code]; + [\%var, \%var_type, \@code]; } sub tr_gen_to { my($line, $orig_transform, $so_far, @tr) = @_; - my($min_window, $var_ref, $var_type_ref, $code_ref) = @$so_far; + my($var_ref, $var_type_ref, $code_ref) = @$so_far; my(%var) = %$var_ref; my(%var_type) = %$var_type_ref; my(@code) = @$code_ref; @@ -1731,8 +1726,6 @@ sub tr_gen_to { my($dummy, $arity); ($dummy, $op, $arity) = @$first; my($comment) = "\n/*\n * Line $line:\n * $orig_transform\n */\n\n"; - $min_window{$key} = $min_window - if $min_window{$key} > $min_window; my $prev_last; $prev_last = pop(@{$gen_transform{$key}}) -- cgit v1.2.3 From e93a66110aa27a5b8228fb46a3459a6de0e626d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 7 Dec 2015 16:12:21 +0100 Subject: Introduce a 'rename' instruction Introduce a 'rename' instruction that can be used to optimize simple renaming with unchanged operands such as: get_tuple_element Reg P Dst => i_get_tuple_element Reg P Dst By allowing it to lower the arity of instruction, transformations such as the following can be handled: trim N Remaining => i_trim N All in all, currently 67 transformations can be optimized in this way, including some commonly used ones. --- erts/emulator/beam/beam_load.c | 6 +++++ erts/emulator/utils/beam_makeops | 55 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 5d03c98657..ad174664ae 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -5077,6 +5077,12 @@ transform_engine(LoaderState* st) instr->arity = gen_opc[op].arity; ap = 0; break; +#ifdef TOP_rename + case TOP_rename: + instr->op = op = *pc++; + instr->arity = gen_opc[op].arity; + return TE_OK; +#endif case TOP_store_type: i = *pc++; instr->a[ap].type = i; diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 8f99fdb201..3d4213d55d 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -1718,6 +1718,8 @@ sub tr_gen_to { push(@code, make_op('', 'end')) unless is_instr($code[$#code], 'call_end'); + tr_maybe_rename(\@code); + # # Chain together all codes segments having the same first operation. # @@ -1743,6 +1745,59 @@ sub tr_gen_to { push(@{$gen_transform{$key}}, @code), } +sub tr_maybe_rename { + my($ref) = @_; + my $s = 'left'; + my $a = 0; + my $num_args = 0; + my $new_instr; + my $first; + my $i; + + for ($i = 1; $i < @$ref; $i++) { + my $instr = $$ref[$i]; + my($size, $instr_ref, $comment) = @$instr; + my($op, @args) = @$instr_ref; + + if ($s eq 'left') { + if ($op eq 'set_var_next_arg') { + if ($num_args == $a and $args[0] == $a) { + $num_args++; + } + $a++; + } elsif ($op eq 'next_arg') { + $a++; + } elsif ($op eq 'commit') { + $a = 0; + $first = $i; + $s = 'committed'; + } elsif ($op eq 'next_instr') { + return; + } + } elsif ($s eq 'committed') { + if ($op eq 'new_instr') { + $new_instr = $args[0]; + $a = 0; + $s = 'right'; + } else { + return; + } + } elsif ($s eq 'right') { + if ($op eq 'store_var_next_arg' && $args[0] == $a) { + $a++; + } elsif ($op eq 'end' && $a <= $num_args) { + my $name = $gen_opname[$new_instr]; + my $arity = $gen_arity[$new_instr]; + my $new_op = make_op("$name/$arity", 'rename', $new_instr); + splice @$ref, $first, $i-$first+1, ($new_op); + return; + } else { + return; + } + } + } +} + sub tr_code_len { my($sum) = 0; my($ref); -- cgit v1.2.3 From 937f527054f13dd524588c064cd5d76e3cfd23eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 5 Apr 2016 12:56:57 +0200 Subject: Avoid rebuilding unchanged instructions In transformations such as: move S X0=x==0 | line Loc | call_ext Ar Func => \ line Loc | move S X0 | call_ext Ar Func we can avoid rebuilding the last instruction in the sequence by introducing a 'keep' instruction. Currently, there are only 13 transformations that are hit by this optimization, but most of them are frequently used. --- erts/emulator/beam/beam_load.c | 11 ++++++++++- erts/emulator/utils/beam_makeops | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index ad174664ae..bdb451a6fe 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -5028,7 +5028,16 @@ transform_engine(LoaderState* st) instr = 0; #endif break; - +#if defined(TOP_keep) + case TOP_keep: + /* Keep the current instruction unchanged. */ + keep = instr; + st->genop = instr; +#ifdef DEBUG + instr = 0; +#endif + break; +#endif #if defined(TOP_call_end) case TOP_call_end: { diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 3d4213d55d..66ffd83ee9 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -1718,6 +1718,7 @@ sub tr_gen_to { push(@code, make_op('', 'end')) unless is_instr($code[$#code], 'call_end'); + tr_maybe_keep(\@code); tr_maybe_rename(\@code); # @@ -1745,6 +1746,46 @@ sub tr_gen_to { push(@{$gen_transform{$key}}, @code), } +sub tr_maybe_keep { + my($ref) = @_; + my @last_instr; + my $pos; + my $reused_instr; + + for (my $i = 0; $i < @$ref; $i++) { + my $instr = $$ref[$i]; + my($size, $instr_ref, $comment) = @$instr; + my($op, @args) = @$instr_ref; + if ($op eq 'next_instr') { + @last_instr = ($args[0]); + } elsif ($op eq 'set_var_next_arg') { + push @last_instr, $args[0]; + } elsif ($op eq 'next_arg') { + push @last_instr, 'ignored'; + } elsif ($op eq 'new_instr') { + unless (defined $pos) { + # 'new_instr' immediately after 'commit'. + $reused_instr = $args[0]; + return unless shift(@last_instr) == $reused_instr; + $pos = $i - 1; + } else { + # Second 'new_instr' after 'commit'. The instructions + # from $pos up to and including $i - 1 rebuilds the + # existing instruction exactly. + my $name = $gen_opname[$reused_instr]; + my $arity = $gen_arity[$reused_instr]; + my $reuse = make_op("$name/$arity", 'keep'); + splice @$ref, $pos, $i-$pos, ($reuse); + return; + } + } elsif ($op eq 'store_var_next_arg') { + return unless shift(@last_instr) eq $args[0]; + } elsif (defined $pos) { + return; + } + } +} + sub tr_maybe_rename { my($ref) = @_; my $s = 'left'; -- cgit v1.2.3 From 72bec464764c919cbfbd2db1c86cce227b2b9c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 4 Apr 2016 15:37:46 +0200 Subject: Remove unused variables after code generation The removal of instructions on the left side of a transformation is done while generating the code for the left side. Postpone removal of unused variables to a later, separate passes to allow more variables to be eliminated after the optimizations passes introduced in the previous commits. --- erts/emulator/utils/beam_makeops | 123 +++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 50 deletions(-) (limited to 'erts') diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 66ffd83ee9..2e7073a8f0 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -1402,8 +1402,7 @@ sub tr_gen { foreach $ref (@g) { my($line, $orig_transform, $from_ref, $to_ref) = @$ref; - my $used_ref = used_vars($from_ref, $to_ref); - my $so_far = tr_gen_from($line, $used_ref, @$from_ref); + my $so_far = tr_gen_from($line, @$from_ref); tr_gen_to($line, $orig_transform, $so_far, @$to_ref); } @@ -1454,55 +1453,14 @@ sub tr_gen { print "};\n\n"; } -sub used_vars { - my($from_ref,$to_ref) = @_; - my %used; - my %seen; - - foreach my $ref (@$from_ref) { - my($name,$arity,@ops) = @$ref; - if ($name =~ /^[.]/) { - foreach my $var (@ops) { - $used{$var} = 1; - } - } else { - # Any variable that is used at least twice on the - # left-hand side is used. (E.g. "move R R".) - foreach my $op (@ops) { - my($var, $type, $type_val) = @$op; - next if $var eq ''; - $used{$var} = 1 if $seen{$var}; - $seen{$var} = 1; - } - } - } - - foreach my $ref (@$to_ref) { - my($name, $arity, @ops) = @$ref; - if ($name =~ /^[.]/) { - foreach my $var (@ops) { - $used{$var} = 1; - } - } else { - foreach my $op (@ops) { - my($var, $type, $type_val) = @$op; - next if $var eq ''; - $used{$var} = 1; - } - } - } - \%used; -} - sub tr_gen_from { - my($line,$used_ref,@tr) = @_; + my($line,@tr) = @_; my(%var) = (); my(%var_type); my($var_num) = 0; my(@code); my($op, $ref); # Loop variables. my $where = "left side of transformation in line $line: "; - my %var_used = %$used_ref; my $may_fail = 0; my $is_first = 1; @@ -1534,7 +1492,10 @@ sub tr_gen_from { } } my $pi = tr_next_index(\@pred_table, \%pred_table, $name, @args); - push(@code, make_op("$name()", 'pred', $pi)); + my $op = make_op("$name()", 'pred', $pi); + my @slots = grep(/^\d+/, map { $var{$_} } @ops); + op_slot_usage($op, @slots); + push(@code, $op); next; } @@ -1595,7 +1556,9 @@ sub tr_gen_from { if (defined $var{$var}) { $ignored_var = ''; $may_fail = 1; - push(@code, &make_op($var, 'is_same_var', $var{$var})); + my $op = make_op($var, 'is_same_var', $var{$var}); + op_slot_usage($op, $var{$var}); + push(@code, $op); } elsif ($type eq '*') { foreach my $type (values %var_type) { error("only one use of a '*' variable is " . @@ -1607,7 +1570,7 @@ sub tr_gen_from { $var{$var} = 'unnumbered'; $var_type{$var} = 'array'; push(@code, make_op($var, 'rest_args')); - } elsif ($var_used{$var}) { + } else { $ignored_var = ''; $var_type{$var} = 'scalar'; $var{$var} = $var_num; @@ -1677,7 +1640,10 @@ sub tr_gen_to { pop(@code); # Get rid of 'commit' instruction my $index = tr_next_index(\@call_table, \%call_table, $name, @args); - push(@code, make_op("$name()", 'call_end', $index)); + my $op = make_op("$name()", 'call_end', $index); + my @slots = grep(/^\d+/, map { $var{$_} } @ops); + op_slot_usage($op, @slots); + push(@code, $op); last; } @@ -1703,7 +1669,9 @@ sub tr_gen_to { } elsif ($var ne '') { &error($where, "variable '$var' unbound") unless defined $var{$var}; - push(@code, &make_op($var, 'store_var_next_arg', $var{$var})); + my $op = make_op($var, 'store_var_next_arg', $var{$var}); + op_slot_usage($op, $var{$var}); + push(@code, $op); } elsif ($type ne '') { push(@code, &make_op('', 'store_type', "TAG_$type")); if ($type_val) { @@ -1720,6 +1688,7 @@ sub tr_gen_to { tr_maybe_keep(\@code); tr_maybe_rename(\@code); + tr_remove_unused(\@code); # # Chain together all codes segments having the same first operation. @@ -1839,6 +1808,55 @@ sub tr_maybe_rename { } } +sub tr_remove_unused { + my($ref) = @_; + my %used; + + # Collect all used variables. + for my $instr (@$ref) { + my $uref = $$instr[3]; + for my $slot (@$uref) { + $used{$slot} = 1; + } + } + + # Replace 'set_var_next_arg' with 'next_arg' if the variable + # is never used. + for my $instr (@$ref) { + my($size, $instr_ref, $comment) = @$instr; + my($op, @args) = @$instr_ref; + if ($op eq 'set_var_next_arg') { + my $var = $args[0]; + next if $used{$var}; + $instr = make_op("$comment (ignored)", 'next_arg'); + } + } + + # Delete a sequence of 'next_arg' instructions when they are + # redundant before instructions such as 'commit'. + my @opcode; + my %ending = (call_end => 1, + commit => 1, + next_instr => 1, + pred => 1, + rename => 1, + keep => 1); + for (my $i = 0; $i < @$ref; $i++) { + my $instr = $$ref[$i]; + my($size, $instr_ref, $comment) = @$instr; + my($opcode) = @$instr_ref; + + if ($ending{$opcode}) { + my $first = $i; + $first-- while $first > 0 and $opcode[$first-1] eq 'next_arg'; + my $n = $i - $first; + splice @$ref, $first, $n; + $i -= $n; + } + $opcode[$i] = $opcode; + } +} + sub tr_code_len { my($sum) = 0; my($ref); @@ -1851,7 +1869,12 @@ sub tr_code_len { sub make_op { my($comment, @op) = @_; - [scalar(@op), [@op], $comment]; + [scalar(@op), [@op], $comment, []]; +} + +sub op_slot_usage { + my($op_ref, @slots) = @_; + $$op_ref[3] = \@slots; } sub is_instr { -- cgit v1.2.3 From 4f33597d52a0cef2e47b07578bc8a35a17c2f969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 6 Apr 2016 07:02:36 +0200 Subject: Don't let the loader do the compiler's job Optimizations that are possible to do by the compiler should be done by the compiler and not by the loader. If the compiler has done its job correctly, attempting to do the two transformations only wastes time. --- erts/emulator/beam/beam_load.c | 7 ------- erts/emulator/beam/ops.tab | 5 ----- 2 files changed, 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index bdb451a6fe..a98900460e 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -2735,13 +2735,6 @@ mixed_types(LoaderState* stp, GenOpArg Size, GenOpArg* Rest) return 0; } -static int -same_label(LoaderState* stp, GenOpArg Target, GenOpArg Label) -{ - return Target.type = TAG_f && Label.type == TAG_u && - Target.val == Label.val; -} - static int is_killed_apply(LoaderState* stp, GenOpArg Reg, GenOpArg Live) { diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 78000160e3..485c072540 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -181,11 +181,6 @@ i_jump_on_val_zero y f I i_jump_on_val x f I I i_jump_on_val y f I I -jump Target | label Lbl | same_label(Target, Lbl) => label Lbl - -is_ne_exact L1 S1 S2 | jump Fail | label L2 | same_label(L1, L2) => \ - is_eq_exact Fail S1 S2 | label L2 - %macro: get_list GetList -pack get_list x x x get_list x x y -- cgit v1.2.3 From 921c838b8142d3c8d1739c6b30c6d88e39e5f147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 6 Apr 2016 07:13:26 +0200 Subject: Eliminate unnecessary renaming of bs_put_utf16/3 There is no reason to rename bs_put_utf16/3. (We rename instructions if we'll need to change the operands or if we will need to avoid an endless transformation loop. Neither of these reasons apply to bs_put_utf16/3.) --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/ops.tab | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index a390422040..09a41f2b56 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4102,7 +4102,7 @@ do { \ StoreBifResult(1, result); } - OpCase(i_bs_put_utf16_jIs): { + OpCase(bs_put_utf16_jIs): { Eterm arg; GetArg1(2, arg); diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 485c072540..15f27835a8 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1350,9 +1350,7 @@ bs_put_utf8 Fail u Src=s => i_bs_put_utf8 Fail Src i_bs_put_utf8 j s -bs_put_utf16 Fail Flags=u Src=s => i_bs_put_utf16 Fail Flags Src - -i_bs_put_utf16 j I s +bs_put_utf16 j I s bs_put_utf32 Fail=j Flags=u Src=s => \ i_bs_validate_unicode Fail Src | bs_put_integer Fail i=32 u=1 Flags Src -- cgit v1.2.3 From 2f1ce217b9eaa0ef4be778193e232ee5faf00a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Apr 2016 16:30:31 +0200 Subject: Replace test_server:os_type/0 with os:type/0 --- erts/test/ignore_cores.erl | 2 +- erts/test/install_SUITE.erl | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/test/ignore_cores.erl b/erts/test/ignore_cores.erl index 4af5efe834..da6f6850c6 100644 --- a/erts/test/ignore_cores.erl +++ b/erts/test/ignore_cores.erl @@ -94,7 +94,7 @@ setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite), end, ok = file:write_file(filename:join([IgnDir, "ignore_core_files"]), <<>>), %% cores are dumped in /cores on MacOS X - CoresDir = case {?t:os_type(), filelib:is_dir("/cores")} of + CoresDir = case {os:type(), filelib:is_dir("/cores")} of {{unix,darwin}, true} -> filelib:fold_files("/cores", "^core.*$", diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl index 4f244d2476..a9a90dcf1e 100644 --- a/erts/test/install_SUITE.erl +++ b/erts/test/install_SUITE.erl @@ -550,23 +550,23 @@ expect(X, Y) -> init_per_suite(Config) -> PD = proplists:get_value(priv_dir, Config), - SymLinks = case ?t:os_type() of - {win32, _} -> false; - _ -> - case file:make_symlink("nothing", - filename:join(PD, - "symlink_test")) of - ok -> true; - _ -> false - end - end, + SymLinks = case os:type() of + {win32, _} -> false; + _ -> + case file:make_symlink("nothing", + filename:join(PD, "symlink_test")) of + ok -> true; + _ -> false + end + end, [{symlinks, SymLinks} | Config]. end_per_suite(_Config) -> ok. init_per_testcase(Case, Config) -> - init_per_testcase_aux(proplists:get_value(symlinks,Config),?t:os_type(),Case,Config). + init_per_testcase_aux(proplists:get_value(symlinks,Config), + os:type(),Case,Config). init_per_testcase_aux(_, {win32, _}, _Case, _Config) -> {skip, "Not on windows"}; -- cgit v1.2.3 From 0914884d534dab195f0ff4752e8ea4feda56dbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Apr 2016 16:52:05 +0200 Subject: erts: Fix process_info/2 result spec --- erts/preloaded/src/erlang.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index dfbd116d6e..50b01703ac 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2130,7 +2130,7 @@ process_flag(_Flag, _Value) -> {message_queue_data, MQD :: message_queue_data()} | {priority, Level :: priority_level()} | {reductions, Number :: non_neg_integer()} | - {registered_name, Atom :: atom()} | + {registered_name, [] | (Atom :: atom())} | {sequential_trace_token, [] | (SequentialTraceToken :: term())} | {stack_size, Size :: non_neg_integer()} | {status, Status :: exiting | garbage_collecting | waiting | running | runnable | suspended} | -- cgit v1.2.3 From c508d0219aebe341805fb747581ec304975d62ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Apr 2016 16:53:05 +0200 Subject: Update preloaded erlang.beam --- erts/preloaded/ebin/erlang.beam | Bin 102200 -> 102316 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 2ea2de4c70..7ceb6daaa3 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ -- cgit v1.2.3 From 4b748a0bb443a919119158266426372077b0f5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 23 Mar 2016 19:27:27 +0100 Subject: Increase timetrap timeout for port_SUITE:huge_env/1 --- erts/emulator/test/port_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 328641f5b9..b85d789bbe 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -908,7 +908,7 @@ try_bad_env(Env) -> %% Test that we can handle a very very large environment gracefully. huge_env(Config) when is_list(Config) -> - ct:timetrap({seconds, 30}), + ct:timetrap({minutes, 2}), Vars = case os:type() of {win32,_} -> 500; _ -> -- cgit v1.2.3 From 8a6ed0a09eb52f62e7df35d64ac5f3a09aa18edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 23 Mar 2016 19:46:14 +0100 Subject: Increase timetrap timeout for op_SUITE:bsl_bsr/1 --- erts/emulator/test/op_SUITE.erl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/op_SUITE.erl b/erts/emulator/test/op_SUITE.erl index 562cf1c92d..cb683b9cbf 100644 --- a/erts/emulator/test/op_SUITE.erl +++ b/erts/emulator/test/op_SUITE.erl @@ -30,7 +30,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}, - {timetrap, {minutes, 3}}]. + {timetrap, {minutes, 5}}]. all() -> [bsl_bsr, logical, t_not, relop_simple, relop, @@ -39,9 +39,16 @@ all() -> %% Test the bsl and bsr operators. bsl_bsr(Config) when is_list(Config) -> Vs = [unvalue(V) || V <- [-16#8000009-2,-1,0,1,2,73,16#8000000,bad,[]]], - Cases = [{Op,X,Y} || Op <- ['bsr','bsl'], X <- Vs, Y <- Vs], - run_test_module(Cases, false), - {comment,integer_to_list(length(Cases)) ++ " cases"}. + %% Try to use less memory by splitting the cases + + Cases1 = [{Op,X,Y} || Op <- ['bsl'], X <- Vs, Y <- Vs], + N1 = length(Cases1), + run_test_module(Cases1, false), + + Cases2 = [{Op,X,Y} || Op <- ['bsr'], X <- Vs, Y <- Vs], + N2 = length(Cases2), + run_test_module(Cases2, false), + {comment,integer_to_list(N1 + N2) ++ " cases"}. %% Test the logical operators and internal BIFs. logical(Config) when is_list(Config) -> -- cgit v1.2.3 From 26d589332638b6652786801d96961e69281bc458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 23 Mar 2016 20:13:55 +0100 Subject: Relax node_container_SUITE --- erts/emulator/test/node_container_SUITE.erl | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 71400142af..5275290d04 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -28,8 +28,6 @@ -module(node_container_SUITE). -author('rickard.green@uab.ericsson.se'). -%-define(line_trace, 1). - -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, init_per_suite/1, end_per_suite/1, @@ -56,7 +54,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}, - {timetrap, {minutes, 10}}]. + {timetrap, {minutes, 12}}]. all() -> @@ -842,11 +840,10 @@ iter_max_procs(Config) when is_list(Config) -> Res = chk_max_proc_line(), Res = chk_max_proc_line(), done = chk_max_proc_line_until(NoMoreTests, Res), - {comment, - io_lib:format("max processes = ~p; " - "process line length = ~p", - [element(2, Res), element(1, Res)])}. - + Cmt = io_lib:format("max processes = ~p; " + "process line length = ~p", + [element(2, Res), element(1, Res)]), + {comment, lists:flatten(Cmt)}. max_proc_line(Root, Parent, N) -> Me = self(), -- cgit v1.2.3 From f821130175400d72dbf29bf82f81f7495fb71272 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 11 Nov 2015 15:21:55 +0100 Subject: erts: Rename atom '' from am_Cookie to am_Empty --- erts/emulator/beam/atom.names | 2 +- erts/emulator/beam/dist.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 169b071cd7..2eb63febe7 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -43,7 +43,7 @@ atom false true atom Underscore='_' atom Noname='nonode@nohost' atom EOT='$end_of_table' -atom Cookie='' +atom Empty='' # # Used in the Beam emulator loop. (Smaller literals usually means tighter code.) diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index fa385f105d..3a6da373c3 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -906,9 +906,9 @@ erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx) if (token != NIL) ctl = TUPLE4(&ctx->ctl_heap[0], - make_small(DOP_SEND_TT), am_Cookie, remote, token); + make_small(DOP_SEND_TT), am_Empty, remote, token); else - ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_SEND), am_Cookie, remote); + ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_SEND), am_Empty, remote); DTRACE6(message_send, sender_name, receiver_name, msize, tok_label, tok_lastcnt, tok_serial); DTRACE7(message_send_remote, sender_name, node_name, receiver_name, @@ -963,10 +963,10 @@ erts_dsig_send_reg_msg(Eterm remote_name, Eterm message, if (token != NIL) ctl = TUPLE5(&ctx->ctl_heap[0], make_small(DOP_REG_SEND_TT), - sender->common.id, am_Cookie, remote_name, token); + sender->common.id, am_Empty, remote_name, token); else ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_REG_SEND), - sender->common.id, am_Cookie, remote_name); + sender->common.id, am_Empty, remote_name); DTRACE6(message_send, sender_name, receiver_name, msize, tok_label, tok_lastcnt, tok_serial); DTRACE7(message_send_remote, sender_name, node_name, receiver_name, -- cgit v1.2.3 From 995f81ed452f95c065bd94d62cb69dbf5289be76 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 11 Nov 2015 16:14:33 +0100 Subject: erts: Refactor ETS compressed encoding of local node Instead of INTERNAL_CREATION (255), use empty atom for node name to mean the local node (regardless of node name or creation). The purpose is to get rid of special value 255, for future expansion of creation to 32-bit. --- erts/emulator/beam/erl_vm.h | 1 - erts/emulator/beam/external.c | 43 +++++++++++++++++++++---------------------- 2 files changed, 21 insertions(+), 23 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 98f27a1725..357094633e 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -165,7 +165,6 @@ extern int erts_atom_table_size;/* Atom table size */ extern int erts_pd_initial_size;/* Initial Process dictionary table size */ #define ORIG_CREATION 0 -#define INTERNAL_CREATION 255 /* macros for extracting bytes from uint16's */ diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 10f03636ec..de0c155fc0 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -51,7 +51,7 @@ #define MAX_STRING_LEN 0xffff -#define is_valid_creation(Cre) ((unsigned)(Cre) < MAX_CREATION || (Cre) == INTERNAL_CREATION) +#define is_valid_creation(Cre) ((unsigned)(Cre) < MAX_CREATION) #undef ERTS_DEBUG_USE_DIST_SEP #ifdef DEBUG @@ -2152,12 +2152,12 @@ static byte* enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags) { Uint on, os; + Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS)) + ? am_Empty : pid_node_name(pid)); *ep++ = PID_EXT; /* insert atom here containing host and sysname */ - ep = enc_atom(acmp, pid_node_name(pid), ep, dflags); - - /* two bytes for each number and serial */ + ep = enc_atom(acmp, sysname, ep, dflags); on = pid_number(pid); os = pid_serial(pid); @@ -2166,8 +2166,7 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags) ep += 4; put_int32(os, ep); ep += 4; - *ep++ = (is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS)) ? - INTERNAL_CREATION : pid_creation(pid); + *ep++ = pid_creation(pid); return ep; } @@ -2249,14 +2248,13 @@ dec_atom(ErtsDistExternal *edep, byte* ep, Eterm* objp) static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint creation) { - switch (creation) { - case INTERNAL_CREATION: + if (sysname == am_Empty) /* && DFLAG_INTERNAL_TAGS */ return erts_this_node; - case ORIG_CREATION: - if (sysname == erts_this_node->sysname) { - creation = erts_this_node->creation; - } - } + + if (sysname == erts_this_node->sysname + && (creation == erts_this_node->creation || creation == ORIG_CREATION)) + return erts_this_node; + return erts_find_or_insert_node(sysname,creation); } @@ -2528,6 +2526,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, case REF_DEF: case EXTERNAL_REF_DEF: { Uint32 *ref_num; + Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj)) + ? am_Empty : ref_node_name(obj)); ASSERT(dflags & DFLAG_EXTENDED_REFERENCES); @@ -2535,9 +2535,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, i = ref_no_of_numbers(obj); put_int16(i, ep); ep += 2; - ep = enc_atom(acmp,ref_node_name(obj),ep,dflags); - *ep++ = ((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj)) ? - INTERNAL_CREATION : ref_creation(obj); + ep = enc_atom(acmp, sysname, ep, dflags); + *ep++ = ref_creation(obj); ref_num = ref_numbers(obj); for (j = 0; j < i; j++) { put_int32(ref_num[j], ep); @@ -2546,17 +2545,17 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, break; } case PORT_DEF: - case EXTERNAL_PORT_DEF: - + case EXTERNAL_PORT_DEF: { + Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj)) + ? am_Empty : port_node_name(obj)); *ep++ = PORT_EXT; - ep = enc_atom(acmp,port_node_name(obj),ep,dflags); + ep = enc_atom(acmp, sysname, ep, dflags); j = port_number(obj); put_int32(j, ep); ep += 4; - *ep++ = ((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj)) ? - INTERNAL_CREATION : port_creation(obj); + *ep++ = port_creation(obj); break; - + } case LIST_DEF: { int is_str; -- cgit v1.2.3 From bf0bf9cf009fa8ccc7fc364fdbbdeb6f491efe43 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 13 Nov 2015 16:47:40 +0100 Subject: erts: Support 32-bit creation for external pid,port,refs from future nodes. --- erts/emulator/beam/erl_node_tables.c | 28 +---- erts/emulator/beam/erl_node_tables.h | 2 +- erts/emulator/beam/erl_term.h | 10 +- erts/emulator/beam/external.c | 157 +++++++++++++++++++++------- erts/emulator/beam/external.h | 5 +- erts/emulator/test/node_container_SUITE.erl | 121 ++++++++++++--------- 6 files changed, 196 insertions(+), 127 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 8617f42d7b..79da705e0f 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -497,31 +497,7 @@ node_table_hash(void *venp) Uint32 cre = ((ErlNode *) venp)->creation; HashValue h = atom_tab(atom_val(((ErlNode *) venp)->sysname))->slot.bucket.hvalue; - h *= PRIME0; - h += cre & 0xff; - -#if MAX_CREATION >= (1 << 8) - h *= PRIME1; - h += (cre >> 8) & 0xff; -#endif - -#if MAX_CREATION >= (1 << 16) - h *= PRIME2; - h += (cre >> 16) & 0xff; -#endif - -#if MAX_CREATION >= (1 << 24) - h *= PRIME3; - h += (cre >> 24) & 0xff; -#endif - -#if 0 -/* XXX Problems in older versions of GCC */ - #if MAX_CREATION >= (1UL << 32) - #error "MAX_CREATION larger than size of expected creation storage (Uint32)" - #endif -#endif - return h; + return (h + cre) * PRIME0; } static int @@ -599,7 +575,7 @@ erts_node_table_info(int to, void *to_arg) } -ErlNode *erts_find_or_insert_node(Eterm sysname, Uint creation) +ErlNode *erts_find_or_insert_node(Eterm sysname, Uint32 creation) { ErlNode *res; ErlNode ne; diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index fb2f2a5407..2b93f1f08a 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -182,7 +182,7 @@ Uint erts_dist_table_size(void); void erts_dist_table_info(int, void *); void erts_set_dist_entry_not_connected(DistEntry *); void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint); -ErlNode *erts_find_or_insert_node(Eterm, Uint); +ErlNode *erts_find_or_insert_node(Eterm, Uint32); void erts_schedule_delete_node(ErlNode *); void erts_set_this_node(Eterm, Uint); Uint erts_node_table_size(void); diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 0a71534790..fc58853b5e 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -558,14 +558,6 @@ _ET_DECLARE_CHECKED(Eterm*,tuple_val,Wterm) #define _GETBITS(X,Pos,Size) (((X) >> (Pos)) & ~(~((Uint) 0) << (Size))) -/* - * Creation in node specific data (pids, ports, refs) - */ - -#define _CRE_SIZE 2 - -/* MAX value for the creation field in pid, port and reference */ -#define MAX_CREATION (1 << _CRE_SIZE) /* * PID layout (internal pids): @@ -579,7 +571,7 @@ _ET_DECLARE_CHECKED(Eterm*,tuple_val,Wterm) * * n : number * - * Old pid layout: + * Very old pid layout: * * |3 3 2 2 2 2 2 2|2 2 2 2 1 1 1 1|1 1 1 1 1 1 | | * |1 0 9 8 7 6 5 4|3 2 1 0 9 8 7 6|5 4 3 2 1 0 9 8|7 6 5 4 3 2 1 0| diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index de0c155fc0..5ea155f83f 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -51,7 +51,18 @@ #define MAX_STRING_LEN 0xffff -#define is_valid_creation(Cre) ((unsigned)(Cre) < MAX_CREATION) +/* MAX value for the creation field in pid, port and reference + for the local node and for the current external format. + + Larger creation values than this are allowed in external pid, port and refs + encoded with NEW_PID_EXT, NEW_PORT_EXT and NEWER_REFERENCE_EXT. + The point here is to prepare for future upgrade to 32-bit creation. + OTP-19 (erts-8.0) can handle big creation values from other (newer) nodes, + but do not use big creation values for the local node yet, + as we still may have to communicate with older nodes. +*/ +#define ERTS_MAX_LOCAL_CREATION (3) +#define is_valid_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_LOCAL_CREATION) #undef ERTS_DEBUG_USE_DIST_SEP #ifdef DEBUG @@ -97,7 +108,7 @@ static byte* enc_pid(ErtsAtomCacheMap *, Eterm, byte*, Uint32); struct B2TContext_t; static byte* dec_term(ErtsDistExternal*, ErtsHeapFactory*, byte*, Eterm*, struct B2TContext_t*); static byte* dec_atom(ErtsDistExternal *, byte*, Eterm*); -static byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, byte*, Eterm*); +static byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, byte*, Eterm*, byte tag); static Sint decoded_size(byte *ep, byte* endp, int internal_tags, struct B2TContext_t*); static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1); @@ -2154,8 +2165,9 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags) Uint on, os; Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS)) ? am_Empty : pid_node_name(pid)); + Uint32 creation = pid_creation(pid); + byte* tagp = ep++; - *ep++ = PID_EXT; /* insert atom here containing host and sysname */ ep = enc_atom(acmp, sysname, ep, dflags); @@ -2166,7 +2178,15 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags) ep += 4; put_int32(os, ep); ep += 4; - *ep++ = pid_creation(pid); + if (creation <= ERTS_MAX_LOCAL_CREATION) { + *tagp = PID_EXT; + *ep++ = creation; + } else { + ASSERT(is_external_pid(pid)); + *tagp = NEW_PID_EXT; + put_int32(creation, ep); + ep += 4; + } return ep; } @@ -2246,7 +2266,7 @@ dec_atom(ErtsDistExternal *edep, byte* ep, Eterm* objp) return ep; } -static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint creation) +static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint32 creation) { if (sysname == am_Empty) /* && DFLAG_INTERNAL_TAGS */ return erts_this_node; @@ -2259,13 +2279,14 @@ static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint creation) } static byte* -dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep, Eterm* objp) +dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep, + Eterm* objp, byte tag) { Eterm sysname; Uint data; Uint num; Uint ser; - Uint cre; + Uint32 cre; ErlNode *node; *objp = NIL; /* In case we fail, don't leave a hole in the heap */ @@ -2281,12 +2302,19 @@ dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep, Eterm* objp) ep += 4; if (ser > ERTS_MAX_PID_SERIAL) return NULL; - cre = get_int8(ep); - ep += 1; - if (!is_valid_creation(cre)) { - return NULL; + if (tag == PID_EXT) { + cre = get_int8(ep); + ep += 1; + if (!is_valid_creation(cre)) { + return NULL; + } + } else { + ASSERT(tag == NEW_PID_EXT); + cre = get_int32(ep); + ep += 4; } + data = make_pid_data(ser, num); /* @@ -2528,15 +2556,24 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 *ref_num; Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj)) ? am_Empty : ref_node_name(obj)); + Uint32 creation = ref_creation(obj); + byte* tagp = ep++; ASSERT(dflags & DFLAG_EXTENDED_REFERENCES); - *ep++ = NEW_REFERENCE_EXT; i = ref_no_of_numbers(obj); put_int16(i, ep); ep += 2; ep = enc_atom(acmp, sysname, ep, dflags); - *ep++ = ref_creation(obj); + if (creation <= ERTS_MAX_LOCAL_CREATION) { + *tagp = NEW_REFERENCE_EXT; + *ep++ = creation; + } else { + ASSERT(is_external_ref(obj)); + *tagp = NEWER_REFERENCE_EXT; + put_int32(creation, ep); + ep += 4; + } ref_num = ref_numbers(obj); for (j = 0; j < i; j++) { put_int32(ref_num[j], ep); @@ -2548,12 +2585,22 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, case EXTERNAL_PORT_DEF: { Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj)) ? am_Empty : port_node_name(obj)); - *ep++ = PORT_EXT; + Uint32 creation = port_creation(obj); + byte* tagp = ep++; + ep = enc_atom(acmp, sysname, ep, dflags); j = port_number(obj); put_int32(j, ep); ep += 4; - *ep++ = port_creation(obj); + if (creation <= ERTS_MAX_LOCAL_CREATION) { + *tagp = PORT_EXT; + *ep++ = creation; + } else { + ASSERT(is_external_port(obj)); + *tagp = NEW_PORT_EXT; + put_int32(creation, ep); + ep += 4; + } break; } case LIST_DEF: @@ -3259,20 +3306,23 @@ dec_term_atom_common: hp += FLOAT_SIZE_OBJECT; break; } - case PID_EXT: + case PID_EXT: + case NEW_PID_EXT: factory->hp = hp; - ep = dec_pid(edep, factory, ep, objp); + ep = dec_pid(edep, factory, ep, objp, ep[-1]); hp = factory->hp; if (ep == NULL) { goto error; } break; - case PORT_EXT: + case PORT_EXT: + case NEW_PORT_EXT: { Eterm sysname; ErlNode *node; Uint num; - Uint cre; + Uint32 cre; + byte tag = ep[-1]; if ((ep = dec_atom(edep, ep, &sysname)) == NULL) { goto error; @@ -3281,12 +3331,17 @@ dec_term_atom_common: goto error; } ep += 4; - cre = get_int8(ep); - ep++; - if (!is_valid_creation(cre)) { - goto error; - } - + if (tag == PORT_EXT) { + cre = get_int8(ep); + ep++; + if (!is_valid_creation(cre)) { + goto error; + } + } + else { + cre = get_int32(ep); + ep += 4; + } node = dec_get_node(sysname, cre); if(node == erts_this_node) { *objp = make_internal_port(num); @@ -3311,7 +3366,7 @@ dec_term_atom_common: Eterm sysname; ErlNode *node; int i; - Uint cre; + Uint32 cre; Uint32 *ref_num; Uint32 r0; Uint ref_words; @@ -3335,9 +3390,6 @@ dec_term_atom_common: ref_words = get_int16(ep); ep += 2; - if (ref_words > ERTS_MAX_REF_NUMBERS) - goto error; - if ((ep = dec_atom(edep, ep, &sysname)) == NULL) goto error; @@ -3350,8 +3402,23 @@ dec_term_atom_common: ep += 4; if (r0 >= MAX_REFERENCE) goto error; + goto ref_ext_common; + + case NEWER_REFERENCE_EXT: + ref_words = get_int16(ep); + ep += 2; + + if ((ep = dec_atom(edep, ep, &sysname)) == NULL) + goto error; + + cre = get_int32(ep); + ep += 4; + r0 = get_int32(ep); /* allow full word */ + ep += 4; ref_ext_common: + if (ref_words > ERTS_MAX_REF_NUMBERS) + goto error; node = dec_get_node(sysname, cre); if(node == erts_this_node) { @@ -3705,9 +3772,9 @@ dec_term_atom_common: *objp = make_fun(funp); /* Creator pid */ - if (*ep != PID_EXT - || (ep = dec_pid(edep, factory, ++ep, - &funp->creator))==NULL) { + if ((*ep != PID_EXT && *ep != NEW_PID_EXT) + || (ep = dec_pid(edep, factory, ep+1, + &funp->creator, *ep))==NULL) { goto error; } @@ -4009,20 +4076,29 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, else result += 1 + 4 + 1 + i; /* tag,size,sign,digits */ break; + case EXTERNAL_PID_DEF: + if (external_pid_creation(obj) > ERTS_MAX_LOCAL_CREATION) + result += 3; + /*fall through*/ case PID_DEF: - case EXTERNAL_PID_DEF: result += (1 + encode_size_struct2(acmp, pid_node_name(obj), dflags) + 4 + 4 + 1); break; + case EXTERNAL_REF_DEF: + if (external_ref_creation(obj) > ERTS_MAX_LOCAL_CREATION) + result += 3; + /*fall through*/ case REF_DEF: - case EXTERNAL_REF_DEF: ASSERT(dflags & DFLAG_EXTENDED_REFERENCES); i = ref_no_of_numbers(obj); result += (1 + 2 + encode_size_struct2(acmp, ref_node_name(obj), dflags) + 1 + 4*i); break; - case PORT_DEF: - case EXTERNAL_PORT_DEF: + case EXTERNAL_PORT_DEF: + if (external_port_creation(obj) > ERTS_MAX_LOCAL_CREATION) + result += 3; + /*fall through*/ + case PORT_DEF: result += (1 + encode_size_struct2(acmp, port_node_name(obj), dflags) + 4 + 1); break; @@ -4349,19 +4425,22 @@ init_done: SKIP(1+atom_extra_skip); atom_extra_skip = 0; break; - case PID_EXT: + case PID_EXT: + case NEW_PID_EXT: atom_extra_skip = 9; /* In case it is an external pid */ heap_size += EXTERNAL_THING_HEAD_SIZE + 1; terms++; break; - case PORT_EXT: + case PORT_EXT: + case NEW_PORT_EXT: atom_extra_skip = 5; /* In case it is an external port */ heap_size += EXTERNAL_THING_HEAD_SIZE + 1; terms++; break; - case NEW_REFERENCE_EXT: + case NEW_REFERENCE_EXT: + case NEWER_REFERENCE_EXT: { int id_words; diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index 87eff2fe9f..49198fb47f 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -18,8 +18,6 @@ * %CopyrightEnd% */ -/* Same order as the ordering of terms in erlang */ - /* Since there are 255 different External tag values to choose from There is no reason to not be extravagant. Hence, the different tags for large/small tuple e.t.c @@ -37,9 +35,12 @@ #define SMALL_ATOM_EXT 's' #define REFERENCE_EXT 'e' #define NEW_REFERENCE_EXT 'r' +#define NEWER_REFERENCE_EXT 'Z' #define PORT_EXT 'f' +#define NEW_PORT_EXT 'Y' #define NEW_FLOAT_EXT 'F' #define PID_EXT 'g' +#define NEW_PID_EXT 'X' #define SMALL_TUPLE_EXT 'h' #define LARGE_TUPLE_EXT 'i' #define NIL_EXT 'j' diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 71400142af..8c4dd082d4 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -126,7 +126,13 @@ term_to_binary_to_term_eq(Config) when is_list(Config) -> LHLRef = binary_to_term(term_to_binary(LHLRef)), LSRef = binary_to_term(term_to_binary(LSRef)), % Get remote node containers - RNode = {get_nodename(), 3}, + ttbtteq_do_remote({get_nodename(), 3}), + ttbtteq_do_remote({get_nodename(), 4}), + ttbtteq_do_remote({get_nodename(), 16#adec0ded}), + nc_refc_check(node()), + ok. + +ttbtteq_do_remote(RNode) -> RPid = mk_pid(RNode, 4711, 1), RXPid = mk_pid(RNode, 32767, 8191), RPort = mk_port(RNode, 4711), @@ -142,7 +148,6 @@ term_to_binary_to_term_eq(Config) when is_list(Config) -> RLRef = binary_to_term(term_to_binary(RLRef)), RHLRef = binary_to_term(term_to_binary(RHLRef)), RSRef = binary_to_term(term_to_binary(RSRef)), - nc_refc_check(node()), ok. @@ -807,7 +812,7 @@ bad_nc(Config) when is_list(Config) -> = (catch mk_ref(RemNode, [(1 bsl 18), 4711, 4711])), {'EXIT', {badarg, mk_ref, _}} = (catch mk_ref(RemNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])), - BadNode = {x@y, 4}, + BadNode = {x@y, bad_creation}, {'EXIT', {badarg, mk_pid, _}} = (catch mk_pid(BadNode, 4711, 17)), {'EXIT', {badarg, mk_port, _}} @@ -1111,6 +1116,9 @@ get_nodename() -> -define(PORT_EXT, 102). -define(PID_EXT, 103). -define(NEW_REFERENCE_EXT, 114). +-define(NEW_PID_EXT, $X). +-define(NEW_PORT_EXT, $Y). +-define(NEWER_REFERENCE_EXT, $Z). uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 -> [(Uint bsr 24) band 16#ff, @@ -1133,51 +1141,65 @@ uint8(Uint) -> exit({badarg, uint8, [Uint]}). +pid_tag(bad_creation) -> ?PID_EXT; +pid_tag(Creation) when Creation =< 3 -> ?PID_EXT; +pid_tag(_Creation) -> ?NEW_PID_EXT. + +enc_creation(bad_creation) -> uint8(4); +enc_creation(Creation) when Creation =< 3 -> uint8(Creation); +enc_creation(Creation) -> uint32_be(Creation). mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) -> mk_pid({atom_to_list(NodeName), Creation}, Number, Serial); mk_pid({NodeName, Creation}, Number, Serial) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PID_EXT, - ?ATOM_EXT, - uint16_be(length(NodeName)), - NodeName, - uint32_be(Number), - uint32_be(Serial), - uint8(Creation)])) of - Pid when is_pid(Pid) -> - Pid; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + pid_tag(Creation), + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + uint32_be(Serial), + enc_creation(Creation)])) of + Pid when is_pid(Pid) -> + Pid; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. +port_tag(bad_creation) -> ?PORT_EXT; +port_tag(Creation) when Creation =< 3 -> ?PORT_EXT; +port_tag(_Creation) -> ?NEW_PORT_EXT. + mk_port({NodeName, Creation}, Number) when is_atom(NodeName) -> mk_port({atom_to_list(NodeName), Creation}, Number); mk_port({NodeName, Creation}, Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PORT_EXT, - ?ATOM_EXT, - uint16_be(length(NodeName)), - NodeName, - uint32_be(Number), - uint8(Creation)])) of - Port when is_port(Port) -> - Port; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_port, [{NodeName, Creation}, Number]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + port_tag(Creation), + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + enc_creation(Creation)])) of + Port when is_port(Port) -> + Port; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_port, [{NodeName, Creation}, Number]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. +ref_tag(bad_creation) -> ?NEW_REFERENCE_EXT; +ref_tag(Creation) when Creation =< 3 -> ?NEW_REFERENCE_EXT; +ref_tag(_Creation) -> ?NEWER_REFERENCE_EXT. + mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName), - is_integer(Creation), - is_list(Numbers) -> + is_list(Numbers) -> mk_ref({atom_to_list(NodeName), Creation}, Numbers); mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName), - is_integer(Creation), - is_integer(Number) -> + Creation =< 3, + is_integer(Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, ?REFERENCE_EXT, ?ATOM_EXT, @@ -1193,25 +1215,24 @@ mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName), exit({unexpected_binary_to_term_result, Other}) end; mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName), - is_integer(Creation), - is_list(Numbers) -> + is_list(Numbers) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?NEW_REFERENCE_EXT, - uint16_be(length(Numbers)), - ?ATOM_EXT, - uint16_be(length(NodeName)), - NodeName, - uint8(Creation), - lists:map(fun (N) -> - uint32_be(N) - end, - Numbers)])) of - Ref when is_reference(Ref) -> - Ref; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ref_tag(Creation), + uint16_be(length(Numbers)), + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + enc_creation(Creation), + lists:map(fun (N) -> + uint32_be(N) + end, + Numbers)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end. exec_loop() -> -- cgit v1.2.3 From 1e7422e64ef5f0713115715abb3925b690796b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Apr 2016 18:42:07 +0200 Subject: Use ct:fail/1 instead of test_server:fail/1 --- erts/emulator/test/scheduler_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 64c280b198..20e473d327 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -1733,7 +1733,7 @@ sched_state([], N, DC, DI) -> {N, DC, DI} catch _ : _ -> - ?t:fail({inconsisten_scheduler_state, {N, DC, DI}}) + ct:fail({inconsisten_scheduler_state, {N, DC, DI}}) end; sched_state([{normal, _, _, _} = S | Rest], _S, DC, DI) -> sched_state(Rest, S, DC, DI); -- cgit v1.2.3 From e4b8b9be39601b1c79dfd5d8e807ae4c404b6935 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 7 Apr 2016 20:38:55 +0200 Subject: erts: Add DFLAG_BIG_CREATION to let future nodes know that we can handle NEW_PID_EXT, NEW_PORT_EXT and NEWER_REFERENCE_EXT. --- erts/emulator/beam/dist.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index fb777d9ac1..e3ff6ebad1 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -43,6 +43,7 @@ #define DFLAG_INTERNAL_TAGS 0x8000 #define DFLAG_UTF8_ATOMS 0x10000 #define DFLAG_MAP_TAG 0x20000 +#define DFLAG_BIG_CREATION 0x40000 /* All flags that should be enabled when term_to_binary/1 is used. */ #define TERM_TO_BINARY_DFLAGS (DFLAG_EXTENDED_REFERENCES \ @@ -51,7 +52,8 @@ | DFLAG_EXTENDED_PIDS_PORTS \ | DFLAG_EXPORT_PTR_TAG \ | DFLAG_BIT_BINARIES \ - | DFLAG_MAP_TAG) + | DFLAG_MAP_TAG \ + | DFLAG_BIG_CREATION) /* opcodes used in distribution messages */ #define DOP_LINK 1 -- cgit v1.2.3 From 3c0f98c1b0e74eb58a4e4e82a3ec89dc7ecdb579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 7 Apr 2016 10:06:44 +0200 Subject: Simplify the raise instruction to reduce code size The raise/2 instruction is almost always used like this: raise x(2) x(1) Therefore, we can translate it to an internal i_raise/0 instruction that uses x(2) x(1) as its implicit operands. We will also remove the backward compatibility with R10-0. It is unlikely that anyone still is using BEAM files compiled with the R10-0 compiler, especially since most of those modules cannot be loaded. The loader will refuse to load any module that uses the old non-GCIng arithmetic instructions or the non-GCing versions of length/1 or size/1. Doing these changes will reduce both the size of the loaded BEAM code and size of the code in process_main(). --- erts/emulator/beam/beam_emu.c | 54 ++++++++++--------------------------------- erts/emulator/beam/ops.tab | 9 +++++++- 2 files changed, 20 insertions(+), 43 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 09a41f2b56..d648a2f23c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3360,48 +3360,18 @@ do { \ goto do_schedule; } - OpCase(raise_ss): { - /* This was not done very well in R10-0; then, we passed the tag in - the first argument and hoped that the existing c_p->ftrace was - still correct. But the ftrace-object already includes the tag - (or rather, the freason). Now, we pass the original ftrace in - the first argument. We also handle atom tags in the first - argument for backwards compatibility. - */ - Eterm raise_val1; - Eterm raise_val2; - GetArg2(0, raise_val1, raise_val2); - c_p->fvalue = raise_val2; - if (c_p->freason == EXC_NULL) { - /* a safety check for the R10-0 case; should not happen */ - c_p->ftrace = NIL; - c_p->freason = EXC_ERROR; - } - /* for R10-0 code, keep existing c_p->ftrace and hope it's correct */ - switch (raise_val1) { - case am_throw: - c_p->freason = EXC_THROWN & ~EXF_SAVETRACE; - break; - case am_error: - c_p->freason = EXC_ERROR & ~EXF_SAVETRACE; - break; - case am_exit: - c_p->freason = EXC_EXIT & ~EXF_SAVETRACE; - break; - default: - {/* R10-1 and later - XXX note: should do sanity check on given trace if it can be - passed from a user! Currently only expecting generated calls. - */ - struct StackTrace *s; - c_p->ftrace = raise_val1; - s = get_trace_from_exc(raise_val1); - if (s == NULL) { - c_p->freason = EXC_ERROR; - } else { - c_p->freason = PRIMARY_EXCEPTION(s->freason); - } - } + OpCase(i_raise): { + Eterm raise_trace = x(2); + Eterm raise_value = x(1); + struct StackTrace *s; + + c_p->fvalue = raise_value; + c_p->ftrace = raise_trace; + s = get_trace_from_exc(raise_trace); + if (s == NULL) { + c_p->freason = EXC_ERROR; + } else { + c_p->freason = PRIMARY_EXCEPTION(s->freason); } goto find_func_info; } diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 15f27835a8..96a3a72bb5 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -251,7 +251,14 @@ case_end x badmatch x if_end -raise s s + +# Operands for raise/2 are almost always in x(2) and x(1). +# Optimize for that case. +raise x==2 x==1 => i_raise +raise Trace=y Value=y => move Trace x=2 | move Value x=1 | i_raise +raise Trace Value => move Trace x=3 | move Value x=1 | move x=3 x=2 | i_raise + +i_raise # Internal now, but could be useful to make known to the compiler. badarg j -- cgit v1.2.3 From 858ded838419a375a505f55f2cdb2d15acdc8e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Apr 2016 18:46:10 +0200 Subject: Don't divide by zero in test logging --- erts/emulator/test/multi_load_SUITE.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/multi_load_SUITE.erl b/erts/emulator/test/multi_load_SUITE.erl index 784b239116..f3fab02170 100644 --- a/erts/emulator/test/multi_load_SUITE.erl +++ b/erts/emulator/test/multi_load_SUITE.erl @@ -81,9 +81,12 @@ many_measure(Ms) -> "Sequential: ~9w µs\n" "Parallel: ~9w µs\n" "Ratio: ~9w\n", - [length(Ms),Us1,Us2,round(Us1/Us2)]), + [length(Ms),Us1,Us2,divide(Us1,Us2)]), ok. +divide(A,B) when B > 0 -> A div B; +divide(_,_) -> inf. + many_load_seq(Ms) -> [erlang:finish_loading([M]) || M <- Ms], ok. -- cgit v1.2.3 From 11827799463aa12ac94c387c6c7a38399af3e072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Apr 2016 18:48:54 +0200 Subject: Remove unnecessary ct boilerplate in suite --- erts/emulator/test/multi_load_SUITE.erl | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/multi_load_SUITE.erl b/erts/emulator/test/multi_load_SUITE.erl index f3fab02170..e8769ea208 100644 --- a/erts/emulator/test/multi_load_SUITE.erl +++ b/erts/emulator/test/multi_load_SUITE.erl @@ -19,32 +19,16 @@ %% -module(multi_load_SUITE). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - many/1,on_load/1,errors/1]). +-export([all/0, suite/0, many/1, on_load/1, errors/1]). -include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [many,on_load,errors]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - many(_Config) -> Ms = make_modules(100, fun many_module/1), @@ -57,7 +41,6 @@ many(_Config) -> io:put_chars("Heavy load\n" "=========="), many_measure(Ms), - ok. many_module(M) -> @@ -138,7 +121,6 @@ on_load(_Config) -> SingleOnPrep = tl(OnPrep), {on_load,[OnLoadMod]} = erlang:finish_loading(SingleOnPrep), ok = erlang:call_on_load_function(OnLoadMod), - ok. on_load_module(M) -> -- cgit v1.2.3 From 313e9305fce0843c68bccd70a2c45e37620dafb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Apr 2016 19:04:12 +0200 Subject: Increase timetrap for atom_roundtrip_r15b --- erts/emulator/test/distribution_SUITE.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index f116ec979b..b068a4c8d2 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -1034,6 +1034,7 @@ atom_roundtrip(Config) when is_list(Config) -> atom_roundtrip_r15b(Config) when is_list(Config) -> case test_server:is_release_available("r15b") of true -> + ct:timetrap({minutes, 6}), AtomData = atom_data(), verify_atom_data(AtomData), {ok, Node} = start_node(Config, [], "r15b"), -- cgit v1.2.3 From 27217260c7ac21d4e81d7758a43b27ff8ed7cdf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Apr 2016 19:12:31 +0200 Subject: Let low_prio test run a bit longer --- erts/emulator/test/process_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 5bb216ff79..789fa7cf06 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -1003,7 +1003,7 @@ low_prio_test(Config) when is_list(Config) -> process_flag(trap_exit, true), S = spawn_link(?MODULE, prio_server, [0, 0]), PCs = spawn_prio_clients(S, erlang:system_info(schedulers_online)), - timer:sleep(2000), + ct:sleep({seconds,3}), lists:foreach(fun (P) -> exit(P, kill) end, PCs), S ! exit, receive {'EXIT', S, {A, B}} -> check_prio(A, B) end, -- cgit v1.2.3 From a9dfa8abdec770880d61cc694b52ce2efca11fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Apr 2016 19:25:47 +0200 Subject: Replace test_server:os_type/0 with os:type/0 --- erts/emulator/test/alloc_SUITE.erl | 4 ++-- erts/emulator/test/erl_drv_thread_SUITE.erl | 2 +- erts/emulator/test/ignore_cores.erl | 2 +- erts/emulator/test/node_container_SUITE.erl | 2 +- erts/emulator/test/port_SUITE.erl | 2 +- erts/emulator/test/scheduler_SUITE.erl | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 1f690c5015..82f2849278 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -81,7 +81,7 @@ migration(Cfg) -> end. erts_mmap(Config) when is_list(Config) -> - case test_server:os_type() of + case os:type() of {unix, _} -> [erts_mmap_do(Config, SCO, SCRPM, SCRFSD) || SCO <-[true,false], SCRFSD <-[1234,0], SCRPM <- [true,false]]; @@ -144,7 +144,7 @@ drv_case(Config) -> drv_case(Config, one_shot, ""). drv_case(Config, Mode, NodeOpts) when is_list(Config) -> - case test_server:os_type() of + case os:type() of {Family, _} when Family == unix; Family == win32 -> {ok, Node} = start_node(Config, NodeOpts), Self = self(), diff --git a/erts/emulator/test/erl_drv_thread_SUITE.erl b/erts/emulator/test/erl_drv_thread_SUITE.erl index 41a761229c..294d9ee05f 100644 --- a/erts/emulator/test/erl_drv_thread_SUITE.erl +++ b/erts/emulator/test/erl_drv_thread_SUITE.erl @@ -65,7 +65,7 @@ drv_case(Config, CaseName, Command, TimeTrap) when is_list(Config), is_atom(CaseName), is_list(Command), is_integer(TimeTrap) -> - case test_server:os_type() of + case os:type() of {Family, _} when Family == unix; Family == win32 -> run_drv_case(Config, CaseName, Command, TimeTrap); SkipOs -> diff --git a/erts/emulator/test/ignore_cores.erl b/erts/emulator/test/ignore_cores.erl index 7373303a39..da6f6850c6 100644 --- a/erts/emulator/test/ignore_cores.erl +++ b/erts/emulator/test/ignore_cores.erl @@ -94,7 +94,7 @@ setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite), end, ok = file:write_file(filename:join([IgnDir, "ignore_core_files"]), <<>>), %% cores are dumped in /cores on MacOS X - CoresDir = case {test_server:os_type(), filelib:is_dir("/cores")} of + CoresDir = case {os:type(), filelib:is_dir("/cores")} of {{unix,darwin}, true} -> filelib:fold_files("/cores", "^core.*$", diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 5275290d04..662e5423b9 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -710,7 +710,7 @@ run_otp_4715(Config) when is_list(Config) -> pid_wrap(Config) when is_list(Config) -> pp_wrap(pid). port_wrap(Config) when is_list(Config) -> - case test_server:os_type() of + case os:type() of {unix, _} -> pp_wrap(port); _ -> diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index b85d789bbe..cfbc664b56 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1757,7 +1757,7 @@ otp_6224_loop() -> exit_status_multi_scheduling_block(Config) when is_list(Config) -> Repeat = 3, - case test_server:os_type() of + case os:type() of {unix, _} -> ct:timetrap({minutes, 2*Repeat}), SleepSecs = 6, diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 20e473d327..0b4b302908 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -876,7 +876,7 @@ get_affinity_mask(_Port, _Status, Affinity) -> Affinity. get_affinity_mask() -> - case test_server:os_type() of + case os:type() of {unix, linux} -> case catch open_port({spawn, "taskset -p " ++ os:getpid()}, [exit_status]) of -- cgit v1.2.3 From d827807d703f03d0d6bdbc771e569b046828c274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Apr 2016 19:38:16 +0200 Subject: Additional logging for alloc_SUITE --- erts/emulator/test/alloc_SUITE.erl | 47 ++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 27 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 82f2849278..03b020c521 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -55,21 +55,13 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %% %% basic(Cfg) -> drv_case(Cfg). - coalesce(Cfg) -> drv_case(Cfg). - threads(Cfg) -> drv_case(Cfg). - realloc_copy(Cfg) -> drv_case(Cfg). - bucket_index(Cfg) -> drv_case(Cfg). - bucket_mask(Cfg) -> drv_case(Cfg). - rbtree(Cfg) -> drv_case(Cfg). - mseg_clear_cache(Cfg) -> drv_case(Cfg). - cpool(Cfg) -> drv_case(Cfg). migration(Cfg) -> @@ -109,25 +101,26 @@ erts_mmap_do(Config, SCO, SCRPM, SCRFSD) -> {ok, Node} = start_node(Config, Opts), Self = self(), Ref = make_ref(), - F = fun () -> - SI = erlang:system_info({allocator,mseg_alloc}), - {erts_mmap,EM} = lists:keyfind(erts_mmap, 1, SI), - {supercarrier,SC} = lists:keyfind(supercarrier, 1, EM), - {sizes,Sizes} = lists:keyfind(sizes, 1, SC), - {free_segs,Segs} = lists:keyfind(free_segs,1,SC), - {total,Total} = lists:keyfind(total,1,Sizes), - Total = SCS*1024*1024, - - {reserved,Reserved} = lists:keyfind(reserved,1,Segs), - true = (Reserved >= SCRFSD), - - case {SCO,lists:keyfind(os,1,EM)} of - {true, false} -> ok; - {false, {os,_}} -> ok - end, - - Self ! {Ref, ok} - end, + F = fun() -> + SI = erlang:system_info({allocator,mseg_alloc}), + {erts_mmap,EM} = lists:keyfind(erts_mmap, 1, SI), + {supercarrier,SC} = lists:keyfind(supercarrier, 1, EM), + {sizes,Sizes} = lists:keyfind(sizes, 1, SC), + {free_segs,Segs} = lists:keyfind(free_segs,1,SC), + {total,Total} = lists:keyfind(total,1,Sizes), + io:format("Expecting total ~w, got ~w~n", [SCS*1024*1024,Total]), + Total = SCS*1024*1024, + + {reserved,Reserved} = lists:keyfind(reserved,1,Segs), + true = (Reserved >= SCRFSD), + + case {SCO,lists:keyfind(os,1,EM)} of + {true, false} -> ok; + {false, {os,_}} -> ok + end, + + Self ! {Ref, ok} + end, spawn_link(Node, F), Result = receive {Ref, Rslt} -> Rslt end, -- cgit v1.2.3 From 415dc5b637624b8f3dc08fcf881e941a555f93da Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Sun, 10 Apr 2016 04:52:52 +0300 Subject: Typos correction, minor wording change --- erts/epmd/src/epmd_int.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index 09317094c7..574e2b5403 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -253,8 +253,8 @@ static const struct in6_addr in6addr_loopback = #define EPMD_TRUE 1 /* If no activity we let select() return every IDLE_TIMEOUT second - A file descriptor that are idle for CLOSE_TIMEOUT seconds and - isn't a ALIVE socket is probably hanging and we close it */ + A file descriptor that has been idle for CLOSE_TIMEOUT seconds and + isn't an ALIVE socket has probably hanged and should be closed */ #define IDLE_TIMEOUT 5 #define CLOSE_TIMEOUT 60 -- cgit v1.2.3 From c56c6d36c3060e62925f036ad020da1753e48829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sat, 9 Apr 2016 00:54:40 +0200 Subject: erts: Optimize '++' operator This also optimizes the BIF lists:append/2 Use one pass to check for properness and copying LHS list. If LHS turns out not being a proper list, bail and reset htop. If we run out of heap, allocate a heap-fragment and calculate the remaining length as normal, thus checking for properness, and then continue copying. Measurements shows this being ~50% faster. --- erts/emulator/beam/erl_bif_lists.c | 93 +++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c index fe64e76575..647a218851 100644 --- a/erts/emulator/beam/erl_bif_lists.c +++ b/erts/emulator/beam/erl_bif_lists.c @@ -40,32 +40,93 @@ static BIF_RETTYPE append(Process* p, Eterm A, Eterm B) Eterm list; Eterm copy; Eterm last; - size_t need; - Eterm* hp; + Eterm* hp = NULL; Sint i; - if ((i = erts_list_length(A)) < 0) { - BIF_ERROR(p, BADARG); + list = A; + + if (is_nil(list)) { + BIF_RET(B); } - if (i == 0) { - BIF_RET(B); - } else if (is_nil(B)) { - BIF_RET(A); + + if (is_not_list(list)) { + BIF_ERROR(p, BADARG); } - need = 2*i; - hp = HAlloc(p, need); - list = A; + /* optimistic append on heap first */ + + if ((i = HeapWordsLeft(p) / 2) < 4) { + goto list_tail; + } + + hp = HEAP_TOP(p); copy = last = CONS(hp, CAR(list_val(list)), make_list(hp+2)); list = CDR(list_val(list)); - hp += 2; + hp += 2; + i -= 2; /* don't use the last 2 words (extra i--;) */ + + while(i-- && is_list(list)) { + Eterm* listp = list_val(list); + last = CONS(hp, CAR(listp), make_list(hp+2)); + list = CDR(listp); + hp += 2; + } + + /* A is proper and B is NIL return A as-is, don't update HTOP */ + + if (is_nil(list) && is_nil(B)) { + BIF_RET(A); + } + + if (is_nil(list)) { + HEAP_TOP(p) = hp; + CDR(list_val(last)) = B; + BIF_RET(copy); + } + +list_tail: + + if ((i = erts_list_length(list)) < 0) { + BIF_ERROR(p, BADARG); + } + + /* remaining list was proper and B is NIL */ + if (is_nil(B)) { + BIF_RET(A); + } + + if (hp) { + /* Note: fall through case, already written + * on the heap. + * The last 2 words of the heap is not written yet + */ + Eterm *hp_save = hp; + ASSERT(i != 0); + HEAP_TOP(p) = hp + 2; + if (i == 1) { + hp[0] = CAR(list_val(list)); + hp[1] = B; + BIF_RET(copy); + } + hp = HAlloc(p, 2*(i - 1)); + last = CONS(hp_save, CAR(list_val(list)), make_list(hp)); + } else { + hp = HAlloc(p, 2*i); + copy = last = CONS(hp, CAR(list_val(list)), make_list(hp+2)); + hp += 2; + } + + list = CDR(list_val(list)); i--; + + ASSERT(i > -1); while(i--) { - Eterm* listp = list_val(list); - last = CONS(hp, CAR(listp), make_list(hp+2)); - list = CDR(listp); - hp += 2; + Eterm* listp = list_val(list); + last = CONS(hp, CAR(listp), make_list(hp+2)); + list = CDR(listp); + hp += 2; } + CDR(list_val(last)) = B; BIF_RET(copy); } -- cgit v1.2.3 From 97cdb6bd87f38b06ee477dbb17d4d2f7854b21e1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 13 Apr 2016 15:18:13 +0200 Subject: erts: Fix bug in open_port with {args,ImproperList} --- erts/emulator/beam/erl_bif_port.c | 2 ++ erts/emulator/test/port_SUITE.erl | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 27c24197ea..e4d4559f48 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -953,6 +953,8 @@ static char **convert_args(Eterm l) } n = erts_list_length(l); + if (n < 0) + return NULL; /* We require at least one element in argv[0] + NULL at end */ pp = erts_alloc(ERTS_ALC_T_TMP, (n + 2) * sizeof(char **)); pp[i++] = erts_default_arg0; diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 3d0509a28c..c0c0470c94 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -86,6 +86,7 @@ name1/1, t_binary/1, parallell/1, t_exit/1, env/1, bad_env/1, cd/1, exit_status/1, + bad_args/1, tps_16_bytes/1, tps_1K/1, line/1, stderr_to_stdout/1, otp_3906/1, otp_4389/1, win_massive/1, win_massive_client/1, mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1, @@ -112,6 +113,7 @@ all() -> {group, multiple_packets}, parallell, dying_port, port_program_with_path, open_input_file_port, open_output_file_port, name1, env, bad_env, cd, + bad_args, exit_status, iter_max_ports, t_exit, {group, tps}, line, stderr_to_stdout, otp_3906, otp_4389, win_massive, mix_up_ports, otp_5112, otp_5119, @@ -923,6 +925,23 @@ try_bad_env(Env) -> error:badarg -> ok end. +%% Test bad 'args' options. +bad_args(Config) when is_list(Config) -> + try_bad_args({args, [self()]}), + try_bad_args({args, ["head" | "tail"]}), + try_bad_args({args, ["head", "body" | "tail"]}), + try_bad_args({args, [<<"head">>, <<"body">> | <<"tail">>]}), + try_bad_args({args, not_a_list}), + try_bad_args({args, ["string",<<"binary">>, 1472, "string"]}), + try_bad_args({args, ["string",<<"binary">>], "element #3"}), + ok. + +try_bad_args(Args) -> + badarg = try open_port({spawn_executable,"ls"}, [Args]) + catch + error:badarg -> badarg + end. + %% 'cd' option %% (Can perhaps be made smaller by calling the other utility functions %% in this module.) -- cgit v1.2.3 From 588ed7d503f7f607ec91d102f16573ee5d64ea7b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 13 Apr 2016 15:20:53 +0200 Subject: erts: Improve port_SUITE:bad_env It's not just ok to throw badarg, it MUST throw badarg. --- erts/emulator/test/port_SUITE.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index c0c0470c94..a40d9a4d1f 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -920,10 +920,10 @@ bad_env(Config) when is_list(Config) -> ok. try_bad_env(Env) -> - try open_port({spawn,"ls"}, [{env,Env}]) - catch - error:badarg -> ok - end. + badarg = try open_port({spawn,"ls"}, [{env,Env}]) + catch + error:badarg -> badarg + end. %% Test bad 'args' options. bad_args(Config) when is_list(Config) -> -- cgit v1.2.3 From 350c2beb2ed583aa6b0345e8118615bd3c5ccc9d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 13 Apr 2016 20:02:21 +0200 Subject: erts: Fix decoding of pids, ports and refs with big creations Must skip 3 extra bytes after node name atom. --- erts/emulator/beam/external.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 5ea155f83f..243ba8d9f3 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -4425,22 +4425,32 @@ init_done: SKIP(1+atom_extra_skip); atom_extra_skip = 0; break; - case PID_EXT: case NEW_PID_EXT: + atom_extra_skip = 12; + goto case_PID; + case PID_EXT: atom_extra_skip = 9; + case_PID: /* In case it is an external pid */ heap_size += EXTERNAL_THING_HEAD_SIZE + 1; terms++; break; - case PORT_EXT: case NEW_PORT_EXT: + atom_extra_skip = 8; + goto case_PORT; + case PORT_EXT: atom_extra_skip = 5; + case_PORT: /* In case it is an external port */ heap_size += EXTERNAL_THING_HEAD_SIZE + 1; terms++; break; - case NEW_REFERENCE_EXT: case NEWER_REFERENCE_EXT: + atom_extra_skip = 4; + goto case_NEW_REFERENCE; + case NEW_REFERENCE_EXT: + atom_extra_skip = 1; + case_NEW_REFERENCE: { int id_words; @@ -4451,7 +4461,7 @@ init_done: goto error; ep += 2; - atom_extra_skip = 1 + 4*id_words; + atom_extra_skip += 4*id_words; /* In case it is an external ref */ #if defined(ARCH_64) heap_size += EXTERNAL_THING_HEAD_SIZE + id_words/2 + 1; -- cgit v1.2.3 From 21041ee50217d90dc21dd484b9dd26c469e8cf87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 13 Apr 2016 15:07:52 +0200 Subject: beam_debug: Correct masking when unpacking packed operands --- erts/emulator/beam/beam_debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index e37bd4d78c..7fddf15dab 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -431,7 +431,7 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) packed >>= 10; break; case '0': /* Tight shift */ - *ap++ = packed & (BEAM_TIGHT_MASK / sizeof(Eterm)); + *ap++ = packed & BEAM_TIGHT_MASK; packed >>= BEAM_TIGHT_SHIFT; break; case '6': /* Shift 16 steps */ -- cgit v1.2.3 From 19f231024d5fc4e0e7084b6b38ca404f3368844a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 14 Apr 2016 10:04:49 +0200 Subject: Eliminate misleading #ifdef ARCH_64 in beam_opcodes.h There is a '#ifdef ARCH_64' beam_opcodes.h, which might make you think that files generated by beam_makeops will work for both 32-bit and 64-bit architectures. They will not. beam_makeops will generate different code depending on its -wordsize option. --- erts/emulator/utils/beam_makeops | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 2e7073a8f0..31cd072d5a 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -624,19 +624,25 @@ sub emulator_output { print "#define NUM_SPECIFIC_OPS ", scalar(@op_to_name), "\n"; print "#define SCRATCH_X_REG 1023\n"; print "\n"; - print "#ifdef ARCH_64\n"; - print "# define BEAM_WIDE_MASK 0xFFFFUL\n"; - print "# define BEAM_LOOSE_MASK 0xFFFFUL\n"; - print "# define BEAM_TIGHT_MASK 0xFFFFUL\n"; - print "# define BEAM_WIDE_SHIFT 32\n"; - print "# define BEAM_LOOSE_SHIFT 16\n"; - print "# define BEAM_TIGHT_SHIFT 16\n"; - print "#else\n"; - print "# define BEAM_LOOSE_MASK 0xFFF\n"; - print "# define BEAM_TIGHT_MASK 0xFFC\n"; - print "# define BEAM_LOOSE_SHIFT 16\n"; - print "# define BEAM_TIGHT_SHIFT 10\n"; - print "#endif\n"; + if ($wordsize == 32) { + print "#if defined(ARCH_64)\n"; + print qq[ #error "32-bit architecture assumed, but ARCH_64 is defined"\n]; + print "#endif\n"; + print "#define BEAM_LOOSE_MASK 0xFFF\n"; + print "#define BEAM_TIGHT_MASK 0xFFC\n"; + print "#define BEAM_LOOSE_SHIFT 16\n"; + print "#define BEAM_TIGHT_SHIFT 10\n"; + } elsif ($wordsize == 64) { + print "#if !defined(ARCH_64)\n"; + print qq[ #error "64-bit architecture assumed, but ARCH_64 not defined"\n]; + print "#endif\n"; + print "#define BEAM_WIDE_MASK 0xFFFFUL\n"; + print "#define BEAM_LOOSE_MASK 0xFFFFUL\n"; + print "#define BEAM_TIGHT_MASK 0xFFFFUL\n"; + print "#define BEAM_WIDE_SHIFT 32\n"; + print "#define BEAM_LOOSE_SHIFT 16\n"; + print "#define BEAM_TIGHT_SHIFT 16\n"; + } print "\n"; # -- cgit v1.2.3 From e72f710ae493cf7ba5da375632b096830df2de5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 13 Apr 2016 16:22:31 +0200 Subject: Correct unpacking of 3 operands on 32-bit archictectures 0a4750f91c83 optimized unpacking by removing a mask operation when unpacking three packed operands. Unfortunately, that optimization is only safe on 64-bit architectures. Here is what happens on 32-bit architectures. The operands to be packed are 10-bit register numbers that have been turned into byte offsets: aaaaaaaaaa00 bbbbbbbbbb00 cccccccccc00 They can be packed into a single word like this: 30 20 10 0 | | | | aa aaaaaaaabb bbbbbbbbcc cccccccc00 If we call the packed word P, the original operands can be extracted like this: C = P band 2#111111111100 B = (P bsr 10) band 2#111111111100 A = (P bsr 20) band 2#111111111100 The bug was that A was extracted without the masking: A = P bsr 20 That would give A the value: aaaaaaaaaaaabb That would only be safe if the two most significant bits in B were zeroes. --- erts/emulator/utils/beam_makeops | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 31cd072d5a..24eace9fc4 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -48,7 +48,7 @@ $pack_shift[4] = ['0', 'BEAM_LOOSE_SHIFT', # Only for 64 bit wordsize '(3*BEAM_LOOSE_SHIFT)']; $pack_mask[2] = ['BEAM_LOOSE_MASK', $WHOLE_WORD]; -$pack_mask[3] = ['BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK', $WHOLE_WORD]; +$pack_mask[3] = ['BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK']; $pack_mask[4] = ['BEAM_LOOSE_MASK', # Only for 64 bit wordsize 'BEAM_LOOSE_MASK', 'BEAM_LOOSE_MASK', @@ -251,6 +251,7 @@ $args_per_word[5] = 3; $args_per_word[6] = 3; if ($wordsize == 64) { + $pack_mask[3] = ['BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK', $WHOLE_WORD]; $args_per_word[4] = 4; } -- cgit v1.2.3 From 20ffaef67ccaf4f2a7df80d25a88ce8cb2a2a15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 14 Apr 2016 07:22:07 +0200 Subject: Mend beam_SUITE:packed_registers/1 packed_registers/1 may have actually tested put_list/3 instructions with high register numbers at some time. Currently, the compiler will generate code that only uses low register numbers. Totally rewrite the test case. It is difficult to arrange so that put_list/3 uses three high register numbers, so we will use get_list/3 instructions with high register numbers. --- erts/emulator/test/beam_SUITE.erl | 77 +++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 39 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl index f61ab431e9..ca37ab68a7 100644 --- a/erts/emulator/test/beam_SUITE.erl +++ b/erts/emulator/test/beam_SUITE.erl @@ -29,6 +29,7 @@ -export([applied/2,swap_temp_applied/1]). -include_lib("common_test/include/ct.hrl"). +-include_lib("syntax_tools/include/merl.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -93,48 +94,46 @@ applied(Starter, N) -> apply_last_bif(Config) when is_list(Config) -> apply(erlang, abs, [1]). -%% Test three high register numbers in a put_list instruction -%% (to test whether packing works properly). +%% Test whether packing works properly. packed_registers(Config) when is_list(Config) -> - PrivDir = proplists:get_value(priv_dir, Config), - Mod = packed_regs, - Name = filename:join(PrivDir, atom_to_list(Mod) ++ ".erl"), - - %% Generate a module which generates a list of tuples. - %% put_list(A) -> [{A, 600}, {A, 999}, ... {A, 0}]. - Code = gen_packed_regs(600, ["-module("++atom_to_list(Mod)++").\n", - "-export([put_list/1]).\n", - "put_list(A) ->\n["]), - ok = file:write_file(Name, Code), - - %% Compile the module. - io:format("Compiling: ~s\n", [Name]), - CompRc = compile:file(Name, [{outdir, PrivDir}, report]), - io:format("Result: ~p\n",[CompRc]), - {ok, Mod} = CompRc, - - %% Load it. - io:format("Loading...\n",[]), - LoadRc = code:load_abs(filename:join(PrivDir, atom_to_list(Mod))), - {module,_Module} = LoadRc, - - %% Call it and verify result. - Term = {a, b}, - L = Mod:put_list(Term), - verify_packed_regs(L, Term, 600), + Mod = ?FUNCTION_NAME, + + %% Generate scrambled sequence. + Seq0 = [{erlang:phash2(I),I} || I <- lists:seq(0, 260)], + Seq = [I || {_,I} <- lists:sort(Seq0)], + + %% Generate a test modules that uses get_list/3 instructions + %% with high register numbers. + S0 = [begin + VarName = list_to_atom("V"++integer_to_list(V)), + {merl:var(VarName),V} + end || V <- Seq], + Vars = [V || {V,_} <- S0], + NewVars = [begin + VarName = list_to_atom("M"++integer_to_list(V)), + merl:var(VarName) + end || V <- Seq], + S = [?Q("_@Var = id(_@Value@)") || {Var,Value} <- S0], + Code = ?Q(["-module('@Mod@').\n" + "-export([f/0]).\n" + "f() ->\n" + "_@S,\n" + "_ = id(0),\n" + "L = [_@Vars],\n" + "_ = id(1),\n" + "[_@NewVars] = L,\n" %Test get_list/3. + "_ = id(2),\n" + "id([_@Vars,_@NewVars]).\n" + "id(I) -> I.\n"]), + merl:compile_and_load(Code), + CombinedSeq = Seq ++ Seq, + CombinedSeq = Mod:f(), + + %% Clean up. + true = code:delete(Mod), + false = code:purge(Mod), ok. -gen_packed_regs(0, Acc) -> - [Acc|"{A,0}].\n"]; -gen_packed_regs(N, Acc) -> - gen_packed_regs(N-1, [Acc,"{A,",integer_to_list(N)|"},\n"]). - -verify_packed_regs([], _, -1) -> ok; -verify_packed_regs([{Term, N}| T], Term, N) -> - verify_packed_regs(T, Term, N-1); -verify_packed_regs(L, Term, N) -> - ct:fail("Expected [{~p, ~p}|T]; got\n~p\n", [Term, N, L]). - buildo_mucho(Config) when is_list(Config) -> buildo_mucho_1(), ok. -- cgit v1.2.3 From 9b2ee6cfdc09090df81be40ca1d7358c3d273743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Sat, 9 Apr 2016 09:51:10 +0200 Subject: Optimize get_tuple_element instructions that target Y registers Several improvements in the compiler (e.g. c288ab87fd6) has lead to an Y register being the target for get_tuple_element instructions. Therefore, introduce i_get_tuple_element2y that combines two consecutive get_tuple_element instructions that target Y registers. --- erts/emulator/beam/beam_emu.c | 11 +++++++++++ erts/emulator/beam/ops.tab | 6 ++++++ 2 files changed, 17 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index d648a2f23c..ce92f60fd2 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -702,6 +702,17 @@ void** beam_ops; dst[1] = E2; \ } while (0) +#define GetTupleElement2Y(Src, Element, D1, D2) \ + do { \ + Eterm* src; \ + Eterm E1, E2; \ + src = ADD_BYTE_OFFSET(tuple_val(Src), (Element)); \ + E1 = src[0]; \ + E2 = src[1]; \ + D1 = E1; \ + D2 = E2; \ + } while (0) + #define GetTupleElement3(Src, Element, Dest) \ do { \ Eterm* src; \ diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 96a3a72bb5..aa720c5b0c 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -229,6 +229,9 @@ i_get_tuple_element y P y %macro: i_get_tuple_element2 GetTupleElement2 -pack i_get_tuple_element2 x P x +%macro: i_get_tuple_element2y GetTupleElement2Y -pack +i_get_tuple_element2y x P y y + %macro: i_get_tuple_element3 GetTupleElement3 -pack i_get_tuple_element3 x P x @@ -649,6 +652,9 @@ get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \ get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \ succ(P1, P2) | succ(D1, D2) => i_get_tuple_element2 Reg P1 D1 +get_tuple_element Reg=x P1 D1=y | get_tuple_element Reg=x P2 D2=y | \ + succ(P1, P2) => i_get_tuple_element2y Reg P1 D1 D2 + get_tuple_element Reg P Dst => i_get_tuple_element Reg P Dst is_integer Fail=f i => -- cgit v1.2.3 From c74f998b9ad46785aaa2557a4033c056574dc937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 12 Apr 2016 06:08:07 +0200 Subject: v3_kernel: Construct literal lists properly Use cerl:make_list/1 instead of a home-made make_list/1 to ensure that literal lists are constructed as literals. In a future release, we would like to forbid in the loader construction of literal lists using instructions like: put_list {atom,a} [] Dst The proper way is: move {literal,[a]} {x,0} Also update the comment about "put_list Const [] Dst" in ops.tab. --- erts/emulator/beam/ops.tab | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 96a3a72bb5..83b0299ddd 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -529,9 +529,9 @@ i_put_tuple x I i_put_tuple y I # -# The instruction "put_list Const [] Dst" will not be generated by -# the current BEAM compiler. But until R15A, play it safe by handling -# that instruction with the following transformation. +# The instruction "put_list Const [] Dst" were generated in rare +# circumstances up to and including OTP 18. Starting with OTP 19, +# AFAIK, it should never be generated. # put_list Const=c n Dst => move Const x | put_list x n Dst -- cgit v1.2.3 From 3e6f813222057941515df9cdb1f5d89ac3dd9cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 14 Apr 2016 15:35:57 +0200 Subject: erts: Don't use function location when process is terminating --- erts/emulator/beam/erl_trace.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index fb3f0d4e62..1654ea58d9 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -3020,6 +3020,7 @@ profile_runnable_proc(Process *p, Eterm status){ Eterm *hp, msg; Eterm where = am_undefined; ErlHeapFragment *bp = NULL; + int use_current = 1; #ifndef ERTS_SMP #define LOCAL_HEAP_SIZE (4 + 6 + ERTS_TRACE_PATCH_TS_MAX_SIZE) @@ -3032,12 +3033,19 @@ profile_runnable_proc(Process *p, Eterm status){ Uint hsz = 4 + 6 + patch_ts_size(erts_system_profile_ts_type)-1; #endif - if (!p->current) { - p->current = find_function_from_pc(p->i); + if (ERTS_PROC_IS_EXITING(p)) { + use_current = 0; + /* could probably set 'where' to 'exiting' here, + * though it's not documented as such */ + } else { + if (!p->current) { + p->current = find_function_from_pc(p->i); + } + use_current = p->current != NULL; } #ifdef ERTS_SMP - if (!p->current) { + if (!use_current) { hsz -= 4; } @@ -3045,7 +3053,7 @@ profile_runnable_proc(Process *p, Eterm status){ hp = bp->mem; #endif - if (p->current) { + if (use_current) { where = TUPLE3(hp, p->current[0], p->current[1], make_small(p->current[2])); hp += 4; } else { where = make_small(0); -- cgit v1.2.3 From 0fc3412387a889663f55515f123f1168e8f66be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 14 Apr 2016 16:46:31 +0200 Subject: erts: Enhance system_profile tests --- erts/emulator/test/system_profile_SUITE.erl | 32 +++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl index a56b394765..2e359b11ce 100644 --- a/erts/emulator/test/system_profile_SUITE.erl +++ b/erts/emulator/test/system_profile_SUITE.erl @@ -27,7 +27,7 @@ system_profile_on_and_off/1, runnable_procs/1, runnable_ports/1, dont_profile_profiler/1, - scheduler/1]). + scheduler/1, sane_location/1]). -export([profiler_process/1, ring_loop/1, port_echo_start/0, list_load/0, run_load/2]). @@ -40,7 +40,8 @@ suite() -> all() -> [system_profile_on_and_off, runnable_procs, - runnable_ports, scheduler, dont_profile_profiler]. + runnable_ports, scheduler, dont_profile_profiler, + sane_location]. %% No specification clause needed for an init function in a conf case!!! @@ -183,6 +184,33 @@ dont_profile_profiler(Config) when is_list(Config) -> exit(Pid,kill), ok. +%% Check sane location (of exits) +sane_location(Config) when is_list(Config) -> + Check = spawn_link(fun() -> flush_sane_location() end), + erlang:system_profile(Check, [runnable_procs]), + Me = self(), + Pids = [spawn_link(fun() -> wat(Me) end) || _ <- lists:seq(1,100)], + [receive {Pid,ok} -> ok end || Pid <- Pids], + Check ! {Me, done}, + receive {Check,ok} -> ok end, + ok. + +wat(Pid) -> + Pid ! {self(), ok}. + +flush_sane_location() -> + receive + {profile,_,_,{M,F,A},_} when is_atom(M), is_atom(F), + is_integer(A) -> + flush_sane_location(); + {profile,_,_,0,_} -> + flush_sane_location(); + {Pid,done} when is_pid(Pid) -> + Pid ! {self(), ok}; + M -> + ct:fail({badness,M}) + end. + %%% Check scheduler profiling -- cgit v1.2.3 From fa2f841dc17da2704c2307b6f04aaee85de2366d Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 10 Dec 2015 11:27:50 +0100 Subject: erts: Calculate flatmap value offset --- erts/emulator/beam/erl_map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index a40070d00d..7af9100906 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -66,7 +66,7 @@ typedef struct flatmap_s { /* erl_term.h stuff */ -#define flatmap_get_values(x) (((Eterm *)(x)) + 3) +#define flatmap_get_values(x) (((Eterm *)(x)) + sizeof(flatmap_t)/sizeof(Eterm)) #define flatmap_get_keys(x) (((Eterm *)tuple_val(((flatmap_t *)(x))->keys)) + 1) #define flatmap_get_size(x) (((flatmap_t*)(x))->size) -- cgit v1.2.3 From 13bd4ca2493d8a76f9d835c27163b56ba86c2aef Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 10 Dec 2015 11:28:56 +0100 Subject: erts: Add erts_nif_get_funcs and erts_nif_call_function These are convinience functions for calling nifs from erts --- erts/emulator/beam/erl_nif.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ erts/emulator/beam/erl_nif.h | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 1f5793f534..61dba4ef82 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -2960,6 +2960,51 @@ void erl_nif_init() resource_type_list.name = THE_NON_VALUE; } +int erts_nif_get_funcs(struct erl_module_nif* mod, + ErlNifFunc **funcs) +{ + *funcs = mod->entry->funcs; + return mod->entry->num_of_funcs; +} + +Eterm erts_nif_call_function(Process *p, struct erl_module_nif* mod, + ErlNifFunc *fun, int argc, Eterm *argv) +{ + Eterm nif_result; +#ifdef DEBUG + /* Verify that function is part of this module */ + int i; + for (i = 0; i < mod->entry->num_of_funcs; i++) + if (fun == mod->entry->funcs+i) + break; + ASSERT(i < mod->entry->num_of_funcs); + if (p) + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) & ERTS_PROC_LOCK_MAIN); +#endif + if (p) { + struct enif_environment_t env; + ASSERT(is_internal_pid(p->common.id)); + erts_pre_nif(&env, p, mod); + env.may_delay_enif_send = 1; + nif_result = (*fun->fptr)(&env, argc, argv); + if (env.exception_thrown) + nif_result = THE_NON_VALUE; + erts_post_nif(&env); + } else { + /* Nif call was done without a process context, + so we create a phony one. */ + struct enif_msg_environment_t msg_env; + setup_nif_env(&msg_env, mod); + msg_env.env.may_delay_enif_send = 1; + nif_result = (*fun->fptr)(&msg_env.env, argc, argv); + if (msg_env.env.exception_thrown) + nif_result = THE_NON_VALUE; + enif_clear_env(&msg_env.env); + } + + return nif_result; +} + #ifdef USE_VM_PROBES void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf) { diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 44a2581436..33785ecaca 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -96,7 +96,7 @@ typedef enum { struct enif_environment_t; typedef struct enif_environment_t ErlNifEnv; -typedef struct +typedef struct enif_func_t { const char* name; unsigned arity; -- cgit v1.2.3 From 37092dab15448ef6a078800e3ff0cc41880ea765 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 10 Dec 2015 11:10:46 +0100 Subject: erts: Implement tracer modules Add the possibility to use modules as trace data receivers. The functions in the module have to be nifs as otherwise complex trace probes will be very hard to handle (complex means trace probes for ports for example). This commit changes the way that the ptab->tracer field works from always being an immediate, to now be NIL if no tracer is present or else be the tuple {TracerModule, TracerState} where TracerModule is an atom that is later used to lookup the appropriate tracer callbacks to call and TracerState is just passed to the tracer callback. The default process and port tracers have been rewritten to use the new API. This commit also changes the order which trace messages are delivered to the potential tracer process. Any enif_send done in a tracer module may be delayed indefinitely because of lock order issues. If a message is delayed any other trace message send from that process is also delayed so that order is preserved for each traced entity. This means that for some trace events (i.e. send/receive) the events may come in an unintuitive order (receive before send) to the trace receiver. Timestamps are taken when the trace message is generated so trace messages from differented processes may arrive with the timestamp out of order. Both the erlang:trace and seq_trace:set_system_tracer accept the new tracer module tracers and also the backwards compatible arguments. OTP-10267 --- erts/doc/src/Makefile | 15 +- erts/doc/src/erl_nif.xml | 3 +- erts/doc/src/erl_tracer.xml | 324 +++ erts/doc/src/erlang.xml | 46 +- erts/doc/src/match_spec.xml | 11 +- erts/doc/src/ref_man.xml | 1 + erts/doc/src/specs.xml | 1 + erts/emulator/Makefile.in | 20 +- erts/emulator/beam/atom.names | 7 +- erts/emulator/beam/beam_bif_load.c | 7 +- erts/emulator/beam/beam_bp.c | 169 +- erts/emulator/beam/beam_bp.h | 16 +- erts/emulator/beam/beam_emu.c | 10 +- erts/emulator/beam/bif.c | 66 +- erts/emulator/beam/bif.tab | 5 +- erts/emulator/beam/code_ix.c | 1 + erts/emulator/beam/dist.c | 16 +- erts/emulator/beam/erl_alloc.c | 2 +- erts/emulator/beam/erl_alloc.types | 3 + erts/emulator/beam/erl_bif_ddll.c | 2 +- erts/emulator/beam/erl_bif_info.c | 5 +- erts/emulator/beam/erl_bif_port.c | 7 +- erts/emulator/beam/erl_bif_trace.c | 543 ++--- erts/emulator/beam/erl_db_util.c | 74 +- erts/emulator/beam/erl_gc.c | 6 +- erts/emulator/beam/erl_hl_timer.c | 6 +- erts/emulator/beam/erl_lock_check.c | 5 +- erts/emulator/beam/erl_message.c | 160 +- erts/emulator/beam/erl_message.h | 110 +- erts/emulator/beam/erl_msacc.c | 2 +- erts/emulator/beam/erl_nif.c | 252 +- erts/emulator/beam/erl_nif.h | 8 +- erts/emulator/beam/erl_process.c | 216 +- erts/emulator/beam/erl_process.h | 8 +- erts/emulator/beam/erl_process_lock.c | 191 +- erts/emulator/beam/erl_process_lock.h | 32 +- erts/emulator/beam/erl_ptab.h | 8 +- erts/emulator/beam/erl_time_sup.c | 2 +- erts/emulator/beam/erl_trace.c | 2540 +++++++------------- erts/emulator/beam/erl_trace.h | 78 +- erts/emulator/beam/erlang_dtrace.d | 29 + erts/emulator/beam/global.h | 26 +- erts/emulator/beam/io.c | 34 +- erts/emulator/beam/register.c | 8 +- erts/emulator/beam/sys.h | 9 + erts/emulator/beam/utils.c | 2 +- erts/emulator/hipe/hipe_debug.c | 2 +- erts/emulator/nifs/common/erl_tracer_nif.c | 250 ++ erts/emulator/test/Makefile | 2 + erts/emulator/test/match_spec_SUITE.erl | 98 +- erts/emulator/test/trace_SUITE.erl | 193 +- erts/emulator/test/tracer_SUITE.erl | 622 +++++ erts/emulator/test/tracer_SUITE_data/Makefile.src | 8 + erts/emulator/test/tracer_SUITE_data/tracer_test.c | 122 + erts/emulator/test/tracer_test.erl | 55 + erts/emulator/utils/make_driver_tab | 13 +- erts/preloaded/ebin/erl_tracer.beam | Bin 0 -> 1864 bytes erts/preloaded/src/Makefile | 3 +- erts/preloaded/src/erl_tracer.erl | 41 + erts/preloaded/src/erlang.erl | 67 +- erts/preloaded/src/erts_internal.erl | 29 +- erts/preloaded/src/init.erl | 4 + 62 files changed, 3918 insertions(+), 2677 deletions(-) create mode 100644 erts/doc/src/erl_tracer.xml create mode 100644 erts/emulator/nifs/common/erl_tracer_nif.c create mode 100644 erts/emulator/test/tracer_SUITE.erl create mode 100644 erts/emulator/test/tracer_SUITE_data/Makefile.src create mode 100644 erts/emulator/test/tracer_SUITE_data/tracer_test.c create mode 100644 erts/emulator/test/tracer_test.erl create mode 100644 erts/preloaded/ebin/erl_tracer.beam create mode 100644 erts/preloaded/src/erl_tracer.erl (limited to 'erts') diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile index e02e89238e..b96cbbce40 100644 --- a/erts/doc/src/Makefile +++ b/erts/doc/src/Makefile @@ -50,12 +50,14 @@ XML_REF1_FILES = epmd.xml \ XML_REF3_EFILES = \ erl_prim_loader.xml \ erlang.xml \ + erl_tracer.xml \ init.xml \ zlib.xml XML_REF3_FILES = \ driver_entry.xml \ erl_nif.xml \ + erl_tracer.xml \ erl_driver.xml \ erl_prim_loader.xml \ erlang.xml \ @@ -154,18 +156,9 @@ clean: rm -f $(SPECDIR)/* rm -f errs core *~ -$(SPECDIR)/specs_driver_entry.xml: +$(SPECDIR)/specs_%.xml: escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ - -o$(dir $@) -module driver_entry -$(SPECDIR)/specs_erl_nif.xml: - escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ - -o$(dir $@) -module erl_nif -$(SPECDIR)/specs_erl_driver.xml: - escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ - -o$(dir $@) -module erl_driver -$(SPECDIR)/specs_erts_alloc.xml: - escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ - -o$(dir $@) -module erts_alloc + -o$(dir $@) -module $(patsubst $(SPECDIR)/specs_%.xml,%,$@) # ---------------------------------------------------- # Release Target diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 5f56d8b595..7546f7ef81 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -566,8 +566,7 @@ typedef struct { typedef void ErlNifResourceDtor(ErlNifEnv* env, void* obj); -

The function prototype of a resource destructor function. - A destructor function is not allowed to call any term-making functions.

+

The function prototype of a resource destructor function.

ErlNifCharEncoding diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml new file mode 100644 index 0000000000..1e8e78b25f --- /dev/null +++ b/erts/doc/src/erl_tracer.xml @@ -0,0 +1,324 @@ + + + + +
+ + 20162016 + Ericsson AB. All Rights Reserved. + + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + erl_tracer + + + + +
+ erl_tracer + Erlang Tracer Behaviour + +

A behaviour module for implementing the back end of the erlang + tracing system. The functions in this module will be called whenever + a trace probe is triggered. Both the enabled and trace + functions are called in the context of the entity that triggered the + trace probe. + This means that the overhead by having the tracing enabled will be + greatly effected by how much time is spent in these functions. So do as + little work as possible in these functions.

+ +

All functions in this behaviour have to be implemented as NIF's. + This is a limitation that may the lifted in the future. + There is an example tracer module nif + implementation at the end of this page.

+
+ +

Do not send messages or issue port commands to the Tracee + in any of the callbacks. Doing so is not allowed and can cause all + sorts of strange behaviour, including but not limited to infinite + recursions.

+
+
+ + + + + +

The different trace tags that the tracer will be called with. + Each trace tag is described in greater detail in + Module:trace/6 +

+
+
+ + + +

The process or port that the trace belongs to. +

+
+
+ + + +

The options for the tracee. + + timestamp + If not set to undefined, the tracer has been requested to + include a timestamp. + match_spec_result + If not set to true, the tracer has been requested to + include the output of a match specification that was run. + scheduler_id + Set to a number of the scheduler id is to be included by the tracer. + Otherwise it is set to undefined. + +

+
+
+ + + +

+ The state which is given when calling + erlang:trace(PidPortSpec,true,[{tracer,Module,TracerState}]). + The tracer state is an immutable value that is passed to erl_tracer callbacks and should + contain all the data that is needed to generate the trace event. +

+
+
+
+ +
+ CALLBACK FUNCTIONS +

The following functions + should be exported from a erl_tracer callback module.

+
+ + + + Module:enabled(TraceTag, TracerState, Tracee) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag() | trace_status + TracerState = term() + Tracee = tracee() + Result = trace | discard | remove + + +

This callback will be called whenever a trace point is triggered. It + allows the tracer to decide whether a trace should be generated or not. + This check is made as early as possible in order to limit the amount of + overhead associated with tracing. If trace is returned the + necessary trace data will be created and the trace call-back of the tracer + will be called. If discard is returned, this trace call + will be discarded and no call to trace will be done. If + remove is returned, the VM will attempt to remove this tracer + from the tracee, together with any trace flags set on the tracee. +

+

trace_status is a special type of TraceTag which is used + to check if the tracer should still be active. It is called in multiple + scenarios, but most significantly it is used when tracing is started + using this tracer.

+

This function may be called multiple times per trace point, so it + is important that it is both fast and side effect free.

+
+
+ + + Module:trace(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag() + TracerState = term() + Tracee = tracee() + FirstTraceTerm = term() + SecondTraceTerm = term() | undefined + Opts = trace_opts() + Result = ok + + +

This callback will be called when a trace point is triggered and + the Module:enabled/3 + callback returned trace. In it any side effects needed by + the tracer should be done. The trace point payload is located in + the FirstTraceTerm and SecondTraceTerm. The content + of the TraceTerms depends on which TraceTag has been triggered. + The FirstTraceTerm and SecondTraceTerm correspond to the + fourth and fifth slot in the trace tuples described in + erlang:trace/3. + If the tuple only has four elements, SecondTraceTerm will be + undefined.

+
+
+ + Module:trace(seq_trace, TracerState, Label, SeqTraceInfo, undefined, Opts) -> Result + Check if a sequence trace event should be generated. + + TracerState = term() + Label = term() + SeqTraceInfo = term() + Opts = trace_opts() + Result = ok + + +

The TraceTag seq_trace is handled a little bit + differently. There is not Tracee for seq_trace, instead the + Label associated with the seq_trace event is given. + For more info on what Label and SeqTraceInfo can be + see the seq_trace manual.

+
+
+
+
+ + Erl Tracer Module example +

In the example below a tracer module with a nif backend sends a message + for each send trace tag containing only the sender and receiver. + Using this tracer module, a much more lightweight message tracer is + used that only records who sent messages to who.

+

Here is an example session using it on Linux.

+
+$ gcc -I erts-8.0/include/ -fPIC -shared -o erl_msg_tracer.so erl_msg_tracer.c
+$ erl
+Erlang/OTP 19 [DEVELOPMENT] [erts-8.0] [source-ed2b56b] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]
+
+Eshell V8.0  (abort with ^G)
+1> c(erl_msg_tracer), erl_msg_tracer:load().
+ok
+2> Tracer = spawn(fun F() -> receive M -> io:format("~p~n",[M]), F() end end).
+<0.37.0>
+3> erlang:trace(new, true, [send,{tracer, erl_msg_tracer, Tracer}]).
+0
+{<0.39.0>,<0.27.0>}
+4> {ok, D} = file:open("/tmp/tmp.data",[write]).
+{trace,#Port<0.486>,<0.40.0>}
+{trace,<0.40.0>,<0.21.0>}
+{trace,#Port<0.487>,<0.4.0>}
+{trace,#Port<0.488>,<0.4.0>}
+{trace,#Port<0.489>,<0.4.0>}
+{trace,#Port<0.490>,<0.4.0>}
+{ok,<0.40.0>}
+{trace,<0.41.0>,<0.27.0>}
+5>
+    
+

erl_msg_tracer.erl

+
+-module(erl_msg_tracer).
+
+-export([enabled/3, trace/6, load/0]).
+
+load() ->
+    erlang:load_nif("erl_msg_tracer", []).
+
+enabled(_, _, _) ->
+    error.
+
+trace(_, _, _,_, _, _) ->
+    error.
+    
+

erl_msg_tracer.c

+
+#include "erl_nif.h"
+
+/* NIF interface declarations */
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
+static void unload(ErlNifEnv* env, void* priv_data);
+
+/* The NIFs: */
+static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
+static ErlNifFunc nif_funcs[] = {
+    {"enabled", 3, enabled},
+    {"trace", 6, trace}
+};
+
+ERL_NIF_INIT(erl_msg_tracer, nif_funcs, load, NULL, upgrade, unload)
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+    *priv_data = NULL;
+    return 0;
+}
+
+static void unload(ErlNifEnv* env, void* priv_data)
+{
+
+}
+
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
+		   ERL_NIF_TERM load_info)
+{
+    if (*old_priv_data != NULL || *priv_data != NULL) {
+	return -1; /* Don't know how to do that */
+    }
+    if (load(env, priv_data, load_info)) {
+	return -1;
+    }
+    return 0;
+}
+
+/*
+ * argv[0]: Trace Tag
+ * argv[1]: TracerState
+ * argv[2]: Tracee
+ */
+static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+    ErlNifPid to_pid;
+    if (enif_get_local_pid(env, argv[1], &to_pid))
+        if (!enif_is_process_alive(env, &to_pid))
+            /* tracer is dead so we should remove this trace point */
+            return enif_make_atom(env, "remove");
+
+    /* Only generate trace for when tracer != tracee */
+    if (enif_is_identical(argv[1], argv[2]))
+        return enif_make_atom(env, "discard");
+
+    /* Only trigger trace messages on 'send' */
+    if (enif_is_identical(enif_make_atom(env, "send"), argv[0]))
+        return enif_make_atom(env, "trace");
+
+    /* Have to answer trace_status */
+    if (enif_is_identical(enif_make_atom(env, "trace_status"), argv[0]))
+        return enif_make_atom(env, "trace");
+
+    return enif_make_atom(env, "discard");
+}
+
+/*
+ * argv[0]: Trace Tag, should only be 'send'
+ * argv[1]: TracerState, process to send {argv[2], argv[4]} to
+ * argv[2]: Tracee
+ * argv[3]: Message, ignored
+ * argv[4]: Recipient
+ * argv[5]: Options, ignored
+ */
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+    ErlNifPid to_pid;
+
+    if (enif_get_local_pid(env, argv[1], &to_pid)) {
+        ERL_NIF_TERM msg = enif_make_tuple3(env, enif_make_atom(env, "trace"), argv[2], argv[4]);
+        enif_send(env, &to_pid, NULL, msg);
+    }
+
+    return enif_make_atom(env, "ok");
+}
+    
+
+
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index ef577c82bf..86bdb1dfe6 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -8371,7 +8371,7 @@ timestamp() -> all -

Sets all trace flags except {tracer, Tracer} and +

Sets all trace flags except tracer and cpu_timestamp, which are in their nature different than the others.

@@ -8529,12 +8529,20 @@ timestamp() ->

Specifies where to send the trace messages. Tracer must be the process identifier of a local process - or the port identifier - of a local port. If this flag is not given, trace - messages are sent to the process that called - erlang:trace/3.

+ or the port identifier of a local port.

+
+ {tracer, TracerModule, TracerState} + +

Specifies that a tracer module should be called + instead of sending a trace message. The tracer module + can then ignore or change the trace message. For more details + on how to write a tracer module see + erl_tracer +

+

If no tracer is given, the calling process + will be receiving all of the trace messages

The effect of combining set_on_first_link with set_on_link is the same as having set_on_first_link alone. Likewise for @@ -8706,9 +8714,9 @@ timestamp() -> garbage collection.

-

If the tracing process dies, the flags are silently - removed.

-

Only one process can trace a particular process. Therefore, +

If the tracing process/port dies or the tracer module returns + remove, the flags are silently removed.

+

Each process can only be traced by one tracer. Therefore, attempts to trace an already traced process fail.

Returns: A number indicating the number of processes that matched PidSpec. @@ -8716,7 +8724,7 @@ timestamp() -> identifier, the return value is 1. If PidSpec is all or existing, the return value is - the number of processes running, excluding tracer processes. + the number of processes running. If PidSpec is new, the return value is 0.

Failure: badarg if the specified arguments are @@ -8750,7 +8758,7 @@ timestamp() -> has not been traced by someone, but if this is the case, no trace messages have been delivered when the trace_delivered message arrives.

-

Notice that that Tracee must refer +

Notice that Tracee must refer to a process currently, or previously existing on the same node as the caller of erlang:trace_delivered(Tracee) resides on. @@ -8801,7 +8809,8 @@ timestamp() -> tracer -

Returns the identifier for process or port tracing this +

Returns the identifier for process, port or a tuple containing + the tracer module and tracer state tracing this process. If this process is not being traced, the return value is [].

@@ -8830,8 +8839,8 @@ timestamp() -> meta -

Returns the meta-trace tracer process or port for this - function, if it has one. If the function is not +

Returns the meta-trace tracer process, port or trace module + for this function, if it has one. If the function is not meta-traced, the returned value is false. If the function is meta-traced but has once detected that the tracer process is invalid, the returned value is [].

@@ -8999,13 +9008,12 @@ timestamp() -> the process, a return_to message is also sent when this function returns to its caller.

- meta | {meta, Pid} + meta | {meta, Pid} | {meta, TracerModule, TracerState} +

Turns on or off meta-tracing for all types of function - calls. Trace messages are sent to the tracer process - or port Pid whenever any of the specified - functions are called, regardless of how they are called. - If no Pid is specified, + calls. Trace messages are sent to the tracer whenever any of + the specified functions are called. If no tracer is specified, self() is used as a default tracer process.

Meta-tracing traces all processes and does not care about the process trace flags set by trace/3, @@ -9013,7 +9021,7 @@ timestamp() -> [call, timestamp].

The match specification function {return_trace} works with meta-trace and sends its trace message to the - same tracer process.

+ same tracer.

call_count diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml index 975f01cf2c..b49e1483aa 100644 --- a/erts/doc/src/match_spec.xml +++ b/erts/doc/src/match_spec.xml @@ -287,7 +287,7 @@ can not be one of the atoms , or (unless, of course, they are registered names). can not be nor - . + . Returns and may only be used in the part when tracing.

@@ -298,7 +298,7 @@ be either a process identifier or a registered name and is given as the first argument to the match_spec function. can not be nor - . Returns + . Returns and may only be used in the part when tracing.

@@ -308,11 +308,14 @@ disable list is applied first, but effectively all changes are applied atomically. The trace flags are the same as for not including - but including . If a + but including . If a tracer is specified in both lists, the tracer in the enable list takes precedence. If no tracer is specified the same tracer as the process executing the match spec is - used. With three parameters to this function the first is + used. When using a tracer module + the module has to be loaded before the match specification is executed. + If it is not loaded the match will fail. + With three parameters to this function the first is either a process identifier or the registered name of a process to set trace flags on, the second is the disable list, and the third is the enable list. Returns diff --git a/erts/doc/src/ref_man.xml b/erts/doc/src/ref_man.xml index aa245ec08a..e45402a397 100644 --- a/erts/doc/src/ref_man.xml +++ b/erts/doc/src/ref_man.xml @@ -56,5 +56,6 @@ + diff --git a/erts/doc/src/specs.xml b/erts/doc/src/specs.xml index 41a3984659..ed6be650e5 100644 --- a/erts/doc/src/specs.xml +++ b/erts/doc/src/specs.xml @@ -2,6 +2,7 @@ + diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 26a737619e..fb486c917f 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -577,7 +577,7 @@ GENERATE += $(TARGET)/erl_version.h # driver table $(TTF_DIR)/driver_tab.c: Makefile.in utils/make_driver_tab - $(gen_verbose)LANG=C $(PERL) utils/make_driver_tab -o $@ -nifs $(STATIC_NIF_LIBS) -drivers $(DRV_OBJS) $(STATIC_DRIVER_LIBS) + $(gen_verbose)LANG=C $(PERL) utils/make_driver_tab -o $@ -nifs $(NIF_OBJS) $(STATIC_NIF_LIBS) -drivers $(DRV_OBJS) $(STATIC_DRIVER_LIBS) GENERATE += $(TTF_DIR)/driver_tab.c @@ -600,8 +600,8 @@ $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_zip.beam \ $(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \ $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam - + $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \ + $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam $(gen_verbose)LANG=C $(PERL) utils/make_preload $(MAKE_PRELOAD_EXTRA) -rc $^ > $@ else PRELOAD_OBJ = $(OBJDIR)/preload.o @@ -616,7 +616,8 @@ $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_zip.beam \ $(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \ $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam + $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \ + $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam $(gen_verbose)LANG=C $(PERL) utils/make_preload -old $^ > $@ endif @@ -690,6 +691,9 @@ $(OBJDIR)/%.o: drivers/common/%.c $(OBJDIR)/%.o: drivers/$(ERLANG_OSTYPE)/%.c $(V_CC) $(CFLAGS) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLANG_OSTYPE) -I../etc/$(ERLANG_OSTYPE) -c $< -o $@ +$(OBJDIR)/%.o: nifs/common/%.c + $(V_CC) $(CFLAGS) -DLIBSCTP=$(LIBSCTP) $(INCLUDES) -Inifs/common -Inifs/$(ERLANG_OSTYPE) -c $< -o $@ + # ---------------------------------------------------------------------- # Specials # @@ -780,6 +784,7 @@ RUN_OBJS = \ $(OBJDIR)/erl_msacc.o LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o +NIF_OBJS = $(OBJDIR)/erl_tracer_nif.o ifeq ($(TARGET),win32) DRV_OBJS = \ @@ -889,7 +894,7 @@ endif BASE_OBJS = $(EMU_OBJS) $(RUN_OBJS) $(OS_OBJS) $(EXTRA_BASE_OBJS) $(LTTNG_OBJS) -before_DTrace_OBJS = $(BASE_OBJS) $(DRV_OBJS) +before_DTrace_OBJS = $(BASE_OBJS) $(DRV_OBJS) $(NIF_OBJS) DTRACE_OBJS = ifdef DTRACE_ENABLED_2STEP @@ -1042,6 +1047,7 @@ endif BEAM_SRC=$(wildcard beam/*.c) DRV_COMMON_SRC=$(wildcard drivers/common/*.c) DRV_OSTYPE_SRC=$(wildcard drivers/$(ERLANG_OSTYPE)/*.c) +NIF_COMMON_SRC=$(wildcard nifs/common/*.c) ALL_SYS_SRC=$(wildcard sys/$(ERLANG_OSTYPE)/*.c) $(wildcard sys/common/*.c) # We use $(shell ls) here instead of wildcard as $(wildcard ) resolved at # loadtime of the makefile and at that time these files are not generated yet. @@ -1074,7 +1080,7 @@ MG_FLAG=-MG endif DEP_CC=$(CC) -DEP_FLAGS=-MM $(MG_FLAG) $(CFLAGS) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLANG_OSTYPE) +DEP_FLAGS=-MM $(MG_FLAG) $(CFLAGS) $(INCLUDES) -Inifs/common -Idrivers/common -Idrivers/$(ERLANG_OSTYPE) SYS_SRC=$(ALL_SYS_SRC) endif @@ -1099,6 +1105,8 @@ $(TTF_DIR)/depend.mk: $(TTF_DIR)/GENERATED $(PRELOAD_SRC) | $(SED_DEPEND) >> $(TTF_DIR)/depend.mk $(V_at)$(DEP_CC) $(DEP_FLAGS) -I../etc/$(ERLANG_OSTYPE) $(DRV_OSTYPE_SRC) \ | $(SED_DEPEND) >> $(TTF_DIR)/depend.mk + $(V_at)$(DEP_CC) $(DEP_FLAGS) $(NIF_COMMON_SRC) \ + | $(SED_DEPEND) >> $(TTF_DIR)/depend.mk $(V_at)$(DEP_CC) $(DEP_FLAGS) $(SYS_SRC) \ | $(SED_DEPEND) >> $(TTF_DIR)/depend.mk $(V_at)$(DEP_CC) $(DEP_FLAGS) $(TARGET_SRC) \ diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index d466f00028..aeea6cc989 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -197,6 +197,7 @@ atom dirty_cpu_schedulers_online atom dirty_io atom disable_trace atom disabled +atom discard atom display_items atom dist atom dist_cmd @@ -231,6 +232,7 @@ atom exception_trace atom extended atom Eq='=:=' atom Eqeq='==' +atom erl_tracer atom erlang atom ERROR='ERROR' atom error_handler @@ -353,6 +355,7 @@ atom match atom match_limit atom match_limit_recursion atom match_spec +atom match_spec_result atom max atom maximum atom max_tables max_processes @@ -569,6 +572,7 @@ atom static atom stderr_to_stdout atom stop atom stream +atom strict_monotonic atom strict_monotonic_timestamp atom sunrm atom suspend @@ -596,8 +600,9 @@ atom total_active_tasks atom total_heap_size atom total_run_queue_lengths atom tpkt -atom trace trace_ts traced +atom trace trace_ts traced atom trace_control_word +atom trace_status atom tracer atom trap_exit atom trim diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 320dee6668..b10250dc49 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -719,13 +719,13 @@ set_default_trace_pattern(Eterm module) Binary *match_spec; Binary *meta_match_spec; struct trace_pattern_flags trace_pattern_flags; - Eterm meta_tracer_pid; + ErtsTracer meta_tracer; erts_get_default_trace_pattern(&trace_pattern_is_on, &match_spec, &meta_match_spec, &trace_pattern_flags, - &meta_tracer_pid); + &meta_tracer); if (trace_pattern_is_on) { Eterm mfa[1]; mfa[0] = module; @@ -733,7 +733,7 @@ set_default_trace_pattern(Eterm module) match_spec, meta_match_spec, 1, trace_pattern_flags, - meta_tracer_pid, 1); + meta_tracer, 1); } } @@ -1264,6 +1264,7 @@ delete_code(Module* modp) modp->curr.code_length = 0; modp->curr.catches = BEAM_CATCHES_NIL; modp->curr.nif = NULL; + } diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 0a13454951..2ee98ed7b5 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -92,15 +92,15 @@ get_mtime(Process *c_p) /* ** Helpers */ -static Eterm do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg, - int local, Binary* ms, Eterm tracer_pid); +static ErtsTracer do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg, + int local, Binary* ms, ErtsTracer tracer); static void set_break(BpFunctions* f, Binary *match_spec, Uint break_flags, - enum erts_break_op count_op, Eterm tracer_pid); + enum erts_break_op count_op, ErtsTracer tracer); static void set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags, enum erts_break_op count_op, - Eterm tracer_pid); + ErtsTracer tracer); static void clear_break(BpFunctions* f, Uint break_flags); static int clear_function_break(BeamInstr *pc, Uint break_flags); @@ -108,7 +108,7 @@ static int clear_function_break(BeamInstr *pc, Uint break_flags); static BpDataTime* get_time_break(BeamInstr *pc); static GenericBpData* check_break(BeamInstr *pc, Uint break_flags); -static void bp_meta_unref(BpMetaPid* bmp); +static void bp_meta_unref(BpMetaTracer* bmt); static void bp_count_unref(BpCount* bcp); static void bp_time_unref(BpDataTime* bdt); static void consolidate_bp_data(Module* modp, BeamInstr* pc, int local); @@ -302,7 +302,7 @@ consolidate_bp_data(Module* modp, BeamInstr* pc, int local) MatchSetUnref(dst->local_ms); } if (flags & ERTS_BPF_META_TRACE) { - bp_meta_unref(dst->meta_pid); + bp_meta_unref(dst->meta_tracer); MatchSetUnref(dst->meta_ms); } if (flags & ERTS_BPF_COUNT) { @@ -343,8 +343,8 @@ consolidate_bp_data(Module* modp, BeamInstr* pc, int local) MatchSetRef(dst->local_ms); } if (flags & ERTS_BPF_META_TRACE) { - dst->meta_pid = src->meta_pid; - erts_refc_inc(&dst->meta_pid->refc, 1); + dst->meta_tracer = src->meta_tracer; + erts_refc_inc(&dst->meta_tracer->refc, 1); dst->meta_ms = src->meta_ms; MatchSetRef(dst->meta_ms); } @@ -436,13 +436,13 @@ uninstall_breakpoint(BeamInstr* pc) void erts_set_trace_break(BpFunctions* f, Binary *match_spec) { - set_break(f, match_spec, ERTS_BPF_LOCAL_TRACE, 0, am_true); + set_break(f, match_spec, ERTS_BPF_LOCAL_TRACE, 0, erts_tracer_true); } void -erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, Eterm tracer_pid) +erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, ErtsTracer tracer) { - set_break(f, match_spec, ERTS_BPF_META_TRACE, 0, tracer_pid); + set_break(f, match_spec, ERTS_BPF_META_TRACE, 0, tracer); } void @@ -450,13 +450,13 @@ erts_set_call_trace_bif(BeamInstr *pc, Binary *match_spec, int local) { Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE; - set_function_break(pc, match_spec, flags, 0, NIL); + set_function_break(pc, match_spec, flags, 0, erts_tracer_nil); } void -erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, Eterm tracer_pid) +erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, ErtsTracer tracer) { - set_function_break(pc, match_spec, ERTS_BPF_META_TRACE, 0, tracer_pid); + set_function_break(pc, match_spec, ERTS_BPF_META_TRACE, 0, tracer); } void @@ -464,7 +464,7 @@ erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op count_op) { set_function_break(pc, NULL, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE, - count_op, NIL); + count_op, erts_tracer_nil); } void @@ -474,21 +474,21 @@ erts_clear_time_trace_bif(BeamInstr *pc) { void erts_set_debug_break(BpFunctions* f) { - set_break(f, NULL, ERTS_BPF_DEBUG, 0, NIL); + set_break(f, NULL, ERTS_BPF_DEBUG, 0, erts_tracer_nil); } void erts_set_count_break(BpFunctions* f, enum erts_break_op count_op) { set_break(f, 0, ERTS_BPF_COUNT|ERTS_BPF_COUNT_ACTIVE, - count_op, NIL); + count_op, erts_tracer_nil); } void erts_set_time_break(BpFunctions* f, enum erts_break_op count_op) { set_break(f, 0, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE, - count_op, NIL); + count_op, erts_tracer_nil); } void @@ -625,19 +625,26 @@ erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg) if (bp_flags & ERTS_BPF_LOCAL_TRACE) { ASSERT((bp_flags & ERTS_BPF_GLOBAL_TRACE) == 0); - (void) do_call_trace(c_p, I, reg, 1, bp->local_ms, am_true); + (void) do_call_trace(c_p, I, reg, 1, bp->local_ms, erts_tracer_true); } else if (bp_flags & ERTS_BPF_GLOBAL_TRACE) { - (void) do_call_trace(c_p, I, reg, 0, bp->local_ms, am_true); + (void) do_call_trace(c_p, I, reg, 0, bp->local_ms, erts_tracer_true); } if (bp_flags & ERTS_BPF_META_TRACE) { - Eterm old_pid; - Eterm new_pid; - - old_pid = (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid); - new_pid = do_call_trace(c_p, I, reg, 1, bp->meta_ms, old_pid); - if (new_pid != old_pid) { - erts_smp_atomic_set_nob(&bp->meta_pid->pid, new_pid); + ErtsTracer old_tracer, new_tracer; + + old_tracer = erts_smp_atomic_read_nob(&bp->meta_tracer->tracer); + + new_tracer = do_call_trace(c_p, I, reg, 1, bp->meta_ms, old_tracer); + if (!ERTS_TRACER_COMPARE(new_tracer, old_tracer)) { + if (old_tracer == erts_smp_atomic_cmpxchg_acqb( + &bp->meta_tracer->tracer, + (erts_aint_t)new_tracer, + (erts_aint_t)old_tracer)) { + ERTS_TRACER_CLEAR(&old_tracer); + } else { + ERTS_TRACER_CLEAR(&new_tracer); + } } } @@ -645,7 +652,8 @@ erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg) erts_smp_atomic_inc_nob(&bp->count->acount); } - if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE && erts_is_tracer_proc_valid(c_p)) { + if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE + && ERTS_TRACER_PROC_IS_ENABLED(c_p)) { Eterm w; erts_trace_time_call(c_p, I, bp->time); w = (BeamInstr) *c_p->cp; @@ -690,7 +698,7 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) Eterm (*func)(Process*, Eterm*, BeamInstr*); Export* ep = bif_export[bif_index]; Uint32 flags = 0, flags_meta = 0; - Eterm meta_tracer_pid = NIL; + ErtsTracer meta_tracer = erts_tracer_nil; int applying = (I == &(ep->code[3])); /* Yup, the apply code for a bif * is actually in the * export entry */ @@ -718,23 +726,32 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) IS_TRACED_FL(p, F_TRACE_CALLS)) { int local = !!(bp_flags & ERTS_BPF_LOCAL_TRACE); flags = erts_call_trace(p, ep->code, bp->local_ms, args, - local, &ERTS_TRACER_PROC(p)); + local, &ERTS_TRACER(p)); } if (bp_flags & ERTS_BPF_META_TRACE) { - Eterm tpid1, tpid2; + ErtsTracer old_tracer; - tpid1 = tpid2 = - (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid); + meta_tracer = erts_smp_atomic_read_nob(&bp->meta_tracer->tracer); + old_tracer = meta_tracer; flags_meta = erts_call_trace(p, ep->code, bp->meta_ms, args, - 0, &tpid2); - meta_tracer_pid = tpid2; - if (tpid1 != tpid2) { - erts_smp_atomic_set_nob(&bp->meta_pid->pid, tpid2); + 0, &meta_tracer); + + if (!ERTS_TRACER_COMPARE(old_tracer, meta_tracer)) { + ErtsTracer new_tracer = erts_tracer_nil; + erts_tracer_update(&new_tracer, meta_tracer); + if (old_tracer == erts_smp_atomic_cmpxchg_acqb( + &bp->meta_tracer->tracer, + (erts_aint_t)new_tracer, + (erts_aint_t)old_tracer)) { + ERTS_TRACER_CLEAR(&old_tracer); + } else { + ERTS_TRACER_CLEAR(&new_tracer); + } } } if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE && IS_TRACED_FL(p, F_TRACE_CALLS) && - erts_is_tracer_proc_valid(p)) { + ERTS_TRACER_PROC_IS_ENABLED(p)) { BeamInstr *pc = (BeamInstr *)ep->code+3; erts_trace_time_call(p, pc, bp->time); } @@ -778,8 +795,6 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) if (reason != TRAP) { Eterm class; Eterm value = p->fvalue; - DeclareTmpHeapNoproc(nocatch,3); - UseTmpHeapNoproc(3); /* Expand error value like in handle_error() */ if (reason & EXF_ARGLIST) { Eterm *tp; @@ -788,7 +803,8 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) value = tp[1]; } if ((reason & EXF_THROWN) && (p->catches <= 0)) { - value = TUPLE2(nocatch, am_nocatch, value); + Eterm *hp = HAlloc(p, 3); + value = TUPLE2(hp, am_nocatch, value); reason = EXC_ERROR; } /* Note: expand_error_value() could theoretically @@ -801,11 +817,11 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) if (flags_meta & MATCH_SET_EXCEPTION_TRACE) { erts_trace_exception(p, ep->code, class, value, - &meta_tracer_pid); + &meta_tracer); } if (flags & MATCH_SET_EXCEPTION_TRACE) { erts_trace_exception(p, ep->code, class, value, - &ERTS_TRACER_PROC(p)); + &ERTS_TRACER(p)); } if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) { /* can only happen if(local)*/ @@ -827,7 +843,6 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) } } } - UnUseTmpHeapNoproc(3); if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) { erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); ERTS_TRACE_FLAGS(p) |= F_EXCEPTION_TRACE; @@ -836,11 +851,11 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) } } else { if (flags_meta & MATCH_SET_RX_TRACE) { - erts_trace_return(p, ep->code, result, &meta_tracer_pid); + erts_trace_return(p, ep->code, result, &meta_tracer); } /* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */ if (flags & MATCH_SET_RX_TRACE) { - erts_trace_return(p, ep->code, result, &ERTS_TRACER_PROC(p)); + erts_trace_return(p, ep->code, result, &ERTS_TRACER(p)); } if (flags & MATCH_SET_RETURN_TO_TRACE) { /* can only happen if(local)*/ @@ -857,9 +872,9 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) return result; } -static Eterm +static ErtsTracer do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg, - int local, Binary* ms, Eterm tracer_pid) + int local, Binary* ms, ErtsTracer tracer) { Eterm* cpp; int return_to_trace = 0; @@ -899,7 +914,7 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg, ASSERT(is_CP(*cpp)); } ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); - flags = erts_call_trace(c_p, I-3, ms, reg, local, &tracer_pid); + flags = erts_call_trace(c_p, I-3, ms, reg, local, &tracer); ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); if (cpp) { c_p->cp = cp_save; @@ -910,7 +925,7 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg, need += 1; } if (flags & MATCH_SET_RX_TRACE) { - need += 3; + need += 3 + size_object(tracer); } if (need) { ASSERT(c_p->htop <= E && E <= c_p->hend); @@ -926,14 +941,15 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg, E[0] = make_cp(c_p->cp); c_p->cp = beam_return_to_trace; } - if (flags & MATCH_SET_RX_TRACE) { + if (flags & MATCH_SET_RX_TRACE) + { E -= 3; + c_p->stop = E; ASSERT(c_p->htop <= E && E <= c_p->hend); ASSERT(is_CP((Eterm) (UWord) (I - 3))); - ASSERT(am_true == tracer_pid || - is_internal_pid(tracer_pid) || is_internal_port(tracer_pid)); + ASSERT(IS_TRACER_VALID(tracer)); E[2] = make_cp(c_p->cp); - E[1] = tracer_pid; + E[1] = copy_object(tracer, c_p); E[0] = make_cp(I - 3); /* We ARE at the beginning of an instruction, the funcinfo is above i. */ @@ -942,9 +958,9 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg, erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); ERTS_TRACE_FLAGS(c_p) |= F_EXCEPTION_TRACE; erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); - } - c_p->stop = E; - return tracer_pid; + } else + c_p->stop = E; + return tracer; } void @@ -1098,9 +1114,9 @@ erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, int local) return 0; } -int +int erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret, - Eterm *tracer_pid_ret) + ErtsTracer *tracer_ret) { GenericBpData* bp = check_break(pc, ERTS_BPF_META_TRACE); @@ -1108,9 +1124,8 @@ erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret, if (match_spec_ret) { *match_spec_ret = bp->meta_ms; } - if (tracer_pid_ret) { - *tracer_pid_ret = - (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid); + if (tracer_ret) { + *tracer_ret = erts_smp_atomic_read_nob(&bp->meta_tracer->tracer); } return 1; } @@ -1391,7 +1406,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) { static void set_break(BpFunctions* f, Binary *match_spec, Uint break_flags, - enum erts_break_op count_op, Eterm tracer_pid) + enum erts_break_op count_op, ErtsTracer tracer) { Uint i; Uint n; @@ -1400,13 +1415,13 @@ set_break(BpFunctions* f, Binary *match_spec, Uint break_flags, for (i = 0; i < n; i++) { BeamInstr* pc = f->matching[i].pc; set_function_break(pc, match_spec, break_flags, - count_op, tracer_pid); + count_op, tracer); } } static void set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags, - enum erts_break_op count_op, Eterm tracer_pid) + enum erts_break_op count_op, ErtsTracer tracer) { GenericBp* g; GenericBpData* bp; @@ -1439,7 +1454,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags, MatchSetUnref(bp->local_ms); } else if (common & ERTS_BPF_META_TRACE) { MatchSetUnref(bp->meta_ms); - bp_meta_unref(bp->meta_pid); + bp_meta_unref(bp->meta_tracer); } else if (common & ERTS_BPF_COUNT) { if (count_op == erts_break_stop) { bp->flags &= ~ERTS_BPF_COUNT_ACTIVE; @@ -1474,13 +1489,15 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags, MatchSetRef(match_spec); bp->local_ms = match_spec; } else if (break_flags & ERTS_BPF_META_TRACE) { - BpMetaPid* bmp; + BpMetaTracer* bmt; + ErtsTracer meta_tracer = erts_tracer_nil; MatchSetRef(match_spec); bp->meta_ms = match_spec; - bmp = Alloc(sizeof(BpMetaPid)); - erts_refc_init(&bmp->refc, 1); - erts_smp_atomic_init_nob(&bmp->pid, tracer_pid); - bp->meta_pid = bmp; + bmt = Alloc(sizeof(BpMetaTracer)); + erts_refc_init(&bmt->refc, 1); + erts_tracer_update(&meta_tracer, tracer); /* copy tracer */ + erts_smp_atomic_init_nob(&bmt->tracer, (erts_aint_t)meta_tracer); + bp->meta_tracer = bmt; } else if (break_flags & ERTS_BPF_COUNT) { BpCount* bcp; @@ -1544,7 +1561,7 @@ clear_function_break(BeamInstr *pc, Uint break_flags) } if (common & ERTS_BPF_META_TRACE) { MatchSetUnref(bp->meta_ms); - bp_meta_unref(bp->meta_pid); + bp_meta_unref(bp->meta_tracer); } if (common & ERTS_BPF_COUNT) { ASSERT((bp->flags & ERTS_BPF_COUNT_ACTIVE) == 0); @@ -1560,10 +1577,12 @@ clear_function_break(BeamInstr *pc, Uint break_flags) } static void -bp_meta_unref(BpMetaPid* bmp) +bp_meta_unref(BpMetaTracer* bmt) { - if (erts_refc_dectest(&bmp->refc, 0) <= 0) { - Free(bmp); + if (erts_refc_dectest(&bmt->refc, 0) <= 0) { + ErtsTracer trc = erts_smp_atomic_read_nob(&bmt->tracer); + ERTS_TRACER_CLEAR(&trc); + Free(bmt); } } diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index f9eca94e2a..bb171be8a6 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -55,15 +55,15 @@ typedef struct { } BpCount; typedef struct { - erts_smp_atomic_t pid; + erts_smp_atomic_t tracer; erts_refc_t refc; -} BpMetaPid; +} BpMetaTracer; typedef struct generic_bp_data { Uint flags; Binary* local_ms; /* Match spec for local call trace */ Binary* meta_ms; /* Match spec for meta trace */ - BpMetaPid* meta_pid; /* Meta trace pid */ + BpMetaTracer* meta_tracer; /* Meta tracer */ BpCount* count; /* For call count */ BpDataTime* time; /* For time trace */ } GenericBpData; @@ -132,10 +132,10 @@ void erts_set_call_trace_bif(BeamInstr *pc, Binary *match_spec, int local); void erts_clear_call_trace_bif(BeamInstr *pc, int local); void erts_set_mtrace_break(BpFunctions *f, Binary *match_spec, - Eterm tracer_pid); + ErtsTracer tracer); void erts_clear_mtrace_break(BpFunctions *f); void erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, - Eterm tracer_pid); + ErtsTracer tracer); void erts_clear_mtrace_bif(BeamInstr *pc); void erts_set_debug_break(BpFunctions *f); @@ -150,13 +150,13 @@ void erts_clear_export_break(Module *modp, BeamInstr* pc); BeamInstr erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg); BeamInstr erts_trace_break(Process *p, BeamInstr *pc, Eterm *args, - Uint32 *ret_flags, Eterm *tracer_pid); + Uint32 *ret_flags, ErtsTracer *tracer); int erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, int local); int erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret, - Eterm *tracer_pid_rte); + ErtsTracer *tracer_ret); int erts_is_mtrace_bif(BeamInstr *pc, Binary **match_spec_ret, - Eterm *tracer_pid_ret); + ErtsTracer *tracer_ret); int erts_is_native_break(BeamInstr *pc); int erts_is_count_break(BeamInstr *pc, Uint *count_ret); int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *call_time); diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 901419c989..c356863b60 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4588,7 +4588,7 @@ do { \ SWAPOUT; /* Needed for shared heap */ ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); - erts_trace_return(c_p, code, r(0), E+1/*Process tracer*/); + erts_trace_return(c_p, code, r(0), ERTS_TRACER_FROM_ETERM(E+1)/* tracer */); ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); SWAPIN; c_p->cp = NULL; @@ -5228,7 +5228,8 @@ next_catch(Process* c_p, Eterm *reg) { BeamInstr *cpp = c_p->cp; if (cpp == beam_exception_trace) { erts_trace_exception(c_p, cp_val(ptr[0]), - reg[1], reg[2], ptr+1); + reg[1], reg[2], + ERTS_TRACER_FROM_ETERM(ptr+1)); /* Skip return_trace parameters */ ptr += 2; } else if (cpp == beam_return_trace) { @@ -5255,7 +5256,8 @@ next_catch(Process* c_p, Eterm *reg) { } if (cp_val(*prev) == beam_exception_trace) { erts_trace_exception(c_p, cp_val(ptr[0]), - reg[1], reg[2], ptr+1); + reg[1], reg[2], + ERTS_TRACER_FROM_ETERM(ptr+1)); } /* Skip return_trace parameters */ ptr += 2; @@ -6049,7 +6051,7 @@ erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* re return -1; } #else /* ERTS_SMP */ - ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); + ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); if (!c_p->msg.len) #endif erts_smp_atomic32_read_band_relb(&c_p->state, ~ERTS_PSFLG_ACTIVE); diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 754e11f047..d633a982ca 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -123,15 +123,12 @@ static int insert_internal_link(Process* p, Eterm rpid) erts_add_link(&ERTS_P_LINKS(p), LINK_PID, rp->common.id); erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, p->common.id); - ASSERT(is_nil(ERTS_TRACER_PROC(p)) - || is_internal_pid(ERTS_TRACER_PROC(p)) - || is_internal_port(ERTS_TRACER_PROC(p))); + ASSERT(IS_TRACER_VALID(ERTS_TRACER(p))); if (IS_TRACED(p)) { if (ERTS_TRACE_FLAGS(p) & (F_TRACE_SOL|F_TRACE_SOL1)) { ERTS_TRACE_FLAGS(rp) |= (ERTS_TRACE_FLAGS(p) & TRACEE_FLAGS); - ERTS_TRACER_PROC(rp) = ERTS_TRACER_PROC(p); /* maybe steal */ - + erts_tracer_replace(&rp->common, ERTS_TRACER(p)); if (ERTS_TRACE_FLAGS(p) & F_TRACE_SOL1) { /* maybe override */ ERTS_TRACE_FLAGS(rp) &= ~(F_TRACE_SOL1 | F_TRACE_SOL); ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOL1 | F_TRACE_SOL); @@ -140,7 +137,8 @@ static int insert_internal_link(Process* p, Eterm rpid) } } if (IS_TRACED_FL(rp, F_TRACE_PROCS)) - trace_proc(p, rp, am_getting_linked, p->common.id); + trace_proc(p, p == rp ? rp_locks : ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, + rp, am_getting_linked, p->common.id); if (p == rp) erts_smp_proc_unlock(p, rp_locks & ~ERTS_PROC_LOCK_MAIN); @@ -159,7 +157,7 @@ BIF_RETTYPE link_1(BIF_ALIST_1) DistEntry *dep; if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) { - trace_proc(BIF_P, BIF_P, am_link, BIF_ARG_1); + trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_P, am_link, BIF_ARG_1); } /* check that the pid or port which is our argument is OK */ @@ -613,7 +611,7 @@ erts_queue_monitor_message(Process *p, ref_copy = copy_struct(ref, ref_size, &hp, ohp); tup = TUPLE5(hp, am_DOWN, ref_copy, type, item_copy, reason_copy); - erts_queue_message(p, p_locksp, msgp, tup, NIL); + erts_queue_message(p, p_locksp, msgp, tup); } static BIF_RETTYPE @@ -1001,6 +999,7 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1) Process *rp; DistEntry *dep; ErtsLink *l = NULL, *rl = NULL; + ErtsProcLocks cp_locks = ERTS_PROC_LOCK_MAIN; /* * SMP specific note concerning incoming exit signals: @@ -1015,7 +1014,7 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1) */ if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) { - trace_proc(BIF_P, BIF_P, am_unlink, BIF_ARG_1); + trace_proc(BIF_P, cp_locks, BIF_P, am_unlink, BIF_ARG_1); } if (is_internal_port(BIF_ARG_1)) { @@ -1120,10 +1119,10 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1) erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); + cp_locks |= ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS; + /* get process struct */ - rp = erts_pid2proc_opt(BIF_P, (ERTS_PROC_LOCK_MAIN - | ERTS_PROC_LOCK_LINK - | ERTS_PROC_LOCK_STATUS), + rp = erts_pid2proc_opt(BIF_P, cp_locks, BIF_ARG_1, ERTS_PROC_LOCK_LINK, ERTS_P2P_FLG_ALLOW_OTHER_X); @@ -1149,14 +1148,17 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1) erts_destroy_link(rl); if (IS_TRACED_FL(rp, F_TRACE_PROCS) && rl != NULL) { - trace_proc(BIF_P, rp, am_getting_unlinked, BIF_P->common.id); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS); + cp_locks &= ~ERTS_PROC_LOCK_STATUS; + trace_proc(BIF_P, (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK), + rp, am_getting_unlinked, BIF_P->common.id); } if (rp != BIF_P) erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); } - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); + erts_smp_proc_unlock(BIF_P, cp_locks & ~ERTS_PROC_LOCK_MAIN); BIF_RET(am_true); @@ -4337,12 +4339,34 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) } else if (BIF_ARG_1 == am_trace_control_word) { BIF_RET(db_set_trace_control_word(BIF_P, BIF_ARG_2)); } else if (BIF_ARG_1 == am_sequential_tracer) { - Eterm old_value = erts_set_system_seq_tracer(BIF_P, - ERTS_PROC_LOCK_MAIN, - BIF_ARG_2); - if (old_value != THE_NON_VALUE) { - BIF_RET(old_value); - } + ErtsTracer new_seq_tracer, old_seq_tracer; + Eterm ret; + + if (BIF_ARG_2 == am_false) + new_seq_tracer = erts_tracer_nil; + else + new_seq_tracer = erts_term_to_tracer(THE_NON_VALUE, BIF_ARG_2); + + if (new_seq_tracer == THE_NON_VALUE) + goto error; + + old_seq_tracer = erts_set_system_seq_tracer(BIF_P, + ERTS_PROC_LOCK_MAIN, + new_seq_tracer); + + ERTS_TRACER_CLEAR(&new_seq_tracer); + + if (old_seq_tracer == THE_NON_VALUE) + goto error; + + if (ERTS_TRACER_IS_NIL(old_seq_tracer)) + BIF_RET(am_false); + + ret = erts_tracer_to_term(BIF_P, old_seq_tracer); + + ERTS_TRACER_CLEAR(&old_seq_tracer); + + BIF_RET(ret); } else if (BIF_ARG_1 == make_small(1)) { int i, max; ErtsMessage* mp; @@ -4679,7 +4703,7 @@ skip_current_msgq(Process *c_p) res = 0; } else { - ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); + ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); c_p->msg.save = c_p->msg.last; res = 1; } diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 9bb77190d6..58cd31cee9 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -181,9 +181,8 @@ bif erlang:port_set_data/2 bif erlang:port_get_data/1 # Tracing & debugging. -bif erlang:trace_pattern/2 -bif erlang:trace_pattern/3 -bif erlang:trace/3 +bif erts_internal:trace_pattern/3 +bif erts_internal:trace/3 bif erlang:trace_info/2 bif erlang:trace_delivered/1 bif erlang:seq_trace/2 diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c index 9da750e366..ec6267711b 100644 --- a/erts/emulator/beam/code_ix.c +++ b/erts/emulator/beam/code_ix.c @@ -94,6 +94,7 @@ void erts_commit_staging_code_ix(void) ix = (ix + 1) % ERTS_NUM_CODE_IX; erts_smp_atomic32_set_nob(&the_staging_code_index, ix); export_staging_unlock(); + erts_tracer_nif_clear(); CIX_TRACE("activate"); } diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 88449adb8e..8cd3f5fbe6 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -337,7 +337,7 @@ static void doit_link_net_exits_sub(ErtsLink *sublnk, void *vlnecp) erts_destroy_link(rlnk); if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) { /* We didn't exit the process and it is traced */ - trace_proc(NULL, rp, am_getting_unlinked, sublnk->pid); + trace_proc(NULL, 0, rp, am_getting_unlinked, sublnk->pid); } } erts_smp_proc_unlock(rp, rp_locks); @@ -397,7 +397,7 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp) msgp = erts_alloc_message_heap(rp, &rp_locks, 3, &hp, &ohp); tup = TUPLE2(hp, am_nodedown, name); - erts_queue_message(rp, &rp_locks, msgp, tup, NIL); + erts_queue_message(rp, &rp_locks, msgp, tup); } erts_smp_proc_unlock(rp, rp_locks); } @@ -1275,7 +1275,7 @@ int erts_net_message(Port *prt, erts_smp_de_links_unlock(dep); if (IS_TRACED_FL(rp, F_TRACE_PROCS)) - trace_proc(NULL, rp, am_getting_linked, from); + trace_proc(NULL, 0, rp, am_getting_linked, from); erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); break; @@ -1300,7 +1300,7 @@ int erts_net_message(Port *prt, lnk = erts_remove_link(&ERTS_P_LINKS(rp), from); if (IS_TRACED_FL(rp, F_TRACE_PROCS) && lnk != NULL) { - trace_proc(NULL, rp, am_getting_unlinked, from); + trace_proc(NULL, 0, rp, am_getting_unlinked, from); } erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); @@ -1628,7 +1628,11 @@ int erts_net_message(Port *prt, ERTS_XSIG_FLG_IGN_KILL); if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) { /* We didn't exit the process and it is traced */ - trace_proc(NULL, rp, am_getting_unlinked, from); + if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) { + erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND); + rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND; + } + trace_proc(NULL, 0, rp, am_getting_unlinked, from); } } erts_smp_proc_unlock(rp, rp_locks); @@ -3313,7 +3317,7 @@ send_nodes_mon_msg(Process *rp, } ASSERT(hend == hp); - erts_queue_message(rp, rp_locksp, mp, msg, NIL); + erts_queue_message(rp, rp_locksp, mp, msg); } static void diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index cfe0bc3205..a6519bd9e4 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3210,7 +3210,7 @@ reply_alloc_info(void *vair) if (hp != hp_end) erts_shrink_message_heap(&mp, rp, hp_start, hp, hp_end, &msg, 1); - erts_queue_message(rp, &rp_locks, mp, msg, NIL); + erts_queue_message(rp, &rp_locks, mp, msg); if (air->req_sched == sched_id) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 9482ab9265..d2fe440d47 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -270,6 +270,9 @@ type PROC_SYS_TSK SHORT_LIVED PROCESSES proc_sys_task type PROC_SYS_TSK_QS SHORT_LIVED PROCESSES proc_sys_task_queues type NEW_TIME_OFFSET SHORT_LIVED SYSTEM new_time_offset type IOB_REQ SHORT_LIVED SYSTEM io_bytes_request +type TRACER_NIF LONG_LIVED SYSTEM tracer_nif +type TRACE_MSG_QUEUE SHORT_LIVED SYSTEM trace_message_queue +type SCHED_ASYNC_JOB SHORT_LIVED SYSTEM async_calls +if threads_no_smp # Need thread safe allocs, but std_alloc and fix_alloc are not; diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index a79ce11563..1e2db38442 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -1737,7 +1737,7 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type, hp += REF_THING_SIZE; mess = TUPLE5(hp,type,r,am_driver,driver_name,tag); } - erts_queue_message(proc, &rp_locks, mp, mess, am_undefined); + erts_queue_message(proc, &rp_locks, mp, mess); erts_smp_proc_unlock(proc, rp_locks); ERTS_SMP_CHK_NO_PROC_LOCKS; } diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 68f6abfcdc..7a72b0d8cc 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -846,6 +846,7 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap, if (unlock_locks) erts_smp_proc_unlock(rp, unlock_locks); + } /* @@ -2162,8 +2163,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) res = build_snifs_term(&hp, NULL, NIL); BIF_RET(res); } else if (BIF_ARG_1 == am_sequential_tracer) { - val = erts_get_system_seq_tracer(); - ASSERT(is_internal_pid(val) || is_internal_port(val) || val==am_false); + ErtsTracer seq_tracer = erts_get_system_seq_tracer(); + val = erts_tracer_to_term(BIF_P, seq_tracer); hp = HAlloc(BIF_P, 3); res = TUPLE2(hp, am_sequential_tracer, val); BIF_RET(res); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 71b6d78094..eb55bd585e 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -79,6 +79,7 @@ BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2) } if (port->drv_ptr->flags & ERL_DRV_FLAG_USE_INIT_ACK) { + /* Copied from erl_port_task.c */ port->async_open_port = erts_alloc(ERTS_ALC_T_PRTSD, sizeof(*port->async_open_port)); @@ -911,7 +912,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) } if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) { - trace_virtual_sched(p, am_out); + trace_sched(p, ERTS_PROC_LOCK_MAIN, am_out); } @@ -935,13 +936,13 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) err_typep ? *err_typep : 4711, err_nump ? *err_nump : 4711)); if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) { - trace_virtual_sched(p, am_in); + trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in); } goto do_return; } if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) { - trace_virtual_sched(p, am_in); + trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in); } if (linebuf && port->linebuf == NULL){ diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index a9443ee8df..049899211f 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -52,7 +52,7 @@ static int erts_default_trace_pattern_is_on; static Binary *erts_default_match_spec; static Binary *erts_default_meta_match_spec; static struct trace_pattern_flags erts_default_trace_pattern_flags; -static Eterm erts_default_meta_tracer_pid; +static ErtsTracer erts_default_meta_tracer; static struct { /* Protected by code write permission */ int current; @@ -75,8 +75,6 @@ static BIF_RETTYPE system_monitor(Process *p, Eterm monitor_pid, Eterm list); static void new_seq_trace_token(Process* p); /* help func for seq_trace_2*/ -static int already_traced(Process *p, Process *tracee_p, Eterm tracer); -static int port_already_traced(Process *p, Port *tracee_port, Eterm tracer); static Eterm trace_info_pid(Process* p, Eterm pid_spec, Eterm key); static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key); static Eterm trace_info_on_load(Process* p, Eterm key); @@ -94,21 +92,15 @@ erts_bif_trace_init(void) erts_default_match_spec = NULL; erts_default_meta_match_spec = NULL; erts_default_trace_pattern_flags = erts_trace_pattern_flags_off; - erts_default_meta_tracer_pid = NIL; + erts_default_meta_tracer = erts_tracer_nil; } /* * Turn on/off call tracing for the given function(s). */ - -Eterm -trace_pattern_2(BIF_ALIST_2) -{ - return trace_pattern(BIF_P, BIF_ARG_1, BIF_ARG_2, NIL); -} Eterm -trace_pattern_3(BIF_ALIST_3) +erts_internal_trace_pattern_3(BIF_ALIST_3) { return trace_pattern(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); } @@ -125,11 +117,10 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) Eterm l; struct trace_pattern_flags flags = erts_trace_pattern_flags_off; int is_global; - Process *meta_tracer_proc = p; - Eterm meta_tracer_pid = p->common.id; + ErtsTracer meta_tracer = erts_tracer_nil; if (!erts_try_seize_code_write_permission(p)) { - ERTS_BIF_YIELD3(bif_export[BIF_trace_pattern_3], p, MFA, Pattern, flaglist); + ERTS_BIF_YIELD3(bif_export[BIF_erts_internal_trace_pattern_3], p, MFA, Pattern, flaglist); } finish_bp.current = -1; @@ -160,31 +151,11 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) is_global = 0; for(l = flaglist; is_list(l); l = CDR(list_val(l))) { if (is_tuple(CAR(list_val(l)))) { - Eterm *tp = tuple_val(CAR(list_val(l))); - - if (arityval(tp[0]) != 2 || tp[1] != am_meta) { - goto error; - } - meta_tracer_pid = tp[2]; - if (is_internal_pid(meta_tracer_pid)) { - meta_tracer_proc = erts_pid2proc(NULL, 0, meta_tracer_pid, 0); - if (!meta_tracer_proc) { - goto error; - } - } else if (is_internal_port(meta_tracer_pid)) { - Port *meta_tracer_port; - meta_tracer_proc = NULL; - meta_tracer_port = (erts_port_lookup( - meta_tracer_pid, - ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP)); - if (!meta_tracer_port) - goto error; - } else { - goto error; - } - if (is_global) { - goto error; - } + meta_tracer = erts_term_to_tracer(am_meta, CAR(list_val(l))); + if (meta_tracer == THE_NON_VALUE) { + meta_tracer = erts_tracer_nil; + goto error; + } flags.breakpoint = 1; flags.meta = 1; } else { @@ -202,6 +173,8 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) } flags.breakpoint = 1; flags.meta = 1; + if (ERTS_TRACER_IS_NIL(meta_tracer)) + meta_tracer = erts_term_to_tracer(THE_NON_VALUE, p->common.id); break; case am_global: if (flags.breakpoint) { @@ -252,14 +225,11 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) MatchSetUnref(erts_default_meta_match_spec); erts_default_meta_match_spec = match_prog_set; MatchSetRef(erts_default_meta_match_spec); - erts_default_meta_tracer_pid = meta_tracer_pid; - if (meta_tracer_proc) { - ERTS_TRACE_FLAGS(meta_tracer_proc) |= F_TRACER; - } + erts_tracer_update(&erts_default_meta_tracer, meta_tracer); } else if (! flags.breakpoint) { MatchSetUnref(erts_default_meta_match_spec); erts_default_meta_match_spec = NULL; - erts_default_meta_tracer_pid = NIL; + ERTS_TRACER_CLEAR(&erts_default_meta_tracer); } if (erts_default_trace_pattern_flags.breakpoint && flags.breakpoint) { @@ -340,20 +310,18 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) if (is_small(mfa[2])) { mfa[2] = signed_val(mfa[2]); } - - if (meta_tracer_proc) { - ERTS_TRACE_FLAGS(meta_tracer_proc) |= F_TRACER; - } matches = erts_set_trace_pattern(p, mfa, specified, match_prog_set, match_prog_set, - on, flags, meta_tracer_pid, 0); + on, flags, meta_tracer, 0); } error: MatchSetUnref(match_prog_set); UnUseTmpHeap(3,p); + ERTS_TRACER_CLEAR(&meta_tracer); + #ifdef ERTS_SMP if (finish_bp.current >= 0) { ASSERT(matches >= 0); @@ -404,7 +372,7 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on, Binary **match_spec, Binary **meta_match_spec, struct trace_pattern_flags *trace_pattern_flags, - Eterm *meta_tracer_pid) + ErtsTracer *meta_tracer) { ERTS_SMP_LC_ASSERT(erts_has_code_write_permission() || erts_smp_thr_progress_is_blocking()); @@ -416,8 +384,8 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on, *meta_match_spec = erts_default_meta_match_spec; if (trace_pattern_flags) *trace_pattern_flags = erts_default_trace_pattern_flags; - if (meta_tracer_pid) - *meta_tracer_pid = erts_default_meta_tracer_pid; + if (meta_tracer) + *meta_tracer = erts_default_meta_tracer; } int erts_is_default_trace_enabled(void) @@ -465,12 +433,12 @@ erts_trace_flag2bit(Eterm flag) ** occurred in the argument list. */ int -erts_trace_flags(Eterm List, - Uint *pMask, Eterm *pTracer, int *pCpuTimestamp) +erts_trace_flags(Eterm List, + Uint *pMask, ErtsTracer *pTracer, int *pCpuTimestamp) { Eterm list = List; Uint mask = 0; - Eterm tracer = NIL; + ErtsTracer tracer = erts_tracer_nil; int cpu_timestamp = 0; while (is_list(list)) { @@ -483,33 +451,72 @@ erts_trace_flags(Eterm List, cpu_timestamp = !0; #endif } else if (is_tuple(item)) { - Eterm* tp = tuple_val(item); - - if (arityval(tp[0]) != 2 || tp[1] != am_tracer) goto error; - if (is_internal_pid(tp[2]) || is_internal_port(tp[2])) { - tracer = tp[2]; - } else goto error; + tracer = erts_term_to_tracer(am_tracer, item); + if (tracer == THE_NON_VALUE) + goto error; } else goto error; list = CDR(list_val(list)); } if (is_not_nil(list)) goto error; - if (pMask && mask) *pMask = mask; - if (pTracer && tracer != NIL) *pTracer = tracer; - if (pCpuTimestamp && cpu_timestamp) *pCpuTimestamp = cpu_timestamp; + if (pMask && mask) *pMask = mask; + if (pTracer && !ERTS_TRACER_IS_NIL(tracer)) *pTracer = tracer; + if (pCpuTimestamp && cpu_timestamp) *pCpuTimestamp = cpu_timestamp; return !0; error: return 0; } -Eterm trace_3(BIF_ALIST_3) +static ERTS_INLINE int +start_trace(Process *c_p, ErtsTracer tracer, + ErtsPTabElementCommon *common, + int on, int mask) +{ + /* We can use the common part of both port+proc without checking what it is + In the code below port is used for both proc and port */ + Port *port = (Port*)common; + + /* + * SMP build assumes that either system is blocked or: + * * main lock is held on c_p + * * all locks are held on port common + */ + + if (!ERTS_TRACER_IS_NIL(tracer)) { + if ((ERTS_TRACE_FLAGS(port) & TRACEE_FLAGS) + && !ERTS_TRACER_COMPARE(ERTS_TRACER(port), tracer)) { + /* This tracee is already being traced, and not by the + * tracer to be */ + if (erts_is_tracer_proc_enabled(c_p, ERTS_PROC_LOCKS_ALL, common)) { + /* The tracer is still in use */ + return 1; + } + /* Current tracer now invalid */ + } + } + + if (on) + ERTS_TRACE_FLAGS(port) |= mask; + else + ERTS_TRACE_FLAGS(port) &= ~mask; + + if ((ERTS_TRACE_FLAGS(port) & TRACEE_FLAGS) == 0) { + tracer = erts_tracer_nil; + erts_tracer_replace(common, erts_tracer_nil); + } else if (!ERTS_TRACER_IS_NIL(tracer)) + erts_tracer_replace(common, tracer); + + return 0; +} + +Eterm erts_internal_trace_3(BIF_ALIST_3) { Process* p = BIF_P; Eterm pid_spec = BIF_ARG_1; Eterm how = BIF_ARG_2; Eterm list = BIF_ARG_3; int on; - Eterm tracer = NIL; + ErtsTracer tracer = erts_tracer_nil; int matches = 0; Uint mask = 0; int cpu_ts = 0; @@ -522,41 +529,24 @@ Eterm trace_3(BIF_ALIST_3) } if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD3(bif_export[BIF_trace_3], BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); + ERTS_BIF_YIELD3(bif_export[BIF_erts_internal_trace_3], + BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); } - if (is_nil(tracer) || is_internal_pid(tracer)) { - Process *tracer_proc = erts_pid2proc(p, - ERTS_PROC_LOCK_MAIN, - is_nil(tracer) ? p->common.id : tracer, - ERTS_PROC_LOCKS_ALL); - if (!tracer_proc) - goto error; - ERTS_TRACE_FLAGS(tracer_proc) |= F_TRACER; - erts_smp_proc_unlock(tracer_proc, - (tracer_proc == p - ? ERTS_PROC_LOCKS_ALL_MINOR - : ERTS_PROC_LOCKS_ALL)); - } else if (is_internal_port(tracer)) { - Port *tracer_port = erts_id2port_sflgs(tracer, - p, - ERTS_PROC_LOCK_MAIN, - ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP); - if (!tracer_port) - goto error; - ERTS_TRACE_FLAGS(tracer_port) |= F_TRACER; - erts_port_release(tracer_port); - } else - goto error; - switch (how) { case am_false: on = 0; break; case am_true: on = 1; - if (is_nil(tracer)) - tracer = p->common.id; + if (ERTS_TRACER_IS_NIL(tracer)) + tracer = erts_term_to_tracer(am_tracer, p->common.id); + + if (tracer == THE_NON_VALUE) { + tracer = erts_tracer_nil; + goto error; + } + break; default: goto error; @@ -575,34 +565,20 @@ Eterm trace_3(BIF_ALIST_3) } #endif - if (pid_spec == tracer) - goto error; - tracee_port = erts_id2port_sflgs(pid_spec, p, ERTS_PROC_LOCK_MAIN, ERTS_PORT_SFLGS_INVALID_LOOKUP); + if (!tracee_port) goto error; - - if (tracer != NIL && port_already_traced(p, tracee_port, tracer)) { + + if (start_trace(p, tracer, &tracee_port->common, on, mask)) { erts_port_release(tracee_port); goto already_traced; - } - - if (on) - ERTS_TRACE_FLAGS(tracee_port) |= mask; - else - ERTS_TRACE_FLAGS(tracee_port) &= ~mask; - - if (!ERTS_TRACE_FLAGS(tracee_port)) - ERTS_TRACER_PROC(tracee_port) = NIL; - else if (tracer != NIL) - ERTS_TRACER_PROC(tracee_port) = tracer; - - erts_port_release(tracee_port); - - matches = 1; + } + erts_port_release(tracee_port); + matches = 1; } else if (is_pid(pid_spec)) { Process *tracee_p; @@ -615,33 +591,19 @@ Eterm trace_3(BIF_ALIST_3) * and not about to be tracing. */ - if (pid_spec == tracer) - goto error; - tracee_p = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, pid_spec, ERTS_PROC_LOCKS_ALL); if (!tracee_p) goto error; - if (tracer != NIL && already_traced(p, tracee_p, tracer)) { + if (start_trace(tracee_p, tracer, &tracee_p->common, on, mask)) { erts_smp_proc_unlock(tracee_p, (tracee_p == p ? ERTS_PROC_LOCKS_ALL_MINOR : ERTS_PROC_LOCKS_ALL)); goto already_traced; - } - - if (on) - ERTS_TRACE_FLAGS(tracee_p) |= mask; - else - ERTS_TRACE_FLAGS(tracee_p) &= ~mask; - - if ((ERTS_TRACE_FLAGS(tracee_p) & TRACEE_FLAGS) == 0) - ERTS_TRACER_PROC(tracee_p) = NIL; - else if (tracer != NIL) - ERTS_TRACER_PROC(tracee_p) = tracer; - - erts_smp_proc_unlock(tracee_p, + } + erts_smp_proc_unlock(tracee_p, (tracee_p == p ? ERTS_PROC_LOCKS_ALL_MINOR : ERTS_PROC_LOCKS_ALL)); @@ -719,22 +681,7 @@ Eterm trace_3(BIF_ALIST_3) Process* tracee_p = erts_pix2proc(i); if (! tracee_p) continue; - if (tracer != NIL) { - if (tracee_p->common.id == tracer) - continue; - if (already_traced(NULL, tracee_p, tracer)) - continue; - } - if (on) { - ERTS_TRACE_FLAGS(tracee_p) |= mask; - } else { - ERTS_TRACE_FLAGS(tracee_p) &= ~mask; - } - if(!(ERTS_TRACE_FLAGS(tracee_p) & TRACEE_FLAGS)) { - ERTS_TRACER_PROC(tracee_p) = NIL; - } else if (tracer != NIL) { - ERTS_TRACER_PROC(tracee_p) = tracer; - } + start_trace(p, tracer, &tracee_p->common, on, mask); matches++; } } @@ -749,21 +696,7 @@ Eterm trace_3(BIF_ALIST_3) state = erts_atomic32_read_nob(&tracee_port->state); if (state & ERTS_PORT_SFLGS_DEAD) continue; - if (tracer != NIL) { - if (tracee_port->common.id == tracer) - continue; - if (port_already_traced(NULL, tracee_port, tracer)) - continue; - } - - if (on) ERTS_TRACE_FLAGS(tracee_port) |= mask; - else ERTS_TRACE_FLAGS(tracee_port) &= ~mask; - - if (!(ERTS_TRACE_FLAGS(tracee_port) & TRACEE_FLAGS)) { - ERTS_TRACER_PROC(tracee_port) = NIL; - } else if (tracer != NIL) { - ERTS_TRACER_PROC(tracee_port) = tracer; - } + start_trace(p, tracer, &tracee_port->common, on, mask); /* matches are not counted for ports since it would violate compatibility */ /* This could be a reason to modify this function or make a new one. */ } @@ -772,10 +705,9 @@ Eterm trace_3(BIF_ALIST_3) if (pid_spec == am_all || pid_spec == am_new) { Uint def_flags = mask; - Eterm def_tracer = tracer; ok = 1; - erts_change_default_tracing(on, &def_flags, &def_tracer); + erts_change_default_tracing(on, &def_flags, tracer); #ifdef HAVE_ERTS_NOW_CPU if (cpu_ts && !on) { @@ -801,6 +733,7 @@ Eterm trace_3(BIF_ALIST_3) } #endif erts_release_code_write_permission(); + ERTS_TRACER_CLEAR(&tracer); BIF_RET(make_small(matches)); @@ -810,6 +743,8 @@ Eterm trace_3(BIF_ALIST_3) error: + ERTS_TRACER_CLEAR(&tracer); + #ifdef ERTS_SMP if (system_blocked) { erts_smp_thr_progress_unblock(); @@ -821,88 +756,6 @@ Eterm trace_3(BIF_ALIST_3) BIF_ERROR(p, BADARG); } -/* Check that the process to be traced is not already traced - * by a valid other tracer than the tracer to be. - */ -static int port_already_traced(Process *c_p, Port *tracee_port, Eterm tracer) -{ - /* - * SMP build assumes that either system is blocked or: - * * main lock is held on c_p - * * all locks are held on port tracee_p - */ - if ((ERTS_TRACE_FLAGS(tracee_port) & TRACEE_FLAGS) - && ERTS_TRACER_PROC(tracee_port) != tracer) { - /* This tracee is already being traced, and not by the - * tracer to be */ - if (is_internal_port(ERTS_TRACER_PROC(tracee_port))) { - if (!erts_is_valid_tracer_port(ERTS_TRACER_PROC(tracee_port))) { - /* Current trace port now invalid - * - discard it and approve the new. */ - goto remove_tracer; - } else - return 1; - } - else if(is_internal_pid(ERTS_TRACER_PROC(tracee_port))) { - Process *tracer_p = erts_proc_lookup(ERTS_TRACER_PROC(tracee_port)); - if (!tracer_p) { - /* Current trace process now invalid - * - discard it and approve the new. */ - goto remove_tracer; - } else - return 1; - } - else { - remove_tracer: - ERTS_TRACE_FLAGS(tracee_port) &= ~TRACEE_FLAGS; - ERTS_TRACER_PROC(tracee_port) = NIL; - } - } - return 0; -} - -/* Check that the process to be traced is not already traced - * by a valid other tracer than the tracer to be. - */ -static int already_traced(Process *c_p, Process *tracee_p, Eterm tracer) -{ - /* - * SMP build assumes that either system is blocked or: - * * main lock is held on c_p - * * all locks multiple are held on tracee_p - */ - if ((ERTS_TRACE_FLAGS(tracee_p) & TRACEE_FLAGS) - && ERTS_TRACER_PROC(tracee_p) != tracer) { - /* This tracee is already being traced, and not by the - * tracer to be */ - if (is_internal_port(ERTS_TRACER_PROC(tracee_p))) { - if (!erts_is_valid_tracer_port(ERTS_TRACER_PROC(tracee_p))) { - /* Current trace port now invalid - * - discard it and approve the new. */ - goto remove_tracer; - } else - return 1; - } - else if(is_internal_pid(ERTS_TRACER_PROC(tracee_p))) { - Process *tracer_p; - - tracer_p = erts_proc_lookup(ERTS_TRACER_PROC(tracee_p)); - if (!tracer_p) { - /* Current trace process now invalid - * - discard it and approve the new. */ - goto remove_tracer; - } else - return 1; - } - else { - remove_tracer: - ERTS_TRACE_FLAGS(tracee_p) &= ~TRACEE_FLAGS; - ERTS_TRACER_PROC(tracee_p) = NIL; - } - } - return 0; -} - /* * Return information about a process or an external function being traced. */ @@ -936,41 +789,30 @@ static Eterm trace_info_pid(Process* p, Eterm pid_spec, Eterm key) { Eterm tracer; - Uint trace_flags; + Uint trace_flags = am_false; Eterm* hp; if (pid_spec == am_new) { - erts_get_default_tracing(&trace_flags, &tracer); + ErtsTracer def_tracer; + erts_get_default_tracing(&trace_flags, &def_tracer); + tracer = erts_tracer_to_term(p, def_tracer); + ERTS_TRACER_CLEAR(&def_tracer); } else if (is_internal_pid(pid_spec)) { Process *tracee; tracee = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, - pid_spec, ERTS_PROC_LOCKS_ALL); + pid_spec, ERTS_PROC_LOCK_MAIN); - if (!tracee) { + if (!tracee) return am_undefined; - } else { - tracer = ERTS_TRACER_PROC(tracee); - trace_flags = ERTS_TRACE_FLAGS(tracee); - } - if (is_internal_pid(tracer)) { - if (!erts_proc_lookup(tracer)) { - reset_tracer: - ERTS_TRACE_FLAGS(tracee) &= ~TRACEE_FLAGS; - trace_flags = ERTS_TRACE_FLAGS(tracee); - tracer = ERTS_TRACER_PROC(tracee) = NIL; - } - } - else if (is_internal_port(tracer)) { - if (!erts_is_valid_tracer_port(tracer)) - goto reset_tracer; - } -#ifdef ERTS_SMP - erts_smp_proc_unlock(tracee, - (tracee == p - ? ERTS_PROC_LOCKS_ALL_MINOR - : ERTS_PROC_LOCKS_ALL)); -#endif + if (!ERTS_TRACER_IS_NIL(ERTS_TRACER(tracee))) + erts_is_tracer_proc_enabled(tracee, ERTS_PROC_LOCK_MAIN, &tracee->common); + + tracer = erts_tracer_to_term(p, ERTS_TRACER(tracee)); + trace_flags = ERTS_TRACE_FLAGS(tracee); + + if (tracee != p) + erts_smp_proc_unlock(tracee, ERTS_PROC_LOCK_MAIN); } else if (is_external_pid(pid_spec) && external_pid_dist_entry(pid_spec) == erts_this_dist_entry) { return am_undefined; @@ -1024,8 +866,10 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) HRelease(p,limit,hp+3); return TUPLE2(hp, key, flag_list); } else if (key == am_tracer) { - hp = HAlloc(p, 3); - return TUPLE2(hp, key, tracer); /* Local pid or port */ + if (tracer == am_false) + tracer = NIL; + hp = HAlloc(p, 3); + return TUPLE2(hp, key, tracer); } else { goto error; } @@ -1054,11 +898,11 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) */ static int function_is_traced(Process *p, Eterm mfa[3], - Binary **ms, /* out */ - Binary **ms_meta, /* out */ - Eterm *tracer_pid_meta, /* out */ - Uint *count, /* out */ - Eterm *call_time) /* out */ + Binary **ms, /* out */ + Binary **ms_meta, /* out */ + ErtsTracer *tracer_pid_meta, /* out */ + Uint *count, /* out */ + Eterm *call_time) /* out */ { Export e; Export* ep; @@ -1123,7 +967,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) Eterm traced = am_false; Eterm match_spec = am_false; Eterm retval = am_false; - Eterm meta = am_false; + ErtsTracer meta = erts_tracer_nil; Eterm call_time = NIL; int r; @@ -1193,7 +1037,10 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) retval = match_spec; break; case am_meta: - retval = meta; + retval = erts_tracer_to_term(p, meta); + if (retval == am_false) + /* backwards compatibility */ + retval = NIL; break; case am_meta_match_spec: if (r & FUNC_TRACE_META_TRACE) { @@ -1216,7 +1063,8 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) } break; case am_all: { - Eterm match_spec_meta = am_false, c = am_false, t, ct = am_false; + Eterm match_spec_meta = am_false, c = am_false, t, ct = am_false, + m = am_false; if (ms) { match_spec = MatchSetGetSource(ms); @@ -1235,6 +1083,9 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) if (r & FUNC_TRACE_TIME_TRACE) { ct = call_time; } + + m = erts_tracer_to_term(p, meta); + hp = HAlloc(p, (3+2)*6); retval = NIL; t = TUPLE2(hp, am_call_count, c); hp += 3; @@ -1243,7 +1094,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) retval = CONS(hp, t, retval); hp += 2; t = TUPLE2(hp, am_meta_match_spec, match_spec_meta); hp += 3; retval = CONS(hp, t, retval); hp += 2; - t = TUPLE2(hp, am_meta, meta); hp += 3; + t = TUPLE2(hp, am_meta, m); hp += 3; retval = CONS(hp, t, retval); hp += 2; t = TUPLE2(hp, am_match_spec, match_spec); hp += 3; retval = CONS(hp, t, retval); hp += 2; @@ -1306,7 +1157,8 @@ trace_info_on_load(Process* p, Eterm key) case am_meta: hp = HAlloc(p, 3); if (erts_default_trace_pattern_flags.meta) { - return TUPLE2(hp, key, erts_default_meta_tracer_pid); + ASSERT(!ERTS_TRACER_IS_NIL(erts_default_meta_tracer)); + return TUPLE2(hp, key, erts_tracer_to_term(p, erts_default_meta_tracer)); } else { return TUPLE2(hp, key, am_false); } @@ -1345,7 +1197,7 @@ trace_info_on_load(Process* p, Eterm key) } case am_all: { - Eterm match_spec = am_false, meta_match_spec = am_false, r = NIL, t; + Eterm match_spec = am_false, meta_match_spec = am_false, r = NIL, t, m; if (erts_default_trace_pattern_flags.local || (! erts_default_trace_pattern_flags.breakpoint)) { @@ -1363,6 +1215,8 @@ trace_info_on_load(Process* p, Eterm key) MatchSetGetSource(erts_default_meta_match_spec); meta_match_spec = copy_object(meta_match_spec, p); } + m = (erts_default_trace_pattern_flags.meta + ? erts_tracer_to_term(p, erts_default_meta_tracer) : am_false); hp = HAlloc(p, (3+2)*5 + 3); t = TUPLE2(hp, am_call_count, (erts_default_trace_pattern_flags.call_count @@ -1370,9 +1224,7 @@ trace_info_on_load(Process* p, Eterm key) r = CONS(hp, t, r); hp += 2; t = TUPLE2(hp, am_meta_match_spec, meta_match_spec); hp += 3; r = CONS(hp, t, r); hp += 2; - t = TUPLE2(hp, am_meta, - (erts_default_trace_pattern_flags.meta - ? erts_default_meta_tracer_pid : am_false)); hp += 3; + t = TUPLE2(hp, am_meta, m); hp += 3; r = CONS(hp, t, r); hp += 2; t = TUPLE2(hp, am_match_spec, match_spec); hp += 3; r = CONS(hp, t, r); hp += 2; @@ -1397,7 +1249,7 @@ int erts_set_trace_pattern(Process*p, Eterm* mfa, int specified, Binary* match_prog_set, Binary *meta_match_prog_set, int on, struct trace_pattern_flags flags, - Eterm meta_tracer_pid, int is_blocking) + ErtsTracer meta_tracer, int is_blocking) { const ErtsCodeIndex code_ix = erts_active_code_ix(); int matches = 0; @@ -1487,7 +1339,7 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified, } if (flags.meta) { erts_set_mtrace_bif(pc, meta_match_prog_set, - meta_tracer_pid); + meta_tracer); m = 1; } if (flags.call_time) { @@ -1527,7 +1379,7 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified, } if (flags.meta) { erts_set_mtrace_break(&finish_bp.f, meta_match_prog_set, - meta_tracer_pid); + meta_tracer); } if (flags.call_count) { erts_set_count_break(&finish_bp.f, on); @@ -2336,50 +2188,79 @@ BIF_RETTYPE system_profile_2(BIF_ALIST_2) } /* End: Trace for System Profiling */ -BIF_RETTYPE -trace_delivered_1(BIF_ALIST_1) +/* Trace delivered send an aux work message to all schedulers + and when all schedulers have acknowledged that they have seen + the message the message is sent to the requesting process. + + IMPORTANT: We have to make sure that the all messages sent + using enif_send have been delivered before we send the message + to the caller. + + There used to be a separate implementation for when only a pid + is passed in, but since this is not performance critical code + we now use the same approach for both. +*/ + +typedef struct { + Process *proc; + Eterm ref; + Eterm ref_heap[REF_THING_SIZE]; + Eterm target; + erts_smp_atomic32_t refc; +} ErtsTraceDeliveredAll; + +static void +reply_trace_delivered_all(void *vtdarp) { - DECL_AM(trace_delivered); -#ifdef ERTS_SMP - ErlHeapFragment *bp; -#else - ErtsProcLocks locks = 0; -#endif - Eterm *hp; - Eterm msg, ref, msg_ref; - Process *p; - if (BIF_ARG_1 == am_all) { - p = NULL; - } else if (! (p = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN, - BIF_ARG_1, ERTS_PROC_LOCKS_ALL))) { - if (is_not_internal_pid(BIF_ARG_1)) { - BIF_ERROR(BIF_P, BADARG); - } - } - - ref = erts_make_ref(BIF_P); + ErtsTraceDeliveredAll *tdarp = (ErtsTraceDeliveredAll *) vtdarp; + ErtsProcLocks rp_locks = 0; -#ifdef ERTS_SMP - bp = new_message_buffer(REF_THING_SIZE + 4); - hp = &bp->mem[0]; - msg_ref = STORE_NC(&hp, &bp->off_heap, ref); -#else - hp = HAlloc(BIF_P, 4); - msg_ref = ref; -#endif + if (erts_smp_atomic32_dec_read_nob(&tdarp->refc) == 0) { + Process *rp = tdarp->proc; + Eterm *hp = NULL; + ErlOffHeap *ohp = NULL; + ErtsMessage *mp = NULL; + Eterm ref_copy, msg; - msg = TUPLE3(hp, AM_trace_delivered, BIF_ARG_1, msg_ref); + mp = erts_alloc_message_heap( + rp, &rp_locks, 4 + NC_HEAP_SIZE(tdarp->ref), &hp, &ohp); -#ifdef ERTS_SMP - erts_send_sys_msg_proc(BIF_P->common.id, BIF_P->common.id, msg, bp); - if (p) - erts_smp_proc_unlock(p, - (BIF_P == p - ? ERTS_PROC_LOCKS_ALL_MINOR - : ERTS_PROC_LOCKS_ALL)); -#else - erts_send_message(BIF_P, BIF_P, &locks, msg, ERTS_SND_FLG_NO_SEQ_TRACE); -#endif + ref_copy = STORE_NC(&hp, ohp, tdarp->ref); + msg = TUPLE3(hp, am_trace_delivered, tdarp->target, ref_copy); + + erts_queue_message(rp, &rp_locks, mp, msg); + + if (rp_locks) + erts_smp_proc_unlock(rp, rp_locks); - BIF_RET(ref); + erts_free(ERTS_ALC_T_MISC_AUX_WORK, vtdarp); + erts_proc_dec_refc(rp); + } +} + +BIF_RETTYPE +trace_delivered_1(BIF_ALIST_1) +{ + + if (BIF_ARG_1 == am_all || is_internal_pid(BIF_ARG_1)) { + Eterm *hp, ref; + ErtsTraceDeliveredAll *tdarp = + erts_alloc(ERTS_ALC_T_MISC_AUX_WORK, sizeof(ErtsTraceDeliveredAll)); + + tdarp->proc = BIF_P; + ref = erts_make_ref(BIF_P); + hp = &tdarp->ref_heap[0]; + tdarp->ref = STORE_NC(&hp, NULL, ref); + tdarp->target = BIF_ARG_1; + erts_smp_atomic32_init_nob(&tdarp->refc, + (erts_aint32_t) erts_no_schedulers); + erts_proc_add_refc(BIF_P, 1); + erts_schedule_multi_misc_aux_work(0, + erts_no_schedulers, + reply_trace_delivered_all, + (void *) tdarp); + BIF_RET(ref); + } else { + BIF_ERROR(BIF_P, BADARG); + } } diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index be14386b14..76b96637ae 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -135,21 +135,22 @@ get_proc(Process *cp, Uint32 cp_locks, Eterm id, Uint32 id_locks) static Eterm -set_tracee_flags(Process *tracee_p, Eterm tracer, Uint d_flags, Uint e_flags) { +set_tracee_flags(Process *tracee_p, ErtsTracer tracer, + Uint d_flags, Uint e_flags) { Eterm ret; Uint flags; - if (tracer == NIL) { + if (ERTS_TRACER_IS_NIL(tracer)) { flags = ERTS_TRACE_FLAGS(tracee_p) & ~TRACEE_FLAGS; } else { flags = ((ERTS_TRACE_FLAGS(tracee_p) & ~d_flags) | e_flags); - if (! flags) tracer = NIL; + if (! flags) tracer = erts_tracer_nil; } - ret = ((ERTS_TRACER_PROC(tracee_p) != tracer + ret = ((!ERTS_TRACER_COMPARE(ERTS_TRACER(tracee_p),tracer) || ERTS_TRACE_FLAGS(tracee_p) != flags) ? am_true : am_false); - ERTS_TRACER_PROC(tracee_p) = tracer; + erts_tracer_replace(&tracee_p->common, tracer); ERTS_TRACE_FLAGS(tracee_p) = flags; return ret; } @@ -163,40 +164,16 @@ set_tracee_flags(Process *tracee_p, Eterm tracer, Uint d_flags, Uint e_flags) { ** returns fail_term on failure. Fails if tracer pid or port is invalid. */ static Eterm -set_match_trace(Process *tracee_p, Eterm fail_term, Eterm tracer, +set_match_trace(Process *tracee_p, Eterm fail_term, ErtsTracer tracer, Uint d_flags, Uint e_flags) { - Eterm ret = fail_term; - Process *tracer_p; - - ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCKS_ALL == - erts_proc_lc_my_proc_locks(tracee_p)); - - if (is_internal_pid(tracer) - && (tracer_p = - erts_pid2proc(tracee_p, ERTS_PROC_LOCKS_ALL, - tracer, ERTS_PROC_LOCKS_ALL))) { - if (tracee_p != tracer_p) { - ret = set_tracee_flags(tracee_p, tracer, d_flags, e_flags); - ERTS_TRACE_FLAGS(tracer_p) |= (ERTS_TRACE_FLAGS(tracee_p) - ? F_TRACER - : 0); - erts_smp_proc_unlock(tracer_p, ERTS_PROC_LOCKS_ALL); - } - } else if (is_internal_port(tracer)) { - Port *tracer_port = - erts_id2port_sflgs(tracer, - tracee_p, - ERTS_PROC_LOCKS_ALL, - ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP); - if (tracer_port) { - ret = set_tracee_flags(tracee_p, tracer, d_flags, e_flags); - erts_port_release(tracer_port); - } - } else { - ASSERT(is_nil(tracer)); - ret = set_tracee_flags(tracee_p, tracer, d_flags, e_flags); - } - return ret; + + ERTS_SMP_LC_ASSERT( + ERTS_PROC_LOCKS_ALL == erts_proc_lc_my_proc_locks(tracee_p) + || erts_thr_progress_is_blocking()); + + if (ERTS_TRACER_IS_NIL(tracer) || erts_is_tracer_enabled(tracee_p, tracer)) + return set_tracee_flags(tracee_p, tracer, d_flags, e_flags); + return fail_term; } /* @@ -2358,7 +2335,7 @@ restart: case matchEnableTrace: if ( (n = erts_trace_flag2bit(esp[-1]))) { BEGIN_ATOMIC_TRACE(c_p); - set_tracee_flags(c_p, ERTS_TRACER_PROC(c_p), 0, n); + set_tracee_flags(c_p, ERTS_TRACER(c_p), 0, n); esp[-1] = am_true; } else { esp[-1] = FAIL_TERM; @@ -2371,7 +2348,7 @@ restart: BEGIN_ATOMIC_TRACE(c_p); if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) { /* Always take over the tracer of the current process */ - set_tracee_flags(tmpp, ERTS_TRACER_PROC(c_p), 0, n); + set_tracee_flags(tmpp, ERTS_TRACER(c_p), 0, n); esp[-1] = am_true; } } @@ -2379,7 +2356,7 @@ restart: case matchDisableTrace: if ( (n = erts_trace_flag2bit(esp[-1]))) { BEGIN_ATOMIC_TRACE(c_p); - set_tracee_flags(c_p, ERTS_TRACER_PROC(c_p), n, 0); + set_tracee_flags(c_p, ERTS_TRACER(c_p), n, 0); esp[-1] = am_true; } else { esp[-1] = FAIL_TERM; @@ -2392,7 +2369,7 @@ restart: BEGIN_ATOMIC_TRACE(c_p); if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) { /* Always take over the tracer of the current process */ - set_tracee_flags(tmpp, ERTS_TRACER_PROC(c_p), n, 0); + set_tracee_flags(tmpp, ERTS_TRACER(c_p), n, 0); esp[-1] = am_true; } } @@ -2428,7 +2405,7 @@ restart: { /* disable enable */ Uint d_flags = 0, e_flags = 0; /* process trace flags */ - Eterm tracer = ERTS_TRACER_PROC(c_p); + ErtsTracer tracer = erts_tracer_nil; /* XXX Atomicity note: Not fully atomic. Default tracer * is sampled from current process but applied to * tracee and tracer later after releasing main @@ -2440,29 +2417,34 @@ restart: * {trace,[],[{{tracer,Tracer}}]} is much, much older. */ int cputs = 0; + erts_tracer_update(&tracer, ERTS_TRACER(c_p)); if (! erts_trace_flags(esp[-1], &d_flags, &tracer, &cputs) || ! erts_trace_flags(esp[-2], &e_flags, &tracer, &cputs) || cputs ) { (--esp)[-1] = FAIL_TERM; + ERTS_TRACER_CLEAR(&tracer); break; } erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); (--esp)[-1] = set_match_trace(c_p, FAIL_TERM, tracer, d_flags, e_flags); erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); + ERTS_TRACER_CLEAR(&tracer); } break; case matchTrace3: { /* disable enable */ Uint d_flags = 0, e_flags = 0; /* process trace flags */ - Eterm tracer = ERTS_TRACER_PROC(c_p); + ErtsTracer tracer = erts_tracer_nil; /* XXX Atomicity note. Not fully atomic. See above. * Above it could possibly be solved, but not here. */ int cputs = 0; Eterm tracee = (--esp)[0]; + + erts_tracer_update(&tracer, ERTS_TRACER(c_p)); if (! erts_trace_flags(esp[-1], &d_flags, &tracer, &cputs) || ! erts_trace_flags(esp[-2], &e_flags, &tracer, &cputs) || @@ -2470,6 +2452,7 @@ restart: ! (tmpp = get_proc(c_p, ERTS_PROC_LOCK_MAIN, tracee, ERTS_PROC_LOCKS_ALL))) { (--esp)[-1] = FAIL_TERM; + ERTS_TRACER_CLEAR(&tracer); break; } if (tmpp == c_p) { @@ -2483,6 +2466,7 @@ restart: erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL); erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); } + ERTS_TRACER_CLEAR(&tracer); } break; case matchCatch: /* Match success, now build result */ @@ -5084,7 +5068,7 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace) lint_res = db_match_set_lint(p, spec, DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE); mps = db_match_set_compile(p, spec, DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE); } - + if (mps == NULL) { hp = HAlloc(p,3); ret = TUPLE2(hp, am_error, lint_res); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 62417fa05c..f33ade27f3 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2233,9 +2233,7 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) n++; } #endif - ASSERT(is_nil(ERTS_TRACER_PROC(p)) || - is_internal_pid(ERTS_TRACER_PROC(p)) || - is_internal_port(ERTS_TRACER_PROC(p))); + ASSERT(IS_TRACER_VALID(ERTS_TRACER(p))); ASSERT(is_pid(follow_moved(p->group_leader, (Eterm) 0))); if (is_not_immed(p->group_leader)) { @@ -2905,7 +2903,7 @@ reply_gc_info(void *vgcirp) hpp = &hp; } - erts_queue_message(rp, &rp_locks, mp, msg, NIL); + erts_queue_message(rp, &rp_locks, mp, msg); if (gcirp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 1a621863bb..8e201d5711 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -1248,7 +1248,7 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs) ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = tmr->btm.bp; erts_queue_message(proc, &proc_locks, mp, - tmr->btm.message, NIL); + tmr->btm.message); erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_SEND); queued_message = 1; proc_locks &= ~ERTS_PROC_LOCKS_MSG_SEND; @@ -1980,7 +1980,7 @@ access_sched_local_btm(Process *c_p, Eterm pid, ERTS_HLT_ASSERT(hp + (async ? 4 : 3) == hp_end); - erts_queue_message(proc, &proc_locks, mp, msg, NIL); + erts_queue_message(proc, &proc_locks, mp, msg); if (c_p) proc_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -2111,7 +2111,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp, msg = TUPLE3(hp, tag, tref, res); - erts_queue_message(c_p, &proc_locks, mp, msg, NIL); + erts_queue_message(c_p, &proc_locks, mp, msg); proc_locks &= ~ERTS_PROC_LOCK_MAIN; if (proc_locks) diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 9231fb1b34..39c0617143 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -97,6 +97,7 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "dist_entry_links", "address" }, { "code_write_permission", NULL }, { "proc_status", "pid" }, + { "proc_trace", "pid" }, { "ports_snapshot", NULL }, { "meta_name_tab", "address" }, { "meta_main_tab_slot", "address" }, @@ -148,6 +149,7 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "dist_entry_out_queue", "address" }, { "port_sched_lock", "port_id" }, { "sys_msg_q", NULL }, + { "tracer_mtx", NULL }, { "port_table", NULL }, #endif { "mtrace_op", NULL }, @@ -160,9 +162,6 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "proclist_pre_alloc_lock", "address" }, { "xports_list_pre_alloc_lock", "address" }, { "inet_buffer_stack_lock", NULL }, - { "gc_info", NULL }, - { "io_wake", NULL }, - { "timer_wheel", NULL }, { "system_block", NULL }, { "timeofday", NULL }, { "get_time", NULL }, diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 7596747b91..1cb02ec274 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -324,7 +324,7 @@ erts_queue_dist_message(Process *rcvr, tok_label, tok_lastcnt, tok_serial); } #endif - erts_queue_message(rcvr, rcvr_locks, mp, msg, token); + erts_queue_message(rcvr, rcvr_locks, mp, msg); } } else { @@ -349,7 +349,7 @@ erts_queue_dist_message(Process *rcvr, } #endif - LINK_MESSAGE(rcvr, mp); + LINK_MESSAGE(rcvr, mp, &mp->next, 1); if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ); @@ -364,31 +364,34 @@ erts_queue_dist_message(Process *rcvr, } } -/* Add a message last in message queue */ +/* Add messages last in message queue */ static Sint -queue_message(Process *c_p, - Process* receiver, - erts_aint32_t *receiver_state, - ErtsProcLocks *receiver_locks, - ErtsMessage* mp, - Eterm message, - Eterm seq_trace_token -#ifdef USE_VM_PROBES - , Eterm dt_utag -#endif - ) +queue_messages(Process *c_p, + Process* receiver, + erts_aint32_t *receiver_state, + ErtsProcLocks *receiver_locks, + ErtsMessage* first, + ErtsMessage** last, + Uint len) { Sint res; int locked_msgq = 0; erts_aint32_t state; - ERTS_SMP_LC_ASSERT(*receiver_locks == erts_proc_lc_my_proc_locks(receiver)); + ASSERT(is_value(ERL_MESSAGE_TERM(first))); + ASSERT(ERL_MESSAGE_TOKEN(first) == am_undefined || + ERL_MESSAGE_TOKEN(first) == NIL || + is_tuple(ERL_MESSAGE_TOKEN(first))); #ifdef ERTS_SMP +#ifdef ERTS_ENABLE_LOCK_CHECK + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(receiver) < ERTS_PROC_LOCK_MSGQ || + *receiver_locks == erts_proc_lc_my_proc_locks(receiver)); +#endif if (!(*receiver_locks & ERTS_PROC_LOCK_MSGQ)) { if (erts_smp_proc_trylock(receiver, ERTS_PROC_LOCK_MSGQ) == EBUSY) { - ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ; + ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ; if (receiver_state) state = *receiver_state; @@ -417,16 +420,10 @@ queue_message(Process *c_p, /* Drop message if receiver is exiting or has a pending exit... */ if (locked_msgq) erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ); - erts_cleanup_messages(mp); + erts_cleanup_messages(first); return 0; } - ERL_MESSAGE_TERM(mp) = message; - ERL_MESSAGE_TOKEN(mp) = seq_trace_token; -#ifdef USE_VM_PROBES - ERL_MESSAGE_DT_UTAG(mp) = dt_utag; -#endif - res = receiver->msg.len; #ifdef ERTS_SMP if (*receiver_locks & ERTS_PROC_LOCK_MAIN) { @@ -440,75 +437,83 @@ queue_message(Process *c_p, */ res += receiver->msg_inq.len; ERTS_SMP_MSGQ_MV_INQ2PRIVQ(receiver); - LINK_MESSAGE_PRIVQ(receiver, mp); + LINK_MESSAGE_PRIVQ(receiver, first, last, len); } else #endif { - LINK_MESSAGE(receiver, mp); + LINK_MESSAGE(receiver, first, last, len); } + if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) { + ErtsMessage *msg = first; + #ifdef USE_VM_PROBES - if (DTRACE_ENABLED(message_queued)) { - DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); - Sint tok_label = 0; - Sint tok_lastcnt = 0; - Sint tok_serial = 0; - - dtrace_proc_str(receiver, receiver_name); - if (seq_trace_token != NIL && is_tuple(seq_trace_token)) { - tok_label = signed_val(SEQ_TRACE_T_LABEL(seq_trace_token)); - tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token)); - tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token)); + if (DTRACE_ENABLED(message_queued)) { + DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + Eterm seq_trace_token = ERL_MESSAGE_TOKEN(msg); + + dtrace_proc_str(receiver, receiver_name); + if (seq_trace_token != NIL && is_tuple(seq_trace_token)) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(seq_trace_token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token)); + } + DTRACE6(message_queued, + receiver_name, size_object(ERL_MESSAGE_TERM(msg)), + receiver->msg.len, + tok_label, tok_lastcnt, tok_serial); } - DTRACE6(message_queued, - receiver_name, size_object(message), receiver->msg.len, - tok_label, tok_lastcnt, tok_serial); - } #endif - if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) - trace_receive(receiver, message); + while (msg) { + trace_receive(receiver, ERL_MESSAGE_TERM(msg)); + msg = msg->next; + } + + } if (locked_msgq) erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ); - erts_proc_notify_new_message(receiver, #ifdef ERTS_SMP - *receiver_locks + erts_proc_notify_new_message(receiver, *receiver_locks); #else - 0 -#endif - ); - -#ifndef ERTS_SMP + erts_proc_notify_new_message(receiver, 0); ERTS_HOLE_CHECK(receiver); #endif return res; } -void -#ifdef USE_VM_PROBES -erts_queue_message_probe(Process* receiver, ErtsProcLocks *receiver_locks, - ErtsMessage* mp, - Eterm message, Eterm seq_trace_token, Eterm dt_utag) -#else +static Sint +queue_message(Process *c_p, + Process* receiver, + erts_aint32_t *receiver_state, + ErtsProcLocks *receiver_locks, + ErtsMessage* mp, Eterm msg) +{ + ERL_MESSAGE_TERM(mp) = msg; + return queue_messages(c_p, receiver, receiver_state, receiver_locks, + mp, &mp->next, 1 ); +} + +Sint erts_queue_message(Process* receiver, ErtsProcLocks *receiver_locks, - ErtsMessage* mp, - Eterm message, Eterm seq_trace_token) -#endif + ErtsMessage* mp, Eterm msg) { - queue_message(NULL, - receiver, - NULL, - receiver_locks, - mp, - message, - seq_trace_token -#ifdef USE_VM_PROBES - , dt_utag -#endif - ); + return queue_message(NULL, receiver, NULL, receiver_locks, mp, msg); +} + + +Sint +erts_queue_messages(Process* receiver, ErtsProcLocks *receiver_locks, + ErtsMessage* first, ErtsMessage** last, Uint len) +{ + return queue_messages(NULL, receiver, NULL, receiver_locks, + first, last, len); } void @@ -821,17 +826,15 @@ erts_send_message(Process* sender, #endif } + ERL_MESSAGE_TOKEN(mp) = token; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = utag; +#endif res = queue_message(sender, receiver, &receiver_state, receiver_locks, - mp, - message, - token -#ifdef USE_VM_PROBES - , utag -#endif - ); + mp, message); BM_SWAP_TIMER(send,system); @@ -887,7 +890,8 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, /* the trace token must in this case be updated by the caller */ seq_trace_output(token, save, SEQ_TRACE_SEND, to->common.id, NULL); temptoken = copy_struct(token, sz_token, &hp, ohp); - erts_queue_message(to, to_locksp, mp, save, temptoken); + ERL_MESSAGE_TOKEN(mp) = temptoken; + erts_queue_message(to, to_locksp, mp, save); } else { sz_from = IS_CONST(from) ? 0 : size_object(from); #ifdef SHCOPY_SEND @@ -909,7 +913,7 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, ? from : copy_struct(from, sz_from, &hp, ohp)); save = TUPLE3(hp, am_EXIT, from_copy, mess); - erts_queue_message(to, to_locksp, mp, save, NIL); + erts_queue_message(to, to_locksp, mp, save); } } diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index ad4e65274c..608cf552a2 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -182,48 +182,64 @@ typedef struct { Sint len; /* queue length */ } ErlMessageInQueue; +typedef struct erl_trace_message_queue__ { + struct erl_trace_message_queue__ *next; /* point to the next receiver */ + Eterm receiver; + ErtsMessage* first; + ErtsMessage** last; /* point to the last next pointer */ + Sint len; /* queue length */ +} ErlTraceMessageQueue; + #endif /* Get "current" message */ #define PEEK_MESSAGE(p) (*(p)->msg.save) +#ifdef USE_VM_PROBES +#define LINK_MESSAGE_DTAG(mp, dt) ERL_MESSAGE_DT_UTAG(mp) = dt +#else +#define LINK_MESSAGE_DTAG(mp, dt) +#endif -/* Add message last in private message queue */ -#define LINK_MESSAGE_PRIVQ(p, mp) do { \ - *(p)->msg.last = (mp); \ - (p)->msg.last = &(mp)->next; \ - (p)->msg.len++; \ -} while (0) - +#define LINK_MESSAGE_IMPL(p, first_msg, last_msg, num_msgs, where) do { \ + *(p)->where.last = (first_msg); \ + (p)->where.last = (last_msg); \ + (p)->where.len += (num_msgs); \ + } while(0) #ifdef ERTS_SMP -/* Move in message queue to end of private message queue */ -#define ERTS_SMP_MSGQ_MV_INQ2PRIVQ(P) \ -do { \ - if ((P)->msg_inq.first) { \ - *(P)->msg.last = (P)->msg_inq.first; \ - (P)->msg.last = (P)->msg_inq.last; \ - (P)->msg.len += (P)->msg_inq.len; \ - (P)->msg_inq.first = NULL; \ - (P)->msg_inq.last = &(P)->msg_inq.first; \ - (P)->msg_inq.len = 0; \ - } \ -} while (0) - -/* Add message last in message queue */ -#define LINK_MESSAGE(p, mp) do { \ - *(p)->msg_inq.last = (mp); \ - (p)->msg_inq.last = &(mp)->next; \ - (p)->msg_inq.len++; \ -} while(0) +/* Add message last in private message queue */ +#define LINK_MESSAGE_PRIVQ(p, first_msg, last_msg, len) \ + do { \ + LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg); \ + } while (0) + +/* Add message last_msg in message queue */ +#define LINK_MESSAGE(p, first_msg, last_msg, len) \ + LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg_inq) + +#define ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p) \ + do { \ + if (p->msg_inq.first) { \ + *p->msg.last = p->msg_inq.first; \ + p->msg.last = p->msg_inq.last; \ + p->msg.len += p->msg_inq.len; \ + p->msg_inq.first = NULL; \ + p->msg_inq.last = &p->msg_inq.first; \ + p->msg_inq.len = 0; \ + } \ + } while (0) #else -#define ERTS_SMP_MSGQ_MV_INQ2PRIVQ(P) +#define ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p) -/* Add message last in message queue */ -#define LINK_MESSAGE(p, mp) LINK_MESSAGE_PRIVQ((p), (mp)) +/* Add message last_msg in message queue */ +#define LINK_MESSAGE(p, first_msg, last_msg, len) \ + do { \ + LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg); \ + } while(0) #endif @@ -259,23 +275,30 @@ do { \ (HEAP_FRAG_P)->off_heap.overhead = 0; \ } while (0) +#ifdef USE_VM_PROBES +#define ERL_MESSAGE_DT_UTAG_INIT(MP) ERL_MESSAGE_DT_UTAG(MP) = NIL +#else +#define ERL_MESSAGE_DT_UTAG_INIT(MP) do{ } while (0) +#endif + +#define ERTS_INIT_MESSAGE(MP) \ + do { \ + (MP)->next = NULL; \ + ERL_MESSAGE_TERM(MP) = THE_NON_VALUE; \ + ERL_MESSAGE_TOKEN(MP) = NIL; \ + ERL_MESSAGE_DT_UTAG_INIT(MP); \ + MP->data.attached = NULL; \ + } while (0) + void init_message(void); ErlHeapFragment* new_message_buffer(Uint); ErlHeapFragment* erts_resize_message_buffer(ErlHeapFragment *, Uint, Eterm *, Uint); void free_message_buffer(ErlHeapFragment *); void erts_queue_dist_message(Process*, ErtsProcLocks*, ErtsDistExternal *, Eterm); -#ifdef USE_VM_PROBES -void erts_queue_message_probe(Process*, ErtsProcLocks*, ErtsMessage*, - Eterm message, Eterm seq_trace_token, Eterm dt_utag); -#define erts_queue_message(RP,RL,BP,Msg,SEQ) \ - erts_queue_message_probe((RP),(RL),(BP),(Msg),(SEQ),NIL) -#else -void erts_queue_message(Process*, ErtsProcLocks*, ErtsMessage*, - Eterm message, Eterm seq_trace_token); -#define erts_queue_message_probe(RP,RL,BP,Msg,SEQ,TAG) \ - erts_queue_message((RP),(RL),(BP),(Msg),(SEQ)) -#endif +Sint erts_queue_message(Process*, ErtsProcLocks*,ErtsMessage*, Eterm); +Sint erts_queue_messages(Process*, ErtsProcLocks*, + ErtsMessage*, ErtsMessage**, Uint); void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm); Sint erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned); void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp); @@ -354,9 +377,7 @@ ERTS_GLB_FORCE_INLINE ErtsMessage *erts_alloc_message(Uint sz, Eterm **hpp) if (sz == 0) { mp = erts_alloc_message_ref(); - mp->next = NULL; - ERL_MESSAGE_TERM(mp) = NIL; - mp->data.attached = NULL; + ERTS_INIT_MESSAGE(mp); if (hpp) *hpp = NULL; return mp; @@ -365,8 +386,7 @@ ERTS_GLB_FORCE_INLINE ErtsMessage *erts_alloc_message(Uint sz, Eterm **hpp) mp = erts_alloc(ERTS_ALC_T_MSG, sizeof(ErtsMessage) + (sz - 1)*sizeof(Eterm)); - mp->next = NULL; - ERL_MESSAGE_TERM(mp) = NIL; + ERTS_INIT_MESSAGE(mp); mp->data.attached = ERTS_MSG_COMBINED_HFRAG; ERTS_INIT_HEAP_FRAG(&mp->hfrag, sz, sz); diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c index 71e3fd8b6e..d0f305900a 100644 --- a/erts/emulator/beam/erl_msacc.c +++ b/erts/emulator/beam/erl_msacc.c @@ -257,7 +257,7 @@ static void send_reply(ErtsMsAcc *msacc, ErtsMSAccReq *msaccrp) { if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx); - erts_queue_message(rp, &rp_locks, msgp, msg, NIL); + erts_queue_message(rp, &rp_locks, msgp, msg); if (esdp && msaccrp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 61dba4ef82..f35c08450c 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -130,17 +130,7 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif) env->fpe_was_unmasked = erts_block_fpe(); env->tmp_obj_list = NULL; env->exception_thrown = 0; -} - -static void pre_nif_noproc(ErlNifEnv* env, struct erl_module_nif* mod_nif) -{ - env->mod_nif = mod_nif; - env->proc = NULL; - env->hp = NULL; - env->hp_end = NULL; - env->heap_frag = NULL; - env->fpe_was_unmasked = erts_block_fpe(); - env->tmp_obj_list = NULL; + env->tracee = NULL; } /* Temporary object header, auto-deallocated when NIF returns @@ -180,13 +170,6 @@ void erts_post_nif(ErlNifEnv* env) free_tmp_objs(env); } -static void post_nif_noproc(ErlNifEnv* env) -{ - erts_unblock_fpe(env->fpe_was_unmasked); - free_tmp_objs(env); -} - - /* Flush out our cached heap pointers to allow an ordinary HAlloc */ static void flush_env(ErlNifEnv* env) @@ -247,18 +230,20 @@ struct enif_msg_environment_t Process phony_proc; }; -ErlNifEnv* enif_alloc_env(void) +static ERTS_INLINE void +setup_nif_env(struct enif_msg_environment_t* msg_env, + struct erl_module_nif* mod) { - struct enif_msg_environment_t* msg_env = - erts_alloc_fnf(ERTS_ALC_T_NIF, sizeof(struct enif_msg_environment_t)); Eterm* phony_heap = (Eterm*) msg_env; /* dummy non-NULL ptr */ - - msg_env->env.hp = phony_heap; + + msg_env->env.hp = phony_heap; msg_env->env.hp_end = phony_heap; msg_env->env.heap_frag = NULL; - msg_env->env.mod_nif = NULL; + msg_env->env.mod_nif = mod; msg_env->env.tmp_obj_list = NULL; + msg_env->env.fpe_was_unmasked = erts_block_fpe(); msg_env->env.proc = &msg_env->phony_proc; + msg_env->env.exception_thrown = 0; memset(&msg_env->phony_proc, 0, sizeof(Process)); HEAP_START(&msg_env->phony_proc) = phony_heap; HEAP_TOP(&msg_env->phony_proc) = phony_heap; @@ -270,6 +255,13 @@ ErlNifEnv* enif_alloc_env(void) msg_env->phony_proc.space_verified = 0; msg_env->phony_proc.space_verified_from = NULL; #endif +} + +ErlNifEnv* enif_alloc_env(void) +{ + struct enif_msg_environment_t* msg_env = + erts_alloc_fnf(ERTS_ALC_T_NIF, sizeof(struct enif_msg_environment_t)); + setup_nif_env(msg_env, NULL); return &msg_env->env; } void enif_free_env(ErlNifEnv* env) @@ -302,24 +294,114 @@ void enif_clear_env(ErlNifEnv* env) menv->env.hp = menv->env.hp_end = HEAP_TOP(p); ASSERT(!is_offheap(&MSO(p))); + erts_unblock_fpe(env->fpe_was_unmasked); free_tmp_objs(env); } -int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, - ErlNifEnv* msg_env, ERL_NIF_TERM msg) + +#ifdef ERTS_SMP +#ifdef DEBUG +static int enif_send_delay = 0; +#define ERTS_FORCE_ENIF_SEND_DELAY() (enif_send_delay++ % 2 == 0) +#else +#ifdef ERTS_PROC_LOCK_OWN_IMPL +#define ERTS_FORCE_ENIF_SEND_DELAY() 0 +#else +/* + * We always schedule messages if we do not use our own + * process lock implementation, as if we try to do a trylock on + * a lock that might already be locked by the same thread. + * And what happens then with different mutex implementations + * is not always guaranteed. + */ +#define ERTS_FORCE_ENIF_SEND_DELAY() 1 +#endif +#endif + +int erts_flush_trace_messages(Process *c_p, ErtsProcLocks c_p_locks) +{ + ErlTraceMessageQueue *msgq, **last_msgq; + int reds = 0; + + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_TRACE); + + msgq = c_p->trace_msg_q; + + if (!msgq) + goto error; + + do { + Process* rp; + ErtsProcLocks rp_locks; + ErtsMessage *first, **last; + Uint len; + + first = msgq->first; + last = msgq->last; + len = msgq->len; + msgq->first = NULL; + msgq->last = &msgq->first; + msgq->len = 0; + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_TRACE); + + ASSERT(len != 0); + + rp = erts_proc_lookup(msgq->receiver); + if (rp) { + rp_locks = 0; + if (rp->common.id == c_p->common.id) + rp_locks = c_p_locks; + erts_queue_messages(rp, &rp_locks, first, last, len); + if (rp->common.id == c_p->common.id) + rp_locks &= ~c_p_locks; + if (rp_locks) + erts_smp_proc_unlock(rp, rp_locks); + reds += len; + } else { + erts_cleanup_messages(first); + } + reds += 1; + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_TRACE); + msgq = msgq->next; + } while (msgq); + + last_msgq = &c_p->trace_msg_q; + + while (*last_msgq) { + msgq = *last_msgq; + if (msgq->len == 0) { + *last_msgq = msgq->next; + erts_free(ERTS_ALC_T_TRACE_MSG_QUEUE, msgq); + } else { + last_msgq = &msgq->next; + } + } + +error: + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_TRACE); + + return reds; +} + +#endif + +int +enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, + ErlNifEnv* msg_env, ERL_NIF_TERM msg) { struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)msg_env; - ErtsProcLocks rp_locks = 0; + ErtsProcLocks rp_locks = 0, lc_locks = 0, c_p_locks = ERTS_PROC_LOCK_MAIN; Process* rp; Process* c_p; ErtsMessage *mp; Eterm receiver = to_pid->pid; int flush_me = 0; - int scheduler = erts_get_scheduler_id() != 0; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + int scheduler = esdp ? esdp->no : 0; if (env != NULL) { c_p = env->proc; if (receiver == c_p->common.id) { - rp_locks = ERTS_PROC_LOCK_MAIN; + rp_locks = c_p_locks; flush_me = 1; } } @@ -329,7 +411,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, #else erts_exit(ERTS_ABORT_EXIT,"enif_send: env==NULL on non-SMP VM"); #endif - } + } rp = (scheduler ? erts_proc_lookup(receiver) @@ -353,7 +435,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, menv->env.heap_frag = NULL; MBUF(&menv->phony_proc) = NULL; } - ASSERT(!is_offheap(&MSO(&menv->phony_proc))); } else { Uint sz = size_object(msg); Eterm *hp; @@ -362,14 +443,83 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ASSERT(hp == mp->hfrag.mem+mp->hfrag.used_size); } - if (flush_me) { - flush_env(env); /* Needed for ERTS_HOLE_CHECK */ + ERL_MESSAGE_TERM(mp) = msg; + + if (flush_me) { + flush_env(env); /* Needed for ERTS_HOLE_CHECK */ + } + + if (!env || !env->tracee) { + erts_queue_message(rp, &rp_locks, mp, msg); + } +#ifdef ERTS_SMP + else { + /* This clause is taken when the nif is called in the context + of a traced process. We do not know which locks we have + so we have to do a try lock and if that fails we en queue + the message in a special trace message output queue of the + tracee */ + ErlTraceMessageQueue *msgq; + Process *t_p = env->tracee; + + + erts_smp_proc_lock(t_p, ERTS_PROC_LOCK_TRACE); + + msgq = t_p->trace_msg_q; + + while (msgq != NULL) { + if (msgq->receiver == receiver) { + break; + } + msgq = msgq->next; + } + +#ifdef ERTS_ENABLE_LOCK_CHECK + lc_locks = erts_proc_lc_my_proc_locks(rp); + rp_locks |= lc_locks; + if (receiver == c_p->common.id) + c_p_locks |= lc_locks; +#endif + if (ERTS_FORCE_ENIF_SEND_DELAY() || msgq || + rp_locks & ERTS_PROC_LOCK_MSGQ || + erts_smp_proc_trylock(rp, ERTS_PROC_LOCK_MSGQ) == EBUSY) { + + if (!msgq) { + + msgq = erts_alloc(ERTS_ALC_T_TRACE_MSG_QUEUE, + sizeof(ErlTraceMessageQueue)); + msgq->receiver = receiver; + msgq->first = mp; + msgq->last = &mp->next; + msgq->len = 1; + + /* Insert in linked list */ + msgq->next = t_p->trace_msg_q; + t_p->trace_msg_q = msgq; + + erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE); + + erts_schedule_flush_trace_messages(t_p->common.id); + + } else { + msgq->len++; + *msgq->last = mp; + msgq->last = &mp->next; + erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE); + } + } else { + erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE); + rp_locks &= ~ERTS_PROC_LOCK_TRACE; + rp_locks |= ERTS_PROC_LOCK_MSGQ; + erts_queue_message(rp, &rp_locks, mp, msg); + } } - erts_queue_message(rp, &rp_locks, mp, msg, am_undefined); +#endif + if (c_p == rp) rp_locks &= ~ERTS_PROC_LOCK_MAIN; - if (rp_locks) - erts_smp_proc_unlock(rp, rp_locks); + if (rp_locks & ~lc_locks) + erts_smp_proc_unlock(rp, rp_locks & ~lc_locks); if (!scheduler) erts_proc_dec_refc(rp); if (flush_me) { @@ -1485,10 +1635,10 @@ static void close_lib(struct erl_module_nif* lib) ASSERT(erts_refc_read(&lib->rt_dtor_cnt,0) == 0); if (lib->entry != NULL && lib->entry->unload != NULL) { - ErlNifEnv env; - pre_nif_noproc(&env, lib); - lib->entry->unload(&env, lib->priv_data); - post_nif_noproc(&env); + struct enif_msg_environment_t msg_env; + setup_nif_env(&msg_env, lib); + lib->entry->unload(&msg_env.env, lib->priv_data); + enif_clear_env(&msg_env.env); } if (!erts_is_static_nif(lib->handle)) erts_sys_ddll_close(lib->handle); @@ -1634,10 +1784,10 @@ static void nif_resource_dtor(Binary* bin) ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == &nif_resource_dtor); if (type->dtor != NULL) { - ErlNifEnv env; - pre_nif_noproc(&env, type->owner); - type->dtor(&env,resource->data); - post_nif_noproc(&env); + struct enif_msg_environment_t msg_env; + setup_nif_env(&msg_env, type->owner); + type->dtor(&msg_env.env, resource->data); + enif_clear_env(&msg_env.env); } if (erts_refc_dectest(&type->refc, 0) == 0) { ASSERT(type->next == NULL); @@ -2916,6 +3066,9 @@ erts_unload_nif(struct erl_module_nif* lib) ASSERT(erts_smp_thr_progress_is_blocking()); ASSERT(lib != NULL); ASSERT(lib->mod != NULL); + + erts_tracer_nif_clear(); + for (rt = resource_type_list.next; rt != &resource_type_list; rt = next) { @@ -2958,6 +3111,7 @@ void erl_nif_init() resource_type_list.owner = NULL; resource_type_list.module = THE_NON_VALUE; resource_type_list.name = THE_NON_VALUE; + } int erts_nif_get_funcs(struct erl_module_nif* mod, @@ -2967,7 +3121,8 @@ int erts_nif_get_funcs(struct erl_module_nif* mod, return mod->entry->num_of_funcs; } -Eterm erts_nif_call_function(Process *p, struct erl_module_nif* mod, +Eterm erts_nif_call_function(Process *p, Process *tracee, + struct erl_module_nif* mod, ErlNifFunc *fun, int argc, Eterm *argv) { Eterm nif_result; @@ -2979,13 +3134,14 @@ Eterm erts_nif_call_function(Process *p, struct erl_module_nif* mod, break; ASSERT(i < mod->entry->num_of_funcs); if (p) - ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) & ERTS_PROC_LOCK_MAIN); + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) & ERTS_PROC_LOCK_MAIN + || erts_smp_thr_progress_is_blocking()); #endif if (p) { struct enif_environment_t env; ASSERT(is_internal_pid(p->common.id)); erts_pre_nif(&env, p, mod); - env.may_delay_enif_send = 1; + env.tracee = tracee; nif_result = (*fun->fptr)(&env, argc, argv); if (env.exception_thrown) nif_result = THE_NON_VALUE; @@ -2995,7 +3151,7 @@ Eterm erts_nif_call_function(Process *p, struct erl_module_nif* mod, so we create a phony one. */ struct enif_msg_environment_t msg_env; setup_nif_env(&msg_env, mod); - msg_env.env.may_delay_enif_send = 1; + msg_env.env.tracee = tracee; nif_result = (*fun->fptr)(&msg_env.env, argc, argv); if (msg_env.env.exception_thrown) nif_result = THE_NON_VALUE; diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 33785ecaca..3964f7f679 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -87,10 +87,10 @@ typedef ErlNifSInt64 ErlNifTime; #define ERL_NIF_TIME_ERROR ((ErlNifSInt64) ERTS_NAPI_TIME_ERROR__) typedef enum { - ERL_NIF_SEC = ERTS_NAPI_SEC__, - ERL_NIF_MSEC = ERTS_NAPI_MSEC__, - ERL_NIF_USEC = ERTS_NAPI_USEC__, - ERL_NIF_NSEC = ERTS_NAPI_NSEC__ + ERL_NIF_SEC = ERTS_NAPI_SEC__, + ERL_NIF_MSEC = ERTS_NAPI_MSEC__, + ERL_NIF_USEC = ERTS_NAPI_USEC__, + ERL_NIF_NSEC = ERTS_NAPI_NSEC__ } ErlNifTimeUnit; struct enif_environment_t; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index d222e79f56..e7d0d127e2 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -444,7 +444,8 @@ int erts_system_profile_ts_type = ERTS_TRACE_FLG_NOW_TIMESTAMP; typedef enum { ERTS_PSTT_GC, /* Garbage Collect */ ERTS_PSTT_CPC, /* Check Process Code */ - ERTS_PSTT_COHMQ /* Change off heap message queue */ + ERTS_PSTT_COHMQ, /* Change off heap message queue */ + ERTS_PSTT_FTMQ /* Flush trace msg queue */ } ErtsProcSysTaskType; #define ERTS_MAX_PROC_SYS_TASK_ARGS 2 @@ -1138,7 +1139,7 @@ reply_sched_wall_time(void *vswtrp) hpp = &hp; } - erts_queue_message(rp, &rp_locks, mp, msg, NIL); + erts_queue_message(rp, &rp_locks, mp, msg); if (swtrp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -1217,7 +1218,7 @@ reply_system_check(void *vscrp) hpp = &hp; msg = STORE_NC(hpp, ohp, scrp->ref); - erts_queue_message(rp, &rp_locks, mp, msg, NIL); + erts_queue_message(rp, &rp_locks, mp, msg); if (scrp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -9315,6 +9316,7 @@ Process *schedule(Process *p, int calls) esdp = erts_scheduler_data; ASSERT(esdp->current_process == p); #endif + reds = actual_reds = calls - esdp->virtual_reds; if (reds < ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST) reds = ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST; @@ -9327,27 +9329,39 @@ Process *schedule(Process *p, int calls) p->reds += actual_reds; - erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); +#ifdef ERTS_SMP + erts_smp_proc_lock(p, ERTS_PROC_LOCK_TRACE); + if (p->trace_msg_q) { + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE); + erts_schedule_flush_trace_messages(p->common.id); + } else + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE); +#endif - state = erts_smp_atomic32_read_acqb(&p->state); + state = erts_smp_atomic32_read_nob(&p->state); if (IS_TRACED(p)) { if (IS_TRACED_FL(p, F_TRACE_CALLS) && !(state & ERTS_PSFLG_FREE)) erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_OUT); - if (state & (ERTS_PSFLG_FREE|ERTS_PSFLG_EXITING)) { + if ((state & (ERTS_PSFLG_FREE|ERTS_PSFLG_EXITING)) == ERTS_PSFLG_EXITING) { if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT)) - trace_sched(p, ((state & ERTS_PSFLG_FREE) - ? am_out_exited - : am_out_exiting)); + trace_sched(p, ERTS_PROC_LOCK_MAIN, + ((state & ERTS_PSFLG_FREE) + ? am_out_exited + : am_out_exiting)); } else { - if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED)) - trace_sched(p, am_out); - else if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_PROCS)) - trace_virtual_sched(p, am_out); + if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED) || + ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_PROCS)) + trace_sched(p, ERTS_PROC_LOCK_MAIN, am_out); } } + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + + /* have to re-read state after taking lock */ + state = erts_smp_atomic32_read_nob(&p->state); + #ifdef ERTS_SMP if (state & ERTS_PSFLG_PENDING_EXIT) erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN @@ -9844,27 +9858,27 @@ Process *schedule(Process *p, int calls) p->fcalls = reds; + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + if (IS_TRACED(p)) { if (state & ERTS_PSFLG_EXITING) { if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT)) - trace_sched(p, am_in_exiting); + trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in_exiting); } else { - if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED)) - trace_sched(p, am_in); - else if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_PROCS)) - trace_virtual_sched(p, am_in); + if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED) || + ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_PROCS)) + trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in); } if (IS_TRACED_FL(p, F_TRACE_CALLS)) { erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_IN); } } - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); #ifdef ERTS_SMP - if (is_not_nil(ERTS_TRACER_PROC(p))) - erts_check_my_tracer_proc(p); + /* Clears tracer if it has been removed */ + (void)ERTS_TRACER_PROC_IS_ENABLED(p); #endif if (state & ERTS_PSFLG_RUNNING_SYS) { @@ -9996,7 +10010,7 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result) ASSERT(hp_start + hsz == hp); #endif - erts_queue_message(rp, &rp_locks, mp, msg, NIL); + erts_queue_message(rp, &rp_locks, mp, msg); if (c_p == rp) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -10164,8 +10178,7 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) { int garbage_collected = 0; erts_aint32_t state = *statep; - int max_reds = in_reds; - int reds = 0; + int reds = in_reds; int qmask = 0; ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN); @@ -10193,12 +10206,12 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) if (c_p->flags & F_DISABLE_GC) { save_gc_task(c_p, st, st_prio); st = NULL; - reds++; + reds--; } else { if (!garbage_collected) { FLAGS(c_p) |= F_NEED_FULLSWEEP; - reds += erts_garbage_collect_nobump(c_p, + reds -= erts_garbage_collect_nobump(c_p, 0, c_p->arg_reg, c_p->arity); @@ -10207,21 +10220,30 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) st_res = am_true; } break; - case ERTS_PSTT_CPC: + case ERTS_PSTT_CPC: { + int cpc_reds = 0; st_res = erts_check_process_code(c_p, st->arg[0], unsigned_val(st->arg[1]), - &reds); + &cpc_reds); + reds -= cpc_reds; if (is_non_value(st_res)) { /* Needed gc, but gc was disabled */ save_gc_task(c_p, st, st_prio); st = NULL; } break; + } case ERTS_PSTT_COHMQ: - reds += erts_complete_off_heap_message_queue_change(c_p); + reds -= erts_complete_off_heap_message_queue_change(c_p); st_res = am_true; break; +#ifdef ERTS_SMP + case ERTS_PSTT_FTMQ: + reds -= erts_flush_trace_messages(c_p, ERTS_PROC_LOCK_MAIN); + st_res = am_true; + break; +#endif default: ERTS_INTERNAL_ERROR("Invalid process sys task type"); st_res = am_false; @@ -10231,11 +10253,11 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) reds += notify_sys_task_executed(c_p, st, st_res); state = erts_smp_atomic32_read_acqb(&c_p->state); - } while (qmask && reds < max_reds); + } while (qmask && reds > 0); *statep = state; - return reds; + return in_reds - reds; } static int @@ -10267,6 +10289,12 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds) case ERTS_PSTT_COHMQ: st_res = am_false; break; +#ifdef ERTS_SMP + case ERTS_PSTT_FTMQ: + reds -= erts_flush_trace_messages(c_p, ERTS_PROC_LOCK_MAIN); + st_res = am_true; + break; +#endif default: ERTS_INTERNAL_ERROR("Invalid process sys task type"); st_res = am_false; @@ -10402,8 +10430,8 @@ badarg: BIF_ERROR(BIF_P, BADARG); } -void -erts_schedule_complete_off_heap_message_queue_change(Eterm pid) +static void +erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type) { Process *rp = erts_proc_lookup(pid); if (rp) { @@ -10413,7 +10441,7 @@ erts_schedule_complete_off_heap_message_queue_change(Eterm pid) st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK, ERTS_PROC_SYS_TASK_SIZE(0)); - st->type = ERTS_PSTT_COHMQ; + st->type = type; st->requester = NIL; st->reply_tag = NIL; st->req_id = NIL; @@ -10428,6 +10456,18 @@ erts_schedule_complete_off_heap_message_queue_change(Eterm pid) } } +void +erts_schedule_complete_off_heap_message_queue_change(Eterm pid) +{ + erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ); +} + +void +erts_schedule_flush_trace_messages(Eterm pid) +{ + erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ); +} + static void save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio) { @@ -10886,6 +10926,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). Eterm res = THE_NON_VALUE; erts_aint32_t state = 0; erts_aint32_t prio = (erts_aint32_t) PRIORITY_NORMAL; + ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL; #ifdef SHCOPY_SPAWN erts_shcopy_t info; INITIALIZE_SHCOPY(info); @@ -11062,7 +11103,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). : STORE_NC(&p->htop, &p->off_heap, parent->group_leader); } - erts_get_default_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER_PROC(p)); + erts_get_default_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER(p)); p->msg.first = NULL; p->msg.last = &p->msg.first; @@ -11098,21 +11139,47 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->last_old_htop = NULL; #endif +#ifdef ERTS_SMP + p->trace_msg_q = NULL; + p->scheduler_data = NULL; + p->suspendee = NIL; + p->pending_suspenders = NULL; + p->pending_exit.reason = THE_NON_VALUE; + p->pending_exit.bp = NULL; +#endif + +#if !defined(NO_FPE_SIGNALS) || defined(HIPE) + p->fp_exception = 0; +#endif + if (IS_TRACED(parent)) { if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOS) { ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent) & TRACEE_FLAGS); - ERTS_TRACER_PROC(p) = ERTS_TRACER_PROC(parent); - } - if (ARE_TRACE_FLAGS_ON(parent, F_TRACE_PROCS)) { - trace_proc_spawn(parent, p->common.id, mod, func, args); + erts_tracer_replace(&p->common, ERTS_TRACER(parent)); } - if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOS1) { + if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOS1) { /* Overrides TRACE_CHILDREN */ ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent) & TRACEE_FLAGS); - ERTS_TRACER_PROC(p) = ERTS_TRACER_PROC(parent); + erts_tracer_replace(&p->common, ERTS_TRACER(parent)); ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOS1 | F_TRACE_SOS); ERTS_TRACE_FLAGS(parent) &= ~(F_TRACE_SOS1 | F_TRACE_SOS); } + if (so->flags & SPO_LINK && ERTS_TRACE_FLAGS(parent) & (F_TRACE_SOL|F_TRACE_SOL1)) { + ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent)&TRACEE_FLAGS); + erts_tracer_replace(&p->common, ERTS_TRACER(parent)); + if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOL1) {/*maybe override*/ + ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOL1 | F_TRACE_SOL); + ERTS_TRACE_FLAGS(parent) &= ~(F_TRACE_SOL1 | F_TRACE_SOL); + } + } + if (ARE_TRACE_FLAGS_ON(parent, F_TRACE_PROCS)) { + locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + erts_smp_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + trace_proc_spawn(parent, p->common.id, mod, func, args); + if (so->flags & SPO_LINK) + trace_proc(parent, locks, parent, am_link, p->common.id); + } } /* @@ -11123,10 +11190,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). #ifdef DEBUG int ret; #endif - if (IS_TRACED_FL(parent, F_TRACE_PROCS)) { - trace_proc(parent, parent, am_link, p->common.id); - } - #ifdef DEBUG ret = erts_add_link(&ERTS_P_LINKS(parent), LINK_PID, p->common.id); ASSERT(ret == 0); @@ -11137,17 +11200,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). erts_add_link(&ERTS_P_LINKS(p), LINK_PID, parent->common.id); #endif - if (IS_TRACED(parent)) { - if (ERTS_TRACE_FLAGS(parent) & (F_TRACE_SOL|F_TRACE_SOL1)) { - ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent)&TRACEE_FLAGS); - ERTS_TRACER_PROC(p) = ERTS_TRACER_PROC(parent); /*maybe steal*/ - - if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOL1) {/*maybe override*/ - ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOL1 | F_TRACE_SOL); - ERTS_TRACE_FLAGS(parent) &= ~(F_TRACE_SOL1 | F_TRACE_SOL); - } - } - } } /* @@ -11162,19 +11214,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). so->mref = mref; } -#ifdef ERTS_SMP - p->scheduler_data = NULL; - p->suspendee = NIL; - p->pending_suspenders = NULL; - p->pending_exit.reason = THE_NON_VALUE; - p->pending_exit.bp = NULL; -#endif - -#if !defined(NO_FPE_SIGNALS) || defined(HIPE) - p->fp_exception = 0; -#endif - - erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL); + erts_smp_proc_unlock(p, locks); res = p->common.id; @@ -11198,7 +11238,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). error: - erts_smp_proc_unlock(parent, ERTS_PROC_LOCKS_ALL_MINOR); + erts_smp_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR); return res; } @@ -11223,7 +11263,7 @@ void erts_init_empty_process(Process *p) p->rcount = 0; p->common.id = ERTS_INVALID_PID; p->reds = 0; - ERTS_TRACER_PROC(p) = NIL; + ERTS_TRACER(p) = erts_tracer_nil; ERTS_TRACE_FLAGS(p) = F_INITIAL_TRACE_FLAGS; p->group_leader = ERTS_INVALID_PID; p->flags = 0; @@ -11343,7 +11383,7 @@ erts_debug_verify_clean_empty_process(Process* p) ASSERT(p->live_hf_end == ERTS_INVALID_HFRAG_PTR); ASSERT(p->heap == NULL); ASSERT(p->common.id == ERTS_INVALID_PID); - ASSERT(ERTS_TRACER_PROC(p) == NIL); + ASSERT(ERTS_TRACER_IS_NIL(ERTS_TRACER(p))); ASSERT(ERTS_TRACE_FLAGS(p) == F_INITIAL_TRACE_FLAGS); ASSERT(p->group_leader == ERTS_INVALID_PID); ASSERT(p->next == NULL); @@ -11690,7 +11730,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp); mess = copy_struct(exit_term, term_size, &hp, ohp); #endif - erts_queue_message(to, to_locksp, mp, mess, NIL); + erts_queue_message(to, to_locksp, mp, mess); } else { Eterm temp_token; Uint sz_token; @@ -11708,9 +11748,10 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, mess = copy_struct(exit_term, term_size, &hp, ohp); #endif /* the trace token must in this case be updated by the caller */ - seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, NULL); + seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, to); temp_token = copy_struct(token, sz_token, &hp, ohp); - erts_queue_message(to, to_locksp, mp, mess, temp_token); + ERL_MESSAGE_TOKEN(mp) = temp_token; + erts_queue_message(to, to_locksp, mp, mess); } } @@ -11819,6 +11860,9 @@ send_exit_signal(Process *c_p, /* current process if and only if ((state & ERTS_PSFLG_TRAP_EXIT) && (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) { + /* have to release the status lock in order to send the exit message */ + erts_smp_proc_unlock(rp, *rp_locks & ERTS_PROC_LOCKS_XSIG_SEND); + *rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND; if (have_seqtrace(token) && token_update) seq_trace_update_send(token_update); if (is_value(exit_tuple)) @@ -12116,6 +12160,7 @@ static void doit_exit_link(ErtsLink *lnk, void *vpcontext) DistEntry *dep; Process *rp; + switch(lnk->type) { case LINK_PID: if(is_internal_port(item)) { @@ -12163,7 +12208,11 @@ static void doit_exit_link(ErtsLink *lnk, void *vpcontext) if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) { /* We didn't exit the process and it is traced */ if (IS_TRACED_FL(rp, F_TRACE_PROCS)) { - trace_proc(p, rp, am_getting_unlinked, p->common.id); + if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) { + erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND); + rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND; + } + trace_proc(NULL, 0, rp, am_getting_unlinked, p->common.id); } } } @@ -12280,8 +12329,6 @@ erts_do_exit_process(Process* p, Eterm reason) if (IS_TRACED_FL(p, F_TRACE_CALLS)) erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_EXITING); - if (IS_TRACED_FL(p,F_TRACE_PROCS)) - trace_proc(p, p, am_exit, reason); } erts_trace_check_exiting(p->common.id); @@ -12297,6 +12344,10 @@ erts_do_exit_process(Process* p, Eterm reason) erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR); + if (IS_TRACED_FL(p,F_TRACE_PROCS)) + trace_proc(p, ERTS_PROC_LOCK_MAIN, p, am_exit, reason); + + /* * p->u.initial of this process can *not* be used anymore; * will be overwritten by misc termination data. @@ -12436,6 +12487,9 @@ erts_continue_exit_process(Process *p) ASSERT(!p->common.u.alive.reg); } + if (IS_TRACED_FL(p, F_TRACE_SCHED_EXIT)) + trace_sched(p, curr_locks, am_out_exited); + erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); curr_locks = ERTS_PROC_LOCKS_ALL; @@ -12559,6 +12613,12 @@ erts_continue_exit_process(Process *p) if (nif_export) erts_destroy_nif_export(nif_export); +#ifdef ERTS_SMP + erts_flush_trace_messages(p, 0); +#endif + + ERTS_TRACER_CLEAR(&ERTS_TRACER(p)); + delete_process(p); #ifdef ERTS_SMP diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index c2303eea49..8261ae28f8 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1041,6 +1041,7 @@ struct process { #ifdef ERTS_SMP ErlMessageInQueue msg_inq; + ErlTraceMessageQueue *trace_msg_q; ErtsPendExit pending_exit; erts_proc_lock_t lock; ErtsSchedulerData *scheduler_data; @@ -1358,7 +1359,6 @@ extern int erts_system_profile_ts_type; #define F_TRACE_FLAG(N) (1 << (ERTS_TRACE_TS_TYPE_BITS + (N))) /* process trace_flags */ - #define F_NOW_TS (ERTS_TRACE_FLG_NOW_TIMESTAMP \ << ERTS_TRACE_FLAGS_TS_TYPE_SHIFT) #define F_STRICT_MON_TS (ERTS_TRACE_FLG_STRICT_MONOTONIC_TIMESTAMP \ @@ -1380,8 +1380,7 @@ extern int erts_system_profile_ts_type; #define F_TRACE_ARITY_ONLY F_TRACE_FLAG(12) #define F_TRACE_RETURN_TO F_TRACE_FLAG(13) /* Return_to trace when breakpoint tracing */ #define F_TRACE_SILENT F_TRACE_FLAG(14) /* No call trace msg suppress */ -#define F_TRACER F_TRACE_FLAG(15) /* May be (has been) tracer */ -#define F_EXCEPTION_TRACE F_TRACE_FLAG(16) /* May have exception trace on stack */ +#define F_EXCEPTION_TRACE F_TRACE_FLAG(15) /* May have exception trace on stack */ /* port trace flags, currently the same as process trace flags */ #define F_TRACE_SCHED_PORTS F_TRACE_FLAG(17) /* Trace of port scheduling */ @@ -1717,6 +1716,8 @@ void erts_schedule_thr_prgr_later_cleanup_op(void (*)(void *), ErtsThrPrgrLaterOp *, UWord); void erts_schedule_complete_off_heap_message_queue_change(Eterm pid); +void erts_schedule_flush_trace_messages(Eterm pid); +int erts_flush_trace_messages(Process *c_p, ErtsProcLocks locks); #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) int erts_dbg_check_halloc_lock(Process *p); @@ -1996,7 +1997,6 @@ erts_psd_set(Process *p, int ix, void *data) #define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, NTE) \ erts_psd_set((P), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE)) - ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p); ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p, Eterm handler); diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index e1f470d381..a69185bc5c 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -106,6 +106,7 @@ static struct { Sint16 proc_lock_msgq; Sint16 proc_lock_btm; Sint16 proc_lock_status; + Sint16 proc_lock_trace; } lc_id; #endif @@ -152,6 +153,7 @@ erts_init_proc_lock(int cpus) lc_id.proc_lock_msgq = erts_lc_get_lock_order_id("proc_msgq"); lc_id.proc_lock_btm = erts_lc_get_lock_order_id("proc_btm"); lc_id.proc_lock_status = erts_lc_get_lock_order_id("proc_status"); + lc_id.proc_lock_trace = erts_lc_get_lock_order_id("proc_trace"); #endif } @@ -1056,6 +1058,11 @@ erts_proc_lock_init(Process *p) ethr_mutex_lock(&p->lock.status.mtx); #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_trylock(1, &p->lock.status.lc); +#endif + erts_mtx_init_x(&p->lock.trace, "proc_trace", p->common.id, do_lock_count); + ethr_mutex_lock(&p->lock.trace.mtx); +#ifdef ERTS_ENABLE_LOCK_CHECK + erts_lc_trylock(1, &p->lock.trace.lc); #endif #endif #ifdef ERTS_PROC_LOCK_DEBUG @@ -1078,6 +1085,7 @@ erts_proc_lock_fin(Process *p) erts_mtx_destroy(&p->lock.msgq); erts_mtx_destroy(&p->lock.btm); erts_mtx_destroy(&p->lock.status); + erts_mtx_destroy(&p->lock.trace); #endif #if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP) erts_lcnt_proc_lock_destroy(p); @@ -1100,26 +1108,29 @@ void erts_lcnt_enable_proc_lock_count(int enable) { void erts_lcnt_proc_lock_init(Process *p) { if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) { erts_lcnt_init_lock_empty(&(p->lock.lcnt_main)); + erts_lcnt_init_lock_empty(&(p->lock.lcnt_link)); erts_lcnt_init_lock_empty(&(p->lock.lcnt_msgq)); erts_lcnt_init_lock_empty(&(p->lock.lcnt_btm)); - erts_lcnt_init_lock_empty(&(p->lock.lcnt_link)); erts_lcnt_init_lock_empty(&(p->lock.lcnt_status)); + erts_lcnt_init_lock_empty(&(p->lock.lcnt_trace)); } else { /* now the common case */ Eterm pid = (p->common.id != ERTS_INVALID_PID) ? p->common.id : NIL; erts_lcnt_init_lock_x(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK, pid); + erts_lcnt_init_lock_x(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK, pid); erts_lcnt_init_lock_x(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK, pid); erts_lcnt_init_lock_x(&(p->lock.lcnt_btm), "proc_btm", ERTS_LCNT_LT_PROCLOCK, pid); - erts_lcnt_init_lock_x(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK, pid); erts_lcnt_init_lock_x(&(p->lock.lcnt_status),"proc_status",ERTS_LCNT_LT_PROCLOCK, pid); + erts_lcnt_init_lock_x(&(p->lock.lcnt_trace), "proc_trace", ERTS_LCNT_LT_PROCLOCK, pid); } /* the lock names should really be aligned to four characters */ } /* logic reversed */ void erts_lcnt_proc_lock_destroy(Process *p) { erts_lcnt_destroy_lock(&(p->lock.lcnt_main)); + erts_lcnt_destroy_lock(&(p->lock.lcnt_link)); erts_lcnt_destroy_lock(&(p->lock.lcnt_msgq)); erts_lcnt_destroy_lock(&(p->lock.lcnt_btm)); - erts_lcnt_destroy_lock(&(p->lock.lcnt_link)); erts_lcnt_destroy_lock(&(p->lock.lcnt_status)); + erts_lcnt_destroy_lock(&(p->lock.lcnt_trace)); } static void lcnt_enable_proc_lock_count(Process *proc, int enable) { @@ -1138,10 +1149,11 @@ static void lcnt_enable_proc_lock_count(Process *proc, int enable) { void erts_lcnt_proc_lock(erts_proc_lock_t *lock, ErtsProcLocks locks) { if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return; if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_lock(&(lock->lcnt_main)); } + if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_lock(&(lock->lcnt_link)); } if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_lock(&(lock->lcnt_msgq)); } if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_lock(&(lock->lcnt_btm)); } - if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_lock(&(lock->lcnt_link)); } if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_lock(&(lock->lcnt_status)); } + if (locks & ERTS_PROC_LOCK_TRACE) { erts_lcnt_lock(&(lock->lcnt_trace)); } } void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks, @@ -1150,44 +1162,50 @@ void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks, if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_lock_post_x(&(lock->lcnt_main), file, line); } + if (locks & ERTS_PROC_LOCK_LINK) { + erts_lcnt_lock_post_x(&(lock->lcnt_link), file, line); + } if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_lock_post_x(&(lock->lcnt_msgq), file, line); } if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_lock_post_x(&(lock->lcnt_btm), file, line); } - if (locks & ERTS_PROC_LOCK_LINK) { - erts_lcnt_lock_post_x(&(lock->lcnt_link), file, line); - } if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_lock_post_x(&(lock->lcnt_status), file, line); } + if (locks & ERTS_PROC_LOCK_TRACE) { + erts_lcnt_lock_post_x(&(lock->lcnt_trace), file, line); + } } void erts_lcnt_proc_lock_unaquire(erts_proc_lock_t *lock, ErtsProcLocks locks) { if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return; if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_lock_unaquire(&(lock->lcnt_main)); } + if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_lock_unaquire(&(lock->lcnt_link)); } if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_lock_unaquire(&(lock->lcnt_msgq)); } if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_lock_unaquire(&(lock->lcnt_btm)); } - if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_lock_unaquire(&(lock->lcnt_link)); } if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_lock_unaquire(&(lock->lcnt_status)); } + if (locks & ERTS_PROC_LOCK_TRACE) { erts_lcnt_lock_unaquire(&(lock->lcnt_trace)); } } void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks) { if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return; if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_unlock(&(lock->lcnt_main)); } + if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_unlock(&(lock->lcnt_link)); } if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_unlock(&(lock->lcnt_msgq)); } if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_unlock(&(lock->lcnt_btm)); } - if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_unlock(&(lock->lcnt_link)); } if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_unlock(&(lock->lcnt_status)); } + if (locks & ERTS_PROC_LOCK_TRACE) { erts_lcnt_unlock(&(lock->lcnt_trace)); } } void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res) { if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return; if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_trylock(&(lock->lcnt_main), res); } + if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_trylock(&(lock->lcnt_link), res); } if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_trylock(&(lock->lcnt_msgq), res); } if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_trylock(&(lock->lcnt_btm), res); } - if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_trylock(&(lock->lcnt_link), res); } if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_trylock(&(lock->lcnt_status), res); } + if (locks & ERTS_PROC_LOCK_TRACE) { erts_lcnt_trylock(&(lock->lcnt_trace), res); } } /* reversed logic */ #endif /* ERTS_ENABLE_LOCK_COUNT */ @@ -1224,6 +1242,10 @@ erts_proc_lc_lock(Process *p, ErtsProcLocks locks, char *file, unsigned int line lck.id = lc_id.proc_lock_status; erts_lc_lock_x(&lck,file,line); } + if (locks & ERTS_PROC_LOCK_TRACE) { + lck.id = lc_id.proc_lock_trace; + erts_lc_lock_x(&lck,file,line); + } } void @@ -1253,6 +1275,10 @@ erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked, lck.id = lc_id.proc_lock_status; erts_lc_trylock_x(locked, &lck, file, line); } + if (locks & ERTS_PROC_LOCK_TRACE) { + lck.id = lc_id.proc_lock_trace; + erts_lc_trylock_x(locked, &lck, file, line); + } } void @@ -1261,6 +1287,10 @@ erts_proc_lc_unlock(Process *p, ErtsProcLocks locks) erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, p->common.id, ERTS_LC_FLG_LT_PROCLOCK); + if (locks & ERTS_PROC_LOCK_TRACE) { + lck.id = lc_id.proc_lock_trace; + erts_lc_unlock(&lck); + } if (locks & ERTS_PROC_LOCK_STATUS) { lck.id = lc_id.proc_lock_status; erts_lc_unlock(&lck); @@ -1292,6 +1322,10 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks) erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, p->common.id, ERTS_LC_FLG_LT_PROCLOCK); + if (locks & ERTS_PROC_LOCK_TRACE) { + lck.id = lc_id.proc_lock_trace; + erts_lc_might_unlock(&lck); + } if (locks & ERTS_PROC_LOCK_STATUS) { lck.id = lc_id.proc_lock_status; erts_lc_might_unlock(&lck); @@ -1323,6 +1357,8 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks) erts_lc_might_unlock(&p->lock.btm.lc); if (locks & ERTS_PROC_LOCK_STATUS) erts_lc_might_unlock(&p->lock.status.lc); + if (locks & ERTS_PROC_LOCK_TRACE) + erts_lc_might_unlock(&p->lock.trace.lc); #endif } @@ -1354,6 +1390,10 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file, lck.id = lc_id.proc_lock_status; erts_lc_require_lock(&lck, file, line); } + if (locks & ERTS_PROC_LOCK_TRACE) { + lck.id = lc_id.proc_lock_trace; + erts_lc_require_lock(&lck, file, line); + } #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL if (locks & ERTS_PROC_LOCK_MAIN) erts_lc_require_lock(&p->lock.main.lc, file, line); @@ -1365,6 +1405,8 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file, erts_lc_require_lock(&p->lock.btm.lc, file, line); if (locks & ERTS_PROC_LOCK_STATUS) erts_lc_require_lock(&p->lock.status.lc, file, line); + if (locks & ERTS_PROC_LOCK_TRACE) + erts_lc_require_lock(&p->lock.trace.lc, file, line); #endif } @@ -1375,6 +1417,10 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks) erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, p->common.id, ERTS_LC_FLG_LT_PROCLOCK); + if (locks & ERTS_PROC_LOCK_TRACE) { + lck.id = lc_id.proc_lock_trace; + erts_lc_unrequire_lock(&lck); + } if (locks & ERTS_PROC_LOCK_STATUS) { lck.id = lc_id.proc_lock_status; erts_lc_unrequire_lock(&lck); @@ -1406,6 +1452,8 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks) erts_lc_unrequire_lock(&p->lock.btm.lc); if (locks & ERTS_PROC_LOCK_STATUS) erts_lc_unrequire_lock(&p->lock.status.lc); + if (locks & ERTS_PROC_LOCK_TRACE) + erts_lc_unrequire_lock(&p->lock.trace.lc); #endif } @@ -1429,6 +1477,8 @@ erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks) lck.id = lc_id.proc_lock_btm; else if (locks & ERTS_PROC_LOCK_STATUS) lck.id = lc_id.proc_lock_status; + else if (locks & ERTS_PROC_LOCK_TRACE) + lck.id = lc_id.proc_lock_trace; else erts_lc_fail("Unknown proc lock found"); @@ -1441,14 +1491,7 @@ erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks) void erts_proc_lc_chk_only_proc_main(Process *p) { -#if ERTS_PROC_LOCK_OWN_IMPL - erts_lc_lock_t proc_main = ERTS_LC_LOCK_INIT(lc_id.proc_lock_main, - p->common.id, - ERTS_LC_FLG_LT_PROCLOCK); - erts_lc_check_exact(&proc_main, 1); -#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL - erts_lc_check_exact(&p->lock.main.lc, 1); -#endif + erts_proc_lc_chk_only_proc(p, ERTS_PROC_LOCK_MAIN); } #if ERTS_PROC_LOCK_OWN_IMPL @@ -1456,14 +1499,67 @@ void erts_proc_lc_chk_only_proc_main(Process *p) ERTS_LC_LOCK_INIT(-1, THE_NON_VALUE, ERTS_LC_FLG_LT_PROCLOCK) #endif /* ERTS_PROC_LOCK_OWN_IMPL */ +void erts_proc_lc_chk_only_proc(Process *p, ErtsProcLocks locks) +{ + int have_locks_len = 0; +#if ERTS_PROC_LOCK_OWN_IMPL + erts_lc_lock_t have_locks[6] = {ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT}; + if (locks & ERTS_PROC_LOCK_MAIN) { + have_locks[have_locks_len].id = lc_id.proc_lock_main; + have_locks[have_locks_len++].extra = p->common.id; + } + if (locks & ERTS_PROC_LOCK_LINK) { + have_locks[have_locks_len].id = lc_id.proc_lock_link; + have_locks[have_locks_len++].extra = p->common.id; + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + have_locks[have_locks_len].id = lc_id.proc_lock_msgq; + have_locks[have_locks_len++].extra = p->common.id; + } + if (locks & ERTS_PROC_LOCK_BTM) { + have_locks[have_locks_len].id = lc_id.proc_lock_btm; + have_locks[have_locks_len++].extra = p->common.id; + } + if (locks & ERTS_PROC_LOCK_STATUS) { + have_locks[have_locks_len].id = lc_id.proc_lock_status; + have_locks[have_locks_len++].extra = p->common.id; + } + if (locks & ERTS_PROC_LOCK_TRACE) { + have_locks[have_locks_len].id = lc_id.proc_lock_trace; + have_locks[have_locks_len++].extra = p->common.id; + } +#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL + erts_lc_lock_t have_locks[6]; + if (locks & ERTS_PROC_LOCK_MAIN) + have_locks[have_locks_len++] = p->lock.main.lc; + if (locks & ERTS_PROC_LOCK_LINK) + have_locks[have_locks_len++] = p->lock.link.lc; + if (locks & ERTS_PROC_LOCK_MSGQ) + have_locks[have_locks_len++] = p->lock.msgq.lc; + if (locks & ERTS_PROC_LOCK_BTM) + have_locks[have_locks_len++] = p->lock.btm.lc; + if (locks & ERTS_PROC_LOCK_STATUS) + have_locks[have_locks_len++] = p->lock.status.lc; + if (locks & ERTS_PROC_LOCK_TRACE) + have_locks[have_locks_len++] = p->lock.trace.lc; +#endif + erts_lc_check_exact(have_locks, have_locks_len); +} + void erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks) { int have_locks_len = 0; #if ERTS_PROC_LOCK_OWN_IMPL - erts_lc_lock_t have_locks[5] = {ERTS_PROC_LC_EMPTY_LOCK_INIT, + erts_lc_lock_t have_locks[6] = {ERTS_PROC_LC_EMPTY_LOCK_INIT, ERTS_PROC_LC_EMPTY_LOCK_INIT, ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, ERTS_PROC_LC_EMPTY_LOCK_INIT, ERTS_PROC_LC_EMPTY_LOCK_INIT}; if (locks & ERTS_PROC_LOCK_MAIN) { @@ -1486,8 +1582,12 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks) have_locks[have_locks_len].id = lc_id.proc_lock_status; have_locks[have_locks_len++].extra = p->common.id; } + if (locks & ERTS_PROC_LOCK_TRACE) { + have_locks[have_locks_len].id = lc_id.proc_lock_trace; + have_locks[have_locks_len++].extra = p->common.id; + } #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL - erts_lc_lock_t have_locks[5]; + erts_lc_lock_t have_locks[6]; if (locks & ERTS_PROC_LOCK_MAIN) have_locks[have_locks_len++] = p->lock.main.lc; if (locks & ERTS_PROC_LOCK_LINK) @@ -1498,6 +1598,8 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks) have_locks[have_locks_len++] = p->lock.btm.lc; if (locks & ERTS_PROC_LOCK_STATUS) have_locks[have_locks_len++] = p->lock.status.lc; + if (locks & ERTS_PROC_LOCK_TRACE) + have_locks[have_locks_len++] = p->lock.trace.lc; #endif erts_lc_check(have_locks, have_locks_len, NULL, 0); } @@ -1508,12 +1610,14 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks) int have_locks_len = 0; int have_not_locks_len = 0; #if ERTS_PROC_LOCK_OWN_IMPL - erts_lc_lock_t have_locks[5] = {ERTS_PROC_LC_EMPTY_LOCK_INIT, + erts_lc_lock_t have_locks[6] = {ERTS_PROC_LC_EMPTY_LOCK_INIT, ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, ERTS_PROC_LC_EMPTY_LOCK_INIT, ERTS_PROC_LC_EMPTY_LOCK_INIT}; - erts_lc_lock_t have_not_locks[5] = {ERTS_PROC_LC_EMPTY_LOCK_INIT, + erts_lc_lock_t have_not_locks[6] = {ERTS_PROC_LC_EMPTY_LOCK_INIT, ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, ERTS_PROC_LC_EMPTY_LOCK_INIT, ERTS_PROC_LC_EMPTY_LOCK_INIT}; @@ -1557,9 +1661,17 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks) have_not_locks[have_not_locks_len].id = lc_id.proc_lock_status; have_not_locks[have_not_locks_len++].extra = p->common.id; } + if (locks & ERTS_PROC_LOCK_TRACE) { + have_locks[have_locks_len].id = lc_id.proc_lock_trace; + have_locks[have_locks_len++].extra = p->common.id; + } + else { + have_not_locks[have_not_locks_len].id = lc_id.proc_lock_trace; + have_not_locks[have_not_locks_len++].extra = p->common.id; + } #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL - erts_lc_lock_t have_locks[5]; - erts_lc_lock_t have_not_locks[5]; + erts_lc_lock_t have_locks[6]; + erts_lc_lock_t have_not_locks[6]; if (locks & ERTS_PROC_LOCK_MAIN) have_locks[have_locks_len++] = p->lock.main.lc; @@ -1581,6 +1693,10 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks) have_locks[have_locks_len++] = p->lock.status.lc; else have_not_locks[have_not_locks_len++] = p->lock.status.lc; + if (locks & ERTS_PROC_LOCK_TRACE) + have_locks[have_locks_len++] = p->lock.trace.lc; + else + have_not_locks[have_not_locks_len++] = p->lock.trace.lc; #endif erts_lc_check(have_locks, have_locks_len, @@ -1590,10 +1706,10 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks) ErtsProcLocks erts_proc_lc_my_proc_locks(Process *p) { - int resv[5]; + int resv[6]; ErtsProcLocks res = 0; #if ERTS_PROC_LOCK_OWN_IMPL - erts_lc_lock_t locks[5] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main, + erts_lc_lock_t locks[6] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main, p->common.id, ERTS_LC_FLG_LT_PROCLOCK), ERTS_LC_LOCK_INIT(lc_id.proc_lock_link, @@ -1606,17 +1722,21 @@ erts_proc_lc_my_proc_locks(Process *p) p->common.id, ERTS_LC_FLG_LT_PROCLOCK), ERTS_LC_LOCK_INIT(lc_id.proc_lock_status, + p->common.id, + ERTS_LC_FLG_LT_PROCLOCK), + ERTS_LC_LOCK_INIT(lc_id.proc_lock_trace, p->common.id, ERTS_LC_FLG_LT_PROCLOCK)}; #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL - erts_lc_lock_t locks[5] = {p->lock.main.lc, + erts_lc_lock_t locks[6] = {p->lock.main.lc, p->lock.link.lc, p->lock.msgq.lc, p->lock.btm.lc, - p->lock.status.lc}; + p->lock.status.lc, + p->lock.trace.lc}; #endif - erts_lc_have_locks(resv, locks, 5); + erts_lc_have_locks(resv, locks, 6); if (resv[0]) res |= ERTS_PROC_LOCK_MAIN; if (resv[1]) @@ -1627,6 +1747,8 @@ erts_proc_lc_my_proc_locks(Process *p) res |= ERTS_PROC_LOCK_BTM; if (resv[4]) res |= ERTS_PROC_LOCK_STATUS; + if (resv[5]) + res |= ERTS_PROC_LOCK_TRACE; return res; } @@ -1634,14 +1756,15 @@ erts_proc_lc_my_proc_locks(Process *p) void erts_proc_lc_chk_no_proc_locks(char *file, int line) { - int resv[5]; - int ids[5] = {lc_id.proc_lock_main, + int resv[6]; + int ids[6] = {lc_id.proc_lock_main, lc_id.proc_lock_link, lc_id.proc_lock_msgq, lc_id.proc_lock_btm, - lc_id.proc_lock_status}; - erts_lc_have_lock_ids(resv, ids, 5); - if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3] || resv[4])) { + lc_id.proc_lock_status, + lc_id.proc_lock_trace}; + erts_lc_have_lock_ids(resv, ids, 6); + if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3] || resv[4] || resv[5])) { erts_lc_fail("%s:%d: Thread has process locks locked when expected " "not to have any process locks locked", file, line); diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index ac324c6f3e..d81ef63af2 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -66,7 +66,7 @@ #endif -#define ERTS_PROC_LOCK_MAX_BIT 4 +#define ERTS_PROC_LOCK_MAX_BIT 5 typedef erts_aint32_t ErtsProcLocks; @@ -84,6 +84,7 @@ typedef struct erts_proc_lock_t_ { erts_lcnt_lock_t lcnt_msgq; erts_lcnt_lock_t lcnt_btm; erts_lcnt_lock_t lcnt_status; + erts_lcnt_lock_t lcnt_trace; #endif #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL erts_mtx_t main; @@ -91,6 +92,7 @@ typedef struct erts_proc_lock_t_ { erts_mtx_t msgq; erts_mtx_t btm; erts_mtx_t status; + erts_mtx_t trace; #else # error "no implementation" #endif @@ -137,9 +139,18 @@ typedef struct erts_proc_lock_t_ { * Protects the following fields in the process structure: * * pending_suspenders * * suspendee + * * sys_tasks * * ... */ -#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << ERTS_PROC_LOCK_MAX_BIT) +#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << 4) + +/* + * Trace message lock: + * Protects the order in which messages are sent + * from trace nifs. This lock is taken inside enif_send. + * + */ +#define ERTS_PROC_LOCK_TRACE (((ErtsProcLocks) 1) << ERTS_PROC_LOCK_MAX_BIT) /* * Special fields: @@ -154,8 +165,8 @@ typedef struct erts_proc_lock_t_ { * all process locks are held, and are allowed to be read if * at least one process lock (whichever one doesn't matter) * is held: - * * tracer_proc - * * tracer_flags + * * common.tracer + * * common.trace_flags * * The following fields are only allowed to be accessed if * both the schedule queue lock and at least one process lock @@ -208,7 +219,7 @@ typedef struct erts_proc_lock_t_ { ((((ErtsProcLocks) 1) << (ERTS_PROC_LOCK_MAX_BIT + 1)) - 1) #define ERTS_PROC_LOCKS_ALL_MINOR (ERTS_PROC_LOCKS_ALL \ - & ~ERTS_PROC_LOCK_MAIN) + & ~ERTS_PROC_LOCK_MAIN) #define ERTS_PIX_LOCKS_BITS 10 @@ -260,6 +271,7 @@ void erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks); void erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks); void erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks); void erts_proc_lc_chk_only_proc_main(Process *p); +void erts_proc_lc_chk_only_proc(Process *p, ErtsProcLocks locks); void erts_proc_lc_chk_no_proc_locks(char *file, int line); ErtsProcLocks erts_proc_lc_my_proc_locks(Process *p); int erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks); @@ -477,9 +489,15 @@ erts_smp_proc_raw_trylock__(Process *p, ErtsProcLocks locks) if (locks & ERTS_PROC_LOCK_STATUS) if (erts_mtx_trylock(&p->lock.status) == EBUSY) goto busy_status; + if (locks & ERTS_PROC_LOCK_TRACE) + if (erts_mtx_trylock(&p->lock.trace) == EBUSY) + goto busy_trace; return 0; +busy_trace: + if (locks & ERTS_PROC_LOCK_TRACE) + erts_mtx_unlock(&p->lock.trace); busy_status: if (locks & ERTS_PROC_LOCK_BTM) erts_mtx_unlock(&p->lock.btm); @@ -568,6 +586,8 @@ erts_smp_proc_lock__(Process *p, erts_mtx_lock(&p->lock.btm); if (locks & ERTS_PROC_LOCK_STATUS) erts_mtx_lock(&p->lock.status); + if (locks & ERTS_PROC_LOCK_TRACE) + erts_mtx_lock(&p->lock.trace); #ifdef ERTS_PROC_LOCK_DEBUG erts_proc_lock_op_debug(p, locks, 1); @@ -653,6 +673,8 @@ erts_smp_proc_unlock__(Process *p, erts_proc_lock_op_debug(p, locks, 0); #endif + if (locks & ERTS_PROC_LOCK_TRACE) + erts_mtx_unlock(&p->lock.trace); if (locks & ERTS_PROC_LOCK_STATUS) erts_mtx_unlock(&p->lock.status); if (locks & ERTS_PROC_LOCK_BTM) diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h index 97df8bc3fc..a5931ffc25 100644 --- a/erts/emulator/beam/erl_ptab.h +++ b/erts/emulator/beam/erl_ptab.h @@ -37,14 +37,16 @@ #include "erl_alloc.h" #include "erl_monitors.h" -#define ERTS_TRACER_PROC(P) ((P)->common.tracer_proc) +#define ERTS_TRACER(P) ((P)->common.tracer) +#define ERTS_TRACER_MODULE(T) (CAR(list_val(T))) +#define ERTS_TRACER_STATE(T) (CDR(list_val(T))) #define ERTS_TRACE_FLAGS(P) ((P)->common.trace_flags) #define ERTS_P_LINKS(P) ((P)->common.u.alive.links) #define ERTS_P_MONITORS(P) ((P)->common.u.alive.monitors) #define IS_TRACED(p) \ - (ERTS_TRACER_PROC((p)) != NIL) + (ERTS_TRACER(p) != NIL) #define ARE_TRACE_FLAGS_ON(p,tf) \ ((ERTS_TRACE_FLAGS((p)) & (tf|F_SENSITIVE)) == (tf)) #define IS_TRACED_FL(p,tf) \ @@ -56,7 +58,7 @@ typedef struct { erts_atomic_t atmc; Sint sint; } refc; - Eterm tracer_proc; + ErtsTracer tracer; Uint trace_flags; erts_smp_atomic_t timer; union { diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 947def7398..346404fe2a 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -1966,7 +1966,7 @@ send_time_offset_changed_notifications(void *new_offsetp) *patch_refp = ref; ASSERT(hsz == size_object(message_template)); message = copy_struct(message_template, hsz, &hp, ohp); - erts_queue_message(rp, &rp_locks, mp, message, NIL); + erts_queue_message(rp, &rp_locks, mp, message); } erts_smp_proc_unlock(rp, rp_locks); } diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 1654ea58d9..1e6ae8c82e 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -39,6 +39,7 @@ #include "erl_bits.h" #include "erl_thr_progress.h" #include "erl_bif_unique.h" +#include "erl_map.h" #if 0 #define DEBUG_PRINTOUTS @@ -46,17 +47,13 @@ #undef DEBUG_PRINTOUTS #endif -extern BeamInstr beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */ -extern BeamInstr beam_return_trace[1]; /* OpCode(i_return_trace) */ -extern BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */ - /* Pseudo export entries. Never filled in with data, only used to yield unique pointers of the correct type. */ Export exp_send, exp_receive, exp_timeout; -static Eterm system_seq_tracer; +static ErtsTracer system_seq_tracer; static Uint default_trace_flags; -static Eterm default_tracer; +static ErtsTracer default_tracer; static Eterm system_monitor; static Eterm system_profile; @@ -70,11 +67,8 @@ static erts_smp_rwmtx_t sys_trace_rwmtx; enum ErtsSysMsgType { SYS_MSG_TYPE_UNDEFINED, - SYS_MSG_TYPE_TRACE, - SYS_MSG_TYPE_SEQTRACE, SYS_MSG_TYPE_SYSMON, SYS_MSG_TYPE_ERRLGR, - SYS_MSG_TYPE_PROC_MSG, SYS_MSG_TYPE_SYSPROF }; @@ -298,43 +292,6 @@ write_ts(int ts_type, Eterm *hp, ErlHeapFragment *bp, Process *tracer) return res; } -/* - * Patch a timestamp into a tuple. The tuple MUST be the last thing - * built on the heap before the call, and the timestamp MUST be - * the last thing after the call. This since patch_ts() might adjust - * the size of the used area. - */ - -#define PATCH_TS__(Type, Tuple, Hp, Bp, Tracer) \ - do { \ - int ts_type__ = (Type); \ - if (ts_type__) \ - patch_ts(ts_type__, (Tuple), (Hp), (Bp), (Tracer)); \ - } while (0) - -#ifdef ERTS_SMP -#define PATCH_TS(Type, Tuple, Hp, Bp, Tracer) \ - PATCH_TS__((Type), (Tuple), (Hp), (Bp), NULL) -#else -#define PATCH_TS(Type, Tuple, Hp, Bp, Tracer) \ - PATCH_TS__((Type), (Tuple), (Hp), (Bp), (Tracer)) -#endif - -static ERTS_INLINE void -patch_ts(int ts_type, Eterm tuple, Eterm* hp, ErlHeapFragment *bp, Process *tracer) -{ - Eterm *tptr = tuple_val(tuple); - int arity = arityval(*tptr); - - ASSERT(ts_type); - ASSERT((tptr+arity+1) == hp); - - tptr[0] = make_arityval(arity+1); - tptr[1] = am_trace_ts; - - *hp = write_ts(ts_type, hp+1, bp, tracer); -} - #ifdef ERTS_SMP static void enqueue_sys_msg_unlocked(enum ErtsSysMsgType type, Eterm from, @@ -349,6 +306,14 @@ static void enqueue_sys_msg(enum ErtsSysMsgType type, static void init_sys_msg_dispatcher(void); #endif +static void init_tracer_nif(void); +static int tracer_cmp_fun(void*, void*); +static HashValue tracer_hash_fun(void*); +static void *tracer_alloc_fun(void*); +static void tracer_free_fun(void*); + +typedef struct ErtsTracerNif_ ErtsTracerNif; + void erts_init_trace(void) { erts_smp_rwmtx_opt_t rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ; @@ -363,79 +328,38 @@ void erts_init_trace(void) { erts_system_monitor_clear(NULL); erts_system_profile_clear(NULL); default_trace_flags = F_INITIAL_TRACE_FLAGS; - default_tracer = NIL; - system_seq_tracer = am_false; + default_tracer = erts_tracer_nil; + system_seq_tracer = erts_tracer_nil; #ifdef ERTS_SMP init_sys_msg_dispatcher(); #endif + init_tracer_nif(); } -static Eterm system_seq_tracer; - #define ERTS_ALLOC_SYSMSG_HEAP(SZ, BPP, OHPP, UNUSED) \ (*(BPP) = new_message_buffer((SZ)), \ *(OHPP) = &(*(BPP))->off_heap, \ (*(BPP))->mem) -#ifdef ERTS_SMP -#define ERTS_ENQ_TRACE_MSG(FPID, TPID, MSG, BP) \ -do { \ - ERTS_LC_ASSERT(erts_smp_lc_mtx_is_locked(&smq_mtx)); \ - enqueue_sys_msg_unlocked(SYS_MSG_TYPE_TRACE, (FPID), (TPID), (MSG), (BP)); \ -} while(0) -#else -#define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \ - do { \ - ErtsMessage *mp__ = erts_alloc_message(0, NULL); \ - mp__->data.heap_frag = (BP); \ - erts_queue_message((TPROC), NULL, mp__, (MSG), NIL); \ - } while (0) -#endif - -/* - * NOTE that the ERTS_GET_TRACER_REF() returns from the function (!!!) - * using it, and resets the parameters used if the tracer is invalid, i.e., - * use it with extreme care! - */ -#ifdef ERTS_SMP -#define ERTS_NULL_TRACER_REF NIL -#define ERTS_TRACER_REF_TYPE Eterm - /* In the smp case, we never find the tracer invalid here (the sys - message dispatcher thread takes care of that). */ -#define ERTS_GET_TRACER_REF(RES, TPID, TRACEE_FLGS) \ -do { (RES) = (TPID); } while(0) -int -erts_is_tracer_proc_valid(Process* p) -{ - return 1; -} -#else -#define ERTS_NULL_TRACER_REF NULL -#define ERTS_TRACER_REF_TYPE Process * -#define ERTS_GET_TRACER_REF(RES, TPID, TRACEE_FLGS) \ -do { \ - (RES) = erts_proc_lookup((TPID)); \ - if (!(RES) || !(ERTS_TRACE_FLAGS((RES)) & F_TRACER)) { \ - (TPID) = NIL; \ - (TRACEE_FLGS) &= ~TRACEE_FLAGS; \ - return; \ - } \ -} while (0) -int -erts_is_tracer_proc_valid(Process* p) -{ - Process* tracer; +static ERTS_INLINE int +send_to_tracer_nif_raw(Process *c_p, Process *tracee, const ErtsTracer tracer, + Uint trace_flags, Eterm t_p_id, ErtsTracerNif *tnif, + Eterm tag, Eterm msg, Eterm extra, Eterm pam_result); +static ERTS_INLINE int +send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, + Eterm t_p_id, ErtsTracerNif *tnif, Eterm tag, + Eterm msg, Eterm extra); +static ERTS_INLINE Eterm +call_enabled_tracer(Process *c_p, const ErtsTracer tracer, + ErtsTracerNif **tnif_ref, Eterm tag, Eterm t_p_id); +static int +is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, + ErtsPTabElementCommon *t_p, + ErtsTracerNif **tnif_ret, Eterm tag); - tracer = erts_proc_lookup(ERTS_TRACER_PROC(p)); - if (tracer && ERTS_TRACE_FLAGS(tracer) & F_TRACER) { - return 1; - } else { - ERTS_TRACER_PROC(p) = NIL; - ERTS_TRACE_FLAGS(p) = ~TRACEE_FLAGS; - return 0; - } -} -#endif +#define SEND_TO_TRACER(c_p, tag, msg) \ + send_to_tracer_nif(c_p, &(c_p)->common, (c_p)->common.id, NULL, tag, \ + msg, THE_NON_VALUE) static Uint active_sched; @@ -450,19 +374,6 @@ static void exiting_reset(Eterm exiting) { erts_smp_rwmtx_rwlock(&sys_trace_rwmtx); - if (exiting == default_tracer) { - default_tracer = NIL; - default_trace_flags &= TRACEE_FLAGS; -#ifdef DEBUG - default_trace_flags |= F_INITIAL_TRACE_FLAGS; -#endif - } - if (exiting == system_seq_tracer) { -#ifdef DEBUG_PRINTOUTS - erts_fprintf(stderr, "seq tracer %T exited\n", exiting); -#endif - system_seq_tracer = am_false; - } if (exiting == system_monitor) { #ifdef ERTS_SMP system_monitor = NIL; @@ -487,11 +398,7 @@ erts_trace_check_exiting(Eterm exiting) { int reset = 0; erts_smp_rwmtx_rlock(&sys_trace_rwmtx); - if (exiting == default_tracer) - reset = 1; - else if (exiting == system_seq_tracer) - reset = 1; - else if (exiting == system_monitor) + if (exiting == system_monitor) reset = 1; else if (exiting == system_profile) reset = 1; @@ -500,23 +407,26 @@ erts_trace_check_exiting(Eterm exiting) exiting_reset(exiting); } -static ERTS_INLINE int -is_valid_tracer(Eterm tracer) -{ - return erts_proc_lookup(tracer) || erts_is_valid_tracer_port(tracer); -} - -Eterm -erts_set_system_seq_tracer(Process *c_p, ErtsProcLocks c_p_locks, Eterm new) +ErtsTracer +erts_set_system_seq_tracer(Process *c_p, ErtsProcLocks c_p_locks, ErtsTracer new) { - Eterm old; - - if (new != am_false && !is_valid_tracer(new)) - return THE_NON_VALUE; + ErtsTracer old; + + if (!ERTS_TRACER_IS_NIL(new)) { + Eterm nif_result = call_enabled_tracer( + NULL, new, NULL, + am_trace_status, am_undefined); + switch (nif_result) { + case am_trace: break; + default: + return THE_NON_VALUE; + } + } erts_smp_rwmtx_rwlock(&sys_trace_rwmtx); old = system_seq_tracer; - system_seq_tracer = new; + system_seq_tracer = erts_tracer_nil; + erts_tracer_update(&system_seq_tracer, new); #ifdef DEBUG_PRINTOUTS erts_fprintf(stderr, "set seq tracer new=%T old=%T\n", new, old); @@ -525,47 +435,56 @@ erts_set_system_seq_tracer(Process *c_p, ErtsProcLocks c_p_locks, Eterm new) return old; } -Eterm +ErtsTracer erts_get_system_seq_tracer(void) { - Eterm st; + ErtsTracer st; erts_smp_rwmtx_rlock(&sys_trace_rwmtx); st = system_seq_tracer; #ifdef DEBUG_PRINTOUTS erts_fprintf(stderr, "get seq tracer %T\n", st); #endif erts_smp_rwmtx_runlock(&sys_trace_rwmtx); + + if (st != erts_tracer_nil && + call_enabled_tracer(NULL, st, NULL, am_trace_status, am_undefined) == am_remove) { + erts_set_system_seq_tracer(NULL, 0, erts_tracer_nil); + st = erts_tracer_nil; + } + return st; } static ERTS_INLINE void -get_default_tracing(Uint *flagsp, Eterm *tracerp) +get_default_tracing(Uint *flagsp, ErtsTracer *tracerp) { if (!(default_trace_flags & TRACEE_FLAGS)) - default_tracer = NIL; + ERTS_TRACER_CLEAR(&default_tracer); - if (is_nil(default_tracer)) { + if (ERTS_TRACER_IS_NIL(default_tracer)) { default_trace_flags &= ~TRACEE_FLAGS; - } else if (is_internal_pid(default_tracer)) { - if (!erts_proc_lookup(default_tracer)) { - reset_tracer: - default_trace_flags &= ~TRACEE_FLAGS; - default_tracer = NIL; - } } else { - ASSERT(is_internal_port(default_tracer)); - if (!erts_is_valid_tracer_port(default_tracer)) - goto reset_tracer; + Eterm nif_result = call_enabled_tracer( + NULL, default_tracer, NULL, + am_trace_status, am_undefined); + switch (nif_result) { + case am_trace: break; + default: + default_trace_flags &= ~TRACEE_FLAGS; + ERTS_TRACER_CLEAR(&default_tracer); + } } if (flagsp) *flagsp = default_trace_flags; - if (tracerp) - *tracerp = default_tracer; + if (tracerp) { + erts_tracer_update(tracerp,default_tracer); + } } void -erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp) +erts_change_default_tracing(int setflags, Uint *flagsp, + const ErtsTracer tracer) { erts_smp_rwmtx_rwlock(&sys_trace_rwmtx); if (flagsp) { @@ -574,16 +493,18 @@ erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp) else default_trace_flags &= ~(*flagsp); } - if (tracerp) - default_tracer = *tracerp; - get_default_tracing(flagsp, tracerp); + + erts_tracer_update(&default_tracer, tracer); + + get_default_tracing(flagsp, NULL); erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx); } void -erts_get_default_tracing(Uint *flagsp, Eterm *tracerp) +erts_get_default_tracing(Uint *flagsp, ErtsTracer *tracerp) { erts_smp_rwmtx_rlock(&sys_trace_rwmtx); + *tracerp = erts_tracer_nil; /* initialize */ get_default_tracing(flagsp, tracerp); erts_smp_rwmtx_runlock(&sys_trace_rwmtx); } @@ -622,29 +543,21 @@ erts_get_system_profile(void) { return profile; } -#ifdef ERTS_SMP -static void -do_send_to_port(Eterm to, - Port* unused_port, - Eterm from, - enum ErtsSysMsgType type, - Eterm message) -{ - Uint sz = size_object(message); - ErlHeapFragment *bp = new_message_buffer(sz); - Uint *hp = bp->mem; - Eterm msg = copy_struct(message, sz, &hp, &bp->off_heap); - enqueue_sys_msg_unlocked(type, from, to, msg, bp); -} - -#define WRITE_SYS_MSG_TO_PORT write_sys_msg_to_port +#ifdef HAVE_ERTS_NOW_CPU +# define GET_NOW(m, s, u) \ +do { \ + if (erts_cpu_timestamp) \ + erts_get_now_cpu(m, s, u); \ + else \ + get_now(m, s, u); \ +} while (0) #else -#define WRITE_SYS_MSG_TO_PORT do_send_to_port +# define GET_NOW(m, s, u) do {get_now(m, s, u);} while (0) #endif static void -WRITE_SYS_MSG_TO_PORT(Eterm unused_to, +write_sys_msg_to_port(Eterm unused_to, Port* trace_port, Eterm unused_from, enum ErtsSysMsgType unused_type, @@ -671,150 +584,6 @@ WRITE_SYS_MSG_TO_PORT(Eterm unused_to, erts_free(ERTS_ALC_T_TMP, (void *) buffer); } - -#ifndef ERTS_SMP -/* Send {trace_ts, Pid, out, 0, Timestamp} - * followed by {trace_ts, Pid, in, 0, NewTimestamp} - * - * 'NewTimestamp' through patch_ts(). - */ -static void -do_send_schedfix_to_port(Port *trace_port, Eterm pid, Eterm timestamp, int ts_type) { -#define LOCAL_HEAP_SIZE (5+5+ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - Eterm message; - Eterm *hp; - Eterm mfarity; - - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - ASSERT(is_pid(pid)); - ASSERT(is_tuple(timestamp)); - ASSERT(*tuple_val(timestamp) == make_arityval(3)); - - hp = local_heap; - mfarity = make_small(0); - message = TUPLE5(hp, am_trace_ts, pid, am_out, mfarity, timestamp); - /* Note, hp is deliberately NOT incremented since it will be reused */ - - do_send_to_port(trace_port->common.id, - trace_port, - pid, - SYS_MSG_TYPE_UNDEFINED, - message); - - - message = TUPLE5(hp, am_trace_ts, pid, am_in, mfarity, - NIL /* Will be overwritten by timestamp */); - hp += 6; - hp[-1] = write_ts(ts_type, hp, NULL, NULL); - - do_send_to_port(trace_port->common.id, - trace_port, - pid, - SYS_MSG_TYPE_UNDEFINED, - message); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE -} -#endif - -/* If (c_p != NULL), a fake schedule out/in message pair will be sent, - * if the driver so requests. - * It is assumed that 'message' is not an 'out' message. - * - * 'c_p' is the currently executing process, "tracee" is the traced process - * which 'message' concerns => if (*tracee_flags & F_TIMESTAMP_MASK), - * 'message' must contain a timestamp. - */ -static void -send_to_port(Process *c_p, Eterm message, - Eterm *tracer_pid, Uint *tracee_flags) { - Port* trace_port; -#ifndef ERTS_SMP - int ts_type; -#define LOCAL_HEAP_SIZE ERTS_TRACE_PATCH_TS_MAX_SIZE - Eterm ts; - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); -#endif - - ASSERT(is_internal_port(*tracer_pid)); -#ifdef ERTS_SMP - if (is_not_internal_port(*tracer_pid)) - return; - - trace_port = NULL; -#else - - trace_port = erts_id2port_sflgs(*tracer_pid, - NULL, - 0, - ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP); - - if (!trace_port) { - *tracee_flags &= ~TRACEE_FLAGS; - *tracer_pid = NIL; - return; - } - - /* - * Make a fake schedule only if the current process is traced - * with 'running' and 'timestamp'. - */ - - if ( c_p == NULL || - (! IS_TRACED_FL(c_p, F_TRACE_SCHED | F_TIMESTAMP_MASK))) { -#endif - do_send_to_port(*tracer_pid, - trace_port, - c_p ? c_p->common.id : NIL, - SYS_MSG_TYPE_TRACE, - message); -#ifndef ERTS_SMP - erts_port_release(trace_port); - return; - } - - /* - * Note that the process being traced for some type of trace messages - * (e.g. getting_linked) need not be the current process. That other - * process might not have timestamps enabled. - */ - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - - /* A fake schedule might be needed. - * Create a dummy trace message with timestamp to be - * passed to do_send_schedfix_to_port(). - */ - ts_type = TFLGS_TS_TYPE(c_p); - ts = write_ts(ts_type, local_heap, NULL, NULL); - - trace_port->control_flags &= ~PORT_CONTROL_FLAG_HEAVY; - do_send_to_port(*tracer_pid, - trace_port, - c_p ? c_p->common.id : NIL, - SYS_MSG_TYPE_TRACE, - message); - - if (trace_port->control_flags & PORT_CONTROL_FLAG_HEAVY) { - /* The driver has just informed us that the last write took a - * non-neglectible amount of time. - * - * We need to fake some trace messages to compensate for the time the - * current process had to sacrifice for the writing of the previous - * trace message. We pretend that the process got scheduled out - * just after writning the real trace message, and now gets scheduled - * in again. - */ - do_send_schedfix_to_port(trace_port, c_p->common.id, ts, ts_type); - } - - erts_port_release(trace_port); - - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE -#endif -} - #ifndef ERTS_SMP /* Profile send * Checks if profiler is port or process @@ -843,11 +612,11 @@ profile_send(Eterm from, Eterm message) { 0, ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP); if (profiler_port) { - do_send_to_port(profiler, - profiler_port, - NIL, /* or current process->common.id */ - SYS_MSG_TYPE_SYSPROF, - message); + write_sys_msg_to_port(profiler, + profiler_port, + NIL, /* or current process->common.id */ + SYS_MSG_TYPE_SYSPROF, + message); erts_port_release(profiler_port); } @@ -867,154 +636,26 @@ profile_send(Eterm from, Eterm message) { else msg = copy_struct(message, sz, &hp, &mp->hfrag.off_heap); - erts_queue_message(profile_p, NULL, mp, msg, NIL); + erts_queue_message(profile_p, NULL, mp, msg); } } #endif - -/* A fake schedule out/in message pair will be sent, - * if the driver so requests. - * - * 'c_p' is the currently executing process, may be NULL. - */ -static void -seq_trace_send_to_port(Process *c_p, - Eterm seq_tracer, - Eterm message) -{ - Port* trace_port; -#ifndef ERTS_SMP - int ts_type; - Eterm ts; -#define LOCAL_HEAP_SIZE ERTS_TRACE_PATCH_TS_MAX_SIZE - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#endif - - ASSERT(is_internal_port(seq_tracer)); -#ifdef ERTS_SMP - if (is_not_internal_port(seq_tracer)) - return; - - trace_port = NULL; -#else - trace_port = erts_id2port_sflgs(seq_tracer, - NULL, - 0, - ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP); - if (!trace_port) { - system_seq_tracer = am_false; -#ifndef ERTS_SMP - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#endif - return; - } - - if (c_p == NULL - || (! IS_TRACED_FL(c_p, F_TRACE_SCHED | F_TIMESTAMP_MASK))) { -#endif - do_send_to_port(seq_tracer, - trace_port, - c_p ? c_p->common.id : NIL, - SYS_MSG_TYPE_SEQTRACE, - message); - -#ifndef ERTS_SMP - erts_port_release(trace_port); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); - return; - } - /* Make a fake schedule only if the current process is traced - * with 'running' and 'timestamp'. - */ - - /* A fake schedule might be needed. - * Create a dummy trace message with timestamp to be - * passed to do_send_schedfix_to_port(). - */ - ts_type = TFLGS_TS_TYPE(c_p); - ts = write_ts(ts_type, local_heap, NULL, NULL); - - trace_port->control_flags &= ~PORT_CONTROL_FLAG_HEAVY; - do_send_to_port(seq_tracer, - trace_port, - c_p ? c_p->common.id : NIL, - SYS_MSG_TYPE_SEQTRACE, - message); - - if (trace_port->control_flags & PORT_CONTROL_FLAG_HEAVY) { - /* The driver has just informed us that the last write took a - * non-neglectible amount of time. - * - * We need to fake some trace messages to compensate for the time the - * current process had to sacrifice for the writing of the previous - * trace message. We pretend that the process got scheduled out - * just after writing the real trace message, and now gets scheduled - * in again. - */ - do_send_schedfix_to_port(trace_port, c_p->common.id, ts, ts_type); - } - - erts_port_release(trace_port); - - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE -#endif -} - -static ERTS_INLINE void -send_to_tracer(Process *tracee, - ERTS_TRACER_REF_TYPE tracer_ref, - Eterm msg, - Eterm **hpp, - ErlHeapFragment *bp, - int no_fake_sched) -{ - ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(tracee)); - - erts_smp_mtx_lock(&smq_mtx); - - if (is_internal_pid(ERTS_TRACER_PROC(tracee))) { - PATCH_TS(TFLGS_TS_TYPE(tracee), msg, *hpp, bp, tracer_ref); - ERTS_ENQ_TRACE_MSG(tracee->common.id, tracer_ref, msg, bp); - } - else { - ASSERT(is_internal_port(ERTS_TRACER_PROC(tracee))); - PATCH_TS(TFLGS_TS_TYPE(tracee), msg, *hpp, NULL, NULL); - send_to_port(no_fake_sched ? NULL : tracee, - msg, - &ERTS_TRACER_PROC(tracee), - &ERTS_TRACE_FLAGS(tracee)); - } - - erts_smp_mtx_unlock(&smq_mtx); - -} - static void -trace_sched_aux(Process *p, Eterm what, int never_fake_sched) +trace_sched_aux(Process *p, ErtsProcLocks locks, Eterm what) { -#define LOCAL_HEAP_SIZE (5+4+1+ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeap(local_heap,LOCAL_HEAP_SIZE,p); - Eterm tmp, mess, *hp; - ErlHeapFragment *bp = NULL; - ErlOffHeap *off_heap; - ERTS_TRACER_REF_TYPE tracer_ref = ERTS_NULL_TRACER_REF; - int sched_no, curr_func, to_port, no_fake_sched; + Eterm tmp, *hp; + int curr_func; + ErtsTracerNif *tnif = NULL; - if (is_nil(ERTS_TRACER_PROC(p))) + if (ERTS_TRACER_IS_NIL(ERTS_TRACER(p))) return; - no_fake_sched = never_fake_sched; - switch (what) { case am_out: case am_out_exiting: case am_out_exited: - no_fake_sched = 1; - break; case am_in: case am_in_exiting: break; @@ -1023,16 +664,8 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched) break; } - sched_no = IS_TRACED_FL(p, F_TRACE_SCHED_NO); - to_port = is_internal_port(ERTS_TRACER_PROC(p)); - - if (!to_port) { - ASSERT(is_internal_pid(ERTS_TRACER_PROC(p))); - - ERTS_GET_TRACER_REF(tracer_ref, - ERTS_TRACER_PROC(p), - ERTS_TRACE_FLAGS(p)); - } + if (!is_tracer_proc_enabled(p, locks, &p->common, &tnif, what)) + return; if (ERTS_PROC_IS_EXITING(p)) curr_func = 0; @@ -1042,44 +675,16 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched) curr_func = p->current != NULL; } - UseTmpHeap(LOCAL_HEAP_SIZE,p); - - if (to_port) - hp = local_heap; - else { - Uint size = 5; - if (curr_func) - size += 4; - if (sched_no) - size += 1; - size += PATCH_TS_SIZE(p); - hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); - } - if (!curr_func) { tmp = make_small(0); } else { + hp = HAlloc(p, 4); tmp = TUPLE3(hp,p->current[0],p->current[1],make_small(p->current[2])); hp += 4; } - if (!sched_no) { - mess = TUPLE4(hp, am_trace, p->common.id, what, tmp); - hp += 5; - } - else { -#ifdef ERTS_SMP - Eterm sched_id = make_small(p->scheduler_data->no); -#else - Eterm sched_id = make_small(1); -#endif - mess = TUPLE5(hp, am_trace, p->common.id, what, sched_id, tmp); - hp += 6; - } - - send_to_tracer(p, tracer_ref, mess, &hp, bp, no_fake_sched); - UnUseTmpHeap(LOCAL_HEAP_SIZE,p); -#undef LOCAL_HEAP_SIZE + send_to_tracer_nif(p, &p->common, p->common.id, tnif, + what, tmp, THE_NON_VALUE); } /* Send {trace_ts, Pid, What, {Mod, Func, Arity}, Timestamp} @@ -1089,9 +694,9 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched) * 'out_exiting', or 'out_exited'. */ void -trace_sched(Process *p, Eterm what) +trace_sched(Process *p, ErtsProcLocks locks, Eterm what) { - trace_sched_aux(p, what, 0); + trace_sched_aux(p, locks, what); } /* Send {trace_ts, Pid, Send, Msg, DestPid, Timestamp} @@ -1102,17 +707,13 @@ trace_sched(Process *p, Eterm what) void trace_send(Process *p, Eterm to, Eterm msg) { - Eterm operation; - unsigned sz_msg; - unsigned sz_to; - Eterm* hp; - Eterm mess; - + Eterm operation = am_send; + ErtsTracerNif *tnif = NULL; + if (!ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND)) { return; } - operation = am_send; if (is_internal_pid(to)) { if (!erts_proc_lookup(to)) goto send_to_non_existing_process; @@ -1125,117 +726,36 @@ trace_send(Process *p, Eterm to, Eterm msg) operation = am_atom_put(s, sys_strlen(s)); } - if (is_internal_port(ERTS_TRACER_PROC(p))) { -#define LOCAL_HEAP_SIZE (6 + ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - - hp = local_heap; - mess = TUPLE5(hp, am_trace, p->common.id, operation, msg, to); - hp += 6; - erts_smp_mtx_lock(&smq_mtx); - PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL); - send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE - erts_smp_mtx_unlock(&smq_mtx); - } else { - Uint need; - ErlHeapFragment *bp; - ErlOffHeap *off_heap; - ERTS_TRACER_REF_TYPE tracer_ref; - - ASSERT(is_internal_pid(ERTS_TRACER_PROC(p))); - - ERTS_GET_TRACER_REF(tracer_ref, - ERTS_TRACER_PROC(p), - ERTS_TRACE_FLAGS(p)); - - sz_msg = size_object(msg); - sz_to = size_object(to); - need = sz_msg + sz_to + 6 + PATCH_TS_SIZE(p); - - hp = ERTS_ALLOC_SYSMSG_HEAP(need, &bp, &off_heap, tracer_ref); - - to = copy_struct(to, - sz_to, - &hp, - off_heap); - msg = copy_struct(msg, - sz_msg, - &hp, - off_heap); - mess = TUPLE5(hp, am_trace, p->common.id, operation, msg, to); - hp += 6; - - erts_smp_mtx_lock(&smq_mtx); - - PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref); - ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp); - erts_smp_mtx_unlock(&smq_mtx); - } + if (is_tracer_proc_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, operation)) + send_to_tracer_nif(p, &p->common, p->common.id, tnif, operation, msg, to); } /* Send {trace_ts, Pid, receive, Msg, Timestamp} * or {trace, Pid, receive, Msg} */ void -trace_receive(Process *rp, Eterm msg) +trace_receive(Process *c_p, Eterm msg) { - Eterm mess; - size_t sz_msg; - Eterm* hp; - - if (is_internal_port(ERTS_TRACER_PROC(rp))) { -#define LOCAL_HEAP_SIZE (5+ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - - hp = local_heap; - mess = TUPLE4(hp, am_trace, rp->common.id, am_receive, msg); - hp += 5; - erts_smp_mtx_lock(&smq_mtx); - PATCH_TS(TFLGS_TS_TYPE(rp), mess, hp, NULL, NULL); - send_to_port(rp, mess, &ERTS_TRACER_PROC(rp), &ERTS_TRACE_FLAGS(rp)); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE - erts_smp_mtx_unlock(&smq_mtx); - } else { - Uint hsz; - ErlHeapFragment *bp; - ErlOffHeap *off_heap; - ERTS_TRACER_REF_TYPE tracer_ref; - - ASSERT(is_internal_pid(ERTS_TRACER_PROC(rp))); - - ERTS_GET_TRACER_REF(tracer_ref, - ERTS_TRACER_PROC(rp), - ERTS_TRACE_FLAGS(rp)); - - sz_msg = size_object(msg); - - hsz = sz_msg + 5 + PATCH_TS_SIZE(rp); - - hp = ERTS_ALLOC_SYSMSG_HEAP(hsz, &bp, &off_heap, tracer_ref); - - msg = copy_struct(msg, sz_msg, &hp, off_heap); - mess = TUPLE4(hp, am_trace, rp->common.id, am_receive, msg); - hp += 5; - - erts_smp_mtx_lock(&smq_mtx); - - PATCH_TS(TFLGS_TS_TYPE(rp), mess, hp, bp, tracer_ref); - ERTS_ENQ_TRACE_MSG(rp->common.id, tracer_ref, mess, bp); - erts_smp_mtx_unlock(&smq_mtx); - } + ErtsTracerNif *tnif = NULL; + if (is_tracer_proc_enabled(NULL, 0, &c_p->common, + &tnif, am_receive)) + send_to_tracer_nif(NULL, &c_p->common, c_p->common.id, + tnif, am_receive, msg, THE_NON_VALUE); } int seq_trace_update_send(Process *p) { - Eterm seq_tracer = erts_get_system_seq_tracer(); + ErtsTracer seq_tracer = erts_get_system_seq_tracer(); ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p)))); - if ((p->common.id == seq_tracer) || have_no_seqtrace(SEQ_TRACE_TOKEN(p))) { + if (have_no_seqtrace(SEQ_TRACE_TOKEN(p)) || + (seq_tracer != NIL && + call_enabled_tracer( NULL, seq_tracer, NULL, am_trace_status, + p ? p->common.id : am_undefined) != am_trace) +#ifdef USE_VM_PROBES + || (SEQ_TRACE_TOKEN(p) == am_have_dt_utag) +#endif + ) { return 0; } SEQ_TRACE_TOKEN_SENDER(p) = p->common.id; @@ -1263,20 +783,28 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, Eterm receiver, Process *process, Eterm exitfrom) { Eterm mess; - ErlHeapFragment* bp; Eterm* hp; Eterm label; Eterm lastcnt_serial; Eterm type_atom; - int sz_exit; - Eterm seq_tracer; - int ts_type; + ErtsTracer seq_tracer; + int seq_tracer_flags = 0; +#define LOCAL_HEAP_SIZE (64) + DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); seq_tracer = erts_get_system_seq_tracer(); ASSERT(is_tuple(token) || is_nil(token)); - if (SEQ_TRACE_T_SENDER(token) == seq_tracer || token == NIL || - (process && ERTS_TRACE_FLAGS(process) & F_SENSITIVE)) { + if (token == NIL || (process && ERTS_TRACE_FLAGS(process) & F_SENSITIVE) || + ERTS_TRACER_IS_NIL(seq_tracer) || + call_enabled_tracer( + NULL, seq_tracer, NULL, am_trace_status, + process ? process->common.id : am_undefined) != am_trace) { + return; + } + + if ((unsigned_val(SEQ_TRACE_T_FLAGS(token)) & type) == 0) { + /* No flags set, nothing to do */ return; } @@ -1289,151 +817,29 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, return; /* To avoid warning */ } - if ((unsigned_val(SEQ_TRACE_T_FLAGS(token)) & type) == 0) { - /* No flags set, nothing to do */ - return; - } + UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - if (seq_tracer == am_false) { - return; /* no need to send anything */ + hp = local_heap; + label = SEQ_TRACE_T_LABEL(token); + lastcnt_serial = TUPLE2(hp, SEQ_TRACE_T_LASTCNT(token), + SEQ_TRACE_T_SERIAL(token)); + hp += 3; + if (exitfrom != NIL) { + msg = TUPLE3(hp, am_EXIT, exitfrom, msg); + hp += 4; } + mess = TUPLE5(hp, type_atom, lastcnt_serial, SEQ_TRACE_T_SENDER(token), + receiver, msg); + hp += 6; - ts_type = ERTS_SEQTFLGS2TSTYPE(unsigned_val(SEQ_TRACE_T_FLAGS(token))); - - if (is_internal_port(seq_tracer)) { -#define LOCAL_HEAP_SIZE (60 + ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); + seq_tracer_flags |= ERTS_SEQTFLGS2TFLGS(unsigned_val(SEQ_TRACE_T_FLAGS(token))); - hp = local_heap; - label = SEQ_TRACE_T_LABEL(token); - lastcnt_serial = TUPLE2(hp, SEQ_TRACE_T_LASTCNT(token), - SEQ_TRACE_T_SERIAL(token)); - hp += 3; - if (exitfrom != NIL) { - msg = TUPLE3(hp, am_EXIT, exitfrom, msg); - hp += 4; - } - mess = TUPLE5(hp, type_atom, lastcnt_serial, SEQ_TRACE_T_SENDER(token), - receiver, msg); - hp += 6; + send_to_tracer_nif_raw(NULL, process, seq_tracer, seq_tracer_flags, + label, NULL, am_seq_trace, mess, + THE_NON_VALUE, am_true); - erts_smp_mtx_lock(&smq_mtx); - if (!ts_type) { - mess = TUPLE3(hp, am_seq_trace, label, mess); - seq_trace_send_to_port(NULL, seq_tracer, mess); - } else { - mess = TUPLE4(hp, am_seq_trace, label, mess, - NIL /* Will be overwritten by timestamp */); - hp += 5; - hp[-1] = write_ts(ts_type, hp, NULL, NULL); - seq_trace_send_to_port(process, seq_tracer, mess); - } - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); + UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); #undef LOCAL_HEAP_SIZE - erts_smp_mtx_unlock(&smq_mtx); - } else { -#ifndef ERTS_SMP - Process* tracer; -#endif - Eterm sender_copy; - Eterm receiver_copy; - Eterm m2; - Uint sz_label, sz_lastcnt_serial, sz_msg, sz_ts, sz_sender, - sz_exitfrom, sz_receiver; - - ASSERT(is_internal_pid(seq_tracer)); - -#ifndef ERTS_SMP - - tracer = erts_proc_lookup(seq_tracer); - if (!tracer) { - system_seq_tracer = am_false; - return; /* no need to send anything */ - } -#endif - if (receiver == seq_tracer) { - return; /* no need to send anything */ - } - - sz_label = size_object(SEQ_TRACE_T_LABEL(token)); - sz_sender = size_object(SEQ_TRACE_T_SENDER(token)); - sz_receiver = size_object(receiver); - sz_lastcnt_serial = 3; /* TUPLE2 */ - sz_msg = size_object(msg); - - sz_ts = patch_ts_size(ts_type); - if (exitfrom != NIL) { - sz_exit = 4; /* create {'EXIT',exitfrom,msg} */ - sz_exitfrom = size_object(exitfrom); - } - else { - sz_exit = 0; - sz_exitfrom = 0; - } - bp = new_message_buffer(4 /* TUPLE3 */ + sz_ts + 6 /* TUPLE5 */ - + sz_lastcnt_serial + sz_label + sz_msg - + sz_exit + sz_exitfrom - + sz_sender + sz_receiver); - hp = bp->mem; - label = copy_struct(SEQ_TRACE_T_LABEL(token), sz_label, &hp, &bp->off_heap); - lastcnt_serial = TUPLE2(hp,SEQ_TRACE_T_LASTCNT(token),SEQ_TRACE_T_SERIAL(token)); - hp += 3; - m2 = copy_struct(msg, sz_msg, &hp, &bp->off_heap); - if (sz_exit) { - Eterm exitfrom_copy = copy_struct(exitfrom, - sz_exitfrom, - &hp, - &bp->off_heap); - m2 = TUPLE3(hp, am_EXIT, exitfrom_copy, m2); - hp += 4; - } - sender_copy = copy_struct(SEQ_TRACE_T_SENDER(token), - sz_sender, - &hp, - &bp->off_heap); - receiver_copy = copy_struct(receiver, - sz_receiver, - &hp, - &bp->off_heap); - mess = TUPLE5(hp, - type_atom, - lastcnt_serial, - sender_copy, - receiver_copy, - m2); - hp += 6; - - erts_smp_mtx_lock(&smq_mtx); - - if (!ts_type) - mess = TUPLE3(hp, am_seq_trace, label, mess); - else { - mess = TUPLE4(hp, am_seq_trace, label, mess, - NIL /* Will be overwritten by timestamp */); - hp += 5; - /* Write timestamp in element 6 of the 'msg' tuple */ - hp[-1] = write_ts(ts_type, hp, bp, -#ifndef ERTS_SMP - tracer -#else - NULL -#endif - ); - } - -#ifdef ERTS_SMP - enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SEQTRACE, NIL, NIL, mess, bp); - erts_smp_mtx_unlock(&smq_mtx); -#else - /* trace_token must be NIL here */ - { - ErtsMessage *mp = erts_alloc_message(0, NULL); - mp->data.heap_frag = bp; - erts_queue_message(tracer, NULL, mp, mess, NIL); - } -#endif - } } /* Send {trace_ts, Pid, return_to, {Mod, Func, Arity}, Timestamp} @@ -1442,63 +848,19 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, void erts_trace_return_to(Process *p, BeamInstr *pc) { -#define LOCAL_HEAP_SIZE (4+5+ERTS_TRACE_PATCH_TS_MAX_SIZE) - Eterm* hp; Eterm mfa; - Eterm mess; - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); BeamInstr *code_ptr = find_function_from_pc(pc); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - - hp = local_heap; - if (!code_ptr) { mfa = am_undefined; } else { + Eterm *hp = HAlloc(p, 4); mfa = TUPLE3(hp, code_ptr[0], code_ptr[1], make_small(code_ptr[2])); - hp += 4; } - - mess = TUPLE4(hp, am_trace, p->common.id, am_return_to, mfa); - hp += 5; - - erts_smp_mtx_lock(&smq_mtx); - - PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL); - - if (is_internal_port(ERTS_TRACER_PROC(p))) { - send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); - } else { - ErlHeapFragment *bp; - ErlOffHeap *off_heap; - ERTS_TRACER_REF_TYPE tracer_ref; - unsigned size; - - /* - * Find the tracer. - */ - ASSERT(is_internal_pid(ERTS_TRACER_PROC(p))); - - ERTS_GET_TRACER_REF(tracer_ref, - ERTS_TRACER_PROC(p), - ERTS_TRACE_FLAGS(p)); - - size = size_object(mess); - hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); - - /* - * Copy the trace message into the buffer and enqueue it. - */ - mess = copy_struct(mess, size, &hp, off_heap); - ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp); - } - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE - erts_smp_mtx_unlock(&smq_mtx); + SEND_TO_TRACER(p, am_return_to, mfa); } @@ -1506,114 +868,53 @@ erts_trace_return_to(Process *p, BeamInstr *pc) * or {trace, Pid, return_from, {Mod, Name, Arity}, Retval} */ void -erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid) +erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, ErtsTracer *tracer) { Eterm* hp; - Eterm mfa; - Eterm mess; - Eterm mod, name; + Eterm mfa, mod, name; int arity; Uint meta_flags, *tracee_flags; - int ts_type; -#ifdef ERTS_SMP - Eterm tracee; -#endif - - ASSERT(tracer_pid); - if (*tracer_pid == am_true) { + + ASSERT(tracer); + if (ERTS_TRACER_COMPARE(*tracer, erts_tracer_true)) { /* Breakpoint trace enabled without specifying tracer => * use process tracer and flags */ - tracer_pid = &ERTS_TRACER_PROC(p); + tracer = &ERTS_TRACER(p); } - if (is_nil(*tracer_pid)) { + if (ERTS_TRACER_IS_NIL(*tracer)) { /* Trace disabled */ return; } - ASSERT(is_internal_pid(*tracer_pid) || is_internal_port(*tracer_pid)); - if (*tracer_pid == p->common.id) { - /* Do not generate trace messages to oneself */ - return; - } - if (tracer_pid == &ERTS_TRACER_PROC(p)) { + ASSERT(IS_TRACER_VALID(*tracer)); + if (tracer == &ERTS_TRACER(p)) { /* Tracer specified in process structure => * non-breakpoint trace => * use process flags */ tracee_flags = &ERTS_TRACE_FLAGS(p); -#ifdef ERTS_SMP - tracee = p->common.id; -#endif + if (! (*tracee_flags & F_TRACE_CALLS)) { + return; + } } else { /* Tracer not specified in process structure => * tracer specified in breakpoint => * meta trace => * use fixed flag set instead of process flags - */ + */ meta_flags = F_TRACE_CALLS | F_NOW_TS; tracee_flags = &meta_flags; -#ifdef ERTS_SMP - tracee = NIL; -#endif - } - if (! (*tracee_flags & F_TRACE_CALLS)) { - return; } - + mod = fi[0]; name = fi[1]; arity = fi[2]; - - ts_type = ERTS_TFLGS2TSTYPE(*tracee_flags); - - if (is_internal_port(*tracer_pid)) { -#define LOCAL_HEAP_SIZE (4+6+ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - hp = local_heap; - mfa = TUPLE3(hp, mod, name, make_small(arity)); - hp += 4; - mess = TUPLE5(hp, am_trace, p->common.id, am_return_from, mfa, retval); - hp += 6; - erts_smp_mtx_lock(&smq_mtx); - PATCH_TS(ts_type, mess, hp, NULL, NULL); - send_to_port(p, mess, tracer_pid, tracee_flags); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE - erts_smp_mtx_unlock(&smq_mtx); - } else { - ErlHeapFragment *bp; - ErlOffHeap *off_heap; - ERTS_TRACER_REF_TYPE tracer_ref; - unsigned size; - unsigned retval_size; - - ASSERT(is_internal_pid(*tracer_pid)); - - ERTS_GET_TRACER_REF(tracer_ref, *tracer_pid, *tracee_flags); - - retval_size = size_object(retval); - size = 6 + 4 + retval_size + patch_ts_size(ts_type); - - hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); - /* - * Build the trace tuple and put it into receive queue of the tracer process. - */ - - mfa = TUPLE3(hp, mod, name, make_small(arity)); - hp += 4; - retval = copy_struct(retval, retval_size, &hp, off_heap); - mess = TUPLE5(hp, am_trace, p->common.id, am_return_from, mfa, retval); - hp += 6; - - erts_smp_mtx_lock(&smq_mtx); - - PATCH_TS(ts_type, mess, hp, bp, tracer_ref); - - ERTS_ENQ_TRACE_MSG(tracee, tracer_ref, mess, bp); - erts_smp_mtx_unlock(&smq_mtx); - } + hp = HAlloc(p, 4); + mfa = TUPLE3(hp, mod, name, make_small(arity)); + hp += 4; + send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, + NULL, am_return_from, mfa, retval, am_true); } /* Send {trace_ts, Pid, exception_from, {Mod, Name, Arity}, {Class,Value}, @@ -1625,116 +926,50 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid) */ void erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value, - Eterm *tracer_pid) + ErtsTracer *tracer) { Eterm* hp; - Eterm mfa_tuple; - Eterm cv; - Eterm mess; + Eterm mfa_tuple, cv; Uint meta_flags, *tracee_flags; - int ts_type; -#ifdef ERTS_SMP - Eterm tracee; -#endif - - ASSERT(tracer_pid); - if (*tracer_pid == am_true) { + + ASSERT(tracer); + if (ERTS_TRACER_COMPARE(*tracer, erts_tracer_true)) { /* Breakpoint trace enabled without specifying tracer => * use process tracer and flags */ - tracer_pid = &ERTS_TRACER_PROC(p); + tracer = &ERTS_TRACER(p); } - if (is_nil(*tracer_pid)) { + if (ERTS_TRACER_IS_NIL(*tracer)) { /* Trace disabled */ return; } - ASSERT(is_internal_pid(*tracer_pid) || is_internal_port(*tracer_pid)); - if (*tracer_pid == p->common.id) { - /* Do not generate trace messages to oneself */ - return; - } - if (tracer_pid == &ERTS_TRACER_PROC(p)) { + ASSERT(IS_TRACER_VALID(*tracer)); + if (tracer == &ERTS_TRACER(p)) { /* Tracer specified in process structure => * non-breakpoint trace => * use process flags */ tracee_flags = &ERTS_TRACE_FLAGS(p); -#ifdef ERTS_SMP - tracee = p->common.id; -#endif - if (! (*tracee_flags & F_TRACE_CALLS)) { - return; - } + if (! (*tracee_flags & F_TRACE_CALLS)) { + return; + } } else { /* Tracer not specified in process structure => * tracer specified in breakpoint => * meta trace => * use fixed flag set instead of process flags - */ + */ meta_flags = F_TRACE_CALLS | F_NOW_TS; tracee_flags = &meta_flags; -#ifdef ERTS_SMP - tracee = NIL; -#endif } - - ts_type = ERTS_TFLGS2TSTYPE(*tracee_flags); - - if (is_internal_port(*tracer_pid)) { -#define LOCAL_HEAP_SIZE (4+3+6+ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - - hp = local_heap; - mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], make_small((Eterm)mfa[2])); - hp += 4; - cv = TUPLE2(hp, class, value); - hp += 3; - mess = TUPLE5(hp, am_trace, p->common.id, am_exception_from, mfa_tuple, cv); - hp += 6; - ASSERT((hp - local_heap) <= LOCAL_HEAP_SIZE); - erts_smp_mtx_lock(&smq_mtx); - PATCH_TS(ts_type, mess, hp, NULL, NULL); - send_to_port(p, mess, tracer_pid, tracee_flags); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE - erts_smp_mtx_unlock(&smq_mtx); - } else { - ErlHeapFragment *bp; - ErlOffHeap *off_heap; - ERTS_TRACER_REF_TYPE tracer_ref; - unsigned size; - unsigned value_size; - - ASSERT(is_internal_pid(*tracer_pid)); - - ERTS_GET_TRACER_REF(tracer_ref, *tracer_pid, *tracee_flags); - - value_size = size_object(value); - size = 6 + 4 + 3 + value_size + patch_ts_size(ts_type); - - hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); - - /* - * Build the trace tuple and put it into receive queue of the tracer process. - */ - - mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], make_small((Eterm) mfa[2])); - hp += 4; - value = copy_struct(value, value_size, &hp, off_heap); - cv = TUPLE2(hp, class, value); - hp += 3; - mess = TUPLE5(hp, am_trace, p->common.id, - am_exception_from, mfa_tuple, cv); - hp += 6; - - erts_smp_mtx_lock(&smq_mtx); - PATCH_TS(ts_type, mess, hp, bp, tracer_ref); - - ERTS_ENQ_TRACE_MSG(tracee, tracer_ref, mess, bp); - erts_smp_mtx_unlock(&smq_mtx); - } + hp = HAlloc(p, 7);; + mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], make_small((Eterm)mfa[2])); + hp += 4; + cv = TUPLE2(hp, class, value); + hp += 3; + send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, + NULL, am_exception_from, mfa_tuple, cv, am_true); } /* @@ -1753,7 +988,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value, */ Uint32 erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, - Eterm* args, int local, Eterm *tracer_pid) + Eterm* args, int local, ErtsTracer *tracer) { Eterm* hp; Eterm mfa_tuple; @@ -1761,55 +996,50 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, int i; Uint32 return_flags; Eterm pam_result = am_true; - Eterm mess; Uint meta_flags, *tracee_flags; - int ts_type; -#ifdef ERTS_SMP - Eterm tracee; -#endif + ErtsTracerNif *tnif = NULL; Eterm transformed_args[MAX_ARG]; - DeclareTypedTmpHeap(ErlSubBin,sub_bin_heap,p); + ErtsTracer pre_ms_tracer = erts_tracer_nil; - ASSERT(tracer_pid); - if (*tracer_pid == am_true) { - /* Breakpoint trace enabled without specifying tracer => + ASSERT(tracer); + if (ERTS_TRACER_COMPARE(*tracer, erts_tracer_true)) { + /* Breakpoint trace enabled without specifying tracer => * use process tracer and flags */ - tracer_pid = &ERTS_TRACER_PROC(p); - } - if (is_nil(*tracer_pid)) { - /* Trace disabled */ - return 0; + tracer = &ERTS_TRACER(p); } - ASSERT(is_internal_pid(*tracer_pid) || is_internal_port(*tracer_pid)); - if (*tracer_pid == p->common.id) { - /* Do not generate trace messages to oneself */ + if (ERTS_TRACER_IS_NIL(*tracer)) { + /* Trace disabled */ return 0; } - if (tracer_pid == &ERTS_TRACER_PROC(p)) { + ASSERT(IS_TRACER_VALID(*tracer)); + if (tracer == &ERTS_TRACER(p)) { /* Tracer specified in process structure => * non-breakpoint trace => * use process flags */ tracee_flags = &ERTS_TRACE_FLAGS(p); -#ifdef ERTS_SMP - tracee = p->common.id; -#endif + if (!is_tracer_proc_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, am_call)) { + return 0; + } } else { /* Tracer not specified in process structure => * tracer specified in breakpoint => * meta trace => * use fixed flag set instead of process flags - */ - if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE) { - /* No trace messages for sensitive processes. */ - return 0; - } + */ + if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE) { + /* No trace messages for sensitive processes. */ + return 0; + } meta_flags = F_TRACE_CALLS | F_NOW_TS; tracee_flags = &meta_flags; -#ifdef ERTS_SMP - tracee = NIL; -#endif + switch (call_enabled_tracer(p, *tracer, &tnif, am_call, p->common.id)) { + default: + case am_remove: *tracer = erts_tracer_nil; + case am_discard: return 0; + case am_trace: break; + } } /* @@ -1820,18 +1050,13 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, * temporarily convert any match contexts to sub binaries. */ arity = (Eterm) mfa[2]; - UseTmpHeap(ERL_SUB_BIN_SIZE,p); -#ifdef DEBUG - sub_bin_heap->thing_word = 0; -#endif for (i = 0; i < arity; i++) { Eterm arg = args[i]; if (is_boxed(arg) && header_is_bin_matchstate(*boxed_val(arg))) { ErlBinMatchState* ms = (ErlBinMatchState *) boxed_val(arg); ErlBinMatchBuffer* mb = &ms->mb; Uint bit_size; - - ASSERT(sub_bin_heap->thing_word == 0); /* At most one of match context */ + ErlSubBin *sub_bin_heap = (ErlSubBin *)HAlloc(p, ERL_SUB_BIN_SIZE); bit_size = mb->size - mb->offset; sub_bin_heap->thing_word = HEADER_SUB_BIN; @@ -1848,275 +1073,98 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, } args = transformed_args; - ts_type = ERTS_TFLGS2TSTYPE(*tracee_flags); - - if (is_internal_port(*tracer_pid)) { - Eterm local_heap[64+ERTS_TRACE_PATCH_TS_MAX_SIZE+MAX_ARG]; - hp = local_heap; + /* + * If there is a PAM program, run it. Return if it fails. + * + * Some precedence rules: + * + * - No proc flags, e.g 'silent' or 'return_to' + * has any effect on meta trace. + * - The 'silent' process trace flag silences all call + * related messages, e.g 'call', 'return_to' and 'return_from'. + * - The {message,_} PAM function does not affect {return_trace}. + * - The {message,false} PAM function shall give the same + * 'call' trace message as no PAM match. + * - The {message,true} PAM function shall give the same + * 'call' trace message as a nonexistent PAM program. + */ - if (!erts_is_valid_tracer_port(*tracer_pid)) { -#ifdef ERTS_SMP - ASSERT(is_nil(tracee) || tracer_pid == &ERTS_TRACER_PROC(p)); - if (is_not_nil(tracee)) - erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); -#endif - *tracee_flags &= ~TRACEE_FLAGS; - *tracer_pid = NIL; -#ifdef ERTS_SMP - if (is_not_nil(tracee)) - erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR); -#endif - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return 0; - } - - /* - * If there is a PAM program, run it. Return if it fails. - * - * Some precedence rules: - * - * - No proc flags, e.g 'silent' or 'return_to' - * has any effect on meta trace. - * - The 'silent' process trace flag silences all call - * related messages, e.g 'call', 'return_to' and 'return_from'. - * - The {message,_} PAM function does not affect {return_trace}. - * - The {message,false} PAM function shall give the same - * 'call' trace message as no PAM match. - * - The {message,true} PAM function shall give the same - * 'call' trace message as a nonexistent PAM program. - */ - - /* BEGIN this code should be the same for port and pid trace */ - return_flags = 0; - if (match_spec) { - pam_result = erts_match_set_run(p, match_spec, args, arity, - ERTS_PAM_TMP_RESULT, &return_flags); - if (is_non_value(pam_result)) { - erts_match_set_release_result(p); - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return 0; - } - } - if (tracee_flags == &meta_flags) { - /* Meta trace */ - if (pam_result == am_false) { - erts_match_set_release_result(p); - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return return_flags; - } - } else { - /* Non-meta trace */ - if (*tracee_flags & F_TRACE_SILENT) { - erts_match_set_release_result(p); - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return 0; - } - if (pam_result == am_false) { - erts_match_set_release_result(p); - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return return_flags; - } - if (local && (*tracee_flags & F_TRACE_RETURN_TO)) { - return_flags |= MATCH_SET_RETURN_TO_TRACE; - } - } - /* END this code should be the same for port and pid trace */ - - /* - * Build the the {M,F,A} tuple in the local heap. - * (A is arguments or arity.) - */ - - if (*tracee_flags & F_TRACE_ARITY_ONLY) { - mfa_tuple = make_small(arity); - } else { - mfa_tuple = NIL; - for (i = arity-1; i >= 0; i--) { - mfa_tuple = CONS(hp, args[i], mfa_tuple); - hp += 2; - } - } - mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], mfa_tuple); - hp += 4; - - /* - * Build the trace tuple and send it to the port. - */ - - mess = TUPLE4(hp, am_trace, p->common.id, am_call, mfa_tuple); - hp += 5; - if (pam_result != am_true) { - hp[-5] = make_arityval(5); - *hp++ = pam_result; - } - erts_smp_mtx_lock(&smq_mtx); - PATCH_TS(ts_type, mess, hp, NULL, NULL); - send_to_port(p, mess, tracer_pid, tracee_flags); - erts_smp_mtx_unlock(&smq_mtx); - erts_match_set_release_result(p); - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return *tracer_pid == NIL ? 0 : return_flags; + return_flags = 0; + if (match_spec) { + /* we have to make a copy of the tracer here as the match spec + may remove it, and we still want to generate a trace message */ + erts_tracer_update(&pre_ms_tracer, *tracer); + tracer = &pre_ms_tracer; + pam_result = erts_match_set_run(p, match_spec, args, arity, + ERTS_PAM_TMP_RESULT, &return_flags); + if (is_non_value(pam_result)) { + erts_match_set_release_result(p); + UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); + ERTS_TRACER_CLEAR(&pre_ms_tracer); + return 0; + } + } + if (tracee_flags == &meta_flags) { + /* Meta trace */ + if (pam_result == am_false) { + erts_match_set_release_result(p); + UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); + ERTS_TRACER_CLEAR(&pre_ms_tracer); + return return_flags; + } } else { - ErlHeapFragment *bp; - ErlOffHeap *off_heap; - Process *tracer; - ERTS_TRACER_REF_TYPE tracer_ref; -#ifdef ERTS_SMP - Eterm tpid; -#endif - unsigned size; - unsigned sizes[MAX_ARG]; - unsigned pam_result_size = 0; - int invalid_tracer; + /* Non-meta trace */ + if (*tracee_flags & F_TRACE_SILENT) { + erts_match_set_release_result(p); + UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); + ERTS_TRACER_CLEAR(&pre_ms_tracer); + return 0; + } + if (pam_result == am_false) { + erts_match_set_release_result(p); + UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); + ERTS_TRACER_CLEAR(&pre_ms_tracer); + return return_flags; + } + if (local && (*tracee_flags & F_TRACE_RETURN_TO)) { + return_flags |= MATCH_SET_RETURN_TO_TRACE; + } + } - ASSERT(is_internal_pid(*tracer_pid)); - - tracer = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, - *tracer_pid, ERTS_PROC_LOCK_STATUS); - if (!tracer) - invalid_tracer = 1; - else { - invalid_tracer = !(ERTS_TRACE_FLAGS(tracer) & F_TRACER); - erts_smp_proc_unlock(tracer, ERTS_PROC_LOCK_STATUS); - } + ASSERT(!ERTS_TRACER_IS_NIL(*tracer)); - if (invalid_tracer) { -#ifdef ERTS_SMP - ASSERT(is_nil(tracee) - || tracer_pid == &ERTS_TRACER_PROC(p)); - if (is_not_nil(tracee)) - erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); -#endif - *tracee_flags &= ~TRACEE_FLAGS; - *tracer_pid = NIL; -#ifdef ERTS_SMP - if (is_not_nil(tracee)) - erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR); -#endif - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return 0; - } - -#ifdef ERTS_SMP - tpid = *tracer_pid; /* Need to save tracer pid, - since *tracer_pid might - be reset by erts_match_set_run() */ - tracer_ref = tpid; -#else - tracer_ref = tracer; -#endif - - /* - * If there is a PAM program, run it. Return if it fails. - * - * See the rules above in the port trace code. - */ - - /* BEGIN this code should be the same for port and pid trace */ - return_flags = 0; - if (match_spec) { - pam_result = erts_match_set_run(p, match_spec, args, arity, - ERTS_PAM_TMP_RESULT, &return_flags); - if (is_non_value(pam_result)) { - erts_match_set_release_result(p); - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return 0; - } - } - if (tracee_flags == &meta_flags) { - /* Meta trace */ - if (pam_result == am_false) { - erts_match_set_release_result(p); - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return return_flags; - } - } else { - /* Non-meta trace */ - if (*tracee_flags & F_TRACE_SILENT) { - erts_match_set_release_result(p); - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return 0; - } - if (pam_result == am_false) { - erts_match_set_release_result(p); - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return return_flags; - } - if (local && (*tracee_flags & F_TRACE_RETURN_TO)) { - return_flags |= MATCH_SET_RETURN_TO_TRACE; - } - } - /* END this code should be the same for port and pid trace */ - - /* - * Calculate number of words needed on heap. - */ - - size = 4 + 5; /* Trace tuple + MFA tuple. */ - if (! (*tracee_flags & F_TRACE_ARITY_ONLY)) { - size += 2*arity; - for (i = arity-1; i >= 0; i--) { - sizes[i] = size_object(args[i]); - size += sizes[i]; - } - } - size += patch_ts_size(ts_type); - if (pam_result != am_true) { - pam_result_size = size_object(pam_result); - size += 1 + pam_result_size; - /* One element in trace tuple + term size. */ - } - - hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); + /* + * Build the the {M,F,A} tuple in the local heap. + * (A is arguments or arity.) + */ - /* - * Build the the {M,F,A} tuple in the message buffer. - * (A is arguments or arity.) - */ - - if (*tracee_flags & F_TRACE_ARITY_ONLY) { - mfa_tuple = make_small(arity); - } else { - mfa_tuple = NIL; - for (i = arity-1; i >= 0; i--) { - Eterm term = copy_struct(args[i], sizes[i], &hp, off_heap); - mfa_tuple = CONS(hp, term, mfa_tuple); - hp += 2; - } - } - mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], mfa_tuple); - hp += 4; - - /* - * Copy the PAM result (if any) onto the heap. - */ - - if (pam_result != am_true) { - pam_result = copy_struct(pam_result, pam_result_size, &hp, off_heap); - } - erts_match_set_release_result(p); + if (*tracee_flags & F_TRACE_ARITY_ONLY) { + hp = HAlloc(p, 4); + mfa_tuple = make_small(arity); + } else { + hp = HAlloc(p, 4 + arity * 2); + mfa_tuple = NIL; + for (i = arity-1; i >= 0; i--) { + mfa_tuple = CONS(hp, args[i], mfa_tuple); + hp += 2; + } + } + mfa_tuple = TUPLE3(hp, (Eterm) mfa[0], (Eterm) mfa[1], mfa_tuple); + hp += 4; - /* - * Build the trace tuple and enqueue it. - */ - - mess = TUPLE4(hp, am_trace, p->common.id, am_call, mfa_tuple); - hp += 5; - if (pam_result != am_true) { - hp[-5] = make_arityval(5); - *hp++ = pam_result; - } + /* + * Build the trace tuple and send it to the port. + */ + send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, + tnif, am_call, mfa_tuple, THE_NON_VALUE, pam_result); + erts_match_set_release_result(p); - erts_smp_mtx_lock(&smq_mtx); + if (match_spec && tracer == &pre_ms_tracer) + ERTS_TRACER_CLEAR(&pre_ms_tracer); - PATCH_TS(ts_type, mess, hp, bp, tracer_ref); - ERTS_ENQ_TRACE_MSG(tracee, tracer_ref, mess, bp); - erts_smp_mtx_unlock(&smq_mtx); - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - return return_flags; - } + return return_flags; } /* Sends trace message: @@ -2128,69 +1176,13 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, * 't_p' is the traced process. */ void -trace_proc(Process *c_p, Process *t_p, Eterm what, Eterm data) +trace_proc(Process *c_p, ErtsProcLocks c_p_locks, + Process *t_p, Eterm what, Eterm data) { - Eterm mess; - Eterm* hp; - int need; - - ERTS_SMP_LC_ASSERT((erts_proc_lc_my_proc_locks(t_p) != 0) - || erts_thr_progress_is_blocking()); - if (is_internal_port(ERTS_TRACER_PROC(t_p))) { -#define LOCAL_HEAP_SIZE (5+5) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - - - hp = local_heap; - mess = TUPLE4(hp, am_trace, t_p->common.id, what, data); - hp += 5; - erts_smp_mtx_lock(&smq_mtx); - PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, NULL, NULL); - send_to_port( -#ifndef ERTS_SMP - /* No fake schedule out and in again after an exit */ - what == am_exit ? NULL : c_p, -#else - /* Fake schedule out and in are never sent when smp enabled */ - c_p, -#endif - mess, - &ERTS_TRACER_PROC(t_p), - &ERTS_TRACE_FLAGS(t_p)); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE - erts_smp_mtx_unlock(&smq_mtx); - } else { - Eterm tmp; - ErlHeapFragment *bp; - ErlOffHeap *off_heap; - ERTS_TRACER_REF_TYPE tracer_ref; - size_t sz_data; - - ASSERT(is_internal_pid(ERTS_TRACER_PROC(t_p))); - - ERTS_GET_TRACER_REF(tracer_ref, - ERTS_TRACER_PROC(t_p), - ERTS_TRACE_FLAGS(t_p)); - - sz_data = size_object(data); - - need = sz_data + 5 + PATCH_TS_SIZE(t_p); - - hp = ERTS_ALLOC_SYSMSG_HEAP(need, &bp, &off_heap, tracer_ref); - - tmp = copy_struct(data, sz_data, &hp, off_heap); - mess = TUPLE4(hp, am_trace, t_p->common.id, what, tmp); - hp += 5; - - erts_smp_mtx_lock(&smq_mtx); - - PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, bp, tracer_ref); - - ERTS_ENQ_TRACE_MSG(t_p->common.id, tracer_ref, mess, bp); - erts_smp_mtx_unlock(&smq_mtx); - } + ErtsTracerNif *tnif = NULL; + if (is_tracer_proc_enabled(c_p, c_p_locks, &t_p->common, &tnif, what)) + send_to_tracer_nif(c_p, &t_p->common, t_p->common.id, tnif, + what, data, THE_NON_VALUE); } @@ -2202,62 +1194,20 @@ trace_proc(Process *c_p, Process *t_p, Eterm what, Eterm data) * and 'args' may be a deep term. */ void -trace_proc_spawn(Process *p, Eterm pid, +trace_proc_spawn(Process *p, Eterm pid, Eterm mod, Eterm func, Eterm args) { - Eterm mfa; - Eterm mess; - Eterm* hp; - - if (is_internal_port(ERTS_TRACER_PROC(p))) { -#define LOCAL_HEAP_SIZE (4+6+5) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - - hp = local_heap; - mfa = TUPLE3(hp, mod, func, args); - hp += 4; - mess = TUPLE5(hp, am_trace, p->common.id, am_spawn, pid, mfa); - hp += 6; - erts_smp_mtx_lock(&smq_mtx); - PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL); - send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE - erts_smp_mtx_unlock(&smq_mtx); - } else { - Eterm tmp; - ErlHeapFragment *bp; - ErlOffHeap *off_heap; - ERTS_TRACER_REF_TYPE tracer_ref; - size_t sz_args, sz_pid; - Uint need; - - ASSERT(is_internal_pid(ERTS_TRACER_PROC(p))); - - ERTS_GET_TRACER_REF(tracer_ref, - ERTS_TRACER_PROC(p), - ERTS_TRACE_FLAGS(p)); - - sz_args = size_object(args); - sz_pid = size_object(pid); - need = sz_args + 4 + 6 + PATCH_TS_SIZE(p); - - hp = ERTS_ALLOC_SYSMSG_HEAP(need, &bp, &off_heap, tracer_ref); - - tmp = copy_struct(args, sz_args, &hp, off_heap); - mfa = TUPLE3(hp, mod, func, tmp); - hp += 4; - tmp = copy_struct(pid, sz_pid, &hp, off_heap); - mess = TUPLE5(hp, am_trace, p->common.id, am_spawn, tmp, mfa); - hp += 6; + ErtsTracerNif *tnif = NULL; + if (is_tracer_proc_enabled(p, ERTS_PROC_LOCKS_ALL & + ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE), + &p->common, &tnif, am_spawn)) { + Eterm mfa; + Eterm* hp; - erts_smp_mtx_lock(&smq_mtx); - - PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref); - - ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp); - erts_smp_mtx_unlock(&smq_mtx); + hp = HAlloc(p, 4); + mfa = TUPLE3(hp, mod, func, args); + hp += 4; + send_to_tracer_nif(p, &p->common, p->common.id, tnif, am_spawn, pid, mfa); } } @@ -2288,69 +1238,25 @@ void save_calls(Process *p, Export *e) * where 'HeapSize', 'OldHeapSize', 'StackSize', 'RecentSize and 'MbufSize' * are all small (atomic) integers. */ -void -trace_gc(Process *p, Eterm what) -{ - ErlHeapFragment *bp = NULL; - ErlOffHeap *off_heap; - ERTS_TRACER_REF_TYPE tracer_ref = ERTS_NULL_TRACER_REF; /* Initialized - to eliminate - compiler - warning */ - Eterm* hp; - Eterm msg = NIL; - Uint size; - -#define LOCAL_HEAP_SIZE \ - (ERTS_PROCESS_GC_INFO_MAX_SIZE) + \ - 5/*4-tuple */ + ERTS_TRACE_PATCH_TS_MAX_SIZE - DeclareTmpHeap(local_heap,LOCAL_HEAP_SIZE,p); - - UseTmpHeap(LOCAL_HEAP_SIZE,p); - - if (is_internal_port(ERTS_TRACER_PROC(p))) { - hp = local_heap; -#ifdef DEBUG - size = 0; - (void) erts_process_gc_info(p, &size, NULL); - - size += 5/*4-tuple*/ + PATCH_TS_SIZE(p); -#endif - } else { - ASSERT(is_internal_pid(ERTS_TRACER_PROC(p))); +void +trace_gc(Process *p, Eterm what) +{ + ErtsTracerNif *tnif = NULL; + Eterm* hp; + Eterm msg = NIL; + Uint size = 0; - ERTS_GET_TRACER_REF(tracer_ref, - ERTS_TRACER_PROC(p), - ERTS_TRACE_FLAGS(p)); + if (is_tracer_proc_enabled( + p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, what)) { - size = 0; (void) erts_process_gc_info(p, &size, NULL); + hp = HAlloc(p, size); - size += 5/*4-tuple*/ + PATCH_TS_SIZE(p); - - hp = ERTS_ALLOC_SYSMSG_HEAP(size, &bp, &off_heap, tracer_ref); - } - - ASSERT(size <= LOCAL_HEAP_SIZE); + msg = erts_process_gc_info(p, NULL, &hp); - msg = erts_process_gc_info(p, NULL, &hp); - - msg = TUPLE4(hp, am_trace, p->common.id, what, msg); - hp += 5; - - erts_smp_mtx_lock(&smq_mtx); - - if (is_internal_port(ERTS_TRACER_PROC(p))) { - PATCH_TS(TFLGS_TS_TYPE(p), msg, hp, NULL, NULL); - send_to_port(p, msg, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); - } - else { - PATCH_TS(TFLGS_TS_TYPE(p), msg, hp, bp, tracer_ref); - ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, msg, bp); + send_to_tracer_nif(p, &p->common, p->common.id, tnif, what, + msg, THE_NON_VALUE); } - erts_smp_mtx_unlock(&smq_mtx); - UnUseTmpHeap(LOCAL_HEAP_SIZE,p); -#undef LOCAL_HEAP_SIZE } void @@ -2412,7 +1318,7 @@ monitor_long_schedule_proc(Process *p, BeamInstr *in_fp, BeamInstr *out_fp, Uint { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(monitor_p, NULL, mp, msg, NIL); + erts_queue_message(monitor_p, NULL, mp, msg); } #endif } @@ -2477,7 +1383,7 @@ monitor_long_schedule_port(Port *pp, ErtsPortTaskType type, Uint time) { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(monitor_p, NULL, mp, msg, NIL); + erts_queue_message(monitor_p, NULL, mp, msg); } #endif } @@ -2552,7 +1458,7 @@ monitor_long_gc(Process *p, Uint time) { { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(monitor_p, NULL, mp, msg, NIL); + erts_queue_message(monitor_p, NULL, mp, msg); } #endif } @@ -2627,7 +1533,7 @@ monitor_large_heap(Process *p) { { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(monitor_p, NULL, mp, msg, NIL); + erts_queue_message(monitor_p, NULL, mp, msg); } #endif } @@ -2659,7 +1565,7 @@ monitor_generic(Process *p, Eterm type, Eterm spec) { { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(monitor_p, NULL, mp, msg, NIL); + erts_queue_message(monitor_p, NULL, mp, msg); } #endif @@ -2756,71 +1662,13 @@ profile_scheduler_q(Eterm scheduler_id, Eterm state, Eterm no_schedulers, Uint M } - -/* Send {trace_ts, Pid, What, {Mod, Func, Arity}, Timestamp} - * or {trace, Pid, What, {Mod, Func, Arity}} - * - * where 'What' is supposed to be 'in' or 'out'. - * - * Virtual scheduling do not fake scheduling for ports. - */ - - -void trace_virtual_sched(Process *p, Eterm what) -{ - trace_sched_aux(p, what, 1); -} - /* Port profiling */ void trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) { - Eterm mess; - Eterm* hp; - - if (is_internal_port(ERTS_TRACER_PROC(p))) { -#define LOCAL_HEAP_SIZE (6+ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - - hp = local_heap; - - mess = TUPLE5(hp, am_trace, calling_pid, am_open, p->common.id, drv_name); - hp += 6; - erts_smp_mtx_lock(&smq_mtx); - PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL); - /* No fake schedule */ - send_to_port(NULL, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE - erts_smp_mtx_unlock(&smq_mtx); - } else { - ErlHeapFragment *bp; - ErlOffHeap *off_heap; - size_t sz_data; - ERTS_TRACER_REF_TYPE tracer_ref; - - ASSERT(is_internal_pid(ERTS_TRACER_PROC(p))); - - sz_data = 6 + PATCH_TS_SIZE(p); - - ERTS_GET_TRACER_REF(tracer_ref, - ERTS_TRACER_PROC(p), - ERTS_TRACE_FLAGS(p)); - - hp = ERTS_ALLOC_SYSMSG_HEAP(sz_data, &bp, &off_heap, tracer_ref); - - mess = TUPLE5(hp, am_trace, calling_pid, am_open, p->common.id, drv_name); - hp += 6; - - erts_smp_mtx_lock(&smq_mtx); - - PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref); - - ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp); - erts_smp_mtx_unlock(&smq_mtx); - } + send_to_tracer_nif(NULL, &p->common, calling_pid, NULL, am_open, + p->common.id, drv_name); } /* Sends trace message: @@ -2832,53 +1680,11 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) { */ void trace_port(Port *t_p, Eterm what, Eterm data) { - Eterm mess; - Eterm* hp; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) || erts_thr_progress_is_blocking()); - - if (is_internal_port(ERTS_TRACER_PROC(t_p))) { -#define LOCAL_HEAP_SIZE (5+ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - - hp = local_heap; - mess = TUPLE4(hp, am_trace, t_p->common.id, what, data); - hp += 5; - erts_smp_mtx_lock(&smq_mtx); - PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, NULL, NULL); - /* No fake schedule */ - send_to_port(NULL,mess,&ERTS_TRACER_PROC(t_p),&ERTS_TRACE_FLAGS(t_p)); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE - erts_smp_mtx_unlock(&smq_mtx); - } else { - ErlHeapFragment *bp; - ErlOffHeap *off_heap; - size_t sz_data; - ERTS_TRACER_REF_TYPE tracer_ref; - - ASSERT(is_internal_pid(ERTS_TRACER_PROC(t_p))); - - sz_data = 5 + PATCH_TS_SIZE(t_p); - - ERTS_GET_TRACER_REF(tracer_ref, - ERTS_TRACER_PROC(t_p), - ERTS_TRACE_FLAGS(t_p)); - - hp = ERTS_ALLOC_SYSMSG_HEAP(sz_data, &bp, &off_heap, tracer_ref); - - mess = TUPLE4(hp, am_trace, t_p->common.id, what, data); - hp += 5; - - erts_smp_mtx_lock(&smq_mtx); - - PATCH_TS(TFLGS_TS_TYPE(t_p), mess, hp, bp, tracer_ref); - - ERTS_ENQ_TRACE_MSG(t_p->common.id, tracer_ref, mess, bp); - erts_smp_mtx_unlock(&smq_mtx); - } + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, NULL, + am_open, data, THE_NON_VALUE); } /* Send {trace_ts, Pid, What, {Mod, Func, Arity}, Timestamp} @@ -2891,83 +1697,14 @@ trace_port(Port *t_p, Eterm what, Eterm data) { void trace_sched_ports(Port *p, Eterm what) { - trace_sched_ports_where(p,what, make_small(0)); + trace_sched_ports_where(p, what, make_small(0)); } -void -trace_sched_ports_where(Port *p, Eterm what, Eterm where) { - Eterm mess; - Eterm* hp; - int ws = 5; - Eterm sched_id = am_undefined; - - if (is_internal_port(ERTS_TRACER_PROC(p))) { -#define LOCAL_HEAP_SIZE (6+ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - - hp = local_heap; - - if (IS_TRACED_FL(p, F_TRACE_SCHED_NO)) { -#ifdef ERTS_SMP - ErtsSchedulerData *esd = erts_get_scheduler_data(); - if (esd) sched_id = make_small(esd->no); - else sched_id = am_undefined; -#else - sched_id = make_small(1); -#endif - mess = TUPLE5(hp, am_trace, p->common.id, what, sched_id, where); - ws = 6; - } else { - mess = TUPLE4(hp, am_trace, p->common.id, what, where); - ws = 5; - } - hp += ws; - - erts_smp_mtx_lock(&smq_mtx); - - PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, NULL, NULL); - - /* No fake scheduling */ - send_to_port(NULL, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p)); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE - erts_smp_mtx_unlock(&smq_mtx); - } else { - ErlHeapFragment *bp; - ErlOffHeap *off_heap; - ERTS_TRACER_REF_TYPE tracer_ref; - - ASSERT(is_internal_pid(ERTS_TRACER_PROC(p))); - - if (IS_TRACED_FL(p, F_TRACE_SCHED_NO)) ws = 6; /* Make place for scheduler id */ - - ERTS_GET_TRACER_REF(tracer_ref, - ERTS_TRACER_PROC(p), - ERTS_TRACE_FLAGS(p)); - - hp = ERTS_ALLOC_SYSMSG_HEAP(ws+PATCH_TS_SIZE(p), &bp, &off_heap, tracer_ref); - - if (IS_TRACED_FL(p, F_TRACE_SCHED_NO)) { -#ifdef ERTS_SMP - ErtsSchedulerData *esd = erts_get_scheduler_data(); - if (esd) sched_id = make_small(esd->no); - else sched_id = am_undefined; -#else - sched_id = make_small(1); -#endif - mess = TUPLE5(hp, am_trace, p->common.id, what, sched_id, where); - } else { - mess = TUPLE4(hp, am_trace, p->common.id, what, where); - } - hp += ws; - - erts_smp_mtx_lock(&smq_mtx); +void +trace_sched_ports_where(Port *t_p, Eterm what, Eterm where) { - PATCH_TS(TFLGS_TS_TYPE(p), mess, hp, bp, tracer_ref); - ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp); - erts_smp_mtx_unlock(&smq_mtx); - } + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, + NULL, am_open, where, THE_NON_VALUE); } /* Port profiling */ @@ -3083,28 +1820,6 @@ profile_runnable_proc(Process *p, Eterm status){ #ifdef ERTS_SMP -void -erts_check_my_tracer_proc(Process *p) -{ - if (is_internal_pid(ERTS_TRACER_PROC(p))) { - Process *tracer = erts_pid2proc(p, - ERTS_PROC_LOCK_MAIN, - ERTS_TRACER_PROC(p), - ERTS_PROC_LOCK_STATUS); - int invalid_tracer = (!tracer - || !(ERTS_TRACE_FLAGS(tracer) & F_TRACER)); - if (tracer) - erts_smp_proc_unlock(tracer, ERTS_PROC_LOCK_STATUS); - if (invalid_tracer) { - erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); - ERTS_TRACE_FLAGS(p) &= ~TRACEE_FLAGS; - ERTS_TRACER_PROC(p) = NIL; - erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR); - } - } -} - - typedef struct ErtsSysMsgQ_ ErtsSysMsgQ; struct ErtsSysMsgQ_ { ErtsSysMsgQ *next; @@ -3170,24 +1885,11 @@ erts_queue_error_logger_message(Eterm from, Eterm msg, ErlHeapFragment *bp) enqueue_sys_msg(SYS_MSG_TYPE_ERRLGR, from, am_error_logger, msg, bp); } -void -erts_send_sys_msg_proc(Eterm from, Eterm to, Eterm msg, ErlHeapFragment *bp) -{ - ASSERT(is_internal_pid(to)); - enqueue_sys_msg(SYS_MSG_TYPE_PROC_MSG, from, to, msg, bp); -} - #ifdef DEBUG_PRINTOUTS static void print_msg_type(ErtsSysMsgQ *smqp) { switch (smqp->type) { - case SYS_MSG_TYPE_TRACE: - erts_fprintf(stderr, "TRACE "); - break; - case SYS_MSG_TYPE_SEQTRACE: - erts_fprintf(stderr, "SEQTRACE "); - break; case SYS_MSG_TYPE_SYSMON: erts_fprintf(stderr, "SYSMON "); break; @@ -3197,9 +1899,6 @@ print_msg_type(ErtsSysMsgQ *smqp) case SYS_MSG_TYPE_ERRLGR: erts_fprintf(stderr, "ERRLGR "); break; - case SYS_MSG_TYPE_PROC_MSG: - erts_fprintf(stderr, "PROC_MSG "); - break; default: erts_fprintf(stderr, "??? "); break; @@ -3211,17 +1910,6 @@ static void sys_msg_disp_failure(ErtsSysMsgQ *smqp, Eterm receiver) { switch (smqp->type) { - case SYS_MSG_TYPE_TRACE: - /* Invalid tracer_proc's are removed when processes - are scheduled in. */ - break; - case SYS_MSG_TYPE_SEQTRACE: - /* Reset seq_tracer if it hasn't changed */ - erts_smp_rwmtx_rwlock(&sys_trace_rwmtx); - if (system_seq_tracer == receiver) - system_seq_tracer = am_false; - erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx); - break; case SYS_MSG_TYPE_SYSMON: if (receiver == NIL && !erts_system_monitor_long_gc @@ -3281,8 +1969,6 @@ sys_msg_disp_failure(ErtsSysMsgQ *smqp, Eterm receiver) no_elgger, tag, CAR(list_val(tp[3]))); break; } - case SYS_MSG_TYPE_PROC_MSG: - break; default: ASSERT(0); } @@ -3400,13 +2086,6 @@ sys_msg_dispatcher_func(void *unused) print_msg_type(smqp); #endif switch (smqp->type) { - case SYS_MSG_TYPE_TRACE: - case SYS_MSG_TYPE_PROC_MSG: - receiver = smqp->to; - break; - case SYS_MSG_TYPE_SEQTRACE: - receiver = erts_get_system_seq_tracer(); - break; case SYS_MSG_TYPE_SYSMON: receiver = erts_get_system_monitor(); if (smqp->from == receiver) { @@ -3441,16 +2120,8 @@ sys_msg_dispatcher_func(void *unused) if (is_internal_pid(receiver)) { proc = erts_pid2proc(NULL, 0, receiver, proc_locks); - if (!proc - || (smqp->type == SYS_MSG_TYPE_TRACE - && !(ERTS_TRACE_FLAGS(proc) & F_TRACER))) { + if (!proc) { /* Bad tracer */ -#ifdef DEBUG_PRINTOUTS - if (smqp->type == SYS_MSG_TYPE_TRACE && proc) - erts_fprintf(stderr, - " "); -#endif goto failure; } else { @@ -3458,7 +2129,7 @@ sys_msg_dispatcher_func(void *unused) queue_proc_msg: mp = erts_alloc_message(0, NULL); mp->data.heap_frag = smqp->bp; - erts_queue_message(proc,&proc_locks,mp,smqp->msg,NIL); + erts_queue_message(proc,&proc_locks,mp,smqp->msg); #ifdef DEBUG_PRINTOUTS erts_fprintf(stderr, "delivered\n"); #endif @@ -3524,12 +2195,6 @@ erts_foreach_sys_msg_in_q(void (*func)(Eterm, for (sm = sys_message_queue; sm; sm = sm->next) { Eterm to; switch (sm->type) { - case SYS_MSG_TYPE_TRACE: - to = sm->to; - break; - case SYS_MSG_TYPE_SEQTRACE: - to = erts_get_system_seq_tracer(); - break; case SYS_MSG_TYPE_SYSMON: to = erts_get_system_monitor(); break; @@ -3567,3 +2232,498 @@ init_sys_msg_dispatcher(void) } #endif + +#include "erl_nif.h" + +struct ErtsTracerNif_ { + HashBucket hb; + Eterm module; + struct erl_module_nif* nif_mod; + ErlNifFunc *enabled; + ErlNifFunc *trace; +}; + +static Hash *tracer_hash = NULL; +static erts_smp_rwmtx_t tracer_mtx; + +static ErtsTracerNif * +load_tracer_nif(const ErtsTracer tracer) +{ + Module* mod = erts_get_module(ERTS_TRACER_MODULE(tracer), + erts_active_code_ix()); + struct erl_module_instance *instance; + ErlNifFunc *funcs; + int num_of_funcs; + ErtsTracerNif tnif_tmpl, *tnif; + int i; + + if (mod && mod->curr.nif != NULL) { + instance = &mod->curr; + } else { + return NULL; + } + + tnif_tmpl.enabled = NULL; + tnif_tmpl.trace = NULL; + tnif_tmpl.nif_mod = instance->nif; + tnif_tmpl.module = ERTS_TRACER_MODULE(tracer); + + num_of_funcs = erts_nif_get_funcs(instance->nif, &funcs); + + for(i = 0; i < num_of_funcs; i++) { + if (strcmp("enabled",funcs[i].name) == 0 && funcs[i].arity == 3) { + tnif_tmpl.enabled = funcs + i; + } else if (strcmp("trace",funcs[i].name) == 0 && funcs[i].arity == 6) { + tnif_tmpl.trace = funcs + i; + } + } + + if (tnif_tmpl.enabled == NULL || + tnif_tmpl.trace == NULL ) { + return NULL; + } + + erts_smp_rwmtx_rwlock(&tracer_mtx); + tnif = hash_put(tracer_hash, &tnif_tmpl); + erts_smp_rwmtx_rwunlock(&tracer_mtx); + + return tnif; +} + +static ERTS_INLINE ErtsTracerNif * +lookup_tracer_nif(const ErtsTracer tracer) +{ + ErtsTracerNif tnif_tmpl; + ErtsTracerNif *tnif; + tnif_tmpl.module = ERTS_TRACER_MODULE(tracer); + erts_smp_rwmtx_rlock(&tracer_mtx); + if ((tnif = hash_get(tracer_hash, &tnif_tmpl)) == NULL) { + erts_smp_rwmtx_runlock(&tracer_mtx); + tnif = load_tracer_nif(tracer); + ASSERT(!tnif || tnif->nif_mod); + return tnif; + } + erts_smp_rwmtx_runlock(&tracer_mtx); + ASSERT(tnif->nif_mod); + return tnif; +} + +/* This function converts an Erlang tracer term to ErtsTracer. + It returns THE_NON_VALUE if an invalid tracer term was given. + Accepted input is: + pid() || port() || {prefix, pid()} || {prefix, port()} || + {prefix, atom(), term()} || {atom(), term()} + */ +ErtsTracer +erts_term_to_tracer(Eterm prefix, Eterm t) +{ + ErtsTracer tracer = erts_tracer_nil; + ASSERT(is_atom(prefix) || prefix == THE_NON_VALUE); + if (!is_nil(t)) { + Eterm module = am_erl_tracer, state = THE_NON_VALUE; + Eterm hp[2]; + if (is_tuple(t)) { + Eterm *tp = tuple_val(t); + if (prefix != THE_NON_VALUE) { + if (arityval(tp[0]) == 2 && tp[1] == prefix) + t = tp[2]; + else if (arityval(tp[0]) == 3 && tp[1] == prefix && is_atom(tp[2])) { + module = tp[2]; + state = tp[3]; + } + } else { + if (arityval(tp[0]) == 2 && is_atom(tp[2])) { + module = tp[1]; + state = tp[2]; + } + } + } + if (state == THE_NON_VALUE && (is_internal_pid(t) || is_internal_port(t))) + state = t; + if (state == THE_NON_VALUE) + return THE_NON_VALUE; + erts_tracer_update(&tracer, CONS(hp, module, state)); + } + if (!lookup_tracer_nif(tracer)) { + ASSERT(ERTS_TRACER_MODULE(tracer) != am_erl_tracer); + ERTS_TRACER_CLEAR(&tracer); + return THE_NON_VALUE; + } + return tracer; +} + +Eterm +erts_tracer_to_term(Process *p, ErtsTracer tracer) +{ + if (ERTS_TRACER_IS_NIL(tracer)) + return am_false; + if (ERTS_TRACER_MODULE(tracer) == am_erl_tracer) + /* Have to manage these specifically in order to be + backwards compatible */ + return ERTS_TRACER_STATE(tracer); + else { + Eterm *hp = HAlloc(p, 3); + return TUPLE2(hp, ERTS_TRACER_MODULE(tracer), + copy_object(ERTS_TRACER_STATE(tracer), p)); + } +} + + +static ERTS_INLINE int +send_to_tracer_nif_raw(Process *c_p, Process *tracee, + const ErtsTracer tracer, Uint tracee_flags, + Eterm t_p_id, ErtsTracerNif *tnif, Eterm tag, Eterm msg, + Eterm extra, Eterm pam_result) +{ + if (tnif || (tnif = lookup_tracer_nif(tracer)) != NULL) { +#define MAP_SIZE 3 + Eterm argv[6], + local_heap[3+MAP_SIZE /* values */+(MAP_SIZE+1 /* keys */)]; + flatmap_t *map = (flatmap_t*)(local_heap+(MAP_SIZE+1)); + Eterm *map_values = flatmap_get_values(map); + + int argc = 6; + + argv[0] = tag; + argv[1] = ERTS_TRACER_STATE(tracer); + argv[2] = t_p_id; + argv[3] = msg; + argv[4] = extra == THE_NON_VALUE ? am_undefined : extra; + argv[5] = make_flatmap(map); + + map->thing_word = MAP_HEADER_FLATMAP; + map->size = MAP_SIZE; + map->keys = TUPLE3(local_heap, am_match_spec_result, am_scheduler_id, + am_timestamp); + + *map_values++ = pam_result; + if (tracee_flags & F_TRACE_SCHED_NO) + *map_values++ = make_small(erts_get_scheduler_id()); + else + *map_values++ = am_undefined; + if (tracee_flags & F_NOW_TS) +#ifdef HAVE_ERTS_NOW_CPU + if (erts_cpu_timestamp) + *map_values++ = am_cpu_timestamp; + else +#endif + *map_values++ = am_timestamp; + else if (tracee_flags & F_STRICT_MON_TS) + *map_values++ = am_strict_monotonic; + else if (tracee_flags & F_MON_TS) + *map_values++ = am_monotonic; + else + *map_values++ = am_undefined; + +#undef MAP_SIZE + erts_nif_call_function(c_p, tracee ? tracee : c_p, + tnif->nif_mod, tnif->trace, argc, argv); + } + return 1; +} + + +static ERTS_INLINE int +send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, + Eterm t_p_id, ErtsTracerNif *tnif, Eterm tag, + Eterm msg, Eterm extra) +{ +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) + if (c_p) { + /* We have to hold the main lock of the currently executing process */ + erts_proc_lc_chk_have_proc_locks(c_p, ERTS_PROC_LOCK_MAIN); + } + if (is_internal_pid(t_p->id)) { + /* We have to have at least one lock */ + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks((Process*)t_p) & ERTS_PROC_LOCKS_ALL); + } else { + ASSERT(is_internal_port(t_p->id)); + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked((Port*)t_p)); + } +#endif + + return send_to_tracer_nif_raw(c_p, + is_internal_pid(t_p->id) ? (Process*)t_p : NULL, + t_p->tracer, t_p->trace_flags, + t_p_id, tnif, tag, msg, extra, + am_true); +} + +static ERTS_INLINE Eterm +call_enabled_tracer(Process *c_p, const ErtsTracer tracer, + ErtsTracerNif **tnif_ret, Eterm tag, Eterm t_p_id) +{ + ErtsTracerNif *tnif = lookup_tracer_nif(tracer); + if (tnif) { + Eterm argv[] = {tag, ERTS_TRACER_STATE(tracer), t_p_id}; + if (tnif_ret) *tnif_ret = tnif; + return erts_nif_call_function( + c_p, NULL, tnif->nif_mod, tnif->enabled, 3, argv); + } + return am_remove; +} + +static int +is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, + ErtsPTabElementCommon *t_p, + ErtsTracerNif **tnif_ret, Eterm tag) +{ + Eterm nif_result; + +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) + if (c_p) + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == c_p_locks + || erts_thr_progress_is_blocking()); + if (is_internal_pid(t_p->id)) { + /* We have to have at least one lock */ + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks((Process*)t_p) & ERTS_PROC_LOCKS_ALL + || erts_thr_progress_is_blocking()); + } else { + ASSERT(is_internal_port(t_p->id)); + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked((Port*)t_p) + || erts_thr_progress_is_blocking()); + } +#endif + + nif_result = call_enabled_tracer(c_p, t_p->tracer, tnif_ret, tag, t_p->id); + switch (nif_result) { + case am_discard: return 0; + case am_trace: return 1; + case THE_NON_VALUE: + case am_remove: break; + default: + /* only am_remove should be returned, but if + something else is returned we fall-through + and remove the tracer. */ + ASSERT(0); + } + + /* Only remove tracer on self() and ports */ + if (is_internal_port(t_p->id) || (c_p && c_p->common.id == t_p->id)) { + ErtsProcLocks c_p_xlocks = 0; + if (is_internal_pid(t_p->id)) { + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN); + if (c_p_locks != ERTS_PROC_LOCKS_ALL) { + c_p_xlocks = ~c_p_locks & ERTS_PROC_LOCKS_ALL; + if (erts_smp_proc_trylock(c_p, c_p_xlocks) == EBUSY) { + erts_smp_proc_unlock(c_p, c_p_locks & ~ERTS_PROC_LOCK_MAIN); + erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); + } + } + } + erts_tracer_replace(t_p, erts_tracer_nil); + t_p->trace_flags &= ~TRACEE_FLAGS; + + if (c_p_xlocks) + erts_smp_proc_unlock(c_p, c_p_xlocks); + } + + + return 0; +} + +int erts_is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, + ErtsPTabElementCommon *t_p) +{ + return is_tracer_proc_enabled(c_p, c_p_locks, t_p, NULL, am_trace_status); +} + +int erts_is_tracer_enabled(Process *c_p, const ErtsTracer tracer) +{ + ErtsTracerNif *tnif = lookup_tracer_nif(tracer); + if (tnif) { + Eterm nif_result = call_enabled_tracer(c_p, tracer, &tnif, + am_trace_status, + c_p->common.id); + switch (nif_result) { + case am_discard: + case am_trace: return 1; + default: + break; + } + } + return 0; +} + +void erts_tracer_replace(ErtsPTabElementCommon *t_p, const ErtsTracer tracer) +{ +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) + if (is_internal_pid(t_p->id) && !erts_thr_progress_is_blocking()) { + erts_proc_lc_chk_have_proc_locks((Process*)t_p, ERTS_PROC_LOCKS_ALL); + } else if (is_internal_port(t_p->id)) { + ERTS_LC_ASSERT(erts_lc_is_port_locked((Port*)t_p) + || erts_thr_progress_is_blocking()); + } +#endif + if (ERTS_TRACER_COMPARE(t_p->tracer, tracer)) + return; + + erts_tracer_update(&t_p->tracer, tracer); +} + +static void free_tracer(void *p) +{ + ErtsTracer tracer = (ErtsTracer)p; + + if (is_immed(ERTS_TRACER_STATE(tracer))) { + erts_free(ERTS_ALC_T_HEAP_FRAG, ptr_val(tracer)); + } else { + ErlHeapFragment *hf = (void*)((char*)(ptr_val(tracer)) - offsetof(ErlHeapFragment, mem)); + free_message_buffer(hf); + } +} + +/* un-define erts_tracer_update before implementation */ +#ifdef erts_tracer_update +#undef erts_tracer_update +#endif + +/* + * ErtsTracer is either NIL, 'true' or [Mod | State] + * + * - If State is immediate then the memory for + * the cons cell is just two words + sizeof(ErtsThrPrgrLaterOp) large. + * - If State is a complex term then the cons cell + * is allocated in an ErlHeapFragment where the cons + * ptr points to the mem field. So in order to get the + * ptr to the fragment you do this: + * (char*)(ptr_val(tracer)) - offsetof(ErlHeapFragment, mem) + * Normally you shouldn't have to care about this though + * as erts_tracer_update takes care of it for you. + * + * When ErtsTracer is stored in the stack as part of a + * return trace, the cons cell is stored on the heap of + * the process. + * + * The cons cell is not always stored on the heap as: + * 1) for port/meta tracing there is no heap + * 2) we would need the main lock in order to + * read the tracer which is undesirable. + * + * One way to optimize this (memory wise) is to keep an refc and only bump + * the refc when *tracer is NIL. + */ +void +erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer) +{ + ErlHeapFragment *hf; + + if (is_not_nil(*tracer)) { + Uint offs = 2; + UWord size = 2 * sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp); + ASSERT(is_list(*tracer)); + if (is_not_immed(ERTS_TRACER_STATE(*tracer))) { + hf = (void*)(((char*)(ptr_val(*tracer)) - offsetof(ErlHeapFragment, mem))); + offs = hf->used_size; + size = hf->alloc_size * sizeof(Eterm) + sizeof(ErlHeapFragment); + ASSERT(offs == size_object(*tracer)); + } + /* We schedule the free:ing of the tracer until after a thread progress + has been made so that we know that no schedulers have any references + to it. Because we do this, it is possible to release all locks of a + process/port and still use the ErtsTracer of that port/process + without having to worry if it is free'd. + */ + erts_schedule_thr_prgr_later_cleanup_op( + free_tracer, (void*)(*tracer), + (ErtsThrPrgrLaterOp*)(ptr_val(*tracer) + offs), + size); + } + + if (is_nil(new_tracer)) { + *tracer = new_tracer; + } else if (is_immed(ERTS_TRACER_STATE(new_tracer))) { + /* If tracer state is an immediate we only allocate a 2 Eterm heap. + Not sure if it is worth it, we save 4 words (sizeof(ErlHeapFragment)) + per tracer. */ + Eterm *hp = erts_alloc(ERTS_ALC_T_HEAP_FRAG, + 2*sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp)); + *tracer = CONS(hp, ERTS_TRACER_MODULE(new_tracer), + ERTS_TRACER_STATE(new_tracer)); + } else { + Eterm *hp, tracer_state = ERTS_TRACER_STATE(new_tracer), + tracer_module = ERTS_TRACER_MODULE(new_tracer); + Uint sz = size_object(tracer_state); + hf = new_message_buffer(sz + 2 /* cons cell */ + (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm)); + hp = hf->mem + 2; + hf->used_size -= (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm); + *tracer = copy_struct(tracer_state, sz, &hp, &hf->off_heap); + *tracer = CONS(hf->mem, tracer_module, *tracer); + ASSERT((void*)(((char*)(ptr_val(*tracer)) - offsetof(ErlHeapFragment, mem))) == hf); + } +} + +static void init_tracer_nif() +{ + erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; + rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ; + rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED; + erts_smp_rwmtx_init_opt(&tracer_mtx, &rwmtx_opt, "tracer_mtx"); + + erts_tracer_nif_clear(); + +} + +int erts_tracer_nif_clear() +{ + + erts_smp_rwmtx_rlock(&tracer_mtx); + if (!tracer_hash || tracer_hash->nobjs) { + + HashFunctions hf; + hf.hash = tracer_hash_fun; + hf.cmp = tracer_cmp_fun; + hf.alloc = tracer_alloc_fun; + hf.free = tracer_free_fun; + hf.meta_alloc = (HMALLOC_FUN) erts_alloc; + hf.meta_free = (HMFREE_FUN) erts_free; + hf.meta_print = (HMPRINT_FUN) erts_print; + + erts_smp_rwmtx_runlock(&tracer_mtx); + erts_smp_rwmtx_rwlock(&tracer_mtx); + + if (tracer_hash) + hash_delete(tracer_hash); + + tracer_hash = hash_new(ERTS_ALC_T_TRACER_NIF, "tracer_hash", 10, hf); + + erts_smp_rwmtx_rwunlock(&tracer_mtx); + return 1; + } + + erts_smp_rwmtx_runlock(&tracer_mtx); + return 0; +} + +static int tracer_cmp_fun(void* a, void* b) +{ + return ((ErtsTracerNif*)a)->module != ((ErtsTracerNif*)b)->module; +} + +static HashValue tracer_hash_fun(void* obj) +{ + return make_internal_hash(((ErtsTracerNif*)obj)->module); +} + +static void *tracer_alloc_fun(void* tmpl) +{ + ErtsTracerNif *obj = erts_alloc(ERTS_ALC_T_TRACER_NIF, + sizeof(ErtsTracerNif) + + sizeof(ErtsThrPrgrLaterOp)); + memcpy(obj, tmpl, sizeof(*obj)); + return obj; +} + +static void tracer_free_fun_cb(void* obj) +{ + erts_free(ERTS_ALC_T_TRACER_NIF, obj); +} + +static void tracer_free_fun(void* obj) +{ + ErtsTracerNif *tnif = obj; + erts_schedule_thr_prgr_later_op( + tracer_free_fun_cb, obj, + (ErtsThrPrgrLaterOp*)(tnif + 1)); + +} diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index 1da27ee128..61f82a6b93 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -46,6 +46,8 @@ #define ERTS_SEQTFLGS2TSTYPE(SEQTFLGS) \ ((int) (((SEQTFLGS) >> ERTS_SEQ_TRACE_FLAGS_TS_TYPE_SHIFT) \ & ERTS_TRACE_TS_TYPE_MASK)) +#define ERTS_SEQTFLGS2TFLGS(SEQTFLGS) \ + (ERTS_SEQTFLGS2TSTYPE(SEQTFLGS) << ERTS_TRACE_FLAGS_TS_TYPE_SHIFT) #endif /* ERL_TRACE_H__FLAGS__ */ @@ -62,15 +64,16 @@ void erts_system_profile_clear(Process *c_p); /* erl_trace.c */ void erts_init_trace(void); void erts_trace_check_exiting(Eterm exiting); -Eterm erts_set_system_seq_tracer(Process *c_p, - ErtsProcLocks c_p_locks, - Eterm new); -Eterm erts_get_system_seq_tracer(void); -void erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp); -void erts_get_default_tracing(Uint *flagsp, Eterm *tracerp); +ErtsTracer erts_set_system_seq_tracer(Process *c_p, + ErtsProcLocks c_p_locks, + ErtsTracer new); +ErtsTracer erts_get_system_seq_tracer(void); +void erts_change_default_tracing(int setflags, Uint *flagsp, + const ErtsTracer tracerp); +void erts_get_default_tracing(Uint *flagsp, ErtsTracer *tracerp); void erts_set_system_monitor(Eterm monitor); Eterm erts_get_system_monitor(void); -int erts_is_tracer_proc_valid(Process* p); +int erts_is_tracer_valid(Process* p); #ifdef ERTS_SMP void erts_check_my_tracer_proc(Process *); @@ -83,22 +86,22 @@ void erts_foreach_sys_msg_in_q(void (*func)(Eterm, void erts_queue_error_logger_message(Eterm, Eterm, ErlHeapFragment *); #endif -void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *); void trace_send(Process*, Eterm, Eterm); -void trace_receive(Process*, Eterm); -Uint32 erts_call_trace(Process *p, BeamInstr mfa[], struct binary *match_spec, Eterm* args, - int local, Eterm *tracer_pid); -void erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid); +void trace_receive(Process *, Eterm); +Uint32 erts_call_trace(Process *p, BeamInstr mfa[], struct binary *match_spec, + Eterm* args, int local, ErtsTracer *tracer); +void erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, + ErtsTracer *tracer); void erts_trace_exception(Process* p, BeamInstr mfa[], Eterm class, Eterm value, - Eterm *tracer); + ErtsTracer *tracer); void erts_trace_return_to(Process *p, BeamInstr *pc); -void trace_sched(Process*, Eterm); -void trace_proc(Process*, Process*, Eterm, Eterm); +void trace_sched(Process*, ErtsProcLocks, Eterm); +void trace_proc(Process*, ErtsProcLocks, Process*, Eterm, Eterm); void trace_proc_spawn(Process*, Eterm pid, Eterm mod, Eterm func, Eterm args); void save_calls(Process *p, Export *); void trace_gc(Process *p, Eterm what); /* port tracing */ -void trace_virtual_sched(Process*, Eterm); +void trace_virtual_sched(Process*, ErtsProcLocks, Eterm); void trace_sched_ports(Port *pp, Eterm); void trace_sched_ports_where(Port *pp, Eterm, Eterm); void trace_port(Port *, Eterm what, Eterm data); @@ -121,7 +124,7 @@ void monitor_large_heap(Process *p); void monitor_generic(Process *p, Eterm type, Eterm spec); Uint erts_trace_flag2bit(Eterm flag); int erts_trace_flags(Eterm List, - Uint *pMask, Eterm *pTracer, int *pCpuTimestamp); + Uint *pMask, ErtsTracer *pTracer, int *pCpuTimestamp); Eterm erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr *I); #ifdef ERTS_SMP @@ -161,15 +164,52 @@ int erts_set_trace_pattern(Process*p, Eterm* mfa, int specified, struct binary* match_prog_set, struct binary *meta_match_prog_set, int on, struct trace_pattern_flags, - Eterm meta_tracer_pid, int is_blocking); + ErtsTracer meta_tracer, int is_blocking); void erts_get_default_trace_pattern(int *trace_pattern_is_on, struct binary **match_spec, struct binary **meta_match_spec, struct trace_pattern_flags *trace_pattern_flags, - Eterm *meta_tracer_pid); + ErtsTracer *meta_tracer); int erts_is_default_trace_enabled(void); void erts_bif_trace_init(void); int erts_finish_breakpointing(void); +/* Nif tracer functions */ +int erts_is_tracer_proc_enabled(Process *c_p, ErtsProcLocks c_p_locks, + ErtsPTabElementCommon *t_p); +int erts_is_tracer_enabled(Process *c_p, const ErtsTracer tracer); +Eterm erts_tracer_to_term(Process *p, ErtsTracer tracer); +ErtsTracer erts_term_to_tracer(Eterm prefix, Eterm term); +void erts_tracer_replace(ErtsPTabElementCommon *t_p, + const ErtsTracer new_tracer); +void erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer); +int erts_tracer_nif_clear(void); + +#define erts_tracer_update(t,n) do { if (*(t) != (n)) erts_tracer_update(t,n); } while(0) +#define ERTS_TRACER_CLEAR(t) erts_tracer_update(t, erts_tracer_nil) + +static const ErtsTracer +ERTS_DECLARE_DUMMY(erts_tracer_true) = am_true; + +static const ErtsTracer +ERTS_DECLARE_DUMMY(erts_tracer_nil) = NIL; + +#define ERTS_TRACER_COMPARE(t1, t2) \ + (EQ((t1), (t2))) + +#define ERTS_TRACER_IS_NIL(t1) ERTS_TRACER_COMPARE(t1, erts_tracer_nil) + +#define IS_TRACER_VALID(tracer) \ + (ERTS_TRACER_COMPARE(tracer,erts_tracer_true) \ + || ERTS_TRACER_IS_NIL(tracer) \ + || (is_list(tracer) && is_atom(CAR(list_val(tracer))))) + +#define ERTS_TRACER_FROM_ETERM(termp) \ + ((ErtsTracer*)(termp)) + +#define ERTS_TRACER_PROC_IS_ENABLED(PROC) \ + (!ERTS_TRACER_IS_NIL(ERTS_TRACER(PROC)) \ + && erts_is_tracer_proc_enabled(PROC, ERTS_PROC_LOCK_MAIN, &(PROC)->common)) + #endif /* ERL_TRACE_H__ */ diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d index 73ef5a108a..237889e0f5 100644 --- a/erts/emulator/beam/erlang_dtrace.d +++ b/erts/emulator/beam/erlang_dtrace.d @@ -700,6 +700,35 @@ provider erlang { */ probe efile_drv__return(int, int, char *, int, int, int); + +/* + * The set of probes called by the erlang tracer nif backend. In order + * to receive events on these you both have to enable tracing in erlang + * using the trace bifs and also from dtrace/systemtap. + */ + + + /** + * A trace message of type `event` was triggered by process `p`. + * + * + * @param p the PID (string form) of the process + * @param event the event that was triggered (i.e. call or spawn) + * @param state the state of the tracer nif as a string + * @param arg1 first argument to the trace event + * @param arg2 second argument to the trace event + */ + probe trace(char *p, char *event, char *state, char *arg1, char *arg2); + + /** + * A sequence trace message of type `label` was triggered. + * + * @param state the state of the tracer nif as a string + * @param label the seq trace label + * @param seq_info the seq trace info tuple as a string + */ + probe trace_seq(char *state, char *label, char *seq_info); + /* * NOTE: * For formatting int64_t arguments within a D script, see: diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 1d3704f0af..5c2e8bbd09 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -44,6 +44,8 @@ #include "erl_port.h" #include "erl_gc.h" +struct enif_func_t; + struct enif_environment_t /* ErlNifEnv */ { struct erl_module_nif* mod_nif; @@ -54,6 +56,7 @@ struct enif_environment_t /* ErlNifEnv */ int fpe_was_unmasked; struct enif_tmp_obj_t* tmp_obj_list; int exception_thrown; /* boolean */ + Process *tracee; }; extern void erts_pre_nif(struct enif_environment_t*, Process*, struct erl_module_nif*); @@ -62,6 +65,12 @@ extern Eterm erts_nif_taints(Process* p); extern void erts_print_nif_taints(int to, void* to_arg); void erts_unload_nif(struct erl_module_nif* nif); extern void erl_nif_init(void); +extern int erts_nif_get_funcs(struct erl_module_nif*, + struct enif_func_t **funcs); +extern Eterm erts_nif_call_function(Process *p, Process *tracee, + struct erl_module_nif*, + struct enif_func_t *, + int argc, Eterm *argv); /* Driver handle (wrapper for old plain handle) */ #define ERL_DE_OK 0 @@ -1532,10 +1541,15 @@ ERTS_GLB_INLINE void dtrace_fun_decode(Process *process, ERTS_GLB_INLINE void dtrace_pid_str(Eterm pid, char *process_buf) { - erts_snprintf(process_buf, DTRACE_TERM_BUF_SIZE, "<%lu.%lu.%lu>", - pid_channel_no(pid), - pid_number(pid), - pid_serial(pid)); + if (is_pid(pid)) + erts_snprintf(process_buf, DTRACE_TERM_BUF_SIZE, "<%lu.%lu.%lu>", + pid_channel_no(pid), + pid_number(pid), + pid_serial(pid)); + else if (is_port(pid)) + erts_snprintf(process_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>", + port_channel_no(pid), + port_number(pid)); } ERTS_GLB_INLINE void @@ -1547,9 +1561,7 @@ dtrace_proc_str(Process *process, char *process_buf) ERTS_GLB_INLINE void dtrace_port_str(Port *port, char *port_buf) { - erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>", - port_channel_no(port->common.id), - port_number(port->common.id)); + dtrace_pid_str(port->common.id, port_buf); } ERTS_GLB_INLINE void diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index de73da8e22..a35c305046 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -212,6 +212,7 @@ static ERTS_INLINE void kill_port(Port *pp) { ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp)); + ERTS_TRACER_CLEAR(&ERTS_TRACER(pp)); erts_ptab_delete_element(&erts_port, &pp->common); /* Time of death */ erts_port_task_free_port(pp); /* In non-smp case the port structure may have been deallocated now */ @@ -404,7 +405,7 @@ static Port *create_port(char *name, prt->os_pid = -1; /* Set default tracing */ - erts_get_default_tracing(&ERTS_TRACE_FLAGS(prt), &ERTS_TRACER_PROC(prt)); + erts_get_default_tracing(&ERTS_TRACE_FLAGS(prt), &ERTS_TRACER(prt)); ERTS_CT_ASSERT(offsetof(Port,common) == 0); @@ -1300,7 +1301,7 @@ try_imm_drv_call(ErtsTryImmDrvCallState *sp) reds_left_in = CONTEXT_REDS/10; else { if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS)) - trace_virtual_sched(c_p, am_out); + trace_sched(c_p, ERTS_PROC_LOCK_MAIN, am_out); /* * No status lock held while sending runnable * proc trace messages. It is however not needed @@ -1384,7 +1385,7 @@ finalize_imm_drv_call(ErtsTryImmDrvCallState *sp) } if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS)) - trace_virtual_sched(c_p, am_in); + trace_sched(c_p, ERTS_PROC_LOCK_MAIN, am_in); /* * No status lock held while sending runnable * proc trace messages. It is however not needed @@ -1447,7 +1448,7 @@ queue_port_sched_op_reply(Process *rp, erts_factory_trim_and_close(factory, &msg, 1); - erts_queue_message(rp, rp_locksp, factory->message, msg, NIL); + erts_queue_message(rp, rp_locksp, factory->message, msg); } static void @@ -1535,9 +1536,8 @@ erts_schedule_proc2port_signal(Process *c_p, ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); c_p->msg.save = c_p->msg.last; - erts_smp_proc_unlock(c_p, - (ERTS_PROC_LOCK_MAIN - | ERTS_PROC_LOCKS_MSG_RECEIVE)); + erts_smp_proc_unlock(c_p, (ERTS_PROC_LOCKS_MSG_RECEIVE + | ERTS_PROC_LOCK_MAIN)); } @@ -2948,7 +2948,7 @@ port_link_failure(Eterm port_id, Eterm linker) if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) { /* We didn't exit the process and it is traced */ if (IS_TRACED_FL(rp, F_TRACE_PROCS)) - trace_proc(NULL, rp, am_getting_unlinked, port_id); + trace_proc(NULL, 0, rp, am_getting_unlinked, port_id); } if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); @@ -3417,7 +3417,7 @@ deliver_result(Eterm sender, Eterm pid, Eterm res) sz_res + 3, &hp, &ohp); res = copy_struct(res, sz_res, &hp, ohp); tuple = TUPLE2(hp, sender, res); - erts_queue_message(rp, &rp_locks, mp, tuple, NIL); + erts_queue_message(rp, &rp_locks, mp, tuple); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); @@ -3513,7 +3513,8 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to, tuple = TUPLE2(hp, prt->common.id, tuple); hp += 3; - erts_queue_message(rp, &rp_locks, mp, tuple, am_undefined); + ERL_MESSAGE_TOKEN(mp) = am_undefined; + erts_queue_message(rp, &rp_locks, mp, tuple); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) @@ -3680,7 +3681,8 @@ deliver_vec_message(Port* prt, /* Port */ tuple = TUPLE2(hp, prt->common.id, tuple); hp += 3; - erts_queue_message(rp, &rp_locks, mp, tuple, am_undefined); + ERL_MESSAGE_TOKEN(mp) = am_undefined; + erts_queue_message(rp, &rp_locks, mp, tuple); erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) erts_proc_dec_refc(rp); @@ -3939,7 +3941,7 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc) if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) { /* We didn't exit the process and it is traced */ if (IS_TRACED_FL(rp, F_TRACE_PROCS)) { - trace_proc(NULL, rp, am_getting_unlinked, + trace_proc(NULL, 0, rp, am_getting_unlinked, psc->port); } } @@ -5036,7 +5038,7 @@ reply_io_bytes(void *vreq) eout = erts_bld_uint64(&hp, NULL, out); msg = TUPLE4(hp, ref, make_small(sched_id), ein, eout); - erts_queue_message(rp, &rp_locks, mp, msg, NIL); + erts_queue_message(rp, &rp_locks, mp, msg); if (req->sched_id == sched_id) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -5529,7 +5531,8 @@ void driver_report_exit(ErlDrvPort ix, int status) hp += 3; tuple = TUPLE2(hp, prt->common.id, tuple); - erts_queue_message(rp, &rp_locks, mp, tuple, am_undefined); + ERL_MESSAGE_TOKEN(mp) = am_undefined; + erts_queue_message(rp, &rp_locks, mp, tuple); erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) @@ -6123,7 +6126,8 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) mess = ESTACK_POP(stack); /* get resulting value */ erts_factory_trim_and_close(&factory, &mess, 1); /* send message */ - erts_queue_message(rp, &rp_locks, factory.message, mess, am_undefined); + ERL_MESSAGE_TOKEN(factory.message) = am_undefined; + erts_queue_message(rp, &rp_locks, factory.message, mess); } else { if (b2t.ix > b2t.used) diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c index 071f6aee08..f4a889b8f8 100644 --- a/erts/emulator/beam/register.c +++ b/erts/emulator/beam/register.c @@ -226,7 +226,8 @@ int erts_register_name(Process *c_p, Eterm name, Eterm id) rp = (RegProc*) hash_put(&process_reg, (void*) &r); if (proc && rp->p == proc) { if (IS_TRACED_FL(proc, F_TRACE_PROCS)) { - trace_proc(c_p, proc, am_register, name); + trace_proc(proc, ERTS_PROC_LOCK_MAIN, + proc, am_register, name); } proc->common.u.alive.reg = rp; } @@ -470,8 +471,8 @@ int erts_unregister_name(Process *c_p, int res = 0; RegProc r, *rp; Port *port = c_prt; + ErtsProcLocks current_c_p_locks = 0; #ifdef ERTS_SMP - ErtsProcLocks current_c_p_locks; /* * SMP note: If 'c_prt != NULL' and 'c_prt->reg->name == name', @@ -555,7 +556,8 @@ int erts_unregister_name(Process *c_p, #endif rp->p->common.u.alive.reg = NULL; if (IS_TRACED_FL(rp->p, F_TRACE_PROCS)) { - trace_proc(c_p, rp->p, am_unregister, r.name); + trace_proc(c_p, current_c_p_locks, + rp->p, am_unregister, r.name); } #ifdef ERTS_SMP if (rp->p != c_p) { diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 8b3eb2db1d..748fba15c7 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -644,6 +644,15 @@ typedef struct preload { unsigned char* code; /* Code pointer */ } Preload; +/* + * ErtsTracer is either NIL, 'true' or [Mod | State] + * + * If set to NIL, it means no tracer. + * If set to 'true' it means the current process' tracer. + * If set to [Mod | State], there is a tracer. + * See erts_tracer_update for more details + */ +typedef Eterm ErtsTracer; /* * This structure contains options to all built in drivers. diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 384b340a1c..b280995488 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2298,7 +2298,7 @@ static void do_send_logger_message(Eterm *hp, ErlOffHeap *ohp, ErlHeapFragment * { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(p, NULL /* only used for smp build */, mp, message, NIL); + erts_queue_message(p, NULL /* only used for smp build */, mp, message); } #endif } diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c index ba41f34012..ace489452f 100644 --- a/erts/emulator/hipe/hipe_debug.c +++ b/erts/emulator/hipe/hipe_debug.c @@ -195,7 +195,7 @@ void hipe_print_pcb(Process *p) U("rcount ", rcount); U("id ", common.id); U("reds ", reds); - U("tracer_pr..", common.tracer_proc); + U("tracer ", common.tracer); U("trace_fla..", common.trace_flags); U("group_lea..", group_leader); U("flags ", flags); diff --git a/erts/emulator/nifs/common/erl_tracer_nif.c b/erts/emulator/nifs/common/erl_tracer_nif.c new file mode 100644 index 0000000000..a1e0e581a4 --- /dev/null +++ b/erts/emulator/nifs/common/erl_tracer_nif.c @@ -0,0 +1,250 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson 2015. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +/* + * Purpose: NIF library for process/port tracer + * + */ + + +#define STATIC_ERLANG_NIF 1 + +#include "erl_nif.h" +#include "config.h" +#include "sys.h" + +#ifdef VALGRIND +# include +#endif + +/* NIF interface declarations */ +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); +static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info); +static void unload(ErlNifEnv* env, void* priv_data); + +/* The NIFs: */ +static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +static ErlNifFunc nif_funcs[] = { + {"enabled", 3, enabled}, + {"trace", 6, trace} +}; + + +ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload) + +#define ATOMS \ + ATOM_DECL(call); \ + ATOM_DECL(command); \ + ATOM_DECL(cpu_timestamp); \ + ATOM_DECL(discard); \ + ATOM_DECL(exception_from); \ + ATOM_DECL(match_spec_result); \ + ATOM_DECL(monotonic); \ + ATOM_DECL(ok); \ + ATOM_DECL(remove); \ + ATOM_DECL(return_from); \ + ATOM_DECL(scheduler_id); \ + ATOM_DECL(send); \ + ATOM_DECL(send_to_non_existing_process); \ + ATOM_DECL(seq_trace); \ + ATOM_DECL(spawn); \ + ATOM_DECL(strict_monotonic); \ + ATOM_DECL(timestamp); \ + ATOM_DECL(trace); \ + ATOM_DECL(trace_ts); \ + ATOM_DECL(true); \ + ATOM_DECL(undefined); + +#define ATOM_DECL(A) static ERL_NIF_TERM atom_##A +ATOMS +#undef ATOM_DECL + +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + +#define ATOM_DECL(A) atom_##A = enif_make_atom(env, #A) +ATOMS +#undef ATOM_DECL + + *priv_data = NULL; + + return 0; +} + +static void unload(ErlNifEnv* env, void* priv_data) +{ + +} + +static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, + ERL_NIF_TERM load_info) +{ + if (*old_priv_data != NULL) { + return -1; /* Don't know how to do that */ + } + if (*priv_data != NULL) { + return -1; /* Don't know how to do that */ + } + if (load(env, priv_data, load_info)) { + return -1; + } + return 0; +} + +static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifPid to_pid; + ErlNifPort to_port; + ASSERT(argc == 3); + + if (enif_get_local_pid(env, argv[1], &to_pid)) { + if (!enif_is_process_alive(env, &to_pid)) + /* tracer is dead so we should remove this trace point */ + return atom_remove; + } else if (enif_get_local_port(env, argv[1], &to_port)) { + if (!enif_is_port_alive(env, &to_port)) + /* tracer is dead so we should remove this trace point */ + return atom_remove; + } + + /* Only generate trace for when tracer != tracee */ + if (enif_is_identical(argv[1], argv[2])) { + return atom_discard; + } + + return atom_trace; +} + +/* + -spec trace(seq_trace, TracerState :: pid() | port(), + Label :: non_neg_integer(), + Msg :: term(), + Opts :: map()) -> ignored(); + trace(Tag :: atom(), TracerState :: pid() | port(), + Tracee :: pid() || port() || undefined, + Msg :: term(), + Opts :: map()) -> ignored(). + -spec trace(Tag :: atom(), TracerState :: pid() | port(), + Tracee :: pid() || port() || undefined, + Msg :: term(), + Extra :: term(), + Opts :: map()) -> ignored(). +*/ +static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM value, msg, tt[8], opts; + ErlNifPid to_pid; + ErlNifPort to_port; + size_t tt_sz = 0; + int is_port = 0; + ASSERT(argc == 6); + + if (!enif_get_local_pid(env, argv[1], &to_pid)) { + if (!enif_get_local_port(env, argv[1], &to_port)) { + /* This only fails if argv[1] is a not a local port/pid + which should not happen as it is checked in enabled */ + ASSERT(0); + return atom_ok; + } + is_port = 1; + } + + if (!enif_is_identical(argv[4], atom_undefined)) { + tt[tt_sz++] = atom_trace; + tt[tt_sz++] = argv[2]; + tt[tt_sz++] = argv[0]; + tt[tt_sz++] = argv[3]; + tt[tt_sz++] = argv[4]; + } else { + if (enif_is_identical(argv[0], atom_seq_trace)) { + tt[tt_sz++] = atom_seq_trace; + tt[tt_sz++] = argv[2]; + tt[tt_sz++] = argv[3]; + } else { + tt[tt_sz++] = atom_trace; + tt[tt_sz++] = argv[2]; + tt[tt_sz++] = argv[0]; + tt[tt_sz++] = argv[3]; + } + } + + opts = argv[5]; + + if (enif_get_map_value(env, opts, atom_match_spec_result, + &value) + && !enif_is_identical(value, atom_true)) { + tt[tt_sz++] = value; + } + + if (enif_get_map_value(env, opts, atom_scheduler_id, &value) + && !enif_is_identical(value, atom_undefined)) { + tt[tt_sz++] = value; + } + + if (enif_get_map_value(env, opts, atom_timestamp, &value) + && !enif_is_identical(value, atom_undefined)) { + ERL_NIF_TERM ts; + if (enif_is_identical(value, atom_monotonic)) { + ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC); + ts = enif_make_int64(env, mon); + } else if (enif_is_identical(value, atom_strict_monotonic)) { + ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC); + ERL_NIF_TERM unique = enif_make_unique_integer( + env, ERL_NIF_UNIQUE_MONOTONIC); + ts = enif_make_tuple2(env, enif_make_int64(env, mon), unique); + } else if (enif_is_identical(value, atom_timestamp)) { + ts = enif_now_time(env); + } else if (enif_is_identical(value, atom_cpu_timestamp)) { + ts = enif_cpu_time(env); + } else { + ASSERT(0); + goto error; + } + tt[tt_sz++] = ts; + if (tt[0] == atom_trace) + tt[0] = atom_trace_ts; + } + + msg = enif_make_tuple_from_array(env, tt, tt_sz); + + if (is_port) { + ErlNifBinary bin; + + if (!enif_term_to_binary(env, msg, &bin)) + goto error; + + msg = enif_make_binary(env, &bin); + + if (!enif_port_command(env, &to_port, NULL, msg)) + /* port has probably died, enabled will clean up */; + + enif_release_binary(&bin); + } else { + + if (!enif_send(env, &to_pid, NULL, msg)) + /* process has probably died, enabled will clean up */; + } + +error: + + return atom_ok; +} diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 2221b5830c..92182e3890 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -110,6 +110,8 @@ MODULES= \ trace_meta_SUITE \ trace_call_count_SUITE \ trace_call_time_SUITE \ + tracer_SUITE \ + tracer_test \ scheduler_SUITE \ old_scheduler_SUITE \ unique_SUITE \ diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 6b8d14b487..ea973276db 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -23,7 +23,7 @@ -export([all/0, suite/0, not_run/1]). -export([test_1/1, test_2/1, test_3/1, bad_match_spec_bin/1, trace_control_word/1, silent/1, silent_no_ms/1, silent_test/1, - ms_trace2/1, ms_trace3/1, boxed_and_small/1, + ms_trace2/1, ms_trace3/1, ms_trace_dead/1, boxed_and_small/1, destructive_in_test_bif/1, guard_exceptions/1, empty_list/1, unary_plus/1, unary_minus/1, moving_labels/1]). @@ -49,7 +49,7 @@ all() -> false -> [test_1, test_2, test_3, bad_match_spec_bin, trace_control_word, silent, silent_no_ms, silent_test, ms_trace2, - ms_trace3, boxed_and_small, destructive_in_test_bif, + ms_trace3, ms_trace_dead, boxed_and_small, destructive_in_test_bif, guard_exceptions, unary_plus, unary_minus, fpe, moving_labels, faulty_seq_trace, @@ -500,6 +500,8 @@ ms_trace2(Config) when is_list(Config) -> [[call,return_to],[]]}, ms_trace2}] end), + %% Silence valgrind + erlang:trace_pattern({?MODULE,fn,'_'},[],[]), ok. @@ -595,7 +597,35 @@ ms_trace3(Config) when is_list(Config) -> end), ok. - +ms_trace_dead(doc) -> + ["Test that a dead tracer is removed using ms"]; +ms_trace_dead(suite) -> []; +ms_trace_dead(Config) when is_list(Config) -> + Self = self(), + TFun = fun F() -> receive M -> Self ! M, F() end end, + {Tracer, MRef} = spawn_monitor(TFun), + MetaTracer = spawn_link(TFun), + erlang:trace_pattern({?MODULE, f1, '_'}, + [{'_',[],[{trace,[], + [call,{const,{tracer,Tracer}}]}]}], + [{meta, MetaTracer}]), + erlang:trace_pattern({?MODULE, f2, '_'}, []), + ?MODULE:f2(1,2), + ?MODULE:f1(1), + {tracer,Tracer} = erlang:trace_info(self(), tracer), + {flags,[call]} = erlang:trace_info(self(), flags), + ?MODULE:f2(2,3), + receive {trace, Self, call, {?MODULE, f2, _}} -> ok end, + exit(Tracer, stop), + receive {'DOWN',MRef,_,_,_} -> ok end, + ?MODULE:f1(2), + {tracer,[]} = erlang:trace_info(self(), tracer), + ?MODULE:f2(3,4), + TRef = erlang:trace_delivered(all), + receive {trace_delivered, _, TRef} -> ok end, + receive {trace_ts, Self, call, {?MODULE, f1, _}, _} -> ok end, + receive {trace_ts, Self, call, {?MODULE, f1, _}, _} -> ok end, + receive M -> ct:fail({unexpected, M}) after 10 -> ok end. %% Test that destructive operations in test bif does not really happen destructive_in_test_bif(Config) when is_list(Config) -> @@ -905,34 +935,40 @@ collect([]) -> collect([TM | TMs]) -> io:format( "Expecting: ~p~n", [TM]), receive - M0 -> - M = case element(1, M0) of - trace_ts -> - list_to_tuple(lists:reverse( - tl(lists:reverse(tuple_to_list(M0))))); - _ -> M0 - end, - case is_function(TM,1) of - true -> - case (catch TM(M)) of - true -> - io:format("Got: ~p~n", [M]), - collect(TMs); - _ -> - io:format("Got unexpected: ~p~n", [M]), - flush({got_unexpected,M}) - end; - - false -> - case M of - TM -> - io:format("Got: ~p~n", [M]), - collect(TMs); - _ -> - io:format("Got unexpected: ~p~n", [M]), - flush({got_unexpected,M}) - end - end + %% We only look at trace messages with the same tracee + %% as the message we are looking for. This because + %% the order of trace messages is only guaranteed from + %% within a single process. + M0 when element(2, M0) =:= element(2, TM); is_function(TM, 1) -> + M = case element(1, M0) of + trace_ts -> + list_to_tuple(lists:reverse( + tl(lists:reverse(tuple_to_list(M0))))); + _ -> M0 + end, + case is_function(TM,1) of + true -> + case (catch TM(M)) of + true -> + io:format("Got: ~p~n", [M]), + collect(TMs); + _ -> + io:format("Got unexpected: ~p~n", [M]), + flush({got_unexpected,M}) + end; + + false -> + case M of + TM -> + io:format("Got: ~p~n", [M]), + collect(TMs); + _ -> + io:format("Got unexpected: ~p~n", [M]), + flush({got_unexpected,M}) + end + end + after 15000 -> + flush(timeout) end. flush(Reason) -> diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 7aece926e3..d9974bd6f7 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -24,8 +24,8 @@ %%% Tests the trace BIF. %%% --export([all/0, suite/0, - receive_trace/1, self_send/1, +-export([all/0, suite/0, link_receive_call_correlation/0, + receive_trace/1, link_receive_call_correlation/1, self_send/1, timeout_trace/1, send_trace/1, procs_trace/1, dist_procs_trace/1, suspend/1, mutual_suspend/1, suspend_exit/1, suspender_exit/1, @@ -48,7 +48,8 @@ suite() -> {timetrap, {seconds, 5}}]. all() -> - [cpu_timestamp, receive_trace, self_send, timeout_trace, + [cpu_timestamp, receive_trace, link_receive_call_correlation, + self_send, timeout_trace, send_trace, procs_trace, dist_procs_trace, suspend, mutual_suspend, suspend_exit, suspender_exit, suspend_system_limit, suspend_opts, suspend_waiting, @@ -87,10 +88,10 @@ receive_trace(Config) when is_list(Config) -> 1 = erlang:trace(Receiver, true, ['receive']), Hello = {hello, world}, Receiver ! Hello, - {trace, Receiver, 'receive', Hello} = receive_first(), + {trace, Receiver, 'receive', Hello} = receive_first_trace(), Hello2 = {hello, again, world}, Receiver ! Hello2, - {trace, Receiver, 'receive', Hello2} = receive_first(), + {trace, Receiver, 'receive', Hello2} = receive_first_trace(), receive_nothing(), %% Another process should not be able to trace Receiver. @@ -104,6 +105,112 @@ receive_trace(Config) when is_list(Config) -> receive_nothing(), ok. +%% Tests that receive of a message always happens before a call with +%% that message and that links/unlinks are ordered together with the +%% 'receive'. +link_receive_call_correlation() -> + [{timetrap, {minutes, 5}}]. +link_receive_call_correlation(Config) when is_list(Config) -> + Receiver = fun_spawn(fun F() -> + receive + stop -> ok; + M -> receive_msg(M), F() + end + end), + process_flag(trap_exit, true), + + %% Trace the process; make sure that we receive the trace messages. + 1 = erlang:trace(Receiver, true, ['receive', procs, call, timestamp, scheduler_id]), + 1 = erlang:trace_pattern({?MODULE, receive_msg, '_'}, [], [local]), + + Num = 100000, + + (fun F(0) -> []; + F(N) -> + if N rem 2 == 0 -> + link(Receiver); + true -> + unlink(Receiver) + end, + [Receiver ! N | F(N-1)] + end)(Num), + + Receiver ! stop, + MonRef = erlang:monitor(process, Receiver), + receive {'DOWN', MonRef, _, _, _} -> ok end, + Ref = erlang:trace_delivered(Receiver), + receive {trace_delivered, _, Ref} -> ok end, + + Msgs = (fun F() -> receive M -> [M | F()] after 1 -> [] end end)(), + + case check_consistent(Receiver, Num, Num, Num, Msgs) of + ok -> + ok; + {error, Reason} -> + ct:log("~p", [Msgs]), + ct:fail({error, Reason}) + end. + +-define(schedid, , _). + +check_consistent(_Pid, Recv, Call, _LU, [Msg | _]) when Recv > Call -> + {error, Msg}; +check_consistent(Pid, Recv, Call, LU, [Msg | Msgs]) -> + + case Msg of + {trace, Pid, 'receive', Recv ?schedid} -> + check_consistent(Pid,Recv - 1, Call, LU, Msgs); + {trace_ts, Pid, 'receive', Recv ?schedid, _} -> + check_consistent(Pid,Recv - 1, Call, LU, Msgs); + + {trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} -> + check_consistent(Pid,Recv, Call - 1, LU, Msgs); + {trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} -> + check_consistent(Pid,Recv, Call - 1, LU, Msgs); + + %% We check that for each receive we have gotten a + %% getting_linked or getting_unlinked message. Also + %% if we receive a getting_linked, then the next + %% message we expect to receive is an even number + %% and odd number for getting_unlinked. + {trace, Pid, getting_linked, _Self ?schedid} + when Recv rem 2 == 0, Recv == LU -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs); + {trace_ts, Pid, getting_linked, _Self ?schedid, _} + when Recv rem 2 == 0, Recv == LU -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs); + + {trace, Pid, getting_unlinked, _Self ?schedid} + when Recv rem 2 == 1, Recv == LU -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs); + {trace_ts, Pid, getting_unlinked, _Self ?schedid, _} + when Recv rem 2 == 1, Recv == LU -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs); + + {trace,Pid,'receive',Ignore ?schedid} + when Ignore == stop; Ignore == timeout -> + check_consistent(Pid, Recv, Call, LU, Msgs); + {trace_ts,Pid,'receive',Ignore ?schedid,_} + when Ignore == stop; Ignore == timeout -> + check_consistent(Pid, Recv, Call, LU, Msgs); + + {trace, Pid, exit, normal ?schedid} -> + check_consistent(Pid, Recv, Call, LU, Msgs); + {trace_ts, Pid, exit, normal ?schedid, _} -> + check_consistent(Pid, Recv, Call, LU, Msgs); + {'EXIT', Pid, normal} -> + check_consistent(Pid, Recv, Call, LU, Msgs); + Msg -> + {error, Msg} + end; +check_consistent(_, 0, 0, 0, []) -> + ok; +check_consistent(_, Recv, Call, LU, []) -> + {error,{Recv, Call, LU}}. + +receive_msg(M) -> + M. + %% Test that traces are generated for messages sent %% and received to/from self(). self_send(Config) when is_list(Config) -> @@ -134,8 +241,8 @@ timeout_trace(Config) when is_list(Config) -> Process = fun_spawn(fun process/0), 1 = erlang:trace(Process, true, ['receive']), Process ! timeout_please, - {trace, Process, 'receive', timeout_please} = receive_first(), - {trace, Process, 'receive', timeout} = receive_first(), + {trace, Process, 'receive', timeout_please} = receive_first_trace(), + {trace, Process, 'receive', timeout} = receive_first_trace(), receive_nothing(), ok. @@ -149,13 +256,13 @@ send_trace(Config) when is_list(Config) -> %% Check that a message sent to another process is traced. 1 = erlang:trace(Sender, true, [send]), Sender ! {send_please, Receiver, to_receiver}, - {trace, Sender, send, to_receiver, Receiver} = receive_first(), + {trace, Sender, send, to_receiver, Receiver} = receive_first_trace(), receive_nothing(), %% Check that a message sent to another registered process is traced. register(?MODULE,Receiver), Sender ! {send_please, ?MODULE, to_receiver}, - {trace, Sender, send, to_receiver, ?MODULE} = receive_first(), + {trace, Sender, send, to_receiver, ?MODULE} = receive_first_trace(), receive_nothing(), unregister(?MODULE), @@ -163,14 +270,14 @@ send_trace(Config) when is_list(Config) -> Sender ! {send_please, self(), to_myself}, receive to_myself -> ok end, Self = self(), - {trace, Sender, send, to_myself, Self} = receive_first(), + {trace, Sender, send, to_myself, Self} = receive_first_trace(), receive_nothing(), %% Check that a message sent to dead process is traced. {Pid,Ref} = spawn_monitor(fun() -> ok end), receive {'DOWN',Ref,_,_,_} -> ok end, Sender ! {send_please, Pid, to_dead}, - {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first(), + {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first_trace(), receive_nothing(), %% Check that a message sent to unknown registrated process is traced. @@ -178,7 +285,7 @@ send_trace(Config) when is_list(Config) -> 1 = erlang:trace(BadargSender, true, [send]), unlink(BadargSender), BadargSender ! {send_please, not_registered, to_unknown}, - {trace, BadargSender, send, to_unknown, not_registered} = receive_first(), + {trace, BadargSender, send, to_unknown, not_registered} = receive_first_trace(), receive_nothing(), %% Another process should not be able to trace Sender. @@ -211,9 +318,9 @@ procs_trace(Config) when is_list(Config) -> %% spawn, link Proc1 ! {spawn_link_please, Self, MFA}, Proc3 = receive {spawned, Proc1, P3} -> P3 end, - {trace, Proc1, spawn, Proc3, MFA} = receive_first(), + {trace, Proc1, spawn, Proc3, MFA} = receive_first_trace(), io:format("Proc3 = ~p ~n", [Proc3]), - {trace, Proc1, link, Proc3} = receive_first(), + {trace, Proc1, link, Proc3} = receive_first_trace(), receive_nothing(), %% %% getting_unlinked by exit() @@ -221,60 +328,60 @@ procs_trace(Config) when is_list(Config) -> Reason3 = make_ref(), Proc1 ! {send_please, Proc3, {exit_please, Reason3}}, receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end, - {trace, Proc1, getting_unlinked, Proc3} = receive_first(), + {trace, Proc1, getting_unlinked, Proc3} = receive_first_trace(), Proc1 ! {trap_exit_please, false}, receive_nothing(), %% %% link Proc1 ! {link_please, Proc2}, - {trace, Proc1, link, Proc2} = receive_first(), + {trace, Proc1, link, Proc2} = receive_first_trace(), receive_nothing(), %% %% unlink Proc1 ! {unlink_please, Proc2}, - {trace, Proc1, unlink, Proc2} = receive_first(), + {trace, Proc1, unlink, Proc2} = receive_first_trace(), receive_nothing(), %% %% getting_linked Proc2 ! {link_please, Proc1}, - {trace, Proc1, getting_linked, Proc2} = receive_first(), + {trace, Proc1, getting_linked, Proc2} = receive_first_trace(), receive_nothing(), %% %% getting_unlinked Proc2 ! {unlink_please, Proc1}, - {trace, Proc1, getting_unlinked, Proc2} = receive_first(), + {trace, Proc1, getting_unlinked, Proc2} = receive_first_trace(), receive_nothing(), %% %% register true = register(Name, Proc1), - {trace, Proc1, register, Name} = receive_first(), + {trace, Proc1, register, Name} = receive_first_trace(), receive_nothing(), %% %% unregister true = unregister(Name), - {trace, Proc1, unregister, Name} = receive_first(), + {trace, Proc1, unregister, Name} = receive_first_trace(), receive_nothing(), %% %% exit (with registered name, due to link) Reason4 = make_ref(), Proc1 ! {spawn_link_please, Self, MFA}, Proc4 = receive {spawned, Proc1, P4} -> P4 end, - {trace, Proc1, spawn, Proc4, MFA} = receive_first(), + {trace, Proc1, spawn, Proc4, MFA} = receive_first_trace(), io:format("Proc4 = ~p ~n", [Proc4]), - {trace, Proc1, link, Proc4} = receive_first(), + {trace, Proc1, link, Proc4} = receive_first_trace(), Proc1 ! {register_please, Name, Proc1}, - {trace, Proc1, register, Name} = receive_first(), + {trace, Proc1, register, Name} = receive_first_trace(), Proc4 ! {exit_please, Reason4}, receive {'EXIT', Proc1, Reason4} -> ok end, - {trace, Proc1, exit, Reason4} = receive_first(), - {trace, Proc1, unregister, Name} = receive_first(), + {trace, Proc1, exit, Reason4} = receive_first_trace(), + {trace, Proc1, unregister, Name} = receive_first_trace(), receive_nothing(), %% %% exit (not linked to tracing process) 1 = erlang:trace(Proc2, true, [procs]), Reason2 = make_ref(), Proc2 ! {exit_please, Reason2}, - {trace, Proc2, exit, Reason2} = receive_first(), + {trace, Proc2, exit, Reason2} = receive_first_trace(), receive_nothing(), ok. @@ -299,45 +406,45 @@ dist_procs_trace(Config) when is_list(Config) -> Proc1 ! {trap_exit_please, true}, Proc3 = receive {spawned, Proc1, P3} -> P3 end, io:format("Proc3 = ~p ~n", [Proc3]), - {trace, Proc1, getting_linked, Proc3} = receive_first(), + {trace, Proc1, getting_linked, Proc3} = receive_first_trace(), Reason3 = make_ref(), Proc1 ! {send_please, Proc3, {exit_please, Reason3}}, receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end, - {trace, Proc1, getting_unlinked, Proc3} = receive_first(), + {trace, Proc1, getting_unlinked, Proc3} = receive_first_trace(), Proc1 ! {trap_exit_please, false}, receive_nothing(), %% %% link Proc1 ! {link_please, Proc2}, - {trace, Proc1, link, Proc2} = receive_first(), + {trace, Proc1, link, Proc2} = receive_first_trace(), receive_nothing(), %% %% unlink Proc1 ! {unlink_please, Proc2}, - {trace, Proc1, unlink, Proc2} = receive_first(), + {trace, Proc1, unlink, Proc2} = receive_first_trace(), receive_nothing(), %% %% getting_linked Proc2 ! {link_please, Proc1}, - {trace, Proc1, getting_linked, Proc2} = receive_first(), + {trace, Proc1, getting_linked, Proc2} = receive_first_trace(), receive_nothing(), %% %% getting_unlinked Proc2 ! {unlink_please, Proc1}, - {trace, Proc1, getting_unlinked, Proc2} = receive_first(), + {trace, Proc1, getting_unlinked, Proc2} = receive_first_trace(), receive_nothing(), %% %% exit (with registered name, due to link) Name = list_to_atom(OtherName), Reason2 = make_ref(), Proc1 ! {link_please, Proc2}, - {trace, Proc1, link, Proc2} = receive_first(), + {trace, Proc1, link, Proc2} = receive_first_trace(), Proc1 ! {register_please, Name, Proc1}, - {trace, Proc1, register, Name} = receive_first(), + {trace, Proc1, register, Name} = receive_first_trace(), Proc2 ! {exit_please, Reason2}, receive {'EXIT', Proc1, Reason2} -> ok end, - {trace, Proc1, exit, Reason2} = receive_first(), - {trace, Proc1, unregister, Name} = receive_first(), + {trace, Proc1, exit, Reason2} = receive_first_trace(), + {trace, Proc1, unregister, Name} = receive_first_trace(), receive_nothing(), %% %% Done. @@ -1247,7 +1354,8 @@ existing_clear(Config) when is_list(Config) -> [N, M]), {flags, []} = erlang:trace_info(Self, flags), {tracer, []} = erlang:trace_info(Self, tracer), - M = N + 1, % Since trace could not be enabled on the tracer. + M = N, % Used to be N + 1, but from 19.0 the tracer is also traced + ok. %% Test that an invalid flag cause badarg @@ -1317,6 +1425,13 @@ receive_first() -> Any -> Any end. +%% Waits for and returns the first message in the message queue. + +receive_first_trace() -> + receive + Any when element(1,Any) =:= trace; element(1,Any) =:= trace_ts -> Any + end. + %% Ensures that there is no message in the message queue. receive_nothing() -> @@ -1421,7 +1536,7 @@ fun_spawn(Fun, Args) -> start_node(Name) -> Pa = filename:dirname(code:which(?MODULE)), Cookie = atom_to_list(erlang:get_cookie()), - test_server:start_node(Name, slave, + test_server:start_node(Name, slave, [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]). stop_node(Node) -> diff --git a/erts/emulator/test/tracer_SUITE.erl b/erts/emulator/test/tracer_SUITE.erl new file mode 100644 index 0000000000..c62512e6ba --- /dev/null +++ b/erts/emulator/test/tracer_SUITE.erl @@ -0,0 +1,622 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(tracer_SUITE). + +%%% +%%% Tests the tracer module interface +%%% + +-export([all/0, suite/0,groups/0, init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, init_per_testcase/2, + end_per_testcase/2]). +-export([load/1, unload/1, reload/1, invalid_tracers/1]). +-export([send/1, recv/1, spawn/1, exit/1, link/1, unlink/1, + getting_linked/1, getting_unlinked/1, register/1, unregister/1, + in/1, out/1, gc_start/1, gc_end/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. + +all() -> + [load, unload, reload, invalid_tracers, {group, basic}]. + +groups() -> + [{ basic, [], [send, recv, spawn, exit, link, unlink, getting_linked, + getting_unlinked, register, unregister, in, out, + gc_start, gc_end]}]. + +init_per_suite(Config) -> + purge(), + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +init_per_testcase(TC, Config) when TC =:= load; TC =:= reload -> + + DataDir = proplists:get_value(data_dir, Config), + + Pid = erlang:spawn(fun F() -> + receive + {get, Pid} -> + Pid ! DataDir, + F() + end + end), + register(tracer_test_config, Pid), + Config; +init_per_testcase(_, Config) -> + DataDir = proplists:get_value(data_dir, Config), + case catch tracer_test:enabled(trace_status, self(), self()) of + discard -> + ok; + _ -> + tracer_test:load(DataDir) + end, + Config. + +end_per_testcase(TC, _Config) when TC =:= load; TC =:= reload -> + purge(), + exit(whereis(tracer_test_config), kill), + ok; +end_per_testcase(_, _Config) -> + purge(), + ok. + +load(_Config) -> + purge(), + 1 = erlang:trace(self(), true, [{tracer, tracer_test, []}, call]), + purge(), + 1 = erlang:trace_pattern({?MODULE, all, 0}, [], + [{meta, tracer_test, []}]), + ok. + +unload(_Config) -> + + ServerFun = fun F(0, undefined) -> + receive + {N, Pid} -> F(N, Pid) + end; + F(0, Pid) -> + Pid ! done, + F(0, undefined); + F(N, Pid) -> + ?MODULE:all(), + F(N-1, Pid) + end, + + Pid = erlang:spawn_link(fun() -> ServerFun(0, undefined) end), + + + Tc = fun(N) -> + Pid ! {N, self()}, + receive done -> ok after 1000 -> ct:fail(timeout) end, + trace_delivered(Pid) + end, + + 1 = erlang:trace(Pid, true, [{tracer, tracer_test, + {#{ call => trace}, self(), []}}, + call]), + 1 = erlang:trace_pattern({?MODULE, all, 0}, [], []), + + Tc(1), + receive _ -> ok after 0 -> ct:fail(timeout) end, + + code:purge(tracer_test), + code:delete(tracer_test), + + Tc(1), + receive M1 -> ct:fail({unexpected_message, M1}) after 0 -> ok end, + + code:purge(tracer_test), + + Tc(1), + receive M2 -> ct:fail({unexpected_message, M2}) after 0 -> ok end, + + ok. + +%% This testcase is here to make sure there are not +%% segfaults when reloading the current nifs. +reload(_Config) -> + + Tracer = spawn_link(fun F() -> receive _M -> F() end end), + Tracee = spawn_link(fun F() -> ?MODULE:all(), F() end), + + + [begin + Ref = make_ref(), + State = {#{ call => trace }, Tracer, [Ref]}, + erlang:trace(Tracee, true, [{tracer, tracer_test,State}, call]), + erlang:trace_pattern({?MODULE, all, 0}, []), + + false = code:purge(tracer_test), + {module, _} = code:load_file(tracer_test), + + %% There is a race involved in between when the internal nif cache + %% is purged and when the reload_loop needs the tracer module + %% so the tracer may be removed or still there. + case erlang:trace_info(Tracee, tracer) of + {tracer, []} -> ok; + {tracer, {tracer_test, State}} -> ok + end, + + false = code:purge(tracer_test), + true = code:delete(tracer_test), + false = code:purge(tracer_test) + end || _ <- lists:seq(1,15)], + + ok. + +invalid_tracers(_Config) -> + FailTrace = fun(A) -> + try erlang:trace(self(), true, A) of + _ -> ct:fail(A) + catch _:_ -> ok end + end, + + FailTrace([{tracer, foobar}, call]), + FailTrace([{tracer, foobar, []}, call]), + FailTrace([{tracer, make_ref(), []}, call]), + FailTrace([{tracer, lists, []}, call]), + + FailTP = fun(MS,FL) -> + try erlang:trace_pattern({?MODULE,all,0}, MS, FL) of + _ -> ct:fail({MS, FL}) + catch _:_ -> ok end + end, + + FailTP([],[{meta, foobar}]), + FailTP([],[{meta, foobar, []}]), + FailTP([],[{meta, make_ref(), []}]), + FailTP([],[{meta, lists, []}]), + + ok. + + + +send(_Config) -> + + Self = self(), + Tc = fun(Pid) -> + Pid ! fun() -> Self ! ok end, + receive ok -> ok after 100 -> ct:fail(timeout) end + end, + + Expect = fun(Pid, State, EOpts) -> + receive + Msg -> + {Pid, send, State, Pid, ok, Self, Opts} = Msg, + check_opts(EOpts, Opts) + end + end, + test(send, Tc, Expect). + + +recv(_Config) -> + + Tc = fun(Pid) -> + Pid ! ok + end, + + Expect = fun(Pid, State, EOpts) -> + receive + Msg -> + {undefined, 'receive', State, Pid, ok, undefined, Opts} = Msg, + check_opts(EOpts, Opts) + end + end, + + test('receive', Tc, Expect, false). + +spawn(_Config) -> + + Tc = fun(Pid) -> + Pid ! fun() -> erlang:spawn(lists,seq,[1,10]), ok end + end, + + Expect = + fun(Pid, State, EOpts) -> + receive + Msg -> + {Pid, spawn, State, Pid, NewPid, + {lists,seq,[1,10]}, Opts} = Msg, + check_opts(EOpts, Opts), + true = is_pid(NewPid) andalso NewPid /= Pid + end + end, + + test(spawn, procs, Tc, Expect, true). + +exit(_Config) -> + Tc = fun(Pid) -> + Pid ! fun() -> exit end + end, + + Expect = + fun(Pid, State, EOpts) -> + receive + Msg -> + {Pid, exit, State, Pid, normal, undefined, Opts} = Msg, + check_opts(EOpts, Opts) + end + end, + + test(exit, procs, Tc, Expect, true, true). + +link(_Config) -> + + Tc = fun(Pid) -> + Pid ! fun() -> + SPid = erlang:spawn(fun() -> receive _ -> ok end end), + erlang:link(SPid), + ok + end + end, + + Expect = + fun(Pid, State, EOpts) -> + receive + Msg -> + {Pid, link, State, Pid, NewPid, undefined, Opts} = Msg, + check_opts(EOpts, Opts), + true = is_pid(NewPid) andalso NewPid /= Pid + end + end, + + test(link, procs, Tc, Expect, true). + +unlink(_Config) -> + + Tc = fun(Pid) -> + Pid ! fun() -> + SPid = erlang:spawn(fun() -> receive _ -> ok end end), + erlang:link(SPid), + erlang:unlink(SPid), + ok + end + end, + + Expect = + fun(Pid, State, EOpts) -> + receive + Msg -> + {Pid, unlink, State, Pid, NewPid, undefined, Opts} = Msg, + check_opts(EOpts, Opts), + true = is_pid(NewPid) andalso NewPid /= Pid + end + end, + + test(unlink, procs, Tc, Expect, true). + +getting_linked(_Config) -> + + Tc = fun(Pid) -> + Pid ! fun() -> + Self = self(), + erlang:spawn(fun() -> erlang:link(Self) end), + ok + end + end, + + Expect = + fun(Pid, State, EOpts) -> + receive + Msg -> + {NewPid, getting_linked, State, Pid, NewPid, undefined, Opts} = Msg, + check_opts(EOpts, Opts), + true = is_pid(NewPid) andalso NewPid /= Pid + end + end, + + test(getting_linked, procs, Tc, Expect, false). + +getting_unlinked(_Config) -> + Tc = fun(Pid) -> + Pid ! fun() -> + Self = self(), + erlang:spawn(fun() -> + erlang:link(Self), + erlang:unlink(Self) + end), + ok + end + end, + + Expect = + fun(Pid, State, EOpts) -> + receive + Msg -> + {NewPid, getting_unlinked, State, Pid, NewPid, undefined, Opts} = Msg, + check_opts(EOpts, Opts), + true = is_pid(NewPid) andalso NewPid /= Pid + end + end, + + test(getting_unlinked, procs, Tc, Expect, false). + +register(_Config) -> + + Tc = fun(Pid) -> + Pid ! fun() -> + erlang:register(?MODULE, self()), + erlang:unregister(?MODULE), + ok + end + end, + + Expect = + fun(Pid, State, EOpts) -> + receive + Msg -> + {Pid, register, State, Pid, ?MODULE, undefined, Opts} = Msg, + check_opts(EOpts, Opts) + end + end, + + test(register, procs, Tc, Expect, true). + +unregister(_Config) -> + + Tc = fun(Pid) -> + Pid ! fun() -> + erlang:register(?MODULE, self()), + erlang:unregister(?MODULE), + ok + end + end, + + Expect = + fun(Pid, State, EOpts) -> + receive + Msg -> + {Pid, unregister, State, Pid, ?MODULE, undefined, Opts} = Msg, + check_opts(EOpts, Opts) + end + end, + + test(unregister, procs, Tc, Expect, true). + +in(_Config) -> + + Tc = fun(Pid) -> + Self = self(), + Pid ! fun() -> receive after 1 -> Self ! ok end end, + receive ok -> ok end + end, + + Expect = + fun(Pid, State, EOpts) -> + N = (fun F(N) -> + receive + Msg -> + {Pid, in, State, Pid, _, + undefined, Opts} = Msg, + check_opts(EOpts, Opts), + F(N+1) + after 0 -> N + end + end)(0), + true = N > 0 + end, + + test(in, running, Tc, Expect, true). + +out(_Config) -> + Tc = fun(Pid) -> + Pid ! fun() -> receive after 10 -> exit end end, + Ref = erlang:monitor(process, Pid), + receive {'DOWN', Ref, _, _, _} -> ok end + end, + + Expect = + fun(Pid, State, EOpts) -> + %% We cannot predict how many out schedules there will be + N = (fun F(N) -> + receive + Msg -> + {Pid, out, State, Pid, _, + undefined, Opts} = Msg, + check_opts(EOpts, Opts), + F(N+1) + after 0 -> N + end + end)(0), + true = N > 0 + end, + + test(out, running, Tc, Expect, true, true). + +gc_start(_Config) -> + + Tc = fun(Pid) -> + Pid ! fun() -> + erlang:garbage_collect(), + ok + end + end, + + Expect = + fun(Pid, State, EOpts) -> + receive + Msg -> + {Pid, gc_start, State, Pid, _, undefined, Opts} = Msg, + check_opts(EOpts, Opts) + end + end, + + test(gc_start, garbage_collection, Tc, Expect, true). + +gc_end(_Config) -> + + Tc = fun(Pid) -> + Pid ! fun() -> + erlang:garbage_collect(), + ok + end + end, + + Expect = + fun(Pid, State, EOpts) -> + receive + Msg -> + {Pid, gc_end, State, Pid, _, undefined, Opts} = Msg, + check_opts(EOpts, Opts) + end + end, + + test(gc_end, garbage_collection, Tc, Expect, true). + +test(Event, Tc, Expect) -> + test(Event, Tc, Expect, true). +test(Event, Tc, Expect, Removes) -> + test(Event, Event, Tc, Expect, Removes). +test(Event, TraceFlag, Tc, Expect, Removes) -> + test(Event, TraceFlag, Tc, Expect, Removes, false). +test(Event, TraceFlag, Tc, Expect, Removes, Dies) -> + + ComplexState = {fun() -> ok end, <<0:(128*8)>>}, + Opts = #{ timestamp => undefined, + scheduler_id => undefined, + match_spec_result => true }, + + %% Test that trace works + State1 = {#{ Event => trace }, self(), ComplexState}, + Pid1 = start_tracee(), + 1 = erlang:trace(Pid1, true, [TraceFlag, {tracer, tracer_test, State1}]), + Tc(Pid1), + ok = trace_delivered(Pid1), + + Expect(Pid1, State1, Opts), + receive M11 -> ct:fail({unexpected, M11}) after 0 -> ok end, + if not Dies -> + {flags, [TraceFlag]} = erlang:trace_info(Pid1, flags), + {tracer, {tracer_test, State1}} = erlang:trace_info(Pid1, tracer), + erlang:trace(Pid1, false, [TraceFlag]); + true -> ok + end, + + %% Test that trace works with scheduler id and timestamp + Pid1T = start_tracee(), + 1 = erlang:trace(Pid1T, true, [TraceFlag, {tracer, tracer_test, State1}, + timestamp, scheduler_id]), + Tc(Pid1T), + ok = trace_delivered(Pid1T), + + Expect(Pid1T, State1, Opts#{ scheduler_id := number, + timestamp := timestamp}), + receive M11T -> ct:fail({unexpected, M11T}) after 0 -> ok end, + if not Dies -> + {flags, [scheduler_id, TraceFlag, timestamp]} + = erlang:trace_info(Pid1T, flags), + {tracer, {tracer_test, State1}} = erlang:trace_info(Pid1T, tracer), + erlang:trace(Pid1T, false, [TraceFlag]); + true -> ok + end, + + %% Test that discard works + Pid2 = start_tracee(), + State2 = {#{ Event => discard }, self(), ComplexState}, + 1 = erlang:trace(Pid2, true, [TraceFlag, {tracer, tracer_test, State2}]), + Tc(Pid2), + ok = trace_delivered(Pid2), + receive M2 -> ct:fail({unexpected, M2}) after 0 -> ok end, + if not Dies -> + {flags, [TraceFlag]} = erlang:trace_info(Pid2, flags), + {tracer, {tracer_test, State2}} = erlang:trace_info(Pid2, tracer), + erlang:trace(Pid2, false, [TraceFlag]); + true -> + ok + end, + + %% Test that remove works + Pid3 = start_tracee(), + State3 = {#{ Event => remove }, self(), ComplexState}, + 1 = erlang:trace(Pid3, true, [TraceFlag, {tracer, tracer_test, State3}]), + Tc(Pid3), + ok = trace_delivered(Pid3), + receive M3 -> ct:fail({unexpected, M3}) after 0 -> ok end, + if not Dies -> + if Removes -> + {flags, []} = erlang:trace_info(Pid3, flags), + {tracer, []} = erlang:trace_info(Pid3, tracer); + true -> + {flags, [TraceFlag]} = erlang:trace_info(Pid3, flags), + {tracer, {tracer_test, State3}} = erlang:trace_info(Pid3, tracer) + end, + erlang:trace(Pid3, false, [TraceFlag]); + true -> + ok + end, + ok. + +check_opts(#{ scheduler_id := number } = E, #{ scheduler_id := N } = O) + when is_integer(N) -> + E1 = maps:remove(scheduler_id, E), + O1 = maps:remove(scheduler_id, O), + if E1 == O1 -> ok; + true -> ct:fail({invalid_opts, E, O}) + end; +check_opts(Opts, Opts) -> + ok; +check_opts(E,O) -> + ct:fail({invalid_opts, E, O}). + +start_tracee() -> + spawn_link( + fun F() -> + receive + Action when is_function(Action) -> + case Action() of + ok -> + F(); + Err -> + Err + end; + _ -> + F() + end + end). + +trace_delivered(Pid) -> + Ref = erlang:trace_delivered(Pid), + receive + {trace_delivered, Pid, Ref} -> + ok + after 1000 -> + timeout + end. + +purge() -> + %% Make sure module is not loaded + case erlang:module_loaded(tracer_test) of + true -> + code:purge(tracer_test), + true = code:delete(tracer_test), + code:purge(tracer_test); + _ -> + ok + end. diff --git a/erts/emulator/test/tracer_SUITE_data/Makefile.src b/erts/emulator/test/tracer_SUITE_data/Makefile.src new file mode 100644 index 0000000000..154bd70ccc --- /dev/null +++ b/erts/emulator/test/tracer_SUITE_data/Makefile.src @@ -0,0 +1,8 @@ + +NIF_LIBS = tracer_test@dll@ + +all: $(NIF_LIBS) + +@SHLIB_RULES@ + +$(NIF_LIBS): tracer_test.c diff --git a/erts/emulator/test/tracer_SUITE_data/tracer_test.c b/erts/emulator/test/tracer_SUITE_data/tracer_test.c new file mode 100644 index 0000000000..8b4be1345d --- /dev/null +++ b/erts/emulator/test/tracer_SUITE_data/tracer_test.c @@ -0,0 +1,122 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2009-2014. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "erl_nif.h" + +#include +#include +#include +#include + +/* NIF interface declarations */ +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); +static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info); +static void unload(ErlNifEnv* env, void* priv_data); + +/* The NIFs: */ +static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +static ErlNifFunc nif_funcs[] = { + {"enabled", 3, enabled}, + {"trace", 6, trace} +}; + +ERL_NIF_INIT(tracer_test, nif_funcs, load, NULL, upgrade, unload) + +static ERL_NIF_TERM atom_discard; +static ERL_NIF_TERM atom_ok; + +#define ASSERT(expr) assert(expr) + +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + + atom_discard = enif_make_atom(env, "discard"); + atom_ok = enif_make_atom(env, "ok"); + + *priv_data = NULL; + + return 0; +} + +static void unload(ErlNifEnv* env, void* priv_data) +{ + +} + +static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, + ERL_NIF_TERM load_info) +{ + if (*old_priv_data != NULL) { + return -1; /* Don't know how to do that */ + } + if (*priv_data != NULL) { + return -1; /* Don't know how to do that */ + } + if (load(env, priv_data, load_info)) { + return -1; + } + return 0; +} + +static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int state_arity; + const ERL_NIF_TERM *state_tuple; + ERL_NIF_TERM value; + ASSERT(argc == 3); + + if (!enif_get_tuple(env, argv[1], &state_arity, &state_tuple)) + return atom_discard; + + if (enif_get_map_value(env, state_tuple[0], argv[0], &value)) { + return value; + } else { + return atom_discard; + } +} + +static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int state_arity; + ErlNifPid self, to; + ERL_NIF_TERM *tuple, msg; + const ERL_NIF_TERM *state_tuple; + ASSERT(argc == 6); + + enif_get_tuple(env, argv[1], &state_arity, &state_tuple); + + tuple = enif_alloc(sizeof(ERL_NIF_TERM)*(argc+1)); + memcpy(tuple+1,argv,sizeof(ERL_NIF_TERM)*argc); + + if (enif_self(env, &self)) { + tuple[0] = enif_make_pid(env, &self); + } else { + tuple[0] = enif_make_atom(env, "undefined"); + } + + msg = enif_make_tuple_from_array(env, tuple, argc + 1); + enif_get_local_pid(env, state_tuple[1], &to); + enif_send(env, &to, NULL, msg); + enif_free(tuple); + + return atom_ok; +} diff --git a/erts/emulator/test/tracer_test.erl b/erts/emulator/test/tracer_test.erl new file mode 100644 index 0000000000..d4778f4531 --- /dev/null +++ b/erts/emulator/test/tracer_test.erl @@ -0,0 +1,55 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(tracer_test). + +%%% +%%% Test tracer +%%% + +-export([enabled/3, trace/6]). +-export([load/1, load/2]). +-on_load(load/0). + +enabled(_, _, _) -> + erlang:nif_error(nif_not_loaded). + +trace(_, _, _, _, _, _) -> + erlang:nif_error(nif_not_loaded). + +load() -> + case whereis(tracer_test_config) of + undefined -> + ok; + Pid -> + Pid ! {get, self()}, + receive + {Conf, Postfix} -> + load(Conf, Postfix); + Conf -> + load(Conf) + end + end. + +load(DataDir) -> + load(DataDir, ""). +load(DataDir, Postfix) -> + SoFile = atom_to_list(?MODULE) ++ Postfix, + erlang:load_nif(filename:join(DataDir, SoFile) , 0). diff --git a/erts/emulator/utils/make_driver_tab b/erts/emulator/utils/make_driver_tab index 646959e8b2..ffb5f58ebf 100755 --- a/erts/emulator/utils/make_driver_tab +++ b/erts/emulator/utils/make_driver_tab @@ -31,7 +31,7 @@ my $file = ""; my $nif = ""; my @emu_drivers = (); my @static_drivers = (); -my @nifs = (); +my @static_nifs = (); my $mode = 1; while (@ARGV) { @@ -55,9 +55,14 @@ while (@ARGV) { push(@static_drivers, $d); } if ($mode == 2) { - push(@nifs, $d); + push(@static_nifs, $d); } next; + } elsif ($mode == 2) { + $d = basename $d; + $d =~ s/_nif(\..*|)$//; # strip nif.* or just nif + push(@static_nifs, $d); + next; } $d = basename $d; $d =~ s/drv(\..*|)$//; # strip drv.* or just drv @@ -120,7 +125,7 @@ typedef struct ErtsStaticNifEntry_ { EOF # prototypes -foreach (@nifs) { +foreach (@static_nifs) { my $d = ${_}; $d =~ s/\.debug//; # strip .debug print "void *".$d."_nif_init(void);\n"; @@ -129,7 +134,7 @@ foreach (@nifs) { # The array itself print "static ErtsStaticNifEntry static_nif_tab[] =\n{\n"; -foreach (@nifs) { +foreach (@static_nifs) { my $d = ${_}; $d =~ s/\.debug//; # strip .debug print "{\"${_}\",&".$d."_nif_init},\n"; diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam new file mode 100644 index 0000000000..dcb74c3bf7 Binary files /dev/null and b/erts/preloaded/ebin/erl_tracer.beam differ diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 1e3de9f1d7..4a447d3a09 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -43,7 +43,8 @@ PRE_LOADED_ERL_MODULES = \ otp_ring0 \ erts_code_purger \ erlang \ - erts_internal + erts_internal \ + erl_tracer PRE_LOADED_BEAM_MODULES = \ prim_eval diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl new file mode 100644 index 0000000000..3415ff3135 --- /dev/null +++ b/erts/preloaded/src/erl_tracer.erl @@ -0,0 +1,41 @@ +-module(erl_tracer). + +-export([enabled/3, trace/6, on_load/0]). + +-type tracee() :: port() | pid() | undefined. +-type trace_tag() :: send | send_to_non_existing_process | 'receive' | + call | return_to | return_from | exception_from | + spawn | exit | link | unlink | getting_linked | + getting_unlinked | register | unregister | in | out | + gc_start | gc_end. +-type trace_opts() :: #{ match_spec_result => true | term(), + scheduler_id => undefined | non_neg_integer(), + timestamp => undefined | timestamp | cpu_timestamp | + monotonic | strict_monotonic }. +-type tracer_state() :: term(). + +on_load() -> + case erlang:load_nif(atom_to_list(?MODULE), 0) of + ok -> ok + end. + +%%% +%%% NIF placeholders +%%% + +-spec enabled(Tag :: trace_tag() | seq_trace | trace_status, + TracerState :: tracer_state(), + Tracee :: tracee()) -> + trace | discard | remove. +enabled(_, _, _) -> + erlang:nif_error(nif_not_loaded). + +-spec trace(Tag :: trace_tag() | seq_trace, + TracerState :: tracer_state(), + Tracee :: tracee(), + Msg :: term(), + Extra :: term(), + Opts :: trace_opts()) -> any(). + +trace(_, _, _, _, _, _) -> + erlang:nif_error(nif_not_loaded). diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 4374bdcd89..8d6d17b043 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -245,12 +245,14 @@ set_on_first_spawn | set_on_link | set_on_first_link | - {tracer, pid() | port()}. + {tracer, pid() | port()} | + {tracer, module(), term()}. -type trace_info_item_result() :: {traced, global | local | false | undefined} | {match_spec, trace_match_spec() | false | undefined} | {meta, pid() | port() | false | undefined | []} | + {meta, module(), term() } | {meta_match_spec, trace_match_spec() | false | undefined} | {call_count, non_neg_integer() | boolean() | undefined} | {call_time, [{pid(), non_neg_integer(), @@ -276,6 +278,7 @@ undefined | {flags, [trace_info_flag()]} | {tracer, pid() | port() | []} | + {tracer, module(), term()} | trace_info_item_result() | {all, [ trace_info_item_result() ] | false | undefined}. @@ -1709,8 +1712,28 @@ time() -> PidSpec :: pid() | existing | new | all, How :: boolean(), FlagList :: [trace_flag()]. -trace(_PidSpec, _How, _FlagList) -> - erlang:nif_error(undefined). +trace(PidSpec, How, FlagList) -> + %% Make sure that we have loaded the tracer module + case lists:keyfind(tracer, 1, FlagList) of + {tracer, Module, State} when erlang:is_atom(Module) -> + case erlang:module_loaded(Module) of + false -> + Module:enabled(trace_status, erlang:self(), State); + true -> + ok + end; + _ -> + ignore + end, + + try erts_internal:trace(PidSpec, How, FlagList) of + Res -> Res + catch E:R -> + {_, [_ | CST]} = erlang:process_info( + erlang:self(), current_stacktrace), + erlang:raise( + E, R, [{?MODULE, trace, [PidSpec, How, FlagList], []} | CST]) + end. %% trace_delivered/1 -spec erlang:trace_delivered(Tracee) -> Ref when @@ -2319,7 +2342,7 @@ subtract(_,_) -> OldState :: preliminary | final | volatile; %% These are deliberately not documented (internal_cpu_topology, term()) -> term(); - (sequential_tracer, pid() | port() | false) -> pid() | port() | false; + (sequential_tracer, pid() | port() | {module(), term()} | false) -> pid() | port() | false; (1,0) -> true. system_flag(_Flag, _Value) -> @@ -2355,12 +2378,20 @@ tl(_List) -> | boolean() | restart | pause. -trace_pattern(_MFA, _MatchSpec) -> - erlang:nif_error(undefined). +trace_pattern(MFA, MatchSpec) -> + try erts_internal:trace_pattern(MFA, MatchSpec, []) of + Res -> Res + catch E:R -> + {_, [_ | CST]} = erlang:process_info( + erlang:self(), current_stacktrace), + erlang:raise( + E, R, [{?MODULE, trace_pattern, [MFA, MatchSpec], []} | CST]) + end. -type trace_pattern_flag() :: global | local | meta | {meta, Pid :: pid()} | + {meta, TracerModule :: module(), TracerState :: term()} | call_count | call_time. @@ -2371,8 +2402,28 @@ trace_pattern(_MFA, _MatchSpec) -> | restart | pause, FlagList :: [ trace_pattern_flag() ]. -trace_pattern(_MFA, _MatchSpec, _FlagList) -> - erlang:nif_error(undefined). +trace_pattern(MFA, MatchSpec, FlagList) -> + %% Make sure that we have loaded the tracer module + case lists:keyfind(meta, 1, FlagList) of + {meta, Module, State} when erlang:is_atom(Module) -> + case erlang:module_loaded(Module) of + false -> + Module:enabled(trace_status, erlang:self(), State); + true -> + ok + end; + _ -> + ignore + end, + + try erts_internal:trace_pattern(MFA, MatchSpec, FlagList) of + Res -> Res + catch E:R -> + {_, [_ | CST]} = erlang:process_info( + erlang:self(), current_stacktrace), + erlang:raise( + E, R, [{?MODULE, trace_pattern, [MFA, MatchSpec, FlagList], []} | CST]) + end. %% Shadowed by erl_bif_types: erlang:tuple_to_list/1 -spec tuple_to_list(Tuple) -> [term()] when diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 769757ba75..2459ea2a2c 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -55,7 +55,9 @@ -export([await_microstate_accounting_modifications/3, gather_microstate_accounting_result/2]). -%% Auto-import name clash +-export([trace/3, trace_pattern/3]). + +%% Auto import name clash -export([check_process_code/2]). %% @@ -403,3 +405,28 @@ microstate_accounting(Ref, Threads) -> {Ref, Res} -> [Res | microstate_accounting(Ref, Threads - 1)] end. + +-spec trace(PidPortSpec, How, FlagList) -> integer() when + PidPortSpec :: pid() | port() + | all | processes | ports + | existing | existing_processes | existing_ports + | new | new_processes | new_ports, + How :: boolean(), + FlagList :: []. +trace(_PidSpec, _How, _FlagList) -> + erlang:nif_error(undefined). + +-type trace_pattern_mfa() :: + {atom(),atom(),arity() | '_'} | on_load. +-type trace_match_spec() :: + [{[term()] | '_' ,[term()],[term()]}]. + +-spec trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when + MFA :: trace_pattern_mfa(), + MatchSpec :: (MatchSpecList :: trace_match_spec()) + | boolean() + | restart + | pause, + FlagList :: [ ]. +trace_pattern(_MFA, _MatchSpec, _FlagList) -> + erlang:nif_error(undefined). diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 5d5d2f8012..77684751c8 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -179,6 +179,10 @@ stop(Status) -> init ! {stop,{stop,Status}}, ok. boot(BootArgs) -> register(init, self()), process_flag(trap_exit, true), + + %% Load the tracer nif + erl_tracer:on_load(), + {Start0,Flags,Args} = parse_boot_args(BootArgs), Start = map(fun prepare_run_args/1, Start0), boot(Start, Flags, Args). -- cgit v1.2.3 From 6cb6b59cd4cd5bd4383053e12ae8ab192711c827 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 8 Feb 2016 18:31:43 +0100 Subject: erts: Extend process and port tracing This commit completes the tracing for processes so that all messages sent by a process (via nifs or otherwise) will be traced. The commit also adds tracing of all types of events from ports. When enabling tracing using erlang:trace, the 'all' flag now also enables tracing on all ports. OTP-13496 --- erts/doc/src/erlang.xml | 329 ++++++++--- erts/emulator/beam/atom.names | 7 + erts/emulator/beam/bif.c | 35 +- erts/emulator/beam/dist.c | 4 +- erts/emulator/beam/erl_bif_port.c | 24 +- erts/emulator/beam/erl_bif_trace.c | 70 ++- erts/emulator/beam/erl_message.c | 6 +- erts/emulator/beam/erl_nif.c | 14 +- erts/emulator/beam/erl_port.h | 2 +- erts/emulator/beam/erl_port_task.c | 2 + erts/emulator/beam/erl_process.c | 4 +- erts/emulator/beam/erl_process.h | 19 +- erts/emulator/beam/erl_trace.c | 345 ++++++++++-- erts/emulator/beam/erl_trace.h | 17 +- erts/emulator/beam/io.c | 236 ++++++-- erts/emulator/beam/register.c | 6 +- erts/emulator/test/Makefile | 1 + erts/emulator/test/call_trace_SUITE.erl | 66 ++- erts/emulator/test/port_trace_SUITE.erl | 600 +++++++++++++++++++++ .../test/port_trace_SUITE_data/Makefile.src | 3 + .../emulator/test/port_trace_SUITE_data/echo_drv.c | 237 ++++++++ erts/emulator/test/trace_port_SUITE.erl | 2 +- erts/preloaded/src/erl_tracer.erl | 3 +- erts/preloaded/src/erlang.erl | 30 +- 24 files changed, 1782 insertions(+), 280 deletions(-) create mode 100644 erts/emulator/test/port_trace_SUITE.erl create mode 100644 erts/emulator/test/port_trace_SUITE_data/Makefile.src create mode 100644 erts/emulator/test/port_trace_SUITE_data/echo_drv.c (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 86bdb1dfe6..abc0d56983 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -8347,22 +8347,47 @@ timestamp() -> How == false) the trace flags in FlagList for the process or processes represented by - PidSpec.

-

PidSpec is either a process identifier - (pid) for a local process, or one of the following atoms:

+ PidPortSpec.

+

PidPortSpec is either a process identifier + (pid) for a local process, a port identifier, + or one of the following atoms:

+ all + +

All currently existing processes and ports and all that + will be created in the future.

+
+ processes + +

All currently existing processes and all that will be created in the future.

+
+ ports + +

All currently existing ports and all that will be created in the future.

+
existing + +

All currently existing processes and ports.

+
+ existing_processes

All currently existing processes.

+ existing_ports + +

All currently existing ports.

+
new -

All processes that are created in the future.

+

All processes and ports that will be created in the future.

- all + new_processes -

All currently existing processes and all processes that - are created in the future.

+

All processes that will be created in the future.

+
+ new_ports + +

All ports that will be created in the future.

FlagList can contain any number of the @@ -8378,28 +8403,21 @@ timestamp() -> send

Traces sending of messages.

-

Message tags: send and - send_to_non_existing_process.

+

Message tags: send and + send_to_non_existing_process.

'receive'

Traces receiving of messages.

-

Message tags: 'receive'.

-
- procs - -

Traces process-related events.

-

Message tags: spawn, exit, - register, unregister, link, - unlink, getting_linked, and - getting_unlinked.

+

Message tags: 'receive'.

- call +call

Traces certain function calls. Specify which function calls to trace by calling erlang:trace_pattern/3.

-

Message tags: call and return_from.

+

Message tags: call and + return_from.

silent @@ -8417,8 +8435,9 @@ timestamp() -> specification function {silent,Bool}, giving a high degree of control of which functions with which arguments that trigger the trace.

-

Message tags: call, return_from, and - return_to. Or rather, the absence of.

+

Message tags: call, + return_from, and + return_to. Or rather, the absence of.

return_to @@ -8439,23 +8458,62 @@ timestamp() ->

To get trace messages containing return values from functions, use the {return_trace} match specification action instead.

-

Message tags: return_to.

+

Message tags: return_to.

+
+ procs + +

Traces process-related events.

+

Message tags: spawn, + exit, + register, + unregister, + link, + unlink, + getting_linked, and + getting_unlinked.

+
+ ports + +

Traces port-related events.

+

Message tags: open, + closed, + register, + unregister, + getting_linked, and + getting_unlinked.

running

Traces scheduling of processes.

-

Message tags: in and out.

+

Message tags: in and + out.

exiting

Traces scheduling of exiting processes.

-

Message tags: in_exiting, out_exiting, and - out_exited.

+

Message tags: in_exiting, + out_exiting, and + out_exited.

+
+ running_procs + +

Traces scheduling of processes just like running. + However this option also includes schedule events when the + process executes within the context of a port without + being scheduled out itself.

+

Message tags: in and + out.

+
+ running_ports + +

Traces scheduling of ports.

+

Message tags: in and + out.

garbage_collection

Traces garbage collections of processes.

-

Message tags: gc_start and gc_end.

+

Message tags: gc_start and gc_end.

timestamp @@ -8470,8 +8528,8 @@ timestamp() -> in CPU time, not wall clock time. That is, cpu_timestamp will not be used if monotonic_timestamp, or strict_monotonic_timestamp is enabled. - Only allowed with PidSpec==all. If the host - machine OS does not support high-resolution + Only allowed with PidPortSpec==all. If the + host machine OS does not support high-resolution CPU time measurements, trace/3 exits with badarg. Notice that most OS do not synchronize this value across cores, so be prepared @@ -8483,8 +8541,8 @@ timestamp() -> Erlang monotonic time time-stamp in all trace messages. The time-stamp (Ts) has the same format and value as produced by - erlang:monotonic_time(nano_seconds). This flag overrides - the cpu_timestamp flag.

+ erlang:monotonic_time(nano_seconds). + This flag overrides the cpu_timestamp flag.

strict_monotonic_timestamp @@ -8493,9 +8551,9 @@ timestamp() -> monotonic time and a monotonically increasing integer in all trace messages. The time-stamp (Ts) has the same format and value as produced by - {erlang:monotonic_time(nano_seconds), - erlang:unique_integer([monotonic])}. This flag overrides - the cpu_timestamp flag.

+ {erlang:monotonic_time(nano_seconds), + erlang:unique_integer([monotonic])}. + This flag overrides the cpu_timestamp flag.

arity @@ -8563,21 +8621,36 @@ timestamp() -> the other one will become active.

- {trace, Pid, 'receive', Msg} - -

When Pid receives message Msg.

-
- {trace, Pid, send, Msg, To} + + + {trace, PidPort, send, Msg, To} + -

When Pid sends message Msg to +

When PidPort sends message Msg to process To.

- {trace, Pid, send_to_non_existing_process, Msg, To} + + + {trace, PidPort, send_to_non_existing_process, Msg, To} + -

When Pid sends message Msg to +

When PidPort sends message Msg to the non-existing process To.

- {trace, Pid, call, {M, F, Args}} + + + {trace, PidPort, 'receive', Msg} + + +

When PidPort receives message Msg. + If Msg is set to timeout, then a receive + statement may have timedout, or the process received + a message with the payload timeout.

+
+ + + {trace, Pid, call, {M, F, Args}} +

When Pid calls a traced function. The return values of calls are never supplied, only the call and its @@ -8586,7 +8659,10 @@ timestamp() -> change the contents of this message, so that Arity is specified instead of Args.

- {trace, Pid, return_to, {M, F, Arity}} + + + {trace, Pid, return_to, {M, F, Arity}} +

When Pid returns to the specified function. This trace message is sent if both @@ -8598,73 +8674,162 @@ timestamp() -> (that is, the functions match specification matched, and {message, false} was not an action).

- {trace, Pid, return_from, {M, F, Arity}, ReturnValue} + + + {trace, Pid, return_from, {M, F, Arity}, ReturnValue} +

When Pid returns from the specified function. This trace message is sent if flag call is set, and the function has a match specification with a return_trace or exception_trace action.

- {trace, Pid, exception_from, {M, F, Arity}, {Class, Value}} + + + {trace, Pid, exception_from, {M, F, Arity}, {Class, Value}} +

When Pid exits from the specified function because of an exception. This trace message is sent if flag call is set, and the function has a match specification with an exception_trace action.

- {trace, Pid, spawn, Pid2, {M, F, Args}} + + + {trace, Pid, spawn, Pid2, {M, F, Args}} +

When Pid spawns a new process Pid2 with the specified function call as entry point.

Args is supposed to be the argument list, but can be any term if the spawn is erroneous.

- {trace, Pid, exit, Reason} + + + {trace, Pid, exit, Reason} +

When Pid exits with reason Reason.

- {trace, Pid, link, Pid2} + + + {trace, PidPort, register, RegName} + + +

When PidPort gets the name RegName registered.

+
+ + + {trace, PidPort, unregister, RegName} + + +

When PidPort gets the name RegName unregistered. + This is done automatically when a registered + process or port exits.

+
+ + + {trace, Pid, link, Pid2} +

When Pid links to a process Pid2.

- {trace, Pid, unlink, Pid2} + + + {trace, Pid, unlink, Pid2} +

When Pid removes the link from a process Pid2.

- {trace, Pid, getting_linked, Pid2} + + + {trace, PidPort, getting_linked, Pid2} + -

When Pid gets linked to a process Pid2.

+

When PidPort gets linked to a process Pid2.

- {trace, Pid, getting_unlinked, Pid2} + + + {trace, PidPort, getting_unlinked, Pid2} + -

When Pid gets unlinked from a process Pid2.

+

When PidPort gets unlinked from a process Pid2.

- {trace, Pid, register, RegName} + + + {trace, Pid, exit, Reason} + -

When Pid gets the name RegName registered.

+

When Pid exits with reason Reason.

- {trace, Pid, unregister, RegName} + + + {trace, Port, open, Pid, Driver} + -

When Pid gets the name RegName unregistered. - This is done automatically when a registered - process exits.

+

When Pid opens a new port Port with + the running the Driver.

+

Driver is the name of the driver as an atom.

- {trace, Pid, in, {M, F, Arity} | 0} + + + {trace, Port, closed, Reason} + + +

When Port closed with Reason.

+
+ + + + {trace, Pid, in | in_exiting, {M, F, Arity} | 0} +

When Pid is scheduled to run. The process runs in function {M, F, Arity}. On some rare occasions, the current function cannot be determined, then the last element is 0.

- {trace, Pid, out, {M, F, Arity} | 0} + + + + + {trace, Pid, out | out_exiting | out_exited, {M, F, Arity} | 0} +

When Pid is scheduled out. The process was running in function {M, F, Arity}. On some rare occasions, the current function cannot be determined, then the last element is 0.

- {trace, Pid, gc_start, Info} + + + {trace, Port, in, Command | 0} + + +

When Port is scheduled to run. Command is the + first thing the port will execute, it may however run several + commands before being scheduled out. On some rare + occasions, the current function cannot be determined, + then the last element is 0.

+

The possible commands are: call | close | command | connect | control | flush | info | link | open | unlink

+
+ + + {trace, Port, out, Command | 0} + + +

When Port is scheduled out. The last command run + was Command. On some rare occasions, + the current function cannot be determined, then the last + element is 0. Command can contain the same + commands as in +

+
+ + + {trace, Pid, gc_start, Info} +

Sent when garbage collection is about to be started. @@ -8706,7 +8871,10 @@ timestamp() ->

All sizes are in words.

- {trace, Pid, gc_end, Info} + + + {trace, Pid, gc_end, Info} +

Sent when garbage collection is finished. Info contains the same kind of list as in message gc_start, @@ -8719,13 +8887,13 @@ timestamp() ->

Each process can only be traced by one tracer. Therefore, attempts to trace an already traced process fail.

Returns: A number indicating the number of processes that - matched PidSpec. - If PidSpec is a process + matched PidPortSpec. + If PidPortSpec is a process identifier, the return value is 1. - If PidSpec + If PidPortSpec is all or existing, the return value is the number of processes running. - If PidSpec is new, the return value is + If PidPortSpec is new, the return value is 0.

Failure: badarg if the specified arguments are not supported. For example, cpu_timestamp is not @@ -8787,12 +8955,15 @@ timestamp() -> -

Returns trace information about a process or function.

-

To get information about a process, - PidOrFunc is to - be a process identifier (pid) or the atom new. - The atom new means that the default trace state for - processes to be created is returned.

+

Returns trace information about a port, process or function.

+

To get information about a port or process, + PidPortOrFunc is to + be a process identifier (pid), port identifier or one of + the atoms new, new_processes, new_ports. + The atom new or new_processes means that the default trace + state for processes to be created is returned. The atom new_ports + means that the default trace state for ports to be created is returned. +

The following Items are valid:

flags @@ -8802,8 +8973,10 @@ timestamp() -> traces are enabled, and one or more of the followings atoms if traces are enabled: send, 'receive', set_on_spawn, call, - return_to, procs, set_on_first_spawn, - set_on_link, running, + return_to, procs, ports, set_on_first_spawn, + set_on_link, running, running_procs, + running_ports, silent, exiting + monotonic_timestamp, strict_monotonic_timestamp, garbage_collection, timestamp, and arity. The order is arbitrary.

@@ -8815,7 +8988,7 @@ timestamp() -> value is [].

-

To get information about a function, PidOrFunc is to +

To get information about a function, PidPortOrFunc is to be the three-element tuple {Module, Function, Arity} or the atom on_load. No wild cards are allowed. Returns undefined if the function does not exist, or @@ -8883,7 +9056,7 @@ timestamp() -> Value is the requested information as described earlier. If a pid for a dead process was given, or the name of a non-existing function, Value is undefined.

-

If PidOrFunc is on_load, the information +

If PidPortOrFunc is on_load, the information returned refers to the default value for code that will be loaded.

diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index aeea6cc989..bd1005867c 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -161,6 +161,7 @@ atom close atom closed atom code atom command +atom commandv atom compact atom compat_rel atom compile @@ -245,6 +246,9 @@ atom exact_reductions atom exclusive atom exit_status atom existing +atom existing_processes +atom existing_ports +atom existing atom exiting atom exports atom external @@ -404,6 +408,8 @@ atom net_kernel_terminated atom never_utf atom new atom new_index +atom new_processes +atom new_ports atom new_uniq atom newline atom next @@ -543,6 +549,7 @@ atom scheme atom scientific atom scope atom seconds +atom send_to_non_existing_process atom sensitive atom sequential_tracer atom sequential_trace_token diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index d633a982ca..ed5b2983dd 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1911,7 +1911,7 @@ static Sint remote_send(Process *p, DistEntry *dep, } if (res >= 0) { - if (IS_TRACED(p)) + if (IS_TRACED_FL(p, F_TRACE_SEND)) trace_send(p, full_to, msg); if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) save_calls(p, &exp_send); @@ -1930,7 +1930,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) Eterm* tp; if (is_internal_pid(to)) { - if (IS_TRACED(p)) + if (IS_TRACED_FL(p, F_TRACE_SEND)) trace_send(p, to, msg); if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) save_calls(p, &exp_send); @@ -1959,7 +1959,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) rp = erts_proc_lookup_raw(id); if (rp) { - if (IS_TRACED(p)) + if (IS_TRACED_FL(p, F_TRACE_SEND)) trace_send(p, to, msg); if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) save_calls(p, &exp_send); @@ -1975,7 +1975,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) goto port_common; } - if (IS_TRACED(p)) + if (IS_TRACED_FL(p, F_TRACE_SEND)) trace_send(p, to, msg); if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) save_calls(p, &exp_send); @@ -2006,11 +2006,20 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) port_common: ret_val = 0; - + if (pt) { int ps_flags = ctx->suspend ? 0 : ERTS_PORT_SIG_FLG_NOSUSPEND; *refp = NIL; + if (IS_TRACED_FL(p, F_TRACE_SEND)) /* trace once only !! */ + trace_send(p, portid, msg); + + if (have_seqtrace(SEQ_TRACE_TOKEN(p))) { + seq_trace_update_send(p); + seq_trace_output(SEQ_TRACE_TOKEN(p), msg, + SEQ_TRACE_SEND, portid, p); + } + switch (erts_port_command(p, ps_flags, pt, msg, refp)) { case ERTS_PORT_OP_CALLER_EXIT: /* We are exiting... */ @@ -2043,18 +2052,10 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) break; } } - - if (IS_TRACED(p)) /* trace once only !! */ - trace_send(p, portid, msg); + if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) save_calls(p, &exp_send); - - if (have_seqtrace(SEQ_TRACE_TOKEN(p))) { - seq_trace_update_send(p); - seq_trace_output(SEQ_TRACE_TOKEN(p), msg, - SEQ_TRACE_SEND, portid, p); - } - + if (ERTS_PROC_IS_EXITING(p)) { KILL_CATCHES(p); /* Must exit */ return SEND_USER_ERROR; @@ -2077,7 +2078,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) if (dep == erts_this_dist_entry) { Eterm id; erts_deref_dist_entry(dep); - if (IS_TRACED(p)) + if (IS_TRACED_FL(p, F_TRACE_SEND)) trace_send(p, to, msg); if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) save_calls(p, &exp_send); @@ -2108,7 +2109,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext* ctx) } return ret; } else { - if (IS_TRACED(p)) /* XXX Is this really neccessary ??? */ + if (IS_TRACED_FL(p, F_TRACE_SEND)) trace_send(p, to, msg); if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) save_calls(p, &exp_send); diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 8cd3f5fbe6..52fd57e101 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -1723,7 +1723,7 @@ decode_error: } data_error: UnUseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE); - erts_deliver_port_exit(prt, dep->cid, am_killed, 0); + erts_deliver_port_exit(prt, dep->cid, am_killed, 0, 1); ERTS_SMP_CHK_NO_PROC_LOCKS; return -1; } @@ -2093,7 +2093,7 @@ erts_dist_command(Port *prt, int reds_limit) erts_smp_de_runlock(dep); if (status & ERTS_DE_SFLG_EXITING) { - erts_deliver_port_exit(prt, prt->common.id, am_killed, 0); + erts_deliver_port_exit(prt, prt->common.id, am_killed, 0, 1); erts_deref_dist_entry(dep); return reds + ERTS_PORT_REDS_DIST_CMD_EXIT; } diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index eb55bd585e..37f4e1de49 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -110,6 +110,10 @@ BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2) erts_add_link(&ERTS_P_LINKS(port), LINK_PID, BIF_P->common.id); erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, port->common.id); + if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) + trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, BIF_P, + am_link, port->common.id); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); erts_port_release(port); @@ -340,8 +344,7 @@ BIF_RETTYPE erts_internal_port_close_1(BIF_ALIST_1) if (!prt) BIF_RET(am_badarg); - - switch (erts_port_exit(BIF_P, 0, prt, prt->common.id, am_normal, &ref)) { + switch (erts_port_exit(BIF_P, 0, prt, BIF_P->common.id, am_normal, &ref)) { case ERTS_PORT_OP_CALLER_EXIT: case ERTS_PORT_OP_BADARG: case ERTS_PORT_OP_DROPPED: @@ -374,7 +377,7 @@ BIF_RETTYPE erts_internal_port_connect_2(BIF_ALIST_2) ref = NIL; #endif - switch (erts_port_connect(BIF_P, 0, prt, prt->common.id, BIF_ARG_2, &ref)) { + switch (erts_port_connect(BIF_P, 0, prt, BIF_P->common.id, BIF_ARG_2, &ref)) { case ERTS_PORT_OP_CALLER_EXIT: case ERTS_PORT_OP_BADARG: case ERTS_PORT_OP_DROPPED: @@ -929,21 +932,22 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) DTRACE3(port_open, process_str, name_buf, port_str); } #endif + + if (port && IS_TRACED_FL(port, F_TRACE_PORTS)) + trace_port(port, am_getting_linked, p->common.id); + erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); + if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) { + trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in); + } + if (!port) { DEBUGF(("open_driver returned (%d:%d)\n", err_typep ? *err_typep : 4711, err_nump ? *err_nump : 4711)); - if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) { - trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in); - } goto do_return; } - - if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) { - trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in); - } if (linebuf && port->linebuf == NULL){ port->linebuf = allocate_linebuf(linebuf); diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 049899211f..7e2fe7fcd4 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -487,7 +487,8 @@ start_trace(Process *c_p, ErtsTracer tracer, && !ERTS_TRACER_COMPARE(ERTS_TRACER(port), tracer)) { /* This tracee is already being traced, and not by the * tracer to be */ - if (erts_is_tracer_proc_enabled(c_p, ERTS_PROC_LOCKS_ALL, common)) { + if (erts_is_tracer_proc_enabled(c_p, ERTS_PROC_LOCKS_ALL, + common, am_trace_status)) { /* The tracer is still in use */ return 1; } @@ -654,18 +655,27 @@ Eterm erts_internal_trace_3(BIF_ALIST_3) } #endif - if (pid_spec == am_all || pid_spec == am_existing) { + if (pid_spec == am_all || pid_spec == am_existing || + pid_spec == am_ports || pid_spec == am_processes || + pid_spec == am_existing_ports || pid_spec == am_existing_processes + ) { int i; int procs = 0; int ports = 0; int mods = 0; if (mask & (ERTS_PROC_TRACEE_FLAGS & ~ERTS_TRACEE_MODIFIER_FLAGS)) - procs = 1; + procs = pid_spec != am_ports && pid_spec != am_existing_ports; if (mask & (ERTS_PORT_TRACEE_FLAGS & ~ERTS_TRACEE_MODIFIER_FLAGS)) - ports = 1; - if (mask & ERTS_TRACEE_MODIFIER_FLAGS) - mods = 1; + ports = pid_spec != am_processes && pid_spec != am_existing_processes; + if (mask & ERTS_TRACEE_MODIFIER_FLAGS) { + if (pid_spec == am_ports || pid_spec == am_existing_ports) + ports = 1; + else if (pid_spec == am_processes || pid_spec == am_existing_processes) + procs = 1; + else + mods = 1; + } #ifdef ERTS_SMP erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN); @@ -697,17 +707,25 @@ Eterm erts_internal_trace_3(BIF_ALIST_3) if (state & ERTS_PORT_SFLGS_DEAD) continue; start_trace(p, tracer, &tracee_port->common, on, mask); - /* matches are not counted for ports since it would violate compatibility */ - /* This could be a reason to modify this function or make a new one. */ + matches++; } } } - if (pid_spec == am_all || pid_spec == am_new) { - Uint def_flags = mask; + if (pid_spec == am_all || pid_spec == am_new + || pid_spec == am_ports || pid_spec == am_processes + || pid_spec == am_new_ports || pid_spec == am_new_processes + ) { ok = 1; - erts_change_default_tracing(on, &def_flags, tracer); + if (mask & ERTS_PROC_TRACEE_FLAGS && + pid_spec != am_ports && pid_spec != am_new_ports) + erts_change_default_proc_tracing( + on, mask & ERTS_PROC_TRACEE_FLAGS, tracer); + if (mask & ERTS_PORT_TRACEE_FLAGS && + pid_spec != am_processes && pid_spec != am_new_processes) + erts_change_default_port_tracing( + on, mask & ERTS_PORT_TRACEE_FLAGS, tracer); #ifdef HAVE_ERTS_NOW_CPU if (cpu_ts && !on) { @@ -773,7 +791,7 @@ Eterm trace_info_2(BIF_ALIST_2) if (What == am_on_load) { res = trace_info_on_load(p, Key); - } else if (is_atom(What) || is_pid(What)) { + } else if (is_atom(What) || is_pid(What) || is_port(What)) { res = trace_info_pid(p, What, Key); } else if (is_tuple(What)) { res = trace_info_func(p, What, Key); @@ -792,11 +810,32 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) Uint trace_flags = am_false; Eterm* hp; - if (pid_spec == am_new) { + if (pid_spec == am_new || pid_spec == am_new_processes) { + ErtsTracer def_tracer; + erts_get_default_proc_tracing(&trace_flags, &def_tracer); + tracer = erts_tracer_to_term(p, def_tracer); + ERTS_TRACER_CLEAR(&def_tracer); + } else if (pid_spec == am_new_ports) { ErtsTracer def_tracer; - erts_get_default_tracing(&trace_flags, &def_tracer); + erts_get_default_port_tracing(&trace_flags, &def_tracer); tracer = erts_tracer_to_term(p, def_tracer); ERTS_TRACER_CLEAR(&def_tracer); + } else if (is_internal_port(pid_spec)) { + Port *tracee; + tracee = erts_id2port_sflgs(pid_spec, p, ERTS_PROC_LOCK_MAIN, + ERTS_PORT_SFLGS_INVALID_LOOKUP); + + if (!tracee) + return am_undefined; + + if (!ERTS_TRACER_IS_NIL(ERTS_TRACER(tracee))) + erts_is_tracer_proc_enabled(NULL, 0, &tracee->common, am_trace_status); + + tracer = erts_tracer_to_term(p, ERTS_TRACER(tracee)); + trace_flags = ERTS_TRACE_FLAGS(tracee); + + erts_port_release(tracee); + } else if (is_internal_pid(pid_spec)) { Process *tracee; tracee = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, @@ -806,7 +845,8 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) return am_undefined; if (!ERTS_TRACER_IS_NIL(ERTS_TRACER(tracee))) - erts_is_tracer_proc_enabled(tracee, ERTS_PROC_LOCK_MAIN, &tracee->common); + erts_is_tracer_proc_enabled(tracee, ERTS_PROC_LOCK_MAIN, + &tracee->common, am_trace_status); tracer = erts_tracer_to_term(p, ERTS_TRACER(tracee)); trace_flags = ERTS_TRACE_FLAGS(tracee); diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 1cb02ec274..9beff52835 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -596,7 +596,7 @@ erts_try_alloc_message_on_heap(Process *pp, #if defined(ERTS_SMP) *plp & ERTS_PROC_LOCK_MAIN #else - 1 + pp #endif ) { #ifdef ERTS_SMP @@ -626,7 +626,7 @@ erts_try_alloc_message_on_heap(Process *pp, *on_heap_p = !0; } #ifdef ERTS_SMP - else if (erts_smp_proc_trylock(pp, ERTS_PROC_LOCK_MAIN) == 0) { + else if (pp && erts_smp_proc_trylock(pp, ERTS_PROC_LOCK_MAIN) == 0) { locked_main = 1; *psp = erts_smp_atomic32_read_nob(&pp->state); *plp |= ERTS_PROC_LOCK_MAIN; @@ -1488,7 +1488,7 @@ erts_factory_message_create(ErtsHeapFactory* factory, int on_heap; erts_aint32_t state; - state = erts_smp_atomic32_read_nob(&proc->state); + state = proc ? erts_smp_atomic32_read_nob(&proc->state) : 0; if (state & ERTS_PSFLG_OFF_HEAP_MSGQ) { msgp = erts_alloc_message(sz, &hp); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index f35c08450c..59a2b20ff2 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -417,6 +417,7 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ? erts_proc_lookup(receiver) : erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN, receiver, rp_locks, ERTS_P2P_FLG_INC_REFC)); + if (rp == NULL) { ASSERT(env == NULL || receiver != c_p->common.id); return 0; @@ -450,9 +451,17 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, } if (!env || !env->tracee) { - erts_queue_message(rp, &rp_locks, mp, msg); + + if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND)) + trace_send(c_p, receiver, msg); + +#ifndef ERTS_SMP } +#endif + + erts_queue_message(rp, &rp_locks, mp, msg); #ifdef ERTS_SMP + } else { /* This clause is taken when the nif is called in the context of a traced process. We do not know which locks we have @@ -548,6 +557,9 @@ enif_port_command(ErlNifEnv *env, const ErlNifPort* to_port, if (!prt) return 0; + if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) + trace_port_receive(prt, env->proc->common.id, am_command, msg); + return erts_port_output_async(prt, env->proc->common.id, msg); } diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index 59aa034f48..c2588e718d 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -944,7 +944,7 @@ erts_schedule_proc2port_signal(Process *, ErtsPortTaskHandle *, ErtsProc2PortSigCallback); -int erts_deliver_port_exit(Port *, Eterm, Eterm, int); +int erts_deliver_port_exit(Port *, Eterm, Eterm, int, int); /* * Port signal flags diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 30ce181ebb..3102e44c11 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -1766,6 +1766,8 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) if (!(state & ERTS_PORT_SFLGS_DEAD)) { DTRACE_DRIVER(driver_timeout, pp); LTTNG_DRIVER(driver_timeout, pp); + if (IS_TRACED_FL(pp, F_TRACE_RECEIVE)) + trace_port(pp, am_receive, am_timeout); (*pp->drv_ptr->timeout)((ErlDrvData) pp->drv_data); } } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index e7d0d127e2..1398bc9c61 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2312,7 +2312,7 @@ handle_reap_ports(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) ERTS_PORT_SFLG_HALT); erts_smp_atomic32_inc_nob(&erts_halt_progress); if (!(state & (ERTS_PORT_SFLG_EXITING|ERTS_PORT_SFLG_CLOSING))) - erts_deliver_port_exit(prt, prt->common.id, am_killed, 0); + erts_deliver_port_exit(prt, prt->common.id, am_killed, 0, 1); } erts_port_release(prt); @@ -11103,7 +11103,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). : STORE_NC(&p->htop, &p->off_heap, parent->group_leader); } - erts_get_default_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER(p)); + erts_get_default_proc_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER(p)); p->msg.first = NULL; p->msg.last = &p->msg.first; diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 8261ae28f8..61acf5924b 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1409,12 +1409,14 @@ extern int erts_system_profile_ts_type; | F_TRACE_SCHED_PORTS | F_TRACE_SCHED_NO \ | F_TRACE_SCHED_EXIT) + #define ERTS_TRACEE_MODIFIER_FLAGS \ - (F_TRACE_SILENT | F_TIMESTAMP_MASK | F_TRACE_SCHED_NO) -#define ERTS_PORT_TRACEE_FLAGS \ - (ERTS_TRACEE_MODIFIER_FLAGS | F_TRACE_PORTS | F_TRACE_SCHED_PORTS) + (F_TRACE_SILENT | F_TIMESTAMP_MASK | F_TRACE_SCHED_NO \ + | F_TRACE_RECEIVE | F_TRACE_SEND) +#define ERTS_PORT_TRACEE_FLAGS \ + (ERTS_TRACEE_MODIFIER_FLAGS | F_TRACE_PORTS | F_TRACE_SCHED_PORTS) #define ERTS_PROC_TRACEE_FLAGS \ - ((TRACEE_FLAGS & ~ERTS_PORT_TRACEE_FLAGS) | ERTS_TRACEE_MODIFIER_FLAGS) + ((TRACEE_FLAGS & ~ERTS_PORT_TRACEE_FLAGS) | ERTS_TRACEE_MODIFIER_FLAGS) #define SEQ_TRACE_FLAG(N) (1 << (ERTS_TRACE_TS_TYPE_BITS + (N))) @@ -2326,14 +2328,17 @@ erts_alloc_message_heap_state(Process *pp, ErlOffHeap **ohpp) { int on_heap; + ErtsMessage *mp; if ((*psp) & ERTS_PSFLG_OFF_HEAP_MSGQ) { - ErtsMessage *mp = erts_alloc_message(sz, hpp); + mp = erts_alloc_message(sz, hpp); *ohpp = sz == 0 ? NULL : &mp->hfrag.off_heap; return mp; } - return erts_try_alloc_message_on_heap(pp, psp, plp, sz, hpp, ohpp, &on_heap); + mp = erts_try_alloc_message_on_heap(pp, psp, plp, sz, hpp, ohpp, &on_heap); + ASSERT(pp || !on_heap); + return mp; } ERTS_GLB_INLINE ErtsMessage * @@ -2343,7 +2348,7 @@ erts_alloc_message_heap(Process *pp, Eterm **hpp, ErlOffHeap **ohpp) { - erts_aint32_t state = erts_smp_atomic32_read_nob(&pp->state); + erts_aint32_t state = pp ? erts_smp_atomic32_read_nob(&pp->state) : 0; return erts_alloc_message_heap_state(pp, &state, plp, sz, hpp, ohpp); } diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 1e6ae8c82e..5ba58e9350 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -52,8 +52,10 @@ Export exp_send, exp_receive, exp_timeout; static ErtsTracer system_seq_tracer; -static Uint default_trace_flags; -static ErtsTracer default_tracer; +static Uint default_proc_trace_flags; +static ErtsTracer default_proc_tracer; +static Uint default_port_trace_flags; +static ErtsTracer default_port_tracer; static Eterm system_monitor; static Eterm system_profile; @@ -327,8 +329,10 @@ void erts_init_trace(void) { erts_bif_trace_init(); erts_system_monitor_clear(NULL); erts_system_profile_clear(NULL); - default_trace_flags = F_INITIAL_TRACE_FLAGS; - default_tracer = erts_tracer_nil; + default_proc_trace_flags = F_INITIAL_TRACE_FLAGS; + default_proc_tracer = erts_tracer_nil; + default_port_trace_flags = F_INITIAL_TRACE_FLAGS; + default_port_tracer = erts_tracer_nil; system_seq_tracer = erts_tracer_nil; #ifdef ERTS_SMP init_sys_msg_dispatcher(); @@ -456,56 +460,111 @@ erts_get_system_seq_tracer(void) } static ERTS_INLINE void -get_default_tracing(Uint *flagsp, ErtsTracer *tracerp) +get_default_tracing(Uint *flagsp, ErtsTracer *tracerp, + Uint *default_trace_flags, + ErtsTracer *default_tracer) { - if (!(default_trace_flags & TRACEE_FLAGS)) - ERTS_TRACER_CLEAR(&default_tracer); + if (!(*default_trace_flags & TRACEE_FLAGS)) + ERTS_TRACER_CLEAR(default_tracer); - if (ERTS_TRACER_IS_NIL(default_tracer)) { - default_trace_flags &= ~TRACEE_FLAGS; + if (ERTS_TRACER_IS_NIL(*default_tracer)) { + *default_trace_flags &= ~TRACEE_FLAGS; } else { Eterm nif_result = call_enabled_tracer( - NULL, default_tracer, NULL, + NULL, *default_tracer, NULL, am_trace_status, am_undefined); switch (nif_result) { case am_trace: break; - default: - default_trace_flags &= ~TRACEE_FLAGS; - ERTS_TRACER_CLEAR(&default_tracer); + default: { + ErtsTracer curr_default_tracer = *default_tracer; + if (tracerp) { + /* we only have a rlock, so we have to unlock and then rwlock */ + erts_smp_rwmtx_runlock(&sys_trace_rwmtx); + erts_smp_rwmtx_rwlock(&sys_trace_rwmtx); + } + /* check if someone else changed default tracer + while we got the write lock, if so we don't do + anything. */ + if (curr_default_tracer == *default_tracer) { + *default_trace_flags &= ~TRACEE_FLAGS; + ERTS_TRACER_CLEAR(default_tracer); + } + if (tracerp) { + erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx); + erts_smp_rwmtx_rlock(&sys_trace_rwmtx); + } + } } } if (flagsp) - *flagsp = default_trace_flags; + *flagsp = *default_trace_flags; if (tracerp) { - erts_tracer_update(tracerp,default_tracer); + erts_tracer_update(tracerp,*default_tracer); } } +static ERTS_INLINE void +erts_change_default_tracing(int setflags, Uint flags, + const ErtsTracer tracer, + Uint *default_trace_flags, + ErtsTracer *default_tracer) +{ + if (setflags) + *default_trace_flags |= flags; + else + *default_trace_flags &= ~flags; + + erts_tracer_update(default_tracer, tracer); + + get_default_tracing(NULL, NULL, default_trace_flags, default_tracer); +} + void -erts_change_default_tracing(int setflags, Uint *flagsp, - const ErtsTracer tracer) +erts_change_default_proc_tracing(int setflags, Uint flagsp, + const ErtsTracer tracer) { erts_smp_rwmtx_rwlock(&sys_trace_rwmtx); - if (flagsp) { - if (setflags) - default_trace_flags |= *flagsp; - else - default_trace_flags &= ~(*flagsp); - } - - erts_tracer_update(&default_tracer, tracer); + erts_change_default_tracing( + setflags, flagsp, tracer, + &default_proc_trace_flags, + &default_proc_tracer); + erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx); +} - get_default_tracing(flagsp, NULL); +void +erts_change_default_port_tracing(int setflags, Uint flagsp, + const ErtsTracer tracer) +{ + erts_smp_rwmtx_rwlock(&sys_trace_rwmtx); + erts_change_default_tracing( + setflags, flagsp, tracer, + &default_port_trace_flags, + &default_port_tracer); erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx); } void -erts_get_default_tracing(Uint *flagsp, ErtsTracer *tracerp) +erts_get_default_proc_tracing(Uint *flagsp, ErtsTracer *tracerp) +{ + erts_smp_rwmtx_rlock(&sys_trace_rwmtx); + *tracerp = erts_tracer_nil; /* initialize */ + get_default_tracing( + flagsp, tracerp, + &default_proc_trace_flags, + &default_proc_tracer); + erts_smp_rwmtx_runlock(&sys_trace_rwmtx); +} + +void +erts_get_default_port_tracing(Uint *flagsp, ErtsTracer *tracerp) { erts_smp_rwmtx_rlock(&sys_trace_rwmtx); *tracerp = erts_tracer_nil; /* initialize */ - get_default_tracing(flagsp, tracerp); + get_default_tracing( + flagsp, tracerp, + &default_port_trace_flags, + &default_port_tracer); erts_smp_rwmtx_runlock(&sys_trace_rwmtx); } @@ -710,9 +769,7 @@ trace_send(Process *p, Eterm to, Eterm msg) Eterm operation = am_send; ErtsTracerNif *tnif = NULL; - if (!ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND)) { - return; - } + ASSERT(ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND)); if (is_internal_pid(to)) { if (!erts_proc_lookup(to)) @@ -720,10 +777,8 @@ trace_send(Process *p, Eterm to, Eterm msg) } else if(is_external_pid(to) && external_pid_dist_entry(to) == erts_this_dist_entry) { - char *s; send_to_non_existing_process: - s = "send_to_non_existing_process"; - operation = am_atom_put(s, sys_strlen(s)); + operation = am_send_to_non_existing_process; } if (is_tracer_proc_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, operation)) @@ -1666,9 +1721,11 @@ profile_scheduler_q(Eterm scheduler_id, Eterm state, Eterm no_schedulers, Uint M void trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) { - - send_to_tracer_nif(NULL, &p->common, calling_pid, NULL, am_open, - p->common.id, drv_name); + ErtsTracerNif *tnif = NULL; + ERTS_SMP_CHK_NO_PROC_LOCKS; + if (is_tracer_proc_enabled(NULL, 0, &p->common, &tnif, am_open)) + send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, am_open, + calling_pid, drv_name); } /* Sends trace message: @@ -1681,10 +1738,208 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) { void trace_port(Port *t_p, Eterm what, Eterm data) { + ErtsTracerNif *tnif = NULL; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) || erts_thr_progress_is_blocking()); - send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, NULL, - am_open, data, THE_NON_VALUE); + ERTS_SMP_CHK_NO_PROC_LOCKS; + if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, what)) + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, + what, data, THE_NON_VALUE); +} + + +static Eterm +trace_port_tmp_binary(char *bin, Sint sz, Binary **bptrp, Eterm **hp) +{ + if (sz <= ERL_ONHEAP_BIN_LIMIT) { + ErlHeapBin *hb = (ErlHeapBin *)*hp; + hb->thing_word = header_heap_bin(sz); + hb->size = sz; + sys_memcpy(hb->data, bin, sz); + *hp += heap_bin_size(sz); + return make_binary(hb); + } else { + ProcBin* pb = (ProcBin *)*hp; + Binary *bptr = erts_bin_nrml_alloc(sz); + erts_refc_init(&bptr->refc, 1); + sys_memcpy(bptr->orig_bytes, bin, sz); + pb->thing_word = HEADER_PROC_BIN; + pb->size = sz; + pb->next = NULL; + pb->val = bptr; + pb->bytes = (byte*) bptr->orig_bytes; + pb->flags = 0; + *bptrp = bptr; + *hp += PROC_BIN_SIZE; + return make_binary(pb); + } +} + +/* Sends trace message: + * {trace, PortPid, 'receive', {pid(), {command, iolist()}}} + * {trace, PortPid, 'receive', {pid(), {control, pid()}}} + * {trace, PortPid, 'receive', {pid(), exit}} + * + */ +void +trace_port_receive(Port *t_p, Eterm caller, Eterm what, ...) +{ + ErtsTracerNif *tnif = NULL; + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) + || erts_thr_progress_is_blocking()); + ERTS_SMP_CHK_NO_PROC_LOCKS; + if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, am_receive)) { + /* We can use a stack heap here, as the nif is called in the + context of a port */ +#define LOCAL_HEAP_SIZE (3 + 3 + heap_bin_size(ERL_ONHEAP_BIN_LIMIT) + 3) + DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); + + Eterm *hp, data, *orig_hp = NULL; + Binary *bptr = NULL; + va_list args; + UseTmpHeapNoproc(LOCAL_HEAP_SIZE); + hp = local_heap; + + if (what == am_close) { + data = what; + } else { + Eterm arg; + va_start(args, what); + if (what == am_command) { + char *bin = va_arg(args, char *); + Sint sz = va_arg(args, Sint); + va_end(args); + arg = trace_port_tmp_binary(bin, sz, &bptr, &hp); + } else if (what == am_call || what == am_control) { + unsigned int command = va_arg(args, unsigned int); + char *bin = va_arg(args, char *); + Sint sz = va_arg(args, Sint); + Eterm cmd; + va_end(args); + arg = trace_port_tmp_binary(bin, sz, &bptr, &hp); +#if defined(ARCH_32) + if (!IS_USMALL(0, command)) { + *hp = make_pos_bignum_header(1); + BIG_DIGIT(hp, 0) = (Uint)command; + cmd = make_big(hp); + hp += 2; + } else +#endif + { + cmd = make_small((Sint)command); + } + arg = TUPLE2(hp, cmd, arg); + hp += 3; + } else if (what == am_commandv) { + ErlIOVec *evp = va_arg(args, ErlIOVec*); + int i; + va_end(args); + if ((6 + evp->vsize * (2+PROC_BIN_SIZE+ERL_SUB_BIN_SIZE)) > LOCAL_HEAP_SIZE) { + hp = erts_alloc(ERTS_ALC_T_TMP, + (6 + evp->vsize * (2+PROC_BIN_SIZE+ERL_SUB_BIN_SIZE)) * sizeof(Eterm)); + orig_hp = hp; + } + arg = NIL; + /* Convert each element in the ErlIOVec to a sub bin that points + to a procbin. We don't have to increment the proc bin refc as + the port task keeps the reference alive. */ + for (i = evp->vsize-1; i >= 0; i--) { + if (evp->iov[i].iov_len) { + ProcBin* pb = (ProcBin*)hp; + ErlSubBin *sb; + ASSERT(evp->binv[i]); + pb->thing_word = HEADER_PROC_BIN; + pb->val = ErlDrvBinary2Binary(evp->binv[i]); + pb->size = pb->val->orig_size; + pb->next = NULL; + pb->bytes = (byte*) pb->val->orig_bytes; + pb->flags = 0; + hp += PROC_BIN_SIZE; + + sb = (ErlSubBin*) hp; + sb->thing_word = HEADER_SUB_BIN; + sb->size = evp->iov[i].iov_len; + sb->offs = (byte*)(evp->iov[i].iov_base) - pb->bytes; + sb->orig = make_binary(pb); + sb->bitoffs = 0; + sb->bitsize = 0; + sb->is_writable = 0; + hp += ERL_SUB_BIN_SIZE; + + arg = CONS(hp, make_binary(sb), arg); + hp += 2; + } + } + what = am_command; + } else { + arg = va_arg(args, Eterm); + va_end(args); + } + data = TUPLE2(hp, what, arg); + hp += 3; + } + + data = TUPLE2(hp, caller, data); + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, + am_receive, data, THE_NON_VALUE); + + if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0) + erts_bin_free(bptr); + + if (orig_hp) + erts_free(ERTS_ALC_T_TMP, orig_hp); + + UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); + } +#undef LOCAL_HEAP_SIZE +} + +void +trace_port_send(Port *t_p, Eterm receiver, Eterm msg, int exists) +{ + ErtsTracerNif *tnif = NULL; + Eterm op = exists ? am_send : am_send_to_non_existing_process; + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) + || erts_thr_progress_is_blocking()); + ERTS_SMP_CHK_NO_PROC_LOCKS; + if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, op)) + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, + op, msg, receiver); +} + +void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz) +{ + ErtsTracerNif *tnif = NULL; + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) + || erts_thr_progress_is_blocking()); + ERTS_SMP_CHK_NO_PROC_LOCKS; + if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, am_send)) { + Eterm msg; + Binary* bptr = NULL; +#define LOCAL_HEAP_SIZE (3 + 3 + heap_bin_size(ERL_ONHEAP_BIN_LIMIT)) + DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); + + Eterm *hp; + + ERTS_CT_ASSERT(heap_bin_size(ERL_ONHEAP_BIN_LIMIT) >= PROC_BIN_SIZE); + UseTmpHeapNoproc(LOCAL_HEAP_SIZE); + hp = local_heap; + + msg = trace_port_tmp_binary(bin, sz, &bptr, &hp); + + msg = TUPLE2(hp, what, msg); + hp += 3; + msg = TUPLE2(hp, t_p->common.id, msg); + hp += 3; + + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, + am_send, msg, to); + if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0) + erts_bin_free(bptr); + + UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); +#undef LOCAL_HEAP_SIZE + } } /* Send {trace_ts, Pid, What, {Mod, Func, Arity}, Timestamp} @@ -1702,9 +1957,13 @@ trace_sched_ports(Port *p, Eterm what) { void trace_sched_ports_where(Port *t_p, Eterm what, Eterm where) { - - send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, - NULL, am_open, where, THE_NON_VALUE); + ErtsTracerNif *tnif = NULL; + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) + || erts_thr_progress_is_blocking()); + ERTS_SMP_CHK_NO_PROC_LOCKS; + if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, what)) + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, + tnif, what, where, THE_NON_VALUE); } /* Port profiling */ @@ -2523,7 +2782,7 @@ is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, } int erts_is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, - ErtsPTabElementCommon *t_p) + ErtsPTabElementCommon *t_p, Eterm type) { return is_tracer_proc_enabled(c_p, c_p_locks, t_p, NULL, am_trace_status); } diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index 61f82a6b93..0fefd6f70d 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -68,9 +68,12 @@ ErtsTracer erts_set_system_seq_tracer(Process *c_p, ErtsProcLocks c_p_locks, ErtsTracer new); ErtsTracer erts_get_system_seq_tracer(void); -void erts_change_default_tracing(int setflags, Uint *flagsp, - const ErtsTracer tracerp); -void erts_get_default_tracing(Uint *flagsp, ErtsTracer *tracerp); +void erts_change_default_proc_tracing(int setflags, Uint flagsp, + const ErtsTracer tracerp); +void erts_get_default_proc_tracing(Uint *flagsp, ErtsTracer *tracerp); +void erts_change_default_port_tracing(int setflags, Uint flagsp, + const ErtsTracer tracerp); +void erts_get_default_port_tracing(Uint *flagsp, ErtsTracer *tracerp); void erts_set_system_monitor(Eterm monitor); Eterm erts_get_system_monitor(void); int erts_is_tracer_valid(Process* p); @@ -106,6 +109,9 @@ void trace_sched_ports(Port *pp, Eterm); void trace_sched_ports_where(Port *pp, Eterm, Eterm); void trace_port(Port *, Eterm what, Eterm data); void trace_port_open(Port *, Eterm calling_pid, Eterm drv_name); +void trace_port_receive(Port *, Eterm calling_pid, Eterm tag, ...); +void trace_port_send(Port *, Eterm to, Eterm msg, int exists); +void trace_port_send_binary(Port *, Eterm to, Eterm what, char *bin, Sint sz); /* system_profile */ void erts_set_system_profile(Eterm profile); @@ -177,7 +183,7 @@ int erts_finish_breakpointing(void); /* Nif tracer functions */ int erts_is_tracer_proc_enabled(Process *c_p, ErtsProcLocks c_p_locks, - ErtsPTabElementCommon *t_p); + ErtsPTabElementCommon *t_p, Eterm type); int erts_is_tracer_enabled(Process *c_p, const ErtsTracer tracer); Eterm erts_tracer_to_term(Process *p, ErtsTracer tracer); ErtsTracer erts_term_to_tracer(Eterm prefix, Eterm term); @@ -210,6 +216,7 @@ ERTS_DECLARE_DUMMY(erts_tracer_nil) = NIL; #define ERTS_TRACER_PROC_IS_ENABLED(PROC) \ (!ERTS_TRACER_IS_NIL(ERTS_TRACER(PROC)) \ - && erts_is_tracer_proc_enabled(PROC, ERTS_PROC_LOCK_MAIN, &(PROC)->common)) + && erts_is_tracer_proc_enabled(PROC, ERTS_PROC_LOCK_MAIN, \ + &(PROC)->common, am_trace_status)) #endif /* ERL_TRACE_H__ */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index a35c305046..18cfb1e641 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -88,7 +88,7 @@ int erts_port_parallelism = 0; static erts_atomic64_t bytes_in; static erts_atomic64_t bytes_out; -static void deliver_result(Eterm sender, Eterm pid, Eterm res); +static void deliver_result(Port *p, Eterm sender, Eterm pid, Eterm res); static int init_driver(erts_driver_t *, ErlDrvEntry *, DE_Handle *); static void terminate_port(Port *p); static void pdl_init(void); @@ -405,7 +405,7 @@ static Port *create_port(char *name, prt->os_pid = -1; /* Set default tracing */ - erts_get_default_tracing(&ERTS_TRACE_FLAGS(prt), &ERTS_TRACER(prt)); + erts_get_default_port_tracing(&ERTS_TRACE_FLAGS(prt), &ERTS_TRACER(prt)); ERTS_CT_ASSERT(offsetof(Port,common) == 0); @@ -710,7 +710,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ if (driver->start) { ERTS_MSACC_PUSH_STATE_M(); if (IS_TRACED_FL(port, F_TRACE_SCHED_PORTS)) { - trace_sched_ports_where(port, am_in, am_start); + trace_sched_ports_where(port, am_in, am_open); } port->caller = pid; #ifdef USE_VM_PROBES @@ -754,7 +754,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ ERTS_MSACC_POP_STATE_M(); port->caller = NIL; if (IS_TRACED_FL(port, F_TRACE_SCHED_PORTS)) { - trace_sched_ports_where(port, am_out, am_start); + trace_sched_ports_where(port, am_out, am_open); } #ifdef ERTS_SMP if (port->xports) @@ -1742,6 +1742,10 @@ call_driver_outputv(int bang_op, ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt) || ERTS_IS_CRASH_DUMPING); + + if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) + trace_port_receive(prt, caller, am_commandv, evp); + #ifdef USE_VM_PROBES if (DTRACE_ENABLED(driver_outputv)) { DTRACE_FORMAT_COMMON_PID_AND_PORT(caller, prt); @@ -1868,6 +1872,9 @@ call_driver_output(int bang_op, } #endif + if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) + trace_port_receive(prt, caller, am_command, bufp, size); + prt->caller = caller; (*drv->output)((ErlDrvData) prt->drv_data, bufp, size); prt->caller = NIL; @@ -2589,7 +2596,10 @@ call_deliver_port_exit(int bang_op, return ERTS_PORT_OP_DROPPED; } - if (!erts_deliver_port_exit(prt, from, reason, bang_op)) + if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) + trace_port_receive(prt, from, am_close); + + if (!erts_deliver_port_exit(prt, from, reason, bang_op, broken_link)) return ERTS_PORT_OP_DROPPED; #ifdef USE_VM_PROBES @@ -2660,13 +2670,13 @@ erts_port_exit(Process *c_p, ERTS_PORT_SFLGS_INVALID_LOOKUP, 0, !refp, - am_exit); + am_close); switch (try_imm_drv_call(&try_call_state)) { case ERTS_TRY_IMM_DRV_CALL_OK: { res = call_deliver_port_exit(flags & ERTS_PORT_SIG_FLG_BANG_OP, - from, + c_p ? c_p->common.id : from, prt, try_call_state.state, reason, @@ -2745,8 +2755,11 @@ set_port_connected(int bang_op, return ERTS_PORT_OP_DROPPED; } + if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) + trace_port_receive(prt, from, am_connect, connect); + ERTS_PORT_SET_CONNECTED(prt, connect); - deliver_result(prt->common.id, from, am_connected); + deliver_result(prt, prt->common.id, from, am_connected); #ifdef USE_VM_PROBES if(DTRACE_ENABLED(port_command)) { @@ -2768,10 +2781,22 @@ set_port_connected(int bang_op, erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, prt->common.id); erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, connect); + if (IS_TRACED_FL(rp, F_TRACE_PROCS)) + trace_proc(NULL, 0, rp, am_getting_linked, prt->common.id); + ERTS_PORT_SET_CONNECTED(prt, connect); erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + if (IS_TRACED_FL(prt, F_TRACE_PORTS)) + trace_port(prt, am_getting_linked, connect); + if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) + trace_port_receive(prt, from, am_connect, connect); + if (IS_TRACED_FL(prt, F_TRACE_SEND)) { + Eterm hp[3]; + trace_port_send(prt, from, TUPLE2(hp, prt->common.id, am_connected), 1); + } + #ifdef USE_VM_PROBES if (DTRACE_ENABLED(port_connect)) { DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); @@ -2874,8 +2899,11 @@ static void port_unlink(Port *prt, Eterm from) { ErtsLink *lnk = erts_remove_link(&ERTS_P_LINKS(prt), from); - if (lnk) + if (lnk) { + if (IS_TRACED_FL(prt, F_TRACE_PORTS)) + trace_port(prt, am_getting_unlinked, from); erts_destroy_link(lnk); + } } static int @@ -2945,7 +2973,7 @@ port_link_failure(Eterm port_id, Eterm linker) NIL, NULL, 0); - if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) { + if (xres >= 0) { /* We didn't exit the process and it is traced */ if (IS_TRACED_FL(rp, F_TRACE_PROCS)) trace_proc(NULL, 0, rp, am_getting_unlinked, port_id); @@ -2959,10 +2987,15 @@ port_link_failure(Eterm port_id, Eterm linker) static void port_link(Port *prt, erts_aint32_t state, Eterm to) { - if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) + if (IS_TRACED_FL(prt, F_TRACE_PORTS)) + trace_port(prt, am_getting_linked, to); + if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) { erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, to); - else + } else { port_link_failure(prt->common.id, to); + if (IS_TRACED_FL(prt, F_TRACE_PORTS)) + trace_port(prt, am_unlink, to); + } } static int @@ -2970,8 +3003,13 @@ port_sig_link(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigd { if (op == ERTS_PROC2PORT_SIG_EXEC) port_link(prt, state, sigdp->u.link.to); - else + else { + if (IS_TRACED_FL(prt, F_TRACE_PORTS)) + trace_port(prt, am_getting_linked, sigdp->u.link.to); port_link_failure(sigdp->u.link.port, sigdp->u.link.to); + if (IS_TRACED_FL(prt, F_TRACE_PORTS)) + trace_port(prt, am_unlink, sigdp->u.link.to); + } if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) port_sched_op_reply(sigdp->caller, sigdp->ref, am_true); return ERTS_PORT_REDS_LINK; @@ -3391,7 +3429,7 @@ static int read_linebuf(LineBufContext *bp) } static void -deliver_result(Eterm sender, Eterm pid, Eterm res) +deliver_result(Port *prt, Eterm sender, Eterm pid, Eterm res) { Process *rp; ErtsProcLocks rp_locks = 0; @@ -3399,12 +3437,22 @@ deliver_result(Eterm sender, Eterm pid, Eterm res) ERTS_SMP_CHK_NO_PROC_LOCKS; + ASSERT(!prt || prt->common.id == sender); +#ifdef ERTS_SMP + ASSERT(!prt || erts_lc_is_port_locked(prt)); +#endif + ASSERT(is_internal_port(sender) && is_internal_pid(pid)); rp = (scheduler ? erts_proc_lookup(pid) : erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_INC_REFC)); + if (prt && IS_TRACED_FL(prt, F_TRACE_SEND)) { + Eterm hp[3]; + trace_port_send(prt, pid, TUPLE2(hp, sender, res), !!rp); + } + if (rp) { Eterm tuple; ErtsMessage *mp; @@ -3449,6 +3497,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to, ErlOffHeap *ohp; ErtsProcLocks rp_locks = 0; int scheduler = erts_get_scheduler_id() != 0; + int trace_send = IS_TRACED_FL(prt, F_TRACE_SEND); ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); ERTS_SMP_CHK_NO_PROC_LOCKS; @@ -3471,7 +3520,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to, if (!rp) return; - mp = erts_alloc_message_heap(rp, &rp_locks, need, &hp, &ohp); + mp = erts_alloc_message_heap(trace_send ? NULL : rp, &rp_locks, need, &hp, &ohp); listp = NIL; if ((state & ERTS_PORT_SFLG_BINARY_IO) == 0) { @@ -3513,6 +3562,9 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to, tuple = TUPLE2(hp, prt->common.id, tuple); hp += 3; + if (trace_send) + trace_port_send(prt, to, tuple, 1); + ERL_MESSAGE_TOKEN(mp) = am_undefined; erts_queue_message(rp, &rp_locks, mp, tuple); if (rp_locks) @@ -3593,6 +3645,7 @@ deliver_vec_message(Port* prt, /* Port */ ErtsProcLocks rp_locks = 0; int scheduler = erts_get_scheduler_id() != 0; erts_aint32_t state; + int trace_send = IS_TRACED_FL(prt, F_TRACE_SEND); ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); ERTS_SMP_CHK_NO_PROC_LOCKS; @@ -3620,7 +3673,7 @@ deliver_vec_message(Port* prt, /* Port */ need += (hlen+csize)*2; } - mp = erts_alloc_message_heap(rp, &rp_locks, need, &hp, &ohp); + mp = erts_alloc_message_heap(trace_send ? NULL : rp, &rp_locks, need, &hp, &ohp); listp = NIL; iov += vsize; @@ -3681,6 +3734,9 @@ deliver_vec_message(Port* prt, /* Port */ tuple = TUPLE2(hp, prt->common.id, tuple); hp += 3; + if (IS_TRACED_FL(prt, F_TRACE_SEND)) + trace_port_send(prt, to, tuple, 1); + ERL_MESSAGE_TOKEN(mp) = am_undefined; erts_queue_message(rp, &rp_locks, mp, tuple); erts_smp_proc_unlock(rp, rp_locks); @@ -3822,6 +3878,11 @@ terminate_port(Port *prt) ASSERT(!prt->xports); #endif } + + if (is_internal_port(send_closed_port_id) + && IS_TRACED_FL(prt, F_TRACE_SEND)) + trace_port_send(prt, connected_id, am_closed, 1); + if(drv->handle != NULL) { erts_smp_rwmtx_rlock(&erts_driver_list_lock); erts_ddll_decrement_port_count(drv->handle); @@ -3853,7 +3914,7 @@ terminate_port(Port *prt) erts_flush_async_exit(erts_halt_code, ""); } if (is_internal_port(send_closed_port_id)) - deliver_result(send_closed_port_id, connected_id, am_closed); + deliver_result(NULL, send_closed_port_id, connected_id, am_closed); } void @@ -3886,7 +3947,7 @@ static void sweep_one_monitor(ErtsMonitor *mon, void *vpsc) typedef struct { - Eterm port; + Port *port; Eterm reason; } SweepContext; @@ -3895,10 +3956,13 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc) SweepContext *psc = vpsc; DistEntry *dep; Process *rp; - + Eterm port_id = psc->port->common.id; ASSERT(lnk->type == LINK_PID); - + + if (IS_TRACED_FL(psc->port, F_TRACE_PORTS)) + trace_port(psc->port, am_unlink, lnk->pid); + if (is_external_pid(lnk->pid)) { dep = external_pid_dist_entry(lnk->pid); if(dep != erts_this_dist_entry) { @@ -3911,9 +3975,9 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc) case ERTS_DSIG_PREP_NOT_CONNECTED: break; case ERTS_DSIG_PREP_CONNECTED: - erts_remove_dist_link(&dld, psc->port, lnk->pid, dep); + erts_remove_dist_link(&dld, port_id, lnk->pid, dep); erts_destroy_dist_link(&dld); - code = erts_dsig_send_exit(&dsd, psc->port, lnk->pid, + code = erts_dsig_send_exit(&dsd, port_id, lnk->pid, psc->reason); ASSERT(code == ERTS_DSIG_SEND_OK); break; @@ -3927,23 +3991,25 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc) ASSERT(is_internal_pid(lnk->pid)); rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks); if (rp) { - ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), psc->port); + ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), port_id); if (rlnk) { int xres = erts_send_exit_signal(NULL, - psc->port, + port_id, rp, &rp_locks, psc->reason, NIL, NULL, 0); - if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) { + if (xres >= 0) { + if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) { + erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND); + rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND; + } /* We didn't exit the process and it is traced */ - if (IS_TRACED_FL(rp, F_TRACE_PROCS)) { - trace_proc(NULL, 0, rp, am_getting_unlinked, - psc->port); - } + if (IS_TRACED_FL(rp, F_TRACE_PROCS)) + trace_proc(NULL, 0, rp, am_getting_unlinked, port_id); } erts_destroy_link(rlnk); } @@ -3965,7 +4031,8 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc) */ int -erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed) +erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed, + int drop_normal) { ErtsLink *lnk; Eterm rreason; @@ -3995,8 +4062,10 @@ erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed) | ERTS_PORT_SFLG_CLOSING)) return 0; - if (reason == am_normal && from != ERTS_PORT_GET_CONNECTED(p) && from != p->common.id) + if (reason == am_normal && from != ERTS_PORT_GET_CONNECTED(p) + && from != p->common.id && drop_normal) { return 0; + } set_state_flags = ERTS_PORT_SFLG_EXITING; if (send_closed) @@ -4007,9 +4076,8 @@ erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed) state = erts_atomic32_read_bor_mb(&p->state, set_state_flags); state |= set_state_flags; - if (IS_TRACED_FL(p, F_TRACE_PORTS)) { + if (IS_TRACED_FL(p, F_TRACE_PORTS)) trace_port(p, am_closed, reason); - } erts_trace_check_exiting(p->common.id); @@ -4019,7 +4087,7 @@ erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed) (void) erts_unregister_name(NULL, 0, p, p->common.u.alive.reg->name); { - SweepContext sc = {p->common.id, rreason}; + SweepContext sc = {p, rreason}; lnk = ERTS_P_LINKS(p); ERTS_P_LINKS(p) = NULL; erts_sweep_links(lnk, &sweep_one_link, &sc); @@ -4141,7 +4209,10 @@ call_driver_control(Eterm caller, command, size); } #endif - + + if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) + trace_port_receive(prt, caller, am_control, command, bufp, size); + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); #ifdef USE_LTTNG_VM_TRACEPOINTS @@ -4168,6 +4239,9 @@ call_driver_control(Eterm caller, if (cres < 0) return ERTS_PORT_OP_BADARG; + if (IS_TRACED_FL(prt, F_TRACE_SEND)) + trace_port_send_binary(prt, caller, am_control, *resp_bufp, cres); + *from_size = (ErlDrvSizeT) cres; return ERTS_PORT_OP_DONE; @@ -4577,6 +4651,9 @@ call_driver_call(Eterm caller, } #endif + if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) + trace_port_receive(prt, caller, am_call, command, bufp, size); + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); prt->caller = caller; @@ -4595,6 +4672,9 @@ call_driver_call(Eterm caller, || ((byte) (*resp_bufp)[0]) != VERSION_MAGIC) return ERTS_PORT_OP_BADARG; + if (IS_TRACED_FL(prt, F_TRACE_SEND)) + trace_port_send_binary(prt, caller, am_call, *resp_bufp, cres); + *from_size = (ErlDrvSizeT) cres; return ERTS_PORT_OP_DONE; @@ -5038,6 +5118,7 @@ reply_io_bytes(void *vreq) eout = erts_bld_uint64(&hp, NULL, out); msg = TUPLE4(hp, ref, make_small(sched_id), ein, eout); + erts_queue_message(rp, &rp_locks, mp, msg); if (req->sched_id == sched_id) @@ -5509,6 +5590,7 @@ void driver_report_exit(ErlDrvPort ix, int status) ErtsProcLocks rp_locks = 0; int scheduler = erts_get_scheduler_id() != 0; Port* prt = erts_drvport2port(ix); + int trace_send = IS_TRACED_FL(prt, F_TRACE_SEND); if (prt == ERTS_INVALID_ERL_DRV_PORT) return; @@ -5525,12 +5607,15 @@ void driver_report_exit(ErlDrvPort ix, int status) if (!rp) return; - mp = erts_alloc_message_heap(rp, &rp_locks, 3+3, &hp, &ohp); + mp = erts_alloc_message_heap(trace_send ? NULL : rp, &rp_locks, 3+3, &hp, &ohp); tuple = TUPLE2(hp, am_exit_status, make_small(status)); hp += 3; tuple = TUPLE2(hp, prt->common.id, tuple); + if (IS_TRACED_FL(prt, F_TRACE_SEND)) + trace_port_send(prt, pid, tuple, 1); + ERL_MESSAGE_TOKEN(mp) = am_undefined; erts_queue_message(rp, &rp_locks, mp, tuple); @@ -5626,13 +5711,13 @@ cleanup_b2t_states(struct b2t_states__ *b2tsp) */ static int -driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) +driver_deliver_term(Port *prt, Eterm to, ErlDrvTermData* data, int len) { #define HEAP_EXTRA 200 #define ERTS_DDT_FAIL do { res = -1; goto done; } while (0) Uint need = 0; int depth = 0; - int res; + int res = 0; ErlDrvTermData* ptr; ErlDrvTermData* ptr_end; DECLARE_ESTACK(stack); @@ -5851,11 +5936,26 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) ? erts_proc_lookup(to) : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_INC_REFC)); if (!rp) { - res = 0; - goto done; - } + if (!prt || !IS_TRACED_FL(prt, F_TRACE_SEND)) + goto done; + if (!erts_is_tracer_proc_enabled(NULL, 0, &prt->common, am_send)) + goto done; - (void) erts_factory_message_create(&factory, rp, &rp_locks, need); + res = -2; + + /* We allocate a temporary heap to be used to create + the message that may be sent using tracing */ + erts_factory_tmp_init(&factory, erts_alloc(ERTS_ALC_T_DRIVER, need*sizeof(Eterm)), + need, ERTS_ALC_T_DRIVER); + + } else { + /* We force the creation of a heap fragment (rp == NULL) when send + tracing so that we don't have the main lock of the process while + tracing */ + Process *trace_rp = prt && IS_TRACED_FL(prt, F_TRACE_SEND) ? NULL : rp; + (void) erts_factory_message_create(&factory, trace_rp, &rp_locks, need); + res = 1; + } /* * Interpret the instructions and build the term. @@ -6118,23 +6218,40 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) ESTACK_PUSH(stack, mess); } - res = 1; - done: if (res > 0) { mess = ESTACK_POP(stack); /* get resulting value */ erts_factory_trim_and_close(&factory, &mess, 1); + + if (prt && IS_TRACED_FL(prt, F_TRACE_SEND)) + trace_port_send(prt, to, mess, 1); + /* send message */ ERL_MESSAGE_TOKEN(factory.message) = am_undefined; erts_queue_message(rp, &rp_locks, factory.message, mess); } + else if (res == -2) { + /* this clause only happens when we were requested to + generate a send trace, but the process to send to + did not exist any more */ + mess = ESTACK_POP(stack); /* get resulting value */ + + trace_port_send(prt, to, mess, 0); + + erts_factory_trim_and_close(&factory, &mess, 1); + erts_free(ERTS_ALC_T_DRIVER, factory.hp_start); + res = 0; + } else { if (b2t.ix > b2t.used) b2t.used = b2t.ix; for (b2t.ix = 0; b2t.ix < b2t.used; b2t.ix++) erts_binary2term_abort(&b2t.state[b2t.ix]); - erts_factory_undo(&factory); + if (factory.mode != FACTORY_CLOSED) { + ERL_MESSAGE_TERM(factory.message) = am_undefined; + erts_factory_undo(&factory); + } } if (rp) { if (rp_locks) @@ -6150,7 +6267,8 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) } static ERTS_INLINE int -deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p) +deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p, + Port **trace_prt) { #ifdef ERTS_SMP ErtsThrPrgrDelayHandle dhndl = erts_thr_progress_unmanaged_delay(); @@ -6178,8 +6296,11 @@ deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p) if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED) { erts_thr_progress_unmanaged_continue(dhndl); ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); - } + } else #endif + { + *trace_prt = prt; + } ERTS_SMP_LC_ASSERT(dhndl == ERTS_THR_PRGR_DHANDLE_MANAGED ? erts_lc_is_port_locked(prt) : !erts_lc_is_port_locked(prt)); @@ -6190,10 +6311,11 @@ int erl_drv_output_term(ErlDrvTermData port_id, ErlDrvTermData* data, int len) { /* May be called from arbitrary thread */ Eterm connected; - int res = deliver_term_check_port(port_id, &connected); + Port *prt = NULL; + int res = deliver_term_check_port(port_id, &connected, &prt); if (res <= 0) return res; - return driver_deliver_term(connected, data, len); + return driver_deliver_term(prt, connected, data, len); } /* @@ -6217,7 +6339,7 @@ driver_output_term(ErlDrvPort drvport, ErlDrvTermData* data, int len) if (state & ERTS_PORT_SFLG_CLOSING) return 0; - return driver_deliver_term(ERTS_PORT_GET_CONNECTED(prt), data, len); + return driver_deliver_term(prt, ERTS_PORT_GET_CONNECTED(prt), data, len); } int erl_drv_send_term(ErlDrvTermData port_id, @@ -6226,10 +6348,11 @@ int erl_drv_send_term(ErlDrvTermData port_id, int len) { /* May be called from arbitrary thread */ - int res = deliver_term_check_port(port_id, NULL); + Port *prt = NULL; + int res = deliver_term_check_port(port_id, NULL, &prt); if (res <= 0) return res; - return driver_deliver_term(to, data, len); + return driver_deliver_term(prt, to, data, len); } /* @@ -6248,20 +6371,21 @@ driver_send_term(ErlDrvPort drvport, * to make this access safe without using a less efficient * internal data representation for ErlDrvPort. */ + Port* prt = NULL; ERTS_SMP_CHK_NO_PROC_LOCKS; #ifdef ERTS_SMP if (erts_thr_progress_is_managed_thread()) #endif { erts_aint32_t state; - Port* prt = erts_drvport2port_state(drvport, &state); + prt = erts_drvport2port_state(drvport, &state); if (prt == ERTS_INVALID_ERL_DRV_PORT) return -1; /* invalid (dead) */ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); if (state & ERTS_PORT_SFLG_CLOSING) return 0; } - return driver_deliver_term(to, data, len); + return driver_deliver_term(prt, to, data, len); } @@ -7437,7 +7561,7 @@ driver_failure_term(ErlDrvPort ix, Eterm term, int eof) if (state & ERTS_PORT_SFLG_CLOSING) { terminate_port(prt); } else if (eof && (state & ERTS_PORT_SFLG_SOFT_EOF)) { - deliver_result(prt->common.id, ERTS_PORT_GET_CONNECTED(prt), am_eof); + deliver_result(prt, prt->common.id, ERTS_PORT_GET_CONNECTED(prt), am_eof); } else { /* XXX UGLY WORK AROUND, Let erts_deliver_port_exit() terminate the port */ if (prt->port_data_lock) @@ -7445,7 +7569,7 @@ driver_failure_term(ErlDrvPort ix, Eterm term, int eof) prt->ioq.size = 0; if (prt->port_data_lock) driver_pdl_unlock(prt->port_data_lock); - erts_deliver_port_exit(prt, prt->common.id, eof ? am_normal : term, 0); + erts_deliver_port_exit(prt, prt->common.id, eof ? am_normal : term, 0, 0); } return 0; } diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c index f4a889b8f8..e995dcbd8a 100644 --- a/erts/emulator/beam/register.c +++ b/erts/emulator/beam/register.c @@ -538,8 +538,12 @@ int erts_unregister_name(Process *c_p, ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(port)); rp->pt->common.u.alive.reg = NULL; - + if (IS_TRACED_FL(port, F_TRACE_PORTS)) { + if (current_c_p_locks) { + erts_smp_proc_unlock(c_p, current_c_p_locks); + current_c_p_locks = 0; + } trace_port(port, am_unregister, r.name); } diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 92182e3890..de395dfb97 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -114,6 +114,7 @@ MODULES= \ tracer_test \ scheduler_SUITE \ old_scheduler_SUITE \ + port_trace_SUITE \ unique_SUITE \ z_SUITE \ old_mod \ diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index 2159546054..6ba6301c7c 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -84,32 +84,46 @@ process_specs(Config) when is_list(Config) -> {tracer,Tracer} = trace_info(self(), tracer), trace_func({?MODULE,worker_foo,1}, []), - %% Test the 'new' flag. - - {Work1A,Work1B} = start_and_trace(new, [1,2,3], A1B={3,2,1}), - {flags,[]} = trace_info(Work1A, flags), - {tracer,[]} = trace_info(Work1A, tracer), - {tracer,Tracer} = trace_info(Work1B, tracer), - {flags,[call]} = trace_info(Work1B, flags), - expect({trace,Work1B,call,{?MODULE,worker_foo,[A1B]}}), - unlink(Work1B), - Mref = erlang:monitor(process, Work1B), - exit(Work1B, kill), - receive - {'DOWN',Mref,_,_,_} -> ok - end, - undefined = trace_info(Work1B, flags), - {flags,[]} = trace_info(new, flags), - {tracer,[]} = trace_info(new, tracer), - - %% Test the 'existing' flag. - {Work2A,_Work2B} = start_and_trace(existing, A2A=[5,6,7], [7,6,5]), - expect({trace,Work2A,call,{?MODULE,worker_foo,[A2A]}}), - - %% Test the 'all' flag. - {Work3A,Work3B} = start_and_trace(all, A3A=[12,13], A3B=[13,12]), - expect({trace,Work3A,call,{?MODULE,worker_foo,[A3A]}}), - expect({trace,Work3B,call,{?MODULE,worker_foo,[A3B]}}), + %% Test the 'new' and 'new_processes' flags. + + New = fun(Flag) -> + {Work1A,Work1B} = start_and_trace(Flag, [1,2,3], A1B={3,2,1}), + {flags,[]} = trace_info(Work1A, flags), + {tracer,[]} = trace_info(Work1A, tracer), + {tracer,Tracer} = trace_info(Work1B, tracer), + {flags,[call]} = trace_info(Work1B, flags), + expect({trace,Work1B,call,{?MODULE,worker_foo,[A1B]}}), + unlink(Work1B), + Mref = erlang:monitor(process, Work1B), + exit(Work1B, kill), + receive + {'DOWN',Mref,_,_,_} -> ok + end, + undefined = trace_info(Work1B, flags), + {flags,[]} = trace_info(Flag, flags), + {tracer,[]} = trace_info(Flag, tracer) + end, + New(new), + New(new_processes), + + %% Test the 'existing' and 'existing_processes' flags. + Existing = + fun(Flag) -> + {Work2A,_Work2B} = start_and_trace(Flag, A2A=[5,6,7], [7,6,5]), + expect({trace,Work2A,call,{?MODULE,worker_foo,[A2A]}}) + end, + Existing(existing), + Existing(existing_processes), + + %% Test the 'all' and 'processes' flags. + All = + fun(Flag) -> + {Work3A,Work3B} = start_and_trace(Flag, A3A=[12,13], A3B=[13,12]), + expect({trace,Work3A,call,{?MODULE,worker_foo,[A3A]}}), + expect({trace,Work3B,call,{?MODULE,worker_foo,[A3B]}}) + end, + All(all), + All(processes), ok. diff --git a/erts/emulator/test/port_trace_SUITE.erl b/erts/emulator/test/port_trace_SUITE.erl new file mode 100644 index 0000000000..41e8a316c4 --- /dev/null +++ b/erts/emulator/test/port_trace_SUITE.erl @@ -0,0 +1,600 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + + +-module(port_trace_SUITE). + +-export([all/0, suite/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2,end_per_testcase/2]). +-export([port_specs/1, ports/1, open_close/1, + command/1, control/1, connect/1, call/1, + output/1, output2/1, output_binary/1, + outputv/1, set_timer/1, failure_eof/1, + failure_atom/1, failure_posix/1, + failure/1, output_term/1, + driver_output_term/1, + send_term/1, driver_send_term/1]). + +-define(ECHO_DRV_NOOP, 0). +-define(ECHO_DRV_OUTPUT, 1). +-define(ECHO_DRV_OUTPUT2, 2). +-define(ECHO_DRV_OUTPUT_BINARY, 3). +-define(ECHO_DRV_OUTPUTV, 4). +-define(ECHO_DRV_SET_TIMER, 5). +-define(ECHO_DRV_FAILURE_EOF, 6). +-define(ECHO_DRV_FAILURE_ATOM, 7). +-define(ECHO_DRV_FAILURE_POSIX, 8). +-define(ECHO_DRV_FAILURE, 9). +-define(ECHO_DRV_OUTPUT_TERM, 10). +-define(ECHO_DRV_DRIVER_OUTPUT_TERM, 11). +-define(ECHO_DRV_SEND_TERM, 12). +-define(ECHO_DRV_DRIVER_SEND_TERM, 13). +-define(ECHO_DRV_SAVE_CALLER, 14). + +suite() -> [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 30}}]. + +all() -> + [port_specs, ports, open_close, + command, control, connect, call, + output, output2, output_binary, + outputv, set_timer, failure_eof, + failure_atom, failure_posix, + failure, output_term, + driver_output_term, + send_term, driver_send_term]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> + erlang:trace(all, false, [all]), + os:unsetenv("OUTPUTV"), + reload_drv(Config), + Config. + +end_per_testcase(_Func, _Config) -> + erlang:trace(all, false, [all]), + ok. + +%% Test the first argument of trace/3 +port_specs(_Config) -> + + S = self(), + + Tracer = fun F() -> + receive + stop -> + ok; + M -> + S ! M, + F() + end + end, + + Test = fun(TraceSpec, Info1, Info2) -> + {TracerPid,Ref} = spawn_monitor(Tracer), + Prt1 = erlang:open_port({spawn, echo_drv}, [binary]), + erlang:trace(TraceSpec, true, ['receive', {tracer, TracerPid}]), + %% We disable trace messages from the testcase process + erlang:trace(self(), false, ['receive']), + Prt2 = erlang:open_port({spawn, echo_drv}, [binary]), + + InfoCheck = + fun(Info, Prt) -> + if + Info -> + {tracer, TracerPid} = erlang:trace_info(Prt, tracer), + {flags,['receive']} = erlang:trace_info(Prt, flags); + not Info -> + {tracer,[]} = erlang:trace_info(Prt, tracer), + {flags,[]} = erlang:trace_info(Prt, flags) + end + end, + InfoCheck(Info1, Prt1), + InfoCheck(Info2, Prt2), + + %% These may create trace messages + erlang:port_command(Prt1, <>), + erlang:port_command(Prt2, <>), + + %% Test what happens when the tracer dies + trace_delivered(), + TracerPid ! stop, + receive {'DOWN', Ref, process, TracerPid, normal} -> ok end, + + %% These should not generate any trace messages + erlang:port_command(Prt1, <>), + erlang:port_command(Prt2, <>), + + InfoCheck(false, Prt1), + InfoCheck(false, Prt2), + + erlang:port_close(Prt1), + erlang:port_close(Prt2), + erlang:trace(all, false, [all]), + {Prt1, Prt2} + end, + + {_Prt11, Prt12} = Test(new, false, true), + [{trace, Prt12, 'receive', {S, {command,<>}}}] + = flush(Prt12), + + {_Prt21, Prt22} = Test(new_ports, false, true), + [{trace, Prt22, 'receive', {S, {command,<>}}}] + = flush(Prt22), + + {Prt31, _Prt32} = Test(existing, true, false), + [{trace, Prt31, 'receive', {S, {command,<>}}}] + = flush(Prt31), + + {Prt41, _Prt42} = Test(existing_ports, true, false), + [{trace, Prt41, 'receive', {S, {command,<>}}}] + = flush(Prt41), + + {Prt51, Prt52} = Test(all, true, true), + [{trace, Prt51, 'receive', {S, {command,<>}}}] + = flush(Prt51), + [{trace, Prt52, 'receive', {S, {command,<>}}}] + = flush(Prt52), + + {Prt61, Prt62} = Test(ports, true, true), + [{trace, Prt61, 'receive', {S, {command,<>}}}] + = flush(Prt61), + [{trace, Prt62, 'receive', {S, {command,<>}}}] + = flush(Prt62), + + ok. + +%% Test that the 'ports' trace flag works +ports(_Config) -> + + {Prt, S} = trace_and_open([ports],[binary]), + + [{trace, Prt, open, S, echo_drv}, + {trace, Prt, getting_linked, S}] = flush(), + + register(?MODULE, Prt), + unregister(?MODULE), + register(?MODULE, Prt), + + [{trace,Prt,register,port_trace_SUITE}, + {trace,Prt,unregister,port_trace_SUITE}, + {trace,Prt,register,port_trace_SUITE}] = flush(), + + unlink(Prt), + link(Prt), + + [{trace,Prt,getting_unlinked,S}, + {trace,Prt,getting_linked,S}] = flush(), + + erlang:port_close(Prt), + + [{trace,Prt,closed,normal}, + {trace,Prt,unregister,port_trace_SUITE}, + {trace,Prt,unlink,S}] = flush(), + + ok. + +%% Test that port_close and ! close generate correct trace messages +open_close(_Config) -> + + S = trace_ports([send,'receive']), + + Prt = erlang:open_port({spawn, echo_drv}, [binary]), + erlang:port_close(Prt), + [{trace, Prt, 'receive', {S, close}}] = flush(), + + Prt2 = erlang:open_port({spawn, echo_drv}, [binary]), + Prt2 ! {S, close}, + recv({Prt2, closed}), + [{trace, Prt2, 'receive', {S, close}}, + {trace, Prt2, send, closed, S}] = flush(), + + catch erlang:port_close(Prt2), + [] = flush(), + + ok. + +%% Test that port_command and ! command generate correct trace messages +command(Config) -> + + Flags = [send,'receive'], + S = trace_ports(Flags), + Prt = erlang:open_port({spawn, echo_drv}, [binary]), + + erlang:port_command(Prt, <>), + [{trace, Prt, 'receive', {S, {command, <>}}}] = flush(), + + erlang:port_command(Prt, [?ECHO_DRV_NOOP, <<0:8>>]), + [{trace, Prt, 'receive', {S, {command, <>}}}] = flush(), + + Prt ! {S, {command, <>}}, + [{trace, Prt, 'receive', {S, {command, <>}}}] = flush(), + + close(Prt, Flags), + + os:putenv("OUTPUTV","true"), + reload_drv(Config), + + Prt2 = erlang:open_port({spawn, echo_drv}, [binary]), + Msg = [<<0:8>>,<<0:(8*512)>>,<<0:(8*256)>>,<<0:8>>], + + erlang:port_command(Prt2, Msg), + [{trace, Prt2, 'receive', {S, {command, Msg}}}] = flush(), + + Prt2 ! {S, {command, Msg}}, + [{trace, Prt2, 'receive', {S, {command, Msg}}}] = flush(), + + close(Prt2, Flags), + + os:unsetenv("OUTPUTV"), + + ok. + +%% Test that port_control generate correct trace messages +control(_Config) -> + + Flags = [send,'receive'], + {Prt, S} = trace_and_open(Flags,[binary]), + + [0] = erlang:port_control(Prt, 1, <>), + [{trace, Prt, 'receive', {S, {control, {1, <>}}}}, + {trace, Prt, send, {Prt, {control, <<0:8>>}}, S}] = flush(), + + [0] = erlang:port_control(Prt, (1 bsl 32) - 1, <>), + [{trace, Prt, 'receive', {S, {control, {(1 bsl 32) - 1, <>}}}}, + {trace, Prt, send, {Prt, {control, <<0:8>>}}, S}] = flush(), + + close(Prt, Flags), + + ok. + +%% Test that port_connect and ! connect generate correct trace messages +%% This includes that the proper getting_linked messages are sent +connect(_Config) -> + + + {Prt, S} = trace_and_open([send, 'receive', ports],[binary]), + + flush(), + + {Pid,Ref} = spawn_monitor( + fun() -> + receive + go -> + Prt ! {self(), {connect, S}}, + receive {Prt, connected} -> unlink(Prt) end + end + end), + erlang:trace(Pid, true, [send, 'receive', procs]), + + erlang:port_connect(Prt, Pid), + unlink(Prt), + + [{trace,Prt,getting_linked,Pid}, + {trace,Prt,'receive',{S,{connect,Pid}}}, + {trace,Prt,send,{Prt,connected},S}, + {trace,Prt,getting_unlinked, S}] = flush(Prt), + + [{trace,Pid,getting_linked,Prt}] = flush(), + + Pid ! go, + recv({'DOWN',Ref,process,Pid,normal}), + + [{trace,Prt,'receive',{Pid,{connect,S}}}, + {trace,Prt,send,{Prt,connected},Pid}, + {trace,Prt,getting_unlinked,Pid}] = flush(Prt), + + [{trace,Pid,'receive',go}, + {trace,Pid,send,{Pid,{connect,S}}, Prt}, + {trace,Pid,'receive',{Prt,connected}}, + {trace,Pid,unlink,Prt}, + {trace,Pid,exit,normal}] = flush(), + + erlang:port_close(Prt), + [{trace, Prt, 'receive', {S, close}}, + {trace, Prt, closed, normal}] = flush(), + ok. + +%% Test that port_call generate correct trace messages +call(_Config) -> + + Flags = [send,'receive'], + {Prt, S} = trace_and_open(Flags,[binary]), + + Msg = {hello, world, make_ref()}, + BinMsg = term_to_binary(Msg), + + Msg = erlang:port_call(Prt, 0, Msg), + [{trace, Prt, 'receive', {S, {call, {0, BinMsg}}}}, + {trace, Prt, send, {Prt, {call, BinMsg}}, S}] = flush(), + + close(Prt, Flags), + + ok. + +%% Test that driver_output generate correct trace messages +output(_Config) -> + + Flags = [send], + {Prt, S} = trace_and_open(Flags,[binary]), + + erlang:port_command(Prt, <>), + recv({Prt,{data,<<123456:32>>}}), + + [{trace, Prt, send, {Prt, {data, <<123456:32>>}}, S}] = flush(), + + close(Prt, Flags), + + ok. + +%% Test that driver_output2 generate correct trace messages +output2(_Config) -> + + Flags = [send], + {Prt, S} = trace_and_open(Flags,[binary]), + + erlang:port_command(Prt, <>), + recv({Prt,{data,[$a|<<123456:32>>]}}), + [{trace, Prt, send, {Prt, {data, [$a|<<123456:32>>]}}, S}] = flush(), + + close(Prt, Flags), + + ok. + +%% Test that driver_output_binary generate correct trace messages +output_binary(_Config) -> + + Flags = [send], + {Prt, S} = trace_and_open(Flags,[binary]), + + erlang:port_command(Prt, <>), + recv({Prt,{data,[$a|<<123456:32>>]}}), + [{trace, Prt, send, {Prt, {data, [$a|<<123456:32>>]}}, S}] = flush(), + + close(Prt, Flags), + + ok. + +%% Test that driver_outputv generate correct trace messages +outputv(_Config) -> + + Flags = [send], + {Prt, S} = trace_and_open(Flags,[binary]), + + erlang:port_command(Prt, <>), + recv({Prt,{data,[$a|<<123456:32>>]}}), + + [{trace, Prt, send, {Prt, {data, [$a|<<123456:32>>]}}, S}] = flush(), + + erlang:port_close(Prt), + [] = flush(), + + ok. + +%% Test that driver_set_timer generate correct trace messages +set_timer(_Config) -> + + Flags = [send,'receive'], + {Prt, S} = trace_and_open(Flags,[binary]), + + erlang:port_command(Prt, <>), + timer:sleep(100), + [{trace, Prt, 'receive', {S, {command, <>}}}, + {trace, Prt, 'receive', timeout}] = flush(), + + close(Prt, Flags), + + ok. + +%% Test that driver_failure* generate correct trace messages +failure_eof(_Config) -> + + Flags = [send,'receive', ports], + S = trace_ports(Flags), + + Prt = erlang:open_port({spawn, echo_drv}, [eof, binary]), + [{trace, Prt, open, S, echo_drv}, + {trace, Prt, getting_linked, S}] = flush(), + + erlang:port_command(Prt, <>), + recv({Prt,eof}), + [{trace, Prt, 'receive', {S, {command, <>}}}, + {trace, Prt, send, {Prt, eof}, S}] = flush(), + + close(Prt, Flags), + + %% Run same test without eof option + failure_test(<>, normal). + +failure_atom(_Config) -> + failure_test(<>, failure). +failure_posix(_Config) -> + failure_test(<>, eagain). +failure(_Config) -> + failure_test(<>, 1). + +failure_test(Failure, Reason) -> + + {Prt, S} = trace_and_open([send, 'receive', ports],[binary]), + + [{trace, Prt, open, S, echo_drv}, + {trace, Prt, getting_linked, S}] = flush(), + + process_flag(trap_exit, true), + erlang:port_command(Prt, Failure), + try + recv({'EXIT',Prt,Reason}) + after + process_flag(trap_exit, false) + end, + [{trace, Prt, 'receive', {S, {command, Failure}}}, + {trace, Prt, closed, Reason}, + {trace, Prt, unlink, S}] = flush(), + + ok. + +%% Test that erl_drv_output_term generate correct trace messages +output_term(_Config) -> + + Flags = [send], + {Prt, S} = trace_and_open(Flags,[binary]), + + erlang:port_command(Prt, <>), + recv({echo, Prt, <<123456:32>>}), + [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(), + + close(Prt, Flags), + + ok. + +%% Test that driver_output_term generate correct trace messages +driver_output_term(_Config) -> + + Flags = [send], + {Prt, S} = trace_and_open(Flags,[binary]), + + erlang:port_command(Prt, <>), + recv({echo, Prt, <<123456:32>>}), + [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(), + + close(Prt, Flags), + + ok. + +%% Test that erl_drv_send_term generate correct trace messages +send_term(_Config) -> + + Flags = [send], + {Prt, S} = trace_and_open(Flags,[binary]), + + erlang:port_command(Prt, <>), + recv({echo, Prt, <<123456:32>>}), + [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(), + + {Pid, Ref} = spawn_monitor(fun() -> erlang:port_command(Prt, <>) end), + recv({'DOWN',Ref,process,Pid,normal}), + erlang:port_command(Prt, <>), + [{trace, Prt, send_to_non_existing_process, {echo, Prt, <<123456:32>>}, Pid}] = flush(), + + close(Prt, Flags), + + ok. + +%% Test that driver_send_term generate correct trace messages +driver_send_term(_Config) -> + + Flags = [send], + {Prt, S} = trace_and_open(Flags,[binary]), + + erlang:port_command(Prt, <>), + recv({echo, Prt, <<123456:32>>}), + [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(), + + {Pid, Ref} = spawn_monitor(fun() -> erlang:port_command(Prt, <>) end), + recv({'DOWN',Ref,process,Pid,normal}), + erlang:port_command(Prt, <>), + [{trace, Prt, send_to_non_existing_process, {echo, Prt, <<123456:32>>}, Pid}] = flush(), + + close(Prt, Flags), + + ok. + +%%%%%%%%%%%%%%%%%%% +%% Helper functions +%%%%%%%%%%%%%%%%%%% + +trace_ports(TraceFlags) -> + erlang:trace(new_ports, true, TraceFlags), + self(). + +trace_and_open(TraceFlags, OpenFlags) -> + S = self(), + Ports = proplists:get_value(ports, TraceFlags), + [trace_ports(TraceFlags) || Ports], + Prt = erlang:open_port({spawn, echo_drv}, OpenFlags), + [erlang:trace(Prt, true, TraceFlags) || Ports == undefined], + {Prt, S}. + +close(Prt, Flags) -> + Recv = proplists:get_value('receive', Flags), + Ports = proplists:get_value(ports, Flags), + S = self(), + + erlang:port_close(Prt), + + if Recv, Ports -> + [{trace, Prt, 'receive', {S, close}}, + {trace, Prt, closed, normal}, + {trace, Prt, unlink, S}] = flush(); + Recv -> + [{trace, Prt, 'receive', {S, close}}] = flush(); + Ports -> + [{trace, Prt, closed, normal}, + {trace, Prt, unlink, S}] = flush(); + true -> + [] = flush() + end. + +trace_delivered() -> + Ref = erlang:trace_delivered(all), + receive {trace_delivered, all, Ref} -> ok end. + +flush() -> + flush(all). +flush(From) -> + trace_delivered(), + f(From). + +f(From) -> + receive + M when From =:= all; element(2, M) == From -> + [M | f(From)] + after 0 -> + [] + end. + +recv(Msg) -> + receive Msg -> ok after 100 -> ct:fail({did_not_get_data,Msg,flush()}) end. + +load_drv(Config) -> + Path = proplists:get_value(data_dir, Config), + case erl_ddll:load_driver(Path, echo_drv) of + ok -> ok; + {error, Error} = Res -> + io:format("~s\n", [erl_ddll:format_error(Error)]), + ct:fail(Res) + end. + +reload_drv(Config) -> + erl_ddll:unload_driver(echo_drv), + load_drv(Config). diff --git a/erts/emulator/test/port_trace_SUITE_data/Makefile.src b/erts/emulator/test/port_trace_SUITE_data/Makefile.src new file mode 100644 index 0000000000..c1bf142ccf --- /dev/null +++ b/erts/emulator/test/port_trace_SUITE_data/Makefile.src @@ -0,0 +1,3 @@ +all: echo_drv@dll@ + +@SHLIB_RULES@ diff --git a/erts/emulator/test/port_trace_SUITE_data/echo_drv.c b/erts/emulator/test/port_trace_SUITE_data/echo_drv.c new file mode 100644 index 0000000000..b5728bc170 --- /dev/null +++ b/erts/emulator/test/port_trace_SUITE_data/echo_drv.c @@ -0,0 +1,237 @@ +#include +#include "erl_driver.h" +#include +#include + + +/* ------------------------------------------------------------------------- +** Data types +**/ + + +typedef struct _erl_drv_data { + ErlDrvPort erlang_port; + ErlDrvTermData caller; +} EchoDrvData; + +#define ECHO_DRV_NOOP 0 +#define ECHO_DRV_OUTPUT 1 +#define ECHO_DRV_OUTPUT2 2 +#define ECHO_DRV_OUTPUT_BINARY 3 +#define ECHO_DRV_OUTPUTV 4 +#define ECHO_DRV_SET_TIMER 5 +#define ECHO_DRV_FAILURE_EOF 6 +#define ECHO_DRV_FAILURE_ATOM 7 +#define ECHO_DRV_FAILURE_POSIX 8 +#define ECHO_DRV_FAILURE 9 +#define ECHO_DRV_OUTPUT_TERM 10 +#define ECHO_DRV_DRIVER_OUTPUT_TERM 11 +#define ECHO_DRV_SEND_TERM 12 +#define ECHO_DRV_DRIVER_SEND_TERM 13 +#define ECHO_DRV_SAVE_CALLER 14 + + +/* ------------------------------------------------------------------------- +** Entry struct +**/ + +static EchoDrvData *echo_drv_start(ErlDrvPort port, char *command); +static void echo_drv_stop(ErlDrvData drv_data); +static void echo_drv_output(ErlDrvData drv_data, char *buf, + ErlDrvSizeT len); +static void echo_drv_outputv(ErlDrvData drv_data, ErlIOVec *iov); +static void echo_drv_finish(void); +static ErlDrvSSizeT echo_drv_control(ErlDrvData drv_data, + unsigned int command, + char *buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen); +static void echo_drv_timeout(ErlDrvData drv_data); +static ErlDrvSSizeT echo_drv_call(ErlDrvData drv_data, + unsigned int command, + char *buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen, + unsigned int *flags); + +static ErlDrvEntry echo_drv_entry = { + NULL, /* init */ + echo_drv_start, + echo_drv_stop, + echo_drv_output, + NULL, /* ready_input */ + NULL, /* ready_output */ + "echo_drv", + echo_drv_finish, + NULL, /* handle */ + echo_drv_control, + echo_drv_timeout, /* timeout */ + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, /* flush */ + echo_drv_call, /* call */ + NULL, /* event */ + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, + NULL, + NULL, + NULL +}; + +/* ------------------------------------------------------------------------- +** Entry functions +**/ + +DRIVER_INIT(echo_drv) +{ + char buff[5]; + size_t size = sizeof(buff); + + if (erl_drv_getenv("OUTPUTV", buff, &size) == -1) { + echo_drv_entry.outputv = NULL; + } else { + echo_drv_entry.outputv = echo_drv_outputv; + } + + return &echo_drv_entry; +} + +static EchoDrvData *echo_drv_start(ErlDrvPort port, char *command) +{ + EchoDrvData *echo_drv_data_p = driver_alloc(sizeof(EchoDrvData)); + echo_drv_data_p->erlang_port = port; + echo_drv_data_p->caller = driver_caller(port); + return echo_drv_data_p; +} + +static void echo_drv_stop(EchoDrvData *data_p) { + driver_free(data_p); +} + +static void echo_drv_outputv(ErlDrvData drv_data, ErlIOVec *iov) +{ + return; +} + +static void echo_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) { + EchoDrvData* data_p = (EchoDrvData *) drv_data; + ErlDrvPort port = data_p->erlang_port; + + switch (buf[0]) { + case ECHO_DRV_OUTPUT: + { + driver_output(port, buf+1, len-1); + break; + } + case ECHO_DRV_OUTPUT2: + { + driver_output2(port, "a", 1, buf+1, len-1); + break; + } + case ECHO_DRV_OUTPUT_BINARY: + { + ErlDrvBinary *bin = driver_alloc_binary(len-1); + memcpy(&bin->orig_bytes, buf+1, len-1); + driver_output_binary(port, "a", 1, bin, 1, len - 2); + driver_free_binary(bin); + break; + } + case ECHO_DRV_OUTPUTV: + { + ErlIOVec iov; + ErlDrvSizeT sz; + driver_enq(port, buf + 1, len - 1); + sz = driver_peekqv(port, &iov); + driver_outputv(port, "a", 1, &iov, 0); + driver_deq(port, sz); + break; + } + case ECHO_DRV_SET_TIMER: + { + driver_set_timer(port, 10); + break; + } + case ECHO_DRV_FAILURE_EOF: + { + driver_failure_eof(port); + break; + } + case ECHO_DRV_FAILURE_ATOM: + { + driver_failure_atom(port, buf+1); + break; + } + case ECHO_DRV_FAILURE_POSIX: + { + driver_failure_posix(port, EAGAIN); + break; + } + case ECHO_DRV_FAILURE: + { + driver_failure(port, buf[1]); + break; + } + case ECHO_DRV_OUTPUT_TERM: + case ECHO_DRV_DRIVER_OUTPUT_TERM: + case ECHO_DRV_SEND_TERM: + case ECHO_DRV_DRIVER_SEND_TERM: + { + ErlDrvTermData term[] = { + ERL_DRV_ATOM, driver_mk_atom("echo"), + ERL_DRV_PORT, driver_mk_port(port), + ERL_DRV_BUF2BINARY, (ErlDrvTermData)(buf+1), + (ErlDrvTermData)(len - 1), + ERL_DRV_TUPLE, 3}; + switch (buf[0]) { + case ECHO_DRV_OUTPUT_TERM: + erl_drv_output_term(driver_mk_port(port), term, sizeof(term) / sizeof(ErlDrvTermData)); + break; + case ECHO_DRV_DRIVER_OUTPUT_TERM: + driver_output_term(port, term, sizeof(term) / sizeof(ErlDrvTermData)); + break; + case ECHO_DRV_SEND_TERM: + driver_send_term(port, data_p->caller, + term, sizeof(term) / sizeof(ErlDrvTermData)); + break; + case ECHO_DRV_DRIVER_SEND_TERM: + erl_drv_send_term(driver_mk_port(port), data_p->caller, + term, sizeof(term) / sizeof(ErlDrvTermData)); + break; + } + break; + } + case ECHO_DRV_SAVE_CALLER: + data_p->caller = driver_caller(port); + break; + default: + break; + } +} + +static void echo_drv_finish() { + +} + +static ErlDrvSSizeT echo_drv_control(ErlDrvData drv_data, + unsigned int command, + char *buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen) +{ + memcpy(*rbuf, buf+1, len-1); + return len-1; +} + +static void echo_drv_timeout(ErlDrvData drv_data) +{ + +} + +static ErlDrvSSizeT echo_drv_call(ErlDrvData drv_data, + unsigned int command, + char *buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen, + unsigned int *flags) +{ + memcpy(*rbuf, buf+command, len-command); + return len-command; +} diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index 98dc96bfa7..838b5093f4 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -440,7 +440,7 @@ default_tracer(Config) when is_list(Config) -> {tracer, []} = erlang:trace_info(G1, tracer), G1 ! {apply,{erlang,exit,[normal]}}, io:format("~p = ~p.~n", [M, N]), - M = N, + M = N - 1, % G1 has been started, but Tracer and Port have died ok. tracer_port_crash(Config) when is_list(Config) -> diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl index 3415ff3135..a8da2b4c14 100644 --- a/erts/preloaded/src/erl_tracer.erl +++ b/erts/preloaded/src/erl_tracer.erl @@ -7,7 +7,8 @@ call | return_to | return_from | exception_from | spawn | exit | link | unlink | getting_linked | getting_unlinked | register | unregister | in | out | - gc_start | gc_end. + in_exiting | out_exiting | out_exited | + open | closed | gc_start | gc_end. -type trace_opts() :: #{ match_spec_result => true | term(), scheduler_id => undefined | non_neg_integer(), timestamp => undefined | timestamp | cpu_timestamp | diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 8d6d17b043..40d0abd321 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -230,17 +230,20 @@ send | 'receive' | procs | + ports | call | - silent | + arity | return_to | + silent | running | exiting | + running_procs | + running_ports | garbage_collection | timestamp | cpu_timestamp | monotonic_timestamp | strict_monotonic_timestamp | - arity | set_on_spawn | set_on_first_spawn | set_on_link | @@ -1708,11 +1711,14 @@ time() -> erlang:nif_error(undefined). %% trace/3 --spec erlang:trace(PidSpec, How, FlagList) -> integer() when - PidSpec :: pid() | existing | new | all, +-spec erlang:trace(PidPortSpec, How, FlagList) -> integer() when + PidPortSpec :: pid() | port() + | all | processes | ports + | existing | existing_processes | existing_ports + | new | new_processes | new_ports, How :: boolean(), FlagList :: [trace_flag()]. -trace(PidSpec, How, FlagList) -> +trace(PidPortSpec, How, FlagList) -> %% Make sure that we have loaded the tracer module case lists:keyfind(tracer, 1, FlagList) of {tracer, Module, State} when erlang:is_atom(Module) -> @@ -1726,13 +1732,13 @@ trace(PidSpec, How, FlagList) -> ignore end, - try erts_internal:trace(PidSpec, How, FlagList) of + try erts_internal:trace(PidPortSpec, How, FlagList) of Res -> Res catch E:R -> {_, [_ | CST]} = erlang:process_info( erlang:self(), current_stacktrace), erlang:raise( - E, R, [{?MODULE, trace, [PidSpec, How, FlagList], []} | CST]) + E, R, [{?MODULE, trace, [PidPortSpec, How, FlagList], []} | CST]) end. %% trace_delivered/1 @@ -1743,14 +1749,16 @@ trace_delivered(_Tracee) -> erlang:nif_error(undefined). %% trace_info/2 --spec erlang:trace_info(PidOrFunc, Item) -> Res when - PidOrFunc :: pid() | new | {Module, Function, Arity} | on_load, +-spec erlang:trace_info(PidPortOrFunc, Item) -> Res when + PidPortOrFunc :: pid() | port() | new | new_processes | new_ports + | {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, + Item :: flags | tracer | traced | match_spec + | meta | meta_match_spec | call_count | call_time | all, Res :: trace_info_return(). -trace_info(_PidOrFunc, _Item) -> +trace_info(_PidPortOrFunc, _Item) -> erlang:nif_error(undefined). %% trunc/1 -- cgit v1.2.3 From d15ddcb9d407462133ec5d84c45353bd5a928167 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 9 Feb 2016 22:04:57 +0100 Subject: erts: Fix FPE bug in erl_nif erts_block/unblock_fpe should only be called at entry to/exit from native user code. --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/erl_nif.c | 51 +++++++++++++++++++++++++++---------------- erts/emulator/beam/global.h | 2 +- 3 files changed, 34 insertions(+), 21 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index c356863b60..7cb259cb8a 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3460,7 +3460,7 @@ do { \ typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]); NifF* fp = vbf = (NifF*) I[1]; struct enif_environment_t env; - erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2]); + erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL); live_hf_end = c_p->mbuf; nif_bif_result = (*fp)(&env, bif_nif_arity, reg); if (env.exception_thrown) diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 59a2b20ff2..7ab5421c91 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -120,7 +120,8 @@ static ERTS_INLINE void ensure_heap(ErlNifEnv* env, unsigned may_need) } #endif -void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif) +void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, + Process* tracee) { env->mod_nif = mod_nif; env->proc = p; @@ -130,7 +131,7 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif) env->fpe_was_unmasked = erts_block_fpe(); env->tmp_obj_list = NULL; env->exception_thrown = 0; - env->tracee = NULL; + env->tracee = tracee; } /* Temporary object header, auto-deallocated when NIF returns @@ -232,7 +233,8 @@ struct enif_msg_environment_t static ERTS_INLINE void setup_nif_env(struct enif_msg_environment_t* msg_env, - struct erl_module_nif* mod) + struct erl_module_nif* mod, + Process* tracee) { Eterm* phony_heap = (Eterm*) msg_env; /* dummy non-NULL ptr */ @@ -241,7 +243,6 @@ setup_nif_env(struct enif_msg_environment_t* msg_env, msg_env->env.heap_frag = NULL; msg_env->env.mod_nif = mod; msg_env->env.tmp_obj_list = NULL; - msg_env->env.fpe_was_unmasked = erts_block_fpe(); msg_env->env.proc = &msg_env->phony_proc; msg_env->env.exception_thrown = 0; memset(&msg_env->phony_proc, 0, sizeof(Process)); @@ -255,13 +256,14 @@ setup_nif_env(struct enif_msg_environment_t* msg_env, msg_env->phony_proc.space_verified = 0; msg_env->phony_proc.space_verified_from = NULL; #endif + msg_env->env.tracee = tracee; } ErlNifEnv* enif_alloc_env(void) { struct enif_msg_environment_t* msg_env = erts_alloc_fnf(ERTS_ALC_T_NIF, sizeof(struct enif_msg_environment_t)); - setup_nif_env(msg_env, NULL); + setup_nif_env(msg_env, NULL, NULL); return &msg_env->env; } void enif_free_env(ErlNifEnv* env) @@ -270,6 +272,20 @@ void enif_free_env(ErlNifEnv* env) erts_free(ERTS_ALC_T_NIF, env); } +static ERTS_INLINE void pre_nif_noproc(struct enif_msg_environment_t* msg_env, + struct erl_module_nif* mod, + Process* tracee) +{ + setup_nif_env(msg_env, mod, tracee); + msg_env->env.fpe_was_unmasked = erts_block_fpe(); +} + +static ERTS_INLINE void post_nif_noproc(struct enif_msg_environment_t* msg_env) +{ + erts_unblock_fpe(msg_env->env.fpe_was_unmasked); + enif_clear_env(&msg_env->env); +} + static ERTS_INLINE void clear_offheap(ErlOffHeap* oh) { oh->first = NULL; @@ -294,7 +310,6 @@ void enif_clear_env(ErlNifEnv* env) menv->env.hp = menv->env.hp_end = HEAP_TOP(p); ASSERT(!is_offheap(&MSO(p))); - erts_unblock_fpe(env->fpe_was_unmasked); free_tmp_objs(env); } @@ -465,7 +480,7 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, else { /* This clause is taken when the nif is called in the context of a traced process. We do not know which locks we have - so we have to do a try lock and if that fails we en queue + so we have to do a try lock and if that fails we enqueue the message in a special trace message output queue of the tracee */ ErlTraceMessageQueue *msgq; @@ -1648,9 +1663,9 @@ static void close_lib(struct erl_module_nif* lib) if (lib->entry != NULL && lib->entry->unload != NULL) { struct enif_msg_environment_t msg_env; - setup_nif_env(&msg_env, lib); + pre_nif_noproc(&msg_env, lib, NULL); lib->entry->unload(&msg_env.env, lib->priv_data); - enif_clear_env(&msg_env.env); + post_nif_noproc(&msg_env); } if (!erts_is_static_nif(lib->handle)) erts_sys_ddll_close(lib->handle); @@ -1797,9 +1812,9 @@ static void nif_resource_dtor(Binary* bin) if (type->dtor != NULL) { struct enif_msg_environment_t msg_env; - setup_nif_env(&msg_env, type->owner); + pre_nif_noproc(&msg_env, type->owner, NULL); type->dtor(&msg_env.env, resource->data); - enif_clear_env(&msg_env.env); + post_nif_noproc(&msg_env); } if (erts_refc_dectest(&type->refc, 0) == 0) { ASSERT(type->next == NULL); @@ -2959,7 +2974,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) } old_func = next_func(mod->curr.nif->entry, &old_incr, old_func); } - erts_pre_nif(&env, BIF_P, lib); + erts_pre_nif(&env, BIF_P, lib, NULL); veto = entry->reload(&env, &lib->priv_data, BIF_ARG_2); erts_post_nif(&env); if (veto) { @@ -2980,7 +2995,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library."); goto error; } - erts_pre_nif(&env, BIF_P, lib); + erts_pre_nif(&env, BIF_P, lib, NULL); veto = entry->upgrade(&env, &lib->priv_data, &mod->old.nif->priv_data, BIF_ARG_2); erts_post_nif(&env); if (veto) { @@ -2991,7 +3006,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) commit_opened_resource_types(lib); } else if (entry->load != NULL) { /********* Initial load ***********/ - erts_pre_nif(&env, BIF_P, lib); + erts_pre_nif(&env, BIF_P, lib, NULL); veto = entry->load(&env, &lib->priv_data, BIF_ARG_2); erts_post_nif(&env); if (veto) { @@ -3152,8 +3167,7 @@ Eterm erts_nif_call_function(Process *p, Process *tracee, if (p) { struct enif_environment_t env; ASSERT(is_internal_pid(p->common.id)); - erts_pre_nif(&env, p, mod); - env.tracee = tracee; + erts_pre_nif(&env, p, mod, tracee); nif_result = (*fun->fptr)(&env, argc, argv); if (env.exception_thrown) nif_result = THE_NON_VALUE; @@ -3162,12 +3176,11 @@ Eterm erts_nif_call_function(Process *p, Process *tracee, /* Nif call was done without a process context, so we create a phony one. */ struct enif_msg_environment_t msg_env; - setup_nif_env(&msg_env, mod); - msg_env.env.tracee = tracee; + pre_nif_noproc(&msg_env, mod, tracee); nif_result = (*fun->fptr)(&msg_env.env, argc, argv); if (msg_env.env.exception_thrown) nif_result = THE_NON_VALUE; - enif_clear_env(&msg_env.env); + post_nif_noproc(&msg_env); } return nif_result; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 5c2e8bbd09..a7bc990deb 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -59,7 +59,7 @@ struct enif_environment_t /* ErlNifEnv */ Process *tracee; }; extern void erts_pre_nif(struct enif_environment_t*, Process*, - struct erl_module_nif*); + struct erl_module_nif*, Process* tracee); extern void erts_post_nif(struct enif_environment_t* env); extern Eterm erts_nif_taints(Process* p); extern void erts_print_nif_taints(int to, void* to_arg); -- cgit v1.2.3 From d89da98527e1a0855fef06cd27839416dfb6efd1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 22 Feb 2016 14:27:06 +0100 Subject: erts: Remove fake schedule testcase Tracing to port in non-smp now creates port tasks instead of calling directly to the port to fake schedule events don't exist any more. --- erts/emulator/test/trace_port_SUITE.erl | 165 +------------------------------- 1 file changed, 1 insertion(+), 164 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index 838b5093f4..1068c1d22d 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -28,10 +28,6 @@ receive_trace/1, process_events/1, schedule/1, - fake_schedule/1, - fake_schedule_after_register/1, - fake_schedule_after_getting_linked/1, - fake_schedule_after_getting_unlinked/1, gc/1, default_tracer/1, tracer_port_crash/1]). @@ -44,10 +40,7 @@ suite() -> all() -> [call_trace, return_trace, send, receive_trace, - process_events, schedule, fake_schedule, - fake_schedule_after_register, - fake_schedule_after_getting_linked, - fake_schedule_after_getting_unlinked, gc, + process_events, schedule, gc, default_tracer, tracer_port_crash]. %% Test sending call trace messages to a port. @@ -234,162 +227,6 @@ schedule(Config) when is_list(Config) -> ok. -run_fake_sched_test(Fun, Config) when is_function(Fun), is_list(Config) -> - case catch erlang:system_info(smp_support) of - true -> - {skipped, - "No need for faked schedule out/in trace messages " - "when smp support is enabled"}; - _ -> - Fun(Config) - end. - -%% Tests time compensating fake out/in scheduling. -fake_schedule(Config) when is_list(Config) -> - run_fake_sched_test(fun fake_schedule_test/1, Config). - -fake_schedule_test(Config) when is_list(Config) -> - Tracer = start_tracer(Config), - Port = get(tracer_port), - General = fun_spawn(fun general/0), - %% - trac(General, true, [send, running]), - %% - %% Test that fake out/in scheduling is not generated unless - %% both 'running' and 'timestamp' is active. - [] = erlang:port_control(Port, $h, []), - General ! nop, - expect({trace, General, in, {?MODULE, general, 0}}), - expect({trace, General, out, {?MODULE, general, 0}}), - expect(), - %% - trac(General, false, [running]), - trac(General, true, [timestamp]), - %% - Ref1 = make_ref(), - Msg1 = {Port, {data, term_to_binary(Ref1)}}, - [] = erlang:port_control(Port, $h, []), - General ! {send, Tracer, Msg1}, - expect({trace_ts, General, send, Msg1, Tracer, ts}), - expect(Ref1), - expect(), - %% - trac(General, true, [running]), - %% - %% Test that fake out/in scheduling can be generated by the driver - Ref2 = make_ref(), - Msg2 = {Port, {data, term_to_binary(Ref2)}}, - [] = erlang:port_control(Port, $h, []), - General ! {send, Tracer, Msg2}, - {_,_,_,_,Ts} = - expect({trace_ts, General, in, {?MODULE, general, 0}, ts}), - expect({trace_ts, General, out, 0, Ts}), - expect({trace_ts, General, in, 0, ts}), - expect({trace_ts, General, send, Msg2, Tracer, ts}), - expect(Ref2), - expect({trace_ts, General, out, {?MODULE, general, 0}, ts}), - expect(), - %% - %% Test that fake out/in scheduling is not generated after an - %% 'out' scheduling event - Ref3 = make_ref(), - Msg3 = {Port, {data, term_to_binary(Ref3)}}, - General ! {apply, {erlang, port_control, [Port, $h, []]}}, - expect({trace_ts, General, in, {?MODULE, general, 0}, ts}), - expect({trace_ts, General, out, {?MODULE, general, 0}, ts}), - General ! {send, Tracer, Msg3}, - expect({trace_ts, General, in, {?MODULE, general, 0}, ts}), - expect({trace_ts, General, send, Msg3, Tracer, ts}), - expect(Ref3), - expect({trace_ts, General, out, {?MODULE, general, 0}, ts}), - expect(), - %% - ok. - -%% Tests fake out/in scheduling contents. -fake_schedule_after_register(Config) when is_list(Config) -> - run_fake_sched_test(fun fake_schedule_after_register_test/1, Config). - -fake_schedule_after_register_test(Config) when is_list(Config) -> - start_tracer(Config), - Port = get(tracer_port), - G1 = fun_spawn(fun general/0), - G2 = fun_spawn(fun general/0), - %% - trac(G1, true, [running, timestamp, procs]), - trac(G2, true, [running, timestamp]), - %% - %% Test fake out/in scheduling after certain messages - erlang:yield(), - G2 ! {apply, {erlang, port_control, [Port, $h, []]}}, - G2 ! {apply, {erlang, register, [fake_schedule_after_register, G1]}}, - expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}), - {_,_,_,_,Ts} = - expect({trace_ts, G1, register, fake_schedule_after_register, ts}), - expect({trace_ts, G2, out, 0, Ts}), - expect({trace_ts, G2, in, 0, ts}), - expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}), - expect(), - %% - ok. - -%% Tests fake out/in scheduling contents. -fake_schedule_after_getting_linked(Config) when is_list(Config) -> - run_fake_sched_test(fun fake_schedule_after_getting_linked_test/1, - Config). - -fake_schedule_after_getting_linked_test(Config) when is_list(Config) -> - start_tracer(Config), - Port = get(tracer_port), - G1 = fun_spawn(fun general/0), - G2 = fun_spawn(fun general/0), - %% - trac(G1, true, [running, timestamp, procs]), - trac(G2, true, [running, timestamp]), - %% - %% Test fake out/in scheduling after certain messages - erlang:yield(), - G2 ! {apply, {erlang, port_control, [Port, $h, []]}}, - G2 ! {apply, {erlang, link, [G1]}}, - expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}), - {_,_,_,_,Ts} = - expect({trace_ts, G1, getting_linked, G2, ts}), - expect({trace_ts, G2, out, 0, Ts}), - expect({trace_ts, G2, in, 0, ts}), - expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}), - expect(), - %% - ok. - -%% Tests fake out/in scheduling contents. -fake_schedule_after_getting_unlinked(Config) when is_list(Config) -> - run_fake_sched_test(fun fake_schedule_after_getting_unlinked_test/1, - Config). - -fake_schedule_after_getting_unlinked_test(Config) when is_list(Config) -> - start_tracer(Config), - Port = get(tracer_port), - G1 = fun_spawn(fun general/0), - G2 = fun_spawn(fun general/0), - %% - trac(G1, true, [running, procs]), - trac(G2, true, [running, timestamp]), - %% - %% Test fake out/in scheduling after certain messages - erlang:yield(), - G2 ! {apply, {erlang, link, [G1]}}, - G2 ! {apply, {erlang, port_control, [Port, $h, []]}}, - G2 ! {apply, {erlang, unlink, [G1]}}, - expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}), - expect({trace, G1, getting_linked, G2}), - expect({trace, G1, getting_unlinked, G2}), - expect({trace_ts, G2, out, 0, ts}), - expect({trace_ts, G2, in, 0, ts}), - expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}), - expect(), - %% - ok. - %% Test sending garbage collection events to a port. gc(Config) when is_list(Config) -> start_tracer(Config), -- cgit v1.2.3 From ecd20cf55ffcc45632825863ea8f0c7442553a0d Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 23 Feb 2016 17:08:42 +0100 Subject: erts: Add tracing examples in match spec docs --- erts/doc/src/match_spec.xml | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml index b49e1483aa..3944f24f84 100644 --- a/erts/doc/src/match_spec.xml +++ b/erts/doc/src/match_spec.xml @@ -528,7 +528,7 @@
- Examples + ETS Examples

Match an argument list of three where the first and third arguments are equal:

The function can be useful for testing complicated ets matches.

+
+ Tracing Examples +

Only generate trace message if trace control word is set to 1:

+ +

Only generate trace message if there is a seq trace token:

+ +

Remove 'silent' trace flag when first argument is 'verbose' + and add it when it is 'silent':

+ +

Add return_trace message if function is of arity 3:

+ +

Only generate trace message if function is of arity 3 and first argument is 'trace':

+ +
-- cgit v1.2.3 From 5194bb9f0a8fd058f701703e06f2b9be3dce9a91 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 24 Feb 2016 16:18:51 +0100 Subject: erts: Fix end_per_testcase crash in local_trace_SUITE --- erts/emulator/test/trace_local_SUITE.erl | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 8901beebca..74c05f24e0 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -1224,18 +1224,22 @@ setup(ProcFlags) -> shutdown() -> trace_off(), - {Pid,Mref} = get(slave), - try erlang:is_process_alive(Pid) of - true -> - Pid ! die, - receive - {'DOWN',Mref,process,Pid,Reason} -> - Reason + case get(slave) of + {Pid,Mref} -> + try erlang:is_process_alive(Pid) of + true -> + Pid ! die, + receive + {'DOWN',Mref,process,Pid,Reason} -> + Reason + end; + _ -> + not_alive + catch _:_ -> + undefined end; _ -> - not_alive - catch _:_ -> - undefined + undefined end. trace_off() -> -- cgit v1.2.3 From d698386e5b101a02786c85af70f1513c6beb191b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 3 Mar 2016 16:33:22 +0100 Subject: erts: Silence harmless valgrind warning in dec_term provoked by nif_SUITE:nif_binary_to_term. If we fail to decode an immediate (unsafe atom for example) with a dummy factory then hp and factory->hp will both be uninitialized and valgrind will complain about comparing them. --- erts/emulator/beam/external.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 9f43240b7e..723c25ff77 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3958,9 +3958,13 @@ error: * Must unlink all off-heap objects that may have been * linked into the process. */ - if (factory->hp < hp) { /* Sometimes we used hp and sometimes factory->hp */ - factory->hp = hp; /* the largest must be the freshest */ + if (factory->mode != FACTORY_CLOSED) { + if (factory->hp < hp) { /* Sometimes we used hp and sometimes factory->hp */ + factory->hp = hp; /* the largest must be the freshest */ + } } + else ASSERT(factory->hp == hp); + error_hamt: erts_factory_undo(factory); PSTACK_DESTROY(hamt_array); -- cgit v1.2.3 From 71a67e69ce792f5464479fb15257ad07dc30b856 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 1 Mar 2016 18:19:15 +0100 Subject: erts: Do 'unregister' as "self-tracing" We have the main lock on rp->p, so why not? --- erts/emulator/beam/register.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c index e995dcbd8a..77f79fcea4 100644 --- a/erts/emulator/beam/register.c +++ b/erts/emulator/beam/register.c @@ -560,7 +560,7 @@ int erts_unregister_name(Process *c_p, #endif rp->p->common.u.alive.reg = NULL; if (IS_TRACED_FL(rp->p, F_TRACE_PROCS)) { - trace_proc(c_p, current_c_p_locks, + trace_proc(rp->p, (c_p == rp->p) ? c_p_locks : ERTS_PROC_LOCK_MAIN, rp->p, am_unregister, r.name); } #ifdef ERTS_SMP -- cgit v1.2.3 From b8ca148ba2c98a2bec923dbac6ed52a80aa167ca Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 3 Mar 2016 16:52:07 +0100 Subject: erts: send and receive no longer need status lock Rickards said that this was ok --- erts/emulator/beam/erl_process_lock.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index d81ef63af2..2cccf0697a 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -209,10 +209,8 @@ typedef struct erts_proc_lock_t_ { /* ERTS_PROC_LOCKS_* are combinations of process locks */ -#define ERTS_PROC_LOCKS_MSG_RECEIVE (ERTS_PROC_LOCK_MSGQ \ - | ERTS_PROC_LOCK_STATUS) -#define ERTS_PROC_LOCKS_MSG_SEND (ERTS_PROC_LOCK_MSGQ \ - | ERTS_PROC_LOCK_STATUS) +#define ERTS_PROC_LOCKS_MSG_RECEIVE ERTS_PROC_LOCK_MSGQ +#define ERTS_PROC_LOCKS_MSG_SEND ERTS_PROC_LOCK_MSGQ #define ERTS_PROC_LOCKS_XSIG_SEND ERTS_PROC_LOCK_STATUS #define ERTS_PROC_LOCKS_ALL \ -- cgit v1.2.3 From 3738103d0b1195e570a7525c4370cd490a5368aa Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 29 Feb 2016 15:09:26 +0100 Subject: erts: Add 'spawned' trace event to 'procs' trace flag OTP-13497 This trace event is triggered when a process is created from the process that is created. --- erts/doc/src/erlang.xml | 11 +++++++++++ erts/emulator/beam/atom.names | 1 + erts/emulator/beam/erl_process.c | 13 ++++++++++++- erts/emulator/beam/erl_trace.c | 6 +++--- erts/emulator/beam/erl_trace.h | 2 +- erts/emulator/test/trace_SUITE.erl | 5 ++++- erts/preloaded/src/erl_tracer.erl | 2 +- 7 files changed, 33 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index abc0d56983..d6fe585c8d 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -8464,6 +8464,7 @@ timestamp() ->

Traces process-related events.

Message tags: spawn, + spawned, exit, register, unregister, @@ -8704,6 +8705,16 @@ timestamp() ->

Args is supposed to be the argument list, but can be any term if the spawn is erroneous.

+ + + {trace, Pid, spawned, Pid2, {M, F, Args}} + + +

When Pid is spawned by process Pid2 with + the specified function call as entry point.

+

Args is supposed to be the argument list, + but can be any term if the spawn is erroneous.

+
{trace, Pid, exit, Reason} diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index bd1005867c..8c51f788c0 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -571,6 +571,7 @@ atom size atom sl_alloc atom spawn_executable atom spawn_driver +atom spawned atom ssl_tls atom stack_size atom start diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 1398bc9c61..a520005c35 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11176,12 +11176,23 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); erts_smp_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); - trace_proc_spawn(parent, p->common.id, mod, func, args); + trace_proc_spawn(parent, am_spawn, p->common.id, mod, func, args); if (so->flags & SPO_LINK) trace_proc(parent, locks, parent, am_link, p->common.id); } } + if (IS_TRACED_FL(p, F_TRACE_PROCS)) { + if (locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)) { + locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + erts_smp_proc_unlock(p, locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)); + erts_smp_proc_unlock(parent, locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)); + } + trace_proc_spawn(p, am_spawned, parent->common.id, mod, func, args); + if (so->flags & SPO_LINK) + trace_proc(p, locks, p, am_getting_linked, parent->common.id); + } + /* * Check if this process should be initially linked to its parent. */ diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 5ba58e9350..3027e34968 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1249,20 +1249,20 @@ trace_proc(Process *c_p, ErtsProcLocks c_p_locks, * and 'args' may be a deep term. */ void -trace_proc_spawn(Process *p, Eterm pid, +trace_proc_spawn(Process *p, Eterm what, Eterm pid, Eterm mod, Eterm func, Eterm args) { ErtsTracerNif *tnif = NULL; if (is_tracer_proc_enabled(p, ERTS_PROC_LOCKS_ALL & ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE), - &p->common, &tnif, am_spawn)) { + &p->common, &tnif, what)) { Eterm mfa; Eterm* hp; hp = HAlloc(p, 4); mfa = TUPLE3(hp, mod, func, args); hp += 4; - send_to_tracer_nif(p, &p->common, p->common.id, tnif, am_spawn, pid, mfa); + send_to_tracer_nif(p, &p->common, p->common.id, tnif, what, pid, mfa); } } diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index 0fefd6f70d..14d0c36016 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -100,7 +100,7 @@ void erts_trace_exception(Process* p, BeamInstr mfa[], Eterm class, Eterm value, void erts_trace_return_to(Process *p, BeamInstr *pc); void trace_sched(Process*, ErtsProcLocks, Eterm); void trace_proc(Process*, ErtsProcLocks, Process*, Eterm, Eterm); -void trace_proc_spawn(Process*, Eterm pid, Eterm mod, Eterm func, Eterm args); +void trace_proc_spawn(Process*, Eterm what, Eterm pid, Eterm mod, Eterm func, Eterm args); void save_calls(Process *p, Export *); void trace_gc(Process *p, Eterm what); /* port tracing */ diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index d9974bd6f7..b7f312635e 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -312,12 +312,14 @@ procs_trace(Config) when is_list(Config) -> Proc2 = spawn(?MODULE, process, [Self]), io:format("Proc2 = ~p ~n", [Proc2]), %% - 1 = erlang:trace(Proc1, true, [procs]), + 1 = erlang:trace(Proc1, true, [procs, set_on_first_spawn]), MFA = {?MODULE, process, [Self]}, %% %% spawn, link Proc1 ! {spawn_link_please, Self, MFA}, Proc3 = receive {spawned, Proc1, P3} -> P3 end, + receive {trace, Proc3, spawned, Proc1, MFA} -> ok end, + receive {trace, Proc3, getting_linked, Proc1} -> ok end, {trace, Proc1, spawn, Proc3, MFA} = receive_first_trace(), io:format("Proc3 = ~p ~n", [Proc3]), {trace, Proc1, link, Proc3} = receive_first_trace(), @@ -328,6 +330,7 @@ procs_trace(Config) when is_list(Config) -> Reason3 = make_ref(), Proc1 ! {send_please, Proc3, {exit_please, Reason3}}, receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end, + receive {trace, Proc3, exit, Reason3} -> ok end, {trace, Proc1, getting_unlinked, Proc3} = receive_first_trace(), Proc1 ! {trap_exit_please, false}, receive_nothing(), diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl index a8da2b4c14..2177e48f60 100644 --- a/erts/preloaded/src/erl_tracer.erl +++ b/erts/preloaded/src/erl_tracer.erl @@ -5,7 +5,7 @@ -type tracee() :: port() | pid() | undefined. -type trace_tag() :: send | send_to_non_existing_process | 'receive' | call | return_to | return_from | exception_from | - spawn | exit | link | unlink | getting_linked | + spawn | spawned | exit | link | unlink | getting_linked | getting_unlinked | register | unregister | in | out | in_exiting | out_exiting | out_exited | open | closed | gc_start | gc_end. -- cgit v1.2.3 From a1cde0f881ee4865d38e2e6e88cc71dcd362fdd2 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 17 Mar 2016 11:15:54 +0100 Subject: erts: Deallocate heap fragments from trace nif calls Any heap fragment created during a nif call to a tracer nif should be free'd immediately in order for the GC not to treat it as live data. --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/erl_nif.c | 31 ++++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 7cb259cb8a..254bfe4ba0 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3346,7 +3346,7 @@ do { \ OpCase(normal_exit): { SWAPOUT; c_p->freason = EXC_NORMAL; - c_p->arity = 0; /* In case this process will ever be garbed again. */ + c_p->arity = 0; /* In case this process will never be garbed again. */ ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); erts_do_exit_process(c_p, am_normal); ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 7ab5421c91..73c0eb8eba 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -92,11 +92,12 @@ static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, unsigned need) } static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp) -{ +{ env->hp = hp; - if (env->heap_frag == NULL) { + if (env->heap_frag == NULL) { ASSERT(HEAP_LIMIT(env->proc) == env->hp_end); - HEAP_TOP(env->proc) = env->hp; + ASSERT(env->hp + need > env->hp_end); + HEAP_TOP(env->proc) = env->hp; } else { env->heap_frag->used_size = hp - env->heap_frag->mem; @@ -3165,13 +3166,37 @@ Eterm erts_nif_call_function(Process *p, Process *tracee, || erts_smp_thr_progress_is_blocking()); #endif if (p) { + /* This is almost a normal nif call like in beam_emu, + except that any heap fragment created in the nif will be + discarded without checking if anything in it is live. + This is because we cannot do a GC here as we don't know + the number of live registers that have to be preserved. + This means that any heap part of the returned term may + not be used outside this function. */ struct enif_environment_t env; + ErlHeapFragment *orig_hf = MBUF(p); + ErlOffHeap orig_oh = MSO(p); ASSERT(is_internal_pid(p->common.id)); + MBUF(p) = NULL; + clear_offheap(&MSO(p)); + erts_pre_nif(&env, p, mod, tracee); nif_result = (*fun->fptr)(&env, argc, argv); if (env.exception_thrown) nif_result = THE_NON_VALUE; erts_post_nif(&env); + + /* Free any offheap and heap fragments created in nif */ + if (MSO(p).first) { + erts_cleanup_offheap(&MSO(p)); + clear_offheap(&MSO(p)); + } + if (MBUF(p)) + free_message_buffer(MBUF(p)); + + /* restore original heap fragment list */ + MBUF(p) = orig_hf; + MSO(p) = orig_oh; } else { /* Nif call was done without a process context, so we create a phony one. */ -- cgit v1.2.3 From 8e2ec999394c77741241ef1a12728b11195961c8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 21 Mar 2016 17:53:26 +0100 Subject: erts: Document erlang:match_spec_test/3 OTP-13501 --- erts/doc/src/erlang.xml | 42 ++++++++++++++++++++++++++++++++++++++++++ erts/preloaded/src/erlang.erl | 8 ++++---- 2 files changed, 46 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index d6fe585c8d..fde04504c8 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2612,6 +2612,48 @@ os_prompt% + + + Test that a match specification works + +

+ This function is a utility to test a match_spec used in calls to + ets:select/2 and + erlang:trace_pattern/3. + The function both tests MatchSpec for "syntactic" correctness and + runs the match_spec against the object. If the match_spec contains + errors, the tuple {error, Errors} is returned where Errors is a list + of natural language descriptions of what was wrong with the match_spec. +

+

+ If the Type is table the object to match + against should be a tuple. The function then returns + {ok,Result,[],Warnings} where Result is what would have been the + result in a real ets:select/2 call or false if the match_spec does + not match the object tuple. +

+ +

+ If Type is trace the object to match + against should be a list. The function returns + {ok, Result, Flags, Warnings} where Result is true if a trace + message should be emitted, false if a trace message should not + be emitted or the message term to be appended to the trace message. + Flags is a list containing all the trace flags that will be enabled, + at the moment this is only return_trace. +

+ +

+ This is a useful debugging and test tool, especially when writing complicated + match specifications. +

+

+ See also + ets:test_ms/2. +

+
+
+ Returns the largest of two terms. diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 40d0abd321..3cc17014ff 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1163,10 +1163,10 @@ map_size(_Map) -> erlang:nif_error(undefined). %% match_spec_test/3 --spec erlang:match_spec_test(P1, P2, P3) -> TestResult when - P1 :: [term()] | tuple(), - P2 :: term(), - P3 :: table | trace, +-spec erlang:match_spec_test(MatchAgainst, MatchSpec, Type) -> TestResult when + MatchAgainst :: [term()] | tuple(), + MatchSpec :: term(), + Type :: table | trace, TestResult :: {ok, term(), [return_trace], [ {error | warning, string()} ]} | {error, [ {error | warning, string()} ]}. match_spec_test(_P1, _P2, _P3) -> erlang:nif_error(undefined). -- cgit v1.2.3 From b4e236a15bb5065facc3b1dee6da936cea5b8ac9 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 22 Mar 2016 17:49:52 +0100 Subject: erts: Optimize tracer reload test Without off_heap message queue the GC of the tracer could take very long and thus delay the code:purge which would in turn allow the trace generator to generate a lot of more messages which would make the tracer GC for even longer etc etc. The time taken for the testcase could go up to as large as 10 seconds and use lots of memory. --- erts/emulator/test/tracer_SUITE.erl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/tracer_SUITE.erl b/erts/emulator/test/tracer_SUITE.erl index c62512e6ba..812e834562 100644 --- a/erts/emulator/test/tracer_SUITE.erl +++ b/erts/emulator/test/tracer_SUITE.erl @@ -143,9 +143,10 @@ unload(_Config) -> %% segfaults when reloading the current nifs. reload(_Config) -> - Tracer = spawn_link(fun F() -> receive _M -> F() end end), - Tracee = spawn_link(fun F() -> ?MODULE:all(), F() end), - + Tracer = spawn_opt(fun F() -> receive _M -> F() end end, + [{message_queue_data, off_heap}]), + erlang:link(Tracer), + Tracee = spawn_link(fun reload_loop/0), [begin Ref = make_ref(), @@ -171,6 +172,10 @@ reload(_Config) -> ok. +reload_loop() -> + ?MODULE:all(), + reload_loop(). + invalid_tracers(_Config) -> FailTrace = fun(A) -> try erlang:trace(self(), true, A) of -- cgit v1.2.3 From a1b352dfc68d501a385240cdb7f45a96cf9e3358 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 30 Mar 2016 10:22:28 +0200 Subject: erts: Add comment about future trace optimizations --- erts/emulator/beam/erl_trace.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 3027e34968..cd3c14e213 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -20,6 +20,19 @@ /* * Support functions for tracing. + * + * Ideas for future speed improvements in tracing framework: + * * Move ErtsTracerNif into ErtsTracer + * + Removes need for locking + * + Removes hash lookup overhead + * + Use a refc on the ErtsTracerNif to know when it can + * be freed. We don't want to allocate a separate + * ErtsTracerNif for each module used. + * * Optimize GenericBp for cache locality by reusing equivalent + * GenericBp and GenericBpData in multiple tracer points. + * + Possibly we want to use specialized instructions for different + * types of trace so that the knowledge of which struct is used + * can be in the instruction. */ #ifdef HAVE_CONFIG_H -- cgit v1.2.3 From c3e7acb4fe304d117f7361292d36f5d73df3e1c7 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 5 Apr 2016 11:26:52 +0200 Subject: erts: Make trace_delivered go via sys msg dispatcher again This is needed as otherwise messages from system_profile will not be guaranteed to arrive before trace delivered. --- erts/doc/src/erlang.xml | 13 ++++++++++--- erts/emulator/beam/erl_bif_trace.c | 23 +++++++++++++++-------- erts/emulator/beam/erl_trace.c | 16 ++++++++++++++++ erts/emulator/beam/erl_trace.h | 1 + 4 files changed, 42 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index fde04504c8..423ccdf98f 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -8958,7 +8958,11 @@ timestamp() -> Notification when trace has been delivered. -

The delivery of trace messages is dislocated on the time-line +

The delivery of trace messages (generated by + erlang:trace/3, + seq_trace or + erlang:system_profile/2) + is dislocated on the time-line compared to other events in the system. If you know that Tracee has passed some specific point in its execution, @@ -8984,8 +8988,11 @@ timestamp() -> or previously existing on the same node as the caller of erlang:trace_delivered(Tracee) resides on. The special Tracee atom all - denotes all processes - that currently are traced in the node.

+ denotes all processes that currently are traced in the node.

+

When used together with an + Tracer Module any message sent in the trace callback + is guaranteed to have reached it's recipient before the + trace_delivered message is sent.

Example: Process A is Tracee, port B is tracer, and process C is the port owner of B. C wants to close B when diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 7e2fe7fcd4..ff2018aa27 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -2253,25 +2253,32 @@ static void reply_trace_delivered_all(void *vtdarp) { ErtsTraceDeliveredAll *tdarp = (ErtsTraceDeliveredAll *) vtdarp; - ErtsProcLocks rp_locks = 0; if (erts_smp_atomic32_dec_read_nob(&tdarp->refc) == 0) { + Eterm ref_copy, msg; Process *rp = tdarp->proc; Eterm *hp = NULL; - ErlOffHeap *ohp = NULL; - ErtsMessage *mp = NULL; - Eterm ref_copy, msg; - + ErlOffHeap *ohp; +#ifdef ERTS_SMP + ErlHeapFragment *bp; + bp = new_message_buffer(4 + NC_HEAP_SIZE(tdarp->ref)); + hp = &bp->mem[0]; + ohp = &bp->off_heap; +#else + ErtsProcLocks rp_locks = 0; + ErtsMessage *mp; mp = erts_alloc_message_heap( rp, &rp_locks, 4 + NC_HEAP_SIZE(tdarp->ref), &hp, &ohp); +#endif ref_copy = STORE_NC(&hp, ohp, tdarp->ref); msg = TUPLE3(hp, am_trace_delivered, tdarp->target, ref_copy); +#ifdef ERTS_SMP + erts_send_sys_msg_proc(rp->common.id, rp->common.id, msg, bp); +#else erts_queue_message(rp, &rp_locks, mp, msg); - - if (rp_locks) - erts_smp_proc_unlock(rp, rp_locks); +#endif erts_free(ERTS_ALC_T_MISC_AUX_WORK, vtdarp); erts_proc_dec_refc(rp); diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index cd3c14e213..bd88769dfc 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -84,6 +84,7 @@ enum ErtsSysMsgType { SYS_MSG_TYPE_UNDEFINED, SYS_MSG_TYPE_SYSMON, SYS_MSG_TYPE_ERRLGR, + SYS_MSG_TYPE_PROC_MSG, SYS_MSG_TYPE_SYSPROF }; @@ -2157,6 +2158,13 @@ erts_queue_error_logger_message(Eterm from, Eterm msg, ErlHeapFragment *bp) enqueue_sys_msg(SYS_MSG_TYPE_ERRLGR, from, am_error_logger, msg, bp); } +void +erts_send_sys_msg_proc(Eterm from, Eterm to, Eterm msg, ErlHeapFragment *bp) +{ + ASSERT(is_internal_pid(to)); + enqueue_sys_msg(SYS_MSG_TYPE_PROC_MSG, from, to, msg, bp); +} + #ifdef DEBUG_PRINTOUTS static void print_msg_type(ErtsSysMsgQ *smqp) @@ -2171,6 +2179,9 @@ print_msg_type(ErtsSysMsgQ *smqp) case SYS_MSG_TYPE_ERRLGR: erts_fprintf(stderr, "ERRLGR "); break; + case SYS_MSG_TYPE_PROC_MSG: + erts_fprintf(stderr, "PROC_MSG "); + break; default: erts_fprintf(stderr, "??? "); break; @@ -2241,6 +2252,8 @@ sys_msg_disp_failure(ErtsSysMsgQ *smqp, Eterm receiver) no_elgger, tag, CAR(list_val(tp[3]))); break; } + case SYS_MSG_TYPE_PROC_MSG: + break; default: ASSERT(0); } @@ -2358,6 +2371,9 @@ sys_msg_dispatcher_func(void *unused) print_msg_type(smqp); #endif switch (smqp->type) { + case SYS_MSG_TYPE_PROC_MSG: + receiver = smqp->to; + break; case SYS_MSG_TYPE_SYSMON: receiver = erts_get_system_monitor(); if (smqp->from == receiver) { diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index 14d0c36016..177fd373a6 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -87,6 +87,7 @@ void erts_foreach_sys_msg_in_q(void (*func)(Eterm, Eterm, ErlHeapFragment *)); void erts_queue_error_logger_message(Eterm, Eterm, ErlHeapFragment *); +void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *); #endif void trace_send(Process*, Eterm, Eterm); -- cgit v1.2.3 From a8d483f660578473236e987f040447f2bfdbdcc9 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 5 Apr 2016 11:48:45 +0200 Subject: erts: more logging in trace_bif_SUITE trace_bif_return --- erts/emulator/test/trace_bif_SUITE.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index a2edf0662d..8c3ffccc45 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -247,7 +247,8 @@ receive_trace_msg(Mess) -> receive_trace_msg_ts({trace_ts, Pid, call, {erlang,F,A}}, PrevTs, TsType) -> receive - {trace_ts, Pid, call, {erlang, F, A}, Ts} -> + {trace_ts, Pid, call, {erlang, F, A}, Ts} = M -> + io:format("~p (PrevTs: ~p)~n",[M, PrevTs]), check_ts(TsType, PrevTs, Ts), Ts; Other -> @@ -260,7 +261,8 @@ receive_trace_msg_ts({trace_ts, Pid, call, {erlang,F,A}}, PrevTs, TsType) -> receive_trace_msg_ts_return_from({trace_ts, Pid, return_from, {erlang,F,A}}, PrevTs, TsType) -> receive - {trace_ts, Pid, return_from, {erlang, F, A}, _Value, Ts} -> + {trace_ts, Pid, return_from, {erlang, F, A}, _Value, Ts} = M -> + io:format("~p (PrevTs: ~p)~n",[M, PrevTs]), check_ts(TsType, PrevTs, Ts), Ts; Other -> @@ -272,7 +274,8 @@ receive_trace_msg_ts_return_from({trace_ts, Pid, return_from, {erlang,F,A}}, Pre receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}, PrevTs, TsType) -> receive - {trace_ts, Pid, return_to, {M, F, A}, Ts} -> + {trace_ts, Pid, return_to, {M, F, A}, Ts} = Msg -> + io:format("~p (PrevTs: ~p)~n",[Msg, PrevTs]), check_ts(TsType, PrevTs, Ts), Ts; Other -> -- cgit v1.2.3 From cff38617986001e0a5f3f48de20acbeccceea978 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 7 Apr 2016 16:33:26 +0200 Subject: erts: Don't trace on link events when port is dead --- erts/emulator/beam/io.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 18cfb1e641..b14ca77a04 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -3004,11 +3004,7 @@ port_sig_link(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigd if (op == ERTS_PROC2PORT_SIG_EXEC) port_link(prt, state, sigdp->u.link.to); else { - if (IS_TRACED_FL(prt, F_TRACE_PORTS)) - trace_port(prt, am_getting_linked, sigdp->u.link.to); port_link_failure(sigdp->u.link.port, sigdp->u.link.to); - if (IS_TRACED_FL(prt, F_TRACE_PORTS)) - trace_port(prt, am_unlink, sigdp->u.link.to); } if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) port_sched_op_reply(sigdp->caller, sigdp->ref, am_true); -- cgit v1.2.3 From 6f137f7f326a03098d72806495a1365938f14314 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 15 Apr 2016 15:12:24 +0200 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 55792 -> 55812 bytes erts/preloaded/ebin/erl_tracer.beam | Bin 1864 -> 1924 bytes erts/preloaded/ebin/erlang.beam | Bin 102316 -> 104196 bytes erts/preloaded/ebin/erts_code_purger.beam | Bin 8704 -> 8724 bytes erts/preloaded/ebin/erts_internal.beam | Bin 9976 -> 10560 bytes erts/preloaded/ebin/init.beam | Bin 46200 -> 46288 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1448 -> 1468 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1320 -> 1340 bytes erts/preloaded/ebin/prim_file.beam | Bin 44800 -> 44828 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72608 -> 72632 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23156 -> 23184 bytes erts/preloaded/ebin/zlib.beam | Bin 14156 -> 14176 bytes 12 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index f224178c4f..77685013ae 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam index dcb74c3bf7..ffe5d5631c 100644 Binary files a/erts/preloaded/ebin/erl_tracer.beam and b/erts/preloaded/ebin/erl_tracer.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 7ceb6daaa3..15e4ca82a7 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index 4188e5fd9b..3e95b1e42e 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 88da34b192..53dd2897de 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 7d73ca2234..b13b33170d 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 3cd2515ba8..51a412b7f7 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 9a208d1545..45bed5cf7e 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 426e764127..c25bffb6a5 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index d68d18ecba..85d74de065 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 01b3b1feb8..1f8fe6f03b 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 7252d866bb..059585ec99 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 6cbd63b281a9690b2db5fead3f756d34140f8fb5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 12 Apr 2016 20:51:49 +0200 Subject: erts: Tweak defaults for literal allocator Reduce main carrier size and number of free descriptors. --- erts/emulator/beam/erl_alloc.c | 4 ++-- erts/emulator/sys/common/erl_mmap.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 9cbe00d719..6b5f5bd63e 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -307,9 +307,9 @@ set_default_literal_alloc_opts(struct au_init *ip) ip->init.util.name_prefix = "literal_"; ip->init.util.alloc_no = ERTS_ALC_A_LITERAL; #ifndef SMALL_MEMORY - ip->init.util.mmbcs = 2*1024*1024; /* Main carrier size */ + ip->init.util.mmbcs = 1024*1024; /* Main carrier size */ #else - ip->init.util.mmbcs = 1*1024*1024; /* Main carrier size */ + ip->init.util.mmbcs = 256*1024; /* Main carrier size */ #endif ip->init.util.ts = ERTS_ALC_MTA_LITERAL; ip->init.util.asbcst = 0; diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 67e131b53e..7c5ddbec0b 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -53,7 +53,7 @@ typedef struct { #define ERTS_LITERAL_VIRTUAL_AREA_SIZE (UWORD_CONSTANT(1)*1024*1024*1024) #define ERTS_MMAP_INIT_LITERAL_INITER \ - {{NULL, NULL}, {NULL, NULL}, ERTS_LITERAL_VIRTUAL_AREA_SIZE, 1, (1 << 16), 0} + {{NULL, NULL}, {NULL, NULL}, ERTS_LITERAL_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0} typedef struct ErtsMemMapper_ ErtsMemMapper; -- cgit v1.2.3 From 499a872d5f6ea09d23eb7b04ea5de2f6d3fabd98 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 11 Apr 2016 18:13:31 +0200 Subject: erts: Refactor callbacks for literal mseg alloc Make the callbacks more general to be usable for any allocator that that uses its own ErtsMemMapper. --- erts/emulator/beam/erl_alloc.c | 7 ++++--- erts/emulator/beam/erl_alloc_util.c | 14 ++++++++------ erts/emulator/beam/erl_alloc_util.h | 8 +++++--- erts/emulator/sys/common/erl_mmap.c | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 6b5f5bd63e..efe31193b9 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -329,9 +329,10 @@ set_default_literal_alloc_opts(struct au_init *ip) ip->init.util.sys_dealloc = &erts_alcu_literal_32_sys_dealloc; #elif defined(ARCH_64) # ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION - ip->init.util.mseg_alloc = &erts_alcu_literal_64_mseg_alloc; - ip->init.util.mseg_realloc = &erts_alcu_literal_64_mseg_realloc; - ip->init.util.mseg_dealloc = &erts_alcu_literal_64_mseg_dealloc; + ip->init.util.mseg_alloc = &erts_alcu_mmapper_mseg_alloc; + ip->init.util.mseg_realloc = &erts_alcu_mmapper_mseg_realloc; + ip->init.util.mseg_dealloc = &erts_alcu_mmapper_mseg_dealloc; + ip->init.util.mseg_mmapper = &erts_literal_mmapper; # endif #else # error Unknown architecture diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 6e682019ba..e362a03646 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -898,8 +898,9 @@ erts_alcu_literal_32_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, #elif defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) +/* Used by literal allocator that has its own mmapper (super carrier) */ void* -erts_alcu_literal_64_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) +erts_alcu_mmapper_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) { void* res; UWord size = (UWord) *size_p; @@ -907,33 +908,33 @@ erts_alcu_literal_64_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) if (flags & ERTS_MSEG_FLG_2POW) mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; - res = erts_mmap(&erts_literal_mmapper, mmap_flags, &size); + res = erts_mmap(allctr->mseg_mmapper, mmap_flags, &size); *size_p = (Uint)size; INC_CC(allctr->calls.mseg_alloc); return res; } void* -erts_alcu_literal_64_mseg_realloc(Allctr_t *allctr, void *seg, +erts_alcu_mmapper_mseg_realloc(Allctr_t *allctr, void *seg, Uint old_size, Uint *new_size_p) { void *res; UWord new_size = (UWord) *new_size_p; - res = erts_mremap(&erts_literal_mmapper, ERTS_MSEG_FLG_NONE, seg, old_size, &new_size); + res = erts_mremap(allctr->mseg_mmapper, ERTS_MSEG_FLG_NONE, seg, old_size, &new_size); *new_size_p = (Uint) new_size; INC_CC(allctr->calls.mseg_realloc); return res; } void -erts_alcu_literal_64_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, +erts_alcu_mmapper_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags) { Uint32 mmap_flags = ERTS_MMAPFLG_SUPERCARRIER_ONLY; if (flags & ERTS_MSEG_FLG_2POW) mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED; - erts_munmap(&erts_literal_mmapper, mmap_flags, seg, (UWord)size); + erts_munmap(allctr->mseg_mmapper, mmap_flags, seg, (UWord)size); INC_CC(allctr->calls.mseg_dealloc); } #endif /* ARCH_64 && ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION */ @@ -6127,6 +6128,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) allctr->mseg_alloc = init->mseg_alloc; allctr->mseg_realloc = init->mseg_realloc; allctr->mseg_dealloc = init->mseg_dealloc; + allctr->mseg_mmapper = init->mseg_mmapper; } else { ASSERT(!init->mseg_realloc && !init->mseg_dealloc); diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index afdff1a71e..51af59e40c 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -72,6 +72,7 @@ typedef struct { void* (*mseg_alloc)(Allctr_t*, Uint *size_p, Uint flags); void* (*mseg_realloc)(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p); void (*mseg_dealloc)(Allctr_t*, void *seg, Uint size, Uint flags); + ErtsMemMapper *mseg_mmapper; #endif void* (*sys_alloc)(Allctr_t *allctr, Uint size, int superalign); void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign); @@ -205,9 +206,9 @@ void* erts_alcu_literal_32_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uin void erts_alcu_literal_32_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags); # elif defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) -void* erts_alcu_literal_64_mseg_alloc(Allctr_t*, Uint *size_p, Uint flags); -void* erts_alcu_literal_64_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p); -void erts_alcu_literal_64_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags); +void* erts_alcu_mmapper_mseg_alloc(Allctr_t*, Uint *size_p, Uint flags); +void* erts_alcu_mmapper_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p); +void erts_alcu_mmapper_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags); # endif #endif /* HAVE_ERTS_MSEG */ @@ -601,6 +602,7 @@ struct Allctr_t_ { void* (*mseg_alloc)(Allctr_t*, Uint *size_p, Uint flags); void* (*mseg_realloc)(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p); void (*mseg_dealloc)(Allctr_t*, void *seg, Uint size, Uint flags); + ErtsMemMapper *mseg_mmapper; #endif void* (*sys_alloc)(Allctr_t *allctr, Uint size, int superalign); void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign); diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 889d6f3868..756d038da4 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -299,7 +299,7 @@ struct ErtsMemMapper_ { int supercarrier; int no_os_mmap; /* - * Super unaligend area is located above super aligned + * Super unaligned area is located above super aligned * area. That is, `sa.bot` is beginning of the super * carrier, `sua.top` is the end of the super carrier, * and sa.top and sua.bot moves towards eachother. -- cgit v1.2.3 From 90641d82a4b07e6b0be142d07ac85107b8ebee9d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 12 Apr 2016 14:52:21 +0200 Subject: erts: Make ErtsMemMapper able to map memory with PROT_EXEC to prepare for hipe native code allocation. --- erts/emulator/sys/common/erl_mmap.c | 68 ++++++++++++++++++++++--------------- erts/emulator/sys/common/erl_mmap.h | 2 +- erts/emulator/sys/common/erl_mseg.c | 4 +-- 3 files changed, 44 insertions(+), 30 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 756d038da4..f5f40b35a8 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -294,10 +294,11 @@ typedef struct { }ErtsFreeSegMap; struct ErtsMemMapper_ { - int (*reserve_physical)(char *, UWord); + int (*reserve_physical)(char *, UWord, int exec); void (*unreserve_physical)(char *, UWord); int supercarrier; int no_os_mmap; + int executable; /* is client a native code allocator? */ /* * Super unaligned area is located above super aligned * area. That is, `sa.bot` is beginning of the super @@ -1232,6 +1233,7 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map) #if HAVE_MMAP # define ERTS_MMAP_PROT (PROT_READ|PROT_WRITE) +# define ERTS_MMAP_PROT_EXEC (PROT_READ|PROT_WRITE|PROT_EXEC) # if defined(MAP_ANONYMOUS) # define ERTS_MMAP_FLAGS (MAP_ANON|MAP_PRIVATE) # define ERTS_MMAP_FD (-1) @@ -1245,24 +1247,26 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map) #endif static ERTS_INLINE void * -os_mmap(void *hint_ptr, UWord size, int try_superalign) +os_mmap(void *hint_ptr, UWord size, int try_superalign, int executable) { #if HAVE_MMAP + const int prot = executable ? ERTS_MMAP_PROT_EXEC : ERTS_MMAP_PROT; void *res; #ifdef MAP_ALIGN if (try_superalign) - res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, ERTS_MMAP_PROT, + res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, prot, ERTS_MMAP_FLAGS|MAP_ALIGN, ERTS_MMAP_FD, 0); else #endif - res = mmap((void *) hint_ptr, size, ERTS_MMAP_PROT, + res = mmap((void *) hint_ptr, size, prot, ERTS_MMAP_FLAGS, ERTS_MMAP_FD, 0); if (res == MAP_FAILED) return NULL; return res; #elif HAVE_VIRTUALALLOC + const DWORD prot = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; return (void *) VirtualAlloc(NULL, (SIZE_T) size, - MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); + MEM_COMMIT|MEM_RESERVE, prot); #else # error "missing mmap() or similar" #endif @@ -1319,6 +1323,7 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign) #if HAVE_MMAP #define ERTS_MMAP_RESERVE_PROT (ERTS_MMAP_PROT) +#define ERTS_MMAP_RESERVE_PROT_EXEC (ERTS_MMAP_PROT_EXEC) #define ERTS_MMAP_RESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED) #define ERTS_MMAP_UNRESERVE_PROT (PROT_NONE) #define ERTS_MMAP_UNRESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_NORESERVE|MAP_FIXED) @@ -1326,9 +1331,10 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign) #define ERTS_MMAP_VIRTUAL_FLAGS (ERTS_MMAP_FLAGS|MAP_NORESERVE) static int -os_reserve_physical(char *ptr, UWord size) +os_reserve_physical(char *ptr, UWord size, int exec) { - void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_RESERVE_PROT, + const int prot = exec ? ERTS_MMAP_RESERVE_PROT_EXEC : ERTS_MMAP_RESERVE_PROT; + void *res = mmap((void *) ptr, (size_t) size, prot, ERTS_MMAP_RESERVE_FLAGS, ERTS_MMAP_FD, 0); if (res == (void *) MAP_FAILED) return 0; @@ -1361,7 +1367,7 @@ os_mmap_virtual(char *ptr, UWord size) #endif /* ERTS_HAVE_OS_MMAP */ -static int reserve_noop(char *ptr, UWord size) +static int reserve_noop(char *ptr, UWord size, int exec) { #ifdef ERTS_MMAP_DEBUG_FILL_AREAS Uint32 *uip, *end = (Uint32 *) (ptr + size); @@ -1404,7 +1410,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm, #if ERTS_HAVE_OS_MMAP if (!mm->no_os_mmap) { - ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0); + ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0, 0); if (ptr) { mm->desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE; ERTS_MMAP_SIZE_OS_INC(ERTS_PAGEALIGNED_SIZE); @@ -1423,7 +1429,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm, da_map = &mm->sua.map; desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE); if (desc) { - if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE)) + if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0)) ERTS_MMAP_SIZE_SC_SUA_INC(ERTS_PAGEALIGNED_SIZE); else desc = NULL; @@ -1433,7 +1439,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm, da_map = &mm->sa.map; desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE); if (desc) { - if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE)) + if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0)) ERTS_MMAP_SIZE_SC_SA_INC(ERTS_PAGEALIGNED_SIZE); else desc = NULL; @@ -1494,7 +1500,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) if (desc) { seg = desc->start; end = seg+asize; - if (!mm->reserve_physical(seg, asize)) + if (!mm->reserve_physical(seg, asize, mm->executable)) goto supercarrier_reserve_failure; if (desc->end == end) { delete_free_seg(&mm->sua.map, desc); @@ -1509,8 +1515,8 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) } if (asize <= mm->sua.bot - mm->sa.top) { - if (!mm->reserve_physical(mm->sua.bot - asize, - asize)) + if (!mm->reserve_physical(mm->sua.bot - asize, asize, + mm->executable)) goto supercarrier_reserve_failure; mm->sua.bot -= asize; seg = mm->sua.bot; @@ -1526,7 +1532,8 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) char *start = seg = desc->start; seg = (char *) ERTS_SUPERALIGNED_CEILING(seg); end = seg+asize; - if (!mm->reserve_physical(start, (UWord) (end - start))) + if (!mm->reserve_physical(start, (UWord) (end - start), + mm->executable)) goto supercarrier_reserve_failure; ERTS_MMAP_SIZE_SC_SA_INC(asize); if (desc->end == end) { @@ -1558,7 +1565,8 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) if (asize + (seg - start) <= mm->sua.bot - start) { end = seg + asize; - if (!mm->reserve_physical(start, (UWord) (end - start))) + if (!mm->reserve_physical(start, (UWord) (end - start), + mm->executable)) goto supercarrier_reserve_failure; mm->sa.top = end; ERTS_MMAP_SIZE_SC_SA_INC(asize); @@ -1580,7 +1588,8 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) seg = (char *) ERTS_SUPERALIGNED_CEILING(org_start); end = seg + asize; - if (!mm->reserve_physical(seg, (UWord) (org_end - seg))) + if (!mm->reserve_physical(seg, (UWord) (org_end - seg), + mm->executable)) goto supercarrier_reserve_failure; ERTS_MMAP_SIZE_SC_SUA_INC(asize); if (org_start != seg) { @@ -1613,13 +1622,13 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) /* Map using OS primitives */ if (!(ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) && !mm->no_os_mmap) { if (!(ERTS_MMAPFLG_SUPERALIGNED & flags)) { - seg = os_mmap(NULL, asize, 0); + seg = os_mmap(NULL, asize, 0, mm->executable); if (!seg) goto failure; } else { asize = ERTS_SUPERALIGNED_CEILING(*sizep); - seg = os_mmap(NULL, asize, 1); + seg = os_mmap(NULL, asize, 1, mm->executable); if (!seg) goto failure; @@ -1629,7 +1638,8 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) os_munmap(seg, asize); - ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1); + ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1, + mm->executable); if (!ptr) goto failure; @@ -1964,7 +1974,8 @@ erts_mremap(ErtsMemMapper* mm, if (next && new_end <= next->end) { if (!mm->reserve_physical(((char *) ptr) + old_size, - asize - old_size)) + asize - old_size, + mm->executable)) goto supercarrier_reserve_failure; if (new_end < next->end) resize_free_seg(&mm->sua.map, next, new_end, next->end); @@ -1982,7 +1993,8 @@ erts_mremap(ErtsMemMapper* mm, if (end == mm->sa.top) { if (new_end <= mm->sua.bot) { if (!mm->reserve_physical(((char *) ptr) + old_size, - asize - old_size)) + asize - old_size, + mm->executable)) goto supercarrier_reserve_failure; mm->sa.top = new_end; new_ptr = ptr; @@ -1994,7 +2006,8 @@ erts_mremap(ErtsMemMapper* mm, adjacent_free_seg(&mm->sa.map, start, end, &prev, &next); if (next && new_end <= next->end) { if (!mm->reserve_physical(((char *) ptr) + old_size, - asize - old_size)) + asize - old_size, + mm->executable)) goto supercarrier_reserve_failure; if (new_end < next->end) resize_free_seg(&mm->sa.map, next, new_end, next->end); @@ -2112,7 +2125,7 @@ static void hard_dbg_mseg_init(void); #endif void -erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init) +erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable) { static int is_first_call = 1; int virtual_map = 0; @@ -2144,6 +2157,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init) mm->supercarrier = 0; mm->reserve_physical = reserve_noop; mm->unreserve_physical = unreserve_noop; + mm->executable = executable; #if HAVE_MMAP && !defined(MAP_ANON) mm->mmap_fd = open("/dev/zero", O_RDWR); @@ -2200,7 +2214,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init) * The whole supercarrier will by physically * reserved all the time. */ - start = os_mmap(NULL, sz, 1); + start = os_mmap(NULL, sz, 1, executable); } if (!start) erts_exit(1, @@ -2274,7 +2288,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init) mm->sua.top -= ERTS_PAGEALIGNED_SIZE; mm->size.supercarrier.used.total += ERTS_PAGEALIGNED_SIZE; #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION - if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE)) + if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE, 0)) #endif add_free_desc_area(mm, mm->sua.top, end); mm->desc.reserved += (end - mm->sua.top) / sizeof(ErtsFreeSegDesc); @@ -2287,7 +2301,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init) * will be used for free segment descritors. */ #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION - if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start)) + if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start, 0)) erts_exit(1, "erts_mmap: Failed to reserve physical memory for descriptors\n"); #endif mm->desc.unused_start = start; diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 7c5ddbec0b..30548bd0b0 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -61,7 +61,7 @@ void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep); void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size); void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep); int erts_mmap_in_supercarrier(ErtsMemMapper*, void *ptr); -void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*); +void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*, int executable); struct erts_mmap_info_struct { UWord sizes[6]; diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 286130bece..f0fbb682b0 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -1402,9 +1402,9 @@ erts_mseg_init(ErtsMsegInit_t *init) erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms"); - erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap); + erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap, 0); #if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) - erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap); + erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap, 0); #endif if (!IS_2POW(GET_PAGE_SIZE)) -- cgit v1.2.3 From 85a6623152988c267cea008d20616b61ea9c223c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 12 Apr 2016 20:41:58 +0200 Subject: erts: Add 'exec_alloc' for hipe code that uses its own super carrier (erts_exec_mmapper) to guarantee low addressed and executable memory (PROT_EXEC). Currently only used on x86_64 that needs low memory for HiPE/AMD64's small code model. By initializing erts_exec_mapper early we secure its low memory area before erts_literal_mmapper might steal it. --- erts/configure.in | 7 +++ erts/doc/src/erts_alloc.xml | 18 +++++- erts/emulator/beam/erl_alloc.c | 77 +++++++++++++++++++++++- erts/emulator/beam/erl_alloc.types | 12 ++++ erts/emulator/beam/erl_bif_info.c | 1 + erts/emulator/hipe/hipe_amd64.c | 116 ++++-------------------------------- erts/emulator/sys/common/erl_mmap.c | 45 ++++++++++++-- erts/emulator/sys/common/erl_mmap.h | 16 ++++- erts/emulator/sys/common/erl_mseg.c | 7 +++ erts/emulator/sys/common/erl_mseg.h | 4 +- erts/etc/common/erlexec.c | 2 + 11 files changed, 188 insertions(+), 117 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 0257079c3b..462a13269e 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3351,6 +3351,13 @@ if test X${enable_hipe} = Xyes; then AC_DEFINE(HIPE,[1],[Define to enable HiPE]) HIPE_HELPERS="xmerl syntax_tools edoc" ENABLE_ALLOC_TYPE_VARS="$ENABLE_ALLOC_TYPE_VARS hipe" + case "$ARCH" in + amd64) + # For now exec_alloc is only used for hipe on amd64 + AC_MSG_NOTICE([Enable exec_alloc for hipe code allocation]) + ENABLE_ALLOC_TYPE_VARS="$ENABLE_ALLOC_TYPE_VARS exec_alloc" + ;; + esac fi fi AC_SUBST(HIPE_HELPERS) diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 0965f9b49c..c54082f8c8 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -63,6 +63,9 @@ fix_alloc A fast allocator used for some frequently used fixed size data types. + exec_alloc + Allocator used by hipe for native executable code + on specific architectures (x86_64). std_alloc Allocator used for most memory blocks not allocated via any of the other allocators described above. @@ -80,7 +83,8 @@ the number of system calls made.

sys_alloc and literal_alloc are always enabled and - cannot be disabled. mseg_alloc is always enabled if it is + cannot be disabled. exec_alloc is only available if it is needed + and cannot be disabled. mseg_alloc is always enabled if it is available and an allocator that uses it is enabled. All other allocators can be enabled or disabled. By default all allocators are enabled. @@ -248,16 +252,17 @@ the currently present allocators:

B: binary_alloc - I: literal_alloc D: std_alloc E: ets_alloc F: fix_alloc H: eheap_alloc + I: literal_alloc L: ll_alloc M: mseg_alloc R: driver_alloc S: sl_alloc T: temp_alloc + X: exec_alloc Y: sys_alloc

The following flags are available for configuration of @@ -576,6 +581,15 @@ and is usually sufficient. The flag is ignored on 32-bit architectures. +

The following flag is special for exec_alloc:

+ + ]]> + + exec_alloc super carrier size (in MB). The amount of + virtual address space reserved for native executable code + used by hipe on specific architectures (x86_64). The default is 512 MB. + +

Instrumentation flags:

+Mim true|false diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index efe31193b9..00911ff027 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -30,6 +30,7 @@ #endif #define ERTS_ALLOC_C__ #define ERTS_ALC_INTERNAL__ +#define ERTS_WANT_MEM_MAPPERS #include "sys.h" #define ERL_THREADS_EMU_INTERNAL__ #include "erl_threads.h" @@ -131,6 +132,9 @@ static ErtsAllocatorState_t ets_alloc_state; static ErtsAllocatorState_t driver_alloc_state; static ErtsAllocatorState_t fix_alloc_state; static ErtsAllocatorState_t literal_alloc_state; +#ifdef ERTS_ALC_A_EXEC +static ErtsAllocatorState_t exec_alloc_state; +#endif static ErtsAllocatorState_t test_alloc_state; typedef struct { @@ -216,6 +220,7 @@ typedef struct { struct au_init driver_alloc; struct au_init fix_alloc; struct au_init literal_alloc; + struct au_init exec_alloc; struct au_init test_alloc; } erts_alc_hndl_args_init_t; @@ -339,6 +344,38 @@ set_default_literal_alloc_opts(struct au_init *ip) #endif } +#ifdef ERTS_ALC_A_EXEC +static void +set_default_exec_alloc_opts(struct au_init *ip) +{ + SET_DEFAULT_ALLOC_OPTS(ip); + ip->enable = 1; + ip->thr_spec = 0; + ip->disable_allowed = 0; + ip->thr_spec_allowed = 0; + ip->carrier_migration_allowed = 0; + ip->atype = BESTFIT; + ip->init.bf.ao = 1; + ip->init.util.ramv = 0; + ip->init.util.mmsbc = 0; + ip->init.util.sbct = ~((UWord) 0); + ip->init.util.name_prefix = "exec_"; + ip->init.util.alloc_no = ERTS_ALC_A_EXEC; + ip->init.util.mmbcs = 0; /* No main carrier */ + ip->init.util.ts = ERTS_ALC_MTA_EXEC; + ip->init.util.asbcst = 0; + ip->init.util.rsbcst = 0; + ip->init.util.rsbcmt = 0; + ip->init.util.rmbcmt = 0; + ip->init.util.acul = 0; + + ip->init.util.mseg_alloc = &erts_alcu_mmapper_mseg_alloc; + ip->init.util.mseg_realloc = &erts_alcu_mmapper_mseg_realloc; + ip->init.util.mseg_dealloc = &erts_alcu_mmapper_mseg_dealloc; + ip->init.util.mseg_mmapper = &erts_exec_mmapper; +} +#endif /* ERTS_ALC_A_EXEC */ + static void set_default_temp_alloc_opts(struct au_init *ip) { @@ -660,6 +697,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) set_default_fix_alloc_opts(&init.fix_alloc, fix_type_sizes); set_default_literal_alloc_opts(&init.literal_alloc); +#ifdef ERTS_ALC_A_EXEC + set_default_exec_alloc_opts(&init.exec_alloc); +#endif set_default_test_alloc_opts(&init.test_alloc); if (argc && argv) @@ -689,6 +729,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) init.driver_alloc.thr_spec = 0; init.fix_alloc.thr_spec = 0; init.literal_alloc.thr_spec = 0; +#ifdef ERTS_ALC_A_EXEC + init.exec_alloc.thr_spec = 0; +#endif #endif /* Make adjustments for carrier migration support */ @@ -702,6 +745,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) adjust_carrier_migration_support(&init.driver_alloc); adjust_carrier_migration_support(&init.fix_alloc); adjust_carrier_migration_support(&init.literal_alloc); +#ifdef ERTS_ALC_A_EXEC + adjust_carrier_migration_support(&init.exec_alloc); +#endif if (init.erts_alloc_config) { /* Adjust flags that erts_alloc_config won't like */ @@ -717,6 +763,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) init.driver_alloc.thr_spec = 0; init.fix_alloc.thr_spec = 0; init.literal_alloc.thr_spec = 0; +#ifdef ERTS_ALC_A_EXEC + init.exec_alloc.thr_spec = 0; +#endif /* No carrier migration */ init.temp_alloc.init.util.acul = 0; @@ -729,6 +778,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) init.driver_alloc.init.util.acul = 0; init.fix_alloc.init.util.acul = 0; init.literal_alloc.init.util.acul = 0; +#ifdef ERTS_ALC_A_EXEC + init.exec_alloc.init.util.acul = 0; +#endif } #ifdef ERTS_SMP @@ -746,6 +798,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) adjust_tpref(&init.driver_alloc, erts_no_schedulers); adjust_tpref(&init.fix_alloc, erts_no_schedulers); adjust_tpref(&init.literal_alloc, erts_no_schedulers); +#ifdef ERTS_ALC_A_EXEC + adjust_tpref(&init.exec_alloc, erts_no_schedulers); +#endif #else /* No thread specific if not smp */ @@ -765,6 +820,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) refuse_af_strategy(&init.driver_alloc); refuse_af_strategy(&init.fix_alloc); refuse_af_strategy(&init.literal_alloc); +#ifdef ERTS_ALC_A_EXEC + refuse_af_strategy(&init.exec_alloc); +#endif #ifdef ERTS_SMP if (!init.temp_alloc.thr_spec) @@ -809,6 +867,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) set_au_allocator(ERTS_ALC_A_DRIVER, &init.driver_alloc, ncpu); set_au_allocator(ERTS_ALC_A_FIXED_SIZE, &init.fix_alloc, ncpu); set_au_allocator(ERTS_ALC_A_LITERAL, &init.literal_alloc, ncpu); +#ifdef ERTS_ALC_A_EXEC + set_au_allocator(ERTS_ALC_A_EXEC, &init.exec_alloc, ncpu); +#endif set_au_allocator(ERTS_ALC_A_TEST, &init.test_alloc, ncpu); for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) { @@ -865,7 +926,11 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) start_au_allocator(ERTS_ALC_A_LITERAL, &init.literal_alloc, &literal_alloc_state); - +#ifdef ERTS_ALC_A_EXEC + start_au_allocator(ERTS_ALC_A_EXEC, + &init.exec_alloc, + &exec_alloc_state); +#endif start_au_allocator(ERTS_ALC_A_TEST, &init.test_alloc, &test_alloc_state); @@ -1500,6 +1565,16 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) else handle_au_arg(&init->literal_alloc, &argv[i][3], argv, &i, 0); break; + case 'X': + if (has_prefix("scs", argv[i]+3)) { +#ifdef ERTS_ALC_A_EXEC + init->mseg.exec_mmap.scs = +#endif + get_mb_value(argv[i]+6, argv, &i); + } + else + handle_au_arg(&init->exec_alloc, &argv[i][3], argv, &i, 0); + break; case 'D': handle_au_arg(&init->std_alloc, &argv[i][3], argv, &i, 0); break; diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 14067283bd..0649ed903a 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -87,6 +87,9 @@ allocator EHEAP true eheap_alloc allocator ETS true ets_alloc allocator FIXED_SIZE true fix_alloc allocator LITERAL true literal_alloc ++if exec_alloc +allocator EXEC true exec_alloc ++endif +else # Non smp build @@ -98,6 +101,9 @@ allocator EHEAP false eheap_alloc allocator ETS false ets_alloc allocator FIXED_SIZE false fix_alloc allocator LITERAL false literal_alloc ++if exec_alloc +allocator EXEC false exec_alloc ++endif +endif @@ -335,8 +341,14 @@ type SL_MPATHS SHORT_LIVED SYSTEM sl_migration_paths # Currently most hipe code use this type. type HIPE SYSTEM SYSTEM hipe_data ++if exec_alloc +type HIPE_EXEC EXEC CODE hipe_code ++endif + +endif + + +if heap_frag_elim_test type SSB SHORT_LIVED PROCESSES ssb diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 0b2c26a548..163070e85a 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -22,6 +22,7 @@ # include "config.h" #endif +#define ERTS_WANT_MEM_MAPPERS #include "sys.h" #include "erl_vm.h" #include "global.h" diff --git a/erts/emulator/hipe/hipe_amd64.c b/erts/emulator/hipe/hipe_amd64.c index 1bb336637d..9d7ada18bc 100644 --- a/erts/emulator/hipe/hipe_amd64.c +++ b/erts/emulator/hipe/hipe_amd64.c @@ -83,21 +83,6 @@ int hipe_patch_call(void *callAddress, void *destAddress, void *trampoline) return 0; } -/* - * Memory allocator for executable code. - * - * This is required on AMD64 because some Linux kernels - * (including 2.6.10-rc1 and newer www.kernel.org ones) - * default to non-executable memory mappings, causing - * ordinary malloc() memory to be non-executable. - * - * Implementing this properly also allows us to ensure that - * executable code ends up in the low 2GB of the address space, - * as required by HiPE/AMD64's small code model. - */ -static unsigned int code_bytes; -static char *code_next; - #if 0 /* change to non-zero to get allocation statistics at exit() */ static unsigned int total_mapped, nr_joins, nr_splits, total_alloc, nr_allocs, nr_large, total_lost; static unsigned int atexit_done; @@ -121,101 +106,20 @@ static void atexit_alloc_code_stats(void) #define ALLOC_CODE_STATS(X) do{}while(0) #endif -/* FreeBSD 6.1 breakage */ -#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) -#define MAP_ANONYMOUS MAP_ANON -#endif - -static int morecore(unsigned int alloc_bytes) -{ - unsigned int map_bytes; - char *map_hint, *map_start; - - /* Page-align the amount to allocate. */ - map_bytes = (alloc_bytes + 4095) & ~4095; - - /* Round up small allocations. */ - if (map_bytes < 1024*1024) - map_bytes = 1024*1024; - else - ALLOC_CODE_STATS(++nr_large); - - /* Create a new memory mapping, ensuring it is executable - and in the low 2GB of the address space. Also attempt - to make it adjacent to the previous mapping. */ - map_hint = code_next + code_bytes; -#if !defined(MAP_32BIT) - /* FreeBSD doesn't have MAP_32BIT, and it doesn't respect - a plain map_hint (returns high mappings even though the - hint refers to a free area), so we have to use both map_hint - and MAP_FIXED to get addresses below the 2GB boundary. - This is even worse than the Linux/ppc64 case. - Similarly, Solaris 10 doesn't have MAP_32BIT, - and it doesn't respect a plain map_hint. */ - if (!map_hint) /* first call */ - map_hint = (char*)(512*1024*1024); /* 0.5GB */ -#endif - if ((unsigned long)map_hint & 4095) - abort(); - map_start = mmap(map_hint, map_bytes, - PROT_EXEC|PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS -#if defined(MAP_32BIT) - |MAP_32BIT -#elif defined(__FreeBSD__) || defined(__sun__) - |MAP_FIXED -#endif - , - -1, 0); - ALLOC_CODE_STATS(fprintf(stderr, "%s: mmap(%p,%u,...) == %p\r\n", __FUNCTION__, map_hint, map_bytes, map_start)); -#if !defined(MAP_32BIT) - if (map_start != MAP_FAILED && - (((unsigned long)map_start + (map_bytes-1)) & ~0x7FFFFFFFUL)) { - fprintf(stderr, "mmap with hint %p returned code memory %p\r\n", map_hint, map_start); - abort(); - } -#endif - if (map_start == MAP_FAILED) - return -1; - - ALLOC_CODE_STATS(total_mapped += map_bytes); - - /* Merge adjacent mappings, so the trailing portion of the previous - mapping isn't lost. In practice this is quite successful. */ - if (map_start == map_hint) { - ALLOC_CODE_STATS(++nr_joins); - code_bytes += map_bytes; -#if !defined(MAP_32BIT) - if (!code_next) /* first call */ - code_next = map_start; -#endif - } else { - ALLOC_CODE_STATS(++nr_splits); - ALLOC_CODE_STATS(total_lost += code_bytes); - code_next = map_start; - code_bytes = map_bytes; - } - - ALLOC_CODE_STATS(atexit_alloc_code_stats()); - - return 0; -} - +/* + * Memory allocator for executable code. + * + * We use a dedicated allocator for executable code (from OTP 19.0) + * to make sure the memory we get is executable (PROT_EXEC) + * and to ensure that executable code ends up in the low 2GB + * of the address space, as required by HiPE/AMD64's small code model. + */ static void *alloc_code(unsigned int alloc_bytes) { - void *res; - - /* Align function entries. */ - alloc_bytes = (alloc_bytes + 3) & ~3; - - if (code_bytes < alloc_bytes && morecore(alloc_bytes) != 0) - return NULL; ALLOC_CODE_STATS(++nr_allocs); ALLOC_CODE_STATS(total_alloc += alloc_bytes); - res = code_next; - code_next += alloc_bytes; - code_bytes -= alloc_bytes; - return res; + + return erts_alloc(ERTS_ALC_T_HIPE_EXEC, alloc_bytes); } void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *p) diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index f5f40b35a8..38970bd24e 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -356,6 +356,12 @@ char* erts_literals_start; UWord erts_literals_size; #endif +#ifdef ERTS_ALC_A_EXEC +ErtsMemMapper erts_exec_mmapper; +#endif + + + #define ERTS_MMAP_SIZE_SC_SA_INC(SZ) \ do { \ mm->size.supercarrier.used.total += (SZ); \ @@ -1351,10 +1357,39 @@ os_unreserve_physical(char *ptr, UWord size) } static void * -os_mmap_virtual(char *ptr, UWord size) +os_mmap_virtual(char *ptr, UWord size, int exec) { - void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_VIRTUAL_PROT, - ERTS_MMAP_VIRTUAL_FLAGS, ERTS_MMAP_FD, 0); + int flags = ERTS_MMAP_VIRTUAL_FLAGS; + void* res; + +#ifdef ERTS_ALC_A_EXEC + if (exec) { + ASSERT(!ptr); + /* OTP-19.0: Nice hack below cut-and-pasted from hipe_amd64.c */ + +# ifdef MAP_32BIT + /* If we got MAP_32BIT (Linux), then use that to ask for low memory */ + flags |= MAP_32BIT; +# else + /* FreeBSD doesn't have MAP_32BIT, and it doesn't respect + a plain map_hint (returns high mappings even though the + hint refers to a free area), so we have to use both map_hint + and MAP_FIXED to get addresses below the 2GB boundary. + This is even worse than the Linux/ppc64 case. + Similarly, Solaris 10 doesn't have MAP_32BIT, + and it doesn't respect a plain map_hint. */ + ptr = (char*)(512*1024*1024); /* 0.5GB */ + +# if defined(__FreeBSD__) || defined(__sun__) + flags |= MAP_FIXED; +# endif +# endif /* !MAP_32BIT */ + } +#else /* !ERTS_ALC_A_EXEC */ + ASSERT(!exec); +#endif + res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_VIRTUAL_PROT, + flags, ERTS_MMAP_FD, 0); if (res == (void *) MAP_FAILED) return NULL; return res; @@ -2177,7 +2212,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable) ptr = (char *) ERTS_PAGEALIGNED_CEILING(init->virtual_range.start); end = (char *) ERTS_PAGEALIGNED_FLOOR(init->virtual_range.end); sz = end - ptr; - start = os_mmap_virtual(ptr, sz); + start = os_mmap_virtual(ptr, sz, executable); if (!start || start > ptr || start >= end) erts_exit(1, "erts_mmap: Failed to create virtual range for super carrier\n"); @@ -2202,7 +2237,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable) sz = ERTS_PAGEALIGNED_CEILING(init->scs); #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION if (!init->scrpm) { - start = os_mmap_virtual(NULL, sz); + start = os_mmap_virtual(NULL, sz, executable); mm->reserve_physical = os_reserve_physical; mm->unreserve_physical = os_unreserve_physical; virtual_map = 1; diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 30548bd0b0..8a7530d943 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -55,6 +55,11 @@ typedef struct { #define ERTS_MMAP_INIT_LITERAL_INITER \ {{NULL, NULL}, {NULL, NULL}, ERTS_LITERAL_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0} +#define ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE (UWORD_CONSTANT(512)*1024*1024) + +#define ERTS_MMAP_INIT_HIPE_EXEC_INITER \ + {{NULL, NULL}, {NULL, NULL}, ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0} + typedef struct ErtsMemMapper_ ErtsMemMapper; void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep); @@ -126,10 +131,17 @@ Eterm erts_mmap_debug_info(ErtsMemMapper*, struct process*); # define ERTS_HAVE_OS_MMAP 1 #endif +#ifdef ERTS_WANT_MEM_MAPPERS +# include "erl_alloc_types.h" + extern ErtsMemMapper erts_dflt_mmapper; -#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) +# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) extern ErtsMemMapper erts_literal_mmapper; -#endif +# endif +# ifdef ERTS_ALC_A_EXEC +extern ErtsMemMapper erts_exec_mmapper; +# endif +#endif /* ERTS_WANT_MEM_MAPPERS */ /*#define HARD_DEBUG_MSEG*/ #ifdef HARD_DEBUG_MSEG diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index f0fbb682b0..f373302e65 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -31,6 +31,7 @@ # include "config.h" #endif +#define ERTS_WANT_MEM_MAPPERS #include "sys.h" #include "erl_mseg.h" #include "global.h" @@ -1402,6 +1403,12 @@ erts_mseg_init(ErtsMsegInit_t *init) erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms"); +#ifdef ERTS_ALC_A_EXEC + /* Initialize erts_exec_mapper *FIRST*, to increase probability + * of getting low memory for HiPE AMD64's small code model. + */ + erts_mmap_init(&erts_exec_mmapper, &init->exec_mmap, 1); +#endif erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap, 0); #if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap, 0); diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index 2acd8f8505..556a135798 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -61,6 +61,7 @@ typedef struct { Uint nos; ErtsMMapInit dflt_mmap; ErtsMMapInit literal_mmap; + ErtsMMapInit exec_mmap; } ErtsMsegInit_t; #define ERTS_MSEG_INIT_DEFAULT_INITIALIZER \ @@ -70,7 +71,8 @@ typedef struct { 10, /* mcs: Max cache size */ \ 1000, /* cci: Cache check interval */ \ ERTS_MMAP_INIT_DEFAULT_INITER, \ - ERTS_MMAP_INIT_LITERAL_INITER \ + ERTS_MMAP_INIT_LITERAL_INITER, \ + ERTS_MMAP_INIT_HIPE_EXEC_INITER \ } typedef struct { diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 54da59e50d..96259f8fa6 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -74,6 +74,7 @@ static const char plusM_au_allocs[]= { 'R', /* driver_alloc */ 'S', /* sl_alloc */ 'T', /* temp_alloc */ + 'X', /* exec_alloc */ 'Z', /* test_alloc */ '\0' }; @@ -123,6 +124,7 @@ static char *plusM_other_switches[] = { "Ytp", "Ytt", "Iscs", + "Xscs", NULL }; -- cgit v1.2.3 From 4e1102cf8c5bc9ae6a8371665ef6aa9de08e653a Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 17 Apr 2016 20:24:32 +0200 Subject: clean up FP exception code in sys_float.c This performs a number of cleanups in the FP exception code: - inline the body of unmask_fpe_conditional() in its only caller, then delete the duplicated and identical definitions of it - start the big processor-specific block with a comment describing the two functions that must be defined, then delete redundant comments at all the mask_*() functions - add a comment before fpe_sig_action() explaining exactly what processor-specific action is required of it - flatten #ifdef nesting in fpe_sig_action() - move common code in the x86 unmask_fpe() and erts_restore_fpu() function into a subroutine - minor tweaks: drop a redundant L suffix on a 0, add a comment after an #else, bump the size of a debug buffer, There should be no change in behaviour from these changes. --- erts/emulator/sys/unix/sys_float.c | 79 +++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 43 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c index 8fe7e599e5..2ac35624fa 100644 --- a/erts/emulator/sys/unix/sys_float.c +++ b/erts/emulator/sys/unix/sys_float.c @@ -53,7 +53,7 @@ static void erts_init_fp_exception(void) void erts_thread_init_fp_exception(void) { unsigned long *fpe = erts_alloc(ERTS_ALC_T_FP_EXCEPTION, sizeof(*fpe)); - *fpe = 0L; + *fpe = 0; erts_tsd_set(fpe_key, fpe); } @@ -102,6 +102,17 @@ void erts_fp_check_init_error(volatile unsigned long *fpexnp) #define __DARWIN__ 1 #endif +/* + * Define two processor and possibly OS-specific primitives: + * + * static void unmask_fpe(void); + * -- unmask invalid, overflow, and divide-by-zero exceptions + * + * static int mask_fpe(void); + * -- mask invalid, overflow, and divide-by-zero exceptions + * -- return non-zero if the previous state was unmasked + */ + #if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__) static void unmask_x87(void) @@ -113,7 +124,6 @@ static void unmask_x87(void) __asm__ __volatile__("fldcw %0" : : "m"(cw)); } -/* mask x87 FPE, return true if the previous state was unmasked */ static int mask_x87(void) { unsigned short cw; @@ -136,7 +146,6 @@ static void unmask_sse2(void) __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr)); } -/* mask SSE2 FPE, return true if the previous state was unmasked */ static int mask_sse2(void) { unsigned int mxcsr; @@ -257,21 +266,19 @@ static int cpu_has_sse2(void) } #endif /* !__x86_64__ */ -static void unmask_fpe(void) +static void unmask_fpe_internal(void) { - __asm__ __volatile__("fnclex"); unmask_x87(); if (cpu_has_sse2()) unmask_sse2(); } -static void unmask_fpe_conditional(int unmasked) +static void unmask_fpe(void) { - if (unmasked) - unmask_fpe(); + __asm__ __volatile__("fnclex"); + unmask_fpe_internal(); } -/* mask x86 FPE, return true if the previous state was unmasked */ static int mask_fpe(void) { int unmasked; @@ -285,9 +292,7 @@ static int mask_fpe(void) void erts_restore_fpu(void) { __asm__ __volatile__("fninit"); - unmask_x87(); - if (cpu_has_sse2()) - unmask_sse2(); + unmask_fpe_internal(); } #elif defined(__sparc__) && defined(__linux__) @@ -310,13 +315,6 @@ static void unmask_fpe(void) __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr)); } -static void unmask_fpe_conditional(int unmasked) -{ - if (unmasked) - unmask_fpe(); -} - -/* mask SPARC FPE, return true if the previous state was unmasked */ static int mask_fpe(void) { unsigned long fsr; @@ -431,13 +429,6 @@ static void unmask_fpe(void) set_fpscr(0x80|0x40|0x10); /* VE, OE, ZE; not UE or XE */ } -static void unmask_fpe_conditional(int unmasked) -{ - if (unmasked) - unmask_fpe(); -} - -/* mask PowerPC FPE, return true if the previous state was unmasked */ static int mask_fpe(void) { int unmasked; @@ -447,20 +438,13 @@ static int mask_fpe(void) return unmasked; } -#else +#else /* !(x86 || (sparc && linux) || (powerpc && (linux || darwin))) */ static void unmask_fpe(void) { fpsetmask(FP_X_INV | FP_X_OFL | FP_X_DZ); } -static void unmask_fpe_conditional(int unmasked) -{ - if (unmasked) - unmask_fpe(); -} - -/* mask IEEE FPE, return true if previous state was unmasked */ static int mask_fpe(void) { const fp_except unmasked_mask = FP_X_INV | FP_X_OFL | FP_X_DZ; @@ -472,6 +456,16 @@ static int mask_fpe(void) #endif +/* + * Define a processor and OS-specific SIGFPE handler. + * + * The effect of receiving a SIGFPE should be: + * 1. Update the processor context: + * a) on x86: mask FP exceptions, do not skip faulting instruction + * b) on SPARC and PowerPC: unmask FP exceptions, skip faulting instruction + * 2. call set_current_fp_exception with the PC of the faulting instruction + */ + #if (defined(__linux__) && (defined(__i386__) || defined(__x86_64__) || defined(__sparc__) || defined(__powerpc__))) || (defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__) || defined(__ppc__))) || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__))) || ((defined(__NetBSD__) || defined(__OpenBSD__)) && defined(__x86_64__)) || (defined(__sun__) && defined(__x86_64__)) #if defined(__linux__) && defined(__i386__) @@ -530,8 +524,7 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc) ucontext_t *uc = puc; unsigned long pc; -#if defined(__linux__) -#if defined(__x86_64__) +#if defined(__linux__) && defined(__x86_64__) mcontext_t *mc = &uc->uc_mcontext; fpregset_t fpstate = mc->fpregs; pc = mc_pc(mc); @@ -543,26 +536,26 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc) set encoding makes that a poor solution here. */ fpstate->mxcsr = 0x1F80; fpstate->swd &= ~0xFF; -#elif defined(__i386__) +#elif defined(__linux__) && defined(__i386__) mcontext_t *mc = &uc->uc_mcontext; fpregset_t fpstate = mc->fpregs; pc = mc_pc(mc); if ((fpstate->status >> 16) == X86_FXSR_MAGIC) ((struct _fpstate*)fpstate)->mxcsr = 0x1F80; fpstate->sw &= ~0xFF; -#elif defined(__sparc__) && defined(__arch64__) +#elif defined(__linux__) && defined(__sparc__) && defined(__arch64__) /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */ struct sigcontext *sc = (struct sigcontext*)puc; pc = sc->sigc_regs.tpc; sc->sigc_regs.tpc = sc->sigc_regs.tnpc; sc->sigc_regs.tnpc += 4; -#elif defined(__sparc__) +#elif defined(__linux__) && defined(__sparc__) /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */ struct sigcontext *sc = (struct sigcontext*)puc; pc = sc->si_regs.pc; sc->si_regs.pc = sc->si_regs.npc; sc->si_regs.npc = (unsigned long)sc->si_regs.npc + 4; -#elif defined(__powerpc__) +#elif defined(__linux__) && defined(__powerpc__) #if defined(__powerpc64__) mcontext_t *mc = &uc->uc_mcontext; unsigned long *regs = &mc->gp_regs[0]; @@ -573,7 +566,6 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc) pc = regs[PT_NIP]; regs[PT_NIP] += 4; regs[PT_FPSCR] = 0x80|0x40|0x10; /* VE, OE, ZE; not UE or XE */ -#endif #elif defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__)) #ifdef DARWIN_MODERN_MCONTEXT mcontext_t mc = uc->uc_mcontext; @@ -641,7 +633,7 @@ static void fpe_sig_action(int sig, siginfo_t *si, void *puc) #endif #if 0 { - char buf[64]; + char buf[128]; snprintf(buf, sizeof buf, "%s: FPE at %p\r\n", __FUNCTION__, (void*)pc); write(2, buf, strlen(buf)); } @@ -726,7 +718,8 @@ int erts_sys_block_fpe(void) void erts_sys_unblock_fpe(int unmasked) { - unmask_fpe_conditional(unmasked); + if (unmasked) + unmask_fpe(); } #endif -- cgit v1.2.3 From 1afc15d988ee9fc57eaf9728dc52d8df75b5b12d Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 18 Apr 2016 17:38:45 +0200 Subject: erts: Fix lock order bug when only child is procs traced --- erts/emulator/beam/erl_process.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a520005c35..1a66044627 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11183,10 +11183,12 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). } if (IS_TRACED_FL(p, F_TRACE_PROCS)) { - if (locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)) { + if ((locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)) + == (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)) { + /* This happens when parent was not traced, but child is */ locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); - erts_smp_proc_unlock(p, locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)); - erts_smp_proc_unlock(parent, locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)); + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + erts_smp_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); } trace_proc_spawn(p, am_spawned, parent->common.id, mod, func, args); if (so->flags & SPO_LINK) -- cgit v1.2.3 From 11e8e1e38bba355c2996d766b7d2893e441c1211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 18 Feb 2016 16:21:30 +0100 Subject: runtime_tools: Initial lttng tracing framework --- erts/emulator/beam/erl_nif.c | 2 +- erts/emulator/beam/erl_trace.c | 175 ++++++++++++++++++++++++++++------------- 2 files changed, 123 insertions(+), 54 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 73c0eb8eba..9a0eea42f0 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -3158,7 +3158,7 @@ Eterm erts_nif_call_function(Process *p, Process *tracee, /* Verify that function is part of this module */ int i; for (i = 0; i < mod->entry->num_of_funcs; i++) - if (fun == mod->entry->funcs+i) + if (fun == &(mod->entry->funcs[i])) break; ASSERT(i < mod->entry->num_of_funcs); if (p) diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index bd88769dfc..96fc46c817 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -359,14 +359,30 @@ void erts_init_trace(void) { *(OHPP) = &(*(BPP))->off_heap, \ (*(BPP))->mem) +enum ErtsTracerOpt { + TRACE_FUN_DEFAULT = 0, + TRACE_FUN_ENABLED = 1, + TRACE_FUN_SEND = 2, + TRACE_FUN_RECEIVE = 3, + TRACE_FUN_CALL = 4, + TRACE_FUN_RUNNING = 5, + TRACE_FUN_GC = 6, + TRACE_FUN_PROCS = 7, + TRACE_FUN_PORTS = 8 +}; + +#define NIF_TRACER_TYPES (9) + + static ERTS_INLINE int send_to_tracer_nif_raw(Process *c_p, Process *tracee, const ErtsTracer tracer, Uint trace_flags, Eterm t_p_id, ErtsTracerNif *tnif, + enum ErtsTracerOpt topt, Eterm tag, Eterm msg, Eterm extra, Eterm pam_result); static ERTS_INLINE int send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, - Eterm t_p_id, ErtsTracerNif *tnif, Eterm tag, - Eterm msg, Eterm extra); + Eterm t_p_id, ErtsTracerNif *tnif, enum ErtsTracerOpt, + Eterm tag, Eterm msg, Eterm extra); static ERTS_INLINE Eterm call_enabled_tracer(Process *c_p, const ErtsTracer tracer, ErtsTracerNif **tnif_ref, Eterm tag, Eterm t_p_id); @@ -375,9 +391,9 @@ is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, ErtsPTabElementCommon *t_p, ErtsTracerNif **tnif_ret, Eterm tag); -#define SEND_TO_TRACER(c_p, tag, msg) \ - send_to_tracer_nif(c_p, &(c_p)->common, (c_p)->common.id, NULL, tag, \ - msg, THE_NON_VALUE) +#define SEND_TO_TRACER(c_p, tag, msg) \ + send_to_tracer_nif(c_p, &(c_p)->common, (c_p)->common.id, NULL, \ + TRACE_FUN_DEFAULT, tag, msg, THE_NON_VALUE) static Uint active_sched; @@ -756,7 +772,7 @@ trace_sched_aux(Process *p, ErtsProcLocks locks, Eterm what) hp += 4; } - send_to_tracer_nif(p, &p->common, p->common.id, tnif, + send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_RUNNING, what, tmp, THE_NON_VALUE); } @@ -796,7 +812,8 @@ trace_send(Process *p, Eterm to, Eterm msg) } if (is_tracer_proc_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, operation)) - send_to_tracer_nif(p, &p->common, p->common.id, tnif, operation, msg, to); + send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_SEND, + operation, msg, to); } /* Send {trace_ts, Pid, receive, Msg, Timestamp} @@ -809,7 +826,8 @@ trace_receive(Process *c_p, Eterm msg) if (is_tracer_proc_enabled(NULL, 0, &c_p->common, &tnif, am_receive)) send_to_tracer_nif(NULL, &c_p->common, c_p->common.id, - tnif, am_receive, msg, THE_NON_VALUE); + tnif, TRACE_FUN_RECEIVE, + am_receive, msg, THE_NON_VALUE); } int @@ -897,14 +915,13 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, msg = TUPLE3(hp, am_EXIT, exitfrom, msg); hp += 4; } - mess = TUPLE5(hp, type_atom, lastcnt_serial, SEQ_TRACE_T_SENDER(token), - receiver, msg); + mess = TUPLE5(hp, type_atom, lastcnt_serial, SEQ_TRACE_T_SENDER(token), receiver, msg); hp += 6; seq_tracer_flags |= ERTS_SEQTFLGS2TFLGS(unsigned_val(SEQ_TRACE_T_FLAGS(token))); send_to_tracer_nif_raw(NULL, process, seq_tracer, seq_tracer_flags, - label, NULL, am_seq_trace, mess, + label, NULL, TRACE_FUN_DEFAULT, am_seq_trace, mess, THE_NON_VALUE, am_true); UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); @@ -983,7 +1000,7 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, ErtsTracer *tracer) mfa = TUPLE3(hp, mod, name, make_small(arity)); hp += 4; send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, - NULL, am_return_from, mfa, retval, am_true); + NULL, TRACE_FUN_DEFAULT, am_return_from, mfa, retval, am_true); } /* Send {trace_ts, Pid, exception_from, {Mod, Name, Arity}, {Class,Value}, @@ -1038,7 +1055,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value, cv = TUPLE2(hp, class, value); hp += 3; send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, - NULL, am_exception_from, mfa_tuple, cv, am_true); + NULL, TRACE_FUN_DEFAULT, am_exception_from, mfa_tuple, cv, am_true); } /* @@ -1227,7 +1244,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, * Build the trace tuple and send it to the port. */ send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, - tnif, am_call, mfa_tuple, THE_NON_VALUE, pam_result); + tnif, TRACE_FUN_CALL, am_call, mfa_tuple, THE_NON_VALUE, pam_result); erts_match_set_release_result(p); if (match_spec && tracer == &pre_ms_tracer) @@ -1250,7 +1267,7 @@ trace_proc(Process *c_p, ErtsProcLocks c_p_locks, { ErtsTracerNif *tnif = NULL; if (is_tracer_proc_enabled(c_p, c_p_locks, &t_p->common, &tnif, what)) - send_to_tracer_nif(c_p, &t_p->common, t_p->common.id, tnif, + send_to_tracer_nif(c_p, &t_p->common, t_p->common.id, tnif, TRACE_FUN_PROCS, what, data, THE_NON_VALUE); } @@ -1276,7 +1293,8 @@ trace_proc_spawn(Process *p, Eterm what, Eterm pid, hp = HAlloc(p, 4); mfa = TUPLE3(hp, mod, func, args); hp += 4; - send_to_tracer_nif(p, &p->common, p->common.id, tnif, what, pid, mfa); + send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_PROCS, + am_spawn, pid, mfa); } } @@ -1323,8 +1341,8 @@ trace_gc(Process *p, Eterm what) msg = erts_process_gc_info(p, NULL, &hp); - send_to_tracer_nif(p, &p->common, p->common.id, tnif, what, - msg, THE_NON_VALUE); + send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_GC, + what, msg, THE_NON_VALUE); } } @@ -1738,8 +1756,8 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) { ErtsTracerNif *tnif = NULL; ERTS_SMP_CHK_NO_PROC_LOCKS; if (is_tracer_proc_enabled(NULL, 0, &p->common, &tnif, am_open)) - send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, am_open, - calling_pid, drv_name); + send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, TRACE_FUN_PORTS, + am_open, calling_pid, drv_name); } /* Sends trace message: @@ -1757,7 +1775,7 @@ trace_port(Port *t_p, Eterm what, Eterm data) { || erts_thr_progress_is_blocking()); ERTS_SMP_CHK_NO_PROC_LOCKS; if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, what)) - send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_PORTS, what, data, THE_NON_VALUE); } @@ -1894,7 +1912,7 @@ trace_port_receive(Port *t_p, Eterm caller, Eterm what, ...) } data = TUPLE2(hp, caller, data); - send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_RECEIVE, am_receive, data, THE_NON_VALUE); if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0) @@ -1917,7 +1935,7 @@ trace_port_send(Port *t_p, Eterm receiver, Eterm msg, int exists) || erts_thr_progress_is_blocking()); ERTS_SMP_CHK_NO_PROC_LOCKS; if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, op)) - send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_SEND, op, msg, receiver); } @@ -1946,7 +1964,7 @@ void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz) msg = TUPLE2(hp, t_p->common.id, msg); hp += 3; - send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_SEND, am_send, msg, to); if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0) erts_bin_free(bptr); @@ -1977,7 +1995,8 @@ trace_sched_ports_where(Port *t_p, Eterm what, Eterm where) { ERTS_SMP_CHK_NO_PROC_LOCKS; if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, what)) send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, - tnif, what, where, THE_NON_VALUE); + tnif, TRACE_FUN_RUNNING, + what, where, THE_NON_VALUE); } /* Port profiling */ @@ -2523,14 +2542,60 @@ init_sys_msg_dispatcher(void) #include "erl_nif.h" +typedef struct { + char *name; + Uint arity; + ErlNifFunc *cb; +} ErtsTracerType; + struct ErtsTracerNif_ { HashBucket hb; Eterm module; struct erl_module_nif* nif_mod; - ErlNifFunc *enabled; - ErlNifFunc *trace; + ErtsTracerType tracers[NIF_TRACER_TYPES]; }; +static void init_tracer_template(ErtsTracerNif *tnif) { + + /* default tracer functions */ + tnif->tracers[TRACE_FUN_DEFAULT].name = "trace"; + tnif->tracers[TRACE_FUN_DEFAULT].arity = 6; + tnif->tracers[TRACE_FUN_DEFAULT].cb = NULL; + + tnif->tracers[TRACE_FUN_ENABLED].name = "enabled"; + tnif->tracers[TRACE_FUN_ENABLED].arity = 3; + tnif->tracers[TRACE_FUN_ENABLED].cb = NULL; + + /* specific tracer functions */ + tnif->tracers[TRACE_FUN_SEND].name = "trace_send"; + tnif->tracers[TRACE_FUN_SEND].arity = 6; + tnif->tracers[TRACE_FUN_SEND].cb = NULL; + + tnif->tracers[TRACE_FUN_RECEIVE].name = "trace_receive"; + tnif->tracers[TRACE_FUN_RECEIVE].arity = 6; + tnif->tracers[TRACE_FUN_RECEIVE].cb = NULL; + + tnif->tracers[TRACE_FUN_CALL].name = "trace_call"; + tnif->tracers[TRACE_FUN_CALL].arity = 6; + tnif->tracers[TRACE_FUN_CALL].cb = NULL; + + tnif->tracers[TRACE_FUN_RUNNING].name = "trace_running"; + tnif->tracers[TRACE_FUN_RUNNING].arity = 6; + tnif->tracers[TRACE_FUN_RUNNING].cb = NULL; + + tnif->tracers[TRACE_FUN_GC].name = "trace_garbage_collection"; + tnif->tracers[TRACE_FUN_GC].arity = 6; + tnif->tracers[TRACE_FUN_GC].cb = NULL; + + tnif->tracers[TRACE_FUN_PROCS].name = "trace_procs"; + tnif->tracers[TRACE_FUN_PROCS].arity = 6; + tnif->tracers[TRACE_FUN_PROCS].cb = NULL; + + tnif->tracers[TRACE_FUN_PORTS].name = "trace_ports"; + tnif->tracers[TRACE_FUN_PORTS].arity = 6; + tnif->tracers[TRACE_FUN_PORTS].cb = NULL; +} + static Hash *tracer_hash = NULL; static erts_smp_rwmtx_t tracer_mtx; @@ -2543,31 +2608,32 @@ load_tracer_nif(const ErtsTracer tracer) ErlNifFunc *funcs; int num_of_funcs; ErtsTracerNif tnif_tmpl, *tnif; - int i; + ErtsTracerType *tracers; + int i,j; - if (mod && mod->curr.nif != NULL) { - instance = &mod->curr; - } else { + if (!mod || !mod->curr.nif) { return NULL; } - tnif_tmpl.enabled = NULL; - tnif_tmpl.trace = NULL; + instance = &mod->curr; + + init_tracer_template(&tnif_tmpl); tnif_tmpl.nif_mod = instance->nif; tnif_tmpl.module = ERTS_TRACER_MODULE(tracer); + tracers = tnif_tmpl.tracers; num_of_funcs = erts_nif_get_funcs(instance->nif, &funcs); for(i = 0; i < num_of_funcs; i++) { - if (strcmp("enabled",funcs[i].name) == 0 && funcs[i].arity == 3) { - tnif_tmpl.enabled = funcs + i; - } else if (strcmp("trace",funcs[i].name) == 0 && funcs[i].arity == 6) { - tnif_tmpl.trace = funcs + i; + for (j = 0; j < NIF_TRACER_TYPES; j++) { + if (strcmp(tracers[j].name, funcs[i].name) == 0 && tracers[j].arity == funcs[i].arity) { + tracers[j].cb = &(funcs[i]); + break; + } } } - if (tnif_tmpl.enabled == NULL || - tnif_tmpl.trace == NULL ) { + if (tracers[TRACE_FUN_DEFAULT].cb == NULL || tracers[TRACE_FUN_ENABLED].cb == NULL ) { return NULL; } @@ -2660,17 +2726,16 @@ erts_tracer_to_term(Process *p, ErtsTracer tracer) static ERTS_INLINE int send_to_tracer_nif_raw(Process *c_p, Process *tracee, const ErtsTracer tracer, Uint tracee_flags, - Eterm t_p_id, ErtsTracerNif *tnif, Eterm tag, Eterm msg, - Eterm extra, Eterm pam_result) + Eterm t_p_id, ErtsTracerNif *tnif, + enum ErtsTracerOpt topt_arg, + Eterm tag, Eterm msg, Eterm extra, Eterm pam_result) { if (tnif || (tnif = lookup_tracer_nif(tracer)) != NULL) { #define MAP_SIZE 3 - Eterm argv[6], - local_heap[3+MAP_SIZE /* values */+(MAP_SIZE+1 /* keys */)]; + Eterm argv[6], local_heap[3+MAP_SIZE /* values */ + (MAP_SIZE+1 /* keys */)]; flatmap_t *map = (flatmap_t*)(local_heap+(MAP_SIZE+1)); Eterm *map_values = flatmap_get_values(map); - - int argc = 6; + enum ErtsTracerOpt topt = (tnif->tracers[topt_arg].cb) ? topt_arg : TRACE_FUN_DEFAULT; argv[0] = tag; argv[1] = ERTS_TRACER_STATE(tracer); @@ -2681,8 +2746,7 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee, map->thing_word = MAP_HEADER_FLATMAP; map->size = MAP_SIZE; - map->keys = TUPLE3(local_heap, am_match_spec_result, am_scheduler_id, - am_timestamp); + map->keys = TUPLE3(local_heap, am_match_spec_result, am_scheduler_id, am_timestamp); *map_values++ = pam_result; if (tracee_flags & F_TRACE_SCHED_NO) @@ -2705,16 +2769,18 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee, #undef MAP_SIZE erts_nif_call_function(c_p, tracee ? tracee : c_p, - tnif->nif_mod, tnif->trace, argc, argv); + tnif->nif_mod, + tnif->tracers[topt].cb, + tnif->tracers[topt].arity, + argv); } return 1; } - static ERTS_INLINE int send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, - Eterm t_p_id, ErtsTracerNif *tnif, Eterm tag, - Eterm msg, Eterm extra) + Eterm t_p_id, ErtsTracerNif *tnif, enum ErtsTracerOpt topt, + Eterm tag, Eterm msg, Eterm extra) { #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) if (c_p) { @@ -2733,7 +2799,7 @@ send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, return send_to_tracer_nif_raw(c_p, is_internal_pid(t_p->id) ? (Process*)t_p : NULL, t_p->tracer, t_p->trace_flags, - t_p_id, tnif, tag, msg, extra, + t_p_id, tnif, topt, tag, msg, extra, am_true); } @@ -2746,7 +2812,10 @@ call_enabled_tracer(Process *c_p, const ErtsTracer tracer, Eterm argv[] = {tag, ERTS_TRACER_STATE(tracer), t_p_id}; if (tnif_ret) *tnif_ret = tnif; return erts_nif_call_function( - c_p, NULL, tnif->nif_mod, tnif->enabled, 3, argv); + c_p, NULL, tnif->nif_mod, + tnif->tracers[TRACE_FUN_ENABLED].cb, + tnif->tracers[TRACE_FUN_ENABLED].arity, + argv); } return am_remove; } -- cgit v1.2.3 From b3c7a581070180750a368ddf3bd55db1bafc11ea Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 15 Apr 2016 18:39:59 +0200 Subject: erts: Make sure literal MBCs have super aligned sizes on 32-bit, as the granularity of the literal bit vector is super-alignment. --- erts/emulator/beam/erl_alloc_util.c | 53 ++++++++++++++++++++++++------------- erts/emulator/beam/erl_alloc_util.h | 16 +++++------ 2 files changed, 42 insertions(+), 27 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index e362a03646..680899c919 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -859,28 +859,34 @@ void* erts_alcu_literal_32_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) { void* res; + Uint sz = ERTS_SUPERALIGNED_CEILING(*size_p); ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL && !allctr->t && allctr->thread_safe); - res = erts_alcu_mseg_alloc(allctr, size_p, flags); - if (res) - set_literal_range(res, *size_p); + res = erts_alcu_mseg_alloc(allctr, &sz, flags); + if (res) { + set_literal_range(res, sz); + *size_p = sz; + } return res; } void* erts_alcu_literal_32_mseg_realloc(Allctr_t *allctr, void *seg, - Uint old_size, Uint *new_size_p) + Uint old_size, Uint *new_size_p) { void* res; + Uint new_sz = ERTS_SUPERALIGNED_CEILING(*new_size_p); ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL && !allctr->t && allctr->thread_safe); if (seg && old_size) clear_literal_range(seg, old_size); - res = erts_alcu_mseg_realloc(allctr, seg, old_size, new_size_p); - if (res) - set_literal_range(res, *new_size_p); + res = erts_alcu_mseg_realloc(allctr, seg, old_size, &new_sz); + if (res) { + set_literal_range(res, new_sz); + *new_size_p = new_sz; + } return res; } @@ -942,9 +948,10 @@ erts_alcu_mmapper_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, #endif /* HAVE_ERTS_MSEG */ void* -erts_alcu_sys_alloc(Allctr_t *allctr, Uint size, int superalign) +erts_alcu_sys_alloc(Allctr_t *allctr, Uint* size_p, int superalign) { void *res; + const Uint size = *size_p; #if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC if (superalign) res = erts_sys_aligned_alloc(ERTS_SACRR_UNIT_SZ, size); @@ -958,9 +965,10 @@ erts_alcu_sys_alloc(Allctr_t *allctr, Uint size, int superalign) } void* -erts_alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign) +erts_alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint *size_p, Uint old_size, int superalign) { void *res; + const Uint size = *size_p; #if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC if (superalign) @@ -995,30 +1003,37 @@ erts_alcu_sys_dealloc(Allctr_t *allctr, void *ptr, Uint size, int superalign) #ifdef ARCH_32 void* -erts_alcu_literal_32_sys_alloc(Allctr_t *allctr, Uint size, int superalign) +erts_alcu_literal_32_sys_alloc(Allctr_t *allctr, Uint* size_p, int superalign) { void* res; + Uint size = ERTS_SUPERALIGNED_CEILING(*size_p); ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL && !allctr->t && allctr->thread_safe); - res = erts_alcu_sys_alloc(allctr, size, 1); - if (res) + res = erts_alcu_sys_alloc(allctr, &size, 1); + if (res) { set_literal_range(res, size); + *size_p = size; + } return res; } void* -erts_alcu_literal_32_sys_realloc(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign) +erts_alcu_literal_32_sys_realloc(Allctr_t *allctr, void *ptr, Uint* size_p, Uint old_size, int superalign) { void* res; + Uint size = ERTS_SUPERALIGNED_CEILING(*size_p); + ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL && !allctr->t && allctr->thread_safe); if (ptr && old_size) clear_literal_range(ptr, old_size); - res = erts_alcu_sys_realloc(allctr, ptr, size, old_size, 1); - if (res) + res = erts_alcu_sys_realloc(allctr, ptr, &size, old_size, 1); + if (res) { set_literal_range(res, size); + *size_p = size; + } return res; } @@ -3865,12 +3880,12 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags) ? UNIT_CEILING(bcrr_sz) : SYS_ALLOC_CARRIER_CEILING(bcrr_sz)); - crr = (Carrier_t *) allctr->sys_alloc(allctr, crr_sz, flags & CFLG_MBC); + crr = (Carrier_t *) allctr->sys_alloc(allctr, &crr_sz, flags & CFLG_MBC); if (!crr) { if (crr_sz > UNIT_CEILING(bcrr_sz)) { crr_sz = UNIT_CEILING(bcrr_sz); - crr = (Carrier_t *) allctr->sys_alloc(allctr, crr_sz, flags & CFLG_MBC); + crr = (Carrier_t *) allctr->sys_alloc(allctr, &crr_sz, flags & CFLG_MBC); } if (!crr) { #if HAVE_ERTS_MSEG @@ -4028,7 +4043,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) new_crr = (Carrier_t *) allctr->sys_realloc(allctr, (void *) old_crr, - new_crr_sz, + &new_crr_sz, old_crr_sz, 0); if (new_crr) { @@ -4049,7 +4064,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) new_crr_sz = UNIT_CEILING(new_crr_sz); new_crr = (Carrier_t *) allctr->sys_realloc(allctr, (void *) old_crr, - new_crr_sz, + &new_crr_sz, old_crr_sz, 0); if (new_crr) diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index 51af59e40c..b3ae4c3281 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -74,8 +74,8 @@ typedef struct { void (*mseg_dealloc)(Allctr_t*, void *seg, Uint size, Uint flags); ErtsMemMapper *mseg_mmapper; #endif - void* (*sys_alloc)(Allctr_t *allctr, Uint size, int superalign); - void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign); + void* (*sys_alloc)(Allctr_t *allctr, Uint *size_p, int superalign); + void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint *size_p, Uint old_size, int superalign); void (*sys_dealloc)(Allctr_t *allctr, void *ptr, Uint size, int superalign); } AllctrInit_t; @@ -212,12 +212,12 @@ void erts_alcu_mmapper_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags # endif #endif /* HAVE_ERTS_MSEG */ -void* erts_alcu_sys_alloc(Allctr_t*, Uint size, int superalign); -void* erts_alcu_sys_realloc(Allctr_t*, void *ptr, Uint size, Uint old_size, int superalign); +void* erts_alcu_sys_alloc(Allctr_t*, Uint *size_p, int superalign); +void* erts_alcu_sys_realloc(Allctr_t*, void *ptr, Uint *size_p, Uint old_size, int superalign); void erts_alcu_sys_dealloc(Allctr_t*, void *ptr, Uint size, int superalign); #ifdef ARCH_32 -void* erts_alcu_literal_32_sys_alloc(Allctr_t*, Uint size, int superalign); -void* erts_alcu_literal_32_sys_realloc(Allctr_t*, void *ptr, Uint size, Uint old_size, int superalign); +void* erts_alcu_literal_32_sys_alloc(Allctr_t*, Uint *size_p, int superalign); +void* erts_alcu_literal_32_sys_realloc(Allctr_t*, void *ptr, Uint *size_p, Uint old_size, int superalign); void erts_alcu_literal_32_sys_dealloc(Allctr_t*, void *ptr, Uint size, int superalign); #endif @@ -604,8 +604,8 @@ struct Allctr_t_ { void (*mseg_dealloc)(Allctr_t*, void *seg, Uint size, Uint flags); ErtsMemMapper *mseg_mmapper; #endif - void* (*sys_alloc)(Allctr_t *allctr, Uint size, int superalign); - void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint size, Uint old_size, int superalign); + void* (*sys_alloc)(Allctr_t *allctr, Uint *size_p, int superalign); + void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint *size_p, Uint old_size, int superalign); void (*sys_dealloc)(Allctr_t *allctr, void *ptr, Uint size, int superalign); void (*init_atoms) (void); -- cgit v1.2.3 From 4319cd608063e3612c74b534180b19ab29173667 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 15 Apr 2016 14:16:14 +0200 Subject: erts: Produce statistics for literal and hipe super carriers called 'literal_mmap' and 'exec_mmap'. Also moved existing erts_mmap info from 'mseg_alloc' to its own system_info({allocator, erts_mmap}) with "allocators" default_mmap, literal_mmap and exec_mmap. --- erts/doc/src/erlang.xml | 14 ++++--- erts/emulator/beam/erl_alloc.c | 79 ++++++++++++++++++++++++++++++-------- erts/emulator/test/alloc_SUITE.erl | 4 +- 3 files changed, 72 insertions(+), 25 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 423ccdf98f..ee34f28b90 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6839,11 +6839,7 @@ ok As from ERTS 5.6.1, the return value is a list of {instance, InstanceNo, InstanceInfo} tuples, where InstanceInfo contains information about - a specific instance of the allocator. As from - ERTS 5.10.4, the returned list when calling - erlang:system_info({allocator, mseg_alloc}) also - includes an {erts_mmap, _} tuple as one element - in the list. If Alloc is not a + a specific instance of the allocator. If Alloc is not a recognized allocator, undefined is returned. If Alloc is disabled, false is returned.

@@ -6855,7 +6851,13 @@ ok briefly documented.

The recognized allocators are listed in erts_alloc(3). - After reading the erts_alloc(3) documentation, + Information about super carriers can be obtained from + ERTS 8.0 with {allocator, erts_mmap} or from + ERTS 5.10.4, the returned list when calling with + {allocator, mseg_alloc} also includes an + {erts_mmap, _} tuple as one element in the list.

+ +

After reading the erts_alloc(3) documentation, the returned information more or less speaks for itself, but it can be worth explaining some things. Call counts are presented by two diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index d04977b9ae..5b76f34165 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -137,6 +137,11 @@ static ErtsAllocatorState_t exec_alloc_state; #endif static ErtsAllocatorState_t test_alloc_state; +#define ERTS_ALC_INFO_A_ALLOC_UTIL (ERTS_ALC_A_MAX + 1) +#define ERTS_ALC_INFO_A_MSEG_ALLOC (ERTS_ALC_A_MAX + 2) +#define ERTS_ALC_INFO_A_ERTS_MMAP (ERTS_ALC_A_MAX + 3) +#define ERTS_ALC_INFO_A_MAX ERTS_ALC_INFO_A_ERTS_MMAP + typedef struct { erts_smp_atomic32_t refc; int only_sz; @@ -145,13 +150,9 @@ typedef struct { Process *proc; Eterm ref; Eterm ref_heap[REF_THING_SIZE]; - int allocs[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+1+2]; + int allocs[ERTS_ALC_INFO_A_MAX - ERTS_ALC_A_MIN + 1 + 1]; } ErtsAllocInfoReq; -#define ERTS_ALC_INFO_A_ALLOC_UTIL (ERTS_ALC_A_MAX + 1) -#define ERTS_ALC_INFO_A_MSEG_ALLOC (ERTS_ALC_A_MAX + 2) -#define ERTS_ALC_INFO_A_MAX ERTS_ALC_INFO_A_MSEG_ALLOC - ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(aireq, ErtsAllocInfoReq, 5, @@ -2842,8 +2843,16 @@ erts_allocator_info(int to, void *arg) erts_print(to, arg, "=allocator:mseg_alloc[%d]\n", i); erts_mseg_info(i, &to, arg, 0, NULL, NULL); } - erts_print(to, arg, "=allocator:mseg_alloc.erts_mmap\n"); + erts_print(to, arg, "=allocator:erts_mmap.default_mmap\n"); erts_mmap_info(&erts_dflt_mmapper, &to, arg, NULL, NULL, &emis); +#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) + erts_print(to, arg, "=allocator:erts_mmap.literal_mmap\n"); + erts_mmap_info(&erts_literal_mmapper, &to, arg, NULL, NULL, &emis); +#endif +#ifdef ERTS_ALC_A_EXEC + erts_print(to, arg, "=allocator:erts_mmap.exec_mmap\n"); + erts_mmap_info(&erts_exec_mmapper, &to, arg, NULL, NULL, &emis); +#endif } #endif @@ -3075,7 +3084,13 @@ reply_alloc_info(void *vair) Uint sz, *szp; ErlOffHeap *ohp = NULL; ErtsMessage *mp = NULL; - struct erts_mmap_info_struct emis; + struct erts_mmap_info_struct mmap_info_dflt; +#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) + struct erts_mmap_info_struct mmap_info_literal; +#endif +#ifdef ERTS_ALC_A_EXEC + struct erts_mmap_info_struct mmap_info_exec; +#endif int i; Eterm (*info_func)(Allctr_t *, int, @@ -3181,6 +3196,39 @@ reply_alloc_info(void *vair) make_small(0), ainfo); break; + case ERTS_ALC_INFO_A_ERTS_MMAP: + alloc_atom = erts_bld_atom(hpp, szp, "erts_mmap"); + + ainfo = (air->only_sz ? NIL : + erts_mmap_info(&erts_dflt_mmapper, NULL, NULL, + hpp, szp, &mmap_info_dflt)); + ainfo = erts_bld_tuple3(hpp, szp, + alloc_atom, + erts_bld_atom(hpp,szp,"default_mmap"), + ainfo); +#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) + ai_list = erts_bld_cons(hpp, szp, + ainfo, ai_list); + ainfo = (air->only_sz ? NIL : + erts_mmap_info(&erts_literal_mmapper, NULL, NULL, + hpp, szp, &mmap_info_literal)); + ainfo = erts_bld_tuple3(hpp, szp, + alloc_atom, + erts_bld_atom(hpp,szp,"literal_mmap"), + ainfo); +#endif +#ifdef ERTS_ALC_A_EXEC + ai_list = erts_bld_cons(hpp, szp, + ainfo, ai_list); + ainfo = (air->only_sz ? NIL : + erts_mmap_info(&erts_exec_mmapper, NULL, NULL, + hpp, szp, &mmap_info_exec)); + ainfo = erts_bld_tuple3(hpp, szp, + alloc_atom, + erts_bld_atom(hpp,szp,"exec_mmap"), + ainfo); +#endif + break; case ERTS_ALC_INFO_A_MSEG_ALLOC: alloc_atom = erts_bld_atom(hpp, szp, "mseg_alloc"); #if HAVE_ERTS_MSEG @@ -3193,14 +3241,6 @@ reply_alloc_info(void *vair) make_small(0), ainfo); - ai_list = erts_bld_cons(hpp, szp, - ainfo, ai_list); - ainfo = (air->only_sz ? NIL : - erts_mmap_info(&erts_dflt_mmapper, NULL, NULL, hpp, szp, &emis)); - ainfo = erts_bld_tuple3(hpp, szp, - alloc_atom, - erts_bld_atom(hpp,szp,"erts_mmap"), - ainfo); #else ainfo = erts_bld_tuple2(hpp, szp, alloc_atom, am_false); @@ -3232,7 +3272,8 @@ reply_alloc_info(void *vair) } switch (ai) { case ERTS_ALC_A_SYSTEM: - case ERTS_ALC_INFO_A_ALLOC_UTIL: + case ERTS_ALC_INFO_A_ALLOC_UTIL: + case ERTS_ALC_INFO_A_ERTS_MMAP: break; case ERTS_ALC_INFO_A_MSEG_ALLOC: #if HAVE_ERTS_MSEG && defined(ERTS_SMP) @@ -3306,7 +3347,7 @@ erts_request_alloc_info(struct process *c_p, int internal) { ErtsAllocInfoReq *air = aireq_alloc(); - Eterm req_ai[ERTS_ALC_A_MAX+1+2] = {0}; + Eterm req_ai[ERTS_ALC_INFO_A_MAX+1] = {0}; Eterm alist; Eterm *hp; int airix = 0, ai; @@ -3342,6 +3383,10 @@ erts_request_alloc_info(struct process *c_p, ai = ERTS_ALC_INFO_A_MSEG_ALLOC; goto save_alloc; } + if (erts_is_atom_str("erts_mmap", alloc, 0)) { + ai = ERTS_ALC_INFO_A_ERTS_MMAP; + goto save_alloc; + } if (erts_is_atom_str("alloc_util", alloc, 0)) { ai = ERTS_ALC_INFO_A_ALLOC_UTIL; save_alloc: diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 5fb560d1ec..1f7b499dcb 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -102,8 +102,8 @@ erts_mmap_do(Config, SCO, SCRPM, SCRFSD) -> Self = self(), Ref = make_ref(), F = fun() -> - SI = erlang:system_info({allocator,mseg_alloc}), - {erts_mmap,EM} = lists:keyfind(erts_mmap, 1, SI), + SI = erlang:system_info({allocator,erts_mmap}), + {default_mmap,EM} = lists:keyfind(default_mmap, 1, SI), {supercarrier,SC} = lists:keyfind(supercarrier, 1, EM), {sizes,Sizes} = lists:keyfind(sizes, 1, SC), {free_segs,Segs} = lists:keyfind(free_segs,1,SC), -- cgit v1.2.3 From 5b90345fe0e31c7d97ae41de1c53871c5815f126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 21 Apr 2016 15:18:16 +0200 Subject: erts: Fix lock-order trace status --- erts/emulator/beam/erl_process.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 1a66044627..d485affa3b 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -10932,9 +10932,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). INITIALIZE_SHCOPY(info); #endif -#ifdef ERTS_SMP erts_smp_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR); -#endif /* * Check for errors. @@ -11235,6 +11233,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). * Schedule process for execution. */ + erts_smp_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR); + schedule_process(p, state, 0); VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->common.id)); @@ -11248,6 +11248,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). DTRACE2(process_spawn, process_name, mfa); } #endif + return res; error: -- cgit v1.2.3 From 98b8650d22e94a5ff839170833f691294f6276d0 Mon Sep 17 00:00:00 2001 From: Alexey Lebedeff Date: Thu, 21 Apr 2016 18:11:58 +0300 Subject: Fix program paths used in build process Not every OS has '/bin/rm' or '/bin/pwd' at exactly that location. In some places in configure scripts result of AC_PATH_PROG was already correctly used, this patch makes this usage more consistent. As for `/bin/pwd` in `otp_build`, shell built-in one is already used in mingw parts - so it should cause no harm to use it everywhere. Difference is only in symlinks resolution - non-builtin `pwd` always returns absolute path, and builtin-one could take into account what sequence of user actions lead to current value of $PWD. --- erts/configure.in | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 4ade3b3086..751dbac249 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -62,9 +62,6 @@ if test x"${ERL_TOP}/erts" != x"$srcdir"; then fi erl_top=${ERL_TOP} -# Remove old configuration information -/bin/rm -f "$ERL_TOP/erts/CONF_INFO" - # echo XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # echo X # echo "X srcdir = $srcdir" @@ -796,6 +793,27 @@ esac AC_SUBST(LIBCARBON) + +_search_path=/bin:/usr/bin:/usr/local/bin:$PATH + +AC_PATH_PROG(RM, rm, false, $_search_path) +if test "$ac_cv_path_RM" = false; then + AC_MSG_ERROR([No 'rm' command found]) +fi + +AC_PATH_PROG(MKDIR, mkdir, false, $_search_path) +if test "$ac_cv_path_MKDIR" = false; then + AC_MSG_ERROR([No 'mkdir' command found]) +fi + +_search_path= + + +# Remove old configuration information. +# Next line should be placed after AC_PATH_PROG(RM, ...), but before +# first output to CONN_INFO. So this is just the right place. +$RM -f "$ERL_TOP/erts/CONF_INFO" + dnl Check if we should/can build a halfword emulator AC_MSG_CHECKING(if we are building a halfword emulator (32bit heap on 64bit machine)) @@ -845,27 +863,13 @@ if test "$ac_cv_prog_AR" = false; then AC_MSG_ERROR([No 'ar' command found in PATH]) fi -_search_path=/bin:/usr/bin:/usr/local/bin:$PATH - -AC_PATH_PROG(RM, rm, false, $_search_path) -if test "$ac_cv_path_RM" = false; then - AC_MSG_ERROR([No 'rm' command found]) -fi - -AC_PATH_PROG(MKDIR, mkdir, false, $_search_path) -if test "$ac_cv_path_MKDIR" = false; then - AC_MSG_ERROR([No 'mkdir' command found]) -fi - -_search_path= - # # Get programs needed for building the documentation # ## Delete previous failed configure results if test -f doc/CONF_INFO; then - rm doc/CONF_INFO + $RM doc/CONF_INFO fi AC_CHECK_PROGS(XSLTPROC, xsltproc) @@ -3967,7 +3971,7 @@ if test "$enable_dtrace_test" = "yes" ; then [$RM -f $DTRACE_2STEP_TEST dtrace -G $DTRACE_CPP $DTRACE_BITS_FLAG -Iemulator/beam -o $DTRACE_2STEP_TEST -s emulator/beam/erlang_dtrace.d conftest.$OBJEXT 2>&AS_MESSAGE_LOG_FD if test -f $DTRACE_2STEP_TEST; then - rm $DTRACE_2STEP_TEST + $RM $DTRACE_2STEP_TEST DTRACE_ENABLED_2STEP=yes fi], []) @@ -4143,7 +4147,7 @@ ssl_done=yes # Default only one run # Remove all SKIP files from previous runs for a in ssl crypto ssh; do - /bin/rm -f $ERL_TOP/lib/$a/SKIP + $RM -f $ERL_TOP/lib/$a/SKIP done SSL_DYNAMIC_ONLY=$enable_dynamic_ssl @@ -4740,7 +4744,7 @@ need_java="jinterface ic/java_src" # Remove all SKIP files from previous runs for a in $need_java ; do - /bin/rm -f $ERL_TOP/lib/$a/SKIP + $RM -f $ERL_TOP/lib/$a/SKIP done if test "X$with_javac" = "Xno"; then @@ -4791,7 +4795,7 @@ dnl this deliberately does not believe that 'gcc' is a C++ compiler AC_CHECK_TOOLS(CXX, [$CCC c++ g++ CC cxx cc++ cl], false) # Remove SKIP file from previous run -/bin/rm -f $ERL_TOP/lib/orber/SKIP +$RM -f $ERL_TOP/lib/orber/SKIP if test "$CXX" = false; then echo "No C++ compiler found" > $ERL_TOP/lib/orber/SKIP -- cgit v1.2.3 From 10cff76885554ab11ebfef2ef83cee9ec7fe410a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 21 Apr 2016 19:55:34 +0200 Subject: erts: Fix copy share with onheap messages --- erts/emulator/beam/erl_gc.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index f33ade27f3..4698458521 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2053,8 +2053,26 @@ copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap, *hp++ = val; break; case TAG_PRIMARY_LIST: +#ifdef SHCOPY_SEND + if (erts_is_literal(val,list_val(val))) { + *hp++ = val; + } else { + *hp++ = offset_ptr(val, offs); + } +#else + *hp++ = offset_ptr(val, offs); +#endif + break; case TAG_PRIMARY_BOXED: - *hp++ = offset_ptr(val, offs); +#ifdef SHCOPY_SEND + if (erts_is_literal(val,boxed_val(val))) { + *hp++ = val; + } else { + *hp++ = offset_ptr(val, offs); + } +#else + *hp++ = offset_ptr(val, offs); +#endif break; case TAG_PRIMARY_HEADER: *hp++ = val; -- cgit v1.2.3 From 6d1f0b6ac13e2a8615a1d92238892196daa4ee4e Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Thu, 31 Mar 2016 15:34:37 +0200 Subject: Windows: Skip tests that requires admin privileges Windows 8 and later have stronger admin checks which requires erlang to run in privileged shells, ignore for now. --- erts/test/nt_SUITE.erl | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/test/nt_SUITE.erl b/erts/test/nt_SUITE.erl index f798a40a6c..624e5484ba 100644 --- a/erts/test/nt_SUITE.erl +++ b/erts/test/nt_SUITE.erl @@ -37,13 +37,13 @@ suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 3}}]. -all() -> - case os:type() of - {win32, nt} -> - [nt, service_basic, service_env, user_env, synced, - service_prio, logout, debug, restart, restart_always, - stopaction]; - _ -> [nt] +all() -> + case {os:type(), os:version()} of + {{win32, nt}, Vsn} when Vsn =< {6,1,999999} -> + [nt, service_basic, service_env, user_env, synced, + service_prio, logout, debug, restart, restart_always, + stopaction]; + _ -> [nt] end. init_per_testcase(_Func, Config) -> @@ -367,11 +367,13 @@ stopaction(Config) when is_list(Config) -> %%% other platforms than NT. nt(Config) when is_list(Config) -> - case os:type() of - {win32,nt} -> - nt_run(); - _ -> - {skipped, "This test case is intended for Win NT only."} + case {os:type(), os:version()} of + {{win32, nt}, Vsn} when Vsn =< {6,1,999999} -> + nt_run(); + {{win32, nt}, _} -> + {skipped, "This test case requires admin privileges on Win 8 and later."}; + _ -> + {skipped, "This test case is intended for Win NT only."} end. -- cgit v1.2.3 From ee1cd28701589cd20f240941957f9a2fb7aadd3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 22 Apr 2016 11:52:41 +0200 Subject: erts: Fix comments --- erts/emulator/beam/beam_bif_load.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index b10250dc49..87508dcf5f 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -832,7 +832,7 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp) /* * Message queue can contains funs, but (at least currently) no - * constants. If we got references to this module from the message + * literals. If we got references to this module from the message * queue, a GC cannot remove these... */ @@ -853,7 +853,7 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp) for (; hfrag; hfrag = hfrag->next) { if (check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size)) return am_true; - /* Should not contain any constants... */ + /* Should not contain any literals... */ ASSERT(!any_heap_refs(&hfrag->mem[0], &hfrag->mem[hfrag->used_size], literals, @@ -908,7 +908,7 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp) #ifdef DEBUG /* * Message buffer fragments should not have any references - * to constants, and off heap lists should already have + * to literals, and off heap lists should already have * been moved into process off heap structure. */ for (msgp = rp->msg_frag; msgp; msgp = msgp->next) { @@ -945,7 +945,7 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp) need_gc &= ~done_gc; /* - * Try to get rid of constants by by garbage collecting. + * Try to get rid of literals by by garbage collecting. * Clear both fvalue and ftrace. */ -- cgit v1.2.3 From 65bd8ade865eebe0d8a3c3210a4e2e9f334e229f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sun, 10 Apr 2016 22:48:55 +0200 Subject: erts: Add BIF maps:take/2 --- erts/emulator/beam/bif.tab | 2 ++ erts/emulator/beam/erl_map.c | 75 +++++++++++++++++++++++++++++++++----------- erts/emulator/beam/erl_map.h | 1 + erts/emulator/beam/erl_nif.c | 5 ++- 4 files changed, 62 insertions(+), 21 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 58cd31cee9..872f0f9b2a 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -652,6 +652,8 @@ bif erts_debug:size_shared/1 bif erts_debug:copy_shared/1 bif erlang:has_prepared_code_on_load/1 +bif maps:take/2 + # # Obsolete # diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 03a96cb00a..8efc983f04 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -54,6 +54,7 @@ * - maps:new/0 * - maps:put/3 * - maps:remove/2 + * - maps:take/2 * - maps:to_list/1 * - maps:update/3 * - maps:values/1 @@ -93,7 +94,7 @@ static Uint hashmap_subtree_size(Eterm node); static Eterm hashmap_to_list(Process *p, Eterm map); static Eterm hashmap_keys(Process *p, Eterm map); static Eterm hashmap_values(Process *p, Eterm map); -static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node); +static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node, Eterm *value); static Eterm flatmap_from_validated_list(Process *p, Eterm list, Uint size); static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size); static Eterm hashmap_from_unsorted_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int reject_dupkeys); @@ -1521,10 +1522,45 @@ BIF_RETTYPE maps_put_3(BIF_ALIST_3) { BIF_ERROR(BIF_P, BADMAP); } -/* maps:remove/3 */ +/* maps:take/2 */ -int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { +BIF_RETTYPE maps_take_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm res, map, val; + if (erts_maps_take(BIF_P, BIF_ARG_1, BIF_ARG_2, &map, &val)) { + Eterm *hp = HAlloc(BIF_P, 3); + res = make_tuple(hp); + *hp++ = make_arityval(2); + *hp++ = val; + *hp++ = map; + BIF_RET(res); + } + BIF_RET(am_error); + } + BIF_P->fvalue = BIF_ARG_2; + BIF_ERROR(BIF_P, BADMAP); +} + +/* maps:remove/2 */ + +BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm res; + (void) erts_maps_take(BIF_P, BIF_ARG_1, BIF_ARG_2, &res, NULL); + BIF_RET(res); + } + BIF_P->fvalue = BIF_ARG_2; + BIF_ERROR(BIF_P, BADMAP); +} + +/* erts_maps_take + * return 1 if key is found, otherwise 0 + * If the key is not found res (output map) will be map (input map) + */ +int erts_maps_take(Process *p, Eterm key, Eterm map, + Eterm *res, Eterm *value) { Uint32 hx; + Eterm ret; if (is_flatmap(map)) { Sint n; Uint need; @@ -1537,7 +1573,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { if (n == 0) { *res = map; - return 1; + return 0; } ks = flatmap_get_keys(mp); @@ -1564,6 +1600,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { if (is_immed(key)) { while (1) { if (*ks == key) { + if (value) *value = *vs; goto found_key; } else if (--n) { *mhp++ = *vs++; @@ -1574,6 +1611,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { } else { while(1) { if (EQ(*ks, key)) { + if (value) *value = *vs; goto found_key; } else if (--n) { *mhp++ = *vs++; @@ -1589,7 +1627,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { HRelease(p, hp_start + need, hp_start); *res = map; - return 1; + return 0; found_key: /* Copy rest of keys and values */ @@ -1601,19 +1639,13 @@ found_key: } ASSERT(is_hashmap(map)); hx = hashmap_make_hash(key); - *res = hashmap_delete(p, hx, key, map); - return 1; -} - -BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { - if (is_map(BIF_ARG_2)) { - Eterm res; - if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) { - BIF_RET(res); - } + ret = hashmap_delete(p, hx, key, map, value); + if (is_value(ret)) { + *res = ret; + return 1; } - BIF_P->fvalue = BIF_ARG_2; - BIF_ERROR(BIF_P, BADMAP); + *res = map; + return 0; } int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) { @@ -2322,7 +2354,8 @@ static Eterm hashmap_values(Process* p, Eterm node) { return res; } -static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) { +static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, + Eterm map, Eterm *value) { Eterm *hp = NULL, *nhp = NULL, *hp_end = NULL; Eterm *ptr; Eterm hdr, res = map, node = map; @@ -2337,8 +2370,12 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) { switch(primary_tag(node)) { case TAG_PRIMARY_LIST: if (EQ(CAR(list_val(node)), key)) { + if (value) { + *value = CDR(list_val(node)); + } goto unroll; } + res = THE_NON_VALUE; goto not_found; case TAG_PRIMARY_BOXED: ptr = boxed_val(node); @@ -2365,6 +2402,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) { n = hashmap_bitcount(hval); } else { /* not occupied */ + res = THE_NON_VALUE; goto not_found; } @@ -2394,6 +2432,7 @@ static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) { break; } /* not occupied */ + res = THE_NON_VALUE; goto not_found; default: erts_exit(ERTS_ERROR_EXIT, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 7af9100906..8b5c9582ba 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -82,6 +82,7 @@ struct ErtsEStack_; Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map); int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res); int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res); +int erts_maps_take(Process *p, Eterm key, Eterm map, Eterm *res, Eterm *value); Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node, int is_update); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 73c0eb8eba..24e72cc949 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -2443,14 +2443,13 @@ int enif_make_map_remove(ErlNifEnv* env, Eterm key, Eterm *map_out) { - int res; if (!is_map(map_in)) { return 0; } flush_env(env); - res = erts_maps_remove(env->proc, key, map_in, map_out); + (void) erts_maps_take(env->proc, key, map_in, map_out, NULL); cache_env(env); - return res; + return 1; } int enif_map_iterator_create(ErlNifEnv *env, -- cgit v1.2.3 From 43416d57174d93660aec1620eeef0b05271b43e6 Mon Sep 17 00:00:00 2001 From: Simon Cornish <7t9jna402@sneakemail.com> Date: Mon, 25 Apr 2016 22:25:56 -0700 Subject: Print heap pointers for garbing processes during crashdump In an SMP emulator, print_garb_info doesn't do anything because the heap pointers could be invalid. However, when crashdumping there are no schedulers running so it's ok to print this additional information which could be useful when examining a core file. --- erts/emulator/beam/break.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 0ddf7f4e6d..e0164c3c73 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -347,8 +347,11 @@ print_process_info(int to, void *to_arg, Process *p) static void print_garb_info(int to, void *to_arg, Process* p) { +#ifdef ERTS_SMP /* ERTS_SMP: A scheduler is probably concurrently doing gc... */ -#ifndef ERTS_SMP + if (!ERTS_IS_CRASH_DUMPING) + return; +#endif erts_print(to, to_arg, "New heap start: %bpX\n", p->heap); erts_print(to, to_arg, "New heap top: %bpX\n", p->htop); erts_print(to, to_arg, "Stack top: %bpX\n", p->stop); @@ -356,7 +359,6 @@ print_garb_info(int to, void *to_arg, Process* p) erts_print(to, to_arg, "Old heap start: %bpX\n", OLD_HEAP(p)); erts_print(to, to_arg, "Old heap top: %bpX\n", OLD_HTOP(p)); erts_print(to, to_arg, "Old heap end: %bpX\n", OLD_HEND(p)); -#endif } void -- cgit v1.2.3 From 0127f930e6aa2abc95ec4a95c9d5638d7e9bd512 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 17 Apr 2016 20:02:28 +0200 Subject: matherr() must not fake an FP exception When FP exceptions are used, matherr() forces a fake FP exception, which is interpreted as an error by the checking code after a math routine call. This is wrong for several reasons: - it's not necessary for error checking: when FP exceptions aren't used, matherr() is a stub and no information is derived from it being called - it's not necessary for FPU maintenance: the FPU only needs to be reprogrammed after an actual FP exception - it causes false negatives: matherr() may be called even though Erlang doesn't consider the case to be an error (exp() and pow() underflows on Solaris for instance); with FP exceptions enabled they are incorrectly considered errors The fix is to remove all FP exception related code from matherr(). --- erts/emulator/sys/unix/sys_float.c | 5 ----- erts/emulator/sys/win32/sys_float.c | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c index 60661d9016..9d2b25a787 100644 --- a/erts/emulator/sys/unix/sys_float.c +++ b/erts/emulator/sys/unix/sys_float.c @@ -818,11 +818,6 @@ sys_chars_to_double(char* buf, double* fp) int matherr(struct exception *exc) { -#if !defined(NO_FPE_SIGNALS) - volatile unsigned long *fpexnp = erts_get_current_fp_exception(); - if (fpexnp != NULL) - *fpexnp = (unsigned long)__builtin_return_address(0); -#endif return 1; } diff --git a/erts/emulator/sys/win32/sys_float.c b/erts/emulator/sys/win32/sys_float.c index a2b9bd1263..2b2d6ab7d3 100644 --- a/erts/emulator/sys/win32/sys_float.c +++ b/erts/emulator/sys/win32/sys_float.c @@ -139,8 +139,7 @@ sys_double_to_chars_ext(double fp, char *buffer, size_t buffer_size, size_t deci int matherr(struct _exception *exc) { - erl_fp_exception = 1; - DEBUGF(("FP exception (matherr) (0x%x) (%d)\n", exc->type, erl_fp_exception)); + DEBUGF(("FP exception (matherr) (0x%x)\n", exc->type)); return 1; } -- cgit v1.2.3 From 6aae129123c4ce8988cc67b3545db9d1ec51324b Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 22 Apr 2016 18:45:30 +0200 Subject: erts: Rename erl flag +xmqd to +hmqd Flags that control the heap should all fall under the +h flag --- erts/doc/src/erl.xml | 24 +++++-------- erts/doc/src/erlang.xml | 10 +++--- erts/emulator/beam/erl_init.c | 48 +++++++++++-------------- erts/emulator/test/message_queue_data_SUITE.erl | 6 ++-- 4 files changed, 38 insertions(+), 50 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index e13470c83c..c499fc8081 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -632,6 +632,15 @@

Sets the initial process dictionary size of processes to the size .

+ +hmqd off_heap|on_heap|mixed +

+ Sets the default value for the process flag + message_queue_data. If +hmqd is not + passed, mixed will be the default. For more information, + see the documentation of + process_flag(message_queue_data, + MQD). +

Enables or disables the kernel poll functionality if @@ -1361,21 +1370,6 @@ error_logger(3) for further information.

- - -

Default process flag settings.

- - +xmqd off_heap|on_heap|mixed -

- Sets the default value for the process flag - message_queue_data. If +xmqd is not - passed, mixed will be the default. For more information, - see the documentation of - process_flag(message_queue_data, - MQD). -

-
-

Miscellaneous flags.

diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 423ccdf98f..b2baf5133c 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4371,7 +4371,7 @@ os_prompt%

The default message_queue_data process flag is determined - by the +xmqd + by the +hmqd erl command line argument.

@@ -4878,7 +4878,9 @@ os_prompt% {total_heap_size, Size}

Size is the total size, in words, of all heap - fragments of the process. This includes the process stack.

+ fragments of the process. This includes the process stack and + any unreceived messages that are considered to be part of the + heap.

{trace, InternalTraceFlags} @@ -5709,7 +5711,7 @@ true flag. MQD should be either off_heap, on_heap, or mixed. The default message_queue_data process flag is determined by the - +xmqd erl + +hmqd erl command line argument. For more information, see the documentation of process_flag(message_queue_data, @@ -7388,7 +7390,7 @@ ok

Returns the default value of the message_queue_data process flag which is either off_heap, on_heap, or mixed. This default is set by the erl command line argument - +xmqd. For more information on the + +hmqd. For more information on the message_queue_data process flag, see documentation of process_flag(message_queue_data, MQD).

diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index dec9bdfedc..2fd1eeb785 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -578,6 +578,8 @@ void erts_usage(void) VH_DEFAULT_SIZE); erts_fprintf(stderr, "-hpds size initial process dictionary size (default %d)\n", erts_pd_initial_size); + erts_fprintf(stderr, "-hmqd val set default message queue data flag for processes,\n"); + erts_fprintf(stderr, " valid values are: off_heap | on_heap | mixed\n"); /* erts_fprintf(stderr, "-i module set the boot module (default init)\n"); */ @@ -655,8 +657,6 @@ void erts_usage(void) erts_fprintf(stderr, "-W set error logger warnings mapping,\n"); erts_fprintf(stderr, " see error_logger documentation for details\n"); - erts_fprintf(stderr, "-xmqd val set default message queue data flag for processes,\n"); - erts_fprintf(stderr, " valid values are: off_heap | on_heap | mixed\n"); erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n"); erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024); erts_fprintf(stderr, "-zdntgc time set delayed node table gc in seconds\n"); @@ -1487,6 +1487,7 @@ erl_start(int argc, char **argv) * h|ms - min_heap_size * h|mbs - min_bin_vheap_size * h|pds - erts_pd_initial_size + * h|mqd - message_queue_data * */ if (has_prefix("mbs", sub_param)) { @@ -1512,6 +1513,23 @@ erl_start(int argc, char **argv) } VERBOSE(DEBUG_SYSTEM, ("using initial process dictionary size %d\n", erts_pd_initial_size)); + } else if (has_prefix("mqd", sub_param)) { + arg = get_arg(sub_param+3, argv[i+1], &i); + if (sys_strcmp(arg, "mixed") == 0) + erts_default_spo_flags &= ~(SPO_ON_HEAP_MSGQ|SPO_OFF_HEAP_MSGQ); + else if (sys_strcmp(arg, "on_heap") == 0) { + erts_default_spo_flags &= ~SPO_OFF_HEAP_MSGQ; + erts_default_spo_flags |= SPO_ON_HEAP_MSGQ; + } + else if (sys_strcmp(arg, "off_heap") == 0) { + erts_default_spo_flags &= ~SPO_ON_HEAP_MSGQ; + erts_default_spo_flags |= SPO_OFF_HEAP_MSGQ; + } + else { + erts_fprintf(stderr, + "Invalid message_queue_data flag: %s\n", arg); + erts_usage(); + } } else { /* backward compatibility */ arg = get_arg(argv[i]+2, argv[i+1], &i); @@ -2047,32 +2065,6 @@ erl_start(int argc, char **argv) } break; - case 'x': { - char *sub_param = argv[i]+2; - if (has_prefix("mqd", sub_param)) { - arg = get_arg(sub_param+3, argv[i+1], &i); - if (sys_strcmp(arg, "mixed") == 0) - erts_default_spo_flags &= ~(SPO_ON_HEAP_MSGQ|SPO_OFF_HEAP_MSGQ); - else if (sys_strcmp(arg, "on_heap") == 0) { - erts_default_spo_flags &= ~SPO_OFF_HEAP_MSGQ; - erts_default_spo_flags |= SPO_ON_HEAP_MSGQ; - } - else if (sys_strcmp(arg, "off_heap") == 0) { - erts_default_spo_flags &= ~SPO_ON_HEAP_MSGQ; - erts_default_spo_flags |= SPO_OFF_HEAP_MSGQ; - } - else { - erts_fprintf(stderr, - "Invalid message_queue_data flag: %s\n", arg); - erts_usage(); - } - } else { - erts_fprintf(stderr, "bad -x option %s\n", argv[i]); - erts_usage(); - } - break; - } - case 'z': { char *sub_param = argv[i]+2; diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl index 6efca5b39e..37523d68e1 100644 --- a/erts/emulator/test/message_queue_data_SUITE.erl +++ b/erts/emulator/test/message_queue_data_SUITE.erl @@ -44,15 +44,15 @@ basic(Config) when is_list(Config) -> basic_test(erlang:system_info(message_queue_data)), - {ok, Node1} = start_node(Config, "+xmqd off_heap"), + {ok, Node1} = start_node(Config, "+hmqd off_heap"), ok = rpc:call(Node1, ?MODULE, basic_test, [off_heap]), stop_node(Node1), - {ok, Node2} = start_node(Config, "+xmqd on_heap"), + {ok, Node2} = start_node(Config, "+hmqd on_heap"), ok = rpc:call(Node2, ?MODULE, basic_test, [on_heap]), stop_node(Node2), - {ok, Node3} = start_node(Config, "+xmqd mixed"), + {ok, Node3} = start_node(Config, "+hmqd mixed"), ok = rpc:call(Node3, ?MODULE, basic_test, [mixed]), stop_node(Node3), -- cgit v1.2.3 From d0bca6c9aceb6b5be35342387d6bbbf0f3c5244c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 26 Apr 2016 15:19:56 +0200 Subject: erts: Fix total_heap_size calculation for on_heap --- erts/emulator/beam/erl_bif_info.c | 7 ++++--- erts/emulator/beam/erl_gc.c | 20 +++++++++++++++++++ erts/emulator/test/message_queue_data_SUITE.erl | 26 +++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index c9741b361f..99fe847ba2 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1359,9 +1359,10 @@ process_info_aux(Process *BIF_P, total_heap_size += rp->mbuf_sz; - for (mp = rp->msg.first; mp; mp = mp->next) - if (mp->data.attached) - total_heap_size += erts_msg_attached_data_size(mp); + if (rp->flags & F_ON_HEAP_MSGQ) + for (mp = rp->msg.first; mp; mp = mp->next) + if (mp->data.attached) + total_heap_size += erts_msg_attached_data_size(mp); (void) erts_bld_uint(NULL, &hsz, total_heap_size); hp = HAlloc(BIF_P, hsz); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 4698458521..881876ba59 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -3006,10 +3006,30 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp) }; Eterm res = THE_NON_VALUE; + ErtsMessage *mp; ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(*tags)); ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == ERTS_PROCESS_GC_INFO_MAX_TERMS); + if (p->abandoned_heap) { + Eterm *htop, *heap; + ERTS_GET_ORIG_HEAP(p, heap, htop); + values[3] = HIGH_WATER(p) - heap; + values[6] = htop - heap; + } + + if (p->flags & F_ON_HEAP_MSGQ) { + /* If on heap messages in the internal queue are counted + as being part of the heap, so we have to add them to the + am_mbuf_size value. process_info(total_heap_size) should + be the same as adding old_heap_block_size + heap_block_size + + mbuf_size. + */ + for (mp = p->msg.first; mp; mp = mp->next) + if (mp->data.attached) + values[2] += erts_msg_attached_data_size(mp); + } + res = erts_bld_atom_uword_2tup_list(hpp, sizep, sizeof(values)/sizeof(*values), diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl index 37523d68e1..226462676c 100644 --- a/erts/emulator/test/message_queue_data_SUITE.erl +++ b/erts/emulator/test/message_queue_data_SUITE.erl @@ -21,7 +21,7 @@ -module(message_queue_data_SUITE). -export([all/0, suite/0]). --export([basic/1, process_info_messages/1]). +-export([basic/1, process_info_messages/1, total_heap_size/1]). -export([basic_test/1]). @@ -32,7 +32,7 @@ suite() -> {timetrap, {minutes, 2}}]. all() -> - [basic, process_info_messages]. + [basic, process_info_messages, total_heap_size]. %% %% @@ -190,6 +190,28 @@ process_info_messages(Config) when is_list(Config) -> ok. +total_heap_size(_Config) -> + + Fun = fun F() -> receive Pid when is_pid(Pid) -> Pid ! ok,F() end end, + + %% Test that on_heap messages grow the heap even if they are not received + OnPid = spawn_opt(Fun, [{message_queue_data, on_heap}]), + {total_heap_size, OnSize} = erlang:process_info(OnPid, total_heap_size), + [OnPid ! lists:duplicate(N,N) || N <- lists:seq(1,100)], + OnPid ! self(), receive ok -> ok end, + {total_heap_size, OnSizeAfter} = erlang:process_info(OnPid, total_heap_size), + ct:log("OnSize = ~p, OnSizeAfter = ~p",[OnSize, OnSizeAfter]), + true = OnSize < OnSizeAfter, + + %% Test that off_heap messages do not grow the heap if they are not received + OffPid = spawn_opt(Fun, [{message_queue_data, off_heap}]), + {total_heap_size, OffSize} = erlang:process_info(OffPid, total_heap_size), + [OffPid ! lists:duplicate(N,N) || N <- lists:seq(1,100)], + OffPid ! self(), receive ok -> ok end, + {total_heap_size, OffSizeAfter} = erlang:process_info(OffPid, total_heap_size), + ct:log("OffSize = ~p, OffSizeAfter = ~p",[OffSize, OffSizeAfter]), + true = OffSize == OffSizeAfter. + %% %% %% helpers -- cgit v1.2.3 From c922f2729dcf2bc5ef571f9d77efea75029469de Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 26 Apr 2016 15:56:21 +0200 Subject: Yield after setting sensitive for save_calls --- erts/emulator/beam/bif.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index ed5b2983dd..c38d031ab8 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1736,7 +1736,9 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2) ERTS_TRACE_FLAGS(BIF_P) &= ~F_SENSITIVE; } erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCKS_ALL_MINOR); - BIF_RET(old_value); + /* make sure to bump all reds so that we get + rescheduled immediately so setting takes effect */ + BIF_RET2(old_value, CONTEXT_REDS); } else if (BIF_ARG_1 == am_monitor_nodes) { /* -- cgit v1.2.3 From 115476cea0db80bb8d53b1a2f08c3b64d62df4ff Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 19 Apr 2016 11:23:39 +0200 Subject: erts: Fix broken doc link to erl_tracer --- erts/doc/src/erlang.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 423ccdf98f..1daa582584 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -8637,8 +8637,8 @@ timestamp() ->

Specifies that a tracer module should be called instead of sending a trace message. The tracer module can then ignore or change the trace message. For more details - on how to write a tracer module see - erl_tracer + on how to write a tracer module see + erl_tracer

@@ -8989,7 +8989,7 @@ timestamp() -> erlang:trace_delivered(Tracee) resides on. The special Tracee atom all denotes all processes that currently are traced in the node.

-

When used together with an +

When used together with an Tracer Module any message sent in the trace callback is guaranteed to have reached it's recipient before the trace_delivered message is sent.

-- cgit v1.2.3 From 071f9bba246684d401260a2729b8d72bbbfa1874 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 19 Apr 2016 11:24:14 +0200 Subject: erts: Remove some dead code --- erts/emulator/beam/beam_bp.h | 12 ------------ erts/emulator/beam/erl_trace.c | 36 +----------------------------------- erts/emulator/beam/utils.c | 35 ----------------------------------- 3 files changed, 1 insertion(+), 82 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index bb171be8a6..08641b86d6 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -173,19 +173,7 @@ void erts_clear_time_trace_bif(BeamInstr *pc); BeamInstr *erts_find_local_func(Eterm mfa[3]); -ERTS_GLB_INLINE Uint erts_bp_sched2ix(void); - #if ERTS_GLB_INLINE_INCL_FUNC_DEF -ERTS_GLB_INLINE Uint erts_bp_sched2ix(void) -{ -#ifdef ERTS_SMP - ErtsSchedulerData *esdp; - esdp = erts_get_scheduler_data(); - return esdp->no - 1; -#else - return 0; -#endif -} extern erts_smp_atomic32_t erts_active_bp_index; extern erts_smp_atomic32_t erts_staging_bp_index; diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index bd88769dfc..ba557ab20e 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -629,6 +629,7 @@ do { \ # define GET_NOW(m, s, u) do {get_now(m, s, u);} while (0) #endif + static void write_sys_msg_to_port(Eterm unused_to, Port* trace_port, @@ -1696,41 +1697,6 @@ profile_scheduler(Eterm scheduler_id, Eterm state) { } -void -profile_scheduler_q(Eterm scheduler_id, Eterm state, Eterm no_schedulers, Uint Ms, Uint s, Uint us) { - Eterm *hp, msg, timestamp; - -#ifndef ERTS_SMP -#define LOCAL_HEAP_SIZE (4 + 7) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); - UseTmpHeapNoproc(LOCAL_HEAP_SIZE); - - hp = local_heap; -#else - ErlHeapFragment *bp; - Uint hsz; - - hsz = 4 + 7; - - bp = new_message_buffer(hsz); - hp = bp->mem; -#endif - - erts_smp_mtx_lock(&smq_mtx); - - timestamp = TUPLE3(hp, make_small(Ms), make_small(s), make_small(us)); hp += 4; - msg = TUPLE6(hp, am_profile, am_scheduler, scheduler_id, state, no_schedulers, timestamp); hp += 7; -#ifndef ERTS_SMP - profile_send(NIL, msg); - UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE); -#undef LOCAL_HEAP_SIZE -#else - enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SYSPROF, NIL, NIL, msg, bp); -#endif - erts_smp_mtx_unlock(&smq_mtx); - -} - /* Port profiling */ void diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index b280995488..68006e7ef3 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -71,41 +71,6 @@ #define HAVE_MALLOPT 0 #endif -/* profile_scheduler mini message queue */ - -typedef struct { - Uint scheduler_id; - Uint no_schedulers; - Uint Ms; - Uint s; - Uint us; - Eterm state; -} profile_sched_msg; - -typedef struct { - profile_sched_msg msg[2]; - Uint n; -} profile_sched_msg_q; - -#ifdef ERTS_SMP - -#if 0 /* Unused */ -static void -dispatch_profile_msg_q(profile_sched_msg_q *psmq) -{ - int i = 0; - profile_sched_msg *msg = NULL; - ASSERT(psmq != NULL); - for (i = 0; i < psmq->n; i++) { - msg = &(psmq->msg[i]); - profile_scheduler_q(make_small(msg->scheduler_id), msg->state, am_undefined, msg->Ms, msg->s, msg->us); - } -} -#endif - -#endif - - Eterm* erts_heap_alloc(Process* p, Uint need, Uint xtra) { -- cgit v1.2.3 From 0547610c0bd1fb0de4352fc8322094722145d9d1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 19 Apr 2016 11:24:35 +0200 Subject: erts: Remove erl_tracer with invalid state --- erts/emulator/nifs/common/erl_tracer_nif.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'erts') diff --git a/erts/emulator/nifs/common/erl_tracer_nif.c b/erts/emulator/nifs/common/erl_tracer_nif.c index a1e0e581a4..b1c8d0b7a1 100644 --- a/erts/emulator/nifs/common/erl_tracer_nif.c +++ b/erts/emulator/nifs/common/erl_tracer_nif.c @@ -124,6 +124,9 @@ static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if (!enif_is_port_alive(env, &to_port)) /* tracer is dead so we should remove this trace point */ return atom_remove; + } else { + /* The state was not a pid or a port */ + return atom_remove; } /* Only generate trace for when tracer != tracee */ -- cgit v1.2.3 From d8371a0598ee7c831e8f096cfd5b0af0a0503474 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 19 Apr 2016 11:26:03 +0200 Subject: erts: Expand trace tests for refc binaries Make sure to cover all of the refc binary cases in tracing --- erts/emulator/beam/erl_trace.c | 2 ++ erts/emulator/test/match_spec_SUITE.erl | 11 +++---- erts/emulator/test/port_trace_SUITE.erl | 34 +++++++++++++++------- .../emulator/test/port_trace_SUITE_data/echo_drv.c | 4 +++ 4 files changed, 34 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index ba557ab20e..32d0ba9f4c 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1860,6 +1860,8 @@ trace_port_receive(Port *t_p, Eterm caller, Eterm what, ...) } data = TUPLE2(hp, caller, data); + hp += 3; + ASSERT(hp <= (local_heap + LOCAL_HEAP_SIZE) || orig_hp); send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, am_receive, data, THE_NON_VALUE); diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index ea973276db..6733237b20 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -597,16 +597,15 @@ ms_trace3(Config) when is_list(Config) -> end), ok. -ms_trace_dead(doc) -> - ["Test that a dead tracer is removed using ms"]; -ms_trace_dead(suite) -> []; -ms_trace_dead(Config) when is_list(Config) -> +%% Test that a dead tracer is removed using ms +ms_trace_dead(_Config) -> Self = self(), TFun = fun F() -> receive M -> Self ! M, F() end end, {Tracer, MRef} = spawn_monitor(TFun), MetaTracer = spawn_link(TFun), erlang:trace_pattern({?MODULE, f1, '_'}, - [{'_',[],[{trace,[], + [{'_',[],[{message, false}, + {trace,[], [call,{const,{tracer,Tracer}}]}]}], [{meta, MetaTracer}]), erlang:trace_pattern({?MODULE, f2, '_'}, []), @@ -623,8 +622,6 @@ ms_trace_dead(Config) when is_list(Config) -> ?MODULE:f2(3,4), TRef = erlang:trace_delivered(all), receive {trace_delivered, _, TRef} -> ok end, - receive {trace_ts, Self, call, {?MODULE, f1, _}, _} -> ok end, - receive {trace_ts, Self, call, {?MODULE, f1, _}, _} -> ok end, receive M -> ct:fail({unexpected, M}) after 10 -> ok end. %% Test that destructive operations in test bif does not really happen diff --git a/erts/emulator/test/port_trace_SUITE.erl b/erts/emulator/test/port_trace_SUITE.erl index 41e8a316c4..bfdea0761b 100644 --- a/erts/emulator/test/port_trace_SUITE.erl +++ b/erts/emulator/test/port_trace_SUITE.erl @@ -240,19 +240,23 @@ command(Config) -> Prt ! {S, {command, <>}}, [{trace, Prt, 'receive', {S, {command, <>}}}] = flush(), + OutputMsg = <>, + Prt ! {S, {command, OutputMsg}}, + [{trace, Prt, 'receive', {S, {command, OutputMsg}}}] = flush(), + close(Prt, Flags), os:putenv("OUTPUTV","true"), reload_drv(Config), Prt2 = erlang:open_port({spawn, echo_drv}, [binary]), - Msg = [<<0:8>>,<<0:(8*512)>>,<<0:(8*256)>>,<<0:8>>], + OutputvMsg = [<<0:8>>,<<0:(8*512)>>,<<0:(8*256)>>,<<0:8>>], - erlang:port_command(Prt2, Msg), - [{trace, Prt2, 'receive', {S, {command, Msg}}}] = flush(), + erlang:port_command(Prt2, OutputvMsg), + [{trace, Prt2, 'receive', {S, {command, OutputvMsg}}}] = flush(), - Prt2 ! {S, {command, Msg}}, - [{trace, Prt2, 'receive', {S, {command, Msg}}}] = flush(), + Prt2 ! {S, {command, OutputvMsg}}, + [{trace, Prt2, 'receive', {S, {command, OutputvMsg}}}] = flush(), close(Prt2, Flags), @@ -274,6 +278,12 @@ control(_Config) -> [{trace, Prt, 'receive', {S, {control, {(1 bsl 32) - 1, <>}}}}, {trace, Prt, send, {Prt, {control, <<0:8>>}}, S}] = flush(), + Msg = <>, + Pat = lists:duplicate(512, 0), + Pat = erlang:port_control(Prt, 1, Msg), + [{trace, Prt, 'receive', {S, {control, {1, Msg}}}}, + {trace, Prt, send, {Prt, {control, <<0:(8*512)>>}}, S}] = flush(), + close(Prt, Flags), ok. @@ -331,12 +341,16 @@ call(_Config) -> Flags = [send,'receive'], {Prt, S} = trace_and_open(Flags,[binary]), - Msg = {hello, world, make_ref()}, - BinMsg = term_to_binary(Msg), + Test = fun(Msg) -> + BinMsg = term_to_binary(Msg), + + Msg = erlang:port_call(Prt, 0, Msg), + [{trace, Prt, 'receive', {S, {call, {0, BinMsg}}}}, + {trace, Prt, send, {Prt, {call, BinMsg}}, S}] = flush() + end, - Msg = erlang:port_call(Prt, 0, Msg), - [{trace, Prt, 'receive', {S, {call, {0, BinMsg}}}}, - {trace, Prt, send, {Prt, {call, BinMsg}}, S}] = flush(), + Test({hello, world, make_ref()}), + Test({hello, world, lists:seq(1,1000)}), close(Prt, Flags), diff --git a/erts/emulator/test/port_trace_SUITE_data/echo_drv.c b/erts/emulator/test/port_trace_SUITE_data/echo_drv.c index b5728bc170..b5ae9389b4 100644 --- a/erts/emulator/test/port_trace_SUITE_data/echo_drv.c +++ b/erts/emulator/test/port_trace_SUITE_data/echo_drv.c @@ -217,6 +217,8 @@ static ErlDrvSSizeT echo_drv_control(ErlDrvData drv_data, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) { + if ((len - 1) > rlen) + *rbuf = driver_alloc(len - 1); memcpy(*rbuf, buf+1, len-1); return len-1; } @@ -232,6 +234,8 @@ static ErlDrvSSizeT echo_drv_call(ErlDrvData drv_data, char **rbuf, ErlDrvSizeT rlen, unsigned int *flags) { + if ((len - command) > rlen) + *rbuf = driver_alloc(len - command); memcpy(*rbuf, buf+command, len-command); return len-command; } -- cgit v1.2.3 From 5c154925e9fb0ecd3555ad91b7c9f11aeb173143 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 19 Apr 2016 11:26:23 +0200 Subject: erts: Add tests for set on link tracing --- erts/emulator/test/trace_SUITE.erl | 49 +++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index b7f312635e..70ead0ecd8 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -32,6 +32,7 @@ suspend_system_limit/1, suspend_opts/1, suspend_waiting/1, new_clear/1, existing_clear/1, set_on_spawn/1, set_on_first_spawn/1, cpu_timestamp/1, + set_on_link/1, set_on_first_link/1, system_monitor_args/1, more_system_monitor_args/1, system_monitor_long_gc_1/1, system_monitor_long_gc_2/1, system_monitor_large_heap_1/1, system_monitor_large_heap_2/1, @@ -54,7 +55,8 @@ all() -> mutual_suspend, suspend_exit, suspender_exit, suspend_system_limit, suspend_opts, suspend_waiting, new_clear, existing_clear, set_on_spawn, - set_on_first_spawn, system_monitor_args, + set_on_first_spawn, set_on_link, set_on_first_link, + system_monitor_args, more_system_monitor_args, system_monitor_long_gc_1, system_monitor_long_gc_2, system_monitor_large_heap_1, system_monitor_long_schedule, @@ -501,6 +503,51 @@ set_on_first_spawn(Config) when is_list(Config) -> receive_nothing(), ok. +%% Tests trace(Pid, How, [set_on_link]). + +set_on_link(Config) -> + Listener = fun_spawn(fun process/0), + + %% Create and trace a process with the set_on_link flag. + %% Make sure it is traced. + Father_SOL = fun_spawn(fun process/0), + 1 = erlang:trace(Father_SOL, true, [send, set_on_link]), + true = is_send_traced(Father_SOL, Listener, sol_father), + + %% Have the process spawn of two children and test that they + %% are traced. + [Child1, Child2] = spawn_children(Father_SOL, 2), + true = is_send_traced(Child1, Listener, child1), + true = is_send_traced(Child2, Listener, child2), + + %% Second generation. + [Child11, Child12] = spawn_children(Child1, 2), + true = is_send_traced(Child11, Listener, child11), + true = is_send_traced(Child12, Listener, child12), + ok. + +%% Tests trace(Pid, How, [set_on_first_spawn]). + +set_on_first_link(Config) -> + ct:timetrap({seconds, 10}), + Listener = fun_spawn(fun process/0), + + %% Create and trace a process with the set_on_first_spawn flag. + %% Make sure it is traced. + Parent = fun_spawn(fun process/0), + 1 = erlang:trace(Parent, true, [send, set_on_first_link]), + is_send_traced(Parent, Listener, sol_father), + + %% Have the process spawn off three children and test that the + %% first is traced. + [Child1, Child2, Child3] = spawn_children(Parent, 3), + true = is_send_traced(Child1, Listener, child1), + false = is_send_traced(Child2, Listener, child2), + false = is_send_traced(Child3, Listener, child3), + receive_nothing(), + ok. + + %% Tests arguments to erlang:system_monitor/0,1,2 system_monitor_args(Config) when is_list(Config) -> -- cgit v1.2.3 From 80512854940a6db33152717246a34bbe68aaa8ed Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 22 Apr 2016 11:32:25 +0200 Subject: erts: Add test for new procs trace with spawn_link --- erts/emulator/test/trace_SUITE.erl | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 70ead0ecd8..29e043dd5c 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -27,7 +27,7 @@ -export([all/0, suite/0, link_receive_call_correlation/0, receive_trace/1, link_receive_call_correlation/1, self_send/1, timeout_trace/1, send_trace/1, - procs_trace/1, dist_procs_trace/1, + procs_trace/1, dist_procs_trace/1, procs_new_trace/1, suspend/1, mutual_suspend/1, suspend_exit/1, suspender_exit/1, suspend_system_limit/1, suspend_opts/1, suspend_waiting/1, new_clear/1, existing_clear/1, @@ -456,6 +456,34 @@ dist_procs_trace(Config) when is_list(Config) -> true = stop_node(OtherNode), ok. +%% Test trace(new, How, [procs]). +procs_new_trace(Config) when is_list(Config) -> + Self = self(), + process_flag(trap_exit, true), + %% + Proc1 = spawn_link(?MODULE, process, [Self]), + io:format("Proc1 = ~p ~n", [Proc1]), + %% + 0 = erlang:trace(new, true, [procs]), + + MFA = {?MODULE, process, [Self]}, + %% + %% spawn, link + Proc1 ! {spawn_link_please, Self, MFA}, + Proc3 = receive {spawned, Proc1, P3} -> P3 end, + receive {trace, Proc3, spawned, Proc1, MFA} -> ok end, + receive {trace, Proc3, getting_linked, Proc1} -> ok end, + io:format("Proc3 = ~p ~n", [Proc3]), + receive_nothing(), + %% + %% + %% exit (not linked to tracing process) + Reason1 = make_ref(), + Proc1 ! {exit_please, Reason1}, + receive {'EXIT', Proc1, Reason1} -> ok end, + {trace, Proc3, exit, Reason1} = receive_first_trace(), + receive_nothing(), + ok. -- cgit v1.2.3 From 4acfd18a0e934789f752fd48d3031e48cdd3fbde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 26 Apr 2016 15:28:48 +0200 Subject: erts: Add profiling of startup Usage: erl -profile_boot ... --- erts/preloaded/src/init.erl | 76 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 77684751c8..618b53f6bb 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -41,6 +41,7 @@ %% -s : Start own processes. %% %% Experimental flags: +%% -profile_boot : Use an 'eprof light' to profile boot sequence %% -init_debug : Activate debug printouts in init %% -loader_debug : Activate debug printouts in erl_prim_loader %% -code_path_choice : strict | relaxed @@ -184,6 +185,11 @@ boot(BootArgs) -> erl_tracer:on_load(), {Start0,Flags,Args} = parse_boot_args(BootArgs), + %% We don't get to profile parsing of BootArgs + case get_flag(profile_boot, Flags, false) of + false -> ok; + true -> debug_profile_start() + end, Start = map(fun prepare_run_args/1, Start0), boot(Start, Flags, Args). @@ -765,7 +771,14 @@ do_boot(Init,Flags,Start) -> %% print the node name into the Purify log. (catch erlang:system_info({purify, "Node: " ++ atom_to_list(node())})), - start_em(Start). + start_em(Start), + case get_flag(profile_boot,Flags,false) of + false -> ok; + true -> + debug_profile_format_mfas(debug_profile_mfas()), + debug_profile_stop() + end, + ok. get_root(Flags) -> case get_argument(root, Flags) of @@ -1339,3 +1352,64 @@ run_on_load_handlers([M|Ms], Debug) -> end end; run_on_load_handlers([], _) -> ok. + + +%% debug profile (light variant of eprof) +debug_profile_start() -> + _ = erlang:trace_pattern({'_','_','_'},true,[call_time]), + _ = erlang:trace_pattern(on_load,true,[call_time]), + _ = erlang:trace(all,true,[call]), + ok. + +debug_profile_stop() -> + _ = erlang:trace_pattern({'_','_','_'},false,[call_time]), + _ = erlang:trace_pattern(on_load,false,[call_time]), + _ = erlang:trace(all,false,[call]), + ok. + +debug_profile_mfas() -> + _ = erlang:trace_pattern({'_','_','_'},pause,[call_time]), + _ = erlang:trace_pattern(on_load,pause,[call_time]), + MFAs = collect_loaded_mfas() ++ erlang:system_info(snifs), + collect_mfas(MFAs,[]). + +%% debug_profile_format_mfas should be called at the end of the boot phase +%% so all pertinent modules should be loaded at that point. +debug_profile_format_mfas(MFAs0) -> + MFAs = lists:sort(MFAs0), + lists:foreach(fun({{Us,C},{M,F,A}}) -> + Str = io_lib:format("~w:~w/~w", [M,F,A]), + io:format(standard_error,"~55s - ~6w : ~w us~n", [Str,C,Us]) + end, MFAs), + ok. + +collect_loaded_mfas() -> + Ms = [M || M <- [element(1, Mi) || Mi <- code:all_loaded()]], + collect_loaded_mfas(Ms,[]). + +collect_loaded_mfas([],MFAs) -> MFAs; +collect_loaded_mfas([M|Ms],MFAs0) -> + MFAs = [{M,F,A} || {F,A} <- M:module_info(functions)], + collect_loaded_mfas(Ms,MFAs ++ MFAs0). + + +collect_mfas([], Info) -> Info; +collect_mfas([MFA|MFAs],Info) -> + case erlang:trace_info(MFA,call_time) of + {call_time, []} -> + collect_mfas(MFAs,Info); + {call_time, false} -> + collect_mfas(MFAs,Info); + {call_time, Data} -> + case collect_mfa(MFA,Data,0,0) of + {{0,_},_} -> + %% ignore mfas with zero time + collect_mfas(MFAs,Info); + MfaData -> + collect_mfas(MFAs,[MfaData|Info]) + end + end. + +collect_mfa(Mfa,[],Count,Time) -> {{Time,Count},Mfa}; +collect_mfa(Mfa,[{_Pid,C,S,Us}|Data],Count,Time) -> + collect_mfa(Mfa,Data,Count + C,Time + S * 1000000 + Us). -- cgit v1.2.3 From 881ff622aab05234807848654a5c9dcedec95d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 26 Apr 2016 18:02:46 +0200 Subject: Update preloaded init.beam --- erts/preloaded/ebin/init.beam | Bin 46288 -> 50232 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index b13b33170d..ee32066f53 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ -- cgit v1.2.3 From 4196b0db4cadbddb41f460320fca76efcf9268e1 Mon Sep 17 00:00:00 2001 From: Constantin Rack Date: Sat, 10 Oct 2015 00:06:28 +0200 Subject: Fix typo in description of EPMD_DUMP_REQ response According to the source code, there is a space before the newline for unused nodes and no space before the newline for active ones. In current documentation, it is exactly opposite. This commit fixes the documentation to match with source code. --- erts/doc/src/erl_dist_protocol.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml index e1a58856f3..c35098cf27 100644 --- a/erts/doc/src/erl_dist_protocol.xml +++ b/erts/doc/src/erl_dist_protocol.xml @@ -364,14 +364,14 @@ If Result > 0, the packet only consists of [119, Result]. NodeInfo is, as expressed in Erlang:

- io:format("active name ~ts at port ~p, fd = ~p ~n", + io:format("active name ~ts at port ~p, fd = ~p~n", [NodeName, Port, Fd]).

or

- io:format("old/unused name ~ts at port ~p, fd = ~p~n", + io:format("old/unused name ~ts at port ~p, fd = ~p ~n", [NodeName, Port, Fd]). -- cgit v1.2.3 From 4c8d8550c4bb2e031440f9a0fdbe0e022247e695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 11 Apr 2016 18:04:47 +0200 Subject: erts: Add tests for maps:take/2 --- erts/emulator/test/map_SUITE.erl | 74 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 956b82335c..b3870f0313 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -48,6 +48,7 @@ t_bif_map_new/1, t_bif_map_put/1, t_bif_map_remove/1, + t_bif_map_take/1, t_bif_map_take_large/1, t_bif_map_update/1, t_bif_map_values/1, t_bif_map_to_list/1, @@ -112,7 +113,9 @@ all() -> [t_build_and_match_literals, t_build_and_match_literals_large, t_bif_map_get,t_bif_map_find,t_bif_map_is_key, t_bif_map_keys, t_bif_map_merge, t_bif_map_new, t_bif_map_put, - t_bif_map_remove, t_bif_map_update, + t_bif_map_remove, + t_bif_map_take, t_bif_map_take_large, + t_bif_map_update, t_bif_map_values, t_bif_map_to_list, t_bif_map_from_list, @@ -1970,7 +1973,7 @@ t_bif_map_remove(Config) when is_list(Config) -> 0 = erlang:map_size(maps:remove(some_key, #{})), M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, - 4 => number, 18446744073709551629 => wat}, + 4 => number, 18446744073709551629 => wat}, M1 = maps:remove("hi", M0), true = is_members([4,18446744073709551629,int,<<"key">>],maps:keys(M1)), @@ -1999,10 +2002,71 @@ t_bif_map_remove(Config) when is_list(Config) -> %% error case do_badmap(fun(T) -> - {'EXIT',{{badmap,T},[{maps,remove,_,_}|_]}} = - (catch maps:remove(a, T)) + {'EXIT',{{badmap,T},[{maps,remove,_,_}|_]}} = (catch maps:remove(a, T)) end), - ok. + ok. + +t_bif_map_take(Config) when is_list(Config) -> + error = maps:take(some_key, #{}), + + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + 5 = maps:size(M0), + {"hello", M1} = maps:take("hi", M0), + true = is_members([4,18446744073709551629,int,<<"key">>],maps:keys(M1)), + true = is_members([number,wat,3,<<"value">>],maps:values(M1)), + error = maps:take("hi", M1), + 4 = maps:size(M1), + + {3, M2} = maps:take(int, M1), + true = is_members([4,18446744073709551629,<<"key">>],maps:keys(M2)), + true = is_members([number,wat,<<"value">>],maps:values(M2)), + error = maps:take(int, M2), + 3 = maps:size(M2), + + {<<"value">>,M3} = maps:take(<<"key">>, M2), + true = is_members([4,18446744073709551629],maps:keys(M3)), + true = is_members([number,wat],maps:values(M3)), + error = maps:take(<<"key">>, M3), + 2 = maps:size(M3), + + {wat,M4} = maps:take(18446744073709551629, M3), + true = is_members([4],maps:keys(M4)), + true = is_members([number],maps:values(M4)), + error = maps:take(18446744073709551629, M4), + 1 = maps:size(M4), + + {number,M5} = maps:take(4, M4), + [] = maps:keys(M5), + [] = maps:values(M5), + error = maps:take(4, M5), + 0 = maps:size(M5), + + {wat,#{ "hi" := "hello", int := 3, 4 := number, <<"key">> := <<"value">>}} = maps:take(18446744073709551629,M0), + + %% error case + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},[{maps,take,_,_}|_]}} = (catch maps:take(a, T)) + end), + ok. + +t_bif_map_take_large(Config) when is_list(Config) -> + KVs = [{{erlang:md5(<>),I}, I}|| I <- lists:seq(1,500)], + M0 = maps:from_list(KVs), + ok = bif_map_take_all(KVs, M0), + ok. + +bif_map_take_all([], M0) -> + 0 = maps:size(M0), + ok; +bif_map_take_all([{K,V}|KVs],M0) -> + {ok,V} = maps:find(K,M0), + {V,M1} = maps:take(K,M0), + error = maps:find(K,M1), + error = maps:take(K,M1), + bif_map_take_all(KVs,M1). + t_bif_map_update(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, -- cgit v1.2.3 From e1c70e582bc9e9eddd56d5483e59edc6eb75c3a6 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 28 Apr 2016 11:02:33 +0200 Subject: erts: Rename erl flag +xmqd to +hmqd in erlexec --- erts/etc/common/erlexec.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 82a0303f86..5a5021b003 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -150,6 +150,7 @@ static char *plush_val_switches[] = { "ms", "mbs", "pds", + "mqd", "", NULL }; @@ -160,12 +161,6 @@ static char *plusr_val_switches[] = { NULL }; -/* +x arguments with values */ -static char *plusx_val_switches[] = { - "mqd", - NULL -}; - /* +z arguments with values */ static char *plusz_val_switches[] = { "dbbl", @@ -986,20 +981,6 @@ int main(int argc, char **argv) add_Eargs(argv[i+1]); i++; break; - case 'x': - if (!is_one_of_strings(&argv[i][2], plusx_val_switches)) { - goto the_default; - } else { - if (i+1 >= argc - || argv[i+1][0] == '-' - || argv[i+1][0] == '+') - usage(argv[i]); - argv[i][0] = '-'; - add_Eargs(argv[i]); - add_Eargs(argv[i+1]); - i++; - } - break; case 'z': if (!is_one_of_strings(&argv[i][2], plusz_val_switches)) { goto the_default; @@ -1200,7 +1181,7 @@ usage_aux(void) "[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] " "[+SP PERCENTAGE_SCHEDULERS:PERCENTAGE_SCHEDULERS_ONLINE] " "[+T LEVEL] [+V] [+v] " - "[+W] [+x DEFAULT_PROC_FLAGS] [+z MISC_OPTION] [args ...]\n"); + "[+W] [+z MISC_OPTION] [args ...]\n"); exit(1); } -- cgit v1.2.3 From a13d4a750dfdf9a2a96d1e7ec0054644187afa59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20L=C3=A5ng?= Date: Sat, 27 Feb 2016 19:23:48 +0100 Subject: Enhance map specs in erts, stdlib, runtime_tools Using the new type syntax, we can specify which keys are required, and which are optional in a way Dialyzer could use. --- erts/preloaded/src/erlang.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 3cc17014ff..20a64e81b4 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2257,9 +2257,9 @@ spawn_opt(_Tuple) -> Input :: non_neg_integer(), Output :: non_neg_integer(); (microstate_accounting) -> [MSAcc_Thread] | undefined when - MSAcc_Thread :: #{ type => MSAcc_Thread_Type, - id => MSAcc_Thread_Id, - counters => MSAcc_Counters}, + MSAcc_Thread :: #{ type := MSAcc_Thread_Type, + id := MSAcc_Thread_Id, + counters := MSAcc_Counters}, MSAcc_Thread_Type :: scheduler | async | aux, MSAcc_Thread_Id :: non_neg_integer(), MSAcc_Counters :: #{ MSAcc_Thread_State => non_neg_integer() }, -- cgit v1.2.3 From 1cb11595f5d4860b38c8bf6d56073ba0032a51a9 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 13 Apr 2016 14:43:43 +0200 Subject: erts: Add exact association types to the abstract format doc --- erts/doc/src/absform.xml | 3 +++ 1 file changed, 3 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 13756ddfdc..6d6ba224a0 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -636,6 +636,9 @@ If A is an association type K => V, where K and V are types, then Rep(A) = {type,LINE,map_field_assoc,[Rep(K),Rep(V)]}. + If A is an association type K := V, where + K and V are types, then Rep(A) = + {type,LINE,map_field_exact,[Rep(K),Rep(V)]}.
-- cgit v1.2.3 From e48518e4520146d4b8461689c2aab5882478339f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20L=C3=A5ng?= Date: Fri, 29 Apr 2016 16:18:23 +0200 Subject: erl_process: Restore R18 HiPE PCB offsets It was recently observed that the LLVM backend for HiPE was broken, generating code that crashes the VM. It turns out that this was caused by LLVM hard-coding an offset into the Erlang PCB, which became incorrect as of commit 3ac08f9b, that introduced a new member into the PCB before the HiPE-specific state, changing its offsets. For now, until a proper fix is implemented, we move this new member to after the HiPE-specific state, allowing the LLVM backend to work once more. --- erts/emulator/beam/erl_process.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 61acf5924b..1c01d705a7 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -925,7 +925,6 @@ struct process { Eterm* stop; /* Stack top */ Eterm* heap; /* Heap start */ Eterm* hend; /* Heap end */ - Eterm* abandoned_heap; Uint heap_sz; /* Size of heap in words */ Uint min_heap_size; /* Minimum size of heap (in words). */ Uint min_vheap_size; /* Minimum size of virtual heap (in words). */ @@ -940,6 +939,16 @@ struct process { struct hipe_process_state hipe; #endif + /* + * Moved to after "struct hipe_process_state hipe", as a temporary fix for + * LLVM hard-coding offsetof(struct process, hipe.nstack) (sic!) + * (see void X86FrameLowering::adjustForHiPEPrologue(...) in + * lib/Target/X86/X86FrameLowering.cpp). + * + * Used to be below "Eterm* hend". + */ + Eterm* abandoned_heap; + /* * Saved x registers. */ -- cgit v1.2.3 From c2c47441ddc436a6d36aa1264ed6f198a9e0c3e0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 29 Apr 2016 16:37:35 +0200 Subject: erts: Move option info for erts_mmap from {allocator,mseg_alloc} to {allocator,erts_mmap} --- erts/emulator/sys/common/erl_mmap.c | 15 +++++++++++---- erts/emulator/sys/common/erl_mseg.c | 5 +---- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index a7e710070b..bf675cf1ab 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -2099,6 +2099,7 @@ int erts_mmap_in_supercarrier(ErtsMemMapper* mm, void *ptr) } static struct { + Eterm options; Eterm total; Eterm total_sa; Eterm total_sua; @@ -2132,6 +2133,7 @@ static void init_atoms(void) erts_mtx_lock(&am.init_mutex); if (!am.is_initialized) { + AM_INIT(options); AM_INIT(total); AM_INIT(total_sa); AM_INIT(total_sua); @@ -2387,9 +2389,9 @@ Eterm erts_mmap_info(ErtsMemMapper* mm, Eterm seg_tags[] = { am.used, am.max, am.allocated, am.reserved, am.used_sa, am.used_sua }; Eterm group[2]; Eterm group_tags[] = { am.sizes, am.free_segs }; - Eterm list[2]; - Eterm list_tags[2]; /* { am.supercarrier, am.os } */ - int lix; + Eterm list[3]; + Eterm list_tags[3]; /* { am.options, am.supercarrier, am.os } */ + int lix = 0; Eterm res = THE_NON_VALUE; if (!hpp) { @@ -2412,6 +2414,12 @@ Eterm erts_mmap_info(ErtsMemMapper* mm, erts_smp_mtx_unlock(&mm->mtx); } + list[lix] = erts_mmap_info_options(mm, "option ", print_to_p, print_to_arg, + hpp, szp); + list_tags[lix] = am.options; + lix++; + + if (print_to_p) { int to = *print_to_p; void *arg = print_to_arg; @@ -2441,7 +2449,6 @@ Eterm erts_mmap_info(ErtsMemMapper* mm, init_atoms(); } - lix = 0; if (mm->supercarrier) { group[0] = erts_bld_atom_uword_2tup_list(hpp, szp, sizeof(size_tags)/sizeof(Eterm), diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 43e75e2573..3a477089e7 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -996,10 +996,7 @@ info_options(ErtsMsegAllctr_t *ma, Uint **hpp, Uint *szp) { - Eterm res; - - res = erts_mmap_info_options(&erts_dflt_mmapper, - prefix, print_to_p, print_to_arg, hpp, szp); + Eterm res = NIL; if (print_to_p) { int to = *print_to_p; -- cgit v1.2.3 From 0a5d8f3ae45bd8a10498ef35c1e704983afbbf25 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 20 Apr 2016 15:57:47 +0200 Subject: erts: Add literal_mmap and exec_mmap to system_info erlang:system_info(allocator) -> {Allocator, Version, Features, Settings} Features includes 'literal_mmap' and/or 'exec_mmap' if they exist. --- erts/emulator/beam/erl_alloc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 5b76f34165..64a323c434 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -2999,7 +2999,12 @@ erts_allocator_options(void *proc) #if ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC terms[length++] = am_atom_put("sys_aligned_alloc", 17); #endif - +#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) + terms[length++] = ERTS_MAKE_AM("literal_mmap"); +#endif +#ifdef ERTS_ALC_A_EXEC + terms[length++] = ERTS_MAKE_AM("exec_mmap"); +#endif features = length ? erts_bld_list(hpp, szp, length, terms) : NIL; #if defined(__GLIBC__) -- cgit v1.2.3 From 48d24da6586a80ab67fe5c242d17ea4d044c917c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 29 Apr 2016 16:44:39 +0200 Subject: erts: Add erts_mmap to system_info(allocator) to the Settings list {Allocator, Version, Features, Settings} --- erts/emulator/beam/erl_alloc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 64a323c434..0f68c6bb10 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -2963,6 +2963,10 @@ erts_allocator_options(void *proc) atoms[length] = am_atom_put("alloc_util", 10); terms[length++] = erts_alcu_au_info_options(NULL, NULL, hpp, szp); + + atoms[length] = ERTS_MAKE_AM("erts_mmap"); + terms[length++] = erts_mmap_info_options(&erts_dflt_mmapper, NULL, NULL, + NULL, hpp, szp); { Eterm o[3], v[3]; o[0] = am_atom_put("m", 1); -- cgit v1.2.3 From 9f5fd83ffda711d80000bfe1cb7f99c40d9fffde Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 29 Apr 2016 16:55:49 +0200 Subject: erts: Fix system_info({allocator_sizes, mseg_alloc}) will now return [{instance,0,[{segments_size,9961472,9961472,11010048}]}, {instance,1,[{segments_size,6291456,6291456,6815744}]}, {instance,2,[{segments_size,524288,524288,786432}]}, {instance,3,[{segments_size,1048576,1048576,1835008}]}, {instance,4,[{segments_size,0,0,262144}]}] and not just empty lists. --- erts/emulator/beam/erl_alloc.c | 14 ++--- erts/emulator/sys/common/erl_mseg.c | 122 ++++++++++++++++++++---------------- erts/emulator/sys/common/erl_mseg.h | 2 +- 3 files changed, 74 insertions(+), 64 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 0f68c6bb10..01db65d539 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -2841,7 +2841,7 @@ erts_allocator_info(int to, void *arg) int i; for (i = 0; i <= max; i++) { erts_print(to, arg, "=allocator:mseg_alloc[%d]\n", i); - erts_mseg_info(i, &to, arg, 0, NULL, NULL); + erts_mseg_info(i, &to, arg, 0, 0, NULL, NULL); } erts_print(to, arg, "=allocator:erts_mmap.default_mmap\n"); erts_mmap_info(&erts_dflt_mmapper, &to, arg, NULL, NULL, &emis); @@ -3241,10 +3241,8 @@ reply_alloc_info(void *vair) case ERTS_ALC_INFO_A_MSEG_ALLOC: alloc_atom = erts_bld_atom(hpp, szp, "mseg_alloc"); #if HAVE_ERTS_MSEG - ainfo = (air->only_sz - ? NIL - : erts_mseg_info(0, NULL, NULL, hpp != NULL, - hpp, szp)); + ainfo = erts_mseg_info(0, NULL, NULL, hpp != NULL, + air->only_sz, hpp, szp); ainfo = erts_bld_tuple3(hpp, szp, alloc_atom, make_small(0), @@ -3287,10 +3285,8 @@ reply_alloc_info(void *vair) case ERTS_ALC_INFO_A_MSEG_ALLOC: #if HAVE_ERTS_MSEG && defined(ERTS_SMP) alloc_atom = erts_bld_atom(hpp, szp, "mseg_alloc"); - ainfo = (air->only_sz - ? NIL - : erts_mseg_info(sched_id, NULL, NULL, - hpp != NULL, hpp, szp)); + ainfo = erts_mseg_info(sched_id, NULL, NULL, + hpp != NULL, air->only_sz, hpp, szp); ainfo = erts_bld_tuple(hpp, szp, 3, alloc_atom, make_small(sched_id), diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 3a477089e7..f3306a888c 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -1107,7 +1107,7 @@ info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp static Eterm info_status(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, - int begin_new_max_period, Uint **hpp, Uint *szp) + int begin_new_max_period, int only_sz, Uint **hpp, Uint *szp) { Eterm res = THE_NON_VALUE; @@ -1120,38 +1120,41 @@ info_status(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, int to = *print_to_p; void *arg = print_to_arg; - erts_print(to, arg, "cached_segments: %beu\n", ma->cache_size); - erts_print(to, arg, "cache_hits: %beu\n", ma->cache_hits); - erts_print(to, arg, "segments: %beu %beu %beu\n", - ma->segments.current.no, ma->segments.max.no, ma->segments.max_ever.no); - erts_print(to, arg, "segments_size: %beu %beu %beu\n", + if (!only_sz) { + erts_print(to, arg, "cached_segments: %beu\n", ma->cache_size); + erts_print(to, arg, "cache_hits: %beu\n", ma->cache_hits); + erts_print(to, arg, "segments: %beu %beu %beu\n", + ma->segments.current.no, ma->segments.max.no, ma->segments.max_ever.no); + erts_print(to, arg, "segments_watermark: %beu\n", + ma->segments.current.watermark); + } + erts_print(to, arg, "segments_size: %beu %beu %beu\n", ma->segments.current.sz, ma->segments.max.sz, ma->segments.max_ever.sz); - erts_print(to, arg, "segments_watermark: %beu\n", - ma->segments.current.watermark); } if (hpp || szp) { res = NIL; - add_2tup(hpp, szp, &res, - am.segments_watermark, - bld_unstable_uint(hpp, szp, ma->segments.current.watermark)); - add_4tup(hpp, szp, &res, - am.segments_size, - bld_unstable_uint(hpp, szp, ma->segments.current.sz), - bld_unstable_uint(hpp, szp, ma->segments.max.sz), - bld_unstable_uint(hpp, szp, ma->segments.max_ever.sz)); - add_4tup(hpp, szp, &res, - am.segments, - bld_unstable_uint(hpp, szp, ma->segments.current.no), - bld_unstable_uint(hpp, szp, ma->segments.max.no), - bld_unstable_uint(hpp, szp, ma->segments.max_ever.no)); - add_2tup(hpp, szp, &res, - am.cache_hits, - bld_unstable_uint(hpp, szp, ma->cache_hits)); - add_2tup(hpp, szp, &res, - am.cached_segments, - bld_unstable_uint(hpp, szp, ma->cache_size)); - + add_4tup(hpp, szp, &res, + am.segments_size, + bld_unstable_uint(hpp, szp, ma->segments.current.sz), + bld_unstable_uint(hpp, szp, ma->segments.max.sz), + bld_unstable_uint(hpp, szp, ma->segments.max_ever.sz)); + if (!only_sz) { + add_2tup(hpp, szp, &res, + am.segments_watermark, + bld_unstable_uint(hpp, szp, ma->segments.current.watermark)); + add_4tup(hpp, szp, &res, + am.segments, + bld_unstable_uint(hpp, szp, ma->segments.current.no), + bld_unstable_uint(hpp, szp, ma->segments.max.no), + bld_unstable_uint(hpp, szp, ma->segments.max_ever.no)); + add_2tup(hpp, szp, &res, + am.cache_hits, + bld_unstable_uint(hpp, szp, ma->cache_hits)); + add_2tup(hpp, szp, &res, + am.cached_segments, + bld_unstable_uint(hpp, szp, ma->cache_size)); + } } if (begin_new_max_period) { @@ -1163,26 +1166,31 @@ info_status(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, } static Eterm info_memkind(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, - int begin_max_per, Uint **hpp, Uint *szp) + int begin_max_per, int only_sz, Uint **hpp, Uint *szp) { Eterm res = THE_NON_VALUE; Eterm atoms[3]; Eterm values[3]; - if (print_to_p) { - erts_print(*print_to_p, print_to_arg, "memory kind: %s\n", "all memory"); - } - if (hpp || szp) { - atoms[0] = am.name; - atoms[1] = am.status; - atoms[2] = am.calls; - values[0] = erts_bld_string(hpp, szp, "all memory"); + if (!only_sz) { + if (print_to_p) { + erts_print(*print_to_p, print_to_arg, "memory kind: %s\n", "all memory"); + } + if (hpp || szp) { + atoms[0] = am.name; + atoms[1] = am.status; + atoms[2] = am.calls; + values[0] = erts_bld_string(hpp, szp, "all memory"); + } } - values[1] = info_status(ma, print_to_p, print_to_arg, begin_max_per, hpp, szp); - values[2] = info_calls(ma, print_to_p, print_to_arg, hpp, szp); + res = info_status(ma, print_to_p, print_to_arg, begin_max_per, only_sz, hpp, szp); + if (!only_sz) { + values[1] = res; + values[2] = info_calls(ma, print_to_p, print_to_arg, hpp, szp); - if (hpp || szp) - res = bld_2tup_list(hpp, szp, 3, atoms, values); + if (hpp || szp) + res = bld_2tup_list(hpp, szp, 3, atoms, values); + } return res; } @@ -1226,6 +1234,7 @@ erts_mseg_info(int ix, int *print_to_p, void *print_to_arg, int begin_max_per, + int only_sz, Uint **hpp, Uint *szp) { @@ -1236,24 +1245,29 @@ erts_mseg_info(int ix, Uint n = 0; if (hpp || szp) { - - if (!atoms_initialized) - init_atoms(ma); - - atoms[0] = am.version; - atoms[1] = am.options; - atoms[2] = am.memkind; - atoms[3] = am.memkind; + if (!atoms_initialized) + init_atoms(ma); + } + if (!only_sz) { + if (hpp || szp) { + atoms[0] = am.version; + atoms[1] = am.options; + atoms[2] = am.memkind; + } + values[n++] = info_version(ma, print_to_p, print_to_arg, hpp, szp); + values[n++] = info_options(ma, "option ", print_to_p, print_to_arg, hpp, szp); } - values[n++] = info_version(ma, print_to_p, print_to_arg, hpp, szp); - values[n++] = info_options(ma, "option ", print_to_p, print_to_arg, hpp, szp); ERTS_MSEG_LOCK(ma); ERTS_DBG_MA_CHK_THR_ACCESS(ma); - values[n++] = info_memkind(ma, print_to_p, print_to_arg, begin_max_per, hpp, szp); - if (hpp || szp) - res = bld_2tup_list(hpp, szp, n, atoms, values); + res = info_memkind(ma, print_to_p, print_to_arg, begin_max_per, only_sz, hpp, szp); + + if (!only_sz) { + values[n++] = res; + if (hpp || szp) + res = bld_2tup_list(hpp, szp, n, atoms, values); + } ERTS_MSEG_UNLOCK(ma); diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index 192e5767e4..a43b409e94 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -99,7 +99,7 @@ void erts_mseg_init(ErtsMsegInit_t *init); void erts_mseg_late_init(void); /* Have to be called after all allocators, threads and timers have been initialized. */ Eterm erts_mseg_info_options(int, int *, void*, Uint **, Uint *); -Eterm erts_mseg_info(int, int *, void*, int, Uint **, Uint *); +Eterm erts_mseg_info(int, int *, void*, int, int, Uint **, Uint *); #endif /* #if HAVE_ERTS_MSEG */ -- cgit v1.2.3 From 8b9b4e8bffe087752463287457330803e69a1c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Mar 2016 16:22:11 +0100 Subject: runtime_tools: Add lttng 'procs' tracing --- erts/emulator/beam/erl_trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 96fc46c817..fa1d03c588 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1294,7 +1294,7 @@ trace_proc_spawn(Process *p, Eterm what, Eterm pid, mfa = TUPLE3(hp, mod, func, args); hp += 4; send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_PROCS, - am_spawn, pid, mfa); + what, pid, mfa); } } -- cgit v1.2.3 From f5fa3ac80d2f7fcd11ac3e702c29df5ef6c204db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 3 Mar 2016 17:13:32 +0100 Subject: runtime_tools: Add lttng 'call' tracing --- erts/emulator/beam/erl_trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index fa1d03c588..0842c0c0a4 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1000,7 +1000,7 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, ErtsTracer *tracer) mfa = TUPLE3(hp, mod, name, make_small(arity)); hp += 4; send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, - NULL, TRACE_FUN_DEFAULT, am_return_from, mfa, retval, am_true); + NULL, TRACE_FUN_CALL, am_return_from, mfa, retval, am_true); } /* Send {trace_ts, Pid, exception_from, {Mod, Name, Arity}, {Class,Value}, @@ -1055,7 +1055,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value, cv = TUPLE2(hp, class, value); hp += 3; send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, - NULL, TRACE_FUN_DEFAULT, am_exception_from, mfa_tuple, cv, am_true); + NULL, TRACE_FUN_CALL, am_exception_from, mfa_tuple, cv, am_true); } /* -- cgit v1.2.3 From 6f89f183317c930fe1199ad3320825b02bb861da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 7 Mar 2016 16:04:04 +0100 Subject: erts: Extend garbage collection trace Replace 'gc_start' and 'gc_end' with * 'gc_minor_start' * 'gc_minor_end' * 'gc_major_start' * 'gc_major_end' --- erts/emulator/beam/atom.names | 4 +++ erts/emulator/beam/erl_gc.c | 41 ++++++++++++++++-------------- erts/emulator/beam/erl_trace.c | 13 ++++++---- erts/emulator/beam/erl_trace.h | 2 +- erts/emulator/nifs/common/erl_tracer_nif.c | 6 +++++ 5 files changed, 41 insertions(+), 25 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 8c51f788c0..3022c0a99a 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -273,6 +273,10 @@ atom garbage_collecting atom garbage_collection atom garbage_collection_info atom gc_end +atom gc_major_end +atom gc_major_start +atom gc_minor_end +atom gc_minor_start atom gc_start atom Ge='>=' atom generational diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index f33ade27f3..afb2ec1cc2 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -593,10 +593,6 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, esdp = erts_get_scheduler_data(); - if (IS_TRACED_FL(p, F_TRACE_GC)) { - trace_gc(p, am_gc_start); - } - erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC); if (erts_system_monitor_long_gc != 0) start_time = erts_get_monotonic_time(esdp); @@ -619,18 +615,29 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, */ if (GEN_GCS(p) < MAX_GEN_GCS(p) && !(FLAGS(p) & F_NEED_FULLSWEEP)) { - DTRACE2(gc_minor_start, pidbuf, need); - reds = minor_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); - DTRACE2(gc_minor_end, pidbuf, reclaimed_now); - if (reds < 0) - goto do_major_collection; - } - else { - do_major_collection: + if (IS_TRACED_FL(p, F_TRACE_GC)) { + trace_gc(p, am_gc_minor_start, need); + } + DTRACE2(gc_minor_start, pidbuf, need); + reds = minor_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); + DTRACE2(gc_minor_end, pidbuf, reclaimed_now); + if (IS_TRACED_FL(p, F_TRACE_GC)) { + trace_gc(p, am_gc_minor_end, reclaimed_now); + } + if (reds < 0) + goto do_major_collection; + } else { +do_major_collection: ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_GC_FULL); - DTRACE2(gc_major_start, pidbuf, need); - reds = major_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); - DTRACE2(gc_major_end, pidbuf, reclaimed_now); + if (IS_TRACED_FL(p, F_TRACE_GC)) { + trace_gc(p, am_gc_major_start, need); + } + DTRACE2(gc_major_start, pidbuf, need); + reds = major_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); + DTRACE2(gc_major_end, pidbuf, reclaimed_now); + if (IS_TRACED_FL(p, F_TRACE_GC)) { + trace_gc(p, am_gc_major_end, reclaimed_now); + } ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_GC); } @@ -646,10 +653,6 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, 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); - } - if (erts_system_monitor_long_gc != 0) { ErtsMonotonicTime end_time; Uint gc_time; diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 0842c0c0a4..9146cc7c7f 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1326,23 +1326,26 @@ void save_calls(Process *p, Export *e) * are all small (atomic) integers. */ void -trace_gc(Process *p, Eterm what) +trace_gc(Process *p, Eterm what, Uint size) { ErtsTracerNif *tnif = NULL; Eterm* hp; Eterm msg = NIL; - Uint size = 0; + Uint sz = 0; + Eterm tup; if (is_tracer_proc_enabled( p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, what)) { - (void) erts_process_gc_info(p, &size, NULL); - hp = HAlloc(p, size); + (void) erts_process_gc_info(p, &sz, NULL); + hp = HAlloc(p, sz + 3 + 2); msg = erts_process_gc_info(p, NULL, &hp); + tup = TUPLE2(hp, am_wordsize, make_small(size)); hp += 3; + msg = CONS(hp, tup, msg); hp += 2; send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_GC, - what, msg, THE_NON_VALUE); + what, msg, am_undefined); } } diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index 177fd373a6..9a007e62ec 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -103,7 +103,7 @@ void trace_sched(Process*, ErtsProcLocks, Eterm); void trace_proc(Process*, ErtsProcLocks, Process*, Eterm, Eterm); void trace_proc_spawn(Process*, Eterm what, Eterm pid, Eterm mod, Eterm func, Eterm args); void save_calls(Process *p, Export *); -void trace_gc(Process *p, Eterm what); +void trace_gc(Process *p, Eterm what, Uint size); /* port tracing */ void trace_virtual_sched(Process*, ErtsProcLocks, Eterm); void trace_sched_ports(Port *pp, Eterm); diff --git a/erts/emulator/nifs/common/erl_tracer_nif.c b/erts/emulator/nifs/common/erl_tracer_nif.c index a1e0e581a4..1bb6b940c4 100644 --- a/erts/emulator/nifs/common/erl_tracer_nif.c +++ b/erts/emulator/nifs/common/erl_tracer_nif.c @@ -72,6 +72,12 @@ ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload) ATOM_DECL(trace); \ ATOM_DECL(trace_ts); \ ATOM_DECL(true); \ + ATOM_DECL(gc_start); \ + ATOM_DECL(gc_end); \ + ATOM_DECL(gc_minor_start); \ + ATOM_DECL(gc_minor_end); \ + ATOM_DECL(gc_major_start); \ + ATOM_DECL(gc_major_end); \ ATOM_DECL(undefined); #define ATOM_DECL(A) static ERL_NIF_TERM atom_##A -- cgit v1.2.3 From a4811133b70370e54162d256d61ef4c084e1f4fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 17 Mar 2016 17:53:55 +0100 Subject: erts: Extend 'enabled' tracer callbacks Adds the following capabilities to a tracer backend * enabled_procs/3 * enabled_ports/3 * enabled_running/3 * enabled_call/3 * enabled_send/3 * enabled_receive/3 * enabled_garbage_collection/3 These functions will fall back to enabled/3 if not provided and the tracepoint is active. --- erts/emulator/beam/erl_trace.c | 241 +++++++++++++++++++++++++---------------- 1 file changed, 147 insertions(+), 94 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 9146cc7c7f..a2f94677bb 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -237,6 +237,7 @@ write_timestamp(ErtsTraceTimeStamp *tsp, Eterm **hpp) } } +#ifdef ERTS_SMP #define PATCH_TS_SIZE(p) patch_ts_size(TFLGS_TS_TYPE(p)) static ERTS_INLINE Uint @@ -257,6 +258,7 @@ patch_ts_size(int ts_type) return 0; } } +#endif /* * Write a timestamp. The timestamp MUST be the last @@ -360,18 +362,25 @@ void erts_init_trace(void) { (*(BPP))->mem) enum ErtsTracerOpt { - TRACE_FUN_DEFAULT = 0, - TRACE_FUN_ENABLED = 1, - TRACE_FUN_SEND = 2, - TRACE_FUN_RECEIVE = 3, - TRACE_FUN_CALL = 4, - TRACE_FUN_RUNNING = 5, - TRACE_FUN_GC = 6, - TRACE_FUN_PROCS = 7, - TRACE_FUN_PORTS = 8 + TRACE_FUN_DEFAULT = 0, + TRACE_FUN_ENABLED = 1, + TRACE_FUN_T_SEND = 2, + TRACE_FUN_T_RECEIVE = 3, + TRACE_FUN_T_CALL = 4, + TRACE_FUN_T_RUNNING = 5, + TRACE_FUN_T_GC = 6, + TRACE_FUN_T_PROCS = 7, + TRACE_FUN_T_PORTS = 8, + TRACE_FUN_E_SEND = 9, + TRACE_FUN_E_RECEIVE = 10, + TRACE_FUN_E_CALL = 11, + TRACE_FUN_E_RUNNING = 12, + TRACE_FUN_E_GC = 13, + TRACE_FUN_E_PROCS = 14, + TRACE_FUN_E_PORTS = 15 }; -#define NIF_TRACER_TYPES (9) +#define NIF_TRACER_TYPES (16) static ERTS_INLINE int @@ -381,15 +390,19 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee, const ErtsTracer tracer, Eterm tag, Eterm msg, Eterm extra, Eterm pam_result); static ERTS_INLINE int send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, - Eterm t_p_id, ErtsTracerNif *tnif, enum ErtsTracerOpt, + Eterm t_p_id, ErtsTracerNif *tnif, + enum ErtsTracerOpt topt, Eterm tag, Eterm msg, Eterm extra); static ERTS_INLINE Eterm call_enabled_tracer(Process *c_p, const ErtsTracer tracer, - ErtsTracerNif **tnif_ref, Eterm tag, Eterm t_p_id); + ErtsTracerNif **tnif_ref, + enum ErtsTracerOpt topt, + Eterm tag, Eterm t_p_id); static int -is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, - ErtsPTabElementCommon *t_p, - ErtsTracerNif **tnif_ret, Eterm tag); +is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks, + ErtsPTabElementCommon *t_p, + ErtsTracerNif **tnif_ret, + enum ErtsTracerOpt topt, Eterm tag); #define SEND_TO_TRACER(c_p, tag, msg) \ send_to_tracer_nif(c_p, &(c_p)->common, (c_p)->common.id, NULL, \ @@ -449,7 +462,7 @@ erts_set_system_seq_tracer(Process *c_p, ErtsProcLocks c_p_locks, ErtsTracer new if (!ERTS_TRACER_IS_NIL(new)) { Eterm nif_result = call_enabled_tracer( NULL, new, NULL, - am_trace_status, am_undefined); + TRACE_FUN_ENABLED, am_trace_status, am_undefined); switch (nif_result) { case am_trace: break; default: @@ -481,7 +494,8 @@ erts_get_system_seq_tracer(void) erts_smp_rwmtx_runlock(&sys_trace_rwmtx); if (st != erts_tracer_nil && - call_enabled_tracer(NULL, st, NULL, am_trace_status, am_undefined) == am_remove) { + call_enabled_tracer(NULL, st, NULL, TRACE_FUN_ENABLED, + am_trace_status, am_undefined) == am_remove) { erts_set_system_seq_tracer(NULL, 0, erts_tracer_nil); st = erts_tracer_nil; } @@ -500,10 +514,11 @@ get_default_tracing(Uint *flagsp, ErtsTracer *tracerp, if (ERTS_TRACER_IS_NIL(*default_tracer)) { *default_trace_flags &= ~TRACEE_FLAGS; } else { - Eterm nif_result = call_enabled_tracer( - NULL, *default_tracer, NULL, - am_trace_status, am_undefined); - switch (nif_result) { + Eterm nif_res; + nif_res = call_enabled_tracer(NULL, *default_tracer, + NULL, TRACE_FUN_ENABLED, + am_trace_status, am_undefined); + switch (nif_res) { case am_trace: break; default: { ErtsTracer curr_default_tracer = *default_tracer; @@ -753,7 +768,7 @@ trace_sched_aux(Process *p, ErtsProcLocks locks, Eterm what) break; } - if (!is_tracer_proc_enabled(p, locks, &p->common, &tnif, what)) + if (!is_tracer_enabled(p, locks, &p->common, &tnif, TRACE_FUN_E_RUNNING, what)) return; if (ERTS_PROC_IS_EXITING(p)) @@ -772,7 +787,7 @@ trace_sched_aux(Process *p, ErtsProcLocks locks, Eterm what) hp += 4; } - send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_RUNNING, + send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_RUNNING, what, tmp, THE_NON_VALUE); } @@ -811,8 +826,9 @@ trace_send(Process *p, Eterm to, Eterm msg) operation = am_send_to_non_existing_process; } - if (is_tracer_proc_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, operation)) - send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_SEND, + if (is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, + TRACE_FUN_E_SEND, operation)) + send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SEND, operation, msg, to); } @@ -823,10 +839,10 @@ void trace_receive(Process *c_p, Eterm msg) { ErtsTracerNif *tnif = NULL; - if (is_tracer_proc_enabled(NULL, 0, &c_p->common, - &tnif, am_receive)) + if (is_tracer_enabled(NULL, 0, &c_p->common, &tnif, + TRACE_FUN_E_RECEIVE, am_receive)) send_to_tracer_nif(NULL, &c_p->common, c_p->common.id, - tnif, TRACE_FUN_RECEIVE, + tnif, TRACE_FUN_T_RECEIVE, am_receive, msg, THE_NON_VALUE); } @@ -837,8 +853,9 @@ seq_trace_update_send(Process *p) ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p)))); if (have_no_seqtrace(SEQ_TRACE_TOKEN(p)) || (seq_tracer != NIL && - call_enabled_tracer( NULL, seq_tracer, NULL, am_trace_status, - p ? p->common.id : am_undefined) != am_trace) + call_enabled_tracer(NULL, seq_tracer, NULL, + TRACE_FUN_ENABLED, am_trace_status, + p ? p->common.id : am_undefined) != am_trace) #ifdef USE_VM_PROBES || (SEQ_TRACE_TOKEN(p) == am_have_dt_utag) #endif @@ -884,9 +901,10 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, ASSERT(is_tuple(token) || is_nil(token)); if (token == NIL || (process && ERTS_TRACE_FLAGS(process) & F_SENSITIVE) || ERTS_TRACER_IS_NIL(seq_tracer) || - call_enabled_tracer( - NULL, seq_tracer, NULL, am_trace_status, - process ? process->common.id : am_undefined) != am_trace) { + call_enabled_tracer(NULL, seq_tracer, + NULL, TRACE_FUN_ENABLED, + am_trace_status, + process ? process->common.id : am_undefined) != am_trace) { return; } @@ -1000,7 +1018,7 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, ErtsTracer *tracer) mfa = TUPLE3(hp, mod, name, make_small(arity)); hp += 4; send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, - NULL, TRACE_FUN_CALL, am_return_from, mfa, retval, am_true); + NULL, TRACE_FUN_T_CALL, am_return_from, mfa, retval, am_true); } /* Send {trace_ts, Pid, exception_from, {Mod, Name, Arity}, {Class,Value}, @@ -1055,7 +1073,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value, cv = TUPLE2(hp, class, value); hp += 3; send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, - NULL, TRACE_FUN_CALL, am_exception_from, mfa_tuple, cv, am_true); + NULL, TRACE_FUN_T_CALL, am_exception_from, mfa_tuple, cv, am_true); } /* @@ -1105,7 +1123,8 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, * use process flags */ tracee_flags = &ERTS_TRACE_FLAGS(p); - if (!is_tracer_proc_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, am_call)) { + if (!is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, + TRACE_FUN_E_CALL, am_call)) { return 0; } } else { @@ -1120,7 +1139,9 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, } meta_flags = F_TRACE_CALLS | F_NOW_TS; tracee_flags = &meta_flags; - switch (call_enabled_tracer(p, *tracer, &tnif, am_call, p->common.id)) { + switch (call_enabled_tracer(p, *tracer, + &tnif, TRACE_FUN_T_CALL, + am_call, p->common.id)) { default: case am_remove: *tracer = erts_tracer_nil; case am_discard: return 0; @@ -1244,7 +1265,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, * Build the trace tuple and send it to the port. */ send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, - tnif, TRACE_FUN_CALL, am_call, mfa_tuple, THE_NON_VALUE, pam_result); + tnif, TRACE_FUN_T_CALL, am_call, mfa_tuple, THE_NON_VALUE, pam_result); erts_match_set_release_result(p); if (match_spec && tracer == &pre_ms_tracer) @@ -1266,8 +1287,9 @@ trace_proc(Process *c_p, ErtsProcLocks c_p_locks, Process *t_p, Eterm what, Eterm data) { ErtsTracerNif *tnif = NULL; - if (is_tracer_proc_enabled(c_p, c_p_locks, &t_p->common, &tnif, what)) - send_to_tracer_nif(c_p, &t_p->common, t_p->common.id, tnif, TRACE_FUN_PROCS, + if (is_tracer_enabled(c_p, c_p_locks, &t_p->common, &tnif, + TRACE_FUN_E_PROCS, what)) + send_to_tracer_nif(c_p, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PROCS, what, data, THE_NON_VALUE); } @@ -1284,16 +1306,16 @@ trace_proc_spawn(Process *p, Eterm what, Eterm pid, Eterm mod, Eterm func, Eterm args) { ErtsTracerNif *tnif = NULL; - if (is_tracer_proc_enabled(p, ERTS_PROC_LOCKS_ALL & + if (is_tracer_enabled(p, ERTS_PROC_LOCKS_ALL & ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE), - &p->common, &tnif, what)) { + &p->common, &tnif, TRACE_FUN_E_PROCS, what)) { Eterm mfa; Eterm* hp; hp = HAlloc(p, 4); mfa = TUPLE3(hp, mod, func, args); hp += 4; - send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_PROCS, + send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_PROCS, what, pid, mfa); } } @@ -1334,8 +1356,7 @@ trace_gc(Process *p, Eterm what, Uint size) Uint sz = 0; Eterm tup; - if (is_tracer_proc_enabled( - p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, what)) { + if (is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, TRACE_FUN_E_GC, what)) { (void) erts_process_gc_info(p, &sz, NULL); hp = HAlloc(p, sz + 3 + 2); @@ -1344,7 +1365,7 @@ trace_gc(Process *p, Eterm what, Uint size) tup = TUPLE2(hp, am_wordsize, make_small(size)); hp += 3; msg = CONS(hp, tup, msg); hp += 2; - send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_GC, + send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_GC, what, msg, am_undefined); } } @@ -1758,8 +1779,8 @@ void trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) { ErtsTracerNif *tnif = NULL; ERTS_SMP_CHK_NO_PROC_LOCKS; - if (is_tracer_proc_enabled(NULL, 0, &p->common, &tnif, am_open)) - send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, TRACE_FUN_PORTS, + if (is_tracer_enabled(NULL, 0, &p->common, &tnif, TRACE_FUN_E_PORTS, am_open)) + send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, TRACE_FUN_T_PORTS, am_open, calling_pid, drv_name); } @@ -1777,8 +1798,8 @@ trace_port(Port *t_p, Eterm what, Eterm data) { ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) || erts_thr_progress_is_blocking()); ERTS_SMP_CHK_NO_PROC_LOCKS; - if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, what)) - send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_PORTS, + if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_PORTS, what)) + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PORTS, what, data, THE_NON_VALUE); } @@ -1823,7 +1844,7 @@ trace_port_receive(Port *t_p, Eterm caller, Eterm what, ...) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) || erts_thr_progress_is_blocking()); ERTS_SMP_CHK_NO_PROC_LOCKS; - if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, am_receive)) { + if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_RECEIVE, am_receive)) { /* We can use a stack heap here, as the nif is called in the context of a port */ #define LOCAL_HEAP_SIZE (3 + 3 + heap_bin_size(ERL_ONHEAP_BIN_LIMIT) + 3) @@ -1915,7 +1936,7 @@ trace_port_receive(Port *t_p, Eterm caller, Eterm what, ...) } data = TUPLE2(hp, caller, data); - send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_RECEIVE, + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_RECEIVE, am_receive, data, THE_NON_VALUE); if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0) @@ -1937,8 +1958,8 @@ trace_port_send(Port *t_p, Eterm receiver, Eterm msg, int exists) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) || erts_thr_progress_is_blocking()); ERTS_SMP_CHK_NO_PROC_LOCKS; - if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, op)) - send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_SEND, + if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SEND, op)) + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_SEND, op, msg, receiver); } @@ -1948,7 +1969,7 @@ void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) || erts_thr_progress_is_blocking()); ERTS_SMP_CHK_NO_PROC_LOCKS; - if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, am_send)) { + if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SEND, am_send)) { Eterm msg; Binary* bptr = NULL; #define LOCAL_HEAP_SIZE (3 + 3 + heap_bin_size(ERL_ONHEAP_BIN_LIMIT)) @@ -1967,7 +1988,7 @@ void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz) msg = TUPLE2(hp, t_p->common.id, msg); hp += 3; - send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_SEND, + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_SEND, am_send, msg, to); if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0) erts_bin_free(bptr); @@ -1996,9 +2017,9 @@ trace_sched_ports_where(Port *t_p, Eterm what, Eterm where) { ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) || erts_thr_progress_is_blocking()); ERTS_SMP_CHK_NO_PROC_LOCKS; - if (is_tracer_proc_enabled(NULL, 0, &t_p->common, &tnif, what)) + if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_RUNNING, what)) send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, - tnif, TRACE_FUN_RUNNING, + tnif, TRACE_FUN_T_RUNNING, what, where, THE_NON_VALUE); } @@ -2570,33 +2591,62 @@ static void init_tracer_template(ErtsTracerNif *tnif) { tnif->tracers[TRACE_FUN_ENABLED].cb = NULL; /* specific tracer functions */ - tnif->tracers[TRACE_FUN_SEND].name = "trace_send"; - tnif->tracers[TRACE_FUN_SEND].arity = 6; - tnif->tracers[TRACE_FUN_SEND].cb = NULL; + tnif->tracers[TRACE_FUN_T_SEND].name = "trace_send"; + tnif->tracers[TRACE_FUN_T_SEND].arity = 6; + tnif->tracers[TRACE_FUN_T_SEND].cb = NULL; + + tnif->tracers[TRACE_FUN_T_RECEIVE].name = "trace_receive"; + tnif->tracers[TRACE_FUN_T_RECEIVE].arity = 6; + tnif->tracers[TRACE_FUN_T_RECEIVE].cb = NULL; + + tnif->tracers[TRACE_FUN_T_CALL].name = "trace_call"; + tnif->tracers[TRACE_FUN_T_CALL].arity = 6; + tnif->tracers[TRACE_FUN_T_CALL].cb = NULL; + + tnif->tracers[TRACE_FUN_T_RUNNING].name = "trace_running"; + tnif->tracers[TRACE_FUN_T_RUNNING].arity = 6; + tnif->tracers[TRACE_FUN_T_RUNNING].cb = NULL; - tnif->tracers[TRACE_FUN_RECEIVE].name = "trace_receive"; - tnif->tracers[TRACE_FUN_RECEIVE].arity = 6; - tnif->tracers[TRACE_FUN_RECEIVE].cb = NULL; + tnif->tracers[TRACE_FUN_T_GC].name = "trace_garbage_collection"; + tnif->tracers[TRACE_FUN_T_GC].arity = 6; + tnif->tracers[TRACE_FUN_T_GC].cb = NULL; - tnif->tracers[TRACE_FUN_CALL].name = "trace_call"; - tnif->tracers[TRACE_FUN_CALL].arity = 6; - tnif->tracers[TRACE_FUN_CALL].cb = NULL; + tnif->tracers[TRACE_FUN_T_PROCS].name = "trace_procs"; + tnif->tracers[TRACE_FUN_T_PROCS].arity = 6; + tnif->tracers[TRACE_FUN_T_PROCS].cb = NULL; - tnif->tracers[TRACE_FUN_RUNNING].name = "trace_running"; - tnif->tracers[TRACE_FUN_RUNNING].arity = 6; - tnif->tracers[TRACE_FUN_RUNNING].cb = NULL; + tnif->tracers[TRACE_FUN_T_PORTS].name = "trace_ports"; + tnif->tracers[TRACE_FUN_T_PORTS].arity = 6; + tnif->tracers[TRACE_FUN_T_PORTS].cb = NULL; - tnif->tracers[TRACE_FUN_GC].name = "trace_garbage_collection"; - tnif->tracers[TRACE_FUN_GC].arity = 6; - tnif->tracers[TRACE_FUN_GC].cb = NULL; + /* specific enabled functions */ + tnif->tracers[TRACE_FUN_E_SEND].name = "enabled_send"; + tnif->tracers[TRACE_FUN_E_SEND].arity = 3; + tnif->tracers[TRACE_FUN_E_SEND].cb = NULL; - tnif->tracers[TRACE_FUN_PROCS].name = "trace_procs"; - tnif->tracers[TRACE_FUN_PROCS].arity = 6; - tnif->tracers[TRACE_FUN_PROCS].cb = NULL; + tnif->tracers[TRACE_FUN_E_RECEIVE].name = "enabled_receive"; + tnif->tracers[TRACE_FUN_E_RECEIVE].arity = 3; + tnif->tracers[TRACE_FUN_E_RECEIVE].cb = NULL; - tnif->tracers[TRACE_FUN_PORTS].name = "trace_ports"; - tnif->tracers[TRACE_FUN_PORTS].arity = 6; - tnif->tracers[TRACE_FUN_PORTS].cb = NULL; + tnif->tracers[TRACE_FUN_E_CALL].name = "enabled_call"; + tnif->tracers[TRACE_FUN_E_CALL].arity = 3; + tnif->tracers[TRACE_FUN_E_CALL].cb = NULL; + + tnif->tracers[TRACE_FUN_E_RUNNING].name = "enabled_running"; + tnif->tracers[TRACE_FUN_E_RUNNING].arity = 3; + tnif->tracers[TRACE_FUN_E_RUNNING].cb = NULL; + + tnif->tracers[TRACE_FUN_E_GC].name = "enabled_garbage_collection"; + tnif->tracers[TRACE_FUN_E_GC].arity = 3; + tnif->tracers[TRACE_FUN_E_GC].cb = NULL; + + tnif->tracers[TRACE_FUN_E_PROCS].name = "enabled_procs"; + tnif->tracers[TRACE_FUN_E_PROCS].arity = 3; + tnif->tracers[TRACE_FUN_E_PROCS].cb = NULL; + + tnif->tracers[TRACE_FUN_E_PORTS].name = "enabled_ports"; + tnif->tracers[TRACE_FUN_E_PORTS].arity = 3; + tnif->tracers[TRACE_FUN_E_PORTS].cb = NULL; } static Hash *tracer_hash = NULL; @@ -2739,6 +2789,7 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee, flatmap_t *map = (flatmap_t*)(local_heap+(MAP_SIZE+1)); Eterm *map_values = flatmap_get_values(map); enum ErtsTracerOpt topt = (tnif->tracers[topt_arg].cb) ? topt_arg : TRACE_FUN_DEFAULT; + ASSERT(topt < NIF_TRACER_TYPES); argv[0] = tag; argv[1] = ERTS_TRACER_STATE(tracer); @@ -2808,26 +2859,29 @@ send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, static ERTS_INLINE Eterm call_enabled_tracer(Process *c_p, const ErtsTracer tracer, - ErtsTracerNif **tnif_ret, Eterm tag, Eterm t_p_id) -{ + ErtsTracerNif **tnif_ret, + enum ErtsTracerOpt topt, + Eterm tag, Eterm t_p_id) { ErtsTracerNif *tnif = lookup_tracer_nif(tracer); if (tnif) { Eterm argv[] = {tag, ERTS_TRACER_STATE(tracer), t_p_id}; + topt = (tnif->tracers[topt].cb) ? topt : TRACE_FUN_ENABLED; + ASSERT(topt < NIF_TRACER_TYPES); + ASSERT(tnif->tracers[topt].cb != NULL); if (tnif_ret) *tnif_ret = tnif; - return erts_nif_call_function( - c_p, NULL, tnif->nif_mod, - tnif->tracers[TRACE_FUN_ENABLED].cb, - tnif->tracers[TRACE_FUN_ENABLED].arity, - argv); + return erts_nif_call_function(c_p, NULL, tnif->nif_mod, + tnif->tracers[topt].cb, + tnif->tracers[topt].arity, + argv); } return am_remove; } static int -is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, - ErtsPTabElementCommon *t_p, - ErtsTracerNif **tnif_ret, Eterm tag) -{ +is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks, + ErtsPTabElementCommon *t_p, + ErtsTracerNif **tnif_ret, + enum ErtsTracerOpt topt, Eterm tag) { Eterm nif_result; #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) @@ -2845,7 +2899,7 @@ is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, } #endif - nif_result = call_enabled_tracer(c_p, t_p->tracer, tnif_ret, tag, t_p->id); + nif_result = call_enabled_tracer(c_p, t_p->tracer, tnif_ret, topt, tag, t_p->id); switch (nif_result) { case am_discard: return 0; case am_trace: return 1; @@ -2878,14 +2932,13 @@ is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, erts_smp_proc_unlock(c_p, c_p_xlocks); } - return 0; } int erts_is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, ErtsPTabElementCommon *t_p, Eterm type) { - return is_tracer_proc_enabled(c_p, c_p_locks, t_p, NULL, am_trace_status); + return is_tracer_enabled(c_p, c_p_locks, t_p, NULL, TRACE_FUN_ENABLED, am_trace_status); } int erts_is_tracer_enabled(Process *c_p, const ErtsTracer tracer) @@ -2893,7 +2946,7 @@ int erts_is_tracer_enabled(Process *c_p, const ErtsTracer tracer) ErtsTracerNif *tnif = lookup_tracer_nif(tracer); if (tnif) { Eterm nif_result = call_enabled_tracer(c_p, tracer, &tnif, - am_trace_status, + TRACE_FUN_ENABLED, am_trace_status, c_p->common.id); switch (nif_result) { case am_discard: -- cgit v1.2.3 From 4e2a61519f022a85e7774d9be65067d33256cb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 20 Apr 2016 11:58:01 +0200 Subject: erts: Extend 'enabled' and 'trace' tracer callbacks Adds the following capabilities to a tracer backend * enabled_running_procs/3 changed from enabled_running/3 * enabled_running_ports/3 * trace_running_procs/6 changed from trace_running/6 * trace_running_ports/6 --- erts/emulator/beam/erl_trace.c | 59 +++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index a2f94677bb..7b52959dd8 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -367,20 +367,22 @@ enum ErtsTracerOpt { TRACE_FUN_T_SEND = 2, TRACE_FUN_T_RECEIVE = 3, TRACE_FUN_T_CALL = 4, - TRACE_FUN_T_RUNNING = 5, - TRACE_FUN_T_GC = 6, - TRACE_FUN_T_PROCS = 7, - TRACE_FUN_T_PORTS = 8, - TRACE_FUN_E_SEND = 9, - TRACE_FUN_E_RECEIVE = 10, - TRACE_FUN_E_CALL = 11, - TRACE_FUN_E_RUNNING = 12, - TRACE_FUN_E_GC = 13, - TRACE_FUN_E_PROCS = 14, - TRACE_FUN_E_PORTS = 15 + TRACE_FUN_T_SCHED_PROC = 5, + TRACE_FUN_T_SCHED_PORT = 6, + TRACE_FUN_T_GC = 7, + TRACE_FUN_T_PROCS = 8, + TRACE_FUN_T_PORTS = 9, + TRACE_FUN_E_SEND = 10, + TRACE_FUN_E_RECEIVE = 11, + TRACE_FUN_E_CALL = 12, + TRACE_FUN_E_SCHED_PROC = 13, + TRACE_FUN_E_SCHED_PORT = 14, + TRACE_FUN_E_GC = 15, + TRACE_FUN_E_PROCS = 16, + TRACE_FUN_E_PORTS = 17 }; -#define NIF_TRACER_TYPES (16) +#define NIF_TRACER_TYPES (18) static ERTS_INLINE int @@ -768,7 +770,7 @@ trace_sched_aux(Process *p, ErtsProcLocks locks, Eterm what) break; } - if (!is_tracer_enabled(p, locks, &p->common, &tnif, TRACE_FUN_E_RUNNING, what)) + if (!is_tracer_enabled(p, locks, &p->common, &tnif, TRACE_FUN_E_SCHED_PROC, what)) return; if (ERTS_PROC_IS_EXITING(p)) @@ -787,7 +789,7 @@ trace_sched_aux(Process *p, ErtsProcLocks locks, Eterm what) hp += 4; } - send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_RUNNING, + send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SCHED_PROC, what, tmp, THE_NON_VALUE); } @@ -2017,9 +2019,9 @@ trace_sched_ports_where(Port *t_p, Eterm what, Eterm where) { ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p) || erts_thr_progress_is_blocking()); ERTS_SMP_CHK_NO_PROC_LOCKS; - if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_RUNNING, what)) + if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SCHED_PORT, what)) send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, - tnif, TRACE_FUN_T_RUNNING, + tnif, TRACE_FUN_T_SCHED_PORT, what, where, THE_NON_VALUE); } @@ -2603,9 +2605,13 @@ static void init_tracer_template(ErtsTracerNif *tnif) { tnif->tracers[TRACE_FUN_T_CALL].arity = 6; tnif->tracers[TRACE_FUN_T_CALL].cb = NULL; - tnif->tracers[TRACE_FUN_T_RUNNING].name = "trace_running"; - tnif->tracers[TRACE_FUN_T_RUNNING].arity = 6; - tnif->tracers[TRACE_FUN_T_RUNNING].cb = NULL; + tnif->tracers[TRACE_FUN_T_SCHED_PROC].name = "trace_running_procs"; + tnif->tracers[TRACE_FUN_T_SCHED_PROC].arity = 6; + tnif->tracers[TRACE_FUN_T_SCHED_PROC].cb = NULL; + + tnif->tracers[TRACE_FUN_T_SCHED_PORT].name = "trace_running_ports"; + tnif->tracers[TRACE_FUN_T_SCHED_PORT].arity = 6; + tnif->tracers[TRACE_FUN_T_SCHED_PORT].cb = NULL; tnif->tracers[TRACE_FUN_T_GC].name = "trace_garbage_collection"; tnif->tracers[TRACE_FUN_T_GC].arity = 6; @@ -2632,9 +2638,13 @@ static void init_tracer_template(ErtsTracerNif *tnif) { tnif->tracers[TRACE_FUN_E_CALL].arity = 3; tnif->tracers[TRACE_FUN_E_CALL].cb = NULL; - tnif->tracers[TRACE_FUN_E_RUNNING].name = "enabled_running"; - tnif->tracers[TRACE_FUN_E_RUNNING].arity = 3; - tnif->tracers[TRACE_FUN_E_RUNNING].cb = NULL; + tnif->tracers[TRACE_FUN_E_SCHED_PROC].name = "enabled_running_procs"; + tnif->tracers[TRACE_FUN_E_SCHED_PROC].arity = 3; + tnif->tracers[TRACE_FUN_E_SCHED_PROC].cb = NULL; + + tnif->tracers[TRACE_FUN_E_SCHED_PORT].name = "enabled_running_ports"; + tnif->tracers[TRACE_FUN_E_SCHED_PORT].arity = 3; + tnif->tracers[TRACE_FUN_E_SCHED_PORT].cb = NULL; tnif->tracers[TRACE_FUN_E_GC].name = "enabled_garbage_collection"; tnif->tracers[TRACE_FUN_E_GC].arity = 3; @@ -2780,7 +2790,7 @@ static ERTS_INLINE int send_to_tracer_nif_raw(Process *c_p, Process *tracee, const ErtsTracer tracer, Uint tracee_flags, Eterm t_p_id, ErtsTracerNif *tnif, - enum ErtsTracerOpt topt_arg, + enum ErtsTracerOpt topt, Eterm tag, Eterm msg, Eterm extra, Eterm pam_result) { if (tnif || (tnif = lookup_tracer_nif(tracer)) != NULL) { @@ -2788,7 +2798,8 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee, Eterm argv[6], local_heap[3+MAP_SIZE /* values */ + (MAP_SIZE+1 /* keys */)]; flatmap_t *map = (flatmap_t*)(local_heap+(MAP_SIZE+1)); Eterm *map_values = flatmap_get_values(map); - enum ErtsTracerOpt topt = (tnif->tracers[topt_arg].cb) ? topt_arg : TRACE_FUN_DEFAULT; + + topt = (tnif->tracers[topt].cb) ? topt : TRACE_FUN_DEFAULT; ASSERT(topt < NIF_TRACER_TYPES); argv[0] = tag; -- cgit v1.2.3 From 4a3d9ea99f54278640f9c958af5f8389bf7ddde5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 20 Apr 2016 16:18:36 +0200 Subject: erts: Update erl_tracer type specs --- erts/preloaded/src/erl_tracer.erl | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl index 2177e48f60..de1e9ca01e 100644 --- a/erts/preloaded/src/erl_tracer.erl +++ b/erts/preloaded/src/erl_tracer.erl @@ -3,12 +3,29 @@ -export([enabled/3, trace/6, on_load/0]). -type tracee() :: port() | pid() | undefined. --type trace_tag() :: send | send_to_non_existing_process | 'receive' | - call | return_to | return_from | exception_from | - spawn | spawned | exit | link | unlink | getting_linked | - getting_unlinked | register | unregister | in | out | - in_exiting | out_exiting | out_exited | - open | closed | gc_start | gc_end. + +-type trace_tag_running_ports() :: in | out | in_exiting | out_exiting | out_exited. +-type trace_tag_running_procs() :: in | out | in_exiting | out_exiting | out_exited. +-type trace_tag_send() :: send | send_to_non_existing_process. +-type trace_tag_receive() :: 'receive'. +-type trace_tag_call() :: call | return_to | return_from | exception_from. +-type trace_tag_procs() :: spawn | spawned | exit | link | unlink + | getting_linked | getting_unlinked + | register | unregister. +-type trace_tag_ports() :: open | closed | link | unlink + | getting_linked | getting_unlinked. +-type trace_tag_gc() :: gc_minor_start | gc_minor_end + | gc_major_start | gc_major_end. + +-type trace_tag() :: trace_tag_send() + | trace_tag_receive() + | trace_tag_call() + | trace_tag_procs() + | trace_tag_ports() + | trace_tag_running_procs() + | trace_tag_running_ports() + | trace_tag_gc(). + -type trace_opts() :: #{ match_spec_result => true | term(), scheduler_id => undefined | non_neg_integer(), timestamp => undefined | timestamp | cpu_timestamp | -- cgit v1.2.3 From 9905d983c7225493a03489b843587ad7d21cbd3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 20 Apr 2016 16:38:23 +0200 Subject: Update preloaded erl_tracer.beam --- erts/preloaded/ebin/erl_tracer.beam | Bin 1924 -> 2128 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam index ffe5d5631c..b8f3f6d8c2 100644 Binary files a/erts/preloaded/ebin/erl_tracer.beam and b/erts/preloaded/ebin/erl_tracer.beam differ -- cgit v1.2.3 From b916d55464f26be9e309753d37608b2c99af5944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 20 Apr 2016 19:09:15 +0200 Subject: erts: Fix erl_tracer documentation typos --- erts/doc/src/erl_tracer.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml index 1e8e78b25f..ee828919de 100644 --- a/erts/doc/src/erl_tracer.xml +++ b/erts/doc/src/erl_tracer.xml @@ -114,11 +114,11 @@ TraceTag = trace_tag() | trace_status TracerState = term() - Tracee = tracee() + Tracee = tracee() Result = trace | discard | remove -

This callback will be called whenever a trace point is triggered. It +

This callback will be called whenever a tracepoint is triggered. It allows the tracer to decide whether a trace should be generated or not. This check is made as early as possible in order to limit the amount of overhead associated with tracing. If trace is returned the @@ -132,7 +132,7 @@ to check if the tracer should still be active. It is called in multiple scenarios, but most significantly it is used when tracing is started using this tracer.

-

This function may be called multiple times per trace point, so it +

This function may be called multiple times per tracepoint, so it is important that it is both fast and side effect free.

@@ -143,17 +143,17 @@ TraceTag = trace_tag() TracerState = term() - Tracee = tracee() + Tracee = tracee() FirstTraceTerm = term() SecondTraceTerm = term() | undefined Opts = trace_opts() Result = ok -

This callback will be called when a trace point is triggered and +

This callback will be called when a tracepoint is triggered and the Module:enabled/3 callback returned trace. In it any side effects needed by - the tracer should be done. The trace point payload is located in + the tracer should be done. The tracepoint payload is located in the FirstTraceTerm and SecondTraceTerm. The content of the TraceTerms depends on which TraceTag has been triggered. The FirstTraceTerm and SecondTraceTerm correspond to the @@ -282,7 +282,7 @@ static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifPid to_pid; if (enif_get_local_pid(env, argv[1], &to_pid)) if (!enif_is_process_alive(env, &to_pid)) - /* tracer is dead so we should remove this trace point */ + /* tracer is dead so we should remove this tracepoint */ return enif_make_atom(env, "remove"); /* Only generate trace for when tracer != tracee */ -- cgit v1.2.3 From 4e90ab912839db19f046d5eb362fa8ce92bcf67d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 20 Apr 2016 19:06:58 +0200 Subject: erts: Update erl_tracer documentation --- erts/doc/src/erl_tracer.xml | 344 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml index ee828919de..2075b962d8 100644 --- a/erts/doc/src/erl_tracer.xml +++ b/erts/doc/src/erl_tracer.xml @@ -54,6 +54,14 @@ + + + + + + + + @@ -105,6 +113,29 @@ CALLBACK FUNCTIONS

The following functions should be exported from a erl_tracer callback module.

+ + Module:enabled/3 + Mandatory + Module:trace/6 + Mandatory + Module:enabled_procs/3 + Optional + Module:trace_procs/6 + Optional + Module:enabled_ports/3 + Optional + Module:trace_ports/6 + Optional + Module:enabled_running_ports/3 + Optional + Module:trace_running_ports/6 + Optional + Module:enabled_running_procs/3 + Optional + Module:trace_running_procs/6 + Optional + +
@@ -181,6 +212,319 @@ see the seq_trace manual.

+ + + + Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_procs() + TracerState = term() + Tracee = tracee() + Result = trace | discard | remove + + +

This callback will be called whenever a tracepoint with trace flag + procs + is triggered.

+

If enabled_procs/3 is not defined enabled/3 will be called instead.

+
+
+ + + + Module:trace_procs(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag() + TracerState = term() + Tracee = tracee() + FirstTraceTerm = term() + SecondTraceTerm = term() | undefined + Opts = trace_opts() + Result = ok + + +

This callback will be called when a tracepoint is triggered and + the Module:enabled_procs/3 + callback returned trace.

+

If trace_procs/6 is not defined trace/6 will be called instead.

+
+
+ + + + Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_ports() + TracerState = term() + Tracee = tracee() + Result = trace | discard | remove + + +

This callback will be called whenever a tracepoint with trace flag + ports + is triggered.

+

If enabled_ports/3 is not defined enabled/3 will be called instead.

+
+
+ + + + Module:trace_ports(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag() + TracerState = term() + Tracee = tracee() + FirstTraceTerm = term() + SecondTraceTerm = term() | undefined + Opts = trace_opts() + Result = ok + + +

This callback will be called when a tracepoint is triggered and + the Module:enabled_ports/3 + callback returned trace.

+

If trace_ports/6 is not defined trace/6 will be called instead.

+
+
+ + + + Module:enabled_running_procs(TraceTag, TracerState, Tracee) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_running_procs() + TracerState = term() + Tracee = tracee() + Result = trace | discard | remove + + +

This callback will be called whenever a tracepoint with trace flag + running_procs | running + is triggered.

+

If enabled_running_procs/3 is not defined enabled/3 will be called instead.

+
+
+ + + + Module:trace_running_procs(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_running_procs() + TracerState = term() + Tracee = tracee() + FirstTraceTerm = term() + SecondTraceTerm = term() | undefined + Opts = trace_opts() + Result = ok + + +

This callback will be called when a tracepoint is triggered and + the Module:enabled_running_procs/3 + callback returned trace.

+

If trace_running_procs/6 is not defined trace/6 will be called instead.

+
+
+ + + + Module:enabled_running_ports(TraceTag, TracerState, Tracee) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_running_ports() + TracerState = term() + Tracee = tracee() + Result = trace | discard | remove + + +

This callback will be called whenever a tracepoint with trace flag + running_ports + is triggered.

+

If enabled_running_ports/3 is not defined enabled/3 will be called instead.

+
+
+ + + + Module:trace_running_ports(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_running_ports() + TracerState = term() + Tracee = tracee() + FirstTraceTerm = term() + SecondTraceTerm = term() | undefined + Opts = trace_opts() + Result = ok + + +

This callback will be called when a tracepoint is triggered and + the Module:enabled_running_ports/3 + callback returned trace.

+

If trace_running_ports/6 is not defined trace/6 will be called instead.

+
+
+ + + + Module:enabled_call(TraceTag, TracerState, Tracee) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_call() + TracerState = term() + Tracee = tracee() + Result = trace | discard | remove + + +

This callback will be called whenever a tracepoint with trace flag + call | return_to + is triggered.

+

If enabled_call/3 is not defined enabled/3 will be called instead.

+
+
+ + + + Module:trace_call(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_call() + TracerState = term() + Tracee = tracee() + FirstTraceTerm = term() + SecondTraceTerm = term() | undefined + Opts = trace_opts() + Result = ok + + +

This callback will be called when a tracepoint is triggered and + the Module:enabled_call/3 + callback returned trace.

+

If trace_call/6 is not defined trace/6 will be called instead.

+
+
+ + + + Module:enabled_send(TraceTag, TracerState, Tracee) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_send() + TracerState = term() + Tracee = tracee() + Result = trace | discard | remove + + +

This callback will be called whenever a tracepoint with trace flag + send + is triggered.

+

If enabled_send/3 is not defined enabled/3 will be called instead.

+
+
+ + + + Module:trace_send(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_send() + TracerState = term() + Tracee = tracee() + FirstTraceTerm = term() + SecondTraceTerm = term() | undefined + Opts = trace_opts() + Result = ok + + +

This callback will be called when a tracepoint is triggered and + the Module:enabled_send/3 + callback returned trace.

+

If trace_send/6 is not defined trace/6 will be called instead.

+
+
+ + + + Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_receive() + TracerState = term() + Tracee = tracee() + Result = trace | discard | remove + + +

This callback will be called whenever a tracepoint with trace flag + 'receive' + is triggered.

+

If enabled_receive/3 is not defined enabled/3 will be called instead.

+
+
+ + + + Module:trace_receive(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_receive() + TracerState = term() + Tracee = tracee() + FirstTraceTerm = term() + SecondTraceTerm = term() | undefined + Opts = trace_opts() + Result = ok + + +

This callback will be called when a tracepoint is triggered and + the Module:enabled_receive/3 + callback returned trace.

+

If trace_receive/6 is not defined trace/6 will be called instead.

+
+
+ + + + Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_gc() + TracerState = term() + Tracee = tracee() + Result = trace | discard | remove + + +

This callback will be called whenever a tracepoint with trace flag + garbage_collection + is triggered.

+

If enabled_garbage_collection/3 is not defined enabled/3 will be called instead.

+
+
+ + + + Module:trace_garbage_collection(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Check if a trace event should be generated. + + TraceTag = trace_tag_gc() + TracerState = term() + Tracee = tracee() + FirstTraceTerm = term() + SecondTraceTerm = term() | undefined + Opts = trace_opts() + Result = ok + + +

This callback will be called when a tracepoint is triggered and + the Module:enabled_garbage_collection/3 + callback returned trace.

+

If trace_garbage_collection/6 is not defined trace/6 will be called instead.

+
+
+
-- cgit v1.2.3 From a1f50deb9c87cbb7117baf22928c6cd7075ac2e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 20 Apr 2016 20:23:56 +0200 Subject: erts: Fix return_to trace callback --- erts/emulator/beam/erl_trace.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 7b52959dd8..56899f574a 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -406,10 +406,6 @@ is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks, ErtsTracerNif **tnif_ret, enum ErtsTracerOpt topt, Eterm tag); -#define SEND_TO_TRACER(c_p, tag, msg) \ - send_to_tracer_nif(c_p, &(c_p)->common, (c_p)->common.id, NULL, \ - TRACE_FUN_DEFAULT, tag, msg, THE_NON_VALUE) - static Uint active_sched; void @@ -966,7 +962,8 @@ erts_trace_return_to(Process *p, BeamInstr *pc) mfa = TUPLE3(hp, code_ptr[0], code_ptr[1], make_small(code_ptr[2])); } - SEND_TO_TRACER(p, am_return_to, mfa); + send_to_tracer_nif(p, &p->common, p->common.id, NULL, TRACE_FUN_T_CALL, + am_return_to, mfa, THE_NON_VALUE); } -- cgit v1.2.3 From db50b217552ea8152cd09d01f25cc0c26205134d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 25 Apr 2016 11:10:08 +0200 Subject: erts: Fix gc messages in trace_port_SUITE --- erts/emulator/test/trace_port_SUITE.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index 1068c1d22d..a66563d15b 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -236,13 +236,13 @@ gc(Config) when is_list(Config) -> trace_info(Garber, flags), Garber ! hi, - expect({trace,Garber,gc_start,info}), - expect({trace,Garber,gc_end,info}), + expect({trace,Garber,gc_major_start,info}), + expect({trace,Garber,gc_major_end,info}), trac(Garber, true, [garbage_collection,timestamp]), Garber ! hi, - expect({trace_ts,Garber,gc_start,info,ts}), - expect({trace_ts,Garber,gc_end,info,ts}), + expect({trace_ts,Garber,gc_major_start,info,ts}), + expect({trace_ts,Garber,gc_major_end,info,ts}), ok. -- cgit v1.2.3 From e4f57b7fbf22b154c6dec11e49283b1ce6886e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 25 Apr 2016 11:12:19 +0200 Subject: erts: Fix gc messages in sensitive_SUITE --- erts/emulator/test/sensitive_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/sensitive_SUITE.erl b/erts/emulator/test/sensitive_SUITE.erl index b7ff4c109c..c3e303bbd1 100644 --- a/erts/emulator/test/sensitive_SUITE.erl +++ b/erts/emulator/test/sensitive_SUITE.erl @@ -311,7 +311,7 @@ gc_trace(Config) when is_list(Config) -> wait_trace(Self), {messages,Messages} = process_info(Tracer, messages), - [{trace,Self,gc_start,_},{trace,Self,gc_end,_}] = Messages, + [{trace,Self,gc_major_start,_},{trace,Self,gc_major_end,_}] = Messages, unlink(Tracer), exit(Tracer, kill), ok. -- cgit v1.2.3 From 699f112db3419c6f1f64e4fac5c617f4e7d2bb2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 25 Apr 2016 11:27:20 +0200 Subject: erts: Fix gc messages in tracer_SUITE --- erts/emulator/test/tracer_SUITE.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/tracer_SUITE.erl b/erts/emulator/test/tracer_SUITE.erl index 812e834562..de44d6656a 100644 --- a/erts/emulator/test/tracer_SUITE.erl +++ b/erts/emulator/test/tracer_SUITE.erl @@ -468,12 +468,12 @@ gc_start(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {Pid, gc_start, State, Pid, _, undefined, Opts} = Msg, + {Pid, gc_major_start, State, Pid, _, undefined, Opts} = Msg, check_opts(EOpts, Opts) end end, - test(gc_start, garbage_collection, Tc, Expect, true). + test(gc_major_start, garbage_collection, Tc, Expect, true). gc_end(_Config) -> @@ -488,12 +488,12 @@ gc_end(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {Pid, gc_end, State, Pid, _, undefined, Opts} = Msg, + {Pid, gc_major_end, State, Pid, _, undefined, Opts} = Msg, check_opts(EOpts, Opts) end end, - test(gc_end, garbage_collection, Tc, Expect, true). + test(gc_major_end, garbage_collection, Tc, Expect, true). test(Event, Tc, Expect) -> test(Event, Tc, Expect, true). -- cgit v1.2.3 From e75fdd38b04896fa6a1ac7b2fdea18ef485c70b3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 22 Jan 2016 20:10:11 +0100 Subject: erts: Clarify docs for trace_pattern/3 --- erts/doc/src/erlang.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 423ccdf98f..c7b5ea8867 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -9199,7 +9199,8 @@ timestamp() -> true -

Enables tracing for the matching functions.

+

Enables tracing for the matching functions. + Any match specification is removed.

MatchSpecList -- cgit v1.2.3 From 4050fc5665a4819b15f204543c28c59726b4aac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 12 Apr 2016 07:07:17 +0200 Subject: Refactor erts_finish_loading() and insert_new_code() As a preparation for fixing some issues with -on_load(), we will integrate insert_new_code() into erts_finish_loading. Also rename insert_new_code() to stub_insert_new_code() to make it clear that it will only be used when making a stub module --- erts/emulator/beam/beam_load.c | 53 +++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 2e21a553ed..9bd9b4bfc0 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -479,9 +479,9 @@ static void free_loader_state(Binary* magic); static ErlHeapFragment* new_literal_fragment(Uint size); static void free_literal_fragment(ErlHeapFragment*); static void loader_state_dtor(Binary* magic); -static Eterm insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, - Eterm group_leader, Eterm module, - BeamCodeHeader* code, Uint size); +static Eterm stub_insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, + Eterm group_leader, Eterm module, + BeamCodeHeader* code, Uint size); static int init_iff_file(LoaderState* stp, byte* code, Uint size); static int scan_iff_file(LoaderState* stp, Uint* chunk_types, Uint num_types, Uint num_mandatory); @@ -513,7 +513,7 @@ static GenOp* gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src, static int freeze_code(LoaderState* stp); -static void final_touch(LoaderState* stp); +static void final_touch(LoaderState* stp, struct erl_module_instance* inst_p); static void short_file(int line, LoaderState* stp, unsigned needed); static void load_printf(int line, LoaderState* context, char *fmt, ...); static int transform_engine(LoaderState* st); @@ -768,6 +768,9 @@ erts_finish_loading(Binary* magic, Process* c_p, { Eterm retval; LoaderState* stp = ERTS_MAGIC_BIN_DATA(magic); + Module* mod_tab_p; + struct erl_module_instance* inst_p; + Uint size; /* * No other process may run since we will update the export @@ -784,11 +787,25 @@ erts_finish_loading(Binary* magic, Process* c_p, */ CHKBLK(ERTS_ALC_T_CODE,stp->code); - retval = insert_new_code(c_p, c_p_locks, stp->group_leader, stp->module, - stp->hdr, stp->loaded_size); - if (retval != NIL) { - goto load_error; - } + retval = beam_make_current_old(c_p, c_p_locks, stp->module); + ASSERT(retval == NIL); + + /* + * Update module table. + */ + + size = stp->loaded_size; + erts_total_code_size += size; + mod_tab_p = erts_put_module(stp->module); + inst_p = &mod_tab_p->curr; + inst_p->code_hdr = stp->hdr; + inst_p->code_length = size; + + /* + * Update ranges (used for finding a function from a PC value). + */ + + erts_update_ranges((BeamInstr*)inst_p->code_hdr, size); /* * Ready for the final touch: fixing the export table entries for @@ -796,7 +813,7 @@ erts_finish_loading(Binary* magic, Process* c_p, */ CHKBLK(ERTS_ALC_T_CODE,stp->code); - final_touch(stp); + final_touch(stp, inst_p); /* * Loading succeded. @@ -820,7 +837,6 @@ erts_finish_loading(Binary* magic, Process* c_p, retval = am_on_load; } - load_error: free_loader_state(magic); return retval; } @@ -1026,9 +1042,9 @@ loader_state_dtor(Binary* magic) } static Eterm -insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, - Eterm group_leader, Eterm module, BeamCodeHeader* code_hdr, - Uint size) +stub_insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, + Eterm group_leader, Eterm module, + BeamCodeHeader* code_hdr, Uint size) { Module* modp; Eterm retval; @@ -4700,14 +4716,13 @@ freeze_code(LoaderState* stp) } static void -final_touch(LoaderState* stp) +final_touch(LoaderState* stp, struct erl_module_instance* inst_p) { int i; int on_load = stp->on_load; unsigned catches; Uint index; BeamInstr* codev = stp->codev; - Module* modp; /* * Allocate catch indices and fix up all catch_yf instructions. @@ -4722,8 +4737,7 @@ final_touch(LoaderState* stp) codev[index+2] = make_catch(catches); index = next; } - modp = erts_put_module(stp->module); - modp->curr.catches = catches; + inst_p->catches = catches; /* * Export functions. @@ -6421,7 +6435,8 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) * Insert the module in the module table. */ - rval = insert_new_code(p, 0, p->group_leader, Mod, code_hdr, code_size); + rval = stub_insert_new_code(p, 0, p->group_leader, Mod, + code_hdr, code_size); if (rval != NIL) { goto error; } -- cgit v1.2.3 From 74eafcdaff43d7d84264110066c52fa01e65f159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 2 May 2016 17:36:33 +0200 Subject: erts: Change argument order for LTTng driver_stop * Be consistent. --- erts/emulator/beam/erlang_lttng.h | 6 +++--- erts/emulator/beam/io.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h index 43ceeda671..f7d751252d 100644 --- a/erts/emulator/beam/erlang_lttng.h +++ b/erts/emulator/beam/erlang_lttng.h @@ -218,13 +218,13 @@ TRACEPOINT_EVENT( driver_stop, TP_ARGS( char*, pid, - char*, driver, - char*, port + char*, port, + char*, driver ), TP_FIELDS( ctf_string(pid, pid) - ctf_string(driver, driver) ctf_string(port, port) + ctf_string(driver, driver) ) ) diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index b14ca77a04..5c2595c69d 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -3861,7 +3861,7 @@ terminate_port(Port *prt) lttng_decl_procbuf(proc_str); lttng_pid_to_str(connected_id, proc_str); lttng_port_to_str(prt, port_str); - LTTNG3(driver_stop, proc_str, drv->name, port_str); + LTTNG3(driver_stop, proc_str, port_str, drv->name); } #endif -- cgit v1.2.3 From 95194afd63545c7d60e31b5148584f0707034347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 2 May 2016 17:44:25 +0200 Subject: erts: Rename LTTng tracepoint aio_pool_add to aio_pool_put * Be consistent. --- erts/emulator/beam/erl_async.c | 4 ++-- erts/emulator/beam/erlang_lttng.h | 2 +- erts/emulator/test/lttng_SUITE.erl | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index 4d6d699d9f..84254af0c2 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -283,10 +283,10 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q) erts_thr_q_enqueue(&q->thr_q, a); #ifdef USE_LTTNG_VM_TRACEPOINTS - if (LTTNG_ENABLED(aio_pool_add)) { + if (LTTNG_ENABLED(aio_pool_put)) { lttng_decl_portbuf(port_str); lttng_portid_to_str(a->port, port_str); - LTTNG2(aio_pool_add, port_str, -1); + LTTNG2(aio_pool_put, port_str, -1); } #endif #ifdef USE_VM_PROBES diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h index f7d751252d..12f68e477b 100644 --- a/erts/emulator/beam/erlang_lttng.h +++ b/erts/emulator/beam/erlang_lttng.h @@ -324,7 +324,7 @@ TRACEPOINT_EVENT( TRACEPOINT_EVENT( com_ericsson_otp, - aio_pool_add, + aio_pool_put, TP_ARGS( char*, port, int, length diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl index d0f6292d5b..efc79f42ed 100644 --- a/erts/emulator/test/lttng_SUITE.erl +++ b/erts/emulator/test/lttng_SUITE.erl @@ -89,8 +89,8 @@ end_per_testcase(Case, _Config) -> %% com_ericsson_otp:carrier_pool_put %% com_ericsson_otp:carrier_destroy %% com_ericsson_otp:carrier_create -%% com_ericsson_otp:aio_pool_add -%% com_ericsson_otp:aio_pool_get +%% com_ericsson_otp:aio_pool_put +%% com_ericsson_otp:aio_pool_get %% com_ericsson_otp:driver_control %% com_ericsson_otp:driver_call %% com_ericsson_otp:driver_finish @@ -151,7 +151,7 @@ t_memory_carrier(Config) -> ok end. -%% com_ericsson_otp:aio_pool_add +%% com_ericsson_otp:aio_pool_put %% com_ericsson_otp:aio_pool_get t_async_io_pool(Config) -> case have_async_threads() of @@ -168,7 +168,7 @@ t_async_io_pool(Config) -> {ok, _} = file:list_dir(Path2), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:aio_pool_add", Res), + ok = check_tracepoint("com_ericsson_otp:aio_pool_put", Res), ok = check_tracepoint("com_ericsson_otp:aio_pool_get", Res), ok end. @@ -416,7 +416,7 @@ txt() -> "%% com_ericsson_otp:carrier_pool_put\n" "%% com_ericsson_otp:carrier_destroy\n" "%% com_ericsson_otp:carrier_create\n" - "%% com_ericsson_otp:aio_pool_add\n" + "%% com_ericsson_otp:aio_pool_put\n" "%% com_ericsson_otp:aio_pool_get\n" "%% com_ericsson_otp:driver_control\n" "%% com_ericsson_otp:driver_call\n" -- cgit v1.2.3 From 7b801aa3641f0a59448604c857cfb0e67eed9e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 2 May 2016 18:29:26 +0200 Subject: erts: Update garbage collection trace documentation --- erts/doc/src/erlang.xml | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 423ccdf98f..3ccb76f7b3 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4702,7 +4702,7 @@ os_prompt% detailed information about garbage collection for this process. The content of GCInfo can be changed without prior notice. - See gc_start in + See gc_minor_start in erlang:trace/3 for details about what each item means.

@@ -7966,7 +7966,7 @@ ok stack_size, mbuf_size, old_heap_size, and old_heap_block_size. These tuples are explained in the description of trace message - gc_start (see + gc_minor_start (see erlang:trace/3). New tuples can be added, and the order of the tuples in the Info list can be changed at any time without @@ -8556,7 +8556,7 @@ timestamp() -> garbage_collection

Traces garbage collections of processes.

-

Message tags: gc_start and gc_end.

+

Message tags: gc_minor_start and gc_minor_end.

timestamp @@ -8880,12 +8880,12 @@ timestamp() ->

- - {trace, Pid, gc_start, Info} + + {trace, Pid, gc_minor_start, Info} - -

Sent when garbage collection is about to be started. + +

Sent when a young garbage collection is about to be started. Info is a list of two-element tuples, where the first element is a key, and the second is the value. Do not depend on any order of the tuples. @@ -8925,15 +8925,32 @@ timestamp() ->

All sizes are in words.

- - {trace, Pid, gc_end, Info} + + {trace, Pid, gc_minor_end, Info} -

Sent when garbage collection is finished. Info - contains the same kind of list as in message gc_start, +

Sent when young garbage collection is finished. Info + contains the same kind of list as in message gc_minor_start, but the sizes reflect the new sizes after garbage collection.

+ + + {trace, Pid, gc_major_start, Info} + + +

Sent when fullsweep garbage collection is about to be started. Info + contains the same kind of list as in message gc_minor_start.

+
+ + + {trace, Pid, gc_major_end, Info} + + +

Sent when fullsweep garbage collection is finished. Info + contains the same kind of list as in message gc_minor_start + but the sizes reflect the new sizes after a fullsweep garbage collection.

+

If the tracing process/port dies or the tracer module returns remove, the flags are silently removed.

-- cgit v1.2.3 From ddf385e4da6ecc453b4df99aba654a2b7525c6df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 2 May 2016 18:37:04 +0200 Subject: erts: Remove forgotten atoms in erl_tracer --- erts/emulator/nifs/common/erl_tracer_nif.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/nifs/common/erl_tracer_nif.c b/erts/emulator/nifs/common/erl_tracer_nif.c index 1bb6b940c4..55bfc1a470 100644 --- a/erts/emulator/nifs/common/erl_tracer_nif.c +++ b/erts/emulator/nifs/common/erl_tracer_nif.c @@ -72,8 +72,6 @@ ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload) ATOM_DECL(trace); \ ATOM_DECL(trace_ts); \ ATOM_DECL(true); \ - ATOM_DECL(gc_start); \ - ATOM_DECL(gc_end); \ ATOM_DECL(gc_minor_start); \ ATOM_DECL(gc_minor_end); \ ATOM_DECL(gc_major_start); \ -- cgit v1.2.3 From de455ee22e14e58dc47e7dcbcbbcf7e8c01a47d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 3 May 2016 16:51:43 +0200 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 55812 -> 55800 bytes erts/preloaded/ebin/erl_tracer.beam | Bin 2128 -> 2128 bytes erts/preloaded/ebin/erlang.beam | Bin 104196 -> 104172 bytes erts/preloaded/ebin/erts_code_purger.beam | Bin 8724 -> 8712 bytes erts/preloaded/ebin/erts_internal.beam | Bin 10560 -> 10552 bytes erts/preloaded/ebin/init.beam | Bin 50232 -> 49944 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1468 -> 1460 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1340 -> 1328 bytes erts/preloaded/ebin/prim_file.beam | Bin 44828 -> 44780 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72632 -> 72564 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23184 -> 23172 bytes erts/preloaded/ebin/zlib.beam | Bin 14176 -> 14152 bytes 12 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 77685013ae..3e82b8e20e 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam index b8f3f6d8c2..67d96d0f0d 100644 Binary files a/erts/preloaded/ebin/erl_tracer.beam and b/erts/preloaded/ebin/erl_tracer.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 15e4ca82a7..321e4e7e1b 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index 3e95b1e42e..0c667663fd 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 53dd2897de..ed2e328f5b 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index ee32066f53..23ebd2cad9 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index 51a412b7f7..b63fa24848 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 45bed5cf7e..7c8fdc82c0 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index c25bffb6a5..d4559bc9ed 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 85d74de065..5a0678632e 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 1f8fe6f03b..3d32dd8449 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 059585ec99..ced9f9c952 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From f77ea1c049c585db93618991eb02d9afbfa7ad21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 12 Apr 2016 07:14:57 +0200 Subject: Reimplement -on_load() Load the module as old code; swap old and new code if the -on_load function succeeds. That way, a failed update attempt for a module that has an -on_load function will preserve the previous version of the code. --- erts/emulator/beam/beam_bif_load.c | 63 ++++++++++++++++++++++---------------- erts/emulator/beam/beam_load.c | 58 +++++++++++++++++++++++++++++------ erts/emulator/beam/erl_nif.c | 62 +++++++++++++++++++++---------------- 3 files changed, 122 insertions(+), 61 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 87508dcf5f..b6f5d66166 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -625,8 +625,8 @@ BIF_RETTYPE call_on_load_function_1(BIF_ALIST_1) { Module* modp = erts_get_module(BIF_ARG_1, erts_active_code_ix()); - if (modp && modp->curr.code_hdr) { - BIF_TRAP_CODE_PTR_0(BIF_P, modp->curr.code_hdr->on_load_function_ptr); + if (modp && modp->old.code_hdr) { + BIF_TRAP_CODE_PTR_0(BIF_P, modp->old.code_hdr->on_load_function_ptr); } else { BIF_ERROR(BIF_P, BADARG); @@ -651,14 +651,14 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) code_ix = erts_active_code_ix(); modp = erts_get_module(BIF_ARG_1, code_ix); - if (!modp || !modp->curr.code_hdr) { + if (!modp || !modp->old.code_hdr) { error: erts_smp_thr_progress_unblock(); erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_release_code_write_permission(); BIF_ERROR(BIF_P, BADARG); } - if (modp->curr.code_hdr->on_load_function_ptr == NULL) { + if (modp->old.code_hdr->on_load_function_ptr == NULL) { goto error; } if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) { @@ -667,44 +667,55 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) if (BIF_ARG_2 == am_true) { int i; + struct erl_module_instance t; + + /* + * Swap old and new code. + */ + t = modp->curr; + modp->curr = modp->old; + modp->old = t; /* * The on_load function succeded. Fix up export entries. */ 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) { + if (ep == NULL || ep->code[0] != BIF_ARG_1) { + continue; + } + if (ep->code[4] != 0) { ep->addressv[code_ix] = (void *) ep->code[4]; ep->code[4] = 0; + } else { + if (ep->addressv[code_ix] == ep->code+3 && + ep->code[3] == (BeamInstr) em_apply_bif) { + continue; + } + ep->addressv[code_ix] = ep->code+3; + ep->code[3] = (BeamInstr) em_call_error_handler; } } modp->curr.code_hdr->on_load_function_ptr = NULL; set_default_trace_pattern(BIF_ARG_1); } else if (BIF_ARG_2 == am_false) { - BeamInstr* code; - BeamInstr* end; + int i; /* - * The on_load function failed. Remove the loaded code. - * This is an combination of delete and purge. We purge - * the current code; the old code is not touched. + * The on_load function failed. Remove references to the + * code that is about to be purged from the export entries. */ - erts_total_code_size -= modp->curr.code_length; - code = (BeamInstr*) modp->curr.code_hdr; - end = (BeamInstr *) ((char *)code + modp->curr.code_length); - erts_cleanup_funs_on_purge(code, end); - beam_catches_delmod(modp->curr.catches, code, modp->curr.code_length, - erts_active_code_ix()); - if (modp->curr.code_hdr->literals_start) { - erts_free(ERTS_ALC_T_LITERAL, modp->curr.code_hdr->literals_start); - } - erts_free(ERTS_ALC_T_CODE, modp->curr.code_hdr); - modp->curr.code_hdr = NULL; - modp->curr.code_length = 0; - modp->curr.catches = BEAM_CATCHES_NIL; - erts_remove_from_ranges(code); + + 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) { + continue; + } + if (ep->code[3] == (BeamInstr) em_apply_bif) { + continue; + } + ep->code[4] = 0; + } } erts_smp_thr_progress_unblock(); erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 9bd9b4bfc0..95489d9a63 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -32,6 +32,7 @@ #include "bif.h" #include "external.h" #include "beam_load.h" +#include "beam_bp.h" #include "big.h" #include "erl_bits.h" #include "beam_catches.h" @@ -766,7 +767,7 @@ Eterm erts_finish_loading(Binary* magic, Process* c_p, ErtsProcLocks c_p_locks, Eterm* modp) { - Eterm retval; + Eterm retval = NIL; LoaderState* stp = ERTS_MAGIC_BIN_DATA(magic); Module* mod_tab_p; struct erl_module_instance* inst_p; @@ -779,16 +780,52 @@ erts_finish_loading(Binary* magic, Process* c_p, ERTS_SMP_LC_ASSERT(erts_initialized == 0 || erts_has_code_write_permission() || erts_smp_thr_progress_is_blocking()); - /* * Make current code for the module old and insert the new code * as current. This will fail if there already exists old code * for the module. */ + mod_tab_p = erts_put_module(stp->module); CHKBLK(ERTS_ALC_T_CODE,stp->code); - retval = beam_make_current_old(c_p, c_p_locks, stp->module); - ASSERT(retval == NIL); + if (!stp->on_load) { + /* + * Normal case -- no -on_load() function. + */ + retval = beam_make_current_old(c_p, c_p_locks, stp->module); + ASSERT(retval == NIL); + } else { + ErtsCodeIndex code_ix = erts_staging_code_ix(); + Eterm module = stp->module; + int i; + + /* + * There is an -on_load() function. We will keep the current + * code, but we must turn off any tracing. + */ + + for (i = 0; i < export_list_size(code_ix); i++) { + Export *ep = export_list(i, code_ix); + if (ep == NULL || ep->code[0] != module) { + continue; + } + if (ep->addressv[code_ix] == ep->code+3) { + if (ep->code[3] == (BeamInstr) em_apply_bif) { + continue; + } else if (ep->code[3] == + (BeamInstr) BeamOp(op_i_generic_breakpoint)) { + ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); + ASSERT(mod_tab_p->curr.num_traced_exports > 0); + erts_clear_export_break(mod_tab_p, ep->code+3); + ep->addressv[code_ix] = (BeamInstr *) ep->code[4]; + ep->code[4] = 0; + } + ASSERT(ep->code[4] == 0); + } + } + ASSERT(mod_tab_p->curr.num_breakpoints == 0); + ASSERT(mod_tab_p->curr.num_traced_exports == 0); + } /* * Update module table. @@ -796,8 +833,11 @@ erts_finish_loading(Binary* magic, Process* c_p, size = stp->loaded_size; erts_total_code_size += size; - mod_tab_p = erts_put_module(stp->module); - inst_p = &mod_tab_p->curr; + if (stp->on_load) { + inst_p = &mod_tab_p->old; + } else { + inst_p = &mod_tab_p->curr; + } inst_p->code_hdr = stp->hdr; inst_p->code_length = size; @@ -4757,10 +4797,10 @@ final_touch(LoaderState* stp, struct erl_module_instance* inst_p) ep->addressv[erts_staging_code_ix()] = address; } else { /* - * Don't make any of the exported functions - * callable yet. + * on_load: Don't make any of the exported functions + * callable yet. Keep any function in the current + * code callable. */ - ep->addressv[erts_staging_code_ix()] = ep->code+3; ep->code[4] = (BeamInstr) address; } } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 941f44b9ec..dc83a780a2 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -2780,7 +2780,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ErlNifEntry* entry = NULL; ErlNifEnv env; int i, err, encoding; - Module* mod; + Module* module_p; Eterm mod_atom; const Atom* mod_atomp; Eterm f_atom; @@ -2790,6 +2790,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) int veto; struct erl_module_nif* lib = NULL; int reload_warning = 0; + struct erl_module_instance* this_mi; + struct erl_module_instance* prev_mi; encoding = erts_get_native_filename_encoding(); if (encoding == ERL_FILENAME_WIN_WCHAR) { @@ -2823,21 +2825,29 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ASSERT(caller != NULL); mod_atom = caller[0]; ASSERT(is_atom(mod_atom)); - mod=erts_get_module(mod_atom, erts_active_code_ix()); - ASSERT(mod != NULL); + module_p = erts_get_module(mod_atom, erts_active_code_ix()); + ASSERT(module_p != NULL); mod_atomp = atom_tab(atom_val(mod_atom)); init_func = erts_static_nif_get_nif_init((char*)mod_atomp->name, mod_atomp->len); if (init_func != NULL) handle = init_func; - if (!in_area(caller, mod->curr.code_hdr, mod->curr.code_length)) { - ASSERT(in_area(caller, mod->old.code_hdr, mod->old.code_length)); + if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) { + if (module_p->old.code_hdr->on_load_function_ptr) { + this_mi = &module_p->old; + prev_mi = &module_p->curr; + } else { + ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old " + "module '%T' not allowed", mod_atom); + goto error; + } + } else { + this_mi = &module_p->curr; + prev_mi = &module_p->old; + } - ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old " - "module '%T' not allowed", mod_atom); - } - else if (init_func == NULL && + if (init_func == NULL && (err=erts_sys_ddll_open(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) { const char slogan[] = "Failed to load NIF library"; if (strstr(errdesc.str, lib_name) != NULL) { @@ -2886,7 +2896,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) for (i=0; i < entry->num_of_funcs && ret==am_ok; i++) { BeamInstr** code_pp; if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1) - || (code_pp = get_func_pp(mod->curr.code_hdr, f_atom, f->arity))==NULL) { + || (code_pp = get_func_pp(this_mi->code_hdr, f_atom, f->arity))==NULL) { ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u", mod_atom, f->name, f->arity); } @@ -2937,9 +2947,9 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) erts_refc_init(&lib->rt_cnt, 0); erts_refc_init(&lib->rt_dtor_cnt, 0); ASSERT(opened_rt_list == NULL); - lib->mod = mod; + lib->mod = module_p; env.mod_nif = lib; - if (mod->curr.nif != NULL) { /*************** Reload ******************/ + if (this_mi->nif != NULL) { /*************** Reload ******************/ /* * Repeated load_nif calls from same Erlang module instance ("reload") * is deprecated and was only ment as a development feature not to @@ -2947,16 +2957,16 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) */ int k, old_incr = 0; ErlNifFunc* old_func; - lib->priv_data = mod->curr.nif->priv_data; + lib->priv_data = this_mi->nif->priv_data; - ASSERT(mod->curr.nif->entry != NULL); + ASSERT(this_mi->nif->entry != NULL); if (entry->reload == NULL) { ret = load_nif_error(BIF_P,reload,"Reload not supported by this NIF library."); goto error; } /* Check that no NIF is removed */ - old_func = mod->curr.nif->entry->funcs; - for (k=0; k < mod->curr.nif->entry->num_of_funcs; k++) { + old_func = this_mi->nif->entry->funcs; + for (k=0; k < this_mi->nif->entry->num_of_funcs; k++) { int incr = 0; ErlNifFunc* f = entry->funcs; for (i=0; i < entry->num_of_funcs; i++) { @@ -2972,7 +2982,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) old_func->name, old_func->arity); goto error; } - old_func = next_func(mod->curr.nif->entry, &old_incr, old_func); + old_func = next_func(this_mi->nif->entry, &old_incr, old_func); } erts_pre_nif(&env, BIF_P, lib, NULL); veto = entry->reload(&env, &lib->priv_data, BIF_ARG_2); @@ -2982,24 +2992,24 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) } else { commit_opened_resource_types(lib); - mod->curr.nif->entry = NULL; /* to prevent 'unload' callback */ - erts_unload_nif(mod->curr.nif); + this_mi->nif->entry = NULL; /* to prevent 'unload' callback */ + erts_unload_nif(this_mi->nif); reload_warning = 1; } } else { lib->priv_data = NULL; - if (mod->old.nif != NULL) { /**************** Upgrade ***************/ - void* prev_old_data = mod->old.nif->priv_data; + if (prev_mi->nif != NULL) { /**************** Upgrade ***************/ + void* prev_old_data = prev_mi->nif->priv_data; if (entry->upgrade == NULL) { ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library."); goto error; } - erts_pre_nif(&env, BIF_P, lib, NULL); - veto = entry->upgrade(&env, &lib->priv_data, &mod->old.nif->priv_data, BIF_ARG_2); + erts_pre_nif(&env, BIF_P, lib, NULL); + veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, BIF_ARG_2); erts_post_nif(&env); if (veto) { - mod->old.nif->priv_data = prev_old_data; + prev_mi->nif->priv_data = prev_old_data; ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful."); } else @@ -3024,12 +3034,12 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) int incr = 0; ErlNifFunc* f = entry->funcs; - mod->curr.nif = lib; + this_mi->nif = lib; for (i=0; i < entry->num_of_funcs; i++) { BeamInstr* code_ptr; erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1); - code_ptr = *get_func_pp(mod->curr.code_hdr, f_atom, f->arity); + code_ptr = *get_func_pp(this_mi->code_hdr, f_atom, f->arity); if (code_ptr[1] == 0) { code_ptr[5+0] = (BeamInstr) BeamOp(op_call_nif); -- cgit v1.2.3 From 19932cade31a7973120eba8db9a3b57b925f674a Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 27 Apr 2016 10:36:10 +0200 Subject: Ensure correct reduction counting --- erts/emulator/beam/beam_bif_load.c | 12 ++-- erts/emulator/beam/beam_emu.c | 94 +++++++++++++++++++++++------ erts/emulator/beam/bif.c | 48 +++++++++++---- erts/emulator/beam/bif.h | 1 + erts/emulator/beam/erl_gc.c | 40 +++++++++---- erts/emulator/beam/erl_gc.h | 2 +- erts/emulator/beam/erl_init.c | 10 ++-- erts/emulator/beam/erl_process.c | 62 +++++++++++++------ erts/emulator/beam/erl_process.h | 19 ++++++ erts/emulator/beam/erl_trace.c | 22 +++---- erts/emulator/beam/global.h | 2 +- erts/emulator/hipe/hipe_mode_switch.c | 102 ++++++++++++++++++++++++-------- erts/emulator/test/save_calls_SUITE.erl | 2 +- 13 files changed, 308 insertions(+), 108 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 87508dcf5f..801218774b 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -38,7 +38,7 @@ #include "erl_thr_progress.h" static void set_default_trace_pattern(Eterm module); -static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp); +static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls); static void delete_code(Module* modp); static void decrement_refc(BeamCodeHeader*); static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size); @@ -467,7 +467,7 @@ check_old_code_1(BIF_ALIST_1) } Eterm -erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp) +erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp, int fcalls) { Module* modp; Eterm res; @@ -483,7 +483,7 @@ erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp) return am_false; erts_rlock_old_code(code_ix); res = (!modp->old.code_hdr ? am_false : - check_process_code(c_p, modp, flags, redsp)); + check_process_code(c_p, modp, flags, redsp, fcalls)); erts_runlock_old_code(code_ix); return res; @@ -506,7 +506,7 @@ BIF_RETTYPE erts_internal_check_process_code_2(BIF_ALIST_2) goto badarg; } - res = erts_check_process_code(BIF_P, BIF_ARG_1, flags, &reds); + res = erts_check_process_code(BIF_P, BIF_ARG_1, flags, &reds, BIF_P->fcalls); ASSERT(is_value(res)); @@ -753,7 +753,7 @@ check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size) static Eterm -check_process_code(Process* rp, Module* modp, Uint flags, int *redsp) +check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls) { BeamInstr* start; char* literals; @@ -955,7 +955,7 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp) if (need_gc & ERTS_ORDINARY_GC__) { FLAGS(rp) |= F_NEED_FULLSWEEP; - *redsp += erts_garbage_collect_nobump(rp, 0, rp->arg_reg, rp->arity); + *redsp += erts_garbage_collect_nobump(rp, 0, rp->arg_reg, rp->arity, fcalls); done_gc |= ERTS_ORDINARY_GC__; } if (need_gc & ERTS_LITERAL_GC__) { diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 72526bca5e..59112e1e43 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -308,7 +308,8 @@ void** beam_ops; if (E - HTOP < (needed + (HeapNeed))) { \ SWAPOUT; \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - FCALLS -= erts_garbage_collect_nobump(c_p, needed + (HeapNeed), reg, (M)); \ + FCALLS -= erts_garbage_collect_nobump(c_p, needed + (HeapNeed), \ + reg, (M), FCALLS); \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ SWAPIN; \ @@ -360,7 +361,7 @@ void** beam_ops; if ((E - HTOP < need) || (MSO(c_p).overhead + (VNh) >= BIN_VHEAP_SZ(c_p))) {\ SWAPOUT; \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live)); \ + FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live), FCALLS); \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ SWAPIN; \ @@ -381,7 +382,7 @@ void** beam_ops; if (E - HTOP < need) { \ SWAPOUT; \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live));\ + FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live), FCALLS); \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ SWAPIN; \ @@ -402,7 +403,7 @@ void** beam_ops; SWAPOUT; \ reg[Live] = Extra; \ PROCESS_MAIN_CHK_LOCKS(c_p); \ - FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live)+1); \ + FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, (Live)+1, FCALLS); \ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \ PROCESS_MAIN_CHK_LOCKS(c_p); \ Extra = reg[Live]; \ @@ -1196,6 +1197,25 @@ init_emulator(void) #define DTRACE_NIF_RETURN(p, m, f, a) do {} while (0) #endif /* USE_VM_PROBES */ +#ifdef DEBUG +#define ERTS_DBG_CHK_REDS(P, FC) \ + do { \ + if (ERTS_PROC_GET_SAVED_CALLS_BUF((P))) { \ + ASSERT(FC <= 0); \ + ASSERT(ERTS_PROC_GET_SCHDATA(c_p)->virtual_reds \ + <= 0 - (FC)); \ + } \ + else { \ + ASSERT(FC <= CONTEXT_REDS); \ + ASSERT(ERTS_PROC_GET_SCHDATA(c_p)->virtual_reds \ + <= CONTEXT_REDS - (FC)); \ + } \ +} while (0) +#else +#define ERTS_DBG_CHK_REDS(P, FC) +#endif + + /* * process_main() is called twice: * The first call performs some initialisation, including exporting @@ -1290,7 +1310,12 @@ void process_main(void) goto do_schedule1; do_schedule: - reds_used = REDS_IN(c_p) - FCALLS; + ASSERT(c_p->debug_reds_in == REDS_IN(c_p)); + if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) + reds_used = REDS_IN(c_p) - FCALLS; + else + reds_used = REDS_IN(c_p) - (CONTEXT_REDS + FCALLS); + ASSERT(reds_used >= 0); do_schedule1: if (start_time != 0) { @@ -1310,6 +1335,7 @@ void process_main(void) ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); c_p = schedule(c_p, reds_used); + ASSERT(!(c_p->flags & F_HIPE_MODE)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); start_time = 0; #ifdef DEBUG @@ -1348,16 +1374,21 @@ void process_main(void) SET_I(c_p->i); - reds = c_p->fcalls; - if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) - && (ERTS_TRACE_FLAGS(c_p) & F_SENSITIVE) == 0) { - neg_o_reds = -reds; - FCALLS = REDS_IN(c_p) = 0; + REDS_IN(c_p) = reds = c_p->fcalls; +#ifdef DEBUG + c_p->debug_reds_in = reds; +#endif + + if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) { + neg_o_reds = -CONTEXT_REDS; + FCALLS = neg_o_reds + reds; } else { neg_o_reds = 0; - FCALLS = REDS_IN(c_p) = reds; + FCALLS = reds; } + ERTS_DBG_CHK_REDS(c_p, FCALLS); + next = (BeamInstr *) *I; SWAPIN; ASSERT(VALID_INSTR(next)); @@ -1770,7 +1801,7 @@ void process_main(void) if (E - HTOP < 3) { SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); - FCALLS -= erts_garbage_collect_nobump(c_p, 3, reg+2, 1); + FCALLS -= erts_garbage_collect_nobump(c_p, 3, reg+2, 1, FCALLS); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); SWAPIN; @@ -1865,6 +1896,7 @@ void process_main(void) c_p->flags |= F_DELAY_GC; loop_rec__: + PROCESS_MAIN_CHK_LOCKS(c_p); msgp = PEEK_MESSAGE(c_p); @@ -2007,6 +2039,8 @@ void process_main(void) ERTS_VBUMP_LEAVE_REDS_INTERNAL(c_p, 5, FCALLS); } + ERTS_DBG_CHK_REDS(c_p, FCALLS); + ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); NextPF(0, next); @@ -2535,6 +2569,7 @@ do { \ GetArg1(2, tmp_reg[0]); bf = (BifFunction) Arg(1); + ERTS_DBG_CHK_REDS(c_p, FCALLS); c_p->fcalls = FCALLS; PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); @@ -2544,6 +2579,7 @@ do { \ PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; + ERTS_DBG_CHK_REDS(c_p, FCALLS); if (is_value(result)) { StoreBifResult(3, result); } @@ -2564,6 +2600,7 @@ do { \ GetArg1(1, tmp_reg[0]); bf = (BifFunction) Arg(0); + ERTS_DBG_CHK_REDS(c_p, FCALLS); c_p->fcalls = FCALLS; PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); @@ -2573,6 +2610,7 @@ do { \ PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; + ERTS_DBG_CHK_REDS(c_p, FCALLS); if (is_value(result)) { StoreBifResult(2, result); } @@ -2591,6 +2629,7 @@ do { \ GetArg1(2, x(live)); bf = (GcBifFunction) Arg(1); + ERTS_DBG_CHK_REDS(c_p, FCALLS); c_p->fcalls = FCALLS; SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2602,6 +2641,7 @@ do { \ SWAPIN; ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; + ERTS_DBG_CHK_REDS(c_p, FCALLS); if (is_value(result)) { StoreBifResult(4, result); } @@ -2630,6 +2670,7 @@ do { \ */ live++; bf = (GcBifFunction) Arg(1); + ERTS_DBG_CHK_REDS(c_p, FCALLS); c_p->fcalls = FCALLS; SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2641,6 +2682,7 @@ do { \ SWAPIN; ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; + ERTS_DBG_CHK_REDS(c_p, FCALLS); if (is_value(result)) { StoreBifResult(5, result); } @@ -2671,6 +2713,7 @@ do { \ */ live += 2; bf = (GcBifFunction) Arg(1); + ERTS_DBG_CHK_REDS(c_p, FCALLS); c_p->fcalls = FCALLS; SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2682,6 +2725,7 @@ do { \ SWAPIN; ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; + ERTS_DBG_CHK_REDS(c_p, FCALLS); if (is_value(result)) { StoreBifResult(5, result); } @@ -2708,6 +2752,7 @@ do { \ GetArg2(2, tmp_reg[0], tmp_reg[1]); bf = (BifFunction) Arg(1); + ERTS_DBG_CHK_REDS(c_p, FCALLS); c_p->fcalls = FCALLS; PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); @@ -2717,6 +2762,7 @@ do { \ PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; + ERTS_DBG_CHK_REDS(c_p, FCALLS); if (is_value(result)) { StoreBifResult(4, result); } @@ -2775,6 +2821,7 @@ do { \ bf = GET_BIF_ADDRESS(Arg(0)); PRE_BIF_SWAPOUT(c_p); + ERTS_DBG_CHK_REDS(c_p, FCALLS); c_p->fcalls = FCALLS - 1; if (FCALLS <= 0) { save_calls(c_p, (Export *) Arg(0)); @@ -2796,6 +2843,7 @@ do { \ PROCESS_MAIN_CHK_LOCKS(c_p); HTOP = HEAP_TOP(c_p); FCALLS = c_p->fcalls; + ERTS_DBG_CHK_REDS(c_p, FCALLS); /* We have to update the cache if we are enabled in order to make sure no book keeping is done after we disabled msacc. We don't always do this as it is quite expensive. */ @@ -3324,8 +3372,13 @@ do { \ * (beacuse the code for the Dispatch() macro becomes shorter that way). */ - reds_used = REDS_IN(c_p) - FCALLS + 1; - + ASSERT(c_p->debug_reds_in == REDS_IN(c_p)); + if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) + reds_used = REDS_IN(c_p) - FCALLS; + else + reds_used = REDS_IN(c_p) - (CONTEXT_REDS + FCALLS); + ASSERT(reds_used >= 0); + /* * Save the argument registers and everything else. */ @@ -3514,6 +3567,7 @@ do { \ DTRACE_BIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); SWAPOUT; + ERTS_DBG_CHK_REDS(c_p, FCALLS - 1); c_p->fcalls = FCALLS - 1; vbf = (BifFunction) Arg(0); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -3549,6 +3603,7 @@ do { \ } SWAPIN; /* There might have been a garbage collection. */ FCALLS = c_p->fcalls; + ERTS_DBG_CHK_REDS(c_p, FCALLS); if (is_value(nif_bif_result)) { r(0) = nif_bif_result; CHECK_TERM(r(0)); @@ -4611,9 +4666,9 @@ do { \ OpCase(i_generic_breakpoint): { BeamInstr real_I; ASSERT(I[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); - SWAPOUT; + HEAVY_SWAPOUT; real_I = erts_generic_breakpoint(c_p, I, reg); - SWAPIN; + HEAVY_SWAPIN; ASSERT(VALID_INSTR(real_I)); Goto(real_I); } @@ -4790,6 +4845,7 @@ do { \ { #define HIPE_MODE_SWITCH(Cmd) \ SWAPOUT; \ + ERTS_DBG_CHK_REDS(c_p, FCALLS); \ c_p->fcalls = FCALLS; \ c_p->def_arg_reg[4] = -neg_o_reds; \ c_p = hipe_mode_switch(c_p, Cmd, reg); \ @@ -4828,6 +4884,9 @@ do { \ #undef HIPE_MODE_SWITCH L_post_hipe_mode_switch: +#ifdef DEBUG + pid = c_p->common.id; /* may have switched process... */ +#endif reg = ERTS_PROC_GET_SCHDATA(c_p)->x_reg_array; freg = ERTS_PROC_GET_SCHDATA(c_p)->f_reg_array; ERL_BITS_RELOAD_STATEP(c_p); @@ -4835,6 +4894,7 @@ do { \ neg_o_reds = -c_p->def_arg_reg[4]; FCALLS = c_p->fcalls; SWAPIN; + ERTS_DBG_CHK_REDS(c_p, FCALLS); switch( c_p->def_arg_reg[3] ) { case HIPE_MODE_SWITCH_RES_RETURN: ASSERT(is_value(reg[0])); @@ -6839,7 +6899,7 @@ erts_current_reductions(Process *current, Process *p) if (current != p) { return 0; } else if (current->fcalls < 0 && ERTS_PROC_GET_SAVED_CALLS_BUF(current)) { - return -current->fcalls; + return current->fcalls + CONTEXT_REDS; } else { return REDS_IN(current) - current->fcalls; } diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index ed5b2983dd..fa5c51ef88 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1569,7 +1569,32 @@ static BIF_RETTYPE process_flag_aux(Process *BIF_P, scb->n = 0; } - scb = ERTS_PROC_SET_SAVED_CALLS_BUF(rp, scb); +#ifdef HIPE + if (rp->flags & F_HIPE_MODE) { + ASSERT(!ERTS_PROC_GET_SAVED_CALLS_BUF(rp)); + scb = ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(rp, scb); + } + else +#endif + { +#ifdef HIPE + ASSERT(!ERTS_PROC_GET_SUSPENDED_SAVED_CALLS_BUF(rp)); +#endif + scb = ERTS_PROC_SET_SAVED_CALLS_BUF(rp, scb); + if (rp == BIF_P && ((scb && i == 0) || (!scb && i != 0))) { + /* Adjust fcalls to match save calls setting... */ + if (i == 0) + BIF_P->fcalls += CONTEXT_REDS; /* disabled it */ + else + BIF_P->fcalls -= CONTEXT_REDS; /* enabled it */ + + /* + * Make sure we reschedule immediately so the + * change take effect at once. + */ + ERTS_VBUMP_ALL_REDS(BIF_P); + } + } if (!scb) old_value = make_small(0); @@ -1578,12 +1603,7 @@ static BIF_RETTYPE process_flag_aux(Process *BIF_P, erts_free(ERTS_ALC_T_CALLS_BUF, (void *) scb); } - /* Make sure the process in question is rescheduled - immediately, if it's us, so the call saving takes effect. */ - if (rp == BIF_P) - BIF_RET2(old_value, CONTEXT_REDS); - else - BIF_RET(old_value); + BIF_RET(old_value); } error: @@ -1775,10 +1795,18 @@ BIF_RETTYPE process_flag_3(BIF_ALIST_3) Process *rp; Eterm res; - if ((rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN, - BIF_ARG_1, ERTS_PROC_LOCK_MAIN)) == NULL) { +#ifdef ERTS_SMP + rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN, + BIF_ARG_1, ERTS_PROC_LOCK_MAIN); + if (rp == ERTS_PROC_LOCK_BUSY) + ERTS_BIF_YIELD3(bif_export[BIF_process_flag_3], BIF_P, + BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); +#else + rp = erts_proc_lookup(BIF_ARG_1); +#endif + + if (!rp) BIF_ERROR(BIF_P, BADARG); - } res = process_flag_aux(BIF_P, rp, BIF_ARG_2, BIF_ARG_3); diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index 546e3830b9..5d751dd67d 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -52,6 +52,7 @@ extern Export *erts_convert_time_unit_trap; (p)->fcalls = 0; \ else \ (p)->fcalls = -CONTEXT_REDS; \ + ASSERT(ERTS_BIF_REDS_LEFT((p)) == 0); \ } while(0) #define ERTS_VBUMP_ALL_REDS_INTERNAL(p, fcalls) \ diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 4698458521..5a180d7e48 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -116,7 +116,7 @@ static Eterm *full_sweep_heaps(Process *p, char *oh, Uint oh_size, Eterm *objv, int nobj); static int garbage_collect(Process* p, ErlHeapFragment *live_hf_end, - int need, Eterm* objv, int nobj); + int need, Eterm* objv, int nobj, int fcalls); static int major_collection(Process* p, ErlHeapFragment *live_hf_end, int need, Eterm* objv, int nobj, Uint *recl); static int minor_collection(Process* p, ErlHeapFragment *live_hf_end, @@ -392,15 +392,15 @@ erts_gc_after_bif_call_lhf(Process* p, ErlHeapFragment *live_hf_end, regs = ERTS_PROC_GET_SCHDATA(p)->x_reg_array; } #endif - cost = garbage_collect(p, live_hf_end, 0, regs, p->arity); + cost = garbage_collect(p, live_hf_end, 0, regs, p->arity, p->fcalls); } else { - cost = garbage_collect(p, live_hf_end, 0, regs, arity); + cost = garbage_collect(p, live_hf_end, 0, regs, arity, p->fcalls); } } else { Eterm val[1]; val[0] = result; - cost = garbage_collect(p, live_hf_end, 0, val, 1); + cost = garbage_collect(p, live_hf_end, 0, val, 1, p->fcalls); result = val[0]; } BUMP_REDS(p, cost); @@ -431,7 +431,7 @@ static ERTS_INLINE void reset_active_writer(Process *p) #define ERTS_ABANDON_HEAP_COST 10 static int -delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need) +delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need, int fcalls) { ErlHeapFragment *hfrag; Eterm *orig_heap, *orig_hend, *orig_htop, *orig_stop; @@ -506,12 +506,16 @@ delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need) /* Make sure that we do a proper GC as soon as possible... */ p->flags |= F_FORCE_GC; - reds_left = ERTS_BIF_REDS_LEFT(p); + reds_left = ERTS_REDS_LEFT(p, fcalls); + ASSERT(CONTEXT_REDS - reds_left >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds); + if (reds_left > ERTS_ABANDON_HEAP_COST) { int vreds = reds_left - ERTS_ABANDON_HEAP_COST; - ERTS_VBUMP_REDS(p, vreds); + ERTS_PROC_GET_SCHDATA((p))->virtual_reds += vreds; } - return ERTS_ABANDON_HEAP_COST; + + ASSERT(CONTEXT_REDS >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds); + return reds_left; } static ERTS_FORCE_INLINE Uint @@ -570,7 +574,7 @@ young_gen_usage(Process *p) */ static int garbage_collect(Process* p, ErlHeapFragment *live_hf_end, - int need, Eterm* objv, int nobj) + int need, Eterm* objv, int nobj, int fcalls) { Uint reclaimed_now = 0; int reds; @@ -581,8 +585,11 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); #endif + ASSERT(CONTEXT_REDS - ERTS_REDS_LEFT(p, fcalls) + >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds); + if (p->flags & (F_DISABLE_GC|F_DELAY_GC)) - return delay_garbage_collection(p, live_hf_end, need); + return delay_garbage_collection(p, live_hf_end, need, fcalls); if (p->abandoned_heap) live_hf_end = ERTS_INVALID_HFRAG_PTR; @@ -700,16 +707,23 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, } int -erts_garbage_collect_nobump(Process* p, int need, Eterm* objv, int nobj) +erts_garbage_collect_nobump(Process* p, int need, Eterm* objv, int nobj, int fcalls) { - return garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj); + int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, fcalls); + int reds_left = ERTS_REDS_LEFT(p, fcalls); + if (reds > reds_left) + reds = reds_left; + ASSERT(CONTEXT_REDS - (reds_left - reds) >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds); + return reds; } void erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) { - int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj); + int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, p->fcalls); BUMP_REDS(p, reds); + ASSERT(CONTEXT_REDS - ERTS_BIF_REDS_LEFT(p) + >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds); } /* diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index 40b7c5d12c..8a6ff99990 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -139,7 +139,7 @@ Eterm erts_process_gc_info(struct process*, Uint *, Eterm **); void erts_gc_info(ErtsGCInfo *gcip); void erts_init_gc(void); -int erts_garbage_collect_nobump(struct process*, int, Eterm*, int); +int erts_garbage_collect_nobump(struct process*, int, Eterm*, int, int); void erts_garbage_collect(struct process*, int, Eterm*, int); void erts_garbage_collect_hibernate(struct process* p); Eterm erts_gc_after_bif_call_lhf(struct process* p, ErlHeapFragment *live_hf_end, diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index dec9bdfedc..4d27530866 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -206,15 +206,15 @@ int erts_no_line_info = 0; /* -L: Don't load line information */ ErtsModifiedTimings erts_modified_timings[] = { /* 0 */ {make_small(0), CONTEXT_REDS, INPUT_REDUCTIONS}, - /* 1 */ {make_small(0), 2*CONTEXT_REDS, 2*INPUT_REDUCTIONS}, + /* 1 */ {make_small(0), (3*CONTEXT_REDS)/4, 2*INPUT_REDUCTIONS}, /* 2 */ {make_small(0), CONTEXT_REDS/2, INPUT_REDUCTIONS/2}, - /* 3 */ {make_small(0), 3*CONTEXT_REDS, 3*INPUT_REDUCTIONS}, + /* 3 */ {make_small(0), (7*CONTEXT_REDS)/8, 3*INPUT_REDUCTIONS}, /* 4 */ {make_small(0), CONTEXT_REDS/3, 3*INPUT_REDUCTIONS}, - /* 5 */ {make_small(0), 4*CONTEXT_REDS, INPUT_REDUCTIONS/2}, + /* 5 */ {make_small(0), (10*CONTEXT_REDS)/11, INPUT_REDUCTIONS/2}, /* 6 */ {make_small(1), CONTEXT_REDS/4, 2*INPUT_REDUCTIONS}, - /* 7 */ {make_small(1), 5*CONTEXT_REDS, INPUT_REDUCTIONS/3}, + /* 7 */ {make_small(1), (5*CONTEXT_REDS)/7, INPUT_REDUCTIONS/3}, /* 8 */ {make_small(10), CONTEXT_REDS/5, 3*INPUT_REDUCTIONS}, - /* 9 */ {make_small(10), 6*CONTEXT_REDS, INPUT_REDUCTIONS/4} + /* 9 */ {make_small(10), (6*CONTEXT_REDS)/7, INPUT_REDUCTIONS/4} }; #define ERTS_MODIFIED_TIMING_LEVELS \ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index d485affa3b..c6c3fd64a1 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -9240,6 +9240,21 @@ erts_set_process_priority(Process *p, Eterm value) } } +static int +scheduler_gc_proc(Process *c_p, int reds_left) +{ + int fcalls, reds; + if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) + fcalls = reds_left; + else + fcalls = reds_left - CONTEXT_REDS; + reds = erts_garbage_collect_nobump(c_p, 0, c_p->arg_reg, c_p->arity, fcalls); + ASSERT(reds_left >= reds); + return reds; +} + + + /* * schedule() is called from BEAM (process_main()) or HiPE * (hipe_mode_switch()) when the current process is to be @@ -9318,6 +9333,7 @@ Process *schedule(Process *p, int calls) #endif reds = actual_reds = calls - esdp->virtual_reds; + ASSERT(actual_reds >= 0); if (reds < ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST) reds = ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST; esdp->virtual_reds = 0; @@ -9740,7 +9756,7 @@ Process *schedule(Process *p, int calls) esdp->current_process = p; - + calls = 0; reds = context_reds; #ifdef ERTS_SMP @@ -9819,9 +9835,7 @@ Process *schedule(Process *p, int calls) /* Migrate to dirty scheduler... */ sunlock_sched_out_proc: erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - p->fcalls = reds; goto sched_out_proc; - } } else { @@ -9856,8 +9870,6 @@ Process *schedule(Process *p, int calls) #endif /* ERTS_SMP */ - p->fcalls = reds; - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); if (IS_TRACED(p)) { @@ -9890,14 +9902,15 @@ Process *schedule(Process *p, int calls) * not allowed to execute system tasks. */ if (!(p->flags & F_DELAY_GC)) { - reds -= execute_sys_tasks(p, &state, reds); + int cost = execute_sys_tasks(p, &state, reds); + calls += cost; + reds -= cost; if (reds <= 0 #ifdef ERTS_DIRTY_SCHEDULERS || ERTS_SCHEDULER_IS_DIRTY(esdp) || (state & ERTS_PSFLGS_DIRTY_WORK) #endif ) { - p->fcalls = reds; goto sched_out_proc; } } @@ -9910,8 +9923,9 @@ Process *schedule(Process *p, int calls) if (((state & (ERTS_PSFLG_SUSPENDED | ERTS_PSFLG_ACTIVE)) != ERTS_PSFLG_ACTIVE) - && !(state & ERTS_PSFLG_EXITING)) + && !(state & ERTS_PSFLG_EXITING)) { goto sched_out_proc; + } n = e = state; n &= ~ERTS_PSFLG_RUNNING_SYS; @@ -9930,11 +9944,11 @@ Process *schedule(Process *p, int calls) if (ERTS_IS_GC_DESIRED(p)) { if (!(state & ERTS_PSFLG_EXITING) && !(p->flags & (F_DELAY_GC|F_DISABLE_GC))) { - reds -= erts_garbage_collect_nobump(p, 0, p->arg_reg, p->arity); - if (reds <= 0) { - p->fcalls = reds; + int cost = scheduler_gc_proc(p, reds); + calls += cost; + reds -= cost; + if (reds <= 0) goto sched_out_proc; - } } } @@ -9942,6 +9956,8 @@ Process *schedule(Process *p, int calls) free_proxy_proc(proxy_p); proxy_p = NULL; } + + p->fcalls = reds; ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); @@ -10211,21 +10227,24 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) else { if (!garbage_collected) { FLAGS(c_p) |= F_NEED_FULLSWEEP; - reds -= erts_garbage_collect_nobump(c_p, - 0, - c_p->arg_reg, - c_p->arity); + reds -= scheduler_gc_proc(c_p, reds); garbage_collected = 1; } st_res = am_true; } break; case ERTS_PSTT_CPC: { + int fcalls; int cpc_reds = 0; + if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) + fcalls = reds; + else + fcalls = reds - CONTEXT_REDS; st_res = erts_check_process_code(c_p, st->arg[0], unsigned_val(st->arg[1]), - &cpc_reds); + &cpc_reds, + fcalls); reds -= cpc_reds; if (is_non_value(st_res)) { /* Needed gc, but gc was disabled */ @@ -10257,6 +10276,9 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) *statep = state; + if (in_reds < reds) + return in_reds; + return in_reds - reds; } @@ -11482,8 +11504,10 @@ delete_process(Process* p) psd = (ErtsPSD *) erts_smp_atomic_read_nob(&p->psd); - if (psd) + if (psd) { + erts_smp_atomic_set_nob(&p->psd, (erts_aint_t) NULL); /* Reduction counting depends on this... */ erts_free(ERTS_ALC_T_PSD, psd); + } /* Clean binaries and funs */ erts_cleanup_offheap(&p->off_heap); @@ -12577,6 +12601,8 @@ erts_continue_exit_process(Process *p) dep = (p->flags & F_DISTRIBUTION) ? erts_this_dist_entry : NULL; scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL); + if (scb) + p->fcalls += CONTEXT_REDS; /* Reduction counting depends on this... */ pbt = ERTS_PROC_SET_CALL_TIME(p, NULL); nif_export = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, NULL); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 61acf5924b..8c7dfc8d2f 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -805,8 +805,15 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi) #define ERTS_PSD_CALL_TIME_BP 3 #define ERTS_PSD_DELAYED_GC_TASK_QS 4 #define ERTS_PSD_NIF_TRAP_EXPORT 5 +#ifdef HIPE +#define ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF 6 +#endif +#ifdef HIPE +#define ERTS_PSD_SIZE 7 +#else #define ERTS_PSD_SIZE 6 +#endif typedef struct { void *data[ERTS_PSD_SIZE]; @@ -1071,6 +1078,10 @@ struct process { Uint space_verified; /* Avoid HAlloc forcing heap fragments when */ Eterm* space_verified_from; /* we rely on available heap space (TestHeap) */ #endif + +#ifdef DEBUG + Uint debug_reds_in; +#endif }; extern const Process erts_invalid_process; @@ -1342,6 +1353,7 @@ extern int erts_system_profile_ts_type; #define F_DELAY_GC (1 << 16) /* Similar to disable GC (see below) */ #define F_SCHDLR_ONLN_WAITQ (1 << 17) /* Process enqueued waiting to change schedulers online */ #define F_HAVE_BLCKD_NMSCHED (1 << 18) /* Process has blocked normal multi-scheduling */ +#define F_HIPE_MODE (1 << 19) /* * F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent @@ -1999,6 +2011,13 @@ erts_psd_set(Process *p, int ix, void *data) #define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, NTE) \ erts_psd_set((P), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE)) +#ifdef HIPE +#define ERTS_PROC_GET_SUSPENDED_SAVED_CALLS_BUF(P) \ + ((struct saved_calls *) erts_psd_get((P), ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF)) +#define ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(P, SCB) \ + ((struct saved_calls *) erts_psd_set((P), ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF, (void *) (SCB))) +#endif + ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p); ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p, Eterm handler); diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index bd88769dfc..00e7cb23ba 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1282,16 +1282,18 @@ trace_proc_spawn(Process *p, Eterm what, Eterm pid, void save_calls(Process *p, Export *e) { - struct saved_calls *scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p); - if (scb) { - Export **ct = &scb->ct[0]; - int len = scb->len; - - ct[scb->cur] = e; - if (++scb->cur >= len) - scb->cur = 0; - if (scb->n < len) - scb->n++; + if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) { + struct saved_calls *scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p); + if (scb) { + Export **ct = &scb->ct[0]; + int len = scb->len; + + ct[scb->cur] = e; + if (++scb->cur >= len) + scb->cur = 0; + if (scb->n < len) + scb->n++; + } } } diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index a7bc990deb..49eec44053 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -997,7 +997,7 @@ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2); #define ERTS_CPC_ALLOW_GC (1 << 0) #define ERTS_CPC_COPY_LITERALS (1 << 1) #define ERTS_CPC_ALL (ERTS_CPC_ALLOW_GC | ERTS_CPC_COPY_LITERALS) -Eterm erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp); +Eterm erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp, int fcalls); typedef struct { Eterm *ptr; diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 43144e75ec..9ad44b25ac 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -218,15 +218,37 @@ static __inline__ void hipe_pop_beam_trap_frame(Process *p) Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) { unsigned result; -#if NR_ARG_REGS > 5 - /* When NR_ARG_REGS > 5, we need to protect the process' input - reduction count (which BEAM stores in def_arg_reg[5]) from - being clobbered by the arch glue code. */ Eterm reds_in = p->def_arg_reg[5]; -#endif -#if NR_ARG_REGS > 4 - Eterm o_reds = p->def_arg_reg[4]; -#endif + /* + * Process is in the normal case scheduled out when reduction + * count reach zero. When "save calls" is enabled reduction + * count is subtracted with CONTEXT_REDS, i.e. initial reduction + * count will be zero or less and process is scheduled out + * when -CONTEXT_REDS is reached. + * + * HiPE does not support the "save calls" feature, so we switch + * to using a positive reduction counter when executing in + * hipe mode, but need to restore the "save calls" when + * returning to beam. We also need to hide the save calls buffer + * from BIFs. We do that by moving the saved calls buf to + * suspended saved calls buf. + * + * Beam has initial reduction count in stored in p->def_arg_reg[5]. + * + * Beam expects -neg_o_reds to be found in p->def_arg_reg[4] + * on return to beam. + */ + + { + struct saved_calls *scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL); + if (scb) { + reds_in += CONTEXT_REDS; + p->fcalls += CONTEXT_REDS; + ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, scb); + } + } + + p->flags |= F_HIPE_MODE; /* inform bifs where we are comming from... */ p->i = NULL; /* Set current_function to undefined. stdlib hibernate tests rely on it. */ @@ -481,12 +503,21 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE); do_schedule: { -#if !(NR_ARG_REGS > 5) - int reds_in = p->def_arg_reg[5]; + struct saved_calls *scb; + + scb = ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, NULL); + if (scb) + ERTS_PROC_SET_SAVED_CALLS_BUF(p, scb); + +#ifdef DEBUG + ASSERT(p->debug_reds_in == reds_in); #endif + p->flags &= ~F_HIPE_MODE; + ERTS_SMP_UNREQ_PROC_MAIN_LOCK(p); p = schedule(p, reds_in - p->fcalls); ERTS_SMP_REQ_PROC_MAIN_LOCK(p); + ASSERT(!(p->flags & F_HIPE_MODE)); #ifdef ERTS_SMP p->hipe_smp.have_receive_locks = 0; reg = p->scheduler_data->x_reg_array; @@ -501,28 +532,32 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) reg[i] = argp[i]; } { -#if !(NR_ARG_REGS > 5) - Eterm reds_in; -#endif -#if !(NR_ARG_REGS > 4) - Eterm o_reds; -#endif + struct saved_calls *scb; reds_in = p->fcalls; - o_reds = 0; - if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) { - o_reds = reds_in; - reds_in = 0; - p->fcalls = 0; - } - p->def_arg_reg[4] = o_reds; p->def_arg_reg[5] = reds_in; +#ifdef DEBUG + p->debug_reds_in = reds_in; +#endif if (p->i == hipe_beam_pc_resume) { + p->flags |= F_HIPE_MODE; /* inform bifs where we are comming from... */ + scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL); + if (scb) + ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, scb); p->i = NULL; p->arity = 0; goto do_resume; } + + scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p); + if (!scb) + p->def_arg_reg[4] = 0; + else { + p->def_arg_reg[4] = CONTEXT_REDS; + p->fcalls = -CONTEXT_REDS + reds_in; + } } + HIPE_CHECK_PCB(p); result = HIPE_MODE_SWITCH_RES_CALL_BEAM; p->def_arg_reg[3] = result; @@ -562,14 +597,29 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) default: erts_exit(ERTS_ERROR_EXIT, "hipe_mode_switch: result %#x\r\n", result); } + + { + struct saved_calls *scb = ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, NULL); + if (!scb) + p->def_arg_reg[4] = 0; + else { + p->def_arg_reg[4] = CONTEXT_REDS; + p->fcalls -= CONTEXT_REDS; + ERTS_PROC_SET_SAVED_CALLS_BUF(p, scb); + } + } + HIPE_CHECK_PCB(p); p->def_arg_reg[3] = result; -#if NR_ARG_REGS > 4 - p->def_arg_reg[4] = o_reds; -#endif #if NR_ARG_REGS > 5 + /* + * When NR_ARG_REGS > 5, we need to protect the process' input + * reduction count (which BEAM stores in def_arg_reg[5]) from + * being clobbered by the arch glue code. + */ p->def_arg_reg[5] = reds_in; #endif + p->flags &= ~F_HIPE_MODE; return p; } diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl index af1a0d35d6..aae7651f6d 100644 --- a/erts/emulator/test/save_calls_SUITE.erl +++ b/erts/emulator/test/save_calls_SUITE.erl @@ -60,7 +60,7 @@ dont_break_reductions(Config) when is_list(Config) -> RPS1 = reds_per_sched(0), RPS2 = reds_per_sched(20), Diff = abs(RPS1 - RPS2), - true = (Diff < (0.05 * RPS1)), + true = (Diff < (0.2 * RPS1)), ok. -- cgit v1.2.3 From 403a8f3e042ba5183efcbd33c5ff293f72f62037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 4 May 2016 17:50:52 +0200 Subject: Remove ?line macros --- erts/epmd/test/epmd_SUITE.erl | 994 +++++++++++++++++++++--------------------- 1 file changed, 494 insertions(+), 500 deletions(-) (limited to 'erts') diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 763984267a..aeafe4326c 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -37,50 +37,47 @@ % Test server specific exports -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2]). - --export( - [ - register_name/1, - register_name_ipv6/1, - register_names_1/1, - register_names_2/1, - register_duplicate_name/1, - unicode_name/1, - long_unicode_name/1, - get_port_nr/1, - slow_get_port_nr/1, - unregister_others_name_1/1, - unregister_others_name_2/1, - register_overflow/1, - name_with_null_inside/1, - name_null_terminated/1, - stupid_names_req/1, - - no_data/1, - one_byte/1, - two_bytes/1, - partial_packet/1, - zero_length/1, - too_large/1, - alive_req_too_small_1/1, - alive_req_too_small_2/1, - alive_req_too_large/1, - - returns_valid_empty_extra/1, - returns_valid_populated_extra_with_nulls/1, - - names_stdout/1, - - buffer_overrun_1/1, - buffer_overrun_2/1, - no_nonlocal_register/1, - no_nonlocal_kill/1, - no_live_killing/1, - - socket_reset_before_alive2_reply_is_written/1 - ]). + init_per_group/2,end_per_group/2, + init_per_testcase/2, end_per_testcase/2]). + +-export([register_name/1, + register_name_ipv6/1, + register_names_1/1, + register_names_2/1, + register_duplicate_name/1, + unicode_name/1, + long_unicode_name/1, + get_port_nr/1, + slow_get_port_nr/1, + unregister_others_name_1/1, + unregister_others_name_2/1, + register_overflow/1, + name_with_null_inside/1, + name_null_terminated/1, + stupid_names_req/1, + + no_data/1, + one_byte/1, + two_bytes/1, + partial_packet/1, + zero_length/1, + too_large/1, + alive_req_too_small_1/1, + alive_req_too_small_2/1, + alive_req_too_large/1, + + returns_valid_empty_extra/1, + returns_valid_populated_extra_with_nulls/1, + + names_stdout/1, + + buffer_overrun_1/1, + buffer_overrun_2/1, + no_nonlocal_register/1, + no_nonlocal_kill/1, + no_live_killing/1, + + socket_reset_before_alive2_reply_is_written/1]). % Port we use for testing @@ -88,7 +85,7 @@ -define(EPMDARGS,"-packet_timeout 1"). -define(DUMMY_PORT, 1000). % Port number to register - % not in real use. +% not in real use. % Timeouts etc inside test cases. Time is in milliseconds. -define(CONN_RETRY, 4). % Times to retry connecting @@ -169,9 +166,9 @@ register_name(doc) -> register_name(suite) -> []; register_name(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = register_node("foobar"), - ?line ok = close(Sock), % Unregister + ok = epmdrun(), + {ok,Sock} = register_node("foobar"), + ok = close(Sock), % Unregister ok. register_name_ipv6(doc) -> @@ -182,14 +179,14 @@ register_name_ipv6(Config) when is_list(Config) -> % Test if the host has an IPv6 loopback address Res = gen_tcp:listen(0, [inet6, {ip, {0,0,0,0,0,0,0,1}}]), case Res of - {ok,LSock} -> - gen_tcp:close(LSock), - ?line ok = epmdrun(), - ?line {ok,Sock} = register_node6("foobar6"), - ?line ok = close(Sock), % Unregister - ok; - _Error -> - {skip, "Host does not have an IPv6 loopback address"} + {ok,LSock} -> + gen_tcp:close(LSock), + ok = epmdrun(), + {ok,Sock} = register_node6("foobar6"), + ok = close(Sock), % Unregister + ok; + _Error -> + {skip, "Host does not have an IPv6 loopback address"} end. register_names_1(doc) -> @@ -197,11 +194,11 @@ register_names_1(doc) -> register_names_1(suite) -> []; register_names_1(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock1} = register_node("foobar"), - ?line {ok,Sock2} = register_node("foozap"), - ?line ok = close(Sock1), % Unregister - ?line ok = close(Sock2), % Unregister + ok = epmdrun(), + {ok,Sock1} = register_node("foobar"), + {ok,Sock2} = register_node("foozap"), + ok = close(Sock1), % Unregister + ok = close(Sock2), % Unregister ok. register_names_2(doc) -> @@ -209,11 +206,11 @@ register_names_2(doc) -> register_names_2(suite) -> []; register_names_2(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock1} = register_node("foobar"), - ?line {ok,Sock2} = register_node("foozap"), - ?line ok = close(Sock2), % Unregister - ?line ok = close(Sock1), % Unregister + ok = epmdrun(), + {ok,Sock1} = register_node("foobar"), + {ok,Sock2} = register_node("foozap"), + ok = close(Sock2), % Unregister + ok = close(Sock1), % Unregister ok. register_duplicate_name(doc) -> @@ -221,10 +218,10 @@ register_duplicate_name(doc) -> register_duplicate_name(suite) -> []; register_duplicate_name(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = register_node("foobar"), - ?line error = register_node("foobar"), - ?line ok = close(Sock), % Unregister + ok = epmdrun(), + {ok,Sock} = register_node("foobar"), + error = register_node("foobar"), + ok = close(Sock), % Unregister ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -273,50 +270,50 @@ register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) -> register_node_v2(Addr, Port, NodeType, Prot, HVsn, LVsn, Name, Extra) -> Req = alive2_req(Port, NodeType, Prot, HVsn, LVsn, Name, Extra), case send_req(Req, Addr) of - {ok,Sock} -> - case recv(Sock,4) of - {ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} -> - {ok,Sock}; - Other -> - test_server:format("recv on sock ~w: ~p~n", - [Sock,Other]), - error - end; - error -> - error + {ok,Sock} -> + case recv(Sock,4) of + {ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} -> + {ok,Sock}; + Other -> + test_server:format("recv on sock ~w: ~p~n", + [Sock,Other]), + error + end; + error -> + error end. % Internal function to fetch information about a node port_please_v2(Name) -> case send_req([?EPMD_PORT_PLEASE2_REQ, - binary_to_list(unicode:characters_to_binary(Name))]) of - {ok,Sock} -> - case recv_until_sock_closes(Sock) of - {ok, Resp} -> - parse_port2_resp(Resp); - Other -> - test_server:format("recv on sock ~w: ~p~n", - [Sock,Other]), - error - end; - error -> - error + binary_to_list(unicode:characters_to_binary(Name))]) of + {ok,Sock} -> + case recv_until_sock_closes(Sock) of + {ok, Resp} -> + parse_port2_resp(Resp); + Other -> + test_server:format("recv on sock ~w: ~p~n", + [Sock,Other]), + error + end; + error -> + error end. parse_port2_resp(Resp) -> case list_to_binary(Resp) of - <> when Res =:= 0 -> - {ok, #node_info{port=Port,node_type=NodeType,prot=Prot, - hvsn=HVsn,lvsn=LVsn, - node_name=unicode:characters_to_list(NodeName), - extra=binary_to_list(Extra)}}; - _Other -> - test_server:format("invalid port2 resp: ~p~n", - [Resp]), - error + <> when Res =:= 0 -> + {ok, #node_info{port=Port,node_type=NodeType,prot=Prot, + hvsn=HVsn,lvsn=LVsn, + node_name=unicode:characters_to_list(NodeName), + extra=binary_to_list(Extra)}}; + _Other -> + test_server:format("invalid port2 resp: ~p~n", + [Resp]), + error end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -326,8 +323,8 @@ name_with_null_inside(doc) -> name_with_null_inside(suite) -> []; name_with_null_inside(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line error = register_node("foo\000bar"), + ok = epmdrun(), + error = register_node("foo\000bar"), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -337,8 +334,8 @@ name_null_terminated(doc) -> name_null_terminated(suite) -> []; name_null_terminated(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line error = register_node("foobar\000"), + ok = epmdrun(), + error = register_node("foobar\000"), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -351,23 +348,23 @@ stupid_names_req(Config) when is_list(Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), LongDog = test_server:timetrap(?MEDIUM_TEST_TIMEOUT), - ?line ok = epmdrun(), - ?line [FirstConn | Conn] = register_many(1, ?REG_REPEAT_LIM, "foo"), - ?line unregister_many([FirstConn]), + ok = epmdrun(), + [FirstConn | Conn] = register_many(1, ?REG_REPEAT_LIM, "foo"), + unregister_many([FirstConn]), sleep(?MEDIUM_PAUSE), - ?line ok = check_names(Conn), - ?line ok = unregister_many(Conn), + ok = check_names(Conn), + ok = unregister_many(Conn), test_server:timetrap_cancel(LongDog), ok. check_names(Conn) -> - ?line {ok,Sock} = connect_active(), - ?line {ok,Reply} = do_get_names(Sock), - ?line SortConn = lists:sort(Conn), - ?line SortReply = lists:sort(Reply), - ?line ok = check_names_cmp(SortConn, SortReply), + {ok,Sock} = connect_active(), + {ok,Reply} = do_get_names(Sock), + SortConn = lists:sort(Conn), + SortReply = lists:sort(Reply), + ok = check_names_cmp(SortConn, SortReply), ok. - + % Compare if the result was the same as was registered @@ -381,43 +378,43 @@ check_names_cmp([{Name,Port,_Sock} | Conn], [{Name,Port} | Reply]) -> -define(int16(X), [(X bsr 8) band 16#ff, X band 16#ff]). -define(u32(X1,X2,X3,X4), - (((X1) bsl 24) bor ((X2) bsl 16) bor ((X3) bsl 8) bor X4)). + (((X1) bsl 24) bor ((X2) bsl 16) bor ((X3) bsl 8) bor X4)). do_get_names(Socket) -> inet_tcp:send(Socket, [?int16(1),?EPMD_NAMES_REQ]), receive - {tcp, Socket, [P0,P1,P2,P3 | T]} -> - EpmdPort = ?u32(P0,P1,P2,P3), - if EpmdPort == ?PORT -> - names_loop(Socket, T, []); - true -> - close(Socket), - {error, address} - end; - {tcp_closed, Socket} -> - {ok, []} + {tcp, Socket, [P0,P1,P2,P3 | T]} -> + EpmdPort = ?u32(P0,P1,P2,P3), + if EpmdPort == ?PORT -> + names_loop(Socket, T, []); + true -> + close(Socket), + {error, address} + end; + {tcp_closed, Socket} -> + {ok, []} end. names_loop(Socket, Acc, Ps) -> receive - {tcp, Socket, Bytes} -> - {NAcc, NPs} = scan_names(Acc ++ Bytes, Ps), - names_loop(Socket, NAcc, NPs); - {tcp_closed, Socket} -> - {_, NPs} = scan_names(Acc, Ps), % Really needed? - {ok, NPs} + {tcp, Socket, Bytes} -> + {NAcc, NPs} = scan_names(Acc ++ Bytes, Ps), + names_loop(Socket, NAcc, NPs); + {tcp_closed, Socket} -> + {_, NPs} = scan_names(Acc, Ps), % Really needed? + {ok, NPs} end. scan_names(Buf, Ps) -> case scan_line(Buf, []) of - {Line, NBuf} -> - case parse_line(Line) of - {ok, Entry} -> - scan_names(NBuf, [Entry | Ps]); - error -> - scan_names(NBuf, Ps) - end; - [] -> {Buf, Ps} + {Line, NBuf} -> + case parse_line(Line) of + {ok, Entry} -> + scan_names(NBuf, [Entry | Ps]); + error -> + scan_names(NBuf, Ps) + end; + [] -> {Buf, Ps} end. scan_line([$\n | Buf], Line) -> {lists:reverse(Line), Buf}; @@ -426,16 +423,16 @@ scan_line([], _) -> []. parse_line([$n,$a,$m,$e,$ | Buf0]) -> case parse_name(Buf0, []) of - {Name, Buf1} -> - case Buf1 of - [$a,$t,$ ,$p,$o,$r,$t,$ | Buf2] -> - case catch list_to_integer(Buf2) of - {'EXIT', _} -> error; - Port -> {ok, {Name, Port}} - end; - _ -> error - end; - error -> error + {Name, Buf1} -> + case Buf1 of + [$a,$t,$ ,$p,$o,$r,$t,$ | Buf2] -> + case catch list_to_integer(Buf2) of + {'EXIT', _} -> error; + Port -> {ok, {Name, Port}} + end; + _ -> error + end; + error -> error end; parse_line(_) -> error. @@ -465,23 +462,23 @@ slow_get_port_nr(Config) when is_list(Config) -> % Internal function used above port_request(M) -> - ?line ok = epmdrun(), + ok = epmdrun(), Port = 1042, - ?line {ok,RSock} = register_node("foo", Port), - ?line {ok,Sock} = connect(), - ?line ok = send(Sock,[size16(M),M]), - ?line case recv_until_sock_closes(Sock) of - {ok, Resp} -> - ?line close(RSock), - ?line {ok,Rec} = parse_port2_resp(Resp), - ?line Port = Rec#node_info.port, - ok; - Other -> - ?line close(RSock), - ?line test_server:format("recv on sock ~w: ~p~n", - [Sock,Other]), - ?line throw({error,Other}) - end, + {ok,RSock} = register_node("foo", Port), + {ok,Sock} = connect(), + ok = send(Sock,[size16(M),M]), + case recv_until_sock_closes(Sock) of + {ok, Resp} -> + close(RSock), + {ok,Rec} = parse_port2_resp(Resp), + Port = Rec#node_info.port, + ok; + Other -> + close(RSock), + test_server:format("recv on sock ~w: ~p~n", + [Sock,Other]), + throw({error,Other}) + end, ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -491,14 +488,14 @@ unregister_others_name_1(doc) -> unregister_others_name_1(suite) -> []; unregister_others_name_1(Config) when is_list(Config) -> - ?line ok = epmdrun("-relaxed_command_check"), - ?line {ok,RSock} = register_node("foo"), - ?line {ok,Sock} = connect(), + ok = epmdrun("-relaxed_command_check"), + {ok,RSock} = register_node("foo"), + {ok,Sock} = connect(), M = [?EPMD_STOP_REQ,"foo"], - ?line ok = send(Sock,[size16(M),M]), + ok = send(Sock,[size16(M),M]), R = "STOPPED", - ?line {ok,R} = recv(Sock,length(R)), - ?line ok = close(RSock), + {ok,R} = recv(Sock,length(R)), + ok = close(RSock), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -508,12 +505,12 @@ unregister_others_name_2(doc) -> unregister_others_name_2(suite) -> []; unregister_others_name_2(Config) when is_list(Config) -> - ?line ok = epmdrun("-relaxed_command_check"), - ?line {ok,Sock} = connect(), + ok = epmdrun("-relaxed_command_check"), + {ok,Sock} = connect(), M = [?EPMD_STOP_REQ,"xxx42"], - ?line ok = send(Sock,[size16(M),M]), + ok = send(Sock,[size16(M),M]), R = "NOEXIST", - ?line {ok,R} = recv(Sock,length(R)), + {ok,R} = recv(Sock,length(R)), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -526,32 +523,32 @@ register_overflow(Config) when is_list(Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), LongDog = test_server:timetrap(?LONG_TEST_TIMEOUT), - ?line ok = epmdrun(), - ?line Conn = register_many(1, ?REG_REPEAT_LIM, "foo"), + ok = epmdrun(), + Conn = register_many(1, ?REG_REPEAT_LIM, "foo"), Count = length(Conn), - ?line ok = unregister_many(Conn), + ok = unregister_many(Conn), sleep(?MEDIUM_PAUSE), test_server:format("Limit was ~w names, now reg/unreg all 10 times~n", - [Count]), - ?line ok = register_repeat(Count), + [Count]), + ok = register_repeat(Count), sleep(?MEDIUM_PAUSE), - ?line ok = rregister_repeat(Count), + ok = rregister_repeat(Count), sleep(?MEDIUM_PAUSE), - ?line ok = register_repeat(Count), + ok = register_repeat(Count), sleep(?MEDIUM_PAUSE), - ?line ok = rregister_repeat(Count), + ok = rregister_repeat(Count), sleep(?MEDIUM_PAUSE), - ?line ok = register_repeat(Count), + ok = register_repeat(Count), sleep(?MEDIUM_PAUSE), - ?line ok = rregister_repeat(Count), + ok = rregister_repeat(Count), sleep(?MEDIUM_PAUSE), - ?line ok = register_repeat(Count), + ok = register_repeat(Count), sleep(?MEDIUM_PAUSE), - ?line ok = rregister_repeat(Count), + ok = rregister_repeat(Count), sleep(?MEDIUM_PAUSE), - ?line ok = register_repeat(Count), + ok = register_repeat(Count), sleep(?MEDIUM_PAUSE), - ?line ok = rregister_repeat(Count), + ok = rregister_repeat(Count), test_server:timetrap_cancel(LongDog), ok. @@ -559,20 +556,20 @@ register_repeat(Count) -> Conn = register_many(1, ?REG_REPEAT_LIM, "foo"), ok = unregister_many(Conn), if - length(Conn) == Count -> - ok; - true -> - error + length(Conn) == Count -> + ok; + true -> + error end. rregister_repeat(Count) -> Conn = register_many(1, ?REG_REPEAT_LIM, "foo"), ok = unregister_many(lists:reverse(Conn)), if - length(Conn) == Count -> - ok; - true -> - error + length(Conn) == Count -> + ok; + true -> + error end. % Return count of successful registrations @@ -584,23 +581,22 @@ register_many(I, N, Prefix) -> Name = gen_name(Prefix, I), Port = ?DUMMY_PORT + I, % Just make it up case register_node(Name, Port) of - {ok,Sock} -> - [{Name,Port,Sock} | register_many(I + 1, N, Prefix)]; - Any -> - test_server:format("Can't register: ~w of 1..~w ~w~n", - [Name,N,Any]), - [] + {ok,Sock} -> + [{Name,Port,Sock} | register_many(I + 1, N, Prefix)]; + Any -> + test_server:format("Can't register: ~w of 1..~w ~w~n", [Name,N,Any]), + [] end. unregister_many([]) -> ok; unregister_many([{Name,_Port,Sock} | Socks]) -> case close(Sock) of - ok -> - unregister_many(Socks); - Any -> - test_server:format("Can't unregister: ~w reason ~w~n", [Name,Any]), - error + ok -> + unregister_many(Socks); + Any -> + test_server:format("Can't unregister: ~w reason ~w~n", [Name,Any]), + error end. gen_name(Str,Int) -> @@ -613,10 +609,10 @@ no_data(doc) -> no_data(suite) -> []; no_data(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = connect(), + ok = epmdrun(), + {ok,Sock} = connect(), sleep(?LONG_PAUSE), - ?line closed = recv(Sock,1), + closed = recv(Sock,1), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -626,11 +622,11 @@ one_byte(doc) -> one_byte(suite) -> []; one_byte(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = connect(), - ?line ok = send(Sock,[0]), + ok = epmdrun(), + {ok,Sock} = connect(), + ok = send(Sock,[0]), sleep(?LONG_PAUSE), - ?line closed = recv(Sock,1), + closed = recv(Sock,1), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -640,11 +636,11 @@ two_bytes(doc) -> two_bytes(suite) -> []; two_bytes(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = connect(), - ?line ok = send(Sock,[put16(3)]), + ok = epmdrun(), + {ok,Sock} = connect(), + ok = send(Sock,[put16(3)]), sleep(?LONG_PAUSE), - ?line closed = recv(Sock,1), + closed = recv(Sock,1), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -654,11 +650,11 @@ partial_packet(doc) -> partial_packet(suite) -> []; partial_packet(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = connect(), - ?line ok = send(Sock,[put16(100),"only a few bytes"]), + ok = epmdrun(), + {ok,Sock} = connect(), + ok = send(Sock,[put16(100),"only a few bytes"]), sleep(?LONG_PAUSE), - ?line closed = recv(Sock,1), + closed = recv(Sock,1), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -668,11 +664,11 @@ zero_length(doc) -> zero_length(suite) -> []; zero_length(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = connect(), - ?line ok = send(Sock,[0,0,0,0,0,0,0,0,0,0]), + ok = epmdrun(), + {ok,Sock} = connect(), + ok = send(Sock,[0,0,0,0,0,0,0,0,0,0]), sleep(?MEDIUM_PAUSE), - ?line closed = recv(Sock,1), + closed = recv(Sock,1), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -682,18 +678,18 @@ too_large(doc) -> too_large(suite) -> []; too_large(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = connect(), + ok = epmdrun(), + {ok,Sock} = connect(), Size = 63000, M = lists:duplicate(Size, $z), - ?line ok = send(Sock,[put16(Size),M]), + ok = send(Sock,[put16(Size),M]), sleep(?MEDIUM_PAUSE), % With such a large packet, even the writes can fail as the % daemon closes before everything is delivered -> econnaborted case recv(Sock,1) of - closed -> ok; - {error,econnaborted} -> ok; - Other -> exit({unexpected,Other}) + closed -> ok; + {error,econnaborted} -> ok; + Other -> exit({unexpected,Other}) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -703,13 +699,13 @@ alive_req_too_small_1(doc) -> alive_req_too_small_1(suite) -> []; alive_req_too_small_1(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = connect(), + ok = epmdrun(), + {ok,Sock} = connect(), M = [?EPMD_ALIVE2_REQ, put16(?DUMMY_PORT),$M,0, put16(5), - put16(5),put16(0)], - ?line ok = send(Sock, [size16(M), M]), + put16(5),put16(0)], + ok = send(Sock, [size16(M), M]), sleep(?MEDIUM_PAUSE), - ?line closed = recv(Sock,1), + closed = recv(Sock,1), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -719,13 +715,13 @@ alive_req_too_small_2(doc) -> alive_req_too_small_2(suite) -> []; alive_req_too_small_2(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = connect(), + ok = epmdrun(), + {ok,Sock} = connect(), M = [?EPMD_ALIVE2_REQ, put16(?DUMMY_PORT),$M,0, put16(5), - put16(5)], - ?line ok = send(Sock, [size16(M), M]), + put16(5)], + ok = send(Sock, [size16(M), M]), sleep(?MEDIUM_PAUSE), - ?line closed = recv(Sock,1), + closed = recv(Sock,1), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -735,28 +731,26 @@ alive_req_too_large(doc) -> alive_req_too_large(suite) -> []; alive_req_too_large(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = connect(), - L = [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - ], + ok = epmdrun(), + {ok,Sock} = connect(), + L = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], S = length(lists:flatten(L)), M = [?EPMD_ALIVE2_REQ, put16(?DUMMY_PORT),$M,0, put16(5), - put16(5), put16(S),L,put16(0)], - ?line ok = send(Sock, [size16(M), M]), + put16(5), put16(S),L,put16(0)], + ok = send(Sock, [size16(M), M]), sleep(?MEDIUM_PAUSE), - ?line {ok,[?EPMD_ALIVE2_RESP,1]} = recv(Sock,2), + {ok,[?EPMD_ALIVE2_RESP,1]} = recv(Sock,2), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -766,10 +760,10 @@ returns_valid_empty_extra(doc) -> returns_valid_empty_extra(suite) -> []; returns_valid_empty_extra(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", []), - ?line {ok,#node_info{extra=[]}} = port_please_v2("foo"), - ?line ok = close(Sock), + ok = epmdrun(), + {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", []), + {ok,#node_info{extra=[]}} = port_please_v2("foo"), + ok = close(Sock), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -779,10 +773,10 @@ returns_valid_populated_extra_with_nulls(doc) -> returns_valid_populated_extra_with_nulls(suite) -> []; returns_valid_populated_extra_with_nulls(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", "ABC\000\000"), - ?line {ok,#node_info{extra="ABC\000\000"}} = port_please_v2("foo"), - ?line ok = close(Sock), + ok = epmdrun(), + {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", "ABC\000\000"), + {ok,#node_info{extra="ABC\000\000"}} = port_please_v2("foo"), + ok = close(Sock), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -792,15 +786,15 @@ names_stdout(doc) -> names_stdout(suite) -> []; names_stdout(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,Sock} = register_node("foobar"), - ?line ok = epmdrun("-names"), - ?line {ok, Data} = receive {_Port, {data, D}} -> {ok, D} - after 10000 -> {error, timeout} - end, - ?line {match,_} = re:run(Data, "^epmd: up and running", [multiline]), - ?line {match,_} = re:run(Data, "^name foobar at port", [multiline]), - ?line ok = close(Sock), + ok = epmdrun(), + {ok,Sock} = register_node("foobar"), + ok = epmdrun("-names"), + {ok, Data} = receive {_Port, {data, D}} -> {ok, D} + after 10000 -> {error, timeout} + end, + {match,_} = re:run(Data, "^epmd: up and running", [multiline]), + {match,_} = re:run(Data, "^name foobar at port", [multiline]), + ok = close(Sock), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -810,44 +804,44 @@ buffer_overrun_1(suite) -> buffer_overrun_1(doc) -> ["Test security vulnerability in fake extra lengths in alive2_req"]; buffer_overrun_1(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line true = alltrue([hostile(N) || N <- lists:seq(1,10000)]), + ok = epmdrun(), + true = alltrue([hostile(N) || N <- lists:seq(1,10000)]), ok. buffer_overrun_2(suite) -> []; buffer_overrun_2(doc) -> ["Test security vulnerability in fake extra lengths in alive2_req"]; buffer_overrun_2(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line [false | Rest] = [hostile2(N) || N <- lists:seq(255*4,10000)], - ?line true = alltrue(Rest), + ok = epmdrun(), + [false | Rest] = [hostile2(N) || N <- lists:seq(255*4,10000)], + true = alltrue(Rest), ok. hostile(N) -> try - Bin= <<$x:8,4747:16,$M:8,0:8,5:16,5:16,5:16,"gurka",N:16>>, - S = size(Bin), - {ok,E}=connect_sturdy(), - gen_tcp:send(E,[<>,Bin]), - closed = recv(E,1), - gen_tcp:close(E), - true + Bin= <<$x:8,4747:16,$M:8,0:8,5:16,5:16,5:16,"gurka",N:16>>, + S = size(Bin), + {ok,E}=connect_sturdy(), + gen_tcp:send(E,[<>,Bin]), + closed = recv(E,1), + gen_tcp:close(E), + true catch - _:_ -> - false + _:_ -> + false end. hostile2(N) -> try - B2 = list_to_binary(lists:duplicate(N,255)), - Bin= <<$x:8,4747:16,$M:8,0:8,5:16,5:16,5:16,"gurka",N:16,B2/binary>>, - S = size(Bin), - {ok,E}=connect_sturdy(), - gen_tcp:send(E,[<>,Bin]), - Z = recv(E,2), - gen_tcp:close(E), - (Z =:= closed) or (Z =:= {ok, [$y,1]}) + B2 = list_to_binary(lists:duplicate(N,255)), + Bin= <<$x:8,4747:16,$M:8,0:8,5:16,5:16,5:16,"gurka",N:16,B2/binary>>, + S = size(Bin), + {ok,E}=connect_sturdy(), + gen_tcp:send(E,[<>,Bin]), + Z = recv(E,2), + gen_tcp:close(E), + (Z =:= closed) or (Z =:= {ok, [$y,1]}) catch - _A:_B -> - false + _A:_B -> + false end. alltrue([]) -> @@ -862,41 +856,41 @@ no_nonlocal_register(suite) -> no_nonlocal_register(doc) -> ["Ensure that we cannot register throug a nonlocal connection"]; no_nonlocal_register(Config) when is_list(Config) -> - ?line case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of - {SSH,Name} when is_list(Name), is_list(SSH) -> - do_no_nonlocal_register(Config,Name); - {false,_} -> - {skip, "No ssh command found to create proxy"}; - _ -> - {skip, "No ssh_proxy_host configured in ts.config"} - end. + case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of + {SSH,Name} when is_list(Name), is_list(SSH) -> + do_no_nonlocal_register(Config,Name); + {false,_} -> + {skip, "No ssh command found to create proxy"}; + _ -> + {skip, "No ssh_proxy_host configured in ts.config"} + end. do_no_nonlocal_register(Config,SSHHost) when is_list(Config) -> - ?line ok = epmdrun(), - ?line ProxyPort = proxy_port(), - ?line ok = ssh_proxy(SSHHost,ProxyPort), + ok = epmdrun(), + ProxyPort = proxy_port(), + ok = ssh_proxy(SSHHost,ProxyPort), Res = try - ?line Name = "gurka_" - %++ - %integer_to_list(A1)++"_"++ - %integer_to_list(A2)++"_"++ - %integer_to_list(A3)++"_"++ - %integer_to_list(A4) - , - ?line Bname = list_to_binary(Name), - ?line NameS = byte_size(Bname), - ?line Bin= <<$x:8,4747:16,$M:8,0:8,5:16, - 5:16,NameS:16,Bname/binary, - 0:16>>, - ?line S = size(Bin), - ?line {ok, E} = connect("localhost",ProxyPort,passive), - ?line gen_tcp:send(E,[<>,Bin]), - ?line closed = recv(E,1), - ?line gen_tcp:close(E), - true - catch - _:_ -> - false - end, + Name = "gurka_" + %++ + %integer_to_list(A1)++"_"++ + %integer_to_list(A2)++"_"++ + %integer_to_list(A3)++"_"++ + %integer_to_list(A4) + , + Bname = list_to_binary(Name), + NameS = byte_size(Bname), + Bin= <<$x:8,4747:16,$M:8,0:8,5:16, + 5:16,NameS:16,Bname/binary, + 0:16>>, + S = size(Bin), + {ok, E} = connect("localhost",ProxyPort,passive), + gen_tcp:send(E,[<>,Bin]), + closed = recv(E,1), + gen_tcp:close(E), + true + catch + _:_ -> + false + end, %erlang:display(Res), true = Res, ok. @@ -906,32 +900,32 @@ no_nonlocal_kill(suite) -> no_nonlocal_kill(doc) -> ["Ensure that we cannot kill through nonlocal connection"]; no_nonlocal_kill(Config) when is_list(Config) -> - ?line case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of - {SSH,Name} when is_list(Name), is_list(SSH) -> - do_no_nonlocal_kill(Config,Name); - {false,_} -> - {skip, "No ssh command found to create proxy"}; - _ -> - {skip, "No ssh_proxy_host configured in ts.config"} - end. + case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of + {SSH,Name} when is_list(Name), is_list(SSH) -> + do_no_nonlocal_kill(Config,Name); + {false,_} -> + {skip, "No ssh command found to create proxy"}; + _ -> + {skip, "No ssh_proxy_host configured in ts.config"} + end. do_no_nonlocal_kill(Config,SSHHost) when is_list(Config) -> - ?line ok = epmdrun(), - ?line ProxyPort = proxy_port(), - ?line ok = ssh_proxy(SSHHost,ProxyPort), + ok = epmdrun(), + ProxyPort = proxy_port(), + ok = ssh_proxy(SSHHost,ProxyPort), Res = try - {ok, E} = connect("localhost",ProxyPort,passive), - M = [?EPMD_KILL_REQ], - send(E, [size16(M), M]), - closed = recv(E,2), - gen_tcp:close(E), - sleep(?MEDIUM_PAUSE), - {ok, E2} = connect("localhost",ProxyPort,passive), - gen_tcp:close(E2), - true - catch - _:_ -> - false - end, + {ok, E} = connect("localhost",ProxyPort,passive), + M = [?EPMD_KILL_REQ], + send(E, [size16(M), M]), + closed = recv(E,2), + gen_tcp:close(E), + sleep(?MEDIUM_PAUSE), + {ok, E2} = connect("localhost",ProxyPort,passive), + gen_tcp:close(E2), + true + catch + _:_ -> + false + end, %erlang:display(Res), true = Res, ok. @@ -941,25 +935,25 @@ no_live_killing(doc) -> no_live_killing(suite) -> []; no_live_killing(Config) when is_list(Config) -> - ?line ok = epmdrun(), - ?line {ok,RSock} = register_node("foo"), - ?line {ok,Sock} = connect(), - ?line M = [?EPMD_KILL_REQ], - ?line ok = send(Sock,[size16(M),M]), - ?line {ok,"NO"} = recv(Sock,2), - ?line close(Sock), - ?line {ok,Sock2} = connect(), - ?line M2 = [?EPMD_STOP_REQ,"foo"], - ?line ok = send(Sock2,[size16(M2),M2]), - ?line closed = recv(Sock2,1), - ?line close(Sock2), - ?line close(RSock), - ?line sleep(?MEDIUM_PAUSE), - ?line {ok,Sock3} = connect(), - ?line M3 = [?EPMD_KILL_REQ], - ?line ok = send(Sock3,[size16(M3),M3]), - ?line {ok,"OK"} = recv(Sock3,2), - ?line close(Sock3), + ok = epmdrun(), + {ok,RSock} = register_node("foo"), + {ok,Sock} = connect(), + M = [?EPMD_KILL_REQ], + ok = send(Sock,[size16(M),M]), + {ok,"NO"} = recv(Sock,2), + close(Sock), + {ok,Sock2} = connect(), + M2 = [?EPMD_STOP_REQ,"foo"], + ok = send(Sock2,[size16(M2),M2]), + closed = recv(Sock2,1), + close(Sock2), + close(RSock), + sleep(?MEDIUM_PAUSE), + {ok,Sock3} = connect(), + M3 = [?EPMD_KILL_REQ], + ok = send(Sock3,[size16(M3),M3]), + {ok,"OK"} = recv(Sock3,2), + close(Sock3), ok. socket_reset_before_alive2_reply_is_written(doc) -> @@ -971,20 +965,20 @@ socket_reset_before_alive2_reply_is_written(Config) when is_list(Config) -> %% - delay_write for easier triggering of race condition %% - relaxed_command_check for gracefull shutdown of epmd even if there %% is stuck node. - ?line ok = epmdrun("-delay_write 1 -relaxed_command_check"), + ok = epmdrun("-delay_write 1 -relaxed_command_check"), %% We can't use send_req/1 directly as we want to do inet:setopts/2 %% on our socket. - ?line {ok, Sock} = connect(), + {ok, Sock} = connect(), %% Issuing close/1 on such socket will result in immediate RST packet. - ?line ok = inet:setopts(Sock, [{linger, {true, 0}}]), + ok = inet:setopts(Sock, [{linger, {true, 0}}]), Req = alive2_req(4711, 77, 0, 5, 5, "test", []), - ?line ok = send(Sock, [size16(Req), Req]), + ok = send(Sock, [size16(Req), Req]), timer:sleep(500), %% Wait for the first 1/2 of delay_write before closing - ?line ok = close(Sock), + ok = close(Sock), timer:sleep(500 + ?SHORT_PAUSE), %% Wait for the other 1/2 of delay_write @@ -992,11 +986,11 @@ socket_reset_before_alive2_reply_is_written(Config) when is_list(Config) -> %% Should be removed when this is issue is fixed there. timer:sleep(1000), - ?line {ok, SockForNames} = connect_active(), + {ok, SockForNames} = connect_active(), %% And there should be no stuck nodes - ?line {ok, []} = do_get_names(SockForNames), - ?line ok = close(SockForNames), + {ok, []} = do_get_names(SockForNames), + ok = close(SockForNames), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1005,14 +999,14 @@ socket_reset_before_alive2_reply_is_written(Config) when is_list(Config) -> cleanup() -> sleep(?MEDIUM_PAUSE), case connect() of - {ok,Sock} -> - M = [?EPMD_KILL_REQ], - send(Sock, [size16(M), M]), - recv(Sock,length("OK")), - close(Sock), - sleep(?MEDIUM_PAUSE); - _ -> - true + {ok,Sock} -> + M = [?EPMD_KILL_REQ], + send(Sock, [size16(M), M]), + recv(Sock,length("OK")), + close(Sock), + sleep(?MEDIUM_PAUSE); + _ -> + true end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1022,11 +1016,11 @@ proxy_port() -> ?PORT+1. ssh_proxy(SSHHost,ProxyPort) -> - ?line Host = lists:nth(2,string:tokens(atom_to_list(node()),"@")), + Host = lists:nth(2,string:tokens(atom_to_list(node()),"@")), % Requires proxy to be a unix host with the command 'read' accessible - ?line osrun("ssh -L "++integer_to_list(ProxyPort)++":"++Host++":" - ++integer_to_list(?PORT)++" "++SSHHost++" read"). - + osrun("ssh -L "++integer_to_list(ProxyPort)++":"++Host++":" + ++integer_to_list(?PORT)++" "++SSHHost++" read"). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1036,21 +1030,21 @@ epmdrun() -> epmdrun([]). epmdrun(Args) -> case os:find_executable(epmd) of - false -> - {error, {could_not_find_epmd_in_path}}; - Path -> - epmdrun(Path,Args) + false -> + {error, {could_not_find_epmd_in_path}}; + Path -> + epmdrun(Path,Args) end. epmdrun(Epmd,Args0) -> - %% test_server:format("epmdrun() => Epmd = ~p",[Epmd]), + %% test_server:format("epmdrun() => Epmd = ~p",[Epmd]), Args = case Args0 of - [] -> - []; - O -> - " "++O - end, - osrun("\"" ++ Epmd ++ "\"" ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT) ++ Args). + [] -> + []; + O -> + " "++O + end, + osrun("\"" ++ Epmd ++ "\"" ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT) ++ Args). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1059,7 +1053,7 @@ epmdrun(Epmd,Args0) -> osrun(Cmd) -> _ = open_port({spawn, Cmd}, []), ok. - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Wrappers of TCP functions @@ -1084,16 +1078,16 @@ connect(Addr, Port, Mode) -> connect(Addr, Port, Mode, ?CONN_SLEEP, ?CONN_RETRY). connect(Addr, Port, Mode, Sleep, Retry) -> case connect_repeat(Addr, Retry, Port, Mode, Sleep) of - {ok,Sock} -> - {ok,Sock}; - {error,timeout} -> - timeout; - {error,Reason} -> - test_server:format("connect: error: ~w~n",[Reason]), - error; - Any -> - test_server:format("connect: unknown message: ~w~n",[Any]), - exit(1) + {ok,Sock} -> + {ok,Sock}; + {error,timeout} -> + timeout; + {error,Reason} -> + test_server:format("connect: error: ~w~n",[Reason]), + error; + Any -> + test_server:format("connect: unknown message: ~w~n",[Any]), + exit(1) end. @@ -1104,33 +1098,33 @@ connect_repeat(Addr, 1, Port, Mode, _Sleep) -> connect_mode(Addr,Port, Mode); connect_repeat(Addr,Retry, Port, Mode, Sleep) -> case connect_mode(Addr,Port, Mode) of - {ok,Sock} -> - {ok,Sock}; - {error,Reason} -> - test_server:format("connect: error: ~w~n",[Reason]), - timer:sleep(Sleep), - connect_repeat(Addr, Retry - 1, Port, Mode, Sleep); - Any -> - test_server:format("connect: unknown message: ~w~n",[Any]), - exit(1) + {ok,Sock} -> + {ok,Sock}; + {error,Reason} -> + test_server:format("connect: error: ~w~n",[Reason]), + timer:sleep(Sleep), + connect_repeat(Addr, Retry - 1, Port, Mode, Sleep); + Any -> + test_server:format("connect: unknown message: ~w~n",[Any]), + exit(1) end. connect_mode(Addr,Port, active) -> gen_tcp:connect(Addr, Port, [{packet, 0}], ?CONN_TIMEOUT); connect_mode(Addr, Port, passive) -> gen_tcp:connect(Addr, Port, [{packet, 0}, {active, false}], - ?CONN_TIMEOUT). + ?CONN_TIMEOUT). close(Sock) -> case gen_tcp:close(Sock) of - {error,_} -> - error; - ok -> - ok; - Any -> - test_server:format("unknown message: ~w~n",[Any]), - exit(1) + {error,_} -> + error; + ok -> + ok; + Any -> + test_server:format("unknown message: ~w~n",[Any]), + exit(1) end. recv(Sock, Len) -> @@ -1138,19 +1132,19 @@ recv(Sock, Len) -> recv(Sock, Len, Timeout) -> case gen_tcp:recv(Sock, Len, Timeout) of - {ok,[]} -> % Should not be the case - recv(Sock, 1, 1); % any longer - {ok,Data} -> - {ok,Data}; - {error,timeout} -> - timeout; - {error,closed} -> - closed; - {error,_}=Error -> - Error; - Any -> - test_server:format("unknown message: ~w~n",[Any]), - exit(1) + {ok,[]} -> % Should not be the case + recv(Sock, 1, 1); % any longer + {ok,Data} -> + {ok,Data}; + {error,timeout} -> + timeout; + {error,closed} -> + closed; + {error,_}=Error -> + Error; + Any -> + test_server:format("unknown message: ~w~n",[Any]), + exit(1) end. %% Send data to socket. The list can be non flat and contain @@ -1159,12 +1153,12 @@ recv(Sock, Len, Timeout) -> send(Sock, SendSpec) -> case send(SendSpec, [], Sock) of - {ok,[]} -> - ok; - {ok,RevBytes} -> - send_direct(Sock, lists:reverse(RevBytes)); - Any -> - Any + {ok,[]} -> + ok; + {ok,RevBytes} -> + send_direct(Sock, lists:reverse(RevBytes)); + Any -> + Any end. @@ -1179,54 +1173,54 @@ send([Byte | Spec], RevBytes, Sock) when is_integer(Byte) -> send(Spec, [Byte | RevBytes], Sock); send([List | Spec], RevBytes, Sock) when is_list(List) -> case send(List, RevBytes, Sock) of - {ok,Left} -> - send(Spec, Left, Sock); - Other -> - Other + {ok,Left} -> + send(Spec, Left, Sock); + Other -> + Other end; send([d | Spec], RevBytes, Sock) -> send([{d,1000} | Spec], RevBytes, Sock); send([{d,S} | Spec], RevBytes, Sock) -> case send_direct(Sock, lists:reverse(RevBytes)) of - ok -> - timer:sleep(S), - send(Spec, [], Sock); - Any -> - Any + ok -> + timer:sleep(S), + send(Spec, [], Sock); + Any -> + Any end. %%%% send_direct(Sock, Bytes) -> case gen_tcp:send(Sock, Bytes) of - ok -> - ok; - {error, closed} -> - closed; - {error, _Reason} -> - error; - Any -> - test_server:format("unknown message: ~w~n",[Any]), - Any + ok -> + ok; + {error, closed} -> + closed; + {error, _Reason} -> + error; + Any -> + test_server:format("unknown message: ~w~n",[Any]), + Any end. send_req(Req) -> send_req(Req, "localhost"). send_req(Req, Addr) -> case connect(Addr) of - {ok,Sock} -> - case send(Sock, [size16(Req), Req]) of - ok -> - {ok,Sock}; - Other -> - test_server:format("Failed to send ~w on sock ~w: ~w~n", - [Req,Sock,Other]), - error - end; - Other -> - test_server:format("Connect failed when sending ~w: ~p~n", - [Req, Other]), - error + {ok,Sock} -> + case send(Sock, [size16(Req), Req]) of + ok -> + {ok,Sock}; + Other -> + test_server:format("Failed to send ~w on sock ~w: ~w~n", + [Req,Sock,Other]), + error + end; + Other -> + test_server:format("Connect failed when sending ~w: ~p~n", + [Req, Other]), + error end. recv_until_sock_closes(Sock) -> @@ -1234,12 +1228,12 @@ recv_until_sock_closes(Sock) -> recv_until_sock_closes_2(Sock,AccData) -> case recv(Sock,0) of - {ok,Data} -> - recv_until_sock_closes_2(Sock,AccData++Data); - closed -> - {ok,AccData}; - Other -> - Other + {ok,Data} -> + recv_until_sock_closes_2(Sock,AccData++Data); + closed -> + {ok,AccData}; + Other -> + Other end. sleep(MilliSeconds) -> @@ -1261,7 +1255,7 @@ flat_count([H|T], N) when is_list(H) -> flat_count([_|T], N) -> flat_count(T, N); flat_count([], N) -> N. - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From beead8e0e29ff6e62f5044f590665d7e02229469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20L=C3=A5ng?= Date: Mon, 2 May 2016 19:35:04 +0200 Subject: hipe_x86: Patch relocations with implicit addends LLVM likes to use relocation addends on x86, which HiPE was ignoring, causing crashes. --- erts/emulator/hipe/hipe_x86.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_x86.c b/erts/emulator/hipe/hipe_x86.c index 3d25646231..5f6c8c200e 100644 --- a/erts/emulator/hipe/hipe_x86.c +++ b/erts/emulator/hipe/hipe_x86.c @@ -37,7 +37,7 @@ void hipe_patch_load_fe(Uint32 *address, Uint32 value) { /* address points to a disp32 or imm32 operand */ - *address = value; + *address += value; } int hipe_patch_insn(void *address, Uint32 value, Eterm type) @@ -54,7 +54,7 @@ int hipe_patch_insn(void *address, Uint32 value, Eterm type) default: return -1; } - *(Uint32*)address = value; + *(Uint32*)address += value; return 0; } -- cgit v1.2.3 From 56b7f0078a269973f6cc90b63667e61c1f9dbedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 4 May 2016 17:59:33 +0200 Subject: Modernize use of timetraps --- erts/epmd/test/epmd_SUITE.erl | 46 +++++++++++-------------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) (limited to 'erts') diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index aeafe4326c..29b10f2d29 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -23,22 +23,19 @@ % Timeout for test cases (rather long to work on slow machines) --define(SHORT_TEST_TIMEOUT, ?t:seconds(30)). % Default --define(MEDIUM_TEST_TIMEOUT, ?t:minutes(3)). --define(LONG_TEST_TIMEOUT, ?t:minutes(10)). +-define(MEDIUM_TEST_TIMEOUT, {minutes,3}). +-define(LONG_TEST_TIMEOUT, {minutes,10}). % Delay inserted into code -define(SHORT_PAUSE, 100). --define(MEDIUM_PAUSE, ?t:seconds(1)). --define(LONG_PAUSE, ?t:seconds(5)). +-define(MEDIUM_PAUSE, 1000). +-define(LONG_PAUSE, 5000). % Information about nodes -record(node_info, {port, node_type, prot, lvsn, hvsn, node_name, extra}). % Test server specific exports --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0, groups/0, init_per_testcase/2, end_per_testcase/2]). -export([register_name/1, register_name_ipv6/1, @@ -108,7 +105,9 @@ %% all/1 %% -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, ?MEDIUM_TEST_TIMEOUT}]. all() -> [register_name, register_name_ipv6, @@ -131,32 +130,16 @@ groups() -> [{buffer_overrun, [], [buffer_overrun_1, buffer_overrun_2]}]. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - %% %% Run before and after each test case %% init_per_testcase(_Func, Config) -> - Dog = test_server:timetrap(?MEDIUM_TEST_TIMEOUT), cleanup(), - [{watchdog, Dog} | Config]. + Config. -end_per_testcase(_Func, Config) -> +end_per_testcase(_Func, _Config) -> cleanup(), - Dog = ?config(watchdog, Config), - catch test_server:timetrap_cancel(Dog), % We may have canceled already ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -345,16 +328,12 @@ stupid_names_req(doc) -> stupid_names_req(suite) -> []; stupid_names_req(Config) when is_list(Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - LongDog = test_server:timetrap(?MEDIUM_TEST_TIMEOUT), ok = epmdrun(), [FirstConn | Conn] = register_many(1, ?REG_REPEAT_LIM, "foo"), unregister_many([FirstConn]), sleep(?MEDIUM_PAUSE), ok = check_names(Conn), ok = unregister_many(Conn), - test_server:timetrap_cancel(LongDog), ok. check_names(Conn) -> @@ -520,9 +499,7 @@ register_overflow(doc) -> register_overflow(suite) -> []; register_overflow(Config) when is_list(Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - LongDog = test_server:timetrap(?LONG_TEST_TIMEOUT), + ct:timetrap(?LONG_TEST_TIMEOUT), ok = epmdrun(), Conn = register_many(1, ?REG_REPEAT_LIM, "foo"), Count = length(Conn), @@ -549,7 +526,6 @@ register_overflow(Config) when is_list(Config) -> ok = register_repeat(Count), sleep(?MEDIUM_PAUSE), ok = rregister_repeat(Count), - test_server:timetrap_cancel(LongDog), ok. register_repeat(Count) -> -- cgit v1.2.3 From 58ee80fc615e19d1c2f2a1f69aa1c92fa9ab4f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 4 May 2016 18:01:28 +0200 Subject: Replace use of test_server:format/2 with io:format/2 There is no practial difference. --- erts/epmd/test/epmd_SUITE.erl | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 29b10f2d29..4fb8875cde 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -258,8 +258,7 @@ register_node_v2(Addr, Port, NodeType, Prot, HVsn, LVsn, Name, Extra) -> {ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} -> {ok,Sock}; Other -> - test_server:format("recv on sock ~w: ~p~n", - [Sock,Other]), + io:format("recv on sock ~w: ~p~n", [Sock,Other]), error end; error -> @@ -276,8 +275,7 @@ port_please_v2(Name) -> {ok, Resp} -> parse_port2_resp(Resp); Other -> - test_server:format("recv on sock ~w: ~p~n", - [Sock,Other]), + io:format("recv on sock ~w: ~p~n", [Sock,Other]), error end; error -> @@ -294,8 +292,7 @@ parse_port2_resp(Resp) -> node_name=unicode:characters_to_list(NodeName), extra=binary_to_list(Extra)}}; _Other -> - test_server:format("invalid port2 resp: ~p~n", - [Resp]), + io:format("invalid port2 resp: ~p~n", [Resp]), error end. @@ -454,8 +451,7 @@ port_request(M) -> ok; Other -> close(RSock), - test_server:format("recv on sock ~w: ~p~n", - [Sock,Other]), + io:format("recv on sock ~w: ~p~n", [Sock,Other]), throw({error,Other}) end, ok. @@ -505,8 +501,7 @@ register_overflow(Config) when is_list(Config) -> Count = length(Conn), ok = unregister_many(Conn), sleep(?MEDIUM_PAUSE), - test_server:format("Limit was ~w names, now reg/unreg all 10 times~n", - [Count]), + io:format("Limit was ~w names, now reg/unreg all 10 times~n", [Count]), ok = register_repeat(Count), sleep(?MEDIUM_PAUSE), ok = rregister_repeat(Count), @@ -551,7 +546,7 @@ rregister_repeat(Count) -> % Return count of successful registrations register_many(I, N, _Prefix) when I > N -> - test_server:format("Done with all ~n", []), + io:format("Done with all ~n", []), []; register_many(I, N, Prefix) -> Name = gen_name(Prefix, I), -- cgit v1.2.3 From 4bf79507bc911bf619e906e6ff294128a0c398a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 4 May 2016 18:09:08 +0200 Subject: Eliminate use of doc and suite clauses Those clause are obsolete and never used by common_test. --- erts/epmd/test/epmd_SUITE.erl | 170 +++++++++--------------------------------- 1 file changed, 37 insertions(+), 133 deletions(-) (limited to 'erts') diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 4fb8875cde..0f0a5acde7 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -144,20 +144,14 @@ end_per_testcase(_Func, _Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -register_name(doc) -> - ["Register a name"]; -register_name(suite) -> - []; +%% Register a name register_name(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = register_node("foobar"), ok = close(Sock), % Unregister ok. -register_name_ipv6(doc) -> - ["Register a name over IPv6"]; -register_name_ipv6(suite) -> - []; +%% Register a name over IPv6 register_name_ipv6(Config) when is_list(Config) -> % Test if the host has an IPv6 loopback address Res = gen_tcp:listen(0, [inet6, {ip, {0,0,0,0,0,0,0,1}}]), @@ -172,10 +166,7 @@ register_name_ipv6(Config) when is_list(Config) -> {skip, "Host does not have an IPv6 loopback address"} end. -register_names_1(doc) -> - ["Register and unregister two nodes"]; -register_names_1(suite) -> - []; +%% Register and unregister two nodes register_names_1(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock1} = register_node("foobar"), @@ -184,10 +175,7 @@ register_names_1(Config) when is_list(Config) -> ok = close(Sock2), % Unregister ok. -register_names_2(doc) -> - ["Register and unregister two nodes"]; -register_names_2(suite) -> - []; +%% Register and unregister two nodes register_names_2(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock1} = register_node("foobar"), @@ -196,10 +184,7 @@ register_names_2(Config) when is_list(Config) -> ok = close(Sock1), % Unregister ok. -register_duplicate_name(doc) -> - ["Two nodes with the same name"]; -register_duplicate_name(suite) -> - []; +%% Two nodes with the same name register_duplicate_name(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = register_node("foobar"), @@ -209,10 +194,7 @@ register_duplicate_name(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -unicode_name(doc) -> - ["Check that we can register and lookup a unicode name"]; -unicode_name(suite) -> - []; +%% Check that we can register and lookup a unicode name unicode_name(Config) when is_list(Config) -> ok = epmdrun(), NodeName = [16#1f608], @@ -224,10 +206,7 @@ unicode_name(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -long_unicode_name(doc) -> - ["Check that we can register and lookup a long unicode name"]; -long_unicode_name(suite) -> - []; +%% Check that we can register and lookup a long unicode name long_unicode_name(Config) when is_list(Config) -> ok = epmdrun(), BaseChar = 16#1f600, @@ -298,10 +277,7 @@ parse_port2_resp(Resp) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -name_with_null_inside(doc) -> - ["Register a name with a null char in it"]; -name_with_null_inside(suite) -> - []; +%% Register a name with a null char in it name_with_null_inside(Config) when is_list(Config) -> ok = epmdrun(), error = register_node("foo\000bar"), @@ -309,10 +285,7 @@ name_with_null_inside(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -name_null_terminated(doc) -> - ["Register a name with terminating null byte"]; -name_null_terminated(suite) -> - []; +%% Register a name with terminating null byte name_null_terminated(Config) when is_list(Config) -> ok = epmdrun(), error = register_node("foobar\000"), @@ -320,10 +293,7 @@ name_null_terminated(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -stupid_names_req(doc) -> - ["Read names from epmd in a stupid way"]; -stupid_names_req(suite) -> - []; +%% Read names from epmd in a stupid way stupid_names_req(Config) when is_list(Config) -> ok = epmdrun(), [FirstConn | Conn] = register_many(1, ?REG_REPEAT_LIM, "foo"), @@ -420,17 +390,11 @@ parse_name([], _Name) -> error. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -get_port_nr(doc) -> - ["Register a name on a port and ask about port nr"]; -get_port_nr(suite) -> - []; +%% Register a name on a port and ask about port nr get_port_nr(Config) when is_list(Config) -> port_request([?EPMD_PORT_PLEASE2_REQ,"foo"]). -slow_get_port_nr(doc) -> - ["Register with slow write and ask about port nr"]; -slow_get_port_nr(suite) -> - []; +%% Register with slow write and ask about port nr slow_get_port_nr(Config) when is_list(Config) -> port_request([?EPMD_PORT_PLEASE2_REQ,d,$f,d,$o,d,$o]). @@ -458,10 +422,7 @@ port_request(M) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -unregister_others_name_1(doc) -> - ["Unregister name of other node"]; -unregister_others_name_1(suite) -> - []; +%% Unregister name of other node unregister_others_name_1(Config) when is_list(Config) -> ok = epmdrun("-relaxed_command_check"), {ok,RSock} = register_node("foo"), @@ -475,10 +436,7 @@ unregister_others_name_1(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -unregister_others_name_2(doc) -> - ["Unregister name of other node"]; -unregister_others_name_2(suite) -> - []; +%% Unregister name of other node unregister_others_name_2(Config) when is_list(Config) -> ok = epmdrun("-relaxed_command_check"), {ok,Sock} = connect(), @@ -490,10 +448,7 @@ unregister_others_name_2(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -register_overflow(doc) -> - ["Register too many, clean and redo 10 times"]; -register_overflow(suite) -> - []; +%% Register too many, clean and redo 10 times register_overflow(Config) when is_list(Config) -> ct:timetrap(?LONG_TEST_TIMEOUT), ok = epmdrun(), @@ -575,10 +530,7 @@ gen_name(Str,Int) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -no_data(doc) -> - ["Open but send no data"]; -no_data(suite) -> - []; +%% Open but send no data no_data(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = connect(), @@ -588,10 +540,7 @@ no_data(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -one_byte(doc) -> - ["Send one byte only"]; -one_byte(suite) -> - []; +%% Send one byte only one_byte(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = connect(), @@ -602,10 +551,7 @@ one_byte(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -two_bytes(doc) -> - ["Send packet size only"]; -two_bytes(suite) -> - []; +%% Send packet size only two_bytes(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = connect(), @@ -616,10 +562,7 @@ two_bytes(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -partial_packet(doc) -> - ["Got only part of a packet"]; -partial_packet(suite) -> - []; +%% Got only part of a packet partial_packet(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = connect(), @@ -630,10 +573,7 @@ partial_packet(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -zero_length(doc) -> - ["Invalid zero packet size"]; -zero_length(suite) -> - []; +%% Invalid zero packet size zero_length(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = connect(), @@ -644,10 +584,7 @@ zero_length(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -too_large(doc) -> - ["Invalid large packet"]; -too_large(suite) -> - []; +%% Invalid large packet too_large(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = connect(), @@ -665,10 +602,7 @@ too_large(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -alive_req_too_small_1(doc) -> - ["Try to register but not enough data"]; -alive_req_too_small_1(suite) -> - []; +%% Try to register but not enough data alive_req_too_small_1(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = connect(), @@ -681,10 +615,7 @@ alive_req_too_small_1(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -alive_req_too_small_2(doc) -> - ["Try to register but not enough data"]; -alive_req_too_small_2(suite) -> - []; +%% Try to register but not enough data alive_req_too_small_2(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = connect(), @@ -697,10 +628,7 @@ alive_req_too_small_2(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -alive_req_too_large(doc) -> - ["Try to register but node name too large"]; -alive_req_too_large(suite) -> - []; +%% Try to register but node name too large alive_req_too_large(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = connect(), @@ -726,10 +654,7 @@ alive_req_too_large(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -returns_valid_empty_extra(doc) -> - ["Check that an empty extra is prefixed by a two byte length"]; -returns_valid_empty_extra(suite) -> - []; +%% Check that an empty extra is prefixed by a two byte length returns_valid_empty_extra(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", []), @@ -739,10 +664,7 @@ returns_valid_empty_extra(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -returns_valid_populated_extra_with_nulls(doc) -> - ["Check a populated extra with embedded null characters"]; -returns_valid_populated_extra_with_nulls(suite) -> - []; +%% Check a populated extra with embedded null characters returns_valid_populated_extra_with_nulls(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", "ABC\000\000"), @@ -752,10 +674,7 @@ returns_valid_populated_extra_with_nulls(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -names_stdout(doc) -> - ["Test that epmd -names prints registered nodes to stdout"]; -names_stdout(suite) -> - []; +%% Test that epmd -names prints registered nodes to stdout names_stdout(Config) when is_list(Config) -> ok = epmdrun(), {ok,Sock} = register_node("foobar"), @@ -770,18 +689,13 @@ names_stdout(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -buffer_overrun_1(suite) -> - []; -buffer_overrun_1(doc) -> - ["Test security vulnerability in fake extra lengths in alive2_req"]; +%% Test security vulnerability in fake extra lengths in alive2_req buffer_overrun_1(Config) when is_list(Config) -> ok = epmdrun(), true = alltrue([hostile(N) || N <- lists:seq(1,10000)]), ok. -buffer_overrun_2(suite) -> - []; -buffer_overrun_2(doc) -> - ["Test security vulnerability in fake extra lengths in alive2_req"]; + +%% Test security vulnerability in fake extra lengths in alive2_req buffer_overrun_2(Config) when is_list(Config) -> ok = epmdrun(), [false | Rest] = [hostile2(N) || N <- lists:seq(255*4,10000)], @@ -822,10 +736,8 @@ alltrue([true|T]) -> alltrue([_|_]) -> false. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -no_nonlocal_register(suite) -> - []; -no_nonlocal_register(doc) -> - ["Ensure that we cannot register throug a nonlocal connection"]; + +%% Ensure that we cannot register throug a nonlocal connection no_nonlocal_register(Config) when is_list(Config) -> case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of {SSH,Name} when is_list(Name), is_list(SSH) -> @@ -866,10 +778,7 @@ do_no_nonlocal_register(Config,SSHHost) when is_list(Config) -> true = Res, ok. -no_nonlocal_kill(suite) -> - []; -no_nonlocal_kill(doc) -> - ["Ensure that we cannot kill through nonlocal connection"]; +%% Ensure that we cannot kill through nonlocal connection no_nonlocal_kill(Config) when is_list(Config) -> case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of {SSH,Name} when is_list(Name), is_list(SSH) -> @@ -901,10 +810,8 @@ do_no_nonlocal_kill(Config,SSHHost) when is_list(Config) -> true = Res, ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -no_live_killing(doc) -> - ["Dont allow killing with live nodes or any unregistering w/o -relaxed_command_check"]; -no_live_killing(suite) -> - []; + +%% Dont allow killing with live nodes or any unregistering w/o -relaxed_command_check no_live_killing(Config) when is_list(Config) -> ok = epmdrun(), {ok,RSock} = register_node("foo"), @@ -927,11 +834,8 @@ no_live_killing(Config) when is_list(Config) -> close(Sock3), ok. -socket_reset_before_alive2_reply_is_written(doc) -> - ["Check for regression - don't make zombie from node which " - "sends TCP RST at wrong time"]; -socket_reset_before_alive2_reply_is_written(suite) -> - []; +%% Check for regression - don't make zombie from node which +%% sends TCP RST at wrong time socket_reset_before_alive2_reply_is_written(Config) when is_list(Config) -> %% - delay_write for easier triggering of race condition %% - relaxed_command_check for gracefull shutdown of epmd even if there -- cgit v1.2.3 From 067c234829144dd1c33b9640cd6a0e5faf3d04b3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 4 May 2016 14:23:26 +0200 Subject: erts: Refactor erl_mmap.h by (only) moving around stuff in the file. in order to make it easier to ifdef away all erts_mmap_* if not supported. --- erts/emulator/sys/common/erl_mmap.h | 93 +++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 45 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index b3c45ba116..3790f2c94e 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -26,9 +26,29 @@ #define ERTS_MMAP_SUPERALIGNED_BITS (18) /* Affects hard limits for sbct and lmbcs documented in erts_alloc.xml */ -#define ERTS_MMAPFLG_OS_ONLY (((Uint32) 1) << 0) -#define ERTS_MMAPFLG_SUPERCARRIER_ONLY (((Uint32) 1) << 1) -#define ERTS_MMAPFLG_SUPERALIGNED (((Uint32) 1) << 2) +#ifndef HAVE_MMAP +# define HAVE_MMAP 0 +#endif +#ifndef HAVE_MREMAP +# define HAVE_MREMAP 0 +#endif +#if HAVE_MMAP +# define ERTS_HAVE_OS_MMAP 1 +# define ERTS_HAVE_GENUINE_OS_MMAP 1 +# if HAVE_MREMAP +# define ERTS_HAVE_OS_MREMAP 1 +# endif +# if defined(MAP_FIXED) && defined(MAP_NORESERVE) +# define ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION 1 +# endif +#endif + +#ifndef HAVE_VIRTUALALLOC +# define HAVE_VIRTUALALLOC 0 +#endif +#if HAVE_VIRTUALALLOC +# define ERTS_HAVE_OS_MMAP 1 +#endif extern UWord erts_page_inv_mask; @@ -60,26 +80,6 @@ typedef struct { #define ERTS_MMAP_INIT_HIPE_EXEC_INITER \ {{NULL, NULL}, {NULL, NULL}, ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0} -typedef struct ErtsMemMapper_ ErtsMemMapper; - -void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep); -void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size); -void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep); -int erts_mmap_in_supercarrier(ErtsMemMapper*, void *ptr); -void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*, int executable); -struct erts_mmap_info_struct -{ - UWord sizes[6]; - UWord segs[6]; - UWord os_used; -}; -Eterm erts_mmap_info(ErtsMemMapper*, int *print_to_p, void *print_to_arg, - Eterm** hpp, Uint* szp, struct erts_mmap_info_struct*); -Eterm erts_mmap_info_options(ErtsMemMapper*, - char *prefix, int *print_to_p, void *print_to_arg, - Uint **hpp, Uint *szp); -struct process; -Eterm erts_mmap_debug_info(ErtsMemMapper*, struct process*); #define ERTS_SUPERALIGNED_SIZE \ (1 << ERTS_MMAP_SUPERALIGNED_BITS) @@ -107,29 +107,32 @@ Eterm erts_mmap_debug_info(ErtsMemMapper*, struct process*); #define ERTS_PAGEALIGNED_SIZE \ (ERTS_INV_PAGEALIGNED_MASK + 1) -#ifndef HAVE_MMAP -# define HAVE_MMAP 0 -#endif -#ifndef HAVE_MREMAP -# define HAVE_MREMAP 0 -#endif -#if HAVE_MMAP -# define ERTS_HAVE_OS_MMAP 1 -# define ERTS_HAVE_GENUINE_OS_MMAP 1 -# if HAVE_MREMAP -# define ERTS_HAVE_OS_MREMAP 1 -# endif -# if defined(MAP_FIXED) && defined(MAP_NORESERVE) -# define ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION 1 -# endif -#endif -#ifndef HAVE_VIRTUALALLOC -# define HAVE_VIRTUALALLOC 0 -#endif -#if HAVE_VIRTUALALLOC -# define ERTS_HAVE_OS_MMAP 1 -#endif +typedef struct ErtsMemMapper_ ErtsMemMapper; + +#define ERTS_MMAPFLG_OS_ONLY (((Uint32) 1) << 0) +#define ERTS_MMAPFLG_SUPERCARRIER_ONLY (((Uint32) 1) << 1) +#define ERTS_MMAPFLG_SUPERALIGNED (((Uint32) 1) << 2) + +void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep); +void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size); +void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep); +int erts_mmap_in_supercarrier(ErtsMemMapper*, void *ptr); +void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*, int executable); +struct erts_mmap_info_struct +{ + UWord sizes[6]; + UWord segs[6]; + UWord os_used; +}; +Eterm erts_mmap_info(ErtsMemMapper*, int *print_to_p, void *print_to_arg, + Eterm** hpp, Uint* szp, struct erts_mmap_info_struct*); +Eterm erts_mmap_info_options(ErtsMemMapper*, + char *prefix, int *print_to_p, void *print_to_arg, + Uint **hpp, Uint *szp); +struct process; +Eterm erts_mmap_debug_info(ErtsMemMapper*, struct process*); + #ifdef ERTS_WANT_MEM_MAPPERS # include "erl_alloc_types.h" -- cgit v1.2.3 From dd5087bf582bba0879fa352bdc0ddcbf06d943d2 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 4 May 2016 17:44:07 +0200 Subject: erts: Add macro HAVE_ERTS_MMAP and make erts_mmap unavailable at compile time if not supported. --- erts/emulator/beam/erl_alloc.c | 23 +++++++++++++++-------- erts/emulator/beam/erl_bif_info.c | 2 +- erts/emulator/sys/common/erl_mmap.c | 13 +++++++++---- erts/emulator/sys/common/erl_mmap.h | 15 +++++++++++++-- 4 files changed, 38 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 01db65d539..f571515def 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -2963,10 +2963,11 @@ erts_allocator_options(void *proc) atoms[length] = am_atom_put("alloc_util", 10); terms[length++] = erts_alcu_au_info_options(NULL, NULL, hpp, szp); - +#if HAVE_ERTS_MMAP atoms[length] = ERTS_MAKE_AM("erts_mmap"); terms[length++] = erts_mmap_info_options(&erts_dflt_mmapper, NULL, NULL, NULL, hpp, szp); +#endif { Eterm o[3], v[3]; o[0] = am_atom_put("m", 1); @@ -3093,12 +3094,14 @@ reply_alloc_info(void *vair) Uint sz, *szp; ErlOffHeap *ohp = NULL; ErtsMessage *mp = NULL; +#if HAVE_ERTS_MMAP struct erts_mmap_info_struct mmap_info_dflt; -#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) +# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) struct erts_mmap_info_struct mmap_info_literal; -#endif -#ifdef ERTS_ALC_A_EXEC +# endif +# ifdef ERTS_ALC_A_EXEC struct erts_mmap_info_struct mmap_info_exec; +# endif #endif int i; Eterm (*info_func)(Allctr_t *, @@ -3207,7 +3210,7 @@ reply_alloc_info(void *vair) break; case ERTS_ALC_INFO_A_ERTS_MMAP: alloc_atom = erts_bld_atom(hpp, szp, "erts_mmap"); - +#if HAVE_ERTS_MMAP ainfo = (air->only_sz ? NIL : erts_mmap_info(&erts_dflt_mmapper, NULL, NULL, hpp, szp, &mmap_info_dflt)); @@ -3215,7 +3218,7 @@ reply_alloc_info(void *vair) alloc_atom, erts_bld_atom(hpp,szp,"default_mmap"), ainfo); -#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) +# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) ai_list = erts_bld_cons(hpp, szp, ainfo, ai_list); ainfo = (air->only_sz ? NIL : @@ -3225,8 +3228,8 @@ reply_alloc_info(void *vair) alloc_atom, erts_bld_atom(hpp,szp,"literal_mmap"), ainfo); -#endif -#ifdef ERTS_ALC_A_EXEC +# endif +# ifdef ERTS_ALC_A_EXEC ai_list = erts_bld_cons(hpp, szp, ainfo, ai_list); ainfo = (air->only_sz ? NIL : @@ -3236,6 +3239,10 @@ reply_alloc_info(void *vair) alloc_atom, erts_bld_atom(hpp,szp,"exec_mmap"), ainfo); +# endif +#else /* !HAVE_ERTS_MMAP */ + ainfo = erts_bld_tuple2(hpp, szp, alloc_atom, + am_false); #endif break; case ERTS_ALC_INFO_A_MSEG_ALLOC: diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index c9741b361f..95d1fef3d3 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3554,7 +3554,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) BIF_RET(res); } else if (ERTS_IS_ATOM_STR("mmap", BIF_ARG_1)) { - BIF_RET(erts_mmap_debug_info(&erts_dflt_mmapper, BIF_P)); + BIF_RET(erts_mmap_debug_info(BIF_P)); } else if (ERTS_IS_ATOM_STR("unique_monotonic_integer_state", BIF_ARG_1)) { BIF_RET(erts_debug_get_unique_monotonic_integer_state(BIF_P)); diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index bf675cf1ab..53009a1481 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -28,6 +28,8 @@ #include "erl_mmap.h" #include +#if HAVE_ERTS_MMAP + /* #define ERTS_MMAP_OP_RINGBUF_SZ 100 */ #if defined(DEBUG) || 0 @@ -2513,9 +2515,13 @@ Eterm erts_mmap_info_options(ErtsMemMapper* mm, return res; } +#endif /* HAVE_ERTS_MMAP */ -Eterm erts_mmap_debug_info(ErtsMemMapper* mm, Process* p) +Eterm erts_mmap_debug_info(Process* p) { +#if HAVE_ERTS_MMAP + ErtsMemMapper* mm = &erts_dflt_mmapper; + if (mm->supercarrier) { ERTS_DECL_AM(sabot); ERTS_DECL_AM(satop); @@ -2553,9 +2559,8 @@ Eterm erts_mmap_debug_info(ErtsMemMapper* mm, Process* p) HRelease(p, hp_end, hp); return list; } - else { - return am_undefined; - } +#endif + return am_undefined; } diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 3790f2c94e..7ac61a82c1 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -50,6 +50,13 @@ # define ERTS_HAVE_OS_MMAP 1 #endif +#ifdef ERTS_HAVE_GENUINE_OS_MMAP +# define HAVE_ERTS_MMAP 1 +#else +# define HAVE_ERTS_MMAP 0 +#endif + + extern UWord erts_page_inv_mask; typedef struct { @@ -107,6 +114,10 @@ typedef struct { #define ERTS_PAGEALIGNED_SIZE \ (ERTS_INV_PAGEALIGNED_MASK + 1) +struct process; +Eterm erts_mmap_debug_info(struct process*); + +#if HAVE_ERTS_MMAP typedef struct ErtsMemMapper_ ErtsMemMapper; @@ -130,8 +141,6 @@ Eterm erts_mmap_info(ErtsMemMapper*, int *print_to_p, void *print_to_arg, Eterm erts_mmap_info_options(ErtsMemMapper*, char *prefix, int *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp); -struct process; -Eterm erts_mmap_debug_info(ErtsMemMapper*, struct process*); #ifdef ERTS_WANT_MEM_MAPPERS @@ -157,4 +166,6 @@ void hard_dbg_remove_mseg(void* seg, UWord sz); # define HARD_DBG_REMOVE_MSEG(SEG,SZ) #endif +#endif /* HAVE_ERTS_MMAP */ + #endif /* ERL_MMAP_H__ */ -- cgit v1.2.3 From 03afaae12811632cf975586728826af14a634ad1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 22 Jan 2016 20:12:40 +0100 Subject: erts: Beautify some code with container_of macro --- erts/emulator/beam/erl_bif_trace.c | 11 ++++------- erts/emulator/beam/sys.h | 3 +++ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index ff2018aa27..09c82ad206 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -1307,7 +1307,7 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified, for (i = 0; i < n; i++) { BeamInstr* pc = fp[i].pc; - Export* ep = (Export *)(((char *)(pc-3)) - offsetof(Export, code)); + Export* ep = ErtsContainerStruct(pc, Export, code[3]); if (on && !flags.breakpoint) { /* Turn on global call tracing */ @@ -1556,11 +1556,10 @@ install_exp_breakpoints(BpFunctions* f) BpFunction* fp = f->matching; Uint ne = f->matched; Uint i; - Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr); for (i = 0; i < ne; i++) { BeamInstr* pc = fp[i].pc; - Export* ep = (Export *) (((char *)pc)-offset); + Export* ep = ErtsContainerStruct(pc, Export, code[3]); ep->addressv[code_ix] = pc; } @@ -1573,11 +1572,10 @@ uninstall_exp_breakpoints(BpFunctions* f) BpFunction* fp = f->matching; Uint ne = f->matched; Uint i; - Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr); for (i = 0; i < ne; i++) { BeamInstr* pc = fp[i].pc; - Export* ep = (Export *) (((char *)pc)-offset); + Export* ep = ErtsContainerStruct(pc, Export, code[3]); if (ep->addressv[code_ix] != pc) { continue; @@ -1594,11 +1592,10 @@ clean_export_entries(BpFunctions* f) BpFunction* fp = f->matching; Uint ne = f->matched; Uint i; - Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr); for (i = 0; i < ne; i++) { BeamInstr* pc = fp[i].pc; - Export* ep = (Export *) (((char *)pc)-offset); + Export* ep = ErtsContainerStruct(pc, Export, code[3]); if (ep->addressv[code_ix] == pc) { continue; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 748fba15c7..dda3ba565c 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -92,6 +92,9 @@ #define ErtsInArea(ptr,start,nbytes) \ ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes)) +#define ErtsContainerStruct(ptr, type, member) \ + (type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member)) + #if defined (__WIN32__) # include "erl_win_sys.h" #else -- cgit v1.2.3 From 9791998defa447e4620da250bbddacb3c8edc02e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 22 Jan 2016 20:15:24 +0100 Subject: erts: Rename struct type as is trips up my editor symbol tagging. --- erts/emulator/hipe/hipe_bif0.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 48cfafb8c5..4063cbf306 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -533,13 +533,13 @@ BIF_RETTYPE hipe_bifs_merge_term_1(BIF_ALIST_1) BIF_RET(val); } -struct mfa { +struct mfa_t { Eterm mod; Eterm fun; Uint ari; }; -static int term_to_mfa(Eterm term, struct mfa *mfa) +static int term_to_mfa(Eterm term, struct mfa_t *mfa) { Eterm mod, fun, a; Uint ari; @@ -597,7 +597,7 @@ static Uint *hipe_find_emu_address(Eterm mod, Eterm name, unsigned int arity) Uint *hipe_bifs_find_pc_from_mfa(Eterm term) { - struct mfa mfa; + struct mfa_t mfa; if (!term_to_mfa(term, &mfa)) return NULL; @@ -617,7 +617,7 @@ BIF_RETTYPE hipe_bifs_set_native_address_3(BIF_ALIST_3) Eterm *pc; void *address; int is_closure; - struct mfa mfa; + struct mfa_t mfa; switch (BIF_ARG_3) { case am_false: @@ -1225,7 +1225,7 @@ void hipe_mfa_set_trampoline(Eterm m, Eterm f, unsigned int arity, void *trampol BIF_RETTYPE hipe_bifs_set_funinfo_native_address_3(BIF_ALIST_3) { - struct mfa mfa; + struct mfa_t mfa; void *address; int is_exported; @@ -1247,7 +1247,7 @@ BIF_RETTYPE hipe_bifs_set_funinfo_native_address_3(BIF_ALIST_3) BIF_RETTYPE hipe_bifs_invalidate_funinfo_native_addresses_1(BIF_ALIST_1) { Eterm lst; - struct mfa mfa; + struct mfa_t mfa; struct hipe_mfa_info *p; hipe_mfa_info_table_rwlock(); @@ -1416,7 +1416,7 @@ BIF_RETTYPE hipe_find_na_or_make_stub(BIF_ALIST_3) BIF_RETTYPE hipe_bifs_find_na_or_make_stub_2(BIF_ALIST_2) { - struct mfa mfa; + struct mfa_t mfa; void *address; int is_remote; @@ -1518,9 +1518,9 @@ struct ref { */ BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2) { - struct mfa callee; + struct mfa_t callee; Eterm *tuple; - struct mfa caller; + struct mfa_t caller; void *address; void *trampoline; unsigned int flags; @@ -1597,7 +1597,7 @@ BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2) */ BIF_RETTYPE hipe_bifs_mark_referred_from_1(BIF_ALIST_1) /* get_refs_from */ { - struct mfa mfa; + struct mfa_t mfa; const struct hipe_mfa_info *p; struct ref *ref; @@ -1649,7 +1649,7 @@ static void hipe_purge_all_refs(void) BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1) { - struct mfa mfa; + struct mfa_t mfa; struct hipe_mfa_info *caller_mfa, *callee_mfa; struct hipe_mfa_info_list *refers_to, *tmp_refers_to; struct ref **prev, *ref; @@ -1703,7 +1703,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1) */ BIF_RETTYPE hipe_bifs_redirect_referred_from_1(BIF_ALIST_1) { - struct mfa mfa; + struct mfa_t mfa; struct hipe_mfa_info *p; struct ref **prev, *ref; int is_remote, res; -- cgit v1.2.3 From af2a4c5a40c62da775caa92df5164fbee08b9245 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 2 Feb 2016 17:30:51 +0100 Subject: erts: Rename constants in enum erts_break_op with uppercase for constants and why not call them 'RESTART' and 'PAUSE' as the API. --- erts/emulator/beam/beam_bp.c | 6 +++--- erts/emulator/beam/beam_bp.h | 8 ++++---- erts/emulator/beam/erl_bif_trace.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 2ee98ed7b5..c4b9259d64 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -1432,7 +1432,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags, g = (GenericBp *) pc[-4]; if (g == 0) { int i; - if (count_op == erts_break_reset || count_op == erts_break_stop) { + if (count_op == ERTS_BREAK_RESTART || count_op == ERTS_BREAK_PAUSE) { /* Do not insert a new breakpoint */ return; } @@ -1456,7 +1456,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags, MatchSetUnref(bp->meta_ms); bp_meta_unref(bp->meta_tracer); } else if (common & ERTS_BPF_COUNT) { - if (count_op == erts_break_stop) { + if (count_op == ERTS_BREAK_PAUSE) { bp->flags &= ~ERTS_BPF_COUNT_ACTIVE; } else { bp->flags |= ERTS_BPF_COUNT_ACTIVE; @@ -1468,7 +1468,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags, BpDataTime* bdt = bp->time; Uint i = 0; - if (count_op == erts_break_stop) { + if (count_op == ERTS_BREAK_PAUSE) { bp->flags &= ~ERTS_BPF_TIME_TRACE_ACTIVE; } else { bp->flags |= ERTS_BPF_TIME_TRACE_ACTIVE; diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index bb171be8a6..fc17f95b5d 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -86,10 +86,10 @@ typedef struct generic_bp { #endif enum erts_break_op{ - erts_break_nop = 0, /* Must be false */ - erts_break_set = !0, /* Must be true */ - erts_break_reset, - erts_break_stop + ERTS_BREAK_NOP = 0, /* Must be false */ + ERTS_BREAK_SET = !0, /* Must be true */ + ERTS_BREAK_RESTART, + ERTS_BREAK_PAUSE }; typedef Uint32 ErtsBpIndex; diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 09c82ad206..ef01aa3340 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -137,10 +137,10 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) on = 1; } else if (Pattern == am_restart) { match_prog_set = NULL; - on = erts_break_reset; + on = ERTS_BREAK_RESTART; } else if (Pattern == am_pause) { match_prog_set = NULL; - on = erts_break_stop; + on = ERTS_BREAK_PAUSE; } else if ((match_prog_set = erts_match_set_compile(p, Pattern)) != NULL) { MatchSetRef(match_prog_set); on = 1; -- cgit v1.2.3 From 4a4475dea3ab0daf29f9a49ce845fa062387362a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 3 Mar 2016 19:34:50 +0100 Subject: erts: Add match spec for send trace --- erts/emulator/beam/beam_bp.c | 5 +- erts/emulator/beam/erl_bif_trace.c | 57 +++++++++++++++++ erts/emulator/beam/erl_trace.c | 59 +++++++++++++----- erts/emulator/beam/erl_trace.h | 8 +++ erts/emulator/test/trace_SUITE.erl | 123 ++++++++++++++++++++++++++++++++----- 5 files changed, 220 insertions(+), 32 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index c4b9259d64..1e30e8d8d1 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -248,7 +248,10 @@ erts_bp_match_export(BpFunctions* f, Eterm mfa[3], int specified) void erts_bp_free_matched_functions(BpFunctions* f) { - Free(f->matching); + if (f->matching) { + Free(f->matching); + } + else ASSERT(f->matched == 0); } void diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index ef01aa3340..6b5bb2f889 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -68,6 +68,9 @@ static struct { /* Protected by code write permission */ static Eterm trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist); +static void +erts_set_trace_send_pattern(Process*, Binary*, int on); + #ifdef ERTS_SMP static void smp_bp_finisher(void* arg); #endif @@ -85,14 +88,23 @@ static void install_exp_breakpoints(BpFunctions* f); static void uninstall_exp_breakpoints(BpFunctions* f); static void clean_export_entries(BpFunctions* f); +ErtsTracingEvent erts_send_tracing[ERTS_NUM_BP_IX]; + void erts_bif_trace_init(void) { + int i; + erts_default_trace_pattern_is_on = 0; erts_default_match_spec = NULL; erts_default_meta_match_spec = NULL; erts_default_trace_pattern_flags = erts_trace_pattern_flags_off; erts_default_meta_tracer = erts_tracer_nil; + + for (i=0; i ERTS_BREAK_SET) { + goto error; + } + erts_set_trace_send_pattern(p, match_prog_set, on); + matches = 1; } + error: MatchSetUnref(match_prog_set); UnUseTmpHeap(3,p); @@ -1467,6 +1486,43 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified, return matches; } +void +erts_set_trace_send_pattern(Process*p, Binary* match_spec, int on) +{ + ErtsTracingEvent* st = &erts_send_tracing[erts_staging_bp_ix()]; + + MatchSetUnref(st->match_spec); + + st->on = on; + st->match_spec = match_spec; + MatchSetRef(match_spec); + + finish_bp.current = 1; /* prepare phase not needed for send trace */ + finish_bp.install = on; + finish_bp.e.matched = 0; + finish_bp.e.matching = NULL; + finish_bp.f.matched = 0; + finish_bp.f.matching = NULL; + +#ifndef ERTS_SMP + while (erts_finish_breakpointing()) { + /* Empty loop body */ + } +#endif +} + +static void +consolidate_send_tracing(void) +{ + ErtsTracingEvent* src = &erts_send_tracing[erts_active_bp_ix()]; + ErtsTracingEvent* dst = &erts_send_tracing[erts_staging_bp_ix()]; + + MatchSetUnref(dst->match_spec); + dst->on = src->on; + dst->match_spec = src->match_spec; + MatchSetRef(dst->match_spec); +} + int erts_finish_breakpointing(void) { @@ -1542,6 +1598,7 @@ erts_finish_breakpointing(void) erts_consolidate_bp_data(&finish_bp.f, 1); erts_bp_free_matched_functions(&finish_bp.e); erts_bp_free_matched_functions(&finish_bp.f); + consolidate_send_tracing(); return 0; default: ASSERT(0); diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 56899f574a..e8516cd09c 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -394,7 +394,8 @@ static ERTS_INLINE int send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, Eterm t_p_id, ErtsTracerNif *tnif, enum ErtsTracerOpt topt, - Eterm tag, Eterm msg, Eterm extra); + Eterm tag, Eterm msg, Eterm extra, + Eterm pam_result); static ERTS_INLINE Eterm call_enabled_tracer(Process *c_p, const ErtsTracer tracer, ErtsTracerNif **tnif_ref, @@ -786,7 +787,7 @@ trace_sched_aux(Process *p, ErtsProcLocks locks, Eterm what) } send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SCHED_PROC, - what, tmp, THE_NON_VALUE); + what, tmp, THE_NON_VALUE, am_true); } /* Send {trace_ts, Pid, What, {Mod, Func, Arity}, Timestamp} @@ -811,9 +812,31 @@ trace_send(Process *p, Eterm to, Eterm msg) { Eterm operation = am_send; ErtsTracerNif *tnif = NULL; + ErtsTracingEvent* te; + Eterm pam_result; ASSERT(ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND)); + te = &erts_send_tracing[erts_active_bp_ix()]; + if (!te->on) { + return; + } + if (te->match_spec) { + Eterm args[2]; + Uint32 return_flags; + args[0] = to; + args[1] = msg; + pam_result = erts_match_set_run(p, te->match_spec, args, 2, + ERTS_PAM_TMP_RESULT, &return_flags); + if (is_non_value(pam_result) + || pam_result == am_false + || (ERTS_TRACE_FLAGS(p) & F_TRACE_SILENT)) { + erts_match_set_release_result(p); + return; + } + } else + pam_result = am_true; + if (is_internal_pid(to)) { if (!erts_proc_lookup(to)) goto send_to_non_existing_process; @@ -825,9 +848,11 @@ trace_send(Process *p, Eterm to, Eterm msg) } if (is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, - TRACE_FUN_E_SEND, operation)) + TRACE_FUN_E_SEND, operation)) { send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SEND, - operation, msg, to); + operation, msg, to, pam_result); + } + erts_match_set_release_result(p); } /* Send {trace_ts, Pid, receive, Msg, Timestamp} @@ -841,7 +866,7 @@ trace_receive(Process *c_p, Eterm msg) TRACE_FUN_E_RECEIVE, am_receive)) send_to_tracer_nif(NULL, &c_p->common, c_p->common.id, tnif, TRACE_FUN_T_RECEIVE, - am_receive, msg, THE_NON_VALUE); + am_receive, msg, THE_NON_VALUE, am_true); } int @@ -963,7 +988,7 @@ erts_trace_return_to(Process *p, BeamInstr *pc) } send_to_tracer_nif(p, &p->common, p->common.id, NULL, TRACE_FUN_T_CALL, - am_return_to, mfa, THE_NON_VALUE); + am_return_to, mfa, THE_NON_VALUE, am_true); } @@ -1289,7 +1314,7 @@ trace_proc(Process *c_p, ErtsProcLocks c_p_locks, if (is_tracer_enabled(c_p, c_p_locks, &t_p->common, &tnif, TRACE_FUN_E_PROCS, what)) send_to_tracer_nif(c_p, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PROCS, - what, data, THE_NON_VALUE); + what, data, THE_NON_VALUE, am_true); } @@ -1315,7 +1340,7 @@ trace_proc_spawn(Process *p, Eterm what, Eterm pid, mfa = TUPLE3(hp, mod, func, args); hp += 4; send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_PROCS, - what, pid, mfa); + what, pid, mfa, am_true); } } @@ -1365,7 +1390,7 @@ trace_gc(Process *p, Eterm what, Uint size) msg = CONS(hp, tup, msg); hp += 2; send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_GC, - what, msg, am_undefined); + what, msg, am_undefined, am_true); } } @@ -1780,7 +1805,7 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) { ERTS_SMP_CHK_NO_PROC_LOCKS; if (is_tracer_enabled(NULL, 0, &p->common, &tnif, TRACE_FUN_E_PORTS, am_open)) send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, TRACE_FUN_T_PORTS, - am_open, calling_pid, drv_name); + am_open, calling_pid, drv_name, am_true); } /* Sends trace message: @@ -1799,7 +1824,7 @@ trace_port(Port *t_p, Eterm what, Eterm data) { ERTS_SMP_CHK_NO_PROC_LOCKS; if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_PORTS, what)) send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PORTS, - what, data, THE_NON_VALUE); + what, data, THE_NON_VALUE, am_true); } @@ -1936,7 +1961,7 @@ trace_port_receive(Port *t_p, Eterm caller, Eterm what, ...) data = TUPLE2(hp, caller, data); send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_RECEIVE, - am_receive, data, THE_NON_VALUE); + am_receive, data, THE_NON_VALUE, am_true); if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0) erts_bin_free(bptr); @@ -1959,7 +1984,7 @@ trace_port_send(Port *t_p, Eterm receiver, Eterm msg, int exists) ERTS_SMP_CHK_NO_PROC_LOCKS; if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SEND, op)) send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_SEND, - op, msg, receiver); + op, msg, receiver, am_true); } void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz) @@ -1988,7 +2013,7 @@ void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz) hp += 3; send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_SEND, - am_send, msg, to); + am_send, msg, to, am_true); if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0) erts_bin_free(bptr); @@ -2019,7 +2044,7 @@ trace_sched_ports_where(Port *t_p, Eterm what, Eterm where) { if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SCHED_PORT, what)) send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_SCHED_PORT, - what, where, THE_NON_VALUE); + what, where, THE_NON_VALUE, am_true); } /* Port profiling */ @@ -2842,7 +2867,7 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee, static ERTS_INLINE int send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, Eterm t_p_id, ErtsTracerNif *tnif, enum ErtsTracerOpt topt, - Eterm tag, Eterm msg, Eterm extra) + Eterm tag, Eterm msg, Eterm extra, Eterm pam_result) { #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) if (c_p) { @@ -2862,7 +2887,7 @@ send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, is_internal_pid(t_p->id) ? (Process*)t_p : NULL, t_p->tracer, t_p->trace_flags, t_p_id, tnif, topt, tag, msg, extra, - am_true); + pam_result); } static ERTS_INLINE Eterm diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index 9a007e62ec..e8e968c0cf 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -220,4 +220,12 @@ ERTS_DECLARE_DUMMY(erts_tracer_nil) = NIL; && erts_is_tracer_proc_enabled(PROC, ERTS_PROC_LOCK_MAIN, \ &(PROC)->common, am_trace_status)) +typedef struct +{ + int on; + struct binary* match_spec; +} ErtsTracingEvent; + +extern ErtsTracingEvent erts_send_tracing[]; + #endif /* ERL_TRACE_H__ */ diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index b7f312635e..c8cc0fc6e8 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -255,30 +255,111 @@ send_trace(Config) when is_list(Config) -> %% Check that a message sent to another process is traced. 1 = erlang:trace(Sender, true, [send]), + F1 = fun (Pat) -> + set_trace_pattern(send, Pat, []), + Sender ! {send_please, Receiver, to_receiver}, + {trace, Sender, send, to_receiver, Receiver} = receive_first_trace(), + receive_nothing() + end, + lists:foreach(F1, [no, + [{[Receiver,to_receiver],[],[]}], + [{['_','_'],[],[]}], + [{['$1','_'],[{is_pid,'$1'}],[]}], + [{['_','$1'],[{is_atom,'$1'}],[]}], + true]), + + %% Test {message, Msg} + F1m = fun ({Pat, Msg}) -> + set_trace_pattern(send, Pat, []), + Sender ! {send_please, Receiver, to_receiver}, + {trace, Sender, send, to_receiver, Receiver, Msg} = receive_first_trace(), + receive_nothing() + end, + lists:foreach(F1m, [{[{['_','_'],[],[{message, 4711}]}], 4711}, + {[{['_','_'],[],[{message, "4711"}]}], "4711"} + ]), + + %% Test {message, {process_dump}} + set_trace_pattern(send, [{['_','_'],[],[{message, {process_dump}}]}], []), Sender ! {send_please, Receiver, to_receiver}, - {trace, Sender, send, to_receiver, Receiver} = receive_first_trace(), + {trace, Sender, send, to_receiver, Receiver, ProcDump} = receive_first_trace(), + true = is_binary(ProcDump), receive_nothing(), + %% Same test with false match spec + F2 = fun (Pat) -> + set_trace_pattern(send, Pat, []), + Sender ! {send_please, Receiver, to_receiver}, + receive_nothing() + end, + lists:foreach(F2, [[{[Sender,to_receiver],[],[]}], + [{[Receiver,nomatch],[],[]}], + [{['$1','_'],[{is_atom,'$1'}],[]}], + [{['_','$1'],[{is_pid,'$1'}],[]}], + false, + [{['_','_'],[],[{message,false}]}], + [{['_','_'],[],[{silent,true}]}]]), + erlang:trace_pattern(send, true, []), + erlang:trace(Sender, false, [silent]), + %% Check that a message sent to another registered process is traced. register(?MODULE,Receiver), - Sender ! {send_please, ?MODULE, to_receiver}, - {trace, Sender, send, to_receiver, ?MODULE} = receive_first_trace(), - receive_nothing(), + F3 = fun (Pat) -> + set_trace_pattern(send, Pat, []), + Sender ! {send_please, ?MODULE, to_receiver}, + {trace, Sender, send, to_receiver, ?MODULE} = receive_first_trace(), + receive_nothing() + end, + lists:foreach(F3, [no, + [{[?MODULE,to_receiver],[],[]}], + [{['_','_'],[],[]}], + [{['$1','_'],[{is_atom,'$1'}],[]}], + [{['_','$1'],[{is_atom,'$1'}],[]}], + true]), + %% Again with false match spec + F4 = fun (Pat) -> + set_trace_pattern(send, Pat, []), + Sender ! {send_please, ?MODULE, to_receiver}, + receive_nothing() + end, + lists:foreach(F4, [[{[nomatch,to_receiver],[],[]}], + [{[?MODULE,nomatch],[],[]}], + [{['$1','_'],[{is_pid,'$1'}],[]}], + [{['_','$1'],[{is_pid,'$1'}],[]}], + [{['_','_'],[],[{message,false}]}], + [{['_','_'],[],[{silent,true}]}] + ]), unregister(?MODULE), + erlang:trace_pattern(send, true, []), + erlang:trace(Sender, false, [silent]), %% Check that a message sent to this process is traced. - Sender ! {send_please, self(), to_myself}, - receive to_myself -> ok end, - Self = self(), - {trace, Sender, send, to_myself, Self} = receive_first_trace(), - receive_nothing(), + F5 = fun (Pat) -> + set_trace_pattern(send, Pat, []), + Sender ! {send_please, self(), to_myself}, + receive to_myself -> ok end, + Self = self(), + {trace, Sender, send, to_myself, Self} = receive_first_trace(), + receive_nothing() + end, + lists:foreach(F5, [no, + [{[self(),to_myself],[],[]}], + [{['_','_'],[],[]}], + true]), %% Check that a message sent to dead process is traced. {Pid,Ref} = spawn_monitor(fun() -> ok end), receive {'DOWN',Ref,_,_,_} -> ok end, - Sender ! {send_please, Pid, to_dead}, - {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first_trace(), - receive_nothing(), + F6 = fun (Pat) -> + set_trace_pattern(send, Pat, []), + Sender ! {send_please, Pid, to_dead}, + {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first_trace(), + receive_nothing() + end, + lists:foreach(F6, [no, + [{[Pid,to_dead],[],[]}], + [{['_','_'],[],[]}], + true]), %% Check that a message sent to unknown registrated process is traced. BadargSender = fun_spawn(fun sender/0), @@ -299,8 +380,22 @@ send_trace(Config) when is_list(Config) -> Sender ! {send_please, self(), to_myself_again}, receive to_myself_again -> ok end, receive_nothing(), + + {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [global])), + {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [local])), + {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [meta])), + {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [{meta,self()}])), + {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [call_count])), + {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [call_time])), + {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, restart, [])), + {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, pause, [])), + + %% Done. ok. +set_trace_pattern(_, no, _) -> 0; +set_trace_pattern(MSA, Pat, Flg) -> erlang:trace_pattern(MSA, Pat, Flg). + %% Test trace(Pid, How, [procs]). procs_trace(Config) when is_list(Config) -> Name = list_to_atom(atom_to_list(?MODULE)++"_procs_trace"), @@ -1441,8 +1536,8 @@ receive_nothing() -> receive Any -> ct:fail({unexpected_message, Any}) - after 200 -> - ok + after 100 -> + ok end. -- cgit v1.2.3 From 3261d580be2263b7b4b70ec3c7670a5de963e91c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 4 Mar 2016 19:25:38 +0100 Subject: erts: Remove multi scheduler blocking in match specs for enable_trace and disable_trace operations. Instead seize needed locks while updating trace flags. --- erts/emulator/beam/erl_db_util.c | 51 +++++++++++++--------------------------- 1 file changed, 16 insertions(+), 35 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 76b96637ae..a1f458038e 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1758,7 +1758,6 @@ Eterm db_prog_match(Process *c_p, Binary *bprog, Eterm (*bif)(Process*, ...); Eterm bif_args[3]; int fail_label; - int atomic_trace; #ifdef DMC_DEBUG Uint *heap_fence; Uint *stack_fence; @@ -1776,28 +1775,6 @@ Eterm db_prog_match(Process *c_p, Binary *bprog, current_scheduled = esdp->current_process; /* SMP: psp->scheduler_data is set by get_match_pseudo_process */ - atomic_trace = 0; -#define BEGIN_ATOMIC_TRACE(p) \ - do { \ - if (! atomic_trace) { \ - erts_refc_inc(&bprog->refc, 2); \ - erts_smp_proc_unlock((p), ERTS_PROC_LOCK_MAIN); \ - erts_smp_thr_progress_block(); \ - atomic_trace = !0; \ - } \ - } while (0) -#define END_ATOMIC_TRACE(p) \ - do { \ - if (atomic_trace) { \ - erts_smp_thr_progress_unblock(); \ - erts_smp_proc_lock((p), ERTS_PROC_LOCK_MAIN); \ - if (erts_refc_dectest(&bprog->refc, 0) == 0) {\ - erts_bin_free(bprog); \ - } \ - atomic_trace = 0; \ - } \ - } while (0) - #ifdef DMC_DEBUG save_op = 0; heap_fence = (Eterm*)((char*) mpsp->u.heap + prog->stack_offset) - 1; @@ -2334,8 +2311,9 @@ restart: break; case matchEnableTrace: if ( (n = erts_trace_flag2bit(esp[-1]))) { - BEGIN_ATOMIC_TRACE(c_p); + erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); set_tracee_flags(c_p, ERTS_TRACER(c_p), 0, n); + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); esp[-1] = am_true; } else { esp[-1] = FAIL_TERM; @@ -2345,18 +2323,22 @@ restart: n = erts_trace_flag2bit((--esp)[-1]); esp[-1] = FAIL_TERM; if (n) { - BEGIN_ATOMIC_TRACE(c_p); - if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) { + if ( (tmpp = get_proc(c_p, ERTS_PROC_LOCK_MAIN, esp[0], ERTS_PROC_LOCKS_ALL))) { /* Always take over the tracer of the current process */ set_tracee_flags(tmpp, ERTS_TRACER(c_p), 0, n); - esp[-1] = am_true; + if (tmpp == c_p) + erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL_MINOR); + else + erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL); + esp[-1] = am_true; } } break; case matchDisableTrace: if ( (n = erts_trace_flag2bit(esp[-1]))) { - BEGIN_ATOMIC_TRACE(c_p); + erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); set_tracee_flags(c_p, ERTS_TRACER(c_p), n, 0); + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); esp[-1] = am_true; } else { esp[-1] = FAIL_TERM; @@ -2366,11 +2348,14 @@ restart: n = erts_trace_flag2bit((--esp)[-1]); esp[-1] = FAIL_TERM; if (n) { - BEGIN_ATOMIC_TRACE(c_p); - if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) { + if ( (tmpp = get_proc(c_p, ERTS_PROC_LOCK_MAIN, esp[0], ERTS_PROC_LOCKS_ALL))) { /* Always take over the tracer of the current process */ set_tracee_flags(tmpp, ERTS_TRACER(c_p), n, 0); - esp[-1] = am_true; + if (tmpp == c_p) + erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL_MINOR); + else + erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL); + esp[-1] = am_true; } } break; @@ -2508,13 +2493,9 @@ success: esdp->current_process = current_scheduled; - END_ATOMIC_TRACE(c_p); - return ret; #undef FAIL #undef FAIL_TERM -#undef BEGIN_ATOMIC_TRACE -#undef END_ATOMIC_TRACE } -- cgit v1.2.3 From 8b2906d9974decf9e8bab24a8f753ba81a025410 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 15 Mar 2016 19:53:42 +0100 Subject: erts: Add matchspec to 'receive' trace --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/erl_bif_trace.c | 40 ++++++++++++++--------- erts/emulator/beam/erl_db.c | 3 +- erts/emulator/beam/erl_db_util.c | 62 ++++++++++++++++++++++++------------ erts/emulator/beam/erl_db_util.h | 3 +- erts/emulator/beam/erl_message.c | 37 +++++++++++---------- erts/emulator/beam/erl_trace.c | 48 ++++++++++++++++++++++------ erts/emulator/beam/erl_trace.h | 19 +++++------ erts/emulator/beam/global.h | 4 ++- erts/emulator/hipe/hipe_native_bif.c | 2 +- erts/emulator/test/trace_SUITE.erl | 20 ++++++++++++ 11 files changed, 166 insertions(+), 74 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 72526bca5e..7eecd059a5 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2166,7 +2166,7 @@ void process_main(void) PreFetch(0, next); if (IS_TRACED_FL(c_p, F_TRACE_RECEIVE)) { - trace_receive(c_p, am_timeout); + trace_receive(c_p, c_p, am_timeout, NULL); } if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) { save_calls(c_p, &exp_timeout); diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 6b5bb2f889..7b21ff0110 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -68,8 +68,8 @@ static struct { /* Protected by code write permission */ static Eterm trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist); -static void -erts_set_trace_send_pattern(Process*, Binary*, int on); +static int +erts_set_tracing_event_pattern(Eterm event, Binary*, int on); #ifdef ERTS_SMP static void smp_bp_finisher(void* arg); @@ -89,6 +89,7 @@ static void uninstall_exp_breakpoints(BpFunctions* f); static void clean_export_entries(BpFunctions* f); ErtsTracingEvent erts_send_tracing[ERTS_NUM_BP_IX]; +ErtsTracingEvent erts_receive_tracing[ERTS_NUM_BP_IX]; void erts_bif_trace_init(void) @@ -104,6 +105,8 @@ erts_bif_trace_init(void) for (i=0; i ERTS_BREAK_SET) { goto error; } - erts_set_trace_send_pattern(p, match_prog_set, on); - matches = 1; + matches = erts_set_tracing_event_pattern(MFA, match_prog_set, on); } - error: MatchSetUnref(match_prog_set); UnUseTmpHeap(3,p); @@ -1486,10 +1487,17 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified, return matches; } -void -erts_set_trace_send_pattern(Process*p, Binary* match_spec, int on) +int +erts_set_tracing_event_pattern(Eterm event, Binary* match_spec, int on) { - ErtsTracingEvent* st = &erts_send_tracing[erts_staging_bp_ix()]; + ErtsBpIndex ix = erts_staging_bp_ix(); + ErtsTracingEvent* st; + + switch (event) { + case am_send: st = &erts_send_tracing[ix]; break; + case am_receive: st = &erts_receive_tracing[ix]; break; + default: return -1; + } MatchSetUnref(st->match_spec); @@ -1497,7 +1505,7 @@ erts_set_trace_send_pattern(Process*p, Binary* match_spec, int on) st->match_spec = match_spec; MatchSetRef(match_spec); - finish_bp.current = 1; /* prepare phase not needed for send trace */ + finish_bp.current = 1; /* prepare phase not needed for event trace */ finish_bp.install = on; finish_bp.e.matched = 0; finish_bp.e.matching = NULL; @@ -1509,13 +1517,14 @@ erts_set_trace_send_pattern(Process*p, Binary* match_spec, int on) /* Empty loop body */ } #endif + return 1; } static void -consolidate_send_tracing(void) +consolidate_event_tracing(ErtsTracingEvent te[]) { - ErtsTracingEvent* src = &erts_send_tracing[erts_active_bp_ix()]; - ErtsTracingEvent* dst = &erts_send_tracing[erts_staging_bp_ix()]; + ErtsTracingEvent* src = &te[erts_active_bp_ix()]; + ErtsTracingEvent* dst = &te[erts_staging_bp_ix()]; MatchSetUnref(dst->match_spec); dst->on = src->on; @@ -1529,7 +1538,7 @@ erts_finish_breakpointing(void) ERTS_SMP_LC_ASSERT(erts_has_code_write_permission()); /* - * Memory barriers will be issued for all processes *before* + * Memory barriers will be issued for all schedulers *before* * each of the stages below. (Unless the other schedulers * are blocked, in which case memory barriers will be issued * when they are awaken.) @@ -1598,7 +1607,8 @@ erts_finish_breakpointing(void) erts_consolidate_bp_data(&finish_bp.f, 1); erts_bp_free_matched_functions(&finish_bp.e); erts_bp_free_matched_functions(&finish_bp.f); - consolidate_send_tracing(); + consolidate_event_tracing(erts_send_tracing); + consolidate_event_tracing(erts_receive_tracing); return 0; default: ASSERT(0); diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 615d23402b..acaca54e9a 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -2833,7 +2833,8 @@ BIF_RETTYPE ets_match_spec_run_r_3(BIF_ALIST_3) BIF_TRAP3(bif_export[BIF_ets_match_spec_run_r_3], BIF_P,lst,BIF_ARG_2,ret); } - res = db_prog_match(BIF_P, mp, CAR(list_val(lst)), NULL, 0, + res = db_prog_match(BIF_P, BIF_P, + mp, CAR(list_val(lst)), NULL, 0, ERTS_PAM_COPY_RESULT, &dummy); if (is_value(res)) { hp = HAlloc(BIF_P, 2); diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index a1f458038e..72d59df813 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1202,14 +1202,16 @@ done: return ret; } -Eterm erts_match_set_run(Process *p, Binary *mpsp, +Eterm erts_match_set_run(Process *c_p, + Process *self, + Binary *mpsp, Eterm *args, int num_args, enum erts_pam_run_flags in_flags, Uint32 *return_flags) { Eterm ret; - ret = db_prog_match(p, mpsp, NIL, args, num_args, + ret = db_prog_match(c_p, self, mpsp, NIL, args, num_args, in_flags, return_flags); #if defined(HARDDEBUG) if (is_non_value(ret)) { @@ -1234,7 +1236,8 @@ static Eterm erts_match_set_run_ets(Process *p, Binary *mpsp, { Eterm ret; - ret = db_prog_match(p, mpsp, args, NULL, num_args, + ret = db_prog_match(p, p, + mpsp, args, NULL, num_args, ERTS_PAM_COPY_RESULT, return_flags); #if defined(HARDDEBUG) @@ -1730,7 +1733,9 @@ static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity) ** the parameter 'arity' is only used if 'term' is actually an array, ** i.e. 'DCOMP_TRACE' was specified */ -Eterm db_prog_match(Process *c_p, Binary *bprog, +Eterm db_prog_match(Process *c_p, + Process *self, + Binary *bprog, Eterm term, Eterm *termp, int arity, @@ -1746,7 +1751,7 @@ Eterm db_prog_match(Process *c_p, Binary *bprog, UWord *pc = prog->text; Eterm *ehp; Eterm ret; - Uint n = 0; /* To avoid warning. */ + Uint n; int i; unsigned do_catch; ErtsMatchPseudoProcess *mpsp; @@ -1764,6 +1769,8 @@ Eterm db_prog_match(Process *c_p, Binary *bprog, Uint save_op; #endif /* DMC_DEBUG */ + ERTS_UNDEF(n,0); + mpsp = get_match_pseudo_process(c_p, prog->heap_size); psp = &mpsp->process; @@ -2233,7 +2240,7 @@ restart: pc += n; break; case matchSelf: - *esp++ = c_p->common.id; + *esp++ = self->common.id; break; case matchWaste: --esp; @@ -2243,6 +2250,7 @@ restart: break; case matchProcessDump: { erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0); + ASSERT(c_p == self); print_process_info(ERTS_PRINT_DSBUF, (void *) dsbufp, c_p); *esp++ = new_binary(build_proc, (byte *)dsbufp->str, dsbufp->str_len); @@ -2261,14 +2269,16 @@ restart: *return_flags |= MATCH_SET_EXCEPTION_TRACE; *esp++ = am_true; break; - case matchIsSeqTrace: + case matchIsSeqTrace: + ASSERT(c_p == self); if (have_seqtrace(SEQ_TRACE_TOKEN(c_p))) *esp++ = am_true; else *esp++ = am_false; break; case matchSetSeqToken: - t = erts_seq_trace(c_p, esp[-1], esp[-2], 0); + ASSERT(c_p == self); + t = erts_seq_trace(c_p, esp[-1], esp[-2], 0); if (is_non_value(t)) { esp[-2] = FAIL_TERM; } else { @@ -2276,7 +2286,8 @@ restart: } --esp; break; - case matchSetSeqTokenFake: + case matchSetSeqTokenFake: + ASSERT(c_p == self); t = seq_trace_fake(c_p, esp[-1]); if (is_non_value(t)) { esp[-2] = FAIL_TERM; @@ -2285,7 +2296,8 @@ restart: } --esp; break; - case matchGetSeqToken: + case matchGetSeqToken: + ASSERT(c_p == self); if (have_no_seqtrace(SEQ_TRACE_TOKEN(c_p))) *esp++ = NIL; else { @@ -2309,7 +2321,8 @@ restart: ASSERT(is_immed(ehp[5])); } break; - case matchEnableTrace: + case matchEnableTrace: + ASSERT(c_p == self); if ( (n = erts_trace_flag2bit(esp[-1]))) { erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); set_tracee_flags(c_p, ERTS_TRACER(c_p), 0, n); @@ -2319,7 +2332,8 @@ restart: esp[-1] = FAIL_TERM; } break; - case matchEnableTrace2: + case matchEnableTrace2: + ASSERT(c_p == self); n = erts_trace_flag2bit((--esp)[-1]); esp[-1] = FAIL_TERM; if (n) { @@ -2334,7 +2348,8 @@ restart: } } break; - case matchDisableTrace: + case matchDisableTrace: + ASSERT(c_p == self); if ( (n = erts_trace_flag2bit(esp[-1]))) { erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); set_tracee_flags(c_p, ERTS_TRACER(c_p), n, 0); @@ -2344,7 +2359,8 @@ restart: esp[-1] = FAIL_TERM; } break; - case matchDisableTrace2: + case matchDisableTrace2: + ASSERT(c_p == self); n = erts_trace_flag2bit((--esp)[-1]); esp[-1] = FAIL_TERM; if (n) { @@ -2359,7 +2375,8 @@ restart: } } break; - case matchCaller: + case matchCaller: + ASSERT(c_p == self); if (!(c_p->cp) || !(cp = find_function_from_pc(c_p->cp))) { *esp++ = am_undefined; } else { @@ -2371,7 +2388,8 @@ restart: ehp[3] = make_small((Uint) cp[2]); } break; - case matchSilent: + case matchSilent: + ASSERT(c_p == self); --esp; if (in_flags & ERTS_PAM_IGNORE_TRACE_SILENT) break; @@ -2386,7 +2404,8 @@ restart: erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); } break; - case matchTrace2: + case matchTrace2: + ASSERT(c_p == self); { /* disable enable */ Uint d_flags = 0, e_flags = 0; /* process trace flags */ @@ -2418,7 +2437,8 @@ restart: ERTS_TRACER_CLEAR(&tracer); } break; - case matchTrace3: + case matchTrace3: + ASSERT(c_p == self); { /* disable enable */ Uint d_flags = 0, e_flags = 0; /* process trace flags */ @@ -5080,7 +5100,8 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace) } save_cp = p->cp; p->cp = NULL; - res = erts_match_set_run(p, mps, arr, n, + res = erts_match_set_run(p, p, + mps, arr, n, ERTS_PAM_COPY_RESULT|ERTS_PAM_IGNORE_TRACE_SILENT, &ret_flags); p->cp = save_cp; @@ -5163,7 +5184,8 @@ Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog, obj = db_alloc_tmp_uncompressed(tb, obj); } - res = db_prog_match(c_p, bprog, make_tuple(obj->tpl), NULL, 0, + res = db_prog_match(c_p, c_p, + bprog, make_tuple(obj->tpl), NULL, 0, ERTS_PAM_COPY_RESULT|ERTS_PAM_CONTIGUOUS_TUPLE, &dummy); if (is_value(res) && hpp!=NULL) { diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 5d7946a525..c3eb82a44a 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -435,7 +435,8 @@ Binary *db_match_compile(Eterm *matchexpr, Eterm *guards, Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog, int all, DbTerm* obj, Eterm** hpp, Uint extra); -Eterm db_prog_match(Process *p, Binary *prog, Eterm term, +Eterm db_prog_match(Process *p, Process *self, + Binary *prog, Eterm term, Eterm *termp, int arity, enum erts_pam_run_flags in_flags, Uint32 *return_flags /* Zeroed on enter */); diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 9beff52835..ee8a7702ce 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -32,6 +32,7 @@ #include "erl_process.h" #include "erl_binary.h" #include "dtrace-wrapper.h" +#include "beam_bp.h" ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message_ref, ErtsMessageRef, @@ -369,11 +370,12 @@ static Sint queue_messages(Process *c_p, Process* receiver, erts_aint32_t *receiver_state, - ErtsProcLocks *receiver_locks, + ErtsProcLocks receiver_locks, ErtsMessage* first, ErtsMessage** last, Uint len) { + ErtsTracingEvent* te; Sint res; int locked_msgq = 0; erts_aint32_t state; @@ -386,12 +388,12 @@ queue_messages(Process *c_p, #ifdef ERTS_SMP #ifdef ERTS_ENABLE_LOCK_CHECK ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(receiver) < ERTS_PROC_LOCK_MSGQ || - *receiver_locks == erts_proc_lc_my_proc_locks(receiver)); + receiver_locks == erts_proc_lc_my_proc_locks(receiver)); #endif - if (!(*receiver_locks & ERTS_PROC_LOCK_MSGQ)) { + if (!(receiver_locks & ERTS_PROC_LOCK_MSGQ)) { if (erts_smp_proc_trylock(receiver, ERTS_PROC_LOCK_MSGQ) == EBUSY) { - ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ; + ErtsProcLocks need_locks; if (receiver_state) state = *receiver_state; @@ -400,10 +402,11 @@ queue_messages(Process *c_p, if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) goto exiting; - if (*receiver_locks & ERTS_PROC_LOCK_STATUS) { - erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_STATUS); - need_locks |= ERTS_PROC_LOCK_STATUS; + need_locks = receiver_locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + if (need_locks) { + erts_smp_proc_unlock(receiver, need_locks); } + need_locks |= ERTS_PROC_LOCK_MSGQ; erts_smp_proc_lock(receiver, need_locks); } locked_msgq = 1; @@ -426,7 +429,7 @@ queue_messages(Process *c_p, res = receiver->msg.len; #ifdef ERTS_SMP - if (*receiver_locks & ERTS_PROC_LOCK_MAIN) { + if (receiver_locks & ERTS_PROC_LOCK_MAIN) { /* * We move 'in queue' to 'private queue' and place * message at the end of 'private queue' in order @@ -445,7 +448,10 @@ queue_messages(Process *c_p, LINK_MESSAGE(receiver, first, last, len); } - if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) { + if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE) + && (te = &erts_receive_tracing[erts_active_bp_ix()], + te->on)) { + ErtsMessage *msg = first; #ifdef USE_VM_PROBES @@ -468,19 +474,18 @@ queue_messages(Process *c_p, tok_label, tok_lastcnt, tok_serial); } #endif - while (msg) { - trace_receive(receiver, ERL_MESSAGE_TERM(msg)); + trace_receive(c_p, receiver, ERL_MESSAGE_TERM(msg), te); msg = msg->next; } } - - if (locked_msgq) + if (locked_msgq) { erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ); + } #ifdef ERTS_SMP - erts_proc_notify_new_message(receiver, *receiver_locks); + erts_proc_notify_new_message(receiver, receiver_locks); #else erts_proc_notify_new_message(receiver, 0); ERTS_HOLE_CHECK(receiver); @@ -496,7 +501,7 @@ queue_message(Process *c_p, ErtsMessage* mp, Eterm msg) { ERL_MESSAGE_TERM(mp) = msg; - return queue_messages(c_p, receiver, receiver_state, receiver_locks, + return queue_messages(c_p, receiver, receiver_state, *receiver_locks, mp, &mp->next, 1 ); } @@ -512,7 +517,7 @@ Sint erts_queue_messages(Process* receiver, ErtsProcLocks *receiver_locks, ErtsMessage* first, ErtsMessage** last, Uint len) { - return queue_messages(NULL, receiver, NULL, receiver_locks, + return queue_messages(NULL, receiver, NULL, *receiver_locks, first, last, len); } diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index e8516cd09c..755deda9c1 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -238,7 +238,6 @@ write_timestamp(ErtsTraceTimeStamp *tsp, Eterm **hpp) } #ifdef ERTS_SMP -#define PATCH_TS_SIZE(p) patch_ts_size(TFLGS_TS_TYPE(p)) static ERTS_INLINE Uint patch_ts_size(int ts_type) @@ -258,7 +257,7 @@ patch_ts_size(int ts_type) return 0; } } -#endif +#endif /* ERTS_SMP */ /* * Write a timestamp. The timestamp MUST be the last @@ -826,7 +825,8 @@ trace_send(Process *p, Eterm to, Eterm msg) Uint32 return_flags; args[0] = to; args[1] = msg; - pam_result = erts_match_set_run(p, te->match_spec, args, 2, + pam_result = erts_match_set_run(p, p, + te->match_spec, args, 2, ERTS_PAM_TMP_RESULT, &return_flags); if (is_non_value(pam_result) || pam_result == am_false @@ -859,14 +859,43 @@ trace_send(Process *p, Eterm to, Eterm msg) * or {trace, Pid, receive, Msg} */ void -trace_receive(Process *c_p, Eterm msg) +trace_receive(Process *c_p, + Process* receiver, + Eterm msg, ErtsTracingEvent* te) { ErtsTracerNif *tnif = NULL; - if (is_tracer_enabled(NULL, 0, &c_p->common, &tnif, - TRACE_FUN_E_RECEIVE, am_receive)) - send_to_tracer_nif(NULL, &c_p->common, c_p->common.id, + Eterm pam_result; + + if (!te) { + te = &erts_receive_tracing[erts_active_bp_ix()]; + if (!te->on) + return; + } + + if (te->match_spec) { + Eterm args[2]; + Uint32 return_flags; + args[0] = am_undefined; /* ToDo: from who? */ + args[1] = msg; + pam_result = erts_match_set_run(c_p, receiver, + te->match_spec, args, 2, + ERTS_PAM_TMP_RESULT, &return_flags); + if (is_non_value(pam_result) + || pam_result == am_false + || (ERTS_TRACE_FLAGS(receiver) & F_TRACE_SILENT)) { + erts_match_set_release_result(c_p); + return; + } + } else + pam_result = am_true; + + if (is_tracer_enabled(NULL, 0, &receiver->common, &tnif, + TRACE_FUN_E_RECEIVE, am_receive)) { + send_to_tracer_nif(NULL, &receiver->common, receiver->common.id, tnif, TRACE_FUN_T_RECEIVE, - am_receive, msg, THE_NON_VALUE, am_true); + am_receive, msg, THE_NON_VALUE, pam_result); + } + erts_match_set_release_result(c_p); } int @@ -1226,7 +1255,8 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, may remove it, and we still want to generate a trace message */ erts_tracer_update(&pre_ms_tracer, *tracer); tracer = &pre_ms_tracer; - pam_result = erts_match_set_run(p, match_spec, args, arity, + pam_result = erts_match_set_run(p, p, + match_spec, args, arity, ERTS_PAM_TMP_RESULT, &return_flags); if (is_non_value(pam_result)) { erts_match_set_release_result(p); diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index e8e968c0cf..040aa296a1 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -56,6 +56,15 @@ struct binary; +typedef struct +{ + int on; + struct binary* match_spec; +} ErtsTracingEvent; + +extern ErtsTracingEvent erts_send_tracing[]; +extern ErtsTracingEvent erts_receive_tracing[]; + /* erl_bif_trace.c */ Eterm erl_seq_trace_info(Process *p, Eterm arg1); void erts_system_monitor_clear(Process *c_p); @@ -91,7 +100,7 @@ void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *); #endif void trace_send(Process*, Eterm, Eterm); -void trace_receive(Process *, Eterm); +void trace_receive(Process*, Process*, Eterm, ErtsTracingEvent*); Uint32 erts_call_trace(Process *p, BeamInstr mfa[], struct binary *match_spec, Eterm* args, int local, ErtsTracer *tracer); void erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, @@ -220,12 +229,4 @@ ERTS_DECLARE_DUMMY(erts_tracer_nil) = NIL; && erts_is_tracer_proc_enabled(PROC, ERTS_PROC_LOCK_MAIN, \ &(PROC)->common, am_trace_status)) -typedef struct -{ - int on; - struct binary* match_spec; -} ErtsTracingEvent; - -extern ErtsTracingEvent erts_send_tracing[]; - #endif /* ERL_TRACE_H__ */ diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index a7bc990deb..916c6277c1 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1493,7 +1493,9 @@ enum erts_pam_run_flags { ERTS_PAM_CONTIGUOUS_TUPLE=4, ERTS_PAM_IGNORE_TRACE_SILENT=8 }; -extern Eterm erts_match_set_run(Process *p, Binary *mpsp, +extern Eterm erts_match_set_run(Process *p, + Process *self, + Binary *mpsp, Eterm *args, int num_args, enum erts_pam_run_flags in_flags, Uint32 *return_flags); diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index 00b572029a..d8076a3e02 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -601,7 +601,7 @@ void hipe_clear_timeout(Process *c_p) } #endif if (IS_TRACED_FL(c_p, F_TRACE_RECEIVE)) { - trace_receive(c_p, am_timeout); + trace_receive(c_p, c_p, am_timeout, NULL); } c_p->flags &= ~F_TIMO; JOIN_MESSAGE(c_p); diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index c8cc0fc6e8..7eb52dcdeb 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -94,6 +94,26 @@ receive_trace(Config) when is_list(Config) -> {trace, Receiver, 'receive', Hello2} = receive_first_trace(), receive_nothing(), + %% Test 'receive' with matchspec + F1 = fun ({Pat, IsMatching}) -> + set_trace_pattern('receive', Pat, []), + Receiver ! Hello, + case IsMatching of + true -> + {trace, Receiver, 'receive', Hello} = receive_first_trace(); + false -> + ok + end, + receive_nothing() + end, + lists:foreach(F1, [{no, true}, + {[{[undefined,"Unexpected"],[],[]}], false}, + {[{['_','_'],[],[]}], true}, + {[{['$1','_'],[{is_integer,'$1'}],[]}], false}, + {[{['_','$1'],[{is_tuple,'$1'}],[]}], true}, + {false, false}, + {true, true}]), + %% Another process should not be able to trace Receiver. Intruder = fun_spawn(fun() -> erlang:trace(Receiver, true, ['receive']) end), {'EXIT', Intruder, {badarg, _}} = receive_first(), -- cgit v1.2.3 From 5cb62b003d082c5a32ef5b12a89d872a317fd05f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 17 Mar 2016 16:35:08 +0100 Subject: erts: Add matchspec restrictions for 'receive' trace and non-call-trace. This is the easy way out to avoid difficult locking scenarios when accessing tracing flags on another process. --- erts/emulator/beam/erl_bif_trace.c | 13 ++- erts/emulator/beam/erl_db_util.c | 223 ++++++++++++++++--------------------- erts/emulator/beam/erl_db_util.h | 5 + erts/emulator/beam/global.h | 2 +- erts/emulator/test/trace_SUITE.erl | 25 ++++- 5 files changed, 131 insertions(+), 137 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 7b21ff0110..c604053caa 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -156,11 +156,14 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) } else if (Pattern == am_pause) { match_prog_set = NULL; on = ERTS_BREAK_PAUSE; - } else if ((match_prog_set = erts_match_set_compile(p, Pattern)) != NULL) { - MatchSetRef(match_prog_set); - on = 1; - } else{ - goto error; + } else { + match_prog_set = erts_match_set_compile(p, Pattern, MFA); + if (match_prog_set) { + MatchSetRef(match_prog_set); + on = 1; + } else{ + goto error; + } } is_global = 0; diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 72d59df813..ae2cc40bfa 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -123,6 +123,9 @@ do { \ #define TermWords(t) (((t) / (sizeof(UWord)/sizeof(Eterm))) + !!((t) % (sizeof(UWord)/sizeof(Eterm)))) +#define add_dmc_err(EINFO, STR, VAR, TERM, SEV) \ + vadd_dmc_err(EINFO, SEV, VAR, STR, TERM) + static ERTS_INLINE Process * get_proc(Process *cp, Uint32 cp_locks, Eterm id, Uint32 id_locks) @@ -889,11 +892,7 @@ void db_match_dis(Binary *prog); #define TRACE /* Nothing */ #define FENCE_PATTERN_SIZE 0 #endif -static void add_dmc_err(DMCErrInfo *err_info, - char *str, - int variable, - Eterm term, - DMCErrorSeverity severity); +static void vadd_dmc_err(DMCErrInfo*, DMCErrorSeverity, int var, const char *str, ...); static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity); @@ -989,12 +988,16 @@ Eterm erts_match_set_get_source(Binary *mpsp) } /* This one is for the tracing */ -Binary *erts_match_set_compile(Process *p, Eterm matchexpr) { +Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA) { Binary *bin; Uint sz; Eterm *hp; + Uint flags = DCOMP_TRACE; + + if (is_tuple(MFA)) flags |= DCOMP_CALL_TRACE; + if (MFA != am_receive) flags |= DCOMP_ALLOW_TRACE_OPS; - bin = db_match_set_compile(p, matchexpr, DCOMP_TRACE); + bin = db_match_set_compile(p, matchexpr, flags); if (bin != NULL) { MatchProg *prog = Binary2MatchProg(bin); sz = size_object(matchexpr); @@ -1124,8 +1127,8 @@ Eterm db_match_set_lint(Process *p, Eterm matchexpr, Uint flags) int i; if (!is_list(matchexpr)) { - add_dmc_err(err_info, "Match programs are not in a list.", - -1, 0UL, dmcError); + add_dmc_err(err_info, "Match programs are not in a list.", + -1, 0UL, dmcError); goto done; } num_heads = 0; @@ -1133,9 +1136,8 @@ Eterm db_match_set_lint(Process *p, Eterm matchexpr, Uint flags) ++num_heads; if (l != NIL) { /* proper list... */ - add_dmc_err(err_info, "Match programs are not in a proper " - "list.", - -1, 0UL, dmcError); + add_dmc_err(err_info, "Match programs are not in a proper list.", + -1, 0UL, dmcError); goto done; } @@ -3260,20 +3262,20 @@ int erts_db_is_compiled_ms(Eterm term) ** Utility to add an error */ -static void add_dmc_err(DMCErrInfo *err_info, - char *str, - int variable, - Eterm term, - DMCErrorSeverity severity) +static void vadd_dmc_err(DMCErrInfo *err_info, + DMCErrorSeverity severity, + int variable, + const char *str, + ...) { + DMCError *e; + va_list args; + va_start(args, str); + + /* Linked in in reverse order, to ease the formatting */ - DMCError *e = erts_alloc(ERTS_ALC_T_DB_DMC_ERROR, sizeof(DMCError)); - if (term != 0UL) { - erts_snprintf(e->error_string, DMC_ERR_STR_LEN, str, term); - } else { - strncpy(e->error_string, str, DMC_ERR_STR_LEN); - e->error_string[DMC_ERR_STR_LEN] ='\0'; - } + e = erts_alloc(ERTS_ALC_T_DB_DMC_ERROR, sizeof(DMCError)); + erts_vsnprintf(e->error_string, DMC_ERR_STR_LEN, str, args); e->variable = variable; e->severity = severity; e->next = err_info->first; @@ -3283,8 +3285,11 @@ static void add_dmc_err(DMCErrInfo *err_info, err_info->first = e; if (severity >= dmcError) err_info->error_added = 1; + + va_end(args); } + /* ** Handle one term in the match expression (not the guard) */ @@ -3483,24 +3488,21 @@ static void do_emit_constant(DMCContext *context, DMC_STACK_TYPE(UWord) *text, context->stack_need = context->stack_used; } -#define RETURN_ERROR_X(String, X, Y, ContextP, ConstantF) \ -do { \ -if ((ContextP)->err_info != NULL) { \ - (ConstantF) = 0; \ - add_dmc_err((ContextP)->err_info, String, X, Y, dmcError); \ - return retOk; \ -} else \ - return retFail; \ -} while(0) +#define RETURN_ERROR_X(VAR, ContextP, ConstantF, String, ARG) \ + (((ContextP)->err_info != NULL) \ + ? ((ConstantF) = 0, \ + vadd_dmc_err((ContextP)->err_info, dmcError, VAR, String, ARG), \ + retOk) \ + : retFail) #define RETURN_ERROR(String, ContextP, ConstantF) \ - RETURN_ERROR_X(String, -1, 0UL, ContextP, ConstantF) + return RETURN_ERROR_X(-1, ContextP, ConstantF, String, 0) #define RETURN_VAR_ERROR(String, N, ContextP, ConstantF) \ - RETURN_ERROR_X(String, N, 0UL, ContextP, ConstantF) + return RETURN_ERROR_X(N, ContextP, ConstantF, String, 0) #define RETURN_TERM_ERROR(String, T, ContextP, ConstantF) \ - RETURN_ERROR_X(String, -1, T, ContextP, ConstantF) + return RETURN_ERROR_X(-1, ContextP, ConstantF, String, T) #define WARNING(String, ContextP) \ add_dmc_err((ContextP)->err_info, String, -1, 0UL, dmcWarning) @@ -3766,7 +3768,7 @@ static DMCRet dmc_variable(DMCContext *context, Uint n = db_is_variable(t); if (n >= heap->vars_used || !heap->vars[n].is_bound) { - RETURN_VAR_ERROR("Variable $%d is unbound.", n, context, *constant); + RETURN_VAR_ERROR("Variable $%%d is unbound.", n, context, *constant); } dmc_add_pushv_variant(context, heap, text, n); @@ -4098,7 +4100,30 @@ static DMCRet dmc_exception_trace(DMCContext *context, return retOk; } - +static int check_trace(const char* op, + DMCContext *context, + int *constant, + int need_cflags, + int allow_in_guard, + DMCRet* retp) +{ + if (!(context->cflags & DCOMP_TRACE)) { + *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' " + "used in wrong dialect.", op); + return 0; + } + if ((context->cflags & need_cflags) != need_cflags) { + *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' " + "not allow for this trace event.", op); + return 0; + } + if (context->is_guard && !allow_in_guard) { + *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' " + "called in guard context.", op); + return 0; + } + return 1; +} static DMCRet dmc_is_seq_trace(DMCContext *context, DMCHeap *heap, @@ -4108,12 +4133,11 @@ static DMCRet dmc_is_seq_trace(DMCContext *context, { Eterm *p = tuple_val(t); Uint a = arityval(*p); + DMCRet ret; - if (!(context->cflags & DCOMP_TRACE)) { - RETURN_ERROR("Special form 'is_seq_trace' used in wrong dialect.", - context, - *constant); - } + if (!check_trace("is_seq_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 1, &ret)) + return ret; + if (a != 1) { RETURN_TERM_ERROR("Special form 'is_seq_trace' called with " "arguments in %T.", t, context, *constant); @@ -4137,16 +4161,8 @@ static DMCRet dmc_set_seq_token(DMCContext *context, DMCRet ret; int c; - - if (!(context->cflags & DCOMP_TRACE)) { - RETURN_ERROR("Special form 'set_seq_token' used in wrong dialect.", - context, - *constant); - } - if (context->is_guard) { - RETURN_ERROR("Special form 'set_seq_token' called in " - "guard context.", context, *constant); - } + if (!check_trace("set_seq_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret)) + return ret; if (a != 3) { RETURN_TERM_ERROR("Special form 'set_seq_token' called with wrong " @@ -4183,16 +4199,11 @@ static DMCRet dmc_get_seq_token(DMCContext *context, { Eterm *p = tuple_val(t); Uint a = arityval(*p); + DMCRet ret; + + if (!check_trace("get_seq_token", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret)) + return ret; - if (!(context->cflags & DCOMP_TRACE)) { - RETURN_ERROR("Special form 'get_seq_token' used in wrong dialect.", - context, - *constant); - } - if (context->is_guard) { - RETURN_ERROR("Special form 'get_seq_token' called in " - "guard context.", context, *constant); - } if (a != 1) { RETURN_TERM_ERROR("Special form 'get_seq_token' called with " "arguments in %T.", t, context, @@ -4256,16 +4267,10 @@ static DMCRet dmc_process_dump(DMCContext *context, { Eterm *p = tuple_val(t); Uint a = arityval(*p); - - if (!(context->cflags & DCOMP_TRACE)) { - RETURN_ERROR("Special form 'process_dump' used in wrong dialect.", - context, - *constant); - } - if (context->is_guard) { - RETURN_ERROR("Special form 'process_dump' called in " - "guard context.", context, *constant); - } + DMCRet ret; + + if (!check_trace("process_dump", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret)) + return ret; if (a != 1) { RETURN_TERM_ERROR("Special form 'process_dump' called with " @@ -4289,17 +4294,8 @@ static DMCRet dmc_enable_trace(DMCContext *context, DMCRet ret; int c; - - if (!(context->cflags & DCOMP_TRACE)) { - RETURN_ERROR("Special form 'enable_trace' used in wrong dialect.", - context, - *constant); - } - if (context->is_guard) { - RETURN_ERROR("Special form 'enable_trace' called in guard context.", - context, - *constant); - } + if (!check_trace("enable_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret)) + return ret; switch (a) { case 2: @@ -4348,18 +4344,9 @@ static DMCRet dmc_disable_trace(DMCContext *context, Uint a = arityval(*p); DMCRet ret; int c; - - if (!(context->cflags & DCOMP_TRACE)) { - RETURN_ERROR("Special form 'disable_trace' used in wrong dialect.", - context, - *constant); - } - if (context->is_guard) { - RETURN_ERROR("Special form 'disable_trace' called in guard context.", - context, - *constant); - } + if (!check_trace("disable_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret)) + return ret; switch (a) { case 2: @@ -4409,17 +4396,8 @@ static DMCRet dmc_trace(DMCContext *context, DMCRet ret; int c; - - if (!(context->cflags & DCOMP_TRACE)) { - RETURN_ERROR("Special form 'trace' used in wrong dialect.", - context, - *constant); - } - if (context->is_guard) { - RETURN_ERROR("Special form 'trace' called in guard context.", - context, - *constant); - } + if (!check_trace("trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret)) + return ret; switch (a) { case 3: @@ -4480,16 +4458,11 @@ static DMCRet dmc_caller(DMCContext *context, { Eterm *p = tuple_val(t); Uint a = arityval(*p); + DMCRet ret; - if (!(context->cflags & DCOMP_TRACE)) { - RETURN_ERROR("Special form 'caller' used in wrong dialect.", - context, - *constant); - } - if (context->is_guard) { - RETURN_ERROR("Special form 'caller' called in " - "guard context.", context, *constant); - } + if (!check_trace("caller", context, constant, + (DCOMP_CALL_TRACE|DCOMP_ALLOW_TRACE_OPS), 0, &ret)) + return ret; if (a != 1) { RETURN_TERM_ERROR("Special form 'caller' called with " @@ -4515,15 +4488,8 @@ static DMCRet dmc_silent(DMCContext *context, DMCRet ret; int c; - if (!(context->cflags & DCOMP_TRACE)) { - RETURN_ERROR("Special form 'silent' used in wrong dialect.", - context, - *constant); - } - if (context->is_guard) { - RETURN_ERROR("Special form 'silent' called in " - "guard context.", context, *constant); - } + if (!check_trace("silent", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret)) + return ret; if (a != 2) { RETURN_TERM_ERROR("Special form 'silent' called with wrong " @@ -5063,11 +5029,14 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace) return THE_NON_VALUE; } if (trace) { - lint_res = db_match_set_lint(p, spec, DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE); - mps = db_match_set_compile(p, spec, DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE); + const Uint cflags = (DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE | + DCOMP_CALL_TRACE | DCOMP_ALLOW_TRACE_OPS); + lint_res = db_match_set_lint(p, spec, cflags); + mps = db_match_set_compile(p, spec, cflags); } else { - lint_res = db_match_set_lint(p, spec, DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE); - mps = db_match_set_compile(p, spec, DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE); + const Uint cflags = (DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE); + lint_res = db_match_set_lint(p, spec, cflags); + mps = db_match_set_compile(p, spec, cflags); } if (mps == NULL) { diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index c3eb82a44a..60f7067d70 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -425,6 +425,11 @@ typedef struct dmc_err_info { #define DCOMP_FAKE_DESTRUCTIVE ((Uint) 8) /* When this is active, no setting of trace control words or seq_trace tokens will be done. */ +/* Allow lock seizing operations on the tracee and 3rd party processes */ +#define DCOMP_ALLOW_TRACE_OPS ((Uint) 0x10) + +/* This is call trace */ +#define DCOMP_CALL_TRACE ((Uint) 0x20) Binary *db_match_compile(Eterm *matchexpr, Eterm *guards, Eterm *body, int num_matches, diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 916c6277c1..328bfe344c 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1483,7 +1483,7 @@ do { \ #define MatchSetGetSource(MPSP) erts_match_set_get_source(MPSP) -extern Binary *erts_match_set_compile(Process *p, Eterm matchexpr); +extern Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA); Eterm erts_match_set_lint(Process *p, Eterm matchexpr); extern void erts_match_set_release_result(Process* p); diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 7eb52dcdeb..de1d292d27 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -119,10 +119,26 @@ receive_trace(Config) when is_list(Config) -> {'EXIT', Intruder, {badarg, _}} = receive_first(), %% Untrace the process; we should not receive anything. - 1 = erlang:trace(Receiver, false, ['receive']), - Receiver ! {hello, there}, - Receiver ! any_garbage, - receive_nothing(), + ?line 1 = erlang:trace(Receiver, false, ['receive']), + ?line Receiver ! {hello, there}, + ?line Receiver ! any_garbage, + ?line receive_nothing(), + + %% Verify restrictions in matchspec for 'receive' + F2 = fun (Pat) -> {'EXIT', {badarg,_}} = (catch erlang:trace_pattern('receive', Pat, [])) end, + lists:foreach(F2, [[{['_','_'],[],[{message, {process_dump}}]}], + [{['_','_'],[{is_seq_trace}],[]}], + [{['_','_'],[],[{set_seq_token,label,4711}]}], + [{['_','_'],[],[{get_seq_token}]}], + [{['_','_'],[],[{enable_trace,call}]}], + [{['_','_'],[],[{enable_trace,self(),call}]}], + [{['_','_'],[],[{disable_trace,call}]}], + [{['_','_'],[],[{disable_trace,self(),call}]}], + [{['_','_'],[],[{trace,[call],[]}]}], + [{['_','_'],[],[{trace,self(),[],[call]}]}], + [{['_','_'],[],[{caller}]}], + [{['_','_'],[],[{silent,true}]}]]), + ok. %% Tests that receive of a message always happens before a call with @@ -409,6 +425,7 @@ send_trace(Config) when is_list(Config) -> {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [call_time])), {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, restart, [])), {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, pause, [])), + {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, [{['_','_'],[],[{caller}]}], [])), %% Done. ok. -- cgit v1.2.3 From 9627711cc39fd311a573a172e6d0e8a6b55dbd17 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 1 Apr 2016 15:38:16 +0200 Subject: erts: Add Sender in 'receive' trace matchspec All 'EXIT' and monitor messages are sent from 'system' Timeouts are "sent" from 'clock_service' --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/bif.c | 2 +- erts/emulator/beam/dist.c | 8 +-- erts/emulator/beam/erl_alloc.c | 2 +- erts/emulator/beam/erl_bif_ddll.c | 2 +- erts/emulator/beam/erl_bif_trace.c | 2 +- erts/emulator/beam/erl_db_util.c | 14 ++++-- erts/emulator/beam/erl_gc.c | 2 +- erts/emulator/beam/erl_hl_timer.c | 8 +-- erts/emulator/beam/erl_message.c | 72 +++++++++++++------------- erts/emulator/beam/erl_message.h | 8 +-- erts/emulator/beam/erl_msacc.c | 2 +- erts/emulator/beam/erl_nif.c | 27 +++++----- erts/emulator/beam/erl_process.c | 10 ++-- erts/emulator/beam/erl_time_sup.c | 2 +- erts/emulator/beam/erl_trace.c | 26 +++++----- erts/emulator/beam/erl_trace.h | 2 +- erts/emulator/beam/io.c | 74 +++++++++++++++------------ erts/emulator/beam/utils.c | 2 +- erts/emulator/hipe/hipe_native_bif.c | 2 +- erts/emulator/test/trace_SUITE.erl | 97 +++++++++++++++++++++++++++++------- 21 files changed, 224 insertions(+), 142 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 7eecd059a5..aab0baebea 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2166,7 +2166,7 @@ void process_main(void) PreFetch(0, next); if (IS_TRACED_FL(c_p, F_TRACE_RECEIVE)) { - trace_receive(c_p, c_p, am_timeout, NULL); + trace_receive(c_p, am_clock_service, am_timeout, NULL); } if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) { save_calls(c_p, &exp_timeout); diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index ed5b2983dd..ac77fa96e1 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -611,7 +611,7 @@ erts_queue_monitor_message(Process *p, ref_copy = copy_struct(ref, ref_size, &hp, ohp); tup = TUPLE5(hp, am_DOWN, ref_copy, type, item_copy, reason_copy); - erts_queue_message(p, p_locksp, msgp, tup); + erts_queue_message(p, *p_locksp, msgp, tup, am_system); } static BIF_RETTYPE diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 52fd57e101..b020f16805 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -397,7 +397,7 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp) msgp = erts_alloc_message_heap(rp, &rp_locks, 3, &hp, &ohp); tup = TUPLE2(hp, am_nodedown, name); - erts_queue_message(rp, &rp_locks, msgp, tup); + erts_queue_message(rp, rp_locks, msgp, tup, am_system); } erts_smp_proc_unlock(rp, rp_locks); } @@ -1456,7 +1456,7 @@ int erts_net_message(Port *prt, token = copy_struct(token, token_size, &hp, ohp); } - erts_queue_dist_message(rp, &locks, ede_copy, token); + erts_queue_dist_message(rp, locks, ede_copy, token, from); if (locks) erts_smp_proc_unlock(rp, locks); } @@ -1505,7 +1505,7 @@ int erts_net_message(Port *prt, token = copy_struct(token, token_size, &hp, ohp); } - erts_queue_dist_message(rp, &locks, ede_copy, token); + erts_queue_dist_message(rp, locks, ede_copy, token, tuple[2]); if (locks) erts_smp_proc_unlock(rp, locks); } @@ -3317,7 +3317,7 @@ send_nodes_mon_msg(Process *rp, } ASSERT(hend == hp); - erts_queue_message(rp, rp_locksp, mp, msg); + erts_queue_message(rp, *rp_locksp, mp, msg, am_system); } static void diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index d04977b9ae..3dc12bac51 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3286,7 +3286,7 @@ reply_alloc_info(void *vair) if (hp != hp_end) erts_shrink_message_heap(&mp, rp, hp_start, hp, hp_end, &msg, 1); - erts_queue_message(rp, &rp_locks, mp, msg); + erts_queue_message(rp, rp_locks, mp, msg, am_system); if (air->req_sched == sched_id) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index 1e2db38442..ef77201544 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -1737,7 +1737,7 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type, hp += REF_THING_SIZE; mess = TUPLE5(hp,type,r,am_driver,driver_name,tag); } - erts_queue_message(proc, &rp_locks, mp, mess); + erts_queue_message(proc, rp_locks, mp, mess, am_system); erts_smp_proc_unlock(proc, rp_locks); ERTS_SMP_CHK_NO_PROC_LOCKS; } diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index c604053caa..ffc0afda58 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -2344,7 +2344,7 @@ reply_trace_delivered_all(void *vtdarp) #ifdef ERTS_SMP erts_send_sys_msg_proc(rp->common.id, rp->common.id, msg, bp); #else - erts_queue_message(rp, &rp_locks, mp, msg); + erts_queue_message(rp, rp_locks, mp, msg, am_system); #endif erts_free(ERTS_ALC_T_MISC_AUX_WORK, vtdarp); diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index ae2cc40bfa..7d64529250 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -414,17 +414,19 @@ get_match_pseudo_process(Process *c_p, Uint heap_size) { ErtsMatchPseudoProcess *mpsp; #ifdef ERTS_SMP - mpsp = (ErtsMatchPseudoProcess *) c_p->scheduler_data->match_pseudo_process; + ErtsSchedulerData *esdp = c_p ? c_p->scheduler_data : erts_get_scheduler_data(); + + mpsp = (ErtsMatchPseudoProcess *) esdp->match_pseudo_process; if (mpsp) cleanup_match_pseudo_process(mpsp, 0); else { ASSERT(erts_smp_tsd_get(match_pseudo_process_key) == NULL); mpsp = create_match_pseudo_process(); - c_p->scheduler_data->match_pseudo_process = (void *) mpsp; + esdp->match_pseudo_process = (void *) mpsp; erts_smp_tsd_set(match_pseudo_process_key, (void *) mpsp); } ASSERT(mpsp == erts_smp_tsd_get(match_pseudo_process_key)); - mpsp->process.scheduler_data = c_p->scheduler_data; + mpsp->process.scheduler_data = esdp; #else mpsp = match_pseudo_process; cleanup_match_pseudo_process(mpsp, 0); @@ -1750,7 +1752,7 @@ Eterm db_prog_match(Process *c_p, Eterm *esp; MatchVariable* variables; BeamInstr *cp; - UWord *pc = prog->text; + const UWord *pc = prog->text; Eterm *ehp; Eterm ret; Uint n; @@ -1773,13 +1775,15 @@ Eterm db_prog_match(Process *c_p, ERTS_UNDEF(n,0); + ASSERT(c_p || !(in_flags & ERTS_PAM_COPY_RESULT)); + mpsp = get_match_pseudo_process(c_p, prog->heap_size); psp = &mpsp->process; /* We need to lure the scheduler into believing in the pseudo process, because of floating point exceptions. Do *after* mpsp is set!!! */ - esdp = ERTS_GET_SCHEDULER_DATA_FROM_PROC(c_p); + esdp = ERTS_GET_SCHEDULER_DATA_FROM_PROC(psp); ASSERT(esdp != NULL); current_scheduled = esdp->current_process; /* SMP: psp->scheduler_data is set by get_match_pseudo_process */ diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index df5d0f4918..85a1467a52 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2924,7 +2924,7 @@ reply_gc_info(void *vgcirp) hpp = &hp; } - erts_queue_message(rp, &rp_locks, mp, msg); + erts_queue_message(rp, rp_locks, mp, msg, am_system); if (gcirp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 8e201d5711..c418762578 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -1247,8 +1247,8 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs) if (!ERTS_PROC_IS_EXITING(proc)) { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = tmr->btm.bp; - erts_queue_message(proc, &proc_locks, mp, - tmr->btm.message); + erts_queue_message(proc, proc_locks, mp, + tmr->btm.message, am_clock_service); erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_SEND); queued_message = 1; proc_locks &= ~ERTS_PROC_LOCKS_MSG_SEND; @@ -1980,7 +1980,7 @@ access_sched_local_btm(Process *c_p, Eterm pid, ERTS_HLT_ASSERT(hp + (async ? 4 : 3) == hp_end); - erts_queue_message(proc, &proc_locks, mp, msg); + erts_queue_message(proc, proc_locks, mp, msg, am_clock_service); if (c_p) proc_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -2111,7 +2111,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp, msg = TUPLE3(hp, tag, tref, res); - erts_queue_message(c_p, &proc_locks, mp, msg); + erts_queue_message(c_p, proc_locks, mp, msg, am_clock_service); proc_locks &= ~ERTS_PROC_LOCK_MAIN; if (proc_locks) diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index ee8a7702ce..9bb6e40a11 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -252,9 +252,10 @@ erts_realloc_shrink_message(ErtsMessage *mp, Uint sz, Eterm *brefs, Uint brefs_s void erts_queue_dist_message(Process *rcvr, - ErtsProcLocks *rcvr_locks, + ErtsProcLocks rcvr_locks, ErtsDistExternal *dist_ext, - Eterm token) + Eterm token, + Eterm from) { ErtsMessage* mp; #ifdef USE_VM_PROBES @@ -266,7 +267,7 @@ erts_queue_dist_message(Process *rcvr, erts_aint_t state; #endif - ERTS_SMP_LC_ASSERT(*rcvr_locks == erts_proc_lc_my_proc_locks(rcvr)); + ERTS_SMP_LC_ASSERT(rcvr_locks == erts_proc_lc_my_proc_locks(rcvr)); mp = erts_alloc_message(0, NULL); mp->data.dist_ext = dist_ext; @@ -281,10 +282,10 @@ erts_queue_dist_message(Process *rcvr, ERL_MESSAGE_TOKEN(mp) = token; #ifdef ERTS_SMP - if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) { + if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ)) { if (erts_smp_proc_trylock(rcvr, ERTS_PROC_LOCK_MSGQ) == EBUSY) { ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ; - if (*rcvr_locks & ERTS_PROC_LOCK_STATUS) { + if (rcvr_locks & ERTS_PROC_LOCK_STATUS) { erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_STATUS); need_locks |= ERTS_PROC_LOCK_STATUS; } @@ -294,7 +295,7 @@ erts_queue_dist_message(Process *rcvr, state = erts_smp_atomic32_read_acqb(&rcvr->state); if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) { - if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) + if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ)) erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ); /* Drop message if receiver is exiting or has a pending exit ... */ erts_cleanup_messages(mp); @@ -302,10 +303,13 @@ erts_queue_dist_message(Process *rcvr, else #endif if (IS_TRACED_FL(rcvr, F_TRACE_RECEIVE)) { + if (from == am_Empty) + from = dist_ext->dep->sysname; + /* Ahh... need to decode it in order to trace it... */ - if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) + if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ)) erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ); - if (!erts_decode_dist_message(rcvr, *rcvr_locks, mp, 0)) + if (!erts_decode_dist_message(rcvr, rcvr_locks, mp, 0)) erts_free_message(mp); else { Eterm msg = ERL_MESSAGE_TERM(mp); @@ -325,7 +329,7 @@ erts_queue_dist_message(Process *rcvr, tok_label, tok_lastcnt, tok_serial); } #endif - erts_queue_message(rcvr, rcvr_locks, mp, msg); + erts_queue_message(rcvr, rcvr_locks, mp, msg, from); } } else { @@ -352,12 +356,12 @@ erts_queue_dist_message(Process *rcvr, LINK_MESSAGE(rcvr, mp, &mp->next, 1); - if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) + if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ)) erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ); erts_proc_notify_new_message(rcvr, #ifdef ERTS_SMP - *rcvr_locks + rcvr_locks #else 0 #endif @@ -367,13 +371,13 @@ erts_queue_dist_message(Process *rcvr, /* Add messages last in message queue */ static Sint -queue_messages(Process *c_p, - Process* receiver, +queue_messages(Process* receiver, erts_aint32_t *receiver_state, ErtsProcLocks receiver_locks, ErtsMessage* first, ErtsMessage** last, - Uint len) + Uint len, + Eterm from) { ErtsTracingEvent* te; Sint res; @@ -475,7 +479,7 @@ queue_messages(Process *c_p, } #endif while (msg) { - trace_receive(c_p, receiver, ERL_MESSAGE_TERM(msg), te); + trace_receive(receiver, from, ERL_MESSAGE_TERM(msg), te); msg = msg->next; } @@ -494,31 +498,31 @@ queue_messages(Process *c_p, } static Sint -queue_message(Process *c_p, - Process* receiver, +queue_message(Process* receiver, erts_aint32_t *receiver_state, - ErtsProcLocks *receiver_locks, - ErtsMessage* mp, Eterm msg) + ErtsProcLocks receiver_locks, + ErtsMessage* mp, Eterm msg, Eterm from) { ERL_MESSAGE_TERM(mp) = msg; - return queue_messages(c_p, receiver, receiver_state, *receiver_locks, - mp, &mp->next, 1 ); + return queue_messages(receiver, receiver_state, receiver_locks, + mp, &mp->next, 1, from); } Sint -erts_queue_message(Process* receiver, ErtsProcLocks *receiver_locks, - ErtsMessage* mp, Eterm msg) +erts_queue_message(Process* receiver, ErtsProcLocks receiver_locks, + ErtsMessage* mp, Eterm msg, Eterm from) { - return queue_message(NULL, receiver, NULL, receiver_locks, mp, msg); + return queue_message(receiver, NULL, receiver_locks, mp, msg, from); } Sint -erts_queue_messages(Process* receiver, ErtsProcLocks *receiver_locks, - ErtsMessage* first, ErtsMessage** last, Uint len) +erts_queue_messages(Process* receiver, ErtsProcLocks receiver_locks, + ErtsMessage* first, ErtsMessage** last, Uint len, + Eterm from) { - return queue_messages(NULL, receiver, NULL, *receiver_locks, - first, last, len); + return queue_messages(receiver, NULL, receiver_locks, + first, last, len, from); } void @@ -835,11 +839,11 @@ erts_send_message(Process* sender, #ifdef USE_VM_PROBES ERL_MESSAGE_DT_UTAG(mp) = utag; #endif - res = queue_message(sender, - receiver, + res = queue_message(receiver, &receiver_state, - receiver_locks, - mp, message); + *receiver_locks, + mp, message, + sender->common.id); BM_SWAP_TIMER(send,system); @@ -896,7 +900,7 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, seq_trace_output(token, save, SEQ_TRACE_SEND, to->common.id, NULL); temptoken = copy_struct(token, sz_token, &hp, ohp); ERL_MESSAGE_TOKEN(mp) = temptoken; - erts_queue_message(to, to_locksp, mp, save); + erts_queue_message(to, *to_locksp, mp, save, am_system); } else { sz_from = IS_CONST(from) ? 0 : size_object(from); #ifdef SHCOPY_SEND @@ -918,7 +922,7 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, ? from : copy_struct(from, sz_from, &hp, ohp)); save = TUPLE3(hp, am_EXIT, from_copy, mess); - erts_queue_message(to, to_locksp, mp, save); + erts_queue_message(to, *to_locksp, mp, save, am_system); } } diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 608cf552a2..851ac37fda 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -295,10 +295,10 @@ ErlHeapFragment* new_message_buffer(Uint); ErlHeapFragment* erts_resize_message_buffer(ErlHeapFragment *, Uint, Eterm *, Uint); void free_message_buffer(ErlHeapFragment *); -void erts_queue_dist_message(Process*, ErtsProcLocks*, ErtsDistExternal *, Eterm); -Sint erts_queue_message(Process*, ErtsProcLocks*,ErtsMessage*, Eterm); -Sint erts_queue_messages(Process*, ErtsProcLocks*, - ErtsMessage*, ErtsMessage**, Uint); +void erts_queue_dist_message(Process*, ErtsProcLocks, ErtsDistExternal *, Eterm, Eterm); +Sint erts_queue_message(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm); +Sint erts_queue_messages(Process*, ErtsProcLocks, + ErtsMessage*, ErtsMessage**, Uint, Eterm); void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm); Sint erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned); void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp); diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c index d0f305900a..0e625f213b 100644 --- a/erts/emulator/beam/erl_msacc.c +++ b/erts/emulator/beam/erl_msacc.c @@ -257,7 +257,7 @@ static void send_reply(ErtsMsAcc *msacc, ErtsMSAccReq *msaccrp) { if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx); - erts_queue_message(rp, &rp_locks, msgp, msg); + erts_queue_message(rp, rp_locks, msgp, msg, am_system); if (esdp && msaccrp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 941f44b9ec..bd3e7ebe54 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -366,7 +366,7 @@ int erts_flush_trace_messages(Process *c_p, ErtsProcLocks c_p_locks) rp_locks = 0; if (rp->common.id == c_p->common.id) rp_locks = c_p_locks; - erts_queue_messages(rp, &rp_locks, first, last, len); + erts_queue_messages(rp, rp_locks, first, last, len, c_p->common.id); if (rp->common.id == c_p->common.id) rp_locks &= ~c_p_locks; if (rp_locks) @@ -405,7 +405,10 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ErlNifEnv* msg_env, ERL_NIF_TERM msg) { struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)msg_env; - ErtsProcLocks rp_locks = 0, lc_locks = 0, c_p_locks = ERTS_PROC_LOCK_MAIN; + ErtsProcLocks rp_locks = 0; +#ifdef ERTS_SMP + ErtsProcLocks lc_locks = 0; +#endif Process* rp; Process* c_p; ErtsMessage *mp; @@ -417,7 +420,7 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, if (env != NULL) { c_p = env->proc; if (receiver == c_p->common.id) { - rp_locks = c_p_locks; + rp_locks = ERTS_PROC_LOCK_MAIN; flush_me = 1; } } @@ -470,14 +473,8 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND)) trace_send(c_p, receiver, msg); - -#ifndef ERTS_SMP } -#endif - - erts_queue_message(rp, &rp_locks, mp, msg); #ifdef ERTS_SMP - } else { /* This clause is taken when the nif is called in the context of a traced process. We do not know which locks we have @@ -502,8 +499,6 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, #ifdef ERTS_ENABLE_LOCK_CHECK lc_locks = erts_proc_lc_my_proc_locks(rp); rp_locks |= lc_locks; - if (receiver == c_p->common.id) - c_p_locks |= lc_locks; #endif if (ERTS_FORCE_ENIF_SEND_DELAY() || msgq || rp_locks & ERTS_PROC_LOCK_MSGQ || @@ -532,19 +527,25 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, msgq->last = &mp->next; erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE); } + goto done; } else { erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE); rp_locks &= ~ERTS_PROC_LOCK_TRACE; rp_locks |= ERTS_PROC_LOCK_MSGQ; - erts_queue_message(rp, &rp_locks, mp, msg); } } -#endif +#endif /* ERTS_SMP */ + + erts_queue_message(rp, rp_locks, mp, msg, + c_p ? c_p->common.id : am_undefined); +#ifdef ERTS_SMP +done: if (c_p == rp) rp_locks &= ~ERTS_PROC_LOCK_MAIN; if (rp_locks & ~lc_locks) erts_smp_proc_unlock(rp, rp_locks & ~lc_locks); +#endif if (!scheduler) erts_proc_dec_refc(rp); if (flush_me) { diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index d485affa3b..da2559839d 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -1139,7 +1139,7 @@ reply_sched_wall_time(void *vswtrp) hpp = &hp; } - erts_queue_message(rp, &rp_locks, mp, msg); + erts_queue_message(rp, rp_locks, mp, msg, am_system); if (swtrp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -1218,7 +1218,7 @@ reply_system_check(void *vscrp) hpp = &hp; msg = STORE_NC(hpp, ohp, scrp->ref); - erts_queue_message(rp, &rp_locks, mp, msg); + erts_queue_message(rp, rp_locks, mp, msg, am_system); if (scrp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -10010,7 +10010,7 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result) ASSERT(hp_start + hsz == hp); #endif - erts_queue_message(rp, &rp_locks, mp, msg); + erts_queue_message(rp, rp_locks, mp, msg, c_p->common.id); if (c_p == rp) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -11744,7 +11744,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp); mess = copy_struct(exit_term, term_size, &hp, ohp); #endif - erts_queue_message(to, to_locksp, mp, mess); + erts_queue_message(to, *to_locksp, mp, mess, am_system); } else { Eterm temp_token; Uint sz_token; @@ -11765,7 +11765,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, to); temp_token = copy_struct(token, sz_token, &hp, ohp); ERL_MESSAGE_TOKEN(mp) = temp_token; - erts_queue_message(to, to_locksp, mp, mess); + erts_queue_message(to, *to_locksp, mp, mess, am_system); } } diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 346404fe2a..fadbd704bd 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -1966,7 +1966,7 @@ send_time_offset_changed_notifications(void *new_offsetp) *patch_refp = ref; ASSERT(hsz == size_object(message_template)); message = copy_struct(message_template, hsz, &hp, ohp); - erts_queue_message(rp, &rp_locks, mp, message); + erts_queue_message(rp, rp_locks, mp, message, am_clock_service); } erts_smp_proc_unlock(rp, rp_locks); } diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 755deda9c1..682f08f667 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -738,7 +738,7 @@ profile_send(Eterm from, Eterm message) { else msg = copy_struct(message, sz, &hp, &mp->hfrag.off_heap); - erts_queue_message(profile_p, NULL, mp, msg); + erts_queue_message(profile_p, 0, mp, msg, from); } } @@ -859,8 +859,8 @@ trace_send(Process *p, Eterm to, Eterm msg) * or {trace, Pid, receive, Msg} */ void -trace_receive(Process *c_p, - Process* receiver, +trace_receive(Process* receiver, + Eterm from, Eterm msg, ErtsTracingEvent* te) { ErtsTracerNif *tnif = NULL; @@ -875,15 +875,15 @@ trace_receive(Process *c_p, if (te->match_spec) { Eterm args[2]; Uint32 return_flags; - args[0] = am_undefined; /* ToDo: from who? */ + args[0] = from; args[1] = msg; - pam_result = erts_match_set_run(c_p, receiver, + pam_result = erts_match_set_run(NULL, receiver, te->match_spec, args, 2, ERTS_PAM_TMP_RESULT, &return_flags); if (is_non_value(pam_result) || pam_result == am_false || (ERTS_TRACE_FLAGS(receiver) & F_TRACE_SILENT)) { - erts_match_set_release_result(c_p); + erts_match_set_release_result(NULL); return; } } else @@ -895,7 +895,7 @@ trace_receive(Process *c_p, tnif, TRACE_FUN_T_RECEIVE, am_receive, msg, THE_NON_VALUE, pam_result); } - erts_match_set_release_result(c_p); + erts_match_set_release_result(NULL); } int @@ -1483,7 +1483,7 @@ monitor_long_schedule_proc(Process *p, BeamInstr *in_fp, BeamInstr *out_fp, Uint { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(monitor_p, NULL, mp, msg); + erts_queue_message(monitor_p, 0, mp, msg, am_system); } #endif } @@ -1548,7 +1548,7 @@ monitor_long_schedule_port(Port *pp, ErtsPortTaskType type, Uint time) { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(monitor_p, NULL, mp, msg); + erts_queue_message(monitor_p, 0, mp, msg, am_system); } #endif } @@ -1623,7 +1623,7 @@ monitor_long_gc(Process *p, Uint time) { { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(monitor_p, NULL, mp, msg); + erts_queue_message(monitor_p, 0, mp, msg, am_system); } #endif } @@ -1698,7 +1698,7 @@ monitor_large_heap(Process *p) { { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(monitor_p, NULL, mp, msg); + erts_queue_message(monitor_p, 0, mp, msg, am_system); } #endif } @@ -1730,7 +1730,7 @@ monitor_generic(Process *p, Eterm type, Eterm spec) { { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(monitor_p, NULL, mp, msg); + erts_queue_message(monitor_p, 0, mp, msg, am_system); } #endif @@ -2514,7 +2514,7 @@ sys_msg_dispatcher_func(void *unused) queue_proc_msg: mp = erts_alloc_message(0, NULL); mp->data.heap_frag = smqp->bp; - erts_queue_message(proc,&proc_locks,mp,smqp->msg); + erts_queue_message(proc,proc_locks,mp,smqp->msg,am_system); #ifdef DEBUG_PRINTOUTS erts_fprintf(stderr, "delivered\n"); #endif diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index 040aa296a1..de4beadb7e 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -100,7 +100,7 @@ void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *); #endif void trace_send(Process*, Eterm, Eterm); -void trace_receive(Process*, Process*, Eterm, ErtsTracingEvent*); +void trace_receive(Process*, Eterm, Eterm, ErtsTracingEvent*); Uint32 erts_call_trace(Process *p, BeamInstr mfa[], struct binary *match_spec, Eterm* args, int local, ErtsTracer *tracer); void erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index b14ca77a04..1d307ae15e 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1432,10 +1432,11 @@ finalize_force_imm_drv_call(ErtsTryImmDrvCallState *sp) static ERTS_INLINE void queue_port_sched_op_reply(Process *rp, - ErtsProcLocks *rp_locksp, + ErtsProcLocks rp_locks, ErtsHeapFactory* factory, Uint32 *ref_num, - Eterm msg) + Eterm msg, + Port* prt) { Eterm* hp = erts_produce_heap(factory, ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE, 0); Eterm ref; @@ -1448,11 +1449,12 @@ queue_port_sched_op_reply(Process *rp, erts_factory_trim_and_close(factory, &msg, 1); - erts_queue_message(rp, rp_locksp, factory->message, msg); + erts_queue_message(rp, rp_locks, factory->message, msg, + prt ? prt->common.id : am_undefined); } static void -port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg) +port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg, Port* prt) { Process *rp = erts_proc_lookup_raw(to); if (rp) { @@ -1478,10 +1480,11 @@ port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg) factory.off_heap)); queue_port_sched_op_reply(rp, - &rp_locks, + rp_locks, &factory, ref_num, - msg_copy); + msg_copy, + prt); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); @@ -1651,7 +1654,7 @@ port_badsig(Port *prt, erts_aint32_t state, int op, state, sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BAD_OUTPUT); if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) - port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg); + port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt); return ERTS_PORT_REDS_BADSIG; } /* port_badsig */ /* bad_port_signal() will @@ -1820,7 +1823,7 @@ port_sig_outputv(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *s } if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) - port_sched_op_reply(sigdp->caller, sigdp->ref, reply); + port_sched_op_reply(sigdp->caller, sigdp->ref, reply, prt); cleanup_scheduled_outputv(sigdp->u.outputv.evp, sigdp->u.outputv.cbinp); @@ -1928,7 +1931,7 @@ port_sig_output(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *si } if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) - port_sched_op_reply(sigdp->caller, sigdp->ref, reply); + port_sched_op_reply(sigdp->caller, sigdp->ref, reply, prt); cleanup_scheduled_output(sigdp->u.output.bufp); @@ -2636,7 +2639,7 @@ port_sig_exit(Port *prt, if (sigdp->u.exit.bp) free_message_buffer(sigdp->u.exit.bp); if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) - port_sched_op_reply(sigdp->caller, sigdp->ref, msg); + port_sched_op_reply(sigdp->caller, sigdp->ref, msg, prt); return ERTS_PORT_REDS_EXIT; } @@ -2829,7 +2832,7 @@ port_sig_connect(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *s msg = am_true; } if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) - port_sched_op_reply(sigdp->caller, sigdp->ref, msg); + port_sched_op_reply(sigdp->caller, sigdp->ref, msg, prt); return ERTS_PORT_REDS_CONNECT; } @@ -2912,7 +2915,7 @@ port_sig_unlink(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *si if (op == ERTS_PROC2PORT_SIG_EXEC) port_unlink(prt, sigdp->u.unlink.from); if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) - port_sched_op_reply(sigdp->caller, sigdp->ref, am_true); + port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt); return ERTS_PORT_REDS_UNLINK; } @@ -3007,7 +3010,7 @@ port_sig_link(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigd port_link_failure(sigdp->u.link.port, sigdp->u.link.to); } if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) - port_sched_op_reply(sigdp->caller, sigdp->ref, am_true); + port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt); return ERTS_PORT_REDS_LINK; } @@ -3064,7 +3067,8 @@ init_ack_send_reply(Port *port, Eterm resp) } port_sched_op_reply(port->async_open_port->to, port->async_open_port->ref, - resp); + resp, + port); erts_free(ERTS_ALC_T_PRTSD, port->async_open_port); port->async_open_port = NULL; @@ -3461,7 +3465,7 @@ deliver_result(Port *prt, Eterm sender, Eterm pid, Eterm res) sz_res + 3, &hp, &ohp); res = copy_struct(res, sz_res, &hp, ohp); tuple = TUPLE2(hp, sender, res); - erts_queue_message(rp, &rp_locks, mp, tuple); + erts_queue_message(rp, rp_locks, mp, tuple, sender); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); @@ -3562,7 +3566,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to, trace_port_send(prt, to, tuple, 1); ERL_MESSAGE_TOKEN(mp) = am_undefined; - erts_queue_message(rp, &rp_locks, mp, tuple); + erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) @@ -3734,7 +3738,7 @@ deliver_vec_message(Port* prt, /* Port */ trace_port_send(prt, to, tuple, 1); ERL_MESSAGE_TOKEN(mp) = am_undefined; - erts_queue_message(rp, &rp_locks, mp, tuple); + erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id); erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) erts_proc_dec_refc(rp); @@ -4388,10 +4392,11 @@ port_sig_control(Port *prt, &factory.hp, factory.off_heap); queue_port_sched_op_reply(rp, - &rp_locks, + rp_locks, &factory, sigdp->ref, - msg); + msg, + prt); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); @@ -4402,7 +4407,7 @@ port_sig_control(Port *prt, /* failure */ if (sigdp->caller != ERTS_INVALID_PID) - port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg); + port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt); done: @@ -4739,10 +4744,11 @@ port_sig_call(Port *prt, msg = TUPLE2(hp, am_ok, msg); queue_port_sched_op_reply(rp, - &rp_locks, + rp_locks, &factory, sigdp->ref, - msg); + msg, + prt); if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); @@ -4754,7 +4760,7 @@ port_sig_call(Port *prt, } } - port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg); + port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt); done: @@ -4969,7 +4975,7 @@ port_sig_info(Port *prt, { ASSERT(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY); if (op != ERTS_PROC2PORT_SIG_EXEC) - port_sched_op_reply(sigdp->caller, sigdp->ref, am_undefined); + port_sched_op_reply(sigdp->caller, sigdp->ref, am_undefined, prt); else { Eterm *hp, *hp_start; Uint hsz; @@ -4995,10 +5001,11 @@ port_sig_info(Port *prt, mp->data.heap_frag = bp; erts_factory_selfcontained_message_init(&factory, mp, hp); queue_port_sched_op_reply(rp, - &rp_locks, + rp_locks, &factory, sigdp->ref, - value); + value, + prt); } if (rp_locks) erts_smp_proc_unlock(rp, rp_locks); @@ -5115,7 +5122,7 @@ reply_io_bytes(void *vreq) msg = TUPLE4(hp, ref, make_small(sched_id), ein, eout); - erts_queue_message(rp, &rp_locks, mp, msg); + erts_queue_message(rp, rp_locks, mp, msg, am_system); if (req->sched_id == sched_id) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -5613,7 +5620,7 @@ void driver_report_exit(ErlDrvPort ix, int status) trace_port_send(prt, pid, tuple, 1); ERL_MESSAGE_TOKEN(mp) = am_undefined; - erts_queue_message(rp, &rp_locks, mp, tuple); + erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id); erts_smp_proc_unlock(rp, rp_locks); if (!scheduler) @@ -6217,15 +6224,20 @@ driver_deliver_term(Port *prt, Eterm to, ErlDrvTermData* data, int len) done: if (res > 0) { + Eterm from = am_undefined; mess = ESTACK_POP(stack); /* get resulting value */ erts_factory_trim_and_close(&factory, &mess, 1); - if (prt && IS_TRACED_FL(prt, F_TRACE_SEND)) - trace_port_send(prt, to, mess, 1); + if (prt) { + if (IS_TRACED_FL(prt, F_TRACE_SEND)) { + trace_port_send(prt, to, mess, 1); + } + from = prt->common.id; + } /* send message */ ERL_MESSAGE_TOKEN(factory.message) = am_undefined; - erts_queue_message(rp, &rp_locks, factory.message, mess); + erts_queue_message(rp, rp_locks, factory.message, mess, from); } else if (res == -2) { /* this clause only happens when we were requested to diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index b280995488..74114626be 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2298,7 +2298,7 @@ static void do_send_logger_message(Eterm *hp, ErlOffHeap *ohp, ErlHeapFragment * { ErtsMessage *mp = erts_alloc_message(0, NULL); mp->data.heap_frag = bp; - erts_queue_message(p, NULL /* only used for smp build */, mp, message); + erts_queue_message(p, 0, mp, message, am_system); } #endif } diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index d8076a3e02..9c03b3811c 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -601,7 +601,7 @@ void hipe_clear_timeout(Process *c_p) } #endif if (IS_TRACED_FL(c_p, F_TRACE_RECEIVE)) { - trace_receive(c_p, c_p, am_timeout, NULL); + trace_receive(c_p, am_clock_service, am_timeout, NULL); } c_p->flags &= ~F_TIMO; JOIN_MESSAGE(c_p); diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index de1d292d27..af35a6d6bf 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -106,14 +106,69 @@ receive_trace(Config) when is_list(Config) -> end, receive_nothing() end, + From = self(), lists:foreach(F1, [{no, true}, {[{[undefined,"Unexpected"],[],[]}], false}, - {[{['_','_'],[],[]}], true}, - {[{['$1','_'],[{is_integer,'$1'}],[]}], false}, + {[{[From,'_'],[],[]}], true}, + {[{['$1','_'],[{'=/=','$1',From}],[]}], false}, {[{['_','$1'],[{is_tuple,'$1'}],[]}], true}, {false, false}, {true, true}]), + %% Remote messages + OtherName = atom_to_list(?MODULE)++"_receive_trace", + {ok, OtherNode} = start_node(OtherName), + RemoteProc = spawn(OtherNode, ?MODULE, process, [self()]), + io:format("RemoteProc = ~p ~n", [RemoteProc]), + + RemoteProc ! {send_please, Receiver, Hello}, + {trace, Receiver, 'receive', Hello} = receive_first_trace(), + RemoteProc ! {send_please, Receiver, 99}, + {trace, Receiver, 'receive', 99} = receive_first_trace(), + + %% Remote with matchspec + F2 = fun (To, {Pat, IsMatching}) -> + set_trace_pattern('receive', Pat, []), + RemoteProc ! {send_please, To, Hello}, + case IsMatching of + true -> + {trace, Receiver, 'receive', Hello} = receive_first_trace(); + false -> + ok + end, + receive_nothing() + end, + F2(Receiver, {no, true}), + F2(Receiver, {[{[undefined,"Unexpected"],[],[]}], false}), + F2(Receiver, {[{[RemoteProc,'_'],[],[]}], true}), + F2(Receiver, {[{['$1','_'], [{'=/=',{node,'$1'},{node}}], []}], true}), + F2(Receiver, {[{['_','$1'], [{is_tuple,'$1'}], []}], true}), + F2(Receiver, {false, false}), + F2(Receiver, {true, true}), + + %% Remote to named with matchspec + Name = trace_SUITE_receiver, + register(Name, Receiver), + NN = {Name, node()}, + F2(NN, {no, true}), + F2(NN, {[{[undefined,"Unexpected"],[],[]}], false}), + F2(NN, {[{[RemoteProc,'_'],[],[]}], true}), + F2(NN, {[{['$1','_'], [{'=/=',{node,'$1'},{node}}], []}], true}), + F2(NN, {[{['_','$1'], [{is_tuple,'$1'}], []}], true}), + F2(NN, {false, false}), + F2(NN, {true, true}), + + true = stop_node(OtherNode), + + %% Timeout + Receiver ! {set_timeout, 10}, + {trace, Receiver, 'receive', {set_timeout, 10}} = receive_first_trace(), + {trace, Receiver, 'receive', timeout} = receive_first_trace(), + erlang:trace_pattern('receive', [{[clock_service,timeout], [], []}], []), + Receiver ! {set_timeout, 7}, + {trace, Receiver, 'receive', timeout} = receive_first_trace(), + erlang:trace_pattern('receive', true, []), + %% Another process should not be able to trace Receiver. Intruder = fun_spawn(fun() -> erlang:trace(Receiver, true, ['receive']) end), {'EXIT', Intruder, {badarg, _}} = receive_first(), @@ -125,19 +180,19 @@ receive_trace(Config) when is_list(Config) -> ?line receive_nothing(), %% Verify restrictions in matchspec for 'receive' - F2 = fun (Pat) -> {'EXIT', {badarg,_}} = (catch erlang:trace_pattern('receive', Pat, [])) end, - lists:foreach(F2, [[{['_','_'],[],[{message, {process_dump}}]}], - [{['_','_'],[{is_seq_trace}],[]}], - [{['_','_'],[],[{set_seq_token,label,4711}]}], - [{['_','_'],[],[{get_seq_token}]}], - [{['_','_'],[],[{enable_trace,call}]}], - [{['_','_'],[],[{enable_trace,self(),call}]}], - [{['_','_'],[],[{disable_trace,call}]}], - [{['_','_'],[],[{disable_trace,self(),call}]}], - [{['_','_'],[],[{trace,[call],[]}]}], - [{['_','_'],[],[{trace,self(),[],[call]}]}], - [{['_','_'],[],[{caller}]}], - [{['_','_'],[],[{silent,true}]}]]), + F3 = fun (Pat) -> {'EXIT', {badarg,_}} = (catch erlang:trace_pattern('receive', Pat, [])) end, + F3([{['_','_'],[],[{message, {process_dump}}]}]), + F3([{['_','_'],[{is_seq_trace}],[]}]), + F3([{['_','_'],[],[{set_seq_token,label,4711}]}]), + F3([{['_','_'],[],[{get_seq_token}]}]), + F3([{['_','_'],[],[{enable_trace,call}]}]), + F3([{['_','_'],[],[{enable_trace,self(),call}]}]), + F3([{['_','_'],[],[{disable_trace,call}]}]), + F3([{['_','_'],[],[{disable_trace,self(),call}]}]), + F3([{['_','_'],[],[{trace,[call],[]}]}]), + F3([{['_','_'],[],[{trace,self(),[],[call]}]}]), + F3([{['_','_'],[],[{caller}]}]), + F3([{['_','_'],[],[{silent,true}]}]), ok. @@ -568,6 +623,7 @@ dist_procs_trace(Config) when is_list(Config) -> Proc2 ! {unlink_please, Proc1}, {trace, Proc1, getting_unlinked, Proc2} = receive_first_trace(), receive_nothing(), + %% %% exit (with registered name, due to link) Name = list_to_atom(OtherName), @@ -1649,9 +1705,14 @@ sender() -> %% Just consumes messages from its message queue. receiver() -> - receive - _Any -> receiver() - end. + receiver(infinity). + +receiver(Timeout) -> + receiver(receive + {set_timeout, NewTimeout} -> NewTimeout; + _Any -> Timeout + after Timeout -> infinity %% reset + end). %% Works as long as it receives CPU time. Will always be RUNNABLE. -- cgit v1.2.3 From da75310ce8973221ac90fd34f6375bfc17ae751b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 31 Mar 2016 21:20:24 +0200 Subject: erts: Change receive matchspec to [NodeOrOther, Pid, Msg] --- erts/emulator/beam/erl_trace.c | 17 +++++++--- erts/emulator/test/trace_SUITE.erl | 63 ++++++++++++++++++++++---------------- 2 files changed, 49 insertions(+), 31 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 682f08f667..be0d2f0939 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -871,14 +871,23 @@ trace_receive(Process* receiver, if (!te->on) return; } + else ASSERT(te->on); if (te->match_spec) { - Eterm args[2]; + Eterm args[3]; Uint32 return_flags; - args[0] = from; - args[1] = msg; + if (is_pid(from)) { + args[0] = pid_node_name(from); + args[1] = from; + } + else { + ASSERT(is_atom(from)); + args[0] = from; /* node name or other atom (e.g 'system') */ + args[1] = am_undefined; + } + args[2] = msg; pam_result = erts_match_set_run(NULL, receiver, - te->match_spec, args, 2, + te->match_spec, args, 3, ERTS_PAM_TMP_RESULT, &return_flags); if (is_non_value(pam_result) || pam_result == am_false diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index af35a6d6bf..1930332691 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -82,7 +82,6 @@ cpu_timestamp(Config) when is_list(Config) -> receive_trace(Config) when is_list(Config) -> Receiver = fun_spawn(fun receiver/0), - process_flag(trap_exit, true), %% Trace the process; make sure that we receive the trace messages. 1 = erlang:trace(Receiver, true, ['receive']), @@ -107,18 +106,19 @@ receive_trace(Config) when is_list(Config) -> receive_nothing() end, From = self(), + Node = node(), lists:foreach(F1, [{no, true}, - {[{[undefined,"Unexpected"],[],[]}], false}, - {[{[From,'_'],[],[]}], true}, - {[{['$1','_'],[{'=/=','$1',From}],[]}], false}, - {[{['_','$1'],[{is_tuple,'$1'}],[]}], true}, + {[{[Node, undefined,"Unexpected"],[],[]}], false}, + {[{[Node, From,'_'],[],[]}], true}, + {[{[Node, '$1','_'],[{'=/=','$1',From}],[]}], false}, + {[{['$1', '_','_'],[{'=:=','$1',Node}],[]}], true}, {false, false}, {true, true}]), %% Remote messages OtherName = atom_to_list(?MODULE)++"_receive_trace", {ok, OtherNode} = start_node(OtherName), - RemoteProc = spawn(OtherNode, ?MODULE, process, [self()]), + RemoteProc = spawn_link(OtherNode, ?MODULE, process, [self()]), io:format("RemoteProc = ~p ~n", [RemoteProc]), RemoteProc ! {send_please, Receiver, Hello}, @@ -139,10 +139,13 @@ receive_trace(Config) when is_list(Config) -> receive_nothing() end, F2(Receiver, {no, true}), - F2(Receiver, {[{[undefined,"Unexpected"],[],[]}], false}), - F2(Receiver, {[{[RemoteProc,'_'],[],[]}], true}), - F2(Receiver, {[{['$1','_'], [{'=/=',{node,'$1'},{node}}], []}], true}), - F2(Receiver, {[{['_','$1'], [{is_tuple,'$1'}], []}], true}), + F2(Receiver, {[{[OtherNode, undefined,"Unexpected"],[],[]}], false}), + F2(Receiver, {[{[OtherNode, RemoteProc,'_'],[],[]}, + {[OtherNode, undefined,'_'],[],[]}], true}), + F2(Receiver, {[{[OtherNode, '$1','_'], + [{'orelse',{'=:=','$1',undefined},{'=/=',{node,'$1'},{node}}}], + []}], true}), + F2(Receiver, {[{['$1', '_','_'], [{'=:=','$1',OtherNode}], []}], true}), F2(Receiver, {false, false}), F2(Receiver, {true, true}), @@ -151,25 +154,30 @@ receive_trace(Config) when is_list(Config) -> register(Name, Receiver), NN = {Name, node()}, F2(NN, {no, true}), - F2(NN, {[{[undefined,"Unexpected"],[],[]}], false}), - F2(NN, {[{[RemoteProc,'_'],[],[]}], true}), - F2(NN, {[{['$1','_'], [{'=/=',{node,'$1'},{node}}], []}], true}), - F2(NN, {[{['_','$1'], [{is_tuple,'$1'}], []}], true}), + F2(NN, {[{[OtherNode, undefined,"Unexpected"],[],[]}], false}), + F2(NN, {[{[OtherNode, RemoteProc,'_'],[],[]}, + {[OtherNode, undefined,'_'],[],[]}], true}), + F2(NN, {[{[OtherNode, '$1','_'], + [{'orelse',{'=:=','$1',undefined},{'=/=',{node,'$1'},{node}}}], + []}], true}), + F2(NN, {[{['$1', '_','_'], [{'==','$1',OtherNode}], []}], true}), F2(NN, {false, false}), F2(NN, {true, true}), + unlink(RemoteProc), true = stop_node(OtherNode), %% Timeout Receiver ! {set_timeout, 10}, {trace, Receiver, 'receive', {set_timeout, 10}} = receive_first_trace(), {trace, Receiver, 'receive', timeout} = receive_first_trace(), - erlang:trace_pattern('receive', [{[clock_service,timeout], [], []}], []), + erlang:trace_pattern('receive', [{[clock_service,undefined,timeout], [], []}], []), Receiver ! {set_timeout, 7}, {trace, Receiver, 'receive', timeout} = receive_first_trace(), erlang:trace_pattern('receive', true, []), %% Another process should not be able to trace Receiver. + process_flag(trap_exit, true), Intruder = fun_spawn(fun() -> erlang:trace(Receiver, true, ['receive']) end), {'EXIT', Intruder, {badarg, _}} = receive_first(), @@ -181,18 +189,19 @@ receive_trace(Config) when is_list(Config) -> %% Verify restrictions in matchspec for 'receive' F3 = fun (Pat) -> {'EXIT', {badarg,_}} = (catch erlang:trace_pattern('receive', Pat, [])) end, - F3([{['_','_'],[],[{message, {process_dump}}]}]), - F3([{['_','_'],[{is_seq_trace}],[]}]), - F3([{['_','_'],[],[{set_seq_token,label,4711}]}]), - F3([{['_','_'],[],[{get_seq_token}]}]), - F3([{['_','_'],[],[{enable_trace,call}]}]), - F3([{['_','_'],[],[{enable_trace,self(),call}]}]), - F3([{['_','_'],[],[{disable_trace,call}]}]), - F3([{['_','_'],[],[{disable_trace,self(),call}]}]), - F3([{['_','_'],[],[{trace,[call],[]}]}]), - F3([{['_','_'],[],[{trace,self(),[],[call]}]}]), - F3([{['_','_'],[],[{caller}]}]), - F3([{['_','_'],[],[{silent,true}]}]), + WC = ['_','_','_'], + F3([{WC,[],[{message, {process_dump}}]}]), + F3([{WC,[{is_seq_trace}],[]}]), + F3([{WC,[],[{set_seq_token,label,4711}]}]), + F3([{WC,[],[{get_seq_token}]}]), + F3([{WC,[],[{enable_trace,call}]}]), + F3([{WC,[],[{enable_trace,self(),call}]}]), + F3([{WC,[],[{disable_trace,call}]}]), + F3([{WC,[],[{disable_trace,self(),call}]}]), + F3([{WC,[],[{trace,[call],[]}]}]), + F3([{WC,[],[{trace,self(),[],[call]}]}]), + F3([{WC,[],[{caller}]}]), + F3([{WC,[],[{silent,true}]}]), ok. -- cgit v1.2.3 From 36e9d73aa08930ddf3e3587addfb9a647a41b3e7 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 6 Apr 2016 15:05:10 +0200 Subject: erts: Add docs for trace_pattern with 'send' and 'receive' --- erts/doc/src/erlang.xml | 140 ++++++++++++++++++++++++++++++++++++++++-- erts/preloaded/src/erlang.erl | 10 ++- 2 files changed, 142 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index c7b5ea8867..f88d05cdc3 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -9124,27 +9124,155 @@ timestamp() -> - Sets trace patterns for global call tracing. + Sets trace patterns for call, send or 'receive' tracing.

The same as - erlang:trace_pattern(MFA, MatchSpec, []), + erlang:trace_pattern(Event, MatchSpec, []), retained for backward compatibility.

- + + Sets trace pattern for message sending. + +

Sets trace pattern for message sending. + Must be combined with + erlang:trace/3 + to set the send trace flag for one or more processes. + By default all messages, sent from send traced processes, + are traced. Use erlang:trace_pattern/3 to limit + traced send events based on the message content, the sender + and/or the receiver.

+

Argument MatchSpec can take the + following forms:

+ + MatchSpecList + +

A list of match specifications. The matching is done + on the list [Receiver, Msg]. Receiver + is the process or port identity of the receiver and + Msg is the message term. The pid of the sending + process can be accessed with the guard function + self/0. An empty list is the same as true. + See the users guide section + Match Specifications in Erlang + for more information.

+
+ true + +

Enables tracing for all sent messages (from send + traced processes). Any match specification is + removed. This is the default.

+
+ false + +

Disables tracing for all sent messages. + Any match specification is removed.

+
+
+

Argument FlagList must be [] + for send tracing.

+

The return value is always 1.

+

Example; only trace messages to a specific process Pid:

+
+> erlang:trace_pattern(send, [{[Pid, '_'],[],[]}], []).
+1
+

Only trace messages matching {reply, _}:

+
+> erlang:trace_pattern(send, [{['_', {reply,'_'}],[],[]}], []).
+1
+

Only trace messages sent to the sender itself:

+
+> erlang:trace_pattern(send, [{['$1', '_'],[{'=:=','$1',{self}}],[]}], []).
+1
+

Only trace messages sent to other nodes:

+
+> erlang:trace_pattern(send, [{['$1', '_'],[{'=/=',{node,'$1'},{node}}],[]}], []).
+1
+

A match specification for send trace can use + all guard and body functions except caller.

+
+
+ + + + Sets trace pattern for tracing of message receiving. + +

+

Sets trace pattern for message receiving. + Must be combined with + erlang:trace/3 + to set the 'receive' trace flag for one or more processes. + By default all messages, received by 'receive' traced processes, + are traced. Use erlang:trace_pattern/3 to limit + traced receive events based on the message content, the sender + and/or the receiver.

+

Argument MatchSpec can take the + following forms:

+ + MatchSpecList + +

A list of match specifications. The matching is done + on the list [Node, Sender, Msg]. Node + is the node name of the sender. Sender is the + process or port identity of the sender, or the atom + undefined if the sender is not known (which may + be the case for remote senders). Msg is the + message term. The pid of the receiving process can be + accessed with the guard function self/0. An empty + list is the same as true. See the users guide section + Match Specifications in Erlang + for more information.

+
+ true + +

Enables tracing for all received messages (to 'receive' + traced processes). Any match specification is + removed. This is the default.

+
+ false + +

Disables tracing for all sent messages. + Any match specification is removed.

+
+
+

Argument FlagList must be [] + for receive tracing.

+

The return value is always 1.

+

Example; only trace messages from a specific process Pid:

+
+> erlang:trace_pattern('receive', [{['_',Pid, '_'],[],[]}], []).
+1
+

Only trace messages matching {reply, _}:

+
+> erlang:trace_pattern('receive', [{['_','_', {reply,'_'}],[],[]}], []).
+1
+

Only trace messages from other nodes:

+
+> erlang:trace_pattern('receive', [{['$1', '_', '_'],[{'=/=','$1',{node}}],[]}], []).
+1
+

A match specification for 'receive' trace can + use all guard and body functions except caller, + is_seq_trace, get_seq_token, set_seq_token, enable_trace, + disable_trace, trace, silent and process_dump.

+
+
+ + + Sets trace patterns for tracing of function calls. -

Enables or disables call tracing for - one or more functions. Must be combined with +

Enables or disables call tracing for one or more functions. + Must be combined with erlang:trace/3 - to set the call trace flag for one or more processes.

+ to set the call trace flag + for one or more processes.

Conceptually, call tracing works as follows. Inside the Erlang Virtual Machine, a set of processes and a set of functions are to be traced. If a traced process diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 20a64e81b4..d09958032b 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2381,7 +2381,7 @@ tl(_List) -> [{[term()] | '_' ,[term()],[term()]}]. -spec erlang:trace_pattern(MFA, MatchSpec) -> non_neg_integer() when - MFA :: trace_pattern_mfa(), + MFA :: trace_pattern_mfa() | send | 'receive', MatchSpec :: (MatchSpecList :: trace_match_spec()) | boolean() | restart @@ -2403,7 +2403,13 @@ trace_pattern(MFA, MatchSpec) -> call_count | call_time. --spec erlang:trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when +-spec erlang:trace_pattern(send, MatchSpec, []) -> non_neg_integer() when + MatchSpec :: (MatchSpecList :: trace_match_spec()) + | boolean(); + ('receive', MatchSpec, []) -> non_neg_integer() when + MatchSpec :: (MatchSpecList :: trace_match_spec()) + | boolean(); + (MFA, MatchSpec, FlagList) -> non_neg_integer() when MFA :: trace_pattern_mfa(), MatchSpec :: (MatchSpecList :: trace_match_spec()) | boolean() -- cgit v1.2.3 From 14b81c60f0c6403e9371be420a6df4d414180c68 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 29 Apr 2016 17:50:18 +0200 Subject: erts: Fix PAM to be callable from non-scheduler thread also simplified the interface to to run PAM from trace --- erts/emulator/beam/erl_db_util.c | 73 ++++++++++++++++++++++++---------------- erts/emulator/beam/erl_trace.c | 61 +++++++++++++++------------------ erts/emulator/beam/global.h | 22 ++++++++---- 3 files changed, 88 insertions(+), 68 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 7d64529250..051c107d1f 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -414,19 +414,27 @@ get_match_pseudo_process(Process *c_p, Uint heap_size) { ErtsMatchPseudoProcess *mpsp; #ifdef ERTS_SMP - ErtsSchedulerData *esdp = c_p ? c_p->scheduler_data : erts_get_scheduler_data(); + ErtsSchedulerData *esdp; + + esdp = c_p ? c_p->scheduler_data : erts_get_scheduler_data(); + + mpsp = esdp ? esdp->match_pseudo_process : + (ErtsMatchPseudoProcess*) erts_smp_tsd_get(match_pseudo_process_key); - mpsp = (ErtsMatchPseudoProcess *) esdp->match_pseudo_process; - if (mpsp) + if (mpsp) { + ASSERT(mpsp == erts_smp_tsd_get(match_pseudo_process_key)); + ASSERT(mpsp->process.scheduler_data == esdp); cleanup_match_pseudo_process(mpsp, 0); + } else { ASSERT(erts_smp_tsd_get(match_pseudo_process_key) == NULL); mpsp = create_match_pseudo_process(); - esdp->match_pseudo_process = (void *) mpsp; + if (esdp) { + esdp->match_pseudo_process = (void *) mpsp; + } + mpsp->process.scheduler_data = esdp; erts_smp_tsd_set(match_pseudo_process_key, (void *) mpsp); } - ASSERT(mpsp == erts_smp_tsd_get(match_pseudo_process_key)); - mpsp->process.scheduler_data = esdp; #else mpsp = match_pseudo_process; cleanup_match_pseudo_process(mpsp, 0); @@ -1206,32 +1214,37 @@ done: return ret; } -Eterm erts_match_set_run(Process *c_p, - Process *self, - Binary *mpsp, - Eterm *args, int num_args, - enum erts_pam_run_flags in_flags, - Uint32 *return_flags) +/* Returns + * am_false if no match or + * if {message,false} has been called, + * am_true if {message,_} has NOT been called or + * if {message,true} has been called, + * Msg if {message,Msg} has been called. + * + * If return value is_not_immed + * then erts_match_set_release_result_trace() must be called to release it. + */ +Eterm erts_match_set_run_trace(Process *c_p, + Process *self, + Binary *mpsp, + Eterm *args, int num_args, + enum erts_pam_run_flags in_flags, + Uint32 *return_flags) { Eterm ret; ret = db_prog_match(c_p, self, mpsp, NIL, args, num_args, in_flags, return_flags); -#if defined(HARDDEBUG) - if (is_non_value(ret)) { - erts_fprintf(stderr, "Failed\n"); - } else { - erts_fprintf(stderr, "Returning : %T\n", ret); + + ASSERT(!(is_non_value(ret) && *return_flags)); + + if (is_non_value(ret) || ret == am_false) { + erts_match_set_release_result(c_p); + return am_false; } -#endif + if (is_immed(ret)) + erts_match_set_release_result(c_p); return ret; - /* Returns - * THE_NON_VALUE if no match - * am_false if {message,false} has been called, - * am_true if {message,_} has not been called or - * if {message,true} has been called, - * Msg if {message,Msg} has been called. - */ } static Eterm erts_match_set_run_ets(Process *p, Binary *mpsp, @@ -1774,6 +1787,7 @@ Eterm db_prog_match(Process *c_p, #endif /* DMC_DEBUG */ ERTS_UNDEF(n,0); + ERTS_UNDEF(current_scheduled,NULL); ASSERT(c_p || !(in_flags & ERTS_PAM_COPY_RESULT)); @@ -1784,8 +1798,8 @@ Eterm db_prog_match(Process *c_p, because of floating point exceptions. Do *after* mpsp is set!!! */ esdp = ERTS_GET_SCHEDULER_DATA_FROM_PROC(psp); - ASSERT(esdp != NULL); - current_scheduled = esdp->current_process; + if (esdp) + current_scheduled = esdp->current_process; /* SMP: psp->scheduler_data is set by get_match_pseudo_process */ #ifdef DMC_DEBUG @@ -2517,7 +2531,8 @@ success: } #endif - esdp->current_process = current_scheduled; + if (esdp) + esdp->current_process = current_scheduled; return ret; #undef FAIL @@ -5073,7 +5088,7 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace) } save_cp = p->cp; p->cp = NULL; - res = erts_match_set_run(p, p, + res = erts_match_set_run_trace(p, p, mps, arr, n, ERTS_PAM_COPY_RESULT|ERTS_PAM_IGNORE_TRACE_SILENT, &ret_flags); diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index be0d2f0939..9f3341537d 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -825,13 +825,13 @@ trace_send(Process *p, Eterm to, Eterm msg) Uint32 return_flags; args[0] = to; args[1] = msg; - pam_result = erts_match_set_run(p, p, - te->match_spec, args, 2, - ERTS_PAM_TMP_RESULT, &return_flags); - if (is_non_value(pam_result) - || pam_result == am_false - || (ERTS_TRACE_FLAGS(p) & F_TRACE_SILENT)) { - erts_match_set_release_result(p); + pam_result = erts_match_set_run_trace(p, p, + te->match_spec, args, 2, + ERTS_PAM_TMP_RESULT, &return_flags); + if (pam_result == am_false) + return; + if (ERTS_TRACE_FLAGS(p) & F_TRACE_SILENT) { + erts_match_set_release_result_trace(p, pam_result); return; } } else @@ -850,9 +850,9 @@ trace_send(Process *p, Eterm to, Eterm msg) if (is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, TRACE_FUN_E_SEND, operation)) { send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SEND, - operation, msg, to, pam_result); + operation, msg, to, pam_result); } - erts_match_set_release_result(p); + erts_match_set_release_result_trace(p, pam_result); } /* Send {trace_ts, Pid, receive, Msg, Timestamp} @@ -886,13 +886,13 @@ trace_receive(Process* receiver, args[1] = am_undefined; } args[2] = msg; - pam_result = erts_match_set_run(NULL, receiver, - te->match_spec, args, 3, - ERTS_PAM_TMP_RESULT, &return_flags); - if (is_non_value(pam_result) - || pam_result == am_false - || (ERTS_TRACE_FLAGS(receiver) & F_TRACE_SILENT)) { - erts_match_set_release_result(NULL); + pam_result = erts_match_set_run_trace(NULL, receiver, + te->match_spec, args, 3, + ERTS_PAM_TMP_RESULT, &return_flags); + if (pam_result == am_false) + return; + if (ERTS_TRACE_FLAGS(receiver) & F_TRACE_SILENT) { + erts_match_set_release_result_trace(NULL, pam_result); return; } } else @@ -904,7 +904,7 @@ trace_receive(Process* receiver, tnif, TRACE_FUN_T_RECEIVE, am_receive, msg, THE_NON_VALUE, pam_result); } - erts_match_set_release_result(NULL); + erts_match_set_release_result_trace(NULL, pam_result); } int @@ -1264,21 +1264,14 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, may remove it, and we still want to generate a trace message */ erts_tracer_update(&pre_ms_tracer, *tracer); tracer = &pre_ms_tracer; - pam_result = erts_match_set_run(p, p, - match_spec, args, arity, - ERTS_PAM_TMP_RESULT, &return_flags); - if (is_non_value(pam_result)) { - erts_match_set_release_result(p); - UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); - ERTS_TRACER_CLEAR(&pre_ms_tracer); - return 0; - } + pam_result = erts_match_set_run_trace(p, p, + match_spec, args, arity, + ERTS_PAM_TMP_RESULT, &return_flags); } if (tracee_flags == &meta_flags) { /* Meta trace */ if (pam_result == am_false) { - erts_match_set_release_result(p); UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); ERTS_TRACER_CLEAR(&pre_ms_tracer); return return_flags; @@ -1286,13 +1279,12 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, } else { /* Non-meta trace */ if (*tracee_flags & F_TRACE_SILENT) { - erts_match_set_release_result(p); + erts_match_set_release_result_trace(p, pam_result); UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); ERTS_TRACER_CLEAR(&pre_ms_tracer); return 0; } if (pam_result == am_false) { - erts_match_set_release_result(p); UnUseTmpHeap(ERL_SUB_BIN_SIZE,p); ERTS_TRACER_CLEAR(&pre_ms_tracer); return return_flags; @@ -1328,11 +1320,14 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, * Build the trace tuple and send it to the port. */ send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id, - tnif, TRACE_FUN_T_CALL, am_call, mfa_tuple, THE_NON_VALUE, pam_result); - erts_match_set_release_result(p); + tnif, TRACE_FUN_T_CALL, am_call, mfa_tuple, + THE_NON_VALUE, pam_result); - if (match_spec && tracer == &pre_ms_tracer) - ERTS_TRACER_CLEAR(&pre_ms_tracer); + if (match_spec) { + erts_match_set_release_result_trace(p, pam_result); + if (tracer == &pre_ms_tracer) + ERTS_TRACER_CLEAR(&pre_ms_tracer); + } return return_flags; } diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 328bfe344c..baa0a201d1 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1486,6 +1486,16 @@ do { \ extern Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA); Eterm erts_match_set_lint(Process *p, Eterm matchexpr); extern void erts_match_set_release_result(Process* p); +ERTS_GLB_INLINE void erts_match_set_release_result_trace(Process* p, Eterm); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF +ERTS_GLB_INLINE +void erts_match_set_release_result_trace(Process* p, Eterm pam_result) +{ + if (is_not_immed(pam_result)) + erts_match_set_release_result(p); +} +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ enum erts_pam_run_flags { ERTS_PAM_TMP_RESULT=1, @@ -1493,12 +1503,12 @@ enum erts_pam_run_flags { ERTS_PAM_CONTIGUOUS_TUPLE=4, ERTS_PAM_IGNORE_TRACE_SILENT=8 }; -extern Eterm erts_match_set_run(Process *p, - Process *self, - Binary *mpsp, - Eterm *args, int num_args, - enum erts_pam_run_flags in_flags, - Uint32 *return_flags); +extern Eterm erts_match_set_run_trace(Process *p, + Process *self, + Binary *mpsp, + Eterm *args, int num_args, + enum erts_pam_run_flags in_flags, + Uint32 *return_flags); extern Eterm erts_match_set_get_source(Binary *mpsp); extern void erts_match_prog_foreach_offheap(Binary *b, void (*)(ErlOffHeap *, void *), -- cgit v1.2.3 From d0ffd5c2a84d15d94dcbc8bac98d527bfc1d4a3c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 2 May 2016 12:29:25 +0200 Subject: erts: Fix missing type in doc for trace_pattern --- erts/doc/src/erlang.xml | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index f88d05cdc3..f42923a009 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -9137,6 +9137,7 @@ timestamp() -> Sets trace pattern for message sending. +

Sets trace pattern for message sending. Must be combined with @@ -9200,6 +9201,7 @@ timestamp() -> Sets trace pattern for tracing of message receiving. +

Sets trace pattern for message receiving. -- cgit v1.2.3 From 6197aa2498bcb35e331c9112a70635f55047bf26 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 2 May 2016 18:40:10 +0200 Subject: erts: Fix bug in trace_pattern for 'on_load' 'on_load' is a call trace. --- erts/emulator/beam/erl_db_util.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 051c107d1f..95b1cd0148 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1002,10 +1002,14 @@ Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA) { Binary *bin; Uint sz; Eterm *hp; - Uint flags = DCOMP_TRACE; + Uint flags; - if (is_tuple(MFA)) flags |= DCOMP_CALL_TRACE; - if (MFA != am_receive) flags |= DCOMP_ALLOW_TRACE_OPS; + switch (MFA) { + case am_receive: flags = DCOMP_TRACE; break; + case am_send: flags = DCOMP_TRACE | DCOMP_ALLOW_TRACE_OPS; break; + default: + flags = DCOMP_TRACE | DCOMP_CALL_TRACE | DCOMP_ALLOW_TRACE_OPS; + } bin = db_match_set_compile(p, matchexpr, flags); if (bin != NULL) { -- cgit v1.2.3 From 54172674e71caf7da7a0b069c9bd92543e4f705d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 4 May 2016 14:45:05 +0200 Subject: erts: Add send and 'receive' to trace_info/2 to obtain match specs --- erts/doc/src/erlang.xml | 33 ++++++++++++++++++++----------- erts/emulator/beam/erl_bif_trace.c | 40 ++++++++++++++++++++++++++++++++++++++ erts/emulator/test/trace_SUITE.erl | 5 ++++- erts/preloaded/src/erlang.erl | 8 ++++---- 4 files changed, 70 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index f42923a009..5af4f0bd66 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -9015,16 +9015,16 @@ timestamp() -> -

Returns trace information about a port, process or function.

-

To get information about a port or process, - PidPortOrFunc is to +

Returns trace information about a port, process, function or event.

+

To get information about a port or process, + PidPortFuncEvent is to be a process identifier (pid), port identifier or one of the atoms new, new_processes, new_ports. The atom new or new_processes means that the default trace state for processes to be created is returned. The atom new_ports means that the default trace state for ports to be created is returned.

-

The following Items are valid:

+

The following Items are valid for ports and processes:

flags @@ -9048,12 +9048,15 @@ timestamp() -> value is [].

-

To get information about a function, PidPortOrFunc is to +

To get information about a function, PidPortFuncEvent is to be the three-element tuple {Module, Function, Arity} or the atom on_load. No wild cards are allowed. Returns undefined if the function does not exist, or - false if the function is not traced.

-

The following Items are valid::

+ false if the function is not traced. If PidPortFuncEvent + is on_load, the information returned refers to + the default value for code that will be loaded.

+ +

The following Items are valid for functions:

traced @@ -9112,13 +9115,21 @@ timestamp() -> is active for this function.

+

To get information about an event, PidPortFuncEvent is to + be one of the atoms send or 'receive'.

+

The only valid Item for events is:

+ + match_spec + +

Returns the match specification for this event, if it + has one, or true if no match specification has been + set.

+
+

The return value is {Item, Value}, where Value is the requested information as described earlier. If a pid for a dead process was given, or the name of a non-existing function, Value is undefined.

-

If PidPortOrFunc is on_load, the information - returned refers to the default value for code that will be - loaded.

@@ -9237,7 +9248,7 @@ timestamp() ->
false -

Disables tracing for all sent messages. +

Disables tracing for all received messages. Any match specification is removed.

diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index ffc0afda58..b65c0e303f 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -81,6 +81,8 @@ static void new_seq_trace_token(Process* p); /* help func for seq_trace_2*/ static Eterm trace_info_pid(Process* p, Eterm pid_spec, Eterm key); static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key); static Eterm trace_info_on_load(Process* p, Eterm key); +static Eterm trace_info_event(Process* p, Eterm event, Eterm key); + static void reset_bif_trace(void); static void setup_bif_trace(void); @@ -814,6 +816,8 @@ Eterm trace_info_2(BIF_ALIST_2) if (What == am_on_load) { res = trace_info_on_load(p, Key); + } else if (What == am_send || What == am_receive) { + res = trace_info_event(p, What, Key); } else if (is_atom(What) || is_pid(What) || is_port(What)) { res = trace_info_pid(p, What, Key); } else if (is_tuple(What)) { @@ -1303,6 +1307,42 @@ trace_info_on_load(Process* p, Eterm key) } } +static Eterm +trace_info_event(Process* p, Eterm event, Eterm key) +{ + ErtsTracingEvent* te; + Eterm retval; + Eterm* hp; + + switch (event) { + case am_send: te = erts_send_tracing; break; + case am_receive: te = erts_receive_tracing; break; + default: + goto error; + } + + if (key != am_match_spec) + goto error; + + te = &te[erts_active_bp_ix()]; + + if (te->on) { + if (!te->match_spec) + retval = am_true; + else + retval = copy_object(MatchSetGetSource(te->match_spec), p); + } + else + retval = am_false; + + hp = HAlloc(p, 3); + return TUPLE2(hp, key, retval); + + error: + BIF_ERROR(p, BADARG); +} + + #undef FUNC_TRACE_NOEXIST #undef FUNC_TRACE_UNTRACED #undef FUNC_TRACE_GLOBAL_TRACE diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index 1930332691..3d7cb41fc0 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -495,7 +495,10 @@ send_trace(Config) when is_list(Config) -> ok. set_trace_pattern(_, no, _) -> 0; -set_trace_pattern(MSA, Pat, Flg) -> erlang:trace_pattern(MSA, Pat, Flg). +set_trace_pattern(MFA, Pat, Flg) -> + R = erlang:trace_pattern(MFA, Pat, Flg), + {match_spec, Pat} = erlang:trace_info(MFA, match_spec), + R. %% Test trace(Pid, How, [procs]). procs_trace(Config) when is_list(Config) -> diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index d09958032b..90fd536b15 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1749,16 +1749,16 @@ trace_delivered(_Tracee) -> erlang:nif_error(undefined). %% trace_info/2 --spec erlang:trace_info(PidPortOrFunc, Item) -> Res when - PidPortOrFunc :: pid() | port() | new | new_processes | new_ports - | {Module, Function, Arity} | on_load, +-spec erlang:trace_info(PidPortFuncEvent, Item) -> Res when + PidPortFuncEvent :: pid() | port() | new | new_processes | new_ports + | {Module, Function, Arity} | on_load | send | 'receive', Module :: module(), Function :: atom(), Arity :: arity(), Item :: flags | tracer | traced | match_spec | meta | meta_match_spec | call_count | call_time | all, Res :: trace_info_return(). -trace_info(_PidPortOrFunc, _Item) -> +trace_info(_PidPortFuncEvent, _Item) -> erlang:nif_error(undefined). %% trunc/1 -- cgit v1.2.3 From 0371d8e6fb2545843e73125eb2d0edaf8fd3e778 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 4 May 2016 21:03:16 +0200 Subject: erts: Refactor ERTS_ALC_INFO_A_* constants as an enum and replace _MAX with _END --- erts/emulator/beam/erl_alloc.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index f571515def..40b36e4b6c 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -137,10 +137,12 @@ static ErtsAllocatorState_t exec_alloc_state; #endif static ErtsAllocatorState_t test_alloc_state; -#define ERTS_ALC_INFO_A_ALLOC_UTIL (ERTS_ALC_A_MAX + 1) -#define ERTS_ALC_INFO_A_MSEG_ALLOC (ERTS_ALC_A_MAX + 2) -#define ERTS_ALC_INFO_A_ERTS_MMAP (ERTS_ALC_A_MAX + 3) -#define ERTS_ALC_INFO_A_MAX ERTS_ALC_INFO_A_ERTS_MMAP +enum { + ERTS_ALC_INFO_A_ALLOC_UTIL = ERTS_ALC_A_MAX + 1, + ERTS_ALC_INFO_A_MSEG_ALLOC, + ERTS_ALC_INFO_A_ERTS_MMAP, + ERTS_ALC_INFO_A_END +}; typedef struct { erts_smp_atomic32_t refc; @@ -150,7 +152,7 @@ typedef struct { Process *proc; Eterm ref; Eterm ref_heap[REF_THING_SIZE]; - int allocs[ERTS_ALC_INFO_A_MAX - ERTS_ALC_A_MIN + 1 + 1]; + int allocs[ERTS_ALC_INFO_A_END - ERTS_ALC_A_MIN + 1]; } ErtsAllocInfoReq; ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(aireq, @@ -3359,7 +3361,7 @@ erts_request_alloc_info(struct process *c_p, int internal) { ErtsAllocInfoReq *air = aireq_alloc(); - Eterm req_ai[ERTS_ALC_INFO_A_MAX+1] = {0}; + Eterm req_ai[ERTS_ALC_INFO_A_END] = {0}; Eterm alist; Eterm *hp; int airix = 0, ai; -- cgit v1.2.3 From c93da5e4fb98f92b9693586dda2ce1bf2d05ade1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 4 May 2016 21:13:08 +0200 Subject: erts: Show exec_alloc as disabled when not used --- erts/emulator/beam/erl_alloc.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 40b36e4b6c..d7919b9db0 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -141,6 +141,7 @@ enum { ERTS_ALC_INFO_A_ALLOC_UTIL = ERTS_ALC_A_MAX + 1, ERTS_ALC_INFO_A_MSEG_ALLOC, ERTS_ALC_INFO_A_ERTS_MMAP, + ERTS_ALC_INFO_A_DISABLED_EXEC, /* fake a disabled "exec_alloc" */ ERTS_ALC_INFO_A_END }; @@ -3262,6 +3263,12 @@ reply_alloc_info(void *vair) am_false); #endif break; +#ifndef ERTS_ALC_A_EXEC + case ERTS_ALC_INFO_A_DISABLED_EXEC: + alloc_atom = erts_bld_atom(hpp, szp, "exec_alloc"); + ainfo = erts_bld_tuple2(hpp, szp, alloc_atom, am_false); + break; +#endif default: alloc_atom = erts_bld_atom(hpp, szp, (char *) ERTS_ALC_A2AD(ai)); @@ -3289,7 +3296,8 @@ reply_alloc_info(void *vair) switch (ai) { case ERTS_ALC_A_SYSTEM: case ERTS_ALC_INFO_A_ALLOC_UTIL: - case ERTS_ALC_INFO_A_ERTS_MMAP: + case ERTS_ALC_INFO_A_ERTS_MMAP: + case ERTS_ALC_INFO_A_DISABLED_EXEC: break; case ERTS_ALC_INFO_A_MSEG_ALLOC: #if HAVE_ERTS_MSEG && defined(ERTS_SMP) @@ -3401,6 +3409,12 @@ erts_request_alloc_info(struct process *c_p, ai = ERTS_ALC_INFO_A_ERTS_MMAP; goto save_alloc; } +#ifndef ERTS_ALC_A_EXEC + if (erts_is_atom_str("exec_alloc", alloc, 0)) { + ai = ERTS_ALC_INFO_A_DISABLED_EXEC; + goto save_alloc; + } +#endif if (erts_is_atom_str("alloc_util", alloc, 0)) { ai = ERTS_ALC_INFO_A_ALLOC_UTIL; save_alloc: -- cgit v1.2.3 From 98855709622835602b4c0a2d37e3f06ceb003af7 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 24 Apr 2016 10:58:11 +0200 Subject: erts_new_mso_binary(): do not truncate len --- erts/emulator/beam/binary.c | 2 +- erts/emulator/beam/global.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index cfd8fbe2f5..071a356260 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -117,7 +117,7 @@ new_binary(Process *p, byte *buf, Uint len) * When heap binary is not desired... */ -Eterm erts_new_mso_binary(Process *p, byte *buf, int len) +Eterm erts_new_mso_binary(Process *p, byte *buf, Uint len) { ProcBin* pb; Binary* bptr; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index a7bc990deb..c8cf7c6191 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -946,7 +946,7 @@ ERTS_GLB_INLINE Eterm erts_equeue_get(ErtsEQueue *q) { void erts_emasculate_writable_binary(ProcBin* pb); Eterm erts_new_heap_binary(Process *p, byte *buf, int len, byte** datap); -Eterm erts_new_mso_binary(Process*, byte*, int); +Eterm erts_new_mso_binary(Process*, byte*, Uint); Eterm new_binary(Process*, byte*, Uint); Eterm erts_realloc_binary(Eterm bin, size_t size); -- cgit v1.2.3 From d053fed5d3f546b2f1f19e16a60c6330f8526c95 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Tue, 26 Apr 2016 09:13:45 +0200 Subject: do not limit heap fragments to 4 giga-words - struct erl_heap_fragment: use Uint for sizes - erl_nif.c: use size_t for sizes - erts_heap_frag_shrink(): remove now redundant cast --- erts/emulator/beam/erl_message.h | 4 ++-- erts/emulator/beam/erl_nif.c | 10 +++++----- erts/emulator/beam/erl_process.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 608cf552a2..60cf9a24e1 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -112,8 +112,8 @@ typedef struct erl_heap_fragment ErlHeapFragment; struct erl_heap_fragment { ErlHeapFragment* next; /* Next heap fragment */ ErlOffHeap off_heap; /* Offset heap data. */ - unsigned alloc_size; /* Size in (half)words of mem */ - unsigned used_size; /* With terms to be moved to heap by GC */ + Uint alloc_size; /* Size in (half)words of mem */ + Uint used_size; /* With terms to be moved to heap by GC */ Eterm mem[1]; /* Data */ }; diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 941f44b9ec..d52bfbc5d9 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -79,9 +79,9 @@ void dtrace_nifenv_str(ErlNifEnv *, char *); #endif #define MIN_HEAP_FRAG_SZ 200 -static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp); +static Eterm* alloc_heap_heavy(ErlNifEnv* env, size_t need, Eterm* hp); -static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, unsigned need) +static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, size_t need) { Eterm* hp = env->hp; env->hp += need; @@ -91,7 +91,7 @@ static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, unsigned need) return alloc_heap_heavy(env, need, hp); } -static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp) +static Eterm* alloc_heap_heavy(ErlNifEnv* env, size_t need, Eterm* hp) { env->hp = hp; if (env->heap_frag == NULL) { @@ -112,7 +112,7 @@ static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp) } #if SIZEOF_LONG != ERTS_SIZEOF_ETERM -static ERTS_INLINE void ensure_heap(ErlNifEnv* env, unsigned may_need) +static ERTS_INLINE void ensure_heap(ErlNifEnv* env, size_t may_need) { if (env->hp + may_need > env->hp_end) { alloc_heap_heavy(env, may_need, env->hp); @@ -1010,7 +1010,7 @@ Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, Eterm orig; Uint offset, bit_offset, bit_size; #ifdef DEBUG - unsigned src_size; + size_t src_size; ASSERT(is_binary(bin_term)); src_size = binary_size(bin_term); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 1c01d705a7..0f69ddeed8 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1291,7 +1291,7 @@ ERTS_GLB_INLINE void erts_heap_frag_shrink(Process* p, Eterm* hp) { ErlHeapFragment* hf = MBUF(p); - ASSERT(hf!=NULL && (hp - hf->mem < (unsigned long)hf->alloc_size)); + ASSERT(hf!=NULL && (hp - hf->mem < hf->alloc_size)); hf->used_size = hp - hf->mem; } -- cgit v1.2.3 From 325f700a35ecf3e50b8d679f504edbdd5a4ec1ab Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 24 Apr 2016 11:17:19 +0200 Subject: erl_unicode.c: fix integer truncation problems - use Sint for 'left' and Uint for 'pos' - cost_to_proc(): adjust types, remove unused return value - copy_utf8_bin(): return Uint - characters_to_utf8_trap(): remove harmful cast - unicode_characters_to_binary_2(): correct types in debug code - erts_convert_filename_to_encoding(): remove useless cast --- erts/emulator/beam/erl_unicode.c | 58 +++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 28 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index 3c3536c021..bd5e1482fb 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -55,7 +55,7 @@ static BIF_RETTYPE finalize_list_to_list(Process *p, Uint num_processed_bytes, Uint num_bytes_to_process, Uint num_resulting_chars, - int state, int left, + int state, Sint left, Eterm tail); static BIF_RETTYPE characters_to_utf8_trap(BIF_ALIST_3); static BIF_RETTYPE characters_to_list_trap_1(BIF_ALIST_3); @@ -173,12 +173,13 @@ static ERTS_INLINE int allowed_iterations(Process *p) else return tmp; } -static ERTS_INLINE int cost_to_proc(Process *p, int cost) + +static ERTS_INLINE void cost_to_proc(Process *p, Sint cost) { - int x = (cost / LOOP_FACTOR); + Sint x = (cost / LOOP_FACTOR); BUMP_REDS(p,x); - return x; } + static ERTS_INLINE int simple_loops_to_common(int cost) { int factor = (LOOP_FACTOR_SIMPLE / LOOP_FACTOR); @@ -243,14 +244,15 @@ static int utf8_len(byte first) return -1; } -static int copy_utf8_bin(byte *target, byte *source, Uint size, - byte *leftover, int *num_leftovers, - byte **err_pos, Uint *characters) { - int copied = 0; +static Uint copy_utf8_bin(byte *target, byte *source, Uint size, + byte *leftover, int *num_leftovers, + byte **err_pos, Uint *characters) +{ + Uint copied = 0; if (leftover != NULL && *num_leftovers) { int need = utf8_len(leftover[0]); int from_source = need - (*num_leftovers); - int c; + Uint c; byte *tmp_err_pos = NULL; ASSERT(need > 0); ASSERT(from_source > 0); @@ -502,8 +504,8 @@ L_Again: /* Restart with sublist, old listend was pushed on stack */ } -static Eterm do_build_utf8(Process *p, Eterm ioterm, int *left, int latin1, - byte *target, int *pos, Uint *characters, int *err, +static Eterm do_build_utf8(Process *p, Eterm ioterm, Sint *left, int latin1, + byte *target, Uint *pos, Uint *characters, int *err, byte *leftover, int *num_leftovers) { int c; @@ -573,7 +575,7 @@ static Eterm do_build_utf8(Process *p, Eterm ioterm, int *left, int latin1, } if (!latin1) { - int num; + Uint num; byte *err_pos = NULL; num = copy_utf8_bin(target + (*pos), bytes, size, leftover, num_leftovers,&err_pos,characters); @@ -804,7 +806,7 @@ static int check_leftovers(byte *source, int size) -static BIF_RETTYPE build_utf8_return(Process *p,Eterm bin,int pos, +static BIF_RETTYPE build_utf8_return(Process *p,Eterm bin,Uint pos, Eterm rest_term,int err, byte *leftover,int num_leftovers,Eterm latin1) { @@ -859,8 +861,8 @@ static BIF_RETTYPE characters_to_utf8_trap(BIF_ALIST_3) #endif byte* bytes; Eterm rest_term; - int left, sleft; - int pos; + Sint left, sleft; + Uint pos; int err; byte leftover[4]; /* used for temp buffer too, otherwise 3 bytes would have been enough */ @@ -874,7 +876,7 @@ static BIF_RETTYPE characters_to_utf8_trap(BIF_ALIST_3) real_bin = binary_val(BIF_ARG_1); ASSERT(*real_bin == HEADER_PROC_BIN); #endif - pos = (int) binary_size(BIF_ARG_1); + pos = binary_size(BIF_ARG_1); bytes = binary_bytes(BIF_ARG_1); sleft = left = allowed_iterations(BIF_P); err = 0; @@ -934,9 +936,9 @@ BIF_RETTYPE unicode_characters_to_binary_2(BIF_ALIST_2) int latin1; Eterm bin; byte *bytes; - int pos; + Uint pos; int err; - int left, sleft; + Sint left, sleft; Eterm rest_term, subject; byte leftover[4]; /* used for temp buffer too, o therwise 3 bytes would have been enough */ @@ -999,7 +1001,7 @@ BIF_RETTYPE unicode_characters_to_binary_2(BIF_ALIST_2) byte *t = NULL; Uint sz = binary_size(bin); byte *by = erts_get_aligned_binary_bytes(bin,&t); - int i; + Uint i; erts_printf("<<"); for (i = 0;i < sz; ++i) { erts_printf((i == sz -1) ? "0x%X" : "0x%X, ", (unsigned) by[i]); @@ -1007,7 +1009,7 @@ BIF_RETTYPE unicode_characters_to_binary_2(BIF_ALIST_2) erts_printf(">>: "); erts_free_aligned_binary_bytes(t); } - erts_printf("%d - %d = %d\n",sleft,left,sleft - left); + erts_printf("%ld - %ld = %ld\n", sleft, left, sleft - left); } #endif cost_to_proc(BIF_P, sleft - left); @@ -1015,10 +1017,10 @@ BIF_RETTYPE unicode_characters_to_binary_2(BIF_ALIST_2) leftover,num_leftovers,BIF_ARG_2); } -static BIF_RETTYPE build_list_return(Process *p, byte *bytes, int pos, Uint characters, +static BIF_RETTYPE build_list_return(Process *p, byte *bytes, Uint pos, Uint characters, Eterm rest_term, int err, byte *leftover, int num_leftovers, - Eterm latin1, int left) + Eterm latin1, Sint left) { Eterm *hp; @@ -1070,11 +1072,11 @@ static BIF_RETTYPE characters_to_list_trap_1(BIF_ALIST_3) { RestartContext *rc; byte* bytes; - int pos; + Uint pos; Uint characters; int err; Eterm rest_term; - int left, sleft; + Sint left, sleft; int latin1 = 0; byte leftover[4]; /* used for temp buffer too, @@ -1107,9 +1109,9 @@ BIF_RETTYPE unicode_characters_to_list_2(BIF_ALIST_2) int latin1; Uint characters = 0; byte *bytes; - int pos; + Uint pos; int err; - int left, sleft; + Sint left, sleft; Eterm rest_term; byte leftover[4]; /* used for temp buffer too, o therwise 3 bytes would have been enough */ @@ -1541,7 +1543,7 @@ static BIF_RETTYPE finalize_list_to_list(Process *p, Uint num_processed_bytes, Uint num_bytes_to_process, Uint num_resulting_chars, - int state, int left, + int state, Sint left, Eterm tail) { Uint num_built; /* characters */ @@ -2016,7 +2018,7 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu ++need; } if (used) - *used = (Sint) need; + *used = need; if (need+extra > statbuf_size) { name_buf = (char *) erts_alloc(alloc_type, need+extra); } else { -- cgit v1.2.3 From ab950a81bc3361322ce955703f468f314c05e546 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Thu, 31 Mar 2016 15:55:46 +0200 Subject: erts: Remove some attributes from the abstract format doc Some wild attributes are no longer mentioned separately. --- erts/doc/src/absform.xml | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 6d6ba224a0..bfabb7f042 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -68,22 +68,12 @@ If D is a module declaration consisting of the forms F_1, ..., F_k, then Rep(D) = [Rep(F_1), ..., Rep(F_k)]. - If F is an attribute -behavior(Behavior), then - Rep(F) = {attribute,LINE,behavior,Behavior}. - If F is an attribute -behaviour(Behaviour), then - Rep(F) = {attribute,LINE,behaviour,Behaviour}. - If F is an attribute -compile(Options), then - Rep(F) = {attribute,LINE,compile,Options}. If F is an attribute -export([Fun_1/A_1, ..., Fun_k/A_k]), then Rep(F) = {attribute,LINE,export,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}. - If F is an attribute -export_type([Type_1/A_1, ..., Type_k/A_k]), then - Rep(F) = {attribute,LINE,export_type,[{Type_1,A_1}, ..., {Type_k,A_k}]}. If F is an attribute -import(Mod,[Fun_1/A_1, ..., Fun_k/A_k]), then Rep(F) = {attribute,LINE,import,{Mod,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}}. If F is an attribute -module(Mod), then Rep(F) = {attribute,LINE,module,Mod}. - If F is an attribute -optional_callbacks([Fun_1/A_1, ..., Fun_k/A_k]), then - Rep(F) = {attribute,LINE,optional_callbacks,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}. If F is an attribute -file(File,Line), then Rep(F) = {attribute,LINE,file,{File,Line}}. If F is a function declaration -- cgit v1.2.3 From e146a3eec5a2d384260aa8829777c89eaab09cbd Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 21 Apr 2016 18:36:45 +0200 Subject: erts: Implement max_heap_size process flag The max_heap_size process flag can be used to limit the growth of a process heap by killing it before it becomes too large to handle. It is possible to set the maximum using the `erl +hmax` option, `system_flag(max_heap_size, ...)`, `spawn_opt(Fun, [{max_heap_size, ...}])` and `process_flag(max_heap_size, ...)`. It is possible to configure the behaviour of the process when the maximum heap size is reached. The process may be sent an untrappable exit signal with reason kill and/or send an error_logger message with details on the process state. A new trace event called gc_max_heap_size is also triggered for the garbage_collection trace flag when the heap grows larger than the configured size. If kill and error_logger are disabled, it is still possible to see that the maximum has been reached by doing garbage collection tracing on the process. The heap size is defined as the sum of the heap memory that the process is currently using. This includes all generational heaps, the stack, any messages that are considered to be part of the heap and any extra memory the garbage collector may need during collection. In the current implementation this means that when a process is set using on_heap message queue data mode, the messages that are in the internal message queue are counted towards this value. For off_heap, only matched messages count towards the size of the heap. For mixed, it depends on race conditions within the VM whether a message is part of the heap or not. Below is an example run of the new behaviour: Eshell V8.0 (abort with ^G) 1> f(P),P = spawn_opt(fun() -> receive ok -> ok end end, [{max_heap_size, 512}]). <0.60.0> 2> erlang:trace(P, true, [garbage_collection, procs]). 1 3> [P ! lists:duplicate(M,M) || M <- lists:seq(1,15)],ok. ok 4> =ERROR REPORT==== 26-Apr-2016::16:25:10 === Process: <0.60.0> Context: maximum heap size reached Max heap size: 512 Total heap size: 723 Kill: true Error Logger: true GC Info: [{old_heap_block_size,0}, {heap_block_size,609}, {mbuf_size,145}, {recent_size,0}, {stack_size,9}, {old_heap_size,0}, {heap_size,211}, {bin_vheap_size,0}, {bin_vheap_block_size,46422}, {bin_old_vheap_size,0}, {bin_old_vheap_block_size,46422}] flush(). Shell got {trace,<0.60.0>,gc_start, [{old_heap_block_size,0}, {heap_block_size,233}, {mbuf_size,145}, {recent_size,0}, {stack_size,9}, {old_heap_size,0}, {heap_size,211}, {bin_vheap_size,0}, {bin_vheap_block_size,46422}, {bin_old_vheap_size,0}, {bin_old_vheap_block_size,46422}]} Shell got {trace,<0.60.0>,gc_max_heap_size, [{old_heap_block_size,0}, {heap_block_size,609}, {mbuf_size,145}, {recent_size,0}, {stack_size,9}, {old_heap_size,0}, {heap_size,211}, {bin_vheap_size,0}, {bin_vheap_block_size,46422}, {bin_old_vheap_size,0}, {bin_old_vheap_block_size,46422}]} Shell got {trace,<0.60.0>,exit,killed} --- erts/doc/src/erl.xml | 28 ++++ erts/doc/src/erlang.xml | 294 +++++++++++++++++++++++++++-------- erts/emulator/beam/atom.names | 2 + erts/emulator/beam/beam_emu.c | 43 +++++ erts/emulator/beam/bif.c | 52 +++++++ erts/emulator/beam/erl_bif_info.c | 49 +++++- erts/emulator/beam/erl_gc.c | 246 +++++++++++++++++++++++++++-- erts/emulator/beam/erl_gc.h | 4 +- erts/emulator/beam/erl_init.c | 54 ++++++- erts/emulator/beam/erl_process.c | 7 +- erts/emulator/beam/erl_process.h | 12 ++ erts/emulator/beam/erl_trace.c | 21 +-- erts/emulator/beam/erl_trace.h | 2 +- erts/emulator/beam/erl_vm.h | 3 + erts/emulator/test/process_SUITE.erl | 186 ++++++++++++++++++++-- erts/etc/common/erlexec.c | 3 + erts/preloaded/src/erlang.erl | 17 ++ 17 files changed, 906 insertions(+), 117 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index c499fc8081..1bbde7f1e0 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -627,6 +627,34 @@

Sets the default binary virtual heap size of processes to the size .

+ + + +

Sets the default maximum heap size of processes to the size + . If +hmax is not given, the default is 0 + which means that no maximum heap size is used. + For more information, see the documentation of + + process_flag(max_heap_size, MaxHeapSize).

+
+ + + +

Sets whether to send an error logger message for processes that reach + the maximum heap size or not. If +hmaxel is not given, the default is true. + For more information, see the documentation of + + process_flag(max_heap_size, MaxHeapSize).

+
+ + + +

Sets whether to kill processes that reach the maximum heap size or not. If + +hmaxk is not given, the default is true. For more information, + see the documentation of + + process_flag(max_heap_size, MaxHeapSize).

+

Sets the initial process dictionary size of processes to the size diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index bf30cc7928..3930e5e11d 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4322,6 +4322,7 @@ os_prompt% + Sets process flag min_heap_size for the calling process. @@ -4340,9 +4341,95 @@ os_prompt%

Returns the old value of the flag.

- + + + Sets process flag max_heap_size for the calling process. + +

+ This flag sets the maximum heap size for the calling process. + If MaxHeapSize is an integer, the system default + values for kill and error_logger are used. + + size + +

+ The maximum size in words of the process. If set to zero, the + heap size limit is disabled. Badarg will be thrown if the value is + smaller than + min_heap_size. + The size check is only done when a garbage collection is triggered. +

+

+ size is the entire heap of the process when garbage collection + is triggered, this includes all generational heaps, the process stack, + any + messages that are considered to be part of the heap and any + extra memory that the garbage collector needs during collection. +

+

+ size is the same as can be retrieved using + + erlang:process_info(Pid, total_heap_size), + or by adding heap_block_size, old_heap_block_size + and mbuf_size from + erlang:process_info(Pid, garbage_collection_info). +

+
+ kill + +

+ When set to true the runtime system will send an + untrappable exit signal with reason kill to the process + if the maximum heap size is reached. The garbage collection + that triggered the kill will not be completed, instead the + process will exit as soon as is possible. When set to false + no exit signal will be sent to the process, instead it will + continue executing. +

+

+ If kill is not defined in the map + the system default will be used. The default system default + is true. It can be changed by either the erl + +hmaxk option, + or + erlang:system_flag(max_heap_size, MaxHeapSize). +

+
+ error_logger + +

+ When set to true the runtime system will send a + message to the current error_logger + containing details about the process when the maximum + heap size is reached. One error_logger report will + be sent each time the limit is reached. +

+

+ If error_logger is not defined in the map the system + default will be used. The default system default is true. + It can be changed by either the erl +hmaxel + option, or + erlang:system_flag(max_heap_size, MaxHeapSize). +

+
+

+ The heap size of a process is quite hard to predict, especially the + amount of memory that is used during the garbage collection. When + contemplating using this option, it is recommended to first run + it in production with kill set to false and inspect + the error_logger reports to see what the normal peak sizes + of the processes in the system is and then tune the value + accordingly. +

+ +

+ + + + + Set process flag message_queue_data for the calling process @@ -4392,7 +4479,7 @@ os_prompt% - + Sets process flag priority for the calling process. @@ -4466,7 +4553,7 @@ os_prompt% - + Sets process flag save_calls for the calling process.

N must be an integer in the interval 0..10000. @@ -4497,7 +4584,7 @@ os_prompt% - + Sets process flag sensitive for the calling process.

Sets or clears flag sensitive for the current process. @@ -4551,6 +4638,7 @@ os_prompt% +

Returns a list containing InfoTuples with @@ -4604,6 +4692,7 @@ os_prompt% +

Returns information about the process identified by @@ -4696,6 +4785,7 @@ os_prompt% The content of GCInfo can be changed without prior notice.

+ {garbage_collection_info, GCInfo}

GCInfo is a list containing miscellaneous @@ -4875,6 +4965,7 @@ os_prompt% total suspend count on Suspendee, only the parts contributed by Pid.

+ {total_heap_size, Size}

Size is the total size, in words, of all heap @@ -5569,6 +5660,7 @@ true Creates a new process with a fun as entry point. + @@ -5586,6 +5678,7 @@ true Creates a new process with a fun as entry point on a given node. + @@ -5602,6 +5695,7 @@ true Creates a new process with a function as entry point. + @@ -5705,6 +5799,16 @@ true fine-tuning an application and to measure the execution time with various VSize values.

+ {max_heap_size, Size} + +

Sets the max_heap_size process flag. The default + max_heap_size is determined by the + +hmax erl + command line argument. For more information, see the + documentation of + process_flag(max_heap_size, + Size).

+
{message_queue_data, MQD}

Sets the state of the message_queue_data process @@ -5725,6 +5829,7 @@ true Creates a new process with a function as entry point on a given node. + @@ -6506,8 +6611,25 @@ ok + + + Sets system flag max_heap_size + +

+ Sets the default maximum heap size settings for processes. + The size is given in words. The new max_heap_size + effects only processes spawned efter the change has been made. + max_heap_size can be set for individual processes using + spawn_opt/N or + process_flag/2.

+

Returns the old value of the flag.

+
+
+ + + Sets system flag multi_scheduling.

@@ -6557,7 +6679,7 @@ ok - + Sets system flag scheduler_bind_type. @@ -6675,7 +6797,7 @@ ok - + Sets system flag scheduler_wall_time.

Turns on or off scheduler wall time measurements.

@@ -6685,7 +6807,7 @@ ok
- + Sets system flag schedulers_online.

@@ -6710,7 +6832,7 @@ ok - + Sets system flag trace_control_word.

Sets the value of the node trace control word to @@ -6724,7 +6846,7 @@ ok - + Finalize the Time Offset

@@ -6990,6 +7112,81 @@ ok + + + + + + + + + + Information about the default process heap settings. + + + fullsweep_after + +

Returns {fullsweep_after, integer() >= 0}, which is + the fullsweep_after garbage collection setting used + by default. For more information, see + garbage_collection described in the following.

+ + garbage_collection + +

Returns a list describing the default garbage collection + settings. A process spawned on the local node by a + spawn or spawn_link uses these + garbage collection settings. The default settings can be + changed by using + system_flag/2. + spawn_opt/4 + can spawn a process that does not use the default + settings.

+
+ max_heap_size + +

Returns {max_heap_size, MaxHeapSize}, + where MaxHeapSize is the current + system-wide max heap size settings for spawned processes. + This setting can be set using the erl command line + flags +hmax, + +hmaxk and + +hmaxel. It can + also be changed at run-time using + + erlang:system_flag(max_heap_size, MaxHeapSize). + For more details about the max_heap_size process flag + see + process_flag(max_heap_size, MaxHeapSize). +

+
+ min_heap_size + +

Returns {min_heap_size, MinHeapSize}, + where MinHeapSize is the current + system-wide minimum heap size for spawned processes.

+
+ message_queue_data + +

Returns the default value of the message_queue_data + process flag which is either off_heap, on_heap, or mixed. + This default is set by the erl command line argument + +hmqd. For more information on the + message_queue_data process flag, see documentation of + process_flag(message_queue_data, + MQD).

+
+ min_bin_vheap_size + +

Returns {min_bin_vheap_size, + MinBinVHeapSize}, where + MinBinVHeapSize is the current system-wide + minimum binary virtual heap size for spawned processes.

+
+ +
+
+ @@ -7010,8 +7207,6 @@ ok - - @@ -7019,10 +7214,6 @@ ok - - - - @@ -7052,6 +7243,7 @@ ok + Information about the system.

Returns various information about the current system @@ -7288,25 +7480,6 @@ ok ERL_MAX_ETS_TABLES before starting the Erlang runtime system.

- fullsweep_after - -

Returns {fullsweep_after, integer() >= 0}, which is - the fullsweep_after garbage collection setting used - by default. For more information, see - garbage_collection described in the following.

-
- garbage_collection - -

Returns a list describing the default garbage collection - settings. A process spawned on the local node by a - spawn or spawn_link uses these - garbage collection settings. The default settings can be - changed by using - system_flag/2. - spawn_opt/4 - can spawn a process that does not use the default - settings.

-
heap_sizes

Returns a list of integers representing valid heap sizes @@ -7381,29 +7554,6 @@ ok

Returns a string containing the Erlang machine name.

- min_heap_size - -

Returns {min_heap_size, MinHeapSize}, - where MinHeapSize is the current - system-wide minimum heap size for spawned processes.

-
- message_queue_data - -

Returns the default value of the message_queue_data - process flag which is either off_heap, on_heap, or mixed. - This default is set by the erl command line argument - +hmqd. For more information on the - message_queue_data process flag, see documentation of - process_flag(message_queue_data, - MQD).

-
- min_bin_vheap_size - -

Returns {min_bin_vheap_size, - MinBinVHeapSize}, where - MinBinVHeapSize is the current system-wide - minimum binary virtual heap size for spawned processes.

-
modified_timing_level

Returns the modified timing-level (an integer) if @@ -8028,12 +8178,13 @@ ok GcPid and Info are the same as for long_gc earlier, except that the tuple tagged with timeout is not present.

-

As of ERTS 5.6, the monitor message is sent - if the sum of the sizes of all memory blocks allocated - for all heap generations is equal to or higher than Size. - Previously the monitor message was sent if the memory block - allocated for the youngest generation was equal to or higher - than Size.

+

The monitor message is sent if the sum of the sizes of + all memory blocks allocated for all heap generations after + a garbage collection is equal to or higher than Size.

+

When a process is killed by + max_heap_size, it is killed before the + garbage collection is complete and thus no large heap message + will be sent.

busy_port @@ -8560,7 +8711,9 @@ timestamp() -> garbage_collection

Traces garbage collections of processes.

-

Message tags: gc_minor_start and gc_minor_end.

+

Message tags: gc_minor_start, + gc_max_heap_size and + gc_minor_end.

timestamp @@ -8928,6 +9081,19 @@ timestamp() ->

All sizes are in words.

+ + + {trace, Pid, gc_max_heap_size, Info} + + +

+ Sent when the max_heap_size + is reached during garbage collection. Info contains the + same kind of list as in message gc_start, + but the sizes reflect the sizes that triggered max_heap_size to + be reached. +

+
{trace, Pid, gc_minor_end, Info} diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 3022c0a99a..8f65e71531 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -275,6 +275,7 @@ atom garbage_collection_info atom gc_end atom gc_major_end atom gc_major_start +atom gc_max_heap_size atom gc_minor_end atom gc_minor_start atom gc_start @@ -366,6 +367,7 @@ atom match_spec atom match_spec_result atom max atom maximum +atom max_heap_size atom max_tables max_processes atom mbuf_size atom md5 diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 99eb26bec5..a88fea3802 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1704,6 +1704,14 @@ void process_main(void) BeamInstr *next; Eterm result; + if (!(FCALLS > 0 || FCALLS > neg_o_reds)) { + /* If we have run out of reductions, we do a context + switch before calling the bif */ + c_p->arity = 2; + c_p->current = NULL; + goto context_switch3; + } + PRE_BIF_SWAPOUT(c_p); c_p->fcalls = FCALLS - 1; result = erl_send(c_p, r(0), x(1)); @@ -2810,6 +2818,15 @@ do { \ BeamInstr *next; ErlHeapFragment *live_hf_end; + + if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) { + /* If we have run out of reductions, we do a context + switch before calling the bif */ + c_p->arity = ((Export *)Arg(0))->code[2]; + c_p->current = NULL; + goto context_switch3; + } + if (ERTS_MSACC_IS_ENABLED_CACHED_X()) { if (GET_BIF_MODULE(Arg(0)) == am_ets) { ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ETS); @@ -3338,10 +3355,19 @@ do { \ context_switch2: /* Entry for fun calls. */ c_p->current = I-3; /* Pointer to Mod, Func, Arity */ + context_switch3: + { Eterm* argp; int i; + if (erts_smp_atomic32_read_nob(&c_p->state) & ERTS_PSFLG_EXITING) { + c_p->i = beam_exit; + c_p->arity = 0; + c_p->current = NULL; + goto do_schedule; + } + /* * Make sure that there is enough room for the argument registers to be saved. */ @@ -3509,6 +3535,14 @@ do { \ BifFunction vbf; ErlHeapFragment *live_hf_end; + if (!((FCALLS - 1) > 0 || (FCALLS - 1) > neg_o_reds)) { + /* If we have run out of reductions, we do a context + switch before calling the nif */ + c_p->arity = I[-1]; + c_p->current = NULL; + goto context_switch3; + } + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF); DTRACE_NIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); @@ -3551,6 +3585,15 @@ do { \ * code[3]: &&apply_bif * code[4]: Function pointer to BIF function */ + + if (!((FCALLS - 1) > 0 || (FCALLS - 1) > neg_o_reds)) { + /* If we have run out of reductions, we do a context + switch before calling the bif */ + c_p->arity = I[-1]; + c_p->current = NULL; + goto context_switch3; + } + if (ERTS_MSACC_IS_ENABLED_CACHED_X()) { if ((Eterm)I[-3] == am_ets) { ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ETS); diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index eed0702688..483c5320d7 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -44,6 +44,7 @@ #include "erl_ptab.h" #include "erl_bits.h" #include "erl_bif_unique.h" +#include "erl_map.h" #include "erl_msacc.h" Export *erts_await_result; @@ -880,6 +881,8 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1) so.flags = erts_default_spo_flags|SPO_USE_ARGS; so.min_heap_size = H_MIN_SIZE; so.min_vheap_size = BIN_VH_MIN_SIZE; + so.max_heap_size = H_MAX_SIZE; + so.max_heap_flags = H_MAX_FLAGS; so.priority = PRIORITY_NORMAL; so.max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs); so.scheduler = 0; @@ -937,6 +940,9 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1) } else { so.min_heap_size = erts_next_heap_size(min_heap_size, 0); } + } else if (arg == am_max_heap_size) { + if (!erts_max_heap_size(val, &so.max_heap_size, &so.max_heap_flags)) + goto error; } else if (arg == am_min_bin_vheap_size && is_small(val)) { Sint min_vheap_size = signed_val(val); if (min_vheap_size < 0) { @@ -970,6 +976,10 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1) goto error; } + if (so.max_heap_size != 0 && so.max_heap_size < so.min_heap_size) { + goto error; + } + /* * Spawn the process. */ @@ -1731,6 +1741,23 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2) } BIF_RET(old_value); } + else if (BIF_ARG_1 == am_max_heap_size) { + Eterm *hp; + Uint sz = 0, max_heap_size, max_heap_flags; + + if (!erts_max_heap_size(BIF_ARG_2, &max_heap_size, &max_heap_flags)) + goto error; + + if ((max_heap_size < MIN_HEAP_SIZE(BIF_P) && max_heap_size != 0)) + goto error; + + erts_max_heap_size_map(MAX_HEAP_SIZE_GET(BIF_P), MAX_HEAP_SIZE_FLAGS_GET(BIF_P), NULL, &sz); + hp = HAlloc(BIF_P, sz); + old_value = erts_max_heap_size_map(MAX_HEAP_SIZE_GET(BIF_P), MAX_HEAP_SIZE_FLAGS_GET(BIF_P), &hp, NULL); + MAX_HEAP_SIZE_SET(BIF_P, max_heap_size); + MAX_HEAP_SIZE_FLAGS_SET(BIF_P, max_heap_flags); + BIF_RET(old_value); + } else if (BIF_ARG_1 == am_message_queue_data) { old_value = erts_change_message_queue_management(BIF_P, BIF_ARG_2); if (is_non_value(old_value)) @@ -4350,6 +4377,31 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); BIF_RET(make_small(oval)); + } else if (BIF_ARG_1 == am_max_heap_size) { + + Eterm *hp, old_value; + Uint sz = 0, max_heap_size, max_heap_flags; + + if (!erts_max_heap_size(BIF_ARG_2, &max_heap_size, &max_heap_flags)) + goto error; + + if (max_heap_size < H_MIN_SIZE && max_heap_size != 0) + goto error; + + erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, NULL, &sz); + hp = HAlloc(BIF_P, sz); + old_value = erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, &hp, NULL); + + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); + + H_MAX_SIZE = max_heap_size; + H_MAX_FLAGS = max_heap_flags; + + erts_smp_thr_progress_unblock(); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + + BIF_RET(old_value); } else if (BIF_ARG_1 == am_display_items) { int oval = display_items; if (!is_small(BIF_ARG_2) || (n = signed_val(BIF_ARG_2)) < 0) { diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 85cadafebc..da480f8fce 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -45,6 +45,7 @@ #include "erl_async.h" #include "erl_thr_progress.h" #include "erl_bif_unique.h" +#include "erl_map.h" #define ERTS_PTAB_WANT_DEBUG_FUNCS__ #include "erl_ptab.h" #ifdef HIPE @@ -594,6 +595,7 @@ static Eterm pi_args[] = { am_suspending, am_min_heap_size, am_min_bin_vheap_size, + am_max_heap_size, am_current_location, am_current_stacktrace, am_message_queue_data, @@ -643,10 +645,11 @@ pi_arg2ix(Eterm arg) case am_suspending: return 26; case am_min_heap_size: return 27; case am_min_bin_vheap_size: return 28; - case am_current_location: return 29; - case am_current_stacktrace: return 30; - case am_message_queue_data: return 31; - case am_garbage_collection_info: return 32; + case am_max_heap_size: return 29; + case am_current_location: return 30; + case am_current_stacktrace: return 31; + case am_message_queue_data: return 32; + case am_garbage_collection_info: return 33; default: return -1; } } @@ -1348,6 +1351,18 @@ process_info_aux(Process *BIF_P, break; } + case am_max_heap_size: { + Uint hsz = 3; + (void) erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp), + MAX_HEAP_SIZE_FLAGS_GET(rp), + NULL, &hsz); + hp = HAlloc(BIF_P, hsz); + res = erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp), + MAX_HEAP_SIZE_FLAGS_GET(rp), + &hp, NULL); + break; + } + case am_total_heap_size: { ErtsMessage *mp; Uint total_heap_size; @@ -1391,8 +1406,12 @@ process_info_aux(Process *BIF_P, case am_garbage_collection: { DECL_AM(minor_gcs); Eterm t; + Uint map_sz = 0; + + erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp), MAX_HEAP_SIZE_FLAGS_GET(rp), NULL, &map_sz); - hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2 + 3+2 + 3); /* last "3" is for outside tuple */ + hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2 + 3+2 + 3+2 + map_sz + 3); + /* last "3" is for outside tuple */ t = TUPLE2(hp, AM_minor_gcs, make_small(GEN_GCS(rp))); hp += 3; res = CONS(hp, t, NIL); hp += 2; @@ -1403,6 +1422,11 @@ process_info_aux(Process *BIF_P, res = CONS(hp, t, res); hp += 2; t = TUPLE2(hp, am_min_bin_vheap_size, make_small(MIN_VHEAP_SIZE(rp))); hp += 3; res = CONS(hp, t, res); hp += 2; + + t = erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp), MAX_HEAP_SIZE_FLAGS_GET(rp), &hp, NULL); + + t = TUPLE2(hp, am_max_heap_size, t); hp += 3; + res = CONS(hp, t, res); hp += 2; break; } @@ -1412,12 +1436,12 @@ process_info_aux(Process *BIF_P, if (rp == BIF_P) { sz += ERTS_PROCESS_GC_INFO_MAX_SIZE; } else { - erts_process_gc_info(rp, &sz, NULL); + erts_process_gc_info(rp, &sz, NULL, 0, 0); sz += 3; } hp = HAlloc(BIF_P, sz); - res = erts_process_gc_info(rp, &actual_sz, &hp); + res = erts_process_gc_info(rp, &actual_sz, &hp, 0, 0); /* We may have some extra space, fill with 0 tuples */ if (actual_sz <= sz - 3) { @@ -2173,7 +2197,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) } else if (BIF_ARG_1 == am_garbage_collection){ Uint val = (Uint) erts_smp_atomic32_read_nob(&erts_max_gen_gcs); Eterm tup; - hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2); + hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2 + 3+2); tup = TUPLE2(hp, am_fullsweep_after, make_small(val)); hp += 3; res = CONS(hp, tup, NIL); hp += 2; @@ -2184,6 +2208,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) tup = TUPLE2(hp, am_min_bin_vheap_size, make_small(BIN_VH_MIN_SIZE)); hp += 3; res = CONS(hp, tup, res); hp += 2; + tup = TUPLE2(hp, am_max_heap_size, make_small(H_MAX_SIZE)); hp += 3; + res = CONS(hp, tup, res); hp += 2; + BIF_RET(res); } else if (BIF_ARG_1 == am_fullsweep_after){ Uint val = (Uint) erts_smp_atomic32_read_nob(&erts_max_gen_gcs); @@ -2194,6 +2221,12 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) hp = HAlloc(BIF_P, 3); res = TUPLE2(hp, am_min_heap_size,make_small(H_MIN_SIZE)); BIF_RET(res); + } else if (BIF_ARG_1 == am_max_heap_size) { + Uint sz = 0; + erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, NULL, &sz); + hp = HAlloc(BIF_P, sz); + res = erts_max_heap_size_map(H_MAX_SIZE, H_MAX_FLAGS, &hp, NULL); + BIF_RET(res); } else if (BIF_ARG_1 == am_min_bin_vheap_size) { hp = HAlloc(BIF_P, 3); res = TUPLE2(hp, am_min_bin_vheap_size,make_small(BIN_VH_MIN_SIZE)); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index bed7e668d7..fa0782867d 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -41,6 +41,7 @@ #endif #include "dtrace-wrapper.h" #include "erl_bif_unique.h" +#include "dist.h" #define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1 #define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20 @@ -146,7 +147,8 @@ static void offset_rootset(Process *p, Sint offs, char* area, Uint area_size, static void offset_off_heap(Process* p, Sint offs, char* area, Uint area_size); static void offset_mqueue(Process *p, Sint offs, char* area, Uint area_size); static void move_msgq_to_heap(Process *p); - +static int reached_max_heap_size(Process *p, Uint total_heap_size, + Uint extra_heap_size, Uint extra_old_heap_size); static void init_gc_info(ErtsGCInfo *gcip); #ifdef HARDDEBUG @@ -577,9 +579,11 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, int need, Eterm* objv, int nobj, int fcalls) { Uint reclaimed_now = 0; + Eterm gc_trace_end_tag; int reds; ErtsMonotonicTime start_time = 0; /* Shut up faulty warning... */ ErtsSchedulerData *esdp; + erts_aint32_t state; ERTS_MSACC_PUSH_STATE_M(); #ifdef USE_VM_PROBES DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); @@ -588,7 +592,9 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, ASSERT(CONTEXT_REDS - ERTS_REDS_LEFT(p, fcalls) >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds); - if (p->flags & (F_DISABLE_GC|F_DELAY_GC)) + state = erts_smp_atomic32_read_nob(&p->state); + + if (p->flags & (F_DISABLE_GC|F_DELAY_GC) || state & ERTS_PSFLG_EXITING) return delay_garbage_collection(p, live_hf_end, need, fcalls); if (p->abandoned_heap) @@ -623,28 +629,28 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, if (GEN_GCS(p) < MAX_GEN_GCS(p) && !(FLAGS(p) & F_NEED_FULLSWEEP)) { if (IS_TRACED_FL(p, F_TRACE_GC)) { - trace_gc(p, am_gc_minor_start, need); + trace_gc(p, am_gc_minor_start, need, THE_NON_VALUE); } DTRACE2(gc_minor_start, pidbuf, need); reds = minor_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); DTRACE2(gc_minor_end, pidbuf, reclaimed_now); - if (IS_TRACED_FL(p, F_TRACE_GC)) { - trace_gc(p, am_gc_minor_end, reclaimed_now); - } - if (reds < 0) + if (reds == -1) { + if (IS_TRACED_FL(p, F_TRACE_GC)) { + trace_gc(p, am_gc_minor_end, reclaimed_now, THE_NON_VALUE); + } goto do_major_collection; + } + gc_trace_end_tag = am_gc_minor_end; } else { do_major_collection: ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_GC_FULL); if (IS_TRACED_FL(p, F_TRACE_GC)) { - trace_gc(p, am_gc_major_start, need); + trace_gc(p, am_gc_major_start, need, THE_NON_VALUE); } DTRACE2(gc_major_start, pidbuf, need); reds = major_collection(p, live_hf_end, need, objv, nobj, &reclaimed_now); DTRACE2(gc_major_end, pidbuf, reclaimed_now); - if (IS_TRACED_FL(p, F_TRACE_GC)) { - trace_gc(p, am_gc_major_end, reclaimed_now); - } + gc_trace_end_tag = am_gc_major_end; ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_GC); } @@ -660,6 +666,33 @@ do_major_collection: erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC); + /* Max heap size has been reached and the process was configured + to be killed, so we kill it and set it in a delayed garbage + collecting state. There should be no gc_end trace or + long_gc/large_gc triggers when this happens as process was + killed before a GC could be done. */ + if (reds == -2) { + ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL; + + erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); + erts_send_exit_signal(p, p->common.id, p, &locks, + am_kill, NIL, NULL, 0); + erts_smp_proc_unlock(p, locks & ERTS_PROC_LOCKS_ALL_MINOR); + + /* erts_send_exit_signal looks for ERTS_PSFLG_GC, so + we have to remove it after the signal is sent */ + erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC); + + /* We have to make sure that we have space for need on the heap */ + return delay_garbage_collection(p, live_hf_end, need, fcalls); + } + + erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC); + + if (IS_TRACED_FL(p, F_TRACE_GC)) { + trace_gc(p, gc_trace_end_tag, reclaimed_now, THE_NON_VALUE); + } + if (erts_system_monitor_long_gc != 0) { ErtsMonotonicTime end_time; Uint gc_time; @@ -761,6 +794,7 @@ erts_garbage_collect_hibernate(Process* p) heap_size = p->heap_sz + (p->old_htop - p->old_heap) + p->mbuf_sz; + heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_TMP_HEAP, sizeof(Eterm)*heap_size); htop = heap; @@ -1030,6 +1064,34 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end, Uint mature_size = p->high_water - mature; Uint size_before = young_gen_usage(p); + /* + * Check if we have gone past the max heap size limit + */ + + if (MAX_HEAP_SIZE_GET(p)) { + Uint heap_size = size_before, + /* Note that we also count the un-allocated area + in between the stack and heap */ + stack_size = HEAP_END(p) - HEAP_TOP(p), + extra_heap_size, + extra_old_heap_size = 0; + + /* Add potential old heap size */ + if (OLD_HEAP(p) == NULL && mature_size != 0) { + extra_old_heap_size = erts_next_heap_size(size_before, 1); + heap_size += extra_old_heap_size; + } else if (OLD_HEAP(p)) + heap_size += OLD_HEND(p) - OLD_HEAP(p); + + /* Add potential new young heap size */ + extra_heap_size = next_heap_size(p, stack_size + size_before, 0); + heap_size += extra_heap_size; + + if (heap_size > MAX_HEAP_SIZE_GET(p)) + if (reached_max_heap_size(p, heap_size, extra_heap_size, extra_old_heap_size)) + return -2; + } + /* * Allocate an old heap if we don't have one and if we'll need one. */ @@ -1139,6 +1201,16 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end, ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0)); ASSERT(MBUF(p) == NULL); + /* The heap usage during GC should be larger than what we end up + after a GC, even if we grow it. If this assertion is not true + we have to check size in grow_new_heap and potentially kill the + process from there */ + ASSERT(!MAX_HEAP_SIZE_GET(p) || + !(MAX_HEAP_SIZE_FLAGS_GET(p) & MAX_HEAP_SIZE_KILL) || + MAX_HEAP_SIZE_GET(p) > (young_gen_usage(p) + + (OLD_HEND(p) - OLD_HEAP(p)) + + (HEAP_END(p) - HEAP_TOP(p)))); + return gc_cost(size_after, adjust_size); } @@ -1457,6 +1529,25 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end, if (new_sz == HEAP_SIZE(p) && FLAGS(p) & F_HEAP_GROW) { new_sz = next_heap_size(p, HEAP_SIZE(p), 1); } + + + if (MAX_HEAP_SIZE_GET(p)) { + Uint heap_size = size_before; + + /* Add unused space in old heap */ + heap_size += OLD_HEND(p) - OLD_HTOP(p); + + /* Add stack + unused space in young heap */ + heap_size += HEAP_END(p) - HEAP_TOP(p); + + /* Add size of new young heap */ + heap_size += new_sz; + + if (MAX_HEAP_SIZE_GET(p) < heap_size) + if (reached_max_heap_size(p, heap_size, new_sz, 0)) + return -2; + } + FLAGS(p) &= ~(F_HEAP_GROW|F_NEED_FULLSWEEP); n_htop = n_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*new_sz); @@ -2986,7 +3077,9 @@ erts_gc_info_request(Process *c_p) } Eterm -erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp) +erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp, + Uint extra_heap_block, + Uint extra_old_heap_block_size) { ERTS_DECL_AM(bin_vheap_size); ERTS_DECL_AM(bin_vheap_block_size); @@ -3009,8 +3102,9 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp) AM_bin_old_vheap_block_size }; UWord values[] = { - OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) : 0, - HEAP_SIZE(p), + OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) + extra_old_heap_block_size + : extra_old_heap_block_size, + HEAP_SIZE(p) + extra_heap_block, MBUF_SIZE(p), HIGH_WATER(p) - HEAP_START(p), STACK_START(p) - p->stop, @@ -3056,6 +3150,130 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp) return res; } +static int +reached_max_heap_size(Process *p, Uint total_heap_size, + Uint extra_heap_size, Uint extra_old_heap_size) +{ + Uint max_heap_flags = MAX_HEAP_SIZE_FLAGS_GET(p); + if (IS_TRACED_FL(p, F_TRACE_GC) || + max_heap_flags & MAX_HEAP_SIZE_LOG) { + Eterm msg; + Uint size = 0; + Eterm *o_hp , *hp; + erts_process_gc_info(p, &size, NULL, extra_heap_size, + extra_old_heap_size); + o_hp = hp = erts_alloc(ERTS_ALC_T_TMP, size * sizeof(Eterm)); + msg = erts_process_gc_info(p, NULL, &hp, extra_heap_size, + extra_old_heap_size); + + if (max_heap_flags & MAX_HEAP_SIZE_LOG) { + int alive = erts_is_alive; + erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); + Eterm *o_hp, *hp, args = NIL; + + /* Build the format message */ + erts_dsprintf(dsbufp, " Process: ~p "); + if (alive) + erts_dsprintf(dsbufp, "on node ~p"); + erts_dsprintf(dsbufp, "~n Context: maximum heap size reached~n"); + erts_dsprintf(dsbufp, " Max heap size: ~p~n"); + erts_dsprintf(dsbufp, " Total heap size: ~p~n"); + erts_dsprintf(dsbufp, " Kill: ~p~n"); + erts_dsprintf(dsbufp, " Error Logger: ~p~n"); + erts_dsprintf(dsbufp, " GC Info: ~p~n"); + + /* Build the args in reverse order */ + o_hp = hp = erts_alloc(ERTS_ALC_T_TMP, 2*(alive ? 7 : 6) * sizeof(Eterm)); + args = CONS(hp, msg, args); hp += 2; + args = CONS(hp, am_true, args); hp += 2; + args = CONS(hp, (max_heap_flags & MAX_HEAP_SIZE_KILL ? am_true : am_false), args); hp += 2; + args = CONS(hp, make_small(total_heap_size), args); hp += 2; + args = CONS(hp, make_small(MAX_HEAP_SIZE_GET(p)), args); hp += 2; + if (alive) { + args = CONS(hp, erts_this_node->sysname, args); hp += 2; + } + args = CONS(hp, p->common.id, args); hp += 2; + + erts_send_error_term_to_logger(p->group_leader, dsbufp, args); + erts_free(ERTS_ALC_T_TMP, o_hp); + } + + if (IS_TRACED_FL(p, F_TRACE_GC)) + trace_gc(p, am_gc_max_heap_size, 0, msg); + + erts_free(ERTS_ALC_T_TMP, o_hp); + } + /* returns true if we should kill the process */ + return max_heap_flags & MAX_HEAP_SIZE_KILL; +} + +Eterm +erts_max_heap_size_map(Sint max_heap_size, Uint max_heap_flags, + Eterm **hpp, Uint *sz) +{ + if (!hpp) { + *sz += (2*3 + 1 + MAP_HEADER_FLATMAP_SZ); + return THE_NON_VALUE; + } else { + Eterm *hp = *hpp; + Eterm keys = TUPLE3(hp, am_error_logger, am_kill, am_size); + flatmap_t *mp; + hp += 4; + mp = (flatmap_t*) hp; + mp->thing_word = MAP_HEADER_FLATMAP; + mp->size = 3; + mp->keys = keys; + hp += MAP_HEADER_FLATMAP_SZ; + *hp++ = max_heap_flags & MAX_HEAP_SIZE_LOG ? am_true : am_false; + *hp++ = max_heap_flags & MAX_HEAP_SIZE_KILL ? am_true : am_false; + *hp++ = make_small(max_heap_size); + *hpp = hp; + return make_flatmap(mp); + } +} + +int +erts_max_heap_size(Eterm arg, Uint *max_heap_size, Uint *max_heap_flags) +{ + Sint sz; + *max_heap_flags = H_MAX_FLAGS; + if (is_small(arg)) { + sz = signed_val(arg); + *max_heap_flags = H_MAX_FLAGS; + } else if (is_map(arg)) { + const Eterm *size = erts_maps_get(am_size, arg); + const Eterm *kill = erts_maps_get(am_kill, arg); + const Eterm *log = erts_maps_get(am_error_logger, arg); + if (size && is_small(*size)) { + sz = signed_val(*size); + } else { + /* size is mandatory */ + return 0; + } + if (kill) { + if (*kill == am_true) + *max_heap_flags |= MAX_HEAP_SIZE_KILL; + else if (*kill == am_false) + *max_heap_flags &= ~MAX_HEAP_SIZE_KILL; + else + return 0; + } + if (log) { + if (*log == am_true) + *max_heap_flags |= MAX_HEAP_SIZE_LOG; + else if (*log == am_false) + *max_heap_flags &= ~MAX_HEAP_SIZE_LOG; + else + return 0; + } + } else + return 0; + if (sz < 0) + return 0; + *max_heap_size = sz; + return 1; +} + #if defined(DEBUG) || defined(ERTS_OFFHEAP_DEBUG) static int diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index 8a6ff99990..54ea9ca3c0 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -135,7 +135,7 @@ typedef struct { #define ERTS_PROCESS_GC_INFO_MAX_TERMS (11) /* number of elements in process_gc_info*/ #define ERTS_PROCESS_GC_INFO_MAX_SIZE \ (ERTS_PROCESS_GC_INFO_MAX_TERMS * (2/*cons*/ + 3/*2-tuple*/ + BIG_UINT_HEAP_SIZE)) -Eterm erts_process_gc_info(struct process*, Uint *, Eterm **); +Eterm erts_process_gc_info(struct process*, Uint *, Eterm **, Uint, Uint); void erts_gc_info(ErtsGCInfo *gcip); void erts_init_gc(void); @@ -155,5 +155,7 @@ void erts_offset_off_heap(struct erl_off_heap*, Sint, Eterm*, Eterm*); void erts_offset_heap_ptr(Eterm*, Uint, Sint, Eterm*, Eterm*); void erts_offset_heap(Eterm*, Uint, Sint, Eterm*, Eterm*); void erts_free_heap_frags(struct process* p); +Eterm erts_max_heap_size_map(Sint, Uint, Eterm **, Uint *); +int erts_max_heap_size(Eterm, Uint *, Uint *); #endif /* __ERL_GC_H__ */ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index fcd2739ac3..0649fb68de 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -164,6 +164,8 @@ int erts_use_sender_punish; Uint display_items; /* no of items to display in traces etc */ int H_MIN_SIZE; /* The minimum heap grain */ int BIN_VH_MIN_SIZE; /* The minimum binary virtual*/ +int H_MAX_SIZE; /* The maximum heap size */ +int H_MAX_FLAGS; /* The maximum heap flags */ Uint32 erts_debug_flags; /* Debug flags. */ int erts_backtrace_depth; /* How many functions to show in a backtrace @@ -576,6 +578,10 @@ void erts_usage(void) H_DEFAULT_SIZE); erts_fprintf(stderr, "-hmbs size set minimum binary virtual heap size in words (default %d)\n", VH_DEFAULT_SIZE); + erts_fprintf(stderr, "-hmax size set maximum heap size in words (default %d)\n", + H_DEFAULT_MAX_SIZE); + erts_fprintf(stderr, "-hmaxk bool enable or disable kill at max heap size (default true)\n"); + erts_fprintf(stderr, "-hmaxel bool enable or disable error_logger report at max heap size (default true)\n"); erts_fprintf(stderr, "-hpds size initial process dictionary size (default %d)\n", erts_pd_initial_size); erts_fprintf(stderr, "-hmqd val set default message queue data flag for processes,\n"); @@ -759,6 +765,8 @@ early_init(int *argc, char **argv) /* erts_async_thread_suggested_stack_size = ERTS_ASYNC_THREAD_MIN_STACK_SIZE; H_MIN_SIZE = H_DEFAULT_SIZE; BIN_VH_MIN_SIZE = VH_DEFAULT_SIZE; + H_MAX_SIZE = H_DEFAULT_MAX_SIZE; + H_MAX_FLAGS = MAX_HEAP_SIZE_KILL|MAX_HEAP_SIZE_LOG; erts_initialized = 0; @@ -1484,10 +1492,13 @@ erl_start(int argc, char **argv) char *sub_param = argv[i]+2; /* set default heap size * - * h|ms - min_heap_size - * h|mbs - min_bin_vheap_size - * h|pds - erts_pd_initial_size - * h|mqd - message_queue_data + * h|ms - min_heap_size + * h|mbs - min_bin_vheap_size + * h|pds - erts_pd_initial_size + * h|mqd - message_queue_data + * h|max - max_heap_size + * h|maxk - max_heap_kill + * h|maxel - max_heap_error_logger * */ if (has_prefix("mbs", sub_param)) { @@ -1530,6 +1541,41 @@ erl_start(int argc, char **argv) "Invalid message_queue_data flag: %s\n", arg); erts_usage(); } + } else if (has_prefix("maxk", sub_param)) { + arg = get_arg(sub_param+4, argv[i+1], &i); + if (strcmp(arg,"true") == 0) { + H_MAX_FLAGS |= MAX_HEAP_SIZE_KILL; + } else if (strcmp(arg,"false") == 0) { + H_MAX_FLAGS &= ~MAX_HEAP_SIZE_KILL; + } else { + erts_fprintf(stderr, "bad max heap kill %s\n", arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, ("using max heap kill %d\n", H_MAX_FLAGS)); + } else if (has_prefix("maxel", sub_param)) { + arg = get_arg(sub_param+5, argv[i+1], &i); + if (strcmp(arg,"true") == 0) { + H_MAX_FLAGS |= MAX_HEAP_SIZE_LOG; + } else if (strcmp(arg,"false") == 0) { + H_MAX_FLAGS &= ~MAX_HEAP_SIZE_LOG; + } else { + erts_fprintf(stderr, "bad max heap error logger %s\n", arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, ("using max heap log %d\n", H_MAX_FLAGS)); + } else if (has_prefix("max", sub_param)) { + arg = get_arg(sub_param+3, argv[i+1], &i); + if ((H_MAX_SIZE = atoi(arg)) < 0) { + erts_fprintf(stderr, "bad max heap size %s\n", arg); + erts_usage(); + } + if (H_MAX_SIZE < H_MIN_SIZE && H_MAX_SIZE) { + erts_fprintf(stderr, "max heap size (%s) is not allowed to be " + "smaller than min heap size (%d)\n", + arg, H_MIN_SIZE); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, ("using max heap size %d\n", H_MAX_SIZE)); } else { /* backward compatibility */ arg = get_arg(argv[i]+2, argv[i+1], &i); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 36b0af0c1a..e2f14c23bf 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11021,9 +11021,13 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->min_heap_size = so->min_heap_size; p->min_vheap_size = so->min_vheap_size; p->max_gen_gcs = so->max_gen_gcs; + MAX_HEAP_SIZE_SET(p, so->max_heap_size); + MAX_HEAP_SIZE_FLAGS_SET(p, so->max_heap_flags); } else { p->min_heap_size = H_MIN_SIZE; p->min_vheap_size = BIN_VH_MIN_SIZE; + MAX_HEAP_SIZE_SET(p, H_MAX_SIZE); + MAX_HEAP_SIZE_FLAGS_SET(p, H_MAX_FLAGS); p->max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs); } p->schedule_count = 0; @@ -11600,7 +11604,8 @@ set_proc_exiting(Process *p, p->i = (BeamInstr *) beam_exit; #ifndef ERTS_SMP - if (state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)) { + if (state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS) + && !(state & ERTS_PSFLG_GC)) { /* * I non smp case: * diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 59da9c1779..12a919bc87 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -918,6 +918,15 @@ struct ErtsPendingSuspend_ { # define BIN_OLD_VHEAP_SZ(p) (p)->bin_old_vheap_sz # define BIN_OLD_VHEAP(p) (p)->bin_old_vheap +# define MAX_HEAP_SIZE_GET(p) ((p)->max_heap_size >> 2) +# define MAX_HEAP_SIZE_SET(p, sz) ((p)->max_heap_size = ((sz) << 2) | \ + MAX_HEAP_SIZE_FLAGS_GET(p)) +# define MAX_HEAP_SIZE_FLAGS_GET(p) ((p)->max_heap_size & 0x3) +# define MAX_HEAP_SIZE_FLAGS_SET(p, flags) ((p)->max_heap_size = flags | \ + ((p)->max_heap_size & ~0x3)) +# define MAX_HEAP_SIZE_KILL 1 +# define MAX_HEAP_SIZE_LOG 2 + struct process { ErtsPTabElementCommon common; /* *Need* to be first in struct */ @@ -935,6 +944,7 @@ struct process { Uint heap_sz; /* Size of heap in words */ Uint min_heap_size; /* Minimum size of heap (in words). */ Uint min_vheap_size; /* Minimum size of virtual heap (in words). */ + Uint max_heap_size; /* Maximum size of heap (in words). */ #if !defined(NO_FPE_SIGNALS) || defined(HIPE) volatile unsigned long fp_exception; @@ -1285,6 +1295,8 @@ typedef struct { Uint min_vheap_size; /* Minimum virtual heap size */ int priority; /* Priority for process. */ Uint16 max_gen_gcs; /* Maximum number of gen GCs before fullsweep. */ + Uint max_heap_size; /* Maximum heap size in words */ + Uint max_heap_flags; /* Maximum heap flags (kill | log) */ int scheduler; } ErlSpawnOpts; diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 1c0fc0a11f..6d6373b706 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1409,25 +1409,28 @@ void save_calls(Process *p, Export *e) * are all small (atomic) integers. */ void -trace_gc(Process *p, Eterm what, Uint size) +trace_gc(Process *p, Eterm what, Uint size, Eterm msg) { ErtsTracerNif *tnif = NULL; Eterm* hp; - Eterm msg = NIL; Uint sz = 0; Eterm tup; - if (is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, TRACE_FUN_E_GC, what)) { + if (is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, + TRACE_FUN_E_GC, what)) { + + if (is_non_value(msg)) { - (void) erts_process_gc_info(p, &sz, NULL); - hp = HAlloc(p, sz + 3 + 2); + (void) erts_process_gc_info(p, &sz, NULL, 0, 0); + hp = HAlloc(p, sz + 3 + 2); - msg = erts_process_gc_info(p, NULL, &hp); - tup = TUPLE2(hp, am_wordsize, make_small(size)); hp += 3; - msg = CONS(hp, tup, msg); hp += 2; + msg = erts_process_gc_info(p, NULL, &hp, 0, 0); + tup = TUPLE2(hp, am_wordsize, make_small(size)); hp += 3; + msg = CONS(hp, tup, msg); hp += 2; + } send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_GC, - what, msg, am_undefined, am_true); + what, msg, THE_NON_VALUE, am_true); } } diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index de4beadb7e..fd5879bc9d 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -112,7 +112,7 @@ void trace_sched(Process*, ErtsProcLocks, Eterm); void trace_proc(Process*, ErtsProcLocks, Process*, Eterm, Eterm); void trace_proc_spawn(Process*, Eterm what, Eterm pid, Eterm mod, Eterm func, Eterm args); void save_calls(Process *p, Export *); -void trace_gc(Process *p, Eterm what, Uint size); +void trace_gc(Process *p, Eterm what, Uint size, Eterm msg); /* port tracing */ void trace_virtual_sched(Process*, ErtsProcLocks, Eterm); void trace_sched_ports(Port *pp, Eterm); diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index f3c54de214..f97716d030 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -50,6 +50,7 @@ #define H_DEFAULT_SIZE 233 /* default (heap + stack) min size */ #define VH_DEFAULT_SIZE 32768 /* default virtual (bin) heap min size (words) */ +#define H_DEFAULT_MAX_SIZE 0 /* default max heap size is off */ #define CP_SIZE 1 @@ -160,6 +161,8 @@ extern int num_instructions; /* Number of instruction in opc[]. */ extern int H_MIN_SIZE; /* minimum (heap + stack) */ extern int BIN_VH_MIN_SIZE; /* minimum virtual (bin) heap */ +extern int H_MAX_SIZE; /* maximum (heap + stack) */ +extern int H_MAX_FLAGS; /* maximum heap flags */ extern int erts_atom_table_size;/* Atom table size */ extern int erts_pd_initial_size;/* Initial Process dictionary table size */ diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 61a68f9759..eaa4026a8a 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -46,7 +46,7 @@ process_status_exiting/1, otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1, process_info_messages/1, process_flag_badarg/1, process_flag_heap_size/1, - spawn_opt_heap_size/1, + spawn_opt_heap_size/1, spawn_opt_max_heap_size/1, processes_large_tab/1, processes_default_tab/1, processes_small_tab/1, processes_this_tab/1, processes_apply_trap/1, processes_last_call_trap/1, processes_gc_trap/1, @@ -60,7 +60,7 @@ system_task_on_suspended/1, gc_request_when_gc_disabled/1, gc_request_blast_when_gc_disabled/1]). --export([prio_server/2, prio_client/2]). +-export([prio_server/2, prio_client/2, init/1, handle_event/2]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -84,7 +84,8 @@ all() -> bump_reductions, low_prio, yield, yield2, otp_4725, bad_register, garbage_collect, process_info_messages, process_flag_badarg, process_flag_heap_size, - spawn_opt_heap_size, otp_6237, {group, processes_bif}, + spawn_opt_heap_size, spawn_opt_max_heap_size, otp_6237, + {group, processes_bif}, {group, otp_7738}, garb_other_running, {group, system_task}]. @@ -425,6 +426,8 @@ t_process_info(Config) when is_list(Config) -> {status, running} = process_info(self(), status), {min_heap_size, 233} = process_info(self(), min_heap_size), {min_bin_vheap_size,46422} = process_info(self(), min_bin_vheap_size), + {max_heap_size, #{ size := 0, kill := true, error_logger := true}} = + process_info(self(), max_heap_size), {current_function,{?MODULE,t_process_info,1}} = process_info(self(), current_function), {current_function,{?MODULE,t_process_info,1}} = @@ -564,6 +567,8 @@ process_info_other_msg(Config) when is_list(Config) -> {min_heap_size, 233} = process_info(Pid, min_heap_size), {min_bin_vheap_size, 46422} = process_info(Pid, min_bin_vheap_size), + {max_heap_size, #{ size := 0, kill := true, error_logger := true}} = + process_info(self(), max_heap_size), Pid ! stop, ok. @@ -914,10 +919,14 @@ process_info_garbage_collection(_Config) -> Parent = self(), Pid = spawn_link( fun() -> + %% We set mqd to off_heap and send an tuple + %% to process in order to force mbuf_size + %% to be used + process_flag(message_queue_data, off_heap), receive go -> ok end, (fun F(0) -> Parent ! deep, - receive ok -> ok end, + receive {ok,_} -> ok end, []; F(N) -> timer:sleep(1), @@ -926,31 +935,52 @@ process_info_garbage_collection(_Config) -> Parent ! shallow, receive done -> ok end end), - {garbage_collection_info, Before} = - erlang:process_info(Pid, garbage_collection_info), + [{garbage_collection_info, Before},{total_heap_size, THSBefore}] = + erlang:process_info(Pid, [garbage_collection_info, total_heap_size]), Pid ! go, receive deep -> ok end, - {_, Deep} = erlang:process_info(Pid, garbage_collection_info), - Pid ! ok, receive shallow -> ok end, - {_, After} = erlang:process_info(Pid, garbage_collection_info), + [{_, Deep},{_,THSDeep}] = + erlang:process_info(Pid, [garbage_collection_info, total_heap_size]), + Pid ! {ok, make_ref()}, receive shallow -> ok end, + [{_, After},{_, THSAfter}] = + erlang:process_info(Pid, [garbage_collection_info, total_heap_size]), Pid ! done, %% Do some general checks to see if everything seems to be roughly correct ct:log("Before: ~p",[Before]), ct:log("Deep: ~p",[Deep]), ct:log("After: ~p",[After]), + ct:log("Before THS: ~p",[THSBefore]), + ct:log("Deep THS: ~p",[THSDeep]), + ct:log("After THS: ~p",[THSAfter]), %% Check stack_size - true = proplists:get_value(stack_size, Before) < proplists:get_value(stack_size, Deep), - true = proplists:get_value(stack_size, After) < proplists:get_value(stack_size, Deep), + true = gv(stack_size, Before) < gv(stack_size, Deep), + true = gv(stack_size, After) < gv(stack_size, Deep), %% Check used heap size - true = proplists:get_value(heap_size, Before) + proplists:get_value(old_heap_size, Before) - < proplists:get_value(heap_size, Deep) + proplists:get_value(old_heap_size, Deep), - true = proplists:get_value(heap_size, Before) + proplists:get_value(old_heap_size, Before) - < proplists:get_value(heap_size, After) + proplists:get_value(old_heap_size, After), + true = gv(heap_size, Before) + gv(old_heap_size, Before) + < gv(heap_size, Deep) + gv(old_heap_size, Deep), + true = gv(heap_size, Before) + gv(old_heap_size, Before) + < gv(heap_size, After) + gv(old_heap_size, After), + + %% Check that total_heap_size == heap_block_size + old_heap_block_size + mbuf_size + THSBefore = gv(heap_block_size, Before) + + gv(old_heap_block_size, Before) + + gv(mbuf_size, Before), + + THSDeep = gv(heap_block_size, Deep) + + gv(old_heap_block_size, Deep) + + gv(mbuf_size, Deep), + + THSAfter = gv(heap_block_size, After) + + gv(old_heap_block_size, After) + + gv(mbuf_size, After), ok. +gv(Key,List) -> + proplists:get_value(Key,List). + %% Tests erlang:bump_reductions/1. bump_reductions(Config) when is_list(Config) -> erlang:garbage_collect(), @@ -1323,6 +1353,28 @@ process_flag_badarg(Config) when is_list(Config) -> chk_badarg(fun () -> process_flag(min_heap_size, gurka) end), chk_badarg(fun () -> process_flag(min_bin_vheap_size, gurka) end), chk_badarg(fun () -> process_flag(min_bin_vheap_size, -1) end), + + chk_badarg(fun () -> process_flag(max_heap_size, gurka) end), + chk_badarg(fun () -> process_flag(max_heap_size, -1) end), + chk_badarg(fun () -> + {_,Min} = process_info(self(), min_heap_size), + process_flag(max_heap_size, Min - 1) + end), + chk_badarg(fun () -> + {_,Min} = process_info(self(), min_heap_size), + process_flag(max_heap_size, #{size => Min - 1}) + end), + chk_badarg(fun () -> process_flag(max_heap_size, #{}) end), + chk_badarg(fun () -> process_flag(max_heap_size, #{ kill => true }) end), + chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233, + kill => gurka }) end), + chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233, + error_logger => gurka }) end), + chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233, + kill => true, + error_logger => gurka }) end), + chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 1 bsl 64 }) end), + chk_badarg(fun () -> process_flag(priority, 4711) end), chk_badarg(fun () -> process_flag(save_calls, hmmm) end), P= spawn_link(fun () -> receive die -> ok end end), @@ -1923,6 +1975,110 @@ spawn_opt_heap_size(Config) when is_list(Config) -> Pid ! stop, ok. +spawn_opt_max_heap_size(_Config) -> + + error_logger:add_report_handler(?MODULE, self()), + + %% Test that numerical limit works + max_heap_size_test(1024, 1024, true, true), + + %% Test that map limit works + max_heap_size_test(#{ size => 1024 }, 1024, true, true), + + %% Test that no kill is sent + max_heap_size_test(#{ size => 1024, kill => false }, 1024, false, true), + + %% Test that no error_logger report is sent + max_heap_size_test(#{ size => 1024, error_logger => false }, 1024, true, false), + + %% Test that system_flag works + erlang:system_flag(max_heap_size, #{ size => 0, kill => false, + error_logger => true}), + max_heap_size_test(#{ size => 1024 }, 1024, false, true), + max_heap_size_test(#{ size => 1024, kill => true }, 1024, true, true), + + erlang:system_flag(max_heap_size, #{ size => 0, kill => true, + error_logger => false}), + max_heap_size_test(#{ size => 1024 }, 1024, true, false), + max_heap_size_test(#{ size => 1024, error_logger => true }, 1024, true, true), + + erlang:system_flag(max_heap_size, #{ size => 1 bsl 20, kill => true, + error_logger => true}), + max_heap_size_test(#{ }, 1 bsl 20, true, true), + + erlang:system_flag(max_heap_size, #{ size => 0, kill => true, + error_logger => true}), + + %% Test that ordinary case works as expected again + max_heap_size_test(1024, 1024, true, true), + + ok. + +max_heap_size_test(Option, Size, Kill, ErrorLogger) + when map_size(Option) == 0 -> + max_heap_size_test([], Size, Kill, ErrorLogger); +max_heap_size_test(Option, Size, Kill, ErrorLogger) + when is_map(Option); is_integer(Option) -> + max_heap_size_test([{max_heap_size, Option}], Size, Kill, ErrorLogger); +max_heap_size_test(Option, Size, Kill, ErrorLogger) -> + OomFun = fun F() -> timer:sleep(5),[lists:seq(1,1000)|F()] end, + Pid = spawn_opt(OomFun, Option), + {max_heap_size, MHSz} = erlang:process_info(Pid, max_heap_size), + ct:log("Default: ~p~nOption: ~p~nProc: ~p~n", + [erlang:system_info(max_heap_size), Option, MHSz]), + + #{ size := Size} = MHSz, + + Ref = erlang:monitor(process, Pid), + if Kill -> + receive + {'DOWN', Ref, process, Pid, killed} -> + ok + end; + true -> + ok + end, + if ErrorLogger -> + receive + {error, _, {emulator, _, [Pid|_]}} -> + ok + end; + true -> + ok + end, + if not Kill -> + exit(Pid, die), + receive + {'DOWN', Ref, process, Pid, die} -> + ok + end, + flush(); + true -> + ok + end, + receive + M -> + ct:fail({unexpected_message, M}) + after 10 -> + ok + end. + +flush() -> + receive + _M -> + flush() + after 1000 -> + ok + end. + +%% error_logger report handler proxy +init(Pid) -> + {ok, Pid}. + +handle_event(Event, Pid) -> + Pid ! Event, + {ok, Pid}. + processes_term_proc_list(Config) when is_list(Config) -> Tester = self(), as_expected = processes_term_proc_list_test(false), diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 5a5021b003..7f4f78b5fe 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -150,6 +150,9 @@ static char *plush_val_switches[] = { "ms", "mbs", "pds", + "max", + "maxk", + "maxel", "mqd", "", NULL diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 90fd536b15..3d152c4e92 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2073,6 +2073,9 @@ open_port(PortName, PortSettings) -> (min_bin_vheap_size, MinBinVHeapSize) -> OldMinBinVHeapSize when MinBinVHeapSize :: non_neg_integer(), OldMinBinVHeapSize :: non_neg_integer(); + (max_heap_size, MaxHeapSize) -> OldMaxHeapSize when + MaxHeapSize :: max_heap_size(), + OldMaxHeapSize :: max_heap_size(); (message_queue_data, MQD) -> OldMQD when MQD :: message_queue_data(), OldMQD :: message_queue_data(); @@ -2154,6 +2157,7 @@ process_flag(_Flag, _Value) -> {messages, MessageQueue :: [term()]} | {min_heap_size, MinHeapSize :: non_neg_integer()} | {min_bin_vheap_size, MinBinVHeapSize :: non_neg_integer()} | + {max_heap_size, MaxHeapSize :: max_heap_size()} | {monitored_by, Pids :: [pid()]} | {monitors, Monitors :: [{process, Pid :: pid() | @@ -2238,6 +2242,7 @@ setelement(_Index, _Tuple1, _Value) -> | {priority, Level :: priority_level()} | {fullsweep_after, Number :: non_neg_integer()} | {min_heap_size, Size :: non_neg_integer()} + | {max_heap_size, Size :: max_heap_size()} | {min_bin_vheap_size, VSize :: non_neg_integer()}. spawn_opt(_Tuple) -> erlang:nif_error(undefined). @@ -2330,6 +2335,9 @@ subtract(_,_) -> OldMinBinVHeapSize when MinBinVHeapSize :: non_neg_integer(), OldMinBinVHeapSize :: non_neg_integer(); + (max_heap_size, MaxHeapSize) -> OldMaxHeapSize when + MaxHeapSize :: max_heap_size(), + OldMaxHeapSize :: max_heap_size(); (multi_scheduling, BlockState) -> OldBlockState when BlockState :: block | unblock | block_normal | unblock_normal, OldBlockState :: blocked | disabled | enabled; @@ -2511,6 +2519,7 @@ tuple_to_list(_Tuple) -> logical_processors_available | logical_processors_online) -> unknown | pos_integer(); (machine) -> string(); + (max_heap_size) -> {max_heap_size, MaxHeapSize :: max_heap_size()}; (message_queue_data) -> message_queue_data(); (min_heap_size) -> {min_heap_size, MinHeapSize :: pos_integer()}; (min_bin_vheap_size) -> {min_bin_vheap_size, @@ -2648,6 +2657,13 @@ spawn_monitor(M, F, A) -> erlang:error(badarg, [M,F,A]). +-type max_heap_size() :: + Size :: non_neg_integer() + %% TODO change size => to := when -type maps support is finalized + | #{ size => non_neg_integer(), + kill => boolean(), + error_logger => boolean() }. + -type spawn_opt_option() :: link | monitor @@ -2655,6 +2671,7 @@ spawn_monitor(M, F, A) -> | {fullsweep_after, Number :: non_neg_integer()} | {min_heap_size, Size :: non_neg_integer()} | {min_bin_vheap_size, VSize :: non_neg_integer()} + | {max_heap_size, Size :: max_heap_size()} | {message_queue_data, MQD :: message_queue_data()}. -spec spawn_opt(Fun, Options) -> pid() | {pid(), reference()} when -- cgit v1.2.3 From dc203f539692a1191240209fb95afa020fa342b5 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 3 May 2016 09:55:15 +0200 Subject: erts: Fix pre-bif yield current_function --- erts/doc/src/erlang.xml | 2 +- erts/emulator/beam/beam_emu.c | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 3930e5e11d..e0723127f2 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4364,7 +4364,7 @@ os_prompt%

size is the entire heap of the process when garbage collection is triggered, this includes all generational heaps, the process stack, - any + any messages that are considered to be part of the heap and any extra memory that the garbage collector needs during collection.

diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index a88fea3802..d00a563087 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -2823,7 +2823,7 @@ do { \ /* If we have run out of reductions, we do a context switch before calling the bif */ c_p->arity = ((Export *)Arg(0))->code[2]; - c_p->current = NULL; + c_p->current = ((Export *)Arg(0))->code; goto context_switch3; } @@ -3538,9 +3538,7 @@ do { \ if (!((FCALLS - 1) > 0 || (FCALLS - 1) > neg_o_reds)) { /* If we have run out of reductions, we do a context switch before calling the nif */ - c_p->arity = I[-1]; - c_p->current = NULL; - goto context_switch3; + goto context_switch; } ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF); @@ -3589,9 +3587,7 @@ do { \ if (!((FCALLS - 1) > 0 || (FCALLS - 1) > neg_o_reds)) { /* If we have run out of reductions, we do a context switch before calling the bif */ - c_p->arity = I[-1]; - c_p->current = NULL; - goto context_switch3; + goto context_switch; } if (ERTS_MSACC_IS_ENABLED_CACHED_X()) { -- cgit v1.2.3 From dc30187900f1bd12043170b14db68dfbbff33828 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 9 May 2016 19:51:04 +0200 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 55800 -> 55780 bytes erts/preloaded/ebin/erl_tracer.beam | Bin 2128 -> 2112 bytes erts/preloaded/ebin/erlang.beam | Bin 104172 -> 104620 bytes erts/preloaded/ebin/erts_code_purger.beam | Bin 8712 -> 8696 bytes erts/preloaded/ebin/erts_internal.beam | Bin 10552 -> 10536 bytes erts/preloaded/ebin/init.beam | Bin 49944 -> 49924 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1460 -> 1444 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1328 -> 1312 bytes erts/preloaded/ebin/prim_file.beam | Bin 44780 -> 44764 bytes erts/preloaded/ebin/prim_inet.beam | Bin 72564 -> 72544 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23172 -> 23152 bytes erts/preloaded/ebin/zlib.beam | Bin 14152 -> 14136 bytes 12 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 3e82b8e20e..de2693472a 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam index 67d96d0f0d..69804540c9 100644 Binary files a/erts/preloaded/ebin/erl_tracer.beam and b/erts/preloaded/ebin/erl_tracer.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 321e4e7e1b..8379bf1768 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index 0c667663fd..b1da0aa861 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index ed2e328f5b..cc4f3dbdaf 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 23ebd2cad9..7b5797e90a 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index b63fa24848..f893b9c181 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index 7c8fdc82c0..e4ce601c03 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index d4559bc9ed..babba3081f 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 5a0678632e..0521060e34 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index 3d32dd8449..e1faca7d96 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index ced9f9c952..5d3cbc1b36 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 36dd5c3dd86c91d04a7fc13fd2a89b10af64db5a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 10 May 2016 15:37:18 +0200 Subject: erts: Fix max heap size exit when in hipe mode --- erts/emulator/hipe/hipe_mode_switch.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 9ad44b25ac..f532d3151f 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -59,6 +59,7 @@ * TODO: check PCB consistency at native BIF calls */ int hipe_modeswitch_debug = 0; +extern BeamInstr beam_exit[]; #define HIPE_DEBUG 0 @@ -509,6 +510,10 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) if (scb) ERTS_PROC_SET_SAVED_CALLS_BUF(p, scb); + /* The process may have died while it was executing, + if so we return out from native code to the interpreter */ + if (erts_smp_atomic32_read_nob(&p->state) & ERTS_PSFLG_EXITING) + p->i = beam_exit; #ifdef DEBUG ASSERT(p->debug_reds_in == reds_in); #endif -- cgit v1.2.3 From 725ffcf9a638d8b15295f0a3709f2e786663c987 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 10 May 2016 16:12:59 +0200 Subject: erts: Fix doc typo in erts_alloc --- erts/doc/src/erts_alloc.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 70775b9f0f..9aef1c0b1f 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -583,7 +583,7 @@

The following flag is special for exec_alloc:

- ]]> + ]]> exec_alloc super carrier size (in MB). The amount of virtual address space reserved for native executable code -- cgit v1.2.3 From 3d85de7d6bd60e6b409be6910b35d973206ef388 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 10 May 2016 16:16:47 +0200 Subject: erts: Skip alloc_SUITE:erts_mmap if ERL_ZFLAGS contains flags for super carrier +MMsc* --- erts/emulator/test/alloc_SUITE.erl | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 1f7b499dcb..84cf4921d3 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -73,16 +73,32 @@ migration(Cfg) -> end. erts_mmap(Config) when is_list(Config) -> - case os:type() of - {unix, _} -> + case {os:type(), mmsc_flags()} of + {{unix,_}, false} -> [erts_mmap_do(Config, SCO, SCRPM, SCRFSD) || SCO <-[true,false], SCRFSD <-[1234,0], SCRPM <- [true,false]]; - {SkipOs,_} -> + {{unix,_}, Flags} -> + {skipped, Flags}; + {{SkipOs,_},_} -> {skipped, lists:flatten(["Not run on " | io_lib:format("~p",[SkipOs])])} end. +%% Check if there are ERL_FLAGS set that will mess up this test case +mmsc_flags() -> + case mmsc_flags("ERL_FLAGS") of + false -> mmsc_flags("ERL_ZFLAGS"); + Flags -> Flags + end. +mmsc_flags(Env) -> + case os:getenv(Env) of + false -> false; + V -> case string:str(V, "+MMsc") of + 0 -> false; + P -> Env ++ "=" ++ string:substr(V, P) + end + end. erts_mmap_do(Config, SCO, SCRPM, SCRFSD) -> %% We use the number of schedulers + 1 * approx main carriers size -- cgit v1.2.3 From 0aa5f873fdfb391a455bc134256b7c464ffa161b Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 15 Apr 2016 11:24:14 +0200 Subject: Remove conditional dirty schedulers API --- erts/configure.in | 40 +++++++++++++--------------- erts/doc/src/erl_nif.xml | 11 +++----- erts/emulator/Makefile.in | 7 ++++- erts/emulator/beam/erl_drv_nif.h | 9 +++---- erts/emulator/beam/erl_nif.h | 13 +++------ erts/emulator/beam/erl_nif_api_funcs.h | 15 ++--------- erts/emulator/beam/io.c | 8 +++--- erts/etc/common/erlexec.c | 2 -- erts/include/erl_native_features_config.h.in | 22 --------------- erts/lib_src/Makefile.in | 1 - 10 files changed, 42 insertions(+), 86 deletions(-) delete mode 100644 erts/include/erl_native_features_config.h.in (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 6765f47a88..4a63381eb7 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -99,7 +99,7 @@ ERL_XCOMP_SYSROOT_INIT AC_ISC_POSIX -AC_CONFIG_HEADER($host/config.h:config.h.in include/internal/$host/ethread_header_config.h:include/internal/ethread_header_config.h.in include/$host/erl_int_sizes_config.h:include/erl_int_sizes_config.h.in include/$host/erl_native_features_config.h:include/erl_native_features_config.h.in) +AC_CONFIG_HEADER($host/config.h:config.h.in include/internal/$host/ethread_header_config.h:include/internal/ethread_header_config.h.in include/$host/erl_int_sizes_config.h:include/erl_int_sizes_config.h.in) dnl ---------------------------------------------------------------------- dnl Optional features. dnl ---------------------------------------------------------------------- @@ -137,7 +137,7 @@ AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]), [ case "$enableval" in no) enable_dirty_schedulers=no ;; *) enable_dirty_schedulers=yes ;; - esac ], enable_dirty_schedulers=no) + esac ], enable_dirty_schedulers=default) AC_ARG_ENABLE(smp-support, AS_HELP_STRING([--enable-smp-support], [enable smp support]) @@ -1006,6 +1006,23 @@ case $ERTS_BUILD_SMP_EMU in ;; esac +AC_MSG_CHECKING(whether dirty schedulers should be enabled) +case $ERTS_BUILD_SMP_EMU-$enable_dirty_schedulers in + yes-yes) + DIRTY_SCHEDULER_SUPPORT=yes;; + yes-default) + ## Maybe yes for OTP 19... + DIRTY_SCHEDULER_SUPPORT=no;; + no-default) + DIRTY_SCHEDULER_SUPPORT=no;; + no-yes) + AC_MSG_ERROR([No smp emulator will be built, but dirty schedulers requested]);; + *) + DIRTY_SCHEDULER_SUPPORT=no;; +esac +AC_MSG_RESULT($DIRTY_SCHEDULER_SUPPORT) +AC_SUBST(DIRTY_SCHEDULER_SUPPORT) + if test $ERTS_BUILD_SMP_EMU = yes; then if test $found_threads = no; then @@ -1208,14 +1225,6 @@ esac if test $emu_threads != yes; then enable_lock_check=no enable_lock_count=no - AC_MSG_CHECKING(whether dirty schedulers should be enabled) - if test "x$enable_dirty_schedulers" != "xno"; then - AC_DEFINE(ERL_NIF_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) - AC_DEFINE(ERL_DRV_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) - AC_MSG_RESULT(yes) - else - AC_MSG_RESULT(no) - fi else # Threads enabled for emulator EMU_THR_LIB_NAME=$ETHR_LIB_NAME @@ -1235,17 +1244,6 @@ else EMU_THR_DEFS="$EMU_THR_DEFS -DERTS_ENABLE_LOCK_COUNT" fi - AC_MSG_CHECKING(whether dirty schedulers should be enabled) - if test "x$enable_dirty_schedulers" != "xno"; then - EMU_THR_DEFS="$EMU_THR_DEFS -DERTS_DIRTY_SCHEDULERS" - AC_DEFINE(ERTS_DIRTY_SCHEDULERS, 1, [Define if the emulator supports dirty schedulers]) - AC_DEFINE(ERL_NIF_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) - AC_DEFINE(ERL_DRV_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) - AC_MSG_RESULT(yes) - else - AC_MSG_RESULT(no) - fi - case $host_os in linux*) AC_MSG_CHECKING([whether dlopen() needs to be called before first call to dlerror()]) diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 7546f7ef81..ef64ac66dc 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -354,10 +354,9 @@ ok passing to it a pointer to the dirty NIF to be executed and indicating with the flags argument whether it expects the operation to be CPU-bound or I/O-bound.

Dirty NIF support is available only when the emulator is configured with dirty - schedulers enabled. This feature is currently disabled by default. To determine whether - the dirty NIF API is available, native code can check to see if the C preprocessor macro - ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined. Also, if the Erlang runtime was built - without threading support, dirty schedulers are disabled. To check at runtime for the presence + schedulers enabled. This feature is currently disabled by default. The Erlang runtime + without SMP support currently do not support dirty schedulers even when the dirty + scheduler support has been enabled. To check at runtime for the presence of dirty scheduler threads, code can use the enif_system_info() API function.

@@ -998,10 +997,6 @@ typedef enum { from within a dirty NIF returns true. It returns false when the calling NIF is a regular NIF running on a normal scheduler thread, or when the emulator is built without threading support.

-

This function is available only when the emulator is configured with dirty - schedulers enabled. This feature is currently disabled by default. To determine whether - the dirty NIF API is available, native code can check to see if the C preprocessor macro - ERL_NIF_DIRTY_SCHEDULER_SUPPORT is defined.

intenif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term) diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index fb486c917f..2212aed5e0 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -50,6 +50,8 @@ LDFLAGS=@LDFLAGS@ ARFLAGS=rc OMIT_OMIT_FP=no +DIRTY_SCHEDULER_SUPPORT=@DIRTY_SCHEDULER_SUPPORT@ + ifeq ($(TYPE),debug) PURIFY = TYPEMARKER = .debug @@ -174,6 +176,10 @@ FLAVOR_MARKER=.smp FLAVOR_FLAGS=-DERTS_SMP ENABLE_ALLOC_TYPE_VARS += smp nofrag M4FLAGS += -DERTS_SMP=1 +ifeq ($(DIRTY_SCHEDULER_SUPPORT),yes) +THR_DEFS += -DERTS_DIRTY_SCHEDULERS +endif + else # If flavor isn't one of the above, it *is* plain flavor... @@ -182,7 +188,6 @@ FLAVOR_MARKER= FLAVOR_FLAGS= ENABLE_ALLOC_TYPE_VARS += nofrag M4FLAGS += - endif TF_MARKER=$(TYPEMARKER)$(FLAVOR_MARKER) diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h index 2700b62854..6ec5fbb895 100644 --- a/erts/emulator/beam/erl_drv_nif.h +++ b/erts/emulator/beam/erl_drv_nif.h @@ -43,12 +43,11 @@ typedef struct { int suggested_stack_size; } ErlDrvThreadOpts; -#if defined(ERL_DRV_DIRTY_SCHEDULER_SUPPORT) || defined(ERL_NIF_DIRTY_SCHEDULER_SUPPORT) + typedef enum { - ERL_DRV_DIRTY_JOB_CPU_BOUND = 1, - ERL_DRV_DIRTY_JOB_IO_BOUND = 2 -} ErlDrvDirtyJobFlags; -#endif + ERL_DIRTY_JOB_CPU_BOUND = 1, + ERL_DIRTY_JOB_IO_BOUND = 2 +} ErlDirtyJobFlags; #ifdef SIZEOF_CHAR # define SIZEOF_CHAR_SAVED__ SIZEOF_CHAR diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 3964f7f679..02c82415fd 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -28,7 +28,6 @@ # include "config.h" #endif -#include "erl_native_features_config.h" #include "erl_drv_nif.h" /* Version history: @@ -167,13 +166,11 @@ typedef int ErlNifTSDKey; typedef ErlDrvThreadOpts ErlNifThreadOpts; -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT typedef enum { - ERL_NIF_DIRTY_JOB_CPU_BOUND = ERL_DRV_DIRTY_JOB_CPU_BOUND, - ERL_NIF_DIRTY_JOB_IO_BOUND = ERL_DRV_DIRTY_JOB_IO_BOUND + ERL_NIF_DIRTY_JOB_CPU_BOUND = ERL_DIRTY_JOB_CPU_BOUND, + ERL_NIF_DIRTY_JOB_IO_BOUND = ERL_DIRTY_JOB_IO_BOUND }ErlNifDirtyTaskFlags; -#endif typedef struct /* All fields all internal and may change */ { @@ -257,11 +254,7 @@ extern TWinDynNifCallbacks WinDynNifCallbacks; # define ERL_NIF_INIT_DECL(MODNAME) ERL_NIF_INIT_EXPORT ErlNifEntry* nif_init(ERL_NIF_INIT_ARGS) #endif -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -# define ERL_NIF_ENTRY_OPTIONS ERL_NIF_DIRTY_NIF_OPTION -#else -# define ERL_NIF_ENTRY_OPTIONS 0 -#endif +#define ERL_NIF_ENTRY_OPTIONS ERL_NIF_DIRTY_NIF_OPTION #ifdef __cplusplus } diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index a5acd86551..93e4313791 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -172,20 +172,11 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, E ERL_NIF_API_FUNC_DECL(int, enif_term_to_binary, (ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)); ERL_NIF_API_FUNC_DECL(size_t, enif_binary_to_term, (ErlNifEnv *env, const unsigned char* data, size_t sz, ERL_NIF_TERM *term, unsigned int opts)); ERL_NIF_API_FUNC_DECL(int, enif_port_command, (ErlNifEnv *env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)); +ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! */ - - -/* - * Conditional EXPERIMENTAL stuff always last. - * Must be moved up and made unconditional to support binary backward - * compatibility on Windows. - */ -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); -#endif #endif /* ERL_NIF_API_FUNC_DECL */ /* @@ -336,6 +327,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_term_to_binary ERL_NIF_API_FUNC_MACRO(enif_term_to_binary) # define enif_binary_to_term ERL_NIF_API_FUNC_MACRO(enif_binary_to_term) # define enif_port_command ERL_NIF_API_FUNC_MACRO(enif_port_command) +# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler) /* ** ADD NEW ENTRIES HERE (before this comment) @@ -346,9 +338,6 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); * Must be moved up and made unconditional to support binary backward * compatibility on Windows. */ -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler) -#endif #endif /* ERL_NIF_API_FUNC_MACRO */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index b9f6ab04c6..c35d80652f 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -7881,11 +7881,13 @@ driver_system_info(ErlDrvSysInfo *sip, size_t si_size) * (driver version 3.1, NIF version 2.7) */ if (si_size >= ERL_DRV_SYS_INFO_SIZE(dirty_scheduler_support)) { -#if defined(ERL_NIF_DIRTY_SCHEDULER_SUPPORT) && defined(USE_THREADS) - sip->dirty_scheduler_support = 1; + sip->dirty_scheduler_support = +#ifdef ERTS_DIRTY_SCHEDULERS + 1 #else - sip->dirty_scheduler_support = 0; + 0 #endif + ; } } diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 5a5021b003..439fe86143 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -842,7 +842,6 @@ int main(int argc, char **argv) if (argv[i][3] != '\0') goto the_default; } -#ifdef ERTS_DIRTY_SCHEDULERS else if (argv[i][2] == 'D') { char* type = argv[i]+3; if (strncmp(type, "cpu", 3) != 0 && @@ -854,7 +853,6 @@ int main(int argc, char **argv) (argv[i][3] == 'i' && argv[i][5] != '\0')) goto the_default; } -#endif else if (argv[i][2] != '\0') goto the_default; if (i+1 >= argc) diff --git a/erts/include/erl_native_features_config.h.in b/erts/include/erl_native_features_config.h.in deleted file mode 100644 index 59a5dde09e..0000000000 --- a/erts/include/erl_native_features_config.h.in +++ /dev/null @@ -1,22 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2004-2016. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -/* Dirty scheduler support */ -#undef ERL_NIF_DIRTY_SCHEDULER_SUPPORT diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in index 6e2f236bdf..601f3917a8 100644 --- a/erts/lib_src/Makefile.in +++ b/erts/lib_src/Makefile.in @@ -465,7 +465,6 @@ RELSYSDIR = $(RELEASE_PATH)/erts-$(VSN) RELEASE_INCLUDES= \ $(ERTS_INCL)/erl_memory_trace_parser.h \ $(ERTS_INCL)/$(TARGET)/erl_int_sizes_config.h \ - $(ERTS_INCL)/$(TARGET)/erl_native_features_config.h \ $(ERTS_INCL)/erl_fixed_size_int_types.h RELEASE_LIBS=$(ERTS_LIBS) -- cgit v1.2.3 From 629163f45f4595e2054e1c6bb5595faf4e3f7d0a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 11 May 2016 10:51:14 +0200 Subject: erts: Add max_heap_size remote gc testcase --- erts/emulator/test/gc_SUITE.erl | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl index 79c229a34d..8a600b7d9f 100644 --- a/erts/emulator/test/gc_SUITE.erl +++ b/erts/emulator/test/gc_SUITE.erl @@ -25,13 +25,13 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0]). --export([grow_heap/1, grow_stack/1, grow_stack_heap/1]). +-export([grow_heap/1, grow_stack/1, grow_stack_heap/1, max_heap_size/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [grow_heap, grow_stack, grow_stack_heap]. + [grow_heap, grow_stack, grow_stack_heap, max_heap_size]. %% Produce a growing list of elements, @@ -163,3 +163,30 @@ show_heap(String) -> {stack_size, SSize}=process_info(self(), stack_size), io:format("Heap/Stack "++String++"~p/~p", [HSize, SSize]). +%% Test that doing a remote GC that triggers the max heap size +%% kills the process. +max_heap_size(_Config) -> + + Pid = spawn_opt(fun long_receive/0,[{max_heap_size, 1024}, + {message_queue_data, on_heap}]), + [Pid ! lists:duplicate(I,I) || I <- lists:seq(1,100)], + Ref = erlang:monitor(process, Pid), + + %% Force messages to be viewed as part of heap + erlang:process_info(Pid, messages), + + %% Do the GC that triggers max heap + erlang:garbage_collect(Pid), + + %% Verify that max heap was triggered + receive + {'DOWN', Ref, process, Pid, killed} -> ok + after 5000 -> + ct:fail({process_did_not_die, Pid, erlang:process_info(Pid)}) + end. + +long_receive() -> + receive + after 10000 -> + ok + end. -- cgit v1.2.3 From 7814ec18b095d40af95f362ff668a68915982e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20L=C3=A5ng?= Date: Wed, 11 May 2016 13:39:53 +0200 Subject: hipe_llvm: Allow LLVM-generated constants Since 3.7, LLVM sometimes generates SSE constants in a special constant section with the requisite alignment (".rodata.cst16"). This broke hipe_llvm since it assumed that all constants that were linked from the text section were constants generated by hipe_llvm. As this is the first time alignments larger than 8 have been required, some small changes were required to hipe_consttab and hipe_bifs:alloc_data/2. Note that hipe_bifs:alloc_data/2 still assumes that erl_alloc will provide the requisite alignment. --- erts/emulator/hipe/hipe_bif0.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 48cfafb8c5..3f95fb67eb 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -418,6 +418,8 @@ BIF_RETTYPE hipe_bifs_enter_code_2(BIF_ALIST_2) BIF_RET(make_tuple(hp)); } +#define IS_POWER_OF_TWO(Val) (((Val) > 0) && (((Val) & ((Val)-1)) == 0)) + /* * Allocate memory for arbitrary non-Erlang data. */ @@ -427,16 +429,18 @@ BIF_RETTYPE hipe_bifs_alloc_data_2(BIF_ALIST_2) void *block; if (is_not_small(BIF_ARG_1) || is_not_small(BIF_ARG_2) || - (align = unsigned_val(BIF_ARG_1), - align != sizeof(long) && align != sizeof(double))) + (align = unsigned_val(BIF_ARG_1), !IS_POWER_OF_TWO(align))) BIF_ERROR(BIF_P, BADARG); nrbytes = unsigned_val(BIF_ARG_2); if (nrbytes == 0) BIF_RET(make_small(0)); block = erts_alloc(ERTS_ALC_T_HIPE, nrbytes); - if ((unsigned long)block & (align-1)) + if ((unsigned long)block & (align-1)) { fprintf(stderr, "%s: erts_alloc(%lu) returned %p which is not %lu-byte aligned\r\n", __FUNCTION__, (unsigned long)nrbytes, block, (unsigned long)align); + erts_free(ERTS_ALC_T_HIPE, block); + BIF_ERROR(BIF_P, EXC_NOTSUP); + } BIF_RET(address_to_term(block, BIF_P)); } -- cgit v1.2.3 From cedf53ed9d29e7a7cf8ebaf77dea971a025205e1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 11 May 2016 15:03:42 +0200 Subject: erts: Fix non-smp max_heap_size assert --- erts/emulator/beam/erl_gc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index fa0782867d..bd238d0f45 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -664,8 +664,6 @@ do_major_collection: ErtsGcQuickSanityCheck(p); - erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC); - /* Max heap size has been reached and the process was configured to be killed, so we kill it and set it in a delayed garbage collecting state. There should be no gc_end trace or @@ -3176,8 +3174,8 @@ reached_max_heap_size(Process *p, Uint total_heap_size, if (alive) erts_dsprintf(dsbufp, "on node ~p"); erts_dsprintf(dsbufp, "~n Context: maximum heap size reached~n"); - erts_dsprintf(dsbufp, " Max heap size: ~p~n"); - erts_dsprintf(dsbufp, " Total heap size: ~p~n"); + erts_dsprintf(dsbufp, " Max Heap Size: ~p~n"); + erts_dsprintf(dsbufp, " Total Heap Size: ~p~n"); erts_dsprintf(dsbufp, " Kill: ~p~n"); erts_dsprintf(dsbufp, " Error Logger: ~p~n"); erts_dsprintf(dsbufp, " GC Info: ~p~n"); -- cgit v1.2.3 From 8d8d8c3817988a50ed1e058e73b247ce0a8c3616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 11 May 2016 16:17:49 +0200 Subject: erts: Add enif_snprintf * Add the capability to format erlang terms to a char buffer in nifs. * Bump NIF version to 2.11 --- erts/emulator/beam/erl_nif.c | 10 ++++++++++ erts/emulator/beam/erl_nif.h | 3 ++- erts/emulator/beam/erl_nif_api_funcs.h | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index a695a028ba..fa20ce3c86 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1603,6 +1603,16 @@ int enif_fprintf(void* filep, const char* format, ...) return ret; } +int enif_snprintf(char *buffer, size_t size, const char* format, ...) +{ + int ret; + va_list arglist; + va_start(arglist, format); + ret = erts_vsnprintf(buffer, size, format, arglist); + va_end(arglist); + return ret; +} + /*********************************************************** ** Memory managed (GC'ed) "resource" objects ** ***********************************************************/ diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 3964f7f679..468f5cf5ec 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -50,9 +50,10 @@ ** 2.8: 18.0 add enif_has_pending_exception ** 2.9: 18.2 enif_getenv ** 2.10: Time API +** 2.11: 19.0 enif_snprintf */ #define ERL_NIF_MAJOR_VERSION 2 -#define ERL_NIF_MINOR_VERSION 10 +#define ERL_NIF_MINOR_VERSION 11 /* * The emulator will refuse to load a nif-lib with a major version diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index a5acd86551..c7389b1626 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -172,6 +172,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, E ERL_NIF_API_FUNC_DECL(int, enif_term_to_binary, (ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)); ERL_NIF_API_FUNC_DECL(size_t, enif_binary_to_term, (ErlNifEnv *env, const unsigned char* data, size_t sz, ERL_NIF_TERM *term, unsigned int opts)); ERL_NIF_API_FUNC_DECL(int, enif_port_command, (ErlNifEnv *env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)); +ERL_NIF_API_FUNC_DECL(int,enif_snprintf,(char * buffer, size_t size, const char *format, ...)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! @@ -336,6 +337,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_term_to_binary ERL_NIF_API_FUNC_MACRO(enif_term_to_binary) # define enif_binary_to_term ERL_NIF_API_FUNC_MACRO(enif_binary_to_term) # define enif_port_command ERL_NIF_API_FUNC_MACRO(enif_port_command) +# define enif_snprintf ERL_NIF_API_FUNC_MACRO(enif_snprintf) /* ** ADD NEW ENTRIES HERE (before this comment) -- cgit v1.2.3 From 8cbf3bd3b9d552423812df4acc7a40d9a23fdeae Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 2 May 2016 16:02:02 +0200 Subject: Add better support for communication with a process executing dirty NIF - Termination of a process... - Modify trace flags of process... - Process info on process... - Register/unregister of name on process... - Set group leader on process... ... while it is executing a dirty NIF. --- erts/doc/src/erl_nif.xml | 245 +++++++--- erts/emulator/beam/beam_bp.c | 8 +- erts/emulator/beam/beam_bp.h | 2 +- erts/emulator/beam/beam_emu.c | 60 ++- erts/emulator/beam/bif.c | 26 +- erts/emulator/beam/bif.h | 42 +- erts/emulator/beam/break.c | 7 +- erts/emulator/beam/erl_alloc.types | 1 + erts/emulator/beam/erl_bif_info.c | 15 +- erts/emulator/beam/erl_bif_unique.c | 4 +- erts/emulator/beam/erl_bits.h | 4 +- erts/emulator/beam/erl_db.c | 2 +- erts/emulator/beam/erl_db_util.c | 2 +- erts/emulator/beam/erl_gc.c | 17 +- erts/emulator/beam/erl_hl_timer.c | 24 +- erts/emulator/beam/erl_message.c | 7 +- erts/emulator/beam/erl_msacc.c | 2 +- erts/emulator/beam/erl_nif.c | 561 +++++++++++++++++------ erts/emulator/beam/erl_nif_api_funcs.h | 2 + erts/emulator/beam/erl_port.h | 39 ++ erts/emulator/beam/erl_process.c | 795 ++++++++++++++++++++++----------- erts/emulator/beam/erl_process.h | 105 +++-- erts/emulator/beam/erl_process_dump.c | 34 +- erts/emulator/beam/erl_time_sup.c | 10 +- erts/emulator/beam/global.h | 1 + erts/emulator/beam/io.c | 4 +- erts/emulator/beam/sys.h | 4 + 27 files changed, 1412 insertions(+), 611 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index ef64ac66dc..fefec3eeb6 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -138,29 +138,6 @@ ok automatically unloaded when the module code that it belongs to is purged by the code server.

-

- As mentioned in the warning text at - the beginning of this document it is of vital importance that a native function - return relatively quickly. It is hard to give an exact maximum amount - of time that a native function is allowed to work, but as a rule of thumb - a well-behaving native function should return to its caller before a - millisecond has passed. This can be achieved using different approaches. - If you have full control over the code to execute in the native - function, the best approach is to divide the work into multiple chunks of - work and call the native function multiple times, either directly from Erlang code - or by having a native function schedule a future NIF call via the - enif_schedule_nif function. Function - enif_consume_timeslice can be - used to help with such work division. In some cases, however, this might not - be possible, e.g. when calling third-party libraries. Then you typically want - to dispatch the work to another thread, return - from the native function, and wait for the result. The thread can send - the result back to the calling thread using message passing. Information - about thread primitives can be found below. If you have built your system - with the currently experimental support for dirty schedulers, - you may want to try out this functionality by dispatching the work to a - dirty NIF, - which does not have the same duration restriction as a normal NIF.

FUNCTIONALITY @@ -328,37 +305,161 @@ ok

- Long-running NIFs -

Native functions - - must normally run quickly, as explained earlier in this document. They - generally should execute for no more than a millisecond. But not all native functions - can execute so quickly; for example, functions that encrypt large blocks of data or - perform lengthy file system operations can often run for tens of seconds or more.

-

If the functionality of a long-running NIF can be split so that its work can be - achieved through a series of shorter NIF calls, the application can either make that series - of NIF calls from the Erlang level, or it can call a NIF that first performs a chunk of the - work, then invokes the enif_schedule_nif - function to schedule another NIF call to perform the next chunk. The final call scheduled - in this manner can then return the overall result. Breaking up a long-running function in - this manner enables the VM to regain control between calls to the NIFs, thereby avoiding - degraded responsiveness, scheduler load balancing problems, and other strange behaviours.

-

A NIF that cannot be split and cannot execute in a millisecond or less is called a "dirty NIF" - because it performs work that the Erlang runtime cannot handle cleanly. - Note that the dirty NIF functionality described here is experimental and that you have to - enable support for dirty schedulers when building OTP in order to try the functionality out. - Applications that make use of such functions must indicate to the runtime that the functions are - dirty so they can be handled specially. To schedule a dirty NIF for execution, the - appropriate flags value can be set for the NIF in its ErlNifFunc - entry, or the application can call enif_schedule_nif, - passing to it a pointer to the dirty NIF to be executed and indicating with the flags - argument whether it expects the operation to be CPU-bound or I/O-bound.

-

Dirty NIF support is available only when the emulator is configured with dirty - schedulers enabled. This feature is currently disabled by default. The Erlang runtime - without SMP support currently do not support dirty schedulers even when the dirty - scheduler support has been enabled. To check at runtime for the presence - of dirty scheduler threads, code can use the - enif_system_info() API function.

+ Long-running NIFs + +

+ As mentioned in the warning text at + the beginning of this document it is of vital importance that a + native function return relatively quickly. It is hard to give an exact + maximum amount of time that a native function is allowed to work, but as a + rule of thumb a well-behaving native function should return to its caller + before a millisecond has passed. This can be achieved using different + approaches. If you have full control over the code to execute in the + native function, the best approach is to divide the work into multiple + chunks of work and call the native function multiple times. In some + cases this might however not always be possible, e.g. when calling + third-party libraries.

+ +

The + enif_consume_timeslice() + function can be used to inform the runtime system about the lenght of the + NIF call. It should typically always be used unless the NIF executes very + quickly.

+ +

If the NIF call is too lenghty one needs to handle this in one of the + following ways in order to avoid degraded responsiveness, scheduler load + balancing problems, and other strange behaviours:

+ + + Yielding NIF + +

+ If the functionality of a long-running NIF can be split so that + its work can be achieved through a series of shorter NIF calls, + the application can either make that series of NIF calls from the + Erlang level, or it can call a NIF that first performs a chunk of + the work, then invokes the + enif_schedule_nif + function to schedule another NIF call to perform the next chunk. + The final call scheduled in this manner can then return the + overall result. Breaking up a long-running function in + this manner enables the VM to regain control between calls to the + NIFs. +

+

+ This approach is always preferred over the other alternatives + described below. This both from a performance perspective and + a system characteristics perspective. +

+
+ + Threaded NIF + +

+ This is accomplished by dispatching the work to another thread + managed by the NIF library, return from the NIF, and wait for the + result. The thread can send the result back to the Erlang + process using enif_send. + Information about thread primitives can be found below. +

+
+ + Dirty NIF + + + +

+ The dirty NIF functionality described here + is experimental. Dirty NIF support is available only when + the emulator is configured with dirty schedulers enabled. This + feature is currently disabled by default. The Erlang runtime + without SMP support do not support dirty schedulers even when + the dirty scheduler support has been enabled. To check at + runtime for the presence of dirty scheduler threads, code can + use the + enif_system_info() + API function. +

+
+ +

+ A NIF that cannot be split and cannot execute in a millisecond or + less is called a "dirty NIF" because it performs work that the + Erlang runtime cannot handle cleanly. Applications that make use + of such functions must indicate to the runtime that the functions + are dirty so they can be handled specially. To schedule a dirty + NIF for execution, the appropriate flags value can be set for the + NIF in its ErlNifFunc + entry, or the application can call + enif_schedule_nif, + passing to it a pointer to the dirty NIF to be executed and + indicating with the flags argument whether it expects the + operation to be CPU-bound or I/O-bound. A dirty NIF executing + on a dirty scheduler does not have the same duration restriction + as a normal NIF. +

+ +

+ While a process is executing a dirty NIF some operations that + communicate with it may take a very long time to complete. + Suspend, or garbage collection of a process executing a dirty + NIF cannot be done until the dirty NIF has returned, so other + processes waiting for such operations to complete might have to + wait for a very long time. Blocking multi scheduling, i.e., + calling + erlang:system_flag(multi_scheduling, + block), might also take a very long time to + complete. This since all ongoing dirty operations on all + dirty schedulers need to complete before the the block + operation can complete. +

+ +

+ A lot of operations communicating with a process executing a + dirty NIF can, however, complete while it is executing the + dirty NIF. For example, retreiving information about it via + process_info(), setting its group leader, + register/unregister its name, etc. +

+ +

+ Termination of a process executing a dirty NIF can only be + completed up to a certain point while it is executing the + dirty NIF. All Erlang resources such as registered names, + ETS tables, etc will be released. All links and monitors + will be triggered. The actual execution of the NIF will + however not be stopped. The NIF can safely contiue + execution, allocate heap memory, etc, but it is of course better + to stop executing as soon as possible. The NIF can check + whether current process is alive or not using + enif_is_current_process_alive. + Communication using + enif_send, + and enif_port_command + will also be dropped when the sending process is not alive. + Deallocation of certain internal resources such as process + heap, and process control block will be delayed until the + dirty NIF has completed. +

+ +

Currently known issues that are planned to be fixed:

+ + +

+ Since purging of a module currently might need to garbage + collect a process in order to determine if it has + references to the module, a process executing a dirty + NIF might delay purging for a very long time. Delaying + a purge operatin implies delaying all code + loding operations which might cause severe problems for + the system as a whole. +

+
+
+ +
+
+
@@ -507,6 +608,10 @@ typedef struct { CPU-bound, its flags field should be set to ERL_NIF_DIRTY_JOB_CPU_BOUND, or for I/O-bound jobs, ERL_NIF_DIRTY_JOB_IO_BOUND.

+

If one of the + ERL_NIF_DIRTY_JOB_*_BOUND flags is set, and the runtime + system has no support for dirty schedulers, the runtime system + will refuse to load the NIF library.

ErlNifBinary @@ -962,6 +1067,13 @@ typedef enum { Determine if a term is a binary

Return true if term is a binary

+ intenif_is_current_process_alive(ErlNifEnv* env) + Determine if currently executing process is alive or not. +

Return true if currently executing process is currently alive; otherwise + false.

+

This function can only be used from a NIF-calling thread, and with an + environment corresponding to currently executing processes.

+
intenif_is_empty_list(ErlNifEnv* env, ERL_NIF_TERM term) Determine if a term is an empty list

Return true if term is an empty list.

@@ -992,11 +1104,10 @@ typedef enum { intenif_is_on_dirty_scheduler(ErlNifEnv* env) Check to see if executing on a dirty scheduler thread -

Check to see if the current NIF is executing on a dirty scheduler thread. If the - emulator is built with threading support, calling enif_is_on_dirty_scheduler - from within a dirty NIF returns true. It returns false when the calling NIF is a regular - NIF running on a normal scheduler thread, or when the emulator is built without threading - support.

+

Check to see if the current NIF is executing on a dirty scheduler thread. If + executing on a dirty scheduler thread true returned; otherwise false.

+

This function can only be used from a NIF-calling thread, and with an + environment corresponding to currently executing processes.

intenif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term) @@ -1010,7 +1121,8 @@ typedef enum { intenif_is_port_alive(ErlNifEnv* env, ErlNifPort *port_id) Determine if a local port is alive or not.

Return true if port_id is currently alive.

-

This function can only be used in a from a NIF-calling thread.

+

This function is only thread-safe when the emulator with SMP support is used. + It can only be used in a non-SMP emulator from a NIF-calling thread.

intenif_is_process_alive(ErlNifEnv* env, ErlNifPid *pid) Determine if a local process is alive or not. @@ -1478,9 +1590,7 @@ enif_map_iterator_destroy(env, &iter); Send a port_command to to_port

This function works the same as erlang:port_command/2 - except that it is always completely asynchronous. This call may return false - if it detects that the port is already dead, otherwise it will return true. -

+ except that it is always completely asynchronous.

env The environment of the calling process. May not be NULL. @@ -1499,7 +1609,10 @@ enif_map_iterator_destroy(env, &iter); calls to enif_alloc_env, enif_make_copy, enif_port_command and enif_free_env into one call. This optimization is only usefull when a majority of the terms are to be copied from env to the msg_env.

-

The call may return false if it detects that the command failed for some reason. Otherwise true is returned.

+

This function return true if the command was successfully sent; otherwise, + false. The call may return false if it detects that the command failed for some + reason. For example, *to_port does not refer to a local port, if currently + executing process, i.e. the sender, is not alive, or if msg is invalid.

See also: enif_get_local_port.

@@ -1630,7 +1743,9 @@ enif_map_iterator_destroy(env, &iter); msg The message term to send. -

Return true on success, or false if *to_pid does not refer to an alive local process.

+

Return true if the message was successfully sent; otherwise, false. The send + operation will fail if *to_pid does not refer to an alive local process, + or if currently executing process, i.e. the sender, is not alive.

The message environment msg_env with all its terms (including msg) will be invalidated by a successful call to enif_send. The environment should either be freed with enif_free_env diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 1e30e8d8d1..308e5ce205 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -82,7 +82,7 @@ erts_smp_atomic32_t erts_staging_bp_index; static ERTS_INLINE ErtsMonotonicTime get_mtime(Process *c_p) { - return erts_get_monotonic_time(ERTS_PROC_GET_SCHDATA(c_p)); + return erts_get_monotonic_time(erts_proc_sched_data(c_p)); } /* ************************************************************************* @@ -976,7 +976,8 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt) BpDataTime *pbdt = NULL; ASSERT(c_p); - ASSERT(erts_smp_atomic32_read_acqb(&c_p->state) & ERTS_PSFLG_RUNNING); + ASSERT(erts_smp_atomic32_read_acqb(&c_p->state) & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING)); /* get previous timestamp and breakpoint * from the process psd */ @@ -1053,7 +1054,8 @@ erts_trace_time_return(Process *p, BeamInstr *pc) BpDataTime *pbdt = NULL; ASSERT(p); - ASSERT(erts_smp_atomic32_read_acqb(&p->state) & ERTS_PSFLG_RUNNING); + ASSERT(erts_smp_atomic32_read_acqb(&p->state) & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING)); /* get previous timestamp and breakpoint * from the process psd */ diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index 9c2fc007a2..541af77211 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -80,7 +80,7 @@ typedef struct generic_bp { #define ERTS_BP_CALL_TIME_SCHEDULE_EXITING (2) #ifdef ERTS_SMP -#define bp_sched2ix_proc(p) ((p)->scheduler_data->no - 1) +#define bp_sched2ix_proc(p) (erts_proc_sched_data(p)->no - 1) #else #define bp_sched2ix_proc(p) (0) #endif diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 99eb26bec5..9402317185 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -64,18 +64,21 @@ # ifdef ERTS_SMP # define PROCESS_MAIN_CHK_LOCKS(P) \ do { \ - if ((P)) { \ + if ((P)) \ erts_proc_lc_chk_only_proc_main((P)); \ - } \ - else \ - erts_lc_check_exact(NULL, 0); \ - ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); \ + ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); \ +} while (0) +# define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) \ +do { \ + if ((P)) \ + erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN, \ + __FILE__, __LINE__); \ +} while (0) +# define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \ +do { \ + if ((P)) \ + erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN); \ } while (0) -# define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) \ - if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN,\ - __FILE__, __LINE__) -# define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \ - if ((P)) erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN) # else # define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) # define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) @@ -1202,12 +1205,12 @@ init_emulator(void) do { \ if (ERTS_PROC_GET_SAVED_CALLS_BUF((P))) { \ ASSERT(FC <= 0); \ - ASSERT(ERTS_PROC_GET_SCHDATA(c_p)->virtual_reds \ + ASSERT(erts_proc_sched_data(c_p)->virtual_reds \ <= 0 - (FC)); \ } \ else { \ ASSERT(FC <= CONTEXT_REDS); \ - ASSERT(ERTS_PROC_GET_SCHDATA(c_p)->virtual_reds \ + ASSERT(erts_proc_sched_data(c_p)->virtual_reds \ <= CONTEXT_REDS - (FC)); \ } \ } while (0) @@ -1321,8 +1324,8 @@ void process_main(void) if (start_time != 0) { Sint64 diff = erts_timestamp_millis() - start_time; if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule -#ifdef ERTS_DIRTY_SCHEDULERS - && !ERTS_SCHEDULER_IS_DIRTY(c_p->scheduler_data) +#if defined(ERTS_SMP) && defined(ERTS_DIRTY_SCHEDULERS) + && !ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(c_p)) #endif ) { BeamInstr *inptr = find_function_from_pc(start_time_i); @@ -1351,8 +1354,8 @@ void process_main(void) start_time_i = c_p->i; } - reg = ERTS_PROC_GET_SCHDATA(c_p)->x_reg_array; - freg = ERTS_PROC_GET_SCHDATA(c_p)->f_reg_array; + reg = erts_proc_sched_data(c_p)->x_reg_array; + freg = erts_proc_sched_data(c_p)->f_reg_array; ERL_BITS_RELOAD_STATEP(c_p); { int reds; @@ -3524,18 +3527,27 @@ do { \ typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]); NifF* fp = vbf = (NifF*) I[1]; struct enif_environment_t env; +#ifdef ERTS_DIRTY_SCHEDULERS + if (!c_p->scheduler_data) + live_hf_end = ERTS_INVALID_HFRAG_PTR; /* On dirty scheduler */ + else +#endif + live_hf_end = c_p->mbuf; erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL); - live_hf_end = c_p->mbuf; nif_bif_result = (*fp)(&env, bif_nif_arity, reg); if (env.exception_thrown) nif_bif_result = THE_NON_VALUE; erts_post_nif(&env); - } - ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result)); - PROCESS_MAIN_CHK_LOCKS(c_p); - ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR); + PROCESS_MAIN_CHK_LOCKS(c_p); + ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR); + if (env.exiting) { + ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); + goto do_schedule; + } + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + } DTRACE_NIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); goto apply_bif_or_nif_epilogue; @@ -4887,8 +4899,8 @@ do { \ #ifdef DEBUG pid = c_p->common.id; /* may have switched process... */ #endif - reg = ERTS_PROC_GET_SCHDATA(c_p)->x_reg_array; - freg = ERTS_PROC_GET_SCHDATA(c_p)->f_reg_array; + reg = erts_proc_sched_data(c_p)->x_reg_array; + freg = erts_proc_sched_data(c_p)->f_reg_array; ERL_BITS_RELOAD_STATEP(c_p); /* XXX: this abuse of def_arg_reg[] is horrid! */ neg_o_reds = -c_p->def_arg_reg[4]; diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index eed0702688..4516d53284 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1686,7 +1686,7 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2) ERTS_PSFLG_BOUND); } - curr = ERTS_GET_SCHEDULER_DATA_FROM_PROC(BIF_P)->run_queue; + curr = erts_proc_sched_data(BIF_P)->run_queue; old = (ERTS_PSFLG_BOUND & state) ? curr : NULL; ASSERT(!old || old == curr); @@ -4198,8 +4198,28 @@ BIF_RETTYPE group_leader_2(BIF_ALIST_2) else { locks &= ~ERTS_PROC_LOCK_STATUS; erts_smp_proc_unlock(new_member, ERTS_PROC_LOCK_STATUS); - new_member->group_leader = STORE_NC_IN_PROC(new_member, - BIF_ARG_1); + if (erts_smp_atomic32_read_nob(&new_member->state) + & !(ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + new_member->group_leader = STORE_NC_IN_PROC(new_member, + BIF_ARG_1); + } + else { + ErlHeapFragment *bp; + Eterm *hp; + /* + * Other process executing on a dirty scheduler, + * so we are not allowed to write to its heap. + * Store in heap fragment. + */ + + bp = new_message_buffer(NC_HEAP_SIZE(BIF_ARG_1)); + hp = bp->mem; + new_member->group_leader = STORE_NC(&hp, + &new_member->off_heap, + BIF_ARG_1); + bp->next = new_member->mbuf; + new_member->mbuf = bp; + } } } diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index 5d751dd67d..2203182a0d 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -59,12 +59,12 @@ extern Export *erts_convert_time_unit_trap; do { \ if (!ERTS_PROC_GET_SAVED_CALLS_BUF((p))) { \ if ((fcalls) > 0) \ - ERTS_PROC_GET_SCHDATA((p))->virtual_reds += (fcalls); \ + erts_proc_sched_data((p))->virtual_reds += (fcalls); \ (fcalls) = 0; \ } \ else { \ if ((fcalls) > -CONTEXT_REDS) \ - ERTS_PROC_GET_SCHDATA((p))->virtual_reds \ + erts_proc_sched_data((p))->virtual_reds \ += ((fcalls) - (-CONTEXT_REDS)); \ (fcalls) = -CONTEXT_REDS; \ } \ @@ -91,22 +91,22 @@ do { \ if (!ERTS_PROC_GET_SAVED_CALLS_BUF((p))) { \ if ((p)->fcalls >= reds) { \ (p)->fcalls -= reds; \ - ERTS_PROC_GET_SCHDATA((p))->virtual_reds += reds; \ + erts_proc_sched_data((p))->virtual_reds += reds; \ } \ else { \ if ((p)->fcalls > 0) \ - ERTS_PROC_GET_SCHDATA((p))->virtual_reds += (p)->fcalls;\ + erts_proc_sched_data((p))->virtual_reds += (p)->fcalls; \ (p)->fcalls = 0; \ } \ } \ else { \ if ((p)->fcalls >= reds - CONTEXT_REDS) { \ (p)->fcalls -= reds; \ - ERTS_PROC_GET_SCHDATA((p))->virtual_reds += reds; \ + erts_proc_sched_data((p))->virtual_reds += reds; \ } \ else { \ if ((p)->fcalls > -CONTEXT_REDS) \ - ERTS_PROC_GET_SCHDATA((p))->virtual_reds \ + erts_proc_sched_data((p))->virtual_reds \ += (p)->fcalls - (-CONTEXT_REDS); \ (p)->fcalls = -CONTEXT_REDS; \ } \ @@ -118,14 +118,14 @@ do { \ if (ERTS_PROC_GET_SAVED_CALLS_BUF((P))) { \ int nreds__ = ((int)(Reds)) - CONTEXT_REDS; \ if ((FCalls) > nreds__) { \ - ERTS_PROC_GET_SCHDATA((P))->virtual_reds \ + erts_proc_sched_data((P))->virtual_reds \ += (FCalls) - nreds__; \ (FCalls) = nreds__; \ } \ } \ else { \ if ((FCalls) > (Reds)) { \ - ERTS_PROC_GET_SCHDATA((P))->virtual_reds \ + erts_proc_sched_data((P))->virtual_reds \ += (FCalls) - (Reds); \ (FCalls) = (Reds); \ } \ @@ -165,7 +165,7 @@ do { \ #define ERTS_BIF_ERROR_TRAPPED1(Proc, Reason, Bif, A0) \ do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \ (Proc)->freason = (Reason); \ (Proc)->current = (Bif)->code; \ reg[0] = (Eterm) (A0); \ @@ -174,7 +174,7 @@ do { \ #define ERTS_BIF_ERROR_TRAPPED2(Proc, Reason, Bif, A0, A1) \ do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \ (Proc)->freason = (Reason); \ (Proc)->current = (Bif)->code; \ reg[0] = (Eterm) (A0); \ @@ -184,7 +184,7 @@ do { \ #define ERTS_BIF_ERROR_TRAPPED3(Proc, Reason, Bif, A0, A1, A2) \ do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \ (Proc)->freason = (Reason); \ (Proc)->current = (Bif)->code; \ reg[0] = (Eterm) (A0); \ @@ -208,7 +208,7 @@ do { \ #define ERTS_BIF_PREP_ERROR_TRAPPED1(Ret, Proc, Reason, Bif, A0) \ do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \ (Proc)->freason = (Reason); \ (Proc)->current = (Bif)->code; \ reg[0] = (Eterm) (A0); \ @@ -217,7 +217,7 @@ do { \ #define ERTS_BIF_PREP_ERROR_TRAPPED2(Ret, Proc, Reason, Bif, A0, A1) \ do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \ (Proc)->freason = (Reason); \ (Proc)->current = (Bif)->code; \ reg[0] = (Eterm) (A0); \ @@ -227,7 +227,7 @@ do { \ #define ERTS_BIF_PREP_ERROR_TRAPPED3(Ret, Proc, Reason, Bif, A0, A1, A2) \ do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \ (Proc)->freason = (Reason); \ (Proc)->current = (Bif)->code; \ reg[0] = (Eterm) (A0); \ @@ -246,7 +246,7 @@ do { \ #define ERTS_BIF_PREP_TRAP1(Ret, Trap, Proc, A0) \ do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \ (Proc)->arity = 1; \ reg[0] = (Eterm) (A0); \ (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \ @@ -256,7 +256,7 @@ do { \ #define ERTS_BIF_PREP_TRAP2(Ret, Trap, Proc, A0, A1) \ do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \ (Proc)->arity = 2; \ reg[0] = (Eterm) (A0); \ reg[1] = (Eterm) (A1); \ @@ -267,7 +267,7 @@ do { \ #define ERTS_BIF_PREP_TRAP3(Ret, Trap, Proc, A0, A1, A2) \ do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \ (Proc)->arity = 3; \ reg[0] = (Eterm) (A0); \ reg[1] = (Eterm) (A1); \ @@ -279,7 +279,7 @@ do { \ #define ERTS_BIF_PREP_TRAP3_NO_RET(Trap, Proc, A0, A1, A2)\ do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((Proc))->x_reg_array; \ (Proc)->arity = 3; \ reg[0] = (Eterm) (A0); \ reg[1] = (Eterm) (A1); \ @@ -296,7 +296,7 @@ do { \ } while(0) #define BIF_TRAP1(Trap_, p, A0) do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((p))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((p))->x_reg_array; \ (p)->arity = 1; \ reg[0] = (A0); \ (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \ @@ -305,7 +305,7 @@ do { \ } while(0) #define BIF_TRAP2(Trap_, p, A0, A1) do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((p))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((p))->x_reg_array; \ (p)->arity = 2; \ reg[0] = (A0); \ reg[1] = (A1); \ @@ -315,7 +315,7 @@ do { \ } while(0) #define BIF_TRAP3(Trap_, p, A0, A1, A2) do { \ - Eterm* reg = ERTS_PROC_GET_SCHDATA((p))->x_reg_array; \ + Eterm* reg = erts_proc_sched_data((p))->x_reg_array; \ (p)->arity = 3; \ reg[0] = (A0); \ reg[1] = (A1); \ diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index d02c6828f9..3c19e82b66 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -118,7 +118,9 @@ process_killer(void) | ERTS_PSFLG_ACTIVE_SYS | ERTS_PSFLG_IN_RUNQ | ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS)) { + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { erts_printf("Can only kill WAITING processes this way\n"); } else { @@ -214,7 +216,8 @@ print_process_info(int to, void *to_arg, Process *p) if (state & ERTS_PSFLG_GC) { garbing = 1; running = 1; - } else if (state & ERTS_PSFLG_RUNNING) + } else if (state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING)) running = 1; /* diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index ba216c7eb4..227fedfb69 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -278,6 +278,7 @@ type IOB_REQ SHORT_LIVED SYSTEM io_bytes_request type TRACER_NIF LONG_LIVED SYSTEM tracer_nif type TRACE_MSG_QUEUE SHORT_LIVED SYSTEM trace_message_queue type SCHED_ASYNC_JOB SHORT_LIVED SYSTEM async_calls +type DIRTY_START STANDARD PROCESSES dirty_start +if threads_no_smp # Need thread safe allocs, but std_alloc and fix_alloc are not; diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 85cadafebc..b12301812a 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1107,7 +1107,7 @@ process_info_aux(Process *BIF_P, break; case am_status: - res = erts_process_status(BIF_P, ERTS_PROC_LOCK_MAIN, rp, rpid); + res = erts_process_status(rp, rpid); ASSERT(res != am_undefined); hp = HAlloc(BIF_P, 3); break; @@ -2035,12 +2035,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) Uint arity = *tp++; return info_1_tuple(BIF_P, tp, arityval(arity)); } else if (BIF_ARG_1 == am_scheduler_id) { -#ifdef ERTS_SMP - ASSERT(BIF_P->scheduler_data); - BIF_RET(make_small(BIF_P->scheduler_data->no)); -#else - BIF_RET(make_small(1)); -#endif + ErtsSchedulerData *esdp = erts_proc_sched_data(BIF_P); + BIF_RET(make_small(esdp->no)); } else if (BIF_ARG_1 == am_compat_rel) { ASSERT(erts_compat_rel > 0); BIF_RET(make_small(erts_compat_rel)); @@ -3589,10 +3585,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) /* Used by timer process_SUITE, timer_bif_SUITE, and node_container_SUITE (emulator) */ if (is_internal_pid(tp[2])) { - BIF_RET(erts_process_status(BIF_P, - ERTS_PROC_LOCK_MAIN, - NULL, - tp[2])); + BIF_RET(erts_process_status(NULL, tp[2])); } } else if (ERTS_IS_ATOM_STR("link_list", tp[1])) { diff --git a/erts/emulator/beam/erl_bif_unique.c b/erts/emulator/beam/erl_bif_unique.c index 1e57e9fa53..7c70217d8d 100644 --- a/erts/emulator/beam/erl_bif_unique.c +++ b/erts/emulator/beam/erl_bif_unique.c @@ -257,7 +257,7 @@ static ERTS_INLINE Eterm unique_integer_bif(Process *c_p, int positive) Uint hsz; Eterm *hp; - esdp = ERTS_PROC_GET_SCHDATA(c_p); + esdp = erts_proc_sched_data(c_p); thr_id = (Uint64) esdp->thr_id; unique = esdp->unique++; bld_unique_integer_term(NULL, &hsz, thr_id, unique, positive); @@ -515,7 +515,7 @@ BIF_RETTYPE make_ref_0(BIF_ALIST_0) hp = HAlloc(BIF_P, REF_THING_SIZE); - res = erts_sched_make_ref_in_buffer(ERTS_PROC_GET_SCHDATA(BIF_P), hp); + res = erts_sched_make_ref_in_buffer(erts_proc_sched_data(BIF_P), hp); BIF_RET(res); } diff --git a/erts/emulator/beam/erl_bits.h b/erts/emulator/beam/erl_bits.h index 1c2a090f07..4bd5b24157 100644 --- a/erts/emulator/beam/erl_bits.h +++ b/erts/emulator/beam/erl_bits.h @@ -83,8 +83,8 @@ typedef struct erl_bin_match_struct{ #ifdef ERTS_SMP /* the state resides in the current process' scheduler data */ #define ERL_BITS_DECLARE_STATEP struct erl_bits_state *EBS -#define ERL_BITS_RELOAD_STATEP(P) do{EBS = &(P)->scheduler_data->erl_bits_state;}while(0) -#define ERL_BITS_DEFINE_STATEP(P) struct erl_bits_state *EBS = &(P)->scheduler_data->erl_bits_state +#define ERL_BITS_RELOAD_STATEP(P) do{EBS = &erts_proc_sched_data((P))->erl_bits_state;}while(0) +#define ERL_BITS_DEFINE_STATEP(P) struct erl_bits_state *EBS = &erts_proc_sched_data((P))->erl_bits_state #else /* reentrant API but with a hidden single global state, for testing only */ extern struct erl_bits_state ErlBitsState_; diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index acaca54e9a..bad34211a5 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -3462,7 +3462,7 @@ static void fix_table_locked(Process* p, DbTable* tb) fix = tb->common.fixations; if (fix == NULL) { tb->common.time.monotonic - = erts_get_monotonic_time(ERTS_PROC_GET_SCHDATA(p)); + = erts_get_monotonic_time(erts_proc_sched_data(p)); tb->common.time.offset = erts_get_time_offset(); } else { diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 95b1cd0148..3e75b9fd5f 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1801,7 +1801,7 @@ Eterm db_prog_match(Process *c_p, /* We need to lure the scheduler into believing in the pseudo process, because of floating point exceptions. Do *after* mpsp is set!!! */ - esdp = ERTS_GET_SCHEDULER_DATA_FROM_PROC(psp); + esdp = erts_get_scheduler_data(); if (esdp) current_scheduled = esdp->current_process; /* SMP: psp->scheduler_data is set by get_match_pseudo_process */ diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index bed7e668d7..201a5acaef 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -389,7 +389,7 @@ erts_gc_after_bif_call_lhf(Process* p, ErlHeapFragment *live_hf_end, if (p->freason == TRAP) { #if HIPE if (regs == NULL) { - regs = ERTS_PROC_GET_SCHDATA(p)->x_reg_array; + regs = erts_proc_sched_data(p)->x_reg_array; } #endif cost = garbage_collect(p, live_hf_end, 0, regs, p->arity, p->fcalls); @@ -404,6 +404,7 @@ erts_gc_after_bif_call_lhf(Process* p, ErlHeapFragment *live_hf_end, result = val[0]; } BUMP_REDS(p, cost); + return result; } @@ -507,14 +508,14 @@ delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need, int /* Make sure that we do a proper GC as soon as possible... */ p->flags |= F_FORCE_GC; reds_left = ERTS_REDS_LEFT(p, fcalls); - ASSERT(CONTEXT_REDS - reds_left >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds); + ASSERT(CONTEXT_REDS - reds_left >= erts_proc_sched_data(p)->virtual_reds); if (reds_left > ERTS_ABANDON_HEAP_COST) { int vreds = reds_left - ERTS_ABANDON_HEAP_COST; - ERTS_PROC_GET_SCHDATA((p))->virtual_reds += vreds; + erts_proc_sched_data((p))->virtual_reds += vreds; } - ASSERT(CONTEXT_REDS >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds); + ASSERT(CONTEXT_REDS >= erts_proc_sched_data(p)->virtual_reds); return reds_left; } @@ -586,7 +587,7 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, #endif ASSERT(CONTEXT_REDS - ERTS_REDS_LEFT(p, fcalls) - >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds); + >= erts_proc_sched_data(p)->virtual_reds); if (p->flags & (F_DISABLE_GC|F_DELAY_GC)) return delay_garbage_collection(p, live_hf_end, need, fcalls); @@ -716,7 +717,7 @@ erts_garbage_collect_nobump(Process* p, int need, Eterm* objv, int nobj, int fca int reds_left = ERTS_REDS_LEFT(p, fcalls); if (reds > reds_left) reds = reds_left; - ASSERT(CONTEXT_REDS - (reds_left - reds) >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds); + ASSERT(CONTEXT_REDS - (reds_left - reds) >= erts_proc_sched_data(p)->virtual_reds); return reds; } @@ -726,7 +727,7 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, p->fcalls); BUMP_REDS(p, reds); ASSERT(CONTEXT_REDS - ERTS_BIF_REDS_LEFT(p) - >= ERTS_PROC_GET_SCHDATA(p)->virtual_reds); + >= erts_proc_sched_data(p)->virtual_reds); } /* @@ -2955,7 +2956,7 @@ reply_gc_info(void *vgcirp) Eterm erts_gc_info_request(Process *c_p) { - ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p); + ErtsSchedulerData *esdp = erts_proc_sched_data(c_p); Eterm ref; ErtsGCInfoReq *gcirp; Eterm *hp; diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index c418762578..ebeff51aac 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -1766,7 +1766,7 @@ setup_bif_timer(Process *c_p, ErtsMonotonicTime timeout_pos, if (is_not_internal_pid(rcvr) && is_not_atom(rcvr)) goto badarg; - esdp = ERTS_PROC_GET_SCHDATA(c_p); + esdp = erts_proc_sched_data(c_p); hp = HAlloc(c_p, REF_THING_SIZE); ref = erts_sched_make_ref_in_buffer(esdp, hp); @@ -1871,7 +1871,7 @@ access_sched_local_btm(Process *c_p, Eterm pid, if (!c_p) esdp = erts_get_scheduler_data(); else { - esdp = ERTS_PROC_GET_SCHDATA(c_p); + esdp = erts_proc_sched_data(c_p); ERTS_HLT_ASSERT(esdp == erts_get_scheduler_data()); } @@ -2138,7 +2138,7 @@ access_bif_timer(Process *c_p, Eterm tref, int cancel, int async, int info) goto no_timer; } - esdp = ERTS_PROC_GET_SCHDATA(c_p); + esdp = erts_proc_sched_data(c_p); trefn = internal_ref_numbers(tref); sid = erts_get_ref_numbers_thr_id(trefn); @@ -2363,7 +2363,7 @@ typedef struct { int erts_cancel_bif_timers(Process *p, ErtsBifTimers *btm, void **vyspp) { - ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(p); + ErtsSchedulerData *esdp = erts_proc_sched_data(p); ErtsBifTimerYieldState ys = {btm, {ERTS_RBT_YIELD_STAT_INITER}}; ErtsBifTimerYieldState *ysp; int res; @@ -2409,7 +2409,7 @@ detach_bif_timer(ErtsHLTimer *tmr, void *vesdp) int erts_detach_accessor_bif_timers(Process *p, ErtsBifTimers *btm, void **vyspp) { - ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(p); + ErtsSchedulerData *esdp = erts_proc_sched_data(p); ErtsBifTimerYieldState ys = {btm, {ERTS_RBT_YIELD_STAT_INITER}}; ErtsBifTimerYieldState *ysp; int res; @@ -2516,7 +2516,7 @@ BIF_RETTYPE send_after_3(BIF_ALIST_3) ErtsMonotonicTime timeout_pos; int short_time, tres; - tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL, + tres = parse_timeout_pos(erts_proc_sched_data(BIF_P), BIF_ARG_1, NULL, 0, &timeout_pos, &short_time); if (tres != 0) BIF_ERROR(BIF_P, BADARG); @@ -2534,7 +2534,7 @@ BIF_RETTYPE send_after_4(BIF_ALIST_4) if (!parse_bif_timer_options(BIF_ARG_4, NULL, NULL, &abs, &accessor)) BIF_ERROR(BIF_P, BADARG); - tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL, + tres = parse_timeout_pos(erts_proc_sched_data(BIF_P), BIF_ARG_1, NULL, abs, &timeout_pos, &short_time); if (tres != 0) BIF_ERROR(BIF_P, BADARG); @@ -2548,7 +2548,7 @@ BIF_RETTYPE start_timer_3(BIF_ALIST_3) ErtsMonotonicTime timeout_pos; int short_time, tres; - tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL, + tres = parse_timeout_pos(erts_proc_sched_data(BIF_P), BIF_ARG_1, NULL, 0, &timeout_pos, &short_time); if (tres != 0) BIF_ERROR(BIF_P, BADARG); @@ -2566,7 +2566,7 @@ BIF_RETTYPE start_timer_4(BIF_ALIST_4) if (!parse_bif_timer_options(BIF_ARG_4, NULL, NULL, &abs, &accessor)) BIF_ERROR(BIF_P, BADARG); - tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL, + tres = parse_timeout_pos(erts_proc_sched_data(BIF_P), BIF_ARG_1, NULL, abs, &timeout_pos, &short_time); if (tres != 0) BIF_ERROR(BIF_P, BADARG); @@ -2720,7 +2720,7 @@ set_proc_timer_common(Process *c_p, ErtsSchedulerData *esdp, Sint64 tmo, int erts_set_proc_timer_term(Process *c_p, Eterm etmo) { - ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p); + ErtsSchedulerData *esdp = erts_proc_sched_data(c_p); ErtsMonotonicTime tmo, timeout_pos; int short_time, tres; @@ -2742,7 +2742,7 @@ erts_set_proc_timer_term(Process *c_p, Eterm etmo) void erts_set_proc_timer_uword(Process *c_p, UWord tmo) { - ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p); + ErtsSchedulerData *esdp = erts_proc_sched_data(c_p); ERTS_HLT_ASSERT(erts_smp_atomic_read_nob(&c_p->common.timer) == ERTS_PTMR_NONE); @@ -2776,7 +2776,7 @@ erts_cancel_proc_timer(Process *c_p) erts_smp_atomic_set_nob(&c_p->common.timer, ERTS_PTMR_NONE); return; } - continue_cancel_ptimer(ERTS_PROC_GET_SCHDATA(c_p), + continue_cancel_ptimer(erts_proc_sched_data(c_p), (ErtsTimer *) tval); } diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 9bb6e40a11..579f6e427d 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -492,7 +492,6 @@ queue_messages(Process* receiver, erts_proc_notify_new_message(receiver, receiver_locks); #else erts_proc_notify_new_message(receiver, 0); - ERTS_HOLE_CHECK(receiver); #endif return res; } @@ -601,7 +600,9 @@ erts_try_alloc_message_on_heap(Process *pp, ASSERT(!(*psp & ERTS_PSFLG_OFF_HEAP_MSGQ)); - if ( + if ((*psp) & ERTS_PSFLGS_VOLATILE_HEAP) + goto in_message_fragment; + else if ( #if defined(ERTS_SMP) *plp & ERTS_PROC_LOCK_MAIN #else @@ -611,7 +612,7 @@ erts_try_alloc_message_on_heap(Process *pp, #ifdef ERTS_SMP try_on_heap: #endif - if ((*psp & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) + if (((*psp) & ERTS_PSFLGS_VOLATILE_HEAP) || (pp->flags & F_DISABLE_GC) || HEAP_LIMIT(pp) - HEAP_TOP(pp) <= sz) { /* diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c index 0e625f213b..544bc8b983 100644 --- a/erts/emulator/beam/erl_msacc.c +++ b/erts/emulator/beam/erl_msacc.c @@ -338,7 +338,7 @@ erts_msacc_request(Process *c_p, int action, Eterm *threads) { #ifdef ERTS_ENABLE_MSACC ErtsMsAcc *msacc = ERTS_MSACC_TSD_GET(); - ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p); + ErtsSchedulerData *esdp = erts_proc_sched_data(c_p); Eterm ref; ErtsMSAccReq *msaccrp; Eterm *hp; diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index a695a028ba..8a3007d52a 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -20,6 +20,23 @@ /* Erlang Native InterFace */ +/* + * Environment contains a pointer to currently executing process. + * In the dirty case this pointer do however not point to the + * actual process structure of the executing process, but instead + * a "shadow process structure". This in order to be able to handle + * heap allocation without the need to acquire the main lock on + * the process. + * + * The dirty process is allowed to allocate on the heap without + * the main lock, i.e., incrementing htop, but is not allowed to + * modify mbuf, offheap, etc without the main lock. The dirty + * process moves mbuf list and offheap list of the shadow process + * structure into the real structure when the dirty nif call + * completes. + */ + + #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -81,6 +98,43 @@ void dtrace_nifenv_str(ErlNifEnv *, char *); #define MIN_HEAP_FRAG_SZ 200 static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp); +static ERTS_INLINE int +is_scheduler(void) +{ + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + if (!esdp) + return 0; + if (ERTS_SCHEDULER_IS_DIRTY(esdp)) + return -1; + return 1; +} + +static ERTS_INLINE void +execution_state(ErlNifEnv *env, Process **c_pp, int *schedp) +{ + if (schedp) + *schedp = is_scheduler(); + if (c_pp) { + if (!env || env->proc->common.id == ERTS_INVALID_PID) + *c_pp = NULL; + else { + Process *c_p = env->proc; + + if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC)) + ASSERT(is_scheduler() > 0); + else { + c_p = env->proc->next; + ASSERT(is_scheduler() < 0); + ASSERT(c_p && env->proc->common.id == c_p->common.id); + } + + *c_pp = c_p; + + ASSERT(!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC)); + } + } +} + static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, unsigned need) { Eterm* hp = env->hp; @@ -124,6 +178,9 @@ static ERTS_INLINE void ensure_heap(ErlNifEnv* env, unsigned may_need) void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, Process* tracee) { +#ifdef ERTS_DIRTY_SCHEDULERS + ErtsSchedulerData *esdp; +#endif env->mod_nif = mod_nif; env->proc = p; env->hp = HEAP_TOP(p); @@ -133,6 +190,61 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, env->tmp_obj_list = NULL; env->exception_thrown = 0; env->tracee = tracee; + + ASSERT(p->common.id != ERTS_INVALID_PID); + +#ifdef ERTS_DIRTY_SCHEDULERS + esdp = erts_get_scheduler_data(); + ASSERT(esdp); + + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { +#ifdef DEBUG + erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state); + + ASSERT(p->scheduler_data == esdp); + ASSERT((state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS)) + && !(state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS))); +#endif + + } + else { + Process *sproc; +#ifdef DEBUG + erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state); + + ASSERT(!p->scheduler_data); + ASSERT((state & ERTS_PSFLG_DIRTY_RUNNING) + && !(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))); +#endif + + sproc = esdp->dirty_shadow_process; + ASSERT(sproc); + ASSERT(sproc->static_flags & ERTS_STC_FLG_SHADOW_PROC); + ASSERT(erts_smp_atomic32_read_nob(&sproc->state) + == (ERTS_PSFLG_ACTIVE + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_PROXY)); + + sproc->next = p; + sproc->common.id = p->common.id; + sproc->htop = p->htop; + sproc->stop = p->stop; + sproc->hend = p->hend; + sproc->heap = p->heap; + sproc->abandoned_heap = p->abandoned_heap; + sproc->heap_sz = p->heap_sz; + sproc->high_water = p->high_water; + sproc->old_hend = p->old_hend; + sproc->old_htop = p->old_htop; + sproc->old_heap = p->old_heap; + sproc->mbuf = NULL; + sproc->mbuf_sz = 0; + ERTS_INIT_OFF_HEAP(&sproc->off_heap); + env->proc = sproc; + } +#endif } /* Temporary object header, auto-deallocated when NIF returns @@ -157,18 +269,75 @@ static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env) void erts_post_nif(ErlNifEnv* env) { erts_unblock_fpe(env->fpe_was_unmasked); - if (env->heap_frag == NULL) { - ASSERT(env->hp_end == HEAP_LIMIT(env->proc)); - ASSERT(env->hp >= HEAP_TOP(env->proc)); - ASSERT(env->hp <= HEAP_LIMIT(env->proc)); - HEAP_TOP(env->proc) = env->hp; + +#ifdef ERTS_DIRTY_SCHEDULERS + if (!(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC)) +#endif + { + ASSERT(is_scheduler() > 0); + if (env->heap_frag == NULL) { + ASSERT(env->hp_end == HEAP_LIMIT(env->proc)); + ASSERT(env->hp >= HEAP_TOP(env->proc)); + ASSERT(env->hp <= HEAP_LIMIT(env->proc)); + HEAP_TOP(env->proc) = env->hp; + } + else { + ASSERT(env->hp_end != HEAP_LIMIT(env->proc)); + ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); + env->heap_frag->used_size = env->hp - env->heap_frag->mem; + ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); + } + env->exiting = ERTS_PROC_IS_EXITING(env->proc); } - else { - ASSERT(env->hp_end != HEAP_LIMIT(env->proc)); - ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); - env->heap_frag->used_size = env->hp - env->heap_frag->mem; - ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); +#ifdef ERTS_DIRTY_SCHEDULERS + else { /* Dirty nif call using shadow process struct */ + Process *c_p = env->proc->next; + + ASSERT(is_scheduler() < 0); + ASSERT(env->proc->common.id == c_p->common.id); + + if (!env->heap_frag) { + ASSERT(env->hp_end == HEAP_LIMIT(c_p)); + ASSERT(env->hp >= HEAP_TOP(c_p)); + ASSERT(env->hp <= HEAP_LIMIT(c_p)); + HEAP_TOP(c_p) = env->hp; + } + else { + ASSERT(env->hp_end != HEAP_LIMIT(c_p)); + ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); + + HEAP_TOP(c_p) = HEAP_TOP(env->proc); + env->heap_frag->used_size = env->hp - env->heap_frag->mem; + + ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); + + if (c_p->mbuf) { + ErlHeapFragment *bp; + for (bp = env->proc->mbuf; bp->next; bp = bp->next) + ; + bp->next = c_p->mbuf; + } + + c_p->mbuf = env->proc->mbuf; + c_p->mbuf_sz += env->proc->mbuf_sz; + + } + + if (!c_p->off_heap.first) + c_p->off_heap.first = env->proc->off_heap.first; + else if (env->proc->off_heap.first) { + struct erl_off_heap_header *ohhp; + for (ohhp = env->proc->off_heap.first; ohhp->next; ohhp = ohhp->next) + ; + ohhp->next = c_p->off_heap.first; + c_p->off_heap.first = env->proc->off_heap.first; + } + c_p->off_heap.overhead += env->proc->off_heap.overhead; + + env->exiting = ERTS_PROC_IS_EXITING(c_p); + BUMP_ALL_REDS(c_p); } +#endif free_tmp_objs(env); } @@ -400,9 +569,8 @@ error: #endif -int -enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, - ErlNifEnv* msg_env, ERL_NIF_TERM msg) +int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, + ErlNifEnv* msg_env, ERL_NIF_TERM msg) { struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)msg_env; ErtsProcLocks rp_locks = 0; @@ -413,34 +581,32 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, Process* c_p; ErtsMessage *mp; Eterm receiver = to_pid->pid; - int flush_me = 0; - ErtsSchedulerData *esdp = erts_get_scheduler_data(); - int scheduler = esdp ? esdp->no : 0; + int scheduler; - if (env != NULL) { - c_p = env->proc; - if (receiver == c_p->common.id) { + execution_state(env, &c_p, &scheduler); + +#ifndef ERTS_SMP + if (!scheduler) { + erts_exit(ERTS_ABORT_EXIT, + "enif_send: called from non-scheduler thread on non-SMP VM"); + return 0; + } +#endif + + if (scheduler > 0) { /* Normal scheduler */ + rp = erts_proc_lookup(receiver); + if (c_p == rp) rp_locks = ERTS_PROC_LOCK_MAIN; - flush_me = 1; - } } else { -#ifdef ERTS_SMP - c_p = NULL; -#else - erts_exit(ERTS_ABORT_EXIT,"enif_send: env==NULL on non-SMP VM"); -#endif + if (c_p && ERTS_PROC_IS_EXITING(c_p)) + return 0; + rp = erts_pid2proc_opt(c_p, 0, receiver, rp_locks, + ERTS_P2P_FLG_INC_REFC); } - - rp = (scheduler - ? erts_proc_lookup(receiver) - : erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN, - receiver, rp_locks, ERTS_P2P_FLG_INC_REFC)); - - if (rp == NULL) { - ASSERT(env == NULL || receiver != c_p->common.id); + if (rp == NULL) return 0; - } + if (menv) { flush_env(msg_env); mp = erts_alloc_message(0, NULL); @@ -465,10 +631,6 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ERL_MESSAGE_TERM(mp) = msg; - if (flush_me) { - flush_env(env); /* Needed for ERTS_HOLE_CHECK */ - } - if (!env || !env->tracee) { if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND)) @@ -546,11 +708,9 @@ done: if (rp_locks & ~lc_locks) erts_smp_proc_unlock(rp, rp_locks & ~lc_locks); #endif - if (!scheduler) + if (scheduler <= 0) erts_proc_dec_refc(rp); - if (flush_me) { - cache_env(env); - } + return 1; } @@ -558,26 +718,52 @@ int enif_port_command(ErlNifEnv *env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg) { - - ErtsSchedulerData *esdp = erts_get_scheduler_data(); - int scheduler = esdp ? esdp->no : 0; + int iflags = (erts_port_synchronous_ops + ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + : ERTS_PORT_SFLGS_INVALID_LOOKUP); + int scheduler; + Process *c_p; Port *prt; + int res; - if (scheduler == 0 || !env) - return 0; + if (!env) + erts_exit(ERTS_ABORT_EXIT, "enif_port_command: env == NULL"); + + execution_state(env, &c_p, &scheduler); + + if (!c_p) + c_p = env->proc; - prt = erts_port_lookup(to_port->port_id, - (erts_port_synchronous_ops - ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP - : ERTS_PORT_SFLGS_INVALID_LOOKUP)); + if (scheduler > 0) + prt = erts_port_lookup(to_port->port_id, iflags); +#ifdef ERTS_DIRTY_SCHEDULERS + else if (scheduler < 0) { + if (ERTS_PROC_IS_EXITING(c_p)) + return 0; + prt = erts_thr_port_lookup(to_port->port_id, iflags); + } +#endif + else { + erts_exit(ERTS_ABORT_EXIT, "enif_port_command: " + "called from non-scheduler thread"); + } if (!prt) - return 0; + res = 0; + else { + + if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) + trace_port_receive(prt, c_p->common.id, am_command, msg); - if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) - trace_port_receive(prt, env->proc->common.id, am_command, msg); + res = erts_port_output_async(prt, c_p->common.id, msg); + } + +#ifdef ERTS_DIRTY_SCHEDULERS + if (scheduler < 0) + erts_port_dec_refc(prt); +#endif - return erts_port_output_async(prt, env->proc->common.id, msg); + return res; } ERL_NIF_TERM enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term) @@ -1039,15 +1225,21 @@ Eterm enif_make_badarg(ErlNifEnv* env) Eterm enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason) { + Process *c_p; + + execution_state(env, &c_p, NULL); + env->exception_thrown = 1; - env->proc->fvalue = reason; - BIF_ERROR(env->proc, EXC_ERROR); + c_p->fvalue = reason; + BIF_ERROR(c_p, EXC_ERROR); } int enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason) { if (env->exception_thrown && reason != NULL) { - *reason = env->proc->fvalue; + Process *c_p; + execution_state(env, &c_p, NULL); + *reason = c_p->fvalue; } return env->exception_thrown; } @@ -1441,56 +1633,71 @@ int enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM term, ERL_NIF_TERM *list return 1; } +int enif_is_current_process_alive(ErlNifEnv* env) +{ + Process *c_p; + int scheduler; + + execution_state(env, &c_p, &scheduler); + + if (!c_p) + erts_exit(ERTS_ABORT_EXIT, + "enif_is_current_process_alive: " + "Invalid environment"); + + if (!scheduler) + erts_exit(ERTS_ABORT_EXIT, "enif_is_current_process_alive: " + "called from non-scheduler thread"); + + return !ERTS_PROC_IS_EXITING(c_p); +} + int enif_is_process_alive(ErlNifEnv* env, ErlNifPid *proc) { - ErtsProcLocks rp_locks = 0; /* We don't need any locks, - just to check if it is alive */ - Eterm target = proc->pid; - Process* rp; - Process* c_p; - int scheduler = erts_get_scheduler_id() != 0; + int scheduler; - if (env != NULL) { - c_p = env->proc; - if (target == c_p->common.id) { - /* We are alive! */ - return 1; - } - } + execution_state(env, NULL, &scheduler); + + if (scheduler > 0) + return !!erts_proc_lookup(proc->pid); else { #ifdef ERTS_SMP - c_p = NULL; + Process* rp = erts_pid2proc_opt(NULL, 0, proc->pid, 0, + ERTS_P2P_FLG_INC_REFC); + if (rp) + erts_proc_dec_refc(rp); + return !!rp; #else - erts_exit(ERTS_ABORT_EXIT,"enif_is_process_alive: " - "env==NULL on non-SMP VM"); -#endif - } - - rp = (scheduler - ? erts_proc_lookup(target) - : erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN, - target, rp_locks, ERTS_P2P_FLG_INC_REFC)); - if (rp == NULL) { - ASSERT(env == NULL || target != c_p->common.id); + erts_exit(ERTS_ABORT_EXIT, "enif_is_process_alive: " + "called from non-scheduler thread"); return 0; - } else { - if (!scheduler) - erts_proc_dec_refc(rp); - return 1; +#endif } } int enif_is_port_alive(ErlNifEnv *env, ErlNifPort *port) { - /* only allowed if called from scheduler */ - if (erts_get_scheduler_id() == 0) - erts_exit(ERTS_ABORT_EXIT,"enif_is_port_alive: called from non-scheduler"); + int scheduler; + Uint32 iflags = (erts_port_synchronous_ops + ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + : ERTS_PORT_SFLGS_INVALID_LOOKUP); - return erts_port_lookup( - port->port_id, - (erts_port_synchronous_ops - ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP - : ERTS_PORT_SFLGS_INVALID_LOOKUP)) != NULL; + execution_state(env, NULL, &scheduler); + + if (scheduler > 0) + return !!erts_port_lookup(port->port_id, iflags); + else { +#ifdef ERTS_SMP + Port *prt = erts_thr_port_lookup(port->port_id, iflags); + if (prt) + erts_port_dec_refc(prt); + return !!prt; +#else + erts_exit(ERTS_ABORT_EXIT, "enif_is_port_alive: " + "called from non-scheduler thread"); + return 0; +#endif + } } ERL_NIF_TERM @@ -1957,16 +2164,19 @@ void* enif_dlsym(void* handle, const char* symbol, int enif_consume_timeslice(ErlNifEnv* env, int percent) { + Process *proc; Sint reds; + execution_state(env, &proc, NULL); + ASSERT(is_proc_bound(env) && percent >= 1 && percent <= 100); if (percent < 1) percent = 1; else if (percent > 100) percent = 100; reds = ((CONTEXT_REDS+99) / 100) * percent; ASSERT(reds > 0 && reds <= CONTEXT_REDS); - BUMP_REDS(env->proc, reds); - return ERTS_BIF_REDS_LEFT(env->proc) == 0; + BUMP_REDS(proc, reds); + return ERTS_BIF_REDS_LEFT(proc) == 0; } /* @@ -2061,10 +2271,19 @@ static ERL_NIF_TERM init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp, int need_save, int argc, const ERL_NIF_TERM argv[]) { - Process* proc = env->proc; - Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; + Process* proc; + Eterm* reg; NifExport* ep; - int i; + int i, scheduler; + + execution_state(env, &proc, &scheduler); + + ASSERT(scheduler); + + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(proc) + & ERTS_PROC_LOCK_MAIN); + + reg = erts_proc_sched_data(proc)->x_reg_array; ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); if (!ep) @@ -2076,12 +2295,13 @@ init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirec } if (env->exception_thrown) { ep->exception_thrown = 1; - ep->rootset[0] = env->proc->fvalue; + ep->rootset[0] = proc->fvalue; } else { ep->exception_thrown = 0; ep->rootset[0] = NIL; } - ERTS_VBUMP_ALL_REDS(proc); + if (scheduler > 0) + ERTS_VBUMP_ALL_REDS(proc); for (i = 0; i < argc; i++) { if (need_save) ep->rootset[i+1] = reg[i]; @@ -2113,7 +2333,12 @@ static void restore_nif_mfa(Process* proc, NifExport* ep, int exception) { int i; - Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; + Eterm* reg = erts_proc_sched_data(proc)->x_reg_array; + + ERTS_SMP_LC_ASSERT(!(proc->static_flags + & ERTS_STC_FLG_SHADOW_PROC)); + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(proc) + & ERTS_PROC_LOCK_MAIN); proc->current[0] = ep->saved_mfa[0]; proc->current[1] = ep->saved_mfa[1]; @@ -2138,11 +2363,13 @@ restore_nif_mfa(Process* proc, NifExport* ep, int exception) static ERL_NIF_TERM dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - Process* proc = env->proc; + Process* proc; NifExport* ep; + execution_state(env, &proc, NULL); + ASSERT(argc == 1); - ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data)); + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc))); ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep); ASSERT(!ep->exception_thrown); @@ -2157,10 +2384,12 @@ dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) static ERL_NIF_TERM dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - Process* proc = env->proc; + Process* proc; NifExport* ep; - ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data)); + execution_state(env, &proc, NULL); + + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc))); ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep); ASSERT(ep->exception_thrown); @@ -2177,23 +2406,32 @@ dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) static ERL_NIF_TERM execute_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - Process* proc = env->proc; - NativeFunPtr fp = (NativeFunPtr) proc->current[6]; + Process* proc; + NativeFunPtr fp; NifExport* ep; ERL_NIF_TERM result; - ASSERT(ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data)); + execution_state(env, &proc, NULL); + + fp = (NativeFunPtr) proc->current[6]; + + ASSERT(ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc))); /* * Set ep->fp to NULL before the native call so we know later whether it scheduled another NIF for execution */ ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); - ASSERT(ep); + ASSERT(ep && fp); ep->fp = NULL; erts_smp_atomic32_read_band_mb(&proc->state, ~(ERTS_PSFLG_DIRTY_CPU_PROC | ERTS_PSFLG_DIRTY_IO_PROC)); + + erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN); + result = (*fp)(env, argc, argv); + erts_smp_proc_lock(proc, ERTS_PROC_LOCK_MAIN); + if (erts_refc_dectest(&env->mod_nif->rt_dtor_cnt, 0) == 0 && env->mod_nif->mod == NULL) close_lib(env->mod_nif); /* @@ -2230,29 +2468,49 @@ execute_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) static ERTS_INLINE ERL_NIF_TERM schedule_dirty_nif(ErlNifEnv* env, int flags, int argc, const ERL_NIF_TERM argv[]) { - erts_aint32_t state, n, a; - Process* proc = env->proc; - NativeFunPtr fp = (NativeFunPtr) proc->current[6]; + ERL_NIF_TERM result; + erts_aint32_t act, dirty_flag; + Process* proc; + NativeFunPtr fp; NifExport* ep; - int need_save; + int need_save, scheduler; + + execution_state(env, &proc, &scheduler); + if (scheduler <= 0) { + ASSERT(scheduler < 0); + erts_smp_proc_lock(proc, ERTS_PROC_LOCK_MAIN); + } + + fp = (NativeFunPtr) proc->current[6]; + + ASSERT(fp); ASSERT(flags==ERL_NIF_DIRTY_JOB_IO_BOUND || flags==ERL_NIF_DIRTY_JOB_CPU_BOUND); - a = erts_smp_atomic32_read_acqb(&proc->state); - while (1) { - n = state = a; + if (flags == ERL_NIF_DIRTY_JOB_CPU_BOUND) + dirty_flag = ERTS_PSFLG_DIRTY_CPU_PROC; + else + dirty_flag = ERTS_PSFLG_DIRTY_IO_PROC; + + act = erts_smp_atomic32_read_bor_nob(&proc->state, dirty_flag); + if (!(act & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC))) + erts_refc_inc(&env->mod_nif->rt_dtor_cnt, 1); + else if ((act & (ERTS_PSFLG_DIRTY_CPU_PROC + | ERTS_PSFLG_DIRTY_IO_PROC)) & ~dirty_flag) { + /* clear other flag... */ if (flags == ERL_NIF_DIRTY_JOB_CPU_BOUND) - n |= ERTS_PSFLG_DIRTY_CPU_PROC; + dirty_flag = ERTS_PSFLG_DIRTY_IO_PROC; else - n |= ERTS_PSFLG_DIRTY_IO_PROC; - a = erts_smp_atomic32_cmpxchg_mb(&proc->state, n, state); - if (a == state) - break; + dirty_flag = ERTS_PSFLG_DIRTY_CPU_PROC; + erts_smp_atomic32_read_band_nob(&proc->state, ~dirty_flag); } - erts_refc_inc(&env->mod_nif->rt_dtor_cnt, 1); + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); need_save = (ep == NULL || is_non_value(ep->saved_mfa[0])); - return init_nif_sched_data(env, execute_dirty_nif, fp, need_save, argc, argv); + result = init_nif_sched_data(env, execute_dirty_nif, fp, need_save, argc, argv); + if (scheduler <= 0) + erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN); + return result; } static ERL_NIF_TERM @@ -2277,11 +2535,14 @@ schedule_dirty_cpu_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) static ERL_NIF_TERM execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - Process* proc = env->proc; - NativeFunPtr fp = (NativeFunPtr) proc->current[6]; + Process* proc; + NativeFunPtr fp; NifExport* ep; ERL_NIF_TERM result; + execution_state(env, &proc, NULL); + fp = (NativeFunPtr) proc->current[6]; + ASSERT(!env->exception_thrown); ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep); @@ -2304,10 +2565,10 @@ enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv*, int, const ERL_NIF_TERM[]), int argc, const ERL_NIF_TERM argv[]) { - Process* proc = env->proc; + Process* proc; NifExport* ep; ERL_NIF_TERM fun_name_atom, result; - int need_save; + int need_save, scheduler; if (argc > MAX_ARG) return enif_make_badarg(env); @@ -2315,6 +2576,13 @@ enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, if (enif_is_exception(env, fun_name_atom)) return fun_name_atom; + execution_state(env, &proc, &scheduler); + if (scheduler <= 0) { + if (scheduler == 0) + enif_make_badarg(env); + erts_smp_proc_lock(proc, ERTS_PROC_LOCK_MAIN); + } + ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); need_save = (ep == NULL || is_non_value(ep->saved_mfa[0])); @@ -2326,12 +2594,15 @@ enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, sched_fun = schedule_dirty_io_nif; else if (chkflgs == ERL_NIF_DIRTY_JOB_CPU_BOUND) sched_fun = schedule_dirty_cpu_nif; - else - return enif_make_badarg(env); + else { + result = enif_make_badarg(env); + goto done; + } result = init_nif_sched_data(env, sched_fun, fp, need_save, argc, argv); #else - return enif_make_badarg(env); + result = enif_make_badarg(env); #endif + goto done; } else result = init_nif_sched_data(env, execute_nif, fp, need_save, argc, argv); @@ -2339,18 +2610,28 @@ enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep); ep->exp.code[1] = (BeamInstr) fun_name_atom; + +done: + if (scheduler < 0) + erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN); + return result; } -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT - int enif_is_on_dirty_scheduler(ErlNifEnv* env) { - return ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data); -} + int scheduler; + Process *c_p; + + execution_state(env, &c_p, &scheduler); -#endif /* ERL_NIF_DIRTY_SCHEDULER_SUPPORT */ + if (!c_p || !scheduler) + erts_exit(ERTS_ABORT_EXIT, "enif_is_on_dirty_scheduler: " + "Invalid env"); + + return scheduler < 0; +} /* Maps */ @@ -3051,16 +3332,16 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) (BeamInstr) BeamOp(op_i_generic_breakpoint)); g->orig_instr = (BeamInstr) BeamOp(op_call_nif); } +#ifdef ERTS_DIRTY_SCHEDULERS if ((entry->major > 2 || (entry->major == 2 && entry->minor >= 7)) && (entry->options & ERL_NIF_DIRTY_NIF_OPTION) && f->flags) { -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT code_ptr[5+3] = (BeamInstr) f->fptr; code_ptr[5+1] = (f->flags == ERL_NIF_DIRTY_JOB_IO_BOUND) ? (BeamInstr) schedule_dirty_io_nif : (BeamInstr) schedule_dirty_cpu_nif; -#endif } else +#endif code_ptr[5+1] = (BeamInstr) f->fptr; code_ptr[5+2] = (BeamInstr) lib; f = next_func(entry, &incr, f); diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 93e4313791..1bdac51d1f 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -166,6 +166,7 @@ ERL_NIF_API_FUNC_DECL(ErlNifTime, enif_convert_time_unit, (ErlNifTime, ErlNifTim ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_now_time, (ErlNifEnv *env)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_cpu_time, (ErlNifEnv *env)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_unique_integer, (ErlNifEnv *env, ErlNifUniqueInteger properties)); +ERL_NIF_API_FUNC_DECL(int, enif_is_current_process_alive, (ErlNifEnv *env)); ERL_NIF_API_FUNC_DECL(int, enif_is_process_alive, (ErlNifEnv *env, ErlNifPid *pid)); ERL_NIF_API_FUNC_DECL(int, enif_is_port_alive, (ErlNifEnv *env, ErlNifPort *port_id)); ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, ErlNifPort* port_id)); @@ -321,6 +322,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_now_time ERL_NIF_API_FUNC_MACRO(enif_now_time) # define enif_cpu_time ERL_NIF_API_FUNC_MACRO(enif_cpu_time) # define enif_make_unique_integer ERL_NIF_API_FUNC_MACRO(enif_make_unique_integer) +# define enif_is_current_process_alive ERL_NIF_API_FUNC_MACRO(enif_is_current_process_alive) # define enif_is_process_alive ERL_NIF_API_FUNC_MACRO(enif_is_process_alive) # define enif_is_port_alive ERL_NIF_API_FUNC_MACRO(enif_is_port_alive) # define enif_get_local_port ERL_NIF_API_FUNC_MACRO(enif_get_local_port) diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index c2588e718d..f0075ca2b9 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -487,6 +487,7 @@ ERTS_GLB_INLINE Port*erts_id2port(Eterm id); ERTS_GLB_INLINE Port *erts_id2port_sflgs(Eterm, Process *, ErtsProcLocks, Uint32); ERTS_GLB_INLINE void erts_port_release(Port *); #ifdef ERTS_SMP +ERTS_GLB_INLINE Port *erts_thr_port_lookup(Eterm id, Uint32 invalid_sflgs); ERTS_GLB_INLINE Port *erts_thr_id2port_sflgs(Eterm id, Uint32 invalid_sflgs); ERTS_GLB_INLINE void erts_thr_port_release(Port *prt); #endif @@ -626,6 +627,44 @@ erts_port_release(Port *prt) } #ifdef ERTS_SMP +/* + * erts_thr_id2port_sflgs() and erts_port_dec_refc(prt) can + * be used by unmanaged threads in the SMP case. + */ +ERTS_GLB_INLINE Port * +erts_thr_port_lookup(Eterm id, Uint32 invalid_sflgs) +{ + Port *prt; + ErtsThrPrgrDelayHandle dhndl; + + if (is_not_internal_port(id)) + return NULL; + + dhndl = erts_thr_progress_unmanaged_delay(); + + prt = (Port *) erts_ptab_pix2intptr_ddrb(&erts_port, + internal_port_index(id)); + + if (!prt || prt->common.id != id) { + erts_thr_progress_unmanaged_continue(dhndl); + return NULL; + } + else { + erts_aint32_t state; + erts_port_inc_refc(prt); + + if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED) + erts_thr_progress_unmanaged_continue(dhndl); + + state = erts_atomic32_read_acqb(&prt->state); + if (state & invalid_sflgs) { + erts_port_dec_refc(prt); + return NULL; + } + + return prt; + } +} /* * erts_thr_id2port_sflgs() and erts_thr_port_release() can diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 36b0af0c1a..b50ca6d009 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -409,6 +409,10 @@ ErtsAlignedSchedulerData *erts_aligned_scheduler_data; #ifdef ERTS_DIRTY_SCHEDULERS ErtsAlignedSchedulerData *erts_aligned_dirty_cpu_scheduler_data; ErtsAlignedSchedulerData *erts_aligned_dirty_io_scheduler_data; +typedef union { + Process dsp; + char align[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(Process))]; +} ErtsAlignedDirtyShadowProcess; #endif typedef union { @@ -589,6 +593,7 @@ dbg_chk_aux_work_val(erts_aint32_t value) valid |= ERTS_SSI_AUX_WORK_CNCLD_TMRS; valid |= ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR; valid |= ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP; + valid |= ERTS_SSI_AUX_WORK_PENDING_EXITERS; #endif #if HAVE_ERTS_MSEG valid |= ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK; @@ -611,7 +616,7 @@ dbg_chk_aux_work_val(erts_aint32_t value) #endif #ifdef ERTS_SMP -static void handle_pending_exiters(ErtsProcList *); +static void do_handle_pending_exiters(ErtsProcList *); static void wake_scheduler(ErtsRunQueue *rq); #endif @@ -679,6 +684,8 @@ erts_pre_init_process(void) = "MISC_THR_PRGR"; erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MISC_IX] = "MISC"; + erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX] + = "PENDING_EXITERS"; erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_SET_TMO_IX] = "SET_TMO"; erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX] @@ -1156,7 +1163,7 @@ reply_sched_wall_time(void *vswtrp) Eterm erts_sched_wall_time_request(Process *c_p, int set, int enable) { - ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p); + ErtsSchedulerData *esdp = erts_proc_sched_data(c_p); Eterm ref; ErtsSchedWallTimeReq *swtrp; Eterm *hp; @@ -1234,7 +1241,7 @@ reply_system_check(void *vscrp) Eterm erts_system_check_request(Process *c_p) { - ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p); + ErtsSchedulerData *esdp = erts_proc_sched_data(c_p); Eterm ref; ErtsSystemCheckReq *scrp; Eterm *hp; @@ -2336,6 +2343,30 @@ handle_mseg_cache_check(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiti #endif +#ifdef ERTS_SMP + +static ERTS_INLINE erts_aint32_t +handle_pending_exiters(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) +{ + ErtsProcList *pnd_xtrs; + ErtsRunQueue *rq; + + rq = awdp->esdp->run_queue; + unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_PENDING_EXITERS); + + erts_smp_runq_lock(rq); + pnd_xtrs = rq->procs.pending_exiters; + rq->procs.pending_exiters = NULL; + erts_smp_runq_unlock(rq); + + if (erts_proclist_fetch(&pnd_xtrs, NULL)) + do_handle_pending_exiters(pnd_xtrs); + + return aux_work & ~ERTS_SSI_AUX_WORK_PENDING_EXITERS; +} + +#endif + static ERTS_INLINE erts_aint32_t handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) { @@ -2427,6 +2458,10 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting) HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_MISC, handle_misc_aux_work); +#ifdef ERTS_SMP + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_PENDING_EXITERS, + handle_pending_exiters); +#endif HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_SET_TMO, handle_setup_aux_work_timer); @@ -3979,6 +4014,33 @@ schedule_bound_processes(ErtsRunQueue *rq, } } +#ifdef ERTS_DIRTY_SCHEDULERS + +static ERTS_INLINE void +clear_proc_dirty_queue_bit(Process *p, ErtsRunQueue *rq, int prio_bit) +{ +#ifdef DEBUG + erts_aint32_t old; +#endif + erts_aint32_t qb = prio_bit; + if (rq == ERTS_DIRTY_CPU_RUNQ) + qb <<= ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET; + else { + ASSERT(rq == ERTS_DIRTY_IO_RUNQ); + qb <<= ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET; + } +#ifdef DEBUG + old = (int) +#else + (void) +#endif + erts_smp_atomic32_read_band_mb(&p->dirty_state, ~qb); + ASSERT(old & qb); +} + +#endif /* ERTS_DIRTY_SCHEDULERS */ + + static void evacuate_run_queue(ErtsRunQueue *rq, ErtsStuckBoundProcesses *sbpp) @@ -4141,29 +4203,8 @@ evacuate_run_queue(ErtsRunQueue *rq, } #ifdef ERTS_DIRTY_SCHEDULERS - - if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) { - erts_aint32_t dqbit = qbit; -#ifdef DEBUG - erts_aint32_t old_dqbit; -#endif - - if (rq == ERTS_DIRTY_CPU_RUNQ) - dqbit <<= ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET; - else { - ASSERT(rq == ERTS_DIRTY_IO_RUNQ); - dqbit <<= ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET; - } - -#ifdef DEBUG - old_dqbit = (int) -#else - (void) -#endif - erts_smp_atomic32_read_band_mb(&real_proc->dirty_state, - ~dqbit); - ASSERT(old_dqbit & dqbit); - } + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) + clear_proc_dirty_queue_bit(real_proc, rq, qbit); #endif if (ERTS_PSFLG_BOUND & real_state) { @@ -5653,7 +5694,8 @@ static void init_scheduler_data(ErtsSchedulerData* esdp, int num, ErtsSchedulerSleepInfo* ssi, ErtsRunQueue* runq, - char** daww_ptr, size_t daww_sz) + char** daww_ptr, size_t daww_sz, + Process *shadow_proc) { esdp->timer_wheel = NULL; #ifdef ERTS_SMP @@ -5677,6 +5719,15 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num, esdp->no = (Uint) num; ERTS_DIRTY_SCHEDULER_NO(esdp) = 0; } + esdp->dirty_shadow_process = shadow_proc; + if (shadow_proc) { + erts_init_empty_process(shadow_proc); + erts_smp_atomic32_init_nob(&shadow_proc->state, + (ERTS_PSFLG_ACTIVE + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_PROXY)); + shadow_proc->static_flags = ERTS_STC_FLG_SHADOW_PROC; + } #else esdp->no = (Uint) num; #endif @@ -5928,30 +5979,40 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online for (ix = 0; ix < n; ix++) { ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(ix); init_scheduler_data(esdp, ix+1, ERTS_SCHED_SLEEP_INFO_IX(ix), - ERTS_RUNQ_IX(ix), &daww_ptr, daww_sz); + ERTS_RUNQ_IX(ix), &daww_ptr, daww_sz, + NULL); } #ifdef ERTS_DIRTY_SCHEDULERS -#ifdef ERTS_SMP - erts_aligned_dirty_cpu_scheduler_data = - erts_alloc_permanent_cache_aligned( - ERTS_ALC_T_SCHDLR_DATA, - no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerData)); - for (ix = 0; ix < no_dirty_cpu_schedulers; ix++) { - ErtsSchedulerData *esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); - init_scheduler_data(esdp, ix+1, ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix), - ERTS_DIRTY_CPU_RUNQ, NULL, 0); - } - erts_aligned_dirty_io_scheduler_data = - erts_alloc_permanent_cache_aligned( - ERTS_ALC_T_SCHDLR_DATA, - no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerData)); - for (ix = 0; ix < no_dirty_io_schedulers; ix++) { - ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix); - init_scheduler_data(esdp, ix+1, ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix), - ERTS_DIRTY_IO_RUNQ, NULL, 0); + { + int dirty_scheds = no_dirty_cpu_schedulers + no_dirty_io_schedulers; + int adspix = 0; + ErtsAlignedDirtyShadowProcess *adsp = + erts_alloc_permanent_cache_aligned( + ERTS_ALC_T_SCHDLR_DATA, + dirty_scheds * sizeof(ErtsAlignedDirtyShadowProcess)); + + erts_aligned_dirty_cpu_scheduler_data = + erts_alloc_permanent_cache_aligned( + ERTS_ALC_T_SCHDLR_DATA, + dirty_scheds * sizeof(ErtsAlignedSchedulerData)); + + erts_aligned_dirty_io_scheduler_data = + &erts_aligned_dirty_cpu_scheduler_data[no_dirty_cpu_schedulers]; + + for (ix = 0; ix < no_dirty_cpu_schedulers; ix++) { + ErtsSchedulerData *esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); + init_scheduler_data(esdp, ix+1, ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix), + ERTS_DIRTY_CPU_RUNQ, NULL, 0, + &adsp[adspix++].dsp); + } + for (ix = 0; ix < no_dirty_io_schedulers; ix++) { + ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix); + init_scheduler_data(esdp, ix+1, ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix), + ERTS_DIRTY_IO_RUNQ, NULL, 0, + &adsp[adspix++].dsp); + } } -#endif #endif init_misc_aux_work(); @@ -6167,7 +6228,7 @@ check_dirty_enqueue_in_prio_queue(Process *c_p, erts_aint32_t dact, max_qbit; /* Termination should be done on an ordinary scheduler */ - if (actual & ERTS_PSFLG_EXITING) { + if ((*newp) & ERTS_PSFLG_EXITING) { *newp &= ~ERTS_PSFLGS_DIRTY_WORK; return ERTS_ENQUEUE_NORMAL_QUEUE; } @@ -6176,7 +6237,7 @@ check_dirty_enqueue_in_prio_queue(Process *c_p, * If we have system tasks, we enqueue on ordinary run-queue * and take care of those system tasks first. */ - if (actual & ERTS_PSFLG_ACTIVE_SYS) + if ((*newp) & ERTS_PSFLG_ACTIVE_SYS) return ERTS_ENQUEUE_NORMAL_QUEUE; dact = erts_smp_atomic32_read_mb(&c_p->dirty_state); @@ -6356,23 +6417,29 @@ select_enqueue_run_queue(int enqueue, int enq_prio, Process *p, erts_aint32_t st * schedule_out_process() return with c_rq locked. */ static ERTS_INLINE int -schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Process *proxy) +schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, + Process *proxy, int is_normal_sched) { - erts_aint32_t a, e, n, enq_prio = -1; + erts_aint32_t a, e, n, enq_prio = -1, running_flgs; int enqueue; /* < 0 -> use proxy */ ErtsRunQueue* runq; + if (is_normal_sched) + running_flgs = ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS; + else + running_flgs = ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS; + a = state; while (1) { n = e = a; - ASSERT(a & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)); + ASSERT(a & running_flgs); enqueue = ERTS_ENQUEUE_NOT; - n &= ~(ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS); - if (a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS) + n &= ~running_flgs; + if ((a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS)) || (a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); } @@ -6485,8 +6552,9 @@ change_proc_schedule_state(Process *p, ErtsProcLocks locks) { /* - * NOTE: ERTS_PSFLG_RUNNING, ERTS_PSFLG_RUNNING_SYS and - * ERTS_PSFLG_ACTIVE_SYS are not allowed to be + * NOTE: ERTS_PSFLG_RUNNING, ERTS_PSFLG_RUNNING_SYS, + * ERTS_PSFLG_DIRTY_RUNNING, ERTS_PSFLG_DIRTY_RUNNING_SYS + * and ERTS_PSFLG_ACTIVE_SYS are not allowed to be * altered by this function! */ erts_aint32_t a = *statep, n; @@ -6500,9 +6568,13 @@ change_proc_schedule_state(Process *p, ASSERT(!(a & ERTS_PSFLG_PROXY)); ASSERT((clear_state_flags & (ERTS_PSFLG_RUNNING | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS | ERTS_PSFLG_ACTIVE_SYS)) == 0); ASSERT((set_state_flags & (ERTS_PSFLG_RUNNING | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS | ERTS_PSFLG_ACTIVE_SYS)) == 0); if (lock_status) @@ -6526,8 +6598,16 @@ change_proc_schedule_state(Process *p, if ((n & (ERTS_PSFLG_SUSPENDED | ERTS_PSFLG_RUNNING | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS | ERTS_PSFLG_IN_RUNQ - | ERTS_PSFLG_ACTIVE)) == ERTS_PSFLG_ACTIVE) { + | ERTS_PSFLG_ACTIVE)) == ERTS_PSFLG_ACTIVE +#ifdef ERTS_DIRTY_SCHEDULERS + || (n & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_EXITING)) == ERTS_PSFLG_EXITING +#endif + ) { /* * Active and seemingly need to be enqueued, but * process may be in a run queue via proxy, need @@ -6551,7 +6631,9 @@ change_proc_schedule_state(Process *p, | ERTS_PSFLG_ACTIVE)) == ERTS_PSFLG_ACTIVE) && (!(a & (ERTS_PSFLG_ACTIVE_SYS | ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS) + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS) && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))))) { /* We activated a prevously inactive process */ @@ -6693,7 +6775,10 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) enqueue = ERTS_ENQUEUE_NOT; n |= ERTS_PSFLG_ACTIVE_SYS; - if (!(a & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))) + if (!(a & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS))) enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); if (a == e) @@ -6706,7 +6791,9 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) if (!(a & (ERTS_PSFLG_ACTIVE_SYS | ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS)) + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) { /* We activated a prevously inactive process */ profile_runnable_proc(p, am_active); @@ -6746,11 +6833,16 @@ suspend_process(Process *c_p, Process *p) if (c_p == p) { state = erts_smp_atomic32_read_bor_relb(&p->state, ERTS_PSFLG_SUSPENDED); - ASSERT(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)); + ASSERT(state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)); suspended = (state & ERTS_PSFLG_SUSPENDED) ? -1: 1; } else { - while (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_EXITING))) { + while (!(state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_EXITING))) { erts_aint32_t n, e; n = e = state; @@ -6776,8 +6868,11 @@ suspend_process(Process *c_p, Process *p) if ((state & (ERTS_PSFLG_ACTIVE | ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_DIRTY_ACTIVE_SYS | ERTS_PSFLG_RUNNING | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS | ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { /* We made process inactive */ profile_runnable_proc(p, am_inactive); @@ -7759,8 +7854,10 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int normal plp = proclist_create(p); erts_proclist_store_last(&msbp->blckrs, plp); p->flags |= have_blckd_flg; - ASSERT(schdlr_sspnd.active == ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0)); - ASSERT(p->scheduler_data->no == 1); + ASSERT(normal + ? 1 == schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, ERTS_SCHED_NORMAL) + : schdlr_sspnd.active == ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0)); + ASSERT(erts_proc_sched_data(p)->no == 1); if (schdlr_sspnd.msb.ongoing) res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; else @@ -7780,7 +7877,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int normal if (schdlr_sspnd.active == ERTS_SCHDLR_SSPND_MAKE_NSCHEDS_VAL(1, 0, 0) || (normal && schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, ERTS_SCHED_NORMAL) == 1)) { - ASSERT(p->scheduler_data->no == 1); + ASSERT(erts_proc_sched_data(p)->no == 1); plp = proclist_create(p); erts_proclist_store_last(&msbp->blckrs, plp); if (schdlr_sspnd.msb.ongoing) @@ -7830,7 +7927,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int normal else res = ERTS_SCHDLR_SSPND_YIELD_DONE_NMSCHED_BLOCKED; } - ASSERT(p->scheduler_data); + ASSERT(erts_proc_sched_data(p)); } } else if (!msbp->ongoing) { @@ -8420,9 +8517,23 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, if (!suspend_process(c_p, rp)) { /* Other process running */ - ASSERT(ERTS_PSFLG_RUNNING + ASSERT((ERTS_PSFLG_RUNNING | ERTS_PSFLG_DIRTY_RUNNING) & erts_smp_atomic32_read_nob(&rp->state)); +#ifdef ERTS_DIRTY_SCHEDULERS + if (!suspend + && (erts_smp_atomic32_read_nob(&rp->state) + & ERTS_PSFLG_DIRTY_RUNNING)) { + ErtsProcLocks need_locks = pid_locks & ~ERTS_PROC_LOCK_STATUS; + if (need_locks && erts_smp_proc_trylock(rp, need_locks) == EBUSY) { + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); + rp = erts_pid2proc(c_p, c_p_locks|ERTS_PROC_LOCK_STATUS, + pid, pid_locks|ERTS_PROC_LOCK_STATUS); + } + goto done; + } +#endif + running: /* @@ -8447,7 +8558,7 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, else { ErtsProcLocks need_locks = pid_locks & ~ERTS_PROC_LOCK_STATUS; if (need_locks && erts_smp_proc_trylock(rp, need_locks) == EBUSY) { - if (ERTS_PSFLG_RUNNING_SYS + if ((ERTS_PSFLG_RUNNING_SYS|ERTS_PSFLG_DIRTY_RUNNING_SYS) & erts_smp_atomic32_read_nob(&rp->state)) { /* Executing system task... */ resume_process(rp, ERTS_PROC_LOCK_STATUS); @@ -8474,7 +8585,7 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, * from being selected for normal execution regardless * of locks held or not held on it... */ - ASSERT(!(ERTS_PSFLG_RUNNING + ASSERT(!((ERTS_PSFLG_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS) & erts_smp_atomic32_read_nob(&rp->state))); if (!suspend) @@ -9015,28 +9126,43 @@ erts_run_queues_len(Uint *qlen, int atomic_queues_read, int incl_active_sched) } Eterm -erts_process_status(Process *c_p, ErtsProcLocks c_p_locks, - Process *rp, Eterm rpid) +erts_process_state2status(erts_aint32_t state) +{ + if (state & ERTS_PSFLG_FREE) + return am_free; + + if (state & ERTS_PSFLG_EXITING) + return am_exiting; + + if (state & ERTS_PSFLG_GC) + return am_garbage_collecting; + + if (state & ERTS_PSFLG_SUSPENDED) + return am_suspended; + + if (state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) + return am_running; + + if (state & (ERTS_PSFLG_ACTIVE + | ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_DIRTY_ACTIVE_SYS)) + return am_runnable; + + return am_waiting; +} + +Eterm +erts_process_status(Process *rp, Eterm rpid) { Eterm res = am_undefined; Process *p = rp ? rp : erts_proc_lookup_raw(rpid); if (p) { erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state); - if (state & ERTS_PSFLG_FREE) - res = am_free; - else if (state & ERTS_PSFLG_EXITING) - res = am_exiting; - else if (state & ERTS_PSFLG_GC) - res = am_garbage_collecting; - else if (state & ERTS_PSFLG_SUSPENDED) - res = am_suspended; - else if (state & ERTS_PSFLG_RUNNING) - res = am_running; - else if (state & ERTS_PSFLG_ACTIVE) - res = am_runnable; - else - res = am_waiting; + res = erts_process_state2status(state); } #ifdef ERTS_SMP else { @@ -9251,7 +9377,76 @@ scheduler_gc_proc(Process *c_p, int reds_left) return reds; } +static ERTS_INLINE void +clean_dirty_start(Process *p) +{ +#if defined(ERTS_DIRTY_SCHEDULERS) && !defined(ARCH_64) + void *ptr = ERTS_PROC_SET_DIRTY_CPU_START(p, NULL); + if (ptr) + erts_free(ERTS_ALC_T_DIRTY_START, ptr); +#endif +} + +static ERTS_INLINE void +save_dirty_start(ErtsSchedulerData *esdp, Process *c_p) +{ +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(esdp->run_queue)) { + ErtsMonotonicTime time = erts_get_monotonic_time(esdp); +#ifdef ARCH_64 + ERTS_PROC_SET_DIRTY_CPU_START(c_p, (void *) time); +#else + ErtsMonotonicTime *stimep; + stimep = (ErtsMonotonicTime *) ERTS_PROC_GET_DIRTY_CPU_START(c_p); + if (!stimep) { + stimep = erts_alloc(ERTS_ALC_T_DIRTY_START, + sizeof(ErtsMonotonicTime)); + ERTS_PROC_SET_DIRTY_CPU_START(c_p, (void *) stimep); + } + *stimep = time; +#endif + } +#endif +} + +static ERTS_INLINE int +get_dirty_reds(ErtsSchedulerData *esdp, Process *c_p) +{ + +#ifndef ERTS_DIRTY_SCHEDULERS + return -1; +#else + ErtsMonotonicTime stime, time; + + if (!ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(esdp->run_queue)) + return 1; + +#ifdef ARCH_64 + stime = (ErtsMonotonicTime) ERTS_PROC_GET_DIRTY_CPU_START(c_p); +#else + { + ErtsMonotonicTime *stimep; + stimep = (ErtsMonotonicTime *) ERTS_PROC_GET_DIRTY_CPU_START(c_p); + ASSERT(stimep); + stime = *stimep; + } +#endif + + time = erts_get_monotonic_time(esdp); + + ASSERT(stime && stime < time); + + time -= stime; + time = ERTS_MONOTONIC_TO_USEC(time); + time *= 2; + + if (time > INT_MAX) + return INT_MAX; + return (int) time; +#endif + +} /* * schedule() is called from BEAM (process_main()) or HiPE @@ -9283,6 +9478,7 @@ Process *schedule(Process *p, int calls) int reds; Uint32 flags; erts_aint32_t state = 0; /* Supress warning... */ + int is_normal_sched; ERTS_MSACC_DECLARE_CACHE(); @@ -9312,25 +9508,44 @@ Process *schedule(Process *p, int calls) */ if (!p) { /* NULL in the very first schedule() call */ esdp = erts_get_scheduler_data(); + is_normal_sched = !ERTS_SCHEDULER_IS_DIRTY(esdp); rq = erts_get_runq_current(esdp); ASSERT(esdp); fcalls = (int) erts_smp_atomic32_read_acqb(&function_calls); actual_reds = reds = 0; erts_smp_runq_lock(rq); } else { - sched_out_proc: - #ifdef ERTS_SMP - ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); +#ifdef ERTS_DIRTY_SCHEDULERS esdp = p->scheduler_data; + is_normal_sched = esdp != NULL; + if (is_normal_sched) + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); + else { + esdp = erts_get_scheduler_data(); + ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp)); + } +#else + esdp = p->scheduler_data; + is_normal_sched = 1; +#endif ASSERT(esdp->current_process == p || esdp->free_process == p); #else esdp = erts_scheduler_data; ASSERT(esdp->current_process == p); + is_normal_sched = 1; #endif - reds = actual_reds = calls - esdp->virtual_reds; + sched_out_proc: + + ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); + + if (is_normal_sched) + reds = actual_reds = calls - esdp->virtual_reds; + else + reds = actual_reds = get_dirty_reds(esdp, p); + ASSERT(actual_reds >= 0); if (reds < ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST) reds = ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST; @@ -9377,7 +9592,7 @@ Process *schedule(Process *p, int calls) state = erts_smp_atomic32_read_nob(&p->state); #ifdef ERTS_SMP - if (state & ERTS_PSFLG_PENDING_EXIT) + if (is_normal_sched && (state & ERTS_PSFLG_PENDING_EXIT)) erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_STATUS)); if (p->pending_suspenders) @@ -9387,7 +9602,8 @@ Process *schedule(Process *p, int calls) esdp->reductions += reds; - schedule_out_process(rq, state, p, proxy_p); /* Returns with rq locked! */ + /* schedule_out_process() returns with rq locked! */ + schedule_out_process(rq, state, p, proxy_p, is_normal_sched); proxy_p = NULL; ERTS_PROC_REDUCTIONS_EXECUTED(esdp, rq, @@ -9405,13 +9621,20 @@ Process *schedule(Process *p, int calls) ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER); if (state & ERTS_PSFLG_FREE) { + if (!is_normal_sched) { + ASSERT(p->flags & F_DELAYED_DEL_PROC); + erts_proc_dec_refc(p); + } + else { #ifdef ERTS_SMP - ASSERT(esdp->free_process == p); - esdp->free_process = NULL; + ASSERT(esdp->free_process == p); + esdp->free_process = NULL; #else - erts_proc_dec_refc(p); + erts_proc_dec_refc(p); #endif + } } + #ifdef ERTS_SMP ASSERT(!esdp->free_process); #endif @@ -9419,7 +9642,7 @@ Process *schedule(Process *p, int calls) ERTS_SMP_CHK_NO_PROC_LOCKS; - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (is_normal_sched) { if (esdp->check_time_reds >= ERTS_CHECK_TIME_REDS) (void) erts_get_monotonic_time(esdp); @@ -9433,23 +9656,15 @@ Process *schedule(Process *p, int calls) } - ERTS_SMP_LC_ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp) - || !erts_thr_progress_is_blocking()); + ERTS_SMP_LC_ASSERT(!is_normal_sched || !erts_thr_progress_is_blocking()); check_activities_to_run: { + erts_aint32_t psflg_running, psflg_running_sys; #ifdef ERTS_SMP ErtsMigrationPaths *mps; ErtsMigrationPath *mp; - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { - ErtsProcList *pnd_xtrs = rq->procs.pending_exiters; - if (erts_proclist_fetch(&pnd_xtrs, NULL)) { - rq->procs.pending_exiters = NULL; - erts_smp_runq_unlock(rq); - handle_pending_exiters(pnd_xtrs); - erts_smp_runq_lock(rq); - } - + if (is_normal_sched) { if (rq->check_balance_reds <= 0) check_balance(rq); @@ -9466,32 +9681,35 @@ Process *schedule(Process *p, int calls) continue_check_activities_to_run: flags = ERTS_RUNQ_FLGS_GET_NOB(rq); continue_check_activities_to_run_known_flags: - ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp) - || flags & ERTS_RUNQ_FLG_NONEMPTY); + ASSERT(!is_normal_sched || (flags & ERTS_RUNQ_FLG_NONEMPTY)); - if (flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND|ERTS_RUNQ_FLG_SUSPENDED)) { - if (flags & ERTS_RUNQ_FLG_SUSPENDED) { - (void) ERTS_RUNQ_FLGS_UNSET_NOB(rq, ERTS_RUNQ_FLG_EXEC); + if (!is_normal_sched) { + if (erts_smp_atomic32_read_acqb(&esdp->ssi->flags) + & ERTS_SSI_FLG_SUSPENDED) { suspend_scheduler(esdp); - flags = ERTS_RUNQ_FLGS_SET_NOB(rq, ERTS_RUNQ_FLG_EXEC); - flags |= ERTS_RUNQ_FLG_EXEC; - } - if (flags & ERTS_RUNQ_FLG_CHK_CPU_BIND) { - flags = ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND); - flags &= ~ ERTS_RUNQ_FLG_CHK_CPU_BIND; - erts_sched_check_cpu_bind(esdp); } } -#ifdef ERTS_DIRTY_SCHEDULERS - else if (ERTS_SCHEDULER_IS_DIRTY(esdp) - && (erts_smp_atomic32_read_acqb(&esdp->ssi->flags) - & ERTS_SSI_FLG_SUSPENDED)) - suspend_scheduler(esdp); -#endif - - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + else { erts_aint32_t aux_work; - int leader_update = erts_thr_progress_update(esdp); + int leader_update; + + ASSERT(is_normal_sched); + + if (flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND|ERTS_RUNQ_FLG_SUSPENDED)) { + if (flags & ERTS_RUNQ_FLG_SUSPENDED) { + (void) ERTS_RUNQ_FLGS_UNSET_NOB(rq, ERTS_RUNQ_FLG_EXEC); + suspend_scheduler(esdp); + flags = ERTS_RUNQ_FLGS_SET_NOB(rq, ERTS_RUNQ_FLG_EXEC); + flags |= ERTS_RUNQ_FLG_EXEC; + } + if (flags & ERTS_RUNQ_FLG_CHK_CPU_BIND) { + flags = ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND); + flags &= ~ERTS_RUNQ_FLG_CHK_CPU_BIND; + erts_sched_check_cpu_bind(esdp); + } + } + + leader_update = erts_thr_progress_update(esdp); aux_work = erts_atomic32_read_acqb(&esdp->ssi->aux_work); if (aux_work | leader_update) { erts_smp_runq_unlock(rq); @@ -9517,19 +9735,13 @@ Process *schedule(Process *p, int calls) flags = ERTS_RUNQ_FLGS_GET_NOB(rq); -#ifdef ERTS_DIRTY_SCHEDULERS - if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && rq->halt_in_progress) { - /* - * TODO: if halt in progress, need to put the dirty scheduler - * to sleep somewhere around here to prevent it from picking up - * new work - */ + if (!is_normal_sched && rq->halt_in_progress) { + /* Wait for emulator to terminate... */ + while (1) + erts_milli_sleep(1000*1000); } - else -#endif - - if ((!(flags & ERTS_RUNQ_FLGS_QMASK) && !rq->misc.start) - || (rq->halt_in_progress && ERTS_EMPTY_RUNQ_PORTS(rq))) { + else if ((!(flags & ERTS_RUNQ_FLGS_QMASK) && !rq->misc.start) + || (rq->halt_in_progress && ERTS_EMPTY_RUNQ_PORTS(rq))) { /* Prepare for scheduler wait */ #ifdef ERTS_SMP ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); @@ -9543,7 +9755,7 @@ Process *schedule(Process *p, int calls) if (flags & ERTS_RUNQ_FLG_INACTIVE) empty_runq(rq); else { - if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && try_steal_task(rq)) + if (is_normal_sched && try_steal_task(rq)) goto continue_check_activities_to_run; empty_runq(rq); @@ -9572,9 +9784,9 @@ Process *schedule(Process *p, int calls) goto check_activities_to_run; } - else if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && - (fcalls > input_reductions && - prepare_for_sys_schedule(!0))) { + else if (is_normal_sched + && (fcalls > input_reductions + && prepare_for_sys_schedule(!0))) { ErtsMonotonicTime current_time; /* * Schedule system-level activities. @@ -9688,11 +9900,17 @@ Process *schedule(Process *p, int calls) ASSERT(p); /* Wrong qmask in rq->flags? */ - if (ERTS_SCHEDULER_IS_DIRTY(esdp)) - psflg_band_mask = ~((erts_aint32_t) 0); - else + if (is_normal_sched) { + psflg_running = ERTS_PSFLG_RUNNING; + psflg_running_sys = ERTS_PSFLG_RUNNING_SYS; psflg_band_mask = ~(((erts_aint32_t) 1) << (ERTS_PSFLGS_GET_PRQ_PRIO(state) + ERTS_PSFLGS_IN_PRQ_MASK_OFFSET)); + } + else { + psflg_running = ERTS_PSFLG_DIRTY_RUNNING; + psflg_running_sys = ERTS_PSFLG_DIRTY_RUNNING_SYS; + psflg_band_mask = ~((erts_aint32_t) 0); + } if (!(state & ERTS_PSFLG_PROXY)) psflg_band_mask &= ~ERTS_PSFLG_IN_RUNQ; @@ -9707,34 +9925,53 @@ Process *schedule(Process *p, int calls) state = erts_smp_atomic32_read_nob(&p->state); } +#ifdef ERTS_DIRTY_SCHEDULERS + if (!is_normal_sched) + clear_proc_dirty_queue_bit(p, rq, qbit); +#endif + while (1) { - erts_aint32_t exp, new, tmp; - tmp = new = exp = state; + erts_aint32_t exp, new; + int run_process; + new = exp = state; new &= psflg_band_mask; - if (!(state & (ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS))) { - tmp = state & (ERTS_PSFLG_SUSPENDED - | ERTS_PSFLG_PENDING_EXIT - | ERTS_PSFLG_ACTIVE_SYS - | ERTS_PSFLG_DIRTY_ACTIVE_SYS); - if (tmp != ERTS_PSFLG_SUSPENDED) { - if (state & (ERTS_PSFLG_ACTIVE_SYS - | ERTS_PSFLG_DIRTY_ACTIVE_SYS)) - new |= ERTS_PSFLG_RUNNING_SYS; - else - new |= ERTS_PSFLG_RUNNING; - } + /* + * Run process if not already running (or free) + * or exiting and not running on a normal + * scheduler, and not suspended (and not in a + * state where suspend should be ignored). + */ + run_process = (((!(state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS + | ERTS_PSFLG_FREE))) +#ifdef ERTS_DIRTY_SCHEDULERS + | (((state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_FREE + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_EXITING)) + == ERTS_PSFLG_EXITING) + & (!!is_normal_sched)) +#endif + ) + & ((state & (ERTS_PSFLG_SUSPENDED + | ERTS_PSFLG_EXITING + | ERTS_PSFLG_FREE + | ERTS_PSFLG_PENDING_EXIT + | ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_DIRTY_ACTIVE_SYS)) + != ERTS_PSFLG_SUSPENDED)); + if (run_process) { + if (state & (ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_DIRTY_ACTIVE_SYS)) + new |= psflg_running_sys; + else + new |= psflg_running; } state = erts_smp_atomic32_cmpxchg_relb(&p->state, new, exp); if (state == exp) { - if ((state & (ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS - | ERTS_PSFLG_FREE)) - || ((state & (ERTS_PSFLG_SUSPENDED - | ERTS_PSFLG_PENDING_EXIT - | ERTS_PSFLG_ACTIVE_SYS - | ERTS_PSFLG_DIRTY_ACTIVE_SYS)) - == ERTS_PSFLG_SUSPENDED)) { + if (!run_process) { if (proxy_p) { free_proxy_proc(proxy_p); proxy_p = NULL; @@ -9761,34 +9998,13 @@ Process *schedule(Process *p, int calls) erts_smp_runq_unlock(rq); -#ifdef ERTS_DIRTY_SCHEDULERS - if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { -#ifdef DEBUG - int old_dqbit; -#endif - int dqbit = qbit; - - if (rq == ERTS_DIRTY_CPU_RUNQ) - dqbit <<= ERTS_PDSFLGS_IN_CPU_PRQ_MASK_OFFSET; - else { - ASSERT(rq == ERTS_DIRTY_IO_RUNQ); - dqbit <<= ERTS_PDSFLGS_IN_IO_PRQ_MASK_OFFSET; - } - -#ifdef DEBUG - old_dqbit = (int) -#else - (void) -#endif - erts_smp_atomic32_read_band_mb(&p->dirty_state, ~dqbit); - ASSERT(old_dqbit & dqbit); - } -#endif /* ERTS_DIRTY_SCHEDULERS */ - #endif /* ERTS_SMP */ } + if (!is_normal_sched) + save_dirty_start(esdp, p); + #ifdef ERTS_SMP if (flags & ERTS_RUNQ_FLG_PROTECTED) @@ -9805,9 +10021,7 @@ Process *schedule(Process *p, int calls) UWord old = ERTS_PROC_SCHED_ID(p, (UWord) esdp->no); int migrated = old && old != esdp->no; -#ifdef ERTS_DIRTY_SCHEDULERS - ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); -#endif + ASSERT(is_normal_sched); prio = (int) ERTS_PSFLGS_GET_USR_PRIO(state); @@ -9821,20 +10035,21 @@ Process *schedule(Process *p, int calls) erts_smp_spin_unlock(&erts_sched_stat.lock); } - ASSERT(!p->scheduler_data); - p->scheduler_data = esdp; - state = erts_smp_atomic32_read_nob(&p->state); -#ifdef ERTS_DIRTY_SCHEDULERS - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { - if (!!(state & ERTS_PSFLGS_DIRTY_WORK) - & !(state & ERTS_PSFLG_ACTIVE_SYS)) { + ASSERT(!p->scheduler_data); +#ifndef ERTS_DIRTY_SCHEDULERS + p->scheduler_data = esdp; +#else /* ERTS_DIRTY_SCHEDULERS */ + if (is_normal_sched) { + if ((!!(state & ERTS_PSFLGS_DIRTY_WORK)) + & (!(state & ERTS_PSFLG_ACTIVE_SYS))) { /* Migrate to dirty scheduler... */ sunlock_sched_out_proc: erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); goto sched_out_proc; } + p->scheduler_data = esdp; } else { if (state & (ERTS_PSFLG_ACTIVE_SYS @@ -9891,7 +10106,8 @@ Process *schedule(Process *p, int calls) (void)ERTS_TRACER_PROC_IS_ENABLED(p); #endif - if (state & ERTS_PSFLG_RUNNING_SYS) { + if (state & (ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { /* * GC is normally never delayed when a process * is scheduled out, but might be when executing @@ -9905,7 +10121,7 @@ Process *schedule(Process *p, int calls) reds -= cost; if (reds <= 0 #ifdef ERTS_DIRTY_SCHEDULERS - || ERTS_SCHEDULER_IS_DIRTY(esdp) + || !is_normal_sched || (state & ERTS_PSFLGS_DIRTY_WORK) #endif ) { @@ -9913,8 +10129,8 @@ Process *schedule(Process *p, int calls) } } - ASSERT(state & ERTS_PSFLG_RUNNING_SYS); - ASSERT(!(state & ERTS_PSFLG_RUNNING)); + ASSERT(state & psflg_running_sys); + ASSERT(!(state & psflg_running)); while (1) { erts_aint32_t n, e; @@ -9926,8 +10142,8 @@ Process *schedule(Process *p, int calls) } n = e = state; - n &= ~ERTS_PSFLG_RUNNING_SYS; - n |= ERTS_PSFLG_RUNNING; + n &= ~psflg_running_sys; + n |= psflg_running; state = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); if (state == e) { @@ -9935,8 +10151,8 @@ Process *schedule(Process *p, int calls) break; } - ASSERT(state & ERTS_PSFLG_RUNNING_SYS); - ASSERT(!(state & ERTS_PSFLG_RUNNING)); + ASSERT(state & psflg_running_sys); + ASSERT(!(state & psflg_running)); } } @@ -10525,7 +10741,10 @@ save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio) } state = erts_smp_atomic32_read_nob(&c_p->state); - ASSERT((ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS) & state); + ASSERT((ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS) & state); while (!(state & ERTS_PSFLG_DELAYED_SYS) || prio < ERTS_PSFLGS_GET_ACT_PRIO(state)) { @@ -10850,6 +11069,8 @@ erts_get_exact_total_reductions(Process *c_p, Uint *redsp, Uint *diffp) erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); } +static void delete_process(Process* p); + void erts_free_proc(Process *p) { @@ -10858,6 +11079,8 @@ erts_free_proc(Process *p) #endif ASSERT(erts_smp_atomic32_read_nob(&p->state) & ERTS_PSFLG_FREE); ASSERT(0 == erts_proc_read_refc(p)); + if (p->flags & F_DELAYED_DEL_PROC) + delete_process(p); erts_free(ERTS_ALC_T_PROC, (void *) p); } @@ -11486,18 +11709,36 @@ erts_cleanup_empty_process(Process* p) #endif } -/* - * p must be the currently executing process. - */ static void delete_process(Process* p) { Eterm *heap; ErtsPSD *psd; + struct saved_calls *scb; + process_breakpoint_time_t *pbt; + void *nif_export; + VERBOSE(DEBUG_PROCESSES, ("Removing process: %T\n",p->common.id)); VERBOSE(DEBUG_SHCOPY, ("[pid=%T] delete process: %p %p %p %p\n", p->common.id, HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p))); + scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL); + + if (scb) { + p->fcalls += CONTEXT_REDS; /* Reduction counting depends on this... */ + erts_free(ERTS_ALC_T_CALLS_BUF, (void *) scb); + } + + pbt = ERTS_PROC_SET_CALL_TIME(p, NULL); + if (pbt) + erts_free(ERTS_ALC_T_BPD, (void *) pbt); + + nif_export = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, NULL); + if (nif_export) + erts_destroy_nif_export(nif_export); + + clean_dirty_start(p); + /* Cleanup psd */ psd = (ErtsPSD *) erts_smp_atomic_read_nob(&p->psd); @@ -11629,7 +11870,10 @@ set_proc_self_exiting(Process *c_p) ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCKS_ALL); state = erts_smp_atomic32_read_nob(&c_p->state); - ASSERT(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)); + ASSERT(state & (ERTS_PSFLG_RUNNING + |ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)); #ifdef DEBUG enqueue = @@ -11679,51 +11923,73 @@ erts_handle_pending_exit(Process *c_p, ErtsProcLocks locks) erts_smp_proc_unlock(c_p, xlocks); } +static void save_pending_exiter(Process *p, ErtsProcList *plp); + static void -handle_pending_exiters(ErtsProcList *pnd_xtrs) +do_handle_pending_exiters(ErtsProcList *pnd_xtrs) { /* 'list' is expected to have been fetched (i.e. not a ring anymore) */ ErtsProcList *plp = pnd_xtrs; while (plp) { - ErtsProcList *free_plp; - Process *p = erts_pid2proc(NULL, 0, plp->pid, ERTS_PROC_LOCKS_ALL); + ErtsProcList *next_plp = plp->next; + Process *p = erts_proc_lookup(plp->pid); if (p) { - if (erts_proclist_same(plp, p)) { - erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state); - if (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))) { - ASSERT(state & ERTS_PSFLG_PENDING_EXIT); - erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL); + erts_aint32_t state; + /* + * If the process is running on a normal scheduler, the + * pending exit will soon be detected and handled by the + * scheduler running the process (at schedule in/out). + */ + if (erts_smp_proc_trylock(p, ERTS_PROC_LOCKS_ALL) != EBUSY) { + if (erts_proclist_same(plp, p)) { + state = erts_smp_atomic32_read_acqb(&p->state); + if (!(state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_EXITING))) { + ASSERT(state & ERTS_PSFLG_PENDING_EXIT); + erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL); + } } + erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL); + } + else { + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + if (erts_proclist_same(plp, p)) { + state = erts_smp_atomic32_read_acqb(&p->state); + if (!(state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_EXITING))) { + /* + * Save process and try to acquire all + * locks at a later time... + */ + save_pending_exiter(p, plp); + plp = NULL; + } + } + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); } - erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL); } - free_plp = plp; - plp = plp->next; - proclist_destroy(free_plp); + if (plp) + proclist_destroy(plp); + plp = next_plp; } } static void -save_pending_exiter(Process *p) +save_pending_exiter(Process *p, ErtsProcList *plp) { - ErtsProcList *plp; + ErtsSchedulerSleepInfo *ssi; ErtsRunQueue *rq; - ErtsSchedulerData *esdp = erts_get_scheduler_data(); ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); - if (!esdp) - rq = RUNQ_READ_RQ(&p->run_queue); - else - rq = esdp->run_queue; + rq = RUNQ_READ_RQ(&p->run_queue); + ASSERT(rq && !ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); -#ifdef ERTS_DIRTY_SCHEDULERS - if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) - rq = ERTS_RUNQ_IX(0); /* Handle on ordinary scheduler */ -#endif - - plp = proclist_create(p); + if (!plp) + plp = proclist_create(p); erts_smp_runq_lock(rq); @@ -11731,9 +11997,11 @@ save_pending_exiter(Process *p) non_empty_runq(rq); + ssi = rq->scheduler->ssi; + erts_smp_runq_unlock(rq); - wake_scheduler(rq); + set_aux_work_flags_wakeup_nob(ssi, ERTS_SSI_AUX_WORK_PENDING_EXITERS); } #endif @@ -11933,7 +12201,7 @@ send_exit_signal(Process *c_p, /* current process if and only if (need_locks && erts_smp_proc_trylock(rp, need_locks) == EBUSY) { /* ... but we havn't got all locks on it ... */ - save_pending_exiter(rp); + save_pending_exiter(rp, NULL); /* * The pending exit will be discovered when next * process is scheduled in @@ -12401,10 +12669,8 @@ erts_continue_exit_process(Process *p) ErtsProcLocks curr_locks = ERTS_PROC_LOCK_MAIN; Eterm reason = p->fvalue; DistEntry *dep; - struct saved_calls *scb; - process_breakpoint_time_t *pbt; erts_aint32_t state; - void *nif_export; + int delay_del_proc = 0; #ifdef DEBUG int yield_allowed = 1; @@ -12547,7 +12813,7 @@ erts_continue_exit_process(Process *p) { /* Do *not* use erts_get_runq_proc() */ ErtsRunQueue *rq; - rq = erts_get_runq_current(ERTS_GET_SCHEDULER_DATA_FROM_PROC(p)); + rq = erts_get_runq_current(erts_proc_sched_data(p)); erts_smp_runq_lock(rq); @@ -12593,16 +12859,24 @@ erts_continue_exit_process(Process *p) break; } +#ifdef ERTS_DIRTY_SCHEDULERS + if (a & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + p->flags |= F_DELAYED_DEL_PROC; + delay_del_proc = 1; + /* + * The dirty scheduler will also decrease + * refc when done... + */ + erts_proc_inc_refc(p); + } +#endif + if (refc_inced && !(n & ERTS_PSFLG_IN_RUNQ)) erts_proc_dec_refc(p); } dep = (p->flags & F_DISTRIBUTION) ? erts_this_dist_entry : NULL; - scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL); - if (scb) - p->fcalls += CONTEXT_REDS; /* Reduction counting depends on this... */ - pbt = ERTS_PROC_SET_CALL_TIME(p, NULL); - nif_export = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, NULL); erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL); #ifdef BM_COUNTERS @@ -12642,22 +12916,14 @@ erts_continue_exit_process(Process *p) have none here */ } - if (scb) - erts_free(ERTS_ALC_T_CALLS_BUF, (void *) scb); - - if (pbt) - erts_free(ERTS_ALC_T_BPD, (void *) pbt); - - if (nif_export) - erts_destroy_nif_export(nif_export); - #ifdef ERTS_SMP erts_flush_trace_messages(p, 0); #endif ERTS_TRACER_CLEAR(&ERTS_TRACER(p)); - delete_process(p); + if (!delay_del_proc) + delete_process(p); #ifdef ERTS_SMP erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); @@ -12687,6 +12953,7 @@ erts_continue_exit_process(Process *p) ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(p)); + BUMP_ALL_REDS(p); } /* @@ -13006,11 +13273,13 @@ void erts_halt(int code) int erts_dbg_check_halloc_lock(Process *p) { + ErtsSchedulerData *esdp; if (ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p)) return 1; if (p->common.id == ERTS_INVALID_PID) return 1; - if (p->scheduler_data && p == p->scheduler_data->match_pseudo_process) + esdp = erts_proc_sched_data(p); + if (esdp && p == esdp->match_pseudo_process) return 1; if (erts_thr_progress_is_blocking()) return 1; diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 59da9c1779..6de6b4f499 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -304,6 +304,7 @@ typedef enum { ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN_IX, ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX, ERTS_SSI_AUX_WORK_MISC_IX, + ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX, ERTS_SSI_AUX_WORK_SET_TMO_IX, ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX, ERTS_SSI_AUX_WORK_REAP_PORTS_IX, @@ -336,6 +337,8 @@ typedef enum { (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX) #define ERTS_SSI_AUX_WORK_MISC \ (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_IX) +#define ERTS_SSI_AUX_WORK_PENDING_EXITERS \ + (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX) #define ERTS_SSI_AUX_WORK_SET_TMO \ (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_SET_TMO_IX) #define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK \ @@ -645,6 +648,7 @@ struct ErtsSchedulerData_ { Uint no; /* Scheduler number for normal schedulers */ #ifdef ERTS_DIRTY_SCHEDULERS ErtsDirtySchedId dirty_no; /* Scheduler number for dirty schedulers */ + Process *dirty_shadow_process; #endif Port *current_port; ErtsRunQueue *run_queue; @@ -805,14 +809,26 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi) #define ERTS_PSD_CALL_TIME_BP 3 #define ERTS_PSD_DELAYED_GC_TASK_QS 4 #define ERTS_PSD_NIF_TRAP_EXPORT 5 -#ifdef HIPE #define ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF 6 -#endif - -#ifdef HIPE -#define ERTS_PSD_SIZE 7 -#else -#define ERTS_PSD_SIZE 6 +#define ERTS_PSD_DIRTY_CPU_START 7 + +#define ERTS_PSD_SIZE 8 + +#if !defined(HIPE) && !defined(ERTS_DIRTY_SCHEDULERS) +# undef ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF +# undef ERTS_PSD_DIRTY_CPU_START +# undef ERTS_PSD_SIZE +# define ERTS_PSD_SIZE 6 +#elif !defined(HIPE) +# undef ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF +# undef ERTS_PSD_DIRTY_CPU_START +# undef ERTS_PSD_SIZE +# define ERTS_PSD_DIRTY_CPU_START 6 +# define ERTS_PSD_SIZE 7 +#elif !defined(ERTS_DIRTY_SCHEDULERS) +# undef ERTS_PSD_DIRTY_CPU_START +# undef ERTS_PSD_SIZE +# define ERTS_PSD_SIZE 7 #endif typedef struct { @@ -1183,7 +1199,10 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(20) #define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(21) #define ERTS_PSFLG_DIRTY_ACTIVE_SYS ERTS_PSFLG_BIT(22) -#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 22) +#define ERTS_PSFLG_DIRTY_RUNNING ERTS_PSFLG_BIT(23) +#define ERTS_PSFLG_DIRTY_RUNNING_SYS ERTS_PSFLG_BIT(24) + +#define ERTS_PSFLG_MAX (ERTS_PSFLGS_ZERO_BIT_OFFSET + 24) #define ERTS_PSFLGS_DIRTY_WORK (ERTS_PSFLG_DIRTY_CPU_PROC \ | ERTS_PSFLG_DIRTY_IO_PROC \ @@ -1194,6 +1213,11 @@ void erts_check_for_holes(Process* p); | ERTS_PSFLG_IN_PRQ_NORMAL \ | ERTS_PSFLG_IN_PRQ_LOW) +#define ERTS_PSFLGS_VOLATILE_HEAP (ERTS_PSFLG_EXITING \ + | ERTS_PSFLG_PENDING_EXIT \ + | ERTS_PSFLG_DIRTY_RUNNING \ + | ERTS_PSFLG_DIRTY_RUNNING_SYS) + #define ERTS_PSFLGS_GET_ACT_PRIO(PSFLGS) \ (((PSFLGS) >> ERTS_PSFLGS_ACT_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK) #define ERTS_PSFLGS_GET_USR_PRIO(PSFLGS) \ @@ -1235,6 +1259,7 @@ void erts_check_for_holes(Process* p); * Static flags that do not change after process creation. */ #define ERTS_STC_FLG_SYSTEM_PROC (((Uint32) 1) << 0) +#define ERTS_STC_FLG_SHADOW_PROC (((Uint32) 1) << 1) /* The sequential tracing token is a tuple of size 5: * @@ -1363,6 +1388,7 @@ extern int erts_system_profile_ts_type; #define F_SCHDLR_ONLN_WAITQ (1 << 17) /* Process enqueued waiting to change schedulers online */ #define F_HAVE_BLCKD_NMSCHED (1 << 18) /* Process has blocked normal multi-scheduling */ #define F_HIPE_MODE (1 << 19) +#define F_DELAYED_DEL_PROC (1 << 20) /* Delay delete process (dirty proc exit case) */ /* * F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent @@ -1783,7 +1809,8 @@ erts_aint32_t erts_set_aux_work_timeout(int, erts_aint32_t, int); void erts_sched_notify_check_cpu_bind(void); Uint erts_active_schedulers(void); void erts_init_process(int, int, int); -Eterm erts_process_status(Process *, ErtsProcLocks, Process *, Eterm); +Eterm erts_process_state2status(erts_aint32_t); +Eterm erts_process_status(Process *, Eterm); Uint erts_run_queues_len(Uint *, int, int); void erts_add_to_runq(Process *); Eterm erts_bound_schedulers_term(Process *c_p); @@ -1860,19 +1887,11 @@ int erts_debug_wait_completed(Process *c_p, int flags); Uint erts_process_memory(Process *c_p, int incl_msg_inq); -#ifdef ERTS_SMP -# define ERTS_GET_SCHEDULER_DATA_FROM_PROC(PROC) ((PROC)->scheduler_data) -# define ERTS_PROC_GET_SCHDATA(PROC) ((PROC)->scheduler_data) -#else -# define ERTS_GET_SCHEDULER_DATA_FROM_PROC(PROC) (erts_scheduler_data) -# define ERTS_PROC_GET_SCHDATA(PROC) (erts_scheduler_data) -#endif - #ifdef ERTS_DO_VERIFY_UNUSED_TEMP_ALLOC # define ERTS_VERIFY_UNUSED_TEMP_ALLOC(P) \ do { \ ErtsSchedulerData *esdp__ = ((P) \ - ? ERTS_PROC_GET_SCHDATA((Process *) (P)) \ + ? erts_proc_sched_data((Process *) (P)) \ : erts_get_scheduler_data()); \ if (esdp__ && !ERTS_SCHEDULER_IS_DIRTY(esdp__)) \ esdp__->verify_unused_temp_alloc( \ @@ -1965,12 +1984,15 @@ erts_psd_set(Process *p, int ix, void *data) ErtsPSD *psd; #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) ErtsProcLocks locks = erts_proc_lc_my_proc_locks(p); - if (ERTS_LC_PSD_ANY_LOCK == erts_psd_required_locks[ix].set_locks) - ERTS_SMP_LC_ASSERT(locks || erts_thr_progress_is_blocking()); - else { - locks &= erts_psd_required_locks[ix].set_locks; - ERTS_SMP_LC_ASSERT(erts_psd_required_locks[ix].set_locks == locks - || erts_thr_progress_is_blocking()); + erts_aint32_t state = state = erts_smp_atomic32_read_nob(&p->state); + if (!(state & ERTS_PSFLG_FREE)) { + if (ERTS_LC_PSD_ANY_LOCK == erts_psd_required_locks[ix].set_locks) + ERTS_SMP_LC_ASSERT(locks || erts_thr_progress_is_blocking()); + else { + locks &= erts_psd_required_locks[ix].set_locks; + ERTS_SMP_LC_ASSERT(erts_psd_required_locks[ix].set_locks == locks + || erts_thr_progress_is_blocking()); + } } #endif psd = (ErtsPSD *) erts_smp_atomic_read_nob(&p->psd); @@ -2027,6 +2049,13 @@ erts_psd_set(Process *p, int ix, void *data) ((struct saved_calls *) erts_psd_set((P), ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF, (void *) (SCB))) #endif +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_PROC_GET_DIRTY_CPU_START(P) \ + ((void *) erts_psd_get((P), ERTS_PSD_DIRTY_CPU_START)) +#define ERTS_PROC_SET_DIRTY_CPU_START(P, DCS) \ + ((void *) erts_psd_set((P), ERTS_PSD_DIRTY_CPU_START, (void *) (DCS))) +#endif + ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p); ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p, Eterm handler); @@ -2169,6 +2198,7 @@ erts_check_emigration_need(ErtsRunQueue *c_rq, int prio) #endif +ERTS_GLB_INLINE ErtsSchedulerData *erts_proc_sched_data(Process *c_p); ERTS_GLB_INLINE int erts_is_scheduler_bound(ErtsSchedulerData *esdp); ERTS_GLB_INLINE Process *erts_get_current_process(void); ERTS_GLB_INLINE Eterm erts_get_current_pid(void); @@ -2201,6 +2231,31 @@ ERTS_GLB_INLINE void erts_shrink_message_heap(ErtsMessage **msgpp, Process *pp, #if ERTS_GLB_INLINE_INCL_FUNC_DEF +ERTS_GLB_INLINE +ErtsSchedulerData *erts_proc_sched_data(Process *c_p) +{ + ErtsSchedulerData *esdp; + ASSERT(c_p); +#if !defined(ERTS_SMP) + esdp = erts_get_scheduler_data(); +#else + esdp = c_p->scheduler_data; +# if defined(ERTS_DIRTY_SCHEDULERS) + if (esdp) { + ASSERT(esdp == erts_get_scheduler_data()); + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); + } + else { + esdp = erts_get_scheduler_data(); + ASSERT(esdp); + ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp)); + } +# endif +#endif + ASSERT(esdp); + return esdp; +} + ERTS_GLB_INLINE int erts_is_scheduler_bound(ErtsSchedulerData *esdp) { @@ -2416,7 +2471,7 @@ ERTS_GLB_INLINE ErtsAtomCacheMap * erts_get_atom_cache_map(Process *c_p) { ErtsSchedulerData *esdp = (c_p - ? ERTS_PROC_GET_SCHDATA(c_p) + ? erts_proc_sched_data(c_p) : erts_get_scheduler_data()); ASSERT(esdp); return &esdp->atom_cache_map; diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index fa76773cac..eeaa9a569c 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -568,23 +568,21 @@ dump_externally(int to, void *to_arg, Eterm term) } } -void erts_dump_process_state(int to, void *to_arg, erts_aint32_t psflg) { - if (psflg & ERTS_PSFLG_FREE) - erts_print(to, to_arg, "Non Existing\n"); /* Should never happen */ - else if (psflg & ERTS_PSFLG_EXITING) - erts_print(to, to_arg, "Exiting\n"); - else if (psflg & ERTS_PSFLG_GC) { - erts_print(to, to_arg, "Garbing\n"); - } - else if (psflg & ERTS_PSFLG_SUSPENDED) - erts_print(to, to_arg, "Suspended\n"); - else if (psflg & ERTS_PSFLG_RUNNING) { - erts_print(to, to_arg, "Running\n"); +void erts_dump_process_state(int to, void *to_arg, erts_aint32_t psflg) +{ + char *s; + switch (erts_process_state2status(psflg)) { + case am_free: s = "Non Existing"; break; /* Should never happen */ + case am_exiting: s = "Exiting"; break; + case am_garbage_collecting: s = "Garbing"; break; + case am_suspended: s = "Suspended"; break; + case am_running: s = "Running"; break; + case am_runnable: s = "Scheduled"; break; + case am_waiting: s = "Waiting"; break; + default: s = "Undefined"; break; /* Should never happen */ } - else if (psflg & ERTS_PSFLG_ACTIVE) - erts_print(to, to_arg, "Scheduled\n"); - else - erts_print(to, to_arg, "Waiting\n"); + + erts_print(to, to_arg, "%s\n", s); } void @@ -668,6 +666,10 @@ erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg) { erts_print(to, to_arg, "DIRTY_IO_PROC"); break; case ERTS_PSFLG_DIRTY_ACTIVE_SYS: erts_print(to, to_arg, "DIRTY_ACTIVE_SYS"); break; + case ERTS_PSFLG_DIRTY_RUNNING: + erts_print(to, to_arg, "DIRTY_RUNNING"); break; + case ERTS_PSFLG_DIRTY_RUNNING_SYS: + erts_print(to, to_arg, "DIRTY_RUNNING_SYS"); break; default: erts_print(to, to_arg, "UNKNOWN(%d)", chk); break; } diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index fadbd704bd..9e37106b88 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -2348,7 +2348,7 @@ erts_napi_convert_time_unit(ErtsMonotonicTime val, int from, int to) BIF_RETTYPE monotonic_time_0(BIF_ALIST_0) { ErtsMonotonicTime mtime = time_sup.r.o.get_time(); - update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime); + update_last_mtime(erts_proc_sched_data(BIF_P), mtime); mtime += ERTS_MONOTONIC_OFFSET_NATIVE; BIF_RET(make_time_val(BIF_P, mtime)); } @@ -2356,7 +2356,7 @@ BIF_RETTYPE monotonic_time_0(BIF_ALIST_0) BIF_RETTYPE monotonic_time_1(BIF_ALIST_1) { ErtsMonotonicTime mtime = time_sup.r.o.get_time(); - update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime); + update_last_mtime(erts_proc_sched_data(BIF_P), mtime); BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, mtime, 1)); } @@ -2365,7 +2365,7 @@ BIF_RETTYPE system_time_0(BIF_ALIST_0) ErtsMonotonicTime mtime, offset; mtime = time_sup.r.o.get_time(); offset = get_time_offset(); - update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime); + update_last_mtime(erts_proc_sched_data(BIF_P), mtime); BIF_RET(make_time_val(BIF_P, mtime + offset)); } @@ -2374,7 +2374,7 @@ BIF_RETTYPE system_time_1(BIF_ALIST_0) ErtsMonotonicTime mtime, offset; mtime = time_sup.r.o.get_time(); offset = get_time_offset(); - update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime); + update_last_mtime(erts_proc_sched_data(BIF_P), mtime); BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, mtime + offset, 0)); } @@ -2404,7 +2404,7 @@ BIF_RETTYPE timestamp_0(BIF_ALIST_0) mtime = time_sup.r.o.get_time(); offset = get_time_offset(); - update_last_mtime(ERTS_PROC_GET_SCHDATA(BIF_P), mtime); + update_last_mtime(erts_proc_sched_data(BIF_P), mtime); make_timestamp_value(&mega_sec, &sec, µ_sec, mtime, offset); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 550db95ba8..1abcc6cbf4 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -57,6 +57,7 @@ struct enif_environment_t /* ErlNifEnv */ struct enif_tmp_obj_t* tmp_obj_list; int exception_thrown; /* boolean */ Process *tracee; + int exiting; /* boolean (dirty nifs might return in exiting state) */ }; extern void erts_pre_nif(struct enif_environment_t*, Process*, struct erl_module_nif*, Process* tracee); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index c35d80652f..ef326fafec 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -2512,7 +2512,7 @@ erts_port_output(Process *c_p, sigdp->flags &= ~ERTS_P2P_SIG_DATA_FLG_NOSUSPEND; else if (async_nosuspend) { ErtsSchedulerData *esdp = (c_p - ? ERTS_PROC_GET_SCHDATA(c_p) + ? erts_proc_sched_data(c_p) : erts_get_scheduler_data()); ASSERT(esdp); ns_pthp = &esdp->nosuspend_port_task_handle; @@ -5140,7 +5140,7 @@ erts_request_io_bytes(Process *c_p) Uint *hp; Eterm ref; Uint32 *refn; - ErtsSchedulerData *esdp = ERTS_PROC_GET_SCHDATA(c_p); + ErtsSchedulerData *esdp = erts_proc_sched_data(c_p); ErtsIOBytesReq *req = erts_alloc(ERTS_ALC_T_IOB_REQ, sizeof(ErtsIOBytesReq)); diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index dda3ba565c..f303d4f167 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -34,6 +34,10 @@ (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12) | __GNUC_PATCHLEVEL__) >= (((MAJ) << 24) | ((MIN) << 12) | (PL))) #endif +#if defined(ERTS_DIRTY_SCHEDULERS) && !defined(ERTS_SMP) +# error "Dirty schedulers not supported without smp support" +#endif + #ifdef ERTS_INLINE # ifndef ERTS_CAN_INLINE # define ERTS_CAN_INLINE 1 -- cgit v1.2.3 From f27c8d32c2f92eb1200ecddd2fbb54ff0fc8edaf Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 2 May 2016 16:16:45 +0200 Subject: Move dirty nif test cases into dirty_nif_SUITE --- erts/emulator/test/Makefile | 1 + erts/emulator/test/dirty_nif_SUITE.erl | 240 +++++++++++++++++++++ .../test/dirty_nif_SUITE_data/Makefile.src | 6 + .../test/dirty_nif_SUITE_data/dirty_nif_SUITE.c | 169 +++++++++++++++ erts/emulator/test/nif_SUITE.erl | 80 +------ erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 123 +---------- erts/emulator/test/scheduler_SUITE.erl | 50 +---- .../test/scheduler_SUITE_data/Makefile.src | 8 - .../test/scheduler_SUITE_data/scheduler_SUITE.c | 37 ---- 9 files changed, 422 insertions(+), 292 deletions(-) create mode 100644 erts/emulator/test/dirty_nif_SUITE.erl create mode 100644 erts/emulator/test/dirty_nif_SUITE_data/Makefile.src create mode 100644 erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c delete mode 100644 erts/emulator/test/scheduler_SUITE_data/Makefile.src delete mode 100644 erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c (limited to 'erts') diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index de395dfb97..b580211eff 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -53,6 +53,7 @@ MODULES= \ crypto_SUITE \ ddll_SUITE \ decode_packet_SUITE \ + dirty_nif_SUITE \ distribution_SUITE \ driver_SUITE \ efile_SUITE \ diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl new file mode 100644 index 0000000000..963b2c5db1 --- /dev/null +++ b/erts/emulator/test/dirty_nif_SUITE.erl @@ -0,0 +1,240 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2014. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(dirty_nif_SUITE). + +%%-define(line_trace,true). +-define(CHECK(Exp,Got), check(Exp,Got,?LINE)). +%%-define(CHECK(Exp,Got), Exp = Got). + +-include_lib("common_test/include/ct.hrl"). + +-export([all/0, suite/0, + init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2, + dirty_nif/1, dirty_nif_send/1, + dirty_nif_exception/1, call_dirty_nif_exception/1, + dirty_scheduler_exit/1]). + +-define(nif_stub,nif_stub_error(?LINE)). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [dirty_nif, + dirty_nif_send, + dirty_nif_exception, + dirty_scheduler_exit]. + +init_per_suite(Config) -> + try erlang:system_info(dirty_cpu_schedulers) of + N when is_integer(N), N > 0 -> + case lib_loaded() of + false -> + ok = erlang:load_nif( + filename:join(?config(data_dir, Config), + "dirty_nif_SUITE"), []); + true -> + ok + end, + Config + catch _:_ -> + {skipped, "No dirty scheduler support"} + end. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(Case, Config) -> + [{testcase, Case} | Config]. + +end_per_testcase(_Case, _Config) -> + ok. + +dirty_nif(Config) when is_list(Config) -> + Val1 = 42, + Val2 = "Erlang", + Val3 = list_to_binary([Val2, 0]), + {Val1, Val2, Val3} = call_dirty_nif(Val1, Val2, Val3), + LargeArray = lists:duplicate(1000, ok), + LargeArray = call_dirty_nif_zero_args(), + ok. + +dirty_nif_send(Config) when is_list(Config) -> + Parent = self(), + Pid = spawn_link(fun() -> + Self = self(), + {ok, Self} = receive_any(), + Parent ! {ok, Self} + end), + {ok, Pid} = send_from_dirty_nif(Pid), + {ok, Pid} = receive_any(), + ok. + +dirty_nif_exception(Config) when is_list(Config) -> + try + %% this checks that the expected exception occurs when the + %% dirty NIF returns the result of enif_make_badarg + %% directly + call_dirty_nif_exception(1), + ct:fail(expected_badarg) + catch + error:badarg -> + [{?MODULE,call_dirty_nif_exception,[1],_}|_] = + erlang:get_stacktrace(), + ok + end, + try + %% this checks that the expected exception occurs when the + %% dirty NIF calls enif_make_badarg at some point but then + %% returns a value that isn't an exception + call_dirty_nif_exception(0), + ct:fail(expected_badarg) + catch + error:badarg -> + [{?MODULE,call_dirty_nif_exception,[0],_}|_] = + erlang:get_stacktrace(), + ok + end, + %% this checks that a dirty NIF can raise various terms as + %% exceptions + ok = nif_raise_exceptions(call_dirty_nif_exception). + +nif_raise_exceptions(NifFunc) -> + ExcTerms = [{error, test}, "a string", <<"a binary">>, + 42, [1,2,3,4,5], [{p,1},{p,2},{p,3}]], + lists:foldl(fun(Term, ok) -> + try + erlang:apply(?MODULE,NifFunc,[Term]), + ct:fail({expected,Term}) + catch + error:Term -> + [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(), + ok + end + end, ok, ExcTerms). + +dirty_scheduler_exit(Config) when is_list(Config) -> + try + erlang:system_info(dirty_cpu_schedulers), + dirty_scheduler_exit_test(Config) + catch + error:badarg -> + {skipped, "No dirty scheduler support"} + end. + +dirty_scheduler_exit_test(Config) -> + {ok, Node} = start_node(Config, "+SDio 1"), + Path = proplists:get_value(data_dir, Config), + NifLib = filename:join(Path, atom_to_list(?MODULE)), + [ok] = mcall(Node, + [fun() -> + ok = erlang:load_nif(NifLib, []), + Start = erlang:monotonic_time(milli_seconds), + ok = test_dirty_scheduler_exit(), + End = erlang:monotonic_time(milli_seconds), + io:format("Time=~p ms~n", [End-Start]), + ok + end]), + stop_node(Node), + ok. + +test_dirty_scheduler_exit() -> + process_flag(trap_exit,true), + test_dse(10,[]). +test_dse(0,Pids) -> + timer:sleep(100), + kill_dse(Pids,[]); +test_dse(N,Pids) -> + Pid = spawn_link(fun () -> + F = fun dirty_sleeper/0, + F() + end), + test_dse(N-1,[Pid|Pids]). + +kill_dse([],Killed) -> + wait_dse(Killed); +kill_dse([Pid|Pids],AlreadyKilled) -> + exit(Pid,kill), + kill_dse(Pids,[Pid|AlreadyKilled]). + +wait_dse([]) -> + ok; +wait_dse([Pid|Pids]) -> + receive + {'EXIT',Pid,Reason} -> + killed = Reason + end, + wait_dse(Pids). + +%% +%% Internal... +%% + +receive_any() -> + receive M -> M end. + +start_node(Config) -> + start_node(Config, ""). + +start_node(Config, Args) when is_list(Config) -> + Pa = filename:dirname(code:which(?MODULE)), + Name = list_to_atom(atom_to_list(?MODULE) + ++ "-" + ++ atom_to_list(proplists:get_value(testcase, Config)) + ++ "-" + ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" + ++ integer_to_list(erlang:unique_integer([positive]))), + test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). + +stop_node(Node) -> + test_server:stop_node(Node). + +mcall(Node, Funs) -> + Parent = self(), + Refs = lists:map(fun (Fun) -> + Ref = make_ref(), + spawn_link(Node, + fun () -> + Res = Fun(), + unlink(Parent), + Parent ! {Ref, Res} + end), + Ref + end, Funs), + lists:map(fun (Ref) -> + receive + {Ref, Res} -> + Res + end + end, Refs). + +%% The NIFs: +lib_loaded() -> false. +call_nif_schedule(_,_) -> ?nif_stub. +call_dirty_nif(_,_,_) -> ?nif_stub. +send_from_dirty_nif(_) -> ?nif_stub. +call_dirty_nif_exception(_) -> ?nif_stub. +call_dirty_nif_zero_args() -> ?nif_stub. +dirty_sleeper() -> ?nif_stub. + +nif_stub_error(Line) -> + exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/dirty_nif_SUITE_data/Makefile.src b/erts/emulator/test/dirty_nif_SUITE_data/Makefile.src new file mode 100644 index 0000000000..e9301753b0 --- /dev/null +++ b/erts/emulator/test/dirty_nif_SUITE_data/Makefile.src @@ -0,0 +1,6 @@ + +NIF_LIBS = dirty_nif_SUITE@dll@ + +all: $(NIF_LIBS) + +@SHLIB_RULES@ diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c new file mode 100644 index 0000000000..72ceb300c9 --- /dev/null +++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c @@ -0,0 +1,169 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2009-2014. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ +#include "erl_nif.h" +#include +#ifndef __WIN32__ +#include +#endif + +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + return 0; +} + +static ERL_NIF_TERM lib_loaded(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return enif_make_atom(env, "true"); +} + +static int have_dirty_schedulers(void) +{ + ErlNifSysInfo si; + enif_system_info(&si, sizeof(si)); + return si.dirty_scheduler_support; +} + +static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int n; + char s[10]; + ErlNifBinary b; + ERL_NIF_TERM result; + if (have_dirty_schedulers()) { + assert(enif_is_on_dirty_scheduler(env)); + } + assert(argc == 3); + enif_get_int(env, argv[0], &n); + enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1); + enif_inspect_binary(env, argv[2], &b); + return enif_make_tuple3(env, + enif_make_int(env, n), + enif_make_string(env, s, ERL_NIF_LATIN1), + enif_make_binary(env, &b)); +} + +static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int n; + char s[10]; + ErlNifBinary b; + assert(!enif_is_on_dirty_scheduler(env)); + if (argc != 3) + return enif_make_badarg(env); + if (have_dirty_schedulers()) { + if (enif_get_int(env, argv[0], &n) && + enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1) && + enif_inspect_binary(env, argv[2], &b)) + return enif_schedule_nif(env, "call_dirty_nif", ERL_NIF_DIRTY_JOB_CPU_BOUND, dirty_nif, argc, argv); + else + return enif_make_badarg(env); + } else { + return dirty_nif(env, argc, argv); + } +} + +static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM result; + ErlNifPid pid; + ErlNifEnv* menv; + int res; + + if (!enif_get_local_pid(env, argv[0], &pid)) + return enif_make_badarg(env); + result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid)); + menv = enif_alloc_env(); + res = enif_send(env, &pid, menv, result); + enif_free_env(menv); + if (!res) + return enif_make_badarg(env); + else + return result; +} + +static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + switch (argc) { + case 1: { + int arg; + if (enif_get_int(env, argv[0], &arg) && arg < 2) { + ERL_NIF_TERM args[255]; + int i; + args[0] = argv[0]; + for (i = 1; i < 255; i++) + args[i] = enif_make_int(env, i); + return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, + call_dirty_nif_exception, 255, args); + } else { + return enif_raise_exception(env, argv[0]); + } + } + case 2: { + int return_badarg_directly; + enif_get_int(env, argv[0], &return_badarg_directly); + assert(return_badarg_directly == 1 || return_badarg_directly == 0); + if (return_badarg_directly) + return enif_make_badarg(env); + else { + /* ignore return value */ enif_make_badarg(env); + return enif_make_atom(env, "ok"); + } + } + default: + return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, + call_dirty_nif_exception, argc-1, argv); + } +} + +static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int i; + ERL_NIF_TERM result[1000]; + ERL_NIF_TERM ok = enif_make_atom(env, "ok"); + assert(argc == 0); + for (i = 0; i < sizeof(result)/sizeof(*result); i++) { + result[i] = ok; + } + return enif_make_list_from_array(env, result, i); +} + +static ERL_NIF_TERM +dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + assert(enif_is_on_dirty_scheduler(env)); +#ifdef __WIN32__ + Sleep(6000); +#else + sleep(6); +#endif + return enif_make_atom(env, "ok"); +} + +static ErlNifFunc nif_funcs[] = +{ + {"lib_loaded", 0, lib_loaded}, + {"call_dirty_nif", 3, call_dirty_nif}, + {"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, + {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND}, +}; + +ERL_NIF_INIT(dirty_nif_SUITE,nif_funcs,load,NULL,NULL,NULL) diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index a7767132ee..a1e1495480 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -38,8 +38,7 @@ is_checks/1, get_length/1, make_atom/1, make_string/1, reverse_list_test/1, otp_9828/1, - otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1, - dirty_nif_exception/1, call_dirty_nif_exception/1, nif_schedule/1, + otp_9668/1, consume_timeslice/1, nif_schedule/1, nif_exception/1, call_nif_exception/1, nif_nan_and_inf/1, nif_atom_too_long/1, nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1, @@ -75,8 +74,7 @@ all() -> make_string,reverse_list_test, otp_9828, otp_9668, consume_timeslice, - nif_schedule, dirty_nif, dirty_nif_send, dirty_nif_exception, - nif_exception, nif_nan_and_inf, nif_atom_too_long, + nif_schedule, nif_exception, nif_nan_and_inf, nif_atom_too_long, nif_monotonic_time, nif_time_offset, nif_convert_time_unit, nif_now_time, nif_cpu_time, nif_unique_integer, nif_is_process_alive, nif_is_port_alive, @@ -1541,76 +1539,6 @@ nif_schedule(Config) when is_list(Config) -> end, ok. -dirty_nif(Config) when is_list(Config) -> - try erlang:system_info(dirty_cpu_schedulers) of - N when is_integer(N) -> - ensure_lib_loaded(Config), - Val1 = 42, - Val2 = "Erlang", - Val3 = list_to_binary([Val2, 0]), - {Val1, Val2, Val3} = call_dirty_nif(Val1, Val2, Val3), - LargeArray = lists:duplicate(1000, ok), - LargeArray = call_dirty_nif_zero_args(), - ok - catch - error:badarg -> - {skipped,"No dirty scheduler support"} - end. - -dirty_nif_send(Config) when is_list(Config) -> - try erlang:system_info(dirty_cpu_schedulers) of - N when is_integer(N) -> - ensure_lib_loaded(Config), - Parent = self(), - Pid = spawn_link(fun() -> - Self = self(), - {ok, Self} = receive_any(), - Parent ! {ok, Self} - end), - {ok, Pid} = send_from_dirty_nif(Pid), - {ok, Pid} = receive_any(), - ok - catch - error:badarg -> - {skipped,"No dirty scheduler support"} - end. - -dirty_nif_exception(Config) when is_list(Config) -> - try erlang:system_info(dirty_cpu_schedulers) of - N when is_integer(N) -> - ensure_lib_loaded(Config), - try - %% this checks that the expected exception occurs when the - %% dirty NIF returns the result of enif_make_badarg - %% directly - call_dirty_nif_exception(1), - ct:fail(expected_badarg) - catch - error:badarg -> - [{?MODULE,call_dirty_nif_exception,[1],_}|_] = - erlang:get_stacktrace(), - ok - end, - try - %% this checks that the expected exception occurs when the - %% dirty NIF calls enif_make_badarg at some point but then - %% returns a value that isn't an exception - call_dirty_nif_exception(0), - ct:fail(expected_badarg) - catch - error:badarg -> - [{?MODULE,call_dirty_nif_exception,[0],_}|_] = - erlang:get_stacktrace(), - ok - end, - %% this checks that a dirty NIF can raise various terms as - %% exceptions - ok = nif_raise_exceptions(call_dirty_nif_exception) - catch - error:badarg -> - {skipped,"No dirty scheduler support"} - end. - nif_exception(Config) when is_list(Config) -> ensure_lib_loaded(Config), try @@ -2078,10 +2006,6 @@ otp_9668_nif(_) -> ?nif_stub. otp_9828_nif(_) -> ?nif_stub. consume_timeslice_nif(_,_) -> ?nif_stub. call_nif_schedule(_,_) -> ?nif_stub. -call_dirty_nif(_,_,_) -> ?nif_stub. -send_from_dirty_nif(_) -> ?nif_stub. -call_dirty_nif_exception(_) -> ?nif_stub. -call_dirty_nif_zero_args() -> ?nif_stub. call_nif_exception(_) -> ?nif_stub. call_nif_nan_or_inf(_) -> ?nif_stub. call_nif_atom_too_long(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 11e5dab58e..73073ad59f 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -23,6 +23,9 @@ #include #include #include +#ifndef __WIN32__ +#include +#endif #include "nif_mod.h" @@ -1574,120 +1577,6 @@ static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TE return result; } -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT - -static int have_dirty_schedulers(void) -{ - ErlNifSysInfo si; - enif_system_info(&si, sizeof(si)); - return si.dirty_scheduler_support; -} - -static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - int n; - char s[10]; - ErlNifBinary b; - ERL_NIF_TERM result; - if (have_dirty_schedulers()) { - assert(enif_is_on_dirty_scheduler(env)); - } - assert(argc == 3); - enif_get_int(env, argv[0], &n); - enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1); - enif_inspect_binary(env, argv[2], &b); - return enif_make_tuple3(env, - enif_make_int(env, n), - enif_make_string(env, s, ERL_NIF_LATIN1), - enif_make_binary(env, &b)); -} - -static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - int n; - char s[10]; - ErlNifBinary b; - assert(!enif_is_on_dirty_scheduler(env)); - if (argc != 3) - return enif_make_badarg(env); - if (have_dirty_schedulers()) { - if (enif_get_int(env, argv[0], &n) && - enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1) && - enif_inspect_binary(env, argv[2], &b)) - return enif_schedule_nif(env, "call_dirty_nif", ERL_NIF_DIRTY_JOB_CPU_BOUND, dirty_nif, argc, argv); - else - return enif_make_badarg(env); - } else { - return dirty_nif(env, argc, argv); - } -} - -static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ERL_NIF_TERM result; - ErlNifPid pid; - ErlNifEnv* menv; - int res; - - if (!enif_get_local_pid(env, argv[0], &pid)) - return enif_make_badarg(env); - result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid)); - menv = enif_alloc_env(); - res = enif_send(env, &pid, menv, result); - enif_free_env(menv); - if (!res) - return enif_make_badarg(env); - else - return result; -} - -static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - switch (argc) { - case 1: { - int arg; - if (enif_get_int(env, argv[0], &arg) && arg < 2) { - ERL_NIF_TERM args[255]; - int i; - args[0] = argv[0]; - for (i = 1; i < 255; i++) - args[i] = enif_make_int(env, i); - return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, - call_dirty_nif_exception, 255, args); - } else { - return enif_raise_exception(env, argv[0]); - } - } - case 2: { - int return_badarg_directly; - enif_get_int(env, argv[0], &return_badarg_directly); - assert(return_badarg_directly == 1 || return_badarg_directly == 0); - if (return_badarg_directly) - return enif_make_badarg(env); - else { - /* ignore return value */ enif_make_badarg(env); - return enif_make_atom(env, "ok"); - } - } - default: - return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, - call_dirty_nif_exception, argc-1, argv); - } -} - -static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - int i; - ERL_NIF_TERM result[1000]; - ERL_NIF_TERM ok = enif_make_atom(env, "ok"); - assert(argc == 0); - for (i = 0; i < sizeof(result)/sizeof(*result); i++) { - result[i] = ok; - } - return enif_make_list_from_array(env, result, i); -} -#endif - /* * If argv[0] is the integer 0, call enif_make_badarg, but don't return its * return value. Instead, return ok. Result should still be a badarg @@ -2162,12 +2051,6 @@ static ErlNifFunc nif_funcs[] = {"otp_9828_nif", 1, otp_9828_nif}, {"consume_timeslice_nif", 2, consume_timeslice_nif}, {"call_nif_schedule", 2, call_nif_schedule}, -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT - {"call_dirty_nif", 3, call_dirty_nif}, - {"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, - {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND}, -#endif {"call_nif_exception", 1, call_nif_exception}, {"call_nif_nan_or_inf", 1, call_nif_nan_or_inf}, {"call_nif_atom_too_long", 1, call_nif_atom_too_long}, diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 6b49b68ec8..f18d79d770 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -57,7 +57,6 @@ scheduler_suspend_basic/1, scheduler_suspend/1, dirty_scheduler_threads/1, - dirty_scheduler_exit/1, reader_groups/1]). suite() -> @@ -72,7 +71,7 @@ all() -> bound_process, {group, scheduler_bind}, scheduler_threads, scheduler_suspend_basic, scheduler_suspend, - dirty_scheduler_threads, dirty_scheduler_exit, + dirty_scheduler_threads, reader_groups]. groups() -> @@ -1162,53 +1161,6 @@ get_dsstate(Config, Cmd) -> stop_node(Node), {DSCPU, DSCPUOnln, DSIO}. -dirty_scheduler_exit(Config) when is_list(Config) -> - try - erlang:system_info(dirty_cpu_schedulers), - dirty_scheduler_exit_test(Config) - catch - error:badarg -> - {skipped, "No dirty scheduler support"} - end. - -dirty_scheduler_exit_test(Config) -> - {ok, Node} = start_node(Config, "+SDio 1"), - [ok] = mcall(Node, - [fun() -> - Path = proplists:get_value(data_dir, Config), - Lib = atom_to_list(?MODULE), - ok = erlang:load_nif(filename:join(Path,Lib), []), - ok = test_dirty_scheduler_exit() - end]), - stop_node(Node), - ok. - -test_dirty_scheduler_exit() -> - process_flag(trap_exit,true), - test_dse(10,[]). -test_dse(0,Pids) -> - timer:sleep(100), - kill_dse(Pids,[]); -test_dse(N,Pids) -> - Pid = spawn_link(fun dirty_sleeper/0), - test_dse(N-1,[Pid|Pids]). -kill_dse([],Killed) -> - wait_dse(Killed); -kill_dse([Pid|Pids],AlreadyKilled) -> - exit(Pid,kill), - kill_dse(Pids,[Pid|AlreadyKilled]). -wait_dse([]) -> - ok; -wait_dse([Pid|Pids]) -> - receive - {'EXIT',Pid,killed} -> - ok - end, - wait_dse(Pids). - -dirty_sleeper() -> - erlang:nif_error({error,?MODULE}). - scheduler_suspend_basic(Config) when is_list(Config) -> case erlang:system_info(multi_scheduling) of disabled -> diff --git a/erts/emulator/test/scheduler_SUITE_data/Makefile.src b/erts/emulator/test/scheduler_SUITE_data/Makefile.src deleted file mode 100644 index 859112cf19..0000000000 --- a/erts/emulator/test/scheduler_SUITE_data/Makefile.src +++ /dev/null @@ -1,8 +0,0 @@ - -SCHEDULER_LIBS = scheduler_SUITE@dll@ - -all: $(SCHEDULER_LIBS) - -@SHLIB_RULES@ - -$(SCHEDULER_LIBS): scheduler_SUITE.c diff --git a/erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c b/erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c deleted file mode 100644 index ab4863337f..0000000000 --- a/erts/emulator/test/scheduler_SUITE_data/scheduler_SUITE.c +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef __WIN32__ -#include -#endif -#include "erl_nif.h" - -static int -load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) -{ - ErlNifSysInfo sys_info; - enif_system_info(&sys_info, sizeof(ErlNifSysInfo)); - if (!sys_info.smp_support || !sys_info.dirty_scheduler_support) - return 1; - return 0; -} - -static ERL_NIF_TERM -dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT -#ifdef __WIN32__ - Sleep(3000); -#else - sleep(3); -#endif -#endif - return enif_make_atom(env, "ok"); -} - -static ErlNifFunc funcs[] = { -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT - {"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND} -#else - {"dirty_sleeper", 0, dirty_sleeper, 0} -#endif -}; - -ERL_NIF_INIT(scheduler_SUITE, funcs, &load, NULL, NULL, NULL); -- cgit v1.2.3 From 4d3c48a050e2d28a41b75c3b02b48520659e52e6 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 2 May 2016 16:17:42 +0200 Subject: Add dirty_call_while_terminated test case --- erts/emulator/test/dirty_nif_SUITE.erl | 59 +++++++++++++++++++++- .../test/dirty_nif_SUITE_data/dirty_nif_SUITE.c | 42 +++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl index 963b2c5db1..71fbf17a2e 100644 --- a/erts/emulator/test/dirty_nif_SUITE.erl +++ b/erts/emulator/test/dirty_nif_SUITE.erl @@ -31,7 +31,7 @@ init_per_testcase/2, end_per_testcase/2, dirty_nif/1, dirty_nif_send/1, dirty_nif_exception/1, call_dirty_nif_exception/1, - dirty_scheduler_exit/1]). + dirty_scheduler_exit/1, dirty_call_while_terminated/1]). -define(nif_stub,nif_stub_error(?LINE)). @@ -41,7 +41,8 @@ all() -> [dirty_nif, dirty_nif_send, dirty_nif_exception, - dirty_scheduler_exit]. + dirty_scheduler_exit, + dirty_call_while_terminated]. init_per_suite(Config) -> try erlang:system_info(dirty_cpu_schedulers) of @@ -184,6 +185,59 @@ wait_dse([Pid|Pids]) -> end, wait_dse(Pids). +dirty_call_while_terminated(Config) when is_list(Config) -> + Me = self(), + Bin = list_to_binary(lists:duplicate(4711, $r)), + {value, {BinAddr, 4711, 1}} = lists:keysearch(4711, 2, + element(2, + process_info(self(), + binary))), + {Dirty, DM} = spawn_opt(fun () -> + dirty_call_while_terminated_nif(Me), + blipp:blupp(Bin) + end, + [monitor,link]), + receive {dirty_alive, Pid} -> ok end, + {value, {BinAddr, 4711, 2}} = lists:keysearch(4711, 2, + element(2, + process_info(self(), + binary))), + Reason = die_dirty_process, + OT = process_flag(trap_exit, true), + exit(Dirty, Reason), + receive + {'DOWN', DM, process, Dirty, R0} -> + R0 = Reason + end, + receive + {'EXIT', Dirty, R1} -> + R1 = Reason + end, + undefined = process_info(Dirty), + undefined = process_info(Dirty, status), + false = erlang:is_process_alive(Dirty), + false = lists:member(Dirty, processes()), + %% Binary still refered by Dirty process not yet cleaned up + %% since the dirty nif has not yet returned... + {value, {BinAddr, 4711, 2}} = lists:keysearch(4711, 2, + element(2, + process_info(self(), + binary))), + receive after 2000 -> ok end, + receive + Msg -> + ct:fail({unexpected_message, Msg}) + after + 0 -> + ok + end, + {value, {BinAddr, 4711, 1}} = lists:keysearch(4711, 2, + element(2, + process_info(self(), + binary))), + process_flag(trap_exit, OT), + ok. + %% %% Internal... %% @@ -234,6 +288,7 @@ call_dirty_nif(_,_,_) -> ?nif_stub. send_from_dirty_nif(_) -> ?nif_stub. call_dirty_nif_exception(_) -> ?nif_stub. call_dirty_nif_zero_args() -> ?nif_stub. +dirty_call_while_terminated_nif(_) -> ?nif_stub. dirty_sleeper() -> ?nif_stub. nif_stub_error(Line) -> diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c index 72ceb300c9..8de60d1020 100644 --- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c +++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c @@ -156,6 +156,47 @@ dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return enif_make_atom(env, "ok"); } +static ERL_NIF_TERM dirty_call_while_terminated_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifPid self; + ERL_NIF_TERM result, self_term; + ErlNifPid to; + ErlNifEnv* menv; + int res; + + if (!enif_get_local_pid(env, argv[0], &to)) + return enif_make_badarg(env); + + if (!enif_self(env, &self)) + return enif_make_badarg(env); + + self_term = enif_make_pid(env, &self); + + result = enif_make_tuple2(env, enif_make_atom(env, "dirty_alive"), self_term); + menv = enif_alloc_env(); + res = enif_send(env, &to, menv, result); + enif_free_env(menv); + if (!res) + return enif_make_badarg(env); + + /* Wait until we have been killed */ + while (enif_is_process_alive(env, &self)) + ; + + result = enif_make_tuple2(env, enif_make_atom(env, "dirty_dead"), self_term); + menv = enif_alloc_env(); + res = enif_send(env, &to, menv, result); + enif_free_env(menv); + +#ifdef __WIN32__ + Sleep(1000); +#else + sleep(1); +#endif + + return enif_make_atom(env, "ok"); +} + static ErlNifFunc nif_funcs[] = { {"lib_loaded", 0, lib_loaded}, @@ -164,6 +205,7 @@ static ErlNifFunc nif_funcs[] = {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND}, {"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND}, + {"dirty_call_while_terminated_nif", 1, dirty_call_while_terminated_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND} }; ERL_NIF_INIT(dirty_nif_SUITE,nif_funcs,load,NULL,NULL,NULL) -- cgit v1.2.3 From 4aea719054a594a06aceb34afca0ea3df65ead77 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 2 May 2016 15:57:50 +0200 Subject: Add dirty_heap_access test case --- erts/emulator/test/dirty_nif_SUITE.erl | 62 ++++++++++++++++------ .../test/dirty_nif_SUITE_data/dirty_nif_SUITE.c | 16 +++++- 2 files changed, 61 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl index 71fbf17a2e..c3afbc0803 100644 --- a/erts/emulator/test/dirty_nif_SUITE.erl +++ b/erts/emulator/test/dirty_nif_SUITE.erl @@ -31,7 +31,8 @@ init_per_testcase/2, end_per_testcase/2, dirty_nif/1, dirty_nif_send/1, dirty_nif_exception/1, call_dirty_nif_exception/1, - dirty_scheduler_exit/1, dirty_call_while_terminated/1]). + dirty_scheduler_exit/1, dirty_call_while_terminated/1, + dirty_heap_access/1]). -define(nif_stub,nif_stub_error(?LINE)). @@ -42,7 +43,8 @@ all() -> dirty_nif_send, dirty_nif_exception, dirty_scheduler_exit, - dirty_call_while_terminated]. + dirty_call_while_terminated, + dirty_heap_access]. init_per_suite(Config) -> try erlang:system_info(dirty_cpu_schedulers) of @@ -133,15 +135,6 @@ nif_raise_exceptions(NifFunc) -> end, ok, ExcTerms). dirty_scheduler_exit(Config) when is_list(Config) -> - try - erlang:system_info(dirty_cpu_schedulers), - dirty_scheduler_exit_test(Config) - catch - error:badarg -> - {skipped, "No dirty scheduler support"} - end. - -dirty_scheduler_exit_test(Config) -> {ok, Node} = start_node(Config, "+SDio 1"), Path = proplists:get_value(data_dir, Config), NifLib = filename:join(Path, atom_to_list(?MODULE)), @@ -164,10 +157,7 @@ test_dse(0,Pids) -> timer:sleep(100), kill_dse(Pids,[]); test_dse(N,Pids) -> - Pid = spawn_link(fun () -> - F = fun dirty_sleeper/0, - F() - end), + Pid = spawn_link(fun dirty_sleeper/0), test_dse(N-1,[Pid|Pids]). kill_dse([],Killed) -> @@ -238,6 +228,47 @@ dirty_call_while_terminated(Config) when is_list(Config) -> process_flag(trap_exit, OT), ok. +dirty_heap_access(Config) when is_list(Config) -> + {ok, Node} = start_node(Config), + Me = self(), + RGL = rpc:call(Node,erlang,whereis,[init]), + Ref = rpc:call(Node,erlang,make_ref,[]), + Dirty = spawn_link(fun () -> + Res = dirty_heap_access_nif(Ref), + garbage_collect(), + Me ! {self(), Res}, + receive after infinity -> ok end + end), + {N, R} = access_dirty_heap(Dirty, RGL, 0, 0), + receive + {Pid, Res} -> + 1000 = length(Res), + lists:foreach(fun (X) -> Ref = X end, Res) + end, + unlink(Dirty), + exit(Dirty, kill), + stop_node(Node), + {comment, integer_to_list(N) ++ " GL change loops; " + ++ integer_to_list(R) ++ " while running dirty"}. + +access_dirty_heap(Dirty, RGL, N, R) -> + case process_info(Dirty, status) of + {status, waiting} -> + {N, R}; + {status, Status} -> + {group_leader, GL} = process_info(Dirty, group_leader), + true = group_leader(RGL, Dirty), + {group_leader, RGL} = process_info(Dirty, group_leader), + true = group_leader(GL, Dirty), + {group_leader, GL} = process_info(Dirty, group_leader), + access_dirty_heap(Dirty, RGL, N+1, case Status of + running -> + R+1; + _ -> + R + end) + end. + %% %% Internal... %% @@ -290,6 +321,7 @@ call_dirty_nif_exception(_) -> ?nif_stub. call_dirty_nif_zero_args() -> ?nif_stub. dirty_call_while_terminated_nif(_) -> ?nif_stub. dirty_sleeper() -> ?nif_stub. +dirty_heap_access_nif(_) -> ?nif_stub. nif_stub_error(Line) -> exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c index 8de60d1020..2013c88167 100644 --- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c +++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c @@ -45,7 +45,6 @@ static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ int n; char s[10]; ErlNifBinary b; - ERL_NIF_TERM result; if (have_dirty_schedulers()) { assert(enif_is_on_dirty_scheduler(env)); } @@ -197,6 +196,18 @@ static ERL_NIF_TERM dirty_call_while_terminated_nif(ErlNifEnv* env, int argc, co return enif_make_atom(env, "ok"); } +static ERL_NIF_TERM dirty_heap_access_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM res = enif_make_list(env, 0); + int i; + assert(enif_is_on_dirty_scheduler(env)); + for (i = 0; i < 1000; i++) + res = enif_make_list_cell(env, enif_make_copy(env, argv[0]), res); + + return res; +} + + static ErlNifFunc nif_funcs[] = { {"lib_loaded", 0, lib_loaded}, @@ -205,7 +216,8 @@ static ErlNifFunc nif_funcs[] = {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND}, {"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND}, - {"dirty_call_while_terminated_nif", 1, dirty_call_while_terminated_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND} + {"dirty_call_while_terminated_nif", 1, dirty_call_while_terminated_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"dirty_heap_access_nif", 1, dirty_heap_access_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND} }; ERL_NIF_INIT(dirty_nif_SUITE,nif_funcs,load,NULL,NULL,NULL) -- cgit v1.2.3 From 3471d44a6a5ed5ab038c4cdc76b350119fe745e2 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 11 May 2016 11:16:16 +0200 Subject: erts: Only allow remove from trace_status callback Make it so that it is only possible to remove a tracer via returning remove from an erl_tracer. This limition is put in place in order to avoid a lot of lock checking and taking in various places, especially in regards to trace events happening on dirty schedulers. --- erts/doc/src/erl_tracer.xml | 20 +++-- erts/emulator/beam/beam_bp.c | 6 +- erts/emulator/beam/erl_bif_trace.c | 19 ++-- erts/emulator/beam/erl_db_util.c | 3 +- erts/emulator/beam/erl_process.c | 11 +-- erts/emulator/beam/erl_trace.c | 100 +++++++++++++-------- erts/emulator/beam/erl_trace.h | 11 +-- erts/emulator/beam/io.c | 2 +- erts/emulator/nifs/common/erl_tracer_nif.c | 10 ++- erts/emulator/test/trace_bif_SUITE.erl | 1 + erts/emulator/test/tracer_SUITE.erl | 90 +++++++++---------- erts/emulator/test/tracer_SUITE_data/tracer_test.c | 12 +-- erts/preloaded/src/erl_tracer.erl | 8 +- 13 files changed, 153 insertions(+), 140 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml index 2075b962d8..7fd5512b33 100644 --- a/erts/doc/src/erl_tracer.xml +++ b/erts/doc/src/erl_tracer.xml @@ -90,7 +90,7 @@ If not set to true, the tracer has been requested to include the output of a match specification that was run. scheduler_id - Set to a number of the scheduler id is to be included by the tracer. + Set to a number if the scheduler id is to be included by the tracer. Otherwise it is set to undefined.

@@ -155,14 +155,13 @@ overhead associated with tracing. If trace is returned the necessary trace data will be created and the trace call-back of the tracer will be called. If discard is returned, this trace call - will be discarded and no call to trace will be done. If - remove is returned, the VM will attempt to remove this tracer - from the tracee, together with any trace flags set on the tracee. + will be discarded and no call to trace will be done.

trace_status is a special type of TraceTag which is used to check if the tracer should still be active. It is called in multiple scenarios, but most significantly it is used when tracing is started - using this tracer.

+ using this tracer. If remove is returned when the trace_status + is checked, the tracer will be removed from the tracee.

This function may be called multiple times per tracepoint, so it is important that it is both fast and side effect free.

@@ -617,7 +616,7 @@ static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, } /* - * argv[0]: Trace Tag + * argv[0]: TraceTag * argv[1]: TracerState * argv[2]: Tracee */ @@ -626,8 +625,11 @@ static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifPid to_pid; if (enif_get_local_pid(env, argv[1], &to_pid)) if (!enif_is_process_alive(env, &to_pid)) - /* tracer is dead so we should remove this tracepoint */ - return enif_make_atom(env, "remove"); + if (enif_is_identical(enif_make_atom(env, "trace_status"), argv[0])) + /* tracer is dead so we should remove this tracepoint */ + return enif_make_atom(env, "remove"); + else + return enif_make_atom(env, "discard"); /* Only generate trace for when tracer != tracee */ if (enif_is_identical(argv[1], argv[2])) @@ -645,7 +647,7 @@ static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) } /* - * argv[0]: Trace Tag, should only be 'send' + * argv[0]: TraceTag, should only be 'send' * argv[1]: TracerState, process to send {argv[2], argv[4]} to * argv[2]: Tracee * argv[3]: Message, ignored diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 308e5ce205..8489897d3a 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -655,8 +655,7 @@ erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg) erts_smp_atomic_inc_nob(&bp->count->acount); } - if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE - && ERTS_TRACER_PROC_IS_ENABLED(c_p)) { + if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE) { Eterm w; erts_trace_time_call(c_p, I, bp->time); w = (BeamInstr) *c_p->cp; @@ -753,8 +752,7 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) } } if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE && - IS_TRACED_FL(p, F_TRACE_CALLS) && - ERTS_TRACER_PROC_IS_ENABLED(p)) { + IS_TRACED_FL(p, F_TRACE_CALLS)) { BeamInstr *pc = (BeamInstr *)ep->code+3; erts_trace_time_call(p, pc, bp->time); } diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index b65c0e303f..66e5146da0 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -512,8 +512,7 @@ start_trace(Process *c_p, ErtsTracer tracer, && !ERTS_TRACER_COMPARE(ERTS_TRACER(port), tracer)) { /* This tracee is already being traced, and not by the * tracer to be */ - if (erts_is_tracer_proc_enabled(c_p, ERTS_PROC_LOCKS_ALL, - common, am_trace_status)) { + if (erts_is_tracer_enabled(tracer, common)) { /* The tracer is still in use */ return 1; } @@ -856,7 +855,7 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) return am_undefined; if (!ERTS_TRACER_IS_NIL(ERTS_TRACER(tracee))) - erts_is_tracer_proc_enabled(NULL, 0, &tracee->common, am_trace_status); + erts_is_tracer_proc_enabled(NULL, 0, &tracee->common); tracer = erts_tracer_to_term(p, ERTS_TRACER(tracee)); trace_flags = ERTS_TRACE_FLAGS(tracee); @@ -864,22 +863,24 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) erts_port_release(tracee); } else if (is_internal_pid(pid_spec)) { - Process *tracee; - tracee = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, - pid_spec, ERTS_PROC_LOCK_MAIN); + Process *tracee = erts_pid2proc_not_running(p, ERTS_PROC_LOCK_MAIN, + pid_spec, ERTS_PROC_LOCK_MAIN); + + if (tracee == ERTS_PROC_LOCK_BUSY) + ERTS_BIF_YIELD2(bif_export[BIF_trace_info_2], p, pid_spec, key); if (!tracee) return am_undefined; if (!ERTS_TRACER_IS_NIL(ERTS_TRACER(tracee))) erts_is_tracer_proc_enabled(tracee, ERTS_PROC_LOCK_MAIN, - &tracee->common, am_trace_status); + &tracee->common); tracer = erts_tracer_to_term(p, ERTS_TRACER(tracee)); trace_flags = ERTS_TRACE_FLAGS(tracee); - if (tracee != p) - erts_smp_proc_unlock(tracee, ERTS_PROC_LOCK_MAIN); + if (tracee != p) + erts_smp_proc_unlock(tracee, ERTS_PROC_LOCK_MAIN); } else if (is_external_pid(pid_spec) && external_pid_dist_entry(pid_spec) == erts_this_dist_entry) { return am_undefined; diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 3e75b9fd5f..6732b708a8 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -174,7 +174,8 @@ set_match_trace(Process *tracee_p, Eterm fail_term, ErtsTracer tracer, ERTS_PROC_LOCKS_ALL == erts_proc_lc_my_proc_locks(tracee_p) || erts_thr_progress_is_blocking()); - if (ERTS_TRACER_IS_NIL(tracer) || erts_is_tracer_enabled(tracee_p, tracer)) + if (ERTS_TRACER_IS_NIL(tracer) + || erts_is_tracer_enabled(tracer, &tracee_p->common)) return set_tracee_flags(tracee_p, tracer, d_flags, e_flags); return fail_term; } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index b50ca6d009..9d895f1867 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -10085,7 +10085,10 @@ Process *schedule(Process *p, int calls) erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - if (IS_TRACED(p)) { + /* Clear tracer if it has been removed */ + if (IS_TRACED(p) && erts_is_tracer_proc_enabled( + p, ERTS_PROC_LOCK_MAIN, &p->common)) { + if (state & ERTS_PSFLG_EXITING) { if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT)) trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in_exiting); @@ -10100,12 +10103,6 @@ Process *schedule(Process *p, int calls) } } - -#ifdef ERTS_SMP - /* Clears tracer if it has been removed */ - (void)ERTS_TRACER_PROC_IS_ENABLED(p); -#endif - if (state & (ERTS_PSFLG_RUNNING_SYS | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { /* diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 1c0fc0a11f..447b46d239 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -396,7 +396,7 @@ send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, Eterm tag, Eterm msg, Eterm extra, Eterm pam_result); static ERTS_INLINE Eterm -call_enabled_tracer(Process *c_p, const ErtsTracer tracer, +call_enabled_tracer(const ErtsTracer tracer, ErtsTracerNif **tnif_ref, enum ErtsTracerOpt topt, Eterm tag, Eterm t_p_id); @@ -459,8 +459,7 @@ erts_set_system_seq_tracer(Process *c_p, ErtsProcLocks c_p_locks, ErtsTracer new if (!ERTS_TRACER_IS_NIL(new)) { Eterm nif_result = call_enabled_tracer( - NULL, new, NULL, - TRACE_FUN_ENABLED, am_trace_status, am_undefined); + new, NULL, TRACE_FUN_ENABLED, am_trace_status, am_undefined); switch (nif_result) { case am_trace: break; default: @@ -492,7 +491,7 @@ erts_get_system_seq_tracer(void) erts_smp_rwmtx_runlock(&sys_trace_rwmtx); if (st != erts_tracer_nil && - call_enabled_tracer(NULL, st, NULL, TRACE_FUN_ENABLED, + call_enabled_tracer(st, NULL, TRACE_FUN_ENABLED, am_trace_status, am_undefined) == am_remove) { erts_set_system_seq_tracer(NULL, 0, erts_tracer_nil); st = erts_tracer_nil; @@ -513,7 +512,7 @@ get_default_tracing(Uint *flagsp, ErtsTracer *tracerp, *default_trace_flags &= ~TRACEE_FLAGS; } else { Eterm nif_res; - nif_res = call_enabled_tracer(NULL, *default_tracer, + nif_res = call_enabled_tracer(*default_tracer, NULL, TRACE_FUN_ENABLED, am_trace_status, am_undefined); switch (nif_res) { @@ -915,8 +914,8 @@ seq_trace_update_send(Process *p) ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p)))); if (have_no_seqtrace(SEQ_TRACE_TOKEN(p)) || (seq_tracer != NIL && - call_enabled_tracer(NULL, seq_tracer, NULL, - TRACE_FUN_ENABLED, am_trace_status, + call_enabled_tracer(seq_tracer, NULL, + TRACE_FUN_ENABLED, am_seq_trace, p ? p->common.id : am_undefined) != am_trace) #ifdef USE_VM_PROBES || (SEQ_TRACE_TOKEN(p) == am_have_dt_utag) @@ -963,9 +962,9 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, ASSERT(is_tuple(token) || is_nil(token)); if (token == NIL || (process && ERTS_TRACE_FLAGS(process) & F_SENSITIVE) || ERTS_TRACER_IS_NIL(seq_tracer) || - call_enabled_tracer(NULL, seq_tracer, + call_enabled_tracer(seq_tracer, NULL, TRACE_FUN_ENABLED, - am_trace_status, + am_seq_trace, process ? process->common.id : am_undefined) != am_trace) { return; } @@ -1186,7 +1185,11 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, * use process flags */ tracee_flags = &ERTS_TRACE_FLAGS(p); + /* Is is not ideal at all to call this check twice, + it should be optimized so that only one call is made. */ if (!is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, + TRACE_FUN_ENABLED, am_trace_status) + || !is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif, TRACE_FUN_E_CALL, am_call)) { return 0; } @@ -1202,13 +1205,21 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, } meta_flags = F_TRACE_CALLS | F_NOW_TS; tracee_flags = &meta_flags; - switch (call_enabled_tracer(p, *tracer, - &tnif, TRACE_FUN_T_CALL, - am_call, p->common.id)) { + switch (call_enabled_tracer(*tracer, + &tnif, TRACE_FUN_ENABLED, + am_trace_status, p->common.id)) { default: case am_remove: *tracer = erts_tracer_nil; case am_discard: return 0; - case am_trace: break; + case am_trace: + switch (call_enabled_tracer(*tracer, + &tnif, TRACE_FUN_T_CALL, + am_call, p->common.id)) { + default: + case am_discard: return 0; + case am_trace: break; + } + break; } } @@ -1346,9 +1357,9 @@ trace_proc(Process *c_p, ErtsProcLocks c_p_locks, Process *t_p, Eterm what, Eterm data) { ErtsTracerNif *tnif = NULL; - if (is_tracer_enabled(c_p, c_p_locks, &t_p->common, &tnif, + if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_PROCS, what)) - send_to_tracer_nif(c_p, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PROCS, + send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PROCS, what, data, THE_NON_VALUE, am_true); } @@ -1365,16 +1376,15 @@ trace_proc_spawn(Process *p, Eterm what, Eterm pid, Eterm mod, Eterm func, Eterm args) { ErtsTracerNif *tnif = NULL; - if (is_tracer_enabled(p, ERTS_PROC_LOCKS_ALL & - ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE), - &p->common, &tnif, TRACE_FUN_E_PROCS, what)) { + if (is_tracer_enabled(NULL, 0, + &p->common, &tnif, TRACE_FUN_E_PROCS, what)) { Eterm mfa; Eterm* hp; hp = HAlloc(p, 4); mfa = TUPLE3(hp, mod, func, args); hp += 4; - send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_PROCS, + send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, TRACE_FUN_T_PROCS, what, pid, mfa, am_true); } } @@ -2896,23 +2906,28 @@ send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p, } static ERTS_INLINE Eterm -call_enabled_tracer(Process *c_p, const ErtsTracer tracer, +call_enabled_tracer(const ErtsTracer tracer, ErtsTracerNif **tnif_ret, enum ErtsTracerOpt topt, Eterm tag, Eterm t_p_id) { ErtsTracerNif *tnif = lookup_tracer_nif(tracer); if (tnif) { - Eterm argv[] = {tag, ERTS_TRACER_STATE(tracer), t_p_id}; + Eterm argv[] = {tag, ERTS_TRACER_STATE(tracer), t_p_id}, + ret; topt = (tnif->tracers[topt].cb) ? topt : TRACE_FUN_ENABLED; ASSERT(topt < NIF_TRACER_TYPES); ASSERT(tnif->tracers[topt].cb != NULL); if (tnif_ret) *tnif_ret = tnif; - return erts_nif_call_function(c_p, NULL, tnif->nif_mod, - tnif->tracers[topt].cb, - tnif->tracers[topt].arity, - argv); + ret = erts_nif_call_function(NULL, NULL, tnif->nif_mod, + tnif->tracers[topt].cb, + tnif->tracers[topt].arity, + argv); + if (tag == am_trace_status && ret != am_remove) + return am_trace; + ASSERT(tag == am_trace_status || ret != am_remove); + return ret; } - return am_remove; + return tag == am_trace_status ? am_remove : am_discard; } static int @@ -2937,12 +2952,12 @@ is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks, } #endif - nif_result = call_enabled_tracer(c_p, t_p->tracer, tnif_ret, topt, tag, t_p->id); + nif_result = call_enabled_tracer(t_p->tracer, tnif_ret, topt, tag, t_p->id); switch (nif_result) { case am_discard: return 0; case am_trace: return 1; case THE_NON_VALUE: - case am_remove: break; + case am_remove: ASSERT(tag == am_trace_status); break; default: /* only am_remove should be returned, but if something else is returned we fall-through @@ -2973,19 +2988,14 @@ is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks, return 0; } -int erts_is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, - ErtsPTabElementCommon *t_p, Eterm type) -{ - return is_tracer_enabled(c_p, c_p_locks, t_p, NULL, TRACE_FUN_ENABLED, am_trace_status); -} - -int erts_is_tracer_enabled(Process *c_p, const ErtsTracer tracer) +int erts_is_tracer_enabled(const ErtsTracer tracer, ErtsPTabElementCommon *t_p) { ErtsTracerNif *tnif = lookup_tracer_nif(tracer); if (tnif) { - Eterm nif_result = call_enabled_tracer(c_p, tracer, &tnif, - TRACE_FUN_ENABLED, am_trace_status, - c_p->common.id); + Eterm nif_result = call_enabled_tracer(tracer, &tnif, + TRACE_FUN_ENABLED, + am_trace_status, + t_p->id); switch (nif_result) { case am_discard: case am_trace: return 1; @@ -2996,6 +3006,20 @@ int erts_is_tracer_enabled(Process *c_p, const ErtsTracer tracer) return 0; } +int erts_is_tracer_proc_enabled(Process* c_p, ErtsProcLocks c_p_locks, + ErtsPTabElementCommon *t_p) +{ + return is_tracer_enabled(c_p, c_p_locks, t_p, NULL, TRACE_FUN_ENABLED, + am_trace_status); +} + +int erts_is_tracer_proc_enabled_send(Process* c_p, ErtsProcLocks c_p_locks, + ErtsPTabElementCommon *t_p) +{ + return is_tracer_enabled(c_p, c_p_locks, t_p, NULL, TRACE_FUN_T_SEND, am_send); +} + + void erts_tracer_replace(ErtsPTabElementCommon *t_p, const ErtsTracer tracer) { #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index de4beadb7e..0a0c5fc913 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -193,8 +193,10 @@ int erts_finish_breakpointing(void); /* Nif tracer functions */ int erts_is_tracer_proc_enabled(Process *c_p, ErtsProcLocks c_p_locks, - ErtsPTabElementCommon *t_p, Eterm type); -int erts_is_tracer_enabled(Process *c_p, const ErtsTracer tracer); + ErtsPTabElementCommon *t_p); +int erts_is_tracer_proc_enabled_send(Process* c_p, ErtsProcLocks c_p_locks, + ErtsPTabElementCommon *t_p); +int erts_is_tracer_enabled(const ErtsTracer tracer, ErtsPTabElementCommon *t_p); Eterm erts_tracer_to_term(Process *p, ErtsTracer tracer); ErtsTracer erts_term_to_tracer(Eterm prefix, Eterm term); void erts_tracer_replace(ErtsPTabElementCommon *t_p, @@ -224,9 +226,4 @@ ERTS_DECLARE_DUMMY(erts_tracer_nil) = NIL; #define ERTS_TRACER_FROM_ETERM(termp) \ ((ErtsTracer*)(termp)) -#define ERTS_TRACER_PROC_IS_ENABLED(PROC) \ - (!ERTS_TRACER_IS_NIL(ERTS_TRACER(PROC)) \ - && erts_is_tracer_proc_enabled(PROC, ERTS_PROC_LOCK_MAIN, \ - &(PROC)->common, am_trace_status)) - #endif /* ERL_TRACE_H__ */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index ef326fafec..0377f6cb5e 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -5941,7 +5941,7 @@ driver_deliver_term(Port *prt, Eterm to, ErlDrvTermData* data, int len) if (!rp) { if (!prt || !IS_TRACED_FL(prt, F_TRACE_SEND)) goto done; - if (!erts_is_tracer_proc_enabled(NULL, 0, &prt->common, am_send)) + if (!erts_is_tracer_proc_enabled_send(NULL, 0, &prt->common)) goto done; res = -2; diff --git a/erts/emulator/nifs/common/erl_tracer_nif.c b/erts/emulator/nifs/common/erl_tracer_nif.c index 8a9a1bf16c..6dddc80607 100644 --- a/erts/emulator/nifs/common/erl_tracer_nif.c +++ b/erts/emulator/nifs/common/erl_tracer_nif.c @@ -70,6 +70,7 @@ ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload) ATOM_DECL(strict_monotonic); \ ATOM_DECL(timestamp); \ ATOM_DECL(trace); \ + ATOM_DECL(trace_status); \ ATOM_DECL(trace_ts); \ ATOM_DECL(true); \ ATOM_DECL(gc_minor_start); \ @@ -118,19 +119,22 @@ static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ErlNifPid to_pid; ErlNifPort to_port; + ERL_NIF_TERM ret = enif_is_identical(argv[0], atom_trace_status) ? + atom_remove : atom_discard; + ASSERT(argc == 3); if (enif_get_local_pid(env, argv[1], &to_pid)) { if (!enif_is_process_alive(env, &to_pid)) /* tracer is dead so we should remove this trace point */ - return atom_remove; + return ret; } else if (enif_get_local_port(env, argv[1], &to_port)) { if (!enif_is_port_alive(env, &to_port)) /* tracer is dead so we should remove this trace point */ - return atom_remove; + return ret; } else { /* The state was not a pid or a port */ - return atom_remove; + return ret; } /* Only generate trace for when tracer != tracee */ diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index 8c3ffccc45..491b37ae46 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -232,6 +232,7 @@ do_trace_bif_return(TsType, TsFlags) -> {?MODULE, bif_process,0}}, Ts11, TsType), check_ts(TsType, Ts12, make_ts(TsType)), + erlang:trace_pattern({erlang,'_','_'}, false, [local]), ok. diff --git a/erts/emulator/test/tracer_SUITE.erl b/erts/emulator/test/tracer_SUITE.erl index de44d6656a..20fb7e475e 100644 --- a/erts/emulator/test/tracer_SUITE.erl +++ b/erts/emulator/test/tracer_SUITE.erl @@ -44,6 +44,8 @@ groups() -> gc_start, gc_end]}]. init_per_suite(Config) -> + erlang:trace_pattern({'_','_','_'}, false, [local]), + erlang:trace_pattern({'_','_','_'}, false, []), purge(), Config. @@ -119,23 +121,24 @@ unload(_Config) -> end, 1 = erlang:trace(Pid, true, [{tracer, tracer_test, - {#{ call => trace}, self(), []}}, + {#{ call => trace }, self(), []}}, call]), 1 = erlang:trace_pattern({?MODULE, all, 0}, [], []), Tc(1), - receive _ -> ok after 0 -> ct:fail(timeout) end, + receive _M -> ok after 0 -> ct:fail(timeout) end, + receive M0 -> ct:fail({unexpected_message0, M0}) after 0 -> ok end, code:purge(tracer_test), code:delete(tracer_test), Tc(1), - receive M1 -> ct:fail({unexpected_message, M1}) after 0 -> ok end, + receive M1 -> ct:fail({unexpected_message1, M1}) after 0 -> ok end, code:purge(tracer_test), Tc(1), - receive M2 -> ct:fail({unexpected_message, M2}) after 0 -> ok end, + receive M2 -> ct:fail({unexpected_message2, M2}) after 0 -> ok end, ok. @@ -167,13 +170,19 @@ reload(_Config) -> false = code:purge(tracer_test), true = code:delete(tracer_test), - false = code:purge(tracer_test) + false = code:purge(tracer_test), + timer:sleep(10) end || _ <- lists:seq(1,15)], ok. reload_loop() -> ?MODULE:all(), + ?MODULE:all(), + ?MODULE:all(), + ?MODULE:all(), + ?MODULE:all(), + timer:sleep(1), reload_loop(). invalid_tracers(_Config) -> @@ -214,7 +223,7 @@ send(_Config) -> Expect = fun(Pid, State, EOpts) -> receive Msg -> - {Pid, send, State, Pid, ok, Self, Opts} = Msg, + {send, State, Pid, ok, Self, Opts} = Msg, check_opts(EOpts, Opts) end end, @@ -230,7 +239,7 @@ recv(_Config) -> Expect = fun(Pid, State, EOpts) -> receive Msg -> - {undefined, 'receive', State, Pid, ok, undefined, Opts} = Msg, + {'receive', State, Pid, ok, undefined, Opts} = Msg, check_opts(EOpts, Opts) end end, @@ -247,14 +256,14 @@ spawn(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {Pid, spawn, State, Pid, NewPid, + {spawn, State, Pid, NewPid, {lists,seq,[1,10]}, Opts} = Msg, check_opts(EOpts, Opts), true = is_pid(NewPid) andalso NewPid /= Pid end end, - test(spawn, procs, Tc, Expect, true). + test(spawn, procs, Tc, Expect, false). exit(_Config) -> Tc = fun(Pid) -> @@ -265,7 +274,7 @@ exit(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {Pid, exit, State, Pid, normal, undefined, Opts} = Msg, + {exit, State, Pid, normal, undefined, Opts} = Msg, check_opts(EOpts, Opts) end end, @@ -286,13 +295,13 @@ link(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {Pid, link, State, Pid, NewPid, undefined, Opts} = Msg, + {link, State, Pid, NewPid, undefined, Opts} = Msg, check_opts(EOpts, Opts), true = is_pid(NewPid) andalso NewPid /= Pid end end, - test(link, procs, Tc, Expect, true). + test(link, procs, Tc, Expect, false). unlink(_Config) -> @@ -309,13 +318,13 @@ unlink(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {Pid, unlink, State, Pid, NewPid, undefined, Opts} = Msg, + {unlink, State, Pid, NewPid, undefined, Opts} = Msg, check_opts(EOpts, Opts), true = is_pid(NewPid) andalso NewPid /= Pid end end, - test(unlink, procs, Tc, Expect, true). + test(unlink, procs, Tc, Expect, false). getting_linked(_Config) -> @@ -331,7 +340,7 @@ getting_linked(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {NewPid, getting_linked, State, Pid, NewPid, undefined, Opts} = Msg, + {getting_linked, State, Pid, NewPid, undefined, Opts} = Msg, check_opts(EOpts, Opts), true = is_pid(NewPid) andalso NewPid /= Pid end @@ -355,7 +364,7 @@ getting_unlinked(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {NewPid, getting_unlinked, State, Pid, NewPid, undefined, Opts} = Msg, + {getting_unlinked, State, Pid, NewPid, undefined, Opts} = Msg, check_opts(EOpts, Opts), true = is_pid(NewPid) andalso NewPid /= Pid end @@ -377,12 +386,12 @@ register(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {Pid, register, State, Pid, ?MODULE, undefined, Opts} = Msg, + {register, State, Pid, ?MODULE, undefined, Opts} = Msg, check_opts(EOpts, Opts) end end, - test(register, procs, Tc, Expect, true). + test(register, procs, Tc, Expect, false). unregister(_Config) -> @@ -398,18 +407,18 @@ unregister(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {Pid, unregister, State, Pid, ?MODULE, undefined, Opts} = Msg, + {unregister, State, Pid, ?MODULE, undefined, Opts} = Msg, check_opts(EOpts, Opts) end end, - test(unregister, procs, Tc, Expect, true). + test(unregister, procs, Tc, Expect, false). in(_Config) -> Tc = fun(Pid) -> Self = self(), - Pid ! fun() -> receive after 1 -> Self ! ok end end, + Pid ! fun() -> receive after 10 -> Self ! ok end end, receive ok -> ok end end, @@ -418,7 +427,7 @@ in(_Config) -> N = (fun F(N) -> receive Msg -> - {Pid, in, State, Pid, _, + {in, State, Pid, _, undefined, Opts} = Msg, check_opts(EOpts, Opts), F(N+1) @@ -428,7 +437,7 @@ in(_Config) -> true = N > 0 end, - test(in, running, Tc, Expect, true). + test(in, running, Tc, Expect, false). out(_Config) -> Tc = fun(Pid) -> @@ -443,7 +452,7 @@ out(_Config) -> N = (fun F(N) -> receive Msg -> - {Pid, out, State, Pid, _, + {out, State, Pid, _, undefined, Opts} = Msg, check_opts(EOpts, Opts), F(N+1) @@ -453,7 +462,7 @@ out(_Config) -> true = N > 0 end, - test(out, running, Tc, Expect, true, true). + test(out, running, Tc, Expect, false, true). gc_start(_Config) -> @@ -468,12 +477,12 @@ gc_start(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {Pid, gc_major_start, State, Pid, _, undefined, Opts} = Msg, + {gc_major_start, State, Pid, _, undefined, Opts} = Msg, check_opts(EOpts, Opts) end end, - test(gc_major_start, garbage_collection, Tc, Expect, true). + test(gc_major_start, garbage_collection, Tc, Expect, false). gc_end(_Config) -> @@ -488,20 +497,20 @@ gc_end(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {Pid, gc_major_end, State, Pid, _, undefined, Opts} = Msg, + {gc_major_end, State, Pid, _, undefined, Opts} = Msg, check_opts(EOpts, Opts) end end, - test(gc_major_end, garbage_collection, Tc, Expect, true). + test(gc_major_end, garbage_collection, Tc, Expect, false). test(Event, Tc, Expect) -> - test(Event, Tc, Expect, true). + test(Event, Tc, Expect, false). test(Event, Tc, Expect, Removes) -> test(Event, Event, Tc, Expect, Removes). test(Event, TraceFlag, Tc, Expect, Removes) -> test(Event, TraceFlag, Tc, Expect, Removes, false). -test(Event, TraceFlag, Tc, Expect, Removes, Dies) -> +test(Event, TraceFlag, Tc, Expect, _Removes, Dies) -> ComplexState = {fun() -> ok end, <<0:(128*8)>>}, Opts = #{ timestamp => undefined, @@ -557,25 +566,6 @@ test(Event, TraceFlag, Tc, Expect, Removes, Dies) -> ok end, - %% Test that remove works - Pid3 = start_tracee(), - State3 = {#{ Event => remove }, self(), ComplexState}, - 1 = erlang:trace(Pid3, true, [TraceFlag, {tracer, tracer_test, State3}]), - Tc(Pid3), - ok = trace_delivered(Pid3), - receive M3 -> ct:fail({unexpected, M3}) after 0 -> ok end, - if not Dies -> - if Removes -> - {flags, []} = erlang:trace_info(Pid3, flags), - {tracer, []} = erlang:trace_info(Pid3, tracer); - true -> - {flags, [TraceFlag]} = erlang:trace_info(Pid3, flags), - {tracer, {tracer_test, State3}} = erlang:trace_info(Pid3, tracer) - end, - erlang:trace(Pid3, false, [TraceFlag]); - true -> - ok - end, ok. check_opts(#{ scheduler_id := number } = E, #{ scheduler_id := N } = O) diff --git a/erts/emulator/test/tracer_SUITE_data/tracer_test.c b/erts/emulator/test/tracer_SUITE_data/tracer_test.c index 8b4be1345d..908f35da9c 100644 --- a/erts/emulator/test/tracer_SUITE_data/tracer_test.c +++ b/erts/emulator/test/tracer_SUITE_data/tracer_test.c @@ -104,16 +104,10 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) enif_get_tuple(env, argv[1], &state_arity, &state_tuple); - tuple = enif_alloc(sizeof(ERL_NIF_TERM)*(argc+1)); - memcpy(tuple+1,argv,sizeof(ERL_NIF_TERM)*argc); + tuple = enif_alloc(sizeof(ERL_NIF_TERM)*(argc)); + memcpy(tuple,argv,sizeof(ERL_NIF_TERM)*argc); - if (enif_self(env, &self)) { - tuple[0] = enif_make_pid(env, &self); - } else { - tuple[0] = enif_make_atom(env, "undefined"); - } - - msg = enif_make_tuple_from_array(env, tuple, argc + 1); + msg = enif_make_tuple_from_array(env, tuple, argc); enif_get_local_pid(env, state_tuple[1], &to); enif_send(env, &to, NULL, msg); enif_free(tuple); diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl index de1e9ca01e..fe15812535 100644 --- a/erts/preloaded/src/erl_tracer.erl +++ b/erts/preloaded/src/erl_tracer.erl @@ -41,10 +41,14 @@ on_load() -> %%% NIF placeholders %%% --spec enabled(Tag :: trace_tag() | seq_trace | trace_status, +-spec enabled(Tag :: trace_status, TracerState :: tracer_state(), Tracee :: tracee()) -> - trace | discard | remove. + trace | remove; + (Tag :: trace_tag() | seq_trace, + TracerState :: tracer_state(), + Tracee :: tracee()) -> + trace | discard. enabled(_, _, _) -> erlang:nif_error(nif_not_loaded). -- cgit v1.2.3 From bd64ad8e15d66e48b36dbe3584315dd5cfc8b59a Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Wed, 11 May 2016 17:22:23 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 625 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 625 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 7501ccd9ce..7d39461f10 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -32,6 +32,631 @@

This document describes the changes made to the ERTS application.

+
Erts 8.0 + +
Fixed Bugs and Malfunctions + + +

The handling of on_load functions has been + improved. The major improvement is that if a code upgrade + fails because the on_load function fails, the + previous version of the module will now be retained.

+

+ Own Id: OTP-12593

+
+ +

is_builtin(erlang, apply, 3) will now return + true.

+

+ Own Id: OTP-13034

+
+ +

+ Fix enif_get_list_length to return false if list + is improper or have length larger than UINT_MAX + (did return true and an incorrect length value).

+

+ Own Id: OTP-13288 Aux Id: PR913

+
+ +

+ Cleanup hipe signal handling code for x86 and make it + more portable.

+

+ Own Id: OTP-13341 Aux Id: PR951

+
+ +

+ Use fsync instead of fdatasync on Mac OSX.

+

+ Own Id: OTP-13411

+
+ +

+ Make sure to create a crash dump when running out of + memory. This was accidentally removed in the erts-7.3 + release.

+

+ Own Id: OTP-13419

+
+ +

+ A bug has been fixed where if erlang was started +B on a + unix platform it would be killed by a SIGUSR2 signal when + creating a crash dump.

+

+ Own Id: OTP-13425

+
+ +

+ Fix race between process_flag(trap_exit,true) and + a received exit signal.

+

+ A process could terminate due to exit signal even though + process_flag(trap_exit,true) had returned. A very + specific timing between call to process_flag/2 and + exit signal from another scheduler was required for this + to happen.

+

+ Own Id: OTP-13452

+
+ +

Don't search for non-existing Map keys twice

+

For maps:get/2,3 and maps:find/2, + searching for an immediate key, e.g. an atom, in a small + map, the search was performed twice if the key did not + exist.

+

+ Own Id: OTP-13459

+
+ +

+ When a abnormally large distribution message is about to + be sent, the VM has been changed to create a crash dump + instead of a core dump.

+

+ Own Id: OTP-13474

+
+ +

+ Fix erlang:process_info/2 type specification

+

+ Own Id: OTP-13485 Aux Id: ERL-123

+
+ +

+ Fix bug in open_port/2 with option {args, + List}. A vm crash could be caused by an improper + List.

+

+ Own Id: OTP-13489 Aux Id: ERL-127

+
+ +

+ Don't crash on terminating processes with + erlang:system_profile/1,2

+

+ Own Id: OTP-13494 Aux Id: ERL-126

+
+ +

+ Fixed bugs where the reduction counter was not handled + correct.

+

+ Own Id: OTP-13512

+
+ +

+ Fixed typo in description of the EPMD_DUMP_REQ + response.

+

+ Own Id: OTP-13517

+
+ +

+ Fixed a bug where a process flagged as sensitive would + sometimes record its save_calls when it shouldn't.

+

+ Own Id: OTP-13540

+
+ +

+ Update configure scripts to not use hardcoded path for + /bin/pwd and /bin/rm.

+

+ Own Id: OTP-13562

+
+
+
+ + +
Improvements and New Features + + +

+ The tracing support has been extended to allow a tracer module to be the + trace event handler instead of a process or port. The + tracer module + makes it possible for trace tools to filter or manipulate + trace event data without the trace event first haing to + be copied from the traced process or port.

+

+ With the introduction of this feature, + erlang:trace(all|existing, _, _) now also returns + the tracer process as part of the number of processes on + which tracing is enabled. The is incompatible with the + previous releases.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-10267

+
+ +

+ Introduce LTTng tracing of Erlang Runtime System

+

+ For LTTng to be enabled OTP needs to be built with + configure option --with-dynamic-trace=lttng.

+

+ This feature introduces tracepoints for schedulers, + drivers, memory carriers, memory and async thread pool.

+

For a list of all tracepoints, see Runtime Tools User's + Guide .

+

+ Own Id: OTP-10282 Aux Id: kunagi-14 [14]

+
+ +

+ Add microstate accounting

+

+ Microstate accounting is a way to track which state the + different threads within ERTS are in. The main usage area + is to pin point performance bottlenecks by checking which + states the threads are in and then from there figuring + out why and where to optimize.

+

+ Since checking whether microstate accounting is on or off + is relatively expensive only a few of the states are + enabled by default and more states can be enabled through + configure.

+

+ There is a convinence module called msacc that has been + added to runtime_tools that can assist in gathering and + interpreting the data from Microstate accounting.

+

+ For more information see erlang:statistics(microstate_accounting, + _) and the msacc module in + runtime_tools.

+

+ Own Id: OTP-12345

+
+ +

+ The port of Erlang/OTP to the realtime operating system + OSE has been removed.

+

+ Own Id: OTP-12573

+
+ +

+ Sharing preserved copy for messages and exit signals

+

+ Enable sharing preserved copy with configure option + --enable-sharing-preserving. This will preserve + sharing, within the process, when communication with + other processes in the Erlang node. There is a trade-off, + the copy is more costly but this cost can be reclaimed if + there is a lot of sharing in the message. With this + feature enabled literals will not be copied in a send + except during a purge phase of the module where the + literals are located.

+

+ Own Id: OTP-12590 Aux Id: OTP-10251

+
+ +

+ Halfword BEAM has been removed.

+

+ Own Id: OTP-12883

+
+ +

+ Added os:perf_counter/1.

+

+ The perf_counter is a very very cheap and high resolution + timer that can be used to timestamp system events. It + does not have monoticity guarantees, but should on most + OS's expose a monotonous time.

+

+ Own Id: OTP-12908

+
+ +

+ Support for a fragmented young heap generation. That is, + the young heap generation can consist of multiple non + continuous memory areas. The main reason for this change + is to avoid extra copying of messages that could not be + allocated directly on the receivers heap.

+

+ Own Id: OTP-13047

+
+ +

+ Erlang linked-in driver can now force the call to + open_port to block until a call to erl_drv_init_ack is + made inside the driver. This is useful when you want to + do some asynchronous initialization, for example getting + configuration from a pipe, and you want the initial + open_port call to fail if the configuration is incomplete + or wrong. See the erl_driver documentation for more + details on the API.

+

+ Own Id: OTP-13086

+
+ +

+ Erlang linked-in drivers can now set their own pid's as + seen in erlang:port_info/1 by using the + erl_drv_set_pid function. For more details see the + erl_driver documentation.

+

+ Own Id: OTP-13087

+
+ +

+ The functionality behind erlang:open_port/2 when + called with spawn or spawn_executable has been redone so + that the forking of the new program is done in a separate + process called erl_child_setup. This allows for a much + more robust implementation that uses less memory and does + not block the entire emulator if the program to be + started is on an un-accessible NFS. Benchmarks have shown + this approach to be about 3-5 times as fast as the old + approach where the fork/vfork was done by erts. This is a + pure stability and performance fix, however some error + messages may have changed, which is why it is marked as a + backwards incompatible change.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13088

+
+ +

Improved yielding strategy in the implementation of + the following native functions:

+ erlang:binary_to_list/1 + erlang:binary_to_list/3 + erlang:bitstring_to_list/1 + erlang:list_to_binary/1 + erlang:iolist_to_binary/1 + erlang:list_to_bitstring/1 + binary:list_to_bin/1

This + in order to improve performance of these functions.

+

+ Own Id: OTP-13096

+
+ +

+ All garbage collections of processes now bump reductions. + Also the amount of reductions bumped when garbage + collecting has been adjusted. It now better corresponds + to the amount of work performed. This in order to improve + the real time characteristics of the system.

+

+ Own Id: OTP-13097

+
+ +

New functions that can load multiple functions at once + have been added to the 'code' module. The + functions are code:atomic_load/1, + code:prepare_loading/1, + code:finish_loading/1, and + code:ensure_modules_loaded/1.

+

+ Own Id: OTP-13111

+
+ +

The function erl_prim_loader:start/3 has been + removed. Its documentation has also been removed.

+

The undocumented and unsupported function + erl_prim_loader:get_files/2 has been removed.

+

+ Own Id: OTP-13112

+
+ +

+ Low level BIF erlang:purge_module/1 is made more + robust against incorrect use. Lingering processes that + still refer the old code are now killed before the module + is purged to prevent fatal VM behavior.

+

+ Own Id: OTP-13122

+
+ +

+ Improved dirty scheduler implementation. For more + information see the NIF documentation.

+

+ Note that support for determining whether dirty NIF + support exist or not at compile time using the C + preprocessor macro ERL_NIF_DIRTY_SCHEDULER_SUPPORT + has been removed.

+

+ Own Id: OTP-13123

+
+ +

+ Various optimizations done to process dictionary access.

+

+ Own Id: OTP-13167

+
+ +

+ Added max_heap_size process flag. See erlang:process_flag + for more details.

+

+ Own Id: OTP-13174

+
+ +

+ Allow dynamic drivers and NIF libraries to be built with + gcc option -fvisibility=hidden for faster loading + and more optimized code.

+

+ Own Id: OTP-13227

+
+ +

+ Add erlang:process_info(Pid, + garbage_collection_info) which returns extended + garbage_collection information. For more details see the + documentation.

+

+ Own Id: OTP-13265

+
+ +

+ The functions erlang:list_to_integer/1 and + string:to_integer/1 have been optimized for large + inputs.

+

+ Own Id: OTP-13293

+
+ +

+ Introduction of configurable management of data referred + to by the message queue of a process. Each process can be + configured individually.

+

+ It is now possible to configure the message queue of a + process, so that all data referred by it will be kept + outside of the heap, and by this prevent this data from + being part of garbage collections.

+

+ For more information see the documentation of process_flag(message_queue_data, + MQD).

+

+ Own Id: OTP-13366 Aux Id: OTP-13047

+
+ +

+ Processes now yield when scanning large message queues + and not finding a matching message. This in order to + improve real time characteristics.

+

+ Own Id: OTP-13401

+
+ +

+ Optimized an erts internal function that is used to + traverse erlang terms. The internal function was mainly + used by term_to_binary and comparison of terms. + Benchmarks have shown up to a 10% performance increase in + those functions after the optimization.

+

+ Own Id: OTP-13440

+
+ +

+ Add the following NIF API functions:

+

+ enif_cpu_time + enif_now_time + enif_make_unique_integer + enif_is_process_alive + enif_is_port_alive + enif_term_to_binary + enif_binary_to_term + enif_port_command +

+

+ for details of what each function does, see the erl_nif + documentation.

+

+ Own Id: OTP-13442

+
+ +

+ Optimize '++' operator and lists:append/2 + by using a single pass to build a new list while checking + for properness.

+

+ Own Id: OTP-13487

+
+ +

+ Handle terms (pids,ports and refs) from nodes with a + 'creation' value larger than 3. This is a preparation of + the distribution protocol to allow OTP 19 nodes to + correctly communicate with future nodes (20 or higher). + The 'creation' value differentiates different + incarnations of the same node (name).

+

+ Own Id: OTP-13488

+
+ +

+ Don't send unasked for systemd notifications in epmd

+

+ Own Id: OTP-13493 Aux Id: PR-999

+
+ +

+ The enif_send API has been extended to allow NULL to be + used as the message environment. When used this way, a + message environent is implicitly created and the given + term is copied into that environment before sending. This + can be an optimization if many small messages are being + sent by the nif.

+

+ Own Id: OTP-13495

+
+ +

+ The tracing support has been extended to allow tracing on + ports. Ports can be traced on using the 'ports', 'send' + and 'receive' trace flags.

+

+ The first argument of erlang:trace/3 has + been extended so that 'all', 'existing' and + 'new' now include both processes and ports. New + Tracee variants, 'all_processes', + 'all_ports', 'existing_processes' etc have + been added to specify only processes or ports.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13496

+
+ +

+ When the 'procs' trace flag is enabled, a + 'spawned' trace event is now also generated by a + newly created process. The previous event 'spawn' + remains, but as it is generated by the process that did + the spawn, it is not guaranteed that it is ordered with + other trace events from the newly spawned process. So + when tracking the lifetime of a process this new event + should be used as the creation event.

+

+ This new trace event is marked as an incompatabiliy + because tools that expect certain trace events when + enabling 'procs' will have to updated.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13497

+
+ +

+ Add the erlang:match_spec_test/3 + function. The functions allows the testing of match + specifications for both tracing and ets tables. It can be + used to test that a match specification does the expected + filtering on specific data. It also returns more verbose + error reasons for incorrectly constructed match + specifications.

+

+ Own Id: OTP-13501

+
+ +

+ The erts internal tracing support has been changed to + have much less overhead and be more scalable.

+

+ This rewrite does not break any backwards + incompatabilities, but it does change the ordering of + some trace messages when compared to previous releases. + It should be noted that this only applies to trace + messages sent to processes or ports, it does not apply to + the new tracer module. However in future releases they + may also be effected by this.

+

+ Trace messages are only guaranteed to be ordered from one + traced process or port. In previous releases this was not + visible as a 'send' trace message would always + arrive before the corresponding 'receive' trace + message that is no longer always the case. This also + means that timestamped trace messages may seem to arrive + out of order as the timestamp is taken when the event is + triggered and not when it is put in the queue of the + tracer.

+

+ Own Id: OTP-13503

+
+ +

+ Add possibility to filter send and receive + trace with match specifications.

+

+ Own Id: OTP-13507

+
+ +

+ Add maps:update_with/3,4 and maps:take/2

+

+ Own Id: OTP-13522 Aux Id: PR-1025

+
+ +

+ Introduce LTTng tracing via Erlang tracing.

+

+ For LTTng to be enabled OTP needs to be built with + configure option --with-dynamic-trace=lttng.

+

The dynamic trace module dyntrace is now + capable to be used as a LTTng sink for Erlang tracing. + For a list of all tracepoints, see Runtime Tools User's + Guide .

+

This feature also introduces an incompatible change in + trace tags. The trace tags gc_start and + gc_end has been split into gc_minor_start, + gc_minor_end and gc_major_start, + gc_major_end.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13532

+
+ +

+ Print heap pointers for garbing processes during + crashdump

+

+ Own Id: OTP-13541 Aux Id: PR-1026

+
+ +

+ Changed and improved low level memory statistics returned + by erlang:system_info/1. The info for + erts_mmap has been moved from mseg_alloc to + its own section returned by {allocator, + erts_mmap}.

+

+ Own Id: OTP-13560

+
+
+
+ +
+
Erts 7.3.1
Fixed Bugs and Malfunctions -- cgit v1.2.3 From d3cde2d80d1cbcbbb70e4b8057a4f3772b7fa97f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 11 May 2016 17:49:45 +0200 Subject: erts: Add tests for enif_snprintf --- erts/emulator/test/nif_SUITE.erl | 17 ++++++++++++++--- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 20 +++++++++++++++++++- 2 files changed, 33 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index a7767132ee..e764071415 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -46,7 +46,8 @@ nif_now_time/1, nif_cpu_time/1, nif_unique_integer/1, nif_is_process_alive/1, nif_is_port_alive/1, nif_term_to_binary/1, nif_binary_to_term/1, - nif_port_command/1 + nif_port_command/1, + nif_snprintf/1 ]). -export([many_args_100/100]). @@ -81,8 +82,8 @@ all() -> nif_now_time, nif_cpu_time, nif_unique_integer, nif_is_process_alive, nif_is_port_alive, nif_term_to_binary, nif_binary_to_term, - nif_port_command - ]. + nif_port_command, + nif_snprintf]. init_per_testcase(_Case, Config) -> Config. @@ -2024,9 +2025,18 @@ nif_port_command(Config) -> port_close(Port), {'EXIT', {badarg, _}} = (catch port_command_nif(Port, "hello\n")), + ok. +nif_snprintf(Config) -> + ensure_lib_loaded(Config), + <<"ok",0>> = format_term_nif(3,ok), + <<"o",0>> = format_term_nif(2,ok), + <<"\"hello world\"",0>> = format_term_nif(14,"hello world"), + <<"{{hello,world,-33},3.14",_/binary>> = format_term_nif(50,{{hello,world, -33}, 3.14, self()}), + <<"{{hello,world,-33},",0>> = format_term_nif(20,{{hello,world, -33}, 3.14, self()}), ok. + %% The NIFs: lib_version() -> undefined. call_history() -> ?nif_stub. @@ -2091,6 +2101,7 @@ is_port_alive_nif(_) -> ?nif_stub. term_to_binary_nif(_, _) -> ?nif_stub. binary_to_term_nif(_, _, _) -> ?nif_stub. port_command_nif(_, _) -> ?nif_stub. +format_term_nif(_,_) -> ?nif_stub. %% maps is_map_nif(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 11e5dab58e..951b6959a8 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -2109,6 +2109,23 @@ static ERL_NIF_TERM port_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar return atom_true; } +static ERL_NIF_TERM format_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary obin; + unsigned int size; + + if (!enif_get_uint(env, argv[0], &size)) + return enif_make_badarg(env); + if (!enif_alloc_binary(size,&obin)) + return enif_make_badarg(env); + + if (enif_snprintf((char*)obin.data, (size_t)size, "%T", argv[1]) < 0) + return atom_false; + + return enif_make_binary(env,&obin); +} + + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -2190,7 +2207,8 @@ static ErlNifFunc nif_funcs[] = {"is_port_alive_nif", 1, is_port_alive}, {"term_to_binary_nif", 2, term_to_binary}, {"binary_to_term_nif", 3, binary_to_term}, - {"port_command_nif", 2, port_command} + {"port_command_nif", 2, port_command}, + {"format_term_nif", 2, format_term} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) -- cgit v1.2.3 From c324aab53c00cfe2d0809bf25fed14a78ccf3ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 11 May 2016 17:59:06 +0200 Subject: erts: Document enif_snprintf --- erts/doc/src/erl_nif.xml | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 7546f7ef81..d13302fa54 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -1653,6 +1653,15 @@ enif_map_iterator_destroy(env, &iter);

Get the byte size of a resource object obj obtained by enif_alloc_resource.

+ + intenif_snprintf(char *str, size_t size, const char *format, ...) + Format strings and Erlang terms + +

Similar to snprintf but this format string also accepts "%T" which formats Erlang terms. +

+
+
+ voidenif_system_info(ErlNifSysInfo *sys_info_ptr, size_t size) Get information about the Erlang runtime system -- cgit v1.2.3 From 663e847459686604ea051f036a0e4caff18cea6f Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 12 May 2016 12:04:14 +0200 Subject: Revert "Prepare release" This reverts commit bd64ad8e15d66e48b36dbe3584315dd5cfc8b59a. --- erts/doc/src/notes.xml | 625 ------------------------------------------------- 1 file changed, 625 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 7d39461f10..7501ccd9ce 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -32,631 +32,6 @@

This document describes the changes made to the ERTS application.

-
Erts 8.0 - -
Fixed Bugs and Malfunctions - - -

The handling of on_load functions has been - improved. The major improvement is that if a code upgrade - fails because the on_load function fails, the - previous version of the module will now be retained.

-

- Own Id: OTP-12593

-
- -

is_builtin(erlang, apply, 3) will now return - true.

-

- Own Id: OTP-13034

-
- -

- Fix enif_get_list_length to return false if list - is improper or have length larger than UINT_MAX - (did return true and an incorrect length value).

-

- Own Id: OTP-13288 Aux Id: PR913

-
- -

- Cleanup hipe signal handling code for x86 and make it - more portable.

-

- Own Id: OTP-13341 Aux Id: PR951

-
- -

- Use fsync instead of fdatasync on Mac OSX.

-

- Own Id: OTP-13411

-
- -

- Make sure to create a crash dump when running out of - memory. This was accidentally removed in the erts-7.3 - release.

-

- Own Id: OTP-13419

-
- -

- A bug has been fixed where if erlang was started +B on a - unix platform it would be killed by a SIGUSR2 signal when - creating a crash dump.

-

- Own Id: OTP-13425

-
- -

- Fix race between process_flag(trap_exit,true) and - a received exit signal.

-

- A process could terminate due to exit signal even though - process_flag(trap_exit,true) had returned. A very - specific timing between call to process_flag/2 and - exit signal from another scheduler was required for this - to happen.

-

- Own Id: OTP-13452

-
- -

Don't search for non-existing Map keys twice

-

For maps:get/2,3 and maps:find/2, - searching for an immediate key, e.g. an atom, in a small - map, the search was performed twice if the key did not - exist.

-

- Own Id: OTP-13459

-
- -

- When a abnormally large distribution message is about to - be sent, the VM has been changed to create a crash dump - instead of a core dump.

-

- Own Id: OTP-13474

-
- -

- Fix erlang:process_info/2 type specification

-

- Own Id: OTP-13485 Aux Id: ERL-123

-
- -

- Fix bug in open_port/2 with option {args, - List}. A vm crash could be caused by an improper - List.

-

- Own Id: OTP-13489 Aux Id: ERL-127

-
- -

- Don't crash on terminating processes with - erlang:system_profile/1,2

-

- Own Id: OTP-13494 Aux Id: ERL-126

-
- -

- Fixed bugs where the reduction counter was not handled - correct.

-

- Own Id: OTP-13512

-
- -

- Fixed typo in description of the EPMD_DUMP_REQ - response.

-

- Own Id: OTP-13517

-
- -

- Fixed a bug where a process flagged as sensitive would - sometimes record its save_calls when it shouldn't.

-

- Own Id: OTP-13540

-
- -

- Update configure scripts to not use hardcoded path for - /bin/pwd and /bin/rm.

-

- Own Id: OTP-13562

-
-
-
- - -
Improvements and New Features - - -

- The tracing support has been extended to allow a tracer module to be the - trace event handler instead of a process or port. The - tracer module - makes it possible for trace tools to filter or manipulate - trace event data without the trace event first haing to - be copied from the traced process or port.

-

- With the introduction of this feature, - erlang:trace(all|existing, _, _) now also returns - the tracer process as part of the number of processes on - which tracing is enabled. The is incompatible with the - previous releases.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-10267

-
- -

- Introduce LTTng tracing of Erlang Runtime System

-

- For LTTng to be enabled OTP needs to be built with - configure option --with-dynamic-trace=lttng.

-

- This feature introduces tracepoints for schedulers, - drivers, memory carriers, memory and async thread pool.

-

For a list of all tracepoints, see Runtime Tools User's - Guide .

-

- Own Id: OTP-10282 Aux Id: kunagi-14 [14]

-
- -

- Add microstate accounting

-

- Microstate accounting is a way to track which state the - different threads within ERTS are in. The main usage area - is to pin point performance bottlenecks by checking which - states the threads are in and then from there figuring - out why and where to optimize.

-

- Since checking whether microstate accounting is on or off - is relatively expensive only a few of the states are - enabled by default and more states can be enabled through - configure.

-

- There is a convinence module called msacc that has been - added to runtime_tools that can assist in gathering and - interpreting the data from Microstate accounting.

-

- For more information see erlang:statistics(microstate_accounting, - _) and the msacc module in - runtime_tools.

-

- Own Id: OTP-12345

-
- -

- The port of Erlang/OTP to the realtime operating system - OSE has been removed.

-

- Own Id: OTP-12573

-
- -

- Sharing preserved copy for messages and exit signals

-

- Enable sharing preserved copy with configure option - --enable-sharing-preserving. This will preserve - sharing, within the process, when communication with - other processes in the Erlang node. There is a trade-off, - the copy is more costly but this cost can be reclaimed if - there is a lot of sharing in the message. With this - feature enabled literals will not be copied in a send - except during a purge phase of the module where the - literals are located.

-

- Own Id: OTP-12590 Aux Id: OTP-10251

-
- -

- Halfword BEAM has been removed.

-

- Own Id: OTP-12883

-
- -

- Added os:perf_counter/1.

-

- The perf_counter is a very very cheap and high resolution - timer that can be used to timestamp system events. It - does not have monoticity guarantees, but should on most - OS's expose a monotonous time.

-

- Own Id: OTP-12908

-
- -

- Support for a fragmented young heap generation. That is, - the young heap generation can consist of multiple non - continuous memory areas. The main reason for this change - is to avoid extra copying of messages that could not be - allocated directly on the receivers heap.

-

- Own Id: OTP-13047

-
- -

- Erlang linked-in driver can now force the call to - open_port to block until a call to erl_drv_init_ack is - made inside the driver. This is useful when you want to - do some asynchronous initialization, for example getting - configuration from a pipe, and you want the initial - open_port call to fail if the configuration is incomplete - or wrong. See the erl_driver documentation for more - details on the API.

-

- Own Id: OTP-13086

-
- -

- Erlang linked-in drivers can now set their own pid's as - seen in erlang:port_info/1 by using the - erl_drv_set_pid function. For more details see the - erl_driver documentation.

-

- Own Id: OTP-13087

-
- -

- The functionality behind erlang:open_port/2 when - called with spawn or spawn_executable has been redone so - that the forking of the new program is done in a separate - process called erl_child_setup. This allows for a much - more robust implementation that uses less memory and does - not block the entire emulator if the program to be - started is on an un-accessible NFS. Benchmarks have shown - this approach to be about 3-5 times as fast as the old - approach where the fork/vfork was done by erts. This is a - pure stability and performance fix, however some error - messages may have changed, which is why it is marked as a - backwards incompatible change.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-13088

-
- -

Improved yielding strategy in the implementation of - the following native functions:

- erlang:binary_to_list/1 - erlang:binary_to_list/3 - erlang:bitstring_to_list/1 - erlang:list_to_binary/1 - erlang:iolist_to_binary/1 - erlang:list_to_bitstring/1 - binary:list_to_bin/1

This - in order to improve performance of these functions.

-

- Own Id: OTP-13096

-
- -

- All garbage collections of processes now bump reductions. - Also the amount of reductions bumped when garbage - collecting has been adjusted. It now better corresponds - to the amount of work performed. This in order to improve - the real time characteristics of the system.

-

- Own Id: OTP-13097

-
- -

New functions that can load multiple functions at once - have been added to the 'code' module. The - functions are code:atomic_load/1, - code:prepare_loading/1, - code:finish_loading/1, and - code:ensure_modules_loaded/1.

-

- Own Id: OTP-13111

-
- -

The function erl_prim_loader:start/3 has been - removed. Its documentation has also been removed.

-

The undocumented and unsupported function - erl_prim_loader:get_files/2 has been removed.

-

- Own Id: OTP-13112

-
- -

- Low level BIF erlang:purge_module/1 is made more - robust against incorrect use. Lingering processes that - still refer the old code are now killed before the module - is purged to prevent fatal VM behavior.

-

- Own Id: OTP-13122

-
- -

- Improved dirty scheduler implementation. For more - information see the NIF documentation.

-

- Note that support for determining whether dirty NIF - support exist or not at compile time using the C - preprocessor macro ERL_NIF_DIRTY_SCHEDULER_SUPPORT - has been removed.

-

- Own Id: OTP-13123

-
- -

- Various optimizations done to process dictionary access.

-

- Own Id: OTP-13167

-
- -

- Added max_heap_size process flag. See erlang:process_flag - for more details.

-

- Own Id: OTP-13174

-
- -

- Allow dynamic drivers and NIF libraries to be built with - gcc option -fvisibility=hidden for faster loading - and more optimized code.

-

- Own Id: OTP-13227

-
- -

- Add erlang:process_info(Pid, - garbage_collection_info) which returns extended - garbage_collection information. For more details see the - documentation.

-

- Own Id: OTP-13265

-
- -

- The functions erlang:list_to_integer/1 and - string:to_integer/1 have been optimized for large - inputs.

-

- Own Id: OTP-13293

-
- -

- Introduction of configurable management of data referred - to by the message queue of a process. Each process can be - configured individually.

-

- It is now possible to configure the message queue of a - process, so that all data referred by it will be kept - outside of the heap, and by this prevent this data from - being part of garbage collections.

-

- For more information see the documentation of process_flag(message_queue_data, - MQD).

-

- Own Id: OTP-13366 Aux Id: OTP-13047

-
- -

- Processes now yield when scanning large message queues - and not finding a matching message. This in order to - improve real time characteristics.

-

- Own Id: OTP-13401

-
- -

- Optimized an erts internal function that is used to - traverse erlang terms. The internal function was mainly - used by term_to_binary and comparison of terms. - Benchmarks have shown up to a 10% performance increase in - those functions after the optimization.

-

- Own Id: OTP-13440

-
- -

- Add the following NIF API functions:

-

- enif_cpu_time - enif_now_time - enif_make_unique_integer - enif_is_process_alive - enif_is_port_alive - enif_term_to_binary - enif_binary_to_term - enif_port_command -

-

- for details of what each function does, see the erl_nif - documentation.

-

- Own Id: OTP-13442

-
- -

- Optimize '++' operator and lists:append/2 - by using a single pass to build a new list while checking - for properness.

-

- Own Id: OTP-13487

-
- -

- Handle terms (pids,ports and refs) from nodes with a - 'creation' value larger than 3. This is a preparation of - the distribution protocol to allow OTP 19 nodes to - correctly communicate with future nodes (20 or higher). - The 'creation' value differentiates different - incarnations of the same node (name).

-

- Own Id: OTP-13488

-
- -

- Don't send unasked for systemd notifications in epmd

-

- Own Id: OTP-13493 Aux Id: PR-999

-
- -

- The enif_send API has been extended to allow NULL to be - used as the message environment. When used this way, a - message environent is implicitly created and the given - term is copied into that environment before sending. This - can be an optimization if many small messages are being - sent by the nif.

-

- Own Id: OTP-13495

-
- -

- The tracing support has been extended to allow tracing on - ports. Ports can be traced on using the 'ports', 'send' - and 'receive' trace flags.

-

- The first argument of erlang:trace/3 has - been extended so that 'all', 'existing' and - 'new' now include both processes and ports. New - Tracee variants, 'all_processes', - 'all_ports', 'existing_processes' etc have - been added to specify only processes or ports.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-13496

-
- -

- When the 'procs' trace flag is enabled, a - 'spawned' trace event is now also generated by a - newly created process. The previous event 'spawn' - remains, but as it is generated by the process that did - the spawn, it is not guaranteed that it is ordered with - other trace events from the newly spawned process. So - when tracking the lifetime of a process this new event - should be used as the creation event.

-

- This new trace event is marked as an incompatabiliy - because tools that expect certain trace events when - enabling 'procs' will have to updated.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-13497

-
- -

- Add the erlang:match_spec_test/3 - function. The functions allows the testing of match - specifications for both tracing and ets tables. It can be - used to test that a match specification does the expected - filtering on specific data. It also returns more verbose - error reasons for incorrectly constructed match - specifications.

-

- Own Id: OTP-13501

-
- -

- The erts internal tracing support has been changed to - have much less overhead and be more scalable.

-

- This rewrite does not break any backwards - incompatabilities, but it does change the ordering of - some trace messages when compared to previous releases. - It should be noted that this only applies to trace - messages sent to processes or ports, it does not apply to - the new tracer module. However in future releases they - may also be effected by this.

-

- Trace messages are only guaranteed to be ordered from one - traced process or port. In previous releases this was not - visible as a 'send' trace message would always - arrive before the corresponding 'receive' trace - message that is no longer always the case. This also - means that timestamped trace messages may seem to arrive - out of order as the timestamp is taken when the event is - triggered and not when it is put in the queue of the - tracer.

-

- Own Id: OTP-13503

-
- -

- Add possibility to filter send and receive - trace with match specifications.

-

- Own Id: OTP-13507

-
- -

- Add maps:update_with/3,4 and maps:take/2

-

- Own Id: OTP-13522 Aux Id: PR-1025

-
- -

- Introduce LTTng tracing via Erlang tracing.

-

- For LTTng to be enabled OTP needs to be built with - configure option --with-dynamic-trace=lttng.

-

The dynamic trace module dyntrace is now - capable to be used as a LTTng sink for Erlang tracing. - For a list of all tracepoints, see Runtime Tools User's - Guide .

-

This feature also introduces an incompatible change in - trace tags. The trace tags gc_start and - gc_end has been split into gc_minor_start, - gc_minor_end and gc_major_start, - gc_major_end.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-13532

-
- -

- Print heap pointers for garbing processes during - crashdump

-

- Own Id: OTP-13541 Aux Id: PR-1026

-
- -

- Changed and improved low level memory statistics returned - by erlang:system_info/1. The info for - erts_mmap has been moved from mseg_alloc to - its own section returned by {allocator, - erts_mmap}.

-

- Own Id: OTP-13560

-
-
-
- -
-
Erts 7.3.1
Fixed Bugs and Malfunctions -- cgit v1.2.3 From 4f5c19c5495eaf45b45faad6bfb51de25e9ad258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 10 May 2016 11:21:01 +0200 Subject: time_SUITE: Make consistency/1 work in any timezone time_SUITE:consistency/1 would only work in CET, which was reasonable when the test suites were always run in Stockholm. Nowadays it is expected that you can run the test suite in any timezone. Therefore, only make sure that that difference between localtime/1 and universaltime/1 is "reasonable". --- erts/emulator/test/time_SUITE.erl | 77 ++++++++++----------------------------- 1 file changed, 19 insertions(+), 58 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index 76d440529f..87b8c62cfa 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -209,23 +209,17 @@ test_seconds_to_univ([]) -> %% Test that the the different time functions return -%% consistent results. (See the test case for assumptions -%% and limitations.) -consistency(Config) when is_list(Config) -> - %% Test the following equations: - %% date() & time() == erlang:localtime() - %% erlang:universaltime() + timezone == erlang:localtime() +%% consistent results. +consistency(_Config) -> + %% Test that: + %% * date() & time() gives the same time as erlang:localtime() %% - %% Assumptions: - %% Middle-European time zone, EU rules for daylight-saving time. - %% - %% Limitations: - %% Localtime and universaltime must be in the same month. - %% Daylight-saving calculations are incorrect from the last - %% Sunday of March and October to the end of the month. + %% * the difference between erlang:universaltime() and + %% erlang:localtime() is reasonable (with assuming any + %% particular timezone) ok = compare_date_time_and_localtime(16), - ok = compare_local_and_universal(16). + compare_local_and_universal(16). compare_date_time_and_localtime(Times) when Times > 0 -> {Year, Mon, Day} = date(), @@ -238,22 +232,18 @@ compare_date_time_and_localtime(0) -> error. compare_local_and_universal(Times) when Times > 0 -> - case compare(erlang:universaltime(), erlang:localtime()) of - true -> ok; - false -> compare_local_and_universal(Times-1) - end; -compare_local_and_universal(0) -> - error. + Utc = erlang:universaltime(), + Local = erlang:localtime(), + io:format("local = ~p, utc = ~p", [Local,Utc]), -compare(Utc0, Local) -> - io:format("local = ~p, utc = ~p", [Local, Utc0]), - Utc = linear_time(Utc0)+effective_timezone(Utc0)*3600, - case linear_time(Local) of - Utc -> true; - Other -> - io:format("Failed: local = ~p, utc = ~p~n", - [Other, Utc]), - false + AcceptableDiff = 14*3600, + case linear_time(Utc) - linear_time(Local) of + Diff when abs(Diff) < AcceptableDiff -> + ok; + Diff -> + io:format("More than ~p seconds difference betwen " + "local and universal time", [Diff]), + ct:fail(huge_diff) end. %% This function converts a date and time to a linear time. @@ -280,35 +270,6 @@ days_in_february(Year) -> _ -> 28 end. -%% This functions returns either the normal timezone or the -%% the DST timezone, depending on the given UTC time. -%% -%% XXX This function uses an approximation of the EU rule for -%% daylight saving time. This function will fail in the -%% following intervals: After the last Sunday in March upto -%% the end of March, and after the last Sunday in October -%% upto the end of October. - -effective_timezone(Time) -> - case os:type() of - {unix,_} -> - case os:cmd("date '+%Z'") of - "SAST"++_ -> - 2; - _ -> - effective_timezone1(Time) - end; - _ -> - effective_timezone1(Time) - end. - -effective_timezone1({{_Year,Mon,_Day}, _}) when Mon < 4 -> - ?timezone; -effective_timezone1({{_Year,Mon,_Day}, _}) when Mon > 10 -> - ?timezone; -effective_timezone1(_) -> - ?dst_timezone. - %% Test (the bif) os:timestamp/0, which is something quite like, but not %% similar to erlang:now... -- cgit v1.2.3 From 031e4e19120af3e40fcc937bebb4cc45b59f5b87 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 11 May 2016 19:08:49 +0200 Subject: erts: Fix confusion among match spec examples Tracing and ETS examples were not separated correctly under the corresponding headings. --- erts/doc/src/match_spec.xml | 75 +++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 37 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml index 3944f24f84..ca73d78b8d 100644 --- a/erts/doc/src/match_spec.xml +++ b/erts/doc/src/match_spec.xml @@ -528,7 +528,7 @@
- ETS Examples + Tracing Examples

Match an argument list of three where the first and third arguments are equal:

has to be a proper list.

-

Match all objects in an ets table where the first element is - the atom 'strider' and the tuple arity is 3 and return the whole - object.

- -

Match all objects in an ets table with arity > 1 and the first - element is 'gandalf', return element 2.

- =',{size, '$1'},2}], - [{element,2,'$1'}]}] - ]]> -

In the above example, if the first element had been the key, - it's much more efficient to match that key in the - part than in the part. The search space of - the tables is restricted with regards to the so - that only objects with the matching key are searched. -

-

Match tuples of 3 elements where the second element is either - 'merry' or 'pippin', return the whole objects.

- -

The function can be useful for testing - complicated ets matches.

-
-
- Tracing Examples

Only generate trace message if trace control word is set to 1:

+ +
+ ETS Examples +

Match all objects in an ets table where the first element is + the atom 'strider' and the tuple arity is 3 and return the whole + object.

+ +

Match all objects in an ets table with arity > 1 and the first + element is 'gandalf', return element 2.

+ =',{size, '$1'},2}], + [{element,2,'$1'}]}] + ]]> +

In the above example, if the first element had been the key, + it's much more efficient to match that key in the + part than in the part. The search space of + the tables is restricted with regards to the so + that only objects with the matching key are searched. +

+

Match tuples of 3 elements where the second element is either + 'merry' or 'pippin', return the whole objects.

+ +

The function can be useful for testing + complicated ets matches.

+
-- cgit v1.2.3 From 46f08788d88e61f41f34f775314444191ebe3b41 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 12 May 2016 18:13:20 +0200 Subject: erts: Add send/receive trace to match spec user guide Introduce section/terminology "Match target". --- erts/doc/src/match_spec.xml | 74 +++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml index ca73d78b8d..7be3d15de6 100644 --- a/erts/doc/src/match_spec.xml +++ b/erts/doc/src/match_spec.xml @@ -33,21 +33,15 @@ match_spec.xml

A "match specification" (match_spec) is an Erlang term describing a - small "program" that will try to match something (either the - parameters to a function as used in the - BIF, or the objects in an ETS table.). + small "program" that will try to match something. It can be used + to either control tracing with + erlang:trace_pattern/3 + or to search for objects in an ETS table with for example + ets:select/2. The match_spec in many ways works like a small function in Erlang, but is interpreted/compiled by the Erlang runtime system to something much more efficient than calling an Erlang function. The match_spec is also very limited compared to the expressiveness of real Erlang functions.

-

Match specifications are given to the BIF to - execute matching of function arguments as well as to define some actions - to be taken when the match succeeds (the part). Match - specifications can also be used in ETS, to specify objects to be - returned from an call (or other select - calls). The semantics and restrictions differ slightly when using - match specifications for tracing and in ETS, the differences are - defined in a separate paragraph below.

The most notable difference between a match_spec and an Erlang fun is of course the syntax. Match specifications are Erlang terms, not Erlang code. A match_spec also has a somewhat strange concept of @@ -382,6 +376,51 @@ the pid() of the current process.

+ +
+ Match target +

Each execution of a match specification is done against + a match target term. The format and content of the target term + depends on the context in which the match is done. The match + target for ETS is always a full table tuple. The match target + for call trace is always a list of all function arguments. The + match target for event trace depends on the event type, see + table below.

+ + + Context + Type + Match target + Description + + + ETS + + {Key, Value1, Value2, ...} + A table object + + + Trace + call + [Arg1, Arg2, ...] + Function arguments + + + Trace + send + [Receiver, Message] + Receiving process/port and message term + + + Trace + 'receive' + [Node, Sender, Message] + Sending node, process/port and message term + + Match target depending on context +
+
+
Variables and literals

Variables take the form ']]> where @@ -396,10 +435,8 @@ parts, only variables bound previously may be used. As a special case, in the parts, the variable - expands to the whole expression which matched the - (i.e., the whole parameter list to the possibly - traced function or the whole matching object in the ets table) - and the variable expands to a list + expands to the whole match target + term and the variable expands to a list of the values of all bound variables in order (i.e. ).

@@ -480,8 +517,8 @@

For each tuple in the list and while no match has succeeded:

- Match the part against the arguments to the - function, + Match the part against the + match target term, binding the ']]> variables (much like in ). If the cannot match the arguments, the match fails. @@ -522,9 +559,6 @@ term. The 's are executed as in an imperative language, i.e. for their side effects. Functions with side effects are also allowed when tracing.

-

In ETS the match head is a (or a single match - variable) while it is a list (or a single match variable) when - tracing.

-- cgit v1.2.3 From cbee76c388d08100856a92f14850bb3302a51207 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 12 May 2016 19:20:41 +0200 Subject: erts: Remove compiler warning 'hx' may be used uninitialized --- erts/emulator/beam/beam_load.c | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 2e21a553ed..e6b50fbd32 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4368,6 +4368,7 @@ gen_get_map_elements(LoaderState* stp, GenOpArg Fail, GenOpArg Src, int good_hash; #endif + ERTS_UNDEF(hx, 0); ASSERT(Size.type == TAG_u); NEW_GENOP(stp, op); -- cgit v1.2.3 From 2fccb09b710b1e42157b3801ac1b154f76ae6898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 16 May 2016 11:58:28 +0200 Subject: Remove the warning about using erlang:raise/3 There is no good reason to say that erlang:raise/3 is only for debugging. Here is an example where it can be extremely useful: try do_something(Args) catch Class:Error -> Stack = erlang:get_stacktrace(), io:format("Args: ~p\n", [Args]), erlang:raise(Class, Error, Stack) That is, we can let it crash, but log additional useful information before crashing. Noticed-by: Per Hedeland --- erts/doc/src/erlang.xml | 4 ---- 1 file changed, 4 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index e0723127f2..34933d6f48 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5070,10 +5070,6 @@ os_prompt%

Stops the execution of the calling process with an exception of given class, reason, and call stack backtrace (stacktrace).

- -

This BIF is intended for debugging. Avoid to use it in applications, - unless you really know what you are doing.

-

Class is error, exit, or throw. So, if it were not for the stacktrace, erlang:raise(Class, Reason, -- cgit v1.2.3 From cc59f962511733c5dfcb1be27a274f9298736006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 16 May 2016 14:56:03 +0200 Subject: Tolerate bad directories in the code path A bad directory in the path would prevent the run-time system from starting: $ echo >foobar $ erl -pa foobar {"init terminating in do_boot",{load_failed,[supervisor,kernel,gen_server,file_io_server,filename,file,erl_parse,error_logger,code_server,erl_lint,proc_lib,code,application_controller,application_master,gen_event,application,error_handler,lists,heart,gen,file_server,ets,erl_eval]}} Crash dump is being written to: erl_crash.dump...done init terminating in do_boot () The reason is that when attempting to read each of the BEAM files, there would be an 'enotdir' error which erl_prim_load:get_modules/2,3 assumed was a fatal error. Update erl_prim_load:get_modules/2,3 to ignore any error and try the next directory in the path. Reported-by: http://bugs.erlang.org/browse/ERL-142 Reported-by: Michael Truog --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 55780 -> 55732 bytes erts/preloaded/src/erl_prim_loader.erl | 6 ++---- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index de2693472a..66e443f396 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index e18e187cb7..b3ec73a60e 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -558,11 +558,9 @@ efile_gm_get_1([P|Ps], File0, Mod, {Parent,Ref}=PR, Process) -> Res = try prim_file:read_file(File) of {ok,Bin} -> gm_process(Mod, File, Bin, Process); - {error,enoent} -> - efile_gm_get_1(Ps, File0, Mod, PR, Process); Error -> - check_file_result(get_modules, File, Error), - Error + _ = check_file_result(get_modules, File, Error), + efile_gm_get_1(Ps, File0, Mod, PR, Process) catch _:Reason -> {error,{crash,Reason}} -- cgit v1.2.3 From 63e287405a442fc76d99595e0f7874a7678f346b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 May 2016 17:53:03 +0200 Subject: erts: Fix free_mem calculation in bs_construct tests * Previous free_mem calculation did not work on all platforms * Increase test timeout for bs_construct_SUITE:huge_binary/1 --- erts/emulator/test/bs_construct_SUITE.erl | 48 ++++++++----------------------- 1 file changed, 12 insertions(+), 36 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 941cb435f7..22a1c0b765 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -527,7 +527,7 @@ huge_float_check({'EXIT',{system_limit,_}}) -> ok; huge_float_check({'EXIT',{badarg,_}}) -> ok. huge_binary(Config) when is_list(Config) -> - ct:timetrap({seconds, 30}), + ct:timetrap({seconds, 60}), 16777216 = size(<<0:(id(1 bsl 26)),(-1):(id(1 bsl 26))>>), garbage_collect(), {Shift,Return} = case free_mem() of @@ -561,30 +561,13 @@ huge_binary(Config) when is_list(Config) -> end. free_mem() -> - Cmd = "uname; free", - Output = string:tokens(os:cmd(Cmd), "\n"), - io:format("Output from command ~p\n~p\n",[Cmd,Output]), - case Output of - [OS, ColumnNames, Values | _] -> - case string:str(OS,"Linux") of - 0 -> - io:format("Unknown OS\n",[]), - undefined; - _ -> - case {string:tokens(ColumnNames, " \t"), - string:tokens(Values, " \t")} of - {[_,_,"free"|_],["Mem:",_,_,FreeKb|_]} -> - list_to_integer(FreeKb) div 1024; - _ -> - io:format("Failed to parse output from 'free':\n",[]), - undefined - end - end; - _ -> - io:format("Too few lines in output\n",[]), - undefined + {ok,Apps} = application:ensure_all_started(os_mon), + Mem = memsup:get_system_memory_data(), + [ok = application:stop(App)||App <- Apps], + case proplists:get_value(free_memory,Mem) of + undefined -> undefined; + Val -> Val div 1024 end. - system_limit(Config) when is_list(Config) -> WordSize = erlang:system_info(wordsize), @@ -614,8 +597,7 @@ system_limit_32() -> {'EXIT',{system_limit,_}} = (catch <<42:536870912/unit:8>>), {'EXIT',{system_limit,_}} = (catch <<42:(id(536870912))/unit:8>>), {'EXIT',{system_limit,_}} = (catch <<0:(id(8)),42:536870912/unit:8>>), - {'EXIT',{system_limit,_}} = - (catch <<0:(id(8)),42:(id(536870912))/unit:8>>), + {'EXIT',{system_limit,_}} = (catch <<0:(id(8)),42:(id(536870912))/unit:8>>), %% The size would be silently truncated, resulting in a crash. {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 35)>>), @@ -627,16 +609,10 @@ system_limit_32() -> ok. badarg(Config) when is_list(Config) -> - {'EXIT',{badarg,_}} = - (catch <<0:(id(1 bsl 100)),0:(id(-1))>>), - {'EXIT',{badarg,_}} = - (catch <<0:(id(1 bsl 100)),0:(id(-(1 bsl 70)))>>), - {'EXIT',{badarg,_}} = - (catch <<0:(id(-(1 bsl 70))),0:(id(1 bsl 100))>>), - - {'EXIT',{badarg,_}} = - (catch <<(id(<<>>))/binary,0:(id(-(1 bsl 100)))>>), - + {'EXIT',{badarg,_}} = (catch <<0:(id(1 bsl 100)),0:(id(-1))>>), + {'EXIT',{badarg,_}} = (catch <<0:(id(1 bsl 100)),0:(id(-(1 bsl 70)))>>), + {'EXIT',{badarg,_}} = (catch <<0:(id(-(1 bsl 70))),0:(id(1 bsl 100))>>), + {'EXIT',{badarg,_}} = (catch <<(id(<<>>))/binary,0:(id(-(1 bsl 100)))>>), ok. copy_writable_binary(Config) when is_list(Config) -> -- cgit v1.2.3 From 29b7e1762d69d4173cba8edb273772f05a215083 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 17 May 2016 13:00:49 +0200 Subject: erts: Remove no longer needed anchors in documentation --- erts/doc/src/erlang.xml | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index e0723127f2..8724139c01 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -52,7 +52,6 @@ ext_binary() -

A binary data object, structured according to the Erlang external term format.

-- cgit v1.2.3 From 65c94fe4976cc4589e42ace1873640a0e7c73bf8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 12 May 2016 13:57:23 +0200 Subject: erts: Fix bug when tracing in non-smp non-scheduler thread --- erts/emulator/beam/erl_nif.c | 18 +++++++-- erts/emulator/test/port_trace_SUITE.erl | 44 ++++++++++++++++++++-- .../emulator/test/port_trace_SUITE_data/echo_drv.c | 36 ++++++++++++++++++ 3 files changed, 92 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 159dc66ad5..bd9367a3d5 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -667,6 +667,9 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, erts_smp_proc_trylock(rp, ERTS_PROC_LOCK_MSGQ) == EBUSY) { if (!msgq) { +#ifdef ERTS_SMP + ErtsThrPrgrDelayHandle dhndl; +#endif msgq = erts_alloc(ERTS_ALC_T_TRACE_MSG_QUEUE, sizeof(ErlTraceMessageQueue)); @@ -681,8 +684,15 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE); +#ifdef ERTS_SMP + if (!scheduler) + dhndl = erts_thr_progress_unmanaged_delay(); +#endif erts_schedule_flush_trace_messages(t_p->common.id); - +#ifdef ERTS_SMP + if (!scheduler) + erts_thr_progress_unmanaged_continue(dhndl); +#endif } else { msgq->len++; *msgq->last = mp; @@ -1669,7 +1679,8 @@ int enif_is_process_alive(ErlNifEnv* env, ErlNifPid *proc) return !!rp; #else erts_exit(ERTS_ABORT_EXIT, "enif_is_process_alive: " - "called from non-scheduler thread"); + "called from non-scheduler thread " + "in non-smp emulator"); return 0; #endif } @@ -1694,7 +1705,8 @@ int enif_is_port_alive(ErlNifEnv *env, ErlNifPort *port) return !!prt; #else erts_exit(ERTS_ABORT_EXIT, "enif_is_port_alive: " - "called from non-scheduler thread"); + "called from non-scheduler thread " + "in non-smp emulator"); return 0; #endif } diff --git a/erts/emulator/test/port_trace_SUITE.erl b/erts/emulator/test/port_trace_SUITE.erl index bfdea0761b..5d9a75bcd3 100644 --- a/erts/emulator/test/port_trace_SUITE.erl +++ b/erts/emulator/test/port_trace_SUITE.erl @@ -31,7 +31,8 @@ failure_atom/1, failure_posix/1, failure/1, output_term/1, driver_output_term/1, - send_term/1, driver_send_term/1]). + send_term/1, driver_send_term/1, + driver_remote_send_term/1]). -define(ECHO_DRV_NOOP, 0). -define(ECHO_DRV_OUTPUT, 1). @@ -48,6 +49,7 @@ -define(ECHO_DRV_SEND_TERM, 12). -define(ECHO_DRV_DRIVER_SEND_TERM, 13). -define(ECHO_DRV_SAVE_CALLER, 14). +-define(ECHO_DRV_REMOTE_SEND_TERM, 15). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {seconds, 30}}]. @@ -60,7 +62,8 @@ all() -> failure_atom, failure_posix, failure, output_term, driver_output_term, - send_term, driver_send_term]. + send_term, driver_send_term, + driver_remote_send_term]. init_per_suite(Config) -> Config. @@ -75,6 +78,13 @@ end_per_group(_GroupName, Config) -> Config. +init_per_testcase(driver_remote_send_term, Config) -> + case erlang:system_info(smp_support) of + false -> + {skip,"Only supported on smp systems"}; + true -> + init_per_testcase(driver_remote_send_term_smp, Config) + end; init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> erlang:trace(all, false, [all]), os:unsetenv("OUTPUTV"), @@ -543,6 +553,34 @@ driver_send_term(_Config) -> ok. +%% Test that driver_send_term from non-scheduler thread does not +%% generate trace messages. +driver_remote_send_term(_Config) -> + + Flags = [send], + {Prt, S} = trace_and_open(Flags,[binary]), + + erlang:port_command(Prt, <>), + recv({echo, Prt, <<123456:32>>}), + [] = flush(), + + Pid = spawn_link( + fun() -> + erlang:port_command(Prt, <>), + S ! ok, + receive M -> S ! M end + end), + recv(ok), + erlang:trace(Pid, true, ['receive']), + + erlang:port_command(Prt, <>), + recv({echo, Prt, <<123456:32>>}), + [{trace, Pid, 'receive', {echo, Prt, <<123456:32>>}}] = flush(), + + close(Prt, Flags), + + ok. + %%%%%%%%%%%%%%%%%%% %% Helper functions %%%%%%%%%%%%%%%%%%% @@ -598,7 +636,7 @@ f(From) -> end. recv(Msg) -> - receive Msg -> ok after 100 -> ct:fail({did_not_get_data,Msg,flush()}) end. + receive Msg -> ok after 1000 -> ct:fail({did_not_get_data,Msg,flush()}) end. load_drv(Config) -> Path = proplists:get_value(data_dir, Config), diff --git a/erts/emulator/test/port_trace_SUITE_data/echo_drv.c b/erts/emulator/test/port_trace_SUITE_data/echo_drv.c index b5ae9389b4..b545523192 100644 --- a/erts/emulator/test/port_trace_SUITE_data/echo_drv.c +++ b/erts/emulator/test/port_trace_SUITE_data/echo_drv.c @@ -14,6 +14,13 @@ typedef struct _erl_drv_data { ErlDrvTermData caller; } EchoDrvData; +struct remote_send_term { + char *buf; + int len; + ErlDrvTermData port; + ErlDrvTermData caller; +}; + #define ECHO_DRV_NOOP 0 #define ECHO_DRV_OUTPUT 1 #define ECHO_DRV_OUTPUT2 2 @@ -29,6 +36,7 @@ typedef struct _erl_drv_data { #define ECHO_DRV_SEND_TERM 12 #define ECHO_DRV_DRIVER_SEND_TERM 13 #define ECHO_DRV_SAVE_CALLER 14 +#define ECHO_DRV_REMOTE_SEND_TERM 15 /* ------------------------------------------------------------------------- @@ -78,6 +86,8 @@ static ErlDrvEntry echo_drv_entry = { NULL }; +static void send_term_thread(void *); + /* ------------------------------------------------------------------------- ** Entry functions **/ @@ -200,6 +210,18 @@ static void echo_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) { } break; } + case ECHO_DRV_REMOTE_SEND_TERM: + { + ErlDrvTid tid; + struct remote_send_term *t = malloc(sizeof(struct remote_send_term)); + t->len = len-1; + t->buf = malloc(len-1); + t->port = driver_mk_port(port); + t->caller = data_p->caller; + memcpy(t->buf, buf+1, t->len); + erl_drv_thread_create("tmp_thread", &tid, send_term_thread, t, NULL); + break; + } case ECHO_DRV_SAVE_CALLER: data_p->caller = driver_caller(port); break; @@ -239,3 +261,17 @@ static ErlDrvSSizeT echo_drv_call(ErlDrvData drv_data, memcpy(*rbuf, buf+command, len-command); return len-command; } + +static void send_term_thread(void *a) +{ + struct remote_send_term *t = (struct remote_send_term*)a; + ErlDrvTermData term[] = { + ERL_DRV_ATOM, driver_mk_atom("echo"), + ERL_DRV_PORT, t->port, + ERL_DRV_BUF2BINARY, (ErlDrvTermData)(t->buf), + (ErlDrvTermData)(t->len), + ERL_DRV_TUPLE, 3}; + erl_drv_send_term(t->port, t->caller, + term, sizeof(term) / sizeof(ErlDrvTermData)); + return; +} -- cgit v1.2.3 From 1ba422f4e4a18dd9874c9704b73cbf38b4252fa7 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 13 May 2016 10:28:22 +0200 Subject: erts: Fix erl_tracer xml doc errors --- erts/doc/src/erl_tracer.xml | 62 ++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 40 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml index 7fd5512b33..d4c8bbad31 100644 --- a/erts/doc/src/erl_tracer.xml +++ b/erts/doc/src/erl_tracer.xml @@ -67,7 +67,7 @@

The different trace tags that the tracer will be called with. Each trace tag is described in greater detail in - Module:trace/6 + Module:trace/6

@@ -81,7 +81,7 @@ -

The options for the tracee. +

The options for the tracee.

timestamp If not set to undefined, the tracer has been requested to @@ -93,7 +93,6 @@ Set to a number if the scheduler id is to be included by the tracer. Otherwise it is set to undefined. -

@@ -114,30 +113,30 @@

The following functions should be exported from a erl_tracer callback module.

- Module:enabled/3 + Module:enabled/3 Mandatory - Module:trace/6 + Module:trace/6 Mandatory - Module:enabled_procs/3 + Module:enabled_procs/3 Optional - Module:trace_procs/6 + Module:trace_procs/6 Optional - Module:enabled_ports/3 + Module:enabled_ports/3 Optional - Module:trace_ports/6 + Module:trace_ports/6 Optional - Module:enabled_running_ports/3 + Module:enabled_running_ports/3 Optional - Module:trace_running_ports/6 + Module:trace_running_ports/6 Optional - Module:enabled_running_procs/3 + Module:enabled_running_procs/3 Optional - Module:trace_running_procs/6 + Module:trace_running_procs/6 Optional
- + Module:enabled(TraceTag, TracerState, Tracee) -> Result @@ -166,7 +165,6 @@ is important that it is both fast and side effect free.

- Module:trace(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result Check if a trace event should be generated. @@ -181,7 +179,7 @@

This callback will be called when a tracepoint is triggered and - the Module:enabled/3 + the Module:enabled/3 callback returned trace. In it any side effects needed by the tracer should be done. The tracepoint payload is located in the FirstTraceTerm and SecondTraceTerm. The content @@ -212,7 +210,6 @@ - Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result Check if a trace event should be generated. @@ -230,7 +227,6 @@ - Module:trace_procs(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result Check if a trace event should be generated. @@ -245,13 +241,12 @@

This callback will be called when a tracepoint is triggered and - the Module:enabled_procs/3 + the Module:enabled_procs/3 callback returned trace.

If trace_procs/6 is not defined trace/6 will be called instead.

- Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result Check if a trace event should be generated. @@ -269,7 +264,6 @@ - Module:trace_ports(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result Check if a trace event should be generated. @@ -284,13 +278,12 @@

This callback will be called when a tracepoint is triggered and - the Module:enabled_ports/3 + the Module:enabled_ports/3 callback returned trace.

If trace_ports/6 is not defined trace/6 will be called instead.

- Module:enabled_running_procs(TraceTag, TracerState, Tracee) -> Result Check if a trace event should be generated. @@ -308,7 +301,6 @@ - Module:trace_running_procs(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result Check if a trace event should be generated. @@ -323,13 +315,12 @@

This callback will be called when a tracepoint is triggered and - the Module:enabled_running_procs/3 + the Module:enabled_running_procs/3 callback returned trace.

If trace_running_procs/6 is not defined trace/6 will be called instead.

- Module:enabled_running_ports(TraceTag, TracerState, Tracee) -> Result Check if a trace event should be generated. @@ -347,7 +338,6 @@ - Module:trace_running_ports(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result Check if a trace event should be generated. @@ -362,13 +352,12 @@

This callback will be called when a tracepoint is triggered and - the Module:enabled_running_ports/3 + the Module:enabled_running_ports/3 callback returned trace.

If trace_running_ports/6 is not defined trace/6 will be called instead.

- Module:enabled_call(TraceTag, TracerState, Tracee) -> Result Check if a trace event should be generated. @@ -386,7 +375,6 @@ - Module:trace_call(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result Check if a trace event should be generated. @@ -401,13 +389,12 @@

This callback will be called when a tracepoint is triggered and - the Module:enabled_call/3 + the Module:enabled_call/3 callback returned trace.

If trace_call/6 is not defined trace/6 will be called instead.

- Module:enabled_send(TraceTag, TracerState, Tracee) -> Result Check if a trace event should be generated. @@ -425,7 +412,6 @@ - Module:trace_send(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result Check if a trace event should be generated. @@ -440,13 +426,12 @@

This callback will be called when a tracepoint is triggered and - the Module:enabled_send/3 + the Module:enabled_send/3 callback returned trace.

If trace_send/6 is not defined trace/6 will be called instead.

- Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result Check if a trace event should be generated. @@ -464,7 +449,6 @@ - Module:trace_receive(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result Check if a trace event should be generated. @@ -479,13 +463,12 @@

This callback will be called when a tracepoint is triggered and - the Module:enabled_receive/3 + the Module:enabled_receive/3 callback returned trace.

If trace_receive/6 is not defined trace/6 will be called instead.

- Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result Check if a trace event should be generated. @@ -503,7 +486,6 @@ - Module:trace_garbage_collection(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result Check if a trace event should be generated. @@ -518,7 +500,7 @@

This callback will be called when a tracepoint is triggered and - the Module:enabled_garbage_collection/3 + the Module:enabled_garbage_collection/3 callback returned trace.

If trace_garbage_collection/6 is not defined trace/6 will be called instead.

-- cgit v1.2.3 From 32a8787eb7651d41371afdf3f55fd3a77486861d Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 12 May 2016 17:37:40 +0200 Subject: erts: Fix OpenBSD gcc compiler bug in re code The OpenBSD 5.8 gcc compiler emits erroneous code which results in the re:run function behaving badly, though strangely enough it does not segfault the vm. This fix moves code around a bit in order to make gcc emit correct code. --- erts/emulator/beam/erl_bif_re.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index f4daecd2a4..ff7746ce1d 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -630,9 +630,15 @@ static Eterm build_exec_return(Process *p, int rc, RestartContext *restartp, Ete } } else { ReturnInfo *ri; - ReturnInfo defri = {RetIndex,0,{0}}; + ReturnInfo defri; if (restartp->ret_info == NULL) { + /* OpenBSD 5.8 gcc compiler for some reason creates + bad code if the above initialization is done + inline with the struct. So don't do that. */ + defri.type = RetIndex; + defri.num_spec = 0; + defri.v[0] = 0; ri = &defri; } else { ri = restartp->ret_info; -- cgit v1.2.3 From 0f04b36c15acd592a650936b039997feeb37a639 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 12 May 2016 18:11:22 +0200 Subject: erts: Move max_heap_size field so that ErLLVM works again --- erts/emulator/beam/erl_process.h | 2 +- erts/emulator/hipe/hipe_mode_switch.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 2801947613..2fc6dfe194 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -960,7 +960,6 @@ struct process { Uint heap_sz; /* Size of heap in words */ Uint min_heap_size; /* Minimum size of heap (in words). */ Uint min_vheap_size; /* Minimum size of virtual heap (in words). */ - Uint max_heap_size; /* Maximum size of heap (in words). */ #if !defined(NO_FPE_SIGNALS) || defined(HIPE) volatile unsigned long fp_exception; @@ -1061,6 +1060,7 @@ struct process { Eterm *old_hend; /* Heap pointers for generational GC. */ Eterm *old_htop; Eterm *old_heap; + Uint max_heap_size; /* Maximum size of heap (in words). */ Uint16 gen_gcs; /* Number of (minor) generational GCs. */ Uint16 max_gen_gcs; /* Max minor gen GCs before fullsweep. */ ErlOffHeap off_heap; /* Off-heap data updated by copy_struct(). */ diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index f532d3151f..884331e969 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -174,6 +174,33 @@ void hipe_mode_switch_init(void) make_catch(beam_catches_cons(hipe_beam_pc_throw, BEAM_CATCHES_NIL)); hipe_mfa_info_table_init(); + +#if (defined(__i386__) || defined(__x86_64__)) && defined(__linux__) + /* Verify that the offset of c-p->hipe does not change. + The ErLLVM hipe backend depends on it being in a specific + position. Kostis et al has promised to fix this in upstream + llvm by OTP 20, so it should be possible to remove these asserts + after that. */ + ERTS_CT_ASSERT(sizeof(ErtsPTabElementCommon) == + (sizeof(Eterm) + /* id */ + sizeof(((ErtsPTabElementCommon*)0)->refc) + + sizeof(ErtsTracer) + /* tracer */ + sizeof(Uint) + /* trace_flags */ + sizeof(erts_smp_atomic_t) + /* timer */ + sizeof(((ErtsPTabElementCommon*)0)->u))); + + ERTS_CT_ASSERT(offsetof(Process, hipe) == + (sizeof(ErtsPTabElementCommon) + /* common */ + sizeof(Eterm*) + /* htop */ + sizeof(Eterm*) + /* stop */ + sizeof(Eterm*) + /* heap */ + sizeof(Eterm*) + /* hend */ + sizeof(Uint) + /* heap_sz */ + sizeof(Uint) + /* min_heap_size */ + sizeof(Uint) + /* min_vheap_size */ + sizeof(volatile unsigned long))); /* fp_exception */ +#endif + } void hipe_set_call_trap(Uint *bfun, void *nfun, int is_closure) -- cgit v1.2.3 From 59fa8124370d63dc810ff27dde3f20d2aa6fc523 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 20 May 2016 08:44:09 +0200 Subject: erts: Require more memory for debug tests --- erts/emulator/test/process_SUITE.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index eaa4026a8a..94adfe5cad 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -2585,7 +2585,10 @@ enable_internal_state() -> _ -> erts_debug:set_internal_state(available_internal_state, true) end. -sys_mem_cond_run(ReqSizeMB, TestFun) when is_integer(ReqSizeMB) -> +sys_mem_cond_run(OrigReqSizeMB, TestFun) when is_integer(OrigReqSizeMB) -> + %% Debug normally needs more memory, so double the requirement + Debug = erlang:system_info(debug_compiled), + ReqSizeMB = if Debug -> OrigReqSizeMB * 2; true -> OrigReqSizeMB end, case total_memory() of TotMem when is_integer(TotMem), TotMem >= ReqSizeMB -> TestFun(); -- cgit v1.2.3 From 761b3bb5f48e5c9df401debc4e3cc63457410f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 20 May 2016 18:07:49 +0200 Subject: erts: Check for carrier capability specifically in test --- erts/emulator/test/lttng_SUITE.erl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl index efc79f42ed..c64ddc40da 100644 --- a/erts/emulator/test/lttng_SUITE.erl +++ b/erts/emulator/test/lttng_SUITE.erl @@ -120,7 +120,7 @@ t_lttng_list(_Config) -> %% com_ericsson_otp:carrier_pool_get %% com_ericsson_otp:carrier_pool_put t_carrier_pool(Config) -> - case have_carriers() of + case have_carriers(ets_alloc) of false -> {skip, "No Memory Carriers configured on system."}; true -> @@ -137,7 +137,7 @@ t_carrier_pool(Config) -> %% com_ericsson_otp:carrier_destroy %% com_ericsson_otp:carrier_create t_memory_carrier(Config) -> - case have_carriers() of + case have_carriers(ets_alloc) of false -> {skip, "No Memory Carriers configured on system."}; true -> @@ -446,11 +446,10 @@ load_driver(Dir, Driver) -> %% check -have_carriers() -> - Cap = element(3,erlang:system_info(allocator)), - case Cap -- [sys_alloc,sys_aligned_alloc] of - [] -> false; - _ -> true +have_carriers(Alloc) -> + case erlang:system_info({allocator,Alloc}) of + false -> false; + _ -> true end. have_async_threads() -> -- cgit v1.2.3 From 949f1d3a807e3bb095668fac4bfc4624fea2e34e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 May 2016 19:17:53 +0200 Subject: erts: Add testcase for purge of literals Specifically t_copy_literals_frags/1 tries to test literals in message heap fragments. --- erts/emulator/test/code_SUITE.erl | 132 +++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 29b95ef674..2347a3d4ef 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -26,7 +26,7 @@ external_fun/1,get_chunk/1,module_md5/1,make_stub/1, make_stub_many_funs/1,constant_pools/1,constant_refc_binaries/1, false_dependency/1,coverage/1,fun_confusion/1, - t_copy_literals/1]). + t_copy_literals/1, t_copy_literals_frags/1]). -define(line_trace, 1). -include_lib("common_test/include/ct.hrl"). @@ -38,7 +38,7 @@ all() -> t_check_process_code_ets, t_check_old_code, external_fun, get_chunk, module_md5, make_stub, make_stub_many_funs, constant_pools, constant_refc_binaries, false_dependency, - coverage, fun_confusion, t_copy_literals]. + coverage, fun_confusion, t_copy_literals, t_copy_literals_frags]. init_per_suite(Config) -> erts_debug:set_internal_state(available_internal_state, true), @@ -766,6 +766,134 @@ t_copy_literals(Config) when is_list(Config) -> ok = flush(), ok. +-define(mod, t_copy_literals_frags). +t_copy_literals_frags(Config) when is_list(Config) -> + Bin = gen_lit(?mod,[{a,{1,2,3,4,5,6,7}}, + {b,"hello world"}, + {c, <<"hello world">>}, + {d, {"hello world", {1.0, 2.0, <<"some">>, "string"}}}, + {e, <<"off heap", 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13,14,15>>}]), + + {module, ?mod} = erlang:load_module(?mod, Bin), + N = 6000, + Recv = spawn_opt(fun() -> receive + read -> + io:format("reading"), + literal_receiver() + end + end, [link,{min_heap_size, 10000}]), + Switcher = spawn_link(fun() -> literal_switcher() end), + Pids = [spawn_opt(fun() -> receive + {Pid, go, Recv, N} -> + io:format("sender batch (~w) start ~w~n",[N,self()]), + literal_sender(N,Recv), + Pid ! {self(), ok} + end + end, [link,{min_heap_size,800}]) || _ <- lists:seq(1,100)], + _ = [Pid ! {self(), go, Recv, N} || Pid <- Pids], + %% don't read immediately + timer:sleep(5), + Recv ! read, + Switcher ! {switch,?mod,Bin,[Recv|Pids],200}, + _ = [receive {Pid, ok} -> ok end || Pid <- Pids], + Switcher ! {self(), done}, + receive {Switcher, ok} -> ok end, + Recv ! {self(), done}, + receive {Recv, ok} -> ok end, + ok. + +literal_receiver() -> + receive + {Pid, done} -> + io:format("reader_done~n"), + Pid ! {self(), ok}; + {_Pid, msg, [A,B,C,D,E]} -> + A = ?mod:a(), + B = ?mod:b(), + C = ?mod:c(), + D = ?mod:d(), + E = ?mod:e(), + literal_receiver(); + {Pid, sender_confirm} -> + io:format("sender confirm ~w~n", [Pid]), + Pid ! {self(), ok}, + literal_receiver() + end. + +literal_sender(0, Recv) -> + Recv ! {self(), sender_confirm}, + receive {Recv, ok} -> ok end; +literal_sender(N, Recv) -> + Recv ! {self(), msg, [?mod:a(), + ?mod:b(), + ?mod:c(), + ?mod:d(), + ?mod:e()]}, + literal_sender(N - 1, Recv). + +literal_switcher() -> + receive + {switch,Mod,Bin,Pids,Tmo} -> + literal_switcher(Mod,Bin,Pids,Tmo) + end. +literal_switcher(Mod,Bin,Pids,Tmo) -> + receive + {Pid,done} -> + Pid ! {self(),ok} + after Tmo -> + io:format("load module ~w~n", [Mod]), + {module, Mod} = erlang:load_module(Mod,Bin), + ok = check_and_purge(Pids,Mod), + io:format("purge complete ~w~n", [Mod]), + literal_switcher(Mod,Bin,Pids,Tmo+Tmo) + end. + +check_and_purge([],Mod) -> + erlang:purge_module(Mod), + ok; +check_and_purge(Pids,Mod) -> + io:format("purge ~w~n", [Mod]), + Tag = make_ref(), + _ = [begin + erlang:check_process_code(Pid,Mod,[{async,{Tag,Pid}}]) + end || Pid <- Pids], + Retry = check_and_purge_receive(Pids,Tag,[]), + check_and_purge(Retry,Mod). + +check_and_purge_receive([Pid|Pids],Tag,Retry) -> + receive + {check_process_code, {Tag, Pid}, false} -> + check_and_purge_receive(Pids,Tag,Retry); + {check_process_code, {Tag, Pid}, true} -> + check_and_purge_receive(Pids,Tag,[Pid|Retry]) + end; +check_and_purge_receive([],_,Retry) -> + Retry. + + +gen_lit(Module,Terms) -> + FunStrings = [lists:flatten(io_lib:format("~w() -> ~w.~n", [F,Term]))||{F,Term}<-Terms], + FunForms = function_forms(FunStrings), + Forms = [{attribute,erl_anno:new(1),module,Module}, + {attribute,erl_anno:new(2),export,[FA || {FA,_} <- FunForms]}] ++ + [Function || {_, Function} <- FunForms], + {ok, Module, Bin} = compile:forms(Forms), + Bin. + +function_forms([]) -> []; +function_forms([S|Ss]) -> + {ok, Ts,_} = erl_scan:string(S), + {ok, Form} = erl_parse:parse_form(Ts), + Fun = element(3, Form), + Arity = element(4, Form), + [{{Fun,Arity}, Form}|function_forms(Ss)]. chase_msg(0, Pid) -> chase_loop(Pid); -- cgit v1.2.3 From 9effc6a6c52d9c492925c4271743d06d68d04bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 18 May 2016 19:38:47 +0200 Subject: erts: Copy literals in messages on module purge During check process code, explicitly copy all referenced literals in a message (in the private queue) to a heap fragment and attach it to the message reference. Not all types of message communication does an explicit copy of a literal and this needs to be taken care of before a module is purged. --- erts/emulator/beam/beam_bif_load.c | 136 ++++++++++++++++++++++++++++++++----- 1 file changed, 118 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 40d44dda4c..8b5d1a0391 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -762,6 +762,11 @@ check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size) return 0; } +static Uint hfrag_literal_size(Eterm* start, Eterm* end, + char* lit_start, Uint lit_size); +static void hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp, + Eterm *start, Eterm *end, + char *lit_start, Uint lit_size); static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls) @@ -842,9 +847,14 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls } /* - * Message queue can contains funs, but (at least currently) no + * Message queue can contains funs, and may contain * literals. If we got references to this module from the message - * queue, a GC cannot remove these... + * queue. + * + * If a literal is in the message queue we maka an explicit copy of + * and attach it to the heap fragment. Each message needs to be + * self contained, we cannot save the literal in the old_heap or + * any other heap than the message it self. */ erts_smp_proc_lock(rp, ERTS_PROC_LOCK_MSGQ); @@ -861,15 +871,31 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls hfrag = msgp->data.heap_frag; else continue; - for (; hfrag; hfrag = hfrag->next) { - if (check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size)) - return am_true; - /* Should not contain any literals... */ - ASSERT(!any_heap_refs(&hfrag->mem[0], - &hfrag->mem[hfrag->used_size], - literals, - lit_bsize)); - } + { + ErlHeapFragment *hf; + Uint lit_sz; + for (hf=hfrag; hf; hf = hf->next) { + if (check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size)) + return am_true; + lit_sz = hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size], + literals, lit_bsize); + } + if (lit_sz > 0) { + ErlHeapFragment *bp = new_message_buffer(lit_sz); + Eterm *hp = bp->mem; + + for (hf=hfrag; hf; hf = hf->next) { + hfrag_literal_copy(&hp, &bp->off_heap, + &hf->mem[0], &hf->mem[hf->used_size], + literals, lit_bsize); + hfrag=hf; + } + /* link new hfrag last */ + ASSERT(hfrag->next == NULL); + hfrag->next = bp; + bp->next = NULL; + } + } } while (1) { @@ -916,11 +942,11 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls goto try_literal_gc; } -#ifdef DEBUG /* - * Message buffer fragments should not have any references - * to literals, and off heap lists should already have - * been moved into process off heap structure. + * Message buffer fragments (matched messages) + * - off heap lists should already have been moved into + * process off heap structure. + * - Check for literals */ for (msgp = rp->msg_frag; msgp; msgp = msgp->next) { if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) @@ -933,12 +959,12 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls hp = &hfrag->mem[0]; hp_end = &hfrag->mem[hfrag->used_size]; - ASSERT(!any_heap_refs(hp, hp_end, literals, lit_bsize)); + + if (any_heap_refs(hp, hp_end, literals, lit_bsize)) + goto try_literal_gc; } } -#endif - return am_false; try_literal_gc: @@ -1038,6 +1064,80 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) return 0; } +static Uint +hfrag_literal_size(Eterm* start, Eterm* end, char* lit_start, Uint lit_size) +{ + Eterm* p; + Eterm val; + Uint sz = 0; + + for (p = start; p < end; p++) { + val = *p; + switch (primary_tag(val)) { + case TAG_PRIMARY_BOXED: + case TAG_PRIMARY_LIST: + if (ErtsInArea(val, lit_start, lit_size)) { + sz += size_object(val); + } + break; + case TAG_PRIMARY_HEADER: + if (!header_is_transparent(val)) { + Eterm* new_p; + if (header_is_bin_matchstate(val)) { + ErlBinMatchState *ms = (ErlBinMatchState*) p; + ErlBinMatchBuffer *mb = &(ms->mb); + if (ErtsInArea(mb->orig, lit_start, lit_size)) { + sz += size_object(mb->orig); + } + } + new_p = p + thing_arityval(val); + ASSERT(start <= new_p && new_p < end); + p = new_p; + } + } + } + return sz; +} + +static void +hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp, + Eterm *start, Eterm *end, + char *lit_start, Uint lit_size) { + Eterm* p; + Eterm val; + Uint sz; + + for (p = start; p < end; p++) { + val = *p; + switch (primary_tag(val)) { + case TAG_PRIMARY_BOXED: + case TAG_PRIMARY_LIST: + if (ErtsInArea(val, lit_start, lit_size)) { + sz = size_object(val); + val = copy_struct(val, sz, hpp, ohp); + *p = val; + } + break; + case TAG_PRIMARY_HEADER: + if (!header_is_transparent(val)) { + Eterm* new_p; + /* matchstate in message, not possible. */ + if (header_is_bin_matchstate(val)) { + ErlBinMatchState *ms = (ErlBinMatchState*) p; + ErlBinMatchBuffer *mb = &(ms->mb); + if (ErtsInArea(mb->orig, lit_start, lit_size)) { + sz = size_object(mb->orig); + mb->orig = copy_struct(mb->orig, sz, hpp, ohp); + } + } + new_p = p + thing_arityval(val); + ASSERT(start <= new_p && new_p < end); + p = new_p; + } + } + } +} + #undef in_area #ifdef ERTS_SMP -- cgit v1.2.3 From 703eea866831dea0753c5cd151d6d8d55debd470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 18 May 2016 19:45:42 +0200 Subject: erts: Refactor ERTS_MSG_COMBINED_HFRAG to heap fragment --- erts/emulator/beam/beam_bif_load.c | 5 +---- erts/emulator/beam/erl_gc.c | 11 ++--------- erts/emulator/beam/erl_message.h | 9 +++++---- 3 files changed, 8 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 8b5d1a0391..15e878ba65 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -949,10 +949,7 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls * - Check for literals */ for (msgp = rp->msg_frag; msgp; msgp = msgp->next) { - if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) - hfrag = &msgp->hfrag; - else - hfrag = msgp->data.heap_frag; + hfrag = erts_message_to_heap_frag(msgp); for (; hfrag; hfrag = hfrag->next) { Eterm *hp, *hp_end; ASSERT(!check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size)); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index d740b2baec..374da9407c 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2279,10 +2279,7 @@ move_msgq_to_heap(Process *p) } else { - if (mp->data.attached == ERTS_MSG_COMBINED_HFRAG) - bp = &mp->hfrag; - else - bp = mp->data.heap_frag; + bp = erts_message_to_heap_frag(mp); if (bp->next) erts_move_multi_frags(&factory.hp, factory.off_heap, bp, @@ -3304,11 +3301,7 @@ within2(Eterm *ptr, Process *p, Eterm *real_htop) while (mp) { - if (mp->data.attached == ERTS_MSG_COMBINED_HFRAG) - bp = &mp->hfrag; - else - bp = mp->data.heap_frag; - + bp = erts_message_to_heap_frag(mp); mp = mp->next; search_heap_frags: diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 851ac37fda..ecf65ca324 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -369,6 +369,10 @@ ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg); #define ERTS_MSG_COMBINED_HFRAG ((void *) 0x1) +#define erts_message_to_heap_frag(MP) \ + (((MP)->data.attached == ERTS_MSG_COMBINED_HFRAG) ? \ + &(MP)->hfrag : (MP)->data.heap_frag) + #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_FORCE_INLINE ErtsMessage *erts_alloc_message(Uint sz, Eterm **hpp) @@ -449,10 +453,7 @@ ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg) ASSERT(msg->data.attached); if (is_value(ERL_MESSAGE_TERM(msg))) { ErlHeapFragment *bp; - if (msg->data.attached == ERTS_MSG_COMBINED_HFRAG) - bp = &msg->hfrag; - else - bp = msg->data.heap_frag; + bp = erts_message_to_heap_frag(msg); return erts_used_frag_sz(bp); } else if (msg->data.dist_ext->heap_size < 0) -- cgit v1.2.3 From f1be30c44044e7bb0167a34d234b6b2322328def Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 1 Dec 2015 13:14:43 +0100 Subject: Clarify limitation on halt/2 slogan string --- erts/doc/src/erlang.xml | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 9287b32fec..1e251d33f3 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1802,6 +1802,8 @@ os_prompt% string() An Erlang crash dump is produced with Status as slogan. Then the runtime system exits with status code 1. + Note that the string may not be longer than 200 characters and only + code points in the range 0-255 may be used. abort -- cgit v1.2.3 From be353901879b3cccda7cd01947936cf1550dea04 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 1 Dec 2015 11:25:12 +0100 Subject: Check exit status in init:stop/1 and simplify documentation --- erts/doc/src/init.xml | 10 ++-------- erts/preloaded/src/init.erl | 36 ++++++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index 84a5aea335..3546099fad 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -178,14 +178,8 @@ Take down an Erlang node smoothly -

All applications are taken down smoothly, all code is - unloaded, and all ports are closed before the system - terminates. If the -heart command line flag was given, - the heart program is terminated before the Erlang node - terminates. Refer to heart(3) for more information.

-

To limit the shutdown time, the time init is allowed - to spend taking down applications, the -shutdown_time - command line flag should be used.

+

The same as + stop(0).

diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 618b53f6bb..04c5210aa3 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -90,6 +90,8 @@ -define(ON_LOAD_HANDLER, init__boot__on_load_handler). +-define(MAX_HALT_STRING_SIZE, 199). + debug(false, _) -> ok; debug(_, T) -> erlang:display(T). @@ -173,7 +175,25 @@ stop() -> init ! {stop,stop}, ok. -spec stop(Status) -> 'ok' when Status :: non_neg_integer() | string(). -stop(Status) -> init ! {stop,{stop,Status}}, ok. +stop(Status) when is_integer(Status), Status >= 0 -> + stop_1(Status); +stop(Status) when is_list(Status) -> + case is_bytelist(Status) of + true -> + stop_1(limit_halt_string(Status)); + false -> + erlang:error(badarg) + end; +stop(_) -> + erlang:error(badarg). + +is_bytelist([B|Bs]) when is_integer(B), B >= 0, B < 256 -> is_bytelist(Bs); +is_bytelist([]) -> true; +is_bytelist(_) -> false. + +%% Note that we check the type of Status beforehand to ensure that +%% the call to halt(Status) by the init process cannot fail +stop_1(Status) -> init ! {stop,{stop,Status}}, ok. -spec boot(BootArgs) -> no_return() when BootArgs :: [binary()]. @@ -285,16 +305,12 @@ things_to_string([]) -> "". halt_string(String, List) -> - HaltString = String ++ things_to_string(List), - if - length(HaltString)<199 -> HaltString; - true -> first198(HaltString, 198) - end. + limit_halt_string(String ++ things_to_string(List)). -first198([H|T], N) when N>0 -> - [H|first198(T, N-1)]; -first198(_, 0) -> - []. +limit_halt_string(String) when length(String) < ?MAX_HALT_STRING_SIZE -> + String; +limit_halt_string(String) -> + lists:sublist(String, ?MAX_HALT_STRING_SIZE-1). %% String = string() %% List = [string() | atom() | pid() | number()] -- cgit v1.2.3 From 730b5c14a33800bb5b0c19e8e48b213e07402179 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 13 May 2016 14:15:21 +0200 Subject: minor fixes --- erts/doc/src/erl_nif.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 33a4fee182..181c6f50d7 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -322,13 +322,13 @@ ok

The enif_consume_timeslice() - function can be used to inform the runtime system about the lenght of the + function can be used to inform the runtime system about the length of the NIF call. It should typically always be used unless the NIF executes very quickly.

-

If the NIF call is too lenghty one needs to handle this in one of the +

If the NIF call is too lengthy one needs to handle this in one of the following ways in order to avoid degraded responsiveness, scheduler load - balancing problems, and other strange behaviours:

+ balancing problems, and other strange behaviors:

Yielding NIF @@ -410,14 +410,14 @@ ok erlang:system_flag(multi_scheduling, block), might also take a very long time to complete. This since all ongoing dirty operations on all - dirty schedulers need to complete before the the block + dirty schedulers need to complete before the block operation can complete.

A lot of operations communicating with a process executing a dirty NIF can, however, complete while it is executing the - dirty NIF. For example, retreiving information about it via + dirty NIF. For example, retrieving information about it via process_info(), setting its group leader, register/unregister its name, etc.

@@ -425,10 +425,10 @@ ok

Termination of a process executing a dirty NIF can only be completed up to a certain point while it is executing the - dirty NIF. All Erlang resources such as registered names, - ETS tables, etc will be released. All links and monitors + dirty NIF. All Erlang resources such as its registered name, + its ETS tables, etc will be released. All links and monitors will be triggered. The actual execution of the NIF will - however not be stopped. The NIF can safely contiue + however not be stopped. The NIF can safely continue execution, allocate heap memory, etc, but it is of course better to stop executing as soon as possible. The NIF can check whether current process is alive or not using @@ -450,8 +450,8 @@ ok collect a process in order to determine if it has references to the module, a process executing a dirty NIF might delay purging for a very long time. Delaying - a purge operatin implies delaying all code - loding operations which might cause severe problems for + a purge operation implies delaying all code + loading operations which might cause severe problems for the system as a whole.

-- cgit v1.2.3 From 7d635a1d4d5e33ac874cbc7fe97589652b73c914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 23 May 2016 14:46:19 +0200 Subject: erts: Print valid version of lttng-ust in configure help --- erts/configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 4a63381eb7..a34dfc6dbd 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3868,7 +3868,7 @@ if test "$enable_lttng_test" = "yes" ; then #define TRACEPOINT_DEFINE], [if(tracepoint_enabled(com_ericsson_otp,dummy)) do {} while(0)])], [AC_MSG_RESULT([yes])], - [AC_MSG_ERROR([no (must be present)])]) + [AC_MSG_ERROR([no (available in lttng-ust v2.7)])]) if test "x$ac_cv_header_lttng_tracepoint_h" = "xyes" \ -a "x$ac_cv_header_lttng_tracepoint_event_h" = "xyes"; then # No straight forward way to test for liblttng-ust when no public symbol exists, -- cgit v1.2.3 From 115f0ba77ad7d01ab95fd9f9bbeca53f04f12284 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 19 May 2016 10:54:43 +0200 Subject: erts: Move tracer SecondTraceTerm to Opts map The extra trace data has been moved to the opts map in order for the tracer to be able to distinguish inbetween extra trace data 'undefined' and no extra trace data. In the same commit all opts associations have been changed so that if the tracer should not use them, the key is left unassicated instead of being sent to undefined. This should be give a small performance gain and also makes the API easier to work with. --- erts/doc/src/erl_tracer.xml | 96 +++++++++---------- erts/emulator/beam/atom.names | 7 +- erts/emulator/beam/erl_trace.c | 65 +++++++------ erts/emulator/nifs/common/erl_tracer_nif.c | 34 +++---- erts/emulator/test/tracer_SUITE.erl | 103 +++++++++++++++------ erts/emulator/test/tracer_SUITE_data/tracer_test.c | 4 +- erts/emulator/test/tracer_test.erl | 4 +- erts/preloaded/ebin/erl_tracer.beam | Bin 2112 -> 2200 bytes erts/preloaded/src/erl_tracer.erl | 14 +-- 9 files changed, 187 insertions(+), 140 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml index d4c8bbad31..7841fdfd63 100644 --- a/erts/doc/src/erl_tracer.xml +++ b/erts/doc/src/erl_tracer.xml @@ -67,7 +67,7 @@

The different trace tags that the tracer will be called with. Each trace tag is described in greater detail in - Module:trace/6 + Module:trace/5

@@ -84,14 +84,18 @@

The options for the tracee.

timestamp - If not set to undefined, the tracer has been requested to - include a timestamp. + If set the tracer has been requested to include a timestamp. + extra + If set the tracepoint has included additonal data about + the trace event. What the additional data is depends on which + TraceTag has been triggered. The extra trace data + corresponds to the fifth elemnt in the trace tuples described in + erlang:trace/3. match_spec_result - If not set to true, the tracer has been requested to - include the output of a match specification that was run. + If set the tracer has been requested to include the output + of a match specification that was run. scheduler_id - Set to a number if the scheduler id is to be included by the tracer. - Otherwise it is set to undefined. + Set the scheduler id is to be included by the tracer. @@ -115,23 +119,23 @@ Module:enabled/3 Mandatory - Module:trace/6 + Module:trace/5 Mandatory Module:enabled_procs/3 Optional - Module:trace_procs/6 + Module:trace_procs/5 Optional Module:enabled_ports/3 Optional - Module:trace_ports/6 + Module:trace_ports/5 Optional Module:enabled_running_ports/3 Optional - Module:trace_running_ports/6 + Module:trace_running_ports/5 Optional Module:enabled_running_procs/3 Optional - Module:trace_running_procs/6 + Module:trace_running_procs/5 Optional @@ -166,14 +170,13 @@
- Module:trace(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Module:trace(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result Check if a trace event should be generated. TraceTag = trace_tag() TracerState = term() Tracee = tracee() FirstTraceTerm = term() - SecondTraceTerm = term() | undefined Opts = trace_opts() Result = ok @@ -182,17 +185,17 @@ the Module:enabled/3 callback returned trace. In it any side effects needed by the tracer should be done. The tracepoint payload is located in - the FirstTraceTerm and SecondTraceTerm. The content - of the TraceTerms depends on which TraceTag has been triggered. - The FirstTraceTerm and SecondTraceTerm correspond to the - fourth and fifth slot in the trace tuples described in + the TraceTerm. The content of the TraceTerm depends on which + TraceTag has been triggered. + The TraceTerm corresponds to the + fourth element in the trace tuples described in erlang:trace/3. - If the tuple only has four elements, SecondTraceTerm will be - undefined.

+ If the trace tuple has five elements, the fifth element will be sent as + the extra value in the Opts maps.

- Module:trace(seq_trace, TracerState, Label, SeqTraceInfo, undefined, Opts) -> Result + Module:trace(seq_trace, TracerState, Label, SeqTraceInfo, Opts) -> Result Check if a sequence trace event should be generated. TracerState = term() @@ -228,14 +231,13 @@ - Module:trace_procs(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result Check if a trace event should be generated. TraceTag = trace_tag() TracerState = term() Tracee = tracee() FirstTraceTerm = term() - SecondTraceTerm = term() | undefined Opts = trace_opts() Result = ok @@ -243,7 +245,7 @@

This callback will be called when a tracepoint is triggered and the Module:enabled_procs/3 callback returned trace.

-

If trace_procs/6 is not defined trace/6 will be called instead.

+

If trace_procs/5 is not defined trace/5 will be called instead.

@@ -265,14 +267,13 @@ - Module:trace_ports(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result Check if a trace event should be generated. TraceTag = trace_tag() TracerState = term() Tracee = tracee() FirstTraceTerm = term() - SecondTraceTerm = term() | undefined Opts = trace_opts() Result = ok @@ -280,7 +281,7 @@

This callback will be called when a tracepoint is triggered and the Module:enabled_ports/3 callback returned trace.

-

If trace_ports/6 is not defined trace/6 will be called instead.

+

If trace_ports/5 is not defined trace/5 will be called instead.

@@ -302,14 +303,13 @@ - Module:trace_running_procs(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Module:trace_running_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result Check if a trace event should be generated. TraceTag = trace_tag_running_procs() TracerState = term() Tracee = tracee() FirstTraceTerm = term() - SecondTraceTerm = term() | undefined Opts = trace_opts() Result = ok @@ -317,7 +317,7 @@

This callback will be called when a tracepoint is triggered and the Module:enabled_running_procs/3 callback returned trace.

-

If trace_running_procs/6 is not defined trace/6 will be called instead.

+

If trace_running_procs/5 is not defined trace/5 will be called instead.

@@ -339,14 +339,13 @@ - Module:trace_running_ports(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Module:trace_running_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result Check if a trace event should be generated. TraceTag = trace_tag_running_ports() TracerState = term() Tracee = tracee() FirstTraceTerm = term() - SecondTraceTerm = term() | undefined Opts = trace_opts() Result = ok @@ -354,7 +353,7 @@

This callback will be called when a tracepoint is triggered and the Module:enabled_running_ports/3 callback returned trace.

-

If trace_running_ports/6 is not defined trace/6 will be called instead.

+

If trace_running_ports/5 is not defined trace/5 will be called instead.

@@ -376,14 +375,13 @@ - Module:trace_call(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result Check if a trace event should be generated. TraceTag = trace_tag_call() TracerState = term() Tracee = tracee() FirstTraceTerm = term() - SecondTraceTerm = term() | undefined Opts = trace_opts() Result = ok @@ -391,7 +389,7 @@

This callback will be called when a tracepoint is triggered and the Module:enabled_call/3 callback returned trace.

-

If trace_call/6 is not defined trace/6 will be called instead.

+

If trace_call/5 is not defined trace/5 will be called instead.

@@ -413,14 +411,13 @@ - Module:trace_send(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result Check if a trace event should be generated. TraceTag = trace_tag_send() TracerState = term() Tracee = tracee() FirstTraceTerm = term() - SecondTraceTerm = term() | undefined Opts = trace_opts() Result = ok @@ -428,7 +425,7 @@

This callback will be called when a tracepoint is triggered and the Module:enabled_send/3 callback returned trace.

-

If trace_send/6 is not defined trace/6 will be called instead.

+

If trace_send/5 is not defined trace/5 will be called instead.

@@ -450,14 +447,13 @@ - Module:trace_receive(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result Check if a trace event should be generated. TraceTag = trace_tag_receive() TracerState = term() Tracee = tracee() FirstTraceTerm = term() - SecondTraceTerm = term() | undefined Opts = trace_opts() Result = ok @@ -465,7 +461,7 @@

This callback will be called when a tracepoint is triggered and the Module:enabled_receive/3 callback returned trace.

-

If trace_receive/6 is not defined trace/6 will be called instead.

+

If trace_receive/5 is not defined trace/5 will be called instead.

@@ -487,14 +483,13 @@ - Module:trace_garbage_collection(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result + Module:trace_garbage_collection(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result Check if a trace event should be generated. TraceTag = trace_tag_gc() TracerState = term() Tracee = tracee() FirstTraceTerm = term() - SecondTraceTerm = term() | undefined Opts = trace_opts() Result = ok @@ -502,7 +497,7 @@

This callback will be called when a tracepoint is triggered and the Module:enabled_garbage_collection/3 callback returned trace.

-

If trace_garbage_collection/6 is not defined trace/6 will be called instead.

+

If trace_garbage_collection/5 is not defined trace/5 will be called instead.

@@ -543,7 +538,7 @@ ok
 -module(erl_msg_tracer).
 
--export([enabled/3, trace/6, load/0]).
+-export([enabled/3, trace/5, load/0]).
 
 load() ->
     erlang:load_nif("erl_msg_tracer", []).
@@ -551,7 +546,7 @@ load() ->
 enabled(_, _, _) ->
     error.
 
-trace(_, _, _,_, _, _) ->
+trace(_, _, _,_, _) ->
     error.
     

erl_msg_tracer.c

@@ -569,7 +564,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ErlNifFunc nif_funcs[] = { {"enabled", 3, enabled}, - {"trace", 6, trace} + {"trace", 5, trace} }; ERL_NIF_INIT(erl_msg_tracer, nif_funcs, load, NULL, upgrade, unload) @@ -632,9 +627,8 @@ static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) * argv[0]: TraceTag, should only be 'send' * argv[1]: TracerState, process to send {argv[2], argv[4]} to * argv[2]: Tracee - * argv[3]: Message, ignored - * argv[4]: Recipient - * argv[5]: Options, ignored + * argv[3]: Recipient + * argv[4]: Options, ignored */ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 8f65e71531..7fdce62bd1 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -228,9 +228,6 @@ atom endian atom env atom eof atom eol -atom exception_from -atom exception_trace -atom extended atom Eq='=:=' atom Eqeq='==' atom erl_tracer @@ -243,6 +240,8 @@ atom ets atom ETS_TRANSFER='ETS-TRANSFER' atom event atom exact_reductions +atom exception_from +atom exception_trace atom exclusive atom exit_status atom existing @@ -251,7 +250,9 @@ atom existing_ports atom existing atom exiting atom exports +atom extended atom external +atom extra atom false atom fcgi atom fd diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index ca001fc156..3dca58d60b 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -2625,7 +2625,7 @@ static void init_tracer_template(ErtsTracerNif *tnif) { /* default tracer functions */ tnif->tracers[TRACE_FUN_DEFAULT].name = "trace"; - tnif->tracers[TRACE_FUN_DEFAULT].arity = 6; + tnif->tracers[TRACE_FUN_DEFAULT].arity = 5; tnif->tracers[TRACE_FUN_DEFAULT].cb = NULL; tnif->tracers[TRACE_FUN_ENABLED].name = "enabled"; @@ -2634,35 +2634,35 @@ static void init_tracer_template(ErtsTracerNif *tnif) { /* specific tracer functions */ tnif->tracers[TRACE_FUN_T_SEND].name = "trace_send"; - tnif->tracers[TRACE_FUN_T_SEND].arity = 6; + tnif->tracers[TRACE_FUN_T_SEND].arity = 5; tnif->tracers[TRACE_FUN_T_SEND].cb = NULL; tnif->tracers[TRACE_FUN_T_RECEIVE].name = "trace_receive"; - tnif->tracers[TRACE_FUN_T_RECEIVE].arity = 6; + tnif->tracers[TRACE_FUN_T_RECEIVE].arity = 5; tnif->tracers[TRACE_FUN_T_RECEIVE].cb = NULL; tnif->tracers[TRACE_FUN_T_CALL].name = "trace_call"; - tnif->tracers[TRACE_FUN_T_CALL].arity = 6; + tnif->tracers[TRACE_FUN_T_CALL].arity = 5; tnif->tracers[TRACE_FUN_T_CALL].cb = NULL; tnif->tracers[TRACE_FUN_T_SCHED_PROC].name = "trace_running_procs"; - tnif->tracers[TRACE_FUN_T_SCHED_PROC].arity = 6; + tnif->tracers[TRACE_FUN_T_SCHED_PROC].arity = 5; tnif->tracers[TRACE_FUN_T_SCHED_PROC].cb = NULL; tnif->tracers[TRACE_FUN_T_SCHED_PORT].name = "trace_running_ports"; - tnif->tracers[TRACE_FUN_T_SCHED_PORT].arity = 6; + tnif->tracers[TRACE_FUN_T_SCHED_PORT].arity = 5; tnif->tracers[TRACE_FUN_T_SCHED_PORT].cb = NULL; tnif->tracers[TRACE_FUN_T_GC].name = "trace_garbage_collection"; - tnif->tracers[TRACE_FUN_T_GC].arity = 6; + tnif->tracers[TRACE_FUN_T_GC].arity = 5; tnif->tracers[TRACE_FUN_T_GC].cb = NULL; tnif->tracers[TRACE_FUN_T_PROCS].name = "trace_procs"; - tnif->tracers[TRACE_FUN_T_PROCS].arity = 6; + tnif->tracers[TRACE_FUN_T_PROCS].arity = 5; tnif->tracers[TRACE_FUN_T_PROCS].cb = NULL; tnif->tracers[TRACE_FUN_T_PORTS].name = "trace_ports"; - tnif->tracers[TRACE_FUN_T_PORTS].arity = 6; + tnif->tracers[TRACE_FUN_T_PORTS].arity = 5; tnif->tracers[TRACE_FUN_T_PORTS].cb = NULL; /* specific enabled functions */ @@ -2834,10 +2834,12 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee, Eterm tag, Eterm msg, Eterm extra, Eterm pam_result) { if (tnif || (tnif = lookup_tracer_nif(tracer)) != NULL) { -#define MAP_SIZE 3 - Eterm argv[6], local_heap[3+MAP_SIZE /* values */ + (MAP_SIZE+1 /* keys */)]; +#define MAP_SIZE 4 + Eterm argv[5], local_heap[3+MAP_SIZE /* values */ + (MAP_SIZE+1 /* keys */)]; flatmap_t *map = (flatmap_t*)(local_heap+(MAP_SIZE+1)); Eterm *map_values = flatmap_get_values(map); + Eterm *map_keys = local_heap + 1; + Uint map_elem_count = 0; topt = (tnif->tracers[topt].cb) ? topt : TRACE_FUN_DEFAULT; ASSERT(topt < NIF_TRACER_TYPES); @@ -2846,31 +2848,40 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee, argv[1] = ERTS_TRACER_STATE(tracer); argv[2] = t_p_id; argv[3] = msg; - argv[4] = extra == THE_NON_VALUE ? am_undefined : extra; - argv[5] = make_flatmap(map); + argv[4] = make_flatmap(map); map->thing_word = MAP_HEADER_FLATMAP; - map->size = MAP_SIZE; - map->keys = TUPLE3(local_heap, am_match_spec_result, am_scheduler_id, am_timestamp); - - *map_values++ = pam_result; - if (tracee_flags & F_TRACE_SCHED_NO) - *map_values++ = make_small(erts_get_scheduler_id()); - else - *map_values++ = am_undefined; + + if (extra != THE_NON_VALUE) { + map_keys[map_elem_count] = am_extra; + map_values[map_elem_count++] = extra; + } + + if (pam_result != am_true) { + map_keys[map_elem_count] = am_match_spec_result; + map_values[map_elem_count++] = pam_result; + } + + if (tracee_flags & F_TRACE_SCHED_NO) { + map_keys[map_elem_count] = am_scheduler_id; + map_values[map_elem_count++] = make_small(erts_get_scheduler_id()); + } + map_keys[map_elem_count] = am_timestamp; if (tracee_flags & F_NOW_TS) #ifdef HAVE_ERTS_NOW_CPU if (erts_cpu_timestamp) - *map_values++ = am_cpu_timestamp; + map_values[map_elem_count++] = am_cpu_timestamp; else #endif - *map_values++ = am_timestamp; + map_values[map_elem_count++] = am_timestamp; else if (tracee_flags & F_STRICT_MON_TS) - *map_values++ = am_strict_monotonic; + map_values[map_elem_count++] = am_strict_monotonic; else if (tracee_flags & F_MON_TS) - *map_values++ = am_monotonic; - else - *map_values++ = am_undefined; + map_values[map_elem_count++] = am_monotonic; + + map->size = map_elem_count; + map->keys = make_tuple(local_heap); + local_heap[0] = make_arityval(map_elem_count); #undef MAP_SIZE erts_nif_call_function(c_p, tracee ? tracee : c_p, diff --git a/erts/emulator/nifs/common/erl_tracer_nif.c b/erts/emulator/nifs/common/erl_tracer_nif.c index 6dddc80607..c0cc48ff42 100644 --- a/erts/emulator/nifs/common/erl_tracer_nif.c +++ b/erts/emulator/nifs/common/erl_tracer_nif.c @@ -45,7 +45,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ErlNifFunc nif_funcs[] = { {"enabled", 3, enabled}, - {"trace", 6, trace} + {"trace", 5, trace} }; @@ -57,6 +57,7 @@ ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload) ATOM_DECL(cpu_timestamp); \ ATOM_DECL(discard); \ ATOM_DECL(exception_from); \ + ATOM_DECL(extra); \ ATOM_DECL(match_spec_result); \ ATOM_DECL(monotonic); \ ATOM_DECL(ok); \ @@ -76,8 +77,7 @@ ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload) ATOM_DECL(gc_minor_start); \ ATOM_DECL(gc_minor_end); \ ATOM_DECL(gc_major_start); \ - ATOM_DECL(gc_major_end); \ - ATOM_DECL(undefined); + ATOM_DECL(gc_major_end); #define ATOM_DECL(A) static ERL_NIF_TERM atom_##A ATOMS @@ -154,11 +154,6 @@ static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) Tracee :: pid() || port() || undefined, Msg :: term(), Opts :: map()) -> ignored(). - -spec trace(Tag :: atom(), TracerState :: pid() | port(), - Tracee :: pid() || port() || undefined, - Msg :: term(), - Extra :: term(), - Opts :: map()) -> ignored(). */ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { @@ -167,7 +162,8 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifPort to_port; size_t tt_sz = 0; int is_port = 0; - ASSERT(argc == 6); + size_t opts_sz = 0; + ASSERT(argc == 5); if (!enif_get_local_pid(env, argv[1], &to_pid)) { if (!enif_get_local_port(env, argv[1], &to_port)) { @@ -179,12 +175,17 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) is_port = 1; } - if (!enif_is_identical(argv[4], atom_undefined)) { + opts = argv[4]; + + if (!enif_get_map_size(env, opts, &opts_sz)) + opts_sz = 0; + + if (opts_sz && enif_get_map_value(env, opts, atom_extra, &value)) { tt[tt_sz++] = atom_trace; tt[tt_sz++] = argv[2]; tt[tt_sz++] = argv[0]; tt[tt_sz++] = argv[3]; - tt[tt_sz++] = argv[4]; + tt[tt_sz++] = value; } else { if (enif_is_identical(argv[0], atom_seq_trace)) { tt[tt_sz++] = atom_seq_trace; @@ -198,21 +199,16 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) } } - opts = argv[5]; - if (enif_get_map_value(env, opts, atom_match_spec_result, - &value) - && !enif_is_identical(value, atom_true)) { + if (opts_sz && enif_get_map_value(env, opts, atom_match_spec_result, &value)) { tt[tt_sz++] = value; } - if (enif_get_map_value(env, opts, atom_scheduler_id, &value) - && !enif_is_identical(value, atom_undefined)) { + if (opts_sz && enif_get_map_value(env, opts, atom_scheduler_id, &value)) { tt[tt_sz++] = value; } - if (enif_get_map_value(env, opts, atom_timestamp, &value) - && !enif_is_identical(value, atom_undefined)) { + if (opts_sz && enif_get_map_value(env, opts, atom_timestamp, &value)) { ERL_NIF_TERM ts; if (enif_is_identical(value, atom_monotonic)) { ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC); diff --git a/erts/emulator/test/tracer_SUITE.erl b/erts/emulator/test/tracer_SUITE.erl index 20fb7e475e..9eb55c9af3 100644 --- a/erts/emulator/test/tracer_SUITE.erl +++ b/erts/emulator/test/tracer_SUITE.erl @@ -28,9 +28,9 @@ init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). -export([load/1, unload/1, reload/1, invalid_tracers/1]). --export([send/1, recv/1, spawn/1, exit/1, link/1, unlink/1, - getting_linked/1, getting_unlinked/1, register/1, unregister/1, - in/1, out/1, gc_start/1, gc_end/1]). +-export([send/1, recv/1, call/1, call_return/1, spawn/1, exit/1, + link/1, unlink/1, getting_linked/1, getting_unlinked/1, + register/1, unregister/1, in/1, out/1, gc_start/1, gc_end/1]). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 1}}]. @@ -39,9 +39,9 @@ all() -> [load, unload, reload, invalid_tracers, {group, basic}]. groups() -> - [{ basic, [], [send, recv, spawn, exit, link, unlink, getting_linked, - getting_unlinked, register, unregister, in, out, - gc_start, gc_end]}]. + [{ basic, [], [send, recv, call, call_return, spawn, exit, + link, unlink, getting_linked, getting_unlinked, + register, unregister, in, out, gc_start, gc_end]}]. init_per_suite(Config) -> erlang:trace_pattern({'_','_','_'}, false, [local]), @@ -223,8 +223,8 @@ send(_Config) -> Expect = fun(Pid, State, EOpts) -> receive Msg -> - {send, State, Pid, ok, Self, Opts} = Msg, - check_opts(EOpts, Opts) + {send, State, Pid, ok, Opts} = Msg, + check_opts(EOpts, Opts, Self) end end, test(send, Tc, Expect). @@ -239,13 +239,59 @@ recv(_Config) -> Expect = fun(Pid, State, EOpts) -> receive Msg -> - {'receive', State, Pid, ok, undefined, Opts} = Msg, + {'receive', State, Pid, ok, Opts} = Msg, check_opts(EOpts, Opts) end end, test('receive', Tc, Expect, false). +call(_Config) -> + + Self = self(), + Tc = fun(Pid) -> + Pid ! fun() -> call_test(Self), Self ! ok end, + receive ok -> ok after 100 -> ct:fail(timeout) end + end, + + erlang:trace_pattern({?MODULE, call_test, 1}, [], [local]), + + Expect = fun(Pid, State, EOpts) -> + receive + Msg -> + {call, State, Pid, {?MODULE, call_test, [Self]}, Opts} = Msg, + check_opts(EOpts, Opts) + end + end, + test(call, Tc, Expect). + +call_return(_Config) -> + + Self = self(), + Tc = fun(Pid) -> + Pid ! fun() -> call_test(undefined), Self ! ok end, + receive ok -> ok after 100 -> ct:fail(timeout) end + end, + + 1 = erlang:trace_pattern({?MODULE, call_test, 1}, [{'_',[],[{return_trace}]}], [local]), + + Expect = fun(Pid, State, EOpts) -> + receive + CallMsg -> + {call, State, Pid, {?MODULE, call_test, [undefined]}, COpts} = CallMsg, + check_opts(EOpts, COpts) + end, + receive + RetMsg -> + {return_from, State, Pid, {?MODULE, call_test, 1}, ROpts} = RetMsg, + check_opts(EOpts, ROpts, undefined) + end + end, + test(call, Tc, Expect). + +call_test(Arg) -> + Arg. + spawn(_Config) -> Tc = fun(Pid) -> @@ -256,9 +302,8 @@ spawn(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {spawn, State, Pid, NewPid, - {lists,seq,[1,10]}, Opts} = Msg, - check_opts(EOpts, Opts), + {spawn, State, Pid, NewPid, Opts} = Msg, + check_opts(EOpts, Opts, {lists,seq,[1,10]}), true = is_pid(NewPid) andalso NewPid /= Pid end end, @@ -274,7 +319,7 @@ exit(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {exit, State, Pid, normal, undefined, Opts} = Msg, + {exit, State, Pid, normal, Opts} = Msg, check_opts(EOpts, Opts) end end, @@ -295,7 +340,7 @@ link(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {link, State, Pid, NewPid, undefined, Opts} = Msg, + {link, State, Pid, NewPid, Opts} = Msg, check_opts(EOpts, Opts), true = is_pid(NewPid) andalso NewPid /= Pid end @@ -318,7 +363,7 @@ unlink(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {unlink, State, Pid, NewPid, undefined, Opts} = Msg, + {unlink, State, Pid, NewPid, Opts} = Msg, check_opts(EOpts, Opts), true = is_pid(NewPid) andalso NewPid /= Pid end @@ -340,7 +385,7 @@ getting_linked(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {getting_linked, State, Pid, NewPid, undefined, Opts} = Msg, + {getting_linked, State, Pid, NewPid, Opts} = Msg, check_opts(EOpts, Opts), true = is_pid(NewPid) andalso NewPid /= Pid end @@ -364,7 +409,7 @@ getting_unlinked(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {getting_unlinked, State, Pid, NewPid, undefined, Opts} = Msg, + {getting_unlinked, State, Pid, NewPid, Opts} = Msg, check_opts(EOpts, Opts), true = is_pid(NewPid) andalso NewPid /= Pid end @@ -386,7 +431,7 @@ register(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {register, State, Pid, ?MODULE, undefined, Opts} = Msg, + {register, State, Pid, ?MODULE, Opts} = Msg, check_opts(EOpts, Opts) end end, @@ -407,7 +452,7 @@ unregister(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {unregister, State, Pid, ?MODULE, undefined, Opts} = Msg, + {unregister, State, Pid, ?MODULE, Opts} = Msg, check_opts(EOpts, Opts) end end, @@ -427,8 +472,7 @@ in(_Config) -> N = (fun F(N) -> receive Msg -> - {in, State, Pid, _, - undefined, Opts} = Msg, + {in, State, Pid, _, Opts} = Msg, check_opts(EOpts, Opts), F(N+1) after 0 -> N @@ -452,8 +496,7 @@ out(_Config) -> N = (fun F(N) -> receive Msg -> - {out, State, Pid, _, - undefined, Opts} = Msg, + {out, State, Pid, _, Opts} = Msg, check_opts(EOpts, Opts), F(N+1) after 0 -> N @@ -477,7 +520,7 @@ gc_start(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {gc_major_start, State, Pid, _, undefined, Opts} = Msg, + {gc_major_start, State, Pid, _, Opts} = Msg, check_opts(EOpts, Opts) end end, @@ -497,7 +540,7 @@ gc_end(_Config) -> fun(Pid, State, EOpts) -> receive Msg -> - {gc_major_end, State, Pid, _, undefined, Opts} = Msg, + {gc_major_end, State, Pid, _, Opts} = Msg, check_opts(EOpts, Opts) end end, @@ -513,9 +556,7 @@ test(Event, TraceFlag, Tc, Expect, Removes) -> test(Event, TraceFlag, Tc, Expect, _Removes, Dies) -> ComplexState = {fun() -> ok end, <<0:(128*8)>>}, - Opts = #{ timestamp => undefined, - scheduler_id => undefined, - match_spec_result => true }, + Opts = #{ }, %% Test that trace works State1 = {#{ Event => trace }, self(), ComplexState}, @@ -540,8 +581,8 @@ test(Event, TraceFlag, Tc, Expect, _Removes, Dies) -> Tc(Pid1T), ok = trace_delivered(Pid1T), - Expect(Pid1T, State1, Opts#{ scheduler_id := number, - timestamp := timestamp}), + Expect(Pid1T, State1, Opts#{ scheduler_id => number, + timestamp => timestamp}), receive M11T -> ct:fail({unexpected, M11T}) after 0 -> ok end, if not Dies -> {flags, [scheduler_id, TraceFlag, timestamp]} @@ -568,6 +609,8 @@ test(Event, TraceFlag, Tc, Expect, _Removes, Dies) -> ok. +check_opts(E, O, Extra) -> + check_opts(E#{ extra => Extra }, O). check_opts(#{ scheduler_id := number } = E, #{ scheduler_id := N } = O) when is_integer(N) -> E1 = maps:remove(scheduler_id, E), diff --git a/erts/emulator/test/tracer_SUITE_data/tracer_test.c b/erts/emulator/test/tracer_SUITE_data/tracer_test.c index 908f35da9c..a26bb33600 100644 --- a/erts/emulator/test/tracer_SUITE_data/tracer_test.c +++ b/erts/emulator/test/tracer_SUITE_data/tracer_test.c @@ -36,7 +36,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ErlNifFunc nif_funcs[] = { {"enabled", 3, enabled}, - {"trace", 6, trace} + {"trace", 5, trace} }; ERL_NIF_INIT(tracer_test, nif_funcs, load, NULL, upgrade, unload) @@ -100,7 +100,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifPid self, to; ERL_NIF_TERM *tuple, msg; const ERL_NIF_TERM *state_tuple; - ASSERT(argc == 6); + ASSERT(argc == 5); enif_get_tuple(env, argv[1], &state_arity, &state_tuple); diff --git a/erts/emulator/test/tracer_test.erl b/erts/emulator/test/tracer_test.erl index d4778f4531..1da80bfe31 100644 --- a/erts/emulator/test/tracer_test.erl +++ b/erts/emulator/test/tracer_test.erl @@ -24,14 +24,14 @@ %%% Test tracer %%% --export([enabled/3, trace/6]). +-export([enabled/3, trace/5]). -export([load/1, load/2]). -on_load(load/0). enabled(_, _, _) -> erlang:nif_error(nif_not_loaded). -trace(_, _, _, _, _, _) -> +trace(_, _, _, _, _) -> erlang:nif_error(nif_not_loaded). load() -> diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam index 69804540c9..22286ed221 100644 Binary files a/erts/preloaded/ebin/erl_tracer.beam and b/erts/preloaded/ebin/erl_tracer.beam differ diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl index fe15812535..c810069d17 100644 --- a/erts/preloaded/src/erl_tracer.erl +++ b/erts/preloaded/src/erl_tracer.erl @@ -1,6 +1,6 @@ -module(erl_tracer). --export([enabled/3, trace/6, on_load/0]). +-export([enabled/3, trace/5, on_load/0]). -type tracee() :: port() | pid() | undefined. @@ -26,9 +26,9 @@ | trace_tag_running_ports() | trace_tag_gc(). --type trace_opts() :: #{ match_spec_result => true | term(), - scheduler_id => undefined | non_neg_integer(), - timestamp => undefined | timestamp | cpu_timestamp | +-type trace_opts() :: #{ extra => term(), match_spec_result => term(), + scheduler_id => non_neg_integer(), + timestamp => timestamp | cpu_timestamp | monotonic | strict_monotonic }. -type tracer_state() :: term(). @@ -41,6 +41,9 @@ on_load() -> %%% NIF placeholders %%% +%% This suppression is needed as trace_tag gets collapsed to atom() +-dialyzer({no_contracts, enabled/3}). + -spec enabled(Tag :: trace_status, TracerState :: tracer_state(), Tracee :: tracee()) -> @@ -56,8 +59,7 @@ enabled(_, _, _) -> TracerState :: tracer_state(), Tracee :: tracee(), Msg :: term(), - Extra :: term(), Opts :: trace_opts()) -> any(). -trace(_, _, _, _, _, _) -> +trace(_, _, _, _, _) -> erlang:nif_error(nif_not_loaded). -- cgit v1.2.3 From f9cb80861f169743a96099a06d68149a91f18dfa Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 20 May 2016 15:58:04 +0200 Subject: erts: Implement halt/0 and halt/1 in Erlang just to make things simpler. --- erts/emulator/beam/bif.c | 50 +------------------------------------------ erts/emulator/beam/bif.tab | 2 -- erts/preloaded/src/erlang.erl | 8 +++---- erts/preloaded/src/init.erl | 1 + 4 files changed, 5 insertions(+), 56 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 2a3bd4afe5..81477422ff 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3836,59 +3836,11 @@ BIF_RETTYPE display_nl_0(BIF_ALIST_0) /**********************************************************************/ -/* stop the system */ -/* ARGSUSED */ -BIF_RETTYPE halt_0(BIF_ALIST_0) -{ - VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt()\n")); - erts_halt(0); - ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined); -} - -/**********************************************************************/ #define HALT_MSG_SIZE 200 static char halt_msg[HALT_MSG_SIZE]; -/* stop the system with exit code */ -/* ARGSUSED */ -BIF_RETTYPE halt_1(BIF_ALIST_1) -{ - Uint code; - - if (term_to_Uint_mask(BIF_ARG_1, &code)) { - int pos_int_code = (int) (code & INT_MAX); - VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1)); - erts_halt(pos_int_code); - ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined); - } - else if (ERTS_IS_ATOM_STR("abort", BIF_ARG_1)) { - VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1)); - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_exit(ERTS_ABORT_EXIT, ""); - } - else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) { - Sint i; - - if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE-1)) < 0) { - goto error; - } - halt_msg[i] = '\0'; - VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1)); - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_exit(ERTS_DUMP_EXIT, "%s\n", halt_msg); - } - else - goto error; - return NIL; /* Pedantic (lint does not know about erts_exit) */ - error: - BIF_ERROR(BIF_P, BADARG); -} - -/**********************************************************************/ - /* stop the system with exit code and flags */ -/* ARGSUSED */ BIF_RETTYPE halt_2(BIF_ALIST_2) { Uint code; @@ -3924,7 +3876,7 @@ BIF_RETTYPE halt_2(BIF_ALIST_2) ("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2)); if (flush) { erts_halt(pos_int_code); - ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined); + ERTS_BIF_YIELD2(bif_export[BIF_halt_2], BIF_P, am_undefined, am_undefined); } else { erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 872f0f9b2a..065018514a 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -72,8 +72,6 @@ bif erlang:get/1 bif erlang:get_keys/1 bif erlang:group_leader/0 bif erlang:group_leader/2 -bif erlang:halt/0 -bif erlang:halt/1 bif erlang:halt/2 bif erlang:phash/2 bif erlang:phash2/1 diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 3d152c4e92..4c456bbed4 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -977,17 +977,15 @@ 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). + erlang:halt(0, []). %% halt/1 -%% Shadowed by erl_bif_types: erlang:halt/1 -spec halt(Status) -> no_return() when Status :: non_neg_integer() | 'abort' | string(). -halt(_Status) -> - erlang:nif_error(undefined). +halt(Status) -> + erlang:halt(Status, []). %% halt/2 %% Shadowed by erl_bif_types: erlang:halt/2 diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 04c5210aa3..e8f02f5056 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -316,6 +316,7 @@ limit_halt_string(String) -> %% List = [string() | atom() | pid() | number()] %% Any other items in List, such as tuples, are ignored when creating %% the string used as argument to erlang:halt/1. +-spec crash(_, _) -> no_return(). crash(String, List) -> halt(halt_string(String, List)). -- cgit v1.2.3 From dcaa52d75e3bcbc808696597a34f2fca5677fff9 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 20 May 2016 15:56:56 +0200 Subject: erts: Make erlang:halt/2 truncate string arg if too long. --- erts/doc/src/erlang.xml | 4 ++-- erts/emulator/beam/bif.c | 11 +++++++---- erts/emulator/beam/utils.c | 8 +++++--- erts/emulator/test/bif_SUITE.erl | 2 ++ 4 files changed, 16 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 1e251d33f3..665429d290 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1802,8 +1802,8 @@ os_prompt% string() An Erlang crash dump is produced with Status as slogan. Then the runtime system exits with status code 1. - Note that the string may not be longer than 200 characters and only - code points in the range 0-255 may be used. + Note that only code points in the range 0-255 may be used + and the string will be truncated if longer than 200 characters. abort diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 81477422ff..21763edc5b 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3838,7 +3838,7 @@ BIF_RETTYPE display_nl_0(BIF_ALIST_0) #define HALT_MSG_SIZE 200 -static char halt_msg[HALT_MSG_SIZE]; +static char halt_msg[HALT_MSG_SIZE+1]; /* stop the system with exit code and flags */ BIF_RETTYPE halt_2(BIF_ALIST_2) @@ -3892,9 +3892,12 @@ BIF_RETTYPE halt_2(BIF_ALIST_2) else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) { Sint i; - if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE-1)) < 0) { - goto error; - } + if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE)) == -1) { + goto error; + } + if (i == -2) /* truncated string */ + i = HALT_MSG_SIZE; + ASSERT(i >= 0 && i <= HALT_MSG_SIZE); halt_msg[i] = '\0'; VERBOSE(DEBUG_SYSTEM, ("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2)); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index cedc88e5fe..f0418446a8 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3893,8 +3893,10 @@ void bin_write(int to, void *to_arg, byte* buf, size_t sz) } /* Fill buf with the contents of bytelist list - return number of chars in list or -1 for error */ - + * return number of chars in list + * or -1 for type error + * or -2 for not enough buffer space (buffer contains truncated result) + */ Sint intlist_to_buf(Eterm list, char *buf, Sint len) { @@ -3917,7 +3919,7 @@ intlist_to_buf(Eterm list, char *buf, Sint len) return -1; listptr = list_val(*(listptr + 1)); } - return -1; /* not enough space */ + return -2; /* not enough space */ } /* diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 26bb416bf0..ec6cb6ab72 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -646,6 +646,8 @@ erlang_halt(Config) when is_list(Config) -> {badrpc,nodedown} = rpc:call(N2, erlang, halt, [0]), {ok,N3} = slave:start(H, halt_node3), {badrpc,nodedown} = rpc:call(N3, erlang, halt, [0,[]]), + {ok,N4} = slave:start(H, halt_node4), + {badrpc,nodedown} = rpc:call(N4, erlang, halt, [lists:duplicate(300,$x)]), % This test triggers a segfault when dumping a crash dump % to make sure that we can handle it properly. -- cgit v1.2.3 From e5b7f259be44efa45ec8ee3355577e691754cc63 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 20 May 2016 18:10:25 +0200 Subject: erts: Remove unnecessary halt string truncation as erlang:halt does it for us now --- erts/preloaded/src/init.erl | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index e8f02f5056..45468b3b9c 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -90,7 +90,6 @@ -define(ON_LOAD_HANDLER, init__boot__on_load_handler). --define(MAX_HALT_STRING_SIZE, 199). debug(false, _) -> ok; debug(_, T) -> erlang:display(T). @@ -180,7 +179,7 @@ stop(Status) when is_integer(Status), Status >= 0 -> stop(Status) when is_list(Status) -> case is_bytelist(Status) of true -> - stop_1(limit_halt_string(Status)); + stop_1(Status); false -> erlang:error(badarg) end; @@ -305,12 +304,7 @@ things_to_string([]) -> "". halt_string(String, List) -> - limit_halt_string(String ++ things_to_string(List)). - -limit_halt_string(String) when length(String) < ?MAX_HALT_STRING_SIZE -> - String; -limit_halt_string(String) -> - lists:sublist(String, ?MAX_HALT_STRING_SIZE-1). + String ++ things_to_string(List). %% String = string() %% List = [string() | atom() | pid() | number()] -- cgit v1.2.3 From 0f8fab10dad03273b75f205bd9fae746d2077829 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 20 May 2016 19:08:10 +0200 Subject: erts: Update preloaded --- erts/preloaded/ebin/erlang.beam | Bin 104620 -> 104656 bytes erts/preloaded/ebin/init.beam | Bin 49924 -> 50052 bytes 2 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 8379bf1768..581ab511d8 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 7b5797e90a..6fc95b914e 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ -- cgit v1.2.3 From 36f98375d57daaba3fec42bb91482cdac9ef4cc9 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 25 May 2016 16:15:36 +0200 Subject: Remove the 'message_queue_data' option 'mixed' --- erts/doc/src/erl.xml | 4 +-- erts/doc/src/erlang.xml | 11 ++----- erts/emulator/beam/atom.names | 1 - erts/emulator/beam/bif.c | 3 -- erts/emulator/beam/erl_bif_info.c | 5 --- erts/emulator/beam/erl_init.c | 6 ++-- erts/emulator/beam/erl_message.c | 28 +--------------- erts/emulator/beam/erl_process.c | 9 ++++- erts/emulator/test/message_queue_data_SUITE.erl | 42 +++++------------------- erts/preloaded/ebin/erlang.beam | Bin 104620 -> 104616 bytes erts/preloaded/src/erlang.erl | 2 +- 11 files changed, 25 insertions(+), 86 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 1bbde7f1e0..2fae3bfb1f 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -660,11 +660,11 @@

Sets the initial process dictionary size of processes to the size .

- +hmqd off_heap|on_heap|mixed + +hmqd off_heap|on_heap

Sets the default value for the process flag message_queue_data. If +hmqd is not - passed, mixed will be the default. For more information, + passed, on_heap will be the default. For more information, see the documentation of process_flag(message_queue_data, MQD). diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 9287b32fec..ddde1c96c4 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4449,11 +4449,6 @@ os_prompt% off heap. This is how messages always have been stored up until ERTS version 8.0.

- mixed -

- Messages may be placed either on the heap or outside - of the heap. -

The default message_queue_data process flag is determined @@ -4877,7 +4872,7 @@ os_prompt%

Returns the current state of the message_queue_data process flag. MQD is either off_heap, - on_heap, or mixed. For more information, see the + or on_heap. For more information, see the documentation of process_flag(message_queue_data, MQD).

@@ -5808,7 +5803,7 @@ true

Sets the state of the message_queue_data process flag. MQD should be either off_heap, - on_heap, or mixed. The default + or on_heap. The default message_queue_data process flag is determined by the +hmqd erl command line argument. For more information, see the @@ -7164,7 +7159,7 @@ ok message_queue_data

Returns the default value of the message_queue_data - process flag which is either off_heap, on_heap, or mixed. + process flag which is either off_heap, or on_heap. This default is set by the erl command line argument +hmqd. For more information on the message_queue_data process flag, see documentation of diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 8f65e71531..464058535a 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -389,7 +389,6 @@ atom min_heap_size atom min_bin_vheap_size atom minor_version atom Minus='-' -atom mixed atom module atom module_info atom monitored_by diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 2a3bd4afe5..e77da526fd 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -917,9 +917,6 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1) goto error; } else if (arg == am_message_queue_data) { switch (val) { - case am_mixed: - so.flags &= ~(SPO_OFF_HEAP_MSGQ|SPO_ON_HEAP_MSGQ); - break; case am_on_heap: so.flags &= ~SPO_OFF_HEAP_MSGQ; so.flags |= SPO_ON_HEAP_MSGQ; diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 2e195db0ee..b410578d37 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1565,9 +1565,6 @@ process_info_aux(Process *BIF_P, case F_ON_HEAP_MSGQ: res = am_on_heap; break; - case 0: - res = am_mixed; - break; default: res = am_error; ERTS_INTERNAL_ERROR("Inconsistent message queue management state"); @@ -2809,8 +2806,6 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(am_off_heap); case SPO_ON_HEAP_MSGQ: BIF_RET(am_on_heap); - case 0: - BIF_RET(am_mixed); default: ERTS_INTERNAL_ERROR("Inconsistent message queue management state"); BIF_RET(am_error); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 0649fb68de..fbdafec4ef 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -585,7 +585,7 @@ void erts_usage(void) erts_fprintf(stderr, "-hpds size initial process dictionary size (default %d)\n", erts_pd_initial_size); erts_fprintf(stderr, "-hmqd val set default message queue data flag for processes,\n"); - erts_fprintf(stderr, " valid values are: off_heap | on_heap | mixed\n"); + erts_fprintf(stderr, " valid values are: off_heap | on_heap\n"); /* erts_fprintf(stderr, "-i module set the boot module (default init)\n"); */ @@ -1526,9 +1526,7 @@ erl_start(int argc, char **argv) erts_pd_initial_size)); } else if (has_prefix("mqd", sub_param)) { arg = get_arg(sub_param+3, argv[i+1], &i); - if (sys_strcmp(arg, "mixed") == 0) - erts_default_spo_flags &= ~(SPO_ON_HEAP_MSGQ|SPO_OFF_HEAP_MSGQ); - else if (sys_strcmp(arg, "on_heap") == 0) { + if (sys_strcmp(arg, "on_heap") == 0) { erts_default_spo_flags &= ~SPO_OFF_HEAP_MSGQ; erts_default_spo_flags |= SPO_ON_HEAP_MSGQ; } diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 579f6e427d..d82532b354 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1123,11 +1123,9 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state) break; case am_on_heap: c_p->flags |= F_ON_HEAP_MSGQ; + c_p->flags &= ~F_OFF_HEAP_MSGQ; erts_smp_atomic32_read_bor_nob(&c_p->state, ERTS_PSFLG_ON_HEAP_MSGQ); - /* fall through */ - case am_mixed: - c_p->flags &= ~F_OFF_HEAP_MSGQ; /* * We are not allowed to clear ERTS_PSFLG_OFF_HEAP_MSGQ * if a off heap change is ongoing. It will be adjusted @@ -1151,11 +1149,6 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state) switch (new_state) { case am_on_heap: break; - case am_mixed: - c_p->flags &= ~F_ON_HEAP_MSGQ; - erts_smp_atomic32_read_band_nob(&c_p->state, - ~ERTS_PSFLG_ON_HEAP_MSGQ); - break; case am_off_heap: c_p->flags &= ~F_ON_HEAP_MSGQ; erts_smp_atomic32_read_band_nob(&c_p->state, @@ -1167,25 +1160,6 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state) } break; - case 0: - res = am_mixed; - - switch (new_state) { - case am_mixed: - break; - case am_on_heap: - c_p->flags |= F_ON_HEAP_MSGQ; - erts_smp_atomic32_read_bor_nob(&c_p->state, - ERTS_PSFLG_ON_HEAP_MSGQ); - break; - case am_off_heap: - goto change_to_off_heap; - default: - res = THE_NON_VALUE; /* badarg */ - break; - } - break; - default: res = am_error; ERTS_INTERNAL_ERROR("Inconsistent message queue management state"); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a853ec585b..f8cbe60e76 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -149,7 +149,7 @@ extern BeamInstr beam_apply[]; extern BeamInstr beam_exit[]; extern BeamInstr beam_continue_exit[]; -int ERTS_WRITE_UNLIKELY(erts_default_spo_flags) = 0; +int ERTS_WRITE_UNLIKELY(erts_default_spo_flags) = SPO_ON_HEAP_MSGQ; int ERTS_WRITE_UNLIKELY(erts_eager_check_io) = 1; int ERTS_WRITE_UNLIKELY(erts_sched_compact_load); int ERTS_WRITE_UNLIKELY(erts_sched_balance_util) = 0; @@ -11206,6 +11206,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). flags |= F_ON_HEAP_MSGQ; } + ASSERT((flags & F_ON_HEAP_MSGQ) || (flags & F_OFF_HEAP_MSGQ)); + if (!rq) rq = erts_get_runq_proc(parent); @@ -11218,6 +11220,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). goto error; } + ASSERT((erts_smp_atomic32_read_nob(&p->state) + & ERTS_PSFLG_ON_HEAP_MSGQ) + || (erts_smp_atomic32_read_nob(&p->state) + & ERTS_PSFLG_OFF_HEAP_MSGQ)); + #ifdef BM_COUNTERS processes_busy++; #endif diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl index 226462676c..44e77dfad0 100644 --- a/erts/emulator/test/message_queue_data_SUITE.erl +++ b/erts/emulator/test/message_queue_data_SUITE.erl @@ -52,18 +52,12 @@ basic(Config) when is_list(Config) -> ok = rpc:call(Node2, ?MODULE, basic_test, [on_heap]), stop_node(Node2), - {ok, Node3} = start_node(Config, "+hmqd mixed"), - ok = rpc:call(Node3, ?MODULE, basic_test, [mixed]), - stop_node(Node3), - ok. is_valid_mqd_value(off_heap) -> true; is_valid_mqd_value(on_heap) -> true; -is_valid_mqd_value(mixed) -> - true; is_valid_mqd_value(_) -> false. @@ -78,9 +72,6 @@ basic_test(Default) -> {message_queue_data, off_heap} = process_info(self(), message_queue_data), off_heap = process_flag(message_queue_data, on_heap), {message_queue_data, on_heap} = process_info(self(), message_queue_data), - on_heap = process_flag(message_queue_data, mixed), - {message_queue_data, mixed} = process_info(self(), message_queue_data), - mixed = process_flag(message_queue_data, Default), {'EXIT', _} = (catch process_flag(message_queue_data, blupp)), P1 = spawn_opt(fun () -> receive after infinity -> ok end end, @@ -101,12 +92,6 @@ basic_test(Default) -> unlink(P3), exit(P3, bye), - P4 = spawn_opt(fun () -> receive after infinity -> ok end end, - [link, {message_queue_data, mixed}]), - {message_queue_data, mixed} = process_info(P4, message_queue_data), - unlink(P4), - exit(P4, bye), - {'EXIT', _} = (catch spawn_opt(fun () -> receive after infinity -> ok end end, [link, {message_queue_data, blapp}])), @@ -116,21 +101,18 @@ process_info_messages(Config) when is_list(Config) -> Tester = self(), P1 = spawn_opt(fun () -> receive after 500 -> ok end, - mixed = process_flag(message_queue_data, off_heap), + on_heap = process_flag(message_queue_data, off_heap), Tester ! first, receive after 500 -> ok end, off_heap = process_flag(message_queue_data, on_heap), Tester ! second, receive after 500 -> ok end, - on_heap = process_flag(message_queue_data, mixed), + on_heap = process_flag(message_queue_data, off_heap), Tester ! third, - receive after 500 -> ok end, - mixed = process_flag(message_queue_data, off_heap), - Tester ! fourth, receive after infinity -> ok end end, - [link, {message_queue_data, mixed}]), + [link, {message_queue_data, on_heap}]), P1 ! "A", receive first -> ok end, @@ -139,25 +121,20 @@ process_info_messages(Config) when is_list(Config) -> P1 ! "C", receive third -> ok end, P1 ! "D", - receive fourth -> ok end, - P1 ! "E", - {messages, ["A", "B", "C", "D", "E"]} = process_info(P1, messages), + {messages, ["A", "B", "C", "D"]} = process_info(P1, messages), P2 = spawn_opt(fun () -> receive after 500 -> ok end, - mixed = process_flag(message_queue_data, off_heap), + on_heap = process_flag(message_queue_data, off_heap), Tester ! first, receive after 500 -> ok end, off_heap = process_flag(message_queue_data, on_heap), Tester ! second, receive after 500 -> ok end, - on_heap = process_flag(message_queue_data, mixed), + on_heap = process_flag(message_queue_data, off_heap), Tester ! third, receive after 500 -> ok end, - mixed = process_flag(message_queue_data, off_heap), - Tester ! fourth, - receive after 500 -> ok end, Tester ! process_info(self(), messages), @@ -165,11 +142,10 @@ process_info_messages(Config) when is_list(Config) -> receive M2 -> M2 = "B" end, receive M3 -> M3 = "C" end, receive M4 -> M4 = "D" end, - receive M5 -> M5 = "E" end, Tester ! self() end, - [link, {message_queue_data, mixed}]), + [link, {message_queue_data, on_heap}]), P2 ! "A", receive first -> ok end, @@ -178,12 +154,10 @@ process_info_messages(Config) when is_list(Config) -> P2 ! "C", receive third -> ok end, P2 ! "D", - receive fourth -> ok end, - P2 ! "E", receive Msg -> - {messages, ["A", "B", "C", "D", "E"]} = Msg + {messages, ["A", "B", "C", "D"]} = Msg end, receive P2 -> ok end, diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 8379bf1768..12262bb5b0 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 3d152c4e92..f3f8696f32 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2059,7 +2059,7 @@ open_port(PortName, PortSettings) -> low | normal | high | max. -type message_queue_data() :: - off_heap | on_heap | mixed. + off_heap | on_heap. -spec process_flag(trap_exit, Boolean) -> OldBoolean when Boolean :: boolean(), -- cgit v1.2.3 From 34f853950685e4e7ab38f30fc3f17a6beac13349 Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Wed, 20 Apr 2016 14:40:39 +0100 Subject: Add -start_epmd command line option Add a command line option that lets you disable automatic starting of epmd when starting a distributed node. This differs from the undocumented setting -no_epmd, in that it does not affect the starting of an erl_epmd process within erl_distribution: the newly started node will expect an epmd instance to have been started previously. --- erts/doc/src/erl.xml | 18 +++++++++++++++++- erts/etc/common/erlexec.c | 21 ++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 1bbde7f1e0..e63928ddb0 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -338,7 +338,8 @@ net_kernel(3). It is also ensured that runs on the current host before Erlang is started. See - epmd(1).

+ epmd(1) and the + -start_epmd option.

The name of the node will be , where is the fully qualified host name of the current host. For short names, use the flag instead.

@@ -463,6 +464,21 @@ flag and those running with the flag, as node names must be unique in distributed Erlang systems.

+ -start_epmd true | false + + +

Specifies whether Erlang should start + epmd on startup. By default + this is true, but if you prefer to start epmd + manually, set this to false.

+ +

This only applies if Erlang is started as a distributed node, + i.e. if -name or -sname is specified. Otherwise, + epmd is not started even if -start_epmd true is given.

+ +

Note that a distributed node will fail to start if epmd is + not running.

+

-smp enable and -smp starts the Erlang runtime diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 42da05b1f7..2b2e0e480a 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -195,6 +195,7 @@ static char *plusz_val_switches[] = { #endif void usage(const char *switchname); +static void usage_format(char *format, ...); void start_epmd(char *epmd); void error(char* format, ...); @@ -795,6 +796,24 @@ int main(int argc, char **argv) get_start_erl_data((char *) NULL); } #endif + else if (strcmp(argv[i], "-start_epmd") == 0) { + if (i+1 >= argc) + usage("-start_epmd"); + + if (strcmp(argv[i+1], "true") == 0) { + /* The default */ + no_epmd = 0; + } + else if (strcmp(argv[i+1], "false") == 0) { + no_epmd = 1; + } + else + usage_format("Expected boolean argument for \'-start_epmd\'.\n"); + + add_arg(argv[i]); + add_arg(argv[i+1]); + i++; + } else add_arg(argv[i]); @@ -1173,7 +1192,7 @@ usage_aux(void) "]" #endif "] " - "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] " + "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] [-start_epmd BOOLEAN] " "[-args_file FILENAME] [+A THREADS] [+a SIZE] [+B[c|d|i]] [+c [BOOLEAN]] " "[+C MODE] [+h HEAP_SIZE_OPTION] [+K BOOLEAN] " "[+l] [+M ] [+P MAX_PROCS] [+Q MAX_PORTS] " -- cgit v1.2.3 From 252c60632664fdf54395d54ad8d7b7e9e6e74cca Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Wed, 20 Apr 2016 17:20:05 +0100 Subject: Use the -epmd_module flag consistently If the -epmd_module flag has been specified on the command line, use that module to register and look up node names instead of the default, erl_epmd. Also document this option. --- erts/doc/src/init.xml | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index 84a5aea335..878a33c9ca 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -241,6 +241,11 @@ marker="kernel:code">code(3).

+ -epmd_module Module + +

Specifies the module to use for registration and lookup of + node names. Defaults to erl_epmd.

+
-eval Expr

Scans, parses and evaluates an arbitrary expression -- cgit v1.2.3 From 8ff06daa90c7c3c599c9e8cbc93fe98c2ed5ebfa Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Wed, 25 May 2016 17:35:11 +0100 Subject: Add tests for -start_epmd and -epmd_module options For -start_epmd, check that the node starts properly and that we can ping it. For -epmd_module, start two nodes that can communicate only because we supply the correct port number through a command line argument. --- erts/emulator/test/distribution_SUITE.erl | 69 ++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index d0096fb1bc..26780f6017 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -55,7 +55,8 @@ bad_dist_ext_receive/1, bad_dist_ext_process_info/1, bad_dist_ext_control/1, - bad_dist_ext_connection_id/1]). + bad_dist_ext_connection_id/1, + start_epmd_false/1, epmd_module/1]). %% Internal exports. -export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0, @@ -64,6 +65,9 @@ dist_evil_parallel_receiver/0, sendersender/4, sendersender2/4]). +%% epmd_module exports +-export([start_link/0, register_node/2, port_please/2]). + suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 4}}]. @@ -76,7 +80,8 @@ all() -> {group, trap_bif}, {group, dist_auto_connect}, dist_parallel_send, atom_roundtrip, unicode_atom_roundtrip, atom_roundtrip_r15b, contended_atom_cache_entry, contended_unicode_atom_cache_entry, - bad_dist_structure, {group, bad_dist_ext}]. + bad_dist_structure, {group, bad_dist_ext}, + start_epmd_false, epmd_module]. groups() -> [{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]}, @@ -1881,6 +1886,66 @@ dmsg_ext(Term) -> dmsg_bad_atom_cache_ref() -> [$R, 137]. +start_epmd_false(Config) when is_list(Config) -> + %% Start a node with the option -start_epmd false. + {ok, OtherNode} = start_node(start_epmd_false, "-start_epmd false"), + %% We should be able to ping it, as epmd was started by us: + pong = net_adm:ping(OtherNode), + stop_node(OtherNode), + + ok. + +epmd_module(Config) when is_list(Config) -> + %% We need a relay node to test this, since the test node uses the + %% standard epmd module. + Sock1 = start_relay_node(epmd_module_node1, "-epmd_module " ++ ?MODULE_STRING), + Node1 = inet_rpc_nodename(Sock1), + %% Ask what port it's listening on - it won't have registered with + %% epmd. + {ok, {ok, Port1}} = do_inet_rpc(Sock1, application, get_env, [kernel, dist_listen_port]), + + %% Start a second node, passing the port number as a secret + %% argument. + Sock2 = start_relay_node(epmd_module_node2, "-epmd_module " ++ ?MODULE_STRING + ++ " -other_node_port " ++ integer_to_list(Port1)), + Node2 = inet_rpc_nodename(Sock2), + %% Node 1 can't ping node 2 + {ok, pang} = do_inet_rpc(Sock1, net_adm, ping, [Node2]), + {ok, []} = do_inet_rpc(Sock1, erlang, nodes, []), + {ok, []} = do_inet_rpc(Sock2, erlang, nodes, []), + %% But node 2 can ping node 1 + {ok, pong} = do_inet_rpc(Sock2, net_adm, ping, [Node1]), + {ok, [Node2]} = do_inet_rpc(Sock1, erlang, nodes, []), + {ok, [Node1]} = do_inet_rpc(Sock2, erlang, nodes, []), + + stop_relay_node(Sock2), + stop_relay_node(Sock1). + +%% epmd_module functions: + +start_link() -> + ignore. + +register_node(_Name, Port) -> + %% Save the port number we're listening on. + application:set_env(kernel, dist_listen_port, Port), + Creation = rand:uniform(3), + {ok, Creation}. + +port_please(_Name, _Ip) -> + case init:get_argument(other_node_port) of + error -> + %% None specified. Default to 42. + Port = 42, + Version = 5, + {port, Port, Version}; + {ok, [[PortS]]} -> + %% Port number given on command line. + Port = list_to_integer(PortS), + Version = 5, + {port, Port, Version} + end. + %%% Utilities timestamp() -> -- cgit v1.2.3 From 50447f0bfa84046961cdc7693f7c374f5b81d461 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 26 May 2016 12:13:19 +0200 Subject: erts: Improve some bad hash functions Multiplying two atoms will always yield the same low 6 bits. The most important tabke 'export' was saved by the prime number table size which seemed to yield a decent uniform distribution anyway. --- erts/emulator/beam/export.c | 2 +- erts/emulator/hipe/hipe_bif0.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c index 02c24557c1..cfe7c06823 100644 --- a/erts/emulator/beam/export.c +++ b/erts/emulator/beam/export.c @@ -31,7 +31,7 @@ #define EXPORT_INITIAL_SIZE 4000 #define EXPORT_LIMIT (512*1024) -#define EXPORT_HASH(m,f,a) ((m)*(f)+(a)) +#define EXPORT_HASH(m,f,a) ((atom_val(m) * atom_val(f)) ^ (a)) #ifdef DEBUG # define IF_DEBUG(x) x diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 4063cbf306..307d2424ce 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -693,7 +693,7 @@ static struct nbif nbifs[BIF_SIZE] = { #undef BIF_LIST }; -#define NBIF_HASH(m,f,a) ((m)*(f)+(a)) +#define NBIF_HASH(m,f,a) (atom_val(m) ^ atom_val(f) ^ (a)) static Hash nbif_table; static HashValue nbif_hash(struct nbif *x) @@ -1059,7 +1059,7 @@ static inline void hipe_mfa_info_table_rwunlock(void) erts_smp_rwmtx_rwunlock(&hipe_mfa_info_table.lock); } -#define HIPE_MFA_HASH(M,F,A) ((M) * (F) + (A)) +#define HIPE_MFA_HASH(M,F,A) (atom_val(M) ^ atom_val(F) ^ (A)) static struct hipe_mfa_info **hipe_mfa_info_table_alloc_bucket(unsigned int size) { -- cgit v1.2.3 From a3fcb808a48a8bbcc785edd06d68b7a1fe079c8d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 26 May 2016 12:13:50 +0200 Subject: erts: Improve bucket search for hipe_mfa_info by actually using the cached hash value. --- erts/emulator/hipe/hipe_bif0.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 307d2424ce..918cdd61e6 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1140,10 +1140,13 @@ static inline struct hipe_mfa_info *hipe_mfa_info_table_get_locked(Eterm m, Eter h = HIPE_MFA_HASH(m, f, arity); i = h & hipe_mfa_info_table.mask; p = hipe_mfa_info_table.bucket[i]; - for (; p; p = p->bucket.next) - /* XXX: do we want to compare p->bucket.hvalue as well? */ - if (p->m == m && p->f == f && p->a == arity) - return p; + for (; p; p = p->bucket.next) { + if (p->bucket.hvalue == h) { + if (p->m == m && p->f == f && p->a == arity) + return p; + } + else ASSERT(!(p->m == m && p->f == f && p->a == arity)); + } return NULL; } @@ -1167,10 +1170,13 @@ static struct hipe_mfa_info *hipe_mfa_info_table_put_rwlocked(Eterm m, Eterm f, h = HIPE_MFA_HASH(m, f, arity); i = h & hipe_mfa_info_table.mask; p = hipe_mfa_info_table.bucket[i]; - for (; p; p = p->bucket.next) - /* XXX: do we want to compare p->bucket.hvalue as well? */ - if (p->m == m && p->f == f && p->a == arity) - return p; + for (; p; p = p->bucket.next) { + if (p->bucket.hvalue == h) { + if (p->m == m && p->f == f && p->a == arity) + return p; + } + else ASSERT(!(p->m == m && p->f == f && p->a == arity)); + } p = hipe_mfa_info_table_alloc(m, f, arity); p->bucket.hvalue = h; p->bucket.next = hipe_mfa_info_table.bucket[i]; -- cgit v1.2.3 From 17cca30753584021dc1c518fffa42f4629a73775 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 26 May 2016 11:34:43 +0200 Subject: erts: Tweak ErtsContainerStruct macro with surrounding parenthesis --- erts/emulator/beam/sys.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index f303d4f167..9a205d50d3 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -97,7 +97,7 @@ ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes)) #define ErtsContainerStruct(ptr, type, member) \ - (type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member)) + ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member))) #if defined (__WIN32__) # include "erl_win_sys.h" -- cgit v1.2.3 From 770442cd4775a9bc56b3d0476754d8ea91ca2e2f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 25 May 2016 20:38:03 +0200 Subject: erts: Add some more use of ErtsContainerStruct --- erts/emulator/beam/erl_ao_firstfit_alloc.c | 2 +- erts/emulator/beam/export.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c index fbe4724047..7e239d1f5d 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.c +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c @@ -123,7 +123,7 @@ struct AOFF_Carrier_t_ { AOFF_RBTree_t rbt_node; /* My node in the carrier tree */ AOFF_RBTree_t* root; /* Root of my block tree */ }; -#define RBT_NODE_TO_MBC(PTR) ((AOFF_Carrier_t*)((char*)(PTR) - offsetof(AOFF_Carrier_t, rbt_node))) +#define RBT_NODE_TO_MBC(PTR) ErtsContainerStruct((PTR), AOFF_Carrier_t, rbt_node) /* To support carrier migration we keep two kinds of rb-trees: diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c index cfe7c06823..2a19211987 100644 --- a/erts/emulator/beam/export.c +++ b/erts/emulator/beam/export.c @@ -79,8 +79,7 @@ struct export_templ static struct export_blob* entry_to_blob(struct export_entry* ee) { - return (struct export_blob*) - ((char*)ee->ep - offsetof(struct export_blob,exp)); + return ErtsContainerStruct(ee->ep, struct export_blob, exp); } void -- cgit v1.2.3 From 8241eb36a733d6fee7c4a6f12c8bdfc206889795 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 26 May 2016 16:17:45 +0200 Subject: Fix message queue update on replacement and removal of message --- erts/emulator/beam/erl_gc.c | 15 +++++---------- erts/emulator/beam/erl_message.c | 15 +++++---------- erts/emulator/beam/erl_message.h | 29 +++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 374da9407c..c7bbbd5ca0 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2293,18 +2293,13 @@ move_msgq_to_heap(Process *p) free_message_buffer(bp); } else { - ErtsMessage *tmp = erts_alloc_message(0, NULL); - sys_memcpy((void *) tmp->m, (void *) mp->m, - sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ); - tmp->next = mp->next; - if (p->msg.save == &mp->next) - p->msg.save = &tmp->next; - if (p->msg.last == &mp->next) - p->msg.last = &tmp->next; - *mpp = tmp; + ErtsMessage *new_mp = erts_alloc_message(0, NULL); + sys_memcpy((void *) new_mp->m, (void *) mp->m, + sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ); + erts_msgq_replace_msg_ref(&p->msg, new_mp, mpp); mp->next = NULL; erts_cleanup_messages(mp); - mp = tmp; + mp = new_mp; } } diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 579f6e427d..b761522d91 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1371,10 +1371,10 @@ erts_prep_msgq_for_inspection(Process *c_p, Process *rp, mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next; - if (rp->msg.save == &bad_mp->next) - rp->msg.save = mpp; - if (rp->msg.last == &bad_mp->next) - rp->msg.last = mpp; + ASSERT((*mpp)->next == bad_mp); + + erts_msgq_update_internal_pointers(&rp->msg, mpp, &bad_mp->next); + mp = mp->next; *mpp = mp; rp->msg.len--; @@ -1411,12 +1411,7 @@ erts_prep_msgq_for_inspection(Process *c_p, Process *rp, sys_memcpy((void *) tmp->m, (void *) mp->m, sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ); mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next; - tmp->next = mp->next; - if (rp->msg.save == &mp->next) - rp->msg.save = &tmp->next; - if (rp->msg.last == &mp->next) - rp->msg.last = &tmp->next; - *mpp = tmp; + erts_msgq_replace_msg_ref(&rp->msg, tmp, mpp); erts_save_message_in_proc(rp, mp); mp = tmp; } diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 46063ea0c2..6df969367b 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -366,6 +366,12 @@ ERTS_GLB_FORCE_INLINE ErtsMessage *erts_shrink_message(ErtsMessage *mp, Uint sz, ERTS_GLB_FORCE_INLINE void erts_free_message(ErtsMessage *mp); ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment*); ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg); +ERTS_GLB_INLINE void erts_msgq_update_internal_pointers(ErlMessageQueue *msgq, + ErtsMessage **newpp, + ErtsMessage **oldpp); +ERTS_GLB_INLINE void erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, + ErtsMessage *newp, + ErtsMessage **oldpp); #define ERTS_MSG_COMBINED_HFRAG ((void *) 0x1) @@ -468,6 +474,29 @@ ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg) return sz; } } + +ERTS_GLB_INLINE void +erts_msgq_update_internal_pointers(ErlMessageQueue *msgq, + ErtsMessage **newpp, + ErtsMessage **oldpp) +{ + if (msgq->save == oldpp) + msgq->save = newpp; + if (msgq->last == oldpp) + msgq->last = newpp; + if (msgq->saved_last == oldpp) + msgq->saved_last = newpp; +} + +ERTS_GLB_INLINE void +erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, ErtsMessage *newp, ErtsMessage **oldpp) +{ + ErtsMessage *oldp = *oldpp; + newp->next = oldp->next; + erts_msgq_update_internal_pointers(msgq, &newp->next, &oldp->next); + *oldpp = newp; +} + #endif #endif -- cgit v1.2.3 From 20383144c26512fe33e72f49dff52dc628666923 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 26 May 2016 16:23:24 +0200 Subject: Improve message allocation in enif_send() --- erts/emulator/beam/erl_nif.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 606b73c7b5..2bbb8e3c91 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -623,10 +623,28 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, } } else { Uint sz = size_object(msg); + ErlOffHeap *ohp; Eterm *hp; - mp = erts_alloc_message(sz, &hp); - msg = copy_struct(msg, sz, &hp, &mp->hfrag.off_heap); - ASSERT(hp == mp->hfrag.mem+mp->hfrag.used_size); + if (env && !env->tracee) { + flush_env(env); + mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp); + cache_env(env); + } + else { + erts_aint_t state = erts_smp_atomic32_read_nob(&rp->state); + if (state & ERTS_PSFLG_OFF_HEAP_MSGQ) { + mp = erts_alloc_message(sz, &hp); + ohp = sz == 0 ? NULL : &mp->hfrag.off_heap; + } + else { + ErlHeapFragment *bp = new_message_buffer(sz); + mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = bp; + hp = bp->mem; + ohp = &bp->off_heap; + } + } + msg = copy_struct(msg, sz, &hp, ohp); } ERL_MESSAGE_TERM(mp) = msg; -- cgit v1.2.3 From 05badf19300e2970cdeb914a62b2c2b424fe7711 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 20 May 2016 17:45:56 +0200 Subject: erts: Split large binaries into multiple iovec On windows the max size of an iov element is long, i.e. 4GB so in order to write larger binaries to file we split the binary into smaller 2GB chunks so that the write is possible. --- erts/emulator/beam/io.c | 59 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 20 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 0377f6cb5e..01df5476db 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -938,18 +938,32 @@ int erts_port_handle_xports(Port *prt) ** -2 on type error */ -#define SET_VEC(iov, bv, bin, ptr, len, vlen) do { \ - (iov)->iov_base = (ptr); \ - (iov)->iov_len = (len); \ - if (sizeof((iov)->iov_len) < sizeof(len) \ - /* Check if (len) overflowed (iov)->iov_len */ \ - && (iov)->iov_len != (len)) { \ - goto L_overflow; \ - } \ - *(bv)++ = (bin); \ - (iov)++; \ - (vlen)++; \ -} while(0) +#ifdef DEBUG +#define MAX_SYSIOVEC_IOVLEN (1ull << (32 - 1)) +#else +#define MAX_SYSIOVEC_IOVLEN (1ull << (sizeof(((SysIOVec*)0)->iov_len) * 8 - 1)) +#endif + +static ERTS_INLINE void +io_list_to_vec_set_vec(SysIOVec **iov, ErlDrvBinary ***binv, + ErlDrvBinary *bin, byte *ptr, Uint len, + int *vlen) +{ + while (len > MAX_SYSIOVEC_IOVLEN) { + (*iov)->iov_base = ptr; + (*iov)->iov_len = MAX_SYSIOVEC_IOVLEN; + ptr += MAX_SYSIOVEC_IOVLEN; + len -= MAX_SYSIOVEC_IOVLEN; + (*iov)++; + (*vlen)++; + *(*binv)++ = bin; + } + (*iov)->iov_base = ptr; + (*iov)->iov_len = len; + *(*binv)++ = bin; + (*iov)++; + (*vlen)++; +} static int io_list_to_vec(Eterm obj, /* io-list */ @@ -960,11 +974,11 @@ io_list_to_vec(Eterm obj, /* io-list */ { DECLARE_ESTACK(s); Eterm* objp; - char *buf = cbin->orig_bytes; + byte *buf = (byte*)cbin->orig_bytes; Uint len = cbin->orig_size; Uint csize = 0; int vlen = 0; - char* cptr = buf; + byte* cptr = buf; goto L_jump_start; /* avoid push */ @@ -1032,15 +1046,17 @@ io_list_to_vec(Eterm obj, /* io-list */ len -= size; } else { if (csize != 0) { - SET_VEC(iov, binv, cbin, cptr, csize, vlen); + io_list_to_vec_set_vec(&iov, &binv, cbin, + cptr, csize, &vlen); cptr = buf; csize = 0; } if (pb->flags) { erts_emasculate_writable_binary(pb); } - SET_VEC(iov, binv, Binary2ErlDrvBinary(pb->val), - pb->bytes+offset, size, vlen); + io_list_to_vec_set_vec( + &iov, &binv, Binary2ErlDrvBinary(pb->val), + pb->bytes+offset, size, &vlen); } } else { ErlHeapBin* hb = (ErlHeapBin *) bptr; @@ -1060,7 +1076,7 @@ io_list_to_vec(Eterm obj, /* io-list */ } if (csize != 0) { - SET_VEC(iov, binv, cbin, cptr, csize, vlen); + io_list_to_vec_set_vec(&iov, &binv, cbin, cptr, csize, &vlen); } DESTROY_ESTACK(s); @@ -1086,10 +1102,13 @@ do { \ if (_bitsize != 0) goto L_type_error; \ if (thing_subtag(*binary_val(_real)) == REFC_BINARY_SUBTAG && \ _bitoffs == 0) { \ - b_size += _size; \ + b_size += _size; \ if (b_size < _size) goto L_overflow_error; \ in_clist = 0; \ - v_size++; \ + v_size++; \ + /* If iov_len is smaller then Uint we split the binary into*/ \ + /* multiple smaller (2GB) elements in the iolist.*/ \ + v_size += _size / MAX_SYSIOVEC_IOVLEN; \ if (_size >= ERL_SMALL_IO_BIN_LIMIT) { \ p_in_clist = 0; \ p_v_size++; \ -- cgit v1.2.3 From 4a2b7cd9a8cab8e8a6842f7d08ddc36613eba678 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 25 May 2016 17:05:49 +0200 Subject: Minor doc fix --- erts/doc/src/erl_nif.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 33a4fee182..57e047af08 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -295,14 +295,14 @@ ok

Time Measurement -

Support for time measurement in NIF libraries: +

Support for time measurement in NIF libraries:

ErlNifTime ErlNifTimeUnit enif_monotonic_time() enif_time_offset() enif_convert_time_unit() -

+
Long-running NIFs -- cgit v1.2.3 From d7519ca4a1834fdcdcc3ee9d7adeb8df5a45600d Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 27 May 2016 10:42:44 +0200 Subject: Fix windows --- erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c index 2013c88167..155788dceb 100644 --- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c +++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c @@ -19,7 +19,9 @@ */ #include "erl_nif.h" #include -#ifndef __WIN32__ +#ifdef __WIN32__ +#include +#else #include #endif -- cgit v1.2.3 From d83ecf6b20d0e1d804f343a837e5e4fe676bf077 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 27 May 2016 10:52:50 +0200 Subject: Bumped runtime dependencies between erts, kernel, stdlib, sasl --- erts/preloaded/src/erts.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index 98e0224a5f..e18da28905 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -37,7 +37,7 @@ {registered, []}, {applications, []}, {env, []}, - {runtime_dependencies, ["stdlib-2.5", "kernel-4.0", "sasl-2.4"]} + {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0"]} ]}. %% vim: ft=erlang -- cgit v1.2.3 From 49ed8a5a1cddc806dd520aaa79e1a089194566c1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 27 May 2016 15:43:38 +0200 Subject: Update preloaded --- erts/preloaded/ebin/erlang.beam | Bin 104656 -> 104648 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 581ab511d8..cde8c9ab72 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ -- cgit v1.2.3 From d319179eee6a523812c81c7ca49afb670de0b277 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 30 May 2016 17:20:54 +0200 Subject: Reintroduce erlang:halt/0/1 in erl_bif_types.erl Removed in f9cb80861f169743 when changed impl from C to Erlang. But seems they are needed to keep dialyzer tests happy. Also improved bif_SUITE:shadow_comments to include all exported in module erlang, not just the "snifs". ...which detected that apply/2 was missing Shadowed comment as well. --- erts/emulator/test/bif_SUITE.erl | 13 +++++++++---- erts/preloaded/src/erlang.erl | 3 +++ 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index ec6cb6ab72..d31399e4af 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -141,9 +141,11 @@ guard_bifs_in_erl_bif_types(_Config) -> shadow_comments(_Config) -> ensure_erl_bif_types_compiled(), + ErlangList = [{erlang,F,A} || {F,A} <- erlang:module_info(exports), + not is_operator(F,A)], List0 = erlang:system_info(snifs), - List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs], - List = [MFA || MFA <- List1, not is_operator(MFA)], + List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs, M =/= erlang], + List = List1 ++ ErlangList, HasTypes = [MFA || {M,F,A}=MFA <- List, erl_bif_types:is_known(M, F, A)], Path = get_code_path(), @@ -253,12 +255,15 @@ specs(_) -> end. is_operator({erlang,F,A}) -> + is_operator(F,A); +is_operator(_) -> false. + +is_operator(F,A) -> erl_internal:arith_op(F, A) orelse erl_internal:bool_op(F, A) orelse erl_internal:comp_op(F, A) orelse erl_internal:list_op(F, A) orelse - erl_internal:send_op(F, A); -is_operator(_) -> false. + erl_internal:send_op(F, A). extract_specs(M, Abstr) -> [{make_mfa(M, Name),Spec} || {attribute,_,spec,{Name,Spec}} <- Abstr]. diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 5283519c0a..94f3078173 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -977,11 +977,13 @@ group_leader(_GroupLeader, _Pid) -> erlang:nif_error(undefined). %% halt/0 +%% Shadowed by erl_bif_types: erlang:halt/0 -spec halt() -> no_return(). halt() -> erlang:halt(0, []). %% halt/1 +%% Shadowed by erl_bif_types: erlang:halt/1 -spec halt(Status) -> no_return() when Status :: non_neg_integer() | 'abort' | string(). halt(Status) -> @@ -2576,6 +2578,7 @@ universaltime_to_localtime(_Universaltime) -> %%-------------------------------------------------------------------------- +%% Shadowed by erl_bif_types: erlang:apply/2 -spec apply(Fun, Args) -> term() when Fun :: function(), Args :: [term()]. -- cgit v1.2.3 From 728b48254bf4e3979a2e4c6cd667ace5cd020d12 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 30 May 2016 20:44:10 +0200 Subject: erts: Fix ASSERT provoked by map_SUITE:t_rare_map_overflow The same bug was fixed for OTP 18 in cb62c989e59f0ec8556f9f1d4e9a45b by provoking yet another GC. But now in 19 we are ok with heap fragments so just remove the asserts. --- erts/emulator/beam/erl_gc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index c7bbbd5ca0..9da82f9bf2 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1198,7 +1198,6 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end, done: ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0)); - ASSERT(MBUF(p) == NULL); /* The heap usage during GC should be larger than what we end up after a GC, even if we grow it. If this assertion is not true @@ -1591,6 +1590,9 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end, HIGH_WATER(p) = HEAP_TOP(p); +#ifdef HARDDEBUG + disallow_heap_frag_ref_in_heap(p); +#endif remove_message_buffers(p); if (p->flags & F_ON_HEAP_MSGQ) @@ -1603,9 +1605,6 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end, adjusted = adjust_after_fullsweep(p, need, objv, nobj); -#ifdef HARDDEBUG - disallow_heap_frag_ref_in_heap(p); -#endif ErtsGcQuickSanityCheck(p); return gc_cost(size_after, adjusted ? size_after : 0); -- cgit v1.2.3 From ca3c71f3e22b93bc633562d0d1a2f6479c21b223 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 30 May 2016 20:49:04 +0200 Subject: erts: Clean up some goto spaghetti and replace with a nice else-if chain. --- erts/emulator/beam/erl_gc.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 9da82f9bf2..d0d74bbf44 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1183,20 +1183,13 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end, adjust_size = p->htop - p->heap; } - goto done; } + else if (need_after > HEAP_SIZE(p)) { + grow_new_heap(p, next_heap_size(p, need_after, 0), objv, nobj); + adjust_size = p->htop - p->heap; + } + /*else: The heap size turned out to be just right. We are done. */ - if (HEAP_SIZE(p) >= need_after) { - /* - * The heap size turned out to be just right. We are done. - */ - goto done; - } - - grow_new_heap(p, next_heap_size(p, need_after, 0), objv, nobj); - adjust_size = p->htop - p->heap; - - done: ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0)); /* The heap usage during GC should be larger than what we end up -- cgit v1.2.3 From 034394661dc1b2467825b58f6264874f6940c279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 31 May 2016 14:32:42 +0200 Subject: erts: Cuddle with lttng_SUITE --- erts/emulator/test/lttng_SUITE.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl index c64ddc40da..1360751aee 100644 --- a/erts/emulator/test/lttng_SUITE.erl +++ b/erts/emulator/test/lttng_SUITE.erl @@ -44,6 +44,7 @@ suite() -> all() -> [t_lttng_list, + t_memory_carrier, t_carrier_pool, t_async_io_pool, t_driver_start_stop, @@ -52,8 +53,7 @@ all() -> t_driver_timeout, t_driver_caller, t_driver_flush, - t_scheduler_poll, - t_memory_carrier]. + t_scheduler_poll]. init_per_suite(Config) -> @@ -178,11 +178,13 @@ t_async_io_pool(Config) -> %% com_ericsson_otp:driver_stop t_driver_start_stop(Config) -> ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + timer:sleep(500), Path = proplists:get_value(priv_dir, Config), Name = filename:join(Path, "sometext.txt"), Bin = txt(), ok = file:write_file(Name, Bin), {ok, Bin} = file:read_file(Name), + timer:sleep(500), Res = lttng_stop_and_view(Config), ok = check_tracepoint("com_ericsson_otp:driver_start", Res), ok = check_tracepoint("com_ericsson_otp:driver_stop", Res), @@ -213,6 +215,7 @@ t_driver_control_ready_async(Config) -> %% com_ericsson_otp:driver_ready_output t_driver_ready_input_output(Config) -> ok = lttng_start_event("com_ericsson_otp:driver_ready_*", Config), + timer:sleep(500), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, active) end), receive {Pid, accept} -> ok end, @@ -225,6 +228,7 @@ t_driver_ready_input_output(Config) -> ok = gen_tcp:close(Sock), receive {Pid, done} -> ok end, + timer:sleep(500), Res = lttng_stop_and_view(Config), ok = check_tracepoint("com_ericsson_otp:driver_ready_input", Res), ok = check_tracepoint("com_ericsson_otp:driver_ready_output", Res), -- cgit v1.2.3 From a6a4ce5c63cae9e0d3a35fea3074a59dd06f22fc Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 31 May 2016 15:00:50 +0200 Subject: Do not send on_heap msgs to error logger in non-smp emulator --- erts/emulator/beam/utils.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index f0418446a8..675fafa726 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2210,7 +2210,9 @@ do_allocate_logger_message(Eterm gleader, Eterm **hp, ErlOffHeap **ohp, #ifndef ERTS_SMP #ifdef USE_THREADS - if (erts_get_scheduler_data()) /* Must be scheduler thread */ + if (!erts_get_scheduler_data()) /* Must be scheduler thread */ + *p = NULL; + else #endif { *p = erts_whereis_process(NULL, 0, am_error_logger, 0, 0); @@ -2226,18 +2228,10 @@ do_allocate_logger_message(Eterm gleader, Eterm **hp, ErlOffHeap **ohp, } /* So we have an error logger, lets build the message */ - if (sz <= HeapWordsLeft(*p)) { - *ohp = &MSO(*p); - *hp = HEAP_TOP(*p); - HEAP_TOP(*p) += sz; - } else { -#endif - *bp = new_message_buffer(sz); - *ohp = &(*bp)->off_heap; - *hp = (*bp)->mem; -#ifndef ERTS_SMP - } #endif + *bp = new_message_buffer(sz); + *ohp = &(*bp)->off_heap; + *hp = (*bp)->mem; return (is_nil(gleader) ? am_noproc -- cgit v1.2.3 From 60557173f8a7bd0d4deafdb2b3e066899c586f56 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Wed, 23 Dec 2015 21:09:19 -0500 Subject: Add dirty_process_main function Dirty schedulers only execute NIFs, so having them execute the full process_main function isn't necessary. Add dirty_process_main for dirty schedulers to execute instead. Add erts_pre_dirty_nif(), called when preparing to execute a dirty nif. Add more dirty NIF tests to verify that activities requiring the process main lock can succeed when the process is executing a dirty NIF. --- erts/emulator/beam/beam_emu.c | 307 ++++++++++++++++++++- erts/emulator/beam/erl_nif.c | 81 +++--- erts/emulator/beam/erl_process.c | 4 +- erts/emulator/beam/global.h | 3 + erts/emulator/test/dirty_nif_SUITE.erl | 129 ++++++++- .../test/dirty_nif_SUITE_data/dirty_nif_SUITE.c | 20 ++ 6 files changed, 492 insertions(+), 52 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index f8f2e29c95..25cc41323b 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -3559,12 +3559,8 @@ do { \ typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]); NifF* fp = vbf = (NifF*) I[1]; struct enif_environment_t env; -#ifdef ERTS_DIRTY_SCHEDULERS - if (!c_p->scheduler_data) - live_hf_end = ERTS_INVALID_HFRAG_PTR; /* On dirty scheduler */ - else -#endif - live_hf_end = c_p->mbuf; + ASSERT(c_p->scheduler_data); + live_hf_end = c_p->mbuf; erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL); nif_bif_result = (*fp)(&env, bif_nif_arity, reg); if (env.exception_thrown) @@ -3574,10 +3570,7 @@ do { \ PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR); - if (env.exiting) { - ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); - goto do_schedule; - } + ASSERT(!env.exiting); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); } @@ -5162,6 +5155,300 @@ do { \ } } +/* + * dirty_process_main() is what dirty schedulers execute. Since they handle + * only NIF calls they do not need to be able to execute all BEAM + * instructions. + */ +void dirty_process_main(void) +{ + Process* c_p = NULL; + int reds_used; +#ifdef DEBUG + ERTS_DECLARE_DUMMY(Eterm pid); +#endif + BeamInstr *do_call_nif = OpCode(call_nif); + + /* Pointer to X registers: x(1)..x(N); reg[0] is used when doing GC, + * in all other cases x0 is used. + */ + register Eterm* reg REG_xregs = NULL; + + /* + * Top of heap (next free location); grows upwards. + */ + register Eterm* HTOP REG_htop = NULL; + + /* Stack pointer. Grows downwards; points + * to last item pushed (normally a saved + * continuation pointer). + */ + register Eterm* E REG_stop = NULL; + + /* + * Pointer to next threaded instruction. + */ + register BeamInstr *I REG_I = NULL; + + /* Number of reductions left. This function + * returns to the scheduler when FCALLS reaches zero. + */ + register Sint FCALLS REG_fcalls = 0; + + /* + * X registers and floating point registers are located in + * scheduler specific data. + */ + register FloatDef *freg = NULL; + + /* + * For keeping the negative old value of 'reds' when call saving is active. + */ + int neg_o_reds = 0; + + ERTS_MSACC_DECLARE_CACHE_X() /* a cached value of the tsd pointer for msacc */ + + ERL_BITS_DECLARE_STATEP; /* Has to be last declaration */ + + c_p = NULL; + reds_used = 0; + + goto do_dirty_schedule1; + + context_switch: + c_p->arity = I[-1]; + c_p->current = I-3; /* Pointer to Mod, Func, Arity */ + + { + Eterm* argp; + int i; + + /* + * Make sure that there is enough room for the argument registers to be saved. + */ + if (c_p->arity > c_p->max_arg_reg) { + /* + * Yes, this is an expensive operation, but you only pay it the first + * time you call a function with more than 6 arguments which is + * scheduled out. This is better than paying for 26 words of wasted + * space for most processes which never call functions with more than + * 6 arguments. + */ + Uint size = c_p->arity * sizeof(c_p->arg_reg[0]); + if (c_p->arg_reg != c_p->def_arg_reg) { + c_p->arg_reg = (Eterm *) erts_realloc(ERTS_ALC_T_ARG_REG, + (void *) c_p->arg_reg, + size); + } else { + c_p->arg_reg = (Eterm *) erts_alloc(ERTS_ALC_T_ARG_REG, size); + } + c_p->max_arg_reg = c_p->arity; + } + + /* + * Since REDS_IN(c_p) is stored in the save area (c_p->arg_reg) we must read it + * now before saving registers. + */ + + ASSERT(c_p->debug_reds_in == REDS_IN(c_p)); + if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) + reds_used = REDS_IN(c_p) - FCALLS; + else + reds_used = REDS_IN(c_p) - (CONTEXT_REDS + FCALLS); + ASSERT(reds_used >= 0); + + /* + * Save the argument registers and everything else. + */ + + argp = c_p->arg_reg; + for (i = c_p->arity - 1; i >= 0; i--) { + argp[i] = reg[i]; + } + SWAPOUT; + c_p->i = I; + goto do_dirty_schedule1; + } + + do_dirty_schedule: + ASSERT(c_p->debug_reds_in == REDS_IN(c_p)); + if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) + reds_used = REDS_IN(c_p) - FCALLS; + else + reds_used = REDS_IN(c_p) - (CONTEXT_REDS + FCALLS); + ASSERT(reds_used >= 0); + do_dirty_schedule1: + + PROCESS_MAIN_CHK_LOCKS(c_p); + ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); + c_p = schedule(c_p, reds_used); + ASSERT(!(c_p->flags & F_HIPE_MODE)); + ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); +#ifdef DEBUG + pid = c_p->common.id; /* Save for debugging purposes */ +#endif + ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); + PROCESS_MAIN_CHK_LOCKS(c_p); + + ERTS_MSACC_UPDATE_CACHE_X(); + + reg = erts_proc_sched_data(c_p)->x_reg_array; + freg = erts_proc_sched_data(c_p)->f_reg_array; + ERL_BITS_RELOAD_STATEP(c_p); + { + int reds; + Eterm* argp; + int i; + + argp = c_p->arg_reg; + for (i = c_p->arity - 1; i >= 0; i--) { + reg[i] = argp[i]; + CHECK_TERM(reg[i]); + } + + /* + * We put the original reduction count in the process structure, to reduce + * the code size (referencing a field in a struct through a pointer stored + * in a register gives smaller code than referencing a global variable). + */ + + I = c_p->i; + + REDS_IN(c_p) = reds = c_p->fcalls; +#ifdef DEBUG + c_p->debug_reds_in = reds; +#endif + + if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) { + neg_o_reds = -CONTEXT_REDS; + FCALLS = neg_o_reds + reds; + } else { + neg_o_reds = 0; + FCALLS = reds; + } + + ERTS_DBG_CHK_REDS(c_p, FCALLS); + + SWAPIN; + +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_scheduled)) { + DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(fun_buf, DTRACE_TERM_BUF_SIZE); + dtrace_proc_str(c_p, process_buf); + + if (ERTS_PROC_IS_EXITING(c_p)) { + strcpy(fun_buf, ""); + } else { + BeamInstr *fptr = find_function_from_pc(c_p->i); + if (fptr) { + dtrace_fun_decode(c_p, (Eterm)fptr[0], + (Eterm)fptr[1], (Uint)fptr[2], + NULL, fun_buf); + } else { + erts_snprintf(fun_buf, sizeof(DTRACE_CHARBUF_NAME(fun_buf)), + "", next); + } + } + + DTRACE2(process_scheduled, process_buf, fun_buf); + } +#endif + } + + { + Eterm nif_bif_result; + Eterm bif_nif_arity; + + OpCase(call_nif): + { + /* + * call_nif is always first instruction in function: + * + * I[-3]: Module + * I[-2]: Function + * I[-1]: Arity + * I[0]: &&call_nif + * I[1]: Function pointer to NIF function + * I[2]: Pointer to erl_module_nif + * I[3]: Function pointer to dirty NIF + */ + BifFunction vbf; + + if (!((FCALLS - 1) > 0 || (FCALLS - 1) > neg_o_reds)) { + /* If we have run out of reductions, we do a context + switch before calling the nif */ + goto context_switch; + } + + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF); + + DTRACE_NIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); + c_p->current = I-3; /* current and vbf set to please handle_error */ + SWAPOUT; + c_p->fcalls = FCALLS - 1; + PROCESS_MAIN_CHK_LOCKS(c_p); + bif_nif_arity = I[-1]; + ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + { + typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]); + NifF* fp = vbf = (NifF*) I[1]; + struct enif_environment_t env; + ASSERT(!c_p->scheduler_data); + erts_pre_dirty_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL); + nif_bif_result = (*fp)(&env, bif_nif_arity, reg); + if (env.exception_thrown) + nif_bif_result = THE_NON_VALUE; + erts_post_nif(&env); + PROCESS_MAIN_CHK_LOCKS(c_p); + ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR); + if (env.exiting) { + ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); + goto do_dirty_schedule; + } + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + } + DTRACE_NIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); + ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); + ERTS_HOLE_CHECK(c_p); + if (ERTS_IS_GC_DESIRED(c_p)) { + nif_bif_result = erts_gc_after_bif_call(c_p, + nif_bif_result, + reg, bif_nif_arity); + } + SWAPIN; /* There might have been a garbage collection. */ + FCALLS = c_p->fcalls; + ERTS_DBG_CHK_REDS(c_p, FCALLS); + if (is_value(nif_bif_result)) { + r(0) = nif_bif_result; + CHECK_TERM(r(0)); + I = c_p->cp; + c_p->cp = 0; + Goto(*I); + } else if (c_p->freason == TRAP) { + I = c_p->i; + if (c_p->flags & F_HIBERNATE_SCHED) { + c_p->flags &= ~F_HIBERNATE_SCHED; + goto do_dirty_schedule; + } + DispatchMacro(); + } + I = handle_error(c_p, c_p->cp, reg, vbf); + } + } + if (I == 0) { + goto do_dirty_schedule; + } else { + ASSERT(!is_value(r(0))); + SWAPIN; + Goto(do_call_nif); + } +} + static BifFunction translate_gc_bif(void* gcf) { diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 2bbb8e3c91..4437adb57b 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -178,8 +178,10 @@ static ERTS_INLINE void ensure_heap(ErlNifEnv* env, size_t may_need) void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, Process* tracee) { +#ifdef DEBUG #ifdef ERTS_DIRTY_SCHEDULERS ErtsSchedulerData *esdp; +#endif #endif env->mod_nif = mod_nif; env->proc = p; @@ -193,12 +195,12 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, ASSERT(p->common.id != ERTS_INVALID_PID); +#ifdef DEBUG #ifdef ERTS_DIRTY_SCHEDULERS esdp = erts_get_scheduler_data(); ASSERT(esdp); if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { -#ifdef DEBUG erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state); ASSERT(p->scheduler_data == esdp); @@ -206,44 +208,57 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, | ERTS_PSFLG_RUNNING_SYS)) && !(state & (ERTS_PSFLG_DIRTY_RUNNING | ERTS_PSFLG_DIRTY_RUNNING_SYS))); + } #endif +#endif +} - } - else { - Process *sproc; +void erts_pre_dirty_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, + Process* tracee) +{ +#ifdef ERTS_DIRTY_SCHEDULERS #ifdef DEBUG - erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state); + erts_aint32_t state; +#endif + Process *sproc; + ErtsSchedulerData *esdp; + esdp = erts_get_scheduler_data(); + ASSERT(esdp); + + erts_pre_nif(env, p, mod_nif, tracee); - ASSERT(!p->scheduler_data); - ASSERT((state & ERTS_PSFLG_DIRTY_RUNNING) - && !(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))); +#ifdef DEBUG + state = erts_smp_atomic32_read_nob(&p->state); + + ASSERT(!p->scheduler_data); + ASSERT((state & ERTS_PSFLG_DIRTY_RUNNING) + && !(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))); #endif - sproc = esdp->dirty_shadow_process; - ASSERT(sproc); - ASSERT(sproc->static_flags & ERTS_STC_FLG_SHADOW_PROC); - ASSERT(erts_smp_atomic32_read_nob(&sproc->state) - == (ERTS_PSFLG_ACTIVE - | ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_PROXY)); - - sproc->next = p; - sproc->common.id = p->common.id; - sproc->htop = p->htop; - sproc->stop = p->stop; - sproc->hend = p->hend; - sproc->heap = p->heap; - sproc->abandoned_heap = p->abandoned_heap; - sproc->heap_sz = p->heap_sz; - sproc->high_water = p->high_water; - sproc->old_hend = p->old_hend; - sproc->old_htop = p->old_htop; - sproc->old_heap = p->old_heap; - sproc->mbuf = NULL; - sproc->mbuf_sz = 0; - ERTS_INIT_OFF_HEAP(&sproc->off_heap); - env->proc = sproc; - } + sproc = esdp->dirty_shadow_process; + ASSERT(sproc); + ASSERT(sproc->static_flags & ERTS_STC_FLG_SHADOW_PROC); + ASSERT(erts_smp_atomic32_read_nob(&sproc->state) + == (ERTS_PSFLG_ACTIVE + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_PROXY)); + + sproc->next = p; + sproc->common.id = p->common.id; + sproc->htop = p->htop; + sproc->stop = p->stop; + sproc->hend = p->hend; + sproc->heap = p->heap; + sproc->abandoned_heap = p->abandoned_heap; + sproc->heap_sz = p->heap_sz; + sproc->high_water = p->high_water; + sproc->old_hend = p->old_hend; + sproc->old_htop = p->old_htop; + sproc->old_heap = p->old_heap; + sproc->mbuf = NULL; + sproc->mbuf_sz = 0; + ERTS_INIT_OFF_HEAP(&sproc->off_heap); + env->proc = sproc; #endif } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index f8cbe60e76..e245c9e6bb 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -8197,7 +8197,7 @@ sched_dirty_cpu_thread_func(void *vesdp) #endif erts_thread_init_float(); - process_main(); + dirty_process_main(); /* No schedulers should *ever* terminate */ erts_exit(ERTS_ABORT_EXIT, "Dirty CPU scheduler thread number %beu terminated\n", @@ -8242,7 +8242,7 @@ sched_dirty_io_thread_func(void *vesdp) #endif erts_thread_init_float(); - process_main(); + dirty_process_main(); /* No schedulers should *ever* terminate */ erts_exit(ERTS_ABORT_EXIT, "Dirty I/O scheduler thread number %beu terminated\n", diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index b76b9cd874..15253bb53e 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -62,6 +62,8 @@ struct enif_environment_t /* ErlNifEnv */ extern void erts_pre_nif(struct enif_environment_t*, Process*, struct erl_module_nif*, Process* tracee); extern void erts_post_nif(struct enif_environment_t* env); +extern void erts_pre_dirty_nif(struct enif_environment_t*, Process*, + struct erl_module_nif*, Process* tracee); extern Eterm erts_nif_taints(Process* p); extern void erts_print_nif_taints(int to, void* to_arg); void erts_unload_nif(struct erl_module_nif* nif); @@ -1152,6 +1154,7 @@ void print_pass_through(int, byte*, int); int catchlevel(Process*); void init_emulator(void); void process_main(void); +void dirty_process_main(void); Eterm build_stacktrace(Process* c_p, Eterm exc); Eterm expand_error_value(Process* c_p, Uint freason, Eterm Value); void erts_save_stacktrace(Process* p, struct StackTrace* s, int depth); diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl index c3afbc0803..83b098a704 100644 --- a/erts/emulator/test/dirty_nif_SUITE.erl +++ b/erts/emulator/test/dirty_nif_SUITE.erl @@ -32,19 +32,23 @@ dirty_nif/1, dirty_nif_send/1, dirty_nif_exception/1, call_dirty_nif_exception/1, dirty_scheduler_exit/1, dirty_call_while_terminated/1, - dirty_heap_access/1]). + dirty_heap_access/1, dirty_process_info/1, + dirty_process_register/1, dirty_process_trace/1]). -define(nif_stub,nif_stub_error(?LINE)). suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> +all() -> [dirty_nif, dirty_nif_send, dirty_nif_exception, dirty_scheduler_exit, dirty_call_while_terminated, - dirty_heap_access]. + dirty_heap_access, + dirty_process_info, + dirty_process_register, + dirty_process_trace]. init_per_suite(Config) -> try erlang:system_info(dirty_cpu_schedulers) of @@ -187,7 +191,7 @@ dirty_call_while_terminated(Config) when is_list(Config) -> blipp:blupp(Bin) end, [monitor,link]), - receive {dirty_alive, Pid} -> ok end, + receive {dirty_alive, _Pid} -> ok end, {value, {BinAddr, 4711, 2}} = lists:keysearch(4711, 2, element(2, process_info(self(), @@ -241,7 +245,7 @@ dirty_heap_access(Config) when is_list(Config) -> end), {N, R} = access_dirty_heap(Dirty, RGL, 0, 0), receive - {Pid, Res} -> + {_Pid, Res} -> 1000 = length(Res), lists:foreach(fun (X) -> Ref = X end, Res) end, @@ -269,12 +273,123 @@ access_dirty_heap(Dirty, RGL, N, R) -> end) end. +%% These tests verify that processes that access a process executing a +%% dirty NIF where the main lock is needed for that access do not get +%% blocked. Each test passes its pid to dirty_sleeper, which sends a +%% 'ready' message when it's running on a dirty scheduler and just before +%% it starts a 6 second sleep. When it receives the message, it verifies +%% that access to the dirty process is as it expects. After the dirty +%% process finishes its 6 second sleep but before it returns from the dirty +%% scheduler, it sends a 'done' message. If the tester already received +%% that message, the test fails because it means attempting to access the +%% dirty process waited for that process to return to a regular scheduler, +%% so verify that we haven't received that message, and also verify that +%% the dirty process is still alive immediately after accessing it. +dirty_process_info(Config) when is_list(Config) -> + access_dirty_process( + Config, + fun() -> ok end, + fun(NifPid) -> + PI = process_info(NifPid), + {current_function,{?MODULE,dirty_sleeper,1}} = + lists:keyfind(current_function, 1, PI), + ok + end, + fun(_) -> ok end). + +dirty_process_register(Config) when is_list(Config) -> + access_dirty_process( + Config, + fun() -> ok end, + fun(NifPid) -> + register(test_dirty_process_register, NifPid), + NifPid = whereis(test_dirty_process_register), + unregister(test_dirty_process_register), + false = lists:member(test_dirty_process_register, + registered()), + ok + end, + fun(_) -> ok end). + +dirty_process_trace(Config) when is_list(Config) -> + access_dirty_process( + Config, + fun() -> + erlang:trace_pattern({?MODULE,dirty_sleeper,1}, + [{'_',[],[{return_trace}]}], + [local,meta]), + ok + end, + fun(NifPid) -> + erlang:trace(NifPid, true, [call,timestamp]), + ok + end, + fun(NifPid) -> + receive + done -> + receive + {trace_ts,NifPid,call,{?MODULE,dirty_sleeper,_},_} -> + ok + after + 0 -> + error(missing_trace_call_message) + end, + receive + {trace_ts,NifPid,return_from,{?MODULE,dirty_sleeper,1}, + ok,_} -> + ok + after + 100 -> + error(missing_trace_return_message) + end + after + 6500 -> + error(missing_done_message) + end, + ok + end). + %% %% Internal... %% +access_dirty_process(Config, Start, Test, Finish) -> + {ok, Node} = start_node(Config, ""), + [ok] = mcall(Node, + [fun() -> + Path = ?config(data_dir, Config), + Lib = atom_to_list(?MODULE), + ok = erlang:load_nif(filename:join(Path,Lib), []), + ok = test_dirty_process_access(Start, Test, Finish) + end]), + stop_node(Node), + ok. + +test_dirty_process_access(Start, Test, Finish) -> + ok = Start(), + Self = self(), + NifPid = spawn_link(fun() -> + ok = dirty_sleeper(Self) + end), + ok = receive + ready -> + ok = Test(NifPid), + receive + done -> + error(dirty_process_info_blocked) + after + 0 -> + true = erlang:is_process_alive(NifPid), + ok + end + after + 3000 -> + error(timeout) + end, + ok = Finish(NifPid). + receive_any() -> - receive M -> M end. + receive M -> M end. start_node(Config) -> start_node(Config, ""). @@ -314,13 +429,13 @@ mcall(Node, Funs) -> %% The NIFs: lib_loaded() -> false. -call_nif_schedule(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. send_from_dirty_nif(_) -> ?nif_stub. call_dirty_nif_exception(_) -> ?nif_stub. call_dirty_nif_zero_args() -> ?nif_stub. dirty_call_while_terminated_nif(_) -> ?nif_stub. dirty_sleeper() -> ?nif_stub. +dirty_sleeper(_) -> ?nif_stub. dirty_heap_access_nif(_) -> ?nif_stub. nif_stub_error(Line) -> diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c index 2013c88167..1d2a099186 100644 --- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c +++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c @@ -146,12 +146,31 @@ static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL static ERL_NIF_TERM dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifPid pid; + ErlNifEnv* msg_env = NULL; + assert(enif_is_on_dirty_scheduler(env)); + + /* If we get a pid argument, it indicates a process involved in the + test wants a message from us. Prior to the sleep we send a 'ready' + message, and then after the sleep, send a 'done' message. */ + if (argc == 1 && enif_get_local_pid(env, argv[0], &pid)) { + msg_env = enif_alloc_env(); + enif_send(env, &pid, msg_env, enif_make_atom(msg_env, "ready")); + } + #ifdef __WIN32__ Sleep(6000); #else sleep(6); #endif + + if (argc == 1) { + assert(msg_env != NULL); + enif_send(env, &pid, msg_env, enif_make_atom(msg_env, "done")); + enif_free_env(msg_env); + } + return enif_make_atom(env, "ok"); } @@ -216,6 +235,7 @@ static ErlNifFunc nif_funcs[] = {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND}, {"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND}, + {"dirty_sleeper", 1, dirty_sleeper, ERL_NIF_DIRTY_JOB_CPU_BOUND}, {"dirty_call_while_terminated_nif", 1, dirty_call_while_terminated_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND}, {"dirty_heap_access_nif", 1, dirty_heap_access_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND} }; -- cgit v1.2.3 From c8bd15f0e5539ec3056565b535ff6db389f42a6b Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 27 May 2016 16:19:08 +0200 Subject: Move dirty reduction count to erts_dirty_process_main() --- erts/emulator/beam/beam_emu.c | 159 +++++++++++++++------------------- erts/emulator/beam/erl_nif.c | 46 ++++------ erts/emulator/beam/erl_process.c | 113 +++++------------------- erts/emulator/beam/erl_process.h | 25 +----- erts/emulator/beam/global.h | 5 +- erts/emulator/hipe/hipe_mode_switch.c | 2 +- 6 files changed, 115 insertions(+), 235 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 25cc41323b..0a59f8785c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1323,11 +1323,7 @@ void process_main(void) if (start_time != 0) { Sint64 diff = erts_timestamp_millis() - start_time; - if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule -#if defined(ERTS_SMP) && defined(ERTS_DIRTY_SCHEDULERS) - && !ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(c_p)) -#endif - ) { + if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule) { BeamInstr *inptr = find_function_from_pc(start_time_i); BeamInstr *outptr = find_function_from_pc(c_p->i); monitor_long_schedule_proc(c_p,inptr,outptr,(Uint) diff); @@ -1337,7 +1333,7 @@ void process_main(void) PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - c_p = schedule(c_p, reds_used); + c_p = erts_schedule(NULL, c_p, reds_used); ASSERT(!(c_p->flags & F_HIPE_MODE)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); start_time = 0; @@ -3559,7 +3555,9 @@ do { \ typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]); NifF* fp = vbf = (NifF*) I[1]; struct enif_environment_t env; +#ifdef ERTS_SMP ASSERT(c_p->scheduler_data); +#endif live_hf_end = c_p->mbuf; erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL); nif_bif_result = (*fp)(&env, bif_nif_arity, reg); @@ -5156,18 +5154,18 @@ do { \ } /* - * dirty_process_main() is what dirty schedulers execute. Since they handle + * erts_dirty_process_main() is what dirty schedulers execute. Since they handle * only NIF calls they do not need to be able to execute all BEAM * instructions. */ -void dirty_process_main(void) +void erts_dirty_process_main(ErtsSchedulerData *esdp) { +#ifdef ERTS_DIRTY_SCHEDULERS Process* c_p = NULL; - int reds_used; + ErtsMonotonicTime start_time; #ifdef DEBUG ERTS_DECLARE_DUMMY(Eterm pid); #endif - BeamInstr *do_call_nif = OpCode(call_nif); /* Pointer to X registers: x(1)..x(N); reg[0] is used when doing GC, * in all other cases x0 is used. @@ -5190,36 +5188,30 @@ void dirty_process_main(void) */ register BeamInstr *I REG_I = NULL; - /* Number of reductions left. This function - * returns to the scheduler when FCALLS reaches zero. - */ - register Sint FCALLS REG_fcalls = 0; - - /* - * X registers and floating point registers are located in - * scheduler specific data. - */ - register FloatDef *freg = NULL; + ERTS_MSACC_DECLARE_CACHE_X() /* a cached value of the tsd pointer for msacc */ /* - * For keeping the negative old value of 'reds' when call saving is active. + * start_time always positive for dirty CPU schedulers, + * and negative for dirty I/O schedulers. */ - int neg_o_reds = 0; - ERTS_MSACC_DECLARE_CACHE_X() /* a cached value of the tsd pointer for msacc */ - - ERL_BITS_DECLARE_STATEP; /* Has to be last declaration */ - - c_p = NULL; - reds_used = 0; + if (ERTS_SCHEDULER_IS_DIRTY_CPU(esdp)) { + start_time = erts_get_monotonic_time(NULL); + ASSERT(start_time >= 0); + } + else { + start_time = ERTS_SINT64_MIN; + ASSERT(start_time < 0); + } - goto do_dirty_schedule1; + goto do_dirty_schedule; context_switch: c_p->arity = I[-1]; c_p->current = I-3; /* Pointer to Mod, Func, Arity */ { + int reds_used; Eterm* argp; int i; @@ -5245,18 +5237,6 @@ void dirty_process_main(void) c_p->max_arg_reg = c_p->arity; } - /* - * Since REDS_IN(c_p) is stored in the save area (c_p->arg_reg) we must read it - * now before saving registers. - */ - - ASSERT(c_p->debug_reds_in == REDS_IN(c_p)); - if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) - reds_used = REDS_IN(c_p) - FCALLS; - else - reds_used = REDS_IN(c_p) - (CONTEXT_REDS + FCALLS); - ASSERT(reds_used >= 0); - /* * Save the argument registers and everything else. */ @@ -5267,23 +5247,46 @@ void dirty_process_main(void) } SWAPOUT; c_p->i = I; - goto do_dirty_schedule1; - } - do_dirty_schedule: - ASSERT(c_p->debug_reds_in == REDS_IN(c_p)); - if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) - reds_used = REDS_IN(c_p) - FCALLS; - else - reds_used = REDS_IN(c_p) - (CONTEXT_REDS + FCALLS); - ASSERT(reds_used >= 0); - do_dirty_schedule1: + do_dirty_schedule: + + if (start_time < 0) { + /* + * Dirty I/O scheduler: + * One reduction consumed regardless of + * time spent in the dirty NIF. + */ + reds_used = esdp->virtual_reds + 1; + } + else { + /* + * Dirty CPU scheduler: + * Currently two reductions consumed per + * micro second spent in the dirty NIF. + */ + ErtsMonotonicTime time; + time = erts_get_monotonic_time(esdp); + time -= start_time; + time = ERTS_MONOTONIC_TO_USEC(time); + time *= (CONTEXT_REDS-1)/1000 + 1; + ASSERT(time >= 0); + if (time == 0) + time = 1; /* At least one reduction */ + time += esdp->virtual_reds; + reds_used = time > INT_MAX ? INT_MAX : (int) time; + } + + PROCESS_MAIN_CHK_LOCKS(c_p); + ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); + c_p = erts_schedule(esdp, c_p, reds_used); + + if (start_time >= 0) { + start_time = erts_get_monotonic_time(esdp); + ASSERT(start_time >= 0); + } + } - PROCESS_MAIN_CHK_LOCKS(c_p); - ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); - ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - c_p = schedule(c_p, reds_used); - ASSERT(!(c_p->flags & F_HIPE_MODE)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); #ifdef DEBUG pid = c_p->common.id; /* Save for debugging purposes */ @@ -5291,13 +5294,11 @@ void dirty_process_main(void) ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); + ASSERT(!(c_p->flags & F_HIPE_MODE)); ERTS_MSACC_UPDATE_CACHE_X(); - reg = erts_proc_sched_data(c_p)->x_reg_array; - freg = erts_proc_sched_data(c_p)->f_reg_array; - ERL_BITS_RELOAD_STATEP(c_p); + reg = esdp->x_reg_array; { - int reds; Eterm* argp; int i; @@ -5315,21 +5316,13 @@ void dirty_process_main(void) I = c_p->i; - REDS_IN(c_p) = reds = c_p->fcalls; -#ifdef DEBUG - c_p->debug_reds_in = reds; -#endif + ASSERT(BeamOp(op_call_nif) == (BeamInstr *) *I); - if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) { - neg_o_reds = -CONTEXT_REDS; - FCALLS = neg_o_reds + reds; - } else { - neg_o_reds = 0; - FCALLS = reds; + if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) + && (ERTS_TRACE_FLAGS(c_p) & F_SENSITIVE) == 0) { + c_p->fcalls = REDS_IN(c_p) = 0; } - ERTS_DBG_CHK_REDS(c_p, FCALLS); - SWAPIN; #ifdef USE_VM_PROBES @@ -5361,7 +5354,6 @@ void dirty_process_main(void) Eterm nif_bif_result; Eterm bif_nif_arity; - OpCase(call_nif): { /* * call_nif is always first instruction in function: @@ -5376,18 +5368,11 @@ void dirty_process_main(void) */ BifFunction vbf; - if (!((FCALLS - 1) > 0 || (FCALLS - 1) > neg_o_reds)) { - /* If we have run out of reductions, we do a context - switch before calling the nif */ - goto context_switch; - } - ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF); DTRACE_NIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); c_p->current = I-3; /* current and vbf set to please handle_error */ SWAPOUT; - c_p->fcalls = FCALLS - 1; PROCESS_MAIN_CHK_LOCKS(c_p); bif_nif_arity = I[-1]; ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); @@ -5398,7 +5383,7 @@ void dirty_process_main(void) NifF* fp = vbf = (NifF*) I[1]; struct enif_environment_t env; ASSERT(!c_p->scheduler_data); - erts_pre_dirty_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL); + erts_pre_dirty_nif(esdp, &env, c_p, (struct erl_module_nif*)I[2], NULL); nif_bif_result = (*fp)(&env, bif_nif_arity, reg); if (env.exception_thrown) nif_bif_result = THE_NON_VALUE; @@ -5421,8 +5406,6 @@ void dirty_process_main(void) reg, bif_nif_arity); } SWAPIN; /* There might have been a garbage collection. */ - FCALLS = c_p->fcalls; - ERTS_DBG_CHK_REDS(c_p, FCALLS); if (is_value(nif_bif_result)) { r(0) = nif_bif_result; CHECK_TERM(r(0)); @@ -5431,11 +5414,8 @@ void dirty_process_main(void) Goto(*I); } else if (c_p->freason == TRAP) { I = c_p->i; - if (c_p->flags & F_HIBERNATE_SCHED) { - c_p->flags &= ~F_HIBERNATE_SCHED; - goto do_dirty_schedule; - } - DispatchMacro(); + ASSERT(!(c_p->flags & F_HIBERNATE_SCHED)); + goto context_switch; } I = handle_error(c_p, c_p->cp, reg, vbf); } @@ -5445,8 +5425,9 @@ void dirty_process_main(void) } else { ASSERT(!is_value(r(0))); SWAPIN; - Goto(do_call_nif); + goto context_switch; } +#endif /* ERTS_DIRTY_SCHEDULERS */ } static BifFunction diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 4437adb57b..f1c35cd5a5 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -178,11 +178,6 @@ static ERTS_INLINE void ensure_heap(ErlNifEnv* env, size_t may_need) void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, Process* tracee) { -#ifdef DEBUG -#ifdef ERTS_DIRTY_SCHEDULERS - ErtsSchedulerData *esdp; -#endif -#endif env->mod_nif = mod_nif; env->proc = p; env->hp = HEAP_TOP(p); @@ -195,46 +190,41 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, ASSERT(p->common.id != ERTS_INVALID_PID); -#ifdef DEBUG -#ifdef ERTS_DIRTY_SCHEDULERS - esdp = erts_get_scheduler_data(); - ASSERT(esdp); +#if defined(DEBUG) && defined(ERTS_DIRTY_SCHEDULERS) + { + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + ASSERT(esdp); - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { - erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state); - ASSERT(p->scheduler_data == esdp); - ASSERT((state & (ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS)) - && !(state & (ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS))); + ASSERT(p->scheduler_data == esdp); + ASSERT((state & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS)) + && !(state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS))); + } } #endif -#endif } -void erts_pre_dirty_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, +void erts_pre_dirty_nif(ErtsSchedulerData *esdp, + ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, Process* tracee) { #ifdef ERTS_DIRTY_SCHEDULERS -#ifdef DEBUG - erts_aint32_t state; -#endif Process *sproc; - ErtsSchedulerData *esdp; - esdp = erts_get_scheduler_data(); - ASSERT(esdp); - - erts_pre_nif(env, p, mod_nif, tracee); - #ifdef DEBUG - state = erts_smp_atomic32_read_nob(&p->state); + erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state); ASSERT(!p->scheduler_data); ASSERT((state & ERTS_PSFLG_DIRTY_RUNNING) && !(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))); + ASSERT(esdp); #endif + erts_pre_nif(env, p, mod_nif, tracee); + sproc = esdp->dirty_shadow_process; ASSERT(sproc); ASSERT(sproc->static_flags & ERTS_STC_FLG_SHADOW_PROC); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index e245c9e6bb..c0b1d7246c 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -8197,7 +8197,7 @@ sched_dirty_cpu_thread_func(void *vesdp) #endif erts_thread_init_float(); - dirty_process_main(); + erts_dirty_process_main(esdp); /* No schedulers should *ever* terminate */ erts_exit(ERTS_ABORT_EXIT, "Dirty CPU scheduler thread number %beu terminated\n", @@ -8242,7 +8242,7 @@ sched_dirty_io_thread_func(void *vesdp) #endif erts_thread_init_float(); - dirty_process_main(); + erts_dirty_process_main(esdp); /* No schedulers should *ever* terminate */ erts_exit(ERTS_ABORT_EXIT, "Dirty I/O scheduler thread number %beu terminated\n", @@ -9377,77 +9377,6 @@ scheduler_gc_proc(Process *c_p, int reds_left) return reds; } -static ERTS_INLINE void -clean_dirty_start(Process *p) -{ -#if defined(ERTS_DIRTY_SCHEDULERS) && !defined(ARCH_64) - void *ptr = ERTS_PROC_SET_DIRTY_CPU_START(p, NULL); - if (ptr) - erts_free(ERTS_ALC_T_DIRTY_START, ptr); -#endif -} - -static ERTS_INLINE void -save_dirty_start(ErtsSchedulerData *esdp, Process *c_p) -{ -#ifdef ERTS_DIRTY_SCHEDULERS - if (ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(esdp->run_queue)) { - ErtsMonotonicTime time = erts_get_monotonic_time(esdp); -#ifdef ARCH_64 - ERTS_PROC_SET_DIRTY_CPU_START(c_p, (void *) time); -#else - ErtsMonotonicTime *stimep; - - stimep = (ErtsMonotonicTime *) ERTS_PROC_GET_DIRTY_CPU_START(c_p); - if (!stimep) { - stimep = erts_alloc(ERTS_ALC_T_DIRTY_START, - sizeof(ErtsMonotonicTime)); - ERTS_PROC_SET_DIRTY_CPU_START(c_p, (void *) stimep); - } - *stimep = time; -#endif - } -#endif -} - -static ERTS_INLINE int -get_dirty_reds(ErtsSchedulerData *esdp, Process *c_p) -{ - -#ifndef ERTS_DIRTY_SCHEDULERS - return -1; -#else - ErtsMonotonicTime stime, time; - - if (!ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(esdp->run_queue)) - return 1; - -#ifdef ARCH_64 - stime = (ErtsMonotonicTime) ERTS_PROC_GET_DIRTY_CPU_START(c_p); -#else - { - ErtsMonotonicTime *stimep; - stimep = (ErtsMonotonicTime *) ERTS_PROC_GET_DIRTY_CPU_START(c_p); - ASSERT(stimep); - stime = *stimep; - } -#endif - - time = erts_get_monotonic_time(esdp); - - ASSERT(stime && stime < time); - - time -= stime; - time = ERTS_MONOTONIC_TO_USEC(time); - time *= 2; - - if (time > INT_MAX) - return INT_MAX; - return (int) time; -#endif - -} - /* * schedule() is called from BEAM (process_main()) or HiPE * (hipe_mode_switch()) when the current process is to be @@ -9466,11 +9395,10 @@ get_dirty_reds(ErtsSchedulerData *esdp, Process *c_p) * so that normal processes get to run more frequently. */ -Process *schedule(Process *p, int calls) +Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) { Process *proxy_p = NULL; ErtsRunQueue *rq; - ErtsSchedulerData *esdp; int context_reds; int fcalls; int input_reductions; @@ -9507,8 +9435,19 @@ Process *schedule(Process *p, int calls) * Clean up after the process being scheduled out. */ if (!p) { /* NULL in the very first schedule() call */ +#ifdef ERTS_DIRTY_SCHEDULERS + is_normal_sched = !esdp; + if (is_normal_sched) { + esdp = erts_get_scheduler_data(); + ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); + } + else { + ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp)); + } +#else esdp = erts_get_scheduler_data(); - is_normal_sched = !ERTS_SCHEDULER_IS_DIRTY(esdp); + is_normal_sched = 1; +#endif rq = erts_get_runq_current(esdp); ASSERT(esdp); fcalls = (int) erts_smp_atomic32_read_acqb(&function_calls); @@ -9517,12 +9456,12 @@ Process *schedule(Process *p, int calls) } else { #ifdef ERTS_SMP #ifdef ERTS_DIRTY_SCHEDULERS - esdp = p->scheduler_data; - is_normal_sched = esdp != NULL; - if (is_normal_sched) + is_normal_sched = !esdp; + if (is_normal_sched) { + esdp = p->scheduler_data; ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); + } else { - esdp = erts_get_scheduler_data(); ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp)); } #else @@ -9541,10 +9480,7 @@ Process *schedule(Process *p, int calls) ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); - if (is_normal_sched) - reds = actual_reds = calls - esdp->virtual_reds; - else - reds = actual_reds = get_dirty_reds(esdp, p); + reds = actual_reds = calls - esdp->virtual_reds; ASSERT(actual_reds >= 0); if (reds < ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST) @@ -9994,17 +9930,10 @@ Process *schedule(Process *p, int calls) calls = 0; reds = context_reds; -#ifdef ERTS_SMP - erts_smp_runq_unlock(rq); -#endif /* ERTS_SMP */ - } - if (!is_normal_sched) - save_dirty_start(esdp, p); - #ifdef ERTS_SMP if (flags & ERTS_RUNQ_FLG_PROTECTED) @@ -11745,8 +11674,6 @@ delete_process(Process* p) if (nif_export) erts_destroy_nif_export(nif_export); - clean_dirty_start(p); - /* Cleanup psd */ psd = (ErtsPSD *) erts_smp_atomic_read_nob(&p->psd); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index b44ac442aa..7569fd81bd 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -810,25 +810,13 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi) #define ERTS_PSD_DELAYED_GC_TASK_QS 4 #define ERTS_PSD_NIF_TRAP_EXPORT 5 #define ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF 6 -#define ERTS_PSD_DIRTY_CPU_START 7 -#define ERTS_PSD_SIZE 8 +#define ERTS_PSD_SIZE 7 -#if !defined(HIPE) && !defined(ERTS_DIRTY_SCHEDULERS) +#if !defined(HIPE) # undef ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF -# undef ERTS_PSD_DIRTY_CPU_START # undef ERTS_PSD_SIZE # define ERTS_PSD_SIZE 6 -#elif !defined(HIPE) -# undef ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF -# undef ERTS_PSD_DIRTY_CPU_START -# undef ERTS_PSD_SIZE -# define ERTS_PSD_DIRTY_CPU_START 6 -# define ERTS_PSD_SIZE 7 -#elif !defined(ERTS_DIRTY_SCHEDULERS) -# undef ERTS_PSD_DIRTY_CPU_START -# undef ERTS_PSD_SIZE -# define ERTS_PSD_SIZE 7 #endif typedef struct { @@ -1831,7 +1819,7 @@ Eterm erts_get_schedulers_binds(Process *c_p); Eterm erts_set_cpu_topology(Process *c_p, Eterm term); Eterm erts_bind_schedulers(Process *c_p, Eterm how); ErtsRunQueue *erts_schedid2runq(Uint); -Process *schedule(Process*, int); +Process *erts_schedule(ErtsSchedulerData *, Process*, int); void erts_schedule_misc_op(void (*)(void *), void *); Eterm erl_create_process(Process*, Eterm, Eterm, Eterm, ErlSpawnOpts*); void erts_do_exit_process(Process*, Eterm); @@ -2061,13 +2049,6 @@ erts_psd_set(Process *p, int ix, void *data) ((struct saved_calls *) erts_psd_set((P), ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF, (void *) (SCB))) #endif -#ifdef ERTS_DIRTY_SCHEDULERS -#define ERTS_PROC_GET_DIRTY_CPU_START(P) \ - ((void *) erts_psd_get((P), ERTS_PSD_DIRTY_CPU_START)) -#define ERTS_PROC_SET_DIRTY_CPU_START(P, DCS) \ - ((void *) erts_psd_set((P), ERTS_PSD_DIRTY_CPU_START, (void *) (DCS))) -#endif - ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p); ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p, Eterm handler); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 15253bb53e..f3d4ac56cd 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -62,7 +62,8 @@ struct enif_environment_t /* ErlNifEnv */ extern void erts_pre_nif(struct enif_environment_t*, Process*, struct erl_module_nif*, Process* tracee); extern void erts_post_nif(struct enif_environment_t* env); -extern void erts_pre_dirty_nif(struct enif_environment_t*, Process*, +extern void erts_pre_dirty_nif(ErtsSchedulerData *, + struct enif_environment_t*, Process*, struct erl_module_nif*, Process* tracee); extern Eterm erts_nif_taints(Process* p); extern void erts_print_nif_taints(int to, void* to_arg); @@ -1154,7 +1155,7 @@ void print_pass_through(int, byte*, int); int catchlevel(Process*); void init_emulator(void); void process_main(void); -void dirty_process_main(void); +void erts_dirty_process_main(ErtsSchedulerData *); Eterm build_stacktrace(Process* c_p, Eterm exc); Eterm expand_error_value(Process* c_p, Uint freason, Eterm Value); void erts_save_stacktrace(Process* p, struct StackTrace* s, int depth); diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 884331e969..ed95045292 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -547,7 +547,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) p->flags &= ~F_HIPE_MODE; ERTS_SMP_UNREQ_PROC_MAIN_LOCK(p); - p = schedule(p, reds_in - p->fcalls); + p = erts_schedule(NULL, p, reds_in - p->fcalls); ERTS_SMP_REQ_PROC_MAIN_LOCK(p); ASSERT(!(p->flags & F_HIPE_MODE)); #ifdef ERTS_SMP -- cgit v1.2.3 From 24da853729d1f02a8c8a3aec1f0c992e00b5ca3f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 31 May 2016 17:36:07 +0200 Subject: erts: Fix possible race in poller wakeup on windows Similar bug that was fixed for unix in 7bbb207b30360c60fb99653. --- erts/emulator/sys/win32/erl_poll.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 94f3840b5f..f23c7ab03d 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -424,7 +424,7 @@ static ERTS_INLINE int wakeup_cause(ErtsPollSet ps) { int res; - erts_aint32_t wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state); + erts_aint32_t wakeup_state = erts_atomic32_read_acqb(&ps->wakeup_state); switch (wakeup_state) { case ERTS_POLL_WOKEN_IO_READY: res = 0; @@ -487,9 +487,8 @@ wake_poller(ErtsPollSet ps, int io_ready) { erts_aint32_t wakeup_state; if (io_ready) { - /* We may set the event multiple times. This is, however, harmless. */ - wakeup_state = erts_atomic32_read_nob(&ps->wakeup_state); - erts_atomic32_set_relb(&ps->wakeup_state, ERTS_POLL_WOKEN_IO_READY); + wakeup_state = erts_atomic32_xchg_relb(&ps->wakeup_state, + ERTS_POLL_WOKEN_IO_READY); } else { ERTS_THR_MEMORY_BARRIER; -- cgit v1.2.3 From 4d15511d6fe25ed15b6c1db00c23f1f4db1e9bb6 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 31 May 2016 18:41:58 +0200 Subject: Properly close message factories in erts_factory_trim_and_close() --- erts/emulator/beam/erl_message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index ac7b9d6606..ea525b50dd 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1725,7 +1725,7 @@ void erts_factory_trim_and_close(ErtsHeapFactory* factory, case FACTORY_MESSAGE: { ErtsMessage *mp = factory->message; if (mp->data.attached == ERTS_MSG_COMBINED_HFRAG) { - if (!mp->hfrag.next) { + if (!factory->heap_frags) { Uint sz = factory->hp - factory->hp_start; mp = erts_shrink_message(mp, sz, brefs, brefs_size); factory->message = mp; -- cgit v1.2.3 From fa0d9d191393e74a579f9c2cacb31b18f715ad6c Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 31 May 2016 20:00:30 +0200 Subject: Fix bad assertion --- erts/emulator/beam/erl_message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index ea525b50dd..71ab92937d 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1345,7 +1345,7 @@ erts_prep_msgq_for_inspection(Process *c_p, Process *rp, mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next; - ASSERT((*mpp)->next == bad_mp); + ASSERT(*mpp == bad_mp); erts_msgq_update_internal_pointers(&rp->msg, mpp, &bad_mp->next); -- cgit v1.2.3 From 9e4d723e601f720e024237e56c6f302ce5125831 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 31 May 2016 21:03:47 +0200 Subject: Update process state flags in etp-commands --- erts/emulator/beam/erl_process.h | 3 +++ erts/etc/unix/etp-commands.in | 25 +++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 7569fd81bd..7c98b60647 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1167,6 +1167,9 @@ void erts_check_for_holes(Process* p); * USR_PRIO -> User prio. i.e., prio the user has set. * PRQ_PRIO -> Prio queue prio, i.e., prio queue currently * enqueued in. + * + * Update etp-proc-state-int in $ERL_TOP/erts/etc/unix/etp-commands.in + * when changing ERTS_PSFLG_*. */ #define ERTS_PSFLGS_ACT_PRIO_MASK \ (ERTS_PSFLGS_PRIO_MASK << ERTS_PSFLGS_ACT_PRIO_OFFSET) diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 4dc24d68b4..8f9945c4b4 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -1657,8 +1657,29 @@ end define etp-proc-state-int # Args: int # - if ($arg0 & 0xff000000) - printf "GARBAGE | " + if ($arg0 & 0x80000000) + printf "GARBAGE<0x80000000> | " + end + if ($arg0 & 0x40000000) + printf "dirty-running-sys | " + end + if ($arg0 & 0x20000000) + printf "dirty-running | " + end + if ($arg0 & 0x10000000) + printf "dirty-active-sys | " + end + if ($arg0 & 0x8000000) + printf "dirty-io-proc | " + end + if ($arg0 & 0x4000000) + printf "dirty-cpu-proc | " + end + if ($arg0 & 0x2000000) + printf "on-heap-msgq | " + end + if ($arg0 & 0x1000000) + printf "off-heap-msgq | " end if ($arg0 & 0x800000) printf "delayed-sys | " -- cgit v1.2.3 From 011954e851d421b882b0b4eaeda04cfc2895d70a Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Wed, 4 May 2016 14:09:04 +0200 Subject: Rewrite inet_drv for AF_LOCAL --- erts/emulator/drivers/common/inet_drv.c | 384 ++++++++++++++++++-------------- erts/preloaded/ebin/prim_inet.beam | Bin 72716 -> 76488 bytes erts/preloaded/src/prim_inet.erl | 341 +++++++++++++++++++--------- 3 files changed, 456 insertions(+), 269 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index bd4543f831..71e321df26 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2015. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -712,7 +713,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #ifdef HAVE_SOCKLEN_T # define SOCKLEN_T socklen_t #else -# define SOCKLEN_T int +# define SOCKLEN_T size_t #endif #include "packet_parser.h" @@ -726,6 +727,22 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) (((unsigned char*) (s))[1] << 8) | \ (((unsigned char*) (s))[0])) +/* strnlen doesn't exist everywhere */ +static size_t my_strnlen(const char *s, size_t maxlen) +{ + size_t i = 0; + while (i < maxlen && s[i] != '\0') + i++; + return i; +} + +/* Check that some character in the buffer != '\0' */ +static int is_nonzero(const char *s, size_t n) +{ + size_t i; + for (i = 0; i < n; i++) if (s[i] != '\0') return !0; + return 0; +} #ifdef VALGRIND # include @@ -751,6 +768,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */ #define INET_AF_LOOPBACK 4 /* INADDR_LOOPBACK or IN6ADDR_LOOPBACK_INIT */ #define INET_AF_LOCAL 5 +#define INET_AF_UNDEFINED 6 /* Unknown */ /* open and INET_REQ_GETTYPE enumeration */ #define INET_TYPE_STREAM 1 @@ -1042,23 +1060,29 @@ typedef union { } inet_address; -/* for AF_INET & AF_INET6 */ -#define inet_address_port(x) ((x)->sai.sin_port) +#define inet_address_port(x) \ + ((((x)->sai.sin_family == AF_INET) || \ + ((x)->sai.sin_family == AF_INET6)) ? \ + ((x)->sai.sin_port) : -1) #ifdef HAVE_SYS_UN_H -#define localaddrlen(family, data) \ - ((family == AF_LOCAL) ? *(unsigned char*)(data) : 0) +#define localaddrlen(data) \ + ((((unsigned char*)(data))[0] == INET_AF_LOCAL) ? \ + (1 + 1 + ((unsigned char*)(data))[1]) : 1) #else - 0 +#define localaddrlen(data) (1) #endif #if defined(HAVE_IN6) && defined(AF_INET6) -#define addrlen(family, data) \ - ((family == AF_INET) ? sizeof(struct in_addr) : \ - ((family == AF_INET6) ? sizeof(struct in6_addr) : localaddrlen(family, data))) +#define addrlen(data) \ + ((((unsigned char*)(data))[0] == INET_AF_INET) ? \ + (1 + 2 + 4) : \ + ((((unsigned char*)(data))[0] == INET_AF_INET6) ? \ + (1 + 2 + 16) : localaddrlen(data))) #else -#define addrlen(family, data) \ - ((family == AF_INET) ? sizeof(struct in_addr) : localaddrlen(family, data)) +#define addrlen(data) \ + ((((unsigned char*)(data))[0] == INET_AF_INET) ? \ + (1 + 2 + 4) : localaddrlen(data)) #endif typedef struct _multi_timer_data { @@ -1149,8 +1173,10 @@ typedef struct { inet_address peer_addr; /* fake peer address */ inet_address name_addr; /* fake local address */ - inet_address* peer_ptr; /* fake peername or NULL */ - inet_address* name_ptr; /* fake sockname or NULL */ + inet_address* peer_ptr; /* fake peername or NULL */ + inet_address* name_ptr; /* fake sockname or NULL */ + SOCKLEN_T peer_addr_len; /* fake peername size */ + SOCKLEN_T name_addr_len; /* fake sockname size */ int bufsz; /* minimum buffer constraint */ unsigned int hsz; /* the list header size, -1 is large !!! */ @@ -1489,6 +1515,7 @@ static int async_ref = 0; /* async reference id generator */ } while (0) static ErlDrvTermData am_ok; +static ErlDrvTermData am_undefined; static ErlDrvTermData am_tcp; static ErlDrvTermData am_error; static ErlDrvTermData am_einval; @@ -1505,6 +1532,7 @@ static ErlDrvTermData am_ssl_tls; static ErlDrvTermData am_udp; static ErlDrvTermData am_udp_passive; static ErlDrvTermData am_udp_error; +static ErlDrvTermData am_local; #endif #ifdef HAVE_SCTP static ErlDrvTermData am_sctp; @@ -1714,46 +1742,59 @@ static void *realloc_wrapper(void *current, ErlDrvSizeT size){ #endif #ifdef HAVE_UDP -static int load_ip_port(ErlDrvTermData* spec, int i, char* buf) -{ - spec[i++] = ERL_DRV_INT; - spec[i++] = (ErlDrvTermData) get_int16(buf); - return i; -} - -static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) +static int load_address(ErlDrvTermData* spec, int i, char* buf) { int n; - if (family == AF_INET) { - for (n = 0; n < 4; n++) { + switch (*buf++) { /* Family */ + case INET_AF_INET: { + for (n = 2; n < 2+4; n++) { spec[i++] = ERL_DRV_INT; spec[i++] = (ErlDrvTermData) ((unsigned char)buf[n]); } spec[i++] = ERL_DRV_TUPLE; spec[i++] = 4; + spec[i++] = ERL_DRV_INT; + spec[i++] = (ErlDrvTermData) get_int16(buf); + break; } #if defined(HAVE_IN6) && defined(AF_INET6) - else if (family == AF_INET6) { - for (n = 0; n < 16; n += 2) { + case INET_AF_INET6: { + for (n = 2; n < 2+16; n += 2) { spec[i++] = ERL_DRV_INT; spec[i++] = (ErlDrvTermData) get_int16(buf+n); } spec[i++] = ERL_DRV_TUPLE; spec[i++] = 8; + spec[i++] = ERL_DRV_INT; + spec[i++] = (ErlDrvTermData) get_int16(buf); + break; } #endif #ifdef HAVE_SYS_UN_H - else if (family == AF_LOCAL) { + case INET_AF_LOCAL: { int len = *(unsigned char*)buf++; - i = LOAD_STRING(spec, i, buf, len); + i = LOAD_ATOM(spec, i, am_local); + i = LOAD_BUF2BINARY(spec, i, buf, len); + spec[i++] = ERL_DRV_TUPLE; + spec[i++] = 2; + spec[i++] = ERL_DRV_INT; + spec[i++] = 0; + break; } #endif - else { + default: { /* INET_AF_UNDEFINED */ + i = LOAD_ATOM(spec, i, am_undefined); + spec[i++] = ERL_DRV_INT; + spec[i++] = 0; spec[i++] = ERL_DRV_TUPLE; + spec[i++] = 2; + spec[i++] = ERL_DRV_INT; spec[i++] = 0; + break; + } } return i; -} + } #endif @@ -1761,10 +1802,13 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) /* For SCTP, we often need to return {IP, Port} tuples: */ static int inet_get_address(char* dst, inet_address* src, unsigned int* len); -#define LOAD_IP_AND_PORT_CNT \ +/* Max of {{int()*8},int()} | {{int()*4},int()} | + * {{'local',binary()},int()} + */ +#define LOAD_INET_GET_ADDRESS_CNT \ (8*LOAD_INT_CNT + LOAD_TUPLE_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT) -static int load_ip_and_port +static int load_inet_get_address (ErlDrvTermData* spec, int i, inet_descriptor* desc, struct sockaddr_storage* addr) { @@ -1782,8 +1826,7 @@ static int load_ip_and_port /* NB: the following functions are safe to use, as they create tuples of copied Ints on the "spec", and do not install any String pts -- a ptr to "abuf" would be dangling upon exiting this function: */ - i = load_ip_address(spec, i, desc->sfamily, abuf+3); - i = load_ip_port (spec, i, abuf+1); + i = load_address(spec, i, abuf); /* IP,Port | Family,Addr */ i = LOAD_TUPLE (spec, i, 2); return i; } @@ -2475,7 +2518,6 @@ static ErlDrvTermData am_http_error; static ErlDrvTermData am_abs_path; static ErlDrvTermData am_absoluteURI; static ErlDrvTermData am_star; -static ErlDrvTermData am_undefined; static ErlDrvTermData am_http; static ErlDrvTermData am_https; static ErlDrvTermData am_scheme; @@ -3166,7 +3208,7 @@ static int sctp_parse_async_event ASSERT(sptr->spc_length <= sz); /* No buffer overrun */ i = LOAD_ATOM (spec, i, am_sctp_paddr_change); - i = load_ip_and_port(spec, i, desc, &sptr->spc_aaddr); + i = load_inet_get_address(spec, i, desc, &sptr->spc_aaddr); switch (sptr->spc_state) { @@ -3617,18 +3659,12 @@ static int packet_binary_message i = LOAD_ATOM(spec, i, am_udp ); /* UDP only */ # endif i = LOAD_PORT(spec, i, desc->dport); /* S */ - - alen = addrlen(desc->sfamily, data+3); - i = load_ip_address(spec, i, desc->sfamily, data+3); - i = load_ip_port(spec, i, data+1); /* IP, Port */ - -# ifdef HAVE_SYS_UN_H - /* AF_LOCAL addresses have a prefix byte containing address length */ - if (desc->sfamily == AF_LOCAL) - alen++; -# endif - offs += (alen + 3); - len -= (alen + 3); + + alen = addrlen(data); + i = load_address(spec, i, data); /* IP,Port | Family,Addr */ + + offs += alen; + len -= alen; # ifdef HAVE_SCTP if (!IS_SCTP(desc)) @@ -4015,6 +4051,7 @@ static int inet_init() # endif INIT_ATOM(ok); + INIT_ATOM(undefined); INIT_ATOM(tcp); #ifdef HAVE_UDP INIT_ATOM(udp); @@ -4031,6 +4068,7 @@ static int inet_init() #ifdef HAVE_UDP INIT_ATOM(udp_passive); INIT_ATOM(udp_error); + INIT_ATOM(local); #endif INIT_ATOM(empty_out_q); INIT_ATOM(ssl_tls); @@ -4043,7 +4081,6 @@ static int inet_init() INIT_ATOM(abs_path); INIT_ATOM(absoluteURI); am_star = driver_mk_atom("*"); - INIT_ATOM(undefined); INIT_ATOM(http); INIT_ATOM(https); INIT_ATOM(scheme); @@ -4139,7 +4176,7 @@ static int inet_init() /* -** Set a inaddr structure: +** Set an inaddr structure: ** src = [P1,P0,X1,X2,.....] ** dst points to a structure large enugh to keep any kind ** of inaddr. @@ -4180,13 +4217,15 @@ static char* inet_set_address(int family, inet_address* dst, } #endif #ifdef HAVE_SYS_UN_H - else if ((family == AF_LOCAL) && (*len >= 3+sizeof(struct sockaddr_un))) { - int n = *((unsigned char*)src+2); - dst->sal.sun_family = family; - sys_memcpy(dst->sal.sun_path, src+3, n); - dst->sal.sun_path[n-1] = '\0'; - *len = n; - return src + 3 + n; + else if ((family == AF_LOCAL) && (*len >= 1)) { + int n = *((unsigned char*)src); + if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) + return NULL; + sys_memzero((char*)dst, sizeof(struct sockaddr_un)); + dst->sal.sun_family = family; + sys_memcpy(dst->sal.sun_path, src+1, n); + *len = offsetof(struct sockaddr_un, sun_path) + n; + return src + 1 + n; } #endif return NULL; @@ -4217,17 +4256,8 @@ static char *inet_set_faddress(int family, inet_address* dst, # endif # ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: { - int n; - if (*len || *len < 3) return NULL; - family = AF_LOCAL; - /* Next two bytes are the length of the local path (< 256) */ - src++; - n = *(unsigned char*)src++; - if (n+3 > *len) return NULL; - dst->sal.sun_family = family; - sys_memcpy(dst->sal.sun_path, src, n); - *len = n; - break; + family = AF_LOCAL; + break; } # endif case INET_AF_ANY: @@ -4303,6 +4333,7 @@ static char *inet_set_faddress(int family, inet_address* dst, */ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) { + /* Compare the code with inet_address_to_erlang() */ int family; short port; @@ -4326,51 +4357,32 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) } #endif #ifdef HAVE_SYS_UN_H - else if ((family == AF_LOCAL) && *len > 0) { - int n = *len - 4; - dst[0] = INET_AF_LOCAL; - put_int16(0, dst+1); - if (n == 0 || n >= sizeof(src->sal.sun_path)) { - *(dst+3) = 0; - *len = 3+1; - } else { - *(dst+3) = n; - sys_memcpy(dst+4, src->sal.sun_path, n); - *len = 3+1+n; - } - return 0; - } + else if (family == AF_LOCAL) { + size_t n, m; + if (*len < offsetof(struct sockaddr_un, sun_path)) return -1; + n = *len - offsetof(struct sockaddr_un, sun_path); + if (255 < n) return -1; + /* Portability fix: Assume that the address is a zero terminated + * string, except when the first byte is \0 i.e the + * string length is 0. Then use the reported length instead. + * This fix handles Linux's abstract socket address + * nonportable extension. + */ + m = my_strnlen(src->sal.sun_path, n); + if ((m == 0) && is_nonzero(src->sal.sun_path, n)) + m = n; + dst[0] = INET_AF_LOCAL; + dst[1] = (char) ((unsigned char) m); + sys_memcpy(dst+2, src->sal.sun_path, m); + *len = 1 + 1 + m; + return 0; + } #endif - return -1; -} - -static int inet_family_get_address(inet_descriptor* desc, char* dst, inet_address* src, unsigned int* len) -{ -#ifdef HAVE_SYS_UN_H - if (desc->sfamily == AF_LOCAL) { - int n = *len - 4; - dst[0] = INET_AF_LOCAL; - put_int16(0, dst+1); - if (n <= 0 || n >= sizeof(src->sal.sun_path)) { - if (desc->name_ptr) { - char* p = desc->name_ptr->sal.sun_path; - n = strlen(p); - *(dst+3) = n; - sys_memcpy(dst+4, p, n); - *len = 3+1+n; - } else { - *(dst+3) = 0; - *len = 3+1; - } - } else { - *(dst+3) = n; - sys_memcpy(dst+4, src->sal.sun_path, n); - *len = 3+1+n; - } - return 0; + else { + dst[0] = INET_AF_UNDEFINED; + *len = 1; } -#endif - return inet_get_address(dst, src, len); + return -1; } /* Same as the above, but take family from the address structure, @@ -4378,7 +4390,9 @@ static int inet_family_get_address(inet_descriptor* desc, char* dst, inet_addres ** according to the size of the current, ** and return the resulting encoded size */ -static int inet_address_to_erlang(char *dst, inet_address **src) { +static int +inet_address_to_erlang(char *dst, inet_address **src, SOCKLEN_T sz) { + /* Compare the code with inet_get_address() */ short port; switch ((*src)->sa.sa_family) { @@ -4405,15 +4419,26 @@ static int inet_address_to_erlang(char *dst, inet_address **src) { #endif #ifdef HAVE_SYS_UN_H case AF_LOCAL: { - int n = strlen((*src)->sal.sun_path); + size_t n, m; + if (sz < offsetof(struct sockaddr_un, sun_path)) return -1; + n = sz - offsetof(struct sockaddr_un, sun_path); + if (255 < n) return -1; + /* Portability fix: Assume that the address is a zero terminated + * string, except when the first byte is \0 i.e the + * string length is 0. Then use the reported length instead. + * This fix handles Linux's abstract socket address + * nonportable extension. + */ + m = my_strnlen((*src)->sal.sun_path, n); + if ((m == 0) && is_nonzero((*src)->sal.sun_path, n)) + m = n; if (dst) { dst[0] = INET_AF_LOCAL; - put_int16(0, dst+1); - *(dst+3) = n; - sys_memcpy(dst+1+2+1, (*src)->sal.sun_path, n); + dst[1] = (char) ((unsigned char) m); + sys_memcpy(dst+2, (*src)->sal.sun_path, m); } (*src) = (inet_address *) (&(*src)->sal + 1); - return 1+2+1+n; + return 1 + 1 + m; } #endif default: @@ -4424,7 +4449,7 @@ static int inet_address_to_erlang(char *dst, inet_address **src) { /* Encode n encoded addresses from addrs in the result buffer */ static ErlDrvSizeT reply_inet_addrs -(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize) { +(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize, SOCKLEN_T sz) { inet_address *ia; int i, s; ErlDrvSizeT rlen; @@ -4432,11 +4457,19 @@ static ErlDrvSizeT reply_inet_addrs if (IS_SOCKET_ERROR(n)) return ctl_error(sock_errno(), rbuf, rsize); if (n == 0) return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); + /* The sz argument is only used when we have got an actual size + * of addrs[0] from e.g getsockname() and then n == 1 + * so we will loop over 1 element below. Otherwise sz + * would be expected to differ between addresses but that + * can only happen for AF_LOCAL and we will only be called with + * n > 1 for SCTP and that will never (?) happen with AF_LOCAL + */ + /* Calculate result length */ rlen = 1; ia = addrs; for (i = 0; i < n; i++) { - s = inet_address_to_erlang(NULL, &ia); + s = inet_address_to_erlang(NULL, &ia, sz); if (s < 0) break; rlen += s; } @@ -4447,7 +4480,7 @@ static ErlDrvSizeT reply_inet_addrs rlen = 1; ia = addrs; for (i = 0; i < n; i++) { - s = inet_address_to_erlang((*rbuf)+rlen, &ia); + s = inet_address_to_erlang((*rbuf)+rlen, &ia, sz); if (s < 0) break; rlen += s; } @@ -4511,8 +4544,7 @@ static void desc_close_read(inet_descriptor* desc) static int erl_inet_close(inet_descriptor* desc) { free_subscribers(&desc->empty_out_q_subs); - if ((desc->prebound == 0 || desc->sfamily == AF_LOCAL) && - (desc->state & INET_F_OPEN)) { + if ((desc->prebound == 0) && (desc->state & INET_F_OPEN)) { desc_close(desc); desc->state = INET_STATE_CLOSED; } else if (desc->prebound && (desc->s != INVALID_SOCKET)) { @@ -4589,6 +4621,7 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, char** rbuf, ErlDrvSizeT rsize) { int save_errno; + int protocol; #ifdef HAVE_SETNS int current_ns, new_ns; current_ns = new_ns = 0; @@ -4627,7 +4660,11 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, } } #endif - if ((desc->s = sock_open(domain, type, desc->sprotocol)) == INVALID_SOCKET) + protocol = desc->sprotocol; +#ifdef HAVE_SYS_UN_H + if (domain == AF_LOCAL) protocol = 0; +#endif + if ((desc->s = sock_open(domain, type, protocol)) == INVALID_SOCKET) save_errno = sock_errno(); #ifdef HAVE_SETNS if (desc->netns != NULL) { @@ -4692,13 +4729,6 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, return ctl_error(sock_errno(), rbuf, rsize); if (name.sa.sa_family != domain) return ctl_error(EINVAL, rbuf, rsize); -#ifdef HAVE_SYS_UN_H - if (domain == AF_LOCAL) { - sys_memcpy(&desc->name_addr, &name, sizeof(desc->name_addr)); - if (desc->name_ptr == NULL) - desc->name_ptr = &desc->name_addr; - } -#endif } #ifdef __OSE__ /* for fdopen duplicating the sd will allow to uniquely identify @@ -7550,14 +7580,14 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, #ifdef HAVE_SCTP #define LOAD_PADDRINFO_CNT \ - (2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + LOAD_IP_AND_PORT_CNT + \ + (2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + LOAD_INET_GET_ADDRESS_CNT + \ 4*LOAD_INT_CNT + LOAD_TUPLE_CNT) static int load_paddrinfo (ErlDrvTermData * spec, int i, inet_descriptor* desc, struct sctp_paddrinfo* pai) { i = LOAD_ATOM (spec, i, am_sctp_paddrinfo); i = LOAD_ASSOC_ID (spec, i, pai->spinfo_assoc_id); - i = load_ip_and_port(spec, i, desc, &pai->spinfo_address); + i = load_inet_get_address(spec, i, desc, &pai->spinfo_address); switch(pai->spinfo_state) { case SCTP_ACTIVE: @@ -7973,7 +8003,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, /* Fill in the response: */ PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + - LOAD_IP_AND_PORT_CNT + 2*LOAD_TUPLE_CNT); + LOAD_INET_GET_ADDRESS_CNT + 2*LOAD_TUPLE_CNT); switch (eopt) { case SCTP_OPT_PRIMARY_ADDR: i = LOAD_ATOM(spec, i, am_sctp_primary_addr); @@ -7987,7 +8017,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, ASSERT(0); } i = LOAD_ASSOC_ID (spec, i, sp.sspp_assoc_id); - i = load_ip_and_port(spec, i, desc, &sp.sspp_addr); + i = load_inet_get_address(spec, i, desc, &sp.sspp_addr); i = LOAD_TUPLE (spec, i, 3); i = LOAD_TUPLE (spec, i, 2); break; @@ -8034,11 +8064,11 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, /* Fill in the response: */ PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + - LOAD_IP_AND_PORT_CNT + 4*LOAD_INT_CNT); + LOAD_INET_GET_ADDRESS_CNT + 4*LOAD_INT_CNT); i = LOAD_ATOM (spec, i, am_sctp_peer_addr_params); i = LOAD_ATOM (spec, i, am_sctp_paddrparams); i = LOAD_ASSOC_ID (spec, i, ap.spp_assoc_id); - i = load_ip_and_port(spec, i, desc, &ap.spp_address); + i = load_inet_get_address(spec, i, desc, &ap.spp_address); i = LOAD_INT (spec, i, ap.spp_hbinterval); i = LOAD_INT (spec, i, ap.spp_pathmaxrxt); @@ -8752,19 +8782,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, assoc_id = get_int32(buf); n = p_sctp_getpaddrs(desc->s, assoc_id, &sa); - rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize, 0); if (n > 0) p_sctp_freepaddrs(sa); return rlen; } #endif { /* Fallback to sock_peer */ inet_address addr; - unsigned int sz; + SOCKLEN_T sz; int i; sz = sizeof(addr); i = sock_peer(desc->s, (struct sockaddr *) &addr, &sz); - return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize, sz); } } @@ -8772,15 +8802,21 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, char tbuf[sizeof(inet_address)]; inet_address peer; inet_address* ptr; - unsigned int sz = sizeof(peer); + unsigned int sz; DEBUGF(("inet_ctl(%ld): PEER\r\n", (long)desc->port)); if (!(desc->state & INET_F_ACTIVE)) return ctl_error(ENOTCONN, rbuf, rsize); - if ((ptr = desc->peer_ptr) == NULL) { + if ((ptr = desc->peer_ptr) != NULL) { + sz = desc->peer_addr_len; + } + else { ptr = &peer; - if (IS_SOCKET_ERROR(sock_peer(desc->s, (struct sockaddr*)ptr,&sz))) + sz = sizeof(peer); + if (IS_SOCKET_ERROR + (sock_peer + (desc->s, (struct sockaddr*)ptr, &sz))) return ctl_error(sock_errno(), rbuf, rsize); } if (inet_get_address(tbuf, ptr, &sz) < 0) @@ -8795,11 +8831,12 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } else if (len < 2) return ctl_error(EINVAL, rbuf, rsize); - else if (inet_set_address(desc->sfamily, &desc->peer_addr, - buf, &len) == NULL) + else if (inet_set_faddress + (desc->sfamily, &desc->peer_addr, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); else { desc->peer_ptr = &desc->peer_addr; + desc->peer_addr_len = (SOCKLEN_T) len; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } } @@ -8821,19 +8858,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, assoc_id = get_int32(buf); n = p_sctp_getladdrs(desc->s, assoc_id, &sa); - rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize, 0); if (n > 0) p_sctp_freeladdrs(sa); return rlen; } #endif { /* Fallback to sock_name */ inet_address addr; - unsigned int sz; + SOCKLEN_T sz; int i; sz = sizeof(addr); i = sock_name(desc->s, (struct sockaddr *) &addr, &sz); - return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize, sz); } } @@ -8841,16 +8878,21 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, char tbuf[sizeof(inet_address)]; inet_address name; inet_address* ptr; - unsigned int sz = sizeof(name); + unsigned int sz; DEBUGF(("inet_ctl(%ld): NAME\r\n", (long)desc->port)); if (!IS_BOUND(desc)) return ctl_error(EINVAL, rbuf, rsize); /* address is not valid */ - if ((ptr = desc->name_ptr) == NULL) { + if ((ptr = desc->name_ptr) != NULL) { + sz = desc->name_addr_len; + } + else { ptr = &name; - if (IS_SOCKET_ERROR(sock_name(desc->s, (struct sockaddr*)ptr, &sz))) + sz = sizeof(name); + if (IS_SOCKET_ERROR + (sock_name(desc->s, (struct sockaddr*)ptr, &sz))) return ctl_error(sock_errno(), rbuf, rsize); } if (inet_get_address(tbuf, ptr, &sz) < 0) @@ -8858,18 +8900,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize); } - case INET_REQ_SETNAME: { /* set fake peername Port Address */ + case INET_REQ_SETNAME: { /* set fake sockname Port Address */ if (len == 0) { desc->name_ptr = NULL; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } else if (len < 2) return ctl_error(EINVAL, rbuf, rsize); - else if (inet_set_address(desc->sfamily, &desc->name_addr, - buf, &len) == NULL) + else if (inet_set_faddress + (desc->sfamily, &desc->name_addr, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); else { desc->name_ptr = &desc->name_addr; + desc->name_addr_len = (SOCKLEN_T) len; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } } @@ -8877,7 +8920,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, case INET_REQ_BIND: { /* bind socket */ char tbuf[2]; inet_address local; - short port; + int port; DEBUGF(("inet_ctl(%ld): BIND\r\n", (long)desc->port)); @@ -8894,13 +8937,14 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, desc->state = INET_STATE_BOUND; - if ((port = inet_address_port(&local)) == 0) { + port = inet_address_port(&local); + if (port == 0) { SOCKLEN_T adrlen = sizeof(local); sock_name(desc->s, &local.sa, &adrlen); port = inet_address_port(&local); } - port = sock_ntohs(port); - put_int16(port, tbuf); + else if (port == -1) port = 0; + put_int16(sock_ntohs((Uint16) port), tbuf); return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); } @@ -9469,8 +9513,8 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, timeout = get_int32(buf); buf += 4; len -= 4; - if (inet_set_address(desc->inet.sfamily, &desc->inet.remote, - buf, &len) == NULL) + if (inet_set_faddress + (desc->inet.sfamily, &desc->inet.remote, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); code = sock_connect(desc->inet.s, @@ -11528,6 +11572,9 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, case INET_AF_INET6: return ctl_xerror("eafnosupport", rbuf, rsize); break; +#endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: af = AF_LOCAL; break; #endif default: return ctl_error(EINVAL, rbuf, rsize); @@ -11611,7 +11658,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, /* For SCTP, we do not set the peer's addr in desc->remote, as multiple peers are possible: */ - if (inet_set_address(desc->sfamily, &remote, buf, &len) == NULL) + if (inet_set_faddress(desc->sfamily, &remote, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); sock_select(desc, FD_CONNECT, 1); @@ -11651,8 +11698,8 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, /* Ignore timeout */ buf += 4; len -= 4; - if (inet_set_address(desc->sfamily, - &desc->remote, buf, &len) == NULL) + if (inet_set_faddress + (desc->sfamily, &desc->remote, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); code = sock_connect(desc->s, @@ -11832,12 +11879,12 @@ static void packet_inet_timeout(ErlDrvData e) /* THIS IS A "send*" REQUEST; on the Erlang side: "port_command". -** input should be: P1 P0 Address buffer . +** input should be: Family Address buffer . ** For UDP, buffer (after Address) is just data to be sent. ** For SCTP, buffer contains a list representing 2 items: ** (1) 6 parms for sctp_sndrcvinfo, as in sctp_get_sendparams(); ** (2) 0+ real data bytes. -** There is no destination address -- SCTYP send is performed over +** There is no destination address -- SCTP send is performed over ** an existing association, using "sctp_sndrcvinfo" specified. */ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) @@ -11912,7 +11959,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) /* UDP socket. Even if it is connected, there is an address prefix here -- ignored for connected sockets: */ sz = len; - qtr = inet_set_address(desc->sfamily, &other, ptr, &sz); + qtr = inet_set_faddress(desc->sfamily, &other, ptr, &sz); if (qtr == NULL) { inet_reply_error(desc, EINVAL); return; @@ -12090,7 +12137,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) inet_input_count(desc, n); udesc->i_ptr += n; - inet_family_get_address(desc, abuf, &other, &len); + inet_get_address(abuf, &other, &len); /* Copy formatted address to the buffer allocated; "len" is the actual length which must be <= than the original reserved. This means that the addr + data in the buffer are contiguous, @@ -12624,9 +12671,8 @@ int erts_sock_connect(erts_sock_t socket, byte *ip_addr, int len, Uint16 port) if (!inet_set_address(AF_INET, &addr, buf, &blen)) return 0; - if (IS_SOCKET_ERROR(sock_connect(s, - (struct sockaddr *) &addr, - sizeof(struct sockaddr_in)))) + if (IS_SOCKET_ERROR + (sock_connect(s, (struct sockaddr *) &addr, blen))) return 0; return 1; } diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 72065661c5..1e2ba02ec2 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 4659448221..c6434fd83b 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -76,7 +76,7 @@ fdopen(Protocol, Family, Type, Fd) when is_integer(Fd) -> fdopen(Protocol, Family, Type, Fd, true). fdopen(Protocol, Family, Type, Fd, Bound) - when is_integer(Fd), Bound == true orelse Bound == false -> + when is_integer(Fd), is_boolean(Bound) -> open(Protocol, Family, Type, [], ?INET_REQ_FDOPEN, [?int32(Fd), enc_value_2(bool, Bound)]). @@ -192,41 +192,52 @@ close_port(S) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bind(S,IP,Port) when is_port(S), is_integer(Port), Port >= 0, Port =< 65535 -> - case ctl_cmd(S,?INET_REQ_BIND,enc_value(set, addr, {IP,Port})) of - {ok, [P1,P0]} -> {ok, ?u16(P1, P0)}; - {error,_}=Error -> Error - end; - %% Multi-homed "bind": sctp_bindx(). The Op is 'add' or 'remove'. %% If no addrs are specified, it just does nothing. %% Function returns {ok, S} on success, unlike TCP/UDP "bind": -bind(S, Op, Addrs) when is_port(S), is_list(Addrs) -> - case Op of - add -> - bindx(S, 1, Addrs); - remove -> - bindx(S, 0, Addrs); - _ -> {error, einval} +bind(S, add, Addrs) when is_port(S), is_list(Addrs) -> + bindx(S, 1, Addrs); +bind(S, remove, Addrs) when is_port(S), is_list(Addrs) -> + bindx(S, 0, Addrs); +bind(S, Addr, _) when is_port(S), tuple_size(Addr) =:= 2 -> + case type_value(set, addr, Addr) of + true -> + case ctl_cmd(S,?INET_REQ_BIND,enc_value(set, addr, Addr)) of + {ok, [P1,P0]} -> {ok, ?u16(P1, P0)}; + {error, _} = Error -> Error + end; + false -> + {error, einval} end; -bind(_, _, _) -> {error, einval}. +bind(S, IP, Port) -> + bind(S, {IP, Port}, 0). bindx(S, AddFlag, Addrs) -> case getprotocol(S) of sctp -> - %% Really multi-homed "bindx". Stringified args: - %% [AddFlag, (AddrBytes see enc_value_2(addr,X))+]: - Args = - [?int8(AddFlag)| - [enc_value(set, addr, {IP,Port}) || - {IP, Port} <- Addrs]], - case ctl_cmd(S, ?SCTP_REQ_BINDX, Args) of - {ok,_} -> {ok, S}; - {error,_}=Error -> Error + case bindx_check_addrs(Addrs) of + true -> + %% Really multi-homed "bindx". Stringified args: + %% [AddFlag, (AddrBytes see enc_value_2(addr,X))+]: + Args = + [?int8(AddFlag)| + [enc_value(set, addr, Addr) || Addr <- Addrs]], + case ctl_cmd(S, ?SCTP_REQ_BINDX, Args) of + {ok, _} -> {ok, S}; + {error, _}=Error -> Error + end; + false -> + {error, einval} end; - _ -> {error, einval} + _ -> + {error, einval} end. +bindx_check_addrs([Addr|Addrs]) -> + type_value(set, addr, Addr) andalso bindx_check_addrs(Addrs); +bindx_check_addrs([]) -> + true. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% CONNECT(insock(), IP, Port [,Timeout]) -> ok | {error, Reason} @@ -245,14 +256,24 @@ bindx(S, AddFlag, Addrs) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% For TCP, UDP or SCTP sockets. %% -connect(S, IP, Port) -> connect0(S, IP, Port, -1). -connect(S, IP, Port, infinity) -> connect0(S, IP, Port, -1); -connect(S, IP, Port, Time) -> connect0(S, IP, Port, Time). +connect(S, IP, Port) -> + connect(S, IP, Port, infinity). +%% +connect(S, Addr, _, Time) when is_port(S), tuple_size(Addr) =:= 2 -> + case type_value(set, addr, Addr) of + true when Time =:= infinity -> + connect0(S, Addr, -1); + true when is_integer(Time) -> + connect0(S, Addr, Time); + false -> + {error, einval} + end; +connect(S, IP, Port, Time) -> + connect(S, {IP, Port}, 0, Time). -connect0(S, IP, Port, Time) when is_port(S), Port > 0, Port =< 65535, - is_integer(Time) -> - case async_connect(S, IP, Port, Time) of +connect0(S, Addr, Time) -> + case async_connect0(S, Addr, Time) of {ok, S, Ref} -> receive {inet_async, S, Ref, Status} -> @@ -261,11 +282,27 @@ connect0(S, IP, Port, Time) when is_port(S), Port > 0, Port =< 65535, Error -> Error end. + +async_connect(S, Addr, _, Time) when is_port(S), tuple_size(Addr) =:= 2 -> + case type_value(set, addr, Addr) of + true when Time =:= infinity -> + async_connect0(S, Addr, -1); + true when is_integer(Time) -> + async_connect0(S, Addr, Time); + false -> + {error, einval} + end; +%% async_connect(S, IP, Port, Time) -> - case ctl_cmd(S, ?INET_REQ_CONNECT, - [enc_time(Time),?int16(Port),ip_to_bytes(IP)]) of + async_connect(S, {IP, Port}, 0, Time). + +async_connect0(S, Addr, Time) -> + case ctl_cmd( + S, ?INET_REQ_CONNECT, + [enc_time(Time),enc_value(set, addr, Addr)]) + of {ok, [R1,R0]} -> {ok, S, ?u16(R1,R0)}; - {error,_}=Error -> Error + {error, _}=Error -> Error end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -403,20 +440,34 @@ send(S, Data) -> %% "sendto" is for UDP. IP and Port are set by the caller to 0 if the socket %% is known to be connected. -sendto(S, IP, Port, Data) when is_port(S), Port >= 0, Port =< 65535 -> - ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p, ~p)~n", [S,IP,Port,Data]), - try erlang:port_command(S, [?int16(Port),ip_to_bytes(IP),Data]) of - true -> - receive - {inet_reply,S,Reply} -> - ?DBG_FORMAT("prim_inet:sendto() -> ~p~n", [Reply]), - Reply - end - catch - error:_ -> - ?DBG_FORMAT("prim_inet:sendto() -> {error,einval}~n", []), - {error,einval} - end. +sendto(S, Addr, _, Data) when is_port(S), tuple_size(Addr) =:= 2 -> + case type_value(set, addr, Addr) of + true -> + ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p)~n", [S,Addr,Data]), + try + erlang:port_command(S, [enc_value(set, addr, Addr),Data]) + of + true -> + receive + {inet_reply,S,Reply} -> + ?DBG_FORMAT( + "prim_inet:sendto() -> ~p~n", [Reply]), + Reply + end + catch + error:_ -> + ?DBG_FORMAT( + "prim_inet:sendto() -> {error,einval}~n", []), + {error,einval} + end; + false -> + ?DBG_FORMAT( + "prim_inet:sendto() -> {error,einval}~n", []), + {error,einval} + end; +sendto(S, IP, Port, Data) -> + sendto(S, {IP, Port}, 0, Data). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% @@ -487,29 +538,37 @@ async_recv(S, Length, Time) -> %% oriented: preserved here only for API compatibility. %% recvfrom(S, Length) -> - recvfrom0(S, Length, -1). + recvfrom(S, Length, infinity). -recvfrom(S, Length, infinity) -> +recvfrom(S, Length, infinity) when is_port(S) -> recvfrom0(S, Length, -1); -recvfrom(S, Length, Time) when is_integer(Time), Time < 16#ffffffff -> - recvfrom0(S, Length, Time); -recvfrom(_, _, _) -> {error,einval}. +recvfrom(S, Length, Time) when is_port(S) -> + if + is_integer(Time), 0 =< Time, Time < 16#ffffffff -> + recvfrom0(S, Length, Time); + true -> + {error, einval} + end. recvfrom0(S, Length, Time) - when is_port(S), is_integer(Length), Length >= 0, Length =< 16#ffffffff -> + when is_integer(Length), 0 =< Length, Length =< 16#ffffffff -> case ctl_cmd(S, ?PACKET_REQ_RECV,[enc_time(Time),?int32(Length)]) of {ok,[R1,R0]} -> Ref = ?u16(R1,R0), receive % Success, UDP: - {inet_async, S, Ref, {ok, [F,P1,P0 | AddrData]}} -> - {IP,Data} = get_ip(F, AddrData), - {ok, {IP, ?u16(P1,P0), Data}}; + {inet_async, S, Ref, {ok, [F | AddrData]}} -> + case get_addr(F, AddrData) of + {{Family, _} = Addr, Data} when is_atom(Family) -> + {ok, {Addr, 0, Data}}; + {{IP, Port}, Data} -> + {ok, {IP, Port, Data}} + end; % Success, SCTP: {inet_async, S, Ref, {ok, {[F,P1,P0 | Addr], AncData, DE}}} -> - {IP, _} = get_ip(F, Addr), - {ok, {IP, ?u16(P1,P0), AncData, DE}}; + {IP, _} = get_ip(F, Addr), + {ok, {IP, ?u16(P1, P0), AncData, DE}}; % Back-end error: {inet_async, S, Ref, Error={error, _}} -> @@ -528,21 +587,26 @@ recvfrom0(_, _, _) -> {error,einval}. peername(S) when is_port(S) -> case ctl_cmd(S, ?INET_REQ_PEER, []) of - {ok, [F, P1,P0 | Addr]} -> - {IP, _} = get_ip(F, Addr), - {ok, { IP, ?u16(P1, P0) }}; - {error,_}=Error -> Error + {ok, [F | Addr]} -> + {A, _} = get_addr(F, Addr), + {ok, A}; + {error, _} = Error -> Error end. -setpeername(S, {IP,Port}) when is_port(S) -> - case ctl_cmd(S, ?INET_REQ_SETPEER, [?int16(Port),ip_to_bytes(IP)]) of - {ok,[]} -> ok; - {error,_}=Error -> Error - end; setpeername(S, undefined) when is_port(S) -> case ctl_cmd(S, ?INET_REQ_SETPEER, []) of - {ok,[]} -> ok; - {error,_}=Error -> Error + {ok, []} -> ok; + {error, _} = Error -> Error + end; +setpeername(S, Addr) when is_port(S) -> + case type_value(set, addr, Addr) of + true -> + case ctl_cmd(S, ?INET_REQ_SETPEER, enc_value(set, addr, Addr)) of + {ok, []} -> ok; + {error, _} = Error -> Error + end; + false -> + {error, einval} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -583,21 +647,28 @@ peernames(S, AssocId) sockname(S) when is_port(S) -> case ctl_cmd(S, ?INET_REQ_NAME, []) of - {ok, [F, P1, P0 | Addr]} -> - {IP, _} = get_ip(F, Addr), - {ok, { IP, ?u16(P1, P0) }}; - {error,_}=Error -> Error + {ok, [F | Addr]} -> + {A, _} = get_addr(F, Addr), + {ok, A}; + {error, _} = Error -> Error end. -setsockname(S, {IP,Port}) when is_port(S) -> - case ctl_cmd(S, ?INET_REQ_SETNAME, [?int16(Port),ip_to_bytes(IP)]) of - {ok,[]} -> ok; - {error,_}=Error -> Error - end; setsockname(S, undefined) when is_port(S) -> case ctl_cmd(S, ?INET_REQ_SETNAME, []) of - {ok,[]} -> ok; - {error,_}=Error -> Error + {ok, []} -> ok; + {error, _} = Error -> Error + end; +setsockname(S, Addr) when is_port(S) -> + case type_value(set, addr, Addr) of + true -> + case + ctl_cmd(S, ?INET_REQ_SETNAME, enc_value(set, addr, Addr)) + of + {ok, []} -> ok; + {error, _} = Error -> Error + end; + false -> + {error, einval} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1499,14 +1570,49 @@ type_value_2(uint8, X) when X band 16#ff =:= X -> true; type_value_2(time, infinity) -> true; type_value_2(time, X) when is_integer(X), X >= 0 -> true; type_value_2(ip,{A,B,C,D}) when ?ip(A,B,C,D) -> true; +%% type_value_2(addr, {any,Port}) -> type_value_2(uint16, Port); type_value_2(addr, {loopback,Port}) -> type_value_2(uint16, Port); -type_value_2(addr, {{A,B,C,D},Port}) when ?ip(A,B,C,D) -> +type_value_2(addr, {IP,_} = Addr) when tuple_size(IP) =:= 4 -> + type_value_2(addr, {inet,Addr}); +type_value_2(addr, {IP,_} = Addr) when tuple_size(IP) =:= 8 -> + type_value_2(addr, {inet6,Addr}); +type_value_2(addr, {Local,_}) when is_list(Local); is_binary(Local) -> + type_value_2(addr, {local,Local}); +%% +type_value_2(addr, {Family,{Tag,Port}}) + when (Family =:= inet orelse Family =:= inet6) andalso + (Tag =:= any orelse Tag =:= loopback) -> + type_value_2(uint16, Port); +type_value_2(addr, {inet,{{A,B,C,D},Port}}) + when ?ip(A,B,C,D) -> type_value_2(uint16, Port); -type_value_2(addr, {{A,B,C,D,E,F,G,H},Port}) when ?ip6(A,B,C,D,E,F,G,H) -> +type_value_2(addr, {inet6,{{A,B,C,D,E,F,G,H},Port}}) + when ?ip6(A,B,C,D,E,F,G,H) -> type_value_2(uint16, Port); +type_value_2(addr, {local,Addr}) -> + if + is_binary(Addr) -> + byte_size(Addr) =< 255; + true -> + try + %% We either get a badarg from byte_size + %% or from characters_to_binary + byte_size( + unicode:characters_to_binary( + Addr, file:native_name_encoding())) + of + N when N =< 255 -> + true; + _ -> + false + catch error:badarg -> + false + end + end; +%% type_value_2(ether,[X1,X2,X3,X4,X5,X6]) when ?ether(X1,X2,X3,X4,X5,X6) -> true; type_value_2({enum,List}, Enum) -> @@ -1614,6 +1720,7 @@ enc_value_2(time, Val) -> ?int32(Val); enc_value_2(ip,{A,B,C,D}) -> [A,B,C,D]; enc_value_2(ip, any) -> [0,0,0,0]; enc_value_2(ip, loopback) -> [127,0,0,1]; +%% enc_value_2(addr, {any,Port}) -> [?INET_AF_ANY|?int16(Port)]; enc_value_2(addr, {loopback,Port}) -> @@ -1622,8 +1729,35 @@ enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 4 -> [?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)]; enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 8 -> [?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)]; -enc_value_2(addr, {File,0}) when is_list(File) -> - [?INET_AF_LOCAL,0,0,length(File)|File]; +enc_value_2(addr, {File,_}) when is_list(File); is_binary(File) -> + [?INET_AF_LOCAL,iolist_size(File)|File]; +%% +enc_value_2(addr, {inet,{any,Port}}) -> + [?INET_AF_INET,?int16(Port),0,0,0,0]; +enc_value_2(addr, {inet,{loopback,Port}}) -> + [?INET_AF_INET,?int16(Port),127,0,0,1]; +enc_value_2(addr, {inet,{IP,Port}}) -> + [?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)]; +enc_value_2(addr, {inet6,{any,Port}}) -> + [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,0]; +enc_value_2(addr, {inet6,{loopback,Port}}) -> + [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,1]; +enc_value_2(addr, {inet6,{IP,Port}}) -> + [?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)]; +enc_value_2(addr, {local,Addr}) -> + %% A binary is passed as is, but anything else will be + %% regarded as a filename and therefore UTF-8 encoded + %% if the system filename encoding flag so dictates. + Bin = + if + is_binary(Addr) -> + Addr; + true -> + unicode:characters_to_binary( + Addr, file:native_name_encoding()) + end, + [?INET_AF_LOCAL,byte_size(Bin)|Bin]; +%% enc_value_2(ether, [_,_,_,_,_,_]=Xs) -> Xs; enc_value_2(sockaddr, any) -> [?INET_AF_ANY]; @@ -1633,8 +1767,6 @@ enc_value_2(sockaddr, IP) when tuple_size(IP) =:= 4 -> [?INET_AF_INET|ip4_to_bytes(IP)]; enc_value_2(sockaddr, IP) when tuple_size(IP) =:= 8 -> [?INET_AF_INET6|ip6_to_bytes(IP)]; -enc_value_2(sockaddr, File) when is_list(File) -> - [?INET_AF_LOCAL,0,0,length(File)|File]; enc_value_2(linkaddr, Linkaddr) -> [?int16(length(Linkaddr)),Linkaddr]; enc_value_2(sctp_assoc_id, Val) -> ?int32(Val); @@ -2256,9 +2388,6 @@ utf8_to_characters(Bs, U, 0) -> utf8_to_characters([B|Bs], U, N) when ((B band 16#3F) bor 16#80) =:= B -> utf8_to_characters(Bs, (U bsl 6) bor (B band 16#3F), N-1). -ip_to_bytes(IP) when tuple_size(IP) =:= 4 -> ip4_to_bytes(IP); -ip_to_bytes(IP) when tuple_size(IP) =:= 8 -> ip6_to_bytes(IP). - ip4_to_bytes({A,B,C,D}) -> [A band 16#ff, B band 16#ff, C band 16#ff, D band 16#ff]. @@ -2268,20 +2397,32 @@ ip6_to_bytes({A,B,C,D,E,F,G,H}) -> get_addrs([]) -> []; -get_addrs([F,P1,P0|Addr]) -> - {IP,Addrs} = get_ip(F, Addr), - [{IP,?u16(P1, P0)}|get_addrs(Addrs)]. - -get_ip(?INET_AF_INET, Addr) -> get_ip4(Addr); -get_ip(?INET_AF_INET6, Addr) -> get_ip6(Addr); -get_ip(?INET_AF_LOCAL, [0]) -> {[], []}; -get_ip(?INET_AF_LOCAL, [N | Addr]) -> lists:split(N, Addr). +get_addrs([F|Addrs]) -> + {Addr,Rest} = get_addr(F, Addrs), + [Addr|get_addrs(Rest)]. + +get_addr(?INET_AF_LOCAL, [0]) -> + {{local,<<>>},[]}; +get_addr(?INET_AF_LOCAL, [N|Addr]) -> + {A,Rest} = lists:split(N, Addr), + {{local,iolist_to_binary(A)},Rest}; +get_addr(?INET_AF_UNDEFINED, Rest) -> + {{undefined,0},Rest}; +get_addr(Family, [P1,P0|Addr]) -> + {IP,Rest} = get_ip(Family, Addr), + {{IP,?u16(P1, P0)},Rest}. + +get_ip(?INET_AF_INET, Addr) -> + get_ip4(Addr); +get_ip(?INET_AF_INET6, Addr) -> + get_ip6(Addr). get_ip4([A,B,C,D | T]) -> {{A,B,C,D},T}. get_ip6([X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13,X14,X15,X16 | T]) -> { { ?u16(X1,X2),?u16(X3,X4),?u16(X5,X6),?u16(X7,X8), - ?u16(X9,X10),?u16(X11,X12),?u16(X13,X14),?u16(X15,X16)}, T}. + ?u16(X9,X10),?u16(X11,X12),?u16(X13,X14),?u16(X15,X16)}, + T }. %% Control command -- cgit v1.2.3 From 45a4a46333b1e8e6f66ba923deaf947a67ae7737 Mon Sep 17 00:00:00 2001 From: Simon Cornish <7t9jna402@sneakemail.com> Date: Mon, 25 Apr 2016 22:21:07 -0700 Subject: Don't kill old erlang if HEART_NO_KILL is set If the environment variable HEART_NO_KILL is set then heart won't kill the old erlang process. This is desirable if the command executed by heart takes care of this. --- erts/etc/common/heart.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'erts') diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index 1a826221fb..b6fbfd075c 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -119,6 +119,8 @@ #define HEART_COMMAND_ENV "HEART_COMMAND" #define ERL_CRASH_DUMP_SECONDS_ENV "ERL_CRASH_DUMP_SECONDS" #define HEART_KILL_SIGNAL "HEART_KILL_SIGNAL" +#define HEART_NO_KILL "HEART_NO_KILL" + #define MSG_HDR_SIZE (2) #define MSG_HDR_PLUS_OP_SIZE (3) @@ -524,6 +526,10 @@ static void kill_old_erlang(void){ HANDLE erlh; DWORD exit_code; + + if (is_env_set(HEART_NO_KILL)) + return; + if(heart_beat_kill_pid != 0){ if((erlh = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE | @@ -557,6 +563,9 @@ kill_old_erlang(void){ int sig = SIGKILL; char *sigenv = NULL; + if (is_env_set(HEART_NO_KILL)) + return; + sigenv = get_env(HEART_KILL_SIGNAL); if (sigenv && strcmp(sigenv, "SIGABRT") == 0) { print_error("kill signal SIGABRT requested"); -- cgit v1.2.3 From 7c133fb1094ad1cabbb5cfc157483a43c816c6a9 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 1 Jun 2016 16:13:33 +0200 Subject: erts: Change ETS hash load factor from 700% to 200% --- erts/emulator/beam/erl_db_hash.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 74979f984a..4c4ff9b470 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -95,7 +95,8 @@ /* * The following symbols can be manipulated to "tune" the linear hash array */ -#define CHAIN_LEN 6 /* Medium bucket chain len */ +#define GROW_LIMIT(NACTIVE) ((NACTIVE)*2) +#define SHRINK_LIMIT(NACTIVE) ((NACTIVE) / 2) /* Number of slots per segment */ #define SEGSZ_EXP 8 @@ -463,7 +464,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle); static ERTS_INLINE void try_shrink(DbTableHash* tb) { int nactive = NACTIVE(tb); - if (nactive > SEGSZ && NITEMS(tb) < (nactive * CHAIN_LEN) + if (nactive > SEGSZ && NITEMS(tb) < SHRINK_LIMIT(nactive) && !IS_FIXED(tb)) { shrink(tb, nactive); } @@ -862,7 +863,7 @@ Lnew: WUNLOCK_HASH(lck); { int nactive = NACTIVE(tb); - if (nitems > nactive * (CHAIN_LEN+1) && !IS_FIXED(tb)) { + if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) { grow(tb, nactive); } } @@ -2250,12 +2251,12 @@ static int db_free_table_continue_hash(DbTable *tbl) done /= 2; while(tb->nslots != 0) { - free_seg(tb, 1); + done += 1 + SEGSZ/64 + free_seg(tb, 1); /* * If we have done enough work, get out here. */ - if (++done >= (DELETE_RECORD_LIMIT / CHAIN_LEN / SEGSZ)) { + if (done >= DELETE_RECORD_LIMIT) { return 0; /* Not done */ } } @@ -2871,7 +2872,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj, int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems); int nactive = NACTIVE(tb); - if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) { + if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) { grow(tb, nactive); } } -- cgit v1.2.3 From f4bdac18cb9dd45185e911308a5ebd95ff10d7fd Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 1 Jun 2016 16:19:04 +0200 Subject: erts: Remove unnecessary access of 'is_resizing' in tables without write_concurrency and remove it totally #ifndef ERTS_SMP --- erts/emulator/beam/erl_db_hash.c | 21 ++++++++++----------- erts/emulator/beam/erl_db_hash.h | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 4c4ff9b470..074ac6d64e 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -671,8 +671,8 @@ int db_create_hash(Process *p, DbTable *tbl) tb->nsegs = NSEG_1; tb->nslots = SEGSZ; - erts_smp_atomic_init_nob(&tb->is_resizing, 0); #ifdef ERTS_SMP + erts_smp_atomic_init_nob(&tb->is_resizing, 0); if (tb->common.type & DB_FINE_LOCKED) { erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; int i; @@ -2605,23 +2605,22 @@ static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2, static ERTS_INLINE int begin_resizing(DbTableHash* tb) { +#ifdef ERTS_SMP if (DB_USING_FINE_LOCKING(tb)) - return !erts_smp_atomic_xchg_acqb(&tb->is_resizing, 1); - else { - if (erts_smp_atomic_read_nob(&tb->is_resizing)) - return 0; - erts_smp_atomic_set_nob(&tb->is_resizing, 1); - return 1; - } + return !erts_atomic_xchg_acqb(&tb->is_resizing, 1); + else + ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock)); +#endif + return 1; } static ERTS_INLINE void done_resizing(DbTableHash* tb) { +#ifdef ERTS_SMP if (DB_USING_FINE_LOCKING(tb)) - erts_smp_atomic_set_relb(&tb->is_resizing, 0); - else - erts_smp_atomic_set_nob(&tb->is_resizing, 0); + erts_atomic_set_relb(&tb->is_resizing, 0); +#endif } /* Grow table with one new bucket. diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h index e654363cd5..081ff8fafc 100644 --- a/erts/emulator/beam/erl_db_hash.h +++ b/erts/emulator/beam/erl_db_hash.h @@ -60,8 +60,8 @@ typedef struct db_table_hash { /* List of slots where elements have been deleted while table was fixed */ erts_smp_atomic_t fixdel; /* (FixedDeletion*) */ erts_smp_atomic_t nactive; /* Number of "active" slots */ - erts_smp_atomic_t is_resizing; /* grow/shrink in progress */ #ifdef ERTS_SMP + erts_smp_atomic_t is_resizing; /* grow/shrink in progress */ DbTableHashFineLocks* locks; #endif #ifdef VALGRIND -- cgit v1.2.3 From 58d52b1d2d3e9f14bda5b9248d9b25ae12535548 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 1 Jun 2016 16:22:09 +0200 Subject: erts: Avoid literals in process independent ErlNifEnv that would go undetected and cause havoc if module is purged. --- erts/emulator/beam/erl_nif.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index f1c35cd5a5..4fd82bad10 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -803,19 +803,13 @@ ERL_NIF_TERM enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term) { Uint sz; Eterm* hp; -#ifdef SHCOPY - erts_shcopy_t info; - INITIALIZE_SHCOPY(info); - sz = copy_shared_calculate(src_term, &info); - hp = alloc_heap(dst_env, sz); - src_term = copy_shared_perform(src_term, sz, &info, &hp, &MSO(dst_env->proc)); - DESTROY_SHCOPY(info); - return src_term; -#else + /* + * No preserved sharing allowed as long as literals are also preserved. + * Process independent environment can not be reached by purge. + */ sz = size_object(src_term); hp = alloc_heap(dst_env, sz); return copy_struct(src_term, sz, &hp, &MSO(dst_env->proc)); -#endif } -- cgit v1.2.3 From 03e6d8e02a187633a6a09c77e36670f0892d063d Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 2 Jun 2016 08:44:59 +0200 Subject: AF_UNIX is more portable Fix dialyzer warning for improper list in prim_inet by not using an improper list. --- erts/emulator/drivers/common/inet_drv.c | 26 +++++++++++++------------- erts/preloaded/src/prim_inet.erl | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 71e321df26..46adbb09f7 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -3639,7 +3639,7 @@ static int tcp_error_message(tcp_descriptor* desc, int err) ** {sctp, S, IP, Port, {[AncilData], Event_or_Data}} ** where ** [H1,...,HSz] are msg headers (without IP/Port, UDP only), -** [AddrLen, H2,...,HSz] are msg headers for UDP AF_LOCAL only +** [AddrLen, H2,...,HSz] are msg headers for UDP AF_UNIX only ** Data : List() | Binary() */ static int packet_binary_message @@ -4217,7 +4217,7 @@ static char* inet_set_address(int family, inet_address* dst, } #endif #ifdef HAVE_SYS_UN_H - else if ((family == AF_LOCAL) && (*len >= 1)) { + else if ((family == AF_UNIX) && (*len >= 1)) { int n = *((unsigned char*)src); if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) return NULL; @@ -4256,7 +4256,7 @@ static char *inet_set_faddress(int family, inet_address* dst, # endif # ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: { - family = AF_LOCAL; + family = AF_UNIX; break; } # endif @@ -4357,7 +4357,7 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) } #endif #ifdef HAVE_SYS_UN_H - else if (family == AF_LOCAL) { + else if (family == AF_UNIX) { size_t n, m; if (*len < offsetof(struct sockaddr_un, sun_path)) return -1; n = *len - offsetof(struct sockaddr_un, sun_path); @@ -4418,7 +4418,7 @@ inet_address_to_erlang(char *dst, inet_address **src, SOCKLEN_T sz) { return 1 + 2 + 16; #endif #ifdef HAVE_SYS_UN_H - case AF_LOCAL: { + case AF_UNIX: { size_t n, m; if (sz < offsetof(struct sockaddr_un, sun_path)) return -1; n = sz - offsetof(struct sockaddr_un, sun_path); @@ -4461,8 +4461,8 @@ static ErlDrvSizeT reply_inet_addrs * of addrs[0] from e.g getsockname() and then n == 1 * so we will loop over 1 element below. Otherwise sz * would be expected to differ between addresses but that - * can only happen for AF_LOCAL and we will only be called with - * n > 1 for SCTP and that will never (?) happen with AF_LOCAL + * can only happen for AF_UNIX and we will only be called with + * n > 1 for SCTP and that will never (?) happen with AF_UNIX */ /* Calculate result length */ @@ -4662,7 +4662,7 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, #endif protocol = desc->sprotocol; #ifdef HAVE_SYS_UN_H - if (domain == AF_LOCAL) protocol = 0; + if (domain == AF_UNIX) protocol = 0; #endif if ((desc->s = sock_open(domain, type, protocol)) == INVALID_SOCKET) save_errno = sock_errno(); @@ -8719,7 +8719,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } #endif #ifdef HAVE_SYS_UN_H - else if (desc->sfamily == AF_LOCAL) { + else if (desc->sfamily == AF_UNIX) { put_int32(INET_AF_LOCAL, &tbuf[0]); } #endif @@ -9424,7 +9424,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, #endif #ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: - domain = AF_LOCAL; + domain = AF_UNIX; break; #endif default: @@ -9455,7 +9455,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, #endif #ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: - domain = AF_LOCAL; + domain = AF_UNIX; break; #endif default: @@ -11521,7 +11521,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, break; #endif #ifdef HAVE_SYS_UN_H - case INET_AF_LOCAL: af = AF_LOCAL; break; + case INET_AF_LOCAL: af = AF_UNIX; break; #endif default: return ctl_error(EINVAL, rbuf, rsize); @@ -11574,7 +11574,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, break; #endif #ifdef HAVE_SYS_UN_H - case INET_AF_LOCAL: af = AF_LOCAL; break; + case INET_AF_LOCAL: af = AF_UNIX; break; #endif default: return ctl_error(EINVAL, rbuf, rsize); diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index c6434fd83b..71dbfbd0a7 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -1756,7 +1756,7 @@ enc_value_2(addr, {local,Addr}) -> unicode:characters_to_binary( Addr, file:native_name_encoding()) end, - [?INET_AF_LOCAL,byte_size(Bin)|Bin]; + [?INET_AF_LOCAL,byte_size(Bin),Bin]; %% enc_value_2(ether, [_,_,_,_,_,_]=Xs) -> Xs; enc_value_2(sockaddr, any) -> -- cgit v1.2.3 From e020f75c10410a6943cd055bfa072a2641eab7da Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 2 Jun 2016 10:55:26 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 693 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 693 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 7501ccd9ce..2b2898f0c1 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -32,6 +32,699 @@

This document describes the changes made to the ERTS application.

+
Erts 8.0 + +
Fixed Bugs and Malfunctions + + +

The handling of on_load functions has been + improved. The major improvement is that if a code upgrade + fails because the on_load function fails, the + previous version of the module will now be retained.

+

+ Own Id: OTP-12593

+
+ +

is_builtin(erlang, apply, 3) will now return + true.

+

+ Own Id: OTP-13034

+
+ +

+ Fix enif_get_list_length to return false if list + is improper or have length larger than UINT_MAX + (did return true and an incorrect length value).

+

+ Own Id: OTP-13288 Aux Id: PR913

+
+ +

+ Cleanup hipe signal handling code for x86 and make it + more portable.

+

+ Own Id: OTP-13341 Aux Id: PR951

+
+ +

+ Use fsync instead of fdatasync on Mac OSX.

+

+ Own Id: OTP-13411

+
+ +

+ Make sure to create a crash dump when running out of + memory. This was accidentally removed in the erts-7.3 + release.

+

+ Own Id: OTP-13419

+
+ +

+ A bug has been fixed where if erlang was started +B on a + unix platform it would be killed by a SIGUSR2 signal when + creating a crash dump.

+

+ Own Id: OTP-13425

+
+ +

+ Fix race between process_flag(trap_exit,true) and + a received exit signal.

+

+ A process could terminate due to exit signal even though + process_flag(trap_exit,true) had returned. A very + specific timing between call to process_flag/2 and + exit signal from another scheduler was required for this + to happen.

+

+ Own Id: OTP-13452

+
+ +

Don't search for non-existing Map keys twice

+

For maps:get/2,3 and maps:find/2, + searching for an immediate key, e.g. an atom, in a small + map, the search was performed twice if the key did not + exist.

+

+ Own Id: OTP-13459

+
+ +

+ When a abnormally large distribution message is about to + be sent, the VM has been changed to create a crash dump + instead of a core dump.

+

+ Own Id: OTP-13474

+
+ +

+ Fix erlang:process_info/2 type specification

+

+ Own Id: OTP-13485 Aux Id: ERL-123

+
+ +

+ Fix bug in open_port/2 with option {args, + List}. A vm crash could be caused by an improper + List.

+

+ Own Id: OTP-13489 Aux Id: ERL-127

+
+ +

+ Don't crash on terminating processes with + erlang:system_profile/1,2

+

+ Own Id: OTP-13494 Aux Id: ERL-126

+
+ +

+ Fixed bugs where the reduction counter was not handled + correct.

+

+ Own Id: OTP-13512

+
+ +

+ Fixed typo in description of the EPMD_DUMP_REQ + response.

+

+ Own Id: OTP-13517

+
+ +

+ Fixed a bug where a process flagged as sensitive would + sometimes record its save_calls when it shouldn't.

+

+ Own Id: OTP-13540

+
+ +

+ Update configure scripts to not use hardcoded path for + /bin/pwd and /bin/rm.

+

+ Own Id: OTP-13562

+
+ +

+ When passing a larger binary than the outputv callback of + a linked-in driver can handle in one io vector slot, the + binary is now split into multiple slots in the io vector. + This change only effects system where the max size of an + io vector slot is smaller then the word size of the + system (e.g. Windows).

+

+ This change means that it is now possible on Windows to + send binaries that are larger than 4GB to port_comnmand, + which is what is used for file:write, gen_tcp:send etc.

+

+ Own Id: OTP-13628

+
+
+
+ + +
Improvements and New Features + + +

+ The tracing support has been extended to allow a tracer module to be the + trace event handler instead of a process or port. The + tracer module + makes it possible for trace tools to filter or manipulate + trace event data without the trace event first haing to + be copied from the traced process or port.

+

+ With the introduction of this feature, + erlang:trace(all|existing, _, _) now also returns + the tracer process as part of the number of processes on + which tracing is enabled. The is incompatible with the + previous releases.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-10267

+
+ +

+ Introduce LTTng tracing of Erlang Runtime System

+

+ For LTTng to be enabled OTP needs to be built with + configure option --with-dynamic-trace=lttng.

+

+ This feature introduces tracepoints for schedulers, + drivers, memory carriers, memory and async thread pool.

+

For a list of all tracepoints, see Runtime Tools User's + Guide .

+

+ Own Id: OTP-10282

+
+ +

+ Add microstate accounting

+

+ Microstate accounting is a way to track which state the + different threads within ERTS are in. The main usage area + is to pin point performance bottlenecks by checking which + states the threads are in and then from there figuring + out why and where to optimize.

+

+ Since checking whether microstate accounting is on or off + is relatively expensive only a few of the states are + enabled by default and more states can be enabled through + configure.

+

+ There is a convinence module called msacc that has been + added to runtime_tools that can assist in gathering and + interpreting the data from Microstate accounting.

+

+ For more information see erlang:statistics(microstate_accounting, + _) and the msacc module in + runtime_tools.

+

+ Own Id: OTP-12345

+
+ +

+ The port of Erlang/OTP to the realtime operating system + OSE has been removed.

+

+ Own Id: OTP-12573

+
+ +

+ Sharing preserved copy for messages and exit signals

+

+ Enable sharing preserved copy with configure option + --enable-sharing-preserving. This will preserve + sharing, within the process, when communication with + other processes in the Erlang node. There is a trade-off, + the copy is more costly but this cost can be reclaimed if + there is a lot of sharing in the message. With this + feature enabled literals will not be copied in a send + except during a purge phase of the module where the + literals are located. This feature is considered + experimental in 19.0.

+

+ Own Id: OTP-12590 Aux Id: OTP-10251

+
+ +

+ Halfword BEAM has been removed.

+

+ Own Id: OTP-12883

+
+ +

+ Added os:perf_counter/1.

+

+ The perf_counter is a very very cheap and high resolution + timer that can be used to timestamp system events. It + does not have monoticity guarantees, but should on most + OS's expose a monotonous time.

+

+ Own Id: OTP-12908

+
+ +

+ Support for a fragmented young heap generation. That is, + the young heap generation can consist of multiple non + continuous memory areas. The main reason for this change + is to avoid extra copying of messages that could not be + allocated directly on the receivers heap.

+

+ Own Id: OTP-13047

+
+ +

+ Erlang linked-in driver can now force the call to + open_port to block until a call to erl_drv_init_ack is + made inside the driver. This is useful when you want to + do some asynchronous initialization, for example getting + configuration from a pipe, and you want the initial + open_port call to fail if the configuration is incomplete + or wrong. See the erl_driver documentation for more + details on the API.

+

+ Own Id: OTP-13086

+
+ +

+ Erlang linked-in drivers can now set their own pid's as + seen in erlang:port_info/1 by using the + erl_drv_set_pid function. For more details see the + erl_driver documentation.

+

+ Own Id: OTP-13087

+
+ +

+ The functionality behind erlang:open_port/2 when + called with spawn or spawn_executable has been redone so + that the forking of the new program is done in a separate + process called erl_child_setup. This allows for a much + more robust implementation that uses less memory and does + not block the entire emulator if the program to be + started is on an un-accessible NFS. Benchmarks have shown + this approach to be about 3-5 times as fast as the old + approach where the fork/vfork was done by erts. This is a + pure stability and performance fix, however some error + messages may have changed, which is why it is marked as a + backwards incompatible change.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13088

+
+ +

Improved yielding strategy in the implementation of + the following native functions:

+ erlang:binary_to_list/1 + erlang:binary_to_list/3 + erlang:bitstring_to_list/1 + erlang:list_to_binary/1 + erlang:iolist_to_binary/1 + erlang:list_to_bitstring/1 + binary:list_to_bin/1

This + in order to improve performance of these functions.

+

+ Own Id: OTP-13096

+
+ +

+ All garbage collections of processes now bump reductions. + Also the amount of reductions bumped when garbage + collecting has been adjusted. It now better corresponds + to the amount of work performed. This in order to improve + the real time characteristics of the system.

+

+ Own Id: OTP-13097

+
+ +

New functions that can load multiple functions at once + have been added to the 'code' module. The + functions are code:atomic_load/1, + code:prepare_loading/1, + code:finish_loading/1, and + code:ensure_modules_loaded/1.

+

+ Own Id: OTP-13111

+
+ +

The -boot_var option for erl now only + supports a single key and single value (as documented). + The option used to allow multiple key/value pairs, but + that behavior was undocumented.

+

The function erl_prim_loader:start/3 has been + removed. Its documentation has also been removed.

+

The undocumented and unsupported function + erl_prim_loader:get_files/2 has been removed.

+

+ Own Id: OTP-13112

+
+ +

+ Low level BIF erlang:purge_module/1 is made more + robust against incorrect use. Lingering processes that + still refer the old code are now killed before the module + is purged to prevent fatal VM behavior.

+

+ Own Id: OTP-13122

+
+ +

+ Improved dirty scheduler implementation. For more + information see the NIF documentation.

+

+ Note that support for determining whether dirty NIF + support exist or not at compile time using the C + preprocessor macro ERL_NIF_DIRTY_SCHEDULER_SUPPORT + has been removed.

+

+ Own Id: OTP-13123

+
+ +

+ Various optimizations done to process dictionary access.

+

+ Own Id: OTP-13167

+
+ +

+ Added max_heap_size process flag. See erlang:process_flag + for more details.

+

+ Own Id: OTP-13174

+
+ +

+ Allow dynamic drivers and NIF libraries to be built with + gcc option -fvisibility=hidden for faster loading + and more optimized code.

+

+ Own Id: OTP-13227

+
+ +

+ Add erlang:process_info(Pid, + garbage_collection_info) which returns extended + garbage_collection information. For more details see the + documentation.

+

+ Own Id: OTP-13265

+
+ +

+ The functions erlang:list_to_integer/1 and + string:to_integer/1 have been optimized for large + inputs.

+

+ Own Id: OTP-13293

+
+ +

+ Improved memory allocation strategy for hipe native code + on x86_64 (amd64) architectures by reserving enough low + virtual address space needed for the HiPE/AMD64 small + code model. The default virtual address area for hipe + code is set to 512Mb, but can be changed with emulator + flag +MXscs.

+

+ Own Id: OTP-13359

+
+ +

+ Introduction of configurable management of data referred + to by the message queue of a process. Each process can be + configured individually.

+

+ It is now possible to configure the message queue of a + process, so that all data referred by it will be kept + outside of the heap, and by this prevent this data from + being part of garbage collections.

+

+ For more information see the documentation of process_flag(message_queue_data, + MQD).

+

+ Own Id: OTP-13366 Aux Id: OTP-13047

+
+ +

+ Processes now yield when scanning large message queues + and not finding a matching message. This in order to + improve real time characteristics.

+

+ Own Id: OTP-13401

+
+ +

+ Optimized an erts internal function that is used to + traverse erlang terms. The internal function was mainly + used by term_to_binary and comparison of terms. + Benchmarks have shown up to a 10% performance increase in + those functions after the optimization.

+

+ Own Id: OTP-13440

+
+ +

+ Add the following NIF API functions:

+

+ enif_cpu_time + enif_now_time + enif_make_unique_integer + enif_is_process_alive + enif_is_port_alive + enif_term_to_binary + enif_binary_to_term + enif_port_command +

+

+ for details of what each function does, see the erl_nif + documentation.

+

+ Own Id: OTP-13442

+
+ +

+ Optimize '++' operator and lists:append/2 + by using a single pass to build a new list while checking + for properness.

+

+ Own Id: OTP-13487

+
+ +

+ Handle terms (pids,ports and refs) from nodes with a + 'creation' value larger than 3. This is a preparation of + the distribution protocol to allow OTP 19 nodes to + correctly communicate with future nodes (20 or higher). + The 'creation' value differentiates different + incarnations of the same node (name).

+

+ Own Id: OTP-13488

+
+ +

+ Don't send unasked for systemd notifications in epmd

+

+ Own Id: OTP-13493 Aux Id: PR-999

+
+ +

+ The enif_send API has been extended to allow NULL to be + used as the message environment. When used this way, a + message environent is implicitly created and the given + term is copied into that environment before sending. This + can be an optimization if many small messages are being + sent by the nif.

+

+ Own Id: OTP-13495

+
+ +

+ The tracing support has been extended to allow tracing on + ports. Ports can be traced on using the 'ports', 'send' + and 'receive' trace flags.

+

+ The first argument of erlang:trace/3 has + been extended so that 'all', 'existing' and + 'new' now include both processes and ports. New + Tracee variants, 'all_processes', + 'all_ports', 'existing_processes' etc have + been added to specify only processes or ports.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13496

+
+ +

+ When the 'procs' trace flag is enabled, a + 'spawned' trace event is now also generated by a + newly created process. The previous event 'spawn' + remains, but as it is generated by the process that did + the spawn, it is not guaranteed that it is ordered with + other trace events from the newly spawned process. So + when tracking the lifetime of a process this new event + should be used as the creation event.

+

+ This new trace event is marked as an incompatabiliy + because tools that expect certain trace events when + enabling 'procs' will have to updated.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13497

+
+ +

+ Add the erlang:match_spec_test/3 + function. The functions allows the testing of match + specifications for both tracing and ets tables. It can be + used to test that a match specification does the expected + filtering on specific data. It also returns more verbose + error reasons for incorrectly constructed match + specifications.

+

+ Own Id: OTP-13501

+
+ +

+ The erts internal tracing support has been changed to + have much less overhead and be more scalable.

+

+ This rewrite does not break any backwards + incompatabilities, but it does change the ordering of + some trace messages when compared to previous releases. + It should be noted that this only applies to trace + messages sent to processes or ports, it does not apply to + the new tracer module. However in future releases they + may also be effected by this.

+

+ Trace messages are only guaranteed to be ordered from one + traced process or port. In previous releases this was not + visible as a 'send' trace message would always + arrive before the corresponding 'receive' trace + message that is no longer always the case. This also + means that timestamped trace messages may seem to arrive + out of order as the timestamp is taken when the event is + triggered and not when it is put in the queue of the + tracer.

+

+ Own Id: OTP-13503

+
+ +

+ Add possibility to filter send and receive + trace with match specifications.

+

+ Own Id: OTP-13507

+
+ +

+ Add maps:update_with/3,4 and maps:take/2

+

+ Own Id: OTP-13522 Aux Id: PR-1025

+
+ +

+ Introduce LTTng tracing via Erlang tracing.

+

+ For LTTng to be enabled OTP needs to be built with + configure option --with-dynamic-trace=lttng.

+

The dynamic trace module dyntrace is now + capable to be used as a LTTng sink for Erlang tracing. + For a list of all tracepoints, see Runtime Tools User's + Guide .

+

This feature also introduces an incompatible change in + trace tags. The trace tags gc_start and + gc_end has been split into gc_minor_start, + gc_minor_end and gc_major_start, + gc_major_end.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13532

+
+ +

+ Print heap pointers for garbing processes during + crashdump

+

+ Own Id: OTP-13541 Aux Id: PR-1026

+
+ +

+ Changed and improved low level memory statistics returned + by erlang:system_info/1. The info for + erts_mmap has been moved from mseg_alloc to + its own section returned by {allocator, + erts_mmap}.

+

+ Own Id: OTP-13560

+
+ +

+ Add enif_snprintf to the NIF API

+

+ The fucntion enif_snprintf is similar to + snprintf call but can handle formating of Erlang + terms via %T format specifier.

+

+ Own Id: OTP-13580

+
+ +

The warning in the documentation for + erlang:raise/3 has been removed. It is now + officially perfectly fine to use raise/3 in production + code. (Thanks to Per Hedeland.)

+

+ Own Id: OTP-13599

+
+ +

+ Add -start_epmd command line option, this lets you + disable automatic starting of epmd when starting a + distributed node.

+

+ Add -epmd_module command line option, this lets + you specify a module to register and lookup node names + in. The default module is erl_epmd.

+

+ Own Id: OTP-13627

+
+ +

+ erlang:halt now truncates strings longer than 200 + characters instead of failing with badarg.

+

+ Own Id: OTP-13630

+
+
+
+ +
+
Erts 7.3.1
Fixed Bugs and Malfunctions -- cgit v1.2.3 From c04cad3ba921deb086d19e2de2526af4854add75 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 2 Jun 2016 11:39:07 +0200 Subject: Revert "Prepare release" This reverts commit e020f75c10410a6943cd055bfa072a2641eab7da. --- erts/doc/src/notes.xml | 693 ------------------------------------------------- 1 file changed, 693 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 2b2898f0c1..7501ccd9ce 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -32,699 +32,6 @@

This document describes the changes made to the ERTS application.

-
Erts 8.0 - -
Fixed Bugs and Malfunctions - - -

The handling of on_load functions has been - improved. The major improvement is that if a code upgrade - fails because the on_load function fails, the - previous version of the module will now be retained.

-

- Own Id: OTP-12593

-
- -

is_builtin(erlang, apply, 3) will now return - true.

-

- Own Id: OTP-13034

-
- -

- Fix enif_get_list_length to return false if list - is improper or have length larger than UINT_MAX - (did return true and an incorrect length value).

-

- Own Id: OTP-13288 Aux Id: PR913

-
- -

- Cleanup hipe signal handling code for x86 and make it - more portable.

-

- Own Id: OTP-13341 Aux Id: PR951

-
- -

- Use fsync instead of fdatasync on Mac OSX.

-

- Own Id: OTP-13411

-
- -

- Make sure to create a crash dump when running out of - memory. This was accidentally removed in the erts-7.3 - release.

-

- Own Id: OTP-13419

-
- -

- A bug has been fixed where if erlang was started +B on a - unix platform it would be killed by a SIGUSR2 signal when - creating a crash dump.

-

- Own Id: OTP-13425

-
- -

- Fix race between process_flag(trap_exit,true) and - a received exit signal.

-

- A process could terminate due to exit signal even though - process_flag(trap_exit,true) had returned. A very - specific timing between call to process_flag/2 and - exit signal from another scheduler was required for this - to happen.

-

- Own Id: OTP-13452

-
- -

Don't search for non-existing Map keys twice

-

For maps:get/2,3 and maps:find/2, - searching for an immediate key, e.g. an atom, in a small - map, the search was performed twice if the key did not - exist.

-

- Own Id: OTP-13459

-
- -

- When a abnormally large distribution message is about to - be sent, the VM has been changed to create a crash dump - instead of a core dump.

-

- Own Id: OTP-13474

-
- -

- Fix erlang:process_info/2 type specification

-

- Own Id: OTP-13485 Aux Id: ERL-123

-
- -

- Fix bug in open_port/2 with option {args, - List}. A vm crash could be caused by an improper - List.

-

- Own Id: OTP-13489 Aux Id: ERL-127

-
- -

- Don't crash on terminating processes with - erlang:system_profile/1,2

-

- Own Id: OTP-13494 Aux Id: ERL-126

-
- -

- Fixed bugs where the reduction counter was not handled - correct.

-

- Own Id: OTP-13512

-
- -

- Fixed typo in description of the EPMD_DUMP_REQ - response.

-

- Own Id: OTP-13517

-
- -

- Fixed a bug where a process flagged as sensitive would - sometimes record its save_calls when it shouldn't.

-

- Own Id: OTP-13540

-
- -

- Update configure scripts to not use hardcoded path for - /bin/pwd and /bin/rm.

-

- Own Id: OTP-13562

-
- -

- When passing a larger binary than the outputv callback of - a linked-in driver can handle in one io vector slot, the - binary is now split into multiple slots in the io vector. - This change only effects system where the max size of an - io vector slot is smaller then the word size of the - system (e.g. Windows).

-

- This change means that it is now possible on Windows to - send binaries that are larger than 4GB to port_comnmand, - which is what is used for file:write, gen_tcp:send etc.

-

- Own Id: OTP-13628

-
-
-
- - -
Improvements and New Features - - -

- The tracing support has been extended to allow a tracer module to be the - trace event handler instead of a process or port. The - tracer module - makes it possible for trace tools to filter or manipulate - trace event data without the trace event first haing to - be copied from the traced process or port.

-

- With the introduction of this feature, - erlang:trace(all|existing, _, _) now also returns - the tracer process as part of the number of processes on - which tracing is enabled. The is incompatible with the - previous releases.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-10267

-
- -

- Introduce LTTng tracing of Erlang Runtime System

-

- For LTTng to be enabled OTP needs to be built with - configure option --with-dynamic-trace=lttng.

-

- This feature introduces tracepoints for schedulers, - drivers, memory carriers, memory and async thread pool.

-

For a list of all tracepoints, see Runtime Tools User's - Guide .

-

- Own Id: OTP-10282

-
- -

- Add microstate accounting

-

- Microstate accounting is a way to track which state the - different threads within ERTS are in. The main usage area - is to pin point performance bottlenecks by checking which - states the threads are in and then from there figuring - out why and where to optimize.

-

- Since checking whether microstate accounting is on or off - is relatively expensive only a few of the states are - enabled by default and more states can be enabled through - configure.

-

- There is a convinence module called msacc that has been - added to runtime_tools that can assist in gathering and - interpreting the data from Microstate accounting.

-

- For more information see erlang:statistics(microstate_accounting, - _) and the msacc module in - runtime_tools.

-

- Own Id: OTP-12345

-
- -

- The port of Erlang/OTP to the realtime operating system - OSE has been removed.

-

- Own Id: OTP-12573

-
- -

- Sharing preserved copy for messages and exit signals

-

- Enable sharing preserved copy with configure option - --enable-sharing-preserving. This will preserve - sharing, within the process, when communication with - other processes in the Erlang node. There is a trade-off, - the copy is more costly but this cost can be reclaimed if - there is a lot of sharing in the message. With this - feature enabled literals will not be copied in a send - except during a purge phase of the module where the - literals are located. This feature is considered - experimental in 19.0.

-

- Own Id: OTP-12590 Aux Id: OTP-10251

-
- -

- Halfword BEAM has been removed.

-

- Own Id: OTP-12883

-
- -

- Added os:perf_counter/1.

-

- The perf_counter is a very very cheap and high resolution - timer that can be used to timestamp system events. It - does not have monoticity guarantees, but should on most - OS's expose a monotonous time.

-

- Own Id: OTP-12908

-
- -

- Support for a fragmented young heap generation. That is, - the young heap generation can consist of multiple non - continuous memory areas. The main reason for this change - is to avoid extra copying of messages that could not be - allocated directly on the receivers heap.

-

- Own Id: OTP-13047

-
- -

- Erlang linked-in driver can now force the call to - open_port to block until a call to erl_drv_init_ack is - made inside the driver. This is useful when you want to - do some asynchronous initialization, for example getting - configuration from a pipe, and you want the initial - open_port call to fail if the configuration is incomplete - or wrong. See the erl_driver documentation for more - details on the API.

-

- Own Id: OTP-13086

-
- -

- Erlang linked-in drivers can now set their own pid's as - seen in erlang:port_info/1 by using the - erl_drv_set_pid function. For more details see the - erl_driver documentation.

-

- Own Id: OTP-13087

-
- -

- The functionality behind erlang:open_port/2 when - called with spawn or spawn_executable has been redone so - that the forking of the new program is done in a separate - process called erl_child_setup. This allows for a much - more robust implementation that uses less memory and does - not block the entire emulator if the program to be - started is on an un-accessible NFS. Benchmarks have shown - this approach to be about 3-5 times as fast as the old - approach where the fork/vfork was done by erts. This is a - pure stability and performance fix, however some error - messages may have changed, which is why it is marked as a - backwards incompatible change.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-13088

-
- -

Improved yielding strategy in the implementation of - the following native functions:

- erlang:binary_to_list/1 - erlang:binary_to_list/3 - erlang:bitstring_to_list/1 - erlang:list_to_binary/1 - erlang:iolist_to_binary/1 - erlang:list_to_bitstring/1 - binary:list_to_bin/1

This - in order to improve performance of these functions.

-

- Own Id: OTP-13096

-
- -

- All garbage collections of processes now bump reductions. - Also the amount of reductions bumped when garbage - collecting has been adjusted. It now better corresponds - to the amount of work performed. This in order to improve - the real time characteristics of the system.

-

- Own Id: OTP-13097

-
- -

New functions that can load multiple functions at once - have been added to the 'code' module. The - functions are code:atomic_load/1, - code:prepare_loading/1, - code:finish_loading/1, and - code:ensure_modules_loaded/1.

-

- Own Id: OTP-13111

-
- -

The -boot_var option for erl now only - supports a single key and single value (as documented). - The option used to allow multiple key/value pairs, but - that behavior was undocumented.

-

The function erl_prim_loader:start/3 has been - removed. Its documentation has also been removed.

-

The undocumented and unsupported function - erl_prim_loader:get_files/2 has been removed.

-

- Own Id: OTP-13112

-
- -

- Low level BIF erlang:purge_module/1 is made more - robust against incorrect use. Lingering processes that - still refer the old code are now killed before the module - is purged to prevent fatal VM behavior.

-

- Own Id: OTP-13122

-
- -

- Improved dirty scheduler implementation. For more - information see the NIF documentation.

-

- Note that support for determining whether dirty NIF - support exist or not at compile time using the C - preprocessor macro ERL_NIF_DIRTY_SCHEDULER_SUPPORT - has been removed.

-

- Own Id: OTP-13123

-
- -

- Various optimizations done to process dictionary access.

-

- Own Id: OTP-13167

-
- -

- Added max_heap_size process flag. See erlang:process_flag - for more details.

-

- Own Id: OTP-13174

-
- -

- Allow dynamic drivers and NIF libraries to be built with - gcc option -fvisibility=hidden for faster loading - and more optimized code.

-

- Own Id: OTP-13227

-
- -

- Add erlang:process_info(Pid, - garbage_collection_info) which returns extended - garbage_collection information. For more details see the - documentation.

-

- Own Id: OTP-13265

-
- -

- The functions erlang:list_to_integer/1 and - string:to_integer/1 have been optimized for large - inputs.

-

- Own Id: OTP-13293

-
- -

- Improved memory allocation strategy for hipe native code - on x86_64 (amd64) architectures by reserving enough low - virtual address space needed for the HiPE/AMD64 small - code model. The default virtual address area for hipe - code is set to 512Mb, but can be changed with emulator - flag +MXscs.

-

- Own Id: OTP-13359

-
- -

- Introduction of configurable management of data referred - to by the message queue of a process. Each process can be - configured individually.

-

- It is now possible to configure the message queue of a - process, so that all data referred by it will be kept - outside of the heap, and by this prevent this data from - being part of garbage collections.

-

- For more information see the documentation of process_flag(message_queue_data, - MQD).

-

- Own Id: OTP-13366 Aux Id: OTP-13047

-
- -

- Processes now yield when scanning large message queues - and not finding a matching message. This in order to - improve real time characteristics.

-

- Own Id: OTP-13401

-
- -

- Optimized an erts internal function that is used to - traverse erlang terms. The internal function was mainly - used by term_to_binary and comparison of terms. - Benchmarks have shown up to a 10% performance increase in - those functions after the optimization.

-

- Own Id: OTP-13440

-
- -

- Add the following NIF API functions:

-

- enif_cpu_time - enif_now_time - enif_make_unique_integer - enif_is_process_alive - enif_is_port_alive - enif_term_to_binary - enif_binary_to_term - enif_port_command -

-

- for details of what each function does, see the erl_nif - documentation.

-

- Own Id: OTP-13442

-
- -

- Optimize '++' operator and lists:append/2 - by using a single pass to build a new list while checking - for properness.

-

- Own Id: OTP-13487

-
- -

- Handle terms (pids,ports and refs) from nodes with a - 'creation' value larger than 3. This is a preparation of - the distribution protocol to allow OTP 19 nodes to - correctly communicate with future nodes (20 or higher). - The 'creation' value differentiates different - incarnations of the same node (name).

-

- Own Id: OTP-13488

-
- -

- Don't send unasked for systemd notifications in epmd

-

- Own Id: OTP-13493 Aux Id: PR-999

-
- -

- The enif_send API has been extended to allow NULL to be - used as the message environment. When used this way, a - message environent is implicitly created and the given - term is copied into that environment before sending. This - can be an optimization if many small messages are being - sent by the nif.

-

- Own Id: OTP-13495

-
- -

- The tracing support has been extended to allow tracing on - ports. Ports can be traced on using the 'ports', 'send' - and 'receive' trace flags.

-

- The first argument of erlang:trace/3 has - been extended so that 'all', 'existing' and - 'new' now include both processes and ports. New - Tracee variants, 'all_processes', - 'all_ports', 'existing_processes' etc have - been added to specify only processes or ports.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-13496

-
- -

- When the 'procs' trace flag is enabled, a - 'spawned' trace event is now also generated by a - newly created process. The previous event 'spawn' - remains, but as it is generated by the process that did - the spawn, it is not guaranteed that it is ordered with - other trace events from the newly spawned process. So - when tracking the lifetime of a process this new event - should be used as the creation event.

-

- This new trace event is marked as an incompatabiliy - because tools that expect certain trace events when - enabling 'procs' will have to updated.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-13497

-
- -

- Add the erlang:match_spec_test/3 - function. The functions allows the testing of match - specifications for both tracing and ets tables. It can be - used to test that a match specification does the expected - filtering on specific data. It also returns more verbose - error reasons for incorrectly constructed match - specifications.

-

- Own Id: OTP-13501

-
- -

- The erts internal tracing support has been changed to - have much less overhead and be more scalable.

-

- This rewrite does not break any backwards - incompatabilities, but it does change the ordering of - some trace messages when compared to previous releases. - It should be noted that this only applies to trace - messages sent to processes or ports, it does not apply to - the new tracer module. However in future releases they - may also be effected by this.

-

- Trace messages are only guaranteed to be ordered from one - traced process or port. In previous releases this was not - visible as a 'send' trace message would always - arrive before the corresponding 'receive' trace - message that is no longer always the case. This also - means that timestamped trace messages may seem to arrive - out of order as the timestamp is taken when the event is - triggered and not when it is put in the queue of the - tracer.

-

- Own Id: OTP-13503

-
- -

- Add possibility to filter send and receive - trace with match specifications.

-

- Own Id: OTP-13507

-
- -

- Add maps:update_with/3,4 and maps:take/2

-

- Own Id: OTP-13522 Aux Id: PR-1025

-
- -

- Introduce LTTng tracing via Erlang tracing.

-

- For LTTng to be enabled OTP needs to be built with - configure option --with-dynamic-trace=lttng.

-

The dynamic trace module dyntrace is now - capable to be used as a LTTng sink for Erlang tracing. - For a list of all tracepoints, see Runtime Tools User's - Guide .

-

This feature also introduces an incompatible change in - trace tags. The trace tags gc_start and - gc_end has been split into gc_minor_start, - gc_minor_end and gc_major_start, - gc_major_end.

-

- *** POTENTIAL INCOMPATIBILITY ***

-

- Own Id: OTP-13532

-
- -

- Print heap pointers for garbing processes during - crashdump

-

- Own Id: OTP-13541 Aux Id: PR-1026

-
- -

- Changed and improved low level memory statistics returned - by erlang:system_info/1. The info for - erts_mmap has been moved from mseg_alloc to - its own section returned by {allocator, - erts_mmap}.

-

- Own Id: OTP-13560

-
- -

- Add enif_snprintf to the NIF API

-

- The fucntion enif_snprintf is similar to - snprintf call but can handle formating of Erlang - terms via %T format specifier.

-

- Own Id: OTP-13580

-
- -

The warning in the documentation for - erlang:raise/3 has been removed. It is now - officially perfectly fine to use raise/3 in production - code. (Thanks to Per Hedeland.)

-

- Own Id: OTP-13599

-
- -

- Add -start_epmd command line option, this lets you - disable automatic starting of epmd when starting a - distributed node.

-

- Add -epmd_module command line option, this lets - you specify a module to register and lookup node names - in. The default module is erl_epmd.

-

- Own Id: OTP-13627

-
- -

- erlang:halt now truncates strings longer than 200 - characters instead of failing with badarg.

-

- Own Id: OTP-13630

-
-
-
- -
-
Erts 7.3.1
Fixed Bugs and Malfunctions -- cgit v1.2.3 From e25df0378738ba17fb66ae0bd947c439ac925800 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 2 Jun 2016 12:28:12 +0200 Subject: Fix erts_dirty_process_main() --- erts/emulator/beam/beam_emu.c | 77 ++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 41 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 0a59f8785c..4716460a6b 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5318,10 +5318,14 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) ASSERT(BeamOp(op_call_nif) == (BeamInstr *) *I); - if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) - && (ERTS_TRACE_FLAGS(c_p) & F_SENSITIVE) == 0) { - c_p->fcalls = REDS_IN(c_p) = 0; - } + /* + * Set fcalls even though we ignore it, so we don't + * confuse code accessing it... + */ + if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) + c_p->fcalls = 0; + else + c_p->fcalls = CONTEXT_REDS; SWAPIN; @@ -5341,7 +5345,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) NULL, fun_buf); } else { erts_snprintf(fun_buf, sizeof(DTRACE_CHARBUF_NAME(fun_buf)), - "", next); + "", *I); } } @@ -5351,8 +5355,10 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) } { - Eterm nif_bif_result; - Eterm bif_nif_arity; +#ifdef DEBUG + Eterm result; +#endif + Eterm arity; { /* @@ -5374,7 +5380,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) c_p->current = I-3; /* current and vbf set to please handle_error */ SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); - bif_nif_arity = I[-1]; + arity = I[-1]; ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); @@ -5383,50 +5389,39 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) NifF* fp = vbf = (NifF*) I[1]; struct enif_environment_t env; ASSERT(!c_p->scheduler_data); - erts_pre_dirty_nif(esdp, &env, c_p, (struct erl_module_nif*)I[2], NULL); - nif_bif_result = (*fp)(&env, bif_nif_arity, reg); - if (env.exception_thrown) - nif_bif_result = THE_NON_VALUE; + + erts_pre_dirty_nif(esdp, &env, c_p, + (struct erl_module_nif*)I[2], NULL); + +#ifdef DEBUG + result = +#else + (void) +#endif + (*fp)(&env, arity, reg); + erts_post_nif(&env); + + ASSERT(!is_value(result)); + ASSERT(c_p->freason == TRAP); + ASSERT(!(c_p->flags & F_HIBERNATE_SCHED)); + PROCESS_MAIN_CHK_LOCKS(c_p); + ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR); - if (env.exiting) { - ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); + if (env.exiting) goto do_dirty_schedule; - } ASSERT(!ERTS_PROC_IS_EXITING(c_p)); } + DTRACE_NIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); - ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); ERTS_HOLE_CHECK(c_p); - if (ERTS_IS_GC_DESIRED(c_p)) { - nif_bif_result = erts_gc_after_bif_call(c_p, - nif_bif_result, - reg, bif_nif_arity); - } - SWAPIN; /* There might have been a garbage collection. */ - if (is_value(nif_bif_result)) { - r(0) = nif_bif_result; - CHECK_TERM(r(0)); - I = c_p->cp; - c_p->cp = 0; - Goto(*I); - } else if (c_p->freason == TRAP) { - I = c_p->i; - ASSERT(!(c_p->flags & F_HIBERNATE_SCHED)); - goto context_switch; - } - I = handle_error(c_p, c_p->cp, reg, vbf); + SWAPIN; + I = c_p->i; + goto context_switch; } } - if (I == 0) { - goto do_dirty_schedule; - } else { - ASSERT(!is_value(r(0))); - SWAPIN; - goto context_switch; - } #endif /* ERTS_DIRTY_SCHEDULERS */ } -- cgit v1.2.3 From 85996c92853e90cb233a1405b1c8c7bb1b8ca54e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 3 Jun 2016 11:43:47 +0200 Subject: erts: make HEART_NO_KILL have to be set to TRUE --- erts/etc/common/heart.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index b6fbfd075c..0dd82cc036 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -526,8 +526,10 @@ static void kill_old_erlang(void){ HANDLE erlh; DWORD exit_code; + char* envvar = NULL; - if (is_env_set(HEART_NO_KILL)) + envvar = get_env(HEART_NO_KILL); + if (!envvar || strcmp(envvar, "TRUE") == 0) return; if(heart_beat_kill_pid != 0){ @@ -561,13 +563,14 @@ kill_old_erlang(void){ pid_t pid; int i, res; int sig = SIGKILL; - char *sigenv = NULL; + char *envvar = NULL; - if (is_env_set(HEART_NO_KILL)) + envvar = get_env(HEART_NO_KILL); + if (!envvar || strcmp(envvar, "TRUE") == 0) return; - sigenv = get_env(HEART_KILL_SIGNAL); - if (sigenv && strcmp(sigenv, "SIGABRT") == 0) { + envvar = get_env(HEART_KILL_SIGNAL); + if (envvar && strcmp(envvar, "SIGABRT") == 0) { print_error("kill signal SIGABRT requested"); sig = SIGABRT; } -- cgit v1.2.3 From 3a5c3a5c1b0eb756a2258aea87f3bacb89199064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 7 Jun 2016 14:50:50 +0200 Subject: erts: Remove tautological compare warning --- erts/emulator/beam/erl_bif_port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 37f4e1de49..fefa9d8391 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -1411,7 +1411,7 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3) trunc_len = val; goto next_option; case am_line_delimiter: - if (type == TCP_PB_LINE_LF && val >= 0 && val <= 255) { + if (type == TCP_PB_LINE_LF && val <= 255) { delimiter = (char)val; goto next_option; } -- cgit v1.2.3 From d8c8e0c66d6faf5402682f3a8568362eedebdfee Mon Sep 17 00:00:00 2001 From: Zandra Date: Wed, 18 May 2016 11:54:53 +0200 Subject: remove unused purify functions --- erts/emulator/test/process_SUITE.erl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index eaa4026a8a..4ebc1f5782 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -147,11 +147,7 @@ spawn_with_binaries(Config) when is_list(Config) -> TwoMeg = lists:duplicate(1024, L), Fun = fun() -> spawn(?MODULE, binary_owner, [list_to_binary(TwoMeg)]), receive after 1 -> ok end end, - Iter = case test_server:purify_is_running() of - true -> 10; - false -> 150 - end, - test_server:do_times(Iter, Fun), + test_server:do_times(150, Fun), ok. binary_owner(Bin) when is_binary(Bin) -> -- cgit v1.2.3 From 4c6ccf1c2f13d4b6ea34bc0eecc8a772cb38e80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 7 Jun 2016 15:29:11 +0200 Subject: erts: Let clang have suppressable unused variables --- erts/emulator/beam/sys.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 9a205d50d3..dfe82cab44 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -154,8 +154,9 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType; # define ERTS_WRITE_UNLIKELY(X) X #endif +/* clang may have too low __GNUC__ versions but can handle it */ #ifdef __GNUC__ -# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) || defined(__clang__) # define ERTS_DECLARE_DUMMY(X) X __attribute__ ((unused)) # else # define ERTS_DECLARE_DUMMY(X) X -- cgit v1.2.3 From 1a26eefb07de7d6152543b252507d618ed844368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 8 Jun 2016 14:38:27 +0200 Subject: erts: Change LTTng otp domain From 'com_ericsson_otp' to 'org_erlang_otp'. This domain name is more suitable. --- erts/configure.in | 6 +- erts/emulator/beam/erlang_lttng.h | 48 ++++----- erts/emulator/beam/lttng-wrapper.h | 12 +-- erts/emulator/test/lttng_SUITE.erl | 210 ++++++++++++++++++------------------- 4 files changed, 138 insertions(+), 138 deletions(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index ac2fae70ce..81ecad4f51 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3858,15 +3858,15 @@ if test "$enable_lttng_test" = "yes" ; then AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [#include - #define TRACEPOINT_PROVIDER com_ericsson_otp + #define TRACEPOINT_PROVIDER org_erlang_otp TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, dummy, TP_ARGS(int, my_int), TP_FIELDS(ctf_integer(int, my_int, my_int))) #define TRACEPOINT_CREATE_PROBES #define TRACEPOINT_DEFINE], - [if(tracepoint_enabled(com_ericsson_otp,dummy)) do {} while(0)])], + [if(tracepoint_enabled(org_erlang_otp,dummy)) do {} while(0)])], [AC_MSG_RESULT([yes])], [AC_MSG_ERROR([no (available in lttng-ust v2.7)])]) if test "x$ac_cv_header_lttng_tracepoint_h" = "xyes" \ diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h index 12f68e477b..4e869671f7 100644 --- a/erts/emulator/beam/erlang_lttng.h +++ b/erts/emulator/beam/erlang_lttng.h @@ -20,7 +20,7 @@ #ifdef USE_LTTNG #undef TRACEPOINT_PROVIDER -#define TRACEPOINT_PROVIDER com_ericsson_otp +#define TRACEPOINT_PROVIDER org_erlang_otp #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "erlang_lttng.h" @@ -33,7 +33,7 @@ /* Schedulers */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, scheduler_poll, TP_ARGS( int, id, @@ -62,7 +62,7 @@ typedef struct { /* Port and Driver Scheduling */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_start, TP_ARGS( char*, pid, @@ -77,7 +77,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_init, TP_ARGS( char*, driver, @@ -94,7 +94,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_outputv, TP_ARGS( char*, pid, @@ -111,7 +111,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_output, TP_ARGS( char*, pid, @@ -128,7 +128,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_input, TP_ARGS( char*, pid, @@ -143,7 +143,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_output, TP_ARGS( char*, pid, @@ -158,7 +158,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_event, TP_ARGS( char*, pid, @@ -173,7 +173,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_timeout, TP_ARGS( char*, pid, @@ -188,7 +188,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_stop_select, TP_ARGS( char*, driver @@ -199,7 +199,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_flush, TP_ARGS( char*, pid, @@ -214,7 +214,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_stop, TP_ARGS( char*, pid, @@ -229,7 +229,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_process_exit, TP_ARGS( char*, pid, @@ -244,7 +244,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_async, TP_ARGS( char*, pid, @@ -259,7 +259,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_finish, TP_ARGS( char*, driver @@ -270,7 +270,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_call, TP_ARGS( char*, pid, @@ -289,7 +289,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_control, TP_ARGS( char*, pid, @@ -310,7 +310,7 @@ TRACEPOINT_EVENT( /* Async pool */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, aio_pool_get, TP_ARGS( char*, port, @@ -323,7 +323,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, aio_pool_put, TP_ARGS( char*, port, @@ -339,7 +339,7 @@ TRACEPOINT_EVENT( /* Memory Allocator */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_create, TP_ARGS( const char*, type, @@ -365,7 +365,7 @@ TRACEPOINT_EVENT( TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_destroy, TP_ARGS( const char*, type, @@ -390,7 +390,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_pool_put, TP_ARGS( const char*, name, @@ -405,7 +405,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_pool_get, TP_ARGS( const char*, name, diff --git a/erts/emulator/beam/lttng-wrapper.h b/erts/emulator/beam/lttng-wrapper.h index 294872c365..0bc75c1552 100644 --- a/erts/emulator/beam/lttng-wrapper.h +++ b/erts/emulator/beam/lttng-wrapper.h @@ -77,23 +77,23 @@ (RQ)->scheduler->no #define LTTNG_ENABLED(Name) \ - tracepoint_enabled(com_ericsson_otp, Name) + tracepoint_enabled(org_erlang_otp, Name) /* include a special LTTNG_DO for do_tracepoint ? */ #define LTTNG1(Name, Arg1) \ - tracepoint(com_ericsson_otp, Name, (Arg1)) + tracepoint(org_erlang_otp, Name, (Arg1)) #define LTTNG2(Name, Arg1, Arg2) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2)) #define LTTNG3(Name, Arg1, Arg2, Arg3) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3)) #define LTTNG4(Name, Arg1, Arg2, Arg3, Arg4) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4)) #define LTTNG5(Name, Arg1, Arg2, Arg3, Arg4, Arg5) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) #else /* USE_LTTNG */ diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl index 1360751aee..6b7ad836f5 100644 --- a/erts/emulator/test/lttng_SUITE.erl +++ b/erts/emulator/test/lttng_SUITE.erl @@ -80,34 +80,34 @@ end_per_testcase(Case, _Config) -> ok. %% Not tested yet -%% com_ericsson_otp:driver_process_exit -%% com_ericsson_otp:driver_event +%% org_erlang_otp:driver_process_exit +%% org_erlang_otp:driver_event %% tracepoints %% -%% com_ericsson_otp:carrier_pool_get -%% com_ericsson_otp:carrier_pool_put -%% com_ericsson_otp:carrier_destroy -%% com_ericsson_otp:carrier_create -%% com_ericsson_otp:aio_pool_put -%% com_ericsson_otp:aio_pool_get -%% com_ericsson_otp:driver_control -%% com_ericsson_otp:driver_call -%% com_ericsson_otp:driver_finish -%% com_ericsson_otp:driver_ready_async -%% com_ericsson_otp:driver_process_exit -%% com_ericsson_otp:driver_stop -%% com_ericsson_otp:driver_flush -%% com_ericsson_otp:driver_stop_select -%% com_ericsson_otp:driver_timeout -%% com_ericsson_otp:driver_event -%% com_ericsson_otp:driver_ready_output -%% com_ericsson_otp:driver_ready_input -%% com_ericsson_otp:driver_output -%% com_ericsson_otp:driver_outputv -%% com_ericsson_otp:driver_init -%% com_ericsson_otp:driver_start -%% com_ericsson_otp:scheduler_poll +%% org_erlang_otp:carrier_pool_get +%% org_erlang_otp:carrier_pool_put +%% org_erlang_otp:carrier_destroy +%% org_erlang_otp:carrier_create +%% org_erlang_otp:aio_pool_put +%% org_erlang_otp:aio_pool_get +%% org_erlang_otp:driver_control +%% org_erlang_otp:driver_call +%% org_erlang_otp:driver_finish +%% org_erlang_otp:driver_ready_async +%% org_erlang_otp:driver_process_exit +%% org_erlang_otp:driver_stop +%% org_erlang_otp:driver_flush +%% org_erlang_otp:driver_stop_select +%% org_erlang_otp:driver_timeout +%% org_erlang_otp:driver_event +%% org_erlang_otp:driver_ready_output +%% org_erlang_otp:driver_ready_input +%% org_erlang_otp:driver_output +%% org_erlang_otp:driver_outputv +%% org_erlang_otp:driver_init +%% org_erlang_otp:driver_start +%% org_erlang_otp:scheduler_poll %% %% Testcases @@ -117,48 +117,48 @@ t_lttng_list(_Config) -> {ok, _} = cmd("lttng list -u"), ok. -%% com_ericsson_otp:carrier_pool_get -%% com_ericsson_otp:carrier_pool_put +%% org_erlang_otp:carrier_pool_get +%% org_erlang_otp:carrier_pool_put t_carrier_pool(Config) -> case have_carriers(ets_alloc) of false -> {skip, "No Memory Carriers configured on system."}; true -> - ok = lttng_start_event("com_ericsson_otp:carrier_pool*", Config), + ok = lttng_start_event("org_erlang_otp:carrier_pool*", Config), ok = ets_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:carrier_pool_get", Res), - ok = check_tracepoint("com_ericsson_otp:carrier_pool_put", Res), + ok = check_tracepoint("org_erlang_otp:carrier_pool_get", Res), + ok = check_tracepoint("org_erlang_otp:carrier_pool_put", Res), ok end. -%% com_ericsson_otp:carrier_destroy -%% com_ericsson_otp:carrier_create +%% org_erlang_otp:carrier_destroy +%% org_erlang_otp:carrier_create t_memory_carrier(Config) -> case have_carriers(ets_alloc) of false -> {skip, "No Memory Carriers configured on system."}; true -> - ok = lttng_start_event("com_ericsson_otp:carrier_*", Config), + ok = lttng_start_event("org_erlang_otp:carrier_*", Config), ok = ets_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:carrier_destroy", Res), - ok = check_tracepoint("com_ericsson_otp:carrier_create", Res), + ok = check_tracepoint("org_erlang_otp:carrier_destroy", Res), + ok = check_tracepoint("org_erlang_otp:carrier_create", Res), ok end. -%% com_ericsson_otp:aio_pool_put -%% com_ericsson_otp:aio_pool_get +%% org_erlang_otp:aio_pool_put +%% org_erlang_otp:aio_pool_get t_async_io_pool(Config) -> case have_async_threads() of false -> {skip, "No Async Threads configured on system."}; true -> - ok = lttng_start_event("com_ericsson_otp:aio_pool_*", Config), + ok = lttng_start_event("org_erlang_otp:aio_pool_*", Config), Path1 = proplists:get_value(priv_dir, Config), {ok, [[Path2]]} = init:get_argument(home), @@ -168,16 +168,16 @@ t_async_io_pool(Config) -> {ok, _} = file:list_dir(Path2), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:aio_pool_put", Res), - ok = check_tracepoint("com_ericsson_otp:aio_pool_get", Res), + ok = check_tracepoint("org_erlang_otp:aio_pool_put", Res), + ok = check_tracepoint("org_erlang_otp:aio_pool_get", Res), ok end. -%% com_ericsson_otp:driver_start -%% com_ericsson_otp:driver_stop +%% org_erlang_otp:driver_start +%% org_erlang_otp:driver_stop t_driver_start_stop(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_*", Config), timer:sleep(500), Path = proplists:get_value(priv_dir, Config), Name = filename:join(Path, "sometext.txt"), @@ -186,35 +186,35 @@ t_driver_start_stop(Config) -> {ok, Bin} = file:read_file(Name), timer:sleep(500), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_start", Res), - ok = check_tracepoint("com_ericsson_otp:driver_stop", Res), - ok = check_tracepoint("com_ericsson_otp:driver_control", Res), - ok = check_tracepoint("com_ericsson_otp:driver_outputv", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_async", Res), + ok = check_tracepoint("org_erlang_otp:driver_start", Res), + ok = check_tracepoint("org_erlang_otp:driver_stop", Res), + ok = check_tracepoint("org_erlang_otp:driver_control", Res), + ok = check_tracepoint("org_erlang_otp:driver_outputv", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res), ok. -%% com_ericsson_otp:driver_control -%% com_ericsson_otp:driver_outputv -%% com_ericsson_otp:driver_ready_async +%% org_erlang_otp:driver_control +%% org_erlang_otp:driver_outputv +%% org_erlang_otp:driver_ready_async t_driver_control_ready_async(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_control", Config), - ok = lttng_start_event("com_ericsson_otp:driver_outputv", Config), - ok = lttng_start_event("com_ericsson_otp:driver_ready_async", Config), + ok = lttng_start_event("org_erlang_otp:driver_control", Config), + ok = lttng_start_event("org_erlang_otp:driver_outputv", Config), + ok = lttng_start_event("org_erlang_otp:driver_ready_async", Config), Path = proplists:get_value(priv_dir, Config), Name = filename:join(Path, "sometext.txt"), Bin = txt(), ok = file:write_file(Name, Bin), {ok, Bin} = file:read_file(Name), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_control", Res), - ok = check_tracepoint("com_ericsson_otp:driver_outputv", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_async", Res), + ok = check_tracepoint("org_erlang_otp:driver_control", Res), + ok = check_tracepoint("org_erlang_otp:driver_outputv", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res), ok. -%% com_ericsson_otp:driver_ready_input -%% com_ericsson_otp:driver_ready_output +%% org_erlang_otp:driver_ready_input +%% org_erlang_otp:driver_ready_output t_driver_ready_input_output(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_ready_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_ready_*", Config), timer:sleep(500), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, active) end), @@ -230,15 +230,15 @@ t_driver_ready_input_output(Config) -> timer:sleep(500), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_ready_input", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_output", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_input", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_output", Res), ok. -%% com_ericsson_otp:driver_stop_select -%% com_ericsson_otp:driver_timeout +%% org_erlang_otp:driver_stop_select +%% org_erlang_otp:driver_timeout t_driver_timeout(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_*", Config), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, timeout) end), receive {Pid, accept} -> ok end, @@ -247,16 +247,16 @@ t_driver_timeout(Config) -> receive {Pid, done} -> ok end, ok = gen_tcp:close(Sock), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_timeout", Res), - ok = check_tracepoint("com_ericsson_otp:driver_stop_select", Res), + ok = check_tracepoint("org_erlang_otp:driver_timeout", Res), + ok = check_tracepoint("org_erlang_otp:driver_stop_select", Res), ok. -%% com_ericsson_otp:driver_call -%% com_ericsson_otp:driver_output -%% com_ericsson_otp:driver_init -%% com_ericsson_otp:driver_finish +%% org_erlang_otp:driver_call +%% org_erlang_otp:driver_output +%% org_erlang_otp:driver_init +%% org_erlang_otp:driver_finish t_driver_caller(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_*", Config), Drv = 'caller_drv', os:putenv("CALLER_DRV_USE_OUTPUTV", "false"), @@ -282,25 +282,25 @@ t_driver_caller(Config) -> erl_ddll:unload_driver(Drv), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_call", Res), - ok = check_tracepoint("com_ericsson_otp:driver_output", Res), - ok = check_tracepoint("com_ericsson_otp:driver_init", Res), - ok = check_tracepoint("com_ericsson_otp:driver_finish", Res), + ok = check_tracepoint("org_erlang_otp:driver_call", Res), + ok = check_tracepoint("org_erlang_otp:driver_output", Res), + ok = check_tracepoint("org_erlang_otp:driver_init", Res), + ok = check_tracepoint("org_erlang_otp:driver_finish", Res), ok. -%% com_ericsson_otp:scheduler_poll +%% org_erlang_otp:scheduler_poll t_scheduler_poll(Config) -> - ok = lttng_start_event("com_ericsson_otp:scheduler_poll", Config), + ok = lttng_start_event("org_erlang_otp:scheduler_poll", Config), ok = memory_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:scheduler_poll", Res), + ok = check_tracepoint("org_erlang_otp:scheduler_poll", Res), ok. -%% com_ericsson_otp:driver_flush +%% org_erlang_otp:driver_flush t_driver_flush(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_flush", Config), + ok = lttng_start_event("org_erlang_otp:driver_flush", Config), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, passive_no_read) end), @@ -324,7 +324,7 @@ t_driver_flush(Config) -> receive {Pid, done} -> ok end, Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_flush", Res), + ok = check_tracepoint("org_erlang_otp:driver_flush", Res), ok. %% @@ -416,29 +416,29 @@ tcp_server(Pid, Type) -> txt() -> <<"%% tracepoints\n" "%%\n" - "%% com_ericsson_otp:carrier_pool_get\n" - "%% com_ericsson_otp:carrier_pool_put\n" - "%% com_ericsson_otp:carrier_destroy\n" - "%% com_ericsson_otp:carrier_create\n" - "%% com_ericsson_otp:aio_pool_put\n" - "%% com_ericsson_otp:aio_pool_get\n" - "%% com_ericsson_otp:driver_control\n" - "%% com_ericsson_otp:driver_call\n" - "%% com_ericsson_otp:driver_finish\n" - "%% com_ericsson_otp:driver_ready_async\n" - "%% com_ericsson_otp:driver_process_exit\n" - "%% com_ericsson_otp:driver_stop\n" - "%% com_ericsson_otp:driver_flush\n" - "%% com_ericsson_otp:driver_stop_select\n" - "%% com_ericsson_otp:driver_timeout\n" - "%% com_ericsson_otp:driver_event\n" - "%% com_ericsson_otp:driver_ready_output\n" - "%% com_ericsson_otp:driver_ready_input\n" - "%% com_ericsson_otp:driver_output\n" - "%% com_ericsson_otp:driver_outputv\n" - "%% com_ericsson_otp:driver_init\n" - "%% com_ericsson_otp:driver_start\n" - "%% com_ericsson_otp:scheduler_poll">>. + "%% org_erlang_otp:carrier_pool_get\n" + "%% org_erlang_otp:carrier_pool_put\n" + "%% org_erlang_otp:carrier_destroy\n" + "%% org_erlang_otp:carrier_create\n" + "%% org_erlang_otp:aio_pool_put\n" + "%% org_erlang_otp:aio_pool_get\n" + "%% org_erlang_otp:driver_control\n" + "%% org_erlang_otp:driver_call\n" + "%% org_erlang_otp:driver_finish\n" + "%% org_erlang_otp:driver_ready_async\n" + "%% org_erlang_otp:driver_process_exit\n" + "%% org_erlang_otp:driver_stop\n" + "%% org_erlang_otp:driver_flush\n" + "%% org_erlang_otp:driver_stop_select\n" + "%% org_erlang_otp:driver_timeout\n" + "%% org_erlang_otp:driver_event\n" + "%% org_erlang_otp:driver_ready_output\n" + "%% org_erlang_otp:driver_ready_input\n" + "%% org_erlang_otp:driver_output\n" + "%% org_erlang_otp:driver_outputv\n" + "%% org_erlang_otp:driver_init\n" + "%% org_erlang_otp:driver_start\n" + "%% org_erlang_otp:scheduler_poll">>. load_driver(Dir, Driver) -> case erl_ddll:load_driver(Dir, Driver) of -- cgit v1.2.3 From f429adba7e7a862b9949821b40aa3cba12455b3e Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Wed, 8 Jun 2016 09:56:04 +0200 Subject: Remove internal state BOUND from inet_drv --- erts/emulator/drivers/common/inet_drv.c | 81 +++++++++++++++----------------- erts/preloaded/ebin/prim_inet.beam | Bin 76292 -> 76348 bytes erts/preloaded/src/prim_inet.erl | 4 +- 3 files changed, 41 insertions(+), 44 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 06dfb2dd10..93ea9f5dcb 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -627,6 +627,7 @@ static int is_nonzero(const char *s, size_t n) */ /* general address encode/decode tag */ +#define INET_AF_UNSPEC 0 #define INET_AF_INET 1 #define INET_AF_INET6 2 #define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */ @@ -655,7 +656,7 @@ static int is_nonzero(const char *s, size_t n) /* INET_REQ_GETSTATUS enumeration */ #define INET_F_OPEN 0x0001 -#define INET_F_BOUND 0x0002 +/* INET_F_BOUND removed - renumber when there comes a bigger rewrite */ #define INET_F_ACTIVE 0x0004 #define INET_F_LISTEN 0x0008 #define INET_F_CON 0x0010 @@ -853,19 +854,15 @@ static int is_nonzero(const char *s, size_t n) #define INET_STATE_CLOSED (0) #define INET_STATE_OPEN (INET_F_OPEN) -#define INET_STATE_BOUND (INET_STATE_OPEN | INET_F_BOUND) -#define INET_STATE_CONNECTED (INET_STATE_BOUND | INET_F_ACTIVE) -#define INET_STATE_LISTENING (INET_STATE_BOUND | INET_F_LISTEN) -#define INET_STATE_CONNECTING (INET_STATE_BOUND | INET_F_CON) +#define INET_STATE_CONNECTED (INET_STATE_OPEN | INET_F_ACTIVE) +#define INET_STATE_LISTENING (INET_STATE_OPEN | INET_F_LISTEN) +#define INET_STATE_CONNECTING (INET_STATE_OPEN | INET_F_CON) #define INET_STATE_ACCEPTING (INET_STATE_LISTENING | INET_F_ACC) #define INET_STATE_MULTI_ACCEPTING (INET_STATE_ACCEPTING | INET_F_MULTI_CLIENT) #define IS_OPEN(d) \ (((d)->state & INET_F_OPEN) == INET_F_OPEN) -#define IS_BOUND(d) \ - (((d)->state & INET_F_BOUND) == INET_F_BOUND) - #define IS_CONNECTED(d) \ (((d)->state & INET_STATE_CONNECTED) == INET_STATE_CONNECTED) @@ -1287,6 +1284,7 @@ static int async_ref = 0; /* async reference id generator */ static ErlDrvTermData am_ok; static ErlDrvTermData am_undefined; +static ErlDrvTermData am_unspec; static ErlDrvTermData am_tcp; static ErlDrvTermData am_error; static ErlDrvTermData am_einval; @@ -1553,10 +1551,18 @@ static int load_address(ErlDrvTermData* spec, int i, char* buf) break; } #endif - default: { /* INET_AF_UNDEFINED */ - i = LOAD_ATOM(spec, i, am_undefined); + case INET_AF_UNSPEC: { + i = LOAD_ATOM(spec, i, am_unspec); + i = LOAD_BUF2BINARY(spec, i, buf, 0); + spec[i++] = ERL_DRV_TUPLE; + spec[i++] = 2; spec[i++] = ERL_DRV_INT; spec[i++] = 0; + break; + } + default: { /* INET_AF_UNDEFINED */ + i = LOAD_ATOM(spec, i, am_undefined); + i = LOAD_BUF2BINARY(spec, i, buf, 0); spec[i++] = ERL_DRV_TUPLE; spec[i++] = 2; spec[i++] = ERL_DRV_INT; @@ -3823,6 +3829,7 @@ static int inet_init() INIT_ATOM(ok); INIT_ATOM(undefined); + INIT_ATOM(unspec); INIT_ATOM(tcp); #ifdef HAVE_UDP INIT_ATOM(udp); @@ -4149,6 +4156,10 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) return 0; } #endif + else if (family == AF_UNSPEC) { + dst[0] = INET_AF_UNSPEC; + *len = 1; + } else { dst[0] = INET_AF_UNDEFINED; *len = 1; @@ -4419,10 +4430,12 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, char** rbuf, ErlDrvSizeT rsize) { inet_address name; - unsigned int sz = sizeof(name); + unsigned int sz; if (bound) { /* check that it is a socket and that the socket is bound */ + sz = sizeof(name); + sys_memzero((char *) &name, sz); if (IS_SOCKET_ERROR(sock_name(s, (struct sockaddr*) &name, &sz))) return ctl_error(sock_errno(), rbuf, rsize); if (name.sa.sa_family != domain) @@ -4437,10 +4450,7 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, driver_select(desc->port, desc->event, ERL_DRV_READ, 1); #endif - if (bound) - desc->state = INET_STATE_BOUND; - else - desc->state = INET_STATE_OPEN; + desc->state = INET_STATE_OPEN; if (type == SOCK_STREAM) { /* check if connected */ sz = sizeof(name); @@ -8448,7 +8458,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (len != 4) return ctl_error(EINVAL, rbuf, rsize); if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); #ifdef HAVE_SCTP if (IS_SCTP(desc) && p_sctp_getpaddrs) { @@ -8524,7 +8533,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (len != 4) return ctl_error(EINVAL, rbuf, rsize); if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); #ifdef HAVE_SCTP if (IS_SCTP(desc) && p_sctp_getladdrs) { @@ -8546,6 +8554,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, int i; sz = sizeof(addr); + sys_memzero((char *) &addr, sz); i = sock_name(desc->s, (struct sockaddr *) &addr, &sz); return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize, sz); } @@ -8559,15 +8568,13 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, DEBUGF(("inet_ctl(%ld): NAME\r\n", (long)desc->port)); - if (!IS_BOUND(desc)) - return ctl_error(EINVAL, rbuf, rsize); /* address is not valid */ - if ((ptr = desc->name_ptr) != NULL) { sz = desc->name_addr_len; } else { ptr = &name; sz = sizeof(name); + sys_memzero((char *) &name, sz); if (IS_SOCKET_ERROR (sock_name(desc->s, (struct sockaddr*)ptr, &sz))) return ctl_error(sock_errno(), rbuf, rsize); @@ -8612,11 +8619,12 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (IS_SOCKET_ERROR(sock_bind(desc->s,(struct sockaddr*) &local, len))) return ctl_error(sock_errno(), rbuf, rsize); - desc->state = INET_STATE_BOUND; + desc->state = INET_STATE_OPEN; port = inet_address_port(&local); if (port == 0) { SOCKLEN_T adrlen = sizeof(local); + sys_memzero((char *) &local, adrlen); sock_name(desc->s, &local.sa, &adrlen); port = inet_address_port(&local); } @@ -9136,8 +9144,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_xerror(EXBADPORT, rbuf, rsize); if (!IS_OPEN(INETP(desc))) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(INETP(desc))) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (len != 2) return ctl_error(EINVAL, rbuf, rsize); backlog = get_int16(buf); @@ -9160,8 +9166,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_xerror(EXBADPORT, rbuf, rsize); if (IS_CONNECTED(INETP(desc))) return ctl_error(EISCONN, rbuf, rsize); - if (!IS_BOUND(INETP(desc))) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (IS_CONNECTING(INETP(desc))) return ctl_error(EINVAL, rbuf, rsize); if (len < 6) @@ -9262,6 +9266,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); } else { n = sizeof(desc->inet.remote); + sys_memzero((char *) &remote, n); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &n); if (s == INVALID_SOCKET) { if (sock_errno() == ERRNO_BLOCK) { @@ -10147,6 +10152,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) inet_async_op *this_op = desc->inet.opt; len = sizeof(desc->inet.remote); + sys_memzero((char *) &remote, len); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len); if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) { /* Just try again, no real error, just a ghost trigger from poll, @@ -10213,6 +10219,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) while (desc->inet.state == INET_STATE_MULTI_ACCEPTING) { len = sizeof(desc->inet.remote); + sys_memzero((char *) &remote, len); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len); if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) { /* Just try again, no real error, keep the last return code */ @@ -10653,7 +10660,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) (struct sockaddr*) &desc->inet.remote, &sz); if (IS_SOCKET_ERROR(code)) { - desc->inet.state = INET_STATE_BOUND; /* restore state */ + desc->inet.state = INET_STATE_OPEN; /* restore state */ ret = async_error(INETP(desc), sock_errno()); goto done; } @@ -10666,7 +10673,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) (void *)&error, &sz); if ((code < 0) || error) { - desc->inet.state = INET_STATE_BOUND; /* restore state */ + desc->inet.state = INET_STATE_OPEN; /* restore state */ ret = async_error(INETP(desc), error); goto done; } @@ -11075,8 +11082,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_xerror(EXBADSEQ, rbuf, rsize); #ifdef HAVE_SCTP if (IS_SCTP(desc)) { inet_address remote; @@ -11163,8 +11168,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_xerror(EXBADPORT, rbuf, rsize); if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (len != 2) return ctl_error(EINVAL, rbuf, rsize); @@ -11211,7 +11214,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_error(sock_errno(), rbuf, rsize); } - desc->state = INET_STATE_BOUND; + desc->state = INET_STATE_OPEN; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } @@ -11228,8 +11231,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_xerror(EXBADPORT, rbuf, rsize); if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (! p_sctp_peeloff) return ctl_error(ENOTSUP, rbuf, rsize); @@ -11270,8 +11271,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, /* INPUT: Timeout(4), Length(4) */ if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_error(EINVAL, rbuf, rsize); if (desc->active || (len != 8)) return ctl_error(EINVAL, rbuf, rsize); timeout = get_int32(buf); @@ -11336,10 +11335,6 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) inet_reply_error(desc, EINVAL); return; } - if (!IS_BOUND(desc)) { - inet_reply_error(desc, EINVAL); - return; - } #ifdef HAVE_SCTP if (IS_SCTP(desc)) @@ -11470,6 +11465,8 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) while(packet_count--) { unsigned int len = sizeof(other); + sys_memzero((char *) &other, sizeof(other)); + /* udesc->i_buf is only kept between SCTP fragments */ if (udesc->i_buf == NULL) { udesc->i_bufsz = desc->bufsz + len; @@ -11665,7 +11662,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) (struct sockaddr*) &desc->remote, &sz); if (IS_SOCKET_ERROR(code)) { - desc->state = INET_STATE_BOUND; /* restore state */ + desc->state = INET_STATE_OPEN; /* restore state */ ret = async_error(desc, sock_errno()); goto done; } @@ -11678,7 +11675,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) (void *)&error, &sz); if ((code < 0) || error) { - desc->state = INET_STATE_BOUND; /* restore state */ + desc->state = INET_STATE_OPEN; /* restore state */ ret = async_error(desc, error); goto done; } diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index edb86d35a6..fdf8bbf4e2 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 71dbfbd0a7..7f8097e17e 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -1746,8 +1746,8 @@ enc_value_2(addr, {inet6,{IP,Port}}) -> [?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)]; enc_value_2(addr, {local,Addr}) -> %% A binary is passed as is, but anything else will be - %% regarded as a filename and therefore UTF-8 encoded - %% if the system filename encoding flag so dictates. + %% regarded as a filename and therefore encoded according to + %% the current system filename encoding. Bin = if is_binary(Addr) -> -- cgit v1.2.3 From 049611f98d67f11f8e06fe0b0fc50eb8ca1925d8 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 8 Jun 2016 14:56:31 +0200 Subject: No GC on dirty IO schedulers --- erts/emulator/beam/erl_process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index c0b1d7246c..5193be85b4 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -10082,7 +10082,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) } } - if (ERTS_IS_GC_DESIRED(p)) { + if (ERTS_IS_GC_DESIRED(p) && !ERTS_SCHEDULER_IS_DIRTY_IO(esdp)) { if (!(state & ERTS_PSFLG_EXITING) && !(p->flags & (F_DELAY_GC|F_DISABLE_GC))) { int cost = scheduler_gc_proc(p, reds); calls += cost; -- cgit v1.2.3 From 77192f7f32d4bbe293a6e8cb9da79b8a6dd6b181 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 8 Jun 2016 14:58:33 +0200 Subject: Replace enif_is_on_dirty_scheduler() with enif_thread_type() --- erts/doc/src/erl_nif.xml | 29 ++++++++++++++-------- erts/emulator/beam/erl_nif.c | 19 ++++++++------ erts/emulator/beam/erl_nif.h | 11 ++++++++ erts/emulator/beam/erl_nif_api_funcs.h | 4 +-- .../test/dirty_nif_SUITE_data/dirty_nif_SUITE.c | 11 +++++--- 5 files changed, 50 insertions(+), 24 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 4efd155b09..b2e2254a65 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -1101,15 +1101,6 @@ typedef enum { Erlang operators =:= and =/=.

- intenif_is_on_dirty_scheduler(ErlNifEnv* env) - Check to see if executing on a dirty scheduler thread - -

Check to see if the current NIF is executing on a dirty scheduler thread. If - executing on a dirty scheduler thread true returned; otherwise false.

-

This function can only be used from a NIF-calling thread, and with an - environment corresponding to currently executing processes.

-
-
intenif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term) Determine if a term is a pid

Return true if term is a pid.

@@ -1820,7 +1811,25 @@ enif_map_iterator_destroy(env, &iter);

Same as erl_drv_thread_self.

- + intenif_thread_type(void) + Determine type of current thread + +

Determine the type of currently executing thread. A positive value + indicates a scheduler thread while a negative value or zero indicates + another type of thread. Currently the following specific types exist + (which may be extended in the future):

+ + ERL_NIF_THR_UNDEFINED +

Undefined thread that is not a scheduler thread.

+ ERL_NIF_THR_NORMAL_SCHEDULER +

A normal scheduler thread.

+ ERL_NIF_THR_DIRTY_CPU_SCHEDULER +

A dirty CPU scheduler thread.

+ ERL_NIF_THR_DIRTY_IO_SCHEDULER +

A dirty I/O scheduler thread.

+
+
+
ErlNifTimeenif_time_offset(ErlNifTimeUnit time_unit) Get current Time Offset diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 4fd82bad10..039f97ef43 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -2658,18 +2658,21 @@ done: } int -enif_is_on_dirty_scheduler(ErlNifEnv* env) +enif_thread_type(void) { - int scheduler; - Process *c_p; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); - execution_state(env, &c_p, &scheduler); + if (!esdp) + return ERL_NIF_THR_UNDEFINED; + + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) + return ERL_NIF_THR_NORMAL_SCHEDULER; - if (!c_p || !scheduler) - erts_exit(ERTS_ABORT_EXIT, "enif_is_on_dirty_scheduler: " - "Invalid env"); + if (ERTS_SCHEDULER_IS_DIRTY_CPU(esdp)) + return ERL_NIF_THR_DIRTY_CPU_SCHEDULER; - return scheduler < 0; + ASSERT(ERTS_SCHEDULER_IS_DIRTY_IO(esdp)); + return ERL_NIF_THR_DIRTY_IO_SCHEDULER; } /* Maps */ diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index da7a754757..494971e118 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -209,6 +209,17 @@ typedef enum { ERL_NIF_BIN2TERM_SAFE = 0x20000000 } ErlNifBinaryToTerm; +/* + * Return values from enif_thread_type(). Negative values + * reserved for specific types of non-scheduler threads. + * Positive values reserved for scheduler thread types. + */ + +#define ERL_NIF_THR_UNDEFINED 0 +#define ERL_NIF_THR_NORMAL_SCHEDULER 1 +#define ERL_NIF_THR_DIRTY_CPU_SCHEDULER 2 +#define ERL_NIF_THR_DIRTY_IO_SCHEDULER 3 + #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct { diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index b211ab4b16..9a8f216773 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -173,7 +173,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, E ERL_NIF_API_FUNC_DECL(int, enif_term_to_binary, (ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)); ERL_NIF_API_FUNC_DECL(size_t, enif_binary_to_term, (ErlNifEnv *env, const unsigned char* data, size_t sz, ERL_NIF_TERM *term, unsigned int opts)); ERL_NIF_API_FUNC_DECL(int, enif_port_command, (ErlNifEnv *env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)); -ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); +ERL_NIF_API_FUNC_DECL(int,enif_thread_type,(void)); ERL_NIF_API_FUNC_DECL(int,enif_snprintf,(char * buffer, size_t size, const char *format, ...)); /* @@ -330,7 +330,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_snprintf,(char * buffer, size_t size, const char # define enif_term_to_binary ERL_NIF_API_FUNC_MACRO(enif_term_to_binary) # define enif_binary_to_term ERL_NIF_API_FUNC_MACRO(enif_binary_to_term) # define enif_port_command ERL_NIF_API_FUNC_MACRO(enif_port_command) -# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler) +# define enif_thread_type ERL_NIF_API_FUNC_MACRO(enif_thread_type) # define enif_snprintf ERL_NIF_API_FUNC_MACRO(enif_snprintf) /* diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c index e38bececde..d92933a096 100644 --- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c +++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c @@ -48,7 +48,8 @@ static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ char s[10]; ErlNifBinary b; if (have_dirty_schedulers()) { - assert(enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type() + || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type()); } assert(argc == 3); enif_get_int(env, argv[0], &n); @@ -65,7 +66,7 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM int n; char s[10]; ErlNifBinary b; - assert(!enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_NORMAL_SCHEDULER == enif_thread_type()); if (argc != 3) return enif_make_badarg(env); if (have_dirty_schedulers()) { @@ -151,7 +152,8 @@ dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifPid pid; ErlNifEnv* msg_env = NULL; - assert(enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type() + || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type()); /* If we get a pid argument, it indicates a process involved in the test wants a message from us. Prior to the sleep we send a 'ready' @@ -221,7 +223,8 @@ static ERL_NIF_TERM dirty_heap_access_nif(ErlNifEnv* env, int argc, const ERL_NI { ERL_NIF_TERM res = enif_make_list(env, 0); int i; - assert(enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type() + || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type()); for (i = 0; i < 1000; i++) res = enif_make_list_cell(env, enif_make_copy(env, argv[0]), res); -- cgit v1.2.3 From 0f072d17a3ec6d793e7bec0787e8af25bca06dfb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 8 Jun 2016 15:22:01 +0200 Subject: Revert "erts: Remove unnecessary access of 'is_resizing'" This reverts commit f4bdac18cb9dd45185e911308a5ebd95ff10d7fd. --- erts/emulator/beam/erl_db_hash.c | 21 +++++++++++---------- erts/emulator/beam/erl_db_hash.h | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 074ac6d64e..4c4ff9b470 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -671,8 +671,8 @@ int db_create_hash(Process *p, DbTable *tbl) tb->nsegs = NSEG_1; tb->nslots = SEGSZ; -#ifdef ERTS_SMP erts_smp_atomic_init_nob(&tb->is_resizing, 0); +#ifdef ERTS_SMP if (tb->common.type & DB_FINE_LOCKED) { erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; int i; @@ -2605,22 +2605,23 @@ static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2, static ERTS_INLINE int begin_resizing(DbTableHash* tb) { -#ifdef ERTS_SMP if (DB_USING_FINE_LOCKING(tb)) - return !erts_atomic_xchg_acqb(&tb->is_resizing, 1); - else - ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock)); -#endif - return 1; + return !erts_smp_atomic_xchg_acqb(&tb->is_resizing, 1); + else { + if (erts_smp_atomic_read_nob(&tb->is_resizing)) + return 0; + erts_smp_atomic_set_nob(&tb->is_resizing, 1); + return 1; + } } static ERTS_INLINE void done_resizing(DbTableHash* tb) { -#ifdef ERTS_SMP if (DB_USING_FINE_LOCKING(tb)) - erts_atomic_set_relb(&tb->is_resizing, 0); -#endif + erts_smp_atomic_set_relb(&tb->is_resizing, 0); + else + erts_smp_atomic_set_nob(&tb->is_resizing, 0); } /* Grow table with one new bucket. diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h index 081ff8fafc..e654363cd5 100644 --- a/erts/emulator/beam/erl_db_hash.h +++ b/erts/emulator/beam/erl_db_hash.h @@ -60,8 +60,8 @@ typedef struct db_table_hash { /* List of slots where elements have been deleted while table was fixed */ erts_smp_atomic_t fixdel; /* (FixedDeletion*) */ erts_smp_atomic_t nactive; /* Number of "active" slots */ -#ifdef ERTS_SMP erts_smp_atomic_t is_resizing; /* grow/shrink in progress */ +#ifdef ERTS_SMP DbTableHashFineLocks* locks; #endif #ifdef VALGRIND -- cgit v1.2.3 From 6bf6e2cfd4dbe48e52456d4111f9ffb52e991b6c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 8 Jun 2016 15:22:16 +0200 Subject: Revert "erts: Change ETS hash load factor" This reverts commit 7c133fb1094ad1cabbb5cfc157483a43c816c6a9. --- erts/emulator/beam/erl_db_hash.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 4c4ff9b470..74979f984a 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -95,8 +95,7 @@ /* * The following symbols can be manipulated to "tune" the linear hash array */ -#define GROW_LIMIT(NACTIVE) ((NACTIVE)*2) -#define SHRINK_LIMIT(NACTIVE) ((NACTIVE) / 2) +#define CHAIN_LEN 6 /* Medium bucket chain len */ /* Number of slots per segment */ #define SEGSZ_EXP 8 @@ -464,7 +463,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle); static ERTS_INLINE void try_shrink(DbTableHash* tb) { int nactive = NACTIVE(tb); - if (nactive > SEGSZ && NITEMS(tb) < SHRINK_LIMIT(nactive) + if (nactive > SEGSZ && NITEMS(tb) < (nactive * CHAIN_LEN) && !IS_FIXED(tb)) { shrink(tb, nactive); } @@ -863,7 +862,7 @@ Lnew: WUNLOCK_HASH(lck); { int nactive = NACTIVE(tb); - if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) { + if (nitems > nactive * (CHAIN_LEN+1) && !IS_FIXED(tb)) { grow(tb, nactive); } } @@ -2251,12 +2250,12 @@ static int db_free_table_continue_hash(DbTable *tbl) done /= 2; while(tb->nslots != 0) { - done += 1 + SEGSZ/64 + free_seg(tb, 1); + free_seg(tb, 1); /* * If we have done enough work, get out here. */ - if (done >= DELETE_RECORD_LIMIT) { + if (++done >= (DELETE_RECORD_LIMIT / CHAIN_LEN / SEGSZ)) { return 0; /* Not done */ } } @@ -2872,7 +2871,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj, int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems); int nactive = NACTIVE(tb); - if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) { + if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) { grow(tb, nactive); } } -- cgit v1.2.3 From 99655be9a918671b56846ff1731196c0d467f4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 8 Jun 2016 18:58:21 +0200 Subject: erts: Don't crash on maps on crash dumps - Large Maps could cause a stack overrun during crash dump generation. - This is a simple workaround until a solution has been implemented. - The error has no impact on a running system. --- erts/emulator/beam/erl_process_dump.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index eeaa9a569c..a70dfb8e73 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -560,6 +560,11 @@ dump_externally(int to, void *to_arg, Eterm term) } } + /* Do not handle maps */ + if (is_map(term)) { + term = am_undefined; + } + s = p = sbuf; erts_encode_ext(term, &p); erts_print(to, to_arg, "E%X:", p-s); -- cgit v1.2.3 From 16e895198a541ccbbbe6c970bd9572cf347a9c77 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Wed, 8 Jun 2016 14:46:44 +0200 Subject: Document the local (unix) address family --- erts/preloaded/src/prim_inet.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 7f8097e17e..560810d222 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -1747,7 +1747,7 @@ enc_value_2(addr, {inet6,{IP,Port}}) -> enc_value_2(addr, {local,Addr}) -> %% A binary is passed as is, but anything else will be %% regarded as a filename and therefore encoded according to - %% the current system filename encoding. + %% the current system filename encoding mode. Bin = if is_binary(Addr) -> -- cgit v1.2.3 From 3b2d98cf1cf8983896293a220275b6ebfa7a609d Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Thu, 19 May 2016 09:21:02 +0200 Subject: erts: Correct character repr in doc of the abstract format Barklund's and Virding's spec states that character literals are represented by {integer, Line, L}. The copy-and-paste bug is fixed the description of the abstract format. --- erts/doc/src/absform.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index bfabb7f042..0b04f8f70e 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -152,9 +152,11 @@ If L is an atom literal, then Rep(L) = {atom,LINE,L}. + If L is a character literal, then + Rep(L) = {char,LINE,L}. If L is a float literal, then Rep(L) = {float,LINE,L}. - If L is an integer or character literal, then + If L is an integer literal, then Rep(L) = {integer,LINE,L}. If L is a string literal consisting of the characters C_1, ..., C_k, then -- cgit v1.2.3 From 7bb89126c6e6b25a120e02c1d680093f08e137e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 9 Jun 2016 18:35:54 +0200 Subject: erts: Fix erl_nif.xml xmllint errors --- erts/doc/src/erl_nif.xml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index b2e2254a65..123d353432 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -1812,23 +1812,23 @@ enif_map_iterator_destroy(env, &iter);

intenif_thread_type(void) - Determine type of current thread - -

Determine the type of currently executing thread. A positive value - indicates a scheduler thread while a negative value or zero indicates - another type of thread. Currently the following specific types exist - (which may be extended in the future):

- - ERL_NIF_THR_UNDEFINED -

Undefined thread that is not a scheduler thread.

- ERL_NIF_THR_NORMAL_SCHEDULER -

A normal scheduler thread.

- ERL_NIF_THR_DIRTY_CPU_SCHEDULER -

A dirty CPU scheduler thread.

- ERL_NIF_THR_DIRTY_IO_SCHEDULER -

A dirty I/O scheduler thread.

-
-
+ Determine type of current thread + +

Determine the type of currently executing thread. A positive value + indicates a scheduler thread while a negative value or zero indicates + another type of thread. Currently the following specific types exist + (which may be extended in the future):

+ + ERL_NIF_THR_UNDEFINED +

Undefined thread that is not a scheduler thread.

+ ERL_NIF_THR_NORMAL_SCHEDULER +

A normal scheduler thread.

+ ERL_NIF_THR_DIRTY_CPU_SCHEDULER +

A dirty CPU scheduler thread.

+ ERL_NIF_THR_DIRTY_IO_SCHEDULER +

A dirty I/O scheduler thread.

+
+
ErlNifTimeenif_time_offset(ErlNifTimeUnit time_unit) -- cgit v1.2.3 From 6c5277b154316137ebc9f83ed8566357ffebd3e0 Mon Sep 17 00:00:00 2001 From: Kenji Rikitake Date: Fri, 10 Jun 2016 09:39:08 +0900 Subject: Disregard MAP_NORESERVE for FreeBSD A fix for running 19.0-rc2 on FreeBSD with HiPE enabled. * erl_mmap.h: disregard MAP_NORESERVE for FreeBSD MAP_NORESERVE is undefined in FreeBSD 10.x and later. This is to enable 64bit HiPE experimentally on FreeBSD. Note that on FreeBSD MAP_NORESERVE was "never implemented" even before 11.x (and the flag does not exist in /usr/src/sys/vm/mmap.c of 10.3-STABLE r301478 either), and HiPE was working on OTP 18.3.3, so mandating MAP_NORESERVE on FreeBSD might not be needed. See the following message on how MAP_NORESERVE was treated on FreeBSD: * erl_mmap.c: disable MAP_NORESERVE for FreeBSD * See also --- erts/emulator/sys/common/erl_mmap.c | 8 ++++++++ erts/emulator/sys/common/erl_mmap.h | 12 +++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index 53009a1481..7bbb406f29 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -1334,9 +1334,17 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign) #define ERTS_MMAP_RESERVE_PROT_EXEC (ERTS_MMAP_PROT_EXEC) #define ERTS_MMAP_RESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED) #define ERTS_MMAP_UNRESERVE_PROT (PROT_NONE) +#if defined(__FreeBSD__) +#define ERTS_MMAP_UNRESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED) +#else #define ERTS_MMAP_UNRESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_NORESERVE|MAP_FIXED) +#endif /* __FreeBSD__ */ #define ERTS_MMAP_VIRTUAL_PROT (PROT_NONE) +#if defined(__FreeBSD__) +#define ERTS_MMAP_VIRTUAL_FLAGS (ERTS_MMAP_FLAGS) +#else #define ERTS_MMAP_VIRTUAL_FLAGS (ERTS_MMAP_FLAGS|MAP_NORESERVE) +#endif /* __FreeBSD__ */ static int os_reserve_physical(char *ptr, UWord size, int exec) diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 7ac61a82c1..fa51b663fa 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -38,7 +38,17 @@ # if HAVE_MREMAP # define ERTS_HAVE_OS_MREMAP 1 # endif -# if defined(MAP_FIXED) && defined(MAP_NORESERVE) +/* + * MAP_NORESERVE is undefined in FreeBSD 10.x and later. + * This is to enable 64bit HiPE experimentally on FreeBSD. + * Note that on FreeBSD MAP_NORESERVE was "never implemented" + * even before 11.x (and the flag does not exist in /usr/src/sys/vm/mmap.c + * of 10.3-STABLE r301478 either), and HiPE was working on OTP 18.3.3, + * so mandating MAP_NORESERVE on FreeBSD might not be needed. + * See the following message on how MAP_NORESERVE was treated on FreeBSD: + * + */ +# if defined(MAP_FIXED) && (defined(MAP_NORESERVE) || defined(__FreeBSD__)) # define ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION 1 # endif #endif -- cgit v1.2.3 From 3ee5343415d6ae0ce1ff1c2a2555051431a9315e Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Wed, 25 May 2016 14:37:03 +0200 Subject: erts: Add port monitors * erlang:monitor/2 with port argument is added, erlang:demonitor, using port task API and avoiding locking; * port_info and process_info support for monitored ports (with named port monitors support); * Exit signals contain type 'process' or 'port'; * Propagation of port exit signals; * Self-cleaning when origin process dies with monitor on; * 8 test cases + testcase for port driver crashing; * Documentation for all of the above (monitor, demonitor, port_info and process_info) updated --- erts/doc/src/erlang.xml | 239 +++++++----- erts/emulator/beam/bif.c | 410 ++++++++++++------- erts/emulator/beam/erl_bif_info.c | 100 +++-- erts/emulator/beam/erl_bif_port.c | 6 + erts/emulator/beam/erl_port.h | 58 ++- erts/emulator/beam/erl_process.c | 21 +- erts/emulator/beam/io.c | 390 +++++++++++++++--- erts/emulator/beam/register.c | 51 +-- erts/emulator/beam/register.h | 2 +- erts/emulator/test/monitor_SUITE.erl | 12 +- erts/emulator/test/port_SUITE.erl | 434 +++++++++++++++++---- erts/emulator/test/port_SUITE_data/Makefile.src | 2 +- .../test/port_SUITE_data/sleep_failure_drv.c | 76 ++++ erts/preloaded/src/erlang.erl | 19 +- 14 files changed, 1370 insertions(+), 450 deletions(-) create mode 100644 erts/emulator/test/port_SUITE_data/sleep_failure_drv.c (limited to 'erts') diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index e0c3fed0c2..fa13e4c142 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2916,107 +2916,105 @@ os_prompt% + Starts monitoring. + -

Send a monitor request of type Type to the - entity identified by Item. The caller of - monitor/2 will later be notified by a monitor message on the - following format if the monitored state is changed:

+

Sends a monitor request of type Type to the + entity identified by Item. If the monitored entity + does not exist or when it dies, the caller of monitor/2 will + be notified by a message on the following format:

{Tag, MonitorRef, Type, Object, Info}

The monitor request is an asynchronous signal. That is, it takes time before the signal reaches its destination.

-

Valid Types:

- - process - -

Monitor the existence of the process identified by - Item. Valid - Items in combination with the - process Type can be any of the following:

- - pid() - -

The process identifier of the process to monitor.

-
- {RegisteredName, Node} - -

A tuple consisting of a registered name of a process and - a node name. The process residing on the node Node - with the registered name {RegisteredName, Node} will - be monitored.

-
- RegisteredName - -

The process locally registered as RegisteredName - will become monitored.

-
-
-

When a registered name is used, the - process that has the registered name when the - monitor request reach its destination will be monitored. - The monitor is not effected if the registered name is - unregistered, or unregistered and later registered on another - process.

-

The monitor is triggered either when the monitored process - terminates, is non existing, or if the connection to it is - lost. In the case the connection to it is lost, we do not know - if it still exist or not. After this type of monitor has been - triggered, the monitor is automatically removed.

-

When the monitor is triggered a 'DOWN' message is - sent to the monitoring process. A 'DOWN' message has - the following pattern:

- {'DOWN', MonitorRef, Type, Object, Info} -

Here MonitorRef and Type are the same as - described earlier, and:

- - Object - -

equals:

- - Item - If Item is specified by a - process identifier. - {RegisteredName, Node} - If Item is specified as - RegisteredName, or {RegisteredName, Node} - where Node corresponds to the node that the - monitored process resides on. - -
- Info - -

Either the exit reason of the process, noproc - (non-existing process), or noconnection (no - connection to the node where the monitored process - resides).

-
-

The monitoring is turned off when the 'DOWN' - message is sent or when - demonitor/1 - is called.

-

If an attempt is made to monitor a process on an older node - (where remote process monitoring is not implemented or - where remote process monitoring by registered name is not - implemented), the call fails with badarg.

- -

The format of the 'DOWN' message changed in ERTS - version 5.2 (OTP R9B) for monitoring - by registered name. Element Object of - the 'DOWN' message could in earlier versions - sometimes be the process identifier of the monitored process and sometimes - be the registered name. Now element Object is - always a tuple consisting of the registered name and - the node name. Processes on new nodes (ERTS version 5.2 - or higher) always get 'DOWN' messages on - the new format even if they are monitoring processes on old - nodes. Processes on old nodes always get 'DOWN' - messages on the old format.

-
-
- time_offset + +

Type can be one of the following atoms: + process, port or time_offset.

+ +

A monitor is triggered only once, after that it is removed from + both monitoring process and the monitored entity. + Monitors are fired when the monitored process or port terminates, + does not exist at the moment of creation, or if the connection to + it is lost. In the case with connection, we lose knowledge about + the fact if it still exists or not. The monitoring is also turned off + when demonitor/1 + is called.

+ +

When monitoring by name please note, that the RegisteredName + is resolved to pid() or port() only once + at the moment of monitor instantiation, later changes to the name + registration will not affect the existing monitor.

+ +

When a monitor is triggered, a 'DOWN' message that has the + following pattern {'DOWN', MonitorRef, Type, Object, Info} + is sent to the monitoring process.

+ +

In monitor message MonitorRef and Type are the same as + described earlier, and:

+ + Object + +

The monitored entity, which triggered the event. When monitoring + a local process or port, Object will be equal to the + pid() or port() that was being monitored. When + monitoring process or port by name, Object will have format + {RegisteredName, Node} where RegisteredName is the + name which has been used with monitor/2 call and + Node is local or remote node name (for ports monitored by + name, Node is always local node name).

+
+ Info + +

Either the exit reason of the process, noproc + (process or port did not exist at the time of monitor creation), + or noconnection (no connection to the node where the + monitored process resides).

+
+ +

If an attempt is made to monitor a process on an older node + (where remote process monitoring is not implemented or + where remote process monitoring by registered name is not + implemented), the call fails with badarg.

+ +

The format of the 'DOWN' message changed in ERTS + version 5.2 (OTP R9B) for monitoring + by registered name. Element Object of + the 'DOWN' message could in earlier versions + sometimes be the process identifier of the monitored process and sometimes + be the registered name. Now element Object is + always a tuple consisting of the registered name and + the node name. Processes on new nodes (ERTS version 5.2 + or higher) always get 'DOWN' messages on + the new format even if they are monitoring processes on old + nodes. Processes on old nodes always get 'DOWN' + messages on the old format.

+
+ + + Monitoring a process + +

Creates monitor between the current process and another + process identified by Item, which can be a + pid() (local or remote), an atom RegisteredName or + a tuple {RegisteredName, Node} for a registered process, + located elsewhere.

+
+ + Monitoring a port + +

Creates monitor between the current process and a port + identified by Item, which can be a + port() (only local), an atom RegisteredName or + a tuple {RegisteredName, Node} for a registered port, + located on this node. Note, that attempt to monitor a remote port + will result in badarg.

+
+ + Monitoring a + time_offset

Monitor changes in time offset @@ -3072,15 +3070,17 @@ os_prompt% Note that you can observe the change of the time offset when calling erlang:time_offset() before you get the 'CHANGE' message.

-
+

Making several calls to monitor/2 for the same - Item and/or Type is not - an error; it results in as many independent monitoring instances.

+ Item and/or Type is not + an error; it results in as many independent monitoring instances.

+

The monitor functionality is expected to be extended. That is, - other Types and Items - are expected to be supported in a future release.

+ other Types and Items + are expected to be supported in a future release.

+

If or when monitor/2 is extended, other possible values for Tag, Object and @@ -4150,6 +4150,22 @@ os_prompt% + Which processes are monitoring this port. + +

Returns list of pids that are monitoring given port at the + moment.

+

If the port identified by Port is not open, + undefined is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + port_info/2 returns undefined.

+

Failure: badarg if Port is not a local + port identifier, or an atom.

+
+
+ + + Information about the name of a port.

Name is the command name set by @@ -4165,7 +4181,7 @@ os_prompt% - + Information about the OS pid of a port.

OsPid is the process identifier (or equivalent) @@ -4184,7 +4200,7 @@ os_prompt% - + Information about the output of a port.

Bytes is the total number of bytes written @@ -4203,7 +4219,7 @@ os_prompt% - + Information about the parallelism hint of a port.

Boolean corresponds to the port parallelism @@ -4214,7 +4230,7 @@ os_prompt% - + Information about the queue size of a port.

Bytes is the total number @@ -4231,7 +4247,7 @@ os_prompt% - + Information about the registered name of a port.

RegisteredName is the registered name of @@ -4865,10 +4881,19 @@ os_prompt%

A list of monitors (started by monitor/2) that are active for the process. For a local process monitor or a remote process monitor by a process - identifier, the list item is {process, Pid}. - For a remote process - monitor by name, the list item is - {process, {RegName, Node}}.

+ identifier, the list consists of:

+ + {process, Pid} + Process is monitored by pid. + {process, {RegName, Node}} + Local or remote process is monitored by name. + {port, PortId} + Local port is monitored by port id. + {port, {RegName, Node}} + Local port is monitored by name. Please note, that + remote port monitors are not supported, so Node will + always be the local node name. + {message_queue_data, MQD} diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index b18910e2c7..fc14061a44 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -282,20 +282,17 @@ res_no_proc: { } } -#define ERTS_DEMONITOR_FALSE 2 -#define ERTS_DEMONITOR_TRUE 1 -#define ERTS_DEMONITOR_BADARG 0 -#define ERTS_DEMONITOR_YIELD_TRUE -1 -#define ERTS_DEMONITOR_INTERNAL_ERROR -2 - -static int +/* This function is allowed to return range of values handled by demonitor/1-2 + * Namely: atoms true, false, yield, internal_error, badarg or THE_NON_VALUE + */ +static Eterm remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) { ErtsDSigData dsd; ErtsMonitor *dmon; ErtsMonitor *mon; int code; - int res; + Eterm res = am_false; #ifndef ERTS_SMP int stale_mon = 0; #endif @@ -328,7 +325,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_LINK); - res = ERTS_DEMONITOR_TRUE; + res = am_true; break; case ERTS_DSIG_PREP_CONNECTED: @@ -352,7 +349,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) * This is possible when smp support is enabled. * 'DOWN' message just arrived. */ - res = ERTS_DEMONITOR_TRUE; + res = am_true; } else { /* @@ -367,16 +364,13 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) : mon->pid), ref, 0); - res = (code == ERTS_DSIG_SEND_YIELD - ? ERTS_DEMONITOR_YIELD_TRUE - : ERTS_DEMONITOR_TRUE); + res = (code == ERTS_DSIG_SEND_YIELD ? am_yield : am_true); erts_destroy_monitor(dmon); - } break; default: ASSERT(! "Invalid dsig prepare result"); - return ERTS_DEMONITOR_INTERNAL_ERROR; + return am_internal_error; } #ifndef ERTS_SMP @@ -404,27 +398,96 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) return res; } -static int demonitor(Process *c_p, Eterm ref, Eterm *multip) +static ERTS_INLINE void +demonitor_local_process(Process *c_p, Eterm ref, Eterm to, Eterm *res) +{ + Process *rp = erts_pid2proc_opt(c_p, + ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, + to, + ERTS_PROC_LOCK_LINK, + ERTS_P2P_FLG_ALLOW_OTHER_X); + ErtsMonitor *mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); + +#ifndef ERTS_SMP + ASSERT(mon); +#else + if (!mon) + *res = am_false; + else +#endif + { + *res = am_true; + erts_destroy_monitor(mon); + } + if (rp) { + ErtsMonitor *rmon; + rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); + if (rp != c_p) + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + if (rmon != NULL) + erts_destroy_monitor(rmon); + } + else { + ERTS_SMP_ASSERT_IS_NOT_EXITING(c_p); + } +} + +static ERTS_INLINE BIF_RETTYPE +demonitor_local_port(Process *origin, Eterm ref, Eterm target) { - ErtsMonitor *mon = NULL; /* The monitor entry to delete */ - Process *rp; /* Local target process */ - Eterm to = NIL; /* Monitor link traget */ - DistEntry *dep = NULL; /* Target's distribution entry */ - int deref_de = 0; - int res; - int unlock_link = 1; + BIF_RETTYPE res = am_false; + Port *port = erts_port_lookup_raw(target); + + if (!port) { + BIF_ERROR(origin, BADARG); + } + erts_smp_proc_unlock(origin, ERTS_PROC_LOCK_LINK); + + if (port) { + Eterm trap_ref; + switch (erts_port_demonitor(origin, ERTS_PORT_DEMONITOR_NORMAL, + port, ref, &trap_ref)) { + case ERTS_PORT_OP_DROPPED: + case ERTS_PORT_OP_BADARG: + break; + case ERTS_PORT_OP_SCHEDULED: + BIF_TRAP3(await_port_send_result_trap, origin, trap_ref, + am_busy_port, am_true); + /* the busy_port atom will never be returned, because it cannot be + * returned from erts_port_(de)monitor, but just in case if in future + * internal API changes - you may see this atom */ + default: + break; + } + } + else { + ERTS_SMP_ASSERT_IS_NOT_EXITING(origin); + } + BIF_RET(res); +} +/* Can return atom true, false, yield, internal_error, badarg or + * THE_NON_VALUE if error occured or trap has been set up + */ +static +BIF_RETTYPE demonitor(Process *c_p, Eterm ref, Eterm *multip) +{ + ErtsMonitor *mon = NULL; /* The monitor entry to delete */ + Eterm to = NIL; /* Monitor link traget */ + DistEntry *dep = NULL; /* Target's distribution entry */ + int deref_de = 0; + BIF_RETTYPE res = am_false; + int unlock_link = 1; erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_LINK); if (is_not_internal_ref(ref)) { - res = ERTS_DEMONITOR_BADARG; + res = am_badarg; goto done; /* Cannot be this monitor's ref */ } mon = erts_lookup_monitor(ERTS_P_MONITORS(c_p), ref); if (!mon) { - res = ERTS_DEMONITOR_FALSE; goto done; } @@ -432,70 +495,50 @@ static int demonitor(Process *c_p, Eterm ref, Eterm *multip) case MON_TIME_OFFSET: *multip = am_true; erts_demonitor_time_offset(ref); - res = ERTS_DEMONITOR_TRUE; + res = am_true; break; case MON_ORIGIN: to = mon->pid; *multip = am_false; if (is_atom(to)) { - /* Monitoring a name at node to */ - ASSERT(is_node_name_atom(to)); - dep = erts_sysname_to_connected_dist_entry(to); - ASSERT(dep != erts_this_dist_entry); - if (dep) - deref_de = 1; + /* Monitoring a name at node to */ + ASSERT(is_node_name_atom(to)); + dep = erts_sysname_to_connected_dist_entry(to); + ASSERT(dep != erts_this_dist_entry); + if (dep) + deref_de = 1; + } else if (is_port(to)) { + if (port_dist_entry(to) != erts_this_dist_entry) { + goto badarg; + } + res = demonitor_local_port(c_p, ref, to); + unlock_link = 0; + goto done; } else { - ASSERT(is_pid(to)); - dep = pid_dist_entry(to); + ASSERT(is_pid(to)); + dep = pid_dist_entry(to); } if (dep != erts_this_dist_entry) { - res = remote_demonitor(c_p, dep, ref, to); - /* remote_demonitor() unlocks link lock on c_p */ - unlock_link = 0; + res = remote_demonitor(c_p, dep, ref, to); + /* remote_demonitor() unlocks link lock on c_p */ + unlock_link = 0; } else { /* Local monitor */ - if (deref_de) { - deref_de = 0; - erts_deref_dist_entry(dep); - } - dep = NULL; - rp = erts_pid2proc_opt(c_p, - ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, - to, - ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); -#ifndef ERTS_SMP - ASSERT(mon); -#else - if (!mon) - res = ERTS_DEMONITOR_FALSE; - else -#endif - { - res = ERTS_DEMONITOR_TRUE; - erts_destroy_monitor(mon); - } - if (rp) { - ErtsMonitor *rmon; - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); - if (rp != c_p) - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (rmon != NULL) - erts_destroy_monitor(rmon); - } - else { - ERTS_SMP_ASSERT_IS_NOT_EXITING(c_p); - } - + if (deref_de) { + deref_de = 0; + erts_deref_dist_entry(dep); + } + dep = NULL; + demonitor_local_process(c_p, ref, to, &res); } break; - default: - res = ERTS_DEMONITOR_BADARG; + default /* case */ : +badarg: + res = am_badarg; /* will be converted to error by caller */ *multip = am_false; break; } - done: +done: if (unlock_link) erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_LINK); @@ -506,21 +549,20 @@ static int demonitor(Process *c_p, Eterm ref, Eterm *multip) } ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p)); - return res; + BIF_RET(res); } BIF_RETTYPE demonitor_1(BIF_ALIST_1) { Eterm multi; switch (demonitor(BIF_P, BIF_ARG_1, &multi)) { - case ERTS_DEMONITOR_FALSE: - case ERTS_DEMONITOR_TRUE: - BIF_RET(am_true); - case ERTS_DEMONITOR_YIELD_TRUE: - ERTS_BIF_YIELD_RETURN(BIF_P, am_true); - case ERTS_DEMONITOR_BADARG: - BIF_ERROR(BIF_P, BADARG); - case ERTS_DEMONITOR_INTERNAL_ERROR: + case am_false: + case am_true: BIF_RET(am_true); + case THE_NON_VALUE: BIF_RET(THE_NON_VALUE); + case am_yield: ERTS_BIF_YIELD_RETURN(BIF_P, am_true); + case am_badarg: BIF_ERROR(BIF_P, BADARG); + + case am_internal_error: default: ASSERT(! "demonitor(): internal error"); BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); @@ -529,11 +571,11 @@ BIF_RETTYPE demonitor_1(BIF_ALIST_1) BIF_RETTYPE demonitor_2(BIF_ALIST_2) { - Eterm res = am_true; - Eterm multi = am_false; - int info = 0; - int flush = 0; - Eterm list = BIF_ARG_2; + BIF_RETTYPE res = am_true; + Eterm multi = am_false; + int info = 0; + int flush = 0; + Eterm list = BIF_ARG_2; while (is_list(list)) { Eterm* consp = list_val(list); @@ -554,24 +596,27 @@ BIF_RETTYPE demonitor_2(BIF_ALIST_2) goto badarg; switch (demonitor(BIF_P, BIF_ARG_1, &multi)) { - case ERTS_DEMONITOR_FALSE: + case THE_NON_VALUE: + /* If other error occured or trap has been set up - pass through */ + BIF_RET(THE_NON_VALUE); + case am_false: if (info) res = am_false; if (flush) { - flush_messages: +flush_messages: BIF_TRAP3(flush_monitor_messages_trap, BIF_P, BIF_ARG_1, multi, res); } - case ERTS_DEMONITOR_TRUE: + case am_true: if (multi == am_true && flush) goto flush_messages; BIF_RET(res); - case ERTS_DEMONITOR_YIELD_TRUE: + case am_yield: ERTS_BIF_YIELD_RETURN(BIF_P, am_true); - case ERTS_DEMONITOR_BADARG: - badarg: + case am_badarg: +badarg: BIF_ERROR(BIF_P, BADARG); - case ERTS_DEMONITOR_INTERNAL_ERROR: + case am_internal_error: default: ASSERT(! "demonitor(): internal error"); BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); @@ -615,14 +660,13 @@ erts_queue_monitor_message(Process *p, erts_queue_message(p, *p_locksp, msgp, tup, am_system); } -static BIF_RETTYPE +static Eterm local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean) { - BIF_RETTYPE ret; - Process *rp; + Eterm ret = mon_ref; + Process *rp; ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK; - ERTS_BIF_PREP_RET(ret, mon_ref); if (target == p->common.id) { return ret; } @@ -658,40 +702,112 @@ local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean) } static BIF_RETTYPE -local_name_monitor(Process *p, Eterm target_name) +local_port_monitor(Process *origin, Eterm target) { - BIF_RETTYPE ret; - Eterm mon_ref; - ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK; - Process *rp; + BIF_RETTYPE ref = erts_make_ref(origin); + Port *port = erts_sig_lookup_port(origin, target); + ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN; - mon_ref = erts_make_ref(p); - ERTS_BIF_PREP_RET(ret, mon_ref); - erts_smp_proc_lock(p, ERTS_PROC_LOCK_LINK); - rp = erts_whereis_process(p, p_locks, target_name, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - if (!rp) { - DeclareTmpHeap(lhp,3,p); + if (!port) { +res_no_proc: + /* Send the DOWN message immediately. Ref is made on the fly because + * caller has never seen it yet. */ + erts_queue_monitor_message(origin, &p_locks, ref, + am_port, target, am_noproc); + } + else { + switch (erts_port_monitor(origin, port, target, &ref)) { + case ERTS_PORT_OP_DROPPED: + case ERTS_PORT_OP_BADARG: + goto res_no_proc; + case ERTS_PORT_OP_SCHEDULED: + BIF_TRAP3(await_port_send_result_trap, origin, ref, + am_busy_port, ref); + /* the busy_port atom will never be returned, because it cannot be + * returned from erts_port_monitor, but just in case if in future + * internal API changes - you may see this atom */ + default: + break; + } + } + erts_smp_proc_unlock(origin, p_locks & ~ERTS_PROC_LOCK_MAIN); + BIF_RET(ref); +} + +/* Type = process | port :: atom(), 1st argument passed to erlang:monitor/2 + */ +static BIF_RETTYPE +local_name_monitor(Process *self, Eterm type, Eterm target_name) +{ + BIF_RETTYPE ret = erts_make_ref(self); + + ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK; + Process *proc = NULL; + Port *port = NULL; + + erts_smp_proc_lock(self, ERTS_PROC_LOCK_LINK); + + erts_whereis_name(self, p_locks, target_name, + &proc, ERTS_PROC_LOCK_LINK, + ERTS_P2P_FLG_ALLOW_OTHER_X, + &port, 0); + + /* If the name is not registered, + * or if we asked for proc and got a port, + * or if we asked for port and got a proc, + * we just send the 'DOWN' message. + */ + if ((!proc && !port) || + (type == am_process && port) || + (type == am_port && proc)) { + DeclareTmpHeap(lhp,3,self); Eterm item; - UseTmpHeap(3,p); - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK); + UseTmpHeap(3,self); + + erts_smp_proc_unlock(self, ERTS_PROC_LOCK_LINK); p_locks &= ~ERTS_PROC_LOCK_LINK; + item = TUPLE2(lhp, target_name, erts_this_dist_entry->sysname); - erts_queue_monitor_message(p, &p_locks, - mon_ref, am_process, item, am_noproc); - UnUseTmpHeap(3,p); - } - else if (rp != p) { - erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, rp->common.id, - target_name); - erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, mon_ref, p->common.id, - target_name); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + erts_queue_monitor_message(self, &p_locks, + ret, + type, /* = process|port :: atom() */ + item, am_noproc); + UnUseTmpHeap(3,self); + } + else if (port) { + erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN); + p_locks &= ~ERTS_PROC_LOCK_MAIN; + + switch (erts_port_monitor(self, port, target_name, &ret)) { + case ERTS_PORT_OP_DONE: + return ret; + case ERTS_PORT_OP_SCHEDULED: { /* Scheduled a signal */ + ASSERT(is_internal_ref(ret)); + BIF_TRAP3(await_port_send_result_trap, self, + ret, am_true, ret); + /* bif_trap returns */ + } break; + default: + goto badarg; + } + } + else if (proc != self) { + erts_add_monitor(&ERTS_P_MONITORS(self), MON_ORIGIN, ret, + proc->common.id, target_name); + erts_add_monitor(&ERTS_P_MONITORS(proc), MON_TARGET, ret, + self->common.id, target_name); + erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_LINK); } - erts_smp_proc_unlock(p, p_locks & ~ERTS_PROC_LOCK_MAIN); - - return ret; + if (p_locks) { + erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN); + } + BIF_RET(ret); +badarg: + if (p_locks) { + erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN); + } + BIF_ERROR(self, BADARG); } static BIF_RETTYPE @@ -758,7 +874,7 @@ remote_monitor(Process *p, Eterm bifarg1, Eterm bifarg2, break; } - return ret; + BIF_RET(ret); } BIF_RETTYPE monitor_2(BIF_ALIST_2) @@ -772,8 +888,9 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2) switch (BIF_ARG_1) { case am_time_offset: { Eterm ref; - if (BIF_ARG_2 != am_clock_service) - goto error; + if (BIF_ARG_2 != am_clock_service) { + goto badarg; + } ref = erts_make_ref(BIF_P); erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); erts_add_monitor(&ERTS_P_MONITORS(BIF_P), MON_TIME_OFFSET, @@ -783,46 +900,57 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2) BIF_RET(ref); } case am_process: + case am_port: break; default: - goto error; + goto badarg; } - if (is_internal_pid(target)) { - local_pid: - ret = local_pid_monitor(BIF_P, target, erts_make_ref(BIF_P), 0); - } else if (is_external_pid(target)) { + if (is_internal_pid(target) && BIF_ARG_1 == am_process) { +local_pid: + ret = local_pid_monitor(BIF_P, target, erts_make_ref(BIF_P), 0); + } else if (is_external_pid(target) && BIF_ARG_1 == am_process) { dep = external_pid_dist_entry(target); if (dep == erts_this_dist_entry) goto local_pid; ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, target, 0); + } else if (is_internal_port(target) && BIF_ARG_1 == am_port) { +local_port: + ret = local_port_monitor(BIF_P, target); + } else if (is_external_port(target) && BIF_ARG_1 == am_port) { + dep = external_port_dist_entry(target); + if (dep == erts_this_dist_entry) { + goto local_port; + } + goto badarg; /* No want remote port */ } else if (is_atom(target)) { - ret = local_name_monitor(BIF_P, target); + ret = local_name_monitor(BIF_P, BIF_ARG_1, target); } else if (is_tuple(target)) { Eterm *tp = tuple_val(target); Eterm remote_node; Eterm name; - if (arityval(*tp) != 2) - goto error; + if (arityval(*tp) != 2) { + goto badarg; + } remote_node = tp[2]; name = tp[1]; if (!is_atom(remote_node) || !is_atom(name)) { - goto error; + goto badarg; } if (!erts_is_alive && remote_node != am_Noname) { - goto error; /* Remote monitor from (this) undistributed node */ + goto badarg; /* Remote monitor from (this) undistributed node */ } dep = erts_sysname_to_connected_dist_entry(remote_node); if (dep == erts_this_dist_entry) { deref_de = 1; - ret = local_name_monitor(BIF_P, name); + ret = local_name_monitor(BIF_P, BIF_ARG_1, name); } else { if (dep) deref_de = 1; ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, name, 1); } } else { - error: +badarg: ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG); } if (deref_de) { diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b410578d37..3fb866733c 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -361,8 +361,13 @@ erts_print_system_version(int to, void *arg, Process *c_p) } typedef struct { + /* {Entity,Node} = {monitor.Name,monitor.Pid} for external by name + * {Entity,Node} = {monitor.Pid,NIL} for external/external by pid + * {Entity,Node} = {monitor.Name,erlang:node()} for internal by name */ Eterm entity; Eterm node; + /* pid is actual target being monitored, no matter pid/port or name */ + Eterm pid; } MonitorInfo; typedef struct { @@ -420,21 +425,27 @@ static void collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp) EXTEND_MONITOR_INFOS(micp); if (is_atom(mon->pid)) { /* external by name */ micp->mi[micp->mi_i].entity = mon->name; - micp->mi[micp->mi_i].node = mon->pid; - micp->sz += 3; /* need one 2-tuple */ + micp->mi[micp->mi_i].node = mon->pid; + micp->sz += 3; /* need one 2-tuple */ } else if (is_external_pid(mon->pid)) { /* external by pid */ micp->mi[micp->mi_i].entity = mon->pid; - micp->mi[micp->mi_i].node = NIL; - micp->sz += NC_HEAP_SIZE(mon->pid); + micp->mi[micp->mi_i].node = NIL; + micp->sz += NC_HEAP_SIZE(mon->pid); } else if (!is_nil(mon->name)) { /* internal by name */ micp->mi[micp->mi_i].entity = mon->name; - micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname; - micp->sz += 3; /* need one 2-tuple */ + micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname; + micp->sz += 3; /* need one 2-tuple */ } else { /* internal by pid */ micp->mi[micp->mi_i].entity = mon->pid; - micp->mi[micp->mi_i].node = NIL; + micp->mi[micp->mi_i].node = NIL; /* no additional heap space needed */ } + + /* have always pid at hand, to assist with figuring out if its a port or + * a process, when we monitored by name and process_info is requested. + * See: erl_bif_info.c:process_info_aux section for am_monitors */ + micp->mi[micp->mi_i].pid = mon->pid; + micp->mi_i++; micp->sz += 2 + 3; /* For a cons cell and a 2-tuple */ } @@ -1190,37 +1201,49 @@ process_info_aux(Process *BIF_P, case am_monitors: { MonitorInfoCollection mic; - int i; + int i; INIT_MONITOR_INFOS(mic); - erts_doforall_monitors(ERTS_P_MONITORS(rp),&collect_one_origin_monitor,&mic); - hp = HAlloc(BIF_P, 3 + mic.sz); + erts_doforall_monitors(ERTS_P_MONITORS(rp), + &collect_one_origin_monitor, &mic); + hp = HAlloc(BIF_P, 3 + mic.sz); res = NIL; for (i = 0; i < mic.mi_i; i++) { if (is_atom(mic.mi[i].entity)) { /* Monitor by name. - * Build {process, {Name, Node}} and cons it. + * Build {process|port, {Name, Node}} and cons it. */ Eterm t1, t2; + /* If pid is an atom, then it is a remote named monitor, which + has to be a process */ + Eterm m_type = is_port(mic.mi[i].pid) ? am_port : am_process; + ASSERT(is_pid(mic.mi[i].pid) + || is_port(mic.mi[i].pid) + || is_atom(mic.mi[i].pid)); t1 = TUPLE2(hp, mic.mi[i].entity, mic.mi[i].node); hp += 3; - t2 = TUPLE2(hp, am_process, t1); + t2 = TUPLE2(hp, m_type, t1); hp += 3; res = CONS(hp, t2, res); - hp += 2; + hp += 2; } else { - /* Monitor by pid. Build {process, Pid} and cons it. */ + /* Monitor by pid. Build {process|port, Pid} and cons it. */ Eterm t; Eterm pid = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity); - t = TUPLE2(hp, am_process, pid); + + Eterm m_type = is_port(mic.mi[i].pid) ? am_port : am_process; + ASSERT(is_pid(mic.mi[i].pid) + || is_port(mic.mi[i].pid)); + + t = TUPLE2(hp, m_type, pid); hp += 3; res = CONS(hp, t, res); - hp += 2; + hp += 2; } } - DESTROY_MONITOR_INFOS(mic); + DESTROY_MONITOR_INFOS(mic); break; } @@ -2880,7 +2903,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) */ Eterm -erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm item) +erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, + Eterm item) { Eterm res = THE_NON_VALUE; @@ -2928,8 +2952,8 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite Eterm item; INIT_MONITOR_INFOS(mic); - - erts_doforall_monitors(ERTS_P_MONITORS(prt), &collect_one_origin_monitor, &mic); + erts_doforall_monitors(ERTS_P_MONITORS(prt), + &collect_one_origin_monitor, &mic); if (szp) *szp += mic.sz; @@ -2938,14 +2962,16 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite res = NIL; for (i = 0; i < mic.mi_i; i++) { Eterm t; - item = STORE_NC(hpp, ohp, mic.mi[i].entity); - t = TUPLE2(*hpp, am_process, item); + Eterm m_type; + + item = STORE_NC(hpp, ohp, mic.mi[i].entity); + m_type = is_port(item) ? am_port : am_process; + t = TUPLE2(*hpp, m_type, item); *hpp += 3; res = CONS(*hpp, t, res); *hpp += 2; } - } - + } // hpp DESTROY_MONITOR_INFOS(mic); if (szp) { @@ -2953,6 +2979,32 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite goto done; } } + else if (item == am_monitored_by) { + MonitorInfoCollection mic; + int i; + Eterm item; + + INIT_MONITOR_INFOS(mic); + erts_doforall_monitors(ERTS_P_MONITORS(prt), + &collect_one_target_monitor, &mic); + if (szp) + *szp += mic.sz; + + if (hpp) { + res = NIL; + for (i = 0; i < mic.mi_i; ++i) { + item = STORE_NC(hpp, ohp, mic.mi[i].entity); + res = CONS(*hpp, item, res); + *hpp += 2; + } + } // hpp + DESTROY_MONITOR_INFOS(mic); + + if (szp) { + res = am_true; + goto done; + } + } else if (item == am_name) { int count = sys_strlen(prt->name); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 37f4e1de49..208935bed9 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -139,6 +139,12 @@ sig_lookup_port(Process *c_p, Eterm id_or_name) return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); } +/* Non-inline copy of sig_lookup_port to be exported */ +Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name) +{ + return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); +} + static ERTS_INLINE Port * data_lookup_port(Process *c_p, Eterm id_or_name) { diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index f0075ca2b9..f90844ccc8 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -361,6 +361,8 @@ Eterm erts_request_io_bytes(Process *c_p); #define ERTS_PORT_REDS_CONNECT (CONTEXT_REDS/200) #define ERTS_PORT_REDS_UNLINK (CONTEXT_REDS/200) #define ERTS_PORT_REDS_LINK (CONTEXT_REDS/200) +#define ERTS_PORT_REDS_MONITOR (CONTEXT_REDS/200) +#define ERTS_PORT_REDS_DEMONITOR (CONTEXT_REDS/200) #define ERTS_PORT_REDS_BADSIG (CONTEXT_REDS/200) #define ERTS_PORT_REDS_CONTROL (CONTEXT_REDS/100) #define ERTS_PORT_REDS_CALL (CONTEXT_REDS/50) @@ -850,16 +852,20 @@ void erts_port_resume_procs(Port *); struct binary; -#define ERTS_P2P_SIG_TYPE_BAD 0 -#define ERTS_P2P_SIG_TYPE_OUTPUT 1 -#define ERTS_P2P_SIG_TYPE_OUTPUTV 2 -#define ERTS_P2P_SIG_TYPE_CONNECT 3 -#define ERTS_P2P_SIG_TYPE_EXIT 4 -#define ERTS_P2P_SIG_TYPE_CONTROL 5 -#define ERTS_P2P_SIG_TYPE_CALL 6 -#define ERTS_P2P_SIG_TYPE_INFO 7 -#define ERTS_P2P_SIG_TYPE_LINK 8 -#define ERTS_P2P_SIG_TYPE_UNLINK 9 +enum { + ERTS_P2P_SIG_TYPE_BAD = 0, + ERTS_P2P_SIG_TYPE_OUTPUT = 1, + ERTS_P2P_SIG_TYPE_OUTPUTV = 2, + ERTS_P2P_SIG_TYPE_CONNECT = 3, + ERTS_P2P_SIG_TYPE_EXIT = 4, + ERTS_P2P_SIG_TYPE_CONTROL = 5, + ERTS_P2P_SIG_TYPE_CALL = 6, + ERTS_P2P_SIG_TYPE_INFO = 7, + ERTS_P2P_SIG_TYPE_LINK = 8, + ERTS_P2P_SIG_TYPE_UNLINK = 9, + ERTS_P2P_SIG_TYPE_MONITOR = 10, + ERTS_P2P_SIG_TYPE_DEMONITOR = 11 +}; #define ERTS_P2P_SIG_TYPE_BITS 4 #define ERTS_P2P_SIG_TYPE_MASK \ @@ -921,6 +927,15 @@ struct ErtsProc2PortSigData_ { struct { Eterm from; } unlink; + struct { + Eterm origin; /* who receives monitor event, pid */ + Eterm name; /* either name for named monitor, or port id */ + } monitor; + struct { + Eterm origin; /* who is at the other end of the monitor, pid */ + Eterm name; /* port id */ + Uint32 ref[ERTS_MAX_REF_NUMBERS]; /* box contents of a ref */ + } demonitor; } u; } ; @@ -1017,6 +1032,29 @@ ErtsPortOpResult erts_port_control(Process *, Port *, unsigned int, Eterm, Eterm ErtsPortOpResult erts_port_call(Process *, Port *, unsigned int, Eterm, Eterm *); ErtsPortOpResult erts_port_info(Process *, Port *, Eterm, Eterm *); +/* Creates monitor between Origin and Target. Ref must be initialized to + * a reference (ref may be rewritten to be used to serve additionally as a + * signal id). Name is atom if user monitors port by name or NIL */ +ErtsPortOpResult erts_port_monitor(Process *origin, Port *target, Eterm name, + Eterm *ref); + +typedef enum { + /* Normal demonitor rules apply with locking and reductions bump */ + ERTS_PORT_DEMONITOR_NORMAL = 1, + /* Relaxed demonitor rules when process is about to die, which means that + * pid lookup won't work, locks won't work, no reductions bump. */ + ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED = 2, +} ErtsDemonitorMode; + +/* Removes monitor between origin and target, identified by ref. + * origin_is_dying can be 0 (false, normal locking rules and reductions bump + * apply) or 1 (true, in case when we avoid origin locking) */ +ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode, + Port *target, Eterm ref, + Eterm *trap_ref); +/* defined in erl_bif_port.c */ +Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name); + int erts_port_output_async(Port *, Eterm, Eterm); /* diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index c0b1d7246c..2c3d1bb7eb 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -12239,7 +12239,6 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) ExitMonitorContext *pcontext = vpcontext; DistEntry *dep; ErtsMonitor *rmon; - Process *rp; switch (mon->type) { case MON_ORIGIN: @@ -12268,9 +12267,10 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) erts_deref_dist_entry(dep); } } else { - ASSERT(is_pid(mon->pid)); - if (is_internal_pid(mon->pid)) { /* local by pid or name */ - rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); + ASSERT(is_pid(mon->pid) || is_port(mon->pid)); + /* if is local by pid or name */ + if (is_internal_pid(mon->pid)) { + Process *rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); if (!rp) { goto done; } @@ -12280,7 +12280,17 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) goto done; } erts_destroy_monitor(rmon); - } else { /* remote by pid */ + } else if (is_internal_port(mon->pid)) { + /* Is a local port */ + Port *prt = erts_port_lookup_raw(mon->pid); + if (!prt) { + goto done; + } + erts_port_demonitor(pcontext->p, + ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED, + prt, mon->ref, NULL); + return; /* let erts_port_demonitor do the deletion */ + } else { /* remote by pid */ ASSERT(is_external_pid(mon->pid)); dep = external_pid_dist_entry(mon->pid); ASSERT(dep != NULL); @@ -12318,6 +12328,7 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) erts_port_release(prt); } else if (is_internal_pid(mon->pid)) {/* local by name or pid */ Eterm watched; + Process *rp; DeclareTmpHeapNoproc(lhp,3); ErtsProcLocks rp_locks = (ERTS_PROC_LOCK_LINK | ERTS_PROC_LOCKS_MSG_SEND); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 01df5476db..cb8792dffa 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1260,7 +1260,7 @@ typedef struct { /* * Try doing an immediate driver callback call from a process. If * this fail, the operation should be scheduled in the normal case... - * + * Returns: ok to do the call, or error (lock busy, does not exist, etc) */ static ERTS_INLINE ErtsTryImmDrvCallResult try_imm_drv_call(ErtsTryImmDrvCallState *sp) @@ -3073,6 +3073,250 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp) port_sig_link); } +static void +port_monitor_failure(Eterm port_id, Eterm origin, Eterm ref_DOWN) +{ + Process *origin_p; + ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK; + ASSERT(is_internal_pid(origin)); + + origin_p = erts_pid2proc(NULL, 0, origin, p_locks); + if (! origin_p) { return; } + + /* Send the DOWN message immediately. Ref is made on the fly because + * caller has never seen it yet. */ + erts_queue_monitor_message(origin_p, &p_locks, ref_DOWN, + am_port, port_id, am_noproc); + erts_smp_proc_unlock(origin_p, p_locks); +} + +/* Origin wants to monitor port Prt. State contains possible error, which has + * happened just before. Name is either NIL or an atom, if user monitors + * a port by name. Ref is premade reference that will be returned to user */ +static void +port_monitor(Port *prt, erts_aint32_t state, Eterm origin, + Eterm name, Eterm ref) +{ + Eterm name_or_nil = is_atom(name) ? name : NIL; + + ASSERT(is_pid(origin)); + ASSERT(is_atom(name) || is_port(name) || name == NIL); + ASSERT(is_internal_ref(ref)); + + if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) { + ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK; + + Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks); + if (! origin_p) { + goto failure; + } + erts_add_monitor(&ERTS_P_MONITORS(origin_p), MON_ORIGIN, ref, + prt->common.id, name_or_nil); + erts_add_monitor(&ERTS_P_MONITORS(prt), MON_TARGET, ref, + origin, name_or_nil); + + erts_smp_proc_unlock(origin_p, p_locks); + } else { +failure: + port_monitor_failure(prt->common.id, origin, ref); + } +} + +static int +port_sig_monitor(Port *prt, erts_aint32_t state, int op, + ErtsProc2PortSigData *sigdp) +{ + Eterm hp[REF_THING_SIZE]; + Eterm ref = make_internal_ref(&hp); + write_ref_thing(hp, sigdp->ref[0], sigdp->ref[1], sigdp->ref[2]); + + if (op == ERTS_PROC2PORT_SIG_EXEC) { + /* erts_add_monitor call inside port_monitor will copy ref from hp */ + port_monitor(prt, state, + sigdp->u.monitor.origin, + sigdp->u.monitor.name, + ref); + } else { + port_monitor_failure(sigdp->u.monitor.name, + sigdp->u.monitor.origin, + ref); + } + if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) { + port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt); + } + return ERTS_PORT_REDS_MONITOR; +} + +/* Creates monitor between Origin and Target. Ref must be initialized to + * a reference (ref may be rewritten to be used to serve additionally as a + * signal id). Name is atom if user monitors port by name or NIL */ +ErtsPortOpResult +erts_port_monitor(Process *origin, Port *port, Eterm name, Eterm *refp) +{ + ErtsProc2PortSigData *sigdp; + ErtsTryImmDrvCallState try_call_state + = ERTS_INIT_TRY_IMM_DRV_CALL_STATE( + origin, port, ERTS_PORT_SFLGS_INVALID_LOOKUP, + 0, + 0, /* trap_ref is always set so !trap_ref always is false */ + am_monitor); + + ASSERT(origin); + ASSERT(port); + ASSERT(is_atom(name) || is_port(name)); + ASSERT(refp); + + switch (try_imm_drv_call(&try_call_state)) { + case ERTS_TRY_IMM_DRV_CALL_OK: + port_monitor(port, try_call_state.state, origin->common.id, name, *refp); + finalize_imm_drv_call(&try_call_state); + BUMP_REDS(origin, ERTS_PORT_REDS_MONITOR); + return ERTS_PORT_OP_DONE; + case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: + return ERTS_PORT_OP_BADARG; + default: + break; /* Schedule call instead... */ + } + + sigdp = erts_port_task_alloc_p2p_sig_data(); + sigdp->flags = ERTS_P2P_SIG_TYPE_MONITOR; + sigdp->u.monitor.origin = origin->common.id; + sigdp->u.monitor.name = name; /* either named monitor, or port id */ + + /* Ref contents will be initialized here */ + return erts_schedule_proc2port_signal(origin, port, origin->common.id, + refp, sigdp, 0, NULL, + port_sig_monitor); +} + +static void +port_demonitor_failure(Eterm port_id, Eterm origin, Eterm ref) +{ + Process *origin_p; + ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK; + ErtsMonitor *mon1; + ASSERT(is_internal_pid(origin)); + + origin_p = erts_pid2proc(NULL, 0, origin, rp_locks); + if (! origin_p) { return; } + + /* do not send any DOWN messages, drop monitors on process */ + mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p), ref); + if (mon1 != NULL) { + erts_destroy_monitor(mon1); + } + + erts_smp_proc_unlock(origin_p, rp_locks); +} + +/* Origin wants to demonitor port Prt. State contains possible error, which has + * happened just before. Ref is reference to monitor */ +static void +port_demonitor(Port *port, erts_aint32_t state, Eterm origin, Eterm ref) +{ + ASSERT(port); + ASSERT(is_pid(origin)); + ASSERT(is_internal_ref(ref)); + + if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) { + ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK; + Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks); + if (origin_p) { + ErtsMonitor *mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p), + ref); + if (mon1 != NULL) { + erts_destroy_monitor(mon1); + } + } + if (1) { + ErtsMonitor *mon2 = erts_remove_monitor(&ERTS_P_MONITORS(port), + ref); + if (mon2 != NULL) { + erts_destroy_monitor(mon2); + } + } + if (origin_p) { /* when origin is dying, it won't be found */ + erts_smp_proc_unlock(origin_p, p_locks); + } + } else { + port_demonitor_failure(port->common.id, origin, ref); + } +} + +static int +port_sig_demonitor(Port *prt, erts_aint32_t state, int op, + ErtsProc2PortSigData *sigdp) +{ + Eterm hp[REF_THING_SIZE]; + Eterm ref = make_internal_ref(&hp); + write_ref_thing(hp, sigdp->u.demonitor.ref[0], + sigdp->u.demonitor.ref[1], + sigdp->u.demonitor.ref[2]); + if (op == ERTS_PROC2PORT_SIG_EXEC) { + port_demonitor(prt, state, sigdp->u.demonitor.origin, ref); + } else { + port_demonitor_failure(sigdp->u.demonitor.name, + sigdp->u.demonitor.origin, + ref); + } + if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) { + port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt); + } + return ERTS_PORT_REDS_DEMONITOR; +} + +/* Removes monitor between origin and target, identified by ref. + * Mode defines normal or relaxed demonitor rules (process is at death) */ +ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode, + Port *target, Eterm ref, + Eterm *trap_ref) +{ + Process *c_p = mode == ERTS_PORT_DEMONITOR_NORMAL ? origin : NULL; + ErtsProc2PortSigData *sigdp; + ErtsTryImmDrvCallState try_call_state + = ERTS_INIT_TRY_IMM_DRV_CALL_STATE( + c_p, + target, ERTS_PORT_SFLGS_INVALID_LOOKUP, + 0, + !trap_ref, + am_demonitor); + + ASSERT(origin); + ASSERT(target); + ASSERT(is_internal_ref(ref)); + + switch (try_imm_drv_call(&try_call_state)) { + case ERTS_TRY_IMM_DRV_CALL_OK: + port_demonitor(target, try_call_state.state, origin->common.id, ref); + finalize_imm_drv_call(&try_call_state); + if (mode == ERTS_PORT_DEMONITOR_NORMAL) { + BUMP_REDS(origin, ERTS_PORT_REDS_DEMONITOR); + } + return ERTS_PORT_OP_DONE; + case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: + return ERTS_PORT_OP_BADARG; + default: + break; /* Schedule call instead... */ + } + + sigdp = erts_port_task_alloc_p2p_sig_data(); + sigdp->flags = ERTS_P2P_SIG_TYPE_DEMONITOR; + sigdp->u.demonitor.origin = origin->common.id; + sigdp->u.demonitor.name = target->common.id; + { + RefThing *reft = ref_thing_ptr(ref); + /* Start from 1 skip ref arity */ + sys_memcpy(sigdp->u.demonitor.ref, + internal_thing_ref_numbers(reft), + sizeof(sigdp->u.demonitor.ref)); + } + + /* Ref contents will be initialized here */ + return erts_schedule_proc2port_signal(c_p, target, origin->common.id, + trap_ref, sigdp, 0, NULL, + port_sig_demonitor); +} + static void init_ack_send_reply(Port *port, Eterm resp) { @@ -3942,23 +4186,30 @@ erts_terminate_port(Port *pp) terminate_port(pp); } +static void port_fire_one_monitor(ErtsMonitor *mon, void *ctx0); static void sweep_one_monitor(ErtsMonitor *mon, void *vpsc) { - ErtsMonitor *rmon; - Process *rp; + switch (mon->type) { + case MON_ORIGIN: { + ErtsMonitor *rmon; + Process *rp; - ASSERT(mon->type == MON_ORIGIN); - ASSERT(is_internal_pid(mon->pid)); - rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); - if (!rp) { - goto done; - } - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (rmon == NULL) { - goto done; + ASSERT(is_internal_pid(mon->pid)); + rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); + if (!rp) { + goto done; + } + rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + if (rmon == NULL) { + goto done; + } + erts_destroy_monitor(rmon); + } break; + case MON_TARGET: { + port_fire_one_monitor(mon, vpsc); /* forward call */ + } break; } - erts_destroy_monitor(rmon); done: erts_destroy_monitor(mon); } @@ -4039,6 +4290,43 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc) erts_destroy_link(lnk); } +static void +port_fire_one_monitor(ErtsMonitor *mon, void *ctx0) +{ + Process *origin; + ErtsProcLocks origin_locks; + + if (mon->type != MON_TARGET || ! is_pid(mon->pid)) { + return; + } + /* + * Proceed here if someone monitors us, we (port) are the target and + * origin is some process + */ + origin_locks = ERTS_PROC_LOCKS_MSG_SEND | ERTS_PROC_LOCK_LINK; + + origin = erts_pid2proc(NULL, 0, mon->pid, origin_locks); + if (origin) { + DeclareTmpHeapNoproc(lhp,3); + SweepContext *ctx = (SweepContext *)ctx0; + ErtsMonitor *rmon; + Eterm watched = (is_atom(mon->name) + ? TUPLE2(lhp, mon->name, erts_this_dist_entry->sysname) + : ctx->port->common.id); + + erts_queue_monitor_message(origin, &origin_locks, mon->ref, am_port, + watched, ctx->reason); + UnUseTmpHeapNoproc(3); + + rmon = erts_remove_monitor(&ERTS_P_MONITORS(origin), mon->ref); + erts_smp_proc_unlock(origin, origin_locks); + + if (rmon) { + erts_destroy_monitor(rmon); + } + } +} + /* 'from' is sending 'this_port' an exit signal, (this_port must be internal). * If reason is normal we don't do anything, *unless* from is our connected * process in which case we close the port. Any other reason kills the port. @@ -4050,39 +4338,40 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc) */ int -erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed, +erts_deliver_port_exit(Port *prt, Eterm from, Eterm reason, int send_closed, int drop_normal) { ErtsLink *lnk; - Eterm rreason; + Eterm modified_reason; erts_aint32_t state, set_state_flags; ERTS_SMP_CHK_NO_PROC_LOCKS; - ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); - rreason = (reason == am_kill) ? am_killed : reason; + modified_reason = (reason == am_kill) ? am_killed : reason; #ifdef USE_VM_PROBES if (DTRACE_ENABLED(port_exit)) { DTRACE_CHARBUF(from_str, DTRACE_TERM_BUF_SIZE); DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); - DTRACE_CHARBUF(rreason_str, 64); + DTRACE_CHARBUF(reason_str, 64); erts_snprintf(from_str, sizeof(DTRACE_CHARBUF_NAME(from_str)), "%T", from); - dtrace_port_str(p, port_str); - erts_snprintf(rreason_str, sizeof(DTRACE_CHARBUF_NAME(rreason_str)), "%T", rreason); - DTRACE4(port_exit, from_str, port_str, p->name, rreason_str); + dtrace_port_str(prt, port_str); + erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)), "%T", + modified_reason); + DTRACE4(port_exit, from_str, port_str, prt->name, reason_str); } #endif - state = erts_atomic32_read_nob(&p->state); + state = erts_atomic32_read_nob(&prt->state); if (state & (ERTS_PORT_SFLGS_DEAD | ERTS_PORT_SFLG_EXITING | ERTS_PORT_SFLG_CLOSING)) return 0; - if (reason == am_normal && from != ERTS_PORT_GET_CONNECTED(p) - && from != p->common.id && drop_normal) { + if (reason == am_normal && from != ERTS_PORT_GET_CONNECTED(prt) + && from != prt->common.id && drop_normal) { return 0; } @@ -4090,53 +4379,54 @@ erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed, if (send_closed) set_state_flags |= ERTS_PORT_SFLG_SEND_CLOSED; - erts_port_task_sched_enter_exiting_state(&p->sched); + erts_port_task_sched_enter_exiting_state(&prt->sched); - state = erts_atomic32_read_bor_mb(&p->state, set_state_flags); + state = erts_atomic32_read_bor_mb(&prt->state, set_state_flags); state |= set_state_flags; - if (IS_TRACED_FL(p, F_TRACE_PORTS)) - trace_port(p, am_closed, reason); + if (IS_TRACED_FL(prt, F_TRACE_PORTS)) + trace_port(prt, am_closed, reason); - erts_trace_check_exiting(p->common.id); + erts_trace_check_exiting(prt->common.id); - set_busy_port(ERTS_Port2ErlDrvPort(p), 0); + set_busy_port(ERTS_Port2ErlDrvPort(prt), 0); - if (p->common.u.alive.reg != NULL) - (void) erts_unregister_name(NULL, 0, p, p->common.u.alive.reg->name); + if (prt->common.u.alive.reg != NULL) + (void) erts_unregister_name(NULL, 0, prt, prt->common.u.alive.reg->name); { - SweepContext sc = {p, rreason}; - lnk = ERTS_P_LINKS(p); - ERTS_P_LINKS(p) = NULL; + SweepContext sc = {prt, modified_reason}; + lnk = ERTS_P_LINKS(prt); + ERTS_P_LINKS(prt) = NULL; erts_sweep_links(lnk, &sweep_one_link, &sc); } - DRV_MONITOR_LOCK_PDL(p); + DRV_MONITOR_LOCK_PDL(prt); { - ErtsMonitor *moni = ERTS_P_MONITORS(p); - ERTS_P_MONITORS(p) = NULL; - erts_sweep_monitors(moni, &sweep_one_monitor, NULL); + SweepContext ctx = {prt, modified_reason}; + ErtsMonitor *moni = ERTS_P_MONITORS(prt); + ERTS_P_MONITORS(prt) = NULL; + erts_sweep_monitors(moni, &sweep_one_monitor, &ctx); } - DRV_MONITOR_UNLOCK_PDL(p); + DRV_MONITOR_UNLOCK_PDL(prt); - if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && p->dist_entry) { - erts_do_net_exits(p->dist_entry, rreason); - erts_deref_dist_entry(p->dist_entry); - p->dist_entry = NULL; - erts_atomic32_read_band_relb(&p->state, + if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && prt->dist_entry) { + erts_do_net_exits(prt->dist_entry, modified_reason); + erts_deref_dist_entry(prt->dist_entry); + prt->dist_entry = NULL; + erts_atomic32_read_band_relb(&prt->state, ~ERTS_PORT_SFLG_DISTRIBUTION); } - if ((reason != am_kill) && !is_port_ioq_empty(p)) { + if ((reason != am_kill) && !is_port_ioq_empty(prt)) { /* must turn exiting flag off */ - erts_atomic32_read_bset_relb(&p->state, + erts_atomic32_read_bset_relb(&prt->state, (ERTS_PORT_SFLG_EXITING | ERTS_PORT_SFLG_CLOSING), ERTS_PORT_SFLG_CLOSING); - flush_port(p); + flush_port(prt); } else { - terminate_port(p); + terminate_port(prt); } return 1; diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c index 77f79fcea4..ac7096745e 100644 --- a/erts/emulator/beam/register.c +++ b/erts/emulator/beam/register.c @@ -323,7 +323,8 @@ erts_whereis_name(Process *c_p, Process** proc, ErtsProcLocks need_locks, int flags, - Port** port) + Port** port, + int lock_port) { RegProc* rp = NULL; HashValue hval; @@ -406,31 +407,33 @@ erts_whereis_name(Process *c_p, *port = NULL; else { #ifdef ERTS_SMP - if (pending_port == rp->pt) - pending_port = NULL; - else { - if (pending_port) { - /* Ahh! Registered port changed while reg lock - was unlocked... */ - erts_port_release(pending_port); - pending_port = NULL; - } + if (lock_port) { + if (pending_port == rp->pt) + pending_port = NULL; + else { + if (pending_port) { + /* Ahh! Registered port changed while reg lock + was unlocked... */ + erts_port_release(pending_port); + pending_port = NULL; + } - if (erts_smp_port_trylock(rp->pt) == EBUSY) { - Eterm id = rp->pt->common.id; /* id read only... */ - /* Unlock all locks, acquire port lock, and restart... */ - if (current_c_p_locks) { - erts_smp_proc_unlock(c_p, current_c_p_locks); - current_c_p_locks = 0; - } - reg_read_unlock(); - pending_port = erts_id2port(id); - goto restart; - } - } + if (erts_smp_port_trylock(rp->pt) == EBUSY) { + Eterm id = rp->pt->common.id; /* id read only... */ + /* Unlock all locks, acquire port lock, and restart... */ + if (current_c_p_locks) { + erts_smp_proc_unlock(c_p, current_c_p_locks); + current_c_p_locks = 0; + } + reg_read_unlock(); + pending_port = erts_id2port(id); + goto restart; + } + } + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(rp->pt)); + } #endif *port = rp->pt; - ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(*port)); } } @@ -452,7 +455,7 @@ erts_whereis_process(Process *c_p, int flags) { Process *proc; - erts_whereis_name(c_p, c_p_locks, name, &proc, need_locks, flags, NULL); + erts_whereis_name(c_p, c_p_locks, name, &proc, need_locks, flags, NULL, 0); return proc; } diff --git a/erts/emulator/beam/register.h b/erts/emulator/beam/register.h index 88ab7b7bf1..d839f55d6b 100644 --- a/erts/emulator/beam/register.h +++ b/erts/emulator/beam/register.h @@ -49,7 +49,7 @@ int erts_register_name(Process *, Eterm, Eterm); Eterm erts_whereis_name_to_id(Process *, Eterm); void erts_whereis_name(Process *, ErtsProcLocks, Eterm, Process**, ErtsProcLocks, int, - Port**); + Port**, int); Process *erts_whereis_process(Process *, ErtsProcLocks, Eterm, diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 8955e62df5..90d2bd8c5d 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -21,6 +21,7 @@ -module(monitor_SUITE). -include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). -export([all/0, suite/0, groups/0, case_1/1, case_1a/1, case_2/1, case_2a/1, mon_e_1/1, demon_e_1/1, demon_1/1, @@ -706,7 +707,7 @@ named_down(Config) when is_list(Config) -> spawn_opt(fun () -> WFun = fun (F, hej) -> F(F, hopp); -(F, hopp) -> F(F, hej) + (F, hopp) -> F(F, hej) end, NoSchedulers = erlang:system_info(schedulers_online), lists:foreach(fun (_) -> @@ -726,13 +727,14 @@ named_down(Config) when is_list(Config) -> NamedProc = spawn_link(fun () -> receive after infinity -> ok end end), - true = register(Name, NamedProc), + ?assertEqual(true, register(Name, NamedProc)), unlink(NamedProc), exit(NamedProc, bang), Mon = erlang:monitor(process, Name), - receive {'DOWN',Mon, _, _, _} -> ok end, - true = register(Name, self()), - true = unregister(Name), + receive {'DOWN',Mon, _, _, bang} -> ok + after 3000 -> ?assert(false) end, + ?assertEqual(true, register(Name, self())), + ?assertEqual(true, unregister(Name)), process_flag(priority,Prio), ok. diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 79abcbde5f..ee07699884 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -74,27 +74,68 @@ %% --export([all/0, suite/0, groups/0, - init_per_testcase/2, end_per_testcase/2, - init_per_suite/1, end_per_suite/1, - stream_small/1, stream_big/1, - basic_ping/1, slow_writes/1, bad_packet/1, bad_port_messages/1, - mul_basic/1, mul_slow_writes/1, - dying_port/1, port_program_with_path/1, - open_input_file_port/1, open_output_file_port/1, - count_fds/1, - iter_max_ports/1, eof/1, input_only/1, output_only/1, - name1/1, - t_binary/1, parallell/1, t_exit/1, - env/1, huge_env/1, bad_env/1, cd/1, exit_status/1, - bad_args/1, - tps_16_bytes/1, tps_1K/1, line/1, stderr_to_stdout/1, - otp_3906/1, otp_4389/1, win_massive/1, win_massive_client/1, - mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1, - exit_status_multi_scheduling_block/1, ports/1, - spawn_driver/1, spawn_executable/1, close_deaf_port/1, - port_setget_data/1, - unregister_name/1, parallelism_option/1]). +-export([all/0, suite/0, groups/0, init_per_testcase/2, end_per_testcase/2, + init_per_suite/1, end_per_suite/1]). +-export([ + bad_args/1, + bad_env/1, + bad_packet/1, + bad_port_messages/1, + basic_ping/1, + cd/1, + close_deaf_port/1, + count_fds/1, + dying_port/1, + env/1, + eof/1, + exit_status/1, + exit_status_multi_scheduling_block/1, + huge_env/1, + input_only/1, + iter_max_ports/1, + line/1, + mix_up_ports/1, + mon_port_invalid_type/1, + mon_port_bad_named/1, + mon_port_bad_remote_on_local/1, + mon_port_local/1, + mon_port_name_demonitor/1, + mon_port_named/1, + mon_port_origin_dies/1, + mon_port_pid_demonitor/1, + mon_port_remote_on_remote/1, + mon_port_driver_die/1, + mon_port_driver_die_demonitor/1, + mul_basic/1, + mul_slow_writes/1, + name1/1, + open_input_file_port/1, + open_output_file_port/1, + otp_3906/1, + otp_4389/1, + otp_5112/1, + otp_5119/1, + otp_6224/1, + output_only/1, + parallelism_option/1, + parallell/1, + port_program_with_path/1, + port_setget_data/1, + ports/1, + slow_writes/1, + spawn_driver/1, + spawn_executable/1, + stderr_to_stdout/1, + stream_big/1, + stream_small/1, + t_binary/1, + t_exit/1, + tps_16_bytes/1, + tps_1K/1, + unregister_name/1, + win_massive/1, + win_massive_client/1 +]). -export([do_iter_max_ports/2]). @@ -105,12 +146,13 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). +-include_lib("eunit/include/eunit.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {seconds, 10}}]. -all() -> +all() -> [otp_6224, {group, stream}, basic_ping, slow_writes, bad_packet, bad_port_messages, {group, options}, {group, multiple_packets}, parallell, dying_port, @@ -123,14 +165,32 @@ all() -> exit_status_multi_scheduling_block, ports, spawn_driver, spawn_executable, close_deaf_port, unregister_name, port_setget_data, - parallelism_option]. - -groups() -> + parallelism_option, + mon_port_invalid_type, + mon_port_local, + mon_port_remote_on_remote, + mon_port_bad_remote_on_local, + mon_port_origin_dies, + mon_port_named, + mon_port_bad_named, + mon_port_pid_demonitor, + mon_port_name_demonitor, + mon_port_driver_die, + mon_port_driver_die_demonitor + ]. + +groups() -> [{stream, [], [stream_small, stream_big]}, {options, [], [t_binary, eof, input_only, output_only]}, {multiple_packets, [], [mul_basic, mul_slow_writes]}, {tps, [], [tps_16_bytes, tps_1K]}]. +init_per_testcase(Case, Config) when Case =:= mon_port_driver_die; + Case =:= mon_port_driver_die_demonitor -> + case erlang:system_info(schedulers_online) of + 1 -> {skip, "Need 2 schedulers to run testcase"}; + _ -> Config + end; init_per_testcase(Case, Config) -> [{testcase, Case} |Config]. @@ -160,7 +220,7 @@ do_win_massive() -> ct:timetrap({minutes, 6}), SuiteDir = filename:dirname(code:which(?MODULE)), Ports = " +Q 8192", - {ok, Node} = + {ok, Node} = test_server:start_node(win_massive, slave, [{args, " -pa " ++ SuiteDir ++ Ports}]), @@ -169,7 +229,7 @@ do_win_massive() -> ok. win_massive_client(N) -> - {ok,P}=gen_tcp:listen(?WIN_MASSIVE_PORT,[{reuseaddr,true}]), + {ok,P}=gen_tcp:listen(?WIN_MASSIVE_PORT,[{reuseaddr,true}]), L = win_massive_loop(P,N), Len = length(L), lists:foreach(fun(E) -> @@ -278,7 +338,7 @@ bad_port_messages(Config) when is_list(Config) -> bad_message(PortTest, {self(),{connect,no_pid}}), ok. -bad_message(PortTest, Message) -> +bad_message(PortTest, Message) -> P = open_port({spawn,PortTest}, []), P ! Message, receive @@ -773,7 +833,7 @@ line(Config) when is_list(Config) -> S1 = lists:flatten(io_lib:format("-l~w", [length(L1)])), io:format("S1 = ~w, L1 = ~w~n", [S1,L1]), port_expect(Config,[{L1, - [{eol, Packet1}, {noeol, Packet2}, eof]}], 0, + [{eol, Packet1}, {noeol, Packet2}, eof]}], 0, S1, [{line,Siz},eof]), %% Test that lonely Don't get treated as newlines port_expect(Config,[{lists:append([Packet1, [13], Packet2, @@ -844,9 +904,9 @@ env(Config) when is_list(Config) -> {"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} + 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"} + ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"} || X <- lists:seq(1,150)], env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)), ok. @@ -1320,22 +1380,22 @@ spawn_driver(Config) when is_list(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 -> + receive + {Port, {data, "Hello port!"}} = Msg1 -> io:format("~p~n", [Msg1]), - ok; + ok; Other -> ct:fail({unexpected, Other}) end, Port ! {self(), close}, receive {Port, closed} -> ok end, - Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, + Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, []), - receive - {Port2, {data, "Hello port?"}} = Msg2 -> + receive + {Port2, {data, "Hello port?"}} = Msg2 -> io:format("~p~n", [Msg2]), - ok; + ok; Other2 -> ct:fail({unexpected2, Other2}) end, @@ -1354,23 +1414,23 @@ parallelism_option(Config) when is_list(Config) -> [{parallelism, true}]), {parallelism, true} = erlang:port_info(Port, parallelism), Port ! {self(), {command, "Hello port!"}}, - receive - {Port, {data, "Hello port!"}} = Msg1 -> + receive + {Port, {data, "Hello port!"}} = Msg1 -> io:format("~p~n", [Msg1]), - ok; + ok; Other -> ct:fail({unexpected, Other}) end, Port ! {self(), close}, receive {Port, closed} -> ok end, - Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, + Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, [{parallelism, false}]), {parallelism, false} = erlang:port_info(Port2, parallelism), - receive - {Port2, {data, "Hello port?"}} = Msg2 -> + receive + {Port2, {data, "Hello port?"}} = Msg2 -> io:format("~p~n", [Msg2]), - ok; + ok; Other2 -> ct:fail({unexpected2, Other2}) end, @@ -1389,20 +1449,20 @@ spawn_executable(Config) when is_list(Config) -> ["echo_args"] = run_echo_args(DataDir,[binary, "echo_args"]), ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]), ["echo_arguments"] = run_echo_args(DataDir,[binary, "echo_arguments"]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]), [ExactFile1] = run_echo_args(DataDir,[default]), [ExactFile1] = run_echo_args(DataDir,[binary, default]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[switch_order,ExactFile1,"hello world", "dlrow olleh"]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[binary,switch_order,ExactFile1,"hello world", "dlrow olleh"]), [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[default,"hello world","dlrow olleh"]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile1,"hello world","dlrow olleh"] = run_echo_args_2(unicode:characters_to_binary("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\"")), @@ -1418,7 +1478,7 @@ spawn_executable(Config) when is_list(Config) -> [ExactFile2] = run_echo_args(SpaceDir,[]), ["echo_args"] = run_echo_args(SpaceDir,["echo_args"]), ["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]), [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[binary, ExactFile2,"hello world","dlrow olleh"]), @@ -1429,16 +1489,16 @@ spawn_executable(Config) when is_list(Config) -> run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]), [ExactFile2] = run_echo_args(SpaceDir,[default]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world", "dlrow olleh"]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile2,"hello world","dlrow olleh"] = run_echo_args_2(unicode:characters_to_binary("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\"")), - ExeExt = + ExeExt = case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of "exe" -> ".exe"; @@ -1452,17 +1512,17 @@ spawn_executable(Config) when is_list(Config) -> [ExactFile3] = run_echo_args(SpaceDir,Executable2,[]), ["echo_args"] = run_echo_args(SpaceDir,Executable2,["echo_args"]), ["echo_arguments"] = run_echo_args(SpaceDir,Executable2,["echo_arguments"]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2,[ExactFile3,"hello world","dlrow olleh"]), [ExactFile3] = run_echo_args(SpaceDir,Executable2,[default]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2, [switch_order,ExactFile3,"hello world", "dlrow olleh"]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2, [default,"hello world","dlrow olleh"]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile3,"hello world","dlrow olleh"] = run_echo_args_2(unicode:characters_to_binary("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\"")), @@ -1510,11 +1570,11 @@ test_bat_file(Dir) -> <<"\r\n">>], file:write_file(Full,list_to_binary(D)), EF = filename:basename(FN), - [DN,"hello","world"] = + [DN,"hello","world"] = run_echo_args(Dir,FN, [default,"hello","world"]), %% The arg0 argumant should be ignored when running batch files - [DN,"hello","world"] = + [DN,"hello","world"] = run_echo_args(Dir,FN, ["knaskurt","hello","world"]), EF = filename:basename(DN), @@ -1533,10 +1593,10 @@ test_sh_file(Dir) -> <<"done\n">>], file:write_file(Full,list_to_binary(D)), chmodplusx(Full), - [Full,"hello","world"] = + [Full,"hello","world"] = run_echo_args(Dir,FN, [default,"hello","world"]), - [Full,"hello","world of spaces"] = + [Full,"hello","world of spaces"] = run_echo_args(Dir,FN, [default,"hello","world of spaces"]), file:write_file(filename:join([Dir,"testfile1"]),<<"testdata1">>), @@ -1544,7 +1604,7 @@ test_sh_file(Dir) -> Pattern = filename:join([Dir,"testfile*"]), L = filelib:wildcard(Pattern), 2 = length(L), - [Full,"hello",Pattern] = + [Full,"hello",Pattern] = run_echo_args(Dir,FN, [default,"hello",Pattern]), ok. @@ -1620,10 +1680,10 @@ mix_up_ports(Config) when is_list(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 -> + receive + {Port, {data, "Hello port!"}} = Msg1 -> io:format("~p~n", [Msg1]), - ok; + ok; Other -> ct:fail({unexpected, Other}) end, @@ -1631,7 +1691,7 @@ mix_up_ports(Config) when is_list(Config) -> receive {Port, closed} -> ok end, loop(start, done, fun(P) -> - Q = + Q = (catch erlang:open_port({spawn, "echo_drv"}, [])), %% io:format("~p ", [Q]), if is_port(Q) -> @@ -1642,7 +1702,7 @@ mix_up_ports(Config) when is_list(Config) -> end end), Port ! {self(), {command, "Hello again port!"}}, - receive + receive Msg2 -> ct:fail({unexpected, Msg2}) after 1000 -> @@ -1802,7 +1862,7 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> %% We want to start port programs from as many schedulers as possible %% and we want these port programs to terminate while multi-scheduling %% is blocked. - %% + %% NoSchedsOnln = erlang:system_info(schedulers_online), Parent = self(), io:format("SleepSecs = ~p~n", [SleepSecs]), @@ -2214,7 +2274,7 @@ ports_snapshots(0, _, _) -> ok; ports_snapshots(Iter, TrafficPid, OtherPorts) -> - TrafficPid ! start, + TrafficPid ! start, receive after 1 -> ok end, Snapshot = erlang:ports(), @@ -2243,7 +2303,7 @@ ports_traffic_stopped(MaxPorts, {PortList, PortCnt}) -> end. ports_traffic_started(MaxPorts, {PortList, PortCnt}, EventList) -> - receive + receive {Pid, stop} -> %%io:format("Traffic stopped in ~p\n",[self()]), Pid ! {self(), EventList, PortList}, @@ -2256,7 +2316,7 @@ ports_traffic_started(MaxPorts, {PortList, PortCnt}, EventList) -> ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) -> N = uniform(MaxPorts), case N > PortCnt of - true -> % Open port + true -> % Open port P = open_port({spawn, "exit_drv"}, []), %%io:format("Created port ~p\n",[P]), ports_traffic_started(MaxPorts, {[P|PortList], PortCnt+1}, @@ -2270,7 +2330,7 @@ ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) -> [{close,P}|EventList]) end. -ports_verify(Ports, PortsAfter, EventList) -> +ports_verify(Ports, PortsAfter, EventList) -> %%io:format("Candidate=~p\nEvents=~p\n", [PortsAfter, EventList]), case lists:sort(Ports) =:= lists:sort(PortsAfter) of true -> @@ -2280,10 +2340,10 @@ ports_verify(Ports, PortsAfter, EventList) -> %% Note that we track the event list "backwards", undoing open/close: case EventList of [{open,P} | Tail] -> - ports_verify(Ports, lists:delete(P,PortsAfter), Tail); + ports_verify(Ports, lists:delete(P,PortsAfter), Tail); [{close,P} | Tail] -> - ports_verify(Ports, [P | PortsAfter], Tail); + ports_verify(Ports, [P | PortsAfter], Tail); [] -> ct:fail("Inconsistent snapshot from erlang:ports()") @@ -2391,3 +2451,227 @@ wait_until(Fun) -> receive after 100 -> ok end, wait_until(Fun) end. + +%% Attempt to monitor pid as port, and port as pid +mon_port_invalid_type(_Config) -> + Port = hd(erlang:ports()), + ?assertError(badarg, erlang:monitor(port, self())), + ?assertError(badarg, erlang:monitor(process, Port)), + ok. + +%% With local port +mon_port_local(Config) -> + Port1 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + Ref1 = erlang:monitor(port, Port1), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Port1)), + Port1 ! {self(), {command, <<"1">>}}, % port test will close self immediately + receive ExitP1 -> ?assertMatch({'DOWN', Ref1, port, Port1, _}, ExitP1) + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Port1)), + + %% Trying to re-monitor a port which exists but is not healthy will + %% succeed but then will immediately send DOWN + Ref2 = erlang:monitor(port, Port1), + receive ExitP2 -> ?assertMatch({'DOWN', Ref2, port, Port1, _}, ExitP2) + after 1000 -> ?assert(false) end, + ok. + +%% With remote port on remote node (should fail) +mon_port_remote_on_remote(_Config) -> + Port3 = binary_to_term(<<131, 102, % Ext term format: PORT_EXT + 100, 0, 13, "fgsfds@fgsfds", % Node :: ATOM_EXT + 1:32/big, % Id + 0>>), % Creation + ?assertError(badarg, erlang:monitor(port, Port3)), + ok. + +%% Remote port belongs to this node and does not exist +%% Port4 produces #Port<0.167772160> which should not exist in a test run +mon_port_bad_remote_on_local(_Config) -> + Port4 = binary_to_term(<<131, 102, % Ext term format: PORT_EXT + 100, 0, 13, "nonode@nohost", % Node + 167772160:32/big, % Id + 0>>), % Creation + ?assertError(badarg, erlang:monitor(port, Port4)), + ok. + +%% Monitor owner (origin) dies before port is closed +mon_port_origin_dies(Config) -> + Port5 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + Self5 = self(), + Proc5 = spawn(fun() -> + Self5 ! test5_started, + erlang:monitor(port, Port5), + receive stop -> ok end + end), + erlang:monitor(process, Proc5), % we want to sync with its death + receive test5_started -> ok + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(Proc5, Port5)), + Proc5 ! stop, + % receive from monitor (removing race condition) + receive ExitP5 -> ?assertMatch({'DOWN', _, process, Proc5, _}, ExitP5) + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(Proc5, Port5)), + Port5 ! {self(), {command, <<"1">>}}, % make port quit + ok. + +%% Monitor a named port +mon_port_named(Config) -> + Name6 = test_port6, + Port6 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + erlang:register(Name6, Port6), + erlang:monitor(port, Name6), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Name6)), + Port6 ! {self(), {command, <<"1">>}}, % port test will close self immediately + receive ExitP6 -> ?assertMatch({'DOWN', _, port, {Name6, _}, _}, ExitP6) + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Name6)), + ok. + +%% Named does not exist: Should succeed but immediately send 'DOWN' +mon_port_bad_named(_Config) -> + Name7 = test_port7, + erlang:monitor(port, Name7), + receive {'DOWN', _, port, {Name7, _}, noproc} -> ok + after 1000 -> ?assert(false) end, + ok. + +%% Monitor a pid and demonitor by ref +mon_port_pid_demonitor(Config) -> + Port8 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + Ref8 = erlang:monitor(port, Port8), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Port8)), + erlang:demonitor(Ref8), + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Port8)), + Port8 ! {self(), {command, <<"1">>}}, % port test will close self immediately + ok. + +%% Monitor by name and demonitor by ref +mon_port_name_demonitor(Config) -> + Name9 = test_port9, + Port9 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + erlang:register(Name9, Port9), + Ref9 = erlang:monitor(port, Name9), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Name9)), + erlang:demonitor(Ref9), + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Name9)), + Port9 ! {self(), {command, <<"1">>}}, % port test will close self immediately + ok. + +%% 1. Spawn a port which will sleep 3 seconds +%% 2. Port driver and dies horribly (via C driver_failure call). This should +%% mark port as exiting or something. +%% 3. While the command happens, a monitor is requested on the port +mon_port_driver_die(Config) -> + erlang:process_flag(scheduler, 1), + + Path = proplists:get_value(data_dir, Config), + ok = load_driver(Path, "sleep_failure_drv"), + Port = open_port({spawn, "sleep_failure_drv"}, []), + + Self = self(), + erlang:spawn_opt(fun() -> + timer:sleep(250), + Ref = erlang:monitor(port, Port), + %% Now check that msg actually arrives + receive + {'DOWN', Ref, _Port2, _, _} = M -> Self ! M + after 3000 -> Self ! no_down_message + end + end,[{scheduler, 2}]), + Port ! {self(), {command, "Fail, please!"}}, + receive + A when is_atom(A) -> ?assertEqual(A, 'A_should_be_printed'); + {'DOWN', _R, port, Port, noproc} -> ok; + {'DOWN', _R, _P, _, _} = M -> ct:fail({got_wrong_down,M}) + after 5000 -> ?assert(false) + end, + ok. + + +%% 1. Spawn a port which will sleep 3 seconds +%% 2. Monitor port +%% 3. Port driver and dies horribly (via C driver_failure call). This should +%% mark port as exiting or something. +%% 4. While the command happens, a demonitor is requested on the port +mon_port_driver_die_demonitor(Config) -> + erlang:process_flag(scheduler, 1), + + Path = proplists:get_value(data_dir, Config), + ok = load_driver(Path, "sleep_failure_drv"), + Port = open_port({spawn, "sleep_failure_drv"}, []), + + Self = self(), + erlang:spawn_opt( + fun() -> + Ref = erlang:monitor(port, Port), + Self ! Ref, + timer:sleep(250), + erlang:demonitor(Ref), + %% Now check that msg still arrives, + %% the demon should have arrived after + %% the port exited + receive + {'DOWN', Ref, _Port2, _, _} = M -> Self ! M + after 3000 -> Self ! no_down_message + end + end,[{scheduler, 2}]), + Ref = receive R -> R end, + Port ! {self(), {command, "Fail, please!"}}, + receive + {'DOWN', Ref, port, Port, normal} -> ok; + {'DOWN', _R, _P, _, _} = M -> ct:fail({got_wrong_down,M}) + after 5000 -> ?assert(false) + end, + ok. + +%% @doc Makes a controllable port for testing. Underlying mechanism of this +%% port is not important, only important is our ability to close/kill it or +%% have it monitored. +create_port(Config, Args) -> + DataDir = ?config(data_dir, Config), + %% Borrow port test utility from port SUITE + Program = filename:join([DataDir, "port_test"]), + erlang:open_port({spawn_executable, Program}, [{args, Args}]). + +%% @doc Checks if process Pid exists, and if so, if its monitoring (or not) +%% the Port (or if port doesn't exist, we assume answer is no). +port_is_monitored(Pid, Port) when is_pid(Pid), is_port(Port) -> + %% Variant for when port is a port id (port()) + A = case erlang:process_info(Pid, monitors) of + undefined -> false; + {monitors, ProcMTargets} -> lists:member({port, Port}, ProcMTargets) + end, + B = case erlang:port_info(Port, monitored_by) of + undefined -> false; + {monitored_by, PortMonitors} -> lists:member(Pid, PortMonitors) + end, + {proc_monitors, A, port_monitored_by, B}; +port_is_monitored(Pid, PortName) when is_pid(Pid), is_atom(PortName) -> + %% Variant for when port is an atom + A = case erlang:process_info(Pid, monitors) of + undefined -> false; + {monitors, ProcMTargets} -> + lists:member({port, {PortName, node()}}, ProcMTargets) + end, + B = case erlang:whereis(PortName) of + undefined -> false; % name is not registered or is dead + PortId -> + case erlang:port_info(PortId, monitored_by) of + undefined -> false; % is dead + {monitored_by, PortMonitors} -> + lists:member(Pid, PortMonitors) + end + end, + {proc_monitors, A, port_monitored_by, B}. diff --git a/erts/emulator/test/port_SUITE_data/Makefile.src b/erts/emulator/test/port_SUITE_data/Makefile.src index ff822ae720..fb7685c4b6 100644 --- a/erts/emulator/test/port_SUITE_data/Makefile.src +++ b/erts/emulator/test/port_SUITE_data/Makefile.src @@ -4,7 +4,7 @@ CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@ CROSSLDFLAGS = @CROSSLDFLAGS@ PROGS = port_test@exe@ echo_args@exe@ dead_port@exe@ -DRIVERS = echo_drv@dll@ exit_drv@dll@ failure_drv@dll@ +DRIVERS = echo_drv@dll@ exit_drv@dll@ failure_drv@dll@ sleep_failure_drv@dll@ all: $(PROGS) $(DRIVERS) port_test.@EMULATOR@ diff --git a/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c b/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c new file mode 100644 index 0000000000..1f52646572 --- /dev/null +++ b/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c @@ -0,0 +1,76 @@ +#include +#include "erl_driver.h" +#ifdef __WIN32__ +# include +#else +# include +#endif + +typedef struct _erl_drv_data FailureDrvData; + +static FailureDrvData *failure_drv_start(ErlDrvPort, char *); +static void failure_drv_stop(FailureDrvData *); +static void failure_drv_output(ErlDrvData, char *, ErlDrvSizeT); +static void failure_drv_finish(void); + +static ErlDrvEntry failure_drv_entry = { + NULL, /* init */ + failure_drv_start, + failure_drv_stop, + failure_drv_output, + NULL, /* ready_input */ + NULL, /* ready_output */ + "sleep_failure_drv", + NULL, /* finish */ + NULL, /* handle */ + NULL, /* control */ + NULL, /* timeout */ + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, + NULL, + NULL, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, + NULL, + NULL, + NULL, +}; + + + +/* ------------------------------------------------------------------------- +** Entry functions +**/ + +DRIVER_INIT(failure_drv) +{ + return &failure_drv_entry; +} + +static FailureDrvData *failure_drv_start(ErlDrvPort port, char *command) { + void *void_ptr; + + return void_ptr = port; +} + +static void failure_drv_stop(FailureDrvData *data_p) { +} + +static void failure_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) { + FailureDrvData *data_p = (FailureDrvData *) drv_data; + void *void_ptr; + ErlDrvPort port = void_ptr = data_p; + +#ifdef __WIN32__ + Sleep(3000); +#else + sleep(3); +#endif + driver_failure(port, 0); +} + +static void failure_drv_finish() { +} diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 94f3078173..edf79b8f75 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1206,16 +1206,18 @@ module_loaded(_Module) -> erlang:nif_error(undefined). -type registered_name() :: atom(). - -type registered_process_identifier() :: registered_name() | {registered_name(), node()}. - -type monitor_process_identifier() :: pid() | registered_process_identifier(). +-type monitor_port_identifier() :: port() | registered_name(). %% monitor/2 --spec monitor(process, monitor_process_identifier()) -> MonitorRef when - MonitorRef :: reference(); - (time_offset, clock_service) -> MonitorRef when - MonitorRef :: reference(). +-spec monitor + (process, monitor_process_identifier()) -> MonitorRef + when MonitorRef :: reference(); + (port, monitor_port_identifier()) -> MonitorRef + when MonitorRef :: reference(); + (time_offset, clock_service) -> MonitorRef + when MonitorRef :: reference(). monitor(_Type, _Item) -> erlang:nif_error(undefined). @@ -2160,7 +2162,7 @@ process_flag(_Flag, _Value) -> {max_heap_size, MaxHeapSize :: max_heap_size()} | {monitored_by, Pids :: [pid()]} | {monitors, - Monitors :: [{process, Pid :: pid() | + Monitors :: [{process | port, Pid :: pid() | port() | {RegName :: atom(), Node :: node()}}]} | {message_queue_data, MQD :: message_queue_data()} | {priority, Level :: priority_level()} | @@ -3087,6 +3089,9 @@ port_info(Port) -> (Port, monitors) -> {monitors, Monitors} | 'undefined' when Port :: port() | atom(), Monitors :: [{process, pid()}]; + (Port, monitored_by) -> {monitored_by, MonitoredBy} | 'undefined' when + Port :: port() | atom(), + MonitoredBy :: [pid()]; (Port, name) -> {name, Name} | 'undefined' when Port :: port() | atom(), Name :: string(); -- cgit v1.2.3 From 98068b0698d6cc0d0dd172ab575b1b4c74d864fb Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 10 Jun 2016 08:34:36 +0200 Subject: erts: Fix HEART_NO_KILL logic --- erts/etc/common/heart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index 0dd82cc036..6f14b08deb 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -529,7 +529,7 @@ kill_old_erlang(void){ char* envvar = NULL; envvar = get_env(HEART_NO_KILL); - if (!envvar || strcmp(envvar, "TRUE") == 0) + if (envvar && strcmp(envvar, "TRUE") == 0) return; if(heart_beat_kill_pid != 0){ @@ -566,7 +566,7 @@ kill_old_erlang(void){ char *envvar = NULL; envvar = get_env(HEART_NO_KILL); - if (!envvar || strcmp(envvar, "TRUE") == 0) + if (envvar && strcmp(envvar, "TRUE") == 0) return; envvar = get_env(HEART_KILL_SIGNAL); -- cgit v1.2.3 From c1100d7f3d52959fc4c8530558dbcacc89b34688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 10 Jun 2016 18:37:08 +0200 Subject: erts: Fix undefined shift to msb in ethr_mutex --- erts/include/internal/ethr_mutex.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h index a510a2c97f..8ef3b1e40b 100644 --- a/erts/include/internal/ethr_mutex.h +++ b/erts/include/internal/ethr_mutex.h @@ -108,13 +108,13 @@ void LeaveCriticalSection(CRITICAL_SECTION *); # error Need a qlock implementation #endif -#define ETHR_RWMTX_W_FLG__ (((ethr_sint32_t) 1) << 31) -#define ETHR_RWMTX_W_WAIT_FLG__ (((ethr_sint32_t) 1) << 30) -#define ETHR_RWMTX_R_WAIT_FLG__ (((ethr_sint32_t) 1) << 29) +#define ETHR_RWMTX_W_FLG__ ((ethr_sint32_t) (1U << 31)) +#define ETHR_RWMTX_W_WAIT_FLG__ ((ethr_sint32_t) (1U << 30)) +#define ETHR_RWMTX_R_WAIT_FLG__ ((ethr_sint32_t) (1U << 29)) /* frequent read kind */ -#define ETHR_RWMTX_R_FLG__ (((ethr_sint32_t) 1) << 28) -#define ETHR_RWMTX_R_ABRT_UNLCK_FLG__ (((ethr_sint32_t) 1) << 27) +#define ETHR_RWMTX_R_FLG__ ((ethr_sint32_t) (1U << 28)) +#define ETHR_RWMTX_R_ABRT_UNLCK_FLG__ ((ethr_sint32_t) (1U << 27)) #define ETHR_RWMTX_R_PEND_UNLCK_MASK__ (ETHR_RWMTX_R_ABRT_UNLCK_FLG__ - 1) /* normal kind */ -- cgit v1.2.3 From 222b0d728869feae00fad9e7da7633e7cb349c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 10 Jun 2016 19:01:43 +0200 Subject: erts: Fix undefined shift to msb in erl_thr_progress --- erts/emulator/beam/erl_thr_progress.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 542541165b..21938e7684 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -95,9 +95,9 @@ #define ERTS_THR_PRGR_FTL_ERR_BLCK_POLL_INTERVAL 100 -#define ERTS_THR_PRGR_LFLG_BLOCK (((erts_aint32_t) 1) << 31) -#define ERTS_THR_PRGR_LFLG_NO_LEADER (((erts_aint32_t) 1) << 30) -#define ERTS_THR_PRGR_LFLG_WAITING_UM (((erts_aint32_t) 1) << 29) +#define ERTS_THR_PRGR_LFLG_BLOCK ((erts_aint32_t) (1U << 31)) +#define ERTS_THR_PRGR_LFLG_NO_LEADER ((erts_aint32_t) (1U << 30)) +#define ERTS_THR_PRGR_LFLG_WAITING_UM ((erts_aint32_t) (1U << 29)) #define ERTS_THR_PRGR_LFLG_ACTIVE_MASK (~(ERTS_THR_PRGR_LFLG_NO_LEADER \ | ERTS_THR_PRGR_LFLG_BLOCK \ | ERTS_THR_PRGR_LFLG_WAITING_UM)) @@ -142,8 +142,8 @@ init_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) #warning "Thread progress state debug is on" #endif -#define ERTS_THR_PROGRESS_STATE_DEBUG_LEADER (((erts_aint32_t) 1) << 0) -#define ERTS_THR_PROGRESS_STATE_DEBUG_ACTIVE (((erts_aint32_t) 1) << 1) +#define ERTS_THR_PROGRESS_STATE_DEBUG_LEADER ((erts_aint32_t) (1U << 0)) +#define ERTS_THR_PROGRESS_STATE_DEBUG_ACTIVE ((erts_aint32_t) (1U << 1)) #define ERTS_THR_PROGRESS_STATE_DEBUG_INIT(ID) \ erts_atomic32_init_nob(&intrnl->thr[(ID)].data.state_debug, \ @@ -179,10 +179,10 @@ do { \ #endif /* ERTS_THR_PROGRESS_STATE_DEBUG */ -#define ERTS_THR_PRGR_BLCKR_INVALID (~((erts_aint32_t) 0)) -#define ERTS_THR_PRGR_BLCKR_UNMANAGED (((erts_aint32_t) 1) << 31) +#define ERTS_THR_PRGR_BLCKR_INVALID ((erts_aint32_t) (~0U)) +#define ERTS_THR_PRGR_BLCKR_UNMANAGED ((erts_aint32_t) (1U << 31)) -#define ERTS_THR_PRGR_BC_FLG_NOT_BLOCKING (((erts_aint32_t) 1) << 31) +#define ERTS_THR_PRGR_BC_FLG_NOT_BLOCKING ((erts_aint32_t) (1U << 31)) #define ERTS_THR_PRGR_BM_BITS 32 #define ERTS_THR_PRGR_BM_SHIFT 5 @@ -1186,7 +1186,7 @@ wakeup_unmanaged_threads(ErtsThrPrgrUnmanagedWakeupData *umwd) int hbase = hix << ERTS_THR_PRGR_BM_SHIFT; int hbit; for (hbit = 0; hbit < ERTS_THR_PRGR_BM_BITS; hbit++) { - if (hmask & (1 << hbit)) { + if (hmask & (1U << hbit)) { erts_aint_t lmask; int lix = hbase + hbit; ASSERT(0 <= lix && lix < umwd->low_sz); @@ -1195,7 +1195,7 @@ wakeup_unmanaged_threads(ErtsThrPrgrUnmanagedWakeupData *umwd) int lbase = lix << ERTS_THR_PRGR_BM_SHIFT; int lbit; for (lbit = 0; lbit < ERTS_THR_PRGR_BM_BITS; lbit++) { - if (lmask & (1 << lbit)) { + if (lmask & (1U << lbit)) { int id = lbase + lbit; wakeup_unmanaged(id); } -- cgit v1.2.3 From c2ff9af2eb8d372af3f6cf8fc2953dc90f9ead78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 10 Jun 2016 19:11:14 +0200 Subject: erts: Fix undefined shift to msb in monotonic time --- erts/emulator/sys/unix/erl_unix_sys.h | 2 +- erts/emulator/sys/win32/erl_win_sys.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 241540b894..3a0d23cd36 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -163,7 +163,7 @@ typedef long long ErtsSysHrTime; typedef ErtsMonotonicTime ErtsSystemTime; typedef ErtsSysHrTime ErtsSysPerfCounter; -#define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63) +#define ERTS_MONOTONIC_TIME_MIN ((ErtsMonotonicTime) (1ULL << 63)) #define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN) /* diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 7bdfac168b..04fbf23109 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -187,7 +187,7 @@ typedef ErtsMonotonicTime ErtsSysPerfCounter; ErtsSystemTime erts_os_system_time(void); -#define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63) +#define ERTS_MONOTONIC_TIME_MIN ((ErtsMonotonicTime) (1ULL << 63)) #define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN) #define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1 -- cgit v1.2.3 From 45c595ab6150792b2b7be9a407c3d30e634e3924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 10 Jun 2016 19:47:47 +0200 Subject: erts: Fix undefined shift to msb in erl_process --- erts/emulator/beam/erl_process.c | 2 +- erts/emulator/beam/erl_ptab.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 48f89d2bd7..66f22979ad 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -3509,7 +3509,7 @@ wake_dirty_schedulers(ErtsRunQueue *rq, int one) #endif #define ERTS_NO_USED_RUNQS_SHIFT 16 -#define ERTS_NO_RUNQS_MASK 0xffff +#define ERTS_NO_RUNQS_MASK 0xffffU #if ERTS_MAX_NO_OF_SCHEDULERS > ERTS_NO_RUNQS_MASK # error "Too large amount of schedulers allowed" diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h index a5931ffc25..fecfd96ab0 100644 --- a/erts/emulator/beam/erl_ptab.h +++ b/erts/emulator/beam/erl_ptab.h @@ -168,7 +168,7 @@ typedef struct { #define ERTS_PTAB_INVALID_ID(TAG) \ ((Eterm) \ - ((((1 << ERTS_PTAB_ID_DATA_SIZE) - 1) << ERTS_PTAB_ID_DATA_SHIFT) \ + ((((1U << ERTS_PTAB_ID_DATA_SIZE) - 1) << ERTS_PTAB_ID_DATA_SHIFT) \ | (TAG))) #define erts_ptab_is_valid_id(ID) \ -- cgit v1.2.3 From 4e87bebe004039465548ed432d363f2d7e42fee6 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 13 Jun 2016 10:08:47 +0200 Subject: erts: Allow enif_port_command in non-sched thread It has to be possible to send trace messages to a port from non-schedulers threads (specifically from the sys_msg_dispatcher). --- erts/emulator/beam/erl_nif.c | 25 +++++++++---------------- erts/emulator/test/trace_port_SUITE.erl | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 039f97ef43..23931f0e54 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -769,32 +769,25 @@ enif_port_command(ErlNifEnv *env, const ErlNifPort* to_port, if (scheduler > 0) prt = erts_port_lookup(to_port->port_id, iflags); -#ifdef ERTS_DIRTY_SCHEDULERS - else if (scheduler < 0) { + else { +#ifdef ERTS_SMP if (ERTS_PROC_IS_EXITING(c_p)) return 0; prt = erts_thr_port_lookup(to_port->port_id, iflags); - } +#else + erts_exit(ERTS_ABORT_EXIT, + "enif_port_command: called from non-scheduler " + "thread on non-SMP VM"); #endif - else { - erts_exit(ERTS_ABORT_EXIT, "enif_port_command: " - "called from non-scheduler thread"); } if (!prt) res = 0; - else { - - if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) - trace_port_receive(prt, c_p->common.id, am_command, msg); - - res = erts_port_output_async(prt, c_p->common.id, msg); - } + else + res = erts_port_output_async(prt, c_p->common.id, msg); -#ifdef ERTS_DIRTY_SCHEDULERS - if (scheduler < 0) + if (scheduler <= 0) erts_port_dec_refc(prt); -#endif return res; } diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index a66563d15b..e4db368ea1 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -26,6 +26,7 @@ return_trace/1, send/1, receive_trace/1, + receive_trace_non_scheduler/1, process_events/1, schedule/1, gc/1, @@ -40,6 +41,7 @@ suite() -> all() -> [call_trace, return_trace, send, receive_trace, + receive_trace_non_scheduler, process_events, schedule, gc, default_tracer, tracer_port_crash]. @@ -184,6 +186,26 @@ receive_trace(Config) when is_list(Config) -> expect({trace_ts,Receiver,'receive',Huge,ts}), ok. +%% Test sending receive traces to a port. +receive_trace_non_scheduler(Config) when is_list(Config) -> + start_tracer(Config), + S = self(), + Receiver = spawn( + fun() -> + receive + go -> + Ref = S ! erlang:trace_delivered(all), + receive {trace_delivered, Ref, all} -> ok end + end + end), + trac(Receiver, true, ['receive']), + Receiver ! go, + Ref = receive R -> R end, + expect({trace,Receiver,'receive',go}), + expect({trace,Receiver,'receive',{trace_delivered, all, Ref}}), + + ok. + %% Tests a few process events (like getting linked). process_events(Config) when is_list(Config) -> start_tracer(Config), -- cgit v1.2.3 From 8c19f3d39ff848817e8032f081577afbd5e75493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 13 Jun 2016 16:55:55 +0200 Subject: erts: Fix profile runnable processes race --- erts/emulator/beam/erl_trace.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 3dca58d60b..4cf38bf894 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -2115,32 +2115,36 @@ profile_runnable_proc(Process *p, Eterm status){ Eterm *hp, msg; Eterm where = am_undefined; ErlHeapFragment *bp = NULL; - int use_current = 1; + BeamInstr *current = NULL; #ifndef ERTS_SMP #define LOCAL_HEAP_SIZE (4 + 6 + ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); hp = local_heap; #else + ErtsThrPrgrDelayHandle dhndl; Uint hsz = 4 + 6 + patch_ts_size(erts_system_profile_ts_type)-1; #endif - - if (ERTS_PROC_IS_EXITING(p)) { - use_current = 0; - /* could probably set 'where' to 'exiting' here, - * though it's not documented as such */ - } else { - if (!p->current) { - p->current = find_function_from_pc(p->i); + /* Assumptions: + * We possibly don't have the MAIN_LOCK for the process p here. + * We assume that we can read from p->current and p->i atomically + */ +#ifdef ERTS_SMP + dhndl = erts_thr_progress_unmanaged_delay(); /* suspend purge operations */ +#endif + + if (!ERTS_PROC_IS_EXITING(p)) { + if (p->current) { + current = p->current; + } else { + current = find_function_from_pc(p->i); } - use_current = p->current != NULL; } #ifdef ERTS_SMP - if (!use_current) { + if (!current) { hsz -= 4; } @@ -2148,11 +2152,15 @@ profile_runnable_proc(Process *p, Eterm status){ hp = bp->mem; #endif - if (use_current) { - where = TUPLE3(hp, p->current[0], p->current[1], make_small(p->current[2])); hp += 4; + if (current) { + where = TUPLE3(hp, current[0], current[1], make_small(current[2])); hp += 4; } else { where = make_small(0); } + +#ifdef ERTS_SMP + erts_thr_progress_unmanaged_continue(dhndl); +#endif erts_smp_mtx_lock(&smq_mtx); -- cgit v1.2.3 From 7b5ca88b32c22a7f1a3152265d8d73418013b1c0 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 13 May 2016 10:38:20 +0200 Subject: erts: Fix doc xml errors --- erts/doc/src/erl.xml | 11 +- erts/doc/src/erl_driver.xml | 4 +- erts/doc/src/erl_nif.xml | 26 ++--- erts/doc/src/erlang.xml | 242 ++++++++++++++++++++++---------------------- 4 files changed, 143 insertions(+), 140 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 5d5bfb141f..7b90a1ccca 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -643,8 +643,7 @@

Sets the default binary virtual heap size of processes to the size .

- - +

Sets the default maximum heap size of processes to the size . If +hmax is not given, the default is 0 @@ -653,8 +652,7 @@ process_flag(max_heap_size, MaxHeapSize).

- - +

Sets whether to send an error logger message for processes that reach the maximum heap size or not. If +hmaxel is not given, the default is true. @@ -662,8 +660,7 @@ process_flag(max_heap_size, MaxHeapSize).

- - +

Sets whether to kill processes that reach the maximum heap size or not. If +hmaxk is not given, the default is true. For more information, @@ -676,7 +673,7 @@

Sets the initial process dictionary size of processes to the size .

- +hmqd off_heap|on_heap + +hmqd off_heap|on_heap

Sets the default value for the process flag message_queue_data. If +hmqd is not diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 175b7f6bfb..82215ead46 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -348,14 +348,14 @@ all will work as before.

Time Measurement -

Support for time measurement in drivers: +

Support for time measurement in drivers:

ErlDrvTime ErlDrvTimeUnit erl_drv_monotonic_time() erl_drv_time_offset() erl_drv_convert_time_unit() -

+
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 123d353432..8b02b3bae1 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -518,13 +518,15 @@ ok int (*reload)(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) -

The reload mechanism is deprecated. It was only intended - as a development feature. Do not use it as an upgrade method for - live production systems. It might be removed in future releases. Be sure - to pass reload as NULL to ERL_NIF_INIT - to disable it when not used.

-
-

reload is called when the NIF library is loaded + + +

The reload mechanism is deprecated. It was only intended + as a development feature. Do not use it as an upgrade method for + live production systems. It might be removed in future releases. Be sure + to pass reload as NULL to ERL_NIF_INIT + to disable it when not used.

+ +

reload is called when the NIF library is loaded and there is already a previously loaded library for this module code.

Works the same as load. The only difference is that @@ -583,9 +585,9 @@ ok

typedef struct { - const char* name; - unsigned arity; - ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + const char* name; + unsigned arity; + ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); unsigned flags; } ErlNifFunc; @@ -618,8 +620,8 @@ typedef struct {

typedef struct { - unsigned size; - unsigned char* data; + unsigned size; + unsigned char* data; } ErlNifBinary;

ErlNifBinary contains transient information about an diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index fa13e4c142..6289f033b2 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4361,86 +4361,86 @@ os_prompt% - Sets process flag max_heap_size for the calling process. +

This flag sets the maximum heap size for the calling process. If MaxHeapSize is an integer, the system default values for kill and error_logger are used. - - size - -

- The maximum size in words of the process. If set to zero, the - heap size limit is disabled. Badarg will be thrown if the value is - smaller than - min_heap_size. - The size check is only done when a garbage collection is triggered. -

-

- size is the entire heap of the process when garbage collection - is triggered, this includes all generational heaps, the process stack, - any - messages that are considered to be part of the heap and any - extra memory that the garbage collector needs during collection. -

-

- size is the same as can be retrieved using - - erlang:process_info(Pid, total_heap_size), - or by adding heap_block_size, old_heap_block_size - and mbuf_size from - erlang:process_info(Pid, garbage_collection_info). -

-
- kill - -

- When set to true the runtime system will send an - untrappable exit signal with reason kill to the process - if the maximum heap size is reached. The garbage collection - that triggered the kill will not be completed, instead the - process will exit as soon as is possible. When set to false - no exit signal will be sent to the process, instead it will - continue executing. -

-

- If kill is not defined in the map - the system default will be used. The default system default - is true. It can be changed by either the erl - +hmaxk option, - or - erlang:system_flag(max_heap_size, MaxHeapSize). -

-
- error_logger - -

- When set to true the runtime system will send a - message to the current error_logger - containing details about the process when the maximum - heap size is reached. One error_logger report will - be sent each time the limit is reached. -

-

- If error_logger is not defined in the map the system - default will be used. The default system default is true. - It can be changed by either the erl +hmaxel - option, or - erlang:system_flag(max_heap_size, MaxHeapSize). -

-
+

+ + size + +

+ The maximum size in words of the process. If set to zero, the + heap size limit is disabled. Badarg will be thrown if the value is + smaller than + min_heap_size. + The size check is only done when a garbage collection is triggered. +

+

+ size is the entire heap of the process when garbage collection + is triggered, this includes all generational heaps, the process stack, + any + messages that are considered to be part of the heap and any + extra memory that the garbage collector needs during collection. +

+

+ size is the same as can be retrieved using + + erlang:process_info(Pid, total_heap_size), + or by adding heap_block_size, old_heap_block_size + and mbuf_size from + erlang:process_info(Pid, garbage_collection_info). +

+
+ kill + +

+ When set to true the runtime system will send an + untrappable exit signal with reason kill to the process + if the maximum heap size is reached. The garbage collection + that triggered the kill will not be completed, instead the + process will exit as soon as is possible. When set to false + no exit signal will be sent to the process, instead it will + continue executing. +

- The heap size of a process is quite hard to predict, especially the - amount of memory that is used during the garbage collection. When - contemplating using this option, it is recommended to first run - it in production with kill set to false and inspect - the error_logger reports to see what the normal peak sizes - of the processes in the system is and then tune the value - accordingly. + If kill is not defined in the map + the system default will be used. The default system default + is true. It can be changed by either the erl + +hmaxk option, + or + erlang:system_flag(max_heap_size, MaxHeapSize).

-
+ + error_logger + +

+ When set to true the runtime system will send a + message to the current error_logger + containing details about the process when the maximum + heap size is reached. One error_logger report will + be sent each time the limit is reached. +

+

+ If error_logger is not defined in the map the system + default will be used. The default system default is true. + It can be changed by either the erl +hmaxel + option, or + erlang:system_flag(max_heap_size, MaxHeapSize). +

+
+ +

+ The heap size of a process is quite hard to predict, especially the + amount of memory that is used during the garbage collection. When + contemplating using this option, it is recommended to first run + it in production with kill set to false and inspect + the error_logger reports to see what the normal peak sizes + of the processes in the system is and then tune the value + accordingly.

@@ -4797,8 +4797,10 @@ os_prompt% The content of GCInfo can be changed without prior notice.

- - {garbage_collection_info, GCInfo} + + + {garbage_collection_info, GCInfo} +

GCInfo is a list containing miscellaneous detailed information about garbage collection for this process. @@ -4986,8 +4988,10 @@ os_prompt% total suspend count on Suspendee, only the parts contributed by Pid.

- - {total_heap_size, Size} + + + {total_heap_size, Size} +

Size is the total size, in words, of all heap fragments of the process. This includes the process stack and @@ -6631,8 +6635,8 @@ ok - Sets system flag max_heap_size +

Sets the default maximum heap size settings for processes. @@ -7136,9 +7140,9 @@ ok + Information about the default process heap settings. - Information about the default process heap settings. fullsweep_after @@ -7183,7 +7187,7 @@ ok where MinHeapSize is the current system-wide minimum heap size for spawned processes.

- message_queue_data + message_queue_data

Returns the default value of the message_queue_data process flag which is either off_heap, or on_heap. @@ -7664,7 +7668,7 @@ ok and erlang:system_info(schedulers).

- otp_release + otp_release

Returns a string containing the OTP release number of the @@ -8617,21 +8621,21 @@ timestamp() -> send

Traces sending of messages.

-

Message tags: send and - send_to_non_existing_process.

+

Message tags: send and + send_to_non_existing_process.

'receive'

Traces receiving of messages.

-

Message tags: 'receive'.

+

Message tags: 'receive'.

call

Traces certain function calls. Specify which function calls to trace by calling erlang:trace_pattern/3.

-

Message tags: call and - return_from.

+

Message tags: call and + return_from.

silent @@ -8649,9 +8653,9 @@ timestamp() -> specification function {silent,Bool}, giving a high degree of control of which functions with which arguments that trigger the trace.

-

Message tags: call, - return_from, and - return_to. Or rather, the absence of.

+

Message tags: call, + return_from, and + return_to. Or rather, the absence of.

return_to @@ -8672,43 +8676,43 @@ timestamp() ->

To get trace messages containing return values from functions, use the {return_trace} match specification action instead.

-

Message tags: return_to.

+

Message tags: return_to.

procs

Traces process-related events.

-

Message tags: spawn, - spawned, - exit, - register, - unregister, - link, - unlink, - getting_linked, and - getting_unlinked.

+

Message tags: spawn, + spawned, + exit, + register, + unregister, + link, + unlink, + getting_linked, and + getting_unlinked.

ports

Traces port-related events.

-

Message tags: open, - closed, - register, - unregister, - getting_linked, and - getting_unlinked.

+

Message tags: open, + closed, + register, + unregister, + getting_linked, and + getting_unlinked.

running

Traces scheduling of processes.

-

Message tags: in and - out.

+

Message tags: in and + out.

exiting

Traces scheduling of exiting processes.

-

Message tags: in_exiting, - out_exiting, and - out_exited.

+

Message tags: in_exiting, + out_exiting, and + out_exited.

running_procs @@ -8716,21 +8720,21 @@ timestamp() -> However this option also includes schedule events when the process executes within the context of a port without being scheduled out itself.

-

Message tags: in and - out.

+

Message tags: in and + out.

running_ports

Traces scheduling of ports.

-

Message tags: in and - out.

+

Message tags: in and + out.

garbage_collection

Traces garbage collections of processes.

-

Message tags: gc_minor_start, - gc_max_heap_size and - gc_minor_end.

+

Message tags: gc_minor_start, + gc_max_heap_size and + gc_minor_end.

timestamp @@ -8758,7 +8762,7 @@ timestamp() -> Erlang monotonic time time-stamp in all trace messages. The time-stamp (Ts) has the same format and value as produced by - erlang:monotonic_time(nano_seconds). + erlang:monotonic_time(nano_seconds). This flag overrides the cpu_timestamp flag.

strict_monotonic_timestamp @@ -8768,8 +8772,8 @@ timestamp() -> monotonic time and a monotonically increasing integer in all trace messages. The time-stamp (Ts) has the same format and value as produced by - {erlang:monotonic_time(nano_seconds), - erlang:unique_integer([monotonic])}. + {erlang:monotonic_time(nano_seconds), + erlang:unique_integer([monotonic])}. This flag overrides the cpu_timestamp flag.

arity -- cgit v1.2.3 From d01f67ffaefbfc6cad980f0d58f38891c674c652 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 31 May 2016 15:06:02 +0200 Subject: erts: fix atom_roundtrip_r15b tc if node fails to start, just skip testcase --- erts/emulator/test/distribution_SUITE.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index d0096fb1bc..258ae57e18 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -1037,10 +1037,13 @@ atom_roundtrip_r15b(Config) when is_list(Config) -> ct:timetrap({minutes, 6}), AtomData = atom_data(), verify_atom_data(AtomData), - {ok, Node} = start_node(Config, [], "r15b"), - do_atom_roundtrip(Node, AtomData), - stop_node(Node), - ok; + case start_node(Config, [], "r15b") of + {ok, Node} -> + do_atom_roundtrip(Node, AtomData), + stop_node(Node); + {error, timeout} -> + {skip,"Unable to start OTP R15B release"} + end; false -> {skip,"No OTP R15B available"} end. -- cgit v1.2.3 From 4f9c3328330df12955a27b455f3763d51f18f4c2 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 31 May 2016 18:35:52 +0200 Subject: erl_interface: Fix decode_ulong on windows --- erts/aclocal.m4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 86799186fd..013bfe5652 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -128,13 +128,13 @@ MIXED_MSYS=no AC_MSG_CHECKING(for mixed cygwin or msys and native VC++ environment) if test "X$host" = "Xwin32" -a "x$GCC" != "xyes"; then if test -x /usr/bin/msys-?.0.dll; then - CFLAGS="-O2" + CFLAGS="$CFLAGS -O2" MIXED_MSYS=yes AC_MSG_RESULT([MSYS and VC]) MIXED_MSYS_VC=yes CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MSYS_VC" elif test -x /usr/bin/cygpath; then - CFLAGS="-O2" + CFLAGS="$CFLAGS -O2" MIXED_CYGWIN=yes AC_MSG_RESULT([Cygwin and VC]) MIXED_CYGWIN_VC=yes @@ -162,7 +162,7 @@ if test "x$MIXED_MSYS" != "xyes"; then AC_MSG_CHECKING(for mixed cygwin and native MinGW environment) if test "X$host" = "Xwin32" -a "x$GCC" = x"yes"; then if test -x /usr/bin/cygpath; then - CFLAGS="-O2" + CFLAGS="$CFLAGS -O2" MIXED_CYGWIN=yes AC_MSG_RESULT([yes]) MIXED_CYGWIN_MINGW=yes -- cgit v1.2.3 From d837c512bff62d5ad932cda615a340ad088a8999 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 3 Jun 2016 16:46:52 +0200 Subject: erts: Ensure bs_add_overflow test has enough memory --- erts/emulator/test/bs_construct_SUITE.erl | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 941cb435f7..4a2bbf69fd 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -23,6 +23,7 @@ -module(bs_construct_SUITE). -export([all/0, suite/0, + init_per_suite/1, end_per_suite/1, test1/1, test2/1, test3/1, test4/1, test5/1, testf/1, not_used/1, in_guard/1, mem_leak/1, coerce_to_float/1, bjorn/1, @@ -43,6 +44,12 @@ all() -> copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width, bad_append, bs_add_overflow]. +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + application:stop(os_mon). + big(1) -> 57285702734876389752897683. @@ -906,10 +913,14 @@ append_unit_16(Bin) -> %% Produce a large result of bs_add that, if cast to signed int, would overflow %% into a negative number that fits a smallnum. -bs_add_overflow(Config) -> +bs_add_overflow(_Config) -> + Memsize = memsize(), + io:format("Memsize = ~w Bytes~n", [Memsize]), case erlang:system_info(wordsize) of 8 -> {skip, "64-bit architecture"}; + _ when Memsize < (2 bsl 30) -> + {skip, "Less then 2 GB of memory"}; 4 -> Large = <<0:((1 bsl 30)-1)>>, {'EXIT',{system_limit,_}} = @@ -918,5 +929,10 @@ bs_add_overflow(Config) -> Large/bits>>), ok end. - + id(I) -> I. + +memsize() -> + application:ensure_all_started(os_mon), + {Tot,_Used,_} = memsup:get_memory_data(), + Tot. -- cgit v1.2.3 From f18d6eb9d6900e21cefe3ce44b644015a0444279 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 31 May 2016 14:36:40 +0200 Subject: erts: Fix distribution_SUITE:bulk_send_bigbig on windows The granualarity of the windows time sometimes makes timer:tc return 0 as the time taken for the test. Change this to use the most common time instead. --- erts/emulator/test/distribution_SUITE.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 258ae57e18..0b5fdbd7ff 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -182,8 +182,13 @@ bulk_sendsend2(Terms, BinSize, BusyBufSize) -> {ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++ integer_to_list(BusyBufSize)), _Send = spawn(NodeSend, erlang, apply, [fun sendersender/4, [self(), Recv, Bin, Terms]]), {Elapsed, {_TermsN, SizeN}, MonitorCount} = - receive {sendersender, BigRes} -> - BigRes + receive + %% On some platforms (windows), the time taken is 0 so we + %% simulate that some little time has passed. + {sendersender, {0.0,T,MC}} -> + {0.0015, T, MC}; + {sendersender, BigRes} -> + BigRes end, stop_node(NodeRecv), stop_node(NodeSend), -- cgit v1.2.3 From 9411a2098d983cac05fd7198bba26b9d56aa484a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 20 May 2016 11:55:45 +0200 Subject: erts: Increase bif and nif call_time trace test This makes the time that the tests execute longer, which means that on systems with low resolution on timers (e.g. windows) the test is less likely to fail. --- erts/emulator/test/trace_call_time_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 40c8bc4340..6582ad134b 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -351,7 +351,7 @@ combo(Config) when is_list(Config) -> %% Tests tracing of bifs bif(Config) when is_list(Config) -> P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - M = 1000000, + M = 5000000, %% 2 = erlang:trace_pattern({erlang, binary_to_term, '_'}, true, [call_time]), 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]), @@ -381,7 +381,7 @@ bif(Config) when is_list(Config) -> nif(Config) when is_list(Config) -> load_nif(Config), P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - M = 1000000, + M = 5000000, %% 1 = erlang:trace_pattern({?MODULE, nif_dec, '_'}, true, [call_time]), 1 = erlang:trace_pattern({?MODULE, with_nif, '_'}, true, [call_time]), -- cgit v1.2.3 From 1576cbabb934bad54f9c25ebc7f763ff6208434f Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 2 Jun 2016 11:23:57 +0200 Subject: erts: Add etp commands to re-compile erlang --- erts/etc/unix/etp-commands.in | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 8f9945c4b4..ce334c9dcb 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -3694,6 +3694,26 @@ document etp-address-to-beam-opcode %--------------------------------------------------------------------------- end +define etp-compile-debug + shell (cd $ERL_TOP && make emulator FLAVOR=smp TYPE=debug) +end + +document etp-compile-debug +%--------------------------------------------------------------------------- +% Re-compile the debug erlang emulator +%--------------------------------------------------------------------------- +end + +define etp-compile + shell (cd $ERL_TOP && make emulator) +end + +document etp-compile +%--------------------------------------------------------------------------- +% Re-compile the erlang emulator +%--------------------------------------------------------------------------- +end + ############################################################################ # Toolbox parameter handling -- cgit v1.2.3 From 59291d4f7413c5f18764e1897190b6c6f5cfce83 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 14 Jun 2016 10:59:30 +0200 Subject: erts: Make etp-*-info take any value as input --- erts/etc/unix/etp-commands.in | 111 ++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 54 deletions(-) (limited to 'erts') diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index ce334c9dcb..15fb718c47 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -1807,52 +1807,53 @@ define etp-process-info # Args: Process* # printf " Pid: " - etp-1 ($arg0)->common.id + set $etp_proc = ((Process*)$arg0) + etp-1 $etp_proc->common.id printf "\n State: " - etp-proc-state $arg0 + etp-proc-state $etp_proc if $proxy_process != 0 - printf " Pointer: (Process *) %p\n", $arg0 + printf " Pointer: (Process *) %p\n", $etp_proc printf " *** PROXY process struct *** refer to: \n" - etp-pid2proc-1 $arg0->common.id + etp-pid2proc-1 $etp_proc->common.id etp-process-info $proc else - if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0 - if ($arg0->common.u.alive.reg) + if (*(((Uint32 *) &($etp_proc->state))) & 0x4) == 0 + if ($etp_proc->common.u.alive.reg) printf " Registered name: " - etp-1 $arg0->common.u.alive.reg->name + etp-1 $etp_proc->common.u.alive.reg->name printf "\n" end end - if ($arg0->current) + if ($etp_proc->current) printf " Current function: " - etp-1 $arg0->current[0] + etp-1 $etp_proc->current[0] printf ":" - etp-1 $arg0->current[1] - printf "/%d\n", $arg0->current[2] + etp-1 $etp_proc->current[1] + printf "/%d\n", $etp_proc->current[2] end - if ($arg0->cp) + if ($etp_proc->cp) printf " CP: " - etp-cp-1 $arg0->cp + etp-cp-1 $etp_proc->cp printf "\n" end - if ($arg0->i) + if ($etp_proc->i) printf " I: " - etp-cp-1 $arg0->i + etp-cp-1 $etp_proc->i printf "\n" end - printf " Heap size: %ld\n", $arg0->heap_sz - if ($arg0->old_heap) - printf " Old-heap size: %ld\n", $arg0->old_hend - $arg0->old_heap + printf " Heap size: %ld\n", $etp_proc->heap_sz + if ($etp_proc->old_heap) + printf " Old-heap size: %ld\n", $etp_proc->old_hend - $etp_proc->old_heap end - printf " Mbuf size: %ld\n", $arg0->mbuf_sz + printf " Mbuf size: %ld\n", $etp_proc->mbuf_sz if (etp_smp_compiled) - printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($arg0->msg.len + $arg0->msg_inq.len), $arg0->msg.len, $arg0->msg_inq.len + printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($etp_proc->msg.len + $etp_proc->msg_inq.len), $etp_proc->msg.len, $etp_proc->msg_inq.len else - printf " Msgq len: %d\n", $arg0->msg.len + printf " Msgq len: %d\n", $etp_proc->msg.len end printf " Parent: " - etp-1 $arg0->parent - printf "\n Pointer: (Process *) %p\n", $arg0 + etp-1 $etp_proc->parent + printf "\n Pointer: (Process *) %p\n", $etp_proc end end @@ -1918,57 +1919,58 @@ end define etp-process-memory-info # Args: Process* # - if ((*(((Uint32 *) &(((Process *) $arg0)->state)))) & 0x400000) + set $etp_pmem_proc = ((Process *) $arg0) + if ((*(((Uint32 *) &($etp_pmem_proc->state)))) & 0x400000) set $proxy_process = 1 else set $proxy_process = 0 end printf " " - etp-1 $arg0->common.id - printf ": (Process *) %p ", $arg0 + etp-1 $etp_pmem_proc->common.id + printf ": (Process *) %p ", $etp_pmem_proc if $proxy_process != 0 - printf "(Process *) %p ", $arg0 + printf "(Process *) %p ", $etp_pmem_proc printf " *** PROXY process struct *** refer to next: \n" - etp-pid2proc-1 $arg0->common.id + etp-pid2proc-1 $etp_pmem_proc->common.id printf " -" etp-process-memory-info $proc else - printf " [Heap: %5ld", $arg0->heap_sz - if ($arg0->old_heap) - printf " | %5ld", $arg0->old_hend - $arg0->old_heap + printf " [Heap: %5ld", $etp_pmem_proc->heap_sz + if ($etp_pmem_proc->old_heap) + printf " | %5ld", $etp_pmem_proc->old_hend - $etp_pmem_proc->old_heap else printf " | none " end - printf "] [Mbuf: %5ld", $arg0->mbuf_sz + printf "] [Mbuf: %5ld", $etp_pmem_proc->mbuf_sz if (etp_smp_compiled) - printf " | %3ld (%3ld | %3ld)", ($arg0->msg.len + $arg0->msg_inq.len), $arg0->msg.len, $arg0->msg_inq.len + printf " | %3ld (%3ld | %3ld)", ($etp_pmem_proc->msg.len + $etp_pmem_proc->msg_inq.len), $etp_pmem_proc->msg.len, $etp_pmem_proc->msg_inq.len else - printf " | %3ld", $arg0->msg.len + printf " | %3ld", $etp_pmem_proc->msg.len end printf "] " - if ($arg0->i) + if ($etp_pmem_proc->i) printf " I: " - etp-cp-1 $arg0->i + etp-cp-1 $etp_pmem_proc->i printf " " end - if ($arg0->current) - etp-1 $arg0->current[0] + if ($etp_pmem_proc->current) + etp-1 $etp_pmem_proc->current[0] printf ":" - etp-1 $arg0->current[1] - printf "/%d ", $arg0->current[2] + etp-1 $etp_pmem_proc->current[1] + printf "/%d ", $etp_pmem_proc->current[2] end - if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0 - if ($arg0->common.u.alive.reg) - etp-1 $arg0->common.u.alive.reg->name + if (*(((Uint32 *) &(((Process *) $etp_pmem_proc)->state))) & 0x4) == 0 + if ($etp_pmem_proc->common.u.alive.reg) + etp-1 $etp_pmem_proc->common.u.alive.reg->name printf " " end end - if ($arg0->cp) + if ($etp_pmem_proc->cp) printf " CP: " - etp-cp-1 $arg0->cp + etp-cp-1 $etp_pmem_proc->cp printf " " end printf "\n" @@ -2166,23 +2168,24 @@ define etp-port-info # Args: Port* # printf " Port: " - etp-1 $arg0->common.id - printf "\n Name: %s\n", $arg0->name + set $etp_pinfo_port = ((Port*)$arg0) + etp-1 $etp_pinfo_port->common.id + printf "\n Name: %s\n", $etp_pinfo_port->name printf " State:" - etp-port-state $arg0 + etp-port-state $etp_pinfo_port printf " Scheduler flags:" - etp-port-sched-flags $arg0 - if (*(((Uint32 *) &(((Port *) $arg0)->state))) & 0x5C00) == 0 - if ($arg0->common.u.alive.reg) + etp-port-sched-flags $etp_pinfo_port + if (*(((Uint32 *) &($etp_pinfo_port->state))) & 0x5C00) == 0 + if ($etp_pinfo_port->common.u.alive.reg) printf " Registered name: " - etp-1 $arg0->common.u.alive.reg->name + etp-1 $etp_pinfo_port->common.u.alive.reg->name printf "\n" end end printf " Connected: " - set $connected = *(((Eterm *) &(((Port *) $arg0)->connected))) + set $connected = *(((Eterm *) &(((Port *) $etp_pinfo_port)->connected))) etp-1 $connected - printf "\n Pointer: (Port *) %p\n", $arg0 + printf "\n Pointer: (Port *) %p\n", $etp_pinfo_port end document etp-port-info -- cgit v1.2.3 From e3a07b08709bd7eacae0bcb0353c347475134ad7 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 15 Jun 2016 16:32:16 +0200 Subject: erts: Change local sysname for ETS compressed Yes this is an ugly workaround. One approach for a better solution could be to introduce an internal secret atom tagged as an atom with a unique index, but impossible to find by string hash lookup/insert. --- erts/emulator/beam/external.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 3c002d43a7..beed847578 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2159,12 +2159,23 @@ enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags) return ep; } +/* + * We use this atom as sysname in local pid/port/refs + * for the ETS compressed format (DFLAG_INTERNAL_TAGS). + * + * We used atom '' earlier but that turned out to cause problems + * for buggy erl_interface/ic usage of c-nodes with empty node names. + * A long atom reduces risk of nodes actually called this and the length + * does not matter anyway as it's encoded with atom index (ATOM_INTERNAL_REF2). + */ +#define INTERNAL_LOCAL_SYSNAME am_await_microstate_accounting_modifications + static byte* enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags) { Uint on, os; Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS)) - ? am_Empty : pid_node_name(pid)); + ? INTERNAL_LOCAL_SYSNAME : pid_node_name(pid)); Uint32 creation = pid_creation(pid); byte* tagp = ep++; @@ -2268,7 +2279,7 @@ dec_atom(ErtsDistExternal *edep, byte* ep, Eterm* objp) static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint32 creation) { - if (sysname == am_Empty) /* && DFLAG_INTERNAL_TAGS */ + if (sysname == INTERNAL_LOCAL_SYSNAME) /* && DFLAG_INTERNAL_TAGS */ return erts_this_node; if (sysname == erts_this_node->sysname @@ -2555,7 +2566,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, case EXTERNAL_REF_DEF: { Uint32 *ref_num; Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj)) - ? am_Empty : ref_node_name(obj)); + ? INTERNAL_LOCAL_SYSNAME : ref_node_name(obj)); Uint32 creation = ref_creation(obj); byte* tagp = ep++; @@ -2584,7 +2595,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, case PORT_DEF: case EXTERNAL_PORT_DEF: { Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj)) - ? am_Empty : port_node_name(obj)); + ? INTERNAL_LOCAL_SYSNAME : port_node_name(obj)); Uint32 creation = port_creation(obj); byte* tagp = ep++; -- cgit v1.2.3 From b97f18edd8896e441fc506e289eb04d2de48e5fa Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 15 Jun 2016 16:57:08 +0200 Subject: Fix mach clock usage on Sierra --- erts/emulator/sys/unix/sys_time.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index 60f8decd96..4f26639703 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -219,7 +219,10 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) #endif init_resp->os_monotonic_time_info.resolution = (Uint64) 1000*1000*1000; -#if defined(HAVE_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID) +#if defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID) + init_resp->os_monotonic_time_info.resolution + = mach_clock_getres(&internal_state.r.o.mach.clock.monotonic); +#elif defined(HAVE_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID) { struct timespec ts; if (clock_getres(MONOTONIC_CLOCK_ID, &ts) == 0) { @@ -229,9 +232,6 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_monotonic_time_info.resolution = 1; } } -#elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID) - init_resp->os_monotonic_time_info.resolution - = mach_clock_getres(&internal_state.r.o.mach.clock.monotonic); #endif #ifdef MONOTONIC_CLOCK_ID_STR @@ -379,7 +379,10 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_system_time_info.locked_use = 0; init_resp->os_system_time_info.resolution = (Uint64) 1000*1000*1000; -#if defined(HAVE_CLOCK_GETRES) && defined(WALL_CLOCK_ID) +#if defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(WALL_CLOCK_ID) + init_resp->os_system_time_info.resolution + = mach_clock_getres(&internal_state.r.o.mach.clock.wall); +#elif defined(HAVE_CLOCK_GETRES) && defined(WALL_CLOCK_ID) { struct timespec ts; if (clock_getres(WALL_CLOCK_ID, &ts) == 0) { @@ -389,9 +392,6 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_system_time_info.resolution = 1; } } -#elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(WALL_CLOCK_ID) - init_resp->os_system_time_info.resolution - = mach_clock_getres(&internal_state.r.o.mach.clock.wall); #endif #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) -- cgit v1.2.3 From e0881861bb8d4bf796134f072e67608c6f32b4c6 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 16 Jun 2016 13:13:49 +0200 Subject: Return eafnosupport when not supported --- erts/emulator/drivers/common/inet_drv.c | 247 ++++++++++++++++++-------------- 1 file changed, 139 insertions(+), 108 deletions(-) (limited to 'erts') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 93ea9f5dcb..5ce0e1de9e 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -591,6 +591,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) (((unsigned char*) (s))[1] << 8) | \ (((unsigned char*) (s))[0])) +#ifdef HAVE_SYS_UN_H + /* strnlen doesn't exist everywhere */ static size_t my_strnlen(const char *s, size_t maxlen) { @@ -608,6 +610,8 @@ static int is_nonzero(const char *s, size_t n) return 0; } +#endif + #ifdef VALGRIND # include #else @@ -1301,6 +1305,8 @@ static ErlDrvTermData am_ssl_tls; static ErlDrvTermData am_udp; static ErlDrvTermData am_udp_passive; static ErlDrvTermData am_udp_error; +#endif +#ifdef HAVE_SYS_UN_H static ErlDrvTermData am_local; #endif #ifdef HAVE_SCTP @@ -1328,7 +1334,10 @@ static ErlDrvTermData am_ipv6_v6only; static ErlDrvTermData am_netns; #endif -/* speical errors for bad ports and sequences */ +static char str_eafnosupport[] = "eafnosupport"; +static char str_einval[] = "einval"; + +/* special errors for bad ports and sequences */ #define EXBADPORT "exbadport" #define EXBADSEQ "exbadseq" @@ -3846,6 +3855,8 @@ static int inet_init() #ifdef HAVE_UDP INIT_ATOM(udp_passive); INIT_ATOM(udp_error); +#endif +#ifdef HAVE_SYS_UN_H INIT_ATOM(local); #endif INIT_ATOM(empty_out_q); @@ -3955,74 +3966,88 @@ static int inet_init() /* ** Set an inaddr structure: -** src = [P1,P0,X1,X2,.....] +** *src = [P1,P0,X1,X2,.....] ** dst points to a structure large enugh to keep any kind ** of inaddr. ** *len is set to length of src on call ** and is set to actual length of dst on return -** return NULL on error and ptr after port address on success +** return NULL if ok or ptr to errno string for error */ static char* inet_set_address(int family, inet_address* dst, - char* src, ErlDrvSizeT* len) + char* *src, ErlDrvSizeT* len) { short port; - if ((family == AF_INET) && (*len >= 2+4)) { + switch (family) { + case AF_INET: { + if (*len < 2+4) return str_einval; sys_memzero((char*)dst, sizeof(struct sockaddr_in)); - port = get_int16(src); + port = get_int16(*src); #ifndef NO_SA_LEN dst->sai.sin_len = sizeof(struct sockaddr_in); #endif dst->sai.sin_family = family; dst->sai.sin_port = sock_htons(port); - sys_memcpy(&dst->sai.sin_addr, src+2, 4); + sys_memcpy(&dst->sai.sin_addr, (*src)+2, 4); *len = sizeof(struct sockaddr_in); - return src + 2+4; + *src += 2 + 4; + return NULL; } #if defined(HAVE_IN6) && defined(AF_INET6) - else if ((family == AF_INET6) && (*len >= 2+16)) { + case AF_INET6: { + if (*len < 2+16) return str_einval; sys_memzero((char*)dst, sizeof(struct sockaddr_in6)); - port = get_int16(src); + port = get_int16(*src); #ifndef NO_SA_LEN dst->sai6.sin6_len = sizeof(struct sockaddr_in6); #endif dst->sai6.sin6_family = family; dst->sai6.sin6_port = sock_htons(port); dst->sai6.sin6_flowinfo = 0; /* XXX this may be set as well ?? */ - sys_memcpy(&dst->sai6.sin6_addr, src+2, 16); - *len = sizeof(struct sockaddr_in6); - return src + 2+16; + sys_memcpy(&dst->sai6.sin6_addr, (*src)+2, 16); + *len = sizeof(struct sockaddr_in6); + *src += 2 + 16; + return NULL; } #endif #ifdef HAVE_SYS_UN_H - else if ((family == AF_UNIX) && (*len >= 1)) { - int n = *((unsigned char*)src); - if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) - return NULL; + case AF_UNIX: { + int n; + if (*len == 0) return str_einval; + n = *((unsigned char*)(*src)); /* Length field */ + if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) { + return str_einval; + } sys_memzero((char*)dst, sizeof(struct sockaddr_un)); dst->sal.sun_family = family; - sys_memcpy(dst->sal.sun_path, src+1, n); + sys_memcpy(dst->sal.sun_path, (*src)+1, n); *len = offsetof(struct sockaddr_un, sun_path) + n; - return src + 1 + n; + *src += 1 + n; + return NULL; } #endif - return NULL; + } + return str_eafnosupport; } /* ** Set an inaddr structure, address family comes from source data, ** or from argument if source data specifies constant address. ** -** src = [TAG,P1,P0] when TAG = INET_AF_ANY | INET_AF_LOOPBACK -** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6 | INET_AF_LOCAL +** *src = [TAG,P1,P0] +** when TAG = INET_AF_ANY | INET_AF_LOOPBACK +** *src = [TAG,P1,P0,X1,X2,...] +** when TAG = INET_AF_INET | INET_AF_INET6 | INET_AF_LOCAL +** *src = [TAG,Len,...] +** when TAG = INET_AF_LOCAL */ static char *inet_set_faddress(int family, inet_address* dst, - char *src, ErlDrvSizeT* len) { + char* *src, ErlDrvSizeT* len) { int tag; - if (*len < 1) return NULL; + if (*len < 1) return str_einval; (*len) --; - tag = *(src ++); + tag = *((*src) ++); switch (tag) { case INET_AF_INET: family = AF_INET; @@ -4042,8 +4067,8 @@ static char *inet_set_faddress(int family, inet_address* dst, case INET_AF_LOOPBACK: { int port; - if (*len < 2) return NULL; - port = get_int16(src); + if (*len < 2) return str_einval; + port = get_int16(*src); switch (family) { case AF_INET: { struct in_addr addr; @@ -4055,7 +4080,7 @@ static char *inet_set_faddress(int family, inet_address* dst, addr.s_addr = sock_htonl(INADDR_LOOPBACK); break; default: - return NULL; + return str_einval; } sys_memzero((char*)dst, sizeof(struct sockaddr_in)); #ifndef NO_SA_LEN @@ -4077,7 +4102,7 @@ static char *inet_set_faddress(int family, inet_address* dst, paddr = &in6addr_loopback; break; default: - return NULL; + return str_einval; } sys_memzero((char*)dst, sizeof(struct sockaddr_in6)); #ifndef NO_SA_LEN @@ -4091,12 +4116,13 @@ static char *inet_set_faddress(int family, inet_address* dst, } break; # endif default: - return NULL; + return str_einval; } - return src + 2; + *src += 2; + return NULL; } break; default: - return NULL; + return str_eafnosupport; } return inet_set_address(family, dst, src, len); } @@ -6691,7 +6717,6 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) case SCTP_OPT_SET_PEER_PRIMARY_ADDR: { ErlDrvSizeT alen; - char *after; CHKLEN(curr, ASSOC_ID_LEN); /* XXX: These 2 opts have isomorphic value data structures, @@ -6702,12 +6727,9 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) /* Fill in "arg.prim.sspp_addr": */ alen = ptr + len - curr; - after = inet_set_faddress(desc->sfamily, - (inet_address*) (&arg.prim.sspp_addr), - curr, &alen); - if (after == NULL) - return -1; - curr = after; + if (inet_set_faddress + (desc->sfamily, (inet_address*) (&arg.prim.sspp_addr), + &curr, &alen) != NULL) return -1; proto = IPPROTO_SCTP; if (eopt == SCTP_OPT_PRIMARY_ADDR) @@ -6733,7 +6755,6 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) case SCTP_OPT_PEER_ADDR_PARAMS: { ErlDrvSizeT alen; - char *after; # ifdef HAVE_STRUCT_SCTP_PADDRPARAMS_SPP_FLAGS int eflags, cflags, hb_enable, hb_disable, pmtud_enable, pmtud_disable; @@ -6748,12 +6769,9 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) /* Fill in "pap.spp_address": */ alen = ptr + len - curr; - after = inet_set_faddress(desc->sfamily, - (inet_address*) (&arg.pap.spp_address), - curr, &alen); - if (after == NULL) - return -1; - curr = after; + if (inet_set_faddress + (desc->sfamily, (inet_address*) (&arg.pap.spp_address), + &curr, &alen) != NULL) return -1; CHKLEN(curr, 4 + 2 + 3*4); @@ -7740,7 +7758,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, struct sctp_paddrparams ap; unsigned int sz = sizeof(ap); int n; - char *after; + char *before, *xerror; ErlDrvSizeT alen; if (buflen < ASSOC_ID_LEN) RETURN_ERROR(spec, -EINVAL); @@ -7748,13 +7766,22 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, buf += ASSOC_ID_LEN; buflen -= ASSOC_ID_LEN; alen = buflen; - after = inet_set_faddress(desc->sfamily, - (inet_address*) (&ap.spp_address), - buf, &alen); - if (after == NULL) RETURN_ERROR(spec, -EINVAL); - buflen -= after - buf; - buf = after; - + before = buf; + xerror = + inet_set_faddress + (desc->sfamily, (inet_address*) (&ap.spp_address), + &buf, &alen); + if (xerror != NULL) { +#ifdef EAFNOSUPPORT + if (xerror == str_eafnosupport) { + RETURN_ERROR(spec, -EAFNOSUPPORT); + } +#else + RETURN_ERROR(spec, -EINVAL); +#endif + } + buflen -= buf - before; + if (sock_getopt(desc->s, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &ap, &sz) < 0) continue; /* Fill in the response: */ @@ -7980,7 +8007,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, { struct sctp_paddrinfo pai; unsigned int sz = sizeof(pai); - char *after; + char *before, *xerror; ErlDrvSizeT alen; if (buflen < ASSOC_ID_LEN) RETURN_ERROR(spec, -EINVAL); @@ -7988,13 +8015,22 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, buf += ASSOC_ID_LEN; buflen -= ASSOC_ID_LEN; alen = buflen; - after = inet_set_faddress(desc->sfamily, - (inet_address*) (&pai.spinfo_address), - buf, &alen); - if (after == NULL) RETURN_ERROR(spec, -EINVAL); - buflen -= after - buf; - buf = after; - + before = buf; + xerror = + inet_set_faddress + (desc->sfamily, (inet_address*) (&pai.spinfo_address), + &buf, &alen); + if (xerror != NULL) { +#ifdef EAFNOSUPPORT + if (xerror == str_eafnosupport) { + RETURN_ERROR(spec, -EAFNOSUPPORT); + } +#else + RETURN_ERROR(spec, -EINVAL); +#endif + } + buflen -= buf - before; + if (sock_getopt(desc->s, IPPROTO_SCTP, SCTP_GET_PEER_ADDR_INFO, &pai, &sz) < 0) continue; /* Fill in the response: */ @@ -8511,15 +8547,16 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } case INET_REQ_SETPEER: { /* set fake peername Port Address */ + char *xerror; if (len == 0) { desc->peer_ptr = NULL; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } else if (len < 2) return ctl_error(EINVAL, rbuf, rsize); - else if (inet_set_faddress - (desc->sfamily, &desc->peer_addr, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + else if ((xerror = inet_set_faddress + (desc->sfamily, &desc->peer_addr, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); else { desc->peer_ptr = &desc->peer_addr; desc->peer_addr_len = (SOCKLEN_T) len; @@ -8585,15 +8622,16 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } case INET_REQ_SETNAME: { /* set fake sockname Port Address */ + char *xerror; if (len == 0) { desc->name_ptr = NULL; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } else if (len < 2) return ctl_error(EINVAL, rbuf, rsize); - else if (inet_set_faddress - (desc->sfamily, &desc->name_addr, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + else if ((xerror = inet_set_faddress + (desc->sfamily, &desc->name_addr, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); else { desc->name_ptr = &desc->name_addr; desc->name_addr_len = (SOCKLEN_T) len; @@ -8602,7 +8640,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } case INET_REQ_BIND: { /* bind socket */ - char tbuf[2]; + char tbuf[2], *xerror; inet_address local; int port; @@ -8613,8 +8651,9 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (desc->state != INET_STATE_OPEN) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (inet_set_faddress(desc->sfamily, &local, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + if ((xerror = inet_set_faddress + (desc->sfamily, &local, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); if (IS_SOCKET_ERROR(sock_bind(desc->s,(struct sockaddr*) &local, len))) return ctl_error(sock_errno(), rbuf, rsize); @@ -9081,10 +9120,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, case INET_AF_INET6: domain = AF_INET6; break; -#else - case INET_AF_INET6: - return ctl_xerror("eafnosupport", rbuf, rsize); - break; #endif #ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: @@ -9092,7 +9127,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, break; #endif default: - return ctl_error(EINVAL, rbuf, rsize); + return ctl_xerror(str_eafnosupport, rbuf, rsize); } if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize); return inet_ctl_open(INETP(desc), domain, SOCK_STREAM, rbuf, rsize); @@ -9112,10 +9147,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, case INET_AF_INET6: domain = AF_INET6; break; -#else - case INET_AF_INET6: - return ctl_xerror("eafnosupport", rbuf, rsize); - break; #endif #ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: @@ -9123,7 +9154,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, break; #endif default: - return ctl_error(EINVAL, rbuf, rsize); + return ctl_xerror(str_eafnosupport, rbuf, rsize); } if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize); @@ -9156,7 +9187,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, case INET_REQ_CONNECT: { /* do async connect */ int code; - char tbuf[2]; + char tbuf[2], *xerror; unsigned timeout; DEBUGF(("tcp_inet_ctl(%ld): CONNECT\r\n", (long)desc->inet.port)); @@ -9173,9 +9204,9 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, timeout = get_int32(buf); buf += 4; len -= 4; - if (inet_set_faddress - (desc->inet.sfamily, &desc->inet.remote, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + if ((xerror = inet_set_faddress + (desc->inet.sfamily, &desc->inet.remote, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); code = sock_connect(desc->inet.s, (struct sockaddr*) &desc->inet.remote, len); @@ -10955,16 +10986,12 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, case INET_AF_INET: af = AF_INET; break; #if defined(HAVE_IN6) && defined(AF_INET6) case INET_AF_INET6: af = AF_INET6; break; -#else - case INET_AF_INET6: - return ctl_xerror("eafnosupport", rbuf, rsize); - break; #endif #ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: af = AF_UNIX; break; #endif default: - return ctl_error(EINVAL, rbuf, rsize); + return ctl_xerror(str_eafnosupport, rbuf, rsize); } switch (buf[1]) { case INET_TYPE_STREAM: type = SOCK_STREAM; break; @@ -11008,16 +11035,12 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, case INET_AF_INET: af = AF_INET; break; #if defined(HAVE_IN6) && defined(AF_INET6) case INET_AF_INET6: af = AF_INET6; break; -#else - case INET_AF_INET6: - return ctl_xerror("eafnosupport", rbuf, rsize); - break; #endif #ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: af = AF_UNIX; break; #endif default: - return ctl_error(EINVAL, rbuf, rsize); + return ctl_xerror(str_eafnosupport, rbuf, rsize); } switch (buf[1]) { case INET_TYPE_STREAM: type = SOCK_STREAM; break; @@ -11085,6 +11108,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, #ifdef HAVE_SCTP if (IS_SCTP(desc)) { inet_address remote; + char *xerror; if (IS_CONNECTING(desc)) return ctl_error(EINVAL, rbuf, rsize); @@ -11096,8 +11120,9 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, /* For SCTP, we do not set the peer's addr in desc->remote, as multiple peers are possible: */ - if (inet_set_faddress(desc->sfamily, &remote, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + if ((xerror = inet_set_faddress + (desc->sfamily, &remote, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); sock_select(desc, FD_CONNECT, 1); code = sock_connect(desc->s, &remote.sa, len); @@ -11133,12 +11158,13 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, else if (len < 6) return ctl_error(EINVAL, rbuf, rsize); else { + char *xerror; /* Ignore timeout */ buf += 4; len -= 4; - if (inet_set_faddress - (desc->sfamily, &desc->remote, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + if ((xerror = inet_set_faddress + (desc->sfamily, &desc->remote, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); code = sock_connect(desc->s, (struct sockaddr*) &desc->remote, len); @@ -11202,11 +11228,13 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, while (curr < buf+len) { + char *xerror; /* List item format: see "inet_set_faddress": */ ErlDrvSizeT alen = buf + len - curr; - curr = inet_set_faddress(desc->sfamily, &addr, curr, &alen); - if (curr == NULL) - return ctl_error(EINVAL, rbuf, rsize); + xerror = inet_set_faddress + (desc->sfamily, &addr, &curr, &alen); + if (xerror != NULL) + return ctl_xerror(xerror, rbuf, rsize); /* Invoke the call: */ if (p_sctp_bindx(desc->s, (struct sockaddr *)&addr, 1, @@ -11325,6 +11353,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) inet_descriptor* desc = INETP(udesc); char* ptr = buf; char* qtr; + char* xerror; ErlDrvSizeT sz; int code; inet_address other; @@ -11387,9 +11416,10 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) /* UDP socket. Even if it is connected, there is an address prefix here -- ignored for connected sockets: */ sz = len; - qtr = inet_set_faddress(desc->sfamily, &other, ptr, &sz); - if (qtr == NULL) { - inet_reply_error(desc, EINVAL); + qtr = ptr; + xerror = inet_set_faddress(desc->sfamily, &other, &qtr, &sz); + if (xerror != NULL) { + inet_reply_error_am(desc, driver_mk_atom(xerror)); return; } len -= (qtr - ptr); @@ -11975,7 +12005,7 @@ void erts_sock_close(erts_sock_t socket) int erts_sock_connect(erts_sock_t socket, byte *ip_addr, int len, Uint16 port) { SOCKET s = (SOCKET) socket; - char buf[2 + 4]; + char buf[2 + 4], *p; ErlDrvSizeT blen = 6; inet_address addr; @@ -11985,7 +12015,8 @@ int erts_sock_connect(erts_sock_t socket, byte *ip_addr, int len, Uint16 port) put_int16(port, buf); memcpy((void *) (buf + 2), (void *) ip_addr, 4); - if (!inet_set_address(AF_INET, &addr, buf, &blen)) + p = buf; + if (inet_set_address(AF_INET, &addr, &p, &blen) != NULL) return 0; if (IS_SOCKET_ERROR -- cgit v1.2.3 From 615b79a01706033e1ef0d78f020ebbb47fc80b86 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 16 Jun 2016 17:33:28 +0200 Subject: Add documentation about dirty job type --- erts/doc/src/erl.xml | 19 ++++++++++++++++++- erts/doc/src/erl_nif.xml | 20 +++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 7b90a1ccca..436c2c57e1 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -915,6 +915,13 @@ number of dirty CPU schedulers online can be changed at run time via erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline).

+

+ The amount of dirty CPU schedulers is limited by the amount of + normal schedulers in order to limit the effect on processes + executing on ordinary schedulers. If the amount of dirty CPU + schedulers was allowed to be unlimited, dirty CPU bound jobs would + potentially starve normal jobs. +

This option is ignored if the emulator doesn't have threading support enabled. Currently, this option is experimental and is supported only if the emulator was configured and built with support for dirty schedulers @@ -944,7 +951,7 @@ enabled (it's disabled by default).

- +

Sets the number of dirty I/O scheduler threads to create when threading support has been enabled. The valid range is 0-1024. By default, the number @@ -952,6 +959,16 @@ threads in the async thread pool .

+

+ The amount of dirty IO schedulers is not limited by the amount of + normal schedulers like the amount of + dirty CPU schedulers. This since only I/O bound work is + expected to execute on dirty I/O schedulers. I/O bound jobs are + expected to either block waiting for I/O, and/or spend a limited + amount of time moving data. However, if the user should schedule CPU + bound jobs on dirty I/O schedulers, these jobs might starve ordinary + jobs executing on ordinary schedulers. +

This option is ignored if the emulator doesn't have threading support enabled. Currently, this option is experimental and is supported only if the emulator was configured and built with support for dirty schedulers diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 8b02b3bae1..c9b2092c17 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -442,6 +442,24 @@ ok dirty NIF has completed.

+

+ It is important to classify the dirty job correct. An I/O bound + job should be classified as such and execute on a dirty I/O + scheduler, and a CPU bound job should be classified as such and + execute on a dirty CPU scheduler. If you classify CPU bound jobs + as I/O bound jobs, dirty I/O schedulers might starve ordinary + schedulers. For more information see the documentation + of the erl command line arguments + +SDcpu, and + +SDio as well + as enif_schedule_nif, + and ErlNifFunc. + A job that alternates between I/O bound and CPU bound can + be reclassified and rescheduled using enif_schedule_nif + so that it exectutes on the correct type of dirty scheduler + at all times. +

+

Currently known issues that are planned to be fixed:

@@ -1702,7 +1720,7 @@ enif_map_iterator_destroy(env, &iter); be converted to an atom, enif_schedule_nif returns a badarg exception.

The flags argument must be set to 0 for a regular NIF, or if the emulator was built the experimental dirty scheduler support enabled, flags can be set to either ERL_NIF_DIRTY_JOB_CPU_BOUND - if the job is expected to be primarily CPU-bound, or ERL_NIF_DIRTY_JOB_IO_BOUND for jobs that will + if the job is expected to be CPU-bound, or ERL_NIF_DIRTY_JOB_IO_BOUND for jobs that will be I/O-bound. If dirty scheduler threads are not available in the emulator, a try to schedule such a job will result in a badarg exception.

-- cgit v1.2.3 From 0cf97f4d5c6a3c30dd7d8e200188001442b3822c Mon Sep 17 00:00:00 2001 From: Zandra Hird Date: Thu, 16 Jun 2016 14:59:31 +0200 Subject: Remove unused Cookie from ControlMessage in the dist protocol doc The Cookie was removed a long time ago, but the documentation was not updated accordingly. --- erts/doc/src/erl_dist_protocol.xml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml index f9fa981d9a..25f601a235 100644 --- a/erts/doc/src/erl_dist_protocol.xml +++ b/erts/doc/src/erl_dist_protocol.xml @@ -930,11 +930,14 @@ DiB == gen_digest(ChA,ICA) ? SEND

- {2, Cookie, ToPid} + {2, Unused, ToPid}

Note followed by Message

+

+ Unused is kept for backward compatibility +

EXIT @@ -961,11 +964,14 @@ DiB == gen_digest(ChA,ICA) ? REG_SEND

- {6, FromPid, Cookie, ToName} + {6, FromPid, Unused, ToName}

Note followed by Message

+

+ Unused is kept for backward compatibility +

GROUP_LEADER @@ -991,11 +997,14 @@ DiB == gen_digest(ChA,ICA) ? SEND_TT

- {12, Cookie, ToPid, TraceToken} + {12, Unused, ToPid, TraceToken}

Note followed by Message

+

+ Unused is kept for backward compatibility +

EXIT_TT @@ -1008,11 +1017,14 @@ DiB == gen_digest(ChA,ICA) ? REG_SEND_TT

- {16, FromPid, Cookie, ToName, TraceToken} + {16, FromPid, Unused, ToName, TraceToken}

Note followed by Message

+

+ Unused is kept for backward compatibility +

EXIT2_TT -- cgit v1.2.3 From c9ad0c9fed1d33891f5fa8a4c4511de48417ec10 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 17 Jun 2016 11:53:29 +0200 Subject: Minor reorganization of dirty NIF documentation --- erts/doc/src/erl.xml | 4 +--- erts/doc/src/erl_nif.xml | 55 +++++++++++++++++++++++++----------------------- 2 files changed, 30 insertions(+), 29 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 436c2c57e1..37387f2c59 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -963,9 +963,7 @@ The amount of dirty IO schedulers is not limited by the amount of normal schedulers like the amount of dirty CPU schedulers. This since only I/O bound work is - expected to execute on dirty I/O schedulers. I/O bound jobs are - expected to either block waiting for I/O, and/or spend a limited - amount of time moving data. However, if the user should schedule CPU + expected to execute on dirty I/O schedulers. If the user should schedule CPU bound jobs on dirty I/O schedulers, these jobs might starve ordinary jobs executing on ordinary schedulers.

diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index c9b2092c17..f3921f1922 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -385,18 +385,39 @@ ok

A NIF that cannot be split and cannot execute in a millisecond or less is called a "dirty NIF" because it performs work that the - Erlang runtime cannot handle cleanly. Applications that make use - of such functions must indicate to the runtime that the functions - are dirty so they can be handled specially. To schedule a dirty - NIF for execution, the appropriate flags value can be set for the - NIF in its ErlNifFunc + ordinary schedulers of the Erlang runtime system cannot handle cleanly. + Applications that make use of such functions must indicate to the + runtime that the functions are dirty so they can be handled + specially. This is handled by executing dirty jobs on a separate + set of schedulers called dirty schedulers. A dirty NIF executing + on a dirty scheduler does not have the same duration restriction + as a normal NIF. +

+ +

+ It is important to classify the dirty job correct. An I/O bound + job should be classified as such, and a CPU bound job should be + classified as such. If you should classify CPU bound jobs + as I/O bound jobs, dirty I/O schedulers might starve ordinary + schedulers. I/O bound jobs are expected to either block waiting + for I/O, and/or spend a limited amount of time moving data. +

+ +

+ To schedule a dirty NIF for execution, the appropriate + flags value can be set for the NIF in its + ErlNifFunc entry, or the application can call enif_schedule_nif, passing to it a pointer to the dirty NIF to be executed and indicating with the flags argument whether it expects the - operation to be CPU-bound or I/O-bound. A dirty NIF executing - on a dirty scheduler does not have the same duration restriction - as a normal NIF. + operation to be CPU-bound or I/O-bound. A job that alternates + between I/O bound and CPU bound can be reclassified and + rescheduled using enif_schedule_nif so that it executes on + the correct type of dirty scheduler at all times. For more + information see the documentation of the erl command line + arguments +SDcpu, + and +SDio.

@@ -442,24 +463,6 @@ ok dirty NIF has completed.

-

- It is important to classify the dirty job correct. An I/O bound - job should be classified as such and execute on a dirty I/O - scheduler, and a CPU bound job should be classified as such and - execute on a dirty CPU scheduler. If you classify CPU bound jobs - as I/O bound jobs, dirty I/O schedulers might starve ordinary - schedulers. For more information see the documentation - of the erl command line arguments - +SDcpu, and - +SDio as well - as enif_schedule_nif, - and ErlNifFunc. - A job that alternates between I/O bound and CPU bound can - be reclassified and rescheduled using enif_schedule_nif - so that it exectutes on the correct type of dirty scheduler - at all times. -

-

Currently known issues that are planned to be fixed:

-- cgit v1.2.3 From 1ef3ced2b55fd226b8f0f68b07cae6c298ea729e Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Mon, 20 Jun 2016 11:53:41 +0200 Subject: Update preloaded modules --- erts/preloaded/ebin/erl_prim_loader.beam | Bin 55732 -> 55752 bytes erts/preloaded/ebin/erl_tracer.beam | Bin 2200 -> 2200 bytes erts/preloaded/ebin/erlang.beam | Bin 104648 -> 104816 bytes erts/preloaded/ebin/erts_code_purger.beam | Bin 8696 -> 8696 bytes erts/preloaded/ebin/erts_internal.beam | Bin 10536 -> 10536 bytes erts/preloaded/ebin/init.beam | Bin 50052 -> 50048 bytes erts/preloaded/ebin/otp_ring0.beam | Bin 1444 -> 1444 bytes erts/preloaded/ebin/prim_eval.beam | Bin 1312 -> 1312 bytes erts/preloaded/ebin/prim_file.beam | Bin 44764 -> 44764 bytes erts/preloaded/ebin/prim_inet.beam | Bin 76348 -> 76236 bytes erts/preloaded/ebin/prim_zip.beam | Bin 23152 -> 23152 bytes erts/preloaded/ebin/zlib.beam | Bin 14136 -> 14136 bytes 12 files changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam index 66e443f396..796cbd74c5 100644 Binary files a/erts/preloaded/ebin/erl_prim_loader.beam and b/erts/preloaded/ebin/erl_prim_loader.beam differ diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam index 22286ed221..4406a82a36 100644 Binary files a/erts/preloaded/ebin/erl_tracer.beam and b/erts/preloaded/ebin/erl_tracer.beam differ diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index cde8c9ab72..b62da04bfd 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam index b1da0aa861..a0da864824 100644 Binary files a/erts/preloaded/ebin/erts_code_purger.beam and b/erts/preloaded/ebin/erts_code_purger.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index cc4f3dbdaf..d897c8e92f 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index 6fc95b914e..b856bff4fe 100644 Binary files a/erts/preloaded/ebin/init.beam and b/erts/preloaded/ebin/init.beam differ diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam index f893b9c181..b601c048b3 100644 Binary files a/erts/preloaded/ebin/otp_ring0.beam and b/erts/preloaded/ebin/otp_ring0.beam differ diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam index e4ce601c03..77909b01f0 100644 Binary files a/erts/preloaded/ebin/prim_eval.beam and b/erts/preloaded/ebin/prim_eval.beam differ diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index babba3081f..5bbbaf14d5 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index fdf8bbf4e2..fba03d52bd 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam index e1faca7d96..6afeb454d6 100644 Binary files a/erts/preloaded/ebin/prim_zip.beam and b/erts/preloaded/ebin/prim_zip.beam differ diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 5d3cbc1b36..4c48742344 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ -- cgit v1.2.3 From 9f779819f6bda734c5953468f77987b73943de35 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 20 Jun 2016 17:33:13 +0200 Subject: erts: Fix spawn driver when env size is above pipe capacity Symptom: open_port() succeeds, port program never starts but instead seem to terminate with exit_status 150 (EINVAL+128). When: If the command payload buffer (including the entire environment) for the port program is between 1 to 4 bytes above the pipe capacity, which is 65536 bytes on newer Linux, but can be as low as 4096 on older 32-bit Linux. Since: OTP-19.0-rc1. --- erts/emulator/sys/unix/sys_drivers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index ac39b8a389..812112fb91 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -762,7 +762,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, } } - if (res < buffsz) { + if (res < (buffsz + sizeof(buffsz))) { /* we only wrote part of the command payload. Enqueue the rest. */ for (i = 0; i < iov_len; i++) { driver_enq(port_num, io_vector[i].iov_base, io_vector[i].iov_len); -- cgit v1.2.3 From 6e51c6d19612d03abc81b86bb70b8d7da678ce5d Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 21 Jun 2016 15:12:41 +0200 Subject: Prepare release --- erts/doc/src/notes.xml | 736 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 736 insertions(+) (limited to 'erts') diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 7501ccd9ce..3c3129d543 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -32,6 +32,742 @@

This document describes the changes made to the ERTS application.

+
Erts 8.0 + +
Fixed Bugs and Malfunctions + + +

The handling of on_load functions has been + improved. The major improvement is that if a code upgrade + fails because the on_load function fails, the + previous version of the module will now be retained.

+

+ Own Id: OTP-12593

+
+ +

is_builtin(erlang, apply, 3) will now return + true.

+

+ Own Id: OTP-13034

+
+ +

+ Fix enif_get_list_length to return false if list + is improper or have length larger than UINT_MAX + (did return true and an incorrect length value).

+

+ Own Id: OTP-13288 Aux Id: PR913

+
+ +

+ Cleanup hipe signal handling code for x86 and make it + more portable.

+

+ Own Id: OTP-13341 Aux Id: PR951

+
+ +

+ Make file:datasync use fsync instead of fdatasync on Mac + OSX.

+

+ Own Id: OTP-13411

+
+ +

+ Make sure to create a crash dump when running out of + memory. This was accidentally removed in the erts-7.3 + release.

+

+ Own Id: OTP-13419

+
+ +

+ A bug has been fixed where if erlang was started +B on a + unix platform it would be killed by a SIGUSR2 signal when + creating a crash dump.

+

+ Own Id: OTP-13425

+
+ +

+ Fix race between process_flag(trap_exit,true) and + a received exit signal.

+

+ A process could terminate due to exit signal even though + process_flag(trap_exit,true) had returned. A very + specific timing between call to process_flag/2 and + exit signal from another scheduler was required for this + to happen.

+

+ Own Id: OTP-13452

+
+ +

Don't search for non-existing Map keys twice

+

For maps:get/2,3 and maps:find/2, + searching for an immediate key, e.g. an atom, in a small + map, the search was performed twice if the key did not + exist.

+

+ Own Id: OTP-13459

+
+ +

+ When an abnormally large distribution message is about to + be sent, the VM has been changed to create a crash dump + instead of a core dump.

+

+ Own Id: OTP-13474

+
+ +

+ Fix erlang:process_info/2 type specification

+

+ Own Id: OTP-13485 Aux Id: ERL-123

+
+ +

+ Fix bug in open_port/2 with option {args, + List}. A vm crash could be caused by an improper + List.

+

+ Own Id: OTP-13489 Aux Id: ERL-127

+
+ +

+ Fixed a race-condition bug where the emulator could crash + when erlang:system_profile/1,2 was enabled and a + process had to be re-scheduled during termination.

+

+ Own Id: OTP-13494 Aux Id: ERL-126

+
+ +

+ Fixed bugs where the reduction counter was not handled + correct.

+

+ Own Id: OTP-13512

+
+ +

+ Fixed typo in description of the EPMD_DUMP_REQ + response.

+

+ Own Id: OTP-13517

+
+ +

+ Fixed a bug where a process flagged as sensitive would + sometimes record its save_calls when it shouldn't.

+

+ Own Id: OTP-13540

+
+ +

+ Update configure scripts to not use hard-coded path for + /bin/pwd and /bin/rm.

+

+ Own Id: OTP-13562

+
+ +

+ When passing a larger binary than the outputv callback of + a linked-in driver can handle in one io vector slot, the + binary is now split into multiple slots in the io vector. + This change only effects system where the max size of an + io vector slot is smaller then the word size of the + system (e.g. Windows).

+

+ This change means that it is now possible on Windows to + send binaries that are larger than 4GB to port_command, + which is what is used for file:write, gen_tcp:send etc.

+

+ Own Id: OTP-13628

+
+ +

+ Workaround of Maps output in crashdumps. Currently the + atom 'undefined' is generated instead of Map data if a + Map type is encountered during crash.

+

+ Own Id: OTP-13657

+
+
+
+ + +
Improvements and New Features + + +

+ The tracing support has been extended to allow a tracer module to be the + trace event handler instead of a process or port. The + tracer module + makes it possible for trace tools to filter or manipulate + trace event data without the trace event first having to + be copied from the traced process or port.

+

+ With the introduction of this feature, + erlang:trace(all|existing, _, _) now also returns + the tracer process as part of the number of processes on + which tracing is enabled. The is incompatible with the + previous releases.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-10267

+
+ +

+ Introduce LTTng tracing of Erlang Runtime System

+

+ For LTTng to be enabled OTP needs to be built with + configure option --with-dynamic-trace=lttng.

+

+ This feature introduces tracepoints for schedulers, + drivers, memory carriers, memory and async thread pool.

+

For a list of all tracepoints, see Runtime Tools User's + Guide .

+

+ Own Id: OTP-10282

+
+ +

+ Make it possible to monitor/demonitor ports using the + erlang:monitor/2 API. + The process and port information functions have also been + updated to include information about monitors from + processes to ports.

+

+ Own Id: OTP-11384

+
+ +

+ Add microstate accounting

+

+ Microstate accounting is a way to track which state the + different threads within ERTS are in. The main usage area + is to pin point performance bottlenecks by checking which + states the threads are in and then from there figuring + out why and where to optimize.

+

+ Since checking whether microstate accounting is on or off + is relatively expensive only a few of the states are + enabled by default and more states can be enabled through + configure.

+

+ There is a convenience module called msacc that has been + added to runtime_tools that can assist in gathering and + interpreting the data from Microstate accounting.

+

+ For more information see erlang:statistics(microstate_accounting, + _) and the msacc module in + runtime_tools.

+

+ Own Id: OTP-12345

+
+ +

+ The port of Erlang/OTP to the real-time operating system + OSE has been removed.

+

+ Own Id: OTP-12573

+
+ +

+ Sharing preserved copy for messages and exit signals

+

+ Enable sharing preserved copy with configure option + --enable-sharing-preserving. This will preserve + sharing, within the process, when communication with + other processes in the Erlang node. There is a trade-off, + the copy is more costly but this cost can be reclaimed if + there is a lot of sharing in the message. In addition + literals will not be copied in a send except during a + purge phase of the module where the literals are located. + This feature is considered experimental in 19.0.

+

+ Own Id: OTP-12590 Aux Id: OTP-10251

+
+ +

+ Halfword BEAM has been removed.

+

+ Own Id: OTP-12883

+
+ +

+ Added os:perf_counter/1.

+

+ The perf_counter is a very very cheap and high resolution + timer that can be used to timestamp system events. It + does not have monoticity guarantees, but should on most + OS's expose a monotonous time.

+

+ Own Id: OTP-12908

+
+ +

+ Support for a fragmented young heap generation. That is, + the young heap generation can consist of multiple non + continuous memory areas. The main reason for this change + is to avoid extra copying of messages that could not be + allocated directly on the receivers heap.

+

+ Own Id: OTP-13047

+
+ +

+ Erlang linked-in driver can now force the call to + open_port to block until a call to erl_drv_init_ack is + made inside the driver. This is useful when you want to + do some asynchronous initialization, for example getting + configuration from a pipe, and you want the initial + open_port call to fail if the configuration is incomplete + or wrong. See the erl_driver documentation for more + details on the API.

+

+ Own Id: OTP-13086

+
+ +

+ Erlang linked-in drivers can now set their own pids as + seen in erlang:port_info/1 by using the + erl_drv_set_pid function. For more details see the + erl_driver documentation.

+

+ Own Id: OTP-13087

+
+ +

+ The functionality behind erlang:open_port/2 when + called with spawn or spawn_executable has been redone so + that the forking of the new program is done in a separate + process called erl_child_setup. This allows for a much + more robust implementation that uses less memory and does + not block the entire emulator if the program to be + started is on an un-accessible NFS. Benchmarks have shown + this approach to be about 3-5 times as fast as the old + approach where the fork/vfork was done by erts. This is a + pure stability and performance fix, however some error + messages may have changed, which is why it is marked as a + backwards incompatible change.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13088

+
+ +

Improved yielding strategy in the implementation of + the following native functions:

+ erlang:binary_to_list/1 + erlang:binary_to_list/3 + erlang:bitstring_to_list/1 + erlang:list_to_binary/1 + erlang:iolist_to_binary/1 + erlang:list_to_bitstring/1 + binary:list_to_bin/1

This + in order to improve performance of these functions.

+

+ Own Id: OTP-13096

+
+ +

+ All garbage collections of processes now bump reductions. + Also the amount of reductions bumped when garbage + collecting has been adjusted. It now better corresponds + to the amount of work performed. This in order to improve + the real time characteristics of the system.

+

+ Own Id: OTP-13097

+
+ +

New functions that can load multiple modules at once + have been added to the 'code' module. The + functions are code:atomic_load/1, + code:prepare_loading/1, + code:finish_loading/1, and + code:ensure_modules_loaded/1.

+

+ Own Id: OTP-13111

+
+ +

The -boot_var option for erl now only + supports a single key and single value (as documented). + The option used to allow multiple key/value pairs, but + that behavior was undocumented.

+

The function erl_prim_loader:start/3 has been + removed. Its documentation has also been removed.

+

The undocumented and unsupported function + erl_prim_loader:get_files/2 has been removed.

+

+ Own Id: OTP-13112

+
+ +

+ Low level BIF erlang:purge_module/1 is made more + robust against incorrect use. Lingering processes that + still refer the old code are now killed before the module + is purged to prevent fatal VM behavior.

+

+ Own Id: OTP-13122

+
+ +

Improved dirty scheduler implementation. For more + information see the NIF + documentation.

The + dirty scheduler support is still + experimental.

The support + for determining whether dirty NIF support exist or not at + compile time using the C preprocessor macro + ERL_NIF_DIRTY_SCHEDULER_SUPPORT has been + removed.

The + enif_is_on_dirty_scheduler() function has been + removed. Use enif_thread_type() + instead.

+

+ Own Id: OTP-13123

+
+ +

+ Various optimizations done to process dictionary access.

+

+ Own Id: OTP-13167

+
+ +

+ Added max_heap_size process flag. max_heap_size allows + the user to limit the maximum heap used by a process. See + erlang:process_flag + for more details.

+

+ Own Id: OTP-13174

+
+ +

+ Allow dynamic drivers and NIF libraries to be built with + gcc option -fvisibility=hidden for faster loading + and more optimized code.

+

+ Own Id: OTP-13227

+
+ +

+ Add erlang:process_info(Pid, + garbage_collection_info) which returns extended + garbage_collection information. For more details see the + documentation.

+

+ Own Id: OTP-13265

+
+ +

+ The functions erlang:list_to_integer/1 and + string:to_integer/1 have been optimized for large + inputs.

+

+ Own Id: OTP-13293

+
+ +

+ Improved memory allocation strategy for hipe native code + on x86_64 (amd64) architectures by reserving enough low + virtual address space needed for the HiPE/AMD64 small + code model. The default virtual address area for hipe + code is set to 512Mb, but can be changed with emulator + flag +MXscs.

+

+ Own Id: OTP-13359

+
+ +

+ Introduction of configurable management of data referred + to by the message queue of a process. Each process can be + configured individually.

+

+ It is now possible to configure the message queue of a + process, so that all data referred by it will be kept + outside of the heap, and by this prevent this data from + being part of garbage collections.

+

+ For more information see the documentation of process_flag(message_queue_data, + MQD).

+

+ Own Id: OTP-13366 Aux Id: OTP-13047

+
+ +

+ Processes now yield when scanning large message queues + and not finding a matching message. This in order to + improve real time characteristics.

+

+ Own Id: OTP-13401

+
+ +

+ Optimized an erts internal function that is used to + traverse erlang terms. The internal function was mainly + used by term_to_binary and comparison of terms. + Benchmarks have shown up to a 10% performance increase in + those functions after the optimization.

+

+ Own Id: OTP-13440

+
+ +

+ Add the following NIF API functions:

+

+ enif_cpu_time + enif_now_time + enif_make_unique_integer + enif_is_process_alive + enif_is_port_alive + enif_term_to_binary + enif_binary_to_term + enif_port_command +

+

+ for details of what each function does, see the erl_nif + documentation.

+

+ Own Id: OTP-13442

+
+ +

+ Optimize '++' operator and lists:append/2 + by using a single pass to build a new list while checking + for properness.

+

+ Own Id: OTP-13487

+
+ +

+ Handle terms (pids,ports and refs) from nodes with a + 'creation' value larger than 3. This is a preparation of + the distribution protocol to allow OTP 19 nodes to + correctly communicate with future nodes (20 or higher). + The 'creation' value differentiates different + incarnations of the same node (name).

+

+ Own Id: OTP-13488

+
+ +

+ Don't send unasked for systemd notifications in epmd

+

+ Own Id: OTP-13493 Aux Id: PR-999

+
+ +

+ The enif_send API has been extended to allow NULL to be + used as the message environment. When used this way, a + message environment is implicitly created and the given + term is copied into that environment before sending. This + can be an optimization if many small messages are being + sent by the nif.

+

+ Own Id: OTP-13495

+
+ +

+ The tracing support has been extended to allow tracing on + ports. Ports can be traced on using the 'ports', 'send' + and 'receive' trace flags.

+

+ The first argument of erlang:trace/3 has + been extended so that 'all', 'existing' and + 'new' now include both processes and ports. New + Tracee variants, 'all_processes', + 'all_ports', 'existing_processes' etc have + been added to specify only processes or ports.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13496

+
+ +

+ When the 'procs' trace flag is enabled, a + 'spawned' trace event is now also generated by a + newly created process. The previous event 'spawn' + remains, but as it is generated by the process that did + the spawn, it is not guaranteed that it is ordered with + other trace events from the newly spawned process. So + when tracking the lifetime of a process this new event + should be used as the creation event.

+

+ This new trace event is marked as an incompatibility + because tools that expect certain trace events when + enabling 'procs' will have to updated.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13497

+
+ +

+ Add the erlang:match_spec_test/3 + function. The functions allows the testing of match + specifications for both tracing and ets tables. It can be + used to test that a match specification does the expected + filtering on specific data. It also returns more verbose + error reasons for incorrectly constructed match + specifications.

+

+ Own Id: OTP-13501

+
+ +

+ The erts internal tracing support has been changed to + have much less overhead and be more scalable.

+

+ This rewrite does not break any backwards + incompatibilities, but it does change the ordering of + some trace messages when compared to previous releases. + It should be noted that this only applies to trace + messages sent to processes or ports, it does not apply to + the new tracer module. However in future releases they + may also be effected by this.

+

+ Trace messages are only guaranteed to be ordered from one + traced process or port. In previous releases this was not + visible as a 'send' trace message would always + arrive before the corresponding 'receive' trace + message that is no longer always the case. This also + means that timestamped trace messages may seem to arrive + out of order as the timestamp is taken when the event is + triggered and not when it is put in the queue of the + tracer.

+

+ Own Id: OTP-13503

+
+ +

+ Add possibility to filter send and receive + trace with match specifications.

+

+ Own Id: OTP-13507

+
+ +

+ Add maps:update_with/3,4 and maps:take/2

+

+ Own Id: OTP-13522 Aux Id: PR-1025

+
+ +

+ Introduce LTTng tracing via Erlang tracing.

+

+ For LTTng to be enabled OTP needs to be built with + configure option --with-dynamic-trace=lttng.

+

The dynamic trace module dyntrace is now + capable to be used as a LTTng sink for Erlang tracing. + For a list of all tracepoints, see Runtime Tools User's + Guide .

+

This feature also introduces an incompatible change in + trace tags. The trace tags gc_start and + gc_end has been split into gc_minor_start, + gc_minor_end and gc_major_start, + gc_major_end.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13532

+
+ +

+ Print heap pointers for garbing processes during + crashdump

+

+ Own Id: OTP-13541 Aux Id: PR-1026

+
+ +

+ Changed and improved low level memory statistics returned + by erlang:system_info/1. The info for + erts_mmap has been moved from mseg_alloc to + its own section returned by {allocator, + erts_mmap}.

+

+ Own Id: OTP-13560

+
+ +

+ Add enif_snprintf to the NIF API

+

+ The function enif_snprintf is similar to + snprintf call but can handle formatting of Erlang + terms via %T format specifier.

+

+ Own Id: OTP-13580

+
+ +

The warning in the documentation for + erlang:raise/3 has been removed. It is now + officially perfectly fine to use raise/3 in production + code.

+

+ Own Id: OTP-13599

+
+ +

+ Fix bugs caused by the VM sometimes truncating object + sizes or offsets to 32 bits on 64-bit hosts. These bugs + were mainly found when working with large unicode strings + and nifs environments.

+

+ Own Id: OTP-13606

+
+ +

+ Add -start_epmd command line option, this lets you + disable automatic starting of epmd when starting a + distributed node.

+

+ Add -epmd_module command line option, this lets + you specify a module to register and look-up node names + in. The default module is erl_epmd.

+

+ Own Id: OTP-13627

+
+ +

+ erlang:halt now truncates strings longer than 200 + characters instead of failing with badarg.

+

+ Own Id: OTP-13630

+
+ +

+ Fix possible race in poller wake up on windows

+

+ Own Id: OTP-13634

+
+
+
+ +
+
Erts 7.3.1
Fixed Bugs and Malfunctions -- cgit v1.2.3